[Pkg-electronics-commits] [pcb-rnd] 01/26: New upstream version 1.1.3+repack1

Dima Kogan dima at secretsauce.net
Mon Jan 16 22:31:51 UTC 2017


This is an automated email from the git hooks/post-receive script.

dkogan pushed a commit to branch master
in repository pcb-rnd.

commit 4b3434c84414ffbffa8b3ec1fa4de2f0f25239f6
Author: Dima Kogan <dima at secretsauce.net>
Date:   Sat Dec 17 20:17:42 2016 -0800

    New upstream version 1.1.3+repack1
---
 AUTHORS                                            |    13 +
 COPYING                                            |   340 +
 Changelog                                          |   701 +
 INSTALL                                            |    69 +
 Makefile                                           |    50 +
 Makefile.conf.in                                   |    14 +
 README                                             |    28 +
 Release_notes                                      |    17 +
 config.h.in                                        |   215 +
 configure                                          |     4 +
 data/Makefile                                      |    64 +
 data/README                                        |    56 +
 data/application-x-excellon-16.png                 |   Bin 0 -> 690 bytes
 data/application-x-excellon-16.svg                 |  1271 ++
 data/application-x-excellon-22.png                 |   Bin 0 -> 1086 bytes
 data/application-x-excellon-22.svg                 |  1571 +++
 data/application-x-excellon-24.png                 |   Bin 0 -> 1100 bytes
 data/application-x-excellon-32.png                 |   Bin 0 -> 1613 bytes
 data/application-x-excellon-32.svg                 |  1406 ++
 data/application-x-excellon-48.png                 |   Bin 0 -> 2438 bytes
 data/application-x-excellon-48.svg                 |  1283 ++
 data/application-x-excellon.svg                    |  1289 ++
 data/application-x-gerber-16.png                   |   Bin 0 -> 650 bytes
 data/application-x-gerber-16.svg                   |   543 +
 data/application-x-gerber-22.png                   |   Bin 0 -> 919 bytes
 data/application-x-gerber-22.svg                   |   608 +
 data/application-x-gerber-24.png                   |   Bin 0 -> 950 bytes
 data/application-x-gerber-32.png                   |   Bin 0 -> 1443 bytes
 data/application-x-gerber-32.svg                   |   544 +
 data/application-x-gerber-48.png                   |   Bin 0 -> 2050 bytes
 data/application-x-gerber-48.svg                   |   707 +
 data/application-x-gerber.svg                      |   712 +
 data/application-x-pcb-footprint-16.png            |   Bin 0 -> 632 bytes
 data/application-x-pcb-footprint-16.svg            |   554 +
 data/application-x-pcb-footprint-22.png            |   Bin 0 -> 840 bytes
 data/application-x-pcb-footprint-22.svg            |   573 +
 data/application-x-pcb-footprint-24.png            |   Bin 0 -> 849 bytes
 data/application-x-pcb-footprint-32.png            |   Bin 0 -> 1075 bytes
 data/application-x-pcb-footprint-32.svg            |  2302 ++++
 data/application-x-pcb-footprint-48.png            |   Bin 0 -> 1682 bytes
 data/application-x-pcb-footprint-48.svg            |   674 +
 data/application-x-pcb-footprint.svg               |   680 +
 data/application-x-pcb-layout-16.png               |   Bin 0 -> 603 bytes
 data/application-x-pcb-layout-16.svg               |  1333 ++
 data/application-x-pcb-layout-22.png               |   Bin 0 -> 885 bytes
 data/application-x-pcb-layout-22.svg               |  1423 ++
 data/application-x-pcb-layout-24.png               |   Bin 0 -> 900 bytes
 data/application-x-pcb-layout-32.png               |   Bin 0 -> 1024 bytes
 data/application-x-pcb-layout-32.svg               |  1362 ++
 data/application-x-pcb-layout-48.png               |   Bin 0 -> 1410 bytes
 data/application-x-pcb-layout-48.svg               |  1341 ++
 data/application-x-pcb-layout.svg                  |  1346 ++
 data/application-x-pcb-netlist-16.png              |   Bin 0 -> 631 bytes
 data/application-x-pcb-netlist-16.svg              |   530 +
 data/application-x-pcb-netlist-22.png              |   Bin 0 -> 872 bytes
 data/application-x-pcb-netlist-22.svg              |   567 +
 data/application-x-pcb-netlist-24.png              |   Bin 0 -> 886 bytes
 data/application-x-pcb-netlist-32.png              |   Bin 0 -> 1126 bytes
 data/application-x-pcb-netlist-32.svg              |  1310 ++
 data/application-x-pcb-netlist-48.png              |   Bin 0 -> 1747 bytes
 data/application-x-pcb-netlist-48.svg              |   451 +
 data/application-x-pcb-netlist.svg                 |   459 +
 data/icon-theme-installer                          |   183 +
 data/pcb-48.png                                    |   Bin 0 -> 2968 bytes
 data/pcb.desktop                                   |    10 +
 data/pcb.svg                                       |  1070 ++
 data/pcb.xml                                       |    40 +
 data/pcb_icon.ico                                  |   Bin 0 -> 10134 bytes
 data/regen_files                                   |   165 +
 data/x-excellon.desktop                            |     8 +
 data/x-gerber.desktop                              |     8 +
 data/x-pcb-footprint.desktop                       |     8 +
 data/x-pcb-layout.desktop                          |     8 +
 data/x-pcb-netlist.desktop                         |     8 +
 doc-orig/Makefile                                  |   154 +
 doc-orig/README                                    |    10 +
 doc-orig/README.w32                                |    24 +
 doc-orig/actions.texi                              |  3574 +++++
 doc-orig/eps2png                                   |   495 +
 doc-orig/extract-docs                              |   322 +
 doc-orig/gcode.pcb                                 |  1000 ++
 doc-orig/gcode.pdf                                 |   Bin 0 -> 9111 bytes
 doc-orig/gcode.png                                 |   Bin 0 -> 10809 bytes
 doc-orig/gcode_control_img.eps                     |   711 +
 doc-orig/gcode_control_img.pdf                     |   Bin 0 -> 6017 bytes
 doc-orig/gcode_control_img.png                     |   Bin 0 -> 4948 bytes
 doc-orig/gcode_tool_path.eps                       |   415 +
 doc-orig/gcode_tool_path.pdf                       |   Bin 0 -> 6212 bytes
 doc-orig/gcode_tool_path.png                       |   Bin 0 -> 16104 bytes
 doc-orig/new_gen/Makefile                          |    14 +
 doc-orig/new_gen/README                            |     2 +
 doc-orig/new_gen/fractional_size.tab               |    65 +
 doc-orig/new_gen/letter_size.tab                   |    28 +
 doc-orig/new_gen/metric_size.tab                   |   190 +
 doc-orig/new_gen/tab2html.sh                       |    47 +
 doc-orig/new_gen/wire_size.tab                     |   100 +
 doc-orig/options.texi                              |   896 ++
 doc-orig/pad.pcb                                   |   911 ++
 doc-orig/pad.pdf                                   |   Bin 0 -> 8493 bytes
 doc-orig/pad.png                                   |   Bin 0 -> 1789 bytes
 doc-orig/pcb.1                                     |    45 +
 doc-orig/pcb.css                                   |    11 +
 doc-orig/pcb.html                                  | 12884 +++++++++++++++++++
 doc-orig/pcbfile.texi                              |   946 ++
 doc-orig/puller.pcb                                |   848 ++
 doc-orig/puller.pdf                                |   Bin 0 -> 3025 bytes
 doc-orig/puller.png                                |   Bin 0 -> 303 bytes
 doc-orig/refcard.pdf                               |   Bin 0 -> 35991 bytes
 doc-orig/refcard.tex                               |   258 +
 doc-orig/thermal.pcb                               |   844 ++
 doc-orig/thermal.pdf                               |   Bin 0 -> 6284 bytes
 doc-orig/thermal.png                               |   Bin 0 -> 6632 bytes
 doc-orig/tutorial/Makefile                         |    21 +
 doc-orig/tutorial/tut1.pcb                         |  2018 +++
 doc-rnd/Autostyle.html                             |    14 +
 doc-rnd/Autostyle.sh                               |    71 +
 doc-rnd/Makefile                                   |    32 +
 doc-rnd/README                                     |    10 +
 doc-rnd/TODO                                       |   154 +
 doc-rnd/UNIX.txt                                   |    31 +
 doc-rnd/conf/groups.html                           |   116 +
 doc-rnd/conf/index.html                            |    92 +
 doc-rnd/conf/index_prog.html                       |     8 +
 doc-rnd/conf/index_user.html                       |    92 +
 doc-rnd/conf/lists.html                            |   125 +
 doc-rnd/conf/merging.png                           |   Bin 0 -> 275462 bytes
 doc-rnd/conf/noextend.html                         |    59 +
 doc-rnd/conf/plugin_chk.html                       |     7 +
 doc-rnd/conf/prio.html                             |    22 +
 doc-rnd/conf/scalars.html                          |    56 +
 doc-rnd/conf/sources.html                          |    72 +
 doc-rnd/conf/src/Makefile                          |     2 +
 doc-rnd/conf/src/merging.dot                       |   103 +
 doc-rnd/conf/syntax.html                           |    45 +
 doc-rnd/conf/tree/CFN_BOOLEAN.html                 |     8 +
 doc-rnd/conf/tree/CFN_COLOR.html                   |     9 +
 doc-rnd/conf/tree/CFN_COORD.html                   |    10 +
 doc-rnd/conf/tree/CFN_INCREMENTS.html              |     8 +
 doc-rnd/conf/tree/CFN_INTEGER.html                 |    10 +
 doc-rnd/conf/tree/CFN_LIST.html                    |    10 +
 doc-rnd/conf/tree/CFN_REAL.html                    |     9 +
 doc-rnd/conf/tree/CFN_STRING.html                  |    13 +
 doc-rnd/conf/tree/CFN_UNIT.html                    |     9 +
 doc-rnd/conf/tree/appearance.html                  |     8 +
 doc-rnd/conf/tree/appearance_color.html            |    30 +
 doc-rnd/conf/tree/appearance_messages.html         |     7 +
 doc-rnd/conf/tree/appearance_misc.html             |     7 +
 doc-rnd/conf/tree/appearance_pinout.html           |    12 +
 doc-rnd/conf/tree/design.html                      |    26 +
 doc-rnd/conf/tree/editor.html                      |    49 +
 doc-rnd/conf/tree/editor_view.html                 |     8 +
 doc-rnd/conf/tree/rc.html                          |    25 +
 doc-rnd/conf/tree/rc_path.html                     |    12 +
 doc-rnd/conf/tree/temp.html                        |     7 +
 doc-rnd/contrib.html                               |    66 +
 doc-rnd/ddrc/examples1.txt                         |    54 +
 doc-rnd/ddrc/proposal1.txt                         |   242 +
 doc-rnd/ddrc/requirements.txt                      |    23 +
 doc-rnd/devlog/20150731a_menu.html                 |    59 +
 doc-rnd/devlog/20150731b_gtk.html                  |    36 +
 doc-rnd/devlog/20150801a_events.html               |    22 +
 doc-rnd/devlog/20150803a_scriptig.html             |    36 +
 doc-rnd/devlog/20150816a_scriptig.html             |    28 +
 doc-rnd/devlog/20150820a_dimensions.html           |    24 +
 doc-rnd/devlog/20150820b_qf.html                   |    27 +
 .../devlog/20150821a_parametric_requirements.html  |    32 +
 doc-rnd/devlog/20150830a_fork_faq.html             |   257 +
 doc-rnd/devlog/20150830b_back_ann.html             |   304 +
 doc-rnd/devlog/20150901a_back_ann.html             |    87 +
 doc-rnd/devlog/20151028_glib.html                  |    65 +
 doc-rnd/devlog/20160101_cschem.html                |    68 +
 doc-rnd/devlog/20160126.html                       |    59 +
 doc-rnd/devlog/20160313_unglib.html                |    49 +
 doc-rnd/devlog/20160314_valgrind_flex.html         |    79 +
 doc-rnd/devlog/20160409.html                       |    48 +
 doc-rnd/devlog/20160409/header                     |     7 +
 doc-rnd/devlog/20160409/header.csv                 |     1 +
 doc-rnd/devlog/20160409/legend.txt                 |    60 +
 doc-rnd/devlog/20160409/pie_col_1.png              |   Bin 0 -> 1133 bytes
 doc-rnd/devlog/20160409/pie_col_2.png              |   Bin 0 -> 1082 bytes
 doc-rnd/devlog/20160409/pie_col_3.png              |   Bin 0 -> 1189 bytes
 doc-rnd/devlog/20160409/pie_col_4.png              |   Bin 0 -> 1025 bytes
 doc-rnd/devlog/20160409/pie_col_5.png              |   Bin 0 -> 1161 bytes
 doc-rnd/devlog/20160409/pie_col_7.png              |   Bin 0 -> 1225 bytes
 doc-rnd/devlog/20160409/poll.csv                   |     9 +
 doc-rnd/devlog/20160409/poll.html                  |    98 +
 doc-rnd/devlog/20160409/poll.tsv                   |     9 +
 doc-rnd/devlog/20160716_sprint.html                |    81 +
 doc-rnd/devlog/20160802.html                       |    25 +
 doc-rnd/devlog/20160808_model.html                 |   101 +
 doc-rnd/devlog/20160823_lhtpers.html               |   209 +
 doc-rnd/devlog/20160826_virtusers.html             |    65 +
 doc-rnd/devlog/20160921_gl.html                    |    56 +
 doc-rnd/devlog/20161014_cleanup.html               |    35 +
 doc-rnd/devlog/20162601/header                     |    11 +
 doc-rnd/devlog/20162601/header.csv                 |     1 +
 doc-rnd/devlog/20162601/poll.csv                   |    10 +
 doc-rnd/devlog/20162601/poll.html                  |    24 +
 doc-rnd/devlog/20162601/poll.tsv                   |    10 +
 doc-rnd/devlog/poll_common/gencsv.sh               |     3 +
 doc-rnd/devlog/poll_common/genhtml.sh              |    54 +
 doc-rnd/devlog/poll_common/genpie.sh               |    38 +
 doc-rnd/devlog/res/20150830b_annot.dot             |    19 +
 doc-rnd/devlog/res/20150830b_annot.png             |   Bin 0 -> 16424 bytes
 doc-rnd/devlog/res/20150830b_s0.png                |   Bin 0 -> 47236 bytes
 doc-rnd/devlog/res/20150830b_s1.png                |   Bin 0 -> 46853 bytes
 doc-rnd/devlog/res/20150830b_s2.png                |   Bin 0 -> 46779 bytes
 doc-rnd/devlog/res/20150830b_s3.png                |   Bin 0 -> 46574 bytes
 doc-rnd/devlog/res/20150830b_s4.png                |   Bin 0 -> 46596 bytes
 doc-rnd/devlog/res/20150830b_s5.png                |   Bin 0 -> 47089 bytes
 doc-rnd/devlog/res/20150830b_s6.png                |   Bin 0 -> 46929 bytes
 doc-rnd/devlog/res/Makefile                        |     4 +
 doc-rnd/distros.txt                                |    47 +
 doc-rnd/djopt/Makefile                             |    50 +
 doc-rnd/djopt/Post.html                            |     4 +
 doc-rnd/djopt/Pre.html                             |    14 +
 doc-rnd/djopt/debumpify.out.pcb                    |    88 +
 doc-rnd/djopt/debumpify.out.png                    |   Bin 0 -> 1078 bytes
 doc-rnd/djopt/debumpify.pcb                        |    89 +
 doc-rnd/djopt/debumpify.png                        |   Bin 0 -> 1104 bytes
 doc-rnd/djopt/debumpify.txt                        |     2 +
 doc-rnd/djopt/index.html                           |    45 +
 doc-rnd/djopt/miter.out.pcb                        |    75 +
 doc-rnd/djopt/miter.out.png                        |   Bin 0 -> 935 bytes
 doc-rnd/djopt/miter.pcb                            |    80 +
 doc-rnd/djopt/miter.png                            |   Bin 0 -> 714 bytes
 doc-rnd/djopt/miter.txt                            |     2 +
 doc-rnd/djopt/orthopull.out.pcb                    |    99 +
 doc-rnd/djopt/orthopull.out.png                    |   Bin 0 -> 988 bytes
 doc-rnd/djopt/orthopull.pcb                        |    98 +
 doc-rnd/djopt/orthopull.png                        |   Bin 0 -> 1009 bytes
 doc-rnd/djopt/orthopull.txt                        |     4 +
 doc-rnd/djopt/unjaggy.out.pcb                      |    79 +
 doc-rnd/djopt/unjaggy.out.png                      |   Bin 0 -> 733 bytes
 doc-rnd/djopt/unjaggy.pcb                          |    81 +
 doc-rnd/djopt/unjaggy.png                          |   Bin 0 -> 767 bytes
 doc-rnd/djopt/unjaggy.txt                          |     2 +
 doc-rnd/djopt/vianudge.out.pcb                     |    79 +
 doc-rnd/djopt/vianudge.out.png                     |   Bin 0 -> 700 bytes
 doc-rnd/djopt/vianudge.pcb                         |    81 +
 doc-rnd/djopt/vianudge.png                         |   Bin 0 -> 705 bytes
 doc-rnd/djopt/vianudge.txt                         |     3 +
 doc-rnd/djopt/viatrim.out.pcb                      |    80 +
 doc-rnd/djopt/viatrim.out.png                      |   Bin 0 -> 792 bytes
 doc-rnd/djopt/viatrim.pcb                          |    87 +
 doc-rnd/djopt/viatrim.png                          |   Bin 0 -> 816 bytes
 doc-rnd/djopt/viatrim.txt                          |     2 +
 doc-rnd/features/ba.html                           |    68 +
 doc-rnd/features/cycdrag.html                      |    46 +
 doc-rnd/features/debian.html                       |    50 +
 doc-rnd/features/debian_list.html                  |    64 +
 doc-rnd/features/dynstyle.html                     |    38 +
 doc-rnd/features/flagcomp.html                     |    41 +
 doc-rnd/features/fp_wget.html                      |    55 +
 doc-rnd/features/fullscreen.html                   |    38 +
 doc-rnd/features/gpmi.html                         |    59 +
 doc-rnd/features/grid.html                         |    99 +
 doc-rnd/features/grid_edge.png                     |   Bin 0 -> 44251 bytes
 doc-rnd/features/grid_global_nosparse.png          |   Bin 0 -> 58791 bytes
 doc-rnd/features/grid_global_sparse.png            |   Bin 0 -> 60095 bytes
 doc-rnd/features/grid_local_16.png                 |   Bin 0 -> 59312 bytes
 doc-rnd/features/grid_local_4.png                  |   Bin 0 -> 59014 bytes
 doc-rnd/features/index.html                        |    61 +
 doc-rnd/features/intconn.html                      |    90 +
 doc-rnd/features/intconn1.png                      |   Bin 0 -> 39766 bytes
 doc-rnd/features/intconn2.png                      |   Bin 0 -> 40317 bytes
 doc-rnd/features/intconn3.png                      |   Bin 0 -> 42478 bytes
 doc-rnd/features/io.html                           |    41 +
 doc-rnd/features/jumper_1206.fp                    |     7 +
 doc-rnd/features/library_t.html                    |    46 +
 doc-rnd/features/mincut.html                       |    65 +
 doc-rnd/features/mincut.png                        |   Bin 0 -> 6161 bytes
 doc-rnd/features/negselect.html                    |    41 +
 doc-rnd/features/nonetlist.html                    |    71 +
 doc-rnd/features/oldplugins.html                   |    69 +
 doc-rnd/features/onpoint.html                      |    68 +
 doc-rnd/features/pcb-fp.html                       |    54 +
 doc-rnd/features/pcb-fp.png                        |   Bin 0 -> 13727 bytes
 doc-rnd/features/pcblib.html                       |    74 +
 doc-rnd/features/pcblib.png                        |   Bin 0 -> 51269 bytes
 doc-rnd/features/polygrid.html                     |    35 +
 doc-rnd/features/propedit.html                     |    65 +
 doc-rnd/features/propedit.png                      |   Bin 0 -> 50892 bytes
 doc-rnd/features/query.html                        |    42 +
 doc-rnd/features/res.html                          |   147 +
 doc-rnd/features/routings.html                     |    97 +
 doc-rnd/features/scconfig.html                     |    33 +
 doc-rnd/features/settings.html                     |    49 +
 doc-rnd/features/square.html                       |    89 +
 doc-rnd/features/square.pcb                        |   893 ++
 doc-rnd/features/square.png                        |   Bin 0 -> 8819 bytes
 doc-rnd/features/tostyle.html                      |    68 +
 doc-rnd/features/unglib.html                       |    35 +
 doc-rnd/gpmi/Credits                               |    11 +
 doc-rnd/gpmi/History                               |    32 +
 doc-rnd/gpmi/Makefile                              |    20 +
 doc-rnd/gpmi/Porting                               |     5 +
 doc-rnd/gpmi/gpmi_flow.dot                         |    63 +
 doc-rnd/gpmi/gpmi_flow.png                         |   Bin 0 -> 131942 bytes
 doc-rnd/gpmi/gpmi_flow_exp.dot                     |    65 +
 doc-rnd/gpmi/gpmi_flow_exp.png                     |   Bin 0 -> 67706 bytes
 doc-rnd/gpmi/gpmi_flow_load.dot                    |    63 +
 doc-rnd/gpmi/gpmi_flow_load.png                    |   Bin 0 -> 65184 bytes
 doc-rnd/gpmi/gpmi_flow_menu.dot                    |    63 +
 doc-rnd/gpmi/gpmi_flow_menu.png                    |   Bin 0 -> 64957 bytes
 doc-rnd/gpmi/gpmi_flow_reg.dot                     |    64 +
 doc-rnd/gpmi/gpmi_flow_reg.png                     |   Bin 0 -> 65216 bytes
 doc-rnd/gpmi/index.html                            |    32 +
 doc-rnd/gpmi/packages/Makefile                     |    15 +
 doc-rnd/gpmi/packages/XREF                         |    85 +
 doc-rnd/gpmi/packages/actions.html                 |   123 +
 doc-rnd/gpmi/packages/actions_ref.html             |    82 +
 doc-rnd/gpmi/packages/dialogs.html                 |    47 +
 doc-rnd/gpmi/packages/dialogs_ref.html             |   105 +
 doc-rnd/gpmi/packages/event_id.html                |    20 +
 doc-rnd/gpmi/packages/hid.html                     |    76 +
 doc-rnd/gpmi/packages/hid_ref.html                 |   205 +
 doc-rnd/gpmi/packages/layout.html                  |    58 +
 doc-rnd/gpmi/packages/layout_ref.html              |   305 +
 doc-rnd/gpmi/rosetta/10_hello/ID.desc              |     2 +
 doc-rnd/gpmi/rosetta/10_hello/ID.name              |     1 +
 doc-rnd/gpmi/rosetta/10_hello/ex.awk               |    14 +
 doc-rnd/gpmi/rosetta/10_hello/ex.bash              |    18 +
 doc-rnd/gpmi/rosetta/10_hello/ex.html              |    25 +
 doc-rnd/gpmi/rosetta/10_hello/ex.lua               |     9 +
 doc-rnd/gpmi/rosetta/10_hello/ex.pl                |    10 +
 doc-rnd/gpmi/rosetta/10_hello/ex.py                |     8 +
 doc-rnd/gpmi/rosetta/10_hello/ex.rb                |     9 +
 doc-rnd/gpmi/rosetta/10_hello/ex.scm               |     9 +
 doc-rnd/gpmi/rosetta/10_hello/ex.stt               |    10 +
 doc-rnd/gpmi/rosetta/10_hello/ex.tcl               |    10 +
 doc-rnd/gpmi/rosetta/10_hello/index.html           |    55 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ID.desc          |     3 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ID.name          |     1 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.awk           |    14 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.bash          |    18 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.html          |    26 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.lua           |     9 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.pl            |    10 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.py            |     8 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.rb            |     9 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.scm           |     9 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.stt           |    10 +
 doc-rnd/gpmi/rosetta/10_hello_gui/ex.tcl           |    10 +
 doc-rnd/gpmi/rosetta/10_hello_gui/index.html       |    55 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ID.desc         |     3 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ID.name         |     1 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.awk          |    20 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.bash         |    24 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.html         |    33 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.lua          |    14 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.pl           |    17 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.py           |    12 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.rb           |    14 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.scm          |    12 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.stt          |    12 +
 doc-rnd/gpmi/rosetta/12_hello_menu/ex.tcl          |    15 +
 doc-rnd/gpmi/rosetta/12_hello_menu/index.html      |    58 +
 doc-rnd/gpmi/rosetta/30_move/ID.desc               |     3 +
 doc-rnd/gpmi/rosetta/30_move/ID.name               |     1 +
 doc-rnd/gpmi/rosetta/30_move/ex.awk                |    22 +
 doc-rnd/gpmi/rosetta/30_move/ex.bash               |    41 +
 doc-rnd/gpmi/rosetta/30_move/ex.html               |    36 +
 doc-rnd/gpmi/rosetta/30_move/ex.lua                |    18 +
 doc-rnd/gpmi/rosetta/30_move/ex.pl                 |    20 +
 doc-rnd/gpmi/rosetta/30_move/ex.py                 |    15 +
 doc-rnd/gpmi/rosetta/30_move/ex.rb                 |    25 +
 doc-rnd/gpmi/rosetta/30_move/ex.scm                |    20 +
 doc-rnd/gpmi/rosetta/30_move/ex.stt                |    24 +
 doc-rnd/gpmi/rosetta/30_move/ex.tcl                |    17 +
 doc-rnd/gpmi/rosetta/30_move/index.html            |    74 +
 doc-rnd/gpmi/rosetta/35_export_drill/ID.desc       |     1 +
 doc-rnd/gpmi/rosetta/35_export_drill/ID.name       |     1 +
 doc-rnd/gpmi/rosetta/35_export_drill/ex.awk        |    60 +
 doc-rnd/gpmi/rosetta/35_export_drill/ex.html       |    60 +
 doc-rnd/gpmi/rosetta/35_export_drill/ex.lua        |    65 +
 doc-rnd/gpmi/rosetta/35_export_drill/ex.tcl        |    63 +
 doc-rnd/gpmi/rosetta/35_export_drill/index.html    |    71 +
 doc-rnd/gpmi/rosetta/index.html                    |    58 +
 doc-rnd/gpmi/rosetta/index.templ.html              |    29 +
 doc-rnd/gpmi/scripting_intro.html                  |   226 +
 doc-rnd/gpmi/util/Makefile                         |     1 +
 doc-rnd/gpmi/util/rosetta_genpages.sh              |   171 +
 doc-rnd/gpmi/util/tags                             |     4 +
 doc-rnd/gpmi_temp_inst.txt                         |    65 +
 doc-rnd/hacking/c89.html                           |    14 +
 doc-rnd/hacking/data1.png                          |   Bin 0 -> 184597 bytes
 doc-rnd/hacking/import.html                        |    64 +
 doc-rnd/hacking/indent.html                        |    15 +
 doc-rnd/hacking/plugin_core_simple.html            |    59 +
 doc-rnd/hacking/plugin_naming.html                 |    12 +
 doc-rnd/hacking/releasing.txt                      |    11 +
 doc-rnd/hacking/src/Makefile                       |     2 +
 doc-rnd/hacking/src/data1.dot                      |    47 +
 doc-rnd/help.html                                  |    62 +
 doc-rnd/index.html                                 |   123 +
 doc-rnd/irc.html                                   |    43 +
 doc-rnd/keys.html                                  |   684 +
 doc-rnd/keys_mkey.html                             |   508 +
 doc-rnd/logo128.png                                |   Bin 0 -> 8677 bytes
 doc-rnd/logo16.png                                 |   Bin 0 -> 887 bytes
 doc-rnd/logo256.png                                |   Bin 0 -> 12673 bytes
 doc-rnd/logo32.png                                 |   Bin 0 -> 1489 bytes
 doc-rnd/logo64.png                                 |   Bin 0 -> 3519 bytes
 doc-rnd/mac.txt                                    |    22 +
 doc-rnd/man/Makefile                               |    58 +
 doc-rnd/man/README                                 |    10 +
 doc-rnd/man/copyright.mml                          |    21 +
 doc-rnd/man/gsch2pcb-rnd.1                         |   134 +
 doc-rnd/man/index.html                             |     3 +
 doc-rnd/man/pcb-rnd.1                              |    47 +
 doc-rnd/man/pcb-rnd.1.html                         |    79 +
 doc-rnd/man/pcb-rnd.1.mml                          |    38 +
 doc-rnd/misc/install_cgi.html                      |    45 +
 doc-rnd/mods/after.png                             |   Bin 0 -> 2213 bytes
 doc-rnd/mods/before.png                            |   Bin 0 -> 2048 bytes
 doc-rnd/mods/gen.sh                                |   128 +
 doc-rnd/mods/index.html                            |   136 +
 doc-rnd/mods/mods.png                              |   Bin 0 -> 6212 bytes
 doc-rnd/mods/post.html                             |     3 +
 doc-rnd/mods/pre.html                              |    60 +
 doc-rnd/mods2/after.png                            |   Bin 0 -> 2232 bytes
 doc-rnd/mods2/before.png                           |   Bin 0 -> 2048 bytes
 doc-rnd/mods2/gen.sh                               |   128 +
 doc-rnd/mods2/index.html                           |   159 +
 doc-rnd/mods2/mods.png                             |   Bin 0 -> 7064 bytes
 doc-rnd/mods2/post.html                            |     3 +
 doc-rnd/mods2/pre.html                             |    60 +
 doc-rnd/mods3/after.png                            |   Bin 0 -> 1767 bytes
 doc-rnd/mods3/before.png                           |   Bin 0 -> 2048 bytes
 doc-rnd/mods3/gen.sh                               |   125 +
 doc-rnd/mods3/index.html                           |   373 +
 doc-rnd/mods3/mods.png                             |   Bin 0 -> 10881 bytes
 doc-rnd/mods3/post.html                            |    17 +
 doc-rnd/mods3/pre.html                             |    60 +
 doc-rnd/motivation.html                            |   109 +
 doc-rnd/myfeature.html                             |    69 +
 doc-rnd/news.html                                  |    53 +
 doc-rnd/packaging.txt                              |    90 +
 doc-rnd/query/tutor_cli.html                       |   168 +
 doc-rnd/screenshot.jpg                             |   Bin 0 -> 10729 bytes
 doc-rnd/windows.txt                                |    38 +
 doc-rnd/wishlist.txt                               |    32 +
 pcblib/Makefile                                    |    34 +
 pcblib/README                                      |     8 +
 pcblib/connector/BNC_LAY.fp                        |    15 +
 pcblib/connector/DB15F.fp                          |    72 +
 pcblib/connector/DB15M.fp                          |    71 +
 pcblib/connector/DB25F.fp                          |    92 +
 pcblib/connector/DB25M.fp                          |    91 +
 pcblib/connector/DB37F.fp                          |   116 +
 pcblib/connector/DB37M.fp                          |   115 +
 pcblib/connector/DB9F.fp                           |    60 +
 pcblib/connector/DB9M.fp                           |    59 +
 pcblib/connector/pwrjack.fp                        |    16 +
 pcblib/parametric/acy                              |    56 +
 pcblib/parametric/acy.awk                          |   152 +
 pcblib/parametric/alf                              |    37 +
 pcblib/parametric/alf.awk                          |    65 +
 pcblib/parametric/bga                              |    69 +
 pcblib/parametric/bga.awk                          |   117 +
 pcblib/parametric/common.awk                       |   517 +
 pcblib/parametric/connector                        |    53 +
 pcblib/parametric/connector.awk                    |    87 +
 pcblib/parametric/dip                              |    21 +
 pcblib/parametric/dip.awk                          |    27 +
 pcblib/parametric/msop                             |    28 +
 pcblib/parametric/plcc                             |    29 +
 pcblib/parametric/plcc.awk                         |    58 +
 pcblib/parametric/qf                               |    87 +
 pcblib/parametric/qf.awk                           |   146 +
 pcblib/parametric/qfn                              |    24 +
 pcblib/parametric/qfn.awk                          |    50 +
 pcblib/parametric/qfp                              |    24 +
 pcblib/parametric/qfp.awk                          |    52 +
 pcblib/parametric/qsop                             |    27 +
 pcblib/parametric/rcy                              |    32 +
 pcblib/parametric/rcy.awk                          |    93 +
 pcblib/parametric/screw                            |    36 +
 pcblib/parametric/screw.awk                        |   127 +
 pcblib/parametric/silkmark.help                    |    11 +
 pcblib/parametric/so                               |    38 +
 pcblib/parametric/so.awk                           |    50 +
 pcblib/parametric/ssop                             |    27 +
 pcblib/parametric/tssop                            |    28 +
 pcblib/smd/01005.fp                                |    32 +
 pcblib/smd/0201.fp                                 |    32 +
 pcblib/smd/0402.fp                                 |    32 +
 pcblib/smd/0603.fp                                 |    32 +
 pcblib/smd/0805.fp                                 |    34 +
 pcblib/smd/1008.fp                                 |    34 +
 pcblib/smd/1206.fp                                 |    34 +
 pcblib/smd/1210.fp                                 |    34 +
 pcblib/smd/1806.fp                                 |    34 +
 pcblib/smd/1825.fp                                 |    34 +
 pcblib/smd/2706.fp                                 |    20 +
 pcblib/smd/DO214.fp                                |    20 +
 pcblib/smd/DO214AB.fp                              |    20 +
 pcblib/smd/MPAK.fp                                 |    31 +
 pcblib/smd/SC70_3.fp                               |    33 +
 pcblib/smd/SC70_4.fp                               |    40 +
 pcblib/smd/SC90.fp                                 |    33 +
 pcblib/smd/SOD106A.fp                              |    20 +
 pcblib/smd/SOD110.fp                               |    20 +
 pcblib/smd/SOD123.fp                               |    20 +
 pcblib/smd/SOD323.fp                               |    20 +
 pcblib/smd/SOD80.fp                                |    20 +
 pcblib/smd/SOD87.fp                                |    20 +
 pcblib/smd/SOT143.fp                               |    38 +
 pcblib/smd/SOT223.fp                               |    29 +
 pcblib/smd/SOT23.fp                                |    33 +
 pcblib/smd/SOT23D.fp                               |    33 +
 pcblib/smd/SOT25.fp                                |    38 +
 pcblib/smd/SOT26.fp                                |    41 +
 pcblib/smd/SOT323.fp                               |    33 +
 pcblib/smd/SOT323D.fp                              |    33 +
 pcblib/smd/SOT325.fp                               |    40 +
 pcblib/smd/SOT326.fp                               |    42 +
 pcblib/smd/SOT89.fp                                |    31 +
 pcblib/smd/TANT_A.fp                               |    21 +
 pcblib/smd/TANT_B.fp                               |    21 +
 pcblib/smd/TANT_C.fp                               |    21 +
 pcblib/smd/TANT_D.fp                               |    21 +
 pcblib/smd/minimelf.fp                             |    12 +
 pcblib/tru-hole/HC49.fp                            |    13 +
 pcblib/tru-hole/HC49U.fp                           |    13 +
 pcblib/tru-hole/HC49UH.fp                          |    13 +
 pcblib/tru-hole/HC49U_3.fp                         |    14 +
 pcblib/tru-hole/HC49U_3H.fp                        |    14 +
 pcblib/tru-hole/HC51U.fp                           |    13 +
 pcblib/tru-hole/HC51UH.fp                          |    13 +
 pcblib/tru-hole/HEPTAWATT.fp                       |    20 +
 pcblib/tru-hole/LED3.fp                            |    19 +
 pcblib/tru-hole/LED5.fp                            |    16 +
 pcblib/tru-hole/MULTIWATT11.fp                     |    24 +
 pcblib/tru-hole/MULTIWATT15.fp                     |    28 +
 pcblib/tru-hole/MULTIWATT8.fp                      |    21 +
 pcblib/tru-hole/OSC14.fp                           |    26 +
 pcblib/tru-hole/PENTAWATT.fp                       |    18 +
 pcblib/tru-hole/TACT_6x6_4p                        |    37 +
 pcblib/tru-hole/TO126.fp                           |    35 +
 pcblib/tru-hole/TO126S.fp                          |    23 +
 pcblib/tru-hole/TO126SW.fp                         |    21 +
 pcblib/tru-hole/TO126W.fp                          |    16 +
 pcblib/tru-hole/TO18.fp                            |    36 +
 pcblib/tru-hole/TO218.fp                           |    17 +
 pcblib/tru-hole/TO220.fp                           |    47 +
 pcblib/tru-hole/TO220ACSTAND.fp                    |    16 +
 pcblib/tru-hole/TO220S.fp                          |    36 +
 pcblib/tru-hole/TO220SW.fp                         |    26 +
 pcblib/tru-hole/TO220W.fp                          |    23 +
 pcblib/tru-hole/TO247.fp                           |    19 +
 pcblib/tru-hole/TO247_2.fp                         |    17 +
 pcblib/tru-hole/TO251.fp                           |    20 +
 pcblib/tru-hole/TO264.fp                           |    18 +
 pcblib/tru-hole/TO39.fp                            |    37 +
 pcblib/tru-hole/TO92.fp                            |    23 +
 scconfig/Makefile                                  |    97 +
 scconfig/Makefile.comp.inc                         |    60 +
 scconfig/Makefile.comp_var.inc                     |    31 +
 scconfig/Makefile.dep.inc                          |    41 +
 scconfig/README                                    |     3 +
 scconfig/Rev.h                                     |     1 +
 scconfig/Rev.tab                                   |    23 +
 scconfig/cquote.c                                  |    93 +
 scconfig/gen_conf.sh                               |   122 +
 scconfig/gen_core_lists.sh                         |    62 +
 scconfig/hooks.c                                   |   732 ++
 scconfig/menucfg.c                                 |    71 +
 scconfig/plugin_3state.h                           |    54 +
 scconfig/plugins.h                                 |    79 +
 scconfig/revtest.c                                 |    83 +
 scconfig/src/LGPL-2.1                              |   510 +
 scconfig/src/c99/INIT.c                            |     2 +
 scconfig/src/c99/INIT.h                            |     1 +
 scconfig/src/c99/Makefile.plugin                   |     6 +
 scconfig/src/c99/find_c99.c                        |   628 +
 scconfig/src/default/Makefile.plugin               |   134 +
 scconfig/src/default/arg.c                         |   103 +
 scconfig/src/default/arg.h                         |     7 +
 scconfig/src/default/db.c                          |   343 +
 scconfig/src/default/db.h                          |    35 +
 scconfig/src/default/dep.c                         |   176 +
 scconfig/src/default/dep.h                         |    12 +
 scconfig/src/default/deps_default.c                |   116 +
 scconfig/src/default/deps_default.h                |     1 +
 scconfig/src/default/find.h                        |   105 +
 scconfig/src/default/find_cc.c                     |   636 +
 scconfig/src/default/find_environ.c                |   166 +
 scconfig/src/default/find_fscalls.c                |   380 +
 scconfig/src/default/find_fstools.c                |   762 ++
 scconfig/src/default/find_io.c                     |    76 +
 scconfig/src/default/find_libs.c                   |   260 +
 scconfig/src/default/find_printf.c                 |   312 +
 scconfig/src/default/find_proc.c                   |   137 +
 scconfig/src/default/find_signal.c                 |   141 +
 scconfig/src/default/find_sys.c                    |   423 +
 scconfig/src/default/find_target.c                 |    50 +
 scconfig/src/default/find_time.c                   |   119 +
 scconfig/src/default/find_types.c                  |   281 +
 scconfig/src/default/find_uname.c                  |   342 +
 scconfig/src/default/hooks.h                       |    30 +
 scconfig/src/default/ht.c                          |   257 +
 scconfig/src/default/ht.h                          |    51 +
 scconfig/src/default/lib_compile.c                 |   287 +
 scconfig/src/default/lib_file.c                    |   236 +
 scconfig/src/default/lib_filelist.c                |   338 +
 scconfig/src/default/lib_pkg_config.c              |   135 +
 scconfig/src/default/lib_srctree.c                 |    77 +
 scconfig/src/default/lib_try.c                     |   179 +
 scconfig/src/default/lib_uniqinc.c                 |   158 +
 scconfig/src/default/libs.h                        |   111 +
 scconfig/src/default/log.c                         |   132 +
 scconfig/src/default/log.h                         |    19 +
 scconfig/src/default/main.c                        |    92 +
 scconfig/src/default/main_custom_args.c            |    49 +
 scconfig/src/default/main_custom_args.h            |     6 +
 scconfig/src/default/main_lib.c                    |   188 +
 scconfig/src/default/main_lib.h                    |     8 +
 scconfig/src/default/regex.c                       |   612 +
 scconfig/src/default/regex.h                       |    14 +
 scconfig/src/default/str.c                         |   183 +
 scconfig/src/generator/INIT.c                      |     0
 scconfig/src/generator/Makefile.plugin             |    11 +
 scconfig/src/generator/generator.c                 |   826 ++
 scconfig/src/generator/generator.h                 |    15 +
 scconfig/src/generator/openfiles.c                 |    95 +
 scconfig/src/generator/openfiles.h                 |    23 +
 scconfig/src/gui/INIT.c                            |     2 +
 scconfig/src/gui/INIT.h                            |     1 +
 scconfig/src/gui/Makefile.plugin                   |    29 +
 scconfig/src/gui/find_gd.c                         |   202 +
 scconfig/src/gui/find_gd.h                         |     4 +
 scconfig/src/gui/find_gtk2.c                       |    73 +
 scconfig/src/gui/find_gtk2.h                       |     2 +
 scconfig/src/gui/find_lesstif2.c                   |    68 +
 scconfig/src/gui/find_lesstif2.h                   |     2 +
 scconfig/src/gui/find_misc.c                       |    66 +
 scconfig/src/gui/find_misc.h                       |     1 +
 scconfig/src/gui/find_x.c                          |    96 +
 scconfig/src/gui/find_x.h                          |     3 +
 scconfig/src/gui/gui.c                             |    23 +
 scconfig/src/math/INIT.c                           |     2 +
 scconfig/src/math/INIT.h                           |     1 +
 scconfig/src/math/Makefile.plugin                  |    18 +
 scconfig/src/math/find_fpenan.c                    |   279 +
 scconfig/src/math/find_fpenan.h                    |     7 +
 scconfig/src/math/find_func.c                      |    86 +
 scconfig/src/math/find_func.h                      |     4 +
 scconfig/src/math/find_math.c                      |    73 +
 scconfig/src/math/find_mfunc_cc.c                  |   159 +
 scconfig/src/math/find_mfunc_cc.h                  |     1 +
 scconfig/src/menulib/Makefile.plugin               |    14 +
 scconfig/src/menulib/hookreadline.c                |   592 +
 scconfig/src/menulib/hookreadline.h                |    97 +
 scconfig/src/menulib/hrl_test.c                    |    51 +
 scconfig/src/menulib/menu_test.c                   |    93 +
 scconfig/src/menulib/scmenu.c                      |   187 +
 scconfig/src/menulib/scmenu.h                      |   138 +
 scconfig/src/menulib/scmenu_menu.c                 |   466 +
 scconfig/src/menulib/scmenu_rl.c                   |    47 +
 scconfig/src/menulib/scmenu_text.c                 |   249 +
 scconfig/src/menulib/vt100.c                       |   189 +
 scconfig/src/menulib/vt100.h                       |    82 +
 scconfig/src/parser/INIT.c                         |     2 +
 scconfig/src/parser/INIT.h                         |     1 +
 scconfig/src/parser/Makefile.plugin                |    10 +
 scconfig/src/parser/find_expat.c                   |    48 +
 scconfig/src/parser/parser.c                       |    31 +
 scconfig/src/parser/parser.h                       |    10 +
 scconfig/src/parsgen/INIT.c                        |     2 +
 scconfig/src/parsgen/INIT.h                        |     1 +
 scconfig/src/parsgen/Makefile.plugin               |     6 +
 scconfig/src/parsgen/find_parsgen.c                |   139 +
 scconfig/src/scripts/INIT.c                        |     2 +
 scconfig/src/scripts/INIT.h                        |     1 +
 scconfig/src/scripts/Makefile.plugin               |    50 +
 scconfig/src/scripts/find_gpmi.c                   |    94 +
 scconfig/src/scripts/find_guile.c                  |    74 +
 scconfig/src/scripts/find_lua.c                    |    72 +
 scconfig/src/scripts/find_m4.c                     |    89 +
 scconfig/src/scripts/find_mawk.c                   |    55 +
 scconfig/src/scripts/find_mruby.c                  |    51 +
 scconfig/src/scripts/find_perl.c                   |    84 +
 scconfig/src/scripts/find_python.c                 |    95 +
 scconfig/src/scripts/find_ruby.c                   |   124 +
 scconfig/src/scripts/find_stutter.c                |    55 +
 scconfig/src/scripts/find_tcl.c                    |   132 +
 scconfig/src/scripts/scripts.c                     |    80 +
 scconfig/src/scripts/scripts.h                     |    23 +
 scconfig/src/socket/INIT.c                         |     2 +
 scconfig/src/socket/INIT.h                         |     1 +
 scconfig/src/socket/Makefile.plugin                |    19 +
 scconfig/src/socket/find_dns.c                     |   134 +
 scconfig/src/socket/find_select.c                  |   105 +
 scconfig/src/socket/find_socket.c                  |   744 ++
 scconfig/src/socket/socket.c                       |   170 +
 scconfig/src/socket/socket.h                       |    40 +
 scconfig/src/sul/INIT.c                            |     2 +
 scconfig/src/sul/INIT.h                            |     1 +
 scconfig/src/sul/Makefile.plugin                   |    23 +
 scconfig/src/sul/find_dbus.c                       |    58 +
 scconfig/src/sul/find_dmalloc.c                    |    56 +
 scconfig/src/sul/find_gettext.c                    |    67 +
 scconfig/src/sul/find_glib.c                       |    68 +
 scconfig/src/sul/find_sul.c                        |    10 +
 scconfig/src/sul/find_sul.h                        |     5 +
 scconfig/src/tmpasm/Makefile                       |    17 +
 scconfig/src/tmpasm/Makefile.plugin                |    15 +
 scconfig/src/tmpasm/TODO                           |    12 +
 scconfig/src/tmpasm/debug.c                        |   111 +
 scconfig/src/tmpasm/debug.h                        |     2 +
 scconfig/src/tmpasm/regression/Makefile.in         |    55 +
 scconfig/src/tmpasm/regression/Tutor01_hello.gasm  |    18 +
 scconfig/src/tmpasm/regression/Tutor01_hello.ref   |    16 +
 scconfig/src/tmpasm/regression/Tutor02_vars.gasm   |    17 +
 scconfig/src/tmpasm/regression/Tutor02_vars.ref    |    21 +
 scconfig/src/tmpasm/regression/Tutor03_blocks.gasm |    31 +
 scconfig/src/tmpasm/regression/Tutor03_blocks.ref  |    35 +
 scconfig/src/tmpasm/regression/Tutor04_if.gasm     |    31 +
 scconfig/src/tmpasm/regression/Tutor04_if.ref      |    42 +
 scconfig/src/tmpasm/regression/Tutor05_switch.gasm |    42 +
 scconfig/src/tmpasm/regression/Tutor05_switch.ref  |    71 +
 .../src/tmpasm/regression/Tutor06_foreach.gasm     |    35 +
 scconfig/src/tmpasm/regression/Tutor06_foreach.ref |    32 +
 scconfig/src/tmpasm/regression/Tutor07_sub.gasm    |    31 +
 scconfig/src/tmpasm/regression/Tutor07_sub.ref     |    40 +
 scconfig/src/tmpasm/regression/Tutor08_uniq.gasm   |    40 +
 scconfig/src/tmpasm/regression/Tutor08_uniq.ref    |    59 +
 scconfig/src/tmpasm/regression/Tutor09_ui.gasm     |    15 +
 scconfig/src/tmpasm/regression/Tutor09_ui.ref      |    11 +
 .../tmpasm/regression/Tutor10_include_redir.gasm   |    52 +
 .../tmpasm/regression/Tutor10_include_redir.ref    |    20 +
 scconfig/src/tmpasm/regression/Tutor11_missing.out |    10 +
 scconfig/src/tmpasm/regression/Tutor11_missing.ref |    10 +
 scconfig/src/tmpasm/regression/append.gasm         |     6 +
 scconfig/src/tmpasm/regression/append.ref          |    15 +
 scconfig/src/tmpasm/regression/comment.gasm        |     3 +
 scconfig/src/tmpasm/regression/comment.ref         |     4 +
 scconfig/src/tmpasm/regression/err_excess_end.gasm |     3 +
 scconfig/src/tmpasm/regression/err_excess_end.ref  |     6 +
 scconfig/src/tmpasm/regression/err_if_else.gasm    |     4 +
 scconfig/src/tmpasm/regression/err_if_else.ref     |     6 +
 scconfig/src/tmpasm/regression/err_if_end.gasm     |     2 +
 scconfig/src/tmpasm/regression/err_if_end.ref      |     6 +
 scconfig/src/tmpasm/regression/err_no_end.gasm     |     3 +
 scconfig/src/tmpasm/regression/err_no_end.ref      |     6 +
 scconfig/src/tmpasm/regression/err_switch_end.gasm |     2 +
 scconfig/src/tmpasm/regression/err_switch_end.ref  |     2 +
 .../src/tmpasm/regression/err_switch_nocond.gasm   |     3 +
 .../src/tmpasm/regression/err_switch_nocond.ref    |     2 +
 scconfig/src/tmpasm/regression/foreach.gasm        |    18 +
 scconfig/src/tmpasm/regression/foreach.ref         |    41 +
 scconfig/src/tmpasm/regression/if.gasm             |    10 +
 scconfig/src/tmpasm/regression/if.ref              |    15 +
 scconfig/src/tmpasm/regression/switch.gasm         |     8 +
 scconfig/src/tmpasm/regression/switch.ref          |    47 +
 scconfig/src/tmpasm/regression/test.gasm           |    47 +
 scconfig/src/tmpasm/regression/test.ref            |    62 +
 scconfig/src/tmpasm/regression/then.gasm           |     6 +
 scconfig/src/tmpasm/regression/then.ref            |    12 +
 scconfig/src/tmpasm/tester.c                       |    64 +
 scconfig/src/tmpasm/tmpasm.c                       |   807 ++
 scconfig/src/tmpasm/tmpasm.h                       |   178 +
 scconfig/src/tmpasm/tmpasm_scconfig.c              |   552 +
 scconfig/src/tmpasm/tmpasm_scconfig.h              |    10 +
 scconfig/src/userpass/INIT.c                       |     2 +
 scconfig/src/userpass/INIT.h                       |     1 +
 scconfig/src/userpass/Makefile.plugin              |    11 +
 scconfig/src/userpass/find_username.c              |    73 +
 scconfig/src/userpass/find_username.h              |     2 +
 scconfig/src/userpass/userpass.c                   |    12 +
 scconfig/src/util/arg_auto_menu.c                  |   106 +
 scconfig/src/util/arg_auto_menu.h                  |     5 +
 scconfig/src/util/arg_auto_set.c                   |    95 +
 scconfig/src/util/arg_auto_set.h                   |    53 +
 scconfig/src/util/ls.c                             |    79 +
 scconfig/template/debug.tmpasm                     |    10 +
 src/Makefile.dep                                   |  3679 ++++++
 src/Makefile.in                                    |   333 +
 src/action_act.c                                   |   110 +
 src/action_helper.c                                |  1242 ++
 src/action_helper.h                                |    76 +
 src/box.h                                          |   206 +
 src/buffer.c                                       |  1449 +++
 src/buffer.h                                       |    56 +
 src/buildin.c.in                                   |    14 +
 src/buildin.h                                      |     2 +
 src/change.c                                       |  2746 ++++
 src/change.h                                       |   122 +
 src/change_act.c                                   |  1582 +++
 src/check_icon.data                                |     4 +
 src/clip.c                                         |   111 +
 src/clip.h                                         |    44 +
 src/compat_dl.c                                    |    68 +
 src/compat_dl.h                                    |    51 +
 src/compat_fs.c                                    |   303 +
 src/compat_fs.h                                    |    14 +
 src/compat_inc.h.in                                |    36 +
 src/compat_lrealpath.c                             |   156 +
 src/compat_lrealpath.h                             |     7 +
 src/compat_misc.c                                  |   134 +
 src/compat_misc.h                                  |    42 +
 src/conf.c                                         |  1810 +++
 src/conf.h                                         |   358 +
 src/conf_act.c                                     |   310 +
 src/conf_core.c                                    |    58 +
 src/conf_core.h                                    |   200 +
 src/conf_hid.c                                     |   137 +
 src/conf_hid.h                                     |    73 +
 src/const.h                                        |   176 +
 src/copy.c                                         |   376 +
 src/copy.h                                         |    47 +
 src/create.c                                       |   952 ++
 src/create.h                                       |    81 +
 src/crosshair.c                                    |  1241 ++
 src/crosshair.h                                    |    55 +
 src/data.c                                         |   174 +
 src/data.h                                         |   110 +
 src/default.pcb                                    |    47 +
 src/default_font                                   |   773 ++
 src/dolists.h                                      |     6 +
 src/draw.c                                         |  1713 +++
 src/draw.h                                         |    83 +
 src/draw_fab.c                                     |   297 +
 src/draw_fab.h                                     |    39 +
 src/drill.c                                        |   243 +
 src/drill.h                                        |    31 +
 src/error.c                                        |   195 +
 src/error.h                                        |    59 +
 src/event.c                                        |   174 +
 src/event.h                                        |    83 +
 src/file_act.c                                     |   372 +
 src/find.c                                         |   183 +
 src/find.h                                         |    96 +
 src/find_act.c                                     |    73 +
 src/find_clear.c                                   |   148 +
 src/find_deadcode.c                                |   145 +
 src/find_debug.c                                   |   135 +
 src/find_drc.c                                     |   830 ++
 src/find_geo.c                                     |   696 +
 src/find_lookup.c                                  |  1425 ++
 src/find_misc.c                                    |   476 +
 src/find_print.c                                   |   288 +
 src/fptr_cast.c                                    |    37 +
 src/fptr_cast.h                                    |    34 +
 src/free_atexit.c                                  |   109 +
 src/free_atexit.h                                  |    38 +
 src/funchash.c                                     |   136 +
 src/funchash.h                                     |    47 +
 src/funchash_core.h                                |    10 +
 src/funchash_core_list.h                           |   126 +
 src/global.h                                       |   554 +
 src/global_element.h                               |    17 +
 src/global_objs.h                                  |   210 +
 src/global_typedefs.h                              |    49 +
 src/globalconst.h                                  |   119 +
 src/gui_act.c                                      |  1273 ++
 src/heap.c                                         |   236 +
 src/heap.h                                         |    62 +
 src/hid.h                                          |   578 +
 src/hid_actions.c                                  |   517 +
 src/hid_actions.h                                  |    35 +
 src/hid_attrib.c                                   |   226 +
 src/hid_attrib.h                                   |    63 +
 src/hid_cfg.c                                      |   516 +
 src/hid_cfg.h                                      |   113 +
 src/hid_cfg_action.c                               |    48 +
 src/hid_cfg_action.h                               |    27 +
 src/hid_cfg_input.c                                |   459 +
 src/hid_cfg_input.h                                |   144 +
 src/hid_color.c                                    |    41 +
 src/hid_color.h                                    |    13 +
 src/hid_draw_helpers.c                             |   436 +
 src/hid_draw_helpers.h                             |    10 +
 src/hid_extents.c                                  |   188 +
 src/hid_extents.h                                  |    12 +
 src/hid_flags.c                                    |   104 +
 src/hid_flags.h                                    |    18 +
 src/hid_helper.c                                   |   125 +
 src/hid_helper.h                                   |    19 +
 src/hid_init.c                                     |   228 +
 src/hid_init.h                                     |    47 +
 src/hid_nogui.c                                    |   467 +
 src/hid_nogui.h                                    |     7 +
 src/ht_element.c                                   |   216 +
 src/ht_element.h                                   |    11 +
 src/icons/Makefile.am                              |     4 +
 src/icons/hand.dat                                 |     9 +
 src/icons/hcurs.dat                                |     8 +
 src/icons/lcurs.dat                                |     6 +
 src/icons/lock.dat                                 |     9 +
 src/insert.c                                       |   278 +
 src/insert.h                                       |    42 +
 src/intersect.c                                    |   267 +
 src/intersect.h                                    |    43 +
 src/layer.c                                        |   831 ++
 src/layer.h                                        |   157 +
 src/line.c                                         |   534 +
 src/line.h                                         |    42 +
 src/list_arc.c                                     |    26 +
 src/list_arc.h                                     |    41 +
 src/list_common.h                                  |    35 +
 src/list_conf.c                                    |    26 +
 src/list_conf.h                                    |    49 +
 src/list_element.c                                 |    30 +
 src/list_element.h                                 |    79 +
 src/list_line.c                                    |    26 +
 src/list_line.h                                    |    41 +
 src/list_pad.c                                     |    26 +
 src/list_pad.h                                     |    41 +
 src/list_pin.c                                     |    26 +
 src/list_pin.h                                     |    41 +
 src/list_poly.c                                    |    26 +
 src/list_poly.h                                    |    41 +
 src/list_rat.c                                     |    26 +
 src/list_rat.h                                     |    41 +
 src/list_text.c                                    |    26 +
 src/list_text.h                                    |    41 +
 src/macro.h                                        |   436 +
 src/main.c                                         |   522 +
 src/main_act.c                                     |   271 +
 src/mirror.c                                       |    98 +
 src/mirror.h                                       |    44 +
 src/misc.c                                         |  1310 ++
 src/misc.h                                         |   103 +
 src/misc_util.c                                    |   181 +
 src/misc_util.h                                    |    51 +
 src/move.c                                         |  1051 ++
 src/move.h                                         |   106 +
 src/mymem.c                                        |   765 ++
 src/mymem.h                                        |   111 +
 src/netlist.c                                      |   257 +
 src/netlist.h                                      |    63 +
 src/netlist_act.c                                  |   348 +
 src/obj_any.c                                      |    27 +
 src/obj_any.h                                      |   120 +
 src/object_act.c                                   |  1024 ++
 src/paths.c                                        |    87 +
 src/paths.h                                        |    23 +
 src/pcb-conf.lht                                   |   250 +
 src/pcb-menu-gtk.lht                               |   607 +
 src/pcb-menu-lesstif.lht                           |   499 +
 src/pcb-menu-mkey.lht                              |   604 +
 src/pcb-printf.c                                   |   875 ++
 src/pcb-printf.h                                   |   126 +
 src/pcb_bool.h                                     |    31 +
 src/plug_footprint.c                               |   405 +
 src/plug_footprint.h                               |    90 +
 src/plug_footprint_act.c                           |    45 +
 src/plug_import.c                                  |   121 +
 src/plug_import.h                                  |    62 +
 src/plug_io.c                                      |   935 ++
 src/plug_io.h                                      |   170 +
 src/plugins.c                                      |   141 +
 src/plugins.h                                      |   108 +
 src/polyarea.h                                     |   180 +
 src/polygon.c                                      |  1846 +++
 src/polygon.h                                      |    90 +
 src/polygon1.c                                     |  3190 +++++
 src/polygon_act.c                                  |   144 +
 src/rats.c                                         |   921 ++
 src/rats.h                                         |    45 +
 src/rats_act.c                                     |   225 +
 src/rats_patch.c                                   |   500 +
 src/rats_patch.h                                   |    76 +
 src/remove.c                                       |   561 +
 src/remove.h                                       |    53 +
 src/remove_act.c                                   |   107 +
 src/rotate.c                                       |   389 +
 src/rotate.h                                       |   103 +
 src/route_style.c                                  |   183 +
 src/route_style.h                                  |    50 +
 src/rtree.c                                        |   999 ++
 src/rtree.h                                        |    83 +
 src/rubberband.c                                   |   492 +
 src/rubberband.h                                   |    38 +
 src/search.c                                       |  1396 ++
 src/search.h                                       |   162 +
 src/select.c                                       |   964 ++
 src/select.h                                       |    58 +
 src/select_act.c                                   |   388 +
 src/set.c                                          |   278 +
 src/set.h                                          |    50 +
 src/strflags.c                                     |   540 +
 src/strflags.h                                     |    77 +
 src/stub_mincut.c                                  |    43 +
 src/stub_mincut.h                                  |    25 +
 src/stub_stroke.c                                  |    38 +
 src/stub_stroke.h                                  |    26 +
 src/stub_vendor.c                                  |    38 +
 src/stub_vendor.h                                  |    32 +
 src/thermal.c                                      |   438 +
 src/thermal.h                                      |    45 +
 src/undo.c                                         |  1669 +++
 src/undo.h                                         |    76 +
 src/undo_act.c                                     |   299 +
 src/unit.c                                         |   281 +
 src/unit.h                                         |   124 +
 src/vtlibrary.c                                    |     3 +
 src/vtlibrary.h                                    |    74 +
 src/vtonpoint.c                                    |     3 +
 src/vtonpoint.h                                    |    64 +
 src/vtptr.c                                        |     3 +
 src/vtptr.h                                        |    36 +
 src/vtroutestyle.c                                 |     3 +
 src/vtroutestyle.h                                 |    74 +
 src_3rd/Makefile.conf                              |     1 +
 src_3rd/README                                     |     1 +
 src_3rd/genlist/Makefile                           |    17 +
 src_3rd/genlist/example_ad.c                       |    17 +
 src_3rd/genlist/example_d.c                        |    16 +
 src_3rd/genlist/genadlist.c                        |   335 +
 src_3rd/genlist/genadlist.h                        |   126 +
 src_3rd/genlist/gendlist.h                         |   395 +
 src_3rd/genlist/genlistalloc.c                     |    46 +
 src_3rd/genlist/genlistalloc.h                     |    45 +
 src_3rd/genlist/gentdlist_impl.c                   |   136 +
 src_3rd/genlist/gentdlist_impl.h                   |    41 +
 src_3rd/genlist/gentdlist_undef.h                  |     9 +
 src_3rd/genregex/Makefile                          |    35 +
 src_3rd/genregex/regex.c                           |    25 +
 src_3rd/genregex/regex.h                           |    25 +
 src_3rd/genregex/regex_be.c                        |     7 +
 src_3rd/genregex/regex_be.h                        |     6 +
 src_3rd/genregex/regex_bei.c                       |     7 +
 src_3rd/genregex/regex_bei.h                       |     6 +
 src_3rd/genregex/regex_bt.c                        |     7 +
 src_3rd/genregex/regex_bt.h                        |     6 +
 src_3rd/genregex/regex_bti.c                       |     7 +
 src_3rd/genregex/regex_bti.h                       |     6 +
 src_3rd/genregex/regex_se.c                        |     7 +
 src_3rd/genregex/regex_se.h                        |     4 +
 src_3rd/genregex/regex_sei.c                       |     7 +
 src_3rd/genregex/regex_sei.h                       |     4 +
 src_3rd/genregex/regex_st.c                        |     7 +
 src_3rd/genregex/regex_st.h                        |     4 +
 src_3rd/genregex/regex_sti.c                       |     7 +
 src_3rd/genregex/regex_sti.h                       |     4 +
 src_3rd/genregex/regex_templ.c                     |   831 ++
 src_3rd/genregex/regex_templ.h                     |    24 +
 src_3rd/genregex/tester.c                          |    81 +
 src_3rd/genregex/tester_main.c                     |    81 +
 src_3rd/gensexpr/Makefile                          |    23 +
 src_3rd/gensexpr/gensexpr_impl.c                   |   312 +
 src_3rd/gensexpr/gensexpr_impl.h                   |    58 +
 src_3rd/gensexpr/gsx_parse.c                       |   233 +
 src_3rd/gensexpr/gsx_parse.h                       |    32 +
 src_3rd/gensexpr/gsxl.c                            |     7 +
 src_3rd/gensexpr/gsxl.h                            |    26 +
 src_3rd/gensexpr/gsxnl.c                           |     6 +
 src_3rd/gensexpr/gsxnl.h                           |    24 +
 src_3rd/genvector/Makefile                         |    53 +
 src_3rd/genvector/gds_char.c                       |     3 +
 src_3rd/genvector/gds_char.h                       |    57 +
 src_3rd/genvector/gds_wchar.c                      |     3 +
 src_3rd/genvector/gds_wchar.h                      |    66 +
 src_3rd/genvector/genvector_impl.c                 |   498 +
 src_3rd/genvector/genvector_impl.h                 |    67 +
 src_3rd/genvector/genvector_undef.h                |    15 +
 src_3rd/genvector/vti0.c                           |     3 +
 src_3rd/genvector/vti0.h                           |    73 +
 src_3rd/genvector/vtii.c                           |     3 +
 src_3rd/genvector/vtii.h                           |    30 +
 src_3rd/genvector/vtp0.c                           |     3 +
 src_3rd/genvector/vtp0.h                           |    30 +
 src_3rd/genvector/vts0.c                           |     3 +
 src_3rd/genvector/vts0.h                           |    30 +
 src_3rd/liblhtpers/Makefile                        |    20 +
 src_3rd/liblhtpers/Makefile.dep                    |     6 +
 src_3rd/liblhtpers/lhtpers.c                       |   793 ++
 src_3rd/liblhtpers/lhtpers.h                       |   105 +
 src_3rd/liblhtpers/output.c                        |   428 +
 src_3rd/liblhtpers/pers_hash.c                     |    86 +
 src_3rd/liblhtpers/pers_list.c                     |   128 +
 src_3rd/liblhtpers/pers_table.c                    |   182 +
 src_3rd/liblhtpers/pers_text.c                     |    34 +
 src_3rd/liblhtpers/tests/Makefile                  |    26 +
 src_3rd/liblhtpers/tests/Makefile.dep              |     0
 src_3rd/liblhtpers/tests/edit/li1.lht              |    14 +
 src_3rd/liblhtpers/tests/edit/li1a.lht             |    13 +
 src_3rd/liblhtpers/tests/edit/li1b.lht             |    12 +
 src_3rd/liblhtpers/tests/edit/li1c.lht             |    13 +
 src_3rd/liblhtpers/tests/edit/li1d.lht             |    20 +
 src_3rd/liblhtpers/tests/edit/li1e.lht             |    13 +
 src_3rd/liblhtpers/tests/edit/li1f.lht             |     5 +
 src_3rd/liblhtpers/tests/edit/lit.lht              |    14 +
 src_3rd/liblhtpers/tests/edit/lita.lht             |    14 +
 src_3rd/liblhtpers/tests/list.lht                  |     8 +
 src_3rd/liblhtpers/tests/perstest.c                |   151 +
 src_3rd/liblhtpers/tests/roundtrip/Makefile        |    32 +
 src_3rd/liblhtpers/tests/roundtrip/list.lht        |     8 +
 src_3rd/liblhtpers/tests/roundtrip/test.sh         |    18 +
 src_3rd/liblhtpers/tests/roundtrip/text.lht        |    17 +
 src_3rd/liblhtpers/tests/roundtrip/text2.lht       |    15 +
 src_3rd/liblihata/Makefile                         |    69 +
 src_3rd/liblihata/Makefile.dep                     |    34 +
 src_3rd/liblihata/Makefile.module                  |     4 +
 src_3rd/liblihata/dom.c                            |   801 ++
 src_3rd/liblihata/dom.h                            |   214 +
 src_3rd/liblihata/dom_hash.c                       |    90 +
 src_3rd/liblihata/dom_internal.h                   |    50 +
 src_3rd/liblihata/dom_list.c                       |   122 +
 src_3rd/liblihata/dom_table.c                      |   244 +
 src_3rd/liblihata/genht/LICENSE                    |     1 +
 src_3rd/liblihata/genht/Makefile                   |    47 +
 src_3rd/liblihata/genht/hash.c                     |   158 +
 src_3rd/liblihata/genht/hash.h                     |    30 +
 src_3rd/liblihata/genht/ht.c                       |   239 +
 src_3rd/liblihata/genht/ht.h                       |   105 +
 src_3rd/liblihata/genht/ht_inlines.h               |    28 +
 src_3rd/liblihata/genht/htip.c                     |    10 +
 src_3rd/liblihata/genht/htip.h                     |    12 +
 src_3rd/liblihata/genht/htpi.c                     |     4 +
 src_3rd/liblihata/genht/htpi.h                     |    12 +
 src_3rd/liblihata/genht/htpp.c                     |     4 +
 src_3rd/liblihata/genht/htpp.h                     |    12 +
 src_3rd/liblihata/genht/htsi.c                     |     4 +
 src_3rd/liblihata/genht/htsi.h                     |    12 +
 src_3rd/liblihata/genht/htsp.c                     |     4 +
 src_3rd/liblihata/genht/htsp.h                     |    12 +
 src_3rd/liblihata/genht/htss.c                     |     4 +
 src_3rd/liblihata/genht/htss.h                     |    12 +
 src_3rd/liblihata/genht/mainsi.c                   |    62 +
 src_3rd/liblihata/genht/siphash24.c                |    75 +
 src_3rd/liblihata/genht/siphash24.h                |     4 +
 src_3rd/liblihata/hash_str.c                       |    17 +
 src_3rd/liblihata/hash_str.h                       |     3 +
 src_3rd/liblihata/lihata.c                         |   133 +
 src_3rd/liblihata/lihata.h                         |    69 +
 src_3rd/liblihata/parser.c                         |   469 +
 src_3rd/liblihata/parser.h                         |    62 +
 src_3rd/liblihata/regression/Eval.awk              |     7 +
 src_3rd/liblihata/regression/Makefile              |    44 +
 src_3rd/liblihata/regression/Makefile.test         |  1480 +++
 src_3rd/liblihata/regression/Makegen.sh            |   193 +
 src_3rd/liblihata/regression/dom_hash_get.tts      |    16 +
 src_3rd/liblihata/regression/dom_hash_put.tts      |    26 +
 src_3rd/liblihata/regression/dom_iterate.tts       |    40 +
 src_3rd/liblihata/regression/dom_list_append.tts   |    17 +
 src_3rd/liblihata/regression/dom_list_insert.tts   |    17 +
 src_3rd/liblihata/regression/dom_list_len.tts      |    10 +
 src_3rd/liblihata/regression/dom_load.tts          |     7 +
 src_3rd/liblihata/regression/dom_node_free.tts     |     4 +
 src_3rd/liblihata/regression/empty.lht             |     0
 src_3rd/liblihata/regression/empty_type_decl.lht   |     1 +
 src_3rd/liblihata/regression/err_brace_in_text.lht |     5 +
 src_3rd/liblihata/regression/err_eof_body.lht      |     2 +
 src_3rd/liblihata/regression/err_eof_comment.lht   |     3 +
 src_3rd/liblihata/regression/err_eof_comment2.lht  |     4 +
 src_3rd/liblihata/regression/err_nobody.lht        |     3 +
 .../liblihata/regression/excess_closing_braces.lht |     2 +
 src_3rd/liblihata/regression/export.tts            |    26 +
 src_3rd/liblihata/regression/hash.lht              |     8 +
 src_3rd/liblihata/regression/list.lht              |     8 +
 src_3rd/liblihata/regression/list.tts              |    36 +
 src_3rd/liblihata/regression/list_del_nth.lht      |     9 +
 src_3rd/liblihata/regression/list_del_nth.tts      |    70 +
 src_3rd/liblihata/regression/list_detach_child.tts |    48 +
 src_3rd/liblihata/regression/list_detach_nth.tts   |    51 +
 src_3rd/liblihata/regression/list_empty.lht        |     2 +
 .../liblihata/regression/list_first_symlink.lht    |    19 +
 .../regression/list_missing_last_semicolon.lht     |     1 +
 src_3rd/liblihata/regression/list_nthname.lht      |    12 +
 src_3rd/liblihata/regression/list_nthname.tts      |    48 +
 .../liblihata/regression/list_replace_child.tts    |    44 +
 src_3rd/liblihata/regression/merge_hash.lht        |    28 +
 .../liblihata/regression/merge_hash_recurse.tts    |    18 +
 src_3rd/liblihata/regression/merge_hash_simple.tts |    18 +
 src_3rd/liblihata/regression/merge_hash_sybad.tts  |    18 +
 src_3rd/liblihata/regression/merge_hash_sygood.tts |    17 +
 src_3rd/liblihata/regression/merge_list.tts        |    30 +
 src_3rd/liblihata/regression/merge_table.tts       |    30 +
 src_3rd/liblihata/regression/merge_text.tts        |    17 +
 src_3rd/liblihata/regression/node_is_under.tts     |    12 +
 src_3rd/liblihata/regression/nodes_for_testing.lht |    13 +
 src_3rd/liblihata/regression/path.tts              |    35 +
 src_3rd/liblihata/regression/ref/dom_hash_get.tref |    19 +
 src_3rd/liblihata/regression/ref/dom_hash_put.tref |    21 +
 src_3rd/liblihata/regression/ref/dom_iterate.tref  |    70 +
 .../liblihata/regression/ref/dom_list_append.tref  |    18 +
 .../liblihata/regression/ref/dom_list_insert.tref  |    18 +
 src_3rd/liblihata/regression/ref/dom_list_len.tref |     2 +
 src_3rd/liblihata/regression/ref/dom_load.tref     |    12 +
 .../liblihata/regression/ref/dom_node_free.tref    |     2 +
 src_3rd/liblihata/regression/ref/empty.dref        |     1 +
 src_3rd/liblihata/regression/ref/empty.eref        |     1 +
 .../liblihata/regression/ref/empty_type_decl.dref  |     1 +
 .../liblihata/regression/ref/empty_type_decl.eref  |     2 +
 .../regression/ref/err_brace_in_text.dref          |     1 +
 .../regression/ref/err_brace_in_text.eref          |     6 +
 src_3rd/liblihata/regression/ref/err_eof_body.dref |     1 +
 src_3rd/liblihata/regression/ref/err_eof_body.eref |     3 +
 .../liblihata/regression/ref/err_eof_comment.dref  |     1 +
 .../liblihata/regression/ref/err_eof_comment.eref  |     4 +
 .../liblihata/regression/ref/err_eof_comment2.dref |     2 +
 .../liblihata/regression/ref/err_eof_comment2.eref |     3 +
 src_3rd/liblihata/regression/ref/err_nobody.dref   |     1 +
 src_3rd/liblihata/regression/ref/err_nobody.eref   |     2 +
 .../regression/ref/excess_closing_braces.dref      |     2 +
 .../regression/ref/excess_closing_braces.eref      |     4 +
 src_3rd/liblihata/regression/ref/export.tref       |    68 +
 src_3rd/liblihata/regression/ref/hash.dref         |     5 +
 src_3rd/liblihata/regression/ref/hash.eref         |     6 +
 src_3rd/liblihata/regression/ref/list.dref         |     5 +
 src_3rd/liblihata/regression/ref/list.eref         |     6 +
 src_3rd/liblihata/regression/ref/list.tref         |    20 +
 src_3rd/liblihata/regression/ref/list_del_nth.dref |     6 +
 src_3rd/liblihata/regression/ref/list_del_nth.eref |     7 +
 src_3rd/liblihata/regression/ref/list_del_nth.tref |    55 +
 .../regression/ref/list_detach_child.tref          |    22 +
 .../liblihata/regression/ref/list_detach_nth.tref  |    44 +
 src_3rd/liblihata/regression/ref/list_empty.dref   |     1 +
 src_3rd/liblihata/regression/ref/list_empty.eref   |     2 +
 .../regression/ref/list_first_symlink.dref         |    15 +
 .../regression/ref/list_first_symlink.eref         |    17 +
 .../ref/list_missing_last_semicolon.dref           |     2 +
 .../ref/list_missing_last_semicolon.eref           |     3 +
 src_3rd/liblihata/regression/ref/list_nthname.dref |     9 +
 src_3rd/liblihata/regression/ref/list_nthname.eref |    10 +
 src_3rd/liblihata/regression/ref/list_nthname.tref |    32 +
 .../regression/ref/list_replace_child.tref         |    16 +
 src_3rd/liblihata/regression/ref/merge_hash.dref   |    23 +
 src_3rd/liblihata/regression/ref/merge_hash.eref   |    24 +
 .../regression/ref/merge_hash_recurse.tref         |    46 +
 .../regression/ref/merge_hash_simple.tref          |    47 +
 .../liblihata/regression/ref/merge_hash_sybad.tref |    49 +
 .../regression/ref/merge_hash_sygood.tref          |    46 +
 src_3rd/liblihata/regression/ref/merge_list.tref   |    24 +
 src_3rd/liblihata/regression/ref/merge_table.tref  |    40 +
 src_3rd/liblihata/regression/ref/merge_text.tref   |    29 +
 .../liblihata/regression/ref/node_is_under.tref    |     4 +
 .../regression/ref/nodes_for_testing.dref          |    12 +
 .../regression/ref/nodes_for_testing.eref          |    13 +
 src_3rd/liblihata/regression/ref/path.tref         |    11 +
 src_3rd/liblihata/regression/ref/quoting.dref      |    18 +
 src_3rd/liblihata/regression/ref/quoting.eref      |    19 +
 src_3rd/liblihata/regression/ref/revpath.dref      |    13 +
 src_3rd/liblihata/regression/ref/revpath.eref      |    14 +
 src_3rd/liblihata/regression/ref/revpath.tref      |    22 +
 src_3rd/liblihata/regression/ref/stream.dref       |     7 +
 src_3rd/liblihata/regression/ref/stream.eref       |     9 +
 src_3rd/liblihata/regression/ref/sy.dref           |     3 +
 src_3rd/liblihata/regression/ref/sy.eref           |     4 +
 src_3rd/liblihata/regression/ref/symlink.dref      |    16 +
 src_3rd/liblihata/regression/ref/symlink.eref      |    17 +
 .../regression/ref/symlink_is_broken.tref          |     2 +
 .../regression/ref/symlink_root_node.dref          |     1 +
 .../regression/ref/symlink_root_node.eref          |     3 +
 src_3rd/liblihata/regression/ref/ta.dref           |    42 +
 src_3rd/liblihata/regression/ref/ta.eref           |    43 +
 src_3rd/liblihata/regression/ref/ta_del_c.tref     |   169 +
 src_3rd/liblihata/regression/ref/ta_del_r.tref     |   146 +
 src_3rd/liblihata/regression/ref/ta_ins_c.tref     |    53 +
 src_3rd/liblihata/regression/ref/ta_ins_c2.tref    |   174 +
 src_3rd/liblihata/regression/ref/ta_ins_r.tref     |    16 +
 src_3rd/liblihata/regression/ref/ta_ins_r2.tref    |   102 +
 src_3rd/liblihata/regression/ref/table.dref        |     9 +
 src_3rd/liblihata/regression/ref/table.eref        |    10 +
 .../liblihata/regression/ref/table_corners.dref    |    23 +
 .../liblihata/regression/ref/table_corners.eref    |    24 +
 .../regression/ref/table_detach_cell.tref          |   304 +
 .../regression/ref/table_detach_child.tref         |    58 +
 .../liblihata/regression/ref/table_find_cell.tref  |    13 +
 .../liblihata/regression/ref/table_named_cell.dref |    26 +
 .../liblihata/regression/ref/table_named_cell.eref |    27 +
 .../liblihata/regression/ref/table_named_cell.tref |    31 +
 src_3rd/liblihata/regression/ref/table_read.tref   |    36 +
 .../regression/ref/table_replace_cell.tref         |   103 +
 .../regression/ref/table_replace_child.tref        |    79 +
 src_3rd/liblihata/regression/ref/text.dref         |    14 +
 src_3rd/liblihata/regression/ref/text.eref         |    15 +
 .../liblihata/regression/ref/text_root_node.dref   |     1 +
 .../liblihata/regression/ref/text_root_node.eref   |     2 +
 src_3rd/liblihata/regression/ref/tiny.dref         |     1 +
 src_3rd/liblihata/regression/ref/tiny.eref         |     2 +
 src_3rd/liblihata/regression/ref/tree_detach.tref  |    32 +
 .../liblihata/regression/ref/tree_dup_hash.tref    |    48 +
 .../liblihata/regression/ref/tree_dup_list.tref    |    12 +
 src_3rd/liblihata/regression/ref/tree_dup_sy.tref  |     8 +
 .../liblihata/regression/ref/tree_dup_table.tref   |    48 +
 .../liblihata/regression/ref/tree_has_symlink.tref |     3 +
 src_3rd/liblihata/regression/ref/tree_unlink.tref  |    29 +
 src_3rd/liblihata/regression/revpath.lht           |    12 +
 src_3rd/liblihata/regression/revpath.tts           |    63 +
 src_3rd/liblihata/regression/stream.lht            |    17 +
 src_3rd/liblihata/regression/sy.lht                |     4 +
 src_3rd/liblihata/regression/symlink.lht           |    17 +
 src_3rd/liblihata/regression/symlink_is_broken.tts |     8 +
 src_3rd/liblihata/regression/symlink_root_node.lht |     2 +
 src_3rd/liblihata/regression/ta.lht                |    22 +
 src_3rd/liblihata/regression/ta_del_c.tts          |    81 +
 src_3rd/liblihata/regression/ta_del_r.tts          |    84 +
 src_3rd/liblihata/regression/ta_ins_c.tts          |    93 +
 src_3rd/liblihata/regression/ta_ins_c2.tts         |    45 +
 src_3rd/liblihata/regression/ta_ins_r.tts          |    57 +
 src_3rd/liblihata/regression/ta_ins_r2.tts         |    99 +
 src_3rd/liblihata/regression/table.lht             |     9 +
 src_3rd/liblihata/regression/table_corners.lht     |    11 +
 src_3rd/liblihata/regression/table_detach_cell.tts |   100 +
 .../liblihata/regression/table_detach_child.tts    |    26 +
 src_3rd/liblihata/regression/table_find_cell.tts   |    47 +
 src_3rd/liblihata/regression/table_named_cell.lht  |    12 +
 src_3rd/liblihata/regression/table_named_cell.tts  |    20 +
 src_3rd/liblihata/regression/table_read.tts        |    72 +
 .../liblihata/regression/table_replace_cell.tts    |    43 +
 .../liblihata/regression/table_replace_child.tts   |    40 +
 src_3rd/liblihata/regression/text.lht              |    15 +
 src_3rd/liblihata/regression/text_root_node.lht    |     1 +
 src_3rd/liblihata/regression/tiny.lht              |     1 +
 src_3rd/liblihata/regression/tree_detach.tts       |    40 +
 src_3rd/liblihata/regression/tree_dup_hash.tts     |    13 +
 src_3rd/liblihata/regression/tree_dup_list.tts     |    13 +
 src_3rd/liblihata/regression/tree_dup_sy.tts       |    13 +
 src_3rd/liblihata/regression/tree_dup_table.tts    |    13 +
 src_3rd/liblihata/regression/tree_has_symlink.tts  |    14 +
 src_3rd/liblihata/regression/tree_unlink.tts       |    37 +
 src_3rd/liblihata/test_dom.c                       |    41 +
 src_3rd/liblihata/test_parser.c                    |    64 +
 src_3rd/liblihata/test_tree.c                      |   955 ++
 src_3rd/liblihata/tree.c                           |   262 +
 src_3rd/liblihata/tree.h                           |   164 +
 src_3rd/liblihata/tree_hash.c                      |    73 +
 src_3rd/liblihata/tree_list.c                      |   205 +
 src_3rd/liblihata/tree_path.c                      |   325 +
 src_3rd/liblihata/tree_symlink.c                   |    82 +
 src_3rd/liblihata/tree_table.c                     |   343 +
 src_3rd/qparse/Makefile                            |    10 +
 src_3rd/qparse/example.c                           |    26 +
 src_3rd/qparse/qparse.c                            |   142 +
 src_3rd/qparse/qparse.h                            |     9 +
 src_3rd/sphash/Makefile                            |    44 +
 src_3rd/sphash/TODO                                |     3 +
 src_3rd/sphash/debian/changelog                    |    51 +
 src_3rd/sphash/debian/control                      |    12 +
 src_3rd/sphash/debian/copyright                    |     3 +
 src_3rd/sphash/debian/rules                        |    56 +
 src_3rd/sphash/sphash.c                            |   584 +
 src_3rd/sphash/test.c                              |    15 +
 src_3rd/sphash/wenums                              |    14 +
 src_3rd/sphash/words                               |     9 +
 src_plugins/Buildin.tmpasm                         |    25 +
 src_plugins/Common_enabled.tmpasm                  |   118 +
 src_plugins/Disable.tmpasm                         |    14 +
 src_plugins/Plugin.tmpasm                          |    46 +
 src_plugins/README                                 |    12 +
 src_plugins/autocrop/Makefile                      |     5 +
 src_plugins/autocrop/Plug.tmpasm                   |     8 +
 src_plugins/autocrop/README                        |     5 +
 src_plugins/autocrop/autocrop.c                    |   247 +
 src_plugins/autoplace/Makefile                     |     5 +
 src_plugins/autoplace/Plug.tmpasm                  |     8 +
 src_plugins/autoplace/README                       |     5 +
 src_plugins/autoplace/action.c                     |    76 +
 src_plugins/autoplace/autoplace.c                  |   763 ++
 src_plugins/autoplace/autoplace.h                  |    40 +
 src_plugins/autoroute/Makefile                     |     5 +
 src_plugins/autoroute/Plug.tmpasm                  |     8 +
 src_plugins/autoroute/README                       |     5 +
 src_plugins/autoroute/action.c                     |   103 +
 src_plugins/autoroute/autoroute.c                  |  4720 +++++++
 src_plugins/autoroute/autoroute.h                  |    42 +
 src_plugins/autoroute/mtspace.c                    |   510 +
 src_plugins/autoroute/mtspace.h                    |    77 +
 src_plugins/autoroute/vector.c                     |   213 +
 src_plugins/autoroute/vector.h                     |    76 +
 src_plugins/boardflip/Makefile                     |     5 +
 src_plugins/boardflip/Plug.tmpasm                  |     8 +
 src_plugins/boardflip/README                       |     6 +
 src_plugins/boardflip/boardflip.c                  |   183 +
 src_plugins/dbus/HACKING                           |     5 +
 src_plugins/dbus/Makefile                          |     5 +
 src_plugins/dbus/Plug.tmpasm                       |    28 +
 src_plugins/dbus/README                            |     6 +
 src_plugins/dbus/dbus-pcbmain.c                    |   310 +
 src_plugins/dbus/dbus-pcbmain.h                    |    29 +
 src_plugins/dbus/dbus.c                            |   387 +
 src_plugins/dbus/dbus.mk                           |    11 +
 src_plugins/dbus/dbus.xml                          |    25 +
 src_plugins/diag/Makefile                          |     5 +
 src_plugins/diag/Plug.tmpasm                       |     8 +
 src_plugins/diag/README                            |     8 +
 src_plugins/diag/diag.c                            |   204 +
 src_plugins/diag/diag_conf.c                       |    41 +
 src_plugins/diag/diag_conf.h                       |     4 +
 src_plugins/distalign/Makefile                     |     5 +
 src_plugins/distalign/Plug.tmpasm                  |     8 +
 src_plugins/distalign/README                       |     8 +
 src_plugins/distalign/distalign.c                  |   643 +
 src_plugins/distaligntext/Makefile                 |     5 +
 src_plugins/distaligntext/Plug.tmpasm              |     8 +
 src_plugins/distaligntext/README                   |     5 +
 src_plugins/distaligntext/distaligntext.c          |   637 +
 src_plugins/djopt/Makefile                         |     6 +
 src_plugins/djopt/Plug.tmpasm                      |     9 +
 src_plugins/djopt/README                           |     5 +
 src_plugins/djopt/djopt.c                          |  2702 ++++
 src_plugins/djopt/djopt.h                          |    34 +
 src_plugins/djopt/djopt_conf.h                     |    14 +
 src_plugins/export_bboard/Makefile                 |     5 +
 src_plugins/export_bboard/Plug.tmpasm              |     8 +
 src_plugins/export_bboard/README                   |     5 +
 src_plugins/export_bboard/bboard.c                 |   614 +
 src_plugins/export_bom/Makefile                    |     5 +
 src_plugins/export_bom/Plug.tmpasm                 |     8 +
 src_plugins/export_bom/README                      |     5 +
 src_plugins/export_bom/bom.c                       |   325 +
 src_plugins/export_dsn/Makefile                    |     5 +
 src_plugins/export_dsn/Plug.tmpasm                 |     8 +
 src_plugins/export_dsn/README                      |     5 +
 src_plugins/export_dsn/dsn.c                       |   589 +
 src_plugins/export_dxf/Makefile                    |     5 +
 src_plugins/export_dxf/Plug.tmpasm                 |     8 +
 src_plugins/export_dxf/README                      |     5 +
 src_plugins/export_dxf/README.orig                 |    47 +
 src_plugins/export_dxf/dxf.c                       |  5978 +++++++++
 src_plugins/export_gcode/Makefile                  |     5 +
 src_plugins/export_gcode/Plug.tmpasm               |    17 +
 src_plugins/export_gcode/README                    |     5 +
 src_plugins/export_gcode/auxiliary.h               |    80 +
 src_plugins/export_gcode/bitmap.h                  |   104 +
 src_plugins/export_gcode/curve.c                   |   114 +
 src_plugins/export_gcode/curve.h                   |    76 +
 src_plugins/export_gcode/decompose.c               |   525 +
 src_plugins/export_gcode/decompose.h               |    15 +
 src_plugins/export_gcode/gcode.c                   |   918 ++
 src_plugins/export_gcode/gcode.h                   |     3 +
 src_plugins/export_gcode/lists.h                   |   285 +
 src_plugins/export_gcode/potracelib.h              |   130 +
 src_plugins/export_gcode/trace.c                   |  1292 ++
 src_plugins/export_gcode/trace.h                   |    14 +
 src_plugins/export_gerber/Makefile                 |     5 +
 src_plugins/export_gerber/Plug.tmpasm              |     8 +
 src_plugins/export_gerber/README                   |     5 +
 src_plugins/export_gerber/gerber.c                 |  1201 ++
 src_plugins/export_ipcd356/Makefile                |     5 +
 src_plugins/export_ipcd356/Plug.tmpasm             |     8 +
 src_plugins/export_ipcd356/README                  |     5 +
 src_plugins/export_ipcd356/ipcd356.c               |   627 +
 src_plugins/export_lpr/Makefile                    |     5 +
 src_plugins/export_lpr/Plug.tmpasm                 |     8 +
 src_plugins/export_lpr/README                      |     5 +
 src_plugins/export_lpr/lpr.c                       |   151 +
 src_plugins/export_nelma/Makefile                  |     5 +
 src_plugins/export_nelma/Plug.tmpasm               |    16 +
 src_plugins/export_nelma/README                    |     5 +
 src_plugins/export_nelma/nelma.c                   |  1026 ++
 src_plugins/export_openscad/Makefile               |     5 +
 src_plugins/export_openscad/Plug.tmpasm            |    12 +
 src_plugins/export_openscad/README                 |     5 +
 src_plugins/export_openscad/scad.c                 |  1177 ++
 src_plugins/export_openscad/scad.h                 |   137 +
 src_plugins/export_openscad/scadcomp.c             |   558 +
 src_plugins/export_openscad/scadproto.c            |   118 +
 src_plugins/export_png/Makefile                    |     5 +
 src_plugins/export_png/Plug.tmpasm                 |    16 +
 src_plugins/export_png/README                      |     5 +
 src_plugins/export_png/png.c                       |  1533 +++
 src_plugins/export_png/png.h                       |     2 +
 src_plugins/export_ps/Makefile                     |     5 +
 src_plugins/export_ps/Plug.tmpasm                  |     8 +
 src_plugins/export_ps/README                       |     5 +
 src_plugins/export_ps/eps.c                        |   638 +
 src_plugins/export_ps/ps.c                         |  1631 +++
 src_plugins/export_ps/ps.h                         |     7 +
 src_plugins/export_svg/Makefile                    |     5 +
 src_plugins/export_svg/Plug.tmpasm                 |     8 +
 src_plugins/export_svg/README                      |     5 +
 src_plugins/export_svg/svg.c                       |   735 ++
 src_plugins/export_test/Makefile                   |     6 +
 src_plugins/export_test/Plug.tmpasm                |     9 +
 src_plugins/export_test/README                     |     6 +
 src_plugins/export_test/export_test.c              |   352 +
 src_plugins/export_xy/Makefile                     |     5 +
 src_plugins/export_xy/Plug.tmpasm                  |     8 +
 src_plugins/export_xy/README                       |     5 +
 src_plugins/export_xy/xy.c                         |   384 +
 src_plugins/fontmode/Makefile                      |     5 +
 src_plugins/fontmode/Plug.tmpasm                   |     8 +
 src_plugins/fontmode/README                        |     5 +
 src_plugins/fontmode/fontmode.c                    |   245 +
 src_plugins/fp_fs/Makefile                         |     5 +
 src_plugins/fp_fs/Plug.tmpasm                      |     8 +
 src_plugins/fp_fs/README                           |     7 +
 src_plugins/fp_fs/fp_fs.c                          |   513 +
 src_plugins/fp_wget/Makefile                       |    12 +
 src_plugins/fp_wget/Plug.tmpasm                    |     8 +
 src_plugins/fp_wget/README                         |     6 +
 src_plugins/fp_wget/fp_wget.c                      |    14 +
 src_plugins/fp_wget/gedasymbols.c                  |   197 +
 src_plugins/fp_wget/gedasymbols.h                  |     7 +
 src_plugins/fp_wget/tester.c                       |    39 +
 src_plugins/fp_wget/wget_common.c                  |   107 +
 src_plugins/fp_wget/wget_common.h                  |     9 +
 src_plugins/gl/README                              |     6 +
 src_plugins/gl/hidgl.c                             |   723 ++
 src_plugins/gl/hidgl.h                             |    79 +
 src_plugins/gl/todo.tmpasm                         |     6 +
 src_plugins/gpmi/Makefile                          |     6 +
 src_plugins/gpmi/Plug.tmpasm                       |   102 +
 src_plugins/gpmi/README                            |     8 +
 src_plugins/gpmi/pcb-gpmi/Makefile                 |    28 +
 src_plugins/gpmi/pcb-gpmi/Makefile.config.in       |    16 +
 src_plugins/gpmi/pcb-gpmi/gpmi_plugin/Makefile     |    37 +
 .../gpmi_plugin/gpmi_pkg/actions/Makefile.am       |    15 +
 .../gpmi_plugin/gpmi_pkg/actions/Makefile.dep      |     1 +
 .../gpmi_plugin/gpmi_pkg/actions/actions.c         |   102 +
 .../gpmi_plugin/gpmi_pkg/actions/actions.h         |    56 +
 .../gpmi_plugin/gpmi_pkg/actions/gpmi.conf         |    18 +
 .../gpmi_plugin/gpmi_pkg/actions/gpmi/package.c    |    19 +
 .../gpmi_plugin/gpmi_pkg/coordgeo/Makefile.am      |    17 +
 .../gpmi_plugin/gpmi_pkg/coordgeo/Makefile.dep     |     1 +
 .../gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.c       |   580 +
 .../gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.h       |   127 +
 .../gpmi_plugin/gpmi_pkg/coordgeo/gpmi.conf        |    18 +
 .../gpmi_plugin/gpmi_pkg/coordgeo/gpmi/package.c   |    19 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/test.c  |   199 +
 .../gpmi_plugin/gpmi_pkg/dialogs/Makefile.am       |    16 +
 .../gpmi_plugin/gpmi_pkg/dialogs/Makefile.dep      |     1 +
 .../gpmi_plugin/gpmi_pkg/dialogs/dialogs.c         |    91 +
 .../gpmi_plugin/gpmi_pkg/dialogs/dialogs.h         |    78 +
 .../gpmi_plugin/gpmi_pkg/dialogs/gpmi.conf         |    18 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.am  |    13 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.dep |     1 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi.conf    |    20 +
 .../gpmi_plugin/gpmi_pkg/hid/gpmi/package.c        |    19 +
 .../gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.c   |   308 +
 .../gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.h   |    90 +
 .../gpmi_plugin/gpmi_pkg/hid/hid_callbacks.c       |   180 +
 .../gpmi_plugin/gpmi_pkg/hid/hid_callbacks.h       |    22 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_events.h |    63 +
 .../gpmi_plugin/gpmi_pkg/layout/Makefile.am        |    20 +
 .../gpmi_plugin/gpmi_pkg/layout/Makefile.dep       |     1 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/coord.c   |    21 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/create.c  |   117 +
 .../gpmi_plugin/gpmi_pkg/layout/debug_draw.c       |    49 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/draw.c    |    45 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/gpmi.conf |    18 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layers.c  |    77 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layout.h  |   267 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/object.c  |   145 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/page.c    |    18 +
 .../pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/search.c  |   186 +
 .../gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c        |   234 +
 .../gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h        |     4 +
 .../gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.c     |   244 +
 .../gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.h     |     1 +
 src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.c    |   386 +
 src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.h    |    42 +
 src_plugins/hid_batch/Makefile                     |     6 +
 src_plugins/hid_batch/Plug.tmpasm                  |     8 +
 src_plugins/hid_batch/README                       |     5 +
 src_plugins/hid_batch/batch.c                      |   411 +
 src_plugins/hid_gtk/Makefile                       |     6 +
 src_plugins/hid_gtk/Plug.tmpasm                    |    52 +
 src_plugins/hid_gtk/README                         |     5 +
 .../hid_gtk/ghid-cell-renderer-visibility.c        |   255 +
 .../hid_gtk/ghid-cell-renderer-visibility.h        |    21 +
 src_plugins/hid_gtk/ghid-coord-entry.c             |   289 +
 src_plugins/hid_gtk/ghid-coord-entry.h             |    34 +
 src_plugins/hid_gtk/ghid-layer-selector.c          |   806 ++
 src_plugins/hid_gtk/ghid-layer-selector.h          |    38 +
 src_plugins/hid_gtk/ghid-main-menu.c               |   521 +
 src_plugins/hid_gtk/ghid-main-menu.h               |    40 +
 src_plugins/hid_gtk/ghid-propedit.c                |   559 +
 src_plugins/hid_gtk/ghid-propedit.h                |    44 +
 src_plugins/hid_gtk/ghid-route-style-selector.c    |   659 +
 src_plugins/hid_gtk/ghid-route-style-selector.h    |    34 +
 src_plugins/hid_gtk/ghid-search-tab.h              |   171 +
 src_plugins/hid_gtk/ghid-search.c                  |   856 ++
 src_plugins/hid_gtk/ghid-search.h                  |    26 +
 src_plugins/hid_gtk/gschem_accel_label.c           |   341 +
 src_plugins/hid_gtk/gschem_accel_label.h           |    77 +
 src_plugins/hid_gtk/gtk_conf_list.c                |   323 +
 src_plugins/hid_gtk/gtk_conf_list.h                |    35 +
 src_plugins/hid_gtk/gtk_debug.h                    |    26 +
 src_plugins/hid_gtk/gtkhid-gdk.c                   |  1378 ++
 src_plugins/hid_gtk/gtkhid-gl-config.h             |    17 +
 src_plugins/hid_gtk/gtkhid-gl.c                    |  1250 ++
 src_plugins/hid_gtk/gtkhid-main.c                  |  2155 ++++
 src_plugins/hid_gtk/gtkhid.h                       |    14 +
 src_plugins/hid_gtk/gui-command-window.c           |   431 +
 src_plugins/hid_gtk/gui-config.c                   |  2836 ++++
 src_plugins/hid_gtk/gui-dialog-print.c             |   408 +
 src_plugins/hid_gtk/gui-dialog.c                   |   553 +
 src_plugins/hid_gtk/gui-drc-window.c               |   806 ++
 src_plugins/hid_gtk/gui-drc-window.h               |    93 +
 src_plugins/hid_gtk/gui-icons-misc.data            |   111 +
 src_plugins/hid_gtk/gui-icons-mode-buttons.data    |   510 +
 src_plugins/hid_gtk/gui-keyref-window.c            |   352 +
 src_plugins/hid_gtk/gui-library-window.c           |   868 ++
 src_plugins/hid_gtk/gui-library-window.h           |    59 +
 src_plugins/hid_gtk/gui-log-window.c               |   201 +
 src_plugins/hid_gtk/gui-misc.c                     |   420 +
 src_plugins/hid_gtk/gui-netlist-window.c           |   968 ++
 src_plugins/hid_gtk/gui-output-events.c            |   534 +
 src_plugins/hid_gtk/gui-pinout-preview.c           |   297 +
 src_plugins/hid_gtk/gui-pinout-preview.h           |    61 +
 src_plugins/hid_gtk/gui-pinout-window.c            |    90 +
 src_plugins/hid_gtk/gui-top-window.c               |  1766 +++
 src_plugins/hid_gtk/gui-utils.c                    |   745 ++
 src_plugins/hid_gtk/gui.h                          |   527 +
 src_plugins/hid_gtk/hid_gtk_conf.h                 |    96 +
 src_plugins/hid_gtk/pcb.rc                         |     1 +
 src_plugins/hid_gtk/win_place.c                    |   108 +
 src_plugins/hid_gtk/win_place.h                    |    22 +
 src_plugins/hid_lesstif/Makefile                   |     6 +
 src_plugins/hid_lesstif/Plug.tmpasm                |    41 +
 src_plugins/hid_lesstif/README                     |     5 +
 src_plugins/hid_lesstif/dialogs.c                  |  1948 +++
 src_plugins/hid_lesstif/lesstif.h                  |    84 +
 src_plugins/hid_lesstif/library.c                  |   195 +
 src_plugins/hid_lesstif/main.c                     |  3854 ++++++
 src_plugins/hid_lesstif/menu.c                     |   979 ++
 src_plugins/hid_lesstif/netlist.c                  |   441 +
 src_plugins/hid_lesstif/stdarg.c                   |    15 +
 src_plugins/hid_lesstif/stdarg.h                   |    10 +
 src_plugins/hid_lesstif/styles.c                   |   440 +
 src_plugins/hid_lesstif/xincludes.h                |    46 +
 src_plugins/import_dsn/Makefile                    |     5 +
 src_plugins/import_dsn/Plug.tmpasm                 |     8 +
 src_plugins/import_dsn/README                      |     5 +
 src_plugins/import_dsn/dsn.c                       |   177 +
 src_plugins/import_edif/Makefile                   |     6 +
 src_plugins/import_edif/Plug.tmpasm                |     9 +
 src_plugins/import_edif/README                     |     5 +
 src_plugins/import_edif/edif.c                     |  6243 +++++++++
 src_plugins/import_edif/edif.h                     |   365 +
 src_plugins/import_edif/edif.y                     |  4455 +++++++
 src_plugins/import_edif/edif_parse.h               |    29 +
 src_plugins/import_edif/import_edif.c              |    93 +
 src_plugins/import_netlist/Makefile                |     6 +
 src_plugins/import_netlist/Plug.tmpasm             |     9 +
 src_plugins/import_netlist/README                  |     5 +
 src_plugins/import_netlist/import_netlist.c        |   188 +
 src_plugins/import_sch/Makefile                    |     5 +
 src_plugins/import_sch/Plug.tmpasm                 |     9 +
 src_plugins/import_sch/README                      |     5 +
 src_plugins/import_sch/import_sch.c                |   481 +
 src_plugins/import_sch/import_sch_conf.h           |    15 +
 src_plugins/io_kicad/Makefile                      |     5 +
 src_plugins/io_kicad/Plug.tmpasm                   |    15 +
 src_plugins/io_kicad/README                        |     6 +
 src_plugins/io_kicad/hacking.txt                   |    87 +
 src_plugins/io_kicad/io_kicad.c                    |    74 +
 src_plugins/io_kicad/read.c                        |  2081 +++
 src_plugins/io_kicad/read.h                        |    33 +
 src_plugins/io_kicad/uniq_name.c                   |    76 +
 src_plugins/io_kicad/uniq_name.h                   |    32 +
 src_plugins/io_kicad/write.c                       |  1304 ++
 src_plugins/io_kicad/write.h                       |    48 +
 src_plugins/io_kicad_legacy/HACKING                |    36 +
 src_plugins/io_kicad_legacy/Makefile               |     5 +
 src_plugins/io_kicad_legacy/Plug.tmpasm            |    11 +
 src_plugins/io_kicad_legacy/README                 |     5 +
 src_plugins/io_kicad_legacy/io_kicad_legacy.c      |    74 +
 src_plugins/io_kicad_legacy/write.c                |  1231 ++
 src_plugins/io_kicad_legacy/write.h                |    48 +
 src_plugins/io_lihata/Makefile                     |     5 +
 src_plugins/io_lihata/Plug.tmpasm                  |    16 +
 src_plugins/io_lihata/README                       |     5 +
 src_plugins/io_lihata/TODO.check                   |     4 +
 src_plugins/io_lihata/common.c                     |    65 +
 src_plugins/io_lihata/common.h                     |     8 +
 src_plugins/io_lihata/io_lihata.c                  |    75 +
 src_plugins/io_lihata/io_lihata.h                  |    26 +
 src_plugins/io_lihata/lht_conf.h                   |    14 +
 src_plugins/io_lihata/read.c                       |  1009 ++
 src_plugins/io_lihata/read.h                       |    25 +
 src_plugins/io_lihata/write.c                      |   802 ++
 src_plugins/io_lihata/write.h                      |    23 +
 src_plugins/io_lihata/write_style.c                |   517 +
 src_plugins/io_lihata/write_style.h                |    29 +
 src_plugins/io_pcb/Makefile                        |     5 +
 src_plugins/io_pcb/Plug.tmpasm                     |    10 +
 src_plugins/io_pcb/README                          |     5 +
 src_plugins/io_pcb/attribs.c                       |   127 +
 src_plugins/io_pcb/attribs.h                       |     2 +
 src_plugins/io_pcb/file.c                          |   581 +
 src_plugins/io_pcb/file.h                          |    62 +
 src_plugins/io_pcb/flags.c                         |    68 +
 src_plugins/io_pcb/flags.h                         |   117 +
 src_plugins/io_pcb/io_pcb.c                        |   100 +
 src_plugins/io_pcb/parse_common.h                  |    41 +
 src_plugins/io_pcb/parse_l.c                       |  2602 ++++
 src_plugins/io_pcb/parse_l.h                       |   332 +
 src_plugins/io_pcb/parse_l.l                       |   466 +
 src_plugins/io_pcb/parse_y.c                       |  3258 +++++
 src_plugins/io_pcb/parse_y.h                       |   123 +
 src_plugins/io_pcb/parse_y.y                       |  2082 +++
 src_plugins/jostle/Makefile                        |     5 +
 src_plugins/jostle/Plug.tmpasm                     |     8 +
 src_plugins/jostle/README                          |     5 +
 src_plugins/jostle/jostle.c                        |   563 +
 src_plugins/lib_gensexpr/Makefile                  |     6 +
 src_plugins/lib_gensexpr/Plug.tmpasm               |    12 +
 src_plugins/lib_gensexpr/README                    |     5 +
 src_plugins/lib_gensexpr/lib_gensexpr.c            |     7 +
 src_plugins/lib_legacy_func/Makefile               |     5 +
 src_plugins/lib_legacy_func/Plug.tmpasm            |     8 +
 src_plugins/lib_legacy_func/README                 |     8 +
 src_plugins/lib_legacy_func/lib_legacy_func.c      |   114 +
 src_plugins/lib_legacy_func/lib_legacy_func.h      |    42 +
 src_plugins/loghid/Makefile                        |     6 +
 src_plugins/loghid/Plug.tmpasm                     |    10 +
 src_plugins/loghid/README                          |     6 +
 src_plugins/loghid/hid-logger.c                    |   232 +
 src_plugins/loghid/hid-logger.h                    |    14 +
 src_plugins/loghid/loghid.c                        |   139 +
 src_plugins/mincut/Makefile                        |     6 +
 src_plugins/mincut/Plug.tmpasm                     |     9 +
 src_plugins/mincut/README                          |     7 +
 src_plugins/mincut/pcb-mincut/COPYING              |   339 +
 src_plugins/mincut/pcb-mincut/H.in                 |     8 +
 src_plugins/mincut/pcb-mincut/Makefile             |    14 +
 src_plugins/mincut/pcb-mincut/README               |    21 +
 src_plugins/mincut/pcb-mincut/graph.c              |   140 +
 src_plugins/mincut/pcb-mincut/graph.h              |    98 +
 src_plugins/mincut/pcb-mincut/load.c               |   262 +
 src_plugins/mincut/pcb-mincut/load.h               |     5 +
 src_plugins/mincut/pcb-mincut/main.c               |    34 +
 src_plugins/mincut/pcb-mincut/proposals.txt        |    99 +
 src_plugins/mincut/pcb-mincut/solve.c              |   254 +
 src_plugins/mincut/pcb-mincut/solve.h              |     5 +
 src_plugins/mincut/pcb-mincut/test_cases/C.in      |     9 +
 src_plugins/mincut/pcb-mincut/test_cases/C.ref     |     1 +
 src_plugins/mincut/pcb-mincut/test_cases/H.in      |     8 +
 src_plugins/mincut/pcb-mincut/test_cases/H.ref     |     1 +
 src_plugins/mincut/pcb-mincut/test_cases/H2.in     |    16 +
 src_plugins/mincut/pcb-mincut/test_cases/H2.ref    |     1 +
 src_plugins/mincut/pcb-mincut/test_cases/H3.in     |    18 +
 src_plugins/mincut/pcb-mincut/test_cases/H3.ref    |     1 +
 src_plugins/mincut/pcb-mincut/test_cases/H4.in     |    12 +
 src_plugins/mincut/pcb-mincut/test_cases/H4.ref    |     2 +
 src_plugins/mincut/pcb-mincut/test_cases/Makefile  |    12 +
 src_plugins/mincut/pcb-mincut/test_cases/O.in      |     9 +
 src_plugins/mincut/pcb-mincut/test_cases/O.ref     |     2 +
 src_plugins/mincut/pcb-mincut/test_cases/rats1.in  |    12 +
 src_plugins/mincut/pcb-mincut/test_cases/rats1.ref |     2 +
 src_plugins/mincut/pcb-mincut/test_cases/rats2.in  |     8 +
 src_plugins/mincut/pcb-mincut/test_cases/rats2.ref |     3 +
 src_plugins/mincut/pcb-mincut/test_cases/rats3.in  |     9 +
 src_plugins/mincut/pcb-mincut/test_cases/rats3.ref |     2 +
 .../mincut/pcb-mincut/test_cases/redundant.in      |    20 +
 src_plugins/mincut/rats_mincut.c                   |   419 +
 src_plugins/mincut/rats_mincut.h                   |    27 +
 src_plugins/mincut/rats_mincut_conf.h              |    14 +
 src_plugins/oldactions/Makefile                    |     5 +
 src_plugins/oldactions/Plug.tmpasm                 |     8 +
 src_plugins/oldactions/README                      |     9 +
 src_plugins/oldactions/oldactions.c                |   320 +
 src_plugins/plugins_ALL.tmpasm                     |     4 +
 src_plugins/plugins_feature.tmpasm                 |    33 +
 src_plugins/plugins_fp.tmpasm                      |     6 +
 src_plugins/plugins_io.tmpasm                      |    31 +
 src_plugins/polycombine/Makefile                   |     5 +
 src_plugins/polycombine/Plug.tmpasm                |     8 +
 src_plugins/polycombine/README                     |     6 +
 src_plugins/polycombine/polycombine.c              |   362 +
 src_plugins/polystitch/Makefile                    |     5 +
 src_plugins/polystitch/Plug.tmpasm                 |     8 +
 src_plugins/polystitch/README                      |     8 +
 src_plugins/polystitch/polystitch.c                |   262 +
 src_plugins/propedit/Makefile                      |     5 +
 src_plugins/propedit/Plug.tmpasm                   |     8 +
 src_plugins/propedit/README                        |     5 +
 src_plugins/propedit/propedit.c                    |    93 +
 src_plugins/propedit/props.c                       |   319 +
 src_plugins/propedit/props.h                       |   112 +
 src_plugins/propedit/propsel.c                     |   565 +
 src_plugins/propedit/propsel.h                     |    26 +
 src_plugins/puller/Makefile                        |     6 +
 src_plugins/puller/Plug.tmpasm                     |     8 +
 src_plugins/puller/README                          |     5 +
 src_plugins/puller/puller.c                        |  2406 ++++
 src_plugins/query/Makefile                         |     6 +
 src_plugins/query/Plug.tmpasm                      |    21 +
 src_plugins/query/README                           |     6 +
 src_plugins/query/basic_fnc.c                      |    42 +
 src_plugins/query/fields.sphash                    |    38 +
 src_plugins/query/query.c                          |   294 +
 src_plugins/query/query.h                          |   152 +
 src_plugins/query/query_access.c                   |   738 ++
 src_plugins/query/query_access.h                   |    41 +
 src_plugins/query/query_act.c                      |   176 +
 src_plugins/query/query_exec.c                     |   465 +
 src_plugins/query/query_exec.h                     |    80 +
 src_plugins/query/query_l.c                        |  2248 ++++
 src_plugins/query/query_l.h                        |   332 +
 src_plugins/query/query_l.l                        |   141 +
 src_plugins/query/query_y.c                        |  2016 +++
 src_plugins/query/query_y.h                        |    95 +
 src_plugins/query/query_y.y                        |   275 +
 src_plugins/renumber/Makefile                      |     6 +
 src_plugins/renumber/Plug.tmpasm                   |     8 +
 src_plugins/renumber/README                        |     6 +
 src_plugins/renumber/renumber.c                    |   382 +
 src_plugins/renumber/renumberblock.c               |   121 +
 src_plugins/report/Makefile                        |     6 +
 src_plugins/report/Plug.tmpasm                     |     9 +
 src_plugins/report/README                          |     5 +
 src_plugins/report/report.c                        |   915 ++
 src_plugins/report/report.h                        |    37 +
 src_plugins/report/report_conf.h                   |    16 +
 src_plugins/shand_cmd/Makefile                     |     6 +
 src_plugins/shand_cmd/Plug.tmpasm                  |     8 +
 src_plugins/shand_cmd/README                       |     5 +
 src_plugins/shand_cmd/command.c                    |   414 +
 src_plugins/shand_cmd/command.h                    |    37 +
 src_plugins/smartdisperse/Makefile                 |     5 +
 src_plugins/smartdisperse/Plug.tmpasm              |     8 +
 src_plugins/smartdisperse/README                   |    10 +
 src_plugins/smartdisperse/smartdisperse.c          |   284 +
 src_plugins/stroke/Makefile                        |     5 +
 src_plugins/stroke/Plug.tmpasm                     |    17 +
 src_plugins/stroke/README                          |     5 +
 src_plugins/stroke/stroke.c                        |   167 +
 src_plugins/teardrops/Makefile                     |     5 +
 src_plugins/teardrops/Plug.tmpasm                  |     8 +
 src_plugins/teardrops/README                       |     5 +
 src_plugins/teardrops/teardrops.c                  |   329 +
 src_plugins/toporouter/Makefile                    |     6 +
 src_plugins/toporouter/Plug.tmpasm                 |    24 +
 src_plugins/toporouter/README                      |     7 +
 src_plugins/toporouter/toporouter.c                |  8242 ++++++++++++
 src_plugins/toporouter/toporouter.h                |   492 +
 src_plugins/vendordrill/Makefile                   |     6 +
 src_plugins/vendordrill/Plug.tmpasm                |     9 +
 src_plugins/vendordrill/README                     |     5 +
 src_plugins/vendordrill/vendor.c                   |   698 +
 src_plugins/vendordrill/vendor.example.lht         |    51 +
 src_plugins/vendordrill/vendor.h                   |    29 +
 src_plugins/vendordrill/vendor_conf.h              |    14 +
 tests/Makefile                                     |     7 +
 tests/conf/Makefile                                |    55 +
 tests/conf/conftest.c                              |   381 +
 tests/conf/help.c                                  |    90 +
 tests/conf/tests/Makefile                          |    24 +
 tests/conf/tests/arr_merge.ref                     |    31 +
 tests/conf/tests/arr_merge.test                    |    49 +
 tests/conf/tests/arr_set.ref                       |    34 +
 tests/conf/tests/arr_set.test                      |    42 +
 tests/conf/tests/list_merge.ref                    |    13 +
 tests/conf/tests/list_merge.test                   |    47 +
 tests/conf/tests/list_set.ref                      |    10 +
 tests/conf/tests/list_set.test                     |    29 +
 tests/conf/tests/scalar.ref                        |    11 +
 tests/conf/tests/scalar.test                       |    29 +
 tests/gsch2pcb-rnd/Makefile.common                 |     9 +
 tests/gsch2pcb-rnd/simple/Makefile                 |     9 +
 tests/gsch2pcb-rnd/simple/main.cmd.ref             |    26 +
 tests/gsch2pcb-rnd/simple/main.net.ref             |     3 +
 tests/gsch2pcb-rnd/simple/main.out.ref             |    20 +
 tests/gsch2pcb-rnd/simple/main.pcb.ref             |   136 +
 tests/gsch2pcb-rnd/simple/main.sch                 |    80 +
 tests/orig/README.txt                              |   106 +
 tests/orig/golden/hid_bom1/bom_general.bom         |    10 +
 tests/orig/golden/hid_bom1/bom_general.xy          |    32 +
 tests/orig/golden/hid_bom2/bom_general.xy          |    32 +
 tests/orig/golden/hid_bom2/test.bom                |    10 +
 tests/orig/golden/hid_bom3/bom_general.bom         |    10 +
 tests/orig/golden/hid_bom3/test.xy                 |    32 +
 tests/orig/golden/hid_bom4/bom_general.bom         |    10 +
 tests/orig/golden/hid_bom4/bom_general.xy          |    32 +
 .../hid_gcode1/gcode_oneline.gcode.bottom.cnc      |    34 +
 .../hid_gcode1/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode1/gcode_oneline.gcode.top.cnc  |    34 +
 .../hid_gcode10/gcode_oneline.gcode.bottom.cnc     |    34 +
 .../hid_gcode10/gcode_oneline.gcode.drill.cnc      |    12 +
 .../golden/hid_gcode10/gcode_oneline.gcode.top.cnc |    34 +
 .../hid_gcode11/gcode_oneline.gcode.bottom.cnc     |    49 +
 .../hid_gcode11/gcode_oneline.gcode.drill.cnc      |    12 +
 .../golden/hid_gcode11/gcode_oneline.gcode.top.cnc |    45 +
 tests/orig/golden/hid_gcode2/out.bottom.cnc        |    34 +
 tests/orig/golden/hid_gcode2/out.drill.cnc         |    12 +
 tests/orig/golden/hid_gcode2/out.top.cnc           |    34 +
 .../hid_gcode3/gcode_oneline.gcode.bottom.cnc      |    40 +
 .../hid_gcode3/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode3/gcode_oneline.gcode.top.cnc  |    40 +
 .../hid_gcode4/gcode_oneline.gcode.bottom.cnc      |    34 +
 .../hid_gcode4/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode4/gcode_oneline.gcode.top.cnc  |    34 +
 .../hid_gcode5/gcode_oneline.gcode.bottom.cnc      |    34 +
 .../hid_gcode5/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode5/gcode_oneline.gcode.top.cnc  |    34 +
 .../hid_gcode6/gcode_oneline.gcode.bottom.cnc      |    43 +
 .../hid_gcode6/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode6/gcode_oneline.gcode.top.cnc  |    43 +
 .../hid_gcode7/gcode_oneline.gcode.bottom.cnc      |    34 +
 .../hid_gcode7/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode7/gcode_oneline.gcode.top.cnc  |    34 +
 .../hid_gcode8/gcode_oneline.gcode.bottom.cnc      |    34 +
 .../hid_gcode8/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode8/gcode_oneline.gcode.top.cnc  |    34 +
 .../hid_gcode9/gcode_oneline.gcode.bottom.cnc      |    34 +
 .../hid_gcode9/gcode_oneline.gcode.drill.cnc       |    12 +
 .../golden/hid_gcode9/gcode_oneline.gcode.top.cnc  |    34 +
 .../golden/hid_gerber1/gerber_oneline.bottom.gbr   |    17 +
 .../orig/golden/hid_gerber1/gerber_oneline.fab.gbr |  1734 +++
 .../hid_gerber1/gerber_oneline.plated-drill.cnc    |     7 +
 .../orig/golden/hid_gerber1/gerber_oneline.top.gbr |    17 +
 tests/orig/golden/hid_gerber2/out.bottom.gbr       |    17 +
 tests/orig/golden/hid_gerber2/out.fab.gbr          |  1754 +++
 tests/orig/golden/hid_gerber2/out.plated-drill.cnc |     7 +
 tests/orig/golden/hid_gerber2/out.top.gbr          |    17 +
 tests/orig/golden/hid_gerber3/arcs.bottom.gbr      |    23 +
 tests/orig/golden/hid_gerber3/arcs.fab.gbr         |  2025 +++
 tests/orig/golden/hid_gerber3/arcs.group1.gbr      |    28 +
 tests/orig/golden/hid_gerber3/arcs.group4.gbr      |    48 +
 .../orig/golden/hid_gerber3/arcs.plated-drill.cnc  |    14 +
 tests/orig/golden/hid_gerber3/arcs.top.gbr         |    28 +
 tests/orig/golden/hid_png1/gerber_oneline.png      |   Bin 0 -> 218 bytes
 tests/orig/golden/hid_png2/myfile.png              |   Bin 0 -> 218 bytes
 tests/orig/golden/hid_png3/gerber_oneline.png      |   Bin 0 -> 845 bytes
 tests/orig/inputs/bom_general.pcb                  |  1211 ++
 tests/orig/inputs/gcode_oneline.pcb                |   829 ++
 tests/orig/inputs/gerber_arcs.pcb                  |   844 ++
 tests/orig/inputs/gerber_oneline.pcb               |   829 ++
 tests/orig/run_tests.sh                            |   681 +
 tests/orig/tests.list                              |   166 +
 tests/pcb-printf/Makefile                          |    44 +
 tests/pcb-printf/prcli.c                           |    29 +
 tests/pcb-printf/tester.c                          |    71 +
 tests/pcb-printf/tester.ref                        |   160 +
 tests/pcbflags/Makefile                            |     4 +
 tests/pcbflags/readme.txt                          |     2 +
 tests/pcbflags/test.sh                             |    95 +
 tests/pcbflags/vanilla.pcb                         |    15 +
 tests/propedit/Makefile                            |    32 +
 tests/propedit/tester.c                            |   109 +
 tests/propedit/tester.ref                          |    17 +
 tests/strflags/Makefile                            |    25 +
 tests/strflags/tester.c                            |   162 +
 tests/strflags/tester.ref                          |    17 +
 tests/uniq_name/Makefile                           |    26 +
 tests/uniq_name/tester.c                           |    58 +
 tests/uniq_name/tester.ref                         |    32 +
 util/Makefile                                      |    30 +
 util/README                                        |     3 +
 util/cgi_common.sh                                 |   118 +
 util/devhelpers/chgstat.sh                         |    72 +
 util/devhelpers/chlog.sh                           |     2 +
 util/devhelpers/copyright_miss.sh                  |     8 +
 util/devhelpers/deblist.sh                         |    69 +
 util/devhelpers/inc_weed_out.sh                    |    94 +
 util/devhelpers/pcb-rtrip                          |   119 +
 util/devhelpers/subst/README                       |     3 +
 util/devhelpers/subst/subst_const.sh               |    25 +
 util/devhelpers/subst/subst_flags.sh               |    38 +
 util/devhelpers/subst/subst_mode.sh                |    27 +
 util/devhelpers/subst/subst_objtype.sh             |    30 +
 util/fp2anim                                       |   703 +
 util/genref.sh                                     |   226 +
 util/gnet-pcbrndfwd.scm                            |   125 +
 util/gsch2pcb-rnd/Makefile.in                      |   138 +
 util/gsch2pcb-rnd/gnet-gsch2pcb-rnd.scm            |    56 +
 util/gsch2pcb-rnd/gsch2pcb.c                       |  1351 ++
 util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h              |    22 +
 util/gsch2pcb-rnd/gsch2pcb_scm/Makefile            |    17 +
 util/gsch2pcb-rnd/gsch2pcb_scm/c1.sch              |    13 +
 util/gsch2pcb-rnd/gsch2pcb_scm/c2.sch              |    15 +
 util/gsch2pcb-rnd/gsch2pcb_scm/c3.sch              |    15 +
 util/gsch2pcb-rnd/gsch2pcb_scm/golden/c1.net       |     1 +
 util/gsch2pcb-rnd/gsch2pcb_scm/golden/c2.net       |     1 +
 util/gsch2pcb-rnd/gsch2pcb_scm/golden/c3.net       |     1 +
 util/gsch2pcb-rnd/help.c                           |   148 +
 util/gsch2pcb-rnd/help.h                           |     2 +
 util/keylist.sh                                    |   227 +
 util/keylist_old_res.sh                            |   263 +
 util/pcb-strip                                     |    87 +
 util/pcblib-map/Makefile                           |    16 +
 util/pcblib-map/cache.sh                           |    51 +
 util/pcblib-map/conn.pcb                           |  1342 ++
 util/pcblib-map/imgmap_fp.sh                       |   127 +
 util/pcblib-map/imgmap_page.sh                     |   131 +
 util/pcblib-map/map.pcb                            |  2737 ++++
 util/pcblib-map/parametric.pcb                     |  1153 ++
 util/pcblib-map/smd2.pcb                           |  1086 ++
 util/pcblib-map/smd3.pcb                           |   895 ++
 util/pcblib-map/smdN.pcb                           |   944 ++
 util/pcblib-map/trh2.pcb                           |   928 ++
 util/pcblib-map/trh3.pcb                           |  1096 ++
 util/pcblib-map/trhN.pcb                           |   983 ++
 util/pcblib-param.cgi                              |   479 +
 util/pcblib-static.cgi                             |   237 +
 util/pcblib.cgi.conf                               |    34 +
 util/workarounds/unwarn.sh                         |    13 +
 util/workarounds/unwarn_all.sh                     |    10 +
 w32/Makefile.am                                    |    18 +
 w32/README                                         |    54 +
 w32/build-all.sh                                   |    51 +
 w32/minipack.conf                                  |    40 +
 w32/mpk                                            |   130 +
 ...001-Export-sysbols-when-building-as-a-DLL.patch |    35 +
 ...tool-requires-no-undefined-to-build-a-DLL.patch |    26 +
 ...nflit-between-libjpeg-and-windows-headers.patch |    33 +
 w32/patches/gettext/01-revert-sed-string.patch     |    21 +
 .../02-Avoid-missing-open-argument-error.patch     |    15 +
 w32/patches/gtk+/02-example.patch                  |    50 +
 w32/patches/zlib/01-shared-lib-support.patch       |   249 +
 w32/patches/zlib/02-cross-build.patch              |    61 +
 w32/recipes/atk.recipe                             |     6 +
 w32/recipes/cairo.recipe                           |     8 +
 w32/recipes/gd.recipe                              |    30 +
 w32/recipes/gettext.recipe                         |     8 +
 w32/recipes/glib.recipe                            |     6 +
 w32/recipes/gtk+.recipe                            |    12 +
 w32/recipes/jpeg.recipe                            |     5 +
 w32/recipes/libiconv.recipe                        |     4 +
 w32/recipes/libpng.recipe                          |    13 +
 w32/recipes/pango.recipe                           |     9 +
 w32/recipes/pcb.recipe                             |    18 +
 w32/recipes/pixman.recipe                          |     4 +
 w32/recipes/pkg-config.recipe                      |     4 +
 w32/recipes/tiff.recipe                            |     4 +
 w32/recipes/zlib.recipe                            |     6 +
 w32/todo.tmpasm                                    |    14 +
 w32/tools/mpk-build                                |   123 +
 w32/tools/mpk-clean                                |    38 +
 w32/tools/mpk-config.guess                         |  1516 +++
 w32/tools/mpk-help                                 |    13 +
 w32/tools/mpk-install                              |    61 +
 w32/tools/mpk-remove                               |    61 +
 w32/tools/mpk-shell                                |    69 +
 w32/tools/mpk-source                               |    85 +
 w32/tools/mpk-unpack                               |   100 +
 w32/tools/mpk-version                              |     5 +
 w32/tools/tool.template                            |     4 +
 2028 files changed, 345055 insertions(+)

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..1a2afbe
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,13 @@
+pcb-rnd maintainer: Tibor 'Igor2' Palinkas
+email:      pcb-rnd (at) igor2.repo.hu
+Chat, IRC:  http://repo.hu/projects/pcb-rnd/irc.html
+
+An always up-to-date list of developers and contributors can be found at
+http://repo.hu/cgi-bin/pcb-rnd-people.cgi
+
+PCB was originally written by Thomas Nau
+Development was later taken over by harry eaton
+The port to GTK was done by Bill Wilson
+Dan McMahill converted the build system from imake to autoconf/automake.
+DJ Delorie wrote the trace optimizer, added symbolic flag support and
+many other improvements.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..48c80d2
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,701 @@
+pcb-rnd 1.1.3 (r4479)
+~~~~~~~~~~~~~~~~~~~~~
+	[mods]
+		-Fix: the IO system remembers which format (plugin) a file is loaded or last saved with and uses that on a 'save' instead of the default one, unless a different format is explicitly requested ('save as')
+		-Fix: load plugins from /usr/lib/pcb-rnd/plugins instead of /usr/lib/pcb/plugins
+		-Fix: macro argument bug in fontmode (thanks Chad!)
+		-Fix: import_sch: rename the gschem forward-annotation script so that it doesn't collide with mainline's
+		-Cleanup: rename legacy_func to lib_legacy_func as part of the plugin lib cleanup
+		-Add: io_kicad: load and save kicad s-expression board layout files
+		-Add: io_kicad_legacy: save boards and/or modules in the old kicad format
+		-Add: io_lihata: preserve formatting and numeric format on load/save
+		-Add: io_lihata: custom indentation rules for boards (more compact, easier to read)
+		-Add: in export_xy: code from mainline to support IPC-7351 on element rotation guesses
+		-Import: autocrop original code
+		-Import: boardflip
+		-Import: distalign
+		-Import: distaligntext
+		-Import: jostle
+		-Import: polycombine
+		-Import: polystich
+		-Import: renumberblock (as part of the renumber plugin)
+		-Import: smartdisperse
+		-Import: teardrops
+		-Import: bboard exporter from git
+		-Import: dsn exporter from git
+		-Import: dxf exporter
+		-Import: ipcd356 exporter from git
+		-Import: openscad exporter from git
+
+	[query]
+		-Add: query language - select/unselect objects using expressions
+		-Add: advanced search dialog box frontend for the query language in the GTK HID
+
+	[core]
+		-Cleanup: const correctness on net creation: style is read-only
+		-Cleanup: move generic find-net-for-pin function from rats_patch to netlist
+		-Fix: changing grid should not only change it in PCB but also in the conf tree - this will deliver the conf change event that will trigger the update in gtk
+		-Fix: const correctness in hid_flags, pin-to-net lookups
+		-Fix: coord conversion rounding errors
+		-Fix: deselect not incrementing undo serial number (from Charles Parker); caused undo panic upon undoing a deselection
+		-Fix: invalid function ptr to data ptr casts
+		-Fix: move global netlist states from global variables to PCB struct fields (on the way to a proper lib...)
+		-Fix: rubber band: when move didn't happen, flags were left behind
+		-Fix: undo element text size bug
+		-Fix: plug_io file load: when multiple formats matched an explicit load format description, try them all, until one worked
+		-Split: generalize format-string-like variable substitution so it's easier to build custom file names (and other strings) from config templates; supports %F (file name), %N (board name), %T (UNIX time), %B (basename) and %D (dirname)
+		-Add: remove_menu concept in HID API
+		-Add: generic flag change macro that can set, clear or toggle the flag depending on an argument
+		-Add: hid export option to draw holes after all copper, silk and mask layers has been drawn so that holes punch through everything
+		-Add: layer creation and rename helper functions
+		-Add: obj_any - a generic type+union struct (and a list of that) for holding pcb objects of any kind
+		-Import: LookupConnectionByPin from mainline
+		-Import: clear flags by object type from mainline (import and port)
+
+	[debian]
+		-Change: redo the whole script for a modular packaging of plugins
+
+	[util]
+		-Add: devhelper script for testing round trips
+		-Add: pcb-strip (remove attributes and/or symbols from a .pcb)
+
+	[gtk]
+		-Add: full screen button in the scroll-bar corner
+		-Fix: call the checkbox menu update function from conf change callback installed at menus, instead of assuming checkbox states are changed only from within the GUI and calling the updater directly
+
+	[propedit]
+		-Add: text set applies on element texts too
+
+	[res]
+		-Add: RemoveMenu() action
+		-Add: gtk can remove menus
+		-Add: update_on field - bind checkbox updating to a config var
+
+	[scconfig]
+		-Fix: when generating dependency generator rules, use -MT for src_3rd targets or else gcc will ruin the target object names; this means proper dependencies in src_3rd
+		-Cleanup: use bison/flex output file name CLI arguments instead of the tmp/mv hacks - the code depended on bison anyway
+		-Add: --symbols to enable debug symbols without the extra asserts and -O0
+
+pcb-rnd 1.1.2 (r3773)
+~~~~~~~~~~~~~~~~~~~~~
+	[cleanup]
+		- ANSI/C89 compliance
+		- char * const correctness
+
+	[mods]
+		-Fix: io_pcb: do not convert measure through int (Chris' mm grid bug)
+		-Fix: import_sch: generate proper error messages if there's no make or gnetlist program configured
+		-Fix: io: qsort cmp function API bug - wrong format preference
+		-Fix: io: make sure the PCBChanged action is ran on revert so that route styles and other properties are updated
+		-Fix: fontmode broken coord-to-cell conversion rendered the font editor useless
+		-Cleanup: export_bom and export_xy are separate plugins; export_xy depends on export_bom because of the name escaping function
+		-Rename: debug plugin to diag plugin to avoid confision with --debug in ./configure command line
+		-Add: diag: layer dump action
+		-Add: diag: evalconf() action to evaluate the configuration situation of a path
+		-Add: io_kicad_legacy: first steps for PCB layout implemented. Mildly broken module exported.
+		-Add: propedit: object property editor (with gtk support)
+		-Add: io_lihata: initial implementation without object ordering
+		-Add: io_pcb: 3 different flavors of the original format (using different numeric formats)
+		-Add: import_sch: make and gnetlist programs configured in the default config
+
+	[lesstif]
+		-Fix: compile and link even if xrender is not found
+		-Fix: print angle as %f in the status line as it's a floating point value
+
+	[gtk]
+		-Fix: library selection window switches to arrow mode before replacing the buffer, so that the outline drawing code doesn't get confused
+		-Fix: library path file chooser should be a "folder chooser" not a "file chooser"
+		-Fix: display the correct $(path) syntax in the library window help
+		-Fix: when file name changes, update window title
+		-Fix: do not allow to zoom out so much that Coord can't handle it anymore and overflows
+		-Fix: work around the control bug on some mac systems
+		-Fix: do not draw global grid beyond board edges
+		-Fix: shift+tab: translate left-tab back to tab, shift is handled by the core
+		-Fix: when popping up the log window for a Message(), don't raise it so focus is not stolen (especially if it's already up)
+		-Fix: when the main window is closed while the command entry main loop is running, quit the CLI main loop first, so the Quit action can execute and break the outer main loop of the GUI
+		-Fix: mark the PCB changed when the route style changes so it's saved
+		-Add: a delete option to the route style selector
+		-Add: explicit <current> route style (pen style) for sizes diverged from any known style
+		-Add: finish gtk preferences dialog's "Config PoV" subtree
+		-Add: "save as" dialog with file format widget
+		-Add: remember preferences tree expansion and other states when the dialog box needs to be rebooted
+		-Add: implement fullscreen mode
+		-Add: local grid implementation: optionally draw grid only in a small radius around the crosshair instead of on the whole screen (speeds up sw rendering on large screen)
+		-Add: global grid can be sparse (drawing every Nth grid point only); min distance is a configuration item
+		-Change: match gtk status line style with elegant lesstif status line
+
+	[scconfig]
+		-Fix: use generic code for plugin vs. plugin dependencies, deps are coded as data
+		-Fix: various portability fixes for a working IRIX port
+		-Add: revtest and revision stamps - this tool will let Makefile automatically request the user to reconfigure
+		-Add: central distclean cleans src_3rd/
+		-Add: --workaround-* system, with a gtk-ctrl workaround
+		-Add: new cli arg for mass-enabling/disabling all plugins: --all=*
+
+	[core] 
+		-Fix: hid input mouse scroll mixup: up and down were for scroll but left and right were for buttons. Prefix all scroll names with "scroll" (fixes macos pan-zoom-select bug)
+		-Fix: cancel on "new pcb" makes sure PCB never ends up NULL; worst case it tries to create an empty PCB to fall back on or exits with a fatal error if even that fails
+		-Fix: funchash should be case-insensitive (case insensitive actions and params)
+		-Fix: introduce pcb_cardinal_t instead of redeclaring X's Cardinal
+		-Fix: cursor position is Coord, not long - long won't work on 64 bit coords
+		-Fix: initialize builtins before loading plugins so later buildin <-> plugin collision can be resolved by not loading the plugin
+		-Fix: full-polygons are sensitive when clicked on the "clipped away" part
+		-Fix: non-refactored line drc enforcement
+		-Fix: qsort cmp function return value bug - wrong ordering of sources for the merge
+		-Add: RouteStyle() action accepts style name, not only index
+		-Add: action, change and undo code for chaning arc radii (width and height really) and start/delta angles
+		-Add: new, future-proof layer API, phasing out the old API
+		-Add: close polygon if the user clicks twice on the same point ('doubleclick'; like with the line tool; thanks to hzeller)
+		-Add: message log entries have severity levels; user can configure color and whether the message log should pop up for each severity
+		-Add: pcb-printf %mH prints human readable metric or imperial coords
+		-Add: inhibit draw counter: when moving an element with all rubber bands (e.g. netlists) attached, inhibit redraw them individually, redraw only once, at the end
+		-Add: reenable signals as detected by scconfig so that emergency save can run on crash
+		-Change: attribute list in all object types
+		-Split: layer.[ch] from misc.[ch]; the layer code is large and complex enough to get an own module; it's also in-line with the module-per-object-type idea
+
+	[util]
+		-Add: workaround script to comment out #warnings in a source file (for real C89 targets)
+		-Add: gsch2pcb-rnd: warn for old config overriding pcb-rnd search paths
+		-Add: install gnet pcb forward backend so that import_sch works out of the box
+
+pcb-rnd 1.1.1 (r2683)
+~~~~~~~~~~~~~
+	[io_kicad_legacy]
+		- Add: save paste buffer elements to kicad (legacy) .mod as native format
+
+	[scconfig]
+		-Change: remove config.manul.h (use scconfig CLI args instead), rename config.auto.h to config.h
+		-Change: coord type, the corresponding MAX value and abs() function should not be set manually in config.manual.h; ./configure --coord=32 or --coord=64 sets these all, 32 being the default value for now
+		-Change: move the dot_pcb_rnd setting from manual config header to scconfig (CLI arg)
+		-Fix: don't attempt to use -rdynamic for compilation, only for linking
+		-Fix: accept that IS_LNK may not be set (using conditional variable reference)
+		-Fix: working cross-compilation: proper separation of host and target tools (cp, ln, ar and friends are part of the build system, use the host version)
+		-Add: ./configure --enable-dmalloc detects dmalloc
+		-Add: detect dbus and compile the plugin if enabled
+		-Add: windows port: detect LoadLibrary(), _getcwd()/getwd(), _spawnvp, getpwuid, realpath, rint(), mkdtemp and mkdir/_mkdir
+		-Cleanup: split up config.auto.h.in: move dbus related item to dbus and gl related items to gtkhid-gl to keep central part plugin-neutral
+		-Cleanup: stop using HAVE_WHATEVER_H, rahter generate the #includes per function
+
+	[core]
+		-API change: rtree returns success and optionally sets the number of objects found - this allows the caller to avoid long jumps to stop the search at the first hit
+		-Fix: action names are case-insensitive again
+		-Fix: don't crash if select-by-text gets empty name or cancel
+		-Fix: undo crashes/asserts for updating NULL rtree with selected element text
+		-Split: huge find.c in smaller files, per topic
+		-Change: use genregex minilib instead of POSIX regex, for better portability
+		-Cleanup: remove find.c dead code
+		-Cleanup: rtree API names return values instead of using 0..1
+		-Cleanup: const correctness in EvaluateFileName
+		-Cleanup: introduce pcb_rnd(), which is a wrapper around rand() (or optionally, random()). rand() is portable, but on some very old system may be weak.
+		-Cleanup: introduce pcb_strdup() and pcb_strndup() and avoid using non-C89 libc calls
+		-Cleanup: always provide pcb_mkdir() as a portability wrapper around mkdir; all code should use this instead of mkdir()
+		-Cleanup: remove #ifdef wrapping of standard C89 header #includes - it's reasonable to assume/require proper C89 support more than 25 years after the standard came out
+		-Cleanup: a set of unused/obsolete #includes
+		-Cleanup: convert const.h #defines into enums (for the type, namespace and debugging); also proper name prefixing
+		-Cleanup: move route-style specific function from action-oriented set.c to object-oriented route-style.c
+		-Cleanup: rename SwapBuffers to pcb_swap_buffers for proper prefixing (win32 API name collision)
+		-Add: central function for looking up a routing style using a sparse list of properties
+
+	[gtk]
+		-Fix: gtk gdk background image drawing: clip for negative pan to avoid artifacts and segfault
+		-Fix: remember Messages() that are written before the message window is set up and append them in the window when it is already constructed
+		-Fix: do not crash if there is no route style defined, just go with an empty style section and let the user create new styles
+		-Fix: rework the win32-workarounds for corss-compiling
+		-Fix: always update bottom style printout when the style changes, even if there's no matching stock style (thanks to miloh for reporting this)
+		-Add: a mandatory <custom> route style to indicate non-matching types (hidden in the menu)
+		-Add: optional file format combo box on the save as dialog
+		-Change: use round radio buttons instead of rectangular toggle buttons for the layer preferences table - on some setups gtk theme made the buttons unreadable
+
+	[mods]
+		-Add: import plugin API
+		-Add: new features in the I/O API to support user-selected output format and listing available formats
+		-Split: move out netlist read from the io plug code to a separate import plugin
+		-Cleanup: remove edif stub, convert edif to an import plugin
+		-Cleanup: do not attempt to extract password entries manually in random plugins, use the central user name retrival function - easier to port, heuristics not duplicated
+		-Cleanup: ps export: get rid of non-portable alloca() - polygrid is used rarely, performance penalty of malloc() should not be a problem
+		-Cleanup: pcb I/O: do not use non-portable bzero, prefer memset()
+		-Fix: dbus: if there's no GUI by the time dbus is uninitialized, don't call it
+
+	[doc-rnd]
+		-Split: move out developer docs from doc-rnd/ to doc-rnd/hacking
+		-Add: windows cross-compilation notes
+		-Add: hacking doc for writing importers (incldues a description of the main pcb data structures)
+
+	[conf]
+		-Fix: gtk gui preferences saves editor/auto_place in the config file
+		-Fix: gtk preferences' general tab should also save editor/save_in_tmp
+
+	[w32]
+		-Add: minipack: recipe for pkg-config
+		-Fix: minipack: calculate output filename for wget so that ?download suffix can be truncated
+		-Fix: minipack: don't attempt to build pcb-rnd from minipack (policy: minipack builds all deps then we crosscompile pcb-rnd)
+		-Update: minipack: gd and tiff recipes for newer versions
+
+pcb-rnd 1.1.0
+~~~~~~~~~~~~~
+	[conf] - full rewrite of the config settings subsystem
+		- Del: settings, color files, preferences - everything is in the conf system from now
+		- Add: config settings are stored in structured lihata files on different levels
+		- Add: all config setting are equal - they can be stored in system, user, project or design file
+
+	[core] - cleanup
+		-Fix: editing text should properly update the rtree
+		-Fix: Display(PinOrPadName) doesn't use crosshair directly, without checking validity, but uses getxy which asks the user in turn, if the crosshair is invalid
+		-Fix: ortho draw lines, polys and poly holes did not update last drawn point and generally did not work
+
+		-Cleanup: remove a bunch of excess #includes to reduce the amount of deps
+		-Cleanup: rename keepaway to clearance; terminology: clearance is copper vs. copper spacing; keepaway is any other spacing
+		-Cleanup: generic dead-code and compiler warning removal
+		-Change: menus: make a new Maintenance submenu in the File menu, move printer calibration and library rescan there
+
+	[poly] - bugfix
+		-Fix: don't xor-overdraw the first line of a poly rubber band while drawing the second line so it doesn't cancel itself out
+		-Fix: first click of the poly hole is also the first point of the hole
+		-Fix: reset attached object state after finishing a poly hole so that the second hole doesn't start in invalid state (caused poly duplication)
+		-Fix: don't let the user start drawing a poly hole if first click is not a poly
+		-Add: draw assumed closing rubber band line dashed
+
+	[dynstyle]
+		-Fix: number of styles is not limited anymore (styles is a dynamic vector)
+
+	[res]
+		-Fix: dynamic menu insertion bug: don't add new level if it is already added
+
+	[mods]
+		-Fix: report doesn't crash if there's nothing to report
+		-Add: debug plugin to host core debugging actions so that they can be removed from core
+		-Add: debug plugin to tmpasm lists
+		-Add: io_* hooks - native file formats are plugins now
+		-Change: turn .pcb (.fp) file IO code into plugin io_pcb
+		-Add: io_pcb can save (CFR_DESIGN) config in the design as attributes
+		-Add: central infrastructure for per plugin help (usage), plus the help text in each plugin
+		-Add: fp_rehash() action to trigger a rehash
+
+	[gtk]
+		-Fix: double-zoom GUI lockup, pan+scroll wheel lockup
+		-Fix: shift+mouse click should work
+		-Fix: preferences window now easily fit in 800x600
+		-Fix: don't destroy the tooptip timer when it destroys itself - fixes the famous tooltip glib warning
+		-Fix: gtk warning/assert introduced by r1439: toolbar can not be a vbox, the parent vbox needs to be tracket separately
+		-Fix: abs() handling with non-int types - more proper handling of coords on corner cases like tiny objects (contributed by Chris Smith)
+		-Cleanup: dialog titles should be pcb-rnd, not PCB
+		-Add: properly remember window positions and sizes, save and reload them if the user enabled the feature
+
+	[scconfig]
+		-Fix: really compile if there's no glib installed and no glib-dependent plugin selected
+		-Fix: remove m4 bin from config.h template - no code uses this anymore; do not even detect m4, it's not needed anywhere
+		-Fix: reorder link command for the main executable so that libs end up at the end of the link list (some linkers are sensitive to this)
+		-Fix: various random fixes for a mac port
+		-Del: hid.conf: not needed anymore, hid types are managed differently
+		-Cleanup: C89 always has atexit() and there's no on_exit
+		-Add: --debug (that shoudl enable asserts and -g) and a tmpasm script to set cflags accordingly
+		-Add: detect gettext if intl is requested
+		-Add: detect bison/flex (with an option to disable it)
+
+pcb-rnd 1.0.10
+~~~~~~~~~~~~~~
+	[res] - resource->lihata conversion
+		-Add: new action: CreateMenu()
+		-Add: centralized hid keyboard handling API
+		-Add: optional: a gtk-clone multi-key capable menu file that copies gschem's hotkeys mostly
+		-Change: remove res parser, use lihata instead; affected plugins: hid_gtk, hid_lesstif, vendordrill
+		-API CHANGE: dynamic create menu call API takes a string path, not an already splitted array
+		-Update: keylist html generator util works from lihata and handles multikey
+		-Fix: remove hardwired layer selection key bindings, use configured hotkeys (alternative menu files may want to use the same keys differently, hardwired keys are bad)
+
+	[library_t] - library data structure cleanup/rewrite
+		- Change: replace MenuEntry and related code to a library_t
+		- Add: allow a generic tree representation in footprint library
+
+	[reduce] - reduce code size without losing actual functionality
+		-Del: extern "C": pcb is a pure C project, if someone wants to include headers from whatever other language, he should take care of it on that side
+		-API CHANGE: rewrite action_funchash to not use custom local hash but genht and to offer cookie based namespaces for modules
+		-Cleanup: rename action_funclist to funchash_core_list to decouple functions from actions
+
+	[scconfig]
+		-Fix: scconfig/build doesnt' fail even if lesstif is not installed
+		-Fix: do not attempt to detect libgtk is the gtk hid is disabled
+		-Fix: don't detect gd if it's not needed; disable exporters that depend on GD if required parts are not available
+		-Fix: relax gcode/nelma png requirements: print a warning about reduced functionality but let them compile if there's no png but gd is present
+		-Fix: get the dep file sorted by target; this should avoid unintended changes introduced by unordered lists
+		-Change: split up the tmpasm list of modules so that it's easier to get gsch2pcb link again using whatever backend plugins
+		-Cleanup: move strflags test code to regression/
+		-Change: portability, realpath are compat_*; compat.[ch] split to _dl and _misc
+		-Move: remove Makefile.in.mod, move module tmpasm files to src_plugins/
+		-Add: detect libstroke and compile with the right cflags and ldflags
+		-Add: tool to quote files into C strings; generate compiled-in, C versions of the new lht menu files, per hid
+
+	[mods]
+		-Add: API to remove functions by cookie
+		-Add: print warning if a module leaves anything in the func hash at exit
+		-Add: infrastructure for switching from one gui to another
+		-Add: fp_wget: web based footprint implementation, gedasymbols.org integration
+		-Move: command.[ch] to a shorthand command plugin (shand_cmd)
+		-Move: report to a feature plugin
+		-Move: fp_fs plugin: separate footprint code from file.[ch]
+		-Move: move debug actions from lesstif to comomn "oldactions" as they are not GUI-dependant and might be generally useful
+		-Move: hid/common/ to core hid_*, split up large, random collections of functions by purpose
+		-Move: split and rename action.[ch] to make room for hid-common actions.[ch] in core and avoid confusion in names; rename actions.h to hid_actions.h for naming consistency
+		-Move: HIDs to plugins, removing the hid/ directory
+		-Move: hidgl to a separate plugin (to be compiled later)
+		-Fix: event unbind shouldn't segfault if multiple adjacent events are free'd
+		-Fix: event_bind() takes cookie as const char *, not as void * to comform the convention
+		-Fix: hids install and uninstall actions, flags and attributes only around their do_export main loop so they don't conflict
+
+	[leak] - cleaning up memory leaks
+		-Add: hid attribute remove by cookie
+		-Fix: lesstif and gtk hids remove their attributes
+		-Fix: vendordrill free()s all cache memory used when the cache is discarded
+
+	[gtk]
+		-Fix: make sure menubar is always wide enough to expose all buttons (using a hidden invisible hbox of calculated size)
+		-Add: copy accel key menuitems from gschem and take over keyboard handling; hid_gtk now supports multi-key hotkeys
+		-Add: set tooltip on menu items
+		-Add: include all hotkeys in the tooltip for submenus
+
+	[lesstif]
+		-Del: remove dumpkeys action/code: this info should be extracted from the lihata file
+		-Fix: there should be no global variable named 'n' in lesstif; make the stdarg thing a bit more robust and reusable
+		-Add: reenable code to set set menu font and color
+
+	[core]
+		-Fix: draw_pad() used uninitialized pad color
+		-Add: SwitchHID action to change the HID module on the fly (works partially)
+
+	[png]
+		-Fix: png max dpi should be 10k - modern printers easily do 2400 but some models are said to do 9600 DPI in one direction
+
+	[gpmi]
+		-Fix: auto-copy .so files to src's plugin dir on compile so that running from source always has the latest .so files
+
+
+pcb-rnd 1.0.9
+~~~~~~~~~~~~~
+	[unglib]
+		-Fix: replace glib with minilibs in core and util/
+		-Del: remove local dynamic string implementation in  favor of genvector
+		-Move: vector.[ch] is used only by the autorouter, move it there
+
+	[scconfig]
+		-Add: print warnings if glib is not found and selected components need to be left out
+		-Add: repeate some of the critical messages below the summary
+		-Fix: compiler warnings
+		-Fix: instead of two booleans, implement a 3-state "/controls" node with values disable, buildin, plugin for the plugins
+		-Change: better module summary printout at the end of ./configure
+		-Fix: use -fPIC on x86_64 to make sure plugins link
+		-Fix: gpmi plugin installation bugs
+		-Fix: src/plugin is cleaned on make clean
+
+	[leak]
+		-API CHANGE: plugin init should return a pointer to the plugin uninit function
+		-Fix: a bunch of random memory leaks
+		-Fix: a bunch of potential buffer overruns
+		-Add: central infrastructure for uninit'ing GUI hids
+		-Fix: missing closedir()
+		-Add: uninit paste buffers
+		-Add: actions, flags and attributes are registered with a cookie so they an be deleted by cookie
+		-Fix: uninit plugins before hids so they have a chance to clean up registrations
+		-Fix: don't use dynamic strings where printing quoted string directly to a file would work
+
+	[mods]
+		-Change: convert the ps and lpr exporters into configurable exporter plugins
+		-Rename: edif plugin to import_edif for consistency
+		-Change: move gcode and nelma from hid to plugin
+		-Move: gerber, bom and png to expoter plugins
+		-Cleanup: move old/unused/legacy functions to a new plugin called legacy_func
+		-Change: move fontmode to a separate plugin
+
+	[core]
+		-Change: use a hash for flag storage - faster and simpler code
+
+	[gpmi]
+		-Add: expotert hid callback for filled pad exporting
+
+pcb-rnd 1.0.8
+~~~~~~~~~~~~~
+	[doc-rnd]
+		-Add: djopt examples
+
+	[mods]
+		-Cleanup: split up action.c and spread the code in core and plugins
+		-Move: action pscalib() to the ps hid code
+		-Move: autoplace to a core plugin
+		-Move: autoroute to a core plugin
+		-Move: dbus to a defunct core plugin
+		-Move: djopt to a core plugin
+		-Move: edif to a core plugin
+		-Move: gpmi to a core plugin
+		-Move: import_sch to a core plugin
+		-Move: mincut to a core plugin
+		-Move: oldactions to a core plugin
+		-Move: puller to a core plugin
+		-Move: renumber to a core plugin
+		-Move: stroke to a core plugin
+		-Move: toporouter to a core plugin
+		-Move: vendordrill to a core plugin
+
+	[ba]
+		-Add: when applying patches to the edited netlist, create missing nets
+		      (new connections may implicitly define new nets)
+		-Fix: get the renumber action() fixed
+		-Fix: enable net(add,net,pin) action again and make it operate on the
+		      edited netlist for the back annotation to take notes of the
+		      changes made
+
+	[libstroke]
+		-Add: menu in the gtk hid's res to toggle stroke enable
+		-Fix: osolete calls in libstroke action - libstroke support compiles now
+		-Fix: get the gtk gui call the central crosshair move event handler
+		      instead of reproducing its code locally (... without libstroke)
+
+	[util]
+		-Add: make clean
+		-Fix: compiler warnings, mostly unused vars in gsch2pcb
+		-Cleanup: indentation in gsch2pcb
+
+
+pcb-rnd 1.0.7
+~~~~~~~~~~~~~
+	-Cleanup: src/ should compile in c89 - no more // comments and variables declared in for()
+	-Cleanup: const correctness in src/
+	-Cleanup: missing #includes in src/
+	-Cleanup: unified, tab based indentation all over src/
+	-Cleanup: move dmalloc include to central config.h
+	-Cleanup: move gts to src/3rd
+	-Cleanup: remove old/obsolete files inherited from the fork
+	-Cleanup: rename doc/ to doc-orig/ to avoid confusion and make it compile
+	-Fix: poly pin bounding box calculation adds clearance as rtree expects ([square])
+	-Fix: loading default.pcb shouldn't override cursor pos and board sizes and shoudln't cause file date to be changed in the gui hids since it is loading a "misc" file
+	-Add: README in each main directory to explain what the directory is for
+
+pcb-rnd 1.0.6
+~~~~~~~~~~~~~
+	[cycdrag]
+		-Add: "negative sized" selection selects anything that touches
+		-Add: cycle drag object action
+
+	[scconfig]
+		-Fix: toporouter Makefile module adds its own action registration code,
+		      so the central Makefile is independent of toporouter
+		-Add: --disable-toporouter
+		-Fix: use cc/cc for c compiler (so that it can be overridden)
+		-Fix: remove -std=gnu99: -std=c99 breaks with new gcc and -std is not
+		      really portable
+		-Fix: include genht before glib.h because glib.h ruins inline
+
+	[pinnum]
+		-Add: action and key binding for changing pin numbers in a footprint
+
+	[gpmi]
+		-Fix: typo in hotkey name for manage plugins (reported by Bert Timmerman)
+
+	[util]
+		-Fix: keylist: tolerate whitepsace in key sequences
+		-Fix: keylist resets locale to avoid broken table when gawk tries to
+		      be too clever
+
+
+pcb-rnd 1.0.5
+~~~~~~~~~~~~~
+	[onpoint]
+		-Add: Robert Drehmel's on-point patch adapted for pcb-rnd
+
+	[scconfig]
+		-Fix: properly configure and build and install even if there's space
+		      in the source path (thanks to Jason White)
+		-Fix: take genht from the local copy, don't depend it being installed
+
+pcb-rnd 1.0.4
+~~~~~~~~~~~~~
+	[ba]
+		-Add: back annotation
+
+	[core]
+		-Fix: suppress no-font error message while loading default.pcb - the actual font is coming from the default font file
+
+	[pcblib-param]
+		-Add: screw(), low level
+		-Fix: typo in the help of rcy()
+
+	[mincut]
+		-Fix: solve debug works again
+		-Fix: make debug pngs during load optional
+		-Fix: don't use function-in-function - that's a gcc-specific feature
+		-Fix: don't use alloca(), use malloc() (C99)
+		-Add: Makefile to test against known refs
+
+	[pcb-fp]
+		-Fix: gtk lib window tag print doesn't segfault if there are no tags for a footprint
+		-Del: local hash and library search implementation for footprint names in buffer.c - libpcb_fp should handle this
+
+pcb-rnd 1.0.3
+~~~~~~~~~~~~~
+	[gpmi]
+		-Add: GPMI plugin/buildin for scripiting (import of project pcb-gpmi)
+	
+	[tostyle]
+		-Add: new feature to adjust the sizes of object(s) to the values of the current routing style
+	
+	[pcb-fp]
+		-Add: support for parsing and caching footprint tags (file elements)
+		-Add: the gtk HID displays tags
+		-Add: the gtk HID can filter for tags
+	
+	[pcblib-param]
+		-Add: bga()
+		-Add: generic qf() generator
+		-Add: qfn() - based on qf()
+		-Add: qfp() - based on qf()
+		-Add: plcc() - based on qf()
+		-Add: qsop() - based on so()
+		-Add: silkmark: accept multiple values
+		-Add: silkmark: can be external
+		-Add: silkmark: new styles exteranl45, dot, circle, and arc
+		-Add: connector() has a sequence option to change pin numbering
+		-Add: some tunings for connector sequence=zigzag to do something usable with etrunc=1
+	
+	[hid]
+		-Add: dynamic menus: create_menu(), menu paths, runtime insertion of menus in core
+		-Add: dynamic menus in gtk
+		-Add: dynamic menus in lesstif
+		-Fix: more const correctness in dialog box code
+	
+	[scconfig]
+		-Add: ./configure --help
+		-Add: print configuration summary at the end
+		-Add: autodetect HOST (use sys/sysid)
+		-Add: src/3rd for central hosting of 3rd party software libs
+		-Add: support "buildins": plugins built into the code
+		-Change: move genht to src/3rd; policy: prefer genht over glib hash
+		-Fix: tests try to run pcb-rnd, not pcb
+		-Fix: central LDFLAGS and CFLAGS should contain the ldflags/cflags detected for generic use
+	
+	[pcb-mincut]
+		-Merge: pcb-mincut is imported in pcb-rnd, the extern repo will be removed
+	
+	[core]
+		-Add: event subsystem
+		-Add: gui_init event
+		-Add: generic plugin support: track plugins loaded, have a menu for managing them
+		-Add: more accessors to query unit details - for the gpmi scripts
+		-Add: pcb-printf %mI prints coordinate in internal coord format
+		-Add: calls for removing actions
+		-Add: calls for removing a hid (useful for removing exporter hids registered by scripts)
+		-Add: path resolution: support ~ in paths
+		-Add: real regex search and real string list search (in search by name actions)
+		-Change: switch over actions from bsearch() to a genht hash - simpler code, potentially also faster
+		-Fix: don't allow the registration of an action with a name that's already registered
+	
+	
+	[fp2anim]
+		-Add: optional dimension lines
+		-Add: more fine grained annotation control
+		-Change: switch over to vector fonts for better scaling
+		-Fix: draw rounded pads (new pad syntax only)
+		-Fix: make sure to generate sane arcs
+	
+	[doc-rnd]
+		-Add: official central key list
+
+pcb-rnd 1.0.2
+~~~~~~~~~~~~~
+	[pcblib-param]
+		-Fix: central bool handling (connector() etrunc), values: on/off, true/false, 1/0
+		-Fix: typo in so() parameter descriptions
+		-Fix: connector() typo in error()
+		-Add: more elaborate help syntax: easier to read with scripts
+
+	[fp2anim]
+		-Fix: read multiline directives properly - a newline after each directive is still needed, tho
+		-Fix: allow whitepsace between directive name and opening bracket
+		-Fix: create all layers in advance - this fixes the case when the fp doesn't draw on some layers (the macro still exists)
+		-Fix: rline() is extended by width/2 if there are no rounding; it seems this how pcb defines lines
+		-Fix: leave extra margin in photo mode for the 3d edges
+		-Add: support for old-style pad()
+		-Add: support for Mark() (relocate the diamond)
+		-Add: options to turn off the grid and annotation - useful for thumbnails
+
+	[scconfig]
+		-Fix: always have config.h, don't ifdef it
+		-Fix: glib is a core dependency that should be added even if the gtk hid is not used
+		-Fix: make clean removes pcb-rnd, not pcb (executable file name typo)
+		-Add: make clean in util/
+		-Add: options to disable gd and/or jpeg or png or gif
+		-Add: be able to detect and configure lesstif 
+		-Add: --disable-gtk and --disable-lesstif; --disable-xinerama and --disable-xrender for lesstif
+		-Add: use detected -rdynamic so that plugins can link against the executable
+		-Change: detect both gtk and lesstif as optional dependencies
+		-Cleanup: generate hidlist.h from tmpasm instead of shell to remove shell dependency
+		-Del: a bunch of obsolete #defines inherited from auto*
+
+	[core]
+		-Add: --gui explicitly selects a gui hid
+		-Add: don't leave preferred order of GUIs to the chance, make it explicit
+
+
+pcb-rnd 1.0.1
+~~~~~~~~~~~~~
+	[core]
+		-Fix: don't read beyond the common part of the struct in IsPointInPad (since it's called for lines too, and those have much less fields)
+		-Fix: where stdarg is avaialble, also print messages to stderr - useful if things go wrong before a GUI is working
+
+	[gtk]
+		-Fix: don't crash but write error message and exit if gpcb-menu.res is empty 
+
+	[square]
+		-Fix: 90 deg rotation rotates shape style
+		-Add: action.c and change.c code to get shaped vias
+		-Fix: don't change pin shape for square and octagon in rotation
+
+	[mincut]
+		-Add: command line setting --enable-mincut (0 or 1) as mincut can be slow
+		      it is a global disbale setting and make a local, per pcb setting
+		      for enabling mincut; also add a per pcb setting/flag
+		-Fix: disable debug draw by default
+		-Fix: fall back to the old short warn method when mincut fails
+		-Fix: avoid segfaults by detecting broken graph early
+		-Workaround: if mincut sees a graph with multiple unconnected components, doesn't try to solve but falls back to randomly highlight something
+
+	[intconn]
+		-Workaround: find intconn pads only on the same layer
+
+	[nonetlist]
+		-Workaround: ignore nonetlist pads even if the flag is in the element name
+
+	[scconfig]
+		-Add: scconfig/ - switch over from autotools to scconfig
+
+	[pcblib]
+		-Cleanup: new, trimmed back pcblib/ with essential footprints only
+
+ [pcblib-param]
+		-Add: new parametric footprints - no more m4-hardwiring, use your
+		      preferred language!
+		-Add: acy(), alf(), rcy(), connector(), dip()
+		-Add: so(), tssop(), msop(), ssop()
+
+	[pcb-fp]
+		-Add: central lib for footprint search and load in pcb and gsch2pcb
+
+	[util]
+		-Add: gsch2pcb fork to support [nonetlist] and [pcblib-param]
+
+	[fp2anim]
+		-Add: fp to animator script for fast preview
+
+	[polygrid]
+		-Add: ps output: draw grid in polys instead of fill (doesn't fully work)
+		-Fix: set proper max value so the control is enabled
+
+
+	[debian]
+		-Update: build the package with scconfig and footprint changes
+
+
+pcb-rnd 1.0.0
+~~~~~~~~~~~~~
+	[square] -Add: initial implementation
+	[intconn] -Add: initial implementation
+	[nonetlist] -Add: initial implementation
+	[flagcomp] -Add: initial implementation
+	[mincut] -Add: initial implementation
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..bbe682e
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,69 @@
+1. Installation
+
+Run ./configure.
+
+Run make.
+
+Run make install.
+
+For compile-time options run ./configure --help
+
+2. Running from source
+
+cd src && ./pcb-rnd
+
+(Note: it is important to cd to src to run pcb-rnd from source; src/pcb-rnd
+won't work unless pcb-rnd is installed).
+
+If this doesn't work, please refer to doc-rnd/UNIX.txt or doc-rnd/mac.txt
+
+--
+
+PCB is organized into:
+  src/            a core program that deals with all of the internal
+                  database procedures
+  src_plugins/    a collection of plugins
+  src_3rd/        third-party minilibs (dependencies, not integral parts)
+  pcblib/         a basic footprint library
+  tests/          automated tests to check whether pcb-rnd works properly
+  util/           utility programs like gsch2pcb-rnd
+
+
+./configure will try to static link most plugins and disable ones that
+have missing dependencies. This process can be controlled using configure
+command line switches, see ./configure --help.
+
+After running ./configure with your selected options, run
+
+  make
+
+to build pcb-rnd.  You can try out the program by running
+
+  cd src
+  ./pcb-rnd
+
+prior to installation (CWD _must_ be src/).
+
+To install PCB after it has been built run:
+
+  make install
+
+from the top level directory. An alternative installation method
+is the link-install, which places symlinks instead of copying files so
+no subsequent make install is needed after a recompilation if no new
+files appeared (useful for developers):
+
+  make linstall
+
+-------- Summary of dependencies --------------------
+For users:
+ - C compiler
+ - make
+ - optional: glib and gtk if you are using the gtk frontend
+ - motif or lesstif if you are using the lesstif frontend
+ - gdlib if you are using the png HID
+
+For developers:
+ - flex
+ - bison
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9b94591
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+all: FORCE
+	cd src && make
+	cd util && make
+	cd pcblib && make
+#	cd doc-rnd && make
+
+clean: FORCE
+	cd src && make clean
+	cd util && make clean
+	cd pcblib && make clean
+	cd doc-rnd && make clean
+
+distclean: FORCE
+	make clean ; true
+	cd doc-rnd && make distclean
+	cd scconfig && make clean ; true
+	cd src_3rd/genlist && make clean ; true
+	cd src_3rd/genregex && make clean ; true
+	cd src_3rd/genvector && make clean ; true
+	cd src_3rd/gts && make clean ; true
+	cd src_3rd/liblihata && make clean ; true
+	cd src_3rd/liblihata/genht && make clean ; true
+	cd src_3rd/qparse && make clean ; true
+
+install: FORCE
+	cd src && make install
+	cd util && make install
+	cd pcblib && make install
+	cd doc-rnd && make install
+
+linstall: FORCE
+	cd src && make linstall
+	cd util && make linstall
+	cd pcblib && make linstall
+	cd doc-rnd && make linstall
+
+uninstall: FORCE
+	cd src && make uninstall
+	cd util && make uninstall
+	cd pcblib && make uninstall
+	cd doc-rnd && make uninstall
+
+deb: FORCE
+	fakeroot debian/rules clean
+	fakeroot debian/rules binary
+
+debclean: FORCE
+	fakeroot debian/rules clean
+
+FORCE:
diff --git a/Makefile.conf.in b/Makefile.conf.in
new file mode 100644
index 0000000..66a8f95
--- /dev/null
+++ b/Makefile.conf.in
@@ -0,0 +1,14 @@
+print [@# generated by ./configure, do not modify
+# prefix is @/local/prefix@
+DOCDIR=$(install_root)@/local/prefix@/share/doc/pcb-rnd
+LIBDIR=$(install_root)@/local/prefix@/lib/pcb-rnd
+BINDIR=$(install_root)@/local/prefix@/bin
+ETCDIR=$(install_root)/etc
+DATADIR=$(install_root)@/local/prefix@/share/pcb-rnd
+MAN1DIR=$(install_root)@/local/prefix@/share/man/man1
+RM=@/host/fstools/rm@
+CP=@/host/fstools/cp@
+LN=@/host/fstools/ln@
+MKDIR=@/host/fstools/mkdir@
+EXE=@/target/sys/ext_exe@
+@]
diff --git a/README b/README
new file mode 100644
index 0000000..e18f360
--- /dev/null
+++ b/README
@@ -0,0 +1,28 @@
+Pcb-rnd is a fork of geda/PCB with some major improvements.
+It is hosted at http://repo.hu/projects/pcb-rnd
+Soruce code: svn://repo.hu/pcb-rnd/trunk
+
+Pcb is a CAD (computer aided design) program for the physical
+design of printed circuit boards.
+
+For installing the release refer to the file 'INSTALL'.
+For additional information read the manual (doc-orig/pcb.pdf) 
+
+If you are updating you may wish to read the ChangeLog
+
+Contact:
+	email:       pcb-rnd (a) igor2.repo.hu
+	chat & IRC:  http://repo.hu/projects/pcb-rnd/irc.html
+
+-------------------------------------------------------------------------
+                            COPYRIGHT
+
+PCB is covered by the GNU General Public License.  See the individual
+files for the exact copyright notices.
+
+  Contact addresses for paper mail and Email:
+  harry eaton
+  6697 Buttonhole Court
+  Columbia, MD 21044
+  haceaton at aplcomm.jhuapl.edu
+
diff --git a/Release_notes b/Release_notes
new file mode 100644
index 0000000..ed827e5
--- /dev/null
+++ b/Release_notes
@@ -0,0 +1,17 @@
+pcb-rnd 1.1.3
+~~~~~~~~~~~~~~
+The main features of this release are kicad compatibility, the new
+query/advanced-search, a bunch of mainline and old plugins imported and
+that the lihata board format has reached production quality.
+
+The lihata board format introduces a feature that is rare in this class
+of applications: it can load and save board files preserving comments,
+the original indentation, bracing style, units and even numerical
+formats. This reduces unwanted diffs.
+
+This is the last release before a major cleanup of internal data structures.
+The lihata board format had to be sorted out so that new core features
+can be saved and loaded without breaking the inflexible .pcb format. Since
+the cleanup will break the remaining binary compatibility with mainline,
+this was also the last moment we could import old plugins with reasonable
+effort.
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..9385d36
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,215 @@
+print [@ /***** Generated by scconfig - DO NOT EDIT *****/
+
+/* Source: config.h.in; to regenerate run ./configure */
+
+#ifndef PCB_CONFIG_H
+#define PCB_CONFIG_H
+
+/****************************************************************************/
+/* Static defines to enable extra features on some systems; once we get
+   testers on those systems, we can remove them and see if scconfig figured
+   everything. */
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+/****************************************************************************/
+/* Package properties */
+
+/* Name of package */
+#define PACKAGE "pcb"
+
+/* Name of this program's gettext domain */
+#define GETTEXT_PACKAGE "pcb"
+
+/****************************************************************************/
+/* These ones are already autodetected by scconfig */
+@]
+
+print {\n\n/* Macro to add a funciton attribute to suppress "function unused" for static inline functions declared in .h files */\n}
+print_ternary cc/func_attr/unused/presents {#define PCB_FUNC_UNUSED __attribute__((unused))} {#define PCB_FUNC_UNUSED}
+
+
+print {\n\n/* Define to 1 if you have the `snprintf' function. */\n}
+print_ternary libs/snprintf {#define HAVE_SNPRINTF 1} {/* #undef HAVE_SNPRINTF */}
+
+print {\n\n/* Define to 1 if you have the `vsnprintf' function. */\n}
+print_ternary libs/vsnprintf {#define HAVE_VSNPRINTF 1} {/* #undef HAVE_VSNPRINTF */}
+
+print {\n\n/* Define to 1 if you have the `getcwd' function. */\n}
+print_ternary libs/fs/getcwd/presents {#define HAVE_GETCWD 1} {/* #undef HAVE_GETCWD */}
+
+print {\n\n/* Define to 1 if you have the `_getcwd' function. */\n}
+print_ternary ?libs/fs/_getcwd/presents {#define HAVE__GETCWD 1} {/* #undef HAVE__GETCWD */}
+
+print {\n\n/* Define to 1 if you have the `getwd' function. */\n}
+print_ternary ?libs/fs/_getwd/presents {#define HAVE_GETWD 1} {/* #undef HAVE_GETWD */}
+
+print {\n\n/* Define to 1 if you have the <gd.h> header file. */\n}
+print_ternary libs/gui/gd/presents {#define HAVE_GD_H 1} {/* #undef HAVE_GD_H */}
+
+print {\n\n/* Define to 1 if you have the `gdImageGif' function. */\n}
+print_ternary libs/gui/gd/gdImageGif/presents {#define HAVE_GDIMAGEGIF 1} {/* #undef HAVE_GDIMAGEGIF */}
+
+print {\n\n/* Define to 1 if you have the `gdImageJpeg' function. */\n}
+print_ternary libs/gui/gd/gdImageJpeg/presents {#define HAVE_GDIMAGEJPEG 1} {/* #undef HAVE_GDIMAGEJPEG */}
+
+print {\n\n/* Define to 1 if you have the `gdImagePng' function. */\n}
+print_ternary libs/gui/gd/gdImagePng/presents {#define HAVE_GDIMAGEPNG 1} {/* #undef HAVE_GDIMAGEPNG */}
+
+print {\n\n/* Define to 1 if you have the `getpwuid' function. */\n}
+print_ternary libs/userpass/getpwuid/presents {#define HAVE_GETPWUID 1} {/* #undef HAVE_GETPWUID */}
+
+print {\n\n/* Define to 1 if you have the `rint' function. */\n}
+print_ternary libs/math/rint/presents {#define HAVE_RINT 1} {/* #undef HAVE_RINT */}
+
+print {\n\n/* Define to 1 if you have the `round' function. */\n}
+print_ternary libs/math/round/presents {#define HAVE_ROUND 1} {/* #undef HAVE_ROUND */}
+
+print {\n\n/* Wrapper for S_ISLNK(x); always return 0 if S_ISLNK doesn't exist */\n}
+switch ?/target/libs/fs/stat/macros/S_ISLNK
+	case {^$} print {#define WRAP_S_ISLNK(x) 0}; end;
+	default print [@#define WRAP_S_ISLNK(x) @/target/libs/fs/stat/macros/S_ISLNK@(x)@]; end;
+end;
+
+print {\n\n/* Define to 1 if Xinerama is available */\n}
+print_ternary libs/gui/xinerama/presents {#define HAVE_XINERAMA 1} {/*#undef HAVE_XINERAMA */}
+
+print {\n\n/* Define to 1 if Xrender is available */\n}
+print_ternary libs/gui/xrender/presents {#define HAVE_XRENDER 1} {/*#undef HAVE_XRENDER */}
+
+print {\n\n/* Define to 1 if translation of program messages to the user's native language is requested. */\n}
+print_ternary /local/pcb/want_nls {#define ENABLE_NLS 1} {/*#undef ENABLE_NLS */}
+
+print {\n\n/* Define to 1 if we should use windows api for dynamic linking. */\n}
+print_ternary ?libs/LoadLibrary/presents {#define USE_LOADLIBRARY 1} {/* #undef USE_LOADLIBRARY */}
+
+print {\n\n/* Define to 1 if we should use mkdtemp for creating temp files. */\n}
+print_ternary ?libs/fs/mkdtemp/presents {#define HAVE_MKDTEMP 1} {/* #undef HAVE_MKDTEMP */}
+
+print {\n\n/* Define to 1 if you have the `realpath' function. */\n}
+print_ternary ?libs/fs/realpath/presents {#define HAVE_REALPATH 1} {/* #undef HAVE_REALPATH */}
+
+print {\n\n/* Select which mechanism to use for running child processes. */\n}
+print_ternary ?/target/libs/proc/wait/presents     {#define USE_FORK_WAIT 1} {/* #undef USE_FORK_WAIT */}
+print {\n}
+print_ternary ?/target/libs/proc/_spawnvp/presents {#define USE_SPAWNVP 1}   {/* #undef USE_SPAWNVP */}
+
+print {\n\n/* Select which mechanism to use for creating a directory. */\n}
+print_ternary ?/target/libs/fs/mkdir/presents     {#define USE_MKDIR 1} {/* #undef USE_MKDIR */}
+print {\n}
+print_ternary ?/target/libs/fs/_mkdir/presents    {#define USE__MKDIR 1} {/* #undef USE__MKDIR */}
+print [@
+#define MKDIR_NUM_ARGS 0@?/target/libs/fs/_mkdir/num_args@@?/target/libs/fs/mkdir/num_args@
+@]
+
+print {\n\n/* Define whether we have specific signals. */\n}
+print_ternary ?signal/names/SIGSEGV/presents    {#define PCB_HAVE_SIGSEGV 1} {/* #undef PCB_HAVE_SIGSEGV */}
+print {\n}
+print_ternary ?signal/names/SIGABRT/presents    {#define PCB_HAVE_SIGABRT 1} {/* #undef PCB_HAVE_SIGABRT */}
+print {\n}
+print_ternary ?signal/names/SIGINT/presents     {#define PCB_HAVE_SIGINT 1}  {/* #undef PCB_HAVE_SIGINT */}
+print {\n}
+print_ternary ?signal/names/SIGHUP/presents     {#define PCB_HAVE_SIGHUP 1}  {/* #undef PCB_HAVE_SIGHUP */}
+print {\n}
+print_ternary ?signal/names/SIGTERM/presents    {#define PCB_HAVE_SIGTERM 1} {/* #undef PCB_HAVE_SIGTERM */}
+print {\n}
+print_ternary ?signal/names/SIGQUIT/presents    {#define PCB_HAVE_SIGQUIT 1} {/* #undef PCB_HAVE_SIGQUIT */}
+print {\n}
+
+print [@
+
+/* The host "triplet" - it's really a pair now: machine-os */
+#define HOST "@sys/sysid@"
+
+/* Directory separator char */
+#define PCB_DIR_SEPARATOR_C '@sys/path_sep@'
+
+/* Directory separator string */
+#define PCB_DIR_SEPARATOR_S "@sys/path_sep@"
+
+/* Search path separator string */
+#define PCB_PATH_DELIMETER ":"
+
+/****************************************************************************/
+/* These are static; they are kept "just in case", for further porting */
+
+/* C89 has atexit() */
+#define HAS_ATEXIT 1
+
+/* C89 has this */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `canonicalize_file_name' function.
+   It's a GNU extension, assume we don't have it and fall back to
+   realpath() */
+/* #define HAVE_CANONICALIZE_FILE_NAME 1 */
+/* Define if canonicalize_file_name is not declared in system header files. */
+/* #undef NEED_DECLARATION_CANONICALIZE_FILE_NAME */
+
+/****************************************************************************/
+/* Paths */
+
+#define PCB_PREFIX "@/local/prefix@"
+#define PCBSHAREDIR PCB_PREFIX "/share/pcb-rnd"
+#define PCBLIBDIR PCB_PREFIX "/lib/pcb-rnd"
+
+#define BINDIR PCB_PREFIX "/bin"
+
+/* Relative path from bindir to exec_prefix */
+#define BINDIR_TO_EXECPREFIX ".."
+
+/* Version number of package */
+#define VERSION "@/local/version@"
+#define REVISION "@/local/revision@"
+
+
+/* Define to 1 if you have the `dmalloc' library (-ldmalloc);
+   (memory debugging without valgrind) */
+@]
+
+print_ternary libs/sul/dmalloc/presents {#define HAVE_LIBDMALLOC 1} {/*#undef HAVE_LIBDMALLOC */}
+
+print [@
+/*
+The documentation says it's not necessary to include dmalloc.h.
+Pros: getting source line info
+Cons: can't include from here, needs to be included from the bottom of the #include list from each file
+#ifdef HAVE_LIBDMALLOC
+#include <dmalloc.h>
+#endif
+*/
+
+@?/local/pcb/include_stdint@
+
+/* Coordinate type and properties, as detected by scconfig */
+typedef @/local/pcb/coord_type@ Coord;
+#define COORD_MAX @/local/pcb/coord_max@
+#define coord_abs @/local/pcb/coord_abs@
+
+/* Other autodetected types */
+typedef @/local/pcb/long64@    pcb_long64_t;
+
+/* the dot-dir: where to save user config under ther user's home; it's used
+   as ~/DOT_PCB_RND/ */
+#define DOT_PCB_RND "@/local/pcb/dot_pcb_rnd@"
+
+/* ./configure --workaround requests: */
+@?/local/pcb/workaround_defs@
+
+#endif
+@]
diff --git a/configure b/configure
new file mode 100755
index 0000000..f19e25d
--- /dev/null
+++ b/configure
@@ -0,0 +1,4 @@
+#!/bin/sh
+cd scconfig
+make
+./configure "$@"
diff --git a/data/Makefile b/data/Makefile
new file mode 100644
index 0000000..3cefb09
--- /dev/null
+++ b/data/Makefile
@@ -0,0 +1,64 @@
+# This Makefile is a plain old hand written one; all configuration settings
+# are included from ../Makefile.conf which is scconfig generated
+
+theme = hicolor
+
+app_icon = pcb
+
+mime_icons = \
+	application-x-pcb-layout \
+	application-x-pcb-footprint \
+	application-x-pcb-netlist \
+	application-x-gerber \
+	application-x-excellon
+
+app_icon_files = \
+	$(app_icon:%=%-48.png) \
+	$(app_icon:%=%.svg)
+#	$(app_icon:%=%-16.png)
+#	$(app_icon:%=%-22.png)
+#	$(app_icon:%=%-24.png)
+#	$(app_icon:%=%-32.png)
+
+mime_icon_files = \
+	$(mime_icons:%=%-16.png) \
+	$(mime_icons:%=%-22.png) \
+	$(mime_icons:%=%-24.png) \
+	$(mime_icons:%=%-32.png) \
+	$(mime_icons:%=%-48.png) \
+	$(mime_icons:%=%.svg)
+
+mime_icon_sources = \
+	$(mime_icons:%=%-16.svg) \
+	$(mime_icons:%=%-22.svg) \
+	$(mime_icons:%=%-32.svg) \
+	$(mime_icons:%=%-48.svg)
+
+theme_icons = \
+	$(mime_icon_files:%=mimetypes,%) \
+	$(app_icon_files:%=apps,%)
+
+all:
+
+install_:
+	./icon-theme-installer \
+		-t $(theme) \
+		-m "$(MKDIR)" \
+		-s `pwd` \
+		-d x \
+		-b "$(themedir)" \
+		-x "$(CPC)" \
+		-i $(theme_icons)
+
+install:
+	make install_ CPC="$(CP)"
+
+linstall:
+	make install_ CPC="$(LN)"
+
+uninstall:
+	$(RM) $(DOCDIR)/examples/tut1.pcb
+
+include ../Makefile.conf
+themedir=$(DATADIR)/icons/$(theme)
+
diff --git a/data/README b/data/README
new file mode 100644
index 0000000..fcfaa66
--- /dev/null
+++ b/data/README
@@ -0,0 +1,56 @@
+
+PCB
+------------------------------------------------------------------------------
+
+README for icon data
+
+This file describes where the various icons came from and their license.
+
+The PCB layout and gerber icons and mime registration data were contributed
+by Tomaz Solc, and subsequently modified by Peter Clifton, including
+creation of an excellon icon file with a ruler element taken from
+Tomaz's gerber icon. The footprint and netlist icons were drawn by
+Peter Clifton.
+
+The page outline featured in all the above icons is from the GNOME icon
+theme's text-x-generic icon by Jakub Steiner.
+
+The icons are licensed under the GPL2 license.
+
+Scalable versions: (128x128 canvas for the "hicolor" fallback theme).
+These were scaled up from the 48x48 pixel targeted version.
+
+application-x-excellon.svg
+application-x-gerber.svg
+application-x-pcb-footprint.svg
+application-x-pcb-layout.svg
+application-x-pcb-netlist.svg
+
+Pixel targeted varients:
+
+application-x-excellon-{16,22,32,48}.svg
+application-x-gerber-{16,22,32,48}.svg
+application-x-pcb-footprint-{16,22,32,48}.svg
+application-x-pcb-layout-{16,22,32,48}.svg
+application-x-pcb-netlist-{16,22,32,48}.svg
+
+
+PNG versions of the above icons were exported from Inkscape. The 24x24 pixel
+versions are copied from the 22x22 version, with a 1 pixel border added:
+
+application-x-excellon-{16,22,24,32,48}.png
+application-x-gerber-{16,22,24,32,48}.png
+application-x-pcb-footprint-{16,22,24,32,48}.png
+application-x-pcb-layout-{16,22,24,32,48}.png
+application-x-pcb-netlist-{16,22,24,32,48}.png
+
+The script "regen_files" will re-export the SVG drawings to PNG and also
+regenerate the windows icon file.
+
+The PCB application icons were created by Peter Clifton, based upon the
+Gnome "text-editor" icon created by Lapo Calamandrei. The PCB specific
+additions are from the mime-type icons by Tomaz Solc.
+These icons are licensed under the GPL2 license.
+
+pcb.svg
+pcb-48.png
diff --git a/data/application-x-excellon-16.png b/data/application-x-excellon-16.png
new file mode 100644
index 0000000..effc5e1
Binary files /dev/null and b/data/application-x-excellon-16.png differ
diff --git a/data/application-x-excellon-16.svg b/data/application-x-excellon-16.svg
new file mode 100644
index 0000000..3a646b9
--- /dev/null
+++ b/data/application-x-excellon-16.svg
@@ -0,0 +1,1271 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="16"
+   width="16"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2270"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3580332,0,0,0.3814965,-0.9615384,-0.7823571)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="24.343483"
+       x2="23.599062"
+       y1="24.191589"
+       x1="26.116739"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4158"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="13.240394"
+       x2="25.726562"
+       y1="13.291025"
+       x1="26.868286"
+       id="linearGradient4149"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2825">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop2827" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop2829" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2831"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient2833">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop2835" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop2837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2839"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2841"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2849"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient2851">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2853" />
+      <stop
+         id="stop2855"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2857" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient2859"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2873"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2875">
+      <stop
+         id="stop2877"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2879"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2881">
+      <stop
+         id="stop2883"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2885"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2887">
+      <stop
+         id="stop2889"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2891"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2893"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2895"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2897"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2899"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2901"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2903"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2911">
+      <stop
+         id="stop2913"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2915" />
+      <stop
+         id="stop2917"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2919"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2921"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2923"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient2925"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2927"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2929"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2931"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2933"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2935"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2937"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient2939"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2941"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2943"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2768"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2766"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3245"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3243"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3241"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3239"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3237"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2759"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2757"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2755"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2747">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2749" />
+      <stop
+         id="stop2751"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2753" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-30.95756,-25.612946)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2739"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,20.68511,-9.3175274)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2737"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,15.73026,-12.552287)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2735"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2726">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2728" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2730" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2720">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2722" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2724" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2714">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2716" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2718" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-5.084646,22.928153)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2712"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3145"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         id="stop5050"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056" />
+      <stop
+         id="stop5052"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3147"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5060"
+       inkscape:collect="always">
+      <stop
+         id="stop5062"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop5064"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3149"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3151"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2683">
+      <stop
+         id="stop2685"
+         offset="0"
+         style="stop-color:#d8c100;stop-opacity:1;" />
+      <stop
+         id="stop2687"
+         offset="1"
+         style="stop-color:#ffe501;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3153"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2676">
+      <stop
+         id="stop2678"
+         offset="0"
+         style="stop-color:#605400;stop-opacity:1;" />
+      <stop
+         id="stop2680"
+         offset="1"
+         style="stop-color:#887800;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4143">
+      <stop
+         id="stop4145"
+         offset="0"
+         style="stop-color:#6f7368;stop-opacity:1;" />
+      <stop
+         id="stop4147"
+         offset="1"
+         style="stop-color:#cccec9;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4699">
+      <stop
+         style="stop-color:#ffe71a;stop-opacity:1;"
+         offset="0"
+         id="stop4701" />
+      <stop
+         style="stop-color:#c7a51e;stop-opacity:1;"
+         offset="1"
+         id="stop4703" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2930"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2932"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2961"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2963"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2965"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2967"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2969"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2971"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2973"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2975"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2977"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.4476299,-4.4892272e-4,-7.7121596)"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2979"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4699"
+       id="linearGradient3028"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3524229,0,0,0.358209,-0.4361232,-0.4179105)"
+       x1="35.5625"
+       y1="33.125"
+       x2="22.75"
+       y2="26.625" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="203"
+     inkscape:window-x="21"
+     inkscape:window-height="666"
+     inkscape:window-width="872"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="11.49354"
+     inkscape:cx="9.6075209"
+     inkscape:zoom="17.481619"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="16px"
+     height="16px"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Excellon file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient2270);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2272);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 1.9283192,0.50572542 L 13.823415,0.50000003 C 14.14841,0.4998436 14.5,0.84206075 14.5,1.0753168 L 14.5,15.07896 C 14.5,15.312218 14.30897,15.5 14.07168,15.5 L 1.9283192,15.5 C 1.6910303,15.5 1.5,15.312218 1.5,15.07896 L 1.5,0.92676521 C 1.5,0.69350923 1.6910303,0.50572542 1.9283192,0.50572542 z "
+       id="rect15391"
+       sodipodi:nodetypes="czccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2267);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 2.5498879,1.5291309 L 13.358707,1.4999999 C 13.358707,1.4999999 13.500063,1.6718479 13.499999,1.6993481 L 13.470416,14.45036 C 13.470416,14.47786 13.448165,14.499999 13.420529,14.499999 L 2.5498879,14.499999 C 2.5222499,14.499999 2.4999999,14.47786 2.4999999,14.45036 L 2.4999999,1.5787708 C 2.4999999,1.5512704 2.5222499,1.5291309 2.5498879,1.5291309 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccscccccc" />
+    <rect
+       rx="0.45982891"
+       y="2"
+       x="3"
+       height="12"
+       width="10"
+       id="rect4669"
+       style="fill:url(#linearGradient3028);fill-opacity:1;stroke:none;stroke-width:1.50000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52836752;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+       id="path3040"
+       sodipodi:cx="-19.519127"
+       sodipodi:cy="16.811073"
+       sodipodi:rx="2.2922819"
+       sodipodi:ry="2.2922819"
+       d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+       sodipodi:start="4.2429731"
+       sodipodi:end="10.524328"
+       sodipodi:open="true"
+       transform="matrix(-0.6542891,0,0,-0.6542968,-0.7711699,22.999431)"
+       inkscape:transform-center-y="3.2632507" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52836752;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+       id="path3038"
+       sodipodi:cx="-19.519127"
+       sodipodi:cy="16.811073"
+       sodipodi:rx="2.2922819"
+       sodipodi:ry="2.2922819"
+       d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+       sodipodi:start="4.2429731"
+       sodipodi:end="10.524328"
+       sodipodi:open="true"
+       transform="matrix(-0.6542891,0,0,-0.6542968,-6.7711699,22.999431)"
+       inkscape:transform-center-y="3.2632507" />
+    <path
+       sodipodi:nodetypes="ccccc"
+       id="path4208"
+       d="M 5.791865,-0.46928089 L 4.7500001,8.113408 L 6.0666863,9.7711787 L 7.791587,8.5948552 L 9.0407016,0.080884306"
+       style="fill:#d3d7cf;fill-rule:evenodd;stroke:#eeeeec;stroke-width:1.50000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+    <g
+       style="fill:url(#linearGradient2961);fill-opacity:1;display:inline"
+       transform="matrix(1.2931815,0.2057418,-7.5417718e-2,0.6606822,-24.58908,-16.749366)"
+       id="g4115">
+      <g
+         style="fill:url(#linearGradient2963);fill-opacity:1"
+         id="g4151">
+        <path
+           sodipodi:nodetypes="cccczcc"
+           id="path4107"
+           d="M 24.375,29.21875 L 24.359375,30.03125 L 25.29525,31.751311 L 26.890625,28.390625 L 26.84375,27.265625 C 26.809535,26.444455 26.25,26.296875 26.25,26.296875 L 24.375,29.21875 z "
+           style="fill:url(#linearGradient2965);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <path
+           sodipodi:nodetypes="cczczcc"
+           id="path4111"
+           d="M 24.453125,24.484375 L 24.46875,25.34375 C 24.46875,25.34375 24.598474,27.12898 24.953125,26.609375 L 26.96875,23.65625 L 26.921875,22.53125 C 26.88766,21.71008 26.328125,21.5625 26.328125,21.5625 L 24.453125,24.484375 z "
+           style="fill:url(#linearGradient2967);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+        <path
+           inkscape:transform-center-x="0.40625"
+           inkscape:transform-center-y="2.5708492"
+           sodipodi:nodetypes="cczczcc"
+           id="path4113"
+           d="M 24.566405,20.003369 L 24.55078,20.886182 C 24.55078,20.886182 24.627261,22.641145 24.980468,22.120557 L 27,19.143995 L 27.011718,17.761182 C 27.015842,17.274511 26.433593,16.667432 26.433593,16.667432 L 24.566405,20.003369 z "
+           style="fill:url(#linearGradient2969);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      </g>
+      <path
+         style="fill:url(#linearGradient2971);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 24.411556,30 L 24.411556,27.640625 C 24.418043,25.968596 26.964528,24.413691 26.984375,23.28125 L 26.953125,25.064296 C 26.828816,26.620424 24.411556,28.192478 24.411556,30 z "
+         id="path3130"
+         sodipodi:nodetypes="cczcz" />
+      <path
+         style="fill:url(#linearGradient2973);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 24.427181,25.296875 L 24.427181,22.9375 C 24.433668,21.265471 26.980153,19.710566 27,18.578125 L 26.96875,20.361171 C 26.844441,21.917299 24.427181,23.489353 24.427181,25.296875 z "
+         id="path4101"
+         sodipodi:nodetypes="cczcz"
+         inkscape:transform-center-y="1.4375" />
+      <path
+         style="fill:url(#linearGradient2975);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 24.55734,21 L 24.55734,18.640625 C 24.782577,16.593596 27.045565,13.805709 27.036409,14.328125 L 27.005159,16.111171 C 26.88085,17.667299 24.55734,19.192478 24.55734,21 z "
+         id="path4103"
+         sodipodi:nodetypes="cczcz" />
+      <rect
+         transform="matrix(1,0,2.6056413e-5,1,0,0)"
+         style="opacity:1;fill:url(#linearGradient2977);fill-opacity:1;stroke:#2e3436;stroke-width:0.10721753;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect4105"
+         width="2.4375"
+         height="10.450078"
+         x="24.655802"
+         y="6.7641392"
+         rx="4.3645973"
+         ry="0" />
+      <path
+         style="fill:url(#linearGradient2979);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.1072175;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 25.30598,31.811622 C 25.687467,30.702093 26.891555,28.930237 26.90625,27.9375 L 26.875,30.048671 L 25.731221,31.83095 L 25.30598,31.811622 z "
+         id="path4109"
+         sodipodi:nodetypes="czccc" />
+    </g>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#eeeeec;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 13.5,6.90247 L 13.5,13.5"
+       id="path3042"
+       sodipodi:nodetypes="cc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-excellon-22.png b/data/application-x-excellon-22.png
new file mode 100644
index 0000000..9bafdee
Binary files /dev/null and b/data/application-x-excellon-22.png differ
diff --git a/data/application-x-excellon-22.svg b/data/application-x-excellon-22.svg
new file mode 100644
index 0000000..ad22dc0
--- /dev/null
+++ b/data/application-x-excellon-22.svg
@@ -0,0 +1,1571 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="22"
+   width="22"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="24.343483"
+       x2="23.599062"
+       y1="24.191589"
+       x1="26.116739"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4007"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="13.240394"
+       x2="25.726562"
+       y1="13.291025"
+       x1="26.868286"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4005"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="24.343483"
+       x2="23.599062"
+       y1="24.191589"
+       x1="26.116739"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2963"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="13.240394"
+       x2="25.726562"
+       y1="13.291025"
+       x1="26.868286"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2961"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4699">
+      <stop
+         id="stop4701"
+         offset="0"
+         style="stop-color:#ffe71a;stop-opacity:1;" />
+      <stop
+         id="stop4703"
+         offset="1"
+         style="stop-color:#c7a51e;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4143">
+      <stop
+         style="stop-color:#6f7368;stop-opacity:1;"
+         offset="0"
+         id="stop4145" />
+      <stop
+         style="stop-color:#cccec9;stop-opacity:1;"
+         offset="1"
+         id="stop4147" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2629">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop2631" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop2633" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3153"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient2636">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop2638" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop2640" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3151"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3149"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5060">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop5062" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5064" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3147"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5050" />
+      <stop
+         id="stop5056"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5052" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3145"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient4225"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-5.084646,22.928153)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2666">
+      <stop
+         id="stop2668"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2670"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2672">
+      <stop
+         id="stop2674"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2676"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2678">
+      <stop
+         id="stop2680"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2682"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2684"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.2500002,0.579507)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2686"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.960493,0,0,1.041132,-0.1035528,-6.694e-2)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2688"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient4211"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,15.73026,-12.552287)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient4209"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,20.68511,-9.3175274)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4207"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-30.95756,-25.612946)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2699">
+      <stop
+         id="stop2701"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2703" />
+      <stop
+         id="stop2705"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2707"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2709"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2711"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3245"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2718"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2720"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2943"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2941"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2939"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2937"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2935"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2933"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2931"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2929"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2927"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2925"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2923"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2921"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2919"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2911">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2913" />
+      <stop
+         id="stop2915"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2917" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2903"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2901"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2899"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2897"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2895"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.8244190"
+       cy="3.7561285"
+       cx="8.8244190"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2893"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2887">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2889" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2891" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2881">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2883" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2885" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2875">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2877" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2879" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2859"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2851">
+      <stop
+         id="stop2853"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2855" />
+      <stop
+         id="stop2857"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2849"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2841"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2839"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2833">
+      <stop
+         id="stop2835"
+         offset="0"
+         style="stop-color:#d8c100;stop-opacity:1;" />
+      <stop
+         id="stop2837"
+         offset="1"
+         style="stop-color:#ffe501;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2831"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2825">
+      <stop
+         id="stop2827"
+         offset="0"
+         style="stop-color:#605400;stop-opacity:1;" />
+      <stop
+         id="stop2829"
+         offset="1"
+         style="stop-color:#887800;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4149"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4158"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4141"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4139"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4137"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4129">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop4131" />
+      <stop
+         id="stop4133"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop4135" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4121"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4119"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4113">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4115" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4117" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4107">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4109" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop4111" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4101">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4103" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4105" />
+    </linearGradient>
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="matrix(0.725,0,0,0.8409306,-1.762498,11.789529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4087"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4081">
+      <stop
+         id="stop4083"
+         offset="0"
+         style="stop-color:#d8c100;stop-opacity:1;" />
+      <stop
+         id="stop4085"
+         offset="1"
+         style="stop-color:#ffe501;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="matrix(0.725,0,0,0.8409306,-1.762498,11.789529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4079"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4073">
+      <stop
+         id="stop4075"
+         offset="0"
+         style="stop-color:#605400;stop-opacity:1;" />
+      <stop
+         id="stop4077"
+         offset="1"
+         style="stop-color:#887800;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4383"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4385"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4699"
+       id="linearGradient4446"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4933921,0,0,0.5074627,-0.8105739,-1.425373)"
+       x1="35.5625"
+       y1="33.125"
+       x2="4.7477508"
+       y2="20.317982" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4450"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4452"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4454"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4456"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4458"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4460"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientTransform="matrix(0.9979686,-0.1028865,5.9911427e-2,0.9958589,-1.9843688,2.1678402)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4462"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientTransform="matrix(0.9979686,-0.1028865,5.9911427e-2,0.9958589,-1.9843688,2.1678402)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4464"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientTransform="matrix(0.9979686,-0.1028865,5.9911427e-2,0.9958589,-1.9843688,2.1678402)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4466"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9979686,-0.1028865,8.6767217e-2,1.4416312,-2.4470638,-5.5123156)"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4468"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientTransform="matrix(0.9979686,-0.1028865,5.9911427e-2,0.9958589,-1.9843688,2.1678402)" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="123"
+     inkscape:window-x="151"
+     inkscape:window-height="815"
+     inkscape:window-width="876"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="21.102805"
+     inkscape:cx="12.263783"
+     inkscape:zoom="12.361371"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="22px"
+     height="22px"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <sodipodi:guide
+       orientation="horizontal"
+       position="53.999577"
+       id="guide4448" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Excellon file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <g
+       style="display:inline"
+       id="g2354"
+       inkscape:label="Shadow"
+       transform="matrix(0.5199887,0,0,0.2954903,-1.4208371,7.7713745)">
+      <g
+         id="g2356"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2358"
+           style="opacity:1;fill:url(#radialGradient2370);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect2360"
+           style="opacity:1;fill:url(#radialGradient2372);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect2362"
+           style="opacity:1;fill:url(#linearGradient2374);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.0601086,0.50760946 L 18.963359,0.5 C 19.144215,0.5 19.5,0.8400659 19.5,1.1500805 L 19.5,19.876525 C 19.5,20.18654 19.250191,20.436118 18.939889,20.436118 L 3.0601086,20.436118 C 2.7498079,20.436118 2.499999,20.18654 2.499999,19.876525 L 2.499999,1.0672027 C 2.499999,0.75718813 2.7498079,0.50760946 3.0601086,0.50760946 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <rect
+       inkscape:export-ydpi="90"
+       inkscape:export-xdpi="90"
+       style="fill:url(#linearGradient4446);fill-opacity:1;stroke:none;stroke-width:1.50000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4669"
+       width="14"
+       height="17"
+       x="4"
+       y="2"
+       rx="0.64376038" />
+    <g
+       id="g3104"
+       transform="matrix(-1.1478494,0,0,-1.1478494,39.422379,38.082977)">
+      <path
+         transform="matrix(0.3800087,0,0,0.3800132,35.817333,21.47887)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3983"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.3800087,0,0,0.3800132,35.817333,17.122898)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3985"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.3800087,0,0,0.3800131,35.817333,12.766928)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3987"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255128;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,12.766925)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3989"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,17.122898)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3991"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,21.47887)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3993"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.5682125,1.5536151 L 18.43423,1.5131665 C 18.520034,1.5274672 18.540449,1.5370178 18.540449,1.5752023 L 18.5,19.494799 C 18.5,19.532984 18.469577,19.563724 18.431788,19.563724 L 3.5682125,19.563724 C 3.5304227,19.563724 3.4999999,19.532984 3.4999999,19.494799 L 3.4999999,1.6225403 C 3.4999999,1.5843558 3.5304227,1.5536151 3.5682125,1.5536151 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       transform="translate(20.367762,-27.201283)"
+       id="g5326"
+       style="opacity:0.38500001;display:inline" />
+    <path
+       style="fill:#d3d7cf;fill-rule:evenodd;stroke:#eeeeec;stroke-width:1.49999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 10.321071,1.5486532 L 8.5676603,13.443305 L 9.7171422,14.672724 L 11.38332,13.879946 L 13.325344,2.0644945"
+       id="path4208"
+       sodipodi:nodetypes="ccccc" />
+    <g
+       id="g4115"
+       transform="matrix(1.1786472,0.2804816,-0.2019289,0.9022082,-13.991976,-20.006169)"
+       style="fill:url(#linearGradient4450);fill-opacity:1">
+      <g
+         id="g4151"
+         style="fill:url(#linearGradient4452);fill-opacity:1"
+         transform="matrix(0.9979686,-0.1028865,5.9911427e-2,0.9958589,-1.9843688,2.1678402)">
+        <path
+           style="fill:url(#linearGradient4454);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 24.375,29.21875 L 24.359375,30.03125 L 25.282326,31.027198 L 26.890625,28.390625 L 26.84375,27.265625 C 26.809535,26.444455 26.25,26.296875 26.25,26.296875 L 24.375,29.21875 z "
+           id="path4107"
+           sodipodi:nodetypes="cccczcc" />
+        <path
+           style="fill:url(#linearGradient4456);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 24.453125,24.484375 L 24.46875,25.34375 C 24.46875,25.34375 24.598474,27.12898 24.953125,26.609375 L 26.96875,23.65625 L 26.921875,22.53125 C 26.88766,21.71008 26.328125,21.5625 26.328125,21.5625 L 24.453125,24.484375 z "
+           id="path4111"
+           sodipodi:nodetypes="cczczcc" />
+        <path
+           style="fill:url(#linearGradient4458);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 24.566405,20.003369 L 24.55078,20.886182 C 24.55078,20.886182 24.627261,22.641145 24.980468,22.120557 L 27,19.143995 L 27.011718,17.761182 C 27.015842,17.274511 26.433593,16.667432 26.433593,16.667432 L 24.566405,20.003369 z "
+           id="path4113"
+           sodipodi:nodetypes="cczczcc"
+           inkscape:transform-center-y="2.5708492"
+           inkscape:transform-center-x="0.40625" />
+      </g>
+      <path
+         sodipodi:nodetypes="cczcz"
+         id="path3130"
+         d="M 24.174939,29.531986 L 24.033586,27.182381 C 23.939886,25.516609 26.388041,23.706144 26.340002,22.576351 L 26.41564,24.355228 C 26.384814,25.917702 24.066648,27.731949 24.174939,29.531986 z "
+         style="fill:url(#linearGradient4460);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         inkscape:transform-center-y="1.4330632"
+         sodipodi:nodetypes="cczcz"
+         id="path4101"
+         d="M 23.908761,24.846729 L 23.767408,22.497125 C 23.673708,20.831352 26.121864,19.020888 26.073824,17.891094 L 26.149463,19.669972 C 26.118636,21.232445 23.80047,23.046692 23.908761,24.846729 z "
+         style="fill:url(#linearGradient4462);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         inkscape:transform-center-x="-0.11284996" />
+      <path
+         sodipodi:nodetypes="cczcz"
+         id="path4103"
+         d="M 23.781224,20.554257 L 23.639871,18.204652 C 23.74201,16.142926 25.833374,13.133753 25.855536,13.654948 L 25.931174,15.433825 C 25.900347,16.996299 23.672933,18.75422 23.781224,20.554257 z "
+         style="fill:url(#linearGradient4464);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      <path
+         sodipodi:nodetypes="cscccc"
+         id="rect4105"
+         d="M 23.248084,10.054367 C 23.747229,10.569122 24.130006,10.473134 24.511317,9.9909255 C 24.761424,9.674638 25.208435,8.830077 25.722106,8.982516 L 26.085672,16.523187 L 23.653122,16.773973 L 23.248084,10.054367 z "
+         style="fill:url(#linearGradient4466);stroke:#2e3436;stroke-width:0.09449018;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="czccc"
+         id="path4109"
+         d="M 25.108322,30.489774 C 25.42256,29.34559 26.585809,28.211494 26.540998,27.221356 L 26.636295,29.327 L 25.533857,30.46527 L 25.108322,30.489774 z "
+         style="fill:url(#linearGradient4468);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+    </g>
+    <g
+       id="g4320"
+       inkscape:label="Text"
+       style="display:inline"
+       transform="translate(28.961512,-16.718176)" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-excellon-24.png b/data/application-x-excellon-24.png
new file mode 100644
index 0000000..fc2a265
Binary files /dev/null and b/data/application-x-excellon-24.png differ
diff --git a/data/application-x-excellon-32.png b/data/application-x-excellon-32.png
new file mode 100644
index 0000000..087302c
Binary files /dev/null and b/data/application-x-excellon-32.png differ
diff --git a/data/application-x-excellon-32.svg b/data/application-x-excellon-32.svg
new file mode 100644
index 0000000..0de4a68
--- /dev/null
+++ b/data/application-x-excellon-32.svg
@@ -0,0 +1,1406 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="32"
+   width="32"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.725,0,0,0.8409306,-1.762498,11.789529)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.725,0,0,0.8409306,-1.762498,11.789529)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       y2="24.343483"
+       x2="23.599062"
+       y1="24.191589"
+       x1="26.116739"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4158"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="13.240394"
+       x2="25.726562"
+       y1="13.291025"
+       x1="26.868286"
+       id="linearGradient4149"
+       xlink:href="#linearGradient4143"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2825">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop2827" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop2829" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2831"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient2833">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop2835" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop2837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2839"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2841"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2849"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient2851">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2853" />
+      <stop
+         id="stop2855"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2857" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient2859"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2873"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2875">
+      <stop
+         id="stop2877"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2879"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2881">
+      <stop
+         id="stop2883"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2885"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2887">
+      <stop
+         id="stop2889"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2891"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2893"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2895"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2897"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2899"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2901"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2903"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2911">
+      <stop
+         id="stop2913"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2915" />
+      <stop
+         id="stop2917"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2919"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2921"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2923"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient2925"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2927"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2929"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2931"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2933"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient2935"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient2937"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient2939"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2941"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient2943"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2720"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2718"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3245"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3243"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3241"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3239"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3237"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2711"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2709"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2707"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2699">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2701" />
+      <stop
+         id="stop2703"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2705" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-30.95756,-25.612946)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient4796"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,20.68511,-9.3175274)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5352"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,15.73026,-12.552287)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2688"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.960493,0,0,1.041132,-0.1035528,-6.694e-2)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2686"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.8244190"
+       cy="3.7561285"
+       cx="8.8244190"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.2500002,0.579507)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2684"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2678">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2680" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2682" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2672">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2674" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2676" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2666">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2668" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2670" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-5.084646,22.928153)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient8166"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3145"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         id="stop5050"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056" />
+      <stop
+         id="stop5052"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3147"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5060"
+       inkscape:collect="always">
+      <stop
+         id="stop5062"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop5064"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3149"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3151"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2636">
+      <stop
+         id="stop2638"
+         offset="0"
+         style="stop-color:#d8c100;stop-opacity:1;" />
+      <stop
+         id="stop2640"
+         offset="1"
+         style="stop-color:#ffe501;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3153"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2629">
+      <stop
+         id="stop2631"
+         offset="0"
+         style="stop-color:#605400;stop-opacity:1;" />
+      <stop
+         id="stop2633"
+         offset="1"
+         style="stop-color:#887800;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4143">
+      <stop
+         id="stop4145"
+         offset="0"
+         style="stop-color:#6f7368;stop-opacity:1;" />
+      <stop
+         id="stop4147"
+         offset="1"
+         style="stop-color:#cccec9;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4699">
+      <stop
+         style="stop-color:#ffe71a;stop-opacity:1;"
+         offset="0"
+         id="stop4701" />
+      <stop
+         style="stop-color:#c7a51e;stop-opacity:1;"
+         offset="1"
+         id="stop4703" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2961"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2963"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2965"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2967"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2969"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2971"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2973"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2975"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2977"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.4476299,-4.4892272e-4,-7.7121596)"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient2979"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4699"
+       id="linearGradient3004"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7048458,0,0,0.7164179,-0.8722467,-0.8358206)"
+       x1="35.5625"
+       y1="33.125"
+       x2="11.588099"
+       y2="19.867088" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4005"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4007"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="7.5109921"
+     inkscape:cx="55.883583"
+     inkscape:zoom="6.6095375"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     gridtolerance="0.4"
+     width="32px"
+     height="32px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Excellon file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="matrix(0.722222,0,0,0.444445,-1.2474509,10.111099)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <rect
+       rx="0.91965771"
+       y="4"
+       x="6"
+       height="24"
+       width="20"
+       id="rect4669"
+       style="fill:url(#linearGradient3004);fill-opacity:1;stroke:none;stroke-width:1.50000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <g
+       transform="matrix(-1.1478494,0,0,-1.1478494,41.09879,41.487463)"
+       id="g3104">
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3102"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,35.817332,23.221258)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3983"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,35.817333,19.736481)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3985"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,35.817333,16.251703)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3987"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,35.817333,12.766926)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3989"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,12.766925)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3991"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,16.251703)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3993"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,19.736481)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3995"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,27.105388,23.221259)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3997"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,21.878222,23.221259)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:2.29255104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         id="path3999"
+         sodipodi:cx="-19.519127"
+         sodipodi:cy="16.811073"
+         sodipodi:rx="2.2922819"
+         sodipodi:ry="2.2922819"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:start="4.2429731"
+         sodipodi:end="10.524328"
+         sodipodi:open="true"
+         transform="matrix(0.3800087,0,0,0.3800132,21.878222,12.766925)" />
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.2577956,2.5103056 L 26.775661,2.5 C 27.191545,2.4998008 27.5,2.8726783 27.5,3.2925332 L 27.5,28.742131 C 27.5,29.161992 27.162024,29.500002 26.742205,29.500002 L 5.2577956,29.500002 C 4.8379769,29.500002 4.5,29.161992 4.5,28.742131 L 4.5,3.2681771 C 4.5,2.8483163 4.8379769,2.5103056 5.2577956,2.5103056 z "
+       id="rect15391"
+       sodipodi:nodetypes="czscccccc" />
+    <g
+       style="display:inline;opacity:0.385"
+       id="g5326"
+       transform="translate(-8.59375,-10.483107)" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.5954975,3.5 L 26.307144,3.5 C 26.439726,3.5 26.500001,3.5982653 26.500001,3.6511397 L 26.500001,28.343295 C 26.500001,28.396168 26.457409,28.438736 26.404502,28.438736 L 5.5954975,28.438736 C 5.5425918,28.438736 5.5000003,28.396168 5.5000003,28.343295 L 5.5000003,3.5954413 C 5.5000003,3.5425669 5.5425918,3.5 5.5954975,3.5 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <rect
+       style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3127"
+       width="29"
+       height="5"
+       x="1.5"
+       y="21.5" />
+    <rect
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3129"
+       width="27"
+       height="3"
+       x="2.5"
+       y="22.5"
+       ry="0" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 28.5,21.5 L 28.5,24.5"
+       id="path3131" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 24.5,21.5 L 24.5,24.5"
+       id="path3133" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 20.5,21.5 L 20.5,24.5"
+       id="path3135" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 16.5,21.5 L 16.5,24.5"
+       id="path3137" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 12.5,21.5 L 12.5,24.5"
+       id="path3139" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 8.499996,21.5 L 8.499996,24.5"
+       id="path3141" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 4.5,21.5 L 4.5,24.5"
+       id="path3143" />
+    <path
+       sodipodi:nodetypes="ccccc"
+       id="path4208"
+       d="M 13.79348,6.2643059 L 11.111699,17.984595 L 12.161117,19.300459 L 13.884388,18.640931 L 16.747985,7.0144044"
+       style="fill:#d3d7cf;fill-rule:evenodd;stroke:#8f5902;stroke-width:1.49999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    <g
+       style="fill:url(#linearGradient4005);fill-opacity:1"
+       transform="matrix(1.1786472,0.2804816,-0.2019289,0.9022082,-11.630277,-15.95571)"
+       id="g4115">
+      <g
+         style="fill:url(#linearGradient4007);fill-opacity:1"
+         id="g4151">
+        <path
+           sodipodi:nodetypes="cccczcc"
+           id="path4107"
+           d="M 24.375,29.21875 L 24.359375,30.03125 L 25.282326,31.027198 L 26.890625,28.390625 L 26.84375,27.265625 C 26.809535,26.444455 26.25,26.296875 26.25,26.296875 L 24.375,29.21875 z "
+           style="fill:url(#linearGradient2965);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <path
+           sodipodi:nodetypes="cczczcc"
+           id="path4111"
+           d="M 24.453125,24.484375 L 24.46875,25.34375 C 24.46875,25.34375 24.598474,27.12898 24.953125,26.609375 L 26.96875,23.65625 L 26.921875,22.53125 C 26.88766,21.71008 26.328125,21.5625 26.328125,21.5625 L 24.453125,24.484375 z "
+           style="fill:url(#linearGradient2967);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+        <path
+           inkscape:transform-center-x="0.40625"
+           inkscape:transform-center-y="2.5708492"
+           sodipodi:nodetypes="cczczcc"
+           id="path4113"
+           d="M 24.566405,20.003369 L 24.55078,20.886182 C 24.55078,20.886182 24.627261,22.641145 24.980468,22.120557 L 27,19.143995 L 27.011718,17.761182 C 27.015842,17.274511 26.433593,16.667432 26.433593,16.667432 L 24.566405,20.003369 z "
+           style="fill:url(#linearGradient2969);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      </g>
+      <path
+         style="fill:url(#linearGradient2971);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 24.411556,30 L 24.411556,27.640625 C 24.418043,25.968596 26.964528,24.413691 26.984375,23.28125 L 26.953125,25.064296 C 26.828816,26.620424 24.411556,28.192478 24.411556,30 z "
+         id="path3130"
+         sodipodi:nodetypes="cczcz" />
+      <path
+         style="fill:url(#linearGradient2973);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 24.427181,25.296875 L 24.427181,22.9375 C 24.433668,21.265471 26.980153,19.710566 27,18.578125 L 26.96875,20.361171 C 26.844441,21.917299 24.427181,23.489353 24.427181,25.296875 z "
+         id="path4101"
+         sodipodi:nodetypes="cczcz"
+         inkscape:transform-center-y="1.4375" />
+      <path
+         style="fill:url(#linearGradient2975);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 24.55734,21 L 24.55734,18.640625 C 24.782577,16.593596 27.045565,13.805709 27.036409,14.328125 L 27.005159,16.111171 C 26.88085,17.667299 24.55734,19.192478 24.55734,21 z "
+         id="path4103"
+         sodipodi:nodetypes="cczcz" />
+      <path
+         transform="matrix(1,0,2.6056413e-5,1,0,0)"
+         style="fill:url(#linearGradient2977);stroke:#2e3436;stroke-width:0.09449018;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 24.655198,10.466587 C 25.121422,11.031651 25.508366,10.975241 25.916999,10.533243 C 26.185027,10.243331 26.680807,9.4464758 27.183213,9.651455 L 27.093303,17.214216 L 24.655802,17.214216 L 24.655198,10.466587 z "
+         id="rect4105"
+         sodipodi:nodetypes="cscccc" />
+      <path
+         style="fill:url(#linearGradient2979);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.09449017;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 25.283691,31.051875 C 25.665178,29.942346 26.891555,28.930237 26.90625,27.9375 L 26.875,30.048671 L 25.708932,31.071203 L 25.283691,31.051875 z "
+         id="path4109"
+         sodipodi:nodetypes="czccc" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-excellon-48.png b/data/application-x-excellon-48.png
new file mode 100644
index 0000000..b4c84b3
Binary files /dev/null and b/data/application-x-excellon-48.png differ
diff --git a/data/application-x-excellon-48.svg b/data/application-x-excellon-48.svg
new file mode 100644
index 0000000..bd0acd4
--- /dev/null
+++ b/data/application-x-excellon-48.svg
@@ -0,0 +1,1283 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="48.000000px"
+   width="48.000000px"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4699">
+      <stop
+         id="stop4701"
+         offset="0"
+         style="stop-color:#ffe71a;stop-opacity:1;" />
+      <stop
+         id="stop4703"
+         offset="1"
+         style="stop-color:#c7a51e;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4143">
+      <stop
+         style="stop-color:#6f7368;stop-opacity:1;"
+         offset="0"
+         id="stop4145" />
+      <stop
+         style="stop-color:#cccec9;stop-opacity:1;"
+         offset="1"
+         id="stop4147" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3153"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3151"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3149"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5060">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop5062" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5064" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3147"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5050" />
+      <stop
+         id="stop5056"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5052" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3145"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-5.084646,22.928153)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.2500002,0.579507)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.960493,0,0,1.041132,-0.1035528,-6.694e-2)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,15.73026,-12.552287)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,20.68511,-9.3175274)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-30.95756,-25.612946)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3245"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3354"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3356"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2943"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2941"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2939"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2937"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2935"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2933"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2931"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2929"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2927"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2925"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2923"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2921"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2919"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2911">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2913" />
+      <stop
+         id="stop2915"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2917" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2903"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2901"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2899"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2897"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2895"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.8244190"
+       cy="3.7561285"
+       cx="8.8244190"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2893"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2887">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2889" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2891" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2881">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2883" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2885" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2875">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2877" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2879" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2859"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2851">
+      <stop
+         id="stop2853"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2855" />
+      <stop
+         id="stop2857"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2849"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2841"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2839"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2833">
+      <stop
+         id="stop2835"
+         offset="0"
+         style="stop-color:#d8c100;stop-opacity:1;" />
+      <stop
+         id="stop2837"
+         offset="1"
+         style="stop-color:#ffe501;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2831"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2825">
+      <stop
+         id="stop2827"
+         offset="0"
+         style="stop-color:#605400;stop-opacity:1;" />
+      <stop
+         id="stop2829"
+         offset="1"
+         style="stop-color:#887800;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4149"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4158"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4182"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4184"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4186"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4188"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4190"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4192"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4194"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientTransform="matrix(1,0,0,1.4476299,-4.4892272e-4,-7.7121596)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4196"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4699"
+       id="linearGradient4697"
+       x1="35.5625"
+       y1="33.125"
+       x2="22.75"
+       y2="26.625"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0572687,0,0,1.0223881,-1.3083701,-0.9011194)" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="5"
+     inkscape:window-height="967"
+     inkscape:window-width="993"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="34.454858"
+     inkscape:cx="38.982143"
+     inkscape:zoom="8"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Excellon file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="translate(-2e-6,2.838692e-5)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 7.6490486,3.5795062 L 40.096063,3.5638812 C 40.846303,3.5635199 41.375,4.232605 41.375,4.869166 L 41.375,43.350952 C 41.375,43.987525 40.862524,44.5 40.225951,44.5 L 7.6490486,44.5 C 7.0124757,44.5 6.5,43.987525 6.5,43.350952 L 6.5,4.7285548 C 6.5,4.0919819 7.0124757,3.5795062 7.6490486,3.5795062 z "
+       id="rect15391"
+       sodipodi:nodetypes="csscccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.8151023,4.5839462 L 40.128994,4.5839462 C 40.128994,4.5839462 40.44194,4.792873 40.44194,4.875446 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient4697);fill-opacity:1;stroke:none;stroke-width:1.50000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect4669"
+       width="30"
+       height="34.25"
+       x="9"
+       y="6"
+       rx="1.3794866" />
+    <path
+       style="fill:#d3d7cf;fill-rule:evenodd;stroke:#8f5902;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 18.8125,12.75 L 15.125,28.375 L 16.75,29.625 L 18.9375,29.25 L 22.875,13.75"
+       id="path4208"
+       sodipodi:nodetypes="ccccc" />
+    <g
+       id="g3104"
+       transform="matrix(-1.5006961,0,0,-1.5006961,56.271231,57.208221)">
+      <path
+         inkscape:transform-center-y="3.264765"
+         transform="matrix(0.4361927,0,0,0.436198,27.014112,14.167043)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3076"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.4361926,0,0,0.4361979,27.014111,10.167045)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3078"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         inkscape:transform-center-y="3.264765"
+         transform="matrix(0.4361927,0,0,0.436198,27.014113,22.167043)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3092"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.4361926,0,0,0.4361979,27.014112,18.167045)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3094"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         inkscape:transform-center-y="3.264765"
+         transform="matrix(0.4361927,0,0,0.436198,38.014111,14.167043)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3096"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.4361926,0,0,0.4361979,38.01411,10.167045)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3098"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.4361926,0,0,0.4361979,38.014111,18.167045)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3100"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.4361926,0,0,0.4361979,38.014111,22.167045)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path3102"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         inkscape:transform-center-y="3.264765"
+         transform="matrix(0.4361927,0,0,0.436198,21.390672,10.13155)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path4200"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+      <path
+         inkscape:transform-center-y="3.264765"
+         transform="matrix(0.4361927,0,0,0.436198,21.390672,22.125984)"
+         sodipodi:open="true"
+         sodipodi:end="10.524328"
+         sodipodi:start="4.2429731"
+         d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+         sodipodi:ry="2.2922819"
+         sodipodi:rx="2.2922819"
+         sodipodi:cy="16.811073"
+         sodipodi:cx="-19.519127"
+         id="path4204"
+         style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+         sodipodi:type="arc" />
+    </g>
+    <g
+       id="g4115"
+       transform="matrix(1.6206621,0.3739264,-0.2776561,1.2027862,-16.145643,-16.872796)"
+       style="fill:url(#linearGradient4149);fill-opacity:1">
+      <g
+         id="g4151"
+         style="fill:url(#linearGradient4158);fill-opacity:1">
+        <path
+           style="fill:url(#linearGradient4182);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 24.375,29.21875 L 24.359375,30.03125 L 25.515625,30.703125 L 26.890625,28.390625 L 26.84375,27.265625 C 26.809535,26.444455 26.25,26.296875 26.25,26.296875 L 24.375,29.21875 z "
+           id="path4107"
+           sodipodi:nodetypes="cccczcc" />
+        <path
+           style="fill:url(#linearGradient4184);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 24.453125,24.484375 L 24.46875,25.34375 C 24.46875,25.34375 24.598474,27.12898 24.953125,26.609375 L 26.96875,23.65625 L 26.921875,22.53125 C 26.88766,21.71008 26.328125,21.5625 26.328125,21.5625 L 24.453125,24.484375 z "
+           id="path4111"
+           sodipodi:nodetypes="cczczcc" />
+        <path
+           style="fill:url(#linearGradient4186);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 24.566405,20.003369 L 24.55078,20.886182 C 24.55078,20.886182 24.627261,22.641145 24.980468,22.120557 L 27,19.143995 L 27.011718,17.761182 C 27.015842,17.274511 26.433593,16.667432 26.433593,16.667432 L 24.566405,20.003369 z "
+           id="path4113"
+           sodipodi:nodetypes="cczczcc"
+           inkscape:transform-center-y="2.5708492"
+           inkscape:transform-center-x="0.40625" />
+      </g>
+      <path
+         sodipodi:nodetypes="cczcz"
+         id="path3130"
+         d="M 24.411556,30 L 24.411556,27.640625 C 24.418043,25.968596 26.964528,24.413691 26.984375,23.28125 L 26.953125,25.064296 C 26.828816,26.620424 24.411556,28.192478 24.411556,30 z "
+         style="fill:url(#linearGradient4188);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         inkscape:transform-center-y="1.4375"
+         sodipodi:nodetypes="cczcz"
+         id="path4101"
+         d="M 24.427181,25.296875 L 24.427181,22.9375 C 24.433668,21.265471 26.980153,19.710566 27,18.578125 L 26.96875,20.361171 C 26.844441,21.917299 24.427181,23.489353 24.427181,25.296875 z "
+         style="fill:url(#linearGradient4190);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      <path
+         sodipodi:nodetypes="cczcz"
+         id="path4103"
+         d="M 24.55734,21 L 24.55734,18.640625 C 24.782577,16.593596 27.045565,13.805709 27.036409,14.328125 L 27.005159,16.111171 C 26.88085,17.667299 24.55734,19.192478 24.55734,21 z "
+         style="fill:url(#linearGradient4192);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      <rect
+         ry="0"
+         rx="4.3645973"
+         y="6.7641392"
+         x="24.655802"
+         height="10.450078"
+         width="2.4375"
+         id="rect4105"
+         style="opacity:1;fill:url(#linearGradient4194);fill-opacity:1;stroke:#2e3436;stroke-width:0.06978973;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         transform="matrix(1,0,2.6056413e-5,1,0,0)" />
+      <path
+         sodipodi:nodetypes="czccc"
+         id="path4109"
+         d="M 25.427181,30.671875 C 25.808668,29.562346 26.891555,28.930237 26.90625,27.9375 L 26.875,30.048671 L 25.852422,30.691203 L 25.427181,30.671875 z "
+         style="fill:url(#linearGradient4196);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       style="display:inline"
+       transform="matrix(2.105461e-2,0,0,2.086758e-2,43.101716,38.66252)"
+       id="g3081">
+      <rect
+         style="opacity:0.40206185;color:#000000;fill:url(#linearGradient3352);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         id="rect3083"
+         width="1339.6335"
+         height="478.35718"
+         x="-1559.2523"
+         y="-150.69685" />
+      <path
+         style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3354);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M -219.61876,-150.68038 C -219.61876,-150.68038 -219.61876,327.65041 -219.61876,327.65041 C -76.744594,328.55086 125.78146,220.48075 125.78138,88.454235 C 125.78138,-43.572302 -33.655436,-150.68036 -219.61876,-150.68038 z "
+         id="path3085"
+         sodipodi:nodetypes="cccc" />
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path3087"
+         d="M -1559.2523,-150.68038 C -1559.2523,-150.68038 -1559.2523,327.65041 -1559.2523,327.65041 C -1702.1265,328.55086 -1904.6525,220.48075 -1904.6525,88.454235 C -1904.6525,-43.572302 -1745.2157,-150.68036 -1559.2523,-150.68038 z "
+         style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3356);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    </g>
+    <rect
+       style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3127"
+       width="40"
+       height="6.0000005"
+       x="4.5"
+       y="35.5" />
+    <rect
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3129"
+       width="38"
+       height="4"
+       x="5.5"
+       y="36.5"
+       ry="0" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 39.5,35.5 L 39.5,38.5"
+       id="path3131" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 34.499995,35.5 L 34.499995,38.5"
+       id="path3133" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 29.499995,35.5 L 29.499995,38.5"
+       id="path3135" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 24.499995,35.5 L 24.499995,38.5"
+       id="path3137" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 19.499995,35.5 L 19.499995,38.5"
+       id="path3139" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 14.499996,35.5 L 14.499996,38.5"
+       id="path3141" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 9.499995,35.5 L 9.499995,38.5"
+       id="path3143" />
+  </g>
+</svg>
diff --git a/data/application-x-excellon.svg b/data/application-x-excellon.svg
new file mode 100644
index 0000000..00fd6b5
--- /dev/null
+++ b/data/application-x-excellon.svg
@@ -0,0 +1,1289 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="128"
+   width="128"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4699">
+      <stop
+         id="stop4701"
+         offset="0"
+         style="stop-color:#ffe71a;stop-opacity:1;" />
+      <stop
+         id="stop4703"
+         offset="1"
+         style="stop-color:#c7a51e;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4143">
+      <stop
+         style="stop-color:#6f7368;stop-opacity:1;"
+         offset="0"
+         id="stop4145" />
+      <stop
+         style="stop-color:#cccec9;stop-opacity:1;"
+         offset="1"
+         id="stop4147" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3153"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3151"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3149"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5060">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop5062" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5064" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3147"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5050" />
+      <stop
+         id="stop5056"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5052" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3145"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-5.084646,22.928153)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.2500002,0.579507)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.960493,0,0,1.041132,-0.1035528,-6.694e-2)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,15.73026,-12.552287)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,20.68511,-9.3175274)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-30.95756,-25.612946)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3245"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3354"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3356"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2943"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2941"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2939"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2937"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2935"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2933"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientTransform="translate(0.2499915,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2931"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2929"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2927"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2925"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2923"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2921"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2919"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2911">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2913" />
+      <stop
+         id="stop2915"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2917" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2903"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2901"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2899"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2897"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2895"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2893"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2887">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2889" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2891" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2881">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2883" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2885" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2875">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2877" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2879" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2859"
+       xlink:href="#linearGradient5048"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2851">
+      <stop
+         id="stop2853"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2855" />
+      <stop
+         id="stop2857"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2849"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2841"
+       xlink:href="#linearGradient5060"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="14.544756"
+       x2="6.256557"
+       y1="14.544756"
+       x1="43.421204"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2839"
+       xlink:href="#linearGradient10424"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2833">
+      <stop
+         id="stop2835"
+         offset="0"
+         style="stop-color:#d8c100;stop-opacity:1;" />
+      <stop
+         id="stop2837"
+         offset="1"
+         style="stop-color:#ffe501;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(44.874995,-2.75)"
+       y2="11.919756"
+       x2="4.6249981"
+       y1="17.482256"
+       x1="44.625"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2831"
+       xlink:href="#linearGradient10440"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2825">
+      <stop
+         id="stop2827"
+         offset="0"
+         style="stop-color:#605400;stop-opacity:1;" />
+      <stop
+         id="stop2829"
+         offset="1"
+         style="stop-color:#887800;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4149"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4158"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4182"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4184"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4186"
+       gradientUnits="userSpaceOnUse"
+       x1="26.116739"
+       y1="24.191589"
+       x2="23.599062"
+       y2="24.343483" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4188"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4190"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4192"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4194"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394"
+       gradientTransform="matrix(1,0,0,1.4476299,-4.4892272e-4,-7.7121596)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4143"
+       id="linearGradient4196"
+       gradientUnits="userSpaceOnUse"
+       x1="26.868286"
+       y1="13.291025"
+       x2="25.726562"
+       y2="13.240394" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4699"
+       id="linearGradient4697"
+       x1="35.5625"
+       y1="33.125"
+       x2="22.75"
+       y2="26.625"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0572687,0,0,1.0223881,-1.3083701,-0.9011194)" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="5"
+     inkscape:window-height="967"
+     inkscape:window-width="993"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer5"
+     inkscape:cy="51.789128"
+     inkscape:cx="5.8207974"
+     inkscape:zoom="2"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     width="128px"
+     height="128px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Excellon file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2357"
+       transform="scale(2.6667,2.6667)">
+      <g
+         transform="translate(-2e-6,2.838692e-5)"
+         inkscape:label="Shadow"
+         id="g2033"
+         style="display:inline">
+        <g
+           transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)"
+           style="opacity:0.4"
+           id="g3712">
+          <rect
+             style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect2801"
+             width="5"
+             height="7"
+             x="38"
+             y="40" />
+          <rect
+             style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3696"
+             width="5"
+             height="7"
+             x="-10"
+             y="-47"
+             transform="scale(-1,-1)" />
+          <rect
+             style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3700"
+             width="28"
+             height="7.0000005"
+             x="10"
+             y="40" />
+        </g>
+      </g>
+      <path
+         sodipodi:nodetypes="csscccccc"
+         id="rect15391"
+         d="M 7.6490486,3.5795062 L 40.096063,3.5638812 C 40.846303,3.5635199 41.375,4.232605 41.375,4.869166 L 41.375,43.350952 C 41.375,43.987525 40.862524,44.5 40.225951,44.5 L 7.6490486,44.5 C 7.0124757,44.5 6.5,43.987525 6.5,43.350952 L 6.5,4.7285548 C 6.5,4.0919819 7.0124757,3.5795062 7.6490486,3.5795062 z "
+         style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15660"
+         d="M 7.8151023,4.5839462 L 40.128994,4.5839462 C 40.128994,4.5839462 40.44194,4.792873 40.44194,4.875446 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <rect
+         rx="1.3794866"
+         y="6"
+         x="9"
+         height="34.25"
+         width="30"
+         id="rect4669"
+         style="opacity:1;fill:url(#linearGradient4697);fill-opacity:1;stroke:none;stroke-width:1.50000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+      <path
+         sodipodi:nodetypes="ccccc"
+         id="path4208"
+         d="M 18.8125,12.75 L 15.125,28.375 L 16.75,29.625 L 18.9375,29.25 L 22.875,13.75"
+         style="fill:#d3d7cf;fill-rule:evenodd;stroke:#8f5902;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      <g
+         transform="matrix(-1.5006961,0,0,-1.5006961,56.271231,57.208221)"
+         id="g3104"
+         style="display:inline">
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3076"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361927,0,0,0.436198,27.014112,14.167043)"
+           inkscape:transform-center-y="3.264765" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3078"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361926,0,0,0.4361979,27.014111,10.167045)" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3092"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361927,0,0,0.436198,27.014113,22.167043)"
+           inkscape:transform-center-y="3.264765" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3094"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361926,0,0,0.4361979,27.014112,18.167045)" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3096"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361927,0,0,0.436198,38.014111,14.167043)"
+           inkscape:transform-center-y="3.264765" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3098"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361926,0,0,0.4361979,38.01411,10.167045)" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3100"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361926,0,0,0.4361979,38.014111,18.167045)" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765882;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path3102"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361926,0,0,0.4361979,38.014111,22.167045)" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path4200"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361927,0,0,0.436198,21.390672,10.13155)"
+           inkscape:transform-center-y="3.264765" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:#2e3436;stroke-width:1.52765846;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="path4204"
+           sodipodi:cx="-19.519127"
+           sodipodi:cy="16.811073"
+           sodipodi:rx="2.2922819"
+           sodipodi:ry="2.2922819"
+           d="M -20.556076,14.766741 A 2.2922819,2.2922819 0 1 1 -20.559816,14.768643"
+           sodipodi:start="4.2429731"
+           sodipodi:end="10.524328"
+           sodipodi:open="true"
+           transform="matrix(0.4361927,0,0,0.436198,21.390672,22.125984)"
+           inkscape:transform-center-y="3.264765" />
+      </g>
+      <g
+         style="fill:url(#linearGradient4149);fill-opacity:1;display:inline"
+         transform="matrix(1.6206621,0.3739264,-0.2776561,1.2027862,-16.145643,-16.872796)"
+         id="g4115">
+        <g
+           style="fill:url(#linearGradient4158);fill-opacity:1"
+           id="g4151">
+          <path
+             sodipodi:nodetypes="cccczcc"
+             id="path4107"
+             d="M 24.375,29.21875 L 24.359375,30.03125 L 25.515625,30.703125 L 26.890625,28.390625 L 26.84375,27.265625 C 26.809535,26.444455 26.25,26.296875 26.25,26.296875 L 24.375,29.21875 z "
+             style="fill:url(#linearGradient4182);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+          <path
+             sodipodi:nodetypes="cczczcc"
+             id="path4111"
+             d="M 24.453125,24.484375 L 24.46875,25.34375 C 24.46875,25.34375 24.598474,27.12898 24.953125,26.609375 L 26.96875,23.65625 L 26.921875,22.53125 C 26.88766,21.71008 26.328125,21.5625 26.328125,21.5625 L 24.453125,24.484375 z "
+             style="fill:url(#linearGradient4184);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+          <path
+             inkscape:transform-center-x="0.40625"
+             inkscape:transform-center-y="2.5708492"
+             sodipodi:nodetypes="cczczcc"
+             id="path4113"
+             d="M 24.566405,20.003369 L 24.55078,20.886182 C 24.55078,20.886182 24.627261,22.641145 24.980468,22.120557 L 27,19.143995 L 27.011718,17.761182 C 27.015842,17.274511 26.433593,16.667432 26.433593,16.667432 L 24.566405,20.003369 z "
+             style="fill:url(#linearGradient4186);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+        </g>
+        <path
+           style="fill:url(#linearGradient4188);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 24.411556,30 L 24.411556,27.640625 C 24.418043,25.968596 26.964528,24.413691 26.984375,23.28125 L 26.953125,25.064296 C 26.828816,26.620424 24.411556,28.192478 24.411556,30 z "
+           id="path3130"
+           sodipodi:nodetypes="cczcz" />
+        <path
+           style="fill:url(#linearGradient4190);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 24.427181,25.296875 L 24.427181,22.9375 C 24.433668,21.265471 26.980153,19.710566 27,18.578125 L 26.96875,20.361171 C 26.844441,21.917299 24.427181,23.489353 24.427181,25.296875 z "
+           id="path4101"
+           sodipodi:nodetypes="cczcz"
+           inkscape:transform-center-y="1.4375" />
+        <path
+           style="fill:url(#linearGradient4192);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 24.55734,21 L 24.55734,18.640625 C 24.782577,16.593596 27.045565,13.805709 27.036409,14.328125 L 27.005159,16.111171 C 26.88085,17.667299 24.55734,19.192478 24.55734,21 z "
+           id="path4103"
+           sodipodi:nodetypes="cczcz" />
+        <rect
+           transform="matrix(1,0,2.6056413e-5,1,0,0)"
+           style="opacity:1;fill:url(#linearGradient4194);fill-opacity:1;stroke:#2e3436;stroke-width:0.06978973;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect4105"
+           width="2.4375"
+           height="10.450078"
+           x="24.655802"
+           y="6.7641392"
+           rx="4.3645973"
+           ry="0" />
+        <path
+           style="fill:url(#linearGradient4196);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.06978972;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 25.427181,30.671875 C 25.808668,29.562346 26.891555,28.930237 26.90625,27.9375 L 26.875,30.048671 L 25.852422,30.691203 L 25.427181,30.671875 z "
+           id="path4109"
+           sodipodi:nodetypes="czccc" />
+      </g>
+      <g
+         id="g3081"
+         transform="matrix(2.105461e-2,0,0,2.086758e-2,43.101716,38.66252)"
+         style="display:inline">
+        <rect
+           y="-150.69685"
+           x="-1559.2523"
+           height="478.35718"
+           width="1339.6335"
+           id="rect3083"
+           style="opacity:0.40206185;color:#000000;fill:url(#linearGradient3352);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           sodipodi:nodetypes="cccc"
+           id="path3085"
+           d="M -219.61876,-150.68038 C -219.61876,-150.68038 -219.61876,327.65041 -219.61876,327.65041 C -76.744594,328.55086 125.78146,220.48075 125.78138,88.454235 C 125.78138,-43.572302 -33.655436,-150.68036 -219.61876,-150.68038 z "
+           style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3354);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3356);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M -1559.2523,-150.68038 C -1559.2523,-150.68038 -1559.2523,327.65041 -1559.2523,327.65041 C -1702.1265,328.55086 -1904.6525,220.48075 -1904.6525,88.454235 C -1904.6525,-43.572302 -1745.2157,-150.68036 -1559.2523,-150.68038 z "
+           id="path3087"
+           sodipodi:nodetypes="cccc" />
+      </g>
+      <rect
+         y="35.5"
+         x="4.5"
+         height="6.0000005"
+         width="40"
+         id="rect3127"
+         style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <rect
+         ry="0"
+         y="36.5"
+         x="5.5"
+         height="4"
+         width="38"
+         id="rect3129"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3131"
+         d="M 39.5,35.5 L 39.5,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3133"
+         d="M 34.499995,35.5 L 34.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3135"
+         d="M 29.499995,35.5 L 29.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3137"
+         d="M 24.499995,35.5 L 24.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3139"
+         d="M 19.499995,35.5 L 19.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3141"
+         d="M 14.499996,35.5 L 14.499996,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3143"
+         d="M 9.499995,35.5 L 9.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-gerber-16.png b/data/application-x-gerber-16.png
new file mode 100644
index 0000000..7313f45
Binary files /dev/null and b/data/application-x-gerber-16.png differ
diff --git a/data/application-x-gerber-16.svg b/data/application-x-gerber-16.svg
new file mode 100644
index 0000000..90490f2
--- /dev/null
+++ b/data/application-x-gerber-16.svg
@@ -0,0 +1,543 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-gerber-16.png"
+   sodipodi:docname="application-x-gerber-16.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="16"
+   width="16"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.6727452,-1.187499,1.7316091)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.6727452,-1.187499,1.7316091)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2254"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2256"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2258"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2260"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2270"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3580332,0,0,0.3814965,-0.9615384,-0.7823571)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="666"
+     inkscape:window-width="872"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="8.0395256"
+     inkscape:cx="3.1008866"
+     inkscape:zoom="24.722742"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="16px"
+     height="16px"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Gerber file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient2270);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2272);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 1.9283192,0.50572542 L 10.668425,0.50000003 C 10.668425,0.50000003 14.5,3.9970506 14.5,4.2303066 L 14.5,15.07896 C 14.5,15.312218 14.30897,15.5 14.07168,15.5 L 1.9283192,15.5 C 1.6910303,15.5 1.5,15.312218 1.5,15.07896 L 1.5,0.92676521 C 1.5,0.69350923 1.6910303,0.50572542 1.9283192,0.50572542 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g3089"
+       style="opacity:0.38461537;display:inline"
+       transform="translate(-19.541195,-20.514447)">
+      <path
+         sodipodi:nodetypes="ccc"
+         id="path3091"
+         d="M 24.041195,22.9574 L 26.027797,24.941006 L 25.987348,27.897462"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3099"
+         d="M 25.527797,27.4574 L 25.527797,28.514447"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3103"
+         d="M 29.527797,27.4574 L 29.527797,28.509514"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path4406"
+         d="M 28.045802,23.017338 L 28.027769,24.029366 L 29.027769,25.000944 L 28.987047,27.9574"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.99999952;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2267);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 2.5498879,1.5291309 L 10.284614,1.4999999 C 10.284614,1.4999999 13.499999,4.4628004 13.499999,4.4903007 L 13.470416,14.45036 C 13.470416,14.47786 13.448165,14.499999 13.420529,14.499999 L 2.5498879,14.499999 C 2.5222499,14.499999 2.4999999,14.47786 2.4999999,14.45036 L 2.4999999,1.5787708 C 2.4999999,1.5512704 2.5222499,1.5291309 2.5498879,1.5291309 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g2455"
+       transform="matrix(0.7602792,0,0,0.7602792,-9.226339,5.4615348)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path2364"
+         d="M 31.011754,-1.8223442 C 30.676762,-2.441693 28.204843,-3.5381018 26.918162,-3.9058239 C 26.996123,-3.1823862 26.861692,-1 26.861692,-1 C 27.810289,-1.6323977 30.652688,-1.8969046 31.011754,-1.8223442 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2254);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2366"
+         d="M 31.207393,-1.878537 C 31.213448,-2.5293972 28.2857,-6.5731136 26.5,-6.525521 C 26.947525,-6.4183606 27.305033,-3.6886865 26.665865,-2.3951458 C 27.930661,-2.3951458 30.764087,-2.7331589 31.207393,-1.878537 z "
+         style="color:#000000;fill:url(#radialGradient2256);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2258);stroke-width:1.31530643;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2260);stroke-width:1.3153069;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 29.385087,-3.7201259 C 29.050095,-4.3394746 29.205755,-3.8768359 28.321508,-4.4457758 C 28.316997,-3.9329987 28.412206,-4.339708 28.300666,-3.7332338 C 28.300666,-3.7332338 29.026021,-3.7946863 29.385087,-3.7201259 z "
+         id="path2368"
+         sodipodi:nodetypes="cccc" />
+    </g>
+    <rect
+       style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3127"
+       width="15"
+       height="4.0000048"
+       x="0.49999988"
+       y="9.4999952" />
+    <rect
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999976;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3129"
+       width="13.000001"
+       height="2.0000002"
+       x="1.5"
+       y="10.5"
+       ry="0" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 12.5,9.6339912 L 12.5,11.5"
+       id="path3137" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 9.5,9.5 L 9.5,11.505824"
+       id="path3139" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 6.5,9.5575468 L 6.5,11.5"
+       id="path3141" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 3.5,9.5510749 L 3.5,11.5"
+       id="path3143" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-gerber-22.png b/data/application-x-gerber-22.png
new file mode 100644
index 0000000..7f70dd1
Binary files /dev/null and b/data/application-x-gerber-22.png differ
diff --git a/data/application-x-gerber-22.svg b/data/application-x-gerber-22.svg
new file mode 100644
index 0000000..563bfba
--- /dev/null
+++ b/data/application-x-gerber-22.svg
@@ -0,0 +1,608 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-gerber-22.png"
+   sodipodi:docname="application-x-gerber-22.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="22"
+   width="22"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.5249999,0,0,0.6727441,-1.8624985,5.7316257)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.5249999,0,0,0.6727441,-1.8624985,5.7316257)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2460"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2462"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2464"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2466"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="815"
+     inkscape:window-width="876"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="11.283134"
+     inkscape:cx="8.5279741"
+     inkscape:zoom="12.361371"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="22px"
+     height="22px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Gerber file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <g
+       style="display:inline"
+       id="g2354"
+       inkscape:label="Shadow"
+       transform="matrix(0.5199887,0,0,0.2954903,-1.4208371,7.7713745)">
+      <g
+         id="g2356"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2358"
+           style="opacity:1;fill:url(#radialGradient2370);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect2360"
+           style="opacity:1;fill:url(#radialGradient2372);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect2362"
+           style="opacity:1;fill:url(#linearGradient2374);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.0601086,0.50760946 L 14.489478,0.5 C 14.489478,0.5 19.5,5.1478405 19.5,5.4578551 L 19.5,19.876525 C 19.5,20.18654 19.250191,20.436118 18.939889,20.436118 L 3.0601086,20.436118 C 2.7498079,20.436118 2.499999,20.18654 2.499999,19.876525 L 2.499999,1.0672027 C 2.499999,0.75718813 2.7498079,0.50760946 3.0601086,0.50760946 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.5682125,1.5536151 L 14.144009,1.5131665 C 14.144009,1.5131665 18.540449,5.6270281 18.540449,5.6652126 L 18.5,19.494799 C 18.5,19.532984 18.469577,19.563724 18.431788,19.563724 L 3.5682125,19.563724 C 3.5304227,19.563724 3.4999999,19.532984 3.4999999,19.494799 L 3.4999999,1.6225403 C 3.4999999,1.5843558 3.5304227,1.5536151 3.5682125,1.5536151 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g3089"
+       style="opacity:0.38461537;display:inline"
+       transform="translate(-14.543631,-17.428931)">
+      <path
+         sodipodi:nodetypes="ccc"
+         id="path3091"
+         d="M 23.027797,19.959636 L 24.043632,21.262263 L 24.043632,27.928932"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3093"
+         d="M 27.043631,27.928932 L 27.043631,20.298886 L 26.679245,19.951361"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         sodipodi:nodetypes="ccc" />
+      <path
+         id="path3095"
+         d="M 31.043631,28.101234 L 31.043631,21.917918"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         sodipodi:nodetypes="cc" />
+      <path
+         id="path3099"
+         d="M 24.501906,27.37074 L 24.501906,28.443166"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3101"
+         d="M 27.543631,27.40046 L 27.543631,28.4574"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.99999964;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3103"
+         d="M 30.543631,27.376817 L 30.543631,28.428931"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3109"
+         d="M 30.543631,34.335929 L 30.543631,35.392869"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3111"
+         d="M 27.543631,34.383937 L 27.543631,35.428931"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3113"
+         d="M 24.460185,34.322516 L 24.460185,35.412448"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path3123"
+         d="M 19.018435,20.900329 L 20.245896,20.92893 L 21.960181,22.535218 L 21.960181,30.516767 L 19.051124,30.48704 L 19.018435,20.900329 z "
+         style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.99999982;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+    </g>
+    <g
+       id="g2455"
+       transform="translate(-11.707402,7.0259378)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path2364"
+         d="M 31.011754,-1.8223442 C 30.676762,-2.441693 28.204843,-3.5381018 26.918162,-3.9058239 C 26.996123,-3.1823862 26.861692,-1 26.861692,-1 C 27.810289,-1.6323977 30.652688,-1.8969046 31.011754,-1.8223442 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2460);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2366"
+         d="M 31.207393,-1.878537 C 31.213448,-2.5293972 28.2857,-6.5731136 26.5,-6.525521 C 26.947525,-6.4183606 27.305033,-3.6886865 26.665865,-2.3951458 C 27.930661,-2.3951458 30.764087,-2.7331589 31.207393,-1.878537 z "
+         style="color:#000000;fill:url(#radialGradient2462);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2464);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2466);stroke-width:1.00000048;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 29.611197,-3.4408139 C 29.276205,-4.0601626 28.899842,-4.3423558 28.015595,-4.9112957 C 28.011084,-4.3985186 28.119593,-4.0470954 28.008053,-3.4406212 C 28.008053,-3.4406212 29.252131,-3.5153743 29.611197,-3.4408139 z "
+         id="path2368"
+         sodipodi:nodetypes="cccc" />
+    </g>
+    <rect
+       style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3127"
+       width="21"
+       height="3.9999986"
+       x="0.5"
+       y="13.500001" />
+    <rect
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999964;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3129"
+       width="19.000002"
+       height="2.0000002"
+       x="1.4999999"
+       y="14.5"
+       ry="0" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 18.5,13.5 L 18.5,15.5"
+       id="path3133" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 15.5,13.5 L 15.5,15.5"
+       id="path3135" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 12.5,13.5 L 12.5,15.5"
+       id="path3137" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 9.5,13.5 L 9.5,15.5"
+       id="path3139" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 6.5,13.5 L 6.5,15.5"
+       id="path3141" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 3.5,13.5 L 3.5,15.5"
+       id="path3143" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-gerber-24.png b/data/application-x-gerber-24.png
new file mode 100644
index 0000000..f10460c
Binary files /dev/null and b/data/application-x-gerber-24.png differ
diff --git a/data/application-x-gerber-32.png b/data/application-x-gerber-32.png
new file mode 100644
index 0000000..93cff3d
Binary files /dev/null and b/data/application-x-gerber-32.png differ
diff --git a/data/application-x-gerber-32.svg b/data/application-x-gerber-32.svg
new file mode 100644
index 0000000..cc0631f
--- /dev/null
+++ b/data/application-x-gerber-32.svg
@@ -0,0 +1,544 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-gerber-32.png"
+   sodipodi:docname="application-x-gerber-32.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="32"
+   width="32"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.725,0,0,0.8409306,-1.762498,11.789529)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.725,0,0,0.8409306,-1.762498,11.789529)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer6"
+     inkscape:cy="16.161771"
+     inkscape:cx="22.452405"
+     inkscape:zoom="22.627418"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     gridtolerance="0.4"
+     width="32px"
+     height="32px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Gerber file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="matrix(0.722222,0,0,0.444445,-1.2474509,10.111099)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.2577956,2.5103056 L 20.72106,2.5 C 20.72106,2.5 27.5,8.7946913 27.5,9.2145522 L 27.5,28.742131 C 27.5,29.161992 27.162024,29.500002 26.742205,29.500002 L 5.2577956,29.500002 C 4.8379769,29.500002 4.5,29.161992 4.5,28.742131 L 4.5,3.2681771 C 4.5,2.8483163 4.8379769,2.5103056 5.2577956,2.5103056 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       style="display:inline;opacity:0.385"
+       id="g5326"
+       transform="translate(-8.59375,-10.483107)">
+      <g
+         id="g3535"
+         transform="translate(9.375e-2,-1.6893e-2)"
+         style="stroke:#000000;stroke-opacity:1">
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 17.862214,14.088389 L 21.088388,19.946991 L 21,24.170409"
+           id="path2828"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 23.922661,23.884311 L 23.911612,18.875864 L 21.116332,14.176777"
+           id="path2830"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 26.966854,23.97833 L 27,17.751728 L 25.000213,14.176777"
+           id="path2832"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 29.977903,23.768621 L 30,15"
+           id="path2890"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20,32 L 20,36 L 17.125,38.8125"
+           id="path2910"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 23,32 L 23,38.875"
+           id="path2912"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 26,32 L 26,36 L 28.823223,38.867417"
+           id="path2914"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 29,32 L 29.015625,34.953125 L 32.414214,38.458408 L 34.916973,38.458408"
+           id="path2916"
+           sodipodi:nodetypes="cccc" />
+        <path
+           style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 14.452123,14.56451 L 17.044194,19.266016 L 17.003906,34.988281 L 14.960937,37 L 14.428686,36.97076 L 14.452123,14.56451 z "
+           id="path2918"
+           sodipodi:nodetypes="cccccc" />
+        <path
+           sodipodi:nodetypes="cccccccc"
+           id="path3242"
+           d="M 34,33.5 L 33,33.5 L 33,30.5 L 33,21.5 L 32,20.5 L 32.007812,18.164062 L 34.007812,18.164062 L 34,33.5 z "
+           style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      </g>
+      <g
+         id="g5316"
+         transform="translate(9.3749881e-2,5.7522119e-2)">
+        <path
+           id="path2894"
+           d="M 20.5,23.446973 L 20.5,25.415546"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3217"
+           d="M 23.5,23.454221 L 23.5,25.380507"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3219"
+           d="M 26.5,23.44207 L 26.5,25.392078"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3221"
+           d="M 29.5,23.449979 L 29.5,25.415975"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3223"
+           d="M 20.5,30.508643 L 20.5,32.358325"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3225"
+           d="M 23.5,30.463383 L 23.5,32.410451"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3227"
+           d="M 26.5,30.408095 L 26.5,32.376612"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3229"
+           d="M 29.5,30.44328 L 29.5,32.369651"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.5954975,3.5 L 21.534173,3.5 C 21.534173,3.5 26.500001,8.6364009 26.500001,8.6892753 L 26.500001,28.343295 C 26.500001,28.396168 26.457409,28.438736 26.404502,28.438736 L 5.5954975,28.438736 C 5.5425918,28.438736 5.5000003,28.396168 5.5000003,28.343295 L 5.5000003,3.5954413 C 5.5000003,3.5425669 5.5425918,3.5 5.5954975,3.5 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <rect
+       style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3127"
+       width="29"
+       height="5"
+       x="1.5"
+       y="21.5" />
+    <rect
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3129"
+       width="27"
+       height="3"
+       x="2.5"
+       y="22.5"
+       ry="0" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 28.5,21.5 L 28.5,24.5"
+       id="path3131" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 24.5,21.5 L 24.5,24.5"
+       id="path3133" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 20.5,21.5 L 20.5,24.5"
+       id="path3135" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 16.5,21.5 L 16.5,24.5"
+       id="path3137" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 12.5,21.5 L 12.5,24.5"
+       id="path3139" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 8.499996,21.5 L 8.499996,24.5"
+       id="path3141" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 4.5,21.5 L 4.5,24.5"
+       id="path3143" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2232"
+       transform="matrix(0.6665574,0,0,0.6665574,0.5640292,0.7438964)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path5348"
+         d="M 39.985189,12.861445 C 39.256827,11.514817 33.882221,9.130934 31.084635,8.3314083 C 31.254143,9.904354 30.961856,14.649439 30.961856,14.649439 C 33.024356,13.274439 39.204485,12.699331 39.985189,12.861445 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2237);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2210"
+         d="M 40.410559,12.739267 C 40.423724,11.324125 34.058025,2.5320142 30.175441,2.6354934 C 31.148479,2.8684884 31.925796,8.803523 30.536076,11.616023 C 33.286076,11.616023 39.446694,10.881093 40.410559,12.739267 z "
+         style="opacity:1;color:#000000;fill:url(#radialGradient2239);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2241);stroke-width:1.50024605;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2243);stroke-width:1.50024641;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 37.558971,9.9767321 C 36.830609,8.6301041 34.525619,5.9151604 32.603033,4.6781344 C 32.654442,6.2680727 33.116647,7.3994674 32.54789,10.055612 C 32.54789,10.055612 36.731384,9.8849421 37.558971,9.9767321 z "
+         id="path2247"
+         sodipodi:nodetypes="cccc" />
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-gerber-48.png b/data/application-x-gerber-48.png
new file mode 100644
index 0000000..d965a71
Binary files /dev/null and b/data/application-x-gerber-48.png differ
diff --git a/data/application-x-gerber-48.svg b/data/application-x-gerber-48.svg
new file mode 100644
index 0000000..0f5f701
--- /dev/null
+++ b/data/application-x-gerber-48.svg
@@ -0,0 +1,707 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-gerber-48.png"
+   sodipodi:docname="application-x-gerber.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="48.000000px"
+   width="48.000000px"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3153"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3151"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3149"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5060">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop5062" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5064" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3147"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5050" />
+      <stop
+         id="stop5056"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5052" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3145"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3245"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3354"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3356"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="3"
+     inkscape:window-height="967"
+     inkscape:window-width="993"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer5"
+     inkscape:cy="27.526762"
+     inkscape:cx="30.878981"
+     inkscape:zoom="10.179605"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Gerber file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="translate(-2e-6,2.838692e-5)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.7526014,3.6464462 L 31.199616,3.6308212 C 31.199616,3.6308212 41.478553,13.174533 41.478553,13.811106 L 41.478553,43.417892 C 41.478553,44.054465 40.966077,44.56694 40.329504,44.56694 L 7.7526014,44.56694 C 7.1160285,44.56694 6.6035528,44.054465 6.6035528,43.417892 L 6.6035528,4.7954948 C 6.6035528,4.1589219 7.1160285,3.6464462 7.7526014,3.6464462 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.8151023,4.5839462 L 32.691494,4.5839462 C 32.691494,4.5839462 40.44194,12.605373 40.44194,12.687946 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g3089"
+       style="opacity:0.38461537;display:inline"
+       transform="translate(0.2499915,0)">
+      <path
+         sodipodi:nodetypes="ccc"
+         id="path3091"
+         d="M 10.5,7.5 L 15.5,7.5 L 20.5,12.5 L 20.5,22.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3093"
+         d="M 24.5,22.5 L 24.5,10.5 L 21.5,7.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3095"
+         d="M 28.5,22.5 L 28.5,8.5 L 27.5,7.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3097"
+         d="M 32.5,22.5 L 32.5,7.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3099"
+         d="M 20.5,22.5 L 20.5,24.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3101"
+         d="M 24.5,22.5 L 24.5,24.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3103"
+         d="M 28.5,22.5 L 28.5,24.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3105"
+         d="M 32.5,22.5 L 32.5,24.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3107"
+         d="M 32.5,31.5 L 32.5,33.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3109"
+         d="M 28.5,31.5 L 28.5,33.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3111"
+         d="M 24.5,31.5 L 24.5,33.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3113"
+         d="M 20.5,31.5 L 20.5,33.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3115"
+         d="M 20.5,33.5 L 20.5,37.5 L 17.5,40.5 L 10.5,40.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3117"
+         d="M 24.5,33.5 L 24.5,40.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3119"
+         d="M 28.5,33.5 L 28.5,37.5 L 31.5,40.5 L 37.5,40.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3121"
+         d="M 32.5,33.5 L 34.5,35.5 L 37.5,35.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="ccccccc"
+         id="path3123"
+         d="M 10.5,10.5 L 14.5,10.5 L 17.5,13.5 L 17.5,35.5 L 15.5,37.5 L 10.5,37.5 L 10.5,10.5 z "
+         style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cccccccc"
+         id="path3125"
+         d="M 37.5,32.5 L 36.5,32.5 L 36.5,29.5 L 36.5,20.5 L 35.5,19.5 L 35.5,9.5 L 37.5,9.5 L 37.5,32.5 z "
+         style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 40.985189,13.861445 C 40.256827,12.514817 34.882221,10.130934 32.084635,9.3314083 C 32.254143,10.904354 31.961856,15.649439 31.961856,15.649439 C 34.024356,14.274439 40.204485,13.699331 40.985189,13.861445 z "
+       id="path5348"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="color:#000000;fill:url(#radialGradient5352);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5350);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible;opacity:1"
+       d="M 41.410559,13.739267 C 41.423724,12.324125 35.058025,3.5320142 31.175441,3.6354934 C 32.148479,3.8684884 32.925796,9.803523 31.536076,12.616023 C 34.286076,12.616023 40.446694,11.881093 41.410559,13.739267 z "
+       id="path2210"
+       sodipodi:nodetypes="cccc"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="path2247"
+       d="M 39.121563,11.586207 C 38.393201,10.239579 34.963027,6.5166576 33.040441,5.2796316 C 33.279381,6.7054805 33.577496,8.9620596 32.961856,11.524439 C 32.961856,11.524439 38.340859,11.424093 39.121563,11.586207 z "
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+    <g
+       style="display:inline"
+       transform="matrix(2.105461e-2,0,0,2.086758e-2,43.101716,38.66252)"
+       id="g3081">
+      <rect
+         style="opacity:0.40206185;color:#000000;fill:url(#linearGradient3352);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         id="rect3083"
+         width="1339.6335"
+         height="478.35718"
+         x="-1559.2523"
+         y="-150.69685" />
+      <path
+         style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3354);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M -219.61876,-150.68038 C -219.61876,-150.68038 -219.61876,327.65041 -219.61876,327.65041 C -76.744594,328.55086 125.78146,220.48075 125.78138,88.454235 C 125.78138,-43.572302 -33.655436,-150.68036 -219.61876,-150.68038 z "
+         id="path3085"
+         sodipodi:nodetypes="cccc" />
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path3087"
+         d="M -1559.2523,-150.68038 C -1559.2523,-150.68038 -1559.2523,327.65041 -1559.2523,327.65041 C -1702.1265,328.55086 -1904.6525,220.48075 -1904.6525,88.454235 C -1904.6525,-43.572302 -1745.2157,-150.68036 -1559.2523,-150.68038 z "
+         style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3356);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    </g>
+    <rect
+       style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3127"
+       width="40"
+       height="6.0000005"
+       x="4.5"
+       y="35.5" />
+    <rect
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       id="rect3129"
+       width="38"
+       height="4"
+       x="5.5"
+       y="36.5"
+       ry="0" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 39.5,35.5 L 39.5,38.5"
+       id="path3131" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 34.499995,35.5 L 34.499995,38.5"
+       id="path3133" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 29.499995,35.5 L 29.499995,38.5"
+       id="path3135" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 24.499995,35.5 L 24.499995,38.5"
+       id="path3137" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 19.499995,35.5 L 19.499995,38.5"
+       id="path3139" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 14.499996,35.5 L 14.499996,38.5"
+       id="path3141" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 9.499995,35.5 L 9.499995,38.5"
+       id="path3143" />
+  </g>
+</svg>
diff --git a/data/application-x-gerber.svg b/data/application-x-gerber.svg
new file mode 100644
index 0000000..cacc586
--- /dev/null
+++ b/data/application-x-gerber.svg
@@ -0,0 +1,712 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-gerber-48.png"
+   sodipodi:docname="application-x-gerber.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="128"
+   width="128"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient10440">
+      <stop
+         style="stop-color:#605400;stop-opacity:1;"
+         offset="0"
+         id="stop10442" />
+      <stop
+         style="stop-color:#887800;stop-opacity:1;"
+         offset="1"
+         id="stop10444" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3153"
+       gradientUnits="userSpaceOnUse"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <linearGradient
+       id="linearGradient10424">
+      <stop
+         style="stop-color:#d8c100;stop-opacity:1;"
+         offset="0"
+         id="stop10426" />
+      <stop
+         style="stop-color:#ffe501;stop-opacity:1;"
+         offset="1"
+         id="stop10428" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3151"
+       gradientUnits="userSpaceOnUse"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756"
+       gradientTransform="translate(44.874995,-2.75)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3149"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5060">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop5062" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5064" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3147"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5050" />
+      <stop
+         id="stop5056"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5052" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3145"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3245"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.2499915,0)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10424"
+       id="linearGradient3348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="43.421204"
+       y1="14.544756"
+       x2="6.256557"
+       y2="14.544756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10440"
+       id="linearGradient3350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0091168,2.5347553e-6,23.847433)"
+       x1="44.625"
+       y1="17.482256"
+       x2="4.6249981"
+       y2="11.919756" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient3352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3354"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient3356"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="3"
+     inkscape:window-height="967"
+     inkscape:window-width="993"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer5"
+     inkscape:cy="-17.698378"
+     inkscape:cx="-8.6571082"
+     inkscape:zoom="2"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     width="128px"
+     height="128px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Gerber file</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2548"
+       transform="scale(2.6667,2.66667)">
+      <g
+         transform="translate(-2e-6,2.838692e-5)"
+         inkscape:label="Shadow"
+         id="g2033"
+         style="display:inline">
+        <g
+           transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)"
+           style="opacity:0.4"
+           id="g3712">
+          <rect
+             style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect2801"
+             width="5"
+             height="7"
+             x="38"
+             y="40" />
+          <rect
+             style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3696"
+             width="5"
+             height="7"
+             x="-10"
+             y="-47"
+             transform="scale(-1,-1)" />
+          <rect
+             style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3700"
+             width="28"
+             height="7.0000005"
+             x="10"
+             y="40" />
+        </g>
+      </g>
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15391"
+         d="M 7.7526014,3.6464462 L 31.199616,3.6308212 C 31.199616,3.6308212 41.478553,13.174533 41.478553,13.811106 L 41.478553,43.417892 C 41.478553,44.054465 40.966077,44.56694 40.329504,44.56694 L 7.7526014,44.56694 C 7.1160285,44.56694 6.6035528,44.054465 6.6035528,43.417892 L 6.6035528,4.7954948 C 6.6035528,4.1589219 7.1160285,3.6464462 7.7526014,3.6464462 z "
+         style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15660"
+         d="M 7.8151023,4.5839462 L 32.691494,4.5839462 C 32.691494,4.5839462 40.44194,12.605373 40.44194,12.687946 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <g
+         transform="translate(0.2499915,0)"
+         style="opacity:0.38461537;display:inline"
+         id="g3089">
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 10.5,7.5 L 15.5,7.5 L 20.5,12.5 L 20.5,22.5"
+           id="path3091"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 24.5,22.5 L 24.5,10.5 L 21.5,7.5"
+           id="path3093" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 28.5,22.5 L 28.5,8.5 L 27.5,7.5"
+           id="path3095" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 32.5,22.5 L 32.5,7.5"
+           id="path3097" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20.5,22.5 L 20.5,24.5"
+           id="path3099" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 24.5,22.5 L 24.5,24.5"
+           id="path3101" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 28.5,22.5 L 28.5,24.5"
+           id="path3103" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 32.5,22.5 L 32.5,24.5"
+           id="path3105" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 32.5,31.5 L 32.5,33.5"
+           id="path3107" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 28.5,31.5 L 28.5,33.5"
+           id="path3109" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 24.5,31.5 L 24.5,33.5"
+           id="path3111" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20.5,31.5 L 20.5,33.5"
+           id="path3113" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20.5,33.5 L 20.5,37.5 L 17.5,40.5 L 10.5,40.5"
+           id="path3115" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 24.5,33.5 L 24.5,40.5"
+           id="path3117" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 28.5,33.5 L 28.5,37.5 L 31.5,40.5 L 37.5,40.5"
+           id="path3119" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 32.5,33.5 L 34.5,35.5 L 37.5,35.5"
+           id="path3121" />
+        <path
+           style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 10.5,10.5 L 14.5,10.5 L 17.5,13.5 L 17.5,35.5 L 15.5,37.5 L 10.5,37.5 L 10.5,10.5 z "
+           id="path3123"
+           sodipodi:nodetypes="ccccccc" />
+        <path
+           style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 37.5,32.5 L 36.5,32.5 L 36.5,29.5 L 36.5,20.5 L 35.5,19.5 L 35.5,9.5 L 37.5,9.5 L 37.5,32.5 z "
+           id="path3125"
+           sodipodi:nodetypes="cccccccc" />
+      </g>
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path5348"
+         d="M 40.985189,13.861445 C 40.256827,12.514817 34.882221,10.130934 32.084635,9.3314083 C 32.254143,10.904354 31.961856,15.649439 31.961856,15.649439 C 34.024356,14.274439 40.204485,13.699331 40.985189,13.861445 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2210"
+         d="M 41.410559,13.739267 C 41.423724,12.324125 35.058025,3.5320142 31.175441,3.6354934 C 32.148479,3.8684884 32.925796,9.803523 31.536076,12.616023 C 34.286076,12.616023 40.446694,11.881093 41.410559,13.739267 z "
+         style="opacity:1;color:#000000;fill:url(#radialGradient5352);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5350);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 39.121563,11.586207 C 38.393201,10.239579 34.963027,6.5166576 33.040441,5.2796316 C 33.279381,6.7054805 33.577496,8.9620596 32.961856,11.524439 C 32.961856,11.524439 38.340859,11.424093 39.121563,11.586207 z "
+         id="path2247"
+         sodipodi:nodetypes="cccc" />
+      <g
+         id="g3081"
+         transform="matrix(2.105461e-2,0,0,2.086758e-2,43.101716,38.66252)"
+         style="display:inline">
+        <rect
+           y="-150.69685"
+           x="-1559.2523"
+           height="478.35718"
+           width="1339.6335"
+           id="rect3083"
+           style="opacity:0.40206185;color:#000000;fill:url(#linearGradient3352);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           sodipodi:nodetypes="cccc"
+           id="path3085"
+           d="M -219.61876,-150.68038 C -219.61876,-150.68038 -219.61876,327.65041 -219.61876,327.65041 C -76.744594,328.55086 125.78146,220.48075 125.78138,88.454235 C 125.78138,-43.572302 -33.655436,-150.68036 -219.61876,-150.68038 z "
+           style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3354);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3356);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M -1559.2523,-150.68038 C -1559.2523,-150.68038 -1559.2523,327.65041 -1559.2523,327.65041 C -1702.1265,328.55086 -1904.6525,220.48075 -1904.6525,88.454235 C -1904.6525,-43.572302 -1745.2157,-150.68036 -1559.2523,-150.68038 z "
+           id="path3087"
+           sodipodi:nodetypes="cccc" />
+      </g>
+      <rect
+         y="35.5"
+         x="4.5"
+         height="6.0000005"
+         width="40"
+         id="rect3127"
+         style="opacity:1;color:#000000;fill:url(#linearGradient3348);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3350);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <rect
+         ry="0"
+         y="36.5"
+         x="5.5"
+         height="4"
+         width="38"
+         id="rect3129"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#fff171;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3131"
+         d="M 39.5,35.5 L 39.5,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3133"
+         d="M 34.499995,35.5 L 34.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3135"
+         d="M 29.499995,35.5 L 29.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3137"
+         d="M 24.499995,35.5 L 24.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3139"
+         d="M 19.499995,35.5 L 19.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3141"
+         d="M 14.499996,35.5 L 14.499996,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path3143"
+         d="M 9.499995,35.5 L 9.499995,38.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#756700;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-pcb-footprint-16.png b/data/application-x-pcb-footprint-16.png
new file mode 100644
index 0000000..5e1851f
Binary files /dev/null and b/data/application-x-pcb-footprint-16.png differ
diff --git a/data/application-x-pcb-footprint-16.svg b/data/application-x-pcb-footprint-16.svg
new file mode 100644
index 0000000..4f3b1e9
--- /dev/null
+++ b/data/application-x-pcb-footprint-16.svg
@@ -0,0 +1,554 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="16"
+   width="16"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2254"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2256"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2258"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2260"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100475,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2270"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3580332,0,0,0.3814965,-0.9615384,-0.7823571)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2607"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2605"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2603"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2595">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2597" />
+      <stop
+         id="stop2599"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2601" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2587"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2585"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2583"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2574">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2576" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2578" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2568">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2570" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2572" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2562">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2564" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2566" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2560"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="g3292"
+     inkscape:cy="9.2955506"
+     inkscape:cx="13.413815"
+     inkscape:zoom="32"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="16px"
+     height="16px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB footprint</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient2270);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2272);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 1.9283192,0.50572542 L 10.668425,0.50000003 C 10.668425,0.50000003 14.5,3.9970506 14.5,4.2303066 L 14.5,15.07896 C 14.5,15.312218 14.30897,15.5 14.07168,15.5 L 1.9283192,15.5 C 1.6910303,15.5 1.5,15.312218 1.5,15.07896 L 1.5,0.92676521 C 1.5,0.69350923 1.6910303,0.50572542 1.9283192,0.50572542 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2267);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 2.5498889,1.529131 L 10.284615,1.5 C 10.284615,1.5 13.5,4.4628005 13.5,4.4903008 L 13.470417,14.45036 C 13.470417,14.47786 13.448166,14.499999 13.42053,14.499999 L 2.5498889,14.499999 C 2.5222509,14.499999 2.5000009,14.47786 2.5000009,14.45036 L 2.5000009,1.5787709 C 2.5000009,1.5512705 2.5222509,1.529131 2.5498889,1.529131 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g2455"
+       transform="matrix(0.7602792,0,0,0.7602792,-9.226339,5.4615348)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path2364"
+         d="M 31.011754,-1.8223442 C 30.676762,-2.441693 28.204843,-3.5381018 26.918162,-3.9058239 C 26.996123,-3.1823862 26.861692,-1 26.861692,-1 C 27.810289,-1.6323977 30.652688,-1.8969046 31.011754,-1.8223442 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2254);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2366"
+         d="M 31.207393,-1.878537 C 31.213448,-2.5293972 28.2857,-6.5731136 26.5,-6.525521 C 26.947525,-6.4183606 27.305033,-3.6886865 26.665865,-2.3951458 C 27.930661,-2.3951458 30.764087,-2.7331589 31.207393,-1.878537 z "
+         style="color:#000000;fill:url(#radialGradient2256);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2258);stroke-width:1.31530643;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2260);stroke-width:1.3153069;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 29.385087,-3.7201259 C 29.050095,-4.3394746 29.205755,-3.8768359 28.321508,-4.4457758 C 28.316997,-3.9329987 28.412206,-4.339708 28.300666,-3.7332338 C 28.300666,-3.7332338 29.026021,-3.7946863 29.385087,-3.7201259 z "
+         id="path2368"
+         sodipodi:nodetypes="cccc" />
+    </g>
+    <g
+       id="g2609"
+       transform="translate(10.427253,-5.1308212)">
+      <g
+         style="display:inline"
+         id="g3292"
+         transform="matrix(0.497233,0,0,0.4736428,-14.112229,1.3428589)">
+        <path
+           id="path3280"
+           d="M 22,18.5 L 25,18.5"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.0606041px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <path
+           id="path3286"
+           d="M 22,15.5 L 25,15.5"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.0606041px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <rect
+           ry="0.73953015"
+           y="13.275748"
+           x="21.5"
+           height="8.2242517"
+           width="4"
+           id="rect3272"
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:2.06060433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3282"
+           d="M 16.9375,30.5 L 19.9375,30.5"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.0606041px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <path
+           id="path3288"
+           d="M 17.0625,33.5 L 20.0625,33.5 L 20.0625,33.5 L 20.0625,33.5"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.0606041px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <rect
+           ry="0.80928588"
+           y="27.5"
+           x="16.5"
+           height="9"
+           width="4"
+           id="rect3274"
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:2.06060433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+        <path
+           id="path3284"
+           d="M 27.0625,30.5 L 30.0625,30.5"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.0606041px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <path
+           id="path3290"
+           d="M 27,33.5 L 30,33.5"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.0606041px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <rect
+           ry="0.80928588"
+           y="27.5"
+           x="26.5"
+           height="9"
+           width="4"
+           id="rect3276"
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:2.06060433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+        <rect
+           ry="1.1612128"
+           y="21.720932"
+           x="15.442717"
+           height="6.3338876"
+           width="16.114565"
+           id="rect3270"
+           style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:2.06060386;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-footprint-22.png b/data/application-x-pcb-footprint-22.png
new file mode 100644
index 0000000..d512c00
Binary files /dev/null and b/data/application-x-pcb-footprint-22.png differ
diff --git a/data/application-x-pcb-footprint-22.svg b/data/application-x-pcb-footprint-22.svg
new file mode 100644
index 0000000..c7aaf32
--- /dev/null
+++ b/data/application-x-pcb-footprint-22.svg
@@ -0,0 +1,573 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="22"
+   width="22"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5352"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient3095"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient3093"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient3091"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient3089"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3087"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3085"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2460"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2462"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2464"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2466"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="g3292"
+     inkscape:cy="6.627056"
+     inkscape:cx="14.281477"
+     inkscape:zoom="24.722741"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="22px"
+     height="22px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB footprint</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <g
+       style="display:inline"
+       id="g2354"
+       inkscape:label="Shadow"
+       transform="matrix(0.5199887,0,0,0.2954903,-1.4208371,7.7713745)">
+      <g
+         id="g2356"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2358"
+           style="opacity:1;fill:url(#radialGradient2370);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect2360"
+           style="opacity:1;fill:url(#radialGradient2372);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect2362"
+           style="opacity:1;fill:url(#linearGradient2374);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.0601086,0.50760946 L 14.489478,0.5 C 14.489478,0.5 19.5,5.1478405 19.5,5.4578551 L 19.5,19.876525 C 19.5,20.18654 19.250191,20.436118 18.939889,20.436118 L 3.0601086,20.436118 C 2.7498079,20.436118 2.499999,20.18654 2.499999,19.876525 L 2.499999,1.0672027 C 2.499999,0.75718813 2.7498079,0.50760946 3.0601086,0.50760946 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.5682125,1.5536151 L 14.144009,1.5131665 C 14.144009,1.5131665 18.540449,5.6270281 18.540449,5.6652126 L 18.5,19.494799 C 18.5,19.532984 18.469577,19.563724 18.431788,19.563724 L 3.5682125,19.563724 C 3.5304227,19.563724 3.4999999,19.532984 3.4999999,19.494799 L 3.4999999,1.6225403 C 3.4999999,1.5843558 3.5304227,1.5536151 3.5682125,1.5536151 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g2455"
+       transform="translate(-11.707402,7.0259378)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path2364"
+         d="M 31.011754,-1.8223442 C 30.676762,-2.441693 28.204843,-3.5381018 26.918162,-3.9058239 C 26.996123,-3.1823862 26.861692,-1 26.861692,-1 C 27.810289,-1.6323977 30.652688,-1.8969046 31.011754,-1.8223442 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2460);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2366"
+         d="M 31.207393,-1.878537 C 31.213448,-2.5293972 28.2857,-6.5731136 26.5,-6.525521 C 26.947525,-6.4183606 27.305033,-3.6886865 26.665865,-2.3951458 C 27.930661,-2.3951458 30.764087,-2.7331589 31.207393,-1.878537 z "
+         style="color:#000000;fill:url(#radialGradient2462);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2464);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2466);stroke-width:1.00000048;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 29.611197,-3.4408139 C 29.276205,-4.0601626 28.899842,-4.3423558 28.015595,-4.9112957 C 28.011084,-4.3985186 28.119593,-4.0470954 28.008053,-3.4406212 C 28.008053,-3.4406212 29.252131,-3.5153743 29.611197,-3.4408139 z "
+         id="path2368"
+         sodipodi:nodetypes="cccc" />
+    </g>
+    <g
+       style="display:inline"
+       transform="translate(11.895702,-6.1130246)"
+       id="g3195">
+      <g
+         style="display:inline"
+         id="g3292"
+         transform="matrix(0.6157597,0,0,0.6253183,-15.33186,1.3753342)">
+        <path
+           id="path3280"
+           d="M 21.008454,19.570339 L 25.36758,19.570339"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1.61154997px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <rect
+           ry="0.89251548"
+           y="13.173595"
+           x="21.008453"
+           height="9.9255896"
+           width="4.8720284"
+           id="rect3272"
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.61155021;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3282"
+           d="M 16.136421,30.764637 L 20.284715,30.764637"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1.61154997px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <rect
+           ry="0.86875755"
+           y="27.500002"
+           x="16.136421"
+           height="9.6613798"
+           width="4.8720307"
+           id="rect3274"
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.61155021;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+        <path
+           id="path3284"
+           d="M 26.524928,30.764637 L 29.961978,30.764637"
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1.61155009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <rect
+           ry="0.86875755"
+           y="27.500002"
+           x="25.880484"
+           height="9.6613798"
+           width="4.8720326"
+           id="rect3276"
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.61155033;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+        <rect
+           ry="1.4659203"
+           y="21.169523"
+           x="14.5"
+           height="7.9959288"
+           width="18"
+           id="rect3270"
+           style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:1.61154985;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3286"
+           d="M 21.008454,16.371968 L 25.36758,16.371968"
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1.61154997px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <path
+           id="path3288"
+           d="M 16.136421,33.963008 L 20.346981,33.963008 L 20.346981,33.963008 L 20.346981,33.963008"
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1.61154997px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        <path
+           id="path3290"
+           d="M 26.591647,33.963008 L 30.61419,33.963008"
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1.61155009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-footprint-24.png b/data/application-x-pcb-footprint-24.png
new file mode 100644
index 0000000..893f58f
Binary files /dev/null and b/data/application-x-pcb-footprint-24.png differ
diff --git a/data/application-x-pcb-footprint-32.png b/data/application-x-pcb-footprint-32.png
new file mode 100644
index 0000000..43be03f
Binary files /dev/null and b/data/application-x-pcb-footprint-32.png differ
diff --git a/data/application-x-pcb-footprint-32.svg b/data/application-x-pcb-footprint-32.svg
new file mode 100644
index 0000000..750c2e1
--- /dev/null
+++ b/data/application-x-pcb-footprint-32.svg
@@ -0,0 +1,2302 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="32"
+   width="32"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316114)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.105238)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2560"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2562">
+      <stop
+         id="stop2564"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2566"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2568">
+      <stop
+         id="stop2570"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2572"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2574">
+      <stop
+         id="stop2576"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2578"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2583"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2585"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2587"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2595">
+      <stop
+         id="stop2597"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2599" />
+      <stop
+         id="stop2601"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2603"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2605"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2607"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2272"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.3580332,0,0,0.3814965,-0.9615384,-0.7823571)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2270"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100475,18.786557)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2267"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2260"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2258"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2256"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2254"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2352"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2348"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2346"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2505">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2507" />
+      <stop
+         id="stop2509"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2511" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2493">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2495" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2497" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2487">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2489" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2491" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2481">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2483" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2485" />
+    </linearGradient>
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3021"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3019"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3017"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3009">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop3011" />
+      <stop
+         id="stop3013"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop3015" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient4796"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5352"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2998"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2996"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.8244190"
+       cy="3.7561285"
+       cx="8.8244190"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2994"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2988">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2990" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2992" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2982">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2984" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2986" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2976">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2978" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2980" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient8166"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3085"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3087"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient3089"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient3091"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient3093"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient3095"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5220"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5218"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5216"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5214"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5206">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5208" />
+      <stop
+         id="stop5210"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5212" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient5198"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5196"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5194"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5192"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5186">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5188" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop5190" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5180">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5182" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop5184" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5174">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5176" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop5178" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5172"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5158"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5156"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5154"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5152"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5150"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5148"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5146"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5144"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5142"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5140"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5138"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5263"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5265"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient5267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2696"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2694"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2692"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2690"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2688"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2686"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2684"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2682"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2674">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2676" />
+      <stop
+         id="stop2678"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2680" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2666"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2664"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2662"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2660"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2654">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2656" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2658" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2648">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2650" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2652" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2642">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2644" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2646" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2640"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2626"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2624"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2622"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2620"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2618"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2616"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2614"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2612"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2610"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2608"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2606"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4206"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4204"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4202"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4200"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4198"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4196"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4194"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4186">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop4188" />
+      <stop
+         id="stop4190"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop4192" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4178"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4176"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4174"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4168">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4170" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4172" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4162">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4164" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop4166" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4156">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4158" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4160" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4505"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4503"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4501"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4499"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4497"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4495"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4493"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4485">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop4487" />
+      <stop
+         id="stop4489"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop4491" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4477"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4475"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4473"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4467">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4469" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4471" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4461">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4463" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop4465" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4455">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4457" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4459" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4547"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4549"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient4551"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient4553"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.105238)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient4555"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316114)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient4557"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4559"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient4561"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient4563"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient4565"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2466"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2464"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2462"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2460"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2687"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2685"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2683"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2681"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2673">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2675" />
+      <stop
+         id="stop2677"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2679" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2665"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2663"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2661"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2659"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2653">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2655" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2657" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2647">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2649" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2651" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2641">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2643" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2645" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2638"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2370"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2372"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2374"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2621"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2619"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2617"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2615"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2613"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2611"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2609"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2607"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2727"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.105238)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2729"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316114)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2731"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="11.767484"
+     inkscape:cx="13.931195"
+     inkscape:zoom="45.254836"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     gridtolerance="0.4"
+     width="32px"
+     height="32px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB footprint</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="matrix(0.6999997,0,0,0.444445,-0.8000002,9.673599)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.2577956,2.5103036 L 20.72106,2.499998 C 20.72106,2.499998 27.5,8.7946893 27.5,9.2145502 L 27.5,28.742129 C 27.5,29.16199 27.162024,29.5 26.742205,29.5 L 5.2577956,29.5 C 4.8379769,29.5 4.5,29.16199 4.5,28.742129 L 4.5,3.2681751 C 4.5,2.8483143 4.8379769,2.5103036 5.2577956,2.5103036 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.5954975,3.5 L 21.534173,3.5 C 21.534173,3.5 26.500001,8.6364009 26.500001,8.6892753 L 26.500001,28.343295 C 26.500001,28.396168 26.457409,28.438736 26.404502,28.438736 L 5.5954975,28.438736 C 5.5425918,28.438736 5.5000003,28.396168 5.5000003,28.343295 L 5.5000003,3.5954413 C 5.5000003,3.5425669 5.5425918,3.5 5.5954975,3.5 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g3195"
+       transform="matrix(0,0.8213957,-0.8278753,0,29.448067,21.75745)"
+       style="display:inline">
+      <g
+         transform="matrix(0.8191474,0,0,0.8681563,-20.713146,-5.0724305)"
+         id="g2718"
+         style="display:inline">
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1.43800819;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 22.357111,18.292677 L 25.701123,18.292677"
+           id="path2720" />
+        <rect
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.43800819;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect2722"
+           width="4.4586825"
+           height="8.7727346"
+           x="21.931139"
+           y="12.727267"
+           ry="0.78885001" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1.43800819;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 16.674769,30.814849 L 19.674769,30.814849"
+           id="path2724" />
+        <rect
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.43800819;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="rect2726"
+           width="4.513773"
+           height="9"
+           x="15.931138"
+           y="27.380259"
+           ry="0.80928588" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1.43800819;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 27.095341,30.814849 L 30.095341,30.814849"
+           id="path2728" />
+        <rect
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.43800819;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="rect2730"
+           width="4.4586835"
+           height="8.8802586"
+           x="26.389828"
+           y="27.5"
+           ry="0.79851866" />
+        <rect
+           style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:1.43800819;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect2732"
+           width="19.326784"
+           height="9.7394676"
+           x="14.499999"
+           y="19.684029"
+           ry="1.7855691" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1.43800819;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 22.422793,15.509972 L 25.766805,15.509972"
+           id="path2734" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1.43800819;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 16.62746,33.597554 L 20.228288,33.597554 L 20.228288,33.597554 L 20.228288,33.597554"
+           id="path2736" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1.43800819;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 27.042953,33.597553 L 30.217723,33.597553"
+           id="path2738" />
+      </g>
+    </g>
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 7.5489685,6.5000002 L 23.5,6.5000002"
+       id="path4573" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 7.5493115,8.5000002 L 23.328059,8.5000002"
+       id="path4575" />
+    <path
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.0000006;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 7.3756535,10.5 L 15.496391,10.5"
+       id="path4577"
+       sodipodi:nodetypes="cc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2232"
+       transform="matrix(0.6665574,0,0,0.6665574,0.5640292,0.7438964)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path5348"
+         d="M 39.985189,12.861445 C 39.256827,11.514817 33.882221,9.130934 31.084635,8.3314083 C 31.254143,9.904354 30.961856,14.649439 30.961856,14.649439 C 33.024356,13.274439 39.204485,12.699331 39.985189,12.861445 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2237);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2210"
+         d="M 40.410559,12.739267 C 40.423724,11.324125 34.058025,2.5320142 30.175441,2.6354934 C 31.148479,2.8684884 31.925796,8.803523 30.536076,11.616023 C 33.286076,11.616023 39.446694,10.881093 40.410559,12.739267 z "
+         style="opacity:1;color:#000000;fill:url(#radialGradient2239);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2241);stroke-width:1.50024605;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2243);stroke-width:1.50024641;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 37.558971,9.9767321 C 36.830609,8.6301041 34.525619,5.9151604 32.603033,4.6781344 C 32.654442,6.2680727 33.116647,7.3994674 32.54789,10.055612 C 32.54789,10.055612 36.731384,9.8849421 37.558971,9.9767321 z "
+         id="path2247"
+         sodipodi:nodetypes="cccc" />
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-pcb-footprint-48.png b/data/application-x-pcb-footprint-48.png
new file mode 100644
index 0000000..a13a6e8
Binary files /dev/null and b/data/application-x-pcb-footprint-48.png differ
diff --git a/data/application-x-pcb-footprint-48.svg b/data/application-x-pcb-footprint-48.svg
new file mode 100644
index 0000000..a021db7
--- /dev/null
+++ b/data/application-x-pcb-footprint-48.svg
@@ -0,0 +1,674 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="48.000000px"
+   width="48.000000px"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2775"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2773"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2771"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2763">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2765" />
+      <stop
+         id="stop2767"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2769" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2755"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2753"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2751"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2749"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2747"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.8244190"
+       cy="3.7561285"
+       cx="8.8244190"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient15656"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2740">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2742" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2744" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2734">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2736" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2738" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2728">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2730" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2732" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2726"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2818"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2822"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2824"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2826"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2828"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="11.11853"
+     inkscape:cx="29.101167"
+     inkscape:zoom="5.6568543"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB footprint</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="translate(-2e-6,2.838692e-5)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.7526014,3.6464462 L 31.199616,3.6308212 C 31.199616,3.6308212 41.478553,13.174533 41.478553,13.811106 L 41.478553,43.417892 C 41.478553,44.054465 40.966077,44.56694 40.329504,44.56694 L 7.7526014,44.56694 C 7.1160285,44.56694 6.6035528,44.054465 6.6035528,43.417892 L 6.6035528,4.7954948 C 6.6035528,4.1589219 7.1160285,3.6464462 7.7526014,3.6464462 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.8151023,4.5839462 L 32.691494,4.5839462 C 32.691494,4.5839462 40.44194,12.605373 40.44194,12.687946 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g3292"
+       transform="matrix(0,1,-1,0,48,7)">
+      <rect
+         ry="0.80928588"
+         y="12.5"
+         x="21.5"
+         height="9"
+         width="4"
+         id="rect3272"
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      <rect
+         ry="0.80928588"
+         y="27.5"
+         x="16.5"
+         height="9"
+         width="4"
+         id="rect3274"
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+      <rect
+         ry="0.80928588"
+         y="27.5"
+         x="26.5"
+         height="9"
+         width="4"
+         id="rect3276"
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline" />
+      <rect
+         ry="1.4552083"
+         y="20.5625"
+         x="14.5"
+         height="7.9374995"
+         width="18"
+         id="rect3270"
+         style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path3280"
+         d="M 22,18.5 L 25,18.5"
+         style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3282"
+         d="M 16.9375,30.5 L 19.9375,30.5"
+         style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3284"
+         d="M 27.0625,30.5 L 30.0625,30.5"
+         style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3286"
+         d="M 22,15.5 L 25,15.5"
+         style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3288"
+         d="M 17.0625,33.5 L 20.0625,33.5 L 20.0625,33.5 L 20.0625,33.5"
+         style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3290"
+         d="M 27,33.5 L 30,33.5"
+         style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+    </g>
+    <g
+       transform="translate(-3.0000002,2.3841858e-7)"
+       id="g2836"
+       style="stroke:#888a85;display:inline">
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 15.5,9.5 L 36.5,9.5"
+         id="path2855" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 15.5,11.5 L 36.5,11.5"
+         id="path2857" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 15.5,13.5 L 36.5,13.5"
+         id="path2859" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 15.5,15.5 L 27.5,15.5"
+         id="path2861"
+         sodipodi:nodetypes="cc" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 40.985189,13.861445 C 40.256827,12.514817 34.882221,10.130934 32.084635,9.3314083 C 32.254143,10.904354 31.961856,15.649439 31.961856,15.649439 C 34.024356,14.274439 40.204485,13.699331 40.985189,13.861445 z "
+       id="path5348"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="color:#000000;fill:url(#radialGradient5352);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5350);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible;opacity:1"
+       d="M 41.410559,13.739267 C 41.423724,12.324125 35.058025,3.5320142 31.175441,3.6354934 C 32.148479,3.8684884 32.925796,9.803523 31.536076,12.616023 C 34.286076,12.616023 40.446694,11.881093 41.410559,13.739267 z "
+       id="path2210"
+       sodipodi:nodetypes="cccc"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="path2247"
+       d="M 39.121563,11.586207 C 38.393201,10.239579 34.963027,6.5166576 33.040441,5.2796316 C 33.279381,6.7054805 33.577496,8.9620596 32.961856,11.524439 C 32.961856,11.524439 38.340859,11.424093 39.121563,11.586207 z "
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+  </g>
+</svg>
diff --git a/data/application-x-pcb-footprint.svg b/data/application-x-pcb-footprint.svg
new file mode 100644
index 0000000..74f7742
--- /dev/null
+++ b/data/application-x-pcb-footprint.svg
@@ -0,0 +1,680 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="128"
+   width="128"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2775"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2773"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2771"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2763">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2765" />
+      <stop
+         id="stop2767"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2769" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2755"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2753"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2751"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2749"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2747"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient15656"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2740">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2742" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2744" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2734">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2736" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2738" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2728">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2730" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2732" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2726"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2818"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2822"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2824"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2826"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2828"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer5"
+     inkscape:cy="80.451323"
+     inkscape:cx="37.062201"
+     inkscape:zoom="2.8284272"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="128px"
+     height="128px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB footprint</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2271"
+       transform="scale(2.6667,2.6667)">
+      <g
+         transform="translate(-2e-6,2.838692e-5)"
+         inkscape:label="Shadow"
+         id="g2033"
+         style="display:inline">
+        <g
+           transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)"
+           style="opacity:0.4"
+           id="g3712">
+          <rect
+             style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect2801"
+             width="5"
+             height="7"
+             x="38"
+             y="40" />
+          <rect
+             style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3696"
+             width="5"
+             height="7"
+             x="-10"
+             y="-47"
+             transform="scale(-1,-1)" />
+          <rect
+             style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3700"
+             width="28"
+             height="7.0000005"
+             x="10"
+             y="40" />
+        </g>
+      </g>
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15391"
+         d="M 7.7526014,3.6464462 L 31.199616,3.6308212 C 31.199616,3.6308212 41.478553,13.174533 41.478553,13.811106 L 41.478553,43.417892 C 41.478553,44.054465 40.966077,44.56694 40.329504,44.56694 L 7.7526014,44.56694 C 7.1160285,44.56694 6.6035528,44.054465 6.6035528,43.417892 L 6.6035528,4.7954948 C 6.6035528,4.1589219 7.1160285,3.6464462 7.7526014,3.6464462 z "
+         style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15660"
+         d="M 7.8151023,4.5839462 L 32.691494,4.5839462 C 32.691494,4.5839462 40.44194,12.605373 40.44194,12.687946 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <g
+         transform="matrix(0,1,-1,0,48,7)"
+         id="g3292"
+         style="display:inline">
+        <rect
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect3272"
+           width="4"
+           height="9"
+           x="21.5"
+           y="12.5"
+           ry="0.80928588" />
+        <rect
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="rect3274"
+           width="4"
+           height="9"
+           x="16.5"
+           y="27.5"
+           ry="0.80928588" />
+        <rect
+           style="opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+           id="rect3276"
+           width="4"
+           height="9"
+           x="26.5"
+           y="27.5"
+           ry="0.80928588" />
+        <rect
+           style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect3270"
+           width="18"
+           height="7.9374995"
+           x="14.5"
+           y="20.5625"
+           ry="1.4552083" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 22,18.5 L 25,18.5"
+           id="path3280" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 16.9375,30.5 L 19.9375,30.5"
+           id="path3282" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 27.0625,30.5 L 30.0625,30.5"
+           id="path3284" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 22,15.5 L 25,15.5"
+           id="path3286" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 17.0625,33.5 L 20.0625,33.5 L 20.0625,33.5 L 20.0625,33.5"
+           id="path3288" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 27,33.5 L 30,33.5"
+           id="path3290" />
+      </g>
+      <g
+         style="stroke:#888a85;display:inline"
+         id="g2836"
+         transform="translate(-3.0000002,2.3841858e-7)">
+        <path
+           id="path2855"
+           d="M 15.5,9.5 L 36.5,9.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2857"
+           d="M 15.5,11.5 L 36.5,11.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2859"
+           d="M 15.5,13.5 L 36.5,13.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           sodipodi:nodetypes="cc"
+           id="path2861"
+           d="M 15.5,15.5 L 27.5,15.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      </g>
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path5348"
+         d="M 40.985189,13.861445 C 40.256827,12.514817 34.882221,10.130934 32.084635,9.3314083 C 32.254143,10.904354 31.961856,15.649439 31.961856,15.649439 C 34.024356,14.274439 40.204485,13.699331 40.985189,13.861445 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2210"
+         d="M 41.410559,13.739267 C 41.423724,12.324125 35.058025,3.5320142 31.175441,3.6354934 C 32.148479,3.8684884 32.925796,9.803523 31.536076,12.616023 C 34.286076,12.616023 40.446694,11.881093 41.410559,13.739267 z "
+         style="opacity:1;color:#000000;fill:url(#radialGradient5352);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5350);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 39.121563,11.586207 C 38.393201,10.239579 34.963027,6.5166576 33.040441,5.2796316 C 33.279381,6.7054805 33.577496,8.9620596 32.961856,11.524439 C 32.961856,11.524439 38.340859,11.424093 39.121563,11.586207 z "
+         id="path2247"
+         sodipodi:nodetypes="cccc" />
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-pcb-layout-16.png b/data/application-x-pcb-layout-16.png
new file mode 100644
index 0000000..1cf2c2e
Binary files /dev/null and b/data/application-x-pcb-layout-16.png differ
diff --git a/data/application-x-pcb-layout-16.svg b/data/application-x-pcb-layout-16.svg
new file mode 100644
index 0000000..6cdf4f2
--- /dev/null
+++ b/data/application-x-pcb-layout-16.svg
@@ -0,0 +1,1333 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-pcb-16.png"
+   sodipodi:docname="application-x-pcb-16.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="16"
+   width="16"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.1303227,52.687269)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2871"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2869"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2762"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2764"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2766"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2780"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.1303227,52.687269)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2782">
+      <stop
+         id="stop2784"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2786"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2788">
+      <stop
+         id="stop2790"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2792"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2794">
+      <stop
+         id="stop2796"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2798"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2800"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2802"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2804"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2812">
+      <stop
+         id="stop2814"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2816" />
+      <stop
+         id="stop2818"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2822"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2824"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2826"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2828"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5143"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.1296478e-2,-0.2660144,0.3444364,-8.7244696e-3,27.683344,23.53042)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3070"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2494981,0,0,0.2714449,28.137815,14.512333)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3068"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1115779,7.7416911e-3,-8.3064858e-3,0.1245599,31.864361,16.99391)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3066"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9755905,0,0,0.6187301,4.6058755,12.758469)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3064"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0,-1.5160988,-1.1387138,0,65.407506,72.437718)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3062"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0,-1.503917,-1.1479369,0,66.120269,77.688631)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3060"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient5135"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient5133"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient5131"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient5129"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2655">
+      <stop
+         id="stop2657"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2659"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2661">
+      <stop
+         id="stop2663"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2665"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2667">
+      <stop
+         id="stop2669"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2671"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5118"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5116"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient5114"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2682">
+      <stop
+         id="stop2684"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2686" />
+      <stop
+         id="stop2688"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient5108"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient5106"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient5104"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient5102"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5100"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5098"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient5096"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5001"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4999"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4997"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4995"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4993"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4991"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4989"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4987"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4985"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4983"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2429"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2427"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2425"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2423"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2421"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2419"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2417"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2415"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2413"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2411"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0,0.6202002,-0.6629454,0,-1.222397,-12.387324)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4231"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4228"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4226"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4169"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4167"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2269"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5068"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2265"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2263"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2261"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2259"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2257"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5055">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5057" />
+      <stop
+         id="stop5059"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5061" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5043">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5045" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop5047" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5037">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5039" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop5041" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5031">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5033" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop5035" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient15668"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient15656"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="8.3777682"
+     inkscape:cx="9.2042411"
+     inkscape:zoom="32"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="16px"
+     height="16px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB layout</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2272);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 1.9283192,0.50572542 C 1.9283192,0.50572542 13.767842,0.49693148 14.071376,0.50000003 C 14.374821,0.50306768 14.511049,0.64934194 14.511049,0.88259794 L 14.5,15.07896 C 14.5,15.312218 14.30897,15.5 14.07168,15.5 L 1.9283192,15.5 C 1.6910303,15.5 1.5,15.312218 1.5,15.07896 L 1.5,0.92676521 C 1.5,0.69350923 1.6910303,0.50572542 1.9283192,0.50572542 z "
+       id="rect15391"
+       sodipodi:nodetypes="czccccccc" />
+    <g
+       id="g3089"
+       style="opacity:1;stroke:#5fc500;stroke-opacity:1;display:inline"
+       transform="translate(-20.478695,-17.514447)">
+      <path
+         sodipodi:nodetypes="ccc"
+         id="path3091"
+         d="M 23.394018,22.310223 L 26.027797,24.941006 L 25.987348,27.897462"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path4406"
+         d="M 25.948019,18.987096 L 26.023736,21.082163 L 29.027769,23.989729 L 28.987047,27.9574"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:0.99999952;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3099"
+         d="M 25.527797,27.4574 L 25.527797,28.514447"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3103"
+         d="M 28.527797,27.4574 L 28.527797,28.509514"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path3471"
+         d="M 29.059228,19.169143 L 29.041195,20.29546 L 32.045228,22.968651 L 32.028196,28.000838"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:0.99999952;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3467"
+         d="M 31.527797,27.405286 L 31.527797,28.4574"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2267);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible;opacity:1"
+       d="M 2.5498879,1.5291309 L 13.49974,1.4999999 C 13.49974,1.4999999 13.511048,1.5459849 13.511048,1.5734852 L 13.470416,14.45036 C 13.470416,14.47786 13.448165,14.499999 13.420529,14.499999 L 2.5498879,14.499999 C 2.5222499,14.499999 2.4999999,14.47786 2.4999999,14.45036 L 2.4999999,1.5787708 C 2.4999999,1.5512704 2.5222499,1.5291309 2.5498879,1.5291309 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="path2247"
+       d="M 56.700902,-5.6584474 C 55.97254,-7.0050754 52.542366,-10.727997 50.61978,-11.965023 C 50.85872,-10.539174 51.156835,-8.2825948 50.541195,-5.7202154 C 50.541195,-5.7202154 55.920198,-5.8205614 56.700902,-5.6584474 z "
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-layout-22.png b/data/application-x-pcb-layout-22.png
new file mode 100644
index 0000000..9c83f9f
Binary files /dev/null and b/data/application-x-pcb-layout-22.png differ
diff --git a/data/application-x-pcb-layout-22.svg b/data/application-x-pcb-layout-22.svg
new file mode 100644
index 0000000..f85aa60
--- /dev/null
+++ b/data/application-x-pcb-layout-22.svg
@@ -0,0 +1,1423 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-pcb-22.png"
+   sodipodi:docname="application-x-pcb-22.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="22"
+   width="22"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-2.3312251,55.152293)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2871"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2869"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2762"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2764"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2766"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2780"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.1303227,52.687269)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2782">
+      <stop
+         id="stop2784"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2786"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2788">
+      <stop
+         id="stop2790"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2792"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2794">
+      <stop
+         id="stop2796"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2798"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2800"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2802"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2804"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2812">
+      <stop
+         id="stop2814"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2816" />
+      <stop
+         id="stop2818"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2822"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2824"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2826"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2828"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.1296478e-2,-0.2660144,0.3444364,-8.7244696e-3,27.683344,23.53042)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3070"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2494981,0,0,0.2714449,28.137815,14.512333)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3068"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1115779,7.7416911e-3,-8.3064858e-3,0.1245599,31.864361,16.99391)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3066"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9755905,0,0,0.6187301,4.6058755,12.758469)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3064"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0,-1.5160988,-1.1387138,0,65.407506,72.437718)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3062"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0,-1.503917,-1.1479369,0,66.120269,77.688631)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3060"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient4616"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4614"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4612"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient4610"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2655">
+      <stop
+         id="stop2657"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2659"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2661">
+      <stop
+         id="stop2663"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2665"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2667">
+      <stop
+         id="stop2669"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2671"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient4599"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient4597"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4595"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2682">
+      <stop
+         id="stop2684"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2686" />
+      <stop
+         id="stop2688"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4589"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4587"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient4585"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4583"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient4581"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient4579"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient4577"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5001"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4999"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4997"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4995"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4993"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4991"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4989"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4987"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4985"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4983"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2429"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2427"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2425"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2423"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2421"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2419"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2417"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2415"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2413"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2411"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0,0.6202002,-0.6629454,0,-1.222397,-12.387324)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4231"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4228"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4226"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4169"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4167"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2269"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2267"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2265"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2263"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2261"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2259"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2257"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4537">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop4539" />
+      <stop
+         id="stop4541"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop4543" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4525">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4527" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4529" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4519">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4521" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop4523" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4513">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4515" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4517" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4499"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4497"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="811"
+     inkscape:window-width="1020"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="15.455928"
+     inkscape:cx="17.128316"
+     inkscape:zoom="17.481619"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="22px"
+     height="22px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB layout</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <g
+       style="display:inline"
+       id="g2354"
+       inkscape:label="Shadow"
+       transform="matrix(0.5199887,0,0,0.2954903,-1.4208371,7.7713745)">
+      <g
+         id="g2356"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2358"
+           style="opacity:1;fill:url(#radialGradient2370);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect2360"
+           style="opacity:1;fill:url(#radialGradient2372);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect2362"
+           style="opacity:1;fill:url(#linearGradient2374);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.0601086,0.50760946 C 3.0601086,0.50760946 18.599074,0.48873598 18.865503,0.5 C 19.132153,0.51127336 19.514301,0.8147177 19.514301,1.1247323 L 19.5,19.876525 C 19.5,20.18654 19.250191,20.436118 18.939889,20.436118 L 3.0601086,20.436118 C 2.7498079,20.436118 2.499999,20.18654 2.499999,19.876525 L 2.499999,1.0672027 C 2.499999,0.75718813 2.7498079,0.50760946 3.0601086,0.50760946 z "
+       id="rect15391"
+       sodipodi:nodetypes="czccccccc" />
+    <g
+       id="g3089"
+       style="opacity:1;stroke:#5fc500;stroke-opacity:1;display:inline"
+       transform="translate(-16.518435,-18.451361)">
+      <path
+         sodipodi:nodetypes="ccc"
+         id="path3091"
+         d="M 23.027797,19.959636 L 24.043632,21.262263 L 24.043632,27.928932"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3093"
+         d="M 27.043631,27.928932 L 27.043631,20.298886 L 26.679245,19.951361"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         sodipodi:nodetypes="ccc" />
+      <path
+         id="path3095"
+         d="M 31.043631,28.033955 L 31.043631,21.018638"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         sodipodi:nodetypes="cc" />
+      <path
+         id="path3099"
+         d="M 24.501906,27.37074 L 24.501906,28.443166"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3101"
+         d="M 27.543631,27.40046 L 27.543631,28.4574"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:1.99999964;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3103"
+         d="M 30.543631,27.376817 L 30.543631,28.428931"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3479"
+         d="M 30.018435,34.928931 L 30.018435,38.0578"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3109"
+         d="M 30.543631,34.398508 L 30.543631,35.423293"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3477"
+         d="M 27.018435,34.928931 L 27.018435,38.0578"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3111"
+         d="M 27.518435,34.414153 L 27.518435,35.443706"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:1.99999976;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3475"
+         d="M 24.373463,34.961856 L 22.735295,37.807585"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3113"
+         d="M 24.518435,34.428931 L 24.518435,35.518863"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="ccccccc"
+         id="path3123"
+         d="M 20.245896,20.92893 L 21.960181,22.535218 L 21.967596,24.02587 L 22.017384,34.034748 L 20.993315,35.915959 L 19.999037,35.945013 L 20.245896,20.92893 z "
+         style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:0.99999982;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3507"
+         d="M 34.018435,23.951361 L 34.018435,27.08023"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3485"
+         d="M 33.518435,27.356505 L 33.518435,28.428931"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3505"
+         d="M 33.018435,34.951361 L 33.018435,38.08023"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path3487"
+         d="M 33.518435,34.405358 L 33.518435,35.452504"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.5682125,1.5536151 C 3.5682125,1.5536151 18.36725,1.4864215 18.462831,1.4988658 C 18.559203,1.5114129 18.526148,1.5513185 18.526148,1.589503 L 18.5,19.494799 C 18.5,19.532984 18.469577,19.563724 18.431788,19.563724 L 3.5682125,19.563724 C 3.5304227,19.563724 3.4999999,19.532984 3.4999999,19.494799 L 3.4999999,1.6225403 C 3.4999999,1.5843558 3.5304227,1.5536151 3.5682125,1.5536151 z "
+       id="rect15660"
+       sodipodi:nodetypes="czccccccc" />
+    <g
+       style="stroke:#eeeeec;stroke-width:0.88475299;stroke-opacity:0.74509804;display:inline"
+       id="g3495"
+       transform="matrix(0.7772363,0,0,0.903468,55.223689,-13.598285)">
+      <rect
+         rx="1.7463098"
+         ry="1.5023171"
+         y="22.255915"
+         x="-63.969288"
+         height="14.373751"
+         width="16.706825"
+         id="rect3489"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.19334769;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.74509804;visibility:visible;display:block;overflow:visible" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-layout-24.png b/data/application-x-pcb-layout-24.png
new file mode 100644
index 0000000..8546f1c
Binary files /dev/null and b/data/application-x-pcb-layout-24.png differ
diff --git a/data/application-x-pcb-layout-32.png b/data/application-x-pcb-layout-32.png
new file mode 100644
index 0000000..8d0b292
Binary files /dev/null and b/data/application-x-pcb-layout-32.png differ
diff --git a/data/application-x-pcb-layout-32.svg b/data/application-x-pcb-layout-32.svg
new file mode 100644
index 0000000..eb93898
--- /dev/null
+++ b/data/application-x-pcb-layout-32.svg
@@ -0,0 +1,1362 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-pcb-32.png"
+   sodipodi:docname="application-x-pcb-32.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="32"
+   width="32"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,2.7369165,0.9724875)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2871"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2869"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2762"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2764"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2766"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2780"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.1303227,52.687269)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2782">
+      <stop
+         id="stop2784"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2786"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2788">
+      <stop
+         id="stop2790"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2792"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2794">
+      <stop
+         id="stop2796"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2798"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2800"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2802"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2804"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2812">
+      <stop
+         id="stop2814"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2816" />
+      <stop
+         id="stop2818"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2822"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2824"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2826"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2828"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.1296478e-2,-0.2660144,0.3444364,-8.7244696e-3,27.683344,23.53042)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3070"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2494981,0,0,0.2714449,28.137815,14.512333)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3068"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1115779,7.7416911e-3,-8.3064858e-3,0.1245599,31.864361,16.99391)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3066"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9755905,0,0,0.6187301,4.6058755,12.758469)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3064"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0,-1.5160988,-1.1387138,0,65.407506,72.437718)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3062"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0,-1.503917,-1.1479369,0,66.120269,77.688631)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3060"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2655">
+      <stop
+         id="stop2657"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2659"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2661">
+      <stop
+         id="stop2663"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2665"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2667">
+      <stop
+         id="stop2669"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2671"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2682">
+      <stop
+         id="stop2684"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2686" />
+      <stop
+         id="stop2688"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4076"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient4074"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient4072"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5001"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4999"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4997"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4995"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4993"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4991"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4989"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4987"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4985"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4983"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2429"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2427"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2425"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2423"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2421"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2419"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2417"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2415"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2413"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2411"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0,0.6202002,-0.6629454,0,-1.222397,-12.387324)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4231"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4228"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4226"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4169"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4167"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2269"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2267"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2265"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2263"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2261"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2259"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2257"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4028">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop4030" />
+      <stop
+         id="stop4032"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop4034" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4016">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4018" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4020" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4010">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4012" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop4014" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4004">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4006" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4008" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3990"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3988"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="18.219677"
+     inkscape:cx="22.724888"
+     inkscape:zoom="16"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     gridtolerance="0.4"
+     width="32px"
+     height="32px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB layout</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="matrix(0.722222,0,0,0.4444449,-1.215281,10.111103)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.2577956,2.5103056 C 5.2577956,2.5103056 26.39323,2.5030175 26.775662,2.5 C 27.15257,2.4969825 27.5,2.8717059 27.5,3.2915668 L 27.5,28.742131 C 27.5,29.161992 27.162024,29.500002 26.742205,29.500002 L 5.2577956,29.500002 C 4.8379769,29.500002 4.5,29.161992 4.5,28.742131 L 4.5,3.2681771 C 4.5,2.8483163 4.8379769,2.5103056 5.2577956,2.5103056 z "
+       id="rect15391"
+       sodipodi:nodetypes="czccccccc" />
+    <g
+       style="display:inline"
+       id="g5326"
+       transform="translate(-8.5,-10.5)">
+      <g
+         id="g3535">
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20,14.038563 L 20,23"
+           id="path2828"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 23,23 L 22.977903,13.983107"
+           id="path2830"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 26,23.994369 L 25.977903,14.016039"
+           id="path2832"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 29,23 L 29.022097,14.231379"
+           id="path2890"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20,32 L 20,36 L 17.125,38.8125"
+           id="path2910"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 23,32 L 23,38.875"
+           id="path2912"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 26,32 L 26,36 L 29,39"
+           id="path2914"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 29,32 L 29.015625,34.953125 L 31,37 L 35.09375,37"
+           id="path2916"
+           sodipodi:nodetypes="cccc" />
+        <path
+           style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 14.327123,14.47076 L 16.996094,14.503906 L 17.003906,34.988281 L 14.960937,37 L 14.334936,37.00201 L 14.327123,14.47076 z "
+           id="path2918"
+           sodipodi:nodetypes="cccccc" />
+        <path
+           sodipodi:nodetypes="cccccccc"
+           id="path3125"
+           d="M 34,33.5 L 33,33.5 L 33,30.5 L 33,21.5 L 32,20.5 L 32.007812,14.529091 L 34.007812,14.529091 L 34,33.5 z "
+           style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <rect
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           id="rect2920"
+           width="13.999999"
+           height="13.999999"
+           x="18"
+           y="21"
+           ry="2.6516502"
+           rx="2.65165" />
+        <path
+           style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 18.489223,29 C 19.97156,28.98973 19.954809,27.035528 18.46875,27.000001"
+           id="path2228"
+           sodipodi:nodetypes="cs" />
+      </g>
+      <g
+         id="g5316"
+         style="stroke:#d3d7cf;stroke-opacity:1">
+        <path
+           id="path2894"
+           d="M 20.5,23.425585 L 20.5,24.487751"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3217"
+           d="M 23.5,23.437834 L 23.5,24.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3219"
+           d="M 26.5,23.437834 L 26.5,24.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3221"
+           d="M 29.5,23.437834 L 29.5,24.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3223"
+           d="M 20.5,31.5 L 20.5,32.562166"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3225"
+           d="M 23.5,31.5 L 23.5,32.562166"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3227"
+           d="M 26.5,31.5 L 26.5,32.562166"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path3229"
+           d="M 29.5,31.5 L 29.5,32.562166"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.5954975,3.5000001 C 5.5954975,3.5000001 26.362107,3.5088312 26.409173,3.5078126 C 26.452334,3.5068785 26.506473,3.5936879 26.506473,3.6466921 L 26.500001,28.404325 C 26.500001,28.457328 26.457409,28.500001 26.404502,28.500001 L 5.5954975,28.500001 C 5.5425918,28.500001 5.5000003,28.457328 5.5000003,28.404325 L 5.5000003,3.5956758 C 5.5000003,3.5426715 5.5425918,3.5000001 5.5954975,3.5000001 z "
+       id="rect15660"
+       sodipodi:nodetypes="czccccccc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-layout-48.png b/data/application-x-pcb-layout-48.png
new file mode 100644
index 0000000..b704ebf
Binary files /dev/null and b/data/application-x-pcb-layout-48.png differ
diff --git a/data/application-x-pcb-layout-48.svg b/data/application-x-pcb-layout-48.svg
new file mode 100644
index 0000000..c3e0141
--- /dev/null
+++ b/data/application-x-pcb-layout-48.svg
@@ -0,0 +1,1341 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-pcb-48.png"
+   sodipodi:docname="application-x-pcb-48.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="48.000000px"
+   width="48.000000px"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3">
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,43.812504,1.5156258)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.960493,0,0,1.041132,40.458951,0.8691785)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <linearGradient
+       id="linearGradient2937">
+      <stop
+         style="stop-color:#3e5512;stop-opacity:1;"
+         offset="0"
+         id="stop2939" />
+      <stop
+         style="stop-color:#567619;stop-opacity:1;"
+         offset="1"
+         id="stop2941" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9747957,0,0,1.032679,3.1601082,0.6484425)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-0.9575584,7.0216417)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2871"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2869"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2762"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2764"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2766"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2780"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.1303227,52.687269)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2782">
+      <stop
+         id="stop2784"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2786"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2788">
+      <stop
+         id="stop2790"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2792"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2794">
+      <stop
+         id="stop2796"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2798"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2800"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2802"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2804"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2812">
+      <stop
+         id="stop2814"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2816" />
+      <stop
+         id="stop2818"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2822"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2824"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2826"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2828"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.1296478e-2,-0.2660144,0.3444364,-8.7244696e-3,27.683344,23.53042)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3070"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2494981,0,0,0.2714449,28.137815,14.512333)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3068"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1115779,7.7416911e-3,-8.3064858e-3,0.1245599,31.864361,16.99391)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3066"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9755905,0,0,0.6187301,4.6058755,12.758469)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3064"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0,-1.5160988,-1.1387138,0,65.407506,72.437718)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3062"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0,-1.503917,-1.1479369,0,66.120269,77.688631)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3060"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2655">
+      <stop
+         id="stop2657"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2659"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2661">
+      <stop
+         id="stop2663"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2665"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2667">
+      <stop
+         id="stop2669"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2671"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient3426"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2682">
+      <stop
+         id="stop2684"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2686" />
+      <stop
+         id="stop2688"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3420"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3418"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient3416"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5001"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4999"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4997"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4995"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4993"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4991"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4989"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4987"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4985"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4983"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2429"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2427"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2425"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2423"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2421"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2419"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2417"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2415"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2413"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2411"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0,0.6202002,-0.6629454,0,-1.222397,-12.387324)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4231"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4228"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4226"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4169"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4167"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2269"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2267"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2265"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2263"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2261"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2259"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2257"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3372">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop3374" />
+      <stop
+         id="stop3376"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop3378" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3360">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop3362" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop3364" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3354">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop3356" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop3358" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3348">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop3350" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop3352" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2251"
+       inkscape:collect="always">
+      <stop
+         id="stop2253"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2255"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3337"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3335"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="967"
+     inkscape:window-width="1112"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="24.583022"
+     inkscape:cx="1.8270777"
+     inkscape:zoom="7.9999995"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB layout</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="translate(-2e-6,2.838692e-5)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#a1a1a1;stroke-width:1.00064719;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 7.6527627,3.646744 C 7.6527627,3.646744 40.30775,3.5066827 40.325214,3.5061448 C 40.979007,3.4860074 41.509479,4.0028484 41.509479,4.6383714 L 41.478229,43.352526 C 41.478229,43.988047 40.964242,44.499676 40.32579,44.499676 L 7.6527627,44.499676 C 7.0143114,44.499676 6.5003236,43.988047 6.5003236,43.352526 L 6.5003236,4.7938955 C 6.5003236,4.1583736 7.0143114,3.646744 7.6527627,3.646744 z "
+       id="rect15391"
+       sodipodi:nodetypes="csccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1.00331986;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 7.6517125,4.5856061 C 7.6517125,4.5856061 40.280989,4.4960918 40.430059,4.5074811 C 40.563591,4.5176832 40.513965,4.6219735 40.513965,4.7045395 L 40.49834,43.379635 C 40.49834,43.462201 40.431417,43.528671 40.348288,43.528671 L 7.6517125,43.528671 C 7.5685833,43.528671 7.5016599,43.462201 7.5016599,43.379635 L 7.5016599,4.734642 C 7.5016599,4.6520761 7.5685833,4.5856061 7.6517125,4.5856061 z "
+       id="rect15660"
+       sodipodi:nodetypes="czccccccc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       style="display:inline"
+       id="g5326">
+      <g
+         id="g3535">
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 10.5,7.5 L 15.5,7.5 L 20.5,12.5 L 20.5,22.5"
+           id="path2828"
+           sodipodi:nodetypes="ccc" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 24.5,22.5 L 24.5,10.5 L 21.5,7.5"
+           id="path2830" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 28.5,22.5 L 28.5,8.5 L 27.5,7.5"
+           id="path2832" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 32.5,22.5 L 32.5,7.5"
+           id="path2890" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 20.5,33.5 L 20.5,37.5 L 17.5,40.5 L 10.5,40.5"
+           id="path2910" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 24.5,33.5 L 24.5,40.5"
+           id="path2912" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 28.5,33.5 L 28.5,37.5 L 31.5,40.5 L 37.5,40.5"
+           id="path2914" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 32.5,33.5 L 34.5,35.5 L 37.5,35.5"
+           id="path2916" />
+        <path
+           style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           d="M 10.5,10.5 L 14.5,10.5 L 17.5,13.5 L 17.5,35.5 L 15.5,37.5 L 10.5,37.5 L 10.5,10.5 z "
+           id="path2918"
+           sodipodi:nodetypes="ccccccc" />
+        <path
+           sodipodi:nodetypes="cccccccc"
+           id="path3125"
+           d="M 37.460937,32.476562 L 36.5,32.5 L 36.5,29.5 L 36.5,20.5 L 35.460937,19.476562 L 35.492187,7.5078124 L 37.492187,7.5078124 L 37.460937,32.476562 z "
+           style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <rect
+           style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+           id="rect2920"
+           width="18"
+           height="17"
+           x="17.5"
+           y="19.5"
+           ry="2.6516504"
+           rx="2.6516504" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#eeeeec;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+           d="M 17.906251,29.515624 C 20.055187,29.499999 20.030903,26.526597 17.876571,26.47254"
+           id="path2228"
+           sodipodi:nodetypes="cs" />
+      </g>
+      <g
+         id="g5316"
+         style="stroke:#d3d7cf;stroke-opacity:1;fill:#ffffff;fill-opacity:1">
+        <path
+           id="path2894"
+           d="M 20.5,22.5 L 20.5,24.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2896"
+           d="M 24.5,22.5 L 24.5,24.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2898"
+           d="M 28.5,22.5 L 28.5,24.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2900"
+           d="M 32.5,22.5 L 32.5,24.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2902"
+           d="M 32.5,31.5 L 32.5,33.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2904"
+           d="M 28.5,31.5 L 28.5,33.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2906"
+           d="M 24.5,31.5 L 24.5,33.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+        <path
+           id="path2908"
+           d="M 20.5,31.5 L 20.5,33.5"
+           style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-pcb-layout.svg b/data/application-x-pcb-layout.svg
new file mode 100644
index 0000000..db21372
--- /dev/null
+++ b/data/application-x-pcb-layout.svg
@@ -0,0 +1,1346 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:export-filename="/home/pcjc2/gedawork/tomazicons/gnomemime/pngs/application-x-pcb-48.png"
+   sodipodi:docname="application-x-pcb.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="128"
+   width="128"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,43.812504,1.5156258)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.960493,0,0,1.041132,40.458951,0.8691785)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <linearGradient
+       id="linearGradient2937">
+      <stop
+         style="stop-color:#3e5512;stop-opacity:1;"
+         offset="0"
+         id="stop2939" />
+      <stop
+         style="stop-color:#567619;stop-opacity:1;"
+         offset="1"
+         id="stop2941" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9747957,0,0,1.032679,3.1601082,0.6484425)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-0.9575584,7.0216417)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2873"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2871"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2869"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2762"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2764"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2766"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2780"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.1303227,52.687269)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2782">
+      <stop
+         id="stop2784"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2786"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2788">
+      <stop
+         id="stop2790"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2792"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2794">
+      <stop
+         id="stop2796"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2798"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2800"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2802"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2804"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2812">
+      <stop
+         id="stop2814"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2816" />
+      <stop
+         id="stop2818"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2822"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2824"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2946"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2944"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2830"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2834"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100485,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.1296478e-2,-0.2660144,0.3444364,-8.7244696e-3,27.683344,23.53042)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3070"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2494981,0,0,0.2714449,28.137815,14.512333)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3068"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1115779,7.7416911e-3,-8.3064858e-3,0.1245599,31.864361,16.99391)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3066"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9755905,0,0,0.6187301,4.6058755,12.758469)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3064"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0,-1.5160988,-1.1387138,0,65.407506,72.437718)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3062"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0,-1.503917,-1.1479369,0,66.120269,77.688631)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3060"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2655">
+      <stop
+         id="stop2657"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2659"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2661">
+      <stop
+         id="stop2663"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2665"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2667">
+      <stop
+         id="stop2669"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2671"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-31.601449,25.72011)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-26.646599,28.95487)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2917"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2682">
+      <stop
+         id="stop2684"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2686" />
+      <stop
+         id="stop2688"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2911"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,23.640708,19.053842)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2909"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-15.664448,-140.85384)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2907"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443"
+       gradientTransform="translate(-4.3474219,36.453842)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5001"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4999"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4997"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4995"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4993"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4991"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4989"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4987"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4985"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4983"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-2.5484665e-2,-0.6941277,0.7770427,-2.2765299e-2,11.97476,39.615529)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2429"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.2579108,0,0,0.2730106,27.935961,15.089164)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2427"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.1153401,7.7863457e-3,-8.5865667e-3,0.1252783,31.78816,17.585055)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2425"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.3563984,0,0,0.9905858,-8.4045503,5.0457243)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2423"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(1.1521472,0,0,0.7955298,-0.6314408,8.4669847)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2421"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(1.1377199,0,0,0.80732,-0.2968403,8.1670973)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2419"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(1.1285784,0,0,0.8138589,-4.2372621,7.6617659)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2417"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2415"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2413"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2411"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0,0.6202002,-0.6629454,0,-1.222397,-12.387324)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4231"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4228"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4226"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4169"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4167"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2269"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2267"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2265"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2263"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2261"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2259"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2257"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2863">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2865" />
+      <stop
+         id="stop2867"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2869" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2851">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2853" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2855" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2845">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2847" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2849" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2839">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2841" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2843" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2251"
+       inkscape:collect="always">
+      <stop
+         id="stop2253"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2255"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6629415,-34.825583,-13.027495)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2828"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,-35.205869,-13.468369)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2826"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="967"
+     inkscape:window-width="1112"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer5"
+     inkscape:cy="83.042965"
+     inkscape:cx="44.74224"
+     inkscape:zoom="4"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="128px"
+     height="128px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB layout</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Tomaz Solc, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2225"
+       transform="scale(2.6667,2.6667)">
+      <g
+         transform="translate(-2e-6,2.838692e-5)"
+         inkscape:label="Shadow"
+         id="g2033"
+         style="display:inline">
+        <g
+           transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)"
+           style="opacity:0.4"
+           id="g3712">
+          <rect
+             style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect2801"
+             width="5"
+             height="7"
+             x="38"
+             y="40" />
+          <rect
+             style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3696"
+             width="5"
+             height="7"
+             x="-10"
+             y="-47"
+             transform="scale(-1,-1)" />
+          <rect
+             style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3700"
+             width="28"
+             height="7.0000005"
+             x="10"
+             y="40" />
+        </g>
+      </g>
+      <path
+         sodipodi:nodetypes="csccccccc"
+         id="rect15391"
+         d="M 7.6527627,3.646744 C 7.6527627,3.646744 40.30775,3.5066827 40.325214,3.5061448 C 40.979007,3.4860074 41.509479,4.0028484 41.509479,4.6383714 L 41.478229,43.352526 C 41.478229,43.988047 40.964242,44.499676 40.32579,44.499676 L 7.6527627,44.499676 C 7.0143114,44.499676 6.5003236,43.988047 6.5003236,43.352526 L 6.5003236,4.7938955 C 6.5003236,4.1583736 7.0143114,3.646744 7.6527627,3.646744 z "
+         style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#a1a1a1;stroke-width:1.00064719;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="czccccccc"
+         id="rect15660"
+         d="M 7.6517125,4.5856061 C 7.6517125,4.5856061 40.280989,4.4960918 40.430059,4.5074811 C 40.563591,4.5176832 40.513965,4.6219735 40.513965,4.7045395 L 40.49834,43.379635 C 40.49834,43.462201 40.431417,43.528671 40.348288,43.528671 L 7.6517125,43.528671 C 7.5685833,43.528671 7.5016599,43.462201 7.5016599,43.379635 L 7.5016599,4.734642 C 7.5016599,4.6520761 7.5685833,4.5856061 7.6517125,4.5856061 z "
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1.00331986;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <g
+         id="g5326"
+         style="display:inline">
+        <g
+           id="g3535">
+          <path
+             sodipodi:nodetypes="ccc"
+             id="path2828"
+             d="M 10.5,7.5 L 15.5,7.5 L 20.5,12.5 L 20.5,22.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2830"
+             d="M 24.5,22.5 L 24.5,10.5 L 21.5,7.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2832"
+             d="M 28.5,22.5 L 28.5,8.5 L 27.5,7.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2890"
+             d="M 32.5,22.5 L 32.5,7.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2910"
+             d="M 20.5,33.5 L 20.5,37.5 L 17.5,40.5 L 10.5,40.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2912"
+             d="M 24.5,33.5 L 24.5,40.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2914"
+             d="M 28.5,33.5 L 28.5,37.5 L 31.5,40.5 L 37.5,40.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             id="path2916"
+             d="M 32.5,33.5 L 34.5,35.5 L 37.5,35.5"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             sodipodi:nodetypes="ccccccc"
+             id="path2918"
+             d="M 10.5,10.5 L 14.5,10.5 L 17.5,13.5 L 17.5,35.5 L 15.5,37.5 L 10.5,37.5 L 10.5,10.5 z "
+             style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             style="opacity:1;color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 37.460937,32.476562 L 36.5,32.5 L 36.5,29.5 L 36.5,20.5 L 35.460937,19.476562 L 35.492187,7.5078124 L 37.492187,7.5078124 L 37.460937,32.476562 z "
+             id="path3125"
+             sodipodi:nodetypes="cccccccc" />
+          <rect
+             rx="2.6516504"
+             ry="2.6516504"
+             y="19.5"
+             x="17.5"
+             height="17"
+             width="18"
+             id="rect2920"
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+          <path
+             sodipodi:nodetypes="cs"
+             id="path2228"
+             d="M 17.906251,29.515624 C 20.055187,29.499999 20.030903,26.526597 17.876571,26.47254"
+             style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+        </g>
+        <g
+           id="g5316"
+           style="stroke:#d3d7cf;stroke-opacity:1">
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 20.5,22.5 L 20.5,24.5"
+             id="path2894" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 24.5,22.5 L 24.5,24.5"
+             id="path2896" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 28.5,22.5 L 28.5,24.5"
+             id="path2898" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 32.5,22.5 L 32.5,24.5"
+             id="path2900" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 32.5,31.5 L 32.5,33.5"
+             id="path2902" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 28.5,31.5 L 28.5,33.5"
+             id="path2904" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 24.5,31.5 L 24.5,33.5"
+             id="path2906" />
+          <path
+             style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+             d="M 20.5,31.5 L 20.5,33.5"
+             id="path2908" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-pcb-netlist-16.png b/data/application-x-pcb-netlist-16.png
new file mode 100644
index 0000000..4c1fc3e
Binary files /dev/null and b/data/application-x-pcb-netlist-16.png differ
diff --git a/data/application-x-pcb-netlist-16.svg b/data/application-x-pcb-netlist-16.svg
new file mode 100644
index 0000000..e662ad1
--- /dev/null
+++ b/data/application-x-pcb-netlist-16.svg
@@ -0,0 +1,530 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="16"
+   width="16"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2254"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2256"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2258"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2260"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient2267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100475,18.786557)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2270"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3580332,0,0,0.3814965,-0.9615384,-0.7823571)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2272"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2607"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2605"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2603"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2595">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2597" />
+      <stop
+         id="stop2599"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2601" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient2587"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2585"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2583"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2574">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2576" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2578" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2568">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2570" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2572" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2562">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2564" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2566" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2560"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="g2609"
+     inkscape:cy="-3.9705098"
+     inkscape:cx="15.489981"
+     inkscape:zoom="16"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="16px"
+     height="16px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB netlist</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient2270);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2272);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 1.9283192,0.50572542 L 10.668425,0.50000003 C 10.668425,0.50000003 14.5,3.9970506 14.5,4.2303066 L 14.5,15.07896 C 14.5,15.312218 14.30897,15.5 14.07168,15.5 L 1.9283192,15.5 C 1.6910303,15.5 1.5,15.312218 1.5,15.07896 L 1.5,0.92676521 C 1.5,0.69350923 1.6910303,0.50572542 1.9283192,0.50572542 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2267);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 2.5498889,1.529131 L 10.284615,1.5 C 10.284615,1.5 13.5,4.4628005 13.5,4.4903008 L 13.470417,14.45036 C 13.470417,14.47786 13.448166,14.499999 13.42053,14.499999 L 2.5498889,14.499999 C 2.5222509,14.499999 2.5000009,14.47786 2.5000009,14.45036 L 2.5000009,1.5787709 C 2.5000009,1.5512705 2.5222509,1.529131 2.5498889,1.529131 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g2455"
+       transform="matrix(0.7602792,0,0,0.7602792,-9.226339,5.4615348)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path2364"
+         d="M 31.011754,-1.8223442 C 30.676762,-2.441693 28.204843,-3.5381018 26.918162,-3.9058239 C 26.996123,-3.1823862 26.861692,-1 26.861692,-1 C 27.810289,-1.6323977 30.652688,-1.8969046 31.011754,-1.8223442 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2254);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2366"
+         d="M 31.207393,-1.878537 C 31.213448,-2.5293972 28.2857,-6.5731136 26.5,-6.525521 C 26.947525,-6.4183606 27.305033,-3.6886865 26.665865,-2.3951458 C 27.930661,-2.3951458 30.764087,-2.7331589 31.207393,-1.878537 z "
+         style="color:#000000;fill:url(#radialGradient2256);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2258);stroke-width:1.31530643;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2260);stroke-width:1.3153069;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 29.385087,-3.7201259 C 29.050095,-4.3394746 29.205755,-3.8768359 28.321508,-4.4457758 C 28.316997,-3.9329987 28.412206,-4.339708 28.300666,-3.7332338 C 28.300666,-3.7332338 29.026021,-3.7946863 29.385087,-3.7201259 z "
+         id="path2368"
+         sodipodi:nodetypes="cccc" />
+    </g>
+    <g
+       id="g2609"
+       transform="translate(10.427253,-5.1308212)">
+      <g
+         style="display:inline"
+         id="g3237"
+         transform="translate(-17.927253,1.130821)">
+        <path
+           id="path2214"
+           d="M 11.631551,8.6500002 L 16.378846,8.6500002"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#555753;stroke-width:1.20000005;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2217"
+           d="M 13.000001,12.5 L 16.562499,12.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2219"
+           d="M 13.000001,14.5 L 16.562499,14.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2221"
+           d="M 13.000001,16.5 L 16.562499,16.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 13.25,10.797923 L 13.25,16.202078"
+           id="path2225" />
+        <path
+           id="path3197"
+           d="M 17.942183,12.5 L 19.423966,12.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#555753;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3199"
+           d="M 17.893319,14.5 L 18.556751,14.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#555753;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3201"
+           d="M 17.944234,16.5 L 19.836613,16.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#555753;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      </g>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-netlist-22.png b/data/application-x-pcb-netlist-22.png
new file mode 100644
index 0000000..3d1b80d
Binary files /dev/null and b/data/application-x-pcb-netlist-22.png differ
diff --git a/data/application-x-pcb-netlist-22.svg b/data/application-x-pcb-netlist-22.svg
new file mode 100644
index 0000000..d249441
--- /dev/null
+++ b/data/application-x-pcb-netlist-22.svg
@@ -0,0 +1,567 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="22"
+   width="22"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5352"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient3095"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient3093"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient3091"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient3089"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3087"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3085"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2374"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2370"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2346"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2348"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2460"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2462"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2464"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2466"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="g3197"
+     inkscape:cy="14.106678"
+     inkscape:cx="19.38641"
+     inkscape:zoom="24.722741"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="22px"
+     height="22px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB netlist</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <g
+       style="display:inline"
+       id="g2354"
+       inkscape:label="Shadow"
+       transform="matrix(0.5199887,0,0,0.2954903,-1.4208371,7.7713745)">
+      <g
+         id="g2356"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2358"
+           style="opacity:1;fill:url(#radialGradient2370);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect2360"
+           style="opacity:1;fill:url(#radialGradient2372);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect2362"
+           style="opacity:1;fill:url(#linearGradient2374);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.0601086,0.50760946 L 14.489478,0.5 C 14.489478,0.5 19.5,5.1478405 19.5,5.4578551 L 19.5,19.876525 C 19.5,20.18654 19.250191,20.436118 18.939889,20.436118 L 3.0601086,20.436118 C 2.7498079,20.436118 2.499999,20.18654 2.499999,19.876525 L 2.499999,1.0672027 C 2.499999,0.75718813 2.7498079,0.50760946 3.0601086,0.50760946 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.5682125,1.5536151 L 14.144009,1.5131665 C 14.144009,1.5131665 18.540449,5.6270281 18.540449,5.6652126 L 18.5,19.494799 C 18.5,19.532984 18.469577,19.563724 18.431788,19.563724 L 3.5682125,19.563724 C 3.5304227,19.563724 3.4999999,19.532984 3.4999999,19.494799 L 3.4999999,1.6225403 C 3.4999999,1.5843558 3.5304227,1.5536151 3.5682125,1.5536151 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g2455"
+       transform="translate(-11.707402,7.0259378)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path2364"
+         d="M 31.011754,-1.8223442 C 30.676762,-2.441693 28.204843,-3.5381018 26.918162,-3.9058239 C 26.996123,-3.1823862 26.861692,-1 26.861692,-1 C 27.810289,-1.6323977 30.652688,-1.8969046 31.011754,-1.8223442 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2460);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2366"
+         d="M 31.207393,-1.878537 C 31.213448,-2.5293972 28.2857,-6.5731136 26.5,-6.525521 C 26.947525,-6.4183606 27.305033,-3.6886865 26.665865,-2.3951458 C 27.930661,-2.3951458 30.764087,-2.7331589 31.207393,-1.878537 z "
+         style="color:#000000;fill:url(#radialGradient2462);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2464);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2466);stroke-width:1.00000048;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 29.611197,-3.4408139 C 29.276205,-4.0601626 28.899842,-4.3423558 28.015595,-4.9112957 C 28.011084,-4.3985186 28.119593,-4.0470954 28.008053,-3.4406212 C 28.008053,-3.4406212 29.252131,-3.5153743 29.611197,-3.4408139 z "
+         id="path2368"
+         sodipodi:nodetypes="cccc" />
+    </g>
+    <g
+       style="display:inline"
+       transform="translate(11.895702,-6.1130246)"
+       id="g3195">
+      <g
+         transform="translate(-17.927253,1.130821)"
+         id="g3197"
+         style="display:inline">
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#989a96;stroke-width:1.20000005;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 11.631551,9.6541256 L 18.191702,9.6541256"
+           id="path3200" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 13.286361,12.43428 L 17.515172,12.43428"
+           id="path3202" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 13.28636,14.464407 L 17.515171,14.464407"
+           id="path3206" />
+        <path
+           id="path3208"
+           d="M 13.457757,11.524013 L 13.457757,14.330454"
+           style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.29999983;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#989a96;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 19.000345,12.482203 L 22.515171,12.482203"
+           id="path3210" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#989a96;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 19.143987,14.464407 L 22.515171,14.464407"
+           id="path3214" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#989a96;stroke-width:1.20000005;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 11.631551,17.260857 L 18.191702,17.260857"
+           id="path4185" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 13.302741,19.452077 L 17.531552,19.452077"
+           id="path4187" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 13.31912,21.482204 L 17.547931,21.482204"
+           id="path4189" />
+        <path
+           id="path4193"
+           d="M 13.474137,18.960161 L 13.474137,21.414818"
+           style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.30000019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           inkscape:export-xdpi="90"
+           inkscape:export-ydpi="90" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#989a96;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 19.016725,19.5 L 22.531551,19.5"
+           id="path4195" />
+        <path
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#989a96;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+           d="M 19.016725,21.5 L 20.821819,21.5"
+           id="path4197" />
+      </g>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline" />
+</svg>
diff --git a/data/application-x-pcb-netlist-24.png b/data/application-x-pcb-netlist-24.png
new file mode 100644
index 0000000..9ddbe52
Binary files /dev/null and b/data/application-x-pcb-netlist-24.png differ
diff --git a/data/application-x-pcb-netlist-32.png b/data/application-x-pcb-netlist-32.png
new file mode 100644
index 0000000..f220618
Binary files /dev/null and b/data/application-x-pcb-netlist-32.png differ
diff --git a/data/application-x-pcb-netlist-32.svg b/data/application-x-pcb-netlist-32.svg
new file mode 100644
index 0000000..820568e
--- /dev/null
+++ b/data/application-x-pcb-netlist-32.svg
@@ -0,0 +1,1310 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="32"
+   width="32"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2237"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-27.58256,-4.478359)"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2239"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,24.06011,11.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2241"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,19.10526,8.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2243"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-1.709646,44.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient2560"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient2562">
+      <stop
+         id="stop2564"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2566"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2568">
+      <stop
+         id="stop2570"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2572"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2574">
+      <stop
+         id="stop2576"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2578"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient2583"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient2585"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient2587"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2595">
+      <stop
+         id="stop2597"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop2599" />
+      <stop
+         id="stop2601"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2603"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2605"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2607"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.3609333,0,0,0.3784312,0.2885314,-0.5454831)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2272"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.3580332,0,0,0.3814965,-0.9615384,-0.7823571)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2270"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.7008044,0,0,0.7437981,-9.9100475,18.786557)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2267"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2260"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2258"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2256"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2254"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2352"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2348"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2346"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2505">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop2507" />
+      <stop
+         id="stop2509"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop2511" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2493">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2495" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2497" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2487">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2489" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2491" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2481">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2483" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2485" />
+    </linearGradient>
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2370"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2372"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2374"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3021"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3019"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3017"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3009">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop3011" />
+      <stop
+         id="stop3013"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop3015" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient4796"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5352"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5350"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2998"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="scale(0.960493,1.041132)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2996"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.8244190"
+       cy="3.7561285"
+       cx="8.8244190"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2994"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2988">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2990" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2992" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2982">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2984" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop2986" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2976">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2978" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2980" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient8166"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3085"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient3087"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient3089"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient3091"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient3093"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient3095"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-1.5076076e-2,-0.4596786,0.4596786,-1.5076076e-2,11.835232,12.52793)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2466"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.1525734,0,0,0.1627359,21.40854,-3.7904327)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2464"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.708450"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(6.8232262e-2,4.6412794e-3,-5.0795931e-3,7.467582e-2,23.687403,-2.3026837)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2462"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929165"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(0.8024106,0,0,0.5904677,-6.43832e-2,-9.7973641)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2460"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-86.361543,21.058056)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5220"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="2.3667307"
+       fx="31.863327"
+       cy="2.3667307"
+       cx="31.863327"
+       gradientTransform="matrix(0.331735,0,0,0.353831,-65.457201,-42.426155)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5218"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="14.9373"
+       fx="30.653816"
+       cy="14.9373"
+       cx="30.653816"
+       gradientTransform="matrix(0.148355,1.0091369e-2,-1.1044379e-2,0.162365,-60.502351,-39.191396)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5216"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-112.23446,-27.483048)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5214"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5206">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop5208" />
+      <stop
+         id="stop5210"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop5212" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-43.582576,21.39082)"
+       r="4.2929163"
+       fy="12.98915"
+       fx="37.030354"
+       cy="12.98915"
+       cx="37.030354"
+       id="radialGradient5198"
+       xlink:href="#linearGradient4790"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.9582194,0,0,1.0327671,-13.46843,25.515628)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5196"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.4681973,0,0,0.5070371,-0.7189363,-1.2043482)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5194"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.4719897,0,0,0.5029633,0.9157706,-0.8895248)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5192"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5186">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5188" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop5190" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5180">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5182" />
+      <stop
+         style="stop-color:#8a8a8a;stop-opacity:1;"
+         offset="1"
+         id="stop5184" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5174">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop5176" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop5178" />
+    </linearGradient>
+    <linearGradient
+       y2="38.070381"
+       x2="34.170048"
+       y1="36.921333"
+       x1="33.396004"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-17.709662,69.931924)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5172"
+       xlink:href="#linearGradient2251"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5158"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5156"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5154"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5152"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <radialGradient
+       r="2.5"
+       fy="43.5"
+       fx="4.9929786"
+       cy="43.5"
+       cx="4.9929786"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5150"
+       xlink:href="#linearGradient3688"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="39.999443"
+       x2="25.058096"
+       y1="47.027729"
+       x1="25.058096"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5148"
+       xlink:href="#linearGradient3702"
+       inkscape:collect="always" />
+    <radialGradient
+       r="86.70845"
+       fy="35.736916"
+       fx="33.966679"
+       cy="35.736916"
+       cx="33.966679"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5146"
+       xlink:href="#linearGradient259"
+       inkscape:collect="always" />
+    <radialGradient
+       r="37.751713"
+       fy="3.7561285"
+       fx="8.824419"
+       cy="3.7561285"
+       cx="8.824419"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5144"
+       xlink:href="#linearGradient269"
+       inkscape:collect="always" />
+    <radialGradient
+       r="38.158695"
+       fy="7.2678967"
+       fx="8.1435566"
+       cy="7.2678967"
+       cx="8.1435566"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5142"
+       xlink:href="#linearGradient15662"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5140"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5138"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5263"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6334434,0,0,0.6866935,0.1449688,0.10524)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5265"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6385744,0,0,0.6811763,2.356631,0.5316134)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient5267"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.620387,0,0,0.6613169,2.7369165,0.9786813)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="6.1548492"
+     inkscape:cx="24.106316"
+     inkscape:zoom="11.313709"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     gridtolerance="0.4"
+     width="32px"
+     height="32px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB netlist</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="matrix(0.6999997,0,0,0.444445,-0.8000002,9.673599)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.2577956,2.5103056 L 20.72106,2.5 C 20.72106,2.5 27.5,8.7946913 27.5,9.2145522 L 27.5,28.742131 C 27.5,29.161992 27.162024,29.500002 26.742205,29.500002 L 5.2577956,29.500002 C 4.8379769,29.500002 4.5,29.161992 4.5,28.742131 L 4.5,3.2681771 C 4.5,2.8483163 4.8379769,2.5103056 5.2577956,2.5103056 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 5.5954975,3.5 L 21.534173,3.5 C 21.534173,3.5 26.500001,8.6364009 26.500001,8.6892753 L 26.500001,28.343295 C 26.500001,28.396168 26.457409,28.438736 26.404502,28.438736 L 5.5954975,28.438736 C 5.5425918,28.438736 5.5000003,28.396168 5.5000003,28.343295 L 5.5000003,3.5954413 C 5.5000003,3.5425669 5.5425918,3.5 5.5954975,3.5 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       transform="translate(-3.0166979,-1.9999996)"
+       style="display:inline"
+       id="g5269">
+      <path
+         id="path5271"
+         d="M 10.497033,9.4999998 L 16.986368,9.4999998"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5273"
+         d="M 12.516697,12.499999 L 18.029335,12.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5275"
+         d="M 12.516697,14.499999 L 18.029335,14.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5277"
+         d="M 12.516697,16.499999 L 18.029335,16.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 12.766698,11.170414 L 12.766698,16.249999"
+         id="path5279" />
+      <path
+         id="path5281"
+         d="M 20.114815,12.499999 L 24.531619,12.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5283"
+         d="M 20.114815,14.499999 L 26.074757,14.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5285"
+         d="M 20.074758,16.499999 L 25.054586,16.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5287"
+         d="M 10.485328,20.5 L 16.081213,20.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5289"
+         d="M 12.562118,23.499999 L 18.074755,23.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5291"
+         d="M 12.562118,25.499999 L 18.074755,25.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5293"
+         d="M 12.562118,27.499999 L 18.074755,27.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         d="M 12.824373,22.170414 L 12.824373,27.249999"
+         id="path5295" />
+      <path
+         id="path5297"
+         d="M 20.090623,23.499999 L 23.695122,23.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5299"
+         d="M 20.136025,25.499999 L 25.766765,25.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5301"
+         d="M 20.101821,27.499999 L 23.163048,27.499999"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      <path
+         id="path5303"
+         d="M 10.5,31.5 L 19.740165,31.5"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2232"
+       transform="matrix(0.6665574,0,0,0.6665574,0.5640292,0.7438964)">
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path5348"
+         d="M 39.985189,12.861445 C 39.256827,11.514817 33.882221,9.130934 31.084635,8.3314083 C 31.254143,9.904354 30.961856,14.649439 30.961856,14.649439 C 33.024356,13.274439 39.204485,12.699331 39.985189,12.861445 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient2237);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2210"
+         d="M 40.410559,12.739267 C 40.423724,11.324125 34.058025,2.5320142 30.175441,2.6354934 C 31.148479,2.8684884 31.925796,8.803523 30.536076,11.616023 C 33.286076,11.616023 39.446694,10.881093 40.410559,12.739267 z "
+         style="opacity:1;color:#000000;fill:url(#radialGradient2239);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient2241);stroke-width:1.50024605;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2243);stroke-width:1.50024641;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 37.558971,9.9767321 C 36.830609,8.6301041 34.525619,5.9151604 32.603033,4.6781344 C 32.654442,6.2680727 33.116647,7.3994674 32.54789,10.055612 C 32.54789,10.055612 36.731384,9.8849421 37.558971,9.9767321 z "
+         id="path2247"
+         sodipodi:nodetypes="cccc" />
+    </g>
+  </g>
+</svg>
diff --git a/data/application-x-pcb-netlist-48.png b/data/application-x-pcb-netlist-48.png
new file mode 100644
index 0000000..eea1842
Binary files /dev/null and b/data/application-x-pcb-netlist-48.png differ
diff --git a/data/application-x-pcb-netlist-48.svg b/data/application-x-pcb-netlist-48.svg
new file mode 100644
index 0000000..2d7f15f
--- /dev/null
+++ b/data/application-x-pcb-netlist-48.svg
@@ -0,0 +1,451 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="48.000000px"
+   width="48.000000px"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.8244190"
+       cy="3.7561285"
+       fx="8.8244190"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0.000000,0.000000,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,-2.3449e-17,2.501087e-17,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.708450" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929165"
+       gradientTransform="matrix(1.744653,2.313551e-22,-1.663e-22,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="0"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer1"
+     inkscape:cy="50.568876"
+     inkscape:cx="10.481007"
+     inkscape:zoom="4"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB netlist</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow">
+    <g
+       style="display:inline"
+       id="g2033"
+       inkscape:label="Shadow"
+       transform="translate(-2e-6,2.838692e-5)">
+      <g
+         id="g3712"
+         style="opacity:0.4"
+         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+        <rect
+           y="40"
+           x="38"
+           height="7"
+           width="5"
+           id="rect2801"
+           style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           transform="scale(-1,-1)"
+           y="-47"
+           x="-10"
+           height="7"
+           width="5"
+           id="rect3696"
+           style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+        <rect
+           y="40"
+           x="10"
+           height="7.0000005"
+           width="28"
+           id="rect3700"
+           style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      </g>
+    </g>
+  </g>
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1">
+    <path
+       style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.7526014,3.6464462 L 31.199616,3.6308212 C 31.199616,3.6308212 41.478553,13.174533 41.478553,13.811106 L 41.478553,43.417892 C 41.478553,44.054465 40.966077,44.56694 40.329504,44.56694 L 7.7526014,44.56694 C 7.1160285,44.56694 6.6035528,44.054465 6.6035528,43.417892 L 6.6035528,4.7954948 C 6.6035528,4.1589219 7.1160285,3.6464462 7.7526014,3.6464462 z "
+       id="rect15391"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:block;overflow:visible"
+       d="M 7.8151023,4.5839462 L 32.691494,4.5839462 C 32.691494,4.5839462 40.44194,12.605373 40.44194,12.687946 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+       id="rect15660"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g3237">
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 10.5,9.4999998 L 18.634649,9.4999998"
+         id="path2214" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.518412,12.5 L 20.518411,12.5"
+         id="path2217" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.518412,14.5 L 20.518411,14.5"
+         id="path2219" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.518412,16.5 L 20.518411,16.5"
+         id="path2221" />
+      <path
+         id="path2225"
+         d="M 13.768412,11.170415 L 13.768412,16.25"
+         style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.518412,12.5 L 29.77733,12.5"
+         id="path3197" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.518412,14.5 L 31.964064,14.5"
+         id="path3199" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.461649,16.5 L 30.518412,16.5"
+         id="path3201" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 10.485328,20.5 L 17.5,20.5"
+         id="path3205" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.576087,23.5 L 20.576086,23.5"
+         id="path3207" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.576087,25.5 L 20.576086,25.5"
+         id="path3209" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.576087,27.5 L 20.576086,27.5"
+         id="path3211" />
+      <path
+         id="path3213"
+         d="M 13.826087,22.170415 L 13.826087,27.25"
+         style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.484131,23.5 L 28.591957,23.5"
+         id="path3215" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.548469,25.5 L 31.527618,25.5"
+         id="path3217" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.0000006;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.5,27.5 L 27.837971,27.5"
+         id="path3219" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 10.5,31.5 L 19.740165,31.5"
+         id="path3221" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.5,34.5 L 20.499999,34.5"
+         id="path3223" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.5,36.5 L 20.499999,36.5"
+         id="path3225" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 13.5,38.5 L 20.499999,38.5"
+         id="path3227" />
+      <path
+         id="path3229"
+         d="M 13.75,33.170415 L 13.75,38.25"
+         style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.412349,34.5 L 28.574064,34.5"
+         id="path3231" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.5,36.5 L 28.852639,36.5"
+         id="path3233" />
+      <path
+         style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 23.443237,38.5 L 30.5,38.5"
+         id="path3235" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 40.985189,13.861445 C 40.256827,12.514817 34.882221,10.130934 32.084635,9.3314083 C 32.254143,10.904354 31.961856,15.649439 31.961856,15.649439 C 34.024356,14.274439 40.204485,13.699331 40.985189,13.861445 z "
+       id="path5348"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="color:#000000;fill:url(#radialGradient5352);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5350);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible;opacity:1"
+       d="M 41.410559,13.739267 C 41.423724,12.324125 35.058025,3.5320142 31.175441,3.6354934 C 32.148479,3.8684884 32.925796,9.803523 31.536076,12.616023 C 34.286076,12.616023 40.446694,11.881093 41.410559,13.739267 z "
+       id="path2210"
+       sodipodi:nodetypes="cccc"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="path2247"
+       d="M 39.121563,11.586207 C 38.393201,10.239579 34.963027,6.5166576 33.040441,5.2796316 C 33.279381,6.7054805 33.577496,8.9620596 32.961856,11.524439 C 32.961856,11.524439 38.340859,11.424093 39.121563,11.586207 z "
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+  </g>
+</svg>
diff --git a/data/application-x-pcb-netlist.svg b/data/application-x-pcb-netlist.svg
new file mode 100644
index 0000000..8988f5f
--- /dev/null
+++ b/data/application-x-pcb-netlist.svg
@@ -0,0 +1,459 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90"
+   inkscape:export-xdpi="90"
+   inkscape:version="0.45.1"
+   sodipodi:version="0.32"
+   id="svg249"
+   height="128"
+   width="128"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   sodipodi:docname="application-x-pcb-netlist.svg"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons/gnomemime/pcb_netlist_ideas"
+   version="1.0">
+  <defs
+     id="defs3">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-3.277938e-2,-0.999463,0.999463,-3.277938e-2,-0.709646,45.06274)"
+       x1="33.396004"
+       y1="36.921333"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         id="stop15664"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15666"
+         offset="1.0000000"
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#fafafa;stop-opacity:1.0000000;" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient15656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient15658"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient15668"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient5350"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.331735,0,0,0.353831,20.10526,9.5823)"
+       cx="31.863327"
+       cy="2.3667307"
+       fx="31.863327"
+       fy="2.3667307"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient5352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.148355,1.009137e-2,-1.104438e-2,0.162365,25.06011,12.81706)"
+       cx="30.653816"
+       cy="14.9373"
+       fx="30.653816"
+       fy="14.9373"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(1.744653,0,0,1.283833,-26.58256,-3.478359)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3688"
+       inkscape:collect="always">
+      <stop
+         id="stop3690"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop3692"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3702">
+      <stop
+         id="stop3704"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop3710" />
+      <stop
+         id="stop3706"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2088"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3688"
+       id="radialGradient2090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)"
+       cx="4.9929786"
+       cy="43.5"
+       fx="4.9929786"
+       fy="43.5"
+       r="2.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3702"
+       id="linearGradient2092"
+       gradientUnits="userSpaceOnUse"
+       x1="25.058096"
+       y1="47.027729"
+       x2="25.058096"
+       y2="39.999443" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-y="52"
+     inkscape:window-x="289"
+     inkscape:window-height="967"
+     inkscape:window-width="1086"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showgrid="true"
+     inkscape:current-layer="layer5"
+     inkscape:cy="50.568876"
+     inkscape:cx="10.481007"
+     inkscape:zoom="4"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="0.25490196"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="false"
+     width="128px"
+     height="128px" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>PCB netlist</dc:title>
+        <dc:subject>
+          <rdf:Bag />
+        </dc:subject>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Peter Clifton, Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="Shadow" />
+  <g
+     style="display:inline"
+     inkscape:groupmode="layer"
+     inkscape:label="Base"
+     id="layer1" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Text"
+     style="display:inline">
+    <g
+       id="g2258"
+       transform="scale(2.6667,2.6667)">
+      <g
+         transform="translate(-2e-6,2.838692e-5)"
+         inkscape:label="Shadow"
+         id="g2033"
+         style="display:inline">
+        <g
+           transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)"
+           style="opacity:0.4"
+           id="g3712">
+          <rect
+             style="opacity:1;fill:url(#radialGradient2088);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect2801"
+             width="5"
+             height="7"
+             x="38"
+             y="40" />
+          <rect
+             style="opacity:1;fill:url(#radialGradient2090);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3696"
+             width="5"
+             height="7"
+             x="-10"
+             y="-47"
+             transform="scale(-1,-1)" />
+          <rect
+             style="opacity:1;fill:url(#linearGradient2092);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect3700"
+             width="28"
+             height="7.0000005"
+             x="10"
+             y="40" />
+        </g>
+      </g>
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15391"
+         d="M 7.7526014,3.6464462 L 31.199616,3.6308212 C 31.199616,3.6308212 41.478553,13.174533 41.478553,13.811106 L 41.478553,43.417892 C 41.478553,44.054465 40.966077,44.56694 40.329504,44.56694 L 7.7526014,44.56694 C 7.1160285,44.56694 6.6035528,44.054465 6.6035528,43.417892 L 6.6035528,4.7954948 C 6.6035528,4.1589219 7.1160285,3.6464462 7.7526014,3.6464462 z "
+         style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         sodipodi:nodetypes="ccccccccc"
+         id="rect15660"
+         d="M 7.8151023,4.5839462 L 32.691494,4.5839462 C 32.691494,4.5839462 40.44194,12.605373 40.44194,12.687946 L 40.44194,43.381282 C 40.44194,43.463855 40.375465,43.530331 40.292892,43.530331 L 7.8151023,43.530331 C 7.7325294,43.530331 7.6660538,43.463855 7.6660538,43.381282 L 7.6660538,4.7329948 C 7.6660538,4.6504219 7.7325294,4.5839462 7.8151023,4.5839462 z "
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <g
+         id="g3237"
+         style="display:inline">
+        <path
+           id="path2214"
+           d="M 10.5,9.4999998 L 18.634649,9.4999998"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2217"
+           d="M 13.518412,12.5 L 20.518411,12.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2219"
+           d="M 13.518412,14.5 L 20.518411,14.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path2221"
+           d="M 13.518412,16.5 L 20.518411,16.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M 13.768412,11.170415 L 13.768412,16.25"
+           id="path2225" />
+        <path
+           id="path3197"
+           d="M 23.518412,12.5 L 29.77733,12.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3199"
+           d="M 23.518412,14.5 L 31.964064,14.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3201"
+           d="M 23.461649,16.5 L 30.518412,16.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3205"
+           d="M 10.485328,20.5 L 17.5,20.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3207"
+           d="M 13.576087,23.5 L 20.576086,23.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3209"
+           d="M 13.576087,25.5 L 20.576086,25.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3211"
+           d="M 13.576087,27.5 L 20.576086,27.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 13.826087,22.170415 L 13.826087,27.25"
+           id="path3213" />
+        <path
+           id="path3215"
+           d="M 23.484131,23.5 L 28.591957,23.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3217"
+           d="M 23.548469,25.5 L 31.527618,25.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3219"
+           d="M 23.5,27.5 L 27.837971,27.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.0000006;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3221"
+           d="M 10.5,31.5 L 19.740165,31.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3223"
+           d="M 13.5,34.5 L 20.499999,34.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3225"
+           d="M 13.5,36.5 L 20.499999,36.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3227"
+           d="M 13.5,38.5 L 20.499999,38.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#729fcf;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+           d="M 13.75,33.170415 L 13.75,38.25"
+           id="path3229" />
+        <path
+           id="path3231"
+           d="M 23.412349,34.5 L 28.574064,34.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3233"
+           d="M 23.5,36.5 L 28.852639,36.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+        <path
+           id="path3235"
+           d="M 23.443237,38.5 L 30.5,38.5"
+           style="opacity:1;color:#000000;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+      </g>
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path5348"
+         d="M 40.985189,13.861445 C 40.256827,12.514817 34.882221,10.130934 32.084635,9.3314083 C 32.254143,10.904354 31.961856,15.649439 31.961856,15.649439 C 34.024356,14.274439 40.204485,13.699331 40.985189,13.861445 z "
+         style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         inkscape:r_cx="true"
+         inkscape:r_cy="true" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         sodipodi:nodetypes="cccc"
+         id="path2210"
+         d="M 41.410559,13.739267 C 41.423724,12.324125 35.058025,3.5320142 31.175441,3.6354934 C 32.148479,3.8684884 32.925796,9.803523 31.536076,12.616023 C 34.286076,12.616023 40.446694,11.881093 41.410559,13.739267 z "
+         style="opacity:1;color:#000000;fill:url(#radialGradient5352);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5350);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         inkscape:r_cy="true"
+         inkscape:r_cx="true"
+         style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+         d="M 39.121563,11.586207 C 38.393201,10.239579 34.963027,6.5166576 33.040441,5.2796316 C 33.279381,6.7054805 33.577496,8.9620596 32.961856,11.524439 C 32.961856,11.524439 38.340859,11.424093 39.121563,11.586207 z "
+         id="path2247"
+         sodipodi:nodetypes="cccc" />
+    </g>
+  </g>
+</svg>
diff --git a/data/icon-theme-installer b/data/icon-theme-installer
new file mode 100755
index 0000000..102463c
--- /dev/null
+++ b/data/icon-theme-installer
@@ -0,0 +1,183 @@
+#!/bin/sh
+
+# icon-theme-installer
+# Copyright (C) 2006 Novell, Inc.
+# Written by Aaron Bockover <abock at gnome.org>
+# Licensed under the MIT/X11 license
+#
+# Modified by Peter Clifton to allow icons with numerals in the filename
+#
+# This script is meant to be invoked from within a Makefile/Makefile.am
+# in the install-data-local and uninstall-data sections. It handles the
+# task of properly installing icons into the icon theme. It requires a
+# few arguments to set up its environment, and a list of files to be
+# installed. The format of the file list is critical:
+#
+# <category>,<local-src-file-name>
+#
+#   apps,music-player-banshee.svg
+#   apps,music-player-banshee-16.png
+#   apps,music-player-banshee-22.png
+#
+# <category> is the icon theme category, for instance, apps, devices,
+# actions, emblems...
+#
+# <local-src-file-name> must have a basename in the form of:
+#
+#   proper-theme-name[-<SIZE>].<EXTENSION>
+#
+# Where <SIZE> should be either nothing, which will default to scalable
+# or \-[0-9]{2}, which will expand to <SIZE>x<SIZE>. For example:
+#
+#   music-player-banshee-16.png
+#
+# The <SIZE> here is -16 and will expand to 16x16 per the icon theme spec
+#
+# What follows is an example Makefile.am for icon theme installation:
+#
+# ---------------
+# theme=hicolor
+# themedir=$(datadir)/icons/$(theme)
+# theme_icons = \
+#	apps,music-player-banshee.svg \
+#	apps,music-player-banshee-16.png \
+#	apps,music-player-banshee-22.png \
+#	apps,music-player-banshee-24.png \
+#	apps,music-player-banshee-32.png
+#
+# install_icon_exec = $(top_srcdir)/build/icon-theme-installer -t $(theme) -s $(srcdir) -d "x$(DESTDIR)" -b $(themedir) -m "$(mkinstalldirs)" -x "$(INSTALL_DATA)"
+# install-data-local:
+#	$(install_icon_exec) -i $(theme_icons)
+#
+#	uninstall-hook:
+#		$(install_icon_exec) -u $(theme_icons)
+#
+#	MAINTAINERCLEANFILES = Makefile.in
+#	EXTRA_DIST = $(wildcard *.svg *.png)
+# ---------------
+#
+# Arguments to this program:
+#
+# -i         : Install
+# -u         : Uninstall
+# -t <theme> : Theme name (hicolor)
+# -b <dir>   : Theme installation dest directory [x$(DESTDIR)] - Always prefix
+#              this argument with x; it will be stripped but will act as a
+#              placeholder for zero $DESTDIRs (only set by packagers)
+# -d <dir>   : Theme installation directory [$(hicolordir)]
+# -s <dir>   : Source directory [$(srcdir)]
+# -m <exec>  : Command to exec for directory creation [$(mkinstalldirs)]
+# -x <exec>  : Command to exec for single file installation [$(INSTALL_DATA)]
+# <remainging> : All remainging should be category,filename pairs
+
+while getopts "iut:b:d:s:m:x:" flag; do
+	case "$flag" in
+		i) INSTALL=yes ;;
+		u) UNINSTALL=yes ;;
+		t) THEME_NAME=$OPTARG ;;
+		d) INSTALL_DEST_DIR="`echo $OPTARG | sed 's;^x;;'`" ;;
+		b) INSTALL_BASE_DIR=$OPTARG ;;
+		s) SRC_DIR=$OPTARG ;;
+		m) MKINSTALLDIRS_EXEC=$OPTARG ;;
+		x) INSTALL_DATA_EXEC=$OPTARG ;;
+	esac
+done
+
+shift `expr $OPTIND - 1`
+
+if test "x$INSTALL" = "xyes" -a "x$UNINSTALL" = "xyes"; then
+	echo "Cannot pass both -i and -u"
+	exit 1
+elif test "x$INSTALL" = "x" -a "x$UNINSTALL" = "x"; then
+	echo "Must path either -i or -u"
+	exit 1
+fi
+
+if test -z "$THEME_NAME"; then
+	echo "Theme name required (-t hicolor)"
+	exit 1
+fi
+
+if test -z "$INSTALL_BASE_DIR"; then
+	echo "Base theme directory required [-d \$(hicolordir)]"
+	exit 1
+fi
+
+#if test ! -x `echo "$MKINSTALLDIRS_EXEC" | cut -f1 -d' '`; then
+#	echo "Cannot find '$MKINSTALLDIRS_EXEC'; You probably want to pass -m \$(mkinstalldirs)"
+#	exit 1
+#fi
+
+#if test ! -x `echo "$INSTALL_DATA_EXEC" | cut -f1 -d' '`; then
+#	echo "Cannot find '$INSTALL_DATA_EXEC'; You probably want to pass -x \$(INSTALL_DATA)"
+#	exit 1
+#fi
+
+if test -z "$SRC_DIR"; then
+	SRC_DIR=.
+fi
+
+for icon in $@; do
+	size=`echo $icon | sed -n 's/.*-\([0-9]*\).*/\1/p'`
+	category=`echo $icon | cut -d, -f1`
+	build_name=`echo $icon | cut -d, -f2`
+	install_name=`echo $build_name | sed 's/-[0-9]\+//g'`
+	install_name=`basename $install_name`
+
+	if test -z $size; then 
+		size=scalable;
+	else
+		size=${size}x${size};
+	fi
+	
+	install_dir=${INSTALL_DEST_DIR}${INSTALL_BASE_DIR}/$size/$category
+	install_path=$install_dir/$install_name
+	
+	if test "x$INSTALL" = "xyes"; then
+		echo "Installing $size $install_name into $THEME_NAME icon theme"
+		
+		$MKINSTALLDIRS_EXEC $install_dir || {
+			echo "Failed to create directory $install_dir"
+			exit 1
+		}
+		
+		$INSTALL_DATA_EXEC $SRC_DIR/$build_name $install_path || {
+			echo "Failed to install $SRC_DIR/$build_name into $install_path"
+			exit 1
+		}
+
+		if test ! -e $install_path; then
+			echo "Failed to install $SRC_DIR/$build_name into $install_path"
+			exit 1
+		fi
+	else
+		if test -e $install_path; then
+			echo "Removing $size $install_name from $THEME_NAME icon theme"
+
+			rm $install_path || { 
+				echo "Failed to remove $install_path"
+				exit 1
+			}
+		fi
+	fi
+done
+
+if test "x$INSTALL" = "xyes"; then
+	gtk_update_icon_cache_bin="`(which gtk-update-icon-cache || echo /opt/gnome/bin/gtk-update-icon-cache)2>/dev/null`"
+	gtk_update_icon_cache_bin="${GTK_UPDATE_ICON_CACHE_BIN:-$gtk_update_icon_cache_bin}"
+
+	gtk_update_icon_cache="$gtk_update_icon_cache_bin -f -t $INSTALL_BASE_DIR"
+
+	if test -z "$INSTALL_DEST_DIR"; then 
+		if test -x $gtk_update_icon_cache_bin; then 
+			echo "Updating GTK icon cache"
+			$gtk_update_icon_cache
+		else
+			echo "*** Icon cache not updated. Could not execute $gtk_update_icon_cache_bin"
+		fi
+	else
+		echo "*** Icon cache not updated. After install, run this:"
+		echo "***   $gtk_update_icon_cache"
+	fi
+fi
+
diff --git a/data/pcb-48.png b/data/pcb-48.png
new file mode 100644
index 0000000..bcdb353
Binary files /dev/null and b/data/pcb-48.png differ
diff --git a/data/pcb.desktop b/data/pcb.desktop
new file mode 100644
index 0000000..729ea2b
--- /dev/null
+++ b/data/pcb.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Version=1.0
+Name=PCB Designer
+GenericName=PCB Design
+Comment=Create and edit printed circuit board designs
+Type=Application
+Exec=pcb %f
+Icon=pcb
+MimeType=application/x-pcb-layout;application/x-pcb-footprint;
+Categories=Engineering;Electronics;
diff --git a/data/pcb.svg b/data/pcb.svg
new file mode 100644
index 0000000..2e6881a
--- /dev/null
+++ b/data/pcb.svg
@@ -0,0 +1,1070 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48"
+   height="48"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docbase="/home/pcjc2/gedawork/tomazicons"
+   sodipodi:docname="pcb.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6445">
+      <stop
+         style="stop-color:#fcaf3e;stop-opacity:1"
+         offset="0"
+         id="stop6447" />
+      <stop
+         style="stop-color:#ce5c00"
+         offset="1"
+         id="stop6449" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6437">
+      <stop
+         style="stop-color:#e9b96e;stop-opacity:1"
+         offset="0"
+         id="stop6439" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="1"
+         id="stop6441" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4700">
+      <stop
+         id="stop4702"
+         offset="0"
+         style="stop-color:#32362e;stop-opacity:1;" />
+      <stop
+         id="stop4704"
+         offset="1"
+         style="stop-color:#376c04;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4682">
+      <stop
+         style="stop-color:white;stop-opacity:1;"
+         offset="0"
+         id="stop4684" />
+      <stop
+         style="stop-color:white;stop-opacity:0;"
+         offset="1"
+         id="stop4686" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4333">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop4335" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop4337" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3415">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop3417" />
+      <stop
+         id="stop3423"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop3419" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3399">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop3401" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop3403" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2919">
+      <stop
+         style="stop-color:#3f7b04;stop-opacity:1;"
+         offset="0"
+         id="stop2921" />
+      <stop
+         style="stop-color:#d3d7cf;stop-opacity:0;"
+         offset="1"
+         id="stop2923" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2301"
+       inkscape:collect="always">
+      <stop
+         id="stop2303"
+         offset="0"
+         style="stop-color:#ce5c00;stop-opacity:1" />
+      <stop
+         id="stop2305"
+         offset="1"
+         style="stop-color:#ce5c00" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1813"
+       inkscape:collect="always">
+      <stop
+         id="stop1815"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop1817"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3399"
+       id="radialGradient3439"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,2.166667,0,-46.08333)"
+       cx="6"
+       cy="39.5"
+       fx="6"
+       fy="39.5"
+       r="3" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3399"
+       id="radialGradient3441"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,2.166667,-45,-125.0833)"
+       cx="6"
+       cy="39.5"
+       fx="6"
+       fy="39.5"
+       r="3" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3415"
+       id="linearGradient3443"
+       gradientUnits="userSpaceOnUse"
+       x1="18"
+       y1="46"
+       x2="18"
+       y2="32.999397" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4333"
+       id="radialGradient3445"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.226496,0,25.67067)"
+       cx="37.375"
+       cy="33.1875"
+       fx="37.375"
+       fy="33.1875"
+       r="14.625" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4682"
+       id="linearGradient3449"
+       gradientUnits="userSpaceOnUse"
+       x1="6.7928934"
+       y1="32.963203"
+       x2="7.9215727"
+       y2="54.448856" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1813"
+       id="linearGradient3451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.415008,6.5485,26.7669)"
+       x1="18.1875"
+       y1="18.53828"
+       x2="32.9375"
+       y2="18.392296" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6445"
+       id="linearGradient3453"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(6.39225,12.18459)"
+       x1="28.078697"
+       y1="12.338078"
+       x2="30.432114"
+       y2="14.691495" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2301"
+       id="linearGradient3455"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(6.39225,12.18459)"
+       x1="23.447809"
+       y1="21.481258"
+       x2="22.810215"
+       y2="22.118853" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6437"
+       id="linearGradient3457"
+       gradientUnits="userSpaceOnUse"
+       x1="26.379272"
+       y1="34.389839"
+       x2="25.485056"
+       y2="32.714375" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2919"
+       id="linearGradient4449"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.489708,-24.78509,-59.695126)"
+       x1="11.949747"
+       y1="40.664974"
+       x2="94.364037"
+       y2="47.897068" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4700"
+       id="linearGradient4568"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-22.78509,-37.142866)"
+       x1="5.6568546"
+       y1="53.320892"
+       x2="4"
+       y2="16.003418" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#afafaf"
+     borderopacity="1"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4142135"
+     inkscape:cx="217.79117"
+     inkscape:cy="8.6973454"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="48px"
+     height="48px"
+     gridspacingx="0.5px"
+     gridspacingy="0.5px"
+     inkscape:showpageshadow="true"
+     borderlayer="true"
+     gridempspacing="2"
+     inkscape:window-width="1051"
+     inkscape:window-height="797"
+     inkscape:window-x="57"
+     inkscape:window-y="79"
+     showgrid="true"
+     showborder="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:grid-points="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Lapo Calamandrei</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+        <dc:title>Text editor</dc:title>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Livello 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(23.785088,38.119518)">
+    <g
+       id="g2637"
+       inkscape:label="base"
+       style="display:inline"
+       transform="translate(60.78337,49.43458)" />
+    <path
+       style="fill:#4e9a06;fill-opacity:1;stroke:url(#linearGradient4568);stroke-width:0.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M -14.51732,-26.619518 L 13.94712,-26.619518 C 14.92646,-26.619518 15.64391,-25.866843 15.71489,-24.851751 L 17.65239,2.857134 L 17.71491,6.357134 L -18.28509,6.357134 L -18.28509,2.857134 L -16.28509,-24.851751 C -16.21502,-25.822602 -15.49667,-26.619518 -14.51732,-26.619518 z "
+       id="path2997"
+       sodipodi:nodetypes="cczcccczc" />
+    <g
+       id="g5565"
+       transform="translate(-16.097588,-83.182018)">
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5567"
+         d="M 10,73 L 10,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5569"
+         d="M 13,73 L 13,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5571"
+         d="M 16,73 L 16,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5573"
+         d="M 19,73 L 19,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5575"
+         d="M 10,70 L 10,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5577"
+         d="M 13,70 L 13,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5579"
+         d="M 16,70 L 16,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5581"
+         d="M 19,70 L 19,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5583"
+         d="M 10,67 L 10,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5585"
+         d="M 13,67 L 13,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5587"
+         d="M 16,67 L 16,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5589"
+         d="M 19,67 L 19,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5591"
+         d="M 10,64 L 10,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5593"
+         d="M 13,64 L 13,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5595"
+         d="M 16,64 L 16,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5597"
+         d="M 19,64 L 19,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5599"
+         d="M 22,73 L 22,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5601"
+         d="M 25,73 L 25,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5603"
+         d="M 28,73 L 28,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5605"
+         d="M 31,73 L 31,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5607"
+         d="M 22,70 L 22,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5609"
+         d="M 25,70 L 25,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5611"
+         d="M 28,70 L 28,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5613"
+         d="M 31,70 L 31,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5615"
+         d="M 22,67 L 22,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5617"
+         d="M 25,67 L 25,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5619"
+         d="M 28,67 L 28,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5621"
+         d="M 31,67 L 31,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5623"
+         d="M 22,64 L 22,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5625"
+         d="M 25,64 L 25,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5627"
+         d="M 28,64 L 28,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5629"
+         d="M 31,64 L 31,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5631"
+         d="M 10,61 L 10,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5633"
+         d="M 13,61 L 13,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5635"
+         d="M 16,61 L 16,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5637"
+         d="M 19,61 L 19,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5639"
+         d="M 10,58 L 10,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5641"
+         d="M 13,58 L 13,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5643"
+         d="M 16,58 L 16,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5645"
+         d="M 19,58 L 19,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5647"
+         d="M 22,61 L 22,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5649"
+         d="M 25,61 L 25,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5651"
+         d="M 28,61 L 28,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5653"
+         d="M 31,61 L 31,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5655"
+         d="M 22,58 L 22,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5657"
+         d="M 25,58 L 25,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5659"
+         d="M 28,58 L 28,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5661"
+         d="M 10,85 L 10,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5663"
+         d="M 13,85 L 13,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5665"
+         d="M 16,85 L 16,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5667"
+         d="M 19,85 L 19,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5669"
+         d="M 10,82 L 10,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5671"
+         d="M 13,82 L 13,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5673"
+         d="M 16,82 L 16,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5675"
+         d="M 19,82 L 19,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5677"
+         d="M 10,79 L 10,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5679"
+         d="M 13,79 L 13,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5681"
+         d="M 16,79 L 16,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5683"
+         d="M 19,79 L 19,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5685"
+         d="M 10,76 L 10,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5687"
+         d="M 13,76 L 13,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5689"
+         d="M 16,76 L 16,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5691"
+         d="M 19,76 L 19,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5693"
+         d="M 22,85 L 22,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5695"
+         d="M 25,85 L 25,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5697"
+         d="M 28,85 L 28,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5699"
+         d="M 31,85 L 31,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5701"
+         d="M 22,82 L 22,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5703"
+         d="M 25,82 L 25,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5705"
+         d="M 28,82 L 28,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5707"
+         d="M 31,82 L 31,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5709"
+         d="M 22,79 L 22,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5711"
+         d="M 25,79 L 25,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5713"
+         d="M 28,79 L 28,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5715"
+         d="M 31,79 L 31,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5717"
+         d="M 22,76 L 22,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5719"
+         d="M 25,76 L 25,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5721"
+         d="M 28,76 L 28,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5723"
+         d="M 31,76 L 31,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5725"
+         d="M 4,73 L 4,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5727"
+         d="M 7,73 L 7,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5729"
+         d="M 4,70 L 4,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5731"
+         d="M 7,70 L 7,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5733"
+         d="M 4,67 L 4,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5735"
+         d="M 7,67 L 7,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5737"
+         d="M 4,64 L 4,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5739"
+         d="M 7,64 L 7,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5741"
+         d="M 4,61 L 4,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5743"
+         d="M 7,61 L 7,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5745"
+         d="M 4,58 L 4,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5747"
+         d="M 7,58 L 7,58"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5749"
+         d="M 4,85 L 4,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5751"
+         d="M 7,85 L 7,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5753"
+         d="M 4,82 L 4,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5755"
+         d="M 7,82 L 7,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5757"
+         d="M 4,79 L 4,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5759"
+         d="M 7,79 L 7,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5761"
+         d="M 4,76 L 4,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5763"
+         d="M 7,76 L 7,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5765"
+         d="M 1,73 L 1,73"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5767"
+         d="M 1,70 L 1,70"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5769"
+         d="M 1,67 L 1,67"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5771"
+         d="M 1,64 L 1,64"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5773"
+         d="M 1,61 L 1,61"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5775"
+         d="M 1,85 L 1,85"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5777"
+         d="M 1,82 L 1,82"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5779"
+         d="M 1,79 L 1,79"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path5781"
+         d="M 1,76 L 1,76"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#316400;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+    </g>
+    <g
+       id="g5792"
+       transform="translate(-4,48.676777)">
+      <path
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M -7.911735,-72.09947 L -4.2637154,-72.144203 L -0.98581343,-68.498826 L -1.726049,-61.118607"
+         id="path2828"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 1.1923678,-61.154394 L 2.0806498,-70.010656 L 0.1139084,-72.197882"
+         id="path2830" />
+      <path
+         style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 4.1107838,-61.19018 L 5.1471128,-71.522486 L 4.4915318,-72.251562"
+         id="path2832" />
+      <path
+         style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 7.0291988,-61.225967 L 8.1395518,-72.296295"
+         id="path2890" />
+      <path
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M -2.5403079,-53.000368 L -2.8364022,-50.04828 L -5.2472848,-47.807374 L -10.354512,-47.744748"
+         id="path2910" />
+      <path
+         style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 0.3781084,-53.036154 L -0.1400566,-47.87"
+         id="path2912" />
+      <path
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 3.2965248,-53.07194 L 3.0004298,-50.119853 L 4.9671708,-47.932626 L 9.344794,-47.986306"
+         id="path2914" />
+      <path
+         style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M 6.2149398,-53.107727 L 7.5260988,-51.649575 L 9.714912,-51.676415"
+         id="path2916" />
+      <path
+         style="color:#000000;fill:#5fc500;fill-opacity:1;fill-rule:nonzero;stroke:#5fc500;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         d="M -8.1338058,-69.885404 L -5.2153901,-69.92119 L -3.248649,-67.733963 L -4.8771671,-51.497483 L -6.484422,-50.003547 L -10.132442,-49.958814 L -8.1338058,-69.885404 z "
+         id="path2918"
+         sodipodi:nodetypes="ccccccc" />
+      <rect
+         transform="matrix(0.9999248,-1.2261314e-2,-9.9799219e-2,0.9950076,0,0)"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.00316036;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+         id="rect2920"
+         width="13.133858"
+         height="12.609322"
+         x="-10.055484"
+         y="-63.747379"
+         ry="1.9667948" />
+      <path
+         id="path2894"
+         d="M -1.726049,-61.118607 L -1.8740961,-59.642563"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2896"
+         d="M 1.1923678,-61.154394 L 1.0443198,-59.67835"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2898"
+         d="M 4.1107838,-61.19018 L 3.9627358,-59.714137"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2900"
+         d="M 7.0291988,-61.225967 L 6.8811508,-59.749923"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2902"
+         d="M 6.3629868,-54.58377 L 6.2149398,-53.107727"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2904"
+         d="M 3.4445718,-54.547983 L 3.2965248,-53.07194"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2906"
+         d="M 0.5261554,-54.512197 L 0.3781084,-53.036154"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+      <path
+         id="path2908"
+         d="M -2.3922609,-54.476411 L -2.5403079,-53.000368"
+         style="color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
+    </g>
+    <g
+       id="g2999"
+       transform="matrix(1.0416677,0,0,0.4579337,-23.72259,-12.731239)"
+       style="opacity:0.4">
+      <rect
+         y="33"
+         x="3"
+         height="13"
+         width="3"
+         id="rect3001"
+         style="opacity:1;fill:url(#radialGradient3439);fill-opacity:1;stroke:none;stroke-width:1.00199997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+      <rect
+         transform="scale(-1,-1)"
+         y="-46"
+         x="-42"
+         height="13"
+         width="3"
+         id="rect3003"
+         style="opacity:1;fill:url(#radialGradient3441);fill-opacity:1;stroke:none;stroke-width:1.00199997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+      <rect
+         y="33"
+         x="6"
+         height="13"
+         width="33"
+         id="rect3005"
+         style="opacity:1;fill:url(#linearGradient3443);fill-opacity:1;stroke:none;stroke-width:1.00199997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+    </g>
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.07000002;fill:url(#radialGradient3445);fill-opacity:1;stroke:none;stroke-width:1.00199997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1"
+       id="path3007"
+       sodipodi:cx="37.375"
+       sodipodi:cy="33.1875"
+       sodipodi:rx="14.625"
+       sodipodi:ry="3.3125"
+       d="M 52 33.1875 A 14.625 3.3125 0 1 1  22.75,33.1875 A 14.625 3.3125 0 1 1  52 33.1875 z"
+       transform="matrix(0.897436,0,0,0.981132,-21.45176,-35.454187)" />
+    <rect
+       style="fill:url(#linearGradient4449);fill-opacity:1;stroke:none;stroke-width:1.00199997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1"
+       id="rect3009"
+       width="35"
+       height="3.0103984"
+       x="-17.785091"
+       y="2.8571317" />
+    <path
+       sodipodi:type="inkscape:offset"
+       inkscape:radius="-0.9722718"
+       inkscape:original="M 8.28125 10.53125 C 7.301907 10.53125 6.570075 11.310399 6.5 12.28125 L 4.5 40 L 4.5 43.5 L 40.5 43.5 L 40.4375 40 L 38.5 12.28125 C 38.429021 11.266158 37.698095 10.53125 36.71875 10.53125 L 8.28125 10.53125 z "
+       style="fill:none;fill-opacity:1;stroke:url(#linearGradient3449);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="path3011"
+       d="M 8.28125,11.5 C 7.8175892,11.5 7.5065211,11.820452 7.46875,12.34375 L 5.46875,40 L 5.46875,40.0625 L 5.46875,42.53125 L 39.5,42.53125 L 39.46875,40.0625 L 39.46875,40.03125 L 37.53125,12.34375 C 37.490391,11.75941 37.211695,11.5 36.71875,11.5 L 8.28125,11.5 z "
+       transform="translate(-22.78509,-37.142866)" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#c17d11;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+       d="M -18.28509,6.357134 L 17.71491,6.357134"
+       id="path3013"
+       sodipodi:nodetypes="cc" />
+    <g
+       id="g3047"
+       transform="translate(-22.78509,-37.142866)">
+      <path
+         id="path3049"
+         d="M 41.07975,28.46584 L 26.07975,32.938753 L 25.95475,33.003598 L 25.861,33.081412 L 23.111,35.93459 L 30.5485,35.091606 L 30.82975,35.065668 L 31.01725,34.987854 L 41.07975,32.304661 L 41.07975,28.46584 z "
+         style="opacity:0.4;fill:url(#linearGradient3451);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+         sodipodi:nodetypes="cccccccccc" />
+      <g
+         id="g3051">
+        <g
+           id="g3053">
+          <path
+             style="fill:url(#linearGradient3453);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3455);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             d="M 25.89225,30.18459 L 42.89225,13.18459 C 45.067299,13.544551 45.976969,14.916815 46.39225,16.68459 L 29.39225,33.68459 L 24.776133,34.389095 L 25.89225,30.18459 z "
+             id="path3055"
+             sodipodi:nodetypes="cccccc" />
+          <path
+             style="opacity:0.28235294;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.0000006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+             d="M 26.792248,30.68459 L 43.289998,14.286842 C 44.379743,14.465277 44.807259,15.274786 45.289998,16.286842 L 28.89225,32.784592 L 25.592247,33.684593 L 26.792248,30.68459 z "
+             id="path3057"
+             sodipodi:nodetypes="cccccc" />
+        </g>
+        <g
+           id="g3059">
+          <path
+             style="fill:url(#linearGradient3457);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.31200001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             d="M 24.549577,34.633026 L 26.215899,30.452717 C 26.215899,30.452717 27.415434,30.698077 28.148076,31.427806 C 28.880718,32.157535 29.146467,33.371634 29.146467,33.371634 L 24.549577,34.633026 z "
+             id="path3061"
+             sodipodi:nodetypes="cczcc" />
+          <path
+             style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#e9b96e;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             d="M 23,21.5 L 17.5,23 L 19.5,18"
+             id="path3063"
+             sodipodi:nodetypes="ccc"
+             transform="translate(6.39225,12.18459)" />
+          <path
+             sodipodi:nodetypes="cccsc"
+             style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+             d="M 23.95475,33.68459 L 23.0485,35.93459 L 25.39225,35.27834 C 25.39427,35.246503 25.39225,35.216934 25.39225,35.18459 C 25.39225,34.382465 24.746942,33.724789 23.95475,33.68459 z "
+             id="path3065" />
+        </g>
+      </g>
+    </g>
+    <g
+       id="layer6"
+       inkscape:label="Shadow"
+       transform="translate(29.464908,-40.795033)" />
+    <g
+       id="layer5"
+       inkscape:label="Text"
+       style="display:inline"
+       transform="translate(29.464908,-40.795033)" />
+  </g>
+</svg>
diff --git a/data/pcb.xml b/data/pcb.xml
new file mode 100644
index 0000000..33bd895
--- /dev/null
+++ b/data/pcb.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+  <mime-type type="application/x-pcb-layout">
+    <sub-class-of type="text/plain"/>
+    <comment>PCB layout</comment>
+    <glob pattern="*.pcb"/>
+    <magic priority="50">
+      <match value="# release: pcb " type="string" offset="0"/>
+    </magic>
+  </mime-type>
+  <mime-type type="application/x-pcb-footprint">
+    <sub-class-of type="text/plain"/>
+    <comment>PCB footprint</comment>
+    <glob pattern="*.fp"/>
+    <magic priority="50">
+      <match value="Element[" type="string" offset="0:20"/>
+    </magic>
+  </mime-type>
+  <mime-type type="application/x-pcb-netlist">
+    <sub-class-of type="text/plain"/>
+    <comment>PCB netlist</comment>
+    <glob pattern="*.net"/>
+  </mime-type>
+  <mime-type type="application/x-gerber">
+    <sub-class-of type="text/plain"/>
+    <comment>Gerber file</comment>
+    <glob pattern="*.gbr"/>
+    <magic priority="50">
+      <match value="G04" type="string" offset="0"/>
+    </magic>
+  </mime-type>
+  <mime-type type="application/x-excellon">
+    <sub-class-of type="text/plain"/>
+    <comment>Excellon drill file</comment>
+    <glob pattern="*.cnc"/>
+    <magic priority="50">
+      <match value="M48" type="string" offset="0"/>
+    </magic>
+  </mime-type>
+</mime-info>
diff --git a/data/pcb_icon.ico b/data/pcb_icon.ico
new file mode 100644
index 0000000..48b9135
Binary files /dev/null and b/data/pcb_icon.ico differ
diff --git a/data/regen_files b/data/regen_files
new file mode 100755
index 0000000..55dd6d8
--- /dev/null
+++ b/data/regen_files
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+
+CONVERT=${CONVERT:-convert}
+COMPOSITE=${COMPOSITE:-composite}
+INKSCAPE=${INKSCAPE:-inkscape}
+PPMTOWINICON=${PPMTOWINICON:-ppmtowinicon}
+
+do_inkscape=yes
+do_convert=yes
+do_winicon=yes
+
+usage() {
+cat << EOF
+
+$0 -- Regenerate desktop icon files and windows icon files
+
+Options
+
+  --help          Displays this message and exits
+
+  --skip-png      Skips the regeneration of the .png file(s)
+
+  --skip-winicon  Skips the regneration of the Windows icon file(s)
+
+EOF
+
+}
+
+while test $# -ne 0 ; do
+	case $1 in
+		--help)
+			usage
+			exit 0
+			;;
+
+		--skip-png)
+			do_inkscape=no
+			shift
+			;;
+
+		--skip-winicon)
+			do_convert=no
+			do_winicon=no
+			shift
+			;;
+
+		-*)
+			echo "$0:  Unknown option $1"
+			usage
+			exit 1
+			;;
+
+		*)
+			break
+			;;
+	esac
+done
+
+if test $? -ne 0 ; then
+	usage
+	exit 1
+fi
+
+##
+## Export the SVG graphics
+##
+
+# see if we have inkscape
+if test $do_inkscape = yes ; then
+${INKSCAPE} --version 2>&1 >/dev/null
+if test $? -ne 0 ; then
+	echo "\"${INKSCAPE} --version\" failed."
+	echo "Make sure that inkscape is installed and functional on your system."
+	echo "Skipping the SVG -> PNG conversion."
+	do_inkscape=no
+fi
+fi
+
+if test $do_inkscape = yes ; then
+	echo "Export SVG graphics to png..."
+
+	for r in 16 22 24 32 48 ; do
+		case ${r} in 
+			24)
+				x=-1
+				y=23
+				rs=22
+				;;
+			*)
+				x=0
+				y=${r}
+				rs=${r}
+				;;
+		esac
+		for f in *-${rs}.svg ; do
+			fb=`basename ${f} ${rs}.svg`
+			p="${fb}${r}.png"
+			echo "${f} -> ${p}"
+			${INKSCAPE} --export-png=${p} --export-area=${x}:${x}:${y}:${y} ${f}
+		done
+	done
+fi
+
+##
+## Generate the windows icon file
+##
+
+app_icon="application-x-pcb-layout"
+
+if test $do_convert = yes ; then
+# see if we have ImageMagick
+${CONVERT} --version 2>&1 >/dev/null
+if test $? -ne 0 ; then
+	echo "\"${CONVERT} --version\" failed."
+	echo "Make sure that ImageMagick is installed and functional on your system."
+	echo "Skipping the PNG -> PPM conversion."
+	do_convert=no
+fi
+fi
+
+if test $do_convert = yes ; then
+echo "Creating windows pbm mask files..."
+${CONVERT} -channel matte -separate +matte ${app_icon}-48.png - |
+  ${CONVERT} -threshold 65534 -negate - 48_mask.pbm
+${CONVERT} -channel matte -separate +matte ${app_icon}-32.png - |
+  ${CONVERT} -threshold 65534 -negate - 32_mask.pbm
+${CONVERT} -channel matte -separate +matte ${app_icon}-16.png - |
+  ${CONVERT} -threshold 65534 -negate - 16_mask.pbm
+
+echo "Creating windows ppm flattened files..."
+${CONVERT} -flatten -colors 16 ${app_icon}-48.png 48_16.ppm
+${CONVERT} -flatten -colors 256 ${app_icon}-48.png 48_256.ppm
+${CONVERT} -flatten -colors 16 ${app_icon}-32.png 32_16.ppm
+${CONVERT} -flatten -colors 256 ${app_icon}-32.png 32_256.ppm
+${CONVERT} -flatten -colors 16 ${app_icon}-16.png 16_16.ppm
+${CONVERT} -flatten -colors 256 ${app_icon}-16.png 16_256.ppm
+fi
+
+# see if we have netpbm
+if test $do_winicon = yes ; then
+${PPMTOWINICON} --version 2>&1 >/dev/null
+if test $? -ne 0 ; then
+	echo "\"${PPMTOWINICON} --version\" failed."
+	echo "Make sure that netpbm is installed and functional on your system."
+	echo "Skipping the pbm -> windows icon conversion."
+	do_winicon=no
+fi
+fi
+
+if test $do_winicon = yes ; then
+echo "Creating windows icon file..."
+${PPMTOWINICON} -output pcb_icon.ico -andpgms\
+    48_16.ppm 48_mask.pbm 48_256.ppm 48_mask.pbm\
+    32_16.ppm 32_mask.pbm 32_256.ppm 32_mask.pbm\
+    16_16.ppm 16_mask.pbm 16_256.ppm 16_mask.pbm
+fi
+
+rm -f \
+    48_16.ppm 48_256.ppm 48_mask.pbm\
+    32_16.ppm 32_256.ppm 32_mask.pbm\
+    16_16.ppm 16_256.ppm 16_mask.pbm
+
+echo "All done"
+
diff --git a/data/x-excellon.desktop b/data/x-excellon.desktop
new file mode 100644
index 0000000..9b2ae8c
--- /dev/null
+++ b/data/x-excellon.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Comment=Excellon drill file
+MimeType=application/x-excellon
+Type=MimeType
+Icon=application-x-excellon
+Patterns=*.cnc
+X-KDE-IsAlso=text/plain
diff --git a/data/x-gerber.desktop b/data/x-gerber.desktop
new file mode 100644
index 0000000..b63c1c5
--- /dev/null
+++ b/data/x-gerber.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Comment=Gerber file
+MimeType=application/x-gerber
+Type=MimeType
+Icon=application-x-gerber
+Patterns=*.gbr
+X-KDE-IsAlso=text/plain
diff --git a/data/x-pcb-footprint.desktop b/data/x-pcb-footprint.desktop
new file mode 100644
index 0000000..c5a175e
--- /dev/null
+++ b/data/x-pcb-footprint.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Comment=PCB footprint
+MimeType=application/x-pcb-footprint
+Type=MimeType
+Icon=application-x-pcb-footprint
+Patterns=*.fp
+X-KDE-IsAlso=text/plain
diff --git a/data/x-pcb-layout.desktop b/data/x-pcb-layout.desktop
new file mode 100644
index 0000000..5d12eb9
--- /dev/null
+++ b/data/x-pcb-layout.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Comment=PCB layout
+MimeType=application/x-pcb-layout
+Type=MimeType
+Icon=application-x-pcb-layout
+Patterns=*.pcb
+X-KDE-IsAlso=text/plain
diff --git a/data/x-pcb-netlist.desktop b/data/x-pcb-netlist.desktop
new file mode 100644
index 0000000..06e0610
--- /dev/null
+++ b/data/x-pcb-netlist.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Comment=PCB netlist
+MimeType=application/x-pcb-netlist
+Type=MimeType
+Icon=application-x-pcb-netlist
+Patterns=*.net
+X-KDE-IsAlso=text/plain
diff --git a/doc-orig/Makefile b/doc-orig/Makefile
new file mode 100644
index 0000000..7f40fae
--- /dev/null
+++ b/doc-orig/Makefile
@@ -0,0 +1,154 @@
+## -*- makefile -*-
+##
+##                            COPYRIGHT
+##
+##  PCB, interactive printed circuit board design
+##  Copyright (C) 1994,1995,1996 Thomas Nau
+##
+##  This program is free software; you can redistribute it and/or modify
+##  it under the terms of the GNU General Public License as published by
+##  the Free Software Foundation; either version 2 of the License, or
+##  (at your option) any later version.
+##
+##  This program is distributed in the hope that it will be useful,
+##  but WITHOUT ANY WARRANTY; without even the implied warranty of
+##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+##  GNU General Public License for more details.
+##
+##  You should have received a copy of the GNU General Public License
+##  along with this program; if not, write to the Free Software
+##  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##
+##  Contact addresses for paper mail and Email:
+##  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+##  Thomas.Nau at rz.uni-ulm.de
+##
+
+## not until it is fully fixed up in terms of building
+##SUBDIRS=	gs
+
+info_TEXINFOS=	pcb.texi
+pcb_TEXINFOS=	${inline_texi} ${pcb_output} ${tab_texi} ${images_output}
+
+dist_html_DATA=	${html_docs} ${pcb_output_inst} ${images_output_inst}
+
+dist_man_MANS=	pcb.1
+
+html_docs=	${info_TEXINFOS:.texi=.html}
+
+DVIS=
+
+# put the html manual into 1 file instead of multiple files?
+AM_MAKEINFOHTMLFLAGS=	--css-include=pcb.css --no-split
+
+EXTRA_DIST= \
+	ascii2texi.awk \
+	eps2png \
+	extract-docs \
+	refcard.tex \
+	pcb.css \
+	${inline_texi} \
+	${pcb_files} \
+	${pcb_output_noinst} \
+	${tab_files} \
+	${tab_texi} \
+	${images}
+
+all:
+	@echo "***"
+	@echo "*** Compiling the old docs is disabled. We keep last compilation as static doc to edit until it is replaced with the new docs. ***"
+	@echo "***"
+	@false
+
+old_all: $(html_docs)
+
+# use this to avoid having the clean target delete pcb.{dvi,html,pdf,ps}
+# which unfortunately deletes files which are part of the distfile
+clean:
+	-rm -rf pcb.aux pcb.cp pcb.cps pcb.fn pcb.fns pcb.ky pcb.kys pcb.log pcb.pg \
+	pcb.pgs pcb.tmp pcb.toc pcb.tp pcb.tps pcb.vr pcb.vrs 
+
+
+BUILT_SOURCES=	${tab_texi} ${pcb_output} ${inline_texi}
+CLEANFILES= \
+	refcard.aux refcard.log refcard.pdf \
+	${pcb_output} ${tab_texi} ${inline_texi} pcb.html \
+	${images_output}
+
+inline_texi=	\
+	options.texi \
+	actions.texi \
+	pcbfile.texi
+
+ASCII2TEXI=	${AWK} -f $(srcdir)/ascii2texi.awk ncol=3
+
+# Tables
+tab_files=	\
+	fractional_size.tab \
+	letter_size.tab \
+	metric_size.tab \
+	wire_size.tab
+tab_texi=	${tab_files:.tab=.texi}
+
+# PCB Drawings
+pcb_files=	\
+	pad.pcb \
+	puller.pcb \
+	thermal.pcb \
+	gcode.pcb
+
+pcb_output_noinst=	\
+	${pcb_files:.pcb=.pdf}
+
+pcb_output_inst=	\
+	${pcb_files:.pcb=.png}
+
+pcb_output=	${pcb_output_inst} ${pcb_output_noinst}
+
+# Additional images
+images= \
+	gcode_control_img.eps \
+	gcode_tool_path.eps
+
+images_output_noinst= \
+	${images:.eps=.pdf}
+
+images_output_inst= \
+	${images:.eps=.png}
+
+images_output= ${images_output_noinst} ${images_output_inst}
+
+${inline_texi} : extracted-docs
+
+# no need to build these when building via a tarball.  They're not used then
+# anyway.
+.PHONY : extracted-docs
+extracted-docs :
+	${PERL} $(srcdir)/extract-docs $(srcdir)
+
+SUFFIXES = .eps .pcb .pdf .png .tab .tex .html .texi
+
+.pcb.eps :
+	${PCB} -x eps --only-visible --font-path $(top_srcdir)/src --eps-file $@ $<
+
+.pcb.png :
+	${PCB} -x png --only-visible --font-path $(top_srcdir)/src --outfile $@ $<
+
+.eps.pdf :
+	${PS2PDF} `${AWK} 'BEGIN{s=8}; /BoundingBox/ {printf("-r%d -g%dx%d", s*72, s*$$4, s*$$5);}' $<` $< $@
+
+.tab.texi:
+	${ASCII2TEXI} $< > $@
+
+.tex.pdf:
+	${PDFLATEX} $<
+	${PDFLATEX} $<
+	${PDFLATEX} $<
+
+.eps.png:
+	${PERL} $(srcdir)/eps2png --png $< > $@
+
+%.html: %.texi
+	rm $@ 2>/dev/null; true
+	makeinfo --html --css-include=pcb.css --no-split  --output=$@ $^
+
diff --git a/doc-orig/README b/doc-orig/README
new file mode 100644
index 0000000..8f1d318
--- /dev/null
+++ b/doc-orig/README
@@ -0,0 +1,10 @@
+This is the original doc dir of pcb.
+
+For the modifications introduced by pcb-rnd, check ../doc-rnd
+
+Most of the original documentation is in texi; makeinfo fails to
+produce pdf from pcb.texi and even generating html throws
+a large amount of warnings.
+
+It may be that pcb-rnd will switch from texi to html for documentation
+source format.
diff --git a/doc-orig/README.w32 b/doc-orig/README.w32
new file mode 100644
index 0000000..7ea8a62
--- /dev/null
+++ b/doc-orig/README.w32
@@ -0,0 +1,24 @@
+(not updated for pcb-rnd yet)
+
+Building PCB for Windows with a MinGW cross-compiler
+
+1) Install a MinGW cross-compiler.
+On Debian and derivatives, you can type 'sudo apt-get install mingw32.'.
+You can also build your own by using the build script provided by the
+MinGW project.
+
+2) Install native (non-cross) dependencies.
+
+* autoconf, automake, libtool, gettext, intltool.
+* glib and gtk+.
+
+3) Edit the w32/minipack.conf file to suit your compiler setup.
+
+4) Enter the w32 directory and run ./build_all.
+
+5) Wait while the script fetches and compiles the PCB dependencies and PCB itself.
+
+6) Run the result with wine: wine result/bin/pcb.exe
+
+7) Copy the result directory to a Windows installation (packaging script is not supplied).
+
diff --git a/doc-orig/actions.texi b/doc-orig/actions.texi
new file mode 100644
index 0000000..0c5f248
--- /dev/null
+++ b/doc-orig/actions.texi
@@ -0,0 +1,3574 @@
+ at c key actions
+ at c ./../src/action.c 225
+
+Many actions take a @code{delta} parameter as the last parameter,
+which is an amount to change something.  That @code{delta} may include
+units, as an additional parameter, such as @code{Action(Object,5,mm)}.
+If no units are specified, the default is PCB's native units
+(currently 1/100 mil).  Also, if the delta is prefixed by @code{+} or
+ at code{-}, the size is increased or decreased by that amount.
+Otherwise, the size size is set to the given amount.
+
+ at example
+Action(Object,5,mil)
+Action(Object,+0.5,mm)
+Action(Object,-1)
+ at end example
+
+Actions which take a @code{delta} parameter which do not accept all
+these options will specify what they do take.
+
+ at c ./../src/action.c 229
+
+ at macro pinshapes
+
+Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+ at end macro
+
+ at c ./../src/command.c 64
+
+ at macro colonaction
+
+This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (@code{:}) and thus the syntax
+is documented for that purpose.
+
+ at end macro
+
+ at c ./../src/action.c 227
+
+Many actions act on indicated objects on the board.  They will have
+parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
+what group of objects they act on.  Unless otherwise specified, these
+parameters are defined as follows:
+
+ at table @code
+
+ at item Object
+ at itemx ToggleObject
+Affects the object under the mouse pointer.  If this action is invoked
+from a menu or script, the user will be prompted to click on an
+object, which is then the object affected.
+
+ at item Selected
+ at itemx SelectedObjects
+
+Affects all objects which are currently selected.  At least, all
+selected objects for which the given action makes sense.
+
+ at item SelectedPins
+ at itemx SelectedVias
+ at itemx Selected at var{Type}
+ at itemx @i{etc}
+Affects all objects which are both selected and of the @var{Type} specified.
+
+ at end table
+
+
+ at menu
+* core actions::
+* common actions::
+* gtk actions::
+* lesstif actions::
+ at end menu
+
+ at node core actions
+ at section Core actions
+ at menu
+* AddRats Action:: Add one or more rat lines to the board.
+* ApplyVendor Action:: Applies the currently loaded vendor drill table to the current design.
+* Atomic Action:: Save or restore the undo serial number.
+* Attributes Action:: Let the user edit the attributes of the layout, current or given
+layer, or selected element.
+* AutoPlaceSelected Action:: Auto-place selected components.
+* AutoRoute Action:: Auto-route some or all rat lines.
+* ChangeClearSize Action:: Changes the clearance size of objects.
+* ChangeDrillSize Action:: Changes the drilling hole size of objects.
+* ChangeFlag Action:: Sets or clears flags on objects.
+* ChangeHole Action:: Changes the hole flag of objects.
+* ChangeJoin Action:: Changes the join (clearance through polygons) of objects.
+* ChangeName Action:: Sets the name of objects.
+* ChangeOctagon Action:: Changes the octagon-flag of pins and vias.
+* ChangePaste Action:: Changes the no paste flag of objects.
+* ChangePinName Action:: Sets the name of a specific pin on a specific element.
+* ChangeSize Action:: Changes the size of objects.
+* ChangeSquare Action:: Changes the square flag of pins and pads.
+* ClearOctagon Action:: Clears the octagon-flag of pins and vias.
+* ClearSquare Action:: Clears the square-flag of pins and pads.
+* ClrFlag Action:: Clears flags on objects.
+* Connection Action:: Searches connections of the object at the cursor position.
+* Delete Action:: Delete stuff.
+* DeleteRats Action:: Delete rat lines.
+* DisableVendor Action:: Disables automatic drill size mapping.
+* DisperseElements Action:: Disperses elements.
+* Display Action:: Several display-related actions.
+* djopt Action:: Perform various optimizations on the current board.
+* DRC Action:: Invoke the DRC check.
+* DumpLibrary Action:: Display the entire contents of the libraries.
+* elementlist Action:: Adds the given element if it doesn't already exist.
+* elementsetattr Action:: Sets or clears an element-specific attribute.
+* EnableVendor Action:: Enables automatic drill size mapping.
+* execcommand Action:: Runs a command.
+* ExecuteFile Action:: Run actions from the given file.
+* Flip Action:: Flip an element to the opposite side of the board.
+* FontEdit Action:: Convert the current font to a PCB for editing.
+* FontSave Action:: Convert the current PCB back to a font.
+* FreeRotateBuffer Action:: Rotates the current paste buffer contents by the specified angle.  The
+angle is given in degrees.  If no angle is given, the user is prompted
+for one.
+
+* GlobalPuller Action:: Pull all traces tight.
+* h Action:: Print a help message for commands.
+* Import Action:: Import schematics.
+* l Action:: Loads layout data.
+* le Action:: Loads an element into the current buffer.
+* LoadFootprint Action:: Loads a single footprint by name.
+* LoadFrom Action:: Load layout data from a file.
+* LoadVendorFrom Action:: Loads the specified vendor resource file.
+* m Action:: Loads a layout into the current buffer.
+* MarkCrosshair Action:: Set/Reset the Crosshair mark.
+* Message Action:: Writes a message to the log window.
+* MinClearGap Action:: Ensures that polygons are a minimum distance from objects.
+* MinMaskGap Action:: Ensures the mask is a minimum distance from pins and pads.
+* Mode Action:: Change or use the tool mode.
+* MorphPolygon Action:: Converts dead polygon islands into separate polygons.
+* MoveLayer Action:: Moves/Creates/Deletes Layers.
+* MoveObject Action:: Moves the object under the crosshair.
+* MoveToCurrentLayer Action:: Moves objects to the current layer.
+* Netlist Action:: Perform various actions on netlists.
+* New Action:: Starts a new layout.
+* OptAutoOnly Action:: Toggles the optimize-only-autorouted flag.
+* PasteBuffer Action:: Various operations on the paste buffer.
+* Polygon Action:: Some polygon related stuff.
+* Puller Action:: Pull an arc-line junction tight.
+* q Action:: Quits the application after confirming.
+* q! Action:: Quits the application without confirming.
+* Quit Action:: Quits the application after confirming.
+* Redo Action:: Redo recent``undo''operations.
+* RemoveSelected Action:: Removes any selected objects.
+* Renumber Action:: Renumber all elements.  The changes will be recorded to filename
+for use in backannotating these changes to the schematic.
+* Report Action:: Produce various report.
+* ReportDialog Action:: Report on the object under the crosshair
+* RipUp Action:: Ripup auto-routed tracks, or convert an element to parts.
+* rn Action:: Reads netlist.
+* RouteStyle Action:: Copies the indicated routing style into the current sizes.
+* s Action:: Saves layout data.
+* SaveSettings Action:: Saves settings.
+* SaveTo Action:: Saves data to a file.
+* Select Action:: Toggles or sets the selection.
+* SetFlag Action:: Sets flags on objects.
+* SetOctagon Action:: Sets the octagon-flag of objects.
+* SetSame Action:: Sets current layer and sizes to match indicated item.
+* SetSquare Action:: sets the square-flag of objects.
+* SetThermal Action:: Set the thermal (on the current layer) of pins or vias to the given style.
+Style = 0 means no thermal.
+Style = 1 has diagonal fingers with sharp edges.
+Style = 2 has horizontal and vertical fingers with sharp edges.
+Style = 3 is a solid connection to the plane.Style = 4 has diagonal fingers with rounded edges.
+Style = 5 has horizontal and vertical fingers with rounded edges.
+
+* SetValue Action:: Change various board-wide values and sizes.
+* ToggleHideName Action:: Toggles the visibility of element names.
+* ToggleVendor Action:: Toggles the state of automatic drill size mapping.
+* Undo Action:: Undo recent changes.
+* UnloadVendor Action:: Unloads the current vendor drill mapping table.
+* Unselect Action:: Unselects the object at the pointer location or the specified objects.
+* w Action:: Saves layout data.
+* wq Action:: Saves the layout data and quits.
+ at end menu
+ at node AddRats Action
+ at subsection AddRats
+ at c key AddRats in hid 
+ at cartouche
+ at format
+AddRats(AllRats|SelectedRats|Close)@end format
+ at end cartouche
+
+Add one or more rat lines to the board.
+ at c ./../src/action.c 3310
+
+ at table @code
+
+ at item AllRats
+Create rat lines for all loaded nets that aren't already connected on
+with copper.
+
+ at item SelectedRats
+Similarly, but only add rat lines for nets connected to selected pins
+and pads.
+
+ at item Close
+Selects the shortest unselected rat on the board.
+
+ at end table
+
+
+ at node ApplyVendor Action
+ at subsection ApplyVendor
+ at c key ApplyVendor in hid 
+ at cartouche
+ at format
+ApplyVendor()@end format
+ at end cartouche
+
+Applies the currently loaded vendor drill table to the current design.
+ at c ./../src/vendor.c 112
+ at cindex vendor map 
+ at cindex vendor drill table
+ at findex ApplyVendor()
+
+This will modify all of your drill holes to match the list of allowed
+sizes for your vendor.
+
+ at node Atomic Action
+ at subsection Atomic
+ at c key Atomic in hid 
+ at cartouche
+ at format
+Atomic(Save|Restore|Close|Block)@end format
+ at end cartouche
+
+Save or restore the undo serial number.
+ at c ./../src/action.c 1720
+
+This action allows making multiple-action bindings into an atomic
+operation that will be undone by a single Undo command.  For example,
+to optimize rat lines, you'd delete the rats and re-add them.  To
+group these into a single undo, you'd want the deletions and the
+additions to have the same undo serial number.  So, you @code{Save},
+delete the rats, @code{Restore}, add the rats - using the same serial
+number as the deletes, then @code{Block}, which checks to see if the
+deletions or additions actually did anything.  If not, the serial
+number is set to the saved number, as there's nothing to undo.  If
+something did happen, the serial number is incremented so that these
+actions are counted as a single undo step.
+
+ at table @code
+
+ at item Save
+Saves the undo serial number.
+
+ at item Restore
+Returns it to the last saved number.
+
+ at item Close
+Sets it to 1 greater than the last save.
+
+ at item Block
+Does a Restore if there was nothing to undo, else does a Close.
+
+ at end table
+
+
+ at node Attributes Action
+ at subsection Attributes
+ at c key Attributes in hid 
+ at cartouche
+ at format
+Attributes(Layout|Layer|Element)
+Attributes(Layer,layername)@end format
+ at end cartouche
+
+Let the user edit the attributes of the layout, current or given
+layer, or selected element.
+ at c ./../src/action.c 6761
+
+This just pops up a dialog letting the user edit the attributes of the
+pcb, an element, or a layer.
+
+
+ at node AutoPlaceSelected Action
+ at subsection AutoPlaceSelected
+ at c key AutoPlaceSelected in hid 
+ at cartouche
+ at format
+AutoPlaceSelected()@end format
+ at end cartouche
+
+Auto-place selected components.
+ at c ./../src/action.c 3450
+
+Attempts to re-arrange the selected components such that the nets
+connecting them are minimized.  Note that you cannot undo this.
+
+
+ at node AutoRoute Action
+ at subsection AutoRoute
+ at c key AutoRoute in hid 
+ at cartouche
+ at format
+AutoRoute(AllRats|SelectedRats)@end format
+ at end cartouche
+
+Auto-route some or all rat lines.
+ at c ./../src/action.c 3471
+
+ at table @code
+
+ at item AllRats
+Attempt to autoroute all rats.
+
+ at item SelectedRats
+Attempt to autoroute the selected rats.
+
+ at end table
+
+Before autorouting, it's important to set up a few things.  First,
+make sure any layers you aren't using are disabled, else the
+autorouter may use them.  Next, make sure the current line and via
+styles are set accordingly.  Last, make sure "new lines clear
+polygons" is set, in case you eventually want to add a copper pour.
+
+Autorouting takes a while.  During this time, the program may not be
+responsive.
+
+
+ at node ChangeClearSize Action
+ at subsection ChangeClearSize
+ at c key ChangeClearSize in hid 
+ at cartouche
+ at format
+ChangeClearSize(Object, delta)
+ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)
+ChangeClearSize(SelectedLines|SelectedArcs, delta
+ChangeClearSize(Selected|SelectedObjects, delta)@end format
+ at end cartouche
+
+Changes the clearance size of objects.
+ at c ./../src/action.c 3686
+
+If the solder mask is currently showing, this action changes the
+solder mask clearance.  If the mask is not showing, this action
+changes the polygon clearance.
+
+
+ at node ChangeDrillSize Action
+ at subsection ChangeDrillSize
+ at c key ChangeDrillSize in hid 
+ at cartouche
+ at format
+ChangeDrillSize(Object, delta)
+ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)@end format
+ at end cartouche
+
+Changes the drilling hole size of objects.
+ at c ./../src/action.c 3630
+
+
+ at node ChangeFlag Action
+ at subsection ChangeFlag
+ at c key ChangeFlag in hid 
+ at cartouche
+ at format
+ChangeFlag(Object|Selected|SelectedObjects, flag, value)
+ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)
+ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)
+ChangeFlag(SelectedElements, flag, value)
+flag = square | octagon | thermal | join
+value = 0 | 1 at end format
+ at end cartouche
+
+Sets or clears flags on objects.
+ at c ./../src/action.c 5750
+
+Toggles the given flag on the indicated object(s).  The flag may be
+one of the flags listed above (square, octagon, thermal, join).  The
+value may be the number 0 or 1.  If the value is 0, the flag is
+cleared.  If the value is 1, the flag is set.
+
+
+ at node ChangeHole Action
+ at subsection ChangeHole
+ at c key ChangeHole in hid 
+ at cartouche
+ at format
+ChangeHole(ToggleObject|Object|SelectedVias|Selected)@end format
+ at end cartouche
+
+Changes the hole flag of objects.
+ at c ./../src/action.c 4571
+
+The "hole flag" of a via determines whether the via is a
+plated-through hole (not set), or an unplated hole (set).
+
+
+ at node ChangeJoin Action
+ at subsection ChangeJoin
+ at c key ChangeJoin in hid 
+ at cartouche
+ at format
+ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)@end format
+ at end cartouche
+
+Changes the join (clearance through polygons) of objects.
+ at c ./../src/action.c 4199
+
+The join flag determines whether a line or arc, drawn to intersect a
+polygon, electrically connects to the polygon or not.  When joined,
+the line/arc is simply drawn over the polygon, making an electrical
+connection.  When not joined, a gap is drawn between the line and the
+polygon, insulating them from each other.
+
+
+ at node ChangeName Action
+ at subsection ChangeName
+ at c key ChangeName in hid 
+ at cartouche
+ at format
+ChangeName(Object)
+ChangeName(Layout|Layer)@end format
+ at end cartouche
+
+Sets the name of objects.
+ at c ./../src/action.c 4005
+
+ at table @code
+
+ at item Object
+Changes the name of the element under the cursor.
+
+ at item Layout
+Changes the name of the layout.  This is printed on the fab drawings.
+
+ at item Layer
+Changes the name of the currently active layer.
+
+ at end table
+
+
+ at node ChangeOctagon Action
+ at subsection ChangeOctagon
+ at c key ChangeOctagon in hid 
+ at cartouche
+ at format
+ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)
+ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)@end format
+ at end cartouche
+
+Changes the octagon-flag of pins and vias.
+ at c ./../src/action.c 4403
+
+ at pinshapes
+
+
+ at node ChangePaste Action
+ at subsection ChangePaste
+ at c key ChangePaste in hid 
+ at cartouche
+ at format
+ChangePaste(ToggleObject|Object|SelectedPads|Selected)@end format
+ at end cartouche
+
+Changes the no paste flag of objects.
+ at c ./../src/action.c 4611
+
+The "no paste flag" of a pad determines whether the solderpaste
+ stencil will have an opening for the pad (no set) or if there wil be
+ no solderpaste on the pad (set).  This is used for things such as
+ fiducial pads.
+
+
+ at node ChangePinName Action
+ at subsection ChangePinName
+ at c key ChangePinName in hid 
+ at cartouche
+ at format
+ChangePinName(ElementName,PinNumber,PinName)@end format
+ at end cartouche
+
+Sets the name of a specific pin on a specific element.
+ at c ./../src/action.c 3924
+
+This can be especially useful for annotating pin names from a
+schematic to the layout without requiring knowledge of the pcb file
+format.
+
+ at example
+ChangePinName(U3, 7, VCC)
+ at end example
+
+
+ at node ChangeSize Action
+ at subsection ChangeSize
+ at c key ChangeSize in hid 
+ at cartouche
+ at format
+ChangeSize(Object, delta)
+ChangeSize(SelectedObjects|Selected, delta)
+ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)
+ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)
+ChangeSize(SelectedElements, delta)@end format
+ at end cartouche
+
+Changes the size of objects.
+ at c ./../src/action.c 3543
+
+For lines and arcs, this changes the width.  For pins and vias, this
+changes the overall diameter of the copper annulus.  For pads, this
+changes the width and, indirectly, the length.  For texts and names,
+this changes the scaling factor.  For elements, this changes the width
+of the silk layer lines and arcs for this element.
+
+
+ at node ChangeSquare Action
+ at subsection ChangeSquare
+ at c key ChangeSquare in hid 
+ at cartouche
+ at format
+ChangeSquare(ToggleObject)
+ChangeSquare(SelectedElements|SelectedPins)
+ChangeSquare(Selected|SelectedObjects)@end format
+ at end cartouche
+
+Changes the square flag of pins and pads.
+ at c ./../src/action.c 4250
+
+Note that @code{Pins} means both pins and pads.
+
+ at pinshapes
+
+
+ at node ClearOctagon Action
+ at subsection ClearOctagon
+ at c key ClearOctagon in hid 
+ at cartouche
+ at format
+ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)
+ClearOctagon(SelectedElements|SelectedPins|SelectedVias)@end format
+ at end cartouche
+
+Clears the octagon-flag of pins and vias.
+ at c ./../src/action.c 4515
+
+ at pinshapes
+
+
+ at node ClearSquare Action
+ at subsection ClearSquare
+ at c key ClearSquare in hid 
+ at cartouche
+ at format
+ClearSquare(ToggleObject|SelectedElements|SelectedPins)@end format
+ at end cartouche
+
+Clears the square-flag of pins and pads.
+ at c ./../src/action.c 4352
+
+Note that @code{Pins} means pins and pads.
+
+ at pinshapes
+
+
+ at node ClrFlag Action
+ at subsection ClrFlag
+ at c key ClrFlag in hid 
+ at cartouche
+ at format
+ClrFlag(Object|Selected|SelectedObjects, flag)
+ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)
+ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)
+ClrFlag(SelectedElements, flag)
+flag = square | octagon | thermal | join at end format
+ at end cartouche
+
+Clears flags on objects.
+ at c ./../src/action.c 5733
+
+Turns the given flag off, regardless of its previous setting.  See
+ at code{ChangeFlag}.
+
+ at example
+ClrFlag(SelectedLines,join)
+ at end example
+
+
+ at node Connection Action
+ at subsection Connection
+ at c key Connection in hid 
+ at cartouche
+ at format
+Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)@end format
+ at end cartouche
+
+Searches connections of the object at the cursor position.
+ at c ./../src/action.c 2093
+
+Connections found with this action will be highlighted in the
+``connected-color'' color and will have the ``found'' flag set.
+
+ at table @code
+
+ at item Find
+The net under the cursor is ``found''.
+
+ at item ResetLinesAndPolygons
+Any ``found'' lines and polygons are marked ``not found''.
+
+ at item ResetPinsAndVias
+Any ``found'' pins and vias are marked ``not found''.
+
+ at item Reset
+All ``found'' objects are marked ``not found''.
+
+ at end table
+
+
+ at node Delete Action
+ at subsection Delete
+ at c key Delete in hid 
+ at cartouche
+ at format
+Delete(Object|Selected)
+Delete(AllRats|SelectedRats)@end format
+ at end cartouche
+
+Delete stuff.
+ at c ./../src/action.c 3371
+
+
+ at node DeleteRats Action
+ at subsection DeleteRats
+ at c key DeleteRats in hid 
+ at cartouche
+ at format
+DeleteRats(AllRats|Selected|SelectedRats)@end format
+ at end cartouche
+
+Delete rat lines.
+ at c ./../src/action.c 3418
+
+
+ at node DisableVendor Action
+ at subsection DisableVendor
+ at c key DisableVendor in hid 
+ at cartouche
+ at format
+DisableVendor()@end format
+ at end cartouche
+
+Disables automatic drill size mapping.
+ at c ./../src/vendor.c 161
+
+ at cindex vendor map 
+ at cindex vendor drill table
+ at findex DisableVendor()
+
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.
+
+
+ at node DisperseElements Action
+ at subsection DisperseElements
+ at c key DisperseElements in hid 
+ at cartouche
+ at format
+DisperseElements(All|Selected)@end format
+ at end cartouche
+
+Disperses elements.
+ at c ./../src/action.c 2146
+
+Normally this is used when starting a board, by selecting all elements
+and then dispersing them.  This scatters the elements around the board
+so that you can pick individual ones, rather than have all the
+elements at the same 0,0 coordinate and thus impossible to choose
+from.
+
+
+ at node Display Action
+ at subsection Display
+ at c key Display in hid 
+ at cartouche
+ at format
+Display(NameOnPCB|Description|Value)
+Display(Grid|Redraw)
+Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)
+Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)
+Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)
+Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)
+Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)
+Display(ToggleLiveRoute|LockNames|OnlyNames)
+Display(Pinout|PinOrPadName)@end format
+ at end cartouche
+
+Several display-related actions.
+ at c ./../src/action.c 2262
+
+ at table @code
+
+ at item NameOnPCB
+ at item Description
+ at item Value
+Specify whether all elements show their name, description, or value.
+
+ at item Redraw
+Redraw the whole board.
+
+ at item Toggle45Degree
+When clear, lines can be drawn at any angle.  When set, lines are
+restricted to multiples of 45 degrees and requested lines may be
+broken up according to the clip setting.
+
+ at item CycleClip
+Changes the way lines are restricted to 45 degree increments.  The
+various settings are: straight only, orthogonal then angled, and angled
+then orthogonal.  If AllDirections is set, this action disables it.
+
+ at item CycleCrosshair
+Changes crosshair drawing.  Crosshair may accept form of 4-ray,
+8-ray and 12-ray cross.
+
+ at item ToggleRubberBandMode
+If set, moving an object moves all the lines attached to it too.
+
+ at item ToggleStartDirection
+If set, each time you set a point in a line, the Clip toggles between
+orth-angle and angle-ortho.
+
+ at item ToggleUniqueNames
+If set, you will not be permitted to change the name of an element to
+match that of another element.
+
+ at item ToggleSnapPin
+If set, pin centers and pad end points are treated as additional grid
+points that the cursor can snap to.
+
+ at item ToggleLocalRef
+If set, the mark is automatically set to the beginning of any move, so
+you can see the relative distance you've moved.
+
+ at item ToggleThindraw
+If set, objects on the screen are drawn as outlines (lines are drawn
+as center-lines).  This lets you see line endpoints hidden under pins,
+for example.
+
+ at item ToggleThindrawPoly
+If set, polygons on the screen are drawn as outlines.
+
+ at item ToggleShowDRC
+If set, pending objects (i.e. lines you're in the process of drawing)
+will be drawn with an outline showing how far away from other copper
+you need to be.
+
+ at item ToggleLiveRoute
+If set, the progress of the autorouter will be visible on the screen.
+
+ at item ToggleAutoDRC
+If set, you will not be permitted to make connections which violate
+the current DRC and netlist settings.
+
+ at item ToggleCheckPlanes
+If set, lines and arcs aren't drawn, which usually leaves just the
+polygons.  If you also disable all but the layer you're interested in,
+this allows you to check for isolated regions.
+
+ at item ToggleOrthoMove
+If set, the crosshair is only allowed to move orthogonally from its
+previous position.  I.e. you can move an element or line up, down,
+left, or right, but not up+left or down+right.
+
+ at item ToggleName
+Selects whether the pinouts show the pin names or the pin numbers.
+
+ at item ToggleLockNames
+If set, text will ignore left mouse clicks and actions that work on
+objects under the mouse. You can still select text with a lasso (left
+mouse drag) and perform actions on the selection.
+
+ at item ToggleOnlyNames
+If set, only text will be sensitive for mouse clicks and actions that
+work on objects under the mouse. You can still select other objects
+with a lasso (left mouse drag) and perform actions on the selection.
+
+ at item ToggleMask
+Turns the solder mask on or off.
+
+ at item ToggleClearLine
+When set, the clear-line flag causes new lines and arcs to have their
+``clear polygons'' flag set, so they won't be electrically connected
+to any polygons they overlap.
+
+ at item ToggleFullPoly
+When set, the full-poly flag causes new polygons to have their
+``full polygon'' flag set, so all parts of them will be displayed
+instead of only the biggest one.
+
+ at item ToggleGrid
+Resets the origin of the current grid to be wherever the mouse pointer
+is (not where the crosshair currently is).  If you provide two numbers
+after this, the origin is set to that coordinate.
+
+ at item Grid
+Toggles whether the grid is displayed or not.
+
+ at item Pinout
+Causes the pinout of the element indicated by the cursor to be
+displayed, usually in a separate window.
+
+ at item PinOrPadName
+Toggles whether the names of pins, pads, or (yes) vias will be
+displayed.  If the cursor is over an element, all of its pins and pads
+are affected.
+
+ at end table
+
+
+ at node djopt Action
+ at subsection djopt
+ at c key djopt in hid 
+ at cartouche
+ at format
+djopt(debumpify|unjaggy|simple|vianudge|viatrim|orthopull)
+djopt(auto) - all of the above
+djopt(miter)@end format
+ at end cartouche
+
+Perform various optimizations on the current board.
+ at c ./../src/djopt.c 2853
+
+The different types of optimizations change your board in order to
+reduce the total trace length and via count.
+
+ at table @code
+
+ at item debumpify
+Looks for U-shaped traces that can be shortened or eliminated.
+
+ at item unjaggy
+Looks for corners which could be flipped to eliminate one or more
+corners (i.e. jaggy lines become simpler).
+
+ at item simple
+Removing uneeded vias, replacing two or more trace segments in a row
+with a single segment.  This is usually performed automatically after
+other optimizations.
+
+ at item vianudge
+Looks for vias where all traces leave in the same direction.  Tries to
+move via in that direction to eliminate one of the traces (and thus a
+corner).
+
+ at item viatrim
+Looks for traces that go from via to via, where moving that trace to a
+different layer eliminates one or both vias.
+
+ at item orthopull
+Looks for chains of traces all going in one direction, with more
+traces orthogonal on one side than on the other.  Moves the chain in
+that direction, causing a net reduction in trace length, possibly
+eliminating traces and/or corners.
+
+ at item splitlines
+Looks for lines that pass through vias, pins, or pads, and splits them
+into separate lines so they can be managed separately.
+
+ at item auto
+Performs the above options, repeating until no further optimizations
+can be made.
+
+ at item miter
+Replaces 90 degree corners with a pair of 45 degree corners, to reduce
+RF losses and trace length.
+
+ at end table
+
+
+ at node DRC Action
+ at subsection DRC
+ at c key DRC in hid 
+ at cartouche
+ at format
+DRC()@end format
+ at end cartouche
+
+Invoke the DRC check.
+ at c ./../src/action.c 1755
+
+Note that the design rule check uses the current board rule settings,
+not the current style settings.
+
+
+ at node DumpLibrary Action
+ at subsection DumpLibrary
+ at c key DumpLibrary in hid 
+ at cartouche
+ at format
+DumpLibrary()@end format
+ at end cartouche
+
+Display the entire contents of the libraries.
+ at c ./../src/action.c 1791
+
+
+
+ at node elementlist Action
+ at subsection elementlist
+ at c key elementlist in hid 
+ at cartouche
+ at format
+ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)@end format
+ at end cartouche
+
+Adds the given element if it doesn't already exist.
+ at c ./../src/action.c 5983
+
+ at table @code
+
+ at item Start
+Indicates the start of an element list; call this before any Need
+actions.
+
+ at item Need
+Searches the board for an element with a matching refdes.
+
+If found, the value and footprint are updated.
+
+If not found, a new element is created with the given footprint and value.
+
+ at item Done
+Compares the list of elements needed since the most recent
+ at code{start} with the list of elements actually on the board.  Any
+elements that weren't listed are selected, so that the user may delete
+them.
+
+ at end table
+
+
+ at node elementsetattr Action
+ at subsection elementsetattr
+ at c key elementsetattr in hid 
+ at cartouche
+ at format
+ElementSetAttr(refdes,name[,value])@end format
+ at end cartouche
+
+Sets or clears an element-specific attribute.
+ at c ./../src/action.c 6176
+
+If a value is specified, the named attribute is added (if not already
+present) or changed (if it is) to the given value.  If the value is
+not specified, the given attribute is removed if present.
+
+
+ at node EnableVendor Action
+ at subsection EnableVendor
+ at c key EnableVendor in hid 
+ at cartouche
+ at format
+EnableVendor()@end format
+ at end cartouche
+
+Enables automatic drill size mapping.
+ at c ./../src/vendor.c 146
+
+ at cindex vendor map 
+ at cindex vendor drill table
+ at findex EnableVendor()
+
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.  To enable drill
+mapping, a vendor resource file containing a drill table must be
+loaded first.
+
+
+ at node execcommand Action
+ at subsection execcommand
+ at c key execcommand in hid 
+ at cartouche
+ at format
+ExecCommand(command)@end format
+ at end cartouche
+
+Runs a command.
+ at c ./../src/action.c 6234
+
+Runs the given command, which is a system executable.
+
+
+ at node ExecuteFile Action
+ at subsection ExecuteFile
+ at c key ExecuteFile in hid 
+ at cartouche
+ at format
+ExecuteFile(filename)@end format
+ at end cartouche
+
+Run actions from the given file.
+ at c ./../src/action.c 5861
+
+Lines starting with @code{#} are ignored.
+
+
+ at node Flip Action
+ at subsection Flip
+ at c key Flip in hid 
+ at cartouche
+ at format
+Flip(Object|Selected|SelectedElements)@end format
+ at end cartouche
+
+Flip an element to the opposite side of the board.
+ at c ./../src/action.c 1841
+
+Note that the location of the element will be symmetric about the
+cursor location; i.e. if the part you are pointing at will still be at
+the same spot once the element is on the other side.  When flipping
+multiple elements, this retains their positions relative to each
+other, not their absolute positions on the board.
+
+
+ at node FontEdit Action
+ at subsection FontEdit
+ at c key FontEdit in hid 
+ at cartouche
+ at format
+FontEdit()@end format
+ at end cartouche
+
+Convert the current font to a PCB for editing.
+ at c ./../src/fontmode.c 73
+
+
+ at node FontSave Action
+ at subsection FontSave
+ at c key FontSave in hid 
+ at cartouche
+ at format
+FontSave()@end format
+ at end cartouche
+
+Convert the current PCB back to a font.
+ at c ./../src/fontmode.c 173
+
+
+ at node FreeRotateBuffer Action
+ at subsection FreeRotateBuffer
+ at c key FreeRotateBuffer in hid 
+ at cartouche
+ at format
+FreeRotateBuffer([Angle])@end format
+ at end cartouche
+
+Rotates the current paste buffer contents by the specified angle.  The
+angle is given in degrees.  If no angle is given, the user is prompted
+for one.
+
+ at c ./../src/buffer.c 1370
+   
+Rotates the contents of the pastebuffer by an arbitrary angle.  If no
+angle is given, the user is prompted for one.
+
+
+ at node GlobalPuller Action
+ at subsection GlobalPuller
+ at c key GlobalPuller in hid 
+ at cartouche
+ at format
+GlobalPuller()@end format
+ at end cartouche
+
+Pull all traces tight.
+ at c ./../src/puller.c 534
+
+
+ at node h Action
+ at subsection h
+ at c key h in hid 
+ at cartouche
+ at format
+h at end format
+ at end cartouche
+
+Print a help message for commands.
+ at c ./../src/command.c 72
+
+ at colonaction
+
+
+ at node Import Action
+ at subsection Import
+ at c key Import in hid 
+ at cartouche
+ at format
+Import()
+Import([gnetlist|make[,source,source,...]])
+Import(setnewpoint[,(mark|center|X,Y)])
+Import(setdisperse,D,units)
+ at end format
+ at end cartouche
+
+Import schematics.
+ at c ./../src/action.c 6464
+
+Imports element and netlist data from the schematics (or some other
+source).  The first parameter, which is optional, is the mode.  If not
+specified, the @code{import::mode} attribute in the PCB is used.
+ at code{gnetlist} means gnetlist is used to obtain the information from
+the schematics.  @code{make} invokes @code{make}, assuming the user
+has a @code{Makefile} in the current directory.  The @code{Makefile}
+will be invoked with the following variables set:
+
+ at table @code
+
+ at item PCB
+The name of the .pcb file
+
+ at item SRCLIST
+A space-separated list of source files
+
+ at item OUT
+The name of the file in which to put the command script, which may
+contain any @pcb{} actions.  By default, this is a temporary file
+selected by @pcb{}, but if you specify an @code{import::outfile}
+attribute, that file name is used instead (and not automatically
+deleted afterwards).
+
+ at end table
+
+The target specified to be built is the first of these that apply:
+
+ at itemize @bullet
+
+ at item
+The target specified by an @code{import::target} attribute.
+
+ at item
+The output file specified by an @code{import::outfile} attribute.
+
+ at item
+If nothing else is specified, the target is @code{pcb_import}.
+
+ at end itemize
+
+If you specify an @code{import::makefile} attribute, then "-f <that
+file>" will be added to the command line.
+
+If you specify the mode, you may also specify the source files
+(schematics).  If you do not specify any, the list of schematics is
+obtained by reading the @code{import::src at var{N}} attributes (like
+ at code{import::src0}, @code{import::src1}, etc).
+
+For compatibility with future extensions to the import file format,
+the generated file @emph{must not} start with the two characters
+ at code{#%}.
+
+If a temporary file is needed the @code{TMPDIR} environment variable
+is used to select its location.
+
+Note that the programs @code{gnetlist} and @code{make} may be
+overridden by the user via the @code{make-program} and @code{gnetlist}
+ at code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
+line).
+
+If @pcb{} cannot determine which schematic(s) to import from, the GUI
+is called to let user choose (see @code{ImportGUI()}).
+
+Note that Import() doesn't delete anything - after an Import, elements
+which shouldn't be on the board are selected and may be removed once
+it's determined that the deletion is appropriate.
+
+If @code{Import()} is called with @code{setnewpoint}, then the location
+of new components can be specified.  This is where parts show up when
+they're added to the board.  The default is the center of the board.
+
+ at table @code
+
+ at item Import(setnewpoint)
+
+Prompts the user to click on the board somewhere, uses that point.  If
+called by a hotkey, uses the current location of the crosshair.
+
+ at item Import(setnewpoint,mark)
+
+Uses the location of the mark.  If no mark is present, the point is
+not changed.
+
+ at item Import(setnewpoint,center)
+
+Resets the point to the center of the board.
+
+ at item Import(setnewpoint,X,Y,units)
+
+Sets the point to the specific coordinates given.  Example:
+ at code{Import(setnewpoint,50,25,mm)}
+
+ at end table
+
+Note that the X and Y locations are stored in attributes named
+ at code{import::newX} and @code{import::newY} so you could change them
+manually if you wished.
+
+Calling @code{Import(setdisperse,D,units)} sets how much the newly
+placed elements are dispersed relative to the set point.  For example,
+ at code{Import(setdisperse,10,mm)} will offset each part randomly up to
+10mm away from the point.  The default dispersion is 1/10th of the
+smallest board dimension.  Dispersion is saved in the
+ at code{import::disperse} attribute.
+
+
+ at node l Action
+ at subsection l
+ at c key l in hid 
+ at cartouche
+ at format
+l [name]@end format
+ at end cartouche
+
+Loads layout data.
+ at c ./../src/command.c 99
+
+Loads a new datafile (layout) and, if confirmed, overwrites any
+existing unsaved data.  The filename and the searchpath
+(@emph{filePath}) are passed to the command defined by
+ at emph{fileCommand}.  If no filename is specified a file select box
+will popup.
+
+ at colonaction
+
+
+ at node le Action
+ at subsection le
+ at c key le in hid 
+ at cartouche
+ at format
+le [name]@end format
+ at end cartouche
+
+Loads an element into the current buffer.
+ at c ./../src/command.c 129
+
+The filename and the searchpath (@emph{elementPath}) are passed to the
+command defined by @emph{elementCommand}.  If no filename is specified
+a file select box will popup.
+
+ at colonaction
+
+
+ at node LoadFootprint Action
+ at subsection LoadFootprint
+ at c key LoadFootprint in hid 
+ at cartouche
+ at format
+LoadFootprint(filename[,refdes,value])@end format
+ at end cartouche
+
+Loads a single footprint by name.
+ at c ./../src/buffer.c 809
+
+Loads a single footprint by name, rather than by reference or through
+the library.  If a refdes and value are specified, those are inserted
+into the footprint as well.  The footprint remains in the paste buffer.
+
+
+ at node LoadFrom Action
+ at subsection LoadFrom
+ at c key LoadFrom in hid 
+ at cartouche
+ at format
+LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)@end format
+ at end cartouche
+
+Load layout data from a file.
+ at c ./../src/action.c 5031
+
+This action assumes you know what the filename is.  The various GUIs
+should have a similar @code{Load} action where the filename is
+optional, and will provide their own file selection mechanism to let
+you choose the file name.
+
+ at table @code
+
+ at item Layout
+Loads an entire PCB layout, replacing the current one.
+
+ at item LayoutToBuffer
+Loads an entire PCB layout to the paste buffer.
+
+ at item ElementToBuffer
+Loads the given element file into the paste buffer.  Element files
+contain only a single @code{Element} definition, such as the
+``newlib'' library uses.
+
+ at item Netlist
+Loads a new netlist, replacing any current netlist.
+
+ at item Revert
+Re-loads the current layout from its disk file, reverting any changes
+you may have made.
+
+ at end table
+
+
+ at node LoadVendorFrom Action
+ at subsection LoadVendorFrom
+ at c key LoadVendorFrom in hid 
+ at cartouche
+ at format
+LoadVendorFrom(filename)@end format
+ at end cartouche
+
+Loads the specified vendor resource file.
+ at c ./../src/vendor.c 201
+
+ at cindex vendor map 
+ at cindex vendor drill table
+ at findex LoadVendorFrom()
+
+ at table @var
+ at item filename
+Name of the vendor resource file.  If not specified, the user will
+be prompted to enter one.
+ at end table
+
+
+ at node m Action
+ at subsection m
+ at c key m in hid 
+ at cartouche
+ at format
+m [name]@end format
+ at end cartouche
+
+Loads a layout into the current buffer.
+ at c ./../src/command.c 157
+
+The filename and the searchpath (@emph{filePath}) are passed to the
+command defined by @emph{fileCommand}.
+If no filename is specified a file select box will popup.
+
+ at colonaction
+
+
+ at node MarkCrosshair Action
+ at subsection MarkCrosshair
+ at c key MarkCrosshair in hid 
+ at cartouche
+ at format
+MarkCrosshair()
+MarkCrosshair(Center)@end format
+ at end cartouche
+
+Set/Reset the Crosshair mark.
+ at c ./../src/action.c 3502
+
+The ``mark'' is a small X-shaped target on the display which is
+treated like a second origin (the normal origin is the upper let
+corner of the board).  The GUI will display a second set of
+coordinates for this mark, which tells you how far you are from it.
+
+If no argument is given, the mark is toggled - disabled if it was
+enabled, or enabled at the current cursor position of disabled.  If
+the @code{Center} argument is given, the mark is moved to the current
+cursor location.
+
+
+ at node Message Action
+ at subsection Message
+ at c key Message in hid 
+ at cartouche
+ at format
+Message(message)@end format
+ at end cartouche
+
+Writes a message to the log window.
+ at c ./../src/action.c 1886
+
+This action displays a message to the log window.  This action is primarily
+provided for use by other programs which may interface with PCB.  If
+multiple arguments are given, each one is sent to the log window
+followed by a newline.
+
+
+ at node MinClearGap Action
+ at subsection MinClearGap
+ at c key MinClearGap in hid 
+ at cartouche
+ at format
+MinClearGap(delta)
+MinClearGap(Selected, delta)@end format
+ at end cartouche
+
+Ensures that polygons are a minimum distance from objects.
+ at c ./../src/action.c 3827
+
+Checks all specified objects, and increases the polygon clearance if
+needed to ensure a minimum distance between their edges and the
+polygon edges.
+
+
+ at node MinMaskGap Action
+ at subsection MinMaskGap
+ at c key MinMaskGap in hid 
+ at cartouche
+ at format
+MinMaskGap(delta)
+MinMaskGap(Selected, delta)@end format
+ at end cartouche
+
+Ensures the mask is a minimum distance from pins and pads.
+ at c ./../src/action.c 3752
+
+Checks all specified pins and/or pads, and increases the mask if
+needed to ensure a minimum distance between the pin or pad edge and
+the mask edge.
+
+
+ at node Mode Action
+ at subsection Mode
+ at c key Mode in hid 
+ at cartouche
+ at format
+Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)
+Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)
+Mode(Notify|Release|Cancel|Stroke)
+Mode(Save|Restore)@end format
+ at end cartouche
+
+Change or use the tool mode.
+ at c ./../src/action.c 2612
+
+ at table @code
+
+ at item Arc
+ at itemx Arrow
+ at itemx Copy
+ at itemx InsertPoint
+ at itemx Line
+ at itemx Lock
+ at itemx Move
+ at itemx None
+ at itemx PasteBuffer
+ at itemx Polygon
+ at itemx Rectangle
+ at itemx Remove
+ at itemx Rotate
+ at itemx Text
+ at itemx Thermal
+ at itemx Via
+Select the indicated tool.
+
+ at item Notify
+Called when you press the mouse button, or move the mouse.
+
+ at item Release
+Called when you release the mouse button.
+
+ at item Cancel
+Cancels any pending tool activity, allowing you to restart elsewhere.
+For example, this allows you to start a new line rather than attach a
+line to the previous line.
+
+ at item Escape
+Similar to Cancel but calling this action a second time will return
+to the Arrow tool.
+
+ at item Stroke
+If your @code{pcb} was built with libstroke, this invokes the stroke
+input method.  If not, this will restart a drawing mode if you were
+drawing, else it will select objects.
+
+ at item Save
+Remembers the current tool.
+
+ at item Restore
+Restores the tool to the last saved tool.
+
+ at end table
+
+
+ at node MorphPolygon Action
+ at subsection MorphPolygon
+ at c key MorphPolygon in hid 
+ at cartouche
+ at format
+MorphPolygon(Object|Selected)@end format
+ at end cartouche
+
+Converts dead polygon islands into separate polygons.
+ at c ./../src/action.c 4087
+
+If a polygon is divided into unconnected "islands", you can use
+this command to convert the otherwise disappeared islands into
+separate polygons. Be sure the cursor is over a portion of the
+polygon that remains visible. Very small islands that may flake
+off are automatically deleted.
+
+
+ at node MoveLayer Action
+ at subsection MoveLayer
+ at c key MoveLayer in hid 
+ at cartouche
+ at format
+MoveLayer(old,new)@end format
+ at end cartouche
+
+Moves/Creates/Deletes Layers.
+ at c ./../src/move.c 1091
+
+Moves a layer, creates a new layer, or deletes a layer.
+
+ at table @code
+
+ at item old
+The is the layer number to act upon.  Allowed values are:
+ at table @code
+
+ at item c
+Currently selected layer.
+
+ at item -1
+Create a new layer.
+
+ at item number
+An existing layer number.
+
+ at end table
+
+ at item new
+Specifies where to move the layer to.  Allowed values are:
+ at table @code
+ at item -1
+Deletes the layer.
+
+ at item up
+Moves the layer up.
+
+ at item down
+Moves the layer down.
+
+ at item c
+Creates a new layer.
+
+ at end table
+
+ at end table
+
+
+ at node MoveObject Action
+ at subsection MoveObject
+ at c key MoveObject in hid 
+ at cartouche
+ at format
+MoveObject(X,Y,dim)@end format
+ at end cartouche
+
+Moves the object under the crosshair.
+ at c ./../src/action.c 5567
+
+The @code{X} and @code{Y} are treated like @code{delta} is for many
+other objects.  For each, if it's prefixed by @code{+} or @code{-},
+then that amount is relative.  Otherwise, it's absolute.  Units can be
+ at code{mil} or @code{mm}; if unspecified, units are PCB's internal
+units, currently 1/100 mil.
+
+
+ at node MoveToCurrentLayer Action
+ at subsection MoveToCurrentLayer
+ at c key MoveToCurrentLayer in hid 
+ at cartouche
+ at format
+MoveToCurrentLayer(Object|SelectedObjects)@end format
+ at end cartouche
+
+Moves objects to the current layer.
+ at c ./../src/action.c 5609
+
+Note that moving an element from a component layer to a solder layer,
+or from solder to component, won't automatically flip it.  Use the
+ at code{Flip()} action to do that.
+
+
+ at node Netlist Action
+ at subsection Netlist
+ at c key Netlist in hid 
+ at cartouche
+ at format
+Net(find|select|rats|norats|clear[,net[,pin]])
+Net(freeze|thaw|forcethaw)
+Net(add,net,pin)@end format
+ at end cartouche
+
+Perform various actions on netlists.
+ at c ./../src/netlist.c 289
+
+Each of these actions apply to a specified set of nets.  @var{net} and
+ at var{pin} are patterns which match one or more nets or pins; these
+patterns may be full names or regular expressions.  If an exact match
+is found, it is the only match; if no exact match is found,
+ at emph{then} the pattern is tried as a regular expression.
+
+If neither @var{net} nor @var{pin} are specified, all nets apply.  If
+ at var{net} is specified but not @var{pin}, all nets matching @var{net}
+apply.  If both are specified, nets which match @var{net} and contain
+a pin matching @var{pin} apply.
+
+ at table @code
+
+ at item find
+Nets which apply are marked @emph{found} and are drawn in the
+ at code{connected-color} color.
+
+ at item select
+Nets which apply are selected.
+
+ at item rats
+Nets which apply are marked as available for the rats nest.
+
+ at item norats
+Nets which apply are marked as not available for the rats nest.
+
+ at item clear
+Clears the netlist.
+
+ at item add
+Add the given pin to the given netlist, creating either if needed.
+
+ at item sort
+Called after a list of add's, this sorts the netlist.
+
+ at item freeze
+ at itemx thaw
+ at itemx forcethaw
+Temporarily prevents changes to the netlist from being reflected in
+the GUI.  For example, if you need to make multiple changes, you
+freeze the netlist, make the changes, then thaw it.  Note that
+freeze/thaw requests may nest, with the netlist being fully thawed
+only when all pending freezes are thawed.  You can bypass the nesting
+by using forcethaw, which resets the freeze count and immediately
+updates the GUI.
+
+ at end table
+
+
+ at node New Action
+ at subsection New
+ at c key New in hid 
+ at cartouche
+ at format
+New([name])@end format
+ at end cartouche
+
+Starts a new layout.
+ at c ./../src/action.c 5093
+
+If a name is not given, one is prompted for.
+
+
+ at node OptAutoOnly Action
+ at subsection OptAutoOnly
+ at c key OptAutoOnly in hid 
+ at cartouche
+ at format
+OptAutoOnly()@end format
+ at end cartouche
+
+Toggles the optimize-only-autorouted flag.
+ at c ./../src/djopt.c 129
+
+The original purpose of the trace optimizer was to clean up the traces
+created by the various autorouters that have been used with PCB.  When
+a board has a mix of autorouted and carefully hand-routed traces, you
+don't normally want the optimizer to move your hand-routed traces.
+But, sometimes you do.  By default, the optimizer only optimizes
+autorouted traces.  This action toggles that setting, so that you can
+optimize hand-routed traces also.
+
+
+ at node PasteBuffer Action
+ at subsection PasteBuffer
+ at c key PasteBuffer in hid 
+ at cartouche
+ at format
+PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)
+PasteBuffer(Rotate, 1..3)
+PasteBuffer(Convert|Save|Restore|Mirror)
+PasteBuffer(ToLayout, X, Y, units)@end format
+ at end cartouche
+
+Various operations on the paste buffer.
+ at c ./../src/action.c 5153
+
+There are a number of paste buffers; the actual limit is a
+compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}.  It
+is currently @code{5}.  One of these is the ``current'' paste buffer,
+often referred to as ``the'' paste buffer.
+
+ at table @code
+
+ at item AddSelected
+Copies the selected objects to the current paste buffer.
+
+ at item Clear
+Remove all objects from the current paste buffer.
+
+ at item Convert
+Convert the current paste buffer to an element.  Vias are converted to
+pins, lines are converted to pads.
+
+ at item Restore
+Convert any elements in the paste buffer back to vias and lines.
+
+ at item Mirror
+Flip all objects in the paste buffer vertically (up/down flip).  To mirror
+horizontally, combine this with rotations.
+
+ at item Rotate
+Rotates the current buffer.  The number to pass is 1..3, where 1 means
+90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
+degrees clockwise (270 CCW).
+
+ at item Save
+Saves any elements in the current buffer to the indicated file.
+
+ at item ToLayout
+Pastes any elements in the current buffer to the indicated X, Y
+coordinates in the layout.  The @code{X} and @code{Y} are treated like
+ at code{delta} is for many other objects.  For each, if it's prefixed by
+ at code{+} or @code{-}, then that amount is relative to the last
+location.  Otherwise, it's absolute.  Units can be
+ at code{mil} or @code{mm}; if unspecified, units are PCB's internal
+units, currently 1/100 mil.
+
+
+ at item 1..MAX_BUFFER
+Selects the given buffer to be the current paste buffer.
+
+ at end table
+
+
+ at node Polygon Action
+ at subsection Polygon
+ at c key Polygon in hid 
+ at cartouche
+ at format
+Polygon(Close|PreviousPoint)@end format
+ at end cartouche
+
+Some polygon related stuff.
+ at c ./../src/action.c 5503
+
+Polygons need a special action routine to make life easier.
+
+ at table @code
+
+ at item Close
+Creates the final segment of the polygon.  This may fail if clipping
+to 45 degree lines is switched on, in which case a warning is issued.
+
+ at item PreviousPoint
+Resets the newly entered corner to the previous one. The Undo action
+will call Polygon(PreviousPoint) when appropriate to do so.
+
+ at end table
+
+
+ at node Puller Action
+ at subsection Puller
+ at c key Puller in hid 
+ at cartouche
+ at format
+Puller()@end format
+ at end cartouche
+
+Pull an arc-line junction tight.
+ at c ./../src/puller.c 411
+
+The @code{Puller()} action is a special-purpose optimization.  When
+invoked while the crosshair is over the junction of an arc and a line,
+it will adjust the arc's angle and the connecting line's endpoint such
+that the line intersects the arc at a tangent.  In the example below,
+the left side is ``before'' with the black target marking where to put
+the crosshair:
+
+ at center @image{puller,,,Example of how puller works,png}
+
+The right side is ``after'' with the black target marking where the
+arc-line intersection was moved to.
+
+
+ at node q Action
+ at subsection q
+ at c key q in hid 
+ at cartouche
+ at format
+q at end format
+ at end cartouche
+
+Quits the application after confirming.
+ at c ./../src/command.c 185
+
+If you have unsaved changes, you will be prompted to confirm (or
+save) before quitting.
+
+ at colonaction
+
+
+ at node q! Action
+ at subsection q!
+ at c key q! in hid 
+ at cartouche
+ at format
+q!@end format
+ at end cartouche
+
+Quits the application without confirming.
+ at c ./../src/command.c 199
+
+Note that this command neither saves your data nor prompts for
+confirmation.
+
+ at colonaction
+
+
+ at node Quit Action
+ at subsection Quit
+ at c key Quit in hid 
+ at cartouche
+ at format
+Quit()@end format
+ at end cartouche
+
+Quits the application after confirming.
+ at c ./../src/action.c 2071
+
+If you have unsaved changes, you will be prompted to confirm (or
+save) before quitting.
+
+
+ at node Redo Action
+ at subsection Redo
+ at c key Redo in hid 
+ at cartouche
+ at format
+Redo()@end format
+ at end cartouche
+
+Redo recent``undo''operations.
+ at c ./../src/action.c 5468
+
+This routine allows you to recover from the last undo command.  You
+might want to do this if you thought that undo was going to revert
+something other than what it actually did (in case you are confused
+about which operations are un-doable), or if you have been backing up
+through a long undo list and over-shoot your stopping point.  Any
+change that is made since the undo in question will trim the redo
+list.  For example if you add ten lines, then undo three of them you
+could use redo to put them back, but if you move a line on the board
+before performing the redo, you will lose the ability to "redo" the
+three "undone" lines.
+
+
+ at node RemoveSelected Action
+ at subsection RemoveSelected
+ at c key RemoveSelected in hid 
+ at cartouche
+ at format
+RemoveSelected()@end format
+ at end cartouche
+
+Removes any selected objects.
+ at c ./../src/action.c 2832
+
+
+ at node Renumber Action
+ at subsection Renumber
+ at c key Renumber in hid 
+ at cartouche
+ at format
+Renumber()
+Renumber(filename)@end format
+ at end cartouche
+
+Renumber all elements.  The changes will be recorded to filename
+for use in backannotating these changes to the schematic.
+ at c ./../src/action.c 2848
+
+
+ at node Report Action
+ at subsection Report
+ at c key Report in hid 
+ at cartouche
+ at format
+Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])@end format
+ at end cartouche
+
+Produce various report.
+ at c ./../src/report.c 942
+
+ at table @code
+
+ at item Object
+The object under the crosshair will be reported, describing various
+aspects of the object.
+
+ at item DrillReport
+A report summarizing the number of drill sizes used, and how many of
+each, will be produced.
+
+ at item FoundPins
+A report listing all pins and pads which are marked as ``found'' will
+be produced.
+
+ at item NetLength
+The name and length of the net under the crosshair will be reported to
+the message log.
+
+ at item AllNetLengths
+The name and length of the net under the crosshair will be reported to
+the message log.  An optional parameter specifies mm, mil, pcb, or in
+units
+
+ at end table
+
+
+ at node ReportDialog Action
+ at subsection ReportDialog
+ at c key ReportDialog in hid 
+ at cartouche
+ at format
+ReportDialog()@end format
+ at end cartouche
+
+Report on the object under the crosshair
+ at c ./../src/report.c 125
+
+This is a shortcut for @code{Report(Object)}.
+
+
+ at node RipUp Action
+ at subsection RipUp
+ at c key RipUp in hid 
+ at cartouche
+ at format
+RipUp(All|Selected|Element)@end format
+ at end cartouche
+
+Ripup auto-routed tracks, or convert an element to parts.
+ at c ./../src/action.c 3199
+
+ at table @code
+
+ at item All
+Removes all lines and vias which were created by the autorouter.
+
+ at item Selected
+Removes all selected lines and vias which were created by the
+autorouter.
+
+ at item Element
+Converts the element under the cursor to parts (vias and lines).  Note
+that this uses the highest numbered paste buffer.
+
+ at end table
+
+
+ at node rn Action
+ at subsection rn
+ at c key rn in hid 
+ at cartouche
+ at format
+rn [name]@end format
+ at end cartouche
+
+Reads netlist.
+ at c ./../src/command.c 214
+
+If no filename is given a file select box will pop up.  The file is
+read via the command defined by the @emph{RatCommand} resource. The
+command must send its output to @emph{stdout}.
+
+Netlists are used for generating rat's nests (see @ref{Rats Nest}) and
+for verifying the board layout (which is also accomplished by the
+ at emph{Ratsnest} command).
+
+ at colonaction
+
+
+ at node RouteStyle Action
+ at subsection RouteStyle
+ at c key RouteStyle in hid 
+ at cartouche
+ at format
+RouteStyle(1|2|3|4)@end format
+ at end cartouche
+
+Copies the indicated routing style into the current sizes.
+ at c ./../src/action.c 5535
+
+
+ at node s Action
+ at subsection s
+ at c key s in hid 
+ at cartouche
+ at format
+s [name]@end format
+ at end cartouche
+
+Saves layout data.
+ at c ./../src/command.c 244
+
+Data and the filename are passed to the command defined by the
+resource @emph{saveCommand}. It must read the layout data from
+ at emph{stdin}.  If no filename is entered, either the last one is used
+again or, if it is not available, a file select box will pop up.
+
+ at colonaction
+
+
+ at node SaveSettings Action
+ at subsection SaveSettings
+ at c key SaveSettings in hid 
+ at cartouche
+ at format
+SaveSettings()
+SaveSettings(local)@end format
+ at end cartouche
+
+Saves settings.
+ at c ./../src/action.c 5015
+
+If you pass no arguments, the settings are stored in
+ at code{$HOME/.pcb/settings}.  If you pass the word @code{local} they're
+saved in @code{./pcb.settings}.
+
+
+ at node SaveTo Action
+ at subsection SaveTo
+ at c key SaveTo in hid 
+ at cartouche
+ at format
+SaveTo(Layout|LayoutAs,filename)
+SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)
+SaveTo(PasteBuffer,filename)@end format
+ at end cartouche
+
+Saves data to a file.
+ at c ./../src/action.c 4919
+
+ at table @code
+
+ at item Layout
+Saves the current layout.
+
+ at item LayoutAs
+Saves the current layout, and remembers the filename used.
+
+ at item AllConnections
+Save all connections to a file.
+
+ at item AllUnusedPins
+List all unused pins to a file.
+
+ at item ElementConnections
+Save connections to the element at the cursor to a file.
+
+ at item PasteBuffer
+Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
+
+ at end table
+
+
+ at node Select Action
+ at subsection Select
+ at c key Select in hid 
+ at cartouche
+ at format
+Select(Object|ToggleObject)
+Select(All|Block|Connection)
+Select(ElementByName|ObjectByName|PadByName|PinByName)
+Select(ElementByName|ObjectByName|PadByName|PinByName, Name)
+Select(TextByName|ViaByName|NetByName)
+Select(TextByName|ViaByName|NetByName, Name)
+Select(Convert)@end format
+ at end cartouche
+
+Toggles or sets the selection.
+ at c ./../src/action.c 4651
+
+ at table @code
+
+ at item ElementByName
+ at item ObjectByName
+ at item PadByName
+ at item PinByName
+ at item TextByName
+ at item ViaByName
+ at item NetByName
+
+These all rely on having a regular expression parser built into
+ at code{pcb}.  If the name is not specified then the user is prompted
+for a pattern, and all objects that match the pattern and are of the
+type specified are selected.
+
+ at item Object
+ at item ToggleObject
+Selects the object under the cursor.
+
+ at item Block
+Selects all objects in a rectangle indicated by the cursor.
+
+ at item All
+Selects all objects on the board.
+
+ at item Connection
+Selects all connections with the ``found'' flag set.
+
+ at item Convert
+Converts the selected objects to an element.  This uses the highest
+numbered paste buffer.
+
+ at end table
+
+
+ at node SetFlag Action
+ at subsection SetFlag
+ at c key SetFlag in hid 
+ at cartouche
+ at format
+SetFlag(Object|Selected|SelectedObjects, flag)
+SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)
+SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)
+SetFlag(SelectedElements, flag)
+flag = square | octagon | thermal | join at end format
+ at end cartouche
+
+Sets flags on objects.
+ at c ./../src/action.c 5716
+
+Turns the given flag on, regardless of its previous setting.  See
+ at code{ChangeFlag}.
+
+ at example
+SetFlag(SelectedPins,thermal)
+ at end example
+
+
+ at node SetOctagon Action
+ at subsection SetOctagon
+ at c key SetOctagon in hid 
+ at cartouche
+ at format
+SetOctagon(Object|ToggleObject|SelectedElements|Selected)@end format
+ at end cartouche
+
+Sets the octagon-flag of objects.
+ at c ./../src/action.c 4459
+
+ at pinshapes
+
+
+ at node SetSame Action
+ at subsection SetSame
+ at c key SetSame in hid 
+ at cartouche
+ at format
+SetSame()@end format
+ at end cartouche
+
+Sets current layer and sizes to match indicated item.
+ at c ./../src/action.c 5648
+
+When invoked over any line, arc, polygon, or via, this changes the
+current layer to be the layer that item is on, and changes the current
+sizes (thickness, keepaway, drill, etc) according to that item.
+
+
+ at node SetSquare Action
+ at subsection SetSquare
+ at c key SetSquare in hid 
+ at cartouche
+ at format
+SetSquare(ToggleObject|SelectedElements|SelectedPins)@end format
+ at end cartouche
+
+sets the square-flag of objects.
+ at c ./../src/action.c 4301
+
+Note that @code{Pins} means pins and pads.
+
+ at pinshapes
+
+
+ at node SetThermal Action
+ at subsection SetThermal
+ at c key SetThermal in hid 
+ at cartouche
+ at format
+SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)@end format
+ at end cartouche
+
+Set the thermal (on the current layer) of pins or vias to the given style.
+Style = 0 means no thermal.
+Style = 1 has diagonal fingers with sharp edges.
+Style = 2 has horizontal and vertical fingers with sharp edges.
+Style = 3 is a solid connection to the plane.Style = 4 has diagonal fingers with rounded edges.
+Style = 5 has horizontal and vertical fingers with rounded edges.
+
+ at c ./../src/action.c 1912
+
+This changes how/whether pins or vias connect to any rectangle or polygon
+on the current layer. The first argument can specify one object, or all
+selected pins, or all selected vias, or all selected pins and vias.
+The second argument specifies the style of connection.
+There are 5 possibilities:
+0 - no connection,
+1 - 45 degree fingers with sharp edges,
+2 - horizontal & vertical fingers with sharp edges,
+3 - solid connection,
+4 - 45 degree fingers with rounded corners,
+5 - horizontal & vertical fingers with rounded corners.
+
+Pins and Vias may have thermals whether or not there is a polygon available 
+to connect with. However, they will have no effect without the polygon.
+
+ at node SetValue Action
+ at subsection SetValue
+ at c key SetValue in hid 
+ at cartouche
+ at format
+SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)@end format
+ at end cartouche
+
+Change various board-wide values and sizes.
+ at c ./../src/action.c 1996
+
+ at table @code
+
+ at item ViaDrillingHole
+Changes the diameter of the drill for new vias.
+
+ at item Grid
+Sets the grid spacing.
+
+ at item Line
+ at item LineSize
+Changes the thickness of new lines.
+
+ at item Via
+ at item ViaSize
+Changes the diameter of new vias.
+
+ at item Text
+ at item TextScale
+Changes the size of new text.
+
+ at end table
+
+
+ at node ToggleHideName Action
+ at subsection ToggleHideName
+ at c key ToggleHideName in hid 
+ at cartouche
+ at format
+ToggleHideName(Object|SelectedElements)@end format
+ at end cartouche
+
+Toggles the visibility of element names.
+ at c ./../src/action.c 4134
+
+If names are hidden you won't see them on the screen and they will not
+appear on the silk layer when you print the layout.
+
+
+ at node ToggleVendor Action
+ at subsection ToggleVendor
+ at c key ToggleVendor in hid 
+ at cartouche
+ at format
+ToggleVendor()@end format
+ at end cartouche
+
+Toggles the state of automatic drill size mapping.
+ at c ./../src/vendor.c 128
+
+ at cindex vendor map 
+ at cindex vendor drill table
+ at findex ToggleVendor()
+
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.  To enable drill
+mapping, a vendor resource file containing a drill table must be
+loaded first.
+
+
+ at node Undo Action
+ at subsection Undo
+ at c key Undo in hid 
+ at cartouche
+ at format
+Undo()
+Undo(ClearList)@end format
+ at end cartouche
+
+Undo recent changes.
+ at c ./../src/action.c 5304
+
+The unlimited undo feature of @code{Pcb} allows you to recover from
+most operations that materially affect you work.  Calling
+ at code{Undo()} without any parameter recovers from the last (non-undo)
+operation. @code{ClearList} is used to release the allocated
+memory. @code{ClearList} is called whenever a new layout is started or
+loaded. See also @code{Redo} and @code{Atomic}.
+
+Note that undo groups operations by serial number; changes with the
+same serial number will be undone (or redone) as a group.  See
+ at code{Atomic}.
+
+
+ at node UnloadVendor Action
+ at subsection UnloadVendor
+ at c key UnloadVendor in hid 
+ at cartouche
+ at format
+UnloadVendor()@end format
+ at end cartouche
+
+Unloads the current vendor drill mapping table.
+ at c ./../src/vendor.c 176
+
+ at cindex vendor map 
+ at cindex vendor drill table
+ at findex UnloadVendor()
+
+
+ at node Unselect Action
+ at subsection Unselect
+ at c key Unselect in hid 
+ at cartouche
+ at format
+Unselect(All|Block|Connection)
+Unselect(ElementByName|ObjectByName|PadByName|PinByName)
+Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)
+Unselect(TextByName|ViaByName)
+Unselect(TextByName|ViaByName, Name)
+ at end format
+ at end cartouche
+
+Unselects the object at the pointer location or the specified objects.
+ at c ./../src/action.c 4803
+
+ at table @code
+
+ at item All
+Unselect all objects.
+
+ at item Block
+Unselect all objects in a rectangle given by the cursor.
+
+ at item Connection
+Unselect all connections with the ``found'' flag set.
+
+ at item ElementByName
+ at item ObjectByName
+ at item PadByName
+ at item PinByName
+ at item TextByName
+ at item ViaByName
+
+These all rely on having a regular expression parser built into
+ at code{pcb}.  If the name is not specified then the user is prompted
+for a pattern, and all objects that match the pattern and are of the
+type specified are unselected.
+
+
+ at end table
+
+
+ at node w Action
+ at subsection w
+ at c key w in hid 
+ at cartouche
+ at format
+w [name]@end format
+ at end cartouche
+
+Saves layout data.
+ at c ./../src/command.c 250
+
+This commands has been added for the convenience of @code{vi} users
+and has the same functionality as @code{s}.
+
+ at colonaction
+
+
+ at node wq Action
+ at subsection wq
+ at c key wq in hid 
+ at cartouche
+ at format
+wq at end format
+ at end cartouche
+
+Saves the layout data and quits.
+ at c ./../src/command.c 291
+
+This command has been added for the convenience of @code{vi} users and
+has the same functionality as @code{s} combined with @code{q}.
+
+ at colonaction
+
+
+ at node common actions
+ at section common actions
+ at c ./../src/hid/common/actions.c 425
+
+ at macro hidaction
+
+This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+ at end macro
+
+ at menu
+* LayersChanged Action:: Tells the GUI that the layers have changed.
+* LibraryChanged Action:: Tells the GUI that the libraries have changed.
+* NetlistChanged Action:: Tells the GUI that the netlist has changed.
+* PCBChanged Action:: Tells the GUI that the whole PCB has changed. The optional``revert"parameter can be used as a hint to the GUI that the same design is beingreloaded, and that it might keep some viewport settings
+* RouteStylesChanged Action:: Tells the GUI that the routing styles have changed.
+ at end menu
+ at node LayersChanged Action
+ at subsection LayersChanged
+ at c key LayersChanged in hid common
+ at cartouche
+ at format
+LayersChanged()@end format
+ at end cartouche
+
+Tells the GUI that the layers have changed.
+ at c ./../src/hid/common/actions.c 446
+
+This includes layer names, colors, stacking order, visibility, etc.
+
+ at hidaction
+
+
+ at node LibraryChanged Action
+ at subsection LibraryChanged
+ at c key LibraryChanged in hid common
+ at cartouche
+ at format
+LibraryChanged()@end format
+ at end cartouche
+
+Tells the GUI that the libraries have changed.
+ at c ./../src/hid/common/actions.c 451
+
+ at hidaction
+
+
+ at node NetlistChanged Action
+ at subsection NetlistChanged
+ at c key NetlistChanged in hid common
+ at cartouche
+ at format
+NetlistChanged()@end format
+ at end cartouche
+
+Tells the GUI that the netlist has changed.
+ at c ./../src/hid/common/actions.c 441
+
+ at hidaction
+
+
+ at node PCBChanged Action
+ at subsection PCBChanged
+ at c key PCBChanged in hid common
+ at cartouche
+ at format
+PCBChanged([revert])@end format
+ at end cartouche
+
+Tells the GUI that the whole PCB has changed. The optional``revert"parameter can be used as a hint to the GUI that the same design is beingreloaded, and that it might keep some viewport settings
+ at c ./../src/hid/common/actions.c 431
+
+ at hidaction
+
+
+ at node RouteStylesChanged Action
+ at subsection RouteStylesChanged
+ at c key RouteStylesChanged in hid common
+ at cartouche
+ at format
+RouteStylesChanged()@end format
+ at end cartouche
+
+Tells the GUI that the routing styles have changed.
+ at c ./../src/hid/common/actions.c 436
+
+ at hidaction
+
+
+ at node gtk actions
+ at section gtk actions
+ at menu
+* gtk About Action:: N_("Tell the user about this version of PCB.");
+* gtk AdjustStyle Action:: Open the window which allows editing of the route styles.
+* gtk Center Action:: N_("Moves the pointer to the center of the window.");
+* gtk Cursor Action:: N_("Move the cursor.");
+* gtk DoWindows Action:: N_("Open various GUI windows.");
+* gtk EditLayerGroups Action:: Open the preferences window which allows editing of the layer groups.
+* gtk GetXY Action:: N_("Get a coordinate.");
+* gtk ImportGUI Action:: N_("Asks user which schematics to import into PCB.
+");
+* gtk Pan Action:: N_("Start or stop panning (Mode = 1 to start, 0 to stop)
+Optional thumb argument is ignored for now in gtk hid.
+");
+* gtk Popup Action:: N_("Bring up the popup menu specified by @code{MenuName}.
+If called by a mouse event then the mouse button number
+must be specified as the optional second argument.");
+* gtk Print Action:: N_("Print the layout.");
+* gtk PrintCalibrate Action:: N_("Calibrate the printer.");
+* gtk Save Action:: N_("Save layout and/or element data to a user-selected file.");
+* gtk SelectLayer Action:: Select which layer is the current layer.
+* gtk SetUnits Action:: N_("Set the default measurement units.");
+* gtk SwapSides Action:: N_("Swaps the side of the board you're looking at.");
+* gtk ToggleView Action:: Toggle the visibility of the specified layer or layer group.
+* gtk Zoom Action:: N_("Various zoom factor changes.");
+ at end menu
+ at node gtk About Action
+ at subsection gtk About
+ at c key gtk About in hid gtk
+ at cartouche
+ at format
+About()@end format
+ at end cartouche
+
+N_("Tell the user about this version of PCB.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1059
+
+This just pops up a dialog telling the user which version of
+ at code{pcb} they're running.
+
+
+ at node gtk AdjustStyle Action
+ at subsection gtk AdjustStyle
+ at c key gtk AdjustStyle in hid gtk
+ at cartouche
+ at format
+AdjustStyle()
+ at end format
+ at end cartouche
+
+Open the window which allows editing of the route styles.
+ at c ./../src/hid/gtk/gui-top-window.c 2081
+
+Opens the window which allows editing of the route styles.
+
+
+ at node gtk Center Action
+ at subsection gtk Center
+ at c key gtk Center in hid gtk
+ at cartouche
+ at format
+Center()
+ at end format
+ at end cartouche
+
+N_("Moves the pointer to the center of the window.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1470
+
+Move the pointer to the center of the window, but only if it's
+currently within the window already.
+
+
+ at node gtk Cursor Action
+ at subsection gtk Cursor
+ at c key gtk Cursor in hid gtk
+ at cartouche
+ at format
+Cursor(Type,DeltaUp,DeltaRight,Units)@end format
+ at end cartouche
+
+N_("Move the cursor.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1515
+
+This action moves the mouse cursor.  Unlike other actions which take
+coordinates, this action's coordinates are always relative to the
+user's view of the board.  Thus, a positive @var{DeltaUp} may move the
+cursor towards the board origin if the board is inverted.
+
+Type is one of @samp{Pan} or @samp{Warp}.  @samp{Pan} causes the
+viewport to move such that the crosshair is under the mouse cursor.
+ at samp{Warp} causes the mouse cursor to move to be above the crosshair.
+
+ at var{Units} can be one of the following:
+
+ at table @samp
+
+ at item mil
+ at itemx mm
+The cursor is moved by that amount, in board units.
+
+ at item grid
+The cursor is moved by that many grid points.
+
+ at item view
+The values are percentages of the viewport's view.  Thus, a pan of
+ at samp{100} would scroll the viewport by exactly the width of the
+current view.
+
+ at item board
+The values are percentages of the board size.  Thus, a move of
+ at samp{50,50} moves you halfway across the board.
+
+ at end table
+
+
+ at node gtk DoWindows Action
+ at subsection gtk DoWindows
+ at c key gtk DoWindows in hid gtk
+ at cartouche
+ at format
+DoWindows(1|2|3|4|5|6)
+DoWindows(Layout|Library|Log|Netlist|Preferences|DRC)@end format
+ at end cartouche
+
+N_("Open various GUI windows.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1563
+
+ at table @code
+
+ at item 1
+ at itemx Layout
+Open the layout window.  Since the layout window is always shown
+anyway, this has no effect.
+
+ at item 2
+ at itemx Library
+Open the library window.
+
+ at item 3
+ at itemx Log
+Open the log window.
+
+ at item 4
+ at itemx Netlist
+Open the netlist window.
+
+ at item 5
+ at itemx Preferences
+Open the preferences window.
+
+ at item 6
+ at itemx DRC
+Open the DRC violations window.
+
+ at end table
+
+
+ at node gtk EditLayerGroups Action
+ at subsection gtk EditLayerGroups
+ at c key gtk EditLayerGroups in hid gtk
+ at cartouche
+ at format
+EditLayerGroups()
+ at end format
+ at end cartouche
+
+Open the preferences window which allows editing of the layer groups.
+ at c ./../src/hid/gtk/gui-top-window.c 2100
+
+Opens the preferences window which is where the layer groups
+are edited.  This action is primarily provides to provide menu
+resource compatibility with the lesstif HID.
+
+
+ at node gtk GetXY Action
+ at subsection gtk GetXY
+ at c key gtk GetXY in hid gtk
+ at cartouche
+ at format
+GetXY()@end format
+ at end cartouche
+
+N_("Get a coordinate.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1074
+
+Prompts the user for a coordinate, if one is not already selected.
+
+
+ at node gtk ImportGUI Action
+ at subsection gtk ImportGUI
+ at c key gtk ImportGUI in hid gtk
+ at cartouche
+ at format
+ImportGUI()@end format
+ at end cartouche
+
+N_("Asks user which schematics to import into PCB.
+");
+ at c ./../src/hid/gtk/gtkhid-main.c 1750
+
+Asks user which schematics to import into PCB.
+
+
+ at node gtk Pan Action
+ at subsection gtk Pan
+ at c key gtk Pan in hid gtk
+ at cartouche
+ at format
+Pan([thumb], Mode)@end format
+ at end cartouche
+
+N_("Start or stop panning (Mode = 1 to start, 0 to stop)
+Optional thumb argument is ignored for now in gtk hid.
+");
+ at c ./../src/hid/gtk/gtkhid-main.c 1687
+
+Start or stop panning.  To start call with Mode = 1, to stop call with
+Mode = 0.
+
+
+ at node gtk Popup Action
+ at subsection gtk Popup
+ at c key gtk Popup in hid gtk
+ at cartouche
+ at format
+Popup(MenuName, [Button])@end format
+ at end cartouche
+
+N_("Bring up the popup menu specified by @code{MenuName}.
+If called by a mouse event then the mouse button number
+must be specified as the optional second argument.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1719
+
+This just pops up the specified menu.  The menu must have been defined
+as a named subresource of the Popups resource in the menu resource
+file.
+
+
+ at node gtk Print Action
+ at subsection gtk Print
+ at c key gtk Print in hid gtk
+ at cartouche
+ at format
+Print()@end format
+ at end cartouche
+
+N_("Print the layout.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1350
+
+This will find the default printing HID, prompt the user for its
+options, and print the layout.
+
+
+ at node gtk PrintCalibrate Action
+ at subsection gtk PrintCalibrate
+ at c key gtk PrintCalibrate in hid gtk
+ at cartouche
+ at format
+PrintCalibrate()@end format
+ at end cartouche
+
+N_("Calibrate the printer.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1401
+
+This will print a calibration page, which you would measure and type
+the measurements in, so that future printouts will be more precise.
+
+
+ at node gtk Save Action
+ at subsection gtk Save
+ at c key gtk Save in hid gtk
+ at cartouche
+ at format
+Save()
+Save(Layout|LayoutAs)
+Save(AllConnections|AllUnusedPins|ElementConnections)
+Save(PasteBuffer)@end format
+ at end cartouche
+
+N_("Save layout and/or element data to a user-selected file.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1230
+
+This action is a GUI front-end to the core's @code{SaveTo} action
+(@pxref{SaveTo Action}).  If you happen to pass a filename, like
+ at code{SaveTo}, then @code{SaveTo} is called directly.  Else, the
+user is prompted for a filename to save, and then @code{SaveTo} is
+called with that filename.
+
+
+ at node gtk SelectLayer Action
+ at subsection gtk SelectLayer
+ at c key gtk SelectLayer in hid gtk
+ at cartouche
+ at format
+SelectLayer(1..MAXLAYER|Silk|Rats)@end format
+ at end cartouche
+
+Select which layer is the current layer.
+ at c ./../src/hid/gtk/gui-top-window.c 1851
+
+The specified layer becomes the currently active layer.  It is made
+visible if it is not already visible
+
+
+ at node gtk SetUnits Action
+ at subsection gtk SetUnits
+ at c key gtk SetUnits in hid gtk
+ at cartouche
+ at format
+SetUnits(mm|mil)@end format
+ at end cartouche
+
+N_("Set the default measurement units.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1606
+
+ at table @code
+
+ at item mil
+Sets the display units to mils (1/1000 inch).
+
+ at item mm
+Sets the display units to millimeters.
+
+ at end table
+
+
+ at node gtk SwapSides Action
+ at subsection gtk SwapSides
+ at c key gtk SwapSides in hid gtk
+ at cartouche
+ at format
+SwapSides(|v|h|r)@end format
+ at end cartouche
+
+N_("Swaps the side of the board you're looking at.");
+ at c ./../src/hid/gtk/gtkhid-main.c 1295
+
+This action changes the way you view the board.
+
+ at table @code
+
+ at item v
+Flips the board over vertically (up/down).
+
+ at item h
+Flips the board over horizontally (left/right), like flipping pages in
+a book.
+
+ at item r
+Rotates the board 180 degrees without changing sides.
+
+ at end table
+
+If no argument is given, the board isn't moved but the opposite side
+is shown.
+
+Normally, this action changes which pads and silk layer are drawn as
+true silk, and which are drawn as the "invisible" layer.  It also
+determines which solder mask you see.
+
+As a special case, if the layer group for the side you're looking at
+is visible and currently active, and the layer group for the opposite
+is not visible (i.e. disabled), then this action will also swap which
+layer group is visible and active, effectively swapping the ``working
+side'' of the board.
+
+
+ at node gtk ToggleView Action
+ at subsection gtk ToggleView
+ at c key gtk ToggleView in hid gtk
+ at cartouche
+ at format
+ToggleView(1..MAXLAYER)
+ToggleView(layername)
+ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)@end format
+ at end cartouche
+
+Toggle the visibility of the specified layer or layer group.
+ at c ./../src/hid/gtk/gui-top-window.c 1792
+
+If you pass an integer, that layer is specified by index (the first
+layer is @code{1}, etc).  If you pass a layer name, that layer is
+specified by name.  When a layer is specified, the visibility of the
+layer group containing that layer is toggled.
+
+If you pass a special layer name, the visibility of those components
+(silk, rats, etc) is toggled.  Note that if you have a layer named
+the same as a special layer, the layer is chosen over the special layer.
+
+
+ at node gtk Zoom Action
+ at subsection gtk Zoom
+ at c key gtk Zoom in hid gtk
+ at cartouche
+ at format
+Zoom()
+Zoom(factor)@end format
+ at end cartouche
+
+N_("Various zoom factor changes.");
+ at c ./../src/hid/gtk/gtkhid-main.c 161
+Changes the zoom (magnification) of the view of the board.  If no
+arguments are passed, the view is scaled such that the board just fits
+inside the visible window (i.e. ``view all'').  Otherwise,
+ at var{factor} specifies a change in zoom factor.  It may be prefixed by
+ at code{+}, @code{-}, or @code{=} to change how the zoom factor is
+modified.  The @var{factor} is a floating point number, such as
+ at code{1.5} or @code{0.75}.
+
+ at table @code
+  
+ at item + at var{factor}
+Values greater than 1.0 cause the board to be drawn smaller; more of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn bigger; less of the board will be visible.
+  
+ at item - at var{factor}
+Values greater than 1.0 cause the board to be drawn bigger; less of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn smaller; more of the board will be visible.
+ 
+ at item =@var{factor}
+ 
+The @var{factor} is an absolute zoom factor; the unit for this value
+is "PCB units per screen pixel".  Since PCB units are 0.01 mil, a
+ at var{factor} of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI,
+about the actual resolution of most screens - resulting in an "actual
+size" board.  Similarly, a @var{factor} of 100 gives you a 10x actual
+size.
+ 
+ at end table
+ 
+Note that zoom factors of zero are silently ignored.
+ 
+
+
+
+ at node lesstif actions
+ at section lesstif actions
+ at menu
+* lesstif About Action:: Tell the user about this version of PCB.
+* lesstif AdjustSizes Action:: Let the user change the board size, DRC parameters, etc
+* lesstif AdjustStyle Action:: Displays the route style adjustment window.
+* lesstif Benchmark Action:: Benchmark the GUI speed.
+* lesstif Command Action:: Displays the command line input window.
+* lesstif Cursor Action:: Move the cursor.
+* lesstif Debug Action:: Debug action.
+* lesstif DebugXY Action:: Debug action, with coordinates
+* lesstif DoWindows Action:: Open various GUI windows.
+* lesstif DumpKeys Action:: Dump Lesstif key bindings.
+* lesstif EditLayerGroups Action:: Let the user change the layer groupings
+* lesstif Export Action:: Export the layout.
+* lesstif GetXY Action:: Get a coordinate.
+* lesstif ImportGUI Action:: Lets the user choose the schematics to import from
+* lesstif LibraryShow Action:: Displays the library window.
+* lesstif Load Action:: Load layout data from a user-selected file.
+* lesstif LoadVendor Action:: Loads a user-selected vendor resource file.
+* lesstif NetlistShow Action:: Selects the given pinname or netname in the netlist window.
+* lesstif Print Action:: Print the layout.
+* lesstif PrintCalibrate Action:: Calibrate the printer.
+* lesstif PromptFor Action:: Prompt for a response.
+* lesstif Return Action:: Simulate a passing or failing action.
+* lesstif Save Action:: Save layout data to a user-selected file.
+* lesstif SelectLayer Action:: Select which layer is the current layer.
+* lesstif SetUnits Action:: Set the default measurement units.
+* lesstif SwapSides Action:: Swaps the side of the board you're looking at.
+* lesstif ToggleView Action:: Toggle the visibility of the specified layer or layer group.
+* lesstif Zoom Action:: Various zoom factor changes.
+ at end menu
+ at node lesstif About Action
+ at subsection lesstif About
+ at c key lesstif About in hid lesstif
+ at cartouche
+ at format
+About()@end format
+ at end cartouche
+
+Tell the user about this version of PCB.
+ at c ./../src/hid/lesstif/dialogs.c 867
+
+This just pops up a dialog telling the user which version of
+ at code{pcb} they're running.
+
+
+ at node lesstif AdjustSizes Action
+ at subsection lesstif AdjustSizes
+ at c key lesstif AdjustSizes in hid lesstif
+ at cartouche
+ at format
+AdjustSizes()@end format
+ at end cartouche
+
+Let the user change the board size, DRC parameters, etc
+ at c ./../src/hid/lesstif/dialogs.c 1136
+
+Displays a dialog box that lets the user change the board
+size, DRC parameters, and text scale.
+
+The units are determined by the default display units.
+
+
+ at node lesstif AdjustStyle Action
+ at subsection lesstif AdjustStyle
+ at c key lesstif AdjustStyle in hid lesstif
+ at cartouche
+ at format
+AdjustStyle()@end format
+ at end cartouche
+
+Displays the route style adjustment window.
+ at c ./../src/hid/lesstif/styles.c 344
+
+
+ at node lesstif Benchmark Action
+ at subsection lesstif Benchmark
+ at c key lesstif Benchmark in hid lesstif
+ at cartouche
+ at format
+Benchmark()@end format
+ at end cartouche
+
+Benchmark the GUI speed.
+ at c ./../src/hid/lesstif/main.c 659
+
+This action is used to speed-test the Lesstif graphics subsystem.  It
+redraws the current screen as many times as possible in ten seconds.
+It reports the amount of time needed to draw the screen once.
+
+
+ at node lesstif Command Action
+ at subsection lesstif Command
+ at c key lesstif Command in hid lesstif
+ at cartouche
+ at format
+Command()@end format
+ at end cartouche
+
+Displays the command line input window.
+ at c ./../src/hid/lesstif/main.c 644
+
+The command window allows the user to manually enter actions to be
+executed.  Action syntax can be done one of two ways:
+
+ at table @code
+
+ at item
+Follow the action name by an open parenthesis, arguments separated by
+commas, end with a close parenthesis.  Example: @code{Abc(1,2,3)}
+
+ at item
+Separate the action name and arguments by spaces.  Example: @code{Abc
+1 2 3}.
+
+ at end table
+
+The first option allows you to have arguments with spaces in them,
+but the second is more ``natural'' to type for most people.
+
+Note that action names are not case sensitive, but arguments normally
+are.  However, most actions will check for ``keywords'' in a case
+insensitive way.
+
+There are three ways to finish with the command window.  If you press
+the @code{Enter} key, the command is invoked, the window goes away,
+and the next time you bring up the command window it's empty.  If you
+press the @code{Esc} key, the window goes away without invoking
+anything, and the next time you bring up the command window it's
+empty.  If you change focus away from the command window (i.e. click
+on some other window), the command window goes away but the next time
+you bring it up it resumes entering the command you were entering
+before.
+
+
+ at node lesstif Cursor Action
+ at subsection lesstif Cursor
+ at c key lesstif Cursor in hid lesstif
+ at cartouche
+ at format
+Cursor(Type,DeltaUp,DeltaRight,Units)@end format
+ at end cartouche
+
+Move the cursor.
+ at c ./../src/hid/lesstif/main.c 716
+
+This action moves the mouse cursor.  Unlike other actions which take
+coordinates, this action's coordinates are always relative to the
+user's view of the board.  Thus, a positive @var{DeltaUp} may move the
+cursor towards the board origin if the board is inverted.
+
+Type is one of @samp{Pan} or @samp{Warp}.  @samp{Pan} causes the
+viewport to move such that the crosshair is under the mouse cursor.
+ at samp{Warp} causes the mouse cursor to move to be above the crosshair.
+
+ at var{Units} can be one of the following:
+
+ at table @samp
+
+ at item mil
+ at itemx mm
+The cursor is moved by that amount, in board units.
+
+ at item grid
+The cursor is moved by that many grid points.
+
+ at item view
+The values are percentages of the viewport's view.  Thus, a pan of
+ at samp{100} would scroll the viewport by exactly the width of the
+current view.
+
+ at item board
+The values are percentages of the board size.  Thus, a move of
+ at samp{50,50} moves you halfway across the board.
+
+ at end table
+
+
+ at node lesstif Debug Action
+ at subsection lesstif Debug
+ at c key lesstif Debug in hid lesstif
+ at cartouche
+ at format
+Debug(...)@end format
+ at end cartouche
+
+Debug action.
+ at c ./../src/hid/lesstif/menu.c 66
+
+This action exists to help debug scripts; it simply prints all its
+arguments to stdout.
+
+
+ at node lesstif DebugXY Action
+ at subsection lesstif DebugXY
+ at c key lesstif DebugXY in hid lesstif
+ at cartouche
+ at format
+DebugXY(...)@end format
+ at end cartouche
+
+Debug action, with coordinates
+ at c ./../src/hid/lesstif/menu.c 72
+
+Like @code{Debug}, but requires a coordinate.  If the user hasn't yet
+indicated a location on the board, the user will be prompted to click
+on one.
+
+
+ at node lesstif DoWindows Action
+ at subsection lesstif DoWindows
+ at c key lesstif DoWindows in hid lesstif
+ at cartouche
+ at format
+DoWindows(1|2|3|4)
+DoWindows(Layout|Library|Log|Netlist)@end format
+ at end cartouche
+
+Open various GUI windows.
+ at c ./../src/hid/lesstif/dialogs.c 831
+
+ at table @code
+
+ at item 1
+ at itemx Layout
+Open the layout window.  Since the layout window is always shown
+anyway, this has no effect.
+
+ at item 2
+ at itemx Library
+Open the library window.
+
+ at item 3
+ at itemx Log
+Open the log window.
+
+ at item 4
+ at itemx Netlist
+Open the netlist window.
+
+ at end table
+
+
+ at node lesstif DumpKeys Action
+ at subsection lesstif DumpKeys
+ at c key lesstif DumpKeys in hid lesstif
+ at cartouche
+ at format
+DumpKeys()@end format
+ at end cartouche
+
+Dump Lesstif key bindings.
+ at c ./../src/hid/lesstif/menu.c 101
+
+Causes the list of key bindings (from @code{pcb-menu.res}) to be
+dumped to stdout.  This is most useful when invoked from the command
+line like this:
+
+ at example
+pcb --action-string DumpKeys
+ at end example
+
+
+ at node lesstif EditLayerGroups Action
+ at subsection lesstif EditLayerGroups
+ at c key lesstif EditLayerGroups in hid lesstif
+ at cartouche
+ at format
+EditLayerGroups()@end format
+ at end cartouche
+
+Let the user change the layer groupings
+ at c ./../src/hid/lesstif/dialogs.c 1448
+
+Displays a dialog that lets the user view and change the layer
+groupings.  Each layer (row) can be a member of any one layer group
+(column).  Note the special layers @code{solder} and @code{component}
+allow you to specify which groups represent the top and bottom of the
+board.
+
+See @ref{ChangeName Action}.
+
+
+ at node lesstif Export Action
+ at subsection lesstif Export
+ at c key lesstif Export in hid lesstif
+ at cartouche
+ at format
+Export()@end format
+ at end cartouche
+
+Export the layout.
+ at c ./../src/hid/lesstif/dialogs.c 957
+
+Prompts the user for an exporter to use.  Then, prompts the user for
+that exporter's options, and exports the layout.
+
+
+ at node lesstif GetXY Action
+ at subsection lesstif GetXY
+ at c key lesstif GetXY in hid lesstif
+ at cartouche
+ at format
+GetXY()@end format
+ at end cartouche
+
+Get a coordinate.
+ at c ./../src/hid/lesstif/menu.c 54
+
+Prompts the user for a coordinate, if one is not already selected.
+
+
+ at node lesstif ImportGUI Action
+ at subsection lesstif ImportGUI
+ at c key lesstif ImportGUI in hid lesstif
+ at cartouche
+ at format
+ImportGUI()@end format
+ at end cartouche
+
+Lets the user choose the schematics to import from
+ at c ./../src/hid/lesstif/dialogs.c 1882
+
+Displays a dialog that lets the user select the schematic(s) to import
+from, then saves that information in the layout's attributes for
+future imports.
+
+
+ at node lesstif LibraryShow Action
+ at subsection lesstif LibraryShow
+ at c key lesstif LibraryShow in hid lesstif
+ at cartouche
+ at format
+LibraryShow()@end format
+ at end cartouche
+
+Displays the library window.
+ at c ./../src/hid/lesstif/library.c 151
+
+
+ at node lesstif Load Action
+ at subsection lesstif Load
+ at c key lesstif Load in hid lesstif
+ at cartouche
+ at format
+Load()
+Load(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert)@end format
+ at end cartouche
+
+Load layout data from a user-selected file.
+ at c ./../src/hid/lesstif/dialogs.c 99
+
+This action is a GUI front-end to the core's @code{LoadFrom} action
+(@pxref{LoadFrom Action}).  If you happen to pass a filename, like
+ at code{LoadFrom}, then @code{LoadFrom} is called directly.  Else, the
+user is prompted for a filename to load, and then @code{LoadFrom} is
+called with that filename.
+
+
+ at node lesstif LoadVendor Action
+ at subsection lesstif LoadVendor
+ at c key lesstif LoadVendor in hid lesstif
+ at cartouche
+ at format
+LoadVendor()@end format
+ at end cartouche
+
+Loads a user-selected vendor resource file.
+ at c ./../src/hid/lesstif/dialogs.c 152
+
+The user is prompted for a file to load, and then
+ at code{LoadVendorFrom} is called (@pxref{LoadVendorFrom Action}) to
+load that vendor file.
+
+
+ at node lesstif NetlistShow Action
+ at subsection lesstif NetlistShow
+ at c key lesstif NetlistShow in hid lesstif
+ at cartouche
+ at format
+NetlistShow(pinname|netname)@end format
+ at end cartouche
+
+Selects the given pinname or netname in the netlist window.
+ at c ./../src/hid/lesstif/netlist.c 415
+
+
+ at node lesstif Print Action
+ at subsection lesstif Print
+ at c key lesstif Print in hid lesstif
+ at cartouche
+ at format
+Print()@end format
+ at end cartouche
+
+Print the layout.
+ at c ./../src/hid/lesstif/dialogs.c 894
+
+This will find the default printing HID, prompt the user for its
+options, and print the layout.
+
+
+ at node lesstif PrintCalibrate Action
+ at subsection lesstif PrintCalibrate
+ at c key lesstif PrintCalibrate in hid lesstif
+ at cartouche
+ at format
+PrintCalibrate()@end format
+ at end cartouche
+
+Calibrate the printer.
+ at c ./../src/hid/lesstif/dialogs.c 937
+
+This will print a calibration page, which you would measure and type
+the measurements in, so that future printouts will be more precise.
+
+
+ at node lesstif PromptFor Action
+ at subsection lesstif PromptFor
+ at c key lesstif PromptFor in hid lesstif
+ at cartouche
+ at format
+PromptFor([message[,default]])@end format
+ at end cartouche
+
+Prompt for a response.
+ at c ./../src/hid/lesstif/dialogs.c 560
+
+This is mostly for testing the lesstif HID interface.  The parameters
+are passed to the @code{prompt_for()} HID function, causing the user
+to be prompted for a response.  The respose is simply printed to the
+user's stdout.
+
+
+ at node lesstif Return Action
+ at subsection lesstif Return
+ at c key lesstif Return in hid lesstif
+ at cartouche
+ at format
+Return(0|1)@end format
+ at end cartouche
+
+Simulate a passing or failing action.
+ at c ./../src/hid/lesstif/menu.c 89
+
+This is for testing.  If passed a 0, does nothing and succeeds.  If
+passed a 1, does nothing but pretends to fail.
+
+
+ at node lesstif Save Action
+ at subsection lesstif Save
+ at c key lesstif Save in hid lesstif
+ at cartouche
+ at format
+Save()
+Save(Layout|LayoutAs)
+Save(AllConnections|AllUnusedPins|ElementConnections)
+Save(PasteBuffer)@end format
+ at end cartouche
+
+Save layout data to a user-selected file.
+ at c ./../src/hid/lesstif/dialogs.c 197
+
+This action is a GUI front-end to the core's @code{SaveTo} action
+(@pxref{SaveTo Action}).  If you happen to pass a filename, like
+ at code{SaveTo}, then @code{SaveTo} is called directly.  Else, the
+user is prompted for a filename to save, and then @code{SaveTo} is
+called with that filename.
+
+
+ at node lesstif SelectLayer Action
+ at subsection lesstif SelectLayer
+ at c key lesstif SelectLayer in hid lesstif
+ at cartouche
+ at format
+SelectLayer(1..MAXLAYER|Silk|Rats)@end format
+ at end cartouche
+
+Select which layer is the current layer.
+ at c ./../src/hid/lesstif/menu.c 387
+
+The specified layer becomes the currently active layer.  It is made
+visible if it is not already visible
+
+
+ at node lesstif SetUnits Action
+ at subsection lesstif SetUnits
+ at c key lesstif SetUnits in hid lesstif
+ at cartouche
+ at format
+SetUnits(mm|mil)@end format
+ at end cartouche
+
+Set the default measurement units.
+ at c ./../src/hid/lesstif/main.c 395
+
+ at table @code
+
+ at item mil
+Sets the display units to mils (1/1000 inch).
+
+ at item mm
+Sets the display units to millimeters.
+
+ at end table
+
+
+ at node lesstif SwapSides Action
+ at subsection lesstif SwapSides
+ at c key lesstif SwapSides in hid lesstif
+ at cartouche
+ at format
+SwapSides(|v|h|r)@end format
+ at end cartouche
+
+Swaps the side of the board you're looking at.
+ at c ./../src/hid/lesstif/main.c 494
+
+This action changes the way you view the board.
+
+ at table @code
+
+ at item v
+Flips the board over vertically (up/down).
+
+ at item h
+Flips the board over horizontally (left/right), like flipping pages in
+a book.
+
+ at item r
+Rotates the board 180 degrees without changing sides.
+
+ at end table
+
+If no argument is given, the board isn't moved but the opposite side
+is shown.
+
+Normally, this action changes which pads and silk layer are drawn as
+true silk, and which are drawn as the "invisible" layer.  It also
+determines which solder mask you see.
+
+As a special case, if the layer group for the side you're looking at
+is visible and currently active, and the layer group for the opposite
+is not visible (i.e. disabled), then this action will also swap which
+layer group is visible and active, effectively swapping the ``working
+side'' of the board.
+
+
+ at node lesstif ToggleView Action
+ at subsection lesstif ToggleView
+ at c key lesstif ToggleView in hid lesstif
+ at cartouche
+ at format
+ToggleView(1..MAXLAYER)
+ToggleView(layername)
+ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)@end format
+ at end cartouche
+
+Toggle the visibility of the specified layer or layer group.
+ at c ./../src/hid/lesstif/menu.c 409
+
+If you pass an integer, that layer is specified by index (the first
+layer is @code{1}, etc).  If you pass a layer name, that layer is
+specified by name.  When a layer is specified, the visibility of the
+layer group containing that layer is toggled.
+
+If you pass a special layer name, the visibility of those components
+(silk, rats, etc) is toggled.  Note that if you have a layer named
+the same as a special layer, the layer is chosen over the special layer.
+
+
+ at node lesstif Zoom Action
+ at subsection lesstif Zoom
+ at c key lesstif Zoom in hid lesstif
+ at cartouche
+ at format
+Zoom()
+Zoom(factor)@end format
+ at end cartouche
+
+Various zoom factor changes.
+ at c ./../src/hid/lesstif/main.c 419
+
+Changes the zoom (magnification) of the view of the board.  If no
+arguments are passed, the view is scaled such that the board just fits
+inside the visible window (i.e. ``view all'').  Otherwise,
+ at var{factor} specifies a change in zoom factor.  It may be prefixed by
+ at code{+}, @code{-}, or @code{=} to change how the zoom factor is
+modified.  The @var{factor} is a floating point number, such as
+ at code{1.5} or @code{0.75}.
+
+ at table @code
+
+ at item + at var{factor}
+Values greater than 1.0 cause the board to be drawn smaller; more of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn bigger; less of the board will be visible.
+
+ at item - at var{factor}
+Values greater than 1.0 cause the board to be drawn bigger; less of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn smaller; more of the board will be visible.
+
+ at item =@var{factor}
+
+The @var{factor} is an absolute zoom factor; the unit for this value
+is "PCB units per screen pixel".  Since PCB units are 0.01 mil, a
+ at var{factor} of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI,
+about the actual resolution of most screens - resulting in an "actual
+size" board.  Similarly, a @var{factor} of 100 gives you a 10x actual
+size.
+
+ at end table
+
+Note that zoom factors of zero are silently ignored.
+
+
diff --git a/doc-orig/eps2png b/doc-orig/eps2png
new file mode 100755
index 0000000..c029991
--- /dev/null
+++ b/doc-orig/eps2png
@@ -0,0 +1,495 @@
+#!/usr/bin/perl
+
+my $RCS_Id = '$Id$ ';
+
+# Author          : Johan Vromans
+# Created On      : Tue Sep 15 15:59:04 1992
+# Last Modified By: Johan Vromans
+# Last Modified On: Sun Jun 24 17:07:29 2001
+# Update Count    : 155
+# Status          : Okay
+
+################ Common stuff ################
+
+use strict;
+use Getopt::Long 2.1;
+
+my $my_package = "Sciurix";
+my ($my_name, $my_version) = $RCS_Id =~ /: (.+).pl,v ([\d.]+)/;
+$my_version .= '*' if length('$Locker$ ') > 12;
+
+use vars qw($VERSION);
+( $VERSION ) = '$Revision$ ' =~ /\$Revision:\s+([^\s]+)/;
+
+################ Program parameters ################
+
+### CONFIG
+# Some GhostScript programs can produce GIF directly.
+# If not, we need the PBM package for the conversion.
+my $use_pbm = 1;		# GhostScript can not produce GIF
+### END CONFIG
+
+my $res = 82;			# default resolution
+my $scale = 1;			# default scaling
+my $mono = 0;			# produce BW images if non-zero
+my $format;			# output format
+my $gs_format;			# GS output type
+my $output;			# output, defaults to STDOUT
+my $antialias = 4;              # antialiasing
+my $width;			# desired widht
+my $height;			# desired height
+
+my ($verbose,$trace,$test,$debug) = (0,0,0,0);
+handle_options ();
+unless ( defined $format ) {
+    if ( $0 =~ /2(gif|jpg|png)$/ ) {
+	set_out_type ($1);
+    }
+    else {
+	set_out_type ('png') unless defined $format;
+    }
+}
+print STDERR ("Producing $format ($gs_format) image.\n") if $verbose;
+
+$trace |= $test | $debug;
+$verbose |= $trace;
+
+################ Presets ################
+
+################ The Process ################
+
+my $eps_file;
+my $err = 0;
+
+foreach $eps_file ( @ARGV ) {
+
+    unless ( open (EPS, $eps_file) ) {
+	print STDERR ("Cannot open $eps_file [$!], skipped\n");
+	$err++;
+	next;
+    }
+
+    my $line = <EPS>;
+    unless ( $line =~ /^%!PS-Adobe.*EPSF-/ ) {
+	print STDERR ("Not EPS file: $eps_file, skipped\n");
+	$err++;
+	next;
+    }
+
+    my $ps = "";		# PostScript input data
+    my $xscale;
+    my $yscale;
+
+    while ( $line = <EPS> ) {
+
+	# Search for BoundingBox.
+	if ( $line =~ /^%%BoundingBox:\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/i ) {
+
+	    print STDERR ("$eps_file: x0=$1, y0=$2, w=", $3-$1, ", h=", $4-$2)
+		if $verbose;
+
+	    if ( defined $width ) {
+		$res = 72;
+		$xscale = $width / ($3 - $1);
+		if ( defined $height ) {
+		    $yscale = $height / ($4 - $2);
+		}
+		else {
+		    $yscale = $xscale;
+		    $height = ($4 - $2) * $yscale;
+		}
+	    }
+	    elsif ( defined $height ) {
+		$res = 72;
+		$yscale = $height / ($4 - $2);
+		if ( defined $width ) {
+		    $xscale = $width / ($3 - $1);
+		}
+		else {
+		    $xscale = $yscale;
+		    $width = ($3 - $1) * $xscale;
+		}
+	    }
+	    unless ( defined $xscale ) {
+		$xscale = $yscale = $scale;
+		# Calculate actual width.
+		$width  = $3 - $1;
+		$height = $4 - $2;
+		# Normal PostScript resolution is 72.
+		$width  *= $res/72 * $xscale;
+		$height *= $res/72 * $yscale;
+		# Round up.
+		$width  = int ($width + 0.5) + 1;
+		$height = int ($height + 0.5) + 1;
+	    }
+	    print STDERR (", width=$width, height=$height\n") if $verbose;
+
+	    # Scale.
+	    $ps .= "$xscale $yscale scale\n"
+	      if $xscale != 1 || $yscale != 1;
+
+	    # Create PostScript code to translate coordinates.
+	    $ps .= (0-$1) . " " . (0-$2) . " translate\n"
+	      unless $1 == 0 && $2 == 0;
+
+	    # Include the image, show and quit.
+	    $ps .= "($eps_file) run\n".
+	      "showpage\n".
+		"quit\n";
+
+	    last;
+	}
+	elsif ( $line =~ /^%%EndComments/i ) {
+	    print STDERR ("No bounding box in $eps_file\n");
+	    $err++;
+	    last;
+	}
+    }
+    close (EPS);
+
+    my $out_file;		# output file
+    my $pbm_file;		# temporary file for PBM conversion
+
+    # Note the temporary PBM file is created where the output file is
+    # located, since that will guarantee accessibility (and a valid
+    # filename).
+    if ( defined $output ) {
+	$out_file = $output;
+	$pbm_file = $output.".ppm";
+    }
+    elsif ( $eps_file =~ /^(.+).epsf?$/i ) {
+	$out_file = "$1.$format";
+	$pbm_file = $1.".ppm";
+    }
+    else {
+	$out_file = $eps_file . ".$format";
+	$pbm_file = $eps_file . ".ppm";
+    }
+    print STDERR ("Creating $out_file\n") if $verbose;
+
+    my $gs0 = "gs -q -dNOPAUSE -r$res -g${width}x$height";
+    my $gs1 = "-";
+    $gs0 .= " -dTextAlphaBits=$antialias -dGraphicsAlphaBits=$antialias"
+      if $antialias;
+    if ( $format eq 'png' ) {
+	mysystem ("$gs0 -sDEVICE=". ($mono ? "pngmono" : $gs_format).
+		  " -sOutputFile=$out_file $gs1", $ps);
+    }
+    elsif ( $format eq 'jpg' ) {
+	mysystem ("$gs0 -sDEVICE=". ($mono ? "jpeggray" : $gs_format).
+		  " -sOutputFile=$out_file $gs1", $ps);
+    }
+    elsif ( $format eq 'gif' ) {
+	if ( $use_pbm ) {
+	    # Convert to PPM and use some of the PBM converters.
+	    mysystem ("$gs0 -sDEVICE=". ($mono ? "pbm" : "ppm").
+		      " -sOutputFile=$pbm_file $gs1", $ps);
+	    # mysystem ("pnmcrop $pbm_file | ppmtogif > $out_file");
+	    mysystem ("ppmtogif $pbm_file > $out_file");
+	    unlink ($pbm_file);
+	}
+	else {
+	    # GhostScript has GIF drivers built-in.
+	    mysystem ("$gs0 -sDEVICE=". ($mono ? "gifmono" : "gif8").
+		      " -sOutputFile=$out_file $gs1", $ps);
+	}
+    }
+    else {
+	print STDERR ("ASSERT ERROR: Unhandled output type: $format\n");
+	exit (1);
+    }
+
+    unless ( -s $out_file ) {
+	print STDERR ("Problem creating $out_file for $eps_file\n");
+	$err++;
+    }
+
+}
+
+exit 1 if $err;
+
+################ Subroutines ################
+
+sub mysystem {
+    my ($cmd, $data) = @_;
+    print STDERR ("+ $cmd\n") if $trace;
+    if ( $data ) {
+	if ( $trace ) {
+	    my $dp = ">> " . $data;
+	    $dp =~ s/\n(.)/\n>> $1/g;
+	    print STDERR ("$dp");
+	}
+	open (CMD, "|$cmd") or die ("cmd: $!\n");
+	print CMD $data;
+	close CMD or die ("cmd close: $!\n");
+    }
+    else {
+	system ($cmd);
+    }
+}
+
+sub set_out_type {
+    my ($opt) = lc (shift (@_));
+    if ( $opt =~ /^png(mono|gray|16|256|16m)?$/ ) {
+	$format = 'png';
+	$gs_format = $format.(defined $1 ? $1 : '16m');
+    }
+    elsif ( $opt =~ /^gif(mono)?$/ ) {
+	$format = 'gif';
+	$gs_format = $format.(defined $1 ? $1 : '');
+    }
+    elsif ( $opt =~ /^(jpg|jpeg)(gray)?$/ ) {
+	$format = 'jpg';
+	$gs_format = 'jpeg'.(defined $2 ? $2 : '');
+    }
+    else {
+	print STDERR ("ASSERT ERROR: Invalid value to set_out_type: $opt\n");
+	exit (1);
+    }
+}
+
+sub handle_options {
+    my  ($help) = 0;		# handled locally
+    my ($ident) = 0;		# handled locally
+
+    # Process options.
+    if ( @ARGV > 0 && $ARGV[0] =~ /^[-+]/ ) {
+	usage () 
+	  unless GetOptions ('ident'	   => \$ident,
+			     'verbose'	   => \$verbose,
+			     'antialias|aa=i'   => \$antialias,
+			     'noantialias|noaa' => sub { $antialias = 0 },
+			     'scale=f'     => \$scale,
+			     'width=i'	   => \$width,
+			     'height=i'	   => \$height,
+			     'output=s'    => \$output,
+			     'png'	   => \&set_out_type,
+			     'pngmono'	   => \&set_out_type,
+			     'pnggray'	   => \&set_out_type,
+			     'png16'	   => \&set_out_type,
+			     'png256'	   => \&set_out_type,
+			     'png16m'	   => \&set_out_type,
+			     'jpg'	   => \&set_out_type,
+			     'jpggray'	   => \&set_out_type,
+			     'jpeg'	   => \&set_out_type,
+			     'jpeggray'	   => \&set_out_type,
+			     'gif'	   => \&set_out_type,
+			     'gifmono'	   => \&set_out_type,
+			     'mono!'	   => \$mono,
+			     'resolution=i' => \$res,
+			     'pbm!'	   => \$use_pbm,
+			     'trace'	   => \$trace,
+			     'help'	   => \$help,
+			     'debug'	   => \$debug)
+	    && !$help;
+    }
+    print STDERR ("This is $my_package [$my_name $my_version]\n")
+	if $ident;
+    die ("Only one file argument is allowed when -output is used\n")
+      if @ARGV > 1 && defined $output;
+    die ("At least one input file name must be specified\n")
+      unless @ARGV;
+    die ("Antialias value must be 0, 1, 2, 4, or 8\n")
+      unless "$antialias" =~ /^[01248]$/;
+}
+
+sub usage {
+    print STDERR <<EndOfUsage;
+This is $my_package [$my_name $my_version]
+Usage: $0 [options] file [...]
+
+    -png -pngmono -pnggray -png16 -png256 -png16m
+                        produce PNG image
+    -jpg -jpggray -jpeg -jpeggray
+                        produce JPG image
+    -gif -gifmono       produce GIF image
+    -[no]mono		monochrome/colour rendition
+    -width XXX		desired with
+    -height XXX		desired height
+    -resolution XXX	resolution (default = $res)
+    -scale XXX		scaling factor
+    -antialias XX	antialias factor (must be 0, 1, 2, 4 or 8; default: 4)
+    -noantialias	no antialiasing (same as -antialias 0)
+    -[no]pbm		GIF only: [do not] convert via pbm format
+    -output XXX		output to this file (only one input file)
+    -help		this message
+    -ident		show identification
+    -verbose		verbose information
+EndOfUsage
+    exit 1;
+}
+
+# For install testing
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+epf2png - convert EPS files to PNG, JPG or GIF images
+
+=head1 SYNOPSIS
+
+    eps2png [ options ] files ...
+    eps2gif [ options ] files ...
+    eps2jpg [ options ] files ...
+
+=head1 DESCRIPTION
+
+Converts files from EPS format (Encapsulated PostScript) to some
+popular image formats.
+
+If installed as C<eps2png> (the default), it produces PNG images by
+default. Likewise, C<eps2gif> defaults to GIF images and C<eps2jpg>
+defaults to JPG. Note that the normal installation procedure will
+I<only> install C<eps2png>.
+
+It uses GhostScript to produce the images. Since modern GhostScript
+programs do not support GIF anymore, GIF images are produced via the
+Portable PixMap converters (PBM-package). In this case, a temporary
+file is created, named after the output file, with the extension
+replaced by ".ppm". It is deleted upon completion.
+
+=head1 ARGUMENTS
+
+B<eps2png> always requires at least one argument: the name of the EPS
+file to be converted. It is possible to specify more than one file
+name. This will cause all named files to be converted into separate
+files, e.g., "C<sample.eps>" will be converted to "C<sample.png>" and
+so on.
+
+=over 4
+
+=item B<-png -pngmono -pnggray -png16 -png256 -png16m>
+
+Each of these options will instruct Ghostscript to use the
+corresponding bitmap generator, and supply a C<.png> default
+extension for output files.
+
+=item B<-jpg -jpggray -jpeg -jpeggray>
+
+Same, but with a C<.jpg> default extension for output files.
+
+=item B<-gif -gifmono>
+
+Same, but with a C<.gif> default extension for output files.
+
+Note: Since modern Ghostscript versions no longer support the GIF
+format due to copyright restrictions, B<eps2png> will request
+Ghostscript to produce a Portable Bitmap File (.ppm or .pbm) instead
+and run the B<ppmtogif> converter to produce the actual GIF file.
+
+=item B<-mono>
+
+This option will select monochrome (BW or gray) output. It forces the
+Ghostscript driver to C<pngmono>, C<jpeggray>, C<pbm>, or C<gifmono>.
+
+=item B<-nomono>
+
+Produces colour images. This is the default.
+
+=item B<-width> I<NN>
+
+The desired width of the output image.
+
+If B<-height> is not specified, the image will be scaled proportionally.
+
+=item B<-height> I<NN>
+
+The desired height of the output image.
+
+If B<-width> is not specified, the image will be scaled proportionally.
+
+=item B<-resolution> I<NN>
+
+Specifies the resolution for the output image. This is the width, in
+pixels, of the bitmap image for an EPS image of one inch wide (72
+PostScript points).
+
+Note that for best results, use the B<-width> and B<-height> options
+instead.
+
+Default value is 82, which causes the converted image to be of more
+or less the same size as the EPS image. On my screen, that is.
+
+=item B<-scale> I<NN>
+
+Specify a scaling factor. This may be a fractional number.
+
+For a one-inch EPS image, the resultant bitmap image will be
+I<scale> times I<resolution>.
+
+Note that for best results, use the B<-width> and B<-height> options
+instead.
+
+=item B<-antialias> I<NN>
+
+Sets the antialiasing depth. I<NN> must be 0 (no antialiasing), 1, 2,
+4, or 8. Default value is 4.
+
+=item B<-noantialias>
+
+Sets the antialiasing depth to 0.
+
+=item B<-pbm>
+
+Forces GIF conversion through the PBM converters.
+
+=item B<-nopbm>
+
+Forces GIF conversion through Ghostscript.
+
+=item B<-output> I<XXX>
+
+Stores the output in this file. Only one input file may be supplied if
+this option is specified.
+
+=item B<-help>
+
+Prints a help message and exits.
+
+=item B<-ident>
+
+Prints the program version before doing anything else.
+
+=item B<-verbose>
+
+Provides more verbose information.
+
+=back
+
+=head1 AUTHOR
+
+Johan Vromans, <jvromans at squirrel.nl>.
+
+=head1 BUGS
+
+GhostScript and, if required, the PBM package, need to be installed and
+accessible through the user's C<PATH>.
+
+GhostScript is assumed to be capable of handling all the image types
+listed above.
+
+The EPS should be well-behaving.
+
+=head1 COPYRIGHT AND DISCLAIMER
+
+This program is Copyright 1994,2001 by Johan Vromans.
+This program is free software; you can redistribute it and/or
+modify it under the terms of the Perl Artistic License or the
+GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+If you do not have a copy of the GNU General Public License write to
+the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+MA 02139, USA.
+
+=cut
diff --git a/doc-orig/extract-docs b/doc-orig/extract-docs
new file mode 100755
index 0000000..a25de8e
--- /dev/null
+++ b/doc-orig/extract-docs
@@ -0,0 +1,322 @@
+#!/usr/bin/perl
+# -*- perl -*-
+#
+# $Id$
+#
+#################################################################
+# This script extracts special comments from the source. It assembles
+# them in texinfo files that are included in the manual.  
+#################################################################
+#
+# The general format of what this script looks for is thusly:
+#
+#  %start-doc category sort-key
+#  texi stuff goes here
+#  %end-doc
+#
+# The lines with the %start-doc and %end-doc are not included in the
+# texi extraction; only the lines between them.  The category is used
+# to determine the file that's created; a category of "foo" causes a
+# file "foo.texi" to be created.  The sort-keys are case insensitive.
+# The text extracted is sorte according to the key and put into the
+# file according to the category.  Each unique sort-key causes a @node
+# to be created, unless that sort-key's text already has a @node in
+# it.
+# If the sort-key contains space characters, it should be enclosed by
+# quotation marks ("). Leading digits in the sort key optionally followed
+# by space are removed after sort but before creation of nodes. This
+# allows to manipulate the order of nodes in the manual.
+#
+# Note that we synthesize a special @syntax command, which should be
+# used for all things syntax.  We change those to whatever the current
+# desired style is for syntaxes (currently, a cartouche box of
+# non-wrapped but variable-pitch font).
+#
+# For extracting actions, this script expects a very specific syntax
+# to be used.  It looks like this, with one or more lines
+# (continuations are like this example):
+#
+# static const char some_string_help[] =
+# "some text\n"
+# "some text";
+#
+# Repeat for some_string_syntax[], then follow those with the usual
+# %start-doc.  Note that the %start-doc for actions must use the
+# category "actions" and the sort key must match the action name.
+#
+# Within start-doc/end-doc pairs, you can use two special @-lines
+# to control the generated node names and document structure.
+#
+# @nodetype section
+#   You can specify section, subsection, unnumberedsubsec, etc.  Each
+#   unique sort key within each category is assigned one of these.
+# @nodename pattern
+#   A sprintf-like pattern to use to modify the sort-key to make a
+#   node name.  Since node names must be unique and have various
+#   restrictions as to what characters you can use in them, this
+#   allows you to use a pattern for various categories which will help
+#   keep node names unique without requiring lots of repetetive typing
+#   in the source files.
+
+$docdir = shift;
+$docdir = "." unless $docdir;
+$srcdir = "$docdir/../src";
+$docdir = ".";
+
+my $debug = 0;
+
+open(FIND, "find $srcdir -type f -name '*.[chly]' -print | sort |");
+while (<FIND>) {
+    s/[\r\n]+$//;
+    &scan_file($_);
+}
+close (FIND);
+
+sub dsort {
+    my ($a, $b) = @_;
+    $a =~ tr/A-Z/a-z/;
+    $b =~ tr/A-Z/a-z/;
+    return $a cmp $b;
+}
+
+for $cat (sort keys %text) {
+    print "$cat\n";
+    @k = sort {&dsort($a,$b)} keys %{$text{$cat}};
+    $new = '';
+    $new .= "\@c key $cat\n";
+    if ($cat eq "actions") {
+	&dump_00_keys($cat, "\0\$");
+	$new .= "\n\@menu\n";
+	for $hid (sort keys %{$hids{$cat}}) {
+	    if ($hid =~ /../) {
+		$new .= "* ${hid} actions::\n";
+	    } else {
+		$new .= "* core actions::\n";
+	    }
+	}
+	$new .= "\@end menu\n\n";
+	for $hid (sort keys %{$hids{$cat}}) {
+	    if ($hid =~ /../) {
+		$new .= "\@node $hid actions\n";
+		$new .= "\@section $hid actions\n";
+		&dump_00_keys($cat, "\0$hid\$");
+	    } else {
+		$new .= "\@node core actions\n";
+		$new .= "\@section Core actions\n";
+	    }
+	    $new .= "\@menu\n";
+	    for $key (@k) {
+		next unless $key =~ /\0$hid$/;
+		next if $key =~ /^00/;
+		$k2 = $title{$cat}{$key};
+		if ($hid =~ /\S/ && $hid !~ /common/) {
+		    $k2 = "$hid $k2";
+		}
+		$new .= "* ${k2} Action:: $desc{$key}\n";
+	    }
+	    $new .= "\@end menu\n";
+	    for $key (@k) {
+		next unless $key =~ /\0$hid$/;
+		next if $key =~ /^00/;
+		$k2 = $title{$cat}{$key};
+		if ($hid =~ /\S/ && $hid !~ /common/) {
+		    $k2 = "$hid $k2";
+		}
+		if ($key !~ /^00/) {
+		    $new .= "\@node $k2 Action\n";
+		    $new .= "\@subsection $k2\n";
+		}
+		$new .= "\@c key $k2 in hid $hid\n";
+		if ($synt{$key}) {
+		    $new .= "\@cartouche\n\@format\n";
+		    $new .= $synt{$key};
+		    $new .= "\@end format\n\@end cartouche\n\n";
+		}
+		if ($desc{$key}) {
+		    $new .= $desc{$key} . "\n";
+		}
+		$new .= $text{$cat}{$key};
+		if (! $desc{$key} && ! $text{$cat}{$key} ) {
+		    $new .= "No documentation yet.\n";
+		}
+		$new .= "\n";
+	    }
+	}
+    } else {
+	$nodetype = "section";
+	&dump_00_keys($cat, "");
+	$new .= "\@menu\n";
+	$nodename = "%s";
+	for $key (@k) {
+	    if ($nodename{$cat}{$key}) {
+		$nodename = $nodename{$cat}{$key};
+	    }
+	    next if $key =~ /^00/;
+	    $k2 = $title{$cat}{$key};
+	    # strip leading digits from the key string
+	    $k2 =~ s/\A\d+\s*//g;
+	    $k2 = sprintf($nodename, $k2);
+	    if ($text{$cat}{$key} !~ /\@node/) {
+		$new .="* ${k2}::\n";
+	    }
+	}
+	$new .= "\@end menu\n";
+	$nodename = "%s";
+	for $key (@k) {
+	    if ($nodetype{$cat}{$key}) {
+		$nodetype = $nodetype{$cat}{$key};
+	    }
+	    if ($nodename{$cat}{$key}) {
+		$nodename = $nodename{$cat}{$key};
+	    }
+	    next if $key =~ /^00/;
+	    $k2 = $title{$cat}{$key};
+	    # strip leading digits from the key string
+	    $k2 =~ s/\A\d+\s*//g;
+	    $k2n = sprintf($nodename, $k2);
+	    $new .= "\@c $cat $k2\n";
+	    if ($text{$cat}{$key} !~ /\@node/) {
+		$new .= "\@node $k2n\n";
+		$new .= "\@$nodetype $k2\n";
+	    }
+	    $new .= $text{$cat}{$key};
+	}
+    }
+    $^A = "";
+    $line = join(' ', @k);
+    formline("    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~\n", $line);
+    print $^A;
+
+    $old = '';
+    if ( -f "$docdir/$cat.texi") {
+	open(CAT, "$docdir/$cat.texi");
+	$old = join('', <CAT>);
+	close CAT;
+    }
+    if ($old ne $new) {
+	open(CAT, ">$docdir/$cat.texi");
+	print CAT $new;
+	close CAT;
+    }
+}
+
+sub dump_00_keys {
+    my($cat, $regex) = @_;
+    for $k (@k) {
+	next unless $k =~ /00.*$regex/;
+	$new .= $text{$cat}{$k};
+    }
+}
+
+sub scan_file {
+    my ($name) = @_;
+    print "DEBUG:  sub_scan($name)\n" if ($debug);
+
+    # if the source file was in $(srcdir)/hid/<hidname>/ then
+    # pick out the name of the hid and put it into $srcdir.
+    if ($name =~ m at hid/([^/]+)/@) {
+	$hid = "$1";
+    } else {
+	$hid = "";
+    }
+    $lineno = 0;
+
+    # skip processing of lex/yacc output files
+    if ($name =~ /\.[ch]$/) {
+	$new = $name;
+	$new =~ s/\.[ch]$/\.y/;
+	return if -f $new;
+	$new =~ s/\.y$/\.l/;
+	return if -f $new;
+    }
+
+    open(F, $name);
+    while (<F>) {
+	$lineno ++;
+	if (/^static\s+const\s+char\s+.*_(help|syntax)\[\]\s*=(.*)/) {
+	    $tag = $1;
+	    $last = 0;
+	    $pending{$tag} = '';
+	    
+	    # note that the help/syntax string may start on the same line
+	    # as the "static const char"... bit so we pick out that part and
+	    # process it first.
+	    $_ = $2;
+	  LOOP: {
+	      do {
+		  # eat trailing whitespace, new-lines, and carriage returns
+		  s/[\r\n\s]+$//;
+		  
+		  # we're done if we found the terminating ";"
+		  $last = 1 if /;$/;
+		  
+		  # otherwise we need to eat leading whitespace and the leading quote
+		  s/^[\s]*\"//; #"
+		  
+		  # convert \n to a newline
+		  s/\\n/\n/g;
+		  
+		  # eat trailing quotes
+		  s/\";?$//; #"
+		  s/\\\"/\"/g; #"
+		  s/ "/``/g;
+		s/" /''/g;
+		  $pending{$tag} .= $_;
+		  last if $last;
+	      } while (<F>);
+	  }
+	    # spit out a warning in case we have a malformed help
+	    if ($pending{$tag}  =~ /%(start|end)-doc/) {
+		print "WARNING:  $name line $lineno has a $1 string that includes a %start-doc or %end-doc\n";
+		print "          tag:\n$pending{$tag}\n\n";
+	    }
+	    next;
+	}
+
+	if (/%start-doc\s+(\S+)\s+([^"^\s]+|".*?")(\s+(.*))?/) {
+	# pattern to look for:
+	# start-doc -> "%start-doc"
+	# \s+ -> one ore more whitespace
+	# (\S+) -> string with no whitespace, goes to $1
+	# \s+ -> one ore more whitespace
+	# ([^"^\s]+|".*?") -> a space-less string, or a string delimited by ", goes to $2
+	# (\s+(.*))? -> zero or more space separated strings
+	
+	    $cat = $1;
+	    $key = $2;
+	    # strip leading and trailing quotation marks from the key string
+	    $key =~ s/\A"//g;
+	    $key =~ s/"\Z//g;
+	    $title = $4;
+	    if ($title) {
+		$title{$cat}{"$key\0$hid"} = $title;
+	    } else {
+		$title{$cat}{"$key\0$hid"} = $key;
+	    }
+	    $text{$cat}{"$key\0$hid"} .= "\@c $name $lineno\n";
+	    $hids{$cat}{$hid} = 1;
+	    if ($cat =~ /^(.*_)?actions/) {
+		$desc{"$key\0$hid"} = $pending{'help'};
+		$synt{"$key\0$hid"} = $pending{'syntax'};
+		%pending = ();
+	    }
+	    while (<F>) {
+		next if /^\*\/$/;
+		next if /^\/\*$/;
+		last if /%end-doc/;
+		s/\@syntax/\@cartouche\n\@format/g;
+		s/\@end syntax/\@end format\n\@end cartouche/g;
+		if (/^\@nodetype\s*(\S+)/) {
+		    $nodetype{$cat}{"$key\0$hid"} = $1;
+		    next;
+		}
+		if (/^\@nodename\s*(.+)/) {
+		    $nodename{$cat}{"$key\0$hid"} = $1;
+		    next;
+		}
+		$text{$cat}{"$key\0$hid"} .= $_;
+	    }
+	}
+    }
+    close (F);
+}
diff --git a/doc-orig/gcode.pcb b/doc-orig/gcode.pcb
new file mode 100644
index 0000000..8f621d1
--- /dev/null
+++ b/doc-orig/gcode.pcb
@@ -0,0 +1,1000 @@
+# release: pcb 20091103
+# date:    Tue Feb  9 17:50:10 2010
+# user:    amc (amc,/home/alberto,S-1-5-21-3544562028-792812758-4257637587-9314)
+# host:    ni28979b.office.amsiag.com
+
+# To read pcb files, the pcb version (or the cvs source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 280000 160000]
+
+Grid[1000.000000 0 0 0]
+Cursor[0 0 0.000000]
+PolyArea[200000000.000000]
+Thermal[0.500000]
+DRC[1000 1000 1000 1000 1500 1000]
+Flags("showdrc,nameonpcb,swapstartdir,clearnew,snappin")
+Groups("1,c:2,s:3:4:5:6:7:8")
+Styles["Signal,4000,8000,3000,2000:Power,2500,6000,3500,1000:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"]
+
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 45 0 50 8)
+	SymbolLine(0 10 0 35 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 10 0 20 8)
+	SymbolLine(10 10 10 20 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 35 20 35 8)
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(15 20 15 40 8)
+	SymbolLine(5 20 5 40 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 40 8)
+	SymbolLine(15 45 20 40 8)
+	SymbolLine(5 45 15 45 8)
+	SymbolLine(0 40 5 45 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(10 25 15 20 8)
+	SymbolLine(5 25 10 25 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(0 50 40 10 8)
+	SymbolLine(35 50 40 45 8)
+	SymbolLine(40 40 40 45 8)
+	SymbolLine(35 35 40 40 8)
+	SymbolLine(30 35 35 35 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(25 40 25 45 8)
+	SymbolLine(25 45 30 50 8)
+	SymbolLine(30 50 35 50 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 35 15 20 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(0 25 25 50 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(0 35 0 45 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 60 10 50 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 30 20 30 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 45 30 15 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(10 10 10 50 8)
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 25 30 8)
+	SymbolLine(20 10 20 50 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 10 0 30 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(5 25 15 25 8)
+	SymbolLine(15 25 20 30 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(25 10 25 25 8)
+	SymbolLine(0 10 25 10 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 25 8)
+	SymbolLine(15 30 20 25 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(20 15 20 30 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 25 5 25 8)
+	SymbolLine(0 35 5 35 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 50 10 40 8)
+	SymbolLine(10 25 10 30 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 30 10 20 8)
+	SymbolLine(0 30 10 40 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 35 20 35 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 20 10 30 8)
+	SymbolLine(0 40 10 30 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 30 10 35 8)
+	SymbolLine(10 45 10 50 8)
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 20 8)
+	SymbolLine(10 30 20 20 8)
+)
+Symbol('@' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 40 50 8)
+	SymbolLine(50 35 50 10 8)
+	SymbolLine(50 10 40 0 8)
+	SymbolLine(40 0 10 0 8)
+	SymbolLine(10 0 0 10 8)
+	SymbolLine(15 20 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 30 35 8)
+	SymbolLine(30 35 35 30 8)
+	SymbolLine(35 30 40 35 8)
+	SymbolLine(35 30 35 15 8)
+	SymbolLine(35 20 30 15 8)
+	SymbolLine(20 15 30 15 8)
+	SymbolLine(20 15 15 20 8)
+	SymbolLine(40 35 50 35 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine(0 15 0 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 30 15 30 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(10 30 20 30 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(25 10 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 10 10 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 10 50 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine(0 10 15 10 8)
+	SymbolLine(15 10 15 45 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 20 50 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 15 25 8)
+	SymbolLine(15 25 30 10 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 10 25 50 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(10 40 20 50 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(5 30 25 50 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 10 20 45 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 10 20 40 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 15 35 8)
+	SymbolLine(15 35 30 50 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 40 25 50 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(25 10 25 15 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 10 25 8)
+	SymbolLine(10 25 20 15 8)
+	SymbolLine(20 10 20 15 8)
+	SymbolLine(10 25 10 50 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine(0 10 25 10 8)
+	SymbolLine(25 10 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 15 30 45 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 15 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(20 45 25 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 10 20 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(20 40 20 35 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 15 5 50 8)
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(10 10 15 10 8)
+	SymbolLine(0 30 10 30 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(20 30 20 60 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 35 0 50 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 20 5 25 8)
+	SymbolLine(5 35 5 60 8)
+	SymbolLine(0 65 5 60 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 15 50 8)
+	SymbolLine(0 35 10 25 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(20 35 25 30 8)
+	SymbolLine(25 30 30 30 8)
+	SymbolLine(30 30 35 35 8)
+	SymbolLine(35 35 35 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 35 5 65 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(10 50 20 50 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 35 20 65 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(20 40 25 45 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 10 5 45 8)
+	SymbolLine(5 45 10 50 8)
+	SymbolLine(0 25 10 25 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 30 20 45 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 30 20 40 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(15 30 15 45 8)
+	SymbolLine(15 45 20 50 8)
+	SymbolLine(20 50 25 50 8)
+	SymbolLine(25 50 30 45 8)
+	SymbolLine(30 30 30 45 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 30 20 50 8)
+	SymbolLine(0 50 20 30 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 60 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 10 0 50 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(5 25 10 30 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 10 30 8)
+	SymbolLine(10 30 15 35 8)
+	SymbolLine(15 35 20 35 8)
+	SymbolLine(20 35 25 30 8)
+)
+Via[48000 28000 8000 4000 0 3000 "" ""]
+Via[63000 86000 8000 4000 0 3000 "" ""]
+
+Element["" "DIP18" "U5" "16F84" 116500 28500 17000 5000 3 100 ""]
+(
+	Pin[0 0 8000 3000 5600 2800 "1" "1" "square"]
+	Pin[0 10000 8000 3000 5600 2800 "2" "2" ""]
+	Pin[0 20000 8000 3000 5600 2800 "3" "3" ""]
+	Pin[0 30000 8000 3000 5600 2800 "4" "4" ""]
+	Pin[0 40000 8000 3000 5600 2800 "5" "5" ""]
+	Pin[0 50000 8000 3000 5600 2800 "6" "6" ""]
+	Pin[0 60000 8000 3000 5600 2800 "7" "7" ""]
+	Pin[0 70000 8000 3000 5600 2800 "8" "8" ""]
+	Pin[0 80000 8000 3000 5600 2800 "9" "9" ""]
+	Pin[30000 80000 8000 3000 5600 2800 "10" "10" ""]
+	Pin[30000 70000 8000 3000 5600 2800 "11" "11" ""]
+	Pin[30000 60000 8000 3000 5600 2800 "12" "12" ""]
+	Pin[30000 50000 8000 3000 5600 2800 "13" "13" ""]
+	Pin[30000 40000 8000 3000 5600 2800 "14" "14" ""]
+	Pin[30000 30000 8000 3000 5600 2800 "15" "15" ""]
+	Pin[30000 20000 8000 3000 5600 2800 "16" "16" ""]
+	Pin[30000 10000 8000 3000 5600 2800 "17" "17" ""]
+	Pin[30000 0 8000 3000 5600 2800 "18" "18" ""]
+	ElementLine [20000 -5000 35000 -5000 1000]
+	ElementLine [-5000 -5000 10000 -5000 1000]
+	ElementLine [35000 85000 35000 -5000 1000]
+	ElementLine [-5000 85000 35000 85000 1000]
+	ElementLine [-5000 -5000 -5000 85000 1000]
+	ElementArc [15000 -5000 5000 5000 0 180 1000]
+
+	)
+
+Element["" "TO220" "U6" "unknown" 179500 45000 23000 -15000 0 100 ""]
+(
+	Pin[0 -10000 8000 3000 8600 5000 "1" "1" "square,edge2"]
+	Pin[0 0 8000 3000 8600 5000 "2" "2" "edge2"]
+	Pin[0 10000 8000 3000 8600 5000 "3" "3" "edge2"]
+	Pin[67000 0 17500 3000 15100 13000 "4" "4" "edge2"]
+	ElementLine [0 -10000 18000 -10000 3000]
+	ElementLine [0 0 18000 0 3000]
+	ElementLine [0 10000 18000 10000 3000]
+	ElementLine [18000 -20000 18000 20000 2000]
+	ElementLine [18000 20000 55500 20000 2000]
+	ElementLine [55500 -20000 55500 20000 2000]
+	ElementLine [18000 -20000 55500 -20000 2000]
+	ElementLine [55500 -20000 55500 20000 2000]
+	ElementLine [55500 20000 68000 20000 2000]
+	ElementLine [68000 18500 68000 20000 2000]
+	ElementLine [68000 18500 75000 18500 2000]
+	ElementLine [75000 18500 75000 20000 2000]
+	ElementLine [75000 20000 79000 20000 2000]
+	ElementLine [79000 -20000 79000 20000 2000]
+	ElementLine [75000 -20000 79000 -20000 2000]
+	ElementLine [75000 -20000 75000 -18500 2000]
+	ElementLine [68000 -18500 75000 -18500 2000]
+	ElementLine [68000 -20000 68000 -18500 2000]
+	ElementLine [55500 -20000 68000 -20000 2000]
+
+	)
+
+Element["" "RCY300" "C11" "unknown" 205500 94500 2500 10000 3 100 ""]
+(
+	Pin[0 0 8000 3000 5600 5000 "1" "1" ""]
+	Pin[0 30000 8000 3000 5600 5000 "2" "2" "square"]
+	ElementArc [0 15000 30000 30000 270 360 1000]
+
+	)
+
+Element["" "ALF400" "Z5" "unknown" 129000 142500 -33800 900 0 100 ""]
+(
+	Pin[0 0 8000 3000 4600 2000 "2" "2" "square,edge2"]
+	Pin[-40000 0 8000 3000 4600 2000 "1" "1" "edge2"]
+	ElementLine [-13300 0 0 0 1000]
+	ElementLine [-40000 0 -26700 0 1000]
+	ElementLine [-13300 0 -26700 6600 1000]
+	ElementLine [-26700 -6600 -26700 6600 1000]
+	ElementLine [-26700 -6600 -13300 0 1000]
+	ElementLine [-13300 -6600 -13300 6600 1000]
+
+	)
+
+Element["" "ACY400" "R21" "unknown" 82500 28000 -5300 32000 3 100 ""]
+(
+	Pin[0 0 8000 3000 5100 3000 "1" "1" "square"]
+	Pin[0 40000 8000 3000 5100 3000 "2" "2" ""]
+	ElementLine [0 0 0 10000 1000]
+	ElementLine [0 30000 0 40000 1000]
+	ElementLine [3300 10000 3300 30000 1000]
+	ElementLine [-3300 30000 3300 30000 1000]
+	ElementLine [-3300 10000 -3300 30000 1000]
+	ElementLine [-3300 10000 3300 10000 1000]
+
+	)
+
+Element["" "AXIAL_LAY-200" "C13" "100n" 87000 110000 -6000 -11400 0 100 ""]
+(
+	Pin[5000 0 8000 3000 8600 3000 "1" "1" "edge2"]
+	Pin[-15000 0 8000 3000 8600 3000 "2" "2" "edge2"]
+	ElementLine [0 -1600 0 1600 1000]
+	ElementLine [-10000 -1600 0 -1600 1000]
+	ElementLine [-10000 -1600 -10000 1600 1000]
+	ElementLine [-10000 1600 0 1600 1000]
+	ElementLine [-15000 0 -10000 0 1000]
+	ElementLine [0 0 5000 0 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+	Line[48000 28000 48000 30000 4000 4000 "clearline"]
+	Line[48000 30000 69000 51000 4000 4000 "clearline"]
+	Line[69000 51000 96000 51000 4000 4000 "clearline"]
+	Line[96000 51000 98000 53000 4000 4000 "clearline"]
+	Line[98000 53000 98000 60000 4000 4000 "clearline"]
+	Line[98000 60000 106000 68000 4000 4000 "clearline"]
+	Line[106000 68000 116000 68000 4000 4000 "clearline"]
+	Line[116000 68000 116500 68500 4000 4000 "clearline"]
+	Line[82500 68000 82500 99500 4000 4000 "clearline"]
+	Line[82500 99500 72000 110000 4000 4000 "clearline"]
+	Line[92000 110000 93000 110000 4000 4000 "clearline"]
+	Line[93000 110000 101000 102000 4000 4000 "clearline"]
+	Line[101000 102000 101000 81000 4000 4000 "clearline"]
+	Line[101000 81000 103000 79000 4000 4000 "clearline"]
+	Line[103000 79000 116000 79000 4000 4000 "clearline"]
+	Line[116000 79000 116500 78500 4000 4000 "clearline"]
+	Line[116500 48500 129500 48500 4000 4000 "clearline"]
+	Line[129500 48500 133000 52000 4000 4000 "clearline"]
+	Line[133000 52000 133000 64000 4000 4000 "clearline"]
+	Line[133000 64000 137000 68000 4000 4000 "clearline"]
+	Line[137000 68000 146000 68000 4000 4000 "clearline"]
+	Line[146000 68000 146500 68500 4000 4000 "clearline"]
+	Line[146500 88500 137500 88500 4000 4000 "clearline"]
+	Line[137500 88500 129000 97000 4000 4000 "clearline"]
+	Line[129000 97000 118000 97000 4000 4000 "clearline"]
+	Line[118000 97000 116500 98500 4000 4000 "clearline"]
+	Line[129000 142500 131500 142500 4000 4000 "clearline"]
+	Line[131500 142500 161000 113000 4000 4000 "clearline"]
+	Line[161000 113000 161000 63000 4000 4000 "clearline"]
+	Line[161000 63000 158000 60000 4000 4000 "clearline"]
+	Line[158000 60000 148000 60000 4000 4000 "clearline"]
+	Line[148000 60000 146500 58500 4000 4000 "clearline"]
+	Line[205500 94500 205500 44500 4000 4000 "clearline"]
+	Line[205500 44500 197000 36000 4000 4000 "clearline"]
+	Line[197000 36000 180500 36000 4000 4000 "clearline"]
+	Line[180500 36000 179500 35000 4000 4000 "clearline"]
+	Line[82500 28000 96500 14000 4000 4000 "clearline"]
+	Line[96500 14000 128000 14000 4000 4000 "clearline"]
+	Line[128000 14000 133000 19000 4000 4000 "clearline"]
+	Line[133000 19000 133000 35000 4000 4000 "clearline"]
+	Line[133000 35000 136500 38500 4000 4000 "clearline"]
+	Line[136500 38500 146500 38500 4000 4000 "clearline"]
+	Line[146500 28500 148000 27000 4000 4000 "clearline"]
+	Line[148000 27000 166000 27000 4000 4000 "clearline"]
+	Line[166000 27000 166000 42000 4000 4000 "clearline"]
+	Line[166000 42000 169000 45000 4000 4000 "clearline"]
+	Line[169000 45000 179500 45000 4000 4000 "clearline"]
+	Line[179500 55000 179500 98500 4000 4000 "clearline"]
+	Line[179500 98500 205500 124500 4000 4000 "clearline"]
+	Line[89000 142500 107500 124000 4000 4000 "clearline"]
+	Line[107500 124000 127000 124000 4000 4000 "clearline"]
+	Line[127000 124000 127000 110000 4000 4000 "clearline"]
+	Line[127000 110000 138500 98500 4000 4000 "clearline"]
+	Line[138500 98500 146500 98500 4000 4000 "clearline"]
+	Line[146500 78500 132500 78500 4000 4000 "clearline"]
+	Line[132500 78500 132000 78000 4000 4000 "clearline"]
+	Line[132000 78000 121500 88500 4000 4000 "clearline"]
+	Line[121500 88500 116500 88500 4000 4000 "clearline"]
+	Line[63000 86000 63000 72000 4000 4000 "clearline"]
+	Line[63000 72000 67000 68000 4000 4000 "clearline"]
+	Line[67000 68000 82500 68000 4000 4000 "clearline"]
+	Polygon("clearpoly")
+	(
+		[257000 147000] [169000 147000] [169000 10000] [257000 10000] 
+	)
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "signal4")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/doc-orig/gcode.pdf b/doc-orig/gcode.pdf
new file mode 100644
index 0000000..b49ddcd
Binary files /dev/null and b/doc-orig/gcode.pdf differ
diff --git a/doc-orig/gcode.png b/doc-orig/gcode.png
new file mode 100644
index 0000000..73fcc48
Binary files /dev/null and b/doc-orig/gcode.png differ
diff --git a/doc-orig/gcode_control_img.eps b/doc-orig/gcode_control_img.eps
new file mode 100644
index 0000000..1df808f
--- /dev/null
+++ b/doc-orig/gcode_control_img.eps
@@ -0,0 +1,711 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: GIMP PostScript file plugin V 1,17 by Peter Kirchgessner
+%%Title: gcode_control_img.eps
+%%CreationDate: Mon May 30 02:37:26 2011
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%Pages: 1
+%%BoundingBox: 0 0 404 231
+%%EndComments
+%%BeginProlog
+% Use own dictionary to avoid conflicts
+10 dict begin
+%%EndProlog
+%%Page: 1 1
+% Translate for offset
+0 0 translate
+% Translate to begin of first scanline
+0 230.40000000000001 translate
+403.20000000000005 -230.40000000000001 scale
+% Image geometry
+1680 960 1
+% Transformation matrix
+[ 1680 0 0 960 0 0 ]
+currentfile /ASCII85Decode filter /RunLengthDecode filter
+%%BeginData:        51668 ASCII Bytes
+image
+JcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqR
+JcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqR
+JcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqR
+JcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqRJcDqR
+nG`K=^]4@!KE(H@!._ib!'c,9nG`K=^]4@!KE(H@!._ib!'c,9nG`K=^]4@!KE(H@!._ib!'c,9
+nG`K=^]4@!KE(H@!._ib!'c,9nG`K=^]4@!KE(H@!._ib!'c,9nG`K=^]4@!jSo5LklA9onG`K=
+^]4@!jSo4qkl:]J])V:"!._ib!'fQEkPtSn])V:"!._ib!'fTF!<20^!!g+8nG`K=^]4@!jo5>Y
+kPtSb])V:"!._ib!'fTF!;PaX!!0\2nG`K=^]4@!jo5>Mk5`*nnG`K=^]4@!jo5>=k5YL3]DqC#
+!._ib!'fTF!5Rat!'e*qnG`K=^]4@!jo5=2k5YK(]DqC#!._ib!'fTFjo>Al]DqC#!._ib!'fWG
+!<2*\!!g.9nG`K=^]4@!k5PGZjo>A`]DqC#!._ib!'fWG!;P[V!!0_3nG`K=^]4@!k5PGNjT)pm
+nG`K=^]4@!k5PG>jT#:1]`7L$!._ib!'fWG!5R[r!'e-rnG`K=^]4@!k5PF3jT#9&]`7L$!._ib
+!'fWGj8]/j]`7L$!._ib!'fZH!<2$Z!!g1:nG`K=^]4@!kPkP[j8]/^]`7L$!._ib!'fZH!;PUT
+!!0b4nG`K=^]4@!kPkPOirHalnG`K=^]4@!kPkP?irB(/^&RU%!._ib!'fZH!5RUp!'e0snG`K=
+^]4@!kPkO4irB'$^&RU%!._ib!'fZHiW&rh^&RU%!._ib!'f]I!<1sX!!g4;nG`K=^]4@!kl1Y\
+iW&r\^&RU%!._ib!'f]I!;QKm!"\Dqr;Zfu^&RU%!._ib!'f]I!:]pe!$CP,!.b"J^&RU%!._ib
+!'f]I!9!eU!'ffL!5SO5!.V`_nG`K=^]4@!kl1Xur;ZhIli-tCr;Zg^^Am^&!._ib!'f]I!.b"J
+lMgkRr;Zg>^Am^&!._ib!'f]Ir;ZfulMgkZr;Zg.^Am^&!._ib!'f`J!<2os!!DNd!;ucq!!g7<
+nG`K=^]4@!l2Lb]r;Zg&lMgk`r;Zg"^Am^&!._ib!'f`J!;QKm!"\>or;Zfu^Am^&!._ib!'f`J
+!;QKm!$CJ*!.b"J^Am^&!._ib!'f`J!:]pe!'f`J!5SO5!.Vc`nG`K=^]4@!l2LbQr;ZhIl2LbA
+r;Zg^^]3g'!._ib!'f`J!9!eUkl1YPr;Zg>^]3g'!._ib!'f`J!9!hV!!2<`!;QKm!"ZjEnG`K=
+^]4@!l2LbArVup#kl1Y\r;Zg&^]3g'!._ib!'f`J!9!hV!!h`f!<2os!!C"9nG`K=^]4@!l2LbA
+rVup/kPtJ\!!0k7nG`K=^]4@!l2LbArVup?kPkO4r;aV4nG`K=^]4@!l2LbArVup_kPkOtr;ZhI
+_#Np(!._ib!'f`J!9!hV!.X23!9!eU!'e:!nG`K=^]4@!l2LbArW)*\!:]pe!$B#VnG`K=^]4@!
+l2LbArW)*\!;QKm!"ZmFnG`K=^]4@!l2LbArW)*\!;ucq!!g=>nG`K=^]4@!l2LbArW)*\!<2os
+!!C%:nG`K=^]4@!l2LbArW)'[r;Zfu_#Np(!._ib!'f`J!9!hVjo5=2r;aY5nG`K=^]4@!l2LbA
+rW)'[!5SO5!.VibnG`K=^]4@!l2LbArW)'[!9!eU!'e="nG`K=^]4@!l2LbArW)'[!:]pe!$B&W
+nG`K=^]4@!l2LbArW)'[!;QKm!"ZpGnG`K=^]4@!l2LbArW)'[!;ucq!!g@?nG`K=^]4@!l2LbA
+rW)'[!<2os!!C(;nG`K=^]4@!l2LbArW)$Zr;Zfu_>j$)!._ib!'f`J!9!hVjSo41r;a\6nG`K=
+^]4@!l2LbArW)$Z!5SO5!.VlcnG`K=^]4@!l2LbArW)$Z!9!eU!'e@#nG`K=^]4@!l2LbArW)$Z
+!:]pe!$B)XnG`K=^]4@!l2LbArW)$Z!;QKm!"ZsHnG`K=^]4@!l2LbArW)$Z!;ucq!!gC at nG`K=
+^]4@!l2LbArW)$Z!<2os!!C+<nG`K=^]4@!l2LbArW)!Yr;Zfu_Z0-*!._ib!'f`J!9!hVj8T+0
+r;a_7nG`K=^]4@!l2LbArW)!Y!5SC1!"[-MnG`K=^]4@!l2LbArW)!Y!9!YQ!"\Gr!T4LFs7$$g
+JA;-b5O&1Ji;N`:rrDQa!!!PsrrW0"!8dbFrr at Sb!!#7JrrD!V!;c]q_#"-%rrDii!!!Psrr`#r
+!'fB at nG`K=^]4@!o)Aa[#Q"H#i;N`QrrBk3!:g'hr:p<m&+0B!huE`fhuE3G!._ib!'g&Srr<%K
+qu6ZSrW)fp!5SI3nc&Ugq#CC*mJd9=!!!,Ys7$$gJA;-bJ+N[Bqu?^=qu6ZSrW)fp!5SI3nGi:a
+!"\JsrVup!huE3G!._ibs8VTh!!iE$!9!hVqYpQ2qZ--c!.anG!"\Mt!<2oshuE3G!._ib!WW)f
+!!!&srrD!V!;c]q_#"-$rrBk2!!!PtrrDuq!!%N,s7$$gJAD3e!WVZY!;lcri;N`QrrBk3!:^!g
+i;*ER&+9Gtq#(0m5N)S2rr at Sc!!33$i:$^HJ,K<Hi;N`QrrBk3!:^!gnG3+b&+9GtnGN=e+5m1g
+rr at Sc!!33$_!h=(5Q(N]i;N`QrrBk3!:^!gq"asj&+9Gti;EWU&)dKWrr at Sc!!39&JFEO=+8l-=
+i;N`QrrBk3!:^!gr;$Bn&+9Gt_#465#N5XOrr at Sc!!*3%mf3=ur;QcTrW)fp!5SI3nG`Lfq>^L+
+mf*9;r;Zg"i;`<H!._lc!<`>i!!!9%rrD!V!;c]q_#"-#s7u]q&+9GtJGfHJ"5s4Krr at Sc!!*?(
+mf3=ir;QcTrW)fp!5SI3n,EB<qZ$U,mf31b!!2$XnG`K=_#OK at r9jUc!W;rti;N`QrrBk3!:Tpf
+_#"*3&+9Jq!!!&Xs7$$gJAD3d#Q4#j!!3#t!9!hVqYpQ2qZ-*b!9!_S!"\Pu!<2iqi;`<H!._lc
+!=/Df!;uisi;N`QrrBk3!:TpfnG<1c&+BMurqc]Rs7$$gJAD3d&,>Pmr;QcTrW)fp!5SI3n,EC_
+qZ$U,n,ECcqZ$VGiW&EI!._lc!>"tn!!%NIrrD!V!;c]q_#"-#rrDuo!!!PurrDuo!!%N-s7$$g
+JAD3d&,>Pm!.XtI!9!hVqYpQ2qZ-*b!;u]o!"\Pu!;u]o!.Wu-nG`K=_#OKHq!J+^5Q1T^i;N`Q
+rrBk3!:Tpfr;-Ho&+BMur;-HoJ)UFsrr at Sc!!*W*mJm5OrVllUrW)fp!5SI3n,ECcqZ$U,n,EC_
+qZ$U\iW&EI!._lc!>"tn!!#7^rrD!V!;c]q_#"-#rrDuo!!!PurrDik!!#7Bs7$$gJAD3d&,>Pm
+!'gG^!9!hVqYpQ2qZ-*b!;u]o!"\Pu!;QEk!'fHBnG`K=_#OKHq!J+^+8u3>i;N`QrrBk3!:Tpf
+r;-Ho&+BMuq"k$k5N2Y3rr at Sc!!*W*mJm5/rVllUrW)fp!5SI3n,ECcqZ$U,n,EC_qZ$U\iW&EI
+!._lc!>"tn!!",>rrD!V!;c]q_#"-#rrDuo!!!PurrDik!!#7Bs7$$gJAD3d&,>Pm!$D1>!9!hV
+qYpQ2qZ-*b!;u]o!"\Pu!;QEk!'fHBnG`K=_#OKHq!J+^+8u3>i;N`QrrBk3!:Tpfr;-Ho&+BMu
+q"k$k5N2Y3rr at Sc!!*W*mJm5/rVllUrW)fp!5SI3n,ECcqZ$U,n,EC_qZ$U\iW&EI!._lc!>"tn
+!!",>rrD!V!;c]q_#"-#rrDuo!!!PurrDuo!!%N-s7$$gJAD3d&,>Pm!$D1>!9!hVqYpQ2qZ-*b
+!;u]o!"\Pu!;u]o!.Wu-nG`K=_#OKHq!J+^+8u3>i;N`QrrBk3!:Tpfr;-Ho&+BMur;-HoJ)UFs
+rr at Sc!!*W*mJm5OrVllUrW)fp!5SI3n,ECcqZ$U,n,ECcqZ$VGiW&EI!.`8n!!2us!<2rt!>"u)
+!!#7ZrrE&q!!#7^rrD!V!;c]q_#"-#rrDuo!!!PurrE&q!9!nHrr at Sn!!!,ts8E!!&,?,(!'g;Z
+!<2iq!'gG^!9!hVqYpQ2qZ-*b!;u]o!"\Pu!<2iqi;`<H!.`8n!!iB#"FpIP&,?,(!'g;Z!<2iq
+!'gG^!9!hVqYpQ2qZ-*b!;u]o!"\Mtqu?]ti;`<H!.`8n!"\r+"Mb!;&,?,(!'g8YqZ$VGrVllU
+rW)fp!5SI3n,ECcqZ$U,mf31b!!2$XnG`K=bQ%VbqYp]V!!!Q)rVup_q#C3k!.XtI!9!hVqYpQ2
+qZ-*b!;u]o!"\Mt!.b"J!!D0ZnG`K=bQ%VbqYp]V!!!Q)rVup_q#:>EquHZp!9!hVqYpQ2qZ-*b
+!;u]o!"\Mt!.b"J!!D0ZnG`K=bQ%VbqZ$d%huE`fq#16n5PY6YJG]EFrrD!V!;c]q_#"-#rrDuo
+!!!Ptrr at TJ!!!8^s7$$gJBRun+8Z$A"5j.Z&,?,(!'g8Y!5SO5!!3#t!9!hVqYpQ2qZ-*b!;u]o
+!"\Mtqu?^-i;`<H!.`8n!$D(<"U!Z_!"\l(!!#7YrrBk5!!!&trrD!V!;c]q_#"-#rrDuo!!!Pu
+rrE&r!!",!s7$$gJBRun+8Z$A"5j.Z&,?,(!'g8Y!9!eU!!E0!!9!hVqYpQ2qZ-*b!;u]o!"\Pu
+!;u`p!'fEAnG`K=bQ%VbqZ$d%huE`fq#16n5PY6YnGN=e#Q4T%i;N`QrrBk3!:Tpfr;-Ho&+BMu
+q"t*lJ)L at rrr@Sn!!",<!!NJ_!!!Q)rVup_q#:?hr;Zg.r;QcTrW)fp!5SI3n,ECcqZ$U,n,ECW
+quGXSnG`K=bQ%VbqZ$d%huE`fq#16n5PY6Yr;?Tq+8l-=i;N`QrrBk3!:Tpfr;-Ho&+BMui;EWU
+!T3qHrr at Sn!!",<!!NJ_!!!Q)rVup_q#:?nr;Zg^r;QcTrW)fp!5SI3n,ECcqZ$U,n,EC'r;Zg"
+huE3G!.`8n!$D(<"U!Z_!"\l(!!#7Xs8;otJ,K<Hi;N`QrrBk3!:Tpfr;-Ho&+BMuJGfHJ&)[EV
+rr at T#!!!&os8)d""5j.Z&,?,(!'g5X!.b%Kqu6ZSrW)fp!5SI3n,ECcqZ$U,n,N:c!'fB at nG`K=
+ec5[lp](*j"U!Z_!"\l(!!#7Xrr^%:!!2us!9!hVqYpQ2qZ-*b!;u]o!"\T!!<2lrhZ**F!.`W#
+pAb!i"U!Z_!"\l(!!#7Xrr_`j!!iE$!9!hVqYpQ2qZ-*b!;u]o!"\T!!;ucq!!hB\nG`K=f)PdQ
+pAb!i"U!Z_!"\l(!!#7Xrr`0!!$D+<!9!hVqYpQ2qZ,4I!;QKm!"[rdnG`K=f)Pd]pAb!i"U!Z_
+!"\l(!!#7Ws8N'!J,B6Gi;N`QrrBk3!7q/MnGN=e+5[%err at T$!!#7Ws8)d""5j.Z&,?,(!'g2W
+!Up?jrrD!V!;c]q_#",_rrD!U!!#7?s7$$gJCac$J+ip>!!NJ_!!!Q)rVup_mJd1ErW(%>!5SO5
+!.Wl*nG`K=f)Pf#pAb!i"U!Z_!"\l(!!#7NrrD!V!6G0?JGfK(s7$$gJCaeos8)d""5j.Z&,?,(
+!'flN!9!hVaT)2=!!1pUnG`K=fDkmPp&Fmh"U!Z_!"\l(!!#7NrrD!V!6P6 at rqufs"5X"Hrr at T%
+!!39&r:'aj"5j.Z&,?,(!'flN!9!hVao;A=r;Zg&h>d!E!.`]%!XJbpnGi^phuE`fq#16n5OJIN
+i;N_urrDim!!!Pcs7$$gJCji'&-'H8!!NJ_!!!Q)rVup_mJd1ErW((?!:]pe!$C%snG`K=fDksp
+s+'V>"U!Z_!"\l(!!#7WrrMToqYpQRrW)]m!<@W"rrD!U!!#7>s7$$gJCji&5QC3Q"U!Z_!"\l(
+!!#7Ws8N'!J,B6G_#=?/rrVZi#M&hS_#465J)1.orr at T%!!.TKn,NUohuE`fq#16n5PP0[qu?^=
+qu6YHrW)`n!l+d:ec,W"r;bXQnG`K=fDksPr9s[i"5j.Z&,?,(!'g5X"7Q9j#Q+Q"!;QTn!!%N"
+s8;ot!Sm_Err at TH!<<'!^u>=q!WVra!!NJ_!!!Q)rVup_p\t?2!!!&trrE&s!;ZWpr;HZr+4pMm
+rqufs"5NqGrr at TI!!=>Cs8M!X!X&Jln,NUohuE`fq#16n5PP0XJGoQHrrDuq!;ZWpq#16n&(gg]
+r;?Tq#Mf at Krr@TJ!!!-"rrDQK!!3E*i:$^M"5j.Z&,?,(!'g5Xr;ZhIrVllmr;cZn!:]sf!!h-U
+!;QKm!"[lbnG`K=r;Zg.rVllqirB,ks1n+("U!Z_!"\l(!!#7YrrE&s!!#7^rrDQe!;ZWpi;N]V
+"5!PQnGN=e+5Hncrr at TJ!!%NHs5O(\+9-l\!!NJ_!!!Q)rVup_q#:?lr;Zg>rVllUr;cZn!5SR6
+!!1^O!9!eU!'f9=nG`K=rVup!qu6ZSj8]3Fs6fpk"5j.Z&,?,(!'g8Y!;QKm!"]&.!5SR6!!2oq
+!.b"JfDbidr;ZhIh#HmD!.b%K!!iE$!;PUT!It+;!!NJ_!!!Q)rVup_q#:?`r;Zg&rVlkJrVup!
+q>^?m!.WZ$!.b"Jg]-dC!.b%K#9X!G^]FK8ro=%[s8;<c"U!Z_!"\l(!!#7YrrD!U!!!-"s8;ot
+"8`#trql`r5M6#7!!!&Ss7$$gJGoNR5QC3Q!!iQ(jT#>_s7l$_"U!Z_!"\l(!!#7YrrBk,!!!,t
+rrDup!!"+orrE&s!!!,Us7$$gJGoQKrr at TK!<<'!^ubUu"97lZ!!NJ_!!!Q)rVup_q#:?0oDejr
+qYpQnqu?^=g&D'Mr;Zg&g]-dC"b6RQ!WW)r!!48Bi8t"@#QN`N!!NJ_!!!Q)rVup_q#:>EoDek%
+qYpQjqu?^-g&D'Ir;Zg.g]-dC"b6RQ#QN`\!!39&puVPX&-'H6!!NJ_!!!Q)rVup_q#:>EoDek5
+qYpQjqu?^-g&D'Ar;Zg>g]-dC"b6RQ&-%1X!!3,tjo>H)s+'P<"U!Z_!"\l(!!#7Ys763j5PkB[
+nGE7d#MK+Wi;EWU5MQ5-rrdiP!$D4;!!,=`jo>EHs6]jj"5j.Z&,?,(!'g8Yo)Jc?qYpQbqu?^%
+g&D&fr;ZhIg]-dC"FpIPJ,K0D!?_@(!!.TKmJmCmhuE`fq#16n5Pb<Zrpp-crrD!T!!!,Srr at TJ
+!8 at JBrrdiP!<;Qc!!3E*^uk[us8;9b"U!Z_!"\l(!!#7ZrrE&j!!!&qrrD!T!!!,Ss8;ot!S[SC
+rrdiP!WTt4!!33$i91.B!WVr_!!NJ_!!!Q)rVup_q>UHooDejnq>UHQqu?^!gA_0Pr;Zg"gAg[B
+"FpISs+'qG!WVZQ!!39&nEp8["5j.Z&,?,(!'g;Z!<2Tj!!i?"!9!bT!!CsT!;ucq!!h6XnG`T@
+!!iQ"!!.TEkPtYhs5<;F"U!Z_!"\l(!!#7ZrrDuh!!!Q*rrBk4!!!&RrrDim!!!P`s7$$jJ,g,X
+q#CF;r9++^&-'H5!!NJ_!!!Q)rVup_q>UHmoDek5q>UH1qu?]tgA_0Br;Zg>gAg[B"+U at nq"Xmj
+&,tbm![%GmmJmCmhuE`fq#16n5Pb<Zr:Bsh5Pb<Z_#+04!S[PRi;EWU5MH/,rr[cO+7T(+!=/Vf
+!!,=am/R:lhuE`fq#16n5Pb<Zr:BshJ,0*E_#+04!S[PR_#465J(jqlrr[cO5N)>;!<`Ac!!.TK
+r;Zg"q#C3k"U!Z_!"\l(!!#7ZrrDuh!;QQo_#+04!S[PRJGfK$s7$$jJ,k)7q#CHss+'>6!WW)r
+!!!9!s8)d""5j.Z&,?,(!'g;Z!;uKi!!2lp!5SL4!!1gRr;Zfug&LRA"+UCOJG9*Fs1mn"!Wi>r
+r;Zg.q#C3k"U!Z_!"\l(!!#7ZrrDui!!!,rrrBk4!!!&SrrE&s!!!,Ss7$$iJ-#]E!!.T-l2Ukf
+s7#se!$D"9qZ$d%huE`fq#16n5Pb<Zr:L$i#Pe<!_#+04!SdVSr;?Tq#MK.HrrR]Orq?Bn5N(f,
+!XJb`r;Zg^q#C3k"U!Z_!"\l(!!#7ZrrDui!!!Q)rrBk4!!!&SrrDim!!!P_s7$$iJ-5`D!!+22
+l2Ukrs1nR5!.XeDqZ$d%huE`fq#16n5Pb<Zrq-6k+8Gj9i;<QT"5EhUnGN=e+5-\`rrR]Ur:^0l
++7SOq![%Gmr;c?e"Q07[&,?,(!'g;Z!<2Wk!'g8Y!9!bT!!D!U!9!eU!'f0:nG`Q?#Pe#n!>"tj
+!!,=ar;ZfunG`XL!!!Q)rVup_q>UHoo`+uAq#:?Pqu?^!g]%8hr;ZhIg&LRA!e:gNpAb4!r9=7_
+J,]BH!!D`j"Mb!;&,?,(!'g;Z!<2Wkp\t6Oqu?^!g]%8(r;bLMnG`Q?&)d0^!<`8b!!3,tr;Zm(
+s5<_R!!iB#"FpIP&,?,(!'g8Yp&G'np\t6_qu?^%g].3P!!1aPnG`Q?+5lkn!<`>e!!33$q#(0o
+&-'HA!!!,ts8E!!&,?,(!'g8Yp&G'pp\t6_qu?^%h#@BRr;Zg"f`1I@!e;B.pAb3prp'Oc"97lh
+!!48BJG9*EJ,]HKrUBdd!>"u)!!#7Yrr at TD!!!8urrDil!!!PbrrDuq!!!8Vs7$$iJ3UjD!!*-#
+lMptks5<hU!C-VY!<)otr;?Tr+8Gg8!'g8Y!.aeD!"\i(!;QHl!"[lb!;QKm!"[`^nG`Q?5CiG-
+!<;?b!Y>=Hr;ZkJrqHHs!WW&us8Mrs!C-AY!!#7YrrBk/!!#7XrrDup!!"+rrrDQe!!"+ns7$$h
+J:I4l!IoXd!!48BJGfHKs8;Wl"UG(C!$D7>!!,=ZrVup_q#:?0pAjpe!;u`p!$C"r!9!eU!'f-9
+nG`N>J+WdBIt6kc!C-V_!!33$q"Ogl&,uV0#Q+N$q#16n5PY6Yi;EWU"7cBkrql`r5MZ8=_#465
+J(XejrrIZLo`,"W_!D%%J,]BH!X&Jlp](F=qu?]tqu6ZkrVup_q#:?`r;Zg&nGiCd!.Wf(!.b"J
+fDk@?!J(1B!!,="li7(dr;?Ts#QN`W!!,=RrVuqJr;QcdrVup_q#:?hr;Zg.nG`K=r;bUPr;Zfu
+fDk@?!J11A!!+2"m/R1is7lNm!Y>=Hp](=Zi;N]V5Q(N]i;N]V5PY6Yr;?Tq+7T:1_#=<6!Sm\T
+rqufs"5!SBrrI]Ko`,"7i9^LG#QO;l!!48BJG9*FJ&)$`!$D.=!5SR6!'g8Y!<2os!'fuQ!9!hV
+!!D$V!;ucq!!h-UnG`N?r:L$j+5lMd!XJb`r;Zj_s7ZKos+((K!"]#-!.b%K!'g5Xr;ZhInG`LX
+rVup'h#@BLr;Zg.fDk@?!J1%=!!*Vgm/R1us1nR5!It+D!!*-#r;Zg&r;Z]q!'g5X!.b%Kn,EC_
+rVup/h#@BDr;Zg>fDk@?!JC1?!!*W"m/R20s+(%J!WW)m!!*-"r;Zg"rVllsr;Zg^p\t?2!!!&g
+rrDur!!"+rrrD!U!!#78s7$$hKD>*>!>"\e!!+2Ar;Zm"s7l?h!<`8u!!!&urrDuq!!#7Xrr_`j
+!!hrlrVuqJh#@Air;ZhIfDk@?!JBn7!!*>om/R/Orqufu"97lc!!*2squH]q!;QKm!'g5X"8i-!
++7B.1^]4Dirr at TJ!7q2>rrIcAo`,!tnEg2V5Q(H[!XJb`p](="nFHV[J+ipB!!%N:rrVZi#M]:W
+!!!&Ns7$$hKCJO6!=/,]!!,=Zr;Zm0s1nC0!=/,c!!%NBrrMTom/I(dJ)(&(rqufs"4mMArrIo5
+o`,!pq!A%^J+3C:!?_ at 9!!*Vgo)Q9"!;ucq!!h*TnG`NEi:R'N"8M6`!Is&+!!,=`pAb4)i:?rX
+rrDim!!!P\s7$$hLZ%h*!<`,a!!.Sbr;ZkJr:^0l&&@f;!!0S/!:]pe!$BelnG`NEi:R'N"8M6`
+!WTt6!!3,ppAb4)_"7U,"1S:1i;EWU5M#l(rrIo5o`,!pq!A%^s+((K!Wi>jpAb49JFigA#Ij^5
+_#465J(FYhrrIo5o`,!pq!A%^s+((K!X&J\pAb49JFigA&%DQ=JGfJus7$$hLVWQ_!<Mu_!!*&t
+!!3E*_"Rg0++Wt`!$A]Mr;Zfuec5.=!KY%h!!*,qmJm7gs8;p!&-%1S!!+0loDekUp&>'^#OMHk
+!.Wi)!<2os!!CdOnG`NM_"@[.!W;?c!<N6!!!+2Ap&G(7o)Jc?p&G$l!.XG:!q60ph>[KQr;Zg&
+ec5.=!KY%h!!*,umJm7gs8;ou5Q:?V!'g&Tp&>-l!!",/rrTt9!T!bUq#(0m&(U^LrrJ1ro`,!n
+r9aOc!WW)u!It%@!!#7U!!!&mrr_`j!!hrlrVuqJhZ!TFr;Zg>ec5.=!KY%h!!*,umJm7gs8;ou
+s7l9f!'g)U!!Doo"2Fm:!Up$gr;HZr+5["ti;EWU5Lof'rrJ1ro`,!nr9aOc!WW-!!Wi>jp&G(W
+oDejrp&>#BrW)Ee!;QNn!"[rd!5SO5!.WQ!nG`NM_"@[.!W;?c!<N6"!!39&i:[-N5P+pU&,#u$
+!!%N<rrDQf!!!8\rr at TJ!7_&<rrJ1ro`,!nr9aOc!WW-!!XJb at p&G(WoDek5pAY-lr;Zg^nG`LH
+rVup#hZ*NS!!1ULnG`NM_"@[.!W;?c!<N6"!!3E*JG&sC+7oO55PG*Wr;?Tq+7T:1_#=<6!T3nW
+rqufs"4[A?rrJ1ro`,!nr9aOc!WW-!!=/Yt!!",5!!%NBrrDim!!!Q!rr at TJ!8meVr;?Tq#LreC
+rrInjo`,!nq!J+_!WW-!!=/Vs!!+0lo`4[b!:]pe!!i#nqu?_Hi;WfPr;Zg.eGo%<!JeJ`!!*,q
+mJm7gs8E!!#Q45p!?Zjc!!!&mrrD!U!!!,krrE&r!!#7ArrDQe!!"+js7$$hLZ%h*!<`,b!!*-#
+rVus(r:L$j++X%b!!Doo!5SO5!!2lp!9!MM!$C/!!9!eU!'f!5nG`NEi:R'N"8M9a!<N6"!!*?&
+o`,"'JG&sC#PJ)s_#465!VlZpJFrmB+5m/!_#465J(4MfrrIo5o`,!pq!J+_!WW-!!=/Pq!!*VG
+p&G('p&>#BquHQm!<2Tj!"\#f!.b"Je,Sq;!JfV+!!*2smJm7gs8E!!#Q45p!>!!>!!",6rr at TI
+!;ZWpr:Bsh&)dKd!!!&Ks7$$hLZ%h*!<`,b!!*-#rVus(r:L$j&)d-]!'g/VqZ$VGqYpQjoDejr
+iVroWr;Zg"e,Sq;!JB>'!!*2kmJm7gs8E!!#Q45p!=.QV!!%NAs8)crJ,90FnFQ\\#N>[_r;?Tq
+#Li_BrrIcAo`,!tnEp8W!WW-!!=/Pq!!*>opAb0op&>$kqZ$U\qYpQRoDejniVroQr;Zg.e,Sq;
+!JBn7!!*>omJm7gs8E!!#Q45p!<_ic!!!8srrE&q!!#7[rrBk,!!!,[rrDQe!!"+is7$$hKCJO6
+!=/,^!!*-#rVus(r:L$j"8Mcon,ECeqZ$U\qYpPGoDejniVro9r;Zg^e,Sq;!JC1?!!*W"mJm7g
+s8E!!#Q45p!<N,s!!!&grrE&q!!#7[s763j"6'7[_#465J(+GerrI]Go`,"'i9gRG!WW-!!=/Pq
+!!*-"r;Zg"n,ECcqZ$U<qu6Zqo)JakiVrn.r;b:GnG`N?q"4Uf&)cjU!<N6"!!*?&oDemkr;Zg&
+n,ECcqZ$U<qu6Zoo)JakiW&iV!!1OJnG`N?r:L$j+5lPe!<N6"!!*?&oDeoAJGoNK&+BMur;-Ho
++8c'<q""Ic!TO+Zrqufs"4I5=rrI]Ko`,"7i9gRG!WW-!!=/Pp!!,="rVup?n,ECcqZ$U<qu6Zc
+o)Jakir9#Vr;Zg&df8h:!J(+@!!+1WmJm7gs8E!!#Q42o!C,NA!!#7PrrDuo!!",<rrD!K!!!&Z
+rrDim!!!PXs7$$hJH#3A!C+Bf!!*-#rVus(r:Bsi+7T70!.XJ;!;u]o!$D+<!5S1+!!2fn!:]4Q
+!$BYhnG`N>Ie<[A5Ci/%!<N6"!!*?&oDf")qu?]tmf*:bqZ$U<qu6YHo)JakpAY-.kl:]Jdf8h:
+!J#[m!!.S"mJm7gs8E!!#Q42o"pb/.!!iOSr;Zg^p&>$iqZ$U<qu?3e!!2fnkPtU4df8h:!J!E-
+!!%N9!!*-#rVus(r:Bsm!WTq8+9)0<!'g/V!;u]o!$D.=!<2Nh!!2io!<20^dJr_9!e<Lcp&G*m
+m/R.fs8E!!#Q4/n"TSB#s8;cp!'g/V!<2iq!'gD]!;uBf!!Duq!;u']!!1LInG`Q?+2IUN!<N2e
+!!*-#rVus(r:9mgJ,]HJq"t*l5P>$VrqcZq5Q(N]q!nCb"8DfqpuqbY"4@/<rrR]m_"Rg0!WMHd
+!<N6"!!*?&o)Jb4rr2uVqu?^]p&>$kqZ$U\r;Qcdnc/Xlp\t6_kl:\gdJr_9!e:g>pAb3rr9XIb
+!WW-!!=/Po!!!Q/rrBk4!!#7VrrE&q!!#7]rrD!J!!!,qrrD!A!!!PWs7$$iJ.LT4!!*3"m/R.f
+s8E!!#Q4/n!"])/!5SL4!'g,UqZ$VGr;Qc4nc/Xpp\t6/kl:]*dJr_9!e:OFpAb4!q!A%^!WW-!
+!=/Po!!",?rrD!T!!#7Us8)crJ,K<HJFW[?#P\5uJE[%65LKN#rrR]Uq"Fah&+Jrd!<N6"!!*?&
+o)Jc?rr2unqu?^]o`"oAquHWonGiP"p]'FW!.WDrnG`Q?"8qln!?^gu!!*-#rVus(r:9perrDup
+!!#7Urr at TI!;uisrp]sg&,?/)roa@/s7$$iJ-#TB!!+2"m/R.fs8E!!#Q42o"TeZ$!WW/s!!#7U
+rrBk5!!!&trrDue!!",9rrDu]!!!&Hs7$$iJ-#ZD!!,=Bm/R.fs8E!!#Q42o"pb1D!$D5kr;Zg^
+o`"p,r;Zfur;QclnGiP2q#:?hkl:\cd/WV8!e::NpAb5D_!M+&!WW-!!=/Pp!!Wu6!!!9(_#465
+5P4sUi;EWU"8r0!q#(0m+8l-=rql`r5PY6YnEBoQ#LNM?rr[cOIt7:o!WR];!!*-#rVus(r:Bsn
++8c*=!WV*V!!#7UrrDQe!!!9%rrDQe!!#7\s82isJ,'$Di9:4A&((@Grr[cO5J['p!<N5e!!*-#
+rVus(r:Bsi5OeXP!Is&+!!#7UrrDim!!!Q-rrDQe!!%NGrr at TJ!;HKn_!(h!+41&Wrr[cO+5lqp
+!<`>f!!*-#rVus(r:Bsi5N)M@!C-)P!!#7UrrDuq!!",=rrD!U!;c]q_#=<6!VcToJE[%65LBH"
+rr[cO+7T(+!=/Vj!!*-#rVus(r:BsiJ&)$`!?_+8!!#7UrrE&s!!#7]rrD!V!!!&rrrD!V!!!,q
+s6'F_J'e5brr[cO&,>r#!>#+p!!*-#rVus(r:Bsis+((K!>#,,!!#7Ts8;otJ,K<Hi;N]V"8`#t
+nGWCf#Pe<!roa at .s7$$jJ,fiPq#CF;q!7t]!WW-!!=/Pq!!*-#r;Zj'r;?Tq5P+mTJGoQGrrD!V
+!!!9#rrDin!!!Q)rrE&_!!!&Gs7$$jJ,f]Oq#CGFnE^,U!WW-!!=/Pq!!*-"r;Zj#rqufs5P+mW
+^]4?8qu6ZSrVup/qYpQnrVup?q#:?lkl:\cci<M7"FpIQs+'qG!WV*E!!*-#rVus(r:L$j"8r)t
+!<N6!!!#7Trr_`j!!iE$!9!hV!$D%:rVuqJq#:?lkl:\gci<M7"FpIPs1nL3!Wi>:li7%es8E!!
+#Q45p!<`,p!!*&t!!#7Trr`0!!$D+<!9!hV!'g;Z!l+d:p\t6gkl:\oci<M7"FpIPJ+3=8!=/Yj
+!!*-#rVus(r:L$j#OqWk!IoY!!!#7Ss8N'!J,B6Gi;N]VJ,0*Gn,N^frrDiY!!"+es7$$kJ,fQk
+r;-Hp+9(R+!<N6"!!*?&o`,!tnGE7eIt7Lu!'g&S!Up?jrrD!V!;HKo!.XbC!;PdY!'eg0nG`WA
+!!!Q/qZ$X]r9F=`!WW-!!=/Pq!!*Vgqu?a^_#=<65O&1Ji;N`BrrDiY!!%Mps7$$lJ,fQSs+("I
+!WVr\!!*-#rVus(r:L$j&)d?c!C+C!!!#7JrrD!V!:0Xbq!%hZ!RC`7rrmoQ!!3,Xr;Zm$s5<2C
+!<N6"!!*?&o`,"'_#+05+2IjU!'f`J!9!hVli-t[l2UehcN!D6!.b%K!WW)r!!48B_!:t$!WW-!
+!=/Pq!!*VGqu?a>_#=<65O&1Ji;N`BrrDin!!!P at s7$$gJGoNM5Q?95!<<)a!!*-#rVus(r:L$j
+++X7h!>",f!!#7JrrD!V!:0Xbq#16n+1hLArr at TK!!aVGn,NFns8M9`!<N6"!!*?&o`,"7JG]BJ
+&)dEe!'f`J!9!hVli-t[rVup_])V:"!.b%K#7(;/^]FK8q!%h[!WW-!!=/Pq!!+0lqu?a.i;N]V
+5O&1Ji;N`BrrDin!!#6ps7$$gJGoNK!W2lsi9C:C!WW-!!=/Pq!!+0lqu?a.i;N]V5P"gTn-Agi
+!9!hVp\t6oJ+rsCq#16n5J$marr at TJ!!%NHs60La!WW-!!=/Pq!!",<!!*>_rVup_o)J^i!.XnG
+!9!hVq#:Eb!!i8u!;QNn!'e'pnG`K=r;Zg.rVllqkl:_bs8E!!#Q45p!'g>\!=.Q^!!#7Trr`0!
+!$D+<!9!hVq#:E2!!2io!;QNn!'e'pnG`K=r;Zg"rVllekl:_bs8E!!#Q45p!'g>\!=/,n!!#7T
+rr_`j!!iE$!9!hVq#C<n!.XeD!;QNn!'e'pnG`K=qu?g at s8W)^!!*-#rVus(r:L$i5PkE]#Oq]m
+!'g)T"2Fm:!W2lsi;N`PrrDur!!",9rrDin!!#6ps7$$gJGT?HrrBju!!*-#rVus(r:L$i5PkE]
+#Oq]m!'g)T!.b%Kqu6ZSrW)co!;QNn!"\l)!;QNn!'e'pnG`K=hZ*ZXs8E!!#Q45p!'g>\!=/,n
+!!#7Ts8;otJ,K<Hi;N`PrrDQf!!!9!rrDin!!#6ps7$$gJDU>-!WW-!!=/Pq!!#7\!!*>orVup_
+o`"pjr;Zg^r;QcTrW)co!9!hV!!E#r!;QNn!'e'pnG`K=hZ*ZXs8E!!#Q45p!$D(<!=.Q^!!#7U
+rrDuq!!",=rrD!V!;ZWp_#=<6!VlZpq#16n5J$marr at T,!!*-#rVus(r:L$i+8Z$=#N5R]!'g,U
+!;QKm!"]#-!9!hVq>UGFr;cWm!;QNn!'e'pnG`K=hZ*ZXs8E!!#Q45p!?Zji!!*VgrVup_o`"p\
+r;Zg&r;QcTrW)coqu?_Hq>UHirVup_])V:"!.`r,!<N6"!!*?&o`,"7JG]BJ&)dEe!'g,U!9!eU
+!!E0!!9!hVqYpQpqu?^]q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,"7JG]BJ&)dEe!'g,U!5SO5
+!!3#t!9!hVqYpQnqu?^=q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,"'JG]BJ&&A/E!'g,U!5SO5
+!!3#t!9!hVqYpQnqu?^=q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,"'_#+05+2IjU!'g,U!.atI
+r;QcTrW)fp!;QHl!"\o*!;QNn!'e'pnG`K=hZ*ZXs8E!!#Q45p!>!!D!!+1WrVup_o`"oAquHZp
+!9!hVqYpQjqu?^-q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,"'i;<QU5J[6u!'g,UqZ$VGrVllU
+rW)fp!:]md!!i?"!;QNn!'e'pnG`K=hZ*ZXs8E!!#Q45p!=.Q\!!,<7rVup_o`+dg!.XtI!9!hV
+qYpQbqu?^%q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,!tnGE7eIt7Lu!'g/V!<2iq!'gG^!9!hV
+qYpQRqu?^!q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,!pnGE7dJ,K?I5P>$VrqcZq5Q1T^i;N`Q
+rrD!T!!!,srrDin!!#6ps7$$gJDU>-!WW-!!=/Pq!!*2squ?`sr;Zg^p&>$kqZ$U\rVllUrW)fp
+!9!bT!!E&s!;QNn!'e'pnG`K=hZ*ZXs8E!!#Q45p!<N,s!!*-"r;Zg^qZ$3g!'gG^!9!hVqYpQR
+qu?^!q>UHirVup_])V:"!.`r,!<N6"!!*?&o`,!nrquft"8r)t!'gA\!;uKi!$D1>!9!hVqYpQ2
+qu?]tq>UHirVup_])V:"!.`r,!<N6"!!*?&oDemkr;Zj'r;?Tq5PtH\nFZb]+8u3>i;N`QrrBk4
+!!!&qrrDin!!#6ps7$$gJDU>-!WW-!!=/Pp!!.S"rVus0q#(0m5PtH\i:R'M+8u3>i;N`QrrBk4
+!!!&qrrDin!!#6ps7$$gJDU>-!WW-!!=/Pp!!,="rVus at nGN=e5PtH\_"@[-+8u3>i;N`QrrBk4
+!!!&qrrDin!!#6ps7$$gJDU>-!WW-!!=/Pp!!,=BrVus`i;EWU5PtH\JFrmB+8u3>i;N`QrrBk4
+!!!&qrrDin!!#6ps7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655PtKQ!!",>rrD!V!;c]q_#+04
+!Vu`qq#16n5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rrE&j!!",>rrD!V!;c]q_#+04
+!Vu`qq#16n5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rrDuh!!",>rrD!V!;c]q_#+04
+!Vu`qq#16n5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rrDid!!",>rrD!V!;c]q_#+04
+!Vu`qq#16n5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rrDQ\!!#7^rrD!V!;c]qi;<QT
+"8VrsnGWCf5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rrD!L!!#7^rrD!V!;c]qi;<QT
+"8Vrsi;N]V5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rrBk,!!#7^rrD!V!;c]qi;<QT
+"8Vrs_#=<65J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]rr at TA!!#7^rrD!V!;c]qi;<QT
+"8VrsJGoNK5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7]s763jJ,TBIi;N`QrrDQd!!!9"
+s8;ot5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7^rrE&i!!%NIrrD!V!;c]qnGE7d#Q"H#
+rqufs5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7^rrDug!;uisi;N`QrrDil!!!Q+rrDuq
+!!#6ps7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q1T^q""L`rrD!V!;c]qq"t*l&,Q;+q#(0m
+5J$marr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7^rrDQ\!!!&trrD!V!;c]qr;6Np+8Z!;nGN=e
+J%G[Lrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7^rrD!L!!!&trrD!V!;c]qr;6Np+8Z!;i;EWU
+J%G[Lrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7^rrBk,!!!-!rrD!V!;c]qrql`r5PkB[_#48D
+s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q1T^JFigA#Q4T%i;N`Ps82isJ,90FJGfJYs7$$g
+JDU>-!WW-!!=/Pp!!+22rVutK_#4655Q1WR!!!Q-rrD!V!;ZWpJGfKDs8;ot!P/7"rr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrE&i!!",=rrD!V!;ZWp_#=<6!W)frrqufs"1eI$rr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrDug!!#7]rrD!V!;ZWpi;N]V"8`#tr;?Tq#J'm(rr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrDic!!%NHrrD!V!;ZWpnGWCf#Q"H#q#(0m&%V`0rr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrDic!;lcri;N`PrrDin!!!Q+rrDQe!!"+Os7$$gJDU>-!WW-!
+!=/Pp!!+22rVutK_#4655Q:Z_nFQ\\!W2lsi;N`PrrDur!!",;rrD!U!!#6os7$$gJDU>-!WW-!
+!=/Pp!!+22rVutK_#4655Q:Z_nFQ\\#Q+N$i;N`Os8DuuJ,90F_#465J%>UKrr at T,!!*-#rVus(
+r:Bsi+7T70!Iqo`!!#7_rrD!U!;lcuqu?^=qu6ZSrW)`n!l+d:q>UGFr;aA-nG`K=hZ*ZXs8E!!
+#Q42o!?^h1!!.Sbr;Zg^rr2uVrVup!qZ$Qq!.XnG!9!hVq#:Eb!!i?"r;Zfu\Gu'u!.`r,!<N6"
+!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hV!!E)t!Up?jrrD!V!;HKo!.XkF!<2os!!Bb2nG`K=hZ*ZX
+s8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrVup'nc&UIrW)Bd!;ucq!!g%6nG`K=hZ*ZXs8E!!#Q42o
+!?^h1!!.Sbr;Zg^rr2uVrVup/nc&UIrW)Bd!;QKm!"ZU>nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sb
+r;Zg^rr2uVrVup?nc&U)rW)Bd!:]pe!$A`NnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uV
+rVup_nc&T>rW)Bd!9!eU!'e!nnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrVuqJnc/Of
+mf*:&r;ZhIp\t9`#K6Z3rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!:g'hrquibrr at TJ
+!;?Hm!!%Mfs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`HrrDuq!:Kmc!!!&orr`0!
+!$B2[nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)co!Up?krrDim!;HKo!.XnG!<2os
+!!Duq"7Q9j#K?`4rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;ZZp!!%NHrrDQe!;QQq
+n,N^jrrDuq!!!8urr^%:!!1(=nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)fp"8i-!
++8l-=i;EZNrrTt9!W2lsq#(0m&,6)(JGoPfs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_
+i;N`Qrr_`j!!iH%!5SR6!!2lprVuqJr;Qcdr;Zg>p](0l!.W#gnG`K=hZ*ZXs8E!!#Q42o!?^h1
+!!.Sbr;Zg^rr2uVrW)fp"2Fm:!W;rtJGoNK!Vu`qr;HZr+8l-=i;EWU5PY6Yrqufs5K<`mrr at T,
+!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;c]qJGoQHs8;ot"8Vrsq#16n&,cG-_#465J,'$D
+r;?Tq+3+?Mrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;c`o!!%NJrrE&s!!!,srrDQf
+!!!9%rr at TJ!;HKnq#(0m&'"Y=rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrrqufs
+5Q:Z_r;?Tq#PnB"i;N]V"8r2t!!!&orrDQe!!!8Ds7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`RrrDuq!!",?rrDim!!!Q*rrBk6!!!&urrE&s!!!,qrrD!U!!!, at s7$$gJDU>-!WW-!
+!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrDid!!",:rr at TA!!!8urrBk+!!%Mqs7$$gJDU>-!WW-!
+!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrDQ\!!#7Zs763j&,6)(_".O++41&Wrr at T,!!*-#rVus(
+r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcri:I!LJ,90Frpp*i+8>d8JF`a@#LNM?rr at T,!!*-#rVus(
+r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcr_"7X&rrDug!!#7Xrr at T@!!!,Js7$$gJDU>-!WW-!!=/Pp
+!!+22rVutK_#4655Q:Z_i;N`RrrBk-!!!&qrrDug!!%NCs7--i!RUl9rr at T,!!*-#rVus(r:Bsi
++7T70!Iqo`!!#7_rrD!V!;lcrJFrmB"8Vrsq""LZs7$*9s7$$gJDU>-!WW-!!=/Pp!!+22rVutK
+_#4655Q:Z_i;N`Rrr at TB!!!9"rrDid!!!&orrE&g!!%Mrs7$$gJDU>-!WW-!!=/Pp!!+22rVutK
+_#4655Q:Z_i;N`Rs7?9k&,H5*nFQ\\"8Dfqrp]sg5LKN#rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`
+!!#7_rrD!V!;lfg!!",:rrDQ\!!!8urrE&g!!"+gs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`SrrE&j!!#7ZrrD!L!!!Q(rrE&g!!!PWs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`SrrE&j!!%NErrD!L!!",8rrDue!!!8Os7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`SrrE&j!;QQoi:I!L5PP0Xr:'ae"4@/<rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_
+rrD!V!;uisrq-6k!VlZpi:I!LJ+rsCr:'ae!R^r:rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_
+rrD!V!;uisr:L$i"8Mlr_"7X#rrDud!7Ci9rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;uisr:L$i#Pe<!_"@[-!VZNnr9s[dJ("Adrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;uisr:L$i&,?/)_"@[-"8;`pr9s[d5LTT$rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;uisr:L$i+8Gj9_"@[-#PS/tr9s[d+4C2Yrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;uisr:L$i5PY6Y_"@[-&,-#'r9s[d&(:LIrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;uisr:L$iJ,'$D_"@[-+85^7r9s[d#L`YArr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;uisr:L'arrBk-!!#7WrrE&f!!!,Ls7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`S
+rrDuj!!!&orrBk-!!%NBrrE&f!!!&Js7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`S
+rrDuj!!!,qrrBk-!;6?lrpKj8s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrE&l
+!!!8urrD!N!!!&mrrE&e!!%Mts7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrE&l
+!!!Q(rrD!N!!!,ns6fpf5L]Z%rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;uisrq6<l
++8>d8i:[-N#PA&b!!"+is7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrE&l!!#7X
+rrD!N!!!Q%rr at T=!!!PYs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rs7QEmJ+rsC
+nFch^+8#R5JFEO=#Li_Brr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lfj!!!&nrrDQ^
+!!%N at rrBk(!!!,Ms7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rrr at TE!!!8trrDig
+!!!&krrBk(!!!&Ks7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rrr at TI!:g'hq"t*l
+&+9Gti;EWU"8VrsJGfJss7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrBk5!!!&i
+rrDup!!",/rrDQe!!!9"rrBk5!!%Mus7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`R
+rrBk5!!!&irrDup!!",/rrDim!!!Q*rrD!U!!#75s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`RrrD!U!!!,krrE&r!!#7OrrDuq!!",:rrDQe!!#75s7$$gJDU>-!WW-!!=/Pp!!+22
+rVutK_#4655Q:Z_i;N`RrrDQe!!!8ns82isJ+!=:rqufs5Pb<Zq#(0m+4U>[rr at T,!!*-#rVus(
+r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrq#(0m&+KT!JGfK7s8;otJ,0*Er;?Tq+4U>[rr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrr;?Tq+7T:1_#=<6!UTgdJGoQDrrE&s!!!PZs7$$g
+JDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrE&s!!#7QrrD!V!!!,frr^%:!!2ior;Zg.
+eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqZ$Ko!.XM<!:]sf!!hlj"2Fm:!VcTo
+JGoNK&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;c]qJGoQ;rrDin!!!Prrr^%:
+!!2io!5SR6!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)fp"2Fm:!Up$gr;HZr
++70"0^]4?8p\t6OrVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqYpZe!!!8l
+s8DuuJ*d1;^]4?8p\t6_rVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqYpZq
+!!",/rrTt9!UKaf^]4?8p\t6grVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hV
+q>^Hp!.XG:!q60pli.(&!!!&orrDur!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_
+i;N`PrrMTom/I(dJ*[+:^]4?8p\t6mrVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_
+!9!hVb5VRZ!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`!rr^%:
+!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW(+@"2Fm:!VcTorr)lt
+&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!6Y<D^]4?8p\t6mrVup/eGo%<!.`r,
+!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVb5VRZ!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp
+!!+22rVutK_#4655Q:Z_i;N`!rr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sb
+r;Zg^rr2uVrW(+@"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;ZWqn-A=[!<@W8rr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uV
+rW)corr<%Kmf*@X!!hii"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_
+rrD!V!;c]tqu?^=mf*@(!!2Ec"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`
+!!#7_rrD!V!;c]tn,NFnmf37d!.XA8"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70
+!Iqo`!!#7_rrD!V!;c]t^]4?8n,ECcrVup?m/I1'!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp
+!!+22rVutK_#4655Q:Z_i;N`Qrr at TK!:Tpfq#16n&+';u^]4?8p\t6mrVup/eGo%<!.`r,!<N6"
+!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqZ$Ko!.XM<!:]sf!!hlj"2Fm:!VcTorr)lt&(LXKrr at T,
+!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrrqufs5Oe[Qi;N]V"76$i^]4?8p\t6mrVup/
+eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqu6Zor;Zg>nG`L(rVup!m/I1'!!!&o
+rrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrDim!!!Q+rrMU2q>UGF
+r;c3a"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrnGN=e
+#Q"H$^^']8qu?_HmJd:(!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_
+i;N`RrrD!U!!!,trrE*#qYpQpqu?^]mJd:(!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22
+rVutK_#4655Q:Z_i;N`RrrBk+!;c]qr;6Np+79(1^]4?8p\t6mrVup/eGo%<!.`r,!<N6"!!*?&
+oDen6nGWCgJ&)!_!'gJ_!9!hVqu6Z3o)Jc?qu6Zoqu?^=mJd:(!!!&orrE&t!!!PZs7$$gJDU>-
+!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rrr at T@!!#7\rrDil!!!Psrr^%:!!2io!<2rt!"[TZ
+nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iq!.aY@!$D+<!;Q0d!.X\A"2Fm:!VcTo
+rr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lfe!!!Q,rrDQ\!!",6rr^%:
+!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iqnc/Xpqu6ZcoDejr
+p&>-0!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrE&h!!!,u
+rrD!L!!!,orr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)lr
+!<2Nh!!2us!9!JL!!2cm"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_
+rrD!V!;uisrp^!crrD!K!;6?o^]4?8p\t6mrVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_
+!'gJ_!9!hVr;QcrnGiQ=r;QcTo)Jc?pAY61!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22
+rVutK_#4655Q:Z_i;N`SrrDue!!#7]rrBk+!!#7Wrr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!
+#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)lr!;u?e!$D.=!5S1+!$Cq7"2Fm:!VcTorr)lt&(LXKrr at T,
+!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;uisr:'ae&,cG-_".O+&,-#*^]4?8p\t6mrVup/
+eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVr;QcpnGiOor;Qc4o)JaqpAY61!!!&o
+rrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrDue!!!-!rrBk+!!!,p
+rr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)lr!;u?e!!3#t
+!5S1+!!2fn"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;uis
+r9s^arrBk*!;?Ep^]4?8p\t6mrVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hV
+r;Qcpn,NH<rVll5nc/Z>p\t?2!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`SrrDud!!#7^rrBk*!!#7Xrr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1
+!!.Sbr;Zg^rr2uVrW)lr!<2Hf!$D1>!9!DJ!$Ct8"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(
+r:Bsi+7T70!Iqo`!!#7_rrD!V!;uisrpTmf&,lM.i:6jJ&,6)+^]4?8p\t6mrVup/eGo%<!.`r,
+!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVr;Qcrn,NFnrVllUnc/Xpp\t?2!!!&orrE&t!!!PZ
+s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrE&f!!!-"rrD!J!!!,qrr^%:!!2io
+!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iqn,NFhrVllenc/Xlp\t?2
+!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rs6fsdrrDQZ!!!&o
+rr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iq!.aP=!.Y"J
+!;Q*b!!2io"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcr
+JFEO=5Q:Z_q!e at Yrr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uV
+rW)iq!5S((!$D4?!;u?ep\t?2!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`RrrBk(!!!Q/rrDue!;HKq^]4?8p\t6mrVup/eGo%<!.`r,!<N6"!!*?&oDen6nGWCg
+J&)!_!'gJ_!9!hVqu6ZSr;Zg"qYpQnr;Zg&rr2utnGr=`"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrnGN=e#Q"H#rqufs"9&8i!;HKq^]4?8p\t6mrVup/
+eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqu6Zkr;Zg.q>^Bn!!3&u!.aV?p\t?2
+!!!&orrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrDuq!!",:rr at TJ
+!<)ot_#=<6!W)frJGoQCrr^%:!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^
+rr2uVrW)iq!<2os!'g;Z!5SO5!.Y"J!9!hV!!E)t!5SR6p\t?2!!!&orrE&t!!!PZs7$$gJDU>-
+!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Qs8;otJ,0*Ei;EWU5Q:Z_nGWCf#Q"H#i;N`Nrr^%:
+!!2io!<2rt!"[TZnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)fp!.b%Kq#:?`r;Zg>
+rr2unrVup/qYpQRrW)]m"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_
+rrD!V!;c]t^]4?8q#:?hr;Zg.rr2urrVup?qYpQRrW)]m"2Fm:!VcTorr)lt&(LXKrr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;c]tn,NFnq#:?lr;Zg&rVuis!.XkF!9!hVp\t?2!!!&o
+rrE&t!!!PZs7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Qrr`0!!$D"9!<2os!!E3"
+!l+d:q>UHQrW)]m"2Fm:!VcTor;HZr#LreCrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V
+!;ZZp!!%NCs8;ot!WE$"n,N^hrrD!V!;HKq^]4?8p\t6grVup#eGo%<!.`r,!<N6"!!*?&oDen6
+nGWCgJ&)!_!'gJ_!9!hVq>UKb#PS/tJGfKGrrE+Lq>UHQrW)]m"2Fm:!VcTonGWCf!S%/=rr at T,
+!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!:9^c_#465J+N[?i;N`Nrr^%:!!2io!9!eUeGo%<
+!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVm/I(Dr;Zg^oD\gKrW)]m"2Fm:!VcTo_#465
+J(=Sgrr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!:9^cnGN=e+7oL4i;N`Nrr^%:!!2io
+!.b"J!'f$6nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)<b!;QKm!"\]$!9!hVp\t?2
+!!!&os82is+4^D\rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!:9^cr;?Tq#P7rqi;N`N
+rr^%:!!2lp!<2lr!"[W[nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)<b!<2os!!Dim
+!9!hVp\t?2!!!&prrE&r!!!P[s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Bs8;ot
+!V?<ki;N`Nrr^%:!!2lp!;u`p!!h'SnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)co
+!Up?err at TJ!<)ou!.XhE!9!hVp\t?2!!!&prrDup!!!8Ss7$$gJDU>-!WW-!!=/Pp!!+22rVutK
+_#4655Q:Z_i;N`Ps8N'!J+imB_#46:J,fQ<!!i?"!9!hVp\t?2!!!&prrDil!!!,Os7$$gJDU>-
+!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Qrr`0!!$Cq7!9!eU"[E%e^]4E2rrD!V!;HKq^]4?8
+q#:?hqu?^!ec5.=!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqYpZe!!!8trrDQe!!48B
+s8DuuJ,90Fi;N`Nrr^%:!!2lp!:]md!!1XMnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uV
+rW)fp"2Fm:!VZNnq#(0o&-)S,!!",;rrD!V!;HKq^]4?8q#:?`qu?]tec5.=!.`r,!<N6"!!*?&
+oDen6nGWCgJ&)!_!'gJ_!9!hVqYpPGrW)Zl!;ucq!XJc#rVup/qYpQRrW)]m"2Fm:!VlZpnGE7d
+!S.5>rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;c`o!!%NCrrE&s!!39&nGWCf#Q"H#
+i;N`Nrr^%:!!2lp!:]md!!1XMnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iq!<2os
+!'g2Wr;Zm"s5<kV!!E)t!9!hVp\t?2!!!&prrD!S!7h,=rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`
+!!#7_rrD!V!;lcrr;?Tq+85^7JGfHKs1nU6!!2rr!9!hVp\t?2!!!&prrD!S!7h,=rr at T,!!*-#
+rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcrq#(0m&,-#'_#466It7ItqYpQRrW)]m"2Fm:!VlZp
+i;3N)s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`RrrDQe!!!8trrD!U!!#7]!!%NG
+rrD!V!;HKq^]4?8q#:?PqZ,1HnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iq!9!eU
+!!Drp!:]pe!$;%<!'gA\!9!hVp\t?2!!!&prrD!S!7h,=rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`
+!!#7_rrD!V!;lcr_"[m05PtH\q"Fag+8c'<i;N`Nrr^%:!!2lp!9!_Sec5.=!.`r,!<N6"!!*?&
+oDen6nGWCgJ&)!_!'gJ_!9!hVqu6Z3p](:)qu6ZopAb18qu6ZSrW)]m"2Fm:!VlZpi;3N)s7$$g
+JDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rrr at TE!!!,urrE&m!!!Q,rrD!V!;HKq^]4?8
+q#:?PqZ,1HnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)iq!.ahE!!2rrpAb1(qu6ZS
+rW)]m"2Fm:!VlZpi;3N)s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rs7QHhrr at TE
+!!!9$rrD!V!;HKq^]4?8q#:?`qu?]tec5.=!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hV
+qu??i!.XnG!5S at 0!!iE$!9!hVp\t?2!!!&prrDQd!!!&Ms7$$gJDU>-!WW-!!=/Pp!!+22rVutK
+_#4655Q:Z_i;N`SrrE&l!!#7\rrD!P!!!,urrD!V!;HKq^]4?8q#:?`qu?]tec5.=!.`r,!<N6"
+!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVr;Qcrp&G(7qu6Zcp](9rqu6ZSrW)]m"2Fm:!VlZpnGE7d
+!S.5>rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;uisrq6<l&,ZA,q"Ogh"8i)ui;N`N
+rr^%:!!2lp!;QHl!!CdOnG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)lr!<2Zl!!iE$
+!;uTl!!E,u!9!hVp\t?2!!!&prrDil!!!,Os7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_
+i;N`SrrDuj!!!,urrE&n!!!&srrD!V!;HKq^]4?8q#:?lqu?^%ec5.=!.`r,!<N6"!!*?&oDen6
+nGWCgJ&)!_!'gJ_!9!hVr;Qcpp&G'nqZ$<j!!2us!9!hVp\t?2!!!&prrDup!!!8Ss7$$gJDU>-
+!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrDui!;c]qJGB0F!W2lsi;N`Nrr^%:!!2lp!<2lr
+!"[W[nG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uVrW)lr!;uKi!.XnG!5SC1!!2us!9!hV
+p\t?2!!!&prrE&r!!!P[s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrDui!!#7\
+rrD!Q!!!&srrD!V!;HKq^]4?8p](-k!$BbknG`K=hZ*ZXs8E!!#Q42o!?^h1!!.Sbr;Zg^rr2uV
+rW)lr!;uKi!$D+<!:]da!!2us!9!hVp\t?2!!!&orr at TJ!!#76s7$$gJDU>-!WW-!!=/Pp!!+22
+rVutK_#4655Q:Z_i;N`SrrDui!!!Q,rrDii!!!&srrD!V!;HKq^]4?8p\t6/r;ZhIec5.=!.`r,
+!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVr;Qcpo`+ssqu6Zoq#CBqqu6ZSrW)]m"2Fm:!VcTo
+i;EZ*s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrDui!!!,urrE&o!!!&srrD!V
+!;HKq^]4?8p\t6_rVup!eGo%<!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVr;Qcro`+sm
+qZ$?k!!E,u!9!hVp\t?2!!!&orrDin!!!,Ns7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_
+i;N`SrrE&j!;c]qJGK6G"8i)ui;N`Nrr^%:!!2io!;ufr!!h$RnG`K=hZ*ZXs8E!!#Q42o!?^h1
+!!.Sbr;Zg^rr2uVrW)lr!<2Tj!.XnG!5SF2!!E,u!9!hVp\t?2!!!&orrE&t!!!PZs7$$gJDU>-
+!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`SrrE&j!!#7\rrD!R!!!,urrD!V!;HKq^]4?8pAY5F
+!!#75s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#4655Q:Z_i;N`Rs7?9k+8c'<nG3+b#Q+N$i;N`N
+rr^%:!!2fn!oO%Ye,Sq;!.`r,!<N6"!!*?&oDen6nGWCgJ&)!_!'gJ_!9!hVqu?9g!"\u,!;QBj
+!!iE$!9!hVp\t?2!!!&nrrVrq"4R;>rr at T,!!*-#rVus(r:Bsi+7T70!Iqo`!!#7_rrD!V!;lcr
+JFrmB#Q+N$r;$Bn&,ZA,i;N`Nrr^%:!!2cm!J!D`s7$$gJDU>-!WW-!!=/Pp!!+22rVutK_#465
+5Q:Z_i;N`Rrr at TB!!!,urrE&p!!!Q,rrD!V!;HKq^]4?8`W,H-!.`r,!<N6"!!*?&oDen6nGWCg
+J&)!_!'gJ_!9!hVqu6Z3o`+smqYpPGqZ$U<qu6ZSrW)]m"2Fm:!QG*.rr at T,!!*3%rVus(rq$0k
++7T70!Iqo`!!#7_rrD!V!;lcr_"7X'rrD!S!!",<rrD!V!;HKq^]4?8`W,H-!.`r,!<`?#!!*3%
+oDen6nGWCgJ&)!_!'gJ_!9!hVqu6ZSr;Zg"rr2u6r;ZhIqYpQpqu?^]qu6ZSrW)]m"2Fm:!QG*.
+rr at T,!!*?&rW!!#s+'bB!?^h1!!.Sbr;Zg^rr2uVrW)iq!:]pe!!iN'!9!eU!'g;Zqu?_Hqu6ZS
+rW)]m"2Fm:!QG*.rr at T,!!*W*r;Zlu_"@[.+7T70!Iqo`!!#7_rrD!V!;lcrq#(0m&,uS/nGN=e
++8Pp:JGfKErrD!V!;HKq^]4?8`W,H-!.`r,!?^h0!!.Sbo`,"7nGWCgJ&)!_!'gJ_!9!hVqu6Zo
+r;Zg>rr2unr;Zg.q>UH1rVup!qYpQRrW)]m"2Fm:!QG*.rr at T,!!+2"r;Zj_i:R'N+7T70!Iqo`
+!!#7_rrD!V!;lcrrqufs5Q:Z_r;?Tq#PnB"i;N]V"8`#ti;N`Nrr^%:!!1(=nG`K=hZ*[A_#466
++7Sq'!?^h1!!.Sbr;Zg^rr2uVrW)fpr;ZhIrr2utr;Zg"q>UHarVup'qYpQRrW)]m"2Fm:!QG*.
+rr at T,!!,<7r;Zj/nFZb^+7T70!Iqo`!!#7_rrD!V!;c]qJGoQHs8;ot!Vu`qq#16n&,Q;+i;N`N
+rr^%:!!1(=nG`K=hZ*Y+qu?a&q"4Uf+7T70!Iqo`!!#7_rrD!V!;c]t^]4?8r;QbIr;cZn!;ufr
+!$D(;!9!hVp\t?2!!!&=s7$$gJDU>,J,B9I#Pdrl!?^h1!!.Sbr;Zg^rr2uVrW)fp"7Q9j#Q4T%
+_#465J,0-D!!%NFrrD!V!;HKq^]4?8`W,H-!.`r,!<2lr!<`8m!!+22rVutK_#4655Q:Z_i;N`Q
+rr`0!!$D.=!9!eU!'g;Z!l+d:q>UHQrW)]m"2Fm:!QG*.rr at T,!!*#r!!*3"o`,"7nGWCgJ&)!_
+!'gJ_!9!hVq>^Hp!.XqH!:]pe!$D%:!q60pq>UHQrW)]m"2Fm:!QG*.rr at T,!!)rp!!*,uo`,"7
+nGWCgJ&)!_!'gJ_!9!hVq>UKb#Q+N$q#(0m&,?/*!.XhE!9!hVp\t?2!!!&=s7$$gJDU>,r;6Nq
+!W;Tj!?^h1!!.Sbr;Zg^rr2uVrW)Kg!;ucq!!hrl!9!hVp\t?2!!!&=s7$$gJD^D.!VlNl!<2Wk
+!?^h1!!.Sbr;Zg^rr2uVrW)Kg!<2os!!DZh!9!hVp\t?2!!!&=s7$$gJD^D.!VlNl!<2Wk!?^h1
+!!.Sbr;Zg^rr2uVrW)Hfr;Zfumf*:FrW)]m"2Fm:!QG*.rr at T-!!*,qqZ$Wqo`,"7nGWCgJ&)!_
+!'gJ_!9!hVnG`K=r;c9c!9!hVp\t?2!!!&=s7$$gJD^D.!VlNl!<2Wk!?^h1!!.Sbr;Zg^rr2uV
+rW)Hf!5SO5!.XJ;!9!hVp\t?2!!!&=s7$$gJD^D.!V#sd!.OS@!?^h1!!.Sbr;Zg^rr2uVrW)Hf
+!9!eU!'frP!9!hVp\t?2!!!&=s7$$gJD^D."7Z0f!.OS@!?^h1!!.Sbr;Zg^rr2uVrW)Hf!:]pe
+!$C\0!9!hVp\t?2!!!&=s7$$gJD^D."7Z0f!.XYA!?^h1!!.Sbr;Zg^rr2uVrW)co!Up?jrrDim
+!!!Q*rrE+Lq>UHQrW)]m"2Fm:!QG*.rr at T-!!*2kqZ$VGo`,"7nGWCgJ&)!_!'gJ_!9!hVq>^Hp
+!.XnG!;ucq!!iB#!q60pq>UHQrW)]m"2Fm:!QG*.rr at T-!!*2kqZ$VGo`,"7nGWCgJ&)!_!'gJ_
+!9!hVqYpZq!!",<rrE&s!!!,trrTt9!Vu`qi;N`Nrr^%:!!1(=nG`K=huEc[nG<1cJ+WdB+7T70
+!Iqo`!!#7_rrD!V!;c]tn,NFnqZ$*d!.XkF!9!hVp\t?2!!!&=s7$$gJD^D."7Z0f!.XYA!?^h1
+!!.Sbr;Zg^rr2uVrW)fp"2Fm:!W)frJF`a at +8Z!;i;N`Nrr^%:!!1(=nG`K=huEcYnG<1cIe<[A
++7T70!Iqo`!!#7_rrD!V!;c]qJGoQFrrBk+!!!Q+rrD!V!;HKq^]4?8`W,H-!.`u-!<M]e!!%K@
+!!+22rVutK_#4655Q:Z_i;N`Qs8;otJ,B6Gi:?pK#Q"H#i;N`Nrr^%:!!1(=nG`K=huEcYq"k$k
+rq-6l+7T70!Iqo`!!#7_rrD!V!;lcrrqufs5PtH\nFHV["8`#ti;N`Nrr^%:!!1(=nG`K=huEcY
+q"k$krq-6l+7T70!Iqo`!!#7_rrD!V!;lcrr;?Tq+8c'<q""Ic!W)fri;N`Nrr^%:!!1(=nG`K=
+huEcYq"k$krq-6l+7T70!Iqo`!!#7_rrD!V!;lcrq#(0m&,ZA,r:0jarrD!V!;HKq^]4?8`W,H-
+!.`r,!;QEk!;uKi!?^h1!!.Sbr;Zg^rr2uVrW)iq!:]pe!!iE$!<2Nh!.XnG!9!hVp\t?2!!!&=
+s7$$gJDU>,r;6Nq!W;Tj!?^h1!!.Sbr;Zg^rr2uVrW)iq!9!eU!!E)tnc/YSqu6ZSrW)]m"2Fm:
+!QG*.rr at T,!!)rp!!*,uo`,"7nGWCgJ&)!_!'gJ_!9!hVqu6Z3q#CC*rr2tKo)Jb4qu6ZSrW)]m
+"2Fm:!QG*.rr at T,!!*#r!!*3"o`,"7nGWCgJ&)!_!'gJ_!9!hVqu6Z3q#CBsrr2u6o)Jb4qu6ZS
+rW)]m"2Fm:!QG*.rr at T,!!%KG!!*2so`,"7nGWCgJ&)!_!'gJ_!9!hVqu6YHp]1<n!9!GK!"\u,
+!9!hVp\t?2!!!&=s7$$gJDU>,J,B9I#Pdrl!?^h1!!.Sbr;Zg^rr2uVrW)iq!.ahE"+U at NnFHV[
+&,ZA,i;N`Nrr^%:!!1(=nG`K=hZ*X at qu?a&nFZb^+7T70!Iqo`!!#7_rrD!V!;lfj!!>Ics7l0c
+!!iE$!9!hVp\t?2!!!&=s7$$gJDU>-5Ci\4!>"\m!!,=RrVutK_#4655Q:Z_i;N`Rs7ZKq+92B=
+o)Jaqqu6ZSrW)]m"2Fm:!QG*.rr at T,!!+1Wr;Zj?i:R'NJ+3F;!Iqo`!!#7_rrD!V!;uisrq?Bp
+&-)\/o)Jamqu6ZSrW)]m"2Fm:!QG*.rr at T,!!+2"r;Zj__"@[.s7$!f!Iqo`!!#7_rrD!V!;uis
+rq?Bm#QFbp!!!,urrD!V!;HKq^]4?8`W,H-!.`r,!>"\u!!.Sbp&G-ps7$!f!Iqo`!!#7_rrD!V
+!;uisrq?Bm"9/<#JFigA"8i)ui;N`Nrr^%:!!1(=nG`K=hZ*Z^q#(0ns+'eC!X&JlrVutK_#465
+5Q:Z_i;N`SrrE&m!!!'!rrBk,!!!,urrD!V!;HKq^]4?8`W,H-!.`r,!<`9!!!*-#o`,$us5<kV
+!Iqo`!!#7_rrD!V!;uisr:U-irrD!L!!!&srrD!V!;HKq^]4?8`W,H-!.`r,!<`?#!!*3$o`,%(
+s1nU6!Iqo`!!#7_rrD!V!;uisr:U*mJ,fQ<oDejlqu6ZSrW)]m"2Fm:!QG*.rr at T,!!*-#rVus(
+rq-6m+9-lj!!.Sbr;Zg^rr2uVrW)lr!;uNj"$chcq"+Od!W2lsi;N`Nrr^%:!!1(=nG`K=h>d`Z
+^]4?Vr:L$j5QCZ^!Is&+!!#7_rrD!V!;uisr:U*m+92B=oDejlqu6ZSrW)]m"2Fm:!QG*.rr at T+
+!!RlA!!%NDo`,#BrquftJ)L8*!'gJ_!9!hVr;Qcpp&G1*s8W)j!!!&srrD!V!;HKq^]4?8`W,H-
+!.`o+"X!XB!WV*N!!3,tr;ZkJ_#4655Q:Z_i;N`SrrDuj!!!9's7?9k!W2lsi;N`Nrr^%:!!1(=
+nG`K=h>d]is1f`V_"Ia0!WVrn!!.Sbr;Zg^rr2uVrW)lr!;uNj!!E6#!.a_B!!2us!9!hVp\t6/
+rW'q;nG`K=h>dN\rVlkJp&G-rs7#se!Iqo`!!#7_rrD!V!;uisr:U*j!WN*!_"@[-!W2lsi;N`N
+rrBk6!!%Mgs7$$gJDL8+!WN*!rq-6m#QN`\!!3,8r;Zg^rr2uVrW)lr!;uKirr2ufo`+smqu6ZS
+rW)]m!5SR6!'eL'nG`K=h#RHS!;uKi!Y>=Hr;ZluJGfHJ5Q:Z_i;N`SrrE&k!!@`Ns8;Ni!!E,u
+!9!hVp\t6/rVup?`rGQ.!.`l*"$chci:R'O+9-lj!!33$JGfHJ5Q:Z_i;N`SrrE&k!!#7YrrD!T
+!!!,urrD!V!;HKn_#=<6&'"Y=rr at T*!!<K+s+'bB!C-V_!!33$JGfHJ5Q:Z_i;N`SrrE&k!!",9
+rrD!T!!!,urrD!V!;HKn_#=<6#KHf5rr at T)!!,=RoDeoArqufu"93oM!!#7_rrD!V!;uisrq-6k
+&,?/)i;<QT"8i)ui;N`NrrBk6!!!, at s7$$gJB\&ps8;fq!=/Z&!!#7_rrD!V!;lfh!!!9!rrDQd
+!!!9$rrD!V!;HKn_#=<6!QP0/rr at Sp!!33$q#(0n&-)P-!'gJ_!9!hVqu?<h!!E#r!:]md!!iE$
+!9!hVp\t6Or;ak;nG`K=c2[nJs7#se!?_==!!#7_rrD!V!;lcrJG&sC!VlZpq"t*l&,ZA,i;N`N
+rrD!U!!%Mhs7$$gJBe,r#QN`\!!,=`qu?^]rr2uVrW)iq!.a_Bq#:?hqu?^-qu6ZSrW)]m!:]pe
+!'eO(nG`K=c2[nVs1nR5!It%F!!#7_rrD!V!;lcr_"@[-J,0*Er;6Np+8c'<i;N`NrrDQe!!"+]
+s7$$gJBe,r+9-li!!3,tqu?^]rr2uVrW)iq!5S7-!'g;Z!;u`p!$D+<!9!hVp\t6gr;Zg.a8bZ/
+!.`>p!C-V_!!33$q"t*l5Q:Z_i;N`RrrD!U!!<?'s7#se!$D%:!<2lr!'gA\!9!hVp\t6kr;Zg&
+a8bZ/!.`>p!It+I!!39&q"t*l5Q:Z_i;N`RrrDQe!!<K+s7lNm!"\l)qu?_Hqu6ZSrW)]m!<2os
+!!C:AnG`K=c2[nFr;?Ts#QN`[!!#7_rrD!V!;lcrq#(0p&-)\-r;Zg&q#:>Er;c]o!9!hV!.XbC
+r;Zfua8bZ/!.`Aq!Wi>rr;Zm0s1nO4!'gJ_!9!hVqu6Zor;ZpAs8W)s!!!,rrrBk6!!!&rrrD!V
+!!#7Xrr at TJ!6>-/rr at Sq!!39&nGN=g+9-lh!!#7_rrD!V!;lcrrqufs5Q:]]!!!&prrD!V!!!,t
+rrD!V!!",8rrBk5!!%Mis7$$gJBn2s#QN`\!!,=aqZ$U\rr2uVrW)fpr;ZhIrr2tKr;cWm!:]sf
+!!iB#!9!hV!"\i(!9!eU!'eR)nG`K=cN""Ws1nR5!It+G!!#7_rrD!V!;c]qJGoQIrrBk5!!%NE
+rrDin!!!Q+rrD!V!!!8urrDQe!!"+^s7$$gJBn2s+9-li!!3,tqZ$U\rr2uVrW)fp"2Fm:!WE#u
+i;EWU5Pb<Zr;HZr+8Z!;i;N]V"8Dfqq#(0m&'4e?rr at Sq!!,=ar;Zm"s7lHk!'gJ_!9!hVqYpZe
+!!!9&rrDQe!!",9s8DuuJ,90Fi;N]V!VcTor;?Tq#KZr7rr at Sq!!.TKr;Zm$s7#mc!'gJ_!9!hV
+qYpZq!!",>rrDim!!!Q)rrTt9!Vu`qi;EZMrrE&s!!!,Bs7$$gJBn2rs8;fq!XJb`qZ$U\rr2uV
+rW)corr<%KrVllqr;Zg&q#:Eb!!i?"!:]pe!.XbCr;ZfuaT(c0!.`Dr!Wi>rr;Zm0s1nL3!'gJ_
+!9!hVq>UKb#Q4T%rqufs"8Dfr!.XhE!:]pe!'g5X!.b"JaT(c0!.`Dr!X&Jlr;Zm at s+'tH!'gJ_
+!9!hVnc/Of!!2Ke!;QKm!$Ct8!5SO5!.W,jnG`K=ci=+Ps5<hU!C-V\!!#7_rrD!V!:g'hJGfK8
+rrDim!!!Q(rrD!U!!#7*s7$$gJC"8t&-'HD!!.TKq>^L[rr2uVrW)Kg!5SO5!.XG:!;ucq!!i8u
+!:]pe!$B>_nG`K=ci=+hs+(%J!WW)o!!#7_rrD!V!:g'hi;EWU5OSOOrqufs"8Dfqq#(0m&'=k@
+rr at Sr!!,=ar;Zm"s7lEj!'gJ_!9!hVnc&UYr;Zg>mJm+b!!2io!;ucq!!gXGnG`K=ci=)rrqufu
+"97le!!#7_rrD!V!:g'hq#(0m&+0AsJGfKBrrE&s!!!,Cs7$$gJC"8ss8;fq!XJb`q>^L[rr2uV
+rW)Kg!;ucq!!hok!5SO5!.XbCr;ZfuaoCl1!.`Gs!Wi>rr;Zm0s1nI2!'gJ_!9!hVq>UKb#Q+N$
+rqufs"8Mls!.XeD!9!eU!'g5X!.b"JaoCl1!.`Gs!X&Jlr;Zm at s+'qG!'gJ_!9!hVq>^Hp!.XnG
+r;Zfuq>UNc!!i<!!:]pe!$Ct8!5SO5!.W/knG`K=d/X4Qs5<hU!C-V[!!#7_rrD!V!;c]tqu?^=
+qu6YHr;cZn!l+d:q#:?hr;Zg.p\t6Or;Zg^b5^u2!.`Gs!Y>=Hr;ZkJrqQNo5Q:Z_i;N`Qrr_`j
+!!iE$!5SO5!.XkFrVuqJq>UHmr;Zg&p\t6_r;Zg>b5^u2!.`Gs![%Gmr;Zlur:p<m5Q:Z_i;N`Q
+rr^%:!!2us!9!eU!'gA\!;ufr!$D%:!<2os!!Duq!;QKm!"[6PnG`K=d/X23s8;p!!WVrj!!#7_
+rrD!V!;c]qJGoQGrrDQe!!",<rrDin!!!Q)s8;ot!VcTor;?Tq#Km)9rr at Ss!!.TKr;Zm$s7#ga
+!'gJ_!9!hVqZ$Ko!.XqH!;QKm!"\u,!:]sf!!i<!!.b"Jp\t6mr;Zg"b5^u2!.`Gs!WW)r!!3E*
+i;!?Q5Q:Z_i;N`RrrE&s!!#7]rrDuq!!!9$rrD!V!!!,rrrBk5!!%NCs8;ot!QtH3rr at St!!33$
+q#(0o&-'H@!!#7_rrD!V!;lcrr;?Tq+8l-=rqufs"8i)u_#=<6!VlZpi;EWU5PP0XJGfJjs7$$g
+JC4E!"97lh!!48BJGB0F5Q:Z_i;N`RrrDim!!!Q,s8;ot!W2lsJGfKCrrDQe!!48#&,H5*_#466
+?k:K]nG`K=dJs=Rs5<hU!C-VZ!!#7_rrD!V!;lcrnGN=e#Q+N$JGfKFs82isJ,0*Eq#(0o%KHM)
+rrD!S!6kK4rr at St!!3]2_#466J,]3C!'gJ_!9!hVqu6ZSr;Zg"qu6Z3r;ZhIrVllsqu?^]q>UHm
+q>^L[qYpQbqZ$U\c2[;5!.`Jt![%Gmr;Zlur:g6l5Q:Z_i;N`RrrBk5!!!&srrD!U!!#7^rrDup
+!!",:rrE&p!!!Q+rrDik!!!PSs7$$gJC4Du5QCZ^!Wi>rp](:Yrr2uVrW)iq!5SO5!!2us!:]pe
+!$D1>!;u`p!$D"9q>^KtqYpQnqZ$Tuc2[;5!.`Jt!It+I!!39&nFut`5Q:Z_i;N`Rrr at TI!;lcr
+q#(0m&,lM.q"t*l&,?/)JGT<H!W)frrqcZq!R:Z6rr at St!!3,tr;Zm(s5<YP!'gJ_!9!hVqu6YH
+quHWo!;ucq!!iK&!;QHl!"\l)!5SF2q>^9kc2[;5!.`Mu!Wi>rr;Zm0s1nC0!'gJ_!9!hVqu?Nn
+!.XqH!<2os!!E3"!:]md!!i<!!9!\R!.XkF!.aqH!.W;onG`K=df9FOs7#se![%Gmp](:Yrr2uV
+rW)iqqZ$VGqu?Tp!!3&u!:]md!!i<!!:]gb!'g>[!5SI3!'ed/nG`K=df9FSs5<hU!C-VY!!#7_
+rrD!V!;uisrqcZq5PtH\JGfKHrrD!T!!!,rrrDij!!",;rrD!S!!"+ds7$$gJC=K"&-'HD!!.TK
+pAb1Xrr2uVrW)lr!<2iq!'gA\!5SO5!.Y"J!9!bT!!E#r!;uZn!"\r+!:]jc!"[BTnG`K=df9Fk
+s+(%J!WW)l!!#7_rrD!V!;uisrqcZq5PtH\i;EWU5Q:Z_i;<QT"8MlrrqZTp#Q"H#q"k$k#L<A=
+rr at Su!!,=ar;Zm"s7l<g!'gJ_!9!hVr;QcrqZ$U\qu6Zcr;Zg>rr2uVqu?^!p]('i!!E)t!;u]o
+!!COHnG`K=df9Durqufu"97lb!!#7_rrD!V!;uisr;-Ho+8c'<q#(0m&,uS/_#+04!VcToJGT<H
+"8`#trqcZq"4$r9rr at Su!!.TIr;Zm(s5<VO!'gJ_!9!hVr;QcpqZ$U<qu6Zor;Zg&rr2u6qu?]t
+p\t6/qZ$Tsq>^<l!!1CFnG`K=df9FKq#(0o&-'H>!!#7_rrD!V!;uisr;-Ho+8c'<rqufs"9/<#
+_#+04!VcToi;3KS!Vu`qJG]BI!RC`7rr at T!!!33$nGN=g+9-lc!!#7_rrD!V!;uisr;-Ho+8Z$9
+!!!'!rrBk4!!!&orrDQb!;ZWp_#",Ws7$$gJCFQ#"97<X!!,=ap&G(Wrr2uVrW)lr!;u]o!$D(;
+!.b"Jrr2u6qu?]tp\t6gq>g?k!9!_ScN!D6!.`Q!!XJb at r;ZkJrq6<l5Q:Z_i;N`SrrDuo!!",;
+rrBk5!!@`Ns1nO4!!2io!;uZn!.XkF!:]jc!.W>pnG`K=e,TO\s+(%J!WW)k!!#7_rrD!V!;uis
+r;-Ho+8Z!;i;EWX5QCc!qu?]tp\t6mq>^MFqYpQjqZ$VGci<M7!.`Q!!?_@?!!33$q"=[f5Q:Z_
+i;N`SrrDuo!!",;rrDQe!!>Ics1nO4!!2fnq>^MFqYpQnqZ$VGci<M7!.`Q!!C-S^!!39&nFch^
+5Q:Z_i;N`SrrDuo!!",;rrDim!!=>Cs1nO4!!2fn!.aqH!.XkF!;u]o!.W>pnG`K=e,TN!r;?Ts
+#QN`U!!#7_rrD!V!;uisrqcZq5PkB[r;?Tt+92B!qu?^!pAY-.qZ$U\qYpQjqZ$U\ci<M7!.`Q!
+!WVrn!!3]2_"Ia.5Q:Z_i;N`SrrE&q!!#7[rrE&s!!<c3s5<eT!!Drp!9!_S!'g>[!;QEk!'eg0
+nG`K=eGoXOs7#se![%Gmp&G(Wrr2uVrW)lr!<2iq!'g;Zr;Zp1s8V$T!!!,prrDQc!!#7[rrDik
+!!#70s7$$gJCOW$"97<X!!,=ao`+tVrr2uVrW)lr!<2iq!'g;Z!.b%K!tYG3i;<QT"8;`pq"k$k
+5PkB[q"k$k5L9B!rr at T"!!3E*_#466J,]*@!'gJ_!9!hVqu?Nn!.XhE!5SR6!tYG3nGE7d#PS/t
+q"k$k5PkB[q"k$k5L9B!rr at T"!!3]2JGfHKs8;Ni!'gJ_!9!hVqu?Nn!.XhE!9!hV!tYG3nGE7d
+#PS/tq"k$k5PkB[q"k$k5L9B!rr at T"!!+2Ar;Zm"s7l6e!'gJ_!9!hVqu6YHquHNl!:]sf!tYG3
+q"t*l&,-#'q"k$k5PkB[q"k$k5L9B!rr at T"!!,=`r;Zm$s7#[]!'gJ_!9!hVqu6YHquHNl!;QNn
+!tYG3q"t*l&,-#'q"k$k5PkB[q"k$k5L9B!rr at T"!!.TIr;Zm(s5<PM!'gJ_!9!hVqu6Z3r;Zfu
+q#:?lrW!$2s8W#p!!",7rrDik!!#7[rrDik!!#70s7$$gJCOW#s7lNm!Y>=Ho`+tVrr2uVrW)iq
+!5SO5!!2lp!<2rt!tYG3r;6Np+85^7r;-HoJ,90Fr;-HoJ'\/arr at T#!!33$nGN=g+9-la!!#7_
+rrD!V!;lcri;EWU"8Mlrrr)m"&-)\/qu?^]pAY-jqZ$VGqYpQnqZ$VGci<M7!.`W#!X&J\r;Zj_
+s7?9k5Q:Z_i;N`RrrDQe!!!9!rrE&t!!!Q/s82isJ+imBr;-HoJ,90Fr;-HoJ'\/arr at T#!!3E*
+_#466J,]'?!'gJ_!9!hVqu6Zkr;Zg.q#:?nrVup/rr2tKr;cNj!;u]o!.XkF!;u]o!.W>pnG`K=
+ec5a^s+(%J!WW)i!!#7_rrD!V!;lcrr;?Tq+8Gj9rr)lt&,uS/_#=<6!VQHmrqc]krrE&q!7(W6
+rr at T#!!+2Ar;Zm"s7l3d!'gJ_!9!hVqu6Zqr;Zg^q#:?nrVup/rr2uVrVup#p&>$kqZ-Hl!<2iq
+cN!D6!.`W#!C-S^!!39&nFQ\\5Q:Z_i;N`Qs8;otJ,'$Drr)lt&,uS/nGWCf#PA&o!!!&ps82is
+!RC`7rr at T#!!.TIr;Zm(s5<ML!'gJ_!9!hVqYpPGrW)]m!<2rt!"])/!;QNn!"\`%qu?]tq#C6l
+!!1CFnG`K=ec5aNq#(0o&-'H;!!#7_rrD!V!;c]t^]4?8p\t6mrVup/rr2urrVup?o`"oAr;Zg"
+q#:>Er;Zg"cN!D6!.`Z$!Wi>jr;Zm at s+'_A!'gJ_!9!hV!.XnG"7Q9j#P\5urr)lt&,lP-!!%N@
+rr at TJ!!!,rrr at TJ!!!,Hs7$$gJCac&"97<X!!,=ao)JbTrr2uVrVup_qu6cr!!",8rrE&t!!!Q.
+rrTt9!V?<k_#465#Pe<!_#465#L<A=rr at T$!!3E*_#466J,]$>!'gJ_!9!hV!$D(;rr<%Kp\t6m
+rVup/rVlrg!!i,q!9!eU!"\l)!9!eU!"[BTnG`K=f)Pj_s+(%J!WW)h!!#7_rrD!V!!!Q+rrMTo
+pAY-lrVup/r;QctJ+N[?nGN=e+8Gj9nGN=e+3soUrr at T$!!+2Ar;Zm"s7#U[!'gJ_!9!hV!!hok
+!<2rt!"\Gr!;QKm!'g8Y!;QKm!'ed/nG`K=f)Ph9rqufu"97<N!!#7_rrD!V!!!,grrE&t!!!Pr
+rrDuq!!%NDrrDuq!!%Mos7$$gJCac%J,K6F!XJb at o)JbTrr2uVrVup!mJd1crVup/m/I(br;cTl
+!<2osc2[;5!.`Z$!WVrn!!3]2JF`a at 5Q:Z_i;EZCrrE&t!!!Pqs8Duu!VZQm!!!&Es7$$gJCji'
+!WVZf!!+2Anc/YSrr2ufr;ZhImf*:drVup/li.';!!!,prr[cO!!CLGnG`K=fDksTs5<hU!C-SS
+!!#7_rrDQe!!#7OrrE&t!!!Pqrr_0Z!"\f'"5j.Z&'b.Drr at T%!!3E*_#466J,Jj;!'gJ_!;QKm
+!$CY/!<2rt!"\Dq"8Dir5PG*Zp](:Yc2[;5!.`]%!Y><]r;Zluq!nCb5Q:Z_q#(0m&+9Gtrr)lt
+&*s5srVurkrrW0"!6kK4rr at T%!!+2Ar;Zm"s7#RZ!'gJ_!;ucq!!hrl!<2rt!"\Ap!T4L\rrM$g
+bl at 24!.`]%!C-S^!!39&i:6jJ5Q:Z_rqufs"7H0hrr)lt&#B6prr at T%!!.TIr;Zm(s1n1*!'gG^
+r;Zfumf*:drVup/V#Tra!.`]%!WVrn!!3]2JFW[?5Q1T^JGfK9rrE&t!!!P*s7$$gJCso(!WVZf
+!!+2AnGiPRrVll5r;ZhIn,ECerVup/V#Tra!.``&!X&J\r;Zj_rp]sg5Q1T^i;EWU5O\UPrr)lt
+&#B6prr at T&!!3E*_#466J,Jg:!'gG^!:]pe!$C\0!<2rt!"Yn*nG`K=f`2'as+(%J!WVrb!!#7^
+rrDim!!!PurrE&t!!!P*s7$$gJCso'+929>!Wi>jnGiPRrVllqr;Zg&n,ECerVup/V#Tra!.``&
+!C-S^!!39&i:-dI5Q1T^rqufs"7Q6irr)lt&#B6prr at T&!!.TIr;Zm(s1n.)!'gD]r;Zfun,ECe
+rVup/V#Tra!.``&!WVrn!!3]2JFNU>5Q(N]JGfK:rrE&t!!!P*s7$$gJD'u)!WVZf!!+2An,NGQ
+r;Qc4r;ZhInG`LfrVup/V#Tra!.`c'!X&J\r;Zj_rpTmf5Q(N]i;EWU5Oe[Qrr)lt&#B6prr at T'
+!!3E*_#466J,Jd9!'gD]!:]pe!$C_1!<2rt!"Yn*nG`K=g&M0bs+(%J!WVra!!#7]rrDim!!!Q!
+rrE&t!!!P*s7$$gJD'u(+929>!Wi>jn,NGQr;Qcpr;Zg&nG`LfrVup/V#Tra!.`c'!C-S^!!39&
+i:$^H5Q(N]rqufs"7Z<jrr)lt&#B6prr at T'!!.TIr;Zm(s1n+(!'gA\r;ZfunG`LfrVup/V#Tra
+!.`c'!WVrn!!3]2JFEO=5PtH\JGfK;rrE&t!!!P*s7$$gJD1&*!WVZf!!+2Amf3>Pqu6Z3r;ZhI
+nc&UgrVup/V#Tra!.`f(!X&J\r;Zj_rpKge5PtH\i;EWU5OnaRrr)lt&#B6prr at T(!!3E*_#466
+J,Ja8!'gA\!:]pe!$Cb2!<2rt!"Yn*nG`K=gAh9cs+(%J!WVr`!!#7\rrDim!!!Q"rrE&t!!!P*
+s7$$gJD1&)+929>!Wi>jmf3>Pqu6Zor;Zg&nc&UgrVup/V#Tra!.`f(!C-S^!!39&i9pXG5PtH\
+rqufs"7cBkrr)lt&#B6prr at T(!!.TIr;Zm(s1n('!'g>[r;Zfunc&UgrVup/V#Tra!.`f(!WVrn
+!!3]2JF<I<5PkB[JGfK<rrE&t!!!P*s7$$gJDU>,+8u3>nGN=f+91a/!'g>[!5SO5!.XS>!<2rt
+!"Yn*nG`K=hZ*X at rVllUr;Zj_rpBad5PkB[i;EWU5P"gSrr)lt&#B6prr at T,!!%NIrrBk5!!.TI
+mJm5OqYpQbr;Zg>o)A^hrVup/V#Tra!.`r,r;QbIr;Zluq!J+^5PkB[q#(0m&+]`#rr)lt&#B6p
+rr at T-!!!&ts8;p!!WVZW!!#7[rrDuq!!!8prrE&t!!!P*s7$$gJD^D-!WE#urqufu"97<I!!#7[
+rrE&s!!!,lrrE&t!!!P*s7$$gJD^D.!V#mb!XJb at mJm5Oq>^Bn!!2Zj!<2rt!"Yn*nG`K=huEcY
+nG*%c&-%1J!!#7Zrr at TJ!:p-irr)lt&#B6prr at T-!!*,iq#CF;s6Tdd5Pb<Z_#465J+N[?rr)lt
+&#B6prr at T-!!*,iq#CF[rp9[c5Pb<Zi;EWU5P+mTrr)lt&#B6prr at T-!!*,iq#CGFr9XIa5Pb<Z
+nGN=e+7oL4rr)lt&#B6prr at T-!!*,iq#CHqq!A%]5Pb<Zq#(0m&+ff$rr)lt&#B6prr at T-!!*,i
+q>^Qts7#CU!'g;Z!;ucq!!i,q!<2rt!"Yn*nG`K=huEcYnG3+d"97<H!!#7ZrrE&s!!!,mrrE&t
+!!!P*s7$$gJD^D.!V#pc!XJb at m/R,Nq#C9m!!2]k!<2rt!"Yn*nG`K=huEcYnG3+d&-%1I!!#7Y
+rr at TJ!;$3jrr)lt&#B6prr at T-!!*,iq>^O<s6K^c5PY6Y_#465J+Wa at rp]sg"0qmqrr at T-!!*,i
+q>^O\rp0Ub5PY6Yi;EWU5P4sUrpToms7$$gJD^D.!V#pc!It%6!!#7YrrDQe!!",5rrE&f!!#6h
+s7$$gJD^D.!V#pc!WVr]!!#7YrrDim!!!Q%rrE&f!!"+Hs7$$gJD^D.!V#sd!Wi>jli7#Mq#:?l
+r;Zg&o`"pjn,NG!ZN'Fo!.`u-!<M]e!!39&i9UFD5PY6Yrqufs"8)TnrpTmf#I=C!rr at T-!!*,i
+qZ$[&s1mt$!'g5Xr;Zfuo`"pjn,NFjZN'Fo!.`u-!<M]e!!3]2JF!795PP0XJGfK?rrE&f!!!&*
+s7$$gJD^D.!V#sd!?_ at -!!#7XrrBk5!!%NArrE&e!4)Xorr at T-!!*,iqZ$X]rp'Oa5PP0Xi;EWU
+5P>$VrpKgeJ$]1Err at T-!!*,iqZ$X]r9F=_5PP0XnGN=e+8,X6rpKge5I:CZrr at T-!!*,iqZ$X]
+q!.n[5PP0Xq#(0m&,#r&rpKge+1)":rr at T-!!*,iqZ$X]nEU&S5PP0Xr;?Tq#PJ)srpKge&$u<*
+rr at T-!!*,iqZ$X]i9L at C5PP0Xrqufs"82ZorpKge#IFI"rr at T-!!*,iqZ$X]_!:t#5PG-U!!!&m
+rrE&e!!!,-s7$$gJD^D.!V#sd!C),#!!#7Wrr at TJ!;6?lrpKge!OMgqrr at T-!!*2kqZ$U\l2UfK
+pAY-.r;ZhIpAY-lmJsTnnG`K=huEc[nG<1c53`+J5PG*Wi;EWU5PG*WrpBadJ$f7Frr at T-!!*2k
+qZ$U[l2UfKpAY-^r;Zg>p&FF[!'djjnG`K=huEc[nG<1c53`+J5PG*Wq#(0m&,#tj!!"+Js7$$g
+JD^D."7Z0f!']ZJ!'g2W!;ucq!!i2s!.aM<!"ZI:nG`K=huEc[nG<1c53`+J5PG*Wrqufs"82Zo
+JF<I<#IOO#rr at T-!!*2kqZ$U[l2UfKp&Fsj!!2cm!5S%'!!BV.nG`K=huEc[nG<1c53`+J5P>$V
+JGfK at rrD!G!!!&,s7$$gJD^D."7Z0f!']ZJ!'g/V!5SO5!.X_B!:]CV[/]Xq!.`u-!<_ig!!#4J
+!!#7VrrD!U!!#7WrrDi^!!%MVs7$$gJD^D."7Z0f!']ZJ!'g/V!:]pe!$Cq7!;u6b!'dmknG`K=
+huEc[nG<1c53`+J5P>$Vq#(0m&,#tj!!"+Ks7$$gJD^D."7Z0f!']ZJ!'g/V!;ucq!!i2s!5S%'
+!"ZL;nG`K=huEc[nG<1c53`+J5P>$Vrqufs"6][ar;?Tq#IXU$rr at T-!!*2kqZ$U[l2UfKo`+ji
+!!29_!<2os!!BY/nG`K=huEc[nG<1c53`+J5P4sUJGfK1s8;ot!O_ssrr at T-!!*2kqZ$U[l2UfK
+o`"p,r;ZhIkPkO4r;a8*nG`K=huEc[nG<1c53`+J5P4sUi;EWU5Ni%H_#465J%#CHrr at T-!!*2k
+qZ$U[l2UfKo`"p\r;Zg>kPkP?r;Zg^[f>js!.`u-!<_ig!!#4J!!#7UrrDim!!!PmrrDQe!!"+L
+s7$$gJD^D."7Z0f!']ZJ!'g,U!;ucq!!h]e!;QKm!"ZO<nG`K=huEc[nG<1c53`+J5P4sUrqufs
+"6][ar;?Tq#Ia[%rr at T-!!*2kqZ$U[l2UfKoDeah!!29_!<2os!!B\0nG`K=huEc[nG<1c53`+J
+5P+mTJGfK1s8;ot!Oi$trr at T-!!*2kqZ$U[l2UfKoD\g+r;ZhIkPkO4r;a;+nG`K=huEc[nG<1c
+53`+J5P+mTi;EWU5Ni%H_#465J%,IIrr at T-!!*,iqZ$U[l2UfKoD\g[r;Zg>kPkP?r;Zg^\,Yst
+!.`u-!<M]e!!#4J!!#7TrrDim!!!PmrrDQe!!"+Ms7$$gJD^D-!W)frrosI`5P+mTr;?Tq#Nu*e
+q#(0m&%DT.rr at T-!!!&rrrE&`!!#7TrrE&s!!!,arrDuq!!!85s7$$gJDUA'rrDu^!!#7Ss8;ot
+!U'I_rqufs"1S="rr at T,!!%NGrrDiZ!!#7Srr at TJ!9X=[!!!&/s7$$gJDU>,5PtH\nEKuR5P"gS
+_#465J*6h3JGfJWs7$$gJDU>,"8i,_!!#7SrrD!U!!#7HrrBk5!!%MYs7$$gJA;-b5P"gSnGN=e
++6WY(i;EWU5Iga_rr at Sb!!#7SrrDim!!!PmrrDQe!!"+Ns7$$gJA;-b5P"gSr;?Tq#Nu*eq#(0m
+&%MZ/rr at Sb!!#7SrrE&s!!!,arrDuq!!!86s7$$gJA;-b5OndP!!!&_rrE&s!!!,2s7$$gJA;-b
+5OnaRJGfK1s8;ot!P&1!rr at Sb!!#7RrrBk5!!%N3rr at TJ!4`'urr at Sb!!#7RrrD!U!!#7HrrBk5
+!!%MZs7$$gJA;-b5OnaRnGN=e+6WY(i;EWU5Ipg`rr at Sb!!#7RrrDim!!!PmrrDQe!!"+Os7$$g
+JA;-b5OnaRr;?Tq#Nu*eq#(0m&%V`0rr at Sb!!#7RrrE&s!!!,arrDuq!!!87s7$$gJA;-b5Oe^O
+!!!&_rrE&s!!!,3s7$$gJA;-b5Oe[QJGfK1s8;ot!P/7"rr at Sb!!#7QrrBk5!!%N3rr at TJ!4i.!
+rr at Sb!!#7QrrD!U!!#7HrrBk5!!%M[s7$$gJA;-b5Oe[QnGN=e+6WY(i;EWU5J$marr at Sb!!#7Q
+rrDim!!!PmrrDQe!!"+Ps7$$gJA;-b5Oe[Qr;?Tq#Nu*eq#(0m&%_f1rr at Sb!!#7QrrE&s!!!,a
+rrDuq!!!88s7$$gJA;-b5O\XN!!!&_rrE&s!!!,4s7$$gJA;-b5O\UPJGfK1s8;ot!P8=#rr at Sb
+!!#7PrrBk5!!%N3rr at TJ!4r4"rr at Sb!!#7PrrD!U!!#7HrrBk5!!%M\s7$$gJA;-b5O\UPnGN=e
++6WY(i;EWU5J-sbrr at Sb!!#7PrrDim!!!PmrrDQe!!"+Qs7$$gJA;-b5O\UPr;?Tq#Nu*eq#(0m
+&%hl2rr at Sb!!#7PrrE&s!!!,arrDuq!!!89s7$$gJA;-b5OSRM!!!&_rrE&s!!!,5s7$$gJA;-b
+5OSOOJGfK1s8;ot!PAC$rr at Sb!!#7OrrBk5!!%N3rr at TJ!5&:#rr at Sb!!#7OrrD!U!!#7HrrBk5
+!!%M]s7$$gJA;-b5OSOOnGN=e+6WY(i;EWU5J7$crr at Sb!!#7OrrDim!!!PmrrDQe!!"+Rs7$$g
+JA;-b5OSOOr;?Tq#Nu*eq#(0m&%qr3rr at Sb!!#7OrrE&s!!!,arrDuq!!!8:s7$$gJA;-b5OJLL
+!!!&_rrE&s!!!,6s7$$gJA;-b5OJINJGfK1s8;ot!PJI%rr at Sb!!#7NrrBk5!!%N3rr at TJ!5/@$
+rr at Sb!!#7NrrD!U!!#7HrrBk5!!%M^s7$$gJA;-b5OJINnGN=e+6WY(i;EWU5J@*drr at Sb!!#7N
+rrDim!!!PmrrDQe!!"+Ss7$$gJA;-b5OJINr;?Tq#Nu*eq#(0m&&&#4rr at Sb!!#7NrrE&s!!!,a
+rrDuq!!!8;s7$$gJA;-b5OAFK!!!&_rrE&s!!!,7s7$$gJA;-b5OACMJGfK1s8;ot!PSO&rr at Sb
+!!#7MrrBk5!!%N3rr at TJ!58F%rr at Sb!!#7MrrD!U!!#7HrrBk5!!%M_s7$$gJA;-b5OACMnFln_
+#OVNki;EWU5JI0err at Sb!!#7MrrDig!!!8krrDQe!!*o=^]3g'!._ib!'fiM!;uQk!!hok!;QEk
+!'e:!nG`K=^]4@!m/I(bpAb0umJd1aqZ$U,_#Np(!._ib!'ffLpAb0umJd1cqZ$Tu_#Np(!._ib
+!'ffL!.ahE!!hljq>e>2nG`K=^]4@!li-t#p](:!m/I'9qZ$VG_>j$)!._ib!'ffL!9!VP!!hlj
+!5SI3!'e="nG`K=^]4@!li-tSp](:!m/I(DqZ$U<_>j$)!._ib!'ffL!;Q<h!!hlj!:]jc!"ZpG
+nG`K=^]4@!li-t_p](:!m/I(\qZ$U$_>j$)!._ib!'ffL!<2`n!!hlj!;u]o!!C(;nG`K=^]4@!
+lMpVZ!!hlj!<2iq!!0q9nG`K=^]4@!lMgj7q#CC"li6e]_>j$)!._ib!'fcK!5SC1!!hii!.aqH
+_>j$)!._ib!'fcK!9!YQ!!hii!5SI3!.VlcnG`K=^]4@!lMgkRq#CC"li-tCqZ$VG_Z0-*!._ib
+!'fcK!;Q?i!!hii!:]jc!'e@#nG`K=^]4@!lMgk^q#CC"li-t[qZ$U\_Z0-*!._ib!'fcK!<2co
+!!hii!;u]o!$B)XnG`K=^]4@!l2UPZ!!hii!<2iq!$B)XnG`K=^]4@!l2La6q>^L#lMp_]!$B)X
+nG`K=^]4@!l2Lb!q>^L#lMp_]!$B)XnG`K=^]4@!l2LbAq>^L#li-taqZ$U,_Z0-*!._ib!'f`J
+!:]gb!!hii!<2iq!"ZsHnG`K=^]4@!l2LbYq>^L#li-taqZ$U,_Z0-*!._ib!'f`J!;uZn!!hii
+!<2iq!"ZsHnG`K=^]4@!l2Lb_q>^L#li-taqZ$U,_Z0-*!._ib!'f]Iq>^L#li-taqZ$U,_Z0-*
+!._ib!'f]I!.aqH!!hii!<2iq!"ZsHnG`K=^]4@!kl1XuqZ$U$li-taqZ$U,_Z0-*!._ib!'f]I
+!9!_S!!hii!<2iq!"ZsHnG`K=^]4@!kl1YPqZ$U$lMp_]!$B)XnG`K=^]4@!kl1YXqZ$U$lMp_]
+!$B)XnG`K=^]4@!kl1Y\qZ$U$lMp_]!$B)XnG`K=^]4@!kl1Y^qZ$U$lMp_]!$B)XnG`K=^]4@!
+kl1Y^qZ$U$lMgj7qu?^]_Z0-*!._ib!'f]I!<2iq!!hfh!.atI!'e@#nG`K=^]4@!kl1Y^qZ$U$
+lMgk"qu?_H_Z0-*!._ib!'f]I!<2iq!!hfh!5SL4!.VlcnG`K=^]4@!kl1Y^qZ$U$lMgkBquFS5
+nG`K=^]4@!kl1Y^qZ$U$lMgkBquFS5nG`K=^]4@!kl1Y^qZ$U$lMgkRr;Zfu_>j$)!._ib!'f]I
+!<2iq!!hfh!;QKm!!C(;nG`K=^]4@!kl1Y^qZ$U$lMgk^r;Zg&_>j$)!._ib!'f]I!<2iq!!hfh
+!<2os!"ZpGnG`K=^]4@!kl1Y^qZ$U$l2U\^!$B&WnG`K=^]4@!kl1Y^qZ$U$l2La6rVup__>j$)
+!._ib!'f]I!<2iq!!hcg!5SR6!.VibnG`K=^]4@!kl1Y^qZ$U$l2LbArW'b6nG`K=^]4@!kl1Y^
+qZ$U$l2Lk\!!!,:s7$$gJA;-b5Nr+IrqcZq#O26jrVup/_#Np(!._ib!'f]I!<2iq!!h`f!e:88
+_#Np(!._ib!'f]I!<2iq!!h`f!Vcc3s7$$gJA;-b5D/ues0)HRs0)HRs0)HRs0)HRs0)HRs0)HR
+s0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HR
+s0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HR
+s0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HR
+s0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HR
+s0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HRs0)HQ~>
+%%EndData
+showpage
+%%Trailer
+end
+%%EOF
diff --git a/doc-orig/gcode_control_img.pdf b/doc-orig/gcode_control_img.pdf
new file mode 100644
index 0000000..3a1476b
Binary files /dev/null and b/doc-orig/gcode_control_img.pdf differ
diff --git a/doc-orig/gcode_control_img.png b/doc-orig/gcode_control_img.png
new file mode 100644
index 0000000..d4bb3ee
Binary files /dev/null and b/doc-orig/gcode_control_img.png differ
diff --git a/doc-orig/gcode_tool_path.eps b/doc-orig/gcode_tool_path.eps
new file mode 100644
index 0000000..a8e14cd
--- /dev/null
+++ b/doc-orig/gcode_tool_path.eps
@@ -0,0 +1,415 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: cairo 1.8.10 (http://cairographics.org)
+%%CreationDate: Mon May 30 02:27:10 2011
+%%Pages: 1
+%%BoundingBox: 0 0 403 230
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%EndComments
+%%BeginProlog
+/cairo_eps_state save def
+/dict_count countdictstack def
+/op_count count 1 sub def
+userdict begin
+/q { gsave } bind def
+/Q { grestore } bind def
+/cm { 6 array astore concat } bind def
+/w { setlinewidth } bind def
+/J { setlinecap } bind def
+/j { setlinejoin } bind def
+/M { setmiterlimit } bind def
+/d { setdash } bind def
+/m { moveto } bind def
+/l { lineto } bind def
+/c { curveto } bind def
+/h { closepath } bind def
+/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
+      0 exch rlineto 0 rlineto closepath } bind def
+/S { stroke } bind def
+/f { fill } bind def
+/f* { eofill } bind def
+/B { fill stroke } bind def
+/B* { eofill stroke } bind def
+/n { newpath } bind def
+/W { clip } bind def
+/W* { eoclip } bind def
+/BT { } bind def
+/ET { } bind def
+/pdfmark where { pop globaldict /?pdfmark /exec load put }
+    { globaldict begin /?pdfmark /pop load def /pdfmark
+    /cleartomark load def end } ifelse
+/BDC { mark 3 1 roll /BDC pdfmark } bind def
+/EMC { mark /EMC pdfmark } bind def
+/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
+/Tj { show currentpoint cairo_store_point } bind def
+/TJ {
+  {
+    dup
+    type /stringtype eq
+    { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
+  } forall
+  currentpoint cairo_store_point
+} bind def
+/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
+    cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
+/Tf { pop /cairo_font exch def /cairo_font_matrix where
+      { pop cairo_selectfont } if } bind def
+/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
+      /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
+      /cairo_font where { pop cairo_selectfont } if } bind def
+/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
+      cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
+/g { setgray } bind def
+/rg { setrgbcolor } bind def
+/d1 { setcachedevice } bind def
+%%EndProlog
+%%Page: 1 1
+%%BeginPageSetup
+%%PageBoundingBox: 0 0 403 230
+%%EndPageSetup
+q
+1 g
+0 0 403 230 rectfill
+1 0 0 rg
+0.5 w
+0 J
+0 j
+[] 0.0 d
+10 M q 1 0 0 1 0 230 cm
+0 -230.398 m 32.879 -14.398 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+32.879 -14.398 m 32.879 -211.922 l 159.84 -211.922 l 159.84 -70.559 l 
+150.238 -70.559 l 148.32 -71.762 l 148.32 -72.238 l 150.238 -73.441 l 
+152.16 -76.32 l 152.641 -79.441 l 152.16 -81.84 l 150.961 -84 l 150.238 
+-84.48 l 150.238 -142.801 l 149.762 -144.238 l 149.039 -145.922 l 
+114.961 -179.281 l 114.961 -186.238 l 114 -186.961 l 100.559 -186.961 l 
+99.602 -186 l 99.602 -172.559 l 100.559 -171.359 l 107.52 -171.359 l 
+138.961 -139.441 l 138.961 -84.48 l 138 -83.281 l 137.281 -81.84 l 
+136.801 -79.199 l 137.281 -76.559 l 138.238 -74.641 l 139.922 -72.961 l 
+141.121 -72 l 139.922 -71.039 l 138.238 -69.359 l 137.281 -67.441 l 
+136.801 -64.801 l 137.281 -62.16 l 138.238 -60.238 l 140.398 -58.078 l 
+137.762 -58.078 l 137.52 -57.602 l 121.922 -57.602 l 112.801 -66.238 l 
+112.801 -130.801 l 113.52 -131.281 l 114.719 -133.441 l 115.199 
+-136.078 l 114.719 -138.961 l 112.801 -141.84 l 110.641 -143.281 l 
+109.441 -143.762 l 106.801 -144 l 104.16 -143.52 l 101.52 -141.602 l 
+100.078 -139.441 l 99.602 -137.52 l 99.602 -134.641 l 100.078 -132.719 
+l 101.52 -130.801 l 101.52 -63.84 l 102.48 -60.719 l 116.16 -47.281 l 
+118.078 -46.32 l 137.039 -46.078 l 137.039 -43.441 l 138.238 -42.48 l 
+151.441 -42.48 l 152.398 -43.68 l 152.398 -57.359 l 151.441 -58.078 l 
+149.281 -58.078 l 149.281 -58.559 l 149.762 -59.039 l 157.68 -59.039 l 
+158.398 -58.32 l 158.398 -38.641 l 159.359 -35.52 l 159.84 -35.281 l 
+159.84 -14.398 l 32.879 -14.398 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+32.879 -14.398 m 46.078 -50.16 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+46.078 -50.16 m 43.441 -50.879 l 41.281 -51.84 l 38.16 -54 l 36 -56.641 
+l 34.32 -60 l 33.602 -64.801 l 34.32 -69.602 l 36 -72.961 l 38.16 
+-75.602 l 41.281 -77.762 l 43.441 -78.719 l 46.32 -79.441 l 50.16 
+-79.441 l 53.039 -78.719 l 55.68 -77.52 l 58.559 -75.359 l 60.238 
+-73.199 l 62.16 -69.602 l 62.879 -65.281 l 62.641 -62.641 l 62.16 -60 l 
+60.238 -56.398 l 58.559 -54.238 l 55.68 -52.078 l 53.039 -50.879 l 
+50.16 -50.16 l 46.078 -50.16 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+46.078 -50.16 m 217.922 -16.801 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+217.922 -16.801 m 217.199 -17.281 l 208.801 -25.441 l 208.078 -26.879 l 
+208.078 -49.199 l 205.441 -52.078 l 197.281 -52.078 l 195.84 -50.398 l 
+193.922 -49.441 l 190.32 -49.441 l 188.398 -50.398 l 186.719 -52.32 l 
+186 -53.762 l 186 -57.359 l 186.719 -58.801 l 188.398 -60.719 l 190.32 
+-61.68 l 193.922 -61.68 l 195.84 -60.719 l 197.281 -59.039 l 207.121 
+-59.039 l 208.559 -58.32 l 214.559 -52.078 l 215.039 -51.121 l 215.039 
+-28.801 l 219.84 -23.762 l 263.039 -23.762 l 277.922 -38.879 l 277.922 
+-46.801 l 290.879 -46.801 l 290.879 -33.84 l 282.238 -33.84 l 266.16 
+-17.52 l 264.719 -16.801 l 217.922 -16.801 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+217.922 -16.801 m 332.879 -34.078 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+332.879 -34.078 m 331.68 -34.559 l 330.238 -35.281 l 328.559 -37.199 l 
+327.84 -38.641 l 327.84 -42 l 328.559 -43.922 l 302.641 -70.078 l 
+264.238 -70.078 l 263.281 -70.559 l 259.199 -74.398 l 258.48 -75.84 l 
+258.48 -85.441 l 249.121 -94.559 l 239.762 -94.559 l 239.039 -93.602 l 
+237.121 -92.641 l 233.52 -92.641 l 231.602 -93.602 l 229.922 -95.52 l 
+229.199 -96.961 l 229.199 -100.559 l 229.922 -102 l 231.602 -103.922 l 
+233.52 -104.879 l 237.121 -104.879 l 239.039 -103.922 l 241.199 -101.52 
+l 251.039 -101.52 l 252 -101.039 l 264.719 -88.559 l 265.441 -87.121 l 
+265.441 -77.52 l 265.922 -77.039 l 304.32 -77.039 l 305.762 -76.32 l 
+335.039 -46.801 l 337.199 -45.84 l 339.121 -44.16 l 340.078 -42.238 l 
+340.078 -38.641 l 339.359 -37.199 l 337.68 -35.281 l 335.762 -34.32 l 
+332.879 -34.078 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+332.879 -34.078 m 228.961 -34.559 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+228.961 -34.559 m 228.961 -47.52 l 241.922 -47.52 l 241.922 -34.559 l 
+228.961 -34.559 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+228.961 -34.559 m 191.039 -34.801 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -34.801 m 189.359 -35.52 l 163.199 -35.52 l 161.762 -36.719 l 
+160.559 -38.16 l 160.559 -59.52 l 158.398 -61.441 l 149.762 -61.441 l 
+148.32 -59.762 l 146.398 -58.801 l 142.801 -58.801 l 141.359 -59.52 l 
+139.441 -61.199 l 138.48 -63.121 l 138.48 -66.719 l 139.441 -68.641 l 
+141.359 -70.32 l 142.801 -71.039 l 146.398 -71.039 l 148.32 -70.078 l 
+149.762 -68.398 l 160.32 -68.398 l 161.281 -67.922 l 166.801 -62.641 l 
+167.52 -61.199 l 167.52 -42.48 l 186 -42.48 l 186.48 -44.16 l 188.398 
+-46.32 l 190.32 -47.281 l 193.922 -47.281 l 195.84 -46.32 l 197.52 
+-44.398 l 198.238 -42.961 l 198.238 -39.359 l 197.52 -37.922 l 195.84 
+-36 l 193.922 -35.039 l 191.039 -34.801 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -34.801 m 138.238 -43.922 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+138.238 -43.922 m 138.238 -48.48 l 118.801 -48.48 l 117.84 -48.961 l 
+104.398 -62.16 l 103.68 -63.602 l 103.68 -131.039 l 102 -132.48 l 
+101.039 -134.398 l 101.039 -138 l 101.762 -139.441 l 103.441 -141.359 l 
+105.359 -142.32 l 108.961 -142.32 l 110.879 -141.359 l 112.559 -139.441 
+l 113.281 -138 l 113.281 -134.398 l 112.32 -132.48 l 110.641 -131.039 l 
+110.641 -65.52 l 120.48 -55.441 l 138.238 -55.441 l 138.238 -56.879 l 
+151.199 -56.879 l 151.199 -43.922 l 138.238 -43.922 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+138.238 -43.922 m 234.238 -49.199 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -49.199 m 233.039 -49.68 l 231.602 -50.398 l 229.922 -52.32 l 
+229.199 -53.762 l 229.199 -57.359 l 230.16 -59.281 l 232.078 -60.961 l 
+233.52 -61.68 l 237.121 -61.68 l 239.039 -60.719 l 240.719 -58.801 l 
+241.441 -57.359 l 241.441 -53.762 l 240.719 -52.32 l 239.039 -50.398 l 
+237.121 -49.441 l 234.238 -49.199 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -49.199 m 191.039 -63.602 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -63.602 m 189.84 -64.078 l 188.398 -64.801 l 186.719 -66.719 l 
+186 -68.16 l 186 -71.762 l 186.961 -73.68 l 188.879 -75.359 l 190.32 
+-76.078 l 193.922 -76.078 l 195.84 -75.121 l 197.52 -73.199 l 198.238 
+-71.762 l 198.238 -68.16 l 197.52 -66.719 l 195.84 -64.801 l 193.922 
+-63.84 l 191.039 -63.602 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -63.602 m 234.238 -63.602 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -63.602 m 233.039 -64.078 l 231.602 -64.801 l 230.16 -66.48 l 
+216 -66.48 l 215.039 -66.961 l 208.801 -72.961 l 208.078 -74.398 l 
+208.078 -90.961 l 204.719 -94.559 l 196.559 -94.559 l 195.84 -93.602 l 
+193.922 -92.641 l 190.32 -92.641 l 188.879 -93.359 l 186.961 -95.039 l 
+186 -96.961 l 186 -100.559 l 186.961 -102.48 l 188.879 -104.16 l 190.32 
+-104.879 l 193.922 -104.879 l 195.84 -103.922 l 198 -101.52 l 206.398 
+-101.52 l 207.84 -100.801 l 214.559 -93.84 l 215.039 -92.879 l 215.039 
+-76.078 l 217.922 -73.441 l 230.16 -73.441 l 231.602 -75.121 l 233.52 
+-76.078 l 237.121 -76.078 l 239.039 -75.121 l 240.719 -73.199 l 241.441 
+-71.762 l 241.441 -68.16 l 240.719 -66.719 l 239.039 -64.801 l 237.121 
+-63.84 l 234.238 -63.602 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -63.602 m 143.52 -72.961 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+143.52 -72.961 m 142.32 -73.441 l 140.879 -74.16 l 139.199 -76.078 l 
+138.48 -77.52 l 138.48 -81.121 l 139.441 -83.039 l 141.121 -84.48 l 
+141.121 -140.879 l 108.961 -172.801 l 100.801 -172.801 l 100.801 
+-185.762 l 113.762 -185.762 l 113.762 -177.359 l 147.359 -144 l 148.078 
+-142.559 l 148.078 -84.48 l 149.762 -83.039 l 150.719 -81.121 l 150.719 
+-77.52 l 150 -76.078 l 148.32 -74.16 l 146.398 -73.199 l 143.52 -72.961 
+l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+143.52 -72.961 m 191.039 -78 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -78 m 189.84 -78.48 l 188.398 -79.199 l 186.48 -81.359 l 186 
+-83.039 l 174.961 -83.039 l 174 -83.52 l 168.48 -88.801 l 167.762 
+-90.238 l 167.762 -163.441 l 168.48 -164.879 l 210.961 -207.121 l 
+210.961 -211.68 l 223.922 -211.68 l 223.922 -198.719 l 211.68 -198.719 
+l 174.719 -161.52 l 174.719 -92.16 l 176.641 -90 l 189.359 -90 l 
+191.281 -90.719 l 193.922 -90.48 l 195.84 -89.52 l 197.52 -87.602 l 
+198.238 -86.16 l 198.238 -82.559 l 197.52 -81.121 l 195.84 -79.199 l 
+193.922 -78.238 l 191.039 -78 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -78 m 234.238 -78 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -78 m 233.039 -78.48 l 231.602 -79.199 l 229.922 -81.121 l 
+229.199 -82.559 l 229.199 -86.16 l 230.16 -88.078 l 232.078 -89.762 l 
+233.52 -90.48 l 237.121 -90.48 l 239.039 -89.52 l 240.719 -87.602 l 
+241.441 -86.16 l 241.441 -82.559 l 240.719 -81.121 l 239.039 -79.199 l 
+237.121 -78.238 l 234.238 -78 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -78 m 283.199 -91.68 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+283.199 -91.68 m 282 -92.16 l 280.559 -92.879 l 278.879 -94.801 l 
+278.16 -96.238 l 278.16 -99.84 l 279.121 -101.762 l 280.801 -103.199 l 
+280.801 -144 l 281.281 -144.961 l 293.039 -156.961 l 293.281 -160.32 l 
+294.238 -162.238 l 296.16 -163.922 l 297.602 -164.641 l 301.199 
+-164.641 l 303.121 -163.68 l 304.801 -161.762 l 305.52 -160.32 l 305.52 
+-156.719 l 304.801 -155.281 l 303.121 -153.359 l 301.199 -152.398 l 
+297.84 -152.16 l 287.762 -142.32 l 287.762 -103.199 l 289.68 -101.52 l 
+305.52 -101.52 l 308.879 -105.121 l 308.879 -118.801 l 307.199 -120.238 
+l 306.238 -122.16 l 306.238 -125.762 l 306.961 -127.199 l 308.641 
+-129.121 l 310.559 -130.078 l 314.16 -130.078 l 316.078 -129.121 l 
+317.762 -127.199 l 318.48 -125.762 l 318.48 -122.16 l 317.52 -120.238 l 
+315.84 -118.801 l 315.84 -103.199 l 315.121 -101.762 l 308.16 -95.039 l 
+307.199 -94.559 l 289.441 -94.559 l 288 -92.879 l 286.078 -91.922 l 
+283.199 -91.68 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+283.199 -91.68 m 191.039 -106.801 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -106.801 m 189.84 -107.281 l 188.398 -108 l 186.719 -109.922 l 
+186 -111.359 l 186 -114.961 l 186.961 -116.879 l 188.879 -118.559 l 
+190.32 -119.281 l 193.922 -119.281 l 195.84 -118.32 l 197.281 -116.641 
+l 212.641 -116.641 l 226.078 -130.32 l 227.52 -131.039 l 230.16 
+-131.039 l 231.602 -132.719 l 233.52 -133.68 l 237.121 -133.68 l 
+238.559 -132.961 l 240.48 -131.281 l 241.441 -129.359 l 241.441 
+-125.762 l 240.48 -123.84 l 238.559 -122.16 l 237.121 -121.441 l 233.52 
+-121.441 l 231.602 -122.398 l 230.16 -124.078 l 229.199 -124.078 l 
+215.039 -109.68 l 213.602 -108.961 l 212.16 -108.961 l 211.199 -109.68 
+l 197.281 -109.68 l 195.84 -108 l 193.922 -107.039 l 191.039 -106.801 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -106.801 m 234.238 -106.801 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -106.801 m 233.039 -107.281 l 231.602 -108 l 229.922 -109.922 l 
+229.199 -111.359 l 229.199 -114.961 l 230.16 -116.879 l 232.078 
+-118.559 l 233.52 -119.281 l 237.121 -119.281 l 239.039 -118.32 l 
+239.762 -117.359 l 253.68 -117.359 l 254.16 -117.84 l 254.16 -147.602 l 
+254.641 -148.559 l 264.238 -158.398 l 264.48 -160.32 l 265.199 -161.762 
+l 266.879 -163.68 l 268.801 -164.641 l 272.398 -164.641 l 274.32 
+-163.68 l 276 -161.762 l 276.719 -160.32 l 276.719 -156.719 l 276 
+-155.281 l 274.32 -153.359 l 272.398 -152.398 l 270.238 -152.16 l 
+268.078 -152.641 l 261.121 -145.922 l 261.121 -116.16 l 260.398 
+-114.719 l 256.32 -110.879 l 255.359 -110.398 l 241.199 -110.398 l 
+239.039 -108 l 237.121 -107.039 l 234.238 -106.801 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -106.801 m 191.039 -121.199 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -121.199 m 189.84 -121.68 l 188.398 -122.398 l 186.719 -124.32 
+l 186 -125.762 l 186 -129.359 l 186.961 -131.281 l 188.879 -132.961 l 
+190.32 -133.68 l 193.922 -133.68 l 195.84 -132.719 l 197.281 -131.039 l 
+204 -131.039 l 215.281 -142.559 l 216.719 -143.281 l 229.199 -143.281 l 
+229.68 -144.961 l 231.602 -147.121 l 233.52 -148.078 l 237.121 -148.078 
+l 238.559 -147.359 l 240.48 -145.68 l 241.441 -143.762 l 241.441 
+-140.16 l 240.48 -138.238 l 238.559 -136.559 l 237.121 -135.84 l 234.48 
+-135.602 l 232.559 -136.32 l 218.398 -136.32 l 207.121 -124.801 l 
+205.68 -124.078 l 197.281 -124.078 l 195.84 -122.398 l 193.922 -121.441 
+l 191.039 -121.199 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -121.199 m 191.039 -135.602 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -135.602 m 189.84 -136.078 l 188.398 -136.801 l 186.719 
+-138.719 l 186 -140.16 l 186 -143.762 l 186.961 -145.68 l 188.879 
+-147.359 l 190.32 -148.078 l 193.922 -148.078 l 195.84 -147.121 l 
+197.281 -145.441 l 202.32 -145.441 l 216.719 -159.602 l 216.719 -179.52 
+l 217.922 -180.961 l 219.359 -182.16 l 247.199 -182.16 l 268.559 
+-203.762 l 268.801 -207.121 l 269.52 -208.559 l 271.199 -210.48 l 
+273.121 -211.441 l 276.719 -211.441 l 278.641 -210.48 l 280.32 -208.559 
+l 281.039 -207.121 l 281.039 -203.52 l 280.32 -202.078 l 278.641 
+-200.16 l 276.719 -199.199 l 273.359 -198.961 l 249.84 -175.68 l 
+248.879 -175.199 l 223.68 -175.199 l 223.68 -157.922 l 223.199 -156.961 
+l 205.68 -139.199 l 204.238 -138.48 l 197.281 -138.48 l 195.84 -136.801 
+l 193.922 -135.84 l 191.039 -135.602 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -135.602 m 191.039 -150 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -150 m 189.84 -150.48 l 188.398 -151.199 l 186.719 -153.121 l 
+186 -154.559 l 186 -158.16 l 186.961 -160.078 l 188.879 -161.762 l 
+190.32 -162.48 l 193.922 -162.48 l 195.84 -161.52 l 197.52 -159.602 l 
+198.238 -158.16 l 198.238 -154.559 l 197.52 -153.121 l 195.84 -151.199 
+l 193.922 -150.238 l 191.039 -150 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+191.039 -150 m 234.238 -150 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -150 m 233.039 -150.48 l 231.602 -151.199 l 229.922 -153.121 l 
+229.199 -154.559 l 229.199 -158.16 l 230.16 -160.078 l 232.078 -161.762 
+l 233.52 -162.48 l 237.121 -162.48 l 239.039 -161.52 l 240.719 -159.602 
+l 241.441 -158.16 l 241.441 -154.559 l 240.719 -153.121 l 239.039 
+-151.199 l 237.121 -150.238 l 234.238 -150 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+234.238 -150 m 46.559 -51.84 l S Q
+0 g
+0.5 w
+q 1 0 0 1 0 230 cm
+46.559 -51.84 m 44.641 -52.32 l 42.238 -53.281 l 39.602 -54.961 l 
+37.441 -57.359 l 36 -60 l 35.281 -62.398 l 35.281 -67.441 l 36 -69.84 l 
+37.441 -72.48 l 39.602 -74.879 l 42.238 -76.559 l 45.359 -77.762 l 
+50.879 -77.762 l 54 -76.559 l 56.641 -74.879 l 58.801 -72.48 l 60.238 
+-69.84 l 60.961 -67.441 l 60.961 -62.398 l 60.238 -60 l 58.801 -57.359 
+l 56.641 -54.961 l 54 -53.281 l 50.879 -52.078 l 46.559 -51.84 l S Q
+1 0 0 rg
+0.5 w
+q 1 0 0 1 0 230 cm
+46.559 -51.84 m S Q
+Q
+showpage
+%%Trailer
+count op_count sub {pop} repeat
+countdictstack dict_count sub {end} repeat
+cairo_eps_state restore
+%%EOF
diff --git a/doc-orig/gcode_tool_path.pdf b/doc-orig/gcode_tool_path.pdf
new file mode 100644
index 0000000..7f6dd2e
Binary files /dev/null and b/doc-orig/gcode_tool_path.pdf differ
diff --git a/doc-orig/gcode_tool_path.png b/doc-orig/gcode_tool_path.png
new file mode 100644
index 0000000..f694f95
Binary files /dev/null and b/doc-orig/gcode_tool_path.png differ
diff --git a/doc-orig/new_gen/Makefile b/doc-orig/new_gen/Makefile
new file mode 100644
index 0000000..d0b8eed
--- /dev/null
+++ b/doc-orig/new_gen/Makefile
@@ -0,0 +1,14 @@
+all: fractional_size.html letter_size.html metric_size.html wire_size.html
+
+fractional_size.html: fractional_size.tab tab2html.sh
+	./tab2html.sh 4 < fractional_size.tab >$@
+
+letter_size.html: letter_size.tab tab2html.sh
+	./tab2html.sh 4 < letter_size.tab >$@
+
+metric_size.html: metric_size.tab tab2html.sh
+	./tab2html.sh 4 < metric_size.tab >$@
+
+wire_size.html: wire_size.tab tab2html.sh
+	./tab2html.sh 4 < wire_size.tab >$@
+
diff --git a/doc-orig/new_gen/README b/doc-orig/new_gen/README
new file mode 100644
index 0000000..0beee07
--- /dev/null
+++ b/doc-orig/new_gen/README
@@ -0,0 +1,2 @@
+New scripts to generate html using old doc input files. This directory
+will be merged in the main doc eventually.
diff --git a/doc-orig/new_gen/fractional_size.tab b/doc-orig/new_gen/fractional_size.tab
new file mode 100644
index 0000000..0e96768
--- /dev/null
+++ b/doc-orig/new_gen/fractional_size.tab
@@ -0,0 +1,65 @@
+ at Drill<br>Size	Diameter<br>(inches)
+1/64		.0156
+1/32		.0313
+3/64		.0469
+1/16		.0625
+5/64		.0781
+3/32		.0938
+7/64		.1094
+1/8		.1250
+9/64		.1406
+5/32		.1562
+11/64		.1719
+3/16		.1875
+13/64		.2031
+7/32		.2188
+15/64		.2344
+1/4		.2500
+17/64		.2656
+9/32		.2812
+19/64		.2969
+5/16		.3125
+21/64		.3281
+11/32		.3438
+23/64		.3594
+3/8		.3750
+25/64		.3906
+13/32		.4062
+27/64		.4219
+7/16		.4375
+29/64		.4531
+15/32		.4688
+31/64		.4844
+1/2		.5000
+33/64		.5156
+17/32		.5313
+35/64		.5469
+9/16		.5625
+37/64		.5781
+19/32		.5938
+39/64		.6094
+5/8		.6250
+41/64		.6406
+21/32		.6562
+43/64		.6719
+11/16		.6875
+45/64		.7031
+23/32		.7188
+47/64		.7344
+3/4		.7500
+49/64		.7656
+25/32		.7812
+51/64		.7969
+13/16		.8125
+53/64		.8281
+27/32		.8438
+55/64		.8594
+7/8		.8750
+57/64		.8906
+29/32		.9062
+59/64		.9219
+15/16		.9375
+61/64		.9531
+31/32		.9688
+63/64		.9844
+1			1.0000
diff --git a/doc-orig/new_gen/letter_size.tab b/doc-orig/new_gen/letter_size.tab
new file mode 100644
index 0000000..f9fef46
--- /dev/null
+++ b/doc-orig/new_gen/letter_size.tab
@@ -0,0 +1,28 @@
+ at Drill<br>Size	Diameter<br>(inches)
+A		.2340
+B		.2380
+C		.2420
+D		.2460
+E		.2500
+F		.2570
+G		.2610
+H		.2660
+I		.2720
+J		.2770
+K		.2810
+L		.2900
+M		.2950
+N		.3020
+O		.3160
+P		.3230
+Q		.3320
+R		.3390
+S		.3480
+T		.3580
+U		.3680
+V		.3770
+W		.3860
+X		.3970
+Y		.4040
+Z		.4130
+
diff --git a/doc-orig/new_gen/metric_size.tab b/doc-orig/new_gen/metric_size.tab
new file mode 100644
index 0000000..dfb85e2
--- /dev/null
+++ b/doc-orig/new_gen/metric_size.tab
@@ -0,0 +1,190 @@
+ at Drill<br>Size	Diameter<br>(inches)
+0.20 mm		.00787
+0.25 mm		.00984
+0.30 mm			.0118
+0.35 mm		.0138
+0.40 mm			.0158
+0.45 mm		.0177
+0.50 mm			.0197
+0.55 mm		.0217
+0.60 mm			.0236
+0.65 mm		.0256
+0.70 mm		.0276
+0.75 mm		.0295
+0.80 mm		.0315
+0.85 mm		.0335
+0.90 mm		.0354
+0.95 mm		.0374
+1.00 mm		.0394
+1.05 mm		.0413
+1.10 mm		.0433
+1.15 mm		.0453
+1.20 mm		.0472
+1.25 mm		.0492
+1.30 mm		.0512
+1.35 mm		.0531
+1.40 mm		.0551
+1.45 mm		.0571
+1.50 mm		.0591
+1.55 mm		.0610
+1.60 mm		.0630
+1.65 mm		.0650
+1.70 mm		.0669
+1.75 mm		.0689
+1.80 mm		.0709
+1.85 mm		.0728
+1.90 mm		.0748
+1.95 mm		.0768
+2.00 mm		.0787
+2.05 mm		.0807
+2.10 mm		.0827
+2.15 mm		.0846
+2.20 mm		.0866
+2.25 mm		.0886
+2.30 mm		.0906
+2.35 mm		.0925
+2.40 mm		.0945
+2.45 mm		.0965
+2.50 mm		.0984
+2.55 mm		.1004
+2.60 mm		.1024
+2.65 mm		.1043
+2.70 mm		.1063
+2.75 mm		.1083
+2.80 mm		.1102
+2.85 mm		.1122
+2.90 mm		.1142
+2.95 mm		.1161
+3.00 mm		.1181
+3.10 mm		.1220
+3.15 mm		.1240
+3.20 mm		.1260
+3.25 mm		.1280
+3.30 mm		.1299
+3.40 mm		.1339
+3.50 mm		.1378
+3.60 mm		.1417
+3.70 mm		.1457
+3.75 mm		.1476
+3.80 mm		.1496
+3.90 mm		.1535
+4.00 mm		.1575
+4.10 mm		.1614
+4.20 mm		.1654
+4.25 mm		.1673
+4.30 mm		.1693
+4.40 mm		.1732
+4.50 mm		.1772
+4.60 mm		.1811
+4.70 mm		.1850
+4.75 mm		.1870
+4.80 mm		.1890
+4.90 mm		.1929
+5.00 mm		.1969
+5.10 mm		.2008
+5.20 mm		.2047
+5.25 mm		.2067
+5.30 mm		.2087
+5.40 mm		.2126
+5.50 mm		.2165
+5.60 mm		.2205
+5.70 mm		.2244
+5.75 mm		.2264
+5.80 mm		.2283
+5.90 mm		.2323
+6.00 mm		.2362
+6.10 mm		.2402
+6.20 mm		.2441
+6.25 mm		.2461
+6.30 mm		.2480
+6.40 mm		.2520
+6.50 mm		.2559
+6.60 mm		.2598
+6.70 mm		.2638
+6.75 mm		.2657
+6.80 mm		.2677
+6.90 mm		.2717
+7.00 mm		.2756
+7.10 mm		.2795
+7.20 mm		.2835
+7.25 mm		.2854
+7.30 mm		.2874
+7.40 mm		.2914
+7.50 mm		.2953
+7.60 mm		.2992
+7.70 mm		.3031
+8.00 mm		.3150
+8.10 mm		.3189
+8.20 mm		.3228
+8.25 mm		.3248
+8.30 mm		.3268
+8.40 mm		.3307
+8.50 mm		.3346
+8.60 mm		.3386
+8.70 mm		.3425
+8.75 mm		.3445
+8.80 mm		.3465
+8.90 mm		.3504
+9.00 mm		.3543
+9.10 mm		.3583
+9.20 mm		.3622
+9.25 mm		.3642
+9.30 mm		.3661
+9.40 mm		.3701
+9.50 mm		.3740
+9.60 mm		.3780
+9.70 mm		.3819
+9.75 mm		.3839
+9.80 mm		.3858
+9.90 mm		.3898
+10.00 mm	.3937
+10.10 mm	.3976
+10.20 mm	.4016
+10.25 mm	.4035
+10.30 mm	.4055
+10.40 mm	.4094
+10.50 mm	.4134
+10.60 mm	.4173
+10.70 mm	.4213
+10.80 mm	.4252
+10.90 mm	.4291
+11.00 mm	.4331
+11.10 mm	.4370
+11.20 mm	.4409
+11.25 mm	.4429
+11.30 mm	.4449
+11.40 mm	.4488
+11.50 mm	.4528
+11.60 mm	.4567
+11.70 mm	.4606
+11.75 mm	.4626
+11.80 mm	.4646
+11.90 mm	.4685
+12.00 mm	.4724
+12.50 mm	.4921
+13.00 mm	.5118
+13.50 mm	.5315
+14.00 mm	.5512
+14.50 mm	.5709
+15.00 mm	.5906
+15.50 mm	.6102
+16.00 mm	.6299
+16.50 mm	.6496
+17.00 mm	.6693
+17.50 mm	.6890
+18.00 mm	.7087
+18.50 mm	.7283
+19.00 mm	.7480
+19.50 mm	.7677
+20.00 mm	.7874
+20.50 mm	.8071
+21.00 mm	.8268
+21.50 mm	.8465
+22.00 mm	.8661
+22.50 mm	.8858
+23.00 mm	.9055
+23.50 mm	.9252
+24.00 mm	.9449
+24.50 mm	.9646
+25.00 mm	.9843
+
diff --git a/doc-orig/new_gen/tab2html.sh b/doc-orig/new_gen/tab2html.sh
new file mode 100755
index 0000000..a00d255
--- /dev/null
+++ b/doc-orig/new_gen/tab2html.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# Configuration: $1 is the number of col pairs in a row
+
+echo "<html><body>"
+echo "<!-- Generated by tab2html.sh, do not edit! -->"
+echo "<table border=1 cellspacing=0>"
+awk -F "[\t]*" -v "cols=$1" '
+BEGIN {
+	len = 0
+}
+
+/^[@]/ {
+# print header
+	print "<tr>"
+	sub("^@", "", $0)
+	for(n = 0; n < cols; n++)
+		print "<th>" $1 "<th>" $2
+
+	next
+}
+
+# load data
+(NF == 2) {
+	CELL[len] = "<td>" $1 "<td>" $2
+	len++
+}
+
+END {
+	rows = len / cols
+	if (rows != int(rows))
+		rows++;
+	rows = int(rows);
+	for(r = 0; r < rows; r++) {
+		print "<tr>"
+		for(c = 0; c < cols; c++) {
+			idx = c*rows + r
+			cell = CELL[idx]
+			if (cell == "")
+				cell = "<td> <td> "
+			print cell
+		}
+	}
+}
+
+'
+echo "</table></body></html>"
diff --git a/doc-orig/new_gen/wire_size.tab b/doc-orig/new_gen/wire_size.tab
new file mode 100644
index 0000000..710d4a1
--- /dev/null
+++ b/doc-orig/new_gen/wire_size.tab
@@ -0,0 +1,100 @@
+ at Drill<br>Size	Diameter<br>(inches)
+97		.0059
+96		.0063
+95		.0067
+94		.0071
+93		.0075
+92		.0079
+91		.0083
+90		.0087
+89		.0091
+88		.0095
+87		.0100
+86		.0105
+85		.0110
+84		.0115
+83		.0120
+82		.0125
+81		.0130
+80		.0135
+79		.0145
+78		.0160
+77		.0180
+76		.0200
+75		.0210
+74		.0225
+73		.0240
+72		.0250
+71		.0260
+70		.0280
+69		.0292
+68		.0310
+67		.0320
+66		.0330
+65		.0350
+64		.0360
+63		.0370
+62		.0380
+61		.0390
+60		.0400
+59		.0410
+58		.0420
+57		.0430
+56		.0465
+55		.0520
+54		.0550
+53		.0595
+52		.0635
+51		.0670
+50		.0700
+49		.0730
+48		.0760
+47		.0785
+46		.0810
+45		.0820
+44		.0860
+43		.0890
+42		.0935
+41		.0960
+40		.0980
+39		.0995
+38		.1015
+37		.1040
+36		.1065
+35		.1100
+34		.1110
+33		.1130
+32		.1160
+31		.1200
+30		.1285
+29		.1360
+28		.1405
+27		.1440
+26		.1470
+25		.1495
+24		.1520
+23		.1540
+22		.1570
+21		.1590
+20		.1610
+19		.1660
+18		.1695
+17		.1730
+16		.1770
+15		.1800
+14		.1820
+13		.1850
+12		.1890
+11		.1910
+10		.1935
+9		.1960
+8		.1990
+7		.2010
+6		.2040
+5		.2055
+4		.2090
+3		.2130
+2		.2210
+1		.2280
+
+
diff --git a/doc-orig/options.texi b/doc-orig/options.texi
new file mode 100644
index 0000000..8602b31
--- /dev/null
+++ b/doc-orig/options.texi
@@ -0,0 +1,896 @@
+ at c key options
+ at menu
+* General Options::
+* General GUI Options::
+* GTK+ GUI Options::
+* lesstif GUI Options::
+* Colors::
+* Layer Names::
+* Paths::
+* Sizes::
+* Commands::
+* DRC Options::
+* BOM Creation::
+* Gerber Export::
+* Postscript Export::
+* Encapsulated Postscript Export::
+* PNG Options::
+* lpr Printing Options::
+* nelma Options::
+ at end menu
+ at c options General Options
+ at node General Options
+ at section General Options
+ at c ./../src/main.c 434
+ at ftable @code
+ at item --help
+Show help on command line options.
+ at end ftable
+ at c ./../src/main.c 439
+ at ftable @code
+ at item --version
+Show version.
+ at end ftable
+ at c ./../src/main.c 443
+ at ftable @code
+ at item --verbose
+Be verbose on stdout.
+ at end ftable
+ at c ./../src/main.c 448
+ at ftable @code
+ at item --copyright
+Show copyright.
+ at end ftable
+ at c ./../src/main.c 453
+ at ftable @code
+ at item --show-defaults
+Show option defaults.
+ at end ftable
+ at c ./../src/main.c 458
+ at ftable @code
+ at item --show-actions
+Show available actions and exit.
+ at end ftable
+ at c ./../src/main.c 463
+ at ftable @code
+ at item --dump-actions
+Dump actions (for documentation).
+ at end ftable
+ at c ./../src/main.c 468
+ at ftable @code
+ at item --grid-units-mm <string>
+Set default grid units. Can be mm or mil. Defaults to mil.
+ at end ftable
+ at c ./../src/main.c 686
+ at ftable @code
+ at item --backup-interval
+Time between automatic backups in seconds. Set to @code{0} to disable.
+The default value is @code{60}.
+ at end ftable
+ at c ./../src/main.c 723
+ at ftable @code
+ at item --groups <string>
+Layer group string. Defaults to @code{"1,c:2:3:4:5:6,s:7:8"}.
+ at end ftable
+ at c ./../src/main.c 788
+ at ftable @code
+ at item --route-styles <string>
+A string that defines the route styles. Defaults to @*
+ at code{"Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000
+	:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"}
+ at end ftable
+ at c ./../src/main.c 807
+ at ftable @code
+ at item --element-path <string>
+A colon separated list of directories or commands (starts with '|').
+The path is passed to the program specified in @option{--element-command}.
+ at end ftable
+ at c ./../src/main.c 817
+ at ftable @code
+ at item --action-script <string>
+If set, this file is executed at startup.
+ at end ftable
+ at c ./../src/main.c 822
+ at ftable @code
+ at item --action-string <string>
+If set, this string of actions is executed at startup.
+ at end ftable
+ at c ./../src/main.c 827
+ at ftable @code
+ at item --fab-author <string>
+Name of author to be put in the Gerber files.
+ at end ftable
+ at c ./../src/main.c 832
+ at ftable @code
+ at item --layer-stack <string>
+Initial layer stackup, for setting up an export. A comma separated list of layer
+names, layer numbers and layer groups.
+ at end ftable
+ at c ./../src/main.c 883
+ at ftable @code
+ at item --save-last-command
+If set, the last user command is saved.
+ at end ftable
+ at c ./../src/main.c 887
+ at ftable @code
+ at item --save-in-tmp
+If set, all data which would otherwise be lost are saved in a temporary file
+ at file{/tmp/PCB.%i.save} . Sequence @samp{%i} is replaced by the process ID.
+ at end ftable
+ at c ./../src/main.c 901
+ at ftable @code
+ at item --reset-after-element
+If set, all found connections are reset before a new component is scanned.
+ at end ftable
+ at c ./../src/main.c 906
+ at ftable @code
+ at item --ring-bell-finished
+Execute the bell command when all rats are routed.
+ at end ftable
+ at c options General GUI Options
+ at node General GUI Options
+ at section General GUI Options
+ at c ./../src/main.c 842
+ at ftable @code
+ at item --pinout-offset-x <num>
+Horizontal offset of the pin number display. Defaults to @code{100mil}.
+ at end ftable
+ at c ./../src/main.c 847
+ at ftable @code
+ at item --pinout-offset-y <num>
+Vertical offset of the pin number display. Defaults to @code{100mil}.
+ at end ftable
+ at c ./../src/main.c 852
+ at ftable @code
+ at item --pinout-text-offset-x <num>
+Horizontal offset of the pin name display. Defaults to @code{800mil}.
+ at end ftable
+ at c ./../src/main.c 857
+ at ftable @code
+ at item --pinout-text-offset-y <num>
+Vertical offset of the pin name display. Defaults to @code{-100mil}.
+ at end ftable
+ at c ./../src/main.c 862
+ at ftable @code
+ at item --draw-grid
+If set, draw the grid at start-up.
+ at end ftable
+ at c ./../src/main.c 866
+ at ftable @code
+ at item --clear-line
+If set, new lines clear polygons.
+ at end ftable
+ at c ./../src/main.c 870
+ at ftable @code
+ at item --full-poly
+If set, new polygons are full ones.
+ at end ftable
+ at c ./../src/main.c 874
+ at ftable @code
+ at item --unique-names
+If set, you will not be permitted to change the name of an component to match that
+of another component.
+ at end ftable
+ at c ./../src/main.c 878
+ at ftable @code
+ at item --snap-pin
+If set, pin centers and pad end points are treated as additional grid points
+that the cursor can snap to.
+ at end ftable
+ at c ./../src/main.c 892
+ at ftable @code
+ at item --all-direction-lines
+Allow all directions, when drawing new lines.
+ at end ftable
+ at c ./../src/main.c 897
+ at ftable @code
+ at item --show-number
+Pinout shows number.
+ at end ftable
+ at c options GTK+ GUI Options
+ at node GTK+ GUI Options
+ at section GTK+ GUI Options
+ at c ./../src/hid/gtk/gui-top-window.c 1615
+ at ftable @code
+ at item --listen
+Listen for actions on stdin.
+ at end ftable
+ at c ./../src/hid/gtk/gui-top-window.c 1621
+ at ftable @code
+ at item --bg-image <string>
+File name of an image to put into the background of the GUI canvas. The image must
+be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
+automatically scaled to fit the canvas.
+ at end ftable
+ at c ./../src/hid/gtk/gui-top-window.c 1627
+ at ftable @code
+ at item --pcb-menu <string>
+Location of the @file{gpcb-menu.res} file which defines the menu for the GTK+ GUI.
+ at end ftable
+ at c options lesstif GUI Options
+ at node lesstif GUI Options
+ at section lesstif GUI Options
+ at c ./../src/hid/lesstif/main.c 201
+ at ftable @code
+ at item --listen
+Listen for actions on stdin.
+ at end ftable
+ at c ./../src/hid/lesstif/main.c 207
+ at ftable @code
+ at item --bg-image <string>
+File name of an image to put into the background of the GUI canvas. The image must
+be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
+automatically scaled to fit the canvas.
+ at end ftable
+ at c ./../src/hid/lesstif/main.c 213
+ at ftable @code
+ at item --pcb-menu <string>
+Location of the @file{pcb-menu.res} file which defines the menu for the lesstif GUI.
+ at end ftable
+ at c options Colors
+ at node Colors
+ at section Colors
+ at c ./../src/main.c 473
+ at ftable @code
+ at item --black-color <string>
+Color value for black. Default: @samp{#000000}
+ at end ftable
+ at c ./../src/main.c 477
+ at ftable @code
+ at item --black-color <string>
+Color value for white. Default: @samp{#ffffff}
+ at end ftable
+ at c ./../src/main.c 481
+ at ftable @code
+ at item --background-color <string>
+Background color of the canvas. Default: @samp{#e5e5e5}
+ at end ftable
+ at c ./../src/main.c 486
+ at ftable @code
+ at item --crosshair-color <string>
+Color of the crosshair. Default: @samp{#ff0000}
+ at end ftable
+ at c ./../src/main.c 491
+ at ftable @code
+ at item --cross-color <string>
+Color of the cross. Default: @samp{#cdcd00}
+ at end ftable
+ at c ./../src/main.c 495
+ at ftable @code
+ at item --via-color <string>
+Color of vias. Default: @samp{#7f7f7f}
+ at end ftable
+ at c ./../src/main.c 499
+ at ftable @code
+ at item --via-selected-color <string>
+Color of selected vias. Default: @samp{#00ffff}
+ at end ftable
+ at c ./../src/main.c 504
+ at ftable @code
+ at item --pin-color <string>
+Color of pins. Default: @samp{#4d4d4d}
+ at end ftable
+ at c ./../src/main.c 508
+ at ftable @code
+ at item --pin-selected-color <string>
+Color of selected pins. Default: @samp{#00ffff}
+ at end ftable
+ at c ./../src/main.c 513
+ at ftable @code
+ at item --pin-name-color <string>
+Color of pin names and pin numbers. Default: @samp{#ff0000}
+ at end ftable
+ at c ./../src/main.c 518
+ at ftable @code
+ at item --element-color <string>
+Color of components. Default: @samp{#000000}
+ at end ftable
+ at c ./../src/main.c 522
+ at ftable @code
+ at item --rat-color <string>
+Color of ratlines. Default: @samp{#b8860b}
+ at end ftable
+ at c ./../src/main.c 526
+ at ftable @code
+ at item --invisible-objects-color <string>
+Color of invisible objects. Default: @samp{#cccccc}
+ at end ftable
+ at c ./../src/main.c 531
+ at ftable @code
+ at item --invisible-mark-color <string>
+Color of invisible marks. Default: @samp{#cccccc}
+ at end ftable
+ at c ./../src/main.c 536
+ at ftable @code
+ at item --element-selected-color <string>
+Color of selected components. Default: @samp{#00ffff}
+ at end ftable
+ at c ./../src/main.c 541
+ at ftable @code
+ at item --rat-selected-color <string>
+Color of selected rats. Default: @samp{#00ffff}
+ at end ftable
+ at c ./../src/main.c 546
+ at ftable @code
+ at item --connected-color <string>
+Color to indicate connections. Default: @samp{#00ff00}
+ at end ftable
+ at c ./../src/main.c 551
+ at ftable @code
+ at item --off-limit-color <string>
+Color of off-canvas area. Default: @samp{#cccccc}
+ at end ftable
+ at c ./../src/main.c 556
+ at ftable @code
+ at item --grid-color <string>
+Color of the grid. Default: @samp{#ff0000}
+ at end ftable
+ at c ./../src/main.c 560
+ at ftable @code
+ at item --layer-color-<n> <string>
+Color of layer @code{<n>}, where @code{<n>} is an integer from 1 to 16.
+ at end ftable
+ at c ./../src/main.c 578
+ at ftable @code
+ at item --layer-selected-color-<n> <string>
+Color of layer @code{<n>}, when selected. @code{<n>} is an integer from 1 to 16.
+ at end ftable
+ at c ./../src/main.c 597
+ at ftable @code
+ at item --warn-color <string>
+Color of offending objects during DRC. Default value is @code{"#ff8000"}
+ at end ftable
+ at c ./../src/main.c 601
+ at ftable @code
+ at item --mask-color <string>
+Color of the mask layer. Default value is @code{"#ff0000"}
+ at end ftable
+ at c options Layer Names
+ at node Layer Names
+ at section Layer Names
+ at c ./../src/main.c 691
+ at ftable @code
+ at item --layer-name-1 <string>
+Name of the 1st Layer. Default is @code{"top"}.
+ at end ftable
+ at c ./../src/main.c 695
+ at ftable @code
+ at item --layer-name-2 <string>
+Name of the 2nd Layer. Default is @code{"ground"}.
+ at end ftable
+ at c ./../src/main.c 699
+ at ftable @code
+ at item --layer-name-3 <string>
+Name of the 3nd Layer. Default is @code{"signal2"}.
+ at end ftable
+ at c ./../src/main.c 703
+ at ftable @code
+ at item --layer-name-4 <string>
+Name of the 4rd Layer. Default is @code{"signal3"}.
+ at end ftable
+ at c ./../src/main.c 707
+ at ftable @code
+ at item --layer-name-5 <string>
+Name of the 5rd Layer. Default is @code{"power"}.
+ at end ftable
+ at c ./../src/main.c 711
+ at ftable @code
+ at item --layer-name-6 <string>
+Name of the 6rd Layer. Default is @code{"bottom"}.
+ at end ftable
+ at c ./../src/main.c 715
+ at ftable @code
+ at item --layer-name-7 <string>
+Name of the 7rd Layer. Default is @code{"outline"}.
+ at end ftable
+ at c ./../src/main.c 719
+ at ftable @code
+ at item --layer-name-8 <string>
+Name of the 8rd Layer. Default is @code{"spare"}.
+ at end ftable
+ at c options Paths
+ at node Paths
+ at section Paths
+ at c ./../src/main.c 769
+ at ftable @code
+ at item --lib-newlib <string>
+Top level directory for the newlib style library.
+ at end ftable
+ at c ./../src/main.c 778
+ at ftable @code
+ at item --lib-name <string>
+The default filename for the library.
+ at end ftable
+ at c ./../src/main.c 783
+ at ftable @code
+ at item --default-font <string>
+The name of the default font.
+ at end ftable
+ at c ./../src/main.c 794
+ at ftable @code
+ at item --file-path <string>
+A colon separated list of directories or commands (starts with '|'). The path
+is passed to the program specified in @option{--file-command} together with the selected
+filename.
+ at end ftable
+ at c ./../src/main.c 802
+ at ftable @code
+ at item --font-path <string>
+A colon separated list of directories to search the default font. Defaults to
+the default library path.
+ at end ftable
+ at c ./../src/main.c 812
+ at ftable @code
+ at item --lib-path <string>
+A colon separated list of directories that will be passed to the commands specified
+by @option{--element-command} and @option{--element-contents-command}.
+ at end ftable
+ at c options Sizes
+ at node Sizes
+ at section Sizes
+ at c ./../src/main.c 606
+All parameters should be given with an unit. If no unit is given, 1/100 mil
+(cmil) will be used. Write units without space to the
+number like @code{3mm}, not @code{3 mm}.
+Valid Units are:
+ @table @samp
+   @item km
+    Kilometer
+   @item m
+    Meter
+   @item cm
+    Centimeter
+   @item mm
+    Millimeter
+   @item um
+    Micrometer
+   @item nm
+    Nanometer
+   @item in
+    Inch (1in = 0.0254m)
+   @item mil
+    Mil (1000mil = 1in)
+   @item cmil
+    Centimil (1/100 mil)
+ at end table
+
+ at ftable @code
+ at item --via-thickness <num>
+Default diameter of vias. Default value is @code{60mil}.
+ at end ftable
+ at c ./../src/main.c 611
+ at ftable @code
+ at item --via-drilling-hole <num>
+Default diameter of holes. Default value is @code{28mil}.
+ at end ftable
+ at c ./../src/main.c 616
+ at ftable @code
+ at item --line-thickness <num>
+Default thickness of new lines. Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 621
+ at ftable @code
+ at item --rat-thickness <num>
+Thickness of rats. Values from 1 to 19 are fixed width in screen pixels.
+Anything larger means PCB units (i.e. 100 means "1 mil"). Default value
+is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 625
+ at ftable @code
+ at item --keepaway <num>
+Default minimum distance between a track and adjacent copper.
+Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 629
+ at ftable @code
+ at item --default-PCB-width <num>
+Default width of the canvas. Default value is @code{6000mil}.
+ at end ftable
+ at c ./../src/main.c 634
+ at ftable @code
+ at item --default-PCB-height <num>
+Default height of the canvas. Default value is @code{5000mil}.
+ at end ftable
+ at c ./../src/main.c 639
+ at ftable @code
+ at item --text-scale <num>
+Default text scale. This value is in percent. Default value is @code{100}.
+ at end ftable
+ at c ./../src/main.c 643
+ at ftable @code
+ at item --alignment-distance <num>
+Specifies the distance between the board outline and alignment targets.
+Default value is @code{2mil}.
+ at end ftable
+ at c ./../src/main.c 677
+ at ftable @code
+ at item --grid <num>
+Initial grid size. Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 681
+ at ftable @code
+ at item --minimum polygon area <num>
+Minimum polygon area.
+ at end ftable
+ at c options Commands
+ at node Commands
+ at section Commands
+ at c ./../src/main.c 728
+pcb uses external commands for input output operations. These commands can be
+configured at start-up to meet local requirements. The command string may include
+special sequences @code{%f}, @code{%p} or @code{%a}. These are replaced when the
+command is called. The sequence @code{%f} is replaced by the file name,
+ at code{%p} gets the path and @code{%a} indicates a package name.
+ at c ./../src/main.c 731
+ at ftable @code
+ at item --font-command <string>
+Command to load a font.
+ at end ftable
+ at c ./../src/main.c 735
+ at ftable @code
+ at item --file-command <string>
+Command to read a file.
+ at end ftable
+ at c ./../src/main.c 739
+ at ftable @code
+ at item --element-command <string>
+Command to read a footprint. @*
+Defaults to @code{"M4PATH='%p';export M4PATH;echo 'include(%f)' | m4"}
+ at end ftable
+ at c ./../src/main.c 745
+ at ftable @code
+ at item --print-file <string>
+Command to print to a file.
+ at end ftable
+ at c ./../src/main.c 749
+ at ftable @code
+ at item --lib-command-dir <string>
+Path to the command that queries the library.
+ at end ftable
+ at c ./../src/main.c 754
+ at ftable @code
+ at item --lib-command <string>
+Command to query the library. @*
+Defaults to @code{"QueryLibrary.sh '%p' '%f' %a"}
+ at end ftable
+ at c ./../src/main.c 759
+ at ftable @code
+ at item --lib-contents-command <string>
+Command to query the contents of the library. @*
+Defaults to @code{"ListLibraryContents.sh %p %f"} or,
+on Windows builds, an empty string (to disable this feature).
+ at end ftable
+ at c ./../src/main.c 774
+ at ftable @code
+ at item --save-command <string>
+Command to save to a file.
+ at end ftable
+ at c ./../src/main.c 798
+ at ftable @code
+ at item --rat-command <string>
+Command for reading a netlist. Sequence @code{%f} is replaced by the netlist filename.
+ at end ftable
+ at c options DRC Options
+ at node DRC Options
+ at section DRC Options
+ at c ./../src/main.c 648
+All parameters should be given with an unit. If no unit is given, 1/100 mil
+(cmil) will be used for backward compability. Valid units are given in section
+ at ref{Sizes}.
+ at c ./../src/main.c 652
+ at ftable @code
+ at item --bloat <num>
+Minimum spacing. Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 656
+ at ftable @code
+ at item --shrink <num>
+Minimum touching overlap. Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 660
+ at ftable @code
+ at item --min-width <num>
+Minimum width of copper. Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 664
+ at ftable @code
+ at item --min-silk <num>
+Minimum width of lines in silk. Default value is @code{10mil}.
+ at end ftable
+ at c ./../src/main.c 668
+ at ftable @code
+ at item --min-drill <num>
+Minimum diameter of holes. Default value is @code{15mil}.
+ at end ftable
+ at c ./../src/main.c 672
+ at ftable @code
+ at item --min-ring <num>
+Minimum width of annular ring. Default value is @code{10mil}.
+ at end ftable
+ at c options BOM Creation
+ at node BOM Creation
+ at section BOM Creation
+ at c ./../src/hid/bom/bom.c 30
+ at ftable @code
+ at item --bomfile <string>
+Name of the BOM output file.
+ at end ftable
+ at c ./../src/hid/bom/bom.c 35
+ at ftable @code
+ at item --xyfile <string>
+Name of the XY output file.
+ at end ftable
+ at c ./../src/hid/bom/bom.c 41
+ at ftable @code
+ at item --xy-unit <unit>
+Unit of XY dimensions. Defaults to mil.
+ at end ftable
+ at c options Gerber Export
+ at node Gerber Export
+ at section Gerber Export
+ at c ./../src/hid/gerber/gerber.c 338
+ at ftable @code
+ at item --gerberfile <string>
+Gerber output file prefix. Can include a path.
+ at end ftable
+ at c ./../src/hid/gerber/gerber.c 344
+ at ftable @code
+ at item --all-layers
+Output contains all layers, even empty ones.
+ at end ftable
+ at c ./../src/hid/gerber/gerber.c 350
+ at ftable @code
+ at item --verbose
+Print file names and aperture counts on stdout.
+ at end ftable
+ at c options Postscript Export
+ at node Postscript Export
+ at section Postscript Export
+ at c ./../src/hid/ps/ps.c 149
+ at ftable @code
+ at item --psfile <string>
+Name of the postscript output file. Can contain a path.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 155
+ at ftable @code
+ at cindex drill-helper
+ at item --drill-helper
+Print a centering target in large drill holes.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 161
+ at ftable @code
+ at cindex align-marks
+ at item --align-marks
+Print alignment marks on each sheet. This is meant to ease alignment during exposure.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 167
+ at ftable @code
+ at item --outline
+Print the contents of the outline layer on each sheet.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 172
+ at ftable @code
+ at item --mirror
+Print mirror image.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 178
+ at ftable @code
+ at item --fill-page
+Scale output to make the board fit the page.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 184
+ at ftable @code
+ at item --auto-mirror
+Print mirror image of appropriate layers.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 190
+ at ftable @code
+ at item --ps-color
+Postscript output in color.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 196
+ at ftable @code
+ at cindex ps-bloat
+ at item --ps-bloat <num>
+Amount to add to trace/pad/pin edges.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 202
+ at ftable @code
+ at cindex ps-invert
+ at item --ps-invert
+Draw objects as white-on-black.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 208
+ at ftable @code
+ at item --media <media-name>
+Size of the media, the postscript is fitted to. The parameter
+ at code{<media-name>} can be any of the standard names for paper size: @samp{A0}
+to @samp{A10}, @samp{B0} to @samp{B10}, @samp{Letter}, @samp{11x17},
+ at samp{Ledger}, @samp{Legal}, @samp{Executive}, @samp{A-Size}, @samp{B-size},
+ at samp{C-Size}, @samp{D-size}, @samp{E-size}, @samp{US-Business_Card},
+ at samp{Intl-Business_Card}.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 214
+ at ftable @code
+ at cindex psfade
+ at item --psfade <num>
+Fade amount for assembly drawings (0.0=missing, 1.0=solid).
+ at end ftable
+ at c ./../src/hid/ps/ps.c 220
+ at ftable @code
+ at item --scale <num>
+Scale value to compensate for printer sizing errors (1.0 = full scale).
+ at end ftable
+ at c ./../src/hid/ps/ps.c 226
+ at ftable @code
+ at cindex multi-file
+ at item --multi-file
+Produce multiple files, one per page, instead of a single multi page file.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 232
+ at ftable @code
+ at item --xcalib <num>
+Paper width. Used for x-Axis calibration.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 238
+ at ftable @code
+ at item --ycalib <num>
+Paper height. Used for y-Axis calibration.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 244
+ at ftable @code
+ at item --drill-copper
+Draw drill holes in pins / vias, instead of leaving solid copper.
+ at end ftable
+ at c ./../src/hid/ps/ps.c 250
+ at ftable @code
+ at cindex show-legend
+ at item --show-legend
+Print file name and scale on printout.
+ at end ftable
+ at c options Encapsulated Postscript Export
+ at node Encapsulated Postscript Export
+ at section Encapsulated Postscript Export
+ at c ./../src/hid/ps/eps.c 78
+ at ftable @code
+ at item --eps-file <string>
+Name of the encapsulated postscript output file. Can contain a path.
+ at end ftable
+ at c ./../src/hid/ps/eps.c 84
+ at ftable @code
+ at item --eps-scale <num>
+Scale EPS output by the parameter @samp{num}.
+ at end ftable
+ at c ./../src/hid/ps/eps.c 90
+ at ftable @code
+ at cindex as-shown (EPS)
+ at item --as-shown
+Export layers as shown on screen.
+ at end ftable
+ at c ./../src/hid/ps/eps.c 96
+ at ftable @code
+ at item --monochrome
+Convert output to monochrome.
+ at end ftable
+ at c ./../src/hid/ps/eps.c 102
+ at ftable @code
+ at cindex only-visible
+ at item --only-visible
+Limit the bounds of the EPS file to the visible items.
+ at end ftable
+ at c options PNG Options
+ at node PNG Options
+ at section PNG Options
+ at c ./../src/hid/png/png.c 168
+ at ftable @code
+ at item --outfile <string>
+Name of the file to be exported to. Can contain a path.
+ at end ftable
+ at c ./../src/hid/png/png.c 174
+ at ftable @code
+ at item --dpi
+Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout.
+ at end ftable
+ at c ./../src/hid/png/png.c 180
+ at ftable @code
+ at item --x-max
+Width of the png image in pixels. No constraint, when set to 0.
+ at end ftable
+ at c ./../src/hid/png/png.c 186
+ at ftable @code
+ at item --y-max
+Height of the png output in pixels. No constraint, when set to 0.
+ at end ftable
+ at c ./../src/hid/png/png.c 192
+ at ftable @code
+ at item --xy-max
+Maximum width and height of the PNG output in pixels. No constraint, when set to 0.
+ at end ftable
+ at c ./../src/hid/png/png.c 198
+ at ftable @code
+ at item --as-shown
+Export layers as shown on screen.
+ at end ftable
+ at c ./../src/hid/png/png.c 204
+ at ftable @code
+ at item --monochrome
+Convert output to monochrome.
+ at end ftable
+ at c ./../src/hid/png/png.c 210
+ at ftable @code
+ at item --only-vivible
+Limit the bounds of the exported PNG image to the visible items.
+ at end ftable
+ at c ./../src/hid/png/png.c 216
+ at ftable @code
+ at item --use-alpha
+Make the background and any holes transparent.
+ at end ftable
+ at c ./../src/hid/png/png.c 222
+ at ftable @code
+ at item --format <string>
+File format to be exported. Parameter @code{<string>} can be @samp{PNG},
+ at samp{GIF}, or @samp{JPEG}.
+ at end ftable
+ at c ./../src/hid/png/png.c 228
+ at ftable @code
+ at item --png-bloat <num><dim>
+Amount of extra thickness to add to traces, pads, or pin edges. The parameter
+ at samp{<num><dim>} is a number, appended by a dimension @samp{mm}, @samp{mil}, or
+ at samp{pix}. If no dimension is given, the default dimension is 1/100 mil.
+ at end ftable
+ at c ./../src/hid/png/png.c 234
+ at ftable @code
+ at cindex photo-mode
+ at item --photo-mode
+Export a photo realistic image of the layout.
+ at end ftable
+ at c ./../src/hid/png/png.c 240
+ at ftable @code
+ at item --photo-flip-x
+In photo-realistic mode, export the reverse side of the layout. Left-right flip.
+ at end ftable
+ at c ./../src/hid/png/png.c 246
+ at ftable @code
+ at item --photo-flip-y
+In photo-realistic mode, export the reverse side of the layout. Up-down flip.
+ at end ftable
+ at c options lpr Printing Options
+ at node lpr Printing Options
+ at section lpr Printing Options
+ at c ./../src/hid/lpr/lpr.c 33
+ at ftable @code
+ at item --lprcommand <string>
+Command to use for printing. Defaults to @code{lpr}. This can be used to produce
+PDF output with a virtual PDF printer. Example: @*
+ at code{--lprcommand "lp -d CUPS-PDF-Printer"}.
+ at end ftable
+ at noindent In addition, all @ref{Postscript Export} options are valid.
+ at c options nelma Options
+ at node nelma Options
+ at section nelma Options
+ at c ./../src/hid/nelma/nelma.c 157
+ at ftable @code
+ at item -- basename <string>
+File name prefix.
+ at end ftable
+ at c ./../src/hid/nelma/nelma.c 163
+ at ftable @code
+ at item --dpi <num>
+Horizontal scale factor (grid points/inch).
+ at end ftable
+ at c ./../src/hid/nelma/nelma.c 169
+ at ftable @code
+ at item --copper-height <num>
+Copper layer height (um).
+ at end ftable
+ at c ./../src/hid/nelma/nelma.c 175
+ at ftable @code
+ at item --substrate-height <num>
+Substrate layer height (um).
+ at end ftable
+ at c ./../src/hid/nelma/nelma.c 181
+ at ftable @code
+ at item --substrate-epsilon <num>
+Substrate relative epsilon.
+ at end ftable
diff --git a/doc-orig/pad.pcb b/doc-orig/pad.pcb
new file mode 100644
index 0000000..bea2ec2
--- /dev/null
+++ b/doc-orig/pad.pcb
@@ -0,0 +1,911 @@
+# release: pcb-bin 1.99q
+
+PCB["pad" 600000 500000]
+
+Grid[2000.00000000 0 0 1]
+Cursor[88600 72400 2.000000]
+Thermal[0.500000]
+DRC[699 400 800 800 1000 750]
+Flags(0x0000000000000840)
+Groups("1,s:2,c:3:4:5:6:7:8")
+Styles["Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000:Fat,4000,6000,3500,1000:Skinny,200,2402,1181,600"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2500 3000 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 3000 1500 3000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[2500 1000 2500 2500 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2500 800]
+	SymbolLine[1500 3000 2000 2500 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 1500 0 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 3000 1500 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 3000 1500 3000 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[0 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 2500 800]
+	SymbolLine[1500 2500 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 2500 4000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 4000 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[500 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[2000 1000 2000 4000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 1500 3500 800]
+	SymbolLine[1500 3500 3000 5000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 2500 4000 800]
+	SymbolLine[2500 4000 2500 5000 800]
+	SymbolLine[0 4000 0 5000 800]
+	SymbolLine[0 4000 2500 1500 800]
+	SymbolLine[2500 1000 2500 1500 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 1000 2500 800]
+	SymbolLine[1000 2500 2000 1500 800]
+	SymbolLine[2000 1000 2000 1500 800]
+	SymbolLine[1000 2500 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[2500 1000 2500 1500 800]
+	SymbolLine[0 4000 2500 1500 800]
+	SymbolLine[0 4000 0 5000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2500 800]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[2000 3000 2000 4000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Layer(1 "component")
+(
+	Line[106000 144000 262000 144000 1000 2000 "clearline"]
+	Line[106000 144000 106000 84000 1000 2000 "clearline"]
+	Line[106000 84000 262000 84000 1000 2000 "clearline"]
+	Line[262000 84000 262000 144000 1000 2000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[256000 90000 256000 138000 1000 2000 "clearline"]
+	Line[112000 138000 112000 90000 1000 2000 "clearline"]
+	Line[112000 90000 256000 90000 1000 2000 "clearline"]
+	Line[112000 138000 256000 138000 1000 2000 "clearline"]
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+	Polygon("clearpoly")
+	(
+		[118000 96000] [250000 96000] [250000 132000] [118000 132000] 
+	)
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "unused")
+(
+)
+Layer(8 "unused")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[118000 96000 118000 132000 500 2000 "clearline"]
+	Line[118000 132000 154000 132000 500 2000 "clearline"]
+	Line[154000 132000 154000 96000 500 2000 "clearline"]
+	Line[154000 96000 118000 96000 500 2000 "clearline"]
+	Line[136000 152000 136000 120000 500 1200 "clearline"]
+	Line[136000 120000 132000 124000 500 1200 "clearline"]
+	Line[132000 114000 140000 114000 1010 1200 "clearline"]
+	Line[140000 124000 136000 120000 500 1200 "clearline"]
+	Line[98000 90000 94000 94000 500 1200 "clearline"]
+	Line[214000 76000 250000 76000 500 1200 "clearline"]
+	Line[98000 138000 98000 90000 500 1200 "clearline"]
+	Line[270000 96000 266000 100000 500 1200 "clearline"]
+	Line[136000 110000 136000 118000 1010 1200 "clearline"]
+	Line[214000 96000 214000 132000 500 2000 "clearline"]
+	Line[252000 132000 276000 132000 500 1200 "clearline"]
+	Line[218000 72000 214000 76000 500 1200 "clearline"]
+	Line[214000 76000 218000 80000 500 1200 "clearline"]
+	Line[270000 96000 274000 100000 500 1200 "clearline"]
+	Line[252000 96000 276000 96000 500 1200 "clearline"]
+	Line[214000 132000 250000 132000 500 2000 "clearline"]
+	Line[136000 90000 140000 86000 500 1200 "clearline"]
+	Line[232000 150000 232000 120000 500 1200 "clearline"]
+	Line[236000 124000 232000 120000 500 1200 "clearline"]
+	Line[232000 110000 232000 118000 1010 1200 "clearline"]
+	Line[232000 120000 228000 124000 500 1200 "clearline"]
+	Line[204000 114000 212000 114000 500 1200 "clearline"]
+	Line[212000 114000 208000 110000 500 1200 "clearline"]
+	Line[212000 114000 208000 118000 500 1200 "clearline"]
+	Line[214000 94000 214000 70000 500 1200 "clearline"]
+	Line[250000 94000 250000 70000 500 1200 "clearline"]
+	Line[204000 114000 204000 48000 500 1200 "clearline"]
+	Line[136000 90000 132000 86000 500 1200 "clearline"]
+	Line[270000 132000 266000 128000 500 1200 "clearline"]
+	Line[250000 132000 250000 96000 500 2000 "clearline"]
+	Line[250000 96000 214000 96000 500 2000 "clearline"]
+	Line[160000 90000 112000 90000 500 2000 "clearline"]
+	Line[160000 138000 112000 138000 500 2000 "clearline"]
+	Line[112000 90000 112000 138000 500 2000 "clearline"]
+	Line[160000 90000 160000 138000 500 2000 "clearline"]
+	Line[136000 74000 136000 90000 500 1200 "clearline"]
+	Line[270000 132000 274000 128000 500 1200 "clearline"]
+	Line[228000 114000 236000 114000 1010 1200 "clearline"]
+	Line[270000 132000 270000 96000 500 1200 "clearline"]
+	Line[250000 76000 246000 72000 500 1200 "clearline"]
+	Line[250000 76000 246000 80000 500 1200 "clearline"]
+	Line[92000 90000 110000 90000 500 1200 "clearline"]
+	Line[186000 96000 190000 92000 500 1200 "clearline"]
+	Line[186000 96000 182000 92000 500 1200 "clearline"]
+	Line[186000 64000 186000 96000 500 1200 "clearline"]
+	Line[98000 138000 94000 134000 500 1200 "clearline"]
+	Line[98000 138000 102000 134000 500 1200 "clearline"]
+	Line[98000 90000 102000 94000 500 1200 "clearline"]
+	Line[92000 138000 110000 138000 500 1200 "clearline"]
+	Line[214000 138000 210000 142000 500 1200 "clearline"]
+	Line[218000 142000 214000 138000 500 1200 "clearline"]
+	Line[214000 168000 214000 138000 500 1200 "clearline"]
+	Line[192000 144000 188000 148000 500 1200 "clearline"]
+	Line[196000 148000 192000 144000 500 1200 "clearline"]
+	Line[192000 184000 192000 144000 500 1200 "clearline"]
+	Line[168000 144000 164000 148000 500 1200 "clearline"]
+	Line[172000 148000 168000 144000 500 1200 "clearline"]
+	Line[168000 176000 168000 144000 500 1200 "clearline"]
+	Line[168000 132000 172000 128000 500 1200 "clearline"]
+	Line[168000 132000 164000 128000 500 1200 "clearline"]
+	Line[168000 120000 168000 132000 500 1200 "clearline"]
+	Text[106000 152000 0 200 "(X0,Y0)" ""]
+	Text[224000 150000 0 200 "(X1,Y1)" ""]
+	Text[226000 72000 1 200 "SIZE" ""]
+	Text[41000 106000 0 200 "Mask Size" ""]
+	Text[56000 62000 0 200 "Mask Aperture" ""]
+	Text[130000 52000 0 200 "Pad Shape" ""]
+	Text[272000 110000 0 200 "SIZE" ""]
+	Text[132000 36000 0 200 "Pad Aperture" ""]
+	Text[210000 168000 0 200 "Soldermask Opening" ""]
+	Text[188000 186000 0 200 "Clearance to non-connecting" ""]
+	Text[188000 198000 0 200 "polygons" ""]
+	Text[102000 176000 0 200 "Clearance/2" ""]
+)
diff --git a/doc-orig/pad.pdf b/doc-orig/pad.pdf
new file mode 100644
index 0000000..e54740c
Binary files /dev/null and b/doc-orig/pad.pdf differ
diff --git a/doc-orig/pad.png b/doc-orig/pad.png
new file mode 100644
index 0000000..4474f12
Binary files /dev/null and b/doc-orig/pad.png differ
diff --git a/doc-orig/pcb.1 b/doc-orig/pcb.1
new file mode 100644
index 0000000..b84c653
--- /dev/null
+++ b/doc-orig/pcb.1
@@ -0,0 +1,45 @@
+.\"
+.\"  This program is free software; you can redistribute it and/or modify
+.\"  it under the terms of the GNU General Public License as published by
+.\"  the Free Software Foundation; either version 2 of the License, or
+.\"  (at your option) any later version.
+.\"
+.\"  This program is distributed in the hope that it will be useful,
+.\"  but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"  GNU General Public License for more details.
+.\"
+.\"  You should have received a copy of the GNU General Public License
+.\"  along with this program; if not, write to the Free Software
+.\"  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+
+.TH PCB 1
+
+.SH NAME
+.B pcb
+\- Printed circuit board layout tool
+
+.SH SYNOPSIS
+.B pcb [options] [pcb file]
+
+.SH DESCRIPTION
+The
+.B pcb
+program is a tool for the layout of printed circuit boards.
+The complete manual for
+.B pcb
+is provided in a GNU texinfo format as well as HTML and PDF.
+The texinfo version of the manual is typically viewed with the
+.B info
+program or alternatively with 
+.B emacs
+or a graphical info viewer such as
+.B tkinfo.
+The PDF and HTML documentation is typically installed as
+/usr/local/share/pcb/pcb.html and /usr/local/share/pcb/pcb.pdf.
+The prefix "/usr/local" may vary at your site.
+
+
+
+
diff --git a/doc-orig/pcb.css b/doc-orig/pcb.css
new file mode 100644
index 0000000..d1242ad
--- /dev/null
+++ b/doc-orig/pcb.css
@@ -0,0 +1,11 @@
+table.cartouche {
+  border-collapse: collapse;
+  border: 1px solid #800000;
+  width: 100%;
+}
+table.cartouche td {
+  padding: 4px;
+}
+pre.format {
+  font-family: monospace;
+}
diff --git a/doc-orig/pcb.html b/doc-orig/pcb.html
new file mode 100644
index 0000000..283b6a1
--- /dev/null
+++ b/doc-orig/pcb.html
@@ -0,0 +1,12884 @@
+<html lang="en">
+<head>
+<title>pcb-rnd documentation</title>
+<meta http-equiv="Content-Type" content="text/html">
+<meta name="description" content="pcb-rnd">
+<link title="Top" rel="top" href="#Top">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css"><!--
+body {
+  font-family: arial, sans-serif;
+}
+  pre.display { font-family:inherit }
+  pre.format  { font-family:inherit }
+  pre.smalldisplay { font-family:inherit; font-size:smaller }
+  pre.smallformat  { font-family:inherit; font-size:smaller }
+  pre.smallexample { font-size:smaller }
+  pre.smalllisp    { font-size:smaller }
+  span.sc    { font-variant:small-caps }
+  span.roman { font-family:serif; font-weight:normal; } 
+  span.sansserif { font-family:sans-serif; font-weight:normal; } 
+table.cartouche {
+  font-family: arial, sans-serif;
+  border-collapse: collapse;
+  border: 1px solid #800000;
+  width: 100%;
+  }
+table.cartouche td, th {
+    border: 1px solid #dddddd;
+    text-align: left;
+    padding: 8px;
+}
+tr:nth-child(even) {
+    background-color: #dddddd;
+}
+pre.format {
+  font-family: arial, sans-serif;
+<!-- font-family: monospace;-->
+}
+--></style>
+</head>
+<body>
+<h1 class="settitle"><code>pcb-rnd</code> documentation</h1>
+<div class="node">
+<a name="Top"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Copying-License-Attribution">Copying, License & Attribution</a>,
+Up: <a rel="up" accesskey="u" href="#dir">(dir)</a>
+
+</div>
+
+<h2 class="unnumbered">pcb-rnd</h2>
+
+<p>This document is a temporary manual for the geda-related project
+<code>pcb-rnd</code>, which is forked from <code>pcb</code>.
+
+<p><code>pcb-rnd</code> is a program used for designing printed circuit board
+layouts.
+
+
+<ul class="menu">
+<li><a accesskey="1" href="#Copying-License-Attribution">Copying, License & Attribution</a>:      <code>pcb-rnd</code> is freely redistributable! 
+<li><a accesskey="2" href="#Background">Background</a>:            Background of pcb-rnd 
+<li><a accesskey="3" href="#Overview">Overview</a>:                An overview of <code>pcb-rnd</code>. 
+<li><a accesskey="4" href="#Intro">Intro</a>:                   A short description of the basic objects. 
+<li><a accesskey="5" href="#Getting-Started">Getting Started</a>:         Introduction to <code>pcb-rnd</code>. 
+<li><a accesskey="6" href="#User-Commands">User Commands</a>:           User commands of <code>pcb-rnd</code>. 
+<li><a accesskey="7" href="#Command_002dLine-Options">Command-Line Options</a>:    Calling <code>pcb-rnd</code> from a shell. 
+<li><a accesskey="8" href="#X11-Interface">X11 Interface</a>:           Action routines, resources and default translation. 
+<li><a href="#File-Formats">File Formats</a>:            Description of <code>ASCII</code> files used by <code>pcb-rnd</code>. 
+<li><a href="#Library-Creation">Library Creation</a>:        Detailed description of symbol library creation. 
+<li><a href="#Schematic-Frontends">Schematic Frontends</a>:     Schematic capture programs that work with PCB. 
+<li><a href="#Installation">Installation</a>:            Compiling, installing and troubleshooting. 
+<li><a href="#Custom-Menus">Custom Menus</a>:            Customizing the menu bar. 
+<li><a href="#Regular-Expressions">Regular Expressions</a>:     Searching for elements with regular expressions
+<li><a href="#Standard-Drill-Sizes">Standard Drill Sizes</a>:    Tables of standard drill sizes
+<li><a href="#Centroid-File-Format">Centroid File Format</a>:    Details of the centroid (x-y) output file
+<li><a href="#Action-Reference">Action Reference</a>:        Documentation for all available actions
+<li><a href="#Glossary">Glossary</a>:                Glossary
+<li><a href="#Index">Index</a>:                   The Index. 
+</ul>
+
+<!--  -->
+<div class="node">
+<a name="Copying-License-Attribution"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Background">Background</a>,
+Previous: <a rel="previous" accesskey="p" href="#Top">Top</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="unnumbered">Copying, License & Attribution</h2>
+<p>Copyright © 1994,1995,1996,1997 Thomas Nau
+
+<p>Copyright © 1998,1999,2000,2001,2002 harry eaton
+
+<p style="color:red;"> © insert new copyright here:  </p>
+
+<p style="color:red;">This program is free software; you may redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+<p style="color:red;">This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANT-ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+<b>GNU General Public License</b> for more details.
+
+<!--  chapter 0 - -->
+<div class="node">
+<a name="Background"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Overview">Overview</a>,
+Previous: <a rel="previous" accesskey="p"href="#Copying-license-attribution">Copying, License, & Atribution</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="unnumbered">Background</h2>
+
+<p><a name="index-pcb-rnd-origin"></a><code>pcb-rnd</code> is a  tool for
+laying out cicuit boards and creating export files required to fabricate them.
+   
+   <p><code>Pcb</code> was first written by Thomas Nau for an
+   Atari ST in 1990 and ported to <code>UNIX</code> and <code>X11</code> in
+   1994.  It was not intended as a professional layout system, but as a tool
+   which supports people who do some home-developing of hardware.
+
+   <p>Release history and credits for <code>Pcb</code> can be found in
+   the documentation files in mainline <code>Pcb</code>, and currently as of this writing at
+   <a href="http://pcb.geda-project.org/manual.html">http://pcb.geda-project.org/manual.html</a>
+
+<!--  overview chapter - -->
+<div class="node">
+<a name="Overview"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Intro">Intro</a>,
+Previous: <a rel="previous" accesskey="p" href="#Background">Background</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">1 Overview</h2>
+
+<p><a name="index-PCB_002c-an-overview-2"></a>
+<code>pcb-rnd</code> is an open source printed circuit board editor. 
+<code>pcb-rnd</code> includes many professional features such as:
+     <ul>
+<li>Up to 16 copper layer designs by default.  By changing a compile time setting, this
+can be set as high as needed. 
+<li>RS-274X (Gerber) output
+<li>NC Drill output
+<li>Centroid (X-Y) data output
+<li>Postscript and Encapsulated Postscript output
+<li>Autorouter
+<li>Trace optimizer
+<li>Rats nest
+<li>Design Rule Checker (DRC)
+<li>Connectivity verification
+<li><code>pcb-rnd</code> is Free Software
+<li>Can interoperate with free schematic capture tools such as gEDA and
+  xcircuit
+<li>Runs under Linux, NetBSD, Solaris, and other similar operating
+systems. 
+<li>Windows version is available
+</ul>
+
+<!--  chapter 1 - -->
+<div class="node">
+<a name="Intro"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Getting-Started">Getting Started</a>,
+Previous: <a rel="previous" accesskey="p" href="#Overview">Overview</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">2 Introduction</h2>
+<!-- note absolute and relative origins are recorded -->
+<p><a name="index-layout-objects_002c-an-overview-3"></a>
+Each layout consists of several, mostly independent, objects. This chapter
+gives an overview of the object types and their relationship to each other. 
+For a complete description of how to use <code>pcb-rnd</code>, refer to
+<a href="#Getting-Started">Getting Started</a>. 
+The layout is generated on-screen on a grid that can have its origin
+at any desired location. 
+The X coordinate increases to the right, Y increases down to the bottom. 
+All distances and sizes in <code>pcb-rnd</code> are measured in mils
+(0.001 inch).  One unit on the coordinate display is one mil in
+distance on the board. 
+The grid may be set on a metric pitch, but is only correct to within
+the nearest +/- 0.01 mil because <code>pcb-rnd</code> stores all dimensions as
+integer multiples of 1/100 of a mil or 0.00001 inch.
+
+   <p>The sections in this chapter are sorted by the
+order of appearance of the objects within a layout file.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Symbol-Objects">Symbol Objects</a>:          Information about fonts and symbols. 
+<li><a accesskey="2" href="#Via-Objects">Via Objects</a>:             Vias and pins connect layers. 
+<li><a accesskey="3" href="#Element-Objects">Element Objects</a>:         Element, the basic type of circuits. 
+<li><a accesskey="4" href="#Layer-Objects">Layer Objects</a>:           A ‘<samp><span class="samp">container</span></samp>’ for lines, text... 
+<li><a accesskey="5" href="#Line-Objects">Line Objects</a>:            Tracks on the board
+<li><a accesskey="6" href="#Arc-Objects">Arc Objects</a>:             Curved tracks
+<li><a accesskey="7" href="#Polygon-Objects">Polygon Objects</a>:         Planes and such
+<li><a accesskey="8" href="#Text-Objects">Text Objects</a>:            Objects to add symbols to your board. 
+<li><a accesskey="9" href="#Net-Objects">Net Objects</a>:             Describes the desired connections on the board. 
+</ul>
+
+<div class="node">
+<a name="Symbol-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Via-Objects">Via Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.1 Symbols</h3>
+
+<p><a name="index-symbols_002c-an-overview-4"></a><a name="index-font_002c-an-overview-5"></a>
+The top object is the layout itself. It uses a set of symbols
+that resides at the first logical level. Each symbol is uniquely identified
+by a seven bit <code>ASCII</code> code. All layout objects share the same set of
+symbols. These symbols are used to form text objects on the silkscreen
+and copper layers.  Undefined symbols are drawn as filled rectangles.
+
+   <p>Every font file is preprocessed by a user-defined command when it is loaded. 
+For details see ‘<samp><span class="samp">fontCommand</span></samp>’, <a href="#Resources">Resources</a>.
+
+<div class="node">
+<a name="Via-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Element-Objects">Element Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Symbol-Objects">Symbol Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.2 Vias</h3>
+
+<p><a name="index-vias_002c-an-overview-6"></a>
+Vias provide through-hole connectivity across all layers. 
+While vias look a lot like element pins, don't use vias
+for adding elements to the layout, even if that seems
+easier than creating a new element. The default solder-mask
+will cover over vias, so you won't be able to solder to them. 
+Of course, you can change this so that vias also have
+solder-mask cut-outs, but it is not the default. 
+Vias are also useful for defining arbitrary drill points such as
+those used for mounting a board. Vias used in this way have
+a special flag set so that they have no annular copper ring,
+and also appear in the unplated drill file. <em>Ctrl-H</em> key over
+a via switches it between being a pure-mounting hole and a regular via. 
+You can assign a name to a via, which is useful during the creation
+of new element definitions. 
+Each via exists on all copper layers. (<em>i.e.</em>
+blind and buried vias are not supported)
+
+<div class="node">
+<a name="Element-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Layer-Objects">Layer Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Via-Objects">Via Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.3 Elements</h3>
+
+<p><a name="index-element_002c-an-overview-7"></a><a name="index-layout_002dname-8"></a>
+Elements represent the components on a board. 
+Elements are loaded from <code>ASCII</code> coded files in a
+similar manner to the layout file itself, or from the
+library selector window. 
+An element is composed of lines and arcs on the silk-screen
+layer (used to define the package outline), pins
+(or pads for SMD) and three labels that define the
+description, the element's layout-name (which also
+appears on the silk-screen layer) and its value. You
+can choose which of the names are displayed on the screen
+with the <b>Screen</b> menu; however, the silk screen in
+the printout will always show the layout-name. 
+Element pins are contained on the first logical level
+and so reside on all layers, but the pads of surface-mount
+elements reside on only the component or solder
+layers. An element can have a mixture of pins, pads
+(on one or both sides), and mounting holes.
+
+   <p>A mark is used to position the element with
+respect to the cross hair during pasting. 
+The mark will lie on a grid point when the element
+is positioned.  The mark is drawn as a small diamond
+shape, but is only visible when <em>both</em> the <code>silk</code>
+and <code>pins/pads</code> layers are visible. 
+All parts of an element are treated as one unit, except for
+the name. 
+It is not possible to delete a single pin or move
+only part of an element on the layout. 
+You can resize separate pieces of an element,
+but doing so is usually a bad idea. You can move/rotate
+the element name independently of the element it belongs
+to. When you move an element name, a line is draw from
+the cursor to the element mark so it is easy to tell
+which element the name belongs to.
+
+   <p>Each pin and pad has two string identifiers, one is the
+"name" which is a functional description of the pin
+(<em>e.g.</em> "clock in") and the other is the "number" of the
+pin which is used to identify it in a netlist. The "number"
+is usually an integer, but it can be any string. You
+can edit the "name" of each pin of an element, but the
+"number" is embedded in the element definition and is
+determined when the new element is first created. 
+Pads are similar to lines on a layer but they must be oriented
+either vertically or horizontally. 
+Pads can have either rounded or square ends. Pins
+can be round, square, or octagonal.
+
+   <p>Elements are supported by several special layers: <code>silk</code>, <code>pins/pads</code> and
+<code>far-side</code>.  The <code>silk</code> layer shows the package outline and
+also holds legend text and element names. The <code>pins/pads</code> layer is used to toggle
+whether the element's pins and pads are displayed. The <code>far-side</code> layer controls visibility
+of objects (silkscreen and pads) that are on the far (<em>i.e.</em> not currently viewed) side
+of the board.
+
+   <p>The “oldlib” style of footprint libraries distributed with
+<code>pcb-rnd</code> rely upon the M4 macro processor.  M4 is typically
+installed under the name <code>m4</code> on most unix-like operating
+systems.  It is recommended that you use the GNU version of M4 to
+avoid limitations found in some vendor implementations.  See the m4
+man page on your system for more information. 
+Every element file is preprocessed by a user-defined command when the file is read. 
+For details see ‘<samp><span class="samp">elementCommand</span></samp>’, <a href="#Resources">Resources</a>. <code>m4</code>, the default
+value of ‘<samp><span class="samp">elementCommand</span></samp>’, allows you to create libraries for
+package definitions that are shared by all elements. 
+The old element libraries distributed with <code>pcb-rnd</code> expect <code>m4</code> or an
+equivalent to be the <em>elementCommand</em>. The new library scheme simply has
+each element stored in a self-contained file, so there is no need to learn
+<code>m4</code> to add to the libraries.
+
+   <p><code>pcb-rnd</code> can create a list of
+all connections from one (or all) elements to the others or a list of
+unconnected pins. 
+It can also verify the layout connections against a netlist file. 
+The element's ‘<samp><span class="samp">layout-name</span></samp>’ is the name used to identify the element
+in a netlist file (see <a href="#Netlist-File">Netlist File</a>).
+
+   <p>The old libraries, or very old (pre-1.6) layout files may have
+incorrect pin numbering since there was no concept of pin numbers
+when they were created. <code>pcb-rnd</code> uses the order of appearance of
+the pin definitions in the layout or library file if it uses the
+old format, but there is no guarantee that it will be correct for
+these old objects.
+
+   <p><a name="index-old-library-9"></a><a name="index-library-accuracy-10"></a><b>Be aware that a few of the old library parts may still be incorrectly
+implemented regarding pin-numbering.</b>  All of the DIL (Dual-
+Inline-Pins) parts are correct and most of the others are too,
+but you should verify the pin numbering
+of any non-DIL part before using an old library part. 
+(use the ‘<samp><span class="samp">generate object report</span></samp>’ in the <b>Info</b> menu
+to see what <code>pcb-rnd</code> thinks a pin's number is)
+All of the old
+library names begin with a ~, so you can easily identify them. 
+The old libraries also <em>may</em> contain other sorts of errors,
+including incorrect pin spacing, silkscreen overlapping solder areas, etc. 
+<b>Check carefully any element in the old library before using it!</b>
+As the new library grows, the old library will be pared down to
+at least remove all of the elements with errors, but this will
+take time.
+
+   <p>You can make your own element definitions graphically now. 
+Simply draw vias for the pins, lines on the solder and/or
+component layers for surface-mount pads (they must be either horizontal
+or vertical),
+and lines and arcs on the silkscreen layer for the silkscreen
+outline. You should <em>name</em> (<em>N</em> key) each via and copper line with the pin <em>number</em>. 
+Once you are happy with the geometry, select everything that is to become part of
+the element, then choose ‘<samp><span class="samp">convert selection to element</span></samp>’ from the <b>Select</b> menu. 
+Afterwords you can make pin (or pad) one
+square if you like, and give the element its various names. You can also give
+the pins and pads their functional names. Note that the
+element mark corresponds to the position you click after choosing the
+conversion from the menu, so decide where the mark goes and make
+sure it falls on a grid point before you request the conversion. 
+If the vias/lines are not named, then the pin numbering will correspond to the
+order in which they were placed.
+
+   <p>When you create a new element, remember that silkscreen lines
+should <em>never</em> overlap the copper part of the
+pins or pads, as this can interfere with soldering. The silkscreen
+should identify the maximum extent of the element package so it
+is easy to see how close elements can be placed together.
+
+   <p>If you want to make an element similar to an existing one, you can
+break an element into constituent pieces from the <b>Buffer</b> menu. 
+Paste the pieces to the layout, make the necessary changes, then
+convert it back into an element. If the pin numbers haven't changed,
+there is no need to name each via/line as they are pre-named when
+the element was broken apart. When you create a new element, you
+can save it to a file in order to have easy access to it the next
+time you run <code>pcb-rnd</code>.
+
+<div class="node">
+<a name="Layer-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Line-Objects">Line Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Element-Objects">Element Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.4 Layers</h3>
+
+<p><a name="index-layers_002c-an-overview-11"></a>
+Every layout consists of several layers that can be used independently
+or treated as a group. 
+Layer groups can be used to logically separate (and color-code)
+different traces (<em>e.g.</em> power and signal); however, all
+layers within a group reside on the same physical
+copper layer of a board, so using different layers within the same
+group won't provide electrical separation where they touch or overlap. 
+For details, see ‘<samp><span class="samp">layerGroups</span></samp>’, <a href="#Resources">Resources</a>. 
+Each layer is drawn in a color defined in the resource file
+and identified by a name that you can change (for details
+see ‘<samp><span class="samp">layerColor</span></samp>’, <a href="#Resources">Resources</a>.) 
+Layers are really just containers for line, arc, polygon, and text objects.  The
+component and solder layers contain SMD elements as well, but the
+file structure doesn't reflect that fact directly.
+
+   <p><a name="index-layer-groups-12"></a>Each layer group
+represents a physical layer on the printed circuit board.  If you want to make
+a four layer board, you'll need to have at least four layer groups. 
+Connections between layer groups are established only through element pins and vias. 
+The relationship between a specific layer and the board itself is configurable from
+the ‘<samp><span class="samp">Edit layer groups</span></samp>’ option in the <b>Settings</b> menu. 
+The layer groups corresponding to the physical layers: <em>component-side</em>
+and <em>solder-side</em> are always defined and you must map at least one
+logical layer to each, even if you plan to make a single-sided board. 
+You are not obligated to put tracks on either of them. 
+Surface mount elements always reside on either the component-side or the
+solder-side layer group. When you paste an element from the buffer,
+it will go onto whichever side of the board you are viewing. 
+You can swap which side of the board you are viewing by pressing
+the <em>Tab</em> key, or by selecting ‘<samp><span class="samp">view solder side</span></samp>’ from the
+<b>Screen</b> menu. 
+The layer groups just have a name or number associated with them - where
+they are sandwiched in the board is left for you to tell the
+manufacturer.
+
+   <p>The silkscreen layer is special because there are actually two silkscreen
+layers, one for the top (component) and one for the bottom (solder) side
+of the board. Which silk layer you draw on is determined by the side of the
+board that you are viewing. If you are viewing the component side, then
+drawing on the silk layer draws to the component-side silk layer.
+
+   <p>The netlist layer is another special layer. It shows rat's-nest lines
+(<em>i.e.</em> guides that show how the netlist expects the element to interconnect). 
+If you make this the active layer, you can use the Line tool to add
+entries into the netlist, or to delete connections from the netlist
+window. Except for these two purposes, you should not
+make the netlist layer the active layer. Usually there is no need to
+do this because a separate schematic package should be used to create the
+netlist. <code>pcb-rnd</code> can automatically draw all of the rats from
+the netlist. In some cases you may want to make a small change without
+going to the trouble of modifying the schematic, which is why this
+facility is provided.
+
+<div class="node">
+<a name="Line-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Arc-Objects">Arc Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Layer-Objects">Layer Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.5 Lines</h3>
+
+<p><a name="index-lines_002c-an-overview-13"></a>
+Lines are used to draw tracks on the pc board. 
+When in the line mode, each <em>Btn1</em>
+press establishes one end of a line. 
+Once the second point is defined, the line is drawn
+and a new line started where the first one ended. 
+You can abandon the new starting point in favor
+of another by pressing <em>Ctrl-Btn1</em>, or
+<em>Btn3</em>, but don't use <em>Btn2</em>. 
+The undo function (<em>U</em> key or ‘<samp><span class="samp">undo last operation</span></samp>’
+from the <b>Edit</b> menu) will take you back
+point by point if you use it while in the line mode.
+
+   <p><a name="index-two-line-mode-14"></a>New lines can be restricted to 45 degree angles if desired. You can toggle this
+restriction on and off while creating lines by pressing the <em>period</em> key. 
+If the 45 degree restriction is turned on, then the <em>/</em> (forward slash) key
+can be used to cycle through three different modes of 45 degree line creation. 
+One mode just creates a single line forced to the nearest 45 degree vector.  The next
+mode creates two lines from the start to end points such that the first line leaves the
+start point at a 90 degree vector, and the second line enters the end point on a 45
+degree vector. The last mode creates two lines such that the first line leaves the
+start point on a 45 degree vector and arrives at the end point on a 90 degree vector. 
+You can temporarily swap between the last two modes by holding the <em>Shift</em> key down.
+
+   <p>It is simple to edit a line object by breaking it into pieces (insert point mode),
+moving an end point or the whole line (<em>Arrow tool</em>),
+or changing the layer it resides on (<em>M</em> key moves the line under the pointer
+to the active layer). 
+In the case when two line segments meet at exactly the same
+point you can delete the intermediate point, otherwise the delete tool removes an entire line. 
+Feel free to experiment
+since <code>pcb-rnd</code> will allow you to undo and redo anything that materially affects your work. 
+If you switch active layers in the midst of placing lines a via will automatically be
+placed, when necessary, in order to continue the connection.
+
+   <p><a name="index-clearance-15"></a>If you draw a line inside a polygon, it will either plow through the
+polygon creating a clearance, or touch the polygon. This behavior is
+selectable in the <b>Settings</b> menu for new lines. To change the
+behavior of an existing line, hit the <em>J</em> key with the cross hair
+over the line. You can increase the size of the clearance by 2 mils on
+each edge with the with the
+<em>K</em> key. <em>Shift-K</em> will decrease the clearance by 2 mils. 
+The increment may be changed from 2 mils through the application
+resource file. 
+The clearance can be also increased, decreased and set
+by the <em>ChangeClearSize</em> action.
+
+   <p>Lines do not need to intersect the center of a pin, pad, via, or other
+line for <code>pcb-rnd</code> to understand that they make electrical connection. 
+If the connection is too tenuous, running the design rule checker will report
+that the connection may break if the line width shrinks slightly.
+
+<div class="node">
+<a name="Arc-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Polygon-Objects">Polygon Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Line-Objects">Line Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.6 Arcs</h3>
+
+<p><a name="index-arc-16"></a>
+<code>pcb-rnd</code> can handle arcs of any angular extent, but when you
+create an arc with the Arc tool, it will
+be a quarter circle (this means they always bend a right angle).  Arcs are very similar
+to lines otherwise.  They are created on the active layer and have the same thickness
+that new lines will have.  The various clicks for creating lines work pretty much the
+same way for creating arcs. 
+In order to make the arc curve in the desired direction, drag the mouse along
+the tangent line from the starting position towards the end position. If the grid is
+too coarse, it may not be possible to distinguish whether you've moved over then up,
+or up then over, so if you can't seem to make the arc go in the direction you want, try pressing
+the <em>Shift</em> key while drawing the arc. Decreasing the grid spacing may also help. 
+Alternatively you can draw the wrong arc, then
+rotate and move it where you want. Like the Line tool, after an arc is drawn a new
+starting point is established at the end point.
+
+   <p>Whenever a starting point is established
+by either the Line or Arc tools it will be retained if you switch directly between the
+tools (e.g. <em>F2</em> key for Lines, <em>F8</em> key for Arcs. Arcs can either touch or
+clear polygons just like lines do. Of course connection
+searches, undo and all the other features you'd expect work with arcs too.
+
+<div class="node">
+<a name="Polygon-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Text-Objects">Text Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Arc-Objects">Arc Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.7 Polygons</h3>
+
+<p><a name="index-polygon_002c-an-overview-17"></a>
+Sometimes it's useful to fill large areas with solid copper. 
+The way to do this is with polygons. 
+Polygons can be created in either the polygon mode or the rectangle mode. 
+In the polygon mode, you'll have to define each corner of the polygon
+with a mouse click (<em>Btn1</em>). When the last point is clicked
+exactly on top of the starting point, the polygon is finished. 
+Since this can be hard to do, the <em>Shift-P</em> key will enter the
+final point for you, closing the polygon. 
+If the 45 degree angle restriction is turned on
+and you try to close the polygon when it is not possible, you'll get a
+warning instead. If you haven't finished entering a polygon, but want to
+undo one (or more) of the points that you've already defined, use the
+undo command (<em>U</em> key).
+
+   <p>With the rectangle tool, defining
+the two diagonally opposite corners is sufficient, but of course the resulting
+polygon is a rectangle. 
+Like lines, a polygon can by edited by deleting, inserting and moving the points
+that define it. Pins and vias <em>always</em> clear through polygons without
+touching them when first positioned. You must add a thermal with the thermal
+tool in order to connect pins and vias to polygons. Thermals can be added and removed by
+clicking <em>Btn1</em> with the thermal tool over the pin or via. 
+The thermal tool always
+places a thermal to polygons on the active layer, so if the tool doesn't
+seem to work, it's probably because the polygon you want to touch is not
+on the active layer.  You can change the style of thermal used or make
+a solid connection by holding down <em>Shift</em> while clicking
+<em>Btn1</em> with the thermal tool over the pin or via.
+
+   <p><code>pcb-rnd</code> is capable of handling complex polygons, but
+using a number of simpler ones improves performance of the connection tracing code. 
+You also must be careful not to create polygons that touch or overlap themselves. 
+The fabricated board may not look the way you expect if you violate this
+principle. It is always ok to have two (or more) polygons touch or overlap
+each other, but not for points within the same polygon to do so.
+
+   <p>The great advantage to this new polygon behavior is that simple or complex ground
+and/or power planes can be easily made with polygons and seen on the screen. 
+If you don't want this auto-clearance behavior, or you load a layout created by
+an early version of <code>pcb-rnd</code>, the old behavior
+(shorts to all piercing pins and vias) is available.  A ‘<samp><span class="samp">ChangeSize</span></samp>’
+operation (<em>S</em> key) toggles a polygon between the new and old polygon/pin
+behavior.
+
+<div class="node">
+<a name="Text-Objects"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Net-Objects">Net Objects</a>,
+Previous: <a rel="previous" accesskey="p" href="#Polygon-Objects">Polygon Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.8 Text</h3>
+
+<p><a name="index-text_002c-an-overview-18"></a><a name="index-strings_002c-an-overview-19"></a>
+Text objects should be used to label a layout or to put additional
+information on the board. Elements have their ‘<samp><span class="samp">layout-name</span></samp>’ labels on the
+silk-screen layer. If you are making a board without a silkscreen,
+you can use copper text to label the elements, but you have to do
+this manually.
+
+   <p>Text is always horizontal when first created, but the
+rotate mode can align it along 0, 90, 180 and 270 degree angles. 
+Text on the far side of the board will automatically appear mirror-imaged.
+
+   <p><em>Warning:</em> <b>TEXT OBJECTS ON A COPPER LAYER CREATE COPPER LINES BUT THEY ARE NOT SCANNED FOR
+CONNECTIONS OR TESTED FOR CREATING SHORTS VS. THE NETLIST. NEITHER ARE TEXT OBJECTS TESTED AGAINST ANY DESIGN RULES</b>.
+
+<div class="node">
+<a name="Net-Objects"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Text-Objects">Text Objects</a>,
+Up: <a rel="up" accesskey="u" href="#Intro">Intro</a>
+
+</div>
+
+<h3 class="section">2.9 Nets</h3>
+
+<p><a name="index-rats_002dnest-20"></a><a name="index-netlist-21"></a>
+Layout files also contain the netlist that describes how the elements
+are supposed to be interconnected. This list of connections can be
+loaded from a netlist file (see <a href="#Netlist-File">Netlist File</a>), or
+entered by drawing rat-lines as described
+previously. Each net has a name and routing style associated with it. 
+The net contains a list of all element <em>layout-name</em> names and
+pin <em>numbers</em> that should
+be connected to the net. Loading a netlist file will replace all
+existing nets with the ones from the file. 
+The <em>Netlist</em> window provides an easy way to
+browse through the net list. You can display the rat's-nest by selecting
+‘<samp><span class="samp">optimize rats-nest</span></samp>’ from the <b>Connects</b> menu. If you move or rotate elements,
+the rat's-nest will automatically follow the movements, but they won't
+necessarily show the shortest paths until you optimize them again.
+
+<!--  chapter 2 - -->
+<div class="node">
+<a name="Getting-Started"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#User-Commands">User Commands</a>,
+Previous: <a rel="previous" accesskey="p" href="#Intro">Intro</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">3 Getting Started</h2>
+
+<p><a name="index-how-to-start-22"></a>
+The goal of this chapter is to give you enough information to learn how
+<code>pcb-rnd</code> works and how to develop your layouts to make the best use of <code>pcb-rnd</code>'s
+features. All event translations (<em>i.e.</em> the buttons and keys you
+press) refer to the default application resource file shipped with <code>pcb-rnd</code>. 
+There is probably no need to change this unless your window
+manager uses some of the button events itself; however, if you <em>want</em>
+to customize the behavior of <code>pcb-rnd</code> then changing the resource file
+is usually the best way to do it.
+
+   <p>Get yourself a printout of this chapter and <em>User Commands</em>, if you
+haven't already done so, and follow the examples.
+
+   <p>Start <code>pcb-rnd</code> (the actual command will use all lower-case letters)
+without any additional options. 
+
+For error messages on startup, see <a href="#problems">problems</a>. 
+<p>
+
+<ul class="menu">
+<li><a accesskey="1" href="#Application-Window">Application Window</a>:      The elements of the main window. 
+<li><a accesskey="2" href="#Log-Window">Log Window</a>:              The optional logging window
+<li><a accesskey="3" href="#Library-Window">Library Window</a>:          The circuit selection window
+<li><a accesskey="4" href="#Netlist-Window">Netlist Window</a>:          The desired connections window
+<li><a accesskey="5" href="#Drawing-and-Removing">Drawing and Removing</a>
+<li><a accesskey="6" href="#Moving-and-Copying">Moving and Copying</a>
+<li><a accesskey="7" href="#Loading-and-Saving">Loading and Saving</a>
+<li><a accesskey="8" href="#Printing">Printing</a>:                Creating Gerber files or postscript files
+<li><a accesskey="9" href="#Exporting">Exporting</a>:               Exporting a layout. 
+<li><a href="#Arrow-Tool">Arrow Tool</a>:              Selecting/Moving objects. 
+<li><a href="#Rats-Nest">Rats Nest</a>: 		   Helps you place and route tracks against a netlist. 
+<li><a href="#Design-Rule-Checking">Design Rule Checking</a>:    Check for manufactureability
+<li><a href="#Trace-Optimizer">Trace Optimizer</a>:         Optimization of layouts
+<li><a href="#Searching-for-elements">Searching for elements</a>:  Searching for elements
+<li><a href="#Measuring-distances">Measuring distances</a>:     Measuring distances
+<li><a href="#Vendor-drill-mapping">Vendor drill mapping</a>:    Mapping drills to a vendor specified list
+<li><a href="#Connection-Lists">Connection Lists</a>:        How to get a list of all or some connections. 
+</ul>
+
+<div class="node">
+<a name="Application-Window"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Log-Window">Log Window</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.1 The Application Window</h3>
+
+<p>The main window consists of five areas: the menu bar at the top, the toolbar
+below the menu bar, the layer controls along the left, the layout area to the
+right of the layer controls, and the status line at the bottom of the window.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Menu">Menu Bar</a>:                                Use a menu
+<li><a accesskey="2" href="#Tool-Selectors">Tool Selectors</a>:                Select a layout tool.
+<li><a accesskey="3" href="#Layer-Controls">Layer Controls</a>:                Switch layers on/off; change current one.
+<li><a accesskey="4" href="#Layout-Area">Layout Area</a>: 		         Where the layout is drawn. 
+<li><a accesskey="5" href="#Status_002dline-and-Input_002dfield">Status-line and Input-field</a>:   Reporting the current configuration.
+</ul>
+
+<div class="node">
+<a name="Menu"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Tool-Selectors">Tool Selectors</a>,
+Up: <a rel="up" accesskey="u" href="#Application-Window">Application Window</a>
+
+</div>
+
+<h4 class="subsection">3.1.1 Menus</h4>
+
+<p><a name="index-menus-23"></a><a name="index-popping-up-menus-24"></a>
+The menus are located at the top of the Layout area. Most, but not all,
+of their functions are also available from the keyboard. Similarly, some
+functions are only achievable through the keyboard or command entry. 
+Some menu entries such as ‘<samp><span class="samp">center layout</span></samp>’ in the <b>Screen</b> menu require a certain cross hair position. 
+In this case a prompt message will popup at the bottom of the screen
+with wording similar to the following:
+<pre class="example">     move pointer to the appropriate screen position and press a button
+</pre>
+   <p>Any mouse button will do the job, whereas any key except the arrow (cursor) keys
+will cancel the operation. If it seems like the menu hasn't done what you
+expected, check to see if it is waiting for the position click. For details see <a href="#Actions">Actions</a>.
+
+   <p>Pressing <em>Btn3</em> in the Layout area also pops up a menu with many of the most common operations (except
+when you're in the midst of drawing a line or arc). When
+a choice in the <em>Btn3</em> popup menu needs a cross hair position, it uses the position
+where the cross hair was when <em>Btn3</em> was pressed. For example, to get detailed
+information on an object, place the cross hair over the object, press <em>Btn3</em>, then
+choose ‘<samp><span class="samp">object report</span></samp>’.  If you pop up the <em>Btn3</em> menu but don't want to
+take any of the actions, click on one of the headers in the menu.
+
+     
+<a name="index-File_002c-popup-menu-25"></a>
+<dl><dt><b>File</b><dd>This menu offers a choice of loading, saving and printing data, saving
+connection information to a file or quitting the application. Most
+of the entries in the <b>File</b> menu are self explanatory. 
+Selecting
+‘<samp><span class="samp">print layout</span></samp>’ pops up a printer control dialog. 
+A selection of several device drivers is available from the printer control
+dialog. Presently <em>PostScript</em>, <em>encapsulated PostScript</em>,
+and <em>GerberX</em> are supported. The <em>GerberX</em> driver produces
+all of the files necessary to have the board professionally manufactured. 
+The connection saving features in the <b>File</b> menu produce outputs in an
+arcane format that is not too useful. They do <em>not</em> produce netlist
+files.
+
+<p><a name="index-Edit_002c-popup-menu-26"></a>
+<a name="index-undo-27"></a>
+<a name="index-redo-28"></a><br><dt><b>Edit</b>
+
+<dd>The <b>Edit</b> menu
+    provides the usual cut, copy, paste which work on selections. To learn how
+    to create complex selections, see <a href="#Arrow-Tool">Arrow Tool</a>.
+    The <b>Edit</b> menu also provides access to Undo and Redo of the last
+    operation. These can also be accomplished with the <em>U</em> key and
+    <em>Shift-R</em> key.  Finally, the <b>Edit</b> menu allows you to change
+    the names of: the layout, the active layer, or text objects on the layout.
+
+     <p><a name="index-Screen_002c-popup-menu-29"></a><a name="index-displaying-element-names-30"></a>
+     <a name="index-element_002c-display-names-of-31"></a><a name="index-grid_002c-display-32"></a>
+     <a name="index-grid_002c-alignment-33"></a>
+     <a name="index-zoom_002c-setting-34"></a>
+     <br>
+
+     <p><a name="index-Settings_002c-popup-menu-37"></a><a name="index-unique-names-38"></a><a name="index-rubber-band-39"></a><a name="index-snap-to-pins-40"></a><a name="index-clearance_002c-for-new-lines-41"></a><br><dt><b>Settings</b><dd>The <b>Settings</b> menu controls several operating configuration
+parameters. The ‘<samp><span class="samp">edit layer groups</span></samp>’ entry brings up a dialog
+that allows you to change the way layers are grouped. Layer grouping
+is described in <a href="#Layer-Objects">Layer Objects</a>. The ‘<samp><span class="samp">all-direction lines</span></samp>’
+entry controls
+the clipping of lines to 45-degree angles. You can also control
+whether moving individual objects causes the attached lines to
+"rubber band" with the move or not from the <b>Settings</b> menu. Another
+entry controls whether the starting clip angle for the two-line
+mode (see <a href="#Line-Objects">Line Objects</a>) alternates every other line. You can
+also control whether element names must be unique from the <b>Settings</b>
+menu.  When unique element names are enforced, copying a new element
+will automatically create a unique ‘<samp><span class="samp">layout-name</span></samp>’ name for it
+provided that the name originally ended with a digit (<em>e.g.</em>
+U7 or R6). The <b>Settings</b> menu allows you to control
+whether the cross hair will snap to pins and pads even when they
+are off-grid. Finally you can control whether new lines and
+arcs touch or clear intersecting polygons from this menu.
+
+     <p><a name="index-Select_002c-popup-menu-42"></a><a name="index-selected-objects_002c-removing-43"></a><a name="index-selected-objects_002c-changing-sizes-44"></a><br><dt><b>Select</b><dd>This menu covers most of the operations that work with selected objects. 
+You may either (un)select all visible objects on a layout or only the ones
+which have been found by the last connection scan see
+<!-- DRM: not sure where this was suppose to xfef to. -->
+<!-- @ref{find connections} -->
+. 
+You can delete all selected objects from this menu. 
+Other entries in the <b>Select</b> menu change the sizes of selected objects. 
+Note that a select action only affects those objects that are
+selected <em>and</em> have their visibility turned on in the Layer Control
+panel. The <b>Select</b> menu also provides a means for selecting objects
+by name using unix <a href="#Regular-Expressions">Regular Expressions</a>.
+
+     <p><a name="index-Buffer_002c-popup-menu-45"></a><a name="index-pastebuffer_002c-popup-menu-46"></a><a name="index-element_002c-editing-47"></a><br><dt><b>Buffer</b><dd>From the <b>Buffer</b> menu you may select one out of five
+buffers to use, rotate or clear its contents or save the buffer contents
+to a file. You can also use the ‘<samp><span class="samp">break buffer element to pieces</span></samp>’ entry
+to de-compose an element into pieces for editing. 
+Note: only objects with visibility turned on are pasted to the layout. If
+you have something in a buffer, then change which side of the board you
+are viewing, the contents of the buffer will automatically be mirrored
+for pasting on the side you are viewing. It is not necessary to clear
+a buffer before cutting or copying something into it - it will automatically
+be cleared first.
+
+     <p><a name="index-Connects_002c-popup-menu-48"></a><a name="index-auto_002drouter-49"></a><a name="index-design-rule-checker_002c-invoking-50"></a><br><dt><b>Connects</b><dd>The entries available through the <b>Connects</b> menu allow you to find
+connections from objects and to manipulate these. 
+You can also optimize or erase rat's nests from this menu. Finally,
+the ‘<samp><span class="samp">auto-route all rats</span></samp>’ entry allows you to auto-route
+all connections show by the rat's nest. The auto-router will use
+any visible copper layer for routing, so turn off the visibility of any
+layers you don't want it to use. The auto-router will automatically
+understand and avoid any traces that are already on the board, but
+it is not restricted to the grid. Finally,
+the auto-router routes using the active sizes (except for nets that
+have a route-style defined). <code>pcb-rnd</code> always knows which tracks
+were routed by the auto-router, and you can selectively remove them
+without fear of changing tracks that you have manually routed
+with the ‘<samp><span class="samp">rip-up all auto-routed tracks</span></samp>’ entry in the <b>Connects</b>
+menu.  The ‘<samp><span class="samp">design rule checker</span></samp>’ entry runs a check for copper
+areas that are too close together, or connections that touch too
+tenuously for reliable production. The DRC stops when the first
+problem is encountered so after fixing a problem be sure to
+run it again until no problems are found.
+     <pre class="display">          <em>Warning:</em> <b>COPPER TEXT IS IGNORED BY THE DRC CHECKER</b>. </pre>
+
+     <p><a name="index-Plugins_002c-popup-menu-55"></a><br><dt><b>Plugins</b><dd>The <b>Plugins</b> allows the user to
+		 manage <code>pcb-rnd's</code> plugins.
+
+     <p><a name="index-Info_002c-popup-menu-51"></a><a name="index-report-52"></a><a name="index-object-report-53"></a><a name="index-drill-report-54"></a><br><dt><b>Info</b><dd>The ‘<samp><span class="samp">generate object report</span></samp>’ entry from the <b>Info</b> menu
+provides a way to get detailed information
+about an object, such as its coordinates, dimensions, etc. 
+You can also get a report summarizing all of the drills
+used on the board with ‘<samp><span class="samp">generate drill summary</span></samp>’. Lastly,
+you can get a list of all pins, pads and vias that were
+found during a connection search.
+
+     <p><a name="index-Window_002c-popup-menu-55"></a><br><dt><b>Window</b><dd>The <b>Window</b> menu provides a way to bring each of <code>pcb-rnd's</code>
+windows to the front. The <em>Library</em> window is used to
+bring elements from the library into the paste-buffer. The
+<em>Message Log</em> window holds the various messages that
+<code>pcb-rnd</code> sends to the user. The <em>Netlist</em> window shows
+the list of connections desired.
+
+   </dl>
+
+   <p>Now that you're familiar with the various menus, it's time
+to try some things out. From the <b>File</b> menu choose
+‘<samp><span class="samp">load layout</span></samp>’, navigate to the tutorial folder, then
+load the file ‘<samp><span class="samp">tut1.pcb</span></samp>’.
+
+<div class="node">
+<a name="Tool-Selectors"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Layer-Controls">Layer Controls</a>,
+Previous: <a rel="previous" accesskey="p" href="#Menu">Menu</a>,
+Up: <a rel="up" accesskey="u" href="#Application-Window">Application Window</a>
+
+</div>
+
+<h4 class="subsection">3.1.2 The Tool Selectors</h4>
+
+<p><a name="index-mode-selection-63"></a><a name="index-tool-selection-64"></a><a name="index-selecting-a-new-tool-65"></a>
+The tool selector buttons reside below the layer controls. 
+They are used to select which layout tool to use in the drawing
+area. Each tool performs its function when <em>Btn1</em> is pressed. 
+Every tool gives the cursor a unique shape that identifies it. 
+The tool selector buttons themselves are icons that illustrate their function. 
+Each layout tool can also be selected from the keyboard:
+<pre class="example">         <em>F1</em> key       Via tool
+         <em>F2</em> key       Line tool
+         <em>F3</em> key       Arc tool
+         <em>F4</em> key       Text tool
+         <em>F5</em> key       Rectangle tool
+         <em>F6</em> key       Polygon tool
+         <em>F7</em> key       Buffer tool
+         <em>F8</em> key       Delete tool
+         <em>F9</em> key       Rotate tool
+         <em>Insert</em> key   Insert-point tool
+         <em>F10</em> key      Thermal tool
+         <em>F11</em> key      Arrow tool
+         <em>F12</em> key      Lock tool
+</pre>
+   <p>Some of the tools are very simple, such as the Via tool.  Clicking
+<em>Btn1</em> with the Via tool creates a via at the cross hair position. 
+The via will have the diameter and drill sizes that are active,
+as shown in the status line. 
+The Buffer tool is similar.  With it, <em><Btn1></em> copies
+the contents of the active buffer to the layout, but only
+those parts that reside on visible layers are copied. 
+The Rotate tool allows you to rotate elements, arcs, and text objects
+90 degrees counter-clockwise with each click. Holding the <em>Shift</em>
+key down changes the Rotate tool to clockwise operation. 
+Anything including groups of objects
+can be rotated inside a buffer using the rotate buffer menu option.
+
+   <p>The Line tool is explained in detail in <a href="#Line-Objects">Line Objects</a>. Go read
+that section if you haven't already. 
+Activate the Line tool. Set the active layer to the solder layer. 
+Try drawing some lines. Use the <em>U</em> key to undo some of the
+lines you just created. Zoom in a bit closer with the <em>Z</em> key. 
+Draw some more lines. Be sure to draw some separate lines by starting
+a new anchor point with <em>Ctrl-Btn1</em>. Change the ‘<samp><span class="samp">crosshair snaps to pin/pads</span></samp>’
+behavior in the <b>Settings</b> menu. Now draw a line. Notice that
+the new line points must now always be on a grid point. It might not
+be able to reach some pins or pads with this setting. Increase the active line thickness
+by pressing the <em>L</em> key. Note that the status line updates
+to reflect the new active line thickness. Now draw another line. Before completing the
+next line, make the component layer active by pressing the <em>4</em> key. 
+Now finish the line. Notice that a via was automatically placed where
+you switched layers. <code>pcb-rnd</code> does not do any checks to make sure that
+the via could safely be placed there. Neither does it interfere with
+your desire to place lines haphazardly. It is up to you to place
+things properly when doing manual routing with the Line tool.
+
+   <p>The Arc tool is explained in detail in <a href="#Arc-Objects">Arc Objects</a>. Its
+use is very similar to the Line tool.
+
+   <p>The Rectangle tool, Polygon tool and Thermal tool are explained in detail in
+<a href="#Polygon-Objects">Polygon Objects</a>. Go read that section. 
+Remember that the Thermal tool will only create and destroy thermals
+to polygons on the active layer. Use the Rectangle tool to make a
+small copper plane on the component layer. Now place a via in the
+middle of the plane. Notice that it does not touch the plane, and
+they are not electrically connected. Use the Thermal tool to make
+the via connect to the plane. Thermals allow the via or pin to
+be heated by a soldering iron without having to heat the entire
+plane. If solid connections were made to the plane, it could be
+nearly impossible to solder. Shift-click on the via with the Thermal
+tool to change the style of thermal used or to make the connection
+solid.  Click on the via again with the Thermal tool to remove the
+connection to the plane.
+
+   <p>The Insert-point tool is an editing tool that allows you to add
+points into lines and polygons.  The
+Insert-point tool enforces the 45 degree line
+rule.  You can force only the shorter line segment to 45
+degrees by holding the <em>Shift</em> key down while inserting the point. 
+Try adding a point into one of the lines you created. Since line
+clipping is turned on, you may need to move the cross hair quite far
+from the point where you first clicked on the line. Turn off the
+line clipping by selecting ‘<samp><span class="samp">all-direction lines</span></samp>’ from the
+<b>Settings</b> menu (or hit
+the <em>Period</em> key). Now you can place an inserted point anywhere. 
+Try adding a point to the rectangle you made earlier. Start by clicking
+somewhere along an edge of the rectangle, then move the pointer to
+a new location and click again.
+
+   <p>The delete-mode deletes the object beneath the cursor with each
+<em>Btn1</em> click. 
+If you click at an end-point that two lines have in common, it will replace the two lines with a single line
+spanning the two remaining points.  This can be used to delete an "inserted"
+point in a line, restoring the previous line.  Now delete one of the original corner
+points of the polygon you were just playing with. To do this, place the cross hair over the
+corner and click on it with the Delete tool. You could also use the <em>Backspace</em> key
+if some other tool is active. Try deleting some of
+the lines and intermediate points that you created earlier. Use undo
+repeatedly to undo all the changes that you've made. Use redo
+a few times to see what happens. Now add a new line. Notice that
+you can no longer use redo since the layout has changed since
+the last undo happened. The undo/redo tree is always pruned in this
+way (<em>i.e.</em> it has a root, but no branches).
+
+   <p>The Arrow tool is so important, it has its own section: <a href="#Arrow-Tool">Arrow Tool</a>. 
+Go read it now.
+
+   <p>The Lock tool allows you to lock objects on the layout. When an object
+is locked, it can't be selected, moved, rotated, or resized. This is
+useful for very large objects like ground planes, or board-outlines that
+are defined as an element. With such large objects, nearly anywhere you
+click with the Arrow tool will be on the large object, so it could be
+hard to draw box selections. If you lock an object, the Arrow tool will
+behave as if it didn't exist.  You cannot unlock an object with undo. 
+You must click on it again with the Lock tool. If an object is locked,
+previous changes to it cannot be undone either. When you lock
+an object, a report message about it is popped up and will always tell
+you what object it is, and that it is locked if you just locked it. 
+Other than noticing your inability to manipulate something, the only
+way to tell an object is locked is with a report from the <b>Info</b>
+menu. Use the Lock tool sparingly.
+
+
+<div class="node">
+<a name="Layer-Controls"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Layout-Area">Layout Area</a>,
+Previous: <a rel="previous" accesskey="p" href="#Layer-Controls">Layer Controls</a>,
+Up: <a rel="up" accesskey="u" href="#Application-Window">Application Window</a>
+
+</div>
+
+<h4 class="subsection">3.1.3 The Layer Controls</h4>
+
+<p><a name="index-layer-controls-59"></a><a name="index-layers_002c-switching-on_002foff-60"></a><a name="index-layers_002c-changing-which-is-active-61"></a><a name="index-change-active-layer-62"></a>
+The layer control panel, located in the upper left, is used to turn on
+and off the display of layer groups and to select the active drawing layer. 
+If a layer hasn't been named, the label "<em>(unknown)</em>" is used as the default. 
+If this happens, it probably means the application resources are not installed
+properly.
+
+   <p>The upper buttons are used to switch layers on and off. Click
+<em><Btn1></em> on one or more of them. Each click toggles the setting. 
+If you turn off the currently active layer, another one that is visible
+will become active. If there are no others visible, you will not be
+able to turn off the active layer. 
+When the layers are grouped, clicking on these buttons will toggle
+the visibility of all layers in the same group. This is a good idea because
+layers in the same group reside on the same physical layer of
+the actual board. Notice that this example has 2 groups each having
+3 layers, plus two other layers named ‘<samp><span class="samp">unused</span></samp>’. 
+Use the ‘<samp><span class="samp">Edit layer groups</span></samp>’ option in the ‘<samp><span class="samp">Settings</span></samp>’ menu to
+change the layer groupings in the lesstif GUI or the ‘<samp><span class="samp">Preferences</span></samp>’
+dialog from the ‘<samp><span class="samp">File</span></samp>’ menu in the GTK+ GUI. Note that changing the
+groupings can radically alter the connectivity on the board. 
+Grouping layers is only useful for helping you to color-code
+signals in your layout. Note that grouping layers actually reduces the number
+of different physical layers available for your board, so to make an eight
+layer board, you cannot group any layers.
+
+   <p>The <em>far side</em> button turns on and off the visibility of elements
+(including SMD pads) on the opposite (to the side you're viewing)
+board side, as well as silk screening on that side. It does not
+hide the x-ray view of the other copper layers, these must be turned off
+separately if desired.  Use the <em>tab</em> key to view the entire board from the other
+side.  To see a view of what the back side of the board will actually look like,
+make the solder layer the active layer then press <em>tab</em> until the status
+line says "solder" on the right, then turn off the visibility of all layers
+except solder, pins/pads, vias, and silk. Now turn them all back on.
+
+   <p>The lowest button, named <em>active</em>, is used to change the active
+drawing layer. Pressing <em><Btn1></em> on it pops up a menu to select which
+layer should be active. 
+Each entry is labeled with the layer's name and drawn in its color. 
+The active layer is automatically made visible. The active layer is
+always drawn on top of the other layers, so the ordering of layers
+on the screen does not generally reflect the ordering of the manufactured
+board. Only the solder, component, silkscreen, and solder-mask layers
+are always drawn in their physical order. Bringing the active layer
+to the top makes it easier to select and change objects on the active layer. 
+Try changing the active layer's name to <em>ABC</em> by selecting
+‘<samp><span class="samp">edit name of active layer</span></samp>’ from the ‘<samp><span class="samp">Edit</span></samp>’ menu. 
+Changing the active layer can also be done by pressing keys
+<em>1..MAX_LAYER</em>.
+
+   <p>Turn off the visibility of the component layer. 
+Now make the component layer the active layer. Notice that it
+automatically became visible. Try setting a few
+other layers as the active layer. You should also experiment
+with turning on and off each of the layers to see what happens.
+
+   <p>The netlist layer is a special layer for adding connections to
+the netlist by drawing rat lines. This is not the recommended
+way to add to the netlist, but occasionally may be convenient. 
+To learn how to use the netlist layer see <a href="#Net-Objects">Net Objects</a>.
+
+
+
+<div class="node">
+<a name="Layout-Area"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Status-line-and-Input-field">Status-line and Input-field</a>,
+Previous: <a rel="previous" accesskey="p" href="#Layer-Controls">Layer Controls</a>,
+Up: <a rel="up" accesskey="u" href="#Application-Window">Application Window</a>
+
+</div>
+
+<h4 class="subsection">3.1.4 Layout Area</h4>
+
+<p><a name="index-grid-66"></a>
+The layout area is where you see the layout. The cursor shape depends
+on the active tool when the pointer is moved into the layout area. 
+A cross hair follows the mouse pointer with respect to the grid setting. 
+Select a new grid from the <em>Screen</em> menu. 
+The new value is updated in the status line. 
+A different way to change the grid is
+<em>Shift<Key>g</em> to decrease or <em><Key>g</em> to increase
+it, but this only works for English (integer mil) grids. 
+The grid setting is saved along with the data when you save a pcb layout. 
+For homemade layouts a value around 50 is a good setting. 
+The cursor can also be moved in the layout area with the cursor (arrow) keys or, for larger
+distances, by pressing the <em>Shift</em> modifier together with a cursor key.
+
+<div class="node">
+<a name="Status-line-and-Input-field"></a>
+<a name="Status_002dline-and-Input_002dfield"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Layout-Area">Layout Area</a>,
+Up: <a rel="up" accesskey="u" href="#Application-Window">Application Window</a>
+</div>
+
+<h4 class="subsection">3.1.5 The Status-line and Input-field</h4>
+
+<p><a name="index-status-information-56"></a><a name="index-displaying-status-information-57"></a><a name="index-input_002dfield_002c-position-of-58"></a>
+The status-line is located at the bottom edge of the main window. 
+During normal operation the status information is visible there. 
+When a selected menu operation requires an additional button click, the
+status-line is replaced by a message telling you to position the cursor
+and click. 
+When a text input is required, the status-line is replaced by the
+Input-field which has a prompt for typing the input.
+
+   <p>The status-line shows, from left to right, the side of the board that you
+are viewing (<em>Tab</em> key changes this), the current grid values,
+if new lines are restricted to 45 degrees,
+which type of 45 degree line mode is active, whether rubberband move and
+rotate mode is on (R), and the zoom factor. 
+This information is followed by the active line-width, via-size
+and drilling hole, keepaway spacing, and text scaling. Last is the active buffer number and the
+name of the layout. An asterisk appearing at the far left indicates that the
+layout has been modified since the last save. 
+Note that the name of the layout is not the same
+thing as the filename of the layout. 
+Change the grid factor to 1.0 mm from the <b>Screen</b> menu. Observe how the
+status line shows the new grid setting. Except for the case of the metric
+grid, all dimensions in the status line are in units of 0.001 inch (1 mil).
+
+   <p>The input-field pops up (temporarily replacing the status-line) whenever user input
+is required. Two keys are bound to the input field: the <em>Escape</em> key
+aborts the input, <em>Return</em> accepts it. Let's change the name of a component
+on the board to see how the input-field works. Position the cross hair over
+R5, and press the <em>N</em> key. The input field pops-up showing the name
+for you to edit. Go ahead and change the name, then hit return. Notice the name
+of the element changed. Now undo the change by pressing the <em>U</em> key. You can
+position the cross hair over the name, or the element before pressing the
+<em>N</em> key.
+
+   <p>Now select ‘<samp><span class="samp">realign grid</span></samp>’ from the <b>Screen</b> menu. Notice that the
+status line has been replaced with an instruction to position the cursor
+where you want a grid point to fall. In this case, since the cross hair
+can only fall on a grid point, you must move the tip of the finger cursor
+to the place where you want a grid point to appear. Do not worry that
+the cross hair is not coincident with the cursor. Click <em>Btn1</em> at
+your chosen location. See how the grid has shifted, and the status line
+has returned.
+
+   <p>The present cross hair position is displayed in the upper right corner of the window. 
+Normally this position is an absolute coordinate, but you can anchor a marker at
+the cross hair location by pressing <em>Ctrl-M</em> (try it now) and then the
+display will read both the absolute cross hair position as well as the difference
+between it and the marker. The numbers enclosed in < > are the X and Y distances
+between the cross hair and the mark, while the numbers enclosed in parenthesis
+are the distance and angle from the mark to the cross hair. The values displayed
+are always in units of 0.001 inch (1 mil). 
+Pressing <em>Ctrl-M</em> again turns the marker off.
+
+<div class="node">
+<a name="Log-Window"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Library-Window">Library Window</a>,
+Previous: <a rel="previous" accesskey="p" href="#Status_002dline-and-Input_002dfield">Status-line and Input-field</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+<h3 class="section">3.2 Log Window</h3>
+
+<p><a name="index-log-window-67"></a><a name="index-messages-68"></a>
+This optional window is used to display all kind of messages including
+the ones written to <em>stderr</em> by external commands. The main advantage
+of using it is
+that its contents are saved in a scrolling list until the
+program exits. Disabling this feature by setting the resource
+<em>useLogWindow</em> to <em>false</em> will generate popup windows to display
+messages. The <em>stderr</em> of external commands will appear on <code>pcb-rnd</code>s
+<u style="color:red;"><u><em>stderr</em> which normally is the parent shell. I suggest you iconify</u></u>
+the log window after startup for example by setting <em>*log.iconic</em> to
+<em>true</em> in the resource file. If <em>raiseLogWindow</em> is set <em>true</em>,
+the window will deiconify and raise itself whenever new messages are to be
+displayed.
+
+<div class="node">
+<a name="Library-Window"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Netlist-Window">Netlist Window</a>,
+Previous: <a rel="previous" accesskey="p" href="#Log-Window">Log Window</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.3 Library Window</h3>
+
+<p><a name="index-library-window-69"></a>
+The library window makes loading elements (or even partial layouts) easy. 
+Just click the appropriate library from the list on the left. A list
+of its elements then appears on the right. Select an element
+from the list by clicking on its description. Selecting an element from the
+library will also automatically copy the element into
+the active buffer, then invoke the <em>Buffer</em> tool so
+you can paste it to the layout. Elements in the old library should be
+taken with a grain of salt (<em>i.e.</em> check them carefully before
+using).  The old library names all begin with ~ so you can easily distinguish between
+the old and new libraries.  All of the elements in the new library
+should  be thoroughly vetted, so you
+can use them with confidence. The new libraries are stored simply
+as directories full of element files, so making additions to the
+new library is easy since there is no need to learn <code>m4</code>. 
+For details on the old libraries,
+check-out <a href="#Library-File">Library File</a> and <a href="#Library-Contents-File">Library Contents File</a>. For
+details on the format of an element file used for the new libraries,
+see <a href="#Element-File">Element File</a>.
+
+<div class="node">
+<a name="Netlist-Window"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Drawing-and-Removing">Drawing and Removing</a>,
+Previous: <a rel="previous" accesskey="p" href="#Library-Window">Library Window</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.4 Netlist Window</h3>
+
+<p><a name="index-Netlist-Window-70"></a>
+The netlist window is very similar to the library window. On the left
+is a list of all of the nets, on the right is the list of connections
+belonging to the chosen net. The chosen net is highlighted in the
+list and also shown on the second line of the window in red. If the
+net name has a star to the left of it then it is "disabled". A disabled
+net is treated as if it were not in the net list. This is useful, for
+example, if you plan to use a ground plane and don't want the ground
+net showing up in the rat's nest. You can enable/disable individual
+nets by double-clicking the net name. If you want to enable or disable
+all nets at once, there are two buttons at the top of the netlist
+window for this purpose.
+
+   <p>The button labeled ‘<samp><span class="samp">Sel Net On Layout</span></samp>’
+can be used to select (on the layout) everything that is connected
+(or is supposed to be connected) to the net. If you click on a
+connection in the connection list, it will select/deselect
+the corresponding pin or pad in the layout and also center the layout
+window where it is located. If you "Find" (‘<samp><span class="samp">lookup connection
+to object</span></samp>’ in the <b>Connects</b> menu [also <em>F</em> key]), a pin
+or pad it will also choose the net and connection in the netlist window
+if it exists in the netlist.
+
+   <p>If no netlist exists for the layout, then the netlist window does not
+appear. You can load a netlist from a file from the <b>File</b> menu. The
+format for netlist files is described in <a href="#Netlist-File">Netlist File</a>.
+
+<div class="node">
+<a name="Drawing-and-Removing"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Moving-and-Copying">Moving and Copying</a>,
+Previous: <a rel="previous" accesskey="p" href="#Netlist-Window">Netlist Window</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.5 Drawing and Removing Basic Objects</h3>
+
+<p><a name="index-drawing-objects-71"></a><a name="index-removing-objects-72"></a><a name="index-erasing-objects-73"></a><a name="index-object_002c-drawing-and-removing-74"></a>
+hace begging gutting here, and do a real-world tutorial example.
+
+   <p>There are several ways of creating new objects: you can draw them yourself,
+you can copy an existing object (or selection), or you can load an element from a file or
+from the Library window. Each type of object has a particular tool for creating it.
+
+   <p>The active tool can be selected from the tool selectors in the bottom
+left corner or by one of the function keys listed earlier in this chapter. 
+Each <em><Btn1></em> press with the tool tells the application to create
+or change the appropriate object or at least take
+the first step to do so. Each tools causes the cursor to take
+on a unique shape and also causes the corresponding
+tool selector button to be highlighted. You can use either cue
+to see which tool is active.
+
+   <p>Insert mode provides the capability of inserting new points into existing
+polygons or lines. The 45 degree line clipping is now enforced when selected. 
+Press and hold the shift key while positioning the new point to only clip
+the line segment to the nearer of the two existing points to 45 degrees. 
+You can also toggle the 45-degree clipping in the middle of a point
+insertion by pressing the <em><Key>.</em>
+If the shift key is not depressed and the 45 degree line clipping mode
+is on, both new line segments must be on 45 degree angles - greatly
+restricting where the new point may be placed. In some cases this can cause
+confusion as to whether an insertion has been started since the two new
+lines may be forced to lie parallel on top of the original line until the
+pointer is moved far from the end points.
+
+   <p>Removing objects, changing their size or moving them only applies to objects
+that are visible when the command is executed.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Common">Common</a>:            Keystrokes common to some objects. 
+<li><a accesskey="2" href="#Lines">Lines</a>
+<li><a accesskey="3" href="#Arcs">Arcs</a>
+<li><a accesskey="4" href="#Polygons">Polygons</a>:          Drawing polygons and rectangles. 
+<li><a accesskey="5" href="#Text">Text</a>
+<li><a accesskey="6" href="#Vias">Vias</a>
+<li><a accesskey="7" href="#Elements">Elements</a>
+<li><a accesskey="8" href="#Pastebuffer">Pastebuffer</a>:       A multi-purpose buffer. 
+</ul>
+
+<div class="node">
+<a name="Common"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Lines">Lines</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.1 Common Drawing and Removing Methods</h4>
+
+<p><a name="index-creating-objects-75"></a><a name="index-object_002c-creating-an-76"></a><a name="index-removing-objects-77"></a><a name="index-object_002c-removing-an-78"></a><a name="index-thickness-of-objects-79"></a><a name="index-object_002c-changing-the-size-of-an-80"></a>
+There are several keystrokes and button events referring to an <em>object</em>
+without identifying its type. Here's a list of them:
+
+   <p><em><Btn1></em> creates (or deletes)  an object depending on the
+current mode.
+
+   <p><em><Key>BackSpace</em> or <em><Key>Delete</em> removes the visible
+object at the cursor location. When more than one object exists at the
+location, the order of removal is: via, line, text, polygon and
+element. The drawn layer order also affects the search - whatever is
+top - most (except elements) is affected before lower items.  Basically
+all this means that what is removed is probably just what you expect. 
+If for some reason it isn't, undo and try again. 
+Only one object is removed for each keystroke. If two or more
+of the same type match, the newest one is removed.
+
+   <p>Use <em><Key>s</em> and <em>Shift<Key>s</em> to change the size (width)
+of lines, arcs, text objects, pins, pads and vias, or to toggle the style
+of polygons (whether pins and vias automatically have clearances).
+
+   <p><em><Key>n</em> changes the name of pins, pads, vias, the
+string of a text object, or the currently displayed label of an element.
+
+   <p><em><Key>m</em> moves the line, arc, or polygon under the cross hair to the
+active layer if it wasn't on that layer already.
+
+   <p><em><Key>u</em> (undo) recovers from an unlimited number of operations
+such as creating, removing, moving, copying, selecting etc. It works like
+you'd expect even if you're in the midst of creating something.
+
+   <p><em>Shift<Key>r</em> restores the last undone operation provided no other
+changes have been made since the undo was performed.
+
+   <p><em><Key>tab</em> changes the board side you are viewing.
+
+   <p>For a complete list of keystrokes and button events see <a href="#Translations">Translations</a>.
+
+<div class="node">
+<a name="Lines"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Arcs">Arcs</a>,
+Previous: <a rel="previous" accesskey="p" href="#Common">Common</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.2 Lines</h4>
+
+<p><a name="index-lines_002c-an-example-81"></a><a name="index-example-of-line-handling-82"></a>
+To draw new lines you have to be in <em>line-mode</em>. Get there either by
+selecting it from the <em>Tool palette</em> or by pressing <em><Key>F2</em>. 
+Each successive <em>notify</em> event creates a new line. The
+adjustment to 45 degree lines is done automatically if it is selected from the
+<em>Display</em> menu. You can toggle the 45 degree mode setting by
+pressing the <em><Key>.</em> (That is the period key). When 45 degree enforcement
+is turned on there are three distinct modes of line creation: a single
+line on the closest 45 degree vector towards the cross hair (but not necessarily
+actually ending at the cross hair), two lines created such that the first leaves
+the start point on a 90 degree vector and the second arrives at the cross hair
+on a 45 degree vector, and finally two lines created such that the first leaves
+the start point on a 45 degree vector and the second arrives at the cross hair
+on a 90 degree vector.  These last two modes always connect all the way from
+the start and end points, and all lines have angles in 45 degree multiples. 
+The <em><Key>/</em> cycles through the three modes.  The status line shows a
+text icon to indicate which of the modes is active and the lines following
+the cross hair motion show the outline of the line(s) that will actually be created. 
+Press <em><Key>Escape</em> to leave line-mode.
+
+   <p><em><Key>l</em>, <em>Shift<Key>l</em> and the entries in the
+<em>Sizes</em> menu change the initial width of new lines.  This width is also
+displayed in the status line.
+
+<div class="node">
+<a name="Arcs"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Polygons">Polygons</a>,
+Previous: <a rel="previous" accesskey="p" href="#Lines">Lines</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.3 Arcs</h4>
+
+<p><a name="index-arc_002c-an-example-83"></a>
+An Arc is drawn  with the <em>arc-tool</em>. Get there either by selecting it
+from the <em>Tool palette</em> or by pressing <em><Key>F8</em>. Press <em>Btn1</em> to
+define the starting point for the arc.  Drag the mouse towards the desired
+end point along the path you want the arc to follow.  The outline of the arc that
+will be created is shown on the screen as you move the mouse.  Arcs are always
+forced to be 90 degrees and have symmetrical length and width ( i.e. they are
+a quarter circle).  The next <em>Btn1</em> click creates the arc.  It will have
+the same width as new lines (displayed in the status line) and appear on the
+active layer. The arc leaves the starting point towards the cross hair along
+the axis whose distance from the cross hair is largest.  Normally this means that
+if you drag along the path you want the arc to follow, you'll get what you
+want.  If the grid is set to the arc radius, then the two distances will be
+equal and you won't be able to get all of the possible directions.  If this
+is thwarting your desires, reduce the grid spacing (<em>!Shift<Key>G</em>) and
+try again.
+
+<div class="node">
+<a name="Polygons"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Text">Text</a>,
+Previous: <a rel="previous" accesskey="p" href="#Arcs">Arcs</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.4 Polygons and Rectangles</h4>
+
+<p><a name="index-polygon_002c-an-example-84"></a><a name="index-example-of-polygon-handling-85"></a><a name="index-rectangle_002c-an-example-86"></a><a name="index-example-of-rectangle-handling-87"></a>
+A polygon is drawn by defining all of its segments as a series of
+consecutive line segments. If the first point matches a new one and if
+the number of points is greater than two, then the polygon is closed. 
+Since matching up with the first point may be difficult, you may use
+<em>Shift<Key>p</em> to close the polygon. The <em>Shift<Key>p</em> won't
+work if clipping to 45 degree lines is selected
+and the final segment cannot match this condition. 
+I suggest you create simple convex polygons in order to avoid a strong
+negative impact on the performance of the connection scanning routines. 
+The <em>rectangle-mode</em> is just an easy way to generate rectangular polygons. 
+<em>Polygon-mode</em> also is selected by <em><Key>F6</em> whereas
+<em>rectangle-mode</em> uses <em><Key>F4</em>. 
+Pressing a <em><Btn1></em> at two locations creates a rectangle by
+defining two of its corners. 
+<em><Key>Insert</em> brings you to <em>insert-point-mode</em> which lets you
+add additional points to an already existing polygon. 
+Single points may be removed by moving the cross hair to them and selecting
+one of the delete actions <em>(remove-mode, BackSpace, or Delete</em>. This only works
+if the remaining polygon will still have three or more corners. 
+Pressing <em><Key>u</em>  or <em><Key>p</em> while entering a new polygon
+brings you back to the previous corner. Removing a point does not
+force clipping to 45 degree angles (because it's not generally possible). 
+Newly created polygons will not connect to pins or vias
+that pierce it unless you create a thermal (using the thermal mode) to make
+the connection. If the edge of a polygon gets too close to a pin or via that
+lies outside of it, a warning will be issued and the pin will be given a
+special color. Increasing the distance between them will remove the warning
+color.
+
+<div class="node">
+<a name="Text"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Vias">Vias</a>,
+Previous: <a rel="previous" accesskey="p" href="#Polygons">Polygons</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.5 Text</h4>
+
+<p><a name="index-text_002c-an-example-88"></a><a name="index-strings_002c-an-example-89"></a><a name="index-example-of-text-handling-90"></a>
+Pressing <em><Key>F5</em> or clicking one of the text selector buttons
+changes to <em>text-mode</em>. 
+Each successive notify event (<em><Btn1></em>)
+pops up the input line at the bottom and queries for a string. 
+Enter it and press <em><Key>Return</em> to confirm or
+<em><Key>Escape</em> to abort. 
+The text object is created with its upper left corner at the current pointer
+location. 
+The initial scaling is changed by <em><Key>t</em> and
+<em>Shift<Key>t</em> or from the <em>Sizes</em> menu.
+
+   <p>Now switch to <em>rotate-mode</em> and press
+<em><Btn1></em> at the text-objects location. Text objects
+on the solder side of the layout are automatically mirrored and
+flipped so that they are seen correctly when viewing the solder-side.
+
+   <p>Use <em><Key>n</em> to edit the string.
+
+   <p><b>TEXT OBJECTS ON COPPER LAYERS CREATE COPPER LINES BUT THEY ARE NOT SCANNED FOR
+CONNECTIONS</b>. If they are moved to the silkscreen layer, they
+no longer create copper.
+
+<div class="node">
+<a name="Vias"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Elements">Elements</a>,
+Previous: <a rel="previous" accesskey="p" href="#Text">Text</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.6 Vias</h4>
+
+<p><a name="index-vias_002c-an-example-91"></a><a name="index-example-of-via-handling-92"></a>
+The initial size of new vias may be changed by <em><Key>v</em> and
+<em>Shift<Key>v</em> or by selecting the appropriate entry from the
+<em>Sizes</em> menu. <em>Mod1<Key>v</em> and <em>Mod1 Shift<Key>v</em> do
+the same for the drilling hole of the via. 
+The statusline is updated with the new values. 
+Creating a via is similar to the other objects. Switch to <em>via-mode</em>
+by using either the selector button or <em><Key>F1</em> then press
+<em><Key>]</em> or <em><Btn1></em> to create one. 
+<em><Key>n</em> changes the name of a via. If you want to create a mounting
+hole for your board, then you can place a via where you want the hole to
+be then convert the via into a hole.  The conversion is done by pressing
+<em>!Ctrl<Key>h</em> with the cross hair over the via.  Conceptually it is
+still a via, but it has no copper annulus.  If you create such a hole in
+the middle of two polygons on different layers, it will short the layers. 
+Theoretically you could arrange for such a hole not to be plated, but a
+metal screw inserted in the hole would still risk shorting the layers. 
+A good rule is to realize that holes in the board really are vias between
+the layers and so place them where they won't interfere with connectivity. 
+You can convert a hole back into a normal via with the same keystroke used
+to convert it in the first place.
+
+<div class="node">
+<a name="Elements"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Pastebuffer">Pastebuffer</a>,
+Previous: <a rel="previous" accesskey="p" href="#Vias">Vias</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.7 Elements</h4>
+
+<p><a name="index-element_002c-an-example-93"></a><a name="index-example-of-element-handling-94"></a>
+Some of the functions related to elements only work if both the package
+layer and the pin layer are switched on.
+
+   <p>Now that you're familiar with many of the basic commands, it is
+time to put the first element on the layout. 
+First of all, you have to load data into the paste buffer. 
+There are four ways to do this:
+<pre class="example">        1) load the data from a library
+        2) load the data from a file
+        3) copy data from an already existing element
+        4) convert objects in the buffer into an element
+</pre>
+   <p>We don't have any elements on the screen yet nor anything in the
+buffer, so we use number one.
+
+   <p><a name="index-example-files-95"></a><a name="index-m4_002c-preprocessing-example-files-96"></a>Select <em>lsi</em> from the menu in the library window press
+<em><Btn1></em> twice at the appropriate text-line to get
+the MC68030 CPU. 
+The data is loaded and the mode is switched to <em>pastebuffer-mode</em>. 
+Each notify event now creates one of these beasts. Leave the mode
+by selecting a different one or by <em><Key>Escape</em> which resets
+all modes.. 
+The cross hair is located at the <em>mark</em> position as defined by
+the data file. Rotating the buffer contents is done by selecting
+the <em>rotate</em> entry of the <em>Buffer</em> menu or by pressing
+<em>Shift<Key>F3</em>. The contents of the buffer
+are valid until new data is loaded into it either by a cut-to-buffer
+operation, copy-to-buffer operation or by loading a new data file. 
+There are 5 buffers
+available  (possibly more or less if changed at compile time
+with the <code>MAX_BUFFER</code> variable in <samp><span class="file">globalconfig.h</span></samp>). 
+Switching between them is done by selecting a menu entry or
+by <em>Shift<Key>1..MAX_BUFFER</em>. 
+Each of the two board sides has its own buffers.
+
+   <p>The release includes all data files for the circuits that are used
+by the demo layout. The elements in the LED example are not found in the library,
+but you can lift them from the example itself if you want. 
+If you have problems with the color of the cross hair, change the resource
+<em>cross hairColor</em> setting to a different one.
+
+   <p><a name="index-example-of-loading-an-element-file-97"></a><a name="index-pins_002c-an-example-98"></a><a name="index-example-of-pin-handling-99"></a>Now load a second circuit, the MC68882 FPU for example. 
+Create the circuit as explained above. You now have two different unnamed
+elements. Unnamed means that the layout-name of the element
+hasn't been set yet. Selecting <em>description</em> from the <em>Display</em>
+menu displays the description string of the two circuits which
+are CPU and FPU. The values of the circuits are set to MC68030 and MC68882. 
+Each of the names of an element may be changed
+by <em><Key>n</em> at the elements location and editing the old name in
+the bottom input line. Naming pins and vias is similar to elements. 
+You can hide the element name so that it won't appear on the board
+silkscreen by pressing <em><key>h</em> with the cursor over the element. 
+Doing so again un-hides the element name.
+
+   <p>Entering <kbd>:le</kbd> and selecting an element data file is
+the second way to load circuits.
+
+   <p>The third way to create a new element is to copy an existing one. 
+Please refer to <a href="#Moving-and-Copying">Moving and Copying</a>.
+
+   <p><a name="index-example-of-creating-an-element-100"></a><a name="index-element_002c-creating-a-new-package-101"></a><a name="index-pastebuffer_002c-convert-contents-to-element-102"></a><a name="index-buffer_002c-convert-contents-to-element-103"></a>The fourth way to create a new element is to convert a buffer's contents
+into an element.  Here's how it's done: Select the Via-tool from the
+<em>Tool pallet</em>.  Set the grid spacing to something appropriate for
+the element pin spacing.  Now create a series of vias where the pins
+go.  Create them in pin number order. It is often handy to place a reference
+point (<em>!Ctrl<Key>m</em>) in the center of the first pin in order to measure
+the location of the other pins.  Next make a solder-side layer the active
+layer from the <em>active-layer</em> popup menu.  Now draw the outline of
+the element using lines and arcs.  When you're done, select everything that
+makes up the element with a box selection (<em><Btn3Down> drag,
+<Btn3Up></em>). Now select "cut selection to buffer" from the <em>Buffer</em>
+menu. Position the cursor over the center of pin 1 and press the left
+button to load the data into the buffer. 
+Finally select "convert buffer to element" from the <em>Buffer</em> menu. 
+You'll only want to create elements this way if they aren't already in the
+library.  It's also probably a good idea to do this before starting any of
+the other aspects of a layout, but it isn't necessary.
+
+   <p>To display the pinout of a circuit move to it and press <em>Shift<Key>d</em>
+or select <em>show pinout</em> from the <em>Objects</em> menu. A new window
+pops up and displays the complete pinout of the element. This display can
+be difficult to read if the component has been rotated 90 degrees :-(
+therefore, the new window will show an un-rotated view so the pin names
+are readable. 
+<em><Key>d</em> displays the name of one or all pins/pads inside the
+Layout area, this is only for display on-screen, it has no effect on any
+printing of the layout.
+
+   <p>You also may want to change a pin's or pad's current size by pressing
+<em><Key>s</em> to increase or <em>Shift<Key>s</em> to decrease it. While
+this is possible, it is not recommended since care was probably taken
+to define the element structure in the first place. You can also change the thickness
+of the element's silkscreen outline with the same keys. You can
+change whether a pin or SMD pad is rounded or square with the <em><Key>q</em>. 
+SMD pads should usually have squared ends. Finally, you can change whether
+the non-square pins are round or octagonal with the <em>!Ctrl<Key>o</em>.
+
+   <p>SMD elements and silkscreen objects are drawn in the "invisible object"
+color if they are located on the opposite side of the board.
+
+   <p>For information on element connections refer to <a href="#Connection-Lists">Connection Lists</a>.
+
+<div class="node">
+<a name="Pastebuffer"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Elements">Elements</a>,
+Up: <a rel="up" accesskey="u" href="#Drawing-and-Removing">Drawing and Removing</a>
+
+</div>
+
+<h4 class="subsection">3.5.8 Pastebuffer</h4>
+
+<p><a name="index-pastebuffer_002c-an-example-104"></a><a name="index-example-of-pastebuffer-handling-105"></a><a name="index-buffer_002c-an-example-106"></a><a name="index-example-of-buffer-handling-107"></a>
+The line-stack and element-buffer of former releases have been replaced
+by 5 (possibly more or less if changed at compile time
+with the <code>MAX_BUFFER</code> variable in <samp><span class="file">globalconfig.h</span></samp>)
+multi-purpose buffers that are selected by
+<em>Shift<Key>1..MAX_BUFFER</em>. The status line shows which buffer is
+the active one. 
+You may load data from a file or layout into them. 
+Cut-and-paste works too. 
+If you followed the instructions earlier in this chapter you should
+now have several objects on the screen. Move the cross hair to one of them
+and press <em><Btn3Down></em> to toggle its selection flag. (If you drag the
+mouse while the button is down, a box selection will be attempted instead
+of toggling the selection.)  The object
+is redrawn in a different color. You also may want to try
+moving the pointer while holding the third button down and
+release it on a different location. This selects all objects inside the
+rectangle and unselects everything else.  If you want to add a box selection
+to an existing selection, drag with <em>Mod1<Btn3Down></em> instead. 
+Dragging <em>Shift Mod1<Btn3Down></em> unselects objects in a box. 
+Now change to <em>pastebuffer-mode</em> and select some operations from the
+<em>Buffer</em> menu. Copying objects to the buffer is available as
+<em>Mod1<Key>c</em> while cutting them uses <em>Mod1<Key>x</em> as
+shortcut. Both clear the buffer before new data is added. 
+If you use the menu entries, you have to supply a cross hair position by
+pressing a mouse button. The objects are attached to the pastebuffer
+relative to that cross hair location. 
+Element data or PCB data may be merged into an existing layout by loading
+the datafiles into the pastebuffer. Both operations are available from
+the <em>File</em> menu or as user commands.
+
+<div class="node">
+<a name="Moving-and-Copying"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Loading-and-Saving">Loading and Saving</a>,
+Previous: <a rel="previous" accesskey="p" href="#Drawing-and-Removing">Drawing and Removing</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.6 Moving and Copying</h3>
+
+<p><a name="index-moving_002c-an-example-108"></a><a name="index-copying_002c-an-example-109"></a><a name="index-example-of-moving-110"></a><a name="index-example-of-copying-111"></a>
+All objects can be moved including element-names, by
+<em><Btn2Down></em>, dragging the pointer while holding the button down
+and releasing it at the new location of the object. If you use
+<em>Mod1<Btn2Down></em> instead, the object is copied. Copying does not work for
+element-names of course. You can move all selected objects with
+<em>Shift <Btn1></em>.  This uses the Pastebuffer, so
+it will remove whatever was previously in the Pastebuffer. 
+Please refer to <a href="#Pastebuffer">Pastebuffer</a>. 
+If you want to give a small nudge to an object, but you don't think
+that the mouse will give you the fine level of control that you want,
+you can position the cursor over the object, press <em><Key>[</em>,
+move it with the arrow keys, then press <em><Key>]</em> when it's at the
+desired position.  Remember that all movements are forced onto grid coordinates, so
+you may want to change the grid spacing first.
+
+   <p><a name="index-moving_002c-traces-to-a-different-layer-112"></a><a name="index-changing-layers-113"></a>To move a trace or group of traces to a different layer, first select
+the tracks to be moved.  It's easiest to do this if you shut off everything
+but that layer first (i.e. silk, pins, other layers, etc). 
+Now set the current layer to be the new layer. 
+Press Shift-M to move all the selected tracks to the current layer. 
+See the <em>MoveToCurrentLayer</em> action for more details.
+
+<div class="node">
+<a name="Loading-and-Saving"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Printing">Printing</a>,
+Previous: <a rel="previous" accesskey="p" href="#Moving-and-Copying">Moving and Copying</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.7 Loading and Saving</h3>
+
+<p><a name="index-loading_002c-an-example-114"></a><a name="index-saving_002c-an-example-115"></a><a name="index-example-of-saving-116"></a><a name="index-example-of-loading-117"></a>
+After your first experience with <code>pcb-rnd</code> you will probably want to save
+your work. <kbd>:s name</kbd> passes the data to an external program which
+is responsible for saving it. For details see <em>saveCommand</em> in
+<a href="#Resources">Resources</a>. 
+Saving also is available from the <em>File</em> menu, either with or
+without supplying a filename. <code>pcb-rnd</code> reuses the last
+filename if you do not pass a new one to the save routine.
+
+   <p>To load an existing layout either select <em>load layout data</em> from the
+<em>File</em> menu or use <kbd>:l filename</kbd>. A file select box pops up if you
+don't specify a filename. Merging existing layouts into the new one is
+supported either by the <em>File</em> menu or by <kbd>:m filename</kbd>.
+
+   <p><a name="index-backup-118"></a><a name="index-saving-layouts-119"></a><a name="index-preventing-loss-of-data-120"></a><a name="index-g_t_002ftmp-121"></a><a name="index-directory-_002ftmp-122"></a><a name="index-temporary-files-123"></a><code>pcb-rnd</code> saves a backup of the current layout at a user specified interval. 
+The backup filename is created by appending a dash, "-", to the <samp><span class="file">.pcb</span></samp> filename. 
+For example, if you are editing the layout in <samp><span class="file">projects/board.pcb</span></samp> then the
+backup file name will be <samp><span class="file">projects/board.pcb-</span></samp>.  If the layout is new and
+has not been saved yet, then the backup file name is <samp><span class="file">PCB.####.backup</span></samp> where the "####"
+will be replaced by the process ID of the currenting running copy of <code>pcb-rnd</code>. 
+This default backup file name may be changed at compilation time via the
+<code>BACKUP_NAME</code>
+variable in <samp><span class="file">globalconfig.h</span></samp>).  During critical
+sections of the program or when data would be lost it is saved as
+<samp><span class="file">PCB.%i.save</span></samp>.  This file name may be changed at compile time
+with the <code>SAVE_NAME</code> variable in <samp><span class="file">globalconfig.h</span></samp>.
+
+<div class="node">
+<a name="Printing"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Exporting">Exporting</a>,
+Previous: <a rel="previous" accesskey="p" href="#Loading-and-Saving">Loading and Saving</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.8 Printing</h3>
+
+<p><a name="index-printing_002c-an-example-124"></a><a name="index-example-of-printing-125"></a>
+<code>pcb-rnd</code> now has support for device drivers,
+<code>PostScript</code>, <em>encapsulated PostScript</em>,
+and <em>Gerber RS-274X</em> drivers are
+available so far.  The <em>Gerber RS-274X</em>
+driver additionally generates a numerical control (NC) drill file for
+automated drilling,
+a bill of materials file to assist in materials procurement and
+inventory control, and a centroid (X-Y) file which includes the
+centroid data needed
+by automatic assembly (pick and place) machines. 
+<u style="color:red;"><u><u> I recommend the use of <code>GhostScript</code> if you</u></u></u>
+don't have a <code>PostScript</code> printer for handling the PostScript
+output. Printing always generates
+a complete set of files for a specified driver. 
+See the page about
+the <em>Print()</em> action for additional information about the filenames. 
+The control panel offers a number of options. Most of them are not available
+for Gerber output because it wouldn't make sense, for example,  to scale the gerber output
+(you'd get an incorrectly made board!)  The options are:
+
+     
+<a name="index-device_002c-selecting-an-output-126"></a>
+<a name="index-output-device-127"></a>
+<dl><dt>‘<samp><span class="samp">device</span></samp>’<dd>The top menu button selects from the available device drivers.
+
+     <p><a name="index-rotating-printout-128"></a><br><dt>‘<samp><span class="samp">rotate</span></samp>’<dd>Rotate layout 90 degrees counter-clockwise before printing (default).
+
+     <p><a name="index-mirroring-printout-129"></a><br><dt>‘<samp><span class="samp">mirror</span></samp>’<dd>Mirror layout before printing. Use this option depending
+on your production line.
+
+     <p><a name="index-color-printout-130"></a><br><dt>‘<samp><span class="samp">color</span></samp>’<dd>Created colored output. All colors will be converted to black if this option
+is inactive.
+
+     <p><a name="index-outline-printout-131"></a><br><dt>‘<samp><span class="samp">outline</span></samp>’<dd>Add a board outline to the output file. The size is determined by the
+maximum board size changeable from the <em>sizes</em> menu. The outline appears
+on the top and bottom sides of the board, but not on the internal layers. 
+An outline can be useful for determining where to shear the board from the
+panel, but be aware that it creates a copper line.  Thus it has the potential
+to cause short circuits if you don't leave enough room from your wiring
+to the board edge.  Use a viewer to see what the output outline looks like
+if you want to know what it looks like.
+
+     <p><a name="index-alignment-targets-132"></a><br><dt>‘<samp><span class="samp">alignment</span></samp>’<dd>Additional alignment targets are added to the output. The distances between
+the board outline is set by the resource <em>alignmentDistance</em>.  Alignment
+targets should only be used if you know for certain that YOU WILL BE USING
+THEM YOURSELF.  It is extremely unlikely that you will want to have alignment
+targets if you send gerber files to a commercial pcb manufacture to be made.
+
+     <p><a name="index-scaling-a-printout-133"></a><br><dt>‘<samp><span class="samp">scaling</span></samp>’<dd>It's quite useful to enlarge your printout for checking the layout. 
+Use the scrollbar to adjust the scaling factor to your needs.
+
+     <p><a name="index-print-media-134"></a><a name="index-media_002c-size-of-135"></a><br><dt>‘<samp><span class="samp">media</span></samp>’<dd>Select the size of the output media from this menu. The user defined size
+may be set by the resource <em>media</em> either from one of the well known
+paper sizes or by a <code>X11</code> geometry specification. 
+This entry is only available if you use <code>X11R5</code> or later. 
+For earlier releases the user defined size or, if not available, <em>A4</em>
+is used. 
+Well known size are:
+     <pre class="display">          	A3
+          	A4
+          	A5
+          	letter
+          	tabloid
+          	ledger
+          	legal
+          	executive
+</pre>
+     <p><a name="index-offset-of-printout-136"></a><a name="index-print-offset-137"></a><br><dt>‘<samp><span class="samp">offset</span></samp>’<dd>Adjust the offsets of the printout by using the panner at the right side
+of the dialog box. 
+This entry is only available if you use <code>X11R5</code> or later. A zero
+offset is used for earlier releases.
+
+     <p><a name="index-DOS-filenames-138"></a><br><dt>‘<samp><span class="samp">8.3 filenames</span></samp>’<dd>Select this button to generate DOS compatible filenames for the output files. 
+The <em>command</em> input area will disappear if selected.
+
+     <p><a name="index-print-command-139"></a><br><dt>‘<samp><span class="samp">commandline</span></samp>’<dd>Use this line to enter a command (starts with <kbd>|</kbd>) or a filename. 
+A %f is replaced by the current filename. 
+The default is set by the resource <em>printCommand</em>.
+
+   </dl>
+
+   <p>The created file includes some labels which are guaranteed to stay unchanged
+     <dl>
+<dt>‘<samp><span class="samp">PCBMIN</span></samp>’<dd>identifies the lowest x and y coordinates in mil.
+
+     <br><dt>‘<samp><span class="samp">PCBMAX</span></samp>’<dd>identifies the highest x and y coordinates in mil.
+
+     <br><dt>‘<samp><span class="samp">PCBOFFSET</span></samp>’<dd>is set to the x and y offset in mil.
+
+     <br><dt>‘<samp><span class="samp">PCBSCALE</span></samp>’<dd>is a floating point value which identifies the scaling factor.
+
+     <br><dt>‘<samp><span class="samp">PCBSTARTDATA</span></samp>’<dt>‘<samp><span class="samp">PCBENDDATA</span></samp>’<dd>all layout data is included between these two marks. You may use them with an
+<code>awk</code> script to produce several printouts on one piece of paper by
+duplicating the code and putting some <code>translate</code> commands in front. 
+Note, the normal <code>PostScript</code> units are 1/72 inch. 
+</dl>
+
+<div class="node">
+<a name="Exporting"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Arrow-Tool">Arrow Tool</a>,
+Previous: <a rel="previous" accesskey="p" href="#Printing">Printing</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.9 Exporting a layout</h3>
+
+<p><a name="index-Exporting-a-layout-140"></a><a name="index-Exporting-a-layout-141"></a>
+To export a layout choose <em>Export layout</em> from the <em>File</em> menu, then
+select the desired exporter.
+
+<ul class="menu">
+<li><a accesskey="1" href="#bom">bom</a>:                 Bill of materials. 
+<li><a accesskey="2" href="#gcode">gcode</a>:               G-code. 
+<li><a accesskey="3" href="#gerber">gerber</a>:              Gerber. 
+<li><a accesskey="4" href="#nelma">nelma</a>:               Nelma. 
+<li><a accesskey="5" href="#png">png</a>: 		       Image. 
+<li><a accesskey="6" href="#ps">ps</a>: 		       Postscript. 
+<li><a accesskey="7" href="#eps">eps</a>: 		       Eps. 
+</ul>
+
+<div class="node">
+<a name="bom"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gcode">gcode</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.1 Bill of materials (bom)</h4>
+
+<p><a name="index-bom-142"></a><a name="index-bill-of-materials-143"></a>
+Produces a bill of materials (BOM) file and a centroid (XY) file.
+
+<div class="node">
+<a name="gcode"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gerber">gerber</a>,
+Previous: <a rel="previous" accesskey="p" href="#bom">bom</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.2 G-code (gcode)</h4>
+
+<p><a name="index-gcode-144"></a><a name="index-g_002dcode-145"></a><a name="index-cnc-146"></a>
+The gcode exporter can generate RS274/NGC G-CODE files to be used with a CNC mill to
+produce pcb's by mechanically removing copper from the perimeter of all elements.
+
+   <p>The elements are enlarged in order to compensate for the cutting tool size so
+that the remaining copper corresponds to the original size; however all
+polygons are left unchanged and will end up being a little smaller; this is not a
+problem because the electrical connection is done with traces, which are correctly
+enlarged.
+
+   <p>A .cnc file is generated for every copper layer, with the bottom layer mirrored so
+that the milling is done right; of course it's not possible to produce directly
+multi-layer (more than 2) pcb's with this method, but the cnc files for
+intermediate layers are generated anyways.
+
+   <p>A drill file is also generated, and it contains all drills regardless of the hole
+size; the drilling sequence is optimized in order to require the least amount of
+movement.
+
+   <p>The export function generates an intermediate raster image before extracting the contour
+of copper elements, and this image is saved as well (in .png format) for inspection.
+
+   <p>When the spacing between two elements is less than the tool diameter they will merge
+and no isolation will be cut between them; the control image should be checked for
+this behaviour.
+
+   <p>Possible workarounds are: increasing spacing, decreasing the tool size, increasing
+the intermediate image resolution.
+
+   <p>To maximize the chance of producing correct pcb's it would be better to increase
+the DRC clearance to at least the tool diameter and use traces as thick as possible;
+the rule is: use the largest element that will not prevent the isolation cut.
+
+   <p>The exporter parameters are:
+
+     <dl>
+<dt><b>basename</b><dd>base name for generated files
+
+     <br><dt><b>dpi</b><dd>intermediate image resolution; affects precision when extracting contours
+
+     <br><dt><b>mill depth</b><dd>should be the copper depth
+
+     <br><dt><b>safe z</b><dd>Z value when moving between polygons
+
+     <br><dt><b>tool radius</b><dd>copper elements are enlarged by this amount
+
+     <br><dt><b>drill depth</b><dd>depth of drills
+
+     <br><dt><b>measurement unit</b><dd>for all parameters above, can be mm,um,inch,mil; g-code is always mm or inch
+</dl>
+
+   <p>All .cnc files specify Z values as parameters, so that it's easy to
+change them without the need to run the exporter again.
+
+   <p>Operation was verified with the EMC2 g-code interpreter.
+
+   <p>Following is a sample layout that is converted with default settings:
+<div align="center"><img src="gcode.png" alt="Sample Layout"></div>
+
+<p>The control image shows that the spacing is sufficient:
+<div align="center"><img src="gcode_control_img.png" alt="Control Image"></div>
+
+<p>The final tool path follows the perimeter of all elements:
+<div align="center"><img src="gcode_tool_path.png" alt="Resulting Tool Path"></div>
+
+<div class="node">
+<a name="gerber"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#nelma">nelma</a>,
+Previous: <a rel="previous" accesskey="p" href="#gcode">gcode</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.3 Gerber (gerber)</h4>
+
+<p><a name="index-gerber-147"></a>
+Produces RS274-X (a.k.a. gerber) photo plot files and Excellon drill files.
+
+<div class="node">
+<a name="nelma"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#png">png</a>,
+Previous: <a rel="previous" accesskey="p" href="#gerber">gerber</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.4 Nelma (nelma)</h4>
+
+<p><a name="index-nelma-148"></a>
+Numerical analysis package export.
+
+<div class="node">
+<a name="png"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ps">ps</a>,
+Previous: <a rel="previous" accesskey="p" href="#nelma">nelma</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.5 Image (png)</h4>
+
+<p><a name="index-png-149"></a><a name="index-image-export-150"></a>
+Produces GIF/JPEG/PNG image files.
+
+<div class="node">
+<a name="ps"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#eps">eps</a>,
+Previous: <a rel="previous" accesskey="p" href="#png">png</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.6 Postscript (ps)</h4>
+
+<p><a name="index-ps-151"></a><a name="index-postscript-152"></a>
+Export as postscript. 
+Can be later converted to pdf.
+
+<div class="node">
+<a name="eps"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#ps">ps</a>,
+Up: <a rel="up" accesskey="u" href="#Exporting">Exporting</a>
+
+</div>
+
+<h4 class="subsection">3.9.7 Encapsulated Postscript (eps)</h4>
+
+<p><a name="index-eps-153"></a><a name="index-encapsulated-postscript-154"></a>
+Export as eps (encapsulated postscript) for inclusion in other documents. 
+Can be later converted to pdf.
+
+<div class="node">
+<a name="Connection-Lists"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Connection-Lists">Connection Lists</a>,
+Previous: <a rel="previous" accesskey="p" href="#Exporting">Exporting</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.10 Connection Lists</h3>
+
+<p><a name="index-example-of-connection-lists-155"></a><a name="index-connections_002c-creating-list-of-156"></a>
+After completing parts of your layout you may want to check if all drawn
+connections match the ones you have in mind. This is probably best done
+in conjunction with a net-list file: see <a href="#Rats-Nest">Rats Nest</a>. 
+The following examples give more rudimentary ways to examine
+the connections.
+<pre class="example">         1) create at least two elements and name them
+         2) create some connections between their pins
+         3) optionally add some vias and connections to them
+</pre>
+   <p>Now select <em>lookup connection</em> from the <em>Connections</em> menu,
+move the cursor to a pin or via and press any mouse button. <code>pcb-rnd</code>
+will look for all other pins and/or vias connected to the one you have
+selected and display the objects in a different color. 
+Now try some of the reset options available from the same menu.
+
+   <p>There also is a way to scan all connections of one element. Select
+<em>a single element</em> from the menu and press any button at the
+element's location. All connections of this element will be saved
+to the specified file. 
+Either the layout name of the element or its canonical name is used to
+identify pins depending on the one which is displayed on the screen
+(may be changed by <em>Display</em> menu).
+
+   <p>An automatic scan of all elements is initiated by choosing
+<em>all elements</em>. It behaves in a similar fashion to scanning a single
+element except the resource <em>resetAfterElement</em>
+is used to determine if connections should be reset before a new element is
+scanned. Doing so will produce very long lists because the power lines are
+rescanned for every element. By default the resource is set to <em>false</em>
+for this reason.
+
+   <p>To scan for unconnected pins select <em>unused pins</em> from the same
+menu.
+
+<div class="node">
+<a name="Arrow-Tool"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Rats-Nest">Rats Nest</a>,
+Previous: <a rel="previous" accesskey="p" href="#Exporting">Exporting</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.11 Arrow Tool</h3>
+
+<p><a name="index-selecting_002c-using-the-arrow-tool-157"></a><a name="index-moving-objects-158"></a><a name="index-arrow-tool-159"></a><a name="index-tool_002c-arrow-160"></a>
+Some commands mentioned earlier in this chapter also are able to operate on all
+selected and visible objects. The Arrow tool is used to select/deselect
+objects and also to move objects or selections.  If you click and release
+on an object with the Arrow tool, it will unselect everything else and
+select the object. Selected objects change color to reflect that
+they are selected. If you <em>Shift</em> click, it will add the object to
+(or remove) the object from the existing selection. If you drag with
+the mouse button down with the Arrow tool, one of several things could
+happen: if you first pressed the button on a selected object, you
+will be moving the selection to where you release the button. If you
+first pressed the button on an unselected object, you will be moving
+that object. If you first pressed the button over empty space, you
+will be drawing a box to select everything inside the box. The <em>Shift</em>
+key works the same way with box selections as it does with single objects.
+
+   <p>Moving a single un-selected object is different from moving a selection. 
+First of all, you can move the end of line, or a point in a polygon this
+way which is impossible by moving selections. Secondly, if rubber banding
+is turned on, moving a single object will rubber-band the attached lines. 
+Finally, it is faster to move a single object this way since there is no need
+to select it first.
+
+   <p>You can select any visible object unless it is locked. If you select an
+object, then turn off its visibility with the Layer controls, it won't
+be moved if you move the remaining visible selection.
+
+   <p>If you have not configured to use strokes in the <code>pcb-rnd</code> user interface, then
+the middle mouse button is automatically bound to the arrow tool, regardless
+of the active tool (which is bound to the first mouse button). So using
+the middle button any time is just like using the first mouse button
+with the Arrow tool active.
+
+   <p>The entries of the <em>Selection</em> menu are hopefully self-explanatory. 
+Many of the <em>Action Commands</em> can take various key words that make
+them function on all or some of the selected items.
+
+<div class="node">
+<a name="Rats-Nest"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Design-Rule-Checking">Design Rule Checking</a>,
+Previous: <a rel="previous" accesskey="p" href="#Arrow-Tool">Arrow Tool</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.12 Rats Nest</h3>
+
+<p><a name="index-rats-nest-161"></a><a name="index-netlist-162"></a><a name="index-rat_002dline-163"></a>
+If you have a netlist that corresponds to the layout you are working on, you
+can use the rats-nest feature to add rat-lines to the layout. 
+First you will need to load a netlist file (see <em>:rn</em>,
+<a href="#User-Commands">User Commands</a>). 
+<em><Key>w</em> adds rat-lines on the active layer using the current
+line thickness shown in the status line (usually you'll want them to be thin lines). 
+Only those rat-lines that fill in missing connectivity (since you have
+probably routed some connections already) are added. 
+If the layout is already completely wired, nothing will be added, and you will
+get a message that the wiring is complete.
+
+   <p>Rat-lines are lines having the special property that they only connect to pins and
+pads at their end points.  Rat-lines may be drawn differently to other lines
+to make them easier to identify since they have special behavior and cannot
+remain in a completed layout. 
+Rat-lines are added in the minimum length straight-line tree pattern
+(always ending on pins or pads) that satisfies the missing connectivity in the circuit. 
+Used in connection with moves and rotates of the elements, they are extremely useful for
+deciding where to place elements on the board. The rat-lines will always automatically
+rubberband to the elements whether or not the rubberband mode is on. The only way for
+you to move them is by moving the parts they connect to. 
+This is because it is never desirable to have the rat-lines disconnected from
+their element pins.  Rat-lines will normally criss-cross
+all over which gives rise to the name "rats nest" describing a layout connected with
+them.  If a SMD pad is unreachable on the active layer, a warning will be issued
+about it and the rat-line to that pad will not be generated.
+
+   <p>A common way to use rats nests is to place some
+elements on the board, add the rat-lines, and then use a series of moves/rotates of the
+elements until the rats nest appears to have minimum tangling.  You may want to iterate this step
+several times. Don't worry if the layout looks messy - as long as you can get a sense for whether
+the criss-crossing is better or worse as you move things, you're fine. 
+After moving some elements around, you may want to optimize the rats nest <em><Key>o</em>
+so that the lines are drawn between the closest points (this can change once you've moved components). 
+Adding rat-lines only to selected pads/pins (<em>Shift<Key>w</em>)
+is often useful to layout a circuit a little bit at a time. 
+Sometimes you'll want to delete all the rat-lines (<em><Key>e</em>) or
+selected rat-lines (<em>Shift<Key>e</em>) in order to reduce confusion. 
+With a little practice you'll be able to achieve a near optimal component placement with
+the use of a rats nest.
+
+   <p>Rat-lines are not only used for assisting your element placement, they can also help
+you to route traces on the board. 
+Use the <em><Key>m</em> to convert a rat-line under the cursor into
+a normal line on the active layer. 
+Inserting a point into a rat-line will also cause the two new lines to be normal lines
+on the board. 
+Another way that you can use rat-lines is to
+use the <em><Key>f</em> with the cursor over a pad or pin.  All of the pins and
+pads and rat-lines belonging to that net will be highlighted. This is a helpful way to
+distinguish one net from the rest of the rats nest.  You can then route those tracks,
+turn off the highlighting (<em>Shift<Key>f</em>) and repeat the process. This will work even
+if the layer that the rat-lines reside on is made invisible - so only the pins and pads
+are highlighted. 
+Be sure to erase the rat-lines (<em><Key>e</em> erases them all) once you've
+duplicated their connectivity by adding your own lines. 
+When in doubt, the <em><Key>o</em> will delete only those
+rat-lines that are no longer needed.
+
+   <p>If connections exist on the board that are not listed in the netlist when
+<em><Key>w</em> is pressed, warning messages are issued and the affected pins and
+pads are drawn in a special <em>warnColor</em> until the next <em>Notify()</em> event. 
+If the entire layout agrees completely with the netlist, a message informs you that
+the layout is complete and no rat-lines will be added (since none are needed). 
+If the layout is complete, but still has rat-lines then you will be warned
+that rat-lines remain. If you get no message at all it's probably because some
+elements listed in the net list can't be found and where reported in an earlier
+message. 
+There shouldn't be any rat-lines left in a completed layout, only normal lines.
+
+   <p>The <em>Shift<Key>w</em> is used to add rat-lines to only those missing connections among
+the selected pins and pads.  This can be used to add rat-lines in an incremental
+manner, or to force a rat-line to route between two points that are not the
+closest points within the net. Often it is best to add the rats nest in an incremental fashion, laying
+out a sub-section of the board before going further. This is easy to accomplish since
+new rat-lines are never added where routed connectivity already makes the necessary
+connections.
+
+<div class="node">
+<a name="Design-Rule-Checking"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Trace-Optimizer">Trace Optimizer</a>,
+Previous: <a rel="previous" accesskey="p" href="#Rats-Nest">Rats Nest</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.13 Design Rule Checking</h3>
+
+<p><a name="index-design-rule-checking-164"></a><a name="index-drc-165"></a><a name="index-spacing_002c-minimum-166"></a><a name="index-overlap_002c-minimum-167"></a>
+After you've finished laying out a board, you may want to check
+to be certain that none of your interconnections are too closely
+spaced or too tenuously touching to be reliably fabricated. The design
+rule checking (DRC) function does this for you. Use the command ":DRC()" (without
+the quotes of course) to invoke the checker.  If there are no problem areas,
+you'll get a message to that effect.  If any problem is encountered, you will get
+a message about it and the affected traces will be highlighted. One part of the
+tracks of concern will be selected, while the other parts of concern will have the
+"FindConnection" highlighting. The screen will automatically be centered in the
+middle of the object having the "FindConnection" (Green) highlighting.  The middle of
+the object is also the coordinates reported to be "near" the problem.  The actual trouble
+region will be somewhere on the boundary of this object.  If the two parts are
+from different nets then there is some place where they approach each
+other closer than the minimum rule.  If the parts are from the same net, then
+there is place where they are only barely connected. Find that place and connect
+them better.
+
+   <p>After a DRC error is found and corrected you must run the DRC again because
+the search for errors is halted as soon as the first problem is found. Unless you've
+been extremely careless there should be no more than a few design rule errors
+in your layout.  The DRC checker does not check for minimum spacing rules to
+copper text, so always be very careful when adding copper text to a layout. 
+The rules for the DRC are specified in the application resource file.  The minimum
+spacing value (in mils) is given by the <em>Settings.Bloat</em> value. The default
+is 7 mils. The minimum touching overlap (in mils) is given by the
+<em>Settings.Shrink</em> value. This value defaults to 5 mils. Check with your
+fabrication process people to determine the values that are right for you.
+
+   <p>If you want to turn off the highlighting produced by the DRC, perform an
+undo (assuming no other changes have been made).  To restore the highlighting,
+use redo.  The redo will restore the highlighting quickly without re-running
+the DRC checker.
+
+<div class="node">
+<a name="Trace-Optimizer"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Searching-for-elements">Searching for elements</a>,
+Previous: <a rel="previous" accesskey="p" href="#Design-Rule-Checking">Design Rule Checking</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.14 Trace Optimizer</h3>
+
+<p><a name="index-trace-optimizer-168"></a><a name="index-optimizer-169"></a>
+PCB includes a flexible trace optimizer.  The trace optimizer can be run
+after auto routing or hand routing to clean up the traces.
+
+     <dl>
+<dt><b>Auto-Optimize</b><dd>Performs debumpify, unjaggy, orthopull, vianudge, and viatrim, in that
+order, repeating until no further optimizations are performed.
+
+     <br><dt><b>Debumpify</b><dd>Looks for U shaped traces that can be shortened or eliminated.
+
+     <br><dt><b>Unjaggy</b><dd>Looks for corners which could be flipped to eliminate one or more
+corners (i.e. jaggy lines become simpler).
+
+     <br><dt><b>Vianudge</b><dd>Looks for vias where all traces leave in the same direction. Tries to
+move via in that direction to eliminate one of the traces (and thus a
+corner).
+
+     <br><dt><b>Viatrim</b><dd>Looks for traces that go from via to via, where moving that trace to a
+different layer eliminates one or both vias.
+
+     <br><dt><b>Orthopull</b><dd>Looks for chains of traces all going in one direction, with more traces
+orthogonal on one side than on the other. Moves the chain in that
+direction, causing a net reduction in trace length, possibly eliminating
+traces and/or corners.
+
+     <br><dt><b>SimpleOpts</b><dd>Removing unneeded vias, replacing two or more trace segments in a row
+with a single segment. This is usually performed automatically after
+other optimizations.
+
+     <br><dt><b>Miter</b><dd>Replaces 90 degree corners with a pair of 45 degree corners, to reduce
+RF losses and trace length.
+
+   </dl>
+
+<div class="node">
+<a name="Searching-for-elements"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Measuring-distances">Measuring distances</a>,
+Previous: <a rel="previous" accesskey="p" href="#Trace-Optimizer">Trace Optimizer</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.15 Searching for elements</h3>
+
+<p><a name="index-Searching-for-elements-170"></a><a name="index-Searching-for-elements-171"></a>
+To locate text or a specific element or grouping of similar elements
+choose ‘<samp><span class="samp">Select by name</span></samp>’ from the <b>Select</b> menu, then choose the
+appropriate subsection.  At the bottom of the screen the prompt
+<em>pattern:</em> appears.  Enter the text or <a href="#Regular-Expressions">Regular Expressions</a>
+of the text to be found.  Found text will be highlighted.
+
+<div class="node">
+<a name="Measuring-distances"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Vendor-drill-mapping">Vendor drill mapping</a>,
+Previous: <a rel="previous" accesskey="p" href="#Searching-for-elements">Searching for elements</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.16 Measuring distances</h3>
+
+<p><a name="index-Measuring-distances-172"></a><a name="index-Measuring-distances-173"></a>
+To measure distances, for example the pin-to-pin pitch of a part to
+validate a footprint, place the cursor at the starting
+measurement point, then press <em>!Ctrl<Key>m</em>.  This marks the
+current  location with a <em>X</em>. The <em>X</em> mark is now the zero point
+origin for the relative cursor position display.  The cursor display
+shows both absolute position and position relative to the mark as
+the mouse is moved away from the mark.  If a mark is already present,
+the mark is removed and the cursor display stops displaying relative
+cursor coordinates.
+
+<div class="node">
+<a name="Vendor-drill-mapping"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Measuring-distances">Measuring distances</a>,
+Up: <a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.17 Vendor Drill Mapping</h3>
+
+<p><a name="index-Vendor-rules-174"></a><a name="index-Vendor-mapping-175"></a><a name="index-Drill-table-176"></a><a name="index-Vendor-drill-table-177"></a>
+<code>pcb-rnd</code> includes support for mapping drill holes to a specified set
+of sizes used by a particular vendor.  Many PCB manufacturers have a
+prefered set of drill sizes and charge extra when others are used. 
+The mapping can be performed on an existing design and can also be
+enabled to automatically map drill holes as vias and elements are
+instantiated.
+
+   <p>The first step in using the vendor drill mapping feature is to create
+a resource file describing the capabilities of your vendor.  The file
+format is the resource file format described in <a href="#Resource-Syntax">Resource Syntax</a>. 
+A complete example is given below.
+
+<pre class="example">     # Optional name of the vendor
+     vendor = "Vendor Name"
+     
+     # units for dimensions in this file.
+     # Allowed values:  mil/inch/mm
+     units = mil
+     
+     # drill table
+     drillmap = {
+        # When mapping drill sizes, select the nearest size
+        # or always round up.  Allowed values:  up/nearest
+        round = up
+     
+        # The list of vendor drill sizes.  Units are as specified
+        # above.
+        20
+        28
+        35
+        38
+        42
+        52
+        59.5
+        86
+       125
+       152
+     
+        # optional section for skipping mapping of certain elements
+        # based on reference designator, value, or description
+        # this is useful for critical parts where you may not
+        # want to change the drill size.  Note that the strings
+        # are regular expressions.
+        skips = {
+           {refdes "^J3$"}  # Skip J3.
+           {refdes "J3"}  # Skip anything with J3 as part of the refdes.
+           {refdes "^U[1-3]$" "^X.*"} # Skip U1, U2, U3, and anything starting with X.
+           {value "^JOHNSTECH_.*"} # Skip all Johnstech footprints based on the value of a part.
+           {descr "^AMP_MICTOR_767054_1$"} # Skip based on the description.
+        }
+     }
+     
+     # If specified, this section will change the current DRC
+     # settings for the design.  Units are as specified above.
+     drc = {
+        copper_space = 7
+        copper_width = 7
+        silk_width = 10
+        copper_overlap = 4
+     }
+</pre>
+   <p>The vendor resource is loaded using the <em>LoadVendor</em> action. 
+This is invoked by entering:
+<pre class="example">     :LoadVendor(vendorfile)
+</pre>
+   <p>from within <code>pcb-rnd</code>.  Substitute the file name of your vendor
+resource file for ‘<samp><span class="samp">vendorfile</span></samp>’.  This action will load the vendor
+resource and modify all the drill holes in the design as well as the
+default via hole size for the various routing styles.
+
+   <p>Once a vendor drill map has been loaded, new vias and elements will
+automatically have their drill hole sizes mapped to the vendor drill
+table.  Automatic drill mapping may be disabled under the “Settings”
+menu.  To re-apply an already loaded vendor drill table to a design,
+choose “Apply vendor drill mapping” from the “Connects” menu.
+
+   <p>See <a href="#Actions">Actions</a> for a complete description of the actions associated
+with vendor drill mapping.
+
+   <p>Note that the expressions used in the <code>skips</code> section are regular
+expressions.  See <a href="#Regular-Expressions">Regular Expressions</a> for an introduction to
+regular expressions.
+
+<!--  User Commands chapter - -->
+<div class="node">
+<a name="User-Commands"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Command_002dLine-Options">Command-Line Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#Getting-Started">Getting Started</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">4 User Commands</h2>
+
+<p><a name="index-user-commands-179"></a><a name="index-entering-user-commands-180"></a>The entering of user-commands is initiated by the action routine
+<em>Command()</em> (normally bound to the <code>(":")</code> character) which
+replaces the bottom statusline with an input area or opens a separate
+command window.  It is finished by either <em><Key>Return</em> or
+<em><Key>Escape</em> to confirm or to abort. These two key-bindings
+cannot be changed from the resource file.  The triggering event,
+normally a key press, is ignored.
+
+   <p>Commands can be entered in one of two styles, command entry syntax:
+“<em>Command arg1 arg2</em>” or action script syntax “<em>Action1(arg1,
+arg2); Action2(arg1, arg2);</em>”.  Quoting arguments works similar to
+bash quoting:
+
+     <ul>
+<li>A backslash (\) is the escape character.  It preserves the literal
+value of the next character that follows.  To get a literal '\' use
+"\\".
+
+     <li>Enclosing characters in single quotes preserves the literal value of
+each character within the quotes.  A single quote may not occur
+between single quotes, even when preceded by a blackslash.
+
+     <li>Enclosing characters in double quotes preserves the literal value of
+all characters within the quotes, with the exception of '\' which
+maintains its special meaning as an escape character. 
+</ul>
+
+   <p>There are simple <em>usage</em> dialogs for each command and one for the
+complete set of commands.
+
+     
+<a name="index-g_t_003al-181"></a>
+<a name="index-loading-layouts-182"></a>
+<a name="index-layout_002c-loading-a-183"></a>
+<dl><dt>‘<samp><span class="samp">l [filename]</span></samp>’<dd>Loads a new datafile (layout) and, if confirmed, overwrites any existing unsaved data. 
+The filename and the searchpath (<em>filePath</em>) are passed to the
+command defined by <em>fileCommand</em>. 
+If no filename is specified a file select box will popup.
+
+     <p><a name="index-g_t_003ale-184"></a><a name="index-loading-elements-to-buffer-185"></a><a name="index-element_002c-loading-to-buffer-186"></a><br><dt>‘<samp><span class="samp">le [filename]</span></samp>’<dd>Loads an element description into the paste buffer. 
+The filename and the searchpath (<em>elementPath</em>) are passed to the
+command defined by <em>elementCommand</em>. 
+If no filename is specified a file select box will popup.
+
+     <p><a name="index-g_t_003am-187"></a><a name="index-loading-a-layout-to-buffer-188"></a><a name="index-merging-layouts-189"></a><a name="index-layout_002c-loading-to-buffer-190"></a><a name="index-layout_002c-merging-a-191"></a><br><dt>‘<samp><span class="samp">m [filename]</span></samp>’<dd>Loads an layout file into the paste buffer. 
+The filename and the searchpath (<em>filePath</em>) are passed to the
+command defined by <em>fileCommand</em>. 
+If no filename is specified a file select box will popup.
+
+     <p><a name="index-g_t_003aq-192"></a><a name="index-exit-193"></a><a name="index-quit-194"></a><br><dt>‘<samp><span class="samp">q[!]</span></samp>’<dd>Quits the program. Uses confirmation dialog if any unsaved data will be lost by quitting.
+q! doesn't ask for confirmation, it just quits.
+
+     <p><a name="index-g_t_003as-195"></a><a name="index-saving-layouts-196"></a><a name="index-layout-files_002c-saving-of-197"></a><br><dt>‘<samp><span class="samp">s [filename]</span></samp>’<dd>Data and the filename are passed to the command defined by the resource
+<em>saveCommand</em>. It must read the layout data from <em>stdin</em>. 
+If no filename is entered, either the last one is used
+again or, if it is not available, a file select box will pop up.
+
+     <p><a name="index-g_t_003arn-198"></a><a name="index-rat_0027s-nest-199"></a><a name="index-layout_002dname-200"></a><br><dt>‘<samp><span class="samp">rn [filename]</span></samp>’<dd>Reads in a netlist file.  If no filename is given
+a file select box will pop up. 
+The file is read via the command defined by the
+<em>RatCommand</em> resource. The command must send its output to <em>stdout</em>.
+
+     <p>Netlists are used for generating rat's nests (see <a href="#Rats-Nest">Rats Nest</a>) and for
+verifying the board layout (which is also accomplished by the <em>Ratsnest</em>
+command).
+
+     <p><a name="index-g_t_003aw_005bq_005d-201"></a><a name="index-saving-layouts-202"></a><a name="index-layout-files_002c-saving-of-203"></a><br><dt>‘<samp><span class="samp">w[q] [filename]</span></samp>’<dd>These commands have been added for the convenience of <code>vi</code> users and
+have the same functionality as <em>s</em> combined with <em>q</em>.
+
+     <p><a name="index-g_t_003aactionCommand_0028_0029-204"></a><a name="index-action-command-205"></a><a name="index-Actions_002c-initiating-206"></a><br><dt>‘<samp><span class="samp">actionCommand</span></samp>’<dd>Causes the actionCommand to be executed.  This allows you to initiate actions
+for which no bindings exist in the resource file.  It can be used to initiate any
+action with whatever arguments you enter.  This makes it possible to do things
+that otherwise would be extremely tedious.  For example, to change the drilling
+hole diameter of all vias in the layout to 32 mils, you could select everything using the
+selection menu, then type "<em>:ChangeDrillSize(SelectedVias, 32)</em>".  (This will
+only work provided the via's diameter is sufficiently large to accommodate a 32 mil hole). 
+Another example might be to set the grid to 1 mil by typing "<em>:SetValue(Grid, 1)</em>". 
+Note that some actions use the current cursor location, so be sure to place the cursor
+where you want before entering the command.  This is one of my favorite new
+features in 1.5 and can be a powerful tool. Study the <a href="#Actions">Actions</a> section to
+see what actions are available.
+
+   </dl>
+
+<!--  chapter 4 - -->
+<div class="node">
+<a name="Command-Line-Options"></a>
+<a name="Command_002dLine-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#X11-Interface">X11 Interface</a>,
+Previous: <a rel="previous" accesskey="p" href="#User-Commands">User Commands</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">5 Command-Line Options</h2>
+
+<p><a name="index-starting-_0040pcb_007b_007d-207"></a><a name="index-command_002dline-options-208"></a>
+The synopsis of the pcb command is:
+
+   <p><code>pcb [OPTION ...] [LAYOUT-FILE.pcb]</code> to start the application in GUI mode,
+
+<p class="noindent">or
+
+   <p><code>pcb [-h | -V | --copyright]</code> for a list of options, version, and copyright,
+
+<p class="noindent">or
+
+   <p><code>pcb -p [OPTION ...] [LAYOUT-FILE.pcb]</code> to print a layout,
+
+<p class="noindent">or
+
+   <p><code>pcb -x HID [OPTION ...] [LAYOUT-FILE.pcb]</code> to export.
+
+<p class="noindent">Possible values for the parameter ‘<samp><span class="samp">HID</span></samp>’ are:
+     <dl>
+   <dt>‘<samp><span class="samp">bom</span></samp>’<dd>    Export a bill of materials
+   <br><dt>‘<samp><span class="samp">gcode</span></samp>’<dd>    Export to G-Code
+   <br><dt>‘<samp><span class="samp">gerber</span></samp>’<dd>    Export RS-274X (Gerber)
+   <br><dt>‘<samp><span class="samp">nelma</span></samp>’<dd>    Numerical analysis package export
+   <br><dt>‘<samp><span class="samp">png</span></samp>’<dd>    export GIF/JPEG/PNG
+   <br><dt>‘<samp><span class="samp">ps</span></samp>’<dd>    export postscript
+   <br><dt>‘<samp><span class="samp">eps</span></samp>’<dd>    export encapsulated postscript
+</dl>
+
+<p class="noindent">There are several resources which may be set or reset in addition to the
+standard toolkit command-line options. For a complete list refer to
+<a href="#Resources">Resources</a>.
+
+<!-- key options -->
+<ul class="menu">
+<li><a accesskey="1" href="#General-Options">General Options</a>
+<li><a accesskey="2" href="#General-GUI-Options">General GUI Options</a>
+<li><a accesskey="3" href="#GTK_002b-GUI-Options">GTK+ GUI Options</a>
+<li><a accesskey="4" href="#lesstif-GUI-Options">lesstif GUI Options</a>
+<li><a accesskey="5" href="#Colors">Colors</a>
+<li><a accesskey="6" href="#Layer-Names">Layer Names</a>
+<li><a accesskey="7" href="#Paths">Paths</a>
+<li><a accesskey="8" href="#Sizes">Sizes</a>
+<li><a accesskey="9" href="#Commands">Commands</a>
+<li><a href="#DRC-Options">DRC Options</a>
+<li><a href="#BOM-Creation">BOM Creation</a>
+<li><a href="#Gerber-Export">Gerber Export</a>
+<li><a href="#Postscript-Export">Postscript Export</a>
+<li><a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a>
+<li><a href="#PNG-Options">PNG Options</a>
+<li><a href="#lpr-Printing-Options">lpr Printing Options</a>
+<li><a href="#nelma-Options">nelma Options</a>
+</ul>
+<!-- options General Options -->
+<div class="node">
+<a name="General-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#General-GUI-Options">General GUI Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.1 General Options</h3>
+
+<!-- ./../src/main.c 434 -->
+     <dl>
+<dt><code>--help</code><a name="index-g_t_002d_002dhelp-209"></a><dd>Show help on command line options. 
+</dl>
+<!-- ./../src/main.c 439 -->
+     <dl>
+<dt><code>--version</code><a name="index-g_t_002d_002dversion-210"></a><dd>Show version. 
+</dl>
+   <!-- ./../src/main.c 443 -->
+     <dl>
+<dt><code>--verbose</code><a name="index-g_t_002d_002dverbose-211"></a><dd>Be verbose on stdout. 
+</dl>
+   <!-- ./../src/main.c 448 -->
+     <dl>
+<dt><code>--copyright</code><a name="index-g_t_002d_002dcopyright-212"></a><dd>Show copyright. 
+</dl>
+   <!-- ./../src/main.c 453 -->
+     <dl>
+<dt><u style="color:red;"><code>--show-defaults</code><a name="index-g_t_002d_002dshow_002ddefaults-213"></a><dd>Show option defaults. 
+</dl>
+   <!-- ./../src/main.c 458 -->
+     <dl>
+<dt><code>--show-actions</code><a name="index-g_t_002d_002dshow_002dactions-214"></a><dd>Show available actions and exit. 
+</dl>
+   <!-- ./../src/main.c 463 -->
+     <dl>
+<dt><code>--dump-actions</code><a name="index-g_t_002d_002ddump_002dactions-215"></a><dd>Dump actions (for documentation). 
+</dl>
+   <!-- ./../src/main.c 468 -->
+     <dl>
+<dt><code>--grid-units-mm <string></code><a name="index-g_t_002d_002dgrid_002dunits_002dmm-_003cstring_003e-216"></a><dd>Set default grid units. Can be mm or mil. Defaults to mil. 
+</dl>
+   <!-- ./../src/main.c 686 -->
+     <dl>
+<dt><code>--backup-interval</code><a name="index-g_t_002d_002dbackup_002dinterval-217"></a><dd>Time between automatic backups in seconds. Set to <code>0</code> to disable. 
+The default value is <code>60</code>. 
+</dl>
+   <!-- ./../src/main.c 723 -->
+     <dl>
+<dt><code>--groups <string></code><a name="index-g_t_002d_002dgroups-_003cstring_003e-218"></a><dd>Layer group string. Defaults to <code>"1,c:2:3:4:5:6,s:7:8"</code>. 
+</dl>
+   <!-- ./../src/main.c 788 -->
+     <dl>
+<dt><code>--route-styles <string></code><a name="index-g_t_002d_002droute_002dstyles-_003cstring_003e-219"></a><dd>A string that defines the route styles. Defaults to <br>
+<code>"Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000
+	:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"</code>
+</dl>
+   <!-- ./../src/main.c 807 -->
+     <dl>
+<dt><code>--element-path <string></code><a name="index-g_t_002d_002delement_002dpath-_003cstring_003e-220"></a><dd>A colon separated list of directories or commands (starts with '|'). 
+The path is passed to the program specified in <samp><span class="option">--element-command</span></samp>. 
+</dl>
+   <!-- ./../src/main.c 817 -->
+     <dl>
+<dt><code>--action-script <string></code><a name="index-g_t_002d_002daction_002dscript-_003cstring_003e-221"></a><dd>If set, this file is executed at startup. 
+</dl>
+   <!-- ./../src/main.c 822 -->
+     <dl>
+<dt><code>--action-string <string></code><a name="index-g_t_002d_002daction_002dstring-_003cstring_003e-222"></a><dd>If set, this string of actions is executed at startup. 
+</dl>
+   <!-- ./../src/main.c 827 -->
+     <dl>
+<dt><code>--fab-author <string></code><a name="index-g_t_002d_002dfab_002dauthor-_003cstring_003e-223"></a><dd>Name of author to be put in the Gerber files. 
+</dl>
+   <!-- ./../src/main.c 832 -->
+     <dl>
+<dt><code>--layer-stack <string></code><a name="index-g_t_002d_002dlayer_002dstack-_003cstring_003e-224"></a><dd>Initial layer stackup, for setting up an export. A comma separated list of layer
+names, layer numbers and layer groups. 
+</dl>
+   <!-- ./../src/main.c 883 -->
+     <dl>
+<dt><code>--save-last-command</code><a name="index-g_t_002d_002dsave_002dlast_002dcommand-225"></a><dd>If set, the last user command is saved. 
+</dl>
+   <!-- ./../src/main.c 887 -->
+     <dl>
+<dt><code>--save-in-tmp</code><a name="index-g_t_002d_002dsave_002din_002dtmp-226"></a><dd>If set, all data which would otherwise be lost are saved in a temporary file
+<samp><span class="file">/tmp/PCB.%i.save</span></samp> . Sequence ‘<samp><span class="samp">%i</span></samp>’ is replaced by the process ID. 
+</dl>
+   <!-- ./../src/main.c 901 -->
+     <dl>
+<dt><code>--reset-after-element</code><a name="index-g_t_002d_002dreset_002dafter_002delement-227"></a><dd>If set, all found connections are reset before a new component is scanned. 
+</dl>
+   <!-- ./../src/main.c 906 -->
+     <dl>
+<dt><code>--ring-bell-finished</code><a name="index-g_t_002d_002dring_002dbell_002dfinished-228"></a><dd>Execute the bell command when all rats are routed. 
+</dl></u>
+   <!-- options General GUI Options -->
+<div class="node">
+<a name="General-GUI-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#GTK_002b-GUI-Options">GTK+ GUI Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#General-Options">General Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.2 General GUI Options</h3>
+<u style="color:red;">
+<!-- ./../src/main.c 842 -->
+     <dl>
+<dt><code>--pinout-offset-x <num></code><a name="index-g_t_002d_002dpinout_002doffset_002dx-_003cnum_003e-229"></a><dd>Horizontal offset of the pin number display. Defaults to <code>100mil</code>. 
+</dl>
+<!-- ./../src/main.c 847 -->
+     <dl>
+<dt><code>--pinout-offset-y <num></code><a name="index-g_t_002d_002dpinout_002doffset_002dy-_003cnum_003e-230"></a><dd>Vertical offset of the pin number display. Defaults to <code>100mil</code>. 
+</dl>
+   <!-- ./../src/main.c 852 -->
+     <dl>
+<dt><code>--pinout-text-offset-x <num></code><a name="index-g_t_002d_002dpinout_002dtext_002doffset_002dx-_003cnum_003e-231"></a><dd>Horizontal offset of the pin name display. Defaults to <code>800mil</code>. 
+</dl>
+   <!-- ./../src/main.c 857 -->
+     <dl>
+<dt><code>--pinout-text-offset-y <num></code><a name="index-g_t_002d_002dpinout_002dtext_002doffset_002dy-_003cnum_003e-232"></a><dd>Vertical offset of the pin name display. Defaults to <code>-100mil</code>. 
+</dl>
+   <!-- ./../src/main.c 862 -->
+     <dl>
+<dt><code>--draw-grid</code><a name="index-g_t_002d_002ddraw_002dgrid-233"></a><dd>If set, draw the grid at start-up. 
+</dl>
+   <!-- ./../src/main.c 866 -->
+     <dl>
+<dt><code>--clear-line</code><a name="index-g_t_002d_002dclear_002dline-234"></a><dd>If set, new lines clear polygons. 
+</dl>
+   <!-- ./../src/main.c 870 -->
+     <dl>
+<dt><code>--full-poly</code><a name="index-g_t_002d_002dfull_002dpoly-235"></a><dd>If set, new polygons are full ones. 
+</dl>
+   <!-- ./../src/main.c 874 -->
+     <dl>
+<dt><code>--unique-names</code><a name="index-g_t_002d_002dunique_002dnames-236"></a><dd>If set, you will not be permitted to change the name of an component to match that
+of another component. 
+</dl>
+   <!-- ./../src/main.c 878 -->
+     <dl>
+<dt><code>--snap-pin</code><a name="index-g_t_002d_002dsnap_002dpin-237"></a><dd>If set, pin centers and pad end points are treated as additional grid points
+that the cursor can snap to. 
+</dl>
+   <!-- ./../src/main.c 892 -->
+     <dl>
+<dt><code>--all-direction-lines</code><a name="index-g_t_002d_002dall_002ddirection_002dlines-238"></a><dd>Allow all directions, when drawing new lines. 
+</dl>
+   <!-- ./../src/main.c 897 -->
+     <dl>
+<dt><code>--show-number</code><a name="index-g_t_002d_002dshow_002dnumber-239"></a><dd>Pinout shows number. 
+</dl></u>
+   <!-- options GTK+ GUI Options -->
+<div class="node">
+<a name="GTK+-GUI-Options"></a>
+<a name="GTK_002b-GUI-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-GUI-Options">lesstif GUI Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#General-GUI-Options">General GUI Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.3 GTK+ GUI Options</h3>
+
+<!-- ./../src/hid/gtk/gui-top-window.c 1615 -->
+     <dl>
+<dt><code>--listen</code><a name="index-g_t_002d_002dlisten-240"></a><dd>Listen for actions on stdin. 
+</dl>
+<!-- ./../src/hid/gtk/gui-top-window.c 1621 -->
+     <dl>
+<dt><code>--bg-image <string></code><a name="index-g_t_002d_002dbg_002dimage-_003cstring_003e-241"></a><dd>File name of an image to put into the background of the GUI canvas. The image must
+be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
+automatically scaled to fit the canvas. 
+</dl>
+   <!-- ./../src/hid/gtk/gui-top-window.c 1627 -->
+     <dl>
+<u style="color:red;"><dt><code>--pcb-menu <string></code><a name="index-g_t_002d_002dpcb_002dmenu-_003cstring_003e-242"></a><dd>Location of the <samp><span class="file">gpcb-menu.res</span></samp> file which defines the menu for the GTK+ GUI. 
+</dl></u>
+   <!-- options lesstif GUI Options -->
+<div class="node">
+<a name="lesstif-GUI-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Colors">Colors</a>,
+Previous: <a rel="previous" accesskey="p" href="#GTK_002b-GUI-Options">GTK+ GUI Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.4 lesstif GUI Options</h3>
+
+<!-- ./../src/hid/lesstif/main.c 201 -->
+     <dl>
+<dt><code>--listen</code><a name="index-g_t_002d_002dlisten-243"></a><dd>Listen for actions on stdin. 
+</dl>
+<!-- ./../src/hid/lesstif/main.c 207 -->
+     <dl>
+<dt><code>--bg-image <string></code><a name="index-g_t_002d_002dbg_002dimage-_003cstring_003e-244"></a><dd>File name of an image to put into the background of the GUI canvas. The image must
+be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
+automatically scaled to fit the canvas. 
+</dl>
+   <!-- ./../src/hid/lesstif/main.c 213 -->
+     <dl>
+<u style="color:red;"><dt><code>--pcb-menu <string></code><a name="index-g_t_002d_002dpcb_002dmenu-_003cstring_003e-245"></a><dd>Location of the <samp><span class="file">pcb-menu.res</span></samp> file which defines the menu for the lesstif GUI. 
+</dl></u>
+   <!-- options Colors -->
+<div class="node">
+<a name="Colors"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Layer-Names">Layer Names</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-GUI-Options">lesstif GUI Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.5 Colors</h3>
+
+<!-- ./../src/main.c 473 -->
+     <dl>
+<dt><code>--black-color <string></code><a name="index-g_t_002d_002dblack_002dcolor-_003cstring_003e-246"></a><dd>Color value for black. Default: ‘<samp><span class="samp">#000000</span></samp>’
+</dl>
+<!-- ./../src/main.c 477 -->
+     <dl>
+<dt><code>--black-color <string></code><a name="index-g_t_002d_002dblack_002dcolor-_003cstring_003e-247"></a><dd>Color value for white. Default: ‘<samp><span class="samp">#ffffff</span></samp>’
+</dl>
+   <!-- ./../src/main.c 481 -->
+     <dl>
+<dt><code>--background-color <string></code><a name="index-g_t_002d_002dbackground_002dcolor-_003cstring_003e-248"></a><dd>Background color of the canvas. Default: ‘<samp><span class="samp">#e5e5e5</span></samp>’
+</dl>
+   <!-- ./../src/main.c 486 -->
+     <dl>
+<dt><code>--crosshair-color <string></code><a name="index-g_t_002d_002dcrosshair_002dcolor-_003cstring_003e-249"></a><dd>Color of the crosshair. Default: ‘<samp><span class="samp">#ff0000</span></samp>’
+</dl>
+   <!-- ./../src/main.c 491 -->
+     <dl>
+<dt><code>--cross-color <string></code><a name="index-g_t_002d_002dcross_002dcolor-_003cstring_003e-250"></a><dd>Color of the cross. Default: ‘<samp><span class="samp">#cdcd00</span></samp>’
+</dl>
+   <!-- ./../src/main.c 495 -->
+     <dl>
+<dt><code>--via-color <string></code><a name="index-g_t_002d_002dvia_002dcolor-_003cstring_003e-251"></a><dd>Color of vias. Default: ‘<samp><span class="samp">#7f7f7f</span></samp>’
+</dl>
+   <!-- ./../src/main.c 499 -->
+     <dl>
+<dt><code>--via-selected-color <string></code><a name="index-g_t_002d_002dvia_002dselected_002dcolor-_003cstring_003e-252"></a><dd>Color of selected vias. Default: ‘<samp><span class="samp">#00ffff</span></samp>’
+</dl>
+   <!-- ./../src/main.c 504 -->
+     <dl>
+<dt><code>--pin-color <string></code><a name="index-g_t_002d_002dpin_002dcolor-_003cstring_003e-253"></a><dd>Color of pins. Default: ‘<samp><span class="samp">#4d4d4d</span></samp>’
+</dl>
+   <!-- ./../src/main.c 508 -->
+     <dl>
+<dt><code>--pin-selected-color <string></code><a name="index-g_t_002d_002dpin_002dselected_002dcolor-_003cstring_003e-254"></a><dd>Color of selected pins. Default: ‘<samp><span class="samp">#00ffff</span></samp>’
+</dl>
+   <!-- ./../src/main.c 513 -->
+     <dl>
+<dt><code>--pin-name-color <string></code><a name="index-g_t_002d_002dpin_002dname_002dcolor-_003cstring_003e-255"></a><dd>Color of pin names and pin numbers. Default: ‘<samp><span class="samp">#ff0000</span></samp>’
+</dl>
+   <!-- ./../src/main.c 518 -->
+     <dl>
+<dt><code>--element-color <string></code><a name="index-g_t_002d_002delement_002dcolor-_003cstring_003e-256"></a><dd>Color of components. Default: ‘<samp><span class="samp">#000000</span></samp>’
+</dl>
+   <!-- ./../src/main.c 522 -->
+     <dl>
+<dt><code>--rat-color <string></code><a name="index-g_t_002d_002drat_002dcolor-_003cstring_003e-257"></a><dd>Color of ratlines. Default: ‘<samp><span class="samp">#b8860b</span></samp>’
+</dl>
+   <!-- ./../src/main.c 526 -->
+     <dl>
+<dt><code>--invisible-objects-color <string></code><a name="index-g_t_002d_002dinvisible_002dobjects_002dcolor-_003cstring_003e-258"></a><dd>Color of invisible objects. Default: ‘<samp><span class="samp">#cccccc</span></samp>’
+</dl>
+   <!-- ./../src/main.c 531 -->
+     <dl>
+<dt><code>--invisible-mark-color <string></code><a name="index-g_t_002d_002dinvisible_002dmark_002dcolor-_003cstring_003e-259"></a><dd>Color of invisible marks. Default: ‘<samp><span class="samp">#cccccc</span></samp>’
+</dl>
+   <!-- ./../src/main.c 536 -->
+     <dl>
+<dt><code>--element-selected-color <string></code><a name="index-g_t_002d_002delement_002dselected_002dcolor-_003cstring_003e-260"></a><dd>Color of selected components. Default: ‘<samp><span class="samp">#00ffff</span></samp>’
+</dl>
+   <!-- ./../src/main.c 541 -->
+     <dl>
+<dt><code>--rat-selected-color <string></code><a name="index-g_t_002d_002drat_002dselected_002dcolor-_003cstring_003e-261"></a><dd>Color of selected rats. Default: ‘<samp><span class="samp">#00ffff</span></samp>’
+</dl>
+   <!-- ./../src/main.c 546 -->
+     <dl>
+<dt><code>--connected-color <string></code><a name="index-g_t_002d_002dconnected_002dcolor-_003cstring_003e-262"></a><dd>Color to indicate connections. Default: ‘<samp><span class="samp">#00ff00</span></samp>’
+</dl>
+   <!-- ./../src/main.c 551 -->
+     <dl>
+<dt><code>--off-limit-color <string></code><a name="index-g_t_002d_002doff_002dlimit_002dcolor-_003cstring_003e-263"></a><dd>Color of off-canvas area. Default: ‘<samp><span class="samp">#cccccc</span></samp>’
+</dl>
+   <!-- ./../src/main.c 556 -->
+     <dl>
+<dt><code>--grid-color <string></code><a name="index-g_t_002d_002dgrid_002dcolor-_003cstring_003e-264"></a><dd>Color of the grid. Default: ‘<samp><span class="samp">#ff0000</span></samp>’
+</dl>
+   <!-- ./../src/main.c 560 -->
+     <dl>
+<dt><code>--layer-color-<n> <string></code><a name="index-g_t_002d_002dlayer_002dcolor_002d_003cn_003e-_003cstring_003e-265"></a><dd>Color of layer <code><n></code>, where <code><n></code> is an integer from 1 to 16. 
+</dl>
+   <!-- ./../src/main.c 578 -->
+     <dl>
+<dt><code>--layer-selected-color-<n> <string></code><a name="index-g_t_002d_002dlayer_002dselected_002dcolor_002d_003cn_003e-_003cstring_003e-266"></a><dd>Color of layer <code><n></code>, when selected. <code><n></code> is an integer from 1 to 16. 
+</dl>
+   <!-- ./../src/main.c 597 -->
+     <dl>
+<dt><code>--warn-color <string></code><a name="index-g_t_002d_002dwarn_002dcolor-_003cstring_003e-267"></a><dd>Color of offending objects during DRC. Default value is <code>"#ff8000"</code>
+</dl>
+   <!-- ./../src/main.c 601 -->
+     <dl>
+<dt><code>--mask-color <string></code><a name="index-g_t_002d_002dmask_002dcolor-_003cstring_003e-268"></a><dd>Color of the mask layer. Default value is <code>"#ff0000"</code>
+</dl>
+   <!-- options Layer Names -->
+<div class="node">
+<a name="Layer-Names"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Paths">Paths</a>,
+Previous: <a rel="previous" accesskey="p" href="#Colors">Colors</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.6 Layer Names</h3>
+
+<!-- ./../src/main.c 691 -->
+     <dl>
+<dt><code>--layer-name-1 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d1-_003cstring_003e-269"></a><dd>Name of the 1st Layer. Default is <code>"top"</code>. 
+</dl>
+<!-- ./../src/main.c 695 -->
+     <dl>
+<dt><code>--layer-name-2 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d2-_003cstring_003e-270"></a><dd>Name of the 2nd Layer. Default is <code>"ground"</code>. 
+</dl>
+   <!-- ./../src/main.c 699 -->
+     <dl>
+<dt><code>--layer-name-3 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d3-_003cstring_003e-271"></a><dd>Name of the 3nd Layer. Default is <code>"signal2"</code>. 
+</dl>
+   <!-- ./../src/main.c 703 -->
+     <dl>
+<dt><code>--layer-name-4 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d4-_003cstring_003e-272"></a><dd>Name of the 4rd Layer. Default is <code>"signal3"</code>. 
+</dl>
+   <!-- ./../src/main.c 707 -->
+     <dl>
+<dt><code>--layer-name-5 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d5-_003cstring_003e-273"></a><dd>Name of the 5rd Layer. Default is <code>"power"</code>. 
+</dl>
+   <!-- ./../src/main.c 711 -->
+     <dl>
+<dt><code>--layer-name-6 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d6-_003cstring_003e-274"></a><dd>Name of the 6rd Layer. Default is <code>"bottom"</code>. 
+</dl>
+   <!-- ./../src/main.c 715 -->
+     <dl>
+<dt><code>--layer-name-7 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d7-_003cstring_003e-275"></a><dd>Name of the 7rd Layer. Default is <code>"outline"</code>. 
+</dl>
+   <!-- ./../src/main.c 719 -->
+     <dl>
+<dt><code>--layer-name-8 <string></code><a name="index-g_t_002d_002dlayer_002dname_002d8-_003cstring_003e-276"></a><dd>Name of the 8rd Layer. Default is <code>"spare"</code>. 
+</dl>
+   <!-- options Paths -->
+<div class="node">
+<a name="Paths"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Sizes">Sizes</a>,
+Previous: <a rel="previous" accesskey="p" href="#Layer-Names">Layer Names</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.7 Paths</h3>
+
+<!-- ./../src/main.c 769 -->
+     <dl>
+<dt><code>--lib-newlib <string></code><a name="index-g_t_002d_002dlib_002dnewlib-_003cstring_003e-277"></a><dd>Top level directory for the newlib style library. 
+</dl>
+<!-- ./../src/main.c 778 -->
+     <dl>
+<dt><code>--lib-name <string></code><a name="index-g_t_002d_002dlib_002dname-_003cstring_003e-278"></a><dd>The default filename for the library. 
+</dl>
+   <!-- ./../src/main.c 783 -->
+     <dl>
+<dt><code>--default-font <string></code><a name="index-g_t_002d_002ddefault_002dfont-_003cstring_003e-279"></a><dd>The name of the default font. 
+</dl>
+   <!-- ./../src/main.c 794 -->
+     <dl>
+<dt><code>--file-path <string></code><a name="index-g_t_002d_002dfile_002dpath-_003cstring_003e-280"></a><dd>A colon separated list of directories or commands (starts with '|'). The path
+is passed to the program specified in <samp><span class="option">--file-command</span></samp> together with the selected
+filename. 
+</dl>
+   <!-- ./../src/main.c 802 -->
+     <dl>
+<dt><code>--font-path <string></code><a name="index-g_t_002d_002dfont_002dpath-_003cstring_003e-281"></a><dd>A colon separated list of directories to search the default font. Defaults to
+the default library path. 
+</dl>
+   <!-- ./../src/main.c 812 -->
+     <dl>
+<dt><code>--lib-path <string></code><a name="index-g_t_002d_002dlib_002dpath-_003cstring_003e-282"></a><dd>A colon separated list of directories that will be passed to the commands specified
+by <samp><span class="option">--element-command</span></samp> and <samp><span class="option">--element-contents-command</span></samp>. 
+</dl>
+   <!-- options Sizes -->
+<div class="node">
+<a name="Sizes"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Commands">Commands</a>,
+Previous: <a rel="previous" accesskey="p" href="#Paths">Paths</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.8 Sizes</h3>
+
+<!-- ./../src/main.c 606 -->
+<p>All parameters should be given with an unit. If no unit is given, 1/100 mil
+(cmil) will be used. Write units without space to the
+number like <code>3mm</code>, not <code>3 mm</code>. 
+Valid Units are:
+     <dl>
+   <dt>‘<samp><span class="samp">km</span></samp>’<dd>    Kilometer
+   <br><dt>‘<samp><span class="samp">m</span></samp>’<dd>    Meter
+   <br><dt>‘<samp><span class="samp">cm</span></samp>’<dd>    Centimeter
+   <br><dt>‘<samp><span class="samp">mm</span></samp>’<dd>    Millimeter
+   <br><dt>‘<samp><span class="samp">um</span></samp>’<dd>    Micrometer
+   <br><dt>‘<samp><span class="samp">nm</span></samp>’<dd>    Nanometer
+   <br><dt>‘<samp><span class="samp">in</span></samp>’<dd>    Inch (1in = 0.0254m)
+   <br><dt>‘<samp><span class="samp">mil</span></samp>’<dd>    Mil (1000mil = 1in)
+   <br><dt>‘<samp><span class="samp">cmil</span></samp>’<dd>    Centimil (1/100 mil)
+</dl>
+
+     <dl>
+<dt><code>--via-thickness <num></code><a name="index-g_t_002d_002dvia_002dthickness-_003cnum_003e-283"></a><dd>Default diameter of vias. Default value is <code>60mil</code>. 
+</dl>
+   <!-- ./../src/main.c 611 -->
+     <dl>
+<dt><code>--via-drilling-hole <num></code><a name="index-g_t_002d_002dvia_002ddrilling_002dhole-_003cnum_003e-284"></a><dd>Default diameter of holes. Default value is <code>28mil</code>. 
+</dl>
+   <!-- ./../src/main.c 616 -->
+     <dl>
+<dt><code>--line-thickness <num></code><a name="index-g_t_002d_002dline_002dthickness-_003cnum_003e-285"></a><dd>Default thickness of new lines. Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 621 -->
+     <dl>
+<dt><code>--rat-thickness <num></code><a name="index-g_t_002d_002drat_002dthickness-_003cnum_003e-286"></a><dd>Thickness of rats. Values from 1 to 19 are fixed width in screen pixels. 
+Anything larger means PCB units (i.e. 100 means "1 mil"). Default value
+is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 625 -->
+     <dl>
+<dt><code>--keepaway <num></code><a name="index-g_t_002d_002dkeepaway-_003cnum_003e-287"></a><dd>Default minimum distance between a track and adjacent copper. 
+Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 629 -->
+     <dl>
+<dt><code>--default-PCB-width <num></code><a name="index-g_t_002d_002ddefault_002dPCB_002dwidth-_003cnum_003e-288"></a><dd>Default width of the canvas. Default value is <code>6000mil</code>. 
+</dl>
+   <!-- ./../src/main.c 634 -->
+     <dl>
+<dt><code>--default-PCB-height <num></code><a name="index-g_t_002d_002ddefault_002dPCB_002dheight-_003cnum_003e-289"></a><dd>Default height of the canvas. Default value is <code>5000mil</code>. 
+</dl>
+   <!-- ./../src/main.c 639 -->
+     <dl>
+<dt><code>--text-scale <num></code><a name="index-g_t_002d_002dtext_002dscale-_003cnum_003e-290"></a><dd>Default text scale. This value is in percent. Default value is <code>100</code>. 
+</dl>
+   <!-- ./../src/main.c 643 -->
+     <dl>
+<dt><code>--alignment-distance <num></code><a name="index-g_t_002d_002dalignment_002ddistance-_003cnum_003e-291"></a><dd>Specifies the distance between the board outline and alignment targets. 
+Default value is <code>2mil</code>. 
+</dl>
+   <!-- ./../src/main.c 677 -->
+     <dl>
+<dt><code>--grid <num></code><a name="index-g_t_002d_002dgrid-_003cnum_003e-292"></a><dd>Initial grid size. Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 681 -->
+     <dl>
+<dt><code>--minimum polygon area <num></code><a name="index-g_t_002d_002dminimum-polygon-area-_003cnum_003e-293"></a><dd>Minimum polygon area. 
+</dl>
+   <!-- options Commands -->
+<div class="node">
+<a name="Commands"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DRC-Options">DRC Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#Sizes">Sizes</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.9 Commands</h3>
+
+<!-- ./../src/main.c 728 -->
+<p>pcb uses external commands for input output operations. These commands can be
+configured at start-up to meet local requirements. The command string may include
+special sequences <code>%f</code>, <code>%p</code> or <code>%a</code>. These are replaced when the
+command is called. The sequence <code>%f</code> is replaced by the file name,
+<code>%p</code> gets the path and <code>%a</code> indicates a package name. 
+<!-- ./../src/main.c 731 -->
+     <dl>
+<dt><code>--font-command <string></code><a name="index-g_t_002d_002dfont_002dcommand-_003cstring_003e-294"></a><dd>Command to load a font. 
+</dl>
+   <!-- ./../src/main.c 735 -->
+     <dl>
+<dt><code>--file-command <string></code><a name="index-g_t_002d_002dfile_002dcommand-_003cstring_003e-295"></a><dd>Command to read a file. 
+</dl>
+   <!-- ./../src/main.c 739 -->
+     <dl>
+<dt><code>--element-command <string></code><a name="index-g_t_002d_002delement_002dcommand-_003cstring_003e-296"></a><dd>Command to read a footprint. <br>
+Defaults to <code>"M4PATH='%p';export M4PATH;echo 'include(%f)' | m4"</code>
+</dl>
+   <!-- ./../src/main.c 745 -->
+     <dl>
+<dt><code>--print-file <string></code><a name="index-g_t_002d_002dprint_002dfile-_003cstring_003e-297"></a><dd>Command to print to a file. 
+</dl>
+   <!-- ./../src/main.c 749 -->
+     <dl>
+<dt><code>--lib-command-dir <string></code><a name="index-g_t_002d_002dlib_002dcommand_002ddir-_003cstring_003e-298"></a><dd>Path to the command that queries the library. 
+</dl>
+   <!-- ./../src/main.c 754 -->
+     <dl>
+<dt><code>--lib-command <string></code><a name="index-g_t_002d_002dlib_002dcommand-_003cstring_003e-299"></a><dd>Command to query the library. <br>
+Defaults to <code>"QueryLibrary.sh '%p' '%f' %a"</code>
+</dl>
+   <!-- ./../src/main.c 759 -->
+     <dl>
+<dt><code>--lib-contents-command <string></code><a name="index-g_t_002d_002dlib_002dcontents_002dcommand-_003cstring_003e-300"></a><dd>Command to query the contents of the library. <br>
+Defaults to <code>"ListLibraryContents.sh %p %f"</code> or,
+on Windows builds, an empty string (to disable this feature). 
+</dl>
+   <!-- ./../src/main.c 774 -->
+     <dl>
+<dt><code>--save-command <string></code><a name="index-g_t_002d_002dsave_002dcommand-_003cstring_003e-301"></a><dd>Command to save to a file. 
+</dl>
+   <!-- ./../src/main.c 798 -->
+     <dl>
+<dt><code>--rat-command <string></code><a name="index-g_t_002d_002drat_002dcommand-_003cstring_003e-302"></a><dd>Command for reading a netlist. Sequence <code>%f</code> is replaced by the netlist filename. 
+</dl>
+   <!-- options DRC Options -->
+<div class="node">
+<a name="DRC-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#BOM-Creation">BOM Creation</a>,
+Previous: <a rel="previous" accesskey="p" href="#Commands">Commands</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.10 DRC Options</h3>
+
+<!-- ./../src/main.c 648 -->
+<p>All parameters should be given with an unit. If no unit is given, 1/100 mil
+(cmil) will be used for backward compability. Valid units are given in section
+<a href="#Sizes">Sizes</a>. 
+<!-- ./../src/main.c 652 -->
+     <dl>
+<dt><code>--bloat <num></code><a name="index-g_t_002d_002dbloat-_003cnum_003e-303"></a><dd>Minimum spacing. Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 656 -->
+     <dl>
+<dt><code>--shrink <num></code><a name="index-g_t_002d_002dshrink-_003cnum_003e-304"></a><dd>Minimum touching overlap. Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 660 -->
+     <dl>
+<dt><code>--min-width <num></code><a name="index-g_t_002d_002dmin_002dwidth-_003cnum_003e-305"></a><dd>Minimum width of copper. Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 664 -->
+     <dl>
+<dt><code>--min-silk <num></code><a name="index-g_t_002d_002dmin_002dsilk-_003cnum_003e-306"></a><dd>Minimum width of lines in silk. Default value is <code>10mil</code>. 
+</dl>
+   <!-- ./../src/main.c 668 -->
+     <dl>
+<dt><code>--min-drill <num></code><a name="index-g_t_002d_002dmin_002ddrill-_003cnum_003e-307"></a><dd>Minimum diameter of holes. Default value is <code>15mil</code>. 
+</dl>
+   <!-- ./../src/main.c 672 -->
+     <dl>
+<dt><code>--min-ring <num></code><a name="index-g_t_002d_002dmin_002dring-_003cnum_003e-308"></a><dd>Minimum width of annular ring. Default value is <code>10mil</code>. 
+</dl>
+   <!-- options BOM Creation -->
+<div class="node">
+<a name="BOM-Creation"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Gerber-Export">Gerber Export</a>,
+Previous: <a rel="previous" accesskey="p" href="#DRC-Options">DRC Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.11 BOM Creation</h3>
+
+<!-- ./../src/hid/bom/bom.c 30 -->
+     <dl>
+<dt><code>--bomfile <string></code><a name="index-g_t_002d_002dbomfile-_003cstring_003e-309"></a><dd>Name of the BOM output file. 
+</dl>
+<!-- ./../src/hid/bom/bom.c 35 -->
+     <dl>
+<dt><code>--xyfile <string></code><a name="index-g_t_002d_002dxyfile-_003cstring_003e-310"></a><dd>Name of the XY output file. 
+</dl>
+   <!-- ./../src/hid/bom/bom.c 41 -->
+     <dl>
+<dt><code>--xy-unit <unit></code><a name="index-g_t_002d_002dxy_002dunit-_003cunit_003e-311"></a><dd>Unit of XY dimensions. Defaults to mil. 
+</dl>
+   <!-- options Gerber Export -->
+<div class="node">
+<a name="Gerber-Export"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Postscript-Export">Postscript Export</a>,
+Previous: <a rel="previous" accesskey="p" href="#BOM-Creation">BOM Creation</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.12 Gerber Export</h3>
+
+<!-- ./../src/hid/gerber/gerber.c 338 -->
+     <dl>
+<dt><code>--gerberfile <string></code><a name="index-g_t_002d_002dgerberfile-_003cstring_003e-312"></a><dd>Gerber output file prefix. Can include a path. 
+</dl>
+<!-- ./../src/hid/gerber/gerber.c 344 -->
+     <dl>
+<dt><code>--all-layers</code><a name="index-g_t_002d_002dall_002dlayers-313"></a><dd>Output contains all layers, even empty ones. 
+</dl>
+   <!-- ./../src/hid/gerber/gerber.c 350 -->
+     <dl>
+<dt><code>--verbose</code><a name="index-g_t_002d_002dverbose-314"></a><dd>Print file names and aperture counts on stdout. 
+</dl>
+   <!-- options Postscript Export -->
+<div class="node">
+<a name="Postscript-Export"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a>,
+Previous: <a rel="previous" accesskey="p" href="#Gerber-Export">Gerber Export</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.13 Postscript Export</h3>
+
+<!-- ./../src/hid/ps/ps.c 149 -->
+     <dl>
+<dt><code>--psfile <string></code><a name="index-g_t_002d_002dpsfile-_003cstring_003e-315"></a><dd>Name of the postscript output file. Can contain a path. 
+</dl>
+<!-- ./../src/hid/ps/ps.c 155 -->
+     
+<a name="index-drill_002dhelper-316"></a>
+<dl><dt><code>--drill-helper</code><a name="index-g_t_002d_002ddrill_002dhelper-317"></a><dd>Print a centering target in large drill holes. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 161 -->
+     
+<a name="index-align_002dmarks-318"></a>
+<dl><dt><code>--align-marks</code><a name="index-g_t_002d_002dalign_002dmarks-319"></a><dd>Print alignment marks on each sheet. This is meant to ease alignment during exposure. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 167 -->
+     <dl>
+<dt><code>--outline</code><a name="index-g_t_002d_002doutline-320"></a><dd>Print the contents of the outline layer on each sheet. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 172 -->
+     <dl>
+<dt><code>--mirror</code><a name="index-g_t_002d_002dmirror-321"></a><dd>Print mirror image. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 178 -->
+     <dl>
+<dt><code>--fill-page</code><a name="index-g_t_002d_002dfill_002dpage-322"></a><dd>Scale output to make the board fit the page. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 184 -->
+     <dl>
+<dt><code>--auto-mirror</code><a name="index-g_t_002d_002dauto_002dmirror-323"></a><dd>Print mirror image of appropriate layers. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 190 -->
+     <dl>
+<dt><code>--ps-color</code><a name="index-g_t_002d_002dps_002dcolor-324"></a><dd>Postscript output in color. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 196 -->
+     
+<a name="index-ps_002dbloat-325"></a>
+<dl><dt><code>--ps-bloat <num></code><a name="index-g_t_002d_002dps_002dbloat-_003cnum_003e-326"></a><dd>Amount to add to trace/pad/pin edges. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 202 -->
+     
+<a name="index-ps_002dinvert-327"></a>
+<dl><dt><code>--ps-invert</code><a name="index-g_t_002d_002dps_002dinvert-328"></a><dd>Draw objects as white-on-black. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 208 -->
+     <dl>
+<dt><code>--media <media-name></code><a name="index-g_t_002d_002dmedia-_003cmedia_002dname_003e-329"></a><dd>Size of the media, the postscript is fitted to. The parameter
+<code><media-name></code> can be any of the standard names for paper size: ‘<samp><span class="samp">A0</span></samp>’
+to ‘<samp><span class="samp">A10</span></samp>’, ‘<samp><span class="samp">B0</span></samp>’ to ‘<samp><span class="samp">B10</span></samp>’, ‘<samp><span class="samp">Letter</span></samp>’, ‘<samp><span class="samp">11x17</span></samp>’,
+‘<samp><span class="samp">Ledger</span></samp>’, ‘<samp><span class="samp">Legal</span></samp>’, ‘<samp><span class="samp">Executive</span></samp>’, ‘<samp><span class="samp">A-Size</span></samp>’, ‘<samp><span class="samp">B-size</span></samp>’,
+‘<samp><span class="samp">C-Size</span></samp>’, ‘<samp><span class="samp">D-size</span></samp>’, ‘<samp><span class="samp">E-size</span></samp>’, ‘<samp><span class="samp">US-Business_Card</span></samp>’,
+‘<samp><span class="samp">Intl-Business_Card</span></samp>’. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 214 -->
+     
+<a name="index-psfade-330"></a>
+<dl><dt><code>--psfade <num></code><a name="index-g_t_002d_002dpsfade-_003cnum_003e-331"></a><dd>Fade amount for assembly drawings (0.0=missing, 1.0=solid). 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 220 -->
+     <dl>
+<dt><code>--scale <num></code><a name="index-g_t_002d_002dscale-_003cnum_003e-332"></a><dd>Scale value to compensate for printer sizing errors (1.0 = full scale). 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 226 -->
+     
+<a name="index-multi_002dfile-333"></a>
+<dl><dt><code>--multi-file</code><a name="index-g_t_002d_002dmulti_002dfile-334"></a><dd>Produce multiple files, one per page, instead of a single multi page file. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 232 -->
+     <dl>
+<dt><code>--xcalib <num></code><a name="index-g_t_002d_002dxcalib-_003cnum_003e-335"></a><dd>Paper width. Used for x-Axis calibration. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 238 -->
+     <dl>
+<dt><code>--ycalib <num></code><a name="index-g_t_002d_002dycalib-_003cnum_003e-336"></a><dd>Paper height. Used for y-Axis calibration. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 244 -->
+     <dl>
+<dt><code>--drill-copper</code><a name="index-g_t_002d_002ddrill_002dcopper-337"></a><dd>Draw drill holes in pins / vias, instead of leaving solid copper. 
+</dl>
+   <!-- ./../src/hid/ps/ps.c 250 -->
+     
+<a name="index-show_002dlegend-338"></a>
+<dl><dt><code>--show-legend</code><a name="index-g_t_002d_002dshow_002dlegend-339"></a><dd>Print file name and scale on printout. 
+</dl>
+   <!-- options Encapsulated Postscript Export -->
+<div class="node">
+<a name="Encapsulated-Postscript-Export"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#PNG-Options">PNG Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#Postscript-Export">Postscript Export</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.14 Encapsulated Postscript Export</h3>
+
+<!-- ./../src/hid/ps/eps.c 78 -->
+     <dl>
+<dt><code>--eps-file <string></code><a name="index-g_t_002d_002deps_002dfile-_003cstring_003e-340"></a><dd>Name of the encapsulated postscript output file. Can contain a path. 
+</dl>
+<!-- ./../src/hid/ps/eps.c 84 -->
+     <dl>
+<dt><code>--eps-scale <num></code><a name="index-g_t_002d_002deps_002dscale-_003cnum_003e-341"></a><dd>Scale EPS output by the parameter ‘<samp><span class="samp">num</span></samp>’. 
+</dl>
+   <!-- ./../src/hid/ps/eps.c 90 -->
+     
+<a name="index-as_002dshown-_0028EPS_0029-342"></a>
+<dl><dt><code>--as-shown</code><a name="index-g_t_002d_002das_002dshown-343"></a><dd>Export layers as shown on screen. 
+</dl>
+   <!-- ./../src/hid/ps/eps.c 96 -->
+     <dl>
+<dt><code>--monochrome</code><a name="index-g_t_002d_002dmonochrome-344"></a><dd>Convert output to monochrome. 
+</dl>
+   <!-- ./../src/hid/ps/eps.c 102 -->
+     
+<a name="index-only_002dvisible-345"></a>
+<dl><dt><code>--only-visible</code><a name="index-g_t_002d_002donly_002dvisible-346"></a><dd>Limit the bounds of the EPS file to the visible items. 
+</dl>
+   <!-- options PNG Options -->
+<div class="node">
+<a name="PNG-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lpr-Printing-Options">lpr Printing Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.15 PNG Options</h3>
+
+<!-- ./../src/hid/png/png.c 168 -->
+     <dl>
+<dt><code>--outfile <string></code><a name="index-g_t_002d_002doutfile-_003cstring_003e-347"></a><dd>Name of the file to be exported to. Can contain a path. 
+</dl>
+<!-- ./../src/hid/png/png.c 174 -->
+     <dl>
+<dt><code>--dpi</code><a name="index-g_t_002d_002ddpi-348"></a><dd>Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout. 
+</dl>
+   <!-- ./../src/hid/png/png.c 180 -->
+     <dl>
+<dt><code>--x-max</code><a name="index-g_t_002d_002dx_002dmax-349"></a><dd>Width of the png image in pixels. No constraint, when set to 0. 
+</dl>
+   <!-- ./../src/hid/png/png.c 186 -->
+     <dl>
+<dt><code>--y-max</code><a name="index-g_t_002d_002dy_002dmax-350"></a><dd>Height of the png output in pixels. No constraint, when set to 0. 
+</dl>
+   <!-- ./../src/hid/png/png.c 192 -->
+     <dl>
+<dt><code>--xy-max</code><a name="index-g_t_002d_002dxy_002dmax-351"></a><dd>Maximum width and height of the PNG output in pixels. No constraint, when set to 0. 
+</dl>
+   <!-- ./../src/hid/png/png.c 198 -->
+     <dl>
+<dt><code>--as-shown</code><a name="index-g_t_002d_002das_002dshown-352"></a><dd>Export layers as shown on screen. 
+</dl>
+   <!-- ./../src/hid/png/png.c 204 -->
+     <dl>
+<dt><code>--monochrome</code><a name="index-g_t_002d_002dmonochrome-353"></a><dd>Convert output to monochrome. 
+</dl>
+   <!-- ./../src/hid/png/png.c 210 -->
+     <dl>
+<dt><code>--only-vivible</code><a name="index-g_t_002d_002donly_002dvivible-354"></a><dd>Limit the bounds of the exported PNG image to the visible items. 
+</dl>
+   <!-- ./../src/hid/png/png.c 216 -->
+     <dl>
+<dt><code>--use-alpha</code><a name="index-g_t_002d_002duse_002dalpha-355"></a><dd>Make the background and any holes transparent. 
+</dl>
+   <!-- ./../src/hid/png/png.c 222 -->
+     <dl>
+<dt><code>--format <string></code><a name="index-g_t_002d_002dformat-_003cstring_003e-356"></a><dd>File format to be exported. Parameter <code><string></code> can be ‘<samp><span class="samp">PNG</span></samp>’,
+‘<samp><span class="samp">GIF</span></samp>’, or ‘<samp><span class="samp">JPEG</span></samp>’. 
+</dl>
+   <!-- ./../src/hid/png/png.c 228 -->
+     <dl>
+<dt><code>--png-bloat <num><dim></code><a name="index-g_t_002d_002dpng_002dbloat-_003cnum_003e_003cdim_003e-357"></a><dd>Amount of extra thickness to add to traces, pads, or pin edges. The parameter
+‘<samp><span class="samp"><num><dim></span></samp>’ is a number, appended by a dimension ‘<samp><span class="samp">mm</span></samp>’, ‘<samp><span class="samp">mil</span></samp>’, or
+‘<samp><span class="samp">pix</span></samp>’. If no dimension is given, the default dimension is 1/100 mil. 
+</dl>
+   <!-- ./../src/hid/png/png.c 234 -->
+     
+<a name="index-photo_002dmode-358"></a>
+<dl><dt><code>--photo-mode</code><a name="index-g_t_002d_002dphoto_002dmode-359"></a><dd>Export a photo realistic image of the layout. 
+</dl>
+   <!-- ./../src/hid/png/png.c 240 -->
+     <dl>
+<dt><code>--photo-flip-x</code><a name="index-g_t_002d_002dphoto_002dflip_002dx-360"></a><dd>In photo-realistic mode, export the reverse side of the layout. Left-right flip. 
+</dl>
+   <!-- ./../src/hid/png/png.c 246 -->
+     <dl>
+<dt><code>--photo-flip-y</code><a name="index-g_t_002d_002dphoto_002dflip_002dy-361"></a><dd>In photo-realistic mode, export the reverse side of the layout. Up-down flip. 
+</dl>
+   <!-- options lpr Printing Options -->
+<div class="node">
+<a name="lpr-Printing-Options"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#nelma-Options">nelma Options</a>,
+Previous: <a rel="previous" accesskey="p" href="#PNG-Options">PNG Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.16 lpr Printing Options</h3>
+
+<!-- ./../src/hid/lpr/lpr.c 33 -->
+     <dl>
+<dt><code>--lprcommand <string></code><a name="index-g_t_002d_002dlprcommand-_003cstring_003e-362"></a><dd>Command to use for printing. Defaults to <code>lpr</code>. This can be used to produce
+PDF output with a virtual PDF printer. Example: <br>
+<code>--lprcommand "lp -d CUPS-PDF-Printer"</code>. 
+</dl>
+In addition, all <a href="#Postscript-Export">Postscript Export</a> options are valid. 
+<!-- options nelma Options -->
+<div class="node">
+<a name="nelma-Options"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#lpr-Printing-Options">lpr Printing Options</a>,
+Up: <a rel="up" accesskey="u" href="#Command_002dLine-Options">Command-Line Options</a>
+
+</div>
+
+<h3 class="section">6.17 nelma Options</h3>
+
+<!-- ./../src/hid/nelma/nelma.c 157 -->
+     <dl>
+<dt><code>-- basename <string></code><a name="index-g_t_002d_002d-basename-_003cstring_003e-363"></a><dd>File name prefix. 
+</dl>
+<!-- ./../src/hid/nelma/nelma.c 163 -->
+     <dl>
+<dt><code>--dpi <num></code><a name="index-g_t_002d_002ddpi-_003cnum_003e-364"></a><dd>Horizontal scale factor (grid points/inch). 
+</dl>
+   <!-- ./../src/hid/nelma/nelma.c 169 -->
+     <dl>
+<dt><code>--copper-height <num></code><a name="index-g_t_002d_002dcopper_002dheight-_003cnum_003e-365"></a><dd>Copper layer height (um). 
+</dl>
+   <!-- ./../src/hid/nelma/nelma.c 175 -->
+     <dl>
+<dt><code>--substrate-height <num></code><a name="index-g_t_002d_002dsubstrate_002dheight-_003cnum_003e-366"></a><dd>Substrate layer height (um). 
+</dl>
+   <!-- ./../src/hid/nelma/nelma.c 181 -->
+     <dl>
+<dt><code>--substrate-epsilon <num></code><a name="index-g_t_002d_002dsubstrate_002depsilon-_003cnum_003e-367"></a><dd>Substrate relative epsilon. 
+</dl>
+
+<!--  chapter 5 - -->
+<div class="node">
+<a name="X11-Interface"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#File-Formats">File Formats</a>,
+Previous: <a rel="previous" accesskey="p" href="#Command_002dLine-Options">Command-Line Options</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">6 X11 Interface</h2>
+
+<p><a name="index-X11-368"></a>
+This chapter gives an overview about the additional <code>X11</code> resources which
+are defined by <code>pcb-rnd</code> as well as the defined action routines.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Resources">Resources</a>:       Non-standard <code>X11</code> application resources. 
+<li><a accesskey="2" href="#Actions">Actions</a>:         A list of available action routines. 
+<li><a accesskey="3" href="#Translations">Translations</a>:    A list of the default key translations (as shipped). 
+</ul>
+
+<div class="node">
+<a name="Resources"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Actions">Actions</a>,
+Up: <a rel="up" accesskey="u" href="#X11-Interface">X11 Interface</a>
+
+</div>
+
+<h3 class="section">7.1 Non-Standard X11 Application Resources</h3>
+
+<p><a name="index-resources-369"></a><a name="index-X11-resources-370"></a>
+In addition to the toolkit resources, <code>pcb-rnd</code> defines the
+following resources:
+
+     
+<a name="index-absoluteGrid-371"></a>
+<a name="index-grid-372"></a>
+<dl><dt>‘<samp><span class="samp">absoluteGrid (boolean)</span></samp>’<dd>Selects if either the grid is relative to the position where it has changed
+last or absolute, the default, to the origin (0,0).
+
+     <p><a name="index-alignmentDistance-373"></a><a name="index-alignment-374"></a><br><dt>‘<samp><span class="samp">alignmentDistance (dimension)</span></samp>’<dd>Specifies the distance between the boards outline to the alignment targets.
+
+     <p><a name="index-allDirectionLines-375"></a><a name="index-lines_002c-clipping-to-45-degree-376"></a><a name="index-clipping-lines-to-45-degree-377"></a><br><dt>‘<samp><span class="samp">allDirectionLines (boolean)</span></samp>’<dd>Enables (default) or disables clipping of new lines to 45 degree angles.
+
+     <p><a name="index-backgroundImage-378"></a><a name="index-background-379"></a><br><dt>‘<samp><span class="samp">backgroundImage (string)</span></samp>’<dd>If specified, this image will be drawn as the background for the
+board.  The purpose of this option is to allow you to use a scan of an
+existing layout as a prototype for your new layout.  To do this, there
+are some limitations as to what this image must be.  The image must be
+a PPM binary image (magic number ‘<samp><span class="samp">P6</span></samp>’).  It must have a maximum
+pixel value of 255 or less (i.e. no 16-bit images).  It must represent
+the entire board, as it will be scaled to fit the board dimensions
+exactly.  Note that it may be scaled unevenly if the image doesn't
+have the same aspect ratio of your board.  You must ensure that the
+image does not use more colors than are available on your system
+(mostly this is for pseudo-color displays, like old 8-bit displays). 
+<u style="color:red;"><u>For best results, I suggest the following procedure using The Gimp:</u></u>
+Load your image (any type).  Image->Scale if needed. 
+Image->Colors->Curves and for each of Red, Green, and Blue channel
+move the lower left point up to about the 3/4 line (value 192).  This
+will make your image pale so it doesn't interfere with the traces
+you'll be adding.  Image->Mode->Indexed and select, say, 32 colors
+with Normal F-S dithering.  File->Save As, file type by extension,
+use <samp><span class="file">.ppm</span></samp> as the extension.  Select Raw formatting.
+
+     <p><a name="index-backupInterval-380"></a><a name="index-backup-381"></a><br><dt>‘<samp><span class="samp">backupInterval (int)</span></samp>’<dd><code>pcb-rnd</code> has an automatic backup feature which saves the current data
+every n seconds. The default is <em>300</em> seconds. A value of zero disables
+the feature. The backup file is named <samp><span class="file">/tmp/PCB.%i.backup</span></samp> by
+default (this may have been changed at compilation time via the
+<code>BACKUP_NAME</code>
+variable in <samp><span class="file">globalconfig.h</span></samp>). 
+<em>%i</em> is replaced by the process ID. 
+See also, the command-line option <em>–backup-interval</em>.
+
+     <p><a name="index-bloat-382"></a><a name="index-bloat-383"></a><a name="index-drc-384"></a><br><dt>‘<samp><span class="samp">Bloat (dimension)</span></samp>’<dd>Specifies the minimum spacing design rule in mils.
+
+     <p><a name="index-connectedColor-385"></a><a name="index-colors-386"></a><a name="index-connections_002c-colors-387"></a><br><dt>‘<samp><span class="samp">connectedColor (color)</span></samp>’<dd>All pins, vias, lines and rectangles which are selected during a connection
+search are drawn with this color. The default value is determined by
+<em>XtDefaultForeground</em>.
+
+     <p><a name="index-cross-hairColor-388"></a><a name="index-colors-389"></a><a name="index-cursor-color-390"></a><br><dt>‘<samp><span class="samp">cross hairColor (color)</span></samp>’<dd>This color is used to draw the cross hair cursor. The color is a result of
+a <em>XOR</em> operation with the contents of the Layout area. The result
+also depends on the default colormap of the <code>X11</code> server because only
+the colormap index is used in the boolean operation and <code>pcb-rnd</code> doesn't
+create its own colormap. The default setting is <em>XtDefaultForeground</em>.
+
+     <p><a name="index-elementColor-391"></a><a name="index-elementSelectedColor-392"></a><a name="index-colors-393"></a><a name="index-element_002c-color-394"></a><br><dt>‘<samp><span class="samp">elementColor (color)</span></samp>’<dt>‘<samp><span class="samp">elementSelectedColor (color)</span></samp>’<dd>The elements package part is drawn in these colors, for normal and selected
+mode, respectively, which both default to <em>XtDefaultForeground</em>.
+
+     <p><a name="index-elementCommand-395"></a><a name="index-element_002c-command-396"></a><a name="index-element_002c-files-397"></a><a name="index-loading-elements-398"></a><a name="index-preprocessing-element-data-399"></a><a name="index-unix-command-400"></a><a name="index-m4-401"></a><br><dt>‘<samp><span class="samp">elementCommand (string)</span></samp>’<dd><code>pcb-rnd</code> uses a user defined command to read element files. This resources
+is used to set the command which is executed by the users default shell. 
+Two escape sequences are defined to pass the selected filename (%f) and the
+current search path (%p). The command must write the element data
+to its standard output. The default value is
+     <pre class="example">              M4PATH="%p";export M4PATH;echo 'include(%f)' | m4
+</pre>
+     <p>Using the GNU version of <code>m4</code> is highly recommended. 
+See also, the command-line option <em>–element-command</em>.
+
+     <p><a name="index-elementPath-402"></a><a name="index-searchpath-for-element-files-403"></a><a name="index-path-for-element-files-404"></a><a name="index-element_002c-files-405"></a><a name="index-loading-elements-406"></a><br><dt>‘<samp><span class="samp">elementPath (string)</span></samp>’<dd>A colon separated list of directories or commands (starts with '|'). 
+The path is passed to the program specified in <em>elementCommand</em> together
+with the selected element name. A specified command will be executed in order
+to create entries for the fileselect box. It must write its results to
+<em>stdout</em> one entry per line. 
+See also, the user-command <em>le[!]</em>.
+
+     <p><a name="index-fileCommand-407"></a><a name="index-file-load-command-408"></a><a name="index-layout-files-409"></a><a name="index-loading-layouts-410"></a><a name="index-preprocessing-layout-data-411"></a><a name="index-unix-command-412"></a><a name="index-cat-413"></a><br><dt>‘<samp><span class="samp">fileCommand (string)</span></samp>’<dd>The command is executed by the user's default shell whenever existing layout
+files are loaded. Data is read from the command's standard output. 
+Two escape sequences may be specified to pass the selected filename (%f)
+and the current search path (%p). The default value is:
+     <pre class="example">              cat %f
+</pre>
+     <p>See also, the command-line option <em>–file-command</em>.
+
+     <p><a name="index-filePath-414"></a><a name="index-searchpath-for-layout-files-415"></a><a name="index-path-for-layout-files-416"></a><a name="index-layout-files-417"></a><a name="index-loading-layouts-418"></a><br><dt>‘<samp><span class="samp">filePath (string)</span></samp>’<dd>A colon separated list of directories or commands (starts with '|'). 
+The path is passed to the program specified in <em>fileCommand</em> together
+with the selected filename. A specified command will be executed in order
+to create entries for the fileselect box. It must write its results to
+<em>stdout</em> one entry per line. 
+See also, the user-command <em>l[!]</em>.
+
+     <p><a name="index-fontCommand-419"></a><a name="index-font-command-420"></a><a name="index-font-files-421"></a><a name="index-loading-fonts-422"></a><a name="index-loading-symbols-423"></a><a name="index-preprocessing-font-data-424"></a><a name="index-unix-command-425"></a><a name="index-cat-426"></a><br><dt>‘<samp><span class="samp">fontCommand (string)</span></samp>’<dd>Loading new symbol sets also is handled by an external command. You again
+may pass the selected filename and the current search path by passing
+%f and %p in the command string. Data is read from the commands standard
+output. This command defaults to
+     <pre class="example">              cat %f
+</pre>
+     <p>See also, the command-line option <em>–font-command</em>.
+
+     <p><a name="index-fontFile-427"></a><a name="index-default-font-428"></a><a name="index-symbols-429"></a><br><dt>‘<samp><span class="samp">fontFile (string)</span></samp>’<dd>The default font for new layouts is read from this file which is searched
+in the directories as defined by the resource <em>fontPath</em>. 
+Searching is only performed if the filename does not contain a directory
+component. 
+The default filename is <samp><span class="file">default_font</span></samp>.
+
+     <p><a name="index-fontPath-430"></a><a name="index-searchpath-for-font-files-431"></a><a name="index-path-for-font-files-432"></a><a name="index-font-files-433"></a><a name="index-loading-fonts-434"></a><a name="index-loading-symbols-435"></a><br><dt>‘<samp><span class="samp">fontPath (string)</span></samp>’<dd>This resource, a colon separated list of directories, defines the searchpath
+for font files. See also, the resource <em>fontFile</em>.
+
+     <p><a name="index-grid-436"></a><a name="index-grid-437"></a><a name="index-cursor-steps-438"></a><br><dt>‘<samp><span class="samp">grid (int)</span></samp>’<dd>This resources defines the initial value of one cursor step. It defaults
+to <em>100 mil</em> and any changes are saved together with the layout data.
+
+     <p><a name="index-gridColor-439"></a><a name="index-colors-440"></a><a name="index-grid-color-441"></a><br><dt>‘<samp><span class="samp">gridColor (color)</span></samp>’<dd>This color is used to draw the grid. The color is a result of
+a <em>INVERT</em> operation with the contents of the Layout area. The result
+also depends on the default colormap of the <code>X11</code> server because only
+the colormap index is used in the boolean operation and <code>pcb-rnd</code> doesn't
+create its own colormap. The default setting is <em>XtDefaultForeground</em>.
+
+     <p><a name="index-invisibleObjectsColor-442"></a><a name="index-colors-443"></a><a name="index-element_002c-color-444"></a><br><dt>‘<samp><span class="samp">invisibleObjectsColor (color)</span></samp>’<dd>Elements located on the opposite side of the board are drawn in this color. 
+The default is <em>XtDefaultForeground</em>.
+
+     <p><a name="index-layerColor-445"></a><a name="index-layerSelectedColor-446"></a><a name="index-colors-447"></a><a name="index-layers_002c-colors-448"></a><br><dt>‘<samp><span class="samp">layerColor1..MAX_LAYER (color)</span></samp>’<dt>‘<samp><span class="samp">layerSelectedColor1..MAX_LAYER (color)</span></samp>’<dd>These resources define the drawing colors of the different layers in
+normal and selected state. All values are preset to <em>XtDefaultForeground</em>.
+
+     <p><a name="index-layerGroups-449"></a><a name="index-layers_002c-groups-450"></a><a name="index-groups-451"></a><br><dt>‘<samp><span class="samp">layerGroups (string)</span></samp>’<dd>The argument to this resource is a colon separated list of comma separated
+layer numbers (1..MAX_LAYER). All layers within one group are switched on/off
+together. The default setting is <em>1:2:3:...:MAX_LAYER</em> which means
+all layers are handled separately. Grouping layers one to three looks like
+<em>1,2,3:4:...:MAX_LAYER</em>
+
+     <p><a name="index-layerName-452"></a><a name="index-layer_002c-name-of-453"></a><br><dt>‘<samp><span class="samp">layerName1..MAX_LAYER (string)</span></samp>’<dd>The default name of the layers in a new layout are determined by these
+resources. The defaults are empty strings.
+
+     <p><a name="index-libraryCommand-454"></a><a name="index-library-command-455"></a><a name="index-loading-elements-456"></a><a name="index-unix-command-457"></a><br><dt>‘<samp><span class="samp">libraryCommand (string)</span></samp>’<dd><code>pcb-rnd</code> uses a command to read element data from libraries. 
+The resources is used to set the command which is executed by the users
+default shell.  Three escape sequences are defined to pass the selected
+filename (%f), the current search path (%p) as well (%a) as the three
+parameters <em>template</em>, <em>value</em> and <em>package</em> to the command. 
+It must write the element data to its standard output. The default value is
+     <pre class="example">              NONE/share/pcb/oldlib/QueryLibrary.sh %p %f %a
+</pre>
+     <p><a name="index-elementContentsCommand-458"></a><a name="index-library-contents-command-459"></a><a name="index-listing-library-contents-460"></a><a name="index-unix-command-461"></a><br><dt>‘<samp><span class="samp">libraryContentsCommand (string)</span></samp>’<dd>Similar to <em>libraryCommand</em>, <code>pcb-rnd</code> uses the command specified
+by this resource to list the contents of a library.
+     <pre class="example">          	NONE/share/pcb/oldlib/ListLibraryContents.sh %p %f
+</pre>
+     <p>is the default.
+
+     <p><a name="index-libraryFilename-462"></a><a name="index-default-library-463"></a><a name="index-library-name-464"></a><br><dt>‘<samp><span class="samp">libraryFilename (string)</span></samp>’<dd>The resource specifies the name of the library. The default value is
+<em>pcblib</em> unless changed at compile time
+with the <code>LIBRARYFILENAME</code> variable in <samp><span class="file">globalconfig.h</span></samp>.
+
+     <p><a name="index-libraryPath-465"></a><a name="index-searchpath-for-libraries-466"></a><a name="index-path-for-libraries-467"></a><a name="index-library-searchpath-468"></a><br><dt>‘<samp><span class="samp">libraryPath (string)</span></samp>’<dd>A colon separated list of directories that will be passed to the commands
+specified by <em>elementCommand</em> and <em>elementContentsCommand</em>.
+
+     <p><a name="index-lineThickness-469"></a><a name="index-lines_002c-size-470"></a><a name="index-size-of-lines-471"></a><a name="index-thickness-of-lines-472"></a><br><dt>‘<samp><span class="samp">lineThickness (dimension)</span></samp>’<dd>The value, in the range [1..250] (the range may be changed at compile
+time with the <code>MIN_LINESIZE</code> and <code>MAX_LINESIZE</code> variables in
+<samp><span class="file">globalconfig.h</span></samp>), defines the
+initial thickness of new lines. The value is preset to <em>ten mil</em>.
+
+     <p><a name="index-media-473"></a><a name="index-media-474"></a><a name="index-media-margin-475"></a><a name="index-print-media-476"></a><br><dt>‘<samp><span class="samp">media (<predefined> | <width>x<height>+-<left_margin>+-<top_margin>)</span></samp>’<dd>The default (user defined) media of the <code>PostScript</code> device. Predefined
+values are <em>a3</em>, <em>a4</em>, <em>a5</em>, <em>letter</em>, <em>tabloit</em>,
+<em>ledger</em>, <em>legal</em>, and <em>executive</em>. 
+The second way is to specify the medias width, height and margins in mil. 
+The resource defaults to <em>a4</em> size unless changed at compile time
+with the <code>DEFAULT_MEDIASIZE</code> variable in <samp><span class="file">globalconfig.h</span></samp>.
+
+     <p><a name="index-offLimitColor-477"></a><a name="index-colors-478"></a><a name="index-off-limit-color-479"></a><br><dt>‘<samp><span class="samp">offLimitColor (color)</span></samp>’<dd>The area outside the current maximum settings for width and height is drawn
+with this color. The default value is determined by <em>XtDefaultBackground</em>.
+
+     <p><a name="index-pinColor-480"></a><a name="index-pinSelectedColor-481"></a><a name="index-colors-482"></a><a name="index-pin-color-483"></a><br><dt>‘<samp><span class="samp">pinColor (color)</span></samp>’<dt>‘<samp><span class="samp">pinSelectedColor(color)</span></samp>’<dd>This resource defines the drawing color of pins and pads in both states. 
+The values are preset to <em>XtDefaultForeground</em>.
+
+     <p><a name="index-pinoutFont0_002e_002e6-484"></a><a name="index-font_002c-used-for-pin-names-485"></a><a name="index-pinout_002c-font-to-display-pin-names-486"></a><br><dt>‘<samp><span class="samp">pinoutFont (string)</span></samp>’<dd>This fonts are used to display pin names. There is one font for each zoom
+value. The values are preset to <em>XtdefaultFont</em>.
+
+     <p><a name="index-pinoutNameLength-487"></a><a name="index-namelength-of-pins-488"></a><a name="index-pin_002c-name-of-489"></a><a name="index-length-of-a-pin-name-490"></a><br><dt>‘<samp><span class="samp">pinoutNameLength (int)</span></samp>’<dd>This resource limits the number of characters which are displayed for
+pin names in the pinout window. By default the string length is limited
+to <em>eight</em> characters per name.
+
+     <p><a name="index-pinoutOffsetX-491"></a><a name="index-pinoutOffsetY-492"></a><a name="index-offset-of-pinout-493"></a><br><dt>‘<samp><span class="samp">pinoutOffsetX (int)</span></samp>’<dt>‘<samp><span class="samp">pinoutOffsetY (int)</span></samp>’<dd>These resources determine the offset in <em>mil</em> of the circuit from the
+upper left corner of the window when displaying pinout information. 
+Both default to <em>100 mil</em>.
+
+     <p><a name="index-pinoutTextOffsetX-494"></a><a name="index-pinoutTextOffsetY-495"></a><a name="index-offset-of-pinnames-496"></a><br><dt>‘<samp><span class="samp">pinoutTextOffsetX (int)</span></samp>’<dt>‘<samp><span class="samp">pinoutTextOffsetY (int)</span></samp>’<dd>The resources determine the distance in mil between the drilling hole of a pin
+to the location where its name is displayed in the pinout window. 
+They default to <em>X:50</em> and <em>Y:0</em>.
+
+     <p><a name="index-pinoutZoom-497"></a><a name="index-pinout_002c-zoomfactor-of-display-498"></a><a name="index-zoom-of-pinout-window-499"></a><br><dt>‘<samp><span class="samp">pinoutZoom (int)</span></samp>’<dd>Sets the zoom factor for the pinout window according to the formula:
+scale = 1:(2 power value). Its default value is <em>two</em> which results in
+a <em>1:4</em> scale.
+
+     <p><a name="index-printCommand-500"></a><a name="index-printing-501"></a><br><dt>‘<samp><span class="samp">printCommand (string)</span></samp>’<dd>Default file for printouts. If the name starts with a '|' the output
+is piped through the command. A %f is replaced by the current filename. 
+There is no default file or command.
+
+     <p><a name="index-raiseLogWindow-502"></a><a name="index-log-window-503"></a><a name="index-messages-504"></a><br><dt>‘<samp><span class="samp">raiseLogWindow (boolean)</span></samp>’<dd>The log window will be raised when new messages arrive if this resource
+is set <em>true</em>, the default.
+
+     <p><a name="index-ratCommand-505"></a><a name="index-rats-nest-506"></a><a name="index-netlist-507"></a><br><dt>‘<samp><span class="samp">ratCommand (string)</span></samp>’<dd>Default command for reading a netlist. A %f is replaced by the netlist
+filename. Its default value is "<em>cat %f</em>".
+
+     <p><a name="index-ratPath-508"></a><a name="index-rats-nest-509"></a><a name="index-netlist-510"></a><br><dt>‘<samp><span class="samp">ratPath (string)</span></samp>’<dd>Default path to look for netlist files. It's default value is "."
+
+     <p><a name="index-resetAfterElement-511"></a><a name="index-connections_002c-reseting-after-element-512"></a><a name="index-reseting-found-connections-513"></a><br><dt>‘<samp><span class="samp">resetAfterElement (boolean)</span></samp>’<dd>If set to <em>true</em>, all found connections will be reset before a new
+element is scanned. This will produce long lists when scanning the whole
+layout for connections. The resource is set to <em>false</em> by default. 
+The feature is only used while looking up connections of all elements.
+
+     <p><a name="index-ringBellWhenFinished-514"></a><a name="index-keyboard-bell-515"></a><br><dt>‘<samp><span class="samp">ringBellWhenFinished (boolean)</span></samp>’<dd>Whether to ring the bell (the default) when a possibly lengthy operation
+has finished or not. 
+See also, the command-line option <em>–ring-bell-finished</em>.
+
+     <p><a name="index-routeStyle-516"></a><a name="index-routing-style-517"></a><br><dt>‘<samp><span class="samp">routeStyle (string)</span></samp>’<dd>Default values for the menu of routing styles (seen in the sizes menu). 
+The string is a comma separated list of name, line thickness,
+via diameter, and via drill size. 
+e.g. "Fat,50,100,40:Skinny,8,35,20:75Ohm,110,110,20"
+See also, the command-line option <em>–route-styles</em> and <em>Sizes Menu</em>
+
+     <p><a name="index-rubberBandMode-518"></a><a name="index-move-519"></a><a name="index-rubberband-520"></a><a name="index-rotate-521"></a><br><dt>‘<samp><span class="samp">rubberBandMode (boolean)</span></samp>’<dd>Whether rubberband move and rotate (attached lines stretch like
+rubberbands) is enabled (the default).
+
+     <p><a name="index-saveCommand-522"></a><a name="index-file-save-command-523"></a><a name="index-layout-files-524"></a><a name="index-saving-layouts-525"></a><a name="index-postprocessing-layout-data-526"></a><a name="index-unix-command-527"></a><a name="index-cat-528"></a><br><dt>‘<samp><span class="samp">saveCommand (string)</span></samp>’<dd>This command is used to save data to a layout file. The filename may be
+indicated by placing <code>%f</code> in the string. It must read the data from
+its standard input.  The default command is:
+     <pre class="example">              cat - > %f
+</pre>
+     <p>See also, the command-line option <em>–save-command</em>.
+
+     <p><a name="index-saveInTMP-529"></a><a name="index-backup-530"></a><a name="index-saving-layouts-531"></a><a name="index-preventing-loss-of-data-532"></a><a name="index-temporary-files-533"></a><a name="index-g_t_002ftmp-534"></a><a name="index-directory-_002ftmp-535"></a><br><dt>‘<samp><span class="samp">saveInTMP (boolean)</span></samp>’<dd>Enabling this resource will save all data which would otherwise be lost
+in a temporary file <samp><span class="file">/tmp/PCB.%i.save</span></samp>.  The file name may
+be changed at compile time
+with the <code>EMERGENCY_NAME</code> variable in <samp><span class="file">globalconfig.h</span></samp>. 
+. 
+<em>%i</em> is replaced by the process ID. 
+As an example, loading a new layout when the old one hasn't been saved would
+use this resource. 
+See also, the command-line option <em>–save-in-tmp</em>.
+
+     <p><a name="index-saveLastCommand-536"></a><a name="index-saving-last-entered-user-command-537"></a><a name="index-inputfield_002c-saving-entered-command_002dline-538"></a><br><dt>‘<samp><span class="samp">saveLastCommand (boolean)</span></samp>’<dd>Enables the saving of the last entered user command. The option is
+<em>disabled</em> by default. 
+See also, the command-line option <em>–save-last-command</em>.
+
+     <p><a name="index-shrink-539"></a><a name="index-shrink-540"></a><a name="index-drc-541"></a><br><dt>‘<samp><span class="samp">Shrink (dimension)</span></samp>’<dd>Specifies the minimum overlap (touching) design rule in mils.
+
+     <p><a name="index-size-542"></a><a name="index-default-layout-size-543"></a><a name="index-layout_002c-default-size-of-544"></a><br><dt>‘<samp><span class="samp">size (<width>x<height>)</span></samp>’<dd>Defines the width and height of a new layout. The default is
+<em>7000x5000</em> unless changed at compile time
+with the <code>DEFAULT_SIZE</code> variable in <samp><span class="file">globalconfig.h</span></samp>.
+
+     <p><a name="index-stipplePolygons-545"></a><a name="index-polygon-546"></a><a name="index-display-547"></a><br><dt>‘<samp><span class="samp">stipllePolygons (boolean)</span></samp>’<dd>Determines whether to display polygons on the screen with a stippled
+pattern.  Stippling can create some amount of transparency so that
+you can still (to some extent) see layers beneath polygons. 
+It defaults to False.
+
+     <p><a name="index-textScale-548"></a><a name="index-text_002c-default-scaling-549"></a><a name="index-default-text-scaling-550"></a><br><dt>‘<samp><span class="samp">textScale (dimension)</span></samp>’<dd>The font scaling in percent is defined by this resource. The default is
+<em>100</em> percent.
+
+     <p><a name="index-useLogWindow-551"></a><a name="index-log-window-552"></a><a name="index-messages-553"></a><br><dt>‘<samp><span class="samp">useLogWindow (boolean)</span></samp>’<dd>Several subroutines send messages to the user if an error occurs. 
+This resource determines if they appear inside the log window or as a separate
+dialog box. See also, the resource <em>raiseLogWindow</em> and the command line
+option <em>-loggeometry</em>. 
+The default value is <em>true</em>.
+
+     <p><a name="index-viaColor-554"></a><a name="index-viaSelectedColor-555"></a><a name="index-colors-556"></a><a name="index-vias_002c-color-557"></a><br><dt>‘<samp><span class="samp">viaColor (color)</span></samp>’<br><dt>‘<samp><span class="samp">viaSelectedColor (color)</span></samp>’<dd>This resource defines the drawing color of vias in both states. 
+The values are preset to <em>XtDefaultForeground</em>.
+
+     <p><a name="index-viaThickness-558"></a><a name="index-viaDrillingHole-559"></a><a name="index-vias_002c-size-560"></a><a name="index-size-of-vias-561"></a><a name="index-thickness-of-vias-562"></a><br><dt>‘<samp><span class="samp">viaThickness (dimension)</span></samp>’<dt>‘<samp><span class="samp">viaDrillingHole (dimension)</span></samp>’<dd>The initial thickness and drilling hole of new vias. The values must be in the
+range [30..400] (the range may be changed at compile
+time with the <code>MIN_PINORVIASIZE</code> and <code>MAX_PINEORVIASIZE</code> variables in
+<samp><span class="file">globalconfig.h</span></samp>), with at least 20
+mil of copper. 
+The default thickness is <em>40 mil</em> and the default drilling hole is
+<em>20 mil</em>.
+
+     <p><a name="index-volume-563"></a><a name="index-speaker-volume-564"></a><a name="index-volume-of-speaker-565"></a><br><dt>‘<samp><span class="samp">volume (int)</span></samp>’<dd>The value is passed to <code>XBell()</code> which sets the volume of the <code>X</code>
+speaker. 
+The value lies in the range -100..100 and it defaults to the maximum volume of
+<em>100</em>.
+
+     <p><a name="index-warnColor-566"></a><a name="index-colors-567"></a><a name="index-color_002c-warning-568"></a><br><dt>‘<samp><span class="samp">warnColor (color)</span></samp>’<dd>This resources defines the color to be used for drawing pins and pads when
+a warning has been issued about them.
+
+     <p><a name="index-zoom-569"></a><a name="index-zoom-of-Layout-area-570"></a><br><dt>‘<samp><span class="samp">zoom (int)</span></samp>’<dd>The initial value for output scaling is set according to the following
+formula: scale = 1:(2 power value). It defaults to <em>three</em> which results
+in an output scale of <em>1:8</em>.
+
+   </dl>
+
+   <p>Refer also to <a href="#Command_002dLine-Options">Command-Line Options</a>.
+
+<div class="node">
+<a name="Actions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Translations">Translations</a>,
+Previous: <a rel="previous" accesskey="p" href="#Resources">Resources</a>,
+Up: <a rel="up" accesskey="u" href="#X11-Interface">X11 Interface</a>
+
+</div>
+
+<h3 class="section">7.2 Actions</h3>
+
+<p><a name="index-actions-571"></a><a name="index-translations-572"></a><a name="index-key-translations-573"></a><a name="index-button-translations-574"></a><a name="index-X11-translations-575"></a>
+All user accessible commands may be bound to almost any <code>X</code> event. Almost
+no default binding for commands is done in the binaries, so it is vital for the
+application that at least a system-wide application resource file exists. 
+This file normally resides in the <samp><span class="file">share/pcb</span></samp> directory and
+is called <samp><span class="file">pcb-rnd</span></samp>. The bindings to which the manual refers to are the
+ones as defined by the shipped resource file. Besides binding an action to
+an X11 event, you can also execute any action command using a ":" command
+(see <a href="#User-Commands">User Commands</a>).
+
+   <p>Take special care about translations related to the functions keys and the
+pointer buttons because most of the window managers use them too. 
+Change the file according to your hardware/software environment. 
+You may have to replace all occurances of <em>baseTranslations</em> to
+<em>translations</em> if you use a <code>X11R4</code> server.
+
+   <p>Passing <em>Object</em> as an argument to an action routine causes the object
+at the cursor location to be changed, removed or whatever. If more than
+one object is located at the cross hair position the smallest type is used. 
+If there are two of the same type the newer one is taken. 
+<em>SelectedObjects</em> will handle all selected and visible objects.
+
+     
+<a name="index-AddRats_0028_0029-576"></a>
+<a name="index-rats-nest-577"></a>
+<a name="index-netlist-578"></a>
+<a name="index-rat_002dline-579"></a>
+<dl><dt>‘<samp><span class="samp">AddRats(AllRats|SelectedRats)</span></samp>’<dd>Adds rat-lines to the layout using the loaded netlist file (see the <em>:rn</em>,
+<a href="#User-Commands">User Commands</a>.). Rat lines are added on the active layer using the current
+line thickness shown in the status line. 
+Only missing connectivity is added by the
+AddRats command so if, for example, the layout is complete nothing will be added. 
+Rat lines may be drawn different to other lines on the screen
+to make them easier to identify since they cannot appear in a completed layout. 
+The rat-lines are added in the minimum length straight-line tree pattern
+(always ending on pins or pads) that satisfies the missing connectivity in the circuit. 
+If a SMD pad is unreachable on the active layer, a warning will be issued
+about it and the rat-line to that pad will not be generated. 
+If connections exist on the board which are not listed in the netlist while
+AllRats are being added, warning messages will be issued and the affected pins and
+pads will be drawn in a special <em>warnColor</em> until the next <em>Notify()</em> event. 
+If the entire layout agrees completely with the net-list a message informs you that
+the layout is complete and no rat-lines are added (since none are needed). 
+If <em>SelectedRats</em>
+is passed as the argument, only those missing connections that might connect among
+the selected pins and pads are drawn. 
+Default:
+     <pre class="example">          None<Key>w:	AddRats(AllRats)
+          !Shift<Key>w:	AddRats(SelectedRats)
+          None<Key>o:	DeleteRats(AllRats) AddRats(AllRats)
+          !Shift<Key>o:	DeleteRats(SelectedRats) AddRats(SelectedRats)
+</pre>
+     <p><a name="index-ApplyVendor_0028_0029-580"></a><a name="index-vendor-map-581"></a><a name="index-vendor-drill-table-582"></a><br><dt>‘<samp><span class="samp">ApplyVendor()</span></samp>’<dd>Applies an already loaded vendor drill map to the design.
+     <pre class="example">          ApplyVendor()
+</pre>
+     <p><a name="index-Atomic_0028_0029-583"></a><a name="index-undo_002c-multi_002daction-resources-584"></a><a name="index-atomic-585"></a><br><dt>‘<samp><span class="samp">Atomic(Save|Restore|Block|Close)</span></samp>’<dd>Controls the undo grouping of sequences of actions. Before the first action
+in a group, Atomic(Save) should be issued.  After each action that might
+be undoable, Atomic(Restore) should be issued.  Atomic(Block) concludes
+and save the undo grouping if there was anything in the group to undo. 
+Atomic(Close) concludes and save the undo grouping even if nothing was
+actually done.  Thus it might produce an "empty" undo.  This can be useful
+when you want to use undo in a group of actions.
+
+     <p><a name="index-Bell_0028_0029-586"></a><a name="index-signal-587"></a><a name="index-bell-588"></a><br><dt>‘<samp><span class="samp">Bell([-100..100])</span></samp>’<dd>Rings the bell of your display. If no value is passed the setting
+of the resource <em>volume</em> will be used.
+
+     <p><a name="index-ChangeClearSize_0028_0029-589"></a><a name="index-change-sizes-590"></a><a name="index-sizes_002c-changing-of-objects-591"></a><a name="index-clearance_002c-changing-of-objects-592"></a><br><dt>‘<samp><span class="samp">ChangeClearSize(Object, value[, unit])</span></samp>’<dt>‘<samp><span class="samp">ChangeClearSize(SelectedPins|SelectedVias, value[, unit])</span></samp>’<dd>The effect of this action depends on if the soldermask display is  [...]
+turned on or off. If soldermask is displayed, then the soldermask
+relief size will be changed.  If soldermask display is turned off,
+then the clearance to polygons will be changed. 
+<em>unit</em> is "mil" or "mm".  If not specified the units will default
+to the internal unit of 0.01 mil.
+     <pre class="example">          !Mod1<Key>k:      ChangeClearSize(Object, +2, mil)
+          !Mod1 Shift<Key>k: ChangeClearSize(Object, -2, mil)
+</pre>
+     <p><a name="index-ChangeDrillSize_0028_0029-593"></a><a name="index-change-sizes-594"></a><a name="index-sizes_002c-changing-of-objects-595"></a><a name="index-drilling-hole_002c-changing-of-objects-596"></a><br><dt>‘<samp><span class="samp">ChangeDrillSize(Object, value[, unit])</span></samp>’<dt>‘<samp><span class="samp">ChangeDrillSize(SelectedPins|SelectedVias, value[, unit])</span></samp>’<dd>This action routine changes the drilling hole of pins and vias. 
+If <em>value</em> starts with + or -, then it adds (or subtracts)
+<em>value</em> from the current hole diameter, otherwise it sets the
+diameter to the value. 
+<em>unit</em> is "mil" or "mm".  If not specified the units will default
+to the internal unit of 0.01 mil. 
+Default:
+     <pre class="example">          !Mod1<Key>s:       Change2ndSize(Object, +5, mil)
+          !Mod1 Shift<Key>s: Change2ndSize(Object, -5, mil)
+</pre>
+     <p><a name="index-ChangeFlag_0028_0029-597"></a><a name="index-flags_002c-changing-598"></a><a name="index-octagonal-flag_002c-changing-599"></a><a name="index-square-flag_002c-changing-600"></a><a name="index-thermal-flag_002c-changing-601"></a><dt>‘<samp><span class="samp">ChangeFlag(Object|SelectElements|SelectedPins|SelectedVias|Selected,thermal|octagon|square,0|1)</span></samp>’<dd>Sets/clears the indicated flag.  This adds/removes thermals, adds/removes the flag
+which indicates a pin/pad should be square, or adds/removes the flag which
+indicates a pin/pad should be octagonal.
+     <pre class="example">          :ChangeFlag(SelectedVias,thermal,1)
+          :ChangeFlag(SelectedPads,square,0)
+</pre>
+     <p><a name="index-ChangeHole_0028_0029-602"></a><a name="index-vias_002c-converting-to-mounting-hole-603"></a><a name="index-mounting-holes-604"></a><br><dt>‘<samp><span class="samp">ChangeHole(Object|SelectedVias)</span></samp>’<dd>This action routine converts a via to and from a hole.  A hole is
+a via that has no copper annulus. The drill size for the via
+determines the hole diameter.
+     <pre class="example">          !Ctrl<Key>h:	ChangeHole(Object)
+</pre>
+     <p><a name="index-ChangeName_0028_0029-605"></a><a name="index-name_002c-change-an-objects-606"></a><a name="index-change-object-name-607"></a><a name="index-object_002c-change-name-of-608"></a><br><dt>‘<samp><span class="samp">ChangeName(Object)</span></samp>’<dt>‘<samp><span class="samp">ChangeName(Layer|Layout)</span></samp>’<dd>Changes the name of the visible object at the cursor location. A text object
+doesn't have a name therefore the text string itself is changed. 
+The element name currently used for display is always the one changed with this
+command. 
+See <em>Display(Description|NameOnPCB|Value)</em> for details. 
+Passing <em>Layer</em> changes the current layers name. 
+Default:
+     <pre class="example">          None<Key>n: ChangeName(Object)
+</pre>
+     <p><a name="index-ChangeOctagon_0028_0029-609"></a><a name="index-pins_002c-changing-shape-of-610"></a><a name="index-vias_002c-changing-shape-of-611"></a><a name="index-octagonal-pins-and-vias-612"></a><dt>‘<samp><span class="samp">ChangeOctagon(Object|SelectElements|SelectedPins|SelectedVias|Selected)</span></samp>’<dd>Toggles what shape the affected pin(s) or via(s) will be drawn when they
+are not square. The shape will either be round or octagonal. 
+Default:
+     <pre class="example">          !Ctrl<Key>o: ChangeOctagon(Object)
+</pre>
+     <p><a name="index-ChangePinName_0028_0029-613"></a><a name="index-changing-pin_002fpad-names-614"></a><a name="index-pin_002fpad-names_002c-changing-615"></a><br><dt>‘<samp><span class="samp">ChangePinName(ElementName, PinNumber, PinName)</span></samp>’<dd>Changes the name for a specified pin or pad number on a specified element. 
+This action is typically used to forward annotate pin/pad names from a schematic
+to the layout.
+     <pre class="example">          ChangePinName(U1, 14, VDD)
+</pre>
+     <p><a name="index-ChangeSize_0028_0029-616"></a><a name="index-change-sizes-617"></a><a name="index-sizes_002c-changing-of-objects-618"></a><a name="index-thickness_002c-changing-of-objects-619"></a><br><dt>‘<samp><span class="samp">ChangeSize(Object, value[, unit])</span></samp>’<dt>‘<samp><span class="samp">ChangeSize(SelectedLines|SelectedPins|SelectedVias, value[, unit])</span></samp>’<dt>‘<samp><span class="samp">ChangeSize(SelectedPads|SelectedTex [...]
+<code>X</code> event (or use :ChangeSize(...)).  If <em>value</em> begins with
+a + or - then the value will be added (or subtracted) from the current
+size, otherwise the size is set equal to <em>value</em>. Range checking is
+done to insure that none of the maximum/minimums of any size are violated. 
+If <em>Object</em> is passed then a single object at the cursor location is
+changed. If any of the <em>Selected</em> arguments are passed then all selected
+and visible objects of that type are changed. If the type being modified is
+an element, then the thickness of the silkscreen lines defining the element
+is changed. 
+<em>unit</em> is "mil" or "mm".  If not specified the units will default
+to the internal unit of 0.01 mil. 
+Default:
+     <pre class="example">          None<Key>s:   ChangeSize(Object, +5)
+          !Shift<Key>s: ChangeSize(Object, -5)
+</pre>
+     <p><a name="index-ChangeSquare_0028_0029-620"></a><a name="index-change-square-flag-621"></a><a name="index-square-flag_002c-changing-of-objects-622"></a><a name="index-thickness_002c-changing-of-objects-623"></a><br><dt>‘<samp><span class="samp">ChangeSquare(Object|SelectedElements|SelectedPins)</span></samp>’<dd>Toggles the setting of the square flag. The flag is used to identify a
+certain pin, normally the first one, of circuits. It is also used to
+make SMD pads have square ends.
+     <pre class="example">          None<Key>q:   ChangeSquare(Object)
+</pre>
+     <p><a name="index-ClrFlag_0028_0029-624"></a><a name="index-flags_002c-clearing-625"></a><a name="index-flags_002c-clearing-626"></a><a name="index-octagonal-flag_002c-clearing-627"></a><a name="index-square-flag_002c-clearing-628"></a><a name="index-thermal-flag_002c-clearing-629"></a><dt>‘<samp><span class="samp">ClrFlag(Object|SelectElements|SelectedPins|SelectedVias|Selected,thermal|octagon|square)</span></samp>’<dd>Clears the indicated flag.  This removes thermals,  [...]
+which indicates a pin/pad should be square, or removes the flag which
+indicates a pin/pad should be octagonal.
+     <pre class="example">          :ClrFlag(SelectedVias,thermal)
+</pre>
+     <p><a name="index-Command_0028_0029-630"></a><a name="index-start-user-input-631"></a><a name="index-inputfield_002c-start-user-input-632"></a><br><dt>‘<samp><span class="samp">Command()</span></samp>’<dd>Calling <em>Command()</em> pops up an input line at the bottom of the window
+which allows you to enter commands. Including all action commands! 
+The dialog ends when <em>None<Key>Return</em>
+to confirm or <em>None<Key>Escape</em> to abort is entered. 
+Default:
+     <pre class="example">          <Key>colon: Command()
+</pre>
+     <p><a name="index-Connection_0028_0029-633"></a><a name="index-scanning-connections-634"></a><a name="index-searching-connections-635"></a><a name="index-connections_002c-reseting-636"></a><a name="index-reseting-found-connections-637"></a><a name="index-connections_002c-searching-for-638"></a><a name="index-saving-found-connections-639"></a><br><dt>‘<samp><span class="samp">Connection(Find)</span></samp>’<dt>‘<samp><span class="samp">Connection(ResetFoundLinesAndR [...]
+line or via to others. 
+The <em>ResetFoundLinesAndRectangles, ResetFoundPinsAndVias</em> and
+<em>Reset</em> arguments may be used to reset all marked lines and rectangles,
+vias and pins or all of them. The search starts with the pin or via
+at the cursor position. All found objects are drawn with the color
+defined by the resource <em>connectedColor</em>. 
+See also, <em>Display(Description|NameOnPCB|Value)</em>. 
+Default:
+     <pre class="example">          !Shift<Key>c: Connection(Reset)
+          None<Key>f:   Connection(Find)
+          !Shift<Key>f: Connection(Reset)
+</pre>
+     <p><a name="index-DeleteRats_0028_0029-640"></a><a name="index-rats-nest-641"></a><a name="index-rat_002dline-642"></a><a name="index-netlist-643"></a><br><dt>‘<samp><span class="samp">DeleteRats(AllRats|SelectedRats)</span></samp>’<dd>This routine deletes either all rat-lines in the layout, or only
+the selected and visible ones. Non-rat-lines and other layout
+objects are unaffected. 
+Default:
+     <pre class="example">          None<Key>e:   DeleteRats(AllRats)
+          !Shift<Key>e: DeleteRats(SelectedRats)
+</pre>
+     <p><a name="index-DisableVendor_0028_0029-644"></a><a name="index-vendor-map_002c-disabling-645"></a><a name="index-vendor-drill-table_002c-disabling-646"></a><br><dt>‘<samp><span class="samp">DisableVendor()</span></samp>’<dd>Disables automatic drill size mapping to the loaded vendor drill table.
+     <pre class="example">          DisableVendor()
+</pre>
+     <p><a name="index-DisperseElements_0028_0029-647"></a><a name="index-dispersing-elements-648"></a><a name="index-distributing-elements-649"></a><a name="index-elements_002c-dispersing-650"></a><a name="index-elements_002c-distributing-651"></a><br><dt>‘<samp><span class="samp">DisperseElements(All|Selected)</span></samp>’<dd>Disperses either all elements or only the selected elements in the
+layout.  This action should be used at the
+start of a design to spread out all footprints before any placement or
+routing is done.
+     <pre class="example">          DisperseElements(All)
+</pre>
+     <p><a name="index-Display_0028_0029-652"></a><a name="index-centering-653"></a><a name="index-redrawing-layout-654"></a><a name="index-refreshing-layout-655"></a><a name="index-name-of-an-element-656"></a><a name="index-displaying-element-names-657"></a><a name="index-element_002c-display-names-of-658"></a><a name="index-grid_002c-absolute-and-relative-659"></a><a name="index-grid_002c-display-660"></a><a name="index-rubberband-661"></a><a name="index-pinout_002c-display-of-662"></a [...]
+used to center the display around the cursor location and to redraw the
+output area optionally after clearing the window. 
+Centering is done with respect to the <em>grid</em> setting. Displaying the
+grid itself may be switched on and off by <em>Grid</em> but only if
+the distance between two pixels exceeds 4 pixels. 
+<code>pcb-rnd</code> is able to handle several labels of an element. One of them
+is a description of the functionality (eg resistor), the second should be
+a unique identifier (R1) whereas the last one is a value (100k). 
+The <em>Display()</em> action selects which of the names is displayed. 
+It also controls which name will be affected by the <em>ChangeName</em> command. 
+If <em>ToggleGrid</em> is passed, <code>pcb-rnd</code> changes between relative
+('rel' in the statusline) and absolute grid (an 'abs' in the statusline). 
+Relative grid means the pointer position when the command is issued is
+used as the grid origin; while (0,0) is used in the absolute grid case. 
+Passing <em>Pinout</em> displays the pinout of the element at the current
+cursor location whereas <em>PinOrPadName</em> toggles displaying of the
+pins or pads name under the cursor. If none of them matches but the cursor
+is inside of an element, the flags is toggled for all of its pins and pads. 
+For details about rubberbands see also the details about <em>Mode</em>. 
+Default:
+     <pre class="example">          None<Key>c:  Display(Center)
+          None<Key>d:  Display(PinOrPadName)
+          !Shift<Key>d: Display(Pinout)
+          None<Key>r:  Display(ClearAndRedraw)
+          None<Key>.:  Display(Toggle45Degree)
+          None<Key>/:  Display(CycleClip)
+</pre>
+     <p><a name="index-DRC_0028_0029-666"></a><a name="index-design-rule-checking-667"></a><a name="index-drc-668"></a><br><dt>‘<samp><span class="samp">DRC()</span></samp>’<dd>Initiates design rule checking of the entire layout. Must be repeated
+until no errors are found.
+
+     <p><a name="index-ExecuteFile_0028_0029-669"></a><a name="index-actions-file_002c-executing-670"></a><a name="index-script-file_002c-executing-671"></a><dt>‘<samp><span class="samp">ExecuteFile(filename)</span></samp>’<dd>Executes the PCB actions contained in the specified file. 
+This can be used to automate a complex sequence of operations.
+     <pre class="example">          :ExecuteFile(custom.cmd)
+</pre>
+     <p>The command file contains a list of PCB actions.  Blank lines
+are ignored and lines starting with a # are treated as comment
+lines.  For example
+     <pre class="example">          # This is a comment line
+          Display(Grid)
+          SetValue(Zoom,2)
+          DRC()
+</pre>
+     <p><a name="index-EditLayerGroups_0028_0029-672"></a><a name="index-layers_002c-editing-of-groups-673"></a><a name="index-groups_002c-editing-of-674"></a><br><dt>‘<samp><span class="samp">EditLayerGroups()</span></samp>’<dd>Pops up a dialog box to edit the layergroup setting. The function is also
+available from the <em>Objects</em> menu. 
+There are no defaults.
+
+     <p><a name="index-EnableVendor_0028_0029-675"></a><a name="index-vendor-map_002c-enabling-676"></a><a name="index-vendor-drill-table_002c-enabling-677"></a><br><dt>‘<samp><span class="samp">EnableVendor()</span></samp>’<dd>Enables automatic drill size mapping to the loaded vendor drill table.
+     <pre class="example">          EnableVendor()
+</pre>
+     <p><a name="index-Load_0028_0029-678"></a><a name="index-loading-files-679"></a><br><dt>‘<samp><span class="samp">Load(ElementToBuffer|Layout|LayoutToBuffer|Nelist)</span></samp>’<dd>This routine pops up a fileselect box to load layout, element data,
+or netlist. 
+The passed filename for layout data is saved and may be reused. 
+<em>ElementToBuffer</em> and <em>LayoutToBuffer</em> load the data into the
+current buffer. 
+There are no defaults.
+
+     <p><a name="index-LoadVendor_0028_0029-680"></a><a name="index-vendor-map_002c-loading-681"></a><a name="index-vendor-drill-table_002c-loading-682"></a><br><dt>‘<samp><span class="samp">LoadVendor(vendorfile)</span></samp>’<dd>Loads the specified vendor resource file.
+     <pre class="example">          LoadVendor(myvendor.res)
+</pre>
+     <p><a name="index-MarkCrosshair_0028_0029-683"></a><a name="index-mark-684"></a><a name="index-cursor-position-685"></a><br><dt>‘<samp><span class="samp">MarkCrosshair()</span></samp>’<dd>This routine marks the current cursor location with an X, and then
+the cursor display shows both absolute position and position relative to
+the mark.  If a mark is already present, this routine removes it and
+stops displaying relative cursor coordinates. 
+Defaults:
+     <pre class="example">          !Ctrl<key>m:	MarkCrosshair()
+</pre>
+     <p><a name="index-Mode_0028_0029-686"></a><a name="index-mode_002c-selecting-of-687"></a><a name="index-operation-modes_002c-selecting-of-688"></a><br><dt>‘<samp><span class="samp">Mode(Copy|InsertPoint|Line|Move|None|PasteBuffer|Polygon|Thermal)</span></samp>’<dt>‘<samp><span class="samp">Mode(Remove|Rectangle|RubberbandMove|Text|Via)</span></samp>’<dt>‘<samp><span class="samp">Mode(Cycle)</span></samp>’<dt>‘<samp><span class="samp">Mode(No [...]
+line around the matching mode selector button. 
+Most of the functionality of <code>pcb-rnd</code> is implemented by selecting a mode
+and calling <em>Mode(Notify)</em>. The arguments <em>Line</em>, <em>Polygon</em>,
+<em>Rectangle</em>, <em>Text</em> and <em>Via</em> are used to create the
+appropriate object whenever <em>Mode(Notify)</em> is called. Some of them,
+such as <em>Polygon</em>, need more than one call for one object to be created. 
+<em>InsertPoint</em> adds points to existing polygons or lines. 
+<em>Save</em> and <em>Restore</em> are used to temporarily save the mode, switch
+to another one, call <em>Mode(Notify)</em> and restore the saved one. Have
+a look at the application resource file for examples. 
+<em>Copy</em> and <em>Move</em> modes are used to change an object's location and,
+optionally, to create a new one. The first call of <em>Mode(Notify)</em> attaches
+the object at the pointer location to the cross hair whereas the second
+one drops it to the layout. The <em>rubberband</em> version of move performs the
+move while overriding the current rubberband mode. 
+Passing <em>PasteBuffer</em> attaches the contents of the currently selected
+buffer to the cross hair. Each call to <em>Mode(Notify)</em> pastes this contents
+to the layout. <em>Mode(Cycle)</em> cycles through the modes available in the
+mode-button pallet. 
+<em>Mode(None)</em> switches all modes off. 
+Default:
+     <pre class="example">          <Key>Escape:             Mode(None)
+          <Key>space:              Mode(Cycle)
+          None<Key>BackSpace:      Mode(Save) Mode(Remove) Mode(Notify) Mode(Restore)
+          None<Key>Delete:         Mode(Save) Mode(Remove) Mode(Notify) Mode(Restore)
+          None<Key>F1:             Mode(Via)
+          None<Key>F2:             Mode(Line)
+          None<Key>F3:             Mode(PasteBuffer)
+          None<Key>F4:             Mode(Rectangle)
+          None<Key>F5:             Mode(Text)
+          None<Key>F6:             Mode(Polygon)
+          None<Key>F7:             Mode(Thermal)
+          None<Key>F8:		 Mode(Arc)
+          None<Key>Insert:         Mode(InsertPoint)
+          None<Key>[:              Mode(Save) Mode(Move) Mode(Notify)
+          None<Key>]:              Mode(Notify) Mode(Restore)
+          None<Btn1>:          Mode(Notify)
+          !Shift Ctrl<Btn1>:   Mode(Save) Mode(Remove) Mode(Notify) Mode(Restore)
+          None<Btn2Down>:          Mode(Save) Mode(Move) Mode(Notify)
+          None<Btn2Up>:            Mode(Notify) Mode(Restore)
+          !Mod1<Btn2Down>:       Mode(Save) Mode(Copy) Mode(Notify)
+          !Mod1<Btn2Up>:         Mode(Notify) Mode(Restore)
+          Shift BTNMOD<Btn2Down>: Mode(Save) Mode(RubberbandMove) Mode(Notify)
+</pre>
+     <p><a name="index-MovePointer_0028_0029-689"></a><a name="index-pointer_002c-moving-of-690"></a><a name="index-cursor-movements-691"></a><br><dt>‘<samp><span class="samp">MovePointer(delta_x, delta_y)</span></samp>’<dd>With this function it is possible to move the cross hair cursor by using the
+cursor keys. The <code>X</code> server's pointer follows because the necessary
+events are generated by <code>pcb-rnd</code>. All movements are performed with respect
+to the currently set grid value. 
+Default:
+     <pre class="example">          None<Key>Up:      MovePointer(0, -1)
+          !Shift<Key>Up:    MovePointer(0, -10)
+          None<Key>Down:    MovePointer(0, 1)
+          !Shift<Key>Down:  MovePointer(0, 10)
+          None<Key>Right:   MovePointer(1, 0)
+          !Shift<Key>Right: MovePointer(10, 0)
+          None<Key>Left:    MovePointer(-1, 0)
+          !Shift<Key>Left:  MovePointer(-10, 0)
+</pre>
+     <p><a name="index-MoveToCurrentLayer_0028_0029-692"></a><a name="index-objects_002c-moving-to-current-layer-693"></a><a name="index-moving-objects-to-current-layer-694"></a><br><dt>‘<samp><span class="samp">MoveToCurrentLayer(Object|SelectedObjects)</span></samp>’<dd>The function moves a single object at the cross hair location or all selected
+objects to the current layer. Elements are not movable by this function. 
+They have to be deleted and replaced on the other side. 
+If a line segment is moved and the movement would result in a loss of
+connectivity to another segment then via(s) are automatically added to
+maintain the connectivity.
+     <pre class="example">          None<Key>m:       MoveToCurrentLayer(Object)
+          !Shift<Key>m:     MoveToCurrentLayer(SelectedObjects)
+</pre>
+     <p><a name="index-New_0028_0029-695"></a><a name="index-layout_002c-start-a-new-696"></a><a name="index-starting-a-new-layout-697"></a><br><dt>‘<samp><span class="samp">New()</span></samp>’<dd>Clear the current layout and starts a new one after entering its name. 
+Refer to the resource <em>backup</em> for more information. 
+No defaults.
+
+     <p><a name="index-PasteBuffer_0028_0029-698"></a><a name="index-buffer_002c-selecting-a-699"></a><a name="index-pastebuffer_002c-selecting-a-700"></a><a name="index-selecting-a-buffer-701"></a><a name="index-rotating-a-buffer-702"></a><a name="index-cutting-objects-703"></a><a name="index-copying-objects-704"></a><br><dt>‘<samp><span class="samp">PasteBuffer(AddSelected|Clear|1..5)</span></samp>’<dt>‘<samp><span class="samp">PasteBuffer(Rotate, 1..3)</span></samp>& [...]
+cut-and-paste operations. Passing a buffer number selects one in of the
+range 1..5. The statusline is updated with the new number. 
+<em>Rotate</em> performs a number of 90 degree counter clockwise rotations
+of the buffer contents. <em>AddSelected</em> as first argument copies all
+selected and visible objects into the buffer. Passing <em>Clear</em> removes
+all objects from the currently selected buffer. <em>Convert</em> causes
+the contents of the buffer (lines, arc, vias) to be converted into an
+element definition. Refer to <a href="#Pastebuffer">Pastebuffer</a>
+for examples. 
+Default:
+     <pre class="example">          !Ctrl<Key>x:       PasteBuffer(Clear) PasteBuffer(AddSelected)
+          		   Mode(PasteBuffer)
+          !Shift Ctrl<Key>x: PasteBuffer(Clear) PasteBuffer(AddSelected)
+          		   RemoveSelected() Mode(PasteBuffer)
+          !Mod1<Key>c:       PasteBuffer(Clear) PasteBuffer(AddSelected)
+          !Mod1<key>x:       PasteBuffer(Clear) PasteBuffer(AddSelected)
+          		   RemoveSelected()
+          !Shift<Key>1:      PasteBuffer(1)
+          !Shift<Key>2:      PasteBuffer(2)
+          !Shift<Key>3:      PasteBuffer(3)
+          !Shift<Key>4:      PasteBuffer(4)
+          !Shift<Key>5:      PasteBuffer(5)
+          None<Key>F3:       Mode(PasteBuffer)
+</pre>
+     <p><a name="index-Polygon_0028_0029-705"></a><a name="index-polygon_002c-closing-a-706"></a><a name="index-polygon-point_002c-go-back-to-previous-707"></a><a name="index-closing-a-polygon-708"></a><br><dt>‘<samp><span class="samp">Polygon((Close|PreviousPoint)</span></samp>’<dd>Polygons need a special action routine to make life easier. Calling
+<em>Polygon(PreviousPoint)</em> resets the newly entered corner to the
+previous one. The Undo action will call Polygon(PreviousPoint)
+when appropriate to do so.  <em>Close</em> creates the final
+segment of the polygon.  This may fail if clipping to 45 degree
+lines is switched on, in which case a warning is issued. 
+Default:
+     <pre class="example">          None<Key>p:             Polygon(Close)
+          !Shift<Key>p:           Polygon(Close)
+</pre>
+     <p><a name="index-Print_0028_0029-709"></a><a name="index-layout_002c-printing-a-710"></a><a name="index-printing-a-layout-711"></a><br><dt>‘<samp><span class="samp">Print()</span></samp>’<dd>Pops up a print control box that lets you select the output
+device, scaling and many more options. Each run creates all
+files that are supported by the selected device. These are
+mask files as well as drilling files, silk screens and so on. The table
+shows the filenames for all possible files:
+     <pre class="example">          	POSIX (extension)             8.3 filename
+          		---------------------------------------------
+          		*_componentmask.*             cmsk.*
+          		*_componentsilk.*             cslk.*
+          		*_soldermask.*                smsk.*
+          		*_soldersilk.*                sslk.*
+          		*_drill.*                     dril.*
+          		*_groundplane.*               gpl.*
+          		*_group[1..8].*     [..8].*
+</pre>
+     <p>The output may be sent to a post-processor by starting the filename with the
+<em>pipe</em> <code>("|")</code> character. Any <code>"%f"</code> in a command is replaced
+with the current filename. The function is available from the <em>file</em> menu. 
+There are no defaults.
+
+     <p><a name="index-Quit_0028_0029-712"></a><a name="index-quit-713"></a><a name="index-exit-714"></a><br><dt>‘<samp><span class="samp">Quit()</span></samp>’<dd>Quits the application after confirming the operation. 
+Default:
+     <pre class="example">          <Message>WM_PROTOCOLS: Quit()
+</pre>
+     <p><a name="index-Redo_0028_0029-715"></a><a name="index-redo-716"></a><a name="index-recover-717"></a><br><dt>‘<samp><span class="samp">Redo()</span></samp>’<dd>This routine allows you to recover from the last undo command. 
+You might want to do this if you thought that undo was going to
+revert something other than what it actually did (in case you
+are confused about which operations are un-doable), or if you
+have been backing up through a long undo list and over-shoot
+your stopping point.  Any change that is made since the undo
+in question will trim the redo list.  For example if you add
+ten lines, then undo three of them you could use redo to put
+them back, but if you move a line on the board before performing
+the redo, you will lose the ability to "redo" the three "undone" lines. 
+Default:
+     <pre class="example">          !Shift<Key>r:	Redo()
+</pre>
+     <p><a name="index-RemoveSelected_0028_0029-718"></a><a name="index-removing-selected-objects-719"></a><a name="index-selected-object_002c-removing-an-720"></a><br><dt>‘<samp><span class="samp">RemoveSelected()</span></samp>’<dd>This routine removes all visible and selected objects. 
+There are no defaults.
+
+     <p><a name="index-Report_0028_0029-721"></a><a name="index-report-722"></a><a name="index-information-about-objects-723"></a><a name="index-drill-724"></a><br><dt>‘<samp><span class="samp">Report(Object|DrillReport)</span></samp>’<dd>This routine pops up a dialog box describing the various
+characteristics of an object (or piece of an object such as a pad or pin)
+in the layout at the cursor position, or a report about all of the
+drill holes in the layout. 
+There are no defaults.
+
+     <p><a name="index-RouteStyle_0028_0029-725"></a><a name="index-routing-style-726"></a><a name="index-size-of-lines-and-vias-727"></a><br><dt>‘<samp><span class="samp">RouteStyle(1|2|3|4)</span></samp>’<dd>This routine copies the sizes corresponding to the numbered route style
+into the active line thickens, via diameter, and via drill size. 
+Defaults:
+     <pre class="example">          !Ctrl<Key>1: RouteStyle(1)
+          ...
+          !Ctrl<Key>NUM_STYLES: RouteStyle(NUM_STYLES)
+</pre>
+     <p>The variable <code>NUM_STYLES</code> is set at compile time in
+<samp><span class="file">globalconfig.h</span></samp>.
+
+     <p><a name="index-Save_0028_0029-728"></a><a name="index-saving-files-729"></a><a name="index-saving-connections-730"></a><br><dt>‘<samp><span class="samp">Save(Layout|LayoutAs)</span></samp>’<dt>‘<samp><span class="samp">Save(AllConnections|AllUnusedPins|ElementConnections)</span></samp>’<dd>Passing <em>Layout</em> saves the layout using the file from which it was
+loaded or, if it is a new layout, calls <em>Save(LayoutAs)</em> which queries
+the user for a filename. 
+The values: <em>AllConnections</em>, <em>AllUnusedPins</em> and
+<em>ElementConnections</em> start a connection scan and save all connections,
+all unused pins or the connections of a single element to a file. 
+There are no defaults.
+
+     <p><a name="index-Select_0028_0029-731"></a><a name="index-selection-732"></a><a name="index-selecting-objects-733"></a><br><dt>‘<samp><span class="samp">Select(All|Block|Connection|ToggleObject)</span></samp>’<dt>‘<samp><span class="samp">Select(ElementByName|ObjectByName|PadByName|PinByName)</span></samp>’<dt>‘<samp><span class="samp">Select(TextByName|ViaByName)</span></samp>’<dd>Toggles either the selection flag of the object at the cross hair [...]
+(<em>ToggleObject</em>) or selects all visible objects, all inside a
+rectangle or all objects which have been found during the last connection
+scan. The <em>ByName</em> functions use a <a href="#Regular-Expressions">Regular Expressions</a> search,
+always case insensitive, to select the objects. 
+Default:
+     <pre class="example">          None<Btn3Down>:  Select(ToggleObject)
+          None<Btn3Down>,None<Btn3Motion>: See resource file - this is complex
+</pre>
+     <p><a name="index-SetFlag_0028_0029-734"></a><a name="index-flags_002c-setting-735"></a><a name="index-octagonal-flag_002c-setting-736"></a><a name="index-square-flag_002c-setting-737"></a><a name="index-thermal-flag_002c-setting-738"></a><dt>‘<samp><span class="samp">SetFlag(Object|SelectElements|SelectedPins|SelectedVias|Selected,thermal|octagon|square)</span></samp>’<dd>Sets the indicated flag.  This adds thermals, sets the flag
+which indicates a pin/pad should be square, or sets the flag which
+indicates a pin/pad should be octagonal.
+     <pre class="example">          :SetFlag(Selected,thermal)
+</pre>
+     <p><a name="index-SetValue_0028_0029-739"></a><a name="index-change-settings-740"></a><a name="index-zoom_002c-setting-of-741"></a><a name="index-grid_002c-setting-of-742"></a><a name="index-drilling-hole_002c-setting-of-initial-size-743"></a><a name="index-vias_002c-setting-of-initial-size-744"></a><a name="index-lines_002c-setting-of-initial-size-745"></a><br><dt>‘<samp><span class="samp">SetValue(Grid|LineSize|TextScale|ViaDrillingHole|ViaSize|Zoom, value)</span></samp>&rsq [...]
+The first parameter specifies which data has to be changed. The other one
+determines if the resource is set to the passed value, if <em>value</em> is
+specified without sign, or increments/decrements if it is specified with
+a plus or minus sign. 
+The function doesn't change any existing object only the initial values of
+new objects.  Use the <em>ChangeSize()</em> and <em>ChangeDrillSize()</em>
+to change existing objects. 
+Default:
+     <pre class="example">          None<Key>g:        SetValue(Grid, +5)
+          !Shift<Key>g:      SetValue(Grid, -5)
+          None<Key>l:        SetValue(LineSize, +5)
+          !Shift<Key>l:      SetValue(LineSize, -5)
+          None<Key>t:        SetValue(TextScale, +10)
+          !Shift<Key>t:      SetValue(TextScale, -10)
+          None<Key>v:        SetValue(ViaSize, +5)
+          !Shift<Key>v:      SetValue(ViaSize, -5)
+          !Mod1<Key>v:       SetValue(ViaDrillingHole, +5)
+          !Mod1 Shift<Key>v: SetValue(ViaDrillingHole, -5)
+          None<Key>z:        SetValue(Zoom, -1)
+          !Shift<Key>z:      SetValue(Zoom, +1)
+</pre>
+     <p><a name="index-SwapSides_0028_0029-746"></a><a name="index-change-viewing-side-747"></a><a name="index-viewing-side_002c-changing-of-748"></a><br><dt>‘<samp><span class="samp">SwapSides()</span></samp>’<dd>This routine changes the board side you are viewing. 
+Default:
+     <pre class="example">          None<Key>Tab:      SwapSides()
+</pre>
+     <p><a name="index-SwitchDrawingLayer_0028_0029-749"></a><a name="index-change-drawing-layer-750"></a><a name="index-layer_002c-change-active-751"></a><br><dt>‘<samp><span class="samp">SwitchDrawingLayer(value)</span></samp>’<dd>Makes layer number 1..MAX_LAYER the current one. 
+Default:
+     <pre class="example">          None<Key>1:        SwitchDrawingLayer(1)
+          ...
+          None<Key>MAX_LAYER:        SwitchDrawingLayer(MAX_LAYER)
+</pre>
+     <p><a name="index-ToggleHideName_0028_0029-752"></a><a name="index-hide-element-name-753"></a><a name="index-element-name_002c-hiding-754"></a><a name="index-element-name_002c-removing-from-silk_002dscreen-755"></a><br><dt>‘<samp><span class="samp">ToggleHideName(Object|SelectedElements)</span></samp>’<dd>Toggles whether the element's name is displayed or hidden. If it
+is hidden you won't see it on the screen and it will not appear
+on the silk layer when you print the layout.
+     <pre class="example">          None<Key>h:	ToggleHideName(Object)
+          !Shift<Key>h:	ToggleHideName(SelectedElements)
+</pre>
+     <p><a name="index-ToggleVendor_0028_0029-756"></a><a name="index-vendor-map_002c-toggling-757"></a><a name="index-vendor-drill-table_002c-toggling-758"></a><br><dt>‘<samp><span class="samp">ToggleVendor()</span></samp>’<dd>Toggles automatic drill size mapping to the loaded vendor drill table.
+     <pre class="example">          ToggleVendor()
+</pre>
+     <p><a name="index-ToggleVisibility_0028_0029-759"></a><a name="index-toggle-layer-visibility-760"></a><a name="index-layer-visibility_002c-toggling-761"></a><br><dt>‘<samp><span class="samp">ToggleVisibility(Layer)</span></samp>’<dd>Toggles the visibility of the layer.
+     <pre class="example">          Mod1<Key>1:	ToggleVisibility(1)
+          Mod1<Key>2:	ToggleVisibility(2)
+          Mod1<Key>3:	ToggleVisibility(3)
+          Mod1<Key>4:	ToggleVisibility(4)
+</pre>
+     <p><a name="index-Undo_0028_0029-762"></a><a name="index-undo-763"></a><a name="index-recover-764"></a><br><dt>‘<samp><span class="samp">Undo()</span></samp>’<dt>‘<samp><span class="samp">Undo(ClearList)</span></samp>’<dd>The unlimited undo feature of <code>pcb-rnd</code> allows you to recover
+from most operations that materially affect you work. 
+Calling <em>Undo()</em> without any parameter recovers
+from the last (non-undo) operation. <em>ClearList</em> is used to release the
+allocated memory. <em>ClearList</em> is called whenever a new layout is started
+or loaded. See also <em>Redo</em>. 
+Default:
+     <pre class="example">          None<Key>u:        Undo()
+          !Shift Ctrl<Key>u: Undo(ClearList)
+</pre>
+     <p><a name="index-UnloadVendor_0028_0029-765"></a><a name="index-vendor-map_002c-unloading-766"></a><a name="index-vendor-drill-table_002c-unloading-767"></a><br><dt>‘<samp><span class="samp">UnloadVendor()</span></samp>’<dd>Unloads the loaded vendor drill table.
+     <pre class="example">          UnloadVendor()
+</pre>
+     <p><a name="index-Unselect_0028_0029-768"></a><a name="index-selection-769"></a><a name="index-unselect-objects-770"></a><br><dt>‘<samp><span class="samp">Unselect(All|Block|Connection)</span></samp>’<dd>Unselects all visible objects, all inside a rectangle or all objects which
+have been found during the last connection scan. 
+Default:
+     <pre class="example">          !Shift <Btn3Down>: Mode(Save) Mode(None) Unselect(Block)
+          !Shift <Btn3Up>:   Unselect(Block) Mode(Restore)
+</pre>
+     </dl>
+
+<div class="node">
+<a name="Translations"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Actions">Actions</a>,
+Up: <a rel="up" accesskey="u" href="#X11-Interface">X11 Interface</a>
+
+</div>
+
+<h3 class="section">7.3 Default Translations</h3>
+
+<p><a name="index-translations-771"></a><a name="index-default-translations-772"></a><a name="index-X11-default-translations-773"></a>
+This section covers some default translations of key and button events as
+defined in the shipped default application resource file. Most of them have
+already been listed in <a href="#Actions">Actions</a>. <code>pcb-rnd</code> makes use of a nice <code>X11</code>
+feature; calling several action routines for one event.
+
+     
+<a name="index-removing-objects-774"></a>
+<a name="index-removing-connections-775"></a>
+<a name="index-object_002c-removing-an-776"></a>
+<a name="index-connection_002c-removing-an-777"></a>
+<dl><dt>‘<samp><span class="samp">None<Key>BackSpace:</span></samp>’<br><dt>‘<samp><span class="samp">None<key>Delete:</span></samp>’<dt>‘<samp><span class="samp">!Shift<Key>BackSpace:</span></samp>’<dt>‘<samp><span class="samp">!Shift Ctrl<Btn1>:</span></samp>’<dd>The object at the cursor location is removed by <em>None<Key>BackSpace</em> or
+<em>Shift Ctrl<Btn1></em> whereas <em>Shift<Key>BackSpace</em> also removes
+all other objects that are fully-connected to the one at the cursor location.
+
+     <p><a name="index-scrolling-778"></a><br><dt>‘<samp><span class="samp">!Mod1 Ctrl<Key>Left:</span></samp>’<dt>‘<samp><span class="samp">!Mod1 Ctrl<Key>Right:</span></samp>’<dt>‘<samp><span class="samp">!Mod1 Ctrl<Key>Up:</span></samp>’<dt>‘<samp><span class="samp">!Mod1 Ctrl<Key>Down:</span></samp>’<dd>Scroll one page in one of the four directions.
+
+     <p><a name="index-scrolling-779"></a><br><dt>‘<samp><span class="samp">None<Key>Left:, !Shift<Key>Left:</span></samp>’<dt>‘<samp><span class="samp">None<Key>Right:, !Shift<Key>Right:</span></samp>’<dt>‘<samp><span class="samp">None<Key>Up:, !Shift<Key>Up:</span></samp>’<dt>‘<samp><span class="samp">None<Key>Down:, !Shift<Key>Down:</span></samp>’<dd>Move cross hair either one or ten points [...]
+
+     <p><a name="index-user-input-780"></a><br><dt>‘<samp><span class="samp">None<Key>Return:</span></samp>’<dd>Finished user input, selects the 'default' button of dialogs.
+
+     <p><a name="index-user-input-781"></a><br><dt>‘<samp><span class="samp">None<Key>Escape:</span></samp>’<dd><em>Mode(Reset)</em>, aborts user input, selects the 'abort' button of
+dialogs or resets all modes.
+
+     <p><a name="index-element_002c-move-name-of-782"></a><a name="index-object_002c-move-an-783"></a><a name="index-object_002c-copy-an-784"></a><a name="index-move-an-object-785"></a><a name="index-copy-an-object-786"></a><br><dt>‘<samp><span class="samp">None<Btn2Down>, Btn2<Motion>, None<Btn2Up>:</span></samp>’<dt>‘<samp><span class="samp">!Mod1<Btn2Down>, Btn2<Motion>, !Mod1<Btn2Up>:</span></samp>’<dd>The first sequence mov [...]
+The second one copies the objects. Copying isn't available for
+element names.
+
+   </dl>
+
+<!--  chapter 6 - -->
+<div class="node">
+<a name="File-Formats"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Library-Creation">Library Creation</a>,
+Previous: <a rel="previous" accesskey="p" href="#X11-Interface">X11 Interface</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">7 File Formats</h2>
+
+<p><a name="index-file-formats-787"></a><a name="index-ASCII-files_002c-format-of-788"></a>
+All files used by <code>pcb-rnd</code> are read from the standard output of a command
+or written to the standard input of one as plain seven bit <code>ASCII</code>. This
+makes it possible to use any editor to change the contents of a layout file. 
+It is the only way for element or font description files to be created. 
+To do so you'll need to study the example files <samp><span class="file">example/*</span></samp> and
+<samp><span class="file">default_font</span></samp> which are shipped with <code>pcb-rnd</code>. 
+For an overview refer to <a href="#Intro">Intro</a>.
+
+   <p><a name="index-elementCommand-789"></a><a name="index-fileCommand-790"></a><a name="index-fontCommand-791"></a><a name="index-libraryCommand-792"></a><a name="index-libraryContentsCommand-793"></a><a name="index-saveCommand-794"></a>The following sections provide the necessary information about the syntax of
+the files. 
+Netlist files are not created by <code>pcb-rnd</code>, but it does use them. For information
+on the format of a netlist file see the <em>:rn</em>,
+<a href="#User-Commands">User Commands</a>. 
+The commands described allow you to add almost any additional
+functionality you may need. Examples are compressed read and write access as
+well as archives. The commands themselves are defined by the resources
+<em>elementCommand</em>, <em>fileCommand</em>, <em>fontCommand</em>,
+<em>libraryCommand</em>, <em>libraryContentsCommand</em> and <em>saveCommand</em>. 
+Note that the commands are not saved along with the data. 
+It is considered an advantage to have the layout file contain all necessary
+information, independent of any other files.
+
+   <p>One thing common to all files is they may include comments, newlines,
+and carriage returns at any place except within quoted strings.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Pad-and-Line-Representation">Pad and Line Representation</a>
+<li><a accesskey="2" href="#Layout-File">Layout File</a>
+<li><a accesskey="3" href="#Element-File">Element File</a>
+<li><a accesskey="4" href="#Font-File">Font File</a>
+<li><a accesskey="5" href="#Netlist-File">Netlist File</a>
+<li><a accesskey="6" href="#Library-Contents-File">Library Contents File</a>
+<li><a accesskey="7" href="#Library-File">Library File</a>
+<li><a accesskey="8" href="#File-Syntax">File Syntax</a>
+<li><a accesskey="9" href="#Object-Flags">Object Flags</a>
+<li><a href="#PCBFlags">PCBFlags</a>
+</ul>
+
+<div class="node">
+<a name="Pad-and-Line-Representation"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Layout-File">Layout File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.1 Pad and Line Representation</h3>
+
+<p><a name="index-pad-specification-795"></a><a name="index-file-formats_002c-pads-and-lines-796"></a>
+Pads and lines (copper traces, silk screen lines, etc) are represented by the
+line end points and the aperture used to draw the line.  It is important to
+understand this when creating the pads for a new footprint.  The following figure
+illustrates a pad or line which is drawn using a square aperture.  The end
+points (X0,Y0), (X1,Y1) specify the center of the aperture.  The size parameter
+specifies the size of the aperture.
+
+<div align="center"><img src="pad.png" alt="Pad Layout"></div>
+
+   <p>Pads and lines are represented in this way because this is how lines are
+specified in RS-274X (Gerber) files which are used for creating
+the masks used in board manufacturing.  In fact, older mask making
+equipment created lines in precisely this fashion.  A physical aperture was
+used to pass light through onto a photosensitive film.
+
+<div class="node">
+<a name="Layout-File"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Element-File">Element File</a>,
+Previous: <a rel="previous" accesskey="p" href="#Pad-and-Line-Representation">Pad and Line Representation</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.2 Layout File Format</h3>
+
+<p><a name="index-layout-files_002c-format-of-797"></a><a name="index-format-of-layout-files-798"></a><a name="index-file-format_002c-layout-data-799"></a>
+The layout file describes a complete layout including symbols, vias,
+elements and layers with lines, rectangles and text. This is the most
+complex file of all.  As <code>pcb-rnd</code> has evolved, the file format has
+changed several times to accommodate new features.  <code>pcb-rnd</code> has
+always been able to read all older versions of the <code>.pcb</code> file. 
+This allows the migration of older designs to newer versions of the
+program.  Obviously older versions of <code>pcb-rnd</code> will not be able
+to properly read layout files stored in newer versions of the file
+format.
+
+   <p>In practice it is very common for footprint libraries to contain
+elements which have been defined in various versions of the <code>pcb-rnd</code>
+file format.  When faced with trying to understand an element file or
+layout file which includes syntax not defined here, the best approach
+is to examine the file <samp><span class="file">src/parse_y.y</span></samp> which is the definitive
+definition of the file format.
+
+   <p>The PCB layout file contains the following contents, in this order (individual items
+are defined in <a href="#File-Syntax">File Syntax</a>:
+
+     <dl>
+<dt><code>PCB</code><dd>This names the board and sets its size
+
+     <br><dt><code>Grid</code><dd>Optional.
+
+     <br><dt><code>Cursor</code><dd>Optional.
+
+     <br><dt><code>Flags</code><dd>Optional.
+
+     <br><dt><code>Groups</code><dd>Optional.
+
+     <br><dt><code>Styles</code><dd>Optional.
+
+     <br><dt><code>Symbols</code><dd>Optional.
+
+     <br><dt><code>Vias, Rats, Layers, and Elements</code><dd>These may occur in any order, at this point in the file.
+
+     <br><dt><code>Netlists</code><dd>Optional.
+
+   </dl>
+
+<div class="node">
+<a name="Element-File"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Font-File">Font File</a>,
+Previous: <a rel="previous" accesskey="p" href="#Layout-File">Layout File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.3 Element File Format</h3>
+
+<p><a name="index-element_002c-file-format-800"></a><a name="index-format-of-element-files-801"></a><a name="index-file-format_002c-element-data-802"></a>
+Element files are used to describe one component which then may be used
+several times within one or more layouts. You will normally split the
+file into two parts, one for the pinout and one for the package description. 
+Using <code>m4</code> allows you to define pin names as macros in one file and
+include a package description file which evaluates the macros. See
+the resource <em>elementCommand</em> for more information. The pins (and pads)
+must appear in sequential order in the element file (new in 1.5) so that
+pin 1 must be the first PIN(...) in the file.
+
+   <p>Doing things this way makes it possible to use one package file for several
+different circuits. See the sample files <samp><span class="file">dil*</span></samp>.
+
+   <p>The lowest x and y coordinates of all sub-objects of an element are
+used as an attachment point for the cross hair cursor of the main
+window, unless the element has a mark, in which case that's the
+attachment point.
+
+<div class="node">
+<a name="Font-File"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Netlist-File">Netlist File</a>,
+Previous: <a rel="previous" accesskey="p" href="#Element-File">Element File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.4 Font File Format</h3>
+
+<p><a name="index-font-file_002c-format-of-803"></a><a name="index-format-of-font-files-804"></a><a name="index-file-format_002c-font-data-805"></a>
+A number of user defined Symbols are called a font. There is only one per
+layout. All symbols are made of lines. See the file <samp><span class="file">default_font</span></samp>
+as an example.
+
+   <p>The lowest x and y coordinates of all lines of a font are transformed to (0,0).
+
+<div class="node">
+<a name="Netlist-File"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Library-Contents-File">Library Contents File</a>,
+Previous: <a rel="previous" accesskey="p" href="#Font-File">Font File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.5 Netlist File Format</h3>
+
+<p><a name="index-netlist_002c-file-format-806"></a><a name="index-netlist_002c-reading-807"></a>
+Netlists read by <code>pcb-rnd</code> must have this simple text form:
+
+<pre class="example">     netname [style] NAME-PINNUM NAME2-PINNUM2 NAME3-PINNUM3 ... [\]
+</pre>
+   <p class="noindent">for each net on the layout. 
+where "netname" is the name of the net which must be unique for each
+net, [style] is an optional route-style name,
+NAME is the layout-name name given to an element,
+and PINNUM is the (usually numeric)
+pin number of the element that connects to the net
+(for details on pin numbering see <a href="#Element-Objects">Element Objects</a>). 
+Spaces or tabs separate the fields. 
+If the line ends with a "\" the
+net continues on the next line and the "\" is treated exactly as if it
+were a space.  If a NAME ends with a lower-case letter,
+all lower-case letters are stripped from the end of the NAME to determine the
+matching layout-name name.  For example:
+
+<pre class="example">          Data U1-3 U2abc-4 FLOP1a-7 Uabc3-A9
+</pre>
+   <p>specifies that the net called "Data" should have
+pin 3 of U1 connected to pin 4 of U2, to pin 7 of
+FLOP1 and to pin A9 of Uabc3.  Note that element name and
+pin number strings are case-sensitive. 
+It is up to you to name the elements so that their layout-name names
+agrees with the netlist.
+
+<div class="node">
+<a name="Library-Contents-File"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Library-File">Library File</a>,
+Previous: <a rel="previous" accesskey="p" href="#Netlist-File">Netlist File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.6 Library Contents File Format</h3>
+
+<p><a name="index-library-contents-file_002c-format-of-808"></a><a name="index-format-of-library-contents-809"></a><a name="index-file-format_002c-library-contents-810"></a>
+There is nothing like a special library format. The ones that have been
+introduced in 1.4.1 just use some nice (and time consuming) features of GNU
+<code>m4</code>. The only predefined format is the one of the contents file
+which is read during startup. It is made up of two basic line types:
+
+<pre class="example">     menu entry      = "TYPE="name
+     contents line   = template":"package":"value":"description
+     name            = String
+     template        = String
+     package         = String
+     value           = String
+     description     = String
+     String          = <anything except ":", "\n" and "\r">
+</pre>
+   <p>No leading white spaces or comments are allowed in this file. If you need
+either one, define a command that removes them before loading. Have a look
+to the <em>libraryContentsCommand</em> resource.
+
+   <p>The menu entry will appear in the selection menu at the top and of the
+library window.
+
+<div class="node">
+<a name="Library-File"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#File-Syntax">File Syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Library-Contents-File">Library Contents File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.7 Library File Format</h3>
+
+<p><a name="index-library-file_002c-format-of-811"></a><a name="index-format-of-libraries-812"></a><a name="index-file-format_002c-libraries-813"></a>
+This section provides an overview about the existing <code>m4</code> definitions
+of the elements. There are basically two different types of files. One
+to define element specific data like the pinout, package and so on, the
+other to define the values. For example the static RAM circuits 43256 and
+62256 are very similar. They therefore share a common definition in the
+macro file but are defined with two different value labels.
+
+   <p>The macro file entry:
+<pre class="example">     define(`Description_43256_dil', `SRAM 32Kx8')
+     define(`Param1_43256_dil', 28)
+     define(`Param2_43256_dil', 600)
+     define(`PinList_43256_dil', ``pin1', `pin2', ...')
+</pre>
+   <p>And the list file:
+<pre class="example">     43256_dil:N:43256:62256
+</pre>
+   <p>The macro must define a description, the pin list and up to two additional
+parameters that are passed to the package definitions. The first one is
+the number of pins whereas the second one defines for example the width
+of a package.
+
+   <p>It is very important to select a unique identifier for each macro. In
+the example this would be <em>43256_dil</em> which is also the templates name. 
+It is required by some low-level macros that
+<em>Description_, Param1_, Param2_</em> and <em>PinList_</em> are perpended.
+
+   <p>The list file uses a syntax:
+<pre class="example">     template:package:value[:more values]
+</pre>
+   <p>This means that the shown example will create two element entries with the
+same package and pinout but with different names.
+
+   <p>A number of packages are defined in <samp><span class="file">common.m4</span></samp>. Included are:
+
+<pre class="example">     DIL packages with suffix D, DW, J, JD, JG, N, NT, P
+     PLCC
+     TO3
+     generic connectors
+     DIN 41.612 connectors
+     zick-zack (SD suffix)
+     15 pin multiwatt
+</pre>
+   <p>If you are going to start your own library please take care about <code>m4</code>
+functions. Be aware of quoting and so on and, most important check your
+additional entry by calling the macro:
+
+<pre class="example">     CreateObject(`template', `value', `package suffix')
+</pre>
+   <p>If quoting is incorrect an endless loop may occur (broken by a out-of-memory
+message).
+
+   <p>The scripts in the <samp><span class="file">lib</span></samp> directory handle the creation of libraries
+as well as of their contents files. Querying is also supported.
+
+   <p>I know quite well that this description of the library implementation is
+not what some out there expect. But in my opinion it's much more useful to
+look at the comments and follow the macros step by step.
+
+<div class="node">
+<a name="File-Syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Object-Flags">Object Flags</a>,
+Previous: <a rel="previous" accesskey="p" href="#Library-File">Library File</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.8 File Syntax</h3>
+
+<p><a name="index-File-sytax-814"></a><a name="index-Syntax_002c-file-815"></a>
+
+<!-- key pcbfile -->
+<!-- ./../src/parse_y.y 143 -->
+   <p>A special note about units: Older versions of <code>pcb</code> used mils
+(1/1000 inch) as the base unit; a value of 500 in the file meant
+half an inch.  Newer versions uses a "high resolution" syntax,
+where the base unit is 1/100 of a mil (0.000010 inch); a value of 500 in
+the file means 5 mils.  As a general rule, the variants of each entry
+listed below which use square brackets are the high resolution formats
+and use the 1/100 mil units, and the ones with parentheses are the older
+variants and use 1 mil units.  Note that when multiple variants
+are listed, the most recent (and most preferred) format is the first
+listed.
+
+   <p>Symbolic and numeric flags (SFlags and NFlags) are described in
+<a href="#Object-Flags">Object Flags</a>.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Arc-syntax">Arc syntax</a>
+<li><a accesskey="2" href="#Attribute-syntax">Attribute syntax</a>
+<li><a accesskey="3" href="#Connect-syntax">Connect syntax</a>
+<li><a accesskey="4" href="#Cursor-syntax">Cursor syntax</a>
+<li><a accesskey="5" href="#DRC-syntax">DRC syntax</a>
+<li><a accesskey="6" href="#Element-syntax">Element syntax</a>
+<li><a accesskey="7" href="#ElementArc-syntax">ElementArc syntax</a>
+<li><a accesskey="8" href="#ElementLine-syntax">ElementLine syntax</a>
+<li><a accesskey="9" href="#FileVersion-syntax">FileVersion syntax</a>
+<li><a href="#Flags-syntax">Flags syntax</a>
+<li><a href="#Grid-syntax">Grid syntax</a>
+<li><a href="#Groups-syntax">Groups syntax</a>
+<li><a href="#Layer-syntax">Layer syntax</a>
+<li><a href="#Line-syntax">Line syntax</a>
+<li><a href="#Mark-syntax">Mark syntax</a>
+<li><a href="#Net-syntax">Net syntax</a>
+<li><a href="#Netlist-syntax">Netlist syntax</a>
+<li><a href="#Pad-syntax">Pad syntax</a>
+<li><a href="#PCB-syntax">PCB syntax</a>
+<li><a href="#Pin-syntax">Pin syntax</a>
+<li><a href="#PolyArea-syntax">PolyArea syntax</a>
+<li><a href="#Polygon-syntax">Polygon syntax</a>
+<li><a href="#Rat-syntax">Rat syntax</a>
+<li><a href="#Styles-syntax">Styles syntax</a>
+<li><a href="#Symbol-syntax">Symbol syntax</a>
+<li><a href="#SymbolLine-syntax">SymbolLine syntax</a>
+<li><a href="#Text-syntax">Text syntax</a>
+<li><a href="#Thermal-syntax">Thermal syntax</a>
+<li><a href="#Via-syntax">Via syntax</a>
+</ul>
+<!-- pcbfile Arc -->
+<div class="node">
+<a name="Arc-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Attribute-syntax">Attribute syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.1 Arc</h4>
+
+<!-- ./../src/parse_y.y 660 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Arc [X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]
+Arc (X Y Width Height Thickness Clearance StartAngle DeltaAngle NFlags)
+Arc (X Y Width Height Thickness StartAngle DeltaAngle NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X Y</var><dd>Coordinates of the center of the arc. 
+<br><dt><var>Width Height</var><dd>The width and height, from the center to the edge.  The bounds of the
+circle of which this arc is a segment, is thus 2*Width by
+2*Height. 
+<br><dt><var>Thickness</var><dd>The width of the copper trace which forms the arc. 
+<br><dt><var>Clearance</var><dd>The amount of space cleared around the arc when the line passes
+through a polygon.  The clearance is added to the thickness to get the
+thickness of the clear; thus the space between the arc and the polygon
+is Clearance/2 wide. 
+<br><dt><var>StartAngle</var><dd>The angle of one end of the arc, in degrees.  In PCB, an angle of zero
+points left (negative X direction), and 90 degrees points down
+(positive Y direction). 
+<br><dt><var>DeltaAngle</var><dd>The sweep of the arc.  This may be negative.  Positive angles sweep
+counterclockwise. 
+<br><dt><var>SFlags</var><dd>Symbolic or numeric flags. 
+<br><dt><var>NFlags</var><dd>Numeric flags. 
+</dl>
+
+<!-- pcbfile Attribute -->
+<div class="node">
+<a name="Attribute-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Connect-syntax">Connect syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Arc-syntax">Arc syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.2 Attribute</h4>
+
+<!-- ./../src/parse_y.y 1268 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Attribute ("Name" "Value")
+</pre>
+   </td></tr></table>
+
+   <p>Attributes allow boards and elements to have arbitrary data attached
+to them, which is not directly used by PCB itself but may be of use by
+other programs or users.
+
+     <dl>
+<dt><var>Name</var><dd>The name of the attribute
+
+     <br><dt><var>Value</var><dd>The value of the attribute.  Values are always stored as strings, even
+if the value is interpreted as, for example, a number.
+
+   </dl>
+
+<!-- pcbfile Connect -->
+<div class="node">
+<a name="Connect-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Cursor-syntax">Cursor syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Attribute-syntax">Attribute syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.3 Connect</h4>
+
+<!-- ./../src/parse_y.y 1258 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Connect ("PinPad")
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>PinPad</var><dd>The name of a pin or pad which is included in this net.  Pin and Pad
+names are named by the refdes and pin name, like <code>"U14-7"</code> for
+pin 7 of U14, or <code>"T4-E"</code> for pin E of T4. 
+</dl>
+
+<!-- pcbfile Cursor -->
+<div class="node">
+<a name="Cursor-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DRC-syntax">DRC syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Connect-syntax">Connect syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.4 Cursor</h4>
+
+<!-- ./../src/parse_y.y 335 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Cursor [X Y Zoom]
+Cursor (X Y Zoom)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X Y</var><dd>Location of the cursor when the board was saved. 
+<br><dt><var>Zoom</var><dd>The current zoom factor.  Note that a zoom factor of "0" means 1 mil
+per screen pixel, N means 2^N mils per screen pixel, etc.  The
+first variant accepts floating point numbers.  The special value
+"1000" means "zoom to fit"
+</dl>
+
+<!-- pcbfile DRC -->
+<div class="node">
+<a name="DRC-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Element-syntax">Element syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Cursor-syntax">Cursor syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.5 DRC</h4>
+
+<!-- ./../src/parse_y.y 376 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DRC [Bloat Shrink Line Silk Drill Ring]
+DRC [Bloat Shrink Line Silk]
+DRC [Bloat Shrink Line]
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Bloat</var><dd>Minimum spacing between copper. 
+<br><dt><var>Shrink</var><dd>Minimum copper overlap to guarantee connectivity. 
+<br><dt><var>Line</var><dd>Minimum line thickness. 
+<br><dt><var>Silk</var><dd>Minimum silk thickness. 
+<br><dt><var>Drill</var><dd>Minimum drill size. 
+<br><dt><var>Ring</var><dd>Minimum width of the annular ring around pins and vias. 
+</dl>
+
+<!-- pcbfile Element -->
+<div class="node">
+<a name="Element-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ElementArc-syntax">ElementArc syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#DRC-syntax">DRC syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.6 Element</h4>
+
+<!-- ./../src/parse_y.y 816 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] (
+Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags) (
+Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags) (
+Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags) (
+Element ("Desc" "Name" TX TY TDir TScale TNFlags) (
+   <small class="dots">...</small> contents <small class="dots">...</small>
+)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>SFlags</var><dd>Symbolic or numeric flags, for the element as a whole. 
+<br><dt><var>NFlags</var><dd>Numeric flags, for the element as a whole. 
+<br><dt><var>Desc</var><dd>The description of the element.  This is one of the three strings
+which can be displayed on the screen. 
+<br><dt><var>Name</var><dd>The name of the element, usually the reference designator. 
+<br><dt><var>Value</var><dd>The value of the element. 
+<br><dt><var>MX MY</var><dd>The location of the element's mark.  This is the reference point
+for placing the element and its pins and pads. 
+<br><dt><var>TX TY</var><dd>The upper left corner of the text (one of the three strings). 
+<br><dt><var>TDir</var><dd>The relative direction of the text.  0 means left to right for
+an unrotated element, 1 means up, 2 left, 3 down. 
+<br><dt><var>TScale</var><dd>Size of the text, as a percentage of the “default” size of of the
+font (the default font is about 40 mils high).  Default is 100 (40
+mils). 
+<br><dt><var>TSFlags</var><dd>Symbolic or numeric flags, for the text. 
+<br><dt><var>TNFlags</var><dd>Numeric flags, for the text. 
+</dl>
+
+   <p>Elements may contain pins, pads, element lines, element arcs,
+attributes, and (for older elements) an optional mark.  Note that
+element definitions that have the mark coordinates in the element
+line, only support pins and pads which use relative coordinates.  The
+pin and pad coordinates are relative to the mark.  Element definitions
+which do not include the mark coordinates in the element line, may
+have a Mark definition in their contents, and only use pin and pad
+definitions which use absolute coordinates.
+
+<!-- pcbfile ElementArc -->
+<div class="node">
+<a name="ElementArc-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ElementLine-syntax">ElementLine syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Element-syntax">Element syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.7 ElementArc</h4>
+
+<!-- ./../src/parse_y.y 927 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
+ElementArc (X Y Width Height StartAngle DeltaAngle Thickness)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X Y</var><dd>Coordinates of the center of the arc.  These are relative to the
+Element's mark point for new element formats, or absolute for older
+formats. 
+<br><dt><var>Width Height</var><dd>The width and height, from the center to the edge.  The bounds of the
+circle of which this arc is a segment, is thus 2*Width by
+2*Height. 
+<br><dt><var>StartAngle</var><dd>The angle of one end of the arc, in degrees.  In PCB, an angle of zero
+points left (negative X direction), and 90 degrees points down
+(positive Y direction). 
+<br><dt><var>DeltaAngle</var><dd>The sweep of the arc.  This may be negative.  Positive angles sweep
+counterclockwise. 
+<br><dt><var>Thickness</var><dd>The width of the silk line which forms the arc. 
+</dl>
+
+<!-- pcbfile ElementLine -->
+<div class="node">
+<a name="ElementLine-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#FileVersion-syntax">FileVersion syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#ElementArc-syntax">ElementArc syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.8 ElementLine</h4>
+
+<!-- ./../src/parse_y.y 925 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ElementLine [X1 Y1 X2 Y2 Thickness]
+ElementLine (X1 Y1 X2 Y2 Thickness)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X1 Y1 X2 Y2</var><dd>Coordinates of the endpoints of the line.  These are relative to the
+Element's mark point for new element formats, or absolute for older
+formats. 
+<br><dt><var>Thickness</var><dd>The width of the silk for this line. 
+</dl>
+
+<!-- pcbfile FileVersion -->
+<div class="node">
+<a name="FileVersion-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Flags-syntax">Flags syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#ElementLine-syntax">ElementLine syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.9 FileVersion</h4>
+
+<!-- ./../src/parse_y.y 258 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">FileVersion[Version]
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Version</var><dd>File format version.  This version number represents the date when the pcb file
+format was last changed. 
+</dl>
+
+   <p>Any version of pcb build from sources equal to or newer
+than this number should be able to read the file.  If this line is not present
+in the input file then file format compatibility is not checked.
+
+<!-- pcbfile Flags -->
+<div class="node">
+<a name="Flags-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Grid-syntax">Grid syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#FileVersion-syntax">FileVersion syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.10 Flags</h4>
+
+<!-- ./../src/parse_y.y 418 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Flags(Number)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Number</var><dd>A number, whose value is normally given in hex, individual bits of which
+represent pcb-wide flags as defined in <a href="#PCBFlags">PCBFlags</a>.
+
+   </dl>
+
+<!-- pcbfile Grid -->
+<div class="node">
+<a name="Grid-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Groups-syntax">Groups syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Flags-syntax">Flags syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.11 Grid</h4>
+
+<!-- ./../src/parse_y.y 294 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Grid [Step OffsetX OffsetY Visible]
+Grid (Step OffsetX OffsetY Visible)
+Grid (Step OffsetX OffsetY)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Step</var><dd>Distance from one grid point to adjacent points.  This value may be a
+floating point number for the first two variants. 
+<br><dt><var>OffsetX OffsetY</var><dd>The "origin" of the grid.  Normally zero. 
+<br><dt><var>Visible</var><dd>If non-zero, the grid will be visible on the screen. 
+</dl>
+
+<!-- pcbfile Groups -->
+<div class="node">
+<a name="Groups-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Layer-syntax">Layer syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Grid-syntax">Grid syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.12 Groups</h4>
+
+<!-- ./../src/parse_y.y 432 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Groups("String")
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>String</var><dd>
+Encodes the layer grouping information.  Each group is separated by a
+colon, each member of each group is separated by a comma.  Group
+members are either numbers from <code>1</code>..<var>N</var> for each layer, and
+the letters <code>c</code> or <code>s</code> representing the component side and
+solder side of the board.  Including <code>c</code> or <code>s</code> marks that
+group as being the top or bottom side of the board.
+
+     <pre class="example">          Groups("1,2,c:3:4:5,6,s:7,8")
+</pre>
+     </dl>
+
+<!-- pcbfile Layer -->
+<div class="node">
+<a name="Layer-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Line-syntax">Line syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Groups-syntax">Groups syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.13 Layer</h4>
+
+<!-- ./../src/parse_y.y 573 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Layer (LayerNum "Name") (
+   <small class="dots">...</small> contents <small class="dots">...</small>
+)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>LayerNum</var><dd>The layer number.  Layers are numbered sequentially, starting with 1. 
+The last two layers (9 and 10 by default) are solder-side silk and
+component-side silk, in that order. 
+<br><dt><var>Name</var><dd>The layer name. 
+<br><dt><var>contents</var><dd>The contents of the layer, which may include attributes, lines, arcs, rectangles,
+text, and polygons. 
+</dl>
+
+<!-- pcbfile Line -->
+<div class="node">
+<a name="Line-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Mark-syntax">Mark syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Layer-syntax">Layer syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.14 Line</h4>
+
+<!-- ./../src/parse_y.y 629 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Line [X1 Y1 X2 Y2 Thickness Clearance SFlags]
+Line (X1 Y1 X2 Y2 Thickness Clearance NFlags)
+Line (X1 Y1 X2 Y2 Thickness NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X1 Y1 X2 Y2</var><dd>The end points of the line
+<br><dt><var>Thickness</var><dd>The width of the line
+<br><dt><var>Clearance</var><dd>The amount of space cleared around the line when the line passes
+through a polygon.  The clearance is added to the thickness to get the
+thickness of the clear; thus the space between the line and the
+polygon is Clearance/2 wide. 
+<br><dt><var>SFlags</var><dd>Symbolic or numeric flags
+<br><dt><var>NFlags</var><dd>Numeric flags. 
+</dl>
+
+<!-- pcbfile Mark -->
+<div class="node">
+<a name="Mark-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Net-syntax">Net syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Line-syntax">Line syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.15 Mark</h4>
+
+<!-- ./../src/parse_y.y 929 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Mark [X Y]
+Mark (X Y)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X Y</var><dd>Coordinates of the Mark, for older element formats that don't have
+the mark as part of the Element line. 
+</dl>
+
+<!-- pcbfile Net -->
+<div class="node">
+<a name="Net-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Netlist-syntax">Netlist syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Mark-syntax">Mark syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.16 Net</h4>
+
+<!-- ./../src/parse_y.y 1235 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Net ("Name" "Style") (
+   <small class="dots">...</small> connects <small class="dots">...</small>
+)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Name</var><dd>The name of this net. 
+<br><dt><var>Style</var><dd>The routing style that should be used when autorouting this net. 
+</dl>
+
+<!-- pcbfile Netlist -->
+<div class="node">
+<a name="Netlist-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Pad-syntax">Pad syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Net-syntax">Net syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.17 Netlist</h4>
+
+<!-- ./../src/parse_y.y 1214 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Netlist ( ) (
+   <small class="dots">...</small> nets <small class="dots">...</small>
+)
+</pre>
+   </td></tr></table>
+
+<!-- pcbfile Pad -->
+<div class="node">
+<a name="Pad-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#PCB-syntax">PCB syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Netlist-syntax">Netlist syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.18 Pad</h4>
+
+<!-- ./../src/parse_y.y 1086 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
+Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
+Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
+Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>rX1 rY1 rX2 rY2</var><dd>Coordinates of the endpoints of the pad, relative to the element's
+mark.  Note that the copper extends beyond these coordinates by half
+the thickness.  To make a square or round pad, specify the same
+coordinate twice. 
+<br><dt><var>aX1 aY1 aX2 aY2</var><dd>Same, but absolute coordinates of the endpoints of the pad. 
+<br><dt><var>Thickness</var><dd>width of the pad. 
+<br><dt><var>Clearance</var><dd>add to thickness to get clearance width. 
+<br><dt><var>Mask</var><dd>width of solder mask opening. 
+<br><dt><var>Name</var><dd>name of pin
+<br><dt><var>Number</var><dd>number of pin
+<br><dt><var>SFlags</var><dd>symbolic or numerical flags
+<br><dt><var>NFlags</var><dd>numerical flags only
+</dl>
+
+<!-- pcbfile PCB -->
+<div class="node">
+<a name="PCB-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Pin-syntax">Pin syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Pad-syntax">Pad syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.19 PCB</h4>
+
+<!-- ./../src/parse_y.y 271 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PCB ["Name" Width Height]
+PCB ("Name" Width Height]
+PCB ("Name")
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Name</var><dd>Name of the PCB project
+<br><dt><var>Width Height</var><dd>Size of the board
+</dl>
+
+   <p>If you don't specify the size of the board, a very large default is
+chosen.
+
+<!-- pcbfile Pin -->
+<div class="node">
+<a name="Pin-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#PolyArea-syntax">PolyArea syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#PCB-syntax">PCB syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.20 Pin</h4>
+
+<!-- ./../src/parse_y.y 1013 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
+Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
+Pin (aX aY Thickness Drill "Name" "Number" NFlags)
+Pin (aX aY Thickness Drill "Name" NFlags)
+Pin (aX aY Thickness "Name" NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>rX rY</var><dd>coordinates of center, relative to the element's mark
+<br><dt><var>aX aY</var><dd>absolute coordinates of center. 
+<br><dt><var>Thickness</var><dd>outer diameter of copper annulus
+<br><dt><var>Clearance</var><dd>add to thickness to get clearance diameter
+<br><dt><var>Mask</var><dd>diameter of solder mask opening
+<br><dt><var>Drill</var><dd>diameter of drill
+<br><dt><var>Name</var><dd>name of pin
+<br><dt><var>Number</var><dd>number of pin
+<br><dt><var>SFlags</var><dd>symbolic or numerical flags
+<br><dt><var>NFlags</var><dd>numerical flags only
+</dl>
+
+<!-- pcbfile PolyArea -->
+<div class="node">
+<a name="PolyArea-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Polygon-syntax">Polygon syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Pin-syntax">Pin syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.21 PolyArea</h4>
+
+<!-- ./../src/parse_y.y 353 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PolyArea [Area]
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Area</var><dd>Minimum area of polygon island to retain. If a polygon has clearances that cause an isolated island to be created, then will only be retained if the area exceeds this minimum area. 
+</dl>
+
+<!-- pcbfile Polygon -->
+<div class="node">
+<a name="Polygon-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Rat-syntax">Rat syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#PolyArea-syntax">PolyArea syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.22 Polygon</h4>
+
+<!-- ./../src/parse_y.y 743 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Polygon (SFlags) (
+   <small class="dots">...</small> (X Y) <small class="dots">...</small>
+   <small class="dots">...</small> [X Y] <small class="dots">...</small>
+   Hole (
+      <small class="dots">...</small> (X Y) <small class="dots">...</small>
+      <small class="dots">...</small> [X Y] <small class="dots">...</small>
+   )
+   <small class="dots">...</small>
+)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>SFlags</var><dd>Symbolic or numeric flags. 
+<br><dt><var>X Y</var><dd>Coordinates of each vertex.  You must list at least three coordinates. 
+<br><dt><var>Hole (...)</var><dd>Defines a hole within the polygon's outer contour. There may be zero or more such sections. 
+</dl>
+
+<!-- pcbfile Rat -->
+<div class="node">
+<a name="Rat-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Styles-syntax">Styles syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Polygon-syntax">Polygon syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.23 Rat</h4>
+
+<!-- ./../src/parse_y.y 558 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Rat [X1 Y1 Group1 X2 Y2 Group2 SFlags]
+Rat (X1 Y1 Group1 X2 Y2 Group2 NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X1 Y1 X2 Y2</var><dd>The endpoints of the rat line. 
+<br><dt><var>Group1 Group2</var><dd>The layer group each end is connected on. 
+<br><dt><var>SFlags</var><dd>Symbolic or numeric flags. 
+<br><dt><var>NFlags</var><dd>Numeric flags. 
+</dl>
+
+<!-- pcbfile Styles -->
+<div class="node">
+<a name="Styles-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Symbol-syntax">Symbol syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Rat-syntax">Rat syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.24 Styles</h4>
+
+<!-- ./../src/parse_y.y 442 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Styles("String")
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>String</var><dd>
+Encodes the four routing styles <code>pcb</code> knows about.  The four styles
+are separated by colons.  Each style consists of five parameters as follows:
+
+          <dl>
+<dt><var>Name</var><dd>The name of the style. 
+<br><dt><var>Thickness</var><dd>Width of lines and arcs. 
+<br><dt><var>Diameter</var><dd>Copper diameter of pins and vias. 
+<br><dt><var>Drill</var><dd>Drill diameter of pins and vias. 
+<br><dt><var>Keepaway</var><dd>Minimum spacing to other nets.  If omitted, 10 mils is the default.
+
+     </dl>
+
+   </dl>
+
+<pre class="example">     Styles("Signal,10,40,20:Power,25,60,35:Fat,40,60,35:Skinny,8,36,20")
+     Styles["Logic,1000,3600,2000,1000:Power,2500,6000,3500,1000:
+        Line,4000,6000,3500,1000:Breakout,600,2402,1181,600"]
+</pre>
+   <p class="noindent">Note that strings in actual files cannot span lines; the above example
+is split across lines only to make it readable.
+
+<!-- pcbfile Symbol -->
+<div class="node">
+<a name="Symbol-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SymbolLine-syntax">SymbolLine syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Styles-syntax">Styles syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.25 Symbol</h4>
+
+<!-- ./../src/parse_y.y 1148 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Symbol [Char Delta] (
+Symbol (Char Delta) (
+   <small class="dots">...</small> symbol lines <small class="dots">...</small>
+)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Char</var><dd>The character or numerical character value this symbol represents. 
+Characters must be in single quotes. 
+<br><dt><var>Delta</var><dd>Additional space to allow after this character. 
+</dl>
+
+<!-- pcbfile SymbolLine -->
+<div class="node">
+<a name="SymbolLine-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Text-syntax">Text syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Symbol-syntax">Symbol syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.26 SymbolLine</h4>
+
+<!-- ./../src/parse_y.y 1197 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SymbolLine [X1 Y1 X2 Y1 Thickness]
+SymbolLine (X1 Y1 X2 Y1 Thickness)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X1 Y1 X2 Y2</var><dd>The endpoints of this line. 
+<br><dt><var>Thickness</var><dd>The width of this line. 
+</dl>
+
+<!-- pcbfile Text -->
+<div class="node">
+<a name="Text-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Thermal-syntax">Thermal syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#SymbolLine-syntax">SymbolLine syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.27 Text</h4>
+
+<!-- ./../src/parse_y.y 689 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Text [X Y Direction Scale "String" SFlags]
+Text (X Y Direction Scale "String" NFlags)
+Text (X Y Direction "String" NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X Y</var><dd>The location of the upper left corner of the text. 
+<br><dt><var>Direction</var><dd>0 means text is drawn left to right, 1 means up, 2 means right to left
+(i.e. upside down), and 3 means down. 
+<br><dt><var>Scale</var><dd>Size of the text, as a percentage of the “default” size of of the
+font (the default font is about 40 mils high).  Default is 100 (40
+mils). 
+<br><dt><var>String</var><dd>The string to draw. 
+<br><dt><var>SFlags</var><dd>Symbolic or numeric flags. 
+<br><dt><var>NFlags</var><dd>Numeric flags. 
+</dl>
+
+<!-- pcbfile Thermal -->
+<div class="node">
+<a name="Thermal-syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Via-syntax">Via syntax</a>,
+Previous: <a rel="previous" accesskey="p" href="#Text-syntax">Text syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.28 Thermal</h4>
+
+<!-- ./../src/parse_y.y 365 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Thermal [Scale]
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>Scale</var><dd>Relative size of thermal fingers.  A value of 1.0 makes the finger
+width twice the clearance gap width (measured across the gap, not
+diameter).  The normal value is 0.5, which results in a finger width
+the same as the clearance gap width. 
+</dl>
+
+<!-- pcbfile Via -->
+<div class="node">
+<a name="Via-syntax"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Thermal-syntax">Thermal syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Syntax">File Syntax</a>
+
+</div>
+
+<h4 class="subsection">8.8.29 Via</h4>
+
+<!-- ./../src/parse_y.y 498 -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Via [X Y Thickness Clearance Mask Drill "Name" SFlags]
+Via (X Y Thickness Clearance Mask Drill "Name" NFlags)
+Via (X Y Thickness Clearance Drill "Name" NFlags)
+Via (X Y Thickness Drill "Name" NFlags)
+Via (X Y Thickness "Name" NFlags)
+</pre>
+   </td></tr></table>
+
+     <dl>
+<dt><var>X Y</var><dd>coordinates of center
+<br><dt><var>Thickness</var><dd>outer diameter of copper annulus
+<br><dt><var>Clearance</var><dd>add to thickness to get clearance diameter
+<br><dt><var>Mask</var><dd>diameter of solder mask opening
+<br><dt><var>Drill</var><dd>diameter of drill
+<br><dt><var>Name</var><dd>string, name of via (vias have names?) 
+<br><dt><var>SFlags</var><dd>symbolic or numerical flags
+<br><dt><var>NFlags</var><dd>numerical flags only
+</dl>
+
+<!-- pcbfile ~objectflags -->
+<!-- ./../src/const.h 110 -->
+<div class="node">
+<a name="Object-Flags"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#PCBFlags">PCBFlags</a>,
+Previous: <a rel="previous" accesskey="p" href="#File-Syntax">File Syntax</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.9 Object Flags</h3>
+
+<p>Note that object flags can be given numerically (like <code>0x0147</code>)
+or symbolically (like <code>"found,showname,square"</code>.  Some numeric
+values are reused for different object types.  The table below lists
+the numeric value followed by the symbolic name.
+
+     <dl>
+<dt><code>0x0001 pin</code><dd>If set, this object is a pin.  This flag is for internal use only. 
+<br><dt><code>0x0002 via</code><dd>Likewise, for vias. 
+<br><dt><code>0x0004 found</code><dd>If set, this object has been found by <code>FindConnection()</code>. 
+<br><dt><code>0x0008 hole</code><dd>For pins and vias, this flag means that the pin or via is a hole
+without a copper annulus. 
+<br><dt><code>0x0010 rat</code><dd>If set for a line, indicates that this line is a rat line instead of a
+copper trace. 
+<br><dt><code>0x0010 pininpoly</code><dd>For pins and pads, this flag is used internally to indicate that the
+pin or pad overlaps a polygon on some layer. 
+<br><dt><code>0x0010 clearpoly</code><dd>For polygons, this flag means that pins and vias will normally clear
+these polygons (thus, thermals are required for electrical
+connection).  When clear, polygons will solidly connect to pins and
+vias. 
+<br><dt><code>0x0010 hidename</code><dd>For elements, when set the name of the element is hidden. 
+<br><dt><code>0x0020 showname</code><dd>For elements, when set the names of pins are shown. 
+<br><dt><code>0x0020 clearline</code><dd>For lines and arcs, the line/arc will clear polygons instead of
+connecting to them. 
+<br><dt><code>0x0020 fullpoly</code><dd>For polygons, the full polygon is drawn (i.e. all parts instead of only the biggest one). 
+<br><dt><code>0x0040 selected</code><dd>Set when the object is selected. 
+<br><dt><code>0x0080 onsolder</code><dd>For elements and pads, indicates that they are on the solder side. 
+<br><dt><code>0x0080 auto</code><dd>For lines and vias, indicates that these were created by the
+autorouter. 
+<br><dt><code>0x0100 square</code><dd>For pins and pads, indicates a square (vs round) pin/pad. 
+<br><dt><code>0x0200 rubberend</code><dd>For lines, used internally for rubber band moves. 
+<br><dt><code>0x0200 warn</code><dd>For pins, vias, and pads, set to indicate a warning. 
+<br><dt><code>0x0400 usetherm</code><dd>Obsolete, indicates that pins/vias should be drawn with thermal
+fingers. 
+<br><dt><code>0x0400</code><dd>Obsolete, old files used this to indicate lines drawn on silk. 
+<br><dt><code>0x0800 octagon</code><dd>Draw pins and vias as octagons. 
+<br><dt><code>0x1000 drc</code><dd>Set for objects that fail DRC. 
+<br><dt><code>0x2000 lock</code><dd>Set for locked objects. 
+<br><dt><code>0x4000 edge2</code><dd>For pads, indicates that the second point is closer to the edge.  For
+pins, indicates that the pin is closer to a horizontal edge and thus
+pinout text should be vertical. 
+<br><dt><code>0x8000 marker</code><dd>Marker used internally to avoid revisiting an object. 
+<br><dt><code>0x10000 nopaste</code><dd>For pads, set to prevent a solderpaste stencil opening for the
+pad.  Primarily used for pads used as fiducials. 
+</dl>
+   <!-- pcbfile ~pcbflags -->
+<!-- ./../src/const.h 149 -->
+<div class="node">
+<a name="PCBFlags"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Object-Flags">Object Flags</a>,
+Up: <a rel="up" accesskey="u" href="#File-Formats">File Formats</a>
+
+</div>
+
+<h3 class="section">8.10 PCBFlags</h3>
+
+     <dl>
+<dt><code>0x00001</code><dd>Pinout displays pin numbers instead of pin names. 
+<br><dt><code>0x00002</code><dd>Use local reference for moves, by setting the mark at the beginning of
+each move. 
+<br><dt><code>0x00004</code><dd>When set, only polygons and their clearances are drawn, to see if
+polygons have isolated regions. 
+<br><dt><code>0x00008</code><dd>Display DRC region on crosshair. 
+<br><dt><code>0x00010</code><dd>Do all move, mirror, rotate with rubberband connections. 
+<br><dt><code>0x00020</code><dd>Display descriptions of elements, instead of refdes. 
+<br><dt><code>0x00040</code><dd>Display names of elements, instead of refdes. 
+<br><dt><code>0x00080</code><dd>Auto-DRC flag.  When set, PCB doesn't let you place copper that
+violates DRC. 
+<br><dt><code>0x00100</code><dd>Enable 'all-direction' lines. 
+<br><dt><code>0x00200</code><dd>Switch starting angle after each click. 
+<br><dt><code>0x00400</code><dd>Force unique names on board. 
+<br><dt><code>0x00800</code><dd>New lines/arc clear polygons. 
+<br><dt><code>0x01000</code><dd>Crosshair snaps to pins and pads. 
+<br><dt><code>0x02000</code><dd>Show the solder mask layer. 
+<br><dt><code>0x04000</code><dd>Draw with thin lines. 
+<br><dt><code>0x08000</code><dd>Move items orthogonally. 
+<br><dt><code>0x10000</code><dd>Draw autoroute paths real-time. 
+<br><dt><code>0x20000</code><dd>New polygons are full ones. 
+<br><dt><code>0x40000</code><dd>Names are locked, the mouse cannot select them. 
+<br><dt><code>0x80000</code><dd>Everything but names are locked, the mouse cannot select anything else. 
+<br><dt><code>0x100000</code><dd>New polygons are full polygons. 
+<br><dt><code>0x200000</code><dd>When set, element names are not drawn. 
+</dl>
+
+<!--  chapter 7 - -->
+<div class="node">
+<a name="Library-Creation"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Schematic-Frontends">Schematic Frontends</a>,
+Previous: <a rel="previous" accesskey="p" href="#File-Formats">File Formats</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">8 Library Creation</h2>
+
+<p><a name="index-library-creation-816"></a>
+This chapter provides a detailed look at how footprint libraries are
+created and used.
+
+   <p>pcb-rnd footprint libraries store each footprint in its own
+file. The footprints are created graphically by placing pads and then
+converting a group of pads to a component. This library method has the
+advantage of being quick to learn and it is easily to build single
+footprints quickly. If you are building a family of parts, however, the
+additional effort in creating each one individually makes this approach
+undesirable. In addition, creating a part with a large pin count
+can be quite tedious when done by hand.
+
+<h3 class="section">9.1 Old Style (m4) Libraries</h3>
+
+<p>The old style libraries for pcb use the <code>m4</code> macro processor to
+allow the definition of a family of parts.  There are several files
+associated with the old style library.  The file <samp><span class="file">common.m4</span></samp> is the
+top level file associated with the library.  <samp><span class="file">common.m4</span></samp> defines a
+few utility macros which are used by other portions of the library,
+and then includes a predefined set of library files (the lines like
+<code>include(geda.inc)</code>).
+
+<h4 class="subsection">9.1.1 Overview of Oldlib Operation</h4>
+
+<p>The big picture view of the old style library system is that the library
+is simply a collection of macro definitions.  The macros are written in
+the <code>m4</code> macro language.  An example of a macro and what it expands
+to is the following.  One of the predefined footprints in the library
+which comes with PCB is the <code>PKG_SO8</code> macro.  Note that all the
+footprint macros begin with <code>PKG_</code>.  For this particular example,
+<code>PKG_SO8</code> is a macro for an 8-pin small outline surface mount
+package.  All of the footprint macros take 3 arguments.  The first is the
+canonical name of the footprint on the board.  In this case "SO8" is an
+appropriate name.  The second argument is the reference designator on
+the board such as "U1" or "U23".  The third and final argument is the
+value.  For an integrated circuit this is usually the part number such
+as "MAX4107" or "78L05" and for a component such as a resistor or
+capacitor it is the resistance or capacitance.  The complete call to the
+macro in our example is ‘<samp><span class="samp">PKG_SO8(SO8, U1, MAX4107)</span></samp>’.  When
+processed by <code>m4</code> using the macros defined in the PCB library, this
+macro expands to
+<pre class="example">     Element(0x00 "SO8" "U1" "MAX4107" 146 50 3 100 0x00)
+     (
+     	Pad(10 25 38 25 20 "1" 0x00)
+     	Pad(10 75 38 75 20 "2" 0x100)
+     	Pad(10 125 38 125 20 "3" 0x100)
+     	Pad(10 175 38 175 20 "4" 0x100)
+     	Pad(214 175 242 175 20 "5" 0x100)
+     	Pad(214 125 242 125 20 "6" 0x100)
+     	Pad(214 75 242 75 20 "7" 0x100)
+     	Pad(214 25 242 25 20 "8" 0x100)
+     	ElementLine(0 0 151 0 10)
+     	ElementArc(126 0 25 25 0 180 10)
+     	ElementLine(101 0 252 0 10)
+     	ElementLine(252 0 252 200 10)
+     	ElementLine(252 200 0 200 10)
+     	ElementLine(0 200 0 0 10)
+     	Mark(29 25)
+     )
+</pre>
+   <p>which is the actual definition of the footprint that the PCB program
+works with.  As a user of PCB the only time you will need or want to run
+<code>m4</code> directly is when you are debugging a new library addition.  In
+normal operation, the calls to <code>m4</code> are made by helper scripts that
+come with PCB.
+
+   <p>Tools such as <code>gsch2pcb</code> (used to interface the gEDA schematic
+capture program to PCB layout) will call <code>m4</code> to produce an initial
+PCB layout that includes all the components on a schematic.  In
+addition, when manually instantiating parts from within PCB, <code>m4</code>
+will be called by PCB's helper scripts to produce the footprints.
+
+<h4 class="subsection">9.1.2 The Library Scripts</h4>
+
+<p>There are several scripts that are used for processing the m4
+libraries.  This section briefly describes these scripts and details how
+they are used by PCB.
+
+<h5 class="subsubsection">9.1.2.1 Scripts Used During Compilation</h5>
+
+<p>The scripts described in this section are used during compilation of
+PCB.  They are run automatically by the build system, but are described
+here to help document the complete library processing that occurs. 
+During the build of PCB, the following actions are taken.  The
+<code>CreateLibrary.sh</code> script is run to produce an M4 "frozen file". 
+This frozen file is simply a partially processed M4 input file which can
+be loaded by M4 more quickly than the original input file.
+
+   <p>A typical call to <code>CreateLibrary.sh</code> used during the compilation of
+PCB is:
+<pre class="example">     ./CreateLibrary.sh -I . pcblib ./common.m4 TTL_74xx_DIL.m4
+     connector.m4 crystal.m4 generic.m4 genericsmt.m4 gtag.m4
+     jerry.m4 linear.m4 logic.m4 lsi.m4 memory.m4 optical.m4 pci.m4
+     resistor_0.25W.m4 resistor_adjust.m4 resistor_array.m4
+     texas_inst_amplifier.m4 texas_inst_voltage_reg.m4
+     transistor.m4 geda.m4
+</pre>
+   <p>The ‘<samp><span class="samp">-I .</span></samp>’ says to search in the current directory for the
+<samp><span class="file">.m4</span></samp> files.  The output frozen file is <samp><span class="file">pcblib</span></samp>.  The main
+<samp><span class="file">common.m4</span></samp> file is listed as well as all of the <samp><span class="file">*.m4</span></samp> files
+which define the components in the library.
+
+   <p>In addition, a library contents file is created during the build with
+the <code>CreateLibraryContents.sh</code> script. 
+A typical call to <code>CreateLibrary.sh</code> used during the compilation of
+PCB is:
+<pre class="example">     ./CreateLibraryContents.sh -I . ./common.m4 TTL_74xx_DIL.list
+     connector.list crystal.list generic.list genericsmt.list gtag.list
+     jerry.list linear.list logic.list lsi.list memory.list optical.list
+     pci.list resistor_0.25W.list resistor_adjust.list resistor_array.list
+     texas_inst_amplifier.list texas_inst_voltage_reg.list transistor.list
+     geda.list > pcblib.contents
+</pre>
+   <p>The <samp><span class="file">pcblib.contents</span></samp> file is used by the PCB program to define the
+libraries and components which will be displayed when you bring up
+the library window from within PCB.  An example of part of the
+<samp><span class="file">pcblib.contents</span></samp> file is:
+<pre class="example">     TYPE=~TTL 74xx DIL
+     7400_dil:N:7400:4 dual-NAND
+     7401_dil:N:7401:4 dual-NAND OC
+     7402_dil:N:7402:4 dual-NOR
+     TYPE=~geda
+     geda_DIP6:DIP6:DIP6:Dual in-line package, narrow (300 mil)
+     geda_DIP8:DIP8:DIP8:Dual in-line package, narrow (300 mil)
+     geda_DIP14:DIP14:DIP14:Dual in-line package, narrow (300 mil)
+     geda_ACY300:ACY300:ACY300:Axial non-polar component,
+</pre>
+   <p>The <code>TYPE=</code> lines define the library name that will show up in the
+library window in PCB.  The other lines define the actual components in
+the library.
+
+<h5 class="subsubsection">9.1.2.2 Scripts Used by PCB at Runtime</h5>
+
+<p>When PCB is first executed, it makes a call to the
+<code>ListLibraryContents.sh</code> script.  This script provides the PCB
+program with the contents of the library contents file created when PCB
+was compiled.  A typical call to <code>ListLibraryContents.sh</code> is
+<pre class="example">     ../lib/ListLibraryContents.sh .:/tmp/pcb-20030903/src/../lib pcblib
+</pre>
+   <p>This command says to search the path
+‘<samp><span class="samp">.:/tmp/pcb-20030903/src/../lib</span></samp>’ for a file called
+<samp><span class="file">pcblib.contents</span></samp> (the <samp><span class="file">.contents</span></samp> part is added
+automatically) and display the contents of the file. 
+PCB parses this output and generates the library window entries.
+
+   <p>When you pick a library component from the library window, PCB calls the
+<code>QueryLibrary.sh</code> script to actually pull the footprint into the
+layout.  For example, when the ACY300 component is selected from the
+<code>~geda</code> library, the generated call may be:
+
+<pre class="example">     /tmp/pcb-20030903/src/../lib/QueryLibrary.sh
+     .:/tmp/pcb-20030903/src/../lib pcblib geda_ACY300 ACY300
+     ACY300
+</pre>
+   <p>If you were to run this command by hand you would see the PCB code for
+the element:
+<pre class="example">     Element(0x00 "Axial non-polar component," "" "ACY300" 245 70 0 100 0x00)
+     (
+     	Pin(0 25 50 20 "1" 0x101)
+     	Pin(300 25 50 20 "2" 0x01)
+     
+     	ElementLine(0 25 75 25 10)
+     	ElementLine(225 25 300 25 10)
+     
+     	ElementLine(75 0 225 0 10)
+     	ElementLine(225 0 225 50 10)
+     	ElementLine(225 50 75 50 10)
+     	ElementLine(75 50 75 0 10)
+     
+     #       ElementArc(X1 Y 50 50 270 180 10)
+     #       ElementArc(X2 Y 50 50 90 180 10)
+     
+     	Mark(75 25)
+     )
+</pre>
+   <h4 class="subsection">9.1.3 Creating an Oldlib Footprint</h4>
+
+<p>This section provides a complete example of defining a family of
+footprints using the M4 style library.  As a vehicle for this example, a
+family of footprints for surface mount resistors and capacitors will be
+developed.   The file <samp><span class="file">example.inc</span></samp> should have been installed on
+your system as <samp><span class="file">$prefix/share/examples/oldlib/example.inc</span></samp> where
+<samp><span class="file">$prefix</span></samp> is often times <samp><span class="file">/usr/local</span></samp>.
+
+   <p>The <samp><span class="file">example.inc</span></samp> file defines a macro called
+<code>COMMON_PKG_RCSMT</code> which is a generic definition for a surface
+mount footprint with two identical, rectangular pads.  This macro will
+be called with different parameters to fill out the family of parts. 
+The arguments to the <code>COMMON_PKG_RCSMT</code> are:
+<pre class="example">     # -------------------------------------------------------------------
+     # the definition for surface mount resistors and capacitors
+     # $1: canonical name
+     # $2: name on PCB
+     # $3: value
+     # $4: pad width   (in direction perpendicular to part)
+     # $5: pad length  (in direction parallel with part)
+     # $6: pad spacing (center to center)
+     # $7: distance from edge of pad to silk (in direction
+     #     perpendicular to part)
+     # $8: distance from edge of pad to silk (in direction parallel
+     #     with part)
+     # $9: Set to "no" to skip silk screen on the sides of the part
+</pre>
+   <pre class="example">     define(`COMMON_PKG_RCSMT',
+     	`define(`XMIN', `eval( -1*`$6'/2 - `$5'/2 - `$8')')
+     	define(`XMAX', `eval(  `$6'/2 + `$5'/2 + `$8')')
+     	define(`YMIN', `eval(-1*`$4'/2 - `$7')')
+     	define(`YMAX', `eval(   `$4'/2 + `$7')')
+     Element(0x00 "$1" "$2" "$3" eval(XMIN+20) eval(YMAX+20) 0 100 0x00)
+     (
+     	ifelse(0, eval($4>$5),
+     	# Pads which have the perpendicular pad dimension less
+     	# than or equal to the parallel pad dimension
+     	Pad(eval(-1*(   $6 + $5 - $4)/2) 0
+     	    eval((-1*$6 + $5 - $4)/2) 0 eval($4) "1" 0x100)
+     	Pad(eval(-1*(-1*$6 + $5 - $4)/2) 0
+     	    eval((   $6 + $5 - $4)/2) 0 eval($4) "2" 0x100)
+     	,
+     	# Pads which have the perpendicular pad dimension greater
+     	# than or equal to the parallel pad dimension
+     	Pad(eval(-1*$6/2) eval(-1*($4 - $5)/2)
+     	    eval(-1*$6/2)  eval(($4 - $5)/2) eval($5) "1" 0x100)
+     	Pad(eval(   $6/2) eval(-1*($4 - $5)/2)
+     	    eval(   $6/2)  eval(($4 - $5)/2) eval($5) "2" 0x100)
+     	)
+     
+     	# silk screen
+     	# ends
+     	ElementLine(XMIN YMIN XMIN YMAX 10)
+     	ElementLine(XMAX YMAX XMAX YMIN 10)
+     	# sides
+     ifelse($9,"no",
+     	#skip side silk
+     	,
+     	ElementLine(XMIN YMIN XMAX YMIN 10)
+     	ElementLine(XMAX YMAX XMIN YMAX 10)
+     )
+     	Mark(0 0)
+     )')
+</pre>
+   <p>Note that the part has been defined with the mark located at
+<code>(0,0)</code> and that the pads have been placed with the mark at the
+common centroid of the footprint.  While not a requirement, this is
+highly desirable when developing a library that will need to interface
+with a pick and place machine used for factory assembly of a board.
+
+   <p>The final part of <samp><span class="file">example.inc</span></samp> defines particular versions of the
+generic footprint we have created.  These particular versions correspond
+to various industry standard package sizes.
+<pre class="example">     # 0402 package
+     #
+     # 30x30 mil pad, 15 mil metal-metal spacing=>
+     # 15 + 15 + 15 = 45 center-to-center
+     define(`PKG_RC0402',
+       `COMMON_PKG_RCSMT(`$1', `$2', `$3', 30, 30, 45, 0, 10, "no")')
+     
+     # 0603 package
+     #
+     # 40x40 mil pad, 30 mil metal-metal spacing=>
+     #  30 + 20 + 20 = 70 center-to-center
+     define(`PKG_RC0603',
+       `COMMON_PKG_RCSMT(`$1', `$2', `$3', 40, 40, 70, 10, 10)')
+     
+     # 1206 package
+     #
+     # 40x60 mil pad, 90 mil metal-metal spacing=>
+     #  90 + 20 + 20 = 130 center-to-center
+     define(`PKG_RC1206',
+       `COMMON_PKG_RCSMT(`$1', `$2', `$3', 60, 40, 130, 10, 10)')
+</pre>
+   <p>At this point, the <samp><span class="file">example.inc</span></samp> file could be used by third party
+tools such as <code>gsch2pcb</code>.  However to fully integrate our
+footprints into PCB we need to create the <samp><span class="file">example.m4</span></samp> and
+<samp><span class="file">example.list</span></samp> files.  The <samp><span class="file">example.m4</span></samp> file defines
+descriptions for the new footprints.
+<pre class="example">     define(`Description_my_RC0402',
+       ``Standard SMT resistor/capacitor (0402)'')
+     define(`Description_my_RC0603',
+       ``Standard SMT resistor/capacitor (0603)'')
+     define(`Description_my_RC1206',
+       ``Standard SMT resistor/capacitor (1206)'')
+</pre>
+   <p>Finally we need to create the <samp><span class="file">example.list</span></samp> file.
+<pre class="example">     my_RC0402:RC0402:RES0402
+     my_RC0402:RC0402:CAP0402
+     my_RC0603:RC0603:RES0603
+     my_RC0603:RC0603:CAP0603
+     my_RC1206:RC1206:RES1206
+     my_RC1206:RC1206:CAP1206
+</pre>
+   <p>The first field in the list file has the name corresponding to the
+Description definitions in <samp><span class="file">example.m4</span></samp>.  The second field is the
+template name which corresponds to the macros <code>PKG_*</code> we defined in
+<samp><span class="file">example.inc</span></samp> with the leading <code>PKG_</code> removed.  It is the
+second field which controls what footprint will actually appear on the
+board.  The final
+field is the name of the part type on the board.  The first line in our
+<samp><span class="file">example.list</span></samp> file will produce a menu entry in the library window
+that reads:
+<pre class="example">     CAP0402, Standard SMT resistor/capacitor (0402)
+</pre>
+   <p>The <code>CAP0402</code> portion comes directly from the third field in
+<code>example.list</code> and the longer description comes from descriptions
+macros in <code>example.m4</code>.  Please note that any extra white space
+at the end of a line in the <samp><span class="file">.list</span></samp> files will cause them to
+not work properly.
+
+<h4 class="subsection">9.1.4 Troubleshooting Old Style Libraries</h4>
+
+<p>A powerful technique to help debug problems with libraries is to invoke
+the <code>m4</code> processor directly.  This approach will provide error
+output which is not visible from within PCB.  The following example
+shows how one might try to debug an 8 pin small outline (SO8) package.  The
+macro name for the package is PKG_SO8.  In this example, the
+canonical name that is to be associated with the part is SO8, the
+reference designator is U1, and the value is MAX4107 (the part number).
+
+<pre class="example">     echo "PKG_SO8(SO8, U1, MAX4107)" | \
+        gm4 common.m4 - | \
+        awk '/^[ \t]*$/ {next} {print}' | \
+        more
+</pre>
+   <p>The <code>awk</code> call simply removes blank lines which make the output
+hard to read.
+
+   <p>For this particular example, the output is:
+<pre class="example">     Element(0x00 "SO8" "U1" "MAX4107" 146 50 3 100 0x00)
+     (
+     	Pad(10 25 38 25 20 "1" 0x00)
+     	Pad(10 75 38 75 20 "2" 0x100)
+     	Pad(10 125 38 125 20 "3" 0x100)
+     	Pad(10 175 38 175 20 "4" 0x100)
+     	Pad(214 175 242 175 20 "5" 0x100)
+     	Pad(214 125 242 125 20 "6" 0x100)
+     	Pad(214 75 242 75 20 "7" 0x100)
+     	Pad(214 25 242 25 20 "8" 0x100)
+     	ElementLine(0 0 151 0 10)
+     	ElementArc(126 0 25 25 0 180 10)
+     	ElementLine(101 0 252 0 10)
+     	ElementLine(252 0 252 200 10)
+     	ElementLine(252 200 0 200 10)
+     	ElementLine(0 200 0 0 10)
+     	Mark(29 25)
+     )
+</pre>
+   <h3 class="section">9.2 New Style Libraries</h3>
+
+<p>Footprints for the new style library are created graphically using the
+PCB program.  A single footprint is saved in each file.
+
+<h4 class="subsection">9.2.1 Creating Newlib Footprints</h4>
+
+<p>To create
+     <ol type=1 start=1>
+<li>Start PCB with an empty layout. 
+<li>Make the component layer active. 
+<li>For a leaded part, select the via tool and place vias where the
+pads for the part should go.  For surface mount pads, draw line
+segments.  Note that until the footprint is completed, the surface
+mount pads will remain rounded.  Currently a rectangle or polygon
+may not be used as a pad. 
+<li>For each via and line segment which will become a pad, select it
+and press 'n' to be able to enter a name.  Enter
+the pin number and press enter. 
+<li>Make the silk layer active. 
+<li>Using the line and arc tools, draw a silk screen outline for the
+part. 
+<li>Using the selection tool, select all of the pins and silk screen
+for the part. 
+<li>Place the pointer above the reference point for the part.  This is
+typically the common centroid.  Keeping the pointer there, shift-right-click
+to bring up the popup menu and choose "convert
+selection to element". 
+<li>At this point, the vias, line segments, and silk screen will have
+been converted to an element.  To change any of the line segments to
+have square ends rather than round ends, select the pads by holding
+down the shift key and clicking each pad with the center mouse button. 
+Now under the Select menu, "Change square-flag of selected objects"
+section, choose "Pins". 
+<li>Select the element, shift-right-click to bring up the popup menu, and
+choose "Copy Selection to Buffer".  Now left-click on the center of
+the new element. 
+<li>Under the buffer menu, choose "save buffer elements to file" to
+save the new footprint to a file. 
+<li>Press ESC to exit from buffer mode.
+        </ol>
+
+<h4 class="subsection">9.2.2 Modifying Newlib Footprints</h4>
+
+     <ol type=1 start=1>
+<li>In the <code>pcb-rnd</code> program, instantiate the footprint you wish to modify. 
+<li>Using the selection tool, select the footprint. 
+<li>Now left-click on the selected element, this brings up a popup menu, choose
+"Cut Selection to Buffer" from the popup menu. 
+<li>Under the buffer menu, choose "break buffer element to pieces",
+and then left-click to place the broken apart footprint to an open area of
+the layout.  Note that you must use the items under the buffer menu, the
+items with the same names in the popup menu do not work. 
+<li>Make your desired modifications to the footprint and then convert
+the pieces back to an element using the same procedure as when starting
+from scratch on a new footprint.
+     </ol>
+
+<!--  chapter 8 - -->
+<div class="node">
+<a name="Schematic-Frontends"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Installation">Installation</a>,
+Previous: <a rel="previous" accesskey="p" href="#Library-Creation">Library Creation</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">9 Schematic Capture for PCB</h2>
+
+<p><a name="index-schematic-capture-817"></a><a name="index-schematic-frontend-818"></a>
+When designing a circuit board of any complexity, a schematic capture
+front-end for the design is highly desired.  Any schematic capture
+program which is able to generate a netlist in a user defined format as
+well as a bill of materials can be made to work with PCB.  Currently, we
+are aware of two freely available schematic capture programs which can
+interface with PCB.  This chapter shows how a design can be taken from
+start to finish using either of these two tools for schematic capture
+and PCB for layout.
+
+<ul class="menu">
+<li><a accesskey="1" href="#gEDA">gEDA</a>:           Interfacing with GNU EDA (gEDA). 
+<li><a accesskey="2" href="#xcircuit">xcircuit</a>:       Interfacing with xcircuit. 
+</ul>
+
+<div class="node">
+<a name="gEDA"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#xcircuit">xcircuit</a>,
+Up: <a rel="up" accesskey="u" href="#Schematic-Frontends">Schematic Frontends</a>
+
+</div>
+
+<h3 class="section">10.1 gEDA</h3>
+
+<p><a name="index-gschem_002c-how-to-interface-with-819"></a><a name="index-gEDA_002c-how-to-interface-with-820"></a>
+This section shows how to use gEDA as the schematic capture front-end for
+a PCB design.  This section is not intended to be complete documentation
+on gEDA and it is assumed that the user has at least some familiarity
+with the gEDA suite of programs.
+
+   <p>The basic steps in a gEDA + PCB design flow are:
+     <ol type=1 start=1>
+<li>Set up project directories
+<li>Set up gEDA (gschem/gnetlist) config files
+<li>Set up gsch2pcb config files
+<li>Capture schematics using <code>gschem</code> (part of gEDA)
+<li>Create any unique PCB footprints needed for the design
+<li>Generate initial PCB design using <code>gsch2pcb</code> (part of gEDA)
+<li>Layout circuit board using <code>pcb</code>
+<li>Make any additional schematic changes with <code>gschem</code> and
+forward annotate to PCB with <code>gsch2pcb</code>
+<li>Generate photoplot files (RS-274X, also known as "Gerber") for
+board vendor
+        </ol>
+
+<h4 class="subsection">10.1.1 Set Up Project Directories</h4>
+
+<p>Although not required, a typical project directory will contain the
+schematics and board layout at the top level. 
+Schematic symbols and circuit board footprints which are unique to this
+project are stored in subdirectories.  For this example, <samp><span class="file">sym</span></samp>
+contains the project specific schematic symbols and <samp><span class="file">pkg</span></samp> contains
+the project specific footprints.  Set up the project subdirectory and
+subdirectories by executing:
+<pre class="example">     mkdir ~/myproj
+     cd ~/myproj
+     mkdir sym
+     mkdir pkg
+     mkdir pkg/newlib
+     mkdir pkg/m4
+</pre>
+   <h4 class="subsection">10.1.2 Set Up gEDA Config Files</h4>
+
+<p>The gEDA tools, specifically <code>gschem</code> and <code>gnetlist</code>, use
+configuration files to set the search path for symbol libraries in
+addition to other user preferences.  Create a file in the top level
+project directory called <samp><span class="file">gschemrc</span></samp>.  Add the following lines to
+that file:
+<pre class="example">     
+     ;; list libraries here.  Order matters as it sets the
+     ;; search order
+     (component-library "./sym")
+     
+</pre>
+   <p>This sets the local search path for the schematic capture program
+<code>gschem</code>.  Now the netlister, <code>gnetlist</code>, must also be
+configured.  This can be done by copying the file <samp><span class="file">gschemrc</span></samp> to
+<samp><span class="file">gnetlistrc</span></samp> by running ‘<samp><span class="samp">cp gschemrc gnetlistrc</span></samp>’. 
+Alternatively, you can create a soft link so only a single file needs to
+be updated if additional symbol paths are added.  The link is created by
+running ‘<samp><span class="samp">ln -s gschemrc gnetlistrc</span></samp>’.
+
+<h4 class="subsection">10.1.3 Set Up <code>gsch2pcb</code> Config Files</h4>
+
+<p>The program <code>gsch2pcb</code>, not to be confused with the older
+<code>gschem2pcb</code> script, is used to link the schematic to layout. 
+<code>gsch2pcb</code> is responsible for creating the netlist used to provide
+connectivity information to PCB as well creating an initial layout with
+all components instantiated in the design.  Forward annotation of
+schematic changes to the layout is also done using <code>gsch2pcb</code>. 
+<code>gsch2pcb</code> uses a project file to set up the schematic file names,
+PCB library locations, and output file names.  Create a project file
+called <samp><span class="file">project</span></samp> using the following as an example:
+<pre class="example">     
+     # List all the schematics to be netlisted
+     # and laid out on the pc board.
+     schematics      first.sch second.sch third.sch
+     
+     # For an output-name of foo, gsch2pcb generates files
+     # foo.net, foo.pcb, and foo.new.pcb.  If there is no
+     # output-name specified, the file names are derived from
+     # the first listed schematic, i.e. first.net, etc.
+     output-name  preamp
+     
+</pre>
+   <h4 class="subsection">10.1.4 Capture Schematics Using <code>gschem</code></h4>
+
+<p>This section is fairly brief and assumes familiarity with using the
+<code>gschem</code> schematic capture program.  As you are creating your
+schematics, be sure to observe the following rules:
+     <ul>
+<li>Make sure that each component in the schematic has a
+<code>footprint</code> attribute that corresponds to a footprint in the PCB
+library or a footprint you plan on creating. 
+<li>Make sure all reference designators are unique.  One way to ensure
+this is to run the <code>refdes_renum</code> script (part of gEDA) after the
+schematics are created. 
+</ul>
+
+<h4 class="subsection">10.1.5 Create Any Unique PCB Footprints</h4>
+
+<p>Create the new footprints you design needs using either the m4 style or
+newlib style of PCB libraries.  Refer to <a href="#Library-Creation">Library Creation</a> for details on this
+process.  For m4 style footprints, store them in the <samp><span class="file">pkg/m4</span></samp>
+subdirectory and for newlib footprints, store them in the
+<samp><span class="file">pkg/newlib</span></samp> subdirectory.
+
+<h4 class="subsection">10.1.6 Generate Initial PCB Design Using <code>gsch2pcb</code></h4>
+
+<p>The <code>gsch2pcb</code> program connects the schematic and layout.  It basic
+operation is to call <code>gnetlist</code> to generate the connectivity
+netlist that PCB used to verify connectivity and to instantiate all
+elements found in the schematic to a new layout. 
+The default, as of <code>gsch2pcb</code> version 0.9,  is to use any found  m4
+style parts first and then search for newlib style if no old style part
+was found.  By using the <code>--use-files</code> or <code>-f</code> flag to <code>gsch2pcb</code>
+priority is given to newlib style parts even if m4 style are found.  You
+may wish to verify this in the <code>gsch2pcb</code> documentation in case
+this changes in the future. 
+To start your layout,
+run ‘<samp><span class="samp">gsch2pcb project</span></samp>’ where <samp><span class="file">project</span></samp> is the project file
+created previously.  This will create a new netlist file,
+<samp><span class="file">preamp.net</span></samp>, and a new layout file, <samp><span class="file">preamp.pcb</span></samp>.
+
+<h4 class="subsection">10.1.7 Layout Circuit Board</h4>
+
+<p>Run PCB on the new layout by running ‘<samp><span class="samp">pcb preamp.pcb</span></samp>’. 
+Load the netlist file by selecting "load netlist file" from the "file"
+menu.  In the file selection dialog box, choose <samp><span class="file">preamp.net</span></samp>.  This
+loads connectivity information into PCB.
+
+   <p>Using the selection tool, grab and move apart the various footprints
+with the middle mouse button.  Once the parts are moved apart from each
+other, choose "optimize rats-nest" from the "Connects" menu.  This menu
+choice will display and optimize the rats nest.  Use the rats nest to
+help guide placement of the parts.  You may wish to re-run the "optimize
+rats-nest" command after moving parts around.
+
+   <p>After the placement is complete, use the line tool to add traces to the
+board.  As traces are added, the corresponding rats line will disappear.
+
+<h4 class="subsection">10.1.8 Forward Annotation of Schematic Changes</h4>
+
+<p>If schematic changes are made after the layout has started,
+<code>gsch2pcb</code> can be used to forward annotate these changes to the
+layout.  To forward annotate schematic changes, run ‘<samp><span class="samp">gsch2pcb
+project</span></samp>’.  This command will create the files <samp><span class="file">preamp.new.pcb</span></samp>,
+<samp><span class="file">preamp.net</span></samp>, and modify the file <samp><span class="file">preamp.pcb</span></samp>.  The
+modifications to <samp><span class="file">preamp.pcb</span></samp> include forward annotation of
+schematic component value changes, adds any new components, and removes
+any deleted components.
+
+<h4 class="subsection">10.1.9 Generate Photoplot Files (RS-274X)</h4>
+
+<p>After the layout is complete, choose "edit layer-groupings" from the
+"Settings" menu.  The LayerGroups form lets you specify which layers
+will appear in each output layer group.  For example, in the default
+form, layer group 1 has "front" and "front side" in it.  The
+output file <samp><span class="file">1.gbr</span></samp> if DOS file names are used, or
+<samp><span class="file">somename_front.gbr</span></samp> if long file names are used will contain the
+"front" and "front side" layers in it.  Usually the defaults are
+sufficient, but this form is still a useful reference.
+
+   <p>Choose "print layout..." from the "File" menu.  In the print dialog box,
+select "Gerber/RS-274X" for the device
+driver.  Select the "outline", "alignment", and "drillhelper" options. 
+To get DOS compatible file names, select the "DOS (8.3) names" option,
+otherwise enter "preamp" for the filename.  Press "OK".
+
+   <p>The following output files should have been created in the project directory. 
+The names in parentheses correspond to the DOS compatible output file names.
+     <dl>
+<dt><samp><span class="file">preamp_frontsilk.gbr (csilk.gbr)</span></samp><dd>Top side silk screen. 
+<br><dt><samp><span class="file">preamp_frontmask.gbr (cmask.gbr)</span></samp><dd>Top side soldermask relief. 
+<br><dt><samp><span class="file">preamp_front.gbr (1.gbr)</span></samp><dd>Top copper. 
+<br><dt><samp><span class="file">preamp_backmask.gbr (smask.gbr)</span></samp><dd>Bottom side soldermask relief. 
+<br><dt><samp><span class="file">preamp_back.gbr (2.gbr)</span></samp><dd>Bottom Copper. 
+<br><dt><samp><span class="file">preamp_fab.gbr (fab.gbr)</span></samp><dd>Fabrication drawing.  Also known as the drill drawing.  This drawing is
+used for reference by the board vendor but is not directly used in the
+fabrication process. 
+<br><dt><samp><span class="file">preamp_plated-drill.cnc (pdrill.cnc)</span></samp><dd>NC Drill format file for the plated through holes. 
+<br><dt><samp><span class="file">preamp_unplated-drill.cnc (udrill.cnc)</span></samp><dd>NC Drill format file for the unplated through holes. 
+<br><dt><samp><span class="file">preamp_bom.txt (bom.txt)</span></samp><dd>A bill of materials for the layout. 
+<br><dt><samp><span class="file">preamp_xy.txt (xy.txt)</span></samp><dd>Centroid (X-Y) data for driving automated assembly equipment. 
+</dl>
+
+<!-- to include an image: -->
+<!-- @image{geda1, 6in, 4in, geda schematic, png} -->
+<div class="node">
+<a name="xcircuit"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#gEDA">gEDA</a>,
+Up: <a rel="up" accesskey="u" href="#Schematic-Frontends">Schematic Frontends</a>
+
+</div>
+
+<h3 class="section">10.2 xcircuit</h3>
+
+<p><a name="index-xcircuit_002c-how-to-interface-with-821"></a><a name="index-xcircuit_002c-how-to-interface-with-822"></a>
+If anyone cares to contribute this section, it will get added.  Please
+submit changes to the bug tracking system for PCB which can be found from
+the PCB homepage at <a href="http://pcb.gpleda.org">http://pcb.gpleda.org</a>.
+
+<!--  Appendix A - -->
+<div class="node">
+<a name="Installation"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Custom-Menus">Custom Menus</a>,
+Previous: <a rel="previous" accesskey="p" href="#Schematic-Frontends">Schematic Frontends</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix A Installation and Troubleshooting</h2>
+
+<p>Compiling and installing the package should be straightforward. If any problems
+occur, please contact the author <a href="mailto:Thomas.Nau at rz.uni-ulm.de">Thomas.Nau at rz.uni-ulm.de</a>, or the
+current maintainer <a href="mailto:haceaton at aplcomm.jhuapl.edu">haceaton at aplcomm.jhuapl.edu</a> to find
+a solution and include it into the next release.
+
+<ul class="menu">
+<li><a accesskey="1" href="#compiling">compiling</a>:      Compiling and installing. 
+<li><a accesskey="2" href="#problems">problems</a>:       Troubleshooting. 
+</ul>
+
+<div class="node">
+<a name="compiling"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#problems">problems</a>,
+Up: <a rel="up" accesskey="u" href="#Installation">Installation</a>
+
+</div>
+
+<h3 class="section">A.1 Compiling and Installing</h3>
+
+<p><a name="index-install_002c-how-to-823"></a><a name="index-compile_002c-how-to-824"></a>
+This section covers the steps which are necessary to compile the package.
+
+<ul class="menu">
+<li><a accesskey="1" href="#quickstart">quickstart</a>:                  Quick start. 
+<li><a accesskey="2" href="#running-configure">running configure</a>:           Customizing pcb-rnd with Configure
+</ul>
+
+<div class="node">
+<a name="quickstart"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#running-configure">running configure</a>,
+Up: <a rel="up" accesskey="u" href="#compiling">compiling</a>
+
+</div>
+
+<h4 class="subsection">A.1.1 Quick Start</h4>
+
+<p><a name="index-GNU-build-system-825"></a>
+Starting with version 2.0, <code>pcb-rnd</code> has switched to a GNU
+autoconf/automake build system.  Installation of <code>pcb-rnd</code> consists of
+three steps:  configuration, building, and installing. 
+In a typical installation, these steps are as simple as
+<pre class="example">     ./configure
+     make
+     make install
+</pre>
+   <div class="node">
+<a name="running-configure"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#quickstart">quickstart</a>,
+Up: <a rel="up" accesskey="u" href="#compiling">compiling</a>
+
+</div>
+
+<h4 class="subsection">A.1.2 Running the configure Script</h4>
+
+<p><a name="index-GNU-configure-script-826"></a><a name="index-configure-827"></a>
+The <code>configure</code> script accepts all of the standard GNU configure
+options.  For a complete list of configuration options, run
+<code>./configure --help</code>.
+
+     
+<a name="index-INFOLIBDIR-828"></a>
+<dl><dt>‘<samp><span class="samp">INFOLIBDIR</span></samp>’<dd>must be set to the directory where your GNU info files are located.
+
+     <p><a name="index-PCBLIBDIR-829"></a><br><dt>‘<samp><span class="samp">PCBLIBDIR</span></samp>’<dd>is the path of a directory where the font files will be installed.
+
+     <p><a name="index-DEFAULTFONT-830"></a><br><dt>‘<samp><span class="samp">DEFAULTFONT</span></samp>’<dd>the name of the default font file.
+
+     <p><a name="index-DEFAULTLIBRARY-831"></a><br><dt>‘<samp><span class="samp">DEFAULTLIBRARY</span></samp>’<dd>the name of the default library.
+
+     <p><a name="index-GNUM4-832"></a><br><dt>‘<samp><span class="samp">GNUM4</span></samp>’<dd>the name of GNUs m4 version.
+
+     <p><a name="index-BTNMOD-833"></a><br><dt>‘<samp><span class="samp">BTNMOD</span></samp>’<dd>If your window manager has already bound <em>Mod1</em> together with some
+function keys you may want to change this setting. This is true for HP-VUE.
+
+   </dl>
+
+   <p>If you find things which must be changed to compile on your system,
+please add the appropriate autoconf tests (if you are familiar with
+that) and mail a copy to the maintainer, harry eaton,  at
+<a href="mailto:haceaton at aplcomm.jhuapl.edu">haceaton at aplcomm.jhuapl.edu</a>.
+
+   <p>If you do not have the appropriate permissions you should run
+<samp><span class="file">./pcbtest.sh</span></samp> in the <samp><span class="file">src</span></samp> directory to run <code>pcb-rnd</code> from
+the installation directory.
+
+<div class="node">
+<a name="problems"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#compiling">compiling</a>,
+Up: <a rel="up" accesskey="u" href="#Installation">Installation</a>
+
+</div>
+
+<h3 class="section">A.2 Troubleshooting</h3>
+
+<p><a name="index-problems-834"></a><a name="index-troubleshooting-835"></a>
+There are some known problems. Most of them are related to
+missing parts of a standard <code>X11</code> distribution. Some others are caused by
+third party applications such as <code>X</code> servers. To make this list more
+complete please mail your problems and, if available, solutions to the author. 
+The mail address may be found at the beginning of this chapter. 
+In any case, read <a href="#X11">X11</a>.
+
+   <p>By the way, you <code>MUST HAVE AN ANSI COMPILER</code> to make <code>pcb-rnd</code> work.
+
+   <p>Another source of problems are older versions of <code>flex</code> and <code>bison</code>. 
+<code>pcb-rnd</code> definitely works with <code>flex-2.4.7</code> and <code>bison-1.22</code> or
+later. The problems will result in a <em>syntax error</em> while parsing files. 
+This should only be a problem if you have modified the <code>flex</code> or
+<code>bison</code> input files.
+
+   <p>The following list gives you just an idea because I'm not able to test
+all <code>pcb-rnd</code> releases on all platforms.
+
+<ul class="menu">
+<li><a accesskey="1" href="#HP">HP</a>:               Hewlett-Packard series 700 and 800 running HP-UX 10.*
+<li><a accesskey="2" href="#Sun">Sun</a>:              Sun, Solaris 2.5
+<li><a accesskey="3" href="#SGI">SGI</a>:              SGI, IRIX 5.3 and 6.*
+<li><a accesskey="4" href="#DEC-Alpha">DEC Alpha</a>:        DEC Alpha, DEC UNIX 3.2c and 4.0
+<li><a accesskey="5" href="#SCO">SCO</a>:              SCO Unix ODT 3.0, PC hardware
+<li><a accesskey="6" href="#Linux">Linux</a>:            Linux 0.99pl14 and later
+<li><a accesskey="7" href="#BSD">BSD</a>:              FreeBSD, NetBSD ... 
+<li><a accesskey="8" href="#X11">X11</a>:              Refers to <code>X11R4</code>, <code>X11R5</code>, and <code>OpenWindows</code>
+<li><a accesskey="9" href="#TeX-and-Manuals">TeX and Manuals</a>:  Problems creating the <samp><span class="file">pcb.dvi</span></samp>
+</ul>
+
+<div class="node">
+<a name="HP"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Sun">Sun</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.1 HP Series 700 and 800</h4>
+
+<p><a name="index-architecture-836"></a><a name="index-HP-837"></a><a name="index-Hewlett-Packard-838"></a>
+You have to install several <code>X11</code> include files
+or, better, install a complete <code>X11R5</code> release. Hewlett-Packard doesn't
+support the Athena Widgets. So the header files and libraries are missing
+from the application media, but they are available as a patch. 
+They also do not ship the <code>ANSI</code> compiler with the normal operating
+system release so you have to buy one or use <code>GCC</code>. 
+Some of the tools are available as patches.
+
+   <p>In addition, <code>pcb-rnd</code> has been successfully tested on these platforms with
+<code>HPUX 9.*, 10.*</code> running self-compiled <code>X11R5</code>.
+
+<div class="node">
+<a name="Sun"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SGI">SGI</a>,
+Previous: <a rel="previous" accesskey="p" href="#HP">HP</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.2 Sun SPARC architecture</h4>
+
+<p><a name="index-architecture-839"></a><a name="index-Sun-840"></a><a name="index-Solaris-841"></a><a name="index-OpenWindows-842"></a>
+There are no known problems with Sun machines if they use <code>X11R5</code> instead
+of <code>OpenWindows</code>. <code>pcb-rnd</code> compiled successfully with all kinds of
+SPARCstations <code>Solaris-2.[345]</code>.
+
+   <p>For problems with <code>OpenWindows</code> refer to <a href="#X11">X11</a>.
+
+<div class="node">
+<a name="SGI"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DEC-Alpha">DEC Alpha</a>,
+Previous: <a rel="previous" accesskey="p" href="#Sun">Sun</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.3 Silicon Graphics</h4>
+
+<p><a name="index-architecture-843"></a><a name="index-Silicon-Graphics-844"></a><a name="index-SGI-845"></a>
+<code>pcb-rnd</code> has been tested on some boxes running either <code>IRIX-4.0.5</code> or
+<code>IRIX-5.3</code>. The former one uses a <code>X11R4</code> server. 
+There are no problems. 
+For known problems
+with <code>X11R4</code>, see <a href="#X11">X11</a>.
+
+<div class="node">
+<a name="DEC-Alpha"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SCO">SCO</a>,
+Previous: <a rel="previous" accesskey="p" href="#SGI">SGI</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.4 DEC Alpha</h4>
+
+<p><a name="index-architecture-846"></a><a name="index-DEC-847"></a><a name="index-Alpha-848"></a>
+<code>pcb-rnd</code> compiled and runs without problems on <code>DEC UNIX V3.2c</code>.
+
+<div class="node">
+<a name="SCO"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Linux">Linux</a>,
+Previous: <a rel="previous" accesskey="p" href="#DEC-Alpha">DEC Alpha</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.5 SCO Unix</h4>
+
+<p><a name="index-architecture-849"></a><a name="index-SCO-850"></a><a name="index-PC-UNIX-851"></a>
+John DuBois <spcecdt at deeptht.armory.com> wrote:
+<pre class="example">     <code>SCO-ODT-3.0</code> requires the latest version of tls003, the Athena
+     widget library (available from sosco.sco.com). The main problems
+<u style="color:red;"><u>     I have encountered are it core dumps fairly often, especially</u></u>
+     while loading/dropping elements...
+</pre>
+<u style="color:red;"><u>   <p>I'll see what I am able to do as soon as I have access to an <code>SCO</code> system.</u></u>
+
+<div class="node">
+<a name="Linux"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#BSD">BSD</a>,
+Previous: <a rel="previous" accesskey="p" href="#SCO">SCO</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.6 Linux</h4>
+
+<p><a name="index-architecture-852"></a><a name="index-Linux-853"></a><a name="index-PC-UNIX-854"></a>
+Since the <code>X11</code> version of <code>pcb-rnd</code> has been developed on a Linux
+system here are no known problems.
+
+<div class="node">
+<a name="BSD"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#X11">X11</a>,
+Previous: <a rel="previous" accesskey="p" href="#Linux">Linux</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.7 FreeBSD and NetBSD</h4>
+
+<p><a name="index-FreeBSD-855"></a><a name="index-NetBSD-856"></a><a name="index-PC-UNIX-857"></a>
+<code>pcb-rnd</code> has been tested on NetBSD and works without any problems. 
+You may also be able to find a NetBSD package at
+<a href="ftp://ftp.netbsd.org/pub/NetBSD/packages/cad/pcb/README.html">ftp://ftp.netbsd.org/pub/NetBSD/packages/cad/pcb/README.html</a> or a
+FreeBSD port at
+<a href="http://www.freebsd.org/cgi/url.cgi?ports/cad/pcb/pkg-descr">http://www.freebsd.org/cgi/url.cgi?ports/cad/pcb/pkg-descr</a>.
+
+<div class="node">
+<a name="X11"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#TeX-and-Manuals">TeX and Manuals</a>,
+Previous: <a rel="previous" accesskey="p" href="#BSD">BSD</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.8 Problems related to X11</h4>
+
+<p><a name="index-X11_002c-problems-858"></a>
+There are a some problems related to <code>X11R4</code> or systems derived from
+<code>X11</code> such as <code>OpenWindows</code>. See <a href="#Sun">Sun</a>. You at least have to change
+all occurances of <em>baseTranslations</em> in the resource files to
+<em>translations</em> if you are using a <code>X11R4</code> server. Look at the
+<code>X11R5</code> <em>Intrinsics</em> manual for details.
+
+   <p>The panner widget (print dialog box) appears only in release <code>X11R5</code> and
+later. It really simplifies adjusting the offsets. 
+With earlier releases the printout will always appear in the center of the
+page.
+
+   <p>You may have some problems in a mixed <code>X11-OpenWindows</code>
+environment.
+
+   <p><code>pcb-rnd</code> has been tested successfully with <code>X11R6</code> under Linux 1.1.59
+and later.
+
+<div class="node">
+<a name="TeX-and-Manuals"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#X11">X11</a>,
+Up: <a rel="up" accesskey="u" href="#problems">problems</a>
+
+</div>
+
+<h4 class="subsection">A.2.9 Problems related to TeX</h4>
+
+<p><a name="index-TeX_002c-problems-859"></a>
+If your <code>TeX</code> installation complains about a missing <samp><span class="file">texinfo.tex</span></samp>
+file copy the one included in this release (directory <samp><span class="file">doc</span></samp>
+to your <code>TeX</code> macro directory. 
+Note, there are probably newer versions of this file available from some
+FTP sites. 
+<code>TeX-3.0</code> failed, <code>TeX-3.14</code> worked just fine. Check our FTP server
+<em>ftp.uni-ulm.de</em> for ready-to-print versions of the manuals.
+
+<!--  Appendix B - -->
+<div class="node">
+<a name="Custom-Menus"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Regular-Expressions">Regular Expressions</a>,
+Previous: <a rel="previous" accesskey="p" href="#Installation">Installation</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix B Customizing the Menus</h2>
+
+<p>The menu system is driven off a data file that contains
+<dfn>resources</dfn>.  A resource is a hierarchical description of a data
+tree which, in this case, is mapped to the hierarchical menus used by
+pcb-rnd.
+
+<ul class="menu">
+<li><a accesskey="1" href="#Resource-Syntax">Resource Syntax</a>:           What a resource file looks like. 
+<li><a accesskey="2" href="#Menu-Definitions">Menu Definitions</a>:          Using a resource to define a menu. 
+<li><a accesskey="3" href="#Menu-Files-and-Defaults">Menu Files and Defaults</a>:   Where pcb-rnd looks for its menu resource. 
+</ul>
+
+<div class="node">
+<a name="Resource-Syntax"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Menu-Definitions">Menu Definitions</a>,
+Up: <a rel="up" accesskey="u" href="#Custom-Menus">Custom Menus</a>
+
+</div>
+
+<h3 class="section">B.1 Resource Syntax</h3>
+
+<p>A resource file is a simple text file.  It contains curly braces to
+group things, spaces between things, and double quotes when strings
+need to include spaces.  There are four fundamental ways of adding
+data to a resource.
+
+   <p>First, a string (either a single word or a quoted string with spaces,
+we call both “strings” in this appendix) can be added all by itself,
+to add a string resource to the current resource.  This is used, for
+example, to define the string printed on a menu button.  In this
+example, four strings are added to the <var>File</var> resource:
+
+<pre class="example">     File = {
+       Sample
+       "longer sample"
+       some text
+     }
+</pre>
+   <p>Second, a named string may be added by giving two strings separated by
+an equals sign.  This is used to specify X resources and a few other
+optional parameters of menus, for example.  Note that a string all by
+itself is thus an “unnamed” string.
+
+<pre class="example">     {"Layer groups" foreground=red sensitive=false}
+</pre>
+   <p>Third, an unnamed subresource may be added.  This is used to create
+submenus and menu buttons.  To add a subresource, simply group other
+things in curly braces.  This example describes a resource containing
+one string and three subresources:
+
+<pre class="example">     {File
+       {New do_new()}
+       {Save do_save()}
+       {Quit do_quit()}
+     }
+</pre>
+   <p>Lastly, a named subresource may be added by prefixing an unnamed
+subresource with a string and an equals sign, just as when naming
+strings.  This syntax is used to name the resources used for the main
+menu and popup menus:
+
+<pre class="example">     MainMenu = {
+       ...
+       }
+</pre>
+   <p>Additionally, the menu parser allows for “hooks” whereby portions of
+the menu system can be programmatically created at runtime by the
+application.  These hooks are invoked by a single word proceeded by an
+at sign, such as this example where most of the Sizes menu is created
+automatically:
+
+<pre class="example">     {Sizes
+         @sizes
+         {"Adjust active sizes ..." AdjustStyle(0)}
+         }
+</pre>
+   <p>In addition to all that, any unquoted pound sign (<code>#</code>) begins a
+comment.  Commented text continues until the end of the containing
+line.  Comments may begin at the beginning of a line, or after other
+text on the line:
+
+<pre class="example">     # This is a comment
+     MainMenu = { # This is also a comment
+</pre>
+   <div class="node">
+<a name="Menu-Definitions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Menu-Files-and-Defaults">Menu Files and Defaults</a>,
+Previous: <a rel="previous" accesskey="p" href="#Resource-Syntax">Resource Syntax</a>,
+Up: <a rel="up" accesskey="u" href="#Custom-Menus">Custom Menus</a>
+
+</div>
+
+<h3 class="section">B.2 Menu Definitions</h3>
+
+<p>To best understand this section, you should find the
+<samp><span class="file">pcb-menu.res</span></samp> file that your pcb-rnd uses and refer to it for
+examples (see <a href="#Menu-Files-and-Defaults">Menu Files and Defaults</a>).  Note that the lesstif
+GUI uses <samp><span class="file">pcb-menu.res</span></samp> and the GTK+ GUI uses <samp><span class="file">gpcb-menu.res</span></samp>. 
+The file format is identical however and if so desired, one can make
+one file be a soft link to the other.
+
+   <p>A resource defines a menu when it meets certain semantic requirements. 
+The menu hierarchy is reflected as a hierarchy of unnamed
+subresources, with the first string of each subresource defining the
+label used for the menu button.  A subresource that itself contains
+subresources becomes a submenu, a subresource that does not becomes a
+button.
+
+   <p>A submenu should only contain subresources for the buttons or submenus
+within that submenu.  Two exceptions are allowed: an initial string
+sets the label, and the string “-” (a single dash) will create a
+separator.
+
+   <p>A button should not contain subresources, but will contain many
+strings, named and unnamed.  The first member shall be an unnamed
+string which is the label for the button.  Any other unnamed strings
+within the button's resource will be used as actions (much like the
+.Xdefaults action strings), which are functions that will be called
+when the button is pressed (or popped up, or created, depending on the
+action).  As a convenience, if a left parenthesis is seen, the current
+“word” will continue at least until the matching right parenthesis. 
+This allows you to pass strings with spaces as arguments to actions
+without needing to quote the action.
+
+   <p>Named resources in button resources will be used as X resources.  Such
+resources can be used to set the font, color, and spacing of buttons. 
+As a convenience, “fg” can be used as an abbreviation for “foreground”.
+
+   <p>Within the menu's resource file, pcb-rnd will look for a few key named
+subresources.  At the moment, the only one it looks for is one called
+<code>MainMenu</code>.  This will be used for the main menu bar.  In the
+future, other named subresources will be used for popup resources.
+
+   <p>Given all this, a small sample <samp><span class="file">pcb-menu.res</span></samp> would be:
+
+<pre class="example">     MainMenu = {
+       {File
+         {"Load layout" Load(Layout)}
+         -
+         {"Quit Program" Quit() fg=red font=10x20}
+       }
+     }
+</pre>
+   <p>Within the pcb-rnd sources are specially crafted comments that mark all
+the actions, flags, menu hooks, and whatnot that pcb-rnd offers.  Read the
+file <samp><span class="file">src/gather-actions</span></samp> in the pcb-rnd source tree for
+documentation for these comments.
+
+<div class="node">
+<a name="Menu-Files-and-Defaults"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Menu-Definitions">Menu Definitions</a>,
+Up: <a rel="up" accesskey="u" href="#Custom-Menus">Custom Menus</a>
+
+</div>
+
+<h3 class="section">B.3 Menu Files and Defaults</h3>
+
+<p>pcb-rnd will look for a file which defines its menus, trying the following
+names (the example is for the lesstif GUI, the GTK+ GUI has “gpcb-menu.res”
+in place of “pcb-menu.res”):
+
+<pre class="example">     ./pcb-menu.res
+     $HOME/.pcb-menu.res
+     $PCBLIBDIR/pcb-menu.res
+     <internal>
+</pre>
+   <p>Note that <var>pcblibdir</var> defaults to <samp><span class="file">/usr/local/share/pcb</span></samp>
+(hence, <samp><span class="file">/usr/local/share/pcb/pcb-menu.res</span></samp>).  The
+<samp><span class="file"><internal></span></samp> entry refers to a menu definition within the pcb-rnd
+application itself.  The master file for all this is the file
+<samp><span class="file">src/pcb-menu.res</span></samp> in the pcb-rnd source tree.  This master source is
+used to create the internal menu definition as well as being installed
+in <samp><span class="file">$pcblibdir</span></samp>.
+
+<!--  Appendix C - -->
+<div class="node">
+<a name="Regular-Expressions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Standard-Drill-Sizes">Standard Drill Sizes</a>,
+Previous: <a rel="previous" accesskey="p" href="#Custom-Menus">Custom Menus</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix C Element Search/Regular Expressions</h2>
+
+<p><a name="index-Element-Search-860"></a><a name="index-Regular-Expressions-861"></a><a name="index-Element-Search-862"></a><a name="index-Regular-Expressions-863"></a>
+
+<h3 class="section">C.1 Element Search/Regular Expressions</h3>
+
+<p><code>pcb-rnd</code>'s search is based on POSIX 1003.2 Regular Expressions.  Full POSIX
+Regular Expressions are supported by <code>pcb-rnd</code> if the regex library was
+available when <code>pcb-rnd</code> was built.  One difference from the regular
+expressions found in tools like awk or grep is that PCB implicitly
+adds a “^” to the begining of a regular expression and “$” to the
+end of the regular expression.  For example, if you enter “C1”, the
+actual regular expression used internally is “^C1$”.  Another difference
+is that search patterns in pcb are not case sensitive. That is, “CON” is
+treated the same as “con”.
+
+   <p>It is easier to show by example how to search than explain
+POSIX 1003.2.  With regular expressions most characters are just
+themselves, but some are special:
+
+     <dl>
+<dt>‘<samp><span class="samp">*</span></samp>’<dd>Matches 0 or more instances of preceding character.
+
+     <br><dt>‘<samp><span class="samp">+</span></samp>’<dd>Matches 1 or more instances of preceding character.
+
+     <br><dt>‘<samp><span class="samp">?</span></samp>’<dd>Matches 0 or 1 instances of preceding character.
+
+     <br><dt>‘<samp><span class="samp">.</span></samp>’<dd>Matches any single character other than the newline character.
+
+     <br><dt>‘<samp><span class="samp">|</span></samp>’<dd>The vertical bar is the alternation operator. It combines two
+regular expressions. The result matches if either of them matches.
+
+     <br><dt>‘<samp><span class="samp">\</span></samp>’<dd>A backslash indicates the next character should not be interpreted literally
+if it normally is, and should be interpreted literally if it normally isn't.
+
+     <br><dt>‘<samp><span class="samp">{n}</span></samp>’<dd>An integer n enclosed in curly brackets matches the preceding item if
+it occurs exactly n times.
+
+     <br><dt>‘<samp><span class="samp">[ ]</span></samp>’<dd>A pair of square brackets matches every character they contain.  Characters
+may be given explicitly, or as ranges.
+
+     <br><dt>‘<samp><span class="samp">-</span></samp>’<dd>A hyphen in the context of square brackets denotes the range between the
+preceding and the following character.  E.g., the range of digits is
+“0-9” .  The range of letters from C to K is “C-K” .
+
+     <br><dt>‘<samp><span class="samp">\^ inside square brackets</span></samp>’<dd>Inside square brackets the caret is an anti operator. Its presence makes
+the square prackets match anything except the contents of the brackets.
+
+     <br><dt>‘<samp><span class="samp">( )</span></samp>’<dd>Round parenthesis group parts of a regular expression. This is very much
+like they do in math formulars.
+
+   </dl>
+
+   <p>If you need a special character literally, you can escape it with a
+backslash.
+
+   <p>The following examples illustrate how regular expressions can be used to
+specify element names (reference designators) to search for.
+     <dl>
+<dt>‘<samp><span class="samp">C5</span></samp>’<dd>Select the element whose name is exactly “C5”.
+
+     <br><dt>‘<samp><span class="samp">C5 | R3</span></samp>’<dd>Select C5 and R3.
+
+     <br><dt>‘<samp><span class="samp">C.*</span></samp>’<dd>Select all elements whose name start with the letter “C”, such as C5, or
+C42, or CF1.
+
+     <br><dt>‘<samp><span class="samp">C.*1</span></samp>’<dd>Select all elements that start with “C” and end with “1”, such as C1,
+or C51 or C5/9B71.
+
+     <br><dt>‘<samp><span class="samp">R10?</span></samp>’<dd>Search for R1 or R10, but will not select R100 or R105. The question mark
+is a quantifier for the character “0”.
+
+     <br><dt>‘<samp><span class="samp">R128+</span></samp>’<dd>Selects R128, R1288, R12888, etc.
+
+     <br><dt>‘<samp><span class="samp">TB.</span></samp>’<dd>Select all terminal blocks having exactly one character designator after
+“TB” such as TB1, TBA, or TBx but not TB.
+
+     <br><dt>‘<samp><span class="samp">TB..</span></samp>’<dd>Select all terminal blocks having a two character designator such as TB21 or
+TB1a.
+
+     <br><dt>‘<samp><span class="samp">TB.*</span></samp>’<dd>Select all terminal blocks with any designator.
+
+     <br><dt>‘<samp><span class="samp">.*31</span></samp>’<dd>Select all items, whose name ends with “31” such as Q31, or R31, or R531.
+
+     <br><dt>‘<samp><span class="samp">Q[12]</span></samp>’<dd>Select Q1 and Q2.
+
+     <br><dt>‘<samp><span class="samp">[A-D].*</span></samp>’<dd>Select all items, whose name starts with “A”, “B”, “C”, or “D”.
+
+     <br><dt>‘<samp><span class="samp">.*N{2}.*</span></samp>’<dd>Select all items, whose name contains two “N” in a row such as
+CONN23, or connA, but not CON5
+
+     <br><dt>‘<samp><span class="samp">[^D].*</span></samp>’<dd>Select all items that do not start with the letter “D”, such as C2, or
+R34, but not D34
+
+   </dl>
+
+<!--  Appendix - drill sizes - -->
+<div class="node">
+<a name="Standard-Drill-Sizes"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Centroid-File-Format">Centroid File Format</a>,
+Previous: <a rel="previous" accesskey="p" href="#Regular-Expressions">Regular Expressions</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix D Standard Drill Size Tables</h2>
+
+<p><a name="index-drill-sizes_002c-list-of-standard-864"></a><a name="index-standard-drill-sizes-865"></a>
+
+<h3 class="section">D.1 American Standard Wire Size Drills</h3>
+
+<!-- Generated file.  Do not edit directly -->
+<!-- $Id$ -->
+<p><table summary=""><tr align="left"><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter
+<br></td></tr><tr align="left"><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches)
+
+<p><br></td></tr><tr align="left"><td valign="top" width="17%">97 </td><td valign="top" width="17%">.0059 </td><td valign="top" width="17%">96 </td><td valign="top" width="17%">.0063 </td><td valign="top" width="17%">95 </td><td valign="top" width="17%">.0067
+<br></td></tr><tr align="left"><td valign="top" width="17%">94 </td><td valign="top" width="17%">.0071 </td><td valign="top" width="17%">93 </td><td valign="top" width="17%">.0075 </td><td valign="top" width="17%">92 </td><td valign="top" width="17%">.0079
+<br></td></tr><tr align="left"><td valign="top" width="17%">91 </td><td valign="top" width="17%">.0083 </td><td valign="top" width="17%">90 </td><td valign="top" width="17%">.0087 </td><td valign="top" width="17%">89 </td><td valign="top" width="17%">.0091
+<br></td></tr><tr align="left"><td valign="top" width="17%">88 </td><td valign="top" width="17%">.0095 </td><td valign="top" width="17%">87 </td><td valign="top" width="17%">.0100 </td><td valign="top" width="17%">86 </td><td valign="top" width="17%">.0105
+<br></td></tr><tr align="left"><td valign="top" width="17%">85 </td><td valign="top" width="17%">.0110 </td><td valign="top" width="17%">84 </td><td valign="top" width="17%">.0115 </td><td valign="top" width="17%">83 </td><td valign="top" width="17%">.0120
+<br></td></tr><tr align="left"><td valign="top" width="17%">82 </td><td valign="top" width="17%">.0125 </td><td valign="top" width="17%">81 </td><td valign="top" width="17%">.0130 </td><td valign="top" width="17%">80 </td><td valign="top" width="17%">.0135
+<br></td></tr><tr align="left"><td valign="top" width="17%">79 </td><td valign="top" width="17%">.0145 </td><td valign="top" width="17%">78 </td><td valign="top" width="17%">.0160 </td><td valign="top" width="17%">77 </td><td valign="top" width="17%">.0180
+<br></td></tr><tr align="left"><td valign="top" width="17%">76 </td><td valign="top" width="17%">.0200 </td><td valign="top" width="17%">75 </td><td valign="top" width="17%">.0210 </td><td valign="top" width="17%">74 </td><td valign="top" width="17%">.0225
+<br></td></tr><tr align="left"><td valign="top" width="17%">73 </td><td valign="top" width="17%">.0240 </td><td valign="top" width="17%">72 </td><td valign="top" width="17%">.0250 </td><td valign="top" width="17%">71 </td><td valign="top" width="17%">.0260
+<br></td></tr><tr align="left"><td valign="top" width="17%">70 </td><td valign="top" width="17%">.0280 </td><td valign="top" width="17%">69 </td><td valign="top" width="17%">.0292 </td><td valign="top" width="17%">68 </td><td valign="top" width="17%">.0310
+<br></td></tr><tr align="left"><td valign="top" width="17%">67 </td><td valign="top" width="17%">.0320 </td><td valign="top" width="17%">66 </td><td valign="top" width="17%">.0330 </td><td valign="top" width="17%">65 </td><td valign="top" width="17%">.0350
+<br></td></tr><tr align="left"><td valign="top" width="17%">64 </td><td valign="top" width="17%">.0360 </td><td valign="top" width="17%">63 </td><td valign="top" width="17%">.0370 </td><td valign="top" width="17%">62 </td><td valign="top" width="17%">.0380
+<br></td></tr><tr align="left"><td valign="top" width="17%">61 </td><td valign="top" width="17%">.0390 </td><td valign="top" width="17%">60 </td><td valign="top" width="17%">.0400 </td><td valign="top" width="17%">59 </td><td valign="top" width="17%">.0410
+<br></td></tr><tr align="left"><td valign="top" width="17%">58 </td><td valign="top" width="17%">.0420 </td><td valign="top" width="17%">57 </td><td valign="top" width="17%">.0430 </td><td valign="top" width="17%">56 </td><td valign="top" width="17%">.0465
+<br></td></tr><tr align="left"><td valign="top" width="17%">55 </td><td valign="top" width="17%">.0520 </td><td valign="top" width="17%">54 </td><td valign="top" width="17%">.0550 </td><td valign="top" width="17%">53 </td><td valign="top" width="17%">.0595
+<br></td></tr><tr align="left"><td valign="top" width="17%">52 </td><td valign="top" width="17%">.0635 </td><td valign="top" width="17%">51 </td><td valign="top" width="17%">.0670 </td><td valign="top" width="17%">50 </td><td valign="top" width="17%">.0700
+<br></td></tr><tr align="left"><td valign="top" width="17%">49 </td><td valign="top" width="17%">.0730 </td><td valign="top" width="17%">48 </td><td valign="top" width="17%">.0760 </td><td valign="top" width="17%">47 </td><td valign="top" width="17%">.0785
+<br></td></tr><tr align="left"><td valign="top" width="17%">46 </td><td valign="top" width="17%">.0810 </td><td valign="top" width="17%">45 </td><td valign="top" width="17%">.0820 </td><td valign="top" width="17%">44 </td><td valign="top" width="17%">.0860
+<br></td></tr><tr align="left"><td valign="top" width="17%">43 </td><td valign="top" width="17%">.0890 </td><td valign="top" width="17%">42 </td><td valign="top" width="17%">.0935 </td><td valign="top" width="17%">41 </td><td valign="top" width="17%">.0960
+<br></td></tr><tr align="left"><td valign="top" width="17%">40 </td><td valign="top" width="17%">.0980 </td><td valign="top" width="17%">39 </td><td valign="top" width="17%">.0995 </td><td valign="top" width="17%">38 </td><td valign="top" width="17%">.1015
+<br></td></tr><tr align="left"><td valign="top" width="17%">37 </td><td valign="top" width="17%">.1040 </td><td valign="top" width="17%">36 </td><td valign="top" width="17%">.1065 </td><td valign="top" width="17%">35 </td><td valign="top" width="17%">.1100
+<br></td></tr><tr align="left"><td valign="top" width="17%">34 </td><td valign="top" width="17%">.1110 </td><td valign="top" width="17%">33 </td><td valign="top" width="17%">.1130 </td><td valign="top" width="17%">32 </td><td valign="top" width="17%">.1160
+<br></td></tr><tr align="left"><td valign="top" width="17%">31 </td><td valign="top" width="17%">.1200 </td><td valign="top" width="17%">30 </td><td valign="top" width="17%">.1285 </td><td valign="top" width="17%">29 </td><td valign="top" width="17%">.1360
+<br></td></tr><tr align="left"><td valign="top" width="17%">28 </td><td valign="top" width="17%">.1405 </td><td valign="top" width="17%">27 </td><td valign="top" width="17%">.1440 </td><td valign="top" width="17%">26 </td><td valign="top" width="17%">.1470
+<br></td></tr><tr align="left"><td valign="top" width="17%">25 </td><td valign="top" width="17%">.1495 </td><td valign="top" width="17%">24 </td><td valign="top" width="17%">.1520 </td><td valign="top" width="17%">23 </td><td valign="top" width="17%">.1540
+<br></td></tr><tr align="left"><td valign="top" width="17%">22 </td><td valign="top" width="17%">.1570 </td><td valign="top" width="17%">21 </td><td valign="top" width="17%">.1590 </td><td valign="top" width="17%">20 </td><td valign="top" width="17%">.1610
+<br></td></tr><tr align="left"><td valign="top" width="17%">19 </td><td valign="top" width="17%">.1660 </td><td valign="top" width="17%">18 </td><td valign="top" width="17%">.1695 </td><td valign="top" width="17%">17 </td><td valign="top" width="17%">.1730
+<br></td></tr><tr align="left"><td valign="top" width="17%">16 </td><td valign="top" width="17%">.1770 </td><td valign="top" width="17%">15 </td><td valign="top" width="17%">.1800 </td><td valign="top" width="17%">14 </td><td valign="top" width="17%">.1820
+<br></td></tr><tr align="left"><td valign="top" width="17%">13 </td><td valign="top" width="17%">.1850 </td><td valign="top" width="17%">12 </td><td valign="top" width="17%">.1890 </td><td valign="top" width="17%">11 </td><td valign="top" width="17%">.1910
+<br></td></tr><tr align="left"><td valign="top" width="17%">10 </td><td valign="top" width="17%">.1935 </td><td valign="top" width="17%">9 </td><td valign="top" width="17%">.1960 </td><td valign="top" width="17%">8 </td><td valign="top" width="17%">.1990
+<br></td></tr><tr align="left"><td valign="top" width="17%">7 </td><td valign="top" width="17%">.2010 </td><td valign="top" width="17%">6 </td><td valign="top" width="17%">.2040 </td><td valign="top" width="17%">5 </td><td valign="top" width="17%">.2055
+<br></td></tr><tr align="left"><td valign="top" width="17%">4 </td><td valign="top" width="17%">.2090 </td><td valign="top" width="17%">3 </td><td valign="top" width="17%">.2130 </td><td valign="top" width="17%">2 </td><td valign="top" width="17%">.2210
+<br></td></tr><tr align="left"><td valign="top" width="17%">1 </td><td valign="top" width="17%">.2280 </td><td valign="top" width="17%"></td><td valign="top" width="17%"></td><td valign="top" width="17%"></td><td valign="top" width="17%">
+<br></td></tr></table>
+
+<h3 class="section">D.2 American Standard Letter Size Drills</h3>
+
+<!-- Generated file.  Do not edit directly -->
+<!-- $Id$ -->
+<p><table summary=""><tr align="left"><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter
+<br></td></tr><tr align="left"><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches)
+
+<p><br></td></tr><tr align="left"><td valign="top" width="17%">A </td><td valign="top" width="17%">.2340 </td><td valign="top" width="17%">B </td><td valign="top" width="17%">.2380 </td><td valign="top" width="17%">C </td><td valign="top" width="17%">.2420
+<br></td></tr><tr align="left"><td valign="top" width="17%">D </td><td valign="top" width="17%">.2460 </td><td valign="top" width="17%">E </td><td valign="top" width="17%">.2500 </td><td valign="top" width="17%">F </td><td valign="top" width="17%">.2570
+<br></td></tr><tr align="left"><td valign="top" width="17%">G </td><td valign="top" width="17%">.2610 </td><td valign="top" width="17%">H </td><td valign="top" width="17%">.2660 </td><td valign="top" width="17%">I </td><td valign="top" width="17%">.2720
+<br></td></tr><tr align="left"><td valign="top" width="17%">J </td><td valign="top" width="17%">.2770 </td><td valign="top" width="17%">K </td><td valign="top" width="17%">.2810 </td><td valign="top" width="17%">L </td><td valign="top" width="17%">.2900
+<br></td></tr><tr align="left"><td valign="top" width="17%">M </td><td valign="top" width="17%">.2950 </td><td valign="top" width="17%">N </td><td valign="top" width="17%">.3020 </td><td valign="top" width="17%">O </td><td valign="top" width="17%">.3160
+<br></td></tr><tr align="left"><td valign="top" width="17%">P </td><td valign="top" width="17%">.3230 </td><td valign="top" width="17%">Q </td><td valign="top" width="17%">.3320 </td><td valign="top" width="17%">R </td><td valign="top" width="17%">.3390
+<br></td></tr><tr align="left"><td valign="top" width="17%">S </td><td valign="top" width="17%">.3480 </td><td valign="top" width="17%">T </td><td valign="top" width="17%">.3580 </td><td valign="top" width="17%">U </td><td valign="top" width="17%">.3680
+<br></td></tr><tr align="left"><td valign="top" width="17%">V </td><td valign="top" width="17%">.3770 </td><td valign="top" width="17%">W </td><td valign="top" width="17%">.3860 </td><td valign="top" width="17%">X </td><td valign="top" width="17%">.3970
+<br></td></tr><tr align="left"><td valign="top" width="17%">Y </td><td valign="top" width="17%">.4040 </td><td valign="top" width="17%">Z </td><td valign="top" width="17%">.4130 </td><td valign="top" width="17%"></td><td valign="top" width="17%">
+<br></td></tr></table>
+
+<h3 class="section">D.3 Fractional Inch Size Drills</h3>
+
+<!-- Generated file.  Do not edit directly -->
+<!-- $Id$ -->
+<p><table summary=""><tr align="left"><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter
+<br></td></tr><tr align="left"><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches)
+
+<p><br></td></tr><tr align="left"><td valign="top" width="17%">1/64 </td><td valign="top" width="17%">.0156 </td><td valign="top" width="17%">1/32 </td><td valign="top" width="17%">.0313 </td><td valign="top" width="17%">3/64 </td><td valign="top" width="17%">.0469
+<br></td></tr><tr align="left"><td valign="top" width="17%">1/16 </td><td valign="top" width="17%">.0625 </td><td valign="top" width="17%">5/64 </td><td valign="top" width="17%">.0781 </td><td valign="top" width="17%">3/32 </td><td valign="top" width="17%">.0938
+<br></td></tr><tr align="left"><td valign="top" width="17%">7/64 </td><td valign="top" width="17%">.1094 </td><td valign="top" width="17%">1/8 </td><td valign="top" width="17%">.1250 </td><td valign="top" width="17%">9/64 </td><td valign="top" width="17%">.1406
+<br></td></tr><tr align="left"><td valign="top" width="17%">5/32 </td><td valign="top" width="17%">.1562 </td><td valign="top" width="17%">11/64 </td><td valign="top" width="17%">.1719 </td><td valign="top" width="17%">3/16 </td><td valign="top" width="17%">.1875
+<br></td></tr><tr align="left"><td valign="top" width="17%">13/64 </td><td valign="top" width="17%">.2031 </td><td valign="top" width="17%">7/32 </td><td valign="top" width="17%">.2188 </td><td valign="top" width="17%">15/64 </td><td valign="top" width="17%">.2344
+<br></td></tr><tr align="left"><td valign="top" width="17%">1/4 </td><td valign="top" width="17%">.2500 </td><td valign="top" width="17%">17/64 </td><td valign="top" width="17%">.2656 </td><td valign="top" width="17%">9/32 </td><td valign="top" width="17%">.2812
+<br></td></tr><tr align="left"><td valign="top" width="17%">19/64 </td><td valign="top" width="17%">.2969 </td><td valign="top" width="17%">5/16 </td><td valign="top" width="17%">.3125 </td><td valign="top" width="17%">21/64 </td><td valign="top" width="17%">.3281
+<br></td></tr><tr align="left"><td valign="top" width="17%">11/32 </td><td valign="top" width="17%">.3438 </td><td valign="top" width="17%">23/64 </td><td valign="top" width="17%">.3594 </td><td valign="top" width="17%">3/8 </td><td valign="top" width="17%">.3750
+<br></td></tr><tr align="left"><td valign="top" width="17%">25/64 </td><td valign="top" width="17%">.3906 </td><td valign="top" width="17%">13/32 </td><td valign="top" width="17%">.4062 </td><td valign="top" width="17%">27/64 </td><td valign="top" width="17%">.4219
+<br></td></tr><tr align="left"><td valign="top" width="17%">7/16 </td><td valign="top" width="17%">.4375 </td><td valign="top" width="17%">29/64 </td><td valign="top" width="17%">.4531 </td><td valign="top" width="17%">15/32 </td><td valign="top" width="17%">.4688
+<br></td></tr><tr align="left"><td valign="top" width="17%">31/64 </td><td valign="top" width="17%">.4844 </td><td valign="top" width="17%">1/2 </td><td valign="top" width="17%">.5000 </td><td valign="top" width="17%">33/64 </td><td valign="top" width="17%">.5156
+<br></td></tr><tr align="left"><td valign="top" width="17%">17/32 </td><td valign="top" width="17%">.5313 </td><td valign="top" width="17%">35/64 </td><td valign="top" width="17%">.5469 </td><td valign="top" width="17%">9/16 </td><td valign="top" width="17%">.5625
+<br></td></tr><tr align="left"><td valign="top" width="17%">37/64 </td><td valign="top" width="17%">.5781 </td><td valign="top" width="17%">19/32 </td><td valign="top" width="17%">.5938 </td><td valign="top" width="17%">39/64 </td><td valign="top" width="17%">.6094
+<br></td></tr><tr align="left"><td valign="top" width="17%">5/8 </td><td valign="top" width="17%">.6250 </td><td valign="top" width="17%">41/64 </td><td valign="top" width="17%">.6406 </td><td valign="top" width="17%">21/32 </td><td valign="top" width="17%">.6562
+<br></td></tr><tr align="left"><td valign="top" width="17%">43/64 </td><td valign="top" width="17%">.6719 </td><td valign="top" width="17%">11/16 </td><td valign="top" width="17%">.6875 </td><td valign="top" width="17%">45/64 </td><td valign="top" width="17%">.7031
+<br></td></tr><tr align="left"><td valign="top" width="17%">23/32 </td><td valign="top" width="17%">.7188 </td><td valign="top" width="17%">47/64 </td><td valign="top" width="17%">.7344 </td><td valign="top" width="17%">3/4 </td><td valign="top" width="17%">.7500
+<br></td></tr><tr align="left"><td valign="top" width="17%">49/64 </td><td valign="top" width="17%">.7656 </td><td valign="top" width="17%">25/32 </td><td valign="top" width="17%">.7812 </td><td valign="top" width="17%">51/64 </td><td valign="top" width="17%">.7969
+<br></td></tr><tr align="left"><td valign="top" width="17%">13/16 </td><td valign="top" width="17%">.8125 </td><td valign="top" width="17%">53/64 </td><td valign="top" width="17%">.8281 </td><td valign="top" width="17%">27/32 </td><td valign="top" width="17%">.8438
+<br></td></tr><tr align="left"><td valign="top" width="17%">55/64 </td><td valign="top" width="17%">.8594 </td><td valign="top" width="17%">7/8 </td><td valign="top" width="17%">.8750 </td><td valign="top" width="17%">57/64 </td><td valign="top" width="17%">.8906
+<br></td></tr><tr align="left"><td valign="top" width="17%">29/32 </td><td valign="top" width="17%">.9062 </td><td valign="top" width="17%">59/64 </td><td valign="top" width="17%">.9219 </td><td valign="top" width="17%">15/16 </td><td valign="top" width="17%">.9375
+<br></td></tr><tr align="left"><td valign="top" width="17%">61/64 </td><td valign="top" width="17%">.9531 </td><td valign="top" width="17%">31/32 </td><td valign="top" width="17%">.9688 </td><td valign="top" width="17%">63/64 </td><td valign="top" width="17%">.9844
+<br></td></tr><tr align="left"><td valign="top" width="17%">1 </td><td valign="top" width="17%">1.0000 </td><td valign="top" width="17%"></td><td valign="top" width="17%"></td><td valign="top" width="17%"></td><td valign="top" width="17%">
+<br></td></tr></table>
+
+<h3 class="section">D.4 Metric Drills</h3>
+
+<!-- Generated file.  Do not edit directly -->
+<!-- $Id$ -->
+<p><table summary=""><tr align="left"><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter </td><td valign="top" width="17%">Drill </td><td valign="top" width="17%">Diameter
+<br></td></tr><tr align="left"><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches) </td><td valign="top" width="17%">Size </td><td valign="top" width="17%">(inches)
+
+<p><br></td></tr><tr align="left"><td valign="top" width="17%">0.20 mm </td><td valign="top" width="17%">.00787 </td><td valign="top" width="17%">0.25 mm </td><td valign="top" width="17%">.00984 </td><td valign="top" width="17%">0.30 mm </td><td valign="top" width="17%">.0118
+<br></td></tr><tr align="left"><td valign="top" width="17%">0.35 mm </td><td valign="top" width="17%">.0138 </td><td valign="top" width="17%">0.40 mm </td><td valign="top" width="17%">.0158 </td><td valign="top" width="17%">0.45 mm </td><td valign="top" width="17%">.0177
+<br></td></tr><tr align="left"><td valign="top" width="17%">0.50 mm </td><td valign="top" width="17%">.0197 </td><td valign="top" width="17%">0.55 mm </td><td valign="top" width="17%">.0217 </td><td valign="top" width="17%">0.60 mm </td><td valign="top" width="17%">.0236
+<br></td></tr><tr align="left"><td valign="top" width="17%">0.65 mm </td><td valign="top" width="17%">.0256 </td><td valign="top" width="17%">0.70 mm </td><td valign="top" width="17%">.0276 </td><td valign="top" width="17%">0.75 mm </td><td valign="top" width="17%">.0295
+<br></td></tr><tr align="left"><td valign="top" width="17%">0.80 mm </td><td valign="top" width="17%">.0315 </td><td valign="top" width="17%">0.85 mm </td><td valign="top" width="17%">.0335 </td><td valign="top" width="17%">0.90 mm </td><td valign="top" width="17%">.0354
+<br></td></tr><tr align="left"><td valign="top" width="17%">0.95 mm </td><td valign="top" width="17%">.0374 </td><td valign="top" width="17%">1.00 mm </td><td valign="top" width="17%">.0394 </td><td valign="top" width="17%">1.05 mm </td><td valign="top" width="17%">.0413
+<br></td></tr><tr align="left"><td valign="top" width="17%">1.10 mm </td><td valign="top" width="17%">.0433 </td><td valign="top" width="17%">1.15 mm </td><td valign="top" width="17%">.0453 </td><td valign="top" width="17%">1.20 mm </td><td valign="top" width="17%">.0472
+<br></td></tr><tr align="left"><td valign="top" width="17%">1.25 mm </td><td valign="top" width="17%">.0492 </td><td valign="top" width="17%">1.30 mm </td><td valign="top" width="17%">.0512 </td><td valign="top" width="17%">1.35 mm </td><td valign="top" width="17%">.0531
+<br></td></tr><tr align="left"><td valign="top" width="17%">1.40 mm </td><td valign="top" width="17%">.0551 </td><td valign="top" width="17%">1.45 mm </td><td valign="top" width="17%">.0571 </td><td valign="top" width="17%">1.50 mm </td><td valign="top" width="17%">.0591
+<br></td></tr><tr align="left"><td valign="top" width="17%">1.55 mm </td><td valign="top" width="17%">.0610 </td><td valign="top" width="17%">1.60 mm </td><td valign="top" width="17%">.0630 </td><td valign="top" width="17%">1.65 mm </td><td valign="top" width="17%">.0650
+<br></td></tr><tr align="left"><td valign="top" width="17%">1.70 mm </td><td valign="top" width="17%">.0669 </td><td valign="top" width="17%">1.75 mm </td><td valign="top" width="17%">.0689 </td><td valign="top" width="17%">1.80 mm </td><td valign="top" width="17%">.0709
+<br></td></tr><tr align="left"><td valign="top" width="17%">1.85 mm </td><td valign="top" width="17%">.0728 </td><td valign="top" width="17%">1.90 mm </td><td valign="top" width="17%">.0748 </td><td valign="top" width="17%">1.95 mm </td><td valign="top" width="17%">.0768
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.00 mm </td><td valign="top" width="17%">.0787 </td><td valign="top" width="17%">2.05 mm </td><td valign="top" width="17%">.0807 </td><td valign="top" width="17%">2.10 mm </td><td valign="top" width="17%">.0827
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.15 mm </td><td valign="top" width="17%">.0846 </td><td valign="top" width="17%">2.20 mm </td><td valign="top" width="17%">.0866 </td><td valign="top" width="17%">2.25 mm </td><td valign="top" width="17%">.0886
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.30 mm </td><td valign="top" width="17%">.0906 </td><td valign="top" width="17%">2.35 mm </td><td valign="top" width="17%">.0925 </td><td valign="top" width="17%">2.40 mm </td><td valign="top" width="17%">.0945
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.45 mm </td><td valign="top" width="17%">.0965 </td><td valign="top" width="17%">2.50 mm </td><td valign="top" width="17%">.0984 </td><td valign="top" width="17%">2.55 mm </td><td valign="top" width="17%">.1004
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.60 mm </td><td valign="top" width="17%">.1024 </td><td valign="top" width="17%">2.65 mm </td><td valign="top" width="17%">.1043 </td><td valign="top" width="17%">2.70 mm </td><td valign="top" width="17%">.1063
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.75 mm </td><td valign="top" width="17%">.1083 </td><td valign="top" width="17%">2.80 mm </td><td valign="top" width="17%">.1102 </td><td valign="top" width="17%">2.85 mm </td><td valign="top" width="17%">.1122
+<br></td></tr><tr align="left"><td valign="top" width="17%">2.90 mm </td><td valign="top" width="17%">.1142 </td><td valign="top" width="17%">2.95 mm </td><td valign="top" width="17%">.1161 </td><td valign="top" width="17%">3.00 mm </td><td valign="top" width="17%">.1181
+<br></td></tr><tr align="left"><td valign="top" width="17%">3.10 mm </td><td valign="top" width="17%">.1220 </td><td valign="top" width="17%">3.15 mm </td><td valign="top" width="17%">.1240 </td><td valign="top" width="17%">3.20 mm </td><td valign="top" width="17%">.1260
+<br></td></tr><tr align="left"><td valign="top" width="17%">3.25 mm </td><td valign="top" width="17%">.1280 </td><td valign="top" width="17%">3.30 mm </td><td valign="top" width="17%">.1299 </td><td valign="top" width="17%">3.40 mm </td><td valign="top" width="17%">.1339
+<br></td></tr><tr align="left"><td valign="top" width="17%">3.50 mm </td><td valign="top" width="17%">.1378 </td><td valign="top" width="17%">3.60 mm </td><td valign="top" width="17%">.1417 </td><td valign="top" width="17%">3.70 mm </td><td valign="top" width="17%">.1457
+<br></td></tr><tr align="left"><td valign="top" width="17%">3.75 mm </td><td valign="top" width="17%">.1476 </td><td valign="top" width="17%">3.80 mm </td><td valign="top" width="17%">.1496 </td><td valign="top" width="17%">3.90 mm </td><td valign="top" width="17%">.1535
+<br></td></tr><tr align="left"><td valign="top" width="17%">4.00 mm </td><td valign="top" width="17%">.1575 </td><td valign="top" width="17%">4.10 mm </td><td valign="top" width="17%">.1614 </td><td valign="top" width="17%">4.20 mm </td><td valign="top" width="17%">.1654
+<br></td></tr><tr align="left"><td valign="top" width="17%">4.25 mm </td><td valign="top" width="17%">.1673 </td><td valign="top" width="17%">4.30 mm </td><td valign="top" width="17%">.1693 </td><td valign="top" width="17%">4.40 mm </td><td valign="top" width="17%">.1732
+<br></td></tr><tr align="left"><td valign="top" width="17%">4.50 mm </td><td valign="top" width="17%">.1772 </td><td valign="top" width="17%">4.60 mm </td><td valign="top" width="17%">.1811 </td><td valign="top" width="17%">4.70 mm </td><td valign="top" width="17%">.1850
+<br></td></tr><tr align="left"><td valign="top" width="17%">4.75 mm </td><td valign="top" width="17%">.1870 </td><td valign="top" width="17%">4.80 mm </td><td valign="top" width="17%">.1890 </td><td valign="top" width="17%">4.90 mm </td><td valign="top" width="17%">.1929
+<br></td></tr><tr align="left"><td valign="top" width="17%">5.00 mm </td><td valign="top" width="17%">.1969 </td><td valign="top" width="17%">5.10 mm </td><td valign="top" width="17%">.2008 </td><td valign="top" width="17%">5.20 mm </td><td valign="top" width="17%">.2047
+<br></td></tr><tr align="left"><td valign="top" width="17%">5.25 mm </td><td valign="top" width="17%">.2067 </td><td valign="top" width="17%">5.30 mm </td><td valign="top" width="17%">.2087 </td><td valign="top" width="17%">5.40 mm </td><td valign="top" width="17%">.2126
+<br></td></tr><tr align="left"><td valign="top" width="17%">5.50 mm </td><td valign="top" width="17%">.2165 </td><td valign="top" width="17%">5.60 mm </td><td valign="top" width="17%">.2205 </td><td valign="top" width="17%">5.70 mm </td><td valign="top" width="17%">.2244
+<br></td></tr><tr align="left"><td valign="top" width="17%">5.75 mm </td><td valign="top" width="17%">.2264 </td><td valign="top" width="17%">5.80 mm </td><td valign="top" width="17%">.2283 </td><td valign="top" width="17%">5.90 mm </td><td valign="top" width="17%">.2323
+<br></td></tr><tr align="left"><td valign="top" width="17%">6.00 mm </td><td valign="top" width="17%">.2362 </td><td valign="top" width="17%">6.10 mm </td><td valign="top" width="17%">.2402 </td><td valign="top" width="17%">6.20 mm </td><td valign="top" width="17%">.2441
+<br></td></tr><tr align="left"><td valign="top" width="17%">6.25 mm </td><td valign="top" width="17%">.2461 </td><td valign="top" width="17%">6.30 mm </td><td valign="top" width="17%">.2480 </td><td valign="top" width="17%">6.40 mm </td><td valign="top" width="17%">.2520
+<br></td></tr><tr align="left"><td valign="top" width="17%">6.50 mm </td><td valign="top" width="17%">.2559 </td><td valign="top" width="17%">6.60 mm </td><td valign="top" width="17%">.2598 </td><td valign="top" width="17%">6.70 mm </td><td valign="top" width="17%">.2638
+<br></td></tr><tr align="left"><td valign="top" width="17%">6.75 mm </td><td valign="top" width="17%">.2657 </td><td valign="top" width="17%">6.80 mm </td><td valign="top" width="17%">.2677 </td><td valign="top" width="17%">6.90 mm </td><td valign="top" width="17%">.2717
+<br></td></tr><tr align="left"><td valign="top" width="17%">7.00 mm </td><td valign="top" width="17%">.2756 </td><td valign="top" width="17%">7.10 mm </td><td valign="top" width="17%">.2795 </td><td valign="top" width="17%">7.20 mm </td><td valign="top" width="17%">.2835
+<br></td></tr><tr align="left"><td valign="top" width="17%">7.25 mm </td><td valign="top" width="17%">.2854 </td><td valign="top" width="17%">7.30 mm </td><td valign="top" width="17%">.2874 </td><td valign="top" width="17%">7.40 mm </td><td valign="top" width="17%">.2914
+<br></td></tr><tr align="left"><td valign="top" width="17%">7.50 mm </td><td valign="top" width="17%">.2953 </td><td valign="top" width="17%">7.60 mm </td><td valign="top" width="17%">.2992 </td><td valign="top" width="17%">7.70 mm </td><td valign="top" width="17%">.3031
+<br></td></tr><tr align="left"><td valign="top" width="17%">8.00 mm </td><td valign="top" width="17%">.3150 </td><td valign="top" width="17%">8.10 mm </td><td valign="top" width="17%">.3189 </td><td valign="top" width="17%">8.20 mm </td><td valign="top" width="17%">.3228
+<br></td></tr><tr align="left"><td valign="top" width="17%">8.25 mm </td><td valign="top" width="17%">.3248 </td><td valign="top" width="17%">8.30 mm </td><td valign="top" width="17%">.3268 </td><td valign="top" width="17%">8.40 mm </td><td valign="top" width="17%">.3307
+<br></td></tr><tr align="left"><td valign="top" width="17%">8.50 mm </td><td valign="top" width="17%">.3346 </td><td valign="top" width="17%">8.60 mm </td><td valign="top" width="17%">.3386 </td><td valign="top" width="17%">8.70 mm </td><td valign="top" width="17%">.3425
+<br></td></tr><tr align="left"><td valign="top" width="17%">8.75 mm </td><td valign="top" width="17%">.3445 </td><td valign="top" width="17%">8.80 mm </td><td valign="top" width="17%">.3465 </td><td valign="top" width="17%">8.90 mm </td><td valign="top" width="17%">.3504
+<br></td></tr><tr align="left"><td valign="top" width="17%">9.00 mm </td><td valign="top" width="17%">.3543 </td><td valign="top" width="17%">9.10 mm </td><td valign="top" width="17%">.3583 </td><td valign="top" width="17%">9.20 mm </td><td valign="top" width="17%">.3622
+<br></td></tr><tr align="left"><td valign="top" width="17%">9.25 mm </td><td valign="top" width="17%">.3642 </td><td valign="top" width="17%">9.30 mm </td><td valign="top" width="17%">.3661 </td><td valign="top" width="17%">9.40 mm </td><td valign="top" width="17%">.3701
+<br></td></tr><tr align="left"><td valign="top" width="17%">9.50 mm </td><td valign="top" width="17%">.3740 </td><td valign="top" width="17%">9.60 mm </td><td valign="top" width="17%">.3780 </td><td valign="top" width="17%">9.70 mm </td><td valign="top" width="17%">.3819
+<br></td></tr><tr align="left"><td valign="top" width="17%">9.75 mm </td><td valign="top" width="17%">.3839 </td><td valign="top" width="17%">9.80 mm </td><td valign="top" width="17%">.3858 </td><td valign="top" width="17%">9.90 mm </td><td valign="top" width="17%">.3898
+<br></td></tr><tr align="left"><td valign="top" width="17%">10.00 mm </td><td valign="top" width="17%">.3937 </td><td valign="top" width="17%">10.10 mm </td><td valign="top" width="17%">.3976 </td><td valign="top" width="17%">10.20 mm </td><td valign="top" width="17%">.4016
+<br></td></tr><tr align="left"><td valign="top" width="17%">10.25 mm </td><td valign="top" width="17%">.4035 </td><td valign="top" width="17%">10.30 mm </td><td valign="top" width="17%">.4055 </td><td valign="top" width="17%">10.40 mm </td><td valign="top" width="17%">.4094
+<br></td></tr><tr align="left"><td valign="top" width="17%">10.50 mm </td><td valign="top" width="17%">.4134 </td><td valign="top" width="17%">10.60 mm </td><td valign="top" width="17%">.4173 </td><td valign="top" width="17%">10.70 mm </td><td valign="top" width="17%">.4213
+<br></td></tr><tr align="left"><td valign="top" width="17%">10.80 mm </td><td valign="top" width="17%">.4252 </td><td valign="top" width="17%">10.90 mm </td><td valign="top" width="17%">.4291 </td><td valign="top" width="17%">11.00 mm </td><td valign="top" width="17%">.4331
+<br></td></tr><tr align="left"><td valign="top" width="17%">11.10 mm </td><td valign="top" width="17%">.4370 </td><td valign="top" width="17%">11.20 mm </td><td valign="top" width="17%">.4409 </td><td valign="top" width="17%">11.25 mm </td><td valign="top" width="17%">.4429
+<br></td></tr><tr align="left"><td valign="top" width="17%">11.30 mm </td><td valign="top" width="17%">.4449 </td><td valign="top" width="17%">11.40 mm </td><td valign="top" width="17%">.4488 </td><td valign="top" width="17%">11.50 mm </td><td valign="top" width="17%">.4528
+<br></td></tr><tr align="left"><td valign="top" width="17%">11.60 mm </td><td valign="top" width="17%">.4567 </td><td valign="top" width="17%">11.70 mm </td><td valign="top" width="17%">.4606 </td><td valign="top" width="17%">11.75 mm </td><td valign="top" width="17%">.4626
+<br></td></tr><tr align="left"><td valign="top" width="17%">11.80 mm </td><td valign="top" width="17%">.4646 </td><td valign="top" width="17%">11.90 mm </td><td valign="top" width="17%">.4685 </td><td valign="top" width="17%">12.00 mm </td><td valign="top" width="17%">.4724
+<br></td></tr><tr align="left"><td valign="top" width="17%">12.50 mm </td><td valign="top" width="17%">.4921 </td><td valign="top" width="17%">13.00 mm </td><td valign="top" width="17%">.5118 </td><td valign="top" width="17%">13.50 mm </td><td valign="top" width="17%">.5315
+<br></td></tr><tr align="left"><td valign="top" width="17%">14.00 mm </td><td valign="top" width="17%">.5512 </td><td valign="top" width="17%">14.50 mm </td><td valign="top" width="17%">.5709 </td><td valign="top" width="17%">15.00 mm </td><td valign="top" width="17%">.5906
+<br></td></tr><tr align="left"><td valign="top" width="17%">15.50 mm </td><td valign="top" width="17%">.6102 </td><td valign="top" width="17%">16.00 mm </td><td valign="top" width="17%">.6299 </td><td valign="top" width="17%">16.50 mm </td><td valign="top" width="17%">.6496
+<br></td></tr><tr align="left"><td valign="top" width="17%">17.00 mm </td><td valign="top" width="17%">.6693 </td><td valign="top" width="17%">17.50 mm </td><td valign="top" width="17%">.6890 </td><td valign="top" width="17%">18.00 mm </td><td valign="top" width="17%">.7087
+<br></td></tr><tr align="left"><td valign="top" width="17%">18.50 mm </td><td valign="top" width="17%">.7283 </td><td valign="top" width="17%">19.00 mm </td><td valign="top" width="17%">.7480 </td><td valign="top" width="17%">19.50 mm </td><td valign="top" width="17%">.7677
+<br></td></tr><tr align="left"><td valign="top" width="17%">20.00 mm </td><td valign="top" width="17%">.7874 </td><td valign="top" width="17%">20.50 mm </td><td valign="top" width="17%">.8071 </td><td valign="top" width="17%">21.00 mm </td><td valign="top" width="17%">.8268
+<br></td></tr><tr align="left"><td valign="top" width="17%">21.50 mm </td><td valign="top" width="17%">.8465 </td><td valign="top" width="17%">22.00 mm </td><td valign="top" width="17%">.8661 </td><td valign="top" width="17%">22.50 mm </td><td valign="top" width="17%">.8858
+<br></td></tr><tr align="left"><td valign="top" width="17%">23.00 mm </td><td valign="top" width="17%">.9055 </td><td valign="top" width="17%">23.50 mm </td><td valign="top" width="17%">.9252 </td><td valign="top" width="17%">24.00 mm </td><td valign="top" width="17%">.9449
+<br></td></tr><tr align="left"><td valign="top" width="17%">24.50 mm </td><td valign="top" width="17%">.9646 </td><td valign="top" width="17%">25.00 mm </td><td valign="top" width="17%">.9843 </td><td valign="top" width="17%"></td><td valign="top" width="17%">
+<br></td></tr></table>
+
+<!--  Appendix - Centroid File Format - -->
+<div class="node">
+<a name="Centroid-File-Format"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Action-Reference">Action Reference</a>,
+Previous: <a rel="previous" accesskey="p" href="#Standard-Drill-Sizes">Standard Drill Sizes</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix E Centroid (X-Y) File Format</h2>
+
+<p><a name="index-centroid-file-format-866"></a><a name="index-x_002dy-file-format-867"></a>
+
+<h3 class="section">E.1 Overview</h3>
+
+<h3 class="section">E.2 File Format</h3>
+
+<p>The centroid output file is in a standard comma seperated values (CSV)
+format.  Comment lines begin with a “#”.  The output file contains a
+header with an RCS Id tag (useful for those who will check the file
+into a version control system), a version number for the file format,
+some comments containing the author and title of the board, and a
+comment describing the remainder of the file format.
+
+   <p>An example centroid file is shown below.
+
+<pre class="example">     
+     # <tt>$</tt>Id<tt>$</tt>
+     # PcbXY Version 1.0
+     # Date: Fri Jul 22 03:40:08 2005 UTC
+     # Author: PCB User
+     # Title: MyBoard - PCB X-Y
+     # RefDes, Description, Value, X, Y, rotation, top/bottom
+     # X,Y in mils.  rotation in degrees.
+     # --------------------------------------------
+     R61,"0603","10",2610.00,3560.00,90,top
+     J5,"AMPHENOL_ARFX1231","unknown",2390.00,4220.00,180,top
+     C13,"0402","0.01u",2340.00,3014.00,270,top
+     
+</pre>
+   <h3 class="section">E.3 Computation of Centroid and Rotation</h3>
+
+<p><a name="index-centroid-file_002c-algorithms-868"></a><a name="index-x_002dy-file_002c-algorithms-869"></a>The center of each element is found by averaging the (X,Y) coordinates
+for the center of each pin and pad in the element.  For example if an
+element has 2 pins, 1 at (1,0) and another at (1,4) then the centroid
+will be at (1,2).
+
+   <p>The calculation of rotation is a bit more complex.  Currently a
+rotation is not stored for each element but rather the rotated element
+is stored.  In other words if the element from the library has a pin
+at (0,0) and (0,2) and it has been rotated by 90 degrees, then the
+<samp><span class="file">.pcb</span></samp> file will store (0,0) and (2,0) for the pin locations with
+no indication that they have been rotated from the original.
+
+   <p>In the event that the element has only 1 pin, then the rotation is set
+to zero.  If the element has only one pad (as opposed to a
+through-hole pin), then the rotation of the pad is used.
+
+   <p>When the element has multiple pins, the location of pin #1 is placed
+in the coordinate system which has the centroid of the part at (0,0). 
+Then which quadrant pin #1 falls in determines the rotation.  Zero
+degrees of rotation is defined as pin #1 being in the upper left
+quadrant.  Increasing angles correspond to counterclockwise rotation so a
+rotation of 90 degrees places pin #1 in the lower left quadrant. 
+Currently, the only allowed rotations are 0, 90, 180, and 270 degrees.
+
+   <p>If pin #1 happens to be at the centroid of the part, then pin #2 is
+examined to see which quadrant it is located in.  The same rules apply
+for the definitions of rotation.  In other words, when pin #1 is at
+the centroid of the part and pin #2 is in the upper left quadrant, the
+rotation is declared to be zero degrees.
+
+<!--  Appendix - Actions - -->
+<div class="node">
+<a name="Action-Reference"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Glossary">Glossary</a>,
+Previous: <a rel="previous" accesskey="p" href="#Centroid-File-Format">Centroid File Format</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix F Action Reference</h2>
+
+<p><a name="index-action-reference-870"></a>
+
+<!-- key actions -->
+<!-- ./../src/action.c 225 -->
+   <p>Many actions take a <code>delta</code> parameter as the last parameter,
+which is an amount to change something.  That <code>delta</code> may include
+units, as an additional parameter, such as <code>Action(Object,5,mm)</code>. 
+If no units are specified, the default is PCB's native units
+(currently 1/100 mil).  Also, if the delta is prefixed by <code>+</code> or
+<code>-</code>, the size is increased or decreased by that amount. 
+Otherwise, the size size is set to the given amount.
+
+<pre class="example">     Action(Object,5,mil)
+     Action(Object,+0.5,mm)
+     Action(Object,-1)
+</pre>
+   <p>Actions which take a <code>delta</code> parameter which do not accept all
+these options will specify what they do take.
+
+<!-- ./../src/action.c 229 -->
+<!-- ./../src/command.c 64 -->
+<!-- ./../src/action.c 227 -->
+   <p>Many actions act on indicated objects on the board.  They will have
+parameters like <code>ToggleObject</code> or <code>SelectedVias</code> to indicate
+what group of objects they act on.  Unless otherwise specified, these
+parameters are defined as follows:
+
+     <dl>
+<dt><code>Object</code><dt><code>ToggleObject</code><dd>Affects the object under the mouse pointer.  If this action is invoked
+from a menu or script, the user will be prompted to click on an
+object, which is then the object affected.
+
+     <br><dt><code>Selected</code><dt><code>SelectedObjects</code><dd>
+Affects all objects which are currently selected.  At least, all
+selected objects for which the given action makes sense.
+
+     <br><dt><code>SelectedPins</code><dt><code>SelectedVias</code><dt><code>Selected</code><var>Type</var><dt><i>etc</i><dd>Affects all objects which are both selected and of the <var>Type</var> specified.
+
+   </dl>
+
+<ul class="menu">
+<li><a accesskey="1" href="#core-actions">core actions</a>
+<li><a accesskey="2" href="#common-actions">common actions</a>
+<li><a accesskey="3" href="#gtk-actions">gtk actions</a>
+<li><a accesskey="4" href="#lesstif-actions">lesstif actions</a>
+</ul>
+
+<div class="node">
+<a name="core-actions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#common-actions">common actions</a>,
+Up: <a rel="up" accesskey="u" href="#Action-Reference">Action Reference</a>
+
+</div>
+
+<h3 class="section">F.1 Core actions</h3>
+
+<ul class="menu">
+<li><a accesskey="1" href="#AddRats-Action">AddRats Action</a>:  Add one or more rat lines to the board. 
+<li><a accesskey="2" href="#ApplyVendor-Action">ApplyVendor Action</a>:  Applies the currently loaded vendor drill table to the current design. 
+<li><a accesskey="3" href="#Atomic-Action">Atomic Action</a>:  Save or restore the undo serial number. 
+<li><a accesskey="4" href="#Attributes-Action">Attributes Action</a>:  Let the user edit the attributes of the layout, current or given
+layer, or selected element. 
+<li><a accesskey="5" href="#AutoPlaceSelected-Action">AutoPlaceSelected Action</a>:  Auto-place selected components. 
+<li><a accesskey="6" href="#AutoRoute-Action">AutoRoute Action</a>:  Auto-route some or all rat lines. 
+<li><a accesskey="7" href="#ChangeClearSize-Action">ChangeClearSize Action</a>:  Changes the clearance size of objects. 
+<li><a accesskey="8" href="#ChangeDrillSize-Action">ChangeDrillSize Action</a>:  Changes the drilling hole size of objects. 
+<li><a accesskey="9" href="#ChangeFlag-Action">ChangeFlag Action</a>:  Sets or clears flags on objects. 
+<li><a href="#ChangeHole-Action">ChangeHole Action</a>:  Changes the hole flag of objects. 
+<li><a href="#ChangeJoin-Action">ChangeJoin Action</a>:  Changes the join (clearance through polygons) of objects. 
+<li><a href="#ChangeName-Action">ChangeName Action</a>:  Sets the name of objects. 
+<li><a href="#ChangeOctagon-Action">ChangeOctagon Action</a>:  Changes the octagon-flag of pins and vias. 
+<li><a href="#ChangePaste-Action">ChangePaste Action</a>:  Changes the no paste flag of objects. 
+<li><a href="#ChangePinName-Action">ChangePinName Action</a>:  Sets the name of a specific pin on a specific element. 
+<li><a href="#ChangeSize-Action">ChangeSize Action</a>:  Changes the size of objects. 
+<li><a href="#ChangeSquare-Action">ChangeSquare Action</a>:  Changes the square flag of pins and pads. 
+<li><a href="#ClearOctagon-Action">ClearOctagon Action</a>:  Clears the octagon-flag of pins and vias. 
+<li><a href="#ClearSquare-Action">ClearSquare Action</a>:  Clears the square-flag of pins and pads. 
+<li><a href="#ClrFlag-Action">ClrFlag Action</a>:  Clears flags on objects. 
+<li><a href="#Connection-Action">Connection Action</a>:  Searches connections of the object at the cursor position. 
+<li><a href="#Delete-Action">Delete Action</a>:  Delete stuff. 
+<li><a href="#DeleteRats-Action">DeleteRats Action</a>:  Delete rat lines. 
+<li><a href="#DisableVendor-Action">DisableVendor Action</a>:  Disables automatic drill size mapping. 
+<li><a href="#DisperseElements-Action">DisperseElements Action</a>:  Disperses elements. 
+<li><a href="#Display-Action">Display Action</a>:  Several display-related actions. 
+<li><a href="#djopt-Action">djopt Action</a>:  Perform various optimizations on the current board. 
+<li><a href="#DRC-Action">DRC Action</a>:  Invoke the DRC check. 
+<li><a href="#DumpLibrary-Action">DumpLibrary Action</a>:  Display the entire contents of the libraries. 
+<li><a href="#elementlist-Action">elementlist Action</a>:  Adds the given element if it doesn't already exist. 
+<li><a href="#elementsetattr-Action">elementsetattr Action</a>:  Sets or clears an element-specific attribute. 
+<li><a href="#EnableVendor-Action">EnableVendor Action</a>:  Enables automatic drill size mapping. 
+<li><a href="#execcommand-Action">execcommand Action</a>:  Runs a command. 
+<li><a href="#ExecuteFile-Action">ExecuteFile Action</a>:  Run actions from the given file. 
+<li><a href="#Flip-Action">Flip Action</a>:  Flip an element to the opposite side of the board. 
+<li><a href="#FontEdit-Action">FontEdit Action</a>:  Convert the current font to a PCB for editing. 
+<li><a href="#FontSave-Action">FontSave Action</a>:  Convert the current PCB back to a font. 
+<li><a href="#FreeRotateBuffer-Action">FreeRotateBuffer Action</a>:  Rotates the current paste buffer contents by the specified angle.  The
+angle is given in degrees.  If no angle is given, the user is prompted
+for one.
+
+<li><a href="#GlobalPuller-Action">GlobalPuller Action</a>:  Pull all traces tight. 
+<li><a href="#h-Action">h Action</a>:  Print a help message for commands. 
+<li><a href="#Import-Action">Import Action</a>:  Import schematics. 
+<li><a href="#l-Action">l Action</a>:  Loads layout data. 
+<li><a href="#le-Action">le Action</a>:  Loads an element into the current buffer. 
+<li><a href="#LoadFootprint-Action">LoadFootprint Action</a>:  Loads a single footprint by name. 
+<li><a href="#LoadFrom-Action">LoadFrom Action</a>:  Load layout data from a file. 
+<li><a href="#LoadVendorFrom-Action">LoadVendorFrom Action</a>:  Loads the specified vendor resource file. 
+<li><a href="#m-Action">m Action</a>:  Loads a layout into the current buffer. 
+<li><a href="#MarkCrosshair-Action">MarkCrosshair Action</a>:  Set/Reset the Crosshair mark. 
+<li><a href="#Message-Action">Message Action</a>:  Writes a message to the log window. 
+<li><a href="#MinClearGap-Action">MinClearGap Action</a>:  Ensures that polygons are a minimum distance from objects. 
+<li><a href="#MinMaskGap-Action">MinMaskGap Action</a>:  Ensures the mask is a minimum distance from pins and pads. 
+<li><a href="#Mode-Action">Mode Action</a>:  Change or use the tool mode. 
+<li><a href="#MorphPolygon-Action">MorphPolygon Action</a>:  Converts dead polygon islands into separate polygons. 
+<li><a href="#MoveLayer-Action">MoveLayer Action</a>:  Moves/Creates/Deletes Layers. 
+<li><a href="#MoveObject-Action">MoveObject Action</a>:  Moves the object under the crosshair. 
+<li><a href="#MoveToCurrentLayer-Action">MoveToCurrentLayer Action</a>:  Moves objects to the current layer. 
+<li><a href="#Netlist-Action">Netlist Action</a>:  Perform various actions on netlists. 
+<li><a href="#New-Action">New Action</a>:  Starts a new layout. 
+<li><a href="#OptAutoOnly-Action">OptAutoOnly Action</a>:  Toggles the optimize-only-autorouted flag. 
+<li><a href="#PasteBuffer-Action">PasteBuffer Action</a>:  Various operations on the paste buffer. 
+<li><a href="#Polygon-Action">Polygon Action</a>:  Some polygon related stuff. 
+<li><a href="#Puller-Action">Puller Action</a>:  Pull an arc-line junction tight. 
+<li><a href="#q-Action">q Action</a>:  Quits the application after confirming. 
+<li><a href="#q_0021-Action">q! Action</a>:  Quits the application without confirming. 
+<li><a href="#Quit-Action">Quit Action</a>:  Quits the application after confirming. 
+<li><a href="#Redo-Action">Redo Action</a>:  Redo recent``undo''operations. 
+<li><a href="#RemoveSelected-Action">RemoveSelected Action</a>:  Removes any selected objects. 
+<li><a href="#Renumber-Action">Renumber Action</a>:  Renumber all elements.  The changes will be recorded to filename
+for use in backannotating these changes to the schematic. 
+<li><a href="#Report-Action">Report Action</a>:  Produce various report. 
+<li><a href="#ReportDialog-Action">ReportDialog Action</a>:  Report on the object under the crosshair
+<li><a href="#RipUp-Action">RipUp Action</a>:  Ripup auto-routed tracks, or convert an element to parts. 
+<li><a href="#rn-Action">rn Action</a>:  Reads netlist. 
+<li><a href="#RouteStyle-Action">RouteStyle Action</a>:  Copies the indicated routing style into the current sizes. 
+<li><a href="#s-Action">s Action</a>:  Saves layout data. 
+<li><a href="#SaveSettings-Action">SaveSettings Action</a>:  Saves settings. 
+<li><a href="#SaveTo-Action">SaveTo Action</a>:  Saves data to a file. 
+<li><a href="#Select-Action">Select Action</a>:  Toggles or sets the selection. 
+<li><a href="#SetFlag-Action">SetFlag Action</a>:  Sets flags on objects. 
+<li><a href="#SetOctagon-Action">SetOctagon Action</a>:  Sets the octagon-flag of objects. 
+<li><a href="#SetSame-Action">SetSame Action</a>:  Sets current layer and sizes to match indicated item. 
+<li><a href="#SetSquare-Action">SetSquare Action</a>:  sets the square-flag of objects. 
+<li><a href="#SetThermal-Action">SetThermal Action</a>:  Set the thermal (on the current layer) of pins or vias to the given style. 
+Style = 0 means no thermal. 
+Style = 1 has diagonal fingers with sharp edges. 
+Style = 2 has horizontal and vertical fingers with sharp edges. 
+Style = 3 is a solid connection to the plane.Style = 4 has diagonal fingers with rounded edges. 
+Style = 5 has horizontal and vertical fingers with rounded edges.
+
+<li><a href="#SetValue-Action">SetValue Action</a>:  Change various board-wide values and sizes. 
+<li><a href="#ToggleHideName-Action">ToggleHideName Action</a>:  Toggles the visibility of element names. 
+<li><a href="#ToggleVendor-Action">ToggleVendor Action</a>:  Toggles the state of automatic drill size mapping. 
+<li><a href="#Undo-Action">Undo Action</a>:  Undo recent changes. 
+<li><a href="#UnloadVendor-Action">UnloadVendor Action</a>:  Unloads the current vendor drill mapping table. 
+<li><a href="#Unselect-Action">Unselect Action</a>:  Unselects the object at the pointer location or the specified objects. 
+<li><a href="#w-Action">w Action</a>:  Saves layout data. 
+<li><a href="#wq-Action">wq Action</a>:  Saves the layout data and quits. 
+</ul>
+<div class="node">
+<a name="AddRats-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ApplyVendor-Action">ApplyVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.1 AddRats</h4>
+
+<!-- key AddRats in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">AddRats(AllRats|SelectedRats|Close)</pre>
+   </td></tr></table>
+
+   <p>Add one or more rat lines to the board. 
+<!-- ./../src/action.c 3310 -->
+
+     <dl>
+<dt><code>AllRats</code><dd>Create rat lines for all loaded nets that aren't already connected on
+with copper.
+
+     <br><dt><code>SelectedRats</code><dd>Similarly, but only add rat lines for nets connected to selected pins
+and pads.
+
+     <br><dt><code>Close</code><dd>Selects the shortest unselected rat on the board.
+
+   </dl>
+
+<div class="node">
+<a name="ApplyVendor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Atomic-Action">Atomic Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#AddRats-Action">AddRats Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.2 ApplyVendor</h4>
+
+<!-- key ApplyVendor in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ApplyVendor()</pre>
+   </td></tr></table>
+
+   <p>Applies the currently loaded vendor drill table to the current design. 
+<!-- ./../src/vendor.c 112 -->
+<a name="index-vendor-map-871"></a><a name="index-vendor-drill-table-872"></a><a name="index-ApplyVendor_0028_0029-873"></a>
+This will modify all of your drill holes to match the list of allowed
+sizes for your vendor.
+
+<div class="node">
+<a name="Atomic-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Attributes-Action">Attributes Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ApplyVendor-Action">ApplyVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.3 Atomic</h4>
+
+<!-- key Atomic in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Atomic(Save|Restore|Close|Block)</pre>
+   </td></tr></table>
+
+   <p>Save or restore the undo serial number. 
+<!-- ./../src/action.c 1720 -->
+
+   <p>This action allows making multiple-action bindings into an atomic
+operation that will be undone by a single Undo command.  For example,
+to optimize rat lines, you'd delete the rats and re-add them.  To
+group these into a single undo, you'd want the deletions and the
+additions to have the same undo serial number.  So, you <code>Save</code>,
+delete the rats, <code>Restore</code>, add the rats - using the same serial
+number as the deletes, then <code>Block</code>, which checks to see if the
+deletions or additions actually did anything.  If not, the serial
+number is set to the saved number, as there's nothing to undo.  If
+something did happen, the serial number is incremented so that these
+actions are counted as a single undo step.
+
+     <dl>
+<dt><code>Save</code><dd>Saves the undo serial number.
+
+     <br><dt><code>Restore</code><dd>Returns it to the last saved number.
+
+     <br><dt><code>Close</code><dd>Sets it to 1 greater than the last save.
+
+     <br><dt><code>Block</code><dd>Does a Restore if there was nothing to undo, else does a Close.
+
+   </dl>
+
+<div class="node">
+<a name="Attributes-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#AutoPlaceSelected-Action">AutoPlaceSelected Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Atomic-Action">Atomic Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.4 Attributes</h4>
+
+<!-- key Attributes in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Attributes(Layout|Layer|Element)
+Attributes(Layer,layername)</pre>
+   </td></tr></table>
+
+   <p>Let the user edit the attributes of the layout, current or given
+layer, or selected element. 
+<!-- ./../src/action.c 6761 -->
+
+   <p>This just pops up a dialog letting the user edit the attributes of the
+pcb, an element, or a layer.
+
+<div class="node">
+<a name="AutoPlaceSelected-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#AutoRoute-Action">AutoRoute Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Attributes-Action">Attributes Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.5 AutoPlaceSelected</h4>
+
+<!-- key AutoPlaceSelected in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">AutoPlaceSelected()</pre>
+   </td></tr></table>
+
+   <p>Auto-place selected components. 
+<!-- ./../src/action.c 3450 -->
+
+   <p>Attempts to re-arrange the selected components such that the nets
+connecting them are minimized.  Note that you cannot undo this.
+
+<div class="node">
+<a name="AutoRoute-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeClearSize-Action">ChangeClearSize Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#AutoPlaceSelected-Action">AutoPlaceSelected Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.6 AutoRoute</h4>
+
+<!-- key AutoRoute in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">AutoRoute(AllRats|SelectedRats)</pre>
+   </td></tr></table>
+
+   <p>Auto-route some or all rat lines. 
+<!-- ./../src/action.c 3471 -->
+
+     <dl>
+<dt><code>AllRats</code><dd>Attempt to autoroute all rats.
+
+     <br><dt><code>SelectedRats</code><dd>Attempt to autoroute the selected rats.
+
+   </dl>
+
+   <p>Before autorouting, it's important to set up a few things.  First,
+make sure any layers you aren't using are disabled, else the
+autorouter may use them.  Next, make sure the current line and via
+styles are set accordingly.  Last, make sure "new lines clear
+polygons" is set, in case you eventually want to add a copper pour.
+
+   <p>Autorouting takes a while.  During this time, the program may not be
+responsive.
+
+<div class="node">
+<a name="ChangeClearSize-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeDrillSize-Action">ChangeDrillSize Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#AutoRoute-Action">AutoRoute Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.7 ChangeClearSize</h4>
+
+<!-- key ChangeClearSize in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeClearSize(Object, delta)
+ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)
+ChangeClearSize(SelectedLines|SelectedArcs, delta
+ChangeClearSize(Selected|SelectedObjects, delta)</pre>
+   </td></tr></table>
+
+   <p>Changes the clearance size of objects. 
+<!-- ./../src/action.c 3686 -->
+
+   <p>If the solder mask is currently showing, this action changes the
+solder mask clearance.  If the mask is not showing, this action
+changes the polygon clearance.
+
+<div class="node">
+<a name="ChangeDrillSize-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeFlag-Action">ChangeFlag Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeClearSize-Action">ChangeClearSize Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.8 ChangeDrillSize</h4>
+
+<!-- key ChangeDrillSize in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeDrillSize(Object, delta)
+ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)</pre>
+   </td></tr></table>
+
+   <p>Changes the drilling hole size of objects. 
+<!-- ./../src/action.c 3630 -->
+
+<div class="node">
+<a name="ChangeFlag-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeHole-Action">ChangeHole Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeDrillSize-Action">ChangeDrillSize Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.9 ChangeFlag</h4>
+
+<!-- key ChangeFlag in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeFlag(Object|Selected|SelectedObjects, flag, value)
+ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)
+ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)
+ChangeFlag(SelectedElements, flag, value)
+flag = square | octagon | thermal | join
+value = 0 | 1</pre>
+   </td></tr></table>
+
+   <p>Sets or clears flags on objects. 
+<!-- ./../src/action.c 5750 -->
+
+   <p>Toggles the given flag on the indicated object(s).  The flag may be
+one of the flags listed above (square, octagon, thermal, join).  The
+value may be the number 0 or 1.  If the value is 0, the flag is
+cleared.  If the value is 1, the flag is set.
+
+<div class="node">
+<a name="ChangeHole-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeJoin-Action">ChangeJoin Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeFlag-Action">ChangeFlag Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.10 ChangeHole</h4>
+
+<!-- key ChangeHole in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeHole(ToggleObject|Object|SelectedVias|Selected)</pre>
+   </td></tr></table>
+
+   <p>Changes the hole flag of objects. 
+<!-- ./../src/action.c 4571 -->
+
+   <p>The "hole flag" of a via determines whether the via is a
+plated-through hole (not set), or an unplated hole (set).
+
+<div class="node">
+<a name="ChangeJoin-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeName-Action">ChangeName Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeHole-Action">ChangeHole Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.11 ChangeJoin</h4>
+
+<!-- key ChangeJoin in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)</pre>
+   </td></tr></table>
+
+   <p>Changes the join (clearance through polygons) of objects. 
+<!-- ./../src/action.c 4199 -->
+
+   <p>The join flag determines whether a line or arc, drawn to intersect a
+polygon, electrically connects to the polygon or not.  When joined,
+the line/arc is simply drawn over the polygon, making an electrical
+connection.  When not joined, a gap is drawn between the line and the
+polygon, insulating them from each other.
+
+<div class="node">
+<a name="ChangeName-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeOctagon-Action">ChangeOctagon Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeJoin-Action">ChangeJoin Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.12 ChangeName</h4>
+
+<!-- key ChangeName in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeName(Object)
+ChangeName(Layout|Layer)</pre>
+   </td></tr></table>
+
+   <p>Sets the name of objects. 
+<!-- ./../src/action.c 4005 -->
+
+     <dl>
+<dt><code>Object</code><dd>Changes the name of the element under the cursor.
+
+     <br><dt><code>Layout</code><dd>Changes the name of the layout.  This is printed on the fab drawings.
+
+     <br><dt><code>Layer</code><dd>Changes the name of the currently active layer.
+
+   </dl>
+
+<div class="node">
+<a name="ChangeOctagon-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangePaste-Action">ChangePaste Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeName-Action">ChangeName Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.13 ChangeOctagon</h4>
+
+<!-- key ChangeOctagon in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)
+ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)</pre>
+   </td></tr></table>
+
+   <p>Changes the octagon-flag of pins and vias. 
+<!-- ./../src/action.c 4403 -->
+
+   <p>Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+   <p>Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+<div class="node">
+<a name="ChangePaste-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangePinName-Action">ChangePinName Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeOctagon-Action">ChangeOctagon Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.14 ChangePaste</h4>
+
+<!-- key ChangePaste in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangePaste(ToggleObject|Object|SelectedPads|Selected)</pre>
+   </td></tr></table>
+
+   <p>Changes the no paste flag of objects. 
+<!-- ./../src/action.c 4611 -->
+
+   <p>The "no paste flag" of a pad determines whether the solderpaste
+ stencil will have an opening for the pad (no set) or if there wil be
+ no solderpaste on the pad (set).  This is used for things such as
+ fiducial pads.
+
+<div class="node">
+<a name="ChangePinName-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeSize-Action">ChangeSize Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangePaste-Action">ChangePaste Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.15 ChangePinName</h4>
+
+<!-- key ChangePinName in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangePinName(ElementName,PinNumber,PinName)</pre>
+   </td></tr></table>
+
+   <p>Sets the name of a specific pin on a specific element. 
+<!-- ./../src/action.c 3924 -->
+
+   <p>This can be especially useful for annotating pin names from a
+schematic to the layout without requiring knowledge of the pcb file
+format.
+
+<pre class="example">     ChangePinName(U3, 7, VCC)
+</pre>
+   <div class="node">
+<a name="ChangeSize-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ChangeSquare-Action">ChangeSquare Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangePinName-Action">ChangePinName Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.16 ChangeSize</h4>
+
+<!-- key ChangeSize in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeSize(Object, delta)
+ChangeSize(SelectedObjects|Selected, delta)
+ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)
+ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)
+ChangeSize(SelectedElements, delta)</pre>
+   </td></tr></table>
+
+   <p>Changes the size of objects. 
+<!-- ./../src/action.c 3543 -->
+
+   <p>For lines and arcs, this changes the width.  For pins and vias, this
+changes the overall diameter of the copper annulus.  For pads, this
+changes the width and, indirectly, the length.  For texts and names,
+this changes the scaling factor.  For elements, this changes the width
+of the silk layer lines and arcs for this element.
+
+<div class="node">
+<a name="ChangeSquare-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ClearOctagon-Action">ClearOctagon Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeSize-Action">ChangeSize Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.17 ChangeSquare</h4>
+
+<!-- key ChangeSquare in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ChangeSquare(ToggleObject)
+ChangeSquare(SelectedElements|SelectedPins)
+ChangeSquare(Selected|SelectedObjects)</pre>
+   </td></tr></table>
+
+   <p>Changes the square flag of pins and pads. 
+<!-- ./../src/action.c 4250 -->
+
+   <p>Note that <code>Pins</code> means both pins and pads.
+
+   <p>Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+   <p>Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+<div class="node">
+<a name="ClearOctagon-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ClearSquare-Action">ClearSquare Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ChangeSquare-Action">ChangeSquare Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.18 ClearOctagon</h4>
+
+<!-- key ClearOctagon in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)
+ClearOctagon(SelectedElements|SelectedPins|SelectedVias)</pre>
+   </td></tr></table>
+
+   <p>Clears the octagon-flag of pins and vias. 
+<!-- ./../src/action.c 4515 -->
+
+   <p>Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+   <p>Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+<div class="node">
+<a name="ClearSquare-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ClrFlag-Action">ClrFlag Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ClearOctagon-Action">ClearOctagon Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.19 ClearSquare</h4>
+
+<!-- key ClearSquare in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ClearSquare(ToggleObject|SelectedElements|SelectedPins)</pre>
+   </td></tr></table>
+
+   <p>Clears the square-flag of pins and pads. 
+<!-- ./../src/action.c 4352 -->
+
+   <p>Note that <code>Pins</code> means pins and pads.
+
+   <p>Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+   <p>Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+<div class="node">
+<a name="ClrFlag-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Connection-Action">Connection Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ClearSquare-Action">ClearSquare Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.20 ClrFlag</h4>
+
+<!-- key ClrFlag in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ClrFlag(Object|Selected|SelectedObjects, flag)
+ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)
+ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)
+ClrFlag(SelectedElements, flag)
+flag = square | octagon | thermal | join</pre>
+   </td></tr></table>
+
+   <p>Clears flags on objects. 
+<!-- ./../src/action.c 5733 -->
+
+   <p>Turns the given flag off, regardless of its previous setting.  See
+<code>ChangeFlag</code>.
+
+<pre class="example">     ClrFlag(SelectedLines,join)
+</pre>
+   <div class="node">
+<a name="Connection-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Delete-Action">Delete Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ClrFlag-Action">ClrFlag Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.21 Connection</h4>
+
+<!-- key Connection in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)</pre>
+   </td></tr></table>
+
+   <p>Searches connections of the object at the cursor position. 
+<!-- ./../src/action.c 2093 -->
+
+   <p>Connections found with this action will be highlighted in the
+“connected-color” color and will have the “found” flag set.
+
+     <dl>
+<dt><code>Find</code><dd>The net under the cursor is “found”.
+
+     <br><dt><code>ResetLinesAndPolygons</code><dd>Any “found” lines and polygons are marked “not found”.
+
+     <br><dt><code>ResetPinsAndVias</code><dd>Any “found” pins and vias are marked “not found”.
+
+     <br><dt><code>Reset</code><dd>All “found” objects are marked “not found”.
+
+   </dl>
+
+<div class="node">
+<a name="Delete-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DeleteRats-Action">DeleteRats Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Connection-Action">Connection Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.22 Delete</h4>
+
+<!-- key Delete in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Delete(Object|Selected)
+Delete(AllRats|SelectedRats)</pre>
+   </td></tr></table>
+
+   <p>Delete stuff. 
+<!-- ./../src/action.c 3371 -->
+
+<div class="node">
+<a name="DeleteRats-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DisableVendor-Action">DisableVendor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Delete-Action">Delete Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.23 DeleteRats</h4>
+
+<!-- key DeleteRats in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DeleteRats(AllRats|Selected|SelectedRats)</pre>
+   </td></tr></table>
+
+   <p>Delete rat lines. 
+<!-- ./../src/action.c 3418 -->
+
+<div class="node">
+<a name="DisableVendor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DisperseElements-Action">DisperseElements Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#DeleteRats-Action">DeleteRats Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.24 DisableVendor</h4>
+
+<!-- key DisableVendor in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DisableVendor()</pre>
+   </td></tr></table>
+
+   <p>Disables automatic drill size mapping. 
+<!-- ./../src/vendor.c 161 -->
+
+   <p><a name="index-vendor-map-874"></a><a name="index-vendor-drill-table-875"></a><a name="index-DisableVendor_0028_0029-876"></a>
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.
+
+<div class="node">
+<a name="DisperseElements-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Display-Action">Display Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#DisableVendor-Action">DisableVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.25 DisperseElements</h4>
+
+<!-- key DisperseElements in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DisperseElements(All|Selected)</pre>
+   </td></tr></table>
+
+   <p>Disperses elements. 
+<!-- ./../src/action.c 2146 -->
+
+   <p>Normally this is used when starting a board, by selecting all elements
+and then dispersing them.  This scatters the elements around the board
+so that you can pick individual ones, rather than have all the
+elements at the same 0,0 coordinate and thus impossible to choose
+from.
+
+<div class="node">
+<a name="Display-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#djopt-Action">djopt Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#DisperseElements-Action">DisperseElements Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.26 Display</h4>
+
+<!-- key Display in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Display(NameOnPCB|Description|Value)
+Display(Grid|Redraw)
+Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)
+Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)
+Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)
+Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)
+Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)
+Display(ToggleLiveRoute|LockNames|OnlyNames)
+Display(Pinout|PinOrPadName)</pre>
+   </td></tr></table>
+
+   <p>Several display-related actions. 
+<!-- ./../src/action.c 2262 -->
+
+     <dl>
+<dt><code>NameOnPCB</code><br><dt><code>Description</code><br><dt><code>Value</code><dd>Specify whether all elements show their name, description, or value.
+
+     <br><dt><code>Redraw</code><dd>Redraw the whole board.
+
+     <br><dt><code>Toggle45Degree</code><dd>When clear, lines can be drawn at any angle.  When set, lines are
+restricted to multiples of 45 degrees and requested lines may be
+broken up according to the clip setting.
+
+     <br><dt><code>CycleClip</code><dd>Changes the way lines are restricted to 45 degree increments.  The
+various settings are: straight only, orthogonal then angled, and angled
+then orthogonal.  If AllDirections is set, this action disables it.
+
+     <br><dt><code>CycleCrosshair</code><dd>Changes crosshair drawing.  Crosshair may accept form of 4-ray,
+8-ray and 12-ray cross.
+
+     <br><dt><code>ToggleRubberBandMode</code><dd>If set, moving an object moves all the lines attached to it too.
+
+     <br><dt><code>ToggleStartDirection</code><dd>If set, each time you set a point in a line, the Clip toggles between
+orth-angle and angle-ortho.
+
+     <br><dt><code>ToggleUniqueNames</code><dd>If set, you will not be permitted to change the name of an element to
+match that of another element.
+
+     <br><dt><code>ToggleSnapPin</code><dd>If set, pin centers and pad end points are treated as additional grid
+points that the cursor can snap to.
+
+     <br><dt><code>ToggleLocalRef</code><dd>If set, the mark is automatically set to the beginning of any move, so
+you can see the relative distance you've moved.
+
+     <br><dt><code>ToggleThindraw</code><dd>If set, objects on the screen are drawn as outlines (lines are drawn
+as center-lines).  This lets you see line endpoints hidden under pins,
+for example.
+
+     <br><dt><code>ToggleThindrawPoly</code><dd>If set, polygons on the screen are drawn as outlines.
+
+     <br><dt><code>ToggleShowDRC</code><dd>If set, pending objects (i.e. lines you're in the process of drawing)
+will be drawn with an outline showing how far away from other copper
+you need to be.
+
+     <br><dt><code>ToggleLiveRoute</code><dd>If set, the progress of the autorouter will be visible on the screen.
+
+     <br><dt><code>ToggleAutoDRC</code><dd>If set, you will not be permitted to make connections which violate
+the current DRC and netlist settings.
+
+     <br><dt><code>ToggleCheckPlanes</code><dd>If set, lines and arcs aren't drawn, which usually leaves just the
+polygons.  If you also disable all but the layer you're interested in,
+this allows you to check for isolated regions.
+
+     <br><dt><code>ToggleOrthoMove</code><dd>If set, the crosshair is only allowed to move orthogonally from its
+previous position.  I.e. you can move an element or line up, down,
+left, or right, but not up+left or down+right.
+
+     <br><dt><code>ToggleName</code><dd>Selects whether the pinouts show the pin names or the pin numbers.
+
+     <br><dt><code>ToggleLockNames</code><dd>If set, text will ignore left mouse clicks and actions that work on
+objects under the mouse. You can still select text with a lasso (left
+mouse drag) and perform actions on the selection.
+
+     <br><dt><code>ToggleOnlyNames</code><dd>If set, only text will be sensitive for mouse clicks and actions that
+work on objects under the mouse. You can still select other objects
+with a lasso (left mouse drag) and perform actions on the selection.
+
+     <br><dt><code>ToggleMask</code><dd>Turns the solder mask on or off.
+
+     <br><dt><code>ToggleClearLine</code><dd>When set, the clear-line flag causes new lines and arcs to have their
+“clear polygons” flag set, so they won't be electrically connected
+to any polygons they overlap.
+
+     <br><dt><code>ToggleFullPoly</code><dd>When set, the full-poly flag causes new polygons to have their
+“full polygon” flag set, so all parts of them will be displayed
+instead of only the biggest one.
+
+     <br><dt><code>ToggleGrid</code><dd>Resets the origin of the current grid to be wherever the mouse pointer
+is (not where the crosshair currently is).  If you provide two numbers
+after this, the origin is set to that coordinate.
+
+     <br><dt><code>Grid</code><dd>Toggles whether the grid is displayed or not.
+
+     <br><dt><code>Pinout</code><dd>Causes the pinout of the element indicated by the cursor to be
+displayed, usually in a separate window.
+
+     <br><dt><code>PinOrPadName</code><dd>Toggles whether the names of pins, pads, or (yes) vias will be
+displayed.  If the cursor is over an element, all of its pins and pads
+are affected.
+
+   </dl>
+
+<div class="node">
+<a name="djopt-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DRC-Action">DRC Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Display-Action">Display Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.27 djopt</h4>
+
+<!-- key djopt in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">djopt(debumpify|unjaggy|simple|vianudge|viatrim|orthopull)
+djopt(auto) - all of the above
+djopt(miter)</pre>
+   </td></tr></table>
+
+   <p>Perform various optimizations on the current board. 
+<!-- ./../src/djopt.c 2853 -->
+
+   <p>The different types of optimizations change your board in order to
+reduce the total trace length and via count.
+
+     <dl>
+<dt><code>debumpify</code><dd>Looks for U-shaped traces that can be shortened or eliminated.
+
+     <br><dt><code>unjaggy</code><dd>Looks for corners which could be flipped to eliminate one or more
+corners (i.e. jaggy lines become simpler).
+
+     <br><dt><code>simple</code><dd>Removing uneeded vias, replacing two or more trace segments in a row
+with a single segment.  This is usually performed automatically after
+other optimizations.
+
+     <br><dt><code>vianudge</code><dd>Looks for vias where all traces leave in the same direction.  Tries to
+move via in that direction to eliminate one of the traces (and thus a
+corner).
+
+     <br><dt><code>viatrim</code><dd>Looks for traces that go from via to via, where moving that trace to a
+different layer eliminates one or both vias.
+
+     <br><dt><code>orthopull</code><dd>Looks for chains of traces all going in one direction, with more
+traces orthogonal on one side than on the other.  Moves the chain in
+that direction, causing a net reduction in trace length, possibly
+eliminating traces and/or corners.
+
+     <br><dt><code>splitlines</code><dd>Looks for lines that pass through vias, pins, or pads, and splits them
+into separate lines so they can be managed separately.
+
+     <br><dt><code>auto</code><dd>Performs the above options, repeating until no further optimizations
+can be made.
+
+     <br><dt><code>miter</code><dd>Replaces 90 degree corners with a pair of 45 degree corners, to reduce
+RF losses and trace length.
+
+   </dl>
+
+<div class="node">
+<a name="DRC-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#DumpLibrary-Action">DumpLibrary Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#djopt-Action">djopt Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.28 DRC</h4>
+
+<!-- key DRC in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DRC()</pre>
+   </td></tr></table>
+
+   <p>Invoke the DRC check. 
+<!-- ./../src/action.c 1755 -->
+
+   <p>Note that the design rule check uses the current board rule settings,
+not the current style settings.
+
+<div class="node">
+<a name="DumpLibrary-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#elementlist-Action">elementlist Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#DRC-Action">DRC Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.29 DumpLibrary</h4>
+
+<!-- key DumpLibrary in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DumpLibrary()</pre>
+   </td></tr></table>
+
+   <p>Display the entire contents of the libraries. 
+<!-- ./../src/action.c 1791 -->
+
+<div class="node">
+<a name="elementlist-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#elementsetattr-Action">elementsetattr Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#DumpLibrary-Action">DumpLibrary Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.30 elementlist</h4>
+
+<!-- key elementlist in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)</pre>
+   </td></tr></table>
+
+   <p>Adds the given element if it doesn't already exist. 
+<!-- ./../src/action.c 5983 -->
+
+     <dl>
+<dt><code>Start</code><dd>Indicates the start of an element list; call this before any Need
+actions.
+
+     <br><dt><code>Need</code><dd>Searches the board for an element with a matching refdes.
+
+     <p>If found, the value and footprint are updated.
+
+     <p>If not found, a new element is created with the given footprint and value.
+
+     <br><dt><code>Done</code><dd>Compares the list of elements needed since the most recent
+<code>start</code> with the list of elements actually on the board.  Any
+elements that weren't listed are selected, so that the user may delete
+them.
+
+   </dl>
+
+<div class="node">
+<a name="elementsetattr-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#EnableVendor-Action">EnableVendor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#elementlist-Action">elementlist Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.31 elementsetattr</h4>
+
+<!-- key elementsetattr in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ElementSetAttr(refdes,name[,value])</pre>
+   </td></tr></table>
+
+   <p>Sets or clears an element-specific attribute. 
+<!-- ./../src/action.c 6176 -->
+
+   <p>If a value is specified, the named attribute is added (if not already
+present) or changed (if it is) to the given value.  If the value is
+not specified, the given attribute is removed if present.
+
+<div class="node">
+<a name="EnableVendor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#execcommand-Action">execcommand Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#elementsetattr-Action">elementsetattr Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.32 EnableVendor</h4>
+
+<!-- key EnableVendor in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">EnableVendor()</pre>
+   </td></tr></table>
+
+   <p>Enables automatic drill size mapping. 
+<!-- ./../src/vendor.c 146 -->
+
+   <p><a name="index-vendor-map-877"></a><a name="index-vendor-drill-table-878"></a><a name="index-EnableVendor_0028_0029-879"></a>
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.  To enable drill
+mapping, a vendor resource file containing a drill table must be
+loaded first.
+
+<div class="node">
+<a name="execcommand-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ExecuteFile-Action">ExecuteFile Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#EnableVendor-Action">EnableVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.33 execcommand</h4>
+
+<!-- key execcommand in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ExecCommand(command)</pre>
+   </td></tr></table>
+
+   <p>Runs a command. 
+<!-- ./../src/action.c 6234 -->
+
+   <p>Runs the given command, which is a system executable.
+
+<div class="node">
+<a name="ExecuteFile-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Flip-Action">Flip Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#execcommand-Action">execcommand Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.34 ExecuteFile</h4>
+
+<!-- key ExecuteFile in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ExecuteFile(filename)</pre>
+   </td></tr></table>
+
+   <p>Run actions from the given file. 
+<!-- ./../src/action.c 5861 -->
+
+   <p>Lines starting with <code>#</code> are ignored.
+
+<div class="node">
+<a name="Flip-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#FontEdit-Action">FontEdit Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ExecuteFile-Action">ExecuteFile Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.35 Flip</h4>
+
+<!-- key Flip in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Flip(Object|Selected|SelectedElements)</pre>
+   </td></tr></table>
+
+   <p>Flip an element to the opposite side of the board. 
+<!-- ./../src/action.c 1841 -->
+
+   <p>Note that the location of the element will be symmetric about the
+cursor location; i.e. if the part you are pointing at will still be at
+the same spot once the element is on the other side.  When flipping
+multiple elements, this retains their positions relative to each
+other, not their absolute positions on the board.
+
+<div class="node">
+<a name="FontEdit-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#FontSave-Action">FontSave Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Flip-Action">Flip Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.36 FontEdit</h4>
+
+<!-- key FontEdit in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">FontEdit()</pre>
+   </td></tr></table>
+
+   <p>Convert the current font to a PCB for editing. 
+<!-- ./../src/fontmode.c 73 -->
+
+<div class="node">
+<a name="FontSave-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#FreeRotateBuffer-Action">FreeRotateBuffer Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#FontEdit-Action">FontEdit Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.37 FontSave</h4>
+
+<!-- key FontSave in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">FontSave()</pre>
+   </td></tr></table>
+
+   <p>Convert the current PCB back to a font. 
+<!-- ./../src/fontmode.c 173 -->
+
+<div class="node">
+<a name="FreeRotateBuffer-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#GlobalPuller-Action">GlobalPuller Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#FontSave-Action">FontSave Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.38 FreeRotateBuffer</h4>
+
+<!-- key FreeRotateBuffer in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">FreeRotateBuffer([Angle])</pre>
+   </td></tr></table>
+
+   <p>Rotates the current paste buffer contents by the specified angle.  The
+angle is given in degrees.  If no angle is given, the user is prompted
+for one.
+
+<!-- ./../src/buffer.c 1370 -->
+   <p>Rotates the contents of the pastebuffer by an arbitrary angle.  If no
+angle is given, the user is prompted for one.
+
+<div class="node">
+<a name="GlobalPuller-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#h-Action">h Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#FreeRotateBuffer-Action">FreeRotateBuffer Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.39 GlobalPuller</h4>
+
+<!-- key GlobalPuller in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">GlobalPuller()</pre>
+   </td></tr></table>
+
+   <p>Pull all traces tight. 
+<!-- ./../src/puller.c 534 -->
+
+<div class="node">
+<a name="h-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Import-Action">Import Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#GlobalPuller-Action">GlobalPuller Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.40 h</h4>
+
+<!-- key h in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">h</pre>
+   </td></tr></table>
+
+   <p>Print a help message for commands. 
+<!-- ./../src/command.c 72 -->
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="Import-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#l-Action">l Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#h-Action">h Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.41 Import</h4>
+
+<!-- key Import in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Import()
+Import([gnetlist|make[,source,source,...]])
+Import(setnewpoint[,(mark|center|X,Y)])
+Import(setdisperse,D,units)
+</pre>
+   </td></tr></table>
+
+   <p>Import schematics. 
+<!-- ./../src/action.c 6464 -->
+
+   <p>Imports element and netlist data from the schematics (or some other
+source).  The first parameter, which is optional, is the mode.  If not
+specified, the <code>import::mode</code> attribute in the PCB is used. 
+<code>gnetlist</code> means gnetlist is used to obtain the information from
+the schematics.  <code>make</code> invokes <code>make</code>, assuming the user
+has a <code>Makefile</code> in the current directory.  The <code>Makefile</code>
+will be invoked with the following variables set:
+
+     <dl>
+<dt><code>PCB</code><dd>The name of the .pcb file
+
+     <br><dt><code>SRCLIST</code><dd>A space-separated list of source files
+
+     <br><dt><code>OUT</code><dd>The name of the file in which to put the command script, which may
+contain any <code>pcb-rnd</code> actions.  By default, this is a temporary file
+selected by <code>pcb-rnd</code>, but if you specify an <code>import::outfile</code>
+attribute, that file name is used instead (and not automatically
+deleted afterwards).
+
+   </dl>
+
+   <p>The target specified to be built is the first of these that apply:
+
+     <ul>
+<li>The target specified by an <code>import::target</code> attribute.
+
+     <li>The output file specified by an <code>import::outfile</code> attribute.
+
+     <li>If nothing else is specified, the target is <code>pcb_import</code>.
+
+   </ul>
+
+   <p>If you specify an <code>import::makefile</code> attribute, then "-f <that
+file>" will be added to the command line.
+
+   <p>If you specify the mode, you may also specify the source files
+(schematics).  If you do not specify any, the list of schematics is
+obtained by reading the <code>import::src</code><var>N</var> attributes (like
+<code>import::src0</code>, <code>import::src1</code>, etc).
+
+   <p>For compatibility with future extensions to the import file format,
+the generated file <em>must not</em> start with the two characters
+<code>#%</code>.
+
+   <p>If a temporary file is needed the <code>TMPDIR</code> environment variable
+is used to select its location.
+
+   <p>Note that the programs <code>gnetlist</code> and <code>make</code> may be
+overridden by the user via the <code>make-program</code> and <code>gnetlist</code>
+<code>pcb</code> settings (i.e. in <code>~/.pcb/settings</code> or on the command
+line).
+
+   <p>If <code>pcb-rnd</code> cannot determine which schematic(s) to import from, the GUI
+is called to let user choose (see <code>ImportGUI()</code>).
+
+   <p>Note that Import() doesn't delete anything - after an Import, elements
+which shouldn't be on the board are selected and may be removed once
+it's determined that the deletion is appropriate.
+
+   <p>If <code>Import()</code> is called with <code>setnewpoint</code>, then the location
+of new components can be specified.  This is where parts show up when
+they're added to the board.  The default is the center of the board.
+
+     <dl>
+<dt><code>Import(setnewpoint)</code><dd>
+Prompts the user to click on the board somewhere, uses that point.  If
+called by a hotkey, uses the current location of the crosshair.
+
+     <br><dt><code>Import(setnewpoint,mark)</code><dd>
+Uses the location of the mark.  If no mark is present, the point is
+not changed.
+
+     <br><dt><code>Import(setnewpoint,center)</code><dd>
+Resets the point to the center of the board.
+
+     <br><dt><code>Import(setnewpoint,X,Y,units)</code><dd>
+Sets the point to the specific coordinates given.  Example:
+<code>Import(setnewpoint,50,25,mm)</code>
+
+   </dl>
+
+   <p>Note that the X and Y locations are stored in attributes named
+<code>import::newX</code> and <code>import::newY</code> so you could change them
+manually if you wished.
+
+   <p>Calling <code>Import(setdisperse,D,units)</code> sets how much the newly
+placed elements are dispersed relative to the set point.  For example,
+<code>Import(setdisperse,10,mm)</code> will offset each part randomly up to
+10mm away from the point.  The default dispersion is 1/10th of the
+smallest board dimension.  Dispersion is saved in the
+<code>import::disperse</code> attribute.
+
+<div class="node">
+<a name="l-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#le-Action">le Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Import-Action">Import Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.42 l</h4>
+
+<!-- key l in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">l [name]</pre>
+   </td></tr></table>
+
+   <p>Loads layout data. 
+<!-- ./../src/command.c 99 -->
+
+   <p>Loads a new datafile (layout) and, if confirmed, overwrites any
+existing unsaved data.  The filename and the searchpath
+(<em>filePath</em>) are passed to the command defined by
+<em>fileCommand</em>.  If no filename is specified a file select box
+will popup.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="le-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#LoadFootprint-Action">LoadFootprint Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#l-Action">l Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.43 le</h4>
+
+<!-- key le in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">le [name]</pre>
+   </td></tr></table>
+
+   <p>Loads an element into the current buffer. 
+<!-- ./../src/command.c 129 -->
+
+   <p>The filename and the searchpath (<em>elementPath</em>) are passed to the
+command defined by <em>elementCommand</em>.  If no filename is specified
+a file select box will popup.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="LoadFootprint-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#LoadFrom-Action">LoadFrom Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#le-Action">le Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.44 LoadFootprint</h4>
+
+<!-- key LoadFootprint in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LoadFootprint(filename[,refdes,value])</pre>
+   </td></tr></table>
+
+   <p>Loads a single footprint by name. 
+<!-- ./../src/buffer.c 809 -->
+
+   <p>Loads a single footprint by name, rather than by reference or through
+the library.  If a refdes and value are specified, those are inserted
+into the footprint as well.  The footprint remains in the paste buffer.
+
+<div class="node">
+<a name="LoadFrom-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#LoadVendorFrom-Action">LoadVendorFrom Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#LoadFootprint-Action">LoadFootprint Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.45 LoadFrom</h4>
+
+<!-- key LoadFrom in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)</pre>
+   </td></tr></table>
+
+   <p>Load layout data from a file. 
+<!-- ./../src/action.c 5031 -->
+
+   <p>This action assumes you know what the filename is.  The various GUIs
+should have a similar <code>Load</code> action where the filename is
+optional, and will provide their own file selection mechanism to let
+you choose the file name.
+
+     <dl>
+<dt><code>Layout</code><dd>Loads an entire PCB layout, replacing the current one.
+
+     <br><dt><code>LayoutToBuffer</code><dd>Loads an entire PCB layout to the paste buffer.
+
+     <br><dt><code>ElementToBuffer</code><dd>Loads the given element file into the paste buffer.  Element files
+contain only a single <code>Element</code> definition, such as the
+“newlib” library uses.
+
+     <br><dt><code>Netlist</code><dd>Loads a new netlist, replacing any current netlist.
+
+     <br><dt><code>Revert</code><dd>Re-loads the current layout from its disk file, reverting any changes
+you may have made.
+
+   </dl>
+
+<div class="node">
+<a name="LoadVendorFrom-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#m-Action">m Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#LoadFrom-Action">LoadFrom Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.46 LoadVendorFrom</h4>
+
+<!-- key LoadVendorFrom in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LoadVendorFrom(filename)</pre>
+   </td></tr></table>
+
+   <p>Loads the specified vendor resource file. 
+<!-- ./../src/vendor.c 201 -->
+
+   <p><a name="index-vendor-map-880"></a><a name="index-vendor-drill-table-881"></a><a name="index-LoadVendorFrom_0028_0029-882"></a>
+     <dl>
+<dt><var>filename</var><dd>Name of the vendor resource file.  If not specified, the user will
+be prompted to enter one. 
+</dl>
+
+<div class="node">
+<a name="m-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MarkCrosshair-Action">MarkCrosshair Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#LoadVendorFrom-Action">LoadVendorFrom Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.47 m</h4>
+
+<!-- key m in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">m [name]</pre>
+   </td></tr></table>
+
+   <p>Loads a layout into the current buffer. 
+<!-- ./../src/command.c 157 -->
+
+   <p>The filename and the searchpath (<em>filePath</em>) are passed to the
+command defined by <em>fileCommand</em>. 
+If no filename is specified a file select box will popup.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="MarkCrosshair-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Message-Action">Message Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#m-Action">m Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.48 MarkCrosshair</h4>
+
+<!-- key MarkCrosshair in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MarkCrosshair()
+MarkCrosshair(Center)</pre>
+   </td></tr></table>
+
+   <p>Set/Reset the Crosshair mark. 
+<!-- ./../src/action.c 3502 -->
+
+   <p>The “mark” is a small X-shaped target on the display which is
+treated like a second origin (the normal origin is the upper let
+corner of the board).  The GUI will display a second set of
+coordinates for this mark, which tells you how far you are from it.
+
+   <p>If no argument is given, the mark is toggled - disabled if it was
+enabled, or enabled at the current cursor position of disabled.  If
+the <code>Center</code> argument is given, the mark is moved to the current
+cursor location.
+
+<div class="node">
+<a name="Message-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MinClearGap-Action">MinClearGap Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MarkCrosshair-Action">MarkCrosshair Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.49 Message</h4>
+
+<!-- key Message in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Message(message)</pre>
+   </td></tr></table>
+
+   <p>Writes a message to the log window. 
+<!-- ./../src/action.c 1886 -->
+
+   <p>This action displays a message to the log window.  This action is primarily
+provided for use by other programs which may interface with PCB.  If
+multiple arguments are given, each one is sent to the log window
+followed by a newline.
+
+<div class="node">
+<a name="MinClearGap-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MinMaskGap-Action">MinMaskGap Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Message-Action">Message Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.50 MinClearGap</h4>
+
+<!-- key MinClearGap in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MinClearGap(delta)
+MinClearGap(Selected, delta)</pre>
+   </td></tr></table>
+
+   <p>Ensures that polygons are a minimum distance from objects. 
+<!-- ./../src/action.c 3827 -->
+
+   <p>Checks all specified objects, and increases the polygon clearance if
+needed to ensure a minimum distance between their edges and the
+polygon edges.
+
+<div class="node">
+<a name="MinMaskGap-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Mode-Action">Mode Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MinClearGap-Action">MinClearGap Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.51 MinMaskGap</h4>
+
+<!-- key MinMaskGap in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MinMaskGap(delta)
+MinMaskGap(Selected, delta)</pre>
+   </td></tr></table>
+
+   <p>Ensures the mask is a minimum distance from pins and pads. 
+<!-- ./../src/action.c 3752 -->
+
+   <p>Checks all specified pins and/or pads, and increases the mask if
+needed to ensure a minimum distance between the pin or pad edge and
+the mask edge.
+
+<div class="node">
+<a name="Mode-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MorphPolygon-Action">MorphPolygon Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MinMaskGap-Action">MinMaskGap Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.52 Mode</h4>
+
+<!-- key Mode in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)
+Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)
+Mode(Notify|Release|Cancel|Stroke)
+Mode(Save|Restore)</pre>
+   </td></tr></table>
+
+   <p>Change or use the tool mode. 
+<!-- ./../src/action.c 2612 -->
+
+     <dl>
+<dt><code>Arc</code><dt><code>Arrow</code><dt><code>Copy</code><dt><code>InsertPoint</code><dt><code>Line</code><dt><code>Lock</code><dt><code>Move</code><dt><code>None</code><dt><code>PasteBuffer</code><dt><code>Polygon</code><dt><code>Rectangle</code><dt><code>Remove</code><dt><code>Rotate</code><dt><code>Text</code><dt><code>Thermal</code><dt><code>Via</code><dd>Select the indicated tool.
+
+     <br><dt><code>Notify</code><dd>Called when you press the mouse button, or move the mouse.
+
+     <br><dt><code>Release</code><dd>Called when you release the mouse button.
+
+     <br><dt><code>Cancel</code><dd>Cancels any pending tool activity, allowing you to restart elsewhere. 
+For example, this allows you to start a new line rather than attach a
+line to the previous line.
+
+     <br><dt><code>Escape</code><dd>Similar to Cancel but calling this action a second time will return
+to the Arrow tool.
+
+     <br><dt><code>Stroke</code><dd>If your <code>pcb</code> was built with libstroke, this invokes the stroke
+input method.  If not, this will restart a drawing mode if you were
+drawing, else it will select objects.
+
+     <br><dt><code>Save</code><dd>Remembers the current tool.
+
+     <br><dt><code>Restore</code><dd>Restores the tool to the last saved tool.
+
+   </dl>
+
+<div class="node">
+<a name="MorphPolygon-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MoveLayer-Action">MoveLayer Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Mode-Action">Mode Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.53 MorphPolygon</h4>
+
+<!-- key MorphPolygon in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MorphPolygon(Object|Selected)</pre>
+   </td></tr></table>
+
+   <p>Converts dead polygon islands into separate polygons. 
+<!-- ./../src/action.c 4087 -->
+
+   <p>If a polygon is divided into unconnected "islands", you can use
+this command to convert the otherwise disappeared islands into
+separate polygons. Be sure the cursor is over a portion of the
+polygon that remains visible. Very small islands that may flake
+off are automatically deleted.
+
+<div class="node">
+<a name="MoveLayer-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MoveObject-Action">MoveObject Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MorphPolygon-Action">MorphPolygon Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.54 MoveLayer</h4>
+
+<!-- key MoveLayer in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MoveLayer(old,new)</pre>
+   </td></tr></table>
+
+   <p>Moves/Creates/Deletes Layers. 
+<!-- ./../src/move.c 1091 -->
+
+   <p>Moves a layer, creates a new layer, or deletes a layer.
+
+     <dl>
+<dt><code>old</code><dd>The is the layer number to act upon.  Allowed values are:
+          <dl>
+<dt><code>c</code><dd>Currently selected layer.
+
+          <br><dt><code>-1</code><dd>Create a new layer.
+
+          <br><dt><code>number</code><dd>An existing layer number.
+
+     </dl>
+
+     <br><dt><code>new</code><dd>Specifies where to move the layer to.  Allowed values are:
+          <dl>
+<dt><code>-1</code><dd>Deletes the layer.
+
+          <br><dt><code>up</code><dd>Moves the layer up.
+
+          <br><dt><code>down</code><dd>Moves the layer down.
+
+          <br><dt><code>c</code><dd>Creates a new layer.
+
+     </dl>
+
+   </dl>
+
+<div class="node">
+<a name="MoveObject-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#MoveToCurrentLayer-Action">MoveToCurrentLayer Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MoveLayer-Action">MoveLayer Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.55 MoveObject</h4>
+
+<!-- key MoveObject in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MoveObject(X,Y,dim)</pre>
+   </td></tr></table>
+
+   <p>Moves the object under the crosshair. 
+<!-- ./../src/action.c 5567 -->
+
+   <p>The <code>X</code> and <code>Y</code> are treated like <code>delta</code> is for many
+other objects.  For each, if it's prefixed by <code>+</code> or <code>-</code>,
+then that amount is relative.  Otherwise, it's absolute.  Units can be
+<code>mil</code> or <code>mm</code>; if unspecified, units are PCB's internal
+units, currently 1/100 mil.
+
+<div class="node">
+<a name="MoveToCurrentLayer-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Netlist-Action">Netlist Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MoveObject-Action">MoveObject Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.56 MoveToCurrentLayer</h4>
+
+<!-- key MoveToCurrentLayer in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">MoveToCurrentLayer(Object|SelectedObjects)</pre>
+   </td></tr></table>
+
+   <p>Moves objects to the current layer. 
+<!-- ./../src/action.c 5609 -->
+
+   <p>Note that moving an element from a component layer to a solder layer,
+or from solder to component, won't automatically flip it.  Use the
+<code>Flip()</code> action to do that.
+
+<div class="node">
+<a name="Netlist-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#New-Action">New Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#MoveToCurrentLayer-Action">MoveToCurrentLayer Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.57 Netlist</h4>
+
+<!-- key Netlist in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Net(find|select|rats|norats|clear[,net[,pin]])
+Net(freeze|thaw|forcethaw)
+Net(add,net,pin)</pre>
+   </td></tr></table>
+
+   <p>Perform various actions on netlists. 
+<!-- ./../src/netlist.c 289 -->
+
+   <p>Each of these actions apply to a specified set of nets.  <var>net</var> and
+<var>pin</var> are patterns which match one or more nets or pins; these
+patterns may be full names or regular expressions.  If an exact match
+is found, it is the only match; if no exact match is found,
+<em>then</em> the pattern is tried as a regular expression.
+
+   <p>If neither <var>net</var> nor <var>pin</var> are specified, all nets apply.  If
+<var>net</var> is specified but not <var>pin</var>, all nets matching <var>net</var>
+apply.  If both are specified, nets which match <var>net</var> and contain
+a pin matching <var>pin</var> apply.
+
+     <dl>
+<dt><code>find</code><dd>Nets which apply are marked <em>found</em> and are drawn in the
+<code>connected-color</code> color.
+
+     <br><dt><code>select</code><dd>Nets which apply are selected.
+
+     <br><dt><code>rats</code><dd>Nets which apply are marked as available for the rats nest.
+
+     <br><dt><code>norats</code><dd>Nets which apply are marked as not available for the rats nest.
+
+     <br><dt><code>clear</code><dd>Clears the netlist.
+
+     <br><dt><code>add</code><dd>Add the given pin to the given netlist, creating either if needed.
+
+     <br><dt><code>sort</code><dd>Called after a list of add's, this sorts the netlist.
+
+     <br><dt><code>freeze</code><dt><code>thaw</code><dt><code>forcethaw</code><dd>Temporarily prevents changes to the netlist from being reflected in
+the GUI.  For example, if you need to make multiple changes, you
+freeze the netlist, make the changes, then thaw it.  Note that
+freeze/thaw requests may nest, with the netlist being fully thawed
+only when all pending freezes are thawed.  You can bypass the nesting
+by using forcethaw, which resets the freeze count and immediately
+updates the GUI.
+
+   </dl>
+
+<div class="node">
+<a name="New-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#OptAutoOnly-Action">OptAutoOnly Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Netlist-Action">Netlist Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.58 New</h4>
+
+<!-- key New in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">New([name])</pre>
+   </td></tr></table>
+
+   <p>Starts a new layout. 
+<!-- ./../src/action.c 5093 -->
+
+   <p>If a name is not given, one is prompted for.
+
+<div class="node">
+<a name="OptAutoOnly-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#PasteBuffer-Action">PasteBuffer Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#New-Action">New Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.59 OptAutoOnly</h4>
+
+<!-- key OptAutoOnly in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">OptAutoOnly()</pre>
+   </td></tr></table>
+
+   <p>Toggles the optimize-only-autorouted flag. 
+<!-- ./../src/djopt.c 129 -->
+
+   <p>The original purpose of the trace optimizer was to clean up the traces
+created by the various autorouters that have been used with PCB.  When
+a board has a mix of autorouted and carefully hand-routed traces, you
+don't normally want the optimizer to move your hand-routed traces. 
+But, sometimes you do.  By default, the optimizer only optimizes
+autorouted traces.  This action toggles that setting, so that you can
+optimize hand-routed traces also.
+
+<div class="node">
+<a name="PasteBuffer-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Polygon-Action">Polygon Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#OptAutoOnly-Action">OptAutoOnly Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.60 PasteBuffer</h4>
+
+<!-- key PasteBuffer in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)
+PasteBuffer(Rotate, 1..3)
+PasteBuffer(Convert|Save|Restore|Mirror)
+PasteBuffer(ToLayout, X, Y, units)</pre>
+   </td></tr></table>
+
+   <p>Various operations on the paste buffer. 
+<!-- ./../src/action.c 5153 -->
+
+   <p>There are a number of paste buffers; the actual limit is a
+compile-time constant <code>MAX_BUFFER</code> in <samp><span class="file">globalconst.h</span></samp>.  It
+is currently <code>5</code>.  One of these is the “current” paste buffer,
+often referred to as “the” paste buffer.
+
+     <dl>
+<dt><code>AddSelected</code><dd>Copies the selected objects to the current paste buffer.
+
+     <br><dt><code>Clear</code><dd>Remove all objects from the current paste buffer.
+
+     <br><dt><code>Convert</code><dd>Convert the current paste buffer to an element.  Vias are converted to
+pins, lines are converted to pads.
+
+     <br><dt><code>Restore</code><dd>Convert any elements in the paste buffer back to vias and lines.
+
+     <br><dt><code>Mirror</code><dd>Flip all objects in the paste buffer vertically (up/down flip).  To mirror
+horizontally, combine this with rotations.
+
+     <br><dt><code>Rotate</code><dd>Rotates the current buffer.  The number to pass is 1..3, where 1 means
+90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
+degrees clockwise (270 CCW).
+
+     <br><dt><code>Save</code><dd>Saves any elements in the current buffer to the indicated file.
+
+     <br><dt><code>ToLayout</code><dd>Pastes any elements in the current buffer to the indicated X, Y
+coordinates in the layout.  The <code>X</code> and <code>Y</code> are treated like
+<code>delta</code> is for many other objects.  For each, if it's prefixed by
+<code>+</code> or <code>-</code>, then that amount is relative to the last
+location.  Otherwise, it's absolute.  Units can be
+<code>mil</code> or <code>mm</code>; if unspecified, units are PCB's internal
+units, currently 1/100 mil.
+
+     <br><dt><code>1..MAX_BUFFER</code><dd>Selects the given buffer to be the current paste buffer.
+
+   </dl>
+
+<div class="node">
+<a name="Polygon-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Puller-Action">Puller Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#PasteBuffer-Action">PasteBuffer Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.61 Polygon</h4>
+
+<!-- key Polygon in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Polygon(Close|PreviousPoint)</pre>
+   </td></tr></table>
+
+   <p>Some polygon related stuff. 
+<!-- ./../src/action.c 5503 -->
+
+   <p>Polygons need a special action routine to make life easier.
+
+     <dl>
+<dt><code>Close</code><dd>Creates the final segment of the polygon.  This may fail if clipping
+to 45 degree lines is switched on, in which case a warning is issued.
+
+     <br><dt><code>PreviousPoint</code><dd>Resets the newly entered corner to the previous one. The Undo action
+will call Polygon(PreviousPoint) when appropriate to do so.
+
+   </dl>
+
+<div class="node">
+<a name="Puller-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#q-Action">q Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Polygon-Action">Polygon Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.62 Puller</h4>
+
+<!-- key Puller in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Puller()</pre>
+   </td></tr></table>
+
+   <p>Pull an arc-line junction tight. 
+<!-- ./../src/puller.c 411 -->
+
+   <p>The <code>Puller()</code> action is a special-purpose optimization.  When
+invoked while the crosshair is over the junction of an arc and a line,
+it will adjust the arc's angle and the connecting line's endpoint such
+that the line intersects the arc at a tangent.  In the example below,
+the left side is “before” with the black target marking where to put
+the crosshair:
+
+<div align="center"><img src="puller.png" alt="Example of how puller works"></div>
+
+   <p>The right side is “after” with the black target marking where the
+arc-line intersection was moved to.
+
+<div class="node">
+<a name="q-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#q_0021-Action">q! Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Puller-Action">Puller Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.63 q</h4>
+
+<!-- key q in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">q</pre>
+   </td></tr></table>
+
+   <p>Quits the application after confirming. 
+<!-- ./../src/command.c 185 -->
+
+   <p>If you have unsaved changes, you will be prompted to confirm (or
+save) before quitting.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="q!-Action"></a>
+<a name="q_0021-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Quit-Action">Quit Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#q-Action">q Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.64 q!</h4>
+
+<!-- key q! in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">q!</pre>
+   </td></tr></table>
+
+   <p>Quits the application without confirming. 
+<!-- ./../src/command.c 199 -->
+
+   <p>Note that this command neither saves your data nor prompts for
+confirmation.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="Quit-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Redo-Action">Redo Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#q_0021-Action">q! Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.65 Quit</h4>
+
+<!-- key Quit in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Quit()</pre>
+   </td></tr></table>
+
+   <p>Quits the application after confirming. 
+<!-- ./../src/action.c 2071 -->
+
+   <p>If you have unsaved changes, you will be prompted to confirm (or
+save) before quitting.
+
+<div class="node">
+<a name="Redo-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#RemoveSelected-Action">RemoveSelected Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Quit-Action">Quit Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.66 Redo</h4>
+
+<!-- key Redo in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Redo()</pre>
+   </td></tr></table>
+
+   <p>Redo recent“undo”operations. 
+<!-- ./../src/action.c 5468 -->
+
+   <p>This routine allows you to recover from the last undo command.  You
+might want to do this if you thought that undo was going to revert
+something other than what it actually did (in case you are confused
+about which operations are un-doable), or if you have been backing up
+through a long undo list and over-shoot your stopping point.  Any
+change that is made since the undo in question will trim the redo
+list.  For example if you add ten lines, then undo three of them you
+could use redo to put them back, but if you move a line on the board
+before performing the redo, you will lose the ability to "redo" the
+three "undone" lines.
+
+<div class="node">
+<a name="RemoveSelected-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Renumber-Action">Renumber Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Redo-Action">Redo Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.67 RemoveSelected</h4>
+
+<!-- key RemoveSelected in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">RemoveSelected()</pre>
+   </td></tr></table>
+
+   <p>Removes any selected objects. 
+<!-- ./../src/action.c 2832 -->
+
+<div class="node">
+<a name="Renumber-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Report-Action">Report Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#RemoveSelected-Action">RemoveSelected Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.68 Renumber</h4>
+
+<!-- key Renumber in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Renumber()
+Renumber(filename)</pre>
+   </td></tr></table>
+
+   <p>Renumber all elements.  The changes will be recorded to filename
+for use in backannotating these changes to the schematic. 
+<!-- ./../src/action.c 2848 -->
+
+<div class="node">
+<a name="Report-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ReportDialog-Action">ReportDialog Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Renumber-Action">Renumber Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.69 Report</h4>
+
+<!-- key Report in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])</pre>
+   </td></tr></table>
+
+   <p>Produce various report. 
+<!-- ./../src/report.c 942 -->
+
+     <dl>
+<dt><code>Object</code><dd>The object under the crosshair will be reported, describing various
+aspects of the object.
+
+     <br><dt><code>DrillReport</code><dd>A report summarizing the number of drill sizes used, and how many of
+each, will be produced.
+
+     <br><dt><code>FoundPins</code><dd>A report listing all pins and pads which are marked as “found” will
+be produced.
+
+     <br><dt><code>NetLength</code><dd>The name and length of the net under the crosshair will be reported to
+the message log.
+
+     <br><dt><code>AllNetLengths</code><dd>The name and length of the net under the crosshair will be reported to
+the message log.  An optional parameter specifies mm, mil, pcb, or in
+units
+
+   </dl>
+
+<div class="node">
+<a name="ReportDialog-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#RipUp-Action">RipUp Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Report-Action">Report Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.70 ReportDialog</h4>
+
+<!-- key ReportDialog in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ReportDialog()</pre>
+   </td></tr></table>
+
+   <p>Report on the object under the crosshair
+<!-- ./../src/report.c 125 -->
+
+   <p>This is a shortcut for <code>Report(Object)</code>.
+
+<div class="node">
+<a name="RipUp-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#rn-Action">rn Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ReportDialog-Action">ReportDialog Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.71 RipUp</h4>
+
+<!-- key RipUp in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">RipUp(All|Selected|Element)</pre>
+   </td></tr></table>
+
+   <p>Ripup auto-routed tracks, or convert an element to parts. 
+<!-- ./../src/action.c 3199 -->
+
+     <dl>
+<dt><code>All</code><dd>Removes all lines and vias which were created by the autorouter.
+
+     <br><dt><code>Selected</code><dd>Removes all selected lines and vias which were created by the
+autorouter.
+
+     <br><dt><code>Element</code><dd>Converts the element under the cursor to parts (vias and lines).  Note
+that this uses the highest numbered paste buffer.
+
+   </dl>
+
+<div class="node">
+<a name="rn-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#RouteStyle-Action">RouteStyle Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#RipUp-Action">RipUp Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.72 rn</h4>
+
+<!-- key rn in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">rn [name]</pre>
+   </td></tr></table>
+
+   <p>Reads netlist. 
+<!-- ./../src/command.c 214 -->
+
+   <p>If no filename is given a file select box will pop up.  The file is
+read via the command defined by the <em>RatCommand</em> resource. The
+command must send its output to <em>stdout</em>.
+
+   <p>Netlists are used for generating rat's nests (see <a href="#Rats-Nest">Rats Nest</a>) and
+for verifying the board layout (which is also accomplished by the
+<em>Ratsnest</em> command).
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="RouteStyle-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#s-Action">s Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#rn-Action">rn Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.73 RouteStyle</h4>
+
+<!-- key RouteStyle in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">RouteStyle(1|2|3|4)</pre>
+   </td></tr></table>
+
+   <p>Copies the indicated routing style into the current sizes. 
+<!-- ./../src/action.c 5535 -->
+
+<div class="node">
+<a name="s-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SaveSettings-Action">SaveSettings Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#RouteStyle-Action">RouteStyle Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.74 s</h4>
+
+<!-- key s in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">s [name]</pre>
+   </td></tr></table>
+
+   <p>Saves layout data. 
+<!-- ./../src/command.c 244 -->
+
+   <p>Data and the filename are passed to the command defined by the
+resource <em>saveCommand</em>. It must read the layout data from
+<em>stdin</em>.  If no filename is entered, either the last one is used
+again or, if it is not available, a file select box will pop up.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="SaveSettings-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SaveTo-Action">SaveTo Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#s-Action">s Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.75 SaveSettings</h4>
+
+<!-- key SaveSettings in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SaveSettings()
+SaveSettings(local)</pre>
+   </td></tr></table>
+
+   <p>Saves settings. 
+<!-- ./../src/action.c 5015 -->
+
+   <p>If you pass no arguments, the settings are stored in
+<code>$HOME/.pcb/settings</code>.  If you pass the word <code>local</code> they're
+saved in <code>./pcb.settings</code>.
+
+<div class="node">
+<a name="SaveTo-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Select-Action">Select Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SaveSettings-Action">SaveSettings Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.76 SaveTo</h4>
+
+<!-- key SaveTo in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SaveTo(Layout|LayoutAs,filename)
+SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)
+SaveTo(PasteBuffer,filename)</pre>
+   </td></tr></table>
+
+   <p>Saves data to a file. 
+<!-- ./../src/action.c 4919 -->
+
+     <dl>
+<dt><code>Layout</code><dd>Saves the current layout.
+
+     <br><dt><code>LayoutAs</code><dd>Saves the current layout, and remembers the filename used.
+
+     <br><dt><code>AllConnections</code><dd>Save all connections to a file.
+
+     <br><dt><code>AllUnusedPins</code><dd>List all unused pins to a file.
+
+     <br><dt><code>ElementConnections</code><dd>Save connections to the element at the cursor to a file.
+
+     <br><dt><code>PasteBuffer</code><dd>Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
+
+   </dl>
+
+<div class="node">
+<a name="Select-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SetFlag-Action">SetFlag Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SaveTo-Action">SaveTo Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.77 Select</h4>
+
+<!-- key Select in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Select(Object|ToggleObject)
+Select(All|Block|Connection)
+Select(ElementByName|ObjectByName|PadByName|PinByName)
+Select(ElementByName|ObjectByName|PadByName|PinByName, Name)
+Select(TextByName|ViaByName|NetByName)
+Select(TextByName|ViaByName|NetByName, Name)
+Select(Convert)</pre>
+   </td></tr></table>
+
+   <p>Toggles or sets the selection. 
+<!-- ./../src/action.c 4651 -->
+
+     <dl>
+<dt><code>ElementByName</code><br><dt><code>ObjectByName</code><br><dt><code>PadByName</code><br><dt><code>PinByName</code><br><dt><code>TextByName</code><br><dt><code>ViaByName</code><br><dt><code>NetByName</code><dd>
+These all rely on having a regular expression parser built into
+<code>pcb</code>.  If the name is not specified then the user is prompted
+for a pattern, and all objects that match the pattern and are of the
+type specified are selected.
+
+     <br><dt><code>Object</code><br><dt><code>ToggleObject</code><dd>Selects the object under the cursor.
+
+     <br><dt><code>Block</code><dd>Selects all objects in a rectangle indicated by the cursor.
+
+     <br><dt><code>All</code><dd>Selects all objects on the board.
+
+     <br><dt><code>Connection</code><dd>Selects all connections with the “found” flag set.
+
+     <br><dt><code>Convert</code><dd>Converts the selected objects to an element.  This uses the highest
+numbered paste buffer.
+
+   </dl>
+
+<div class="node">
+<a name="SetFlag-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SetOctagon-Action">SetOctagon Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Select-Action">Select Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.78 SetFlag</h4>
+
+<!-- key SetFlag in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetFlag(Object|Selected|SelectedObjects, flag)
+SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)
+SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)
+SetFlag(SelectedElements, flag)
+flag = square | octagon | thermal | join</pre>
+   </td></tr></table>
+
+   <p>Sets flags on objects. 
+<!-- ./../src/action.c 5716 -->
+
+   <p>Turns the given flag on, regardless of its previous setting.  See
+<code>ChangeFlag</code>.
+
+<pre class="example">     SetFlag(SelectedPins,thermal)
+</pre>
+   <div class="node">
+<a name="SetOctagon-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SetSame-Action">SetSame Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SetFlag-Action">SetFlag Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.79 SetOctagon</h4>
+
+<!-- key SetOctagon in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetOctagon(Object|ToggleObject|SelectedElements|Selected)</pre>
+   </td></tr></table>
+
+   <p>Sets the octagon-flag of objects. 
+<!-- ./../src/action.c 4459 -->
+
+   <p>Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+   <p>Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+<div class="node">
+<a name="SetSame-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SetSquare-Action">SetSquare Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SetOctagon-Action">SetOctagon Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.80 SetSame</h4>
+
+<!-- key SetSame in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetSame()</pre>
+   </td></tr></table>
+
+   <p>Sets current layer and sizes to match indicated item. 
+<!-- ./../src/action.c 5648 -->
+
+   <p>When invoked over any line, arc, polygon, or via, this changes the
+current layer to be the layer that item is on, and changes the current
+sizes (thickness, keepaway, drill, etc) according to that item.
+
+<div class="node">
+<a name="SetSquare-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SetThermal-Action">SetThermal Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SetSame-Action">SetSame Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.81 SetSquare</h4>
+
+<!-- key SetSquare in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetSquare(ToggleObject|SelectedElements|SelectedPins)</pre>
+   </td></tr></table>
+
+   <p>sets the square-flag of objects. 
+<!-- ./../src/action.c 4301 -->
+
+   <p>Note that <code>Pins</code> means pins and pads.
+
+   <p>Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+   <p>Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+<div class="node">
+<a name="SetThermal-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#SetValue-Action">SetValue Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SetSquare-Action">SetSquare Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.82 SetThermal</h4>
+
+<!-- key SetThermal in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)</pre>
+   </td></tr></table>
+
+   <p>Set the thermal (on the current layer) of pins or vias to the given style. 
+Style = 0 means no thermal. 
+Style = 1 has diagonal fingers with sharp edges. 
+Style = 2 has horizontal and vertical fingers with sharp edges. 
+Style = 3 is a solid connection to the plane.Style = 4 has diagonal fingers with rounded edges. 
+Style = 5 has horizontal and vertical fingers with rounded edges.
+
+<!-- ./../src/action.c 1912 -->
+   <p>This changes how/whether pins or vias connect to any rectangle or polygon
+on the current layer. The first argument can specify one object, or all
+selected pins, or all selected vias, or all selected pins and vias. 
+The second argument specifies the style of connection. 
+There are 5 possibilities:
+0 - no connection,
+1 - 45 degree fingers with sharp edges,
+2 - horizontal & vertical fingers with sharp edges,
+3 - solid connection,
+4 - 45 degree fingers with rounded corners,
+5 - horizontal & vertical fingers with rounded corners.
+
+   <p>Pins and Vias may have thermals whether or not there is a polygon available
+to connect with. However, they will have no effect without the polygon.
+
+<div class="node">
+<a name="SetValue-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ToggleHideName-Action">ToggleHideName Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SetThermal-Action">SetThermal Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.83 SetValue</h4>
+
+<!-- key SetValue in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)</pre>
+   </td></tr></table>
+
+   <p>Change various board-wide values and sizes. 
+<!-- ./../src/action.c 1996 -->
+
+     <dl>
+<dt><code>ViaDrillingHole</code><dd>Changes the diameter of the drill for new vias.
+
+     <br><dt><code>Grid</code><dd>Sets the grid spacing.
+
+     <br><dt><code>Line</code><br><dt><code>LineSize</code><dd>Changes the thickness of new lines.
+
+     <br><dt><code>Via</code><br><dt><code>ViaSize</code><dd>Changes the diameter of new vias.
+
+     <br><dt><code>Text</code><br><dt><code>TextScale</code><dd>Changes the size of new text.
+
+   </dl>
+
+<div class="node">
+<a name="ToggleHideName-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#ToggleVendor-Action">ToggleVendor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#SetValue-Action">SetValue Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.84 ToggleHideName</h4>
+
+<!-- key ToggleHideName in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ToggleHideName(Object|SelectedElements)</pre>
+   </td></tr></table>
+
+   <p>Toggles the visibility of element names. 
+<!-- ./../src/action.c 4134 -->
+
+   <p>If names are hidden you won't see them on the screen and they will not
+appear on the silk layer when you print the layout.
+
+<div class="node">
+<a name="ToggleVendor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Undo-Action">Undo Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ToggleHideName-Action">ToggleHideName Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.85 ToggleVendor</h4>
+
+<!-- key ToggleVendor in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ToggleVendor()</pre>
+   </td></tr></table>
+
+   <p>Toggles the state of automatic drill size mapping. 
+<!-- ./../src/vendor.c 128 -->
+
+   <p><a name="index-vendor-map-883"></a><a name="index-vendor-drill-table-884"></a><a name="index-ToggleVendor_0028_0029-885"></a>
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.  To enable drill
+mapping, a vendor resource file containing a drill table must be
+loaded first.
+
+<div class="node">
+<a name="Undo-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#UnloadVendor-Action">UnloadVendor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#ToggleVendor-Action">ToggleVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.86 Undo</h4>
+
+<!-- key Undo in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Undo()
+Undo(ClearList)</pre>
+   </td></tr></table>
+
+   <p>Undo recent changes. 
+<!-- ./../src/action.c 5304 -->
+
+   <p>The unlimited undo feature of <code>pcb-rnd</code> allows you to recover from
+most operations that materially affect you work.  Calling
+<code>Undo()</code> without any parameter recovers from the last (non-undo)
+operation. <code>ClearList</code> is used to release the allocated
+memory. <code>ClearList</code> is called whenever a new layout is started or
+loaded. See also <code>Redo</code> and <code>Atomic</code>.
+
+   <p>Note that undo groups operations by serial number; changes with the
+same serial number will be undone (or redone) as a group.  See
+<code>Atomic</code>.
+
+<div class="node">
+<a name="UnloadVendor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Unselect-Action">Unselect Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Undo-Action">Undo Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.87 UnloadVendor</h4>
+
+<!-- key UnloadVendor in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">UnloadVendor()</pre>
+   </td></tr></table>
+
+   <p>Unloads the current vendor drill mapping table. 
+<!-- ./../src/vendor.c 176 -->
+
+   <p><a name="index-vendor-map-886"></a><a name="index-vendor-drill-table-887"></a><a name="index-UnloadVendor_0028_0029-888"></a>
+
+<div class="node">
+<a name="Unselect-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#w-Action">w Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#UnloadVendor-Action">UnloadVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.88 Unselect</h4>
+
+<!-- key Unselect in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Unselect(All|Block|Connection)
+Unselect(ElementByName|ObjectByName|PadByName|PinByName)
+Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)
+Unselect(TextByName|ViaByName)
+Unselect(TextByName|ViaByName, Name)
+</pre>
+   </td></tr></table>
+
+   <p>Unselects the object at the pointer location or the specified objects. 
+<!-- ./../src/action.c 4803 -->
+
+     <dl>
+<dt><code>All</code><dd>Unselect all objects.
+
+     <br><dt><code>Block</code><dd>Unselect all objects in a rectangle given by the cursor.
+
+     <br><dt><code>Connection</code><dd>Unselect all connections with the “found” flag set.
+
+     <br><dt><code>ElementByName</code><br><dt><code>ObjectByName</code><br><dt><code>PadByName</code><br><dt><code>PinByName</code><br><dt><code>TextByName</code><br><dt><code>ViaByName</code><dd>
+These all rely on having a regular expression parser built into
+<code>pcb</code>.  If the name is not specified then the user is prompted
+for a pattern, and all objects that match the pattern and are of the
+type specified are unselected.
+
+   </dl>
+
+<div class="node">
+<a name="w-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#wq-Action">wq Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#Unselect-Action">Unselect Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.89 w</h4>
+
+<!-- key w in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">w [name]</pre>
+   </td></tr></table>
+
+   <p>Saves layout data. 
+<!-- ./../src/command.c 250 -->
+
+   <p>This commands has been added for the convenience of <code>vi</code> users
+and has the same functionality as <code>s</code>.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="wq-Action"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#w-Action">w Action</a>,
+Up: <a rel="up" accesskey="u" href="#core-actions">core actions</a>
+
+</div>
+
+<h4 class="subsection">F.1.90 wq</h4>
+
+<!-- key wq in hid -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">wq</pre>
+   </td></tr></table>
+
+   <p>Saves the layout data and quits. 
+<!-- ./../src/command.c 291 -->
+
+   <p>This command has been added for the convenience of <code>vi</code> users and
+has the same functionality as <code>s</code> combined with <code>q</code>.
+
+   <p>This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (<code>:</code>) and thus the syntax
+is documented for that purpose.
+
+<div class="node">
+<a name="common-actions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-actions">gtk actions</a>,
+Previous: <a rel="previous" accesskey="p" href="#core-actions">core actions</a>,
+Up: <a rel="up" accesskey="u" href="#Action-Reference">Action Reference</a>
+
+</div>
+
+<h3 class="section">F.2 common actions</h3>
+
+<!-- ./../src/hid/common/actions.c 425 -->
+<ul class="menu">
+<li><a accesskey="1" href="#LayersChanged-Action">LayersChanged Action</a>:  Tells the GUI that the layers have changed. 
+<li><a accesskey="2" href="#LibraryChanged-Action">LibraryChanged Action</a>:  Tells the GUI that the libraries have changed. 
+<li><a accesskey="3" href="#NetlistChanged-Action">NetlistChanged Action</a>:  Tells the GUI that the netlist has changed. 
+<li><a accesskey="4" href="#PCBChanged-Action">PCBChanged Action</a>:  Tells the GUI that the whole PCB has changed. The optional``revert"parameter can be used as a hint to the GUI that the same design is beingreloaded, and that it might keep some viewport settings
+<li><a accesskey="5" href="#RouteStylesChanged-Action">RouteStylesChanged Action</a>:  Tells the GUI that the routing styles have changed. 
+</ul>
+<div class="node">
+<a name="LayersChanged-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#LibraryChanged-Action">LibraryChanged Action</a>,
+Up: <a rel="up" accesskey="u" href="#common-actions">common actions</a>
+
+</div>
+
+<h4 class="subsection">F.2.1 LayersChanged</h4>
+
+<!-- key LayersChanged in hid common -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LayersChanged()</pre>
+   </td></tr></table>
+
+   <p>Tells the GUI that the layers have changed. 
+<!-- ./../src/hid/common/actions.c 446 -->
+
+   <p>This includes layer names, colors, stacking order, visibility, etc.
+
+   <p>This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+<div class="node">
+<a name="LibraryChanged-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#NetlistChanged-Action">NetlistChanged Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#LayersChanged-Action">LayersChanged Action</a>,
+Up: <a rel="up" accesskey="u" href="#common-actions">common actions</a>
+
+</div>
+
+<h4 class="subsection">F.2.2 LibraryChanged</h4>
+
+<!-- key LibraryChanged in hid common -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LibraryChanged()</pre>
+   </td></tr></table>
+
+   <p>Tells the GUI that the libraries have changed. 
+<!-- ./../src/hid/common/actions.c 451 -->
+
+   <p>This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+<div class="node">
+<a name="NetlistChanged-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#PCBChanged-Action">PCBChanged Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#LibraryChanged-Action">LibraryChanged Action</a>,
+Up: <a rel="up" accesskey="u" href="#common-actions">common actions</a>
+
+</div>
+
+<h4 class="subsection">F.2.3 NetlistChanged</h4>
+
+<!-- key NetlistChanged in hid common -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">NetlistChanged()</pre>
+   </td></tr></table>
+
+   <p>Tells the GUI that the netlist has changed. 
+<!-- ./../src/hid/common/actions.c 441 -->
+
+   <p>This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+<div class="node">
+<a name="PCBChanged-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#RouteStylesChanged-Action">RouteStylesChanged Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#NetlistChanged-Action">NetlistChanged Action</a>,
+Up: <a rel="up" accesskey="u" href="#common-actions">common actions</a>
+
+</div>
+
+<h4 class="subsection">F.2.4 PCBChanged</h4>
+
+<!-- key PCBChanged in hid common -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PCBChanged([revert])</pre>
+   </td></tr></table>
+
+   <p>Tells the GUI that the whole PCB has changed. The optional“revert"parameter can be used as a hint to the GUI that the same design is beingreloaded, and that it might keep some viewport settings
+<!-- ./../src/hid/common/actions.c 431 -->
+
+   <p>This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+<div class="node">
+<a name="RouteStylesChanged-Action"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#PCBChanged-Action">PCBChanged Action</a>,
+Up: <a rel="up" accesskey="u" href="#common-actions">common actions</a>
+
+</div>
+
+<h4 class="subsection">F.2.5 RouteStylesChanged</h4>
+
+<!-- key RouteStylesChanged in hid common -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">RouteStylesChanged()</pre>
+   </td></tr></table>
+
+   <p>Tells the GUI that the routing styles have changed. 
+<!-- ./../src/hid/common/actions.c 436 -->
+
+   <p>This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+<div class="node">
+<a name="gtk-actions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-actions">lesstif actions</a>,
+Previous: <a rel="previous" accesskey="p" href="#common-actions">common actions</a>,
+Up: <a rel="up" accesskey="u" href="#Action-Reference">Action Reference</a>
+
+</div>
+
+<h3 class="section">F.3 gtk actions</h3>
+
+<ul class="menu">
+<li><a accesskey="1" href="#gtk-About-Action">gtk About Action</a>:  N_("Tell the user about this version of PCB.");
+<li><a accesskey="2" href="#gtk-AdjustStyle-Action">gtk AdjustStyle Action</a>:  Open the window which allows editing of the route styles. 
+<li><a accesskey="3" href="#gtk-Center-Action">gtk Center Action</a>:  N_("Moves the pointer to the center of the window.");
+<li><a accesskey="4" href="#gtk-Cursor-Action">gtk Cursor Action</a>:  N_("Move the cursor.");
+<li><a accesskey="5" href="#gtk-DoWindows-Action">gtk DoWindows Action</a>:  N_("Open various GUI windows.");
+<li><a accesskey="6" href="#gtk-EditLayerGroups-Action">gtk EditLayerGroups Action</a>:  Open the preferences window which allows editing of the layer groups. 
+<li><a accesskey="7" href="#gtk-GetXY-Action">gtk GetXY Action</a>:  N_("Get a coordinate.");
+<li><a accesskey="8" href="#gtk-ImportGUI-Action">gtk ImportGUI Action</a>:  N_("Asks user which schematics to import into PCB. 
+");
+<li><a accesskey="9" href="#gtk-Pan-Action">gtk Pan Action</a>:  N_("Start or stop panning (Mode = 1 to start, 0 to stop)
+Optional thumb argument is ignored for now in gtk hid. 
+");
+<li><a href="#gtk-Popup-Action">gtk Popup Action</a>:  N_("Bring up the popup menu specified by <code>MenuName</code>. 
+If called by a mouse event then the mouse button number
+must be specified as the optional second argument.");
+<li><a href="#gtk-Print-Action">gtk Print Action</a>:  N_("Print the layout.");
+<li><a href="#gtk-PrintCalibrate-Action">gtk PrintCalibrate Action</a>:  N_("Calibrate the printer.");
+<li><a href="#gtk-Save-Action">gtk Save Action</a>:  N_("Save layout and/or element data to a user-selected file.");
+<li><a href="#gtk-SelectLayer-Action">gtk SelectLayer Action</a>:  Select which layer is the current layer. 
+<li><a href="#gtk-SetUnits-Action">gtk SetUnits Action</a>:  N_("Set the default measurement units.");
+<li><a href="#gtk-SwapSides-Action">gtk SwapSides Action</a>:  N_("Swaps the side of the board you're looking at.");
+<li><a href="#gtk-ToggleView-Action">gtk ToggleView Action</a>:  Toggle the visibility of the specified layer or layer group. 
+<li><a href="#gtk-Zoom-Action">gtk Zoom Action</a>:  N_("Various zoom factor changes.");
+</ul>
+<div class="node">
+<a name="gtk-About-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-AdjustStyle-Action">gtk AdjustStyle Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.1 gtk About</h4>
+
+<!-- key gtk About in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">About()</pre>
+   </td></tr></table>
+
+   <p>N_("Tell the user about this version of PCB.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1059 -->
+
+   <p>This just pops up a dialog telling the user which version of
+<code>pcb</code> they're running.
+
+<div class="node">
+<a name="gtk-AdjustStyle-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Center-Action">gtk Center Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-About-Action">gtk About Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.2 gtk AdjustStyle</h4>
+
+<!-- key gtk AdjustStyle in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">AdjustStyle()
+</pre>
+   </td></tr></table>
+
+   <p>Open the window which allows editing of the route styles. 
+<!-- ./../src/hid/gtk/gui-top-window.c 2081 -->
+
+   <p>Opens the window which allows editing of the route styles.
+
+<div class="node">
+<a name="gtk-Center-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Cursor-Action">gtk Cursor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-AdjustStyle-Action">gtk AdjustStyle Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.3 gtk Center</h4>
+
+<!-- key gtk Center in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Center()
+</pre>
+   </td></tr></table>
+
+   <p>N_("Moves the pointer to the center of the window.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1470 -->
+
+   <p>Move the pointer to the center of the window, but only if it's
+currently within the window already.
+
+<div class="node">
+<a name="gtk-Cursor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-DoWindows-Action">gtk DoWindows Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-Center-Action">gtk Center Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.4 gtk Cursor</h4>
+
+<!-- key gtk Cursor in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Cursor(Type,DeltaUp,DeltaRight,Units)</pre>
+   </td></tr></table>
+
+   <p>N_("Move the cursor.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1515 -->
+
+   <p>This action moves the mouse cursor.  Unlike other actions which take
+coordinates, this action's coordinates are always relative to the
+user's view of the board.  Thus, a positive <var>DeltaUp</var> may move the
+cursor towards the board origin if the board is inverted.
+
+   <p>Type is one of ‘<samp><span class="samp">Pan</span></samp>’ or ‘<samp><span class="samp">Warp</span></samp>’.  ‘<samp><span class="samp">Pan</span></samp>’ causes the
+viewport to move such that the crosshair is under the mouse cursor. 
+‘<samp><span class="samp">Warp</span></samp>’ causes the mouse cursor to move to be above the crosshair.
+
+   <p><var>Units</var> can be one of the following:
+
+     <dl>
+<dt>‘<samp><span class="samp">mil</span></samp>’<dt>‘<samp><span class="samp">mm</span></samp>’<dd>The cursor is moved by that amount, in board units.
+
+     <br><dt>‘<samp><span class="samp">grid</span></samp>’<dd>The cursor is moved by that many grid points.
+
+     <br><dt>‘<samp><span class="samp">view</span></samp>’<dd>The values are percentages of the viewport's view.  Thus, a pan of
+‘<samp><span class="samp">100</span></samp>’ would scroll the viewport by exactly the width of the
+current view.
+
+     <br><dt>‘<samp><span class="samp">board</span></samp>’<dd>The values are percentages of the board size.  Thus, a move of
+‘<samp><span class="samp">50,50</span></samp>’ moves you halfway across the board.
+
+   </dl>
+
+<div class="node">
+<a name="gtk-DoWindows-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-EditLayerGroups-Action">gtk EditLayerGroups Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-Cursor-Action">gtk Cursor Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.5 gtk DoWindows</h4>
+
+<!-- key gtk DoWindows in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DoWindows(1|2|3|4|5|6)
+DoWindows(Layout|Library|Log|Netlist|Preferences|DRC)</pre>
+   </td></tr></table>
+
+   <p>N_("Open various GUI windows.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1563 -->
+
+     <dl>
+<dt><code>1</code><dt><code>Layout</code><dd>Open the layout window.  Since the layout window is always shown
+anyway, this has no effect.
+
+     <br><dt><code>2</code><dt><code>Library</code><dd>Open the library window.
+
+     <br><dt><code>3</code><dt><code>Log</code><dd>Open the log window.
+
+     <br><dt><code>4</code><dt><code>Netlist</code><dd>Open the netlist window.
+
+     <br><dt><code>5</code><dt><code>Preferences</code><dd>Open the preferences window.
+
+     <br><dt><code>6</code><dt><code>DRC</code><dd>Open the DRC violations window.
+
+   </dl>
+
+<div class="node">
+<a name="gtk-EditLayerGroups-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-GetXY-Action">gtk GetXY Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-DoWindows-Action">gtk DoWindows Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.6 gtk EditLayerGroups</h4>
+
+<!-- key gtk EditLayerGroups in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">EditLayerGroups()
+</pre>
+   </td></tr></table>
+
+   <p>Open the preferences window which allows editing of the layer groups. 
+<!-- ./../src/hid/gtk/gui-top-window.c 2100 -->
+
+   <p>Opens the preferences window which is where the layer groups
+are edited.  This action is primarily provides to provide menu
+resource compatibility with the lesstif HID.
+
+<div class="node">
+<a name="gtk-GetXY-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-ImportGUI-Action">gtk ImportGUI Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-EditLayerGroups-Action">gtk EditLayerGroups Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.7 gtk GetXY</h4>
+
+<!-- key gtk GetXY in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">GetXY()</pre>
+   </td></tr></table>
+
+   <p>N_("Get a coordinate.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1074 -->
+
+   <p>Prompts the user for a coordinate, if one is not already selected.
+
+<div class="node">
+<a name="gtk-ImportGUI-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Pan-Action">gtk Pan Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-GetXY-Action">gtk GetXY Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.8 gtk ImportGUI</h4>
+
+<!-- key gtk ImportGUI in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ImportGUI()</pre>
+   </td></tr></table>
+
+   <p>N_("Asks user which schematics to import into PCB. 
+");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1750 -->
+
+   <p>Asks user which schematics to import into PCB.
+
+<div class="node">
+<a name="gtk-Pan-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Popup-Action">gtk Popup Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-ImportGUI-Action">gtk ImportGUI Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.9 gtk Pan</h4>
+
+<!-- key gtk Pan in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Pan([thumb], Mode)</pre>
+   </td></tr></table>
+
+   <p>N_("Start or stop panning (Mode = 1 to start, 0 to stop)
+Optional thumb argument is ignored for now in gtk hid. 
+");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1687 -->
+
+   <p>Start or stop panning.  To start call with Mode = 1, to stop call with
+Mode = 0.
+
+<div class="node">
+<a name="gtk-Popup-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Print-Action">gtk Print Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-Pan-Action">gtk Pan Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.10 gtk Popup</h4>
+
+<!-- key gtk Popup in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Popup(MenuName, [Button])</pre>
+   </td></tr></table>
+
+   <p>N_("Bring up the popup menu specified by <code>MenuName</code>. 
+If called by a mouse event then the mouse button number
+must be specified as the optional second argument.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1719 -->
+
+   <p>This just pops up the specified menu.  The menu must have been defined
+as a named subresource of the Popups resource in the menu resource
+file.
+
+<div class="node">
+<a name="gtk-Print-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-PrintCalibrate-Action">gtk PrintCalibrate Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-Popup-Action">gtk Popup Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.11 gtk Print</h4>
+
+<!-- key gtk Print in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Print()</pre>
+   </td></tr></table>
+
+   <p>N_("Print the layout.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1350 -->
+
+   <p>This will find the default printing HID, prompt the user for its
+options, and print the layout.
+
+<div class="node">
+<a name="gtk-PrintCalibrate-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Save-Action">gtk Save Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-Print-Action">gtk Print Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.12 gtk PrintCalibrate</h4>
+
+<!-- key gtk PrintCalibrate in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PrintCalibrate()</pre>
+   </td></tr></table>
+
+   <p>N_("Calibrate the printer.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1401 -->
+
+   <p>This will print a calibration page, which you would measure and type
+the measurements in, so that future printouts will be more precise.
+
+<div class="node">
+<a name="gtk-Save-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-SelectLayer-Action">gtk SelectLayer Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-PrintCalibrate-Action">gtk PrintCalibrate Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.13 gtk Save</h4>
+
+<!-- key gtk Save in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Save()
+Save(Layout|LayoutAs)
+Save(AllConnections|AllUnusedPins|ElementConnections)
+Save(PasteBuffer)</pre>
+   </td></tr></table>
+
+   <p>N_("Save layout and/or element data to a user-selected file.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1230 -->
+
+   <p>This action is a GUI front-end to the core's <code>SaveTo</code> action
+(see <a href="#SaveTo-Action">SaveTo Action</a>).  If you happen to pass a filename, like
+<code>SaveTo</code>, then <code>SaveTo</code> is called directly.  Else, the
+user is prompted for a filename to save, and then <code>SaveTo</code> is
+called with that filename.
+
+<div class="node">
+<a name="gtk-SelectLayer-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-SetUnits-Action">gtk SetUnits Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-Save-Action">gtk Save Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.14 gtk SelectLayer</h4>
+
+<!-- key gtk SelectLayer in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SelectLayer(1..MAXLAYER|Silk|Rats)</pre>
+   </td></tr></table>
+
+   <p>Select which layer is the current layer. 
+<!-- ./../src/hid/gtk/gui-top-window.c 1851 -->
+
+   <p>The specified layer becomes the currently active layer.  It is made
+visible if it is not already visible
+
+<div class="node">
+<a name="gtk-SetUnits-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-SwapSides-Action">gtk SwapSides Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-SelectLayer-Action">gtk SelectLayer Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.15 gtk SetUnits</h4>
+
+<!-- key gtk SetUnits in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetUnits(mm|mil)</pre>
+   </td></tr></table>
+
+   <p>N_("Set the default measurement units.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1606 -->
+
+     <dl>
+<dt><code>mil</code><dd>Sets the display units to mils (1/1000 inch).
+
+     <br><dt><code>mm</code><dd>Sets the display units to millimeters.
+
+   </dl>
+
+<div class="node">
+<a name="gtk-SwapSides-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-ToggleView-Action">gtk ToggleView Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-SetUnits-Action">gtk SetUnits Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.16 gtk SwapSides</h4>
+
+<!-- key gtk SwapSides in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SwapSides(|v|h|r)</pre>
+   </td></tr></table>
+
+   <p>N_("Swaps the side of the board you're looking at.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 1295 -->
+
+   <p>This action changes the way you view the board.
+
+     <dl>
+<dt><code>v</code><dd>Flips the board over vertically (up/down).
+
+     <br><dt><code>h</code><dd>Flips the board over horizontally (left/right), like flipping pages in
+a book.
+
+     <br><dt><code>r</code><dd>Rotates the board 180 degrees without changing sides.
+
+   </dl>
+
+   <p>If no argument is given, the board isn't moved but the opposite side
+is shown.
+
+   <p>Normally, this action changes which pads and silk layer are drawn as
+true silk, and which are drawn as the "invisible" layer.  It also
+determines which solder mask you see.
+
+   <p>As a special case, if the layer group for the side you're looking at
+is visible and currently active, and the layer group for the opposite
+is not visible (i.e. disabled), then this action will also swap which
+layer group is visible and active, effectively swapping the “working
+side” of the board.
+
+<div class="node">
+<a name="gtk-ToggleView-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#gtk-Zoom-Action">gtk Zoom Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#gtk-SwapSides-Action">gtk SwapSides Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.17 gtk ToggleView</h4>
+
+<!-- key gtk ToggleView in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ToggleView(1..MAXLAYER)
+ToggleView(layername)
+ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)</pre>
+   </td></tr></table>
+
+   <p>Toggle the visibility of the specified layer or layer group. 
+<!-- ./../src/hid/gtk/gui-top-window.c 1792 -->
+
+   <p>If you pass an integer, that layer is specified by index (the first
+layer is <code>1</code>, etc).  If you pass a layer name, that layer is
+specified by name.  When a layer is specified, the visibility of the
+layer group containing that layer is toggled.
+
+   <p>If you pass a special layer name, the visibility of those components
+(silk, rats, etc) is toggled.  Note that if you have a layer named
+the same as a special layer, the layer is chosen over the special layer.
+
+<div class="node">
+<a name="gtk-Zoom-Action"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#gtk-ToggleView-Action">gtk ToggleView Action</a>,
+Up: <a rel="up" accesskey="u" href="#gtk-actions">gtk actions</a>
+
+</div>
+
+<h4 class="subsection">F.3.18 gtk Zoom</h4>
+
+<!-- key gtk Zoom in hid gtk -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Zoom()
+Zoom(factor)</pre>
+   </td></tr></table>
+
+   <p>N_("Various zoom factor changes.");
+<!-- ./../src/hid/gtk/gtkhid-main.c 161 -->
+Changes the zoom (magnification) of the view of the board.  If no
+arguments are passed, the view is scaled such that the board just fits
+inside the visible window (i.e. “view all”).  Otherwise,
+<var>factor</var> specifies a change in zoom factor.  It may be prefixed by
+<code>+</code>, <code>-</code>, or <code>=</code> to change how the zoom factor is
+modified.  The <var>factor</var> is a floating point number, such as
+<code>1.5</code> or <code>0.75</code>.
+
+     <dl>
+
+     <dt><code>+</code><var>factor</var><dd>Values greater than 1.0 cause the board to be drawn smaller; more of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn bigger; less of the board will be visible.
+
+     <br><dt><code>-</code><var>factor</var><dd>Values greater than 1.0 cause the board to be drawn bigger; less of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn smaller; more of the board will be visible.
+
+     <br><dt><code>=</code><var>factor</var><dd>
+The <var>factor</var> is an absolute zoom factor; the unit for this value
+is "PCB units per screen pixel".  Since PCB units are 0.01 mil, a
+<var>factor</var> of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI,
+about the actual resolution of most screens - resulting in an "actual
+size" board.  Similarly, a <var>factor</var> of 100 gives you a 10x actual
+size.
+
+   </dl>
+
+   <p>Note that zoom factors of zero are silently ignored.
+
+<div class="node">
+<a name="lesstif-actions"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#gtk-actions">gtk actions</a>,
+Up: <a rel="up" accesskey="u" href="#Action-Reference">Action Reference</a>
+
+</div>
+
+<h3 class="section">F.4 lesstif actions</h3>
+
+<ul class="menu">
+<li><a accesskey="1" href="#lesstif-About-Action">lesstif About Action</a>:  Tell the user about this version of PCB. 
+<li><a accesskey="2" href="#lesstif-AdjustSizes-Action">lesstif AdjustSizes Action</a>:  Let the user change the board size, DRC parameters, etc
+<li><a accesskey="3" href="#lesstif-AdjustStyle-Action">lesstif AdjustStyle Action</a>:  Displays the route style adjustment window. 
+<li><a accesskey="4" href="#lesstif-Benchmark-Action">lesstif Benchmark Action</a>:  Benchmark the GUI speed. 
+<li><a accesskey="5" href="#lesstif-Command-Action">lesstif Command Action</a>:  Displays the command line input window. 
+<li><a accesskey="6" href="#lesstif-Cursor-Action">lesstif Cursor Action</a>:  Move the cursor. 
+<li><a accesskey="7" href="#lesstif-Debug-Action">lesstif Debug Action</a>:  Debug action. 
+<li><a accesskey="8" href="#lesstif-DebugXY-Action">lesstif DebugXY Action</a>:  Debug action, with coordinates
+<li><a accesskey="9" href="#lesstif-DoWindows-Action">lesstif DoWindows Action</a>:  Open various GUI windows. 
+<li><a href="#lesstif-DumpKeys-Action">lesstif DumpKeys Action</a>:  Dump Lesstif key bindings. 
+<li><a href="#lesstif-EditLayerGroups-Action">lesstif EditLayerGroups Action</a>:  Let the user change the layer groupings
+<li><a href="#lesstif-Export-Action">lesstif Export Action</a>:  Export the layout. 
+<li><a href="#lesstif-GetXY-Action">lesstif GetXY Action</a>:  Get a coordinate. 
+<li><a href="#lesstif-ImportGUI-Action">lesstif ImportGUI Action</a>:  Lets the user choose the schematics to import from
+<li><a href="#lesstif-LibraryShow-Action">lesstif LibraryShow Action</a>:  Displays the library window. 
+<li><a href="#lesstif-Load-Action">lesstif Load Action</a>:  Load layout data from a user-selected file. 
+<li><a href="#lesstif-LoadVendor-Action">lesstif LoadVendor Action</a>:  Loads a user-selected vendor resource file. 
+<li><a href="#lesstif-NetlistShow-Action">lesstif NetlistShow Action</a>:  Selects the given pinname or netname in the netlist window. 
+<li><a href="#lesstif-Print-Action">lesstif Print Action</a>:  Print the layout. 
+<li><a href="#lesstif-PrintCalibrate-Action">lesstif PrintCalibrate Action</a>:  Calibrate the printer. 
+<li><a href="#lesstif-PromptFor-Action">lesstif PromptFor Action</a>:  Prompt for a response. 
+<li><a href="#lesstif-Return-Action">lesstif Return Action</a>:  Simulate a passing or failing action. 
+<li><a href="#lesstif-Save-Action">lesstif Save Action</a>:  Save layout data to a user-selected file. 
+<li><a href="#lesstif-SelectLayer-Action">lesstif SelectLayer Action</a>:  Select which layer is the current layer. 
+<li><a href="#lesstif-SetUnits-Action">lesstif SetUnits Action</a>:  Set the default measurement units. 
+<li><a href="#lesstif-SwapSides-Action">lesstif SwapSides Action</a>:  Swaps the side of the board you're looking at. 
+<li><a href="#lesstif-ToggleView-Action">lesstif ToggleView Action</a>:  Toggle the visibility of the specified layer or layer group. 
+<li><a href="#lesstif-Zoom-Action">lesstif Zoom Action</a>:  Various zoom factor changes. 
+</ul>
+<div class="node">
+<a name="lesstif-About-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-AdjustSizes-Action">lesstif AdjustSizes Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.1 lesstif About</h4>
+
+<!-- key lesstif About in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">About()</pre>
+   </td></tr></table>
+
+   <p>Tell the user about this version of PCB. 
+<!-- ./../src/hid/lesstif/dialogs.c 867 -->
+
+   <p>This just pops up a dialog telling the user which version of
+<code>pcb</code> they're running.
+
+<div class="node">
+<a name="lesstif-AdjustSizes-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-AdjustStyle-Action">lesstif AdjustStyle Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-About-Action">lesstif About Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.2 lesstif AdjustSizes</h4>
+
+<!-- key lesstif AdjustSizes in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">AdjustSizes()</pre>
+   </td></tr></table>
+
+   <p>Let the user change the board size, DRC parameters, etc
+<!-- ./../src/hid/lesstif/dialogs.c 1136 -->
+
+   <p>Displays a dialog box that lets the user change the board
+size, DRC parameters, and text scale.
+
+   <p>The units are determined by the default display units.
+
+<div class="node">
+<a name="lesstif-AdjustStyle-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Benchmark-Action">lesstif Benchmark Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-AdjustSizes-Action">lesstif AdjustSizes Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.3 lesstif AdjustStyle</h4>
+
+<!-- key lesstif AdjustStyle in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">AdjustStyle()</pre>
+   </td></tr></table>
+
+   <p>Displays the route style adjustment window. 
+<!-- ./../src/hid/lesstif/styles.c 344 -->
+
+<div class="node">
+<a name="lesstif-Benchmark-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Command-Action">lesstif Command Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-AdjustStyle-Action">lesstif AdjustStyle Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.4 lesstif Benchmark</h4>
+
+<!-- key lesstif Benchmark in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Benchmark()</pre>
+   </td></tr></table>
+
+   <p>Benchmark the GUI speed. 
+<!-- ./../src/hid/lesstif/main.c 659 -->
+
+   <p>This action is used to speed-test the Lesstif graphics subsystem.  It
+redraws the current screen as many times as possible in ten seconds. 
+It reports the amount of time needed to draw the screen once.
+
+<div class="node">
+<a name="lesstif-Command-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Cursor-Action">lesstif Cursor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Benchmark-Action">lesstif Benchmark Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.5 lesstif Command</h4>
+
+<!-- key lesstif Command in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Command()</pre>
+   </td></tr></table>
+
+   <p>Displays the command line input window. 
+<!-- ./../src/hid/lesstif/main.c 644 -->
+
+   <p>The command window allows the user to manually enter actions to be
+executed.  Action syntax can be done one of two ways:
+
+     <dl>
+<dt><dd>Follow the action name by an open parenthesis, arguments separated by
+commas, end with a close parenthesis.  Example: <code>Abc(1,2,3)</code>
+
+     <br><dt><dd>Separate the action name and arguments by spaces.  Example: <code>Abc
+1 2 3</code>.
+
+   </dl>
+
+   <p>The first option allows you to have arguments with spaces in them,
+but the second is more “natural” to type for most people.
+
+   <p>Note that action names are not case sensitive, but arguments normally
+are.  However, most actions will check for “keywords” in a case
+insensitive way.
+
+   <p>There are three ways to finish with the command window.  If you press
+the <code>Enter</code> key, the command is invoked, the window goes away,
+and the next time you bring up the command window it's empty.  If you
+press the <code>Esc</code> key, the window goes away without invoking
+anything, and the next time you bring up the command window it's
+empty.  If you change focus away from the command window (i.e. click
+on some other window), the command window goes away but the next time
+you bring it up it resumes entering the command you were entering
+before.
+
+<div class="node">
+<a name="lesstif-Cursor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Debug-Action">lesstif Debug Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Command-Action">lesstif Command Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.6 lesstif Cursor</h4>
+
+<!-- key lesstif Cursor in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Cursor(Type,DeltaUp,DeltaRight,Units)</pre>
+   </td></tr></table>
+
+   <p>Move the cursor. 
+<!-- ./../src/hid/lesstif/main.c 716 -->
+
+   <p>This action moves the mouse cursor.  Unlike other actions which take
+coordinates, this action's coordinates are always relative to the
+user's view of the board.  Thus, a positive <var>DeltaUp</var> may move the
+cursor towards the board origin if the board is inverted.
+
+   <p>Type is one of ‘<samp><span class="samp">Pan</span></samp>’ or ‘<samp><span class="samp">Warp</span></samp>’.  ‘<samp><span class="samp">Pan</span></samp>’ causes the
+viewport to move such that the crosshair is under the mouse cursor. 
+‘<samp><span class="samp">Warp</span></samp>’ causes the mouse cursor to move to be above the crosshair.
+
+   <p><var>Units</var> can be one of the following:
+
+     <dl>
+<dt>‘<samp><span class="samp">mil</span></samp>’<dt>‘<samp><span class="samp">mm</span></samp>’<dd>The cursor is moved by that amount, in board units.
+
+     <br><dt>‘<samp><span class="samp">grid</span></samp>’<dd>The cursor is moved by that many grid points.
+
+     <br><dt>‘<samp><span class="samp">view</span></samp>’<dd>The values are percentages of the viewport's view.  Thus, a pan of
+‘<samp><span class="samp">100</span></samp>’ would scroll the viewport by exactly the width of the
+current view.
+
+     <br><dt>‘<samp><span class="samp">board</span></samp>’<dd>The values are percentages of the board size.  Thus, a move of
+‘<samp><span class="samp">50,50</span></samp>’ moves you halfway across the board.
+
+   </dl>
+
+<div class="node">
+<a name="lesstif-Debug-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-DebugXY-Action">lesstif DebugXY Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Cursor-Action">lesstif Cursor Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.7 lesstif Debug</h4>
+
+<!-- key lesstif Debug in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Debug(...)</pre>
+   </td></tr></table>
+
+   <p>Debug action. 
+<!-- ./../src/hid/lesstif/menu.c 66 -->
+
+   <p>This action exists to help debug scripts; it simply prints all its
+arguments to stdout.
+
+<div class="node">
+<a name="lesstif-DebugXY-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-DoWindows-Action">lesstif DoWindows Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Debug-Action">lesstif Debug Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.8 lesstif DebugXY</h4>
+
+<!-- key lesstif DebugXY in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DebugXY(...)</pre>
+   </td></tr></table>
+
+   <p>Debug action, with coordinates
+<!-- ./../src/hid/lesstif/menu.c 72 -->
+
+   <p>Like <code>Debug</code>, but requires a coordinate.  If the user hasn't yet
+indicated a location on the board, the user will be prompted to click
+on one.
+
+<div class="node">
+<a name="lesstif-DoWindows-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-DumpKeys-Action">lesstif DumpKeys Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-DebugXY-Action">lesstif DebugXY Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.9 lesstif DoWindows</h4>
+
+<!-- key lesstif DoWindows in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DoWindows(1|2|3|4)
+DoWindows(Layout|Library|Log|Netlist)</pre>
+   </td></tr></table>
+
+   <p>Open various GUI windows. 
+<!-- ./../src/hid/lesstif/dialogs.c 831 -->
+
+     <dl>
+<dt><code>1</code><dt><code>Layout</code><dd>Open the layout window.  Since the layout window is always shown
+anyway, this has no effect.
+
+     <br><dt><code>2</code><dt><code>Library</code><dd>Open the library window.
+
+     <br><dt><code>3</code><dt><code>Log</code><dd>Open the log window.
+
+     <br><dt><code>4</code><dt><code>Netlist</code><dd>Open the netlist window.
+
+   </dl>
+
+<div class="node">
+<a name="lesstif-DumpKeys-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-EditLayerGroups-Action">lesstif EditLayerGroups Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-DoWindows-Action">lesstif DoWindows Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.10 lesstif DumpKeys</h4>
+
+<!-- key lesstif DumpKeys in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">DumpKeys()</pre>
+   </td></tr></table>
+
+   <p>Dump Lesstif key bindings. 
+<!-- ./../src/hid/lesstif/menu.c 101 -->
+
+   <p>Causes the list of key bindings (from <code>pcb-menu.res</code>) to be
+dumped to stdout.  This is most useful when invoked from the command
+line like this:
+
+<pre class="example">     pcb --action-string DumpKeys
+</pre>
+   <div class="node">
+<a name="lesstif-EditLayerGroups-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Export-Action">lesstif Export Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-DumpKeys-Action">lesstif DumpKeys Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.11 lesstif EditLayerGroups</h4>
+
+<!-- key lesstif EditLayerGroups in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">EditLayerGroups()</pre>
+   </td></tr></table>
+
+   <p>Let the user change the layer groupings
+<!-- ./../src/hid/lesstif/dialogs.c 1448 -->
+
+   <p>Displays a dialog that lets the user view and change the layer
+groupings.  Each layer (row) can be a member of any one layer group
+(column).  Note the special layers <code>solder</code> and <code>component</code>
+allow you to specify which groups represent the top and bottom of the
+board.
+
+   <p>See <a href="#ChangeName-Action">ChangeName Action</a>.
+
+<div class="node">
+<a name="lesstif-Export-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-GetXY-Action">lesstif GetXY Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-EditLayerGroups-Action">lesstif EditLayerGroups Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.12 lesstif Export</h4>
+
+<!-- key lesstif Export in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Export()</pre>
+   </td></tr></table>
+
+   <p>Export the layout. 
+<!-- ./../src/hid/lesstif/dialogs.c 957 -->
+
+   <p>Prompts the user for an exporter to use.  Then, prompts the user for
+that exporter's options, and exports the layout.
+
+<div class="node">
+<a name="lesstif-GetXY-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-ImportGUI-Action">lesstif ImportGUI Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Export-Action">lesstif Export Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.13 lesstif GetXY</h4>
+
+<!-- key lesstif GetXY in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">GetXY()</pre>
+   </td></tr></table>
+
+   <p>Get a coordinate. 
+<!-- ./../src/hid/lesstif/menu.c 54 -->
+
+   <p>Prompts the user for a coordinate, if one is not already selected.
+
+<div class="node">
+<a name="lesstif-ImportGUI-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-LibraryShow-Action">lesstif LibraryShow Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-GetXY-Action">lesstif GetXY Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.14 lesstif ImportGUI</h4>
+
+<!-- key lesstif ImportGUI in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ImportGUI()</pre>
+   </td></tr></table>
+
+   <p>Lets the user choose the schematics to import from
+<!-- ./../src/hid/lesstif/dialogs.c 1882 -->
+
+   <p>Displays a dialog that lets the user select the schematic(s) to import
+from, then saves that information in the layout's attributes for
+future imports.
+
+<div class="node">
+<a name="lesstif-LibraryShow-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Load-Action">lesstif Load Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-ImportGUI-Action">lesstif ImportGUI Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.15 lesstif LibraryShow</h4>
+
+<!-- key lesstif LibraryShow in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LibraryShow()</pre>
+   </td></tr></table>
+
+   <p>Displays the library window. 
+<!-- ./../src/hid/lesstif/library.c 151 -->
+
+<div class="node">
+<a name="lesstif-Load-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-LoadVendor-Action">lesstif LoadVendor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-LibraryShow-Action">lesstif LibraryShow Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.16 lesstif Load</h4>
+
+<!-- key lesstif Load in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Load()
+Load(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert)</pre>
+   </td></tr></table>
+
+   <p>Load layout data from a user-selected file. 
+<!-- ./../src/hid/lesstif/dialogs.c 99 -->
+
+   <p>This action is a GUI front-end to the core's <code>LoadFrom</code> action
+(see <a href="#LoadFrom-Action">LoadFrom Action</a>).  If you happen to pass a filename, like
+<code>LoadFrom</code>, then <code>LoadFrom</code> is called directly.  Else, the
+user is prompted for a filename to load, and then <code>LoadFrom</code> is
+called with that filename.
+
+<div class="node">
+<a name="lesstif-LoadVendor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-NetlistShow-Action">lesstif NetlistShow Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Load-Action">lesstif Load Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.17 lesstif LoadVendor</h4>
+
+<!-- key lesstif LoadVendor in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">LoadVendor()</pre>
+   </td></tr></table>
+
+   <p>Loads a user-selected vendor resource file. 
+<!-- ./../src/hid/lesstif/dialogs.c 152 -->
+
+   <p>The user is prompted for a file to load, and then
+<code>LoadVendorFrom</code> is called (see <a href="#LoadVendorFrom-Action">LoadVendorFrom Action</a>) to
+load that vendor file.
+
+<div class="node">
+<a name="lesstif-NetlistShow-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Print-Action">lesstif Print Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-LoadVendor-Action">lesstif LoadVendor Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.18 lesstif NetlistShow</h4>
+
+<!-- key lesstif NetlistShow in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">NetlistShow(pinname|netname)</pre>
+   </td></tr></table>
+
+   <p>Selects the given pinname or netname in the netlist window. 
+<!-- ./../src/hid/lesstif/netlist.c 415 -->
+
+<div class="node">
+<a name="lesstif-Print-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-PrintCalibrate-Action">lesstif PrintCalibrate Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-NetlistShow-Action">lesstif NetlistShow Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.19 lesstif Print</h4>
+
+<!-- key lesstif Print in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Print()</pre>
+   </td></tr></table>
+
+   <p>Print the layout. 
+<!-- ./../src/hid/lesstif/dialogs.c 894 -->
+
+   <p>This will find the default printing HID, prompt the user for its
+options, and print the layout.
+
+<div class="node">
+<a name="lesstif-PrintCalibrate-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-PromptFor-Action">lesstif PromptFor Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Print-Action">lesstif Print Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.20 lesstif PrintCalibrate</h4>
+
+<!-- key lesstif PrintCalibrate in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PrintCalibrate()</pre>
+   </td></tr></table>
+
+   <p>Calibrate the printer. 
+<!-- ./../src/hid/lesstif/dialogs.c 937 -->
+
+   <p>This will print a calibration page, which you would measure and type
+the measurements in, so that future printouts will be more precise.
+
+<div class="node">
+<a name="lesstif-PromptFor-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Return-Action">lesstif Return Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-PrintCalibrate-Action">lesstif PrintCalibrate Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.21 lesstif PromptFor</h4>
+
+<!-- key lesstif PromptFor in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">PromptFor([message[,default]])</pre>
+   </td></tr></table>
+
+   <p>Prompt for a response. 
+<!-- ./../src/hid/lesstif/dialogs.c 560 -->
+
+   <p>This is mostly for testing the lesstif HID interface.  The parameters
+are passed to the <code>prompt_for()</code> HID function, causing the user
+to be prompted for a response.  The respose is simply printed to the
+user's stdout.
+
+<div class="node">
+<a name="lesstif-Return-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Save-Action">lesstif Save Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-PromptFor-Action">lesstif PromptFor Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.22 lesstif Return</h4>
+
+<!-- key lesstif Return in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Return(0|1)</pre>
+   </td></tr></table>
+
+   <p>Simulate a passing or failing action. 
+<!-- ./../src/hid/lesstif/menu.c 89 -->
+
+   <p>This is for testing.  If passed a 0, does nothing and succeeds.  If
+passed a 1, does nothing but pretends to fail.
+
+<div class="node">
+<a name="lesstif-Save-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-SelectLayer-Action">lesstif SelectLayer Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Return-Action">lesstif Return Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.23 lesstif Save</h4>
+
+<!-- key lesstif Save in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Save()
+Save(Layout|LayoutAs)
+Save(AllConnections|AllUnusedPins|ElementConnections)
+Save(PasteBuffer)</pre>
+   </td></tr></table>
+
+   <p>Save layout data to a user-selected file. 
+<!-- ./../src/hid/lesstif/dialogs.c 197 -->
+
+   <p>This action is a GUI front-end to the core's <code>SaveTo</code> action
+(see <a href="#SaveTo-Action">SaveTo Action</a>).  If you happen to pass a filename, like
+<code>SaveTo</code>, then <code>SaveTo</code> is called directly.  Else, the
+user is prompted for a filename to save, and then <code>SaveTo</code> is
+called with that filename.
+
+<div class="node">
+<a name="lesstif-SelectLayer-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-SetUnits-Action">lesstif SetUnits Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-Save-Action">lesstif Save Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.24 lesstif SelectLayer</h4>
+
+<!-- key lesstif SelectLayer in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SelectLayer(1..MAXLAYER|Silk|Rats)</pre>
+   </td></tr></table>
+
+   <p>Select which layer is the current layer. 
+<!-- ./../src/hid/lesstif/menu.c 387 -->
+
+   <p>The specified layer becomes the currently active layer.  It is made
+visible if it is not already visible
+
+<div class="node">
+<a name="lesstif-SetUnits-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-SwapSides-Action">lesstif SwapSides Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-SelectLayer-Action">lesstif SelectLayer Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.25 lesstif SetUnits</h4>
+
+<!-- key lesstif SetUnits in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SetUnits(mm|mil)</pre>
+   </td></tr></table>
+
+   <p>Set the default measurement units. 
+<!-- ./../src/hid/lesstif/main.c 395 -->
+
+     <dl>
+<dt><code>mil</code><dd>Sets the display units to mils (1/1000 inch).
+
+     <br><dt><code>mm</code><dd>Sets the display units to millimeters.
+
+   </dl>
+
+<div class="node">
+<a name="lesstif-SwapSides-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-ToggleView-Action">lesstif ToggleView Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-SetUnits-Action">lesstif SetUnits Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.26 lesstif SwapSides</h4>
+
+<!-- key lesstif SwapSides in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">SwapSides(|v|h|r)</pre>
+   </td></tr></table>
+
+   <p>Swaps the side of the board you're looking at. 
+<!-- ./../src/hid/lesstif/main.c 494 -->
+
+   <p>This action changes the way you view the board.
+
+     <dl>
+<dt><code>v</code><dd>Flips the board over vertically (up/down).
+
+     <br><dt><code>h</code><dd>Flips the board over horizontally (left/right), like flipping pages in
+a book.
+
+     <br><dt><code>r</code><dd>Rotates the board 180 degrees without changing sides.
+
+   </dl>
+
+   <p>If no argument is given, the board isn't moved but the opposite side
+is shown.
+
+   <p>Normally, this action changes which pads and silk layer are drawn as
+true silk, and which are drawn as the "invisible" layer.  It also
+determines which solder mask you see.
+
+   <p>As a special case, if the layer group for the side you're looking at
+is visible and currently active, and the layer group for the opposite
+is not visible (i.e. disabled), then this action will also swap which
+layer group is visible and active, effectively swapping the “working
+side” of the board.
+
+<div class="node">
+<a name="lesstif-ToggleView-Action"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#lesstif-Zoom-Action">lesstif Zoom Action</a>,
+Previous: <a rel="previous" accesskey="p" href="#lesstif-SwapSides-Action">lesstif SwapSides Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.27 lesstif ToggleView</h4>
+
+<!-- key lesstif ToggleView in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">ToggleView(1..MAXLAYER)
+ToggleView(layername)
+ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)</pre>
+   </td></tr></table>
+
+   <p>Toggle the visibility of the specified layer or layer group. 
+<!-- ./../src/hid/lesstif/menu.c 409 -->
+
+   <p>If you pass an integer, that layer is specified by index (the first
+layer is <code>1</code>, etc).  If you pass a layer name, that layer is
+specified by name.  When a layer is specified, the visibility of the
+layer group containing that layer is toggled.
+
+   <p>If you pass a special layer name, the visibility of those components
+(silk, rats, etc) is toggled.  Note that if you have a layer named
+the same as a special layer, the layer is chosen over the special layer.
+
+<div class="node">
+<a name="lesstif-Zoom-Action"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#lesstif-ToggleView-Action">lesstif ToggleView Action</a>,
+Up: <a rel="up" accesskey="u" href="#lesstif-actions">lesstif actions</a>
+
+</div>
+
+<h4 class="subsection">F.4.28 lesstif Zoom</h4>
+
+<!-- key lesstif Zoom in hid lesstif -->
+<p><table class="cartouche" summary="cartouche" border="1"><tr><td>
+<pre class="format">Zoom()
+Zoom(factor)</pre>
+   </td></tr></table>
+
+   <p>Various zoom factor changes. 
+<!-- ./../src/hid/lesstif/main.c 419 -->
+
+   <p>Changes the zoom (magnification) of the view of the board.  If no
+arguments are passed, the view is scaled such that the board just fits
+inside the visible window (i.e. “view all”).  Otherwise,
+<var>factor</var> specifies a change in zoom factor.  It may be prefixed by
+<code>+</code>, <code>-</code>, or <code>=</code> to change how the zoom factor is
+modified.  The <var>factor</var> is a floating point number, such as
+<code>1.5</code> or <code>0.75</code>.
+
+     <dl>
+<dt><code>+</code><var>factor</var><dd>Values greater than 1.0 cause the board to be drawn smaller; more of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn bigger; less of the board will be visible.
+
+     <br><dt><code>-</code><var>factor</var><dd>Values greater than 1.0 cause the board to be drawn bigger; less of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn smaller; more of the board will be visible.
+
+     <br><dt><code>=</code><var>factor</var><dd>
+The <var>factor</var> is an absolute zoom factor; the unit for this value
+is "PCB units per screen pixel".  Since PCB units are 0.01 mil, a
+<var>factor</var> of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI,
+about the actual resolution of most screens - resulting in an "actual
+size" board.  Similarly, a <var>factor</var> of 100 gives you a 10x actual
+size.
+
+   </dl>
+
+   <p>Note that zoom factors of zero are silently ignored.
+
+<!--  Appendix - Glossary - -->
+<div class="node">
+<a name="Glossary"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Index">Index</a>,
+Previous: <a rel="previous" accesskey="p" href="#Action-Reference">Action Reference</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix G Glossary</h2>
+
+<p><a name="index-glossary-889"></a><a name="index-terminology-890"></a><a name="index-index-of-terms-891"></a>
+     <dl>
+<dt>Footprint<dd>The pattern of metal, silkscreen, soldermask relief, and drills which
+defines where you place a component on a circuit board. 
+Footprints are the placed by the user onto the PC board during the
+placement phase of PCB layout.
+
+     <br><dt>Gerber File<dd>The file format used in the industry to convey a board database to the
+manufacturer is RS-274X (which replaces the now obsolete RS-274D
+format).  This file format was originally developed by Gerber for
+their photo plotters and thus RS-274D and RS-274X format files
+are often times refered to as “Gerber” files.
+
+     <br><dt>Thermal, Thermal Relief<dd>A thermal relief is a way of connecting a pin to a ground
+or power plane.  Instead of directly connecting to the plane, small "spokes"
+are used to increase the thermal resistance between the pin and the plane. 
+Often times these connections are refered to as simply a thermal.  By increasing
+the thermal resistance to the plane, it becomes easier to solder to the
+pin.  In the drawing below, the pin on the left is connected to the
+polygon using a solid connection with no thermal relief, the middle
+pin is connected using a thermal, while the pin on the right has no
+connection to the polygon.  In PCB, the “Thermal” Tool is used to
+make both a solid connection and one with thermal relief (see <a href="#Polygon-Objects">Polygon Objects</a>).
+
+     <div align="center"><img src="thermal.png" alt="Example of a thermal relief"></div>
+
+   </dl>
+
+<!--  -->
+<div class="node">
+<a name="Index"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Glossary">Glossary</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="unnumbered">Index of Resources</h2>
+
+<ul class="index-vr" compact>
+<li><a href="#index-absoluteGrid-371"><code>absoluteGrid</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-alignmentDistance-373"><code>alignmentDistance</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-allDirectionLines-375"><code>allDirectionLines</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-backgroundImage-378"><code>backgroundImage</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-backupInterval-380"><code>backupInterval</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-bloat-382"><code>bloat</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-BTNMOD-833"><code>BTNMOD</code></a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-connectedColor-385"><code>connectedColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-cross-hairColor-388"><code>cross hairColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-DEFAULTFONT-830"><code>DEFAULTFONT</code></a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-DEFAULTLIBRARY-831"><code>DEFAULTLIBRARY</code></a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-Element-Search-862"><code>Element Search</code></a>: <a href="#Regular-Expressions">Regular Expressions</a></li>
+<li><a href="#index-elementColor-391"><code>elementColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-elementCommand-789"><code>elementCommand</code></a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-elementCommand-395"><code>elementCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-elementContentsCommand-458"><code>elementContentsCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-elementPath-402"><code>elementPath</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-elementSelectedColor-392"><code>elementSelectedColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Exporting-a-layout-141"><code>Exporting a layout</code></a>: <a href="#Exporting">Exporting</a></li>
+<li><a href="#index-fileCommand-790"><code>fileCommand</code></a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-fileCommand-407"><code>fileCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-filePath-414"><code>filePath</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-fontCommand-791"><code>fontCommand</code></a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-fontCommand-419"><code>fontCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-fontFile-427"><code>fontFile</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-fontPath-430"><code>fontPath</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-GNUM4-832"><code>GNUM4</code></a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-grid-436"><code>grid</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-gridColor-439"><code>gridColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-INFOLIBDIR-828"><code>INFOLIBDIR</code></a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-invisibleObjectsColor-442"><code>invisibleObjectsColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layerColor-445"><code>layerColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layerGroups-449"><code>layerGroups</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layerName-452"><code>layerName</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layerSelectedColor-446"><code>layerSelectedColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-libraryCommand-792"><code>libraryCommand</code></a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-libraryCommand-454"><code>libraryCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-libraryContentsCommand-793"><code>libraryContentsCommand</code></a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-libraryFilename-462"><code>libraryFilename</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-libraryPath-465"><code>libraryPath</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-lineThickness-469"><code>lineThickness</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Measuring-distances-173"><code>Measuring distances</code></a>: <a href="#Measuring-distances">Measuring distances</a></li>
+<li><a href="#index-media-473"><code>media</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-offLimitColor-477"><code>offLimitColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-PCBLIBDIR-829"><code>PCBLIBDIR</code></a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-pinColor-480"><code>pinColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutFont0_002e_002e6-484"><code>pinoutFont0..6</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutNameLength-487"><code>pinoutNameLength</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutOffsetX-491"><code>pinoutOffsetX</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutOffsetY-492"><code>pinoutOffsetY</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutTextOffsetX-494"><code>pinoutTextOffsetX</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutTextOffsetY-495"><code>pinoutTextOffsetY</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinoutZoom-497"><code>pinoutZoom</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinSelectedColor-481"><code>pinSelectedColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-printCommand-500"><code>printCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-raiseLogWindow-502"><code>raiseLogWindow</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-ratCommand-505"><code>ratCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-ratPath-508"><code>ratPath</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Regular-Expressions-863"><code>Regular Expressions</code></a>: <a href="#Regular-Expressions">Regular Expressions</a></li>
+<li><a href="#index-resetAfterElement-511"><code>resetAfterElement</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-ringBellWhenFinished-514"><code>ringBellWhenFinished</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-routeStyle-516"><code>routeStyle</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-rubberBandMode-518"><code>rubberBandMode</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-saveCommand-794"><code>saveCommand</code></a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-saveCommand-522"><code>saveCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-saveInTMP-529"><code>saveInTMP</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-saveLastCommand-536"><code>saveLastCommand</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Searching-for-elements-171"><code>Searching for elements</code></a>: <a href="#Searching-for-elements">Searching for elements</a></li>
+<li><a href="#index-shrink-539"><code>shrink</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-size-542"><code>size</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-stipplePolygons-545"><code>stipplePolygons</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-textScale-548"><code>textScale</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-useLogWindow-551"><code>useLogWindow</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-viaColor-554"><code>viaColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-viaDrillingHole-559"><code>viaDrillingHole</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-viaSelectedColor-555"><code>viaSelectedColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-viaThickness-558"><code>viaThickness</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-volume-563"><code>volume</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-warnColor-566"><code>warnColor</code></a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-zoom-569"><code>zoom</code></a>: <a href="#Resources">Resources</a></li>
+</ul><h2 class="unnumbered">Index of Actions, Commands and Options</h2>
+
+
+
+<ul class="index-fn" compact>
+<li><a href="#index-g_t_002d_002d-basename-_003cstring_003e-363"><code>-- basename <string></code></a>: <a href="#nelma-Options">nelma Options</a></li>
+<li><a href="#index-g_t_002d_002daction_002dscript-_003cstring_003e-221"><code>--action-script <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002daction_002dstring-_003cstring_003e-222"><code>--action-string <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dalign_002dmarks-319"><code>--align-marks</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dalignment_002ddistance-_003cnum_003e-291"><code>--alignment-distance <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dall_002ddirection_002dlines-238"><code>--all-direction-lines</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dall_002dlayers-313"><code>--all-layers</code></a>: <a href="#Gerber-Export">Gerber Export</a></li>
+<li><a href="#index-g_t_002d_002das_002dshown-352"><code>--as-shown</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002das_002dshown-343"><code>--as-shown</code></a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dauto_002dmirror-323"><code>--auto-mirror</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dbackground_002dcolor-_003cstring_003e-248"><code>--background-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dbackup_002dinterval-217"><code>--backup-interval</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dbg_002dimage-_003cstring_003e-244"><code>--bg-image <string></code></a>: <a href="#lesstif-GUI-Options">lesstif GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dbg_002dimage-_003cstring_003e-241"><code>--bg-image <string></code></a>: <a href="#GTK_002b-GUI-Options">GTK+ GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dblack_002dcolor-_003cstring_003e-246"><code>--black-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dbloat-_003cnum_003e-303"><code>--bloat <num></code></a>: <a href="#DRC-Options">DRC Options</a></li>
+<li><a href="#index-g_t_002d_002dbomfile-_003cstring_003e-309"><code>--bomfile <string></code></a>: <a href="#BOM-Creation">BOM Creation</a></li>
+<li><a href="#index-g_t_002d_002dclear_002dline-234"><code>--clear-line</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dconnected_002dcolor-_003cstring_003e-262"><code>--connected-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dcopper_002dheight-_003cnum_003e-365"><code>--copper-height <num></code></a>: <a href="#nelma-Options">nelma Options</a></li>
+<li><a href="#index-g_t_002d_002dcopyright-212"><code>--copyright</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dcross_002dcolor-_003cstring_003e-250"><code>--cross-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dcrosshair_002dcolor-_003cstring_003e-249"><code>--crosshair-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002ddefault_002dfont-_003cstring_003e-279"><code>--default-font <string></code></a>: <a href="#Paths">Paths</a></li>
+<li><a href="#index-g_t_002d_002ddefault_002dPCB_002dheight-_003cnum_003e-289"><code>--default-PCB-height <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002ddefault_002dPCB_002dwidth-_003cnum_003e-288"><code>--default-PCB-width <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002ddpi-348"><code>--dpi</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002ddpi-_003cnum_003e-364"><code>--dpi <num></code></a>: <a href="#nelma-Options">nelma Options</a></li>
+<li><a href="#index-g_t_002d_002ddraw_002dgrid-233"><code>--draw-grid</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002ddrill_002dcopper-337"><code>--drill-copper</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002ddrill_002dhelper-317"><code>--drill-helper</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002ddump_002dactions-215"><code>--dump-actions</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002delement_002dcolor-_003cstring_003e-256"><code>--element-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002delement_002dcommand-_003cstring_003e-296"><code>--element-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002delement_002dpath-_003cstring_003e-220"><code>--element-path <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002delement_002dselected_002dcolor-_003cstring_003e-260"><code>--element-selected-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002deps_002dfile-_003cstring_003e-340"><code>--eps-file <string></code></a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002deps_002dscale-_003cnum_003e-341"><code>--eps-scale <num></code></a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dfab_002dauthor-_003cstring_003e-223"><code>--fab-author <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dfile_002dcommand-_003cstring_003e-295"><code>--file-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dfile_002dpath-_003cstring_003e-280"><code>--file-path <string></code></a>: <a href="#Paths">Paths</a></li>
+<li><a href="#index-g_t_002d_002dfill_002dpage-322"><code>--fill-page</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dfont_002dcommand-_003cstring_003e-294"><code>--font-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dfont_002dpath-_003cstring_003e-281"><code>--font-path <string></code></a>: <a href="#Paths">Paths</a></li>
+<li><a href="#index-g_t_002d_002dformat-_003cstring_003e-356"><code>--format <string></code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dfull_002dpoly-235"><code>--full-poly</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dgerberfile-_003cstring_003e-312"><code>--gerberfile <string></code></a>: <a href="#Gerber-Export">Gerber Export</a></li>
+<li><a href="#index-g_t_002d_002dgrid-_003cnum_003e-292"><code>--grid <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dgrid_002dcolor-_003cstring_003e-264"><code>--grid-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dgrid_002dunits_002dmm-_003cstring_003e-216"><code>--grid-units-mm <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dgroups-_003cstring_003e-218"><code>--groups <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dhelp-209"><code>--help</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dinvisible_002dmark_002dcolor-_003cstring_003e-259"><code>--invisible-mark-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dinvisible_002dobjects_002dcolor-_003cstring_003e-258"><code>--invisible-objects-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dkeepaway-_003cnum_003e-287"><code>--keepaway <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dcolor_002d_003cn_003e-_003cstring_003e-265"><code>--layer-color-<n> <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d1-_003cstring_003e-269"><code>--layer-name-1 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d2-_003cstring_003e-270"><code>--layer-name-2 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d3-_003cstring_003e-271"><code>--layer-name-3 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d4-_003cstring_003e-272"><code>--layer-name-4 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d5-_003cstring_003e-273"><code>--layer-name-5 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d6-_003cstring_003e-274"><code>--layer-name-6 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d7-_003cstring_003e-275"><code>--layer-name-7 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dname_002d8-_003cstring_003e-276"><code>--layer-name-8 <string></code></a>: <a href="#Layer-Names">Layer Names</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dselected_002dcolor_002d_003cn_003e-_003cstring_003e-266"><code>--layer-selected-color-<n> <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dlayer_002dstack-_003cstring_003e-224"><code>--layer-stack <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dlib_002dcommand-_003cstring_003e-299"><code>--lib-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dlib_002dcommand_002ddir-_003cstring_003e-298"><code>--lib-command-dir <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dlib_002dcontents_002dcommand-_003cstring_003e-300"><code>--lib-contents-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dlib_002dname-_003cstring_003e-278"><code>--lib-name <string></code></a>: <a href="#Paths">Paths</a></li>
+<li><a href="#index-g_t_002d_002dlib_002dnewlib-_003cstring_003e-277"><code>--lib-newlib <string></code></a>: <a href="#Paths">Paths</a></li>
+<li><a href="#index-g_t_002d_002dlib_002dpath-_003cstring_003e-282"><code>--lib-path <string></code></a>: <a href="#Paths">Paths</a></li>
+<li><a href="#index-g_t_002d_002dline_002dthickness-_003cnum_003e-285"><code>--line-thickness <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dlisten-243"><code>--listen</code></a>: <a href="#lesstif-GUI-Options">lesstif GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dlisten-240"><code>--listen</code></a>: <a href="#GTK_002b-GUI-Options">GTK+ GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dlprcommand-_003cstring_003e-362"><code>--lprcommand <string></code></a>: <a href="#lpr-Printing-Options">lpr Printing Options</a></li>
+<li><a href="#index-g_t_002d_002dmask_002dcolor-_003cstring_003e-268"><code>--mask-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dmedia-_003cmedia_002dname_003e-329"><code>--media <media-name></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dmin_002ddrill-_003cnum_003e-307"><code>--min-drill <num></code></a>: <a href="#DRC-Options">DRC Options</a></li>
+<li><a href="#index-g_t_002d_002dmin_002dring-_003cnum_003e-308"><code>--min-ring <num></code></a>: <a href="#DRC-Options">DRC Options</a></li>
+<li><a href="#index-g_t_002d_002dmin_002dsilk-_003cnum_003e-306"><code>--min-silk <num></code></a>: <a href="#DRC-Options">DRC Options</a></li>
+<li><a href="#index-g_t_002d_002dmin_002dwidth-_003cnum_003e-305"><code>--min-width <num></code></a>: <a href="#DRC-Options">DRC Options</a></li>
+<li><a href="#index-g_t_002d_002dminimum-polygon-area-_003cnum_003e-293"><code>--minimum polygon area <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dmirror-321"><code>--mirror</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dmonochrome-353"><code>--monochrome</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dmonochrome-344"><code>--monochrome</code></a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dmulti_002dfile-334"><code>--multi-file</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002doff_002dlimit_002dcolor-_003cstring_003e-263"><code>--off-limit-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002donly_002dvisible-346"><code>--only-visible</code></a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002donly_002dvivible-354"><code>--only-vivible</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002doutfile-_003cstring_003e-347"><code>--outfile <string></code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002doutline-320"><code>--outline</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dpcb_002dmenu-_003cstring_003e-245"><code>--pcb-menu <string></code></a>: <a href="#lesstif-GUI-Options">lesstif GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dpcb_002dmenu-_003cstring_003e-242"><code>--pcb-menu <string></code></a>: <a href="#GTK_002b-GUI-Options">GTK+ GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dphoto_002dflip_002dx-360"><code>--photo-flip-x</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dphoto_002dflip_002dy-361"><code>--photo-flip-y</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dphoto_002dmode-359"><code>--photo-mode</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dpin_002dcolor-_003cstring_003e-253"><code>--pin-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dpin_002dname_002dcolor-_003cstring_003e-255"><code>--pin-name-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dpin_002dselected_002dcolor-_003cstring_003e-254"><code>--pin-selected-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dpinout_002doffset_002dx-_003cnum_003e-229"><code>--pinout-offset-x <num></code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dpinout_002doffset_002dy-_003cnum_003e-230"><code>--pinout-offset-y <num></code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dpinout_002dtext_002doffset_002dx-_003cnum_003e-231"><code>--pinout-text-offset-x <num></code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dpinout_002dtext_002doffset_002dy-_003cnum_003e-232"><code>--pinout-text-offset-y <num></code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dpng_002dbloat-_003cnum_003e_003cdim_003e-357"><code>--png-bloat <num><dim></code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dprint_002dfile-_003cstring_003e-297"><code>--print-file <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dps_002dbloat-_003cnum_003e-326"><code>--ps-bloat <num></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dps_002dcolor-324"><code>--ps-color</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dps_002dinvert-328"><code>--ps-invert</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dpsfade-_003cnum_003e-331"><code>--psfade <num></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dpsfile-_003cstring_003e-315"><code>--psfile <string></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002drat_002dcolor-_003cstring_003e-257"><code>--rat-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002drat_002dcommand-_003cstring_003e-302"><code>--rat-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002drat_002dselected_002dcolor-_003cstring_003e-261"><code>--rat-selected-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002drat_002dthickness-_003cnum_003e-286"><code>--rat-thickness <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dreset_002dafter_002delement-227"><code>--reset-after-element</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dring_002dbell_002dfinished-228"><code>--ring-bell-finished</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002droute_002dstyles-_003cstring_003e-219"><code>--route-styles <string></code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dsave_002dcommand-_003cstring_003e-301"><code>--save-command <string></code></a>: <a href="#Commands">Commands</a></li>
+<li><a href="#index-g_t_002d_002dsave_002din_002dtmp-226"><code>--save-in-tmp</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dsave_002dlast_002dcommand-225"><code>--save-last-command</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dscale-_003cnum_003e-332"><code>--scale <num></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dshow_002dactions-214"><code>--show-actions</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dshow_002ddefaults-213"><code>--show-defaults</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dshow_002dlegend-339"><code>--show-legend</code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dshow_002dnumber-239"><code>--show-number</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dshrink-_003cnum_003e-304"><code>--shrink <num></code></a>: <a href="#DRC-Options">DRC Options</a></li>
+<li><a href="#index-g_t_002d_002dsnap_002dpin-237"><code>--snap-pin</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002dsubstrate_002depsilon-_003cnum_003e-367"><code>--substrate-epsilon <num></code></a>: <a href="#nelma-Options">nelma Options</a></li>
+<li><a href="#index-g_t_002d_002dsubstrate_002dheight-_003cnum_003e-366"><code>--substrate-height <num></code></a>: <a href="#nelma-Options">nelma Options</a></li>
+<li><a href="#index-g_t_002d_002dtext_002dscale-_003cnum_003e-290"><code>--text-scale <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dunique_002dnames-236"><code>--unique-names</code></a>: <a href="#General-GUI-Options">General GUI Options</a></li>
+<li><a href="#index-g_t_002d_002duse_002dalpha-355"><code>--use-alpha</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dverbose-314"><code>--verbose</code></a>: <a href="#Gerber-Export">Gerber Export</a></li>
+<li><a href="#index-g_t_002d_002dverbose-211"><code>--verbose</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dversion-210"><code>--version</code></a>: <a href="#General-Options">General Options</a></li>
+<li><a href="#index-g_t_002d_002dvia_002dcolor-_003cstring_003e-251"><code>--via-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dvia_002ddrilling_002dhole-_003cnum_003e-284"><code>--via-drilling-hole <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dvia_002dselected_002dcolor-_003cstring_003e-252"><code>--via-selected-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dvia_002dthickness-_003cnum_003e-283"><code>--via-thickness <num></code></a>: <a href="#Sizes">Sizes</a></li>
+<li><a href="#index-g_t_002d_002dwarn_002dcolor-_003cstring_003e-267"><code>--warn-color <string></code></a>: <a href="#Colors">Colors</a></li>
+<li><a href="#index-g_t_002d_002dx_002dmax-349"><code>--x-max</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dxcalib-_003cnum_003e-335"><code>--xcalib <num></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_002d_002dxy_002dmax-351"><code>--xy-max</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dxy_002dunit-_003cunit_003e-311"><code>--xy-unit <unit></code></a>: <a href="#BOM-Creation">BOM Creation</a></li>
+<li><a href="#index-g_t_002d_002dxyfile-_003cstring_003e-310"><code>--xyfile <string></code></a>: <a href="#BOM-Creation">BOM Creation</a></li>
+<li><a href="#index-g_t_002d_002dy_002dmax-350"><code>--y-max</code></a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-g_t_002d_002dycalib-_003cnum_003e-336"><code>--ycalib <num></code></a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-g_t_003aactionCommand_0028_0029-204"><code>:actionCommand()</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003al-181"><code>:l</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003ale-184"><code>:le</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003am-187"><code>:m</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003aq-192"><code>:q</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003arn-198"><code>:rn</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003as-195"><code>:s</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-g_t_003aw_005bq_005d-201"><code>:w[q]</code></a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-AddRats_0028_0029-576"><code>AddRats()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ApplyVendor_0028_0029-873"><code>ApplyVendor()</code></a>: <a href="#ApplyVendor-Action">ApplyVendor Action</a></li>
+<li><a href="#index-ApplyVendor_0028_0029-580"><code>ApplyVendor()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Atomic_0028_0029-583"><code>Atomic()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Bell_0028_0029-586"><code>Bell()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeClearSize_0028_0029-589"><code>ChangeClearSize()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeDrillSize_0028_0029-593"><code>ChangeDrillSize()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeFlag_0028_0029-597"><code>ChangeFlag()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeHole_0028_0029-602"><code>ChangeHole()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeName_0028_0029-605"><code>ChangeName()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeOctagon_0028_0029-609"><code>ChangeOctagon()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangePinName_0028_0029-613"><code>ChangePinName()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeSize_0028_0029-616"><code>ChangeSize()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ChangeSquare_0028_0029-620"><code>ChangeSquare()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ClrFlag_0028_0029-624"><code>ClrFlag()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Command_0028_0029-630"><code>Command()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Connection_0028_0029-633"><code>Connection()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-DeleteRats_0028_0029-640"><code>DeleteRats()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-DisableVendor_0028_0029-876"><code>DisableVendor()</code></a>: <a href="#DisableVendor-Action">DisableVendor Action</a></li>
+<li><a href="#index-DisableVendor_0028_0029-644"><code>DisableVendor()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-DisperseElements_0028_0029-647"><code>DisperseElements()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Display_0028_0029-652"><code>Display()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-DRC_0028_0029-666"><code>DRC()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-EditLayerGroups_0028_0029-672"><code>EditLayerGroups()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-EnableVendor_0028_0029-879"><code>EnableVendor()</code></a>: <a href="#EnableVendor-Action">EnableVendor Action</a></li>
+<li><a href="#index-EnableVendor_0028_0029-675"><code>EnableVendor()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ExecuteFile_0028_0029-669"><code>ExecuteFile()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Load_0028_0029-678"><code>Load()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-LoadVendor_0028_0029-680"><code>LoadVendor()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-LoadVendorFrom_0028_0029-882"><code>LoadVendorFrom()</code></a>: <a href="#LoadVendorFrom-Action">LoadVendorFrom Action</a></li>
+<li><a href="#index-MarkCrosshair_0028_0029-683"><code>MarkCrosshair()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Mode_0028_0029-686"><code>Mode()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-MovePointer_0028_0029-689"><code>MovePointer()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-MoveToCurrentLayer_0028_0029-692"><code>MoveToCurrentLayer()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-New_0028_0029-695"><code>New()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-PasteBuffer_0028_0029-698"><code>PasteBuffer()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Polygon_0028_0029-705"><code>Polygon()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Print_0028_0029-709"><code>Print()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Quit_0028_0029-712"><code>Quit()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Redo_0028_0029-715"><code>Redo()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-RemoveSelected_0028_0029-718"><code>RemoveSelected()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Report_0028_0029-721"><code>Report()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-RouteStyle_0028_0029-725"><code>RouteStyle()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Save_0028_0029-728"><code>Save()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Select_0028_0029-731"><code>Select()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-SetFlag_0028_0029-734"><code>SetFlag()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-SetValue_0028_0029-739"><code>SetValue()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-SwapSides_0028_0029-746"><code>SwapSides()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-SwitchDrawingLayer_0028_0029-749"><code>SwitchDrawingLayer()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ToggleHideName_0028_0029-752"><code>ToggleHideName()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ToggleVendor_0028_0029-885"><code>ToggleVendor()</code></a>: <a href="#ToggleVendor-Action">ToggleVendor Action</a></li>
+<li><a href="#index-ToggleVendor_0028_0029-756"><code>ToggleVendor()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-ToggleVisibility_0028_0029-759"><code>ToggleVisibility()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Undo_0028_0029-762"><code>Undo()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-UnloadVendor_0028_0029-888"><code>UnloadVendor()</code></a>: <a href="#UnloadVendor-Action">UnloadVendor Action</a></li>
+<li><a href="#index-UnloadVendor_0028_0029-765"><code>UnloadVendor()</code></a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Unselect_0028_0029-768"><code>Unselect()</code></a>: <a href="#Actions">Actions</a></li>
+   </ul><h2 class="unnumbered">Index of Concepts</h2>
+
+
+
+<ul class="index-cp" compact>
+<li><a href="#index-g_t_002ftmp-534">/tmp</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-g_t_002ftmp-121">/tmp</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-action-command-205">action command</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-action-reference-870">action reference</a>: <a href="#Action-Reference">Action Reference</a></li>
+<li><a href="#index-actions-571">actions</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-actions-file_002c-executing-670">actions file, executing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Actions_002c-initiating-206">Actions, initiating</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-align_002dmarks-318">align-marks</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-alignment-374">alignment</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-alignment-targets-132">alignment targets</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-Alpha-848">Alpha</a>: <a href="#DEC-Alpha">DEC Alpha</a></li>
+<li><a href="#index-arc-16">arc</a>: <a href="#Arc-Objects">Arc Objects</a></li>
+<li><a href="#index-arc_002c-an-example-83">arc, an example</a>: <a href="#Arcs">Arcs</a></li>
+<li><a href="#index-architecture-852">architecture</a>: <a href="#Linux">Linux</a></li>
+<li><a href="#index-architecture-849">architecture</a>: <a href="#SCO">SCO</a></li>
+<li><a href="#index-architecture-846">architecture</a>: <a href="#DEC-Alpha">DEC Alpha</a></li>
+<li><a href="#index-architecture-843">architecture</a>: <a href="#SGI">SGI</a></li>
+<li><a href="#index-architecture-839">architecture</a>: <a href="#Sun">Sun</a></li>
+<li><a href="#index-architecture-836">architecture</a>: <a href="#HP">HP</a></li>
+<li><a href="#index-arrow-tool-159">arrow tool</a>: <a href="#Arrow-Tool">Arrow Tool</a></li>
+<li><a href="#index-as_002dshown-_0028EPS_0029-342">as-shown (EPS)</a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-ASCII-files_002c-format-of-788">ASCII files, format of</a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-Atari-version-1">Atari version</a>: <a href="#History">History</a></li>
+<li><a href="#index-atomic-585">atomic</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-auto_002drouter-49">auto-router</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-autorouter-178">autorouter</a>: <a href="#Autorouter">Autorouter</a></li>
+<li><a href="#index-background-379">background</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-backup-381">backup</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-backup-118">backup</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-bell-588">bell</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-bill-of-materials-143">bill of materials</a>: <a href="#bom">bom</a></li>
+<li><a href="#index-bloat-383">bloat</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-bom-142">bom</a>: <a href="#bom">bom</a></li>
+<li><a href="#index-buffer_002c-an-example-106">buffer, an example</a>: <a href="#Pastebuffer">Pastebuffer</a></li>
+<li><a href="#index-buffer_002c-convert-contents-to-element-103">buffer, convert contents to element</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-Buffer_002c-popup-menu-45">Buffer, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-buffer_002c-selecting-a-699">buffer, selecting a</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-button-translations-574">button translations</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-cat-413">cat</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-centering-653">centering</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-centroid-file-format-866">centroid file format</a>: <a href="#Centroid-File-Format">Centroid File Format</a></li>
+<li><a href="#index-centroid-file_002c-algorithms-868">centroid file, algorithms</a>: <a href="#Centroid-File-Format">Centroid File Format</a></li>
+<li><a href="#index-change-active-layer-62">change active layer</a>: <a href="#Layer-Controls">Layer Controls</a></li>
+<li><a href="#index-change-drawing-layer-750">change drawing layer</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-change-object-name-607">change object name</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-change-settings-740">change settings</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-change-sizes-590">change sizes</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-change-square-flag-621">change square flag</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-change-viewing-side-747">change viewing side</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-changing-layers-113">changing layers</a>: <a href="#Moving-and-Copying">Moving and Copying</a></li>
+<li><a href="#index-changing-pin_002fpad-names-614">changing pin/pad names</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-clearance-15">clearance</a>: <a href="#Line-Objects">Line Objects</a></li>
+<li><a href="#index-clearance_002c-changing-of-objects-592">clearance, changing of objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-clearance_002c-for-new-lines-41">clearance, for new lines</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-clipping-lines-to-45-degree-665">clipping lines to 45 degree</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-clipping-lines-to-45-degree-377">clipping lines to 45 degree</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-closing-a-polygon-708">closing a polygon</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-cnc-146">cnc</a>: <a href="#gcode">gcode</a></li>
+<li><a href="#index-color-printout-130">color printout</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-color_002c-warning-568">color, warning</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-colors-386">colors</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-command_002dline-options-208">command-line options</a>: <a href="#Command_002dLine-Options">Command-Line Options</a></li>
+<li><a href="#index-compile_002c-how-to-824">compile, how to</a>: <a href="#compiling">compiling</a></li>
+<li><a href="#index-configure-827">configure</a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-connection_002c-removing-an-777">connection, removing an</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-connections_002c-colors-387">connections, colors</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-connections_002c-creating-list-of-156">connections, creating list of</a>: <a href="#Connection-Lists">Connection Lists</a></li>
+<li><a href="#index-connections_002c-reseting-636">connections, reseting</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-connections_002c-reseting-after-element-512">connections, reseting after element</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-connections_002c-searching-for-638">connections, searching for</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Connects_002c-popup-menu-48">Connects, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-copy-an-object-786">copy an object</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-copying-objects-704">copying objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-copying_002c-an-example-109">copying, an example</a>: <a href="#Moving-and-Copying">Moving and Copying</a></li>
+<li><a href="#index-creating-objects-75">creating objects</a>: <a href="#Common">Common</a></li>
+<li><a href="#index-cursor-color-390">cursor color</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-cursor-movements-691">cursor movements</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-cursor-position-685">cursor position</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-cursor-steps-438">cursor steps</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-cutting-objects-703">cutting objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-DEC-847">DEC</a>: <a href="#DEC-Alpha">DEC Alpha</a></li>
+<li><a href="#index-default-font-428">default font</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-default-layout-size-543">default layout size</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-default-library-463">default library</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-default-text-scaling-550">default text scaling</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-default-translations-772">default translations</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-design-rule-checker_002c-invoking-50">design rule checker, invoking</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-design-rule-checking-667">design rule checking</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-design-rule-checking-164">design rule checking</a>: <a href="#Design-Rule-Checking">Design Rule Checking</a></li>
+<li><a href="#index-device_002c-selecting-an-output-126">device, selecting an output</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-directory-_002ftmp-535">directory /tmp</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-directory-_002ftmp-122">directory /tmp</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-dispersing-elements-648">dispersing elements</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-display-547">display</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-displaying-element-names-657">displaying element names</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-displaying-element-names-30">displaying element names</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-displaying-pinout-663">displaying pinout</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-displaying-status-information-57">displaying status information</a>: <a href="#Status_002dline-and-Input_002dfield">Status-line and Input-field</a></li>
+<li><a href="#index-distributing-elements-649">distributing elements</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-DOS-filenames-138">DOS filenames</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-drawing-objects-71">drawing objects</a>: <a href="#Drawing-and-Removing">Drawing and Removing</a></li>
+<li><a href="#index-drc-668">drc</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-drc-384">drc</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-drc-165">drc</a>: <a href="#Design-Rule-Checking">Design Rule Checking</a></li>
+<li><a href="#index-drill-724">drill</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-drill-report-54">drill report</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-drill-sizes_002c-list-of-standard-864">drill sizes, list of standard</a>: <a href="#Standard-Drill-Sizes">Standard Drill Sizes</a></li>
+<li><a href="#index-Drill-table-176">Drill table</a>: <a href="#Vendor-drill-mapping">Vendor drill mapping</a></li>
+<li><a href="#index-drill_002dhelper-316">drill-helper</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-drilling-hole_002c-changing-of-objects-596">drilling hole, changing of objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-drilling-hole_002c-setting-of-initial-size-743">drilling hole, setting of initial size</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Edit_002c-popup-menu-26">Edit, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-element-name_002c-hiding-754">element name, hiding</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-element-name_002c-removing-from-silk_002dscreen-755">element name, removing from silk-screen</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Element-Search-860">Element Search</a>: <a href="#Regular-Expressions">Regular Expressions</a></li>
+<li><a href="#index-element_002c-an-example-93">element, an example</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-element_002c-an-overview-7">element, an overview</a>: <a href="#Element-Objects">Element Objects</a></li>
+<li><a href="#index-element_002c-color-394">element, color</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-element_002c-command-396">element, command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-element_002c-creating-a-new-package-101">element, creating a new package</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-element_002c-display-names-of-658">element, display names of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-element_002c-display-names-of-31">element, display names of</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-element_002c-editing-47">element, editing</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-element_002c-file-format-800">element, file format</a>: <a href="#Element-File">Element File</a></li>
+<li><a href="#index-element_002c-files-397">element, files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-element_002c-loading-to-buffer-186">element, loading to buffer</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-element_002c-move-name-of-782">element, move name of</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-elements_002c-dispersing-650">elements, dispersing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-elements_002c-distributing-651">elements, distributing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-encapsulated-postscript-154">encapsulated postscript</a>: <a href="#eps">eps</a></li>
+<li><a href="#index-entering-user-commands-180">entering user commands</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-eps-153">eps</a>: <a href="#eps">eps</a></li>
+<li><a href="#index-erasing-objects-73">erasing objects</a>: <a href="#Drawing-and-Removing">Drawing and Removing</a></li>
+<li><a href="#index-example-files-95">example files</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-example-of-buffer-handling-107">example of buffer handling</a>: <a href="#Pastebuffer">Pastebuffer</a></li>
+<li><a href="#index-example-of-connection-lists-155">example of connection lists</a>: <a href="#Connection-Lists">Connection Lists</a></li>
+<li><a href="#index-example-of-copying-111">example of copying</a>: <a href="#Moving-and-Copying">Moving and Copying</a></li>
+<li><a href="#index-example-of-creating-an-element-100">example of creating an element</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-example-of-element-handling-94">example of element handling</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-example-of-line-handling-82">example of line handling</a>: <a href="#Lines">Lines</a></li>
+<li><a href="#index-example-of-loading-117">example of loading</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-example-of-loading-an-element-file-97">example of loading an element file</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-example-of-moving-110">example of moving</a>: <a href="#Moving-and-Copying">Moving and Copying</a></li>
+<li><a href="#index-example-of-pastebuffer-handling-105">example of pastebuffer handling</a>: <a href="#Pastebuffer">Pastebuffer</a></li>
+<li><a href="#index-example-of-pin-handling-99">example of pin handling</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-example-of-polygon-handling-85">example of polygon handling</a>: <a href="#Polygons">Polygons</a></li>
+<li><a href="#index-example-of-printing-125">example of printing</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-example-of-rectangle-handling-87">example of rectangle handling</a>: <a href="#Polygons">Polygons</a></li>
+<li><a href="#index-example-of-saving-116">example of saving</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-example-of-text-handling-90">example of text handling</a>: <a href="#Text">Text</a></li>
+<li><a href="#index-example-of-via-handling-92">example of via handling</a>: <a href="#Vias">Vias</a></li>
+<li><a href="#index-exit-714">exit</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-exit-193">exit</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-Exporting-a-layout-140">Exporting a layout</a>: <a href="#Exporting">Exporting</a></li>
+<li><a href="#index-file-format_002c-element-data-802">file format, element data</a>: <a href="#Element-File">Element File</a></li>
+<li><a href="#index-file-format_002c-font-data-805">file format, font data</a>: <a href="#Font-File">Font File</a></li>
+<li><a href="#index-file-format_002c-layout-data-799">file format, layout data</a>: <a href="#Layout-File">Layout File</a></li>
+<li><a href="#index-file-format_002c-libraries-813">file format, libraries</a>: <a href="#Library-File">Library File</a></li>
+<li><a href="#index-file-format_002c-library-contents-810">file format, library contents</a>: <a href="#Library-Contents-File">Library Contents File</a></li>
+<li><a href="#index-file-formats-787">file formats</a>: <a href="#File-Formats">File Formats</a></li>
+<li><a href="#index-file-formats_002c-pads-and-lines-796">file formats, pads and lines</a>: <a href="#Pad-and-Line-Representation">Pad and Line Representation</a></li>
+<li><a href="#index-file-load-command-408">file load command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-file-save-command-523">file save command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-File-sytax-814">File sytax</a>: <a href="#File-Syntax">File Syntax</a></li>
+<li><a href="#index-File_002c-popup-menu-25">File, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-flags_002c-changing-598">flags, changing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-flags_002c-clearing-625">flags, clearing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-flags_002c-setting-735">flags, setting</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-font-command-420">font command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-font-file_002c-format-of-803">font file, format of</a>: <a href="#Font-File">Font File</a></li>
+<li><a href="#index-font-files-421">font files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-font_002c-an-overview-5">font, an overview</a>: <a href="#Symbol-Objects">Symbol Objects</a></li>
+<li><a href="#index-font_002c-used-for-pin-names-485">font, used for pin names</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-format-of-element-files-801">format of element files</a>: <a href="#Element-File">Element File</a></li>
+<li><a href="#index-format-of-font-files-804">format of font files</a>: <a href="#Font-File">Font File</a></li>
+<li><a href="#index-format-of-layout-files-798">format of layout files</a>: <a href="#Layout-File">Layout File</a></li>
+<li><a href="#index-format-of-libraries-812">format of libraries</a>: <a href="#Library-File">Library File</a></li>
+<li><a href="#index-format-of-library-contents-809">format of library contents</a>: <a href="#Library-Contents-File">Library Contents File</a></li>
+<li><a href="#index-FreeBSD-855">FreeBSD</a>: <a href="#BSD">BSD</a></li>
+<li><a href="#index-g_002dcode-145">g-code</a>: <a href="#gcode">gcode</a></li>
+<li><a href="#index-gcode-144">gcode</a>: <a href="#gcode">gcode</a></li>
+<li><a href="#index-gEDA_002c-how-to-interface-with-820">gEDA, how to interface with</a>: <a href="#gEDA">gEDA</a></li>
+<li><a href="#index-gerber-147">gerber</a>: <a href="#gerber">gerber</a></li>
+<li><a href="#index-glossary-889">glossary</a>: <a href="#Glossary">Glossary</a></li>
+<li><a href="#index-GNU-build-system-825">GNU build system</a>: <a href="#quickstart">quickstart</a></li>
+<li><a href="#index-GNU-configure-script-826">GNU configure script</a>: <a href="#running-configure">running configure</a></li>
+<li><a href="#index-grid-372">grid</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-grid-66">grid</a>: <a href="#Layout-Area">Layout Area</a></li>
+<li><a href="#index-grid-color-441">grid color</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-grid_002c-absolute-and-relative-659">grid, absolute and relative</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-grid_002c-alignment-33">grid, alignment</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-grid_002c-display-660">grid, display</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-grid_002c-display-32">grid, display</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-grid_002c-setting-of-742">grid, setting of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-groups-451">groups</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-groups_002c-editing-of-674">groups, editing of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-gschem_002c-how-to-interface-with-819">gschem, how to interface with</a>: <a href="#gEDA">gEDA</a></li>
+<li><a href="#index-Hewlett-Packard-838">Hewlett Packard</a>: <a href="#HP">HP</a></li>
+<li><a href="#index-hide-element-name-753">hide element name</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-how-to-start-22">how to start</a>: <a href="#Getting-Started">Getting Started</a></li>
+<li><a href="#index-HP-837">HP</a>: <a href="#HP">HP</a></li>
+<li><a href="#index-image-export-150">image export</a>: <a href="#png">png</a></li>
+<li><a href="#index-index-of-terms-891">index of terms</a>: <a href="#Glossary">Glossary</a></li>
+<li><a href="#index-Info_002c-popup-menu-51">Info, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-information-about-objects-723">information about objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-input_002dfield_002c-position-of-58">input-field, position of</a>: <a href="#Status_002dline-and-Input_002dfield">Status-line and Input-field</a></li>
+<li><a href="#index-inputfield_002c-saving-entered-command_002dline-538">inputfield, saving entered command-line</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-inputfield_002c-start-user-input-632">inputfield, start user input</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-install_002c-how-to-823">install, how to</a>: <a href="#compiling">compiling</a></li>
+<li><a href="#index-key-translations-573">key translations</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-keyboard-bell-515">keyboard bell</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layer-controls-59">layer controls</a>: <a href="#Layer-Controls">Layer Controls</a></li>
+<li><a href="#index-layer-groups-12">layer groups</a>: <a href="#Layer-Objects">Layer Objects</a></li>
+<li><a href="#index-layer-visibility_002c-toggling-761">layer visibility, toggling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-layer_002c-change-active-751">layer, change active</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-layer_002c-name-of-453">layer, name of</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layers_002c-an-overview-11">layers, an overview</a>: <a href="#Layer-Objects">Layer Objects</a></li>
+<li><a href="#index-layers_002c-changing-which-is-active-61">layers, changing which is active</a>: <a href="#Layer-Controls">Layer Controls</a></li>
+<li><a href="#index-layers_002c-colors-448">layers, colors</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layers_002c-editing-of-groups-673">layers, editing of groups</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-layers_002c-groups-450">layers, groups</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layers_002c-switching-on_002foff-60">layers, switching on/off</a>: <a href="#Layer-Controls">Layer Controls</a></li>
+<li><a href="#index-layout-files-409">layout files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layout-files_002c-format-of-797">layout files, format of</a>: <a href="#Layout-File">Layout File</a></li>
+<li><a href="#index-layout-files_002c-saving-of-197">layout files, saving of</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-layout-objects_002c-an-overview-3">layout objects, an overview</a>: <a href="#Intro">Intro</a></li>
+<li><a href="#index-layout_002c-default-size-of-544">layout, default size of</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-layout_002c-loading-a-183">layout, loading a</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-layout_002c-loading-to-buffer-190">layout, loading to buffer</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-layout_002c-merging-a-191">layout, merging a</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-layout_002c-printing-a-710">layout, printing a</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-layout_002c-start-a-new-696">layout, start a new</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-layout_002dname-200">layout-name</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-layout_002dname-8">layout-name</a>: <a href="#Element-Objects">Element Objects</a></li>
+<li><a href="#index-length-of-a-pin-name-490">length of a pin name</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-library-accuracy-10">library accuracy</a>: <a href="#Element-Objects">Element Objects</a></li>
+<li><a href="#index-library-command-455">library command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-library-contents-command-459">library contents command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-library-contents-file_002c-format-of-808">library contents file, format of</a>: <a href="#Library-Contents-File">Library Contents File</a></li>
+<li><a href="#index-library-creation-816">library creation</a>: <a href="#Library-Creation">Library Creation</a></li>
+<li><a href="#index-library-file_002c-format-of-811">library file, format of</a>: <a href="#Library-File">Library File</a></li>
+<li><a href="#index-library-name-464">library name</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-library-searchpath-468">library searchpath</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-library-window-69">library window</a>: <a href="#Library-Window">Library Window</a></li>
+<li><a href="#index-lines_002c-an-example-81">lines, an example</a>: <a href="#Lines">Lines</a></li>
+<li><a href="#index-lines_002c-an-overview-13">lines, an overview</a>: <a href="#Line-Objects">Line Objects</a></li>
+<li><a href="#index-lines_002c-clipping-to-45-degree-664">lines, clipping to 45 degree</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-lines_002c-clipping-to-45-degree-376">lines, clipping to 45 degree</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-lines_002c-setting-of-initial-size-745">lines, setting of initial size</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-lines_002c-size-470">lines, size</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Linux-853">Linux</a>: <a href="#Linux">Linux</a></li>
+<li><a href="#index-listing-library-contents-460">listing library contents</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-loading-a-layout-to-buffer-188">loading a layout to buffer</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-loading-elements-398">loading elements</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-loading-elements-to-buffer-185">loading elements to buffer</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-loading-files-679">loading files</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-loading-fonts-422">loading fonts</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-loading-layouts-410">loading layouts</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-loading-layouts-182">loading layouts</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-loading-symbols-423">loading symbols</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-loading_002c-an-example-114">loading, an example</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-log-window-503">log window</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-log-window-67">log window</a>: <a href="#Log-Window">Log Window</a></li>
+<li><a href="#index-m4-401">m4</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-m4_002c-preprocessing-example-files-96">m4, preprocessing example files</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-mark-684">mark</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Measuring-distances-172">Measuring distances</a>: <a href="#Measuring-distances">Measuring distances</a></li>
+<li><a href="#index-media-474">media</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-media-margin-475">media margin</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-media_002c-size-of-135">media, size of</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-menus-23">menus</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-merging-layouts-189">merging layouts</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-messages-504">messages</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-messages-68">messages</a>: <a href="#Log-Window">Log Window</a></li>
+<li><a href="#index-mirroring-printout-129">mirroring printout</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-mode-selection-63">mode selection</a>: <a href="#Tool-Selectors">Tool Selectors</a></li>
+<li><a href="#index-mode_002c-selecting-of-687">mode, selecting of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-mounting-holes-604">mounting holes</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-move-519">move</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-move-an-object-785">move an object</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-moving-objects-158">moving objects</a>: <a href="#Arrow-Tool">Arrow Tool</a></li>
+<li><a href="#index-moving-objects-to-current-layer-694">moving objects to current layer</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-moving_002c-an-example-108">moving, an example</a>: <a href="#Moving-and-Copying">Moving and Copying</a></li>
+<li><a href="#index-moving_002c-traces-to-a-different-layer-112">moving, traces to a different layer</a>: <a href="#Moving-and-Copying">Moving and Copying</a></li>
+<li><a href="#index-multi_002dfile-333">multi-file</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-name-of-an-element-656">name of an element</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-name_002c-change-an-objects-606">name, change an objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-namelength-of-pins-488">namelength of pins</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-nelma-148">nelma</a>: <a href="#nelma">nelma</a></li>
+<li><a href="#index-NetBSD-856">NetBSD</a>: <a href="#BSD">BSD</a></li>
+<li><a href="#index-netlist-578">netlist</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-netlist-507">netlist</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-netlist-162">netlist</a>: <a href="#Rats-Nest">Rats Nest</a></li>
+<li><a href="#index-netlist-21">netlist</a>: <a href="#Net-Objects">Net Objects</a></li>
+<li><a href="#index-Netlist-Window-70">Netlist Window</a>: <a href="#Netlist-Window">Netlist Window</a></li>
+<li><a href="#index-netlist_002c-file-format-806">netlist, file format</a>: <a href="#Netlist-File">Netlist File</a></li>
+<li><a href="#index-netlist_002c-reading-807">netlist, reading</a>: <a href="#Netlist-File">Netlist File</a></li>
+<li><a href="#index-object-report-53">object report</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-object_002c-change-name-of-608">object, change name of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-object_002c-changing-the-size-of-an-80">object, changing the size of an</a>: <a href="#Common">Common</a></li>
+<li><a href="#index-object_002c-copy-an-784">object, copy an</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-object_002c-creating-an-76">object, creating an</a>: <a href="#Common">Common</a></li>
+<li><a href="#index-object_002c-drawing-and-removing-74">object, drawing and removing</a>: <a href="#Drawing-and-Removing">Drawing and Removing</a></li>
+<li><a href="#index-object_002c-move-an-783">object, move an</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-object_002c-removing-an-776">object, removing an</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-object_002c-removing-an-78">object, removing an</a>: <a href="#Common">Common</a></li>
+<li><a href="#index-objects_002c-moving-to-current-layer-693">objects, moving to current layer</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-octagonal-flag_002c-changing-599">octagonal flag, changing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-octagonal-flag_002c-clearing-627">octagonal flag, clearing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-octagonal-flag_002c-setting-736">octagonal flag, setting</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-octagonal-pins-and-vias-612">octagonal pins and vias</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-off-limit-color-479">off limit color</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-offset-of-pinnames-496">offset of pinnames</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-offset-of-pinout-493">offset of pinout</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-offset-of-printout-136">offset of printout</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-old-library-9">old library</a>: <a href="#Element-Objects">Element Objects</a></li>
+<li><a href="#index-only_002dvisible-345">only-visible</a>: <a href="#Encapsulated-Postscript-Export">Encapsulated Postscript Export</a></li>
+<li><a href="#index-OpenWindows-842">OpenWindows</a>: <a href="#Sun">Sun</a></li>
+<li><a href="#index-operation-modes_002c-selecting-of-688">operation modes, selecting of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-optimizer-169">optimizer</a>: <a href="#Trace-Optimizer">Trace Optimizer</a></li>
+<li><a href="#index-outline-printout-131">outline printout</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-output-device-127">output device</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-overlap_002c-minimum-167">overlap, minimum</a>: <a href="#Design-Rule-Checking">Design Rule Checking</a></li>
+<li><a href="#index-pad-specification-795">pad specification</a>: <a href="#Pad-and-Line-Representation">Pad and Line Representation</a></li>
+<li><a href="#index-pastebuffer_002c-an-example-104">pastebuffer, an example</a>: <a href="#Pastebuffer">Pastebuffer</a></li>
+<li><a href="#index-pastebuffer_002c-convert-contents-to-element-102">pastebuffer, convert contents to element</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-pastebuffer_002c-popup-menu-46">pastebuffer, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-pastebuffer_002c-selecting-a-700">pastebuffer, selecting a</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-path-for-element-files-404">path for element files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-path-for-font-files-432">path for font files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-path-for-layout-files-416">path for layout files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-path-for-libraries-467">path for libraries</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-PC-UNIX-857">PC UNIX</a>: <a href="#BSD">BSD</a></li>
+<li><a href="#index-PC-UNIX-854">PC UNIX</a>: <a href="#Linux">Linux</a></li>
+<li><a href="#index-PC-UNIX-851">PC UNIX</a>: <a href="#SCO">SCO</a></li>
+<li><a href="#index-PCB_002c-an-overview-2">PCB, an overview</a>: <a href="#Overview">Overview</a></li>
+<li><a href="#index-photo_002dmode-358">photo-mode</a>: <a href="#PNG-Options">PNG Options</a></li>
+<li><a href="#index-pin-color-483">pin color</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pin_002c-name-of-489">pin, name of</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pin_002fpad-names_002c-changing-615">pin/pad names, changing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-pinout_002c-display-of-662">pinout, display of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-pinout_002c-font-to-display-pin-names-486">pinout, font to display pin names</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pinout_002c-zoomfactor-of-display-498">pinout, zoomfactor of display</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-pins_002c-an-example-98">pins, an example</a>: <a href="#Elements">Elements</a></li>
+<li><a href="#index-pins_002c-changing-shape-of-610">pins, changing shape of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-png-149">png</a>: <a href="#png">png</a></li>
+<li><a href="#index-pointer_002c-moving-of-690">pointer, moving of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-polygon-546">polygon</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-polygon-point_002c-go-back-to-previous-707">polygon point, go back to previous</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-polygon_002c-an-example-84">polygon, an example</a>: <a href="#Polygons">Polygons</a></li>
+<li><a href="#index-polygon_002c-an-overview-17">polygon, an overview</a>: <a href="#Polygon-Objects">Polygon Objects</a></li>
+<li><a href="#index-polygon_002c-closing-a-706">polygon, closing a</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-popping-up-menus-24">popping up menus</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-postprocessing-layout-data-526">postprocessing layout data</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-postscript-152">postscript</a>: <a href="#ps">ps</a></li>
+<li><a href="#index-preprocessing-element-data-399">preprocessing element data</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-preprocessing-font-data-424">preprocessing font data</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-preprocessing-layout-data-411">preprocessing layout data</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-preventing-loss-of-data-532">preventing loss of data</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-preventing-loss-of-data-120">preventing loss of data</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-print-command-139">print command</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-print-media-476">print media</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-print-media-134">print media</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-print-offset-137">print offset</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-printing-501">printing</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-printing-a-layout-711">printing a layout</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-printing_002c-an-example-124">printing, an example</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-problems-834">problems</a>: <a href="#problems">problems</a></li>
+<li><a href="#index-ps-151">ps</a>: <a href="#ps">ps</a></li>
+<li><a href="#index-ps_002dbloat-325">ps-bloat</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-ps_002dinvert-327">ps-invert</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-psfade-330">psfade</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-quit-713">quit</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-quit-194">quit</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-rat_0027s-nest-199">rat's nest</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-rat_002dline-579">rat-line</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-rat_002dline-163">rat-line</a>: <a href="#Rats-Nest">Rats Nest</a></li>
+<li><a href="#index-rats-nest-577">rats nest</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-rats-nest-506">rats nest</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-rats-nest-161">rats nest</a>: <a href="#Rats-Nest">Rats Nest</a></li>
+<li><a href="#index-rats_002dnest-20">rats-nest</a>: <a href="#Net-Objects">Net Objects</a></li>
+<li><a href="#index-recover-717">recover</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-rectangle_002c-an-example-86">rectangle, an example</a>: <a href="#Polygons">Polygons</a></li>
+<li><a href="#index-redo-716">redo</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-redo-28">redo</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-redrawing-layout-654">redrawing layout</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-refreshing-layout-655">refreshing layout</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Regular-Expressions-861">Regular Expressions</a>: <a href="#Regular-Expressions">Regular Expressions</a></li>
+<li><a href="#index-removing-connections-775">removing connections</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-removing-objects-774">removing objects</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-removing-objects-77">removing objects</a>: <a href="#Common">Common</a></li>
+<li><a href="#index-removing-objects-72">removing objects</a>: <a href="#Drawing-and-Removing">Drawing and Removing</a></li>
+<li><a href="#index-removing-selected-objects-719">removing selected objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-report-722">report</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-report-52">report</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-reseting-found-connections-637">reseting found connections</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-reseting-found-connections-513">reseting found connections</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-resources-369">resources</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-rotate-521">rotate</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-rotating-a-buffer-702">rotating a buffer</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-rotating-printout-128">rotating printout</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-routing-style-726">routing style</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-routing-style-517">routing style</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-rubber-band-39">rubber band</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-rubberband-661">rubberband</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-rubberband-520">rubberband</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-saving-connections-730">saving connections</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-saving-files-729">saving files</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-saving-found-connections-639">saving found connections</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-saving-last-entered-user-command-537">saving last entered user command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-saving-layouts-525">saving layouts</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-saving-layouts-196">saving layouts</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-saving-layouts-119">saving layouts</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-saving_002c-an-example-115">saving, an example</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-scaling-a-printout-133">scaling a printout</a>: <a href="#Printing">Printing</a></li>
+<li><a href="#index-scanning-connections-634">scanning connections</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-schematic-capture-817">schematic capture</a>: <a href="#Schematic-Frontends">Schematic Frontends</a></li>
+<li><a href="#index-schematic-frontend-818">schematic frontend</a>: <a href="#Schematic-Frontends">Schematic Frontends</a></li>
+<li><a href="#index-SCO-850">SCO</a>: <a href="#SCO">SCO</a></li>
+<li><a href="#index-Screen_002c-popup-menu-29">Screen, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-script-file_002c-executing-671">script file, executing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-scrolling-778">scrolling</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-searching-connections-635">searching connections</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Searching-for-elements-170">Searching for elements</a>: <a href="#Searching-for-elements">Searching for elements</a></li>
+<li><a href="#index-searchpath-for-element-files-403">searchpath for element files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-searchpath-for-font-files-431">searchpath for font files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-searchpath-for-layout-files-415">searchpath for layout files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-searchpath-for-libraries-466">searchpath for libraries</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Select_002c-popup-menu-42">Select, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-selected-object_002c-removing-an-720">selected object, removing an</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-selected-objects_002c-changing-sizes-44">selected objects, changing sizes</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-selected-objects_002c-removing-43">selected objects, removing</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-selecting-a-buffer-701">selecting a buffer</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-selecting-a-new-tool-65">selecting a new tool</a>: <a href="#Tool-Selectors">Tool Selectors</a></li>
+<li><a href="#index-selecting-objects-733">selecting objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-selecting_002c-using-the-arrow-tool-157">selecting, using the arrow tool</a>: <a href="#Arrow-Tool">Arrow Tool</a></li>
+<li><a href="#index-selection-732">selection</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Settings_002c-popup-menu-37">Settings, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-SGI-845">SGI</a>: <a href="#SGI">SGI</a></li>
+<li><a href="#index-show_002dlegend-338">show-legend</a>: <a href="#Postscript-Export">Postscript Export</a></li>
+<li><a href="#index-shrink-540">shrink</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-signal-587">signal</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Silicon-Graphics-844">Silicon Graphics</a>: <a href="#SGI">SGI</a></li>
+<li><a href="#index-size-of-lines-471">size of lines</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-size-of-lines-and-vias-727">size of lines and vias</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-size-of-vias-561">size of vias</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-sizes_002c-changing-of-objects-591">sizes, changing of objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Sizes_002c-popup-menu-36">Sizes, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-snap-to-pins-40">snap to pins</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-Solaris-841">Solaris</a>: <a href="#Sun">Sun</a></li>
+<li><a href="#index-solder-mask_002c-viewing-and-editing-35">solder mask, viewing and editing</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-spacing_002c-minimum-166">spacing, minimum</a>: <a href="#Design-Rule-Checking">Design Rule Checking</a></li>
+<li><a href="#index-speaker-volume-564">speaker volume</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-square-flag_002c-changing-600">square flag, changing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-square-flag_002c-changing-of-objects-622">square flag, changing of objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-square-flag_002c-clearing-628">square flag, clearing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-square-flag_002c-setting-737">square flag, setting</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-standard-drill-sizes-865">standard drill sizes</a>: <a href="#Standard-Drill-Sizes">Standard Drill Sizes</a></li>
+<li><a href="#index-start-user-input-631">start user input</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-starting-a-new-layout-697">starting a new layout</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-starting-_0040pcb_007b_007d-207">starting <code>pcb-rnd</code></a>: <a href="#Command_002dLine-Options">Command-Line Options</a></li>
+<li><a href="#index-status-information-56">status information</a>: <a href="#Status_002dline-and-Input_002dfield">Status-line and Input-field</a></li>
+<li><a href="#index-strings_002c-an-example-89">strings, an example</a>: <a href="#Text">Text</a></li>
+<li><a href="#index-strings_002c-an-overview-19">strings, an overview</a>: <a href="#Text-Objects">Text Objects</a></li>
+<li><a href="#index-Sun-840">Sun</a>: <a href="#Sun">Sun</a></li>
+<li><a href="#index-symbols-429">symbols</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-symbols_002c-an-overview-4">symbols, an overview</a>: <a href="#Symbol-Objects">Symbol Objects</a></li>
+<li><a href="#index-Syntax_002c-file-815">Syntax, file</a>: <a href="#File-Syntax">File Syntax</a></li>
+<li><a href="#index-temporary-files-533">temporary files</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-temporary-files-123">temporary files</a>: <a href="#Loading-and-Saving">Loading and Saving</a></li>
+<li><a href="#index-terminology-890">terminology</a>: <a href="#Glossary">Glossary</a></li>
+<li><a href="#index-TeX_002c-problems-859">TeX, problems</a>: <a href="#TeX-and-Manuals">TeX and Manuals</a></li>
+<li><a href="#index-text_002c-an-example-88">text, an example</a>: <a href="#Text">Text</a></li>
+<li><a href="#index-text_002c-an-overview-18">text, an overview</a>: <a href="#Text-Objects">Text Objects</a></li>
+<li><a href="#index-text_002c-default-scaling-549">text, default scaling</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-thermal-flag_002c-changing-601">thermal flag, changing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-thermal-flag_002c-clearing-629">thermal flag, clearing</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-thermal-flag_002c-setting-738">thermal flag, setting</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-thickness-of-lines-472">thickness of lines</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-thickness-of-objects-79">thickness of objects</a>: <a href="#Common">Common</a></li>
+<li><a href="#index-thickness-of-vias-562">thickness of vias</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-thickness_002c-changing-of-objects-619">thickness, changing of objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-toggle-layer-visibility-760">toggle layer visibility</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-tool-selection-64">tool selection</a>: <a href="#Tool-Selectors">Tool Selectors</a></li>
+<li><a href="#index-tool_002c-arrow-160">tool, arrow</a>: <a href="#Arrow-Tool">Arrow Tool</a></li>
+<li><a href="#index-trace-optimizer-168">trace optimizer</a>: <a href="#Trace-Optimizer">Trace Optimizer</a></li>
+<li><a href="#index-translations-771">translations</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-translations-572">translations</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-troubleshooting-835">troubleshooting</a>: <a href="#problems">problems</a></li>
+<li><a href="#index-two-line-mode-14">two line mode</a>: <a href="#Line-Objects">Line Objects</a></li>
+<li><a href="#index-undo-763">undo</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-undo-27">undo</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-undo_002c-multi_002daction-resources-584">undo, multi-action resources</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-unique-names-38">unique names</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-unix-command-400">unix command</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-unselect-objects-770">unselect objects</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-user-commands-179">user commands</a>: <a href="#User-Commands">User Commands</a></li>
+<li><a href="#index-user-input-780">user input</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-vendor-drill-table-887">vendor drill table</a>: <a href="#UnloadVendor-Action">UnloadVendor Action</a></li>
+<li><a href="#index-vendor-drill-table-884">vendor drill table</a>: <a href="#ToggleVendor-Action">ToggleVendor Action</a></li>
+<li><a href="#index-vendor-drill-table-881">vendor drill table</a>: <a href="#LoadVendorFrom-Action">LoadVendorFrom Action</a></li>
+<li><a href="#index-vendor-drill-table-878">vendor drill table</a>: <a href="#EnableVendor-Action">EnableVendor Action</a></li>
+<li><a href="#index-vendor-drill-table-875">vendor drill table</a>: <a href="#DisableVendor-Action">DisableVendor Action</a></li>
+<li><a href="#index-vendor-drill-table-872">vendor drill table</a>: <a href="#ApplyVendor-Action">ApplyVendor Action</a></li>
+<li><a href="#index-vendor-drill-table-582">vendor drill table</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Vendor-drill-table-177">Vendor drill table</a>: <a href="#Vendor-drill-mapping">Vendor drill mapping</a></li>
+<li><a href="#index-vendor-drill-table_002c-disabling-646">vendor drill table, disabling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-drill-table_002c-enabling-677">vendor drill table, enabling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-drill-table_002c-loading-682">vendor drill table, loading</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-drill-table_002c-toggling-758">vendor drill table, toggling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-drill-table_002c-unloading-767">vendor drill table, unloading</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-map-886">vendor map</a>: <a href="#UnloadVendor-Action">UnloadVendor Action</a></li>
+<li><a href="#index-vendor-map-883">vendor map</a>: <a href="#ToggleVendor-Action">ToggleVendor Action</a></li>
+<li><a href="#index-vendor-map-880">vendor map</a>: <a href="#LoadVendorFrom-Action">LoadVendorFrom Action</a></li>
+<li><a href="#index-vendor-map-877">vendor map</a>: <a href="#EnableVendor-Action">EnableVendor Action</a></li>
+<li><a href="#index-vendor-map-874">vendor map</a>: <a href="#DisableVendor-Action">DisableVendor Action</a></li>
+<li><a href="#index-vendor-map-871">vendor map</a>: <a href="#ApplyVendor-Action">ApplyVendor Action</a></li>
+<li><a href="#index-vendor-map-581">vendor map</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-map_002c-disabling-645">vendor map, disabling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-map_002c-enabling-676">vendor map, enabling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-map_002c-loading-681">vendor map, loading</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-map_002c-toggling-757">vendor map, toggling</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vendor-map_002c-unloading-766">vendor map, unloading</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-Vendor-mapping-175">Vendor mapping</a>: <a href="#Vendor-drill-mapping">Vendor drill mapping</a></li>
+<li><a href="#index-Vendor-rules-174">Vendor rules</a>: <a href="#Vendor-drill-mapping">Vendor drill mapping</a></li>
+<li><a href="#index-vias_002c-an-example-91">vias, an example</a>: <a href="#Vias">Vias</a></li>
+<li><a href="#index-vias_002c-an-overview-6">vias, an overview</a>: <a href="#Via-Objects">Via Objects</a></li>
+<li><a href="#index-vias_002c-changing-shape-of-611">vias, changing shape of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vias_002c-color-557">vias, color</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-vias_002c-converting-to-mounting-hole-603">vias, converting to mounting hole</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vias_002c-setting-of-initial-size-744">vias, setting of initial size</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-vias_002c-size-560">vias, size</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-viewing-side_002c-changing-of-748">viewing side, changing of</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-volume-of-speaker-565">volume of speaker</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-Window_002c-popup-menu-55">Window, popup menu</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-x_002dy-file-format-867">x-y file format</a>: <a href="#Centroid-File-Format">Centroid File Format</a></li>
+<li><a href="#index-x_002dy-file_002c-algorithms-869">x-y file, algorithms</a>: <a href="#Centroid-File-Format">Centroid File Format</a></li>
+<li><a href="#index-X11-368">X11</a>: <a href="#X11-Interface">X11 Interface</a></li>
+<li><a href="#index-X11-default-translations-773">X11 default translations</a>: <a href="#Translations">Translations</a></li>
+<li><a href="#index-X11-resources-370">X11 resources</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-X11-translations-575">X11 translations</a>: <a href="#Actions">Actions</a></li>
+<li><a href="#index-X11_002c-problems-858">X11, problems</a>: <a href="#X11">X11</a></li>
+<li><a href="#index-xcircuit_002c-how-to-interface-with-821">xcircuit, how to interface with</a>: <a href="#xcircuit">xcircuit</a></li>
+<li><a href="#index-zoom-of-Layout-area-570">zoom of Layout area</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-zoom-of-pinout-window-499">zoom of pinout window</a>: <a href="#Resources">Resources</a></li>
+<li><a href="#index-zoom_002c-setting-34">zoom, setting</a>: <a href="#Menu">Menu</a></li>
+<li><a href="#index-zoom_002c-setting-of-741">zoom, setting of</a>: <a href="#Actions">Actions</a></li>
+   </ul>
+<div class="contents">
+<h2>Table of Contents</h2>
+<ul>
+<li><a name="toc_Top" href="#Top">pcb-rnd</a>
+<li><a name="toc_Copying" href="#Copying">Copying</a>
+<li><a name="toc_History" href="#History">History</a>
+<li><a name="toc_Overview" href="#Overview">1 Overview</a>
+<li><a name="toc_Intro" href="#Intro">2 Introduction</a>
+<ul>
+<li><a href="#Symbol-Objects">2.1 Symbols</a>
+<li><a href="#Via-Objects">2.2 Vias</a>
+<li><a href="#Element-Objects">2.3 Elements</a>
+<li><a href="#Layer-Objects">2.4 Layers</a>
+<li><a href="#Line-Objects">2.5 Lines</a>
+<li><a href="#Arc-Objects">2.6 Arcs</a>
+<li><a href="#Polygon-Objects">2.7 Polygons</a>
+<li><a href="#Text-Objects">2.8 Text</a>
+<li><a href="#Net-Objects">2.9 Nets</a>
+</li></ul>
+<li><a name="toc_Getting-Started" href="#Getting-Started">3 Getting Started</a>
+<ul>
+<li><a href="#Application-Window">3.1 The Application Window</a>
+<ul>
+<li><a href="#Menu">3.1.1 Menus</a>
+<li><a href="#Status_002dline-and-Input_002dfield">3.1.2 The Status-line and Input-field</a>
+<li><a href="#Layer-Controls">3.1.3 The Layer Controls</a>
+<li><a href="#Tool-Selectors">3.1.4 The Tool Selectors</a>
+<li><a href="#Layout-Area">3.1.5 Layout Area</a>
+</li></ul>
+<li><a href="#Log-Window">3.2 Log Window</a>
+<li><a href="#Library-Window">3.3 Library Window</a>
+<li><a href="#Netlist-Window">3.4 Netlist Window</a>
+<li><a href="#Drawing-and-Removing">3.5 Drawing and Removing Basic Objects</a>
+<ul>
+<li><a href="#Common">3.5.1 Common Drawing and Removing Methods</a>
+<li><a href="#Lines">3.5.2 Lines</a>
+<li><a href="#Arcs">3.5.3 Arcs</a>
+<li><a href="#Polygons">3.5.4 Polygons and Rectangles</a>
+<li><a href="#Text">3.5.5 Text</a>
+<li><a href="#Vias">3.5.6 Vias</a>
+<li><a href="#Elements">3.5.7 Elements</a>
+<li><a href="#Pastebuffer">3.5.8 Pastebuffer</a>
+</li></ul>
+<li><a href="#Moving-and-Copying">3.6 Moving and Copying</a>
+<li><a href="#Loading-and-Saving">3.7 Loading and Saving</a>
+<li><a href="#Printing">3.8 Printing</a>
+<li><a href="#Exporting">3.9 Exporting a layout</a>
+<ul>
+<li><a href="#bom">3.9.1 Bill of materials (bom)</a>
+<li><a href="#gcode">3.9.2 G-code (gcode)</a>
+<li><a href="#gerber">3.9.3 Gerber (gerber)</a>
+<li><a href="#nelma">3.9.4 Nelma (nelma)</a>
+<li><a href="#png">3.9.5 Image (png)</a>
+<li><a href="#ps">3.9.6 Postscript (ps)</a>
+<li><a href="#eps">3.9.7 Encapsulated Postscript (eps)</a>
+</li></ul>
+<li><a href="#Connection-Lists">3.10 Connection Lists</a>
+<li><a href="#Arrow-Tool">3.11 Arrow Tool</a>
+<li><a href="#Rats-Nest">3.12 Rats Nest</a>
+<li><a href="#Design-Rule-Checking">3.13 Design Rule Checking</a>
+<li><a href="#Trace-Optimizer">3.14 Trace Optimizer</a>
+<li><a href="#Searching-for-elements">3.15 Searching for elements</a>
+<li><a href="#Measuring-distances">3.16 Measuring distances</a>
+<li><a href="#Vendor-drill-mapping">3.17 Vendor Drill Mapping</a>
+</li></ul>
+<li><a name="toc_Autorouter" href="#Autorouter">4 Autorouter</a>
+<li><a name="toc_User-Commands" href="#User-Commands">5 User Commands</a>
+<li><a name="toc_Command_002dLine-Options" href="#Command_002dLine-Options">6 Command-Line Options</a>
+<ul>
+<li><a href="#General-Options">6.1 General Options</a>
+<li><a href="#General-GUI-Options">6.2 General GUI Options</a>
+<li><a href="#GTK_002b-GUI-Options">6.3 GTK+ GUI Options</a>
+<li><a href="#lesstif-GUI-Options">6.4 lesstif GUI Options</a>
+<li><a href="#Colors">6.5 Colors</a>
+<li><a href="#Layer-Names">6.6 Layer Names</a>
+<li><a href="#Paths">6.7 Paths</a>
+<li><a href="#Sizes">6.8 Sizes</a>
+<li><a href="#Commands">6.9 Commands</a>
+<li><a href="#DRC-Options">6.10 DRC Options</a>
+<li><a href="#BOM-Creation">6.11 BOM Creation</a>
+<li><a href="#Gerber-Export">6.12 Gerber Export</a>
+<li><a href="#Postscript-Export">6.13 Postscript Export</a>
+<li><a href="#Encapsulated-Postscript-Export">6.14 Encapsulated Postscript Export</a>
+<li><a href="#PNG-Options">6.15 PNG Options</a>
+<li><a href="#lpr-Printing-Options">6.16 lpr Printing Options</a>
+<li><a href="#nelma-Options">6.17 nelma Options</a>
+</li></ul>
+<li><a name="toc_X11-Interface" href="#X11-Interface">7 X11 Interface</a>
+<ul>
+<li><a href="#Resources">7.1 Non-Standard X11 Application Resources</a>
+<li><a href="#Actions">7.2 Actions</a>
+<li><a href="#Translations">7.3 Default Translations</a>
+</li></ul>
+<li><a name="toc_File-Formats" href="#File-Formats">8 File Formats</a>
+<ul>
+<li><a href="#Pad-and-Line-Representation">8.1 Pad and Line Representation</a>
+<li><a href="#Layout-File">8.2 Layout File Format</a>
+<li><a href="#Element-File">8.3 Element File Format</a>
+<li><a href="#Font-File">8.4 Font File Format</a>
+<li><a href="#Netlist-File">8.5 Netlist File Format</a>
+<li><a href="#Library-Contents-File">8.6 Library Contents File Format</a>
+<li><a href="#Library-File">8.7 Library File Format</a>
+<li><a href="#File-Syntax">8.8 File Syntax</a>
+<ul>
+<li><a href="#Arc-syntax">8.8.1 Arc</a>
+<li><a href="#Attribute-syntax">8.8.2 Attribute</a>
+<li><a href="#Connect-syntax">8.8.3 Connect</a>
+<li><a href="#Cursor-syntax">8.8.4 Cursor</a>
+<li><a href="#DRC-syntax">8.8.5 DRC</a>
+<li><a href="#Element-syntax">8.8.6 Element</a>
+<li><a href="#ElementArc-syntax">8.8.7 ElementArc</a>
+<li><a href="#ElementLine-syntax">8.8.8 ElementLine</a>
+<li><a href="#FileVersion-syntax">8.8.9 FileVersion</a>
+<li><a href="#Flags-syntax">8.8.10 Flags</a>
+<li><a href="#Grid-syntax">8.8.11 Grid</a>
+<li><a href="#Groups-syntax">8.8.12 Groups</a>
+<li><a href="#Layer-syntax">8.8.13 Layer</a>
+<li><a href="#Line-syntax">8.8.14 Line</a>
+<li><a href="#Mark-syntax">8.8.15 Mark</a>
+<li><a href="#Net-syntax">8.8.16 Net</a>
+<li><a href="#Netlist-syntax">8.8.17 Netlist</a>
+<li><a href="#Pad-syntax">8.8.18 Pad</a>
+<li><a href="#PCB-syntax">8.8.19 PCB</a>
+<li><a href="#Pin-syntax">8.8.20 Pin</a>
+<li><a href="#PolyArea-syntax">8.8.21 PolyArea</a>
+<li><a href="#Polygon-syntax">8.8.22 Polygon</a>
+<li><a href="#Rat-syntax">8.8.23 Rat</a>
+<li><a href="#Styles-syntax">8.8.24 Styles</a>
+<li><a href="#Symbol-syntax">8.8.25 Symbol</a>
+<li><a href="#SymbolLine-syntax">8.8.26 SymbolLine</a>
+<li><a href="#Text-syntax">8.8.27 Text</a>
+<li><a href="#Thermal-syntax">8.8.28 Thermal</a>
+<li><a href="#Via-syntax">8.8.29 Via</a>
+</li></ul>
+<li><a href="#Object-Flags">8.9 Object Flags</a>
+<li><a href="#PCBFlags">8.10 PCBFlags</a>
+</li></ul>
+<li><a name="toc_Library-Creation" href="#Library-Creation">9 Library Creation</a>
+<ul>
+<li><a href="#Library-Creation">9.1 Old Style (m4) Libraries</a>
+<ul>
+<li><a href="#Library-Creation">9.1.1 Overview of Oldlib Operation</a>
+<li><a href="#Library-Creation">9.1.2 The Library Scripts</a>
+<ul>
+<li><a href="#Library-Creation">9.1.2.1 Scripts Used During Compilation</a>
+<li><a href="#Library-Creation">9.1.2.2 Scripts Used by PCB at Runtime</a>
+</li></ul>
+<li><a href="#Library-Creation">9.1.3 Creating an Oldlib Footprint</a>
+<li><a href="#Library-Creation">9.1.4 Troubleshooting Old Style Libraries</a>
+</li></ul>
+<li><a href="#Library-Creation">9.2 New Style Libraries</a>
+<ul>
+<li><a href="#Library-Creation">9.2.1 Creating Newlib Footprints</a>
+<li><a href="#Library-Creation">9.2.2 Modifying Newlib Footprints</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_Schematic-Frontends" href="#Schematic-Frontends">10 Schematic Capture for PCB</a>
+<ul>
+<li><a href="#gEDA">10.1 gEDA</a>
+<ul>
+<li><a href="#gEDA">10.1.1 Set Up Project Directories</a>
+<li><a href="#gEDA">10.1.2 Set Up gEDA Config Files</a>
+<li><a href="#gEDA">10.1.3 Set Up <code>gsch2pcb</code> Config Files</a>
+<li><a href="#gEDA">10.1.4 Capture Schematics Using <code>gschem</code></a>
+<li><a href="#gEDA">10.1.5 Create Any Unique PCB Footprints</a>
+<li><a href="#gEDA">10.1.6 Generate Initial PCB Design Using <code>gsch2pcb</code></a>
+<li><a href="#gEDA">10.1.7 Layout Circuit Board</a>
+<li><a href="#gEDA">10.1.8 Forward Annotation of Schematic Changes</a>
+<li><a href="#gEDA">10.1.9 Generate Photoplot Files (RS-274X)</a>
+</li></ul>
+<li><a href="#xcircuit">10.2 xcircuit</a>
+</li></ul>
+<li><a name="toc_Installation" href="#Installation">Appendix A Installation and Troubleshooting</a>
+<ul>
+<li><a href="#compiling">A.1 Compiling and Installing</a>
+<ul>
+<li><a href="#quickstart">A.1.1 Quick Start</a>
+<li><a href="#running-configure">A.1.2 Running the configure Script</a>
+</li></ul>
+<li><a href="#problems">A.2 Troubleshooting</a>
+<ul>
+<li><a href="#HP">A.2.1 HP Series 700 and 800</a>
+<li><a href="#Sun">A.2.2 Sun SPARC architecture</a>
+<li><a href="#SGI">A.2.3 Silicon Graphics</a>
+<li><a href="#DEC-Alpha">A.2.4 DEC Alpha</a>
+<li><a href="#SCO">A.2.5 SCO Unix</a>
+<li><a href="#Linux">A.2.6 Linux</a>
+<li><a href="#BSD">A.2.7 FreeBSD and NetBSD</a>
+<li><a href="#X11">A.2.8 Problems related to X11</a>
+<li><a href="#TeX-and-Manuals">A.2.9 Problems related to TeX</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_Custom-Menus" href="#Custom-Menus">Appendix B Customizing the Menus</a>
+<ul>
+<li><a href="#Resource-Syntax">B.1 Resource Syntax</a>
+<li><a href="#Menu-Definitions">B.2 Menu Definitions</a>
+<li><a href="#Menu-Files-and-Defaults">B.3 Menu Files and Defaults</a>
+</li></ul>
+<li><a name="toc_Regular-Expressions" href="#Regular-Expressions">Appendix C Element Search/Regular Expressions</a>
+<ul>
+<li><a href="#Regular-Expressions">C.1 Element Search/Regular Expressions</a>
+</li></ul>
+<li><a name="toc_Standard-Drill-Sizes" href="#Standard-Drill-Sizes">Appendix D Standard Drill Size Tables</a>
+<ul>
+<li><a href="#Standard-Drill-Sizes">D.1 American Standard Wire Size Drills</a>
+<li><a href="#Standard-Drill-Sizes">D.2 American Standard Letter Size Drills</a>
+<li><a href="#Standard-Drill-Sizes">D.3 Fractional Inch Size Drills</a>
+<li><a href="#Standard-Drill-Sizes">D.4 Metric Drills</a>
+</li></ul>
+<li><a name="toc_Centroid-File-Format" href="#Centroid-File-Format">Appendix E Centroid (X-Y) File Format</a>
+<ul>
+<li><a href="#Centroid-File-Format">E.1 Overview</a>
+<li><a href="#Centroid-File-Format">E.2 File Format</a>
+<li><a href="#Centroid-File-Format">E.3 Computation of Centroid and Rotation</a>
+</li></ul>
+<li><a name="toc_Action-Reference" href="#Action-Reference">Appendix F Action Reference</a>
+<ul>
+<li><a href="#core-actions">F.1 Core actions</a>
+<ul>
+<li><a href="#AddRats-Action">F.1.1 AddRats</a>
+<li><a href="#ApplyVendor-Action">F.1.2 ApplyVendor</a>
+<li><a href="#Atomic-Action">F.1.3 Atomic</a>
+<li><a href="#Attributes-Action">F.1.4 Attributes</a>
+<li><a href="#AutoPlaceSelected-Action">F.1.5 AutoPlaceSelected</a>
+<li><a href="#AutoRoute-Action">F.1.6 AutoRoute</a>
+<li><a href="#ChangeClearSize-Action">F.1.7 ChangeClearSize</a>
+<li><a href="#ChangeDrillSize-Action">F.1.8 ChangeDrillSize</a>
+<li><a href="#ChangeFlag-Action">F.1.9 ChangeFlag</a>
+<li><a href="#ChangeHole-Action">F.1.10 ChangeHole</a>
+<li><a href="#ChangeJoin-Action">F.1.11 ChangeJoin</a>
+<li><a href="#ChangeName-Action">F.1.12 ChangeName</a>
+<li><a href="#ChangeOctagon-Action">F.1.13 ChangeOctagon</a>
+<li><a href="#ChangePaste-Action">F.1.14 ChangePaste</a>
+<li><a href="#ChangePinName-Action">F.1.15 ChangePinName</a>
+<li><a href="#ChangeSize-Action">F.1.16 ChangeSize</a>
+<li><a href="#ChangeSquare-Action">F.1.17 ChangeSquare</a>
+<li><a href="#ClearOctagon-Action">F.1.18 ClearOctagon</a>
+<li><a href="#ClearSquare-Action">F.1.19 ClearSquare</a>
+<li><a href="#ClrFlag-Action">F.1.20 ClrFlag</a>
+<li><a href="#Connection-Action">F.1.21 Connection</a>
+<li><a href="#Delete-Action">F.1.22 Delete</a>
+<li><a href="#DeleteRats-Action">F.1.23 DeleteRats</a>
+<li><a href="#DisableVendor-Action">F.1.24 DisableVendor</a>
+<li><a href="#DisperseElements-Action">F.1.25 DisperseElements</a>
+<li><a href="#Display-Action">F.1.26 Display</a>
+<li><a href="#djopt-Action">F.1.27 djopt</a>
+<li><a href="#DRC-Action">F.1.28 DRC</a>
+<li><a href="#DumpLibrary-Action">F.1.29 DumpLibrary</a>
+<li><a href="#elementlist-Action">F.1.30 elementlist</a>
+<li><a href="#elementsetattr-Action">F.1.31 elementsetattr</a>
+<li><a href="#EnableVendor-Action">F.1.32 EnableVendor</a>
+<li><a href="#execcommand-Action">F.1.33 execcommand</a>
+<li><a href="#ExecuteFile-Action">F.1.34 ExecuteFile</a>
+<li><a href="#Flip-Action">F.1.35 Flip</a>
+<li><a href="#FontEdit-Action">F.1.36 FontEdit</a>
+<li><a href="#FontSave-Action">F.1.37 FontSave</a>
+<li><a href="#FreeRotateBuffer-Action">F.1.38 FreeRotateBuffer</a>
+<li><a href="#GlobalPuller-Action">F.1.39 GlobalPuller</a>
+<li><a href="#h-Action">F.1.40 h</a>
+<li><a href="#Import-Action">F.1.41 Import</a>
+<li><a href="#l-Action">F.1.42 l</a>
+<li><a href="#le-Action">F.1.43 le</a>
+<li><a href="#LoadFootprint-Action">F.1.44 LoadFootprint</a>
+<li><a href="#LoadFrom-Action">F.1.45 LoadFrom</a>
+<li><a href="#LoadVendorFrom-Action">F.1.46 LoadVendorFrom</a>
+<li><a href="#m-Action">F.1.47 m</a>
+<li><a href="#MarkCrosshair-Action">F.1.48 MarkCrosshair</a>
+<li><a href="#Message-Action">F.1.49 Message</a>
+<li><a href="#MinClearGap-Action">F.1.50 MinClearGap</a>
+<li><a href="#MinMaskGap-Action">F.1.51 MinMaskGap</a>
+<li><a href="#Mode-Action">F.1.52 Mode</a>
+<li><a href="#MorphPolygon-Action">F.1.53 MorphPolygon</a>
+<li><a href="#MoveLayer-Action">F.1.54 MoveLayer</a>
+<li><a href="#MoveObject-Action">F.1.55 MoveObject</a>
+<li><a href="#MoveToCurrentLayer-Action">F.1.56 MoveToCurrentLayer</a>
+<li><a href="#Netlist-Action">F.1.57 Netlist</a>
+<li><a href="#New-Action">F.1.58 New</a>
+<li><a href="#OptAutoOnly-Action">F.1.59 OptAutoOnly</a>
+<li><a href="#PasteBuffer-Action">F.1.60 PasteBuffer</a>
+<li><a href="#Polygon-Action">F.1.61 Polygon</a>
+<li><a href="#Puller-Action">F.1.62 Puller</a>
+<li><a href="#q-Action">F.1.63 q</a>
+<li><a href="#q_0021-Action">F.1.64 q!</a>
+<li><a href="#Quit-Action">F.1.65 Quit</a>
+<li><a href="#Redo-Action">F.1.66 Redo</a>
+<li><a href="#RemoveSelected-Action">F.1.67 RemoveSelected</a>
+<li><a href="#Renumber-Action">F.1.68 Renumber</a>
+<li><a href="#Report-Action">F.1.69 Report</a>
+<li><a href="#ReportDialog-Action">F.1.70 ReportDialog</a>
+<li><a href="#RipUp-Action">F.1.71 RipUp</a>
+<li><a href="#rn-Action">F.1.72 rn</a>
+<li><a href="#RouteStyle-Action">F.1.73 RouteStyle</a>
+<li><a href="#s-Action">F.1.74 s</a>
+<li><a href="#SaveSettings-Action">F.1.75 SaveSettings</a>
+<li><a href="#SaveTo-Action">F.1.76 SaveTo</a>
+<li><a href="#Select-Action">F.1.77 Select</a>
+<li><a href="#SetFlag-Action">F.1.78 SetFlag</a>
+<li><a href="#SetOctagon-Action">F.1.79 SetOctagon</a>
+<li><a href="#SetSame-Action">F.1.80 SetSame</a>
+<li><a href="#SetSquare-Action">F.1.81 SetSquare</a>
+<li><a href="#SetThermal-Action">F.1.82 SetThermal</a>
+<li><a href="#SetValue-Action">F.1.83 SetValue</a>
+<li><a href="#ToggleHideName-Action">F.1.84 ToggleHideName</a>
+<li><a href="#ToggleVendor-Action">F.1.85 ToggleVendor</a>
+<li><a href="#Undo-Action">F.1.86 Undo</a>
+<li><a href="#UnloadVendor-Action">F.1.87 UnloadVendor</a>
+<li><a href="#Unselect-Action">F.1.88 Unselect</a>
+<li><a href="#w-Action">F.1.89 w</a>
+<li><a href="#wq-Action">F.1.90 wq</a>
+</li></ul>
+<li><a href="#common-actions">F.2 common actions</a>
+<ul>
+<li><a href="#LayersChanged-Action">F.2.1 LayersChanged</a>
+<li><a href="#LibraryChanged-Action">F.2.2 LibraryChanged</a>
+<li><a href="#NetlistChanged-Action">F.2.3 NetlistChanged</a>
+<li><a href="#PCBChanged-Action">F.2.4 PCBChanged</a>
+<li><a href="#RouteStylesChanged-Action">F.2.5 RouteStylesChanged</a>
+</li></ul>
+<li><a href="#gtk-actions">F.3 gtk actions</a>
+<ul>
+<li><a href="#gtk-About-Action">F.3.1 gtk About</a>
+<li><a href="#gtk-AdjustStyle-Action">F.3.2 gtk AdjustStyle</a>
+<li><a href="#gtk-Center-Action">F.3.3 gtk Center</a>
+<li><a href="#gtk-Cursor-Action">F.3.4 gtk Cursor</a>
+<li><a href="#gtk-DoWindows-Action">F.3.5 gtk DoWindows</a>
+<li><a href="#gtk-EditLayerGroups-Action">F.3.6 gtk EditLayerGroups</a>
+<li><a href="#gtk-GetXY-Action">F.3.7 gtk GetXY</a>
+<li><a href="#gtk-ImportGUI-Action">F.3.8 gtk ImportGUI</a>
+<li><a href="#gtk-Pan-Action">F.3.9 gtk Pan</a>
+<li><a href="#gtk-Popup-Action">F.3.10 gtk Popup</a>
+<li><a href="#gtk-Print-Action">F.3.11 gtk Print</a>
+<li><a href="#gtk-PrintCalibrate-Action">F.3.12 gtk PrintCalibrate</a>
+<li><a href="#gtk-Save-Action">F.3.13 gtk Save</a>
+<li><a href="#gtk-SelectLayer-Action">F.3.14 gtk SelectLayer</a>
+<li><a href="#gtk-SetUnits-Action">F.3.15 gtk SetUnits</a>
+<li><a href="#gtk-SwapSides-Action">F.3.16 gtk SwapSides</a>
+<li><a href="#gtk-ToggleView-Action">F.3.17 gtk ToggleView</a>
+<li><a href="#gtk-Zoom-Action">F.3.18 gtk Zoom</a>
+</li></ul>
+<li><a href="#lesstif-actions">F.4 lesstif actions</a>
+<ul>
+<li><a href="#lesstif-About-Action">F.4.1 lesstif About</a>
+<li><a href="#lesstif-AdjustSizes-Action">F.4.2 lesstif AdjustSizes</a>
+<li><a href="#lesstif-AdjustStyle-Action">F.4.3 lesstif AdjustStyle</a>
+<li><a href="#lesstif-Benchmark-Action">F.4.4 lesstif Benchmark</a>
+<li><a href="#lesstif-Command-Action">F.4.5 lesstif Command</a>
+<li><a href="#lesstif-Cursor-Action">F.4.6 lesstif Cursor</a>
+<li><a href="#lesstif-Debug-Action">F.4.7 lesstif Debug</a>
+<li><a href="#lesstif-DebugXY-Action">F.4.8 lesstif DebugXY</a>
+<li><a href="#lesstif-DoWindows-Action">F.4.9 lesstif DoWindows</a>
+<li><a href="#lesstif-DumpKeys-Action">F.4.10 lesstif DumpKeys</a>
+<li><a href="#lesstif-EditLayerGroups-Action">F.4.11 lesstif EditLayerGroups</a>
+<li><a href="#lesstif-Export-Action">F.4.12 lesstif Export</a>
+<li><a href="#lesstif-GetXY-Action">F.4.13 lesstif GetXY</a>
+<li><a href="#lesstif-ImportGUI-Action">F.4.14 lesstif ImportGUI</a>
+<li><a href="#lesstif-LibraryShow-Action">F.4.15 lesstif LibraryShow</a>
+<li><a href="#lesstif-Load-Action">F.4.16 lesstif Load</a>
+<li><a href="#lesstif-LoadVendor-Action">F.4.17 lesstif LoadVendor</a>
+<li><a href="#lesstif-NetlistShow-Action">F.4.18 lesstif NetlistShow</a>
+<li><a href="#lesstif-Print-Action">F.4.19 lesstif Print</a>
+<li><a href="#lesstif-PrintCalibrate-Action">F.4.20 lesstif PrintCalibrate</a>
+<li><a href="#lesstif-PromptFor-Action">F.4.21 lesstif PromptFor</a>
+<li><a href="#lesstif-Return-Action">F.4.22 lesstif Return</a>
+<li><a href="#lesstif-Save-Action">F.4.23 lesstif Save</a>
+<li><a href="#lesstif-SelectLayer-Action">F.4.24 lesstif SelectLayer</a>
+<li><a href="#lesstif-SetUnits-Action">F.4.25 lesstif SetUnits</a>
+<li><a href="#lesstif-SwapSides-Action">F.4.26 lesstif SwapSides</a>
+<li><a href="#lesstif-ToggleView-Action">F.4.27 lesstif ToggleView</a>
+<li><a href="#lesstif-Zoom-Action">F.4.28 lesstif Zoom</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_Glossary" href="#Glossary">Appendix G Glossary</a>
+<li><a name="toc_Index" href="#Index">Index of Resources</a>
+<li><a name="toc_Index" href="#Index">Index of Actions, Commands and Options</a>
+<li><a name="toc_Index" href="#Index">Index of Concepts</a>
+</li></ul>
+</div>
+
+</body></html>
+
diff --git a/doc-orig/pcbfile.texi b/doc-orig/pcbfile.texi
new file mode 100644
index 0000000..e4f79ff
--- /dev/null
+++ b/doc-orig/pcbfile.texi
@@ -0,0 +1,946 @@
+ at c key pcbfile
+ at c ./../src/parse_y.y 143
+
+A special note about units: Older versions of @code{pcb} used mils
+(1/1000 inch) as the base unit; a value of 500 in the file meant
+half an inch.  Newer versions uses a "high resolution" syntax,
+where the base unit is 1/100 of a mil (0.000010 inch); a value of 500 in
+the file means 5 mils.  As a general rule, the variants of each entry
+listed below which use square brackets are the high resolution formats
+and use the 1/100 mil units, and the ones with parentheses are the older
+variants and use 1 mil units.  Note that when multiple variants
+are listed, the most recent (and most preferred) format is the first
+listed.
+
+Symbolic and numeric flags (SFlags and NFlags) are described in
+ at ref{Object Flags}.
+
+ at menu
+* Arc syntax::
+* Attribute syntax::
+* Connect syntax::
+* Cursor syntax::
+* DRC syntax::
+* Element syntax::
+* ElementArc syntax::
+* ElementLine syntax::
+* FileVersion syntax::
+* Flags syntax::
+* Grid syntax::
+* Groups syntax::
+* Layer syntax::
+* Line syntax::
+* Mark syntax::
+* Net syntax::
+* Netlist syntax::
+* Pad syntax::
+* PCB syntax::
+* Pin syntax::
+* PolyArea syntax::
+* Polygon syntax::
+* Rat syntax::
+* Styles syntax::
+* Symbol syntax::
+* SymbolLine syntax::
+* Text syntax::
+* Thermal syntax::
+* Via syntax::
+ at end menu
+ at c pcbfile Arc
+ at node Arc syntax
+ at subsection Arc
+ at c ./../src/parse_y.y 660
+
+ at cartouche
+ at format
+Arc [X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]
+Arc (X Y Width Height Thickness Clearance StartAngle DeltaAngle NFlags)
+Arc (X Y Width Height Thickness StartAngle DeltaAngle NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X Y
+Coordinates of the center of the arc.
+ at item Width Height
+The width and height, from the center to the edge.  The bounds of the
+circle of which this arc is a segment, is thus @math{2*Width} by
+ at math{2*Height}.
+ at item Thickness
+The width of the copper trace which forms the arc.
+ at item Clearance
+The amount of space cleared around the arc when the line passes
+through a polygon.  The clearance is added to the thickness to get the
+thickness of the clear; thus the space between the arc and the polygon
+is @math{Clearance/2} wide.
+ at item StartAngle
+The angle of one end of the arc, in degrees.  In PCB, an angle of zero
+points left (negative X direction), and 90 degrees points down
+(positive Y direction).
+ at item DeltaAngle
+The sweep of the arc.  This may be negative.  Positive angles sweep
+counterclockwise.
+ at item SFlags
+Symbolic or numeric flags.
+ at item NFlags
+Numeric flags.
+ at end table
+
+ at c pcbfile Attribute
+ at node Attribute syntax
+ at subsection Attribute
+ at c ./../src/parse_y.y 1268
+
+ at cartouche
+ at format
+Attribute ("Name" "Value")
+ at end format
+ at end cartouche
+
+Attributes allow boards and elements to have arbitrary data attached
+to them, which is not directly used by PCB itself but may be of use by
+other programs or users.
+
+ at table @var
+ at item Name
+The name of the attribute
+
+ at item Value
+The value of the attribute.  Values are always stored as strings, even
+if the value is interpreted as, for example, a number.
+
+ at end table
+
+ at c pcbfile Connect
+ at node Connect syntax
+ at subsection Connect
+ at c ./../src/parse_y.y 1258
+
+ at cartouche
+ at format
+Connect ("PinPad")
+ at end format
+ at end cartouche
+
+ at table @var
+ at item PinPad
+The name of a pin or pad which is included in this net.  Pin and Pad
+names are named by the refdes and pin name, like @code{"U14-7"} for
+pin 7 of U14, or @code{"T4-E"} for pin E of T4.
+ at end table
+
+ at c pcbfile Cursor
+ at node Cursor syntax
+ at subsection Cursor
+ at c ./../src/parse_y.y 335
+
+ at cartouche
+ at format
+Cursor [X Y Zoom]
+Cursor (X Y Zoom)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X Y
+Location of the cursor when the board was saved.
+ at item Zoom
+The current zoom factor.  Note that a zoom factor of "0" means 1 mil
+per screen pixel, N means @math{2^N} mils per screen pixel, etc.  The
+first variant accepts floating point numbers.  The special value
+"1000" means "zoom to fit"
+ at end table
+
+ at c pcbfile DRC
+ at node DRC syntax
+ at subsection DRC
+ at c ./../src/parse_y.y 376
+
+ at cartouche
+ at format
+DRC [Bloat Shrink Line Silk Drill Ring]
+DRC [Bloat Shrink Line Silk]
+DRC [Bloat Shrink Line]
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Bloat
+Minimum spacing between copper.
+ at item Shrink
+Minimum copper overlap to guarantee connectivity.
+ at item Line
+Minimum line thickness.
+ at item Silk
+Minimum silk thickness.
+ at item Drill
+Minimum drill size.
+ at item Ring
+Minimum width of the annular ring around pins and vias.
+ at end table
+
+ at c pcbfile Element
+ at node Element syntax
+ at subsection Element
+ at c ./../src/parse_y.y 816
+
+ at cartouche
+ at format
+Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] (
+Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags) (
+Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags) (
+Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags) (
+Element ("Desc" "Name" TX TY TDir TScale TNFlags) (
+@ @ @ @dots{} contents @dots{}
+)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item SFlags
+Symbolic or numeric flags, for the element as a whole.
+ at item NFlags
+Numeric flags, for the element as a whole.
+ at item Desc
+The description of the element.  This is one of the three strings
+which can be displayed on the screen.
+ at item Name
+The name of the element, usually the reference designator.
+ at item Value
+The value of the element.
+ at item MX MY
+The location of the element's mark.  This is the reference point
+for placing the element and its pins and pads.
+ at item TX TY
+The upper left corner of the text (one of the three strings).
+ at item TDir
+The relative direction of the text.  0 means left to right for
+an unrotated element, 1 means up, 2 left, 3 down.
+ at item TScale
+Size of the text, as a percentage of the ``default'' size of of the
+font (the default font is about 40 mils high).  Default is 100 (40
+mils).
+ at item TSFlags
+Symbolic or numeric flags, for the text.
+ at item TNFlags
+Numeric flags, for the text.
+ at end table
+
+Elements may contain pins, pads, element lines, element arcs,
+attributes, and (for older elements) an optional mark.  Note that
+element definitions that have the mark coordinates in the element
+line, only support pins and pads which use relative coordinates.  The
+pin and pad coordinates are relative to the mark.  Element definitions
+which do not include the mark coordinates in the element line, may
+have a Mark definition in their contents, and only use pin and pad
+definitions which use absolute coordinates.
+
+ at c pcbfile ElementArc
+ at node ElementArc syntax
+ at subsection ElementArc
+ at c ./../src/parse_y.y 927
+
+ at cartouche
+ at format
+ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
+ElementArc (X Y Width Height StartAngle DeltaAngle Thickness)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X Y
+Coordinates of the center of the arc.  These are relative to the
+Element's mark point for new element formats, or absolute for older
+formats.
+ at item Width Height
+The width and height, from the center to the edge.  The bounds of the
+circle of which this arc is a segment, is thus @math{2*Width} by
+ at math{2*Height}.
+ at item StartAngle
+The angle of one end of the arc, in degrees.  In PCB, an angle of zero
+points left (negative X direction), and 90 degrees points down
+(positive Y direction).
+ at item DeltaAngle
+The sweep of the arc.  This may be negative.  Positive angles sweep
+counterclockwise.
+ at item Thickness
+The width of the silk line which forms the arc.
+ at end table
+
+ at c pcbfile ElementLine
+ at node ElementLine syntax
+ at subsection ElementLine
+ at c ./../src/parse_y.y 925
+
+ at cartouche
+ at format
+ElementLine [X1 Y1 X2 Y2 Thickness]
+ElementLine (X1 Y1 X2 Y2 Thickness)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X1 Y1 X2 Y2
+Coordinates of the endpoints of the line.  These are relative to the
+Element's mark point for new element formats, or absolute for older
+formats.
+ at item Thickness
+The width of the silk for this line.
+ at end table
+
+ at c pcbfile FileVersion
+ at node FileVersion syntax
+ at subsection FileVersion
+ at c ./../src/parse_y.y 258
+
+ at cartouche
+ at format
+FileVersion[Version]
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Version
+File format version.  This version number represents the date when the pcb file
+format was last changed.
+ at end table
+
+Any version of pcb build from sources equal to or newer
+than this number should be able to read the file.  If this line is not present
+in the input file then file format compatibility is not checked.
+
+
+ at c pcbfile Flags
+ at node Flags syntax
+ at subsection Flags
+ at c ./../src/parse_y.y 418
+
+ at cartouche
+ at format
+Flags(Number)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Number
+A number, whose value is normally given in hex, individual bits of which
+represent pcb-wide flags as defined in @ref{PCBFlags}.
+
+ at end table
+
+ at c pcbfile Grid
+ at node Grid syntax
+ at subsection Grid
+ at c ./../src/parse_y.y 294
+
+ at cartouche
+ at format
+Grid [Step OffsetX OffsetY Visible]
+Grid (Step OffsetX OffsetY Visible)
+Grid (Step OffsetX OffsetY)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Step
+Distance from one grid point to adjacent points.  This value may be a
+floating point number for the first two variants.
+ at item OffsetX OffsetY
+The "origin" of the grid.  Normally zero.
+ at item Visible
+If non-zero, the grid will be visible on the screen.
+ at end table
+
+ at c pcbfile Groups
+ at node Groups syntax
+ at subsection Groups
+ at c ./../src/parse_y.y 432
+
+ at cartouche
+ at format
+Groups("String")
+ at end format
+ at end cartouche
+
+ at table @var
+ at item String
+
+Encodes the layer grouping information.  Each group is separated by a
+colon, each member of each group is separated by a comma.  Group
+members are either numbers from @code{1}.. at var{N} for each layer, and
+the letters @code{c} or @code{s} representing the component side and
+solder side of the board.  Including @code{c} or @code{s} marks that
+group as being the top or bottom side of the board.
+
+ at example
+Groups("1,2,c:3:4:5,6,s:7,8")
+ at end example
+
+ at end table
+
+ at c pcbfile Layer
+ at node Layer syntax
+ at subsection Layer
+ at c ./../src/parse_y.y 573
+
+ at cartouche
+ at format
+Layer (LayerNum "Name") (
+@ @ @ @dots{} contents @dots{}
+)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item LayerNum
+The layer number.  Layers are numbered sequentially, starting with 1.
+The last two layers (9 and 10 by default) are solder-side silk and
+component-side silk, in that order.
+ at item Name
+The layer name.
+ at item contents
+The contents of the layer, which may include attributes, lines, arcs, rectangles,
+text, and polygons.
+ at end table
+
+ at c pcbfile Line
+ at node Line syntax
+ at subsection Line
+ at c ./../src/parse_y.y 629
+
+ at cartouche
+ at format
+Line [X1 Y1 X2 Y2 Thickness Clearance SFlags]
+Line (X1 Y1 X2 Y2 Thickness Clearance NFlags)
+Line (X1 Y1 X2 Y2 Thickness NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X1 Y1 X2 Y2
+The end points of the line
+ at item Thickness
+The width of the line
+ at item Clearance
+The amount of space cleared around the line when the line passes
+through a polygon.  The clearance is added to the thickness to get the
+thickness of the clear; thus the space between the line and the
+polygon is @math{Clearance/2} wide.
+ at item SFlags
+Symbolic or numeric flags
+ at item NFlags
+Numeric flags.
+ at end table
+
+ at c pcbfile Mark
+ at node Mark syntax
+ at subsection Mark
+ at c ./../src/parse_y.y 929
+
+ at cartouche
+ at format
+Mark [X Y]
+Mark (X Y)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X Y
+Coordinates of the Mark, for older element formats that don't have
+the mark as part of the Element line.
+ at end table
+
+ at c pcbfile Net
+ at node Net syntax
+ at subsection Net
+ at c ./../src/parse_y.y 1235
+
+ at cartouche
+ at format
+Net ("Name" "Style") (
+@ @ @ @dots{} connects @dots{}
+)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Name
+The name of this net.
+ at item Style
+The routing style that should be used when autorouting this net.
+ at end table
+
+ at c pcbfile Netlist
+ at node Netlist syntax
+ at subsection Netlist
+ at c ./../src/parse_y.y 1214
+
+ at cartouche
+ at format
+Netlist ( ) (
+@ @ @ @dots{} nets @dots{}
+)
+ at end format
+ at end cartouche
+
+ at c pcbfile Pad
+ at node Pad syntax
+ at subsection Pad
+ at c ./../src/parse_y.y 1086
+
+ at cartouche
+ at format
+Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
+Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
+Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
+Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item rX1 rY1 rX2 rY2
+Coordinates of the endpoints of the pad, relative to the element's
+mark.  Note that the copper extends beyond these coordinates by half
+the thickness.  To make a square or round pad, specify the same
+coordinate twice.
+ at item aX1 aY1 aX2 aY2
+Same, but absolute coordinates of the endpoints of the pad.
+ at item Thickness
+width of the pad.
+ at item Clearance
+add to thickness to get clearance width.
+ at item Mask
+width of solder mask opening.
+ at item Name
+name of pin
+ at item Number
+number of pin
+ at item SFlags
+symbolic or numerical flags
+ at item NFlags
+numerical flags only
+ at end table
+
+ at c pcbfile PCB
+ at node PCB syntax
+ at subsection PCB
+ at c ./../src/parse_y.y 271
+
+ at cartouche
+ at format
+PCB ["Name" Width Height]
+PCB ("Name" Width Height]
+PCB ("Name")
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Name
+Name of the PCB project
+ at item Width Height
+Size of the board
+ at end table
+
+If you don't specify the size of the board, a very large default is
+chosen.
+
+ at c pcbfile Pin
+ at node Pin syntax
+ at subsection Pin
+ at c ./../src/parse_y.y 1013
+
+ at cartouche
+ at format
+Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
+Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
+Pin (aX aY Thickness Drill "Name" "Number" NFlags)
+Pin (aX aY Thickness Drill "Name" NFlags)
+Pin (aX aY Thickness "Name" NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item rX rY
+coordinates of center, relative to the element's mark
+ at item aX aY
+absolute coordinates of center.
+ at item Thickness
+outer diameter of copper annulus
+ at item Clearance
+add to thickness to get clearance diameter
+ at item Mask
+diameter of solder mask opening
+ at item Drill
+diameter of drill
+ at item Name
+name of pin
+ at item Number
+number of pin
+ at item SFlags
+symbolic or numerical flags
+ at item NFlags
+numerical flags only
+ at end table
+
+ at c pcbfile PolyArea
+ at node PolyArea syntax
+ at subsection PolyArea
+ at c ./../src/parse_y.y 353
+
+ at cartouche
+ at format
+PolyArea [Area]
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Area 
+Minimum area of polygon island to retain. If a polygon has clearances that cause an isolated island to be created, then will only be retained if the area exceeds this minimum area.
+ at end table
+
+ at c pcbfile Polygon
+ at node Polygon syntax
+ at subsection Polygon
+ at c ./../src/parse_y.y 743
+
+ at cartouche
+ at format
+Polygon (SFlags) (
+@ @ @ @dots{} (X Y) @dots{}
+@ @ @ @dots{} [X Y] @dots{}
+@ @ @ Hole (
+@ @ @ @ @ @ @dots{} (X Y) @dots{}
+@ @ @ @ @ @ @dots{} [X Y] @dots{}
+@ @ @ )
+@ @ @ @dots{}
+)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item SFlags
+Symbolic or numeric flags.
+ at item X Y
+Coordinates of each vertex.  You must list at least three coordinates.
+ at item Hole (...)
+Defines a hole within the polygon's outer contour. There may be zero or more such sections.
+ at end table
+
+ at c pcbfile Rat
+ at node Rat syntax
+ at subsection Rat
+ at c ./../src/parse_y.y 558
+
+ at cartouche
+ at format
+Rat [X1 Y1 Group1 X2 Y2 Group2 SFlags]
+Rat (X1 Y1 Group1 X2 Y2 Group2 NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X1 Y1 X2 Y2
+The endpoints of the rat line.
+ at item Group1 Group2
+The layer group each end is connected on.
+ at item SFlags
+Symbolic or numeric flags.
+ at item NFlags
+Numeric flags.
+ at end table
+
+ at c pcbfile Styles
+ at node Styles syntax
+ at subsection Styles
+ at c ./../src/parse_y.y 442
+
+ at cartouche
+ at format
+Styles("String")
+ at end format
+ at end cartouche
+
+ at table @var
+ at item String
+
+Encodes the four routing styles @code{pcb} knows about.  The four styles
+are separated by colons.  Each style consists of five parameters as follows:
+
+ at table @var
+ at item Name
+The name of the style.
+ at item Thickness
+Width of lines and arcs.
+ at item Diameter
+Copper diameter of pins and vias.
+ at item Drill
+Drill diameter of pins and vias.
+ at item Keepaway
+Minimum spacing to other nets.  If omitted, 10 mils is the default.
+
+ at end table
+
+ at end table
+
+ at example
+Styles("Signal,10,40,20:Power,25,60,35:Fat,40,60,35:Skinny,8,36,20")
+Styles["Logic,1000,3600,2000,1000:Power,2500,6000,3500,1000:
+@ @ @ Line,4000,6000,3500,1000:Breakout,600,2402,1181,600"]
+ at end example
+
+ at noindent
+Note that strings in actual files cannot span lines; the above example
+is split across lines only to make it readable.
+
+ at c pcbfile Symbol
+ at node Symbol syntax
+ at subsection Symbol
+ at c ./../src/parse_y.y 1148
+
+ at cartouche
+ at format
+Symbol [Char Delta] (
+Symbol (Char Delta) (
+@ @ @ @dots{} symbol lines @dots{}
+)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Char
+The character or numerical character value this symbol represents.
+Characters must be in single quotes.
+ at item Delta
+Additional space to allow after this character.
+ at end table
+
+ at c pcbfile SymbolLine
+ at node SymbolLine syntax
+ at subsection SymbolLine
+ at c ./../src/parse_y.y 1197
+
+ at cartouche
+ at format
+SymbolLine [X1 Y1 X2 Y1 Thickness]
+SymbolLine (X1 Y1 X2 Y1 Thickness)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X1 Y1 X2 Y2
+The endpoints of this line.
+ at item Thickness
+The width of this line.
+ at end table
+
+ at c pcbfile Text
+ at node Text syntax
+ at subsection Text
+ at c ./../src/parse_y.y 689
+
+ at cartouche
+ at format
+Text [X Y Direction Scale "String" SFlags]
+Text (X Y Direction Scale "String" NFlags)
+Text (X Y Direction "String" NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X Y
+The location of the upper left corner of the text.
+ at item Direction
+0 means text is drawn left to right, 1 means up, 2 means right to left
+(i.e. upside down), and 3 means down.
+ at item Scale
+Size of the text, as a percentage of the ``default'' size of of the
+font (the default font is about 40 mils high).  Default is 100 (40
+mils).
+ at item String
+The string to draw.
+ at item SFlags
+Symbolic or numeric flags.
+ at item NFlags
+Numeric flags.
+ at end table
+
+ at c pcbfile Thermal
+ at node Thermal syntax
+ at subsection Thermal
+ at c ./../src/parse_y.y 365
+
+ at cartouche
+ at format
+Thermal [Scale]
+ at end format
+ at end cartouche
+
+ at table @var
+ at item Scale
+Relative size of thermal fingers.  A value of 1.0 makes the finger
+width twice the clearance gap width (measured across the gap, not
+diameter).  The normal value is 0.5, which results in a finger width
+the same as the clearance gap width.
+ at end table
+
+ at c pcbfile Via
+ at node Via syntax
+ at subsection Via
+ at c ./../src/parse_y.y 498
+
+ at cartouche
+ at format
+Via [X Y Thickness Clearance Mask Drill "Name" SFlags]
+Via (X Y Thickness Clearance Mask Drill "Name" NFlags)
+Via (X Y Thickness Clearance Drill "Name" NFlags)
+Via (X Y Thickness Drill "Name" NFlags)
+Via (X Y Thickness "Name" NFlags)
+ at end format
+ at end cartouche
+
+ at table @var
+ at item X Y
+coordinates of center
+ at item Thickness
+outer diameter of copper annulus
+ at item Clearance
+add to thickness to get clearance diameter
+ at item Mask
+diameter of solder mask opening
+ at item Drill
+diameter of drill
+ at item Name
+string, name of via (vias have names?)
+ at item SFlags
+symbolic or numerical flags
+ at item NFlags
+numerical flags only
+ at end table
+
+ at c pcbfile ~objectflags
+ at c ./../src/const.h 110
+ at node Object Flags
+ at section Object Flags
+
+Note that object flags can be given numerically (like @code{0x0147})
+or symbolically (like @code{"found,showname,square"}.  Some numeric
+values are reused for different object types.  The table below lists
+the numeric value followed by the symbolic name.
+
+ at table @code
+ at item 0x0001 pin
+If set, this object is a pin.  This flag is for internal use only.
+ at item 0x0002 via
+Likewise, for vias.
+ at item 0x0004 found
+If set, this object has been found by @code{FindConnection()}.
+ at item 0x0008 hole
+For pins and vias, this flag means that the pin or via is a hole
+without a copper annulus.
+ at item 0x0010 rat
+If set for a line, indicates that this line is a rat line instead of a
+copper trace.
+ at item 0x0010 pininpoly
+For pins and pads, this flag is used internally to indicate that the
+pin or pad overlaps a polygon on some layer.
+ at item 0x0010 clearpoly
+For polygons, this flag means that pins and vias will normally clear
+these polygons (thus, thermals are required for electrical
+connection).  When clear, polygons will solidly connect to pins and
+vias.
+ at item 0x0010 hidename
+For elements, when set the name of the element is hidden.
+ at item 0x0020 showname
+For elements, when set the names of pins are shown.
+ at item 0x0020 clearline
+For lines and arcs, the line/arc will clear polygons instead of
+connecting to them.
+ at item 0x0020 fullpoly
+For polygons, the full polygon is drawn (i.e. all parts instead of only the biggest one).
+ at item 0x0040 selected
+Set when the object is selected.
+ at item 0x0080 onsolder
+For elements and pads, indicates that they are on the solder side.
+ at item 0x0080 auto
+For lines and vias, indicates that these were created by the
+autorouter.
+ at item 0x0100 square
+For pins and pads, indicates a square (vs round) pin/pad.
+ at item 0x0200 rubberend
+For lines, used internally for rubber band moves.
+ at item 0x0200 warn
+For pins, vias, and pads, set to indicate a warning.
+ at item 0x0400 usetherm
+Obsolete, indicates that pins/vias should be drawn with thermal
+fingers.
+ at item 0x0400
+Obsolete, old files used this to indicate lines drawn on silk.
+ at item 0x0800 octagon
+Draw pins and vias as octagons.
+ at item 0x1000 drc
+Set for objects that fail DRC.
+ at item 0x2000 lock
+Set for locked objects.
+ at item 0x4000 edge2
+For pads, indicates that the second point is closer to the edge.  For
+pins, indicates that the pin is closer to a horizontal edge and thus
+pinout text should be vertical.
+ at item 0x8000 marker
+Marker used internally to avoid revisiting an object.
+ at item 0x10000 nopaste
+For pads, set to prevent a solderpaste stencil opening for the
+pad.  Primarily used for pads used as fiducials.
+ at end table
+ at c pcbfile ~pcbflags
+ at c ./../src/const.h 149
+ at node PCBFlags
+ at section PCBFlags
+ at table @code
+ at item 0x00001
+Pinout displays pin numbers instead of pin names.
+ at item 0x00002
+Use local reference for moves, by setting the mark at the beginning of
+each move.
+ at item 0x00004
+When set, only polygons and their clearances are drawn, to see if
+polygons have isolated regions.
+ at item 0x00008
+Display DRC region on crosshair.
+ at item 0x00010
+Do all move, mirror, rotate with rubberband connections.
+ at item 0x00020
+Display descriptions of elements, instead of refdes.
+ at item 0x00040
+Display names of elements, instead of refdes.
+ at item 0x00080
+Auto-DRC flag.  When set, PCB doesn't let you place copper that
+violates DRC.
+ at item 0x00100
+Enable 'all-direction' lines.
+ at item 0x00200
+Switch starting angle after each click.
+ at item 0x00400
+Force unique names on board.
+ at item 0x00800
+New lines/arc clear polygons.
+ at item 0x01000
+Crosshair snaps to pins and pads.
+ at item 0x02000
+Show the solder mask layer.
+ at item 0x04000
+Draw with thin lines.
+ at item 0x08000
+Move items orthogonally.
+ at item 0x10000
+Draw autoroute paths real-time.
+ at item 0x20000
+New polygons are full ones.
+ at item 0x40000
+Names are locked, the mouse cannot select them.
+ at item 0x80000
+Everything but names are locked, the mouse cannot select anything else.
+ at item 0x100000
+New polygons are full polygons.
+ at item 0x200000
+When set, element names are not drawn.
+ at end table
diff --git a/doc-orig/puller.pcb b/doc-orig/puller.pcb
new file mode 100644
index 0000000..3cd03d5
--- /dev/null
+++ b/doc-orig/puller.pcb
@@ -0,0 +1,848 @@
+# release: pcb 1.99s
+
+PCB["" 110000 30000]
+
+Grid[10000.00000000 0 0 1]
+Cursor[0 0 0.000000]
+Thermal[0.500000]
+DRC[1000 1000 1000 1000 1500 1000]
+Flags(0x0000000000001c40)
+Groups("1,c:2,s:3:4:5:6:7:8")
+Styles["Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000:Fat,5000,10000,5000,1000:Skinny,600,2402,1181,600"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2500 3000 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 3000 1500 3000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[2500 1000 2500 2500 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2500 800]
+	SymbolLine[1500 3000 2000 2500 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 1500 0 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 3000 1500 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 3000 1500 3000 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[0 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 2500 800]
+	SymbolLine[1500 2500 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 2500 4000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 4000 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[500 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[2000 1000 2000 4000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 1500 3500 800]
+	SymbolLine[1500 3500 3000 5000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 2500 4000 800]
+	SymbolLine[2500 4000 2500 5000 800]
+	SymbolLine[0 4000 0 5000 800]
+	SymbolLine[0 4000 2500 1500 800]
+	SymbolLine[2500 1000 2500 1500 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 1000 2500 800]
+	SymbolLine[1000 2500 2000 1500 800]
+	SymbolLine[2000 1000 2000 1500 800]
+	SymbolLine[1000 2500 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[2500 1000 2500 1500 800]
+	SymbolLine[0 4000 2500 1500 800]
+	SymbolLine[0 4000 0 5000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2500 800]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[2000 3000 2000 4000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Via[30000 20000 10000 2000 0 5000 "" ""]
+Via[80000 20000 10000 2000 0 5000 "" ""]
+Layer(1 "component")
+(
+	Line[10000 20000 20000 20000 2000 2000 "clearline"]
+	Line[40000 20000 50000 20000 2000 2000 "clearline"]
+	Line[60000 20000 70000 20000 2000 2000 "clearline"]
+	Line[100000 20000 84848 11254 2000 2000 "clearline"]
+	Arc[30000 20000 10000 10000 2000 2000 270 -90 "clearline"]
+	Arc[30000 20000 10000 10000 2000 2000 270 90 "clearline"]
+	Arc[80000 20000 10000 10000 2000 2000 270 -29 "clearline"]
+	Arc[80000 20000 10000 10000 2000 2000 270 90 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "signal4")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[33000 20000 38000 20000 500 2000 "clearline"]
+	Line[40000 13000 40000 18000 500 2000 "clearline"]
+	Line[42000 20000 47000 20000 500 2000 "clearline"]
+	Line[40000 22000 40000 27000 500 2000 "clearline"]
+	Line[77848 11254 82848 11254 500 2000 "clearline"]
+	Line[84848 4254 84848 9254 500 2000 "clearline"]
+	Line[86848 11254 91848 11254 500 2000 "clearline"]
+	Line[84848 13254 84848 18254 500 2000 "clearline"]
+	Arc[40000 20000 2000 2000 500 2000 0 -90 "clearline"]
+	Arc[39999 20001 2001 2001 500 2000 270 -90 "clearline"]
+	Arc[40002 20002 1998 1998 500 2000 180 -90 "clearline"]
+	Arc[40002 19998 2002 2002 500 2000 90 -90 "clearline"]
+	Arc[84848 11254 2000 2000 500 2000 0 -90 "clearline"]
+	Arc[84847 11255 2001 2001 500 2000 270 -90 "clearline"]
+	Arc[84850 11256 1998 1998 500 2000 180 -90 "clearline"]
+	Arc[84850 11252 2002 2002 500 2000 90 -90 "clearline"]
+)
diff --git a/doc-orig/puller.pdf b/doc-orig/puller.pdf
new file mode 100644
index 0000000..dab6555
Binary files /dev/null and b/doc-orig/puller.pdf differ
diff --git a/doc-orig/puller.png b/doc-orig/puller.png
new file mode 100644
index 0000000..2ab3302
Binary files /dev/null and b/doc-orig/puller.png differ
diff --git a/doc-orig/refcard.pdf b/doc-orig/refcard.pdf
new file mode 100644
index 0000000..fd5fbf4
Binary files /dev/null and b/doc-orig/refcard.pdf differ
diff --git a/doc-orig/refcard.tex b/doc-orig/refcard.tex
new file mode 100644
index 0000000..8072351
--- /dev/null
+++ b/doc-orig/refcard.tex
@@ -0,0 +1,258 @@
+% $Id$
+%
+%                            COPYRIGHT
+%
+%  PCB, interactive printed circuit board design
+%  Copyright (C) 1994, 1995 Thomas Nau
+%  Copyright (C) 1998, 1999, 2000, 2001 harry eaton
+%  Copyright (C) 2009 Chitlesh Goorah
+%
+%  This program is free software; you can redistribute it and/or modify
+%  it under the terms of the GNU General Public License as published by
+%  the Free Software Foundation; either version 2 of the License, or
+%  (at your option) any later version.
+%
+%  This program is distributed in the hope that it will be useful,
+%  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%  GNU General Public License for more details.
+%
+%  You should have received a copy of the GNU General Public License
+%  along with this program; if not, write to the Free Software
+%  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%
+%  Contact addresses for paper mail and Email:
+%  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+%  Thomas.Nau at rz.uni-ulm.de
+
+
+\documentclass[11pt,landscape]{article}
+\usepackage{multicol}
+\usepackage{calc}
+\usepackage{ifthen}
+\usepackage[landscape,left=2.5cm,top=2cm,right=2cm,bottom=2cm,nohead]{geometry}
+
+% Turn off header and footer
+\pagestyle{empty}
+ 
+% Redefine section commands to use less space
+\makeatletter
+\renewcommand{\section}{\@startsection{section}{1}{0mm}%
+  {-1ex plus -.5ex minus -.2ex}%
+  {0.5ex plus .2ex}%x
+  {\normalfont\large\bfseries}}
+\renewcommand{\subsection}{\@startsection{subsection}{2}{0mm}%
+  {-1explus -.5ex minus -.2ex}%
+  {0.5ex plus .2ex}%
+  {\normalfont\normalsize\bfseries}}
+\renewcommand{\subsubsection}{\@startsection{subsubsection}{3}{0mm}%
+  {-1ex plus -.5ex minus -.2ex}%
+  {1ex plus .2ex}%
+  {\normalfont\small\bfseries}}
+\makeatother
+
+% Define BibTeX command
+\def\BibTeX{{\rm B\kern-.05em{\sc i\kern-.025em b}\kern-.08em
+    T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
+
+% Don't print section numbers
+\setcounter{secnumdepth}{0}
+
+
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{0pt plus 0.5ex}
+
+%------------------------------------------------------------------
+% some new commands to define the modifier keys
+%
+\newcommand{\Shift}{{\it [S]}}
+\newcommand{\Ctrl}{{\it [C]}}
+\newcommand{\Mod}{{\it [M]}}
+\newcommand{\Btn}{{\it Btn}}
+\newcommand{\Fun}{{\it F}}
+
+\begin{document}
+\raggedright
+\footnotesize
+\begin{multicols}{3}
+
+
+% multicol parameters
+% These lengths are set only within the two main columns
+%\setlength{\columnseprule}{0.25pt}
+\setlength{\premulticols}{1pt}
+\setlength{\postmulticols}{1pt}
+\setlength{\multicolsep}{1pt}
+\setlength{\columnsep}{2pt}
+
+\begin{center}
+  \Large{\textbf{PCB Command reference}}
+  \footnote{http://pcb.gpleda.org/index.html}
+  \footnote{Obviously \Shift, \Ctrl, \Mod, \Fun \space and \Btn \space mean the shift,
+    control, modifier1 (BTNMOD for buttons), function key and mouse button.} \\
+\end{center}
+
+\section{Misc operations}
+\begin{tabular}{@{}ll@{}}
+backspace    & remove object \\
+\Shift\Ctrl\Btn1  & remove object \\
+scroll wheel & vertical pan \\
+\Shift scroll wheel  & horizontal pan \\
+\Btn1  & current mode action\\
+u & undo operation \\
+\Shift r & redo operation \\
+\Shift\Ctrl u & clear undo-list \\
+tab & switch viewing side \\
+cursor key & move crosshair 1 grid\\
+\Shift cursor key! & move crosshair 10 grid\\
+\end{tabular}
+
+
+\section{Connections}
+\begin{tabular}{@{}ll@{}}
+\Shift f & reset found connections \\
+f & find connections \\
+\Shift backspace & remove connections \\
+\end{tabular}
+
+
+\section{User (:) commands}
+\begin{tabular}{@{}ll@{}}
+:DRC() & check layout for rule violations \\
+:l [file] & load data file \\
+:le [file] & load element to buffer \\
+:m [file] & load layout to buffer \\
+:q & quit application \\
+:rn [file] & load netlist \\
+:s [file] & save data as file \\
+\end{tabular}
+
+\section{Display}
+\begin{tabular}{@{}ll@{}}
+c & center display \\
+g & increase grid spacing \\
+\Shift g & decrease grid spacing \\
+\Ctrl m & mark location \\
+r & clear and redraw output \\
+z & zoom in \\
+\Shift z & zoom out \\
+v & zoom extents \\
+\Shift\Btn3 & temporary zoom extents \\
+\end{tabular}
+
+
+\section{Selections}
+\begin{tabular}{@{}ll@{}}
+\Btn2 & select/deselect object \\
+\Shift\Btn2 & toggle object to selection \\
+drag \Btn2 & select only objects in box \\
+drag \Shift\Btn2 & add box to selection \\
+\Shift m & move selected to current layer \\
+\end{tabular}
+
+
+\section{Copy and move}
+\begin{tabular}{@{}ll@{}}
+drag \Btn2 & move object or selection\\
+drag \Mod\Btn2 & copy object \\
+drag \Shift\Mod\Btn2 & override rubberband \& move \\
+m & move to current layer \\
+\end{tabular}
+
+
+\section{Pastebuffer}
+\begin{tabular}{@{}ll@{}}
+\Ctrl x & copy selected objects to buffer \\
+        & and enter pastebuffer mode \\
+\Shift \Ctrl x & cut selected objects to buffer \\
+        & and enter pastebuffer mode \\
+\Btn1 & in pastebuffer mode copy to layout \\
+\Shift \Fun7 & rotate 90 degree cc \\
+\Ctrl 1$\cdots$5 & select buffer \# 1$\cdots$5 \\
+\end{tabular}
+
+
+\section{Sizing}
+\begin{tabular}{@{}ll@{}}
+s & increase size of TLAPV\footnotemark\\
+\Shift s & decrease size of TLAPV\\
+\Mod s & increase drill size of PV \\
+\Shift\Mod s & decrease drill size of PV \\
+k & increase clearance of LAPV\\
+\Shift\ k & decrease clearance of LAPV\\
+\end{tabular}
+\footnotetext{TLAPV: text, line, arc, pin or via}
+
+
+\section{Element}
+\begin{tabular}{@{}ll@{}}
+d & display pinout \\
+\Shift d & open pinout window \\
+h & hide/show element name \\
+n & change element name \\
+\end{tabular}
+
+
+\section{Pin/pad}
+\begin{tabular}{@{}ll@{}}
+n & change name \\
+q & toggle square flag \\
+\end{tabular}
+
+
+\section{Via}
+\begin{tabular}{@{}ll@{}}
+\Fun1 & enter via-mode \\
+\Ctrl v & increase initial size \\
+\Shift \Ctrl v & decrease initial size \\
+\Mod v & inc. initial drilling hole \\
+\Shift\Mod v & dec. initial drilling hole \\
+\Ctrl h & convert via to mounting hole \\
+\end{tabular}
+
+
+\section{Lines and arcs}
+\begin{tabular}{@{}ll@{}}
+\Fun2 & enter line mode \\
+\Fun3 & enter arc mode \\
+l & increase initial line size \\
+\Shift l & decrease initial line size \\
+period & toggle 45 degree enforcement \\
+/ & cycle multiline mode \\
+\Shift & override multiline mode \\
+\end{tabular}
+
+
+\section{Polygon}
+\begin{tabular}{@{}ll@{}}
+\Fun5 & enter rectangle-mode \\
+\Fun6 & enter polygon-mode \\
+\Shift p & close path \\
+insert & enter insert point mode \\
+\end{tabular}
+
+
+\section{Text}
+\begin{tabular}{@{}ll@{}}
+\Fun4 & enter text-mode \\
+n & edit string \\
+t & increase initial text size \\
+\Shift t & decrease initial text size \\
+\end{tabular}
+
+
+\section{Rats nest}
+\begin{tabular}{@{}ll@{}}
+w & add all rats \\
+\Shift w & add rats to selected pins/pads \\
+e & delete all rats \\
+\Shift e & delete selected rats \\
+o & optimize all rats \\
+\Shift o & optimize selected rats \\
+\end{tabular}
+
+
+\end{multicols}
+
+
+\end{document}
diff --git a/doc-orig/thermal.pcb b/doc-orig/thermal.pcb
new file mode 100644
index 0000000..82d0afa
--- /dev/null
+++ b/doc-orig/thermal.pcb
@@ -0,0 +1,844 @@
+# release: pcb-bin 1.99q
+
+PCB["thermal" 300000 300000]
+
+Grid[5000.00000000 0 0 1]
+Cursor[88600 72400 2.000000]
+Thermal[0.500000]
+DRC[699 400 800 800 1000 750]
+Flags(0x0000000000000840)
+Groups("1,s:2,c:3:4:5:6:7:8")
+Styles["Signal,2500,30000,10000,0:Power,2500,6000,3500,1000:Fat,4000,6000,3500,1000:Skinny,200,2402,1181,600"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2500 3000 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 3000 1500 3000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[2500 1000 2500 2500 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2500 800]
+	SymbolLine[1500 3000 2000 2500 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 1500 0 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 3000 1500 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 3000 1500 3000 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[0 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 2500 800]
+	SymbolLine[1500 2500 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 2500 4000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 4000 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[500 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[2000 1000 2000 4000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 1500 3500 800]
+	SymbolLine[1500 3500 3000 5000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 2500 4000 800]
+	SymbolLine[2500 4000 2500 5000 800]
+	SymbolLine[0 4000 0 5000 800]
+	SymbolLine[0 4000 2500 1500 800]
+	SymbolLine[2500 1000 2500 1500 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 0 1500 800]
+	SymbolLine[0 1500 1000 2500 800]
+	SymbolLine[1000 2500 2000 1500 800]
+	SymbolLine[2000 1000 2000 1500 800]
+	SymbolLine[1000 2500 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[2500 1000 2500 1500 800]
+	SymbolLine[0 4000 2500 1500 800]
+	SymbolLine[0 4000 0 5000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2500 800]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[2000 3000 2000 4000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Via[45000 135000 30000 10000 0 10000 "" "thermal(0S)"]
+Via[255000 135000 30000 10000 0 10000 "" ""]
+Via[150000 135000 30000 10000 0 10000 "" "thermal(0)"]
+Layer(1 "component")
+(
+	Polygon("clearpoly")
+	(
+		[5000 95000] [290000 95000] [290000 175000] [5000 175000] 
+	)
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "unused")
+(
+)
+Layer(8 "unused")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[255000 180000 255000 160000 2500 10000 "clearline"]
+	Line[255000 160000 260000 165000 2500 10000 "clearline"]
+	Line[45000 160000 50000 165000 2500 10000 "clearline"]
+	Line[45000 160000 40000 165000 2500 10000 "clearline"]
+	Line[45000 180000 45000 160000 2500 10000 "clearline"]
+	Line[255000 160000 250000 165000 2500 10000 "clearline"]
+	Line[150000 160000 155000 165000 2500 10000 "clearline"]
+	Line[150000 160000 145000 165000 2500 10000 "clearline"]
+	Line[150000 180000 150000 160000 2500 10000 "clearline"]
+	Text[220000 190000 0 200 "no connection" ""]
+	Text[15000 180000 0 200 "Via/Pin with " ""]
+	Text[5000 190000 0 200 "solid connection " ""]
+	Text[225000 180000 0 200 "Via/Pin with" ""]
+	Text[120000 180000 0 200 "Via/Pin with " ""]
+	Text[130000 190000 0 200 "thermal" ""]
+)
diff --git a/doc-orig/thermal.pdf b/doc-orig/thermal.pdf
new file mode 100644
index 0000000..548acf0
Binary files /dev/null and b/doc-orig/thermal.pdf differ
diff --git a/doc-orig/thermal.png b/doc-orig/thermal.png
new file mode 100644
index 0000000..6223d78
Binary files /dev/null and b/doc-orig/thermal.png differ
diff --git a/doc-orig/tutorial/Makefile b/doc-orig/tutorial/Makefile
new file mode 100644
index 0000000..be4f806
--- /dev/null
+++ b/doc-orig/tutorial/Makefile
@@ -0,0 +1,21 @@
+# This Makefile is a plain old hand written one; all configuration settings
+# are included from ../Makefile.conf which is scconfig generated
+
+all:
+
+install_:
+	$(MKDIR) "$(DOCDIR)/examples"
+	$(CPC) "`pwd`/tut1.pcb" "$(DOCDIR)/examples/tut1.pcb"
+
+install:
+	make install_ CPC="$(CP)"
+
+linstall:
+	make install_ CPC="$(LN)"
+
+uninstall:
+	$(RM) "$(DOCDIR)/examples/tut1.pcb"
+
+clean:
+
+include ../Makefile.conf
diff --git a/doc-orig/tutorial/tut1.pcb b/doc-orig/tutorial/tut1.pcb
new file mode 100644
index 0000000..d384f52
--- /dev/null
+++ b/doc-orig/tutorial/tut1.pcb
@@ -0,0 +1,2018 @@
+# release: pcb 1.9.9d.ALPHA
+
+PCB("" 3500 3300)
+
+Grid(39.37007904 18 1 1)
+Cursor(766 1300 1)
+Flags(0x0000000000001750)
+Groups("4,5,6,c:1,2,3,s:8:7:")
+Styles("Signal,10,40,20,10:Power,25,60,35,10:Fat,40,60,35,10:Skinny,8,36,20,10")
+
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 35 0 40 8)
+	SymbolLine(0 0 0 25 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 0 0 10 8)
+	SymbolLine(10 0 10 10 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 15 20 15 8)
+	SymbolLine(15 10 15 30 8)
+	SymbolLine(5 10 5 30 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 5 20 10 8)
+	SymbolLine(5 5 15 5 8)
+	SymbolLine(0 10 5 5 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 30 8)
+	SymbolLine(15 35 20 30 8)
+	SymbolLine(5 35 15 35 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(10 0 10 40 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 5 0 10 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 10 0 8)
+	SymbolLine(10 0 15 5 8)
+	SymbolLine(15 5 15 10 8)
+	SymbolLine(10 15 15 10 8)
+	SymbolLine(5 15 10 15 8)
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(0 40 40 0 8)
+	SymbolLine(35 40 40 35 8)
+	SymbolLine(40 30 40 35 8)
+	SymbolLine(35 25 40 30 8)
+	SymbolLine(30 25 35 25 8)
+	SymbolLine(25 30 30 25 8)
+	SymbolLine(25 30 25 35 8)
+	SymbolLine(25 35 30 40 8)
+	SymbolLine(30 40 35 40 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 25 15 10 8)
+	SymbolLine(5 40 10 40 8)
+	SymbolLine(10 40 20 30 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(5 0 10 0 8)
+	SymbolLine(10 0 15 5 8)
+	SymbolLine(15 5 15 10 8)
+	SymbolLine(0 25 0 35 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 10 10 0 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 35 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 0 5 5 8)
+	SymbolLine(5 5 5 35 8)
+	SymbolLine(0 40 5 35 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 10 20 30 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 20 20 20 8)
+	SymbolLine(10 10 10 30 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 20 20 20 8)
+	SymbolLine(10 10 10 30 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 50 10 40 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 20 20 20 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 40 5 40 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 35 30 5 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 30 20 10 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(10 0 10 40 8)
+	SymbolLine(0 10 10 0 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 25 40 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 20 20 20 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 20 20 0 8)
+	SymbolLine(0 20 25 20 8)
+	SymbolLine(20 0 20 40 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(0 0 0 20 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(20 20 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(0 20 15 20 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 25 20 35 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(25 0 25 15 8)
+	SymbolLine(0 0 25 0 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 15 8)
+	SymbolLine(15 20 20 15 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(20 5 20 20 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(5 20 20 20 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 15 5 15 8)
+	SymbolLine(0 25 5 25 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 40 10 30 8)
+	SymbolLine(10 15 10 20 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 20 10 10 8)
+	SymbolLine(0 20 10 30 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 15 20 15 8)
+	SymbolLine(0 25 20 25 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 10 10 20 8)
+	SymbolLine(0 30 10 20 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 20 10 25 8)
+	SymbolLine(10 35 10 40 8)
+	SymbolLine(0 5 0 10 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 10 8)
+	SymbolLine(10 20 20 10 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine(0 5 0 40 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 40 8)
+	SymbolLine(0 20 25 20 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 15 8)
+	SymbolLine(20 20 25 15 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 20 0 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 35 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(0 0 20 0 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 20 15 20 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 0 20 0 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(0 20 15 20 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(10 20 20 20 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(25 0 25 40 8)
+	SymbolLine(0 20 25 20 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 0 10 0 8)
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 40 10 40 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine(0 0 15 0 8)
+	SymbolLine(15 0 15 35 8)
+	SymbolLine(10 40 15 35 8)
+	SymbolLine(5 40 10 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 20 20 0 8)
+	SymbolLine(0 20 20 40 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 40 20 40 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 0 15 15 8)
+	SymbolLine(15 15 30 0 8)
+	SymbolLine(30 0 30 40 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 0 0 5 8)
+	SymbolLine(0 5 25 30 8)
+	SymbolLine(25 0 25 40 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 15 8)
+	SymbolLine(20 20 25 15 8)
+	SymbolLine(5 20 20 20 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(10 30 20 40 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 15 8)
+	SymbolLine(20 20 25 15 8)
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(5 20 25 40 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(10 0 10 40 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 0 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 0 20 35 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine(0 0 0 30 8)
+	SymbolLine(0 30 10 40 8)
+	SymbolLine(10 40 20 30 8)
+	SymbolLine(20 0 20 30 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 40 15 25 8)
+	SymbolLine(15 25 30 40 8)
+	SymbolLine(30 0 30 40 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine(0 0 0 5 8)
+	SymbolLine(0 5 25 30 8)
+	SymbolLine(25 30 25 40 8)
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 30 25 5 8)
+	SymbolLine(25 0 25 5 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine(0 0 0 5 8)
+	SymbolLine(0 5 10 15 8)
+	SymbolLine(10 15 20 5 8)
+	SymbolLine(20 0 20 5 8)
+	SymbolLine(10 15 10 40 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine(0 0 25 0 8)
+	SymbolLine(25 0 25 5 8)
+	SymbolLine(0 30 25 5 8)
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 40 25 40 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 0 5 0 8)
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 40 5 40 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 5 30 35 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 0 5 0 8)
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 40 5 40 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 10 5 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 40 20 40 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(20 20 20 35 8)
+	SymbolLine(20 35 25 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 25 20 35 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 20 40 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 0 20 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(20 30 20 25 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 5 5 40 8)
+	SymbolLine(5 5 10 0 8)
+	SymbolLine(10 0 15 0 8)
+	SymbolLine(0 20 10 20 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(0 50 5 55 8)
+	SymbolLine(5 55 15 55 8)
+	SymbolLine(15 55 20 50 8)
+	SymbolLine(20 20 20 50 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 40 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 25 0 40 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 10 5 15 8)
+	SymbolLine(5 25 5 50 8)
+	SymbolLine(0 55 5 50 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 25 15 40 8)
+	SymbolLine(0 25 10 15 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 0 0 35 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 25 5 40 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 40 8)
+	SymbolLine(20 25 25 20 8)
+	SymbolLine(25 20 30 20 8)
+	SymbolLine(30 20 35 25 8)
+	SymbolLine(35 25 35 40 8)
+	SymbolLine(0 20 5 25 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 25 5 40 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 40 8)
+	SymbolLine(0 20 5 25 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 25 5 55 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 20 20 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(10 40 20 40 8)
+	SymbolLine(5 35 10 40 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 25 20 55 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 25 5 40 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 20 20 8)
+	SymbolLine(0 20 5 25 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 0 5 35 8)
+	SymbolLine(5 35 10 40 8)
+	SymbolLine(0 15 10 15 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 20 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 20 20 35 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine(0 20 0 30 8)
+	SymbolLine(0 30 10 40 8)
+	SymbolLine(10 40 20 30 8)
+	SymbolLine(20 20 20 30 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 20 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 10 40 8)
+	SymbolLine(10 40 15 35 8)
+	SymbolLine(15 20 15 35 8)
+	SymbolLine(15 35 20 40 8)
+	SymbolLine(20 40 25 40 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(30 20 30 35 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 20 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(20 20 20 50 8)
+	SymbolLine(15 55 20 50 8)
+	SymbolLine(5 55 15 55 8)
+	SymbolLine(0 50 5 55 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 20 20 20 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 40 20 40 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 5 10 0 8)
+	SymbolLine(5 5 5 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(5 25 5 35 8)
+	SymbolLine(5 35 10 40 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 0 0 40 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 0 5 5 8)
+	SymbolLine(5 5 5 15 8)
+	SymbolLine(5 15 10 20 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(5 25 5 35 8)
+	SymbolLine(0 40 5 35 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 10 20 8)
+	SymbolLine(10 20 15 25 8)
+	SymbolLine(15 25 20 25 8)
+	SymbolLine(20 25 25 20 8)
+)
+Via(2075 1455 50 24 80 28 "" 0x10100002)
+Via(1790 1570 50 22 80 28 "" 0x10100002)
+Via(1450 1575 50 24 80 28 "" 0x10100002)
+Via(2965 3005 110 30 140 110 "" 0x0000000a)
+Via(465 605 110 30 140 110 "" 0x0000000a)
+Via(465 3015 110 30 140 110 "" 0x0000000a)
+Via(2965 595 110 30 140 110 "" 0x0000000a)
+Via(1985 1300 50 30 80 28 "" 0x00000002)
+
+Element(0x00000000 "" "X1" "" 2330 850 444 -101 0 125 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "" "1" 0x00000001)
+	Pin(300 0 60 30 90 28 "" "2" 0x00000001)
+	Pin(300 -300 60 30 90 28 "" "3" 0x00000001)
+	Pin(0 -300 60 30 90 28 "" "4" 0x00000001)
+	ElementLine (-110 110 -110 -350 10)
+	ElementLine (-60 -400 350 -400 10)
+	ElementLine (400 -350 400 60 10)
+	ElementLine (350 110 -110 110 10)
+	ElementArc (-60 -350 50 50 270 90 10)
+	ElementArc (350 -350 50 50 180 90 10)
+	ElementArc (350 60 50 50 90 90 10)
+	)
+
+Element(0x00000000 "RS422 Transciever" "U6" "LTC490" 1005 895 -505 -55 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "Vcc" "1" 0x00000101)
+	Pin(0 -100 60 30 90 28 "R" "2" 0x00000001)
+	Pin(0 -200 60 30 90 28 "D" "3" 0x00000001)
+	Pin(0 -300 60 30 90 28 "GND" "4" 0x00000001)
+	Pin(-300 -300 60 30 90 28 "Y" "5" 0x00000001)
+	Pin(-300 -200 60 30 90 28 "Z" "6" 0x00000001)
+	Pin(-300 -100 60 30 90 28 "B" "7" 0x00000001)
+	Pin(-300 0 60 30 90 28 "A" "8" 0x00000001)
+	ElementLine (50 50 50 -350 10)
+	ElementLine (50 -350 -350 -350 10)
+	ElementLine (-350 -350 -350 50 10)
+	ElementLine (50 50 -100 50 10)
+	ElementLine (-200 50 -350 50 10)
+	ElementArc (-150 50 50 50 180 180 10)
+	)
+
+Element(0x00000000 "PIC16C54A" "U5" "PIC16C54" 1195 860 465 80 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "RA2" "1" 0x00000101)
+	Pin(100 0 60 30 90 28 "RA3" "2" 0x00000001)
+	Pin(200 0 60 30 90 28 "RTCC" "3" 0x00000001)
+	Pin(300 0 60 30 90 28 "/MCLR" "4" 0x00000001)
+	Pin(400 0 60 30 90 28 "Vss" "5" 0x00000001)
+	Pin(500 0 60 30 90 28 "RB0" "6" 0x00000001)
+	Pin(600 0 60 30 90 28 "RB1" "7" 0x00000001)
+	Pin(700 0 60 30 90 28 "RB2" "8" 0x00000001)
+	Pin(800 0 60 30 90 28 "RB3" "9" 0x00000001)
+	Pin(800 -300 60 30 90 28 "RB4" "10" 0x00000001)
+	Pin(700 -300 60 30 90 28 "RB5" "11" 0x00000001)
+	Pin(600 -300 60 30 90 28 "RB6" "12" 0x00000001)
+	Pin(500 -300 60 30 90 28 "RB7" "13" 0x00000001)
+	Pin(400 -300 60 30 90 28 "Vdd" "14" 0x00000001)
+	Pin(300 -300 60 30 90 28 "OSC2/CLKOUT" "15" 0x00000001)
+	Pin(200 -300 60 30 90 28 "OSC1/CLKIN" "16" 0x00000001)
+	Pin(100 -300 60 30 90 28 "RA0" "17" 0x00000001)
+	Pin(0 -300 60 30 90 28 "RA1" "18" 0x00000001)
+	ElementLine (-50 50 850 50 10)
+	ElementLine (850 50 850 -350 10)
+	ElementLine (850 -350 -50 -350 10)
+	ElementLine (-50 50 -50 -100 10)
+	ElementLine (-50 -200 -50 -350 10)
+	ElementArc (-50 -150 50 50 90 180 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C17" "" 2155 815 -90 -329 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x00000001)
+	Pin(0 -200 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (-50 50 50 50 10)
+	ElementLine (50 50 50 -250 10)
+	ElementLine (50 -250 -50 -250 10)
+	ElementLine (-50 -250 -50 50 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C13" "" 2525 1510 -30 45 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(0 -90 0 -90 60 30 90 "2" "2" 0x00000100)
+	ElementLine (-35 -125 -35 35 10)
+	ElementLine (35 -125 -35 -125 10)
+	ElementLine (35 35 35 -125 10)
+	ElementLine (-35 35 35 35 10)
+	)
+
+Element(0x00000010 "SMD 0805" "C16" "" 1080 1355 -15 -115 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(90 0 90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (125 -35 -35 -35 10)
+	ElementLine (125 35 125 -35 10)
+	ElementLine (-35 35 125 35 10)
+	ElementLine (-35 -35 -35 35 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C14" "" 1175 1690 -195 20 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(0 90 0 90 60 30 90 "2" "2" 0x00000100)
+	ElementLine (35 125 35 -35 10)
+	ElementLine (-35 125 35 125 10)
+	ElementLine (-35 -35 -35 125 10)
+	ElementLine (35 -35 -35 -35 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C15" "" 1280 1785 65 -174 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x00000001)
+	Pin(0 -200 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (-50 50 50 50 10)
+	ElementLine (50 50 50 -250 10)
+	ElementLine (50 -250 -50 -250 10)
+	ElementLine (-50 -250 -50 50 10)
+	)
+
+Element(0x00000010 "SMD 0805" "C10" "" 2095 1310 -190 -60 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(90 0 90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (125 -35 -35 -35 10)
+	ElementLine (125 35 125 -35 10)
+	ElementLine (-35 35 125 35 10)
+	ElementLine (-35 -35 -35 35 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C12" "" 2345 1320 -35 -129 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x00000001)
+	Pin(200 0 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (-50 -50 -50 50 10)
+	ElementLine (-50 50 250 50 10)
+	ElementLine (250 50 250 -50 10)
+	ElementLine (250 -50 -50 -50 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R13" "100" 1130 1435 230 65 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(400 0 60 30 90 28 "2" "2" 0x10000001)
+	ElementLine (100 -50 300 -50 10)
+	ElementLine (300 -50 300 50 10)
+	ElementLine (300 50 100 50 10)
+	ElementLine (100 50 100 -50 10)
+	ElementLine (40 0 100 0 10)
+	ElementLine (300 0 360 0 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R14" "100" 1105 1075 -265 60 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000001)
+	Pin(-400 0 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (-100 50 -300 50 10)
+	ElementLine (-300 50 -300 -50 10)
+	ElementLine (-300 -50 -100 -50 10)
+	ElementLine (-100 -50 -100 50 10)
+	ElementLine (-40 0 -100 0 10)
+	ElementLine (-300 0 -360 0 10)
+	)
+
+Element(0x00000000 "R 0.5W" "R11" "" 2815 1700 60 -90 0 150 0x00000000)
+(
+	Pin(0 0 85 30 115 48 "1" "1" 0x00000101)
+	Pin(0 -800 85 30 115 48 "2" "2" 0x00000001)
+	ElementLine (0 -60 0 -115 10)
+	ElementLine (-115 -115 115 -115 10)
+	ElementLine (115 -115 115 -685 10)
+	ElementLine (115 -685 -115 -685 10)
+	ElementLine (-115 -685 -115 -115 10)
+	ElementLine (0 -685 0 -750 10)
+	)
+
+Element(0x00000000 "TK11950" "U3" "5.0V" 2235 1510 -139 -2 0 150 0x00000000)
+(
+	Pad(-7 0 8 0 24 30 54 "NOISE BYPASS" "1" 0x00000100)
+	Pad(-7 37 8 37 24 30 54 "CONTROL" "2" 0x00000100)
+	Pad(-7 75 8 75 24 30 54 "RESET OUT" "3" 0x00000100)
+	Pad(119 75 134 75 24 30 54 "VO" "4" 0x00000100)
+	Pad(119 37 134 37 24 30 54 "GND" "5" 0x00000100)
+	Pad(119 0 134 0 24 30 54 "VIN" "6" 0x00000100)
+	ElementLine (0 -22 0 -34 10)
+	ElementLine (0 -34 132 -34 10)
+	ElementLine (132 -34 132 -22 10)
+	ElementLine (0 96 0 108 10)
+	ElementLine (0 108 132 108 10)
+	ElementLine (132 108 132 96 10)
+	ElementLine (13 -24 119 -24 10)
+	)
+
+Element(0x00000000 "TK11950" "U4" "5.0V" 1180 1590 -254 -132 0 150 0x00000000)
+(
+	Pad(7 0 -8 0 24 30 54 "NOISE BYPASS" "1" 0x00000100)
+	Pad(7 -37 -8 -37 24 30 54 "CONTROL" "2" 0x00000100)
+	Pad(7 -75 -8 -75 24 30 54 "RESET OUT" "3" 0x00000100)
+	Pad(-119 -75 -134 -75 24 30 54 "VO" "4" 0x00000100)
+	Pad(-119 -37 -134 -37 24 30 54 "GND" "5" 0x00000100)
+	Pad(-119 0 -134 0 24 30 54 "VIN" "6" 0x00000100)
+	ElementLine (0 22 0 34 10)
+	ElementLine (0 34 -132 34 10)
+	ElementLine (-132 34 -132 22 10)
+	ElementLine (0 -96 0 -108 10)
+	ElementLine (0 -108 -132 -108 10)
+	ElementLine (-132 -108 -132 -96 10)
+	ElementLine (-13 24 -119 24 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C5" "" 1495 1315 -225 -30 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(-90 0 -90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (-125 35 35 35 10)
+	ElementLine (-125 -35 -125 35 10)
+	ElementLine (35 -35 -125 -35 10)
+	ElementLine (35 35 35 -35 10)
+	)
+
+Element(0x00000010 "SMD 0805" "C11" "" 2270 1420 -260 -30 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(-90 0 -90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (-125 35 35 35 10)
+	ElementLine (-125 -35 -125 35 10)
+	ElementLine (35 -35 -125 -35 10)
+	ElementLine (35 35 35 -35 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C9" "" 2030 1185 -145 -40 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x20200001)
+	Pin(200 0 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (0 45 0 50 10)
+	ElementLine (0 50 200 50 10)
+	ElementLine (200 50 200 45 10)
+	ElementLine (200 -45 200 -50 10)
+	ElementLine (200 -50 0 -50 10)
+	ElementLine (0 -50 0 -45 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R7" "100" 1655 2065 150 -30 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000001)
+	Pin(400 0 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (100 -50 300 -50 10)
+	ElementLine (300 -50 300 50 10)
+	ElementLine (300 50 100 50 10)
+	ElementLine (100 50 100 -50 10)
+	ElementLine (40 0 100 0 10)
+	ElementLine (300 0 360 0 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C8" "" 2030 1075 65 -124 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x20200001)
+	Pin(200 0 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (-50 -50 -50 50 10)
+	ElementLine (-50 50 250 50 10)
+	ElementLine (250 50 250 -50 10)
+	ElementLine (250 -50 -50 -50 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C7" "" 1530 1075 -370 -144 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x20200001)
+	Pin(-200 0 80 30 110 35 "2" "2" 0x10100001)
+	ElementLine (50 50 50 -50 10)
+	ElementLine (50 -50 -250 -50 10)
+	ElementLine (-250 -50 -250 50 10)
+	ElementLine (-250 50 50 50 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C2" "" 1945 1845 65 -19 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x00000101)
+	Pin(-200 0 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (50 50 50 -50 10)
+	ElementLine (50 -50 -250 -50 10)
+	ElementLine (-250 -50 -250 50 10)
+	ElementLine (-250 50 50 50 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C6" "" 1530 1185 -335 -30 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x20200001)
+	Pin(-200 0 80 30 110 35 "2" "2" 0x10100001)
+	ElementLine (0 -45 0 -50 10)
+	ElementLine (0 -50 -200 -50 10)
+	ElementLine (-200 -50 -200 -45 10)
+	ElementLine (-200 45 -200 50 10)
+	ElementLine (-200 50 0 50 10)
+	ElementLine (0 50 0 45 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C1" "" 1490 1950 -40 71 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x00000101)
+	Pin(0 -200 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (-50 50 50 50 10)
+	ElementLine (50 50 50 -250 10)
+	ElementLine (50 -250 -50 -250 10)
+	ElementLine (-50 -250 -50 50 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R6" "100" 2375 1680 65 115 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (50 100 50 300 10)
+	ElementLine (50 300 -50 300 10)
+	ElementLine (-50 300 -50 100 10)
+	ElementLine (-50 100 50 100 10)
+	ElementLine (0 40 0 100 10)
+	ElementLine (0 300 0 360 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R8" "100" 2270 1680 -30 245 1 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (50 100 50 300 10)
+	ElementLine (50 300 -50 300 10)
+	ElementLine (-50 300 -50 100 10)
+	ElementLine (-50 100 50 100 10)
+	ElementLine (0 40 0 100 10)
+	ElementLine (0 300 0 360 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R5" "100" 1375 1750 -170 175 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (50 100 50 300 10)
+	ElementLine (50 300 -50 300 10)
+	ElementLine (-50 300 -50 100 10)
+	ElementLine (-50 100 50 100 10)
+	ElementLine (0 40 0 100 10)
+	ElementLine (0 300 0 360 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R9" "100" 1655 2170 125 -25 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000001)
+	Pin(400 0 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (100 -50 300 -50 10)
+	ElementLine (300 -50 300 50 10)
+	ElementLine (300 50 100 50 10)
+	ElementLine (100 50 100 -50 10)
+	ElementLine (40 0 100 0 10)
+	ElementLine (300 0 360 0 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R10" "100" 2160 1675 -30 275 1 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (50 100 50 300 10)
+	ElementLine (50 300 -50 300 10)
+	ElementLine (-50 300 -50 100 10)
+	ElementLine (-50 100 50 100 10)
+	ElementLine (0 40 0 100 10)
+	ElementLine (0 300 0 360 10)
+	)
+
+Element(0x00000000 "ADC12138CIMSA" "U2" "ADC12138" 1630 1690 105 -449 0 150 0x00000000)
+(
+	Pad(0 30 0 -30 12 30 42 "CH0" "1" 0x00000100)
+	Pad(26 30 26 -30 12 30 42 "CH1" "2" 0x00000100)
+	Pad(51 30 51 -30 12 30 42 "CH2" "3" 0x00000100)
+	Pad(77 30 77 -30 12 30 42 "CH3" "4" 0x00000100)
+	Pad(102 30 102 -30 12 30 42 "CH4" "5" 0x00000100)
+	Pad(128 30 128 -30 12 30 42 "CH5" "6" 0x00000100)
+	Pad(154 30 154 -30 12 30 42 "CH6" "7" 0x00000100)
+	Pad(179 30 179 -30 12 30 42 "CH7" "8" 0x00000100)
+	Pad(205 30 205 -30 12 30 42 "COM" "9" 0x00000100)
+	Pad(230 30 230 -30 12 30 42 "MuOut1" "10" 0x00000100)
+	Pad(256 30 256 -30 12 30 42 "A/Din1" "11" 0x00000100)
+	Pad(281 30 281 -30 12 30 42 "MuxOut2" "12" 0x00000100)
+	Pad(307 30 307 -30 12 30 42 "A/Din2" "13" 0x00000100)
+	Pad(333 30 333 -30 12 30 42 "DGND" "14" 0x00000100)
+	Pad(333 -350 333 -290 12 30 42 "VA+" "15" 0x00000100)
+	Pad(307 -350 307 -290 12 30 42 "Vref-" "16" 0x00000100)
+	Pad(281 -350 281 -290 12 30 42 "Vref+" "17" 0x00000100)
+	Pad(256 -350 256 -290 12 30 42 "AGND" "18" 0x00000100)
+	Pad(230 -350 230 -290 12 30 42 "PD" "19" 0x00000100)
+	Pad(205 -350 205 -290 12 30 42 "EOC" "20" 0x00000100)
+	Pad(179 -350 179 -290 12 30 42 "~CONV" "21" 0x00000100)
+	Pad(154 -350 154 -290 12 30 42 "~CS" "22" 0x00000100)
+	Pad(128 -350 128 -290 12 30 42 "DO" "23" 0x00000100)
+	Pad(102 -350 102 -290 12 30 42 "DI" "24" 0x00000100)
+	Pad(77 -350 77 -290 12 30 42 "SCLK" "25" 0x00000100)
+	Pad(51 -350 51 -290 12 30 42 "CCLK" "26" 0x00000100)
+	Pad(26 -350 26 -290 12 30 42 "~DOR" "27" 0x00000100)
+	Pad(0 -350 0 -290 12 30 42 "VD+" "28" 0x00000100)
+	ElementLine (0 -70 0 -70 30)
+	ElementLine (-12 -54 -37 -54 10)
+	ElementLine (-37 -54 -37 -266 10)
+	ElementLine (-37 -266 -12 -266 10)
+	ElementLine (343 -54 370 -54 10)
+	ElementLine (370 -54 370 -266 10)
+	ElementLine (370 -266 343 -266 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R4" "100" 1615 1955 130 -35 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(400 0 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (100 -50 300 -50 10)
+	ElementLine (300 -50 300 50 10)
+	ElementLine (300 50 100 50 10)
+	ElementLine (100 50 100 -50 10)
+	ElementLine (40 0 100 0 10)
+	ElementLine (300 0 360 0 10)
+	)
+
+Element(0x00000010 "SMD 0805" "C4" "" 2070 2390 -25 -180 0 115 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(0 -90 0 -90 60 30 90 "2" "2" 0x00000100)
+	ElementLine (-35 -125 -35 35 10)
+	ElementLine (35 -125 -35 -125 10)
+	ElementLine (35 35 35 -125 10)
+	ElementLine (-35 35 35 35 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R1" "100" 2180 2625 -35 -145 1 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 -400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (-50 -100 -50 -300 10)
+	ElementLine (-50 -300 50 -300 10)
+	ElementLine (50 -300 50 -100 10)
+	ElementLine (50 -100 -50 -100 10)
+	ElementLine (0 -40 0 -100 10)
+	ElementLine (0 -300 0 -360 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R2" "100" 2400 2625 60 -100 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 -400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (-50 -100 -50 -300 10)
+	ElementLine (-50 -300 50 -300 10)
+	ElementLine (50 -300 50 -100 10)
+	ElementLine (50 -100 -50 -100 10)
+	ElementLine (0 -40 0 -100 10)
+	ElementLine (0 -300 0 -360 10)
+	)
+
+Element(0x00000000 "LM13700" "U1" "LM13700" 1955 2285 -475 -130 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "Iabc1" "1" 0x00000101)
+	Pin(-100 0 60 30 90 28 "Dbias1" "2" 0x00000001)
+	Pin(-200 0 60 30 90 28 "In+1" "3" 0x00000001)
+	Pin(-300 0 60 30 90 28 "In-1" "4" 0x00000001)
+	Pin(-400 0 60 30 90 28 "Out1" "5" 0x00000001)
+	Pin(-500 0 60 30 90 28 "V-" "6" 0x00000001)
+	Pin(-600 0 60 30 90 28 "BufIn1" "7" 0x00000001)
+	Pin(-700 0 60 30 90 28 "BufOut1" "8" 0x00000001)
+	Pin(-700 300 60 30 90 28 "BufOut2" "9" 0x00000001)
+	Pin(-600 300 60 30 90 28 "BufIn2" "10" 0x00000001)
+	Pin(-500 300 60 30 90 28 "V+" "11" 0x00000001)
+	Pin(-400 300 60 30 90 28 "Out2" "12" 0x00000001)
+	Pin(-300 300 60 30 90 28 "In-2" "13" 0x00000001)
+	Pin(-200 300 60 30 90 28 "In+2" "14" 0x00000001)
+	Pin(-100 300 60 30 90 28 "Dbias2" "15" 0x00000001)
+	Pin(0 300 60 30 90 28 "Iabc2" "16" 0x00000001)
+	ElementLine (50 -50 -750 -50 10)
+	ElementLine (-750 -50 -750 350 10)
+	ElementLine (-750 350 50 350 10)
+	ElementLine (50 -50 50 100 10)
+	ElementLine (50 200 50 350 10)
+	ElementArc (50 150 50 50 270 180 10)
+	)
+
+Element(0x00000000 "R 0.25W" "R3" "100" 2290 2625 -30 -135 1 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "1" "1" 0x00000101)
+	Pin(0 -400 60 30 90 28 "2" "2" 0x00000001)
+	ElementLine (-50 -100 -50 -300 10)
+	ElementLine (-50 -300 50 -300 10)
+	ElementLine (50 -300 50 -100 10)
+	ElementLine (50 -100 -50 -100 10)
+	ElementLine (0 -40 0 -100 10)
+	ElementLine (0 -300 0 -360 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C3" "" 2075 2510 -40 140 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(0 90 0 90 60 30 90 "2" "2" 0x00000100)
+	ElementLine (35 125 35 -35 10)
+	ElementLine (-35 125 35 125 10)
+	ElementLine (-35 -35 -35 125 10)
+	ElementLine (35 -35 -35 -35 10)
+	)
+
+Element(0x00000000 "3 TERM BLOCK" "J2" "DK ED1602-ND" 1695 2925 -590 76 0 150 0x00000000)
+(
+	Pin(0 0 110 30 140 48 "1" "1" 0x00000001)
+	Pin(-194 0 110 30 140 48 "2" "2" 0x00000001)
+	Pin(-388 0 110 30 140 48 "3" "3" 0x00000001)
+	ElementLine (-388 160 -388 140 10)
+	ElementLine (-194 160 -194 140 10)
+	ElementLine (0 160 0 140 10)
+	ElementLine (81 -170 101 -170 10)
+	ElementLine (81 -190 81 -170 10)
+	ElementLine (101 -190 81 -190 10)
+	ElementLine (-509 -170 -489 -170 10)
+	ElementLine (-509 -190 -509 -170 10)
+	ElementLine (-489 -190 -509 -190 10)
+	ElementLine (101 -200 101 160 10)
+	ElementLine (-489 -200 101 -200 10)
+	ElementLine (-489 160 -489 -200 10)
+	ElementLine (101 160 -489 160 10)
+	)
+
+Element(0x00000000 "SMD Cap" "C21" "" 2565 1070 -50 61 0 150 0x00000000)
+(
+	Pin(0 0 80 30 110 35 "1" "1" 0x00000001)
+	Pin(-200 0 80 30 110 35 "2" "2" 0x00000001)
+	ElementLine (50 50 50 -50 10)
+	ElementLine (50 -50 -250 -50 10)
+	ElementLine (-250 -50 -250 50 10)
+	ElementLine (-250 50 50 50 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C20" "" 2665 2375 -115 55 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(-90 0 -90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (-125 35 35 35 10)
+	ElementLine (-125 -35 -125 35 10)
+	ElementLine (35 -35 -125 -35 10)
+	ElementLine (35 35 35 -35 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C19" "" 2785 2375 -20 55 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(90 0 90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (125 -35 -35 -35 10)
+	ElementLine (125 35 125 -35 10)
+	ElementLine (-35 35 125 35 10)
+	ElementLine (-35 -35 -35 35 10)
+	)
+
+Element(0x00000000 "SMD 0805" "C18" "" 2665 1800 155 -20 0 150 0x00000000)
+(
+	Pad(0 0 0 0 60 30 90 "1" "1" 0x00000100)
+	Pad(90 0 90 0 60 30 90 "2" "2" 0x00000100)
+	ElementLine (125 -35 -35 -35 10)
+	ElementLine (125 35 125 -35 10)
+	ElementLine (-35 35 125 35 10)
+	ElementLine (-35 -35 -35 35 10)
+	)
+
+Element(0x00000000 "AMP 745781-4" "J3" "A2100-ND" 820 1610 -125 837 0 150 0x00000000)
+(
+	Pin(0 432 80 30 110 42 "1" "1" 0x00000101)
+	Pin(0 324 80 30 110 42 "2" "2" 0x00000001)
+	Pin(0 216 80 30 110 42 "3" "3" 0x00000001)
+	Pin(0 108 80 30 110 42 "4" "4" 0x00000001)
+	Pin(0 0 80 30 110 42 "5" "5" 0x00000001)
+	Pin(-112 378 80 30 110 42 "6" "6" 0x00000001)
+	Pin(-112 270 80 30 110 42 "7" "7" 0x00000001)
+	Pin(-112 162 80 30 110 42 "8" "8" 0x00000001)
+	Pin(-112 54 80 30 110 42 "9" "9" 0x00000001)
+	Pin(-56 -276 120 30 150 120 "MOUNT HOLE" "10" 0x00000009)
+	Pin(-56 708 120 30 150 120 "MOUNT HOLE" "11" 0x00000009)
+	ElementLine (-317 -390 58 -390 10)
+	ElementLine (-317 -138 -317 -390 10)
+	ElementLine (-433 455 -434 -22 10)
+	ElementLine (-317 823 -317 571 10)
+	ElementLine (58 823 -317 823 10)
+	ElementLine (58 -390 58 823 10)
+	ElementArc (-375 -138 58 58 90 90 10)
+	ElementArc (-375 -22 58 58 270 90 10)
+	ElementArc (-375 455 58 58 0 90 10)
+	ElementArc (-375 571 58 58 180 90 10)
+	)
+
+Element(0x00000000 "3 TERM BLOCK" "J1" "DK ED1602-ND" 2285 2925 140 51 0 150 0x00000000)
+(
+	Pin(0 0 110 30 140 48 "1" "1" 0x00000001)
+	Pin(-194 0 110 30 140 48 "2" "2" 0x00000001)
+	Pin(-388 0 110 30 140 48 "3" "3" 0x00000001)
+	ElementLine (-388 160 -388 140 10)
+	ElementLine (-194 160 -194 140 10)
+	ElementLine (0 160 0 140 10)
+	ElementLine (81 -170 101 -170 10)
+	ElementLine (81 -190 81 -170 10)
+	ElementLine (101 -190 81 -190 10)
+	ElementLine (-509 -170 -489 -170 10)
+	ElementLine (-509 -190 -509 -170 10)
+	ElementLine (-489 -190 -509 -190 10)
+	ElementLine (101 -200 101 160 10)
+	ElementLine (-489 -200 101 -200 10)
+	ElementLine (-489 160 -489 -200 10)
+	ElementLine (101 160 -489 160 10)
+	)
+
+Element(0x00000000 "OP-AMP" "U7" "LTC1152" 2575 1925 380 200 0 150 0x00000000)
+(
+	Pin(0 0 60 30 90 28 "SHDN" "1" 0x00000101)
+	Pin(0 100 60 30 90 28 "-IN" "2" 0x00000001)
+	Pin(0 200 60 30 90 28 "+IN" "3" 0x00000001)
+	Pin(0 300 60 30 90 28 "V-" "4" 0x00000001)
+	Pin(300 300 60 30 90 28 "COMP" "5" 0x00000001)
+	Pin(300 200 60 30 90 28 "OUT" "6" 0x00000001)
+	Pin(300 100 60 30 90 28 "V+" "7" 0x00000001)
+	Pin(300 0 60 30 90 28 "CP" "8" 0x00000001)
+	ElementLine (-50 -50 -50 350 10)
+	ElementLine (-50 350 350 350 10)
+	ElementLine (350 350 350 -50 10)
+	ElementLine (-50 -50 100 -50 10)
+	ElementLine (200 -50 350 -50 10)
+	ElementArc (150 -50 50 50 0 180 10)
+	)
+
+Element(0x00000000 "R 0.5W" "R12" "" 1035 1935 -205 700 0 150 0x00000000)
+(
+	Pin(0 0 85 30 115 48 "1" "1" 0x00000101)
+	Pin(0 800 90 30 120 48 "2" "2" 0x00000001)
+	ElementLine (0 60 0 115 10)
+	ElementLine (115 115 -115 115 10)
+	ElementLine (-115 115 -115 685 10)
+	ElementLine (-115 685 115 685 10)
+	ElementLine (115 685 115 115 10)
+	ElementLine (0 685 0 750 10)
+	)
+Rat(2630 850 1 2155 815 1  0x00000010)
+Rat(2330 550 1 2155 615 1  0x00000010)
+Rat(2354 1547 0 2515 1415 0  0x00000010)
+Rat(2230 1075 1 2365 1070 1  0x00000010)
+Rat(2230 1185 1 2230 1075 1  0x00000010)
+Rat(2185 1310 0 2230 1185 1  0x00000010)
+Rat(1790 1570 1 1885 1460 0  0x00000010)
+Rat(2055 2065 1 1835 1720 0  0x00000010)
+Rat(2055 2065 1 2055 2170 1  0x00000010)
+Rat(2160 2075 1 2055 2065 1  0x00000010)
+Rat(2575 2225 0 2160 2075 1  0x00000010)
+Rat(1595 860 0 2155 815 1  0x00000010)
+Rat(1175 1780 0 820 2042 1  0x00000010)
+Rat(1280 1585 1 1175 1780 0  0x00000010)
+Rat(1330 1185 1 1405 1315 0  0x00000010)
+Rat(1187 1553 0 1061 1553 0  0x00000010)
+Rat(1355 2285 1 2815 1700 1  0x00000010)
+Rat(1200 2355 0 1035 2735 1  0x00000010)
+Rat(1455 2585 1 1285 2355 0  0x00000010)
+Rat(2100 1250 0 2095 1310 0  0x00000010)
+Rat(2015 1955 1 2375 2080 1  0x00000010)
+Rat(1707 1720 0 1655 2065 1  0x00000010)
+Rat(1945 1845 1 2015 1955 1  0x00000010)
+Rat(1707 1720 0 1945 1845 1  0x00000010)
+Rat(1615 1955 1 1555 2285 1  0x00000010)
+Rat(1745 1845 1 1615 1955 1  0x00000010)
+Rat(1681 1720 0 1745 1845 1  0x00000010)
+Rat(1655 2170 1 2270 2080 1  0x00000010)
+Rat(1490 1950 1 1655 2170 1  0x00000010)
+Rat(1490 1950 1 1375 2150 1  0x00000010)
+Rat(1656 1720 0 1490 1950 1  0x00000010)
+Rat(1600 1750 0 1555 2585 1  0x00000010)
+Rat(1655 2285 1 1307 2925 1  0x00000010)
+Rat(1755 2285 1 1501 2925 1  0x00000010)
+Rat(2360 1315 0 2815 900 1  0x00000010)
+Rat(705 595 1 820 1934 1  0x00000010)
+Rat(705 695 1 820 1826 1  0x00000010)
+Rat(1595 560 1 2155 615 1  0x00000010)
+Rat(1530 1075 1 1595 560 1  0x00000010)
+Rat(1495 1315 0 1530 1185 1  0x00000010)
+Rat(1495 1315 0 1530 1435 1  0x00000010)
+Rat(1080 1355 0 1495 1315 0  0x00000010)
+Rat(2075 1455 1 2180 1420 0  0x00000010)
+Rat(2180 1420 0 2185 1310 0  0x00000010)
+Rat(2228 1547 0 2354 1547 0  0x00000010)
+Layer(1 "solder")
+(
+	Line(1005 1235 1065 1175 15 30 0x00000000)
+	Line(1010 980 1105 1075 15 30 0x00000000)
+	Line(1345 955 1405 955 15 30 0x00000000)
+	Line(1065 1175 1125 1175 15 30 0x00000000)
+	Line(1190 665 1295 560 15 30 0x00000000)
+	Line(705 895 705 1075 15 30 0x00000000)
+	Line(705 795 890 980 15 30 0x00000000)
+	Line(1005 695 1140 560 15 30 0x00000000)
+	Line(1945 1015 1945 1260 15 30 0x00000000)
+	Line(1135 665 1190 665 15 30 0x00000000)
+	Line(1105 1075 970 1210 15 30 0x00000000)
+	Line(890 980 1010 980 15 30 0x00000000)
+	Line(2460 965 1995 965 15 30 0x00000000)
+	Line(1855 2285 1855 2585 15 30 0x00000000)
+	Line(970 1210 970 1568 15 30 0x00000000)
+	Line(1005 795 1135 665 15 30 0x00000000)
+	Line(1405 955 1485 875 15 30 0x00000000)
+	Line(1995 965 1945 1015 15 30 0x00000000)
+	Line(1005 1315 1005 1235 15 30 0x00000000)
+	Line(2565 1070 2460 965 15 30 0x00000000)
+	Line(1945 1260 1985 1300 15 30 0x00000000)
+	Line(1125 1175 1345 955 15 30 0x00000000)
+	Line(2400 2625 2290 2625 10 30 0x00000000)
+	Line(970 1568 820 1718 15 30 0x00000000)
+	Line(1140 560 1195 560 15 30 0x00000000)
+	Line(1125 1435 1005 1315 15 30 0x00000000)
+	Text(525 2805 0 120 "LED (BACK)" 0x00000080)
+)
+Layer(2 "GND-solder")
+(
+)
+Layer(3 "Vcc-solder")
+(
+)
+Layer(4 "component")
+(
+	Line(1732 1750 1732 1720 10 30 0x00000000)
+	Line(1911 1720 1937 1720 10 30 0x00000000)
+	Line(1985 1300 1937 1300 10 30 0x00000000)
+	Line(2015 985 1909 985 10 30 0x00000000)
+	Line(1860 1720 1886 1720 10 30 0x00000000)
+	Line(2075 2615 1985 2615 15 30 0x00000000)
+	Line(1950 2285 2070 2285 15 30 0x00000000)
+	Line(1795 2225 1315 2225 15 30 0x00000000)
+	Line(2125 2585 2290 2420 10 30 0x00000000)
+	Line(2075 2515 2075 2415 30 30 0x00000000)
+	Line(2070 2285 2120 2285 15 30 0x00000000)
+	Line(1490 1750 1375 1750 15 30 0x00000000)
+	Line(1520 2415 1455 2285 30 30 0x00000000)
+	Line(1315 2225 1255 2285 15 30 0x00000000)
+	Line(2160 1750 1732 1750 10 30 0x00000000)
+	Line(2160 1675 2440 1955 15 30 0x00000000)
+	Line(2160 1680 2160 1750 10 30 0x00000000)
+	Line(2440 2110 2345 2205 15 30 0x00000000)
+	Line(1490 1750 1600 1750 10 30 0x00000000)
+	Line(1190 1595 1190 1675 15 30 0x00000000)
+	Line(1190 1510 1190 1435 15 30 0x00000000)
+	Line(1190 1435 1130 1435 15 30 0x00000000)
+	Line(1695 860 1695 1276 10 30 0x00000000)
+	Line(1795 860 1732 923 10 30 0x00000000)
+	Line(1797 1015 1797 1225 10 30 0x00000000)
+	Line(1995 860 1927 927 10 30 0x00000000)
+	Line(1495 560 1646 711 10 30 0x00000000)
+	Line(1657 973 1657 1279 10 30 0x00000000)
+	Line(1657 1279 1681 1303 10 30 0x00000000)
+	Line(1681 1303 1681 1340 10 30 0x00000000)
+	Line(1927 927 1885 927 10 30 0x00000000)
+	Line(1895 860 1758 997 10 30 0x00000000)
+	Line(1732 923 1732 1340 10 30 0x00000000)
+	Line(1893 959 1829 1024 10 30 0x00000000)
+	Line(1646 962 1657 973 10 30 0x00000000)
+	Line(1797 1225 1784 1239 10 30 0x00000000)
+	Line(1646 711 1646 962 10 30 0x00000000)
+	Line(1895 560 2055 720 10 30 0x00000000)
+	Line(1885 927 1797 1015 10 30 0x00000000)
+	Line(2090 910 2015 985 10 30 0x00000000)
+	Line(1985 2615 1955 2585 15 30 0x00000000)
+	Line(1758 997 1758 1340 10 30 0x00000000)
+	Line(1707 1288 1707 1340 10 30 0x00000000)
+	Line(2235 1475 2275 1435 15 30 0x00000000)
+	Line(1909 985 1860 1035 10 30 0x00000000)
+	Line(1005 1195 1005 895 35 30 0x00000000)
+	Line(1060 1255 1005 1195 35 30 0x00000000)
+	Line(585 2350 585 1787 35 30 0x00000000)
+	Line(1860 1035 1860 1261 10 30 0x00000000)
+	Line(2675 2125 2575 2025 15 30 0x00000000)
+	Line(2555 475 1480 475 15 30 0x00000000)
+	Line(2630 550 2555 475 15 30 0x00000000)
+	Line(1455 2285 1290 2450 35 30 0x00000000)
+	Line(2090 655 2090 910 10 30 0x00000000)
+	Line(2120 2285 2180 2225 15 30 0x00000000)
+	Line(1995 560 2090 655 10 30 0x00000000)
+	Line(705 1075 635 1145 15 30 0x00000000)
+	Line(2290 2420 2290 2225 10 30 0x00000000)
+	Line(1480 475 1395 560 15 30 0x00000000)
+	Line(635 1425 820 1610 15 30 0x00000000)
+	Line(1809 1263 1809 1340 10 30 0x00000000)
+	Line(2075 2615 2125 2615 10 30 0x00000000)
+	Line(1290 2450 685 2450 35 30 0x00000000)
+	Line(1600 1750 1630 1720 10 30 0x00000000)
+	Line(1897 2827 1897 2925 15 30 0x00000000)
+	Line(2055 720 2055 895 10 30 0x00000000)
+	Line(1835 1285 1835 1340 10 30 0x00000000)
+	Line(1755 2585 2091 2925 15 30 0x00000000)
+	Line(2235 1500 2235 1475 15 30 0x00000000)
+	Line(1655 2585 1897 2827 15 30 0x00000000)
+	Line(2875 1800 2755 1800 15 30 0x00000000)
+	Line(2075 2415 1520 2415 30 30 0x00000000)
+	Line(1855 2285 1795 2225 15 30 0x00000000)
+	Line(2055 895 1991 959 10 30 0x00000000)
+	Line(1829 1024 1829 1244 10 30 0x00000000)
+	Line(1991 959 1893 959 10 30 0x00000000)
+	Line(2125 2615 2125 2585 10 30 0x00000000)
+	Line(2495 2530 2400 2625 15 30 0x00000000)
+	Line(2440 1955 2440 2110 15 30 0x00000000)
+	Line(2345 2865 2285 2925 15 30 0x00000000)
+	Line(2345 2205 2345 2865 15 30 0x00000000)
+	Line(2785 2215 2785 2375 15 30 0x00000000)
+	Line(1860 1261 1835 1285 10 30 0x00000000)
+	Line(2875 2225 2875 2375 15 30 0x00000000)
+	Line(1784 1239 1784 1340 10 30 0x00000000)
+	Line(2575 2125 2495 2215 15 30 0x00000000)
+	Line(2665 2375 2785 2375 15 30 0x00000000)
+	Line(2575 2225 2575 2375 15 30 0x00000000)
+	Line(2495 2215 2495 2530 15 30 0x00000000)
+	Line(2875 1925 2875 1800 15 30 0x00000000)
+	Line(2875 2125 2785 2215 15 30 0x00000000)
+	Line(1937 1300 1937 1340 10 30 0x00000000)
+	Line(2875 2125 2675 2125 15 30 0x00000000)
+	Line(635 1145 635 1425 15 30 0x00000000)
+	Line(2180 2625 2290 2625 15 30 0x00000000)
+	Line(1695 1276 1707 1288 10 30 0x00000000)
+	Line(685 2450 585 2350 35 30 0x00000000)
+	Line(2975 1480 2565 1070 15 30 0x00000000)
+	Line(2975 2025 2975 1480 15 30 0x00000000)
+	Line(2875 2125 2975 2025 15 30 0x00000000)
+	Line(1829 1244 1809 1263 10 30 0x00000000)
+	Line(585 1787 708 1664 35 30 0x00000000)
+	Text(2515 2705 0 140 "LED rev 1" 0x00000000)
+)
+Layer(5 "GND-component")
+(
+	Line(1505 950 1595 860 30 30 0x00000000)
+	Line(1440 950 1505 950 30 30 0x00000000)
+	Line(2220 1545 2155 1545 20 30 0x00000000)
+	Line(2515 1350 2515 1415 30 30 0x00000000)
+	Line(1200 1350 1230 1350 35 30 0x00000000)
+	Line(1860 1395 1860 1430 10 30 0x00000000)
+	Line(1965 1495 1965 1635 25 30 0x00000000)
+	Line(1855 1495 1965 1495 25 30 0x00000000)
+	Line(1835 1665 1835 1625 10 30 0x00000000)
+	Line(1885 1395 1885 1460 10 30 0x00000000)
+	Line(1965 1660 1965 1635 10 30 0x00000000)
+	Line(1810 1665 1810 1630 10 30 0x00000000)
+	Line(1785 1660 1785 1625 10 30 0x00000000)
+	Line(1760 1625 1760 1660 10 30 0x00000000)
+	Line(1280 1580 1280 1490 25 30 0x00000000)
+	Line(1195 1555 1280 1555 20 30 0x00000000)
+	Line(2545 1320 2515 1350 30 30 0x00000000)
+	Line(1005 595 1085 595 30 30 0x00000000)
+	Line(1085 595 1085 955 30 30 0x00000000)
+	Line(1085 955 1145 1015 30 30 0x00000000)
+	Line(1145 1015 1245 1015 30 30 0x00000000)
+	Arc(1445 995 45 45 30 30 270 90 0x00000000)
+	Polygon(0x00000010)
+	(
+		(2250 1535) (2270 1535) (2270 1485) (2325 1485) (2325 1535) 
+		(2345 1535) (2345 1560) (2325 1560) (2325 1630) (2160 1630) 
+		(2160 1610) (2270 1610) (2270 1560) (2250 1560) 
+	)
+	Polygon(0x00000010)
+	(
+		(1995 1470) (1880 1470) (1880 1450) (2015 1450) (2015 1365) 
+		(2160 1365) (2160 1330) (2195 1330) (2195 1425) (2160 1460) 
+		(2160 1630) (2070 1630) (2070 1720) (1995 1720) 
+	)
+	Polygon(0x00000010)
+	(
+		(1370 1635) (1930 1635) (1930 1525) (1370 1525) 
+	)
+	Polygon(0x00000010)
+	(
+		(1865 1510) (1225 1510) (1225 995) (1415 995) (1415 1255) 
+		(1365 1255) (1365 1365) (1605 1365) (1605 1415) (1865 1415) 
+	)
+	Polygon(0x00000000)
+	(
+		(1360 1295) (1390 1295) (1390 1335) (1360 1335) 
+	)
+	Polygon(0x00000010)
+	(
+		(1070 1565) (1090 1565) (1090 1810) (1150 1810) (1150 1765) 
+		(1125 1740) (1125 1640) (1145 1640) (1145 1565) (1165 1565) 
+		(1165 1540) (1145 1540) (1145 1485) (1090 1485) (1090 1540) 
+		(1070 1540) 
+	)
+)
+Layer(6 "Vcc-component")
+(
+	Line(1045 1870 1045 1935 25 30 0x00000000)
+	Line(708 2155 708 1988 40 30 0x00000000)
+	Line(2575 1925 2490 2010 25 30 0x00000000)
+	Line(1630 1340 1630 1250 10 30 0x00000000)
+	Line(1910 1430 1965 1430 10 30 0x00000000)
+	Line(1910 1395 1910 1430 10 30 0x00000000)
+	Line(1200 2355 1000 2155 35 30 0x00000000)
+	Line(2575 1925 2485 1835 25 30 0x00000000)
+	Line(1070 1265 1070 1330 25 30 0x00000000)
+	Line(2360 1500 2360 1315 25 30 0x00000000)
+	Line(2445 1250 2445 1675 30 30 0x00000000)
+	Line(2100 1250 2445 1250 30 30 0x00000000)
+	Line(1910 1340 1910 1250 10 30 0x00000000)
+	Line(1965 1430 1965 1400 10 30 0x00000000)
+	Line(2375 1585 2440 1585 25 30 0x00000000)
+	Line(2665 1835 2575 1925 30 30 0x00000000)
+	Line(1120 1875 1190 1875 20 30 0x00000000)
+	Line(1045 1595 1045 1875 20 30 0x00000000)
+	Line(1045 1875 1120 1875 20 30 0x00000000)
+	Line(2875 2025 2675 2025 25 30 0x00000000)
+	Line(1285 2355 1200 2355 35 30 0x00000000)
+	Line(1355 2285 1285 2355 35 30 0x00000000)
+	Line(2675 2025 2575 1925 25 30 0x00000000)
+	Line(1000 2155 708 2155 40 30 0x00000000)
+	Line(1060 1510 1060 1375 25 30 0x00000000)
+	Line(1190 1875 1280 1785 20 30 0x00000000)
+	Line(2490 2010 2490 2135 25 30 0x00000000)
+	Line(2450 1510 2495 1510 35 30 0x00000000)
+	Line(2485 1835 2485 1680 25 30 0x00000000)
+	Line(2485 1680 2375 1680 25 30 0x00000000)
+	Line(2665 1800 2665 1835 30 30 0x00000000)
+	Line(2375 1680 2270 1680 30 30 0x00000000)
+	Line(2490 2135 2400 2225 25 30 0x00000000)
+	Polygon(0x00000000)
+	(
+		(1500 1255) (1530 1255) (1530 1295) (1500 1295) 
+	)
+	Polygon(0x00000010)
+	(
+		(1445 1255) (1635 1255) (1635 995) (1635 995) (1635 995) 
+		(1635 995) (1635 995) (1445 995) 
+	)
+	Polygon(0x00000010)
+	(
+		(1885 1260) (2120 1260) (2120 1005) (1921 1003) (1885 1039) 
+	)
+	Polygon(0x00000000)
+	(
+		(2080 1250) (2110 1250) (2110 1295) (2080 1295) 
+	)
+)
+Layer(7 "unused")
+(
+)
+Layer(8 "unused")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Text(2210 3120 0 115 "ANODE" 0x00000400)
+	Text(1855 3125 0 115 "-Y" 0x00000400)
+	Text(2045 3130 0 115 "+Y" 0x00000400)
+	Text(1255 3125 0 115 "-X" 0x00000400)
+	Text(1455 3125 0 115 "+X" 0x00000400)
+	Text(525 295 0 280 "FLARE GENESIS" 0x00000400)
+	Text(435 2815 0 165 "LED Interface" 0x00000400)
+	Text(1885 285 0 295 "harry eaton" 0x00000400)
+)
+NetList()
+(
+	Net("AGND" "(unknown)")
+	(
+		Connect("C8-2")
+		Connect("C9-2")
+		Connect("C10-2")
+		Connect("C11-2")
+		Connect("C12-2")
+		Connect("C13-2")
+		Connect("C20-2")
+		Connect("C21-2")
+		Connect("R7-2")
+		Connect("R9-2")
+		Connect("R10-2")
+		Connect("U2-6")
+		Connect("U2-7")
+		Connect("U2-8")
+		Connect("U2-9")
+		Connect("U2-18")
+		Connect("U3-2")
+		Connect("U3-5")
+		Connect("U7-4")
+	)
+	Net("DGND" "(unknown)")
+	(
+		Connect("C5-2")
+		Connect("C6-2")
+		Connect("C7-2")
+		Connect("C14-2")
+		Connect("C15-2")
+		Connect("C16-2")
+		Connect("C17-1")
+		Connect("J3-1")
+		Connect("U2-14")
+		Connect("U2-19")
+		Connect("U4-2")
+		Connect("U4-5")
+		Connect("U5-5")
+		Connect("U6-4")
+		Connect("X1-2")
+	)
+	Net("Minus12V" "(unknown)")
+	(
+		Connect("C3-1")
+		Connect("C4-1")
+		Connect("J3-9")
+		Connect("U1-6")
+	)
+	Net("Plus12V" "(unknown)")
+	(
+		Connect("J3-6")
+		Connect("R11-1")
+		Connect("R12-2")
+		Connect("U1-7")
+		Connect("U1-11")
+	)
+	Net("S00001" "(unknown)")
+	(
+		Connect("C8-1")
+		Connect("C9-1")
+		Connect("C10-1")
+		Connect("C13-1")
+		Connect("C18-1")
+		Connect("R2-2")
+		Connect("R6-1")
+		Connect("R8-1")
+		Connect("U2-15")
+		Connect("U2-17")
+		Connect("U3-4")
+		Connect("U7-1")
+		Connect("U7-7")
+	)
+	Net("SIG10" "Skinny")
+	(
+		Connect("U1-2")
+		Connect("U1-8")
+		Connect("U1-15")
+	)
+	Net("SIG41" "Power")
+	(
+		Connect("C3-2")
+		Connect("R3-2")
+		Connect("U1-16")
+	)
+	Net("SIG43" "(unknown)")
+	(
+		Connect("C4-2")
+		Connect("R1-2")
+		Connect("U1-1")
+	)
+	Net("SIG49" "(unknown)")
+	(
+		Connect("C2-1")
+		Connect("R4-2")
+		Connect("R6-2")
+		Connect("R7-1")
+		Connect("U2-4")
+	)
+	Net("SIG50" "(unknown)")
+	(
+		Connect("C2-2")
+		Connect("R4-1")
+		Connect("U1-5")
+		Connect("U2-3")
+	)
+	Net("SIG51" "(unknown)")
+	(
+		Connect("C1-1")
+		Connect("R5-2")
+		Connect("R8-2")
+		Connect("R9-1")
+		Connect("U2-2")
+	)
+	Net("SIG52" "(unknown)")
+	(
+		Connect("C1-2")
+		Connect("R5-1")
+		Connect("U1-12")
+		Connect("U2-1")
+	)
+	Net("SIG87" "(unknown)")
+	(
+		Connect("U2-10")
+		Connect("U2-11")
+	)
+	Net("SIG88" "(unknown)")
+	(
+		Connect("U2-12")
+		Connect("U2-13")
+	)
+	Net("SIG91" "(unknown)")
+	(
+		Connect("C19-1")
+		Connect("C20-1")
+		Connect("C21-1")
+		Connect("U2-16")
+		Connect("U7-2")
+		Connect("U7-6")
+	)
+	Net("SIG100" "(unknown)")
+	(
+		Connect("U2-21")
+		Connect("U5-11")
+	)
+	Net("SIG101" "(unknown)")
+	(
+		Connect("U2-20")
+		Connect("U5-10")
+	)
+	Net("SIG124" "(unknown)")
+	(
+		Connect("J2-3")
+		Connect("U1-4")
+	)
+	Net("SIG125" "(unknown)")
+	(
+		Connect("J2-2")
+		Connect("U1-3")
+	)
+	Net("SIG127" "(unknown)")
+	(
+		Connect("J1-2")
+		Connect("U1-14")
+	)
+	Net("SIG139" "(unknown)")
+	(
+		Connect("C11-1")
+		Connect("U3-1")
+	)
+	Net("SIG146" "(unknown)")
+	(
+		Connect("C12-1")
+		Connect("R11-2")
+		Connect("U3-6")
+	)
+	Net("SIG150" "(unknown)")
+	(
+		Connect("C15-1")
+		Connect("R12-1")
+		Connect("U4-6")
+	)
+	Net("SIG155" "(unknown)")
+	(
+		Connect("C14-1")
+		Connect("U4-1")
+	)
+	Net("SIG191" "(unknown)")
+	(
+		Connect("U5-16")
+		Connect("X1-3")
+	)
+	Net("SIG241" "(unknown)")
+	(
+		Connect("U2-26")
+		Connect("U5-15")
+	)
+	Net("SIG252" "(unknown)")
+	(
+		Connect("R13-1")
+		Connect("U4-3")
+		Connect("U5-4")
+	)
+	Net("SIG258" "(unknown)")
+	(
+		Connect("U5-18")
+		Connect("U6-3")
+	)
+	Net("SIG259" "(unknown)")
+	(
+		Connect("U5-17")
+		Connect("U6-2")
+	)
+	Net("SIG285" "(unknown)")
+	(
+		Connect("J3-4")
+		Connect("R14-1")
+		Connect("U6-7")
+	)
+	Net("SIG286" "(unknown)")
+	(
+		Connect("J3-5")
+		Connect("R14-2")
+		Connect("U6-8")
+	)
+	Net("SIG291" "(unknown)")
+	(
+		Connect("U2-22")
+		Connect("U5-9")
+	)
+	Net("SIG292" "(unknown)")
+	(
+		Connect("U2-23")
+		Connect("U5-8")
+	)
+	Net("SIG293" "(unknown)")
+	(
+		Connect("U2-24")
+		Connect("U5-7")
+	)
+	Net("SIG294" "(unknown)")
+	(
+		Connect("U2-25")
+		Connect("U5-6")
+	)
+	Net("SIG296" "(unknown)")
+	(
+		Connect("J3-2")
+		Connect("U6-5")
+	)
+	Net("SIG297" "(unknown)")
+	(
+		Connect("J3-3")
+		Connect("U6-6")
+	)
+	Net("SIG310" "(unknown)")
+	(
+		Connect("J1-1")
+		Connect("R10-1")
+		Connect("U2-5")
+	)
+	Net("SIG311" "(unknown)")
+	(
+		Connect("J1-3")
+		Connect("U1-13")
+	)
+	Net("SIG321" "(unknown)")
+	(
+		Connect("C5-1")
+		Connect("C6-1")
+		Connect("C7-1")
+		Connect("C16-1")
+		Connect("C17-2")
+		Connect("R13-2")
+		Connect("U2-28")
+		Connect("U4-4")
+		Connect("U5-14")
+		Connect("U6-1")
+		Connect("X1-4")
+	)
+	Net("SIG338" "(unknown)")
+	(
+		Connect("R1-1")
+		Connect("R2-1")
+		Connect("R3-1")
+		Connect("U7-3")
+	)
+	Net("SIG341" "(unknown)")
+	(
+		Connect("C18-2")
+		Connect("U7-8")
+	)
+	Net("SIG343" "(unknown)")
+	(
+		Connect("C19-2")
+		Connect("U7-5")
+	)
+)
diff --git a/doc-rnd/Autostyle.html b/doc-rnd/Autostyle.html
new file mode 100644
index 0000000..7e43607
--- /dev/null
+++ b/doc-rnd/Autostyle.html
@@ -0,0 +1,14 @@
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
diff --git a/doc-rnd/Autostyle.sh b/doc-rnd/Autostyle.sh
new file mode 100755
index 0000000..bca2ee9
--- /dev/null
+++ b/doc-rnd/Autostyle.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+autostyle()
+{
+	awk -v "template=$1" '
+	BEGIN {
+		while((getline < template) > 0) {
+			if (parse_auto(RES, $0)) {
+				if (RES["action"] == "begin")
+					curr = RES["ID"]
+				else
+					reset_curr = 1
+			}
+			if (curr != "")
+				AUTO[curr] = AUTO[curr] $0 "\n"
+			if (reset_curr) {
+				curr = ""
+				reset_curr = 0
+			}
+		}
+	}
+
+	function parse_auto(RES, line     ,tmp)
+	{
+		if (!(line ~ "<!--AUTO"))
+			return 0
+		sub(".*<!--AUTO[ \t]*", "", line)
+		sub("[ \t]*-->.*", "", line)
+		line = tolower(line)
+		tmp = line
+		sub("[ \t].*$", "", tmp)
+		RES["ID"] = tmp
+		tmp = line
+		sub("^[^ \t]*[ \t]*", "", tmp)
+		RES["action"] = tmp
+		return 1
+	}
+
+	{
+		if (parse_auto(RES, $0)) {
+			if (RES["action"] == "begin")
+				skip = 1
+			else if (RES["action"] == "end") {
+				printf("%s", AUTO[RES["ID"]])
+				skip = 0
+			}
+			next
+		}
+	}
+
+	(!skip) { print $0 }
+
+	'
+}
+
+for html in $*
+do
+	case $html in
+		Autostyle.html) ;;
+		*)
+			mv $html $html.tmp
+			autostyle "Autostyle.html" < $html.tmp > $html
+			if test $? = 0
+			then
+				rm $html.tmp
+			else
+				echo "Failed on $html, keeping the original version."
+				mv $html.tmp $html
+			fi
+	esac
+done
diff --git a/doc-rnd/Makefile b/doc-rnd/Makefile
new file mode 100644
index 0000000..9db603d
--- /dev/null
+++ b/doc-rnd/Makefile
@@ -0,0 +1,32 @@
+MENU_RES=../src/pcb-menu-gtk.lht ../src/pcb-menu-lesstif.lht
+MENU_RES_MKEY=../src/pcb-menu-mkey.lht
+KEYLIST=../util/keylist.sh
+DEBLIST=../util/devhelpers/deblist.sh
+
+all: keys.html keys_mkey.html features/debian_list.html
+	./Autostyle.sh *.html features/*.html
+
+keys.html: $(MENU_RES) $(KEYLIST)
+	$(KEYLIST) $(MENU_RES) > keys.html
+
+keys_mkey.html: $(MENU_RES_MKEY) $(KEYLIST)
+	$(KEYLIST) $(MENU_RES_MKEY) > keys_mkey.html
+
+features/debian_list.html: ../debian/control
+	$(DEBLIST) < ../debian/control > features/debian_list.html
+	./Autostyle.sh features/debian_list.html
+
+install:
+	cd man && make install
+
+linstall:
+	cd man && make linstall
+
+uninstall:
+	cd man && make uninstall
+
+clean:
+	cd man && make clean
+
+distclean:
+	cd man && make distclean
diff --git a/doc-rnd/README b/doc-rnd/README
new file mode 100644
index 0000000..30444c7
--- /dev/null
+++ b/doc-rnd/README
@@ -0,0 +1,10 @@
+pcb-rnd "diff" documentation: this directory contains documentation
+on the features that differ from the original pcb version it was forked from.
+
+Most notable subdirectories:
+	features/           details of pcb-rnd features
+	conf/               documentation of the configuration system
+	hacking/            documentation for developers
+	devlog/             random thoughts and articles
+
+Documentation of the original version can be found in ../doc-orig
diff --git a/doc-rnd/TODO b/doc-rnd/TODO
new file mode 100644
index 0000000..d12323d
--- /dev/null
+++ b/doc-rnd/TODO
@@ -0,0 +1,154 @@
+- BUGS
+	? all route styles deleted when using 'File>load layout data to paste buffer' 
+	+ in the 45-deg line mode, while DRC enforce settings toggled 'on' can cause isolated trace crossing, violating DRC
+	- save as: change format, save as again: it sets back the format to default but the file name is wrong [report:Erich]
+
+- Lihata persistent save:
+	- flag compatibility: save unknown, string-flags as loaded by io_pcb
+	- ind: space in ha:layer name
+	- ind: symbol height is not in-line on the output
+	- ind: indentation bug in via and netlists
+	- ind: indentation bug in inline struct therm: closing } is preceded by indentation whitespace for some reason
+	- keep numeric format: test all
+	- keep unknown subtrees
+
+- replace settings with lihata (conf_*)
+	- TEST:
+		- vendor drill mapping (all functionality, including regex based refdes ignore)
+		- route style in gtk and lesstif, save, load, 0, 1 or a lot of styles
+		- route style vs. autorouter
+
+- query & advanced search
+	- search expr wizard should pick up the expression from the button when clicked
+	- field accessors:
+		- pad area
+		- pin area
+		- line (and pad) length and length^2
+	- make a run on alarge board, speed test, with -O0 and -O3:
+		- iteration speed (a simple @)
+		- eval speed (a simple @ with a lot of constants in an expr)
+		- geo speed
+		- regex and string cmp match speed vs. select by name
+
+CLEANUP #5
+- save/load bugs:
+	- gtk load dialog extension filter should reflect known formats
+	- when changing format in the save dialog, the file extension is not modified
+	- there should be an "auto" or "guess by extension" option in the save dialog format
+- convert PCBChanged into an event (and provide a core action for compatibility)
+- rework the gpmi plugin to make it more standard in regard of the build system and to remove a few directories
+- dir rename trunk/pcblib/tru-hole should handle make correctly and not walk on existing web services or user installs
+- grid status line label doesn't update when using setvalue(grid,delta) add another hook to update this value [miloh]
+- Erich's select undo bug: place an element, select, save & load, unselect, undo 2 times: bad serial
+- Erich's gtk lag report: press 's' a lot then move the mouse - 's' ends up in the new loc!
+- conf: throw an error if user config can not be written
+- the TAB bug: (over ssh -X) press tab for a good 15 seconds; release; it won't work again for a fraction of a second
+- mark bug - ortho shouldn't use it, it should use last point (crosshair.X2); make sure nothing else abuses it [James]
+- conf:
+	- remove CFN_INCREMENTS, test if saving increments from preferences works
+	- config pov: change prio
+	- gtk preferences: check what takes 131 megs of VIRT ram (on top); destroy the dialog structures when closed
+	- gtk preferences: poly isle area widget missing from the sizes tab
+	- debug why default layer names from the user config are ignored
+	- fp_fs_search: kill anything that gets ':' separated string as path list, get a config list instead
+	- switch the lesstif HID over from attribs to conf
+	- remove redundancy: don't store values in the PCB struct that are in the design conf
+	- increments are not really implemented
+	- if font file is not found:
+		- embedded version?
+		- warn when try to write text on pcb?
+- action bug: gui_act.c shouldn't reference Crosshair.X directly; check d-fix.patch about how to fix it
+- next_gui: keep them open, hide
+- look for #warnings
+- fix librarychanged: disable update of gedasymbols on-start, make it an explicit refresh button
+- libstroke: zoom
+- cleanup/rewrite resources:
+	- load/swap menus (TODO#1)
+	- gpmi:
+		- hid: hid_destroy (pair of hid_create)
+		- cleanup on unload: remove menus
+- check whether local copy of gts is needed, bisect when toporouter broke 
+- check gpmi when pcb-rnd is fully linstalled - broken symlinks?
+- multi-key: display state on the gui
+- implement loglevels and log style tags in the lesstif HID
+- TODO#3: works only on US keyboard
+- gsch2pcb: generalize plugin/buildin loading for external tools, check if gsch2pcb can work from plugins
+
+CLEANUP #6:
+- lihata board format:
+	- pads should be polygons (0 long line can't be rotated!)
+- layer groups from config (e.g. CLI) is ignored
+- res:
+	- search for vendor in the hid plugins, there should be no trace of it (vendor should register its in submenus with anchors)
+	- re-add dynamic menus after a gui change:
+		- either remember dynamic menus in a separate list so they can be rebuilt
+		- or provide hooks and let plugins deal with it
+	- gpmi: auto-remove menus by cookie (TODO#2)
+	- load new res on the fly (replace the menu system):
+		- low level reload code (re-add the dynamic menus too!)
+		- action to reload if file name is known
+		- gui load dialog with tags listed
+- decide about exporter policy:
+	- png exports selection (even in not-as-shown), others don't
+	- extend png hid attribs with a flag for this, maybe do the same for others
+	- document the decision in "hacking"
+- reduce
+		- export_bom: rewrite the string list code using htsp and/or lists
+		- hash in hid_attrib.c?
+		- nelma and gcode both invent .png name locally
+		- get rid of gcode/lists.h,  and vector.[ch] (autorouter)
+		- vendordrill
+			- search for /units, replace it with pcb-printf something
+			- add round down
+			- replace ignore_refdes, ignore_value and ignore_descr with genvector
+	- mods:
+		- gpmi (and other buildins/plugins) not showing up in the about box
+- self contained
+	- files
+		- default font
+	- action (--show-paths!) and dialog box to print where the actual files are coming from
+	- project specific menus from extra lihata files - maybe from project.lht
+- main.c:
+	- SIGPIPE - detect it (needed for popen)
+
+CLEANUP #7: the big object split
+- remove pcb_obj_type_t from const.h - use obj_any.h instead
+
+
+
+
+FEATURES
+- BUILD: menuconfig and a config file for scconfig
+
+Low prio:
+- scconfig: check if it picks up settings from env vars (for a gentoo build)
+- replace mkdtemp() with something safer
+- display net names on pins, vias (and maybe tracks?) when zoomed in enough
+- DRC should warn for thin poly hair
+- rotate shaped vias don't rotate the shape (is via rotated at all?)
+- new examples
+	- blinking led with parametric footprints
+	- example of nonetlist: 1206 jumpers and logos
+- decide what to do with old doc - texi doesn't seem to work at all
+- rethink/rewrite the action/change infrastructure - too many void *ptr1
+  pointers, too many code duplication
+- double sided board, poly on both layers; grab existing lines on one layer and
+  move then around. If all layers are visible, redraw of the far side layer
+  is slow and causes flickering elements from that layer until the front is
+  redrawn. Maybe we should have less flushes?
+- gpmi:
+	- dialog: attribute dialog: mini parser from text
+	- fix debug draw in the gtk hid
+	- ACTE_action(): coords should not be int but Coord
+	- get timers to work
+- dmalloc: #include from the bottom from each file. Is this a good idea?!
+- win32 port {large}
+	- clean up central Makefile.in rules:
+		- remove cat, sed, grep where possible, use scconfig instead
+- arc bug: draw an arc with one end out of the drawing area; it will be jumpy and can not be placed properly
+	-> AttachForCopy() calls SetCrosshairRange() that then sets corsshair max* which
+	limits the arc to move freely. Problem: this is not arc-specific, this happens with any buffer copy! going for no-design-limit is probably better
+- while drawing a line, the temporary outline should reflect the layer color
+- push&shove
+	- keep 45-deg knee when grabbing a point for drag&drop in non-all-dir
+- examine current handling of long options [miloh]
diff --git a/doc-rnd/UNIX.txt b/doc-rnd/UNIX.txt
new file mode 100644
index 0000000..b88acd6
--- /dev/null
+++ b/doc-rnd/UNIX.txt
@@ -0,0 +1,31 @@
+State on UNIX systems
+~~~~~~~~~~~~~~~~~~~~~
+
+Source releases starting from 1.1.2 should compile and run out-of-the-box
+on old UNIX systems, such as IRIX. Does NOT require any GNU installed.
+
+Requirements:
+  - x11 and motif (for the GUI)
+  - an awk implementation that supports gsub(), e.g. nawk
+
+1. If your C compiler does not support #warning, run a script to patch
+   the source:
+
+     # cwd is the root of the distribution
+     util/workarounds/unwarn_all.sh
+
+   This will take a while and will modify a lot of .c files.
+
+2. ./configure
+
+3. make
+
+Considerations listed in ../INSTALL apply.
+
+The above procedure has been tested on IRIX 5.3 on IP22.
+
+Expected compilation times [minute:second]:
+
+./configure       compile, -O0     compile -O3   system
+-------------------------------------------------------------------------------
+1:55              7:40             14:27         IRIX 5.3, IP22 @ 100 MHz
diff --git a/doc-rnd/conf/groups.html b/doc-rnd/conf/groups.html
new file mode 100644
index 0000000..81cf368
--- /dev/null
+++ b/doc-rnd/conf/groups.html
@@ -0,0 +1,116 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> grouping - flat vs. tree </H2>
+The original settings and HID attribute system in pcb were both flat:
+basically a list of type:key=val triplets. All settings in a few big bags;
+which bag sometimes doesn't depend on some logical categorizing, but
+historical reasons (e.g. which part of the code needs the given setting).
+<p>
+This works well for a small amount of settings but lack of categories or
+hierarchy (or sets or groups) makes it harder to understand what setting does
+what and orients users to just accept defaults. After a while it also
+makes programmers think twice before adding a new setting, increasing the
+crowd in a bag. This in turn results in a less configurable
+system.
+<p>
+Introducing a hierarchy of settings can solve these problems by grouping
+and categorizing settings. If the user is interested in how footprints are
+searched, they can ignore the settings the editor/ subtree has. It is also
+easier to save and reload selectively: when the hierarchy is done right,
+closely related settings end up in the same category (thus the same subtree).
+Thus saving or loading a subtree can fully save or restore a property of the
+program, even if that property is affected by multiple settings.
+
+<h2> pcb-rnd config tree </h2>
+The config tree, the full tree is, something that exists in memory. Actual
+config files often contain only a subset of the tree. Multiple config files
+(e.g. system level, user level, settings from the .pcb file) are loaded and
+merged to form the final config tree. The hierarchy of the tree is represented
+by setting groups, which are like directories on a file system. Actual settings
+are always leaves of the tree, placed in a specific group at any level (just
+like in file systems). A full <i>path</i> to a setting is written like a
+path on a file system: group1/group2/item, where group1 and group2 are
+names of setting groups and item is the name of the setting. Note: unlike
+with real file systems, the leading slash (representing the root) is omitted.
+<p>
+Details/constraints: <small>
+A valid path unambiguously identifies a setting (or a setting group). Settings
+and groups always have exactly one parent (except for the root group that
+has no parent). There is only one root of the config tree. </small>
+<p>
+The main groups in the logical tree are:
+
+<dl>
+<table border=0 cellspacing=0 cellpadding=0>
+<tr><td colspan=2> (root)              <td width=30%>   <td> (config root)
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td colspan=3> +- rc               <td> <a href="tree/rc.html"> run control (program startup) </a>
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- path        <td> <a href="tree/rc_path.html">paths automatically set up by the program at startup - do not specify these </a>
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td colspan=3> +- design           <td>  <a href="tree/design.html">some default settings of a new design; minimum/maximum value of some design settings </a>
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td colspan=3> +- editor           <td> <a href="tree/editor.html"> how the pcb editor <i>behaves</i> - independent of HIDs or the GUI </a>
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- increments_mm  <td> interactive increment/decrement steps when active unit is mm
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- increments_mil <td> interactive increment/decrement steps when active unit is mil
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- view           <td> <a href="tree/editor_view.html">default view parameters</a>
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td colspan=3> +- appearance       <td> <a href="tree/appearance.html">how the GUI <i>looks like</i> - common to all GUI HIDs </a>
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- color    <td> <a href="tree/appearance_color.html">layer colors, GUI colors, misc design colors </a>
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- pinout   <td> <a href="tree/pinout.html">pin label properties </a>
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- messages <td> <a href="tree/messages.html">message window properties </a>
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- misc     <td> <a href="tree/misc.html">non-GUI settings handled by the GUI HIDs </a>
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td colspan=3> +- plugins          <td> dynamic subtree for various plugin settings
+<tr><td> |  <td colspan=2> |      <td>  
+<tr><td> |  <td colspan=2> +- <i>foo</i> <td> all settings of the imaginary <i>foo</i> plugin are registered in this group
+<tr><td> |  <td colspan=2>   <td>  
+<tr><td colspan=3> +- utils            <td> dynamic subtree for various plugin settings
+<tr><td>    <td colspan=2> |      <td>  
+<tr><td>    <td colspan=2> +- <i>bar</i> <td> all settings of the imaginary <i>bar</i> utility are registered in this group
+</table>
+</dl>
+
+<h2> dynamic subtrees </h2>
+
+The plugins/ and utils/ subtree are <i>dynamic</i>, which means their contents
+are not defined by core pcb.
+<p>
+In plugins/ each plugin should create a group for its own settings. What
+this subtree should contain depends on what plugins are actually loaded.
+The benefit of this approach is that plugins can use the central config
+infrastructure instead of inventing their own config files. This makes
+user's life easier on many levels: single config syntax to learn; uniform
+GUI (gtk HID's preferences window) to change all settings; uniform way to
+save/restore parts of the settings.
+<p>
+The utils/ subtree is very similar in all aspects except that it is for
+external utility programs. Utils that are closely related to pcb-rnd, such
+as gsch2pcb-rnd, should work from the same configuration (e.g. to
+make sure the same footprint paths are searched). If they already load the
+pcb-rnd config files it's easier to keep their settings in the same tree,
+in the same format.
+<p>
+Pcb-rnd doesn't generate warning for unrecognized settings in dynamic subtrees.
+This lets the user configure plugins that are not always loaded and let util
+settings sit in their subtree.
+
+<h2> what happens to all these settings </h2>
+
+After loading all config files they are merged: if the same setting is
+described in multiple files, the higher priority wins or if the setting is
+a list (e.g. library search paths) the items are merged in a final list.
+At this point the full logical config tree is built. Next the textual values
+from the logical tree are converted into binary (native C values like
+"long int" or "double") and are saved in C variables for the code to
+access them directly. 
+
diff --git a/doc-rnd/conf/index.html b/doc-rnd/conf/index.html
new file mode 100644
index 0000000..47b3b5b
--- /dev/null
+++ b/doc-rnd/conf/index.html
@@ -0,0 +1,92 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Why, what was wrong with the old one? </H2>
+The old config system had several limitations that would have been
+hard to fix keeping the original design:
+<ul>
+	<li> config settings were specified in a flat list - no <a href="groups.html"> grouping </a>
+	<li> the core had a rather static system - HIDs or plugins couldn't extend it, thus they had <a href="noextend.html"> to invent their own </a> config files
+	<li> ... this led to a variety of configuration formats; using one format not always because it was better suited for the task, but for historical reasons
+	<li> ... this also led to a collection of config files - again not always split by boundaries of settings, but often by arbitrary boundaries of code
+	<li> the old system didn't support lists or arrays well
+	<li> it didn't have a coherent concept of how settings from different <a href="sources.html"> sources </a> would override eachother
+	<li> ... this resulted in the rigid structure that most of the settings could come from only one place (e.g. if it's an user setting, the design won't be able to override it)
+</ul>
+
+<H2> What the new system offers </H2>
+<ul>
+	<li> unified format: <a href="http://repo.hu/projects/lihata"> lihata </a> ...
+	<li> ... more future proof: generic markup language - easier to extend wihtout having to worry about breaking the syntax
+	<li> ... the configuration is represented in a tree, groupped by the nature of settings
+	<li> ... there are <a href="lists.html">arrays and lists</a>
+	<li> ... a config file can overwrite a list or <a href="lists.html">prepend/append</a> to it (e.g. design-level config prepending an extra library path keeping system set paths as well)
+	<li> there are different <a href="sources.html"> sources </a> of configuration, e.g. system-wise, user-wise, project-wise, etc.
+	<li> the user has the power to change default config <a href="prio.html">priorty per setting</a>; e.g. normally design config overrides user config, but it's possible to mark a setting from user config so strong that it overrides even the setting read from the .pcb file
+	<li> the way settings are stored is flexible and extensible so that a plugin can define their subtree of settings
+	<li> ... since the API even makes it easier to access such settings (vs. parsing your own config file), plugins will tend to use the unified config format/system instead of inventing teir own
+	<li> ... the GUI (gtk's preferences dialog) thus can automatically handle the new settings
+	<li> ... plugins don't have to implement actions to set/toggle/query their settings for the menu system, there are generic config set/toggle/query actions the menu config can use
+	<li> ... plugins also get the multi-source, <a href="prio.html">priority-based config mechanism</a>
+	<li> ... which also means plugin settings can be automatically saved as user setting, project setting or even design setting
+	<li> all these are true for all kind of settings, be them GUI preferences, paths, layer colors, grid settings; there should be no exception
+</ul>
+
+<H2> How to get started </H2>
+<ul>
+	<li> If you are an user, read the <a href="index_user.html">user documentation</a>
+	<li> If you are an programmer, read the <a href="index_user.html">user documentation</a> and the <a href="index_prog.html">programmer documentation</a>
+	<li> If you are a plugin programmer, there's a <a href="plugin_chk.html"> plugin programmer's conf checklist </a> that summarizes what to do
+	<li> In any case, these links may help: <a href="syntax.html">syntax</a>, <a href="sources.html">config files</a>, <a href="prio.html">priority handling</a>
+</ul>
+
+
+<H2> But isn't this more complicated for the user? </H2>
+Hopefully not much. There are a few extra features, like
+multiple <a href="sources.html"> sources </a> with levels that did not
+exist in pcb and <a href="lists.html">lists with prepend/append</a>. Some of these
+features present in other software so users should be comfortable with the ideas.
+The learning curve is probably compensated by the more orthogonal system.
+The syntax is also geared for simplicity and easy use with text editors.
+Finally, the new preferences dialog in the GTK HID and config actions help
+the user to explore how settings got used from all the config sources. There's
+an intended layering in complexity: simple things can be done easily without
+having to understand the whole system.
+<p>
+All in all, the extra features the user needs to learn is comparable with
+the old customs that he/she can forget.
+
+<H2> And did it make the code more complicated? </H2>
+The size of the code did not change much after the initial config rewrite.
+The new system has new features, some of which brought in a few hundred lines of
+code, but a similar amount of old code could be removed. What came in is
+infrastructure, what had to go was a bunch of repetitive config parsing,
+boolean-setting-accessor-action code. This means on the long run, the more
+settings are added, the more the new system pays back.
+<p>
+Read access, which is far the most common way to use the config in the
+code (e.g. if (setting == true) { }) is very similar to the old Settings
+system. Write access needs to go through a function call API, but this
+is usually a single call per setting (instead of directly modifying a
+variable).
+<p>
+For plugin programmers, the new system makes life much easier as they can
+plug their settings in.
+
+
+<H2> Compatibility with mainline </H2>
+
+None. The new configuration system uses a new logical structure, new file
+format, new locations on the file system. Most setting names are the same
+or very similar to the old ones. Some settings are renamed for clarity:
+clearance is always called clearance, on the UI, in the code and in
+config files as well (mainline pcb sometimes call it keepaway).The new,
+tree-based logics adds a twist too: full names of settings are paths.
+
+<p>
+
+Since configuration is stored in new files, pcb-rnd settings do not interfere
+with pcb settings on any level.
+
+</body>
+</html>
diff --git a/doc-rnd/conf/index_prog.html b/doc-rnd/conf/index_prog.html
new file mode 100644
index 0000000..f0ecab2
--- /dev/null
+++ b/doc-rnd/conf/index_prog.html
@@ -0,0 +1,8 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Programmer's documentation </H2>
+
+TODO
+
+
diff --git a/doc-rnd/conf/index_user.html b/doc-rnd/conf/index_user.html
new file mode 100644
index 0000000..1b97a42
--- /dev/null
+++ b/doc-rnd/conf/index_user.html
@@ -0,0 +1,92 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> User documentation </H2>
+As of 1.1.0, pcb-rnd switched to a <a href="syntax.html">lihata</a> based configuration system.
+The purpose of this document is to describes the basic system design going into
+enough details to provide the user with full control over the configuration.
+The other side, how the system is implemented is described in the
+<a href="index_prog.html"> programmer's manual </a> and there is also a
+<a href="plugin_chk.html">checklist</a> to assist plugin programmers.
+
+<h2> Architecture: data flows, merging, dispatching </h2>
+The final configuration is a collection of values for
+<a href="groups.html"> all known settings, arranged in a tree</a>. The <i>config
+tree</i> is a theoretical concept; different representations of the tree are
+actually built runtime, in-memory. Pcb-rnd code, plugins and utilities
+are constantly reading these in-memory representations to decide how to
+carry out their tasks.
+<p>
+Config settings are imported from multiple sources: from different files,
+from environment vareiables, from command line arguments, from the .pcb
+files on load. Any source can define any part of the <i>config tree</i>.
+When the configuration is processed, each source is read into a temporary
+tree and then all the temporary trees are merged into the final
+<i>config tree</i>. The following diagram demonstrates all configuration
+related data flows.
+<p>
+<img src="merging.png">
+</p>
+The leftmost column of nodes are the sources. (Note: paths mentioned there are
+the default paths, for reference, it is possible to change them compile-time.)
+Along the black arrows, from left to right, each source is imported into a
+tree representing a <i>role</i>: the role or 
+<a href="sources.html">purpose of the source</a>. The next
+step is following the red arrows in two steps:
+<ul>
+	<li> first merge all the role treesinto a flat list; this determines the value of each setting;
+	<li> then dispatch the values to the right component of the code.
+</ul>
+Some components may change some of the settings run-time. The trivial example
+is the GUI (hid_gtk on this diagram) that provides menus and dialog boxes for
+the user to change settings. Such a change is always fed back (blue arrow)
+to the design role tree directly, from where the new value is again merged
+and dispatched along the red arrows. Changes in the design role are saved
+with the .pcb file (thus the bidirectional black arrow between the source and
+the in-memory tree for the design role). Occassionally the user wants to
+save parts of the setting as a <a href="sources.html"> project setting or
+as an user setting</a> - in this case, along the dashed blue lines, the
+corresponding project or user roles are modified. This again results in updating
+the hash and the binary representation; these roles also have
+bidirectional black arrows and their changes are also saved in the original
+source.
+
+<h2> Merge details </h2>
+In the new system it is up to the user to decide what settings are
+system-level and what settings are user- or project-level. This is possible
+because any source can define any setting. In the merging step (red arrows
+between roles and the hash) it may turn out that there are overlaps (multiple
+sources defining value for the same setting) or blind spots (no source
+sets a given item).
+
+<h3> overlaps </h3>
+Each setting in each source has a <a href="prio.html">prioirty.</a> The
+priority can be defined in the source, or if it is not defined, each source
+inherits a fallback default priority. The fallback is designed to provide
+the intuitive order: cli > design > project > user > system.
+<p>
+When multiple sources are describing a value for the same setting,
+priority decides the final value. Most settings are scalar:
+a single integer, string or a single "yes/no" or "on/off" value (e.g.
+the background color or whether polygons are thin-drawn). For scalars
+the rule is simple: the higher priority value wins and all lower priority
+values are discarded when putting the values into the hash. More
+details: <a href="scalars.html"> how different roles and priorities
+can be used with scalars.</a>
+
+<p>
+There are some settings that are represented as an array or list of
+values. They are described in a lihata list item ("li:") in the config
+files and are generally called lists in this document. How lists
+are merged is controlled by the merging <i>policy</i>, which can be
+in each source, just like the prioirty is set. Check out the
+<a href="lists.html">list merging section</a> for more details.
+
+<h3> blind spots </h3>
+At the end the code does need a value for each setting, so in the final
+render (after the hash) every setting must have a value. To avoid blind spots,
+values not initialized, there is a built-in configuration file, compiled into
+the executable. This file is loaded into role <i>CFR_INTERNAL</i>, and has
+the lowest priority. This configuration file contains the default value for
+all settings.
+
diff --git a/doc-rnd/conf/lists.html b/doc-rnd/conf/lists.html
new file mode 100644
index 0000000..2b36f0e
--- /dev/null
+++ b/doc-rnd/conf/lists.html
@@ -0,0 +1,125 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Lists and arrays </H2>
+
+Non-scalar settings are arrays or lists. Arrays can be explicitly indexed
+
+The default <i>policy</i> is always <i>overwrite</i>.
+<p>
+There are three active policies: <i>overwrite</i>, <i>prepend</i> and <i>append</i>.
+When dealing with lists:
+<ul>
+	<li> step 1: the output list is reset to empty
+	<li> step 2: all sources that describe the list are sorted by priority 
+	<li> step 3: sources are applied in order
+</ul>
+Step 3 is straight-forward: if policy is <i>overwrite</i>, reset the output
+list and copy the source's list into the output list. If policy is
+<i>prepend</i> (or <i>append</i>), keep the current output list and prepend
+(or append) the list provided  by the source. 
+<p>
+In practice this means the user can replace, prepend or append ordered lists
+from various sources. A common example is setting the library search paths.
+
+<h2> examples </h2>
+
+<h3> simple overwrite </h3>
+Config sources (ordered by priority):
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A,B,C
+	<tr><td> user    <td> 400      <td> overwrite <th> (not defined)
+	<tr><td> project <td> 600      <td> overwrite <th> D,E
+</table>
+<p>Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A,B,C   <td>  
+	<tr><td> 2.   <td> apply <i>user</i>   <td> A,B,C   <td> "not defined" doesn't mean "empty", so the list is not deleted - no change
+	<tr><td> 3.   <td> apply <i>project</i><td> D,E     <td> replace the original output because of the overwrite policy
+</table>
+<p>Example scenario: the project is restricted to local footprint libs; this setup
+makes sure no system or user configuration injects external footprint paths.
+
+<h3> empty overwrite </h3>
+Config sources (ordered by priority):
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A,B,C
+	<tr><td> user    <td> 400      <td> overwrite <th> (not defined)
+	<tr><td> project <td> 600      <td> overwrite <th> defined to be an empty list
+</table>
+<p>Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A,B,C   <td>  
+	<tr><td> 2.   <td> apply <i>user</i>   <td> A,B,C   <td> "not defined" doesn't mean "empty", so the list is not deleted - no change
+	<tr><td> 3.   <td> apply <i>project</i><td> (empty) <td> replace the original output because of the overwrite policy
+</table>
+
+<h3> prepend </h3>
+Config sources (ordered by priority):
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A,B,C
+	<tr><td> user    <td> 400      <td> prepend   <th> (not defined)
+	<tr><td> project <td> 600      <td> prepend   <th> D,E
+</table>
+<p>Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A,B,C   <td>  
+	<tr><td> 2.   <td> apply <i>user</i>   <td> A,B,C   <td> "not defined" doesn't mean "empty", so the list is not deleted - no change
+	<tr><td> 3.   <td> apply <i>project</i><td> D,E,A,B,C <td>  
+</table>
+<p>Example scenario: the project has its own footprint libs with two paths; these
+should be searched before system and user paths, still, system path is also
+kept so stock footprints can be found.
+<p>
+This is better than hardwiring A,B,C in the project's list: A, B and C may
+depend on the installation on a given system. A project file has no idea
+about how the system is installed but it is assumed system installation
+and the system configuration file are consistent.
+
+<h3> append </h3>
+Config sources (ordered by priority):
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A,B,C
+	<tr><td> user    <td> 400      <td> append   <th> (not defined)
+	<tr><td> project <td> 600      <td> append   <th> D,E
+</table>
+<p>Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A,B,C   <td>  
+	<tr><td> 2.   <td> apply <i>user</i>   <td> A,B,C   <td> "not defined" doesn't mean "empty", so the list is not deleted - no change
+	<tr><td> 3.   <td> apply <i>project</i><td> A,B,C,D,E <td>  
+</table>
+<p>Example scenario: the project has its own footprint libs with two paths; these
+should be searched after system and user paths. This means the local footprint
+lib has lower priority than the stock footprints. See system-dependent
+installation remarks in the previous point.
+
+
+<h3> prepend+append </h3>
+Config sources (ordered by priority):
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A,B,C
+	<tr><td> user    <td> 400      <td> prepend   <th> X,Y,Z
+	<tr><td> project <td> 600      <td> append    <th> D,E
+</table>
+<p>Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A,B,C   <td>  
+	<tr><td> 2.   <td> apply <i>user</i>   <td> X,Y,Z,A,B,C <td>  
+	<tr><td> 3.   <td> apply <i>project</i><td> X,Y,Z,A,B,C,D,E <td>  
+</table>
diff --git a/doc-rnd/conf/merging.png b/doc-rnd/conf/merging.png
new file mode 100644
index 0000000..e5aecc7
Binary files /dev/null and b/doc-rnd/conf/merging.png differ
diff --git a/doc-rnd/conf/noextend.html b/doc-rnd/conf/noextend.html
new file mode 100644
index 0000000..5d2bae5
--- /dev/null
+++ b/doc-rnd/conf/noextend.html
@@ -0,0 +1,59 @@
+<html>
+<body>
+<H1> The OLD config system in pcb-rnd </H1>
+<H2> If the config system is too static </H2>
+
+This document describes the old situation, focusing on drawbacks for
+a purpose: to give a hint on why some of the design decisions are made
+the way they are, in the new conf system.
+
+<h3> Settings, preferences, colors, and... </h3>
+The core implemented a so called Settings. It was a flat list of items,
+each bound to a C variable. There was a bunch of metadata attached to
+the items: name, type, description. Generic code could query or change
+the value of any setting, the C code could read and write them directly
+too. The content is saved to ~/.pcb/settings.
+<p>
+On the downside, the actual items this system knew about was pretty much
+static, hardwired in core. A plugin could not register its own settings.
+Multiple parallel methods were present in the code to overcome this
+limitation:
+<ul>
+	<li> HID attributes: another way to describe name-type-metadata lists; this
+	was used to pass on some rc-like parameters to the GUI HIDs. It's also the
+	main API to describe export parameters for the export HIDs. However,
+	HID attributes is not well suited for saving plugin (or HID) user
+	preferences, it is more about a per action configuration.
+
+	<li> The gtk HID also saved its own settings to a separate file called
+	~/.pcb/preferences.
+
+	<li> Some gtk HID settings didn't quiet fit in the preferences - the color
+	scheme can be saved in another file, usually under ~/.pcb/colors
+
+	<li> A random plugin could either use the HID attributes to get a limited
+	service or like the gtk HID did, had to implement its own save-restore of
+	persistent settings.
+</ul>
+
+<h3> Meta-drawbacks </h3>
+This also introduced a more subtle drawback: the configuration was now
+scattered into different files, <i>randomly</i> (looking from the
+user's point of view). In other words, the actual structure did not reflect
+some logical grouping, but mostly historical or source code organizational
+reasons.
+<p>
+In turn, this also limited (again somewhat randomly) what settings can be
+stored system-wise, user-wise, or on project or design level.
+<p>
+Finally, handling of file search paths was not very sophisticated. There
+was the system and user configuration that reflected where the stock
+library or the user's generic library were installed.  And then
+there was the per-project local footprint libs that had to be somehow
+added too.
+<p>
+There was a hardwired way of handling the situation where multiple set
+of paths were specified. In practice it was usually possible to get this
+to work for the simpler cases, but it was not powerful enough to express
+things like "use all system and user libraries first, then the project's local
+library" vs. "the project's local library has priority over system libraries".
diff --git a/doc-rnd/conf/plugin_chk.html b/doc-rnd/conf/plugin_chk.html
new file mode 100644
index 0000000..c4fbfcc
--- /dev/null
+++ b/doc-rnd/conf/plugin_chk.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Plugin programmer's checklist </H2>
+
+TODO
+
diff --git a/doc-rnd/conf/prio.html b/doc-rnd/conf/prio.html
new file mode 100644
index 0000000..688bdfd
--- /dev/null
+++ b/doc-rnd/conf/prio.html
@@ -0,0 +1,22 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Priorities </H2>
+
+Priority is an integer property of each config root.
+<a href="syntax.html">Syntax-wise</a> it is part of the name of the config
+root. In the lihata config file it is either specified or omitted. When
+omitted,  <a href="sources.html"> a role dependent default value</a> is
+used. The default values are chosen in an intuitive way, thus most
+commonly the priority value is omitted.
+<p>
+For <a href="scalars.html">scalar</a> settings, the highest priority
+value determines the final value of a setting after the merge. If there
+is a tie, role decides: the role closer to the CLI is stronger.
+<p>
+For <a href="lists.html">lists</a> and arrays, priority determines the
+order of merge, which changes the order of itmes in the final list as
+config roots prepend and append items.
+
+
+
diff --git a/doc-rnd/conf/scalars.html b/doc-rnd/conf/scalars.html
new file mode 100644
index 0000000..470600e
--- /dev/null
+++ b/doc-rnd/conf/scalars.html
@@ -0,0 +1,56 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Scalars </H2>
+
+Scalar settings have only one value after the merge. The only <i>policy</i>
+available is <i>overwrite</i> - this policy is applied regardless of the
+current policy setting.
+
+<h2> examples </h2>
+
+<h3> simple overwrite </h3>
+Config sources:
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A
+	<tr><td> user    <td> 400      <td> overwrite <th> (not defined)
+	<tr><td> project <td> 600      <td> overwrite <th> Q
+</table>
+<p>
+Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A       <td>  
+	<tr><td> 2.   <td> apply <i>user</i>   <td> A       <td> "not defined" doesn't mean "empty", so the list is not deleted - no change
+	<tr><td> 3.   <td> apply <i>project</i><td> Q       <td> replace the original output because of the overwrite policy
+</table>
+<p>Example scenario: system default overriden by a project setting.
+
+<h3> user-forced value </h3>
+Config sources append:
+<table border=1>
+	<tr><th> role    <th> priority <th> policy    <th> content
+	<tr><td> system  <td> 200      <td> overwrite <th> A
+	<tr><td> user    <td> 650      <td> overwrite <th> E
+	<tr><td> project <td> 600      <td> overwrite <th> Q
+</table>
+<p>
+Merge iterations:
+<table border=1>
+	<tr><th> step <th> description         <th> output list after executing this step <th> remarks
+	<tr><td> 0.   <td> reset the output    <td> (empty) <td>  
+	<tr><td> 1.   <td> apply <i>system</i> <td> A       <td>  
+	<tr><td> 2.   <td> apply <i>project</i><td> Q       <td>  
+	<tr><td> 3.   <td> apply <i>user</i>   <td> E       <td>  
+</table>
+<p>Example scenario: user preference enforced: even if the project file would use
+'Q' for the given setting, the user prefers 'E'. This affects runtime
+(the value of the setting after the merge, in other words how pcb-rnd works),
+but does nto change the project configuration. This allows the given user to
+always use 'E' for the given setting while lets other users working on the
+same project use the value set in the project file.
+
+
+
diff --git a/doc-rnd/conf/sources.html b/doc-rnd/conf/sources.html
new file mode 100644
index 0000000..0ddf1fb
--- /dev/null
+++ b/doc-rnd/conf/sources.html
@@ -0,0 +1,72 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Sources </H2>
+There are different sources of configuration settings. These are
+different configuration files, sometimes located on the file system.
+The full list of config sources is:
+
+<table border=1>
+<tr><th> role     <th> default<br>setting<br>prio <th> location <th> presence <th> remarks
+<tr><td> internal
+    <td> 100
+    <td> (compiled into the executable)
+    <td> always
+    <td> the ultimate fallback; allows pcb-rnd even if no other configuration file is found
+
+
+<tr><td> system
+    <td> 200
+    <td> /usr/share/pcb-rnd/pcb-conf.lht
+    <td> recommended
+    <td> should hold system and installation specific settigns, e.g. path to the system-wise installed footprint library
+
+<tr><td> default.pcb
+    <td> 300
+    <td> /usr/share/pcb-rnd/default.pcb
+    <td> deprecated
+    <td> pcb editable defaults
+
+<tr><td> user
+    <td> 400
+    <td> ~/.pcb-rnd/pcb-conf.lht
+    <td> recommended
+    <td> store user preferences, user's common footprint lib path, etc; this is the first file the user can modify (even from the GUI)
+
+<tr><td> environment
+    <td> 500
+    <td> environment variables (TODO)
+    <td> occassional
+    <td> inject the same (temporary) settings in multiple pcb-rnd sessions without having to change config files
+
+<tr><td> project
+    <td> 600
+    <td> projdect.lht in the project directory
+    <td> optional
+    <td> local project settings - useful for large projects with multiple design (.pcb) files
+
+<tr><td> design
+    <td> 700
+    <td> saved in the design (.pcb) file
+    <td> optional, common
+    <td> per design deviation from the user+system config
+
+<tr><td> cli
+    <td> 800
+    <td> command line argument
+    <td> occassional
+    <td> inject/change a setting for a single session; useful in batch/automated processing
+</table>
+
+<p>
+Pcb-rnd reads them all, then merges all settings into a master binary
+representation. If a setting is specified in multiple sources, the one
+with the higher priority wins, except for lists where it is also possible
+to prepend/append items. Default priorities are designed to result
+precedence in an intuitive way (e.g. design settigns overwrite user settings).
+However, <a href="prio.html">priority can be changed</a> per setting, resulting
+in weak settings ("use this value if it was not already set") or strong settings
+("I always want to use mincut, so I enable it from my user's config with high
+priority and a version controlled project setting can not turn it off")
+
+
diff --git a/doc-rnd/conf/src/Makefile b/doc-rnd/conf/src/Makefile
new file mode 100644
index 0000000..3b48549
--- /dev/null
+++ b/doc-rnd/conf/src/Makefile
@@ -0,0 +1,2 @@
+../merging.png: merging.dot
+	dot -Tpng merging.dot > ../merging.png
\ No newline at end of file
diff --git a/doc-rnd/conf/src/merging.dot b/doc-rnd/conf/src/merging.dot
new file mode 100644
index 0000000..c5eadb0
--- /dev/null
+++ b/doc-rnd/conf/src/merging.dot
@@ -0,0 +1,103 @@
+digraph g {
+	rankdir=LR;
+
+	subgraph cluster_memtree {
+		label="in-memory lihata trees"
+		bgcolor=grey
+		rank=same
+		CFR_INTERNAL    [label="CFR_INTERNAL\nultimate fallback"]
+		CFR_SYSTEM      [label="CFR_SYSTEM\nsystem level configuration"]
+		CFR_DEFAULTPCB  [label="CFR_DEFAULTPCB"]
+		CFR_USER        [label="CFR_USER\nuser level configuration"]
+		CFR_ENV         [label="CFR_ENV"]
+		CFR_PROJECT     [label="CFR_PROJECT\nproject level configuration"]
+		CFR_DESIGN      [label="CFR_DESIGN"]
+		CFR_CLI         [label="CFR_CLI"]
+	}
+
+	subgraph cluster_fields {
+		label="string -> conf_native_t hash"
+		bgcolor=grey
+		conf_fields     [label="conf_fields\ncentral hash\nof all\nknown settings"]
+	}
+
+	subgraph cluster_native {
+		label="native C structures\nper module"
+		bgcolor=grey
+		conf_core       [label="conf_core\npcb-rnd core settings"]
+		conf_hid_gtk    [label="conf_hid_gtk\nthe hid_gtk plugin's settings"]
+		conf_mincut     [label="conf_mincut\nthe mincut plugin's settings"]
+		conf_report     [label="conf_report\nthe report plugin's settings"]
+		conf_other      [label="...\nother plugin's settings"]
+	}
+
+	CFR_INTERNAL    -> conf_fields [color=red]
+	CFR_SYSTEM      -> conf_fields [color=red]
+	CFR_DEFAULTPCB  -> conf_fields [color=red]
+	CFR_USER        -> conf_fields [color=red]
+	CFR_ENV         -> conf_fields [color=red]
+	CFR_PROJECT     -> conf_fields [color=red]
+	CFR_DESIGN      -> conf_fields [color=red]
+	CFR_CLI         -> conf_fields [color=red]
+
+
+#	CFR_INTERNAL    -> CFR_SYSTEM
+#	CFR_SYSTEM      -> CFR_DEFAULTPCB
+#	CFR_DEFAULTPCB  -> CFR_USER
+#	CFR_USER        -> CFR_ENV
+#	CFR_ENV         -> CFR_PROJECT
+#	CFR_PROJECT     -> CFR_DESIGN
+#	CFR_DESIGN      -> CFR_CLI
+
+	conf_fields -> conf_core      [color=red]
+	conf_fields -> conf_hid_gtk   [color=red]
+	conf_fields -> conf_mincut    [color=red]
+	conf_fields -> conf_report    [color=red]
+	conf_fields -> conf_other     [color=red]
+
+
+
+	subgraph cluster_files {
+		label="config files"
+		bgcolor=grey
+		lht_system        [label="/usr/share/pcb-rnd/pcb-conf.lht" shape=hexagon]
+		pcb_default       [label="/usr/share/pcb-rnd/default.pcb" shape=hexagon]
+		project           [label="./project.lht" shape=hexagon]
+		lht_user          [label="~/.pcb-rnd/pcb-conf.lht" shape=hexagon]
+	}
+
+	subgraph cluster_exec_env {
+		label="execution environment"
+		bgcolor=grey
+		env               [label="environmental variables"]
+		cli               [label="command line arguments\ne.g. -c or\npluginspecific args"]
+	}
+
+	lht_internal      [label="hardwired\nin the\nexecutable"]
+	design            [label="settings\nin the\n.pcb file" shape=hexagon]
+
+	lht_internal -> CFR_INTERNAL    [label="program startup"]
+	lht_system   -> CFR_SYSTEM      [label="loaded at startup"]
+	pcb_default  -> CFR_DEFAULTPCB  [label="loadad in CreateNewPCB()"]
+	lht_user     -> CFR_USER        [label="loaded at startup" dir=both]
+	env          -> CFR_ENV         [label="built at startup"]
+	project      -> CFR_PROJECT     [label="loaded when a\nnew .pcb or project\nis loaded" dir=both]
+	design       -> CFR_DESIGN      [label="extracted when loading a design" dir=both]
+	cli          -> CFR_CLI         [label="built during\ncommand line argument\nparsing"]
+
+
+	hid_gtk [label="the GTK HID"]
+
+	conf_core     -> hid_gtk [weight=100]
+	conf_hid_gtk  -> hid_gtk
+
+	hid_gtk -> CFR_DESIGN  [color=blue weigth=0]
+	hid_gtk -> CFR_PROJECT [color=blue weigth=0 style=dashed]
+	hid_gtk -> CFR_USER    [color=blue weigth=0 style=dashed]
+
+
+	editor  [label="core:\nediting pcb"]
+	conf_core -> editor   [weight=100]
+	editor -> CFR_DESIGN  [color=blue weigth=0]
+
+}
\ No newline at end of file
diff --git a/doc-rnd/conf/syntax.html b/doc-rnd/conf/syntax.html
new file mode 100644
index 0000000..c2b861e
--- /dev/null
+++ b/doc-rnd/conf/syntax.html
@@ -0,0 +1,45 @@
+<html>
+<body>
+<H1> The new config system in pcb-rnd </H1>
+<H2> Config file syntax </H2>
+
+The config file sytnax is <a href="http://repo.hu/projects/lihata">lihata</a>.
+Most users don't need to understand most of the syntax, just follow the
+patterns seen in the examples. A few thumb of rules:
+<ul>
+	<li> srtuctural nodes usually start with a ha: or li: prefix; which one needs to be used is pretty much tied to the node name; thus casual users don't need to care about what they are for, just remember them as part of the name
+	<li> non-structural nodes are usually given in the form of name = value; use braces around the value if it contains any non-alphanumeric, non-whitespace character
+	<li> list and array members should better be braced
+	<li> list and array separator should be semicolon (not comma)
+</ul>
+
+<H3> config root syntax </H3>
+<p>
+A pcb-rnd config file, (or <i>document</i> for short) has a single root
+node whose name must be li:pcb-rnd-conf-v1 - this is the signature of the
+document. It is a flat list of one or more <i>config root/</i> subtrees.
+TODO: is this really a list or a hash?
+<p>
+Each config root is a partial description of the
+<a href=index_user.html"> config tree </a> (which is the logical
+confgiuration of all possible settings). Config roots have a policy and
+a <a href="prio.html">priority</a> attached. This is done in the name
+of the config root, which must be of the form of <i>policy-priority</i>,
+e.g. "overwrite-300" or "append-125". The priority part (with the dash)
+can be omitted (and then the per role default priority is used), e.g.
+"overwrite" or "append" are valid config root names.
+<p>
+Under the config root, a tree of sections (hashes) and setting values
+(text nodes) are built. These structures and values are in 1:1 
+correspondance with <a href="groups.html">the config tree</a>. Excess
+(unknown) keys are considered a warning (except in the plugin/ and
+utils/ subtrees). Missing keys or missing subtrees is normal because a config
+root can be partial.
+<p>
+TODO: examples
+
+<H3> list syntax </h3>
+TODO: list syntax
+
+<H3> in project files </H3>
+TODO
diff --git a/doc-rnd/conf/tree/CFN_BOOLEAN.html b/doc-rnd/conf/tree/CFN_BOOLEAN.html
new file mode 100644
index 0000000..febb64e
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_BOOLEAN.html
@@ -0,0 +1,8 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: boolean</h2>
+Boolean value: true or false. Most often used for determining whether a
+feature or a (display mode) is enabled.
+<p>
+Example values: true, false, on, off, yes, no, 1, 0.
diff --git a/doc-rnd/conf/tree/CFN_COLOR.html b/doc-rnd/conf/tree/CFN_COLOR.html
new file mode 100644
index 0000000..de3ae09
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_COLOR.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: color</h2>
+A color description. Use the "webcolor" format: #rrggbb, where
+rr, gg and bb are 2 digit hexadecimal values for red, green and blue
+components.
+<p>
+Example values: #ff0000 for red, #555555 for grey.
diff --git a/doc-rnd/conf/tree/CFN_COORD.html b/doc-rnd/conf/tree/CFN_COORD.html
new file mode 100644
index 0000000..214160f
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_COORD.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: coord</h2>
+A coordinate: size, distance, spacing. A decimal number with an unit. Unit
+can be metric (e.g. mm, cm, m) or imperial (e.g. mil). 
+<p>
+Example values: 1.5mm, 15 mil
+
+
diff --git a/doc-rnd/conf/tree/CFN_INCREMENTS.html b/doc-rnd/conf/tree/CFN_INCREMENTS.html
new file mode 100644
index 0000000..9b2a4a1
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_INCREMENTS.html
@@ -0,0 +1,8 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: increments</h2>
+A collection of coordinates representing an increment configuration.
+<p>
+TODO
+
diff --git a/doc-rnd/conf/tree/CFN_INTEGER.html b/doc-rnd/conf/tree/CFN_INTEGER.html
new file mode 100644
index 0000000..4731f90
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_INTEGER.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: integer</h2>
+A decimal integer value without unit. The value might be stored in a
+32 bit integer; safe range is approximately -2^31 .. 2^31.
+<p>
+Example values: 4, 1500, 2545343, -6
+
+
diff --git a/doc-rnd/conf/tree/CFN_LIST.html b/doc-rnd/conf/tree/CFN_LIST.html
new file mode 100644
index 0000000..8a64694
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_LIST.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: list</h2>
+An ordered list of strings.
+<p>
+Example values: 
+<pre>
+li:{ foo; bar; {foo/bar/with-punctuation}; 123}
+</pre>
diff --git a/doc-rnd/conf/tree/CFN_REAL.html b/doc-rnd/conf/tree/CFN_REAL.html
new file mode 100644
index 0000000..f089a89
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_REAL.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: real</h2>
+A decimal numeric value without unit. 
+<p>
+Example values: 3.141592654, 5, -12, 0
+
+
diff --git a/doc-rnd/conf/tree/CFN_STRING.html b/doc-rnd/conf/tree/CFN_STRING.html
new file mode 100644
index 0000000..638e355
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_STRING.html
@@ -0,0 +1,13 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: string</h2>
+Text value.
+<p>
+Example values:
+<pre>
+foo
+bar
+{long text with / punctuation?}
+</pre>
+
diff --git a/doc-rnd/conf/tree/CFN_UNIT.html b/doc-rnd/conf/tree/CFN_UNIT.html
new file mode 100644
index 0000000..a477411
--- /dev/null
+++ b/doc-rnd/conf/tree/CFN_UNIT.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<h1>pcb-rnd conf tree</h1>
+<h2>type: unit</h2>
+Name of a unit.
+<p>
+Example values: mm, cm, m, mil
+
+
diff --git a/doc-rnd/conf/tree/appearance.html b/doc-rnd/conf/tree/appearance.html
new file mode 100644
index 0000000..fa5f781
--- /dev/null
+++ b/doc-rnd/conf/tree/appearance.html
@@ -0,0 +1,8 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: appearance</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> rat_thickness <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <rat_thickness>
+<tr><td> mark_size <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> relative marker size
+</table></body></html>
diff --git a/doc-rnd/conf/tree/appearance_color.html b/doc-rnd/conf/tree/appearance_color.html
new file mode 100644
index 0000000..da354f5
--- /dev/null
+++ b/doc-rnd/conf/tree/appearance_color.html
@@ -0,0 +1,30 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: appearance/color</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> black <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <black>
+<tr><td> white <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <white>
+<tr><td> background <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> background and cursor color ...
+<tr><td> crosshair <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> different object colors
+<tr><td> cross <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <cross>
+<tr><td> via <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <via>
+<tr><td> via_selected <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <via_selected>
+<tr><td> pin <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <pin>
+<tr><td> pin_selected <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <pin_selected>
+<tr><td> pin_name <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <pin_name>
+<tr><td> element <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <element>
+<tr><td> element_nonetlist <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <element_nonetlist>
+<tr><td> rat <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <rat>
+<tr><td> invisible_objects <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <invisible_objects>
+<tr><td> invisible_mark <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <invisible_mark>
+<tr><td> element_selected <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <element_selected>
+<tr><td> rat_selected <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <rat_selected>
+<tr><td> connected <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <connected>
+<tr><td> off_limit <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <off_limit>
+<tr><td> grid <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <grid>
+<tr><td> layer <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <layer>
+<tr><td> layer_selected <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <layer_selected>
+<tr><td> warn <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <warn>
+<tr><td> mask <td><a href="CFN_COLOR.html"> color </a><td> 0 <td> <mask>
+</table></body></html>
diff --git a/doc-rnd/conf/tree/appearance_messages.html b/doc-rnd/conf/tree/appearance_messages.html
new file mode 100644
index 0000000..2b8253e
--- /dev/null
+++ b/doc-rnd/conf/tree/appearance_messages.html
@@ -0,0 +1,7 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: appearance/messages</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> char_per_line <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> width of an output line in characters (used by separator drawing in find.c)
+</table></body></html>
diff --git a/doc-rnd/conf/tree/appearance_misc.html b/doc-rnd/conf/tree/appearance_misc.html
new file mode 100644
index 0000000..ae101d8
--- /dev/null
+++ b/doc-rnd/conf/tree/appearance_misc.html
@@ -0,0 +1,7 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: appearance/misc</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> volume <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> the speakers volume -100..100
+</table></body></html>
diff --git a/doc-rnd/conf/tree/appearance_pinout.html b/doc-rnd/conf/tree/appearance_pinout.html
new file mode 100644
index 0000000..3bd297a
--- /dev/null
+++ b/doc-rnd/conf/tree/appearance_pinout.html
@@ -0,0 +1,12 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: appearance/pinout</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> name_length <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> <name_length>
+<tr><td> zoom <td><a href="CFN_REAL.html"> real </a><td> 0 <td> <zoom>
+<tr><td> offset_x <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> X offset of origin
+<tr><td> offset_y <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> Y offset of origin
+<tr><td> text_offset_x <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> X offset of text from pin center
+<tr><td> text_offset_y <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> Y offset of text from pin center
+</table></body></html>
diff --git a/doc-rnd/conf/tree/design.html b/doc-rnd/conf/tree/design.html
new file mode 100644
index 0000000..c86b623
--- /dev/null
+++ b/doc-rnd/conf/tree/design.html
@@ -0,0 +1,26 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: design</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> via_thickness <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <via_thickness>
+<tr><td> via_drilling_hole <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <via_drilling_hole>
+<tr><td> line_thickness <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <line_thickness>
+<tr><td> clearance <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <clearance>
+<tr><td> max_width <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <max_width>
+<tr><td> max_height <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <max_height>
+<tr><td> alignment_distance <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> default drc size
+<tr><td> bloat <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> default drc size
+<tr><td> shrink <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <shrink>
+<tr><td> min_wid <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <min_wid>
+<tr><td> min_slk <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <min_slk>
+<tr><td> min_drill <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <min_drill>
+<tr><td> min_ring <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> <min_ring>
+<tr><td> text_scale <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> text scaling in %
+<tr><td> poly_isle_area <td><a href="CFN_REAL.html"> real </a><td> 0 <td> polygon min area
+<tr><td> default_layer_name <td><a href="CFN_STRING.html"> string </a><td> 0 <td> <default_layer_name>
+<tr><td> fab_author <td><a href="CFN_STRING.html"> string </a><td> 0 <td> Full name of author for FAB drawings
+<tr><td> initial_layer_stack <td><a href="CFN_STRING.html"> string </a><td> 0 <td> If set, the initial layer stack is set to this
+<tr><td> groups <td><a href="CFN_STRING.html"> string </a><td> 0 <td> string with layergroups
+<tr><td> routes <td><a href="CFN_STRING.html"> string </a><td> 0 <td> string with route styles
+</table></body></html>
diff --git a/doc-rnd/conf/tree/editor.html b/doc-rnd/conf/tree/editor.html
new file mode 100644
index 0000000..ce85e99
--- /dev/null
+++ b/doc-rnd/conf/tree/editor.html
@@ -0,0 +1,49 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: editor</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> grid_unit <td><a href="CFN_UNIT.html"> unit </a><td> 0 <td> select whether you draw in mm or mil
+<tr><td> grid <td><a href="CFN_COORD.html"> coord </a><td> 0 <td> grid in pcb-units
+<tr><td> increments_mm <td><a href="CFN_INCREMENTS.html"> increments </a><td> 0 <td> increments (size deltas) when drawing in mil
+<tr><td> increments_mil <td><a href="CFN_INCREMENTS.html"> increments </a><td> 0 <td> increments (size deltas) when drawing in mil
+<tr><td> zoom <td><a href="CFN_REAL.html"> real </a><td> 0 <td> default zoom
+<tr><td> mode <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> currently active mode
+<tr><td> buffer_number <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> number of the current buffer
+<tr><td> clear_line <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> new lines/arc clear polygons.
+<tr><td> full_poly <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> new polygons are full polygons.
+<tr><td> unique_names <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> force unique names
+<tr><td> snap_pin <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> snap to pins and pads
+<tr><td> snap_offgrid_line <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> Snap to certain off-grid points along a line.
+<tr><td> highlight_on_point <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> Highlight if crosshair is on endpoints.
+<tr><td> show_solder_side <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> mirror output
+<tr><td> save_last_command <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> the command entry editline always starts with the last command entered by user in the current session
+<tr><td> line_refraction <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> value for line lookahead setting
+<tr><td> save_in_tmp <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> emergency save unsaved PCB data (despite the user clicks don't save) when: user starts a new PCB; user quits pcb-rnd. Does not affect the on-crash emergency save.
+<tr><td> draw_grid <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> draw grid points
+<tr><td> all_direction_lines <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> enable lines to all directions
+<tr><td> rubber_band_mode <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> move, rotate use rubberband connections
+<tr><td> swap_start_direction <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> change starting direction after each click
+<tr><td> show_drc <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> show drc region on crosshair
+<tr><td> auto_drc <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> when set, PCB doesn't let you place copper that violates DRC.
+<tr><td> show_number <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> pinout shows number
+<tr><td> orthogonal_moves <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> move items orthogonally.
+<tr><td> reset_after_element <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> reset connections after each element while saving all connections
+<tr><td> auto_place <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> flag which says we should force placement of the windows on startup
+<tr><td> lock_names <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> lock down text so they can not be moved or selected
+<tr><td> only_names <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> lock down everything else but text so only text objects can be moved or selected
+<tr><td> thin_draw <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> if set, objects on the screen are drawn as outlines (lines are drawn as center-lines).  This lets you see line endpoints hidden under pins, for example.
+<tr><td> thin_draw_poly <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> if set, polygons on the screen are drawn as outlines.
+<tr><td> local_ref <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> use local reference for moves, by setting the mark at the beginning of each move.
+<tr><td> check_planes <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> when set, only polygons and their clearances are drawn, to see if polygons have isolated regions.
+<tr><td> show_mask <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> show the solder mask layer
+<tr><td> hide_names <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> when set, element names are not drawn.
+<tr><td> description <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> display element description as element name, instead of value
+<tr><td> name_on_pcb <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> display Reference Designator as element name, instead of value
+<tr><td> fullscreen <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> hide widgets to make more room for the drawing
+<tr><td> click_time <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> default time for click expiration, in ms
+<tr><td> enable_stroke <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> Enable libstroke gestures on middle mouse button when non-zero
+<tr><td> live_routing <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> autorouter shows tracks in progress
+<tr><td> beep_when_finished <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> flag if a signal should be produced when searching of  connections is done
+<tr><td> undo_warning_size <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> warn the user when undo list exceeds this amount of kilobytes in memory
+</table></body></html>
diff --git a/doc-rnd/conf/tree/editor_view.html b/doc-rnd/conf/tree/editor_view.html
new file mode 100644
index 0000000..a2c3932
--- /dev/null
+++ b/doc-rnd/conf/tree/editor_view.html
@@ -0,0 +1,8 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: editor/view</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> flip_x <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> view: flip the board along the X (horizontal) axis
+<tr><td> flip_y <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> view: flip the board along the Y (vertical) axis
+</table></body></html>
diff --git a/doc-rnd/conf/tree/rc.html b/doc-rnd/conf/tree/rc.html
new file mode 100644
index 0000000..2ca707f
--- /dev/null
+++ b/doc-rnd/conf/tree/rc.html
@@ -0,0 +1,25 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: rc</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> verbose <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> <verbose>
+<tr><td> backup_interval <td><a href="CFN_INTEGER.html"> integer </a><td> 0 <td> time between two backups in seconds
+<tr><td> font_command <td><a href="CFN_STRING.html"> string </a><td> 0 <td> commands for file loading...
+<tr><td> file_command <td><a href="CFN_STRING.html"> string </a><td> 0 <td> <file_command>
+<tr><td> file_path <td><a href="CFN_STRING.html"> string </a><td> 0 <td> <file_path>
+<tr><td> library_shell <td><a href="CFN_STRING.html"> string </a><td> 0 <td> <library_shell>
+<tr><td> library_search_paths <td><a href="CFN_LIST.html"> list </a><td> 0 <td> <library_search_paths>
+<tr><td> emergency_name <td><a href="CFN_STRING.html"> string </a><td> 0 <td> file name template for emergency save anonymous .pcb files (when pcb-rnd crashes); optional field: %ld --> pid; must be shorter than 240 characters. Don't do emergency save if this item is empty.
+<tr><td> backup_name <td><a href="CFN_STRING.html"> string </a><td> 0 <td> file name template for periodic backup anonymous .pcb files; optional fields: %P --> pid
+<tr><td> save_command <td><a href="CFN_STRING.html"> string </a><td> 0 <td> command to pipe the pcb, footprint or buffer file into, when saving (makes lihata persist impossible)
+<tr><td> keep_save_backups <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> a copy is made before a save operation overwrites an existing file; if this setting is true, keep the copy even after a successful save
+<tr><td> default_font_file <td><a href="CFN_LIST.html"> list </a><td> 0 <td> name of default font file (list of names to search)
+<tr><td> default_pcb_file <td><a href="CFN_LIST.html"> list </a><td> 0 <td> <default_pcb_file>
+<tr><td> script_filename <td><a href="CFN_STRING.html"> string </a><td> 0 <td> PCB Actions script to execute on startup
+<tr><td> action_string <td><a href="CFN_STRING.html"> string </a><td> 0 <td> PCB Actions string to execute on startup
+<tr><td> rat_path <td><a href="CFN_STRING.html"> string </a><td> 0 <td> <rat_path>
+<tr><td> rat_command <td><a href="CFN_STRING.html"> string </a><td> 0 <td> <rat_command>
+<tr><td> preferred_gui <td><a href="CFN_LIST.html"> list </a><td> 0 <td> if set, try GUI HIDs in this order when no GUI is explicitly selected
+<tr><td> have_regex <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> whether we have regex compiled in
+</table></body></html>
diff --git a/doc-rnd/conf/tree/rc_path.html b/doc-rnd/conf/tree/rc_path.html
new file mode 100644
index 0000000..4aada00
--- /dev/null
+++ b/doc-rnd/conf/tree/rc_path.html
@@ -0,0 +1,12 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: rc/path</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> prefix <td><a href="CFN_STRING.html"> string </a><td> 0 <td> e.g. /usr/local
+<tr><td> lib <td><a href="CFN_STRING.html"> string </a><td> 0 <td> e.g. /usr/lib/pcb-rnd
+<tr><td> bin <td><a href="CFN_STRING.html"> string </a><td> 0 <td> e.g. /usr/bin
+<tr><td> share <td><a href="CFN_STRING.html"> string </a><td> 0 <td> e.g. /usr/share/pcb-rnd
+<tr><td> home <td><a href="CFN_STRING.html"> string </a><td> 0 <td> user's home dir, determined run-time
+<tr><td> exec_prefix <td><a href="CFN_STRING.html"> string </a><td> 0 <td> exec prefix path (extracted from argv[0])
+</table></body></html>
diff --git a/doc-rnd/conf/tree/temp.html b/doc-rnd/conf/tree/temp.html
new file mode 100644
index 0000000..3e871e5
--- /dev/null
+++ b/doc-rnd/conf/tree/temp.html
@@ -0,0 +1,7 @@
+<html><body>
+<h1>pcb-rnd conf tree</h1>
+<h2>subtree: temp</h2>
+<table border=1>
+<tr><th>node name <th> type <td> flags <td> description
+<tr><td> rat_warn <td><a href="CFN_BOOLEAN.html"> boolean </a><td> 0 <td> rats nest has set warnings
+</table></body></html>
diff --git a/doc-rnd/contrib.html b/doc-rnd/contrib.html
new file mode 100644
index 0000000..6ba1bb3
--- /dev/null
+++ b/doc-rnd/contrib.html
@@ -0,0 +1,66 @@
+<html>
+<head>
+	<title> pcb-rnd - contribution </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - contribution </h1>
+
+If you are interested to help out, please contact me using
+<a href="irc.html"> the live chat (CET daytime)</a> or mail to:
+pcb-rnd (at) igor2.repo.hu.
+
+<H2> Contributing as a user </h2>
+Using pcb-rnd in production and just reporting bugs is already a huge contribution.
+<p>
+More dedicated users can join in scheduled, systematic testing. Such testing
+is split up into independent chunks that each can be done in an hour.
+<p>
+Even more dedicated users can join developing the documentation.
+<p>
+None of the above requires or assumes any programming skill.
+
+<H2> Contributing as a developer </h2>
+
+The project is lead in an autocratic way by Igor2.
+
+Developer freedom taken away:
+<ul>
+	<li> the basic concept of pcb-rnd is set and won't change
+	<li> the toolchain is chosen, and won't change; the usual hot topics: svn, scconfig, <a href="hacking/c89.html">C</a>.
+	<li> don't add code that restricts usability for others; add code that increases usability for some; thus prefer to add code in plugins!
+</ul>
+<p>
+Developer freedom promptly granted:
+<ul>
+	<li> svn commit access from day 0
+	<li> work together in trunk/, not alone in a branch
+	<li> pcb-rnd has a strong plugin support; want to implement a strange/controversial feature? In a plugin, almost anything goes
+	<li> especially in plugins, work on whatever you want, even if noone else needs that feature
+</ul>
+<p>
+Coding style/indentation: there's an <a href="hacking/indent.html">unified style</a>
+in core and core plugins. If you work on those parts, try to stick to it. If you
+are working on a new plugin, it's still the preferred style but you can use
+a different style. However, the rule is that if someone else starts hacking
+that part too, he is allowed to convert the code to the unified format; but
+unified format can not be converted to anything else. So it's a one way
+process which long term grants unified style while not demotivates a plugin
+developer by forcing a style on him in the early, "when the bulk of the code
+is written" phase.
+</body>
+</html>
diff --git a/doc-rnd/ddrc/examples1.txt b/doc-rnd/ddrc/examples1.txt
new file mode 100644
index 0000000..bdab078
--- /dev/null
+++ b/doc-rnd/ddrc/examples1.txt
@@ -0,0 +1,54 @@
+# Example 1: Sensitive RF elements should be close
+#
+# e1 is the list of elements that have the user tag RF
+# e2 is a copy of e1; it's needed to get two loops (e1 vs. e2 objects form pairs)
+# Assert that any two components are closer than 25 mm to each other
+
+rule sensitive RF elements
+	let e1 (type(@, element)) && (@.a.group == "RF")
+	let e2 e1
+	assert (distance(e1, e2) < 25 mm)
+
+# Example 2: matched trace lengths on dram data 0
+#
+# e1 is the list of dram traces
+# e2 is a copy of e1 (see Example 1)
+# Assert that any two net lengths are matched within 2 mm
+
+rule matched length dram traces
+	let e1 (type(@, net)) && (@.a.group == "dram_data_0")
+	let e2 e1
+	assert abs(netlen(e1) - netlen(e2)) < 2 mm
+
+# Example 3: check if isolation gap is maintained between any HV and LV nets
+# (Lazy: assume any copper is part of a network, compare net-to-net only)
+#
+# hv  is the list of high voltage nets
+# lv  is a list of non-hv networks
+#
+
+rule isolation gap 1
+	let hv  (type(@, net)) && (@.a.high_voltage)
+	let lv  (type(@, net)) && !(@.a.high_voltage)
+	assert !is_closer(lv, hv, 4 mm) 
+
+# Example 4: check if isolation gap is maintained between any HV and LV nets
+# (Proper way: do not forget about copper not part of any network!)
+#
+# hv  is the list of high voltage nets
+# hvo is a list of all objects found in hv
+# cp  is a list of all copper object
+# There are two iterators in the assert: cp (all copper objects) and hv.
+# For each hv and cp pair:
+#   - lcomplement returns a cp object if the cp object is not in hv (i.e.
+#     the object is low voltage); else the return value is empty and is_closer
+#     returns invalid which is not a violation
+#   - hvo is required because cp is an object while hv is a list of nets
+#     (so cp is never on the hv list)
+#   - if there was a valid (low voltage) cp object found, 
+
+rule isolation gap 2
+	let hv  (type(@, net)) && (@.a.high_voltage)
+	let hvo lobjs(hv)
+	let cp  (type(@, copper))
+	assert !is_closer(lcomplement(cp, hvo), hv, 4 mm) 
diff --git a/doc-rnd/ddrc/proposal1.txt b/doc-rnd/ddrc/proposal1.txt
new file mode 100644
index 0000000..cffce71
--- /dev/null
+++ b/doc-rnd/ddrc/proposal1.txt
@@ -0,0 +1,242 @@
+Dynamic DRC proposal 1: a Declarative DRC language.
+
+P0  A DRC program is an unordered list of rules. Rules are evaluated and
+    violations reported. The advantage of a declarative language is that
+    intermediate results can be cached and reused.
+
+P1  The language is intended to be human readable and human writable, but
+    the main goal is to let programs and scripts (e.g. netlisters) to
+    generate it.
+
+P2  A rule consists of three parts:
+     - the rule keyword; syntax: rule NAME
+     - build variables (lists) using search statements
+     - state assertions about those lists.
+     - comments, empty lines
+
+P3  Variables are named by the user and are local to the rule (TODO)
+
+P4  Lists are ordered.
+
+P5  A list is consists of zero or more objects. An object is:
+P6   - the board
+P7   - a layer
+P8   - a board drawing primitive (line, arc, polygon, via, text)
+P9   - an element primitive (element line, element arc(?), pin, pad, element name)
+P10  - an element as a whole
+P11  - a net
+P61  - a 2D coordinate with or without layer information
+
+P12 Objects have named properties (or fields):
+P13  - core attributes: for each object type a predefined set of key=value
+       pairs that always exist (e.g. thickness of a line, start angle of
+       an arc); these field names starts with "p."
+P14  - user attributes: free-form key=value pairs optionally assigned by
+       the user; these field names start with "a."
+
+P15 Note: the language is case sensitive with keywords and builtins using
+    lowercase only. For better readability, in syntax description in this
+    document uppercase words are user chosen identifiers or fields. Whitespace
+    character sequences are usually treated as a single whitespace. (This
+    does not mean identifiers have to be uppercase in a program.)
+
+    The syntax of a search statement is:
+
+P16   let LISTNAME EXPR
+
+P17 It creates a list called LISTNAME and evaluates expression EXPR to all
+    available objects and adds the objects that match EXPR to the list. Each
+    matching object is added only once. The particular order of objects on
+    the list is random. Object "matches EXPR" when the EXPR evaluated on
+    the object yields true.
+
+P18 The current object used in the iteration during the search is called
+    @.
+
+P19 An expression returns a value. A value can be:
+P20   - an object
+P21   - a list
+P22   - scalar: a number or string (might be suffixed, like "42 mm")
+P23   - void (empty, also known as false)
+
+P23 A value is considered true if:
+P24  - it is an existing object
+P25  - it is a non-empty list
+P26  - it is a non-zero number or non-empty string
+P69  - it is a valid coordinate
+
+    An expression is one of:
+
+    syntax                      meaning
+    ----------------------------------------------------------------
+P27 (EXPR)                      change precedence
+P28 EXPR || EXPR                logical OR (result: number)
+P29 EXPR && EXPR                logical AND (result: number)
+P30 EXPR + EXPR                 add (number only)
+P31 EXPR - EXPR                 subtract (number only)
+P32 EXPR * EXPR                 multiply or ... (number only)
+P32 EXPR / EXPR                 multiply or ... (number only)
+P32 EXPR == EXPR                the two values are equal
+P33 EXPR != EXPR                the two values are not equal
+P71 EXPR ~ string               regex match left EXPR using pattern right string
+P34 EXPR > EXPR                 left EXPR is greater than right EXPR (number only)
+P35 EXPR >= EXPR                left EXPR is greater than or equal to right EXPR (number only)
+P36 EXPR < EXPR                 left EXPR is less than right EXPR (number only)
+P37 EXPR <= EXPR                left EXPR is less than or equal to right EXPR (number only)
+P38 !EXPR                       logical NOT (result: number, 0 or 1)
+P39 FUNC(EXPR, EXPR, ...)       call a function with 0 or more arguments
+P40 EXPR.field                  evaluated to the value of an object field (see P45, P46)
+
+    The syntax of an assertion is:
+
+P41   assert EXPR
+
+P42 If the EXPR in an assert evaluates to false, a DRC violation is generated.
+
+P43 If an assert EXPR is a list anywhere else than in a function argument, it is
+    evaluated for all valid members of the list (see P45, P46). For example
+    if there is a variable called FOO, which is a list of objects
+    (built using a search statement), expression
+
+      FOO.p.thickness
+
+    is evaluated as many times as many objects are on the list, and the
+    full assert is checked each case. If there is another similar list
+    called BAR, an expression:
+
+     (FOO.p.thickness < BAR.p.thickness)
+
+    will compare each possible pair of FOO and BAR objects. That is, if
+    FOO has 4 objects and BAR has 15 objects, that is 4*15 = 60 comparisons.
+
+P44 However, each list is iterated only once, even if it is referenced multiple
+    times in the same expression. For example, with the above lists:
+
+    (FOO.p.clearance > 10 mil) && (FOO.p.thickness < BAR.p.thickness)
+
+    the potential number of iterations is still 4*15, and not 4*4*15. In
+    practice the engine leverages lazy evaluation so if FOO.p.clearance
+    is smaller than 10 mil, the right size is not evaluated. See also: P45, P46.
+
+P45 A field reference is valid if the field exists. For example a line object
+    has a thickness attribute, thus the .p.thickness is valid, but a polygon
+    object does not have a thickness and .p.thickness on a polygon is invalid.
+    An user attribute reference (e.g. field .a.baz) is valid if the attribute
+    key exists in the given object.
+
+P46 Invalid fields are skipped in iterations. Thus if variable BLOBB is a list
+    that consists of 3 line, 2 arc and a layer objects, the following assert
+    will result in 2 comparisons only:
+
+      (BLOBB.p.width >= 10 mm)
+
+    (because only arc objects have valid .p.width field).
+
+P47. An invalid field in an expression is never considered an
+     error. In an assert statement it causes skipping an iteration. In a
+     search statement it evaluates to void.
+
+P48. A void value is never equal to anything. A void value is not equal
+     even to another void value.
+
+P49. Comments are lines starting with #
+
+
+     BUILTIN FUNCTIONS
+
+P70 list(LISTNAME)
+    Within an expression, a reference to a list may become an iterator and
+    refer to a single object. In case the expression needs the listm the
+    list() function protects LISTNAME from becoming an iterator. For
+    example llen(list(@)) is the number of all objects the design consists.
+
+P50 llen(EXPR)
+    Returns to the number of items of a list (an integer number >= 0).
+
+P51 lvalid(EXPR, field)
+    Returns a list of items on the list for which field is valid.
+    EXPR can be a list or an object.
+
+P52 lunion(EXPR1, EXPR2)
+    A new list is created; items of EXPR1 are copied to the new list in order.
+    Those items from EXPR2 that are not on the list already are appended,
+    keeping their order. The new list is returned.
+
+    Both EXPR1 and EXPR2 can be either a list or an object.
+
+    Note: order of arguments does matter (affects order of objects on the
+    resulting list).
+
+P53 lintersect(EXPR1, EXPR2)
+    A new list is created; items that are present on both EXPR1 and EXPR2
+    are copied onto the new list. The new list is returned.
+
+    Both EXPR1 and EXPR2 can be either a list or an object.
+
+    Note 1: this function can be used as "is this object on the list" query:
+    list_intersection(LIST, OBJ) will result an empty list (which means false)
+    if OBJ is not on the list.
+
+    Note 2: similarly, if both argument are objects, the result is false
+    if the two arguments are not the same object.
+
+P54 lcomplement(EXPR1, EXPR2)
+    Creates a new list with all items that are present in EXPR1 but not in
+    EXPR2. The new list is returned.
+
+    Both EXPR1 and EXPR2 can be either a list or an object.
+
+
+P55 ldiff(EXPR1, EXPR2)
+    Creates a new list with all items that are present either in EXPR1 or in
+    EXPR2 but not in both. The new list is returned.
+
+
+P56 distance(EXPR1, EXPR2)
+    Calculates the shortest distance between two objects. Returns a number.
+
+    Note: if any expression is a layer, the stack distance is calculated
+    (which will be 0 for now, as pcb-rnd doesn't yet know about layer thickness).
+    If the expression is a net, the whole shape of the net is used
+    (expensive! use is_closer() instead)
+    If the expression is the board, the operation is invalid (see P46).
+
+P57 is_closer(EXPR1, EXPR2, NUMBER)
+    Checks if EXPR1 and EXPR2 are closer to each-other than NUMBER. It uses
+    the classic bloat-and-find algorithm originally used by the classic DRC,
+    thus it is much cheaper than distance(EXPR1, EXPR2) < NUMBER.
+
+P58 netlen(EXPR)
+    Network length of EXRP1: sum of all non-overlapping trace/arc lengths that
+    make up a network; polygons are approximated with their average diameter
+    (TODO). If EXPR is an object, its length is returned.
+
+P59 netobjs(EXPR)
+    Creates and returns a list of objects consists of a net.
+
+P60 type(EXPR, TYPENAME)
+    EXPR is an object. Returns the object if it's type matches TYPENAME, else
+    returns void (a.k.a. false). This call is useful to decide whether an
+    object is of a specific type. TYPENAME is one of:
+     - drawing primitive type: arc, line, polygon, text, via, element
+     - element primitive type: element_line, element_name, pin, pad
+     - misc type: layer, net
+     - meta-type, or a group:
+       - copper: primitive that results in copper (arc, line, polygon, text (on copper), via, pin, pad)
+       - drilled: anything that drills the board (via, pin)
+
+P62 gridup(EXPR, NUM1, NUM2)
+    If expression is an object, return a list of coordinates that are on
+    the object, evenly spaced:
+P63  - for lines and arcs, the points are evenly spaced on the centerlane with
+        a gap of NUM1, starting from both endpoints, having the corner case in
+        the middle; if NUM2 is 0, the gap at the middle may be larger than NUM1,
+        else it may be smaller than NUM1.
+P64  - for polygons take a grid of NUM1*NUM2 spacing (NUM1 is always axis
+       aligned to X, NUM2 to Y). The offset of the grid is unspecified
+P65  - element and text are ignored
+P66  - a pad or pin is approximated with a polygon
+P67  - for networks, iterate over all objects and append unique coordinates on
+       the resulting list
+P68 There's no guarantee on the particular order of the list.
+
diff --git a/doc-rnd/ddrc/requirements.txt b/doc-rnd/ddrc/requirements.txt
new file mode 100644
index 0000000..b53c3f7
--- /dev/null
+++ b/doc-rnd/ddrc/requirements.txt
@@ -0,0 +1,23 @@
+The Dynamic DRC should be able to solve at least these tasks:
+	- check start grounding
+	- require element placement on same layer 
+		- specify element proximity to each other on same layer
+		- specify element proximity to the edge
+		- specify element proximity to network or other copper feature
+		on the same layer
+	- perform copper vs. copper checks
+		- minimal gap between an object or network and anything else
+		- minimal gap between high voltage and low voltage networks
+	- perform copper geometry checks
+		- detect minimal width (high current)
+		- detect poly hairpin
+		- number and length of stubs (hight freq)
+		- "via cage": a given network needs to be sorrounded by a set of gnd vias
+	- network related copper checks
+		- matched length lines (e.g. fast dram bus)
+		- balanced transmission line (distance between the tracks)
+		- match and/or limit number of vias
+		- limit layer usage
+		- require layer stackup properties, e.g. microstrip, stripline
+			- e.g. require ground poly on the next layer
+			- e.g. number of gaps in the ground poly the line jumps
diff --git a/doc-rnd/devlog/20150731a_menu.html b/doc-rnd/devlog/20150731a_menu.html
new file mode 100644
index 0000000..44ebc7e
--- /dev/null
+++ b/doc-rnd/devlog/20150731a_menu.html
@@ -0,0 +1,59 @@
+<HTML>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> Dynamic menus </h2>
+<h3> Background </h3>
+Before integrating gpmi scripting, pcb-rnd needs to be prepared to
+interface with the scripts. The engine is pretty much ready since ages:
+the action interface is ideal for scripts to register their actions or
+execute existing actions. This also means the command line and batch
+UI are instantly connected with scripts.
+<p>
+The only largely visible remaining portion is the GUI. pcb-gpmi supports
+building dialog boxes using the current HID (so called attribute dialogs,
+originally invented for exporter settings) and calling some simpler predefined
+dialogs like a progress bar or alert.
+<p>
+What's really missing is a way to create new menus on the fly. The user
+loads a script and the script makes up a menu with submenus, all bound to
+the proper actions.
+<p>
+This introduces a nice dilemma, tho: there is a real cool menu configuration
+file that makes the user able to reconfigure the menu system, hot keys, tool
+tips and whatnot (I wish more applications had this feature!). What if a script
+comes in and trolls the whole thing creating random menus at surprising
+places in the menu system? How the user can control what the script could do
+with his preciously crafted menu setup tailored to his own preferences?
+<p>
+I believe in sharp tools and careful users. I indeed plan to allow scripts to
+do whatever they want with the menu system but I invent some conventions too.
+As long as scripts stick to these conventions, the user retain control over
+the menu layout.
+
+<h3> How it works </h3>
+
+PCB reads *menu.res and builds the gui on startup; the menu system is
+static after that point. pcb-rnd does the same on startup but also
+stores all menus (referenced by their paths, like "/File/Save as") in a
+hash table. Later on a call to a new hid function (create_menu()) can
+create new menus or submenus, extending the menu tree at any point.
+<p>
+When a new menu "/File/foo" is created, using the hash create_menu()
+finds out that /File already exists and doesn't create it but use the
+existing widget from the hash. This means if the user creates menus from
+the res file that are also created by the script, the res file sort of
+overrides the script's later actions as all those menus will already exist
+by the time the script tries to create them.
+<p>
+And here comes the <b>conventions</b> part:
+<ul>
+	<li> there will be a new menu, called "/Plugins"
+	<li> each plugin should have its own submenu there (gpmi for scripting: "/Plugins/GPMI scripting")
+	<li> each script is just a child of the gpmi plugin so it should have its submenus under "Plugins/GPMI scripting/scriptname"
+</ul>
+
+As long as plugins and scripts stick to this convention, the user can
+create all the menus  for all the plugins and scripts in advance. Stock
+menu res files will have the /Plugin menu at least, so its place is fixed.
+
+
diff --git a/doc-rnd/devlog/20150731b_gtk.html b/doc-rnd/devlog/20150731b_gtk.html
new file mode 100644
index 0000000..664c360
--- /dev/null
+++ b/doc-rnd/devlog/20150731b_gtk.html
@@ -0,0 +1,36 @@
+<HTML>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> dynamic gtk menus </h2>
+As described <a href="20150731a_menu.html"> in the previous post</a>,
+pcb-rnd will feature dynamic menus. I first implemented this in the
+gtk hid.
+<p>
+It turned out gtk was way too OOP for my taste<sup>1</sup>. It took about 6
+hours total, to implement and debug the feature. (As a comparison, it
+took about 90 minutes to make the research related to
+the resource structs in PCB and implement the HID modifications for
+the new hid command). The trickiest part was
+to figure the need of calling a show() on the menubar after adding
+the new items.
+<p>
+When I didn't have that, only plain action menu/submenus showed up
+(in already existing menu/ widgets), but
+new (main) menus and menu/submenu/submenus didn't. After many hours of gdb
+sessions I finally made sure (by printing all the widgets to stderr
+recursively) that:
+<ul>
+	<li> I am really adding the new widgets...
+	<li> ... under the right parent
+	<li> ... with the right properties
+</ul>
+Then I also printed allocated dimensions and coordinates - but the values
+were misleading. Finally I figured there was different flags like realization
+and visibility, so printed them too. This revealed that all my new menus were
+there in an invisible state.
+<hr>
+<h4>Footnotes</h4>
+<sup>1</sup>: not because of the show() call, but because of the way objects
+are stored in memory: too many generic pointers casted forth and back, makes it
+really hard to print all properties of a menu item (including its label)
+with gdb.
diff --git a/doc-rnd/devlog/20150801a_events.html b/doc-rnd/devlog/20150801a_events.html
new file mode 100644
index 0000000..7015b87
--- /dev/null
+++ b/doc-rnd/devlog/20150801a_events.html
@@ -0,0 +1,22 @@
+<HTML>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> events </h2>
+PCB has a nice <i>action infrastructure</i>: most user actions are
+implemented in smallish functions taking (int argc, char *argv[]). Menu
+items, hotkeys, the command line and batch processing just call these
+actions. This is the user->pcb_core direction of commands.
+<p>
+Before adding scripting to pcb-rnd, the other direction (pcb_core->user) has
+to be implemented: an <i>event infrastructure</i>. Random parts of the core
+or the HID code will yield events using a simple vararg event() function.
+Other parts of PCB, especially plugins, can bind (sign up to) events with
+function pointers. When an event is triggered, all functions signed up to
+the event are called (in random order).
+<p>
+Passing arguments to events is similar to the arguments of actions, except
+argv[] is not a char *[], but an event_arg_t *[]. event_arg_t has an enum
+field for argument type and an union for the argument value. This means the
+API is binary: integers are passed as integers, not as strings.
+
+
diff --git a/doc-rnd/devlog/20150803a_scriptig.html b/doc-rnd/devlog/20150803a_scriptig.html
new file mode 100644
index 0000000..1aeafc6
--- /dev/null
+++ b/doc-rnd/devlog/20150803a_scriptig.html
@@ -0,0 +1,36 @@
+<HTML>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> scripting </h2>
+Most of the infrastructure for a scriptable pcb had been there in pcb-gpmi.
+I had also finished some gpmi cleanup and pcb 
+(<a href="20150731a_menu.html">menu</a> and 
+<a href="20150801a_events.html"> events </a>) subprojects last week.
+These made it easier to get to reach the current status after a weekend
+coding marathon:
+<ul>
+	<li> dialog boxes for managing (loading/unloading) scripts -- check out the videos:
+<a href="https://archive.org/details/Pcb-rndManageScriptsDialog"> list scripts and get details </a> or 
+<a href="https://archive.org/details/Pcb-rndLoadScript"> load a new script </a>
+
+
+	<li> import, test, extend the glue layer; scripts can:
+	<ul>
+		<li> register new actions
+		<li> create new menus and submenus
+		<li> execute existing actions
+		<li> pop up simple dialog boxes (message, report, progress bar, file selection) -- <a href="https://archive.org/details/Pcb-rndCarcScript"> check out the video </a>
+		<li> build and pop up custom dialog boxes (so called attribute dialogs)
+		<li> search for objects (lines, arc, polys, vias, etc.) on the layout
+		<li> create new objects on the layout
+		<li> change "page" properties (dimensions of the board)
+		<li> debug draw on the gui (slightly broken on gtk due to some gtk hid bugs)
+	</ul>
+</ul>
+<p>
+My example scripts are written in <b>awk</b>, <b>lua</b> and <b>tcl</b>
+at this point, but gpmi supports 7 other languages
+(<b>scheme</b>, <b>stutter (lisp)</b>, <b>pascal</b>, <b>perl</b>
+<b>php</b>, <b>python</b>, <b>ruby</b>) which are also available in
+pcb-rnd.
+
diff --git a/doc-rnd/devlog/20150816a_scriptig.html b/doc-rnd/devlog/20150816a_scriptig.html
new file mode 100644
index 0000000..609f13b
--- /dev/null
+++ b/doc-rnd/devlog/20150816a_scriptig.html
@@ -0,0 +1,28 @@
+<HTML>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> scripting, 2 </h2>
+I've been working on the documentation for pcb-rnd scripting lately. 
+Proof reading and comments would be appreciated. The documents are 
+work-in-progress.
+<p>
+I have a <a href="http://repo.hu/projects/pcb-rnd/gpmi/rosetta/index.html">Rosetta stone</a>
+of examples demonstrating how to write simple scripts.
+It doesn't explain how the system works, but shows easy-to-understand 
+practical examples. Useful for those who like learn by doing and look up 
+the "theoretical" background only after seeing things in practice.
+<p>
+Another document, the <a href="http://repo.hu/projects/pcb-rnd/gpmi/scripting_intro.html">
+scripting intro</a> focuses on explaining how things are built up. This
+one is useful for those who first want to understand the design  and then
+look at how to do things in practice.
+<p>
+As a next step I plan to reorganize the package documentation and split 
+them all into a high level "what's the concept" doc and a low level 
+reference manual.
+<p>
+I also plan to improve the links between the docs and write more rosetta 
+examples. I plan to have a few more all-language examples on the most 
+basic things. The more complex examples would be written in awk, lua and 
+maybe ruby.
+
diff --git a/doc-rnd/devlog/20150820a_dimensions.html b/doc-rnd/devlog/20150820a_dimensions.html
new file mode 100644
index 0000000..3b4a6a7
--- /dev/null
+++ b/doc-rnd/devlog/20150820a_dimensions.html
@@ -0,0 +1,24 @@
+<HTML>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> fp2anim dimensions </h2>
+After attacking the qfn()/qfp() parametric footprint problem today,
+I realized fp2anim lacked a very important feature: dimension lines.
+The footprint being generated is to match the datasheet. Checking the match
+requires visible dimensions.
+<p>
+The new feature of fp2anim is to optionally display custom dimensions on
+the preview. Generator scripts print #dimension comments in the footprint
+file (not breaking the file format). The generator passes on a dimension
+name along with the value. As a first attempt my conventions are:
+<ul>
+	<li> add dimension lines for dimension type input parameters
+	<li> add dimension lines for calculated dimensions if they are likely to be informative and be included in the datasheet
+</ul>
+
+<h2> fp2anim vector font </h2>
+While working on the dimensions, I realized I had to switch to vector fonts:
+the built-in pixel font of animator can not be rotated. The size of a vector
+font text can be calculated, which also enables fp2anim to optionally place
+semi-transparent bars behind the text to make it more visible on dark background
+(like pads).
diff --git a/doc-rnd/devlog/20150820b_qf.html b/doc-rnd/devlog/20150820b_qf.html
new file mode 100644
index 0000000..ef6d046
--- /dev/null
+++ b/doc-rnd/devlog/20150820b_qf.html
@@ -0,0 +1,27 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> qf() </h2>
+The next group of footprints I decided to generate are qfn(), tqfp(), lqfp().
+The generic set of rules for these footprints is:
+<ul>
+	<li> there is a virtual rectangle that forms rows of pads
+	<li> pads are evenly spaced among the edges of that rectangle
+	<li> pin numbering is counter-clockwise
+	<li> the body of the part may be inside of the rectangle or may extend over the pads
+</ul>
+
+<h2> low level flexibility vs. high level comfort </h2>
+qf() currently has 14 arguments. It is flexible enough to generate qfn, tqfp,
+lqfp, and anything similar I've seen in the package datasheet of a major
+vendor. However, it is not straight forward to convert datasheet tables
+into qf() parameters.
+<p>
+On the other hand, in practice we need qfn() and tqfp(), which are special
+cases of qf(). To nail the common use cases, qfn() and tqfp() narrows down
+the number of parameters to 3..4. Even better, these parameters are exactly
+those that are in the name of a typical QFN or TQFP footprint or in the first
+few lines of the dimensions table. I call these scripts frontends to qf().
+<p>
+This makes the common footprints very easy to produce using frontends while
+leaves a (bit more complicated) plan B, qf(), for special cases.
diff --git a/doc-rnd/devlog/20150821a_parametric_requirements.html b/doc-rnd/devlog/20150821a_parametric_requirements.html
new file mode 100644
index 0000000..3ace404
--- /dev/null
+++ b/doc-rnd/devlog/20150821a_parametric_requirements.html
@@ -0,0 +1,32 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+<h2> requirements for parametric footprint generation </h2>
+
+It's appealing to write parametric footprint generators for new footprint
+families: invest some time once, and have a tool that can spit out
+dozens of footprints. However, it is not always worth having a generator
+instead of static footprints. The minimal requirements for a generator are:
+
+<ul>
+
+<li> the footprint family is a series of footprints that can be generated 
+using the same code, with changing some base properties (e.g. number of 
+pins)
+
+<li> dimensions and other properties can be calculated from the base 
+properties with reasonable formulas and conditional code; e.g. sot* is not 
+a good candidate for generation, as sot23 or sot89 can not be generated 
+from some common anestor by varying one or two parameters, but would need 
+a large table that translates package name to a pattern - easier to keep 
+those in static footprint files
+
+<li> has a reasonable amount of existing variations; e.g. it is not worth 
+writing a generator for the dsub family because there are only a few of 
+them in common use (db9, db15, db25, and maybe db37).
+
+<li> preferably a link to a datasheet that shows at least 3 members of the 
+family; if that's not possible, separate datasheets describing at least 3 
+members of the family.
+
+</ul>
diff --git a/doc-rnd/devlog/20150830a_fork_faq.html b/doc-rnd/devlog/20150830a_fork_faq.html
new file mode 100644
index 0000000..fc0e9be
--- /dev/null
+++ b/doc-rnd/devlog/20150830a_fork_faq.html
@@ -0,0 +1,257 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> Fork FAQ </H2>
+
+This document contains my short answers to questions commonly popping up
+in endless flamewars on the geda mailing list. <b>The purpose of this
+document is not to convince anyone about any of the questions</b>, only
+to save me some time typing the same answers when someone asks me
+one these questions for the 100th time. As I don't want to convince you
+that my answers are the best ones, please don't try to convince me that
+they are bad. Been there, done that, never worked in either way: e.g.
+I never convinced any git fanboy to switch to svn, and they never
+convince me to switch to git.
+
+<H3> 1. Why did you fork? </H3>
+<a id="1"><a id="fork">
+<H4> 1.1. Fragmentation is bad, your fork is harmful! </H4>
+I agree that fragmentation is bad. I believe the same aspects of
+the current project management and especially the choice of VCS inevitably
+leads to forks (just look at how many git forks are out there!). Such
+fragmentation of developer and user resources may be bad for a project.
+However, these decisions are not mine: the project is set up like
+that, I can't change it
+<p>
+My fork is just a fork, just like any of the other forks. My fork happens to
+be in an svn repository. I don't think it changes anything important from the
+mainline's point of view.
+
+<H4> 1.2. ... but how will you get all these merged with mainline? </H4>
+I won't. I haven't really used the official, current mainline since like
+2011. I don't really care in what directions it evolves or whether it
+takes features, code or ideas from pcb-rnd or not.
+
+<H4> 1.3. ... but you are in the best position for a merge! </H4>
+I don't think so. I know my changes, but I don't know the changes happened
+in the mainline since ages. I am also not efficient with git. It wouldn't
+take me any less time than for mainline developers. Also please read point 1.2.:
+I have no motivation in spending any time on a merge.
+
+<H4> 1.4. Ok then, but you won't have too many users. </H4>
+Fine. My main intention was to produce a pcb editor for my own use. I am
+happy if it's also useful for others, so I publish it. Having users
+may increase code quality - but other than that, I don't get paid for having
+more users, the project doesn't die if I code it alone, so I am not worried
+about this.
+<p>
+<b>Update (Aug 2016):</b> an active user/contributor community is forming
+around pcb-rnd. It is not a one-man-show anymore.
+
+<H4> 1.5. I'd like to merge something from pcb-rnd to mainline </H4>
+Go ahead. I can help you by explaining my code. I won't generate
+patches, make changes to get my code compatible with the mainline or
+test how it performs in mainline versions.
+
+<H4> 1.6. I'd like to merge something from mainline to pcb-rnd </H4>
+If the feature is in line with the goals of pcb-rnd, fine, I'll give
+you svn commit access from day 0.
+
+<h4> 1.7. Would you abandon the development of pcb-rnd and join the mainline, if... </H4>
+Unlikely. There are many things I have in pcb-rnd which I believe won't ever
+happen in mainline. There are a few which I find critical: if I'd need to
+give up any single item from this list, that would be a deal-breaker for me:
+<ul>
+	<li> simple, centralized VCS (not just the UI, the whole thing)
+	<li> VCS based, zero-administration release and publish interface
+	<li> a sane build system instead of autotools
+	<li> the code won't switch to C++
+</ul>
+
+
+<h4> 1.8. Would you join the development of gschem? </h4>
+Unlikely. See point 1.7. Gschem is not aiming to a C++ transition AFAIK,
+but has a lot of scheme. I don't consider joining or contributing to
+gschem until those points listed in 1.7. are fixed and a new scheme
+policy is introduced. The new policy should be: "from now on
+do not write new code in scheme, use C; while making changes and fixes,
+convert affected scheme code to C. Long term, explicit plan: convert all
+scheme code to C and remove the guile dependency."
+<p>
+I don't expect any of this to happen.
+<p>
+<b>Update (Aug 2016):</b> Instead, I'll launch <a href="20160101_cschem.html"> cschem </a>.
+
+
+<H3> 2. git: did you know... </H3>
+<a id="2"><a id="git">
+Preface: I don't try to convince you not to use git in your project; in
+return, please don't try to convince me I should use git in mine.
+
+<H4> 2.1. ... that there was an svn frontend to git? </H4>
+Yes. 2/3 of my problems with git and DVCS in general are not of technical
+nature. I know the <b>concepts</b> of DVCS and I find them suboptimal in
+case of small teams. A different UI or a good document won't help in that.
+
+<H4> 2.2. ... that there was this great document about git? </H4>
+See 2.1.
+
+<H4> 2.3. ... that DVCS is the future, anything centralized is bad, svn is obsolete? </H4>
+These are not facts, but slogans I don't believe in.
+
+<H4> 2.4. What if someone has to develop offline for long? </H4>
+In the 21th century, when we have cheap mobile internet even in eastern
+Europe?
+
+<H4> 2.5. But feature X is harder with svn! </H4>
+Almost all those features are bad for team work in my experience. They
+are not even needed. Yes, some aspects of the development have to be
+done differently without those - but this is good for the project.
+
+<H4> 2.6. But there are no local repositories and you have to commit unfinished stuff and worry if anything breaks! And branching is hard </H4>
+This is the point. I do not branch. I do not attempt to work offline for long,
+whatever technical support the VCS may provide for that. I try to work in
+a team, on a common code base.
+<p>
+I commit small things. I make sure I do a big transition using
+these small commits in a way that the code stays compilable in between any
+two commits. It rarely breaks trunk/ for longer than a few minutes. I
+need a real branch once a decade.
+
+<H4> 2.7. But that's extra effort and makes life harder! </H4>
+Yes and no. It's certainly harder to design and carry out a big
+reorganization in small steps. This is an extra cost. However, the
+benefits outweight this: everyone working in the same repo,
+other developers see and comment what you are working on, if done right,
+merging is nearly never needed and conflicts are as rare as bugfree
+release of a multi-million line proprietary software.
+
+<h4> 2.8. I don't agree with any of this, git is better! </H4>
+Cool, so go on using git. I didn't try to convince you not to, I just
+explained why I won't.
+
+<H3> 3. opengl </H3>
+<a id="3"><a id="opengl">
+<H4> 3.1. But I need opengl! </H4>
+Use the mainline then. Or contribute the code for sane opengl support
+in pcb-rnd. 
+<p>
+<b>Update (Aug 2016):</b> GUI HIDs are plugins now, multiple GUIs can
+be compiled and chosen run-time. Default GUI is a runtime configuration too.
+All we need to have an opengl GUI HID now is a gl-capable volunteer.
+
+<H4> 3.2. ... good, then please make that opengl hid!</H4>
+No, thanks. I don't need it, I wouldn't use it, there's no point in spending
+my time on it. If you need it, I invite you to do it.
+<p>
+<b>Update (Aug 2016):</b> I can do the build system and plugin administration
+part for you.
+
+<H4> 3.3. Did you know that opengl can be turned off in mainline? </H4>
+Yes. If I have a fork, I want its defaults to reflect my preferences.
+
+<H4> 3.4. But transparent renders are so good on a board with many layers! </H4>
+Not for me: transparency made even a 2 layer board cryptic when I tried. See
+3.1.
+
+
+<H3> 4. programming languages, file formats </H3>
+<a id="4"><a id="c">
+<H4> 4.1. switch to C++, it is so much better </H4>
+Nope. I prefer C.
+
+<H4> 4.2. but pcb-rnd doesn't compile with a C++ compiled </H4>
+Correct: pcb-rnd is written in C. It also doesn't compile with a Pascal
+compiler.
+
+<H4> 4.3. but mainline already invested in preparing a C++ transition </H4>
+Good for them. If I didn't have a fork, I'd fork the day when the first
+C++ class is committed.
+
+<H4> 4.4. we need SQL for the file format </H4>
+No, we don't. I prefer human readable text formats. No, converters won't
+solve that. No, I don't care if python has support for loading SQL files.
+<p>
+<b>Update (Aug 2016):</b> the file format is a replacable plugin now;
+it's even possible to have multiple alternative formats active in the same
+time. It's up to you to implement your format in a plugin.
+
+<H4> 4.3. we need to switch to xml/json (or python or perl arrays) </H4>
+No, we don't need to.
+<p>
+<b>Update (Aug 2016):</b> ... unless you implement it in a plugin, see point 4.4.
+
+<h4> 4.4. ... but they are easier to parse because of existing libs </h4>
+Yup, but in any real life task that part of the parsing is like 5%. The
+rest 95% needs to be programmed anyway. The costs of such a file format change
+is not justified by this minor gain.
+
+<h4> 4.5. ... but the current file format doesn't support feature X </h4>
+<small>
+True. And that's because the internal model (the in-core representation
+and <b>all the code handling that</b>) doesn't support the feature either.
+Changing the file format won't help that. It's similar to point 4.4.: 95%
+of the effort is needed to get the code to support the feature, and by that
+time the cost of getting it into the file format is very small. Costs
+are not justified.
+</small>
+<p>
+<b>Update (Aug 2016):</b> long term the new primary file format will be
+lihata to overcome the above limitations. Later on the internal representation
+may change too.
+
+<H4> 4.6. ... but I can't build a database of the current lib </h4>
+Too bad. Either figure how to do it, or just don't do it. pcb-rnd
+offers scripting, even in languages that have SQL support. In theory
+it wouldn't be that hard to write scripts running in PCB manipulating
+the buffer or even the in-core footprints on one end and connecting
+an SQL database on the other end.
+
+
+<H3> 5. scconfig, autotools, build systems </H3>
+<a id="5"><a id="scconfig">
+<h4> 5.1. scconfig doesn't support feature X that autotools does </h4>
+Are you sure? Maybe it does, just uses a different syntax. If not, did
+you try to send me a feature request?
+
+<h4> 5.2. scconfig's syntax is different </h4>
+Yes. Scconfig has a totally different internal model thus the UI differs
+too. Same as vim has a different UI than emacs, while they are both text
+editors and the two communities are not trying too hard to unify the UIs.
+
+<h4> 5.3. But ./configure has to have the same UI as autotools </h4>
+False.
+
+<h4> 5.4. Most people know autotools, there are merits in supporting the same features or UI </h4>
+I do realize that. I've been working on scconfig since 2008. I've invested
+a lot of time in it. Believe me, I did think it over before I decided that
+the benefits would overweight the drawbacks of developing/using a custom
+config/build system.
+
+<h4> 5.5. But autotools is so much better! </H4>
+No, it isn't. My experience is that with random open source projects
+on anything different from Linux, modern BSD, and sometimes windows, it
+just breaks at least 8 times out of 10. Try it once on IRIX or minix.
+
+<h4> 5.6. So why don't you rather fix autotools? </H4>
+I believe there are multiple design problems in autotools and most of
+the practical problems I faced when using it were directly caused by those.
+In a sense, I did fix these design problems by using a different design.
+The different design is called scconfig and it was much easier to
+write it from scratch.
+
+<h4> 5.7. But scconfig doesn't do cross compilation! </H4>
+False. It does. It's been supported since May 2008. It's been added
+about 1.5 months after the very first file.
+<p>
+<b>Update (Aug 2016):</b> as a practical example, windows .exe has
+been cross-compiled on a Linux box.
+
+<h4> 5.8. I just don't like scconfig. </H4>
+If you have something particular that makes you dislike scconfig,
+a missing feature for example, please let me know.
+<p>
+Else maybe you don't like using anything else than autotools. It's your
+choice; mine is that I do keep on using scconfig in my projects.
+
diff --git a/doc-rnd/devlog/20150830b_back_ann.html b/doc-rnd/devlog/20150830b_back_ann.html
new file mode 100644
index 0000000..cb217e3
--- /dev/null
+++ b/doc-rnd/devlog/20150830b_back_ann.html
@@ -0,0 +1,304 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> back annotation </H2>
+
+<H3> netlists, annotations </H3>
+
+Pcb-rnd (and mainline pcb) maintains a netlist as part of the design. Pcb
+doesn't modify the netlist. The netlist is imported from an external source,
+typically from gschem. This process is called forward annotation.
+<p>
+Sometimes there are a set of connections which contain pin pairs that could
+be swapped. For example the data lines of an external parallel SRAM interface
+to an MCU: it doesn't matter if data bit 1 at the MCU is wired to data bit
+1 or 5 of the SRAM, as there is an 1:1 mapping and no one else is using the
+same bus wires. In this case connections should be swapped during pcb routing
+and annotated back to gschem so that the schematics can be updated. Both
+paths are illustrated below.
+<p>
+<img src="res/20150830b_annot.png" alt="annotation paths">
+<p>
+Forward annotation passes on complete netlists along arrows forward1 and
+forward2. Back annotation would pass back netlists, changes or modification
+requests on the back1, back2 path. Gnetlist takes sch files to extract
+and build a netlist in whatever format the receiver needs. There should be a
+glue layer, called <i>foo</i> on the drawing, that does the reverse: receives
+whatever format the sender has and generates something that gschem will
+understand.
+
+<H3> Support in pcb-rnd: core </H3>
+Pcb-rnd gets a complete netlist. If the user could change the netlist directly,
+there should be some sort of diff tool in <i>foo</i> that can explain the
+changes to gschem, or a diff tool in gschem. What is worse, forward annotation
+happens much more often than back annotation and pcb-rnd would need to be able
+to merge a new netlist with local changes. The simple "gsch2pcb overwrites the
+netlist in pcb from whatever gnetlist produced" approach would not work.
+<p>
+An alternative is to keep the netlist as-is, and maintain a separate list of
+changes. The form proposed hereby is a table of "operation,pinID,net" or
+"operation,args...". Netlist operation is one of "del_conn", "add_conn" <!--, "del_net",
+"add_net"--> and "net_info". The table is called the <i>netlist patch</i>.
+<p>
+For example assume two components with pins A1, A2 and B1, B2, with connections
+n1=A1-B1 and n2=A2-B2. While routing the designer decides changing them to
+n1=A1-B2 and n2=A2-B1 would be easier and is acceptable by the design. The
+table of changes would contain this:
+<table border=1>
+	<tr><th>op          <th> pinID <th> net 
+	<tr><td>del_conn    <td> B1    <td> n1  
+	<tr><td>del_conn    <td> B2    <td> n2  
+	<tr><td>add_conn    <td> B2    <td> n1  
+	<tr><td>add_conn    <td> B1    <td> n2  
+</table>
+The first two lines would remove pins B1 and B2 from n1 and n2. The last
+two would put them back, swapped. New nets could be created or unused nets
+could be removed using the add_net and del_net commands that have empty pinID.
+The table is ordered, rows are strictly executed from top to bottom.
+<p>
+Pcb-rnd would store this table in memory. When some code calls the netlist
+code to find out the members of a net, or which net a given pin is connected to,
+after running the original netlist code, the result would be adjusted by the table.
+<p>
+The table would be normalized after operations. For example:
+<table border=1>
+	<tr><th>op          <th> pinID  <th> net 
+	<tr><td>del_conn    <td> B1     <td> n1  
+	<tr><td>add_conn    <td> B1     <td> n2  
+	<tr><td>add_conn    <td> B1     <td> n3  
+	<tr><td>del_conn    <td> B1     <td> n2  
+</table>
+would be reduced to
+<table border=1>
+	<tr><th>op          <th> pinID <th> net 
+	<tr><td>del_conn    <td> B1    <td> n1  
+	<tr><td>add_conn    <td> B1    <td> n3  
+</table>
+Simple linear crawls on the table seems sufficient: it is expected that
+pcb designers will make netlist modifications rarely and they will back
+annotate them ASAP. In extreme cases there may be 64 bit wide bus systems that
+need total reordering; even a 4 such reorders will introduce about 1024 items
+on the list which seems not too big for O(1) algorithms. See section TODO
+for better approaches.
+<p>
+Pcb-rnd would save the normalized table in the pcb file in a new section.
+Upon a netlist change in pcb (import/load netlist or load the pcb), pcb-rnd
+would check each row of the table: it is easy to decide whether that row
+has been implemented in the netlist or not. Obsolete rows of the table would
+be deleted.
+<p>
+A corner case is when B1 is removed from n1 and then added to n2 by the table,
+while a new forward annotation removes B1 from n1 and adds it to n3. In this
+case the first row of the table is deleted, as B1 is already removed from n1,
+but pcb-rnd has no chance to decide if netlist adding B1 to n3 should affect
+the table adding B1 to n2, so that rule is kept.
+<p>
+<b>net_info</b> is used to describe the <b>original</b> members of a net, in
+the state they were before any change on the netlist occured.
+
+<H3> Support in pcb-rnd: GUI </H3>
+A trivial idea is to extend the netlist window so that pins can be moved in
+between nets or deleted or assigned to nets. Changes should be marked. This
+is not the preferred way of editing the netlist, tho: not much more convenient
+than making changes in gschem and doing forward annotation.
+<p>
+There should be a separate dialog box or a separate region of the netlist box
+showing the <i>netlist patch</i> with edit capabilities.
+<p>
+Finally, the most important feature would be new actions resolving shorts.
+Using the above example (n1=A1-B1 and n2=A2-B2 changed to n1=A1-B2 and n2=A2-B1),
+I believe the user would:
+<table border=1>
+	<tr><th> action <th> screenshot <th> patch list after the actions
+	<tr><td><ul><li> look at initial rats</ul><td> <img src="res/20150830b_s0.png" width="50%"> <td> (empty)
+	<tr><td><ul><li> first connect A1 to B1</ul> <td> <img src="res/20150830b_s1.png"  width="50%"> <td> (empty)
+
+	<tr><td><ul>
+	<li> then realize it's very hard to connect A2 to B2 while the previous connection is there
+	<li> he would then revert the first connection
+	<li> and connect A1 to B2
+	<li> which would cause shorts
+	</ul><td> <img src="res/20150830b_s2.png"  width="50%">
+	<td>(empty)
+
+	<tr><td><ul>
+	<li> then he would use the "approve netlist change" hotkey/action on the network;
+	     this would add <i>netlist patch</i> commands for the A1-B2 connection,
+	     but would also keep the A1-B1 connection, which remains a rat; because
+	     of the new connection there'd be a rat between A1 and A2 or B1 and B2 too
+	     (all 4 pins connected together on the patched netlist at the moment!)
+	</ul><td> <img src="res/20150830b_s3.png" width="50%">
+	<td>
+<pre>
+net_info n1 A1 B1
+net_info n2 A2 B2
+del_conn B1 n1
+add_conn B1 n2
+</pre>
+
+	<tr><td><ul>
+	<li> the user would then use an action (maybe the same one?) on the rat line
+	     so that pcb-rnd would understand that rat is not needed anymore and
+	     would add a patch to remove the A1-B1 connection
+	<li> the same thing would need to happen to the A2-B2 rat
+	</ul><td> <img src="res/20150830b_s4.png" width="50%">
+<td>
+<pre>
+net_info n1 A1 B1
+net_info n2 A2 B2
+del_conn B1 n1
+add_conn B1 n2
+del_conn B2 n2
+</pre>
+
+
+	<tr><td><ul><li> the user then would connect A2 to B1, which again is a short</ul><td><img src="res/20150830b_s5.png" width="50%">
+	<tr><td><ul>
+	<li> the user would approve it as a new connection
+	<li> we have exactly 2 del_conn and 2 add_conn patches.
+	</ul><td><img src="res/20150830b_s6.png" width="50%">
+<td>
+<pre>
+net_info n1 A1 B1
+net_info n2 A2 B2
+del_conn B1 n1
+add_conn B1 n2
+del_conn B2 n2
+add_conn B2 n1
+</pre>
+
+</table>
+An experienced user may think a few steps in advance and
+chose to first remove the A1-B1 and A2-B2 rats and then create the A1-B2
+and A2-B1 connections and then approve the two new connections.
+<p>
+An alternative is drag&drop ratline endpoint onto snap points; it may
+be tricky to convert that to net/pin relations if a rat line is between two
+line segments, tho.
+<p>
+These changes would live immediately, leaving the board free of shorts and
+rats. There should be, however, some warning in the "congratulation" message
+that tells the user a back annotation is still required.
+
+<H3> Support in gschem </H3>
+Ideally there should be a very small change in gschem and an optional
+plugin script could do the rest. The plugin script would be in contant
+with foo.
+<p>
+There are multiple ways pins can be connected to a net in gschem. It's
+probably not a good idea to have too much automatism in the gschem's side,
+trying to actually removing connections and adding new ones using the patch
+(or whatever info <i>foo</i> converted the patch into).
+<p>
+However, gschem should support four things natively:
+<ul>
+	<li> it should have a concept of an unwanted pin-network connection; a connection
+	     becomes unwanted only when the back annotation says so
+	<li> it should be able to mark unwanted connections on the active schematic page
+	<li> it should be able to tell the user if there are unwanted connections on
+	     any of the pages open
+	<li> it should be able to refresh its idea of unwanted connections while
+	     schematic pages are open
+</ul>
+<p>
+Displaying unwanted connections happen at:
+<ul>
+	<li> a pin of a component is connected to a net using a "blue line" net: mark the pin-net connection point
+	<li> a pin is directly connected to another pin, no net line in between: mark the connection point
+	<li> a pin is connected to a net using a pin attribute: mark the pin
+	<li> TODO: are there more?
+</ul>
+<p>
+TODO: there are a lot to think over about special cases related to
+multipage schematics, hierarchies, slots, split symbols.
+
+<H3> What <i>foo</i> does exactly </H3>
+... is not clear yet. It depends on what sort of support gschem would provide.
+
+<H3> Amendment 1: other parameters (1st sep)</H3>
+I originally forgot to mention my other intentions in the above document:
+back annotate non-netlist properites. It probably happened because netlist
+related stuff are the hardest to solve.
+<p>
+There are other parameters that sometimes change during routing. A common case
+for my 1 or 2 layer boards is when I originally intend to use 0603 parts but
+during routing I figure I need to pass a trace between the pads. I need to
+change the part to 0805 or 1206 (for two traces). I'd like to be able to
+do this <b>in-place</b> in pcb with an action that replaces the footprint
+but keeps the origin in place. This obviously still requires some manual
+fiddling afterwards, but would remove the long, tedious chain I have now:
+<ul>
+	<li>1. remember or note down which parts to change footprints for
+	<li>2. go back to gschem and change them
+	<li>3. get the changes in pcb (I use gsch2pcb and Makefiles, one step; the import menu is one step too, just another one)
+	<li>4. disperse the new elements
+	<li>5. find where they used to be
+	<li>6. and then do the fiddling to fit them in
+</ul>
+<p>
+The new process would be:
+<ul>
+	<li> 1. get the footprint replaced, in-place; this would already approve the
+	        change and there'd be a command for it in the patch table
+	<li> 2. do the fiddling to fit the new part in
+	<li> 3. do a back annotation
+	<li> (4. optionally, if we go for non-automatic change of attributes in gschem,
+	        change them manually in gschem, cycling through the affected
+	        items using some UI feature)
+</ul>
+<p>
+The same thing could work for values, which is the other attribute PCB also
+sees. The same mechanism could work from other programs as well, e.g. tuning
+the values of some parts in a simulator and then back annotating the changes
+to the schematics. The patch table format <i>foo</i> handles would be in the
+simplest plain text form.
+
+<a id="a2">
+<H3> Amendment 2: examples from gschem's point of view (3rd Sep)</H3>
+<h4> netlist change </h4>
+<ul>
+	<li> The user creates the schematics and imports it in pcb; the original
+	     netlist contains a line "netname1 U1-1 CONN1-2"
+	<li> The user, as part of some pin swapping, decides that U1-1 should be
+	     connected to net "netname2" instead of "netname1". Changes are
+	     done in pcb-rnd as described above.
+	<li> The netlist patch generated for this single change by pcb-rnd would be:
+	<pre>
+del_conn netname1 U1-1
+add_conn netname2 U1-1
+	</pre>
+	<li> the user may need to load the netlist patch in ghscem
+	<li> In gschem there would be an indication that highlights any U1-1 pin or
+	U1 symbol that makes a connection to netname 1, graphically or using
+	attributes. When asked, gschem UI would also tell the user that U1-1 is
+	now connected to netname1 but should be connected to netname2 instead.
+	<li> The user would find this indication and would resolve the situation
+	by whatever changes he finds appropriate
+	<li> gschem would rerun the patch commands and would figure that the del_conn
+	fails to run because U1-1 is no longer connected to netname1 and the add_conn
+	fails too because U1-1 is connected to netname2. This leaves U1-1 without
+	any known issue, so the indication on U1-1 would be gone.
+</ul>
+
+<h4> attribute change: footprint change </h4>
+<ul>
+	<li> The user creates the schematics and imports it in pcb; originally
+	     U1 has an attribute footprint=DIP(8).
+	<li> during the layout the user figures using the footpritn SO(8) is
+	     more appropriate. He does the change in pcb-rnd.
+	<li> pcb-rnd emits the following netlist patch for this:
+	<pre>
+change_attrib U1 footprint=DIP(8) footprint=SO(8)
+	</pre>
+	<p>
+	(or it could be a del_attrib and add_attrib pair, like with connections)
+	<li> the user may need to load the netlist patch in ghscem
+	<li> In gschem there would be an indication that highlights any U1 instances
+	     that has footprint=DIP(8)
+	<li> The user would find this indication and would resolve the situation
+	     by whatever changes he finds appropriate (e.g. change the attribute)
+	<li> gschem would rerun the patch commands and would figure the change is
+	     no longer requred and would remove the indication
+</ul>
+
diff --git a/doc-rnd/devlog/20150901a_back_ann.html b/doc-rnd/devlog/20150901a_back_ann.html
new file mode 100644
index 0000000..2ddb113
--- /dev/null
+++ b/doc-rnd/devlog/20150901a_back_ann.html
@@ -0,0 +1,87 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> back annotation </H2>
+
+<a id="1">
+<H3> Conclusions of the first thread </H3>
+DJ has another <a href="http://www.delorie.com/pcb/pin-mapping.html"> model </a>
+where back annotation is only a subset of a bigger mechanism.
+<p>
+Many other users commented the thread, but no one else presented a
+plan that formed a <b>complete</b> system.
+<p>
+While there were some useful feedback about some details, no one explicitly
+said he'd be contributing the gschem part (... for any of the ideas floating
+around).
+<p>
+The thread is swamped in a chaotic set of random ideas and opinions - the
+same way as previous related threads usually did.
+
+<a id="2">
+<H3> Second thread </H3>
+In the second thread I will focus on actual contribution. For this,
+I'm narrowing down what exactly needs to be contributed:
+
+
+<ul>
+<li> 1. minor UI changes, most probably in the C part of the gschem code. 
+somehow ending up in the official repo; I'd prefer to avoid maintaining a 
+fork of gschem (no, having the fork in git doesn't help).
+
+<li> 2. a scheme script that can be plugged into gschem and do real simple 
+things like toggling flags for point 1, counting how many flags are 
+toggled, warn the user about the counter is being non-zero; this script 
+doesn't need to get into the official repo
+
+<li> 3. depending on whether we (me and my actual contributor who contributes 
+code) go for push or pull, we need: a new action or menu or whatever that 
+can trigger a pull or some means that can collect a change list pushed and 
+then indicate that something's happened. It's not really a third piece of 
+code, just a third piece of concept that is spread across 1 and 2.
+</ul>
+<p>
+First, I seek a contributor for exactly these 3 things. Alternatively if 
+there's someone who is really willing to contribute actual code and spend 
+time on this, I'm open to change parts of my plan if he has better ideas 
+as long as the new approach still solves the actual problems I have.
+
+<a id="3">
+<H3> Preparing for the third phase (3rd sep)</H3>
+Options are being eliminated slowly. I couldn't find out who are currently the
+maintainers of gschem, so I couldn't ask their opinion about my back annotation
+plan directly. Last stable release is about 2 years old, last unstable is more
+than a year old.
+<p>
+The main options currently are:
+<ul>
+	<li> 1. Evan offered contribution on the gschem side; Markus offered him write
+	     access to the official git repo. We could have a branch there. We'd
+	     aim for a merge, so minimal changes and a lot of scheme hacking (...
+	     that still none of us want to do, afaik).
+	     Without positive feedback from maintainers, I believe this branch
+	     has a very low chance to get merged in mainline. <i>If it doesn't get
+	     merged, all the extra effort on scheme, git, and trying to
+	     do things in the gschem-way are just energy wasted.</i>
+
+	<li> 2. I start an svn repo and implement the stuff the better way (no
+	     scheme, bigger change, no worries about whether it gets merged). Keep
+	     changes on-topic and small, so later on if someone wants to merge,
+	     there's a chance to get it into a branch in the git repo first then
+	     do the merge. Has even lower chance to get merged, but certainly
+	     speeds up development and is much easier to work on, distribute and
+	     use than a bitrotting git branch. <i>If it doesn't get merged, 
+	     only a small amount of efforts wasted on trying to keep changes
+	     merge-friendly.</i>
+
+	<li> 3. I start an svn repo and implement the stuff the best I can - without
+	     considering any merging aspects. This is the option that'd grant
+	     the most development speed and efficiency. <i>It doesn't get merged,
+	     but no energy is wasted at all and the resulting code is better.</i>
+</ul>
+There are some other options, but those are just variants of the above three.
+Currently I think option 1 is unlikely to work, for I don't touch git,
+and noone wants to touch scheme. Both 2 or 3 could work, but the total lack
+of gschem maintainer feedback doesn't make option 2 look too good.
+
diff --git a/doc-rnd/devlog/20151028_glib.html b/doc-rnd/devlog/20151028_glib.html
new file mode 100644
index 0000000..76cf8b8
--- /dev/null
+++ b/doc-rnd/devlog/20151028_glib.html
@@ -0,0 +1,65 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> why glib is a bad idea </H2>
+
+Levente tried to compile pcb-rnd on bsd and used a different c compiler 
+than gcc. For this in the first step I fixed the build system so that it 
+doesn't have gcc --std=gnu99 but gcc --std=c99.
+<p>
+And then everything broke. A minilib I use for hashing,
+<a href="http://repo.hu/projects/genht">genht</a>, failed to 
+link against hid/common/action.c. I first thought it was a bug in genht: 
+genht was compiled without --std while the rest of the code compiled with 
+--std=gnu99 or --std=c99. Genht heavily depends on static inline 
+functions for performance, maybe that's why.
+<p>
+So I tried to reproduce the situation in a hello-world like program and 
+tried all combinaton of --std, -DNDEBUG, -rdynamic and all build flags 
+used in pcb-rnd for the genht lib and the test program, but all combination 
+worked. It looked like it broke only in pcb-rnd.
+<p>
+I gave up on the minimal test case and went back to pcb-rnd. I realized if 
+the build is the same, the only way it may break is that some header 
+included before genht's headers change some global state. I started to 
+shuffle the #includes. Long story short, it turned out if <glib.h> is 
+included before genht's headers, it breaks.
+<p>
+Some more tracing showed it was because glib over-#defines the keyword 
+<i>inline</i> in a public header that gets included from glib.h. It's all wrapped 
+in a complicated tree of #ifdefs, so it behaves differently depending on 
+the --std setting.
+<p>
+The morale of the story is... Well, I have multiple conclusions.
+<ul>
+<li> glib is not a lib that tries to solve something, it is a prorgamming 
+environment that tries to supersede C.
+
+<li> As such, it feels free to mess with the environment, redefine C 
+keywords as it sees fit, because once you use glib, why would you use 
+anything else? And once you use glib, you are programming in glib, not in 
+"plain C".
+
+<li> Grepping through the pcb-rnd code, I see that pcb-rnd does not try to 
+use glib as a programming environment, but needs only 2 and a half 
+features: hash, list and rarely dynamic strings. Any other glib call is 
+just a must that had to be done that way to get hashes and lists working.
+
+<li> genht is 510 sloc. I have a generic list implementation (which, by the 
+way, is more efficient than glib's) It costs 256 sloc. So having 
+type-independent hashes and lists in C89 costs less than 800 lines of code 
+if you pick the right libs. Glib's <b>include/ alone is over 24000 sloc!</b>
+And in return glib breaks inline...
+</ul>
+
+<p>
+In a nuthsell this is why I don't believe in glib-like solve-all megalibs. I 
+don't say size alone determines this, but size is a good indication of 
+potential problems.
+<p>
+If I need hash and lists and the offer is longer than 5k sloc, I know it 
+will bring a lot of unneeded bloat that likely to break things.
+
+</body>
+</html>
\ No newline at end of file
diff --git a/doc-rnd/devlog/20160101_cschem.html b/doc-rnd/devlog/20160101_cschem.html
new file mode 100644
index 0000000..b7e3970
--- /dev/null
+++ b/doc-rnd/devlog/20160101_cschem.html
@@ -0,0 +1,68 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> cschem </H2>
+
+Cschem is a project I plan to start within the next few years. It's goals
+and some design concepts are similar to gschem's and geda's, while it
+also breaks some traditions to fix shortcomings in the design of geda. It's
+named after gschem, not after geda, to emphasize that the editor needs to
+be connected more to the rest of the system (see details later).
+<p>
+Some concepts cschem will try to follow (marking with * where there's major
+difference to geda):
+<ul>
+	<li> 1. design
+		<ul>
+			<li> 1.1. modularity, aka. toolkit approach
+			<li> 1.2. flexibility (trough attributes)
+			<li> 1.3. one schematics file is one sheet
+			<li> 1.4. multi page projects (hierarchic, flat)
+			<li> 1.5. data is in structured text files (no builtin sql support in core)
+			<li> 1.6. * the concept of a "project"; it's optional, tools can work on a set of schematics files _or_ on a complete project
+			<li> 1.7. * nets and components are uniquely identifiable using the same identifiers by all projects
+			<li> 1.8. * no excess "smartness" in the GUI editor: no slotting, no pin numbering, no auto renumbering, etc; these all should be done in the netlist layer and results fed back to the editor
+			<li> 1.9. * direct, bidirectional communication between the editor (GUI) and the netlist layer <b>without</b> any integration of the two, through simple and clean API, keeping both parts replaceable; attributes cschem got back from the netlister are "volatile": not saved, do not override attributes provided by the user
+			<li> 1.10. * less format-specific tricks built into the GUI code, more generic approaches (e.g. a search is a search, not a search-for-text-attribute and results on the result lists are any object of the design)
+			<li> 1.11. * slotting, pin mapping, device mapping are in backends
+			<li> 1.12. * back annotation should not be any harder than forward annotation
+			<li> 1.13. * since a lot of info is invented in backends, not in the GUI (e.g. pin numbers, when slotting), the GUI needs to be able to switch between "views": what (combination of) backend(s) to get these info from; Note: this would also provide an interactive DRC on the GUI with the DRC still implemented in the netlister!
+			<li> 1.14. * the scriptable plugin system is based on GPMI from the start to guarantee the tool is not tied to any one specific scripting engine or scripting language
+		</ul>
+	<li> 2. implementation
+		<ul>
+			<li> 2.1. a core library that does common things like figuring what objects are connected with net lines
+			<li> 2.2. the simple GUI editor should provide the frame; exotic functions should come from user plugins
+			<li> 2.3. a simple netlister that provides only generic (* absolutely no backend specific) queries; actual backends are implemented as plugins
+			<li> 2.4. * the core library and the netlister and the GUI core are all implemented in plain C:
+			<ul>
+				<li> 2.4.1. * no dependency on any specific scripting engine or scripting language; no core functionality implemented in anything else but C
+				<li> 2.4.2. * no dependency on big "solves-everything" libraries (e.g. no glib or cairo dependency)
+				<li> 2.4.3. * the actual GUI is behind the plugin system (like PCB's HIDs)
+				<li> 2.4.4. * the first gui, in accordance with the no big libraries, will not be gtk or qt but sdl2 based; this would guarantee to have a front end that doesn't need to be rewritten every 5 years just for the sake of the rewrite
+				<li> 2.4.5. * scconfig instead of autotools: smaller, easier to maintain, works better outside of the gnu-win32 world
+			</ul>
+	</ul>
+	<li> 3. project management
+		<ul>
+			<li> 3.1. automatic tests wherever possible, as early as possible
+			<li> 3.2. * VCS: simple, centralized svn with straight linear developement, actively trying to avoid branching
+			<li> 3.3. * near-zero-administration releases with svn commits 
+			<li> 3.4. * users should be able to submit bug reports anonymously, without having to register, without having to run javascript, java applet, flash, etc.
+		</ul>
+	</ul>
+</ul>
+
+<p>
+There are a lot of open questions:
+<ul>
+	<li> q1. how buses should work
+	<li> q2. how slotting should work (but at least we can have alternatives here)
+	<li> q3. how hierarchical design should work
+	<li> q4. * how the GUI should display large amount of attributes without making the whole page an unreadable mess
+	<li> q5. flat vs. non-flat netlists - if turns out it's not just an implementation detail in a netlister backend
+	<li> q6. * whether project file format is a custom format or just a top sch page referencing other sch pages - pcb-rnd is leading here, there is an optional project file format already defined, so this is more or less decided
+	<li> q7. attribute conventions
+</ul>
+
diff --git a/doc-rnd/devlog/20160126.html b/doc-rnd/devlog/20160126.html
new file mode 100644
index 0000000..e7e9b48
--- /dev/null
+++ b/doc-rnd/devlog/20160126.html
@@ -0,0 +1,59 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> Burried/blind via poll </H2>
+
+<H3> Results </h3>
+
+I received 10 full answers to the poll - thanks everyone who
+answered. This is a small sample, but this is the best I could get
+(I can't reach more pcb users).
+<p>
+<a href="20162601">Raw results</a> are available in html, tsv and
+csv format.
+
+<H3> My interpretation </h3>
+
+(<i>User </i> obviously means "those users who answered the poll")
+
+<ul>
+	<li> 1. only half of the users would consider even trying pcb-rnd for blind/burried vias
+	<li> 2. there was only one user who'd consider switching to pcb-rnd for blind-burried vias - this feature doesn't seem to be valuable enough to attract users (details below)
+	<li> 3. about half of the users need blind/burried via; when they do, they don't use pcb
+	<li> 4. 9 out of 10 users runs PCB on Linux
+</ul>
+
+<H3> My conclusions </h3>
+Because of 1. and 2., pcb-rnd doesn't seem to need blind/burried vias. The
+purpose of question 7 was to find out whether users value this feature high
+enough to actually consider investing time/effort in return, compared to
+question 6. This pair of questions was designed to avoid noise that comes
+from the fact that we, on the list tend to express our opinions in vast
+crowds while only a few invest more time than talking and do actual work.
+According to the result of line 7, my conclusion is that it's absolutely
+not blind/burried vias that potential pcb-rnd users need. Thus
+<b>my decision is that I won't spend time on this feature in the near future</b>.
+<p>
+The situation for pcb might be different, tho. I worded the first 4 questions
+to find out how strong the need is among the users of mainline pcb and whether
+they are devoted to pcb or choose the tool according to the design requirements.
+There seems to be a correlation between having to use blind/burried vias
+and using other layout tools. Or in other words: those who are happy with pcb
+usually don't need or want blind/burried vias anyway and those who do have
+already switched to another tool.
+<p>
+This suggests mainline pcb could benefit from burried/blind vias. However,
+the demand is much lower than "every new design needs this" or "no new
+user would consider pcb because of this missing feature".
+<p>
+Linux: it seems pcb power users mostly use Linux. I am not sure if it's
+because PCB works well on Linux and is a bit harder to compile on
+anything else - or the other way around (Linux users are more attracted
+to PCB).
+
+
+
+
+
+
diff --git a/doc-rnd/devlog/20160313_unglib.html b/doc-rnd/devlog/20160313_unglib.html
new file mode 100644
index 0000000..f4d9fc1
--- /dev/null
+++ b/doc-rnd/devlog/20160313_unglib.html
@@ -0,0 +1,49 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> unglib: glib removal </H2>
+
+<H3> glib problems </h3>
+
+As mentioned in a previous post, <a href="20151028_glib.html"> glib is
+sometimes dangerous</a>. Even if it was not, it's still huge and contradicts
+the UNIX philosophy: <i>Do One Thing and Do It Well</i>. Glib tries to do a lot
+of things. I do not doubt it is trying to do each of these things well, but as
+a combination it's pretty much impossible to <i>Keep It Simple, Stupid</i>.
+While glib is modular in design, from the viewpoint of an application it is
+not modular: it's highly unlikely that the application can replace a part
+of glib while keeping the rest.
+<p>
+The source of these problems is that glib is a "megalib" that tries to solve
+a host of problems as a package.
+
+<H3> The solution </h3>
+
+The solution is to replace the megalib with
+a set of independent minilibs. Each minilib:
+<ul>
+	<li> tries to do one thing - e.g. linked lists, without coupling it with a custom memory allocator
+	<li> is simple - the API is so small that it's easy to learn it in minutes
+	<li> is small - so that if anything breaks it's very easy to find and fix the bug (when did you last debug internals of glib?)
+	<li> is replaceable - since they are independent, if one doesn't work up to expectations, it's real easy to replace it without affecting any other part; e.g. replacing linked lists without replacing hash tables
+</ul>
+<p>
+The minilibs are imported as svn externals in trunk/src_3rd. They are small
+enough so that they can be distributed together with pcb sources.
+
+<H3> Current state </h3>
+
+The "unglib" patch is mostly done. All references of glib are removed
+from the core and the lesstif hid. There are a three components that
+still depend on glib, but they each can be disabled:
+<ul>
+	<li> the GTK HID: because gtk is already coupled with glib, it doesn't make much sense to remove glib from the gtk HID code
+	<li> the toporouter plugin: the code is huge and will be potentially deprecated anyway (lack of developer resources/user interest)
+	<li> the puller: glib should be removed from the puller long term but the code is big and there's no much user need for it in pcb-rnd
+</ul>
+<p>
+This means pcb-rnd can be compiled with lesstif (or a compatible motif)
+on a UNIX box without depending on glib. Together with the earlier effort
+that removed autotools, it means a UNIX box without any "GNU infection"
+should be able to compile and run pcb-rnd.
diff --git a/doc-rnd/devlog/20160314_valgrind_flex.html b/doc-rnd/devlog/20160314_valgrind_flex.html
new file mode 100644
index 0000000..52112d8
--- /dev/null
+++ b/doc-rnd/devlog/20160314_valgrind_flex.html
@@ -0,0 +1,79 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> "valgrinding" flex/bison parsers </H2>
+
+In a flex/bison parser it's quiet common that strings are allocated
+in flex, passed on to bison and then either free'd there or saved in
+the output data. Since both free and save happens a lot, it's not an
+easy mechanical review of the .y file to find the reason for leaks. Especially
+if the code has to store some strings in temporary storage until a later
+stage of the parsing.
+<p>
+A typical valgrind log for such a leak looks like this:
+<pre>
+==20520== 20 bytes in 1 blocks are still reachable in loss record 3 of 6
+==20520==    at 0x402B0D5: calloc (vg_replace_malloc.c:623)
+==20520==    by 0x80E6EF0: yylex (parse_l.l:177)
+==20520==    by 0x80E0D6C: yyparse (parse_y.tab.c:1696)
+==20520==    by 0x80E85ED: Parse (parse_l.l:292)
+==20520==    by 0x80E876B: ParsePCB (parse_l.l:347)
+==20520==    by 0x8078591: real_load_pcb (file.c:390)
+==20520==    by 0x80787E9: LoadPCB (file.c:459)
+==20520==    by 0x8097719: main (main.c:1781)
+</pre>
+
+The code at <i>parse_l.l:177</i> is just a calloc() and some string
+operation: this is where the string is created. The STRING token is
+referenced about 58 times in the grammar. After reading through the
+whole file 4..5 times, I still didn't see any obvious place for the leak.
+<p>
+The leak was also a rare one: happened for one string per file. This suggested
+it was in the header - unless there's an one-instance object somewhere in the
+.pcb or it's a cache where the same pointer is free()'d and overwritten for
+multiple occurrences and simply no one free()'s the last.
+<p>
+Assuming it's a header, a cheap ways to find which header field leaked:
+<ul>
+	<li> remove the header lines one by one and see if the leak is still there - won't work if a missing header changes how the whole code runs
+	<li> change the size of each header, one by one, and see which action changes leak size - a lot of iterations, and valgrinding is slow
+</ul>
+<p>
+At this point I figured that I'd depend on the reported <i>size</i> of the leak
+with my tests. I didn't want to do multiple runs and didn't want to risk 
+the whole parser to run differently so I didn't want to modify the input. Instead
+I figured there's a simple, yet generic way to track these sort of leaks.
+<p>
+I estimated no string in the file is longer than 1000 characters. Right above
+the calloc() in the lexer I introduced a new static integer variable starting
+at 1000, increased before each allocation. This counter is sort of an ID of
+each allocation. Then I modified the calloc() to ignore the originally calculated
+string length and use this ID for allocation size. I also printed the ID-string
+pairs. The original code looked like this (simplified):
+<pre>
+	/* ... predict string size ... */
+	yylval.str = calloc(predicted_size, 1);
+	/* ... build the string here ... */
+	return STRING;
+</pre>
+
+The resulting code looked like this (simplified):
+<pre>
+	/* ... predict string size ... */
+	static int alloc_id = 1000;
+	alloc_id++;
+	yylval.str = calloc(alloc_id, 1);
+	/* ... build the string here ... */
+	fprintf(stderr, "STRING: %d '%s'\n", alloc_id, yylval.str);
+	return STRING;
+</pre>
+I saved the list printed on stderr and checked valgrind's log to find
+the two strings in question were ID 1002 and ID 1007, both looked
+something like this:
+<pre>
+1,3,4,c:2,5,6,s:7:8
+</pre>
+The only thing that looks like this is the layer group description ("Groups()").
+From this point it was trivial to find the bug in the grammar.
+
diff --git a/doc-rnd/devlog/20160409.html b/doc-rnd/devlog/20160409.html
new file mode 100644
index 0000000..2f0a73f
--- /dev/null
+++ b/doc-rnd/devlog/20160409.html
@@ -0,0 +1,48 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> Keyboard via poll </H2>
+
+<H3> Results </h3>
+
+I received 9 partial answers to the poll - thanks everyone who
+answered. This is a small sample, but this is the best I could get
+(I can't reach more pcb users).
+<p>
+<a href="20160409/poll.html">Raw results</a> are available in html, tsv and
+csv format.
+
+<H3> My interpretation </h3>
+
+(<i>User </i> obviously means "those users who answered the poll")
+
+<ul>
+	<li> 1. very few users use the lesstif hid, still user count is more than 1
+	<li> 2. users are open to try alternative menu/hotkey layouts...
+	<li> 3. ... and only a few users change the default layout
+	<li> 4. ... because they are happy with the defaults and/or don't want to maintain the diffs on upgrades
+	<li> 5. there is a strong demand for multikey in the GTK HID
+	<li> 7. use of vendor (drill) mapping is rare
+</ul>
+
+<H3> My conclusions </h3>
+pcb-rnd: the GTK HID needs multi-key support.
+<p>
+Community: although users express their needs, they do not contribute
+even in smallish, non-programming tasks like designing the hotkey layout.
+This is probably in-line with the answers for 4: they don't really customize
+things, they want to use the software as is.
+<p>
+Conclusion: it is worth shipping multiple menu/keyboard layouts as long as
+they are all maintained in the central repo (questions 2, 3 and 4). It is
+pointless to invest in tools that make customization easier or to prepare
+for collecting user-customized menu files, as users won't invest any time in
+this.
+
+
+
+
+
+
+
diff --git a/doc-rnd/devlog/20160409/header b/doc-rnd/devlog/20160409/header
new file mode 100644
index 0000000..afc3c0e
--- /dev/null
+++ b/doc-rnd/devlog/20160409/header
@@ -0,0 +1,7 @@
+1. Do you use the lesstif HID?
+2. If there were different menu resources files distributed with PCB, would you try them?
+3. Do you customize your menu resource file?
+4. If you do not costumize your menu resource file, it's because
+5. Do you miss multi-key sequences from the GTK hid?
+6. If the GTK hid supported multi-key sequences, would that change any of your previous answers?
+7. Vendor (drill) mapping also uses a resource file.
diff --git a/doc-rnd/devlog/20160409/header.csv b/doc-rnd/devlog/20160409/header.csv
new file mode 100644
index 0000000..b4ddf77
--- /dev/null
+++ b/doc-rnd/devlog/20160409/header.csv
@@ -0,0 +1 @@
+1. Do you use the lesstif HID?,2. If there were different menu resources files distributed with PCB, would you try them?,3. Do you customize your menu resource file?,4. If you do not costumize your menu resource file, it's because,5. Do you miss multi-key sequences from the GTK hid?,6. If the GTK hid supported multi-key sequences, would that change any of your previous answers?,7. Vendor (drill) mapping also uses a resource file.,
\ No newline at end of file
diff --git a/doc-rnd/devlog/20160409/legend.txt b/doc-rnd/devlog/20160409/legend.txt
new file mode 100644
index 0000000..4812fb3
--- /dev/null
+++ b/doc-rnd/devlog/20160409/legend.txt
@@ -0,0 +1,60 @@
+1. Do you use the lesstif HID? (select one)
+a. yes, exclusively
+b. yes, often
+c. sometimes, rarely
+d. never
+
+
+2. If there were different menu resources files distributed with PCB, 
+would you try them? (select one)
+a. yes, I'd give each variant a try before deciding which one to use
+b. no, I'm fine with the default
+c. I don't know what a menu resource file is
+
+
+3. Do you customize your menu resource file? (select one)
+a. yes, always (e.g. I have an own variant I use with all installation of 
+PCB)
+b. yes, sometimes, rarely (e.g. I once had to do something repeatedly and 
+added a key binding for that)
+c. never, I know where I'd perform the changes if I ever needed 
+them but defalts are good enough for now
+d. never, I don't know what a menu resource file is
+
+
+4. If you do not costumize your menu resource file, it's because (select 
+zero or more):
+a. I don't need to
+b. the file is too long
+c. too many keys are taken, it's hard to find a free one
+d. I don't like the format of the file
+e. I don't like the idea of editing text config files, I want a GUI for 
+this
+f. I don't want to diverge from the default settings (e.g. because of 
+potetial hassle at a later upgrade)
+
+
+5. Do you miss multi-key sequences from the GTK hid? (select one)
+a. yes, I'd prefer to use them over modifiers (ctrl, alt, shift)
+b. yes, I'd use them together with the modifiers
+c. maybe I'd use some
+d. no, I prefer modifiers
+e. I hate the idea so much that I'd even disable it compile time if that 
+was possible
+f. N/A, don't know
+
+
+6. If the GTK hid supported multi-key sequences, would that change any of 
+your previous answers? (fill in zero or more with a letter)
+a. my new choice for 2. would be:
+b. my new choice for 3. would be:
+
+7. slightly off-topic: vendor (drill) mapping also uses a resource file. 
+Do you use this feature? (select one)
+a. yes, often, many of my boards rely on vendor mapping and I maintain 
+my own resource files per vendor
+b. yes, sometimes, rarely (e.g. I needed it once...)
+c. no, I know how to use it but never needed it
+d. no, I know the feature exists and I know where to look it up but I 
+don't really know what exactly it can do or why I should bother
+e. no, I never heard about this feature
diff --git a/doc-rnd/devlog/20160409/pie_col_1.png b/doc-rnd/devlog/20160409/pie_col_1.png
new file mode 100644
index 0000000..28790bb
Binary files /dev/null and b/doc-rnd/devlog/20160409/pie_col_1.png differ
diff --git a/doc-rnd/devlog/20160409/pie_col_2.png b/doc-rnd/devlog/20160409/pie_col_2.png
new file mode 100644
index 0000000..c4747a1
Binary files /dev/null and b/doc-rnd/devlog/20160409/pie_col_2.png differ
diff --git a/doc-rnd/devlog/20160409/pie_col_3.png b/doc-rnd/devlog/20160409/pie_col_3.png
new file mode 100644
index 0000000..027453f
Binary files /dev/null and b/doc-rnd/devlog/20160409/pie_col_3.png differ
diff --git a/doc-rnd/devlog/20160409/pie_col_4.png b/doc-rnd/devlog/20160409/pie_col_4.png
new file mode 100644
index 0000000..002e672
Binary files /dev/null and b/doc-rnd/devlog/20160409/pie_col_4.png differ
diff --git a/doc-rnd/devlog/20160409/pie_col_5.png b/doc-rnd/devlog/20160409/pie_col_5.png
new file mode 100644
index 0000000..0173809
Binary files /dev/null and b/doc-rnd/devlog/20160409/pie_col_5.png differ
diff --git a/doc-rnd/devlog/20160409/pie_col_7.png b/doc-rnd/devlog/20160409/pie_col_7.png
new file mode 100644
index 0000000..71423e7
Binary files /dev/null and b/doc-rnd/devlog/20160409/pie_col_7.png differ
diff --git a/doc-rnd/devlog/20160409/poll.csv b/doc-rnd/devlog/20160409/poll.csv
new file mode 100644
index 0000000..695492f
--- /dev/null
+++ b/doc-rnd/devlog/20160409/poll.csv
@@ -0,0 +1,9 @@
+a,a,a,-,-,-,c
+d,a,c,f,a,-,d
+b,a,b,-,c,-,c
+d,c,d,a+f,c,-,e
+d,b,c,a+f,b,-,b
+d,a,a,a,d,-,c
+-,a,c,-,a,-,-
+c,a,b,f,a,-,c
+c,a,c,-,a,-,-
diff --git a/doc-rnd/devlog/20160409/poll.html b/doc-rnd/devlog/20160409/poll.html
new file mode 100644
index 0000000..351a1ba
--- /dev/null
+++ b/doc-rnd/devlog/20160409/poll.html
@@ -0,0 +1,98 @@
+<html><body><table border=1>
+<tr>
+<th>1. Do you use the lesstif HID?
+<th>2. If there were different menu resources files distributed with PCB, would you try them?
+<th>3. Do you customize your menu resource file?
+<th>4. If you do not costumize your menu resource file, it's because
+<th>5. Do you miss multi-key sequences from the GTK hid?
+<th>6. If the GTK hid supported multi-key sequences, would that change any of your previous answers?
+<th>7. Vendor (drill) mapping also uses a resource file.
+<tr><td>a<td>a<td>a<td>-<td>-<td>-<td>c
+<tr><td>d<td>a<td>c<td>f<td>a<td>-<td>d
+<tr><td>b<td>a<td>b<td>-<td>c<td>-<td>c
+<tr><td>d<td>c<td>d<td>a+f<td>c<td>-<td>e
+<tr><td>d<td>b<td>c<td>a+f<td>b<td>-<td>b
+<tr><td>d<td>a<td>a<td>a<td>d<td>-<td>c
+<tr><td>-<td>a<td>c<td>-<td>a<td>-<td>-
+<tr><td>c<td>a<td>b<td>f<td>a<td>-<td>c
+<tr><td>c<td>a<td>c<td>-<td>a<td>-<td>-
+<tr>
+<td><img src="pie_col_1.png">
+<td><img src="pie_col_2.png">
+<td><img src="pie_col_3.png">
+<td><img src="pie_col_4.png">
+<td><img src="pie_col_5.png">
+<td> 
+<td><img src="pie_col_7.png">
+</table>
+<p>
+<h3> Legend </h3>
+<pre>
+1. Do you use the lesstif HID? (select one)
+a. yes, exclusively
+b. yes, often
+c. sometimes, rarely
+d. never
+
+
+2. If there were different menu resources files distributed with PCB, 
+would you try them? (select one)
+a. yes, I'd give each variant a try before deciding which one to use
+b. no, I'm fine with the default
+c. I don't know what a menu resource file is
+
+
+3. Do you customize your menu resource file? (select one)
+a. yes, always (e.g. I have an own variant I use with all installation of 
+PCB)
+b. yes, sometimes, rarely (e.g. I once had to do something repeatedly and 
+added a key binding for that)
+c. never, I know where I'd perform the changes if I ever needed 
+them but defalts are good enough for now
+d. never, I don't know what a menu resource file is
+
+
+4. If you do not costumize your menu resource file, it's because (select 
+zero or more):
+a. I don't need to
+b. the file is too long
+c. too many keys are taken, it's hard to find a free one
+d. I don't like the format of the file
+e. I don't like the idea of editing text config files, I want a GUI for 
+this
+f. I don't want to diverge from the default settings (e.g. because of 
+potetial hassle at a later upgrade)
+
+
+5. Do you miss multi-key sequences from the GTK hid? (select one)
+a. yes, I'd prefer to use them over modifiers (ctrl, alt, shift)
+b. yes, I'd use them together with the modifiers
+c. maybe I'd use some
+d. no, I prefer modifiers
+e. I hate the idea so much that I'd even disable it compile time if that 
+was possible
+f. N/A, don't know
+
+
+6. If the GTK hid supported multi-key sequences, would that change any of 
+your previous answers? (fill in zero or more with a letter)
+a. my new choice for 2. would be:
+b. my new choice for 3. would be:
+
+7. slightly off-topic: vendor (drill) mapping also uses a resource file. 
+Do you use this feature? (select one)
+a. yes, often, many of my boards rely on vendor mapping and I maintain 
+my own resource files per vendor
+b. yes, sometimes, rarely (e.g. I needed it once...)
+c. no, I know how to use it but never needed it
+d. no, I know the feature exists and I know where to look it up but I 
+don't really know what exactly it can do or why I should bother
+e. no, I never heard about this feature
+</pre>
+<h3> Downloads </h3>
+<ul>
+	<li><a href="poll.csv"> csv data </a> | <a href="header.csv"> csv header </a>
+	<li><a href="poll.tsv"> tsv data </a> (raw, no header)
+	<li><a href="legend.txt"> legend </a>
+<ul>
+</body></html>
diff --git a/doc-rnd/devlog/20160409/poll.tsv b/doc-rnd/devlog/20160409/poll.tsv
new file mode 100644
index 0000000..f01eeec
--- /dev/null
+++ b/doc-rnd/devlog/20160409/poll.tsv
@@ -0,0 +1,9 @@
+a	a	a	-	-	-	c
+d	a	c	f	a	-	d
+b	a	b	-	c	-	c
+d	c	d	a+f	c	-	e
+d	b	c	a+f	b	-	b
+d	a	a	a	d	-	c
+-	a	c	-	a	-	-
+c	a	b	f	a	-	c
+c	a	c	-	a	-	-
diff --git a/doc-rnd/devlog/20160716_sprint.html b/doc-rnd/devlog/20160716_sprint.html
new file mode 100644
index 0000000..d422fec
--- /dev/null
+++ b/doc-rnd/devlog/20160716_sprint.html
@@ -0,0 +1,81 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> Testing guide for the test sprint </H2>
+
+Please start with procedure I and proceed to procedure II then to procedure III.
+You can postpone or stop any time, even partial results are useful.
+<p>
+The most important procedures are I. and II., which together are estimated
+to take something between 40 and 90 minutes.
+<p>
+No installation, no root privileges required. No interference expected with
+your pcb mainline installation. What you'll need is about the same you would
+need for compiling mainline or a random application:
+<ul>
+	<li> an svn client (e.g. apt-get install subversion)
+	<li> a C compiler (e.g. apt-get install gcc - anything that supports C99 should work)
+	<li> the normal development libs (e.g. libc)
+	<li> extra libs depending on your desired setup (e.g. libgtk2-0-dev)
+	<li> make
+</ul>
+
+<h3> I. Setup [10..30 minutes] </h3>
+<ul>
+	<li> Objective: get a running pcb-rnd.
+	<li> steps:
+		<ul>
+			<li> 1. check out the code in a new directory: <tt>svn checkout svn://repo.hu/pcb-rnd/trunk</tt>
+			<li> 2. configure: <tt>cd trunk; ./configure </tt>
+			<li> 3. check the summary the last step produced; if you don't like it or anything is suspicious, consult Igor2 on IRC
+			<li> 4. compile: <tt>cd trunk; make</tt>
+			<li> 5. check if the compilation terminated with error (->Igor2 on IRC)
+			<li> 6. dry run: <tt>cd trunk/src; ./pcb-rnd</tt>
+			<li> 7. check if it generally runs as expected (note: there's no opengl, the menu layout and some default settings are slightly different)
+			<li> 8. please <b>report even if everything worked</b>: it's for the record/statistics; please include, in your report:
+			<ul>
+				<li> a. your system: <tt>uname -a</tt>
+				<li> b. whether you used the gtk HID or the lesstif HID or both
+			</ul>
+		</ul>
+</ul>
+
+<h3> II. Test drive [30..60 minutes] </h3>
+
+<ul>
+	<li> Objective: find random bugs and make suggestions via a simulated production test drive.
+	<li> steps:
+		<ul>
+			<li> 1. take on of your existing .pcb (preferably one that you can share in case of a bugreport is needed)
+			<li> 2. copy the .pcb to trunk/src as foo.pcb
+			<li> 3. run <tt> cd trunk/src; ./pcb-rnd foo.pcb </tt>
+			<li> 4. pretend carrying out a real modification; e.g. replace one of the ICs with a footprint of the same pin count but different dimensions (e.g. so8 -> ssop8 or sot23->to220). Pretend you really want to do this modification and pretend that the board will go in production. Do <b>your</b> usual routine (e.g. check gerbers).
+			<li> 5. if you see anything strange, find bugs, have suggestions:
+			<ul>
+				<li> a. consult Igor2 on IRC
+				<li> b. if there are files to be "attached" or your observation/question can only be stated in a medium sized novel, please send an email to pcb-rnd (at) igor2.repo.hu (and then we can still discuss it on IRC)
+				<li> c. it is sometimes worth checking the behavior of a recent mainline pcb; there are bugs and oddities present in both current mainline and pcb-rnd (because of the common ancestor)
+			</ul>
+		</ul>
+</ul>
+
+<h3> III. Systematic testing [60..90] </h3>
+<ul>
+	<li> Objective: systematic testing of specific features that are affected
+	     by recent code rewrite/development.
+	<li> steps:
+		<ul>
+			<li> 1. contact Igor2 on IRC to get a chunk of the tests
+			<li> 2. understand the subsystem in question (from user perspective)
+			<li> 3. use your fantasy to invent test methods to see whether the
+			        subsystem works in common or normal use cases
+			<li> 4. use your fantasy to invent test methods to see whether the
+			        subsystem works in corner cases
+			<li> 5. report anything that seems like a bug or undesired behavior or regression
+			<li> 6. please <b>do report</b> if there's no bug and the given subsystem
+			     worked as expected; positive results are just as important as negative ones
+			<li> 7. start over from step III/1.
+</ul>
+</body>
+</html>
diff --git a/doc-rnd/devlog/20160802.html b/doc-rnd/devlog/20160802.html
new file mode 100644
index 0000000..31663e1
--- /dev/null
+++ b/doc-rnd/devlog/20160802.html
@@ -0,0 +1,25 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> Languages and libs </H2>
+
+Reflecting to (but not joining) the annual geda-user language/libs debate,
+the policy of pcb-rnd (and later cschem) on this:
+<ul>
+	<li> the core part of the code is in C - not in C++, C#, cobol, ada, haskell, etc.
+	<li> external dependencies are minimized, especially core shouldn't depend on too many external libs and tools (e.g. core shouldn't depend on glib)
+	<li> core doesn't know anything about [turing complete] scripting languages, user scripting is provided by a plugin
+	<li> the scripting plugin does not attempt to restrict the user to My Favorite Language - it rather supports 10+ different languages, from ruby through awk through python to lua; it even supports an older version of guile; all languages are options.
+</ul>
+The above set of rules is crafted so that compiling core functionality is easy
+and excess external libs (gtk, glib) are needed only for optional features
+(such as the gtk hid in pcb-rnd). However, it's equally important not to
+restrict users in optional extras that may require more dependencies. This is
+solved by minimizing core and moving functionality behind APIs in a plugin
+system.
+<p>
+<b>This policy is very unlikely to change in the future</b> - if you believe
+the core should be rewritten in your favorite language, please fork the
+project or start a new one.
+
diff --git a/doc-rnd/devlog/20160808_model.html b/doc-rnd/devlog/20160808_model.html
new file mode 100644
index 0000000..7520f1a
--- /dev/null
+++ b/doc-rnd/devlog/20160808_model.html
@@ -0,0 +1,101 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> How to represent the world in pcb-rnd </H2>
+
+<H3> Proper implementation </H3>
+
+<daydreaming>
+<p>
+In a proper model there's no element/footprint. There are a few simple 
+primitives and there are groups, like the ones in svg: groups contain 
+metadata, primitives and groups.
+<p>
+An element is just a group. The whole design may contain groups of groups 
+of groups. A stereo amplifier may be a group for a PSU and two instances 
+of a single-channel amp group that match square-unit to square-unit.
+<p>
+What makes up an element now is a refdes, its pins/pads we can identify 
+(for the netlist-related things) and the feature that you can drag the 
+whole thing as one. A group could do all these as well: refdes is just 
+metadata, silk lines are just primitives, pins/pads are just small groups 
+of primitives and metadata.
+<p>
+There could be "group types" and type-specific code handling them. In such 
+a setup you probably don't need to have a text object: just a group that 
+consists of mostly lines (or polygons) and some metadata (the string 
+version of the text); the group type would assigns the text-handling code 
+with to the group. So for the rest of the code, text is not a special 
+thing, not a bounding box, but just a set of primitives. When you edit it, 
+you can edit it either line-by-line or as text with the assigned code.
+<p>
+This would allow us to have a very small amount of primitives: line, arc, 
+poly, hole (without a ring - a via is just a group of a few primitives). 
+Each primitive would have a layer-span field; this would solve the 
+blind/buried via question for good. (In theory line and arc could be one 
+thing, spline oslt, but I don't think performance penalty is worth that.)
+<p>
+However, this means a total rewrite as almost every non-trivial piece of 
+the code depends on the object model, from find/search/drc to the whole 
+polygon code, just to name two huge ones. The file format thing is the 
+smallest of this all. The only major parts I think we could keep are the 
+GUI and the new conf system. At the current rate of development this would 
+take more than a year to have a mostly working version and probably yet 
+another year to get everything tested out and reach current level of 
+service again.
+<p>
+I am not against such a rewrite, but it would really need a very strong 
+user/tester backing and maybe more developers than the current amount.
+<p>
+Also, Imagine we'd have a pcb program you couldn't trust on 
+anything: e.g. losing your data, generating broken gerber, etc. for a very 
+long time...
+<p>
+So I generally agree that we should do this, but for practical reasons I 
+am a bit skeptic whether this would work out in the current situation. 
+It'd kill any cschem plans for years, and because of the long development 
+cycle it would most probably kill pcb-rnd too.
+<p>
+</daydreaming>
+
+<H3>Improper implementation</h3>
+
+However, I see an alternative that is not as good as the real thing, but 
+might be good enough that users don't see much difference. It's 
+possible to implement it step by step, without long years of "nothing 
+works as used to" phases. The alternative is:
+<p>
+<ul>
+<li>1. Add io_lihata, first as a second native file format option, but long 
+term as the primary file format; it's because we can't really extend the 
+code without breaking compatibility with mainline on the .pcb format. In 
+other words, we either _pretend_ we are compatible, but using any new 
+feature would break compatibility (current state) or we admit that new 
+features are implemented only in the lihata syntax and when you save to 
+.pcb it's like saving png from gimp, losing some extra layer info. This 
+step will happen either way, for other reasons.
+
+<li> 2. rearrange some internals; the core is currently split per operation
+(e.g. there's create.c for creating all sort of objects and remove.c
+for removing them). The new setup should be per object type (e.g. arc.c
+that knows how to create or remove arcs).
+
+<li>3. Once lihata is in place, we can extend/rewrite some parts. An option 
+is to rewrite footprint support to allow them to be sort of a small pcb 
+design. So objects on arbitrary layers, footprint-in-footprint, etc.
+
+<li>4. When 3. is done, we could extend element handling so that pcb-rnd knows 
+which element is there for why. So you could have some real elements, with 
+refdes, part of the netlist, etc, but you could also do a panelization as 
+a matrix of full pcb designs place like if they were elements.
+</ul>
+<p>
+This 3.,4. would not be the same as building up everything from generic 
+atoms, but would be close to it and would be much simpler to implement 
+(still a big one, tho). I believe in practice, for end users, it would 
+probably be 99% the same as the proper solution.
+<p>
+I believe 3.,4. would be more or less the pcb-rnd equivalent of gschem's 
+implementation of schematics and symbols with about the same level of 
+generalism. Not perfect, but maybe good enough.
diff --git a/doc-rnd/devlog/20160823_lhtpers.html b/doc-rnd/devlog/20160823_lhtpers.html
new file mode 100644
index 0000000..b34355e
--- /dev/null
+++ b/doc-rnd/devlog/20160823_lhtpers.html
@@ -0,0 +1,209 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> File format vs. VCS vs. script and manual editing </H2>
+
+<H3> The problem </H3>
+
+The file a CAD program loads and saves is likely to end up in a Version
+Control System (VCS) sooner or later. To make VCS more meaningful, the
+changes to the file need to be minimal and relevant. For example in a
+load-save round trip (i.e. load the file in the CAD and save it in another
+file without any modification) the diff should be empty. If the user adds a new
+object, the diff should contain only lines added for the new object. (And maybe
+a few lines of changes in an index, if there's an index, but I believe it's
+better if there's no index.) Likewise, if an object is deleted, it is expected
+to result a few lines removed, preferably at a single location.
+
+<p>
+Terminology used in this document:
+<ul>
+	<li> <i>file</i> is the data file, represented in a structured text format;
+	     for the whole document except for the last chapter, any format may work:
+	     xml, json, ini, lihata, etc.
+	<li> <i>node</i> a data node; may be:
+		<ul>
+			<li> a leaf node with <i>payload</i>: an actual data item (like a number)
+			<li> a <i>hash</i> that contains an unordered set of other named nodes
+			<li> a <i>list</i> is an ordered list of named or unnamed nodes
+		</ul>
+</ul>
+<p>
+The file is a tree; even an ini file is a tree with 1 or 2 levels (bottom
+level is a hash of text nodes, top level, groups, represent another hash). An
+xml is a list of lists with text nodes and non-text-node attributes
+representing a hash.
+
+<H4> Problem #0: order </H4>
+To achieve the above goals, both gschem and pcb and pcb-rnd are prudent:
+objects are ordered in memory. This is necessary because in an unordered
+list or hash if the number of objects changes (objects added or removed)
+the saving order may shuffle, causing a lot of unnecessary changes.
+<p>
+This problem is already solved.
+
+<H4> Problem #1: canonical form </H4>
+Our file formats are plain text. While there are certain constructs that have
+strict format, e.g. a string in quotes can be described only one way without
+changing the content of the string, other parts have alternatives:
+<ul>
+	<li> white spaces and other field separators
+	<li> numerical formats: is 8 the same as 08 or 8.0?
+	<li> pcb flag formats, old/new style: the same object can be described
+	     using different syntax constructions, sometimes resulting the same
+	     object
+</ul>
+<p>
+This problem is <b>seemingly</b> solved by using a canonical form: even if the
+loader may accept alternatives, the save code always generates the same formatted
+output, down to the last whitespace indentation.
+
+<H4> Problem #2: hand editing, external scripts </H4>
+However, the canonical format is not always the most convenient for the user.
+Sometimes different indentation, maybe even locally, may improve readability.
+Unfortunately a CAD round-trip brings back the file to canonical format, removing
+any user formatting. Or in other words, the user needs to learn the syntax
+of the file format <b>and</b> how the canonical format looks like, else user
+changes to a file may cause VCS noise later.
+<p>
+Even worse, 3rd party scripts need to learn the canonical format too, for the
+same reason.
+
+
+<H3> The solution </H3>
+One solution is to merge the canonical format and the syntax, so the resulting
+specification is so strict that there are no alternatives but everything can
+be written using only a single, well described format. This solves problem
+#1 and #2, but creates problem #3: a text file format as rigid as a binary
+format would be. While it could be still parsed by text tools easily, it
+would be much harder to emit properly using the same tools or even harder to
+edit manually, with a text editor. In this sense it may start losing its
+most important text-file-property: free form, to some extent.
+<p>
+An alternative is that the CAD program doesn't save canonical format over
+an existing file but tries to accommodate to the format the file already features.
+This solves problem #0, #1, #2 and #3, allowing the user (or 3rd party scripts)
+to use whatever formatting they prefer, with clean round trips.
+
+<H3> How to implement it (in theory) </H3>
+A few unusual features are required:
+<ul>
+	<li> F1: the parser needs to be aware of otherwise unimportant
+	     characters (e.g. whitespace in indentation)
+	<li> F2: if (payload) data is to be converted to a native format by
+	     the program, the original text representation needs to be saved;
+	     e.g. the input file contains 4.99999999, the program loads it to
+	     a float and later saved back, it may end up as 4.999997836;
+	     instead if the data did not change, the original text shall be
+	     saved back; this also solves the 005 vs 5 vs 5.0 problem
+	<li> F3: new data should be inserted, obsolete data should be removed
+	     without reordering stationary data
+</ul>
+
+<H4> The obvious approach </H4>
+An obvious approach would be to smarten the parser and store delimiters
+(e.g. whitespace) and the original text version for each data node, along with
+the order of nodes in the original file.
+
+<H4> The less obvious approach </H4>
+A second approach is to ignore these details on load and keep on storing data
+in native binary format in memory. On save, start parsing the original file
+(or a snapshot of it taken load-time), and:
+<ul>
+	<li> any non-data-payload character should be copied the the output;
+	     this includes whitespace, delimiters, comments, syntax sugar;
+	     this <b>solves F1</b>
+	<li> if a node did not change, the parser copies it byte-to-byte to
+	     the output (<b>solves F2 partially</b> - no format change if data is
+	     unchanged in memory);
+	<li> value change check: while parsing, convert the original value to
+	     native format again and compare, which <b>solves F2</b>: the same
+	     string->native conversion should yield the same native result;
+	     if our in-memory representation differs, it has changed and the
+	     new value should be copied to the output instead of the old value;
+	     the format of the new value doesn't matter, can be canonical, since
+	     the diff is already caused by the fact that the value has changed.
+	<li> if the language supports ordered lists: before emitting list items,
+	     the input list must be read through (pre-read) and the in-memory and
+	     just-parse lists need to be compared to detect changes in order;
+	     existing list items are copied as described above; list items
+	     deleted in-memory are ignored; list items added in-memory are
+	     written out (<b>solves F3</b>)
+	<li> if the language supports unordered hashes: check each key parsed;
+	     if it exists in-memory, copy/write it normally; if it doesn't,
+	     delete it; add new hash items (the ones present in-memory but
+	     not seen when the parser reaches the end of the hash; <b>solves F3</b>)
+</ul>
+<p>
+This may sound more expensive than the first approach, because the parser needs
+to run again on save. However, the costs of the previous approach are
+high too:
+<ul>
+	<li> essentially the whole file needs to be stored in-memory
+	     (for whitespace), but not in one bug chunk but in many
+	     little chunks among with the data nodes (which leads to
+	     allocation overhead); the original values need to be stored in
+	     text too, to solve the numeric problems
+	<li> either the same string->native conversion needs to be done using
+	     stored text values; or the code needs to keep track of which node
+	     changed using some data-changed bit
+	<li> the same list/hash merging needs to be done
+</ul>
+<p>
+If the syntax-sugar overhead of the file format is low, majority of all
+bytes will be actual node data (need to be stored) and delimiters/indentation
+(need to be stored). Then it might be cheaper to store it all in one bug chunk
+and re-parse it (without building a DOM) than storing the same data in small
+chunks.
+
+
+<H4> An even less obvious approach </H4>
+Take the previous method, but assume the application does not change the
+order of objects on ordered lists and each object has some sort of unique
+identifier (so two identical looking objects can't be on a list).
+This makes list merging much simpler and doable in a single pass, keeping
+a list item pointer with the in-memory list:
+<ul>
+	<li> initialization: set the pointer to the first element of the list
+	<li> iteration: parse the first (next) item of the list
+	<li> check if it exists in-memory and is the first
+		<ul>
+			<li> if yes, copy from the original file, increase the pointer by
+			     one element
+			<li> if not, check if it got deleted (not present on the list
+			     in-memory) and skip it if so; pointer stays in place
+			<li> if not, check if it is on the in-memory list as a later entry;
+			     if so, insert all preceding entries from the in-memory list,
+			     increasing the pointer by one for each element written
+		</ul>
+	<li> repeat the iteration until there are no more items on the input list
+	<li> check if there are items remaining from the pointer, if so output
+	     them (they will end up at the end of the list in the output file)
+	<li> finished: the output file's list now matches the in-memory list.
+</ul>
+<p>
+Empty lists may need special (but trivial) treatment.
+<p>
+While copying an existing node, it is possible to "pick up" style
+e.g. indentation. When a new item needs to be added, such picked up local
+style info can be used to generate output that looks similar to other items
+in its local environment. For example the first time a list or hash item
+needs to be added is right after at least one item of the given hash or
+list is already parsed, which means the style of an item is already know.
+
+<H3> How it is implemented in practice </H3>
+Using the last approach from the above list, with an utility library for
+lihata. The full list of requirements (on the application and the tree
+structure):
+<ul>
+	<li> the root element of the input file and the in-memory representation
+	     must fully match
+	<li> list items must have names that are unique to the list, even across
+	     item removal from the list within a session (so a newly added item
+	     never has the same name as a past item parsed back from the file)
+	<li> the application must not change the order of elements on a list -
+	     new elements may be inserted anywhere and old elements may be
+	     removed from anywhere but the order of any two remaining old
+	     elements must be preserved
+</ul>
diff --git a/doc-rnd/devlog/20160826_virtusers.html b/doc-rnd/devlog/20160826_virtusers.html
new file mode 100644
index 0000000..2b92450
--- /dev/null
+++ b/doc-rnd/devlog/20160826_virtusers.html
@@ -0,0 +1,65 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> Offer vs. pull (virtual users vs. active users) </H2>
+Pcb-rnd has an pulled-by-users policy on deciding what features to implement.
+Below I try to explain how we ended up using this policy.
+
+<h3> Past: offer, virtual users </H3>
+
+When I started pcb-rnd, although I published it, I didn't think anyone else
+would use it. It was just about having mainline with different default
+settings.
+<p>
+Later on I added a few features, and pcb-rnd slowly became much more than
+just different defaults. I felt other users may find the new features useful,
+so I had <i>put</i> it on offer: advertised the repository. I thought users
+would download and try the software. Judging from the feedback, they didn't. I
+didn't mind, becuase I was working on features I needed, to get pcb-rnd do what
+I wanted: I was my own target audience and anyone else trying pcb-rnd could
+only be a side effect. Until when I ran out of features because it already
+had everything I needed.
+<p>
+The next step was to implement features for other users. Between summer
+of 2015 and summer of 2016, I tried to be a bit more proactive: made
+public polls to map what users needed and tried to focus implementing
+those features. This did not bring too many users either.
+<p>
+This was when I realized what was really happening: I was <i>offering</i>
+features for <i>virtual</i> users, addressing my communication to the wide
+auidence. Noone really felt it was for him. Even if he was complaining about
+a missing feature in pcb and a few days later I announced the fix in pcb-rnd, it
+was not specifically for him, but for the Greater Good.
+<p>
+Virtual users nearly never became real users. My conclusion was that there
+was no point in implementing features for virtual users as noone ever would
+use those features.
+
+<h3> Present: pull, active users </H3>
+Mid summer 2016, I switched strategy. Instead of polling the crowd, I picked
+a few users with known feature requests and asked them if they were willing to
+test pcb-rnd if I implemented their feature. More than half of them said yes
+and many of them got hooked up. They all became productive, active members
+of the pcb-rnd community.
+<p>
+Learning from this experiece, the new strategy of pcb-rnd is as follows:
+<ul>
+	<li> pull instead of offer: do not hope a new feature would attract users,
+	     rather arrange active users who then pull the project with their
+	     feature requests;
+	<li> do not invest too much time in anything that is not currently pulled
+	     by actual, existing, active users; rather invest that time in fulfilling
+	     the needs of already existing users
+	<li> reaching 100 users with an average of 0.1% activity will result in
+	     0 active useres whereas reaching only 10 users with 30% activity
+	     will result in 3 active users; thus a small active communit around
+	     pcb-rnd is more efficient than the much larger geda community;
+	<li> users really appreciate fast response on bugreports and feature requests.
+	     Instead of trying to impress virtual users, rather keep already interested
+	     users happy.
+</ul>
+
+
+
+
diff --git a/doc-rnd/devlog/20160921_gl.html b/doc-rnd/devlog/20160921_gl.html
new file mode 100644
index 0000000..23a9274
--- /dev/null
+++ b/doc-rnd/devlog/20160921_gl.html
@@ -0,0 +1,56 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> what's missing for: opengl </H2>
+<p>
+What we don't have:
+<ul>
+	<li> rendering with opengl
+	<li> utilizing the 2d or 3d hardware acceleration of the GPU in drawing
+	<li> transparent copper layers
+	<li> on some CPU+GPU combination the current software rendering is slower than mainline's opengl rendering
+</ul>
+<p>
+Why we don't have it:
+<ul>
+	<li> I don't have a hardware+driver combination where opengl rendering works at acceptable speed
+	<li> I have no experience with opengl programming
+	<li> I am not much interested in changing any of the above two
+</ul>
+<p>
+What's needed:
+<ul>
+	<li> 1. split the gtk hid into:
+		<ul>
+			<li> a generic gtk UI feature plugin (menu, widgets, controls, no drawing)
+			<li> a gtk-gdk (software render) HID plugin that depends on the gtk feature plugin
+			<li> an gtk-opengl HID plugin that depends on the gtk feature plugin and on the opengl utility plugin
+		</ul>
+	<li> 2. get scconfig to detect opengl headers/libs
+	<li> 3. resurrect the opengl utility plugin
+	<li> 4. resurrect the opengl drawing code
+</ul>
+<p>
+How we could have it, gtk+gl:
+<ul>
+	<li> A. contribute: I can do 1. and 2. so you need to concentrate only on the opengl part (3. and 4.);
+	<li> B. or donation/sponsoration:
+		<ul>
+			<li> buy the time of a random developer for point 3. and 4.; I can still do 1. and 2. in free time, scheduled with the other pcb-rnd duties
+			<li> buy my time so I don't need to sit in my regular job but have a few days off to do all four points
+		</ul>
+</ul>
+<p>
+How we could have it, long term:
+<ul>
+	<li> I plan to write an SDL2 based HID
+	<li> SDL2 has transparent render support for both software and accelerated drawing, the latter probably means 2d hardware acceleration which is probably as good as direct opengl drawing would be
+</ul>
+<p>
+What we can't do:
+<ul>
+	<li> make transparent layers with the current gtk/gdk software render (too much effort)
+	<li> make transparent layers with lesstif (too much effort)
+	<li> speed up the software render
+</ul>
diff --git a/doc-rnd/devlog/20161014_cleanup.html b/doc-rnd/devlog/20161014_cleanup.html
new file mode 100644
index 0000000..aad7ccb
--- /dev/null
+++ b/doc-rnd/devlog/20161014_cleanup.html
@@ -0,0 +1,35 @@
+<html>
+<body>
+<h1> pcb-rnd <a href="http://repo.hu/projects/pcb-rnd/devlog">devlog</a> </h1>
+
+<H2> The way forward on cleanup </H2>
+This is a long term plan (these will happen slowly, step-by-step, gradually, in
+the background, in parallel with the normal schedule of development:
+<ul>
+	<li> phase 0: port the rest [almost done]
+		<ul>
+			<li> import the remaining plugins and exporters before the big cleanup because it's cheaper to port them now
+		</ul>
+	<li> phase 1: reorg
+		<ul>
+			<li> reorganize the code: from action-oriented (e.g. change.c, remove.c, mirror.c) into object-type-oriented code (e.g. obj_line.c, obj_arc.c)
+			<li> global*.h must be dissolved/removed
+			<li> macro.h should be dissolved/removed
+			<li> const.h should be reduced to 1/3
+		</ul>
+	<li> phase 2: clean the namespace
+		<ul>
+			<li> pcb_ prefixes
+			<li> unify convention: underscores instead of CamelCase, prefer shorter names
+			<li> better connection between file name and function name prefixes (don't invent 3 different names for the same thing)
+		</ul>
+	<li> phase 3: split & pack
+		<ul>
+			<li> move out generic, non-pcb-specific code to "external libs" (e.g. polygon, heap, the conf system)
+			<li> determine layering of the new lib system (e.g. a generic unit, printf, etc. util lib on layer 0, then a lib about the basic object types on layer 1, then footprint libraries and other plugins on layer 2)
+			<li> compile one .a file per layer, link the final executable using those .a files
+			<li> make the libs installable, together with the headers
+			<li> fix gsch2pcb-rnd to use the new lib(s)
+		</ul>
+</ul>
+
diff --git a/doc-rnd/devlog/20162601/header b/doc-rnd/devlog/20162601/header
new file mode 100644
index 0000000..337ee8e
--- /dev/null
+++ b/doc-rnd/devlog/20162601/header
@@ -0,0 +1,11 @@
+1. How many of your boards REQUIRE burried or blind vias? [percentage]
+2. How many of your boards could BENEFIT burried or blind vias? [percentage]
+3. When your board REQUIRED them, do you more often find a workaround to stick with pcb instead of switching to another design tool? [yes/no]
+4. When your board could BENEFIT using them, do you more often find a workaround to stick with pcb instead of switching to another design tool? [yes/no]
+5. Have you ever tried pcb-rnd? [yes/no]
+6. Would you try pcb-rnd if it offered blind/burried vias? [yes/no]
+7. Would you consider switching to pcb-rnd if it offered blind/burried vias? [yes/no]
+8. Approximately how many boards have you done so far with pcb (pcb mainline/branches/forks included, using gschem/gnetlist not required)? "1 board done" means you had one or more physical copies of the board. [rough estimation, positive integer]
+9. Approximately how many boards have you done with other packages? [rough estimation, positive integer]
+10. What OS would you prefer to do your pcb layouts on (assuming your favorite layout tool is/would be available on it)? [one of: (GNU/)Linux, *BSD, OSX, windows, other; if you like more, select the one you would spend most time on!]
+
diff --git a/doc-rnd/devlog/20162601/header.csv b/doc-rnd/devlog/20162601/header.csv
new file mode 100644
index 0000000..2066a05
--- /dev/null
+++ b/doc-rnd/devlog/20162601/header.csv
@@ -0,0 +1 @@
+1. How many of your boards REQUIRE burried or blind vias? [percentage],2. How many of your boards could BENEFIT burried or blind vias? [percentage],3. When your board REQUIRED them, do you more often find a workaround to stick with pcb instead of switching to another design tool? [yes/no],4. When your board could BENEFIT using them, do you more often find a workaround to stick with pcb instead of switching to another design tool? [yes/no],5. Have you ever tried pcb-rnd? [yes/no],6. Would [...]
\ No newline at end of file
diff --git a/doc-rnd/devlog/20162601/poll.csv b/doc-rnd/devlog/20162601/poll.csv
new file mode 100644
index 0000000..88a7e09
--- /dev/null
+++ b/doc-rnd/devlog/20162601/poll.csv
@@ -0,0 +1,10 @@
+0,50,yes,yes,no,no,no,1,2,Linux
+10,40,no,n/a,no,yes,n/a,20,2,Linux
+0,5,n/a,no,no,no,no,20,4,Linux
+5,20,no,no,no,yes,no,5,20,Linux
+0,0,n/a,n/a,no,no,no,10,0,Linux
+0,80,yes,yes,no,no,no,15,0,Linux
+50,80,no,no,no,yes,n/a,30,100,OSX
+20,75,no,no,no,yes,yes,15,3,Linux
+0,8,yes,yes,yes,yes,no,2,1,Linux
+0,2,yes,yes,no,no,no,8,4,Linux
diff --git a/doc-rnd/devlog/20162601/poll.html b/doc-rnd/devlog/20162601/poll.html
new file mode 100644
index 0000000..f50cf4b
--- /dev/null
+++ b/doc-rnd/devlog/20162601/poll.html
@@ -0,0 +1,24 @@
+<html><body><table border=1>
+<tr>
+<th>1. How many of your boards REQUIRE burried or blind vias? [percentage]
+<th>2. How many of your boards could BENEFIT burried or blind vias? [percentage]
+<th>3. When your board REQUIRED them, do you more often find a workaround to stick with pcb instead of switching to another design tool? [yes/no]
+<th>4. When your board could BENEFIT using them, do you more often find a workaround to stick with pcb instead of switching to another design tool? [yes/no]
+<th>5. Have you ever tried pcb-rnd? [yes/no]
+<th>6. Would you try pcb-rnd if it offered blind/burried vias? [yes/no]
+<th>7. Would you consider switching to pcb-rnd if it offered blind/burried vias? [yes/no]
+<th>8. Approximately how many boards have you done so far with pcb (pcb mainline/branches/forks included, using gschem/gnetlist not required)? "1 board done" means you had one or more physical copies of the board. [rough estimation, positive integer]
+<th>9. Approximately how many boards have you done with other packages? [rough estimation, positive integer]
+<th>10. What OS would you prefer to do your pcb layouts on (assuming your favorite layout tool is/would be available on it)? [one of: (GNU/)Linux, *BSD, OSX, windows, other; if you like more, select the one you would spend most time on!]
+<th>
+<tr><td>0<td>50<td>yes<td>yes<td>no<td>no<td>no<td>1<td>2<td>Linux
+<tr><td>10<td>40<td>no<td>n/a<td>no<td>yes<td>n/a<td>20<td>2<td>Linux
+<tr><td>0<td>5<td>n/a<td>no<td>no<td>no<td>no<td>20<td>4<td>Linux
+<tr><td>5<td>20<td>no<td>no<td>no<td>yes<td>no<td>5<td>20<td>Linux
+<tr><td>0<td>0<td>n/a<td>n/a<td>no<td>no<td>no<td>10<td>0<td>Linux
+<tr><td>0<td>80<td>yes<td>yes<td>no<td>no<td>no<td>15<td>0<td>Linux
+<tr><td>50<td>80<td>no<td>no<td>no<td>yes<td>n/a<td>30<td>100<td>OSX
+<tr><td>20<td>75<td>no<td>no<td>no<td>yes<td>yes<td>15<td>3<td>Linux
+<tr><td>0<td>8<td>yes<td>yes<td>yes<td>yes<td>no<td>2<td>1<td>Linux
+<tr><td>0<td>2<td>yes<td>yes<td>no<td>no<td>no<td>8<td>4<td>Linux
+</table></body></html>
diff --git a/doc-rnd/devlog/20162601/poll.tsv b/doc-rnd/devlog/20162601/poll.tsv
new file mode 100644
index 0000000..ef35ad5
--- /dev/null
+++ b/doc-rnd/devlog/20162601/poll.tsv
@@ -0,0 +1,10 @@
+0	50	yes	yes	no	no	no	1	2	Linux
+10	40	no	n/a	no	yes	n/a	20	2	Linux
+0	5	n/a	no	no	no	no	20	4	Linux
+5	20	no	no	no	yes	no	5	20	Linux
+0	0	n/a	n/a	no	no	no	10	0	Linux
+0	80	yes	yes	no	no	no	15	0	Linux
+50	80	no	no	no	yes	n/a	30	100	OSX
+20	75	no	no	no	yes	yes	15	3	Linux
+0	8	yes	yes	yes	yes	no	2	1	Linux
+0	2	yes	yes	no	no	no	8	4	Linux
diff --git a/doc-rnd/devlog/poll_common/gencsv.sh b/doc-rnd/devlog/poll_common/gencsv.sh
new file mode 100755
index 0000000..2eec9f2
--- /dev/null
+++ b/doc-rnd/devlog/poll_common/gencsv.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+sed "s@\t@, at g" < poll.tsv > poll.csv
+tr "\n" "," < header > header.csv
diff --git a/doc-rnd/devlog/poll_common/genhtml.sh b/doc-rnd/devlog/poll_common/genhtml.sh
new file mode 100755
index 0000000..a991bd5
--- /dev/null
+++ b/doc-rnd/devlog/poll_common/genhtml.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+(
+echo '<html><body><table border=1>'
+echo "<tr>"
+sed "s@^@<th>@" < header
+sed "s@^@<tr><td>@;s@\t@<td>@g" < poll.tsv
+echo "<tr>"
+hl=`wc -l < header`
+for n in `seq $hl`
+do
+	./genpie.sh $n
+	fn=pie_col_$n.png
+	if test -f "$fn"
+	then
+		echo "<td><img src=\"$fn\">"
+	else
+		echo "<td> "
+	fi
+done
+
+echo '</table>'
+if test -f "legend.txt"
+then
+	echo "<p>"
+	echo "<h3> Legend </h3>"
+	echo "<pre>"
+	cat legend.txt
+	echo "</pre>"
+fi
+
+echo "<h3> Downloads </h3>"
+echo '<ul>'
+
+if test -f poll.csv
+then
+	echo '	<li><a href="poll.csv"> csv data </a> | <a href="header.csv"> csv header </a>'
+fi
+
+if test -f poll.tsv
+then
+	echo '	<li><a href="poll.tsv"> tsv data </a> (raw, no header)'
+fi
+
+if test -f legend.txt
+then
+	echo '	<li><a href="legend.txt"> legend </a>'
+fi
+
+echo '<ul>'
+
+echo '</body></html>'
+) > poll.html
+
+
diff --git a/doc-rnd/devlog/poll_common/genpie.sh b/doc-rnd/devlog/poll_common/genpie.sh
new file mode 100755
index 0000000..e597647
--- /dev/null
+++ b/doc-rnd/devlog/poll_common/genpie.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+gen()
+{
+	awk -F "[\t]" -v "col=$1" '
+	BEGIN {
+		if (col < 0) {
+			col = -col
+			keep_na = 1
+		}
+	}
+	{
+		val=$col
+		if (val == "-") {
+			if (keep_na)
+				val="n/a"
+			else
+				next
+		}
+		split(val, VAL, "[+]")
+		for(n in VAL) {
+			VOTE[VAL[n]]++
+		}
+	}
+
+	END {
+		for(n in VOTE) {
+			print "@slice\n" VOTE[n]
+			print "@label\n" n
+		}
+	}
+	'
+}
+
+for n in $*
+do
+	(gen $n < poll.tsv | animpie && echo "screenshot \"pie_col_$n.png\"") | animator -H -x 150 -y 150
+done
diff --git a/doc-rnd/devlog/res/20150830b_annot.dot b/doc-rnd/devlog/res/20150830b_annot.dot
new file mode 100644
index 0000000..9fd8ce7
--- /dev/null
+++ b/doc-rnd/devlog/res/20150830b_annot.dot
@@ -0,0 +1,19 @@
+digraph annot {
+
+
+	gnetlist [shape=box]
+
+	{rank=same pcb gschem spacer1}
+
+	spacer1 [style=invisible]
+
+	foo [shape=box]
+
+	gschem -> gnetlist [label="forward1"]
+	pcb -> foo [label="back1"]
+
+	gschem->spacer1->pcb [style=invisible arrowhead=none]
+
+	gnetlist -> pcb [label="forward2"]
+	foo -> gschem [label="back2"]
+}
diff --git a/doc-rnd/devlog/res/20150830b_annot.png b/doc-rnd/devlog/res/20150830b_annot.png
new file mode 100644
index 0000000..0dac4a3
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_annot.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s0.png b/doc-rnd/devlog/res/20150830b_s0.png
new file mode 100644
index 0000000..f95a7cb
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s0.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s1.png b/doc-rnd/devlog/res/20150830b_s1.png
new file mode 100644
index 0000000..3c8ea94
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s1.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s2.png b/doc-rnd/devlog/res/20150830b_s2.png
new file mode 100644
index 0000000..486bf79
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s2.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s3.png b/doc-rnd/devlog/res/20150830b_s3.png
new file mode 100644
index 0000000..1a5670a
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s3.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s4.png b/doc-rnd/devlog/res/20150830b_s4.png
new file mode 100644
index 0000000..159b775
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s4.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s5.png b/doc-rnd/devlog/res/20150830b_s5.png
new file mode 100644
index 0000000..56521ff
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s5.png differ
diff --git a/doc-rnd/devlog/res/20150830b_s6.png b/doc-rnd/devlog/res/20150830b_s6.png
new file mode 100644
index 0000000..0fc1de8
Binary files /dev/null and b/doc-rnd/devlog/res/20150830b_s6.png differ
diff --git a/doc-rnd/devlog/res/Makefile b/doc-rnd/devlog/res/Makefile
new file mode 100644
index 0000000..8ce9f59
--- /dev/null
+++ b/doc-rnd/devlog/res/Makefile
@@ -0,0 +1,4 @@
+all: 20150830b_annot.png
+
+%.png: %.dot
+	dot -Tpng < $^ > $@
diff --git a/doc-rnd/distros.txt b/doc-rnd/distros.txt
new file mode 100644
index 0000000..b8f0ecd
--- /dev/null
+++ b/doc-rnd/distros.txt
@@ -0,0 +1,47 @@
+How to request packaging (distros sorted by popularity, 2016):
+
+Ubuntu:
+	easiest is to get in Debian, Ubuntu picks up packages from sid
+	alternative: ubuntu-devel-discuss at lists.ubuntu.com (pending)
+	alternative: https://bugs.launchpad.net/ubuntu/+filebug?no-redirect&field.tag=needs-packaging
+
+linux mint
+	https://blueprints.launchpad.net/linuxmint
+	(same maintainer as debian?)
+
+slackware:
+	Promised (AFK, ask again in 3rd week of sept)
+	geda/pcb maintainer: pfeifer[dot]felix[at]googlemail[dot]com (sent mail)
+	https://slackbuilds.org/guidelines/
+	needs an existing package
+	<MLanden> since the slackbuild needs slackware's pkgtools usually at the end of a ./configure --prefix=/usr --extrastuff..make.. make install DESTDIR=/tmp/NAMEOFPACKAGE..cd /tmp/NAMEOFPACKAGE..makepkg -l y -c n ../NAMEOFPACKAGE-123.txz
+	installwatch can build a package
+	check out #slackbuilds
+
+Debian:
+	Bdale?
+	(alternative: pkg-electronics-devel at lists.alioth.debian.org - pending)
+	(alternative: RFP through reportbug)
+
+fedora:
+	https://fedoraproject.org/wiki/Package_maintainers_wishlist?rd=PackageMaintainers/WishList
+	(wrote mail to cicku)
+
+OpenSuse:
+	adrian [at] suse.de (mail sent)
+
+Arch:
+	DONE! needs to be modularized
+	Kyle Keen <keenerd [at] gmail.com> (mail sent)
+
+Manjaro:
+	Same as arch
+
+----
+FreeBSD:
+	hrs [at] freebsd.org (Hiroki Sato - mail sent)
+
+OpenBSD:
+	andreas.bihlmaier [at] gmx.de -> ENOTIME, he suggests ports at openbsd.org
+
+
diff --git a/doc-rnd/djopt/Makefile b/doc-rnd/djopt/Makefile
new file mode 100644
index 0000000..c6af240
--- /dev/null
+++ b/doc-rnd/djopt/Makefile
@@ -0,0 +1,50 @@
+PCB=../../src/pcb-rnd
+HTML=index.html
+CASES = \
+ debumpify.$(TARGET) \
+ miter.$(TARGET) \
+ orthopull.$(TARGET) \
+ unjaggy.$(TARGET) \
+ vianudge.$(TARGET) \
+ viatrim.$(TARGET)
+
+all:
+	make conv TARGET=out.png
+	make conv TARGET=png
+	make index
+
+index:
+	make index.html TARGET=txt
+
+index.html: Pre.html Post.html $(CASES) Makefile
+	cat Pre.html > $(HTML)
+	make conv TARGET=tbl
+	cat Post.html >>$(HTML)
+
+conv: $(CASES)
+
+.SUFFIXES: .pcb .out.pcb .png .out.png .tbl
+.PRECIOUS: %.pcb %.out.pcb %.png %.out.png
+
+.pcb.out.pcb:
+	echo "OptAutoOnly(); djopt($*); SaveTo(LayoutAs, $*.out.pcb.tmp);"| $(PCB) --gui batch $*.pcb
+	awk '/Symbol.. . 1800./,/^Attribute/ { next } { print $$0 }' < $*.out.pcb.tmp > $*.out.pcb
+	rm $*.out.pcb.tmp
+
+.pcb.tbl:
+	echo '<tr><th>$*<td>'>>$(HTML)
+	cat $*.txt >>$(HTML)
+	echo '  <td><a href="$*.pcb"><img src="$*.png"></a>  <td><a href="$*.out.pcb"><img src="$*.out.png"></a>'>>$(HTML)
+
+.pcb.png:
+	$(PCB) -x png --dpi 300  $*.pcb
+
+out.pcb.out.png:
+	$(PCB) -x png --dpi 300  $*.out.pcb
+
+clean:
+	rm *.out.pcb *.png index.html 2>/dev/null; true
+
+
+
+
diff --git a/doc-rnd/djopt/Post.html b/doc-rnd/djopt/Post.html
new file mode 100644
index 0000000..21a62da
--- /dev/null
+++ b/doc-rnd/djopt/Post.html
@@ -0,0 +1,4 @@
+<tr><th>auto<td>run all the above except miter; run them multiple times until there's no more change possible <td>   <td>  
+</table>
+</body>
+</html>
diff --git a/doc-rnd/djopt/Pre.html b/doc-rnd/djopt/Pre.html
new file mode 100644
index 0000000..761df33
--- /dev/null
+++ b/doc-rnd/djopt/Pre.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+<H1> Action djopt() </h1>
+
+The different types of optimizations change your board in order to
+reduce the total trace length and via count. Each optimization is accessible
+using an argument, e.g. djopt(miter). The basic actions have to be run multiple
+times as each iteration will change the design making new changes possible for
+subsequent iterations with the same or other actions.
+<p>
+Click on the images to download the example .pcb design.
+
+<table border=1 cellspacing=0>
+<tr><th>argument name<th> description <th> example in <th> example out after 1st iteration
diff --git a/doc-rnd/djopt/debumpify.out.pcb b/doc-rnd/djopt/debumpify.out.pcb
new file mode 100644
index 0000000..ec4b853
--- /dev/null
+++ b/doc-rnd/djopt/debumpify.out.pcb
@@ -0,0 +1,88 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[0 5000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 17500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 117500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[23405 32500 30000 32500 1000 4000 "clearline"]
+	Line[92500 32500 111595 32500 1000 4000 "clearline"]
+	Line[30000 32500 30000 32500 1000 4000 "clearline"]
+	Line[30000 32500 37500 32500 1000 4000 "clearline"]
+	Line[37500 32500 37500 32500 1000 4000 "clearline"]
+	Line[37500 32500 47500 32500 1000 4000 "clearline"]
+	Line[47500 32500 57500 22500 1000 4000 "clearline"]
+	Line[57500 22500 67500 22500 1000 4000 "clearline"]
+	Line[67500 22500 77500 32500 1000 4000 "clearline"]
+	Line[77500 32500 82500 32500 1000 4000 "clearline"]
+	Line[82500 32500 85000 22500 1000 4000 "clearline"]
+	Line[90000 22500 92500 32500 1000 4000 "clearline"]
+	Line[85000 22500 90000 22500 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Text[30000 5000 0 105 "90" "clearline"]
+	Text[60000 5000 0 105 "45" "clearline"]
+	Text[82500 5000 0 105 "rand" "clearline"]
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/debumpify.out.png b/doc-rnd/djopt/debumpify.out.png
new file mode 100644
index 0000000..4fa2ecf
Binary files /dev/null and b/doc-rnd/djopt/debumpify.out.png differ
diff --git a/doc-rnd/djopt/debumpify.pcb b/doc-rnd/djopt/debumpify.pcb
new file mode 100644
index 0000000..b341d72
--- /dev/null
+++ b/doc-rnd/djopt/debumpify.pcb
@@ -0,0 +1,89 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[0 5000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Attribute("PCB::grid::unit" "mm")
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 17500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 117500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[23405 32500 30000 32500 1000 4000 "clearline"]
+	Line[92500 32500 111595 32500 1000 4000 "clearline"]
+	Line[30000 32500 30000 22500 1000 4000 "clearline"]
+	Line[30000 22500 37500 22500 1000 4000 "clearline"]
+	Line[37500 22500 37500 32500 1000 4000 "clearline"]
+	Line[37500 32500 47500 32500 1000 4000 "clearline"]
+	Line[47500 32500 57500 22500 1000 4000 "clearline"]
+	Line[57500 22500 67500 22500 1000 4000 "clearline"]
+	Line[67500 22500 77500 32500 1000 4000 "clearline"]
+	Line[77500 32500 82500 32500 1000 4000 "clearline"]
+	Line[82500 32500 85000 22500 1000 4000 "clearline"]
+	Line[90000 22500 92500 32500 1000 4000 "clearline"]
+	Line[85000 22500 90000 22500 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Text[30000 5000 0 105 "90" "clearline"]
+	Text[60000 5000 0 105 "45" "clearline"]
+	Text[82500 5000 0 105 "rand" "clearline"]
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/debumpify.png b/doc-rnd/djopt/debumpify.png
new file mode 100644
index 0000000..d3c5225
Binary files /dev/null and b/doc-rnd/djopt/debumpify.png differ
diff --git a/doc-rnd/djopt/debumpify.txt b/doc-rnd/djopt/debumpify.txt
new file mode 100644
index 0000000..03a5791
--- /dev/null
+++ b/doc-rnd/djopt/debumpify.txt
@@ -0,0 +1,2 @@
+Looks for U-shaped traces (with 90 degree corners) that can be shortened
+or eliminated.
diff --git a/doc-rnd/djopt/index.html b/doc-rnd/djopt/index.html
new file mode 100644
index 0000000..0988d74
--- /dev/null
+++ b/doc-rnd/djopt/index.html
@@ -0,0 +1,45 @@
+<html>
+<body>
+<H1> Action djopt() </h1>
+
+The different types of optimizations change your board in order to
+reduce the total trace length and via count. Each optimization is accessible
+using an argument, e.g. djopt(miter). The basic actions have to be run multiple
+times as each iteration will change the design making new changes possible for
+subsequent iterations with the same or other actions.
+<p>
+Click on the images to download the example .pcb design.
+
+<table border=1 cellspacing=0>
+<tr><th>argument name<th> description <th> example in <th> example out after 1st iteration
+<tr><th>debumpify<td>
+Looks for U-shaped traces (with 90 degree corners) that can be shortened
+or eliminated.
+  <td><a href="debumpify.pcb"><img src="debumpify.png"></a>  <td><a href="debumpify.out.pcb"><img src="debumpify.out.png"></a>
+<tr><th>miter<td>
+Replaces 90 degree corners with a pair of 45 degree corners, to reduce
+RF losses and trace length.
+  <td><a href="miter.pcb"><img src="miter.png"></a>  <td><a href="miter.out.pcb"><img src="miter.out.png"></a>
+<tr><th>orthopull<td>
+Looks for chains of traces all going in one direction, with more
+traces orthogonal on one side than on the other.  Moves the chain in
+that direction, causing a net reduction in trace length, possibly
+eliminating traces and/or corners.
+  <td><a href="orthopull.pcb"><img src="orthopull.png"></a>  <td><a href="orthopull.out.pcb"><img src="orthopull.out.png"></a>
+<tr><th>unjaggy<td>
+Looks for corners which could be flipped to eliminate one or more
+corners (i.e. jaggy lines become simpler).
+  <td><a href="unjaggy.pcb"><img src="unjaggy.png"></a>  <td><a href="unjaggy.out.pcb"><img src="unjaggy.out.png"></a>
+<tr><th>vianudge<td>
+Looks for vias where all traces leave in the same direction.  Tries to
+move via in that direction to eliminate one of the traces (and thus a
+corner).
+  <td><a href="vianudge.pcb"><img src="vianudge.png"></a>  <td><a href="vianudge.out.pcb"><img src="vianudge.out.png"></a>
+<tr><th>viatrim<td>
+Looks for traces that go from via to via, where moving that trace to a
+different layer eliminates one or both vias.
+  <td><a href="viatrim.pcb"><img src="viatrim.png"></a>  <td><a href="viatrim.out.pcb"><img src="viatrim.out.png"></a>
+<tr><th>auto<td>run all the above except miter; run them multiple times until there's no more change possible <td>   <td>  
+</table>
+</body>
+</html>
diff --git a/doc-rnd/djopt/miter.out.pcb b/doc-rnd/djopt/miter.out.pcb
new file mode 100644
index 0000000..1761648
--- /dev/null
+++ b/doc-rnd/djopt/miter.out.pcb
@@ -0,0 +1,75 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[500.0 0 0 1]
+Cursor[500 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 45000 15000 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 80000 47500 -4350 -5650 3 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+Layer(1 "component")
+(
+	Line[50905 15000 53405 15000 1000 4000 "clearline"]
+	Line[80000 41595 80000 41595 1000 4000 "clearline"]
+	Line[53405 15000 80000 41595 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/miter.out.png b/doc-rnd/djopt/miter.out.png
new file mode 100644
index 0000000..eaad770
Binary files /dev/null and b/doc-rnd/djopt/miter.out.png differ
diff --git a/doc-rnd/djopt/miter.pcb b/doc-rnd/djopt/miter.pcb
new file mode 100644
index 0000000..61db653
--- /dev/null
+++ b/doc-rnd/djopt/miter.pcb
@@ -0,0 +1,80 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[500.0 0 0 1]
+Cursor[500 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Attribute("PCB::grid::unit" "mil")
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 45000 15000 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 80000 47500 -4350 -5650 3 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+Layer(1 "component")
+(
+	Line[50905 15000 80000 15000 1000 4000 "clearline"]
+	Line[80000 15000 80000 41595 1000 4000 "clearline"]
+	Line[81181 41595 80000 41595 1000 4000 "clearline"]
+	Line[80000 41595 78819 41595 1000 4000 "clearline"]
+	Line[50905 16181 50905 15000 1000 4000 "clearline"]
+	Line[50905 15000 50905 13819 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/miter.png b/doc-rnd/djopt/miter.png
new file mode 100644
index 0000000..ad60253
Binary files /dev/null and b/doc-rnd/djopt/miter.png differ
diff --git a/doc-rnd/djopt/miter.txt b/doc-rnd/djopt/miter.txt
new file mode 100644
index 0000000..05de284
--- /dev/null
+++ b/doc-rnd/djopt/miter.txt
@@ -0,0 +1,2 @@
+Replaces 90 degree corners with a pair of 45 degree corners, to reduce
+RF losses and trace length.
diff --git a/doc-rnd/djopt/orthopull.out.pcb b/doc-rnd/djopt/orthopull.out.pcb
new file mode 100644
index 0000000..fca1b58
--- /dev/null
+++ b/doc-rnd/djopt/orthopull.out.pcb
@@ -0,0 +1,99 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[47500 32500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 52500 10000 4350 5650 1 100 ""]
+(
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [3740 -2362 3740 2362 800]
+	ElementLine [-3740 -2362 -3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R103" "1206" 72500 10000 4350 5650 1 100 ""]
+(
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [3740 -2362 3740 2362 800]
+	ElementLine [-3740 -2362 -3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R104" "1206" 62500 52500 -4350 -5650 3 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 35000 10000 -4350 -5650 3 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+Layer(1 "component")
+(
+	Line[52500 15905 52500 22500 1000 4000 "clearline"]
+	Line[72500 15905 72500 22500 1000 4000 "clearline"]
+	Line[62500 46595 62500 22500 1000 4000 "clearline"]
+	Line[35000 22500 52500 22500 1000 4000 "clearline"]
+	Line[35000 22500 35000 15905 1000 4000 "clearline"]
+	Line[62500 22500 72500 22500 1000 4000 "clearline"]
+	Line[52500 22500 62500 22500 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+		Connect("R103-2")
+		Connect("R104-2")
+	)
+)
diff --git a/doc-rnd/djopt/orthopull.out.png b/doc-rnd/djopt/orthopull.out.png
new file mode 100644
index 0000000..412187a
Binary files /dev/null and b/doc-rnd/djopt/orthopull.out.png differ
diff --git a/doc-rnd/djopt/orthopull.pcb b/doc-rnd/djopt/orthopull.pcb
new file mode 100644
index 0000000..286fb32
--- /dev/null
+++ b/doc-rnd/djopt/orthopull.pcb
@@ -0,0 +1,98 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[47500 32500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Attribute("PCB::grid::unit" "mm")
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 52500 10000 4350 5650 1 100 ""]
+(
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [3740 -2362 3740 2362 800]
+	ElementLine [-3740 -2362 -3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R103" "1206" 72500 10000 4350 5650 1 100 ""]
+(
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [3740 -2362 3740 2362 800]
+	ElementLine [-3740 -2362 -3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R104" "1206" 62500 52500 -4350 -5650 3 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 35000 10000 -4350 -5650 3 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+Layer(1 "component")
+(
+	Line[52500 15905 52500 37500 1000 4000 "clearline"]
+	Line[72500 15905 72500 37500 1000 4000 "clearline"]
+	Line[62500 46595 62500 37500 1000 4000 "clearline"]
+	Line[35000 37500 72500 37500 1000 4000 "clearline"]
+	Line[35000 37500 35000 15905 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+		Connect("R103-2")
+		Connect("R104-2")
+	)
+)
diff --git a/doc-rnd/djopt/orthopull.png b/doc-rnd/djopt/orthopull.png
new file mode 100644
index 0000000..1c927be
Binary files /dev/null and b/doc-rnd/djopt/orthopull.png differ
diff --git a/doc-rnd/djopt/orthopull.txt b/doc-rnd/djopt/orthopull.txt
new file mode 100644
index 0000000..6ee4615
--- /dev/null
+++ b/doc-rnd/djopt/orthopull.txt
@@ -0,0 +1,4 @@
+Looks for chains of traces all going in one direction, with more
+traces orthogonal on one side than on the other.  Moves the chain in
+that direction, causing a net reduction in trace length, possibly
+eliminating traces and/or corners.
diff --git a/doc-rnd/djopt/unjaggy.out.pcb b/doc-rnd/djopt/unjaggy.out.pcb
new file mode 100644
index 0000000..7a39a14
--- /dev/null
+++ b/doc-rnd/djopt/unjaggy.out.pcb
@@ -0,0 +1,79 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[12500 10000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 17500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 65000 12500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[23405 32500 37500 32500 1000 4000 "clearline"]
+	Line[37500 32500 37500 15000 1000 4000 "clearline"]
+	Line[37500 15000 37500 15000 1000 4000 "clearline"]
+	Line[37500 32500 37500 32500 1000 4000 "clearline"]
+	Line[37500 15000 45000 15000 1000 4000 "clearline"]
+	Line[45000 15000 45000 10000 1000 4000 "clearline"]
+	Line[45000 10000 59095 10000 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/unjaggy.out.png b/doc-rnd/djopt/unjaggy.out.png
new file mode 100644
index 0000000..6819f9a
Binary files /dev/null and b/doc-rnd/djopt/unjaggy.out.png differ
diff --git a/doc-rnd/djopt/unjaggy.pcb b/doc-rnd/djopt/unjaggy.pcb
new file mode 100644
index 0000000..229212f
--- /dev/null
+++ b/doc-rnd/djopt/unjaggy.pcb
@@ -0,0 +1,81 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[12500 10000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Attribute("PCB::grid::unit" "mm")
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 17500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 65000 12500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[23405 32500 30000 32500 1000 4000 "clearline"]
+	Line[30000 22500 37500 22500 1000 4000 "clearline"]
+	Line[37500 22500 37500 15000 1000 4000 "clearline"]
+	Line[30000 32500 30000 22500 1000 4000 "clearline"]
+	Line[37500 15000 45000 15000 1000 4000 "clearline"]
+	Line[45000 15000 45000 10000 1000 4000 "clearline"]
+	Line[45000 10000 56595 10000 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/unjaggy.png b/doc-rnd/djopt/unjaggy.png
new file mode 100644
index 0000000..4154377
Binary files /dev/null and b/doc-rnd/djopt/unjaggy.png differ
diff --git a/doc-rnd/djopt/unjaggy.txt b/doc-rnd/djopt/unjaggy.txt
new file mode 100644
index 0000000..a873864
--- /dev/null
+++ b/doc-rnd/djopt/unjaggy.txt
@@ -0,0 +1,2 @@
+Looks for corners which could be flipped to eliminate one or more
+corners (i.e. jaggy lines become simpler).
diff --git a/doc-rnd/djopt/vianudge.out.pcb b/doc-rnd/djopt/vianudge.out.pcb
new file mode 100644
index 0000000..14ff8be
--- /dev/null
+++ b/doc-rnd/djopt/vianudge.out.pcb
@@ -0,0 +1,79 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[17500 22500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Via[85000 27500 8661 4000 0 3937 "" ""]
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 57500 10000 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["onsolder" "Standard SMT resistor, capacitor etc" "R102" "1206" 10000 10000 5650 4350 2 100 "auto"]
+(
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "1" "1" "onsolder,square"]
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "2" "2" "onsolder,square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[63405 10000 85000 10000 1000 4000 "clearline"]
+	Line[85000 10000 85000 27500 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[15905 10000 30000 10000 2000 4000 "clearline"]
+	Line[30000 10000 30000 27500 2000 4000 "clearline"]
+	Line[30000 27500 85000 27500 2000 4000 "clearline"]
+	Line[85000 27500 85000 27500 2000 4000 "clearline"]
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/vianudge.out.png b/doc-rnd/djopt/vianudge.out.png
new file mode 100644
index 0000000..96fe0f4
Binary files /dev/null and b/doc-rnd/djopt/vianudge.out.png differ
diff --git a/doc-rnd/djopt/vianudge.pcb b/doc-rnd/djopt/vianudge.pcb
new file mode 100644
index 0000000..141cef5
--- /dev/null
+++ b/doc-rnd/djopt/vianudge.pcb
@@ -0,0 +1,81 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[17500 22500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Attribute("PCB::grid::unit" "mm")
+Via[85000 50000 8661 4000 0 3937 "" ""]
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 57500 10000 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["onsolder" "Standard SMT resistor, capacitor etc" "R102" "1206" 10000 10000 5650 4350 2 100 "auto"]
+(
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "1" "1" "onsolder,square"]
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "2" "2" "onsolder,square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[63405 10000 85000 10000 1000 4000 "clearline"]
+	Line[85000 10000 85000 50000 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[15905 10000 30000 10000 2000 4000 "clearline"]
+	Line[30000 10000 30000 27500 2000 4000 "clearline"]
+	Line[30000 27500 85000 27500 2000 4000 "clearline"]
+	Line[85000 50000 85000 27500 2000 4000 "clearline"]
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/vianudge.png b/doc-rnd/djopt/vianudge.png
new file mode 100644
index 0000000..66748cc
Binary files /dev/null and b/doc-rnd/djopt/vianudge.png differ
diff --git a/doc-rnd/djopt/vianudge.txt b/doc-rnd/djopt/vianudge.txt
new file mode 100644
index 0000000..6d298ab
--- /dev/null
+++ b/doc-rnd/djopt/vianudge.txt
@@ -0,0 +1,3 @@
+Looks for vias where all traces leave in the same direction.  Tries to
+move via in that direction to eliminate one of the traces (and thus a
+corner).
diff --git a/doc-rnd/djopt/viatrim.out.pcb b/doc-rnd/djopt/viatrim.out.pcb
new file mode 100644
index 0000000..02368f0
--- /dev/null
+++ b/doc-rnd/djopt/viatrim.out.pcb
@@ -0,0 +1,80 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[37500 10000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Via[60000 32500 7874 4000 0 3150 "" ""]
+Via[92500 32500 7874 4000 0 3150 "" ""]
+Via[75000 32500 7874 4000 0 3150 "" ""]
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 17500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 117500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[23405 32500 60000 32500 1000 4000 "clearline"]
+	Line[92500 32500 111595 32500 1000 4000 "clearline"]
+	Line[60000 32500 60000 32500 1000 4000 "clearline"]
+	Line[60000 32500 75000 32500 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[75000 32500 92500 32500 1000 4000 "clearline"]
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/viatrim.out.png b/doc-rnd/djopt/viatrim.out.png
new file mode 100644
index 0000000..18ae77d
Binary files /dev/null and b/doc-rnd/djopt/viatrim.out.png differ
diff --git a/doc-rnd/djopt/viatrim.pcb b/doc-rnd/djopt/viatrim.pcb
new file mode 100644
index 0000000..dfd1470
--- /dev/null
+++ b/doc-rnd/djopt/viatrim.pcb
@@ -0,0 +1,87 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 140000 67500]
+
+Grid[2500.0 0 0 1]
+Cursor[37500 10000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+
+Attribute("PCB::grid::unit" "mm")
+Via[60000 32500 7874 4000 0 3150 "" ""]
+Via[92500 32500 7874 4000 0 3150 "" ""]
+Via[75000 32500 7874 4000 0 3150 "" ""]
+Via[40000 32500 7874 4000 0 3150 "" ""]
+
+Element["" "Standard SMT resistor, capacitor etc" "R101" "1206" 17500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R102" "1206" 117500 32500 -5650 4350 0 100 ""]
+(
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2362 3740 2362 3740 800]
+	ElementLine [-2362 -3740 2362 -3740 800]
+
+	)
+Layer(1 "component")
+(
+	Line[23405 32500 40000 32500 1000 4000 "clearline"]
+	Line[92500 32500 111595 32500 1000 4000 "clearline"]
+	Line[111595 33681 111595 32500 1000 4000 "clearline"]
+	Line[111595 32500 111595 31319 1000 4000 "clearline"]
+	Line[23405 33681 23405 32500 1000 4000 "clearline"]
+	Line[23405 32500 23405 31319 1000 4000 "clearline"]
+	Line[40000 32500 60000 32500 1000 4000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[60000 32500 75000 32500 1000 4000 "clearline"]
+	Line[75000 32500 92500 32500 1000 4000 "clearline"]
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+NetList()
+(
+	Net("GND" "(unknown)")
+	(
+		Connect("R101-1")
+		Connect("R102-2")
+	)
+)
diff --git a/doc-rnd/djopt/viatrim.png b/doc-rnd/djopt/viatrim.png
new file mode 100644
index 0000000..7080a16
Binary files /dev/null and b/doc-rnd/djopt/viatrim.png differ
diff --git a/doc-rnd/djopt/viatrim.txt b/doc-rnd/djopt/viatrim.txt
new file mode 100644
index 0000000..a259f99
--- /dev/null
+++ b/doc-rnd/djopt/viatrim.txt
@@ -0,0 +1,2 @@
+Looks for traces that go from via to via, where moving that trace to a
+different layer eliminates one or both vias.
diff --git a/doc-rnd/features/ba.html b/doc-rnd/features/ba.html
new file mode 100644
index 0000000..f8b00ad
--- /dev/null
+++ b/doc-rnd/features/ba.html
@@ -0,0 +1,68 @@
+<html>
+<head>
+	<title> pcb-rnd - back annotation </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [ba]  patches </h1>
+Back annotation was a long standing missing functionality. There has been
+different suggestions, including:
+<ul>
+	<li> back annotation is not needed at all, the user can do it manually
+	<li> pcb should generate a new netlist, the user should run diff on the netlists and manually edit the schematics
+	<li> gschem should be able to load netlists emitted by PCB; or gnetlist could reverse-process such a netlist
+</ul>
+<p>
+The current implementation allows pcb-rnd users to make deliberate pin
+swapping and footprint replacement. The changes can be saved in a
+"netlist patch" format. A modified gschem can load these and present them
+as search results pointing to parts of the schematics that need to be changed.
+There is a <a href="https://archive.org/details/pcb-rnd-back-annotation">
+demo video </a> about how this happens in practice.
+<p>
+The modified gschem can be checked out from svn://repo.hu/geda-gaf-ba/trunk
+<p>
+The underlying mechanism is versatile and potentially allows more changes
+to be back annotated. These are not yet accessible due to the lack of PCB
+actions and GUI.
+
+<h2> Design decisions </h2>
+This <a href="devlog/20150830b_back_ann.html"> devlog </a> entry functions
+as a design decision document.
+<a href="devlog/20150901a_back_ann.html"> Another entry </a> is a summary
+on how the feature ended up in a private feature-fork of gschem.
+
+<h2> save/load and compatibility </h2>
+Deliberate changes are tracked so that a new forward annotation from
+the old schematics won't break them. To do this, pcb-rnd saves a
+NetListPatch() section in the file. Mainline PCB can not handle this section.
+Compatibility, by use case:
+<ul>
+	<li> 1. No back annotation is made in pcb-rnd: the file <b>stays compatible</b>
+	<li> 2. Back annotation is made in pcb-rnd, then the changes are done on the schematics and a forward annotation is also done: the netlist patch in pcb-rnd becomes empty so the pcb file is <b>again compatible</b> with mainline pcb; this is called resolution of netlist patches
+	<li> 3. There are unresolved netlist patches saved in pcb-rnd and the user attempts to load the pcb file in mainline pcb: <b>syntax error</b> (but no data loss); solution: resolve the netlist patch as described in point 2.
+	<li> 4. Cheat for situation 3.: manually remove the NetListPatch() section from the save file. This way the back annotation info is lost, but mainline pcb can load the file again. This action is sort of "revert back annotation".
+	<li> 5. Any file saved by mainline PCB can be opened by pcb-rnd, back annotation does not affect that.
+
+</ul>
+
+
+<h2> plans </h2>
+Back annotation for further changes, e.g. value change, naming/renaming nets,
+etc.
+</body>
+</html>
diff --git a/doc-rnd/features/cycdrag.html b/doc-rnd/features/cycdrag.html
new file mode 100644
index 0000000..cca9844
--- /dev/null
+++ b/doc-rnd/features/cycdrag.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+	<title> pcb-rnd - [cycdrag] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [cycdrag]  patches </h1>
+
+A long standing misfeature of pcb (and pcb-rnd) has been that when dragging the
+end of connected traces, pcb chosen one of the traces "randomly". It often
+didn't pick the one the user wanted to move. The workaround was to move the
+one that pcb picked and then return and move the target trace then
+move the other trace back. This gets even more annoying if there are more than
+two objects connected in the given point: 3 traces and a via for example.
+<p>
+The cycdrag patch addresses this issue by defining an action that can cycle
+through objects that could be dragged in the given point while the left mouse
+button is pressed. This lets the user explicitly select the one object to
+work on.
+<p>
+This <a href="https://archive.org/details/pcb-rnd-cycdrag"> demo video </a>
+demonstrates how it works with three lines and a via.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+It does not work with the lesstif HID. It does not work with the rubber band
+mode.
+
+</body>
+</html>
diff --git a/doc-rnd/features/debian.html b/doc-rnd/features/debian.html
new file mode 100644
index 0000000..7c8881e
--- /dev/null
+++ b/doc-rnd/features/debian.html
@@ -0,0 +1,50 @@
+<html>
+<head>
+	<title> pcb-rnd - [debian] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [debian] patch </h1>
+
+Our current Debian packaging reflects the modularity of pcb-rnd: it has
+most plugins in separate packages. The main package is called
+<b>pcb-rnd-core</b>: this provides the executable and the footprint library
+and the batch HID. The GUI HIDs are in <b>pcb-rnd-gtk</b> and
+<b>pcb-rnd-lesstif</b> - it's possible to install neither, either or both.
+When there are multiple HIDs installed, the user can select one by the
+--gui command line parameter or by changing the GUI preference in the
+configuration (the default preference is gtk > lesstif > batch).
+<p>
+The rest of the packages are features, importers and exporters, e.g.
+<b>pcb-rnd-svg</b> is the SVG exporter, <b>pcb-rnd-query</b> is the object
+query language needed for the advanced search.
+<p>
+A metapackage called <b>pcb-rnd</b> is provided for convenience: it installs
+the packages for the most common, yet small setup, with the GTK HID.
+<p>
+How to get the packages:
+<ul>
+	<li> decide which packages you need; this <a href="debian_list.html"> package list </a> may help
+	<li> use <i>apt-get install</i> after configuring <a href="http://repo.hu/debian"> repo.hu's debian repository </a> as source in your /etc/apt/sources.list
+	<li> use <i>dpkg -i</i> after manually downloading the packages from repo.hu's <a href="http://repo.hu/debian/list.html"> flat package list </a> or from <a href="http://repo.hu/debian/pool/main/p/pcb-rnd"> the package pool </a>
+	<li> build them on your Debian box: use svn trunk, install debhelper and all the build dependencies of pcb-rnd and run "fakeroot debian/rules binary" in trunk (shorthand: "make deb")
+</ul>
+
+<h2> plans </h2>
+No plans - this feature is fully implemented.
+</body>
+</html>
diff --git a/doc-rnd/features/debian_list.html b/doc-rnd/features/debian_list.html
new file mode 100644
index 0000000..138a53f
--- /dev/null
+++ b/doc-rnd/features/debian_list.html
@@ -0,0 +1,64 @@
+
+<html>
+<!-- THIS FILE IS GENERATED BY deblist.sh, DO NOT EDIT -->
+<head>
+	<title> pcb-rnd - Debian package list </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - Debian package list </h1>
+<table border=1 cellspacing=0>
+<tr><th>name <th> internal dependencies <th> description
+
+<tr><td><b>pcb-rnd</b><td> pcb-rnd-core, pcb-rnd-gtk, pcb-rnd-gerber, pcb-rnd-lpr, pcb-rnd-png, pcb-rnd-propedit, pcb-rnd-query, pcb-rnd-report, pcb-rnd-svg, pcb-rnd-xy<td>  pcb-rnd is load/save compatible with gEDA/PCB and kicad and offers various  auxiliary import/export formats.<p>  This metapackage installs the core and the most commonly used set of plugins. 
+<tr><td><b>pcb-rnd-core</b><td> <td>  This package contains the core program. Most functionality, including the  GUI frontend, is in the corresponding plugin package. 
+<tr><td><b>pcb-rnd-gtk</b><td>  pcb-rnd-core<td>  This package contains the GTK+ user-interface for pcb-rnd. 
+<tr><td><b>pcb-rnd-lesstif</b><td>  pcb-rnd-core<td>  This package contains the Lesstif/Motif user-interface for pcb-rnd. 
+<tr><td><b>pcb-rnd-autocrop</b><td>  pcb-rnd-core<td>  This package provides the autocrop feature that can resize the  board to minimum around all the existing objects. 
+<tr><td><b>pcb-rnd-autoplace</b><td>  pcb-rnd-core<td>  This package provides the autoplace feature used to place elements  automatically and offers two different algorithms to do so. 
+<tr><td><b>pcb-rnd-autoroute</b><td>  pcb-rnd-core<td>  This package provides the classic gEDA/pcb autorouter. 
+<tr><td><b>pcb-rnd-diag</b><td>  pcb-rnd-core<td>  This package provides actions for debugging/diagnostic purposes. This  feature may be needed for reporting bugs. 
+<tr><td><b>pcb-rnd-distalign</b><td>  pcb-rnd-core<td>  This package provides actions for arranging elements in an array. 
+<tr><td><b>pcb-rnd-distaligntext</b><td>  pcb-rnd-core<td>  This package provides actions for arranging text objects in an array. 
+<tr><td><b>pcb-rnd-djopt</b><td>  pcb-rnd-core<td>  This package provides actions to clean up (optimize) tracks.  It is especially useful after autorouting. 
+<tr><td><b>pcb-rnd-gcode</b><td>  pcb-rnd-core<td>  This package allows exporting the design to gcode, useful for CNC  milling. 
+<tr><td><b>pcb-rnd-gerber</b><td>  pcb-rnd-core<td>  This package allows exporting the design to gerber, accepted  by most PCB fab houses. 
+<tr><td><b>pcb-rnd-lpr</b><td>  pcb-rnd-core<td>  This package allows printing directly from the GUI, using lpr. 
+<tr><td><b>pcb-rnd-nelma</b><td>  pcb-rnd-core<td>  This package allows exporting the design to nelma, for numerical  capacitance calculation (via external tool) 
+<tr><td><b>pcb-rnd-png</b><td>  pcb-rnd-core<td>  This package allows exporting the design in various bitmap formats,  including png and jpeg. 
+<tr><td><b>pcb-rnd-svg</b><td>  pcb-rnd-core<td>  This package allows exporting the design in SVG, the Scalable Vector  Graphics format. It is useful for publishing the design on the web. 
+<tr><td><b>pcb-rnd-xy</b><td>  pcb-rnd-core<td>  This package allows exporting the design in XY, suitable for  pick&place machines. 
+<tr><td><b>pcb-rnd-fontmode</b><td>  pcb-rnd-core<td>  This package lets the user to turn the GUI into a PCB font editor. 
+<tr><td><b>pcb-rnd-fp-wget</b><td>  pcb-rnd-core<td>  This package provides a wget based footprint accessor, which allows  integrating web directories of footprints in the library. The  only currently supported site is gedasymbols.org. 
+<tr><td><b>pcb-rnd-edif</b><td>  pcb-rnd-core<td>  This package allows pcb-rnd to import netlists in the EDIF format. 
+<tr><td><b>pcb-rnd-mincut</b><td>  pcb-rnd-core<td>  This package upgrades the indication of short circuits: instead of  highlighting two random pins/pads, it tries to determine the minimal-cut  that would resolve the short. In practice this means it makes a suggestion  where to cut the networks to remove the short circuit. 
+<tr><td><b>pcb-rnd-oldactions</b><td>  pcb-rnd-core<td>  This package provides some old user actions. These are not used  daily anymore, but some old actions scripts may still depend on them. 
+<tr><td><b>pcb-rnd-polycombine</b><td>  pcb-rnd-core<td>  This package provides actions to combine (merge) multiple polygons  into a single, more complex polygon. 
+<tr><td><b>pcb-rnd-propedit</b><td>  pcb-rnd-core<td>  This package provides the property editor function for the GUI. The  property editor collects all properties and attributes of all selected  objects in a table and allows the user to change them. 
+<tr><td><b>pcb-rnd-puller</b><td>  pcb-rnd-core<td>  This package provides actions to "pull" tracks, minimizing their length. 
+<tr><td><b>pcb-rnd-query</b><td>  pcb-rnd-core<td>  This package provides the query language used in the advanced search on  the GUI. 
+<tr><td><b>pcb-rnd-renumber</b><td>  pcb-rnd-core<td>  This package provides various actions to automatically renumber elements  on the design. These actions change the refdes of some (or all) elements. 
+<tr><td><b>pcb-rnd-report</b><td>  pcb-rnd-core<td>  This package provides the report action that displays basic information  about design objects. Unlike propedit, report does not allow the user  to manipulate any of the data reported. 
+<tr><td><b>pcb-rnd-shand-cmd</b><td>  pcb-rnd-core<td>  This package provides 1..2 character long shorthands for the most  commonly used core commands. 
+<tr><td><b>pcb-rnd-smartdisperse</b><td>  pcb-rnd-core<td>  This package implements a smart algorithm to disperse elements. It is  useful after importing a new design from the schematics. It is an  alternative to autplace. 
+<tr><td><b>pcb-rnd-stroke</b><td>  pcb-rnd-core, pcb-rnd-gtk<td>  This package implements mouse gestures using libstroke. 
+<tr><td><b>pcb-rnd-teardrops</b><td>  pcb-rnd-core<td>  This package embeds each pin in a teardrop drawn from multiple arcs.  May be useful for toner-transfer boards. 
+<tr><td><b>pcb-rnd-vendordrill</b><td>  pcb-rnd-core<td>  This package provides vendor drill mapping and vendor specific design  rule application. 
+<tr><td><b>pcb-rnd-gsch2pcb</b><td> <td>  This package provides the external tool gsch2pcb-rnd that can convert  a gschem schematics to files that can be imported in pcb-rnd.   
+
+</table>
+
diff --git a/doc-rnd/features/dynstyle.html b/doc-rnd/features/dynstyle.html
new file mode 100644
index 0000000..377d160
--- /dev/null
+++ b/doc-rnd/features/dynstyle.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+	<title> pcb-rnd - [dynstyle] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [dynstyle] patch </h1>
+
+Pcb-rnd doesn't have a hardwired limit on number of routing styles
+anymore. Routing styles can be created on the fly and are saved to and loaded
+from .pcb files.
+<p>
+There's no theoretical maximum explicitly set either. An implicit maximum
+exists and is sizeof(int) - should be at least 2^31 on most systems.
+
+<h2> save/load and compatibility </h2>
+The first 4 styles are loaded and preserved by mainline. Styles above
+4 would probably be deleted in a mainline load-save cycle. The number 4
+is a constant value that can be changed if mainline is recompiled.
+
+<h2> plans </h2>
+GUI HID representation of styles (especially in menus) need more testing.
+</body>
+</html>
diff --git a/doc-rnd/features/flagcomp.html b/doc-rnd/features/flagcomp.html
new file mode 100644
index 0000000..3e9b02f
--- /dev/null
+++ b/doc-rnd/features/flagcomp.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+	<title> pcb-rnd - [flagcomp] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [flagcomp] patch </h1>
+
+Many of the patches in this fork, and in possible future branches/forks
+may introduce new pin, pad or element flags. PCB loads and saves flags
+by converting the text representation to an in-memory binary format. Any
+flag not understood is lost.
+<p>
+This patch adds a linked list of string flags, filled in with the unknown
+flags while loading a PCB. On save, these flags are appended to the normal
+flag list. This preserves all unknown flags (but not order of flags) in
+a load/save cycle.
+
+<h2> save/load and compatibility </h2>
+This patch ensures compatibility in save/load cycles with flags introduced
+by later versions of mainline PCB or different branches/forks of PCB by
+not removing flags they introduced.
+
+<h2> plans </h2>
+No plans - this feature is fully implemented.
+</body>
+</html>
diff --git a/doc-rnd/features/fp_wget.html b/doc-rnd/features/fp_wget.html
new file mode 100644
index 0000000..6b8d007
--- /dev/null
+++ b/doc-rnd/features/fp_wget.html
@@ -0,0 +1,55 @@
+<html>
+<head>
+	<title> pcb-rnd - [fp_wget] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [fp_wget] patch </h1>
+
+Since version 1.0.10, pcb-rnd implements a new footprint mechanism (see
+<a href="pcblib.html">[fp_fs]</a> and <a href="library_t.html">[library_t]</a>).
+The new code allows footprint backend plugins to get library from anywhere.
+The [fp_wget] plugin is an implementation that:
+<ul>
+	<li> downloads a library list from the web on startup into a local cache
+	<li> downloads footprints from the web on-demand into a local cache
+</ul>
+<p>
+This is all transparent, the user experience is that the remote library is
+like a read-only local library reachable from the library window. 
+<p>
+A web site used as a library should be able to:
+<ul>
+	<li> generate a plain text list of all footprints available
+	<li> return the raw footprint file by name
+</ul>
+<p>
+The plugin uses external program <i>wget</i> to communicate on the web.
+
+
+<h3> How to configure for gedasymbols.org </h3>
+Add wget at gedasymbols in the library search path (e.g. in preferences as library-newlib).
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Better feedback on progress, explicit user requested refresh, refresh in
+the background.
+
+</body>
+</html>
diff --git a/doc-rnd/features/fullscreen.html b/doc-rnd/features/fullscreen.html
new file mode 100644
index 0000000..5f5ea73
--- /dev/null
+++ b/doc-rnd/features/fullscreen.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+	<title> pcb-rnd - full screen gtk </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - full screen gtk </h1>
+On small screen the overhead of the menu line, bottom status line and left
+layer selection bar is just too expensive. Since 1.1.2, the gtk hid can
+be switched between the usual setup and a so called "full screen mode"
+where most widgets are hidden, leaving much more screen space to the
+editor widget. There's a FullScreen() action that can set or toggle
+the state and a default key binding in gtk on the backslash ('\') to
+toggle it.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Make hiding various parts of the GUI configurable. No plans to implement
+it for the lesstif HID.
+
+</body>
+</html>
diff --git a/doc-rnd/features/gpmi.html b/doc-rnd/features/gpmi.html
new file mode 100644
index 0000000..88624f5
--- /dev/null
+++ b/doc-rnd/features/gpmi.html
@@ -0,0 +1,59 @@
+<html>
+<head>
+	<title> pcb-rnd - [gpmi] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [gpmi] patch </h1>
+
+Thanks to gpmi, pcb-rnd is scriptable in about 10 scripting languages (e.g.
+lua, awk, ruby, python, scheme, tcl). Scripts are integrated in pcb-rnd and
+have access to most of the internals. Scripts
+are able to:
+	<ul>
+		<li> register new actions
+		<li> create new menus and submenus
+		<li> execute existing actions
+		<li> pop up simple dialog boxes (message, report, progress bar, file selection) -- <a href="https://archive.org/details/Pcb-rndCarcScript"> check out the video </a>
+		<li> build and pop up custom dialog boxes (so called <i>attribute dialogs</i>)
+		<li> search for objects (lines, arc, polys, vias, etc.) on the layout
+		<li> change and move existing objects on the layout
+		<li> create new objects on the layout
+		<li> change "page" properties (dimensions of the board)
+		<li> debug draw on the gui (slightly broken on gtk due to some gtk hid bugs)
+	</ul>
+</ul>
+<p>
+This feature has three options:
+<ul>
+	<li> <i>disabled</i>: not compiled at all - when gpmi is not installed (no gpmi scripting in PCB)
+	<li> <i>buildin</i>: compiled and linked in the executable - pcb-rnd always can load and run scripts
+	<li> <i>plugin</i>: compiled as a loadable plugin - pcb-rnd can load and run scripts if the plugin is installed
+</ul>
+
+<h3> Example </h3>
+Check out <a href="../gpmi/rosetta/index.html">the Rosetta stone </a> of
+pcb-rnd.
+
+<h2> save/load and compatibility </h2>
+Save/load files are not affected.
+
+<h2> plans </h2>
+Expose more internals, write more example scripts and documentation.
+
+</body>
+</html>
diff --git a/doc-rnd/features/grid.html b/doc-rnd/features/grid.html
new file mode 100644
index 0000000..1e44e40
--- /dev/null
+++ b/doc-rnd/features/grid.html
@@ -0,0 +1,99 @@
+<html>
+<head>
+	<title> pcb-rnd - gtk grid fixes </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - gtk grid fixes </h1>
+
+<h2> Problem with the original gtk grid </h2>
+
+The original code draws every grid point on screen, even the ones that are
+off the board. When the user zooms out with a dense grid, it can become too
+dense - the original code turns the grid off in this case.
+<p>
+If the grid is very dense, on a large screen the software render may slow down.
+This is noticeable in pan and zoom mostly. The bottleneck is the gtk call
+that draws the grid points: if there are too many of them, it's slow.
+
+
+<h2> New grid </h2>
+
+To overcome this problem, pcb-rnd offers configuration settings to limit
+the number of dots to be drawn. There are mainly two parallel approaches:
+using a <i>local grid</i> or draw less dots in a <i>global grid</i>. The
+old code defined only a global grid and did not have runtime settings for
+the properties of the grid.
+<p>
+There's a new "Grid properties" submenu in the view menu in the default
+gtk hid menu file. Screenshots were taken with this menu teared-down to
+demonstrate the settings.
+
+<h3> Global grid improvements </h3>
+
+The most trivial optimization is that grid points off the board are simply
+not drawn:
+<p>
+<img src="grid_edge.png">
+<p>
+Next, the compile-time configurable "minimum distance between grid points
+before it is too dense" setting is user configurable now. The default value
+can be changed in any of the usual
+<a href="../conf/sources.html">configuration sources</a>. However,
+when the configured density is reached while zooming out, there are two options.
+<p>
+The first, default option is to do the same that the original code did: just
+hide the grid:
+<p>
+<img src="grid_global_nosparse.png">
+<p>
+An alternative is to use <i>sparse global grid</i> which means only
+every 2nd, 3rd, 4th, ... Nth grid point is drawn. The cursor still snaps
+to every real grid point, so the grid got sparse only on the display. Note
+how the line is drawn on invisible grid points:
+<p>
+<img src="grid_global_sparse.png">
+
+<h3> Introduction of the local grid </h3>
+Another approach is to use a local grid. The grid helps finding whether
+existing objects are aligned or where the next move/click would end up.
+These are usually interesting only in a small range around the crosshair and
+less interesting on the other end of the board. Local grid draws the grid
+points only near the crosshair. Such local grid follows the crosshair
+everywhere:
+<p>
+<img src="grid_local_4.png">
+<p>
+The user can configure or interactively set the radius in which the grid
+points are drawn:
+<p>
+<img src="grid_local_16.png">
+<p>
+The radius is given in "number of grid points", thus the local grid yields
+a constant number of points that is independent of the actual grid size. When
+a local grid gets too dense, it is hidden - there's no sparse option, since
+local grids usually have too few points to make reasonable skips.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+This feature is gtk-specific and is complete. There are no plans to
+implement this in the lesstif HID at the moment.
+
+</body>
+</html>
diff --git a/doc-rnd/features/grid_edge.png b/doc-rnd/features/grid_edge.png
new file mode 100644
index 0000000..e364fa4
Binary files /dev/null and b/doc-rnd/features/grid_edge.png differ
diff --git a/doc-rnd/features/grid_global_nosparse.png b/doc-rnd/features/grid_global_nosparse.png
new file mode 100644
index 0000000..9b3a06b
Binary files /dev/null and b/doc-rnd/features/grid_global_nosparse.png differ
diff --git a/doc-rnd/features/grid_global_sparse.png b/doc-rnd/features/grid_global_sparse.png
new file mode 100644
index 0000000..af3ead9
Binary files /dev/null and b/doc-rnd/features/grid_global_sparse.png differ
diff --git a/doc-rnd/features/grid_local_16.png b/doc-rnd/features/grid_local_16.png
new file mode 100644
index 0000000..83110ae
Binary files /dev/null and b/doc-rnd/features/grid_local_16.png differ
diff --git a/doc-rnd/features/grid_local_4.png b/doc-rnd/features/grid_local_4.png
new file mode 100644
index 0000000..94092d4
Binary files /dev/null and b/doc-rnd/features/grid_local_4.png differ
diff --git a/doc-rnd/features/index.html b/doc-rnd/features/index.html
new file mode 100644
index 0000000..8073ff4
--- /dev/null
+++ b/doc-rnd/features/index.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+	<title> pcb-rnd - features </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd </h1>
+<h2> Change summary, per topic </H2>
+<table border=1 cellspacing=0 cellpadding=5>
+<tr><th bgcolor=#eeeeee>commit message tag and doc<th bgcolor=#eeeeee>description
+<tr><td bgcolor=#ccccff><a href="gpmi.html">[gpmi]</a>             <td bgcolor=#ddddff> scripting PCB (including GUI dialogs, actions, menus, changing the layout)
+<tr><td bgcolor=#ccccff><a href="intconn.html">[intconn]</a>       <td bgcolor=#ddddff> component internal connections
+<tr><td bgcolor=#ccccff><a href="nonetlist.html">[nonetlist]</a>   <td bgcolor=#ddddff> components that are not part of the netlist and should not cause shorts
+<tr><td bgcolor=#ccccff><a href="tostyle.html">[tostyle]</a>       <td bgcolor=#ddddff> actions, menu and hotkey to change ring dia, line width, drill dia and clearance sizes to match the values defined for the current routing style
+<tr><td bgcolor=#ccccff><a href="mincut.html">[mincut]</a>         <td bgcolor=#ddddff> minimal cut based warnings on shorts
+<tr><td bgcolor=#ccccff><a href="square.html">[square]</a>         <td bgcolor=#ddddff> change square pad to a generic shaped-pin based on the octagon pin code - this is an alternative to teardrops
+<tr><td bgcolor=#ccccff><a href="flagcomp.html">[flagcomp]</a>     <td bgcolor=#ddddff> unknown flag compatibility
+<tr><td bgcolor=#ccccff><a href="scconfig.html">[scconfig]</a>     <td bgcolor=#ddddff> use scconfig instead of autotools
+<tr><td bgcolor=#ccccff><a href="pcb-fp.html">[pcb-fp]</a>         <td bgcolor=#ddddff> generic parametric footprints; on-the-fly footprint generation by external tools written in any language (remove m4 hardwirings)
+<tr><td bgcolor=#ccccff><a href="pcblib.html">[pcblib], [fp_fs],
+                                       [pcblib-param]</a>          <td bgcolor=#ddddff> clean up the footprint library shipped
+<tr><td bgcolor=#ccccff><a href="library_t.html">[library_t]</a>   <td bgcolor=#ddddff> footprint library is an arbitrary tree instead of a special, 2 level tree
+<tr><td bgcolor=#ccccff><a href="fp_wget.html">[fp_wget]</a>       <td bgcolor=#ddddff> web based footprint libraries, integration of gedasymbols.org
+<tr><td bgcolor=#ccccff><a href="res.html">[res]</a>               <td bgcolor=#ddddff> replace resource files with lihata and enable multi-key hotkeys in both gtk and lesstif hids
+<tr><td bgcolor=#ccccff><a href="debian.html">[debian]</a>         <td bgcolor=#ddddff> Debian packaging the binaries configured to my own taste
+<tr><td bgcolor=#ccccff><a href="ba.html">[ba]</a>                 <td bgcolor=#ddddff> back annotation
+<tr><td bgcolor=#ccccff><a href="onpoint.html">[onpoint]</a>       <td bgcolor=#ddddff> on-point by Robert Drehmel
+<tr><td bgcolor=#ccccff><a href="cycdrag.html">[cycdrag]</a>       <td bgcolor=#ddddff> cycle drag; with additional feature: <a href="negselect.html"> negative box select </a>
+<tr><td bgcolor=#ccccff><a href="../mods3/">[mods]</a>             <td bgcolor=#ddddff> modularize the code to reduce core size - for comparison, previous stats: <a href="../mods/">1.0.8</a>, <a href="../mods2/">1.0.9</a>
+<tr><td bgcolor=#ccccff><a href="unglib.html">[unglib]</a>         <td bgcolor=#ddddff> remove glib dependency from core
+<tr><td bgcolor=#ccccff><a href="io.html">[io_*]</a>               <td bgcolor=#ddddff> .pcb and .fp file format plugins
+<tr><td bgcolor=#ccccff><a href="dynstyle.html">[dynstyle]</a>     <td bgcolor=#ddddff> dynamic routuing style: sypport more than 4 of them - with no limit
+<tr><td bgcolor=#ccccff><a href="../conf/index.html">[conf]</a>    <td bgcolor=#ddddff> new, unified, config file system
+<tr><td bgcolor=#ccccff><a href="propedit.html">[propedit]</a>     <td bgcolor=#ddddff> property/attribute editor (gtk)
+<tr><td bgcolor=#ccccff><a href="oldplugins.html">[oldplugins]</a> <td bgcolor=#ddddff> import old PCB plugins
+<tr><td bgcolor=#ccccff><a href="query.html">[query]</a>           <td bgcolor=#ddddff> query language
+<tr><td bgcolor=#ccccff><a href="routings.html">routing styles</a> <td bgcolor=#ddddff> routing style fixes
+<tr><td bgcolor=#ccccff><a href="grid.html">(gtk grid)</a>         <td bgcolor=#ddddff> gtk grid improvements: sparse global grids, local grids
+<tr><td bgcolor=#ccccff><a href="fullscreen.html">(full screen)</a><td bgcolor=#ddddff> gtk full screen edit mode
+<tr><td bgcolor=#ccccff><a href="settings.html">(settings)</a>     <td bgcolor=#ddddff> minor changes in default settings
+
+
+<!--<tr><td><a href="polygrid.html">[polygrid]</a>   <td> ps output: an option to draw grids in polygons instead of filling them -->
+</table>
+
+</body>
+</html>
diff --git a/doc-rnd/features/intconn.html b/doc-rnd/features/intconn.html
new file mode 100644
index 0000000..d163f56
--- /dev/null
+++ b/doc-rnd/features/intconn.html
@@ -0,0 +1,90 @@
+<html>
+<head>
+	<title> pcb-rnd - [intconn] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [intconn] patch </h1>
+
+There are parts with internal connections (e.g. pin 2 and 4 of a SO8
+package are internally connected). Mainline PCB can not handle this,
+leaving the following options:
+<ul>
+	<li> connect both pins to the net from the schematics - this works if all the internally connected pins are required to connect to copper (common with GND or power pins) but is very inconvenient for signal pins where only one of them needs to be connected
+	<li> back-annotate which pin is connected - there's no easy back annotation
+	<li> one pin connected, the other is closer to the next target; PCB doesn't understand that they are already connected internally; normally one shouldn't use the internal connection of a component instead of copper; except for the common practice to use 0 ohm SMD resistors for jumping wires
+</ul>
+<p>
+The patch introduces a new pin flag <i>intconn(g)</i> which marks the pin
+to have internal connections in group <i>g</i>. If there
+are multiple pins using the same <i>g</i> value within a single element, they
+are internally connected. In other words, <i>g</i> is a group (or net name)
+within the element and pins can join to one of the numbered groups (or internal
+nets). The value of <i>g</i> shall be between 1 and 255, 0 means no internal
+connection (equivalent to the case when intconn(0) is omitted).
+<p>
+When pin numbers are displayed (key 'd'), internal connection groups are
+written in square brackets, e.g. "2 [9]" means "pin 2, internally connected
+to group 9".
+<p>
+Combined with the [<a href="nonetlist.html">nonetlist</a>] patch, this
+solves the "0-ohm 1206 jumper" problem: the element should be marked
+as nonetlist, with both pins set intconn(1) - this will result in a 2
+pad element, pads internally connected, that can be part of any one network
+without causing short.
+<h2> Example </h2>
+The first image depicts crossing traces, a common problem encountered when rats
+nesting a new layout from a netlist. One method to resolve such issues is to
+use a zero ohm jumper resistor that allows one signal trace to 'jump' across
+another.
+<p>
+The second image shows the layout routing the nonconflicting rats and a open
+unrouted point where the rat would require one trace to cross another.
+<p>
+In the third image a 1206 SMD footprint for a 0 Ohm 1206 resistor called J1 is
+placed with an intconn between the two pads which resolves the final rat line.
+<p>
+<img src="intconn1.png">
+<p>
+<img src="intconn2.png">
+<p>
+<img src="intconn3.png">
+<p>
+
+
+
+<h2> save/load and compatibility </h2>
+This patch introduces a new pin flag. In the following example
+pin 2 and 4 are connected internally as group 9, while pin 3
+does not have any internal connections:
+<pre>
+Pin[40000 60000 6000 3000 6600 2800 "2" "2" "square,intconn(9)"]
+Pin[40000 50000 6000 3000 6600 2800 "3" "3" "square"]
+Pin[40000 40000 6000 3000 6600 2800 "4" "4" "square,intconn(9)"]
+</pre>
+Mainline PCB will load the design ignoring internal connections -
+this may introduce new rats.
+<p>
+Mainline PCB doesn't save intconn() and elements are embedded in the file -
+once the design is loaded and saved with mainline PCB, internal connection
+info is lost.
+
+<h2> plans </h2>
+No plans - this feature is fully implemented. There is no plan for implementing
+a GUI, internal connections should be hand-edited into the element.
+</body>
+</html>
diff --git a/doc-rnd/features/intconn1.png b/doc-rnd/features/intconn1.png
new file mode 100644
index 0000000..9b3dbc1
Binary files /dev/null and b/doc-rnd/features/intconn1.png differ
diff --git a/doc-rnd/features/intconn2.png b/doc-rnd/features/intconn2.png
new file mode 100644
index 0000000..0d6c248
Binary files /dev/null and b/doc-rnd/features/intconn2.png differ
diff --git a/doc-rnd/features/intconn3.png b/doc-rnd/features/intconn3.png
new file mode 100644
index 0000000..ebf5857
Binary files /dev/null and b/doc-rnd/features/intconn3.png differ
diff --git a/doc-rnd/features/io.html b/doc-rnd/features/io.html
new file mode 100644
index 0000000..5772e17
--- /dev/null
+++ b/doc-rnd/features/io.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+	<title> pcb-rnd - [io_*] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [io_*]  patches </h1>
+
+Mainline PCB core was coupled with the file format; the [io] patch set is
+an effort to decouple any file design I/O from core. The original file format
+(plain text .pcb and .fp) lives on as plugin called io_pcb.
+
+<p>
+TODO
+
+<h2> save/load and compatibility </h2>
+Not affected when io_pcb is used and .pcb or .fp files are loaded or saved.
+<p>
+Using other io_* implementations will obviously result in files that are
+incompatible with mainline pcb (unless mainline pcb learns how to load those
+formats).
+
+<h2> plans </h2>
+The project is still in an early phase.
+
+</body>
+</html>
diff --git a/doc-rnd/features/jumper_1206.fp b/doc-rnd/features/jumper_1206.fp
new file mode 100644
index 0000000..cc37d7d
--- /dev/null
+++ b/doc-rnd/features/jumper_1206.fp
@@ -0,0 +1,7 @@
+Element["nonetlist" "1206 jumper, 0 ohm" "" "1206" 0 0 -3150 -3150 0 100 ""]
+(
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "1" "1" "square,intconn(1)"]
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "2" "2" "square,intconn(1)"]
+	ElementLine[-2362 -3740 2362 -3740 800]
+	ElementLine[-2362 3740 2362 3740 800]
+)
diff --git a/doc-rnd/features/library_t.html b/doc-rnd/features/library_t.html
new file mode 100644
index 0000000..30e7341
--- /dev/null
+++ b/doc-rnd/features/library_t.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+	<title> pcb-rnd - [library_t] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [library_t] patch </h1>
+
+The original code has a special setup for representing trees, C structures
+called LibraryMenu and LibraryEntry. This system can represent only a subset
+of trees: there is a root, a level consist of directories only and a next level,
+each directory consist of data nodes only. This has been enough for newlib,
+which strictly follows this model in the file system hierarchy. The lesstif
+HID also hardwired this model in the GUI.
+<p>
+In pcb-rnd this has been replaced with a new struct type called library_t
+that can represent an arbitrary tree: directories and files within directories
+down to many levels.
+<p>
+Both the gtk and the lesstif had has been modified accordingly and can
+properly display the tree. This in turn enables alternative footprint backend
+implementations such as <a href="fp_wget.html"> fp_wget <a> to import
+more complex libraries, e.g. the one on gedasymbols.org.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Finished, no plans.
+
+</body>
+</html>
diff --git a/doc-rnd/features/mincut.html b/doc-rnd/features/mincut.html
new file mode 100644
index 0000000..ac9b7b3
--- /dev/null
+++ b/doc-rnd/features/mincut.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+	<title> pcb-rnd - [mincut] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [mincut] patch </h1>
+
+The original code was highlighting pins/pads only when a short came around
+after rats nest optimization. This was not very helpful on a complex board.
+There had been a long discussion on the mailing list about the best solutions.
+There were a few very good ideas, including:
+<ul>
+	<li> manual tagging of objects (lines, polys, arcs, vias) with net and warn
+	     where two differently tagged net connects
+	<li> automatic tagging based on "where it was connected first", then the same
+	     warning mechanism as above
+	<li> trace history (using the undo buffer?) and go back until when it was
+	     not broken, check what exactly broke it
+	<li> history combined with tagging
+	<li> calculate minimal cut
+</ul>
+<p>
+I choose minimal cut for my patch because it doesn't require tracing the
+full history or any manual administration of nets vs. objects (which I
+would find inevitable even with manual tagging - directly or indirectly
+the user needs to be able to change net tags).
+<p>
+The minimal cut is the least amount of object whose removal would resolve
+the short. It is best demonstrated on an example:
+<p>
+<img src="mincut.png">
+<p>
+Removing all the marked lines/polys/vias would surely resolve the short
+(sometimes leaving rat lines behind). Minimal cut is better than randomly
+removing objects, tho: it guarantees that the minimal amount of objects
+are to be removed. On a complex board, this place is likely to be close
+to the place where the problem really is - much closer than the pins/pads.
+<p>
+Since mincut can be expensive on large boards, the feature can be enabled
+per board (a new PCB flag) and can be disbaled globally (--enable-mincut 0
+when starting PCB).
+
+<h2> save/load and compatibility </h2>
+New PCB flag enablemincut. Mainline pcb ignores this flag but does not
+preserve it.
+
+<h2> plans </h2>
+Finished, no plans.
+</body>
+</html>
diff --git a/doc-rnd/features/mincut.png b/doc-rnd/features/mincut.png
new file mode 100644
index 0000000..d8948aa
Binary files /dev/null and b/doc-rnd/features/mincut.png differ
diff --git a/doc-rnd/features/negselect.html b/doc-rnd/features/negselect.html
new file mode 100644
index 0000000..2daf76f
--- /dev/null
+++ b/doc-rnd/features/negselect.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+	<title> pcb-rnd - negative box select </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [cycdrag] patches, negative box select </h1>
+
+When a selection is made using drag&drop (box selection), depending
+on the direction the rule for object selection differ:
+<ul>
+	<li> drag from top-right towards bottom-left (positive sized box):
+	     select objects that are fully contained in the box (original behaviour)
+	<li> drag from bottom-left towards top-right (negative sized box):
+	     select objects that are even partially within the box or merely touch
+	     the edge of the box
+</ul>
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Work more on some rough corners of the negative direction: e.g. pads are
+handled as 0 width lines so the selection has to hit the center.
+
+</body>
+</html>
diff --git a/doc-rnd/features/nonetlist.html b/doc-rnd/features/nonetlist.html
new file mode 100644
index 0000000..b3e9ada
--- /dev/null
+++ b/doc-rnd/features/nonetlist.html
@@ -0,0 +1,71 @@
+<html>
+<head>
+	<title> pcb-rnd - [nonetlist] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [nonetlist] patch </h1>
+
+The [nonetlist] patch adds an element flag that makes PCB ignore the marked
+element when dealing with netlists. This means connecting a net to a pin of
+a nonetlist element will not cause a short. The refdes of a nonetlist
+part is drawn with color element-color-nonetlist (ElementColor_nonetlist
+in the source; default value #777777, grey).
+<p>
+Uses of the nonetlist feature:
+<ul>
+	<li> smd jumper: Combined with the [<a href="intconn.html">intconn</a>] patch, this
+	     solves the "0-ohm 1206 jumper" problem: the element should be marked
+	     as nonetlist, with both pins set intconn(1) - this will result in a 2
+	     pad element, pads internally connected, that can be part of any one network
+	     without causing short and passing the DRC.
+	<li> mechanical parts that should not show up on the schematics:
+		<ul>
+			<li> mounting hole elements: single pin holes with silk marking the head of the screw
+			<li> chassis (often with mounting holes or keep-out areas)
+			<li> logos in footprints (no poly support in footprint - yet?)
+			<li> mechanical parts which do not have solderable pins (plastic spacers, extra connector shields)
+			<li> "spare connectors", e.g. unused/disconnected DIP sockets, pin grids, or connectors in the corner of the PCB for prototyping (dev-board style)
+		</ul>
+</ul>
+<p>
+Preservation: gsch2pcb-rnd leaves nonetlist elements in the PCB.
+<h2> save/load and compatibility </h2>
+This patch introduces a new element flag. The following example demonstrates
+a <a href="jumper_1206.fp">1206 jumper footprint</a>:
+<pre>
+Element["nonetlist" "1206 jumper, 0 ohm" "" "1206" 0 0 -3150 -3150 0 100 ""]
+(
+	Pad[-5905 -1181 -5905 1181 5118 2000 5718 "1" "1" "square,intconn(1)"]
+	Pad[5905 -1181 5905 1181 5118 2000 5718 "2" "2" "square,intconn(1)"]
+	ElementLine[-2362 -3740 2362 -3740 800]
+	ElementLine[-2362 3740 2362 3740 800]
+)
+</pre>
+Mainline PCB will load the design ignoring internal connections and nonetlist
+flag - this will cause shorts on all connected pins/pads and will break
+the connection.
+<p>
+Mainline PCB doesn't save nonetlist and elements are embedded in the file -
+once the design is loaded and saved with mainline PCB, the flag is lost.
+After reloading the file in pcb-rnd, the element causes the same shorts
+as in mainline PCB.
+
+<h2> plans </h2>
+No plans, the feature is complete.
+</body>
+</html>
diff --git a/doc-rnd/features/oldplugins.html b/doc-rnd/features/oldplugins.html
new file mode 100644
index 0000000..2b1ba6d
--- /dev/null
+++ b/doc-rnd/features/oldplugins.html
@@ -0,0 +1,69 @@
+<html>
+<head>
+	<title> pcb-rnd - [oldplugins] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [oldplugins] </h1>
+
+In this effort I imported a set of old PCB plugins to be core plugins of
+pcb-rnd. This will make it easier to keep them up to date, to defeat
+the bitrot effect. The feature plugins that got imported so far:
+<ul>
+	<li> autocrop
+	<li> boardflip
+	<li> distalign
+	<li> distaligntext
+	<li> jostle - doesn't work properly
+	<li> polycombine
+	<li> polystich - segfaults
+	<li> teardrops
+	<li> renumberblock - as part of the renumber plugin
+	<li> smaprtdisperse
+</ul>
+<p>
+The import/export plugins improted over the default set of the last official
+mainline release:
+<ul>
+	<li> import_dsn - specctra importer - compiles, parser needs a full rewrite, it tries to read an s-expression line by line with fgets()
+	<li> export_dsn - specctra exporter - compiles, produces valid-looking output - <b>need tester</b> to validate the output
+	<li> export_bboard - breadboard exporter - compiles, produces valid-looking output - <b>need tester</b>
+	<li> export_dxf - mechanical cad export - compiles, missing header data renders exported files unusable
+	<li> export_ipcd356 - electrical test output - compiles, produces valid-looking output - <b>need tester</b>
+	<li> export_openscad - 3d modeling - compiles, output sort of works, models not yet imported, <b>need tester</b>
+</ul>
+
+Plugins that won't be imported:
+<ul>
+	<li> findelement - <a href="query.html">[query]</a> takes care of that
+	<li> findrat - <a href="query.html">[query]</a> will take care of that
+	<li> join-found - not clear what it would do
+	<li> lockelements - <a href="query.html">[query]</a> can select elements then SetFlag() should be able to lock them
+	<li> sedrename - <a href="query.html">[query]</a> will take care of that
+	<li> upth2pth - <a href="query.html">[query]</a> will take care of that
+	<li> ratsel - written in C++
+	<li> stipple - written in C++
+</ul>
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Any interesting plugin is subject to be imported.
+
+</body>
+</html>
diff --git a/doc-rnd/features/onpoint.html b/doc-rnd/features/onpoint.html
new file mode 100644
index 0000000..b1f722c
--- /dev/null
+++ b/doc-rnd/features/onpoint.html
@@ -0,0 +1,68 @@
+<html>
+<head>
+	<title> pcb-rnd - [onpoint] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [onpoint]  patches </h1>
+
+Robert Drehmel writes:
+<pre>
+When (e.g.) routing 5mm power traces on a small grid, it's not always easy to hit the
+point where the trace ended (which is the center of the semicircle at the end of the
+line) to start the next line.  If you have selected the line tool, finding the end of
+the line can become guesswork as the cursor doesn't change shape like it does with the
+select tool.
+I want my traces to consist of lines and arcs that are perfectly connected and I want
+to work as fast as possible.
+
+Attached is a small patch that
+
+  - makes it possible to deactivate snapping to "some sensible point along a line".
+    (that's what a comment in the code says). This snapping algorithm gets in the way
+    sometimes so you have to slowly go over a line to find out where it really ends,
+    bouncing back and forth between the points of the small grid, the end of the line
+    and these "sensible points", which is wasting time. The command is
+    "Display(ToggleSnapOffGridLine)". It is still activated by default to avoid
+    violating POLA.
+
+  - more importantly, introduces a new command called "Display(ToggleHighlightOnPoint)"
+    that highlights all lines and arcs which have (end)points exactly on the position
+    where the cross hair is currently snapped to. It therefore helps finding the end
+    points of lines and arcs, but sometimes also shows redundant traces, traces that
+    aren't perfectly connected to each other, traces that don't end directly on the
+    center of a via but should, etc. It works with thin draw too and I tested it with
+    gtk and lesstif.
+
+I use the second option mostly in conjunction with deactivating the first. Both commands
+have been added to the menu by means of (g)pcb-menu.res.in and are available as command
+line options as well.
+Caveats:
+  - The HID API expects all HIDs to make a copy of the color string when setting a color.
+  - The function that lightens up a color could be improved.
+  - I used it for a while, but after porting it from my local fork, it probably needs
+    more testing.
+</pre>
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+The feature is complete.
+
+</body>
+</html>
diff --git a/doc-rnd/features/pcb-fp.html b/doc-rnd/features/pcb-fp.html
new file mode 100644
index 0000000..e1892ce
--- /dev/null
+++ b/doc-rnd/features/pcb-fp.html
@@ -0,0 +1,54 @@
+<html>
+<head>
+	<title> pcb-rnd - [pcb-fp] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [pcb-fp] patch </h1>
+
+Pcb-fp is an effort to clean up the footprint situation:
+<ul>
+	<li> replace lib and newlib with pcblib, a library that tries to provide common footprints only
+	<li> clear the syntax: if a footprint name contains parenthesis, it's generated (parametric footprint), else it's the name of a static footprint file
+	<li> parametric footprints: replace m4 with a generic, language-independent footprint generator framework
+	<ul>
+		<li> implement libpcb_fp, which centralizes searching and loading footprints
+		<li> fork gsch2pcb to gsch2pcb-rnd that uses libpcb_fp (and does not have any m4 references hardwired)
+		<li> fork gnet_gsch2pcb.scm (the gnetlist backend) to remove m4 heuristics
+	</ul>
+</ul>
+
+<h3> Example </h3>
+Intaractive parametric footprint selection in pcb-rnd:
+<p>
+<img src="pcb-fp.png">
+<p>
+An <a href="http://igor2.repo.hu/cgi-bin/pcblib-param.cgi"> online footprint generator web1.0 version</a> is also available.
+
+<h2> save/load and compatibility </h2>
+Save/load files are not affected. If a schematics is written for the new
+library and depends on parametric footprints:
+<ul>
+	<li> mainline gsch2pcb won't find those footprints
+	<li> mainline pcb won't show those footprints in the footprint selection dialog
+</ul>
+
+<h2> plans </h2>
+No plans - this feature is fully implemented.
+
+</body>
+</html>
diff --git a/doc-rnd/features/pcb-fp.png b/doc-rnd/features/pcb-fp.png
new file mode 100644
index 0000000..57ce9d8
Binary files /dev/null and b/doc-rnd/features/pcb-fp.png differ
diff --git a/doc-rnd/features/pcblib.html b/doc-rnd/features/pcblib.html
new file mode 100644
index 0000000..8e244c7
--- /dev/null
+++ b/doc-rnd/features/pcblib.html
@@ -0,0 +1,74 @@
+<html>
+<head>
+	<title> pcb-rnd - [pcblib] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [pcblib] and [pcblib-param] and [fp_fs] patches </h1>
+<table border=0>
+<tr><td valign=top>
+The footprint library shipped with mainline pcb is cluttered with
+special puprose parts. I believe PCB encourages the user from
+an early stage to build his own library. Thus the purpose of
+the library shipped with PCB should be
+to provide a minimal collection of real essential footprints ...
+<ul>
+	<li> ... for the very beginning of the learning curve;
+	<li> ... and to be the core of the user's own library later.
+</ul>
+<p>
+[pcblib] is a replacement of newlib/ and lib/ and the m4 macros with
+such an essential core library of static footprints ("file elements")
+and easier-to-use parametric footprints.
+<p>
+There is an <a href="http://igor2.repo.hu/tmp/pcblib"> online map</a> of
+the library and <a href="http://igor2.repo.hu/cgi-bin/pcblib-param.cgi">
+an online interface to the parametric footprint generators. </a>
+
+<h2> Design decisions </h2>
+Parts are sorted only in a few directories: smd, tru-hole, connector and
+parametric. I believe there are so many orthogonal properties of footpritns
+that there's no obvious hierarchy. Also, pcblib contains much fewer footpritns
+than newlib so it should be still easy to navigate.
+<p>
+Parametric footprints are in a separate directory for now, even tho they
+would fit under smd, tru-hole or connector. The reason is purely historical
+and the layout may change in the future.
+
+<h2> Example </h2>
+To the right: Footprint selection dialog on pcblib, with the smd directory
+open. Note how few smd parts are there. Still, smd/ is the most crowded
+subdirectory!
+
+<h2> [fp_fs] </h2>
+As of vesion 1.0.10, the footprint list/search/load of footprints is a plugin.
+The original code that handles local file system footprint libraries (e.g.
+pcblib or newlib) is now a plugin. Altnerative plugins can be provided that work
+from databases or <a href="fp_wget.html"> from the web</a>. In extreme
+situations the file system based footprint plugin can even be disabled.
+
+<h2> save/load and compatibility </h2>
+Not affected: elements are embedded in the PCB.
+
+<h2> plans </h2>
+None, the feature is complete.
+<td>            
+<td>
+<img src="pcblib.png">
+</table>
+</body>
+</html>
diff --git a/doc-rnd/features/pcblib.png b/doc-rnd/features/pcblib.png
new file mode 100644
index 0000000..a65bf27
Binary files /dev/null and b/doc-rnd/features/pcblib.png differ
diff --git a/doc-rnd/features/polygrid.html b/doc-rnd/features/polygrid.html
new file mode 100644
index 0000000..39dcfdb
--- /dev/null
+++ b/doc-rnd/features/polygrid.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+	<title> pcb-rnd - [polygrid] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [polygrid] patch </h1>
+
+Polygrid adds an option to the ps exporter to fill polygons with a grid
+of horizontal and vertical lines instead of full fill. When toner transfer
+is used or test prints are produced, the grid may save some toner.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Fix bugs.
+<p>
+No (feature) plans - this feature is fully implemented.
+</body>
+</html>
diff --git a/doc-rnd/features/propedit.html b/doc-rnd/features/propedit.html
new file mode 100644
index 0000000..da827fd
--- /dev/null
+++ b/doc-rnd/features/propedit.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+	<title> pcb-rnd - [propedit] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - [propedit] </h1>
+Since 1.1.2, pcb-rnd core is able to attach user defined <i>attributes</i>
+(arbitrary textual key=value pairs) to any object. The original mainline
+pcb-rnd was forked from already supported attributes on some objects (e.g.
+board attributes, net attributes) but not on all. However, the user had
+very little access to the attributes - no GUI or action would handle them.
+<p>
+Propedit introduces a property editor window in gtk that is able to
+edit attributes and core properties of all selected objects. Core properties
+include all hardwired properties of objects, such as geometry (e.g.
+trace width, hole diameter, clearance) or textual data (e.g. string of
+a text).
+<p>
+<img src="propedit.png">
+<p>
+Properties and attributes of the selected objects are collected in a
+sorted list - each row of the list is a property (starting with p/) or
+an attribute (starting with a/). For each row all values seen in the selection
+are also collected so that the following values can be presented on the list,
+per row:
+<ul>
+	<li> the most common value
+	<li> minimum value (for numeric value types)
+	<li> maximum value (for numeric value types)
+	<li> average value (for numeric value types)
+</ul>
+<p>
+When the user clicks on a row, an edit box is activated and the value can
+be changed. A combo box lists all existing values for the given row, so
+it is easy to unify the value of a property or attribute among all selected
+objects to one of the existing values, but the user is also free to enter
+a new value.
+<p>
+It is also possible to remove existing attributes or to add new attributes.
+
+<h2> save/load and compatibility </h2>
+Attributes of most objects can not be saved in the original .pcb format.
+The propedit feature is most useful when used with the lihata board format.
+
+<h2> plans </h2>
+Various improvements. Currently there's no plan to make this function available
+in the lesstif HID.
+
+</body>
+</html>
diff --git a/doc-rnd/features/propedit.png b/doc-rnd/features/propedit.png
new file mode 100644
index 0000000..cc03690
Binary files /dev/null and b/doc-rnd/features/propedit.png differ
diff --git a/doc-rnd/features/query.html b/doc-rnd/features/query.html
new file mode 100644
index 0000000..6c3beb8
--- /dev/null
+++ b/doc-rnd/features/query.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+	<title> pcb-rnd - [query] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - [query] </h1>
+Pcb-rnd features a flexible query language that can list (or select or find, etc.)
+objects mathcing an expression. The language handles different data types,
+including lists, provides means to iterate over objects and lists, supports
+the common logical and arithmetic operators.
+<p>
+The query language is the engine behind the advanced search & select
+functionality. It is also the foundation of the programmable DRC.
+<p>
+The query language is implemented as a core plugin.
+<p>
+TODO: more details will come later, when the specification and implementation
+stabilizes.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Next stage will be a programmable DRC.
+
+</body>
+</html>
diff --git a/doc-rnd/features/res.html b/doc-rnd/features/res.html
new file mode 100644
index 0000000..81c8bd5
--- /dev/null
+++ b/doc-rnd/features/res.html
@@ -0,0 +1,147 @@
+<html>
+<head>
+	<title> pcb-rnd - [res] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [res] patch </h1>
+
+PCB used to have an own file format for describing resources (menu structure
+and hotkey bindings, vendor drill mapping). The resource format was generic
+enough to describe these things, but the syntax was somewhat wierd: mixed
+positional and named fields. More precisely, composite nodes could contain
+named and anonymous subnodes, and the meaning of anonymous subnodes depended
+on their position in the anon-subnode-list.
+<p>
+The code that dealt with the in-memory representation of the resource tree
+was wierd and big chunks duplicated in the HIDs and vendor drill module. It
+was also hard to parse a resource file with external tools.
+<p>
+Since version 1.0.10, pcb-rnd replaces resource files with 
+<a href="http://repo.hu/projects/lihata">lihata</a>. Lihata is a small, generic
+purpose container format that can describe arbitrary trees. Just like resource
+file syntax, lihata is optimized for hand-editing: no need to use excess
+quoting or long boilerplate blocks.
+<p>
+Liblihata also provides a lot of helper functions that made the code
+dealing with the menus and vendor drill resources much simpler and less
+redundant. Since the parser is small and external, and since there are
+external converter tools available, it is also easier to deal with the
+files outside of the pcb executable.
+
+<h2> menu files </h2>
+There are pcb-menu-gtk.lht and pcb-menu-lesstif.lht. They are in trunk/src
+in the source tree and are instaslled in the SHAREDIR. Currently each GUI
+HID (lesstif, gtk) loads the corresponding menu file.
+
+<h2> menu resource lihata structure </h2>
+The root of a menu resource file should be a <i>ha:</i> with the following
+children:
+<ul>
+	<li> <i>li:mouse</i> for mouse button bindings
+	<li> <i>li:main_menu</i> for describing the main menu
+	<li> <i>li:popups</i> for describing the popup menus
+</ul>
+All children are optional, but recommended. Thus the file stucture, zoomed
+out, is:
+<pre>
+ha:{
+	li:mouse { ... }
+	li:main_menu { ... }
+	li:popups { ... }
+}
+</pre>
+
+<h3> li:mouse </h3>
+The mouse subtree may contain a <i>li:</i> for each mouse button action;
+the children of the list are further <i>li:</i> nodes for key modifiers, whose
+children are text nodes: actions executed in order.
+<p>
+Buttons supported are: left, right, middle, up, down  - the last two
+are for the scroll wheel. Modifier name should start with "press" or "release"
+optionally followed by modifier key suffixes separated with dashes, e.g.
+"press-alt-shift" means the given button is pressed while alt and shift
+were also pressed.
+<p>
+Example structure:
+<pre>
+	li:mouse {
+		li:left {
+			li:press            = { Mode(Notify) }
+			li:press-ctrl       = { Mode(Save); Mode(None); }
+		}
+	}
+</pre>
+
+<h3> li:main_menu </h3>
+The main menu is a list of menubar items that may host submenu items
+recursively. Each normal item is a hash with the following children:
+<ul>
+	<li> <i>li:submenu</i> an ordered list of submenu nodes (should not have accel key or action)
+	<li> <i>tip</i> tooltip text
+	<li> <i>action</i> text or list of actions to execute when menu is selected
+	<li> <i>a</i> a key description for an accelerator key (hotkey)
+	<li> <i>li:a</i> a list of key descriptions for an accelerator keys (hotkeys); all keys will be bound to the menu and the first key is shown in the menu
+</ul>
+Special menu items are text nodes instead of hashes; they are:
+<ul>
+	<li> starting with @, are dynamic, auto-generated items (e.g. layers; might be HID-dependent)
+	<li> a singel dash: separator
+</ul>
+<p>
+A key description is a text in the form of:
+<ul>
+	<li> the name of the node is the visible name of the menu item
+	<li> <key>keyname, e.g. "<key>k" for key K, or "<key>F10" for F10
+	<li> modifier<key>keyname, e.g. "Alt-<key>K" for Alt+K
+	<li> modifier-modifier<key>keyname, e.g. "Shift-Alt-<key>K" for Shift+Alt+K; modifiers are Alt, Shift and Ctrl; order does not matter, all three can be used together.
+	<li> multikey sequence: multiple of the above, separated by semicolons (protected with {} for lihata, as the text contains semicolon); e.g. "{<key>f;<key>o}" means the user presses "f" then "o". Sequences can be a dozen stroke long and any segment may use modifiers
+</ul>
+
+An example menu item with submenus (can be a main menu or a submenu of
+another menu item):
+<pre>
+ha:example menu item {
+	li:submenu {
+		ha:menu item {
+			action=Save(ElementConnections)
+			tip=example menu
+		}
+		-
+		ha:another menu item {
+			a={Shift-Alt<key>r}
+			action={Action1(); Action2();}
+		}
+	}
+}
+</pre>
+
+<h3> li:popups </h3>
+Each children is a hash that describes a popup menu. A popup menu behaves
+exactly like a menu item, it should have a submenu list. Popup windows will
+be popped up by executing an action with the name of the popup menu.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+The resource file format conversion is done. There are other parts of the code
+that will probably get lihata instead of the current custom parsers, e.g.
+the preferences/settings file.
+
+</body>
+</html>
diff --git a/doc-rnd/features/routings.html b/doc-rnd/features/routings.html
new file mode 100644
index 0000000..c920bfa
--- /dev/null
+++ b/doc-rnd/features/routings.html
@@ -0,0 +1,97 @@
+<html>
+<head>
+	<title> pcb-rnd - routing style fixes </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - routing style fixes </h1>
+
+<h2> Number of styles </h2>
+In the original code there are a compile-time fixed number (4) of routing
+styles. This is often not enough for more complex designs. While in theory
+this limit can be raised, the resulting .pcb files will be incomatible with
+pcb compilations using a different number of styles.
+<p>
+Pcb-rnd uses a dynamic vector for storing styles and allows any number
+of styles from 0 up to a very large value (2^31-1). There is no compile-time
+configurable limit. The number of default styles (and the actual style
+configuration) is coming from the template pcb file.
+<p>
+Creating a new style in the Route Styles dialog is not "for this session only"
+anymore - styles are saved with the design.
+
+<h2> Explicit custom style </h2>
+Pcb had 4 explicit styles and a hidden, implicit style. All drawing
+action uses the hidden style. When the user selects one of the explicit
+styles, its properties are copied into the implicit style. Any drawing
+action uses the implicit style, this it's sort of the "pen style".
+<p>
+In mainline PCB the implicit (or pen) style is hidden. As long as it always
+matches one of the existing styles, the user doesn't even know about it.
+However, if there are objects that do not comform to any of the existing
+styles, it is possible to bump into this. For example:
+<ul>
+	<li> draw a line using one of the existing styles
+	<li> increase the width (with the 's' key) a few times - now the style of the line does not match any of the existing styles
+	<li> use the SetSame() action over the line (hover the pointer over the line and press 'a') - it will try to set the style that corresponds to the line
+	<li> since no style matches the line's, the radio button gets deselected - that's good
+	<li> now click on the Route Style button - it will show the properties of the last selected route style, even tho it is not selected at the moment
+</ul>
+<p>
+In contrast, pcb-rnd offers an explicit routing style called <i><custom></i>.
+If SetSame() is invoked on an object that doesn't match any of the existing styles,
+the <i><custom></i> style is selected:
+<ul>
+	<li> no style marked on the radio button (just like in mainline)
+	<li> the Route Style button opens with the <i><custom></i> style, with the values picked up from the object by SetSame
+	<li> the user can change the values, which will affect the implicit pen style
+	<li> this means the user can draw with the new settings, without having to create a permanent style
+</ul>
+<p>
+In other words, it is now possible to use the implicit pen style as temporal
+style, to explicitly set a line width, via diameter before placing a few
+unusual lines or vias, without having to create a new style. It is also
+possible to pick up the style of such an unusual object later, without the
+GUI confusing it with any of the existing styles.
+
+<h2> SetSame() bugs fixed </h2>
+A line does not have drill parameters. When mainline tries to pick up
+object properties for a line, it will pick thickness and clearance but
+will leave hole and ring diameters unchanged. This very often results in
+a mixed style: e.g. "signal" line properties picked up from a line while
+"power" hole/ring properties left over from the current style selection.
+Such a mixed pickup will result in the GUI get confused and not selecting any
+of the styles. The expected behaviour is to select the "signal" style if the
+line width/clearance matches the parameters of that style, and ignore the
+drill/ring parameters.
+<p>
+The same mixup happens for picking up arc parameters, and a similar mixup
+for via parameters (a via doesn't have a line width).
+<p>
+Pcb-rnd fixes this by searching for the matching style using only the parameters
+that the given object really had. This results in valid style selection
+the way the user may expect.
+
+<h2> save/load and compatibility </h2>
+Saving a design with number of styles not equal to 4 may cause problems
+when loading with mainline. The rest of these features do not affect
+compatibility.
+
+<h2> plans </h2>
+No plans - these features are fully implemented.
+</body>
+</html>
diff --git a/doc-rnd/features/scconfig.html b/doc-rnd/features/scconfig.html
new file mode 100644
index 0000000..93342dd
--- /dev/null
+++ b/doc-rnd/features/scconfig.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+	<title> pcb-rnd - [scconfig] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [scconfig] patch </h1>
+
+Pcb-rnd uses <a href="http://repo.hu/projects/scconfig">scconfig</a>
+for ./configure instead of autotools. Scconfig is smaller and easier
+to maintain.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+No plans - this feature is fully implemented by now.
+</body>
+</html>
diff --git a/doc-rnd/features/settings.html b/doc-rnd/features/settings.html
new file mode 100644
index 0000000..f46c082
--- /dev/null
+++ b/doc-rnd/features/settings.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+	<title> pcb-rnd - different default settings </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - settings </h1>
+
+There are a few minor changes in default settings compared to mainline
+pcb.
+<table border=1>
+<tr>
+	<th> name
+	<th> description
+	<th> where to change
+
+<tr>
+	<td> layers
+	<td> the default layer stack is optimized for two sided boards with 3 layers on both sides
+	<td> edit or replace /usr/share/pcb-rnd/default.pcb
+
+<tr>
+	<td> styles & DRC
+	<td> default styles and DRC settings are optimized for toner transfer
+	<td> edit or replace /usr/share/pcb-rnd/default.pcb
+
+<tr>
+	<td> grid
+	<td> on startup "enable visible grid" is on and grid is set to 25 mil
+	<td> change editor/draw_grid in pcb-conf.lht (in system install dir, in user dir) or in project.lht
+
+</table>
+
+</body>
+</html>
diff --git a/doc-rnd/features/square.html b/doc-rnd/features/square.html
new file mode 100644
index 0000000..b6699b3
--- /dev/null
+++ b/doc-rnd/features/square.html
@@ -0,0 +1,89 @@
+<html>
+<head>
+	<title> pcb-rnd - [square] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [square] patch </h1>
+Most of my PCBs end up in <i>toner transfer</i>. There are a lot
+of tricks around prototyping at home. One of the problems I often
+face is small rings peeling off during rework (and rework tend to
+happen on the first prototypes). The solution for this is increasing
+ring size - which is not suitable if traces are passing between pins.
+Another solution is to increase the area of the pin:
+<ul>
+	<li> square pin: while it makes the pin slightly larger still letting traces pass between pins, it's too symmetrical and can not use up extra room
+	<li> DJ's teardrop plugin: this increases the area only toward the trace, whereas very often there's more room on the opposite side
+	<li> manually add an extra poly around the pin; with multiple pins it's hard to get the polys separate
+	<li> manually add a short extra track on the pin, width matching the size of the pin; this is very close, but increases workload when components are moved, layers are changed
+</ul>
+<img src="square.png">
+<p>
+The patch takes an octagon pin and stretches points in various directions.
+There are 4 bits (for left, right, up and down) to indicate in which directions
+the stretch applies. Pressing 'q' on a pin cycles thru round pin, square pin,
+16 stretched octagons and the original octagon.
+<p>
+The code is also patched to handle clearances, shorts and connections (find.c).
+<p>
+Thermals are not fully working for funny shaped pins, but it has low priority:
+they still work fine for rounded and square pins and if there is a poly around
+the pin, I wouldn't use shaped pins anyway.
+
+<h2> save/load and compatibility </h2>
+This patch introduces a new pin flag called shape(n), where n is an integer
+selecting the shape of the pin when the square flag is also set:
+<pre>
+Pin[40000 60000 6000 3000 6600 2800 "8" "8" "square,shape(3)"]
+</pre>
+Mainline PCB will load the design ignoring the custom shape and will use a
+square pin. As long a traces end in the center point of the pin, this
+should not break connections.
+<p>
+Mainline PCB doesn't save shape() - once the design is loaded and saved with
+mainline PCB pin shape info is lost.
+
+<h2> plans </h2>
+In the original code there are separate code paths for round, octagonal and
+square pins. The separation repeats for at least:
+<ul>
+	<li> drawing the pin shape
+	<li> calculating the clearance
+	<li> checking whether things overlap or connect (pin vs pin, pin vs line, etc.)
+	<li> autorouter
+</ul>
+In most cases a set of hardwired constants are written in the C code. A notable
+exception was the octagon pin draw function, that had x and y offsets in a const
+table for 8 points and a loop to create the poly (or line segments in thin draw).
+<p>
+The [square] feature is a good base for cleaning up the code a bit and for
+moving toward a generic pin shape patch:
+<ul>
+	<li> hardwired octagon calculations should be replaced to use the same x;y const offset table
+	<li> the table should be replaced by a struct that describes length (number of points) - alternatively use POLYAREA
+	<li> square pin should be renamed to polygon pin
+	<li> octagon flag shall be removed - it should be a configuration of the x;y const table
+	<li> square flag shall be removed - it should be a configuration
+	<li> the only flag remaining should be shape(); if a pin is not shaped, it's round
+	<li> the pin shape struct should have a field for compatibility - to mark the entry corresponding to the original square and octagon pins so PCB-rnd format can be converted to and from the mainline PCB format.
+	<li> there should be a set of pin shapes in a default configuration table, including square and octagonal; these should be static
+	<li> the PCB file format should be extended with an option to expand the pin shape table with custom shapes
+	<li> UI: shapes could be imported (converted) from polys around vias
+	<li> UI: since there may be a lot of pin shapes, a GUI selector alternative shall be offered while also keeping the cycle-thru 'q' key
+</ul>
+</body>
+</html>
diff --git a/doc-rnd/features/square.pcb b/doc-rnd/features/square.pcb
new file mode 100644
index 0000000..b4a24b1
--- /dev/null
+++ b/doc-rnd/features/square.pcb
@@ -0,0 +1,893 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 130000 105000]
+
+Grid[2500.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1000 1000 1000 1000 1500 1000]
+Flags("nameonpcb,uniquename,clearnew")
+Groups("1,c:2,s:3:4:5:6:7:8")
+Styles["Signal,1500,6000,3150,2500:Power,3000,6000,3937,2500:Fat,8000,6000,3500,2500:Skinny,1200,2402,1181,2500"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+
+Element["" "Dual in-line package, medium wide (400 mil)" "" "DIP14M" 62500 22500 22000 5000 3 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 10000 6000 3000 6600 2800 "2" "2" ""]
+	Pin[0 20000 6000 3000 6600 2800 "3" "3" "thermal(1S)"]
+	Pin[0 30000 6000 3000 6600 2800 "4" "4" "square,shape(3)"]
+	Pin[0 40000 6000 3000 6600 2800 "5" "5" "square,shape(2)"]
+	Pin[0 50000 6000 3000 6600 2800 "6" "6" "square,shape(4)"]
+	Pin[0 60000 6000 3000 6600 2800 "7" "7" "square,shape(8)"]
+	Pin[40000 60000 6000 3000 6600 2800 "8" "8" "square,shape(8)"]
+	Pin[40000 50000 6000 3000 6600 2800 "9" "9" "square,shape(4)"]
+	Pin[40000 40000 6000 3000 6600 2800 "10" "10" "square,shape(2)"]
+	Pin[40000 30000 6000 3000 6600 2800 "11" "11" "square,shape(3)"]
+	Pin[40000 20000 6000 3000 6600 2800 "12" "12" ""]
+	Pin[40000 10000 6000 3000 6600 2800 "13" "13" ""]
+	Pin[40000 0 6000 3000 6600 2800 "14" "14" "square,shape(1)"]
+	ElementLine [-5000 -5000 -5000 65000 1000]
+	ElementLine [-5000 65000 45000 65000 1000]
+	ElementLine [45000 65000 45000 -5000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [25000 -5000 45000 -5000 1000]
+	ElementArc [20000 -5000 5000 5000 0 180 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+	Line[10000 17500 67500 17500 1500 5000 "clearline"]
+	Line[67500 17500 77500 27500 1500 5000 "clearline"]
+	Line[77500 27500 85000 27500 1500 5000 "clearline"]
+	Line[62500 22500 37500 22500 1500 5000 "clearline"]
+	Line[10000 27500 67500 27500 1500 5000 "clearline"]
+	Line[67500 27500 77500 37500 1500 5000 "clearline"]
+	Line[77500 37500 85000 37500 1500 5000 "clearline"]
+	Line[62500 32500 45000 32500 1500 5000 "clearline"]
+	Line[10000 37500 67500 37500 1500 5000 "clearline"]
+	Line[67500 37500 77500 47500 1500 5000 "clearline"]
+	Line[77500 47500 85000 47500 1500 5000 "clearline"]
+	Line[62500 42500 27500 42500 1500 5000 ""]
+	Line[10000 47500 67500 47500 1500 5000 "clearline"]
+	Line[67500 47500 77500 57500 1500 5000 "clearline"]
+	Line[77500 57500 85000 57500 1500 5000 "clearline"]
+	Line[62500 52500 37500 52500 1500 5000 "clearline"]
+	Line[37500 57500 67500 57500 1500 5000 "clearline"]
+	Line[67500 57500 77500 67500 1500 5000 "clearline"]
+	Line[77500 67500 85000 67500 1500 5000 "clearline"]
+	Line[62500 62500 37500 62500 1500 5000 "clearline"]
+	Line[37500 67500 67500 67500 1500 5000 "clearline"]
+	Line[67500 67500 77500 77500 1500 5000 "clearline"]
+	Line[77500 77500 85000 77500 1500 5000 "clearline"]
+	Line[62500 72500 37500 72500 1500 5000 "clearline"]
+	Line[37500 77500 67500 77500 1500 5000 "clearline"]
+	Line[67500 77500 77500 87500 1500 5000 "clearline"]
+	Line[77500 87500 85000 87500 1500 5000 "clearline"]
+	Line[62500 82500 37500 82500 1500 5000 "clearline"]
+	Line[65000 32500 60000 32500 6000 5000 "clearline"]
+	Line[105000 32500 100000 32500 6000 5000 "clearline"]
+	Line[102500 32500 125000 32500 1500 5000 "clearline"]
+	Line[102500 52500 125000 52500 1500 5000 "clearline"]
+	Line[102500 62500 125000 62500 1500 5000 "clearline"]
+	Line[102500 72500 125000 72500 1500 5000 "clearline"]
+	Line[102500 82500 125000 82500 1500 5000 "clearline"]
+	Line[102500 22500 125000 22500 1500 5000 "clearline"]
+	Polygon("clearpoly")
+	(
+		[55000 37500] [70000 37500] [70000 47500] [55000 47500] 
+	)
+	Polygon("clearpoly")
+	(
+		[85000 7500] [117500 7500] [117500 92500] [85000 92500] 
+	)
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "signal4")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Text[10000 17500 0 130 "square" "clearline"]
+	Text[10000 27500 0 130 "fat trace" "clearline"]
+	Text[10000 37500 0 130 "poly" "clearline"]
+	Text[25000 95000 1 130 "with [square]" "clearline"]
+	Text[15000 92500 1 130 "shaped pins" "clearline"]
+)
diff --git a/doc-rnd/features/square.png b/doc-rnd/features/square.png
new file mode 100644
index 0000000..0603803
Binary files /dev/null and b/doc-rnd/features/square.png differ
diff --git a/doc-rnd/features/tostyle.html b/doc-rnd/features/tostyle.html
new file mode 100644
index 0000000..8978416
--- /dev/null
+++ b/doc-rnd/features/tostyle.html
@@ -0,0 +1,68 @@
+<html>
+<head>
+	<title> pcb-rnd - [tostyle] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [tostyle] patch </h1>
+
+Footprints bring their own hole sizes, copper ring sizes and clearances.
+These parameters often depend on the manufacturing process, and such a value
+coming from a footprint may differ much from the values used on the board already.
+<p>
+PCB has actions (and menus and hotkeys) to change sizes manually. In my practice,
+I try to stick to the sizes defined in my routing styles and try to avoid
+manually changing clearances or ring sizes. Still, the random values coming
+from various footprints should be changed.
+<p>
+After many years of struggling with this, I realized the feature I need is
+a way to change object sizes to not a relative or absolute <i>number</i> but
+to the current <i>routing style</i>. The [tostyle] patch does exactly this.
+It implements the following new features:
+<ul>
+	<li> change clearance size now works on elements: it changes the clearance of all pins/pads; this is the same as change drill has been working for a long time
+	<li> size change actions normally take a value and a unit; if the value is not a number but text <b>style</b>, the value is copied from the currently active routing style
+	<li> a new ChangeSizes() action that attempts to execute the other three change size actions with the same arguments and fails only if all of them failed; the three sizes are: main size, drill size, clearance size
+	<li> a menu item and hotkey binding to key 'Shift+Y' (for routing stYle) that calls ChangeSizes() of the selected or current object(s) to resize them to the current routing style
+</ul>
+<p>
+The new route style set works on:
+<ul>
+	<li> lines and arcs: sets their line width and clearance
+	<li> vias and individual pins: sets their ring dia, drill dia and clearance
+	<li> individual pads: sets their clearance
+	<li> elements: set all their pins and pads 
+</ul>
+
+<h2> Example </h2>
+GUI: select a routing style; hover above a line, a via, a pin/pad of an element
+or the silk of an element; press Shift+Y; undo if necessary.
+<p>
+CLI: select objects, execute action ChangeSizes(selected, style)
+<p>
+CLI: to adjust drill sizes only: select objects, execute action ChangeDrillSize(selected, style)
+
+
+<h2> save/load and compatibility </h2>
+Not affected, since the patch introduces actions and UI changes, no change
+related to the data model.
+
+<h2> plans </h2>
+No plans, the feature is complete.
+
+</body>
+</html>
diff --git a/doc-rnd/features/unglib.html b/doc-rnd/features/unglib.html
new file mode 100644
index 0000000..ac57944
--- /dev/null
+++ b/doc-rnd/features/unglib.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+	<title> pcb-rnd - [unglib] </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - the [unglib] patch </h1>
+
+Removes glib dependency from core, in favor of
+<a href="http://repo.hu/projects/articles/minilibs">minilibs</a> to help
+keeping the code <a href="devlog/20160313_unglib.html">small, modular
+and easier to fix</a>.
+
+<h2> save/load and compatibility </h2>
+Not affected.
+
+<h2> plans </h2>
+Remove glib dependency from the puller plugin.
+
+</body>
+</html>
diff --git a/doc-rnd/gpmi/Credits b/doc-rnd/gpmi/Credits
new file mode 100644
index 0000000..74d19da
--- /dev/null
+++ b/doc-rnd/gpmi/Credits
@@ -0,0 +1,11 @@
+2006...2009
+~~~~~~~~~~~
+Big thanks to DJ Delorie who added external HID support in PCB and commited
+many small patches needed to get this project working in the period
+when the whole project was a plugin to vanilla pcb.
+
+The following people helped the project with their valuable feedbacks
+in the early stage:
+ John Griessen
+ Carlos Nieves Onega
+
diff --git a/doc-rnd/gpmi/History b/doc-rnd/gpmi/History
new file mode 100644
index 0000000..12976d1
--- /dev/null
+++ b/doc-rnd/gpmi/History
@@ -0,0 +1,32 @@
+History
+~~~~~~~
+As of October 2006, PCB has native support for external (or dynamic
+loadable) HIDs. This project is plugin that provides scripting features.
+
+For a long time I believe that good applications should be scriptable,
+and real good ones should not restrict scripting to one or two languages.
+For this reason I have been working on GPMI with various other developers
+for years. GPMI provides a general way to split up big applications in
+smaller, usually dynamic loadable parts, and encourages that some of these
+parts should be written in a script language. For me, the ideal model
+of an application is a small core that knows how to manipulate the
+internal representation of the task and a lot of dynamic functionality
+plugins and support plugins that do all the high-level job for the user.
+
+PCB is an old and big software, with a stable user base. Restructuring the
+whole source to "GPMIize" it is no option. Instead, a nice compromise is
+implementing this functionality in a plugin that pcb can load for the users
+who are interested in such extras.
+
+From 2006 to 2009 spring the project was on hold, because I was nagging
+PCB developers to add support for customizable dialog boxes. In late March
+2009, PCB developers pointed out that the interface I was waiting for had been
+there for who-knows-how-long. With the help of DJ Delorie I extended the
+action handling of PCB to remember action contexts, which was another showstopper.
+After all obstacles removed, I allocated a 24 hour coding session where I
+finished the exporter functionality, added action binding, dialog box handling
+and layout manipulation functions.
+
+During the summer of 2015 I merged pcb-gpmi into pcb-rnd and made it
+the default scirpting engine for pcb-rnd.
+
diff --git a/doc-rnd/gpmi/Makefile b/doc-rnd/gpmi/Makefile
new file mode 100644
index 0000000..6406e2f
--- /dev/null
+++ b/doc-rnd/gpmi/Makefile
@@ -0,0 +1,20 @@
+DPI_BIG=-Gsize=8,8 -Gdpi=166
+DPI=-Gsize=8,8 -Gdpi=100
+
+all:  gpmi_flow.png gpmi_flow_load.png gpmi_flow_reg.png gpmi_flow_menu.png gpmi_flow_exp.png
+	cd rosetta && make
+
+gpmi_flow.png: gpmi_flow.dot Makefile
+	dot $(DPI_BIG) -Tpng gpmi_flow.dot >$@
+
+gpmi_flow_load.png: gpmi_flow_load.dot Makefile
+	dot $(DPI) -Tpng gpmi_flow_load.dot >$@
+
+gpmi_flow_reg.png: gpmi_flow_reg.dot Makefile
+	dot $(DPI) -Tpng gpmi_flow_reg.dot >$@
+
+gpmi_flow_menu.png: gpmi_flow_menu.dot Makefile
+	dot $(DPI) -Tpng gpmi_flow_menu.dot >$@
+
+gpmi_flow_exp.png: gpmi_flow_exp.dot Makefile
+	dot $(DPI) -Tpng gpmi_flow_exp.dot >$@
diff --git a/doc-rnd/gpmi/Porting b/doc-rnd/gpmi/Porting
new file mode 100644
index 0000000..eee85c0
--- /dev/null
+++ b/doc-rnd/gpmi/Porting
@@ -0,0 +1,5 @@
+Currently the project works on Linux only. GPMI is portable, so we need
+only Makefile tweakings on pcb-gpmi to be able to compile it anywhere
+where GPMI compiles. However, first I want to see some test results
+from Linux users.
+
diff --git a/doc-rnd/gpmi/gpmi_flow.dot b/doc-rnd/gpmi/gpmi_flow.dot
new file mode 100644
index 0000000..95bebca
--- /dev/null
+++ b/doc-rnd/gpmi/gpmi_flow.dot
@@ -0,0 +1,63 @@
+digraph g {
+	rankdir=LR
+	splines="polyline"
+#	nodesep=1
+#	ranksep=1.5
+
+	subgraph cluster_0 {
+		label="pcb-rnd"
+#		core [label="pcb-rnd core|{{<f0>HID system|<f1>plugin system}}" shape=record]
+		core [label="core" shape=box]
+		hids [label="HID system" shape=box height=2]
+		plugins [label="plugin system" shape=box height=2]
+	}
+
+	subgraph cluster_1 {
+		label="GPMI"
+		gpmi_hid [label="gpmi HID"]
+		gpmi_pkg [label="gpmi package\n(host glue layer)"]
+		gpmi_mod1 [label="gpmi lua module\n(script glue layer)"]
+		gpmi_scr1 [label="lua script"]
+		gpmi_mod2 [label="gpmi mawk module\n(script glue layer)"]
+		gpmi_scr2 [label="awk script"]
+	}
+
+
+
+	gui [label="the active\nGUI HID\n(e.g. gtk)"]
+	exp1 [label="PS exporer"]
+	exp2 [label="PNG exporer"]
+
+
+	exp1 -> hids [label="register"]
+	exp2 -> hids [label="register"]
+
+	hids -> exp1 [label="drawing"]
+	hids -> exp2 [label="drawing"]
+	hids -> gui [label="drawing"]
+
+	plugins -> gpmi_hid [label="load plugin"]
+	gpmi_hid -> hids [label="register"]
+	hids -> gpmi_hid [label="drawing"]
+	core -> gpmi_hid [label="event"]
+
+	gpmi_pkg -> core [label="calls & variables"]
+	gpmi_pkg -> hids [label="calls & variables"]
+	gpmi_pkg -> gpmi_hid [label="calls & variables"]
+	gpmi_pkg -> plugins [label="calls & variables"]
+
+
+	gpmi_hid -> gpmi_mod1 [label="load script"]
+	gpmi_hid -> gpmi_mod1 [label="event"]
+	gpmi_hid -> gpmi_mod2 [label="load script"]
+	gpmi_hid -> gpmi_mod2 [label="event"]
+
+	gpmi_mod1 -> gpmi_scr1 [label="load and exec"]
+	gpmi_mod1 -> gpmi_scr1 [label="event" style=dashed]
+	gpmi_scr1 -> gpmi_pkg [label="function calls" style=dashed]
+
+	gpmi_mod2 -> gpmi_scr2 [label="load and exec"]
+	gpmi_mod2 -> gpmi_scr2 [label="event" style=dashed]
+	gpmi_scr2 -> gpmi_pkg [label="function calls" style=dashed]
+
+}
\ No newline at end of file
diff --git a/doc-rnd/gpmi/gpmi_flow.png b/doc-rnd/gpmi/gpmi_flow.png
new file mode 100644
index 0000000..aa5202f
Binary files /dev/null and b/doc-rnd/gpmi/gpmi_flow.png differ
diff --git a/doc-rnd/gpmi/gpmi_flow_exp.dot b/doc-rnd/gpmi/gpmi_flow_exp.dot
new file mode 100644
index 0000000..ef1bb7d
--- /dev/null
+++ b/doc-rnd/gpmi/gpmi_flow_exp.dot
@@ -0,0 +1,65 @@
+digraph g {
+	rankdir=LR
+	splines="polyline"
+#	nodesep=1
+#	ranksep=1.5
+
+	subgraph cluster_0 {
+		label="pcb-rnd"
+#		core [label="pcb-rnd core|{{<f0>HID system|<f1>plugin system}}" shape=record]
+		core [label="core" shape=box]
+		hids [label="HID system" shape=box height=2]
+		plugins [label="plugin system" shape=box height=2]
+	}
+
+	subgraph cluster_1 {
+		label="GPMI"
+		gpmi_hid [label="gpmi HID"]
+		gpmi_pkg [label="gpmi package\n(host glue layer)"]
+		gpmi_mod1 [label="gpmi lua module\n(script glue layer)"]
+		gpmi_scr1 [label="lua script"]
+		gpmi_mod2 [label="gpmi mawk module\n(script glue layer)"]
+		gpmi_scr2 [label="awk script"]
+		outfile [label="export\noutput file" color=green]
+	}
+
+
+
+	gui [label="the active\nGUI HID\n(e.g. gtk)"]
+	exp1 [label="PS exporer"]
+	exp2 [label="PNG exporer"]
+
+
+	exp1 -> hids [label="register"]
+	exp2 -> hids [label="register"]
+
+	hids -> exp1 [label="drawing"]
+	hids -> exp2 [label="drawing"]
+	hids -> gui [label="drawing"]
+
+	plugins -> gpmi_hid [label="load plugin"]
+	gpmi_hid -> hids [label="register"]
+	hids -> gpmi_hid [label="drawing"  color=green]
+	core -> gpmi_hid [label="event"]
+
+	gpmi_pkg -> core [label="calls & variables"]
+	gpmi_pkg -> hids [label="calls & variables" color=red]
+	gpmi_pkg -> gpmi_hid [label="calls & variables"]
+	gpmi_pkg -> plugins [label="calls & variables"]
+
+
+	gpmi_hid -> gpmi_mod1 [label="load script"]
+	gpmi_hid -> gpmi_mod1 [label="event" color=green]
+	gpmi_hid -> gpmi_mod2 [label="load script"]
+	gpmi_hid -> gpmi_mod2 [label="event"]
+
+	gpmi_mod1 -> gpmi_scr1 [label="load and exec"]
+	gpmi_mod1 -> gpmi_scr1 [label="event" style=dashed color=green]
+	gpmi_scr1 -> gpmi_pkg [label="function calls" style=dashed color=red]
+	gpmi_scr1 -> outfile [label="write" color=green]
+
+	gpmi_mod2 -> gpmi_scr2 [label="load and exec"]
+	gpmi_mod2 -> gpmi_scr2 [label="event" style=dashed]
+	gpmi_scr2 -> gpmi_pkg [label="function calls" style=dashed]
+
+}
\ No newline at end of file
diff --git a/doc-rnd/gpmi/gpmi_flow_exp.png b/doc-rnd/gpmi/gpmi_flow_exp.png
new file mode 100644
index 0000000..0fd24f8
Binary files /dev/null and b/doc-rnd/gpmi/gpmi_flow_exp.png differ
diff --git a/doc-rnd/gpmi/gpmi_flow_load.dot b/doc-rnd/gpmi/gpmi_flow_load.dot
new file mode 100644
index 0000000..9619fb2
--- /dev/null
+++ b/doc-rnd/gpmi/gpmi_flow_load.dot
@@ -0,0 +1,63 @@
+digraph g {
+	rankdir=LR
+	splines="polyline"
+#	nodesep=1
+#	ranksep=1.5
+
+	subgraph cluster_0 {
+		label="pcb-rnd"
+#		core [label="pcb-rnd core|{{<f0>HID system|<f1>plugin system}}" shape=record]
+		core [label="core" shape=box]
+		hids [label="HID system" shape=box height=2]
+		plugins [label="plugin system" shape=box height=2]
+	}
+
+	subgraph cluster_1 {
+		label="GPMI"
+		gpmi_hid [label="gpmi HID"]
+		gpmi_pkg [label="gpmi package\n(host glue layer)"]
+		gpmi_mod1 [label="gpmi lua module\n(script glue layer)"]
+		gpmi_scr1 [label="lua script"]
+		gpmi_mod2 [label="gpmi mawk module\n(script glue layer)"]
+		gpmi_scr2 [label="awk script"]
+	}
+
+
+
+	gui [label="the active\nGUI HID\n(e.g. gtk)"]
+	exp1 [label="PS exporer"]
+	exp2 [label="PNG exporer"]
+
+
+	exp1 -> hids [label="register"]
+	exp2 -> hids [label="register"]
+
+	hids -> exp1 [label="drawing"]
+	hids -> exp2 [label="drawing"]
+	hids -> gui [label="drawing"]
+
+	plugins -> gpmi_hid [label="load plugin" color=red]
+	gpmi_hid -> hids [label="register"]
+	hids -> gpmi_hid [label="drawing"]
+	core -> gpmi_hid [label="event"]
+
+	gpmi_pkg -> core [label="calls & variables"]
+	gpmi_pkg -> hids [label="calls & variables"]
+	gpmi_pkg -> gpmi_hid [label="calls & variables" color=green]
+	gpmi_pkg -> plugins [label="calls & variables"]
+
+
+	gpmi_hid -> gpmi_mod1 [label="load script" color=red]
+	gpmi_hid -> gpmi_mod1 [label="event"]
+	gpmi_hid -> gpmi_mod2 [label="load script"]
+	gpmi_hid -> gpmi_mod2 [label="event"]
+
+	gpmi_mod1 -> gpmi_scr1 [label="load and exec" color=red]
+	gpmi_mod1 -> gpmi_scr1 [label="event" style=dashed]
+	gpmi_scr1 -> gpmi_pkg [label="function calls" style=dashed color=green]
+
+	gpmi_mod2 -> gpmi_scr2 [label="load and exec"]
+	gpmi_mod2 -> gpmi_scr2 [label="event" style=dashed]
+	gpmi_scr2 -> gpmi_pkg [label="function calls" style=dashed]
+
+}
\ No newline at end of file
diff --git a/doc-rnd/gpmi/gpmi_flow_load.png b/doc-rnd/gpmi/gpmi_flow_load.png
new file mode 100644
index 0000000..c0ad8b2
Binary files /dev/null and b/doc-rnd/gpmi/gpmi_flow_load.png differ
diff --git a/doc-rnd/gpmi/gpmi_flow_menu.dot b/doc-rnd/gpmi/gpmi_flow_menu.dot
new file mode 100644
index 0000000..6dde1ae
--- /dev/null
+++ b/doc-rnd/gpmi/gpmi_flow_menu.dot
@@ -0,0 +1,63 @@
+digraph g {
+	rankdir=LR
+	splines="polyline"
+#	nodesep=1
+#	ranksep=1.5
+
+	subgraph cluster_0 {
+		label="pcb-rnd"
+#		core [label="pcb-rnd core|{{<f0>HID system|<f1>plugin system}}" shape=record]
+		core [label="core" shape=box]
+		hids [label="HID system" shape=box height=2]
+		plugins [label="plugin system" shape=box height=2]
+	}
+
+	subgraph cluster_1 {
+		label="GPMI"
+		gpmi_hid [label="gpmi HID"]
+		gpmi_pkg [label="gpmi package\n(host glue layer)"]
+		gpmi_mod1 [label="gpmi lua module\n(script glue layer)"]
+		gpmi_scr1 [label="lua script"]
+		gpmi_mod2 [label="gpmi mawk module\n(script glue layer)"]
+		gpmi_scr2 [label="awk script"]
+	}
+
+
+
+	gui [label="the active\nGUI HID\n(e.g. gtk)"]
+	exp1 [label="PS exporer"]
+	exp2 [label="PNG exporer"]
+
+
+	exp1 -> hids [label="register"]
+	exp2 -> hids [label="register"]
+
+	hids -> exp1 [label="drawing"]
+	hids -> exp2 [label="drawing"]
+	hids -> gui [label="drawing" color=green]
+
+	plugins -> gpmi_hid [label="load plugin"]
+	gpmi_hid -> hids [label="register"]
+	hids -> gpmi_hid [label="drawing"]
+	core -> gpmi_hid [label="event" color=red]
+
+	gpmi_pkg -> core [label="calls & variables"]
+	gpmi_pkg -> hids [label="calls & variables" color=green]
+	gpmi_pkg -> gpmi_hid [label="calls & variables"]
+	gpmi_pkg -> plugins [label="calls & variables"]
+
+
+	gpmi_hid -> gpmi_mod1 [label="load script"]
+	gpmi_hid -> gpmi_mod1 [label="event" color=red]
+	gpmi_hid -> gpmi_mod2 [label="load script"]
+	gpmi_hid -> gpmi_mod2 [label="event"]
+
+	gpmi_mod1 -> gpmi_scr1 [label="load and exec"]
+	gpmi_mod1 -> gpmi_scr1 [label="event" style=dashed color=red]
+	gpmi_scr1 -> gpmi_pkg [label="function calls" style=dashed color=green]
+
+	gpmi_mod2 -> gpmi_scr2 [label="load and exec"]
+	gpmi_mod2 -> gpmi_scr2 [label="event" style=dashed]
+	gpmi_scr2 -> gpmi_pkg [label="function calls" style=dashed]
+
+}
\ No newline at end of file
diff --git a/doc-rnd/gpmi/gpmi_flow_menu.png b/doc-rnd/gpmi/gpmi_flow_menu.png
new file mode 100644
index 0000000..f8afd40
Binary files /dev/null and b/doc-rnd/gpmi/gpmi_flow_menu.png differ
diff --git a/doc-rnd/gpmi/gpmi_flow_reg.dot b/doc-rnd/gpmi/gpmi_flow_reg.dot
new file mode 100644
index 0000000..7ee6199
--- /dev/null
+++ b/doc-rnd/gpmi/gpmi_flow_reg.dot
@@ -0,0 +1,64 @@
+digraph g {
+	rankdir=LR
+	splines="polyline"
+#	nodesep=1
+#	ranksep=1.5
+
+	subgraph cluster_0 {
+		label="pcb-rnd"
+#		core [label="pcb-rnd core|{{<f0>HID system|<f1>plugin system}}" shape=record]
+		core [label="core" shape=box]
+		hids [label="HID system" shape=box height=2]
+		plugins [label="plugin system" shape=box height=2]
+	}
+
+	subgraph cluster_1 {
+		label="GPMI"
+		gpmi_hid [label="gpmi HID"]
+		gpmi_pkg [label="gpmi package\n(host glue layer)"]
+		gpmi_mod1 [label="gpmi lua module\n(script glue layer)"]
+		gpmi_scr1 [label="lua script"]
+		gpmi_mod2 [label="gpmi mawk module\n(script glue layer)"]
+		gpmi_scr2 [label="awk script"]
+	}
+
+
+
+	gui [label="the active\nGUI HID\n(e.g. gtk)"]
+	exp1 [label="PS exporer"]
+	exp2 [label="PNG exporer"]
+
+
+	exp1 -> hids [label="register"]
+	exp2 -> hids [label="register"]
+
+	hids -> exp1 [label="drawing"]
+	hids -> exp2 [label="drawing"]
+	hids -> gui [label="drawing"]
+
+	plugins -> gpmi_hid [label="load plugin"]
+	gpmi_hid -> hids [label="register"]
+	hids -> gpmi_hid [label="drawing"]
+	core -> gpmi_hid [label="event"]
+
+	gpmi_pkg -> core [label="calls & variables" color=green]
+	gpmi_pkg -> hids [label="calls & variables"]
+	gpmi_pkg -> gpmi_hid [label="calls & variables" color=red]
+	gpmi_pkg -> plugins [label="calls & variables"]
+
+
+	gpmi_hid -> gpmi_mod1 [label="load script"]
+	gpmi_hid -> gpmi_mod1 [label="event"]
+	gpmi_hid -> gpmi_mod2 [label="load script"]
+	gpmi_hid -> gpmi_mod2 [label="event"]
+
+	gpmi_mod1 -> gpmi_scr1 [label="load and exec"]
+	gpmi_mod1 -> gpmi_scr1 [label="event" style=dashed]
+	gpmi_scr1 -> gpmi_pkg [label="function calls" style=dashed color=red]
+	gpmi_scr1 -> gpmi_pkg [label="function calls" style=dashed color=green]
+
+	gpmi_mod2 -> gpmi_scr2 [label="load and exec"]
+	gpmi_mod2 -> gpmi_scr2 [label="event" style=dashed]
+	gpmi_scr2 -> gpmi_pkg [label="function calls" style=dashed]
+
+}
\ No newline at end of file
diff --git a/doc-rnd/gpmi/gpmi_flow_reg.png b/doc-rnd/gpmi/gpmi_flow_reg.png
new file mode 100644
index 0000000..14a5e81
Binary files /dev/null and b/doc-rnd/gpmi/gpmi_flow_reg.png differ
diff --git a/doc-rnd/gpmi/index.html b/doc-rnd/gpmi/index.html
new file mode 100644
index 0000000..e2b21c5
--- /dev/null
+++ b/doc-rnd/gpmi/index.html
@@ -0,0 +1,32 @@
+<html>
+<body>
+<H1> pcb-rnd GPMI scripting - TOC </H1>
+
+<H2> Installation, configuration </H2>
+<UL>
+	<LI> <a href="../gpmi_temp_inst.txt"> Installation </a>
+</UL>
+
+
+<H2> High level docs </H2>
+<UL>
+	<LI> <a href="scripting_intro.html"> Introduction to scripting </a>
+	<LI> <a href="rosetta/index.html"> Rosetta: example scripts </a>
+</UL>
+
+<H2> Glue layer documentation </H2>
+<UL>
+	<LI> actions: create and call actions; <a href="packages/actions.html"> package docs </a> ; <a href="packages/actions_ref.html"> API ref </a>
+	<LI> dialogs: pop up dialog boxes; <a href="packages/dialogs.html"> package docs </a> ; <a href="packages/dialogs_ref.html"> API ref </a>
+	<LI> hid: register exporters and build the fields of a custom dialog box; <a href="packages/hid.html"> package docs  </a> ; <a href="packages/hid_ref.html"> API ref  </a>
+	<LI> layout: search, move, create PCB objects; <a href="packages/layout.html"> package docs </a> ; <a href="packages/layout_ref.html"> API ref</a>
+</UL>
+
+<H2> Authors, credits, misc </H2>
+<UL>
+	<LI> <a href="Credits"> Credits </a>
+	<LI> <a href="History"> History </a>
+</UL>
+
+</body>
+</html>
diff --git a/doc-rnd/gpmi/packages/Makefile b/doc-rnd/gpmi/packages/Makefile
new file mode 100644
index 0000000..2d601b1
--- /dev/null
+++ b/doc-rnd/gpmi/packages/Makefile
@@ -0,0 +1,15 @@
+ROOT=../../..
+GPMIDIR=$(ROOT)/src_plugins/gpmi/pcb-gpmi
+ALL= actions dialogs hid layout
+
+all: XREF $(ALL:%=%_ref.html)
+
+include $(GPMIDIR)/Makefile.config
+
+%_ref.html REF.% : $(GPMIDIR)/gpmi_plugin/gpmi_pkg/%.h
+	$(ROOT)/util/genref.sh "$^" "$(ROOT)"  "../packages/$*_ref.html" $(PCB_CFLAGS) >$*_ref.html
+
+XREF: $(ALL:%=REF.%)
+	cat $^ > $@
+
+
diff --git a/doc-rnd/gpmi/packages/XREF b/doc-rnd/gpmi/packages/XREF
new file mode 100644
index 0000000..305e7fe
--- /dev/null
+++ b/doc-rnd/gpmi/packages/XREF
@@ -0,0 +1,85 @@
+event ACTE_action ../packages/actions_ref.html#ACTE_action
+event ACTE_gui_init ../packages/actions_ref.html#ACTE_gui_init
+event ACTE_unload ../packages/actions_ref.html#ACTE_unload
+function action_register ../packages/actions_ref.html#action_register
+function action_arg ../packages/actions_ref.html#action_arg
+function action ../packages/actions_ref.html#action
+function create_menu ../packages/actions_ref.html#create_menu
+enum dialog_fileselect_e ../packages/dialogs_ref.html#dialog_fileselect_e
+function dialog_log ../packages/dialogs_ref.html#dialog_log
+function dialog_confirm ../packages/dialogs_ref.html#dialog_confirm
+function dialog_report ../packages/dialogs_ref.html#dialog_report
+function dialog_prompt ../packages/dialogs_ref.html#dialog_prompt
+function dialog_fileselect ../packages/dialogs_ref.html#dialog_fileselect
+function dialog_beep ../packages/dialogs_ref.html#dialog_beep
+function dialog_progress ../packages/dialogs_ref.html#dialog_progress
+function dialog_attribute ../packages/dialogs_ref.html#dialog_attribute
+enum hid_attr_type_e ../packages/hid_ref.html#hid_attr_type_e
+enum EndCapStyle_e ../packages/hid_ref.html#EndCapStyle_e
+event HIDE_get_export_options ../packages/hid_ref.html#HIDE_get_export_options
+event HIDE_do_export_start ../packages/hid_ref.html#HIDE_do_export_start
+event HIDE_do_export_finish ../packages/hid_ref.html#HIDE_do_export_finish
+event HIDE_set_layer ../packages/hid_ref.html#HIDE_set_layer
+event HIDE_set_color ../packages/hid_ref.html#HIDE_set_color
+event HIDE_set_line_cap ../packages/hid_ref.html#HIDE_set_line_cap
+event HIDE_set_line_width ../packages/hid_ref.html#HIDE_set_line_width
+event HIDE_set_draw_xor ../packages/hid_ref.html#HIDE_set_draw_xor
+event HIDE_set_draw_faded ../packages/hid_ref.html#HIDE_set_draw_faded
+event HIDE_draw_line ../packages/hid_ref.html#HIDE_draw_line
+event HIDE_draw_arc ../packages/hid_ref.html#HIDE_draw_arc
+event HIDE_draw_rect ../packages/hid_ref.html#HIDE_draw_rect
+event HIDE_fill_circle ../packages/hid_ref.html#HIDE_fill_circle
+event HIDE_fill_polygon ../packages/hid_ref.html#HIDE_fill_polygon
+event HIDE_fill_rect ../packages/hid_ref.html#HIDE_fill_rect
+event HIDE_use_mask ../packages/hid_ref.html#HIDE_use_mask
+event HIDE_make_gc ../packages/hid_ref.html#HIDE_make_gc
+event HIDE_destroy_gc ../packages/hid_ref.html#HIDE_destroy_gc
+event HIDE_fill_pcb_pv ../packages/hid_ref.html#HIDE_fill_pcb_pv
+event HIDE_fill_pcb_pad ../packages/hid_ref.html#HIDE_fill_pcb_pad
+function hid_create ../packages/hid_ref.html#hid_create
+function hid_add_attribute ../packages/hid_ref.html#hid_add_attribute
+function hid_get_attribute ../packages/hid_ref.html#hid_get_attribute
+function hid_register ../packages/hid_ref.html#hid_register
+function hid_gpmi_data_set ../packages/hid_ref.html#hid_gpmi_data_set
+function hid_gpmi_data_get ../packages/hid_ref.html#hid_gpmi_data_get
+function hid_string2val ../packages/hid_ref.html#hid_string2val
+enum layout_object_mask_e ../packages/layout_ref.html#layout_object_mask_e
+enum layout_object_coord_e ../packages/layout_ref.html#layout_object_coord_e
+enum layout_flag_e ../packages/layout_ref.html#layout_flag_e
+enum layer_field_e ../packages/layout_ref.html#layer_field_e
+function layout_search_box ../packages/layout_ref.html#layout_search_box
+function layout_search_selected ../packages/layout_ref.html#layout_search_selected
+function layout_search_found ../packages/layout_ref.html#layout_search_found
+function layout_search_get ../packages/layout_ref.html#layout_search_get
+function layout_search_free ../packages/layout_ref.html#layout_search_free
+function layout_obj_coord ../packages/layout_ref.html#layout_obj_coord
+function layout_obj_type ../packages/layout_ref.html#layout_obj_type
+function layout_obj_move ../packages/layout_ref.html#layout_obj_move
+function layout_arc_angles ../packages/layout_ref.html#layout_arc_angles
+function layout_create_line ../packages/layout_ref.html#layout_create_line
+function layout_create_via ../packages/layout_ref.html#layout_create_via
+function layout_create_arc ../packages/layout_ref.html#layout_create_arc
+function layout_switch_to_layer ../packages/layout_ref.html#layout_switch_to_layer
+function layout_get_current_layer ../packages/layout_ref.html#layout_get_current_layer
+function layout_resolve_layer ../packages/layout_ref.html#layout_resolve_layer
+function layout_get_max_possible_layer ../packages/layout_ref.html#layout_get_max_possible_layer
+function layout_get_max_copper_layer ../packages/layout_ref.html#layout_get_max_copper_layer
+function layout_get_max_layer ../packages/layout_ref.html#layout_get_max_layer
+function layout_layer_name ../packages/layout_ref.html#layout_layer_name
+function layout_layer_color ../packages/layout_ref.html#layout_layer_color
+function layout_layer_field ../packages/layout_ref.html#layout_layer_field
+function layout_get_page_width ../packages/layout_ref.html#layout_get_page_width
+function layout_get_page_height ../packages/layout_ref.html#layout_get_page_height
+function layout_set_page_size ../packages/layout_ref.html#layout_set_page_size
+function mil2pcb_multiplier ../packages/layout_ref.html#mil2pcb_multiplier
+function mm2pcb_multiplier ../packages/layout_ref.html#mm2pcb_multiplier
+function current_grid_unit ../packages/layout_ref.html#current_grid_unit
+function debug_draw_request ../packages/layout_ref.html#debug_draw_request
+function debug_draw_flush ../packages/layout_ref.html#debug_draw_flush
+function debug_draw_finish ../packages/layout_ref.html#debug_draw_finish
+function debug_draw_dctx ../packages/layout_ref.html#debug_draw_dctx
+function draw_set_color ../packages/layout_ref.html#draw_set_color
+function draw_set_line_width ../packages/layout_ref.html#draw_set_line_width
+function draw_set_draw_xor ../packages/layout_ref.html#draw_set_draw_xor
+function draw_set_draw_faded ../packages/layout_ref.html#draw_set_draw_faded
+function draw_line ../packages/layout_ref.html#draw_line
diff --git a/doc-rnd/gpmi/packages/actions.html b/doc-rnd/gpmi/packages/actions.html
new file mode 100644
index 0000000..f7bbf60
--- /dev/null
+++ b/doc-rnd/gpmi/packages/actions.html
@@ -0,0 +1,123 @@
+<HTML>
+<BODY>
+<H1> The actions package </H1>
+
+The action package is used to register actions and menus in PCB and
+to execute existing actions. In PCB actions
+are generated when the user selects a menu, presses a key or issues a
+command on the PCB command line.
+
+<H2> Registration of new actions </H2>
+The script may register new actions
+using arbitrary action names. If any of the registered actions is
+detected, an ACTE_action event is sent to the script. If multiple
+actions are used in a script, argument <I>name</I> of the event handler
+can be used to identify the triggering action. <small>(This may be time consuming
+in scripting languages (a series of string comparison) - those scripts
+binding to frequent actions should not bind to too many different actions.)</small>
+pcb-gpmi guarantees that the event handler of a script is triggered only
+when an action is caught that previously was registered by the same script.
+<P>
+The process of binding an action:
+<OL>
+	<LI> create an event handler for event <I>ACTE_action</I>
+	<LI> register one or more actions using function <I>action_register</I>
+	<LI> optional: when the event handler is triggered, check argument <I>name</I> (for scripts with multiple actions registered)
+	<LI> optional: use argument <I>argc</I> and function <I>action_arg</I> to fetch action arguments
+	<LI> optional: use arguments <I>x, y</I> to learn the cursor position on the layout.
+</OL>
+
+Example (written in lua):
+<PRE>
+-- load the package
+PkgLoad("pcb-gpmi/actions", 0);
+
+-- action callback
+function ev_action(id, name, argc, x, y)
+	if name == "cake" then
+		size = action_arg(1);
+		-- put cake drawing code here
+	else
+		-- must be candy
+		amount = action_arg(1);
+		-- put candy drawing code here
+	end
+end
+
+-- register and bind action
+action_register("cake",  "cake center xy?", "cake service", "cake(size)", "");
+action_register("candy", "candy cloud center xy?", "cake service", "candy(amount)", "");
+Bind("ACTE_action", "ev_action");
+</PRE>
+<p>
+When the script is unloaded all actions the script registered 
+are removed from pcb-rnd automatically.
+
+<H2> Executing actions </H2>
+An existing action can be executed using the action() call. The only one
+argument is a command line string. The syntyax is the same as in pcb
+command line.
+
+Example (written in lua):
+<PRE>
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+
+function ev_action1(id, name, argc, x, y)
+	action("undo()")
+	action("undo()")
+end
+
+-- register and bind action
+action_register("untwo", "", "undo twice", "untwo()", "CONTEXT!");
+</PRE>
+<p>
+The above script registers a new action called untwo(). When untwo() is executed,
+it exectues action undo() twice.
+
+
+<H2> Creating menus </H2>
+It is possible to insert menus and submenus runtime, using the call
+create_menu(). The script should do this only after the gui has been initialized.
+The most common way is to create all menus from the ACTE_gui_init event,
+which is called after the gui finished setting up.
+<p>
+The first argument of create_menu() is the menu path. The path is a list
+of visible menu names separated by slashes (e.g. "/main_menu/File/Save as..." means
+"File" menu, "Save as..." submenu). 
+<p>
+Paths are interpreted as menu paths, which are a slightly simplified version
+of lihata paths found in menu.lht. Basically there's a main directory directly
+under root that determines the type of the menu:
+<p>
+<ul>
+	<li> /main_menu/ is the menubar
+	<li> /popups/something/ is a popup menu called something; the lesstif HID does
+	     not support popups; popups are popped up by actions.
+</ul>
+<p>
+The simplification compared to lihata paths is that only menu and submenu
+names are on the path, so lihata nodes like "li:submenu" should be ignored.
+In other words, the path represents how the menus can be reached from the user
+interface, plus a "main menu type prefix" as discussed above.
+<p>
+By <b>convention</b>, scripts should
+create new menu items under the "/main_menu/Plugins/" menu.
+<p>
+The following example lua script registers a new menu item
+<pre>
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+
+function ev_gui_init(argc, argv)
+	create_menu("/main_menu/Plugins/foo", "undo()", "o", "Ctrl<Key>o", "tooltip for foo");
+end
+
+-- register and bind action
+Bind("ACTE_gui_init", "ev_gui_init");
+</pre>
+<p>
+When the user clicks on the menu, the action script specified in the second argument
+of create_menu() is executed. Thus the script usually registers a new action
+first then registers one or more menu items executing those actions.
+
+</BODY>
+</HTML>
diff --git a/doc-rnd/gpmi/packages/actions_ref.html b/doc-rnd/gpmi/packages/actions_ref.html
new file mode 100644
index 0000000..5c1750d
--- /dev/null
+++ b/doc-rnd/gpmi/packages/actions_ref.html
@@ -0,0 +1,82 @@
+<html>
+<body>
+<h1>PCB GPMI</h1>
+<h2>Reference manual for package actions</h2>
+<small>Automatically generated from actions.h</small>
+
+<h3> Events </h3>
+<dl>
+<p>Events do not have return value. The first argument is always <a href="event_id.html">the even id</a>. Event handlers defined in scripts get all event arguments converted to string (types below are informational).
+<a id="ACTE_action">
+<H4> ACTE_action(int event_id, const char* name, int argc, int x, int y) </H4>
+<pre>
+ Generated when an action registered by the script is executed.
+   Arguments:
+    name: name of the action (as registed using function action_register())
+    argc: number of arguments. Arguments can be accessed using function action_arg
+    x, y: optional coords, if need_xy was not empty at action_register 
+</pre>
+<a id="ACTE_gui_init">
+<H4> ACTE_gui_init(int event_id, int argc, char** argv) </H4>
+<pre>
+ Generated right after gui initialization, before the gui main loop.
+   Arguments:
+    argc: number of arguments the gui was initialized with.
+    argv[]: arguments the gui was initialized with - unaccessible for the scripts. 
+</pre>
+<a id="ACTE_unload">
+<H4> ACTE_unload(int event_id, const char* conffile) </H4>
+<pre>
+ Generated right before unloading a script to give the script a chance
+   to clean up.
+   Arguments:
+    conffile: the name of the config file that originally triggered laoding the script, or empty if the script was loaded from the gui. 
+</pre>
+</dl>
+<h3> Functions </h3>
+<dl>
+<p>The following functions are registered in script context.
+<a id="action_register">
+<H4>  int action_register(const char* name, const char* need_xy, const char* description, const char* syntax)  </H4>
+<pre>
+ Register an action in PCB - when the action is executed, event
+   ACTE_action is generated with the action name.
+   Multiple actions can be registered. Any action registered by the script
+   will trigger an ACTE_event sent to the script.
+   Arguments:
+    name: name of the action
+    need_xy: the question the user is asked when he needs to choose a coordinate; if empty, no coordinate is asked
+    description: description of the action (for the help)
+    syntax: syntax of the action (for the help)
+   Returns 0 on success.
+ 
+</pre>
+<a id="action_arg">
+<H4>  const char* action_arg(int argn)  </H4>
+<pre>
+ extract the (argn)th event argument for the current action (makes sense only in an ACTE_action event handler 
+</pre>
+<a id="action">
+<H4>  int action(const char* cmdline)  </H4>
+<pre>
+ call an existing action using PCB syntax (e.g. foo(1, 2, 3))
+   Returns non-zero on error; generally returns value of the action
+   (which is also non-zero on error). 
+</pre>
+<a id="create_menu">
+<H4>  void create_menu(const char* path, const char* action, const char* mnemonic, const char* hotkey, const char* tooltip)  </H4>
+<pre>
+ Create a new menu or submenu at path. Missing parents are created
+   automatically with empty action, mnemonic, hotkey and tooltip.
+   Arguments:
+    path: the full path of the new menu
+    action: this action is executed when the user clicks on the menu
+    mnemonic: which letter to underline in the menu text (will be the fast-jump-there key once the menu is open)
+    hotkey: when this key is pressed in the main gui, the action is also triggered; the format is modifiers<Key>letter, where modifiers is Alt, Shift or Ctrl. This is the same syntax that is used in the .res files.
+    tooltip: short help text 
+</pre>
+</dl>
+
+</body>
+</html>
+
diff --git a/doc-rnd/gpmi/packages/dialogs.html b/doc-rnd/gpmi/packages/dialogs.html
new file mode 100644
index 0000000..bb0e405
--- /dev/null
+++ b/doc-rnd/gpmi/packages/dialogs.html
@@ -0,0 +1,47 @@
+<HTML>
+<BODY>
+	<H1> dialogs package </H1>
+	The purpose of this package is to expose the dialog box handling interface
+	of the active GUI hid. Using this package scripts can pop up dialog boxes
+	to inform or ask the user. If there is no active GUI, call logs are dumped
+	on stderr. Note: message arguments usually may contain newline (\n) characters
+	to split the message.
+
+	<H2> Common conventions </H2>
+	Dialog boxes are blocking calls to the GUI HID: when the script
+	calls a dialog box, the script is suspended until the dialog box
+	is closed by the user. In other words, dialog boxes behave
+	as regular function calls from the scripts: when they return,
+	the dialog is over and the result is known.
+	<p>
+	The only exception is <i>dialog_progress()</i>, which opens or
+	updates or closes the already open progress dialog box, and 
+	returns immediately (even when the box is left open).
+
+	<H2> Simple dialogs vs. custom dialogs </H2>
+	Most of the calls will pop up a static dialog box. Static means
+	that widgets are predefined by the GUI HID. The script
+	is free to fill in data, but can not change the basic structure of
+	the dialog box. For example <i>dialog_confirm(), dialog_prompt(),
+	dialog_fileselect()</i> are static.
+	<p>
+	When the script needs a custom, dynamic dialog box, it needs to
+	create an <i>attribute dialog</i>. The script sets up a HID
+	structure using the hid package, builds up all the input fields
+	then calls <i>dialog_attribute()</i> with the hid.
+
+	<H2> Progress dialogs </H2>
+
+	The script should call dialog_progress() periodicly from a process that
+	runs time consuming calculations and check the return value and break
+	the loop on cancel. The process should have an idea of how long it
+	will take. This is passed on in argument <i>total</i>. As long as
+	argument <i>so_far</i> is less than <i>total</i>, the dialog is
+	open.
+	<p>
+	After the process has finished, a call with <i>so_far=total+1</i> should be
+	made to make sure the window is closed. If the call returns non-zero,
+	the process should be cancelled.
+
+</BODY>
+</HTML>
diff --git a/doc-rnd/gpmi/packages/dialogs_ref.html b/doc-rnd/gpmi/packages/dialogs_ref.html
new file mode 100644
index 0000000..4c2a30e
--- /dev/null
+++ b/doc-rnd/gpmi/packages/dialogs_ref.html
@@ -0,0 +1,105 @@
+<html>
+<body>
+<h1>PCB GPMI</h1>
+<h2>Reference manual for package dialogs</h2>
+<small>Automatically generated from dialogs.h</small>
+
+<h3> Enums </h3>
+<dl>
+<p>Enum values should be passed on as strings.
+<a id="dialog_fileselect_e">
+<H4> dialog_fileselect_e</H4>
+<pre>
+ Filter on what files a file select dialog should list 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> FS_NONE <td>  none of the below 
+<tr><td> FS_READ <td>  when the selected file will be read, not written (HID_FILESELECT_READ) 
+<tr><td> FS_NOT_EXIST <td>  the function calling hid->fileselect will deal with the case when the selected file already exists.  If not given, then the gui will prompt with an "overwrite?" prompt.  Only used when writing. (HID_FILESELECT_MAY_NOT_EXIST)  
+<tr><td> FS_TEMPLATE <td>  the call is supposed to return a file template (for gerber output for example) instead of an actual file.  Only used when writing. (HID_FILESELECT_IS_TEMPLATE) 
+</table>
+</dl>
+<h3> Functions </h3>
+<dl>
+<p>The following functions are registered in script context.
+<a id="dialog_log">
+<H4>  void dialog_log(const char* msg)  </H4>
+<pre>
+ Append a msg to the log (log window and/or stderr). 
+</pre>
+<a id="dialog_confirm">
+<H4>  int dialog_confirm(const char* msg, const char* ok, const char* cancel)  </H4>
+<pre>
+ Ask the user for confirmation (usually using a popup). Returns 0 for
+   cancel and 1 for ok.
+   Arguments:
+     msg: message to the user
+     ok: label of the OK button
+     cancel: label of the cancel button
+  Arguments "ok" and "cancel" may be empty (or NULL) in which
+  case the GUI will use the default (perhaps localized) labels for
+  those buttons. 
+</pre>
+<a id="dialog_report">
+<H4>  void dialog_report(const char* title, const char* msg)  </H4>
+<pre>
+ Pop up a report dialog.
+   Arguments:
+     title: title of the window
+     msg: message 
+</pre>
+<a id="dialog_prompt">
+<H4>  char* dialog_prompt(const char* msg, const char* default_)  </H4>
+<pre>
+ Ask the user to input a string (usually in a popup).
+   Arguments:
+     msg: message or question text
+     default_: default answer (this may be filled in on start)
+   Returns the answer. 
+</pre>
+<a id="dialog_fileselect">
+<H4>  char* dialog_fileselect(const char* title, const char* descr, char* default_file_, char* default_ext, const char* history_tag, dialog_fileselect_t flags)  </H4>
+<pre>
+ Pops up a file selection dialog.
+   Arguments:
+     title: window title
+     descr: description
+     default_file_
+     default_ext: default file name extension
+     history_tag
+     flags: one or more flags (see below)
+   Returns the selected file or NULL (empty). 
+</pre>
+<a id="dialog_beep">
+<H4>  void dialog_beep(void)  </H4>
+<pre>
+ Audible beep 
+</pre>
+<a id="dialog_progress">
+<H4>  int dialog_progress(int so_far, int total, const char* message)  </H4>
+<pre>
+ Request the GUI hid to draw a progress bar.
+   Arguments:
+     int so_far: achieved state
+     int total: maximum state
+     const char *message: informs the users what they are waiting for
+   If so_far is bigger than total, the progress bar is closed.
+   Returns nonzero if the user wishes to cancel the operation.
+
+</pre>
+<a id="dialog_attribute">
+<H4>  int dialog_attribute(hid_t* hid, const char* title, const char* descr)  </H4>
+<pre>
+ Pop up an attribute dialog; content (widgets) of the dialog box are coming
+   from hid (see the hid package).
+   Arguments:
+     hid: widgets
+     title: title of the window
+     descr: descripting printed in the dialog 
+</pre>
+</dl>
+
+</body>
+</html>
+
diff --git a/doc-rnd/gpmi/packages/event_id.html b/doc-rnd/gpmi/packages/event_id.html
new file mode 100644
index 0000000..7e98182
--- /dev/null
+++ b/doc-rnd/gpmi/packages/event_id.html
@@ -0,0 +1,20 @@
+<html>
+<body>
+<H1> event_id </H1>
+
+The first argument passed to an event handler is always the event ID.
+It is useful if there are several GPMI events with the same arguments and
+the script wants to bind them all to the same event handler. In that case
+using event_id the event handler can determine which event triggered the
+call. This design is similar to the signal handler documented in 
+signal(2).
+
+<P>
+
+The case described above (dispatcher in the event handler) is rare.
+Most users will bind one event to one function and can safely ignore this
+argument. Nevertheless it must present as the first argument
+on the event handler's argument list.
+
+</BODY>
+</HTML>
diff --git a/doc-rnd/gpmi/packages/hid.html b/doc-rnd/gpmi/packages/hid.html
new file mode 100644
index 0000000..1995d51
--- /dev/null
+++ b/doc-rnd/gpmi/packages/hid.html
@@ -0,0 +1,76 @@
+<HTML>
+<BODY>
+<H1> hid package </H1>
+
+The hid package has two purposes:
+<UL>
+	<LI> it is glue layer for exporter dialog boxes and <i>attribute dialog boxes</i>;
+	<LI> it can register exporter HIDs in PCB
+</UL>
+
+<H2> Building a custom dialog box</H2>
+First hid_create() needs to be called. It returns a new hid_t, which is
+an opaq structure for the package that is used to describe attributes of
+an attribute dialog or an exporter hid. Attributes are added using
+hid_add_attribute(), which returns an unique ID of the attribute. The
+attribute ID can be used later for querying attribute value set by the
+user using hid_get_attribute().
+<p>
+The process of building a dialog box is closed by a call to
+<ul>
+	<li> hid_register(), which registers a new exporter hid using the
+	     attributes of the dialog box; or 
+	<li> dialog_attribute() (of the dialog package) which pops up a custom
+	     dialog box immediately.
+</ul>
+
+<H2> Registering an exporter</H2>
+	Function hid_register() registers the hid as an exporter. Should be
+	called after all attributes have been added using hid_add_atrtibute().
+	The export is coordinated by pcb core; when the user request an export
+	using the exporter, a series of events are delivered to the script:
+	<ol>
+		<li> envelope events to set up exporting
+		<li> many draw events to actually export the objects
+		<li> an envelope event to finish exporting
+	</ol>
+
+	<H3> Envelope: events generated before or after exporting </H3>
+		<UL>
+			<LI>HIDE_get_export_options(void *hid): Generated before get_exporter_options returns the option list to the GUI hid
+			<LI>HIDE_do_export_start(void *hid): Generated before export redraw starts
+			<LI>HIDE_do_export_finish(void *hid): Generated after export redraw finihsed
+		</UL>
+
+	<H3> Drawing: events generated during exporting </H3>
+		Note: there may be multiple <I>gc</I>s (graphic contexts), each having its own color, line
+		properties, xor drawing and faded state. Graphic contexts are created
+		and destroyed by the following events:
+		<UL>
+			<LI>HIDE_make_gc(void *hid, void *gc);
+			<LI>HIDE_destroy_gc(void *hid, void *gc);
+		</UL>
+
+		<I>Gc</I> properties are changed by the following events:
+		<UL>
+			<LI>HIDE_set_layer(void *hid, const char *name, int group);
+			<LI>HIDE_set_color(void *hid, void *gc, const char *name);
+			<LI>HIDE_set_line_cap(void *hid, void *gc, EndCapStyle style);
+			<LI>HIDE_set_line_width(void *hid, void *gc, int width);
+			<LI>HIDE_set_draw_xor(void *hid, void *gc, int xor);
+			<LI>HIDE_set_draw_faded(void *hid, void *gc, int faded);
+		</UL>
+
+		Finally, the actual drawing operations:
+		<UL>
+			<LI>HIDE_draw_line(void *hid, void *gc, int x1, int y1, int x2, int y2);
+			<LI>HIDE_draw_arc(void *hid, void *gc, int cx, int cy, int xradius, int yradius, int start_angle, int delta_angle);
+			<LI>HIDE_draw_rect(void *hid, void *gc, int x1, int y1, int x2, int y2);
+			<LI>HIDE_fill_circle(void *hid, void *gc, int cx, int cy, int radius);
+			<LI>HIDE_fill_polygon(void *hid, void *gc, int n_coords, int *x, int *y);
+			<LI>HIDE_fill_rect(void *hid, void *gc, int x1, int y1, int x2, int y2);
+			<LI>HIDE_use_mask(void *hid, int use_it); [TODO]
+		</UL>
+
+</BODY>
+</HTML>
diff --git a/doc-rnd/gpmi/packages/hid_ref.html b/doc-rnd/gpmi/packages/hid_ref.html
new file mode 100644
index 0000000..079b243
--- /dev/null
+++ b/doc-rnd/gpmi/packages/hid_ref.html
@@ -0,0 +1,205 @@
+<html>
+<body>
+<h1>PCB GPMI</h1>
+<h2>Reference manual for package hid</h2>
+<small>Automatically generated from hid.h</small>
+
+<h3> Enums </h3>
+<dl>
+<p>Enum values should be passed on as strings.
+<a id="hid_attr_type_e">
+<H4> hid_attr_type_e</H4>
+<pre>
+ Type of an HID attribute (usually a widget on an attribute dialog box) 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> HIDA_Label <td>  non-editable label displayed on the GUI 
+<tr><td> HIDA_Integer <td>  a sugned integer value 
+<tr><td> HIDA_Real <td>  a floating point value 
+<tr><td> HIDA_String <td>  one line textual input 
+<tr><td> HIDA_Boolean <td>  true/false boolean value 
+<tr><td> HIDA_Enum <td>  select an item of a predefined list 
+<tr><td> HIDA_Mixed <td>  TODO 
+<tr><td> HIDA_Path <td>  path to a file or directory 
+<tr><td> HIDA_Unit <td>  select a dimension unit 
+<tr><td> HIDA_Coord <td>  enter a coordinate 
+</table>
+<a id="EndCapStyle_e">
+<H4> EndCapStyle_e</H4>
+<pre>
+ Line or arc ending style 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> Trace_Cap <td>  filled circle (trace drawing) 
+<tr><td> Square_Cap <td>  rectangular lines (square pad) 
+<tr><td> Round_Cap <td>  round pins or round-ended pads, thermals 
+<tr><td> Beveled_Cap <td>  octagon pins or bevel-cornered pads 
+</table>
+</dl>
+<h3> Events </h3>
+<dl>
+<p>Events do not have return value. The first argument is always <a href="event_id.html">the even id</a>. Event handlers defined in scripts get all event arguments converted to string (types below are informational).
+<a id="HIDE_get_export_options">
+<H4> HIDE_get_export_options(int event_id, void* hid) </H4>
+<pre>
+ Called before get_exporter_options returns the option list to the GUI hid 
+</pre>
+<a id="HIDE_do_export_start">
+<H4> HIDE_do_export_start(int event_id, void* hid) </H4>
+<pre>
+ Called before export redraw starts 
+</pre>
+<a id="HIDE_do_export_finish">
+<H4> HIDE_do_export_finish(int event_id, void* hid) </H4>
+<pre>
+ Called after export redraw finihsed 
+</pre>
+<a id="HIDE_set_layer">
+<H4> HIDE_set_layer(int event_id, void* hid, const char* name, int group, int empty) </H4>
+<pre>
+ PCB callback events for drawing: change layer 
+</pre>
+<a id="HIDE_set_color">
+<H4> HIDE_set_color(int event_id, void* hid, void* gc, const char* name) </H4>
+<pre>
+ PCB callback events for drawing: change drawing color 
+</pre>
+<a id="HIDE_set_line_cap">
+<H4> HIDE_set_line_cap(int event_id, void* hid, void* gc, EndCapStyle style) </H4>
+<pre>
+ PCB callback events for drawing: change drawing line cap style
+</pre>
+<a id="HIDE_set_line_width">
+<H4> HIDE_set_line_width(int event_id, void* hid, void* gc, int width) </H4>
+<pre>
+ PCB callback events for drawing: change drawing line width 
+</pre>
+<a id="HIDE_set_draw_xor">
+<H4> HIDE_set_draw_xor(int event_id, void* hid, void* gc, int xor) </H4>
+<pre>
+ PCB callback events for drawing: toggle xor drawing method 
+</pre>
+<a id="HIDE_set_draw_faded">
+<H4> HIDE_set_draw_faded(int event_id, void* hid, void* gc, int faded) </H4>
+<pre>
+ PCB callback events for drawing: toggle faded drawing method 
+</pre>
+<a id="HIDE_draw_line">
+<H4> HIDE_draw_line(int event_id, void* hid, void* gc, int x1, int y1, int x2, int y2) </H4>
+<pre>
+ PCB callback events for drawing: draw a line 
+</pre>
+<a id="HIDE_draw_arc">
+<H4> HIDE_draw_arc(int event_id, void* hid, void* gc, int cx, int cy, int xradius, int yradius, double start_angle, double delta_angle) </H4>
+<pre>
+ PCB callback events for drawing: draw an arc from center cx;cy 
+</pre>
+<a id="HIDE_draw_rect">
+<H4> HIDE_draw_rect(int event_id, void* hid, void* gc, int x1, int y1, int x2, int y2) </H4>
+<pre>
+ PCB callback events for drawing: draw a rectangle 
+</pre>
+<a id="HIDE_fill_circle">
+<H4> HIDE_fill_circle(int event_id, void* hid, void* gc, int cx, int cy, int radius) </H4>
+<pre>
+ PCB callback events for drawing: draw a filled circle 
+</pre>
+<a id="HIDE_fill_polygon">
+<H4> HIDE_fill_polygon(int event_id, void* hid, void* gc, int n_coords, int* x, int* y) </H4>
+<pre>
+ PCB callback events for drawing: draw a filled ploygon 
+</pre>
+<a id="HIDE_fill_rect">
+<H4> HIDE_fill_rect(int event_id, void* hid, void* gc, int x1, int y1, int x2, int y2) </H4>
+<pre>
+ PCB callback events for drawing: draw a filled rectangle 
+</pre>
+<a id="HIDE_use_mask">
+<H4> HIDE_use_mask(int event_id, void* hid, int use_it) </H4>
+<pre>
+ PCB callback events for drawing: TODO 
+</pre>
+<a id="HIDE_make_gc">
+<H4> HIDE_make_gc(int event_id, void* hid, void* gc) </H4>
+<pre>
+ PCB callback events for drawing: create a new graphical context 
+</pre>
+<a id="HIDE_destroy_gc">
+<H4> HIDE_destroy_gc(int event_id, void* hid, void* gc) </H4>
+<pre>
+ PCB callback events for drawing: destroy a graphical context 
+</pre>
+<a id="HIDE_fill_pcb_pv">
+<H4> HIDE_fill_pcb_pv(int event_id, void* hid, void* fg_gc, void* bg_gc, void* pad, int drawHole, int mask) </H4>
+<pre>
+ PCB callback events for drawing: TODO 
+</pre>
+<a id="HIDE_fill_pcb_pad">
+<H4> HIDE_fill_pcb_pad(int event_id, void* hid, void* pad, int clear, int mask) </H4>
+<pre>
+ PCB callback events for drawing: TODO 
+</pre>
+</dl>
+<h3> Functions </h3>
+<dl>
+<p>The following functions are registered in script context.
+<a id="hid_create">
+<H4>  hid_t* hid_create(char* hid_name, char* description)  </H4>
+<pre>
+ Creates a new hid context. Name and description matters only if the hid is
+registered as an exporter later. 
+</pre>
+<a id="hid_add_attribute">
+<H4>  int hid_add_attribute(hid_t* hid, char* attr_name, char* help, hid_attr_type_t type, int min, int max, char* default_val)  </H4>
+<pre>
+ Append an attribute in a hid previously created using hid_create().
+   Arguments:
+     hid: hid_t previously created using hid_create()
+     attr_name: name of the attribute
+     help: help text for the attribute
+     type: type of the attribute (input widget type)
+     min: minimum value of the attribute, if type is integer or real)
+     max: maximum value of the attribute, if type is integer or real)
+     default_val: default value of the attribute
+  Returns an unique ID of the attribute the caller should store for
+  later reference. For example this ID is used when retrieving the
+  value of the attribute after the user finished entering data in
+  the dialog. 
+</pre>
+<a id="hid_get_attribute">
+<H4>  char* hid_get_attribute(hid_t* hid, int attr_id)  </H4>
+<pre>
+ Query an attribute from the hid after dialog_attributes() returned.
+   Arguments:
+     hid: hid_t previously created using hid_create()
+     attr_id: the unique ID of the attribute (returned by hid_add_attribute())
+   Returns the value (converted to string) set by the user. 
+</pre>
+<a id="hid_register">
+<H4>  int hid_register(hid_t* hid)  </H4>
+<pre>
+ Register the hid; call it after a hid is created and its attributes
+   are all set up 
+</pre>
+<a id="hid_gpmi_data_set">
+<H4>  void hid_gpmi_data_set(hid_t* h, void* data)  </H4>
+<pre>
+ For internal use 
+</pre>
+<a id="hid_gpmi_data_get">
+<H4>  hid_t* hid_gpmi_data_get(HID* h)  </H4>
+<pre>
+ For internal use 
+</pre>
+<a id="hid_string2val">
+<H4>  HID_Attr_Val hid_string2val(const hid_attr_type_t type, const char* str)  </H4>
+<pre>
+ For internal use 
+</pre>
+</dl>
+
+</body>
+</html>
+
diff --git a/doc-rnd/gpmi/packages/layout.html b/doc-rnd/gpmi/packages/layout.html
new file mode 100644
index 0000000..0e71575
--- /dev/null
+++ b/doc-rnd/gpmi/packages/layout.html
@@ -0,0 +1,58 @@
+<HTML>
+<BODY>
+<H1> layout package </H1>
+
+Layout package searches and manipulates the current layout. Dimension units
+are in nanometer unless otherwise mentioned.
+
+<H2> Page: board dimensions</H2>
+	Functions used to query or set width and height of the drawing:
+	<UL>
+		<LI>int layout_get_page_width();
+		<LI>int layout_get_page_height();
+		<LI>void layout_set_page_size(int width, int height);
+	</UL>
+
+<H2> Layer manipulation </H2>
+Most functions perform operations similar to user commands affecting
+the current layer.
+The following few calls can change the current layer. Warning: this is the
+same current layer as the user's; the script most probably wants to save
+the current layer before changing it and then restore it after the operation.
+	<UL>
+		<LI>int layout_resolve_layer(const char *name): resolve layer number by name (case sensitive); returns negative number if not found
+		<LI>int layout_get_current_layer(): returns the number of the current layer
+		<LI>void layout_switch_to_layer(int layer): switch to layer (further actions will take place there)
+	</UL>
+<p>
+
+<H2> Object searches </H2>
+	Search results are collected on lists identified by their name (search_ID).
+	If a new search is done with the same name, old search results for that
+	name are discarded.
+	Search functions return the number of objects found (size of the list) that can 
+	be then used while querying the results using layout_search_get().
+	Results should be freed using layout_search_free() when they are no
+	longer needed.
+	<p>
+	TODO: validity of a list in time
+	<p>
+Once the search list is ready, the script can iterate over it and resolve
+the object handle of each object found, using layout_search_get(). Having
+an object handle,  The layout_obj_*() functions may be used by the script
+to access fields of an object structure.
+
+<H2>Create new objects </H2>
+	The layour_create_*() calls are used to create new objects on the current
+	layer (set by layout_switch_to_layer()). 
+
+
+<H2> API reference - page, untis and coordinates </H2>
+(angles are in radian)
+TODO
+
+<H2> API reference - debug draw </H2>
+TODO
+
+</BODY>
+</HTML>
diff --git a/doc-rnd/gpmi/packages/layout_ref.html b/doc-rnd/gpmi/packages/layout_ref.html
new file mode 100644
index 0000000..1b7b5b0
--- /dev/null
+++ b/doc-rnd/gpmi/packages/layout_ref.html
@@ -0,0 +1,305 @@
+<html>
+<body>
+<h1>PCB GPMI</h1>
+<h2>Reference manual for package layout</h2>
+<small>Automatically generated from layout.h</small>
+
+<h3> Enums </h3>
+<dl>
+<p>Enum values should be passed on as strings.
+<a id="layout_object_mask_e">
+<H4> layout_object_mask_e</H4>
+<pre>
+ Object type search mask bits 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> OM_LINE <td>  lines (traces, silk lines, not font) 
+<tr><td> OM_TEXT <td>  text written using the font 
+<tr><td> OM_POLYGON <td>  polygons, including rectangles 
+<tr><td> OM_ARC <td>  arcs, circles 
+<tr><td> OM_VIA <td>  vias and holes which are not part of a footprint 
+<tr><td> OM_PIN <td>  pins/pads of a footprint 
+<tr><td> OM_ANY <td>  shorthand for "find anything" 
+</table>
+<a id="layout_object_coord_e">
+<H4> layout_object_coord_e</H4>
+<pre>
+ Which coordinate of the object is referenced 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> OC_BX1 <td>  bounding box X1 
+<tr><td> OC_BX2 <td>  bounding box X2 
+<tr><td> OC_BY1 <td>  bounding box Y1 
+<tr><td> OC_BY2 <td>  bounding box Y2 
+<tr><td> OC_P1X <td>  point 1 X 
+<tr><td> OC_P2X <td>  point 2 X 
+<tr><td> OC_P1Y <td>  point 1 Y 
+<tr><td> OC_P2Y <td>  point 2 Y 
+<tr><td> OC_OBJ <td>  the whole object 
+<tr><td> OC_P1 <td>  point 1 is P1X
+<tr><td> OC_P2 <td>  point 2 is P2X 
+</table>
+<a id="layout_flag_e">
+<H4> layout_flag_e</H4>
+<pre>
+ of layout_object_coord_t 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> FL_NONE <td> <comment missing in the header>
+<tr><td> FL_SHOWNUMBER <td> <comment missing in the header>
+<tr><td> FL_LOCALREF <td> <comment missing in the header>
+<tr><td> FL_CHECKPLANS <td> <comment missing in the header>
+<tr><td> FL_SHOWDRC <td> <comment missing in the header>
+<tr><td> FL_RUBBERBAND <td> <comment missing in the header>
+<tr><td> FL_DESCRIPTION <td> <comment missing in the header>
+<tr><td> FL_NAMEONPCB <td> <comment missing in the header>
+<tr><td> FL_AUTODRC <td> <comment missing in the header>
+<tr><td> FL_ALLDIRECTION <td> <comment missing in the header>
+<tr><td> FL_SWAPSTARTDIR <td> <comment missing in the header>
+<tr><td> FL_UNIQUENAME <td> <comment missing in the header>
+<tr><td> FL_CLEARNEW <td> <comment missing in the header>
+<tr><td> FL_SNAPPIN <td> <comment missing in the header>
+<tr><td> FL_SHOWMASK <td> <comment missing in the header>
+<tr><td> FL_THINDRAW <td> <comment missing in the header>
+<tr><td> FL_ORTHOMOVE <td> <comment missing in the header>
+<tr><td> FL_LIVEROUTE <td> <comment missing in the header>
+<tr><td> FL_THINDRAWPOLY <td> <comment missing in the header>
+<tr><td> FL_LOCKNAMES <td> <comment missing in the header>
+<tr><td> FL_ONLYNAMES <td> <comment missing in the header>
+<tr><td> FL_NEWFULLPOLY <td> <comment missing in the header>
+<tr><td> FL_HIDENAMES <td> <comment missing in the header>
+<tr><td> FL_THERMALSTYLE1 <td> <comment missing in the header>
+<tr><td> FL_THERMALSTYLE2 <td> <comment missing in the header>
+<tr><td> FL_THERMALSTYLE3 <td> <comment missing in the header>
+<tr><td> FL_THERMALSTYLE4 <td> <comment missing in the header>
+<tr><td> FL_THERMALSTYLE5 <td> <comment missing in the header>
+</table>
+<a id="layer_field_e">
+<H4> layer_field_e</H4>
+<pre>
+ Field name of the layer structure 
+</pre>
+<table border=1>
+<tr><th>value <th>meaning
+<tr><td> LFLD_NUM_LINES <td>  number of lines on the layer 
+<tr><td> LFLD_NUM_TEXTS <td>  number of texts on the layer 
+<tr><td> LFLD_NUM_POLYS <td>  number of polygons on the layer 
+<tr><td> LFLD_NUM_ARCS <td>  number of arcs on the layer 
+<tr><td> LFLD_VISIBLE <td>  non-zero if the layer is visible 
+<tr><td> LFLD_NODRC <td>  non-zero if the layer doesn't use DRC 
+</table>
+</dl>
+<h3> Functions </h3>
+<dl>
+<p>The following functions are registered in script context.
+<a id="layout_search_box">
+<H4>  int layout_search_box(const char* search_ID, layout_object_mask_t obj_types, int x1, int y1, int x2, int y2)  </H4>
+<pre>
+ creates a new search and adds all objects that matches obj_types mask within the given rectangle on the current layer
+   Arguments:
+     search_ID: unique name of the search (overwrites existing search on the same name)
+     obj_types: on or more object types
+     x1, y1, x2, y2: box the search is done within (PCB coords)
+   Returns the number of object on the search list. 
+</pre>
+<a id="layout_search_selected">
+<H4>  int layout_search_selected(const char* search_ID, layout_object_mask_t obj_types)  </H4>
+<pre>
+ creates a new search and adds all selected objects
+   Arguments:
+     search_ID: unique name of the search (overwrites existing search on the same name)
+     obj_types: on or more object types
+   Returns the number of object on the search list. 
+</pre>
+<a id="layout_search_found">
+<H4>  int layout_search_found(const char* search_ID, layout_object_mask_t obj_types)  </H4>
+<pre>
+ creates a new search and adds all found objects (the green highlight)
+   Arguments:
+     search_ID: unique name of the search (overwrites existing search on the same name)
+     obj_types: on or more object types
+   Returns the number of object on the search list. 
+</pre>
+<a id="layout_search_get">
+<H4>  layout_object_t* layout_search_get(const char* search_ID, int n)  </H4>
+<pre>
+ Returns the nth object from a search list (or NULL pointer if n is beyond the list) 
+</pre>
+<a id="layout_search_free">
+<H4>  int layout_search_free(const char* search_ID)  </H4>
+<pre>
+ Frees all memory related to a search. Returns 0 on success.
+   Argument:
+     search_ID: unique name of the search (requires an existing search) 
+</pre>
+<a id="layout_obj_coord">
+<H4>  int layout_obj_coord(layout_object_t* obj, layout_object_coord_t coord)  </H4>
+<pre>
+ Return the requested coord of an object; except for the bounding box
+    coordinates, the meaning of coordinates are object-specific.
+    Point 1 and point 2 are usually endpoints of the object (line, arc),
+    "the whole object" coordinate is a central point. 
+</pre>
+<a id="layout_obj_type">
+<H4>  layout_object_mask_t layout_obj_type(layout_object_t* obj)  </H4>
+<pre>
+ Return the type of an object (always a single bit) 
+</pre>
+<a id="layout_obj_move">
+<H4>  int layout_obj_move(layout_object_t* obj, layout_object_coord_t coord, int dx, int dy)  </H4>
+<pre>
+ Change location of an object or parts of the object (like move endpoint of a line);
+   Arguments:
+     obj: the object
+     coord: which coordinate to drag (e.g. move only the endpoint)
+     dx, dy: relative x and y coordinates the selected coordinate is displaced by
+   Returns 0 on success 
+</pre>
+<a id="layout_arc_angles">
+<H4>  int layout_arc_angles(layout_object_t* obj, int relative, int start, int delta)  </H4>
+<pre>
+ change angles of an arc; start and delate are relative if relative is non-zero; returns 0 on success 
+</pre>
+<a id="layout_create_line">
+<H4>  int layout_create_line(int x1, int y1, int x2, int y2, int thickness, int clearance, layout_flag_t flags)  </H4>
+<pre>
+ create a line 
+</pre>
+<a id="layout_create_via">
+<H4>  int layout_create_via(int x, int y, int thickness, int clearance, int mask, int hole, const char* name, layout_flag_t flags)  </H4>
+<pre>
+ create a named via 
+</pre>
+<a id="layout_create_arc">
+<H4>  int layout_create_arc(int x, int y, int width, int height, int sa, int dir, int thickness, int clearance, layout_flag_t flags)  </H4>
+<pre>
+ create a new arc; sa is start angle, dir is delta angle 
+</pre>
+<a id="layout_switch_to_layer">
+<H4>  void layout_switch_to_layer(int layer)  </H4>
+<pre>
+ switch to layer (further layer-specific actions will take place there) 
+</pre>
+<a id="layout_get_current_layer">
+<H4>  int layout_get_current_layer()  </H4>
+<pre>
+ returns the number of the current layer 
+</pre>
+<a id="layout_resolve_layer">
+<H4>  int layout_resolve_layer(const char* name)  </H4>
+<pre>
+ resolve layer number by name (case sensitive); returns negative number if not found 
+</pre>
+<a id="layout_get_max_possible_layer">
+<H4>  int layout_get_max_possible_layer()  </H4>
+<pre>
+ return the theoretical number of layers supported by PCB 
+</pre>
+<a id="layout_get_max_copper_layer">
+<H4>  int layout_get_max_copper_layer()  </H4>
+<pre>
+ return the actual number of copper layers on the current design 
+</pre>
+<a id="layout_get_max_layer">
+<H4>  int layout_get_max_layer()  </H4>
+<pre>
+ return the actual number of layers on the current design 
+</pre>
+<a id="layout_layer_name">
+<H4>  const char* layout_layer_name(int layer)  </H4>
+<pre>
+ return the name of a layer 
+</pre>
+<a id="layout_layer_color">
+<H4>  const char* layout_layer_color(int layer)  </H4>
+<pre>
+ return the color of a layer 
+</pre>
+<a id="layout_layer_field">
+<H4>  int layout_layer_field(int layer, layer_field_t fld)  </H4>
+<pre>
+ return an integer field of a layer 
+</pre>
+<a id="layout_get_page_width">
+<H4>  int layout_get_page_width()  </H4>
+<pre>
+ query or set width and height of the drawing 
+</pre>
+<a id="layout_get_page_height">
+<H4>  int layout_get_page_height()  </H4>
+<pre>
+<comment missing in the header>
+</pre>
+<a id="layout_set_page_size">
+<H4>  void layout_set_page_size(int width, int height)  </H4>
+<pre>
+<comment missing in the header>
+</pre>
+<a id="mil2pcb_multiplier">
+<H4>  double mil2pcb_multiplier()  </H4>
+<pre>
+ -- coordinate system -- (coord.c) 
+</pre>
+<a id="mm2pcb_multiplier">
+<H4>  double mm2pcb_multiplier()  </H4>
+<pre>
+<comment missing in the header>
+</pre>
+<a id="current_grid_unit">
+<H4>  const char* current_grid_unit()  </H4>
+<pre>
+<comment missing in the header>
+</pre>
+<a id="debug_draw_request">
+<H4>  int debug_draw_request(void)  </H4>
+<pre>
+ Initialize debug drawing; returns 1 if worked, 0 if denied 
+</pre>
+<a id="debug_draw_flush">
+<H4>  void debug_draw_flush(void)  </H4>
+<pre>
+ Flush the drawing 
+</pre>
+<a id="debug_draw_finish">
+<H4>  void debug_draw_finish(dctx_t* ctx)  </H4>
+<pre>
+ Finish (close) drawing 
+</pre>
+<a id="debug_draw_dctx">
+<H4>  dctx_t* debug_draw_dctx(void)  </H4>
+<pre>
+ Get the draw context of debug draw 
+</pre>
+<a id="draw_set_color">
+<H4>  void draw_set_color(dctx_t* ctx, const char* name)  </H4>
+<pre>
+ Debug draw style: set drawing color 
+</pre>
+<a id="draw_set_line_width">
+<H4>  void draw_set_line_width(dctx_t* ctx, int width)  </H4>
+<pre>
+ Debug draw style: set line width 
+</pre>
+<a id="draw_set_draw_xor">
+<H4>  void draw_set_draw_xor(dctx_t* ctx, int xor)  </H4>
+<pre>
+ Debug draw style: set whether drawing should happen in xor 
+</pre>
+<a id="draw_set_draw_faded">
+<H4>  void draw_set_draw_faded(dctx_t* ctx, int faded)  </H4>
+<pre>
+ Debug draw style: set whether drawing should happen in faded mode  
+</pre>
+<a id="draw_line">
+<H4>  void draw_line(dctx_t* ctx, int x1_, int y1_, int x2_, int y2_)  </H4>
+<pre>
+ Debug draw: draw a line using the current style settings 
+</pre>
+</dl>
+
+</body>
+</html>
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ID.desc b/doc-rnd/gpmi/rosetta/10_hello/ID.desc
new file mode 100644
index 0000000..a8dd6d4
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ID.desc
@@ -0,0 +1,2 @@
+Create a new action hello() that prints "Hello world!" in the message log.
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ID.name b/doc-rnd/gpmi/rosetta/10_hello/ID.name
new file mode 100644
index 0000000..64f65d7
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ID.name
@@ -0,0 +1 @@
+hello world (text, log)
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.awk b/doc-rnd/gpmi/rosetta/10_hello/ex.awk
new file mode 100644
index 0000000..2fd0793
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.awk
@@ -0,0 +1,14 @@
+BEGIN {
+	PkgLoad("pcb-rnd-gpmi/actions", 0);
+	PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+}
+
+function ev_action(id, name, argc, x, y)
+{
+	dialog_log("Hello world!\n");
+}
+
+BEGIN {
+	Bind("ACTE_action", "ev_action");
+	action_register("hello", "", "log hello world", "hello()");
+}
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.bash b/doc-rnd/gpmi/rosetta/10_hello/ex.bash
new file mode 100644
index 0000000..d1d5b79
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.bash
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+function load_packages()
+{
+	GPMI PkgLoad "pcb-rnd-gpmi/actions" 0
+	GPMI PkgLoad "pcb-rnd-gpmi/dialogs" 0
+}
+
+function ev_action() {
+	GPMI dialog_log "Hello world!\n"
+}
+
+function main()
+{
+	load_packages
+	GPMI Bind "ACTE_action"  "ev_action"
+	GPMI action_register "hello" "" "log hello world" "hello()"
+}
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.html b/doc-rnd/gpmi/rosetta/10_hello/ex.html
new file mode 100644
index 0000000..cc89929
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.html
@@ -0,0 +1,25 @@
+Load packages the script depends on:
+<ul>
+	<li> <ref>actions</ref> for registering the new action;
+	<li> <ref>dialogs</ref> for printing in the message log.
+</ul>
+<p>
+Create a function <i>ev_action</i> that will be called when any of
+the actions registered by the script is executed. The script registers
+only one action, so it does not need to check which action caused
+the function to be called.
+<p>
+When the action event is called, use <ref>dialog_log</ref> to append
+a log message.
+<p>
+In the "main" section of the script, bind event <ref>ACTE_action</ref> to
+our local function <i>ev_action</i> - this gets <i>ev_action</i> to
+be called when any of the actions registered by this script is executed.
+<p>
+Finally use <ref>action_register</ref> to register the action:
+<ul>
+	<li> first argument is the name of the action
+	<li> second is the xy query string; it is empty since this action won't need coordinates to operate on
+	<li> third is the description that shows up in dumps and helps
+	<li> fourth is the syntax description (also for helps)
+</ul>
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.lua b/doc-rnd/gpmi/rosetta/10_hello/ex.lua
new file mode 100644
index 0000000..923397d
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.lua
@@ -0,0 +1,9 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+function ev_action(id, name, argc, x, y)
+	dialog_log("Hello world!\n");
+end
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.pl b/doc-rnd/gpmi/rosetta/10_hello/ex.pl
new file mode 100644
index 0000000..7ddb0ea
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.pl
@@ -0,0 +1,10 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+sub ev_action {
+	my($id, $name, $argc, $x, $y) = @_;
+	dialog_log("Hello world!\n");
+}
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.py b/doc-rnd/gpmi/rosetta/10_hello/ex.py
new file mode 100644
index 0000000..d0b58da
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.py
@@ -0,0 +1,8 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+def ev_action(id, name, argc, x, y):
+	dialog_log("Hello world!\n");
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.rb b/doc-rnd/gpmi/rosetta/10_hello/ex.rb
new file mode 100644
index 0000000..e58b35c
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.rb
@@ -0,0 +1,9 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+def ev_action(id, name, argc, x, y)
+	dialog_log("Hello world!\n");
+end
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.scm b/doc-rnd/gpmi/rosetta/10_hello/ex.scm
new file mode 100644
index 0000000..e6c59f8
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.scm
@@ -0,0 +1,9 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/dialogs" 0)
+
+(define ev_action (lambda (id name argc x y)
+  (dialog_log "Hello world!\n")))
+
+(Bind "ACTE_action" "ev_action")
+(action_register "hello" "" "log hello world" "hello()")
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.stt b/doc-rnd/gpmi/rosetta/10_hello/ex.stt
new file mode 100644
index 0000000..fb131c0
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.stt
@@ -0,0 +1,10 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/dialogs" 0)
+
+(defun ev_action (id name argc x y)
+	(dialog_log "Hello world!\n"))
+
+(Bind "ACTE_action" "ev_action")
+(action_register "hello" "" "log hello world" "hello()")
+
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello/ex.tcl b/doc-rnd/gpmi/rosetta/10_hello/ex.tcl
new file mode 100644
index 0000000..b4f14f3
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/ex.tcl
@@ -0,0 +1,10 @@
+PkgLoad pcb-rnd-gpmi/actions 0
+PkgLoad pcb-rnd-gpmi/dialogs 0
+
+proc ev_action {id, name, argc, x, y} {
+	dialog_log "Hello world!\n"
+}
+
+Bind ACTE_action ev_action
+action_register "hello" "" "log hello world" "hello()"
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello/index.html b/doc-rnd/gpmi/rosetta/10_hello/index.html
new file mode 100644
index 0000000..bbe7865
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello/index.html
@@ -0,0 +1,55 @@
+<html><body>
+<!-- ******* DO NOT EDIT THIS FILE, it is generated by rosetta_genpages.sh ******* -->
+<-- back to the <a href="../index.html">index of Rosetta examples</a>
+<h1>hello world (text, log)</h1>
+Create a new action hello() that prints "Hello world!" in the message log.
+<H2> Example implementations </H2>
+<a href="ex.awk">awk</a>
+ | 
+<a href="ex.bash">bash</a>
+ | 
+<a href="ex.lua">lua</a>
+ | 
+<a href="ex.pl">pl</a>
+ | 
+<a href="ex.py">py</a>
+ | 
+<a href="ex.rb">rb</a>
+ | 
+<a href="ex.scm">scm</a>
+ | 
+<a href="ex.stt">stt</a>
+ | 
+<a href="ex.tcl">tcl</a>
+<h2> Explanation, step by step </h2>
+Load packages the script depends on: 
+<ul> 	
+<li> 
+<i>actions</i>
+ for registering the new action; 	
+<li> 
+<i>dialogs</i>
+ for printing in the message log. </ul>
+ 
+<p> Create a function 
+<i>ev_action</i>
+ that will be called when any of the actions registered by the script is executed. The script registers only one action, so it does not need to check which action caused the function to be called. 
+<p> When the action event is called, use 
+<i><a href="../../packages/dialogs_ref.html#dialog_log">dialog_log</a></i>
+ to append a log message. 
+<p> In the "main" section of the script, bind event 
+<i><a href="../../packages/actions_ref.html#ACTE_action">ACTE_action</a></i>
+ to our local function 
+<i>ev_action</i>
+ - this gets 
+<i>ev_action</i>
+ to be called when any of the actions registered by this script is executed. 
+<p> Finally use 
+<i><a href="../../packages/actions_ref.html#action_register">action_register</a></i>
+ to register the action: 
+<ul> 	
+<li> first argument is the name of the action 	
+<li> second is the xy query string; it is empty since this action won't need coordinates to operate on 	
+<li> third is the description that shows up in dumps and helps 	
+<li> fourth is the syntax description (also for helps) </ul>
+ 
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ID.desc b/doc-rnd/gpmi/rosetta/10_hello_gui/ID.desc
new file mode 100644
index 0000000..ca74d1f
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ID.desc
@@ -0,0 +1,3 @@
+Create a new action hello() that prints "Hello world!" in a popup window.
+
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ID.name b/doc-rnd/gpmi/rosetta/10_hello_gui/ID.name
new file mode 100644
index 0000000..b1a052f
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ID.name
@@ -0,0 +1 @@
+hello world (popup window)
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.awk b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.awk
new file mode 100644
index 0000000..a395acb
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.awk
@@ -0,0 +1,14 @@
+BEGIN {
+	PkgLoad("pcb-rnd-gpmi/actions", 0);
+	PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+}
+
+function ev_action(id, name, argc, x, y)
+{
+	dialog_report("Greeting window", "Hello world!");
+}
+
+BEGIN {
+	Bind("ACTE_action", "ev_action");
+	action_register("hello", "", "log hello world", "hello()");
+}
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.bash b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.bash
new file mode 100644
index 0000000..c2de3ae
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.bash
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+function load_packages()
+{
+	GPMI PkgLoad "pcb-rnd-gpmi/actions" 0
+	GPMI PkgLoad "pcb-rnd-gpmi/dialogs" 0
+}
+
+function ev_action() {
+	GPMI dialog_report "Greeting window" "Hello world!"
+}
+
+function main()
+{
+	load_packages
+	GPMI Bind "ACTE_action"  "ev_action"
+	GPMI action_register "hello" "" "log hello world" "hello()"
+}
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.html b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.html
new file mode 100644
index 0000000..ac0c77d
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.html
@@ -0,0 +1,26 @@
+Load packages the script depends on:
+<ul>
+	<li> <ref>actions</ref> for registering the new action;
+	<li> <ref>dialogs</ref> for creating a dialog box.
+</ul>
+<p>
+Create a function <i>ev_action</i> that will be called when any of
+the actions registered by the script is executed. The script registers
+only one action, so it does not need to check which action caused
+the function to be called.
+<p>
+When the action event is called, use <ref>dialog_report</ref> to pop
+up a report dialog. First argument donates the name (title) of the window,
+the second is the text printed in the window.
+<p>
+In the "main" section of the script, bind event <ref>ACTE_action</ref> to
+our local function <i>ev_action</i> - this gets <i>ev_action</i> to
+be called when any of the actions registered by this script is executed.
+<p>
+Finally use <ref>action_register</ref> to register the action:
+<ul>
+	<li> first argument is the name of the action
+	<li> second is the xy query string; it is empty since this action won't need coordinates to operate on
+	<li> third is the description that shows up in dumps and helps
+	<li> fourth is the syntax description (also for helps)
+</ul>
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.lua b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.lua
new file mode 100644
index 0000000..184d63b
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.lua
@@ -0,0 +1,9 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+function ev_action(id, name, argc, x, y)
+	dialog_report("Greeting window", "Hello world!");
+end
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.pl b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.pl
new file mode 100644
index 0000000..eb84ed0
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.pl
@@ -0,0 +1,10 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+sub ev_action {
+	my($id, $name, $argc, $x, $y) = @_;
+	dialog_report("Greeting window", "Hello world!");
+}
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.py b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.py
new file mode 100644
index 0000000..f54ab57
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.py
@@ -0,0 +1,8 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+def ev_action(id, name, argc, x, y):
+	dialog_report("Greeting window", "Hello world!");
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.rb b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.rb
new file mode 100644
index 0000000..a8fa4c9
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.rb
@@ -0,0 +1,9 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+def ev_action(id, name, argc, x, y)
+	dialog_report("Greeting window", "Hello world!");
+end
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.scm b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.scm
new file mode 100644
index 0000000..92fec6d
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.scm
@@ -0,0 +1,9 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/dialogs" 0)
+
+(define ev_action (lambda (id name argc x y)
+  (dialog_report "Greeting window" "Hello world!")))
+
+(Bind "ACTE_action" "ev_action")
+(action_register "hello" "" "log hello world" "hello()")
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.stt b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.stt
new file mode 100644
index 0000000..b24e559
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.stt
@@ -0,0 +1,10 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/dialogs" 0)
+
+(defun ev_action (id name argc x y)
+	(dialog_report "Greeting window" "Hello world!"))
+
+(Bind "ACTE_action" "ev_action")
+(action_register "hello" "" "log hello world" "hello()")
+
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/ex.tcl b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.tcl
new file mode 100644
index 0000000..15df29f
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/ex.tcl
@@ -0,0 +1,10 @@
+PkgLoad pcb-rnd-gpmi/actions 0
+PkgLoad pcb-rnd-gpmi/dialogs 0
+
+proc ev_action {id, name, argc, x, y} {
+	dialog_report "Greeting window" "Hello world!"
+}
+
+Bind ACTE_action ev_action
+action_register "hello" "" "log hello world" "hello()"
+
diff --git a/doc-rnd/gpmi/rosetta/10_hello_gui/index.html b/doc-rnd/gpmi/rosetta/10_hello_gui/index.html
new file mode 100644
index 0000000..9d1a7b1
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/10_hello_gui/index.html
@@ -0,0 +1,55 @@
+<html><body>
+<!-- ******* DO NOT EDIT THIS FILE, it is generated by rosetta_genpages.sh ******* -->
+<-- back to the <a href="../index.html">index of Rosetta examples</a>
+<h1>hello world (popup window)</h1>
+Create a new action hello() that prints "Hello world!" in a popup window.
+<H2> Example implementations </H2>
+<a href="ex.awk">awk</a>
+ | 
+<a href="ex.bash">bash</a>
+ | 
+<a href="ex.lua">lua</a>
+ | 
+<a href="ex.pl">pl</a>
+ | 
+<a href="ex.py">py</a>
+ | 
+<a href="ex.rb">rb</a>
+ | 
+<a href="ex.scm">scm</a>
+ | 
+<a href="ex.stt">stt</a>
+ | 
+<a href="ex.tcl">tcl</a>
+<h2> Explanation, step by step </h2>
+Load packages the script depends on: 
+<ul> 	
+<li> 
+<i>actions</i>
+ for registering the new action; 	
+<li> 
+<i>dialogs</i>
+ for creating a dialog box. </ul>
+ 
+<p> Create a function 
+<i>ev_action</i>
+ that will be called when any of the actions registered by the script is executed. The script registers only one action, so it does not need to check which action caused the function to be called. 
+<p> When the action event is called, use 
+<i><a href="../../packages/dialogs_ref.html#dialog_report">dialog_report</a></i>
+ to pop up a report dialog. First argument donates the name (title) of the window, the second is the text printed in the window. 
+<p> In the "main" section of the script, bind event 
+<i><a href="../../packages/actions_ref.html#ACTE_action">ACTE_action</a></i>
+ to our local function 
+<i>ev_action</i>
+ - this gets 
+<i>ev_action</i>
+ to be called when any of the actions registered by this script is executed. 
+<p> Finally use 
+<i><a href="../../packages/actions_ref.html#action_register">action_register</a></i>
+ to register the action: 
+<ul> 	
+<li> first argument is the name of the action 	
+<li> second is the xy query string; it is empty since this action won't need coordinates to operate on 	
+<li> third is the description that shows up in dumps and helps 	
+<li> fourth is the syntax description (also for helps) </ul>
+ 
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ID.desc b/doc-rnd/gpmi/rosetta/12_hello_menu/ID.desc
new file mode 100644
index 0000000..1a7d436
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ID.desc
@@ -0,0 +1,3 @@
+Create a new action hello() that prints "Hello world!" in a popup window, create a menu (under "Plugins/GPMI scripting/") that executes the action.
+
+
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ID.name b/doc-rnd/gpmi/rosetta/12_hello_menu/ID.name
new file mode 100644
index 0000000..ef6ad34
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ID.name
@@ -0,0 +1 @@
+hello world (popup window + submenu)
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.awk b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.awk
new file mode 100644
index 0000000..9d38f8f
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.awk
@@ -0,0 +1,20 @@
+BEGIN {
+	PkgLoad("pcb-rnd-gpmi/actions", 0);
+	PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+}
+
+function ev_action(id, name, argc, x, y)
+{
+	dialog_report("Greeting window", "Hello world!");
+}
+
+function ev_gui_init(id, argc, argv)
+{
+	create_menu("/main_menu/Plugins/GPMI scripting/hello", "hello()", "h", "Ctrl<Key>w", "tooltip for hello");
+}
+
+BEGIN {
+	Bind("ACTE_action", "ev_action");
+	Bind("ACTE_gui_init", "ev_gui_init");
+	action_register("hello", "", "log hello world", "hello()");
+}
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.bash b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.bash
new file mode 100644
index 0000000..ef717e6
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.bash
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+function load_packages()
+{
+	GPMI PkgLoad "pcb-rnd-gpmi/actions" 0
+	GPMI PkgLoad "pcb-rnd-gpmi/dialogs" 0
+}
+
+function ev_action() {
+	GPMI dialog_report "Greeting window" "Hello world!"
+}
+
+function ev_gui_init()
+{
+	GPMI create_menu "/main_menu/Plugins/GPMI scripting/hello" "hello()" "h" "Ctrl<Key>w" "tooltip for hello"
+}
+
+function main()
+{
+	load_packages
+	GPMI Bind "ACTE_action"  "ev_action"
+	GPMI Bind "ACTE_gui_init" "ev_gui_init"
+	GPMI action_register "hello" "" "log hello world" "hello()"
+}
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.html b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.html
new file mode 100644
index 0000000..8580f81
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.html
@@ -0,0 +1,33 @@
+Load packages the script depends on:
+<ul>
+	<li> <ref>actions</ref> for registering the new action;
+	<li> <ref>dialogs</ref> for creating a dialog box.
+</ul>
+<p>
+Create a function <i>ev_action</i> that will be called when any of
+the actions registered by the script is executed. The script registers
+only one action, so it does not need to check which action caused
+the function to be called.
+<p>
+When the action event is called, use <ref>dialog_report</ref> to pop
+up a report dialog. First argument donates the name (title) of the window,
+the second is the text printed in the window.
+<p>
+Create a function <i>ev_gui_init</i> that will be called when the
+gui has been initialized - this is the right moment to register
+new menus in the GUI. Put the new menu item in the
+"Plugins/GPMI scripting/" menu by convention, and set hot key to ctrl+w.
+The menu should execute action hello().
+<p>
+In the "main" section of the script, bind event <ref>ACTE_action</ref> to
+our local function <i>ev_action</i> - this gets <i>ev_action</i> to
+be called when any of the actions registered by this script is executed.
+Also bind ev_gui_init so that the new menu can be safely created.
+<p>
+Finally use <ref>action_register</ref> to register the action:
+<ul>
+	<li> first argument is the name of the action
+	<li> second is the xy query string; it is empty since this action won't need coordinates to operate on
+	<li> third is the description that shows up in dumps and helps
+	<li> fourth is the syntax description (also for helps)
+</ul>
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.lua b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.lua
new file mode 100644
index 0000000..da9dee1
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.lua
@@ -0,0 +1,14 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+function ev_action(id, name, argc, x, y)
+	dialog_report("Greeting window", "Hello world!");
+end
+
+function ev_gui_init(id, argc, argv)
+	create_menu("/main_menu/Plugins/GPMI scripting/hello", "hello()", "h", "Ctrl<Key>w", "tooltip for hello");
+end
+
+Bind("ACTE_action", "ev_action");
+Bind("ACTE_gui_init", "ev_gui_init");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.pl b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.pl
new file mode 100644
index 0000000..49013a0
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.pl
@@ -0,0 +1,17 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+sub ev_action {
+	my($id, $name, $argc, $x, $y) = @_;
+	dialog_report("Greeting window", "Hello world!");
+}
+
+sub ev_gui_init
+{
+	my($id, $name, $argc, $argv) = @_;
+	create_menu("/main_menu/Plugins/GPMI scripting/hello", "hello()", "h", "Ctrl<Key>w", "tooltip for hello");
+}
+
+Bind("ACTE_action", "ev_action");
+Bind("ACTE_gui_init", "ev_gui_init");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.py b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.py
new file mode 100644
index 0000000..0d08661
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.py
@@ -0,0 +1,12 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+def ev_action(id, name, argc, x, y):
+	dialog_report("Greeting window", "Hello world!");
+
+def ev_gui_init(id, argc, argv):
+	create_menu("/main_menu/Plugins/GPMI scripting/hello", "hello()", "h", "Ctrl<Key>w", "tooltip for hello");
+
+Bind("ACTE_action", "ev_action");
+Bind("ACTE_gui_init", "ev_gui_init");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.rb b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.rb
new file mode 100644
index 0000000..f3e4844
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.rb
@@ -0,0 +1,14 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/dialogs", 0);
+
+def ev_action(id, name, argc, x, y)
+	dialog_report("Greeting window", "Hello world!");
+end
+
+def ev_gui_init(id, argc, argv)
+	create_menu("/main_menu/Plugins/GPMI scripting/hello", "hello()", "h", "Ctrl<Key>w", "tooltip for hello");
+end
+
+Bind("ACTE_action", "ev_action");
+Bind("ACTE_gui_init", "ev_gui_init");
+action_register("hello", "", "log hello world", "hello()");
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.scm b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.scm
new file mode 100644
index 0000000..07cfbfb
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.scm
@@ -0,0 +1,12 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/dialogs" 0)
+
+(define ev_action (lambda (id name argc x y)
+  (dialog_report "Greeting window" "Hello world!")))
+
+(define ev_gui_init (lambda (id argc argv)
+  (create_menu "/main_menu/Plugins/GPMI scripting/hello" "hello()" "h" "Ctrl<Key>w" "tooltip for hello")))
+
+(Bind "ACTE_action" "ev_action")
+(Bind "ACTE_gui_init" "ev_gui_init")
+(action_register "hello" "" "log hello world" "hello()")
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.stt b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.stt
new file mode 100644
index 0000000..348a930
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.stt
@@ -0,0 +1,12 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/dialogs" 0)
+
+(defun ev_action (id name argc x y)
+	(dialog_report "Greeting window" "Hello world!"))
+
+(defun ev_gui_init (id argc argv)
+	(create_menu "/main_menu/Plugins/GPMI scripting/hello" "hello()" "h" "Ctrl<Key>w" "tooltip for hello"))
+
+(Bind "ACTE_action" "ev_action")
+(Bind "ACTE_gui_init" "ev_gui_init")
+(action_register "hello" "" "log hello world" "hello()")
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/ex.tcl b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.tcl
new file mode 100644
index 0000000..c2bdc06
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/ex.tcl
@@ -0,0 +1,15 @@
+PkgLoad pcb-rnd-gpmi/actions 0
+PkgLoad pcb-rnd-gpmi/dialogs 0
+
+proc ev_action {id, name, argc, x, y} {
+	dialog_report "Greeting window" "Hello world!"
+}
+
+proc ev_gui_init {id, argc, argv} {
+	create_menu "/main_menu/Plugins/GPMI scripting/hello" "hello()" "h" "Ctrl<Key>w" "tooltip for hello"
+}
+
+Bind ACTE_action ev_action
+Bind ACTE_gui_init ev_gui_init
+action_register "hello" "" "log hello world" "hello()"
+
diff --git a/doc-rnd/gpmi/rosetta/12_hello_menu/index.html b/doc-rnd/gpmi/rosetta/12_hello_menu/index.html
new file mode 100644
index 0000000..5d9d7a1
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/12_hello_menu/index.html
@@ -0,0 +1,58 @@
+<html><body>
+<!-- ******* DO NOT EDIT THIS FILE, it is generated by rosetta_genpages.sh ******* -->
+<-- back to the <a href="../index.html">index of Rosetta examples</a>
+<h1>hello world (popup window + submenu)</h1>
+Create a new action hello() that prints "Hello world!" in a popup window, create a menu (under "Plugins/GPMI scripting/") that executes the action.
+<H2> Example implementations </H2>
+<a href="ex.awk">awk</a>
+ | 
+<a href="ex.bash">bash</a>
+ | 
+<a href="ex.lua">lua</a>
+ | 
+<a href="ex.pl">pl</a>
+ | 
+<a href="ex.py">py</a>
+ | 
+<a href="ex.rb">rb</a>
+ | 
+<a href="ex.scm">scm</a>
+ | 
+<a href="ex.stt">stt</a>
+ | 
+<a href="ex.tcl">tcl</a>
+<h2> Explanation, step by step </h2>
+Load packages the script depends on: 
+<ul> 	
+<li> 
+<i>actions</i>
+ for registering the new action; 	
+<li> 
+<i>dialogs</i>
+ for creating a dialog box. </ul>
+ 
+<p> Create a function 
+<i>ev_action</i>
+ that will be called when any of the actions registered by the script is executed. The script registers only one action, so it does not need to check which action caused the function to be called. 
+<p> When the action event is called, use 
+<i><a href="../../packages/dialogs_ref.html#dialog_report">dialog_report</a></i>
+ to pop up a report dialog. First argument donates the name (title) of the window, the second is the text printed in the window. 
+<p> Create a function 
+<i>ev_gui_init</i>
+ that will be called when the gui has been initialized - this is the right moment to register new menus in the GUI. Put the new menu item in the "Plugins/GPMI scripting/" menu by convention, and set hot key to ctrl+w. The menu should execute action hello(). 
+<p> In the "main" section of the script, bind event 
+<i><a href="../../packages/actions_ref.html#ACTE_action">ACTE_action</a></i>
+ to our local function 
+<i>ev_action</i>
+ - this gets 
+<i>ev_action</i>
+ to be called when any of the actions registered by this script is executed. Also bind ev_gui_init so that the new menu can be safely created. 
+<p> Finally use 
+<i><a href="../../packages/actions_ref.html#action_register">action_register</a></i>
+ to register the action: 
+<ul> 	
+<li> first argument is the name of the action 	
+<li> second is the xy query string; it is empty since this action won't need coordinates to operate on 	
+<li> third is the description that shows up in dumps and helps 	
+<li> fourth is the syntax description (also for helps) </ul>
+ 
diff --git a/doc-rnd/gpmi/rosetta/30_move/ID.desc b/doc-rnd/gpmi/rosetta/30_move/ID.desc
new file mode 100644
index 0000000..6820187
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ID.desc
@@ -0,0 +1,3 @@
+Create a new action mv(dx,dy) that moves selected objects relative by dx and dy mm.
+
+
diff --git a/doc-rnd/gpmi/rosetta/30_move/ID.name b/doc-rnd/gpmi/rosetta/30_move/ID.name
new file mode 100644
index 0000000..3d47e00
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ID.name
@@ -0,0 +1 @@
+action: move selected objects
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.awk b/doc-rnd/gpmi/rosetta/30_move/ex.awk
new file mode 100644
index 0000000..8b7f0e7
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.awk
@@ -0,0 +1,22 @@
+BEGIN {
+	PkgLoad("pcb-rnd-gpmi/actions", 0);
+	PkgLoad("pcb-rnd-gpmi/layout", 0);
+}
+
+function ev_action(id, name, argc, x, y      ,dx,dy,n,num_objs,obj_ptr)
+{
+	dx = action_arg(0) * mm2pcb_multiplier()
+	dy = action_arg(1) * mm2pcb_multiplier()
+
+	num_objs = layout_search_selected("mv_search", "OM_ANY")
+	for(n = 0; n < num_objs; n++) {
+		obj_ptr = layout_search_get("mv_search", n)
+		layout_obj_move(obj_ptr, "OC_OBJ", dx, dy)
+	}
+	layout_search_free("mv_search")
+}
+
+BEGIN {
+	Bind("ACTE_action", "ev_action");
+	action_register("mv", "", "move selected objects by dx and dy mm", "mv(dx,dy)");
+}
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.bash b/doc-rnd/gpmi/rosetta/30_move/ex.bash
new file mode 100644
index 0000000..5e34ddf
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.bash
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+function load_packages()
+{
+	GPMI PkgLoad "pcb-rnd-gpmi/actions" 0
+	GPMI PkgLoad "pcb-rnd-gpmi/layout" 0
+}
+
+function ev_action() {
+	local dx dy n num_objs
+
+	GPMI action_arg 0
+	a0=`bash_int "$result"`
+
+	GPMI action_arg 1
+	a1=`bash_int "$result"`
+	
+	GPMI mm2pcb_multiplier
+	conv=`bash_int "$result"`
+
+	dx=$(($a0 * $conv))
+	dy=$(($a1 * $conv))
+
+	GPMI layout_search_selected "mv_search" "OM_ANY"
+	num_objs=`bash_int "$result"`
+
+	for n in `seq 0 $(($num_objs-1))`
+	do
+		GPMI layout_search_get "mv_search" $n
+		obj_ptr="$result"
+		GPMI layout_obj_move $obj_ptr "OC_OBJ" $dx $dy
+	done
+	GPMI layout_search_free "mv_search"
+}
+
+function main()
+{
+	load_packages
+	GPMI Bind "ACTE_action"  "ev_action"
+	GPMI action_register "mv" "" "move selected objects by dx and dy mm" "mv(dx,dy)"
+}
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.html b/doc-rnd/gpmi/rosetta/30_move/ex.html
new file mode 100644
index 0000000..08251a7
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.html
@@ -0,0 +1,36 @@
+Load packages the script depends on:
+<ul>
+	<li> <ref>actions</ref> for registering the new action;
+	<li> <ref>layout</ref> for searching selected objects and moving objects.
+</ul>
+<p>
+Create a function <i>ev_action</i> that will be called when any of
+the actions registered by the script is executed. The script registers
+only one action, so it does not need to check which action caused
+the function to be called.
+<p>
+When the action event is called, first set up:
+<ul>
+	<li> fetch event argument 0 and 1 using <ref>action_arg</ref>;
+	<li> resolve the conversion rate from mm to PCB's units using <ref>mm2pcb_multiplier</ref>;
+	<li> perform a search for all selected object using <ref>layout_search_selected</ref> (return value is the number of objects found), name the resulting list "mv_search"
+</ul>
+A loop then iterates over the objects on the list:
+<ul>
+	<li> resolve the Nth element of the list using <ref>layout_search_get</ref>, referecing the list by name ("mv_search") and specifying which element to get (the Nth); the result is an object pointer;
+	<li> move the object using <ref>layout_obj_move</ref>; specify the object pointer, then which part of the object should be moved ("OC_OBJ" means the whole object) and relative offsets (dx and dy calculated earlier)
+</ul>
+Finally the search list shall be destroyed to reclaim memory, using <ref>layout_search_free</ref>.
+<p>
+
+In the "main" section of the script, bind event <ref>ACTE_action</ref> to
+our local function <i>ev_action</i> - this gets <i>ev_action</i> to
+be called when any of the actions registered by this script is executed.
+<p>
+Finally use <ref>action_register</ref> to register the action:
+<ul>
+	<li> first argument is the name of the action
+	<li> second is the xy query string; it is empty since this action won't need coordinates to operate on
+	<li> third is the description that shows up in dumps and helps
+	<li> fourth is the syntax description (also for helps)
+</ul>
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.lua b/doc-rnd/gpmi/rosetta/30_move/ex.lua
new file mode 100644
index 0000000..7b6a0e5
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.lua
@@ -0,0 +1,18 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/layout", 0);
+
+function ev_action(id, name, argc, x, y)
+	local dx = action_arg(0) * mm2pcb_multiplier()
+	local dy = action_arg(1) * mm2pcb_multiplier()
+
+	local num_objs = layout_search_selected("mv_search", "OM_ANY")
+	for n=0,num_objs+1
+	do
+		obj_ptr = layout_search_get("mv_search", n)
+		layout_obj_move(obj_ptr, "OC_OBJ", dx, dy)
+	end
+	layout_search_free("mv_search")
+end
+
+Bind("ACTE_action", "ev_action");
+action_register("mv", "", "move selected objects by dx and dy mm", "mv(dx,dy)");
\ No newline at end of file
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.pl b/doc-rnd/gpmi/rosetta/30_move/ex.pl
new file mode 100644
index 0000000..3e418fb
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.pl
@@ -0,0 +1,20 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/layout", 0);
+
+sub ev_action {
+	my($id, $name, $argc, $x, $y) = @_;
+
+	my $dx = action_arg(0) * mm2pcb_multiplier();
+	my $dy = action_arg(1) * mm2pcb_multiplier();
+
+	my $num_objs = layout_search_selected("mv_search", "OM_ANY");
+	for(my $n = 0; $n < $num_objs; $n++) {
+		$obj_ptr = layout_search_get("mv_search", $n);
+		layout_obj_move($obj_ptr, "OC_OBJ", $dx, $dy);
+	}
+	layout_search_free("mv_search");
+
+}
+
+Bind("ACTE_action", "ev_action");
+action_register("mv", "", "move selected objects by dx and dy mm", "mv(dx,dy)");
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.py b/doc-rnd/gpmi/rosetta/30_move/ex.py
new file mode 100644
index 0000000..2626f3c
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.py
@@ -0,0 +1,15 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/layout", 0);
+
+def ev_action(id, name, argc, x, y):
+	dx = int(action_arg(0)) * float(mm2pcb_multiplier())
+	dy = int(action_arg(1)) * float(mm2pcb_multiplier())
+	num_objs = int(layout_search_selected("mv_search", "OM_ANY"))
+	for n in xrange(0, num_objs):
+		obj_ptr = layout_search_get("mv_search", n)
+		layout_obj_move(obj_ptr, "OC_OBJ", dx, dy)
+	layout_search_free("mv_search")
+
+Bind("ACTE_action", "ev_action");
+action_register("hello", "", "log hello world", "hello()");
+action_register("mv", "", "move selected objects by dx and dy mm", "mv(dx,dy)");
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.rb b/doc-rnd/gpmi/rosetta/30_move/ex.rb
new file mode 100644
index 0000000..e161a60
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.rb
@@ -0,0 +1,25 @@
+PkgLoad("pcb-rnd-gpmi/actions", 0);
+PkgLoad("pcb-rnd-gpmi/layout", 0);
+
+def ev_action(id, name, argc, x, y)
+# TODO: how to convert 1e+06 in ruby?
+#	conv = mm2pcb_multiplier().to_i
+	conv = 1000000
+
+
+	dx = action_arg(0).to_i * conv.to_i;
+	dy = action_arg(1).to_i * conv.to_i;
+
+	num_objs = layout_search_selected("mv_search", "OM_ANY").to_i;
+
+	for n in 0..(num_objs-1)
+		obj_ptr = layout_search_get("mv_search", n);
+		layout_obj_move(obj_ptr, "OC_OBJ", dx, dy);
+		n += 1;
+	end
+	layout_search_free("mv_search");
+
+end
+
+Bind("ACTE_action", "ev_action");
+action_register("mv", "", "move selected objects by dx and dy mm", "mv(dx,dy)");
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.scm b/doc-rnd/gpmi/rosetta/30_move/ex.scm
new file mode 100644
index 0000000..16cd3ab
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.scm
@@ -0,0 +1,20 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/layout" 0)
+
+(define ev_action (lambda (id name argc x y)
+(let
+	(
+		(dx (* (string->number (action_arg 0)) (string->number (mm2pcb_multiplier))))
+		(dy (* (string->number (action_arg 1)) (string->number (mm2pcb_multiplier))))
+		(num_objs (string->number (layout_search_selected "mv_search" "OM_ANY")))
+		(obj_ptr 0)
+	)
+	(do ((n 0 (1+ n)))
+	    ((> n num_objs))
+		(set! obj_ptr (layout_search_get "mv_search" n))
+		(layout_obj_move obj_ptr "OC_OBJ" (inexact->exact dx) (inexact->exact dy))
+	)
+)))
+
+(Bind "ACTE_action" "ev_action")
+(action_register "mv" "" "move selected objects by dx and dy mm" "mv(dx,dy)")
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.stt b/doc-rnd/gpmi/rosetta/30_move/ex.stt
new file mode 100644
index 0000000..b01c2db
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.stt
@@ -0,0 +1,24 @@
+(PkgLoad "pcb-rnd-gpmi/actions" 0)
+(PkgLoad "pcb-rnd-gpmi/layout" 0)
+
+(defun ev_action (id name argc x y)
+(let
+	(
+		(dx (* (->num (action_arg 0)) (->num (mm2pcb_multiplier))))
+		(dy (* (->num (action_arg 1)) (->num (mm2pcb_multiplier))))
+		(num_objs (->num (layout_search_selected "mv_search" "OM_ANY")))
+		(obj_ptr 0)
+	)
+	(do ((n 0 (1+ n)))
+	    ((> n num_objs))
+		(set! obj_ptr (layout_search_get "mv_search" n))
+		(layout_obj_move obj_ptr "OC_OBJ" (->int dx) (->int dy))
+	)
+)
+)
+
+(Bind "ACTE_action" "ev_action")
+(action_register "mv" "" "move selected objects by dx and dy mm" "mv(dx,dy)")
+
+
+
diff --git a/doc-rnd/gpmi/rosetta/30_move/ex.tcl b/doc-rnd/gpmi/rosetta/30_move/ex.tcl
new file mode 100644
index 0000000..659e00e
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/ex.tcl
@@ -0,0 +1,17 @@
+PkgLoad pcb-rnd-gpmi/actions 0
+PkgLoad pcb-rnd-gpmi/layout 0
+
+proc ev_action {id, name, argc, x, y} {
+	set dx [expr round([action_arg 0]  * [mm2pcb_multiplier])]
+	set dy [expr round([action_arg 1]  * [mm2pcb_multiplier])]
+
+	set num_objs [layout_search_selected mv_search OM_ANY]
+	for {set n 0} {$n < $num_objs} {incr n} {
+		set obj_ptr [layout_search_get mv_search $n]
+		layout_obj_move $obj_ptr OC_OBJ $dx $dy
+	}
+	layout_search_free mv_search
+}
+
+Bind ACTE_action ev_action
+action_register "mv"  ""  "move selected objects by dx and dy mm"  "mv(dx,dy)"
diff --git a/doc-rnd/gpmi/rosetta/30_move/index.html b/doc-rnd/gpmi/rosetta/30_move/index.html
new file mode 100644
index 0000000..b5969b9
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/30_move/index.html
@@ -0,0 +1,74 @@
+<html><body>
+<!-- ******* DO NOT EDIT THIS FILE, it is generated by rosetta_genpages.sh ******* -->
+<-- back to the <a href="../index.html">index of Rosetta examples</a>
+<h1>action: move selected objects</h1>
+Create a new action mv(dx,dy) that moves selected objects relative by dx and dy mm.
+<H2> Example implementations </H2>
+<a href="ex.awk">awk</a>
+ | 
+<a href="ex.bash">bash</a>
+ | 
+<a href="ex.lua">lua</a>
+ | 
+<a href="ex.pl">pl</a>
+ | 
+<a href="ex.py">py</a>
+ | 
+<a href="ex.rb">rb</a>
+ | 
+<a href="ex.scm">scm</a>
+ | 
+<a href="ex.stt">stt</a>
+ | 
+<a href="ex.tcl">tcl</a>
+<h2> Explanation, step by step </h2>
+Load packages the script depends on: 
+<ul> 	
+<li> 
+<i>actions</i>
+ for registering the new action; 	
+<li> 
+<i>layout</i>
+ for searching selected objects and moving objects. </ul>
+ 
+<p> Create a function 
+<i>ev_action</i>
+ that will be called when any of the actions registered by the script is executed. The script registers only one action, so it does not need to check which action caused the function to be called. 
+<p> When the action event is called, first set up: 
+<ul> 	
+<li> fetch event argument 0 and 1 using 
+<i><a href="../../packages/actions_ref.html#action_arg">action_arg</a></i>
+; 	
+<li> resolve the conversion rate from mm to PCB's units using 
+<i><a href="../../packages/layout_ref.html#mm2pcb_multiplier">mm2pcb_multiplier</a></i>
+; 	
+<li> perform a search for all selected object using 
+<i><a href="../../packages/layout_ref.html#layout_search_selected">layout_search_selected</a></i>
+ (return value is the number of objects found), name the resulting list "mv_search" </ul>
+ A loop then iterates over the objects on the list: 
+<ul> 	
+<li> resolve the Nth element of the list using 
+<i><a href="../../packages/layout_ref.html#layout_search_get">layout_search_get</a></i>
+, referecing the list by name ("mv_search") and specifying which element to get (the Nth); the result is an object pointer; 	
+<li> move the object using 
+<i><a href="../../packages/layout_ref.html#layout_obj_move">layout_obj_move</a></i>
+; specify the object pointer, then which part of the object should be moved ("OC_OBJ" means the whole object) and relative offsets (dx and dy calculated earlier) </ul>
+ Finally the search list shall be destroyed to reclaim memory, using 
+<i><a href="../../packages/layout_ref.html#layout_search_free">layout_search_free</a></i>
+. 
+<p>  In the "main" section of the script, bind event 
+<i><a href="../../packages/actions_ref.html#ACTE_action">ACTE_action</a></i>
+ to our local function 
+<i>ev_action</i>
+ - this gets 
+<i>ev_action</i>
+ to be called when any of the actions registered by this script is executed. 
+<p> Finally use 
+<i><a href="../../packages/actions_ref.html#action_register">action_register</a></i>
+ to register the action: 
+<ul> 	
+<li> first argument is the name of the action 	
+<li> second is the xy query string; it is empty since this action won't need coordinates to operate on 	
+<li> third is the description that shows up in dumps and helps 	
+<li> fourth is the syntax description (also for helps) </ul>
+ 
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/ID.desc b/doc-rnd/gpmi/rosetta/35_export_drill/ID.desc
new file mode 100644
index 0000000..44fab97
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/ID.desc
@@ -0,0 +1 @@
+Create a new exporter that prints a list of drills in x,y,dia table in CSV, TSV or text format
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/ID.name b/doc-rnd/gpmi/rosetta/35_export_drill/ID.name
new file mode 100644
index 0000000..b95f3b1
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/ID.name
@@ -0,0 +1 @@
+drill list exporter
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/ex.awk b/doc-rnd/gpmi/rosetta/35_export_drill/ex.awk
new file mode 100644
index 0000000..c90a9e0
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/ex.awk
@@ -0,0 +1,60 @@
+BEGIN {
+	PkgLoad("pcb-rnd-gpmi/hid", 0)
+	PkgLoad("pcb-rnd-gpmi/layout", 0)
+	PkgLoad("pcb-rnd-gpmi/dialogs", 0)
+
+	hid = hid_create("drill", "drill list export")
+	attr_path =  hid_add_attribute(hid, "filename", "name of the output file", "HIDA_Path", 0, 0, "drill.txt")
+	attr_format = hid_add_attribute(hid, "format",  "file format", "HIDA_Enum", 0, 0, "CSV|TSV|text")
+	hid_register(hid)
+
+	fmt = 0
+	conv = mm2pcb_multiplier()
+	channel = ""
+	green_light = 0
+}
+
+function make_gc(event_id, hid, gc) {
+	if (channel == "") {
+		channel = hid_get_attribute(hid, attr_path)
+# awk: can't check whether the file can be open here
+		fmt = hid_get_attribute(hid, attr_format)
+	}
+}
+
+function destroy_gc(event_id, hid, gc) {
+	if (channel != "") {
+		close(channel)
+		channel = ""
+	}
+}
+
+function set_layer(event_id, hid, name, group, empty) {
+	if (name == "topassembly")
+		green_light = 1
+	else
+		green_light = 0
+}
+
+function fill_circle(event_id, hid, gc, cx, cy, r      ,dia) {
+	if (green_light) {
+		cx  = cx / conv
+		cy  = cy / conv
+		dia = r  / conv * 2
+
+		if (fmt == "CSV") {
+			print cx "," cy "," dia > channel
+		} else if (fmt == "TSV") {
+			print cx "\t" cy "\t" dia > channel
+		} else if (fmt == "text") {
+			print cx "    " cy "    " dia > channel
+		}
+	}
+}
+
+BEGIN {
+	Bind("HIDE_make_gc", "make_gc")
+	Bind("HIDE_destroy_gc", "destroy_gc")
+	Bind("HIDE_set_layer", "set_layer")
+	Bind("HIDE_fill_circle", "fill_circle")
+}
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/ex.html b/doc-rnd/gpmi/rosetta/35_export_drill/ex.html
new file mode 100644
index 0000000..40ce791
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/ex.html
@@ -0,0 +1,60 @@
+<!-- TODO: move this to the high level doc -->
+An exporter is a hid registered. During the export
+process, pcb-rnd calls the exporter to draw objects on layers. The GPMI
+layer converts these calls to bindable events. An exporter script catches
+the drawing events relevant to the output format and draws everything from
+event handlers. 
+<p>
+An exporter is sort of a drawing backend: pcb-rnd sets up graphic contexts
+(gc for short) and draws on them. The first gc set up should open the
+output file, the last gc closed should flush/close the file.
+<p>
+<hr>
+
+<h3>Theory of operation</h3>
+Assume only <b>filled circles</b> on the 
+<b>top assembly layer</b> are holes. Emit a line for each of these
+and ignore everything else.
+
+<h3> Implementation </h3>
+Load packages the script depends on:
+<ul>
+	<li> <ref>hid</ref> for registering the new exporter;
+	<li> <ref>layout</ref> for unit conversion;
+	<li> <ref>dialogs</ref> for error reporting.
+</ul>
+<p>
+Set up an exporter hid, storing the handle of each object created in
+a global variable:
+<ul>
+	<li> create a hid using <ref>hid_create</ref>
+	<li> add two attributes (fields) to the dialog using <ref>hid_add_attribute</ref>
+	<li> register the new hid using <ref>hid_register</ref> - this makes it an exporter
+</ul>
+<p>
+Set up global state variables:
+<ul>
+	<li> fmt will be a cache for the format selected by the user
+	<li> conv is the conversion scale between mm and PCB internal coordinate unit
+	<li> channel specifies a file handle or indicates that the output file is open
+	<li> green_light indicates that the exporter is drawing the relevant layer
+</ul>
+<p>
+Define a <i>make_gc</i> callback. It is called when a new graphic
+context is created (e.g. for layers).  An exporter is sort of a drawing
+backend: pcb-rnd sets up gc's and draws on them. The first gc set up
+should open the output file, the last gc closed should flush/close the file.
+Define a <i>destroy_gc</i> callback for the latter. GCs are
+destroyed at the end of the export process. Use global variable <i>channel</i>
+to make sure the file is open/close only once (see TODO <ref>script context</ref>).
+<p>
+Define a <i>set_layer</i> callback that sets green_light to true if
+layer name is "topasssembly", false otherwise.
+<p>
+Callback <i>fill_circle</i> is the workhorse: check if we have the green
+light to emit lines, then convert coordinates and diameter to mm and
+decide what format to use for printing a line for each filled circle.
+<p>
+Finally, bind all four callbacks to exporter events.
+
+
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/ex.lua b/doc-rnd/gpmi/rosetta/35_export_drill/ex.lua
new file mode 100644
index 0000000..9b1371c
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/ex.lua
@@ -0,0 +1,65 @@
+PkgLoad("pcb-rnd-gpmi/hid", 0)
+PkgLoad("pcb-rnd-gpmi/layout", 0)
+PkgLoad("pcb-rnd-gpmi/dialogs", 0)
+
+hid = hid_create("drill", "drill list export")
+attr_path =  hid_add_attribute(hid, "filename", "name of the output file", "HIDA_Path", 0, 0, "drill.txt")
+attr_format = hid_add_attribute(hid, "format",  "file format", "HIDA_Enum", 0, 0, "CSV|TSV|text")
+hid_register(hid)
+
+fmt = 0
+conv = mm2pcb_multiplier()
+channel = nil
+green_light = 0
+
+function make_gc(event_id, hid, gc)
+	if channel == nil
+	then
+		channel = io.open(hid_get_attribute(hid, attr_path), "w")
+		if channel == nil
+		then
+			dialog_report("Error exporting drill", "Could not open file [hid_get_attribute $hid $attr_path] for\nwriting, exporting drill failed.")
+			return
+		end
+		fmt = hid_get_attribute(hid, attr_format)
+	end
+end
+
+function destroy_gc(event_id, hid, gc)
+	if channel ~= nil
+	then
+		channel:close()
+		channel = nil
+	end
+end
+
+function set_layer(event_id, hid, name, group, empty)
+	if name == "topassembly"
+	then
+		green_light = 1
+	else
+		green_light = 0
+	end
+end
+
+function fill_circle(event_id, hid, gc, cx, cy, r)
+	if green_light
+	then
+		cx  = cx / conv
+		cy  = cy / conv
+		local dia = r  / conv * 2
+
+		if fmt == "CSV" then
+			channel:write(cx .. "," .. cy .. "," .. dia .. "\n")
+		elseif (fmt == "TSV") then
+			channel:write(cx .. "\t" .. cy .. "\t" .. dia .. "\n")
+		elseif (fmt == "text") then
+			channel:write(cx .. "    " .. cy .. "    " .. dia .. "\n")
+		end
+	end
+end
+
+Bind("HIDE_make_gc", "make_gc")
+Bind("HIDE_destroy_gc", "destroy_gc")
+Bind("HIDE_set_layer", "set_layer")
+Bind("HIDE_fill_circle", "fill_circle")
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/ex.tcl b/doc-rnd/gpmi/rosetta/35_export_drill/ex.tcl
new file mode 100644
index 0000000..3311117
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/ex.tcl
@@ -0,0 +1,63 @@
+PkgLoad pcb-rnd-gpmi/hid 0
+PkgLoad pcb-rnd-gpmi/layout 0
+PkgLoad pcb-rnd-gpmi/dialogs 0
+
+set hid [hid_create "drill" "drill list export"]
+set attr_path   [hid_add_attribute $hid "filename" "name of the output file" "HIDA_Path" 0 0 "drill.txt"]
+set attr_format [hid_add_attribute $hid "format"   "file format" "HIDA_Enum" 0 0 "CSV|TSV|text"]
+hid_register $hid
+
+set fmt 0
+set conv [mm2pcb_multiplier]
+set channel -1
+set green_light 0
+
+proc make_gc {event_id hid gc} {
+	global channel attr_path attr_format fmt
+
+	if {$channel == -1} {
+		if {[catch {open [hid_get_attribute $hid $attr_path] "w"} channel]} {
+			dialog_report "Error exporting drill" "Could not open file [hid_get_attribute $hid $attr_path] for\nwriting, exporting drill failed."
+			return
+		}
+		set fmt [hid_get_attribute $hid $attr_format]
+	}
+}
+
+proc destroy_gc {event_id hid gc} {
+	global channel
+
+	if {$channel > -1} {
+		close $channel
+		set channel -1
+	}
+}
+
+proc set_layer {event_id hid name group empty} {
+	global green_light
+
+	if { $name eq "topassembly" } { set green_light 1 } { set green_light 0 }
+}
+
+proc fill_circle {event_id hid gc cx cy r} {
+	global channel conv fmt green_light
+
+	if {$green_light} {
+		set cx  [expr $cx / $conv]
+		set cy  [expr $cy / $conv]
+		set dia [expr $r  / $conv * 2]
+
+		if {$fmt eq "CSV"} {
+			puts $channel "$cx,$cy,$dia"
+		} elseif {$fmt eq "TSV"} {
+			puts $channel "$cx	$cy	$dia"
+		} elseif {$fmt eq "text"} {
+			puts $channel "$cx    $cy    $dia"
+		}
+	}
+}
+
+Bind HIDE_make_gc make_gc
+Bind HIDE_destroy_gc destroy_gc
+Bind HIDE_set_layer set_layer
+Bind HIDE_fill_circle fill_circle
diff --git a/doc-rnd/gpmi/rosetta/35_export_drill/index.html b/doc-rnd/gpmi/rosetta/35_export_drill/index.html
new file mode 100644
index 0000000..7423e26
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/35_export_drill/index.html
@@ -0,0 +1,71 @@
+<html><body>
+<!-- ******* DO NOT EDIT THIS FILE, it is generated by rosetta_genpages.sh ******* -->
+<-- back to the <a href="../index.html">index of Rosetta examples</a>
+<h1>drill list exporter</h1>
+Create a new exporter that prints a list of drills in x,y,dia table in CSV, TSV or text format
+<H2> Example implementations </H2>
+<a href="ex.awk">awk</a>
+ | 
+<a href="ex.lua">lua</a>
+ | 
+<a href="ex.tcl">tcl</a>
+<h2> Explanation, step by step </h2>
+
+<!-- TODO: move this to the high level doc --> An exporter is a hid registered. During the export process, pcb-rnd calls the exporter to draw objects on layers. The GPMI layer converts these calls to bindable events. An exporter script catches the drawing events relevant to the output format and draws everything from event handlers.  
+<p> An exporter is sort of a drawing backend: pcb-rnd sets up graphic contexts (gc for short) and draws on them. The first gc set up should open the output file, the last gc closed should flush/close the file. 
+<p> 
+<hr>  
+<h3>Theory of operation</h3>
+ Assume only 
+<b>filled circles</b>
+ on the  
+<b>top assembly layer</b>
+ are holes. Emit a line for each of these and ignore everything else.  
+<h3> Implementation </h3>
+ Load packages the script depends on: 
+<ul> 	
+<li> 
+<i>hid</i>
+ for registering the new exporter; 	
+<li> 
+<i>layout</i>
+ for unit conversion; 	
+<li> 
+<i>dialogs</i>
+ for error reporting. </ul>
+ 
+<p> Set up an exporter hid, storing the handle of each object created in a global variable: 
+<ul> 	
+<li> create a hid using 
+<i><a href="../../packages/hid_ref.html#hid_create">hid_create</a></i>
+ 	
+<li> add two attributes (fields) to the dialog using 
+<i><a href="../../packages/hid_ref.html#hid_add_attribute">hid_add_attribute</a></i>
+ 	
+<li> register the new hid using 
+<i><a href="../../packages/hid_ref.html#hid_register">hid_register</a></i>
+ - this makes it an exporter </ul>
+ 
+<p> Set up global state variables: 
+<ul> 	
+<li> fmt will be a cache for the format selected by the user 	
+<li> conv is the conversion scale between mm and PCB internal coordinate unit 	
+<li> channel specifies a file handle or indicates that the output file is open 	
+<li> green_light indicates that the exporter is drawing the relevant layer </ul>
+ 
+<p> Define a 
+<i>make_gc</i>
+ callback. It is called when a new graphic context is created (e.g. for layers).  An exporter is sort of a drawing backend: pcb-rnd sets up gc's and draws on them. The first gc set up should open the output file, the last gc closed should flush/close the file. Define a 
+<i>destroy_gc</i>
+ callback for the latter. GCs are destroyed at the end of the export process. Use global variable 
+<i>channel</i>
+ to make sure the file is open/close only once (see TODO 
+<i>script context</i>
+). 
+<p> Define a 
+<i>set_layer</i>
+ callback that sets green_light to true if layer name is "topasssembly", false otherwise. 
+<p> Callback 
+<i>fill_circle</i>
+ is the workhorse: check if we have the green light to emit lines, then convert coordinates and diameter to mm and decide what format to use for printing a line for each filled circle. 
+<p> Finally, bind all four callbacks to exporter events.   
diff --git a/doc-rnd/gpmi/rosetta/index.html b/doc-rnd/gpmi/rosetta/index.html
new file mode 100644
index 0000000..fc1a901
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/index.html
@@ -0,0 +1,58 @@
+<html>
+<body>
+
+<H1> pcb-rnd scripting - Rosetta </H1>
+The <i><a href="https://en.wikipedia.org/wiki/Rosetta_Stone">Rosetta Stone</a>
+of <a href="../scripting_intro.html"> pcb-rnd scripting </a></i> is a collection of example scripts implemented in
+various scripting languages. Rosetta has multiple purposes:
+<ul>
+	<li> to provide examples of using the pcb-rnd API;
+	<li> to not scare away newcomers by showing the first examples in an unfamiliar language;
+	<li> to help the user to decide which language to use for a specific task;
+	<li> to provide examples on how to convert idioms of pcb-rnd scripts from one language to another.
+</ul>
+<p>
+Each example comes with an explanation, written in plain English about what the
+scripts do, step by step. Instead of trying to exploit powerful features
+of the language, example implementations try to be simple.
+<p>
+The list below is ordered from the least complex to the most complex examples.
+Column <i>lvl</i> is the complexity score. The less the score is, the simpler
+the example is.
+
+
+<H2> Index of examples </H2>
+
+
+<table border=1 cellspacing=0 cellpadding=15>
+<tr><th>lvl<th>example <th> languages <th> description
+<tr>
+ <td>10
+ <td><a href="10_hello/index.html">hello world (text, log)</a>
+ <td> awk bash lua perl python ruby scheme stutter tcl
+ <td>Create a new action hello() that prints "Hello world!" in the message log.
+<tr>
+ <td>10
+ <td><a href="10_hello_gui/index.html">hello world (popup window)</a>
+ <td> awk bash lua perl python ruby scheme stutter tcl
+ <td>Create a new action hello() that prints "Hello world!" in a popup window.
+<tr>
+ <td>12
+ <td><a href="12_hello_menu/index.html">hello world (popup window + submenu)</a>
+ <td> awk bash lua perl python ruby scheme stutter tcl
+ <td>Create a new action hello() that prints "Hello world!" in a popup window, create a menu (under "Plugins/GPMI scripting/") that executes the action.
+<tr>
+ <td>30
+ <td><a href="30_move/index.html">action: move selected objects</a>
+ <td> awk bash lua perl python ruby scheme stutter tcl
+ <td>Create a new action mv(dx,dy) that moves selected objects relative by dx and dy mm.
+<tr>
+ <td>35
+ <td><a href="35_export_drill/index.html">drill list exporter</a>
+ <td> awk lua tcl
+ <td>Create a new exporter that prints a list of drills in x,y,dia table in CSV, TSV or text format
+</table>
+
+
+</body>
+</html>
diff --git a/doc-rnd/gpmi/rosetta/index.templ.html b/doc-rnd/gpmi/rosetta/index.templ.html
new file mode 100644
index 0000000..ed01e75
--- /dev/null
+++ b/doc-rnd/gpmi/rosetta/index.templ.html
@@ -0,0 +1,29 @@
+<html>
+<body>
+
+<H1> pcb-rnd scripting - Rosetta </H1>
+The <i><a href="https://en.wikipedia.org/wiki/Rosetta_Stone">Rosetta Stone</a>
+of <a href="../scripting_intro.html"> pcb-rnd scripting </a></i> is a collection of example scripts implemented in
+various scripting languages. Rosetta has multiple purposes:
+<ul>
+	<li> to provide examples of using the pcb-rnd API;
+	<li> to not scare away newcomers by showing the first examples in an unfamiliar language;
+	<li> to help the user to decide which language to use for a specific task;
+	<li> to provide examples on how to convert idioms of pcb-rnd scripts from one language to another.
+</ul>
+<p>
+Each example comes with an explanation, written in plain English about what the
+scripts do, step by step. Instead of trying to exploit powerful features
+of the language, example implementations try to be simple.
+<p>
+The list below is ordered from the least complex to the most complex examples.
+Column <i>lvl</i> is the complexity score. The less the score is, the simpler
+the example is.
+
+
+<H2> Index of examples </H2>
+
+<rosetta index>
+
+</body>
+</html>
diff --git a/doc-rnd/gpmi/scripting_intro.html b/doc-rnd/gpmi/scripting_intro.html
new file mode 100644
index 0000000..a70d50a
--- /dev/null
+++ b/doc-rnd/gpmi/scripting_intro.html
@@ -0,0 +1,226 @@
+<html>
+<body>
+<H1> Scripting intro </H1>
+This document is an introduction to GPMI for pcb-rnd users. It focuses on
+scripting pcb-rnd and doesn't discusses GPMI deeper than the minimum necessary.
+GPMI is more generic than shown here.
+<p>
+The scope of the document is to describe the relations between pcb-rnd, 
+hids, GPMI, glue packages and scripts. Details on how specific glue
+packages access pcb-rnd internals (or how those internals work) are
+described in other documents.
+
+<H2> 1. pcb-rnd internals </H2>
+
+Since scripts are glued to pcb-rnd internals, scripters need to know
+the basic concepts of how pcb-rnd is structured.
+
+<H3> 1.1 pcb-rnd, HIDs, plugins and GPMI </h2>
+pcb-rnd consists of:
+<ul>
+	<li> a <b>core</b> that handles the file format, data structures, UI and exporter logics
+	<li> a <b>hid system</b> that is a layer between core and the HIDs
+	<li> a set of <b>GUI HIDs</b> which are responsible for interactive graphical display of the current design
+	<li> a set of <b>exporter HIDs</b> which are responsible for exporting offline representation of the current design
+	<li> a <b>plugin system</b> that can load HIDs from on the fly, from dynamic loadable libraries (.so or .dll)
+	<li> a <b>buildin system</b> which is the "static link" variant of the plugin system: some plugins can be selected to be statically linked into the pcb-rnd executable so they are "always loaded"
+</ul>
+<p>
+Note 1: at the moment GUI hids can not be plugins or buildins.
+<br>
+Note 2: plugins/buildins are always exporter HIDs technically, but in practice
+they do not have to offer exporting. This means such a HID plugin is loaded
+and registered as an exporter on paper, but it doesn't really create a
+new export facility, just sits there, interacting with pcb-rnd (creating
+menus, actions, etc). Most script plugins do this.
+<p>
+When pcb-rnd executable is compiled, the core, the default GUI and exporter HIDs
+and buildins are compiled into the executable. Later on 
+<p>
+The <i>GPMI hid</i> is an <i>optional plugin</i> that, by compile-time choice
+of the user, can be:
+<ul>
+	<li> left out all-together - pcb-rnd does not have scripting
+	<li> compiled as a plugin - when the .so (or .dll) is put in the right directory, pcb-rnd loads it during startup and has scripting
+	<li> compiled as buildin - becomes part of the pcb-rnd executable so that scripting is always available
+</ul>
+
+<H3> 1.2. Actions, menus, exporters </h2>
+The core implements actions. An action is a command with custom arguments,
+e.g. Delete(Selected). These actions are the commands on the default command
+line in pcb-rnd. In batch mode the command language uses these actions as well.
+Remote controlling pcb-rnd using the --listen switch will read these actions
+on the standard input.
+<p>
+GUI menus are configured to execute actions too. This also means it is impossible
+to realize an user-accessible functionality that can be triggered from
+the GUI command line, menu or batch input without making it an action.
+<p>
+The only exception, where new functionality is not behind a new action is
+exporters. An exporter is a structured dialog box of options and a set of
+callback functions implementing a drawing API. Exporting works by the following
+steps:
+<ol>
+	<li> An exporter HID may register one or more exporters.
+	<li> When the user tries to export the design, pcb-rnd lists all
+	     registered exporters.
+	<li> The user chooses one, and the preconfigured dialog box
+	     with the selected exporter's options are popped up.
+	<li> pcb-rnd core runs the dialog and saves the results if the user
+	     clicked on the ok button.
+	<li> pcb-rnd starts the exporting process: it calls the callbacks
+	     to draw the design layer by layer, object by object.
+</ol>
+
+<H3> 1.3. How a script can interact with the user </h2>
+<ul>
+	<li> The script may register <i>actions</i> - these actions are instantly accessible from the GUI command line, on stdin with --listen and in batch mode
+	<li> The script may <i>create menus</i> and bind them to actions - when the menu is selected, the action is executed
+	<li> The script may <i>bind to events</i> that will be generated asynchronously by pcb-rnd - when such an event is received, the script executes some code
+	<li> The script may <i>register new exporter(s)</i> - the user will be able to export the design using the script
+	<li> The script may run arbitrary code <i>"on load"</i> (when the script is loaded) - however, that time the design or even the GUI may not be loaded/initialized yet - this is useful for registering actions, bind to events or set up exporters
+</ul>
+
+
+<H2> 2. GPMI intro </H2>
+
+<H3> 2.1. GPMI's place in the pcb-rnd world </H3>
+<table border=0 widht=100%><tr><td valign=top>
+GPMI is a plugin/buildin HID. Instead of doing actual work, it loads scripts
+and provides a glue layer between pcb-rnd and the scripts. The actual work
+is performed by the scripts.
+The glue layer comes in two kinds:
+<ul>
+	<li> gpmi module: dynamic lib written in C, knows how to load and interpret a script and how to deliver events to the script
+	<li> gpmi package: dynamic lib, provides C functions the script can directly call; package functions then know how to deal with PCB internals
+</ul>
+Arrows drawn with dashed line represents a slow, <i>string based communication</i>.
+<td><img src="gpmi_flow.png"></table>
+
+<H3> 2.2. Module, script, script context, packages </H3>
+<table border=0 widht=100%><tr><td valign=top>
+Each time a script needs to be loaded, first a module is loaded and the name
+of the script is passed to the module. During module initialization, the module
+sets up a script interpreter and script context and loads the script into the
+context.
+<p>
+If there are 3 separate lua scripts running in pcb-rnd, there are 3 separate
+lua modules loaded, each dealing with one of the scripts. The process of
+loading a script is illustrated by highlighting the relevant paths with red
+for step 1 and green for step 2.
+<p>
+Step 0: the <b>GPMI HID</b> finds a script has to be loaded. The idea comes
+from the config file (pcb-rnd-gpmi.conf) or from the GUI (manage scripts)
+or as a request from a script already loaded. 
+<p>
+Step 1: the <b>GPMI HID</b> loads the corresponding module which in turns
+loads the script. The script has a "main" part that is run on load. For
+most languages this is the global code sections; in some languages it is
+a specific function, usually called main. A few basic glue packages
+are already loaded before the script.
+<p>
+Step 2: the <b>script</b> can load glue packages. This usually happens
+from the on-load main part from the script. The actual mechanism is to
+call PkgLoad() from a glue package that was automatically loaded in
+Step 1. The green arrows represent this path: the script executes PkgLoad()
+which in turns loads other package(s) into the GPMI hid.
+<p>
+Packages are loaded only once and are globally accessible for multiple modules.
+<td><img src="gpmi_flow_load.png"></table>
+
+<H3> 2.3. Binding events, registering actions, creating menus </H3>
+<table border=0 widht=100%><tr><td valign=top>
+Binding an event in a script is done by calling the Bind() function
+(this is implemented in a package automatically loaded). The first
+argument is the name of the event, the second argument is the name of
+the script function that should be called when the event is triggered. Both
+arguments are strings. The event binding mechanism is shown in red in the
+map to the right.
+<p>
+The script can create new actions using the action_register() function
+(the actions package needs to be loaded first). A script may register multiple
+actions. This call is marked with green in the above map.
+If any of the actions registered by the script is called, the event "ACTE_action"
+is generated. This has two implications:
+<ul>
+	<li> a script that registers actions needs to bind the ACTE_action event to serve the action requests
+	<li> if a script registers multiple actions, in the event handler it needs to check which action triggered the event (e.g. with a switch()-like construction on the event name)
+</ul>
+<td><img src="gpmi_flow_reg.png"></table>
+
+<table border=0 widht=100%><tr><td valign=top>
+Menus are created using the create_menu() call. <b>Menus can be
+created only when the GUI is already set up - this may happen only
+after some of the scripts are already loaded</b>. Thus scripts shall
+create menus from an event handler bound to the ACTE_gui_init event.
+This event is triggered right after the GUI has been set up.
+On the map to the right the red arrows represent the path of ACTE_gui_init;
+the green arrows represent the reaction of the script, creating the new
+menu.
+<p>
+<td><img src="gpmi_flow_menu.png"></table>
+
+<H3> 2.4. Exporting </H3>
+<table border=0 widht=100%><tr><td valign=top>
+Exporter scripts first have to set up an exporter hid. This typically
+happens from their on-load main part. Related calls are in the hid
+package. The following map shows this process with red arrows:
+<p>
+When the user chooses to use the exporter, among the green arrows,
+a series of events are triggered and the script can generate output
+directly to a file from event handlers bound to these exporting events.
+<td><img src="gpmi_flow_exp.png"></table>
+
+<H3> 2.5. Making modifications on the design </H3>
+The purpose of a script might be to manipulate the objects in the current design.
+Such a script registers actions, and implements the handler of the actions
+using the layout package. The layout package provides calls to query,
+change and create design objects.
+
+
+<H2> 3. How it works in practice </H2>
+
+<H3> 3.1. Loading the GPMI plugin </H3>
+Check the output of ./configure, it will tell if gpmi is compiled as buildin
+or plugin (or not at all).
+If the gpmi plugin is compiled as a buildin, it is already loaded, no
+further steps are required.
+<p>
+If gpmi is a plugin, gpmi_plugin.so (or gpmi_plugin.dll) needs to be
+copied in one of the plugin directories pcb-rnd is looking into on startup:
+<table border=1>
+	<tr><th> path <th> purpose
+	<tr><td>
+	<tr><td>$prefix/lib/pcb-rnd/plugins/$arch/  <td> system plugins, multihost
+	<tr><td>$prefix/lib/pcb-rnd/plugins/        <td> system plugins
+	<tr><td>~/.pcb/plugins/$arch/               <td> user plugins, multihost
+	<tr><td>~/.pcb/plugins/                     <td> user plugins
+	<tr><td>./plugins/$arch/                    <td> project plugins, multihost
+	<tr><td>./plugins/                          <td> project plugins
+</table>
+In the above table:
+<ul>
+	<li> $prefix is the installation prefix (normally /usr)
+	<li> $arch is the host arch triplet, e.g. i386-unknown-linux; this is useful if multiple architectures share the same NFS mounted plugin dir
+	<li> ~ is the user home directory, if exists, e.g. /home/jdoe
+	<li> ./ is the current working directory pcb has been started from; may be useful for project-local scripts
+</ul>
+
+<H3> 3.2. Loading scripts </H3>
+The gpmi plugin looks for a file called "pcb-rnd-gpmi.conf" in each of the
+plugin directories. Wherever the file is found, it is loaded and parsed.
+The file is plain text, each line describes a script to be loaded. Empty
+lines and lines starting with # are comments and are ignored.
+<p>
+Script load lines contain two words separated by a space: a module name
+and a script name. Relative paths in the the script name are relative to
+the directory the config file is found in.
+<p>
+Example config:
+<pre>
+# load the carc script (written in lua) from next to the config file:
+lua carc.lua
+
+# load foo.awk, whcih is a mawk script, from its installation path
+mawk /usr/lib/foo/foo.awk
+</pre>
diff --git a/doc-rnd/gpmi/util/Makefile b/doc-rnd/gpmi/util/Makefile
new file mode 100644
index 0000000..1263948
--- /dev/null
+++ b/doc-rnd/gpmi/util/Makefile
@@ -0,0 +1 @@
+all:
diff --git a/doc-rnd/gpmi/util/rosetta_genpages.sh b/doc-rnd/gpmi/util/rosetta_genpages.sh
new file mode 100755
index 0000000..efd89b4
--- /dev/null
+++ b/doc-rnd/gpmi/util/rosetta_genpages.sh
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+clean_id()
+{
+	tr "\r\n" "~" | sed '
+s/~*$//;
+s/|/\&pipe;/g;
+s/&/\&/g;
+s/</\</g;
+s/>/\>/g;
+s/"/\"/g;
+s/'\''/\'/g;
+s/~\+/<br>/g;
+'
+	echo ""
+}
+
+genpage()
+{
+	local dir scripts name desc
+	
+	dir="$1"
+	scripts="$2"
+
+	name=`cat $dir/ID.name`
+	desc=`cat $dir/ID.desc`
+	./tags < "$dir/ex.html" | awk -v "fn_ref=../packages/XREF" -v "scripts=$scripts" -v "name=$name" -v "desc=$desc" '
+	BEGIN {
+		while((getline < fn_ref) > 0) {
+			REF[$2] = $3
+		}
+		print "<html><body>"
+		print "<!-- ******* DO NOT EDIT THIS FILE, it is generated by rosetta_genpages.sh ******* -->"
+		print "<-- back to the <a href=\"../index.html\">index of Rosetta examples</a>"
+		print "<h1>" name "</h1>"
+		print desc
+
+		print "<H2> Example implementations </H2>"
+		v = split(scripts, S, "[\r\n]+")
+		for(n = 1; n <= v; n++) {
+			lang=S[n]
+			sub("^ex[.]", "", lang)
+			if (n != 1)
+				print " | "
+			print "<a href=\"" S[n] "\">" lang "</a>"
+		}
+
+		print "<h2> Explanation, step by step </h2>"
+	}
+	/^<ref>/ {
+		gsub("</?ref>", "", $0)
+		name=$0
+		gsub("[ \t]*", "", name)
+		if (name in REF) {
+			link_begin="<a href=\"../" REF[name]  "\">"
+			link_end = "</a>"
+		}
+		else {
+			link_begin=""
+			link_end=""
+		}
+		print "<i>" link_begin $0 link_end "</i>"
+		next
+	}
+
+	{ print $0 }
+
+	' > "$dir/index.html"
+}
+
+gen_index()
+{
+
+	awk -v "template=$1" '
+		BEGIN {
+			FS="[|]"
+			q = "\""
+			LANGS["rb"] = "ruby"
+			LANGS["pl"] = "perl"
+			LANGS["py"] = "python"
+			LANGS["stt"] = "stutter"
+			LANGS["scm"] = "scheme"
+		}
+
+		($1 == "scripts") {
+			s = $3
+			gsub("ex[.]", "", s)
+			v = split(s, S, " ")
+			s = ""
+			for(n = 1; n <= v; n++) {
+				if (S[n] in LANGS)
+					s = s " " LANGS[S[n]]
+				else
+					s = s " " S[n]
+			}
+			DATA[$2, $1] = s
+			next
+		}
+
+		($1 == "name") {
+			if (names == "")
+				names = $2
+			else
+				names = names "|" $2
+		}
+
+
+		{
+			# DATA[script, name] = "hello world"
+			DATA[$2, $1] = $3
+		}
+
+		function generate(cmd   ,N,n,v,name,level) {
+			if (cmd == "index") {
+				print "<table border=1 cellspacing=0 cellpadding=15>"
+				print "<tr><th>lvl<th>example <th> languages <th> description"
+				v = split(names, N, "[|]")
+				for(n = 1; n <= v; n++) {
+					name = N[n]
+					level = name
+					sub("_.*", "", level)
+					if (level ~ "[^0-9]")
+						level = "n/a"
+					print "<tr>"
+					print " <td>" level
+					print " <td><a href=" q name "/index.html" q ">" DATA[name, "name"] "</a>"
+					print " <td>" DATA[name, "scripts"]
+					print " <td>" DATA[name, "desc"]
+				}
+				print "</table>"
+			}
+			else
+				print "Do not know how to generate " cmd > "/dev/stderr"
+		}
+
+		END {
+			FS=""
+			while((getline < template) > 0) {
+				if (match($0, "<rosetta[ \t][^>]*>")) {
+					print substr($0, 1, RSTART-1)
+					cmd=substr($0, RSTART+8, RLENGTH-9)
+					sub("^[ \t]*", "", cmd)
+					generate(cmd)
+					print substr($0, RSTART+RLENGTH)
+				}
+				else
+					print $0
+			}
+		}
+	'
+
+}
+
+
+for n in ../rosetta/*
+do
+	if test -d "$n"
+	then
+		bn=`basename $n`
+		scripts=`cd $n && ls ex.* | grep -v ".pyc$\|.html$" `
+		genpage "$n" "$scripts"
+		echo -n "desc|$bn|"
+		clean_id < $n/ID.desc
+		echo -n "name|$bn|"
+		clean_id < $n/ID.name
+		echo -n "scripts|$bn|"
+		echo $scripts
+	fi
+done | gen_index ../rosetta/index.templ.html > ../rosetta/index.html
+
+
diff --git a/doc-rnd/gpmi/util/tags b/doc-rnd/gpmi/util/tags
new file mode 100755
index 0000000..37472da
--- /dev/null
+++ b/doc-rnd/gpmi/util/tags
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+tr "\n" " " | sed "s@\(<[^/]\)@\n\1 at g;s@\(</[^>]*>\)@\1\n at g"
+
diff --git a/doc-rnd/gpmi_temp_inst.txt b/doc-rnd/gpmi_temp_inst.txt
new file mode 100644
index 0000000..68d1387
--- /dev/null
+++ b/doc-rnd/gpmi_temp_inst.txt
@@ -0,0 +1,65 @@
+GPMI system-wide installation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The installation process is somewhat manual at this early stage. Please 
+follow the steps below.
+
+First, you'll need libgpmi - it's a general purpose script glue interface.
+
+A/1. svn checkout svn://repo.hu/gpmi/trunk gpmi/trunk
+A/2. look at doc/languages.txt there; if you have preferred languages, 
+please install the -dev packages of those. My favorite is mawk, but I 
+think most of the example pcb scripts would be in lua. Versions should 
+match; it may be that gpmi works with a newer minor version out of the 
+box, tho.
+A/3. run ./configure
+A/4. check if it found the language(s) you wanted; if not, or anything 
+else breaks, please send me your scconfig/config.log
+A/5. make
+A/6. make install (as root, but see below)
+
+There are options to step 6:
+
+- if you want to check what it'd do, you could run this:
+
+   install_root=/tmp/foo make install
+
+   it's like DESTDIR for autotools: it will do everything but will use
+   /tmp/foo instead of / so you see what it'd do without using root or
+   messing with /usr
+
+- you can use make linstall instead of make install; this creates symlinks 
+(pointing to the binaries in your user checkout) instead of copying the 
+files. If gpmi changes and you need to update and recompile, you won't 
+need to reinstall (until the number of files change).
+
+- there's a make uninstall, but it's more or less untested (failed once 
+and worked once for me)
+
+Second, you'll need pcb-rnd:
+
+B/1. svn checkout svn://repo.hu/pcb-rnd/trunk pcb-rnd/trunk
+B/2. ./configure
+B/3. check the output; if anything went wrong, please send me 
+scconfig/config.log
+B/4. make
+B/5. you don't need to install it for testing: cd src; ./pcb-rnd
+
+
+There are options in step 2: by default, if gpmi is found, it's compiled
+in "buildin" mode, which means script support is compiled into the pcb-rnd
+executable. An alternative is to have it as a plugin (dynamic linkable
+object). This can be done using the --plugin-gpmi argument for ./configure.
+
+
+GPMI local installation
+~~~~~~~~~~~~~~~~~~~~~~~
+GPMI can be installed without root, into a local user directory (typically
+under home). To achieve this, follow the above steps with the following
+modifications (assuming you want to install gpmi in ~/usr):
+
+A/3. ./configure --prefix=~/usr
+B/2. ./configure --gpmi-prefix=~/usr
+
+NOTE: you don't need to use install_root; gpmi will install under ~/usr as
+if it was /usr. 
diff --git a/doc-rnd/hacking/c89.html b/doc-rnd/hacking/c89.html
new file mode 100644
index 0000000..9617af3
--- /dev/null
+++ b/doc-rnd/hacking/c89.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+<h1> pcb-rnd - C89 </h1>
+
+The code is written in C, namely C89 for most parts with a few exceptions
+written in C99. It's not yet decided whether the code will be C89 or C99
+long term. If it'll be C89, the C99 parts will be rewritten. If it'll be
+C99, new code can use C99 features freely. Until the decision, keep
+new code C89. Especlially watch out for these:
+<ul>
+	<li> commengt: always use /* */, never //
+	<li> don't mix variable declaration with code - open a new {} block, which will also scope your new variables, or better yet, split up the function into smaller, static functions, the compiler will inline them anyway
+	<li> try to avoid vararg macros, use vararg functions instead
+</ul>
diff --git a/doc-rnd/hacking/data1.png b/doc-rnd/hacking/data1.png
new file mode 100644
index 0000000..3ed1629
Binary files /dev/null and b/doc-rnd/hacking/data1.png differ
diff --git a/doc-rnd/hacking/import.html b/doc-rnd/hacking/import.html
new file mode 100644
index 0000000..8b9226c
--- /dev/null
+++ b/doc-rnd/hacking/import.html
@@ -0,0 +1,64 @@
+<html>
+<body>
+<h1> pcb-rnd hacking - how importers work </h1>
+
+<h2> Introduction </h2>
+This document describes the common aspects of import code, be it an
+import_ plugin or the load part of an io_plugin, importing a footprint,
+loading a board or a netlist.
+
+<h2> Data structures </h2>
+
+The board is stored in PCBType *. Some import code, like the one
+that loads a design (an io plugin's ->parse_pcb function), gets the
+target in an argument. (Other, non-import code usually operate on
+the global variable that represents the current board:
+<i>PCBType *PCB.</i>).
+<p>
+In either case, PCBType's most important field is <i>PCBDataType *data</i>,
+which holds lists and vectors to store <b>all objects on the board</b>. When
+the target is a buffer (e.g. io plugin's ->parse_element function), it's
+a <i>DataType *buf</i> which also have a <i>PCBDataType *data</i> containing
+the same data a board could. 
+<p>
+A PCBDataType struct in turn has three sets of important fields:
+<ul>
+	<li> lists for layer-independent objects (e.g. rat lines, vias, elements)
+	<li> number of layers in use (LayerN) and an array of layers (see later)
+	<li> rtree structures - these are used for looking up objects for locations
+</ul>
+Each layer is a LayerType structure. A layer has the following fields:
+<ul>
+	<li> lists for lines, texts, arcs, polygons
+	<li> some generic layer attributes (colors, flags, key-value pairs)
+	<li> an rtree struct for each object type.
+</ul>
+<p>
+Any code that needs to create objects should use the functions in create.[ch].
+The functions that operate layer-independently usually get a destination as
+<i>PCBDataType</i>, so they can operate both on boards and buffers. A typical
+example is <i>PinTypePtr CreateNewVia(DataTypePtr Data, ...)</i> that returns
+NULL or the pointer to the new via that is already added to the corresponding list
+of Data and in the rtree.
+<p>
+Layer object creation is done referencing the layer as <i>LayerType *</i>. A
+typical example is <i>LineTypePtr CreateDrawnLineOnLayer(LayerTypePtr Layer, ...)</i>
+which returns NULL on error or a pointer to the new line object created (inserted
+in the layer's line list, added to the rtree).
+<p>
+Code should <b>avoid</b> manipulating the lists and rtree structures directly.
+<table border=1>
+<tr><td><img src="data1.png">
+<br>
+Figure 1. simplified map of object related data structures
+<br>
+<small>diamond: variable; rectangle: struct; round: struct field</small>
+</table>
+
+
+<h2> Extra steps around creating a new board </h2>
+TODO: postproc, post*, other setup
+
+
+
+
diff --git a/doc-rnd/hacking/indent.html b/doc-rnd/hacking/indent.html
new file mode 100644
index 0000000..d68f883
--- /dev/null
+++ b/doc-rnd/hacking/indent.html
@@ -0,0 +1,15 @@
+<html>
+<body>
+<h1> pcb-rnd - indentation </h1>
+
+As of r1022 all old code is converted using indent(1) with the following
+command line:
+
+<pre>
+indent --line-length128 -brs -br -nce --tab-size2 -ut -npsl  -npcs -hnl $*
+</pre>
+<p>
+Contributors are kindly asked to follow this style or run the above commandline
+on their code before sending a patch or commiting to svn.
+</body>
+</html>
diff --git a/doc-rnd/hacking/plugin_core_simple.html b/doc-rnd/hacking/plugin_core_simple.html
new file mode 100644
index 0000000..1473930
--- /dev/null
+++ b/doc-rnd/hacking/plugin_core_simple.html
@@ -0,0 +1,59 @@
+<html>
+<body>
+<h1> pcb-rnd hacking - simple core plugins </h1>
+
+<h2> Introduction </h2>
+
+<h2> Setting up a new core plugin </h2>
+<ul>
+	<li> 1. make up a name for the plugin. There are a few <a href="plugin_naming.html"> conventions. </a> This document will refer to the name as <i>plg</i>, always replace it with your chosen name.
+	<li> 2. cd src_plugins; svn mkdir <i>plg</i>
+	<li> 3. copy Makefile, Plug.tmpasm and README from another, small plugin, for example from the <i>report</i> plugin (the next points will assume you picked this plugin as the source of the copy)
+	<li> 4. edit Makefile: replace <b>report</b> to <i>plg</i>
+	<li> 5. edit README; there should be short summary about the purpose of the plugin then "#key: value" pairs for statistics:
+		<ul>
+			<li> <i>#state:</i> either <b>works</b> or <b>disabled</b>
+			<li> <i>#lstate:</i> if state is disabled, add a new line <b>#lstate:</b> with a few words on why the plugin is disabled
+			<li> <i>#default:</i> <b>disabled</b> or <b>builtin</b> or <b>plugin</b> (this field might be removed later because of redundancy)
+			<li> <i>#implements:</i> the same as the plugin name prefix (see <a href="plugin_naming.html"> conventions. </a>) or <b>(feature)</b> for feature plugins; it's redundant but is kept for supporting external plugins
+		</ul>
+	<li> 6. edit Plug.tmpasm:
+		<pre>
+put /local/pcb/mod {<i>plg</i>}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/<i>plg</i>/foo.o $(PLUGDIR)/<i>plg</i>/bar.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/<i>plg</i>/foo_conf.h}
+put /local/pcb/mod/YACC [@ $(PLUGDIR)/<i>plg</i>/bary @]
+put /local/pcb/mod/LEX  [@ $(PLUGDIR)/<i>plg</i>/barl @]
+		</pre>
+
+		<ul>
+			<li> /local/pcb/mod: the name of the module we are compiling
+			<li> /local/pcb/mod/OBJS: a space separated list of object files to compile 
+			<li> /local/pcb/mod/CONF: optional: the name of the <a href="plugin_conf.h"> conf struct header </a> - if the plugin doesn't have runtime configuration settings, remove this line
+			<li> /local/pcb/mod/YACC: optional: if you have files for yacc-processing, list them without the .y suffix here (space separated)
+			<li> /local/pcb/mod/LEX: optional: if you have files for lex-processing, list them without the .l suffix here (space separated)
+			<li> Leave the switch part on the bottom intact.
+		</ul>
+	<li> 7. create your C sources and headers in src_plugins/<i>plg</i>/
+	<li> 8. edit scconfig/plugins.h: copy an existing line and edit it:
+<pre>
+plugin_def("<i>plg</i>",       "<i>short description</i>",       <i>default</i>)
+</pre>
+	Default is one of:
+	<ul>
+		<li> sbuildin - unless the user requests otherwise, your plugin is linked into the executable
+		<li> sdisable - unless the user requests otherwise, your plugin is not compiled (recommended until the plugin gets some user testing)
+		<li> splugin - unless the user requests otherwise, your plugin is dynamically linked; not recommended: the policy is to let users explicitly request dynamic linking
+	</ul>
+	The order of entries in the file is the same as the order in the summary table
+	at the end of ./configure; insert your plugin in the right group!
+	<li> 9. add your plugin to the build list in one of the src_plugins/plugins_*.tmpasm files; choose the file that lists plugins similar to yours.
+	<li> 10. run ./configure --buildin-<i>plg</i>
+	<li> 11. run make to see if it compiles and test
+	<li> (12. while developing, you can run make from src_plugins/<i>plg</i> - if
+	your plugin is a builtin, it will recompile pcb-rnd. For some of us it's convenient
+	to run make in the same directory where all the source files are.)
+</ul>
+
+</body>
+</html>
diff --git a/doc-rnd/hacking/plugin_naming.html b/doc-rnd/hacking/plugin_naming.html
new file mode 100644
index 0000000..b4f9afb
--- /dev/null
+++ b/doc-rnd/hacking/plugin_naming.html
@@ -0,0 +1,12 @@
+<html>
+<body>
+<h1> pcb-rnd hacking - plugin naming conventions </h1>
+	<ul>
+		<li> export plugins start with export_; these render to a non-native format that can not be then loaded
+		<li> native formats are prefixed with io_; even if your plugin can now only load or save, if it's possible to do a load-save-load round-trip without data loss, it's an io_
+		<li> interactive, often GUI, frontends are prefixed with hid_
+		<li> footprint access plugins start with fp_
+		<li> anything else is considered a feature plugin and has no specific prefix.
+	</ul>
+</body>
+</html>
diff --git a/doc-rnd/hacking/releasing.txt b/doc-rnd/hacking/releasing.txt
new file mode 100644
index 0000000..22772fc
--- /dev/null
+++ b/doc-rnd/hacking/releasing.txt
@@ -0,0 +1,11 @@
+release check list
+
+0. test compile and test run a clean checkout on a clean system
+   configure with --debug --all=buildin, then without
+1. check next version number and previous revision in tags/
+2. update the changelog
+3. rewrite the release notes
+4. modify version number in scconfig
+5. update changelog in debian/
+6. commit trunk
+7. svn tag using URLs
diff --git a/doc-rnd/hacking/src/Makefile b/doc-rnd/hacking/src/Makefile
new file mode 100644
index 0000000..ec0d08d
--- /dev/null
+++ b/doc-rnd/hacking/src/Makefile
@@ -0,0 +1,2 @@
+../data1.png: data1.dot
+	dot -Tpng $^ > $@
diff --git a/doc-rnd/hacking/src/data1.dot b/doc-rnd/hacking/src/data1.dot
new file mode 100644
index 0000000..1baecdd
--- /dev/null
+++ b/doc-rnd/hacking/src/data1.dot
@@ -0,0 +1,47 @@
+digraph pcb_data {
+	PCBType_misc [label="misc fields:\nID\nName\nFileName\ncolors\ndrc settings\ncursor coords\ngrid\nlayergroups\nroute styles\n..."]
+	PCBType_flags [label="flags:\nChanged\nViaOn (vias drawn?)\n..."]
+	PCBType -> PCBType_misc
+	PCBType -> PCBType_flags
+	PCBType -> DataType
+	PCBType [shape=box]
+
+	PCB [label="extern PCBType PCB\nglobal variable\nholding the current\nboard" shape=diamond]
+	PCB -> PCBType
+
+	Buffers [label="extern BufferType Buffers[]\nglobal variable holding\nall paste buffers" shape=diamond]
+	Buffers -> BufferType
+
+	BufferType_misc [label="misc fields:\nbounding box\noffset"]
+	BufferType -> BufferType_misc
+	BufferType -> DataType
+	BufferType [shape=box]
+
+	DataType_lists [label="layer-independent lists:\nrats\nvias\nelements"]
+	DataType_rtrees [label="layer-independent rtrees"]
+	DataType_LayerN [label="LayerN: number of\nlayers in use"]
+	DataType_layers [label="an array of layers"]
+
+	DataType -> DataType_misc
+	DataType -> DataType_LayerN
+	DataType -> DataType_layers
+	DataType -> DataType_lists
+	DataType -> DataType_rtrees
+	DataType [shape=box]
+
+	DataType_layers -> LayerType
+
+
+	LayerType_lines [label="list and rtree of lines"]
+	LayerType_arcs [label="list and rtree of arcs"]
+	LayerType_texts [label="list and rtree of text objects"]
+	LayerType_polygons [label="list and rtree of polygons"]
+	LayerType_misc [label="misc fields:\nflags\ncolors"]
+
+	LayerType -> LayerType_misc
+	LayerType -> LayerType_lines
+	LayerType -> LayerType_arcs
+	LayerType -> LayerType_texts
+	LayerType -> LayerType_polygons
+	LayerType [shape=box]
+}
diff --git a/doc-rnd/help.html b/doc-rnd/help.html
new file mode 100644
index 0000000..0f112b4
--- /dev/null
+++ b/doc-rnd/help.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+	<title> pcb-rnd - help wanted </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - help wanted </h1>
+The project is looking for volunteers for the features listed below. If
+you want to contribute, you will need to have an svn client and an email
+client. Furthermore an IRC client is strongly recommended. 
+<p>
+You will become part of the team and get svn commit right immediately
+when you start working.
+<p>
+<b>Don't have much free time?</b> Don't worry: the tasks below are split up in
+very small (1..2 hours) chunks. Coordination is granted, the administrative
+overhead is almost zero - you can focus on the task, and finish it whenever
+you have a free hour.
+<p>
+Worrying about your <b>contribution to go in /dev/null?</b> Don't: pcb-rnd
+has very short loops; you start contributing and get immediate feedback. Your
+work is part of the official thing immediately and revised and accepted (or
+rarely refused) within hours or at most days.
+<b>There are no bitrotting branches.</b>
+<p>
+You are not confident enough with your skills? pcb-rnd is an optimal
+project for learning. If you start contributing, you get support. There
+are small and simple entry level tasks. Most of the tasks don't require
+any programming skills.
+<p>
+Sign up: mail to pcb-rnd (at) igor2.repo.hu .
+If you want to work on a feature not listed below, feel free to drop me
+a mail.
+<p>
+<table border=1 cellspacing=0 cellpadding=0>
+<tr><th> ID        <th> skill required <th> description
+<tr><td> tutorial  <td> geda user      <td> tutorial projects
+<tr><td> windows   <td> geda user      <td> generic testing on windows
+<tr><td> mtest     <td> geda user      <td> systematic manual testing
+<tr><td> doc       <td> geda user, html<td> write sections of user documentation
+<tr><td> logo      <td> designer       <td> the official pcb-rnd logo
+<tr><td> banner    <td> designer       <td> pcb-rnd banner for the homepage
+<tr><td> icons     <td> designer       <td> icons, other graphical elements for the GTK HID
+<tr><td> css       <td> designer       <td> CSS for the homepage (only design/style, no content).
+<tr><td> atest     <td> C/beginner     <td> program automated test cases
+</table>
+</body>
+</html>
diff --git a/doc-rnd/index.html b/doc-rnd/index.html
new file mode 100644
index 0000000..5361d48
--- /dev/null
+++ b/doc-rnd/index.html
@@ -0,0 +1,123 @@
+<html>
+<head>
+	<title> pcb-rnd - main </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<br>
+<!--content-->
+<table border=0 cellpadding=10> <tr>
+<td valign=top bgcolor=#f2f2f2>
+	<h2> Summary </h2>
+	<table border=0 cellpadding=20>
+	<tr><th align=right valign=top bgcolor=#ccccff> 
+		<table border=0 width=100% cellpadding=0 cellspacing=0><tr><td align=left><img src="logo128.png"><th align=right>pcb-rnd</table>
+		<td bgcolor=#ddddff>
+		    <b>is a flexible, modular Printed Circuit Board editor</b>
+		<p><a href="motivation.html">historically</a> is a fork of <a href="http://pcb.geda-project.org">PCB</a>
+		<p>is an informal part of the geda project
+		<p>features a lot of small and large <a href="features/index.html">improvements and bugfixes</a>
+		
+
+	<tr><th align=right valign=top bgcolor=#ccccff> Version Control              <td bgcolor=#ddddff> <a href="http://igor2.repo.hu/cgi-bin/minisvn.cgi?cmd=browse&repo=pcb-rnd"> svn://repo.hu/pcb-rnd/trunk </a>
+	<tr><th align=right valign=top bgcolor=#ccccff> Download                     <td bgcolor=#ddddff> <a href="releases/"> source releases </a>
+	<tr><th align=right valign=top bgcolor=#ccccff> Comments, feedback, patches  <td bgcolor=#ddddff> <a href="irc.html"> live chat with the developer </a><br>or mail to: pcb-rnd (at) igor2.repo.hu<br>Mailing list: pcb-rnd (at) list.repo.hu (send a mail with subject: subscribe)
+	<tr><th align=right valign=top bgcolor=#ccccff> Contribution and support     <td bgcolor=#ddddff>
+		    How to <a href="contrib.html"> join or contribute </a>
+		<br>We are looking for <a href="help.html"> help</a>.
+		<br>Do you have a <a href="myfeature.html">feature request</a>?
+
+	<tr><th align=right valign=top bgcolor=#ccccff> Key features <td bgcolor=#ddddff>
+		     editor for multilayer Printed Circuit Boards
+		<br> <a href="features/gpmi.html"> scriptable </a> in 10+ different scripting languages
+		<br> <a href="features/pcblib.html">parametric footprint</a> generation, <a href="features/fp_wget.html"> web footprints </a>
+		<br> <a href="mods3/">modular code</a> with a flexible plugin system
+		<br> fits well in a UNIXy workflow
+		<br> supports CLI and server applications
+		<br> active development, frequent releases
+		<br> friendly and efficient developer and user community
+		<br> <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> predictable development cycles </a>
+
+	<tr><th align=right valign=top bgcolor=#ccccff> Suported platforms <td bgcolor=#ddddff>
+		     Linux desktop (various distributions, from source)
+		<br> <a href="mac.txt"> Mac OS X </a>
+		<br> <a href="https://aur.archlinux.org/packages/pcb-rnd-svn/"> Arch Linux (user package) </a>
+<!--		<br> <a href="windows.txt"> Windows </a> -->
+		<br> <a href="UNIX.txt"> IRIX 5.3 </a>
+		<br> (Likely: any 90's UNIX system with motif)
+		<br> Screen resolution as small as 800x600
+		<br> GUI options: motif/lesstif, gtk
+		<br><table border=0 width=100%><tr><td align=right><img src="screenshot.jpg"></table>
+</table>
+
+<!-- spacer -->
+	<td width=0>
+
+<!-- rnd -->
+	<td width=20% bgcolor=#f2f2f2>
+	<h2> What is -rnd? </h2>
+	<br>
+	<table border=0 cellspacing=10 cellpadding=0  bgcolor=#ccffcc>
+	<tr><td><font size=+2><b>R</b>a<b>ND</b>om</font> <td>
+	    When it started, it used to be a <a href="motivation.html">random collection of small fixes and improvements.</a> We got much further than that by now.
+	</table>
+
+	<p>
+
+	<table border=0 cellspacing=10 cellpadding=0  bgcolor=#ccffcc>
+	<tr>
+	<td><font size=+2><b>R</b>espo<b>N</b>sive <b>D</b>evelopers</font>
+	<td> Developers try to respond on user needs, adding features that are actually needed by current users.
+	</table>
+
+	<p>
+
+	<table border=0 cellspacing=10 cellpadding=0  bgcolor=#ccffcc>
+	<tr><td>
+	<font size=+2><b>R</b>esponse <b>N</b>ot <b>D</b>elayed</font>
+	<td> Bugrepots, user requests and patches submitted are answered ASAP. There are no patches bitrotting for months. There are no forgotten bugreports.
+	</table>
+
+	<p>
+
+	<table border=0 cellspacing=10 cellpadding=0  bgcolor=#ccffcc width=400>
+	<tr>
+	<td><font size=+2><b>R</b>esearch <br><b>&</b> <br><b>D</b>evelopment</font>
+	<br>
+	<br>
+	<b>R</b>�z�s, <br><b>N</b>eh�z <br><b>D</b>�nt�sek <br><small>(Brave, hard decisions)</small>
+	<td> There is a constant experimentation with new features and directions. We are willing to try strange/unusual ideas without risking the stability of the daily workflow.
+	</table>
+
+	<p>
+
+	<table border=0 cellspacing=10 cellpadding=0  bgcolor=#ccffcc>
+	<tr>
+	<td><font size=+2><b>R</b>ants <br><b>N</b>ow <br><b>D</b>issipating</font>
+	<td> Instead of talking and ranting a lot about what could work better, we just sit down and make it work better.
+	</table>
+
+	<p>
+
+	<table border=0 cellspacing=10 cellpadding=0  bgcolor=#ccffcc>
+	<tr>
+	<td><font size=+2><b>R</b>ather <br><b>N</b>icely <br><b>D</b>ecentralized</font>
+	<td> <a href="mods3/"> Slim, generic core</a>; most of the code organized in replacable plugins. Most plugins depend only on the core.
+	</table>
+
+</body>
+</html>
diff --git a/doc-rnd/irc.html b/doc-rnd/irc.html
new file mode 100644
index 0000000..176a499
--- /dev/null
+++ b/doc-rnd/irc.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+	<title> pcb-rnd - IRC </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<H1> Live support via IRC (chat) </H1>
+<br>Irc <b>server</b>: repo.hu       <b>Port</b>: 6667       <b>Channel</b>: #pcb-rnd
+<br>  
+<p>I'm normally online as Igor2 between 5:00 CET and 19:00 CET; on Sunday there's an AFK-window between 11:00 CET and 16:00 CET. If I don't answer in a few minutes I'm probably away doing something, please be patient; say hi before you type your long question so you see if I'm available.
+<hr>
+<p>
+Below is an iframe of <a href="https://kiwiirc.com/client/repo.hu/?nick=pcbuser|?#pcb-rnd">
+kiwiirc </a> irc client configured with the above settings. If it doesn't
+work, please visit their <a href="https://kiwiirc.com"> page </a> and
+try to manually configure it, it's just 4 fields.
+<p>
+Using the web client: you may change your nickname; there's no password
+or registration or channel key or ssl of any form. Your real IP won't be
+shown on the IRC network (check kiwiirc's page about thier privacy
+policy).
+<p>
+Connecting with the web client may take some time, give it a minute.
+<hr>
+
+<iframe src="https://kiwiirc.com/client/repo.hu/?nick=pcbuser|?#pcb-rnd" style="border:0; width:100%; height:450px;"></iframe>
+
+</html>
+</body>
diff --git a/doc-rnd/keys.html b/doc-rnd/keys.html
new file mode 100644
index 0000000..e9f27c9
--- /dev/null
+++ b/doc-rnd/keys.html
@@ -0,0 +1,684 @@
+<html><body>
+<h1> Key to action bindings </h1>
+<table border=1 cellspacing=0>
+<tr><th> key <th>pcb-menu-lesstif.lht <th>pcb-menu-gtk.lht
+<tr bgcolor=#DDFFFF>
+	<th align=left>.
+	<td> <I>'All-direction' lines</I><br>conf(toggle, editor/all_direction_lines, design) 
+	<td> <I>'All-direction' lines</I><br>conf(toggle, editor/all_direction_lines, design) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>/
+	<td> <I>Cycle Clip</I><br>Display(CycleClip) 
+	<td> <I>Cycle Clip</I><br>Display(CycleClip) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>0
+	<td> <I>Select Layer 10</I><br>SelectLayer(10) 
+	<td> <I>Select Layer 10</I><br>SelectLayer(10) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>0-alt
+	<td> <I>Select Layer 20</I><br>SelectLayer(20) 
+	<td> <I>Select Layer 20</I><br>SelectLayer(20) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>0-alt-ctrl
+	<td> <I>Toggle Layer 20</I><br>ToggleView(20) 
+	<td> <I>Toggle Layer 20</I><br>ToggleView(20) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>0-ctrl
+	<td> <I>Toggle Layer 10</I><br>ToggleView(10) 
+	<td> <I>Toggle Layer 10</I><br>ToggleView(10) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>1
+	<td> <I>Select Layer 1</I><br>SelectLayer(1) 
+	<td> <I>Select Layer 1</I><br>SelectLayer(1) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>1-alt
+	<td> <I>Select Layer 11</I><br>SelectLayer(11) 
+	<td> <I>Select Layer 11</I><br>SelectLayer(11) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>1-alt-ctrl
+	<td> <I>Toggle Layer 11</I><br>ToggleView(11) 
+	<td> <I>Toggle Layer 11</I><br>ToggleView(11) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>1-ctrl
+	<td> <I>Toggle Layer 1</I><br>ToggleView(1) 
+	<td> <I>Toggle Layer 1</I><br>ToggleView(1) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>1-shift
+	<td> <I>#1</I><br>PasteBuffer(1) 
+	<td> <I>Select Buffer #1</I><br>PasteBuffer(1)
+<tr bgcolor=#FFFFFF>
+	<th align=left>2
+	<td> <I>Select Layer 2</I><br>SelectLayer(2) 
+	<td> <I>Select Layer 2</I><br>SelectLayer(2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>2-alt
+	<td> <I>Select Layer 12</I><br>SelectLayer(12) 
+	<td> <I>Select Layer 12</I><br>SelectLayer(12) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>2-alt-ctrl
+	<td> <I>Toggle Layer 12</I><br>ToggleView(12) 
+	<td> <I>Toggle Layer 12</I><br>ToggleView(12) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>2-ctrl
+	<td> <I>Toggle Layer 2</I><br>ToggleView(2) 
+	<td> <I>Toggle Layer 2</I><br>ToggleView(2) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>2-shift
+	<td> <I>#2</I><br>PasteBuffer(2) 
+	<td> <I>Select Buffer #2</I><br>PasteBuffer(2)
+<tr bgcolor=#DDFFFF>
+	<th align=left>3
+	<td> <I>Select Layer 3</I><br>SelectLayer(3) 
+	<td> <I>Select Layer 3</I><br>SelectLayer(3) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>3-alt
+	<td> <I>Select Layer 13</I><br>SelectLayer(13) 
+	<td> <I>Select Layer 13</I><br>SelectLayer(13) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>3-alt-ctrl
+	<td> <I>Toggle Layer 13</I><br>ToggleView(13) 
+	<td> <I>Toggle Layer 13</I><br>ToggleView(13) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>3-ctrl
+	<td> <I>Toggle Layer 3</I><br>ToggleView(3) 
+	<td> <I>Toggle Layer 3</I><br>ToggleView(3) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>3-shift
+	<td> <I>#3</I><br>PasteBuffer(3) 
+	<td> <I>Select Buffer #3</I><br>PasteBuffer(3)
+<tr bgcolor=#FFFFFF>
+	<th align=left>4
+	<td> <I>Select Layer 4</I><br>SelectLayer(4) 
+	<td> <I>Select Layer 4</I><br>SelectLayer(4) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>4-alt
+	<td> <I>Select Layer 14</I><br>SelectLayer(14) 
+	<td> <I>Select Layer 14</I><br>SelectLayer(14) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>4-alt-ctrl
+	<td> <I>Toggle Layer 14</I><br>ToggleView(14) 
+	<td> <I>Toggle Layer 14</I><br>ToggleView(14) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>4-ctrl
+	<td> <I>Toggle Layer 4</I><br>ToggleView(4) 
+	<td> <I>Toggle Layer 4</I><br>ToggleView(4) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>4-shift
+	<td> <I>#4</I><br>PasteBuffer(4) 
+	<td> <I>Select Buffer #4</I><br>PasteBuffer(4)
+<tr bgcolor=#DDFFFF>
+	<th align=left>5
+	<td> <I>Select Layer 5</I><br>SelectLayer(5) 
+	<td> <I>Select Layer 5</I><br>SelectLayer(5) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>5-alt
+	<td> <I>Select Layer 15</I><br>SelectLayer(15) 
+	<td> <I>Select Layer 15</I><br>SelectLayer(15) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>5-alt-ctrl
+	<td> <I>Toggle Layer 15</I><br>ToggleView(15) 
+	<td> <I>Toggle Layer 15</I><br>ToggleView(15) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>5-ctrl
+	<td> <I>Toggle Layer 5</I><br>ToggleView(5) 
+	<td> <I>Toggle Layer 5</I><br>ToggleView(5) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>5-shift
+	<td> <I>#5</I><br>PasteBuffer(5) 
+	<td> <I>Select Buffer #5</I><br>PasteBuffer(5)
+<tr bgcolor=#FFFFFF>
+	<th align=left>6
+	<td> <I>Select Layer 6</I><br>SelectLayer(6) 
+	<td> <I>Select Layer 6</I><br>SelectLayer(6) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>6-alt
+	<td> <I>Select Layer 16</I><br>SelectLayer(16) 
+	<td> <I>Select Layer 16</I><br>SelectLayer(16) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>6-alt-ctrl
+	<td> <I>Toggle Layer 16</I><br>ToggleView(16) 
+	<td> <I>Toggle Layer 16</I><br>ToggleView(16) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>6-ctrl
+	<td> <I>Toggle Layer 6</I><br>ToggleView(6) 
+	<td> <I>Toggle Layer 6</I><br>ToggleView(6) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>7
+	<td> <I>Select Layer 7</I><br>SelectLayer(7) 
+	<td> <I>Select Layer 7</I><br>SelectLayer(7) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>7-alt
+	<td> <I>Select Layer 17</I><br>SelectLayer(17) 
+	<td> <I>Select Layer 17</I><br>SelectLayer(17) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>7-alt-ctrl
+	<td> <I>Toggle Layer 17</I><br>ToggleView(17) 
+	<td> <I>Toggle Layer 17</I><br>ToggleView(17) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>7-ctrl
+	<td> <I>Toggle Layer 7</I><br>ToggleView(7) 
+	<td> <I>Toggle Layer 7</I><br>ToggleView(7) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>8
+	<td> <I>Select Layer 8</I><br>SelectLayer(8) 
+	<td> <I>Select Layer 8</I><br>SelectLayer(8) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>8-alt
+	<td> <I>Select Layer 18</I><br>SelectLayer(18) 
+	<td> <I>Select Layer 18</I><br>SelectLayer(18) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>8-alt-ctrl
+	<td> <I>Toggle Layer 18</I><br>ToggleView(18) 
+	<td> <I>Toggle Layer 18</I><br>ToggleView(18) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>8-ctrl
+	<td> <I>Toggle Layer 8</I><br>ToggleView(8) 
+	<td> <I>Toggle Layer 8</I><br>ToggleView(8) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>9
+	<td> <I>Select Layer 9</I><br>SelectLayer(9) 
+	<td> <I>Select Layer 9</I><br>SelectLayer(9) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>9-alt
+	<td> <I>Select Layer 19</I><br>SelectLayer(19) 
+	<td> <I>Select Layer 19</I><br>SelectLayer(19) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>9-alt-ctrl
+	<td> <I>Toggle Layer 19</I><br>ToggleView(19) 
+	<td> <I>Toggle Layer 19</I><br>ToggleView(19) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>9-ctrl
+	<td> <I>Toggle Layer 9</I><br>ToggleView(9) 
+	<td> <I>Toggle Layer 9</I><br>ToggleView(9) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>:
+	<td> <I>Command</I><br>Command() 
+	<td> <I>Command Entry</I><br>Command() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>=
+	<td> <I>SimpleOpts</I><br>djopt(simple) 
+	<td> <I>Simple optimization</I><br>djopt(simple) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>=-shift
+	<td> <I>Auto-Optimize</I><br>djopt(auto) 
+	<td> <I>Auto-Optimize</I><br>djopt(auto) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>[
+	<td> <I>Temp Arrow ON</I><br>Mode(Save); Mode(Arrow); Mode(Notify)
+	<td> <I>Temp Arrow ON</I><br>Mode(Save); Mode(Arrow); Mode(Notify)
+<tr bgcolor=#FFFFFF>
+	<th align=left>\
+	<td>  
+	<td> <I>Full screen</I><br>fullscreen(toggle) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>]
+	<td> <I>Temp Arrow OFF</I><br>Mode(Release); Mode(Restore)
+	<td> <I>Temp Arrow OFF</I><br>Mode(Release); Mode(Restore)
+<tr bgcolor=#FFFFFF>
+	<th align=left>`
+	<td> <I>Zoom Toggle</I><br>Zoom(Toggle) 
+	<td>  
+<tr bgcolor=#DDFFFF>
+	<th align=left>a
+	<td> <I>Set Same</I><br>SetSame() 
+	<td> <I>Set Same</I><br>SetSame() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>a-alt
+	<td> <I>Select all visible</I><br>Select(All) 
+	<td> <I>Select all visible</I><br>Select(All) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>a-alt-shift
+	<td> <I>Unselect all</I><br>Unselect(All) 
+	<td> <I>Unselect all</I><br>Unselect(All) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>b
+	<td> <I>Flip Object</I><br>Flip(Object) 
+	<td> <I>Flip Object</I><br>Flip(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>b-alt-ctrl
+	<td>  
+	<td> <I>netlist patch for back annotation</I><br>SavePatch() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>b-shift
+	<td> <I>Move selected elements to other side</I><br>Flip(SelectedElements) 
+	<td> <I>Move selected elements to other side</I><br>Flip(SelectedElements) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>backspace
+	<td> <I>Remove</I><br>Delete(Selected) 
+	<td> <I>Remove Selected</I><br>RemoveSelected() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>backspace-shift
+	<td> <I>Remove Connected</I><br>Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)
+	<td> <I>Remove Connected</I><br>Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)
+<tr bgcolor=#DDFFFF>
+	<th align=left>c
+	<td> <I>Center cursor</I><br>Center() 
+	<td> <I>Center cursor</I><br>Center() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>c-ctrl
+	<td> <I>Copy selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Mode(PasteBuffer)
+	<td> <I>Copy selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Unselect(All); Mode(PasteBuffer)
+<tr bgcolor=#DDFFFF>
+	<th align=left>d
+	<td> <I>Pins/Via show Name/Number</I><br>Display(PinOrPadName) 
+	<td> <I>Pins/Via show Name/Number</I><br>Display(PinOrPadName) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>d-shift
+	<td> <I>Pinout</I><br>Display(Pinout) 
+<br> <b>Error: key prefix collision</b>
+	<td> <I>Pinout</I><br>Display(Pinout) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>delete
+	<td> <I>Delete selected objects</I><br>Delete(Selected) 
+	<td> <I>Remove</I><br>Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore)
+<tr bgcolor=#FFFFFF>
+	<th align=left>delete-shift
+	<td> <I>Remove Connected</I><br>Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)
+	<td> <I>Remove selected objects</I><br>RemoveSelected() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>down
+	<td> <I>Step Down</I><br>Cursor(Warp,0,-1,grid) 
+	<td> <I>Step Down</I><br>Cursor(Warp,0,-1,grid) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>down-shift
+	<td> <I>Step +Down</I><br>Cursor(Pan,0,-50,view) 
+	<td> <I>Step +Down</I><br>Cursor(Pan,0,-50,view) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>e
+	<td> <I>Erase rats-nest</I><br>DeleteRats(AllRats) 
+	<td> <I>Erase rats nest</I><br>DeleteRats(AllRats) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>e-ctrl
+	<td>  
+	<td> <I>Edit properties of selected...</I><br>PropEdit(Selected) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>e-shift
+	<td> <I>Erase selected rats</I><br>DeleteRats(SelectedRats) 
+	<td> <I>Erase selected rats</I><br>DeleteRats(SelectedRats) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>enter
+	<td> <I>"Click"</I><br>Mode(Notify); Mode(Release)
+	<td> <I>Click</I><br>Mode(Notify); Mode(Release)
+<tr bgcolor=#DDFFFF>
+	<th align=left>escape
+	<td> <I>Cancel</I><br>Mode(Cancel) 
+	<td> <I>Cancel</I><br>Mode(Escape) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>f
+	<td> <I>Find Connections</I><br>Connection(Reset); Connection(Find)
+	<td> <I>Find Connections</I><br>Connection(Reset); Connection(Find)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f-alt-shift
+	<td>  
+	<td> <I>Replace footprint</I><br>ReplaceFootprint() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>f-ctrl
+	<td> <I>Lookup connection to object</I><br>GetXY(Click on the object); Connection(Find)
+	<td> <I>Lookup connection to object</I><br>GetXY(Click on the object); Connection(Find)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f-shift
+	<td> <I>Reset all connections</I><br>Connection(Reset); Display(Redraw)
+	<td> <I>Reset all connections</I><br>Connection(Reset); Display(Redraw)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f1
+	<td> <I>Via</I><br>Mode(Via) 
+	<td> <I>Via</I><br>Mode(Via)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f10
+	<td> <I>Thermal</I><br>Mode(Thermal) 
+	<td> <I>Thermal</I><br>Mode(Thermal)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f11
+	<td> <I>Arrow</I><br>Mode(Arrow) 
+	<td> <I>Arrow</I><br>Mode(Arrow)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f12
+	<td> <I>Lock</I><br>Mode(Lock) 
+	<td> <I>Lock</I><br>Mode(Lock)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f2
+	<td> <I>Line</I><br>Mode(Line) 
+	<td> <I>Line</I><br>Mode(Line)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f3
+	<td> <I>Arc</I><br>Mode(Arc) 
+	<td> <I>Arc</I><br>Mode(Arc)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f4
+	<td> <I>Text</I><br>Mode(Text) 
+	<td> <I>Text</I><br>Mode(Text)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f5
+	<td> <I>Rectangle</I><br>Mode(Rectangle) 
+	<td> <I>Rectangle</I><br>Mode(Rectangle)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f6
+	<td> <I>Polygon</I><br>Mode(Polygon) 
+	<td> <I>Polygon</I><br>Mode(Polygon)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f7
+	<td> <I>Buffer</I><br>Mode(PasteBuffer) 
+	<td> <I>Buffer</I><br>Mode(PasteBuffer)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f7-shift
+	<td> <I>Rotate buffer 90 deg CCW</I><br>Mode(PasteBuffer); PasteBuffer(Rotate,1)
+	<td> <I>Rotate buffer 90 deg CCW</I><br>Mode(PasteBuffer); PasteBuffer(Rotate,1)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f8
+	<td> <I>Remove</I><br>Mode(Remove) 
+	<td> <I>Remove</I><br>Mode(Remove)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f9
+	<td> <I>Rotate</I><br>Mode(Rotate) 
+	<td> <I>Rotate</I><br>Mode(Rotate)
+<tr bgcolor=#DDFFFF>
+	<th align=left>g
+	<td> <I>Grid +5mil</I><br>SetValue(Grid,+5,mil) 
+	<td> <I>Grid +5mil</I><br>SetValue(Grid,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>g-ctrl
+	<td> <I>Grid +0.05mm</I><br>SetValue(Grid,+0.05,mm) 
+	<td> <I>Grid +0.05mm</I><br>SetValue(Grid,+0.05,mm) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>g-ctrl-shift
+	<td> <I>Grid -0.05mm</I><br>SetValue(Grid,-0.05,mm) 
+	<td> <I>Grid -0.05mm</I><br>SetValue(Grid,-0.05,mm) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>g-shift
+	<td> <I>Grid -5mil</I><br>SetValue(Grid,-5,mil) 
+	<td> <I>Grid -5mil</I><br>SetValue(Grid,-5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>h
+	<td> <I>ToggleHideName Object</I><br>ToggleHideName(Object) 
+	<td> <I>ToggleHideName Object</I><br>ToggleHideName(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>h-ctrl
+	<td> <I>ChangeHole Object</I><br>ChangeHole(Object) 
+	<td> <I>ChangeHole Object</I><br>ChangeHole(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>h-shift
+	<td> <I>ToggleHideName SelectedElement</I><br>ToggleHideName(SelectedElements) 
+	<td> <I>ToggleHideName SelectedElement</I><br>ToggleHideName(SelectedElements) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>i
+	<td>  
+	<td> <I>Library</I><br>DoWindows(Library) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>insert
+	<td> <I>Insert Point</I><br>Mode(InsertPoint) 
+	<td> <I>Insert Point</I><br>Mode(InsertPoint)
+<tr bgcolor=#FFFFFF>
+	<th align=left>j
+	<td> <I>ChangeJoin Object</I><br>ChangeJoin(Object) 
+	<td> <I>ChangeJoin Object</I><br>ChangeJoin(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>j-shift
+	<td> <I>ChangeJoin SelectedObject</I><br>ChangeJoin(SelectedObjects) 
+	<td> <I>ChangeJoin SelectedObject</I><br>ChangeJoin(SelectedObjects) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>k
+	<td> <I>Clear Object +2 mil</I><br>ChangeClearSize(Object,+2,mil) 
+	<td> <I>Clear Object +2 mil</I><br>ChangeClearSize(Object,+2,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>k-ctrl
+	<td> <I>Clear Selected +2 mil</I><br>ChangeClearSize(SelectedObjects,+2,mil) 
+	<td> <I>Clear Selected +2 mil</I><br>ChangeClearSize(SelectedObjects,+2,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>k-ctrl-shift
+	<td> <I>Clear Selected -2 mil</I><br>ChangeClearSize(SelectedObjects,-2,mil) 
+	<td> <I>Clear Selected -2 mil</I><br>ChangeClearSize(SelectedObjects,-2,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>k-shift
+	<td> <I>Clear Object -2 mil</I><br>ChangeClearSize(Object,-2,mil) 
+	<td> <I>Clear Object -2 mil</I><br>ChangeClearSize(Object,-2,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>l
+	<td> <I>Line Tool size +5 mil</I><br>SetValue(LineSize,+5,mil) 
+	<td> <I>Line Tool size +5 mil</I><br>SetValue(LineSize,+5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>l-shift
+	<td> <I>Line Tool size -5 mil</I><br>SetValue(LineSize,-5,mil) 
+	<td> <I>Line Tool size -5 mil</I><br>SetValue(LineSize,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>left
+	<td> <I>Step Left</I><br>Cursor(Warp,-1,0,grid) 
+	<td> <I>Step Left</I><br>Cursor(Warp,-1,0,grid) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>left-shift
+	<td> <I>Step +Left</I><br>Cursor(Pan,-50,0,view) 
+	<td> <I>Step +Left</I><br>Cursor(Pan,-50,0,view) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>m
+	<td> <I>Move Object to current layer</I><br>MoveToCurrentLayer(Object) 
+	<td> <I>Move Object to current layer</I><br>MoveToCurrentLayer(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>m-ctrl
+	<td> <I>MarkCrosshair</I><br>MarkCrosshair() 
+	<td> <I>MarkCrosshair</I><br>MarkCrosshair() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>m-shift
+	<td> <I>Move selected to current layer</I><br>MoveToCurrentLayer(Selected) 
+	<td> <I>Move selected to current layer</I><br>MoveToCurrentLayer(Selected) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>n
+	<td> <I>Change text on layout</I><br>ChangeName(Object) 
+	<td> <I>text on layout</I><br>ChangeName(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>n-alt
+	<td>  
+	<td> <I>Nonetlist</I><br>ChangeNonetlist(Element) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>n-ctrl
+	<td> <I>Start new layout</I><br>New() 
+	<td> <I>Start New Layout</I><br>New() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>n-ctrl-shift
+	<td> <I>Change text on layout</I><br>ChangeName(Object, Number) 
+	<td> <I>pin on layout</I><br>ChangeName(Object, Number) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>n-shift
+	<td> <I>Select shortest rat</I><br>AddRats(Close) 
+	<td> <I>Select shortest rat</I><br>AddRats(Close) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>o
+	<td> <I>Optimize rats-nest</I><br>Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(AllRats); Atomic(Block)
+	<td> <I>Optimize rats nest</I><br>Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(AllRats); Atomic(Block)
+<tr bgcolor=#DDFFFF>
+	<th align=left>o-ctrl
+	<td> <I>ChangeOctagon Object</I><br>ChangeOctagon(Object) 
+	<td> <I>ChangeOctagon Object</I><br>ChangeOctagon(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>o-shift
+	<td> <I>AddRats to selected pins</I><br>Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(SelectedRats); Atomic(Block)
+	<td> <I>AddRats to selected pins</I><br>Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(SelectedRats); Atomic(Block)
+<tr bgcolor=#DDFFFF>
+	<th align=left>p
+	<td> <I>Polygon PreviousPoint</I><br>Polygon(PreviousPoint) 
+	<td> <I>Polygon PreviousPoint</I><br>Polygon(PreviousPoint) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>p-alt
+	<td> <I>Manage plugins...</I><br>ManagePlugins() 
+	<td> <I>Manage plugins...</I><br>ManagePlugins() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>p-ctrl
+	<td> <I>Auto-place selected elements</I><br>AutoPlaceSelected() 
+	<td> <I>Auto-place selected elements</I><br>AutoPlaceSelected() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>p-ctrl-shift
+	<td> <I>Thin draw poly</I><br>conf(toggle, editor/thin_draw_poly, design) 
+	<td> <I>Thin draw poly</I><br>conf(toggle, editor/thin_draw_poly, design) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>p-shift
+	<td> <I>Polygon Close</I><br>Polygon(Close) 
+	<td> <I>Polygon Close</I><br>Polygon(Close) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>q
+	<td> <I>ChangeSquare Object</I><br>ChangeSquare(Object) 
+	<td> <I>ChangeSquare Object</I><br>ChangeSquare(ToggleObject) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>q-ctrl
+	<td> <I>Quit Program</I><br>Quit() 
+	<td> <I>Quit Program</I><br>Quit() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>r
+	<td> <I>Report net length</I><br>Report(NetLength) 
+	<td>  
+<tr bgcolor=#DDFFFF>
+	<th align=left>r-alt
+	<td> <I>Auto-route selected rats</I><br>AutoRoute(SelectedRats) 
+	<td> <I>Auto-route selected rats</I><br>AutoRoute(SelectedRats) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>r-ctrl
+	<td> <I>Generate object report</I><br>ReportObject() 
+	<td> <I>Generate object report</I><br>ReportObject() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>r-shift
+	<td> <I>Redo last undone operation</I><br>Redo() 
+	<td> <I>Redo last undone operation</I><br>Redo() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>right
+	<td> <I>Step Right</I><br>Cursor(Warp,1,0,grid) 
+	<td> <I>Step Right</I><br>Cursor(Warp,1,0,grid) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>right-shift
+	<td> <I>Step +Right</I><br>Cursor(Pan,50,0,view) 
+	<td> <I>Step +Right</I><br>Cursor(Pan,50,0,view) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>s
+	<td> <I>ChangeSize +5 mil</I><br>ChangeSize(Object,+5,mil) 
+	<td> <I>ChangeSize +5 mil</I><br>ChangeSize(Object,+5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>s-alt
+	<td> <I>ChangeDrill +5 mil</I><br>ChangeDrillSize(Object,+5,mil) 
+	<td> <I>ChangeDrill +5 mil</I><br>ChangeDrillSize(Object,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>s-alt-shift
+	<td> <I>ChangeDrill -5 mil</I><br>ChangeDrillSize(Object,-5,mil) 
+	<td> <I>ChangeDrill -5 mil</I><br>ChangeDrillSize(Object,-5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>s-ctrl
+	<td> <I>Save layout</I><br>Save(Layout) 
+	<td> <I>Save Layout</I><br>Save(Layout)
+<tr bgcolor=#FFFFFF>
+	<th align=left>s-ctrl-shift
+	<td> <I>Save layout as...</I><br>Save(LayoutAs) 
+	<td> <I>Save Layout As...</I><br>Save(LayoutAs)
+<tr bgcolor=#DDFFFF>
+	<th align=left>s-shift
+	<td> <I>ChangeSize -5 mil</I><br>ChangeSize(Object,-5,mil) 
+	<td> <I>ChangeSize -5 mil</I><br>ChangeSize(Object,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>space
+	<td> <I>Arrow</I><br>Mode(Arrow) 
+	<td> <I>Arrow Mode</I><br>Mode(Arrow)
+<tr bgcolor=#DDFFFF>
+	<th align=left>t
+	<td> <I>Text Tool scale +10 mil</I><br>SetValue(TextScale,+10,mil) 
+	<td> <I>Text Tool scale +10 mil</I><br>SetValue(TextScale,+10,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>t-shift
+	<td> <I>Text Tool scale -10 mil</I><br>SetValue(TextScale,-10,mil) 
+	<td> <I>Text Tool scale -10 mil</I><br>SetValue(TextScale,-10,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>tab
+	<td> <I>Flip up/down</I><br>SwapSides(V) 
+	<td> <I>Flip up/down</I><br>SwapSides(V) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>tab-ctrl
+	<td> <I>Spin 180�</I><br>SwapSides(R) 
+	<td> <I>Spin 180 degrees</I><br>SwapSides(R) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>tab-ctrl-shift
+	<td> <I>Swap Sides</I><br>SwapSides() 
+	<td> <I>Swap Sides</I><br>SwapSides() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>tab-shift
+	<td> <I>Flip left/right</I><br>SwapSides(H) 
+	<td> <I>Flip left/right</I><br>SwapSides(H) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>u
+	<td> <I>Undo last operation</I><br>Undo() 
+	<td> <I>Undo last operation</I><br>Undo() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>u-ctrl-shift
+	<td> <I>Clear undo-buffer</I><br>Undo(ClearList)
+	<td> <I>Clear undo-buffer</I><br>Undo(ClearList) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>up
+	<td> <I>Step Up</I><br>Cursor(Warp,0,1,grid) 
+	<td> <I>Step Up</I><br>Cursor(Warp,0,1,grid) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>up-shift
+	<td> <I>Step +Up</I><br>Cursor(Pan,0,50,view) 
+	<td> <I>Step +Up</I><br>Cursor(Pan,0,50,view) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v
+	<td> <I>Zoom Max</I><br>Zoom() 
+	<td> <I>Zoom Max</I><br>Zoom() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v-alt
+	<td> <I>Via Tool drill +5 mil</I><br>SetValue(ViaDrillingHole,+5,mil) 
+	<td> <I>Via Tool drill +5 mil</I><br>SetValue(ViaDrillingHole,+5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v-alt-shift
+	<td> <I>Via Tool drill -5 mil</I><br>SetValue(ViaDrillingHole,-5,mil) 
+	<td> <I>Via Tool drill -5 mil</I><br>SetValue(ViaDrillingHole,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v-ctrl
+	<td> <I>Paste buffer to layout</I><br>Mode(PasteBuffer) 
+	<td> <I>Paste buffer to layout</I><br>Mode(PasteBuffer) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v-ctrl-shift
+	<td> <I>Via Tool size -5 mil</I><br>SetValue(ViaSize,-5,mil) 
+	<td> <I>Via Tool size -5 mil</I><br>SetValue(ViaSize,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v-shift
+	<td> <I>Via Tool size +5 mil</I><br>SetValue(ViaSize,+5,mil) 
+	<td> <I>Via Tool size +5 mil</I><br>SetValue(ViaSize,+5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>w
+	<td> <I>Add All Rats</I><br>AddRats(AllRats) 
+	<td> <I>Add All Rats</I><br>AddRats(AllRats) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>w-shift
+	<td> <I>AddRats Selected</I><br>AddRats(SelectedRats) 
+	<td> <I>AddRats Selected</I><br>AddRats(SelectedRats) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>x
+	<td>  
+	<td> <I>Cycle object being dragged</I><br>CycleDrag() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>x-ctrl
+	<td> <I>Cut selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)
+	<td> <I>Cut selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)
+<tr bgcolor=#DDFFFF>
+	<th align=left>x-shift
+	<td>  
+	<td> <I>Swap nets on two selected pins</I><br>net(swap) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>y
+	<td> <I>Puller</I><br>Puller() 
+	<td> <I>Puller</I><br>Puller() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>y-shift
+	<td> <I>ChangeSizes to Route style</I><br>ChangeSizes(Object,style,mil)
+	<td> <I>ChangeSizes to Route style</I><br>ChangeSizes(Object,style,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>z
+	<td> <I>Zoom In 20%</I><br>Zoom(-1.2) 
+	<td> <I>Zoom In 20%</I><br>Zoom(-1.2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>z-alt
+	<td> <I>Undo</I><br>Undo() 
+	<td>  
+<tr bgcolor=#FFFFFF>
+	<th align=left>z-shift
+	<td> <I>Zoom Out 20%</I><br>Zoom(+1.2)
+	<td> <I>Zoom Out 20%</I><br>Zoom(+1.2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>|
+	<td> <I>Thin draw</I><br>conf(toggle, editor/thin_draw, design) 
+	<td> <I>Thin draw</I><br>conf(toggle, editor/thin_draw, design) 
+</table>
+<br>pcb-menu-lesstif.lht: d-shift vs. d-shift;
+</body></html>
diff --git a/doc-rnd/keys_mkey.html b/doc-rnd/keys_mkey.html
new file mode 100644
index 0000000..cce2d30
--- /dev/null
+++ b/doc-rnd/keys_mkey.html
@@ -0,0 +1,508 @@
+<html><body>
+<h1> Key to action bindings </h1>
+<table border=1 cellspacing=0>
+<tr><th> key <th>pcb-menu-mkey.lht
+<tr bgcolor=#DDFFFF>
+	<th align=left>.
+	<td> <I>'All-direction' lines</I><br>conf(toggle, editor/all_direction_lines, design) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>/
+	<td> <I>Cycle Clip</I><br>Display(CycleClip) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>0
+	<td> <I>Select Layer 10</I><br>SelectLayer(10) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>0-alt
+	<td> <I>Select Layer 20</I><br>SelectLayer(20) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>0-alt-ctrl
+	<td> <I>Toggle Layer 20</I><br>ToggleView(20) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>0-ctrl
+	<td> <I>Toggle Layer 10</I><br>ToggleView(10) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>1
+	<td> <I>Select Layer 1</I><br>SelectLayer(1) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>1-alt
+	<td> <I>Select Layer 11</I><br>SelectLayer(11) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>1-alt-ctrl
+	<td> <I>Toggle Layer 11</I><br>ToggleView(11) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>1-ctrl
+	<td> <I>Toggle Layer 1</I><br>ToggleView(1) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>1-shift
+	<td> <I>Buffer/Select Buffer #1</I><br>PasteBuffer(1)
+<tr bgcolor=#FFFFFF>
+	<th align=left>2
+	<td> <I>Select Layer 2</I><br>SelectLayer(2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>2-alt
+	<td> <I>Select Layer 12</I><br>SelectLayer(12) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>2-alt-ctrl
+	<td> <I>Toggle Layer 12</I><br>ToggleView(12) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>2-ctrl
+	<td> <I>Toggle Layer 2</I><br>ToggleView(2) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>2-shift
+	<td> <I>Buffer/Select Buffer #2</I><br>PasteBuffer(2)
+<tr bgcolor=#DDFFFF>
+	<th align=left>3
+	<td> <I>Select Layer 3</I><br>SelectLayer(3) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>3-alt
+	<td> <I>Select Layer 13</I><br>SelectLayer(13) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>3-alt-ctrl
+	<td> <I>Toggle Layer 13</I><br>ToggleView(13) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>3-ctrl
+	<td> <I>Toggle Layer 3</I><br>ToggleView(3) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>3-shift
+	<td> <I>Buffer/Select Buffer #3</I><br>PasteBuffer(3)
+<tr bgcolor=#FFFFFF>
+	<th align=left>4
+	<td> <I>Select Layer 4</I><br>SelectLayer(4) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>4-alt
+	<td> <I>Select Layer 14</I><br>SelectLayer(14) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>4-alt-ctrl
+	<td> <I>Toggle Layer 14</I><br>ToggleView(14) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>4-ctrl
+	<td> <I>Toggle Layer 4</I><br>ToggleView(4) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>4-shift
+	<td> <I>Buffer/Select Buffer #4</I><br>PasteBuffer(4)
+<tr bgcolor=#DDFFFF>
+	<th align=left>5
+	<td> <I>Select Layer 5</I><br>SelectLayer(5) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>5-alt
+	<td> <I>Select Layer 15</I><br>SelectLayer(15) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>5-alt-ctrl
+	<td> <I>Toggle Layer 15</I><br>ToggleView(15) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>5-ctrl
+	<td> <I>Toggle Layer 5</I><br>ToggleView(5) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>5-shift
+	<td> <I>Buffer/Select Buffer #5</I><br>PasteBuffer(5)
+<tr bgcolor=#FFFFFF>
+	<th align=left>6
+	<td> <I>Select Layer 6</I><br>SelectLayer(6) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>6-alt
+	<td> <I>Select Layer 16</I><br>SelectLayer(16) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>6-alt-ctrl
+	<td> <I>Toggle Layer 16</I><br>ToggleView(16) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>6-ctrl
+	<td> <I>Toggle Layer 6</I><br>ToggleView(6) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>7
+	<td> <I>Select Layer 7</I><br>SelectLayer(7) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>7-alt
+	<td> <I>Select Layer 17</I><br>SelectLayer(17) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>7-alt-ctrl
+	<td> <I>Toggle Layer 17</I><br>ToggleView(17) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>7-ctrl
+	<td> <I>Toggle Layer 7</I><br>ToggleView(7) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>8
+	<td> <I>Select Layer 8</I><br>SelectLayer(8) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>8-alt
+	<td> <I>Select Layer 18</I><br>SelectLayer(18) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>8-alt-ctrl
+	<td> <I>Toggle Layer 18</I><br>ToggleView(18) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>8-ctrl
+	<td> <I>Toggle Layer 8</I><br>ToggleView(8) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>9
+	<td> <I>Select Layer 9</I><br>SelectLayer(9) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>9-alt
+	<td> <I>Select Layer 19</I><br>SelectLayer(19) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>9-alt-ctrl
+	<td> <I>Toggle Layer 19</I><br>ToggleView(19) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>9-ctrl
+	<td> <I>Toggle Layer 9</I><br>ToggleView(9) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>:
+	<td> <I>Command Entry</I><br>Command() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>=
+	<td> <I>Simple optimization</I><br>djopt(simple) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>=-shift
+	<td> <I>Auto-Optimize</I><br>djopt(auto) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>[
+	<td> <I>Grid -5mil</I><br>SetValue(Grid,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>\
+	<td> <I>Full screen</I><br>fullscreen(toggle) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>]
+	<td> <I>Grid +5mil</I><br>SetValue(Grid,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>a-alt
+	<td> <I>Select all visible</I><br>Select(All) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>a-alt-shift
+	<td> <I>Unselect all</I><br>Unselect(All) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>a<br> c
+	<td> <I>Library</I><br>DoWindows(Library) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>b
+	<td> <I>Flip Object</I><br>Flip(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>b-alt-ctrl
+	<td> <I>netlist patch for back annotation</I><br>SavePatch() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>b-shift
+	<td> <I>Move selected elements to other side</I><br>Flip(SelectedElements) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>backspace
+	<td> <I>Remove Selected</I><br>RemoveSelected() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>backspace-shift
+	<td> <I>Remove Connected</I><br>Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)
+<tr bgcolor=#FFFFFF>
+	<th align=left>c
+	<td> <I>Center cursor</I><br>Center() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>c-ctrl<br> 
+	<td> <I>Copy selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Unselect(All); Mode(PasteBuffer)
+<tr bgcolor=#FFFFFF>
+	<th align=left>d
+	<td> <I>Pins/Via show Name/Number</I><br>Display(PinOrPadName) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>d-shift
+	<td> <I>Pinout</I><br>Display(Pinout) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>delete
+	<td> <I>Remove</I><br>Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore)
+<tr bgcolor=#DDFFFF>
+	<th align=left>delete-shift
+	<td> <I>Remove selected objects</I><br>RemoveSelected() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>down
+	<td> <I>Step Down</I><br>Cursor(Warp,0,-1,grid) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>down-shift
+	<td> <I>Step +Down</I><br>Cursor(Pan,0,-50,view) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>e-shift
+	<td> <I>Erase selected rats</I><br>DeleteRats(SelectedRats) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>e<br> c
+	<td> <I>Copy selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Unselect(All); Mode(PasteBuffer)
+<tr bgcolor=#FFFFFF>
+	<th align=left>e<br> d
+	<td> <I>Remove</I><br>Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore)
+<tr bgcolor=#DDFFFF>
+	<th align=left>e<br> i
+	<td> <I>Buffer/Mirror buffer (up/down)</I><br>Mode(PasteBuffer); PasteBuffer(Mirror)
+<tr bgcolor=#FFFFFF>
+	<th align=left>e<br> r
+	<td> <I>Rotate buffer 90 deg CCW</I><br>
+<tr bgcolor=#DDFFFF>
+	<th align=left>e<br> r-shift
+	<td> <I>Redo last undone operation</I><br>Redo() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>e<br> u-shift
+	<td> <I>Undo last operation</I><br>Undo() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>e<br> x
+	<td> <I>text on layout</I><br>ChangeName(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>enter
+	<td> <I>Click</I><br>Mode(Notify); Mode(Release)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f-alt-shift
+	<td> <I>Replace footprint</I><br>ReplaceFootprint() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>f-ctrl
+	<td> <I>Lookup connection to object</I><br>GetXY(Click on the object); Connection(Find)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f-shift
+	<td> <I>Reset all connections</I><br>Connection(Reset); Display(Redraw)
+<tr bgcolor=#FFFFFF>
+	<th align=left>f<br> a
+	<td> <I>Save Layout As...</I><br>Save(LayoutAs)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f<br> c
+	<td> <I>Quit Program</I><br>Quit() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>f<br> i
+	<td> <I>Export layout...</I><br>Export()
+<tr bgcolor=#DDFFFF>
+	<th align=left>f<br> n
+	<td> <I>Start New Layout</I><br>New() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>f<br> o
+	<td> <I>Load layout</I><br>Load(Layout)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f<br> p
+	<td> <I>Print layout...</I><br>Print()
+<tr bgcolor=#FFFFFF>
+	<th align=left>f<br> r
+	<td> <I>Revert</I><br>Load(Revert,none)
+<tr bgcolor=#DDFFFF>
+	<th align=left>f<br> s
+	<td> <I>Save Layout</I><br>Save(Layout)
+<tr bgcolor=#FFFFFF>
+	<th align=left>g
+	<td> <I>Grid +5mil</I><br>SetValue(Grid,+5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>g-ctrl
+	<td> <I>Grid +0.05mm</I><br>SetValue(Grid,+0.05,mm) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>g-ctrl-shift
+	<td> <I>Grid -0.05mm</I><br>SetValue(Grid,-0.05,mm) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>g-shift
+	<td> <I>Grid -5mil</I><br>SetValue(Grid,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>h-ctrl
+	<td> <I>ChangeHole Object</I><br>ChangeHole(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>h-shift
+	<td> <I>ToggleHideName SelectedElement</I><br>ToggleHideName(SelectedElements) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>h<br> a
+	<td> <I>About...</I><br>About() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>j
+	<td> <I>ChangeJoin Object</I><br>ChangeJoin(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>j-shift
+	<td> <I>ChangeJoin SelectedObject</I><br>ChangeJoin(SelectedObjects) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>k
+	<td> <I>Clear Object +2 mil</I><br>ChangeClearSize(Object,+2,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>k-ctrl
+	<td> <I>Clear Selected +2 mil</I><br>ChangeClearSize(SelectedObjects,+2,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>k-ctrl-shift
+	<td> <I>Clear Selected -2 mil</I><br>ChangeClearSize(SelectedObjects,-2,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>k-shift
+	<td> <I>Clear Object -2 mil</I><br>ChangeClearSize(Object,-2,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>l
+	<td> <I>Line Tool size +5 mil</I><br>SetValue(LineSize,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>l-shift
+	<td> <I>Line Tool size -5 mil</I><br>SetValue(LineSize,-5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>left
+	<td> <I>Step Left</I><br>Cursor(Warp,-1,0,grid) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>left-shift
+	<td> <I>Step +Left</I><br>Cursor(Pan,-50,0,view) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>m
+	<td> <I>Move Object to current layer</I><br>MoveToCurrentLayer(Object) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>m-ctrl
+	<td> <I>MarkCrosshair</I><br>MarkCrosshair() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>m-shift
+	<td> <I>Move selected to current layer</I><br>MoveToCurrentLayer(Selected) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>n
+	<td> <I>text on layout</I><br>ChangeName(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>n-alt
+	<td> <I>Nonetlist</I><br>ChangeNonetlist(Element) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>n-ctrl
+	<td> <I>Start New Layout</I><br>New() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>n-ctrl-shift
+	<td> <I>pin on layout</I><br>ChangeName(Object, Number) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>n-shift
+	<td> <I>Select shortest rat</I><br>AddRats(Close) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>o
+	<td> <I>Optimize rats nest</I><br>Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(AllRats); Atomic(Block)
+<tr bgcolor=#FFFFFF>
+	<th align=left>o-ctrl
+	<td> <I>ChangeOctagon Object</I><br>ChangeOctagon(Object) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>o-shift
+	<td> <I>AddRats to selected pins</I><br>Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(SelectedRats); Atomic(Block)
+<tr bgcolor=#FFFFFF>
+	<th align=left>p
+	<td> <I>Polygon PreviousPoint</I><br>Polygon(PreviousPoint) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>p-alt
+	<td> <I>Manage plugins...</I><br>ManagePlugins() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>p-ctrl
+	<td> <I>Auto-place selected elements</I><br>AutoPlaceSelected() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>p-ctrl-shift
+	<td> <I>Thin draw poly</I><br>conf(toggle, editor/thin_draw_poly, design) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>p-shift
+	<td> <I>Polygon Close</I><br>Polygon(Close) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>q
+	<td> <I>ChangeSquare Object</I><br>ChangeSquare(ToggleObject) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>q-ctrl
+	<td> <I>Quit Program</I><br>Quit() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>r-alt
+	<td> <I>Auto-route selected rats</I><br>AutoRoute(SelectedRats) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>r-ctrl
+	<td> <I>Generate object report</I><br>ReportObject() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>r-shift
+	<td> <I>Redo last undone operation</I><br>Redo() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>right
+	<td> <I>Step Right</I><br>Cursor(Warp,1,0,grid) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>right-shift
+	<td> <I>Step +Right</I><br>Cursor(Pan,50,0,view) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>s
+	<td> <I>ChangeSize +5 mil</I><br>ChangeSize(Object,+5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>s-alt
+	<td> <I>ChangeDrill +5 mil</I><br>ChangeDrillSize(Object,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>s-alt-shift
+	<td> <I>ChangeDrill -5 mil</I><br>ChangeDrillSize(Object,-5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>s-ctrl
+	<td> <I>Save Layout</I><br>Save(Layout)
+<tr bgcolor=#FFFFFF>
+	<th align=left>s-ctrl-shift
+	<td> <I>Save Layout As...</I><br>Save(LayoutAs)
+<tr bgcolor=#DDFFFF>
+	<th align=left>s-shift
+	<td> <I>ChangeSize -5 mil</I><br>ChangeSize(Object,-5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>space
+	<td> <I>Arrow Mode</I><br>Mode(Arrow)
+<tr bgcolor=#DDFFFF>
+	<th align=left>t
+	<td> <I>Text Tool scale +10 mil</I><br>SetValue(TextScale,+10,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>t-shift
+	<td> <I>Text Tool scale -10 mil</I><br>SetValue(TextScale,-10,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>tab
+	<td> <I>Flip up/down</I><br>SwapSides(V) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>tab-ctrl
+	<td> <I>Spin 180 degrees</I><br>SwapSides(R) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>tab-ctrl-shift
+	<td> <I>Swap Sides</I><br>SwapSides() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>tab-shift
+	<td> <I>Flip left/right</I><br>SwapSides(H) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>u
+	<td> <I>Undo last operation</I><br>Undo() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>u-ctrl-shift
+	<td> <I>Clear undo-buffer</I><br>Undo(ClearList) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>up
+	<td> <I>Step Up</I><br>Cursor(Warp,0,1,grid) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>up-shift
+	<td> <I>Step +Up</I><br>Cursor(Pan,0,50,view) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v-alt
+	<td> <I>Via Tool drill +5 mil</I><br>SetValue(ViaDrillingHole,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v-alt-shift
+	<td> <I>Via Tool drill -5 mil</I><br>SetValue(ViaDrillingHole,-5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v-ctrl
+	<td> <I>Paste buffer to layout</I><br>Mode(PasteBuffer) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v-ctrl-shift
+	<td> <I>Via Tool size -5 mil</I><br>SetValue(ViaSize,-5,mil) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v-shift
+	<td> <I>Via Tool size +5 mil</I><br>SetValue(ViaSize,+5,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v<br> e
+	<td> <I>Zoom Max</I><br>Zoom() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v<br> f
+	<td> <I>Zoom Max</I><br>Zoom() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>v<br> i
+	<td> <I>Zoom In 2X</I><br>Zoom(-2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>v<br> o
+	<td> <I>Zoom Out 2X</I><br>Zoom(+2) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>w
+	<td> <I>Add All Rats</I><br>AddRats(AllRats) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>w-shift
+	<td> <I>AddRats Selected</I><br>AddRats(SelectedRats) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>x
+	<td> <I>Cycle object being dragged</I><br>CycleDrag() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>x-ctrl
+	<td> <I>Cut selection to buffer</I><br>GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)
+<tr bgcolor=#FFFFFF>
+	<th align=left>x-shift
+	<td> <I>Swap nets on two selected pins</I><br>net(swap) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>y
+	<td> <I>Puller</I><br>Puller() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>y-ctrl
+	<td> <I>Redo last undone operation</I><br>Redo() 
+<tr bgcolor=#DDFFFF>
+	<th align=left>y-shift
+	<td> <I>ChangeSizes to Route style</I><br>ChangeSizes(Object,style,mil) 
+<tr bgcolor=#FFFFFF>
+	<th align=left>z
+	<td> <I>Zoom In 20%</I><br>Zoom(-1.2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>z-ctrl
+	<td> <I>Undo last operation</I><br>Undo() 
+<tr bgcolor=#FFFFFF>
+	<th align=left>z-shift
+	<td> <I>Zoom Out 20%</I><br>Zoom(+1.2) 
+<tr bgcolor=#DDFFFF>
+	<th align=left>|
+	<td> <I>Thin draw</I><br>conf(toggle, editor/thin_draw, design) 
+</table>
+
+</body></html>
diff --git a/doc-rnd/logo128.png b/doc-rnd/logo128.png
new file mode 100644
index 0000000..c14c819
Binary files /dev/null and b/doc-rnd/logo128.png differ
diff --git a/doc-rnd/logo16.png b/doc-rnd/logo16.png
new file mode 100644
index 0000000..96146b8
Binary files /dev/null and b/doc-rnd/logo16.png differ
diff --git a/doc-rnd/logo256.png b/doc-rnd/logo256.png
new file mode 100644
index 0000000..a4d8ae3
Binary files /dev/null and b/doc-rnd/logo256.png differ
diff --git a/doc-rnd/logo32.png b/doc-rnd/logo32.png
new file mode 100644
index 0000000..75932f2
Binary files /dev/null and b/doc-rnd/logo32.png differ
diff --git a/doc-rnd/logo64.png b/doc-rnd/logo64.png
new file mode 100644
index 0000000..d3af8d7
Binary files /dev/null and b/doc-rnd/logo64.png differ
diff --git a/doc-rnd/mac.txt b/doc-rnd/mac.txt
new file mode 100644
index 0000000..69b3770
--- /dev/null
+++ b/doc-rnd/mac.txt
@@ -0,0 +1,22 @@
+State on MacOSX
+~~~~~~~~~~~~~~~
+
+Source releases starting from 1.1.0 should compile and run out-of-the-box.
+
+Requirements for GUI:  x11 and gtk+
+  - X11 server and client libraries for OS X are available from the XQuartz 
+     download and install
+  - gtk+ is available from brew
+     * brew install dependencies (gtk+ and libgd) with the following commands
+       brew install libgd
+       brew install gtk+
+     * if you want to use gEDA with PCB-RND you may want to install it as well
+       via brew
+       brew install homebrew/science/geda-gaf
+
+Known issues:
+  - application window opens behind other windows; it seems to be a gtk bug
+    that can not be worked around from the application
+  - slow rendering and error messages about invalid numerics on the console
+
+Considerations listed in ../INSTALL apply.
diff --git a/doc-rnd/man/Makefile b/doc-rnd/man/Makefile
new file mode 100644
index 0000000..4364dbb
--- /dev/null
+++ b/doc-rnd/man/Makefile
@@ -0,0 +1,58 @@
+# This Makefile is a plain old hand written one; all configuration settings
+# are included from ../../Makefile.conf which is scconfig generated
+
+IN=pcb-rnd.1.mml
+OUT_HTML = pcb-rnd.1.html
+OUT_MAN1 = pcb-rnd.1
+OUT_LINT = pcb-rnd.1.lint
+
+OUTPUT = $(OUT_HTML) $(OUT_MAN1) index.html
+MML = /usr/bin/mml
+
+all: $(OUTPUT)
+
+lint: pcb-rnd.1.lint
+
+.SUFFIXES: .html .mml .lint
+
+.mml.html: .mml_linkmap
+	$(MML) -i copyright.mml -f html $<  > $@
+
+.mml:
+	$(MML) -i copyright.mml -f man $<  > $@
+
+.mml_linkmap:
+	$(MML) -i copyright.mml -f linkmap $(IN) > $@
+
+index.html:
+	@echo "<HTML><BODY>" > $@
+	$(MML) -i copyright.mml -f indexhtml $(IN) >> $@
+	@echo "</BODY></HTML>" >> $@
+
+clean:
+
+distclean:
+
+genclean:
+	rm $(OUTPUT) 2>/dev/null ; true
+
+.mml.lint:
+	$(MML) -i copyright.mml -f lint $<
+
+install_:
+	$(MKDIR) "$(MAN1DIR)"
+	$(CPC) "`pwd`/pcb-rnd.1" "$(MAN1DIR)/pcb-rnd.1"
+	$(CPC) "`pwd`/gsch2pcb-rnd.1" "$(MAN1DIR)/gsch2pcb-rnd.1"
+
+uninstall:
+	$(RM) "$(MAN1DIR)/pcb-rnd.1"
+	$(RM) "$(MAN1DIR)/gsch2pcb-rnd.1"
+
+install:
+	make install_ CPC="$(CP)"
+
+linstall:
+	make install_ CPC="$(LN)"
+
+include ../../Makefile.conf
+
diff --git a/doc-rnd/man/README b/doc-rnd/man/README
new file mode 100644
index 0000000..73bceae
--- /dev/null
+++ b/doc-rnd/man/README
@@ -0,0 +1,10 @@
+Manual pages are written in the manual markup format and are compiled into
+manual and html using mml. Do not edit the generated manual or html pages.
+
+The generated files are commited in the repository so dependencies are not
+required for casual users to build the project.
+
+The required tools and dependencies are:
+ svn://repo.hu/libmawk/trunk
+ svn://repo.hu/web3.0/trunk
+ svn://repo.hu/shscripts/trunk/manmarkup
diff --git a/doc-rnd/man/copyright.mml b/doc-rnd/man/copyright.mml
new file mode 100644
index 0000000..2b11db1
--- /dev/null
+++ b/doc-rnd/man/copyright.mml
@@ -0,0 +1,21 @@
+<copyright>
+pcb-rnd - manual\n
+Copyright (C) 2016  Tibor 'Igor2' Palinkas\n
+\n
+This program is free software; you can redistribute it and/or modify\n
+it under the terms of the GNU General Public License as published by\n
+the Free Software Foundation; either version 2 of the License, or\n
+(at your option) any later version.\n
+\n
+This program is distributed in the hope that it will be useful,\n
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n
+GNU General Public License for more details.\n
+\n
+You should have received a copy of the GNU General Public License along\n
+with this program; if not, write to the Free Software Foundation, Inc.,\n
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n
+\n
+Contact: pcb-rnd[removethis]@igor2.repo.hu
+</copyright>
+<manual>pcb-rnd manual</manual>
diff --git a/doc-rnd/man/gsch2pcb-rnd.1 b/doc-rnd/man/gsch2pcb-rnd.1
new file mode 100644
index 0000000..bf66021
--- /dev/null
+++ b/doc-rnd/man/gsch2pcb-rnd.1
@@ -0,0 +1,134 @@
+.TH gsch2pcb-rnd 1 "September 25th, 2013" "gEDA Project" 1.8.2.20130925
+.SH NAME
+gsch2pcb-rnd - Update pcb-rnd layouts from gEDA/gaf schematics
+.SH SYNOPSIS
+\fBgsch2pcb-rnd\fR [\fIOPTION\fR ...] {\fIPROJECT\fR | \fIFILE\fR ...}
+.SH DESCRIPTION
+.PP
+\fBgsch2pcb-rnd\fR is a frontend to \fBgnetlist\fR(1) which aids in
+creating and updating \fBpcb-rnd\fR(1) printed circuit board layouts based
+on a set of electronic schematics created with \fBgschem\fR(1).
+
+.PP
+Instead of specifying all options and input gEDA schematic \fIFILE\fRs
+on the command line, \fBgsch2pcb-rnd\fR can use a \fIPROJECT\fR file
+instead.
+
+.PP
+\fBgsch2pcb-rnd\fR first runs \fBgnetlist\fR(1) with the `PCB' backend to
+create a `<name>.net' file containing a \fBpcb-rnd\fR(1) formatted netlist for
+the design.
+
+.PP
+The second step is to run \fBgnetlist\fR(1) again with the `gsch2pcb-rnd'
+backend to find any \fBM4\fR(1) elements required by the schematics.
+Any missing elements are found by searching a set of file element
+directories.  If no `<name>.pcb' file exists for the design yet, it is
+created with the required elements; otherwise, any new elements
+are output to a `<name>.new.pcb' file.
+
+.PP
+If a `<name>.pcb' file exists, it is searched for elements with a
+non-empty element name with no matching schematic symbol.  These
+elements are removed from the `<name>.pcb' file, with a backup in a
+`<name>.pcb.bak' file.
+
+.PP
+Finally, \fBgnetlist\fR(1) is run a third time with the `pcbpins'
+backend to create a `<name>.cmd' file.  This can be loaded into
+\fBpcb-rnd\fR(1) to rename all pin names in the PCB layout to match the
+schematic.
+
+.SH OPTIONS
+.TP 8
+\fB-o\fR, \fB--output-name\fR=\fIBASENAME\fR
+Use output filenames `\fIBASENAME\fR.net', `\fIBASENAME\fR.pcb', and
+`\fIBASENAME\fR.new.pcb'.  By default, the basename of the first
+schematic file in the list of input files is used.
+.TP 8
+\fB-d\fR, \fB--elements-dir\fR=\fIDIRECTORY\fR
+Add \fIDIRECTORY\fR to the list of directories to search for PCB file
+elements.
+.TP 8
+\fB-r\fR, \fB--remove-unfound\fR
+Don't include references to unfound elements in the generated `.pcb'
+files.  Use if you want \fBpcb-rnd\fR(1) to be able to load the
+(incomplete) `.pcb' file.  This is enabled by default.
+.TP 8
+\fB-k\fR, \fB--keep-unfound\fR
+Keep include references to unfound elements in the generated `.pcb'
+files.  Use if you want to hand edit or otherwise preprocess the
+generated `.pcb' file before running \fBpcb\fR(1).
+.TP 8
+\fB-p\fR, \fB--preserve\fR
+Preserve elements in PCB files which are not found in the schematics.
+Since elements with an empty element name (schematic "refdes") are
+never deleted, this option is rarely useful.
+.TP 8
+\fB--gnetlist\fR \fIBACKEND\fR
+In addition to the default backends, run \fBgnetlist\fR(1) with `\-g
+\fIBACKEND\fR', with output to `<name>.\fIBACKEND\fR'.
+.TP 8
+\fB--gnetlist-arg\fR \fIARG\fR
+Pass \fIARG\fR as an additional argument to \fBgnetlist\fR(1).
+.TP 8
+\fB--empty-footprint\fR \fINAME\fR
+If \fINAME\fR is not `none', \fBgsch2pcb-rnd\fR will not add elements for
+components with that name to the PCB file.  Note that if the omitted
+components have net connections, they will still appear in the netlist
+and \fBpcb-rnd\fR(1) will warn that they are missing.
+.TP 8
+\fB--fix-elements\fR
+If a schematic component's `footprint' attribute is not equal to the
+`Description' of the corresponding PCB element, update the
+`Description' instead of replacing the element.
+.TP 8
+\fB-q\fR, \fB--quiet\fR
+Don't output information on steps to take after running \fBgsch2pcb-rnd\fR.
+.TP 8
+\fB-v\fR, \fB--verbose\fR
+Output extra debugging information.  This option can be specified
+twice (`\-v \-v') to obtain additional debugging for file elements.
+.TP 8
+\fB-h\fR, \fB--help\fR
+Print a help message.
+.TP 8
+\fB-V\fR, \fB--version\fR
+Print \fBgsch2pcb-rnd\fR version information.
+
+.SH PROJECT FILES
+.PP
+A \fBgsch2pcb-rnd\fR project file is a file (not ending in `.sch')
+containing a list of schematics to process and some options.  Any
+long-form command line option can appear in the project file with the
+leading `\-\-' removed, with the exception of `\-\-gnetlist-arg',
+`\-\-fix-elements', `\-\-verbose', and `\-\-version'.  Schematics should be
+listed on a line beginning with `schematics'.
+.PP
+An example project file might look like:
+
+.nf
+	schematics partA.sch partB.sch
+	output-name design
+.ad b
+
+.SH ENVIRONMENT
+.TP 8
+.B GNETLIST
+specifies the \fBgnetlist\fR(1) program to run.  The default is
+`gnetlist'.
+
+.SH AUTHORS
+See the `AUTHORS' file included with this program.
+
+.SH COPYRIGHT
+.nf
+Copyright \(co 1999-2011 gEDA Contributors.  License GPLv2+: GNU GPL
+version 2 or later.  Please see the `COPYING' file included with this
+program for full details.
+.PP
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+.SH SEE ALSO
+\fBgschem\fR(1), \fBgnetlist\fR(1), \fBpcb-rnd\fR(1)
diff --git a/doc-rnd/man/index.html b/doc-rnd/man/index.html
new file mode 100644
index 0000000..778a6e6
--- /dev/null
+++ b/doc-rnd/man/index.html
@@ -0,0 +1,3 @@
+<HTML><BODY>
+		<LI> <a href="pcb-rnd.1.html">pcb-rnd</a> <I>(1)</I> -- pcb-rnd - Printed Circuit Board editor
+</BODY></HTML>
diff --git a/doc-rnd/man/pcb-rnd.1 b/doc-rnd/man/pcb-rnd.1
new file mode 100644
index 0000000..7ce2b59
--- /dev/null
+++ b/doc-rnd/man/pcb-rnd.1
@@ -0,0 +1,47 @@
+.\" pcb-rnd - manual
+.\" Copyright (C) 2016 Tibor 'Igor2' Palinkas
+.\" 
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\" 
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\" 
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.\" 
+.\" Contact: pcb-rnd[removethis]@igor2.repo.hu
+.TH pcb-rnd 1 2016-10-29 "" "pcb-rnd manual"
+.SH NAME
+pcb-rnd - Printed Circuit Board editor
+.SH SYNPOSIS
+.nf
+.sp
+\fBpcb-rnd [\fIoptions\fB] [\fIinputfile\fB]
+.fi
+.SH DECSRIPTION
+
+.BR pcb-rnd
+is a modular PCB editor. The main use is interactive editing. If no \fI-x\fR is specified on the command line, the graphical editor mode is initiated. Automated, headless processing is also possible with \fI-x\fR or \fI--gui batch\fR.
+.SH OPTIONS
+
+
+.TP
+
+.B -x \fIexporter\fR [\fIexportflags] \fIinputfile\fR\fR 
+Instead of interactive editing, export the design (loaded from \fIinputfile\fR) to a file using the specified \fIexporter\fR plugin. A list of exporter-specific parameters may follow to control the details of the process. 
+.TP
+
+.B --gui \fIhid\fR 
+Use the \fIhid\fR plugin for the "gui" frontend. Common choices are "gtk" or "lesstif" for graphical user intrfaces and "batch" for a headless command-line interface (or automated batch processing). 
+.TP
+
+.B -c \fIpath=val\fR 
+Set a config node in role CFR_CLI. The path of the node is \fIpath\fR (e.g. editor/grid) and the new value is \fIval\fR (e.g. 5mm).
+.PP
+
diff --git a/doc-rnd/man/pcb-rnd.1.html b/doc-rnd/man/pcb-rnd.1.html
new file mode 100644
index 0000000..9194ce0
--- /dev/null
+++ b/doc-rnd/man/pcb-rnd.1.html
@@ -0,0 +1,79 @@
+<HTML><BODY>
+<!--
+pcb-rnd - manual
+Copyright (C) 2016 Tibor 'Igor2' Palinkas
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Contact: pcb-rnd[removethis]@igor2.repo.hu
+-->
+<table width=100%>
+<TR>
+		<TH align=left>   pcb-rnd 1
+		<TH align=middle> 2016-10-29
+		<TH align=right>  pcb-rnd manual
+</table>
+
+
+
+<H3>NAME</H3>
+<blockquote>
+pcb-rnd - Printed Circuit Board editor
+</blockquote>
+
+<H3>SYNPOSIS</H3>
+<blockquote>
+<P>
+<tt><B>pcb-rnd</B> [<I>options</I>] [<I>inputfile</I>]</tt>
+
+
+
+</blockquote>
+
+<H3>DESCRIPTION</H3>
+<blockquote>
+<B>pcb-rnd</B> is a modular PCB editor. The main use is interactive editing. If no <I>-x</I> is specified on the command line, the graphical editor mode is initiated. Automated, headless processing is also possible with <I>-x</I> or <I>--gui batch</I>.
+</blockquote>
+
+
+<H3>OPTIONS</H3>
+<blockquote>
+
+<table border=1>
+
+	<TR>
+
+		<TD> -x <I>exporter</I> [<I>exportflags] <I>inputfile</I></I> 
+		<TD> Instead of interactive editing, export the design (loaded from <I>inputfile</I>) to a file using the specified <I>exporter</I> plugin. A list of exporter-specific parameters may follow to control the details of the process. 
+	<TR>
+
+		<TD> --gui <I>hid</I> 
+		<TD> Use the <I>hid</I> plugin for the "gui" frontend. Common choices are "gtk" or "lesstif" for graphical user intrfaces and "batch" for a headless command-line interface (or automated batch processing). 
+	<TR>
+
+		<TD> -c <I>path=val</I> 
+		<TD> Set a config node in role CFR_CLI. The path of the node is <I>path</I> (e.g. editor/grid) and the new value is <I>val</I> (e.g. 5mm).
+</table>
+</blockquote>
+<P>
+<table width=100%>
+<TR>
+		<TH align=left>   pcb-rnd 1
+		<TH align=middle> 2016-10-29
+		<TH align=right>  pcb-rnd manual
+</table>
+
+
+</BODY></HTML>
diff --git a/doc-rnd/man/pcb-rnd.1.mml b/doc-rnd/man/pcb-rnd.1.mml
new file mode 100644
index 0000000..916ffe7
--- /dev/null
+++ b/doc-rnd/man/pcb-rnd.1.mml
@@ -0,0 +1,38 @@
+<title>pcb-rnd</title>
+<sect>1</sect>
+<date>2016-10-29</date>
+
+<name> pcb-rnd - Printed Circuit Board editor </name>
+<syn> <call>pcb-rnd</call> [<arg>options</arg>] [<arg>inputfile</arg>] </syn>
+
+<description>
+<call>pcb-rnd</call> is a modular PCB editor. The main use is interactive
+editing. If no <arg>-x</arg> is specified on the command line, the
+graphical editor mode is initiated. Automated, headless processing
+is also possible with <arg>-x</arg> or <arg>--gui batch</arg>.
+</description>
+
+<options>
+<kvlist>
+	<item>
+		<key> -x <arg>exporter</arg> [<arg>exportflags] <arg>inputfile</arg> </key>
+		<value> Instead of interactive editing, export the design (loaded from
+		        <arg>inputfile</arg>) to a file using the specified
+		        <arg>exporter</arg> plugin. A list of exporter-specific parameters
+		        may follow to control the details of the process. </value>
+	</item>
+	<item>
+		<key> --gui <arg>hid</arg> </key>
+		<value> Use the <arg>hid</arg> plugin for the "gui" frontend. Common
+		        choices are "gtk" or "lesstif" for graphical user intrfaces
+		        and "batch" for a headless command-line interface (or automated
+		        batch processing). </value>
+	</item>
+	<item>
+		<key> -c <arg>path=val</arg> </key>
+		<value> Set a config node in role CFR_CLI. The path of the node is
+		        <arg>path</arg> (e.g. editor/grid) and the new value is
+		        <arg>val</arg> (e.g. 5mm). </value>
+	</item>
+</kvlist>
+</options>
diff --git a/doc-rnd/misc/install_cgi.html b/doc-rnd/misc/install_cgi.html
new file mode 100644
index 0000000..7d1b58d
--- /dev/null
+++ b/doc-rnd/misc/install_cgi.html
@@ -0,0 +1,45 @@
+<html>
+<body>
+<h1> Installation of the footprint CGIs </h1>
+<h2> system requirements, 3rd party software </h2>
+	The following software needs to be installed on the system
+	that will host the scripts:
+	<ul>
+		<li> /bin/bash (did not test with POSIX shell)
+		<li> awk (tested with gawk, may work with other modern implementation)
+		<li> common binutils, e.g. ls, find
+		<li> <a href="http://repo.hu/projects/animator">animator</a>, the svn HEAD version; NOTE: it is possible to manually compile animator into a static executable (useful on dedicated web servers)
+		<li> a web server that can execute plain old CGI scripts
+		<li> a full checkout of pcb-rnd/trunk from svn://repo.hu/pcb-rnd/trunk
+	</ul>
+
+<h2> Installation </h2>
+Since the CGIs are just demo scripts for repo.hu and are not really installed
+anywhere else normally, there is no install script but a manual installation
+process. For both the parametric and the static footprint CGI:
+<ol>
+	<li> check out pcb-rnd/trunk in a directory readable (but not writable) by www-data (or whatever user the CGI will run with)
+	<li> copy the config file pcblib.cgi.conf from pcb-rnd/trunk/util to /etc
+	<li> edit the config file; ignore setting sdir for now
+	<li> hardlink, copy, or wrap the cgi files from under pcb-rnd/trunk/util into the web server's CGI directory
+</ol>
+<p>
+For the static footprint CGI (set up the map cache):
+<ol>
+	<li> cd to trunk/util/pcblib-map in the checkout and run make - NOTE: some awk scripts have hardwired CGI paths yet (TODO)
+	<li> copy this directory to a web-accessible directory
+	<li> set sdir to the absolute path of the web-accessible copy in /etc/pcblib.cgi.conf
+</ol>
+
+<h2> Tips and tricks </h2>
+<h3> cgi wrapping </h3>
+Making the checkout directly into the cgi-bin dir is not a good idea. Using
+a hard link on each CGI between the checkout and the cgi-bin dir is better,
+but an svn up will silently break the link leaving the old version live.
+The most stable solution is placing a wrapper script in the cgi-bin dir:
+<pre>
+#!/bin/bash
+. /full/path/to/the/checkout/util/pcblib-param.cgi
+</pre>
+</body>
+</html>
diff --git a/doc-rnd/mods/after.png b/doc-rnd/mods/after.png
new file mode 100644
index 0000000..2bd9b36
Binary files /dev/null and b/doc-rnd/mods/after.png differ
diff --git a/doc-rnd/mods/before.png b/doc-rnd/mods/before.png
new file mode 100644
index 0000000..dcec960
Binary files /dev/null and b/doc-rnd/mods/before.png differ
diff --git a/doc-rnd/mods/gen.sh b/doc-rnd/mods/gen.sh
new file mode 100755
index 0000000..d227922
--- /dev/null
+++ b/doc-rnd/mods/gen.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+path=../../src_plugins
+
+sloc()
+{
+	(cd "$1" && sloccount .) | awk '/^Total Phys/ { size=$9; sub(",", "", size); print size }'
+}
+
+gen_pie()
+{
+	local bn=$1 code_size=$2 color=$3
+	echo ""
+	echo "@slice"
+	echo "$code_size"
+	echo "@label"
+	echo "$bn ($code_size)"
+	if test ! -z "$color"
+	then
+		echo "@color"
+		echo "$color"
+	fi
+}
+
+echo "#autogenerated by gen.sh" > mods.pie
+echo "#autogenerated by gen.sh" > after.pie
+
+echo HIDs >&2
+code_size=`sloc ../../src/hid`
+gen_pie "HIDs" $code_size "orangered" >> after.pie
+
+echo Core >&2
+tmp=/tmp/pcb-mods-stat
+mkdir $tmp
+cp -r ../../src/*.c ../../src/*.h ../../src/Makefile* $tmp
+code_size=`sloc $tmp`
+gen_pie "core" $code_size "#00ff88" >> after.pie
+
+#echo 3rd >&2
+#code_size=`sloc ../../src_3rd`
+#gen_pie "3rd" $code_size >> after.pie
+
+
+(
+cat pre.html
+for n in $path/*
+do
+	if test -d "$n"
+	then
+		echo $n >&2
+		bn=`basename $n`
+		code_size=`sloc $n`
+		total=$(($total + $code_size))
+		gen_pie $bn $code_size >> mods.pie
+#		case $bn in
+#			gpmi) echo "@pull" >> mods.pie; echo "0.1" >> mods.pie;;
+#		esac
+
+		echo "<tr><th align=left>$bn<td>$code_size"
+		awk '
+			/^#/ {
+				key=$1
+				sub("#", "", key)
+				sub("[:=]", "", key)
+				$1=""
+				DB[key]=$0
+				next
+			}
+			{ desc = desc " " $0 }
+
+			function strip(s) {
+				sub("^[ \t]*", "", s)
+				sub("[ \t]*$", "", s)
+				return s
+			}
+
+			END {
+				st = DB["state"]
+				if (st ~ "partial")
+					clr = "bgcolor=\"yellow\""
+				else if (st ~ "works")
+					clr = "bgcolor=\"lightgreen\""
+				else if ((st ~ "fail") || (st ~ "disable"))
+					clr = "bgcolor=\"red\""
+				else
+					clr=""
+
+				clr2 = clr
+				if (clr2 != "") {
+					sub("bgcolor=\"", "", clr2)
+					sub("\"", "", clr2)
+					print "@color" >> "mods.pie"
+					print clr2 >> "mods.pie"
+				}
+
+				print "<td " clr " >" st
+				if (DB["lstate"] != "")
+					print "<br> (" strip(DB["lstate"]) ")"
+
+				dfl = DB["default"]
+				if (dfl ~ "buildin")
+					clr = "bgcolor=\"lightgreen\""
+				else if (dfl ~ "plugin")
+					clr = "bgcolor=\"yellow\""
+				else if ((dfl ~ "fail") || (dfl ~ "disable"))
+					clr = "bgcolor=\"red\""
+				else
+					clr=""
+
+				print "<td " clr ">" dfl
+				if (DB["ldefault"] != "")
+					print "<br> (" strip(DB["ldefault"]) ")"
+				print "<td>" desc
+			}
+		' < $n/README
+	fi
+done
+cat post.html
+gen_pie "plugins" "$total" "#0088ff" >> after.pie
+) > index.html
+
+for n in mods after
+do
+	animpie < $n.pie | animator -H -d $n
+	pngtopnm ${n}0000.png | pnmcrop | pnmtopng > $n.png
+	rm ${n}0000.png
+done
+
diff --git a/doc-rnd/mods/index.html b/doc-rnd/mods/index.html
new file mode 100644
index 0000000..fcba2f7
--- /dev/null
+++ b/doc-rnd/mods/index.html
@@ -0,0 +1,136 @@
+<html>
+<body>
+<H1> pcb-rnd modularization </H1>
+<H2> Why bother... </H2>
+I believe good software should be modular. This is especially important in
+the context of large software, such as CAD applications. There should be
+a thin core that can model the world and provide the basic operations defined
+on it but anything else should go in separate modules.
+<p>
+Fortunately PCB already had a strong infrastructure supporting this idea.
+It has dynamic loadable plugins and the GUI and exporters are in separate
+HID modules. While working on pcb-gpmi and later pcb-rnd, I added the
+gpmi module as a separate plugin.
+<p>
+As of version 1.0.8 a cosiderable chunk of core code has been moved into
+<i>core plugins</i>. A <i>core plugin</i> is just a plugin that is
+maintained together with the core, in the same repository, still the code is
+somewhat detached from the core. More importantly, the user can choose, for
+each plugin, separately:
+<ul>
+	<li> to compile it as a buildin (static-link it into the pcb executable)
+	<li> to compile it as a plugin (dynamic-link it runtime, if the .so is installed in the plugins/ directory)
+	<li> to disable the plugin, so it is not compiled at all
+</ul>
+<p>
+I believe such modularization has benefits on multiple levels:
+<ul>
+	<li> it is possible to compiler smaller, potentially faster executables by omitting features the specific user would never use anyway
+	<li> in a distribution dynamic-link plugins can be distributed as separate packages providing the user with the option to decide what features to install
+	<li> such plugins have to have some sort of APIs if they want to reference eachother or if the code needs to reference them; such an API may not (and often did not) exist when the code is part of the core
+</ul>
+
+<H2> Progress in charts </H2>
+<h3> Before-after </h3>
+All numbers are in <a href="http://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>
+and are acquired running sloccount on the given directory. While lines of
+code alone is not a true measure of complexity, it's a good estimation. The
+slices of pie charts are the major components of the pcb-rnd executable.
+<table border=1 cellspacing=0> <tr><td>
+	<table border=0>
+	<tr><td><img src="before.png"><td>        <td><img src="after.png">
+	<tr><td>Before modularization: pcb-rnd version 1.0.7
+	        <br>Note: gpmi was already a plugin
+	    <td>
+	    <td> After modularization: pcb-rnd version 1.0.8
+	        <br>Note: gpmi is part of the "plugins" slice
+	</table>
+</table>
+<h3>Zooming on to the plugins</h3>
+<p>
+<img src="mods.png">
+<p>
+(Red means the plugin doesn't really work).
+
+<H2> Progress in numbers </H2>
+Below is a table with the summary of core plugins.
+<table border=1 cellspacing=0>
+<tr><th>module <th>size [sloc] <th> status <th> configure<br>default  <th> description
+
+
+<tr><th align=left>autoplace<td>608
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Automatically place elements. 
+<tr><th align=left>autoroute<td>4177
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Automatically route selected or all rats. This is the original autorouter. 
+<tr><th align=left>dbus<td>438
+<td bgcolor="red" > disabled
+<br> (TODO: needs scconfig support)
+<td bgcolor="red"> disabled
+<td> Remote control PCB using DBUS. 
+<tr><th align=left>djopt<td>2315
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Various board optimization algorithms. 
+<tr><th align=left>export_gcode<td>2450
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to gcode 
+<tr><th align=left>export_lpr<td>96
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to lpr (using export_ps to generate postscript) 
+<tr><th align=left>export_nelma<td>683
+<tr><th align=left>export_ps<td>1608
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export postscript or embedded postscript. 
+<tr><th align=left>gpmi<td>2992
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<br> (if gpmi is installed)
+<td> Scriptable plugin system with about 10 scripting languages supported and dynamic load/unload of scripts that can manipulate the GUI, the board, can implement exporters, etc. 
+<tr><th align=left>import_edif<td>3578
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Import code for netlists in the EDIF format. 
+<tr><th align=left>import_sch<td>255
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Imports element and netlist data from the schematics (or some other source). 
+<tr><th align=left>mincut<td>886
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Use the minimal cut algorithm to indicate shorts: instead of highlighting two random pins/pads, try to highlight the least number of objects that connect the two networks. 
+<tr><th align=left>oldactions<td>53
+<td bgcolor="lightgreen" > works
+<td bgcolor="red"> disabled
+<td> Random collection of old/obsolete actions. Bell(): audible feedback; DumpLibrary(): print footprint library on stdout 
+<tr><th align=left>puller<td>1878
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Pull traces to minimize their length. 
+<tr><th align=left>renumber<td>218
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Renumber elements (renaming them) and generate a text file for back annotation. 
+<tr><th align=left>stroke<td>124
+<td bgcolor="yellow" > partially works (doesn't work with lesstif; works with the gtk hid, but there's no zoom bindings)
+<td bgcolor="red"> disabled
+<br> (requires libstroke installed)
+<td> Gesture recognition with libstroke. 
+<tr><th align=left>toporouter<td>6161
+<td bgcolor="red" > fails
+<br> (infinite loop in gts)
+<td bgcolor="red"> disabled
+<td> Automatically route selected or all rats using a topological algorithm. This is the new autorouter from 2009. 
+<tr><th align=left>vendordrill<td>567
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Vendor drill mapping. 
+</table>
+</body>
+</html>
diff --git a/doc-rnd/mods/mods.png b/doc-rnd/mods/mods.png
new file mode 100644
index 0000000..538e3e3
Binary files /dev/null and b/doc-rnd/mods/mods.png differ
diff --git a/doc-rnd/mods/post.html b/doc-rnd/mods/post.html
new file mode 100644
index 0000000..81d8c88
--- /dev/null
+++ b/doc-rnd/mods/post.html
@@ -0,0 +1,3 @@
+</table>
+</body>
+</html>
diff --git a/doc-rnd/mods/pre.html b/doc-rnd/mods/pre.html
new file mode 100644
index 0000000..3822520
--- /dev/null
+++ b/doc-rnd/mods/pre.html
@@ -0,0 +1,60 @@
+<html>
+<body>
+<H1> pcb-rnd modularization </H1>
+<H2> Why bother... </H2>
+I believe good software should be modular. This is especially important in
+the context of large software, such as CAD applications. There should be
+a thin core that can model the world and provide the basic operations defined
+on it but anything else should go in separate modules.
+<p>
+Fortunately PCB already had a strong infrastructure supporting this idea.
+It has dynamic loadable plugins and the GUI and exporters are in separate
+HID modules. While working on pcb-gpmi and later pcb-rnd, I added the
+gpmi module as a separate plugin.
+<p>
+As of version 1.0.8 a cosiderable chunk of core code has been moved into
+<i>core plugins</i>. A <i>core plugin</i> is just a plugin that is
+maintained together with the core, in the same repository, still the code is
+somewhat detached from the core. More importantly, the user can choose, for
+each plugin, separately:
+<ul>
+	<li> to compile it as a buildin (static-link it into the pcb executable)
+	<li> to compile it as a plugin (dynamic-link it runtime, if the .so is installed in the plugins/ directory)
+	<li> to disable the plugin, so it is not compiled at all
+</ul>
+<p>
+I believe such modularization has benefits on multiple levels:
+<ul>
+	<li> it is possible to compiler smaller, potentially faster executables by omitting features the specific user would never use anyway
+	<li> in a distribution dynamic-link plugins can be distributed as separate packages providing the user with the option to decide what features to install
+	<li> such plugins have to have some sort of APIs if they want to reference eachother or if the code needs to reference them; such an API may not (and often did not) exist when the code is part of the core
+</ul>
+
+<H2> Progress in charts </H2>
+<h3> Before-after </h3>
+All numbers are in <a href="http://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>
+and are acquired running sloccount on the given directory. While lines of
+code alone is not a true measure of complexity, it's a good estimation. The
+slices of pie charts are the major components of the pcb-rnd executable.
+<table border=1 cellspacing=0> <tr><td>
+	<table border=0>
+	<tr><td><img src="before.png"><td>        <td><img src="after.png">
+	<tr><td>Before modularization: pcb-rnd version 1.0.7
+	        <br>Note: gpmi was already a plugin
+	    <td>
+	    <td> After modularization: pcb-rnd version 1.0.8
+	        <br>Note: gpmi is part of the "plugins" slice
+	</table>
+</table>
+<h3>Zooming on to the plugins</h3>
+<p>
+<img src="mods.png">
+<p>
+(Red means the plugin doesn't really work).
+
+<H2> Progress in numbers </H2>
+Below is a table with the summary of core plugins.
+<table border=1 cellspacing=0>
+<tr><th>module <th>size [sloc] <th> status <th> configure<br>default  <th> description
+
+
diff --git a/doc-rnd/mods2/after.png b/doc-rnd/mods2/after.png
new file mode 100644
index 0000000..96d7297
Binary files /dev/null and b/doc-rnd/mods2/after.png differ
diff --git a/doc-rnd/mods2/before.png b/doc-rnd/mods2/before.png
new file mode 100644
index 0000000..dcec960
Binary files /dev/null and b/doc-rnd/mods2/before.png differ
diff --git a/doc-rnd/mods2/gen.sh b/doc-rnd/mods2/gen.sh
new file mode 100755
index 0000000..d227922
--- /dev/null
+++ b/doc-rnd/mods2/gen.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+path=../../src_plugins
+
+sloc()
+{
+	(cd "$1" && sloccount .) | awk '/^Total Phys/ { size=$9; sub(",", "", size); print size }'
+}
+
+gen_pie()
+{
+	local bn=$1 code_size=$2 color=$3
+	echo ""
+	echo "@slice"
+	echo "$code_size"
+	echo "@label"
+	echo "$bn ($code_size)"
+	if test ! -z "$color"
+	then
+		echo "@color"
+		echo "$color"
+	fi
+}
+
+echo "#autogenerated by gen.sh" > mods.pie
+echo "#autogenerated by gen.sh" > after.pie
+
+echo HIDs >&2
+code_size=`sloc ../../src/hid`
+gen_pie "HIDs" $code_size "orangered" >> after.pie
+
+echo Core >&2
+tmp=/tmp/pcb-mods-stat
+mkdir $tmp
+cp -r ../../src/*.c ../../src/*.h ../../src/Makefile* $tmp
+code_size=`sloc $tmp`
+gen_pie "core" $code_size "#00ff88" >> after.pie
+
+#echo 3rd >&2
+#code_size=`sloc ../../src_3rd`
+#gen_pie "3rd" $code_size >> after.pie
+
+
+(
+cat pre.html
+for n in $path/*
+do
+	if test -d "$n"
+	then
+		echo $n >&2
+		bn=`basename $n`
+		code_size=`sloc $n`
+		total=$(($total + $code_size))
+		gen_pie $bn $code_size >> mods.pie
+#		case $bn in
+#			gpmi) echo "@pull" >> mods.pie; echo "0.1" >> mods.pie;;
+#		esac
+
+		echo "<tr><th align=left>$bn<td>$code_size"
+		awk '
+			/^#/ {
+				key=$1
+				sub("#", "", key)
+				sub("[:=]", "", key)
+				$1=""
+				DB[key]=$0
+				next
+			}
+			{ desc = desc " " $0 }
+
+			function strip(s) {
+				sub("^[ \t]*", "", s)
+				sub("[ \t]*$", "", s)
+				return s
+			}
+
+			END {
+				st = DB["state"]
+				if (st ~ "partial")
+					clr = "bgcolor=\"yellow\""
+				else if (st ~ "works")
+					clr = "bgcolor=\"lightgreen\""
+				else if ((st ~ "fail") || (st ~ "disable"))
+					clr = "bgcolor=\"red\""
+				else
+					clr=""
+
+				clr2 = clr
+				if (clr2 != "") {
+					sub("bgcolor=\"", "", clr2)
+					sub("\"", "", clr2)
+					print "@color" >> "mods.pie"
+					print clr2 >> "mods.pie"
+				}
+
+				print "<td " clr " >" st
+				if (DB["lstate"] != "")
+					print "<br> (" strip(DB["lstate"]) ")"
+
+				dfl = DB["default"]
+				if (dfl ~ "buildin")
+					clr = "bgcolor=\"lightgreen\""
+				else if (dfl ~ "plugin")
+					clr = "bgcolor=\"yellow\""
+				else if ((dfl ~ "fail") || (dfl ~ "disable"))
+					clr = "bgcolor=\"red\""
+				else
+					clr=""
+
+				print "<td " clr ">" dfl
+				if (DB["ldefault"] != "")
+					print "<br> (" strip(DB["ldefault"]) ")"
+				print "<td>" desc
+			}
+		' < $n/README
+	fi
+done
+cat post.html
+gen_pie "plugins" "$total" "#0088ff" >> after.pie
+) > index.html
+
+for n in mods after
+do
+	animpie < $n.pie | animator -H -d $n
+	pngtopnm ${n}0000.png | pnmcrop | pnmtopng > $n.png
+	rm ${n}0000.png
+done
+
diff --git a/doc-rnd/mods2/index.html b/doc-rnd/mods2/index.html
new file mode 100644
index 0000000..aabe26b
--- /dev/null
+++ b/doc-rnd/mods2/index.html
@@ -0,0 +1,159 @@
+<html>
+<body>
+<H1> pcb-rnd modularization </H1>
+<H2> Why bother... </H2>
+I believe good software should be modular. This is especially important in
+the context of large software, such as CAD applications. There should be
+a thin core that can model the world and provide the basic operations defined
+on it but anything else should go in separate modules.
+<p>
+Fortunately PCB already had a strong infrastructure supporting this idea.
+It has dynamic loadable plugins and the GUI and exporters are in separate
+HID modules. While working on pcb-gpmi and later pcb-rnd, I added the
+gpmi module as a separate plugin.
+<p>
+In version 1.0.8 and 1.0.9 a cosiderable chunk of core code has been moved into
+<i>core plugins</i>. A <i>core plugin</i> is just a plugin that is
+maintained together with the core, in the same repository, still the code is
+somewhat detached from the core. More importantly, the user can choose, for
+each plugin, separately:
+<ul>
+	<li> to compile it as a buildin (static-link it into the pcb executable)
+	<li> to compile it as a plugin (dynamic-link it runtime, if the .so is installed in the plugins/ directory)
+	<li> to disable the plugin, so it is not compiled at all
+</ul>
+<p>
+I believe such modularization has benefits on multiple levels:
+<ul>
+	<li> it is possible to compiler smaller, potentially faster executables by omitting features the specific user would never use anyway
+	<li> in a distribution dynamic-link plugins can be distributed as separate packages providing the user with the option to decide what features to install
+	<li> such plugins have to have some sort of APIs if they want to reference eachother or if the code needs to reference them; such an API may not (and often did not) exist when the code is part of the core
+</ul>
+
+<H2> Progress in charts </H2>
+<h3> Before-after </h3>
+All numbers are in <a href="http://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>
+and are acquired running sloccount on the given directory. While lines of
+code alone is not a true measure of complexity, it's a good estimation. The
+slices of pie charts are the major components of the pcb-rnd executable.
+<table border=1 cellspacing=0> <tr><td>
+	<table border=0>
+	<tr><td><img src="before.png"><td>        <td><img src="after.png">
+	<tr><td>Before modularization: pcb-rnd version 1.0.7
+	        <br>Note: gpmi was already a plugin
+	    <td>
+	    <td> After modularization: pcb-rnd version 1.0.9
+	        <br>Note: gpmi is part of the "plugins" slice
+	</table>
+</table>
+<h3>Zooming on to the plugins</h3>
+<p>
+<img src="mods.png">
+<p>
+(Red means the plugin doesn't really work).
+
+<H2> Progress in numbers </H2>
+Below is a table with the summary of core plugins.
+<table border=1 cellspacing=0>
+<tr><th>module <th>size [sloc] <th> status <th> configure<br>default  <th> description
+
+
+<tr><th align=left>autoplace<td>612
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Automatically place elements. 
+<tr><th align=left>autoroute<td>4337
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Automatically route selected or all rats. This is the original autorouter. 
+<tr><th align=left>dbus<td>438
+<td bgcolor="red" > disabled
+<br> (TODO: needs scconfig support)
+<td bgcolor="red"> disabled
+<td> Remote control PCB using DBUS. 
+<tr><th align=left>djopt<td>2320
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Various board optimization algorithms. 
+<tr><th align=left>export_bom<td>374
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export bom (Bill of Materials) 
+<tr><th align=left>export_gcode<td>2452
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to gcode 
+<tr><th align=left>export_gerber<td>972
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to gerber 
+<tr><th align=left>export_lpr<td>96
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to lpr (using export_ps to generate postscript) 
+<tr><th align=left>export_nelma<td>685
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to nelma (Numerical capacitance calculator) 
+<tr><th align=left>export_png<td>1107
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export to png, gif and jpeg 
+<tr><th align=left>export_ps<td>1613
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Export postscript or embedded postscript. 
+<tr><th align=left>fontmode<td>163
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Font editing actions. 
+<tr><th align=left>gpmi<td>3003
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<br> (if gpmi is installed)
+<td> Scriptable plugin system with about 10 scripting languages supported and dynamic load/unload of scripts that can manipulate the GUI, the board, can implement exporters, etc. 
+<tr><th align=left>import_edif<td>3578
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Import code for netlists in the EDIF format. 
+<tr><th align=left>import_sch<td>259
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Imports element and netlist data from the schematics (or some other source). 
+<tr><th align=left>legacy_func<td>72
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Random collection of old/obsolete (legacy) functions. 3rd party plugins may depend on them. This module implements C functions and variables and does not register actions or flags. 
+<tr><th align=left>mincut<td>886
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Use the minimal cut algorithm to indicate shorts: instead of highlighting two random pins/pads, try to highlight the least number of objects that connect the two networks. 
+<tr><th align=left>oldactions<td>57
+<td bgcolor="lightgreen" > works
+<td bgcolor="red"> disabled
+<td> Random collection of old/obsolete actions. Bell(): audible feedback; DumpLibrary(): print footprint library on stdout 
+<tr><th align=left>puller<td>1884
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Pull traces to minimize their length. 
+<tr><th align=left>renumber<td>222
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Renumber elements (renaming them) and generate a text file for back annotation. 
+<tr><th align=left>stroke<td>124
+<td bgcolor="yellow" > partially works (doesn't work with lesstif; works with the gtk hid, but there's no zoom bindings)
+<td bgcolor="red"> disabled
+<br> (requires libstroke installed)
+<td> Gesture recognition with libstroke. 
+<tr><th align=left>toporouter<td>6165
+<td bgcolor="red" > fails
+<br> (infinite loop in gts)
+<td bgcolor="red"> disabled
+<td> Automatically route selected or all rats using a topological algorithm. This is the new autorouter from 2009. 
+<tr><th align=left>vendordrill<td>572
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> Vendor drill mapping. 
+</table>
+</body>
+</html>
diff --git a/doc-rnd/mods2/mods.png b/doc-rnd/mods2/mods.png
new file mode 100644
index 0000000..089d550
Binary files /dev/null and b/doc-rnd/mods2/mods.png differ
diff --git a/doc-rnd/mods2/post.html b/doc-rnd/mods2/post.html
new file mode 100644
index 0000000..81d8c88
--- /dev/null
+++ b/doc-rnd/mods2/post.html
@@ -0,0 +1,3 @@
+</table>
+</body>
+</html>
diff --git a/doc-rnd/mods2/pre.html b/doc-rnd/mods2/pre.html
new file mode 100644
index 0000000..2e9e198
--- /dev/null
+++ b/doc-rnd/mods2/pre.html
@@ -0,0 +1,60 @@
+<html>
+<body>
+<H1> pcb-rnd modularization </H1>
+<H2> Why bother... </H2>
+I believe good software should be modular. This is especially important in
+the context of large software, such as CAD applications. There should be
+a thin core that can model the world and provide the basic operations defined
+on it but anything else should go in separate modules.
+<p>
+Fortunately PCB already had a strong infrastructure supporting this idea.
+It has dynamic loadable plugins and the GUI and exporters are in separate
+HID modules. While working on pcb-gpmi and later pcb-rnd, I added the
+gpmi module as a separate plugin.
+<p>
+In version 1.0.8 and 1.0.9 a cosiderable chunk of core code has been moved into
+<i>core plugins</i>. A <i>core plugin</i> is just a plugin that is
+maintained together with the core, in the same repository, still the code is
+somewhat detached from the core. More importantly, the user can choose, for
+each plugin, separately:
+<ul>
+	<li> to compile it as a buildin (static-link it into the pcb executable)
+	<li> to compile it as a plugin (dynamic-link it runtime, if the .so is installed in the plugins/ directory)
+	<li> to disable the plugin, so it is not compiled at all
+</ul>
+<p>
+I believe such modularization has benefits on multiple levels:
+<ul>
+	<li> it is possible to compiler smaller, potentially faster executables by omitting features the specific user would never use anyway
+	<li> in a distribution dynamic-link plugins can be distributed as separate packages providing the user with the option to decide what features to install
+	<li> such plugins have to have some sort of APIs if they want to reference eachother or if the code needs to reference them; such an API may not (and often did not) exist when the code is part of the core
+</ul>
+
+<H2> Progress in charts </H2>
+<h3> Before-after </h3>
+All numbers are in <a href="http://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>
+and are acquired running sloccount on the given directory. While lines of
+code alone is not a true measure of complexity, it's a good estimation. The
+slices of pie charts are the major components of the pcb-rnd executable.
+<table border=1 cellspacing=0> <tr><td>
+	<table border=0>
+	<tr><td><img src="before.png"><td>        <td><img src="after.png">
+	<tr><td>Before modularization: pcb-rnd version 1.0.7
+	        <br>Note: gpmi was already a plugin
+	    <td>
+	    <td> After modularization: pcb-rnd version 1.0.9
+	        <br>Note: gpmi is part of the "plugins" slice
+	</table>
+</table>
+<h3>Zooming on to the plugins</h3>
+<p>
+<img src="mods.png">
+<p>
+(Red means the plugin doesn't really work).
+
+<H2> Progress in numbers </H2>
+Below is a table with the summary of core plugins.
+<table border=1 cellspacing=0>
+<tr><th>module <th>size [sloc] <th> status <th> configure<br>default  <th> description
+
+
diff --git a/doc-rnd/mods3/after.png b/doc-rnd/mods3/after.png
new file mode 100644
index 0000000..89efbe8
Binary files /dev/null and b/doc-rnd/mods3/after.png differ
diff --git a/doc-rnd/mods3/before.png b/doc-rnd/mods3/before.png
new file mode 100644
index 0000000..dcec960
Binary files /dev/null and b/doc-rnd/mods3/before.png differ
diff --git a/doc-rnd/mods3/gen.sh b/doc-rnd/mods3/gen.sh
new file mode 100755
index 0000000..d41df83
--- /dev/null
+++ b/doc-rnd/mods3/gen.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+path=../../src_plugins
+
+sloc()
+{
+	(cd "$1" && sloccount .) | awk '/^Total Phys/ { size=$9; sub(",", "", size); print size }'
+}
+
+gen_pie()
+{
+	local bn=$1 code_size=$2 color=$3
+	echo ""
+	echo "@slice"
+	echo "$code_size"
+	echo "@label"
+	echo "$bn ($code_size)"
+	if test ! -z "$color"
+	then
+		echo "@color"
+		echo "$color"
+	fi
+}
+
+echo "#autogenerated by gen.sh" > mods.pie
+echo "#autogenerated by gen.sh" > after.pie
+
+echo Core >&2
+tmp=/tmp/pcb-mods-stat
+mkdir $tmp
+cp -r ../../src/*.c ../../src/*.h ../../src/Makefile* $tmp
+code_size=`sloc $tmp`
+gen_pie "core" $code_size "#00ff88" >> after.pie
+
+#echo 3rd >&2
+#code_size=`sloc ../../src_3rd`
+#gen_pie "3rd" $code_size >> after.pie
+
+
+(
+cat pre.html
+for n in $path/*
+do
+	if test -d "$n"
+	then
+		echo $n >&2
+		bn=`basename $n`
+		code_size=`sloc $n`
+		total=$(($total + $code_size))
+		gen_pie $bn $code_size >> mods.pie
+#		case $bn in
+#			gpmi) echo "@pull" >> mods.pie; echo "0.1" >> mods.pie;;
+#		esac
+
+		echo "<tr><th align=left>$bn<td>$code_size"
+		awk '
+			/^#/ {
+				key=$1
+				sub("#", "", key)
+				sub("[:=]", "", key)
+				$1=""
+				DB[key]=$0
+				next
+			}
+			{ desc = desc " " $0 }
+
+			function strip(s) {
+				sub("^[ \t]*", "", s)
+				sub("[ \t]*$", "", s)
+				return s
+			}
+
+			END {
+				st = DB["state"]
+				if (st ~ "partial")
+					clr = "bgcolor=\"yellow\""
+				else if (st ~ "works")
+					clr = "bgcolor=\"lightgreen\""
+				else if ((st ~ "fail") || (st ~ "disable"))
+					clr = "bgcolor=\"red\""
+				else
+					clr=""
+
+				clr2 = clr
+				if (clr2 != "") {
+					sub("bgcolor=\"", "", clr2)
+					sub("\"", "", clr2)
+					print "@color" >> "mods.pie"
+					print clr2 >> "mods.pie"
+				}
+
+				print "<td " clr " >" st
+				if (DB["lstate"] != "")
+					print "<br> (" strip(DB["lstate"]) ")"
+
+				dfl = DB["default"]
+				if (dfl ~ "buildin")
+					clr = "bgcolor=\"lightgreen\""
+				else if (dfl ~ "plugin")
+					clr = "bgcolor=\"yellow\""
+				else if ((dfl ~ "fail") || (dfl ~ "disable"))
+					clr = "bgcolor=\"red\""
+				else
+					clr=""
+
+				print "<td " clr ">" dfl
+				if (DB["ldefault"] != "")
+					print "<br> (" strip(DB["ldefault"]) ")"
+				print "<td>" DB["implements"]
+				print "<td>" desc
+			}
+		' < $n/README
+	fi
+done
+cat post.html
+gen_pie "plugins" "$total" "#0088ff" >> after.pie
+) > index.html
+
+for n in mods after
+do
+	animpie < $n.pie | animator -H -d $n
+	pngtopnm ${n}0000.png | pnmcrop | pnmtopng > $n.png
+	rm ${n}0000.png
+done
+
diff --git a/doc-rnd/mods3/index.html b/doc-rnd/mods3/index.html
new file mode 100644
index 0000000..14ad27d
--- /dev/null
+++ b/doc-rnd/mods3/index.html
@@ -0,0 +1,373 @@
+<html>
+<body>
+<H1> pcb-rnd modularization </H1>
+<H2> Why bother... </H2>
+I believe good software should be modular. This is especially important in
+the context of large software, such as CAD applications. There should be
+a thin core that can model the world and provide the basic operations defined
+on it but anything else should go in separate modules.
+<p>
+Fortunately PCB already had a strong infrastructure supporting this idea.
+It has dynamic loadable plugins and the GUI and exporters are in separate
+HID modules. While working on pcb-gpmi and later pcb-rnd, I added the
+gpmi module as a separate plugin.
+<p>
+In version 1.0.8 to 1.1.0 a cosiderable chunk of core code has been moved into
+<i>core plugins</i>. A <i>core plugin</i> is just a plugin that is
+maintained together with the core, in the same repository, still the code is
+somewhat detached from the core. More importantly, the user can choose, for
+each plugin, separately:
+<ul>
+	<li> to compile it as a buildin (static-link it into the pcb executable)
+	<li> to compile it as a plugin (dynamic-link it runtime, if the .so is installed in the plugins/ directory)
+	<li> to disable the plugin, so it is not compiled at all
+</ul>
+<p>
+I believe such modularization has benefits on multiple levels:
+<ul>
+	<li> it is possible to compiler smaller, potentially faster executables by omitting features the specific user would never use anyway
+	<li> in a distribution dynamic-link plugins can be distributed as separate packages providing the user with the option to decide what features to install
+	<li> such plugins have to have some sort of APIs if they want to reference eachother or if the code needs to reference them; such an API may not (and often did not) exist when the code is part of the core
+</ul>
+
+<H2> Progress in charts </H2>
+<h3> Before-after </h3>
+All numbers are in <a href="http://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>
+and are acquired running sloccount on the given directory. While lines of
+code alone is not a true measure of complexity, it's a good estimation. The
+slices of pie charts are the major components of the pcb-rnd executable.
+<table border=1 cellspacing=0> <tr><td>
+	<table border=0>
+	<tr><td><img src="before.png"><td>        <td><img src="after.png">
+	<tr><td>Before modularization: pcb-rnd version 1.0.7
+	        <br>Note: gpmi was already a plugin
+	    <td>
+	    <td> After modularization: pcb-rnd version 1.1.0
+	        <br>Note: gpmi is part of the "plugins" slice
+	</table>
+</table>
+<h3>Zooming on to the plugins</h3>
+<p>
+<img src="mods.png">
+<p>
+(Red means the plugin doesn't really work).
+
+<H2> Progress in numbers </H2>
+Below is a table with the summary of core plugins.
+<table border=1 cellspacing=0>
+<tr><th>module <th>size [sloc] <th> status <th> configure<br>default <th> class <th> description
+
+
+<tr><th align=left>autocrop<td>158
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Reduce the board dimensions to just enclose the elements. 
+<tr><th align=left>autoplace<td>614
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Automatically place elements. 
+<tr><th align=left>autoroute<td>4343
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Automatically route selected or all rats. This is the original autorouter. 
+<tr><th align=left>boardflip<td>131
+<td  > WIP
+<br> (doesn't update rtrees)
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> All objects on the board are up-down flipped. 
+<tr><th align=left>dbus<td>483
+<td  > WIP
+<br> (needs to install the xml?)
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Remote control PCB using DBUS. 
+<tr><th align=left>diag<td>162
+<td bgcolor="lightgreen" > works
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Actions for pcb-rnd core diagnostics, intended for developers. These are not in core because end users normally don't need these. As a plugin, due to dynamic loading, it can be dropped on an existing pcb-rnd installation with minimal risk of scaring away a reproducible bug. 
+<tr><th align=left>distalign<td>428
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Introducing Align() and Distribute(), which work much like the similarly named functions in Visio. Given that PCB does not have the concept of "first selected object" to draw on, the reference points can be selected by arguments. 
+<tr><th align=left>distaligntext<td>466
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Same as distalign, operates on text objects. 
+<tr><th align=left>djopt<td>2320
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Various board optimization algorithms. 
+<tr><th align=left>export_bboard<td>426
+<td  > WIP
+<td bgcolor="red"> disabled
+<td> export
+<td> Export breadboard 
+<tr><th align=left>export_bom<td>230
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export bom (Bill of Materials) 
+<tr><th align=left>export_dsn<td>443
+<td  > Work-in-progress
+<td bgcolor="red"> disable
+<td> export
+<td> Export specctra .dsn files 
+<tr><th align=left>export_dxf<td>3992
+<td  > WIP
+<td bgcolor="red"> disabled
+<td> export
+<td> Export dxf 
+<tr><th align=left>export_gcode<td>2466
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export to gcode 
+<tr><th align=left>export_gerber<td>971
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export to gerber 
+<tr><th align=left>export_ipcd356<td>468
+<td  > Work-in-progress
+<td bgcolor="red"> disable
+<td> export
+<td> IPC-D-356 Netlist export. 
+<tr><th align=left>export_lpr<td>106
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export to lpr (using export_ps to generate postscript) 
+<tr><th align=left>export_nelma<td>679
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export to nelma (Numerical capacitance calculator) 
+<tr><th align=left>export_openscad<td>1394
+<td  > WIP
+<td bgcolor="red"> disabled
+<td> export
+<td> Export openscad 
+<tr><th align=left>export_png<td>1120
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export to png, gif and jpeg 
+<tr><th align=left>export_ps<td>1638
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export postscript or embedded postscript. 
+<tr><th align=left>export_test<td>257
+<td bgcolor="red" > disabled
+<br> (work in progress)
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> A thin layer of code to dump exporter calls for testing the HID exporter API. 
+<tr><th align=left>export_xy<td>270
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> export
+<td> Export XY centroid element data for pick & place. 
+<tr><th align=left>fontmode<td>165
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Font editing actions. 
+<tr><th align=left>fp_fs<td>379
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> fp
+<td> Footprint: file system based implementation. Used to be called Newlib: load footprints from directories. Run external processes for the parametric footprints. 
+<tr><th align=left>fp_wget<td>302
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> fp
+<td> Footprint: get static (file) footprints from the web, e.g. from http://gedasymbols.org 
+<tr><th align=left>gl<td>590
+<td bgcolor="red" > disabled
+<br> (pcb-rnd has no support for opengl.)
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Common gl functions for hids. 
+<tr><th align=left>gpmi<td>3046
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<br> (if gpmi is installed)
+<td> (feature)
+<td> Scriptable plugin system with about 10 scripting languages supported and dynamic load/unload of scripts that can manipulate the GUI, the board, can implement exporters, etc. 
+<tr><th align=left>hid_batch<td>333
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> hid
+<td> HID without GUI; read actions from stdin. 
+<tr><th align=left>hid_gtk<td>15972
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> hid
+<td> GUI: the GTK HID. 
+<tr><th align=left>hid_lesstif<td>6923
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> hid
+<td> GUI: the lesstif HID. 
+<tr><th align=left>import_dsn<td>114
+<td  > Work-in-progress
+<td bgcolor="red"> disable
+<td> import
+<td> Import specctra .dsn files 
+<tr><th align=left>import_edif<td>3621
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> import
+<td> Import plugin for netlists in the EDIF format. 
+<tr><th align=left>import_netlist<td>131
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> import
+<td> Import plugin for netlists in the classic pcb netlist format. 
+<tr><th align=left>import_sch<td>301
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> import
+<td> Imports element and netlist data from the schematics (or some other source). 
+<tr><th align=left>io_kicad<td>998
+<td  > work-in-progress
+<td bgcolor="red"> disabled
+<td> io
+<td> Load and save the design and elements in Kicad's s-expression format - this is the new, currently preferred format in Kicad. 
+<tr><th align=left>io_kicad_legacy<td>941
+<td  > work-in-progress
+<td bgcolor="lightgreen"> buildin
+<td> io
+<td> Load and save the design and elements in Kicad's legacy format. 
+<tr><th align=left>io_lihata<td>1042
+<td  > WIP
+<td bgcolor="red"> disabled
+<td> io
+<td> Load and save the design and elements in the lihata board format. 
+<tr><th align=left>io_pcb<td>2174
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> io
+<td> Load and save the design and elements in the original pcb text format. 
+<tr><th align=left>jostle<td>446
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Pushes lines out of the way. 
+<tr><th align=left>lib_gensexpr<td>6
+<td bgcolor="lightgreen" > works
+<td bgcolor="red"> disabled
+<td> (lib)
+<td> S-expression parser lib 
+<tr><th align=left>lib_legacy_func<td>74
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (lib)
+<td> Random collection of old/obsolete (legacy) functions. 3rd party plugins may depend on them. This module implements C functions and variables and does not register actions or flags. 
+<tr><th align=left>loghid<td>273
+<td  > WIP
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Sits between a HID (or exporter) and the core and logs all core->plugin calls made through the HID structure. 
+<tr><th align=left>mincut<td>909
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Use the minimal cut algorithm to indicate shorts: instead of highlighting two random pins/pads, try to highlight the least number of objects that connect the two networks. 
+<tr><th align=left>oldactions<td>155
+<td bgcolor="lightgreen" > works
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Random collection of old/obsolete actions. Bell(): audible feedback; DumpLibrary(): print footprint library on stdout; a set of debug actions useful for writing pcb scripts: Debug(), DebugXY(), Return(). Old plugin actions to toggle or set settings that are now accessible via the unified config system (vendordrill, djopt) 
+<tr><th align=left>polycombine<td>208
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> The selected polygons are combined together according to the ordering of their points. 
+<tr><th align=left>polystitch<td>185
+<td  > segfaults
+<td bgcolor="red"> disable
+<td> (feature)
+<td> The polygon under the cursor (based on closest-corner) is stitched together with the polygon surrounding it on the same layer. Use with pstoedit conversions where there's a "hole" in the shape - select the hole. 
+<tr><th align=left>propedit<td>766
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> List and edit properties of a group of objects. 
+<tr><th align=left>puller<td>1888
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Pull traces to minimize their length. 
+<tr><th align=left>query<td>1823
+<td  > WIP
+<td bgcolor="red"> disable
+<td> (feature)
+<td> pcb-rnd query language: execute expressions on objects and rules for the programmed drc. 
+<tr><th align=left>renumber<td>310
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Renumber elements (renaming them) and generate a text file for back annotation. 
+<tr><th align=left>report<td>749
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Report() and ReportObject() actions - print a report about design objects. 
+<tr><th align=left>shand_cmd<td>212
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> vi-like command shorthands (1..3 character long commands) 
+<tr><th align=left>smartdisperse<td>173
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Improve the initial dispersion of elements by choosing an order based on the netlist, rather than the arbitrary element order.  This isn't the same as a global autoplace, it's more of a linear autoplace.  It might make some useful local groupings.  For example, you should not have to chase all over the board to find the resistor that goes with a given LED. 
+<tr><th align=left>stroke<td>135
+<td bgcolor="yellow" > partially works (doesn't work with lesstif; works with the gtk hid, but there's no zoom bindings)
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Gesture recognition with libstroke. 
+<tr><th align=left>teardrops<td>226
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Draw teardrops on pins. 
+<tr><th align=left>toporouter<td>6162
+<td bgcolor="red" > fails
+<br> (infinite loop in gts)
+<td bgcolor="red"> disabled
+<td> (feature)
+<td> Automatically route selected or all rats using a topological algorithm. This is the new autorouter from 2009. 
+<tr><th align=left>vendordrill<td>513
+<td bgcolor="lightgreen" > works
+<td bgcolor="lightgreen"> buildin
+<td> (feature)
+<td> Vendor drill mapping. 
+</table>
+
+<H3> Classes </H3>
+Each plugin implements a class (rarely a set of classes). Classes are:
+<table border=1 cellspacing=0>
+	<tr><th>name         <th> description
+	<tr><td>(feature)    <td> random features directly accessible for the user, usually actions
+	<tr><td>(lib)        <td> support code library for other plugins (core doesn't depend on these); functionality not directly accessible for the user but other plugins may depend on it
+	<tr><td>hid          <td> Human Interface Device: interactive user interface, usually GUI
+	<tr><td>import       <td> load alien formats into the design space
+	<tr><td>export       <td> save (parts of) the design space in alien formats
+	<tr><td>fp           <td> footprint (element) library implementation
+	<tr><td>io           <td> native file format (save & load) implementation
+</table>
+
+</body>
+</html>
diff --git a/doc-rnd/mods3/mods.png b/doc-rnd/mods3/mods.png
new file mode 100644
index 0000000..def8f72
Binary files /dev/null and b/doc-rnd/mods3/mods.png differ
diff --git a/doc-rnd/mods3/post.html b/doc-rnd/mods3/post.html
new file mode 100644
index 0000000..1c30215
--- /dev/null
+++ b/doc-rnd/mods3/post.html
@@ -0,0 +1,17 @@
+</table>
+
+<H3> Classes </H3>
+Each plugin implements a class (rarely a set of classes). Classes are:
+<table border=1 cellspacing=0>
+	<tr><th>name         <th> description
+	<tr><td>(feature)    <td> random features directly accessible for the user, usually actions
+	<tr><td>(lib)        <td> support code library for other plugins (core doesn't depend on these); functionality not directly accessible for the user but other plugins may depend on it
+	<tr><td>hid          <td> Human Interface Device: interactive user interface, usually GUI
+	<tr><td>import       <td> load alien formats into the design space
+	<tr><td>export       <td> save (parts of) the design space in alien formats
+	<tr><td>fp           <td> footprint (element) library implementation
+	<tr><td>io           <td> native file format (save & load) implementation
+</table>
+
+</body>
+</html>
diff --git a/doc-rnd/mods3/pre.html b/doc-rnd/mods3/pre.html
new file mode 100644
index 0000000..e6e2d7c
--- /dev/null
+++ b/doc-rnd/mods3/pre.html
@@ -0,0 +1,60 @@
+<html>
+<body>
+<H1> pcb-rnd modularization </H1>
+<H2> Why bother... </H2>
+I believe good software should be modular. This is especially important in
+the context of large software, such as CAD applications. There should be
+a thin core that can model the world and provide the basic operations defined
+on it but anything else should go in separate modules.
+<p>
+Fortunately PCB already had a strong infrastructure supporting this idea.
+It has dynamic loadable plugins and the GUI and exporters are in separate
+HID modules. While working on pcb-gpmi and later pcb-rnd, I added the
+gpmi module as a separate plugin.
+<p>
+In version 1.0.8 to 1.1.0 a cosiderable chunk of core code has been moved into
+<i>core plugins</i>. A <i>core plugin</i> is just a plugin that is
+maintained together with the core, in the same repository, still the code is
+somewhat detached from the core. More importantly, the user can choose, for
+each plugin, separately:
+<ul>
+	<li> to compile it as a buildin (static-link it into the pcb executable)
+	<li> to compile it as a plugin (dynamic-link it runtime, if the .so is installed in the plugins/ directory)
+	<li> to disable the plugin, so it is not compiled at all
+</ul>
+<p>
+I believe such modularization has benefits on multiple levels:
+<ul>
+	<li> it is possible to compiler smaller, potentially faster executables by omitting features the specific user would never use anyway
+	<li> in a distribution dynamic-link plugins can be distributed as separate packages providing the user with the option to decide what features to install
+	<li> such plugins have to have some sort of APIs if they want to reference eachother or if the code needs to reference them; such an API may not (and often did not) exist when the code is part of the core
+</ul>
+
+<H2> Progress in charts </H2>
+<h3> Before-after </h3>
+All numbers are in <a href="http://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>
+and are acquired running sloccount on the given directory. While lines of
+code alone is not a true measure of complexity, it's a good estimation. The
+slices of pie charts are the major components of the pcb-rnd executable.
+<table border=1 cellspacing=0> <tr><td>
+	<table border=0>
+	<tr><td><img src="before.png"><td>        <td><img src="after.png">
+	<tr><td>Before modularization: pcb-rnd version 1.0.7
+	        <br>Note: gpmi was already a plugin
+	    <td>
+	    <td> After modularization: pcb-rnd version 1.1.0
+	        <br>Note: gpmi is part of the "plugins" slice
+	</table>
+</table>
+<h3>Zooming on to the plugins</h3>
+<p>
+<img src="mods.png">
+<p>
+(Red means the plugin doesn't really work).
+
+<H2> Progress in numbers </H2>
+Below is a table with the summary of core plugins.
+<table border=1 cellspacing=0>
+<tr><th>module <th>size [sloc] <th> status <th> configure<br>default <th> class <th> description
+
+
diff --git a/doc-rnd/motivation.html b/doc-rnd/motivation.html
new file mode 100644
index 0000000..1663b6d
--- /dev/null
+++ b/doc-rnd/motivation.html
@@ -0,0 +1,109 @@
+<html>
+<head>
+	<title> pcb-rnd - motivation </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<H1> pcb-rnd motivation </H1>
+<H2> Phase 1: At the beginning... (2013..2014)</H2>
+I use PCB a lot on various computers. I used to try to join the mainstream
+development with small contribution (minor patches) and was active on
+IRC and the mailing lists for a while. However, it didn't work out well,
+and:
+<ul>
+	<li> PCB already knew 95% of everything I'd ever need years ago
+	<li> the remaining 5% was not on the TODO list of developers and generally no one shown much interest in getting patches for those
+	<li> meanwhile a lot of new features have been added, from which most I find totally useless:
+		<ul>
+			<li> dbus
+			<li> gl - transparency makes even simple 2 sided boards unusable; slower than the classic version sw render on all my computers
+			<li> preparation for a C++ transition
+			<li> 3d support in core (I believe communication to 3d cad programs should be via exporter plugins)
+		</ul>
+	<li> the official Debian package enables (requires, depends on) both dbus and gl
+	<li> DVCS - it almost always results in chaos, and has no benefit for such a small group of developers; there are posts from time to time on the mailing list about how to handle the chaos; my choice is to stick with a simple, centralized version control system
+	<li> DVCS <b>always</b> results in increased administration, which I hate to spend my time on - I'd rather invest the time in bugfixing, documentation or implementing new features
+	<li> it's nearly impossible to get small patches applied^1:
+		<ul>
+			<li> often no one has the time to revise and commit them
+			<li> communication requires web2.0 (lp)
+			<li> there are too many cycles of "please fix this and change that first"
+			<li> with the chaos VCS, it's too likely that new feature patches would require ongoing maintenance while sitting in a "feature branch", to avoid that a large scale merge (or rebase, whatever) breaks it when it finally hits the official branch
+			<li> there are too much pondering and too less prototyping; there are features that are being considered for years (for example back annotation, internal connections) with multiple concurrent ideas, but no code. Instead, I'd like to implement some code from the early phase of the idea, test it, and deprecate it if it didn't work out well.
+			<li> I wouldn't even dream about getting larger patches in the official release
+		</ul>
+	<li> no stable release for years; maintaining a set of patches (like pcb-gpmi) and porting them to new releases is too much hassle
+</ul>
+I was pondering a fork for years. The trigger was that one day I've upgraded
+Debian on my lab computer and the new version of PCB came with gl enabled; this
+made PCB absolutely unusable (had to wait like 10 seconds for a scroll) while
+all the transparent polys over traces made the whole screen a mess. I should
+have recompiled everything and built a new Debian package with gl disabled or
+install from source (but I have too many computers for that). My decision
+was to set up my own .deb but then build it from a fork (it's not much of
+an extra effort), so I can add some of the features I miss in daily use.
+My plans with this fork:
+<ul>
+	<li> I stick with my fork and will use it on all my computers for all my designs
+	<li> Because of that, there's no emphasis on keeping the file formats compatible - breaking compatibility is not a goal either; as long as mainline doesn't change the format, pcb-rnd is about 98% compatible (the 2% is where pcb-rnd features are not supported by mainline)
+	<li> I won't actively seek ways to get my changes into the mainstream; I will keep on using my svn repo in a manner that (as a side effect) makes such merges easier, tho. If PCB developers decide to pick them up from svn and merge them in the official repo, it's fine (but I personally will keep using my fork anyway).
+	<li> Most probably I won't merge anything back from the mainstream to my fork - the past few years showed that it's unlikely there would be new features I'd need
+	<li> My plans for new features were:
+		<ul>
+			<li> pin shapes (a <a href="features/square.html">preliminary version</a> is already implemented)
+			<li> 1206 jumpers without having to add them on the schematics/netlist (done: <a href="features/intconn.html">[intconn]</a> and <a href="features/nonetlist.html"> [nonetlist] </a> are the PCB-side features for this)
+			<li> merge pcb-gpmi; GPMI would be an optional addon, it'd probably stay a plugin, but there should not be a separate repo (<a href="gpmi"> done </a>)
+		</ul>
+</ul>
+<p>
+Footnotes:
+<ul>
+	<li> ^1: this may have changed lately and pcb developers are more open to newcomers; there seems to be a shortage of developers tho, which still makes it slow to get bigger patches through
+</ul>
+
+<H2> Phase 2: major cleanups (2015..2016) </H2>
+In the first phase I was mostly implementing a set of small features and fixes.
+As I got more familiar with the code base, I decided to bite the bullet and
+do some refactoring:
+<ul>
+	<li> replaced the <a href="features/pcb-fp.html">footprint mapping/loading code</a>; instead of a hardwired m4 dependency, parametric (generated-on-the-fly) footprints can be written in any language
+	<li> replaced the default footprint library shipped with the software; the <a href="features/pcblib.html"> new library </a> ships a small, well organized collection of essentials, omitting special/rarely used footprints
+	<li> got the code much more modular - <a href="mods3"> a lot of core code got converted into plugins </a>
+	<li> threw out the resource parser and file format (manu.res and friends) <a href="features/res.html"> in favor of lihata;</a> this removed a lot of code duplication and a strangely designed resource tree data structure I really hated; as a side effect the gtk hid has multi-stroke hotkeys
+	<li> <a href="features/unglib.html">replaced glib</a> with a set of <a href="http://repo.hu/projects/articles/minilibs"> mini libs </a> in core and most of the plugins; at the end only the gtk hid should depend on glib; this made the code easier to maintain and debug; a lot of checks are now compile-time (through the C type system) instead of runtime (glib lists)
+	<li> replaced the settings/rc/preferences system with a central, <a href="conf/"> lihata based configuration system </a> - long term hid attributes will be converted too
+</ul>
+<p>
+Plans for the future includes:
+<ul>
+	<li> turning most of the core code into a library for external tools to reuse
+	<li> extending the core to provide an infrastructure for composite objects handled by plugins
+	<li> support for saving and loading pcb and footprint files in the lihata format
+	<li> plans for a set of smallish features that can be implemented in a weekend or two each.
+</ul>
+
+<H2> Phase 3: community requested features (from 2016 onward)  </H2>
+Overlapping phase 2 there is an ongoing
+<a href="http://igor2.repo.hu/cgi-bin/pcb-rnd-poll.cgi"> feature poll </a>. If there
+are enough <b>active</b> users/testers for a feature, it gets implemented in
+phase 3.
+<p>
+There is a small, active, constructive community forming around pcb-rnd. Future
+directions will be mainly set by their need.
+</html>
+</body>
+
diff --git a/doc-rnd/myfeature.html b/doc-rnd/myfeature.html
new file mode 100644
index 0000000..7efd2f1
--- /dev/null
+++ b/doc-rnd/myfeature.html
@@ -0,0 +1,69 @@
+<html>
+<head>
+	<title> pcb-rnd - my feature </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - how to get my favorite feature implemented </h1>
+
+The list below is sorted: top items have higher priority, but you
+are free to choose any.
+
+<H3> 1. If you are a programmer with free time to spend </H3>
+Read the <a href="contrib.html"> contribution howto</a> and join the project.
+You are welcome to work only on the feature you are interested in. You get
+all the support (e.g. for understending the API, to get a blank plugin
+set up so you need to fill in only the feature-specific parts, etc.). Success
+rate shoudl be near to 100%.
+
+<H3> 2. If you are not a programmer and have free time </H3>
+You can join and work on things you don't need that much, but others do.
+This will build your reputation in the community which in turns encourages
+others to implement your favorite feature even if they wouldn't do it for
+themselves. The success rate is somewhat more random, obviously.
+
+<H3> 3. If you don't have free time </H3>
+Consider donation: buy someone else's time. To do so, contact me
+(see the <a href="contrib.html"> contribution howto</a> page). Since
+you don't need to cover all costs and the hourly rates are much lower
+on a pet project, and your feature may be simpler than it looks, and
+someone who already knows the code also saves the learning cureve, it may
+all be cheaper than you think. The success rate should be close to 100%.
+<p>
+However, this is a last resort solution, the above ones are clearly preferred.
+
+
+<H3> 4. If you don't have anything to contribute with </H3>
+[DEL:Vote for it in the <a href="http://igor2.repo.hu/cgi-bin/pcb-rnd-poll.cgi">
+feature poll</a>. If your feature is not there, ask on the mailing list.
+Chance for success: if it's a popular demand on the feature poll, chances
+are somewhat good. Else chances are very low, but not zero.:DEL]
+<p>
+Because of 100% passivity of the geda community, I decided not to spend more
+time on community-related tasks. The poll is there, you can use it,
+but you will have to orgnaize at least 5 active, capable users who are
+commited enough to spend significant time on testing/supporting development.
+I will consider implementing your feature only if you can bring and keep
+those people motivated and active.
+
+<H3> 5. If you tried everything, even point 4. and all failed... </H3>
+Complain loudly on the mailing list, advertise your opinion, try pushing
+the thing - a.k.a. go trolling. <b>Success rate is exactly 0%</b>,
+you make a lot of good <del>friends</del> enemies that will make it harder
+to ask or contribute later for another feature, but it may make you feel
+better. </irony>.
+
diff --git a/doc-rnd/news.html b/doc-rnd/news.html
new file mode 100644
index 0000000..254aa78
--- /dev/null
+++ b/doc-rnd/news.html
@@ -0,0 +1,53 @@
+<html>
+<head>
+	<title> pcb-rnd - news </title>
+<!--AUTO head begin-->
+	<link rel="icon" href="http://repo.hu/projects/pcb-rnd/logo16.png">
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+<table border=0 cellspacing=2 cellpadding=10  bgcolor=#eeeeee width=100%>
+<tr>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/"> Main </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/projects/pcb-rnd/news.html"> News </a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi">People</a>
+<th align=center bgcolor=#ccc0cc> <a href="http://repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=events">Events</a> & <a href="http://www.repo.hu/cgi-bin/pcb-rnd-people.cgi?cmd=timeline"> timeline </a>
+<td align=right width=60%> <font size=+2><i>pcb-rnd</i></font> <img src="http://repo.hu/projects/pcb-rnd/logo32.png">
+</table>
+<!--AUTO navbar end-->
+
+<br>
+<!--content-->
+<h1> News </h1>
+<table border=0 cellpadding=20 width=100%>
+<tr><td align=right valign=top bgcolor=#eeeeee> 
+	<table border=0 width=100% cellpadding=15 cellspacing=15>
+	<tr>
+		<th align=right valign=top bgcolor=#ccccff width=20%>
+			2016-09-04
+			<br>
+			Rebranding
+		<td bgcolor=#ddddff> 
+			We have a new main page reflecting the transition how we regard pcb-rnd:
+			it is not much of just a fork of geda/pcb by now, but a separate PCB
+			editor with its own team and own course. File save/load compatibility is
+			maintained.
+
+	<tr>
+		<th align=right valign=top bgcolor=#ccccff width=20%>
+			2016-08-24
+			<br>
+			Release 1.1.1
+		<td bgcolor=#ddddff>
+			Available at <a href="releases/"> the download page </a>.
+			<br>
+			The next development cycle will focus on editable/searchable attributes
+			and a new native file format.
+
+	</table>
+</table>
+
+</body>
+</html>
diff --git a/doc-rnd/packaging.txt b/doc-rnd/packaging.txt
new file mode 100644
index 0000000..3df60c3
--- /dev/null
+++ b/doc-rnd/packaging.txt
@@ -0,0 +1,90 @@
+Packaging pcb-rnd for a distribution
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1. program, libs, desktop integration
+
+Pcb-rnd is an interactive printed circuit board editor program. A typical
+user is an electric engineer. This sort of programs are often called EDA,
+Electronics Design Automation.
+
+Pcb-rnd has different frontends: graphical and stdio/text based batch
+processing. There are different graphical frontends - pcb-rnd does not
+depend on gtk or motif, the specific frontend modules may depend on them.
+
+Pcb-rnd is scriptable if libgpmi is available. Since libgpmi is not
+a mainstream library for the scripting feature it should also be packaged.
+
+Pcb-rnd is intended for UNIX-fans. It is heavily keyboard-oriented and it
+doesn't assume its users don't want to understand or learn how it works.
+Many typical user workflows include Makefiles and it's a common thing to
+start pcb-rnd from a shell. Thus desktop integration is not in focus
+at the moment. If you fixed things for this while packaging, please let
+us know, we may merge your patches (see below).
+
+2. options, plugins, dependencies
+
+Pcb-rnd is a modular software with a lot of options. Majority of these options
+are selected by the state of a module. A module has three states:
+ - disable: (not compiled at all)
+ - buildin: enabled and static linked
+ - plugin: enabled and dynamic loaded (from a .so)
+
+The configure script takes --disable-*, --building-* and --plugin-*
+arguments with * substituted with the name of the module. The configure
+script also determines the dependencies according to the selection. Special
+arguments --all=plugin and --all=buildin are provided in configure to select
+all working modules to be plugins or buildins.
+
+For binary distributions it's strongly recommended to have most of the
+modules compiled as plugins and have them in separate packages. This
+would let the user to install a core pcb-rnd package and then the GTK
+or the lesstif plugin (or both) from separate packages. This also reduces
+package dependencies.
+
+This is less relevant for source distributions, if the user has full control
+over the configuration.
+
+If non-critical dependencies are not met, modules are automatically disabled
+by ./configure. 
+
+3. typical ./configure options - scconfig vs. auto*
+
+./configurfe --prefix works as expected. DESTDIR is called install_root.
+
+Typical commands for configuring pcb-rnd for packaging would be:
+
+  ./configure --all=plugin --prefix=/usr
+  make all
+  install_root=/tmp/pkg_tmp make install
+
+We are happy with scconfig. Please don't write about how much better
+autoconf or cmake would be, we won't switch.
+
+4. release cycles, tarballs, svn trunk - what to package
+
+Development happens in svn trunk/, there are usually no branches. While
+we try to keep trunk/ compilable any time, it's not guaranteed that it
+would work out-of-the-box between releases. It is not recommended to
+package trunk/ - please package stable releases instead.
+
+We follow the "release early, release often" rule. Source release tarballs
+are always considered stable. Beside the project web page, releases are
+also accessible as svn tags: svn://repo.hu/pcb-rnd/tags .
+
+Sometimes we have minor releases with 1..2 month period, sometimes a
+longer period passes between releases.
+
+There's no automated release notification at the moment; if you want to
+get notified about new releases, please contact me (email
+pcb-rnd (at) igor2.repo.hu)
+
+5. bug reporting and fixes for packaging
+
+There's no special method for reporting packaging related bugs and
+feature requests, please follow the normal bug report instructions or just
+drop me a mail. Please mention that the issue is related to packaging, this
+usually gives it a higher priority.
+
+We are willing to make minor changes to make packaging easier - if you bump
+into something you'd need to work around while packaging, please let us know,
+we may just fix it "upstream".
+
diff --git a/doc-rnd/query/tutor_cli.html b/doc-rnd/query/tutor_cli.html
new file mode 100644
index 0000000..e6aff0a
--- /dev/null
+++ b/doc-rnd/query/tutor_cli.html
@@ -0,0 +1,168 @@
+<html>
+<body>
+
+<H1> advanced searc, command line tutorial: query language </H1>
+
+<h2> scope </h2>
+
+pcb-rnd from version 1.1.3 features a flexible advnced search that helps
+the user selecting/unselecting objects that match a given logical expression.
+The core of the feature is the pcb-rnd query language. The same language
+is used in the programmable DRC (see also: <a href="../ddrc/proposal1.txt">
+a more formal description of the language</a>).
+<p>
+The current document is a walk through of the practical aspects of the
+language. It starts with the simplest examples while working towards more
+complex cases.
+<p>
+A limited version of the functionality is accessible through a GUI
+wizard when using the GTK HID. A  <a href="tutor_gtk.html">separate
+tutorial</a> is dealing with that feature.
+
+<h2> Actions </h2>
+The query(act, expr) action creates the list called "@", which contains all
+objects of the design. Then it iterates over this list (if needed) and
+evaluates the query expression on each object. For each evaluation "act"
+is performed; "act" is one of:
+<ul>
+	<li> select - add the matching object to the selections if expr evaluated to true
+	<li> unselect - remove the matching object from the selections if expr evaluated to true
+	<li> eval - print the result of the expression to stdout
+	<li> dump - this inhibits evaluating; it compiles the expression and dumps the parse tree (useful for debugging)
+</ul>
+<p>
+The symbol @ in the query represents the iterator, or in other words,
+the <i>current object</i> we are checking. The engine iterates over all
+copper objects, silk objects, pins, holes, layers and nets. A simple query
+that selects all objects looks like this:
+<pre>
+query(select, '@')
+</pre>
+The actual query experssion was a single @ in this case. This made
+the iteration happen, got the expression evaluated one each object. The
+result of each evaluation was the given object. Since these objects
+were all existing, valid objects, they were taken as logical value TRUE,
+thus for each the add-to-selection was performed.
+<p>
+Note: it's usually a good idea to write the expression in single quotes, because
+it may contain commas, double quotes and parenthesis that pcb-rnd's action parser may
+take as action syntax.
+<p>
+The same expression can ran with eval would print the result of each
+evaluation:
+<pre>
+query(eval, '@')
+</pre>
+This is visible on the terminal pcb-rnd was started from - on X, it's a good
+idea to start a terminal and run pcb-rnd from that instead from a menu or
+icon. The rest of this tutorial will use the eval query because it's easier
+to include the result of an eval than of a selection. Most examples will
+specify the query expression only, without quotes - the reader should
+add the <i>query(eval, ' ')</i> part.
+
+<H2> iteration vs. evaluate once </h2>
+If an expression does not reference the @ object, no iteration is performed
+and the expression is ran only once:
+<pre>
+query(eval, '1')
+</pre>
+This will calculate the value of 1 and prints it to the standard output. Since
+theres no iteration, this can not result in changes in selection. However,
+it makes it easy to demonstrate some basic concepts of the query language.
+<p>
+Note: if @ is present multiple times in the expression, it's still only
+one loop over all objects. When evaluating the expression for a given object,
+all instances of @ are substituted with the same object in that iteration.
+
+
+<H2> grammar: arithmetics and logics </h2>
+For example the integer and floating point numbers and the usual
+arithmetic and logical operators work as expected:
+
+<table border=1>
+	<tr><th>expression <th> result <th> explanation
+	<tr><td>42         <td> 42     <td> the integer value 42
+	<tr><td>3.14       <td> 3.14   <td> the floating point value 3.14
+	<tr><td>10 mil     <td> 254000 <td> a number with a unit suffix is converted to pcb-rnd's internal coordinate unit (nanometers)
+	<tr><td>1+2        <td> 3      <td> sum of 1 and 2
+	<tr><td>2*4        <td> 8      <td> multiplication
+	<tr><td>47/4       <td> 11     <td> <i>integer</i> division (because both operands were integers)
+	<tr><td>47/4.0     <td> 11.75  <td> <i>floating point</i> division (because at least one of the operands was a float)
+	<tr><td>(1+2)*5    <td> 15     <td> parenthesis works as usual
+	<tr><td>1 && 0<td> 0   <td> logical AND - the result is 1 if both operands were TRUE, 0 else
+	<tr><td>1 || 0     <td> 1      <td> logical OR  - the result is 1 if either operand was TRUE, 0 else
+	<tr><td>!2         <td> 0      <td> logical NOT - 2 is non-zero, so it is TRUE, negate this to get the result (FALSE, which is 0)
+	<tr><td>4 > 2      <td> 1      <td> because four is greater than two; all the usual relational operats work: == is equal, != is not-equal, <, &lt=,  > and &gt=.
+</table>
+
+<H2> grammar: object properties </h2>
+Object have named properties, e.g. the thickness of a line (or arc, or
+trace in general) is called <i>".thickness"</i>, so the thickness of
+the current object is:
+<pre>
+	@.thickness
+</pre>
+<p>
+Next, we can already select all traces thicker than 10 mil:
+<pre>
+	query(select, '@.thickness > 10 mil')
+</pre>
+<p>
+Because logical expressions are available, it's easy to select all medium-thick
+lines:
+<pre>
+	(@.thickness >= 10 mil) && (@.thickness <= 30 mil)
+</pre>
+<p>
+or any trace that's too thin or too thick:
+<pre>
+	(@.thickness < 10 mil) || (@.thickness > 30 mil)
+</pre>
+<p>
+or traces that don't match our preferred 8, 10 and 15 mil thicnkess values:
+<pre>
+	(@.thickness != 8 mil) && (@.thickness != 10 mil) && (@.thickness != 15 mil)
+</pre>
+
+<H2> grammar: invalid value </h2>
+But how comes an expression like '@.thickness > 10 mil' works while
+@ iterates over non-trace objects, like layers, nets or elements that
+clearly have no thickness?
+<p>
+The answer is the <i>invalid value</i>, which is neither true nor false. If
+a property does not exist in a given object, the result is the <i>invalid value</i>
+or <i>invalid</i> for short. The invalid is treated specially:
+<ul>
+	<li> in arithmetic operations it propagates: if either operand is invalid, the result is invalid, e.g.  1+invalid = invalid; this affects +, -, *, / and all the relational operators
+	<li> in binary logic operations (&& and ||), it is ignored: it is assumed to be true in a && and false in a || so the outcome depends on the other operand
+	<li> in logical NOT (!), TODO
+</ul>
+When @ refers to a non-trace object, @.thickness is invalid and
+'@.thickness > 10 mil' is evaluated t invalid. If the result is not TRUE,
+the requested action is not performed.
+<p>
+The invalid value is generated only when the expression is valid but the
+actual object doesn't have the feature needed. If the expression is wrong,
+an error is generated and the expression stops evaluating immediately.
+
+<H2> grammar: more properties </h2>
+Some properties are a single numeric value, like thickness, or the
+<i>.name</i> of a layer (which is a string) but others are objects. For
+example the <i>.layer</i> property of a line or arc refers to the layer
+object the given line or arc is drawn on. These in turn can be combined,
+and the "name of the layer the current object is on":
+<pre>
+	@.layer.name
+</pre>
+<p>
+Referencing non-existing properties yields an error, e.g. @.thickness.layer
+is an error - in contrast with @.layer, which may evaluate to the
+<i>invalid value</i> (e.g. for vias or nets, because they don't have layers).
+The difference is that tickness can not have a layer ever (thus is an error)
+while @.layer is either applicable or not, but potentially could work, this
+when it doesn't, it's only an invalid value.
+<p>
+Further useful property combinatons:
+TODO
+<p>
+There is a <a href="props.html">full list of all properties</a>.
diff --git a/doc-rnd/screenshot.jpg b/doc-rnd/screenshot.jpg
new file mode 100644
index 0000000..13989a7
Binary files /dev/null and b/doc-rnd/screenshot.jpg differ
diff --git a/doc-rnd/windows.txt b/doc-rnd/windows.txt
new file mode 100644
index 0000000..0bdc078
--- /dev/null
+++ b/doc-rnd/windows.txt
@@ -0,0 +1,38 @@
+State on windows
+~~~~~~~~~~~~~~~~
+
+Does not work yet. This file documents how far we got. The situation
+is constantly improving.
+
+1. Cross compilation from Linux to win32
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(developers may need this, if you are an user, skip to 2.)
+
+TODO: deps with mpk
+
+Use mingw and wine:
+
+	./configure --emu="wine cmd /c" --target=i586-mingw32msvc "$@"
+
+(Replace i586- with whatever toolchain prefix you have)
+
+2. download the binary pack
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Temporary URL: http://igor2.repo.hu/tmp/pcb-rnd-pack.zip
+
+Download, unpack, run setup.bat then pcb-rnd.bat
+
+All binaries in the pack are cross-compiled from source, using mingw and
+mpk. All binaries are 32-bit. The pack is self-contained, no gtk or other
+external dependencies.
+
+Known bugs:
+	- inserting elements fail (even static ones)
+	- no shell and awk in the pack -> parametric footprints fail
+	- text (including menu text) doesn't appear on XP
+	- no exporters that depend on libgd (png, jpeg, gif, nelma)
+
+Worked on systems:
+	- Windows 8.1 pro, Hungarian, 32 bit
+	- Windows 7 pro, Hungarian, 64 bit
+	- Windows 7 pro, English, 64 bit
diff --git a/doc-rnd/wishlist.txt b/doc-rnd/wishlist.txt
new file mode 100644
index 0000000..ce6d399
--- /dev/null
+++ b/doc-rnd/wishlist.txt
@@ -0,0 +1,32 @@
+Active user wishes:
+	W1: cschem [Chris]
+	W2: proper primitives [Chris]
+	W3: more generic footprints [Chris, Evan]
+	W4: user doc [Miloh]
+	W5: 3d modeller export [Miloh, Evan]
+	W6: GUI access to object attributes: gtk editor, find/select by attrib [James]
+	W7: programmable drc (star grounding) [James]
+	W8: gl support [Erich, Evan]
+	W9: push & shove [Erich, Evan]
+
+Plan (parallel threads of development):
+
+	A. file format and model
+		Steps:
+			1. lihata persistency
+			2. native lihata file format
+			3. extend the file format
+				- everything should have an attribute hash -> W6
+				- footprints should have optional attachments (e.g. 3d models) -> W5
+		This helps us testing out the persistent-lihata idea that'd be
+		later used in chscem from the start -> W1
+		This is also needed for more generic footprint implementation -> W3
+
+	B. visible attribute support
+		Steps:
+			1. GTK dialog box for attribute edition
+			2. search & select should work by attribute
+			3. extend the routing style system to auto-add attributes
+			   (this needs A., the new file format)
+		Allows the user to manage properties like "50 ohm net" in attributes,
+		to group objects by property, and to select them by property. -> W6
diff --git a/pcblib/Makefile b/pcblib/Makefile
new file mode 100644
index 0000000..a850c03
--- /dev/null
+++ b/pcblib/Makefile
@@ -0,0 +1,34 @@
+# This Makefile is a plain old hand written one; all configuration settings
+# are included from ../Makefile.conf which is scconfig generated
+
+all:
+
+# NOTE: this rule is _not_ called from linstall
+install_:
+	$(MKDIR) "$(DATADIR)/pcblib"
+	$(MKDIR) "$(DATADIR)/pcblib/connector"
+	$(CPC) "`pwd`/connector"/* "$(DATADIR)/pcblib/connector"
+	$(MKDIR) "$(DATADIR)/pcblib/parametric"
+	$(CPC) "`pwd`/parametric"/* "$(DATADIR)/pcblib/parametric"
+	$(MKDIR) "$(DATADIR)/pcblib/smd"
+	$(CPC) "`pwd`/smd"/* "$(DATADIR)/pcblib/smd"
+	$(MKDIR) "$(DATADIR)/pcblib/tru-hole"
+	$(CPC) "`pwd`/tru-hole"/* "$(DATADIR)/pcblib/tru-hole"
+
+install:
+	make install_ CPC="$(CP)"
+
+# hack: pcb's chdir() based approach gets fooled on symlinks because of "cd .."
+# returns to the wrong dir - rather symlink the whole dir
+linstall:
+	make uninstall
+	$(MKDIR) "$(DATADIR)"
+	$(LN) "`pwd`" "$(DATADIR)/pcblib"
+
+uninstall:
+	$(RM) "$(DATADIR)/pcblib"
+
+clean:
+
+include ../Makefile.conf
+
diff --git a/pcblib/README b/pcblib/README
new file mode 100644
index 0000000..9c855c3
--- /dev/null
+++ b/pcblib/README
@@ -0,0 +1,8 @@
+This directory hosts the pcb-rnd's new footprint library. To avoid
+confusuin with "newlib", which is in use in mainline, this one is called
+pcblib.
+
+Some footprints are dynamically generated by scripts; they are
+called parametric footprints and are in parametric/. Other footprints
+are plain old data file footprints (static footpritns) in all the other
+directories.
diff --git a/pcblib/connector/BNC_LAY.fp b/pcblib/connector/BNC_LAY.fp
new file mode 100644
index 0000000..33bd922
--- /dev/null
+++ b/pcblib/connector/BNC_LAY.fp
@@ -0,0 +1,15 @@
+##from:pcb
+##geo:90
+##geo:female
+
+Element(0x00 "right angle BNC" "" "BNC_LAY" 200 0 3 100 0x00)
+(
+	ElementLine(-60 -290 490 -290 10)
+	ElementLine(490 -290 490 290 10)
+	ElementLine(490 290 -60 290 10)
+	ElementLine(-60 290 -60 -290 10)
+	Pin(0 0 60 35 "1" 0x101)
+	Pin(0 -100 60 35 "2" 0x01)
+	Pin(200 -200 100 81 "m1" 0x01)
+	Pin(200 200 100 81 "m2" 0x01)
+)
diff --git a/pcblib/connector/DB15F.fp b/pcblib/connector/DB15F.fp
new file mode 100644
index 0000000..c0ca13e
--- /dev/null
+++ b/pcblib/connector/DB15F.fp
@@ -0,0 +1,72 @@
+##from:pcb
+##geo:90
+##geo:female
+
+Element(0x00 "DSUB connector, female/male" "" "DB15F" 1000 2026 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 2416 10)
+	ElementLine(665 2416 635 2416 10)
+	ElementLine(635 2416 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 2356 665 2356 10)
+	ElementLine(635 2236 665 2236 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 2186 20)
+	ElementLine(770 2186 665 2186 20)
+	ElementLine(665 2186 665 1110 10)
+	# Pins
+		# Pin 1
+		Pin(1056 2026 60 35 "1" 0x101)
+		ElementLine(1016 2026 770 2026 20)
+		# Plazierungsmarkierung == PIN 1
+		# Changed PY1 to PY2 13-Dec-1999 LRD
+		Mark(1056 2026)
+		# Remainder of the first row
+			Pin(1056 1918 60 35 "2" 0x01)
+			ElementLine(1016 1918 770 1918 20)
+			Pin(1056 1810 60 35 "3" 0x01)
+			ElementLine(1016 1810 770 1810 20)
+			Pin(1056 1702 60 35 "4" 0x01)
+			ElementLine(1016 1702 770 1702 20)
+			Pin(1056 1594 60 35 "5" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1486 60 35 "6" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1378 60 35 "7" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1270 60 35 "8" 0x01)
+			ElementLine(1016 1270 770 1270 20)
+		# Second row
+			Pin(944 1972 60 35 "9" 0x01)
+			ElementLine(904 1972 770 1972 20)
+			Pin(944 1864 60 35 "10" 0x01)
+			ElementLine(904 1864 770 1864 20)
+			Pin(944 1756 60 35 "11" 0x01)
+			ElementLine(904 1756 770 1756 20)
+			Pin(944 1648 60 35 "12" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1540 60 35 "13" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1432 60 35 "14" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1324 60 35 "15" 0x01)
+			ElementLine(904 1324 770 1324 20)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 2296 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB15M.fp b/pcblib/connector/DB15M.fp
new file mode 100644
index 0000000..8183251
--- /dev/null
+++ b/pcblib/connector/DB15M.fp
@@ -0,0 +1,71 @@
+##from:pcb
+##geo:90
+##geo:male
+
+Element(0x00 "DSUB connector, female/male" "" "DB15M" 1000 2026 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 2416 10)
+	ElementLine(665 2416 635 2416 10)
+	ElementLine(635 2416 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 2356 665 2356 10)
+	ElementLine(635 2236 665 2236 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 2186 20)
+	ElementLine(770 2186 665 2186 20)
+	ElementLine(665 2186 665 1110 10)
+	# Pins
+		# First row
+			Pin(1056 1270 60 35 "1" 0x101)
+			ElementLine(1016 1270 770 1270 20)
+			Pin(1056 1378 60 35 "2" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1486 60 35 "3" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1594 60 35 "4" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1702 60 35 "5" 0x01)
+			ElementLine(1016 1702 770 1702 20)
+			Pin(1056 1810 60 35 "6" 0x01)
+			ElementLine(1016 1810 770 1810 20)
+			Pin(1056 1918 60 35 "7" 0x01)
+			ElementLine(1016 1918 770 1918 20)
+		# Last pin in first row
+		Pin(1056 2026 60 35 "8" 0x01)
+		ElementLine(1016 2026 770 2026 20)
+		# Second row
+			Pin(944 1324 60 35 "9" 0x01)
+			ElementLine(904 1324 770 1324 20)
+			Pin(944 1432 60 35 "10" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1540 60 35 "11" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1648 60 35 "12" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1756 60 35 "13" 0x01)
+			ElementLine(904 1756 770 1756 20)
+			Pin(944 1864 60 35 "14" 0x01)
+			ElementLine(904 1864 770 1864 20)
+			Pin(944 1972 60 35 "15" 0x01)
+			ElementLine(904 1972 770 1972 20)
+		# Plazierungsmarkierung == PIN 1
+		Mark(1050 1270)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 2296 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB25F.fp b/pcblib/connector/DB25F.fp
new file mode 100644
index 0000000..d4a5230
--- /dev/null
+++ b/pcblib/connector/DB25F.fp
@@ -0,0 +1,92 @@
+##from:pcb
+##geo:90
+##geo:female
+
+Element(0x00 "DSUB connector, female/male" "" "DB25F" 1000 2566 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 2956 10)
+	ElementLine(665 2956 635 2956 10)
+	ElementLine(635 2956 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 2896 665 2896 10)
+	ElementLine(635 2776 665 2776 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 2726 20)
+	ElementLine(770 2726 665 2726 20)
+	ElementLine(665 2726 665 1110 10)
+	# Pins
+		# Pin 1
+		Pin(1056 2566 60 35 "1" 0x101)
+		ElementLine(1016 2566 770 2566 20)
+		# Plazierungsmarkierung == PIN 1
+		# Changed PY1 to PY2 13-Dec-1999 LRD
+		Mark(1056 2566)
+		# Remainder of the first row
+			Pin(1056 2458 60 35 "2" 0x01)
+			ElementLine(1016 2458 770 2458 20)
+			Pin(1056 2350 60 35 "3" 0x01)
+			ElementLine(1016 2350 770 2350 20)
+			Pin(1056 2242 60 35 "4" 0x01)
+			ElementLine(1016 2242 770 2242 20)
+			Pin(1056 2134 60 35 "5" 0x01)
+			ElementLine(1016 2134 770 2134 20)
+			Pin(1056 2026 60 35 "6" 0x01)
+			ElementLine(1016 2026 770 2026 20)
+			Pin(1056 1918 60 35 "7" 0x01)
+			ElementLine(1016 1918 770 1918 20)
+			Pin(1056 1810 60 35 "8" 0x01)
+			ElementLine(1016 1810 770 1810 20)
+			Pin(1056 1702 60 35 "9" 0x01)
+			ElementLine(1016 1702 770 1702 20)
+			Pin(1056 1594 60 35 "10" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1486 60 35 "11" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1378 60 35 "12" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1270 60 35 "13" 0x01)
+			ElementLine(1016 1270 770 1270 20)
+		# Second row
+			Pin(944 2512 60 35 "14" 0x01)
+			ElementLine(904 2512 770 2512 20)
+			Pin(944 2404 60 35 "15" 0x01)
+			ElementLine(904 2404 770 2404 20)
+			Pin(944 2296 60 35 "16" 0x01)
+			ElementLine(904 2296 770 2296 20)
+			Pin(944 2188 60 35 "17" 0x01)
+			ElementLine(904 2188 770 2188 20)
+			Pin(944 2080 60 35 "18" 0x01)
+			ElementLine(904 2080 770 2080 20)
+			Pin(944 1972 60 35 "19" 0x01)
+			ElementLine(904 1972 770 1972 20)
+			Pin(944 1864 60 35 "20" 0x01)
+			ElementLine(904 1864 770 1864 20)
+			Pin(944 1756 60 35 "21" 0x01)
+			ElementLine(904 1756 770 1756 20)
+			Pin(944 1648 60 35 "22" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1540 60 35 "23" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1432 60 35 "24" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1324 60 35 "25" 0x01)
+			ElementLine(904 1324 770 1324 20)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 2836 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB25M.fp b/pcblib/connector/DB25M.fp
new file mode 100644
index 0000000..9201b01
--- /dev/null
+++ b/pcblib/connector/DB25M.fp
@@ -0,0 +1,91 @@
+##from:pcb
+##geo:90
+##geo:male
+
+Element(0x00 "DSUB connector, female/male" "" "DB25M" 1000 2566 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 2956 10)
+	ElementLine(665 2956 635 2956 10)
+	ElementLine(635 2956 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 2896 665 2896 10)
+	ElementLine(635 2776 665 2776 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 2726 20)
+	ElementLine(770 2726 665 2726 20)
+	ElementLine(665 2726 665 1110 10)
+	# Pins
+		# First row
+			Pin(1056 1270 60 35 "1" 0x101)
+			ElementLine(1016 1270 770 1270 20)
+			Pin(1056 1378 60 35 "2" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1486 60 35 "3" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1594 60 35 "4" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1702 60 35 "5" 0x01)
+			ElementLine(1016 1702 770 1702 20)
+			Pin(1056 1810 60 35 "6" 0x01)
+			ElementLine(1016 1810 770 1810 20)
+			Pin(1056 1918 60 35 "7" 0x01)
+			ElementLine(1016 1918 770 1918 20)
+			Pin(1056 2026 60 35 "8" 0x01)
+			ElementLine(1016 2026 770 2026 20)
+			Pin(1056 2134 60 35 "9" 0x01)
+			ElementLine(1016 2134 770 2134 20)
+			Pin(1056 2242 60 35 "10" 0x01)
+			ElementLine(1016 2242 770 2242 20)
+			Pin(1056 2350 60 35 "11" 0x01)
+			ElementLine(1016 2350 770 2350 20)
+			Pin(1056 2458 60 35 "12" 0x01)
+			ElementLine(1016 2458 770 2458 20)
+		# Last pin in first row
+		Pin(1056 2566 60 35 "13" 0x01)
+		ElementLine(1016 2566 770 2566 20)
+		# Second row
+			Pin(944 1324 60 35 "14" 0x01)
+			ElementLine(904 1324 770 1324 20)
+			Pin(944 1432 60 35 "15" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1540 60 35 "16" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1648 60 35 "17" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1756 60 35 "18" 0x01)
+			ElementLine(904 1756 770 1756 20)
+			Pin(944 1864 60 35 "19" 0x01)
+			ElementLine(904 1864 770 1864 20)
+			Pin(944 1972 60 35 "20" 0x01)
+			ElementLine(904 1972 770 1972 20)
+			Pin(944 2080 60 35 "21" 0x01)
+			ElementLine(904 2080 770 2080 20)
+			Pin(944 2188 60 35 "22" 0x01)
+			ElementLine(904 2188 770 2188 20)
+			Pin(944 2296 60 35 "23" 0x01)
+			ElementLine(904 2296 770 2296 20)
+			Pin(944 2404 60 35 "24" 0x01)
+			ElementLine(904 2404 770 2404 20)
+			Pin(944 2512 60 35 "25" 0x01)
+			ElementLine(904 2512 770 2512 20)
+		# Plazierungsmarkierung == PIN 1
+		Mark(1050 1270)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 2836 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB37F.fp b/pcblib/connector/DB37F.fp
new file mode 100644
index 0000000..edad322
--- /dev/null
+++ b/pcblib/connector/DB37F.fp
@@ -0,0 +1,116 @@
+##from:pcb
+##geo:90
+##geo:female
+
+Element(0x00 "DSUB connector, female/male" "" "DB37F" 1000 3214 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 3604 10)
+	ElementLine(665 3604 635 3604 10)
+	ElementLine(635 3604 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 3544 665 3544 10)
+	ElementLine(635 3424 665 3424 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 3374 20)
+	ElementLine(770 3374 665 3374 20)
+	ElementLine(665 3374 665 1110 10)
+	# Pins
+		# Pin 1
+		Pin(1056 3214 60 35 "1" 0x101)
+		ElementLine(1016 3214 770 3214 20)
+		# Plazierungsmarkierung == PIN 1
+		# Changed PY1 to PY2 13-Dec-1999 LRD
+		Mark(1056 3214)
+		# Remainder of the first row
+			Pin(1056 3106 60 35 "2" 0x01)
+			ElementLine(1016 3106 770 3106 20)
+			Pin(1056 2998 60 35 "3" 0x01)
+			ElementLine(1016 2998 770 2998 20)
+			Pin(1056 2890 60 35 "4" 0x01)
+			ElementLine(1016 2890 770 2890 20)
+			Pin(1056 2782 60 35 "5" 0x01)
+			ElementLine(1016 2782 770 2782 20)
+			Pin(1056 2674 60 35 "6" 0x01)
+			ElementLine(1016 2674 770 2674 20)
+			Pin(1056 2566 60 35 "7" 0x01)
+			ElementLine(1016 2566 770 2566 20)
+			Pin(1056 2458 60 35 "8" 0x01)
+			ElementLine(1016 2458 770 2458 20)
+			Pin(1056 2350 60 35 "9" 0x01)
+			ElementLine(1016 2350 770 2350 20)
+			Pin(1056 2242 60 35 "10" 0x01)
+			ElementLine(1016 2242 770 2242 20)
+			Pin(1056 2134 60 35 "11" 0x01)
+			ElementLine(1016 2134 770 2134 20)
+			Pin(1056 2026 60 35 "12" 0x01)
+			ElementLine(1016 2026 770 2026 20)
+			Pin(1056 1918 60 35 "13" 0x01)
+			ElementLine(1016 1918 770 1918 20)
+			Pin(1056 1810 60 35 "14" 0x01)
+			ElementLine(1016 1810 770 1810 20)
+			Pin(1056 1702 60 35 "15" 0x01)
+			ElementLine(1016 1702 770 1702 20)
+			Pin(1056 1594 60 35 "16" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1486 60 35 "17" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1378 60 35 "18" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1270 60 35 "19" 0x01)
+			ElementLine(1016 1270 770 1270 20)
+		# Second row
+			Pin(944 3160 60 35 "20" 0x01)
+			ElementLine(904 3160 770 3160 20)
+			Pin(944 3052 60 35 "21" 0x01)
+			ElementLine(904 3052 770 3052 20)
+			Pin(944 2944 60 35 "22" 0x01)
+			ElementLine(904 2944 770 2944 20)
+			Pin(944 2836 60 35 "23" 0x01)
+			ElementLine(904 2836 770 2836 20)
+			Pin(944 2728 60 35 "24" 0x01)
+			ElementLine(904 2728 770 2728 20)
+			Pin(944 2620 60 35 "25" 0x01)
+			ElementLine(904 2620 770 2620 20)
+			Pin(944 2512 60 35 "26" 0x01)
+			ElementLine(904 2512 770 2512 20)
+			Pin(944 2404 60 35 "27" 0x01)
+			ElementLine(904 2404 770 2404 20)
+			Pin(944 2296 60 35 "28" 0x01)
+			ElementLine(904 2296 770 2296 20)
+			Pin(944 2188 60 35 "29" 0x01)
+			ElementLine(904 2188 770 2188 20)
+			Pin(944 2080 60 35 "30" 0x01)
+			ElementLine(904 2080 770 2080 20)
+			Pin(944 1972 60 35 "31" 0x01)
+			ElementLine(904 1972 770 1972 20)
+			Pin(944 1864 60 35 "32" 0x01)
+			ElementLine(904 1864 770 1864 20)
+			Pin(944 1756 60 35 "33" 0x01)
+			ElementLine(904 1756 770 1756 20)
+			Pin(944 1648 60 35 "34" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1540 60 35 "35" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1432 60 35 "36" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1324 60 35 "37" 0x01)
+			ElementLine(904 1324 770 1324 20)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 3484 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB37M.fp b/pcblib/connector/DB37M.fp
new file mode 100644
index 0000000..88ea6b8
--- /dev/null
+++ b/pcblib/connector/DB37M.fp
@@ -0,0 +1,115 @@
+##from:pcb
+##geo:90
+##geo:male
+
+Element(0x00 "DSUB connector, female/male" "" "DB37M" 1000 3214 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 3604 10)
+	ElementLine(665 3604 635 3604 10)
+	ElementLine(635 3604 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 3544 665 3544 10)
+	ElementLine(635 3424 665 3424 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 3374 20)
+	ElementLine(770 3374 665 3374 20)
+	ElementLine(665 3374 665 1110 10)
+	# Pins
+		# First row
+			Pin(1056 1270 60 35 "1" 0x101)
+			ElementLine(1016 1270 770 1270 20)
+			Pin(1056 1378 60 35 "2" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1486 60 35 "3" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1594 60 35 "4" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1702 60 35 "5" 0x01)
+			ElementLine(1016 1702 770 1702 20)
+			Pin(1056 1810 60 35 "6" 0x01)
+			ElementLine(1016 1810 770 1810 20)
+			Pin(1056 1918 60 35 "7" 0x01)
+			ElementLine(1016 1918 770 1918 20)
+			Pin(1056 2026 60 35 "8" 0x01)
+			ElementLine(1016 2026 770 2026 20)
+			Pin(1056 2134 60 35 "9" 0x01)
+			ElementLine(1016 2134 770 2134 20)
+			Pin(1056 2242 60 35 "10" 0x01)
+			ElementLine(1016 2242 770 2242 20)
+			Pin(1056 2350 60 35 "11" 0x01)
+			ElementLine(1016 2350 770 2350 20)
+			Pin(1056 2458 60 35 "12" 0x01)
+			ElementLine(1016 2458 770 2458 20)
+			Pin(1056 2566 60 35 "13" 0x01)
+			ElementLine(1016 2566 770 2566 20)
+			Pin(1056 2674 60 35 "14" 0x01)
+			ElementLine(1016 2674 770 2674 20)
+			Pin(1056 2782 60 35 "15" 0x01)
+			ElementLine(1016 2782 770 2782 20)
+			Pin(1056 2890 60 35 "16" 0x01)
+			ElementLine(1016 2890 770 2890 20)
+			Pin(1056 2998 60 35 "17" 0x01)
+			ElementLine(1016 2998 770 2998 20)
+			Pin(1056 3106 60 35 "18" 0x01)
+			ElementLine(1016 3106 770 3106 20)
+		# Last pin in first row
+		Pin(1056 3214 60 35 "19" 0x01)
+		ElementLine(1016 3214 770 3214 20)
+		# Second row
+			Pin(944 1324 60 35 "20" 0x01)
+			ElementLine(904 1324 770 1324 20)
+			Pin(944 1432 60 35 "21" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1540 60 35 "22" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1648 60 35 "23" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1756 60 35 "24" 0x01)
+			ElementLine(904 1756 770 1756 20)
+			Pin(944 1864 60 35 "25" 0x01)
+			ElementLine(904 1864 770 1864 20)
+			Pin(944 1972 60 35 "26" 0x01)
+			ElementLine(904 1972 770 1972 20)
+			Pin(944 2080 60 35 "27" 0x01)
+			ElementLine(904 2080 770 2080 20)
+			Pin(944 2188 60 35 "28" 0x01)
+			ElementLine(904 2188 770 2188 20)
+			Pin(944 2296 60 35 "29" 0x01)
+			ElementLine(904 2296 770 2296 20)
+			Pin(944 2404 60 35 "30" 0x01)
+			ElementLine(904 2404 770 2404 20)
+			Pin(944 2512 60 35 "31" 0x01)
+			ElementLine(904 2512 770 2512 20)
+			Pin(944 2620 60 35 "32" 0x01)
+			ElementLine(904 2620 770 2620 20)
+			Pin(944 2728 60 35 "33" 0x01)
+			ElementLine(904 2728 770 2728 20)
+			Pin(944 2836 60 35 "34" 0x01)
+			ElementLine(904 2836 770 2836 20)
+			Pin(944 2944 60 35 "35" 0x01)
+			ElementLine(904 2944 770 2944 20)
+			Pin(944 3052 60 35 "36" 0x01)
+			ElementLine(904 3052 770 3052 20)
+			Pin(944 3160 60 35 "37" 0x01)
+			ElementLine(904 3160 770 3160 20)
+		# Plazierungsmarkierung == PIN 1
+		Mark(1050 1270)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 3484 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB9F.fp b/pcblib/connector/DB9F.fp
new file mode 100644
index 0000000..4d42374
--- /dev/null
+++ b/pcblib/connector/DB9F.fp
@@ -0,0 +1,60 @@
+##from:pcb
+##geo:90
+##geo:female
+
+Element(0x00 "DSUB connector, female/male" "" "DB9F" 1000 1702 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 2092 10)
+	ElementLine(665 2092 635 2092 10)
+	ElementLine(635 2092 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 2032 665 2032 10)
+	ElementLine(635 1912 665 1912 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 1862 20)
+	ElementLine(770 1862 665 1862 20)
+	ElementLine(665 1862 665 1110 10)
+	# Pins
+		# Pin 1
+		Pin(1056 1702 60 35 "1" 0x101)
+		ElementLine(1016 1702 770 1702 20)
+		# Plazierungsmarkierung == PIN 1
+		# Changed PY1 to PY2 13-Dec-1999 LRD
+		Mark(1056 1702)
+		# Remainder of the first row
+			Pin(1056 1594 60 35 "2" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+			Pin(1056 1486 60 35 "3" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1378 60 35 "4" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1270 60 35 "5" 0x01)
+			ElementLine(1016 1270 770 1270 20)
+		# Second row
+			Pin(944 1648 60 35 "6" 0x01)
+			ElementLine(904 1648 770 1648 20)
+			Pin(944 1540 60 35 "7" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1432 60 35 "8" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1324 60 35 "9" 0x01)
+			ElementLine(904 1324 770 1324 20)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 1972 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/DB9M.fp b/pcblib/connector/DB9M.fp
new file mode 100644
index 0000000..6fd637c
--- /dev/null
+++ b/pcblib/connector/DB9M.fp
@@ -0,0 +1,59 @@
+##from:pcb
+##geo:90
+##geo:male
+
+Element(0x00 "DSUB connector, female/male" "" "DB9M" 1000 1702 1 150 0x00)
+(
+	# Gehaeuse (schmaler Kasten incl. Bohrungen)
+	ElementLine(635 880 665 880 10)
+	ElementLine(665 880 665 2092 10)
+	ElementLine(665 2092 635 2092 10)
+	ElementLine(635 2092 635 880 10)
+	ElementLine(635 940 665 940 10)
+	ElementLine(635 1060 665 1060 10)
+	ElementLine(635 2032 665 2032 10)
+	ElementLine(635 1912 665 1912 10)
+	# Gehaeuse (aeusserer Kasten)
+	# This part of the connector normally hangs off the circuit board,
+	# so it is confusing to actually mark it on the silkscreen
+	# define(`X1', `eval(BASEX-PANEL_DISTANCE-260)')
+	# define(`Y1', `eval(PY1-100)')
+	# define(`X2', `eval(BASEX-PANEL_DISTANCE)')
+	# define(`Y2', `eval(PY2+100)')
+	# ElementLine(X1 Y1 X2 Y1 20)
+	# ElementLine(X2 Y1 X2 Y2 10)
+	# ElementLine(X2 Y2 X1 Y2 20)
+	# ElementLine(X1 Y2 X1 Y1 20)
+	# Gehaeuse (innerer Kasten)
+	ElementLine(665 1110 770 1110 20)
+	ElementLine(770 1110 770 1862 20)
+	ElementLine(770 1862 665 1862 20)
+	ElementLine(665 1862 665 1110 10)
+	# Pins
+		# First row
+			Pin(1056 1270 60 35 "1" 0x101)
+			ElementLine(1016 1270 770 1270 20)
+			Pin(1056 1378 60 35 "2" 0x01)
+			ElementLine(1016 1378 770 1378 20)
+			Pin(1056 1486 60 35 "3" 0x01)
+			ElementLine(1016 1486 770 1486 20)
+			Pin(1056 1594 60 35 "4" 0x01)
+			ElementLine(1016 1594 770 1594 20)
+		# Last pin in first row
+		Pin(1056 1702 60 35 "5" 0x01)
+		ElementLine(1016 1702 770 1702 20)
+		# Second row
+			Pin(944 1324 60 35 "6" 0x01)
+			ElementLine(904 1324 770 1324 20)
+			Pin(944 1432 60 35 "7" 0x01)
+			ElementLine(904 1432 770 1432 20)
+			Pin(944 1540 60 35 "8" 0x01)
+			ElementLine(904 1540 770 1540 20)
+			Pin(944 1648 60 35 "9" 0x01)
+			ElementLine(904 1648 770 1648 20)
+		# Plazierungsmarkierung == PIN 1
+		Mark(1050 1270)
+	# Befestigungsbohrung
+	Pin(1000  1000 250 125 "C1" 0x01)
+	Pin(1000 1972 250 125 "C2" 0x01)
+)
diff --git a/pcblib/connector/pwrjack.fp b/pcblib/connector/pwrjack.fp
new file mode 100644
index 0000000..076043e
--- /dev/null
+++ b/pcblib/connector/pwrjack.fp
@@ -0,0 +1,16 @@
+##from:repo.hu/projects/openhw
+##geo:90
+##geo:male
+
+Element["" "" "" "pwrjack" 124016 141732 0 0 0 100 ""]
+(
+	Pin[0 0 19685 2000 20285 11811 "" "2" ""]
+	Pin[0 -27559 19685 2000 20285 11811 "" "1" ""]
+	Pin[-17717 -13779 19685 2000 20285 11811 "" "3" ""]
+	ElementLine [-17717 -55118 -17717 0 1000]
+	ElementLine [-17717 0 17716 0 1000]
+	ElementLine [17716 0 17716 -55118 1000]
+	ElementLine [17716 -55118 -17717 -55118 1000]
+	ElementLine [-17717 -43307 17716 -43307 1000]
+
+	)
diff --git a/pcblib/parametric/acy b/pcblib/parametric/acy
new file mode 100755
index 0000000..ee33c30
--- /dev/null
+++ b/pcblib/parametric/acy
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+#@@example acy(300)
+
+#@@purpose Generate axial lead through-hole component
+
+#@@desc Generate axial lead through-hole component with 2 pins (typical use: resistor)
+#@@params spacing,type,pol,dia
+
+#@@param:spacing spacing between the two pins
+#@@dim:spacing
+
+#@@param:type silk symbol type
+#@@enum:type:block eu-style block resistor symbol
+#@@enum:type:endcap block resistor with caps on the ends
+#@@enum:type:zigzag us-style zigzag resistor symbol
+#@@enum:type:line a single line (e.g. for jumper wires)
+#@@enum:type:standing vertically aligned, body standing on pin 1, pin 2 bent back
+#@@enum:type:coil wavy coil symbol
+#@@enum:type:core wavy coil symbol with a parallel line
+#@@enum:type:core2 wavy coil symbol with two parallel lines
+#@@optional:type
+#@@default:type block
+#@@preview_args:type 300
+
+#@@param:pol how to mark polarity
+#@@enum:pol:none no marking
+#@@enum:pol:sign + and - signs; pin 1 is +
+#@@enum:pol:bar bar next to pin 1
+#@@enum:pol:dot dot next to pin 1
+#@@optional:pol
+#@@default:pol none
+#@@preview_args:pol 300
+
+#@@param:dia body diameter - affects silk size
+#@@dim:dia 
+#@@optional:dia
+#@@default:dia spacing/6
+
+
+#@@param:wiper silk symbol wiper type
+#@@enum:wiper:none no wiper
+#@@enum:wiper:parrow perpendicular arrow, pointing inwards
+#@@enum:wiper:aarrow angled arrow, pointing outwards
+#@@enum:wiper:looparrow arrow starting at pin 2 looping back to point inwards on the body
+#@@enum:wiper:thermistor wiper of a thermistor symbol
+#@@optional:wiper
+#@@default:wiper none
+#@@preview_args:wiper 400
+
+#@@thumbsize 2
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/acy.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/acy.awk b/pcblib/parametric/acy.awk
new file mode 100644
index 0000000..5712eb5
--- /dev/null
+++ b/pcblib/parametric/acy.awk
@@ -0,0 +1,152 @@
+function wave(type, repeat,    step,x,y)
+{
+	step = len/repeat
+	for(x = sx1; x < sx2; x += step) {
+		if (type == 1) {
+			element_arc(x+step/2, 0, step/2, step/2, 0, -180)
+		}
+		else if (type == 2) {
+			y = dia
+			element_line(x, 0, x+step/4, -y)
+			element_line(x+step/4, -y, x+3*step/4, y)
+			element_line(x+3*step/4, y, x+step, 0)
+			
+		}
+	}
+}
+
+BEGIN {
+	set_arg(P, "?type", "block")
+	proc_args(P, "spacing,type,pol,dia", "spacing")
+
+	spacing = parse_dim(P["spacing"])
+	dia = either(parse_dim(P["dia"]), spacing/6)
+
+# oops, dia is a radius rather
+	dia=dia/2
+
+	offs_x = +spacing/2
+
+	element_begin("", "R1", "acy" P["spacing"]    ,0,0, spacing/2-spacing/5,-mil(20))
+
+	element_pin(-spacing/2, 0, 1)
+	element_pin(+spacing/2, 0, 2)
+
+	dimension(-spacing/2, 0, +spacing/2, 0, dia*4, "spacing")
+
+# silk pins
+	if (P["type"] != "line") {
+		element_line(-spacing/2, 0, -spacing/4, 0)
+		element_line(+spacing/4, 0, +spacing/2, 0)
+	}
+
+# silk symbol
+	sx1 = -spacing/4
+	sx2 = +spacing/4
+	len = sx2-sx1
+	if (P["type"] == "block") {
+		element_rectangle(sx1, -dia, sx2, +dia)
+	}
+	else if (P["type"] == "zigzag") {
+		wave(2, 3)
+	}
+	else if (P["type"] == "coil") {
+		wave(1, 4)
+	}
+	else if (P["type"] == "endcap") {
+		cl1 = len/9
+		cl2 = len/8
+		y1 = dia*1.2
+		y2 = dia
+		rarc = dia/5
+		element_line(sx1+cl2, y2, sx2-cl2, y2)
+		element_line(sx1+cl2, y2, sx1+cl1, y1)
+		element_line(sx2-cl2, y2, sx2-cl1, y1)
+		
+		element_line(sx1+cl2, -y2, sx2-cl2, -y2)
+		element_line(sx1+cl2, -y2, sx1+cl1, -y1)
+		element_line(sx2-cl2, -y2, sx2-cl1, -y1)
+
+		element_rectangle(sx1, y1, sx1+cl1, -y1, "right,NE,SE", rarc)
+		element_rectangle(sx2-cl1, y1, sx2, -y1, "left,NW,SW", rarc)
+	}
+	else if (P["type"] ~ "^core") {
+		wave(1, 4)
+		nlines = P["type"]
+		sub("^core", "", nlines)
+		if (nlines == "")
+			nlines = 1
+
+		cdist = 3 * DEFAULT["line_thickness"]
+		y = -len/8
+		for(nlines = int(nlines); nlines > 0; nlines--) {
+			y-=cdist
+			element_line(sx1, y, sx2, y)
+		}
+	}
+	else if (P["type"] == "line") {
+		element_line(-spacing/2, 0, +spacing/2, 0)
+	}
+	else if (P["type"] == "standing") {
+		r = dia*2
+		if (r < DEFAULT["pin_ringdia"]/2*1.2)
+			r = DEFAULT["pin_ringdia"]/2*1.2
+		element_arc(-spacing/2, 0, r, r, 0, 360)
+		element_line(-spacing/2, 0, +spacing/2, 0)
+	}
+	else {
+		error("Invalid type")
+	}
+
+	dimension(sx2, -dia, sx2, dia, spacing/2, "dia")
+
+# silk wiper
+	if (P["wiper"] == "thermistor") {
+		x = len/3
+		element_line(-4*x/4, dia*2, -2*x/4, dia*2)
+		element_line(-2*x/4, dia*2, +2*x/4, -dia*2)
+	}
+	else if (P["wiper"] == "aarrow") {
+		x = len/3
+		element_arrow(-2*x/4, dia*2, +2*x/4, -dia*2-mil(30))
+	}
+	else if (P["wiper"] == "parrow") {
+		element_arrow(0, -dia*2-mil(30), 0, -dia)
+	}
+	else if (P["wiper"] == "looparrow") {
+		y = -dia*2-mil(30)
+		x = sx2+len/8
+		element_arrow(0, y, 0, -dia)
+		element_line(0, y, x, y)
+		element_line(x, y, x, 0)
+	}
+	else if ((P["wiper"] != "none") && (P["wiper"] != "")) {
+		error("Invalid wiper")
+	}
+
+# silk sign
+	if (P["pol"] == "sign") {
+		size=mil(10)
+
+		offs_y = size*2.2
+		offs_x = DEFAULT["pin_ringdia"]/2+size*1.1
+		element_line(-size, 0, +size, 0)
+
+		offs_x = spacing - (DEFAULT["pin_ringdia"]/2+size*1.1)
+		element_line(-size, 0, +size, 0)
+		element_line(0, -size, 0, +size)
+	}
+	else if (P["pol"] == "bar") {
+		offs=DEFAULT["line_thickness"]
+		element_rectangle(-spacing/4-offs, -dia, -spacing/4+offs, +dia,  DEFAULT["line_thickness"]*4)
+	}
+	else if (P["pol"] == "dot") {
+		r=2*DEFAULT["line_thickness"]/3
+		element_arc(-spacing/4-r*3, -dia/2, r, r, 0, 360)
+	}
+	else if ((P["pol"] != "") && (P["pol"] != "none")) {
+		error("Invalid pol")
+	}
+
+	element_end()
+}
diff --git a/pcblib/parametric/alf b/pcblib/parametric/alf
new file mode 100755
index 0000000..7d06916
--- /dev/null
+++ b/pcblib/parametric/alf
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+#@@example alf(300, schottky)
+
+#@@purpose Generate ALF: axial lead through-hole component for diodes
+
+#@@desc Generate axial lead through-hole component with 2 pin diodes
+#@@params spacing,type,dia
+#@@thumbsize 2
+
+#@@param:spacing spacing between the two pins
+#@@dim:spacing
+
+#@@param:type symbol type
+#@@enum:type:normal
+#@@enum:type:schottky
+#@@enum:type:zener
+#@@enum:type:tunnel
+#@@enum:type:varactor
+#@@optional:type
+#@@default:type normal
+#@@preview_args:type 300
+
+#@@param:dia body diameter - affects silk symbol size
+#@@dim:dia
+#@@optional:dia
+#@@default:dia spacing/12
+
+#@@param:aspect silk symbol aspect: total width expressed as a portion of spacing
+#@@optional:aspect
+#@@default:aspect spacing/6
+
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/alf.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/alf.awk b/pcblib/parametric/alf.awk
new file mode 100644
index 0000000..5a88b9b
--- /dev/null
+++ b/pcblib/parametric/alf.awk
@@ -0,0 +1,65 @@
+BEGIN {
+	
+	set_arg(P, "?aspect", 6)
+	set_arg(P, "?type", "normal")
+
+	proc_args(P, "spacing,type,dia,aspect", "spacing")
+
+	spacing = parse_dim(P["spacing"])
+	dia = either(parse_dim(P["dia"]), spacing/6)
+	aspect = P["aspect"]
+
+	offs_x = +spacing/2
+
+	element_begin("", "D1", "acy" P["spacing"]    ,0,0, 2.2*spacing/3,-mil(50))
+
+	element_pin(-spacing/2, 0, 1)
+	element_pin(+spacing/2, 0, 2)
+	dimension(-spacing/2, 0, +spacing/2, 0, dia*4, "spacing")
+
+# pins
+	element_line(-spacing/2, 0, -spacing/aspect, 0)
+	element_line(+spacing/aspect, 0, +spacing/2, 0)
+
+# triangle
+	element_line(+spacing/aspect, -dia, +spacing/aspect, +dia)
+	element_line(+spacing/aspect, -dia, -spacing/aspect, 0)
+	element_line(+spacing/aspect, +dia, -spacing/aspect, 0)
+
+	dimension(+spacing/aspect, -dia, +spacing/aspect, dia, "@" spacing*1.2 ";0", "dia")
+
+
+# front cross line with decoration
+	r = dia*0.3
+	if (P["type"] == "normal") {
+		element_line(-spacing/aspect, -dia, -spacing/aspect, +dia)
+	}
+	else if (P["type"] == "zener") {
+		element_line(-spacing/aspect, -dia, -spacing/aspect, +dia)
+		element_line(-spacing/aspect, +dia, -spacing/aspect-r, +dia)
+		element_line(-spacing/aspect, -dia, -spacing/aspect+r, -dia)
+	}
+	else if (P["type"] == "tunnel") {
+		element_line(-spacing/aspect, -dia, -spacing/aspect, +dia)
+		element_line(-spacing/aspect, +dia, -spacing/aspect+r, +dia)
+		element_line(-spacing/aspect, -dia, -spacing/aspect+r, -dia)
+	}
+	else if (P["type"] == "varactor") {
+		element_line(-spacing/aspect, -dia, -spacing/aspect, +dia)
+		element_line(-spacing/aspect-r, -dia, -spacing/aspect-r, +dia)
+	}
+	else if (P["type"] == "schottky") {
+		cx = -spacing/aspect + r
+		cy = -(dia-r)
+		element_line(-spacing/aspect, -(dia-r), -spacing/aspect, +dia-r)
+		element_arc(cx, cy, r, r, 0, -180)
+		cx = -spacing/aspect - r
+		cy = +(dia-r)
+		element_arc(cx, cy, r, r, 0, +180)
+	}
+	else if ((P["type"] != "") && (P["type"] != "none")) {
+		error("Invalid type")
+	}
+
+	element_end()
+}
diff --git a/pcblib/parametric/bga b/pcblib/parametric/bga
new file mode 100755
index 0000000..bf15bb5
--- /dev/null
+++ b/pcblib/parametric/bga
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), SSOP
+
+#@@example bga(map=a1:a2:a2:#:b1:!:b3:#:c1:c2:!)
+
+#@@purpose Generate ball grid array
+
+#@@desc Generate a grid of circular pads for BGA chips
+
+#@@params nx,ny,spacing,balldia,silkmark,map,width,height,automap,automap2
+
+#@@param:nx number of pins in the X direction
+#@@optional:nx
+#@@default:nx deduced from the map
+
+#@@param:ny number of pins in the Y direction
+#@@optional:ny
+#@@default:ny deduced from the map
+
+#@@param:spacing spacing between the pins
+#@@dim:spacing
+#@@default:spacing 0.5 mm
+
+#@@param:balldia diameter of a ball
+#@@dim:balldia
+#@@default:spacing 0.35 mm
+
+#@@include silkmark.help
+#@@optional:silkmark
+#@@default:silkmark square
+#@@preview_args:silkmark 3,3
+
+#@@param:map pin name map; a colon separated list of names, from left to right, rows first. If a name is empty or is a hash mark, a new row is started. If a name is a !, the given pin is missing and no pad is generated.
+#@@optional:map
+
+#@@param:width width of the box (silk rectangle x size)
+#@@dim:width
+#@@optional:width
+#@@default:width two columns wider than the array
+
+#@@param:height height of the box (silk rectangle y size)
+#@@dim:height
+#@@optional:height
+#@@default:height two columns higher than the array
+
+
+#@@param:automap assume a regular nx*ny array, automap (autonumber) the pins
+#@@optional:automap
+#@@enum:automap:none do not autonumber pins
+#@@enum:automap:alnum number y rows from A..Z (then AA..AZ) from top to bottom, number x rows from 0..nx left to right
+#@@default:none
+#@@preview_args:automap 3,3
+#@@thumbsize:automap 3
+#@@thumbnum:automap 1
+
+#@@param:automap2 change how automap behaves - multiple values separated by colon are accepted (e.g. automap2=pivot,reversex)
+#@@enum:automap2:pivot swap x and y
+#@@enum:automap2:reversex number x from right to left
+#@@enum:automap2:reversey number y from bottom up
+#@@preview_args:automap2 3,3,automap=alnum
+#@@thumbsize:automap2 3
+#@@thumbnum:automap2 1
+
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/bga.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/bga.awk b/pcblib/parametric/bga.awk
new file mode 100644
index 0000000..dc9857c
--- /dev/null
+++ b/pcblib/parametric/bga.awk
@@ -0,0 +1,117 @@
+function pinalpha(p,    s)
+{
+	if (p >= 26)
+		s = pinalpha(int(p/26)-1)
+	return s sprintf("%c", 65 + (p % 26))
+}
+
+function automap(algo, pivot, revx, revy   ,xx,yy)
+{
+	if (algo == 1) {
+		for(y = 0; y < ny; y++) {
+			if (revy)
+				yy = ny - y - 1
+			else
+				yy = y
+			for(x = 0; x < nx; x++) {
+				if (revx)
+					xx = nx - x - 1
+				else
+					xx = x
+				if (pivot)
+					MAP[x,y] = pinalpha(xx) yy+1
+				else
+					MAP[x,y] = pinalpha(yy) xx+1
+			}
+		}
+	}
+}
+
+BEGIN {
+	set_arg(P, "?spacing", "0.5mm")
+	set_arg(P, "?balldia", "0.35mm")
+	set_arg(P, "?silkmark", "arc")
+
+	proc_args(P, "nx,ny,spacing,balldia,silkmark,map,width,height,automap,automap2", "")
+
+	step = parse_dim(P["spacing"])
+
+	half=step/2
+
+	nx = int(P["nx"])
+	ny = int(P["ny"])
+
+	if (P["map"] != "") {
+		v = split(P["map"], A, ":")
+		x = 0
+		y = 0
+		for(n = 1; n <= v; n++) {
+			if ((A[n] == "") || (A[n] == "#")) {
+				x = 0
+				y++
+				continue;
+			}
+			if (x > nx)
+				nx = x
+			if (y > ny)
+				ny = y
+			print x,y,A[n] > "/dev/stderr"
+			MAP[x, y] = A[n]
+			x++
+		}
+		ny++;
+	}
+	else {
+		if ((nx == "") || (ny == ""))
+			error("missing argument: need nx,ny or a map")
+		if (P["automap"] ~ "alnum")
+			automap(1, (P["automap2"] ~ "pivot"), (P["automap2"] ~ "reversex"), (P["automap2"] ~ "reversey"))
+		else if ((P["automap"] ~ "none") || (P["automap"] == "")) {
+		}
+		else
+			error("automap should be alnum or none")
+	}
+
+	balldia = parse_dim(P["balldia"])
+	bw  = parse_dim(P["width"])
+	bh  = parse_dim(P["height"])
+
+	if (bw == "")
+		bw = (nx+1)*step
+	if (bh == "")
+		bh = (ny+1)*step
+
+	xo = (nx-1)*step/2
+	yo = (ny-1)*step/2
+
+	element_begin("", "U1", nx "*" ny   ,0,0, 0, -bh)
+
+	for(x = 0; x < nx; x++) {
+		for(y = 0; y < ny; y++) {
+			xx = x * step - xo
+			yy = y * step - yo
+			name = MAP[x,y]
+			if (name == "!")
+				continue
+			if (name == "")
+				name = "NC"
+			element_pad_circle(xx, yy, balldia, name)
+		}
+	}
+
+	dimension(-xo, -yo, -xo+step, -yo, bw/2, "spacing")
+	dimension(-xo-balldia/2, +yo, -xo+balldia/2, +yo, -bw*0.75, "balldia")
+
+
+	xx = -1 * (bw/2)
+	yy = -1 * (bh/2)
+	element_rectangle(xx, yy, bw/2, bh/2)
+
+	dimension(xx, yy, bw/2, yy, bw/2, "width")
+	dimension(bw/2, yy, bw/2, bh/2, +bh/2, "height")
+
+	silkmark(P["silkmark"], xx, yy, half*1.5)
+
+
+	element_end()
+}
diff --git a/pcblib/parametric/common.awk b/pcblib/parametric/common.awk
new file mode 100644
index 0000000..8578a94
--- /dev/null
+++ b/pcblib/parametric/common.awk
@@ -0,0 +1,517 @@
+#@@param:pin_ringdia pin's copper ring (annulus) outer diameter (in mil by default, mm suffix can be used)
+#@@optional:pin_ringdia
+#@@dim:pin_ringdia
+
+#@@param:pin_clearance pin's copper clearance diameter (in mil by default, mm suffix can be used)
+#@@optional:pin_clearance
+#@@dim:pin_clearance
+
+#@@param:pin_mask pin's solder mask diameter (in mil by default, mm suffix can be used)
+#@@optional:pin_mask
+#@@dim:pin_mask
+
+#@@param:pin_drill copper pin's drill diameter (in mil by default, mm suffix can be used)
+#@@optional:pin_drill
+#@@dim:pin_drill
+
+#@@param:pad_thickness width of pads (in mil by default, mm suffix can be used)
+#@@optional:pad_thickness
+#@@dim:pad_thickness
+
+#@@param:pad_clearance copper clearance around the pad (in mil by default, mm suffix can be used)
+#@@optional:pad_clearance
+#@@dim:pad_clearance
+
+#@@param:pad_mask pin's solder mask (in mil by default, mm suffix can be used)
+#@@optional:pad_mask
+#@@dim:pad_mask
+
+#@@param:line_thickness silk line thickness (in mil by default, mm suffix can be used)
+#@@optional:line_thickness
+#@@dim:line_thickness
+
+BEGIN {
+	q="\""
+
+	DEFAULT["pin_ringdia"] = 8000
+	DEFAULT["pin_ringdia", "dim"] = 1
+
+	DEFAULT["pin_clearance"] = 5000
+	DEFAULT["pin_clearance", "dim"] = 1
+
+	DEFAULT["pin_mask"] = 8600
+	DEFAULT["pin_mask", "dim"] = 1
+
+	DEFAULT["pin_drill"] = 3937
+	DEFAULT["pin_drill", "dim"] = 1
+
+	DEFAULT["line_thickness"] = 1000
+	DEFAULT["line_thickness", "dim"] = 1
+
+	DEFAULT["pad_thickness"] = 2000
+	DEFAULT["pad_thickness", "dim"] = 1
+	DEFAULT["pad_clearance"] = 1000
+	DEFAULT["pad_clearance", "dim"] = 1
+	DEFAULT["pad_mask"] = 3000
+	DEFAULT["pad_mask", "dim"] = 1
+
+	DEFAULT["pin_flags"] = "__auto"
+
+	s_default=1
+	s_weak=2
+	s_explicit=3
+	
+	offs_x = 0
+	offs_y = 0
+
+	pi=3.141592654
+}
+
+# Throw an error and exit
+function error(msg)
+{
+	print "Error: " msg > "/dev/stderr"
+	exit 1
+}
+
+# return a if it is not empty, else return b
+function either(a, b)
+{
+	return a != "" ? a : b
+}
+
+# strip leading/trailing whitespaces
+function strip(s)
+{
+	sub("^[ \t\r\n]*", "", s)
+	sub("[ \t\r\n]*$", "", s)
+	return s
+}
+
+# translate coordinates
+function coord_x(x) { return int(x + offs_x) }
+function coord_y(y) { return int(y + offs_y) }
+
+# generate an element header line; any argument may be empty
+function element_begin(desc, name, value, cent_x, cent_y, text_x, text_y, text_dir, text_scale)
+{
+	if (desc == "") {
+		desc = gen "(" args ")"
+		gsub("[\r\n\t ]*[?][^,]*,[\r\n\t ]*", "", desc)
+		gsub("[\r\n\t]", " ", desc)
+	}
+	print "Element[" q q, q desc q, q name q, q value q, 
+	int(either(cent_x, 0)), int(either(cent_y, 0)),
+	int(either(text_x, 0)), int(either(text_y, 0)),
+	int(either(text_dir, 0)), int(either(text_scale, 100)), q q "]"
+	print "("
+}
+
+# generate an element footer line
+function element_end()
+{
+	print ")"
+}
+
+# generate a pin; arguments from ringdia are optional (defaults are in global vars pin_*)
+function element_pin(x, y,  number, flags,   ringdia, clearance, mask, drill, name)
+{
+	if (number == "")
+		number = ++pin_number
+
+	flags = either(flags, DEFAULT["pin_flags"])
+
+	if (flags == "__auto") {
+		if (number == 1)
+			flags = "square"
+		else
+			flags = ""
+	}
+
+	print "	Pin[" coord_x(x), coord_y(y),
+		int(either(ringdia, DEFAULT["pin_ringdia"])), int(either(clearance, DEFAULT["pin_clearance"])), int(either(mask, DEFAULT["pin_mask"])),
+		int(either(drill, DEFAULT["pin_drill"])), q name q, q number q, q flags q "]"
+}
+
+# draw element pad
+function element_pad(x1, y1, x2, y2, thickness,   number, flags,   clearance, mask, name)
+{
+	print "	Pad[", coord_x(x1), coord_y(y1), coord_x(x2), coord_y(y2), int(either(thickness, DEFAULT["pad_thickness"])),
+		int(either(clearance, DEFAULT["pad_clearance"])), int(either(mask, DEFAULT["pad_mask"])),
+		q name q, q number q, q flags q "]"
+}
+
+# draw element pad - no thickness, but exact corner coordinates given
+function element_pad_rectangle(x1, y1, x2, y2,   number, flags,   clearance, mask, name,     th,dx,dy,cx,cy)
+{
+	if (x2 < x1) {
+		th = x2
+		x2 = x1
+		x1 = th
+	}
+	if (y2 < y1) {
+		th = y2
+		y2 = y1
+		y1 = th
+	}
+
+	dx = x2-x1
+	dy = y2-y1
+
+	if (dx >= dy) {
+		th = dy
+		cy = (y1+y2)/2
+
+		print "	Pad[", coord_x(x1)+th/2, coord_y(cy), coord_x(x2)-th/2, coord_y(cy), th,
+			int(either(clearance, DEFAULT["pad_clearance"])), int(either(mask, DEFAULT["pad_mask"])),
+			q name q, q number q, q flags q "]"
+	}
+	else {
+		th = dx
+		cx = (x1+x2)/2
+
+		print "	Pad[", coord_x(cx), coord_y(y1)+th/2, coord_x(cx), coord_y(y2)-th/2, th,
+			int(either(clearance, DEFAULT["pad_clearance"])), int(either(mask, DEFAULT["pad_mask"])),
+			q name q, q number q, q flags q "]"
+	}
+}
+
+# draw element pad circle
+function element_pad_circle(x1, y1, radius,   number,  clearance, mask, name)
+{
+	print "	Pad[", coord_x(x1), coord_y(y1), coord_x(x1), coord_y(y1), int(either(radius, DEFAULT["pad_thickness"])),
+		int(either(clearance, DEFAULT["pad_clearance"])), int(either(mask, DEFAULT["pad_mask"])),
+		q name q, q number q, q "" q "]"
+}
+
+# draw a line on silk; thickness is optional (default: line_thickness)
+function element_line(x1, y1, x2, y2,   thickness)
+{
+	print "	ElementLine[" coord_x(x1), coord_y(y1), coord_x(x2), coord_y(y2), int(either(thickness, DEFAULT["line_thickness"])) "]"
+}
+
+function element_arrow(x1, y1, x2, y2,  asize,  thickness   ,vx,vy,nx,ny,len,xb,yb)
+{
+	element_line(x1, y1, x2,y2, thickness)
+
+	if (asize == 0)
+		asize = mil(20)
+
+	vx = x2-x1
+	vy = y2-y1
+	len = sqrt(vx*vx+vy*vy)
+	if (len != 0) {
+		vx /= len
+		vy /= len
+		nx = vy
+		ny = -vx
+		xb = x2 - vx*asize
+		yb = y2 - vy*asize
+#		element_line(x2, y2, xb + 1000, yb + 1000)
+		element_line(x2, y2, xb + nx*asize/2, yb + ny*asize/2)
+		element_line(x2, y2, xb - nx*asize/2, yb - ny*asize/2)
+		element_line(xb - nx*asize/2, yb - ny*asize/2, xb + nx*asize/2, yb + ny*asize/2)
+	}
+}
+
+# draw a rectangle of silk lines 
+# omit sides as requested in omit
+# if r is non-zero, round corners - omit applies as NW, NW, SW, SE
+# if omit includes "arc", corners are "rounded" with lines
+function element_rectangle(x1, y1, x2, y2,    omit,  r, thickness   ,tmp,r1,r2)
+{
+# order coords for round corners
+	if (x1 > x2) {
+		tmp = x1
+		x1 = x2
+		x2 = tmp
+	}
+	if (y1 > y2) {
+		tmp = y1
+		y1 = y2
+		y2 = tmp
+	}
+
+	if (!(omit ~ "left")) {
+		r1 = (omit ~ "NW") ? 0 : r
+		r2 = (omit ~ "SW") ? 0 : r
+		element_line(x1, y1+r1, x1, y2-r2, thickness)
+	}
+	if (!(omit ~ "top")) {
+		r1 = (omit ~ "NW") ? 0 : r
+		r2 = (omit ~ "NE") ? 0 : r
+		element_line(x1+r1, y1, x2-r2, y1, thickness)
+	}
+	if (!(omit ~ "bottom")) {
+		r1 = (omit ~ "SE") ? 0 : r
+		r2 = (omit ~ "SW") ? 0 : r
+		element_line(x2-r1, y2, x1+r2, y2, thickness)
+	}
+	if (!(omit ~ "right")) {
+		r1 = (omit ~ "SE") ? 0 : r
+		r2 = (omit ~ "NE") ? 0 : r
+		element_line(x2, y2-r1, x2, y1+r2, thickness)
+	}
+
+	if (r > 0) {
+		if (omit ~ "arc") {
+			if (!(omit ~ "NW"))
+				element_line(x1, y1+r, x1+r, y1)
+			if (!(omit ~ "SW"))
+				element_line(x1, y2-r, x1+r, y2)
+			if (!(omit ~ "NE"))
+				element_line(x2, y1+r, x2-r, y1)
+			if (!(omit ~ "SE"))
+				element_line(x2, y2-r, x2-r, y2)
+		}
+		else {
+			if (!(omit ~ "NW"))
+				element_arc(x1+r, y1+r, r, r, 270, 90)
+			if (!(omit ~ "SW"))
+				element_arc(x1+r, y2-r, r, r, 0, 90)
+			if (!(omit ~ "NE"))
+				element_arc(x2-r, y1+r, r, r, 180, 90)
+			if (!(omit ~ "SE"))
+				element_arc(x2-r, y2-r, r, r, 90, 90)
+		}
+	}
+}
+
+# draw a rectangle corners of silk lines, wx and wy long in x and y directions
+# omit sides as requested in omit: NW, NW, SW, SE
+# corners are always sharp
+function element_rectangle_corners(x1, y1, x2, y2, wx, wy,    omit,  thickness   ,tmp)
+{
+	if (!(omit ~ "NW")) {
+		element_line(x1, y1,     x1+wx, y1,   thickness)
+		element_line(x1, y1,     x1, y1+wy,   thickness)
+	}
+	if (!(omit ~ "NE")) {
+		element_line(x2-wx, y1,  x2, y1,    thickness)
+		element_line(x2, y1,     x2, y1+wy, thickness)
+	}
+	if (!(omit ~ "SW")) {
+		element_line(x1, y2,     x1+wx, y2, thickness)
+		element_line(x1, y2-wy,  x1, y2,    thickness)
+	}
+	if (!(omit ~ "SE")) {
+		element_line(x2-wx, y2,  x2, y2,   thickness)
+		element_line(x2, y2-wy,  x2, y2,   thickness)
+	}
+}
+
+# draw a line on silk; thickness is optional (default: line_thickness)
+function element_arc(cx, cy, rx, ry, a_start, a_delta,   thickness)
+{
+	print "	ElementArc[" coord_x(cx), coord_y(cy), int(rx), int(ry), int(a_start), int(a_delta), int(either(thickness, DEFAULT["line_thickness"])) "]"
+}
+
+
+# convert coord given in mils to footprint units
+function mil(coord)
+{
+	return coord * 100
+}
+
+# reverse mil(): converts footprint units back to mil
+function rev_mil(coord)
+{
+	return coord/100
+}
+
+
+# convert coord given in mm to footprint units
+function mm(coord)
+{
+	return coord*3937
+}
+
+# reverse mm(): converts footprint units back to mm
+function rev_mm(coord)
+{
+	return coord/3937
+}
+
+
+function set_arg_(OUT, key, value, strength)
+{
+	if (OUT[key, "strength"] > strength)
+		return
+
+	OUT[key] = value
+	OUT[key, "strength"] = strength
+}
+
+# set parameter key=value with optioal strength (s_* consts) in array OUT[]
+# set only if current strength is stronger than the original value
+# if key starts with a "?", use s_weak
+# if key is in DEFAULT[], use DEFAULT[] instead of OUT[]
+function set_arg(OUT, key, value     ,strength)
+{
+	if (strength == "")
+		strength = s_explicit
+	if (key ~ "^[?]") {
+		sub("^[?]", "", key)
+		strength = s_weak
+	}
+
+	if (key in DEFAULT) {
+		if (DEFAULT[key, "dim"]) {
+			if (value ~ "mm")
+				value = mm(value)
+			else
+				value = mil(value)
+		}
+		set_arg_(DEFAULT, key, value, strength)
+	}
+	else
+		set_arg_(OUT, key, value, strength)
+}
+
+# Process a generator argument list from arg_names. Save the result in
+# array OUT. If mandatory is specified, check whether all mandatory
+# parameters are specified
+# Both arg_names and mandatory are comma separated list of argument names
+function proc_args(OUT, arg_names,   mandatory,  N,A,M,v,n,key,val,pos)
+{
+	gsub(" ", "", arg_names)
+	gsub(" ", "", mandatory)
+	split(arg_names, N, ",")
+	v = split(args, A, ",")
+
+# fill in all named and positional arguments
+	pos = 1
+	for(n = 1; n <= v; n++) {
+		A[n] = strip(A[n])
+		if (A[n] == "")
+			continue
+		if (A[n] ~ "=") {
+#			named
+			key=A[n]
+			val=A[n]
+			sub("=.*", "", key)
+			sub("^[^=]*=", "", val)
+			set_arg(OUT, key, val, s_explicit)
+		}
+		else {
+#			positional
+			if (N[pos] == "") {
+				error("too many positional arguments at " A[n])
+			}
+			while((N[pos] in OUT) && (N[pos, "strength"] == s_explicit)) pos++
+			set_arg(OUT, N[pos], A[n], s_explicit)
+			pos++
+		}
+	}
+
+# check whether all mandatory arguments are specified
+	v = split(mandatory, M, ",")
+	for(n = 1; n <= v; n++) {
+		if (!(M[n] in OUT)) {
+			error("missing argument " M[n] " (or positional " n ")")
+			exit 1
+		}
+	}
+}
+
+# Assume h is a dimension; if it has an "mm" suffix, convert it from mm,
+# else convert it from mil.
+function parse_dim(h)
+{
+	if (h == "")
+		return ""
+	if (h ~ "mm") {
+		sub("mm", "", h)
+		return mm(h)
+	}
+	return mil(h)
+}
+
+# Draw a DIP outline: useful for any rectangular package with a little
+# half circle centered on the top line
+#  arcr: radius of the half circle
+#  xhalf: optional coordinate where the circle should be put
+function dip_outline(x1, y1, x2, y2, arcr   ,xhalf)
+{
+	if (xhalf == "")
+		xhalf=(x1+x2)/2
+
+	element_rectangle(x1, y1, x2, y2, "top")
+	element_line(x1, y1, xhalf-arcr, y1)
+	element_line(xhalf+arcr, y1, x2, y1)
+
+	element_arc(xhalf, y1,  arcr, arcr,  0, 180)
+}
+
+# decide whether x is true or false
+# returns 1 if true
+# returns 0 if false
+function tobool(x)
+{
+	if (x == int(x))
+		return (int(x) != 0)
+
+	x = tolower(x)
+	return (x == "true") || (x == "yes") || (x == "on")
+}
+
+# default pin1 mark on a box
+# style: mark style, ":" separated list
+# x,y: the coordinates of the origin corner (top-left)
+# half: half the stepping of the pin grid - the size of the mark
+# step: optional size of the external arrow or square (defaults to 2*half)
+function silkmark(style, x, y, half,    step,   S,n,v)
+{
+	if (step == "")
+	step = half*2
+
+	v = split(style, S, ":")
+
+	for(n = 1; n <= v; n++) {
+		if (S[n] == "angled") {
+			element_line(x+half, y,  x, y+half)
+		}
+		else if (S[n] == "square") {
+			element_line(x, y+step,  x+2*half, y+step)
+			element_line(x+step, y,  x+2*half, y+step)
+		}
+		else if ((S[n] == "external") || (S[n] == "externalx")) {
+			element_line(x, y+half,         x-step+half, y+half/2)
+			element_line(x, y+half,         x-step+half, y+half*1.5)
+			element_line(x-step+half, y+half/2,         x-step+half, y+half*1.5)
+		}
+		else if (S[n] == "externaly") {
+			element_line(x+half, y,         x-half/2+half, y-step+half)
+			element_line(x+half, y,         x+half/2+half, y-step+half)
+			element_line(x-half/2+half, y-step+half,  x+half/2+half, y-step+half)
+		}
+		else if (S[n] == "external45") {
+			element_line(x, y,       x-half, y-half/3)
+			element_line(x, y,       x-half/3, y-half)
+			element_line(x-half, y-half/3, x-half/3, y-half)
+		}
+		else if (S[n] == "arc") {
+			element_arc(x, y, step/2, step/2, 180, 270)
+		}
+		else if (S[n] == "circle") {
+			element_arc(x, y, step/2, step/2, 0, 360)
+		}
+		else if (S[n] == "dot") {
+			element_arc(x-step/2, y-step/2, step/4, step/4, 0, 360)
+		}
+		else if ((S[n] != "none") && (S[n] != "")) {
+			error("invalid silkmark parameter: " S[n])
+		}
+	}
+}
+
+# output a dimension specification between x1;y1 and x2;y2, text distance at dist
+# for a name,value pair
+# if name is empty, only value is printed
+# if value is empty, it's calculated
+# if only name should be printed, value should be "!"
+# if dist starts with a "@", it's the absolute coordinate of the center of the dim line (text base), else it's relative distance from the measured line
+function dimension(x1, y1, x2, y2, dist, name,    value,    vx,vy)
+{
+	print "#dimension", coord_x(x1), coord_y(y1), coord_x(x2), coord_y(y2), dist, name, value
+}
diff --git a/pcblib/parametric/connector b/pcblib/parametric/connector
new file mode 100755
index 0000000..e2873be
--- /dev/null
+++ b/pcblib/parametric/connector
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+#@@example connector(2, 3, silkmark=external)
+
+#@@purpose Generate pin-array connectors (e.g. headers).
+
+#@@desc Generate thru-hole connectors that consits of an array of
+#@@desc evenly spaced pins, a plain frame and some optional pin marks
+#@@desc on the silk layer.
+
+#@@params nx, ny, spacing, silkmark, eshift, etrunc
+
+#@@param:nx number of pins in the X direction
+#@@param:ny number of pins in the Y direction
+#@@param:spacing spacing between the pins
+#@@dim:spacing
+#@@default:spacing 100 mil
+
+#@@include silkmark.help
+#@@optional:silkmark
+#@@default:silkmark square
+#@@preview_args:silkmark 2,3
+
+#@@param:eshift shift even rows or columns by half spacing (optional; default: don't shift)
+#@@enum:eshift:x shift columns
+#@@enum:eshift:y shift rows
+#@@enum:eshift:none do not shift anything
+#@@default:eshift none
+#@@optional:eshift
+#@@preview_args:eshift 2,3
+
+#@@param:etrunc truncate the last pin of a shifted row or column
+#@@bool:etrunc
+#@@default:etrunc false
+#@@optional:etrunc
+#@@preview_args:etrunc 2,3,eshift=x
+
+#@@param:sequence pin numbering sequence
+#@@enum:sequence:normal increase by y first, then by x
+#@@enum:sequence:pivot increase by x first, then by y
+#@@enum:sequence:zigzag "dip-style" numbering in zig-zag: number odd x rows by y ascending, even x rows by y descending
+#@@preview_args:sequence 2,4
+#@@thumbsize:sequence 3
+#@@thumbnum:sequence 1
+#@@default:sequence normal
+#@@optional:sequence
+
+
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/connector.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/connector.awk b/pcblib/parametric/connector.awk
new file mode 100644
index 0000000..598e649
--- /dev/null
+++ b/pcblib/parametric/connector.awk
@@ -0,0 +1,87 @@
+BEGIN {
+	set_arg(P, "?spacing", 100)
+	set_arg(P, "?silkmark", "square")
+	set_arg(P, "?sequence", "normal")
+
+	proc_args(P, "nx,ny,spacing,silkmark,eshift,etrunc", "nx,ny")
+
+
+	step = parse_dim(P["spacing"])
+
+	if (pin_ringdia > step*0.9)
+		pin_ringdia = step*0.9
+
+	if (pin_drill > pin_ringdia*0.9)
+		pin_drill = pin_ringdia*0.9
+
+	half=step/2
+
+	P["nx"] = int(P["nx"])
+	P["ny"] = int(P["ny"])
+
+	eshift=tolower(P["eshift"])
+	etrunc=tobool(P["etrunc"])
+	if ((eshift != "x") && (eshift != "y") && (eshift != "") && (eshift != "none"))
+		error("eshift must be x or y or none (got: " eshift ")");
+
+	element_begin("", "CONN1", P["nx"] "*" P["ny"]    ,0,0, 0, -step)
+
+	for(x = 0; x < P["nx"]; x++) {
+		if ((eshift == "x") && ((x % 2) == 1))
+			yo = step/2
+		else
+			yo = 0
+		for(y = 0; y < P["ny"]; y++) {
+			if ((eshift == "y") && ((y % 2) == 1)) {
+				xo = step/2
+				if ((etrunc) && (x == P["nx"]-1))
+					continue
+			}
+			else {
+				xo = 0
+				if ((etrunc) && (y == P["ny"]-1) && (yo != 0))
+					continue
+			}
+			if (P["sequence"] == "normal") {
+				pinno++
+			}
+			else if (P["sequence"] == "pivot") {
+				pinno = y * P["nx"] + x + 1
+			}
+			else if (P["sequence"] == "zigzag") {
+				if (x % 2) {
+					pinno = (x+1) * P["ny"] - y
+					if ((etrunc) && (eshift == "x"))
+						pinno -= int(x/2)+1
+					else if ((etrunc) && (eshift == "y"))
+						pinno += int(x/2)
+				}
+				else {
+					pinno = x * P["ny"] + y + 1
+					if ((etrunc) && (eshift == "x"))
+						pinno -= x/2
+					else if ((etrunc) && (eshift == "y"))
+						pinno += x/2-1
+				}
+			}
+			element_pin(x * step + xo, y * step + yo, pinno)
+		}
+	}
+
+	xo = 0
+	yo = 0
+	if (!etrunc) {
+		if (eshift == "y")
+			xo = step/2
+		if (eshift == "x")
+			yo = step/2
+	}
+
+	element_rectangle(-half, -half, P["nx"] * step - half + xo, P["ny"] * step - half + yo)
+
+	silkmark(P["silkmark"], -half, -half, half)
+
+	dimension(0, step, step, step, step*2, "spacing")
+
+	element_end()
+}
diff --git a/pcblib/parametric/dip b/pcblib/parametric/dip
new file mode 100755
index 0000000..d48e7ec
--- /dev/null
+++ b/pcblib/parametric/dip
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+#@@example dip(18)
+
+#@@purpose Generate classic DIP packages.
+
+#@@desc Generate thru-hole DIP packages with variable number of pins and
+#@@desc row spacing
+#@@params n, spacing
+
+#@@param:n number of pins
+
+#@@param:spacing spacing between the two rows of pins
+#@@dim:spacing
+#@@optional:spacing
+#@@default:spacing 100 mil
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/dip.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/dip.awk b/pcblib/parametric/dip.awk
new file mode 100644
index 0000000..a247a06
--- /dev/null
+++ b/pcblib/parametric/dip.awk
@@ -0,0 +1,27 @@
+BEGIN {
+	set_arg(P, "?spacing", 300)
+
+	proc_args(P, "n,spacing", "n")
+
+	P["n"] = int(P["n"])
+	if ((P["n"] < 2) || ((P["n"] % 2) != 0))
+		error("Number of pins have to be an even positive number")
+
+	spacing=parse_dim(P["spacing"])
+
+	element_begin("", "U1", P["n"] "*" P["spacing"]    ,0,0, 0, mil(-100))
+
+	half = mil(50)
+
+	for(n = 1; n <= P["n"]/2; n++) {
+		element_pin(0, (n-1) * mil(100), n)
+		element_pin(spacing, (n-1) * mil(100), P["n"] - n + 1)
+	}
+
+	dip_outline(-half, -half, spacing + half , (n-2) * mil(100) + half,  half)
+
+
+	dimension(0, 0, spacing, 0, mil(100), "spacing")
+
+	element_end()
+}
diff --git a/pcblib/parametric/msop b/pcblib/parametric/msop
new file mode 100755
index 0000000..f1b24ad
--- /dev/null
+++ b/pcblib/parametric/msop
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), MSOP
+
+#@@example msop(8)
+
+#@@purpose Generate MSOP packages.
+
+#@@desc Generate MSOP packages with variable number of pins and
+#@@desc row spacing
+
+#@@include so
+#@@include common.awk
+
+
+defaults='
+?pad_spacing=0.65mm,
+?row_spacing=4.9mm,
+?int_bloat=0.6mm,
+?ext_bloat=0.4mm,
+?rarc=25,
+?silk_ext_x=18,
+?silk_ext_y=22,
+?pad_thickness=0.45mm
+'
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/so.awk -v "args=$defaults,$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/plcc b/pcblib/parametric/plcc
new file mode 100755
index 0000000..9ba1a8d
--- /dev/null
+++ b/pcblib/parametric/plcc
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), SSOP
+
+#@@example plcc(20)
+
+#@@purpose Generate PLCC packages
+#@@desc Generate square PLCC packages - a simplified frontend to qf() - 
+#@@desc only the number of pins is really needed for a square PLCC footprint!
+#@@desc NOTE: some of the qf() parameters can be also used; PLCC32 is non-square, and can not be properly generated using this generator.
+
+#@@params pins,size,pitch,cpad_size
+
+#@@param:pin total number of pins (leads); must be divisible by 4
+
+#@@param:size a single integer N or NxN or NxNxH; outmost dimensions (width or height of the package, leads included) in mil
+#@@optional:size
+#@@default:size calculated from total number of pins and pitch
+
+#@@param:pitch lead pitch (distance between the centerline of two adjacent leads), in mil; must be 50 for now
+#@@optional:pitch
+#@@default:pitch 50mil
+
+#@@param:cpad_size width (and height) of the central pad, in mil
+#@@optional:cpad_size
+#@@default:cpad_size empty, there's no central pad
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/plcc.awk -f `dirname $0`/qf.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/plcc.awk b/pcblib/parametric/plcc.awk
new file mode 100644
index 0000000..2b4ed0c
--- /dev/null
+++ b/pcblib/parametric/plcc.awk
@@ -0,0 +1,58 @@
+function parri(A     ,s,i)
+{
+	for(i in A)
+		s = s " " i
+	return s
+}
+
+BEGIN {
+
+	PT["50"] = "26mil"
+	set_arg(P, "?pitch", "50")
+
+	proc_args(P, "pins,size,pitch,cpad_size,pad_thickness", "pins")
+
+	pitch = P["pitch"]
+	sub("[.]0*$", "", pitch)
+
+	if (!(args ~ "pad_thickness=")) {
+		if (!(pitch in PT))
+			error("Unknown pitch (" pitch "), should be one of:" parri(PT))
+		pt = PT[pitch]
+	}
+	else
+		pt = rev_mil(DEFAULT["pad_thickness"])
+
+	if (P["size"] == "")
+		P["size"] = int(P["pins"] * (pitch/4) + 140)
+
+	split(P["size"], S, "x")
+	if (S[2] == "")
+		S[2] = S[1]
+	if (S[1] != S[2])
+		error("need n*n size")
+
+	pins = int(P["pins"])
+	if (pins / 4 != int(pins / 4))
+		error("number of pins must be divisible by 4")
+
+	pins=pins/4
+
+	if ((pins % 2) != 1)
+		error("number of pins per side must be odd")
+
+	S[1] -= 60
+	S[2] -= 60
+	args = args ",nx=" pins ",ny=" pins ",x_spacing=" S[1] "mil,y_spacing=" S[2] "mil,pad_spacing=" pitch "mil,pad_thickness=" pt "mil"
+
+	args = args ",width=" S[1]-150 " mil,height=" S[2]-150 " mil"
+
+
+	if (P["cpad_size"] != "")
+		args = args ",cpad_width=" P["cpad_size"] "mil,cpad_height=" P["cpad_size"] "mil"
+
+	args = args ",int_bloat=47mil,ext_bloat=47mil"
+	args = args ",?bodysilk=plcc,?silkmark=circle,pinoffs=" int(pins/2+0.5)
+
+
+}
diff --git a/pcblib/parametric/qf b/pcblib/parametric/qf
new file mode 100755
index 0000000..d358492
--- /dev/null
+++ b/pcblib/parametric/qf
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+#@@example qf(nx=4,ny=4,cpad_auto=1)
+
+#@@purpose Generate generic *qf* (e.g. tqfp, qfn) packages
+
+#@@desc Generate *qf* (e.g. tqfp, qfn) packages: smd pads around a square; this is the generic implementation, end users may want to use the specific ones, e.g. tqfp(), qfn(), etc.
+#@@params nx,ny,x_spacing,y_spacing,pad_spacing,ext_bloat,int_bloat,width,height,cpad_width,cpad_height,cpad_auto,bodysilk,pinoffs,silkmark
+
+#@@param:nx number of pins along the horizontal sides
+
+#@@param:ny number of pins along the vertical sides
+#@@optional:ny
+#@@default:ny assume ny=nx
+
+#@@param:x_spacing spacing between the two vertical rows of pins
+#@@dim:x_spacing
+#@@optional:x_spacing
+#@@default:x_spacing calculated using nx and pad_spacing
+
+#@@param:y_spacing spacing between the two vertical rows of pins
+#@@dim:y_spacing
+#@@optional:y_spacing
+#@@default:y_spacing calculated using ny and pad_spacing
+
+#@@param:pad_spacing spacing between the centerlines of two adjacent pads (aka pitch)
+#@@dim:pad_spacing
+#@@optional:pad_spacing
+
+#@@param:ext_bloat how much longer the pad should extend outwards from the end-point of the pin
+#@@dim:ext_bloat
+#@@optional:ext_bloat
+#@@default:ext_bloat 0.37 mm
+
+#@@param:int_bloat how much longer the pad should extend inwards from the end-point of the pin
+#@@dim:int_bloat
+#@@optional:int_bloat
+#@@default:int_bloat 0.37 mm
+
+#@@param:width width (horizontal, x axis size) of the box drawn on silk
+#@@dim:width
+#@@optional:width
+#@@default:width calculated from the pad row geometry
+
+#@@param:height height (vertical, y axis size) of the box drawn on silk
+#@@dim:height
+#@@optional:height
+#@@default:height calculated from the pad row geometry
+
+#@@param:cpad_width width (horizontal, x axis size) of the central pad
+#@@dim:cpad_width
+#@@optional:cpad_width
+#@@default:cpad_width empty, no central pad
+
+#@@param:cpad_height height (vertical, y axis size) of the central pad
+#@@dim:cpad_height
+#@@optional:cpad_height
+#@@default:cpad_height empty, no central pad
+
+#@@param:cpad_auto when true, calculate central pad sizes automatically from the pad row geometry
+#@@bool:cpad_auto
+#@@optional:cpad_height
+#@@default:cpad_height false, no central pad
+
+#@@param:bodysilk how to draw body on the silk layer
+#@@enum:bodysilk:none no drawing
+#@@enum:bodysilk:corners draw the corners, silkmark is outside of the top-left corner
+#@@enum:bodysilk:full draw a full rectanlge, silkmark of the top-left corner
+#@@enum:bodysilk:plcc angled top-left corner, horizontal line on the top
+#@@optional:bodysilk
+#@@default:bodysilk corners
+
+#@@param:pinoffs rotate pins by this amount, CW
+#@@optional:pinoffs
+#@@default:pinoffs 0
+
+#@@include silkmark.help
+#@@optional:silkmark
+#@@default:silkmark square
+#@@preview_args:silkmark 4,4
+
+#@@default:silkmark dot
+#@@include common.awk
+
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/qf.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/qf.awk b/pcblib/parametric/qf.awk
new file mode 100644
index 0000000..0382345
--- /dev/null
+++ b/pcblib/parametric/qf.awk
@@ -0,0 +1,146 @@
+function qf_globals(pre_args, post_args    ,reqs)
+{
+	if (hook_spc_conv == "")
+		hook_spc_conv = 1.8
+	if (hook_cpad_mult == "")
+		hook_cpad_mult = 1
+
+	if (!qf_no_defaults) {
+		set_arg(P, "?pad_spacing", "0.5mm")
+		set_arg(P, "?ext_bloat", "0.37mm")
+		set_arg(P, "?int_bloat", "0.37mm")
+		set_arg(P, "?pad_thickness", "0.3mm")
+		set_arg(P, "?silkmark", "dot")
+		set_arg(P, "?line_thickness", "0.1mm")
+	}
+
+	reqs = "nx,ny"
+	
+	if (pre_args != "")
+		reqs=""
+
+	if ((post_args != "") && (!(post_args ~ "^,")))
+		post_args = "," post_args
+
+	if ((pre_args != "") && (!(pre_args ~ ",$")))
+		pre_args = pre_args ","
+
+	proc_args(P, pre_args "nx,ny,x_spacing,y_spacing,pad_spacing,ext_bloat,int_bloat,width,height,cpad_width,cpad_height,cpad_auto,bodysilk,pinoffs,silkmark" post_args, reqs)
+
+	nx = int(P["nx"])
+	ny = int(P["ny"])
+
+	if (P["ny"] == "")
+		ny = nx
+
+	if ((nx < 2) || (ny < 2))
+		error("Number of pins have to be more than 2 in both directions")
+
+	x_spacing=parse_dim(P["x_spacing"])
+	y_spacing=parse_dim(P["y_spacing"])
+	pad_spacing=parse_dim(P["pad_spacing"])
+	pt = DEFAULT["pad_thickness"]
+	ext_bloat=parse_dim(P["ext_bloat"]) - pt/2
+	int_bloat=parse_dim(P["int_bloat"]) - pt/2
+	width=parse_dim(P["width"])
+	height=parse_dim(P["height"])
+	pinoffs = int(P["pinoffs"])
+
+	if (x_spacing == "")
+		x_spacing = (nx+hook_spc_conv) * pad_spacing
+	if (y_spacing == "")
+		y_spacing = (ny+hook_spc_conv) * pad_spacing
+
+	cpad_width=parse_dim(P["cpad_width"])
+	cpad_height=parse_dim(P["cpad_height"])
+
+	if (tobool(P["cpad_auto"]) || hook_cpad_auto) {
+		if (cpad_width == "")
+			cpad_width = (x_spacing*0.85 - int_bloat*2 - pt) * hook_cpad_mult
+		if (cpad_height == "")
+			cpad_height = (y_spacing*0.85 - int_bloat*2 - pt) * hook_cpad_mult
+	}
+
+
+	if (width == "")
+		width = x_spacing
+	if (height == "")
+		height = y_spacing
+
+	pinmax=(nx+ny)*2
+}
+
+function pinnum(num)
+{
+	return ((num-1) + pinoffs) % (pinmax)+1
+}
+
+BEGIN {
+	qf_globals()
+
+
+	element_begin("", "U1", 2*nx + 2*ny   ,0,0, -width/2 - mm(1), -height/2 - mm(2))
+
+	cx = (nx+1)/2
+	cy = (ny+1)/2
+
+	for(n = 1; n <= ny; n++) {
+		y = (-cy + n) * pad_spacing
+		x1 = -x_spacing/2
+		x2 = x_spacing/2
+		element_pad(x1-ext_bloat, y, x1+int_bloat, y, pad_width, pinnum(n), "square")
+		element_pad(x2-int_bloat, y, x2+ext_bloat, y, pad_width, pinnum(nx+2*ny-n+1), "square")
+		if (n == 1)
+			y1 = y
+		if (n == 2)
+			dimension(x1, y1, x1, y, (ext_bloat * -3), "pad_spacing")
+	}
+
+	dimension(x1, y-pt/2, x1, y+pt/2, (ext_bloat * -3), "pad_thickness")
+
+	for(n = 1; n <= nx; n++) {
+		x = (-cx + n) * pad_spacing
+		y1 = -y_spacing/2
+		y2 = y_spacing/2
+		element_pad(x, y1-ext_bloat, x, y1+int_bloat, pad_width, pinnum(nx*2+ny*2-n+1), "square")
+		element_pad(x, y2-int_bloat, x, y2+ext_bloat, pad_width, pinnum(n+ny), "square")
+	}
+
+
+	if ((cpad_width != "") && (cpad_height != "")) {
+		element_pad_rectangle(-cpad_width/2, -cpad_height/2, +cpad_width/2, +cpad_height/2, 2*nx+2*ny+1, "square")
+		dimension(-cpad_width/2, -cpad_height/2, +cpad_width/2, -cpad_height/2, "@0;" (height * -0.6-ext_bloat), "cpad_width")
+		dimension(cpad_width/2, -cpad_height/2, cpad_width/2, +cpad_height/2, "@" (width * 0.8+ext_bloat) ";0", "cpad_height")
+	}
+
+	wx = (width  - nx * pad_spacing) / 3.5
+	wy = (height - ny * pad_spacing) / 3.5
+
+	bodysilk = P["bodysilk"]
+	if ((bodysilk == "corners") || (bodysilk == "")) {
+		element_rectangle_corners(-width/2, -height/2, width/2, height/2, wx, wy)
+		silkmark(P["silkmark"], -width/2 - wx/2, -height/2+wy*1.5, (wx+wy)/4)
+	}
+	else if (bodysilk == "full") {
+		element_rectangle(-width/2, -height/2, width/2, height/2)
+		silkmark(P["silkmark"], -width/2 + wx*3, -height/2+wy*3, (wx+wy)/2)
+	}
+	else if (bodysilk == "plcc") {
+		r = (width+height)/10
+		element_rectangle(-width/2, -height/2, width/2, height/2, "arc,NE,SW,SE", r)
+		element_line(-width/2, -height/2+r, width/2, -height/2+r)
+		silkmark(P["silkmark"], 0, -height*0.2, height/40)
+	}
+	else if (bodysilk != "none")
+		error("invalid bodysilk parameter")
+
+	dimension(-width/2, -height/2, +width/2, -height/2, "@0;" height*-0.8-ext_bloat,       "width")
+	dimension(+width/2, -height/2, +width/2, +height/2, "@" (width * 1+ext_bloat) ";0",  "height")
+
+	dimension(-x_spacing/2, -height/2, +x_spacing/2, -height/2, "@0;" height*-1-ext_bloat,       "x_spacing")
+	dimension(+width/2, -y_spacing/2, +width/2, +y_spacing/2, "@" (width * 1.2+ext_bloat) ";0",  "y_spacing")
+
+
+
+	element_end()
+}
diff --git a/pcblib/parametric/qfn b/pcblib/parametric/qfn
new file mode 100755
index 0000000..bfb6e7c
--- /dev/null
+++ b/pcblib/parametric/qfn
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), SSOP
+
+#@@example qfn(16,3x3,0.5)
+
+#@@purpose Generate QFN packages
+#@@desc Generate QFN packages - a simplified frontend to qf() - 
+#@@desc look up 3..4 data in the datasheet and get a regular QFN footprint!
+#@@desc Should work for QFN, TQFN, etc.
+#@@desc NOTE: some of the qf() parameters can be also used.
+
+#@@params pins,size,pitch,cpad_size
+
+#@@param:pin total number of pins (leads); must be divisible by 4
+#@@param:size a single integer N or NxN or NxNxH; the pad spacing dimension (outer dimension, lead-to-lead) in mm
+#@@param:pitch lead pitch (distance between the centerline of two adjacent leads), in mm; must be one of 0.4, 0.5, 0.65
+
+#@@param:cpad_size width (and height) of the central pad, in mm
+#@@optional:cpad_size
+#@@default:cpad_size empty, there's no central pad
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/qfn.awk -f `dirname $0`/qf.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/qfn.awk b/pcblib/parametric/qfn.awk
new file mode 100644
index 0000000..48f0b34
--- /dev/null
+++ b/pcblib/parametric/qfn.awk
@@ -0,0 +1,50 @@
+function parri(A     ,s,i)
+{
+	for(i in A)
+		s = s " " i
+	return s
+}
+
+BEGIN {
+
+	PT["0.65"] = "0.35mm"
+	PT["0.5"] = "0.3mm"
+	PT["0.4"] = "0.2mm"
+
+	proc_args(P, "pins,size,pitch,cpad_size,pad_thickness", "pins,size,pitch")
+
+	pitch = P["pitch"]
+	sub("0*$", "", pitch)
+
+	if (!(args ~ "pad_thickness=")) {
+		if (!(pitch in PT))
+			error("Unknown pitch, should be one of:" parri(PT))
+		pt = PT[pitch]
+	}
+	else
+		pt = rev_mm(DEFAULT["pad_thickness"])
+
+	split(P["size"], S, "x")
+	if (S[2] == "")
+		S[2] = S[1]
+	if (S[1] != S[2])
+		error("need n*n size")
+
+	pins = int(P["pins"])
+	if (pins / 4 != int(pins / 4))
+		error("number of pins must be divisible by 4")
+
+	pins=pins/4
+
+
+	args = args ",nx=" pins ",ny=" pins ",x_spacing=" S[1] "mm,y_spacing=" S[2] "mm,pad_spacing=" pitch "mm,pad_thickness=" pt
+
+	if (P["cpad_size"] != "")
+		args = args ",cpad_width=" P["cpad_size"] "mm,cpad_height=" P["cpad_size"] "mm"
+
+
+	print "args=" args > "/dev/stderr"
+
+#	qf_globals("pins,size")
+
+}
diff --git a/pcblib/parametric/qfp b/pcblib/parametric/qfp
new file mode 100755
index 0000000..fe8d120
--- /dev/null
+++ b/pcblib/parametric/qfp
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), SSOP
+
+#@@example qfp(32,7x7,0.8)
+
+#@@purpose Generate QFP packages
+#@@desc Generate QFP packages - a simplified frontend to qf() - 
+#@@desc look up 3..4 data in the datasheet and get a regular QFP footprint!
+#@@desc NOTE: some of the qf() parameters can be also used.
+#@@desc Should work for QFP, TQFP, LQFP, etc.
+
+#@@params pins,size,pitch,cpad_size
+
+#@@param:pin total number of pins (leads); must be divisible by 4
+#@@param:size a single integer N or NxN or NxNxH; body dimension (width or height of the plastic body) in mm
+#@@param:pitch lead pitch (distance between the centerline of two adjacent leads), in mm; must be one of 0.4, 0.5, 0.65
+
+#@@param:cpad_size width (and height) of the central pad, in mm
+#@@optional:cpad_size
+#@@default:cpad_size empty, there's no central pad
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/qfp.awk -f `dirname $0`/qf.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/qfp.awk b/pcblib/parametric/qfp.awk
new file mode 100644
index 0000000..1ffcfb2
--- /dev/null
+++ b/pcblib/parametric/qfp.awk
@@ -0,0 +1,52 @@
+function parri(A     ,s,i)
+{
+	for(i in A)
+		s = s " " i
+	return s
+}
+
+BEGIN {
+
+	PT["0.8"] = "0.55mm"
+	PT["0.65"] = "0.35mm"
+	PT["0.5"] = "0.3mm"
+	PT["0.4"] = "0.2mm"
+
+	proc_args(P, "pins,size,pitch,cpad_size,pad_thickness", "pins,size,pitch")
+
+	pitch = P["pitch"]
+	sub("0*$", "", pitch)
+
+	if (!(args ~ "pad_thickness=")) {
+		if (!(pitch in PT))
+			error("Unknown pitch, should be one of:" parri(PT))
+		pt = PT[pitch]
+	}
+	else
+		pt = rev_mm(DEFAULT["pad_thickness"])
+
+	split(P["size"], S, "x")
+	if (S[2] == "")
+		S[2] = S[1]
+	if (S[1] != S[2])
+		error("need n*n size")
+
+	pins = int(P["pins"])
+	if (pins / 4 != int(pins / 4))
+		error("number of pins must be divisible by 4")
+
+	pins=pins/4
+
+	args = args ",width=" S[1] " mm,height=" S[2] " mm"
+
+	S[1] += 1.45
+	S[2] += 1.45
+	args = args ",nx=" pins ",ny=" pins ",x_spacing=" S[1] "mm,y_spacing=" S[2] "mm,pad_spacing=" pitch "mm,pad_thickness=" pt "mm"
+
+
+	if (P["cpad_size"] != "")
+		args = args ",cpad_width=" P["cpad_size"] "mm,cpad_height=" P["cpad_size"] "mm"
+
+	args = args ",int_bloat=0.5mm,ext_bloat=1.1mm"
+	args = args ",?bodysilk=full,?silkmark=circle"
+}
diff --git a/pcblib/parametric/qsop b/pcblib/parametric/qsop
new file mode 100755
index 0000000..420a5eb
--- /dev/null
+++ b/pcblib/parametric/qsop
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), QSOP
+
+#@@example qsop(14)
+
+#@@purpose Generate QSOP packages.
+
+#@@desc Generate QSOP packages with variable number of pins and
+#@@desc row spacing
+
+#@@include so
+#@@include common.awk
+
+defaults='
+?pad_spacing=25mil,
+?row_spacing=213mil,
+?int_bloat=22mil,
+?ext_bloat=23mil,
+?rarc=25,
+?silk_ext_x=18,
+?silk_ext_y=22,
+?pad_thickness=16mil
+'
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/so.awk -v "args=$defaults,$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/rcy b/pcblib/parametric/rcy
new file mode 100755
index 0000000..9c7435c
--- /dev/null
+++ b/pcblib/parametric/rcy
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#@@example rcy(300,bar+)
+
+#@@purpose Generate radial lead through-hole component
+
+#@@desc Generate radial lead through-hole component with 2 pins (typical use: electrolytic caps)
+#@@params spacing,pol,dia
+
+#@@param:spacing spacing between the two pins
+#@@dim:spacing
+
+#@@param:pol how to mark polarity:  (optional; default: sign)
+#@@enum:pol:none no marking
+#@@enum:pol:sign print + next to pin 1 and - next to pin 2
+#@@enum:pol:bar+ draw a bar inside the circle, around pin 2
+#@@enum:pol:bar- draw a bar inside the circle, around pin 1
+#@@enum:pol:bar same as bar+
+#@@enum:pol:bar+sign bar+ and sign combined
+#@@enum:pol:bar-sign bar- and sign combined
+#@@optional:pol
+#@@default:pol sign
+#@@preview_args:pol 300
+
+#@@param:dia body diameter - affects the silk circle
+#@@optional:dia
+#@@default:dia spacing*2
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/rcy.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/rcy.awk b/pcblib/parametric/rcy.awk
new file mode 100644
index 0000000..e33daef
--- /dev/null
+++ b/pcblib/parametric/rcy.awk
@@ -0,0 +1,93 @@
+function pol_sign(   ox,oy)
+{
+	ox = offs_x
+	
+		size=mil(20)
+
+		offs_x = spacing/2 +dia/2+size*2
+		element_line(-size, 0, +size, 0)
+
+		offs_x = spacing/2 -dia/2-size*2
+		element_line(-size, 0, +size, 0)
+		element_line(0, -size, 0, +size)
+
+	offs_x = ox
+}
+
+BEGIN {
+	
+	set_arg(P, "?pol", "sign")
+	proc_args(P, "spacing,pol,dia", "spacing")
+
+	spacing = parse_dim(P["spacing"])
+	dia = either(parse_dim(P["dia"]), spacing*2)
+
+	offs_x = +spacing/2
+
+	element_begin("", "C1", "acy" P["spacing"]    ,0,0, spacing/2-spacing/5,-mil(20))
+
+	element_pin(-spacing/2, 0, 1)
+	element_pin(+spacing/2, 0, 2)
+	dimension(-spacing/2, 0, +spacing/2, 0, dia*0.8, "spacing")
+
+
+# silk rectangle and pins
+	element_arc(0, 0, dia/2, dia/2, 0, 360)
+	dimension(-dia/2, 0, +dia/2, 0, dia*-0.8, "dia")
+
+
+	if (P["pol"] == "sign") {
+		pol_sign()
+	}
+	else if (P["pol"] ~ "^bar") {
+# determine bar side (default to -)
+		side=P["pol"]
+		sub("^bar", "", side)
+		if (side ~ "sign") {
+			pol_sign()
+			sub("sign", "", side)
+		}
+		if (side == "")
+			side = "-"
+		side = int(side "1") * -1
+
+		th = mm(1)
+		ystep = th/2
+		r = dia/2-th/2
+		xs = dia/8
+		ring = DEFAULT["pin_ringdia"]
+		for(y = 0; y < dia/2; y+=ystep) {
+			x = r*r-y*y
+			if (x < 0)
+				break
+			x = sqrt(x)
+			if (x <= xs)
+				break
+			if (y > ring/2+th/2) {
+				element_line(side*xs, y, side*x, y, th)
+				if (y != 0)
+					element_line(side*xs, -y, side*x, -y, th)
+			}
+			else {
+# keep out a rectangle around the pin
+				end1=spacing/2-ring
+				end2=spacing/2+ring
+				if (end1 > xs)
+					element_line(side*xs, y, side*end1, y, th)
+				if (end2 < x)
+					element_line(side*end2, y, side*x, y, th)
+				if (y != 0) {
+					if (end1 > xs)
+						element_line(side*xs, -y, side*end1, -y, th)
+					if (end2 < x)
+						element_line(side*end2, -y, side*x, -y, th)
+				}
+			}
+		}
+	}
+	else if ((P["pol"] != "") && (P["pol"] != "none")) {
+		error("Invalid pol")
+	}
+
+	element_end()
+}
diff --git a/pcblib/parametric/screw b/pcblib/parametric/screw
new file mode 100755
index 0000000..3469f86
--- /dev/null
+++ b/pcblib/parametric/screw
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+#@@example screw(3.2mm, 6mm, circle:hex)
+
+#@@purpose Generic screw.
+
+#@@desc Generate a "single pin" screw with optional head shapes
+#@@params hole,head,shape,ring
+
+#@@param:hole hole diameter or name of a standard screw (e.g. M3)
+#@@dim:hole
+
+
+#@@param:head head outmost diameter (or optional head name for a standard screw: pan, button, cheese, flat-washer, internal-lock-washer)
+#@@dim:head
+
+#@@param:ring copper ring outer diameter
+#@@dim:ring
+#@@optional:ring
+#@@default:ring 80% of head diameter
+
+
+#@@param:shape shape of the head drawn on silkmark; multiple values can be listed separated with colons, e.g. "shape=circle:hex"
+#@@enum:shape:circle circle
+#@@enum:shape:hex hexagon with straight line edges, size is from corner to corner
+#@@enum:shape:tx "torx": hexagon with arced edges
+#@@enum:shape:xzn "triple square"
+#@@enum:shape:ph philips slot (cross) - useful together with circle
+#@@enum:shape:slot a single straight line slot - useful together with circle
+#@@default:shape circle
+#@@preview_args:shape 3mm,6mm
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/screw.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/screw.awk b/pcblib/parametric/screw.awk
new file mode 100644
index 0000000..dd2cf53
--- /dev/null
+++ b/pcblib/parametric/screw.awk
@@ -0,0 +1,127 @@
+function shp(r, edges, tx      ,a,x,y,xl,yl,step,x1,y1,x2,y2,tx1,tx2,txs)
+{
+	step = 2*3.141592654/edges
+	if (tx == 1) {
+		tx1 = 0.7
+		tx2 = 0.6
+		txs = 5
+	}
+	else if (tx == 2) {
+		tx1 = 0.85
+		tx2 = 0.8
+		txs = 5
+	}
+	else if (tx == 3) {
+		tx1 = 0.2
+		tx2 = 0.2
+		txs = 3
+	}
+	for(n = 0; n <= edges; n++) {
+		a += step
+		x = cos(a)*r
+		y = sin(a)*r
+		if (xl != "") {
+			if (tx) {
+				x1 = cos(a-(txs-1)*step/txs)*r*tx1
+				y1 = sin(a-(txs-1)*step/txs)*r*tx1
+				x2 = cos(a-step/txs)*r*tx1
+				y2 = sin(a-step/txs)*r*tx1
+				x3 = cos(a-step/2)*r*tx2
+				y3 = sin(a-step/2)*r*tx2
+				element_line(xl, yl, x1, y1)
+				element_line(x, y,   x2, y2)
+				element_line(x3, y3, x1, y1)
+				element_line(x3, y3, x2, y2)
+			}
+			else
+				element_line(xl, yl, x, y)
+		}
+		xl = x
+		yl = y
+	}
+}
+
+function round_up(num, to)
+{
+	if ((num/to) == int(num/to))
+		return num
+	return int(num/to+1)*to
+}
+
+BEGIN {
+	set_arg(P, "?shape", "circle")
+	proc_args(P, "hole,head,shape,ring", "hole")
+
+	element_begin("", "S1", "screw:" P["hole"] "," P["head"]"," P["shape"]    ,0,0, 0, mil(-100))
+
+	if (P["hole"] ~ "^M") {
+		hole = P["hole"]
+		sub("^M", "", hole)
+		h = parse_dim(int(hole) "mm")
+		if ((hole ~ "tight") || (hole ~ "close.fit"))
+			hole = h * 1.05
+		else
+			hole = h * 1.1
+		hd = parse_dim(P["head"])
+		if ((hd == 0) || (hd == "")) {
+			hd = P["head"]
+			if (hd == "button")
+				head = 1.9*h 
+			else if (hd == "button")
+				head = 1.9*h 
+			else if (hd == "cheese")
+				head = round_up(1.7*h, mm(0.5))
+			else if (hd ~ "flat.washer")
+				head = round_up(2.1*h, mm(1))
+			else if ((hd == "") || (hd == "pan") || (hd ~ "int.*.lock.washer"))
+				head = 2*h
+			else
+				error("Unknown standard head: " hd)
+		}
+		else
+			head = hd
+#		print hole, head > "/dev/stderr"
+	}
+	else {
+		hole = parse_dim(P["hole"])
+		head = parse_dim(P["head"])
+	}
+
+	if (head == "")
+		error("need a standard screw name, e.g. M3, or a head diameter")
+
+	if (head < hole)
+		error("head diameter must be larger than hole diameter")
+
+	ring = parse_dim(P["ring"])
+
+	if (ring == "")
+		ring = head*0.8
+
+	element_pin(0, 0, 1, "none", ring, "", "", hole)
+
+	shape = ":" P["shape"] ":"
+
+	if (shape ~ ":circle:")
+		element_arc(0, 0, head/2, head/2, 0, 360)
+
+	if (shape ~ ":hex:")
+		shp(head/2, 6, 0)
+
+	if (shape ~ ":tx:")
+		shp(head/2, 6, 1)
+
+	if (shape ~ ":xzn:")
+		shp(head/2, 12, 2)
+
+	if (shape ~ ":ph:")
+		shp(head*0.4, 4, 3)
+
+	if (shape ~ ":slot:")
+		element_line(-head/2, 0, head/2, 0)
+
+	dimension(-head/2, 0, head/2, 0, head*0.7, "head")
+	dimension(-hole/2, 0, hole/2, 0, head*0.6, "hole")
+
+	element_end()
+}
diff --git a/pcblib/parametric/silkmark.help b/pcblib/parametric/silkmark.help
new file mode 100644
index 0000000..800cc35
--- /dev/null
+++ b/pcblib/parametric/silkmark.help
@@ -0,0 +1,11 @@
+#@@param:silkmark how to mark pin 1 on the silk layer; multiple values can be listed separated with colons, e.g. "silkmark=externalx:angled"
+#@@enum:silkmark:square a rectangle around pin 1
+#@@enum:silkmark:externalx a little trinagle placed outside of the box pointing in line with the first x row
+#@@enum:silkmark:externaly a little trinagle placed outside of the box pointing in line with the first y row
+#@@enum:silkmark:external45 a little trinagle placed outside of the box pointing at the corner in 45 degree
+#@@enum:silkmark:external shorthand for external
+#@@enum:silkmark:angled an angled line in the corner
+#@@enum:silkmark:arc an external 270 degree arc
+#@@enum:silkmark:circle a circle, 270 degrees external
+#@@enum:silkmark:dot a small external dot
+#@@enum:silkmark:none no mark
diff --git a/pcblib/parametric/so b/pcblib/parametric/so
new file mode 100755
index 0000000..db833a8
--- /dev/null
+++ b/pcblib/parametric/so
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+#@@example so(14)
+
+#@@purpose Generate SO and SO-like packages.
+
+#@@desc Generate SO packages with variable number of pins and
+#@@desc row spacing
+#@@params n, row_spacing, pad_spacing
+
+#@@param:n number of pins
+
+#@@param:row_spacing spacing between the two rows of pads: distance between the end of the first and last pins
+#@@dim:row_spacing
+#@@optional:row_spacing
+#@@default:row_spacing 250 mil
+
+
+#@@param:pad_spacing spacing between the centerline of two pads
+#@@dim:pad_spacing
+#@@optional:pad_spacing
+#@@default:pad_spacing 250 mil
+
+
+#@@param:ext_bloat how much longer the pad should extend outwards from the end-point of the pin
+#@@dim:ext_bloat
+#@@optional:ext_bloat
+#@@default:ext_bloat 10 mil
+
+#@@param:int_bloat how much longer the pad should extend inwards from the end-point of the pin
+#@@dim:int_bloat
+#@@optional:int_bloat
+#@@default:int_bloat 55 mil
+
+#@@include common.awk
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/so.awk -v "args=$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/so.awk b/pcblib/parametric/so.awk
new file mode 100644
index 0000000..d6174e9
--- /dev/null
+++ b/pcblib/parametric/so.awk
@@ -0,0 +1,50 @@
+BEGIN {
+	set_arg(P, "?row_spacing", 250)
+	set_arg(P, "?pad_spacing", 50)
+	set_arg(P, "?ext_bloat", 10)
+	set_arg(P, "?int_bloat", 55)
+
+	proc_args(P, "n,row_spacing,pad_spacing,ext_bloat,int_bloat", "n")
+
+	P["n"] = int(P["n"])
+	if ((P["n"] < 2) || ((P["n"] % 2) != 0))
+		error("Number of pins have to be an even positive number")
+
+	row_spacing=parse_dim(P["row_spacing"])
+	pad_spacing=parse_dim(P["pad_spacing"])
+	ext_bloat=parse_dim(P["ext_bloat"])
+	int_bloat=parse_dim(P["int_bloat"])
+
+# translate origin to the middle (int() and -0.5 rounds it for odd number of pins)
+	offs_x = -(row_spacing/2)
+	offs_y = -int((P["n"]/4-0.5) * pad_spacing)
+
+	element_begin("", "U1", P["n"] "*" P["row_spacing"]    ,0,0, 0, mil(-100))
+
+	for(n = 1; n <= P["n"]/2; n++) {
+		y = (n-1) * pad_spacing
+		element_pad(-ext_bloat, y, int_bloat, y, pad_width, n, "square")
+		element_pad(row_spacing-int_bloat, y,  row_spacing+ext_bloat, y, pad_width,     P["n"] - n + 1, "square")
+	}
+
+	silk_dist_x = either(parse_dim(P["silk_ext_x"]), pad_spacing/2)
+	silk_dist_y = either(parse_dim(P["silk_ext_y"]), pad_spacing/2)
+	rarc = either(parse_dim(P["rarc"]), pad_spacing/2)
+
+	dip_outline(-silk_dist_x-ext_bloat, -silk_dist_y, row_spacing + silk_dist_x+ext_bloat , (n-2) * pad_spacing + silk_dist_y,  rarc)
+
+	left_dim="@" -silk_dist_x-ext_bloat-pad_spacing ";0"
+	dimension(0, 0, 0, pad_spacing, left_dim, "pad_spacing")
+	dimension(0, 0, row_spacing, 0, (pad_spacing * 1.8), "row_spacing")
+	dimension(-ext_bloat, 0, 0, 0, (pad_spacing * 1.2), "ext_bloat")
+	dimension(row_spacing-int_bloat, 0, row_spacing, 0, (pad_spacing * 1.2), "int_bloat")
+
+	th=DEFAULT["pad_thickness"]
+	y = (n-2) * pad_spacing
+	dimension(-ext_bloat-th/2, y, +int_bloat+th/2, y, (pad_spacing * -1.2), "<auto>")
+	dimension(0, y-th/2, 0, y+th/2, left_dim, "pad_thickness")
+
+#	dimension(-silk_dist_x-ext_bloat, -silk_dist_y, row_spacing + silk_dist_x+ext_bloat, -silk_dist_y, pad_spacing*2.5, "<auto>")
+
+	element_end()
+}
diff --git a/pcblib/parametric/ssop b/pcblib/parametric/ssop
new file mode 100755
index 0000000..2f42d03
--- /dev/null
+++ b/pcblib/parametric/ssop
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), SSOP
+
+#@@example ssop(14)
+
+#@@purpose Generate SSOP packages.
+
+#@@desc Generate SSOP packages with variable number of pins and
+#@@desc row spacing
+
+#@@include so
+#@@include common.awk
+
+defaults='
+?pad_spacing=0.65mm,
+?row_spacing=7.8mm,
+?int_bloat=0.5mm,
+?ext_bloat=0.13mm,
+?rarc=25,
+?silk_ext_x=18,
+?silk_ext_y=22,
+?pad_thickness=0.45mm
+'
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/so.awk -v "args=$defaults,$*" -v gen=`basename $0`
+
diff --git a/pcblib/parametric/tssop b/pcblib/parametric/tssop
new file mode 100755
index 0000000..7935e2e
--- /dev/null
+++ b/pcblib/parametric/tssop
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Reference: Microchip Packaging Specification DS00000049BX (en012702.pdf), MSOP
+
+#@@example tssop(8)
+
+#@@purpose Generate TSSOP packages.
+
+#@@desc Generate TSSOP 4.4mm body packages with variable number of pins and
+#@@desc row spacing
+
+#@@include so
+#@@include common.awk
+
+
+defaults='
+?pad_spacing=0.65mm,
+?row_spacing=6.4mm,
+?int_bloat=0.6mm,
+?ext_bloat=0.4mm,
+?rarc=25,
+?silk_ext_x=18,
+?silk_ext_y=22,
+?pad_thickness=0.45mm
+'
+
+awk -f `dirname $0`/common.awk -f `dirname $0`/so.awk -v "args=$defaults,$*" -v gen=`basename $0`
+
diff --git a/pcblib/smd/01005.fp b/pcblib/smd/01005.fp
new file mode 100644
index 0000000..0d83ce4
--- /dev/null
+++ b/pcblib/smd/01005.fp
@@ -0,0 +1,32 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "01005" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-807 -19
+		-807 19
+		984 2000 1584 "1" "1" "square"]
+	    Pad[807 -19
+		807 19
+		984 2000 1584 "2" "2" "square"]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/0201.fp b/pcblib/smd/0201.fp
new file mode 100644
index 0000000..8d36bc0
--- /dev/null
+++ b/pcblib/smd/0201.fp
@@ -0,0 +1,32 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "0201" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-1181 0
+		-1181 0
+		1574 2000 2174 "1" "1" "square"]
+	    Pad[1181 0
+		1181 0
+		1574 2000 2174 "2" "2" "square"]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/0402.fp b/pcblib/smd/0402.fp
new file mode 100644
index 0000000..a95b766
--- /dev/null
+++ b/pcblib/smd/0402.fp
@@ -0,0 +1,32 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "0402" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-1574 -393
+		-1574 393
+		1968 2000 2568 "1" "1" "square"]
+	    Pad[1574 -393
+		1574 393
+		1968 2000 2568 "2" "2" "square"]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/0603.fp b/pcblib/smd/0603.fp
new file mode 100644
index 0000000..88564d9
--- /dev/null
+++ b/pcblib/smd/0603.fp
@@ -0,0 +1,32 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "0603" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-2559 -492
+		-2559 492
+		2952 2000 3552 "1" "1" "square"]
+	    Pad[2559 -492
+		2559 492
+		2952 2000 3552 "2" "2" "square"]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/0805.fp b/pcblib/smd/0805.fp
new file mode 100644
index 0000000..6454ad9
--- /dev/null
+++ b/pcblib/smd/0805.fp
@@ -0,0 +1,34 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "0805" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-3543 -393
+		-3543 393
+		5118 2000 5718 "1" "1" "square"]
+	    Pad[3543 -393
+		3543 393
+		5118 2000 5718 "2" "2" "square"]
+	ElementLine[-393 -2755 393 -2755 800]
+	    ElementLine[-393 2755 393 2755 800]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/1008.fp b/pcblib/smd/1008.fp
new file mode 100644
index 0000000..eb72366
--- /dev/null
+++ b/pcblib/smd/1008.fp
@@ -0,0 +1,34 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "1008" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-5118 -2362
+		-5118 2362
+		4330 2000 4930 "1" "1" "square"]
+	    Pad[5118 -2362
+		5118 2362
+		4330 2000 4930 "2" "2" "square"]
+	ElementLine[-1377 -4527 1377 -4527 800]
+	    ElementLine[-1377 4527 1377 4527 800]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/1206.fp b/pcblib/smd/1206.fp
new file mode 100644
index 0000000..6bddc3c
--- /dev/null
+++ b/pcblib/smd/1206.fp
@@ -0,0 +1,34 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "1206" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-5905 -1181
+		-5905 1181
+		5118 2000 5718 "1" "1" "square"]
+	    Pad[5905 -1181
+		5905 1181
+		5118 2000 5718 "2" "2" "square"]
+	ElementLine[-2362 -3740 2362 -3740 800]
+	    ElementLine[-2362 3740 2362 3740 800]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/1210.fp b/pcblib/smd/1210.fp
new file mode 100644
index 0000000..712c6e5
--- /dev/null
+++ b/pcblib/smd/1210.fp
@@ -0,0 +1,34 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "1210" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-5905 -2755
+		-5905 2755
+		5118 2000 5718 "1" "1" "square"]
+	    Pad[5905 -2755
+		5905 2755
+		5118 2000 5718 "2" "2" "square"]
+	ElementLine[-1968 -5314 1968 -5314 800]
+	    ElementLine[-1968 5314 1968 5314 800]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/1806.fp b/pcblib/smd/1806.fp
new file mode 100644
index 0000000..aadeac5
--- /dev/null
+++ b/pcblib/smd/1806.fp
@@ -0,0 +1,34 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "1806" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-7874 -3543
+		-7874 3543
+		6299 2000 6899 "1" "1" "square"]
+	    Pad[7874 -3543
+		7874 3543
+		6299 2000 6899 "2" "2" "square"]
+	ElementLine[-3149 -6692 3149 -6692 800]
+	    ElementLine[-3149 6692 3149 6692 800]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/1825.fp b/pcblib/smd/1825.fp
new file mode 100644
index 0000000..6cb3c88
--- /dev/null
+++ b/pcblib/smd/1825.fp
@@ -0,0 +1,34 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# grab the input values and convert to 1/100 mil
+	# how much to grow the pads by for soldermask [1/100 mil]
+	# clearance from planes [1/100 mil]
+	# silk screen width  [1/100 mil]
+	# courtyard silk screen width  [1/100 mil]
+# element_flags, description, pcb-name, value, mark_x, mark_y,
+# text_x, text_y, text_direction, text_scale, text_flags
+Element[0x00000000 "Standard SMT resistor, capacitor etc" "" "1825" 0 0 -3150 -3150 0 100 ""]
+(
+# 
+# Pad[x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags]
+	Pad[-7874 -10236
+		-7874 10236
+		6299 2000 6899 "1" "1" "square"]
+	    Pad[7874 -10236
+		7874 10236
+		6299 2000 6899 "2" "2" "square"]
+	ElementLine[-3149 -13385 3149 -13385 800]
+	    ElementLine[-3149 13385 3149 13385 800]
+#
+# This draws a 1 mil placement courtyard outline in silk.  It should probably
+# not be included since you wont want to try and fab a 1 mil silk line.  Then
+# again, it is most useful during parts placement.  It really is time for some
+# additional non-fab layers...
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+#	ElementLine[eval(-1*V1/2) eval(-1*V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(   V1/2) eval(-1*V2/2) CYW]
+#	ElementLine[eval(   V1/2) eval(   V2/2) eval(-1*V1/2) eval(   V2/2) CYW]
+)
diff --git a/pcblib/smd/2706.fp b/pcblib/smd/2706.fp
new file mode 100644
index 0000000..80d3a46
--- /dev/null
+++ b/pcblib/smd/2706.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:resistor
+##for:capacitor
+##for:led
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "Standard SMT resistor, capacitor etc" "" "2706" 0 0 179 0 3 100 0x00)
+(
+	ElementLine(-159 -54 -159 54 10)
+	    ElementLine(-159 54 159 54 10)
+	    ElementLine(159 54 159 -54 10)
+	    ElementLine(159 -54 -159 -54 10)
+	Pad(-108 -3 
+		-108 3
+		78 20 84 "1" "1" 0x00000100)
+	    Pad(108 -3 
+		108 3
+		78 20 84 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/DO214.fp b/pcblib/smd/DO214.fp
new file mode 100644
index 0000000..6b07fc7
--- /dev/null
+++ b/pcblib/smd/DO214.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "DO214" 0 0 221 0 3 100 0x00)
+(
+	ElementLine(-211 -89 -211 89 20)
+	    ElementLine(-211 89 -141 114 10)
+	    ElementLine(-141 114 201 114 10)
+	    ElementLine(201 114 201 -114 10)
+	    ElementLine(201 -114 -141 -114 10)
+	    ElementLine(-141 -114 -211 -89 10)
+	Pad(-106 -19 
+		-106 19
+		140 20 146 "1" "1" 0x00000100)
+	    Pad(106 -19 
+		106 19
+		140 20 146 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/DO214AB.fp b/pcblib/smd/DO214AB.fp
new file mode 100644
index 0000000..0c98a3c
--- /dev/null
+++ b/pcblib/smd/DO214AB.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "DO214AB" 0 0 227 0 3 100 0x00)
+(
+	ElementLine(-217 -92 -217 92 20)
+	    ElementLine(-217 92 -145 118 10)
+	    ElementLine(-145 118 207 118 10)
+	    ElementLine(207 118 207 -118 10)
+	    ElementLine(207 -118 -145 -118 10)
+	    ElementLine(-145 -118 -217 -92 10)
+	Pad(-109 -20 
+		-109 20
+		145 20 151 "1" "1" 0x00000100)
+	    Pad(109 -20 
+		109 20
+		145 20 151 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/MPAK.fp b/pcblib/smd/MPAK.fp
new file mode 100644
index 0000000..2e7f3a0
--- /dev/null
+++ b/pcblib/smd/MPAK.fp
@@ -0,0 +1,31 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+Element(0x00 "Pressure transducer" "" "MPAK" 235 0 3 100 0x00)
+(
+	ElementLine(0 0 0 558 10)
+	ElementLine(0 558 215 558 10)
+	ElementLine(215 558 215 0 10)
+	ElementLine(215 0 0 0 10)
+	# 1st pin on pin side
+	Pad(32 469
+	    32 525
+			   31 "1" "1" 0x100)
+	Pad(82 469
+	       82 525
+			   31 "2" "2" 0x100)
+	   Pad(132 469
+	       132 525
+			   31 "3" "3" 0x100)
+	# last pin on pin side
+	Pad(182 469
+	    182 525
+			   31 "4" "4" 0x100)
+	# extra wide pin on opposite side
+	Pad(144 60
+	    70 60
+			   87 "5" "5" 0x100)
+	Mark(32 497)
+)
diff --git a/pcblib/smd/SC70_3.fp b/pcblib/smd/SC70_3.fp
new file mode 100644
index 0000000..e964611
--- /dev/null
+++ b/pcblib/smd/SC70_3.fp
@@ -0,0 +1,33 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 3 pins" "" "SC70_3" 114 0 3 100 0x00)
+(
+	ElementLine(0 0 0 119 10)
+	ElementLine(0 119 94 119 10)
+	ElementLine(94 119 94 0 10)
+	ElementLine(94 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(21 91
+	       21 97
+			   29
+			      "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(72 91
+	    72 97
+			   29
+			      "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	# 2nd side, 2nd pin
+	Pad(47 21
+	       47 27
+			   29
+			      "3" "3" 0x100)
+	# 2nd side, 1st pin
+	Mark(21 94)
+)
diff --git a/pcblib/smd/SC70_4.fp b/pcblib/smd/SC70_4.fp
new file mode 100644
index 0000000..cb8846e
--- /dev/null
+++ b/pcblib/smd/SC70_4.fp
@@ -0,0 +1,40 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 4 pins" "" "SC70_4" 114 0 3 100 0x00)
+(
+	ElementLine(0 0 0 119 10)
+	ElementLine(0 119 94 119 10)
+	ElementLine(94 119 94 0 10)
+	ElementLine(94 0 0 0 10)
+	# 1st side, 1st pin
+	# extra width
+	   Pad(24 94
+	       27 94
+			   29
+			      "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(72 91
+	    72 97
+			   29
+			      "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	Pad(72 21
+	       72 27
+			   29
+			      "3" "3" 0x100)
+	# 2nd side, 2nd pin
+	# 2nd side, 1st pin
+	Pad(21 21
+	       21 27
+			   29
+			      "4" "4" 0x100)
+	Mark(21 94)
+)
diff --git a/pcblib/smd/SC90.fp b/pcblib/smd/SC90.fp
new file mode 100644
index 0000000..89b2ffe
--- /dev/null
+++ b/pcblib/smd/SC90.fp
@@ -0,0 +1,33 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 3 pins" "" "SC90" 93 0 3 100 0x00)
+(
+	ElementLine(0 0 0 98 10)
+	ElementLine(0 98 73 98 10)
+	ElementLine(73 98 73 0 10)
+	ElementLine(73 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(17 76
+	       17 80
+			   24
+			      "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(56 76
+	    56 80
+			   24
+			      "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	# 2nd side, 2nd pin
+	Pad(36 17
+	       36 21
+			   24
+			      "3" "3" 0x100)
+	# 2nd side, 1st pin
+	Mark(17 78)
+)
diff --git a/pcblib/smd/SOD106A.fp b/pcblib/smd/SOD106A.fp
new file mode 100644
index 0000000..0b1a87c
--- /dev/null
+++ b/pcblib/smd/SOD106A.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOD106A" 0 0 166 0 3 100 0x00)
+(
+	ElementLine(-156 -68 -156 68 20)
+	    ElementLine(-156 68 -105 87 10)
+	    ElementLine(-105 87 146 87 10)
+	    ElementLine(146 87 146 -87 10)
+	    ElementLine(146 -87 -105 -87 10)
+	    ElementLine(-105 -87 -156 -68 10)
+	Pad(-76 -17 
+		-76 17
+		102 20 108 "1" "1" 0x00000100)
+	    Pad(76 -17 
+		76 17
+		102 20 108 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/SOD110.fp b/pcblib/smd/SOD110.fp
new file mode 100644
index 0000000..99c5a50
--- /dev/null
+++ b/pcblib/smd/SOD110.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOD110" 0 0 83 0 3 100 0x00)
+(
+	ElementLine(-73 -38 -73 38 20)
+	    ElementLine(-73 38 -50 49 10)
+	    ElementLine(-50 49 63 49 10)
+	    ElementLine(63 49 63 -49 10)
+	    ElementLine(63 -49 -50 -49 10)
+	    ElementLine(-50 -49 -73 -38 10)
+	Pad(-29 -15 
+		-29 15
+		46 20 52 "1" "1" 0x00000100)
+	    Pad(29 -15 
+		29 15
+		46 20 52 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/SOD123.fp b/pcblib/smd/SOD123.fp
new file mode 100644
index 0000000..9ec31d2
--- /dev/null
+++ b/pcblib/smd/SOD123.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOD123" 0 0 120 0 3 100 0x00)
+(
+	ElementLine(-110 -40 -110 40 20)
+	    ElementLine(-110 40 -76 51 10)
+	    ElementLine(-76 51 100 51 10)
+	    ElementLine(100 51 100 -51 10)
+	    ElementLine(100 -51 -76 -51 10)
+	    ElementLine(-76 -51 -110 -40 10)
+	Pad(-55 -6 
+		-55 6
+		69 20 75 "1" "1" 0x00000100)
+	    Pad(55 -6 
+		55 6
+		69 20 75 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/SOD323.fp b/pcblib/smd/SOD323.fp
new file mode 100644
index 0000000..ed46220
--- /dev/null
+++ b/pcblib/smd/SOD323.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOD323" 0 0 93 0 3 100 0x00)
+(
+	ElementLine(-83 -35 -83 35 20)
+	    ElementLine(-83 35 -58 45 10)
+	    ElementLine(-58 45 73 45 10)
+	    ElementLine(73 45 73 -45 10)
+	    ElementLine(73 -45 -58 -45 10)
+	    ElementLine(-58 -45 -83 -35 10)
+	Pad(-37 -10 
+		-37 10
+		51 20 57 "1" "1" 0x00000100)
+	    Pad(37 -10 
+		37 10
+		51 20 57 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/SOD80.fp b/pcblib/smd/SOD80.fp
new file mode 100644
index 0000000..323ce51
--- /dev/null
+++ b/pcblib/smd/SOD80.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOD80" 0 0 116 0 3 100 0x00)
+(
+	ElementLine(-106 -43 -106 43 20)
+	    ElementLine(-106 43 -80 55 10)
+	    ElementLine(-80 55 96 55 10)
+	    ElementLine(96 55 96 -55 10)
+	    ElementLine(96 -55 -80 -55 10)
+	    ElementLine(-80 -55 -106 -43 10)
+	Pad(-58 -16 
+		-58 16
+		53 20 59 "1" "1" 0x00000100)
+	    Pad(58 -16 
+		58 16
+		53 20 59 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/SOD87.fp b/pcblib/smd/SOD87.fp
new file mode 100644
index 0000000..a049c2b
--- /dev/null
+++ b/pcblib/smd/SOD87.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:diode
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOD87" 0 0 124 0 3 100 0x00)
+(
+	ElementLine(-114 -57 -114 57 20)
+	    ElementLine(-114 57 -84 73 10)
+	    ElementLine(-84 73 104 73 10)
+	    ElementLine(104 73 104 -73 10)
+	    ElementLine(104 -73 -84 -73 10)
+	    ElementLine(-84 -73 -114 -57 10)
+	Pad(-58 -26 
+		-58 26
+		61 20 67 "1" "1" 0x00000100)
+	    Pad(58 -26 
+		58 26
+		61 20 67 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/SOT143.fp b/pcblib/smd/SOT143.fp
new file mode 100644
index 0000000..015e491
--- /dev/null
+++ b/pcblib/smd/SOT143.fp
@@ -0,0 +1,38 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 4 pins" "" "SOT143" 144 0 3 100 0x00)
+(
+	ElementLine(0 0 0 139 10)
+	ElementLine(0 139 124 139 10)
+	ElementLine(124 139 124 0 10)
+	ElementLine(124 0 0 0 10)
+	# 1st side, 1st pin
+	# extra width
+	   Pad(28 110
+	       31 110
+			   34
+			      "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(99 107
+	    99 113
+			   34
+			      "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	Pad(99 25
+	       99 31
+			   34
+			      "3" "3" 0x100)
+	# 2nd side, 2nd pin
+	# 2nd side, 1st pin
+	Pad(25 25
+	       25 31
+			   34
+			      "4" "4" 0x100)
+	Mark(25 110)
+)
diff --git a/pcblib/smd/SOT223.fp b/pcblib/smd/SOT223.fp
new file mode 100644
index 0000000..9f72c80
--- /dev/null
+++ b/pcblib/smd/SOT223.fp
@@ -0,0 +1,29 @@
+##from:pcb
+##for:transistor
+
+Element(0x00 "SMT transistor, 4 pins" "" "SOT223" 305 0 3 100 0x00)
+(
+	ElementLine(0 0 0 414 10)
+	ElementLine(0 414 285 414 10)
+	ElementLine(285 414 285 0 10)
+	ElementLine(285 0 0 0 10)
+	# 1st pin on pin side
+	Pad(52 296
+	    52 362
+			   56
+			      "1" "1" 0x100)
+	Pad(142 296
+	       142 362
+			   56
+			      "2" "2" 0x100)
+	# last pin on pin side
+	Pad(233 296
+	    233 362
+			   56
+			      "3" "3" 0x100)
+	# extra wide pin on opposite side
+	Pad(187 85
+	    97 85
+			   122 "4" "4" 0x100)
+	Mark(52 329)
+)
diff --git a/pcblib/smd/SOT23.fp b/pcblib/smd/SOT23.fp
new file mode 100644
index 0000000..e09842a
--- /dev/null
+++ b/pcblib/smd/SOT23.fp
@@ -0,0 +1,33 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 3 pins" "" "SOT23" 148 0 3 100 0x00)
+(
+	ElementLine(0 0 0 139 10)
+	ElementLine(0 139 128 139 10)
+	ElementLine(128 139 128 0 10)
+	ElementLine(128 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(25 107
+	       25 113
+			   34
+			      "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(103 107
+	    103 113
+			   34
+			      "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	# 2nd side, 2nd pin
+	Pad(64 25
+	       64 31
+			   34
+			      "3" "3" 0x100)
+	# 2nd side, 1st pin
+	Mark(25 110)
+)
diff --git a/pcblib/smd/SOT23D.fp b/pcblib/smd/SOT23D.fp
new file mode 100644
index 0000000..c24c524
--- /dev/null
+++ b/pcblib/smd/SOT23D.fp
@@ -0,0 +1,33 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOT23D" 148 0 3 100 0x00)
+(
+	ElementLine(0 0 0 139 10)
+	ElementLine(0 139 128 139 10)
+	ElementLine(128 139 128 0 10)
+	ElementLine(128 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(25 107
+	       25 113
+			   34
+			      "2" "2" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(103 107
+	    103 113
+			   34
+			      "3" "3" 0x100)
+	# 2nd side, 3rd pin
+	# 2nd side, 2nd pin
+	Pad(64 25
+	       64 31
+			   34
+			      "1" "1" 0x100)
+	# 2nd side, 1st pin
+	Mark(25 110)
+)
diff --git a/pcblib/smd/SOT25.fp b/pcblib/smd/SOT25.fp
new file mode 100644
index 0000000..51cb833
--- /dev/null
+++ b/pcblib/smd/SOT25.fp
@@ -0,0 +1,38 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 5 pins" "" "SOT25" 138 0 3 100 0x00)
+(
+	ElementLine(0 0 0 139 10)
+	ElementLine(0 139 118 139 10)
+	ElementLine(118 139 118 0 10)
+	ElementLine(118 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(20 102
+	       20 118
+			   24 "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(98 102
+	    98 118
+			   24 "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	Pad(98 20
+	       98 36
+			   24 "3" "3" 0x100)
+	# 2nd side, 2nd pin
+	Pad(59 20
+	       59 36
+			   24 "4" "4" 0x100)
+	# 2nd side, 1st pin
+	Pad(20 20
+	       20 36
+			   24 "5" "5" 0x100)
+	Mark(20 110)
+)
diff --git a/pcblib/smd/SOT26.fp b/pcblib/smd/SOT26.fp
new file mode 100644
index 0000000..3a028c3
--- /dev/null
+++ b/pcblib/smd/SOT26.fp
@@ -0,0 +1,41 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 6 pins" "" "SOT26" 138 0 3 100 0x00)
+(
+	ElementLine(0 0 0 139 10)
+	ElementLine(0 139 118 139 10)
+	ElementLine(118 139 118 0 10)
+	ElementLine(118 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(20 102
+	       20 118
+			   24 "1" "1" 0x100)
+	# 1st side, 2nd pin
+	Pad(59 102
+	       59 118
+			   24 "2" "2" 0x100)
+	# 1st side, 3rd pin
+	Pad(98 102
+	    98 118
+			   24 "3" "3" 0x100)
+	# 2nd side, 3rd pin
+	Pad(98 20
+	       98 36
+			   24 "4" "4" 0x100)
+	# 2nd side, 2nd pin
+	Pad(59 20
+	       59 36
+			   24 "5" "5" 0x100)
+	# 2nd side, 1st pin
+	Pad(20 20
+	       20 36
+			   24 "6" "6" 0x100)
+	Mark(20 110)
+)
diff --git a/pcblib/smd/SOT323.fp b/pcblib/smd/SOT323.fp
new file mode 100644
index 0000000..243d5d1
--- /dev/null
+++ b/pcblib/smd/SOT323.fp
@@ -0,0 +1,33 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT transistor, 3 pins" "" "SOT323" 114 0 3 100 0x00)
+(
+	ElementLine(0 0 0 119 10)
+	ElementLine(0 119 94 119 10)
+	ElementLine(94 119 94 0 10)
+	ElementLine(94 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(21 91
+	       21 97
+			   29
+			      "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(72 91
+	    72 97
+			   29
+			      "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	# 2nd side, 2nd pin
+	Pad(47 21
+	       47 27
+			   29
+			      "3" "3" 0x100)
+	# 2nd side, 1st pin
+	Mark(21 94)
+)
diff --git a/pcblib/smd/SOT323D.fp b/pcblib/smd/SOT323D.fp
new file mode 100644
index 0000000..8f21159
--- /dev/null
+++ b/pcblib/smd/SOT323D.fp
@@ -0,0 +1,33 @@
+##from:pcb
+##for:transistor
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+Element(0x00 "SMT diode (pin 1 is cathode)" "" "SOT323D" 114 0 3 100 0x00)
+(
+	ElementLine(0 0 0 119 10)
+	ElementLine(0 119 94 119 10)
+	ElementLine(94 119 94 0 10)
+	ElementLine(94 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(21 91
+	       21 97
+			   29
+			      "2" "2" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(72 91
+	    72 97
+			   29
+			      "3" "3" 0x100)
+	# 2nd side, 3rd pin
+	# 2nd side, 2nd pin
+	Pad(47 21
+	       47 27
+			   29
+			      "1" "1" 0x100)
+	# 2nd side, 1st pin
+	Mark(21 94)
+)
diff --git a/pcblib/smd/SOT325.fp b/pcblib/smd/SOT325.fp
new file mode 100644
index 0000000..24d6a28
--- /dev/null
+++ b/pcblib/smd/SOT325.fp
@@ -0,0 +1,40 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+# alias sc70-5
+Element(0x00 "SMT transistor, 5 pins" "" "SOT325" 100 0 3 100 0x00)
+(
+	ElementLine(0 0 0 119 10)
+	ElementLine(0 119 80 119 10)
+	ElementLine(80 119 80 0 10)
+	ElementLine(80 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(14 84
+	       14 104
+			   15 "1" "1" 0x100)
+	# 1st side, 2nd pin
+	# 1st side, 3rd pin
+	Pad(65 84
+	    65 104
+			   15 "2" "2" 0x100)
+	# 2nd side, 3rd pin
+	Pad(65 14
+	       65 34
+			   15 "3" "3" 0x100)
+	# 2nd side, 2nd pin
+	Pad(40 14
+	       40 34
+			   15 "4" "4" 0x100)
+	# 2nd side, 1st pin
+	Pad(14 14
+	       14 34
+			   15 "5" "5" 0x100)
+	Mark(14 94)
+)
diff --git a/pcblib/smd/SOT326.fp b/pcblib/smd/SOT326.fp
new file mode 100644
index 0000000..adf3e64
--- /dev/null
+++ b/pcblib/smd/SOT326.fp
@@ -0,0 +1,42 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+	                             # 78 for SOT23
+	                             # 82 for SOT23
+	      # 41 for SOT23
+	               # 34 for SOT23, 24 for SOT25
+# alias sc70-6
+Element(0x00 "SMT transistor, 6 pins" "" "SOT326" 100 0 3 100 0x00)
+(
+	ElementLine(0 0 0 119 10)
+	ElementLine(0 119 80 119 10)
+	ElementLine(80 119 80 0 10)
+	ElementLine(80 0 0 0 10)
+	# 1st side, 1st pin
+	Pad(14 84
+	       14 104
+			   15 "1" "1" 0x100)
+	# 1st side, 2nd pin
+	Pad(40 84
+	       40 104
+			   15 "2" "2" 0x100)
+	# 1st side, 3rd pin
+	Pad(65 84
+	    65 104
+			   15 "3" "3" 0x100)
+	# 2nd side, 3rd pin
+	Pad(65 14
+	       65 34
+			   15 "4" "4" 0x100)
+	# 2nd side, 2nd pin
+	Pad(40 14
+	       40 34
+			   15 "5" "5" 0x100)
+	# 2nd side, 1st pin
+	Pad(14 14
+	       14 34
+			   15 "6" "6" 0x100)
+	Mark(14 94)
+)
diff --git a/pcblib/smd/SOT89.fp b/pcblib/smd/SOT89.fp
new file mode 100644
index 0000000..70a4b84
--- /dev/null
+++ b/pcblib/smd/SOT89.fp
@@ -0,0 +1,31 @@
+##from:pcb
+##for:transistor
+##for:stabilizer
+##for:linear
+
+Element(0x00 "SMT transistor, 4 pins" "" "SOT89" 203 0 3 100 0x00)
+(
+	ElementLine(0 0 0 207 10)
+	ElementLine(0 207 183 207 10)
+	ElementLine(183 207 183 0 10)
+	ElementLine(183 0 0 0 10)
+	# 1st pin on pin side
+	Pad(30 152
+	    30 176
+			   37
+			      "1" "1" 0x100)
+	Pad(91 152
+	       91 176
+			   37
+			      "2" "2" 0x100)
+	# last pin on pin side
+	Pad(152 152
+	    152 176
+			   37
+			      "3" "3" 0x100)
+	# extra wide pin on opposite side
+	Pad(121 42
+	    61 42
+			   61 "4" "4" 0x100)
+	Mark(30 164)
+)
diff --git a/pcblib/smd/TANT_A.fp b/pcblib/smd/TANT_A.fp
new file mode 100644
index 0000000..1b6027b
--- /dev/null
+++ b/pcblib/smd/TANT_A.fp
@@ -0,0 +1,21 @@
+##from:pcb
+##for:capacitor
+##for:tantal
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_A" 0 0 106 0 3 100 0x00)
+(
+	ElementLine(-96 -43 -96 43 20)
+	    ElementLine(-96 43 -72 55 10)
+	    ElementLine(-72 55 86 55 10)
+	    ElementLine(86 55 86 -55 10)
+	    ElementLine(86 -55 -72 -55 10)
+	    ElementLine(-72 -55 -96 -43 10)
+	Pad(-50 -18 
+		-50 18
+		49 20 55 "1" "1" 0x00000100)
+	    Pad(50 -18 
+		50 18
+		49 20 55 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/TANT_B.fp b/pcblib/smd/TANT_B.fp
new file mode 100644
index 0000000..5951a59
--- /dev/null
+++ b/pcblib/smd/TANT_B.fp
@@ -0,0 +1,21 @@
+##from:pcb
+##for:capacitor
+##for:tantal
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_B" 0 0 132 0 3 100 0x00)
+(
+	ElementLine(-122 -77 -122 77 20)
+	    ElementLine(-122 77 -87 99 10)
+	    ElementLine(-87 99 112 99 10)
+	    ElementLine(112 99 112 -99 10)
+	    ElementLine(112 -99 -87 -99 10)
+	    ElementLine(-87 -99 -122 -77 10)
+	Pad(-55 -41 
+		-55 41
+		71 20 77 "1" "1" 0x00000100)
+	    Pad(55 -41 
+		55 41
+		71 20 77 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/TANT_C.fp b/pcblib/smd/TANT_C.fp
new file mode 100644
index 0000000..3407fab
--- /dev/null
+++ b/pcblib/smd/TANT_C.fp
@@ -0,0 +1,21 @@
+##from:pcb
+##for:capacitor
+##for:tantal
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_C" 0 0 188 0 3 100 0x00)
+(
+	ElementLine(-178 -87 -178 87 20)
+	    ElementLine(-178 87 -130 112 10)
+	    ElementLine(-130 112 168 112 10)
+	    ElementLine(168 112 168 -112 10)
+	    ElementLine(168 -112 -130 -112 10)
+	    ElementLine(-130 -112 -178 -87 10)
+	Pad(-94 -39 
+		-94 39
+		97 20 103 "1" "1" 0x00000100)
+	    Pad(94 -39 
+		94 39
+		97 20 103 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/TANT_D.fp b/pcblib/smd/TANT_D.fp
new file mode 100644
index 0000000..6dc4024
--- /dev/null
+++ b/pcblib/smd/TANT_D.fp
@@ -0,0 +1,21 @@
+##from:pcb
+##for:capacitor
+##for:tantal
+
+	# how much to grow the pads by for soldermask
+	# clearance from planes
+Element(0x00 "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_D" 0 0 229 0 3 100 0x00)
+(
+	ElementLine(-219 -117 -219 117 20)
+	    ElementLine(-219 117 -158 150 10)
+	    ElementLine(-158 150 209 150 10)
+	    ElementLine(209 150 209 -150 10)
+	    ElementLine(209 -150 -158 -150 10)
+	    ElementLine(-158 -150 -219 -117 10)
+	Pad(-115 -56 
+		-115 56
+		123 20 129 "1" "1" 0x00000100)
+	    Pad(115 -56 
+		115 56
+		123 20 129 "2" "2" 0x00000100)
+)
diff --git a/pcblib/smd/minimelf.fp b/pcblib/smd/minimelf.fp
new file mode 100644
index 0000000..31ac05c
--- /dev/null
+++ b/pcblib/smd/minimelf.fp
@@ -0,0 +1,12 @@
+##from:repo.hu/projects/openhw
+##for:diode
+
+
+Element["" "" "D101" "" 145669 141732 -3720 -2087 0 70 ""]
+(
+	Pad[7087 -984 7087 984 5118 5000 7618 "" "1" "square"]
+	Pad[-7087 -984 -7087 984 5118 5000 7618 "" "2" "square"]
+	ElementLine [-3937 -3150 3937 -3150 787]
+	ElementLine [-3937 3150 3937 3150 787]
+
+	)
diff --git a/pcblib/tru-hole/HC49.fp b/pcblib/tru-hole/HC49.fp
new file mode 100644
index 0000000..f85f928
--- /dev/null
+++ b/pcblib/tru-hole/HC49.fp
@@ -0,0 +1,13 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC49" 0 -60 0 100 0x00)
+(
+	Pin(50 50 60 28 "1" 0x101)
+	Pin(250 50 60 28 "2" 0x01)
+	ElementLine(50 0 250 0 20)
+	ElementArc(250 50 50 50 90 180 20)
+	ElementLine(250 100 50 100 20)
+	ElementArc(50 50 50 50 270 180 20)
+	Mark(50 50)
+)
diff --git a/pcblib/tru-hole/HC49U.fp b/pcblib/tru-hole/HC49U.fp
new file mode 100644
index 0000000..58a5342
--- /dev/null
+++ b/pcblib/tru-hole/HC49U.fp
@@ -0,0 +1,13 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC49U" 0 -60 0 100 0x00)
+(
+	Pin(121 91 60 32 "1" 0x101)
+	Pin(313 91 60 32 "2" 0x01)
+	ElementLine(91 0 344 0 20)
+	ElementArc(344 91 91 91 90 180 20)
+	ElementLine(344 183 91 183 20)
+	ElementArc(91 91 91 91 270 180 20)
+	Mark(121 91)
+)
diff --git a/pcblib/tru-hole/HC49UH.fp b/pcblib/tru-hole/HC49UH.fp
new file mode 100644
index 0000000..7c951fa
--- /dev/null
+++ b/pcblib/tru-hole/HC49UH.fp
@@ -0,0 +1,13 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC49UH" 0 -60 0 100 0x00)
+(
+	Pin(121 615 60 32 "1" 0x101)
+	Pin(313 615 60 32 "2" 0x01)
+	ElementLine(0 0 435 0 20)
+	ElementLine(435 0 435 515 20)
+	ElementLine(435 515 0 515 20)
+	ElementLine(0 515 0 0 20)
+	Mark(121 615)
+)
diff --git a/pcblib/tru-hole/HC49U_3.fp b/pcblib/tru-hole/HC49U_3.fp
new file mode 100644
index 0000000..0c59c44
--- /dev/null
+++ b/pcblib/tru-hole/HC49U_3.fp
@@ -0,0 +1,14 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC49U_3" 0 -60 0 100 0x00)
+(
+	Pin(121 91 60 32 "1" 0x101)
+	Pin(217 91 60 32 "2" 0x01)
+	Pin(313 91 60 32 "3" 0x01)
+	ElementLine(91 0 344 0 20)
+	ElementArc(344 91 91 91 90 180 20)
+	ElementLine(344 183 91 183 20)
+	ElementArc(91 91 91 91 270 180 20)
+	Mark(121 91)
+)
diff --git a/pcblib/tru-hole/HC49U_3H.fp b/pcblib/tru-hole/HC49U_3H.fp
new file mode 100644
index 0000000..23aae06
--- /dev/null
+++ b/pcblib/tru-hole/HC49U_3H.fp
@@ -0,0 +1,14 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC49U_3H" 0 -60 0 100 0x00)
+(
+	Pin(121 615 60 32 "1" 0x101)
+	Pin(217 615 60 32 "2" 0x01)
+	Pin(313 615 60 32 "3" 0x01)
+	ElementLine(0 0 435 0 20)
+	ElementLine(435 0 435 515 20)
+	ElementLine(435 515 0 515 20)
+	ElementLine(0 515 0 0 20)
+	Mark(121 615)
+)
diff --git a/pcblib/tru-hole/HC51U.fp b/pcblib/tru-hole/HC51U.fp
new file mode 100644
index 0000000..22ef712
--- /dev/null
+++ b/pcblib/tru-hole/HC51U.fp
@@ -0,0 +1,13 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC51U" 0 -60 0 100 0x00)
+(
+	Pin(136 176 80 40 "1" 0x101)
+	Pin(621 176 80 40 "2" 0x01)
+	ElementLine(176 0 581 0 20)
+	ElementArc(581 176 176 176 90 180 20)
+	ElementLine(581 352 176 352 20)
+	ElementArc(176 176 176 176 270 180 20)
+	Mark(136 176)
+)
diff --git a/pcblib/tru-hole/HC51UH.fp b/pcblib/tru-hole/HC51UH.fp
new file mode 100644
index 0000000..6a1b756
--- /dev/null
+++ b/pcblib/tru-hole/HC51UH.fp
@@ -0,0 +1,13 @@
+##from:pcb
+##for:crystal
+##for:xtal
+Element(0x00 "Crystals" "" "HC51UH" 0 -60 0 100 0x00)
+(
+	Pin(136 975 80 40 "1" 0x101)
+	Pin(621 975 80 40 "2" 0x01)
+	ElementLine(0 0 757 0 20)
+	ElementLine(757 0 757 775 20)
+	ElementLine(757 775 0 775 20)
+	ElementLine(0 775 0 0 20)
+	Mark(136 975)
+)
diff --git a/pcblib/tru-hole/HEPTAWATT.fp b/pcblib/tru-hole/HEPTAWATT.fp
new file mode 100644
index 0000000..a6233e2
--- /dev/null
+++ b/pcblib/tru-hole/HEPTAWATT.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:IC
+Element(0x00 "Power IC, as in MULTIWATT15" "" "HEPTAWATT" 469 50 3 100 0x00)
+(
+       Pin(54 310 90 60 "1" 0x101)
+       Pin(104 110 90 60 "2" 0x01)
+       Pin(154 310 90 60 "3" 0x01)
+       Pin(204 110 90 60 "4" 0x01)
+       Pin(254 310 90 60 "5" 0x01)
+       Pin(304 110 90 60 "6" 0x01)
+       Pin(354 310 90 60 "7" 0x01)
+       ElementLine(0 0 0 189 20)
+       ElementLine(0 189 409 189 20)
+       ElementLine(409 189 409 0 20)
+       ElementLine(409 0 0 0 20)
+       ElementLine(0 50 409 50 10)
+       ElementLine(129 0 129 50 10)
+       ElementLine(279 0 279 50 10)
+       Mark(54 249)
+ ) 
diff --git a/pcblib/tru-hole/LED3.fp b/pcblib/tru-hole/LED3.fp
new file mode 100644
index 0000000..9d270bb
--- /dev/null
+++ b/pcblib/tru-hole/LED3.fp
@@ -0,0 +1,19 @@
+##from:pcb
+##for:crystal
+##for:led
+##ref:http://www.lumex.com
+Element(0x00 "LED, size in mm (pin 1 is +, 2 is -)" "" "LED3" 100 70 0 100 0x00)
+(
+# typical LED is 0.5 mm or 0.020" square pin.  See for example
+# http://www.lumex.com and part number SSL-LX3054LGD.
+# 0.020" square is 0.0288" diagonal.  A number 57 drill is 
+# 0.043" which should be enough.  a 65 mil pad gives 11 mils
+# of annular ring.
+	Pin(-50 0 65 43 "1" 0x101)
+	Pin(50 0 65 43 "2" 0x01)
+   ElementArc(0 0 59 59    45  90 10)
+	ElementArc(0 0 59 59   225  90 10)
+   ElementArc(0 0 79 79    45  90 10)
+	ElementArc(0 0 79 79   225  90 10)
+	Mark(0 0)
+)
diff --git a/pcblib/tru-hole/LED5.fp b/pcblib/tru-hole/LED5.fp
new file mode 100644
index 0000000..3ef0118
--- /dev/null
+++ b/pcblib/tru-hole/LED5.fp
@@ -0,0 +1,16 @@
+##from:pcb
+##for:led
+##ref:http://www.lumex.com
+Element(0x00 "LED, size in mm (pin 1 is +, 2 is -)" "" "LED5" 100 70 0 100 0x00)
+(
+# typical LED is 0.5 mm or 0.020" square pin.  See for example
+# http://www.lumex.com and part number SSL-LX3054LGD.
+# 0.020" square is 0.0288" diagonal.  A number 57 drill is 
+# 0.043" which should be enough.  a 65 mil pad gives 11 mils
+# of annular ring.
+	Pin(-50 0 65 43 "1" 0x101)
+	Pin(50 0 65 43 "2" 0x01)
+   ElementArc(0 0 118 118     0 360 10)
+   ElementArc(0 0 138 138     0 360 10)
+	Mark(0 0)
+)
diff --git a/pcblib/tru-hole/MULTIWATT11.fp b/pcblib/tru-hole/MULTIWATT11.fp
new file mode 100644
index 0000000..467b048
--- /dev/null
+++ b/pcblib/tru-hole/MULTIWATT11.fp
@@ -0,0 +1,24 @@
+##from:pcb
+##for:IC
+Element(0x00 "Power IC, as in MULTIWATT15" "" "MULTIWATT11" 860 50 3 100 0x00)
+(
+       Pin(65 380 90 60 "1" 0x101)
+       Pin(132 180 90 60 "2" 0x01)
+       Pin(199 380 90 60 "3" 0x01)
+       Pin(266 180 90 60 "4" 0x01)
+       Pin(333 380 90 60 "5" 0x01)
+       Pin(400 180 90 60 "6" 0x01)
+       Pin(467 380 90 60 "7" 0x01)
+       Pin(534 180 90 60 "8" 0x01)
+       Pin(601 380 90 60 "9" 0x01)
+       Pin(668 180 90 60 "10" 0x01)
+       Pin(735 380 90 60 "11" 0x01)
+       ElementLine(0 0 0 200 20)
+       ElementLine(0 200 800 200 20)
+       ElementLine(800 200 800 0 20)
+       ElementLine(800 0 0 0 20)
+       ElementLine(0 50 800 50 10)
+       ElementLine(325 0 325 50 10)
+       ElementLine(475 0 475 50 10)
+       Mark(65 260)
+ ) 
diff --git a/pcblib/tru-hole/MULTIWATT15.fp b/pcblib/tru-hole/MULTIWATT15.fp
new file mode 100644
index 0000000..e3ce4cb
--- /dev/null
+++ b/pcblib/tru-hole/MULTIWATT15.fp
@@ -0,0 +1,28 @@
+##from:pcb
+##for:IC
+Element(0x00 "Power IC, as in MULTIWATT15" "" "MULTIWATT15" 860 50 3 100 0x00)
+(
+       Pin(50 380 90 60 "1" 0x101)
+       Pin(100 180 90 60 "2" 0x01)
+       Pin(150 380 90 60 "3" 0x01)
+       Pin(200 180 90 60 "4" 0x01)
+       Pin(250 380 90 60 "5" 0x01)
+       Pin(300 180 90 60 "6" 0x01)
+       Pin(350 380 90 60 "7" 0x01)
+       Pin(400 180 90 60 "8" 0x01)
+       Pin(450 380 90 60 "9" 0x01)
+       Pin(500 180 90 60 "10" 0x01)
+       Pin(550 380 90 60 "11" 0x01)
+       Pin(600 180 90 60 "12" 0x01)
+       Pin(650 380 90 60 "13" 0x01)
+       Pin(700 180 90 60 "14" 0x01)
+       Pin(750 380 90 60 "15" 0x01)
+       ElementLine(0 0 0 200 20)
+       ElementLine(0 200 800 200 20)
+       ElementLine(800 200 800 0 20)
+       ElementLine(800 0 0 0 20)
+       ElementLine(0 50 800 50 10)
+       ElementLine(325 0 325 50 10)
+       ElementLine(475 0 475 50 10)
+       Mark(50 260)
+ ) 
diff --git a/pcblib/tru-hole/MULTIWATT8.fp b/pcblib/tru-hole/MULTIWATT8.fp
new file mode 100644
index 0000000..40eecbb
--- /dev/null
+++ b/pcblib/tru-hole/MULTIWATT8.fp
@@ -0,0 +1,21 @@
+##from:pcb
+##for:IC
+Element(0x00 "Power IC, as in MULTIWATT15" "" "MULTIWATT8" 860 50 3 100 0x00)
+(
+       Pin(50 115 90 60 "1" 0x101)
+       Pin(150 115 90 60 "2" 0x01)
+       Pin(250 115 90 60 "3" 0x01)
+       Pin(350 115 90 60 "4" 0x01)
+       Pin(450 115 90 60 "5" 0x01)
+       Pin(550 115 90 60 "6" 0x01)
+       Pin(650 115 90 60 "7" 0x01)
+       Pin(750 115 90 60 "8" 0x01)
+       ElementLine(0 0 0 200 20)
+       ElementLine(0 200 800 200 20)
+       ElementLine(800 200 800 0 20)
+       ElementLine(800 0 0 0 20)
+       ElementLine(0 50 800 50 10)
+       ElementLine(325 0 325 50 10)
+       ElementLine(475 0 475 50 10)
+       Mark(50 260)
+ ) 
diff --git a/pcblib/tru-hole/OSC14.fp b/pcblib/tru-hole/OSC14.fp
new file mode 100644
index 0000000..9f74184
--- /dev/null
+++ b/pcblib/tru-hole/OSC14.fp
@@ -0,0 +1,26 @@
+##from:pcb
+##for:oscillator
+##for:xtal
+	Element(0x00 "Crystal oscillator" "" "OSC14" 270 300 3 100 0x00)
+(
+	Pin(100 100 50 28 "NC" 0x01)
+	Pin(100 700 50 28 "GND" 0x01)
+	Pin(400 700 50 28 "CLK" 0x01)
+	Pin(400 100 50 28 "VCC" 0x01)
+	ElementLine(5 5 400 5 10)
+	ElementArc(400 100 95 95 180 90 10)
+	ElementLine(495 100 495 700 10)
+	ElementArc(400 700 95 95 90 90 10)
+	ElementLine(400 795 100 795 10)
+	ElementArc(100 700 95 95 0 90 10)
+	ElementLine(5 700 5 5 10)
+	ElementLine(100 60 400 60 10)
+	ElementArc(400 100 40 40 180 90 10)
+	ElementLine(440 100 440 700 10)
+	ElementArc(400 700 40 40 90 90 10)
+	ElementLine(400 740 100 740 10)
+	ElementArc(100 700 40 40 0 90 10)
+	ElementLine(60 700 60 100 10)
+	ElementArc(100 100 40 40 270 90 10)
+	Mark(100 100)
+)
diff --git a/pcblib/tru-hole/PENTAWATT.fp b/pcblib/tru-hole/PENTAWATT.fp
new file mode 100644
index 0000000..2c59f5c
--- /dev/null
+++ b/pcblib/tru-hole/PENTAWATT.fp
@@ -0,0 +1,18 @@
+##from:pcb
+##for:IC
+Element(0x00 "Power IC, as in MULTIWATT15" "" "PENTAWATT" 469 50 3 100 0x00)
+(
+       Pin(70 334 90 60 "1" 0x101)
+       Pin(137 177 90 60 "2" 0x01)
+       Pin(204 334 90 60 "3" 0x01)
+       Pin(271 177 90 60 "4" 0x01)
+       Pin(338 334 90 60 "5" 0x01)
+       ElementLine(0 0 0 189 20)
+       ElementLine(0 189 409 189 20)
+       ElementLine(409 189 409 0 20)
+       ElementLine(409 0 0 0 20)
+       ElementLine(0 50 409 50 10)
+       ElementLine(129 0 129 50 10)
+       ElementLine(279 0 279 50 10)
+       Mark(70 249)
+ ) 
diff --git a/pcblib/tru-hole/TACT_6x6_4p b/pcblib/tru-hole/TACT_6x6_4p
new file mode 100644
index 0000000..58e5fb5
--- /dev/null
+++ b/pcblib/tru-hole/TACT_6x6_4p
@@ -0,0 +1,37 @@
+##from:repo.hu/projects/openhw
+##geo:180
+##for:switch
+##for:button
+
+Element["" "" "" "" 175500 115500 0 0 0 100 ""]
+(
+	Pin[13091 9000 9000 5000 9600 3937 "" "4" "edge2,intconn(2)"]
+	Pin[-12500 9000 9000 5000 9600 3937 "" "3" "edge2,intconn(2)"]
+	Pin[13091 -9000 9000 5000 9600 3937 "" "2" "edge2,intconn(1)"]
+	Pin[-12500 -9000 9000 5000 9600 3937 "" "1" "edge2,intconn(1)"]
+	ElementLine [12181 11736 12181 -11886 787]
+	ElementLine [-11441 11736 12181 11736 787]
+	ElementLine [-11441 -11886 -11441 11736 787]
+	ElementLine [-11441 -11886 12181 -11886 787]
+	ElementArc [7815 -8031 2756 2756 90 90 787]
+	ElementArc [7815 -8031 2756 2756 0 90 787]
+	ElementArc [7815 -8031 2756 2756 270 90 787]
+	ElementArc [7815 -8031 2756 2756 180 90 787]
+	ElementArc [7815 7717 2756 2756 0 90 787]
+	ElementArc [7815 7717 2756 2756 270 90 787]
+	ElementArc [7815 7717 2756 2756 180 90 787]
+	ElementArc [7815 7717 2756 2756 90 90 787]
+	ElementArc [-7933 7717 2756 2756 270 90 787]
+	ElementArc [-7933 7717 2756 2756 180 90 787]
+	ElementArc [-7933 7717 2756 2756 90 90 787]
+	ElementArc [-7933 7717 2756 2756 0 90 787]
+	ElementArc [-7933 -8031 2756 2756 180 90 787]
+	ElementArc [-7933 -8031 2756 2756 90 90 787]
+	ElementArc [-7933 -8031 2756 2756 0 90 787]
+	ElementArc [-7933 -8031 2756 2756 270 90 787]
+	ElementArc [-59 -157 6693 6693 270 90 787]
+	ElementArc [-59 -157 6693 6693 180 90 787]
+	ElementArc [-59 -157 6693 6693 90 90 787]
+	ElementArc [-59 -157 6693 6693 0 90 787]
+
+	)
diff --git a/pcblib/tru-hole/TO126.fp b/pcblib/tru-hole/TO126.fp
new file mode 100644
index 0000000..4379d34
--- /dev/null
+++ b/pcblib/tru-hole/TO126.fp
@@ -0,0 +1,35 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:laying
+	Element(0x00 "Transistor" "" "TO126" 80 480 1 100 0x00)
+(
+# From the JEDEC drawing, the pins are rectangular with dimensions
+# 25-35 mil X 15-25 mil
+#
+# This gives a diagonal dimension of 29.2 to 43.0 mils.
+# Pin pitch is 80 to 100 mils.
+# 
+# For a minimum clearance of 10 mils (probably not unreasonable if
+# you are doing a design with leaded parts, this gives a max pad size
+# of 80 mils.  A 52 mil drill will give 14 mil annular ring which should
+# be plenty.
+#
+# The mounting hole is 100 to 130 mils diameter
+	Pin(110 600 80 52 "1" 0x101)
+	Pin(200 600 80 52 "2" 0x01)
+	Pin(290 600 80 52 "3" 0x01) 
+	# Befestigungsbohrung
+	Pin(200 170 130 110 "4" 0x01)
+	# Anschlussdraehte
+	ElementLine(100 600 100 500 30)
+	ElementLine(200 600 200 500 30)
+	ElementLine(300 600 300 500 30)
+	# Gehaeuse
+	ElementLine( 50 500 350 500 20)
+	ElementLine(350 500 350  70 20)
+	ElementLine(350  70  50  70 20)
+	ElementLine( 50  70  50 500 20)
+	Mark(100 600)
+)
diff --git a/pcblib/tru-hole/TO126S.fp b/pcblib/tru-hole/TO126S.fp
new file mode 100644
index 0000000..7049f24
--- /dev/null
+++ b/pcblib/tru-hole/TO126S.fp
@@ -0,0 +1,23 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+	Element(0x00 "Transistor" "" "TO126S" 80 480 1 100 0x00)
+(
+	Pin(110 600 80 52 "1" 0x101)
+	Pin(200 700 80 52 "2" 0x01)
+	Pin(290 600 80 52 "3" 0x01)
+	# Befestigungsbohrung
+	Pin(200 170 130 110 "4" 0x01)
+	# Anschlussdraehte
+	ElementLine(100 600 100 500 30)
+	ElementLine(200 700 200 500 30)
+	ElementLine(300 600 300 500 30)
+	# Gehaeuse
+	ElementLine( 50 500 350 500 20)
+	ElementLine(350 500 350  70 20)
+	ElementLine(350  70  50  70 20)
+	ElementLine( 50  70  50 500 20)
+	Mark(100 600)
+)
diff --git a/pcblib/tru-hole/TO126SW.fp b/pcblib/tru-hole/TO126SW.fp
new file mode 100644
index 0000000..a8b9dd2
--- /dev/null
+++ b/pcblib/tru-hole/TO126SW.fp
@@ -0,0 +1,21 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+	Element(0x00 "Transistor" "" "TO126SW" 270 170 0 100 0x00)
+(
+	Pin(110 100 80 52 "1" 0x101)
+	Pin(200 200 80 52 "2" 0x01)
+	Pin(290 100 80 52 "3" 0x01)
+	# Gehaeuse
+	ElementLine(200 200 200 150 30)
+	ElementLine( 50  50 350  50 20)
+	ElementLine(350  50 350 150 20)
+	ElementLine(350 150  50 150 20)
+	ElementLine( 50 150  50  50 20) 
+	# Bohrung
+	ElementLine(150 50 150 150 10)
+	ElementLine(250 50 250 150 10)
+	Mark(100 100)
+)
diff --git a/pcblib/tru-hole/TO126W.fp b/pcblib/tru-hole/TO126W.fp
new file mode 100644
index 0000000..f64c6ef
--- /dev/null
+++ b/pcblib/tru-hole/TO126W.fp
@@ -0,0 +1,16 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+	Element(0x00 "Transistor" "" "TO126W" 60 170 0 100 0x00)
+(
+	Pin(110 100 80 52 "1" 0x101)
+	Pin(200 100 80 52 "2" 0x01)
+	Pin(290 100 80 52 "3" 0x01)
+	ElementLine(50 50 350 50 20)
+	ElementLine(350 50 350 150 20)
+	ElementLine(350 150 50 150 20)
+	ElementLine(50 150 50 50 20)
+	Mark(100 100)
+)
diff --git a/pcblib/tru-hole/TO18.fp b/pcblib/tru-hole/TO18.fp
new file mode 100644
index 0000000..f1f1cf4
--- /dev/null
+++ b/pcblib/tru-hole/TO18.fp
@@ -0,0 +1,36 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+	Element["" "Transistor" "" "TO18" 10300 11100 6000 7000 0 100 ""]
+(
+# The JEDEC drawing shows a pin diameter of 16-21 mils
+#
+#
+#         ___x_
+#        /     \
+# TO18:  |3   1|     <-- bottom view (supposed to be a circle)
+#        \  2  /
+#          ---
+#       
+# NOTE:  some vendors, ST for example, number the pins
+# differently.  Here we follow the JEDEC drawing.
+#
+# the pins are arranged along a 100 mil diameter
+# circle.  The can outline is 178 to 195 mils
+# for the top of the can and 209 to 230 mils
+# for the bottom edge of the can
+#
+        Pin[0 -5000 5500 3000 6100 3500 "1" "1" ""]
+        Pin[-5000 0 5500 3000 6100 3500 "2" "2" ""]
+        Pin[0 5000 5500 3000 6100 3500 "3" "3" ""]
+# x, y, width, height, start angle, delta angle, thickness
+        ElementArc [0 0 9800 9800 0 360 1000]
+# tab is 28 to 48 mils long, 36 to 46 wide
+# and comes off at an angle of 45 deg clockwise from
+# pin 1 when looking at the top of the board
+        ElementLine [6700 -7900 9400 -10600 1000]
+        ElementLine [7300 -7300 10000 -10000 1000]
+        ElementLine [7900 -6700 10600 -9400 1000]
+        ElementLine [9400 -10600 10600 -9400 1000]
+)
diff --git a/pcblib/tru-hole/TO218.fp b/pcblib/tru-hole/TO218.fp
new file mode 100644
index 0000000..150f2f9
--- /dev/null
+++ b/pcblib/tru-hole/TO218.fp
@@ -0,0 +1,17 @@
+##from:pcb
+##for:diode
+##geo:standing
+Element(0x00 "diode in TO220" "" "TO218" 675 50 3 100 0x00)
+(
+       Pin(88 120 100 60 "1" 0x101)
+       Pin(307 120 100 60 "2" 0x01)
+       Pin(526 120 100 60 "3" 0x01)
+       ElementLine(0 0 0 200 20)
+       ElementLine(0 200 615 200 20)
+       ElementLine(615 200 615 0 20)
+       ElementLine(615 0 0 0 20)
+       ElementLine(0 50 615 50 10)
+       ElementLine(232 0 232 50 10)
+       ElementLine(382 0 382 50 10)
+       Mark(88 260)
+ )
diff --git a/pcblib/tru-hole/TO220.fp b/pcblib/tru-hole/TO220.fp
new file mode 100644
index 0000000..5342924
--- /dev/null
+++ b/pcblib/tru-hole/TO220.fp
@@ -0,0 +1,47 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:laying
+##ref:http://www.zetex.com/3.0/pdf/TO220.pdf
+	Element(0x00 "Transistor" "" "TO220" 50 570 1 100 0x00)
+(
+# I have been unable to locate the JEDEC drawing.  However, refering
+# to  http://www.zetex.com/3.0/pdf/TO220.pdf which claims to be JEDEC
+# compliant, I see that the pins are rectangular with dimensions:
+#
+# 15-40 mils X 16-20 mils which gives a diagonal of
+# 21.9 to 44.7 mils
+#
+# The pin pitch is 90 to 110 mils.
+#
+# The mounting hole is 139 to 160 mils diameter
+	Pin(100 800 90 60 "1" 0x101)
+	Pin(200 800 90 60 "2" 0x01)
+	Pin(300 800 90 60 "3" 0x01)
+	# Befestigungsbohrung
+	Pin(200 130 150 130 "4" 0x01)
+	# Anschlussdraehte
+	ElementLine(100 800 100 620 30)
+	ElementLine(200 800 200 620 30)
+	ElementLine(300 800 300 620 30)
+	# Gehaeuse
+	ElementLine(  0 620 400 620 20)
+	ElementLine(400 620 400 245 20)
+	ElementLine(400 245   0 245 20)
+	ElementLine(  0 245   0 620 20)
+	# Kuehlfahne mit Kerben
+	ElementLine(  0 245 400 245 20)
+	ElementLine(400 245 400 120 20)
+	ElementLine(400 120 385 120 20)
+	ElementLine(385 120 385  50 20)
+	ElementLine(385  50 400  50 20)
+	ElementLine(400  50 400  10 20)
+	ElementLine(400  10   0  10 20)
+	ElementLine(  0  10   0  50 20)
+	ElementLine(  0  50  15  50 20)
+	ElementLine( 15  50  15 120 20)
+	ElementLine( 15 120   0 120 20)
+	ElementLine(  0 120   0 245 20)
+	Mark(200 800)
+)
diff --git a/pcblib/tru-hole/TO220ACSTAND.fp b/pcblib/tru-hole/TO220ACSTAND.fp
new file mode 100644
index 0000000..f37e1c0
--- /dev/null
+++ b/pcblib/tru-hole/TO220ACSTAND.fp
@@ -0,0 +1,16 @@
+##from:pcb
+##for:diode
+##geo:standing
+Element(0x00 "diode in TO220" "" "TO220ACSTAND" 460 50 3 100 0x00)
+(
+       Pin(100 100 80 40 "1" 0x101)
+       Pin(300 100 80 40 "2" 0x01)
+       ElementLine(0 0 0 180 20)
+       ElementLine(0 180 400 180 20)
+       ElementLine(400 180 400 0 20)
+       ElementLine(400 0 0 0 20)
+       ElementLine(0 50 400 50 10)
+       ElementLine(125 0 125 50 10)
+       ElementLine(275 0 275 50 10)
+       Mark(100 220)
+ )
diff --git a/pcblib/tru-hole/TO220S.fp b/pcblib/tru-hole/TO220S.fp
new file mode 100644
index 0000000..57d3830
--- /dev/null
+++ b/pcblib/tru-hole/TO220S.fp
@@ -0,0 +1,36 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:stadning
+	Element(0x00 "Transistor" "" "TO220S" 50 570 1 100 0x00)
+(
+	Pin(100 800 90 60 "1" 0x101)
+	Pin(200 900 90 60 "2" 0x01)
+	Pin(300 800 90 60 "3" 0x01)
+	# Befestigungsbohrung  
+	Pin(200 130 150 130 "4" 0x01)
+	# Anschlussdraehte  
+	ElementLine(100 800 100 620 30)
+	ElementLine(200 900 200 620 30)
+	ElementLine(300 800 300 620 30)
+	# Gehaeuse
+	ElementLine(  0 620 400 620 20)
+	ElementLine(400 620 400 245 20)
+	ElementLine(400 245   0 245 20)
+	ElementLine(  0 245   0 620 20)
+	# Kuehlfahne mit Kerben
+	ElementLine(  0 245 400 245 20)
+	ElementLine(400 245 400 120 20)
+	ElementLine(400 120 385 120 20)
+	ElementLine(385 120 385  50 20)
+	ElementLine(385  50 400  50 20)
+	ElementLine(400  50 400  10 20)
+	ElementLine(400  10   0  10 20)
+	ElementLine(  0  10   0  50 20)
+	ElementLine(  0  50  15  50 20)
+	ElementLine( 15  50  15 120 20)
+	ElementLine( 15 120   0 120 20)
+	ElementLine(  0 120   0 245 20)
+	Mark(200 800)
+)
diff --git a/pcblib/tru-hole/TO220SW.fp b/pcblib/tru-hole/TO220SW.fp
new file mode 100644
index 0000000..c9260a9
--- /dev/null
+++ b/pcblib/tru-hole/TO220SW.fp
@@ -0,0 +1,26 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+	Element(0x00 "Transistor" "" "TO220SW" 0 10 0 100 0x00)
+(
+	Pin(100 200 90 60 "1" 0x101)
+	Pin(200 300 90 60 "2" 0x01)
+	Pin(300 200 90 60 "3" 0x01)
+	# Gehaeuse
+	ElementLine(  0  80 400  80 20)
+	ElementLine(400  80 400 260 20)
+	ElementLine(400 260   0 260 20) 
+	ElementLine(  0 260   0  80 20) 
+	# Kuehlfahne icl. Bohrung
+	ElementLine(  0  80 400  80 20)
+	ElementLine(400  80 400 140 20)
+	ElementLine(400 140   0 140 20)
+	ElementLine(  0 140   0  80 20)
+	ElementLine(130 80 130 140 10)
+	ElementLine(270 80 270 140 10)
+	# Anschlussdraht
+	ElementLine(200 300 200 260 30)
+	Mark(100 200)
+)
diff --git a/pcblib/tru-hole/TO220W.fp b/pcblib/tru-hole/TO220W.fp
new file mode 100644
index 0000000..53ab3c4
--- /dev/null
+++ b/pcblib/tru-hole/TO220W.fp
@@ -0,0 +1,23 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing	Element(0x00 "Transistor" "" "TO220W" 0 10 0 100 0x00)
+(
+	Pin(100 200 90 60 "1" 0x101)
+	Pin(200 200 90 60 "2" 0x01)
+	Pin(300 200 90 60 "3" 0x01)
+	# Gehaeuse
+	ElementLine(  0  80 400  80 20)
+	ElementLine(400  80 400 260 20)
+	ElementLine(400 260   0 260 20) 
+	ElementLine(  0 260   0  80 20) 
+	# Kuehlfahne icl. Bohrung
+	ElementLine(  0  80 400  80 20)
+	ElementLine(400  80 400 140 20)
+	ElementLine(400 140   0 140 20)
+	ElementLine(  0 140   0  80 20)
+	ElementLine(130 80 130 140 10)
+	ElementLine(270 80 270 140 10)
+	Mark(100 200)
+)
diff --git a/pcblib/tru-hole/TO247.fp b/pcblib/tru-hole/TO247.fp
new file mode 100644
index 0000000..d62561b
--- /dev/null
+++ b/pcblib/tru-hole/TO247.fp
@@ -0,0 +1,19 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+Element(0x00 "diode in TO220" "" "TO247" 690 50 3 100 0x00)
+(
+       Pin(96 130 100 60 "1" 0x101)
+       Pin(315 130 100 60 "2" 0x01)
+       Pin(534 130 100 60 "3" 0x01)
+       ElementLine(0 0 0 210 20)
+       ElementLine(0 210 630 210 20)
+       ElementLine(630 210 630 0 20)
+       ElementLine(630 0 0 0 20)
+       ElementLine(0 50 630 50 10)
+       ElementLine(240 0 240 50 10)
+       ElementLine(390 0 390 50 10)
+       Mark(96 270)
+ )
diff --git a/pcblib/tru-hole/TO247_2.fp b/pcblib/tru-hole/TO247_2.fp
new file mode 100644
index 0000000..aff271c
--- /dev/null
+++ b/pcblib/tru-hole/TO247_2.fp
@@ -0,0 +1,17 @@
+##from:pcb
+##for:diode
+##geo:standing
+
+Element(0x00 "diode in TO220" "" "TO247_2" 690 50 3 100 0x00)
+(
+       Pin(96 130 100 60 "1" 0x101)
+       Pin(534 130 100 60 "2" 0x01)
+       ElementLine(0 0 0 210 20)
+       ElementLine(0 210 630 210 20)
+       ElementLine(630 210 630 0 20)
+       ElementLine(630 0 0 0 20)
+       ElementLine(0 50 630 50 10)
+       ElementLine(240 0 240 50 10)
+       ElementLine(390 0 390 50 10)
+       Mark(96 270)
+ )
diff --git a/pcblib/tru-hole/TO251.fp b/pcblib/tru-hole/TO251.fp
new file mode 100644
index 0000000..b89b061
--- /dev/null
+++ b/pcblib/tru-hole/TO251.fp
@@ -0,0 +1,20 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+
+Element(0x00 "diode in TO220" "" "TO251" 325 50 3 100 0x00)
+(
+       Pin(42 50 70 40 "1" 0x101)
+       Pin(132 50 70 40 "2" 0x01)
+       Pin(222 50 70 40 "3" 0x01)
+       ElementLine(0 0 0 100 20)
+       ElementLine(0 100 265 100 20)
+       ElementLine(265 100 265 0 20)
+       ElementLine(265 0 0 0 20)
+       ElementLine(0 50 265 50 10)
+       ElementLine(57 0 57 50 10)
+       ElementLine(207 0 207 50 10)
+       Mark(42 140)
+ )
diff --git a/pcblib/tru-hole/TO264.fp b/pcblib/tru-hole/TO264.fp
new file mode 100644
index 0000000..0c88971
--- /dev/null
+++ b/pcblib/tru-hole/TO264.fp
@@ -0,0 +1,18 @@
+##from:pcb
+##for:diode
+##geo:standing
+
+Element(0x00 "diode in TO220" "" "TO264" 860 50 3 100 0x00)
+(
+       Pin(181 130 100 60 "1" 0x101)
+       Pin(400 130 100 60 "2" 0x01)
+       Pin(619 130 100 60 "3" 0x01)
+       ElementLine(0 0 0 210 20)
+       ElementLine(0 210 800 210 20)
+       ElementLine(800 210 800 0 20)
+       ElementLine(800 0 0 0 20)
+       ElementLine(0 50 800 50 10)
+       ElementLine(325 0 325 50 10)
+       ElementLine(475 0 475 50 10)
+       Mark(181 270)
+ )
diff --git a/pcblib/tru-hole/TO39.fp b/pcblib/tru-hole/TO39.fp
new file mode 100644
index 0000000..8bd6b26
--- /dev/null
+++ b/pcblib/tru-hole/TO39.fp
@@ -0,0 +1,37 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+
+	Element["" "Transistor" "" "TO39" 18800 18800 6000 7000 0 100 ""]
+(
+# The JEDEC drawing shows a pin diameter of 16-21 mils
+#
+#
+#         ___x_
+#        /     \
+# TO39:  |3   1|     <-- bottom view (supposed to be a circle)
+#        \  2  /
+#          ---
+#       
+# NOTE:  some vendors, ST for example, number the pins
+# differently.  Here we follow the JEDEC drawing.
+#
+# the pins are arranged along a 200 mil diameter
+# circle.  The can outline is 315 to 335 mils (320 nom)
+# for the top of the can and 350 to 370 mils (360 nom)
+# for the bottom edge of thecan
+#
+        Pin[0 -10000 5500 3000 6100 3500 "1" "1" "square"]
+        Pin[-10000 0 5500 3000 6100 3500 "2" "2" ""]
+        Pin[0 10000 5500 3000 6100 3500 "3" "3" ""]
+# tab is 29 to 40 mils long, 28 to 34 wide
+# and comes off at an angle of 45 deg clockwise from
+# pin 1 when looking at the top of the board
+        ElementLine [12700 -13900 14800 -16000 1000]
+        ElementLine [13300 -13300 15400 -15400 1000]
+        ElementLine [13900 -12700 16000 -14800 1000]
+        ElementLine [16000 -14800 14800 -16000 1000]
+# x, y, width, height, start angle, delta angle, thickness
+        ElementArc [0 0 18300 18300 0 360 1000]
+        )
diff --git a/pcblib/tru-hole/TO92.fp b/pcblib/tru-hole/TO92.fp
new file mode 100644
index 0000000..9744cdc
--- /dev/null
+++ b/pcblib/tru-hole/TO92.fp
@@ -0,0 +1,23 @@
+##from:pcb
+##for:transistor
+##for:linear
+##for:stabilizer
+##geo:standing
+
+	Element(0x00 "Transistor" "" "TO92" 60 70 0 100 0x00)
+(
+# The JEDEC drawing shows a pin diameter of 16-21 mils
+#
+#
+#         _______
+# TO92:  | 1 2 3 |   <-- bottom view
+#         \_____/
+#       
+# The pin to pin spacing is 100 mils.
+	Pin(250 200 72 42 "1" 0x101)
+	Pin(150 200 72 42 "2" 0x01)
+	Pin(50 200 72 42 "3" 0x01)
+	ElementArc(150 200 100 100 315 270 10)
+	ElementLine( 80 130 220 130 10)
+	Mark(50 200)
+)
diff --git a/scconfig/Makefile b/scconfig/Makefile
new file mode 100644
index 0000000..b897d73
--- /dev/null
+++ b/scconfig/Makefile
@@ -0,0 +1,97 @@
+# --- configuration part --
+
+# - generic configuration -
+SRC=src
+BIN=src
+
+#  what cflags to use to compile scconfig
+USER_CFLAGS = -DGENCALL -Isrc
+#USER_CFLAGS = -Wall -Wextra -g -DGENCALL -Isrc
+
+#  what ldflags to use to link scconfig
+USER_LDFLAGS =
+
+#  in case hooks.c needs to link to something local
+USER_OBJS = src/util/arg_auto_set.o
+
+#  what to build - a ./configure
+all: configure cquote
+
+# This line imports scconfig core and default tests
+include src/default/Makefile.plugin
+
+#
+# - PLUGINS -
+#
+#  Comment this line if you are not interested in c99 features
+include src/c99/Makefile.plugin
+
+#  Comment this line if you do not need script libs to be detected
+include src/scripts/Makefile.plugin
+
+#  Comment this line if you do not need parser libs to be detected
+# include src/parser/Makefile.plugin
+
+#  Comment this line if you do not need to detect parser generators
+include src/parsgen/Makefile.plugin
+
+#  Comment this line if you do not need math related libs
+include src/math/Makefile.plugin
+
+#  Comment this line if you do not need socket/networking
+# include src/socket/Makefile.plugin
+
+#  Comment this line if you do not need user/password API detection
+include src/userpass/Makefile.plugin
+
+#  Comment this line if you do not need gui (X11, toolkits)
+include src/gui/Makefile.plugin
+
+#  Comment this line if you do not need software utility libs (glib)
+include src/sul/Makefile.plugin
+
+#  Uncomment this line if you need menus
+include src/menulib/Makefile.plugin
+
+#  Comment this line if you do not need generator (templating)
+#include src/generator/Makefile.plugin
+
+#  Comment this line if you do not need tmpasm (templating); conflicts with generator
+include src/tmpasm/Makefile.plugin
+
+# --- you shouldn't edit the lines below ---
+OBJS = $(USER_OBJS) hooks.o $(DEFAULT_NOMAIN_OBJS) $(SCRIPT_OBJS) $(PARSER_OBJS) $(GENERATOR_OBJS) $(TMPASM_OBJS) $(C99_OBJS) $(PARSGEN_OBJS) $(MATH_OBJS) $(SOCKET_OBJS) $(USERPASS_OBJS) $(GUI_OBJS) $(SUL_OBJS)
+CFLAGS = $(USER_CFLAGS) $(DEFAULT_CFLAGS) $(SCRIPT_CFLAGS) $(PARSER_CFLAGS) $(GENERATOR_CFLAGS) $(TMPASM_CFLAGS) $(C99_CFLAGS) $(PARSGEN_CFLAGS) $(MATH_CFLAGS) $(SOCKET_CFLAGS) $(USERPASS_CFLAGS) $(GUI_CFLAGS)  $(SUL_CFLAGS) $(MENULIB_CFLAGS) -Isrc/default
+LDFLAGS = $(USER_LDFLAGS) $(DEFAULT_LDFLAGS) $(SCRIPT_LDFLAGS) $(PARSER_LDFLAGS) $(GENERATOR_LDFLAGS) $(TMPASM_LDFLAGS) $(C99_LDFLAGS) $(PARSGEN_LDFLAGS) $(MATH_LDFLAGS) $(SOCKET_LDFLAGS) $(USERPASS_LDFLAGS) $(GUI_LDFLAGS) $(SUL_LDFLAGS) $(MENULIB_LDFLAGS)
+
+all: configure revtest
+
+revtest: revtest.o
+	$(CC) -o revtest revtest.o
+
+revtest.o: revtest.c  Rev.h
+	$(CC) -c $(CFLAGS) -o revtest.o revtest.c
+
+configure: $(OBJS)  $(DEFAULT_MAIN_OBJS)
+	$(CC) -o configure $(OBJS)  $(DEFAULT_MAIN_OBJS)
+
+menuconfig: menucfg
+	./menucfg
+
+menucfg: $(OBJS) $(MENULIB_OBJS) src/default/main_lib.o src/default/main_custom_args.o menucfg.o src/util/arg_auto_menu.o src/util/arg_auto_set.o
+	$(CC) -o menucfg $(OBJS) $(MENULIB_OBJS) menucfg.o src/default/main_lib.o src/default/main_custom_args.o src/util/arg_auto_menu.o
+
+menucfg.o: menucfg.c
+	$(CC) -c $(CFLAGS) -o menucfg.o menucfg.c
+
+hooks.o: plugin_3state.h plugins.h Rev.h
+
+src/util/arg_auto_set.o: src/util/arg_auto_set.c src/util/arg_auto_set.h
+	$(CC) -c $(CFLAGS) -o src/util/arg_auto_set.o src/util/arg_auto_set.c
+
+
+cquote: cquote.c
+
+clean:
+	rm $(OBJS) $(DEFAULT_MAIN_OBJS) configure revtest revtest.o
+
diff --git a/scconfig/Makefile.comp.inc b/scconfig/Makefile.comp.inc
new file mode 100644
index 0000000..f5de060
--- /dev/null
+++ b/scconfig/Makefile.comp.inc
@@ -0,0 +1,60 @@
+# Generate Makefile code to compile .c -> .o
+# Arguments:
+#  /local/comp/OBJS     list of .o files (assuming each have at least a .c)
+#  /local/comp/OBJS_C99 list of .o files (assuming each have at least a .c), for non-c89 compilation
+
+print {
+### explicit rules for .c -> .o ###
+}
+
+append /local/comp/CFLAGS {}
+
+foreach /local/o in /local/comp/OBJS
+put /local/c /local/o
+sub /local/c {.o$} {.c}
+switch /local/c
+	case {.*_y.c}
+		put /local/extinc [@ -I@/local/c@@]
+		sub /local/extinc {/[^/]*$} {}
+		end
+	case {.*_l.c}
+		put /local/extinc [@ -I@/local/c@@]
+		sub /local/extinc {/[^/]*$} {}
+		end
+	default
+		put /local/extinc {}
+		end
+end
+print [@
+@/local/o@: @/local/c@
+	$(CC) -c $(C89FLAGS) @/local/comp/CFLAGS@@/local/extinc@ -o @/local/o@ $<
+@]
+end
+
+append /local/comp/OBJS_C99 {}
+foreach /local/o in /local/comp/OBJS_C99
+put /local/c /local/o
+sub /local/c {.o$} {.c}
+switch /local/c
+	case {.*_y.c}
+		put /local/extinc [@ -I@/local/c@@]
+		sub /local/extinc {/[^/]*$} {}
+		end
+	case {.*_l.c}
+		put /local/extinc [@ -I@/local/c@@]
+		sub /local/extinc {/[^/]*$} {}
+		end
+	default
+		put /local/extinc {}
+		end
+end
+print [@
+@/local/o@: @/local/c@
+	$(CC) -c $(CFLAGS) @/local/comp/CFLAGS@@/local/extinc@ -o @/local/o@ $<
+@]
+end
+
+put /local/comp/OBJS {}
+put /local/comp/OBJS_C99 {}
+put /local/comp/CFLAGS {}
+
diff --git a/scconfig/Makefile.comp_var.inc b/scconfig/Makefile.comp_var.inc
new file mode 100644
index 0000000..98f652e
--- /dev/null
+++ b/scconfig/Makefile.comp_var.inc
@@ -0,0 +1,31 @@
+# Generate Makefile code to compile .c -> .o
+# Arguments:
+#  /local/comp/OBJS     list of .o files (assuming each have at least a .c)
+#  /local/comp/OBJS_C99 list of .o files (assuming each have at least a .c) for non-c89 compilation
+
+put /local/comp/output {}
+append /local/comp/CFLAGS {}
+
+foreach /local/o in /local/comp/OBJS
+put /local/c /local/o
+sub /local/c {.o$} {.c}
+append /local/comp/output [@
+@/local/o@: @/local/c@
+	$(CC) -c $(C89FLAGS) @/local/comp/CFLAGS@ -o @/local/o@ $<
+@]
+end
+
+append /local/comp/OBJS_C99 {}
+foreach /local/o in /local/comp/OBJS_C99
+put /local/c /local/o
+sub /local/c {.o$} {.c}
+append /local/comp/output [@
+@/local/o@: @/local/c@
+	$(CC) -c $(CFLAGS) @/local/comp/CFLAGS@ -o @/local/o@ $<
+@]
+end
+
+put /local/comp/OBJS {}
+put /local/comp/OBJS_C99 {}
+put /local/comp/CFLAGS {}
+
diff --git a/scconfig/Makefile.dep.inc b/scconfig/Makefile.dep.inc
new file mode 100644
index 0000000..7f8c088
--- /dev/null
+++ b/scconfig/Makefile.dep.inc
@@ -0,0 +1,41 @@
+# Generate Makefile code that can generate Makefile.dep
+# Arguments:
+#  /local/dep/CFLAGS   CFLAGS used for compiling
+#  /local/dep/SRCS     list of c soures
+
+print [@
+
+### generate dependencies (requires gcc) ###
+FORCE:
+
+include Makefile.dep
+
+dep: FORCE
+	echo "### Generated file, do not edit, run make dep ###" > Makefile.dep
+	echo "" >> Makefile.dep
+@]
+
+gsub /local/dep/CFLAGS {-I} {-isystem }
+gsub /local/dep/CFLAGS {-isystem [.][.]} {-I ..}
+gsub /local/dep/CFLAGS {-isystem [.]} {-I .}
+sortuniq /local/dep/SRCS_SORTED /local/dep/SRCS
+
+foreach /local/c in /local/dep/SRCS_SORTED
+	put /local/o /local/c
+	sub {/local/o} {.c$} {.o}
+	switch /local/c
+		case {/src_plugins/} end
+		case {$(PLUGDIR)}
+			print [@	gcc -MT @/local/o@ -MM @/local/c@ @/local/dep/CFLAGS@ @/local/pcb/DEPCFLAGS@ >> Makefile.dep
+@]; end
+		case {$(SRC_3RD_DIR)}
+			print [@	gcc -MT @/local/o@ -MM @/local/c@ @/local/dep/CFLAGS@ >> Makefile.dep
+@]; end
+		case {../src_3rd/}
+			print [@	gcc -MT @/local/o@ -MM @/local/c@ @/local/dep/CFLAGS@ >> Makefile.dep
+@]; end
+
+		default print [@	gcc -MM @/local/c@ @/local/dep/CFLAGS@ >> Makefile.dep
+@]; end
+	end
+end
diff --git a/scconfig/README b/scconfig/README
new file mode 100644
index 0000000..d4f9097
--- /dev/null
+++ b/scconfig/README
@@ -0,0 +1,3 @@
+The configuration system that runs ./configure.
+
+The engine is scconfig.
diff --git a/scconfig/Rev.h b/scconfig/Rev.h
new file mode 100644
index 0000000..62b415a
--- /dev/null
+++ b/scconfig/Rev.h
@@ -0,0 +1 @@
+static const int myrev = 4450;
diff --git a/scconfig/Rev.tab b/scconfig/Rev.tab
new file mode 100644
index 0000000..1d9b81a
--- /dev/null
+++ b/scconfig/Rev.tab
@@ -0,0 +1,23 @@
+4450	configure	io_lihata plugin config and 3rd party lib dependency fix
+4398	configure	io_kicad plugin interdeps and enable io_kicad by default
+4375	configure	custom output style support in io_lihata
+4321	configure	imported lihata persistent save lib in src_3rd as svn extern
+4160	configure	lib_* plugins (renames, s-expression)
+4063	configure	netlist.c split and cleanup
+4019	configure	case sensitive string regex for query
+4011	configure	sphash available for all plugins (needed by query first)
+4002	configure	auto-enable the query plugin
+3926	configure	advanced search in gtk, imported 10 old plugins
+3841	configure	link string->int hash table (for the query language)
+3822	configure	new global struct: obj_any_t that will allow a query to list objects
+3745	configure	gsch2pcb-rnd footprint lib initialization bug fixed
+3695	configure	bind signals to emergency save so that a copy of the in-memory PCB is saved on most crashes
+3513	configure	rename detect gcc attribute "unused" and use it for static inline local functions
+3510	configure	rename the debug plugin to diag plugin to avoid confusion with --debug in ./configure
+3509	configure	solve non-c89-compliant function pointer <-> data pointer casts
+3361	configure	cleanup: layer.c is compiled into layer.o now
+3339	configure	C89: round() is detected by scconfig
+3183	distclean,configure	C89 switchover
+3179	configure	C89 correctness: do not depend on hardwired long long, detect it
+2985	configure	Revcheck installed in Makefiles.
+2984	configure	Introduction of the config-rev system.
diff --git a/scconfig/cquote.c b/scconfig/cquote.c
new file mode 100644
index 0000000..31c9e66
--- /dev/null
+++ b/scconfig/cquote.c
@@ -0,0 +1,93 @@
+/*
+    pcb-rnd - quote file and pack it in a C string (ANSI C code)
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdio.h>
+
+void copy(const char *inds)
+{
+	int c, nl = 0, qt = 1, ind = 1;
+	while((c = getc(stdin)) != EOF) {
+		if (ind) {
+			printf("%s", inds);
+			if (!nl)
+				printf("   ");
+			ind = 0;
+		}
+		if (nl) {
+			printf("NL ");
+			nl = 0;
+		}
+		if (qt) {
+			printf("\"");
+			qt = 0;
+		}
+		switch(c) {
+			case '\t': printf("	");  break;
+			case '\n': printf("\"\n"); nl = qt = ind = 1; break;
+			case '\r': break;
+			case '\\': printf("\\\\");  break;
+			case '"': printf("\\\"");  break;
+			default:
+				if ((c < 32) || (c>126))
+					printf("\\%3o", c);
+				else
+					putc(c, stdout);
+		}
+	}
+	if (!qt)
+		printf("\"");
+	if (nl) {
+		if (ind)
+			printf("%s", inds);
+		printf("NL");
+	}
+	printf(";\n");
+}
+
+
+
+int main(int argc, char *argv[])
+{
+	char *varname = "quoted_file";
+	char *inds = "\t";
+	char *banner = "/* Autogenerated by cquote.c - DO NOT EDIT */\n";
+	char *cmd, *arg;
+	int n;
+
+	for(n = 1; n < argc; n++) {
+		cmd = argv[n];
+		arg = argv[n+1];
+		while(*cmd == '-') cmd++;
+		switch(*cmd) {
+			case 'n': varname = arg; n++; break;
+			case 'i': inds = arg; n++; break;
+		}
+	}
+
+	printf("%s", banner);
+
+	printf("#define NL \"\\n\"\n");
+
+	printf("const char *%s = \\\n", varname);
+	copy(inds);
+	return 0;
+}
diff --git a/scconfig/gen_conf.sh b/scconfig/gen_conf.sh
new file mode 100755
index 0000000..fcc71bb
--- /dev/null
+++ b/scconfig/gen_conf.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+# TODO: rewrite this in C for better portability
+
+if test -z "$AWK"
+then
+	AWK="awk"
+fi
+
+$AWK -v "docdir=$1" '
+	BEGIN {
+		level = -1
+		q = "\""
+		cm = ","
+	}
+
+	function doc_head(fn, path)
+	{
+		if (fn in DOCS)
+			return
+		DOCS[fn]++
+		print "<html><body>" > fn
+		print "<h1>pcb-rnd conf tree</h1>" > fn
+		print "<h2>subtree: " path "</h2>" > fn
+		print "<table border=1>" > fn
+		print "<tr><th>node name <th> type <td> flags <td> description" > fn
+
+	}
+
+	function doc_foot(fn)
+	{
+		print "</table></body></html>" > fn
+	}
+
+	/[{]/ { level++ }
+
+	/struct.*/ {
+		name = $0
+		sub(".*struct[ \t]*", "", name)
+		sub("[ \t{].*", "", name)
+		if (level == 0)
+			path = ""
+		else
+			path = path "/" name
+	}
+
+	/^[ \t]*[\/][\/]/ { next }
+
+	/CFT_/ {
+		if (level < 1)
+			next
+
+		name=$0
+		sub("^.*CFT_", "CFN_", name)
+		sub(";.*", "", name)
+		type=name
+		sub("[ \t]*[^ \t]*$", "", type)
+		sub("[^ \t]*[ \t]*", "", name)
+		array = (name ~ "[[]")
+		if (array)
+			sub("[[].*$", "", name)
+		id = path
+		sub("^/", "", id)
+		gsub("/", ".", id)
+		id = id "." name
+
+		desc = $0
+		if (desc ~ "/[*]") {
+			sub("^.*/[*]", "", desc)
+			sub("[*]/.*$", "", desc)
+			sub("^[ \t]*", "", desc)
+			sub("[ \t]*$", "", desc)
+		}
+		else
+			desc = ""
+		if (desc == "")
+			desc = "<" name ">"
+
+		flags = "";
+		while(match(desc, "@[a-zA-Z_]+")) {
+			flag=substr(desc, RSTART, RLENGTH)
+			sub("[ \t]*" flag "[ \t]*", " ", desc)
+			sub("^@", "",flag)
+			flag="CFF_" toupper(flag)
+			if (flags == "")
+				flags = flag
+			else
+				flags = flags " | " flag
+		}
+		if (flags == "")
+			flags = 0;
+		sub("^[ \t]*", "", desc)
+		sub("[ \t]*$", "", desc)
+
+		path_tmp=path
+		sub("^/", "", path_tmp)
+		printf("conf_reg(%-36s %s %-16s %-25s %-25s %s, %s)\n", id cm, (array ? "array, " : "scalar,"), type cm, q path_tmp q cm, q name q cm, q desc q, flags)
+		if (docdir != "") {
+			path_tmp2 = path_tmp
+			gsub("/", "_", path_tmp2)
+			fn = docdir "/" path_tmp2 ".html"
+			doc_head(fn, path_tmp)
+			type2 = tolower(type)
+			sub("^cfn_", "", type2)
+
+			print "<tr><td>", name, "<td><a href=\"" type ".html\">", type2, "</a><td>", flags, "<td>", desc > fn
+		}
+	}
+
+	/[}]/ {
+		level--
+		sub("[/][^/]*$", "", path)
+	}
+
+
+	END {
+		if (level != -1)
+			print "Error: unbalanced braces" > "/dev/stderr"
+		for(fn in DOCS)
+			doc_foot(fn)
+	}
+'
diff --git a/scconfig/gen_core_lists.sh b/scconfig/gen_core_lists.sh
new file mode 100755
index 0000000..3062f3d
--- /dev/null
+++ b/scconfig/gen_core_lists.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+if test -z "$AWK"
+then
+	AWK="awk"
+fi
+
+for f in $*
+do
+	echo "___name___ $f"
+	cat $f
+done | $AWK '
+BEGIN {
+	q = "\""
+	print "/**** DO NOT EDIT - automatically generated by gen_core_lists.sh ****/"
+	print ""
+}
+
+/^___name___/ {
+	fullname = $2
+	basename = $2
+	sub("/[^/]*$", "", basename)
+	if (fullname ~ "src_plugins/")
+		TYPE[basename]="plugdir"
+}
+
+/^type=/ {
+	type = $0
+	sub("^type=", "", type)
+	TYPE[basename] = type
+	print "/* ", type, basename " */"
+}
+
+
+/^REGISTER/ {
+	LIST[basename] = LIST[basename] $0 "\n"
+}
+
+END {
+	for(n in LIST) {
+		hn = n
+		sub("^hid/", "", hn)
+#		if (hn in HIDNAME_FIXUP)
+#			hn = HIDNAME_FIXUP[hn]
+
+		print "/* " n  " (" TYPE[n] ") */"
+		if (TYPE[n] == "gui")
+			print "if ((gui != NULL) && (strcmp(gui->name, " q hn q ") == 0)) {"
+		if (TYPE[n] == "plugdir") {
+			vname = LIST[n]
+			sub("REGISTER_ACTIONS.*[(]", "", vname)
+			sub("[)].*[\\n\\r]*", "", vname)
+			print "extern HID_Action " vname "[];"
+		}
+		print LIST[n]
+		if (TYPE[n] == "gui")
+			print "}"
+	}
+}
+
+'
+
diff --git a/scconfig/hooks.c b/scconfig/hooks.c
new file mode 100644
index 0000000..c911e04
--- /dev/null
+++ b/scconfig/hooks.c
@@ -0,0 +1,732 @@
+#include <stdio.h>
+#include <string.h>
+#include "arg.h"
+#include "log.h"
+#include "dep.h"
+#include "libs.h"
+#include "db.h"
+#include "tmpasm.h"
+#include "tmpasm_scconfig.h"
+#include "util/arg_auto_set.h"
+#include "Rev.h"
+
+#define version "1.1.3"
+
+#include "plugin_3state.h"
+
+int want_intl = 0, want_coord_bits;
+
+const arg_auto_set_t disable_libs[] = { /* list of --disable-LIBs and the subtree they affect */
+	{"disable-xrender",   "libs/gui/xrender",             arg_lib_nodes, "$do not use xrender for lesstif"},
+	{"disable-xinerama",  "libs/gui/xinerama",            arg_lib_nodes, "$do not use xinerama for lesstif"},
+	{"disable-gd",        "libs/gui/gd",                  arg_lib_nodes, "$do not use gd (many exporters need it)"},
+	{"disable-gd-gif",    "libs/gui/gd/gdImageGif",       arg_lib_nodes, "$no gif support in the png exporter"},
+	{"disable-gd-png",    "libs/gui/gd/gdImagePng",       arg_lib_nodes, "$no png support in the png exporter"},
+	{"disable-gd-jpg",    "libs/gui/gd/gdImageJpeg",      arg_lib_nodes, "$no jpeg support in the png exporter"},
+	{"disable-bison",     "parsgen/bison",                arg_lib_nodes, "$do not regenerate language files"},
+	{"enable-dmalloc",    "/local/pcb/want_dmalloc",      arg_true,      "$compile with lib dmalloc"},
+	{"disable-dmalloc",   "/local/pcb/want_dmalloc",      arg_false,     "$compile without lib dmalloc"},
+
+#undef plugin_def
+#undef plugin_header
+#undef plugin_dep
+#define plugin_def(name, desc, default_, all_) plugin3_args(name, desc)
+#define plugin_header(sect)
+#define plugin_dep(plg, on)
+#include "plugins.h"
+
+	{NULL, NULL, NULL, NULL}
+};
+
+static void help1(void)
+{
+	printf("./configure: configure pcb-rnd.\n");
+	printf("\n");
+	printf("Usage: ./configure [options]\n");
+	printf("\n");
+	printf("options are:\n");
+	printf(" --prefix=path              change installation prefix from /usr to path\n");
+	printf(" --debug                    build full debug version (-g -O0, extra asserts)\n");
+	printf(" --symbols                  include symbols (add -g, but no -O0 or extra asserts)\n");
+	printf(" --coord=32|64              set coordinate integer type's width in bits\n");
+	printf(" --dot_pcb_pcb=path         .pcb-rnd config path under $HOME/\n");
+	printf(" --workaround-gtk-ctrl      enable GTK control key query workaround\n");
+	printf(" --all=plugin               enable all working plugins for dynamic load\n");
+	printf(" --all=buildin              enable all working plugins for static link\n");
+	printf(" --all=disable              disable all plugins (compile core only)\n");
+}
+
+static void help2(void)
+{
+	printf("\n");
+	printf("Some of the --disable options will make ./configure to skip detection of the given feature and mark them \"not found\".");
+	printf("\n");
+}
+
+char *repeat = NULL;
+#define report_repeat(msg) \
+do { \
+	report(msg); \
+	if (repeat != NULL) { \
+		char *old = repeat; \
+		repeat = str_concat("", old, msg, NULL); \
+		free(old); \
+	} \
+	else \
+		repeat = strclone(msg); \
+} while(0)
+
+static void all_plugin_select(const char *state);
+
+/* Runs when a custom command line argument is found
+ returns true if no further argument processing should be done */
+int hook_custom_arg(const char *key, const char *value)
+{
+	if (strcmp(key, "prefix") == 0) {
+		report("Setting prefix to '%s'\n", value);
+		put("/local/prefix", strclone(value));
+		return 1;
+	}
+	if (strcmp(key, "debug") == 0) {
+		put("/local/pcb/debug", strue);
+		return 1;
+	}
+	if (strcmp(key, "symbols") == 0) {
+		put("/local/pcb/symbols", strue);
+		return 1;
+	}
+	if (strcmp(key, "coord") == 0) {
+		int v = atoi(value);
+		if ((v != 32) && (v != 64)) {
+			report("ERROR: --coord needs to be 32 or 64.\n");
+			exit(1);
+		}
+		put("/local/pcb/coord_bits", value);
+		want_coord_bits = v;
+		return 1;
+	}
+	if (strcmp(key, "all") == 0) {
+		if ((strcmp(value, sbuildin) == 0) || (strcmp(value, splugin) == 0) || (strcmp(value, sdisable) == 0)) {
+			all_plugin_select(value);
+			return 1;
+		}
+		report("Error: unknown --all argument: %s\n", value);
+		exit(1);
+	}
+	if (strcmp(key, "coord") == 0)
+		put("/local/pcb/dot_pcb_rnd", value);
+	if (strncmp(key, "workaround-", 11) == 0) {
+		const char *what = key+11;
+		if (strcmp(what, "gtk-ctrl") == 0) append("/local/pcb/workaround_defs", "\n#define PCB_WORKAROUND_GTK_CTRL 1");
+		else {
+			report("ERROR: unknown workaround '%s'\n", what);
+			exit(1);
+		}
+		return 1;
+	}
+	if ((strcmp(key, "with-intl") == 0) || (strcmp(key, "enable-intl") == 0)) {
+		want_intl = 1;
+		return 1;
+	}
+	else if (strcmp(key, "help") == 0) {
+		help1();
+		arg_auto_print_options(stdout, " ", "                         ", disable_libs);
+		help2();
+		exit(0);
+	}
+
+	return arg_auto_set(key, value, disable_libs);
+}
+
+/* execute plugin dependency statements, depending on "require":
+	require = 0 - attempt to mark any dep as buildin
+	require = 1 - check if dependencies are met, disable plugins that have
+	              unmet deps
+*/
+void plugin_dep1(int require, const char *plugin, const char *deps_on)
+{
+	char buff[1024];
+	const char *st_plugin, *st_deps_on;
+
+	sprintf(buff, "/local/pcb/%s/controls", plugin);
+	st_plugin = get(buff);
+	sprintf(buff, "/local/pcb/%s/controls", deps_on);
+	st_deps_on = get(buff);
+
+	if (require) {
+		if ((strcmp(st_plugin, sbuildin) == 0) || (strcmp(st_plugin, splugin) == 0)) {
+			if (strcmp(st_deps_on, sbuildin) != 0) {
+				sprintf(buff, "WARNING: disabling %s because the %s is not enabled as a buildin...\n", plugin, deps_on);
+				report_repeat(buff);
+				sprintf(buff, "disable-%s", plugin);
+				hook_custom_arg(buff, NULL);
+			}
+		}
+	}
+	else {
+		if ((strcmp(st_plugin, sbuildin) == 0) || (strcmp(st_plugin, splugin) == 0))
+			put(buff, sbuildin);
+	}
+}
+
+static void all_plugin_select(const char *state)
+{
+	char buff[1024];
+
+#undef plugin_def
+#undef plugin_header
+#undef plugin_dep
+#define plugin_def(name, desc, default_, all_) \
+	if (all_) { \
+		sprintf(buff, "/local/pcb/%s/controls", name); \
+		put(buff, state); \
+	}
+#define plugin_header(sect)
+#define plugin_dep(plg, on)
+#include "plugins.h"
+}
+
+void plugin_deps(int require)
+{
+#undef plugin_def
+#undef plugin_header
+#undef plugin_dep
+#define plugin_def(name, desc, default_, all_)
+#define plugin_header(sect)
+#define plugin_dep(plg, on) plugin_dep1(require, plg, on);
+#include "plugins.h"
+}
+
+
+/* Runs before anything else */
+int hook_preinit()
+{
+	return 0;
+}
+
+/* Runs after initialization */
+int hook_postinit()
+{
+	db_mkdir("/local");
+	db_mkdir("/local/pcb");
+
+	/* DEFAULTS */
+	put("/local/prefix", "/usr/local");
+
+#undef plugin_def
+#undef plugin_header
+#undef plugin_dep
+#define plugin_def(name, desc, default_, all_) plugin3_default(name, default_)
+#define plugin_header(sect)
+#define plugin_dep(plg, on)
+#include "plugins.h"
+
+	put("/local/pcb/debug", sfalse);
+	put("/local/pcb/symbols", sfalse);
+	put("/local/pcb/coord_bits", "32");
+	want_coord_bits = 32;
+	put("/local/pcb/dot_pcb_rnd", ".pcb-rnd");
+
+	return 0;
+}
+
+/* Runs after all arguments are read and parsed */
+int hook_postarg()
+{
+	plugin_deps(0);
+	return 0;
+}
+
+
+
+/* Runs when things should be detected for the host system */
+int hook_detect_host()
+{
+	require("fstools/ar",  0, 1);
+	require("fstools/mkdir", 0, 1);
+	require("fstools/rm",  0, 1);
+	require("fstools/cp",  0, 1);
+	require("fstools/ln",  0, 1);
+	require("fstools/mkdir",  0, 1);
+
+/* until we rewrite the generators in C */
+	require("fstools/awk",  0, 1);
+
+	if (istrue(get("/local/pcb/debug")))
+		require("cc/argstd/*", 0, 0);
+
+	require("cc/func_attr/unused/*", 0, 0);
+	require("cc/inline", 0, 0);
+
+	return 0;
+}
+
+int safe_atoi(const char *s)
+{
+	if (s == NULL)
+		return 0;
+	return atoi(s);
+}
+
+/* Runs when things should be detected for the target system */
+int hook_detect_target()
+{
+	int want_glib = 0, want_gtk, want_gd, want_stroke, need_inl = 0;
+
+	want_gtk    = plug_is_enabled("hid_gtk");
+	want_gd     = plug_is_enabled("export_png") ||  plug_is_enabled("export_nelma") ||  plug_is_enabled("export_gcode");
+	want_stroke = plug_is_enabled("stroke");
+
+	require("cc/fpic",  0, 1);
+	require("signal/names/*",  0, 0);
+	require("libs/fs/mkdtemp/*",  0, 0);
+	require("libs/fs/realpath/*",  0, 0);
+	require("libs/fs/readdir/*",  0, 1);
+	require("libs/math/rint/*",  0, 0);
+	require("libs/math/round/*",  0, 0);
+	require("libs/userpass/getpwuid/*",  0, 0);
+
+	if (require("libs/ldl",  0, 0) != 0) {
+		if (require("libs/LoadLibrary",  0, 0) != 0) {
+			report_repeat("\nERROR: no dynamic linking found on your system. Can not compile pcb-rnd.\n\n");
+			return 1;
+		}
+	}
+
+	if (require("libs/proc/wait",  0, 0) != 0) {
+		if (require("libs/proc/_spawnvp",  0, 0) != 0) {
+			report_repeat("\nERROR: no fork or _spawnvp. Can not compile pcb-rnd.\n\n");
+			return 1;
+		}
+	}
+
+	if (require("libs/fs/_mkdir",  0, 0) != 0) {
+		if (require("libs/fs/mkdir",  0, 0) != 0) {
+			report_repeat("\nERROR: no mkdir() or _mkdir(). Can not compile pcb-rnd.\n\n");
+			return 1;
+		}
+	}
+
+	if (require("libs/fs/getcwd/*",  0, 0) != 0)
+		if (require("libs/fs/_getcwd/*",  0, 0) != 0)
+			if (require("libs/fs/getwd/*",  0, 0) != 0) {
+				report_repeat("\nERROR: Can not find any getcwd() variant.\n\n");
+				return 1;
+			}
+
+	if (want_intl) {
+		require("libs/sul/gettext/presents", 0, 0);
+		if (!istrue(get("libs/sul/gettext/presents"))) {
+			report_repeat("\nERROR: intl support explicitly requested but gettext is not found on your system.\n\n");
+			return 1;
+		}
+		put("/local/pcb/want_nls", strue);
+	}
+	else
+		put("/local/pcb/want_nls", sfalse);
+
+	if (want_stroke) {
+		require("libs/gui/libstroke/presents", 0, 0);
+		if (!istrue(get("libs/gui/libstroke/presents"))) {
+			report_repeat("WARNING: Since there's no libstroke found, disabling the stroke plugin...\n");
+			hook_custom_arg("disable-stroke", NULL);
+		}
+	}
+
+	if (want_gtk) {
+		require("libs/gui/gtk2/presents", 0, 0);
+		if (!istrue(get("libs/gui/gtk2/presents"))) {
+			report_repeat("WARNING: Since there's no libgtk2 found, disabling the gtk hid...\n");
+			hook_custom_arg("disable-hid_gtk", NULL);
+		}
+	}
+
+	if (plug_is_enabled("hid_lesstif")) {
+		require("libs/gui/lesstif2/presents", 0, 0);
+		if (istrue(get("libs/gui/lesstif2/presents"))) {
+			require("libs/gui/xinerama/presents", 0, 0);
+			require("libs/gui/xrender/presents", 0, 0);
+		}
+		else {
+			report_repeat("WARNING: Since there's no lesstif2 found, disabling the lesstif HID and xinerama and xrender...\n");
+			hook_custom_arg("disable-xinerama", NULL);
+			hook_custom_arg("disable-xrender", NULL);
+			hook_custom_arg("disable-hid_lesstif", NULL);
+		}
+	}
+	else {
+		hook_custom_arg("disable-xinerama", NULL);
+		hook_custom_arg("disable-xrender", NULL);
+	}
+
+
+	if (want_gtk)
+		want_glib = 1;
+
+	if (plug_is_enabled("toporouter"))
+		want_glib = 1;
+
+	if (plug_is_enabled("export_dsn"))
+		want_glib = 1;
+
+	if (plug_is_enabled("puller"))
+		want_glib = 1;
+
+	if (want_glib) {
+		require("libs/sul/glib", 0, 0);
+		if (!istrue(get("libs/sul/glib/presents"))) {
+			if (want_gtk) {
+				report_repeat("WARNING: Since GLIB is not found, disabling the GTK HID...\n");
+				hook_custom_arg("disable-gtk", NULL);
+			}
+			if (plug_is_enabled("toporouter")) {
+				report_repeat("WARNING: Since GLIB is not found, disabling the toporouter...\n");
+				hook_custom_arg("disable-toporouter", NULL);
+			}
+			if (plug_is_enabled("puller")) {
+				report_repeat("WARNING: Since GLIB is not found, disabling the puller...\n");
+				hook_custom_arg("disable-puller", NULL);
+			}
+			if (plug_is_enabled("export_dsn")) {
+				report_repeat("WARNING: Since GLIB is not found, disabling the dsn exporter...\n");
+				hook_custom_arg("disable-export_dsn", NULL);
+			}
+		}
+	}
+	else {
+		report("No need for glib, skipping GLIB detection\n");
+		put("libs/sul/glib/presents", "false");
+		put("libs/sul/glib/cflags", "");
+		put("libs/sul/glib/ldflags", "");
+	}
+
+	if (!istrue(get("libs/sul/glib/presents"))) {
+		/* Makefile templates will still reference these variables, they should be empty */
+		put("libs/sul/glib/cflags", "");
+		put("libs/sul/glib/ldflags", "");
+	}
+
+	if (want_gd) {
+		require("libs/gui/gd/presents", 0, 0);
+		if (!istrue(get("libs/gui/gd/presents"))) {
+			report_repeat("WARNING: Since there's no libgd, disabling gd based exports (png, nelma, gcode)...\n");
+			hook_custom_arg("disable-gd-gif", NULL);
+			hook_custom_arg("disable-gd-png", NULL);
+			hook_custom_arg("disable-gd-jpg", NULL);
+			hook_custom_arg("disable-export_png", NULL);
+			hook_custom_arg("disable-export_nelma", NULL);
+			hook_custom_arg("disable-export_gcode", NULL);
+			want_gd = 0;
+			goto disable_gd_formats;
+		}
+		else {
+			require("libs/gui/gd/gdImagePng/presents", 0, 0);
+			require("libs/gui/gd/gdImageGif/presents", 0, 0);
+			require("libs/gui/gd/gdImageJpeg/presents", 0, 0);
+			if (!istrue(get("libs/gui/gd/gdImagePng/presents"))) {
+				report_repeat("WARNING: libgd is installed, but its png code fails, some exporters will be compiled with reduced functionality; exporters affected: export_nelma, export_gcode\n");
+			}
+		}
+	}
+	else {
+		put("libs/gui/gd/presents", sfalse);
+		disable_gd_formats:;
+		put("libs/gui/gd/gdImagePng/presents", sfalse);
+		put("libs/gui/gd/gdImageGif/presents", sfalse);
+		put("libs/gui/gd/gdImageJpeg/presents", sfalse);
+	}
+
+	/* generic utils for Makefiles */
+	require("sys/ext_exe", 0, 1);
+	require("sys/sysid", 0, 1);
+
+	/* options for config.h */
+	require("sys/path_sep", 0, 1);
+	require("sys/types/size/*", 0, 1);
+	require("cc/rdynamic", 0, 0);
+	require("libs/snprintf", 0, 0);
+	require("libs/vsnprintf", 0, 0);
+	require("libs/fs/getcwd", 0, 0);
+	require("libs/fs/stat/macros/*", 0, 0);
+
+	if (istrue(get("/local/pcb/want_dmalloc"))) {
+		require("libs/sul/dmalloc/*", 0, 1);
+	}
+	else {
+		put("libs/sul/dmalloc/presents", sfalse);
+		put("libs/sul/dmalloc/cflags", "");
+		put("libs/sul/dmalloc/ldflags", "");
+	}
+
+	/* yacc/lex - are we able to regenerate languages? */
+	if (!isfalse(get("parsgen/bison/presents"))) {
+		require("parsgen/flex/*", 0, 0);
+		require("parsgen/bison/*", 0, 0);
+	}
+	else
+		report("Bison/flex are disabled, among with parser generation.\n");
+
+	if (!istrue(get("parsgen/flex/presents")) || !istrue(get("parsgen/bison/presents")))
+		put("/local/pcb/want_parsgen", sfalse);
+	else
+		put("/local/pcb/want_parsgen", strue);
+
+	if (get("cc/rdynamic") == NULL)
+		put("cc/rdynamic", "");
+
+	{
+		const char *tmp, *fpic, *debug;
+		fpic = get("/target/cc/fpic");
+		if (fpic == NULL) fpic = "";
+		debug = get("/arg/debug");
+		if (debug == NULL) debug = "";
+		tmp = str_concat(" ", fpic, debug, NULL);
+		put("/local/global_cflags", tmp);
+	}
+
+
+	/* plugin dependencies */
+	if (plug_is_enabled("dbus")) {
+		require("libs/sul/dbus/presents", 0, 0);
+		if (!istrue(get("libs/sul/dbus/presents"))) {
+			report_repeat("WARNING: disabling the DBUS interface plugin because libdbus is not found or not configured...\n");
+			hook_custom_arg("disable-dbus", NULL);
+		}
+	}
+
+	plugin_deps(1);
+
+	if (plug_is_enabled("gpmi")) {
+		require("libs/script/gpmi/presents", 0, 0);
+		if (!istrue(get("libs/script/gpmi/presents"))) {
+			report_repeat("WARNING: disabling the gpmi scripting because libgpmi is not found or not configured...\n");
+			hook_custom_arg("disable-gpmi", NULL);
+		}
+	}
+
+	/* figure coordinate bits */
+	{
+		int int_bits       = safe_atoi(get("sys/types/size/signed_int")) * 8;
+		int long_bits      = safe_atoi(get("sys/types/size/signed_long_int")) * 8;
+		int long_long_bits = safe_atoi(get("sys/types/size/signed_long_long_int")) * 8;
+		int int64_bits     = safe_atoi(get("sys/types/size/uint64_t")) * 8;
+		const char *chosen, *abs_name, *postfix;
+		char tmp[64];
+		int need_stdint = 0;
+
+		if (want_coord_bits == int_bits)             { postfix="U";   chosen = "int";           abs_name="abs"; }
+		else if (want_coord_bits == long_bits)       { postfix="UL";  chosen = "long int";      abs_name="labs"; }
+		else if (want_coord_bits == int64_bits)      { postfix="ULL"; chosen = "int64_t";       abs_name="llabs"; need_stdint = 1; }
+		else if (want_coord_bits == long_long_bits)  { postfix="ULL"; chosen = "long long int"; abs_name="llabs"; }
+		else {
+			report("ERROR: can't find a suitable integer type for coord to be %d bits wide\n", want_coord_bits);
+			exit(1);
+		}
+
+		sprintf(tmp, "((1%s<<%d)-1)", postfix, want_coord_bits - 1);
+		put("/local/pcb/coord_type", chosen);
+		put("/local/pcb/coord_max", tmp);
+		put("/local/pcb/coord_abs", abs_name);
+
+		chosen = NULL;
+		if (istrue(get("/local/pcb/debug"))) { /* debug: c89 */
+			if (int64_bits >= 64) {
+				/* to suppress warnings on systems that support c99 but are forced to compile in c89 mode */
+				chosen = "int64_t";
+				need_stdint = 1;
+			}
+		}
+
+		if (chosen == NULL) { /* non-debug, thus non-c89 */
+			if (long_long_bits >= 64) chosen = "long long int";
+			else if (long_bits >= 64) chosen = "long int";
+			else chosen = "double";
+		}
+		put("/local/pcb/long64", chosen);
+		if (need_stdint)
+			put("/local/pcb/include_stdint", "#include <stdint.h>");
+	}
+
+	/* set cflags for C89 */
+	put("/local/pcb/c89flags", "");
+	if (istrue(get("/local/pcb/debug"))) {
+		const char *ansi = get("/host/cc/argstd/ansi");
+		const char *ped = get("/host/cc/argstd/pedantic");
+
+		if ((ansi != NULL) && (*ansi != '\0')) {
+			append("/local/pcb/c89flags", " ");
+			append("/local/pcb/c89flags", ansi);
+			need_inl = 1;
+		}
+		if ((ped != NULL) && (*ped != '\0')) {
+			append("/local/pcb/c89flags", " ");
+			append("/local/pcb/c89flags", ped);
+			need_inl = 1;
+		}
+	}
+
+	if (!istrue(get("cc/inline")))
+		need_inl = 1;
+
+	if (need_inl) {
+		/* disable inline for C89 */
+		append("/local/pcb/c89flags", " ");
+		append("/local/pcb/c89flags", "-Dinline= ");
+	}
+
+	return 0;
+}
+
+#ifdef GENCALL
+/* If enabled, generator implements ###call *### and generator_callback is
+   the callback function that will be executed upon ###call### */
+void generator_callback(char *cmd, char *args)
+{
+	printf("* generator_callback: '%s' '%s'\n", cmd, args);
+}
+#endif
+
+static int gpmi_config(void)
+{
+	char *tmp;
+	const char *gcfg = get("libs/script/gpmi/gpmi-config");
+	int generr = 0;
+
+	printf("Generating pcb-gpmi/Makefile.conf (%d)\n", generr |= tmpasm("../src_plugins/gpmi/pcb-gpmi", "Makefile.config.in", "Makefile.config"));
+
+
+	printf("Configuring gpmi packages...\n");
+	tmp = str_concat("", "cd ../src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg && ", gcfg, " --pkggrp && ./configure", NULL);
+	generr |= system(tmp);
+	free(tmp);
+
+	printf("Configuring gpmi plugin \"app\"\n");
+	tmp = str_concat("", "cd ../src_plugins/gpmi//pcb-gpmi/gpmi_plugin && ", gcfg, " --app", NULL);
+	generr |= system(tmp);
+	free(tmp);
+
+	printf("Finished configuring gpmi packages\n");
+
+	return generr;
+}
+static void plugin_stat(const char *header, const char *path, const char *name)
+{
+	const char *val = get(path);
+
+	if (*header == '#') /* don't print hidden plugins */
+		return;
+
+	printf(" %-32s", header);
+
+	if (val == NULL)
+		printf("??? (NULL)   ");
+	else if (strcmp(val, sbuildin) == 0)
+		printf("yes, buildin ");
+	else if (strcmp(val, splugin) == 0)
+		printf("yes, PLUGIN  ");
+	else
+		printf("no           ");
+
+	printf("   [%s]\n", name);
+}
+
+static void print_sum_setting(const char *node, const char *desc)
+{
+	const char *res, *state;
+	state = get(node);
+	if (istrue(state))
+		res = "enabled";
+	else if (isfalse(state))
+		res = "disabled";
+	else
+		res = "UNKNOWN - disabled?";
+	printf("%-55s %s\n", desc, res);
+}
+
+static void print_sum_cfg_val(const char *node, const char *desc)
+{
+	const char *state = get(node);
+	printf("%-55s %s\n", desc, state);
+}
+
+/* Runs after detection hooks, should generate the output (Makefiles, etc.) */
+int hook_generate()
+{
+	char *rev = "non-svn", *tmp;
+	int manual_config = 0, generr = 0;
+
+	tmp = svn_info(0, "../src", "Revision:");
+	if (tmp != NULL) {
+		rev = str_concat("", "svn r", tmp, NULL);
+		free(tmp);
+	}
+	put("/local/revision", rev);
+	put("/local/version",  version);
+
+	printf("Generating Makefile.conf (%d)\n", generr |= tmpasm("..", "Makefile.conf.in", "Makefile.conf"));
+
+	printf("Generating gts/Makefile (%d)\n", generr |= tmpasm("../src_3rd/gts", "Makefile.in", "Makefile"));
+	printf("Generating pcb/Makefile (%d)\n", generr |= tmpasm("../src", "Makefile.in", "Makefile"));
+
+	/* Has to be after pcb/Makefile so that all the modules are loaded. */
+	printf("Generating pcb/buildin  (%d)\n", generr |= tmpasm("../src", "buildin.c.in", "buildin.c"));
+
+	printf("Generating util/gsch2pcb-rnd/Makefile (%d)\n", generr |= tmpasm("../util", "gsch2pcb-rnd/Makefile.in", "gsch2pcb-rnd/Makefile"));
+
+	printf("Generating config.h (%d)\n", generr |= tmpasm("..", "config.h.in", "config.h"));
+
+	printf("Generating compat_inc.h (%d)\n", generr |= tmpasm("../src", "compat_inc.h.in", "compat_inc.h"));
+
+	if (plug_is_enabled("gpmi"))
+		gpmi_config();
+
+	if (!generr) {
+	printf("\n\n");
+	printf("=====================\n");
+	printf("Configuration summary\n");
+	printf("=====================\n");
+
+	print_sum_setting("/local/pcb/want_parsgen",   "Regenerating languages with bison & flex");
+	print_sum_setting("/local/pcb/want_nls",       "Internationalization with gettext");
+	print_sum_setting("/local/pcb/debug",          "Compilation for debugging");
+	print_sum_setting("/local/pcb/symbols",        "Include debug symbols");
+	print_sum_setting("libs/sul/dmalloc/presents", "Compile with dmalloc");
+	print_sum_cfg_val("/local/pcb/coord_bits",     "Coordinate type bits");
+	print_sum_cfg_val("/local/pcb/dot_pcb_rnd",    ".pcb_rnd config dir under $HOME");
+
+#undef plugin_def
+#undef plugin_header
+#undef plugin_dep
+#define plugin_def(name, desc, default_, all_) plugin3_stat(name, desc)
+#define plugin_header(sect) printf(sect);
+#define plugin_dep(plg, on)
+#include "plugins.h"
+
+	if (repeat != NULL) {
+		printf("\n%s\n", repeat);
+	}
+
+	{
+		FILE *f;
+		f = fopen("Rev.stamp", "w");
+		fprintf(f, "%d", myrev);
+		fclose(f);
+	}
+
+	}
+	else
+		fprintf(stderr, "Error generating some of the files\n");
+
+	return 0;
+}
+
+/* Runs before everything is uninitialized */
+void hook_preuninit()
+{
+}
+
+/* Runs at the very end, when everything is already uninitialized */
+void hook_postuninit()
+{
+}
+
diff --git a/scconfig/menucfg.c b/scconfig/menucfg.c
new file mode 100644
index 0000000..9a39a21
--- /dev/null
+++ b/scconfig/menucfg.c
@@ -0,0 +1,71 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "default/hooks.h"
+#include "default/libs.h"
+#include "default/main_custom_args.h"
+#include "default/main_lib.h"
+#include "menulib/scmenu.h"
+#include "util/arg_auto_set.h"
+#include "util/arg_auto_menu.h"
+
+
+void re_fail(char *s, char c)
+{
+	fprintf(stderr, "Regex error: %s [opcode %o]\n", s, c);
+	abort();
+}
+
+int cb_configure(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *w, int selection)
+{
+	printf("configure!!\n");
+	sleep(1);
+	return -1;
+}
+
+
+
+scm_menu_entry_t me_hid_gtk[8]        = {{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}};
+scm_menu_entry_t me_hid_lesstif[8]    = {{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}};
+scm_menu_entry_t me_export[8]         = {{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}};
+scm_menu_entry_t me_gpmi[8]           = {{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}};
+scm_menu_entry_t me_autorouter[8]     = {{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}};
+scm_menu_entry_t me_all_settings[32]  = {{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}};
+
+scm_menu_entry_t me_settings[] = {
+	{SCM_SUBMENU,        NULL, "HID: gtk",           "gtk settings -->",           NULL, me_hid_gtk, SCM_AUTO_RUN},
+	{SCM_SUBMENU,        NULL, "HID: lesstif",       "lesstif settings -->",       NULL, me_hid_lesstif, SCM_AUTO_RUN},
+	{SCM_SUBMENU,        NULL, "HID: exporters",     "export format settings -->", NULL, me_export, SCM_AUTO_RUN},
+	{SCM_SUBMENU,        NULL, "scripting",          "gpmi settings -->",          NULL, me_gpmi, SCM_AUTO_RUN},
+	{SCM_SUBMENU,        NULL, "auto routers",       "autorouter settings -->",    NULL, me_autorouter, SCM_AUTO_RUN},
+	{SCM_EMPTY,          NULL, NULL, NULL, NULL, 0},
+	{SCM_SUBMENU,        NULL, "all",                "all settings at once -->",   NULL, me_all_settings, SCM_AUTO_RUN},
+	{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}
+};
+
+scm_menu_entry_t me_main[] = {
+	{SCM_SUBMENU,        NULL, "Edit settings",      "Edit user configurable settings -->",       NULL, me_settings, SCM_AUTO_RUN},
+	{SCM_SUBMENU,        NULL, "Manual detection",   "Run detection of individual libs -->",      NULL, NULL, SCM_AUTO_RUN},
+	{SCM_SUBMENU_CB,     NULL, "Configure pcb-rnd",  "Run the configuration process -->",         NULL, cb_configure, SCM_AUTO_RUN},
+	{SCM_EMPTY,          NULL, NULL, NULL, NULL, 0},
+	{SCM_KEY_VALUE,      NULL, "Exit & save",        "Exit with saving the settings",             NULL, NULL, SCM_AUTO_RUN},
+	{SCM_KEY_VALUE,      NULL, "Exit",               "Exit without saving the settings",          NULL, NULL, SCM_AUTO_RUN},
+	{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}
+};
+
+extern const arg_auto_set_t disable_libs[] ;
+
+int main()
+{
+	scm_ctx_t ctx;
+
+	main_init();
+
+	append_settings_auto_set(me_export, 32, disable_libs, "-gd", NULL, "^disable-",   SCM_SUBMENU, 0);
+
+	scm_init(&ctx);
+	scm_menu_autowin(&ctx,  "main menu", me_main);
+
+	main_uninit();
+
+	return 0;
+}
diff --git a/scconfig/plugin_3state.h b/scconfig/plugin_3state.h
new file mode 100644
index 0000000..50c75ae
--- /dev/null
+++ b/scconfig/plugin_3state.h
@@ -0,0 +1,54 @@
+/* 3-state plugin system; possible states of each plugin, stored in
+   /local/pcb/PLUGIN_NAME/controls:
+    "disable" = do not compile it at all
+    "buildin" = enable, static link into the executable
+    "plugin"  = enable, make it a dynamic link library (runtime load plugin)
+*/
+
+#define sdisable "disable"
+#define sbuildin "buildin"
+#define splugin  "plugin"
+
+
+/* Macros to check the state */
+
+#define plug_eq(name, val) \
+	((get("/local/pcb/" name "/controls") != NULL) && (strcmp(get("/local/pcb/" name "/controls"), val) == 0))
+
+#define plug_is_enabled(name)  (plug_eq(name, splugin) || plug_eq(name, sbuildin))
+#define plug_is_disabled(name) (plug_eq(name, sdisabled))
+#define plug_is_buildin(name)  (plug_eq(name, sbuildin))
+#define plug_is_plugin(name)   (plug_eq(name, splugin))
+
+/* auto-set tables to change control to the desired value */
+const arg_auto_set_node_t arg_disable[] = {
+	{"controls",    sdisable},
+	{NULL, NULL}
+};
+
+const arg_auto_set_node_t arg_buildin[] = {
+	{"controls",    sbuildin},
+	{NULL, NULL}
+};
+
+const arg_auto_set_node_t arg_plugin[] = {
+	{"controls",    splugin},
+	{NULL, NULL}
+};
+
+
+/* plugin_def implementation to create CLI args */
+#define plugin3_args(name, desc) \
+	{"disable-" name, "/local/pcb/" name,  arg_disable,   "$do not compile " desc}, \
+	{"buildin-" name, "/local/pcb/" name,  arg_buildin,   "$static link " desc " into the executable"}, \
+	{"plugin-"  name, "/local/pcb/" name,  arg_plugin,    "$" desc " is a dynamic loadable plugin"},
+
+
+/* plugin_def implementation to set default state */
+#define plugin3_default(name, default_) \
+	db_mkdir("/local/pcb/" name); \
+	put("/local/pcb/" name "/controls", default_);
+
+/* plugin_def implementation to print a report with the final state */
+#define plugin3_stat(name, desc) \
+	plugin_stat(desc, "/local/pcb/" name "/controls", name);
diff --git a/scconfig/plugins.h b/scconfig/plugins.h
new file mode 100644
index 0000000..a3f30e8
--- /dev/null
+++ b/scconfig/plugins.h
@@ -0,0 +1,79 @@
+plugin_header("\nLibrary plugins:\n")
+plugin_def("lib_gensexpr",    "#s-expression library",     sdisable, 0)
+plugin_def("lib_legacy_func", "legacy functions",          sbuildin, 1)
+
+plugin_header("\nFeature plugins:\n")
+plugin_def("gpmi",            "GPMI scripting",            sbuildin, 1)
+plugin_def("diag",            "diagnostic acts. for devs", sdisable, 1)
+plugin_def("autocrop",        "crop board to fit objects", sbuildin, 1)
+plugin_def("autoroute",       "the original autorouter",   sbuildin, 1)
+plugin_def("boardflip",       "flip board objects",        sdisable, 0)
+plugin_def("distalign",       "distribute and align objs", sbuildin, 1)
+plugin_def("distaligntext",   "distribute and align text", sbuildin, 1)
+plugin_def("jostle",          "push lines out of the way", sbuildin, 1)
+plugin_def("polycombine",     "combine selected polygons", sbuildin, 1)
+plugin_def("polystitch",      "stitch polygon at cursor",  sdisable, 0)
+plugin_def("teardrops",       "draw teardrops on pins",    sbuildin, 1)
+plugin_def("smartdisperse",   "netlist based dispenser",   sbuildin, 1)
+plugin_def("toporouter",      "topological autorouter",    sdisable, 0)
+plugin_def("autoplace",       "auto place components",     sbuildin, 1)
+plugin_def("vendordrill",     "vendor drill mapping",      sbuildin, 1)
+plugin_def("puller",          "puller",                    sbuildin, 1)
+plugin_def("djopt",           "djopt",                     sbuildin, 1)
+plugin_def("mincut",          "minimal cut shorts",        sbuildin, 1)
+plugin_def("renumber",        "renumber action",           sbuildin, 1)
+plugin_def("oldactions",      "old/obsolete actions",      sdisable, 1)
+plugin_def("fontmode",        "font editor",               sbuildin, 1)
+plugin_def("stroke",          "libstroke gestures",        sdisable, 1)
+plugin_def("report",          "report actions",            sbuildin, 1)
+plugin_def("dbus",            "DBUS interface",            sdisable, 1)
+plugin_def("shand_cmd",       "command shorthands",        sbuildin, 1)
+plugin_def("propedit",        "object property editor",    sbuildin, 1)
+plugin_def("loghid",          "diagnostics: log HID calls",sdisable, 1)
+plugin_def("query",           "query language",            sbuildin, 1)
+
+plugin_header("\nFootprint backends:\n")
+plugin_def("fp_fs",           "filesystem footprints",     sbuildin, 1)
+plugin_def("fp_wget",         "web footprints",            sbuildin, 1)
+
+plugin_header("\nImport plugins:\n")
+plugin_def("import_sch",      "import sch",                sbuildin, 1)
+plugin_def("import_edif",     "import edif",               sbuildin, 1)
+plugin_def("import_netlist",  "import netlist",            sbuildin, 1)
+plugin_def("import_dsn",      "specctra .dsn importer",    sdisable, 0)
+
+plugin_header("\nExport plugins:\n")
+plugin_def("export_gcode",    "gcode exporter",            sbuildin, 1)
+plugin_def("export_nelma",    "nelma exporter",            sbuildin, 1)
+plugin_def("export_png",      "png/gif/jpg exporter",      sbuildin, 1)
+plugin_def("export_bom",      "bom exporter",              sbuildin, 1)
+plugin_def("export_xy",       "xy (centroid) exporter",    sbuildin, 1)
+plugin_def("export_gerber",   "gerber exporter",           sbuildin, 1)
+plugin_def("export_lpr",      "lpr exporter (printer)",    sbuildin, 1)
+plugin_def("export_ps",       "postscript exporter",       sbuildin, 1)
+plugin_def("export_dxf",      "DXF exporter",              sdisable, 0)
+plugin_def("export_test",     "dummy test exporter",       sdisable, 1)
+plugin_def("export_bboard",   "breadboard exporter",       sdisable, 0)
+plugin_def("export_openscad", "openscad exporter",         sdisable, 0)
+plugin_def("export_dsn",      "specctra .dsn exporter",    sdisable, 0)
+plugin_def("export_ipcd356",  "IPC-D-356 Netlist exporter",sdisable, 0)
+plugin_def("export_svg",      "SVG exporter",              sbuildin, 1)
+
+plugin_header("\nIO plugins (file formats):\n")
+plugin_def("io_lihata",       "lihata board format",       sbuildin, 1)
+plugin_def("io_pcb",          "the original pcb format",   sbuildin, 1)
+plugin_def("io_kicad_legacy", "Kicad's legacy format",     sbuildin, 1)
+plugin_def("io_kicad",        "Kicad's s-expr format",     sbuildin, 1)
+
+plugin_header("\nHID plugins:\n")
+plugin_def("hid_batch",       "batch process (no-gui HID)",sbuildin, 1)
+plugin_def("hid_gtk",         "the GTK gui",               sbuildin, 1)
+plugin_def("hid_lesstif",     "the lesstif gui",           sbuildin, 1)
+
+
+plugin_dep("export_lpr", "export_ps")
+plugin_dep("export_xy", "export_bom")
+plugin_dep("io_kicad", "lib_gensexpr")
+
+/* for the uniq name lib: */
+plugin_dep("io_kicad_legacy", "io_kicad")
diff --git a/scconfig/revtest.c b/scconfig/revtest.c
new file mode 100644
index 0000000..63d37cf
--- /dev/null
+++ b/scconfig/revtest.c
@@ -0,0 +1,83 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "Rev.h"
+
+const char *act_names[] = {
+	"distclean",
+	"configure",
+	"make clean",
+	NULL
+};
+
+const char *act_desc[] = {
+	"run 'make distclean' in trunk/",
+	"rerun ./configure",
+	"run 'make clean' in trunk/",
+	NULL
+};
+
+int act_needed[16];
+
+int main(int argc, char *argv[])
+{
+	char line[8192];
+	int res = 0, stamp = 0, banner = 0;
+	FILE *f;
+
+	f = fopen(argv[1], "r");
+	if (f != NULL) {
+		fscanf(f, "%d", &stamp);
+		fclose(f);
+	}
+
+	while(fgets(line, sizeof(line), stdin) != NULL) {
+		char *end;
+		int rev;
+
+		if (*line == '#')
+			continue;
+		rev = strtol(line, &end, 10);
+		if (*end != '\t')
+			continue;
+
+		if (rev > stamp) {
+			char *act, *txt;
+			const char **a;
+			int idx;
+			res = 1;
+			act = end;
+			while(*act == '\t')
+				act++;
+			txt = strchr(act, '\t');
+			if (txt != NULL) {
+				*txt = '\0';
+				txt++;
+				while(*txt == '\t')
+					txt++;
+			}
+			if (!banner) {
+				fprintf(stderr, "\nYour source tree is stale (last configured after config-milestone r%d).\nRecent new features:\n", stamp);
+				banner = 1;
+			}
+			fprintf(stderr, "\nr%d: %s\n", rev, txt);
+			for(idx = 0, a = act_names; *a != NULL; idx++, a++)
+				if (strstr(act, *a) != NULL)
+					act_needed[idx]++;
+		}
+	}
+
+	if (res) {
+		const char **a;
+		int idx;
+
+		fprintf(stderr, "(Stamp: %d myrev: %d)\n", stamp, myrev);
+		fprintf(stderr, "\nBefore running make, please do the following actions in this order:\n");
+		for(idx = 0, a = act_names; *a != NULL; idx++, a++)
+			if (act_needed[idx])
+				fprintf(stderr, "  %s\n", act_desc[idx]);
+		fprintf(stderr, "\n");
+	}
+
+	return res;
+}
diff --git a/scconfig/src/LGPL-2.1 b/scconfig/src/LGPL-2.1
new file mode 100644
index 0000000..2d2d780
--- /dev/null
+++ b/scconfig/src/LGPL-2.1
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+

+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+

+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+

+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+

+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+

+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+

+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+

+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+

+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/scconfig/src/c99/INIT.c b/scconfig/src/c99/INIT.c
new file mode 100644
index 0000000..b60405d
--- /dev/null
+++ b/scconfig/src/c99/INIT.c
@@ -0,0 +1,2 @@
+	deps_c99_init();
+
diff --git a/scconfig/src/c99/INIT.h b/scconfig/src/c99/INIT.h
new file mode 100644
index 0000000..03221ec
--- /dev/null
+++ b/scconfig/src/c99/INIT.h
@@ -0,0 +1 @@
+void deps_c99_init();
diff --git a/scconfig/src/c99/Makefile.plugin b/scconfig/src/c99/Makefile.plugin
new file mode 100644
index 0000000..4084cf0
--- /dev/null
+++ b/scconfig/src/c99/Makefile.plugin
@@ -0,0 +1,6 @@
+C99_CFLAGS = -DPLUGIN_C99
+C99_OBJS = \
+  $(BIN)/c99/find_c99.o
+
+$(BIN)/c99/find_c99.o: $(SRC)/c99/find_c99.c
+	$(CC) $(CFLAGS) -c $(SRC)/c99/find_c99.c -o $(BIN)/c99/find_c99.o
diff --git a/scconfig/src/c99/find_c99.c b/scconfig/src/c99/find_c99.c
new file mode 100644
index 0000000..9154d49
--- /dev/null
+++ b/scconfig/src/c99/find_c99.c
@@ -0,0 +1,628 @@
+/*
+    scconfig - c99 feature detection
+    Copyright (C) 2009  Szabolcs Nagy
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+/*
+NOTES. please read before extending this file.
+The process of adding a new test is:
+	1. copy one of the c99_find_*() functions
+	2. replace the test program and/or the code that is looking for alternatives
+	3. add your new c99_find_*() in deps_c99_init(); make sure deps_c99_init() is in the bottom of the file
+	4. edit ../../doc/tree.txt, add your new detection under std/c99/
+	5. to test recompile in trunk/ and run ./configure --detect=std/c99/YOUR_NODE_NAME
+*/
+
+static int try(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected)
+{
+	char *out;
+
+	logprintf(logdepth, "trying cc99:try with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags);
+
+	if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) {
+		if (((out == NULL) && (iscross)) || (strncmp(out, expected, strlen(expected)) == 0)) {
+			free(out);
+			return 1;
+		}
+		free(out);
+	}
+	return 0;
+}
+
+int find_c99_stdc(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "#if __STDC_VERSION__ >= 199901L"
+		NL "	puts(\"OK\");"
+		NL "#endif"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 __STDC_VERSION__ macro... ");
+	logprintf(logdepth, "find_c99_stdc: trying to find c99 __STDC_VERSION__ macro...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/stdc", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/stdc", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_emptymacro(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#define d(x,y,z) x # y # z"
+		NL "int main() {"
+		NL "	puts(d(,,OK));"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 empty macro... ");
+	logprintf(logdepth, "find_c99_emptymacro: trying to find c99 empty macro...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/emptymacro", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/emptymacro", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_variadicmacro(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#define p(...) printf(__VA_ARGS__)"
+		NL "int main() {"
+		NL "	p(\"%s\\n\", \"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 variadic macro... ");
+	logprintf(logdepth, "find_c99_variadicmacro: trying to find c99 variadic macro...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/variadicmacro", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/variadicmacro", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_funcmacro(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "void OK() {puts(__func__);}"
+		NL "int main() {"
+		NL "	OK();"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 __func__ macro... ");
+	logprintf(logdepth, "find_c99_funcmacro: trying to find c99 __func__ macro...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/funcmacro", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/funcmacro", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_stdint(int logdepth, int fatal)
+{
+	const char *test_c =
+		/* <stdint.h>:  [u]intNN_t, [u]int_fastNN_t, [u]int_least_t, [u]intmax_t, [u]intptr_t, +limits */
+		NL "#include <inttypes.h>" /* includes stdint +defines printf formats */
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	uint8_t a = 1;"
+		NL "	int16_t b = 1;"
+		NL "	int32_t c = 1;"
+		NL "	int64_t d = 1;"
+		NL "	printf(\"%\"PRIu8 \"%\"PRId16 \"%\"PRId32 \"%\"PRId64 \"\\n\", a, b, c, d);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 stdint... ");
+	logprintf(logdepth, "find_c99_stdint: trying to find c99 stdint...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "1111")) {
+		put("std/c99/stdint", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/stdint", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_wchar(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stddef.h>"
+		NL "wchar_t w[] = L\"OK\";"
+		NL "#include <wchar.h>"
+		NL "#include <wctype.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	printf(\"%ls\\n\", w);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 wchar_t... ");
+	logprintf(logdepth, "find_c99_wchar: trying to find c99 wchar_t...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/wchar", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/wchar", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_longlong(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	unsigned long long int a = 42ULL;"
+		NL "	printf(\"%lld\\n\", a);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 long long int... ");
+	logprintf(logdepth, "find_c99_longlong: trying to find c99 long long int...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "42")) {
+		put("std/c99/longlong", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/longlong", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_bool(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <stdbool.h>"
+		NL "int main() {"
+		NL "	_Bool a = true;"
+		NL "	bool b = false;"
+		NL "	if (a && !b) puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 bool... ");
+	logprintf(logdepth, "find_c99_bool: trying to find c99 bool...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/bool", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/bool", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_inline(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "inline int f() {puts(\"OK\");}"
+		NL "int main() {"
+		NL "	f();"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 inline function specifier... ");
+	logprintf(logdepth, "find_c99_inline: trying to find c99 inline function specifier...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/inline", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/inline", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_restrict(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "void f(char * restrict s) {puts(s);}"
+		NL "int main() {"
+		NL "	f(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 restrict pointer qualifier... ");
+	logprintf(logdepth, "find_c99_restrict: trying to find c99 restrict pointer qualifier...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/restrict", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/restrict", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_comment(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	// puts(\"comment\");"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 // style comment... ");
+	logprintf(logdepth, "find_c99_comment: trying to find c99 // style comment...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/comment", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/comment", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_endcomma(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "enum {A=12,B=30,};"
+		NL "int main() {"
+		NL "	int a[] = {A+B,};"
+		NL "	printf(\"%d\\n\", a[0]);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 end comma... ");
+	logprintf(logdepth, "find_c99_endcomma: trying to find c99 end comma...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "42")) {
+		put("std/c99/endcomma", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/endcomma", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_nonconstinit(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int f() {return 42;}"
+		NL "int main() {"
+		NL "	int a[] = {f()};"
+		NL "	printf(\"%d\\n\", a[0]);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 non-const initializer... ");
+	logprintf(logdepth, "find_c99_nonconstinit: trying to find c99 non-const initializer...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "42")) {
+		put("std/c99/nonconstinit", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/nonconstinit", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_init(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "struct s_t {int x; int y;};"
+		NL "int main() {"
+		NL "	int a[] = {[3]=10,[2]=12};"
+		NL "	struct s_t s = {.x=20};"
+		NL "	printf(\"%d\\n\", a[1]+a[2]+a[3]+s.x+s.y);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 designated initializer... ");
+	logprintf(logdepth, "find_c99_init: trying to find c99 designated initializer...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "42")) {
+		put("std/c99/init", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/init", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_compoundlit(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int f(int *a) {return a[2];}"
+		NL "int main() {"
+		NL "	printf(\"%d\\n\", f((int[]){0,1,42}));"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 compound literals... ");
+	logprintf(logdepth, "find_c99_compoundlit: trying to find c99 compound literals...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "42")) {
+		put("std/c99/compoundlit", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/compoundlit", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_decl(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	int a;"
+		NL "	a = 40;"
+		NL "	int b;"
+		NL "	b = 2;"
+		NL "	printf(\"%d\\n\", a+b);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 declaration and statement mixing... ");
+	logprintf(logdepth, "find_c99_decl: trying to find c99 declaration and statement mixing...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "42")) {
+		put("std/c99/decl", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/decl", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_for(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	for (int i = 0; i < 1; i++)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 for loop... ");
+	logprintf(logdepth, "find_c99_for: trying to find c99 for loop...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/for", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/for", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_divmod(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	if(-22/7 == -3 && -22%7 == -1) puts(\"Fortran\");"
+		NL "	if(-22/7 == -4 && -22%7 ==  6) puts(\"Pascal\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 negative div,mod... ");
+	logprintf(logdepth, "find_c99_divmod: trying to find c99 negative div,mod...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "Fortran")) {
+		put("std/c99/divmod", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/divmod", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_incomparray(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stddef.h>"
+		NL "#include <stdio.h>"
+		NL "struct t {int n; char s[];};"
+		NL "int main() {"
+		NL "	if(sizeof(struct t) == offsetof(struct t, s))"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 incomplete array member... ");
+	logprintf(logdepth, "find_c99_incomparray: trying to find c99 incomplete array member...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "OK")) {
+		put("std/c99/incomparray", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/incomparray", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_snprintf(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	char s[4];"
+		NL "	if(snprintf(s, 4, \"12345\") == 5)"
+		NL "		puts(s);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 snprintf... ");
+	logprintf(logdepth, "find_c99_snprintf: trying to find c99 snprintf...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "123")) {
+		put("std/c99/snprintf", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/snprintf", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_c99_floatfmt(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	double f = 1.0;"
+		NL "	long double Lf = 1.0;"
+		NL "	printf(\"%.1F %.1lf %.1LF %.1a %.1A\\n\", f, f, Lf, f, f);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for c99 float printf formats... ");
+	logprintf(logdepth, "find_c99_floatfmt: trying to find c99 float printf formats...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, NULL, NULL, "1.0 1.0 1.0 0x1.0p+0 0X1.0P+0")) {
+		put("std/c99/floatfmt", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("std/c99/floatfmt", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+
+void deps_c99_init()
+{
+	dep_add("std/c99/stdc",           find_c99_stdc);
+	dep_add("std/c99/emptymacro",     find_c99_emptymacro);
+	dep_add("std/c99/variadicmacro",  find_c99_variadicmacro);
+	dep_add("std/c99/funcmacro",      find_c99_funcmacro);
+	dep_add("std/c99/stdint",         find_c99_stdint);
+	dep_add("std/c99/wchar",          find_c99_wchar);
+	dep_add("std/c99/longlong",       find_c99_longlong);
+	dep_add("std/c99/bool",           find_c99_bool);
+	dep_add("std/c99/inline",         find_c99_inline);
+	dep_add("std/c99/restrict",       find_c99_restrict);
+	dep_add("std/c99/comment",        find_c99_comment);
+	dep_add("std/c99/endcomma",       find_c99_endcomma);
+	dep_add("std/c99/nonconstinit",   find_c99_nonconstinit);
+	dep_add("std/c99/init",           find_c99_init);
+	dep_add("std/c99/compoundlit",    find_c99_compoundlit);
+	dep_add("std/c99/decl",           find_c99_decl);
+	dep_add("std/c99/for",            find_c99_for);
+	dep_add("std/c99/divmod",         find_c99_divmod);
+	dep_add("std/c99/incomparray",    find_c99_incomparray);
+	dep_add("std/c99/snprintf",       find_c99_snprintf);
+	dep_add("std/c99/floatfmt",       find_c99_floatfmt);
+}
diff --git a/scconfig/src/default/Makefile.plugin b/scconfig/src/default/Makefile.plugin
new file mode 100644
index 0000000..0bee281
--- /dev/null
+++ b/scconfig/src/default/Makefile.plugin
@@ -0,0 +1,134 @@
+DEFAULT_NOMAIN_OBJS = \
+  $(BIN)/default/find_cc.o \
+  $(BIN)/default/lib_compile.o \
+  $(BIN)/default/lib_uniqinc.o \
+  $(BIN)/default/lib_file.o \
+  $(BIN)/default/lib_try.o \
+  $(BIN)/default/str.o \
+  $(BIN)/default/ht.o \
+  $(BIN)/default/log.o \
+  $(BIN)/default/arg.o \
+  $(BIN)/default/db.o \
+  $(BIN)/default/dep.o \
+  $(BIN)/default/deps_default.o \
+  $(BIN)/default/find_libs.o \
+  $(BIN)/default/find_fscalls.o \
+  $(BIN)/default/find_printf.o \
+  $(BIN)/default/find_proc.o \
+  $(BIN)/default/find_fstools.o \
+  $(BIN)/default/find_uname.o \
+  $(BIN)/default/find_target.o \
+  $(BIN)/default/find_io.o \
+  $(BIN)/default/find_time.o \
+  $(BIN)/default/find_types.o \
+  $(BIN)/default/find_signal.o \
+  $(BIN)/default/find_environ.o \
+  $(BIN)/default/regex.o \
+  $(BIN)/default/lib_filelist.o \
+  $(BIN)/default/lib_srctree.o \
+  $(BIN)/default/lib_pkg_config.o \
+  $(BIN)/default/find_sys.o
+
+DEFAULT_MAIN_OBJS = \
+  $(BIN)/default/main.o \
+  $(BIN)/default/main_custom_args.o \
+  $(BIN)/default/main_lib.o
+
+DEFAULT_OBJS = $(DEFAULT_NOMAIN_OBJS) $(DEFAULT_MAIN_OBJS)
+
+$(BIN)/default/lib_compile.o: $(SRC)/default/lib_compile.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_compile.c -o $(BIN)/default/lib_compile.o
+
+$(BIN)/default/lib_file.o: $(SRC)/default/lib_file.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_file.c -o $(BIN)/default/lib_file.o
+
+$(BIN)/default/lib_try.o: $(SRC)/default/lib_try.c $(SRC)/default/log.h $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_try.c -o $(BIN)/default/lib_try.o
+
+$(BIN)/default/str.o: $(SRC)/default/str.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/str.c -o $(BIN)/default/str.o
+
+$(BIN)/default/ht.o: $(SRC)/default/ht.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/ht.c -o $(BIN)/default/ht.o
+
+$(BIN)/default/log.o: $(SRC)/default/log.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/log.c -o $(BIN)/default/log.o
+
+$(BIN)/default/arg.o: $(SRC)/default/arg.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/arg.c -o $(BIN)/default/arg.o
+
+$(BIN)/default/db.o: $(SRC)/default/db.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/db.c -o $(BIN)/default/db.o
+
+$(BIN)/default/dep.o: $(SRC)/default/dep.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/dep.c -o $(BIN)/default/dep.o
+
+$(BIN)/default/deps_default.o: $(SRC)/default/deps_default.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/deps_default.c -o $(BIN)/default/deps_default.o
+
+$(BIN)/default/find_libs.o: $(SRC)/default/find_libs.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_libs.c -o $(BIN)/default/find_libs.o
+
+$(BIN)/default/find_fscalls.o: $(SRC)/default/find_fscalls.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_fscalls.c -o $(BIN)/default/find_fscalls.o
+
+$(BIN)/default/find_signal.o: $(SRC)/default/find_signal.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_signal.c -o $(BIN)/default/find_signal.o
+
+$(BIN)/default/find_printf.o: $(SRC)/default/find_printf.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_printf.c -o $(BIN)/default/find_printf.o
+
+$(BIN)/default/find_proc.o: $(SRC)/default/find_proc.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_proc.c -o $(BIN)/default/find_proc.o
+
+$(BIN)/default/find_fstools.o: $(SRC)/default/find_fstools.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_fstools.c -o $(BIN)/default/find_fstools.o
+
+$(BIN)/default/find_uname.o: $(SRC)/default/find_uname.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_uname.c -o $(BIN)/default/find_uname.o
+
+$(BIN)/default/find_target.o: $(SRC)/default/find_target.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_target.c -o $(BIN)/default/find_target.o
+
+$(BIN)/default/regex.o: $(SRC)/default/regex.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/regex.c -o $(BIN)/default/regex.o
+
+$(BIN)/default/lib_filelist.o: $(SRC)/default/lib_filelist.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_filelist.c -o $(BIN)/default/lib_filelist.o
+
+$(BIN)/default/lib_srctree.o: $(SRC)/default/lib_srctree.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_srctree.c -o $(BIN)/default/lib_srctree.o
+
+$(BIN)/default/lib_pkg_config.o: $(SRC)/default/lib_pkg_config.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_pkg_config.c -o $(BIN)/default/lib_pkg_config.o
+
+$(BIN)/default/lib_uniqinc.o: $(SRC)/default/lib_uniqinc.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/lib_uniqinc.c -o $(BIN)/default/lib_uniqinc.o
+
+$(BIN)/default/find_sys.o: $(SRC)/default/find_sys.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_sys.c -o $(BIN)/default/find_sys.o
+
+$(BIN)/default/find_cc.o: $(SRC)/default/find_cc.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_cc.c -o $(BIN)/default/find_cc.o
+
+$(BIN)/default/find_environ.o: $(SRC)/default/find_environ.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_environ.c -o $(BIN)/default/find_environ.o
+
+$(BIN)/default/find_io.o: $(SRC)/default/find_io.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_io.c -o $(BIN)/default/find_io.o
+
+$(BIN)/default/find_time.o: $(SRC)/default/find_time.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_time.c -o $(BIN)/default/find_time.o
+
+$(BIN)/default/find_types.o: $(SRC)/default/find_types.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+	$(CC) $(CFLAGS) -c $(SRC)/default/find_types.c -o $(BIN)/default/find_types.o
+
+$(BIN)/default/main.o: $(SRC)/default/main.c $(SRC)/default/dep.h $(SRC)/default/libs.h Makefile
+	$(CC) $(CFLAGS) -c $(SRC)/default/main.c -o $(BIN)/default/main.o
+
+$(BIN)/default/main_custom_args.o: $(SRC)/default/main_custom_args.c
+	$(CC) $(CFLAGS) -c $(SRC)/default/main_custom_args.c -o $(BIN)/default/main_custom_args.o
+
+$(BIN)/default/main_lib.o: $(SRC)/default/main_lib.c
+	$(CC) $(CFLAGS) -c $(SRC)/default/main_lib.c -o $(BIN)/default/main_lib.o
+
diff --git a/scconfig/src/default/arg.c b/scconfig/src/default/arg.c
new file mode 100644
index 0000000..7e42f56
--- /dev/null
+++ b/scconfig/src/default/arg.c
@@ -0,0 +1,103 @@
+/*
+    scconfig - command line argument processing
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "db.h"
+#include "arg.h"
+#include "dep.h"
+#include "log.h"
+#include "libs.h"
+
+typedef struct {
+	char *arg;
+	char *path;
+	int (*callback)(const char *key, const char *value);
+} argtbl_t;
+
+argtbl_t argument_table[] = {
+	{"import",      NULL,                   import_args},
+	{"target",      "/arg/sys/target",      NULL},
+	{"target-name", "/arg/sys/target-name", NULL},
+	{"emu",         "/arg/sys/emu",         NULL},
+	{"pkg-config",  "/arg/sys/pkg-config",  NULL},
+	{"pkg-config-zap","/arg/sys/pkg-config-zap",NULL},
+
+/* the followings are autoconf compatibility translations */
+	{"CC",          "/arg/cc/cc",           NULL},
+	{"CFLAGS",      "/arg/cc/cflags",       NULL},
+	{"LDFLAGS",     "/arg/cc/ldflags",      NULL},
+	{"LDL",         "/arg/libs/ldl",        NULL},
+
+	{"gpmi-prefix", "/arg/gpmi/prefix",     NULL},
+	{NULL,          NULL,                   NULL}
+};
+
+void process_args(int argc, char *argv[])
+{
+	int n;
+	char *key, *value;
+	argtbl_t *a;
+	int found, tainted = 0;
+
+	db_mkdir("/arg");
+
+	for(n = 1; n < argc; n++) {
+		key = argv[n];
+		while(*key == '-') key++;
+		value = str_chr(key, '=');
+		found = 0;
+		if (value != NULL) {
+			*value = '\0';
+			value++;
+			/* Look in the argument translate table */
+			for(a = argument_table; a->arg != NULL; a++) {
+				if (strcmp(a->arg, key) == 0) {
+					found = 1;
+					if (a->callback != NULL) {
+						if (!(a->callback(key, value))) {
+							error("Processing argument '%s' failed in the callback\n", argv[n]);
+							abort();
+						}
+					}
+					if (a->path != NULL)
+						put(a->path, value);
+				}
+			}
+			/* Look in known deps table or /arg */
+			if (found == 0) {
+				if ((is_dep_known(key)) || (strncmp(key, "/arg/", 5) == 0)) {
+					put(key, value);
+					found = 1;
+				}
+			}
+		}
+		if (found == 0) {
+			if (custom_arg(key, value) == 0) {
+				error("Unknown argument '%s'\n", key);
+				tainted++;
+			}
+		}
+	}
+	if (tainted)
+		exit(1);
+}
diff --git a/scconfig/src/default/arg.h b/scconfig/src/default/arg.h
new file mode 100644
index 0000000..3b753f3
--- /dev/null
+++ b/scconfig/src/default/arg.h
@@ -0,0 +1,7 @@
+
+void process_args(int argc, char *argv[]);
+
+
+/* main.c: */
+extern int custom_arg(const char *key, const char *value);
+extern int num_custom_reqs;
diff --git a/scconfig/src/default/db.c b/scconfig/src/default/db.c
new file mode 100644
index 0000000..b95e54c
--- /dev/null
+++ b/scconfig/src/default/db.c
@@ -0,0 +1,343 @@
+/*
+    scconfig - database
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include "db.h"
+#include "log.h"
+#include "libs.h"
+
+ht_t *DBs = NULL;
+char *db_cwd = NULL;
+
+void append(const char *key, const char *value)
+{
+	const char *orig;
+	char *new;
+	int l1, l2;
+
+	assert(key   != NULL);
+	assert(value != NULL);
+	if (*value == '\0')
+		return;
+
+	orig = get(key);
+	if (orig == NULL) {
+		put(key, value);
+		return;
+	}
+
+	l1   = strlen(orig);
+	l2   = strlen(value);
+	new  = malloc(l1 + l2 + 1);
+	memcpy(new,      orig,  l1);
+	memcpy(new + l1, value, l2);
+	new[l1+l2] = '\0';
+	put(key, new);
+}
+
+
+static const char *db_split_path(const char *key, ht_t **ht, char **fld)
+{
+	size_t fld_len;
+	const char *path;
+	char first_level_dir[32];
+
+	path = str_chr((char *)(key+1), '/');
+	assert(path != NULL);
+	fld_len = path - key;
+	path++;
+	if (*path == '\0') {
+		*ht = NULL;
+		if (fld != NULL)
+			*fld = NULL;
+		return NULL;
+	}
+	assert(fld_len < sizeof(first_level_dir));
+	strncpy(first_level_dir, key, fld_len);
+	first_level_dir[fld_len] = '\0';
+	*ht = ht_get(DBs, first_level_dir+1);
+	if (fld != NULL)
+		*fld = first_level_dir;
+	return path;
+}
+
+int export_(FILE *f, int export_empty, ht_t *table, const char *fld)
+{
+	ht_entry_t *h;
+
+	for(h = ht_first(table); h != NULL; h = ht_next(table, h))
+		if (export_empty || ((h->value != NULL) && (*(char *)h->value != '\0'))) {
+			fprintf(f, "/%s/%s=%s\n", fld, h->key, (char *)h->value);
+		}
+	return 0;
+}
+
+int export(const char *fn, int export_empty, const char *root)
+{
+	FILE *f;
+	int ret;
+/*	ht_t *table; */
+	ht_entry_t *h;
+
+	if (fn != NULL) {
+		f = fopen(fn, "w");
+		if (f == NULL)
+			return -1;
+	}
+	else
+		f = stdout;
+
+	if ((root == NULL) || ((root[0] == '/') && (root[1] == '\0'))) {
+		/* export all directories */
+		for(h = ht_first(DBs); h != NULL; h = ht_next(DBs, h))
+			ret += export_(f, export_empty, h->value, h->key);
+	}
+	else {
+		error("not yet implemented\n");
+		abort();
+		/* db_split_path(); */
+	}
+
+	if (f != stdout)
+		fclose(f);
+
+	return ret;
+}
+
+int import(const char *fn)
+{
+	char line[1024];
+	char *key, *value, *nl, *slash;
+	int num_records, num_lines;
+	FILE *f;
+
+	f = fopen(fn, "r");
+	if (f == NULL)
+		return -1;
+
+	for(num_records = 0, num_lines = 0; !feof(f); num_lines++) {
+		*line = '\0';
+		fgets(line, sizeof(line) - 1, f);
+		if ((*line != '#') && (*line != '\n') && (*line != '\r') && (*line != '\0')) {
+			key = line;
+			value = str_chr(key, '=');
+			if (value == NULL) {
+				error("Error importing: missing '=' in line %d in file %s.\n", num_lines, fn);
+				abort();
+			}
+			num_records++;
+			*value = '\0';
+			value++;
+			nl = str_chr(value, '\n');
+			if (nl != NULL)
+				*nl = '\0';
+			slash = str_chr(key+1, '/');
+			if (slash == NULL) {
+				error("Error importing: no directory name for %s.\n", key);
+				abort();
+			}
+			*slash = '\0';
+			db_mkdir(key);
+			*slash = '/';
+			put(key, value);
+			logprintf(0, "(Import from '%s': '%s'='%s')\n", fn, key, value);
+		}
+	}
+
+	fclose(f);
+	return num_records;
+}
+
+int import_args(const char *key, const char *fn)
+{
+	(void) key; /* suppress compiler warnings for unused key; needed because function pointers to this function from arg.c */
+	db_mkdir("/target");
+	db_mkdir("/host");
+
+	return import(fn) >= 0;
+}
+
+
+static const char *db_get(const char *key)
+{
+	const char *path;
+	ht_t *ht;
+
+	path = db_split_path(key, &ht, NULL);
+	if (ht == NULL)
+		return NULL;
+	return ht_get(ht, path);
+}
+
+static const char *db_put(const char *key, const char *value)
+{
+	const char *path;
+	ht_t *ht;
+
+	path = db_split_path(key, &ht, NULL);
+	if (ht == NULL) {
+		error("db_put: can't find top level hash for '%s'\n", key);
+		abort();
+	}
+	return ht_set(ht, path, (void *)value);
+}
+
+#define assamble_path \
+	assert(strlen(key) + strlen(db_cwd) < sizeof(tmp)-1); \
+	sprintf(tmp, "%s/%s", db_cwd, key);
+
+const char *get(const char *key)
+{
+	char tmp[256];
+
+	if (*key == '/')
+		return db_get(key);
+	assamble_path;
+	return db_get(tmp);
+}
+
+const char *put(const char *key, const char *value)
+{
+	char tmp[256];
+
+	if (*key == '/')
+		return db_put(key, value);
+	assamble_path;
+	return db_put(tmp, value);
+}
+
+void db_init(void)
+{
+	DBs = ht_resize(ht_alloc(0), 16);
+}
+
+void db_uninit(void)
+{
+	ht_entry_t *h;
+	ht_t *dir;
+
+	for(h = ht_first(DBs); h != NULL; h = ht_next(DBs, h)) {
+		dir = h->value;
+		dir->refcount--;
+		if (dir->refcount == 0)
+			ht_free(dir);
+	}
+	ht_free(DBs);
+	if (db_cwd != NULL)
+		free(db_cwd);
+/* Just in case someone calls db_init again... */
+	db_cwd = NULL;
+	DBs    = NULL;
+}
+
+void db_cd(const char *path)
+{
+	assert(*path == '/');
+	if (db_cwd != NULL)
+		free(db_cwd);
+	db_cwd = strclone(path);
+}
+
+void db_mkdir(const char *path)
+{
+	ht_t *ht, *target;
+	assert(*path == '/');
+	target = ht_get(DBs, path+1);
+	if (target == NULL) {
+		ht = ht_resize(ht_alloc(1), 256);
+		ht_set(DBs, path+1, ht);
+	}
+}
+
+void db_rmdir(const char *path)
+{
+	ht_t *ht;
+	assert(*path == '/');
+	ht = ht_get(DBs, path+1);
+	if (ht == NULL)
+		return;
+	ht_del(DBs, path+1);
+/*	ht_free(ht); */
+}
+
+void db_link(const char *existing, const char *new)
+{
+	ht_t *ht;
+
+	assert(*new == '/');
+	ht = ht_get(DBs, existing+1);
+	assert(ht != NULL);
+	ht_set(DBs, new+1, ht);
+	ht->refcount++;
+}
+
+char *concat_nodes(const char *prefix, ...)
+{
+	char *buff;
+	const char *node, *value;
+	int allocated = 256, len, totallen;
+
+	va_list ap;
+	va_start(ap, prefix);
+	buff = malloc(allocated);
+	if (prefix != NULL) {
+		strcpy(buff, prefix);
+		totallen = strlen(prefix);
+		buff[totallen] = ' ';
+		totallen++;
+	}
+	else
+		totallen = 0;
+
+	while((node = va_arg(ap, const char *)) != NULL) {
+		value = get(node);
+		if (value != NULL) {
+			len = strlen(value);
+			if (totallen + len >= allocated) {
+				allocated = totallen + len + 256;
+				buff = realloc(buff, allocated);
+			}
+			memcpy(buff + totallen, value, len);
+			totallen += len;
+			buff[totallen] = ' ';
+			totallen++;
+
+			buff[totallen] = '\0';
+		}
+	}
+
+	buff[totallen - 1] = '\0';
+	va_end(ap);
+	return buff;
+}
+
+int node_istrue(const char *key)
+{
+	const char *s = get(key);
+	if (s == NULL)
+		return 0;
+	return istrue(s);
+}
diff --git a/scconfig/src/default/db.h b/scconfig/src/default/db.h
new file mode 100644
index 0000000..8124b03
--- /dev/null
+++ b/scconfig/src/default/db.h
@@ -0,0 +1,35 @@
+#include "ht.h"
+
+
+#define strue  "true"
+#define sfalse "false"
+#define istrue(s) ((s != NULL) && (*s == 't'))
+#define isfalse(s) ((s != NULL) && (*s == 'f'))
+/* the 3rd option is "unknown" */
+
+/* accessors */
+const char *get(const char *key);
+const char *put(const char *key, const char *data);
+void append(const char *key, const char *value);
+char *concat_nodes(const char *prefix, ...);
+int node_istrue(const char *key);
+
+
+/* init/uninit */
+void db_init(void);
+void db_uninit(void);
+
+/* export/import */
+int export(const char *fn, int export_empty, const char *root);
+int import(const char *fn);
+int import_args(const char *key, const char *fn);
+
+/* file system features */
+extern char *db_cwd;
+void db_cd(const char *path);
+void db_mkdir(const char *path);
+void db_link(const char *existing, const char *new);
+void db_rmdir(const char *path);
+
+extern ht_t *DBs;
+#define iscross  (ht_get(DBs, "target") != ht_get(DBs, "host"))
diff --git a/scconfig/src/default/dep.c b/scconfig/src/default/dep.c
new file mode 100644
index 0000000..09d2882
--- /dev/null
+++ b/scconfig/src/default/dep.c
@@ -0,0 +1,176 @@
+/*
+    scconfig - dependencies
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "dep.h"
+#include "db.h"
+#include "log.h"
+#include "libs.h"
+
+typedef struct {
+	int (*fn)(int logdepth, int fatal);
+} fn_wrap_t;
+
+
+static ht_t *deps = NULL;
+
+
+/* find name_ and decide if it was a wildcard request;
+   NOTE: there are requests and servers, both can be wildcard independently.
+    - if a request ends with a / *, it is an explicit wildcard request (*wild=1)
+    - if a request names a "directory" that is wildcard-server, that's a wildcard request (*wild=1)
+    - else the request is a normal request (*wild=0).
+   For normal requests, a required node was explicitly named; if that node is
+   not created by the detection function, that's a failure. For wildcard
+   requests we don't look for any specific node to be created.
+   TODO: we may still check if at least the directory is created
+   */
+fn_wrap_t *get_wrap(const char *name_, int *wild, int *missing)
+{
+	fn_wrap_t *w;
+	char *name, *sep;
+	int len;
+
+	len = strlen(name_);
+	*wild = name_[len-1] == '*';
+
+	if (*wild) {
+		char *pres;
+		pres = malloc(len+16);
+		memcpy(pres, name_, len-1);
+		strcpy(pres+len-1, "presents");
+		*missing = get(pres) == NULL;
+		if (*missing) { /* if there's no /presents, it may be a non-directory node with an actual non-empty string value */
+			const char *val;
+			pres[len-2] = '\0';
+			val = get(pres);
+			if (val != NULL)
+				*missing = !strlen(val);
+		}
+		free(pres);
+		if (!(*missing)) /* already detected, won't be detected again */
+			return NULL;
+	}
+	*missing = 1;
+
+	/* try full match first */
+	w = ht_get(deps, name_);
+	if (w != NULL)
+		return w;
+
+
+	/* try substituting the last part of the path with * for wildcard matches */
+	name = malloc(len+3);       /* worst case: ends in a / and we need to append *\0; allocate a bit more */
+	memcpy(name, name_, len+1); /* make a copy we can modify */
+	if (name[len-1] != '/') {
+		name[len] = '/';            /* append a / - if name_ was a "directory", this will result in name/ *  */
+		name[len+1] = '\0';
+	}
+
+	*wild = 1; /* if we append a / *, then it's a wildcard request */
+	for(;;) {
+		sep = str_rchr(name, '/');
+		if (sep == NULL)
+			goto error;
+		sep[1] = '*';
+		sep[2] = '\0';
+		w = ht_get(deps, name);
+		if (w != NULL) {
+			free(name);
+			return w;
+		}
+		*sep = '\0';
+		*wild = 0; /* cutting back the second layer - not wildcard request anymore, but a request to a specific node served by a wildcard */
+	}
+
+	/* no match, exit with error  */
+	error:;
+	*wild = 0;
+	free(name);
+	return NULL;
+}
+
+
+int require(const char *name, int logdepth, int fatal)
+{
+	fn_wrap_t *w;
+	int wild, missing;
+
+	if (get(name) == NULL) {
+		w = get_wrap(name, &wild, &missing);
+		if (!missing)
+			return 0;
+		if ((w == NULL) || (w->fn == NULL)) {
+			error("Node %s is required but I don't know how to detect it.\n", name);
+			abort();
+		}
+
+		logprintf(logdepth, "(Required node: '%s')\n", name);
+		if (w->fn(logdepth+1, fatal) != 0) {
+			if (fatal) {
+				error("Node %s is required but provided detection callback fails to find that feature on that system.\n", name);
+				abort();
+			}
+			else {
+				logprintf(logdepth, "(Feature not found, but it is not fatal)");
+				return 1;
+			}
+		}
+		if ((!wild) && (get(name) == NULL)) {
+			error("Node %s is required but provided detection callback didn't create it (looks like an internal error in scconfig).  (db_cwd='%s')\n", name, db_cwd);
+			abort();
+		}
+	}
+	return 0;
+}
+
+const char *dep_add(const char *name, int (*finder)(int logdepth, int fatal))
+{
+	fn_wrap_t *w;
+	w = malloc(sizeof(fn_wrap_t));
+	w->fn = finder;
+	return ht_set(deps, name, w);
+}
+
+void dep_init(void)
+{
+	deps = ht_resize(ht_alloc(0), 128);
+}
+
+void dep_uninit(void)
+{
+	ht_free(deps);
+}
+
+int is_dep_known(const char *name)
+{
+	return (ht_get(deps, name) != NULL);
+}
+
+void require_all(int fatal)
+{
+	ht_entry_t *h;
+
+	for(h = ht_first(deps); h != NULL; h = ht_next(deps, h))
+		require(h->key, 0, fatal);
+}
diff --git a/scconfig/src/default/dep.h b/scconfig/src/default/dep.h
new file mode 100644
index 0000000..830bbc7
--- /dev/null
+++ b/scconfig/src/default/dep.h
@@ -0,0 +1,12 @@
+#include "ht.h"
+
+int is_dep_known(const char *name);
+int require(const char *name, int logdepth, int fatal);
+const char *dep_add(const char *name, int (*finder)(int logdepth, int fatal));
+void require_all(int fatal);
+
+
+/* for internal use */
+void dep_init(void);
+void dep_uninit(void);
+
diff --git a/scconfig/src/default/deps_default.c b/scconfig/src/default/deps_default.c
new file mode 100644
index 0000000..21ce5bb
--- /dev/null
+++ b/scconfig/src/default/deps_default.c
@@ -0,0 +1,116 @@
+/*
+    scconfig - dependency list of default tests
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "dep.h"
+#include "find.h"
+
+void deps_default_init(void)
+{
+	dep_add("cc/cc",                    find_cc);
+	dep_add("cc/argstd/*",              find_cc_argstd);
+	dep_add("cc/cflags",                find_cc);
+	dep_add("cc/ldflags",               find_cc);
+	dep_add("cc/inline",                find_inline);
+	dep_add("cc/varargmacro",           find_varargmacro);
+	dep_add("cc/funcmacro",             find_funcmacro);
+	dep_add("cc/constructor",           find_constructor);
+	dep_add("cc/destructor",            find_destructor);
+	dep_add("cc/rdynamic",              find_rdynamic);
+	dep_add("cc/soname",                find_soname);
+	dep_add("cc/wlrpath",               find_wlrpath);
+	dep_add("cc/fpic",                  find_fpic);
+	dep_add("cc/alloca/*",              find_alloca);
+	dep_add("cc/_exit/*",               find__exit);
+	dep_add("cc/ldflags_dynlib",        find_ldflags_dynlib);
+	dep_add("cc/func_attr/unused/*",    find_fattr_unused);
+	dep_add("libs/ldl",                 find_lib_ldl);
+	dep_add("libs/LoadLibrary/*",       find_lib_LoadLibrary);
+	dep_add("libs/lpthread",            find_lib_lpthread);
+	dep_add("libs/lpthread-recursive",  find_lib_lpthread);
+	dep_add("libs/errno/*",             find_lib_errno);
+	dep_add("libs/printf_x",            find_printf_x);
+	dep_add("libs/printf_ptrcast",      find_printf_ptrcast);
+	dep_add("libs/snprintf",            find_snprintf);
+	dep_add("libs/snprintf_safe",       find_snprintf);
+	dep_add("libs/dprintf",             find_dprintf);
+	dep_add("libs/vdprintf",            find_vdprintf);
+	dep_add("libs/vsnprintf",           find_vsnprintf);
+	dep_add("libs/proc/_spawnvp/*",     find_proc__spawnvp);
+	dep_add("libs/proc/fork/*",         find_proc_fork);
+	dep_add("libs/proc/wait/*",         find_proc_wait);
+	dep_add("libs/fs/realpath/*",       find_fs_realpath);
+	dep_add("libs/fs/readdir/*",        find_fs_readdir);
+	dep_add("libs/fs/findnextfile/*",   find_fs_findnextfile);
+	dep_add("libs/fs/stat/macros/*",    find_fs_stat_macros);
+	dep_add("libs/fs/getcwd/*",         find_fs_getcwd);
+	dep_add("libs/fs/_getcwd/*",        find_fs__getcwd);
+	dep_add("libs/fs/getwd/*",          find_fs_getwd);
+	dep_add("libs/fs/mkdir/*",          find_fs_mkdir);
+	dep_add("libs/fs/_mkdir/*",         find_fs__mkdir);
+	dep_add("libs/fs/mkdtemp/*",        find_fs_mkdtemp);
+	dep_add("libs/io/pipe/*",           find_io_pipe);
+	dep_add("libs/io/dup2/*",           find_io_dup2);
+	dep_add("libs/time/usleep/*",       find_time_usleep);
+	dep_add("libs/types/stdint/*",      find_types_stdint);
+	dep_add("sys/types/size/*",         find_types_sizes);
+	dep_add("libs/time/Sleep/*",        find_time_Sleep);
+	dep_add("libs/time/gettimeofday/*", find_time_gettimeofday);
+	dep_add("libs/time/ftime/*",        find_time_ftime);
+	dep_add("libs/env/main_3arg/*",     find_main_arg3);
+	dep_add("libs/env/putenv/*",        find_putenv);
+	dep_add("libs/env/setenv/*",        find_setenv);
+	dep_add("libs/env/environ/*",       find_environ);
+	dep_add("signal/raise/*",           find_signal_raise);
+	dep_add("signal/names/*",           find_signal_names);
+	dep_add("fstools/cp",               find_fstools_cp);
+	dep_add("fstools/ln",               find_fstools_ln);
+	dep_add("fstools/mv",               find_fstools_mv);
+	dep_add("fstools/rm",               find_fstools_rm);
+	dep_add("fstools/mkdir",            find_fstools_mkdir);
+	dep_add("fstools/ar",               find_fstools_ar);
+	dep_add("fstools/ranlib",           find_fstools_ranlib);
+	dep_add("fstools/awk",              find_fstools_awk);
+	dep_add("fstools/cat",              find_fstools_cat);
+	dep_add("fstools/sed",              find_fstools_sed);
+	dep_add("fstools/chmodx",           find_fstools_chmodx);
+	dep_add("sys/name",                 find_uname);
+	dep_add("sys/uname",                find_uname);
+	dep_add("sys/triplet",              find_triplet);
+	dep_add("sys/sysid",                find_sysid);
+	dep_add("sys/shell",                find_shell);
+	dep_add("sys/shell_needs_quote",    find_shell);
+	dep_add("sys/tmp",                  find_tmp);
+	dep_add("sys/path_sep",             find_tmp);
+	dep_add("sys/shell_eats_backslash", find_tmp);
+	dep_add("sys/ext_exe",              find_uname);
+	dep_add("sys/ext_dynlib",           find_uname);
+	dep_add("sys/ext_stalib",           find_uname);
+	dep_add("sys/ptrwidth",             find_sys_ptrwidth);
+	dep_add("sys/byte_order",           find_sys_byte_order);
+	dep_add("sys/types/size_t/*",       find_types_size_t);
+	dep_add("sys/types/off_t/*",        find_types_off_t);
+	dep_add("sys/types/off64_t/*",      find_types_off64_t);
+	dep_add("sys/types/ptrdiff_t/*",    find_types_ptrdiff_t);
+
+	dep_add("/internal/filelist/cmd",    find_filelist);
+	dep_add("/internal/filelist/method", find_filelist);
+}
diff --git a/scconfig/src/default/deps_default.h b/scconfig/src/default/deps_default.h
new file mode 100644
index 0000000..77ebf1f
--- /dev/null
+++ b/scconfig/src/default/deps_default.h
@@ -0,0 +1 @@
+void deps_default_init(void);
diff --git a/scconfig/src/default/find.h b/scconfig/src/default/find.h
new file mode 100644
index 0000000..53c6e91
--- /dev/null
+++ b/scconfig/src/default/find.h
@@ -0,0 +1,105 @@
+/* cc */
+int find_cc(int logdepth, int fatal);
+int find_cc_argstd(int logdepth, int fatal);
+int find_inline(int logdepth, int fatal);
+int find_varargmacro(int logdepth, int fatal);
+int find_funcmacro(int logdepth, int fatal);
+int find_constructor(int logdepth, int fatal);
+int find_destructor(int logdepth, int fatal);
+int find_fattr_unused(int logdepth, int fatal);
+int find_rdynamic(int logdepth, int fatal);
+int find_soname(int logdepth, int fatal);
+int find_wlrpath(int logdepth, int fatal);
+int find_fpic(int logdepth, int fatal);
+int find_ldflags_dynlib(int logdepth, int fatal);
+int find_alloca(int logdepth, int fatal);
+int find__exit(int logdepth, int fatal);
+
+/* libs */
+int find_lib_ldl(int logdepth, int fatal);
+int find_lib_LoadLibrary(int logdepth, int fatal);
+int find_lib_lpthread(int logdepth, int fatal);
+int find_lib_errno(int logdepth, int fatal);
+
+/* fscalls */
+int find_fs_realpath(int logdepth, int fatal);
+int find_fs_readdir(int logdepth, int fatal);
+int find_fs_findnextfile(int logdepth, int fatal);
+int find_fs_stat_macros(int logdepth, int fatal);
+int find_fs_getcwd(int logdepth, int fatal);
+int find_fs__getcwd(int logdepth, int fatal);
+int find_fs_getwd(int logdepth, int fatal);
+int find_fs_mkdir(int logdepth, int fatal);
+int find_fs__mkdir(int logdepth, int fatal);
+int find_fs_mkdtemp(int logdepth, int fatal);
+
+/* printf */
+int find_printf_x(int logdepth, int fatal);
+int find_printf_ptrcast(int logdepth, int fatal);
+int find_snprintf(int logdepth, int fatal);
+int find_dprintf(int logdepth, int fatal);
+int find_vdprintf(int logdepth, int fatal);
+int find_vsnprintf(int logdepth, int fatal);
+
+/* proc */
+int find_proc__spawnvp(int logdepth, int fatal);
+int find_proc_fork(int logdepth, int fatal);
+int find_proc_wait(int logdepth, int fatal);
+
+/* fstools */
+int find_fstools_cp(int logdepth, int fatal);
+int find_fstools_ln(int logdepth, int fatal);
+int find_fstools_mv(int logdepth, int fatal);
+int find_fstools_rm(int logdepth, int fatal);
+int find_fstools_mkdir(int logdepth, int fatal);
+int find_fstools_ar(int logdepth, int fatal);
+int find_fstools_ranlib(int logdepth, int fatal);
+int find_fstools_awk(int logdepth, int fatal);
+int find_fstools_cat(int logdepth, int fatal);
+int find_fstools_sed(int logdepth, int fatal);
+int find_fstools_chmodx(int logdepth, int fatal);
+
+/* uname */
+int find_uname(int logdepth, int fatal);
+int find_triplet(int logdepth, int fatal);
+int find_sysid(int logdepth, int fatal);
+
+/* find_target */
+int find_target(int logdepth, int fatal);
+
+/* filelist */
+int find_filelist(int logdepth, int fatal);
+
+/* find_sys.c */
+int find_sys_ptrwidth(int logdepth, int fatal);
+int find_sys_byte_order(int logdepth, int fatal);
+int find_tmp(int logdepth, int fatal);
+int find_shell(int logdepth, int fatal);
+
+/* find_io.c */
+int find_io_pipe(int logdepth, int fatal);
+int find_io_dup2(int logdepth, int fatal);
+
+/* find_time.c */
+int find_time_usleep(int logdepth, int fatal);
+int find_time_Sleep(int logdepth, int fatal);
+int find_time_gettimeofday(int logdepth, int fatal);
+int find_time_ftime(int logdepth, int fatal);
+
+/* find_types.c */
+int find_types_stdint(int logdepth, int fatal);
+int find_types_sizes(int logdepth, int fatal);
+int find_types_size_t(int logdepth, int fatal);
+int find_types_off_t(int logdepth, int fatal);
+int find_types_off64_t(int logdepth, int fatal);
+int find_types_ptrdiff_t(int logdepth, int fatal);
+
+/* find_signal.c */
+int find_signal_names(int logdepth, int fatal);
+int find_signal_raise(int logdepth, int fatal);
+
+/* environ.c */
+int find_main_arg3(int logdepth, int fatal);
+int find_putenv(int logdepth, int fatal);
+int find_setenv(int logdepth, int fatal);
+int find_environ(int logdepth, int fatal);
diff --git a/scconfig/src/default/find_cc.c b/scconfig/src/default/find_cc.c
new file mode 100644
index 0000000..eda2991
--- /dev/null
+++ b/scconfig/src/default/find_cc.c
@@ -0,0 +1,636 @@
+/*
+    scconfig - detection of cc and compiler features
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+
+
+static int try_flags(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected)
+{
+	char *out;
+
+	logprintf(logdepth, "trying cc:try_flags with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags);
+
+	if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) {
+		if (((out == NULL) && (iscross)) || (strncmp(out, expected, strlen(expected)) == 0)) {
+			free(out);
+			return 1;
+		}
+		free(out);
+	}
+	return 0;
+}
+
+static int try_flags_inv(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected_bad)
+{
+	char *out;
+
+	logprintf(logdepth, "trying cc:try_flags with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags);
+
+	if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) {
+		if (((out == NULL) && (iscross)) || (strncmp(out, expected_bad, strlen(expected_bad)) != 0)) {
+			free(out);
+			return 1;
+		}
+		free(out);
+	}
+	return 0;
+}
+
+static int try(int logdepth, const char *cc, const char *test_c, const char *expected)
+{
+	return try_flags(logdepth, cc, test_c, NULL, NULL, expected);
+}
+
+
+static int trycc(int logdepth, const char *cc, const char *test_c)
+{
+	int ret;
+
+	if (cc == NULL)
+		return 0;
+
+	ret = try(logdepth, cc, test_c, "OK");
+	if (ret)
+		put("cc/cc", cc);
+	return ret;
+}
+
+int find_cc(int logdepth, int fatal)
+{
+	char *test_c = "#include <stdio.h>\nint main() { printf(\"OK\\n\");\nreturn 0;}\n";
+	char *out = NULL, *targetcc;
+	const char *cc, *cflags, *ldflags, *target, *sys;
+	int len;
+
+	require("sys/name", logdepth, fatal);
+
+	sys = istarget(db_cwd) ? "target" : "host";
+	report("Checking for cc (%s)... ", sys);
+	logprintf(logdepth, "find_cc: trying to find cc (%s)...\n", sys);
+	logdepth++;
+
+	cc = get("/arg/cc/cc");
+	if (cc == NULL) {
+		target = get("sys/target");
+		if (target != NULL) {
+			logprintf(logdepth+1, "find_cc: crosscompiling for '%s', looking for target cc\n", target);
+			len = strlen(target);
+			targetcc = malloc(len + 8);
+			memcpy(targetcc, target, len);
+			strcpy(targetcc + len, "-gcc");
+			if (!trycc(logdepth+1, targetcc, test_c)) {
+				strcpy(targetcc + len, "-cc");
+				if (!trycc(logdepth+1, targetcc, test_c)) {
+					report("FAILED: failed to find crosscompiler for target '%s'\n", target);
+					logprintf(logdepth, "find_cc: FAILED to find a crosscompiler for target '%s'\n", target);
+					return 1;
+				}
+			}
+			put("cc/cc", targetcc);
+		}
+		else {
+			cc = getenv("CC");
+			logprintf(logdepth, "find_cc: Detecting cc (host)\n");
+			/* Find a working cc (no arguments) */
+			if (!(((cc != NULL) && (trycc(logdepth+1, cc, test_c))) || trycc(logdepth+1, "gcc", test_c) || trycc(logdepth+1, "cc", test_c))) {
+				report("FAILED to find a compiler\n");
+				logprintf(logdepth, "find_cc: FAILED to find a compiler\n");
+				return 1;
+			}
+		}
+	}
+	else {
+		put("cc/cc", cc);
+		logprintf(logdepth+1, "using user supplied '%s' (will test later)\n", cc);
+	}
+
+	/* cflags */
+	cflags = get("/arg/cc/cflags");
+	if (cflags == NULL) {
+		logprintf(logdepth, "find_cc: Detecting -pipe\n");
+
+		if (compile_run(logdepth+1, test_c, NULL, "-pipe", "", &out) == 0) {
+			if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+				append("cc/cflags", " -pipe");
+			}
+			free(out);
+		}
+	}
+	else {
+		logprintf(logdepth+1, "using user supplied cflags '%s' (will test later)\n", cflags);
+	}
+	if (get("cc/cflags") == NULL)
+		put("cc/cflags", "");
+
+	/* ldflags */
+	ldflags = get("/arg/cc/ldflags");
+	if (ldflags != NULL) {
+		logprintf(logdepth+1, "using user supplied ldflags '%s' (will test later)\n", cflags);
+		put("cc/ldflags", ldflags);
+	}
+	if (get("cc/ldflags") == NULL)
+		put("cc/ldflags", "");
+
+	/* Final test of all arguments together */
+	logprintf(logdepth, "find_cc: final test on cc and all flags \n");
+	if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) != 0) {
+		report("FAILED to get the compiler and all flags to work together\n");
+		logprintf(logdepth, "find_cc: the compiler and all the flags don't work well together, aborting\n");
+		if (out != NULL)
+			free(out);
+		return 1;
+	}
+
+	report("OK ('%s', '%s', '%s')\n", get("cc/cc"), get("cc/cflags"), get("cc/ldflags"));
+	logprintf(logdepth, "find_cc: conclusion: cc='%s' cflags='%s' ldflags='%s'\n", get("cc/cc"), get("cc/cflags"), get("cc/ldflags"));
+	if (out != NULL)
+		free(out);
+	return 0;
+}
+
+int find_cc_argstd(int logdepth, int fatal)
+{
+	char *test_c = "#include <stdio.h>\nint main() { printf(\"OK\\n\");\nreturn 0;}\n";
+	char *out = NULL;
+	char **flg, *flags[] = {"-ansi", "-pedantic", "-Wall", NULL};
+
+	require("cc/cc", logdepth, fatal);
+
+	logprintf(logdepth, "find_cc: Detecting CC args\n");
+	report("Checking for cc args for std... ");
+
+	for(flg = flags; *flg != NULL; flg++) {
+		char name[128];
+		const char *found = "";
+		sprintf(name, "cc/argstd/%s", (*flg)+1);
+		if (compile_run(logdepth+1, test_c, NULL, *flg, "", &out) == 0) {
+			if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+				found = *flg;
+				report(" ");
+				report(found);
+			}
+			free(out);
+		}
+		put(name, found);
+	}
+
+	report("\n");
+	return 0;
+}
+
+int find_inline(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "static inline void test_inl()"
+		NL "{"
+		NL "	puts(\"OK\");"
+		NL "}"
+		NL "int main() {"
+		NL "	test_inl();"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for inline... ");
+	logprintf(logdepth, "find_inline: trying to find inline...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, "OK")) {
+		put("cc/inline", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("cc/inline", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_varargmacro(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#define pr(fmt, x...) {printf(\"PR \"); printf(fmt, x); }"
+		NL "int main() {"
+		NL "	pr(\"%d %d %s\", 42, 8192, \"test\");"
+		NL "	puts(\"\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for vararg macro... ");
+	logprintf(logdepth, "find_varargmacro: trying to find vararg macro...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, "PR 42 8192 test")) {
+		put("cc/varargmacro", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("cc/varargmacro", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_funcmacro(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	printf(\"%s\\n\", __func__);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for __func__ macro... ");
+	logprintf(logdepth, "find_funcmacro: trying to find __func__ macro...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, "main")) {
+		put("cc/funcmacro", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("cc/funcmacro", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_constructor(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "void startup() __attribute__ ((constructor));"
+		NL "void startup()"
+		NL "{"
+		NL "	puts(\"OK\");"
+		NL "}"
+		NL "int main() {"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for constructor... ");
+	logprintf(logdepth, "find_constructor: trying to find constructor...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, "OK")) {
+		put("cc/constructor", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("cc/constructor", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_destructor(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "void startup() __attribute__ ((destructor));"
+		NL "void startup()"
+		NL "{"
+		NL "	puts(\"OK\");"
+		NL "}"
+		NL "int main() {"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for destructor... ");
+	logprintf(logdepth, "find_destructor: trying to find destructor...\n");
+	logdepth++;
+	if (try(logdepth, NULL, test_c, "OK")) {
+		put("cc/destructor", strue);
+		report("Found.\n");
+		return 0;
+	}
+	put("cc/destructor", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+static int test_fattr(int logdepth, int fatal, const char *fattr)
+{
+	char path[64];
+	char test_c[256];
+	const char *test_c_tmp =
+		NL "#include <stdio.h>"
+		NL "static void test1() __attribute__ ((%s));"
+		NL "static void test1()"
+		NL "{"
+		NL "	puts(\"OK\");"
+		NL "}"
+		NL "int main() {"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	sprintf(test_c, test_c_tmp, fattr);
+	sprintf(path, "cc/func_attr/%s/presents", fattr);
+
+	report("Checking for function attribute %s... ", fattr);
+	logprintf(logdepth, "test_fattr: trying to find %s...\n", fattr);
+	logdepth++;
+	if (try(logdepth, NULL, test_c, "OK")) {
+		put(path, strue);
+		report("Found.\n");
+		return 0;
+	}
+	put(path, sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_fattr_unused(int logdepth, int fatal)
+{
+	return test_fattr(logdepth, fatal, "unused");
+}
+
+/* Hello world program to test compiler flags */
+static const char *test_hello_world =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+static int try_hello(int logdepth, const char *cflags, const char *ldflags, const char *name, const char *value)
+{
+	if (try_flags(logdepth, NULL, test_hello_world, cflags, ldflags, "OK")) {
+		put(name, value);
+		report("OK (%s)\n", value);
+		return 1;
+	}
+	return 0;
+}
+
+int find_rdynamic(int logdepth, int fatal)
+{
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for rdynamic... ");
+	logprintf(logdepth, "find_rdynamic: trying to find rdynamic...\n");
+	logdepth++;
+
+	if (try_hello(logdepth, NULL, "+-rdynamic", "cc/rdynamic", "-rdynamic")) return 0;
+	if (try_hello(logdepth, NULL, NULL,         "cc/rdynamic", ""))          return 0;
+
+	report("Not found.\n");
+	return 1;
+}
+
+int find_soname(int logdepth, int fatal)
+{
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for soname... ");
+	logprintf(logdepth, "find_soname: trying to find soname...\n");
+	logdepth++;
+
+	if (try_hello(logdepth, NULL, "+-Wl,-soname,libscconfig.0", "cc/soname", "-Wl,-soname,")) return 0;
+	if (try_hello(logdepth, NULL, NULL,                         "cc/soname", ""))             return 0;
+
+	report("Not found.\n");
+	return 1;
+}
+
+
+int find_wlrpath(int logdepth, int fatal)
+{
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for soname... ");
+	logprintf(logdepth, "find_soname: trying to find soname...\n");
+	logdepth++;
+
+	if (try_hello(logdepth, NULL, "+-Wl,-rpath=.",  "cc/wlrpath", "-Wl,-rpath=")) return 0;
+
+	report("Not found.\n");
+	return 1;
+}
+
+int find_fpic(int logdepth, int fatal)
+{
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for -fpic... ");
+	logprintf(logdepth, "find_fpic: trying to find -fpic...\n");
+	logdepth++;
+
+	if (try_hello(logdepth, NULL, "+-fPIC", "cc/fpic", "-fPIC")) return 0;
+	if (try_hello(logdepth, NULL, "+-fpic", "cc/fpic", "-fpic")) return 0;
+	if (try_hello(logdepth, NULL, NULL,     "cc/fpic", ""))      return 0;
+
+	report("Not found.\n");
+	return 1;
+}
+
+/* Hello world lib... */
+static const char *test_lib =
+		NL "#include <stdio.h>"
+		NL "int hello() {"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+/* ...and the corresponding host application */
+static const char *test_host =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "#include %s"
+		NL "int main() {"
+		NL "	void *handle = NULL;"
+		NL "	void (*func)() = NULL;"
+		NL "	char *error;"
+		NL
+		NL "	handle = dlopen(\"%s\", RTLD_NOW);"
+		NL "	if (handle == NULL) {"
+		NL "		printf(\"dlopen fails: \", dlerror());"
+		NL "		return 1;"
+		NL "	}"
+		NL "	func = dlsym(handle, \"hello\");"
+		NL "	if (func == NULL) {"
+		NL "		printf(\"dlsym fails: \", dlerror());"
+		NL "		return 1;"
+		NL "	}"
+		NL "	func();"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+static int try_dynlib(int logdepth, const char *cflags, char *concated_ldflags, const char *name, const char *value, const char *host_app_cflags, const char *host_app_ldflags)
+{
+	char test_host_app[1024];
+	const char *fpic;
+	const char *ld_include;
+	const char *dlc;
+	char *libname;
+	char *cflags_c;
+	char *oname = ".o";
+	int ret = 0;
+
+	dlc = get("libs/dl-compat");
+	if ((dlc != NULL) && (strcmp(dlc, strue) == 0))
+		ld_include = "<dl-compat.h>";
+	else
+		ld_include = "<dlfcn.h>";
+
+	fpic = get("cc/fpic");
+
+	if (cflags == NULL)
+		cflags="";
+	else if (*cflags == '+')
+		cflags++;
+
+	cflags_c = malloc(strlen(cflags) + 8 + sizeof(fpic));
+	sprintf(cflags_c, "+%s -c %s", cflags, fpic);
+
+
+	libname = (char *)get("sys/ext_dynlib");
+	if ((compile_code(logdepth, test_lib, &oname, NULL, cflags_c, NULL) != 0) ||
+			(compile_file(logdepth, oname, &libname, NULL, NULL, concated_ldflags) != 0)) {
+			report("FAILED (compiling dynlib)\n");
+	}
+	else {
+		sprintf(test_host_app, test_host, ld_include, libname);
+		if (try_flags(logdepth, NULL, test_host_app, host_app_cflags, host_app_ldflags, "OK")) {
+			put(name, value);
+			report("OK (%s)\n", value);
+			ret = 1;
+		}
+	}
+	unlink(libname);
+	unlink(oname);
+	free(libname);
+	free(oname);
+	free(concated_ldflags);
+	free(cflags_c);
+	return ret;
+}
+
+
+int find_ldflags_dynlib(int logdepth, int fatal)
+{
+
+	require("cc/cc",       logdepth, fatal);
+	require("cc/rdynamic", logdepth, fatal);
+	require("cc/fpic",     logdepth, fatal);
+	require("libs/ldl",    logdepth, fatal);
+
+	report("Checking for dynamic library ldflags... ");
+	logprintf(logdepth, "find_ldflags_dynlib: trying to find dynamic library ldflags...\n");
+	logdepth++;
+
+	if (try_dynlib(logdepth, NULL, concat_nodes("+-dynamic -shared", "cc/rdynamic", "libs/ldl", NULL), "cc/ldflags_dynlib", "-dynamic -shared", NULL, get("libs/ldl"))) return 0;
+	if (try_dynlib(logdepth, NULL, concat_nodes("+-shared",          "cc/rdynamic", "libs/ldl", NULL), "cc/ldflags_dynlib", "-shared",          NULL, get("libs/ldl"))) return 0;
+	report("Not found.\n");
+	return 1;
+}
+
+/* Hello world program to test compiler flags */
+static const char *test_alloca =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	char *s;"
+		NL "	s = alloca(128);"
+		NL "	if (s != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+static int try_alloca(int logdepth, const char *cflags, const char *ldflags, const char *name, const char *value)
+{
+	if (try_flags(logdepth, NULL, test_alloca, cflags, ldflags, "OK")) {
+		put(name, value);
+		report("OK (%s)\n", value);
+		return 1;
+	}
+	return 0;
+}
+
+int find_alloca(int logdepth, int fatal)
+{
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for alloca()... ");
+	logprintf(logdepth, "find_alloca: trying to find alloca()...\n");
+	logdepth++;
+
+	if (try_alloca(logdepth, NULL, NULL,     "cc/alloca/presents", "true"))      return 0;
+
+	put("cc/alloca/presents", "false");
+	report("Not found.\n");
+	return 1;
+}
+
+
+int find__exit(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	_exit(0);"
+		NL "	puts(\"BAD\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for _exit()... ");
+	logprintf(logdepth, "find__exit: trying to find _exit()...\n");
+	logdepth++;
+
+	if (try_flags_inv(logdepth, NULL, test_c, "+", "+", "BAD")) {
+		put("cc/_exit/presents", strue);
+		report("found\n");
+		return 0;
+	}
+
+	put("cc/_exit/presents", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
diff --git a/scconfig/src/default/find_environ.c b/scconfig/src/default/find_environ.c
new file mode 100644
index 0000000..933eec3
--- /dev/null
+++ b/scconfig/src/default/find_environ.c
@@ -0,0 +1,166 @@
+/*
+    scconfig - detection of environmental variable access features
+    Copyright (C) 2014  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_main_arg3(int logdepth, int fatal)
+{
+	char *out;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main(int argc, char *argv[], char *env[])"
+		NL "{"
+		NL "	char **e;"
+		NL "	int cnt;"
+		NL "	for(e = env, cnt = 0; *e != NULL; e++, cnt++) ;"
+		NL "	printf(\"%d\\n\", cnt);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for main() with 3 arguments... ");
+	logprintf(logdepth, "find_main_3args: checking for main() with 3 arguments\n");
+	if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) {
+		if (atoi(out) > 1) {
+			put("libs/env/main_3arg", strue);
+			report("OK\n");
+			free(out);
+			return 0;
+		}
+		free(out);
+		report("not found (broken output).\n");
+	}
+	else {
+		report("not found (no output).\n");
+	}
+	put("libs/env/main_3arg", sfalse);
+	return 1;
+}
+
+int find_environ(int logdepth, int fatal)
+{
+	char *out;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "extern char **environ;"
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	char **e;"
+		NL "	int cnt;"
+		NL "	for(e = environ, cnt = 0; *e != NULL; e++, cnt++) ;"
+		NL "	printf(\"%d\\n\", cnt);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for extern environ... ");
+	logprintf(logdepth, "find_environ: checking for extern environ\n");
+	if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) {
+		if (atoi(out) > 1) {
+			put("libs/env/environ", strue);
+			report("OK\n");
+			free(out);
+			return 0;
+		}
+		free(out);
+		report("not found (broken output).\n");
+	}
+	else {
+		report("not found (no output).\n");
+	}
+	put("libs/env/environ", sfalse);
+	return 1;
+}
+
+int find_putenv(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	putenv(\"SCCONFIG_TEST=bad\");"
+		NL "	putenv(\"SCCONFIG_TEST=OK\");"
+		NL "	printf(\"%s\\n\", getenv(\"SCCONFIG_TEST\"));"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for putenv()... ");
+	logprintf(logdepth, "find_putenv: trying to find putenv...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/env/putenv", test_c, "", NULL, NULL))
+		return 0;
+	if (try_icl(logdepth, "libs/env/putenv", test_c, "#define _XOPEN_SOURCE", NULL, NULL))
+		return 0;
+	if (try_icl(logdepth, "libs/env/putenv", test_c, "#define _SVID_SOURCE", NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/env/putenv");
+}
+
+
+int find_setenv(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	setenv(\"SCCONFIG_TEST\", \"bad\", 1);"
+		NL "	setenv(\"SCCONFIG_TEST\", \"OK\", 1);"
+		NL "	printf(\"%s\\n\", getenv(\"SCCONFIG_TEST\"));"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for setenv()... ");
+	logprintf(logdepth, "find_setenv: trying to find setenv...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/env/setenv", test_c, "", NULL, NULL))
+		return 0;
+	if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _BSD_SOURCE", NULL, NULL))
+		return 0;
+	if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _POSIX_C_SOURCE 200112L", NULL, NULL))
+		return 0;
+	if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _XOPEN_SOURCE 600", NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/env/setenv");
+}
+
diff --git a/scconfig/src/default/find_fscalls.c b/scconfig/src/default/find_fscalls.c
new file mode 100644
index 0000000..e7bc9a8
--- /dev/null
+++ b/scconfig/src/default/find_fscalls.c
@@ -0,0 +1,380 @@
+/*
+    scconfig - detection of standard library features: file system specific calls
+    Copyright (C) 2010  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_fs_realpath(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	char out[1024];"
+		NL "	if (realpath(\".\", out) == out)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for realpath()... ");
+	logprintf(logdepth, "find_fs_realpath: trying to find realpath...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/fs/realpath", test_c, NULL, NULL, NULL)) return 0;
+	if (try_icl(logdepth, "libs/fs/realpath", test_c, "#define _BSD_SOURCE", NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/fs/realpath");
+}
+
+
+int find_fs_readdir(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	DIR *dirp;"
+		NL "	struct dirent *dp;"
+		NL "	int found = 0;"
+		NL "	if ((dirp = opendir(\".\")) == 0)"
+		NL "		return -1;"
+		NL "	while ((dp = readdir(dirp)) != 0)"
+		NL "		if (strcmp(dp->d_name, \"configure\") == 0)"
+		NL "			found++;"
+		NL "	closedir(dirp);"
+		NL "	if (found == 1)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}";
+	char *includes[] = {
+		"#include <dirent.h>",
+		"#include <sys/dir.h>",    /* 4.2BSD */
+		NULL
+	};
+	char **i;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for readdir()... ");
+	logprintf(logdepth, "find_fs_readdir: trying to find readdir...\n");
+	logdepth++;
+
+	for (i = includes; *i != NULL; i++)
+		if (try_icl(logdepth, "libs/fs/readdir", test_c, *i, NULL, NULL))
+			return 0;
+	return try_fail(logdepth, "libs/fs/readdir");
+}
+
+
+int find_fs_findnextfile(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "#include <windows.h>"
+		NL "int main(int argc, char *argv[]) {"
+		NL "	WIN32_FIND_DATA fd;"
+		NL "	HANDLE h;"
+		NL "	int found=0;"
+		NL "	h = FindFirstFile(argv[0], &fd);"
+		NL "	if (h == INVALID_HANDLE_VALUE)"
+		NL "		return -1;"
+		NL "	while (FindNextFile(h, &fd) != 0);"
+		NL "		found++;"
+		NL "	FindClose(h);"
+		NL "	if (found > 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}";
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for FindNextFile()... ");
+	logprintf(logdepth, "find_fs_findnextfile: trying to find FindNextFile...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/fs/findnextfile", test_c, NULL, NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/fs/findnextfile");
+}
+
+
+int find_fs_stat_macros(int logdepth, int fatal)
+{
+	char *test_c_templ =
+		NL "#include <sys/stat.h>"
+		NL "#include <sys/types.h>"
+		NL "int main() {"
+		NL "	int a = %s(0);"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[256];
+
+	char *names[][3] = {
+		{"S_ISREG",  "S_IFREG",  NULL},
+		{"S_ISDIR",  "S_IFDIR",  NULL},
+		{"S_ISCHR",  "S_IFCHR",  NULL},
+		{"S_ISBLK",  "S_IFBLK",  NULL},
+		{"S_ISFIFO", "S_IFFIFO", NULL},
+		{"S_ISLNK",  "S_IFLNK",  NULL},
+		{"S_ISCHR",  "S_IFCHR",  NULL},
+		{"S_ISSOCK", "S_IFSOCK", NULL},
+		{NULL,       NULL,       NULL}
+		};
+	char **n;
+	int name, pr;
+	char nodename[128];
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for stat macros:\n");
+	logprintf(logdepth, "find_fs_stat_macros: trying to find stat macros...\n");
+	logdepth++;
+
+	pr = 0;
+	for(name = 0; *names[name] != NULL; name++) {
+		report(" %s...\t", names[name][0]);
+		for(n = &names[name][0]; *n != NULL; n++) {
+			sprintf(test_c, test_c_templ, *n);
+			if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
+				sprintf(nodename, "libs/fs/stat/macros/%s", names[name][0]);
+				put(nodename, *n);
+				report("found as %s\n", *n);
+				pr++;
+				goto found;
+			}
+		}
+		report("not found\n");
+		found:;
+	}
+
+	put("libs/fs/stat/macros/presents", ((pr > 0) ? (strue) : (sfalse)));
+	return (pr == 0);
+}
+
+int find_fs_getcwd(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <unistd.h>"
+		NL "int main() {"
+		NL "	char b[1024];"
+		NL "	if (getcwd(b, sizeof(b)) != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for getcwd... ");
+	logprintf(logdepth, "find_fs_getcwd: trying to find getcwd()...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/fs/getcwd", test_c, NULL, NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/fs/getcwd");
+}
+
+int find_fs__getcwd(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "int main() {"
+		NL "	char b[1024];"
+		NL "	if (_getcwd(b, sizeof(b)) != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for _getcwd... ");
+	logprintf(logdepth, "find_fs__getcwd: trying to find _getcwd()...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/fs/_getcwd", test_c, "#include <direct.h>", NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/fs/_getcwd");
+}
+
+
+int find_fs_getwd(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <unistd.h>"
+		NL "int main() {"
+		NL "	char b[8192];"
+		NL "	if (getwd(b) != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for getwd... ");
+	logprintf(logdepth, "find_fs_getwd: trying to find getwd()...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/fs/getwd", test_c, NULL, NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/fs/getwd");
+}
+
+int find_fs_mkdir(int logdepth, int fatal)
+{
+	char *dir;
+	char test_c[1024];
+	char *test_c_in =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	if (mkdir(\"%s\"%s) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	dir = tempfile_new("");
+	unlink(dir);
+
+	report("Checking for mkdir... ");
+	logprintf(logdepth, "find_fs_mkdir: trying to find mkdir()...\n");
+	logdepth++;
+
+	/* POSIX, 2 arguments, standard includes */
+	sprintf(test_c, test_c_in, dir, ", 0755");
+	if (try_icl(logdepth, "libs/fs/mkdir", test_c, "#include <sys/types.h>\n#include <sys/stat.h>\n", NULL, NULL)) {
+		if (!is_dir(dir))
+			goto oops1;
+		put("libs/fs/mkdir/num_args", "2");
+		rmdir(dir);
+		return 0;
+	}
+
+	/* POSIX, 2 arguments, no includes */
+	oops1:;
+	sprintf(test_c, test_c_in, dir, ", 0755");
+	if (try_icl(logdepth, "libs/fs/mkdir", test_c, NULL, NULL, NULL)) {
+		if (!is_dir(dir))
+			goto oops2;
+		put("libs/fs/mkdir/num_args", "2");
+		rmdir(dir);
+		return 0;
+	}
+
+	oops2:;
+	put("libs/fs/mkdir/includes", "");
+	put("libs/fs/mkdir/ldflags", "");
+	put("libs/fs/mkdir/cdflags", "");
+
+	rmdir(dir);
+	return try_fail(logdepth, "libs/fs/mkdir");
+}
+
+int find_fs__mkdir(int logdepth, int fatal)
+{
+	char *dir;
+	char test_c[1024];
+	char *test_c_in =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	if (_mkdir(\"%s\"%s) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	dir = tempfile_new("");
+	unlink(dir);
+
+	report("Checking for _mkdir... ");
+	logprintf(logdepth, "find_fs__mkdir: trying to find _mkdir()...\n");
+	logdepth++;
+
+	/* win32, 2 arguments, standard includes */
+	sprintf(test_c, test_c_in, dir, ", 0755");
+	if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include <direct.h>\n", NULL, NULL)) {
+		if (!is_dir(dir))
+			goto oops1;
+		put("libs/fs/_mkdir/num_args", "2");
+		rmdir(dir);
+		return 0;
+	}
+
+	oops1:;
+	/* win32, 1 argument, standard includes */
+	sprintf(test_c, test_c_in, dir, "");
+	if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include <direct.h>\n", NULL, NULL)) {
+		if (!is_dir(dir))
+			goto oops2;
+		put("libs/fs/_mkdir/num_args", "1");
+		rmdir(dir);
+		return 0;
+	}
+
+	oops2:;
+	put("libs/fs/_mkdir/includes", "");
+	put("libs/fs/_mkdir/ldflags", "");
+	put("libs/fs/_mkdir/cdflags", "");
+
+	rmdir(dir);
+	return try_fail(logdepth, "libs/fs/_mkdir");
+}
+
+int find_fs_mkdtemp(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <unistd.h>"
+		NL "#include <string.h>"
+		NL "int main() {"
+		NL "	char fn[32], *o;"
+		NL "	strcpy(fn, \"scc.XXXXXX\");"
+		NL "	o = mkdtemp(fn);"
+		NL "	if (o != NULL) {"
+		NL "		unlink(fn);"
+		NL "		puts(\"OK\");"
+		NL "	}"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for mkdtemp... ");
+	logprintf(logdepth, "find_fs_mkdtemp: trying to find mkdtemp()...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/fs/mkdtemp", test_c, "#include <stdlib.h>\n", NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/fs/mkdtemp");
+}
diff --git a/scconfig/src/default/find_fstools.c b/scconfig/src/default/find_fstools.c
new file mode 100644
index 0000000..f749042
--- /dev/null
+++ b/scconfig/src/default/find_fstools.c
@@ -0,0 +1,762 @@
+/*
+    scconfig - detection of file system tools
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+#include "dep.h"
+
+static int test_cp_ln(int logdepth, const char *command, int link)
+{
+	char *src, *dst, *src_esc, *dst_esc;
+	char *cmd, *result;
+	char *test_string = "works.";
+	int ret;
+
+	logprintf(logdepth, "trying '%s'\n", command);
+
+	src = tempfile_dump(test_string, "");
+	dst = tempfile_new("");
+	if (link)
+		unlink(dst);
+
+	src_esc = shell_escape_dup(src);
+	dst_esc = shell_escape_dup(dst);
+	cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32);
+	sprintf(cmd, "%s %s %s", command, src_esc, dst_esc);
+	run_shell(logdepth, cmd, NULL);
+	free(cmd);
+	free(src_esc);
+	free(dst_esc);
+
+	result = load_file(dst);
+	ret = !strcmp(result, test_string);
+	logprintf(logdepth+1, "result: '%s' == '%s' (%d)\n", result, test_string, ret);
+	free(result);
+
+	unlink(src);
+	free(src);
+
+	result = load_file(dst);
+	if (link) {
+		if (strcmp(result, test_string) == 0) {
+			report("Warning: link is copy (or hard link). ");
+			logprintf(logdepth+1, "Warning: link is copy (or hard link).\n");
+		}
+	}
+	else {
+		if (strcmp(result, test_string) != 0) {
+			report("Warning: copy is symlink. ");
+			logprintf(logdepth+1, "Warning: copy is symlink.\n");
+		}
+	}
+	free(result);
+
+	if (ret) {
+		if (link)
+			put("fstools/ln", command);
+		else
+			put("fstools/cp", command);
+
+		report("OK (%s)\n", command);
+	}
+
+	unlink(dst);
+	free(dst);
+	return ret;
+}
+
+static int test_mv(int logdepth, const char *command)
+{
+	char *src, *dst, *src_esc, *dst_esc;
+	char *cmd, *result;
+	char *test_string = "works.";
+	int ret;
+
+	logprintf(logdepth, "trying '%s'\n", command);
+
+	src = tempfile_dump(test_string, "");
+	dst = tempfile_new("");
+	unlink(dst);
+
+	src_esc = shell_escape_dup(src);
+	dst_esc = shell_escape_dup(dst);
+	cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32);
+	sprintf(cmd, "%s %s %s", command, src_esc, dst_esc);
+	run_shell(logdepth, cmd, NULL);
+	free(cmd);
+	free(src_esc);
+	free(dst_esc);
+
+	result = load_file(dst);
+	ret = !strcmp(result, test_string);
+	logprintf(logdepth+1, "result: '%s' == '%s' (%d)\n", result, test_string, ret);
+	free(result);
+
+	if (file_size(src) > 0) {
+		report("Warning: mv is copy. ");
+		logprintf(logdepth+1, "Warning: mv is copy.\n");
+	}
+
+	if (ret) {
+		put("fstools/mv", command);
+		report("OK (%s)\n", command);
+	}
+
+	unlink(dst);
+	unlink(src);
+	free(dst);
+	free(src);
+	return ret;
+}
+
+static int test_mkdir(int logdepth, const char *command)
+{
+	char *dir, *file;
+	char *dir_esc;
+	char *cmd, *result;
+	char *test_string = "works.";
+	int ret = 0, had_p;
+	FILE *f;
+
+	logprintf(logdepth, "trying '%s'\n", command);
+	dir = tempfile_new("");
+	dir_esc = shell_escape_dup(dir);
+	unlink(dir);
+
+	had_p = is_dir("-p");
+
+	cmd = malloc(strlen(command) + strlen(dir_esc) + 32);
+	sprintf(cmd, "%s %s", command, dir_esc);
+	run_shell(logdepth, cmd, NULL);
+	free(cmd);
+
+	file = malloc(strlen(dir) + 32);
+	sprintf(file, "%s/test", dir);
+	f = fopen(file, "w");
+	if (f != NULL) {
+		fputs(test_string, f);
+		fclose(f);
+		result = load_file(file);
+		if (strcmp(result, test_string) == 0)
+			ret = 1;
+		free(result);
+	}
+
+	unlink(file);
+	unlink(dir);
+
+	cmd = malloc(strlen(dir) + 32);
+	sprintf(cmd, "rmdir %s", dir_esc);
+	run_shell(logdepth, cmd, NULL);
+	free(cmd);
+
+	free(file);
+	free(dir);
+	free(dir_esc);
+
+	/* This is a bit ugly, but on win32 or other systems where mkdir works
+	   but -p doesn't have an effect, a directory called -p may be left over... */
+	if ((!had_p) && (is_dir("-p"))) {
+		unlink("-p");
+		return 0;
+	}
+
+	if (ret != 0) {
+		put("fstools/mkdir", command);
+		report("OK (%s)\n", command);
+	}
+
+	return ret;
+}
+
+static int test_rm(int logdepth, const char *command)
+{
+	char *src, *src_esc, *cmd, *test_string = "works.";
+	int ret;
+
+	logprintf(logdepth, "trying '%s'\n", command);
+
+	src = tempfile_dump(test_string, "");
+
+	if (file_size(src) < 0) {
+		report("error: can't create temp file\n");
+		free(src);
+		return 0;
+	}
+
+
+	src_esc = shell_escape_dup(src);
+	cmd = malloc(strlen(command) + strlen(src_esc) + 32);
+	sprintf(cmd, "%s %s", command, src_esc);
+	run_shell(logdepth, cmd, NULL);
+	free(cmd);
+	free(src_esc);
+
+	ret = file_size(src) < 0;
+
+	if (ret) {
+		put("fstools/rm", command);
+		report("OK (%s)\n", command);
+	}
+	else
+		unlink(src);
+
+	free(src);
+	return ret;
+}
+
+static int test_ar(int logdepth, const char *command)
+{
+	char *src, *dst, *src_esc, *dst_esc;
+	char *cmd, *result, *expected;
+	char *test_string = "works.";
+	const char *path_sep;
+	int ret = 0;
+
+	logprintf(logdepth, "trying '%s'\n", command);
+	path_sep = get("sys/path_sep");
+
+	src = tempfile_dump(test_string, "");
+	dst = tempfile_new("");
+	unlink(dst);
+
+	src_esc = shell_escape_dup(src);
+	dst_esc = shell_escape_dup(dst);
+	cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32);
+	sprintf(cmd, "%s ru %s %s", command, dst_esc, src_esc);
+	run_shell(logdepth, cmd, NULL);
+	sprintf(cmd, "%s t %s", command, dst_esc);
+	run_shell(logdepth, cmd, &result);
+	free(cmd);
+	free(dst_esc);
+	free(src_esc);
+
+	if (result != NULL) {
+		expected = str_rchr(src, *path_sep);
+		if (expected == NULL)
+			expected = src;
+		else
+			expected++;
+
+		ret = strncmp(expected, result, strlen(expected)) == 0;
+		if (ret) {
+			put("fstools/ar", command);
+			report("OK (%s)\n", command);
+		}
+		free(result);
+	}
+
+	unlink(src);
+	unlink(dst);
+	free(src);
+	free(dst);
+	return ret;
+}
+
+static int test_ranlib(int logdepth, const char *command, const char *obj)
+{
+	char *cmd, *archive, *archive_esc, *obj_esc;
+	const char *ar;
+	int ret;
+	ar = get("fstools/ar");
+	logprintf(logdepth, "trying '%s'\n", command);
+
+	archive = tempfile_new(".a");
+	archive_esc = shell_escape_dup(archive);
+	obj_esc = shell_escape_dup(obj);
+	cmd = malloc(strlen(command) + strlen(obj_esc) + strlen(archive_esc) + 64);
+
+	sprintf(cmd, "%s r %s %s", ar, archive_esc, obj_esc);
+	unlink(archive);
+	ret = run_shell(logdepth, cmd, NULL) == 0;
+	if (!ret)
+		goto fin;
+
+	sprintf(cmd, "%s %s", command, archive_esc);
+	ret = run_shell(logdepth, cmd, NULL) == 0;
+
+	if (ret) {
+		put("fstools/ranlib", command);
+		report("OK (%s)\n", command);
+	}
+
+fin:;
+	unlink(archive);
+	free(archive);
+	free(cmd);
+	free(archive_esc);
+	free(obj_esc);
+	return ret;
+}
+
+static int test_awk(int logdepth, const char *command)
+{
+	char cmd[1024];
+	char *out;
+	int ret = 0;
+	char *script, *script_esc;
+
+	/* For some reason windows awk doesn't like the code with NLs */
+	char *test_awk =
+		"BEGIN {"
+		" gsub(\"b\", \"B\", t);"
+		" print t;"
+		"}";
+
+	logprintf(logdepth, "trying '%s'\n", command);
+	script = tempfile_dump(test_awk, ".awk");
+	script_esc = shell_escape_dup(script);
+	sprintf(cmd, "%s -v \"t=blobb\" -f %s", command, script_esc);
+	free(script_esc);
+	run_shell(logdepth, cmd, &out);
+	unlink(script);
+	free(script);
+
+	if ((out != NULL) && (strncmp(out, "BloBB", 5) == 0)) {
+		put("fstools/awk", command);
+		report("OK (%s)\n", command);
+		ret = 1;
+	}
+
+	free(out);
+	return ret;
+}
+
+static int test_cat(int logdepth, const char *command)
+{
+	char cmd[1024];
+	char *out;
+	int ret = 0;
+	char *fn, *fn_esc;
+	const char *test_str = "hello world";
+
+	logprintf(logdepth, "trying '%s'\n", command);
+	fn = tempfile_dump(test_str, ".txt");
+	fn_esc = shell_escape_dup(fn);
+	sprintf(cmd, "%s %s", command, fn_esc);
+	run_shell(logdepth, cmd, &out);
+	unlink(fn);
+	free(fn);
+	free(fn_esc);
+
+	if ((out != NULL) && (strncmp(out, test_str, strlen(test_str)) == 0)) {
+		put("fstools/cat", command);
+		report("OK (%s)\n", command);
+		ret = 1;
+	}
+
+	free(out);
+	return ret;
+}
+
+static int test_sed(int logdepth, const char *command)
+{
+	char cmd[1024];
+	char *out;
+	int ret = 0;
+	char *fn, *fn_esc;
+	const char *test_str_in  = "hello world";
+	const char *test_str_out = "he11o wor1d";
+
+	logprintf(logdepth, "trying '%s'\n", command);
+	fn = tempfile_dump(test_str_in, ".txt");
+	fn_esc = shell_escape_dup(fn);
+	sprintf(cmd, "%s \"s/l/1/g\" < %s", command, fn_esc);
+	run_shell(logdepth, cmd, &out);
+	unlink(fn);
+	free(fn);
+	free(fn_esc);
+
+	if ((out != NULL) && (strncmp(out, test_str_out, strlen(test_str_out)) == 0)) {
+		put("fstools/sed", command);
+		report("OK (%s)\n", command);
+		ret = 1;
+	}
+
+	free(out);
+	return ret;
+}
+
+static int test_chmodx(int logdepth, const char *command)
+{
+	char *cmd, *tmp, *tmp_esc, *out, *s;
+	int ret;
+
+	logprintf(logdepth, "trying '%s'\n", command);
+	tmp = tempfile_dump("#!/bin/sh\necho OK\n", ".bat");
+
+	tmp_esc = shell_escape_dup(tmp);
+	cmd = malloc(strlen(command) + strlen(tmp_esc) + 16);
+	sprintf(cmd, "%s %s", command, tmp_esc);
+	ret = run_shell(logdepth, cmd, NULL) == 0;
+	free(cmd);
+	if (!ret) {
+		free(tmp_esc);
+		return ret;
+	}
+
+	ret = run(logdepth+1, tmp_esc, &out);
+	free(tmp_esc);
+
+	if (ret == 0) {
+		for(s = out; s != NULL; s = str_chr(s, '\n')) {
+			logprintf(logdepth+1, "chmod line to test: '%s'\n", s);
+			if ((s[0] == 'O') && (s[1] == 'K')) {
+				logprintf(logdepth+2, "(OK)\n");
+				ret = 1;
+				break;
+			}
+			s++;
+		}
+	}
+	else
+		ret = 0;
+
+	free(out);
+	if (ret) {
+		put("fstools/chmodx", command);
+		logprintf(logdepth, "chmodx command validated: '%s'\n", command);
+		report("OK (%s)\n", command);
+	}
+	unlink(tmp);
+	return ret;
+}
+
+
+int find_fstools_cp(int logdepth, int fatal)
+{
+	const char *cp;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for cp... ");
+	logprintf(logdepth, "find_fstools_cp: trying to find cp...\n");
+	logdepth++;
+
+	cp = get("/arg/fstools/cp");
+	if (cp == NULL) {
+		if (test_cp_ln(logdepth, "cp -rp", 0)) return 0;
+		if (test_cp_ln(logdepth, "cp -r",  0)) return 0;
+		if (test_cp_ln(logdepth, "copy /r",  0)) return 0; /* wine */
+	}
+	else {
+		report(" user provided (%s)...", cp);
+		if (test_cp_ln(logdepth, cp, 0)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_ln(int logdepth, int fatal)
+{
+	const char *ln;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for ln... ");
+	logprintf(logdepth, "find_fstools_ln: trying to find ln...\n");
+	logdepth++;
+
+	ln = get("/arg/fstools/ln");
+	if (ln == NULL) {
+		if (test_cp_ln(logdepth, "ln -sf",1 )) return 0;
+		if (test_cp_ln(logdepth, "ln -s",1 ))  return 0;
+		if (test_cp_ln(logdepth, "ln",   1))   return 0;
+		if (test_cp_ln(logdepth, "cp",   1))   return 0;
+	}
+	else {
+		report(" user provided (%s)...", ln);
+		if (test_cp_ln(logdepth, ln, 1)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_mv(int logdepth, int fatal)
+{
+	const char *mv;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for mv... ");
+	logprintf(logdepth, "find_fstools_mv: trying to find mv...\n");
+	logdepth++;
+
+	mv = get("/arg/fstools/mv");
+	if (mv == NULL) {
+		if (test_mv(logdepth, "mv"))   return 0;
+		if (test_mv(logdepth, "move")) return 0; /* win32 */
+		if (test_mv(logdepth, "cp"))   return 0;
+	}
+	else {
+		report(" user provided (%s)...", mv);
+		if (test_mv(logdepth, mv)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_rm(int logdepth, int fatal)
+{
+	const char *rm;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for rm... ");
+	logprintf(logdepth, "find_fstools_rm: trying to find rm...\n");
+	logdepth++;
+
+	rm = get("/arg/fstools/rm");
+	if (rm == NULL) {
+		if (test_rm(logdepth, "rm -rf")) return 0;
+		if (test_rm(logdepth, "rm -f"))  return 0;
+		if (test_rm(logdepth, "rm"))     return 0;
+		if (test_rm(logdepth, "del"))    return 0; /* for win32 */
+	}
+	else {
+		report(" user provided (%s)...", rm);
+		if (test_rm(logdepth, rm)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_mkdir(int logdepth, int fatal)
+{
+	const char *mkdir;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for mkdir... ");
+	logprintf(logdepth, "find_fstools_mkdir: trying to find mkdir...\n");
+	logdepth++;
+
+	mkdir = get("/arg/fstools/mkdir");
+	if (mkdir == NULL) {
+		if (test_mkdir(logdepth, "mkdir -p")) return 0;
+		if (test_mkdir(logdepth, "md"))       return 0; /* for win32 */
+	}
+	else {
+		report(" user provided (%s)...", mkdir);
+		if (test_mkdir(logdepth, mkdir)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_ar(int logdepth, int fatal)
+{
+	const char *ar, *target;
+	char *targetar;
+	int len;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	require("sys/path_sep", logdepth, fatal);
+
+
+	report("Checking for ar... ");
+	logprintf(logdepth, "find_fstools_ar: trying to find ar...\n");
+	logdepth++;
+
+	ar = get("/arg/fstools/ar");
+	if (ar == NULL) {
+		target = get("/arg/sys/target");
+		if (target != NULL) {
+			logprintf(logdepth+1, "find_ar: crosscompiling for '%s', looking for target ar\n", target);
+			len = strlen(target);
+			targetar = malloc(len + 8);
+			memcpy(targetar, target, len);
+			strcpy(targetar + len, "-ar");
+			if (test_ar(logdepth, targetar)) {
+				free(targetar);
+				return 0;
+			}
+			free(targetar);
+		}
+		if (test_ar(logdepth, "ar")) return 0;
+		if (test_ar(logdepth, "/usr/bin/ar")) return 0;
+	}
+	else {
+		report(" user provided (%s)...", ar);
+		if (test_ar(logdepth, ar)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_ranlib(int logdepth, int fatal)
+{
+	const char *ranlib, *target;
+	char *targetranlib;
+	int len;
+	char *test_code = NL "int zero() { return 0; }" NL;
+	char *obj = ".o";
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	require("fstools/ar", logdepth, fatal);
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for ranlib... ");
+	logprintf(logdepth, "find_fstools_ranlib: trying to find ranlib...\n");
+	logdepth++;
+
+	logprintf(logdepth, "compiling test object...\n");
+	if (compile_code(logdepth+1, test_code, &obj, NULL, "+-c", NULL) != 0) {
+		logprintf(logdepth, "ERROR: Can't compile test object\n");
+		report("ERROR: Can't compile test object\n");
+		abort();
+	}
+
+	ranlib = get("/arg/fstools/ranlib");
+	if (ranlib == NULL) {
+		target = get("/arg/sys/target");
+		if (target != NULL) {
+			logprintf(logdepth+1, "find_ranlib: crosscompiling for '%s', looking for target ranlib\n", target);
+			len = strlen(target);
+			targetranlib = malloc(len + 16);
+			memcpy(targetranlib, target, len);
+			strcpy(targetranlib + len, "-ranlib");
+			if (test_ranlib(logdepth, targetranlib, obj)) {
+				free(targetranlib);
+				return 0;
+			}
+			free(targetranlib);
+		}
+		if (test_ranlib(logdepth, "ranlib", obj)) goto found;
+		if (test_ranlib(logdepth, "/usr/bin/ranlib", obj)) goto found;
+		if (test_ranlib(logdepth, "ar -s", obj)) goto found;
+		if (test_ranlib(logdepth, "/usr/bin/ar -s", obj)) goto found;
+
+		/* some systems (for example IRIX) can't run s without doing
+		   something else; t is harmless */
+		if (test_ranlib(logdepth, "ar ts", obj)) goto found;
+		if (test_ranlib(logdepth, "/usr/bin/ar ts", obj)) goto found;
+
+		/* final fallback: some systems (for example minix3) simply
+		   do not have ranlib or ar equivalent; it's easier to detect
+		   a dummy command than to force conditions into Makefiles */
+		if (test_ranlib(logdepth, "true", obj)) goto found;
+	}
+	else {
+		report(" user provided (%s)...", ranlib);
+		if (test_ranlib(logdepth, ranlib, obj)) goto found;
+	}
+	unlink(obj);
+	free(obj);
+	return 1;
+found:;
+	unlink(obj);
+	free(obj);
+	return 0;
+}
+
+int find_fstools_awk(int logdepth, int fatal)
+{
+	const char *awk;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for awk... ");
+	logprintf(logdepth, "find_fstools_awk: trying to find awk...\n");
+	logdepth++;
+
+	awk = get("/arg/fstools/awk");
+	if (awk == NULL) {
+		if (test_awk(logdepth, "awk")) return 0;
+		if (test_awk(logdepth, "gawk")) return 0;
+		if (test_awk(logdepth, "mawk")) return 0;
+		if (test_awk(logdepth, "nawk")) return 0;
+	}
+	else {
+		report(" user provided (%s)...", awk);
+		if (test_awk(logdepth, awk)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_chmodx(int logdepth, int fatal)
+{
+	const char *chmod;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for chmod to executable... ");
+	logprintf(logdepth, "find_fstools_awk: trying to find chmod to executable...\n");
+	logdepth++;
+
+	chmod = get("/arg/fstools/chmodx");
+	if (chmod == NULL) {
+		if (test_chmodx(logdepth, "chmod +x")) return 0;
+		if (test_chmodx(logdepth, "chmod 755")) return 0;
+		if (test_chmodx(logdepth, "")) return 0; /* on some systems we don't need to do anything */
+	}
+	else {
+		report(" user provided (%s)...", chmod);
+		if (test_chmodx(logdepth, chmod)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_cat(int logdepth, int fatal)
+{
+	const char *cat;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for cat... ");
+	logprintf(logdepth, "find_fstools_cat: trying to find cat...\n");
+	logdepth++;
+
+	cat = get("/arg/fstools/cat");
+	if (cat == NULL) {
+		if (test_cat(logdepth, "cat")) return 0;
+		if (test_cat(logdepth, "type")) return 0;
+	}
+	else {
+		report(" user provided (%s)...", cat);
+		if (test_cat(logdepth, cat)) return 0;
+	}
+	return 1;
+}
+
+int find_fstools_sed(int logdepth, int fatal)
+{
+	const char *sed;
+
+	(void) fatal; /* to suppress compiler warnings about not using fatal */
+
+	report("Checking for sed... ");
+	logprintf(logdepth, "find_fstools_sed: trying to find sed...\n");
+	logdepth++;
+
+	sed = get("/arg/fstools/sed");
+	if (sed == NULL) {
+		if (test_sed(logdepth, "sed")) return 0;
+	}
+	else {
+		report(" user provided (%s)...", sed);
+		if (test_sed(logdepth, sed)) return 0;
+	}
+	return 1;
+}
diff --git a/scconfig/src/default/find_io.c b/scconfig/src/default/find_io.c
new file mode 100644
index 0000000..c20e994
--- /dev/null
+++ b/scconfig/src/default/find_io.c
@@ -0,0 +1,76 @@
+/*
+    scconfig - detect I/O features of the system
+    Copyright (C) 2010  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_io_pipe(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <unistd.h>"
+		NL "int main() {"
+		NL "	int fd[2];"
+		NL "	if (pipe(fd) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for pipe(2)... ");
+	logprintf(logdepth, "find_io_pipe: trying to find pipe(2)...\n");
+	logdepth++;
+
+
+	if (try_icl(logdepth, "libs/io/pipe", test_c, NULL, NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/io/pipe");
+}
+
+int find_io_dup2(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <unistd.h>"
+		NL "int main() {"
+		NL "	int fd;"
+		NL "	if (dup2(1, 4) == 4)"
+		NL "		write(4, \"OK\\n\", 3); "
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for dup2(2)... ");
+	logprintf(logdepth, "find_io_dup2: trying to find dup2(2)...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/io/dup2", test_c, NULL, NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/io/dup2");
+}
+
diff --git a/scconfig/src/default/find_libs.c b/scconfig/src/default/find_libs.c
new file mode 100644
index 0000000..7d04a24
--- /dev/null
+++ b/scconfig/src/default/find_libs.c
@@ -0,0 +1,260 @@
+/*
+    scconfig - detection of standard library features
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int trydlc(int logdepth, const char *test_c_dlc, const char *cflagsf, const char *ldflagsf, const char *dlc)
+{
+	char *cflags, *ldflags;
+
+	cflags = malloc(strlen(dlc) + 64);
+	ldflags = malloc(strlen(dlc)*2 + 256);
+	sprintf(cflags,  cflagsf, dlc);
+	sprintf(ldflags, ldflagsf, dlc, dlc);
+	if (try_icl(logdepth, NULL, test_c_dlc, NULL, cflags, ldflags)) {
+		*cflags  = ' ';
+		append("cc/cflags", cflags);
+		put("libs/ldl", ldflags);
+		put("libs/dl-compat", strue);
+		report("OK (%s and %s)\n", cflags, ldflags);
+		free(cflags);
+		free(ldflags);
+		return 1;
+	}
+	free(cflags);
+	free(ldflags);
+	return 0;
+}
+
+int find_lib_ldl(int logdepth, int fatal)
+{
+	const char *ldl, *dlc;
+	char *s;
+
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <dlfcn.h>"
+		NL "int main() {"
+		NL "	void *handle;"
+		NL "	handle = dlopen(\"/this file does not exist.\", RTLD_NOW);"
+		NL "	if (handle == NULL) printf(\"OK\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *test_c_dlc =
+		NL "#include <stdio.h>"
+		NL "#include <dl-compat.h>"
+		NL "int main() {"
+		NL "	void *handle;"
+		NL "	handle = dlopen(\"/this file does not exist.\", RTLD_NOW);"
+		NL "	if (handle == NULL) printf(\"OK\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for -ldl... ");
+	logprintf(logdepth, "find_lib_ldl: trying to find ldl...\n");
+	logdepth++;
+
+	ldl = get("/arg/libs/ldl");
+	if (ldl == NULL) {
+		dlc = get("/arg/libs/dl-compat");
+
+		if (dlc == NULL) {
+		/* If dlc is not explicitly requested by the user, try standard
+		   dl (see whether we need -ldl for dlopen()) */
+			if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
+				put("libs/ldl", "");
+				put("libs/ldl/includes", "#include <dlfcn.h>\\n");
+				report("OK ()\n");
+				return 0;
+			}
+			if (try_icl(logdepth, NULL, test_c, NULL, NULL, "+-ldl")) {
+				put("libs/ldl", "-ldl");
+				put("libs/ldl/includes", "#include <dlfcn.h>\\n");
+				report("OK (-ldl)\n");
+				return 0;
+			}
+		}
+		/* try dl-compat (dl compatibility lib) */
+		if (dlc != NULL) {
+			/* test at user supplied dlc prefix:
+			   - first assume the linker will find it
+			   - next assume gcc and pass rpath to the linker
+			   - finally try static linking */
+			if (trydlc(logdepth, test_c_dlc, "+-I%s/include", "-L%s/lib -ldl-compat\000%s", dlc)) {
+				put("libs/ldl/includes", "#include <dl-compat.h>\\n");
+				return 0;
+			}
+			if (trydlc(logdepth, test_c_dlc, "+-I%s/include", "-L%s/lib -Wl,-rpath=%s/lib -ldl-compat", dlc)) {
+				put("libs/ldl/includes", "#include <dl-compat.h>\\n");
+				return 0;
+			}
+			if (trydlc(logdepth, test_c_dlc, "+-I%s/include", "%s/lib/libdl-compat.a\000%s", dlc)) {
+				put("libs/ldl/includes", "#include <dl-compat.h>\\n");
+				return 0;
+			}
+		}
+		else if (try_icl(logdepth, NULL, test_c_dlc, NULL, NULL, "+-ldl-compat")) {
+			/* check at normal system installation */
+			put("libs/ldl", "-ldl-compat");
+			put("libs/ldl/includes", "#include <dl-compat.h>\\n");
+			report("OK (-ldl-compat)\n");
+			return 0;
+		}
+	}
+	else {
+		report("User provided... ");
+		s = malloc(strlen(ldl)+1);
+		*s = '+';
+		strcpy(s+1, ldl);
+		if (try_icl(logdepth, NULL, test_c, NULL, NULL, s)) {
+			put("libs/ldl", ldl);
+			put("libs/ldl/includes", "#include <dlfcn.h>\\n");
+			report("OK (%s)\n", ldl);
+			free(s);
+			return 0;
+		}
+		free(s);
+	}
+	report("Not found\n");
+	return 1;
+}
+
+int find_lib_LoadLibrary(int logdepth, int fatal)
+{
+	char *s;
+
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	void *handle;"
+		NL "	handle = LoadLibrary(\"/this file does not exist.\");"
+		NL "	if (handle == NULL) printf(\"OK\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for LoadLibrary... ");
+	logprintf(logdepth, "find_lib_LoadLibrary: trying to find LoadLibrary...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/LoadLibrary", test_c, "#include <windows.h>", NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/LoadLibrary");
+}
+
+int find_lib_lpthread(int logdepth, int fatal)
+{
+	const char *lpthread;
+	char *s;
+	int ret = 0;
+
+	char *test_c_recursive =
+		NL "#define _GNU_SOURCE 1 /* Needed for recursive thread-locking */"
+		NL "#include <pthread.h>"
+		NL "pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;"
+		NL "int main() {"
+		NL "	pthread_attr_t a;"
+		NL "	if (pthread_attr_init(&a) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	char *test_c_simple =
+		NL "#include <pthread.h>"
+		NL "int main() {"
+		NL "	pthread_attr_t a;"
+		NL "	if (pthread_attr_init(&a) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for -lpthread... ");
+	logprintf(logdepth, "find_lib_lpthread: trying to find lpthread...\n");
+	logdepth++;
+
+	lpthread = get("/arg/libs/lpthread");
+
+	if (lpthread != NULL) {
+		put("libs/lpthread", lpthread);
+		report("User provided... ");
+		s = malloc(strlen(lpthread)+1);
+		*s = '+';
+		strcpy(s+1, lpthread);
+	}
+	else
+		s = strclone("+-lpthread");
+
+	if (try_icl(logdepth, NULL, test_c_recursive, NULL, NULL, s)) {
+		put("libs/lpthread", (char *)(s+1));
+		put("libs/lpthread-recursive", strue);
+		report("OK, recursive (%s)\n", (char *)(s+1));
+	}
+	else if (try_icl(logdepth, NULL, test_c_simple, NULL, NULL, s)) {
+		put("libs/lpthread", (char *)(s+1));
+		put("libs/lpthread-recursive", sfalse);
+		report("OK, NOT RECURSIVE (%s)\n", (char *)(s+1));
+	}
+	else
+		ret = 1;
+
+	free(s);
+	return ret;
+}
+
+
+int find_lib_errno(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <errno.h>"
+		NL "int main() {"
+		NL "	errno = 0;"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for errno.h... ");
+	logprintf(logdepth, "find_lib_errno: trying to find errno...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/errno", test_c, NULL, NULL, NULL)) return 0;
+	return try_fail(logdepth, "libs/errno");
+}
diff --git a/scconfig/src/default/find_printf.c b/scconfig/src/default/find_printf.c
new file mode 100644
index 0000000..4471aca
--- /dev/null
+++ b/scconfig/src/default/find_printf.c
@@ -0,0 +1,312 @@
+/*
+    scconfig - detection of printf-related features
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int tryx(int logdepth, const char *test_c, const char *trying, const char *expected)
+{
+	char *out = NULL;
+	char buff[512];
+
+	logprintf(logdepth, "trying '%s'\n", trying);
+	sprintf(buff, test_c, trying, trying);
+	if (compile_run(logdepth+1, buff, NULL, NULL, NULL, &out) == 0) {
+		if (strncmp(out, expected, strlen(expected)) == 0) {
+			free(out);
+			return 1;
+		}
+		free(out);
+	}
+	return 0;
+}
+
+static int tryc(int logdepth, const char *test_c, const char *trying)
+{
+	char *out = NULL;
+	char buff[512];
+	char *spc, *end;
+
+	logprintf(logdepth, "trying '%s'\n", trying);
+	sprintf(buff, test_c, trying);
+	if (compile_run(logdepth+1, buff, NULL, NULL, NULL, &out) == 0) {
+		spc = str_chr(out, ' ');
+		if (spc == NULL)
+			return 0;
+		*spc = '\0';
+		spc++;
+		end = str_chr(spc, ' ');
+		if (end == NULL)
+			return 0;
+		*end = '\0';
+		if (strcmp(out, spc) == 0) {
+			free(out);
+			put("libs/printf_ptrcast", trying);
+			report("OK (%s)\n", trying);
+			return 1;
+		}
+		free(out);
+	}
+	return 0;
+}
+
+int find_printf_x(int logdepth, int fatal)
+{
+	const char *pfx;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	printf(\"'%s%%x'/'%s%%x'\\n\", (size_t)0x1234, NULL);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char *expected = "'0x1234'/'0x0'";
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for printf %%x prefix... ");
+	logprintf(logdepth, "find_printf_x: trying to find printf %%x prefix...\n");
+	logdepth++;
+
+	pfx = get("/arg/libs/printf_x");
+	if (pfx == NULL) {
+
+		if (tryx(logdepth, test_c, "", expected)) {
+			put("libs/printf_x", "");
+			report("OK ()\n");
+			return 0;
+		}
+		if (tryx(logdepth, test_c, "0x", expected)) {
+			put("libs/printf_x", "0x");
+			report("OK (0x)\n");
+			return 0;
+		}
+	}
+	else {
+		report("User provided... ");
+		if (tryx(logdepth, test_c, pfx, expected)) {
+			put("libs/printf_x", pfx);
+			report("OK (%s)\n", pfx);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+int find_printf_ptrcast(int logdepth, int fatal)
+{
+	const char *cast;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	printf(\"%%d %%d \\n\", sizeof(void *), sizeof(%s));"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for printf %%x pointer cast... ");
+	logprintf(logdepth, "find_printf_ptrcast: trying to find printf %%x pointer cast...\n");
+	logdepth++;
+
+	cast = get("/arg/libs/printf_ptrcast");
+	if (cast == NULL) {
+		if (tryc(logdepth, test_c, "unsigned int"))        return 0;
+		if (tryc(logdepth, test_c, "unsigned long"))       return 0;
+		if (tryc(logdepth, test_c, "unsigned long long"))  return 0;
+	}
+	else {
+		report("User provided... ");
+		if (tryc(logdepth, test_c, cast)) return 0;
+	}
+	return 1;
+}
+
+int find_snprintf(int logdepth, int fatal)
+{
+	char *out;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	char buff[9];"
+		NL "	char *s = buff+2;"
+		NL
+		NL "	/* build a fence */"
+		NL "	buff[0] = 0;"
+		NL "	buff[1] = 65;"
+		NL "	buff[7] = 66;"
+		NL "	buff[8] = 0;"
+		NL
+		NL "	snprintf(s, 4, \"%d\", 123456);"
+		NL "	if ((buff[0] == 0) && (buff[1] == 65) && (buff[7] == 65) && (buff[8] == 0))"
+		NL "	printf(\"%s\\n\", s);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for snprintf... ");
+	logprintf(logdepth, "find_snprintf_works: trying to find snprintf...\n");
+	logdepth++;
+	logprintf(logdepth, "trying snprintf...\n");
+
+	if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) {
+		if (strcmp(out, "123")) {
+			put("libs/snprintf", strue);
+			put("libs/snprintf_safe", strue);
+			report("OK (safe)\n");
+			free(out);
+			return 0;
+		}
+		if (strcmp(out, "1234")) {
+			put("libs/snprintf", strue);
+			put("libs/snprintf_safe", sfalse);
+			report("OK (UNSAFE)\n");
+			free(out);
+			return 0;
+		}
+		free(out);
+		report("not found (broken output).\n");
+	}
+	else {
+		report("not found (no output).\n");
+	}
+	put("libs/snprintf", sfalse);
+	return 1;
+}
+
+int find_dprintf(int logdepth, int fatal)
+{
+	char *out;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	dprintf(1, \"OK\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for dprintf... ");
+	logprintf(logdepth, "find_dprintf: trying to find dprintf...\n");
+	logdepth++;
+	logprintf(logdepth, "trying dprintf...\n");
+
+	if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) {
+		put("libs/dprintf", strue);
+		report("found\n");
+		free(out);
+		return 0;
+	}
+	put("libs/dprintf", sfalse);
+		report("not found\n");
+	return 1;
+}
+
+int find_vdprintf(int logdepth, int fatal)
+{
+	char *out;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "#include <stdarg.h>"
+		NL "void local_dprintf(int fd, const char *fmt, ...)"
+		NL "{"
+		NL "	va_list ap;"
+		NL "	va_start(ap, fmt);"
+		NL "	vdprintf(fd, fmt, ap);"
+		NL "}"
+		NL "int main() {"
+		NL "	local_dprintf(1, \"OK\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for vdprintf... ");
+	logprintf(logdepth, "find_vdprintf: trying to find vdprintf...\n");
+	logdepth++;
+	logprintf(logdepth, "trying vdprintf...\n");
+
+	if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) {
+		put("libs/vdprintf", strue);
+		report("found\n");
+		free(out);
+		return 0;
+	}
+	put("libs/vdprintf", sfalse);
+		report("not found\n");
+	return 1;
+}
+
+int find_vsnprintf(int logdepth, int fatal)
+{
+	char *out;
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "#include <stdarg.h>"
+		NL "void local_vsnprintf(char *s, int len, const char *fmt, ...)"
+		NL "{"
+		NL "	va_list ap;"
+		NL "	va_start(ap, fmt);"
+		NL "	vsnprintf(s, len, fmt, ap);"
+		NL "}"
+		NL "int main() {"
+		NL "	char s[16];"
+		NL "	*s = '\\0';"
+		NL "	local_vsnprintf(s, 14, \"OK\\n\");"
+		NL "	printf(\"%s\", s);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for vsnprintf... ");
+	logprintf(logdepth, "find_vsnprintf: trying to find vsnprintf...\n");
+	logdepth++;
+	logprintf(logdepth, "trying vsnprintf...\n");
+
+	if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) {
+		put("libs/vsnprintf", strue);
+		report("found\n");
+		free(out);
+		return 0;
+	}
+	put("libs/vsnprintf", sfalse);
+		report("not found\n");
+	return 1;
+}
diff --git a/scconfig/src/default/find_proc.c b/scconfig/src/default/find_proc.c
new file mode 100644
index 0000000..763c32e
--- /dev/null
+++ b/scconfig/src/default/find_proc.c
@@ -0,0 +1,137 @@
+/*
+    scconfig - detection of standard library features (processes)
+    Copyright (C) 2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_proc__spawnvp(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "int main() {"
+		NL "	const char *a[3] = {\"/c\", \"echo OK\", NULL};"
+		NL "	_spawnvp(_P_WAIT, \"cmd\", a);"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for _spawnvp... ");
+	logprintf(logdepth, "find_proc__spawnvp: trying to find _spawnvp...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/proc/_spawnvp", test_c, "#include <process.h>", NULL, NULL)) return 0;
+
+	return try_fail(logdepth, "libs/proc/_spawnvp");
+}
+
+
+
+int find_proc_fork(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	if (fork() == 0) { return 0; }"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	/* NOTE: can't print OK from the child process because of a possible race
+	   with the parent immediately exiting without wait(). */
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for fork... ");
+	logprintf(logdepth, "find_proc_fork: trying to find fork...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/proc/fork", test_c, "#include <unistd.h>", NULL, NULL)) return 0;
+
+	return try_fail(logdepth, "libs/proc/fork");
+}
+
+
+int find_proc_wait(int logdepth, int fatal)
+{
+	char *inc;
+	const char *inc1;
+	char test_c[1024];
+	char *test_c_in =
+		NL "%s\n"
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	int st = 0;"
+		NL "	if (fork() == 0) {"
+		NL "		printf(\"O\");"
+		NL "		return 42;"
+		NL "	}"
+		NL "	wait(&st);"
+		NL "	if (WIFEXITED(st) && (WEXITSTATUS(st) == 42))"
+		NL "		printf(\"K\");"
+		NL "	else"
+		NL "		printf(\"%d\", st);"
+		NL "	printf(\"\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL ;
+
+	require("cc/cc", logdepth, fatal);
+	if (require("libs/proc/fork", logdepth, fatal))
+		return try_fail(logdepth, "libs/proc/wait");
+
+	report("Checking for wait... ");
+	logprintf(logdepth, "find_proc_wait: trying to find wait...\n");
+	logdepth++;
+
+	inc1 = get("libs/proc/fork/includes");
+	if (inc1 != NULL) {
+		char *i, *o;
+		inc = strclone(inc1);
+		for(i = o = inc; *i != '\0'; i++,o++) {
+			if ((i[0] == '\\') && (i[1] == 'n')) {
+				*o = '\n';
+				i++;
+			}
+			else
+				*o = *i;
+		}
+		*o = '\0';
+		sprintf(test_c, test_c_in, inc);
+		free(inc);
+	}
+	else
+		sprintf(test_c, test_c_in, "");
+
+	if (try_icl(logdepth, "libs/proc/wait", test_c, "#include <sys/types.h>\n#include <sys/wait.h>", NULL, NULL)) return 0;
+
+	return try_fail(logdepth, "libs/proc/wait");
+}
diff --git a/scconfig/src/default/find_signal.c b/scconfig/src/default/find_signal.c
new file mode 100644
index 0000000..b5cfc3b
--- /dev/null
+++ b/scconfig/src/default/find_signal.c
@@ -0,0 +1,141 @@
+/*
+    scconfig - detection of standard library features
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int try_bad(int logdepth, const char *test_c, char *cflags, char *ldflags)
+{
+	char *out = NULL;
+
+	logprintf(logdepth, "trying signal (neg) with ldflags '%s'\n", ldflags == NULL ? get("cc/ldflags") : ldflags);
+	if (compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out) == 0) {
+		if (target_emu_fail(out) || (strncmp(out, "BAD", 3) == 0)) {
+			free(out);
+			return 0;
+		}
+		free(out);
+	}
+	return 1;
+}
+
+
+int find_signal_raise(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <signal.h>"
+		NL "int main(int argc, char *argv[]) {"
+		NL "	printf(\"OK\\n\");"
+		NL "	if (argc == 16)"
+		NL "		raise(1);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for raise()... ");
+	logprintf(logdepth, "find_signal_raise: trying to find raise()...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "signal/raise", test_c, NULL, NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "signal/raise");
+}
+
+
+int find_signal_names(int logdepth, int fatal)
+{
+	char *test_c_exists =
+		NL "#include <stdio.h>"
+		NL "#include <signal.h>"
+		NL "int main(int argc, char *argv[]) {"
+		NL "	printf(\"OK\\n\");"
+		NL "	if (argc == 16)"
+		NL "		raise(%s);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char *test_c_terms =
+		NL "#include <stdio.h>"
+		NL "#include <signal.h>"
+		NL "int main() {"
+		NL "	raise(%s);"
+		NL "	printf(\"BAD\\n\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[256];
+	const char *names[] = {"SIGINT", "SIGABRT", "SIGKILL", "SIGTERM", "SIGQUIT", "SIGHUP", "SIGFPE", "SIGSEGV", NULL};
+	const char **name;
+	char path[256], *pathend;
+	const char *prefix = "signal/names/";
+
+	require("cc/cc", logdepth, fatal);
+	require("signal/raise/*", logdepth, fatal);
+
+	strcpy(path, prefix);
+	pathend = path + strlen(prefix);
+
+	for(name = names; *name != NULL; name++) {
+		/* check whether it exists */
+		report("Checking whether %s exists... ", *name);
+		logprintf(logdepth, "find_signal_names: checking whether %s exists\n", *name);
+		logdepth++;
+		sprintf(test_c, test_c_exists, *name);
+		strcpy(pathend, *name);
+		if (!try_icl(logdepth, path, test_c, NULL, NULL, NULL)) {
+			logdepth--;
+			continue;
+		}
+
+		/* check whether it exists */
+		logdepth--;
+		report("Checking whether %s terminates... ", *name);
+		logprintf(logdepth, "find_signal_names: checking whether %s terminates\n", *name);
+		logdepth++;
+
+		sprintf(test_c, test_c_terms, *name);
+		sprintf(pathend, "%s/terminates", *name);
+		if (try_bad(logdepth, test_c, NULL, "")) {
+			put(path, strue);
+			report("terminates\n");
+		}
+		else {
+			report("does not terminate\n");
+			put(path, sfalse);
+		}
+		logdepth--;
+	}
+
+/* to avoid redetection */
+	put("signal/names/presents", strue);
+
+	return 0;
+}
+
diff --git a/scconfig/src/default/find_sys.c b/scconfig/src/default/find_sys.c
new file mode 100644
index 0000000..096801a
--- /dev/null
+++ b/scconfig/src/default/find_sys.c
@@ -0,0 +1,423 @@
+/*
+    scconfig - detect features of the system or the host/target computer
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_sys_ptrwidth(int logdepth, int fatal)
+{
+	char *end, W[32];
+	char *out = NULL;
+	int w;
+
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	void *ptr;"
+		NL "	printf(\"%d\\n\", sizeof(ptr));"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for pointer width... ");
+	logprintf(logdepth, "find_sys_ptrwidth: trying to find pointer width...\n");
+	logdepth++;
+
+	if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
+		w = strtol(out, &end, 10);
+		if ((*end != '\0') && (*end != '\n') && (*end != '\r')) {
+			report("FAILED (test code failed)\n");
+			logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end);
+			return 1;
+		}
+		sprintf(W, "%d", w * 8);
+		report("OK (%s bits)\n", W);
+		put("sys/ptrwidth", W);
+		logprintf(logdepth+1, "OK (%s bits)\n", W);
+	}
+	return 0;
+}
+
+int find_sys_byte_order(int logdepth, int fatal)
+{
+	char *end, *W;
+	char *out = NULL;
+	int w;
+
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "	long int i = 8;"
+		NL "	char *s = (char *)&i;"
+		NL "	printf(\"%d\\n\", s[0]);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for byte order... ");
+	logprintf(logdepth, "find_sys_byte_order: trying to find byte order...\n");
+	logdepth++;
+
+	if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
+		w = strtol(out, &end, 10);
+		if (((*end != '\0') && (*end != '\n') && (*end != '\r')) || ((w != 0) && (w != 8))) {
+			report("FAILED (test code failed)\n");
+			logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end);
+			return 1;
+		}
+		if (w == 0)
+			W = "MSB";
+		else
+			W = "LSB";
+
+		report("OK (%s first)\n", W);
+		put("sys/byte_order", W);
+		logprintf(logdepth+1, "OK (%s first)\n", W);
+	}
+	return 0;
+}
+
+static int test_shell_eats_backslash(int logdepth)
+{
+	char *test = "echo c:\\n";
+	char *out;
+
+
+	logprintf(logdepth, "testing if shell eats \\...\n");
+	run_shell(logdepth+1, test, &out);
+	if (out == NULL) {
+		logprintf(logdepth+1, "oops, couldn't run shell?! (returned NULL)\n");
+		report("ERROR: shell fails.");
+		abort();
+	}
+	if (out[2] == '\\') {
+		logprintf(logdepth, "shell does NOT eat \\...\n");
+		put("sys/shell_eats_backslash", sfalse);
+		free(out);
+		return 0;
+	}
+
+	free(out);
+	logprintf(logdepth, "shell eats \\...\n");
+	put("sys/shell_eats_backslash", strue);
+	return 1;
+}
+
+
+static int try_get_cwd(int logdepth, const char *cmd)
+{
+	char *cwd, *end;
+	run_shell(logdepth+1, cmd, &cwd);
+	if (cwd != NULL) {
+		end = strpbrk(cwd, "\r\n");
+		if (end != NULL)
+			*end = '\0';
+		if (*cwd != '\0') {
+			end = cwd + strlen(cwd) - 1;
+			while((*end == ' ') || (*end == '\t')) { *end = '\0'; end--; }
+			put("sys/tmp", cwd);
+			/* ugly hack for win32: paths there start as c:\ */
+			if ((cwd[1] == ':') && (cwd[2] == '\\'))
+				append("sys/tmp", "\\");
+			else
+				append("sys/tmp", "/");
+			logprintf(logdepth, "cwd is '%s'\n", get("sys/tmp"));
+			free(cwd);
+			return 1;
+		}
+		else
+			free(cwd);
+	}
+	return 0;
+}
+
+static int try_tmp(int logdepth)
+{
+	char *fn;
+
+	logprintf(logdepth, "validating temp dir '%s'\n", get("sys/tmp"));
+	fn = tempfile_new_noabort("");
+	if (fn != NULL) {
+		unlink(fn);
+		free(fn);
+		logprintf(logdepth, "temp dir works!\n");
+		return 1;
+	}
+
+	logprintf(logdepth, "temp dir fails\n");
+	return 0;
+}
+
+/* test temp dir with all sort of workarounds */
+static int try_tmp_all(int logdepth)
+{
+	const char *tmp, *si;
+	char c;
+	char *t, *so, *old_tmp;
+	int eats, n;
+
+	tmp = get("sys/tmp");
+
+	/* path must end in path separator */
+	c = tmp[strlen(tmp)-1];
+	if ((c != '/') && (c != '\\')) {
+		append("sys/tmp", "/");
+		tmp = get("sys/tmp");
+	}
+
+	logprintf(logdepth, "trying detected temp dir '%s'\n", tmp);
+	if (try_tmp(logdepth+1)) return 1;
+
+	/* try msys-on-windows hack: if path starts with /d/something, try d:/ instead */
+	if ((tmp[0] == '/') && (isalpha(tmp[1])) && (tmp[2] == '/')) {
+		/* for the next test we shouldn't use our half-detected tmp path but go with . */
+		old_tmp = strclone(tmp);
+		put("sys/tmp", "");
+		eats = istrue(get("sys/shell_eats_backslash"));
+		tmp = old_tmp;
+		logprintf(logdepth, "tmp2='%s' eats=%d\n", tmp, eats);
+		t = malloc(strlen(tmp) * 2);
+		t[0] = tmp[1];
+		t[1] = ':';
+		for(si = tmp + 2, so = t + 2; *si != '\0'; si++, so++) {
+			if (*si == '/') {
+				*so = '\\';
+				if (eats) {
+					for(n = 0; n < 3; n++) {
+						so++;
+						*so = '\\';
+					}
+				}
+			}
+			else
+				*so = *si;
+		}
+		*so = '\0';
+		free(old_tmp);
+
+		logprintf(logdepth, "trying windows fix: '%s'\n", t);
+		put("sys/tmp", t);
+		free(t);
+		if (try_tmp(logdepth+1)) {
+			if (eats)
+				put("sys/path_sep", "\\\\\\\\");
+			else
+				put("sys/path_sep", "\\");
+			return 1;
+		}
+		tmp = get("sys/tmp");
+	}
+
+	/* fail. Set back tmp to empty so next command has a chance to run */
+	put("sys/tmp", "");
+	return 0;
+}
+
+int find_tmp(int logdepth, int fatal)
+{
+	const char *usertmp;
+
+	/* we need shell for later tests; do this detection in . */
+	put("sys/tmp", "");
+	require("sys/shell", logdepth, fatal);
+
+	put("sys/path_sep", "/");
+
+	report("Detecting temp dir...");
+	logprintf(logdepth, "Finding temp dir (current working directory)...\n");
+
+	usertmp = get("/arg/sys/tmp");
+
+	/* . as tmp would fail for commands including a "cd" - this would cause
+	   temporary files left in the target dir. We start out with empty
+	   string (which is ., but on windows ./ would fail), and run
+	   pwd (without cd) to find out the current directory (as getcwd() is not
+	   portable). If pwd fails, we stay with ./ */
+	put("sys/tmp", "");
+
+	/* we need to know about shell backslash problem regardless of how
+	   we end up with tmp - currently tmp is ., where the test could run
+	   safely */
+	test_shell_eats_backslash(logdepth+1);
+
+	/* Special case: cross-compilation with emulator; we can not assume
+	   the emulator uses the same paths as the host system, while we mix
+	   accessing files from host and emu. If we stay in ., both emulator
+	   and host system should be (more or less) happy. */
+	if (istarget(db_cwd) && iscross) {
+		if (usertmp == NULL) {
+			report("using temp dir . for cross-compilation\n");
+			logprintf(logdepth, "staying with . for cross-compilation\n");
+		}
+		else {
+			put("sys/tmp", usertmp);
+			report("using user supplied temp dir '%s' for cross-compilation\n", usertmp);
+			logprintf(logdepth, "using user supplied temp dir '%s' for cross-compilation\n", usertmp);
+		}
+		return 0;
+	}
+
+	if ((usertmp != NULL))
+			put("sys/tmp", usertmp);
+
+	if (
+	   ((usertmp != NULL) && (try_tmp_all(logdepth+2))) ||                      /* try user supplied temp dir */
+	   ((try_get_cwd(logdepth+1, "pwd")) && (try_tmp_all(logdepth+2))) ||       /* try pwd for finding out cwd */
+	   ((try_get_cwd(logdepth+1, "echo %cd%") && (try_tmp_all(logdepth+2))))) { /* try windows-specific way for finding out cwd */
+
+		report(" validated %s\n", get("sys/tmp"));
+		logprintf(logdepth, "Detected temp dir '%s'\n", get("sys/tmp"));
+		return 0;
+	}
+
+	put("sys/tmp", "");
+	report("using temp dir fallback .\n");
+	logprintf(logdepth, "all temp directories failed, using . as tmp\n");
+	return 0;
+}
+
+int test_shell(const char *shell, int logdepth, int quote)
+{
+	char *test = "echo hello";
+	char *cmd;
+	char *out;
+	char *q;
+
+	if (quote)
+		q = "\"";
+	else
+		q = "";
+
+	logprintf(logdepth, "testing '%s' as shell\n", shell);
+	cmd = malloc(strlen(test) + strlen(shell) + 8);
+	sprintf(cmd, "%s %s%s%s", shell, q, test, q);
+
+	run(logdepth+1, cmd, &out);
+
+	free(cmd);
+
+	if ((out != NULL) && (strncmp(out, "hello", 5) == 0)) {
+		put("sys/shell", shell);
+		if (quote)
+			put("sys/shell_needs_quote", strue);
+		else
+			put("sys/shell_needs_quote", sfalse);
+		logprintf(logdepth, "accepted.\n");
+		free(out);
+		return 1;
+	}
+
+	logprintf(logdepth, "refused.\n");
+	free(out);
+	return 0;
+}
+
+static int find_shell_escape(int logdepth, int fatal, const char *shell)
+{
+	char cmdline[256];
+	char **t;
+	char *tests[] = {
+		"\\", "\\ {}&;|",
+		"^",  "^ &",
+		NULL, NULL
+	};
+	(void) fatal;  /* not used */
+	(void) shell;  /* not used */
+
+	report("Looking for a shell escape character... ");
+	logprintf(logdepth, "finding shell escape character...\n");
+
+	for(t = tests; *t != NULL; t += 2) {
+		char *s, *end, *out, *start;
+		strcpy(cmdline, "echo ");
+		end = cmdline+5;
+		for(s = t[1]; *s != '\0'; s++) {
+			*end++ = *t[0];
+			*end++ = *s;
+		}
+		*end = '\0';
+		run(logdepth+1, cmdline, &out);
+		if (out != NULL) {
+			int res;
+			if (*out == '\"') /* wine likes to wrap the output in quotes for some reason */
+				start = out+1;
+			else
+				start = out;
+
+			res = strncmp(start, t[1], strlen(t[1]));
+			free(out);
+			if (res == 0) {
+				report("found: '%s'\n", t[0]);
+				logprintf(logdepth, "found shell escape char '%s'\n", t[0]);
+				put("sys/shell_escape_char", t[0]);
+				return 0;
+			}
+		}
+	}
+	report("NOT FOUND\n");
+	logprintf(logdepth, "shell escape character not found\n");
+
+	return 1;
+}
+
+int find_shell(int logdepth, int fatal)
+{
+	const char *shells[] = {
+		"/bin/sh -c",
+		"/bin/bash -c",
+		"bash -c",
+		"cmd.exe /c",
+		"sh -c",
+		"/bin/dash -c",
+		"dash -c",
+		"/bin/ksh -c",
+		"ksh -c",
+		NULL
+	};
+	const char **s;
+
+	report("Looking for a shell... ");
+	logprintf(logdepth, "finding a shell\n");
+
+	for(s = shells; *s != NULL; s++) {
+		if ((test_shell(*s, logdepth+1, 0)) || (test_shell(*s, logdepth+1, 1))) {
+			report("%s\n", *s);
+			logprintf(logdepth, "found a shell '%s', need quote: %s\n", *s, get("sys/shell_needs_quote"));
+			return find_shell_escape(logdepth, fatal, *s);
+		}
+	}
+
+	report("NOT FOUND\n");
+	logprintf(logdepth, "shell not found\n");
+	return 1;
+}
diff --git a/scconfig/src/default/find_target.c b/scconfig/src/default/find_target.c
new file mode 100644
index 0000000..de67b0f
--- /dev/null
+++ b/scconfig/src/default/find_target.c
@@ -0,0 +1,50 @@
+/*
+    scconfig - glue layer for proper crosscompiling to target
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include "db.h"
+#include "libs.h"
+
+int find_target(int logdepth, int fatal)
+{
+	const char *target = get("/arg/sys/target");
+	const char *emu = get("/arg/sys/emu");
+
+	(void) logdepth; /* to suppress compiler warnings about not using logdepth */
+	(void) fatal;    /* to suppress compiler warnings about not using fatal */
+
+	/* Does target differ from host? */
+	if (target == NULL) {
+		db_link("/host", "/target");
+		return 0;
+	}
+	else
+		db_mkdir("/target");
+
+	put("/target/sys/target", target);
+	if (emu != NULL)
+		put("/target/sys/emu", emu);
+
+	/* If so, check if emulator is provided */
+	cross_blind = 1;
+	return 0;
+}
diff --git a/scconfig/src/default/find_time.c b/scconfig/src/default/find_time.c
new file mode 100644
index 0000000..11a8624
--- /dev/null
+++ b/scconfig/src/default/find_time.c
@@ -0,0 +1,119 @@
+/*
+    scconfig - detection of standard library features: time/date/sleep related calls
+    Copyright (C) 2011..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_time_usleep(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <unistd.h>"
+		NL "int main() {"
+		NL "	if (usleep(1) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for usleep()... ");
+	logprintf(logdepth, "find_time_usleep: trying to find usleep...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/time/usleep", test_c, NULL, NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/time/usleep");
+}
+
+int find_time_Sleep(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	Sleep(1);"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for Sleep()... ");
+	logprintf(logdepth, "find_time_Sleep: trying to find Sleep...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/time/Sleep", test_c, "#include \"windows.h\"", NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/time/Sleep");
+}
+
+int find_time_gettimeofday(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "int main() {"
+		NL "	struct timeval tv;"
+		NL "	if (gettimeofday(&tv, NULL) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for gettimeofday()... ");
+	logprintf(logdepth, "find_time_gettimeofday: trying to find gettimeofday...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/time/gettimeofday", test_c, "#include <sys/time.h>", NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/time/gettimeofday");
+}
+
+
+int find_time_ftime(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	struct timeb tb;"
+		NL "	if (ftime(&tb) == 0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for ftime()... ");
+	logprintf(logdepth, "find_time_ftime: trying to find ftime...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/time/ftime", test_c, "#include <sys/timeb.h>", NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/time/ftime");
+}
+
diff --git a/scconfig/src/default/find_types.c b/scconfig/src/default/find_types.c
new file mode 100644
index 0000000..73c4da1
--- /dev/null
+++ b/scconfig/src/default/find_types.c
@@ -0,0 +1,281 @@
+/*
+    scconfig - detection of types and type sizes
+    Copyright (C) 2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+/* assume there is no integer that is at least this wide, in bytes */
+#define MAX_INT_WIDTH 9
+
+static int try_size(int logdepth, char *cflags, char *ldflags, const char *type, int use_stdint, const char *path, const char **sizearr)
+{
+	char *out = NULL;
+	const char *test_c_template =
+		NL "#include <stdio.h>"
+		NL "int main() {"
+		NL "		printf(\"OK %%d\\n\", sizeof(%s));"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[512], *start;
+	const char *inc = "#include <stdint.h>\n";
+	int size;
+
+	if (use_stdint) {
+		strcpy(test_c, inc);
+		start = test_c + strlen(inc);
+	}
+	else
+		start = test_c;
+	sprintf(start, test_c_template, type);
+
+	report("Testing size of type %25s... ", type);
+
+	logprintf(logdepth, "trying size with ldflags '%s'\n", ldflags == NULL ? get("cc/ldflags") : ldflags);
+	if (compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out) == 0) {
+		if (target_emu_fail(out)) {
+			report(" FAIL (emulator)\n");
+			free(out);
+			return -1;
+		}
+
+		if (strncmp(out, "OK", 2) == 0) {
+			size = atoi(out+3);
+			if ((size > 0) && (size < MAX_INT_WIDTH)) {
+				sprintf(test_c, "%d", size);
+				put(path, test_c);
+				sizearr[size] = type;
+				report(" OK, size %d byte%s\n", size, (size > 1) ? "s" : "");
+			}
+			else {
+				report(" FAIL, size %d bytes\n", size);
+				size = -1;
+			}
+			free(out);
+			return size;
+		}
+		free(out);
+	}
+	report(" FAIL (compile)\n");
+	return -1;
+}
+
+int find_types_stdint(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdint.h>"
+		NL "int main() {"
+		NL "	if (sizeof(uint8_t) == 1)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for stdint.h... ");
+	logprintf(logdepth, "find_types_stdint: trying to find stdint.h...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/types/stdint", test_c, NULL, NULL, NULL))
+		return 0;
+	return try_fail(logdepth, "libs/types/stdint");
+}
+
+int find_types_sizes(int logdepth, int fatal)
+{
+	const char *stdint;
+	const char *sizearr_u[MAX_INT_WIDTH];
+	const char *sizearr_s[MAX_INT_WIDTH];
+	int n;
+	const char *path_template = "sys/types/size/%d_%c_int";
+	char path[64];
+
+	require("cc/cc", logdepth, fatal);
+	require("libs/types/stdint/presents", logdepth, 0);
+	stdint = get("libs/types/stdint/presents");
+
+	for(n = 0; n < MAX_INT_WIDTH; n++) {
+		sizearr_u[n] = NULL;
+		sizearr_s[n] = NULL;
+	}
+
+	try_size(logdepth+1, NULL, NULL, "unsigned long long int", 0, "sys/types/size/unsigned_long_long_int", sizearr_u);
+	try_size(logdepth+1, NULL, NULL, "unsigned char", 0, "sys/types/size/unsigned_char", sizearr_u);
+	try_size(logdepth+1, NULL, NULL, "unsigned short int", 0, "sys/types/size/unsigned_short_int", sizearr_u);
+	try_size(logdepth+1, NULL, NULL, "unsigned int", 0, "sys/types/size/unsigned_int", sizearr_u);
+	try_size(logdepth+1, NULL, NULL, "unsigned long int", 0, "sys/types/size/unsigned_long_int", sizearr_u);
+
+	try_size(logdepth+1, NULL, NULL, "signed long long int", 0, "sys/types/size/signed_long_long_int", sizearr_s);
+	try_size(logdepth+1, NULL, NULL, "signed char", 0, "sys/types/size/signed_char", sizearr_s);
+	try_size(logdepth+1, NULL, NULL, "signed short int", 0, "sys/types/size/signed_short_int", sizearr_s);
+	try_size(logdepth+1, NULL, NULL, "signed int", 0, "sys/types/size/signed_int", sizearr_s);
+	try_size(logdepth+1, NULL, NULL, "signed long int", 0, "sys/types/size/signed_long_int", sizearr_s);
+
+	if ((stdint != NULL) && (istrue(stdint))) {
+		try_size(logdepth+1, NULL, NULL, "uint8_t", 1, "sys/types/size/uint8_t", sizearr_u);
+		try_size(logdepth+1, NULL, NULL, "uint16_t", 1, "sys/types/size/uint16_t", sizearr_u);
+		try_size(logdepth+1, NULL, NULL, "uint32_t", 1, "sys/types/size/uint32_t", sizearr_u);
+		try_size(logdepth+1, NULL, NULL, "uint64_t", 1, "sys/types/size/uint64_t", sizearr_u);
+		try_size(logdepth+1, NULL, NULL, "int8_t", 1, "sys/types/size/int8_t", sizearr_s);
+		try_size(logdepth+1, NULL, NULL, "int16_t", 1, "sys/types/size/int16_t", sizearr_s);
+		try_size(logdepth+1, NULL, NULL, "int32_t", 1, "sys/types/size/int32_t", sizearr_s);
+		try_size(logdepth+1, NULL, NULL, "int64_t", 1, "sys/types/size/int64_t", sizearr_s);
+	}
+
+	for(n = 0; n < MAX_INT_WIDTH; n++) {
+		if (sizearr_u[n] != NULL) {
+			report("Found best fit %d bytes wide uint: %s\n", n, sizearr_u[n]);
+			sprintf(path, path_template, n, 'u');
+			put(path, sizearr_u[n]);
+		}
+		if (sizearr_s[n] != NULL) {
+			report("Found best fit %d bytes wide sint: %s\n", n, sizearr_s[n]);
+			sprintf(path, path_template, n, 's');
+			put(path, sizearr_s[n]);
+		}
+	}
+
+	put("sys/types/size/presents", strue); /* to avoid redetection */
+
+	return 0;
+}
+
+
+static int find_types_something_t(int logdepth, int fatal, const char *typ, const char *preinclude)
+{
+	char *out = NULL;
+	int res;
+	char test_c[512];
+	char node[256], *nodeend;
+	const char **include, *includes[] = {"#include <stdlib.h>", "#include <stddef.h>", "#include <sys/types/types.h>", "#include <unistd.h>", "#include <stdio.h>", NULL};
+
+	char *test_c_include =
+		NL "%s"
+		NL "int puts(const char *s);"
+		NL "%s"
+		NL "int main() {"
+		NL "	%s s;"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *test_c_broken =
+		NL "%s"
+		NL "int puts(const char *s);"
+		NL "%s"
+		NL "int main() {"
+		NL "	%s s;"
+		NL "	void *v;"
+		NL "	if (sizeof(v) != sizeof(s)) puts(\"yes\");"
+		NL "	else puts(\"no\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for type %s... ", typ);
+	logprintf(logdepth, "find_types_something_t: Checking for %s...\n", typ);
+	logdepth++;
+
+	sprintf(node, "sys/types/%s/", typ);
+	nodeend = node + strlen(node);
+
+	if (preinclude == NULL)
+		preinclude = "";
+
+	for(include = includes; *include != NULL; include++) {
+		sprintf(test_c, test_c_include, preinclude, *include, typ);
+		if ((compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) && (strncmp(out, "OK", 2) == 0)) {
+			report("Found; ");
+			logprintf(logdepth+1, "include %s works\n", *include);
+			sprintf(nodeend, "includes");
+			put(node, *include);
+			break;
+		}
+		logprintf(logdepth+1, "include %s fails\n", *include);
+		if (out != NULL)
+			free(out);
+	}
+	if (*include == NULL) {
+		report("Not found\n");
+		return 1;
+	}
+
+	sprintf(nodeend, "presents");
+	put(node, strue);
+
+	/* check if typ is broken (smaller than void *) */
+	sprintf(test_c, test_c_broken, preinclude, *include, typ);
+	if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
+		if ((out != NULL) && (strncmp(out, "yes", 3) == 0)) {
+			report("(%s is narrower than void *)\n", typ);
+			sprintf(nodeend, "broken");
+			put(node, strue);
+			res = 0;
+		}
+		else if ((out != NULL) && (strncmp(out, "no", 2) == 0)) {
+			report("(%s is not narrower than void *)\n", typ);
+			sprintf(nodeend, "broken");
+			put(node, sfalse);
+			res = 0;
+		}
+		else {
+			report("ERROR: test failed (%s)\n", out);
+			res = 1;
+		}
+	}
+	if (out != NULL)
+		free(out);
+	return res;
+}
+
+
+int find_types_size_t(int logdepth, int fatal)
+{
+	return find_types_something_t(logdepth, fatal, "size_t", NULL);
+}
+
+int find_types_off_t(int logdepth, int fatal)
+{
+	return find_types_something_t(logdepth, fatal, "off_t", NULL);
+}
+
+int find_types_off64_t(int logdepth, int fatal)
+{
+	return find_types_something_t(logdepth, fatal, "off64_t", NULL) &&
+	       find_types_something_t(logdepth, fatal, "off64_t", "#define _LARGEFILE64_SOURCE");
+}
+
+
+int find_types_ptrdiff_t(int logdepth, int fatal)
+{
+	return find_types_something_t(logdepth, fatal, "ptrdiff_t", NULL);
+}
+
diff --git a/scconfig/src/default/find_uname.c b/scconfig/src/default/find_uname.c
new file mode 100644
index 0000000..a7de4e0
--- /dev/null
+++ b/scconfig/src/default/find_uname.c
@@ -0,0 +1,342 @@
+/*
+    scconfig - evaluate uname and classify the system
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "regex.h"
+#include "log.h"
+#include "db.h"
+#include "libs.h"
+#include "dep.h"
+
+static void sys_unix(void)
+{
+	put("sys/ext_exe",    "");
+	put("sys/ext_dynlib", ".so");
+	put("sys/ext_stalib", ".a");
+}
+
+static void sys_netbsd(void)
+{
+	sys_unix();
+	put("cc/ldflags", "-Wl,-R/usr/pkg/lib -L/usr/pkg/lib"); /* TODO: is this the best way? */
+}
+
+static void sys_win32dlc(void)
+{
+	put("sys/ext_exe",    ".exe");
+	put("sys/ext_dynlib", ".dlc");
+	put("sys/ext_stalib", ".a");
+}
+
+typedef void (*callback_t)(void);
+
+typedef struct {
+	char *uname_regex;
+	char *name;
+	char *class;
+
+	callback_t callback;
+} uname_t;
+
+typedef struct {
+	char *file_name;
+	char *name;
+	char *class;
+
+	callback_t callback;
+} magic_file_t;
+
+/* Guess system class by uname; class is informative, nothing important
+   should depend on it.
+   Order *does* matter */
+uname_t unames[] = {
+	{"[Nn]et[Bb][Ss][Dd]",   "NetBSD", "UNIX",  sys_netbsd},
+	{"[Ll]inux",             "Linux",  "UNIX",  sys_unix},
+	{"[Bb][Ss][Dd]",         "BSD",    "UNIX",  sys_unix},
+	{"SunOS",                "SunOS",  "UNIX",  sys_unix},
+	{"OSF1",                 "OSF",    "UNIX",  sys_unix}, /* TODO: note the difference in cflags for debugging ("-ms -g") */
+	{"IRIX",                 "IRIX",   "UNIX",  sys_unix},
+	{"SunOS",                "SunOS",  "UNIX",  sys_unix},
+	{"[Mm]inix",             "Minix",  "UNIX",  sys_unix},
+	{"[Aa][Rr][Oo][Ss]",     "Aros",   "UNIX",  sys_unix},
+	{"^Darwin",              "MacOSX", "UNIX",  sys_unix},
+	{"[Cc]ygwin",            "cygwin", "WIN32", sys_win32dlc},
+	{"[Mm][Ii][Nn][Gg][Ww]", "mingw",  "WIN32", sys_win32dlc},
+	{"win32",                "win32",  "WIN32", sys_win32dlc}, /* vanilla windows */
+	{NULL, NULL, NULL, NULL}
+};
+
+/* Fallback: extract machine name from uname -a if uname -m fails */
+static const char *machine_names[] = {
+	"i[0-9]86[^ ]*", "x86_[^ ]*", "amd[0-9]*", "armv[0-9][^ ]*", "ppc[0-9]+",
+	"sparc[0-9]*", "BePC", "ia64", "x86", "IP[0-9]*", "k1om", "sun4u",
+	"RM600", "R4000", "alpha",
+	NULL
+};
+
+/* Fallback: extract system name from uname -a if uname -s fails */
+static const char *system_names[] = {
+	"[Ll]inux", "sn5[0-9]*", "CYGWIN_NT[^ ]*", "GNU[^ ]*", "DragonFly",
+	"[^ ]*BSD[^ ]*", "Haiku", "HP-UX", "AIX", "OS4000", "Interix",
+	"IRIX[0-9]*", "Darwin", "Minix", "MINGW[^ ]*", "ReliantUNIX[^ ]*",
+	"SunOS", "OSF1", "ULTRIX", "UWIN-W7", "IS/WB", "OS/390",
+	"SCO[^ ]*", "QNX",
+	NULL
+};
+
+/* Fallback: if uname -a fails, guess system by looking at "magic file names" */
+magic_file_t magic_files[] = {
+	{"/dev/null",                "UNIX",   "UNIX",  sys_unix},
+	{"c:\\config.sys",           "win32",  "WIN32", sys_win32dlc},
+	{"c:\\windows\\system.ini",  "win32",  "WIN32", sys_win32dlc},
+	{"c:\\windows\\win.ini",     "win32",  "WIN32", sys_win32dlc},
+	{"c:\\windows\\notepad.exe", "win32",  "WIN32", sys_win32dlc},
+	{NULL, NULL, NULL, NULL}
+} ;
+
+static int match(const char *regex, const char *str)
+{
+	re_comp(regex);
+	return re_exec(str);
+}
+
+/* match uname against each pattern on the list; on a match, put() the portion
+   of the string matched in node and return 1 */
+int uname_guess(const char *node, const char *uname, const char *list[])
+{
+	const char **l;
+	if (uname == NULL)
+		return 0;
+	for(l = list; *l != NULL; l++) {
+		if (match(*l, uname)) {
+			char *s;
+			int len = eopat[0] - bopat[0];
+			s = malloc(len+1);
+			memcpy(s, bopat[0], len);
+			s[len] = '\0';
+			put(node, s);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Don't worry about linear search or matching regexes all the time - this
+   function will be run at most two times */
+static callback_t lookup_uname(char **uname, const char **name, const char **class)
+{
+	uname_t *u;
+	for(u = unames; u->uname_regex != NULL; u++) {
+		if (
+		      ((*uname != NULL) && (match(u->uname_regex, *uname)))        /* uname match */
+		   || ((*name  != NULL)  && ((strcmp(u->name, *name) == 0)))   /* name match */
+		   || ((*class != NULL)  && ((strcmp(u->class, *class) == 0))) /* class match */
+		   ) {
+			if (*name  == NULL) *name  = u->name;
+			if (*class == NULL) *class = u->class;
+			return u->callback;
+		   }
+	}
+	return NULL;
+}
+
+static callback_t lookup_magic_file(int logdepth, const char **name, const char **class)
+{
+	magic_file_t *u;
+	for(u = magic_files; u->file_name != NULL; u++) {
+		if (is_file(u->file_name)) {
+			logprintf(logdepth, "%s -> %s\n", u->file_name, u->class);
+
+			if (*name  == NULL) *name  = u->name;
+			if (*class == NULL) *class = u->class;
+			return u->callback;
+		}
+	}
+	return NULL;
+}
+
+int find_uname(int logdepth, int fatal)
+{
+	const char *name, *class, *tname, *uname_orig;
+	char *s, *uname, *mname, *sname;
+	void (*callback)(void);
+
+	require("sys/tmp", logdepth, fatal);
+
+	if (istarget(db_cwd))
+		require("/target/sys/target", logdepth, fatal);
+
+	report("Checking for system type... ");
+	logprintf(logdepth, "[find_uname] checking for sys/name\n");
+	logdepth++;
+
+	tname = get("/arg/sys/target-name");
+	if (istarget(db_cwd) && (tname != NULL))
+		put("sys/name", tname);
+
+	tname = get("/arg/sys/target-uname");
+	if (istarget(db_cwd) && (tname != NULL))
+		put("sys/uname", tname);
+
+	name       = get("sys/name");
+	uname_orig = get("sys/uname");
+
+	if (name == NULL) {
+		if (uname_orig == NULL) {
+			logprintf(logdepth, "not set, running\n");
+			run_shell(logdepth, "uname -a", (char **)&uname);
+			if (uname != NULL) {
+				for(s = uname; *s != '\0'; s++)
+					if ((*s == '\n') || (*s == '\r')) *s = ' ';
+				put("sys/uname", uname);
+			}
+			else
+				put("sys/uname", "");
+
+			if (run_shell(logdepth, "uname -m", (char **)&mname) == 0)
+				put("sys/machine_name", strip(mname));
+			else
+				put("sys/machine_name", NULL);
+
+			if (mname != NULL)
+				free(mname);
+
+			if (run_shell(logdepth, "uname -o", (char **)&sname) == 0)
+				put("sys/system_name", strip(sname));
+			else if (run_shell(logdepth, "uname -s", (char **)&sname) == 0)
+				put("sys/system_name", strip(sname));
+			else
+				put("sys/system_name", NULL);
+			if (sname != NULL)
+				free(sname);
+		}
+
+		/* we have uname by now, set sys/name */
+		name  = NULL;
+		class = NULL;
+		callback = lookup_uname(&uname, &name, &class);
+		if (name == NULL) {
+			/* no uname or unknown system by uname - fallback: check for cross target */
+			const char *target = get("/arg/sys/target");
+			if ((target != NULL) && (strstr(target, "mingw") != NULL)) {
+				name = "WIN32";
+				report("(detected mingw cross compilation to WIN32)\n");
+			}
+			else {
+				report("Warning: unknown system\n");
+				name = "unknown";
+			}
+		}
+		put("sys/name", name);
+	}
+	else {
+		/* we had sys/name, that should be enough */
+		uname = NULL;
+		class = name;
+		callback = lookup_uname(&uname, &name, &class);
+	}
+
+	/* predefined and/or detected uname failed, try magic file method */
+	if (callback == NULL) {
+		logprintf(logdepth, "System class is unknown by uname, running heuristics...\n");
+		report("System class is unknown by uname, running heuristics... ");
+
+		callback = lookup_magic_file(logdepth + 1, &name, &class);
+	}
+
+
+	if (callback == NULL) {
+		/* System unknown. */
+		error("Unknown system '%s'\n", get("sys/uname"));
+		abort();
+	}
+
+	callback();
+	report("OK (name: %s; class: %s)\n", name, class);
+	put("sys/class", class);
+
+	/* fallbacks */
+	if (get("sys/machine_name") == NULL)
+		uname_guess("sys/machine_name", uname, machine_names);
+
+	if (get("sys/system_name") == NULL)
+		uname_guess("sys/system_name", uname, system_names);
+
+	return 0;
+}
+
+static int find_triplet_(int logdepth, int fatal, const char *nodename, int include_vendor, char *sep, char *esc)
+{
+	const char *machine, *vendor, *os;
+	char *triplet, *s;
+	char fake_sep[2];
+
+	fake_sep[0] = 1;
+	fake_sep[1] = 0;
+
+	require("sys/uname", logdepth, fatal);
+
+	machine = get("sys/machine_name");
+	if (machine == NULL)
+		machine = "unknown";
+
+	vendor = "unknown";
+
+	os = get("sys/system_name");
+	if (os == NULL)
+		os = "unknown";
+
+	if (include_vendor)
+		triplet = str_concat(fake_sep, machine, vendor, os, NULL);
+	else
+		triplet = str_concat(fake_sep, machine, os, NULL);
+
+	for(s = triplet; *s != '\0'; s++) {
+		if ((esc != NULL) && (*s == *sep))
+			*s = *esc;
+		if (isalnum(*s))
+			*s = tolower(*s);
+		else {
+			if (*s == *fake_sep)
+				*s = *sep;
+			else if (esc != NULL)
+				*s = *esc;
+			else
+				*s = '-';
+		}
+	}
+	put(nodename, triplet);
+	free(triplet);
+	return 0;
+}
+
+int find_triplet(int logdepth, int fatal)
+{
+	return find_triplet_(logdepth, fatal, "sys/triplet", 1, "-", NULL);
+}
+
+int find_sysid(int logdepth, int fatal)
+{
+	return find_triplet_(logdepth, fatal, "sys/sysid", 0, "-", "_");
+}
diff --git a/scconfig/src/default/hooks.h b/scconfig/src/default/hooks.h
new file mode 100644
index 0000000..b1d93d4
--- /dev/null
+++ b/scconfig/src/default/hooks.h
@@ -0,0 +1,30 @@
+/* Runs when a custom command line argument is found
+ returns true if no further argument processing should be done */
+int hook_custom_arg(const char *key, const char *value);
+
+/* If any of the int hooks return non-zero, that means failure and stops
+   the whole process */
+
+/* Runs before anything else */
+int hook_preinit(void);
+
+/* Runs after initialization */
+int hook_postinit(void);
+
+/* Runs after all arguments are read and parsed */
+int hook_postarg(void);
+
+/* Runs when things should be detected for the host system */
+int hook_detect_host(void);
+
+/* Runs when things should be detected for the target system */
+int hook_detect_target(void);
+
+/* Runs after detection hooks, should generate the output (Makefiles, etc.) */
+int hook_generate(void);
+
+/* Runs before everything is uninitialized */
+void hook_preuninit(void);
+
+/* Runs at the very end, when everything is already uninitialized */
+void hook_postuninit(void);
diff --git a/scconfig/src/default/ht.c b/scconfig/src/default/ht.c
new file mode 100644
index 0000000..b7955cf
--- /dev/null
+++ b/scconfig/src/default/ht.c
@@ -0,0 +1,257 @@
+/*
+    scconfig - hash tables
+    Copyright (C) 2007, 2008, 2009 by Szabolcs Nagy
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include "libs.h"
+#include "ht.h"
+
+#define HT_MINSIZE 8
+#define HT_MAXSIZE ((UINT_MAX >> 1U) + 1U)
+
+#define JUMP(i, j) i += j++
+#define JUMP_FIRST(i, j) j = 1, i += j++
+
+/* portable str hash */
+#define HASH_INIT 0xbabeface
+static unsigned int keyhash(const char *key) {
+	unsigned int a = HASH_INIT;
+
+	while (*key)
+		a += (a << 5) + *key++;
+	return a;
+}
+
+/* fill threshold = 3/4 */
+#define HT_LOG_THRES 2
+static void checkfill(ht_t *ht)
+{
+	if (ht->fill > ht->mask - (ht->mask >> HT_LOG_THRES) || ht->fill > ht->used << 2)
+		ht_resize(ht, ht->used << (ht->used > 1U << 15 ? 1 : 2));
+}
+
+static ht_t *ht_init(ht_t *ht, int isstr)
+{
+	ht->mask = HT_MINSIZE - 1;
+	ht->fill = 0;
+	ht->used = 0;
+	ht->isstr = isstr;
+	ht->table = calloc(ht->mask + 1, sizeof(ht_entry_t));
+	ht->refcount = 1;
+	return ht;
+}
+
+static ht_t *ht_uninit(ht_t *ht)
+{
+	ht_entry_t *entry;
+
+	for (entry = ht->table; entry != ht->table + ht->mask + 1; entry++)
+		if (ht_isused(entry)) {
+			if (ht->isstr)
+				free(entry->value);
+			free(entry->key);
+		}
+	free(ht->table);
+	return ht;
+}
+
+ht_t *ht_alloc(int isstr)
+{
+	ht_t *ht;
+
+	ht = malloc(sizeof(ht_t));
+	return ht_init(ht, isstr);
+}
+
+void ht_free(ht_t *ht)
+{
+	ht_uninit(ht);
+	free(ht);
+}
+
+ht_t *ht_clear(ht_t *ht)
+{
+	ht_uninit(ht);
+	return ht_init(ht, ht->isstr);
+}
+
+/* one lookup function to rule them all */
+static ht_entry_t *ht_lookup(ht_t *ht, const char *key, unsigned int hash)
+{
+	unsigned int mask;
+	unsigned int i;
+	unsigned int j;
+	ht_entry_t *table;
+	ht_entry_t *entry;
+	ht_entry_t *free_entry;
+
+	mask = ht->mask;
+	i = hash;
+	table = ht->table;
+	assert(ht->table);
+	entry = table + (i & mask);
+	if (ht_isempty(entry) || !strcmp(entry->key, key))
+		return entry;
+	/* free_entry: first deleted entry for insert */
+	free_entry = ht_isdeleted(entry) ? entry : NULL;
+	assert(ht->fill <= ht->mask);
+	for (JUMP_FIRST(i, j); ; JUMP(i, j)) {
+		entry = table + (i & mask);
+		if (ht_isempty(entry))
+			return (free_entry == NULL) ? entry : free_entry;
+		if (entry->hash == hash && !strcmp(entry->key, key))
+			return entry;
+		if (ht_isdeleted(entry) && free_entry == NULL)
+			free_entry = entry;
+	}
+}
+
+/* for resize: no deleted entries in ht, entry->key is not in ht, no strdup */
+static void cleaninsert(ht_t *ht, const ht_entry_t *entry)
+{
+	unsigned int i;
+	unsigned int j;
+	ht_entry_t *newentry;
+
+	i = entry->hash;
+	newentry = ht->table + (i & ht->mask);
+	if (!ht_isempty(newentry))
+		for (JUMP_FIRST(i, j); !ht_isempty(newentry); JUMP(i, j))
+			newentry = ht->table + (i & ht->mask);
+	++ht->fill;
+	++ht->used;
+	memcpy(newentry, entry, sizeof(ht_entry_t));
+}
+
+ht_t *ht_resize(ht_t *ht, unsigned int hint)
+{
+	unsigned int newsize;
+	unsigned int oldused;
+	ht_entry_t *oldtable, *newtable, *entry;
+
+	oldused = ht->used;
+	if (hint < oldused << 1)
+		hint = oldused << 1;
+	assert(hint <= HT_MAXSIZE && hint > oldused);
+	for (newsize = HT_MINSIZE; newsize < hint; newsize <<= 1);
+	newtable = calloc(newsize, sizeof(ht_entry_t));
+	oldtable = ht->table;
+	ht->mask = newsize - 1;
+	ht->fill = 0;
+	ht->used = 0;
+	ht->table = newtable;
+	for (entry = oldtable; oldused > 0; ++entry)
+		if (ht_isused(entry)) {
+			--oldused;
+			cleaninsert(ht, entry);
+		}
+	free(oldtable);
+	return ht;
+}
+
+void *ht_get(ht_t *ht, const char *key)
+{
+	ht_entry_t *entry;
+
+	entry = ht_lookup(ht, key, keyhash(key));
+	return ht_isused(entry) ? entry->value : NULL;
+}
+
+void *ht_insert(ht_t *ht, const char *key, void *value)
+{
+	unsigned int hash;
+	ht_entry_t *entry;
+
+	hash = keyhash(key);
+	entry = ht_lookup(ht, key, hash);
+	if (ht_isused(entry))
+		return entry->value;
+	if (ht_isempty(entry))
+		++ht->fill;
+	++ht->used;
+	entry->hash = hash;
+	entry->key = strclone(key);
+	entry->value = ht->isstr ? strclone(value) : value;
+	checkfill(ht);
+	return NULL;
+}
+
+const char *ht_set(ht_t *ht, const char *key, void *value)
+{
+	unsigned int hash;
+	ht_entry_t *entry;
+	char *k;
+
+	hash = keyhash(key);
+	entry = ht_lookup(ht, key, hash);
+	if (ht_isused(entry)) {
+		if (ht->isstr) {
+			free(entry->value);
+			entry->value = strclone(value);
+		} else
+			entry->value = value;
+		return entry->key;
+	}
+	if (ht_isempty(entry))
+		++ht->fill;
+	++ht->used;
+	entry->hash = hash;
+	entry->key = k = strclone(key);
+	entry->value = ht->isstr ? strclone(value) : value;
+	checkfill(ht);
+	return k;
+}
+
+const char *ht_del(ht_t *ht, const char *key)
+{
+	ht_entry_t *entry;
+
+	entry = ht_lookup(ht, key, keyhash(key));
+	if (!ht_isused(entry))
+		return NULL;
+	--ht->used;
+	free(entry->key);
+	if (ht->isstr)
+		free(entry->value);
+	entry->key = ht_deleted_key;
+	return ht_deleted_key;
+}
+
+ht_entry_t *ht_first(const ht_t *ht)
+{
+	ht_entry_t *entry = 0;
+
+	if (ht->used)
+		for (entry = ht->table; !ht_isused(entry); ++entry);
+	return entry;
+}
+
+ht_entry_t *ht_next(const ht_t *ht, ht_entry_t *entry)
+{
+	while (++entry != ht->table + ht->mask + 1)
+		if (ht_isused(entry))
+			return entry;
+	return 0;
+}
diff --git a/scconfig/src/default/ht.h b/scconfig/src/default/ht.h
new file mode 100644
index 0000000..ff9a61b
--- /dev/null
+++ b/scconfig/src/default/ht.h
@@ -0,0 +1,51 @@
+#ifndef STR_HT_H
+#define STR_HT_H
+
+/* char * -> void *  open addressing hashtable */
+/* keys and values are strdupped (strcloned) */
+
+#define ht_deleted_key ((char *)1)
+#define ht_isused(e) ((e)->key && (e)->key != ht_deleted_key)
+#define ht_isempty(e) (((e)->key == NULL) || (e)->key == ht_deleted_key)
+#define ht_isdeleted(e) ((e)->key == ht_deleted_key)
+
+typedef struct {
+	unsigned int hash;
+	char *key;
+	void *value;
+} ht_entry_t;
+
+typedef struct {
+	unsigned int mask;
+	unsigned int fill;
+	unsigned int used;
+	int isstr;
+	ht_entry_t *table;
+	int refcount;
+} ht_t;
+
+ht_t *ht_alloc(int isstr);
+void ht_free(ht_t *ht);
+ht_t *ht_clear(ht_t *ht);
+ht_t *ht_resize(ht_t *ht, unsigned int hint);
+
+/* value of ht[key], NULL if key is empty or deleted */
+void *ht_get(ht_t *ht, const char *key);
+/* ht[key] = value and return NULL or return ht[key] if key is already used */
+void *ht_insert(ht_t *ht, const char *key, void *value);
+/* ht[key] = value and return a pointer to the strdupped key */
+const char *ht_set(ht_t *ht, const char *key, void *value);
+/* delete key and return ht_deleted_key or NULL if key was not used */
+const char *ht_del(ht_t *ht, const char *key);
+
+/* iteration */
+#define foreach(ht, e) \
+	for (e = (ht)->table; e != (ht)->table + (ht)->mask + 1; e++) \
+		if (ht_isused(e))
+
+/* first used (useful for iteration) NULL if empty */
+ht_entry_t *ht_first(const ht_t *ht);
+/* next used (useful for iteration) NULL if there is no more used */
+ht_entry_t *ht_next(const ht_t *ht, ht_entry_t *entry);
+
+#endif
diff --git a/scconfig/src/default/lib_compile.c b/scconfig/src/default/lib_compile.c
new file mode 100644
index 0000000..00fc4dc
--- /dev/null
+++ b/scconfig/src/default/lib_compile.c
@@ -0,0 +1,287 @@
+/*
+    scconfig - library functions for compiling and running test code
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "log.h"
+#include "libs.h"
+#include "db.h"
+#include "dep.h"
+
+/*
+#define KEEP_TEST_SRCS
+*/
+
+int cross_blind = 0;
+
+
+static char *clone_flags(const char *input, const char *node)
+{
+	char *output;
+	const char *s;
+	int len;
+
+	if (input != NULL) {
+		if (*input == '+') {
+			s = get(node);
+			if (s != NULL) {
+				len = strlen(s);
+				output = malloc(len + strlen(input) + 4);
+				memcpy(output, s, len);
+				output[len] = ' ';
+				strcpy(output + len + 1, input + 1);
+			}
+			else
+				output = strclone(input);
+		}
+		else
+			output = strclone(input);
+	}
+	else {
+		s   = get(node);
+		if (s != NULL)
+			output = strclone(s);
+		else
+			output = strclone("");
+	}
+	return output;
+}
+
+int compile_file(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags)
+{
+	char cmd[2048];
+	char *ldflags_, *cflags_, *temp_out;
+	char *cc_esc, *fn_input_esc, *fn_output_esc, *temp_out_esc;
+	int ret;
+
+	if (*fn_output == NULL)
+		*fn_output = tempfile_new(get("sys/ext_exe"));
+	else
+		*fn_output = tempfile_new(*fn_output);
+	unlink(*fn_output);
+
+	temp_out = tempfile_new(".out");
+	cflags_  = clone_flags(cflags,  "cc/cflags");
+	ldflags_ = clone_flags(ldflags, "cc/ldflags");
+
+	cc_esc = shell_escape_dup(cc == NULL ? get("cc/cc") : cc);
+	fn_input_esc = shell_escape_dup(fn_input);
+	fn_output_esc = shell_escape_dup(*fn_output);
+	temp_out_esc = shell_escape_dup(temp_out);
+
+	sprintf(cmd, "%s \"%s %s %s %s -o %s 2>&1\" >%s", get("/host/sys/shell"), cc_esc, cflags_, fn_input_esc, ldflags_, fn_output_esc, temp_out_esc);
+
+	free(cc_esc);
+	free(fn_input_esc);
+	free(fn_output_esc);
+	free(temp_out_esc);
+	free(cflags_);
+	free(ldflags_);
+
+	logprintf(logdepth, "compile: '%s'\n", cmd);
+	ret = system(cmd);
+	log_merge(logdepth + 1, temp_out);
+#ifndef KEEP_TEST_SRCS
+	unlink(temp_out);
+#endif
+	free(temp_out);
+	logprintf(logdepth, "compile result: %d\n", ret);
+
+	return ret;
+}
+
+int compile_code(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags)
+{
+	char *temp_in;
+	int ret;
+
+	require("sys/ext_exe", logdepth, 1);
+
+	assert(testcode  != NULL);
+	assert(fn_output != NULL);
+
+	temp_in = tempfile_dump(testcode, ".c");
+	ret = compile_file(logdepth, temp_in, fn_output, cc, cflags, ldflags);
+#ifndef KEEP_TEST_SRCS
+	unlink(temp_in);
+#endif
+	free(temp_in);
+
+	return ret;
+}
+
+char *shell_escape_dup(const char *in)
+{
+	char *o, *out;
+	const char *i;
+	const char *esc = get("sys/shell_escape_char");
+
+	/* in the early phase, before detecting the shell, this happens */
+	if (esc == NULL)
+		return strclone(in);
+
+	out = malloc(strlen(in)*2+1);
+	for(i = in, o = out; *i != '\0'; i++) {
+		if (*i == *esc) {
+			*o++ = *esc;
+		}
+		else if (!isalnum(*i)) {
+			switch(*i) {
+				case '/':
+				case '_':
+				case '-':
+				case '.':
+					break;
+				default:
+					*o++ = *esc;
+			}
+		}
+		*o++ = *i;
+	}
+	*o = '\0';
+	return out;
+}
+
+int run(int logdepth, const char *cmd_, char **stdout_saved)
+{
+	char *cmd;
+	char *fn_out, *temp_out;
+	char *fn_out_esc, *temp_out_esc;
+	int ret;
+	const char *emu;
+
+	assert(cmd_ != NULL);
+
+	emu = get("sys/emu");
+
+	/* emu == NULL means we need an emulator but we don't have one and
+	   we should pretend everything went well (and of course can't provide
+	   output.) */
+	if (emu == NULL) {
+		if (stdout_saved != NULL)
+			*stdout_saved = NULL;
+		return 0;
+	}
+
+	/* emu == false means we need an emulator and we don't want to pretend -> fatal */
+	if (strcmp(emu, sfalse) == 0) {
+		error("Trying to run unavailable emulator (db_cwd='%s')\n", db_cwd);
+		abort();
+	}
+
+	temp_out = tempfile_new(".out");
+	fn_out   = tempfile_new("");
+
+	temp_out_esc = shell_escape_dup(temp_out);
+	fn_out_esc = shell_escape_dup(fn_out);
+	cmd = malloc(strlen(emu) + strlen(cmd_) + strlen(fn_out_esc) + strlen(temp_out_esc) + 32);
+	sprintf(cmd, "%s %s >%s 2>>%s", emu, cmd_, fn_out_esc, temp_out_esc);
+	free(temp_out_esc);
+	free(fn_out_esc);
+
+	logprintf(logdepth, "run: '%s'\n", cmd);
+	ret = system(cmd);
+	log_merge(logdepth + 1, temp_out);
+	unlink(temp_out);
+	free(temp_out);
+	logprintf(logdepth, "run result: %d\n", ret);
+	free(cmd);
+
+	if (stdout_saved != NULL) {
+		if (ret == 0) {
+			*stdout_saved = load_file(fn_out);
+			logprintf(logdepth, "stdout: '%s'\n", *stdout_saved);
+		}
+		else
+			*stdout_saved = NULL;
+	}
+
+	unlink(fn_out);
+	free(fn_out);
+	return ret;
+}
+
+int run_shell(int logdepth, const char *cmd_, char **stdout_saved)
+{
+	int ret;
+	char *cmd, *cmd_esc;
+	const char *emu;
+
+	emu = get("sys/emulator");
+	if (emu == NULL)
+		emu = "";
+
+	cmd_esc = shell_escape_dup(cmd_);
+	cmd = malloc(strlen(emu) + strlen(get("sys/shell")) + strlen(cmd_esc) + 16);
+	if (istrue(get("sys/shell_needs_quote")))
+		sprintf(cmd, "%s %s \"%s\"", emu, get("sys/shell"), cmd_);
+	else
+		sprintf(cmd, "%s %s %s", emu, get("sys/shell"), cmd_);
+	free(cmd_esc);
+
+	ret = run(logdepth, cmd, stdout_saved);
+	free(cmd);
+	return ret;
+}
+
+
+int compile_run(int logdepth, const char *testcode, const char *cc, const char *cflags, const char *ldflags, char **stdout_saved)
+{
+	int ret;
+	char *fn_output = NULL;
+
+	ret = compile_code(logdepth+1, testcode, &fn_output, cc, cflags, ldflags);
+
+	if (ret == 0) {
+		char *fn_output_esc = shell_escape_dup(fn_output);
+		ret = run(logdepth+1, fn_output_esc, stdout_saved);
+		free(fn_output_esc);
+	}
+
+	if (fn_output != NULL) {
+		unlink(fn_output);
+		free(fn_output);
+	}
+	return ret;
+}
+
+int run_script(int logdepth, const char *interpreter, const char *script, const char *suffix, char **out)
+{
+	char *temp, *cmd;
+	int res;
+
+	temp = tempfile_dump(script, suffix);
+	cmd = malloc(strlen(temp) + strlen(interpreter) + 4);
+	sprintf(cmd, "%s %s", interpreter, temp);
+
+	res = run(logdepth, cmd, out);
+
+	unlink(temp);
+	free(temp);
+	free(cmd);
+	return res;
+}
+
diff --git a/scconfig/src/default/lib_file.c b/scconfig/src/default/lib_file.c
new file mode 100644
index 0000000..a6da13b
--- /dev/null
+++ b/scconfig/src/default/lib_file.c
@@ -0,0 +1,236 @@
+/*
+    scconfig - library to query files and directories
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include "db.h"
+#include "libs.h"
+#include "log.h"
+#include "dep.h"
+
+int file_size(const char *name)
+{
+	struct stat buf;
+	if (stat(name, &buf) != 0)
+		return -1;
+	return buf.st_size;
+}
+
+char *tempdir_new(int logdepth, const char *suffix)
+{
+	char s[1024];
+	char *cmd;
+	const char *tmp;
+	const char *mkdir, *emu;
+	unsigned int rn, n;
+
+	require("sys/tmp", logdepth+1, 1);
+	tmp = get("sys/tmp");
+
+	if (strlen(suffix) > sizeof(s) - strlen(tmp) - 32) {
+		fprintf(stderr, "Not enough room for creating temporary file name\n");
+		abort();
+	}
+
+	require("fstools/mkdir", logdepth+1, 1);
+	mkdir = get("fstools/mkdir");
+
+	emu = get("sys/emu");
+	if (emu == NULL)
+		emu = "";
+
+	for(n = 0; n < 128; n++) {
+		rn = rand() % 100000;
+		sprintf(s, "%sscc_%u%s", tmp, rn, suffix);
+		if (!exists(s)) {
+			char *s_esc = shell_escape_dup(s);
+			cmd = malloc(strlen(s_esc) + strlen(mkdir) + 16);
+			sprintf(cmd, "%s %s", mkdir, s_esc);
+			run_shell(logdepth+1, cmd, NULL);
+			free(s_esc);
+			free(cmd);
+			if (is_dir(s))
+				return strclone(s);
+		}
+	}
+	error("Couldn't find a suitable temp dir name\n");
+	abort();
+}
+
+char *tempfile_new_noabort(const char *suffix)
+{
+	char s[1024];
+	const char *tmp;
+	unsigned int rn, n;
+	FILE *f;
+
+
+	require("sys/tmp", 0, 1);
+	tmp = get("sys/tmp");
+	if (strlen(suffix) > sizeof(s) - strlen(tmp) - 32) {
+		fprintf(stderr, "tempfile_new_noabort(): not enough room for creating temporary file name\n");
+		abort();
+	}
+
+	for(n = 0; n < 128; n++) {
+		rn = rand() % 100000;
+		sprintf(s, "%sscc_%u%s", tmp, rn, suffix);
+		if (!is_file(s)) { /* can not test for exists() because that would recurse is_dir */
+			f = fopen(s, "w");
+			if (f != NULL) {
+				fclose(f);
+				return strclone(s);
+			}
+		}
+	}
+	return NULL;
+}
+
+char *tempfile_new(const char *suffix)
+{
+	char *tmp;
+
+	tmp = tempfile_new_noabort(suffix);
+	if (tmp == NULL) {
+		error("Couldn't find a suitable temp file name\n");
+		abort();
+	}
+	return tmp;
+}
+
+char *tempfile_dump(const char *testcode, const char *suffix)
+{
+	char *fn;
+	FILE *f;
+
+	fn = tempfile_new(suffix);
+	f  = fopen(fn, "w");
+	fprintf(f, "%s", testcode);
+	fclose(f);
+	return fn;
+}
+
+char *load_file(const char *name)
+{
+	int size;
+	char *content;
+	FILE *f;
+
+	size = file_size(name);
+	if (size > 0) {
+		content = malloc(size+1);
+		*content = '\0';
+		f = fopen(name, "r");
+		if (f != NULL) {
+			fread(content, size, 1, f);
+			content[size] = '\0';
+			fclose(f);
+		}
+	}
+	else {
+		content = malloc(1);
+		*content = '\0';
+	}
+	return content;
+}
+
+int is_dir(const char *path)
+{
+	char *tmp, *path_esc;
+	int ret;
+
+	path_esc = shell_escape_dup(path);
+	tmp = malloc(strlen(path_esc) + 16);
+	sprintf(tmp, "cd %s", path_esc);
+	ret = run_shell(0, tmp, NULL);
+	free(tmp);
+	free(path_esc);
+	return !ret;
+}
+
+int is_file(const char *path)
+{
+	return file_size(path) >= 0;
+}
+
+int exists(const char *path)
+{
+	return is_file(path) || is_dir(path);
+}
+
+
+int exists_in(const char *dir, const char *file)
+{
+	char *path;
+	int ret;
+
+	path = malloc(strlen(dir) + strlen(file) + 5);
+	sprintf(path, "%s/%s", dir, file);
+	ret = is_file(path) || is_dir(path);
+	free(path);
+	return ret;
+}
+
+const char *file_name_ptr(const char *path)
+{
+	const char *s;
+	s = str_rchr((char *)path, '/');
+	if (s == NULL)
+		s = str_rchr((char *)path, '\\');
+	return s;
+}
+
+char *file_name(const char *path)
+{
+	const char *s;
+	s = file_name_ptr(path);
+	if (s == NULL)
+		return strclone(path);
+	s++;
+	return strclone(s);
+}
+
+char *dir_name(const char *path)
+{
+	char *s, *r;
+	s = strclone(path);
+	r = (char *)file_name_ptr(s);
+	if (r == NULL) {
+		free(s);
+		return strclone("");
+	}
+	*r = '\0';
+	return s;
+}
+
+int touch_file(const char *path)
+{
+	FILE *f;
+	f = fopen(path, "a");
+	if (f == NULL)
+		return -1;
+	fclose(f);
+	return 0;
+}
diff --git a/scconfig/src/default/lib_filelist.c b/scconfig/src/default/lib_filelist.c
new file mode 100644
index 0000000..563d933
--- /dev/null
+++ b/scconfig/src/default/lib_filelist.c
@@ -0,0 +1,338 @@
+/*
+    scconfig - library for listing files in a directory
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "db.h"
+#include "libs.h"
+#include "log.h"
+#include "dep.h"
+
+static void destroy_testdir(int logdepth, char *dir)
+{
+	const char *rm;
+	char *cmd, *dir_esc;
+
+	rm = get("fstools/rm");
+	if (rm == NULL) {
+		logprintf(logdepth, "CAN NOT delete test directory '%s': no rm available\n", dir);
+		return;
+	}
+
+	if (dir == NULL)
+		return;
+
+	logprintf(logdepth, "deleting test directory '%s'\n", dir);
+
+	cmd = malloc(strlen(dir) + strlen(rm) + 4);
+	dir_esc = shell_escape_dup(dir);
+	sprintf(cmd, "%s %s", rm, dir_esc);
+	run_shell(0, cmd, NULL);
+	free(cmd);
+	free(dir);
+	free(dir_esc);
+}
+
+static char *create_testdir(int logdepth)
+{
+	char *dir, *fn, *cmd;
+	const char *mkdir;
+	int n;
+	logprintf(logdepth, "creating test directory\n");
+
+	dir = tempdir_new(logdepth+1, "");
+	logprintf(logdepth, "sandbox is: '%s'\n", dir);
+
+	fn = malloc(strlen(dir) + 32);
+	for(n = 0; n < 2; n++) {
+		FILE *f;
+		sprintf(fn, "%s%sfile%d", dir, get("sys/path_sep"), n+1);
+		f = fopen(fn, "w");
+		if (f != NULL) {
+			fclose(f);
+			if (!is_file(fn)) {
+				logprintf(logdepth, "Can not create file %s\n", fn);
+				free(fn);
+				destroy_testdir(logdepth, dir);
+				return NULL;
+			}
+		}
+	}
+
+	mkdir = get("fstools/mkdir");
+
+	cmd = malloc(strlen(dir) + 64);
+	for(n = 0; n < 2; n++) {
+		char *fn_esc;
+		sprintf(fn, "%s%sdir%d", dir, get("sys/path_sep"), n+1);
+		fn_esc = shell_escape_dup(fn);
+		sprintf(cmd, "%s %s", mkdir, fn_esc);
+		free(fn_esc);
+		if (run_shell(logdepth+1, cmd, NULL) || (!is_dir(fn))) {
+			logprintf(logdepth, "Can not create directory %s\n", fn);
+			free(fn);
+			free(cmd);
+			destroy_testdir(logdepth, dir);
+			return NULL;
+		}
+	}
+	free(cmd);
+	free(fn);
+	return dir;
+}
+
+static int test(int logdepth, int argc, char *argv[])
+{
+	int dir[2], file[2], n;
+	int *arr, idx;
+
+	for(n = 0; n < 2; n++) {
+		dir[n] = 0;
+		file[n] = 0;
+	}
+
+	/* count the list of files, increase arrays by hit */
+	for(n = 0; n < argc; n++) {
+		arr =  NULL;
+		if (strncmp(argv[n], "dir", 3) == 0)  { arr = dir;  idx = atoi(argv[n]+3); }
+		if (strncmp(argv[n], "file", 4) == 0) { arr = file; idx = atoi(argv[n]+4); }
+		if (arr == NULL) {
+			logprintf(logdepth, "test fails: unknown existing file on the list: '%s'\n", argv[n]);
+			return 0;
+		}
+		idx--;
+		if ((idx < 0) || (idx > 1)) {
+			logprintf(logdepth, "test fails: file name changed: '%s'\n", argv[n]);
+			return 0;
+		}
+		arr[idx]++;
+	}
+
+	/* check if every item was found exactly once */
+	for(n = 0; n < 2; n++) {
+		if ((dir[n] != 1) || (file[n] != 1)) {
+			logprintf(logdepth, "test fails: %s%d not found \n", dir[n] ? "file" : "dir", n);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static void filelist_extract(char *out, const char *dir, const char *method, int *argc, char ***argv)
+{
+	char *s, sep, *start, *end;
+	int len, allocated = 0, count = 0;
+	char **arr = NULL;
+	const char *psep;
+
+	psep = get("sys/path_sep");
+
+	len = strlen(dir);
+
+	/* uniform separator */
+	if (*method == 'w') {
+		/* if word splitting then convert newlines to spaces and convert tabs to spaces */
+		for(s = out; *s != '\0'; s++) {
+			if ((*s == '\n') || (*s == '\r') || (*s == '\t'))
+				*s = ' ';
+		}
+		sep = ' ';
+	}
+	else {
+		for(s = out; *s != '\0'; s++) {
+			if (*s == '\r')
+				*s = '\n';
+		}
+		sep = '\n';
+	}
+
+	start = out;
+	while((s = str_chr(start, sep)) != NULL) {
+		*s = '\0';
+		if (strncmp(dir, start, len) == 0)
+			start += len;
+		while(*start == *psep)
+			start++;
+
+		if (*start != '\0') {
+			end = str_chr(start, *psep);
+			if (end != NULL)
+				*end = '\0';
+
+			/* add only if not the same as previous and exists */
+			if ((!((count > 0) && (strcmp(arr[count - 1], start) == 0))) && (exists_in(dir, start))) {
+
+				if (count >= allocated) {
+					allocated = count + 32;
+					arr = realloc(arr, sizeof(char *) * allocated);
+				}
+				arr[count] = strclone(start);
+				count++;
+			}
+		}
+
+		start = s+1;
+		while(*start == sep) start++;
+	}
+	*argc = count;
+	*argv = arr;
+}
+
+void filelist_free(int *argc, char ***argv)
+{
+	int n;
+
+	if (*argv == NULL)
+		return;
+
+	for(n = 0; n < *argc; n++)
+		free((*argv)[n]);
+	free(*argv);
+	*argc = 0;
+}
+
+static char *filelist_asmcmd(const char *dir, const char *list_cmd)
+{
+	char *cmd;
+
+	cmd = malloc(strlen(dir) + strlen(list_cmd) + 32);
+	sprintf(cmd, list_cmd, dir);
+	return cmd;
+}
+
+static int try(int logdepth, const char *dir, const char *list_cmd, const char *method)
+{
+	char *cmd, *out, *dir_esc;
+	int argc, res;
+	char **argv;
+
+	dir_esc = shell_escape_dup(dir);
+	cmd = filelist_asmcmd(dir_esc, list_cmd);
+	free(dir_esc);
+	logprintf(logdepth, "trying '%s'...\n", cmd);
+
+	run_shell(logdepth+1, cmd, &out);
+	if (out != NULL) {
+		filelist_extract(out, dir, method, &argc, &argv);
+		res = test(logdepth+1, argc, argv);
+		filelist_free(&argc, &argv);
+		free(out);
+	}
+
+	if (res) {
+		logprintf(logdepth+1, "Works.", cmd);
+		put("/internal/filelist/cmd", list_cmd);
+		put("/internal/filelist/method", method);
+		report("OK ('%s' with %s split)\n", list_cmd, method);
+	}
+
+	free(cmd);
+	return res;
+}
+
+int find_filelist(int logdepth, int fatal)
+{
+	char *dir;
+	char *old_cwd;
+	int ret;
+
+	old_cwd = strclone(db_cwd);
+	db_cd("/host");
+
+	require("fstools/mkdir", logdepth, fatal);
+	require("fstools/rm", logdepth, fatal);
+
+
+	report("Checking for filelist... ");
+	logprintf(logdepth, "find_filelist: trying to find file listing...\n");
+	logdepth++;
+
+
+	dir = create_testdir(logdepth);
+	if (dir == NULL) {
+		report("Failed to creat sandbox\n");
+		ret = 1;
+		goto end;
+	}
+
+	if (
+			try(logdepth, dir, "ls %s", "line") || /* should return one file name per line since the output is redirected */
+			try(logdepth, dir, "ls -1 %s", "line") || /* try to force one file name per line */
+			try(logdepth, dir, "ls --format=single-column %s", "line") || /* for gnu ls */
+			try(logdepth, dir, "find %s", "line") || /* if ls fails, we try find */
+	    try(logdepth, dir, "ls %s", "word") ||   /* if that fails too, ls may still have a list in multiple columns */
+	    try(logdepth, dir, "dir %s", "word") ||  /* or we are on windows where we need to use dir maybe */
+	    try(logdepth, dir, "echo %s/*", "word")) { /* or on a system without ls, dir or anything alike, but shell globbing may still work */
+
+		destroy_testdir(logdepth, dir);
+		ret = 0;
+		goto end;
+	}
+
+	destroy_testdir(logdepth, dir);
+	ret = 1;
+	end:;
+	db_cd(old_cwd);
+	free(old_cwd);
+	return ret;
+}
+
+
+void filelist(int logdepth, const char *dir, int *argc, char ***argv)
+{
+	const char *list_cmd, *method;
+	char *cmd, *out, *dir_esc;
+	char *old_cwd;
+
+	old_cwd = strclone(db_cwd);
+	db_cd("/host");
+
+	/* make sure these are set to invalid for easier return in case we fail anywhere later */
+	*argc = -1;
+	*argv = NULL;
+
+	if (!is_dir(dir))
+		goto end;
+
+	require("/internal/filelist/cmd", logdepth, 1);
+	require("/internal/filelist/method", logdepth, 1);
+
+	list_cmd = get("/internal/filelist/cmd");
+	method   = get("/internal/filelist/method");
+
+	dir_esc = shell_escape_dup(dir);
+	cmd = filelist_asmcmd(dir_esc, list_cmd);
+	free(dir_esc);
+	run_shell(logdepth+1, cmd, &out);
+	if (out != NULL) {
+		filelist_extract(out, dir, method, argc, argv);
+		logprintf(logdepth, "filelist: Getting list of files in %s\n", dir);
+		free(out);
+	}
+
+	free(cmd);
+	end:;
+	db_cd(old_cwd);
+	free(old_cwd);
+}
diff --git a/scconfig/src/default/lib_pkg_config.c b/scconfig/src/default/lib_pkg_config.c
new file mode 100644
index 0000000..63fd12f
--- /dev/null
+++ b/scconfig/src/default/lib_pkg_config.c
@@ -0,0 +1,135 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "log.h"
+#include "libs.h"
+#include "db.h"
+#include "dep.h"
+#include "regex.h"
+
+static void zap(char **str)
+{
+	const char *pat = get("/arg/sys/pkg-config-zap");
+	char *n;
+
+	if (pat == NULL)
+		return;
+	if (re_comp(pat) != NULL)
+		return;
+	while (re_exec(*str)) {
+		n = re_subs_dup("");
+		free(*str);
+		*str = n;
+	}
+}
+
+int run_gen_config(int logdepth, const char *confname, const char *pkgname, char **cflags, char **ldflags)
+{
+	char cmd[256];
+
+	assert(strlen(pkgname) < sizeof(cmd) - 64);
+
+	if (cflags != NULL) {
+		sprintf(cmd, "%s --cflags %s", confname, pkgname);
+		if (run(logdepth, cmd, cflags) != 0) {
+			report("not found: %s --cflags failed.", confname);
+			logprintf(logdepth, "not found: %s --cflags failed.\n", confname);
+			return -1;
+		}
+		zap(cflags);
+		strip(*cflags);
+	}
+
+	if (ldflags != NULL) {
+		sprintf(cmd, "%s --libs %s", confname, pkgname);
+		if (run(logdepth, cmd, ldflags) != 0) {
+			report("not found: %s --libs failed.", confname);
+			logprintf(logdepth, "not found: %s --libs failed.\n", confname);
+			if (cflags != NULL)
+				free(*cflags);
+			return -1;
+		}
+		zap(ldflags);
+		strip(*ldflags);
+	}
+
+	return 0;
+}
+
+const char *pkg_config_name()
+{
+	const char *name;
+	name = get("/arg/sys/pkg-config");
+	if (name != NULL)
+		return name;
+	return "pkg-config"; /* fallback */
+}
+
+int run_pkg_config(int logdepth, const char *pkgname, char **cflags, char **ldflags)
+{
+	
+	return run_gen_config(logdepth, pkg_config_name(), pkgname, cflags, ldflags);
+}
+
+void run_pkg_config_lst(int logdepth, const char *pkgpat, int *argc, char ***argv)
+{
+	char *end, *s, *next;
+	int n = 0, a = 0;
+	char **sf = NULL;
+	static const char *pkg_cfg_cache = NULL;
+	static char no_pkg_cfg;
+	char *list;
+
+	if (pkg_cfg_cache == &no_pkg_cfg)
+		goto error;
+
+	if (pkg_cfg_cache == NULL) {
+		char *cmd = str_concat(" ", pkg_config_name(), "--list-all", NULL);
+		run(logdepth, cmd, (char **)&pkg_cfg_cache);
+		free(cmd);
+		if (pkg_cfg_cache == NULL) {
+			pkg_cfg_cache = &no_pkg_cfg;
+			goto error;
+		}
+	}
+
+	if (re_comp(pkgpat) != NULL)
+		goto error;
+
+	s = list = strdup(pkg_cfg_cache);
+	for(;;) {
+		while(isspace(*s)) s++;
+		if (*s == '\0')
+			break;
+		next = strpbrk(s, "\r\n");
+		if (next != NULL)
+			*next = '\0';
+		if (re_exec(s)) {
+			if ((n+2) >= a) { /* n+2: make sure there's always room for the NULL at the end */
+				a += 16;
+				sf = realloc(sf, sizeof(char *) * a);
+			}
+			end = strpbrk(s, " \t");
+			if (end != NULL)
+				*end = '\0';
+
+			sf[n] = strclone(s);
+			sf[n+1] = re_subs_dup("");
+/*			report("\ns='%s' sf='%s'\n", s, sf[n]);*/
+			n+=2;
+		}
+		s = next+1;
+	}
+
+	if (sf != NULL)
+		sf[n] = NULL;
+
+	free(list);
+error:;
+	*argc = n;
+	*argv = sf;
+	return;
+}
diff --git a/scconfig/src/default/lib_srctree.c b/scconfig/src/default/lib_srctree.c
new file mode 100644
index 0000000..17809f4
--- /dev/null
+++ b/scconfig/src/default/lib_srctree.c
@@ -0,0 +1,77 @@
+/*
+    scconfig - library to explore the source tree
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "db.h"
+#include "libs.h"
+#include "log.h"
+#include "dep.h"
+
+
+char *svn_info(int logdepth, const char *dir, const char *key)
+{
+	char *cmd, *stdo = NULL;
+	char *res = NULL;
+	int keylen = strlen(key);
+
+	cmd = str_concat(" ", "svn info", dir, NULL);
+	if (run_shell(logdepth, cmd, &stdo) == 0) {
+		char *line, *nline;
+
+		/* check key against each line */
+		for(line = stdo; line != NULL; line = nline) {
+			/* split line */
+			nline = strpbrk(line, "\r\n");
+			if (nline != NULL) {
+				*nline = '\0';
+				nline++;
+				while((*nline == '\n') || (*nline == '\r')) nline++;
+			}
+
+			/* compare key */
+			if (strncmp(line, key, keylen) == 0) {
+				char *val;
+
+				/* extract value */
+				val = strchr(line, ':');
+				if (val != NULL) {
+					val++;
+					while((*val == ' ') || (*val == '\t')) val++;
+				}
+				else
+					val = line;
+
+				/* produce output */
+				res = strclone(val);
+				goto found;
+			}
+		}
+	}
+
+	found:;
+	if (stdo != NULL)
+		free(stdo);
+	free(cmd);
+	return res;
+}
diff --git a/scconfig/src/default/lib_try.c b/scconfig/src/default/lib_try.c
new file mode 100644
index 0000000..5fda2cd
--- /dev/null
+++ b/scconfig/src/default/lib_try.c
@@ -0,0 +1,179 @@
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+
+static int try_icl_(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, int run)
+{
+	char *out = NULL;
+	char *tmp, *inc;
+	const char *test_c;
+	char c[1024];
+	int l, compres;
+
+	if (includes != NULL) {
+		l = strlen(includes);
+		memcpy(c, includes, l);
+		c[l] = '\n';
+		l++;
+		strcpy(c+l, test_c_in);
+		test_c = c;
+	}
+	else
+		test_c = test_c_in;
+
+	logprintf(logdepth, "trying '%s' and '%s' and '%s', %s\n", includes == NULL ? "" : includes, cflags, ldflags, run ? "with a run" : "with no run");
+
+	if (run)
+		compres = compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out);
+	else {
+		char *fn_output = NULL;
+		compres = compile_code(logdepth+1, test_c, &fn_output, NULL, cflags, ldflags);
+		if (fn_output != NULL) {
+			unlink(fn_output);
+			free(fn_output);
+		}
+	}
+
+
+	if (compres == 0) {
+		if (!run || target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+			free(out);
+
+			/* no prefix: don't modify the database, the caller will do that */
+			if (prefix == NULL)
+				return 1;
+
+			if ((cflags != NULL) && (*cflags == '+'))
+				cflags++;
+			if ((ldflags != NULL) && (*ldflags == '+'))
+				ldflags++;
+
+			tmp = malloc(strlen(prefix) + 32);
+
+			if (includes != NULL) {
+				if (*includes == '\0')
+					inc = strclone("");
+				else
+					inc = uniq_inc_str(includes, NULL, "\\n", 0);
+				sprintf(tmp, "%s/includes", prefix);
+				put(tmp, inc);
+			}
+			else
+				inc = NULL;
+
+			if (cflags != NULL) {
+				sprintf(tmp, "%s/cflags", prefix);
+				if (*cflags == '+')
+					put(tmp, cflags+1);
+				else
+					put(tmp, cflags);
+			}
+
+			if (ldflags != NULL) {
+				sprintf(tmp, "%s/ldflags", prefix);
+				if (*ldflags == '+')
+					put(tmp, ldflags+1);
+				else
+					put(tmp, ldflags);
+			}
+
+			if (inc != NULL) {
+				report("OK ('%s', '%s' and '%s')\n", inc, cflags, ldflags);
+				free(inc);
+			}
+			else
+				report("OK ('%s' and '%s')\n", cflags, ldflags);
+
+			sprintf(tmp, "%s/presents", prefix);
+			put(tmp, strue);
+			free(tmp);
+			return 1;
+		}
+		free(out);
+	}
+	return 0;
+}
+
+int try_icl(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags)
+{
+	return try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, 1);
+}
+
+int try_icl_norun(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags)
+{
+	return try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, 0);
+}
+
+
+int try_fail(int logdepth, const char *prefix)
+{
+	char *tmp;
+	tmp = malloc(strlen(prefix) + 32);
+	sprintf(tmp, "%s/presents", prefix);
+	put(tmp, sfalse);
+	free(tmp);
+	report("not found\n");
+	logprintf(logdepth, "NOT FOUND.");
+	return 1;
+}
+
+static int try_pkg_config_(int logdepth, char *pkgname, const char *prefix, const char *test_c)
+{
+	char *cflags, *ldflags;
+	int res;
+
+	logprintf(logdepth, "Trying pkg-config %s\n", pkgname);
+	if (run_pkg_config(logdepth+1, pkgname, &cflags, &ldflags) == 0)
+		res = try_icl(logdepth+1, prefix, test_c, NULL, cflags, ldflags);
+	else
+		res = 0;
+	free(cflags);
+	free(ldflags);
+
+	return res;
+}
+
+int try_icl_pkg_config(int logdepth, const char *prefix, const char *test_c, char *includes, const char *pkgpat, const char *reqver)
+{
+	char **pkg_ver, **s;
+	int num_pkg_ver;
+	int res;
+	(void) includes;  /* not used */
+
+	run_pkg_config_lst(logdepth, pkgpat, &num_pkg_ver, &pkg_ver);
+	if (pkg_ver == NULL)
+		return 0;
+
+	if (reqver != NULL) {
+		/* search the list for the preferred version */
+		for(s = pkg_ver; *s != NULL; s+=2) {
+			if (strcmp(s[1], reqver) == 0) {
+				if (try_pkg_config_(logdepth, s[0], prefix, test_c)) {
+					res = 1;
+					report("Found version required (%s) using pkg_config.\n", reqver);
+					goto out;
+				}
+				else {
+					report("The version required (%s) is found (via pkg_config) but does not work\n", reqver);
+					goto out;
+				}
+			}
+		}
+		goto out;
+	}
+
+	for(s = pkg_ver; *s != NULL; s+=2) {
+		if (try_pkg_config_(logdepth, s[0], prefix, test_c)) {
+			res = 1;
+			goto out;
+		}
+	}
+
+out:;
+	filelist_free(&num_pkg_ver, &pkg_ver);
+	return res;
+}
+
diff --git a/scconfig/src/default/lib_uniqinc.c b/scconfig/src/default/lib_uniqinc.c
new file mode 100644
index 0000000..888ede3
--- /dev/null
+++ b/scconfig/src/default/lib_uniqinc.c
@@ -0,0 +1,158 @@
+/*
+    scconfig - library for making includes on a list unique
+    Copyright (C) 2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "libs.h"
+#include "db.h"
+
+#define grow \
+	if (used >= alloced) { \
+		alloced += 16; \
+		list = realloc(list, alloced * sizeof(char *)); \
+	}
+
+char **uniq_inc_arr(const char *includes, int indirect, const char *sep_, int *numlines)
+{
+	char *node, *next, *cw, *nw, *snode, *orig_node;
+	char *sep;
+	char **list;
+	int alloced, used, n;
+
+	orig_node = strclone(includes);
+	node = orig_node;
+	if (sep_ == NULL)
+		sep = strclone("\n\r");
+	else
+		sep = strclone(sep_);
+
+	/* reset list */
+	alloced = used = 0;
+	list = NULL;
+	/* take arguments one by one */
+	while(node != NULL) {
+		if (indirect) {
+			while((*node == ' ') || (*node == '\t')) node++;
+			next = strpbrk(node, " \t");
+		}
+		else {
+			for(;;) {
+				next = strpbrk(node, sep);
+				if ((next > node) || (next == NULL))
+					break;
+				node = next+1;
+			}
+		}
+		if (next != NULL) {
+			*next = '\0';
+			next++;
+		}
+		if (indirect)
+			snode = str_subsn(get(node));
+		else
+			snode = node;
+		cw = snode;
+		/* split node value (s) by sep */
+/*		fprintf(stderr, "nodename=%s snode=%s next=%s\n", node, snode, next);*/
+		while(cw != NULL) {
+			nw = strpbrk(cw, sep);
+			if (nw != NULL) {
+				*nw = '\0';
+				nw++;
+			}
+
+			if (*cw != '\0') {
+				/* try to find cw in the existing list - this is a slow linear search for now */
+				for(n = 0; n < used; n++) {
+					if (strcmp(list[n], cw) == 0)
+						goto already_on_list;
+				}
+				/* not found, append */
+				grow;
+				list[used] = strclone(cw);
+				used++;
+			}
+			already_on_list:;
+			cw = nw;
+		}
+		if (indirect)
+			free(snode);
+		node = next;
+	}
+	grow
+	list[used] = NULL;
+	if (numlines != NULL)
+		*numlines = used;
+
+	free(orig_node);
+	free(sep);
+
+	return list;
+}
+
+void uniq_inc_free(char **arr)
+{
+	char **s;
+	for(s = arr; *s != NULL; s++)
+		free(*s);
+	free(arr);
+}
+
+static int uniq_inc_str_cmp(const void *a_, const void *b_)
+{
+	char **a = (char **)a_, **b = (char **)b_;
+	return strcmp(*a, *b);
+}
+
+char *uniq_inc_str(const char *includes, const char *isep, const char *osep, int sort)
+{
+	char **arr, **s, *ret, *end;
+	int len, numelem, oseplen;
+
+	oseplen = strlen(osep);
+	arr = uniq_inc_arr(includes, 0, isep, NULL);
+
+	len = 4; /* safety margin, for terminator \0, etc. */
+	numelem = 0;
+	for(s = arr; *s != NULL; s++) {
+		len += strlen(*s) + oseplen + 1;
+		numelem++;
+	}
+
+	if (sort)
+		qsort(arr, numelem, sizeof(char *), uniq_inc_str_cmp);
+
+	ret = malloc(len);
+
+	for(end = ret, s = arr; *s != NULL; s++) {
+		len = strlen(*s);
+		memcpy(end, *s, len);
+		end += len;
+		memcpy(end, osep, oseplen);
+		end += oseplen;
+		free(*s);
+	}
+	*end = '\0';
+
+	free(arr);
+	return ret;
+}
diff --git a/scconfig/src/default/libs.h b/scconfig/src/default/libs.h
new file mode 100644
index 0000000..b732a0c
--- /dev/null
+++ b/scconfig/src/default/libs.h
@@ -0,0 +1,111 @@
+#define NL "\n"
+
+/* main.c */
+extern int no_autodetect_sys; /* set this to 1 to suppress system and cross detection */
+extern int no_save_cache;     /* set this to 1 to avoid saving config.cache */
+
+/* lib_try.c: try to compile and run a test code; save results under prefix, if worked */
+/* include, compile-flags, link-flags;
+   NULL includes, cflags, *ldflags means don't put anything in the db; cflags
+   and ldflags may be prefixed with "+" to include standard flags;
+   the test code has to print "OK" if it worked. If prefix is NULL, do not
+   modify the db or announce the output, silently return 0 or 1.
+   Returns 1 if worked, 0 if not */
+int try_icl(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags);
+
+/* same as try_icl(), but does not execute the code, only compiles. Useful
+   for test programs with undesirable side effects (e.g. gtk: would open a window) */
+int try_icl_norun(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags);
+
+/* use try_icl() on a list of packages found by pkg-config. Stick to the version
+   required if reqver is non-NULL, else try them in the order pkg-config returned
+   them. */
+int try_icl_pkg_config(int logdepth, const char *prefix, const char *test_c, char *includes, const char *pkgpat, const char *reqver);
+
+/* call this when failed to find the feature (after multiple try_*() calls);
+   always returns 1 (so that return try_fail() does the Right Thing) */
+int try_fail(int logdepth, const char *prefix);
+
+/* lib_compile.c */
+extern int cross_blind; /* 1 if crosscompiling is blind (no emulator to test with) */
+
+char *shell_escape_dup(const char *in); /* strdup in and escape any special char for the shell */
+
+int compile_file(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags);
+int compile_code(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags);
+int run(int logdepth, const char *cmd_, char **stdout_saved);
+int run_shell(int logdepth, const char *cmd_, char **stdout_saved);
+int compile_run(int logdepth, const char *testcode, const char *cc, const char *cflags, const char *ldflags, char **stdout_saved);
+int run_script(int logdepth, const char *interpreter, const char *script, const char *suffix, char **out);
+
+/* lib_file.c */
+int file_size(const char *name);
+char *tempdir_new(int logdepth, const char *suffix);
+char *tempfile_new(const char *suffix);
+char *tempfile_dump(const char *testcode, const char *suffix);
+char *load_file(const char *name);
+int is_dir(const char *path);
+int is_file(const char *path);
+int exists(const char *path);
+int exists_in(const char *dir, const char *file);
+char *file_name(const char *path); /* returns malloc'd buffer */
+char *dir_name(const char *path);  /* returns malloc'd buffer */
+char *tempfile_new_noabort(const char *suffix); /* for internal use - returns NULL instead of aborting when temp file can not be created */
+int touch_file(const char *path);
+
+/* lib_filelist.c */
+void filelist(int logdepth, const char *dir, int *argc, char ***argv);
+void filelist_free(int *argc, char ***argv);
+
+/* lib_pkg_config.c */
+
+/* run pkg config on pkgname:
+    - with --cflags if cflags is not NULL, storing the result in cflags (malloc()'d)
+    - with --libs if ldflags is not NULL, storing the result in ldflags (malloc()'d)
+   Returns 0 on success.
+*/
+int run_pkg_config(int logdepth, const char *pkgname, char **cflags, char **ldflags);
+
+/* same as run_pkg_config, but runs a generic config tool (e.g. gdconfig)
+   passed in confname */
+int run_gen_config(int logdepth, const char *confname, const char *pkgname, char **cflags, char **ldflags);
+
+/* run pkg-config --list-all and keep lines matching regex pkgpat.
+
+   argc/argv is a filelist output, each item pair is package name returned by
+   pkg_config (odd items are full package names, even items are suffixes:
+   pkgpath match removed)
+*/
+void run_pkg_config_lst(int logdepth, const char *pkgpat, int *argc, char ***argv);
+
+
+/* lib_uniqinc.c */
+char **uniq_inc_arr(const char *includes, int indirect, const char *sep, int *numlines); /* split includes by sep; includes is a list of nodes to get() if indirect is non-zero; return a NULL-terminated array of unique include strings and set *numlines if numlines is not NULL */
+void uniq_inc_free(char **arr); /* free an array returned by uniq_inc_arr() */
+char *uniq_inc_str(const char *includes, const char *isep, const char *osep, int sort); /* take a long list of includes separated by isep and emit an uniq list separated by osep */
+
+/* str.c */
+char *strclone(const char *str);
+char *trim_left(char *str);
+char *trim_right(char *str);
+char *strip(char *str);
+char *str_chr(char *str, char c);
+char *str_rchr(char *str, char c);
+char *str_subsn(const char *str); /* advanced strdup that also interprets \n */
+char *str_concat(const char *sep, ...); /* concat a list of strings into a newly allocated buffer, putting sep between them */
+char *esc_interpret(const char *str);
+int chr_inset(char c, const char *set); /* returns whether c is in set */
+
+/* srctree.c */
+
+/* Run svn info on dir and extract the value for key;
+   key is case sensitive. The first match is returned or NULL if not found
+   or on error. */
+char *svn_info(int logdepth, const char *dir, const char *key);
+
+#define isblind(root)  ((strncmp((root), "/target", 7) == 0) && cross_blind)
+#define istarget(root) (strncmp((root), "/target", 7) == 0)
+
+#define target_emu_fail(out) ((isblind(db_cwd)) && (out == NULL))
+
+#define safeNULL(s)  ((s) == NULL ? "(NULL)" : (s))
diff --git a/scconfig/src/default/log.c b/scconfig/src/default/log.c
new file mode 100644
index 0000000..cafc36f
--- /dev/null
+++ b/scconfig/src/default/log.c
@@ -0,0 +1,132 @@
+/*
+    scconfig - logging
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "log.h"
+#include <stdio.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+char *spaces  = "                                                                ";
+FILE *logfile = NULL;
+char *fn_log = "config.log";
+
+void log_init(void)
+{
+	if (fn_log != NULL) {
+		/* double open for truncate - for extreme portability, please do not "fix" */
+		logfile = fopen(fn_log, "w");
+		assert(logfile != NULL);
+		fclose(logfile);
+		logfile = fopen(fn_log, "a");
+		assert(logfile != NULL);
+	}
+}
+
+void log_uninit(void)
+{
+	if (logfile != NULL)
+		fclose(logfile);
+}
+
+void logprintf(int depth, const char *format, ...)
+{
+	va_list ap;
+	va_start(ap, format);
+
+	if (logfile != NULL) {
+		fprintf(logfile, "%s", logprefix(depth));
+		vfprintf(logfile, format, ap);
+		fflush(logfile);
+	}
+
+	va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+	va_list ap;
+	va_start(ap, format);
+	vfprintf(stderr, format, ap);
+	va_end(ap);
+
+	va_start(ap, format);
+	if (logfile != NULL) {
+		fprintf(logfile, "###error### ");
+		vfprintf(logfile, format, ap);
+		fflush(logfile);
+	}
+	va_end(ap);
+
+}
+
+void report(const char *format, ...)
+{
+	va_list ap;
+	va_start(ap, format);
+	vprintf(format, ap);
+	fflush(stdout);
+	va_end(ap);
+
+	va_start(ap, format);
+	if (logfile != NULL) {
+		fprintf(logfile, "###report### ");
+		vfprintf(logfile, format, ap);
+		fflush(logfile);
+	}
+	va_end(ap);
+}
+
+void log_merge(int logdepth, const char *fn)
+{
+	FILE *f;
+	char line[2048];
+	int lines;
+
+	if (logfile == NULL)
+		return;
+
+	f = fopen(fn, "r");
+	if (f == NULL) {
+		logprintf(logdepth, "scconfig error: couldn't open %s for merging.\n", fn);
+		return;
+	}
+	lines = 0;
+	while(!(feof(f))) {
+		*line = '\0';
+		fgets(line, sizeof(line), f);
+		if (*line != '\0') {
+			if (lines == 0)
+				logprintf(logdepth, "========= output dump start ============\n");
+			lines++;
+			logprintf(logdepth, "%s", line);
+			/* Make sure we have newline at the end of each line */
+			if (line[strlen(line)-1] != '\n')
+				logprintf(0, "\n");
+		}
+	}
+	if (lines == 0)
+		logprintf(logdepth, "========= empty stderr =================\n");
+	else
+		logprintf(logdepth, "========= output dump end ==============\n");
+	fclose(f);
+}
diff --git a/scconfig/src/default/log.h b/scconfig/src/default/log.h
new file mode 100644
index 0000000..4d5e97f
--- /dev/null
+++ b/scconfig/src/default/log.h
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#define max_spaces 64
+extern char *spaces;
+
+#define logprefix(n) (((n) > max_spaces) ? spaces : (spaces+max_spaces-(n)))
+
+void logprintf(int depth, const char *format, ...);
+void error(const char *format, ...);
+void report(const char *format, ...);
+
+void log_merge(int logdepth, const char *fn);
+
+extern FILE *logfile;
+extern void log_init(void);
+void log_uninit(void);
+
+extern char *fn_log;
diff --git a/scconfig/src/default/main.c b/scconfig/src/default/main.c
new file mode 100644
index 0000000..465b30d
--- /dev/null
+++ b/scconfig/src/default/main.c
@@ -0,0 +1,92 @@
+/*
+    scconfig - test code for default and scripts
+    Copyright (C) 2009..2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "db.h"
+#include "find.h"
+#include "log.h"
+#include "arg.h"
+#include "dep.h"
+#include "deps_default.h"
+#include "libs.h"
+#include "hooks.h"
+#include "regex.h"
+#include "main_custom_args.h"
+#include "main_lib.h"
+
+void re_fail(char *s, char c)
+{
+	fprintf(stderr, "Regex error: %s [opcode %o]\n", s, c);
+	abort();
+}
+
+int no_autodetect_sys = 0;
+int no_save_cache = 0;
+int main(int argc, char *argv[])
+{
+	if (main_init() != 0)
+		return 1;
+
+	if (main_process_args(argc, argv) != 0)
+		return 1;
+
+	if (!no_autodetect_sys) {
+		find_target(0, 1);
+		printf("--- Detecting host\n");
+
+		require("sys/name", 0, 1);
+	}
+
+	if (hook_detect_host())  {
+		fprintf(stderr, "hook_detect_host failed, exiting\n");
+		return 1;
+	}
+
+	if (!no_autodetect_sys) {
+		if (!iscross)
+			printf("--- Detecting target (same as host)\n");
+		else
+			printf("--- Detecting target (differs from host)\n");
+	}
+	db_cd("/target");
+	run_custom_reqs();
+
+
+	if (hook_detect_target()) {
+		fprintf(stderr, "hook_detect_target failed, exiting\n");
+		return 1;
+	}
+
+	if (hook_generate()) {
+		fprintf(stderr, "hook_generate failed, exiting\n");
+		return 1;
+	}
+
+	if (!no_save_cache)
+		export("config.cache", 1, "/");
+
+	main_uninit();
+	return 0;
+}
+
diff --git a/scconfig/src/default/main_custom_args.c b/scconfig/src/default/main_custom_args.c
new file mode 100644
index 0000000..54eccb9
--- /dev/null
+++ b/scconfig/src/default/main_custom_args.c
@@ -0,0 +1,49 @@
+/*
+    scconfig - default way to handle custom args (save them in an array)
+    Copyright (C) 2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "hooks.h"
+#include "main_custom_args.h"
+
+char *custom_reqs[MAX_CUSTOM_REQS];
+int num_custom_reqs = 0;
+
+int custom_arg(const char *key, const char *value)
+{
+	if (hook_custom_arg(key, value))
+		return 1;
+	if (strcmp(key, "detect") == 0) {
+		printf("Will detect: %s\n", value);
+		if (num_custom_reqs >= MAX_CUSTOM_REQS) {
+			report("Too many custom reqs from the command line, exiting\n");
+			exit(1);
+		}
+		custom_reqs[num_custom_reqs] = strclone(value);
+		num_custom_reqs++;
+		return 1;
+	}
+	return 0;
+}
diff --git a/scconfig/src/default/main_custom_args.h b/scconfig/src/default/main_custom_args.h
new file mode 100644
index 0000000..9f9c86b
--- /dev/null
+++ b/scconfig/src/default/main_custom_args.h
@@ -0,0 +1,6 @@
+#define MAX_CUSTOM_REQS 32
+extern char *custom_reqs[MAX_CUSTOM_REQS];
+extern int num_custom_reqs;
+
+int custom_arg(const char *key, const char *value);
+
diff --git a/scconfig/src/default/main_lib.c b/scconfig/src/default/main_lib.c
new file mode 100644
index 0000000..164089d
--- /dev/null
+++ b/scconfig/src/default/main_lib.c
@@ -0,0 +1,188 @@
+/*
+    scconfig - helpers for a main()
+    Copyright (C) 2009..2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "db.h"
+#include "find.h"
+#include "log.h"
+#include "arg.h"
+#include "dep.h"
+#include "deps_default.h"
+#include "libs.h"
+#include "hooks.h"
+#include "regex.h"
+#include "main_custom_args.h"
+
+#ifdef PLUGIN_SCRIPTS
+#include "../scripts/INIT.h"
+#endif
+
+#ifdef PLUGIN_PARSER
+#include "../parser/INIT.h"
+#endif
+
+#ifdef PLUGIN_C99
+#include "../c99/INIT.h"
+#endif
+
+#ifdef PLUGIN_PARSGEN
+#include "../parsgen/INIT.h"
+#endif
+
+#ifdef PLUGIN_MATH
+#include "../math/INIT.h"
+#endif
+
+#ifdef PLUGIN_SOCKET
+#include "../socket/INIT.h"
+#endif
+
+#ifdef PLUGIN_USERPASS
+#include "../userpass/INIT.h"
+#endif
+
+#ifdef PLUGIN_GUI
+#include "../gui/INIT.h"
+#endif
+
+#ifdef PLUGIN_SUL
+#include "../sul/INIT.h"
+#endif
+
+#ifdef PLUGIN_GENERATOR
+#include "generator.h"
+#endif
+
+
+void init(void)
+{
+	db_init();
+	log_init();
+	dep_init();
+	deps_default_init();
+
+	/* common internal directory */
+	db_mkdir("/internal");
+
+	/* We have a host system for sure - also make it the default */
+	db_mkdir("/host");
+	db_cd("/host");
+
+	/* emulator for the host system is empty string */
+	put("sys/emu", "");
+
+#ifdef PLUGIN_SCRIPTS
+#include "../scripts/INIT.c"
+#endif
+
+#ifdef PLUGIN_PARSER
+#include "../parser/INIT.c"
+#endif
+
+#ifdef PLUGIN_C99
+#include "../c99/INIT.c"
+#endif
+
+#ifdef PLUGIN_PARSGEN
+#include "../parsgen/INIT.c"
+#endif
+
+#ifdef PLUGIN_MATH
+#include "../math/INIT.c"
+#endif
+
+#ifdef PLUGIN_SOCKET
+#include "../socket/INIT.c"
+#endif
+
+#ifdef PLUGIN_USERPASS
+#include "../userpass/INIT.c"
+#endif
+
+#ifdef PLUGIN_GUI
+#include "../gui/INIT.c"
+#endif
+
+#ifdef PLUGIN_SUL
+#include "../sul/INIT.c"
+#endif
+
+#ifdef PLUGIN_GENERATOR
+#include "../generator/INIT.c"
+#endif
+}
+
+void uninit(void)
+{
+	log_uninit();
+	dep_uninit();
+	db_uninit();
+}
+
+void run_custom_reqs(void)
+{
+	int n;
+	if (num_custom_reqs > 0) {
+		printf("Running custom requirements\n");
+		for(n = 0; n < num_custom_reqs; n++) {
+			if (custom_reqs[n] == NULL) {
+				fprintf(stderr, "Error: requested detection of empty string - please check your command line, syntax is --detect=node\n");
+				exit(1);
+			}
+			require(custom_reqs[n], 1, 1);
+		}
+	}
+}
+
+int main_init(void)
+{
+	re_modw("./\\");
+	if (hook_preinit()) {
+		fprintf(stderr, "hook_preinit failed, exiting\n");
+		return 1;
+	}
+	init();
+	if (hook_postinit())  {
+		fprintf(stderr, "hook_postinit failed, exiting\n");
+		return 1;
+	}
+	return 0;
+}
+
+int main_process_args(int argc, char *argv[])
+{
+	process_args(argc, argv);
+	if (hook_postarg()) {
+		fprintf(stderr, "hook_postarg failed, exiting\n");
+		return 1;
+	}
+	return 0;
+}
+
+void main_uninit(void)
+{
+	hook_preuninit();
+	uninit();
+	hook_postuninit();
+}
diff --git a/scconfig/src/default/main_lib.h b/scconfig/src/default/main_lib.h
new file mode 100644
index 0000000..1b5e5b3
--- /dev/null
+++ b/scconfig/src/default/main_lib.h
@@ -0,0 +1,8 @@
+int main_init(void);
+int main_process_args(int argc, char *argv[]);
+void main_uninit(void);
+
+/* internal */
+void init(void);
+void uninit(void);
+void run_custom_reqs(void);
diff --git a/scconfig/src/default/regex.c b/scconfig/src/default/regex.c
new file mode 100644
index 0000000..eb9b06f
--- /dev/null
+++ b/scconfig/src/default/regex.c
@@ -0,0 +1,612 @@
+/*
+
+ * regex - Regular expression pattern matching  and replacement
+ *
+ * By:  Ozan S. Yigit (oz)
+ *      Dept. of Computer Science
+ *      York University
+ *
+ * These routines are the PUBLIC DOMAIN equivalents of regex
+ * routines as found in 4.nBSD UN*X, with minor extensions.
+ *
+ * These routines are derived from various implementations found
+ * in software tools books, and Conroy's grep. They are NOT derived
+ * from licensed/restricted software.
+ * For more interesting/academic/complicated implementations,
+ * see Henry Spencer's regexp routines, or GNU Emacs pattern
+ * matching module.
+ *
+ * const correctness patch by Tibor 'Igor2' Palinkas in 2009..2010
+ * new subs code by Tibor 'Igor2' Palinkas in 2015
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "regex.h"
+
+#define MAXNFA  1024
+#define MAXTAG  10
+
+#define OKP     1
+#define NOP     0
+
+#define CHR     1
+#define ANY     2
+#define CCL     3
+#define BOL     4
+#define EOL     5
+#define BOT     6
+#define EOT     7
+#define BOW	8
+#define EOW	9
+#define REF     10
+#define CLO     11
+
+#define END     0
+
+/*
+ * The following defines are not meant to be changeable.
+ * They are for readability only.
+ */
+#define MAXCHR	128
+#define CHRBIT	8
+#define BITBLK	MAXCHR/CHRBIT
+#define BLKIND	0170
+#define BITIND	07
+
+#define ASCIIB	0177
+
+#ifdef NO_UCHAR
+typedef char CHAR;
+#else
+typedef unsigned char CHAR;
+#endif
+
+static int  tagstk[MAXTAG];             /* subpat tag stack..*/
+static CHAR nfa[MAXNFA];                /* automaton..       */
+static int  sta = NOP;                  /* status of lastpat */
+
+static CHAR bittab[BITBLK];             /* bit table for CCL */
+                                        /* pre-set bits...   */
+static CHAR bitarr[] = {1,2,4,8,16,32,64,128};
+
+static void
+chset(CHAR c)
+{
+	bittab[(CHAR) ((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
+}
+
+#define badpat(x)	(*nfa = END, x)
+#define store(x)	*mp++ = x
+
+char *
+re_comp(const char *pat)
+{
+	register const char *p;         /* pattern pointer   */
+	register CHAR *mp=nfa;          /* nfa pointer       */
+	register CHAR *lp;              /* saved pointer..   */
+	register CHAR *sp=nfa;          /* another one..     */
+
+	register int tagi = 0;          /* tag stack index   */
+	register int tagc = 1;          /* actual tag count  */
+
+	register int n;
+	register CHAR mask;		/* xor mask -CCL/NCL */
+	int c1, c2;
+
+	if (!pat || !*pat) {
+		if (sta)
+			return 0;
+		else
+			return badpat("No previous regular expression");
+	}
+	sta = NOP;
+
+	for (p = pat; *p; p++) {
+		lp = mp;
+		switch(*p) {
+
+		case '.':               /* match any char..  */
+			store(ANY);
+			break;
+
+		case '^':               /* match beginning.. */
+			if (p == pat)
+				store(BOL);
+			else {
+				store(CHR);
+				store(*p);
+			}
+			break;
+
+		case '$':               /* match endofline.. */
+			if (!*(p+1))
+				store(EOL);
+			else {
+				store(CHR);
+				store(*p);
+			}
+			break;
+
+		case '[':               /* match char class..*/
+			store(CCL);
+
+			if (*++p == '^') {
+				mask = 0377;
+				p++;
+			}
+			else
+				mask = 0;
+
+			if (*p == '-')		/* real dash */
+				chset(*p++);
+			if (*p == ']')		/* real brac */
+				chset(*p++);
+			while (*p && *p != ']') {
+				if (*p == '-' && *(p+1) && *(p+1) != ']') {
+					p++;
+					c1 = *(p-2) + 1;
+					c2 = *p++;
+					while (c1 <= c2)
+						chset((CHAR)c1++);
+				}
+#ifdef EXTEND
+				else if (*p == '\\' && *(p+1)) {
+					p++;
+					chset(*p++);
+				}
+#endif
+				else
+					chset(*p++);
+			}
+			if (!*p)
+				return badpat("Missing ]");
+
+			for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
+				store(mask ^ bittab[n]);
+
+			break;
+
+		case '*':               /* match 0 or more.. */
+		case '+':               /* match 1 or more.. */
+			if (p == pat)
+				return badpat("Empty closure");
+			lp = sp;		/* previous opcode */
+			if (*lp == CLO)		/* equivalence..   */
+				break;
+			switch(*lp) {
+
+			case BOL:
+			case BOT:
+			case EOT:
+			case BOW:
+			case EOW:
+			case REF:
+				return badpat("Illegal closure");
+			default:
+				break;
+			}
+
+			if (*p == '+')
+				for (sp = mp; lp < sp; lp++)
+					store(*lp);
+
+			store(END);
+			store(END);
+			sp = mp;
+			while (--mp > lp)
+				*mp = mp[-1];
+			store(CLO);
+			mp = sp;
+			break;
+
+		case '\\':              /* tags, backrefs .. */
+			switch(*++p) {
+
+			case '(':
+				if (tagc < MAXTAG) {
+					tagstk[++tagi] = tagc;
+					store(BOT);
+					store(tagc++);
+				}
+				else
+					return badpat("Too many \\(\\) pairs");
+				break;
+			case ')':
+				if (*sp == BOT)
+					return badpat("Null pattern inside \\(\\)");
+				if (tagi > 0) {
+					store(EOT);
+					store(tagstk[tagi--]);
+				}
+				else
+					return badpat("Unmatched \\)");
+				break;
+			case '<':
+				store(BOW);
+				break;
+			case '>':
+				if (*sp == BOW)
+					return badpat("Null pattern inside \\<\\>");
+				store(EOW);
+				break;
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				n = *p-'0';
+				if (tagi > 0 && tagstk[tagi] == n)
+					return badpat("Cyclical reference");
+				if (tagc > n) {
+					store(REF);
+					store(n);
+				}
+				else
+					return badpat("Undetermined reference");
+				break;
+#ifdef EXTEND
+			case 'b':
+				store(CHR);
+				store('\b');
+				break;
+			case 'n':
+				store(CHR);
+				store('\n');
+				break;
+			case 'f':
+				store(CHR);
+				store('\f');
+				break;
+			case 'r':
+				store(CHR);
+				store('\r');
+				break;
+			case 't':
+				store(CHR);
+				store('\t');
+				break;
+#endif
+			default:
+				store(CHR);
+				store(*p);
+			}
+			break;
+
+		default :               /* an ordinary char  */
+			store(CHR);
+			store(*p);
+			break;
+		}
+		sp = lp;
+	}
+	if (tagi > 0)
+		return badpat("Unmatched \\(");
+	store(END);
+	sta = OKP;
+	return 0;
+}
+
+
+static const char *bol;
+const char *bopat[MAXTAG];
+const char *eopat[MAXTAG];
+static const char *pmatch(const char *, CHAR *, int *);
+
+/*
+ * re_exec:
+ *	execute nfa to find a match.
+ *
+ *	special cases: (nfa[0])
+ *		BOL
+ *			Match only once, starting from the
+ *			beginning.
+ *		CHR
+ *			First locate the character without
+ *			calling pmatch, and if found, call
+ *			pmatch for the remaining string.
+ *		END
+ *			re_comp failed, poor luser did not
+ *			check for it. Fail fast.
+ *
+ *	If a match is found, bopat[0] and eopat[0] are set
+ *	to the beginning and the end of the matched fragment,
+ *	respectively.
+ *
+ */
+
+int
+re_exec(const char *lp)
+{
+	register CHAR c;
+	register const char *ep = 0;
+	register CHAR *ap = nfa;
+	int score = 1;
+
+	bol = lp;
+
+	bopat[0] = 0;
+	bopat[1] = 0;
+	bopat[2] = 0;
+	bopat[3] = 0;
+	bopat[4] = 0;
+	bopat[5] = 0;
+	bopat[6] = 0;
+	bopat[7] = 0;
+	bopat[8] = 0;
+	bopat[9] = 0;
+
+	switch(*ap) {
+
+	case BOL:			/* anchored: match from BOL only */
+		ep = pmatch(lp,ap, &score);
+		break;
+	case CHR:			/* ordinary char: locate it fast */
+		c = *(ap+1);
+		while (*lp && *lp != c)
+			lp++;
+		if (!*lp)		/* if EOS, fail, else fall thru. */
+			return 0;
+	default:			/* regular matching all the way. */
+#ifdef OLD
+		while (*lp) {
+			if ((ep = pmatch(lp,ap, &score)))
+				break;
+			lp++;
+		}
+#else	/* match null string */
+		do {
+			if ((ep = pmatch(lp,ap, &score)))
+				break;
+			lp++;
+		} while (*lp);
+#endif
+		break;
+	case END:			/* munged automaton. fail always */
+		return 0;
+	}
+	if (!ep)
+		return 0;
+
+	bopat[0] = lp;
+	eopat[0] = ep;
+	return score;
+}
+
+/*
+ * pmatch: internal routine for the hard part
+ *
+ *	This code is partly snarfed from an early grep written by
+ *	David Conroy. The backref and tag stuff, and various other
+ *	innovations are by oz.
+ *
+ *	special case optimizations: (nfa[n], nfa[n+1])
+ *		CLO ANY
+ *			We KNOW .* will match everything up to the
+ *			end of line. Thus, directly go to the end of
+ *			line, without recursive pmatch calls. As in
+ *			the other closure cases, the remaining pattern
+ *			must be matched by moving backwards on the
+ *			string recursively, to find a match for xy
+ *			(x is ".*" and y is the remaining pattern)
+ *			where the match satisfies the LONGEST match for
+ *			x followed by a match for y.
+ *		CLO CHR
+ *			We can again scan the string forward for the
+ *			single char and at the point of failure, we
+ *			execute the remaining nfa recursively, same as
+ *			above.
+ *
+ *	At the end of a successful match, bopat[n] and eopat[n]
+ *	are set to the beginning and end of subpatterns matched
+ *	by tagged expressions (n = 1 to 9).
+ *
+ */
+
+#ifndef re_fail
+extern void re_fail(char *, unsigned char);
+#endif
+
+/*
+ * character classification table for word boundary operators BOW
+ * and EOW. the reason for not using ctype macros is that we can
+ * let the user add into our own table. see re_modw. This table
+ * is not in the bitset form, since we may wish to extend it in the
+ * future for other character classifications.
+ *
+ *	TRUE for 0-9 A-Z a-z _
+ */
+static CHAR chrtyp[MAXCHR] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+	0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 0, 0, 0, 0, 0
+	};
+
+#define inascii(x)	(0177&(x))
+#define iswordc(x)	chrtyp[inascii(x)]
+#define isinset(x,y)	((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
+
+/*
+ * skip values for CLO XXX to skip past the closure
+ */
+
+#define ANYSKIP	2	/* [CLO] ANY END ...	     */
+#define CHRSKIP	3	/* [CLO] CHR chr END ...     */
+#define CCLSKIP 18	/* [CLO] CCL 16bytes END ... */
+
+static const char *
+pmatch(const char *lp, CHAR *ap, int *score)
+{
+	register int op, c, n;
+	register const char *e;		/* extra pointer for CLO */
+	register const char *bp;		/* beginning of subpat.. */
+	register const char *ep;		/* ending of subpat..	 */
+	const char *are;			/* to save the line ptr. */
+
+	while ((op = *ap++) != END)
+		switch(op) {
+
+		case CHR:
+			if (*lp++ != *ap++)
+				return 0;
+			(*score) += 100;
+			break;
+		case ANY:
+			if (!*lp++)
+				return 0;
+			(*score)++;
+			break;
+		case CCL:
+			c = *lp++;
+			if (!isinset(ap,c))
+				return 0;
+			ap += BITBLK;
+			(*score) += 2;
+			break;
+		case BOL:
+			if (lp != bol)
+				return 0;
+			(*score) += 10;
+			break;
+		case EOL:
+			if (*lp)
+				return 0;
+			(*score) += 10;
+			break;
+		case BOT:
+			bopat[*ap++] = lp;
+			break;
+		case EOT:
+			eopat[*ap++] = lp;
+			break;
+		case BOW:
+			if ((lp!=bol && iswordc(lp[-1])) || !iswordc(*lp))
+				return 0;
+			(*score) += 5;
+			break;
+		case EOW:
+			if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp))
+				return 0;
+			(*score) += 5;
+			break;
+		case REF:
+			n = *ap++;
+			bp = bopat[n];
+			ep = eopat[n];
+			while (bp < ep) {
+				if (*bp++ != *lp++)
+					return 0;
+			(*score) += 2;
+			}
+			break;
+		case CLO:
+			are = lp;
+			switch(*ap) {
+
+			case ANY:
+				while (*lp)
+					lp++;
+				n = ANYSKIP;
+				(*score)++;
+				break;
+			case CHR:
+				c = *(ap+1);
+				while (*lp && c == *lp)
+					lp++;
+				n = CHRSKIP;
+				(*score) += 100;
+				break;
+			case CCL:
+				while ((c = *lp) && isinset(ap+1,c))
+					lp++;
+				n = CCLSKIP;
+				(*score) += 2;
+				break;
+			default:
+				re_fail("closure: bad nfa.", *ap);
+				return 0;
+			}
+
+			ap += n;
+
+			while (lp >= are) {
+				e = pmatch(lp, ap, score);
+				if (e)
+					return e;
+				--lp;
+			}
+			return 0;
+		default:
+			re_fail("re_exec: bad nfa.", op);
+			return 0;
+		}
+	return lp;
+}
+
+/*
+ * re_modw:
+ *	add new characters into the word table to change re_exec's
+ *	understanding of what a word should look like. Note that we
+ *	only accept additions into the word definition.
+ *
+ *	If the string parameter is 0 or null string, the table is
+ *	reset back to the default containing A-Z a-z 0-9 _. [We use
+ *	the compact bitset representation for the default table]
+ */
+
+static CHAR deftab[16] = {
+	0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207,
+	0376, 0377, 0377, 007
+};
+
+void
+re_modw(char *s)
+{
+	register int i;
+
+	if (!s || !*s) {
+		for (i = 0; i < MAXCHR; i++)
+			if (!isinset(deftab,i))
+				iswordc(i) = 0;
+	}
+	else
+		while(*s)
+			iswordc(*s++) = 1;
+}
+
+/* Substitute the matching part in the last re_exec call with sub. The
+   result is returned in a newly allocated string. */
+char *re_subs_dup(char *sub)
+{
+	char *dst;
+	const char *end;
+	int l1, l2, l3;
+	end = bol + strlen(bol);
+	l1 = bopat[0] - bol;
+	if (sub != NULL)
+		l2 = strlen(sub);
+	else
+		l2 = 0;
+	l3 = end - eopat[0];
+	if (l3 < 0)
+		l3 = 0;
+	dst = malloc(l1+l2+l3+1);
+	memcpy(dst, bol, l1);
+	if (l2 != 0)
+		memcpy(dst+l1, sub, l2);
+	memcpy(dst+l1+l2, eopat[0], l3+1);
+	return dst;
+}
diff --git a/scconfig/src/default/regex.h b/scconfig/src/default/regex.h
new file mode 100644
index 0000000..03830a0
--- /dev/null
+++ b/scconfig/src/default/regex.h
@@ -0,0 +1,14 @@
+#ifndef REGEX_H
+#define REGEX_H
+
+extern const char *bopat[];
+extern const char *eopat[];
+
+
+extern char *re_comp(const char *);
+extern int re_exec(const char *);
+extern void re_modw(char *);
+char *re_subs_dup(char *sub);
+
+
+#endif /* REGEX_H */
diff --git a/scconfig/src/default/str.c b/scconfig/src/default/str.c
new file mode 100644
index 0000000..b2b2034
--- /dev/null
+++ b/scconfig/src/default/str.c
@@ -0,0 +1,183 @@
+/*
+    scconfig - non-standard string manipulation routines
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+char *strclone(const char *str)
+{
+	int l;
+	char *ret;
+
+	if (str == NULL)
+		return NULL;
+
+	l   = strlen(str)+1;
+	ret = malloc(l);
+	memcpy(ret, str, l);
+	return ret;
+}
+
+#define SPACE(c) (((c) == '\r') || ((c) == '\n') || ((c) == '\t') || ((c) == ' '))
+
+char *trim_left(char *str)
+{
+	while(SPACE(*str)) str++;
+	return str;
+}
+
+char *trim_right(char *str)
+{
+	char *end;
+
+	end = str + strlen(str) - 1;
+	while((end >= str) && SPACE(*end)) { *end = '\0'; end--; }
+	return str;
+}
+
+char *strip(char *str)
+{
+	return trim_left(trim_right(str));
+}
+
+char *str_chr(char *str, char c)
+{
+	char *s;
+
+	for(s = str; *s != '\0'; s++)
+		if (*s == c)
+			return s;
+	return NULL;
+}
+
+char *str_rchr(char *str, char c)
+{
+	char *s, *last;
+
+	last = NULL;
+	for(s = str; *s != '\0'; s++)
+		if (*s == c)
+			last = s;
+	return last;
+}
+
+char *str_subsn(const char *str)
+{
+	char *out, *o;
+	const char *i;
+	if (str == NULL)
+		return strclone("");
+	o = out = malloc(strlen(str)+1);
+	for(i = str; *i != '\0'; i++, o++) {
+		if ((i[0] == '\\') && (i[1] == 'n')) {
+			i++;
+			*o = '\n';
+		}
+		else
+			*o = *i;
+	}
+	*o = '\0';
+	return out;
+}
+
+char *str_concat(const char *sep, ...)
+{
+#	define CONCAT_MAX 64
+	int len[CONCAT_MAX];
+	const char *str[CONCAT_MAX];
+	int n, v, sum, sl;
+	char *out, *o;
+	va_list ap;
+	va_start(ap, sep);
+
+	if (sep == NULL)
+		sep = "";
+
+	/* load all strings into an array, measure their lengths */
+	sum = 0;
+	for(v = 0; ;v++) {
+		if (v >= CONCAT_MAX) {
+			fprintf(stderr, "Internal error: str_concat got more strings than CONCAT_MAX\n");
+			abort();
+		}
+		str[v] = va_arg(ap, const char *);
+		if (str[v] == NULL) {
+			len[v] = 0;
+			break;
+		}
+		len[v] = strlen(str[v]);
+		sum += len[v];
+	}
+
+	sl = strlen(sep);
+	sum += (v-1) * sl + 1; /* + a sep between each two strings and a terminator at the end */
+	o = out = malloc(sum);
+	for(n = 0; n < v; n++) {
+		if ((n > 0) && (sl > 0)) {
+			memcpy(o, sep, sl);
+			o += sl;
+		}
+		if (len[n] > 0) {
+			memcpy(o, str[n], len[n]);
+			o += len[n];
+		}
+	}
+	*o = '\0';
+	va_end(ap);
+	return out;
+}
+
+char *esc_interpret(const char *str)
+{
+	char *out, *si, *so;
+
+	out = strclone(str);
+	/* replace (interpret) \ sequences in seq */
+	for(si = so = out; *si != '\0'; si++,so++) {
+		if (si[0] == '\\') {
+			switch(si[1]) {
+				case 'n': *so = '\n'; break;
+				case 't': *so = '\t'; break;
+				case 's': *so = ' '; break;
+				case '\\': *so = '\\'; break;
+			}
+			si++;
+		}
+		else
+			*so = *si;
+	}
+	*so = '\0';
+	return out;
+}
+
+int chr_inset(char c, const char *set)
+{
+	while (*set != '\0') {
+		if (c == *set)
+			return 1;
+		set++;
+	}
+	return 0;
+}
+
diff --git a/scconfig/src/generator/INIT.c b/scconfig/src/generator/INIT.c
new file mode 100644
index 0000000..e69de29
diff --git a/scconfig/src/generator/Makefile.plugin b/scconfig/src/generator/Makefile.plugin
new file mode 100644
index 0000000..09310a6
--- /dev/null
+++ b/scconfig/src/generator/Makefile.plugin
@@ -0,0 +1,11 @@
+GENERATOR_OBJS = \
+  $(BIN)/generator/generator.o \
+  $(BIN)/generator/openfiles.o
+
+GENERATOR_CFLAGS = -I$(SRC)/generator
+
+$(BIN)/generator/generator.o: $(SRC)/generator/generator.c $(SRC)/default/dep.h $(SRC)/default/log.h $(SRC)/default/regex.h
+	$(CC) $(CFLAGS) -c $(SRC)/generator/generator.c -o $(BIN)/generator/generator.o
+
+$(BIN)/generator/openfiles.o: $(SRC)/generator/openfiles.c
+	$(CC) $(CFLAGS) -c $(SRC)/generator/openfiles.c -o $(BIN)/generator/openfiles.o
diff --git a/scconfig/src/generator/generator.c b/scconfig/src/generator/generator.c
new file mode 100644
index 0000000..360b45b
--- /dev/null
+++ b/scconfig/src/generator/generator.c
@@ -0,0 +1,826 @@
+/*
+    scconfig - minimalistic template system
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "db.h"
+#include "log.h"
+#include "libs.h"
+#include "regex.h"
+#include "openfiles.h"
+#include "generator.h"
+
+#define separator '#'
+
+typedef enum {
+	cp_mute,   /* drop characters */
+	cp_out,    /* copy characters on output */
+	cp_buff    /* copy characters in an internal buffer */
+} copymode_t;
+
+typedef struct stack_s {
+	copymode_t copy_mode;
+	char *buff;
+	int alloced, used;
+	int nonest;
+	char *forvar, *forvarin;
+	FILE *in, *out;
+	char *cmd;
+	int sepcount;
+} gen_stack_t;
+
+/* Stack for nested ifs and fors. */
+#define stack_max 128
+static gen_stack_t stack[stack_max];
+static int sp; /* points to the first free slot above the top of the stack */
+#define TOP stack[sp-1]
+static openfiles_t of;
+
+static char cmd_buff[1024];
+static char *cmd;
+static int sepcount;
+
+static void push()
+{
+	if (sp >= stack_max) {
+		error("ifs are nested too deep\n");
+		abort();
+	}
+	stack[sp].copy_mode = TOP.copy_mode;
+	stack[sp].nonest    = TOP.nonest;
+	stack[sp].buff      = NULL;
+	stack[sp].alloced   = 0;
+	stack[sp].used      = 0;
+	stack[sp].forvar    = NULL;
+	stack[sp].forvarin  = NULL;
+	stack[sp].in        = TOP.in;
+	stack[sp].out       = TOP.out;
+	stack[sp].sepcount  = sepcount;
+	*cmd = '\0';
+	stack[sp].cmd       = strclone(cmd_buff);
+	cmd = cmd_buff;
+	*cmd = '\0';
+	sp++;
+}
+
+static void pop()
+{
+	strcpy(cmd_buff, TOP.cmd);
+	cmd = cmd_buff + strlen(cmd_buff);
+	free(TOP.cmd);
+	TOP.cmd = NULL;
+	sepcount = TOP.sepcount;
+
+	if (TOP.buff != NULL)
+		free(TOP.buff);
+	if (TOP.forvar != NULL)
+		free(TOP.forvar);
+	if (TOP.forvarin != NULL)
+		free(TOP.forvarin);
+	sp--;
+	if (sp <= 0) {
+		error("if underflow (too many endifs)\n");
+		abort();
+	}
+}
+
+genmode_t generate_gotchar(genmode_t mode, int c);
+
+int exec_file(const char *input, const char *output);
+
+/* generate some output: emit different objects */
+void generator_emit_char(int c)
+{
+	switch(TOP.copy_mode) {
+		case cp_mute:
+			break;
+		case cp_out:
+			fputc(c, TOP.out);
+			break;
+		case cp_buff:
+			if (TOP.used >= TOP.alloced) {
+				TOP.alloced += 256;
+				TOP.buff = realloc(TOP.buff, TOP.alloced);
+			}
+			TOP.buff[TOP.used] = c;
+			TOP.used++;
+			break;
+	}
+}
+
+void generator_emit_string(const char *s)
+{
+	if (TOP.copy_mode == cp_mute)
+		return;
+	for(;*s != '\0'; s++)
+		generator_emit_char(*s);
+}
+
+void generator_emit_separators(int count)
+{
+	int n;
+
+	if (TOP.copy_mode == cp_mute)
+		return;
+
+	for(n = 0; n < count; n++)
+		generator_emit_char(separator);
+}
+
+/* execute different commands */
+static void do_put(char *cmd, int appending, int node, int eval)
+{
+	char *name, *value, *dvalue;
+	const char *cvalue;
+
+	name  = cmd;
+	value = strchr(name, ' ');
+	if (value == NULL) {
+		error("no value for put or append");
+		abort();
+	}
+	*value = '\0';
+	value++;
+	if (node) {
+		cvalue = get(value);
+		if (value == NULL)
+			cvalue = "";
+	}
+	else
+		cvalue = value;
+
+	/* whether need to eval the result */
+	if (eval) {
+		dvalue = generator_eval(cvalue, mode_cmd);
+		cvalue = dvalue;
+	}
+	else
+		dvalue = NULL;
+
+	if (appending) {
+		if (cvalue != NULL)
+			append(name, cvalue);
+	}
+	else
+		put(name, cvalue);
+	if (dvalue != NULL)
+		free(dvalue);
+}
+
+static void do_report(char *cmd)
+{
+	report("%s\n", cmd);
+}
+
+static void do_abort(char *cmd)
+{
+	report("Abort requested by template.\n");
+	abort();
+}
+
+static void do_if(char *cmd)
+{
+	char *left, *condition, *right, *end;
+	const char *node;
+	float node_val, right_val;
+	int invert = 1, outcome = 0;
+	int invalid_num = 0;
+
+
+	left = cmd;
+	condition = strchr(left, ' ');
+	if (condition != NULL) {
+		*condition = '\0';
+		condition++;
+		right = strchr(condition, ' ');
+		if (right == NULL) {
+			if (*condition != '@') {
+				error("no right part of if with condition");
+				abort();
+			}
+		}
+		else {
+			*right = '\0';
+			right++;
+		}
+	}
+	else
+		right = NULL;
+
+	while(*left == '!') {
+		left++;
+		invert = -invert;
+	}
+
+	node = get(left);
+	if (node == NULL)
+		node = "";
+	node_val  = strtod(node, &end);
+	if (*end != '\0') invalid_num++;
+	if (right != NULL) {
+		right_val = strtod(right, &end);
+		if (*end != '\0') invalid_num++;
+	}
+
+	if (condition == NULL) {
+		outcome = istrue(node);
+	}
+	else {
+		if (invalid_num && ((*condition == '>') || (*condition == '<'))) {
+			error("Invalid numeric format for if\n");
+			abort();
+		}
+		switch(*condition) {
+			case '=': break;
+			case '@': outcome = (*node == '\0'); break;
+			case '<': outcome = (node_val < right_val); break;
+			case '>': outcome = (node_val > right_val); break;
+			case '~':
+				re_comp(right);
+				outcome = re_exec(node);
+				break;
+			default:
+				error("Illegal condition\n");
+				abort();
+		}
+		if (condition[1] == '=') {
+			outcome |= (strcmp(node, right) == 0);
+			if (!invalid_num)
+				outcome |= (node_val == right_val);
+		}
+	}
+	if (invert < 0)
+		outcome = !outcome;
+
+	push();
+	if (outcome)
+		TOP.copy_mode = stack[sp-2].copy_mode;
+	else
+		TOP.copy_mode = cp_mute;
+}
+
+static void do_else(char *cmd)
+{
+	cmd = NULL; /* suppress compiler warning about unused arg */
+
+	if (TOP.copy_mode == cp_mute)
+		TOP.copy_mode = stack[sp-2].copy_mode;
+	else
+		TOP.copy_mode = cp_mute;
+}
+
+static void do_endif(char *cmd)
+{
+	cmd = NULL; /* suppress compiler warning about unused arg */
+
+	pop();
+}
+
+static void do_get(char *cmd)
+{
+	const char *data;
+	data = get(cmd);
+
+	if (data != NULL)
+		generator_emit_string(data);
+
+}
+
+static void do_foreach(char *cmd)
+{
+	char *forvar;
+	char *forvarin;
+
+	forvar = cmd;
+	while((*forvar == ' ') || (*forvar == '\t')) forvar++;
+	forvarin = strchr(forvar, ' ');
+	if (forvarin != NULL) {
+		*forvarin = '\0';
+		forvarin++;
+		while((*forvarin == ' ') || (*forvarin == '\t')) forvarin++;
+	}
+	else {
+		fprintf(stderr, "Error: foreach: not enough arguments\n");
+		abort();
+	}
+
+	/* start saving bytes into a new buffer */
+	push();
+	TOP.copy_mode = cp_buff;
+	TOP.nonest = 1;
+
+	/* remember settings for do_done */
+	TOP.forvar = strclone(forvar);
+	TOP.forvarin = strclone(forvarin);
+}
+
+static void do_done(char *cmd)
+{
+	genmode_t mode;
+	char *b, *s, *next;
+	const char *cs;
+	cmd = NULL; /* suppress compiler warning about unused arg */
+
+	/* run the loop */
+	TOP.buff[TOP.used] = '\0';
+	cs = get(TOP.forvarin);
+	if (cs != NULL) {
+		while(*cs == ' ') cs++;
+		s = strclone(cs);
+		do {
+			next = strchr(s, ' ');
+			if (next != NULL) {
+				*next = '\0';
+				next++;
+				while(*next == ' ') next++;
+			}
+			put(TOP.forvar, s);
+			b = TOP.buff;
+			push();
+			mode          = mode_copy;
+			TOP.nonest    = 0;
+			TOP.copy_mode = stack[sp-3].copy_mode;
+			for(; *b != '\0'; b++) {
+				mode = generate_gotchar(mode, *b);
+				if (mode == mode_fatal)
+					break;
+			}
+			pop();
+			s = next;
+		} while(s != NULL);
+		free(s);
+	}
+	pop();
+}
+
+static void do_include(char *cmd, int node)
+{
+	FILE *in;
+	const char *fn;
+
+	while((*cmd == ' ') || (*cmd == '\t')) cmd++;
+	if (node) {
+		fn = get(cmd);
+		if (fn == NULL)
+			fn = "";
+	}
+	else
+		fn = cmd;
+
+	in = openfile_open(&of, fn, "r");
+	if (in != NULL) {
+		rewind(in);
+		push();
+		TOP.in = in;
+		TOP.out = stack[sp-2].out;
+		exec_file(NULL, NULL);
+		pop();
+	}
+	else
+		report("Error: generator: can't open %s for reading (for include)\n", fn);
+}
+
+
+static void do_sub(char *cmd, int global, int subnode)
+{
+	char *node, *pat, *sub, *err, *start;
+	char *buff, *end;
+	const char *val, *csub;
+	int score, slen;
+
+	node = cmd;
+	pat = strchr(cmd, ' ');
+	if (pat == NULL) {
+		fprintf(stderr, "Error: generator: no node argument for sub\n"); /* TODO: line number! */
+		return;
+	}
+	*pat = '\0';
+	pat++;
+
+	sub = pat;
+	while(1) {
+		sub = strchr(sub, ' ');
+		if (sub == NULL) {
+			fprintf(stderr, "Error: generator: no substitution string argument for sub\n"); /* TODO: line number! */
+			return;
+		}
+		if (sub[-1] != '\\') {
+			*sub = '\0';
+			sub++;
+			break;
+		}
+		sub++;
+	}
+
+	val = get(node);
+	if (val == NULL)
+		val="";
+	err = re_comp(pat);
+	if (err != NULL) {
+		fprintf(stderr, "Error: generator: regex syntax error: %s\n", err); /* TODO: line number! */
+		return;
+	}
+
+	if (subnode) {
+		csub = (char *)get(sub);
+		if (sub == NULL) {
+			fprintf(stderr, "Error: generator: invalid second node for nsub/ngsub\n"); /* TODO: line number! */
+			return;
+		}
+	}
+	else
+		csub = sub;
+
+	slen = strlen(csub);
+	if (global)
+		buff = malloc(strlen(val)*(slen+3)+32); /* big enough for worst case, when every letter and $ and ^ are replaced with sub */
+	else
+		buff = malloc(strlen(val)+slen+32);  /* only one replacement will be done */
+	strcpy(buff, val);
+
+
+	start = buff;
+	do {
+		score = re_exec(start);
+		if (score == 0)
+			break;
+		end = buff + strlen(buff);
+		if (eopat[0] - bopat[0] != slen) {
+			int mlen = end - eopat[0]+1;
+			if (mlen > 0)
+				memmove(bopat[0] + slen, eopat[0], mlen);
+		}
+		memcpy(bopat[0], csub, slen);
+		start = bopat[0] + slen;
+	} while(global);
+
+	generator_emit_string(buff);
+	free(buff);
+}
+
+static void do_redir(char *cmd)
+{
+	FILE *out;
+	if (*cmd == '\0') {
+		pop();
+		return;
+	}
+	while((*cmd == ' ') || (*cmd == '\t')) cmd++;
+	push();
+	out = openfile_open(&of, cmd, "w");
+	if (out == NULL)
+		report("Error: generator: can't open %s for writing (for redirection)\n", cmd);
+	else
+		TOP.out = out;
+}
+
+static void do_neval(char *cmd)
+{
+	char *res;
+
+	res = generator_eval(get(cmd), mode_copy);
+	if (res != NULL) {
+		generator_emit_string(res);
+		free(res);
+	}
+}
+
+static void do_nsplit(char *cmd)
+{
+	const char *s;
+	char *str, *arr, *regex, *val, *path, *idx;
+	int len, i;
+
+	str = cmd;
+	arr = strpbrk(str, " \t");
+	if (arr == NULL)
+		goto nsplit_err;
+	*arr = '\0';
+	arr++;
+	regex = strpbrk(arr, " \t");
+	if (regex == NULL)
+		goto nsplit_err;
+	*regex = '\0';
+	regex++;
+
+	s = get(str);
+	re_comp(regex);
+	path = malloc(strlen(arr) + 32);
+	strcpy(path, arr);
+	idx = path + strlen(arr);
+	*idx = '/';
+	idx++;
+	i = 1;
+	while((s != NULL) && (re_exec(s) > 0)) {
+		len = bopat[0] - s;
+		val = malloc(len + 2);
+		strncpy(val, s, len);
+		val[len] = '\0';
+		sprintf(idx, "%d", i);
+		put(path, val);
+		free(val);
+		s = eopat[0];
+		i++;
+	};
+	sprintf(idx, "%d", i);
+	put(path, s);
+
+	return;
+	nsplit_err:;
+	report("Error: generator: invalid number of arguments for nsplit: %s\n", cmd);
+}
+
+static void do_uniq(char *cmd)
+{
+	char *sep, *node;
+	char **list;
+	int used, n;
+
+	node = strpbrk(cmd, " \t");
+	*node = '\0';
+	node++;
+	if (node == NULL)
+		return;
+
+	sep = esc_interpret(cmd);
+
+	list = uniq_inc_arr(node, 1, sep, &used);
+
+	/* print list */
+	for(n = 0; n < used; n++) {
+		if (n > 0)
+			generator_emit_char(*sep);
+		generator_emit_string(list[n]);
+	}
+	uniq_inc_free(list);
+}
+
+
+#ifdef GENCALL
+extern void generator_callback(char *cmd, char *args);
+
+static void do_call(char *cmd)
+{
+	char *args;
+
+	args = strpbrk(cmd, " \t");
+	if (args != NULL) {
+		*args = '\0';
+		args++;
+	}
+	generator_callback(cmd, args);
+}
+#endif
+
+
+/* the command dispatcher */
+static void do_cmd(char *cmd)
+{
+#define CLOSE \
+	if ((TOP.nonest == 0) || ((--TOP.nonest) == 0)) {
+
+#define ELSE \
+	} else goto pass;
+
+#define OPEN \
+	if (TOP.nonest != 0) {\
+		TOP.nonest++; \
+		goto pass; \
+	} \
+	else
+
+
+#define NEUTRAL \
+	if (TOP.nonest != 0) goto pass; \
+	if (TOP.copy_mode != cp_mute)
+
+
+#define NEUTRAL_IF \
+	if (TOP.nonest != 0) goto pass; \
+
+	     if (strncmp(cmd, "endif",     5) == 0) { CLOSE   do_endif(cmd+5); ELSE }
+	else if (strncmp(cmd, "done",      4) == 0) { CLOSE   do_done(cmd+4); ELSE }
+	else if (strncmp(cmd, "foreach ",  8) == 0) { OPEN    do_foreach(cmd+8); }
+	else if (strncmp(cmd, "if ",       3) == 0) { OPEN    do_if(cmd+3); }
+	else if (strncmp(cmd, "report ",   7) == 0) { NEUTRAL do_report(cmd+7); }
+	else if (strncmp(cmd, "abort",     5) == 0) { NEUTRAL do_abort(cmd+5); }
+	else if (strncmp(cmd, "sep",       3) == 0) { NEUTRAL generator_emit_separators(3); }
+	else if (strncmp(cmd, "redir",     5) == 0) { NEUTRAL do_redir(cmd+5); }
+	else if (strncmp(cmd, "include ",  8) == 0) { NEUTRAL do_include(cmd+8, 0); }
+	else if (strncmp(cmd, "ninclude ", 9) == 0) { NEUTRAL do_include(cmd+8, 1); }
+	else if (strncmp(cmd, "sep",       3) == 0) { NEUTRAL generator_emit_separators(3); }
+	else if (strncmp(cmd, "neval ",    6) == 0) { NEUTRAL do_neval(cmd+6); }
+	else if (strncmp(cmd, "nsplit ",   7) == 0) { NEUTRAL do_nsplit(cmd+7); }
+	else if (strncmp(cmd, "put ",      4) == 0) { NEUTRAL do_put(cmd+4, 0, 0, 0); }
+	else if (strncmp(cmd, "append ",   7) == 0) { NEUTRAL do_put(cmd+7, 1, 0, 0); }
+	else if (strncmp(cmd, "nput ",     5) == 0) { NEUTRAL do_put(cmd+5, 0, 1, 0); }
+	else if (strncmp(cmd, "nappend ",  8) == 0) { NEUTRAL do_put(cmd+8, 1, 1, 0); }
+	else if (strncmp(cmd, "puteval ",  8) == 0) { NEUTRAL do_put(cmd+8, 0, 0, 1); }
+	else if (strncmp(cmd, "appendeval ",11) == 0) { NEUTRAL do_put(cmd+11, 1, 0, 1); }
+	else if (strncmp(cmd, "nputeval ", 9) == 0) { NEUTRAL do_put(cmd+9, 0, 1, 1); }
+	else if (strncmp(cmd, "nappendeval ",12) == 0) { NEUTRAL do_put(cmd+12, 1, 1, 1); }
+	else if (strncmp(cmd, "sub ",      4) == 0) { NEUTRAL do_sub(cmd+4, 0, 0); }
+	else if (strncmp(cmd, "gsub ",     5) == 0) { NEUTRAL do_sub(cmd+5, 1, 0); }
+	else if (strncmp(cmd, "nsub ",     5) == 0) { NEUTRAL do_sub(cmd+5, 0, 1); }
+	else if (strncmp(cmd, "ngsub ",    6) == 0) { NEUTRAL do_sub(cmd+6, 1, 1); }
+	else if (strncmp(cmd, "uniq ",     5) == 0) { NEUTRAL do_uniq(cmd+5); }
+#ifdef GENCALL
+	else if (strncmp(cmd, "call ",     5) == 0) { NEUTRAL do_call(cmd+5); }
+#endif
+	else if (strncmp(cmd, "else",      4) == 0) { NEUTRAL_IF do_else(cmd+5); }
+	else                                       { NEUTRAL do_get(cmd); }
+
+	/* falling trough here means one of the do_ ran */
+	return;
+	pass:;
+		generator_emit_separators(3);
+		generator_emit_string(cmd);
+		generator_emit_separators(3);
+
+#undef CLOSE
+#undef OPEN
+}
+
+
+genmode_t generate_gotchar(genmode_t mode, int c)
+{
+		switch(mode) {
+			case mode_copy:
+				if (c == separator) {
+					sepcount = 1;
+					mode = mode_copy_sep;
+				}
+				else
+					generator_emit_char(c);
+				break;
+			case mode_cmd:
+				if (c == separator) {
+					sepcount = 1;
+					mode = mode_cmd_sep;
+				}
+				else {
+					*cmd = c;
+					cmd++;
+					if (cmd - cmd_buff >= (int)sizeof(cmd_buff)) {
+						/* no room for the terminator */
+						return mode_fatal;
+					}
+				}
+				break;
+			case mode_copy_sep:
+				if (c == separator) {
+					sepcount++;
+					if (sepcount == 3) {
+						cmd  = cmd_buff;
+						mode = mode_cmd;
+					}
+				}
+				else {
+					/* one or two hashmarks then something else */
+					generator_emit_separators(sepcount);
+					mode = mode_copy_sep;
+					generator_emit_char(c);
+					sepcount = 0;
+				}
+				break;
+			case mode_cmd_sep:
+				if (c == separator) {
+					sepcount++;
+					if (sepcount == 3) {
+						*cmd = '\0';
+						do_cmd(cmd_buff);
+						mode = mode_copy;
+					}
+				}
+				else {
+					/* one or two separators then something else */
+					generator_emit_separators(sepcount);
+					mode = mode_copy_sep;
+					generator_emit_char(c);
+					sepcount = 0;
+				}
+				break;
+			case mode_fatal:
+				fprintf(stderr, "scconfig generator: internal error in parser state machine (executed mode_fatal)\n");
+				abort();
+		}
+	return mode;
+}
+
+static int execute()
+{
+	genmode_t mode;
+	int c;
+
+	mode = mode_copy;
+
+	while((c = fgetc(TOP.in)) != EOF) {
+		mode = generate_gotchar(mode, c);
+		if (mode == mode_fatal) {
+			return -3;
+		}
+	}
+	return 0;
+}
+
+
+/* generate an output file from an input file */
+int exec_file(const char *input, const char *output)
+{
+	int ret, startsp;
+
+	startsp = sp;
+
+	if (input != NULL) {
+		TOP.in = openfile_open(&of, input, "r");
+		if (TOP.in == NULL)
+			return -1;
+	}
+
+	if (output != NULL) {
+		TOP.out = openfile_open(&of, output, "w");
+		if (TOP.out == NULL) {
+			fclose(TOP.in);
+			return -1;
+		}
+	}
+
+	ret = execute();
+
+	if (sp != startsp) {
+		error("Error in nesting: not enough endif or done tags\n");
+		return -2;
+	}
+	return 0;
+}
+
+/* wrapper for setting up stack */
+int generate(const char *input, const char *output)
+{
+	int ret;
+
+	sp            = 1;
+	TOP.copy_mode = cp_out;
+	TOP.buff      = NULL;
+	TOP.alloced   = 0;
+	TOP.used      = 0;
+	TOP.nonest    = 0;
+	TOP.forvar    = NULL;
+	TOP.forvarin  = NULL;
+
+	memset(&of, 0, sizeof(of));
+	ret = exec_file(input, output);
+	openfile_free(&of);
+
+	return ret;
+}
+
+char *generator_eval(const char *input, genmode_t start_mode)
+{
+	char *res;
+	const char *b;
+	genmode_t mode;
+
+	if (input == NULL)
+		return NULL;
+
+	mode = start_mode;
+	push();
+	TOP.copy_mode = cp_buff;
+	TOP.nonest    = 0;
+	for(b = input; *b != '\0'; b++) {
+		mode = generate_gotchar(mode, *b);
+		if (mode == mode_fatal)
+			break;
+	}
+	if ((start_mode == mode_cmd) && (mode == mode_cmd)) {
+		mode = generate_gotchar(mode, separator);
+		mode = generate_gotchar(mode, separator);
+		mode = generate_gotchar(mode, separator);
+	}
+	generator_emit_char('\0');
+	res = TOP.buff;
+	TOP.buff = NULL;
+	pop();
+	return res;
+}
diff --git a/scconfig/src/generator/generator.h b/scconfig/src/generator/generator.h
new file mode 100644
index 0000000..be4df60
--- /dev/null
+++ b/scconfig/src/generator/generator.h
@@ -0,0 +1,15 @@
+int generate(const char *input, const char *output);
+
+typedef enum {
+	mode_copy,
+	mode_cmd,
+	mode_copy_sep,
+	mode_cmd_sep,
+	mode_fatal
+} genmode_t;
+
+char *generator_eval(const char *input, genmode_t mode);
+
+void generator_emit_char(int c);
+void generator_emit_string(const char *s);
+void generator_emit_separators(int count);
diff --git a/scconfig/src/generator/openfiles.c b/scconfig/src/generator/openfiles.c
new file mode 100644
index 0000000..92ac58d
--- /dev/null
+++ b/scconfig/src/generator/openfiles.c
@@ -0,0 +1,95 @@
+#include <stdlib.h>
+#include <string.h>
+#include "openfiles.h"
+#include "libs.h"
+
+static openfile_t *find_file_by_name(openfiles_t *of, const char *name, int alloc, const char *mode, int recursion)
+{
+	int n;
+	struct stat buf;
+	FILE *f;
+
+	if (recursion > 4) {
+		fprintf(stderr, "scconfig internal error: openfiles infinite recursion for %s\n", name);
+		abort();
+	}
+
+	if (stat(name, &buf) != 0) {
+		/* File does not exist - try to create it or return NULL */
+		if (*mode == 'w') {
+			f = fopen(name, "w");
+			if (f == NULL)
+				return NULL;
+			fclose(f);
+			return find_file_by_name(of, name, alloc, mode, recursion + 1);
+		}
+		return NULL;
+	}
+
+	/* look for an existing open file in the list */
+	for(n = 0; n < of->used; n++)
+		if ((of->files[n].dev == buf.st_dev) && (of->files[n].ino == buf.st_ino) && (strcmp(of->files[n].mode, mode) == 0))
+			return &(of->files[n]);
+
+	if (!alloc)
+		return NULL;
+
+	/* File exists but not on the list yet, allocate a new slot for it */
+	/* TODO: try to find an empty slot first */
+	if (of->used >= of->alloced) {
+		of->alloced += 16;
+		of->files = realloc(of->files, sizeof(openfile_t) * of->alloced);
+	}
+
+	n = of->used;
+	of->files[n].dev  = buf.st_dev;
+	of->files[n].ino  = buf.st_ino;
+	of->files[n].f    = NULL;
+	of->files[n].mode = strclone(mode);
+	of->used++;
+	return &(of->files[n]);
+}
+
+void release(openfile_t *o)
+{
+	if (o->mode != NULL) {
+		free(o->mode);
+		o->mode = NULL;
+	}
+	if (o->f != NULL) {
+		fclose(o->f);
+		o->f = NULL;
+	}
+	o->dev = -1;
+	o->ino = -1;
+}
+
+FILE *openfile_open(openfiles_t *of, const char *fn, const char *mode)
+{
+	openfile_t *o;
+	o = find_file_by_name(of, fn, 1, mode, 0);
+	if (o == NULL)
+		return NULL;
+	o->f = fopen(fn, mode);
+	if (o->f == NULL) {
+		release(o);
+		return NULL;
+	}
+	return o->f;
+}
+
+void openfile_closeall(openfiles_t *of)
+{
+	int n;
+	if (of->files == NULL)
+		return;
+	for(n = 0; n < of->used; n++)
+		release(&(of->files[n]));
+}
+
+void openfile_free(openfiles_t *of)
+{
+	openfile_closeall(of);
+	if (of->files != NULL)
+		free(of->files);
+}
diff --git a/scconfig/src/generator/openfiles.h b/scconfig/src/generator/openfiles.h
new file mode 100644
index 0000000..bb378c6
--- /dev/null
+++ b/scconfig/src/generator/openfiles.h
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+typedef struct openfile_s {
+	FILE *f;
+
+	/* identify the file: */
+	dev_t dev;
+	ino_t ino;
+
+	char *mode;
+} openfile_t;
+
+typedef struct openfiles_s {
+	int alloced, used;
+	openfile_t *files;
+} openfiles_t;
+
+FILE *openfile_open(openfiles_t *of, const char *fn, const char *mode);
+void openfile_closeall(openfiles_t *of);
+void openfile_free(openfiles_t *of);
diff --git a/scconfig/src/gui/INIT.c b/scconfig/src/gui/INIT.c
new file mode 100644
index 0000000..0669f3b
--- /dev/null
+++ b/scconfig/src/gui/INIT.c
@@ -0,0 +1,2 @@
+	deps_gui_init();
+
diff --git a/scconfig/src/gui/INIT.h b/scconfig/src/gui/INIT.h
new file mode 100644
index 0000000..9b799ba
--- /dev/null
+++ b/scconfig/src/gui/INIT.h
@@ -0,0 +1 @@
+void deps_gui_init();
diff --git a/scconfig/src/gui/Makefile.plugin b/scconfig/src/gui/Makefile.plugin
new file mode 100644
index 0000000..d8d4fcf
--- /dev/null
+++ b/scconfig/src/gui/Makefile.plugin
@@ -0,0 +1,29 @@
+GUI_CFLAGS = -DPLUGIN_GUI
+GUI_OBJS = \
+  $(BIN)/gui/find_gtk2.o \
+  $(BIN)/gui/find_lesstif2.o \
+  $(BIN)/gui/find_x.o \
+  $(BIN)/gui/find_gd.o \
+  $(BIN)/gui/find_misc.o \
+  $(BIN)/gui/gui.o
+
+
+
+$(BIN)/gui/find_gtk2.o: $(SRC)/gui/find_gtk2.c
+	$(CC) $(CFLAGS) -c $(SRC)/gui/find_gtk2.c -o $(BIN)/gui/find_gtk2.o
+
+$(BIN)/gui/find_lesstif2.o: $(SRC)/gui/find_lesstif2.c
+	$(CC) $(CFLAGS) -c $(SRC)/gui/find_lesstif2.c -o $(BIN)/gui/find_lesstif2.o
+
+$(BIN)/gui/find_x.o: $(SRC)/gui/find_x.c
+	$(CC) $(CFLAGS) -c $(SRC)/gui/find_x.c -o $(BIN)/gui/find_x.o
+
+$(BIN)/gui/find_gd.o: $(SRC)/gui/find_gd.c
+	$(CC) $(CFLAGS) -c $(SRC)/gui/find_gd.c -o $(BIN)/gui/find_gd.o
+
+$(BIN)/gui/find_misc.o: $(SRC)/gui/find_misc.c
+	$(CC) $(CFLAGS) -c $(SRC)/gui/find_misc.c -o $(BIN)/gui/find_misc.o
+
+$(BIN)/gui/gui.o: $(SRC)/gui/gui.c
+	$(CC) $(CFLAGS) -c $(SRC)/gui/gui.c -o $(BIN)/gui/gui.o
+
diff --git a/scconfig/src/gui/find_gd.c b/scconfig/src/gui/find_gd.c
new file mode 100644
index 0000000..ce6735f
--- /dev/null
+++ b/scconfig/src/gui/find_gd.c
@@ -0,0 +1,202 @@
+/*
+    scconfig - gui lib detection - libgd
+    Copyright (C) 2013  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_gd(int logdepth, int fatal, const char *call, const char *arg)
+{
+	const char *test_c =
+		NL "#include \"gd.h\""
+		NL "int main() {"
+		NL "	gdImagePtr imtype;"
+		NL
+		NL "	if (gdImageCreateTrueColor(32, 32) != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	const char *node = "libs/gui/gd";
+	char *cflags = NULL;
+	char *ldflags = NULL;
+	(void) call;  /* not used */
+	(void) arg;  /* not used */
+
+	if (require("cc/cc", logdepth, fatal))
+		return try_fail(logdepth, node);
+
+	report("Checking for gd... ");
+	logprintf(logdepth, "find_gd: running pkg-config...\n");
+	logdepth++;
+
+	if (run_pkg_config(logdepth, "gdlib", &cflags, &ldflags) == 0) {
+		if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) != 0)
+			goto success;
+	}
+
+	if ((run_gen_config(logdepth, "gdlib-config", "", &cflags, &ldflags) == 0) || (run_pkg_config(logdepth, "gdlib", &cflags, &ldflags) == 0)) {
+		char *tmp;
+
+		if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) != 0)
+			goto success;
+
+		/* Some versions of gdlib-config --libs is broken and does not return -lgd */
+		tmp = ldflags;
+		ldflags = str_concat(" ", ldflags, "-lgd", NULL);
+		free(tmp);
+
+		if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) != 0)
+			goto success;
+
+		/* none of the above worked, fail */
+		free(cflags);
+		free(ldflags);
+		return try_fail(logdepth, node);
+	}
+
+	return try_fail(logdepth, node);
+
+	success:;
+	if (cflags != NULL)
+		free(cflags);
+	if (ldflags != NULL)
+		free(ldflags);
+	return 0;
+}
+
+int find_gdimagepng(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include \"gd.h\""
+		NL "int main() {"
+		NL "	gdImagePtr img;"
+		NL
+		NL "	if ((img = gdImageCreateTrueColor(32, 32)) == NULL)"
+		NL "		return 1;"
+		NL "	gdImagePng(img, stderr);"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	const char *node = "libs/gui/gd/gdImagePng";
+	const char *cflags, *ldflags;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/gui/gd/presents", logdepth, fatal))
+		return 1;
+	if (!istrue(get("libs/gui/gd/presents")))
+		return 1;
+
+	cflags = get("libs/gui/gd/cflags");
+	ldflags = get("libs/gui/gd/ldflags");
+
+	report("Checking for gdImagePng... ");
+	logprintf(logdepth, "find_gdimagepng: running pkg-config...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) == 0)
+		return try_fail(logdepth, node);
+
+	return 0;
+}
+
+int find_gdimagejpeg(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include \"gd.h\""
+		NL "int main() {"
+		NL "	gdImagePtr img;"
+		NL
+		NL "	if ((img = gdImageCreateTrueColor(32, 32)) == NULL)"
+		NL "		return 1;"
+		NL "	gdImageJpeg(img, stderr, 0.5);"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	const char *node = "libs/gui/gd/gdImageJpeg";
+	const char *cflags, *ldflags;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/gui/gd/presents", logdepth, fatal))
+		return 1;
+	if (!istrue(get("libs/gui/gd/presents")))
+		return 1;
+
+	cflags = get("libs/gui/gd/cflags");
+	ldflags = get("libs/gui/gd/ldflags");
+
+	report("Checking for gdImageJpeg... ");
+	logprintf(logdepth, "find_gdimagejpeg: running pkg-config...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) == 0)
+		return try_fail(logdepth, node);
+
+	return 0;
+}
+
+int find_gdimagegif(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include \"gd.h\""
+		NL "int main() {"
+		NL "	gdImagePtr img;"
+		NL
+		NL "	if ((img = gdImageCreateTrueColor(32, 32)) == NULL)"
+		NL "		return 1;"
+		NL "	gdImageGif(img, stderr);"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	const char *node = "libs/gui/gd/gdImageGif";
+	const char *cflags, *ldflags;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/gui/gd/presents", logdepth, fatal))
+		return 1;
+	if (!istrue(get("libs/gui/gd/presents")))
+		return 1;
+
+	cflags = get("libs/gui/gd/cflags");
+	ldflags = get("libs/gui/gd/ldflags");
+
+	report("Checking for gdImageGif... ");
+	logprintf(logdepth, "find_gdimagegif: running pkg-config...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) == 0)
+		return try_fail(logdepth, node);
+
+	return 0;
+}
diff --git a/scconfig/src/gui/find_gd.h b/scconfig/src/gui/find_gd.h
new file mode 100644
index 0000000..6a0176a
--- /dev/null
+++ b/scconfig/src/gui/find_gd.h
@@ -0,0 +1,4 @@
+int find_gd(int logdepth, int fatal);
+int find_gdimagepng(int logdepth, int fatal);
+int find_gdimagegif(int logdepth, int fatal);
+int find_gdimagejpeg(int logdepth, int fatal);
diff --git a/scconfig/src/gui/find_gtk2.c b/scconfig/src/gui/find_gtk2.c
new file mode 100644
index 0000000..3e22fc7
--- /dev/null
+++ b/scconfig/src/gui/find_gtk2.c
@@ -0,0 +1,73 @@
+/*
+    scconfig - gui lib detection - gtk2
+    Copyright (C) 2013  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_gtk2(int logdepth, int fatal, const char *call, const char *arg)
+{
+	const char *test_c =
+		NL "#include <gtk/gtk.h>"
+		NL "#include <stdlib.h>"
+		NL
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	gtk_init(&argc, &argv);"
+		NL "	GtkWidget* mainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);"
+		NL "	gtk_window_set_default_size(GTK_WINDOW(mainWindow), 500, 500);"
+		NL "	gtk_window_set_title(GTK_WINDOW(mainWindow), \"foobar\");"
+		NL "	gtk_widget_show_all(mainWindow);"
+		NL "	gtk_main();"
+		NL "	return EXIT_SUCCESS;"
+		NL "}";
+	const char *node = "libs/gui/gtk2";
+	char *cflags;
+	char *ldflags;
+	(void) call;  /* not used */
+	(void) arg;  /* not used */
+
+	if (require("cc/cc", logdepth, fatal))
+		return try_fail(logdepth, node);
+
+	report("Checking for gtk+2... ");
+	logprintf(logdepth, "find_gtk2: running pkg-config...\n");
+	logdepth++;
+
+	if (run_pkg_config(logdepth, "gtk+-2.0", &cflags, &ldflags) != 0) {
+		return try_fail(logdepth, node);
+	}
+
+
+	if (try_icl_norun(logdepth, node, test_c, NULL, cflags, ldflags) == 0) {
+		free(cflags);
+		free(ldflags);
+		return try_fail(logdepth, node);
+	}
+
+	free(cflags);
+	free(ldflags);
+	return 0;
+}
diff --git a/scconfig/src/gui/find_gtk2.h b/scconfig/src/gui/find_gtk2.h
new file mode 100644
index 0000000..334c8e8
--- /dev/null
+++ b/scconfig/src/gui/find_gtk2.h
@@ -0,0 +1,2 @@
+int find_gtk2(int logdepth, int fatal);
+
diff --git a/scconfig/src/gui/find_lesstif2.c b/scconfig/src/gui/find_lesstif2.c
new file mode 100644
index 0000000..89541f1
--- /dev/null
+++ b/scconfig/src/gui/find_lesstif2.c
@@ -0,0 +1,68 @@
+/*
+    scconfig - gui lib detection - lesstif
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_lesstif2(int logdepth, int fatal, const char *call, const char *arg)
+{
+	const char *test_c =
+		NL "#include <Xm/MainW.h>"
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	XtAppContext context;"
+		NL "	Display *dsp;"
+		NL "	Colormap cmap;"
+		NL "	XColor color;"
+		NL "	Widget toplevel;"
+		NL "	toplevel = XtAppInitialize(& context, \"\", NULL, 0, &argc, argv, NULL, NULL, 0);"
+		NL "	XAllocColor(dsp, cmap, &color);"
+		NL "	return toplevel != NULL;"
+		NL "}";
+
+	const char *node = "libs/gui/lesstif2";
+	char **cflags,  *cflags_arr[]  = {"", "-I/opt/X11/include", NULL};
+	char **ldflags, *ldflags_arr[] = {"-lXm -lX11", "-lXm -lXt", "-L/opt/X11/lib -lXm -lXt  -lX11", NULL}; /* note: -lXt must be after -lXm else lesstif fails to init with runtime error */
+	(void) call;  /* not used */
+	(void) arg;  /* not used */
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for lesstif2... ");
+	logprintf(logdepth, "find_lesstif:\n");
+	logdepth++;
+
+	for(cflags = cflags_arr; *cflags != NULL; cflags++) {
+		for(ldflags = ldflags_arr; *ldflags != NULL; ldflags++) {
+			if (try_icl_norun(logdepth, node, test_c, NULL, *cflags, *ldflags) != 0) {
+				return 0;
+			}
+		}
+	}
+	return try_fail(logdepth, node);
+
+}
diff --git a/scconfig/src/gui/find_lesstif2.h b/scconfig/src/gui/find_lesstif2.h
new file mode 100644
index 0000000..68eeafd
--- /dev/null
+++ b/scconfig/src/gui/find_lesstif2.h
@@ -0,0 +1,2 @@
+int find_lesstif2(int logdepth, int fatal);
+
diff --git a/scconfig/src/gui/find_misc.c b/scconfig/src/gui/find_misc.c
new file mode 100644
index 0000000..d62e9e1
--- /dev/null
+++ b/scconfig/src/gui/find_misc.c
@@ -0,0 +1,66 @@
+/*
+    scconfig - gui lib detection - misc libs
+    Copyright (C) 2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_libstroke(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stroke.h>"
+		NL "int main()"
+		NL "{"
+		NL "	char msg[256];"
+		NL "	int n;"
+		NL "	stroke_init();"
+		NL "	for(n = 1000; n < 2000; n+=123)"
+		NL "		stroke_record(n, 1000);"
+		NL "	stroke_trans(msg);"
+		NL "	return !(atoi(msg) == 456);"
+		NL "}";
+
+	const char *node = "libs/gui/libstroke";
+	char **cflags,  *cflags_arr[]  = {"", NULL};
+	char **ldflags, *ldflags_arr[] = {"-lstroke", NULL};
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for libstroke... ");
+	logprintf(logdepth, "find_libstroke:\n");
+	logdepth++;
+
+	for(cflags = cflags_arr; *cflags != NULL; cflags++) {
+		for(ldflags = ldflags_arr; *ldflags != NULL; ldflags++) {
+			if (try_icl_norun(logdepth, node, test_c, NULL, *cflags, *ldflags) != 0) {
+				return 0;
+			}
+		}
+	}
+	return try_fail(logdepth, node);
+}
+
diff --git a/scconfig/src/gui/find_misc.h b/scconfig/src/gui/find_misc.h
new file mode 100644
index 0000000..05d1f11
--- /dev/null
+++ b/scconfig/src/gui/find_misc.h
@@ -0,0 +1 @@
+int find_libstroke(int logdepth, int fatal);
diff --git a/scconfig/src/gui/find_x.c b/scconfig/src/gui/find_x.c
new file mode 100644
index 0000000..e67d914
--- /dev/null
+++ b/scconfig/src/gui/find_x.c
@@ -0,0 +1,96 @@
+/*
+    scconfig - gui lib detection - lesstif
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_xinerama(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <X11/Xlib.h>"
+		NL "#include <stdlib.h>"
+		NL "int main()"
+		NL "{"
+		NL "	Display *d = XOpenDisplay(NULL);"
+		NL "	if (d != NULL)"
+		NL "		XineramaIsActive(d);"
+		NL "	return 0;"
+		NL "}";
+
+	const char *node = "libs/gui/xinerama";
+	char **cflags,  *cflags_arr[]  = {"", "-I/opt/X11/include", NULL};
+	char **ldflags, *ldflags_arr[] = {"-lXinerama", "-lX11 -lXinerama", "-L/opt/X11/lib -lX11 -lXinerama", NULL};
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for Xinerama... ");
+	logprintf(logdepth, "find_xinerama:\n");
+	logdepth++;
+
+	for(cflags = cflags_arr; *cflags != NULL; cflags++) {
+		for(ldflags = ldflags_arr; *ldflags != NULL; ldflags++) {
+			if (try_icl_norun(logdepth, node, test_c, NULL, *cflags, *ldflags) != 0) {
+				return 0;
+			}
+		}
+	}
+	return try_fail(logdepth, node);
+}
+
+int find_xrender(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <X11/Xlib.h>"
+		NL "#include <X11/extensions/Xrender.h>"
+		NL "#include <stdlib.h>"
+		NL "int main()"
+		NL "{"
+		NL "	Display *d = XOpenDisplay(NULL);"
+		NL "	XRenderFreePicture (d, 0);"
+		NL "	return 0;"
+		NL "}";
+
+	const char *node = "libs/gui/xrender";
+	char **cflags,  *cflags_arr[]  = {"", "-I/opt/X11/include",  NULL};
+	char **ldflags, *ldflags_arr[] = {"-lXrender", "-lX11 -lXrender", "-L/opt/X11/lib -lX11 -lXrender", NULL};
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for Xrender... ");
+	logprintf(logdepth, "find_xrender:\n");
+	logdepth++;
+
+	for(cflags = cflags_arr; *cflags != NULL; cflags++) {
+		for(ldflags = ldflags_arr; *ldflags != NULL; ldflags++) {
+			if (try_icl_norun(logdepth, node, test_c, NULL, *cflags, *ldflags) != 0) {
+				return 0;
+			}
+		}
+	}
+	return try_fail(logdepth, node);
+}
diff --git a/scconfig/src/gui/find_x.h b/scconfig/src/gui/find_x.h
new file mode 100644
index 0000000..3e5c525
--- /dev/null
+++ b/scconfig/src/gui/find_x.h
@@ -0,0 +1,3 @@
+int find_xinerama(int logdepth, int fatal);
+int find_xrender(int logdepth, int fatal);
+
diff --git a/scconfig/src/gui/gui.c b/scconfig/src/gui/gui.c
new file mode 100644
index 0000000..d88df40
--- /dev/null
+++ b/scconfig/src/gui/gui.c
@@ -0,0 +1,23 @@
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+#include "find_x.h"
+#include "find_gtk2.h"
+#include "find_lesstif2.h"
+#include "find_gd.h"
+#include "find_misc.h"
+
+void deps_gui_init()
+{
+	dep_add("libs/gui/xinerama/*",          find_xinerama);
+	dep_add("libs/gui/xrender/*",           find_xrender);
+	dep_add("libs/gui/gtk2/*",              find_gtk2);
+	dep_add("libs/gui/lesstif2/*",          find_lesstif2);
+	dep_add("libs/gui/libstroke/*",         find_libstroke);
+	dep_add("libs/gui/gd/gdImagePng/*",     find_gdimagepng);
+	dep_add("libs/gui/gd/gdImageGif/*",     find_gdimagegif);
+	dep_add("libs/gui/gd/gdImageJpeg/*",    find_gdimagejpeg);
+	dep_add("libs/gui/gd/*",                find_gd);
+}
diff --git a/scconfig/src/math/INIT.c b/scconfig/src/math/INIT.c
new file mode 100644
index 0000000..b4f86f1
--- /dev/null
+++ b/scconfig/src/math/INIT.c
@@ -0,0 +1,2 @@
+	deps_math_init();
+
diff --git a/scconfig/src/math/INIT.h b/scconfig/src/math/INIT.h
new file mode 100644
index 0000000..4deb09a
--- /dev/null
+++ b/scconfig/src/math/INIT.h
@@ -0,0 +1 @@
+void deps_math_init();
diff --git a/scconfig/src/math/Makefile.plugin b/scconfig/src/math/Makefile.plugin
new file mode 100644
index 0000000..c50eb14
--- /dev/null
+++ b/scconfig/src/math/Makefile.plugin
@@ -0,0 +1,18 @@
+MATH_CFLAGS = -DPLUGIN_MATH
+MATH_OBJS = \
+  $(BIN)/math/find_math.o \
+  $(BIN)/math/find_fpenan.o \
+  $(BIN)/math/find_func.o \
+  $(BIN)/math/find_mfunc_cc.o
+
+$(BIN)/math/find_math.o: $(SRC)/math/find_math.c
+	$(CC) $(CFLAGS) -c $(SRC)/math/find_math.c -o $(BIN)/math/find_math.o
+
+$(BIN)/math/find_fpenan.o: $(SRC)/math/find_fpenan.c
+	$(CC) $(CFLAGS) -c $(SRC)/math/find_fpenan.c -o $(BIN)/math/find_fpenan.o
+
+$(BIN)/math/find_func.o: $(SRC)/math/find_func.c
+	$(CC) $(CFLAGS) -c $(SRC)/math/find_func.c -o $(BIN)/math/find_func.o
+
+$(BIN)/math/find_mfunc_cc.o: $(SRC)/math/find_mfunc_cc.c
+	$(CC) $(CFLAGS) -c $(SRC)/math/find_mfunc_cc.c -o $(BIN)/math/find_mfunc_cc.o
diff --git a/scconfig/src/math/find_fpenan.c b/scconfig/src/math/find_fpenan.c
new file mode 100644
index 0000000..449b56b
--- /dev/null
+++ b/scconfig/src/math/find_fpenan.c
@@ -0,0 +1,279 @@
+/*
+    scconfig - math feature detection
+    Copyright (C) 2013  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+#include "find_mfunc_cc.h"
+
+#define MATHH "#include <math.h>"
+int find_math_isnan(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	if (!isnan(1.0))"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for isnan... ");
+	logprintf(logdepth, "find_math_isnan: trying to find isnan...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/math/isnan", test_c, MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnan", test_c, MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isnan", test_c, "#define _BSD_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnan", test_c, "#define _BSD_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isnan", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnan", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "+-lm")) return 0;
+
+	return try_fail(logdepth, "libs/math/isnan");
+}
+
+int find_math_isinf(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	if (!isinf(1.0))"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for isinf... ");
+	logprintf(logdepth, "find_math_isinf: try_icling to find isinf...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/math/isinf", test_c, MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isinf", test_c, MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isinf", test_c, "#define _BSD_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isinf", test_c, "#define _BSD_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isinf", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isinf", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "+-lm")) return 0;
+
+	return try_fail(logdepth, "libs/math/isinf");
+}
+
+
+int find_math_isfinite(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	if (isfinite(1.0))"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for isfinite... ");
+	logprintf(logdepth, "find_math_isfinite: try_icling to find isfinite...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/math/isfinite", test_c, MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isfinite", test_c, MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isfinite", test_c, "#define _BSD_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isfinite", test_c, "#define _BSD_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isfinite", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isfinite", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "+-lm")) return 0;
+
+	return try_fail(logdepth, "libs/math/isfinite");
+}
+
+
+int find_math_isnormal(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	if (isnormal(1.0))"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for isnormal... ");
+	logprintf(logdepth, "find_math_isnormal: try_icling to find isnormal...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _BSD_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _BSD_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _ISOC99_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _ISOC99_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _XOPEN_SOURCE 600\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/isnormal", test_c, "#define _XOPEN_SOURCE 600\n" MATHH, "", "+-lm")) return 0;
+
+	return try_fail(logdepth, "libs/math/isnormal");
+}
+
+
+int find_math_nan(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	if (nan(\"foo\") != 0.0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for nan... ");
+	logprintf(logdepth, "find_math_nan: try_icling to find nan...\n");
+	logdepth++;
+
+	if (try_icl(logdepth, "libs/math/nan", test_c, MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/nan", test_c, MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/nan", test_c, "#define _ISOC99_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/nan", test_c, "#define _ISOC99_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, "libs/math/nan", test_c, "#define _XOPEN_SOURCE 600\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, "libs/math/nan", test_c, "#define _XOPEN_SOURCE 600\n" MATHH, "", "+-lm")) return 0;
+
+	return try_fail(logdepth, "libs/math/nan");
+}
+
+
+int find_math_nanop(int logdepth, int fatal)
+{
+	char *test_c_temp =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "%s"
+		NL
+		NL "double s2d(const char *s)"
+		NL "{"
+		NL "	if (strcmp(s, \"nan\") == 0)"
+		NL "		return nan(\"nan\");"
+		NL "	return atof(s);"
+		NL "}"
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	double op1, op2, res;"
+		NL "	op1 = s2d(argv[1]);"
+		NL "	op2 = s2d(argv[3]);"
+
+		NL "	switch(*argv[2]) {"
+		NL "		case '+': res = op1 + op2; break;"
+		NL "		case '-': res = op1 - op2; break;"
+		NL "		case 'M': res = op1 * op2; break;"
+		NL "		case '/': res = op1 / op2; break;"
+		NL "	}"
+		NL "	if (isnan(res))"
+		NL "		printf(\"nan\\n\");"
+		NL "	else"
+		NL "		printf(\"%%f\\n\", res);"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[2048];
+	char *inc, *test_bin, *test_bin_esc, *out, *s, *cmd = test_c;
+	char *tests[] = {
+		"%s 1 + 1",    "2.0",  NULL,
+		"%s nan + 1",  "nan",  "add",
+		"%s nan - 1",  "nan",  "sub",
+		"%s nan M 1",  "nan",  "multiply",
+		"%s nan / 1",  "nan",  "divide",
+		NULL, NULL
+	};
+	char **test;
+	int bad = 0;
+
+	require("libs/math/nan/presents", logdepth, fatal);
+	require("libs/math/isnan/presents", logdepth, fatal);
+	require("cc/cc", logdepth, fatal);
+	if (!istrue(get("libs/math/isnan/presents")) || !istrue(get("libs/math/nan/presents")))
+		return try_fail(logdepth, "libs/math/nanop/presents");
+
+	inc = esc_interpret(get("libs/math/isnan/includes"));
+	sprintf(test_c, test_c_temp, inc);
+	free(inc);
+
+	report("Checking for nanop... ");
+	logprintf(logdepth, "find_math_nan: try_icling to find nan...\n");
+	logdepth++;
+
+	test_bin = NULL;
+	if (compile_code(logdepth, test_c, &test_bin, NULL, get("libs/math/nan/cflags"), get("libs/math/nan/ldflags")) != 0)
+		return try_fail(logdepth, "libs/math/nanop/presents");
+	report("found\n");
+
+	test_bin_esc = shell_escape_dup(test_bin);
+	for(test = tests; *test != NULL; test += 3) {
+		report(test[0], "");
+		report("... ");
+		sprintf(cmd, test[0], test_bin_esc);
+		run(logdepth, cmd, &out);
+		if (test[2] != NULL)
+			sprintf(cmd, "libs/math/nanop/%s", test[2]);
+		if (out != NULL) {
+			if (target_emu_fail(out) || (test[1] == NULL) || (strncmp(out, test[1], strlen(test[1])) == 0)) {
+				report("OK (%s)\n", test[1]);
+				if (test[2] != NULL)
+					put(cmd, test[1]);
+			}
+			else {
+				report("? (");
+				for(s = out; *s != '\0'; s++)
+					if ((*s == '\n') || (*s == '\r'))
+						*s = ' ';
+				report(out);
+				report(")\n");
+				if (test[2] != NULL)
+					put(cmd, out);
+				bad = 1;
+			}
+			free(out);
+		}
+		else
+			bad = 1;
+	}
+
+	if (bad)
+		put("libs/math/nanop/allok", sfalse);
+	else
+		put("libs/math/nanop/allok", strue);
+
+	put("libs/math/nanop/presents", strue);
+
+	unlink(test_bin);
+	free(test_bin);
+	free(test_bin_esc);
+	return 0;
+}
+
diff --git a/scconfig/src/math/find_fpenan.h b/scconfig/src/math/find_fpenan.h
new file mode 100644
index 0000000..472b6b4
--- /dev/null
+++ b/scconfig/src/math/find_fpenan.h
@@ -0,0 +1,7 @@
+int find_math_isnan(int logdepth, int fatal);
+int find_math_isinf(int logdepth, int fatal);
+int find_math_isfinite(int logdepth, int fatal);
+int find_math_isnormal(int logdepth, int fatal);
+int find_math_nan(int logdepth, int fatal);
+int find_math_nanop(int logdepth, int fatal);
+
diff --git a/scconfig/src/math/find_func.c b/scconfig/src/math/find_func.c
new file mode 100644
index 0000000..5a99489
--- /dev/null
+++ b/scconfig/src/math/find_func.c
@@ -0,0 +1,86 @@
+/*
+    scconfig - math feature detection
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+#define MATHH "#include <math.h>"
+
+static int test_mathf(int logdepth, int fatal, const char *fname, const char *cond)
+{
+	char *test_c_template =
+		NL "float one=1.0, zero=0.0;"
+		NL "int main() {"
+		NL "	if (%s)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char test_c[512];
+	char node_name[128];
+
+	sprintf(test_c, test_c_template, cond);
+	sprintf(node_name, "libs/math/%s", fname);
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for %s... ", fname);
+	logprintf(logdepth, "test_mathf: trying to find %s...\n",fname);
+	logdepth++;
+
+
+	if (try_icl(logdepth, node_name, test_c, MATHH, "", "")) return 0;
+	if (try_icl(logdepth, node_name, test_c, MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, node_name, test_c, "#define _BSD_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, node_name, test_c, "#define _BSD_SOURCE\n" MATHH, "", "+-lm")) return 0;
+	if (try_icl(logdepth, node_name, test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "")) return 0;
+	if (try_icl(logdepth, node_name, test_c, "#define _XOPEN_SOURCE\n" MATHH, "", "+-lm")) return 0;
+
+	return try_fail(logdepth, node_name);
+}
+
+int find_math_expf(int logdepth, int fatal)
+{
+	return test_mathf(logdepth, fatal, "expf", "expf(zero) == 1.0");
+}
+
+
+int find_math_logf(int logdepth, int fatal)
+{
+	return test_mathf(logdepth, fatal, "logf", "logf(one) == 0.0");
+}
+
+int find_math_rint(int logdepth, int fatal)
+{
+	return test_mathf(logdepth, fatal, "rint", "rint(4.0) == 4.0");
+}
+
+int find_math_round(int logdepth, int fatal)
+{
+	return test_mathf(logdepth, fatal, "round", "round(3.6) == 4.0");
+}
diff --git a/scconfig/src/math/find_func.h b/scconfig/src/math/find_func.h
new file mode 100644
index 0000000..f1e752e
--- /dev/null
+++ b/scconfig/src/math/find_func.h
@@ -0,0 +1,4 @@
+int find_math_expf(int logdepth, int fatal);
+int find_math_logf(int logdepth, int fatal);
+int find_math_rint(int logdepth, int fatal);
+int find_math_round(int logdepth, int fatal);
diff --git a/scconfig/src/math/find_math.c b/scconfig/src/math/find_math.c
new file mode 100644
index 0000000..b139140
--- /dev/null
+++ b/scconfig/src/math/find_math.c
@@ -0,0 +1,73 @@
+/*
+    scconfig - math feature detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+#include "find_func.h"
+#include "find_mfunc_cc.h"
+#include "find_fpenan.h"
+
+int find_math_minpack(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	int one=1;"
+		NL "	if (dpmpar_(&one) != 0.0)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for minpack... ");
+	logprintf(logdepth, "find_math_minpack: trying to find minpack...\n");
+	logdepth++;
+
+	/* Look at the standard places */
+	if (try_icl(logdepth, "libs/math/minpack", test_c, "#include <minpack.h>", NULL, "+-lminpack")) return 0;
+
+	return try_fail(logdepth, "libs/math/minpack");
+}
+
+
+void deps_math_init()
+{
+	dep_add("libs/math/minpack/*",  find_math_minpack);
+	dep_add("libs/math/cc/log/*",   find_math_cc_log);
+	dep_add("libs/math/isnan/*",    find_math_isnan);
+	dep_add("libs/math/isinf/*",    find_math_isinf);
+	dep_add("libs/math/isfinite/*", find_math_isfinite);
+	dep_add("libs/math/isnormal/*", find_math_isnormal);
+	dep_add("libs/math/nan/*",      find_math_nan);
+	dep_add("libs/math/nanop/*",    find_math_nanop);
+
+	dep_add("libs/math/expf/*",     find_math_expf);
+	dep_add("libs/math/logf/*",     find_math_logf);
+	dep_add("libs/math/rint/*",     find_math_rint);
+	dep_add("libs/math/round/*",    find_math_round);
+}
diff --git a/scconfig/src/math/find_mfunc_cc.c b/scconfig/src/math/find_mfunc_cc.c
new file mode 100644
index 0000000..209bb70
--- /dev/null
+++ b/scconfig/src/math/find_mfunc_cc.c
@@ -0,0 +1,159 @@
+/*
+    scconfig - math func corner case detection
+    Copyright (C) 2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static char *test_c_templ =
+	NL "#include <stdio.h>"
+	NL "#include <math.h>"
+	NL "#include <errno.h>"
+	NL "int main(int argc, char *argv[]) {"
+	NL "	double magic[] = { %s };"
+	NL "	errno = 0;\n"
+	NL "	printf(\"%%f:%%d\\n\", %s(magic[atoi(argv[1])]), errno);"
+	NL "	return 0;"
+	NL "}"
+	NL;
+
+/* returns 0 if couldn't run (emu), -1 on error and 1 when got enough results */
+static int try(int logdepth, const char *func, const char *values, char **res, int num_res)
+{
+	char *test_c, *fn = NULL, *fn_esc, *cmd, *out;
+	int n, len;
+
+	len = strlen(test_c_templ) + strlen(func) + strlen(values) + 1;
+	test_c = malloc(len);
+	sprintf(test_c, test_c_templ, values, func);
+
+	logprintf(logdepth, "testing '%s' on '%s'\n", func, values);
+
+	if (compile_code(logdepth+1, test_c, &fn, NULL, "+", "-lm") != 0) {
+		free(test_c);
+		return -1;
+	}
+	free(test_c);
+
+	if (isblind(db_cwd)) {
+		/* assume corner cases passed as we have nothing better to do */
+		for(n = 0; n < num_res; n++)
+			res[n] = strclone("unknown");
+		unlink(fn);
+		free(fn);
+		return 0;
+	}
+
+	fn_esc = shell_escape_dup(fn);
+	cmd = malloc(strlen(fn_esc) + 32);
+	for(n = 0; n < num_res; n++) {
+		sprintf(cmd, "%s %d", fn_esc, n);
+		if (run(logdepth+1, cmd, &out) == 0) {
+			char *s, *dot = NULL;
+			int is_int = 1;
+			for(s = out; *s != '\0'; s++) {
+				switch(*s) {
+					case '\n':
+					case '\r':
+						*s = '\0';
+						goto at_end;
+					case '.':
+						dot = s;
+						break;
+					default:
+						if ((dot != NULL) && (*s != '0'))
+							is_int = 0;
+						*s = tolower(*s);
+				}
+			}
+			at_end:;
+			if ((is_int) && (dot != NULL))
+				*dot = '\0';
+			if (*out == '+')
+				res[n] = strclone(out+1);
+			else
+				res[n] = strclone(out);
+			free(out);
+		}
+		else
+			res[n] = strclone("fpe");
+	}
+	free(cmd);
+	free(fn_esc);
+	unlink(fn);
+	free(fn);
+	return 1;
+}
+
+
+static int find_math_cc(int logdepth, int fatal, char *func, char *inp, char **node_names, int num_tests)
+{
+	char *res[256];
+	char node_name[256];
+	int n, ret;
+
+	ret = 0;
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for %s() corner cases... ", func);
+	logprintf(logdepth, "find_math_cc_log: Checking for %s() corner cases... \n", func);
+	logdepth++;
+
+	if (try(logdepth, func, inp, res, num_tests) >= 0) {
+		for(n = 0; n < num_tests; n++) {
+			char *sep;
+			report(".");
+			sep = strchr(res[n], ':');
+			if (sep != NULL) {
+				*sep = 0;
+				sep++;
+				sprintf(node_name, "libs/math/cc/%s/%s/return", func, node_names[n]);
+				put(node_name, res[n]);
+				sprintf(node_name, "libs/math/cc/%s/%s/errno", func, node_names[n]);
+				put(node_name, sep);
+			}
+			else
+				ret = 1;
+		}
+	}
+
+	/* avoid redetection */
+	sprintf(node_name, "libs/math/cc/%s/presents", func);
+	put(node_name, strue);
+
+
+	report(ret ? "done with errors\n" : " done.\n");
+	return ret;
+}
+
+int find_math_cc_log(int logdepth, int fatal)
+{
+	char *inp     = "+0.0, -0.0, 1.0, -1.0, 1/0.0";
+	char *nodes[] = {"p_0", "m_0", "p_1", "m_1", "p_inf"};
+	return find_math_cc(logdepth, fatal, "log", inp, nodes, sizeof(nodes) / sizeof(char *));
+}
diff --git a/scconfig/src/math/find_mfunc_cc.h b/scconfig/src/math/find_mfunc_cc.h
new file mode 100644
index 0000000..c611dbb
--- /dev/null
+++ b/scconfig/src/math/find_mfunc_cc.h
@@ -0,0 +1 @@
+int find_math_cc_log(int logdepth, int fatal);
diff --git a/scconfig/src/menulib/Makefile.plugin b/scconfig/src/menulib/Makefile.plugin
new file mode 100644
index 0000000..7740f93
--- /dev/null
+++ b/scconfig/src/menulib/Makefile.plugin
@@ -0,0 +1,14 @@
+MENULIB_CFLAGS = -DPLUGIN_MENULIB
+MENULIB_OBJS = \
+  $(BIN)/menulib/hookreadline.o \
+  $(BIN)/menulib/scmenu.o \
+  $(BIN)/menulib/vt100.o
+
+$(BIN)/menulib/hookreadline.o: $(SRC)/menulib/hookreadline.c $(SRC)/menulib/hookreadline.h $(SRC)/menulib/vt100.h
+	$(CC) $(CFLAGS) -c $(SRC)/menulib/hookreadline.c -o $(BIN)/menulib/hookreadline.o
+
+$(BIN)/menulib/vt100.o: $(SRC)/menulib/vt100.c $(SRC)/menulib/vt100.h
+	$(CC) $(CFLAGS) -c $(SRC)/menulib/vt100.c -o $(BIN)/menulib/vt100.o
+
+$(BIN)/menulib/scmenu.o: $(SRC)/menulib/scmenu.c $(SRC)/menulib/scmenu_menu.c $(SRC)/menulib/scmenu_rl.c $(SRC)/menulib/scmenu_text.c $(SRC)/menulib/scmenu.h
+	$(CC) $(CFLAGS) -c $(SRC)/menulib/scmenu.c -o $(BIN)/menulib/scmenu.o
diff --git a/scconfig/src/menulib/hookreadline.c b/scconfig/src/menulib/hookreadline.c
new file mode 100644
index 0000000..027be2f
--- /dev/null
+++ b/scconfig/src/menulib/hookreadline.c
@@ -0,0 +1,592 @@
+/*
+    scconfig - hook readline lib
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include "hookreadline.h"
+
+#define trprintf(...)
+
+#define hrl_puts(h, s)       h->write(h, s, strlen(s))
+
+#define query_curpos(h) \
+	vt100_where(&h->term)
+
+#define update_curspos(h) \
+	do {\
+		if ((h->term.ccol <= 1) || (h->term.ccol >= h->term.maxcol) || (h->term.crow >= h->term.maxrow)) { \
+			if (!h->certain_cpos) {\
+				trprintf("q1\n"); \
+				query_curpos(h); \
+				h->queried_pos = 1; \
+			} \
+		} \
+	} while(0)
+
+#define insert(h, c, prnt) \
+	do {\
+		h->certain_cpos = 0; \
+		if ((h->used+1) >= h->alloced) { \
+			h->alloced += h->grow; \
+			h->line = realloc(h->line, h->alloced); \
+		} \
+		if (h->cursor != h->used) {\
+			trprintf(" mm %d %d %d\n", h->cursor+1, h->cursor, h->used-(h->cursor)); \
+			memmove(h->line+h->cursor+1, h->line+h->cursor, h->used-(h->cursor)); \
+		} \
+		h->line[h->cursor] = c; \
+		h->used++; \
+		h->line[h->used] = '\0'; \
+		h->cursor++; \
+		if (prnt) {\
+			char cb__ = c; \
+			h->write(h, &cb__, 1); \
+			if  (h->term.ccol == h->term.maxcol) {\
+				h->write(h, "\n", 1); \
+				h->term.ccol = 1; \
+			} \
+		} \
+		if (h->cursor != h->used) {\
+			vt100_csave(&(h->term)); \
+			hrl_puts(h, h->line+h->cursor); \
+			vt100_crestore(&(h->term)); \
+		} \
+		h->term.ccol++; \
+		update_curspos(h); \
+	} while(0)
+
+static void store(hookreadline_t *h, const char *nw)
+{
+	h->used = strlen(nw);
+	if ((h->used+1) >= h->alloced) {
+		h->alloced = h->used+1;
+		h->line = realloc(h->line, h->alloced);
+	}
+	memcpy(h->line, nw, h->used+1);
+	h->cursor = h->used;
+}
+
+
+static void redraw(hookreadline_t *h, int content)
+{
+	int row, col, c;
+
+	if (content) {
+		trprintf("move redraw %d %d\n", h->srow, h->scol);
+		vt100_move(&(h->term), h->srow, h->scol - strlen(h->prompt));
+#warning TODO: make this more efficient
+		hrl_puts(h, h->prompt);
+		if (h->line != NULL)
+			hrl_puts(h, h->line);
+		h->write(h, " ", 1);
+	}
+
+	c = h->promptlen + h->cursor;
+	row = c / h->term.maxcol;
+	col = c % h->term.maxcol;
+	if (row > h->max_row_offs) {
+		h->max_row_offs = row;
+		h->srow--;
+		h->srow_pending_at = row + h->srow;
+		h->certain_cpos = 0;
+		trprintf("q2\n");
+		query_curpos(h);
+	}
+
+/* TODO: this one is required on IRIX but fails on Linux */
+/*	if (row > 0)
+		col++;
+*/
+	trprintf("move redraw2 %d %d\n", row + h->srow, col+1);
+	vt100_move(&(h->term), row + h->srow, col+1);
+
+	h->term.crow = row + h->srow;
+	h->term.ccol = col + 1;
+}
+
+static void del(hookreadline_t *h, int where, int move)
+{
+	int n;
+	const char delchar = 8;
+
+	memmove(h->line+where, h->line+where+1, h->used - where);
+	h->used--;
+	h->cursor += move;
+	if (move < 0) {
+		for(n = -move; n > 0; n--)
+			h->write(h, &delchar, 1);
+		if (h->term.ccol >= 1) {
+			if (h->term.ccol <= 2)
+				query_curpos(h);
+			h->term.ccol--;
+		}
+	}
+	hrl_puts(h, h->line+h->cursor);
+	h->write(h, " ", 1);
+	redraw(h, 0);
+}
+
+void hrl_calibrate(hookreadline_t *h)
+{
+	int ic, ir, c,r,last_col,last_row,chr;
+	int found_row = 0;
+	int found_col = 0;
+
+	if (h->term_enter != NULL)
+		h->term_enter(h);
+	vt100_where(&(h->term));
+
+	h->term.maxcol = h->term.maxrow = 0;
+	last_row = last_col = 1;
+	for(ic = -1, ir = -1;;) {
+		chr = h->readc(h);
+		switch(vt100_interpret(&(h->term), chr)) {
+			case VT100_WHERE:
+/*					trprintf("= curr=%d;%d max=%d;%d\n", h->term.crow, h->term.ccol, h->term.maxrow, h->term.maxcol);*/
+
+					if (found_row) {
+						if (h->term.maxcol <= last_col)
+							found_col = 1;
+					}
+
+					if (h->term.maxrow <= last_row) {
+						found_row = 1;
+						r = 19;
+					}
+
+					if (found_col & found_row) {
+						vt100_move(&(h->term), ir, ic);
+						goto quit;
+					}
+				last_col = h->term.ccol;
+				last_row = h->term.crow;
+				if (ic < 0) {
+					c = ic = h->term.ccol;
+					r = ir = h->term.crow;
+					h->term.maxrow = 0;
+					r++;
+				}
+				else {
+					if (!found_row)
+						r++;
+					else
+						c++;
+				}
+/*				trprintf("MOVE %d;%d\n", r, c);*/
+				vt100_move(&(h->term), r, c);
+/*				trprintf("> %d;%d found: %d %d\n", r, c, found_row, found_col);*/
+				vt100_where(&(h->term));
+				break;
+		}
+	}
+	quit:;
+	if (h->term_leave != NULL)
+		h->term_leave(h);
+}
+
+static int ctrl_chr(hookreadline_t *h, int c)
+{
+	if (h->ctrl_char != NULL)
+		return h->ctrl_char(h, c);
+	return 0;
+}
+
+char *hrl_readline(hookreadline_t *h, hrl_action_t action)
+{
+	int c;
+	char *ret;
+	const char *nw;
+
+	if (h->suspended_row < 0) {
+		if (h->term_enter != NULL)
+			h->term_enter(h);
+		if (h->prompt != NULL) {
+			if ((!h->prompt_printed) || (action == HRL_REDRAW)) {
+				h->promptlen = strlen(h->prompt);
+				h->write(h, h->prompt, h->promptlen);
+				h->prompt_printed = 1;
+			}
+		}
+		else
+			h->promptlen = 0;
+
+		if (!h->queried_pos) {
+			trprintf("q3\n");
+			query_curpos(h);
+			h->queried_pos = 1;
+		}
+		h->scol = -1;
+		h->srow = -1;
+		trprintf("ZERO used\n");
+		if (!h->preloaded) {
+			h->used = 0;
+			h->cursor = 0;
+			h->preloaded = 0;
+		}
+		else {
+			h->write(h, h->line, h->used);
+		}
+		h->redraw = 0;
+		h->max_row_offs = 0;
+		h->srow_pending_at = -1;
+		h->edited = 0;
+		h->certain_cpos = 0;
+	}
+	else {
+		trprintf("move initsus %d %d\n", h->suspended_row, h->suspended_col);
+		if ((h->redraw) || (action == HRL_REDRAW)) {
+			redraw(h, 1);
+			h->redraw = 0;
+		}
+		else {
+			vt100_move(&h->term, h->suspended_row, h->suspended_col);
+			h->suspended_row = -1;
+			h->suspended_col = -1;
+		}
+	}
+
+	if (action == HRL_REDRAW)
+		goto suspend;
+
+	for(;;) {
+		c = h->readc(h);
+		if (c == HRL_NOMORE)
+			goto suspend;
+		if (c > 0) {
+			int vt100c;
+			vt100c = vt100_interpret(&(h->term), c);
+			switch(vt100c) {
+				case VT100_SEQ:
+				case VT100_KEY_ESC:
+					/* ignore these events/keys */
+					break;
+				case VT100_WHERE:
+					h->queried_pos = 0;
+					if (h->ignore_pos) {
+						h->ignore_pos = 0;
+						break;
+					}
+					h->certain_cpos = 1;
+					trprintf("cursor ANSWER: r=%d c=%d curs=%d\n", h->term.crow, h->term.ccol, h->cursor);
+					if (h->scol < 0) {
+						h->scol = h->term.ccol;
+						h->srow = h->term.crow;
+					}
+					if (h->srow_pending_at >= 0) {
+						if (h->term.crow != h->srow_pending_at) {
+							h->srow++;
+							redraw(h, 0);
+						}
+						h->srow_pending_at = -1;
+					}
+					break;
+				case '\n':
+				case '\r':
+					h->cursor = h->used;
+					insert(h, '\0', 0);
+					ret = h->line;
+					goto out;
+				case '\t':
+					if (h->complete != NULL) {
+						char *line;
+						int cursor;
+						line = h->line;
+						cursor = h->cursor;
+						if (h->complete(h, &line, &cursor)) {
+							h->line = line;
+							h->cursor = cursor;
+							h->alloced = h->used = strlen(h->line);
+							redraw(h, 1);
+						}
+					}
+					break;
+				case 4: /* ^D */
+					ret = NULL;
+					goto out;
+				case 12: /* ^L */
+					redraw(h, 1);
+					break;
+				case 8:
+				case 127: /* backspace */
+					if (h->cursor > 0) {
+						del(h, h->cursor-1, -1);
+					}
+					break;
+				case 126: /* del */
+					if (h->cursor < h->used) {
+						del(h, h->cursor, 0);
+					}
+					break;
+				case VT100_KEY_UP:
+					trprintf("hist up\n");
+					nw = h->hist_up(h, h->edited ? h->line : NULL);
+					if (nw != NULL) {
+						h->certain_cpos = 0;
+						store(h, nw);
+						redraw(h, 1);
+					}
+					h->edited = 0;
+					break;
+				case VT100_KEY_DOWN:
+					nw = h->hist_down(h);
+					if (nw != NULL) {
+						h->certain_cpos = 0;
+						store(h, nw);
+						redraw(h, 1);
+					}
+					h->edited = 0;
+					break;
+				case VT100_KEY_LEFT:
+					h->cursor--;
+					if (h->cursor >= 0) {
+						h->term.ccol--;
+						if (h->term.ccol < 1) {
+							h->term.ccol = h->term.maxcol;
+							h->term.crow--;
+						}
+					}
+					else
+						h->cursor = 0;
+					h->certain_cpos = 0;
+					redraw(h, 0);
+					break;
+				case VT100_KEY_RIGHT:
+					h->cursor++;
+					if (h->cursor >= h->used)
+						h->cursor = h->used;
+					h->certain_cpos = 0;
+					redraw(h, 0);
+					update_curspos(h);
+					break;
+/*				case '.': goto suspend;*/
+				default:
+					if (vt100c < 0) {
+						ctrl_chr(h, vt100c);
+					}
+					else if (c < 32) {
+						ctrl_chr(h, c);
+					}
+					else {
+						trprintf("insert '%c' at %d/%d\n", c, h->cursor,h->used);
+						insert(h, c, 1);
+						h->edited++;
+					}
+			}
+		}
+	}
+
+	out:;
+	if (h->term_leave != NULL)
+		h->term_leave(h);
+	h->suspended_row = -1;
+	h->suspended_col = -1;
+	h->prompt_printed = 0;
+	if (h->term_leave != NULL)
+		h->term_leave(h);
+	if (h->queried_pos)
+		h->ignore_pos = 1;
+	trprintf("out\n");
+	return ret;
+
+	suspend:;
+	if (!h->certain_cpos) {
+		trprintf("q4\n");
+		query_curpos(h);
+		h->queried_pos = 1;
+	}
+	if (h->scol > 0)  {
+		trprintf("susp save: %d %d\n", h->term.crow, h->term.ccol);
+		h->suspended_row = h->term.crow;
+		h->suspended_col = h->term.ccol;
+	}
+	return NULL;
+}
+
+void hrl_stdio_enter(hookreadline_t *h)
+{
+	struct termios t;
+
+	if (tcgetattr(0, &t) < 0)
+		return;
+
+	t.c_lflag &= ~ICANON;
+	t.c_lflag &= ~ECHO;
+	t.c_cc[VMIN] = 0;
+	t.c_cc[VTIME] = 10;
+
+	tcsetattr(0, TCSANOW, &t);
+}
+
+void hrl_stdio_leave(hookreadline_t *h)
+{
+	struct termios t;
+
+	if (tcgetattr(0, &t) < 0)
+		return;
+
+	t.c_lflag |= ICANON;
+	t.c_lflag |= ECHO;
+
+	tcsetattr(0, TCSANOW, &t);
+}
+
+typedef struct history_s history_t;
+
+struct history_s {
+	const char *line;
+	history_t *next, *prev;
+};
+
+const char *hrl_hist_up(hookreadline_t *h, const char *current)
+{
+	history_t *hi;
+	int len;
+
+	if (h->historyp == NULL)
+		h->historyp = h->history;
+	else
+		h->historyp = ((history_t *)(h->historyp))->next;
+
+	if (h->historyp == NULL)
+		return NULL;
+
+	if (current != NULL) {
+		len = strlen(current)+1;
+		hi = malloc(sizeof(history_t));
+		hi->line = malloc(len); /* strdup() is not portable */
+		memcpy((char *)hi->line, current, len);
+		hi->next = h->history;
+		hi->prev = NULL;
+		hi->next->prev = h->history = h;
+	}
+	return ((history_t *)(h->historyp))->line;
+}
+
+const char *hrl_hist_down(hookreadline_t *h)
+{
+	if (h->historyp == NULL)
+		return NULL;
+	h->historyp = ((history_t *)(h->historyp))->prev;
+	if (h->historyp == NULL)
+		return NULL;
+	return ((history_t *)(h->historyp))->line;
+}
+
+int hrl_stdio_readc(hookreadline_t *h)
+{
+	unsigned char c;
+	int len;
+	len = read(0, &c, 1);
+	if (len <= 0)
+		return -1;
+	return c;
+}
+
+int hrl_stdio_write(hookreadline_t *h, const char *s, int len)
+{
+	int acc;
+	acc = 0;
+	while(len > 0) {
+		int l;
+		l = write(1, s, len);
+		if (l <= 0)
+			return -1;
+		len -= l;
+		s += l;
+		acc += l;
+	}
+	return acc;
+}
+
+hookreadline_t *hrl_default(hookreadline_t *h)
+{
+	if (h == NULL) {
+		h = malloc(sizeof(hookreadline_t));
+		h->dynamic = 1;
+	}
+	else
+		h->dynamic = 0;
+
+	h->readc = hrl_stdio_readc;
+	h->write = hrl_stdio_write;
+	h->term_enter = hrl_stdio_enter;
+	h->term_leave = hrl_stdio_leave;
+	h->ctrl_char = NULL;
+	h->line = NULL;
+	h->prompt = NULL;
+	h->hist_up   = hrl_hist_up;
+	h->hist_down = hrl_hist_down;
+	h->complete  = NULL;
+	h->suspended_row = -1;
+	h->queried_pos = 0;
+	h->ignore_pos = 0;
+	h->prompt_printed = 0;
+
+	h->history = h->historyp = h->user_data = NULL;
+
+	h->grow = 128;
+
+	h->used = h->alloced = 0;
+	memset(&(h->term), 0, sizeof(h->term));
+	vt100_init(&(h->term), (int (*)(void *, const char *, int))h->write, h);
+	return h;
+}
+
+
+void hrl_destroy(hookreadline_t *h)
+{
+	if (h->line != NULL) {
+		free(h->line);
+		h->line = NULL;
+	}
+	h->used = h->alloced = 0;
+	if (h->prompt != NULL) {
+		free(h->prompt);
+		h->prompt = NULL;
+	}
+}
+
+void hrl_set_line(hookreadline_t *h, const char *line)
+{
+	h->used = strlen(line);
+	if (h->alloced < h->used+1) {
+		free(h->line);
+		h->alloced = h->used + h->grow;
+		h->line = malloc(h->alloced);
+	}
+	memcpy(h->line, line, h->used+1);
+	h->cursor = h->used;
+	h->redraw = 1;
+	h->preloaded = 1;
+}
+
+void hrl_set_prompt(hookreadline_t *h, const char *prompt)
+{
+	int len = strlen(prompt)+1;
+	if (h->prompt != NULL)
+		free(h->prompt);
+	h->prompt = malloc(len);
+	memcpy(h->prompt, prompt, len);
+}
diff --git a/scconfig/src/menulib/hookreadline.h b/scconfig/src/menulib/hookreadline.h
new file mode 100644
index 0000000..eccf4df
--- /dev/null
+++ b/scconfig/src/menulib/hookreadline.h
@@ -0,0 +1,97 @@
+/*
+    scconfig - hook readline lib
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+
+#include "vt100.h"
+
+typedef struct hookreadline_s hookreadline_t;
+
+struct hookreadline_s {
+	char *prompt;     /* must be malloced, will be free'd */
+	int dynamic;      /* free pointer on destroy */
+
+	/* I/O hooks */
+	int (*readc)(hookreadline_t *h);
+	int (*write)(hookreadline_t *h, const char *s, int len);
+	void (*term_enter)(hookreadline_t *h); /* called at enter to turn off line buffering */
+	void (*term_leave)(hookreadline_t *h); /* called at enter to turn off line buffering */
+
+	/* event hooks */
+	const char *(*hist_up)(hookreadline_t *h, const char *current); /* user pressed up arrow; current is line being edited or NULL for subsequent up arrows without editing; return next history line or NULL */
+	const char *(*hist_down)(hookreadline_t *h);                    /* user pressed up arrow */
+	int (*complete)(hookreadline_t *h, char **line, int *cursor);   /* callback for tab-completion; returns non-zero on change in line and cursor (line may be realloc'd) */
+	int (*ctrl_char)(hookreadline_t *h, int c);                     /* control char received; alt+char is -ascii; ctrl+alpha is 1..27 */
+
+
+	/* for the caller to store data */
+	void *history, *historyp;
+	void *user_data;
+
+	/* advanced configuration */
+	int grow;           /* when line needs to grow, how many new bytes to allocate */
+	int redraw;         /* full redraw in the next call */
+
+	/* internal */
+	char *line;          /* line being edited, terminated with \0 */
+	int used, alloced;   /* line length and allocation info */
+	int preloaded;       /* 1 if line should not be cleared when entering htl_readline() */
+	int cursor;          /* cursor index */
+	vt100_state_t term;  /* terminal info */
+	int srow, scol;      /* start row, start col */
+	int promptlen;       /* length of prompt cache */
+	int edited;          /* whether user edited current line already (used for hist_up) */
+	int certain_cpos;    /* are we certain about cursor pos? */
+
+	/* internal: for scroll-hack */
+	int max_row_offs;    /* max row offset seen while moving cursor */
+	int srow_pending_at; /* was at physical row when last hacked start row */
+	int prompt_printed;  /* the prompt has been printed already - matters in non-blocking mode */
+	int queried_pos;     /* guarantee there's only one outstanding cursor pos query at a time to avoid falling out of sync */
+	int ignore_pos;
+
+	/* if >=0: there is an edit in progress that was suspended in the last call; these are the previous cursor coords */
+	int suspended_row, suspended_col;
+};
+
+typedef enum hrl_action_e {
+	HRL_READLINE = 0,                 /* normal readline in blocking or non-blocking mode */
+	HRL_REDRAW,                       /* redraw and return */
+} hrl_action_t;
+
+hookreadline_t *hrl_default(hookreadline_t *h);
+void hrl_destroy(hookreadline_t *h);
+char *hrl_readline(hookreadline_t *h, hrl_action_t action);
+
+void hrl_set_line(hookreadline_t *h, const char *line);
+void hrl_set_prompt(hookreadline_t *h, const char *prompt);
+
+/* measure terminal dimensions */
+void hrl_calibrate(hookreadline_t *h);
+
+/* default hooks */
+void hrl_stdio_enter(hookreadline_t *h);
+void hrl_stdio_leave(hookreadline_t *h);
+
+/* readc() should return HRL_NOMORE in non-blocking mode if it would block */
+#define HRL_NOMORE (-2)
+
+#define hrl_suspended(h)  ((h)->suspended_row >= 0)
diff --git a/scconfig/src/menulib/hrl_test.c b/scconfig/src/menulib/hrl_test.c
new file mode 100644
index 0000000..7cd3078
--- /dev/null
+++ b/scconfig/src/menulib/hrl_test.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <string.h>
+#include "hookreadline.h"
+
+static int dummy_complete(hookreadline_t *h, char **line, int *cursor)
+{
+	char insert[32]; /* large enough for integer in decimal */
+	char *newline;
+	char c;
+	static int ctr = 0;
+
+	newline = malloc(strlen(*line) + 16);
+	c = (*line)[*cursor];
+	(*line)[*cursor] = '\0';
+	sprintf(insert, "%d", ctr++);
+	sprintf(newline, "%s%s%c%s", *line, insert, c, (*line)+*cursor+1);
+	free(*line);
+	(*line) = newline;
+	(*cursor) += strlen(insert);
+	return 1;
+}
+
+int main()
+{
+	hookreadline_t h;
+	char *l;
+	int suspcnt = 0;
+
+	hrl_default(&h);
+	hrl_calibrate(&h);
+	fprintf(stderr, "size row=%d col=%d\n", h.term.maxrow, h.term.maxcol);
+	h.complete = dummy_complete;
+	h.prompt = malloc(16); /* strdup() is not portable */
+	strcpy(h.prompt, "hrl-test> ");
+
+	for(;;) {
+		l = hrl_readline(&h, HRL_READLINE);
+		if (l == NULL) {
+			if (hrl_suspended(&h)) {
+				vt100_move(&h.term, 20,20);
+				printf("susp %d\n", suspcnt++);
+			}
+			else {
+				printf("\nEOF\n");
+				break;
+			}
+		}
+		printf("\nline: '%s'\n", l);
+	}
+	return 0;
+}
diff --git a/scconfig/src/menulib/menu_test.c b/scconfig/src/menulib/menu_test.c
new file mode 100644
index 0000000..e84793b
--- /dev/null
+++ b/scconfig/src/menulib/menu_test.c
@@ -0,0 +1,93 @@
+#include <stdlib.h>
+#include <string.h>
+#include "scmenu.h"
+
+scm_menu_entry_t me_en_dis[] = {
+	{SCM_KEY_VALUE,       NULL, "enable",  "let it be", NULL, NULL,  SCM_RADIO},
+	{SCM_KEY_VALUE,       NULL, "disable", "no way!", NULL, NULL, SCM_RADIO},
+	{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}
+};
+
+scm_menu_entry_t me_list[] = {
+	{SCM_KEY_ONLY,       NULL, "textbox", NULL, NULL, NULL, 0},
+	{SCM_KEY_VALUE,      NULL, "readline", "original text", NULL, NULL, 0},
+	{SCM_KEY_ONLY,       NULL, "radio/check", NULL, NULL, NULL, 0},
+	{SCM_COMBO,          NULL, "auto combo", "<select one>", NULL, me_en_dis, SCM_AUTO_RUN},
+	{SCM_KEY_VALUE_EDIT, NULL, "auto val",   "val4", NULL, NULL, SCM_AUTO_RUN},
+	{SCM_SEPARATOR,      NULL, NULL,   NULL, NULL, NULL, 0},
+	{SCM_KEY_VALUE_EDIT, NULL, "key5", "", NULL, NULL, 0},
+	{SCM_EMPTY,          NULL, NULL,   NULL, NULL, NULL, 0},
+	{SCM_KEY_VALUE_EDIT, NULL, "key6", "sdhfaskjdfasdf", NULL, NULL, 0},
+	{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}
+};
+
+scm_menu_entry_t me_list2[] = {
+	{SCM_KEY_ONLY,       NULL, "c1", NULL, NULL, NULL, SCM_CHECKBOX},
+	{SCM_KEY_ONLY,       NULL, "c2", NULL, NULL, NULL, SCM_CHECKBOX | SCM_CHECKED},
+	{SCM_KEY_ONLY,       NULL, "c3 long text for checking sizes", NULL, NULL, NULL, SCM_CHECKBOX},
+	{SCM_SEPARATOR,      NULL, NULL,   NULL, NULL, NULL, 0},
+	{SCM_KEY_ONLY,       NULL, "r1", NULL, NULL, NULL, SCM_RADIO},
+	{SCM_KEY_ONLY,       NULL, "r2", NULL, NULL, NULL, SCM_RADIO | SCM_CHECKED},
+	{SCM_KEY_ONLY,       NULL, "r3", NULL, NULL, NULL, SCM_RADIO | SCM_CHECKED},
+	{SCM_KEY_ONLY,       NULL, "nothing", NULL, NULL, NULL, 0},
+	{SCM_TERMINATOR,     NULL, NULL, NULL, NULL, NULL, 0}
+};
+
+
+int menu_cb(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *w, int selection)
+{
+	int res;
+	char *ress;
+
+	switch(selection) {
+		case 0:
+			res = scm_popup(ctx, "title", "line 1\nline    2\nwhat\nis\nthis", SCM_YES | SCM_CANCEL);
+			vt100_move(&ctx->hrl.term, 24, 1);
+			printf("res=%d\n", res);
+			return -2;
+		case 1:
+			ress = scm_readline(ctx, "read this line now", "prompt: ", me_list[1].value);
+			if (ress != NULL) {
+				me_list[1].value = ress;
+			}
+			return -2;
+		case 2:
+			{
+				scm_menu_t m2;
+				scm_win_t w2;
+
+				memset(&w2, 0, sizeof(w2));
+				w2.title = "checkbox & radio";
+
+				memset(&m2, 0, sizeof(m2));
+				m2.entries = me_list2;
+				m2.num_entries = -1;
+				m2.cursor = 0;
+
+				scm_menu(ctx, &m2, &w2, NULL);
+			}
+			return -2;
+	}
+	return selection;
+}
+
+int main()
+{
+	scm_ctx_t ctx;
+	scm_win_t w;
+	scm_menu_t m;
+
+	memset(&w, 0, sizeof(w));
+	w.title = "main menu";
+	w.cfg_h = 5;
+
+	memset(&m, 0, sizeof(m));
+	m.entries = me_list;
+	m.num_entries = -1;
+	m.cursor = 0;
+
+	scm_init(&ctx);
+	scm_menu(&ctx, &m, &w, menu_cb);
+
+	return 0;
+}
diff --git a/scconfig/src/menulib/scmenu.c b/scconfig/src/menulib/scmenu.c
new file mode 100644
index 0000000..7304f8d
--- /dev/null
+++ b/scconfig/src/menulib/scmenu.c
@@ -0,0 +1,187 @@
+/*
+    scconfig - simple c menus - windowing
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include "scmenu.h"
+#include "vt100.h"
+
+#define TERM (&ctx->hrl.term)
+
+/* enough spaces to fill in a row on the screen */
+static const char *spaces = "                                                                                                                                                                                                                                                                ";
+static const char *dashes = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
+
+int scm_init(scm_ctx_t *ctx)
+{
+	hrl_default(&ctx->hrl);
+	hrl_calibrate(&ctx->hrl);
+	vt100_clear(&ctx->hrl.term);
+	return 0;
+}
+
+static int draw_box(scm_ctx_t *ctx, scm_coord_t x1, scm_coord_t y1, scm_coord_t w, scm_coord_t h, int top_only)
+{
+	int n;
+	char frame_line[257];
+	char cont_line[257];
+
+	memset(&frame_line[1], '-', w-2);
+	frame_line[0] = '+';
+	frame_line[w-1] = '+';
+
+	vt100_move(TERM, y1, x1);
+	vt100_write(TERM, frame_line, w);
+	if (top_only)
+		return 0;
+	vt100_move(TERM, y1+h, x1);
+	vt100_write(TERM, frame_line, w);
+
+	memset(&cont_line[1], ' ', w-2);
+	cont_line[0] = '|';
+	cont_line[w-1] = '|';
+
+	for(n = y1+1; n < y1+h; n++) {
+		vt100_move(TERM, n, x1);
+		vt100_write(TERM, cont_line, w);
+	}
+
+	return 0;
+}
+
+static int clear_box(scm_ctx_t *ctx, scm_coord_t x1, scm_coord_t y1, scm_coord_t w, scm_coord_t h)
+{
+	int n;
+
+	for(n = y1; n <= y1+h; n++) {
+		vt100_move(TERM, n, x1);
+		vt100_write(TERM, spaces, w);
+	}
+
+	return 0;
+}
+
+static int window_draw(scm_ctx_t *ctx, scm_win_t *win, int title_only)
+{
+	vt100_mode_normal(TERM);
+	draw_box(ctx, win->x1, win->y1, win->w, win->h, win->top_only | title_only);
+	if (win->title != NULL) {
+		int len, x1, sl = 0;
+		char *suffix;
+
+		len = strlen(win->title);
+		if (len > win->w - 4) {
+			len = win->w - 12;
+			if (len < 0)
+				len = 0;
+			suffix=" ...";
+			sl = strlen(suffix);
+		}
+		x1 = win->x1 + (win->w - (len+4+sl)) / 2;
+
+		vt100_move(TERM, win->y1, x1);
+		vt100_write(TERM, "{", 1);
+		if (win->active)
+			vt100_mode_inverse(TERM);
+		else
+			vt100_mode_normal(TERM);
+
+		vt100_write(TERM, " ", 1);
+		vt100_write(TERM, win->title, len);
+		if (sl > 0)
+			vt100_write(TERM, suffix, sl);
+		vt100_write(TERM, " ", 1);
+		if (win->active)
+			vt100_mode_normal(TERM);
+		vt100_write(TERM, "}", 1);
+	}
+	return 0;
+}
+
+static int window_clear(scm_ctx_t *ctx, scm_win_t *win)
+{
+	vt100_mode_normal(TERM);
+	clear_box(ctx, win->x1, win->y1, win->w, win->h);
+	return 0;
+}
+
+static void win_adjust(scm_ctx_t *ctx, scm_win_t *win, int cwidth, int cheight)
+{
+	if (win->title != NULL) {
+		int tlen;
+		tlen = strlen(win->title)+8;
+		if (tlen > cwidth)
+			cwidth = tlen;
+	}
+
+	if (win->cfg_w == 0) {
+		win->w = cwidth+2;
+		if (win->w > TERM->maxcol)
+			win->w = TERM->maxcol;
+	}
+	else
+		win->w = win->cfg_w;
+
+	if (win->cfg_h == 0) {
+		win->h = cheight+2;
+		if (win->h > TERM->maxrow-1)
+			win->h = TERM->maxrow-1;
+	}
+	else
+		win->h = win->cfg_h;
+
+	if (win->cfg_x1 == 0)
+		win->x1 = (TERM->maxcol - win->w) / 2;
+	else
+		win->x1 = win->cfg_x1;
+
+	if (win->cfg_y1 == 0)
+		win->y1 = (TERM->maxrow - win->h) / 2;
+	else
+		win->y1 = win->cfg_y1;
+
+	if (win->x1 < 1)
+		win->x1 = 1;
+	if (win->y1 < 1)
+		win->y1 = 1;
+}
+
+int scm_menu_autowin(scm_ctx_t *ctx, const char *title, scm_menu_entry_t *entries)
+{
+	scm_win_t w;
+	scm_menu_t m;
+
+	memset(&w, 0, sizeof(w));
+	w.title = title;
+
+	memset(&m, 0, sizeof(m));
+	m.entries = entries;
+	m.num_entries = -1;
+	m.cursor = 0;
+
+	scm_menu(ctx, &m, &w, NULL);
+}
+
+
+
+#include "scmenu_menu.c"
+#include "scmenu_rl.c"
+#include "scmenu_text.c"
diff --git a/scconfig/src/menulib/scmenu.h b/scconfig/src/menulib/scmenu.h
new file mode 100644
index 0000000..d6c89f1
--- /dev/null
+++ b/scconfig/src/menulib/scmenu.h
@@ -0,0 +1,138 @@
+/*
+    scconfig - simple c menus
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "hookreadline.h"
+
+typedef unsigned char scm_coord_t;
+
+typedef struct {
+	hookreadline_t hrl;
+} scm_ctx_t;
+
+int scm_init(scm_ctx_t *ctx);
+
+typedef struct {
+	int cfg_x1, cfg_y1, cfg_w, cfg_h; /* desired values */
+	int top_only;
+	const char *title;
+	int active;
+
+	scm_coord_t x1, y1, w, h; /* actual values */
+} scm_win_t;
+
+/**** menu ****/
+typedef enum {
+	SCM_TERMINATOR,
+	SCM_KEY_ONLY,
+	SCM_KEY_VALUE,
+	SCM_KEY_VALUE_EDIT,
+
+	SCM_COMBO,
+	SCM_SUBMENU,
+	SCM_SUBMENU_CB,
+
+	SCM_EMPTY,
+	SCM_SEPARATOR
+} scm_entry_type_t;
+
+typedef enum {
+	SCM_CHANGED = 1,
+	SCM_CHECKED = 2,    /* whether a checkbox is checked */
+	SCM_CHECKBOX = 4,   /* whether the entry has a checkbox */
+	SCM_RADIO    = 8,   /* whether the entry has a radio button */
+	SCM_AUTO_RUN = 16   /* do not return but try to automatically run the menu item clicked */
+} scm_menu_entry_flags_t;
+
+typedef struct {
+	scm_entry_type_t type;
+	char *prefix;
+	char *key;
+	char *value;
+	void *user_data;
+	void *auto_data;
+	int flags; /* bitfield of scm_menu_entry_flags_t */
+} scm_menu_entry_t;
+
+typedef struct scm_menu_s scm_menu_t;
+struct scm_menu_s {
+	scm_menu_entry_t *entries; /* must be terminated by an SCM_TERMINATOR entry */
+	int cursor;    /* cursor in the array */
+	int scroll;    /* scroll offset */
+
+	int num_entries; /* calculated by the lib */
+	int kwidth;      /* calculated max key width */
+	int prev_cursor; /* save of the previous cursor pos for speeding up redraws */
+	int is_select;   /* calculated: has radio or checkbox */
+	int has_radio;   /* calculated: has radio button */
+	int radio_cursor;
+};
+
+/* should return the same as scm_menu, or -2 to reiterate */
+typedef int (*scm_menu_enter_t)(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, int selection);
+
+/* Draw and run a menu. If menu_enter is not NULL, call it when enter
+   is pressed, else return.
+   Return value:
+     - on esc, -1 (window is closed if menu_enter==NULL)
+     - for plain menus, menu_enter==NULL: cursor position
+     - for radio buttons: radio cursor
+*/
+int scm_menu(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, scm_menu_enter_t menu_enter);
+
+int scm_menu_autowin(scm_ctx_t *ctx, const char *title, scm_menu_entry_t *entries);
+
+
+int scm_menu_run(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, scm_menu_enter_t menu_enter);
+
+
+/**** readline ****/
+
+char *scm_readline(scm_ctx_t *ctx, const char *title, const char *prompt, const char *orig);
+
+/**** text and popup ****/
+
+typedef enum {
+	SCM_YES    = 1,
+	SCM_NO     = 2,
+	SCM_OK     = 4,
+	SCM_CANCEL = 8,
+	SCM_QUIT   = 16,
+
+	SCM_BUTTON_BITS = 5    /* number of button bits */
+} scm_button_bits_t;
+
+typedef struct {
+	char **row;
+	int num_rows;  /* must be filled in before the call */
+	int scroll;    /* scroll offset */
+	scm_button_bits_t buttons;
+
+	/* internal */
+	int num_buttons, bcursor, prev_scroll;
+	scm_coord_t button_start;
+} scm_text_t;
+
+scm_button_bits_t scm_text(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *w);
+
+scm_button_bits_t scm_popup(scm_ctx_t *ctx, const char *title, const char *text, scm_button_bits_t buttons);
+
+
diff --git a/scconfig/src/menulib/scmenu_menu.c b/scconfig/src/menulib/scmenu_menu.c
new file mode 100644
index 0000000..c2b3fda
--- /dev/null
+++ b/scconfig/src/menulib/scmenu_menu.c
@@ -0,0 +1,466 @@
+/*
+    scconfig - simple c menus - menu window
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+static void menu_scroll_valid(scm_menu_t *menu, scm_win_t *win)
+{
+	int emin, emax;
+
+	if (menu->cursor < 0)
+		menu->cursor = 0;
+
+	if (menu->cursor >= menu->num_entries)
+		menu->cursor = menu->num_entries-1;
+
+	if (menu->scroll >= menu->num_entries - (win->h-3))
+		menu->scroll = menu->num_entries - win->h;
+	if (menu->scroll < 0)
+		menu->scroll = 0;
+
+	emin = menu->scroll;
+	emax = menu->scroll + (win->h - 3);
+	if (menu->cursor < emin)
+		menu->scroll -= emin - menu->cursor;
+	if (menu->cursor > emax)
+		menu->scroll += menu->cursor - emax;
+
+}
+
+
+static int menu_entry_draw_key(scm_ctx_t *ctx, scm_menu_t *menu, scm_menu_entry_t *entry, int keylen, int silent)
+{
+	if (keylen > menu->kwidth)
+		keylen = menu->kwidth;
+	if (!silent)
+		vt100_write(TERM, entry->key, keylen);
+	if (keylen < menu->kwidth) {
+		if (!silent)
+			vt100_write(TERM, spaces, menu->kwidth - keylen);
+		return menu->kwidth;
+	}
+	return keylen;
+}
+
+static int menu_entry_draw_(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, scm_menu_entry_t *entry, int silent, int *keylen, int ret_padded_len)
+{
+	int *klen, vlen, klen_tmp, plen;
+	if (keylen == NULL)
+		klen = &klen_tmp;
+	else
+		klen = keylen;
+
+	if ((menu->is_select) && (!silent)) {
+		if (entry->flags & SCM_RADIO) {
+			if (entry->flags & SCM_CHECKED)
+				vt100_write(TERM, "(*) ", 4);
+			else
+				vt100_write(TERM, "( ) ", 4);
+		}
+		else if (entry->flags & SCM_CHECKBOX) {
+			if (entry->flags & SCM_CHECKED)
+				vt100_write(TERM, "[*] ", 4);
+			else
+				vt100_write(TERM, "[ ] ", 4);
+		}
+		else {
+			switch(entry->type) {
+				case SCM_TERMINATOR:
+				case SCM_EMPTY:
+				case SCM_SEPARATOR:
+					break;
+				case SCM_KEY_ONLY:
+				case SCM_KEY_VALUE:
+				case SCM_KEY_VALUE_EDIT:
+				case SCM_COMBO:
+				case SCM_SUBMENU:
+				case SCM_SUBMENU_CB:
+					vt100_write(TERM, "    ", 4);
+					break;
+			}
+		}
+	}
+
+	switch(entry->type) {
+		case SCM_TERMINATOR:
+		case SCM_EMPTY:
+			*klen = 0;
+			return 0;
+		case SCM_SEPARATOR:
+			if (!silent)
+				vt100_write(TERM, dashes, win->w-2);
+			return 0;
+		case SCM_KEY_ONLY:
+			*klen = strlen(entry->key);
+			plen = menu_entry_draw_key(ctx, menu, entry, *klen, silent);
+			if (ret_padded_len)
+				*klen = plen;
+			return *klen;
+		case SCM_KEY_VALUE:
+		case SCM_SUBMENU:
+		case SCM_SUBMENU_CB:
+			*klen = strlen(entry->key);
+			vlen = strlen(entry->value);
+			plen = menu_entry_draw_key(ctx, menu, entry, *klen, silent);
+			if (!silent) {
+				vt100_write(TERM, "  ", 2);
+				vt100_write(TERM, entry->value, vlen);
+			}
+			if (ret_padded_len)
+				*klen = plen;
+			return *klen+vlen+2;
+		case SCM_KEY_VALUE_EDIT:
+		case SCM_COMBO:
+			*klen = strlen(entry->key);
+			vlen = strlen(entry->value);
+			plen = menu_entry_draw_key(ctx, menu, entry, *klen, silent);
+			if (!silent) {
+				vt100_write(TERM, " [", 2);
+				vt100_write(TERM, entry->value, vlen);
+				vt100_write(TERM, "]", 1);
+			}
+			if (ret_padded_len)
+				*klen = plen;
+			return *klen+vlen+2+1;
+	}
+	return 0;
+}
+
+static void menu_set_cursor(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win)
+{
+	vt100_move(TERM, menu->cursor - menu->scroll + win->y1+2, win->x1+1+(!!menu->is_select));
+}
+
+static void menu_entry_draw(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, int winrow, scm_menu_entry_t *e)
+{
+	vt100_move(TERM, win->y1+winrow+2, win->x1+1);
+	menu_entry_draw_(ctx, menu, win, e, 0, NULL, 0);
+}
+
+static void menu_draw(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win)
+{
+	int n;
+	scm_menu_entry_t *e;
+
+	menu_scroll_valid(menu, win);
+	window_draw(ctx, win, 0);
+	vt100_mode_normal(TERM);
+	for(n = 0, e = menu->entries + menu->scroll; n < win->h-2 && e->type != SCM_TERMINATOR; n++, e++) {
+		if (menu->scroll+n == menu->cursor)
+			vt100_mode_highlight(TERM);
+		menu_entry_draw(ctx, menu, win, n, e);
+		if (menu->scroll+n == menu->cursor)
+			vt100_mode_normal(TERM);
+	}
+
+	menu_set_cursor(ctx, menu, win);
+}
+
+static void menu_moved(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win)
+{
+	int old_scroll = menu->scroll;
+
+	menu_scroll_valid(menu, win);
+
+	if (old_scroll == menu->scroll) {
+		vt100_mode_normal(TERM);
+		menu_entry_draw(ctx, menu, win, menu->prev_cursor - menu->scroll, &menu->entries[menu->prev_cursor]);
+		vt100_mode_highlight(TERM);
+		menu_entry_draw(ctx, menu, win, menu->cursor - menu->scroll, &menu->entries[menu->cursor]);
+		vt100_mode_normal(TERM);
+	}
+	else
+		menu_draw(ctx, menu, win);
+
+
+	menu_set_cursor(ctx, menu, win);
+}
+
+void scm_menu_move(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, int delta)
+{
+	menu->prev_cursor = menu->cursor;
+	menu->cursor += delta;
+	menu_moved(ctx, menu, win);
+}
+
+void scm_menu_moveto(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, int pos)
+{
+	menu->prev_cursor = menu->cursor;
+	menu->cursor = pos;
+	menu_moved(ctx, menu, win);
+}
+
+static void scm_menu_skip(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, int delta)
+{
+	menu->prev_cursor = menu->cursor;
+	while((menu->entries[menu->cursor].type == SCM_SEPARATOR) || (menu->entries[menu->cursor].type == SCM_EMPTY)) {
+		menu->cursor += delta;
+		if (menu->cursor < 0) {
+			menu->cursor = 0;
+			break;
+		}
+		if (menu->cursor >= menu->num_entries) {
+			menu->cursor = menu->num_entries;
+			break;
+		}
+	}
+	menu_moved(ctx, menu, win);
+}
+
+static void menu_toggle_current(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win)
+{
+	scm_menu_entry_t *e;
+	int redraw = 0;
+
+	e = menu->entries + menu->cursor;
+	if (e->flags & SCM_RADIO) {
+		if (menu->radio_cursor >= 0) {
+			menu->entries[menu->radio_cursor].flags &= ~SCM_CHECKED;
+			redraw = 1;
+		}
+		e->flags |= SCM_CHECKED;
+		menu->radio_cursor = menu->cursor;
+	}
+	else if (e->flags & SCM_CHECKBOX)
+		e->flags ^= SCM_CHECKED;
+
+	if (!redraw) {
+		vt100_mode_highlight(TERM);
+		menu_entry_draw(ctx, menu, win, menu->cursor - menu->scroll, &menu->entries[menu->cursor]);
+		vt100_mode_normal(TERM);
+		menu_set_cursor(ctx, menu, win);
+	}
+	else
+		menu_draw(ctx, menu, win);
+}
+
+static int combo_set(scm_ctx_t *ctx, scm_menu_t *menu, const char *value)
+{
+	int n, c;
+	c = 0;
+	for(n = 0; menu->entries[n].type != SCM_TERMINATOR; n++) {
+		if (strcmp(value, menu->entries[n].key) == 0) {
+			menu->entries[n].flags |= SCM_CHECKED;
+			c = n;
+		}
+		else
+			menu->entries[n].flags &= ~SCM_CHECKED;
+	}
+	return c;
+}
+
+int scm_menu_auto_run(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, int item_idx)
+{
+	scm_menu_entry_t *e = &menu->entries[item_idx];
+	switch(e->type) {
+		case SCM_TERMINATOR:
+		case SCM_EMPTY:
+		case SCM_SEPARATOR:
+			/* do nothing: */
+			return -2;
+		case SCM_KEY_ONLY:
+		case SCM_KEY_VALUE:
+			/* return the selection */
+			break;
+		case SCM_KEY_VALUE_EDIT:
+			/* run a readline on value */
+			{
+				char *str;
+				str = scm_readline(ctx, e->key, "", e->value);
+				if (str != NULL)
+					e->value = str;
+			}
+			return -2;
+		case SCM_COMBO:
+			{
+				scm_win_t cwin;
+				scm_menu_t cmenu;
+				int res;
+
+				memset(&cwin, 0, sizeof(cwin));
+				cwin.title = e->key;
+
+				memset(&cmenu, 0, sizeof(cmenu));
+				cmenu.entries = (scm_menu_entry_t *)e->auto_data;
+				cmenu.num_entries = -1;
+				cmenu.cursor = combo_set(ctx, &cmenu, e->value);
+
+				res = scm_menu(ctx, &cmenu, &cwin, NULL);
+				if (res >= 0)
+					e->value = cmenu.entries[res].key;
+			}
+			return -2;
+		case SCM_SUBMENU_CB:
+			{
+				scm_menu_enter_t cb = (scm_menu_enter_t *)e->auto_data;
+				return cb(ctx, menu, win, item_idx);
+			}
+		case SCM_SUBMENU:
+			scm_menu_autowin(ctx,  e->key, e->auto_data);
+			return -2;
+	}
+	return item_idx;
+}
+
+
+int scm_menu_run(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, scm_menu_enter_t menu_enter)
+{
+	int res = -2;
+
+	if (ctx->hrl.term_enter != NULL)
+		ctx->hrl.term_enter(&ctx->hrl);
+
+	while(res == -2) {
+		int c;
+		c = ctx->hrl.readc(&ctx->hrl);
+		c = vt100_interpret(TERM, c);
+		switch(c) {
+			case VT100_KEY_UP:
+			case 'k':
+				scm_menu_move(ctx, menu, win, -1);
+				scm_menu_skip(ctx, menu, win, -1);
+				break;
+			case VT100_KEY_DOWN:
+			case 'j':
+				scm_menu_move(ctx, menu, win, +1);
+				scm_menu_skip(ctx, menu, win, +1);
+				break;
+			case VT100_KEY_PGUP:
+			case 'u':
+				scm_menu_move(ctx, menu, win, -(win->h-3));
+				scm_menu_skip(ctx, menu, win, -1);
+				break;
+			case VT100_KEY_PGDN:
+			case 'i':
+				scm_menu_move(ctx, menu, win, +(win->h-3));
+				scm_menu_skip(ctx, menu, win, +1);
+				break;
+			case VT100_KEY_HOME:
+				scm_menu_moveto(ctx, menu, win, 0);
+				scm_menu_skip(ctx, menu, win, +1);
+				break;
+			case VT100_KEY_END:
+				scm_menu_moveto(ctx, menu, win, menu->num_entries);
+				scm_menu_skip(ctx, menu, win, -1);
+				break;
+			case ' ':
+				menu_toggle_current(ctx, menu, win);
+				break;
+			case '\r':
+			case '\n':
+				if ((menu_enter != NULL) || (menu->entries[menu->cursor].flags & SCM_AUTO_RUN)) {
+					win->active = 0;
+					window_draw(ctx, win, 1);
+					if (ctx->hrl.term_leave != NULL)
+						ctx->hrl.term_leave(&ctx->hrl);
+					if (menu_enter != NULL)
+						res = menu_enter(ctx, menu, win, menu->cursor);
+					if (menu->entries[menu->cursor].flags & SCM_AUTO_RUN)
+						res = scm_menu_auto_run(ctx, menu, win, menu->cursor);
+					if (res != -2)
+						return res;
+					if (ctx->hrl.term_enter != NULL)
+						ctx->hrl.term_enter(&ctx->hrl);
+					win->active = 1;
+					menu_draw(ctx, menu, win);
+				}
+				else {
+					if (menu->has_radio != -1)
+						res = menu->radio_cursor;
+					else
+						res = menu->cursor;
+				}
+				break;
+			case 27:
+				res = -1;
+				break;
+		}
+	}
+	if (ctx->hrl.term_leave != NULL)
+		ctx->hrl.term_leave(&ctx->hrl);
+
+	return res;
+}
+
+int scm_menu(scm_ctx_t *ctx, scm_menu_t *menu, scm_win_t *win, scm_menu_enter_t menu_enter)
+{
+	scm_win_t autowin;
+	scm_menu_entry_t *e;
+	int cwidth, res, c;
+
+	if (win == NULL) {
+		win = &autowin;
+		memset(&autowin, 0, sizeof(autowin));
+	}
+
+	menu->num_entries = 0;
+	menu->kwidth = 0;
+	menu->is_select = 0;
+	menu->radio_cursor = -1;
+	menu->has_radio = 0;
+	for(e = menu->entries, c = 0; e->type != SCM_TERMINATOR; e++, c++) {
+		int kw;
+		menu_entry_draw_(ctx, menu, win, e, 1, &kw, 0);
+		if (kw > menu->kwidth)
+			menu->kwidth = kw;
+		menu->num_entries++;
+		if (e->flags & (SCM_CHECKBOX | SCM_RADIO))
+			menu->is_select = 1;
+
+		/* keep only the first radio selection and put the radio cursor and cursor there */
+		if (e->flags & SCM_RADIO) {
+			menu->has_radio = 1;
+			if (e->flags & SCM_CHECKED) {
+				if (menu->radio_cursor == -1) {
+					menu->radio_cursor = c;
+					menu->cursor = c;
+				}
+				else
+					e->flags ^= SCM_CHECKED;
+			}
+		}
+	}
+
+	cwidth = 0;
+	for(e = menu->entries; e->type != SCM_TERMINATOR; e++) {
+		int w;
+		w = menu_entry_draw_(ctx, menu, win, e, 1, NULL, 1);
+		if (w > cwidth)
+			cwidth = w;
+	}
+
+	if (menu->is_select)
+		cwidth += 4;
+
+	win->active = 1;
+	win_adjust(ctx, win, cwidth, menu->num_entries);
+	menu_draw(ctx, menu, win);
+
+	res = scm_menu_run(ctx, menu, win, menu_enter);
+
+	if (menu_enter != NULL) {
+		win->active = 0;
+		menu_draw(ctx, menu, win);
+	}
+	else
+		window_clear(ctx, win);
+
+	return res;
+}
diff --git a/scconfig/src/menulib/scmenu_rl.c b/scconfig/src/menulib/scmenu_rl.c
new file mode 100644
index 0000000..d2577ff
--- /dev/null
+++ b/scconfig/src/menulib/scmenu_rl.c
@@ -0,0 +1,47 @@
+/*
+    scconfig - simple c menus - readline window
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+char *scm_readline(scm_ctx_t *ctx, const char *title, const char *prompt, const char *orig)
+{
+	scm_win_t w;
+	char *res;
+
+	memset(&w, 0, sizeof(w));
+	w.title = title;
+	w.h = 5;
+	w.w = TERM->maxcol;
+	w.x1 = 1;
+	w.y1 = TERM->maxrow - w.h;
+	w.top_only = 1;
+	w.active = 1;
+
+	window_draw(ctx, &w, 0);
+	vt100_move(TERM, w.y1+1, 1);
+
+	hrl_set_prompt(&ctx->hrl, prompt);
+	hrl_set_line(&ctx->hrl, orig);
+	res = hrl_readline(&ctx->hrl, HRL_READLINE);
+
+	window_clear(ctx, &w);
+
+	return res;
+}
diff --git a/scconfig/src/menulib/scmenu_text.c b/scconfig/src/menulib/scmenu_text.c
new file mode 100644
index 0000000..6c53391
--- /dev/null
+++ b/scconfig/src/menulib/scmenu_text.c
@@ -0,0 +1,249 @@
+/*
+    scconfig - simple c menus - text window and popup
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+static char *button_text[]   = {"yes", "no", "ok", "cancel", "quit"};
+static int button_text_len[] = {3,     2,    2,    6,        4};
+
+static void text_scroll_valid(scm_text_t *text, scm_win_t *win)
+{
+	if (text->scroll < 0)
+		text->scroll = 0;
+
+	if (text->scroll >= text->num_rows)
+		text->scroll = text->num_rows-1;
+}
+
+static void text_draw_buttons(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win)
+{
+	int n, c, cx, x;
+
+	cx = x = win->x1+text->button_start;
+	vt100_move(TERM, win->y1+win->h-1, x);
+	for(n = 0, c = 0; n < SCM_BUTTON_BITS; n++) {
+		if (text->buttons & (1 << n)) {
+			vt100_write(TERM, " <", 2);
+			if (text->bcursor == c) {
+				vt100_mode_highlight(TERM);
+				cx = x + 2 + button_text_len[n]/2;
+			}
+			vt100_write(TERM, button_text[n], button_text_len[n]);
+			if (text->bcursor == c)
+				vt100_mode_normal(TERM);
+			vt100_write(TERM, "> ", 2);
+			c++;
+			x += 4 + button_text_len[n];
+		}
+	}
+	vt100_move(TERM, win->y1+win->h-1, cx);
+}
+
+static void text_draw(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win)
+{
+	int n;
+	char **r;
+
+	text_scroll_valid(text, win);
+	window_draw(ctx, win, 0);
+	vt100_mode_normal(TERM);
+	for(n = 0, r = text->row + text->scroll; (n < win->h - 4) && (r < text->row + text->num_rows); n++, r++) {
+		vt100_move(TERM, win->y1+n+2, win->x1+1);
+		vt100_write(TERM, *r, strlen(*r));
+	}
+	text_draw_buttons(ctx, text, win);
+}
+
+static void text_moved(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win)
+{
+	text_scroll_valid(text, win);
+
+	if (text->prev_scroll != text->scroll)
+		text_draw(ctx, text, win);
+}
+
+void scm_text_move(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win, int delta)
+{
+	text->prev_scroll = text->scroll;
+	text->scroll += delta;
+	text_moved(ctx, text, win);
+}
+
+void scm_text_moveto(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win, int pos)
+{
+	text->prev_scroll = text->scroll;
+	text->scroll = pos;
+	text_moved(ctx, text, win);
+}
+
+int scm_text_run(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win)
+{
+	int res = -2;
+
+	ctx->hrl.term_enter(&ctx->hrl);
+	while(res == -2) {
+		int c;
+		c = ctx->hrl.readc(&ctx->hrl);
+		c = vt100_interpret(TERM, c);
+		switch(c) {
+			case VT100_KEY_UP:
+			case 'k':
+				scm_text_move(ctx, text, win, -1);
+				break;
+			case VT100_KEY_DOWN:
+			case 'j':
+				scm_text_move(ctx, text, win, +1);
+				break;
+			case VT100_KEY_PGUP:
+			case 'u':
+				scm_text_move(ctx, text, win, -(win->h-3));
+				break;
+			case VT100_KEY_PGDN:
+			case 'i':
+				scm_text_move(ctx, text, win, +(win->h-3));
+				break;
+			case VT100_KEY_HOME:
+				scm_text_moveto(ctx, text, win, 0);
+				break;
+			case VT100_KEY_END:
+				scm_text_moveto(ctx, text, win, text->num_rows);
+				break;
+			case '\t':
+				text->bcursor++;
+				if (text->bcursor >= text->num_buttons)
+					text->bcursor = 0;
+				text_draw_buttons(ctx, text, win);
+				break;
+			case VT100_KEY_RIGHT:
+				text->bcursor++;
+				if (text->bcursor >= text->num_buttons)
+					text->bcursor = text->num_buttons-1;
+				text_draw_buttons(ctx, text, win);
+				break;
+			case VT100_KEY_LEFT:
+				text->bcursor--;
+				if (text->bcursor < 0)
+					text->bcursor = 0;
+				text_draw_buttons(ctx, text, win);
+				break;
+			case '\r':
+			case '\n':
+				{
+					int n, c;
+					for(n = 0, c = 0; n < SCM_BUTTON_BITS; n++) {
+						if (text->buttons & (1 << n)) {
+							if (text->bcursor == c)
+								res = (1 << n);
+							c++;
+						}
+					}
+				}
+				break;
+			case 27:
+				res = -1;
+				break;
+		}
+	}
+	ctx->hrl.term_leave(&ctx->hrl);
+
+	return res;
+}
+
+
+
+scm_button_bits_t scm_text(scm_ctx_t *ctx, scm_text_t *text, scm_win_t *win)
+{
+	int bwidth, cwidth, r;
+	scm_button_bits_t res;
+
+	text->num_buttons = 0;
+	if (text->buttons == 0)
+		text->buttons = SCM_OK;
+
+	bwidth = 0;
+	for(r = 0; r < SCM_BUTTON_BITS; r++) {
+		if (text->buttons & (1 << r)) {
+			bwidth += 4 + button_text_len[r];
+			text->num_buttons++;
+		}
+	}
+
+	cwidth = bwidth;
+	for(r = 0; r < text->num_rows; r++) {
+		int l = strlen(text->row[r]);
+		if (l > cwidth)
+			cwidth = l;
+	}
+
+	win->active = 1;
+	win_adjust(ctx, win, cwidth, text->num_rows + 2);
+	text->button_start = (win->w - bwidth) / 2;
+	text_draw(ctx, text, win);
+
+	res = scm_text_run(ctx, text, win);
+
+	window_clear(ctx, win);
+
+	return res;
+}
+
+scm_button_bits_t scm_popup(scm_ctx_t *ctx, const char *title, const char *text_, scm_button_bits_t buttons)
+{
+	int tlen = strlen(text_), r;
+	char *text, *s, *start;
+	scm_win_t w;
+	scm_text_t t;
+	scm_button_bits_t res;
+
+	text = malloc(tlen+1);
+	memcpy(text, text_, tlen+1);
+
+	memset(&t, 0, sizeof(t));
+
+	t.buttons = buttons;
+	t.num_rows = 1;
+	for(s = text; *s != '\0'; s++) {
+		if (*s == '\n')
+			t.num_rows++;
+		if (*s == '\r')
+			*s = ' ';
+	}
+
+	t.row = malloc(sizeof(char *) * t.num_rows);
+	for(start = s = text, r=0; *s != '\0'; s++) {
+		if (*s == '\n') {
+			*s = '\0';
+			if (start != s) {
+				t.row[r] = start;
+				r++;
+			}
+			start = s+1;
+		}
+	}
+	t.row[r] = start;
+
+	memset(&w, 0, sizeof(w));
+	w.title = title;
+
+	res = scm_text(ctx, &t, &w);
+	free(text);
+	return res;
+}
+
diff --git a/scconfig/src/menulib/vt100.c b/scconfig/src/menulib/vt100.c
new file mode 100644
index 0000000..4c47710
--- /dev/null
+++ b/scconfig/src/menulib/vt100.c
@@ -0,0 +1,189 @@
+/*
+    scconfig - vt100 controls
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "vt100.h"
+
+#define ESC "\x1b"
+
+void vt100_where(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[6n");
+}
+
+void vt100_move(vt100_state_t *state, int row, int col)
+{
+	char s[128];
+	int len;
+	len = sprintf(s, ESC "[%d;%df", row, col);
+	vt100_write(state, s, len);
+}
+
+void vt100_csave(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[s");
+}
+
+void vt100_crestore(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[u");
+}
+
+void vt100_clear(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "c");
+}
+
+void vt100_mode_normal(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[m");
+}
+
+void vt100_mode_inverse(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[7m");
+}
+
+void vt100_mode_highlight(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[1m");
+}
+
+void vt100_mode_blink(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[5m");
+}
+
+void vt100_mode_underline(vt100_state_t *state)
+{
+	vt100_puts(state, ESC "[4m");
+}
+
+static int sequence(vt100_state_t *state, char term)
+{
+	char *end, *next;
+	int i1, i2;
+	switch(term) {
+		case 'R':
+			next = strchr(state->seq, ';');
+			if (next == NULL)
+				return VT100_SEQ;
+			*next = '\0';
+			next++;
+			i1 = strtol(state->seq, &end, 10);
+			if (*end != '\0')
+				return VT100_SEQ;
+			i2 = strtol(next, &end, 10);
+			if (*end != '\0')
+				return VT100_SEQ;
+			state->crow  = i1;
+			state->ccol = i2;
+			if (i1 > state->maxrow)
+				state->maxrow = i1;
+			if (i2 > state->maxcol)
+				state->maxcol = i2;
+			return VT100_WHERE;
+		case 'A': return VT100_KEY_UP;
+		case 'B': return VT100_KEY_DOWN;
+		case 'C': return VT100_KEY_RIGHT;
+		case 'D': return VT100_KEY_LEFT;
+		case '~':
+			switch(state->seq[0]) {
+				case '1': return VT100_KEY_HOME;
+				case '4': return VT100_KEY_END;
+				case '5': return VT100_KEY_PGUP;
+				case '6': return VT100_KEY_PGDN;
+			}
+			break;
+	}
+	return VT100_SEQ;
+}
+
+void d1(){}
+
+int vt100_interpret(vt100_state_t *state, char c_)
+{
+	int c = c_;
+	switch(state->state) {
+		case ST_FREE:
+			switch(c) {
+				case 27:
+					state->state = ST_ESC;
+					return VT100_SEQ;
+				case '\r':
+					return '\n';
+				default:
+					return c;
+			}
+			break;
+		case ST_ESC:
+			switch(c) {
+				case '[':
+					state->state = ST_SEQ;
+					state->seqi = 0;
+					return VT100_SEQ;
+				default: /* unknown sequence, exit */
+					state->state = ST_FREE;
+					if (isalnum(c))
+						return -c;
+					return c;
+			}
+			break;
+		case ST_SEQ:
+			switch(c) {
+				case '0': case '1': case '2': case '3': case '4': case '5':
+				case '6': case '8': case '7': case '9':
+				case ';':
+					/* append */
+					state->seq[state->seqi] = c;
+					state->seqi++;
+					if (state->seqi >= sizeof(state->seq)) {
+						state->state = ST_FREE;
+						return VT100_SEQ;
+					}
+					return VT100_SEQ;
+				default:
+					state->seq[state->seqi] = '\0';
+					state->state = ST_FREE;
+					return sequence(state, c);
+			}
+	}
+	abort();
+	return -1; /* suppress warning */
+}
+
+vt100_state_t *vt100_init(vt100_state_t *state, int (*write)(void *handle, const char *s, int len), void *user_data)
+{
+	if (state == NULL)
+		state = malloc(sizeof(vt100_state_t));
+	state->write = write;
+	state->user_data = user_data;
+	state->state = ST_FREE;
+	state->maxrow = 0;
+	state->maxcol = 0;
+	state->ccol = 0;
+	state->crow = 0;
+	return state;
+}
diff --git a/scconfig/src/menulib/vt100.h b/scconfig/src/menulib/vt100.h
new file mode 100644
index 0000000..a0ebf98
--- /dev/null
+++ b/scconfig/src/menulib/vt100.h
@@ -0,0 +1,82 @@
+/*
+    scconfig - vt100 controls
+    Copyright (C) 2016   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+
+#ifndef VT100_H
+#define VT100_H
+
+#include <stdio.h>
+
+typedef enum vt100_keycodes_e {
+	VT100_SEQ = 256, /* in sequence, need more characters */
+	VT100_WHERE,
+	VT100_KEY_UP,
+	VT100_KEY_DOWN,
+	VT100_KEY_LEFT,
+	VT100_KEY_RIGHT,
+	VT100_KEY_PGUP,
+	VT100_KEY_PGDN,
+	VT100_KEY_HOME,
+	VT100_KEY_END,
+	VT100_KEY_ESC,
+} vt100_keycodes_t;
+
+
+typedef enum vt100_istate_s {
+	ST_FREE, /* not in sequence */
+	ST_ESC,  /* in sequence, read esc */
+	ST_SEQ
+} vt100_istate_t;
+
+typedef struct vt100_state_s {
+	int (*write)(void *ud, const char *s, int len);
+	vt100_istate_t state;
+	int crow, ccol;
+	int maxrow, maxcol;
+	char seq[128];
+	int seqi;
+	void *user_data;
+} vt100_state_t;
+
+#define vt100_puts(state, s)        state->write(state->user_data, s, strlen(s))
+#define vt100_write(state, s, len)  state->write(state->user_data, s, len)
+
+vt100_state_t *vt100_init(vt100_state_t *state, int (*write)(void *handle, const char *s, int len), void *user_data);
+int vt100_interpret(vt100_state_t *state, char c);
+
+void vt100_where(vt100_state_t *state);
+void vt100_move(vt100_state_t *state, int row, int col);
+void vt100_csave(vt100_state_t *state);
+void vt100_crestore(vt100_state_t *state);
+
+void vt100_clear(vt100_state_t *state);
+
+void vt100_mode_normal(vt100_state_t *state);
+void vt100_mode_inverse(vt100_state_t *state);
+void vt100_mode_highlight(vt100_state_t *state);
+void vt100_mode_blink(vt100_state_t *state);
+void vt100_mode_underline(vt100_state_t *state);
+
+#define vt100_keycode_alt(chr)  ((int)chr * -1)
+#define vt100_keycode_ctrl(chr)  (chr - 'a' + 1)
+
+#endif
diff --git a/scconfig/src/parser/INIT.c b/scconfig/src/parser/INIT.c
new file mode 100644
index 0000000..9663e7f
--- /dev/null
+++ b/scconfig/src/parser/INIT.c
@@ -0,0 +1,2 @@
+	deps_parser_init();
+
diff --git a/scconfig/src/parser/INIT.h b/scconfig/src/parser/INIT.h
new file mode 100644
index 0000000..c7d3d74
--- /dev/null
+++ b/scconfig/src/parser/INIT.h
@@ -0,0 +1 @@
+void deps_parser_init();
diff --git a/scconfig/src/parser/Makefile.plugin b/scconfig/src/parser/Makefile.plugin
new file mode 100644
index 0000000..bd6f454
--- /dev/null
+++ b/scconfig/src/parser/Makefile.plugin
@@ -0,0 +1,10 @@
+PARSER_CFLAGS = -DPLUGIN_PARSER
+PARSER_OBJS = \
+  $(BIN)/parser/find_expat.o \
+  $(BIN)/parser/parser.o
+
+$(BIN)/parser/find_expat.o: $(SRC)/parser/find_expat.c
+	$(CC) $(CFLAGS) -c $(SRC)/parser/find_expat.c -o $(BIN)/parser/find_expat.o
+
+$(BIN)/parser/parser.o: $(SRC)/parser/parser.c
+	$(CC) $(CFLAGS) -c $(SRC)/parser/parser.c -o $(BIN)/parser/parser.o
diff --git a/scconfig/src/parser/find_expat.c b/scconfig/src/parser/find_expat.c
new file mode 100644
index 0000000..a28aa88
--- /dev/null
+++ b/scconfig/src/parser/find_expat.c
@@ -0,0 +1,48 @@
+/*
+    scconfig - libexpat detection
+    Copyright (C) 2010  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "parser.h"
+#include <unistd.h>
+
+
+int find_parser_expat(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	void *parser;"
+		NL "	parser = XML_ParserCreate(NULL);"
+		NL "	if (parser != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for expat... ");
+	logprintf(logdepth, "find_parser_expat: trying to find libexpat...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_icl(logdepth, "libs/parser/expat", test_c, "#include <expat.h>", "+", "+-lexpat")) return 0;
+	return try_fail(logdepth, "libs/parser/expat");
+}
diff --git a/scconfig/src/parser/parser.c b/scconfig/src/parser/parser.c
new file mode 100644
index 0000000..c1b6a02
--- /dev/null
+++ b/scconfig/src/parser/parser.c
@@ -0,0 +1,31 @@
+/*
+    scconfig - helper functions for detecting parser libs
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "parser.h"
+#include <assert.h>
+#include "find.h"
+
+void deps_parser_init()
+{
+	dep_add("libs/parser/expat/*",       find_parser_expat);
+}
+
diff --git a/scconfig/src/parser/parser.h b/scconfig/src/parser/parser.h
new file mode 100644
index 0000000..b79f924
--- /dev/null
+++ b/scconfig/src/parser/parser.h
@@ -0,0 +1,10 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+/* parser detection */
+int find_parser_expat(int logdepth, int fatal);
diff --git a/scconfig/src/parsgen/INIT.c b/scconfig/src/parsgen/INIT.c
new file mode 100644
index 0000000..f0f30d6
--- /dev/null
+++ b/scconfig/src/parsgen/INIT.c
@@ -0,0 +1,2 @@
+	deps_parsgen_init();
+
diff --git a/scconfig/src/parsgen/INIT.h b/scconfig/src/parsgen/INIT.h
new file mode 100644
index 0000000..248ce97
--- /dev/null
+++ b/scconfig/src/parsgen/INIT.h
@@ -0,0 +1 @@
+void deps_parsgen_init();
diff --git a/scconfig/src/parsgen/Makefile.plugin b/scconfig/src/parsgen/Makefile.plugin
new file mode 100644
index 0000000..8f2c8bb
--- /dev/null
+++ b/scconfig/src/parsgen/Makefile.plugin
@@ -0,0 +1,6 @@
+PARSGEN_CFLAGS = -DPLUGIN_PARSGEN
+PARSGEN_OBJS = \
+  $(BIN)/parsgen/find_parsgen.o
+
+$(BIN)/parsgen/find_parsgen.o: $(SRC)/parsgen/find_parsgen.c
+	$(CC) $(CFLAGS) -c $(SRC)/parsgen/find_parsgen.c -o $(BIN)/parsgen/find_parsgen.o
diff --git a/scconfig/src/parsgen/find_parsgen.c b/scconfig/src/parsgen/find_parsgen.c
new file mode 100644
index 0000000..d9e28de
--- /dev/null
+++ b/scconfig/src/parsgen/find_parsgen.c
@@ -0,0 +1,139 @@
+/*
+    scconfig - c99 feature detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_parsgen_flex(int logdepth, int fatal)
+{
+	const char *test_flex =
+		NL "%%"
+		NL "foo { return 1; }"
+		NL "%%"
+		NL ;
+	char *out, *temp_in, *temp_in_esc, *cmd;
+	int ret;
+	char *lexfile = "lex.yy.c";
+	(void) fatal;  /* not used */
+
+	report("Checking for flex... ");
+	logprintf(logdepth, "find_flex: trying to find flex...\n");
+	logdepth++;
+
+	if (is_file(lexfile)) {
+		report("ERROR: %s exists, and I don't dare to delete it. Can't text flex, please remove the file by hand.\n", lexfile);
+		logprintf(logdepth, "ERROR: %s exists, and I don't dare to delete it. Can't text flex, please remove the file by hand.\n", lexfile);
+		exit(1);
+	}
+	temp_in = tempfile_dump(test_flex, ".lex");
+	temp_in_esc = shell_escape_dup(temp_in);
+	cmd = malloc(strlen(temp_in_esc) + 16);
+	sprintf(cmd, "flex %s", temp_in_esc);
+	free(temp_in_esc);
+	ret = run(logdepth, cmd, &out);
+	remove(temp_in);
+	free(temp_in);
+	if (out != NULL)
+		free(out);
+
+	if (is_file(lexfile)) {
+		remove(lexfile);
+		if (ret == 0) {
+			put("parsgen/flex", "flex");
+			put("parsgen/flex/presents", strue);
+			report("Found.\n");
+			return 0;
+		}
+	}
+
+	put("parsgen/flex/presents", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+int find_parsgen_bison(int logdepth, int fatal)
+{
+	const char *test_bison =
+		NL "%union { char *str; double num;}"
+		NL "%%"
+		NL "%token <str> TOK1;"
+		NL "%token <num> TOK2;"
+		NL "root: one | two;"
+		NL "one: TOK1;"
+		NL "two: TOK2;"
+		NL ;
+	char *out, *temp_in, *temp_in_esc, *cmd;
+	int ret;
+	char *bisfile, *s;
+	(void) fatal;  /* not used */
+
+	report("Checking for bison... ");
+	logprintf(logdepth, "find_bison: trying to find bison...\n");
+	logdepth++;
+
+	temp_in = tempfile_dump(test_bison, ".y");
+	bisfile = malloc(strlen(temp_in) + 32);
+	strcpy(bisfile, temp_in);
+	s = strchr(bisfile+1, '.');
+	strcpy(s, ".tab.c");
+	if (is_file(bisfile)) {
+		report("ERROR: %s exists, and I don't dare to delete it. Can't text bison, please remove the file by hand.\n", bisfile);
+		logprintf(logdepth, "ERROR: %s exists, and I don't dare to delete it. Can't text bison, please remove the file by hand.\n", bisfile);
+		exit(1);
+	}
+	temp_in_esc = shell_escape_dup(temp_in);
+	cmd = malloc(strlen(temp_in_esc) + 16);
+	sprintf(cmd, "bison %s", temp_in_esc);
+	free(temp_in_esc);
+
+
+	ret = run(logdepth, cmd, &out);
+	remove(temp_in);
+	free(temp_in);
+	if (out != NULL)
+		free(out);
+
+	if (is_file(bisfile)) {
+		remove(bisfile);
+		if (ret == 0) {
+			put("parsgen/bison", "bison");
+			put("parsgen/bison/presents", strue);
+			report("Found.\n");
+			return 0;
+		}
+	}
+
+	put("parsgen/bison/presents", sfalse);
+	report("Not found.\n");
+	return 1;
+}
+
+void deps_parsgen_init()
+{
+	dep_add("parsgen/flex/*",                     find_parsgen_flex);
+	dep_add("parsgen/bison/*",                    find_parsgen_bison);
+}
diff --git a/scconfig/src/scripts/INIT.c b/scconfig/src/scripts/INIT.c
new file mode 100644
index 0000000..f390003
--- /dev/null
+++ b/scconfig/src/scripts/INIT.c
@@ -0,0 +1,2 @@
+	deps_scripts_init();
+
diff --git a/scconfig/src/scripts/INIT.h b/scconfig/src/scripts/INIT.h
new file mode 100644
index 0000000..87c486e
--- /dev/null
+++ b/scconfig/src/scripts/INIT.h
@@ -0,0 +1 @@
+void deps_scripts_init();
diff --git a/scconfig/src/scripts/Makefile.plugin b/scconfig/src/scripts/Makefile.plugin
new file mode 100644
index 0000000..16f2e6e
--- /dev/null
+++ b/scconfig/src/scripts/Makefile.plugin
@@ -0,0 +1,50 @@
+SCRIPT_CFLAGS = -DPLUGIN_SCRIPTS
+SCRIPT_OBJS = \
+  $(BIN)/scripts/scripts.o \
+  $(BIN)/scripts/find_gpmi.o \
+  $(BIN)/scripts/find_tcl.o \
+  $(BIN)/scripts/find_ruby.o \
+  $(BIN)/scripts/find_mruby.o \
+  $(BIN)/scripts/find_python.o \
+  $(BIN)/scripts/find_perl.o \
+  $(BIN)/scripts/find_mawk.o \
+  $(BIN)/scripts/find_lua.o \
+  $(BIN)/scripts/find_guile.o \
+  $(BIN)/scripts/find_stutter.o \
+  $(BIN)/scripts/find_m4.o
+
+$(BIN)/scripts/scripts.o: $(SRC)/scripts/scripts.c
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/scripts.c -o $(BIN)/scripts/scripts.o
+
+$(BIN)/scripts/find_tcl.o: $(SRC)/scripts/find_tcl.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_tcl.c -o $(BIN)/scripts/find_tcl.o
+
+$(BIN)/scripts/find_gpmi.o: $(SRC)/scripts/find_gpmi.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_gpmi.c -o $(BIN)/scripts/find_gpmi.o
+
+$(BIN)/scripts/find_ruby.o: $(SRC)/scripts/find_ruby.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_ruby.c -o $(BIN)/scripts/find_ruby.o
+
+$(BIN)/scripts/find_mruby.o: $(SRC)/scripts/find_mruby.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_mruby.c -o $(BIN)/scripts/find_mruby.o
+
+$(BIN)/scripts/find_python.o: $(SRC)/scripts/find_python.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_python.c -o $(BIN)/scripts/find_python.o
+
+$(BIN)/scripts/find_perl.o: $(SRC)/scripts/find_perl.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_perl.c -o $(BIN)/scripts/find_perl.o
+
+$(BIN)/scripts/find_mawk.o: $(SRC)/scripts/find_mawk.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_mawk.c -o $(BIN)/scripts/find_mawk.o
+
+$(BIN)/scripts/find_lua.o: $(SRC)/scripts/find_lua.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_lua.c -o $(BIN)/scripts/find_lua.o
+
+$(BIN)/scripts/find_guile.o: $(SRC)/scripts/find_guile.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_guile.c -o $(BIN)/scripts/find_guile.o
+
+$(BIN)/scripts/find_stutter.o: $(SRC)/scripts/find_stutter.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_stutter.c -o $(BIN)/scripts/find_stutter.o
+
+$(BIN)/scripts/find_m4.o: $(SRC)/scripts/find_m4.c $(SRC)/scripts/scripts.h
+	$(CC) $(CFLAGS) -c $(SRC)/scripts/find_m4.c -o $(BIN)/scripts/find_m4.o
diff --git a/scconfig/src/scripts/find_gpmi.c b/scconfig/src/scripts/find_gpmi.c
new file mode 100644
index 0000000..5d86a22
--- /dev/null
+++ b/scconfig/src/scripts/find_gpmi.c
@@ -0,0 +1,94 @@
+/*
+    scconfig - libgpmi detection
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+
+int find_script_gpmi(int logdepth, int fatal)
+{
+	char *out, *cmd, *end, **cfg;
+	const char *usr;
+	char *cfgs[] = {
+		NULL,
+		"gpmi-config",
+		"/usr/bin/gpmi-config",
+		"/usr/local/bin/gpmi-config",
+		"~/bin/gpmi-config",
+		NULL
+	};
+
+	(void) fatal;  /* not used */
+
+	report("Checking for gpmi... ");
+	logprintf(logdepth, "find_gpmi: trying to find gpmi...\n");
+	logdepth++;
+
+	/* Try the config script */
+	logprintf(logdepth, "running config tcl\n");
+	cmd = malloc(128);
+	usr = get("/arg/gpmi/prefix");
+	if (usr != NULL) {
+		const char *rp1 = NULL;
+		require("cc/wlrpath", logdepth, 0);
+		rp1 = get("cc/wlrpath");
+		cfgs[0] = str_concat("/", usr, "bin/gpmi-config", NULL);
+		put("libs/script/gpmi/ldflags", str_concat("", "-L", usr, "/lib", " -lgpmi ", rp1, usr, "/lib", NULL));
+		put("libs/script/gpmi/cflags", str_concat("", "-I", usr, "/include", NULL));
+		cfg = cfgs;
+	}
+	else {
+		cfg = cfgs+1;
+		put("libs/script/gpmi/cflags", "");
+		put("libs/script/gpmi/ldflags", "-lgpmi");
+	}
+	for(; *cfg != NULL; cfg++) {
+		sprintf(cmd, "%s --version", *cfg);
+		if (run(logdepth+1, cmd, &out) == 0) {
+			put("libs/script/gpmi/gpmi-config", *cfg);
+			put("libs/script/gpmi/presents", strue);
+			end = strrchr(out, ' ');
+			if (end != NULL)
+				put("libs/script/gpmi/version", strip(end));
+			free(out);
+			sprintf(cmd, "%s --id", *cfg);
+			if (run(logdepth+1, cmd, &out) == 0) {
+				end = strrchr(out, ' ');
+				if (end != NULL)
+					put("libs/script/gpmi/configapi", strip(end));
+				free(out);
+			}
+			free(cmd);
+			if (cfgs[0] != NULL)
+				free(cfgs[0]);
+			return 0;
+		}
+	}
+	free(cmd);
+	if (out != NULL)
+		free(out);
+
+	if (cfg[0] != NULL)
+		free(cfg[0]);
+
+	return try_fail(logdepth, "libs/script/gpmi");
+}
diff --git a/scconfig/src/scripts/find_guile.c b/scconfig/src/scripts/find_guile.c
new file mode 100644
index 0000000..4b2e7bc
--- /dev/null
+++ b/scconfig/src/scripts/find_guile.c
@@ -0,0 +1,74 @@
+/*
+    scconfig - guile lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+
+int find_script_guile(int logdepth, int fatal)
+{
+	char *cflags, *ldflags, *tmpc, *tmpl;
+
+/* temp hack: guile/gh makes sure we have the old, 1.8 version */
+	char *test_c =
+		NL "#include <libguile.h>"
+		NL "#include <guile/gh.h>"
+		NL "int main(int argc, char *argv[]) {"
+		NL "	scm_init_guile();"
+		NL
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for guile... ");
+	logprintf(logdepth, "find_guile: trying to find guile...\n");
+	logdepth++;
+
+	if (run(logdepth, "guile-config compile", &tmpc) + run(logdepth, "guile-config link", &tmpl) > 0) {
+		free(tmpl);
+		free(tmpc);
+		put("libs/script/guile/presents", sfalse);
+		report("FAILED (guile-config failed)\n");
+		return 1;
+	}
+
+	cflags = malloc(strlen(tmpc)+4);
+	sprintf(cflags, "+%s", strip(tmpc));
+	free(tmpc);
+	ldflags = malloc(strlen(tmpl)+4);
+	sprintf(ldflags, "+%s", strip(tmpl));
+	free(tmpl);
+
+	/* TODO: do we need -ldl? */
+	if (try_icl(logdepth, "libs/script/guile", test_c, NULL, cflags, ldflags)) {
+		free(ldflags);
+		free(cflags);
+		return 0;
+	}
+
+	free(ldflags);
+	free(cflags);
+	return try_fail(logdepth, "libs/script/guile");
+}
diff --git a/scconfig/src/scripts/find_lua.c b/scconfig/src/scripts/find_lua.c
new file mode 100644
index 0000000..df8137b
--- /dev/null
+++ b/scconfig/src/scripts/find_lua.c
@@ -0,0 +1,72 @@
+/*
+    scconfig - lua lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+int find_script_lua(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <lua.h>"
+		NL "#include <lauxlib.h>"
+		NL "int main() {"
+		NL "	lua_State *state;"
+		NL "	state = luaL_newstate();"
+		NL "	luaL_loadfile(state, \"nothing\");"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+
+	require("cc/cc", logdepth, fatal);
+	require("/internal/filelist/method", logdepth, fatal);
+
+	report("Checking for lua... ");
+	logprintf(logdepth, "find_lua: trying to find lua...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	/* TODO: do we need -ldl? */
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, NULL, "+-llua -llualib -lm")) return 0;
+
+	/* lualib doesn't exist in lua 5.1.1 */
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, NULL, "+-llua -lm")) return 0;
+
+	/* OS specific include dir */
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "+-I/usr/include", "+-llua -llualib -lm")) return 0;
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "+-I/usr/include/lua5.2", "+-llua5.2 -lm")) return 0;
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "+-I/usr/local/include", "+-llua -llualib -lm")) return 0;
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "+-I/usr/include", "+-llua -lm")) return 0;
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "+-I/usr/local/include", "+-llua -lm")) return 0;
+
+	/* This one is for OSX (by Bjarni) */
+	if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "+-I/sw/include", "+-llua -llualib -lm")) return 0;
+
+	/* no luck - try to find by brute force, listing directory names */
+	if (brute_force_include(logdepth, "lua", test_c, NULL, "/usr/include") ||
+	    brute_force_include(logdepth, "lua", test_c, NULL, "/usr/local/include")) {
+	    return 0;
+	}
+
+	return try_fail(logdepth, "libs/script/lua");
+}
diff --git a/scconfig/src/scripts/find_m4.c b/scconfig/src/scripts/find_m4.c
new file mode 100644
index 0000000..fdeebdb
--- /dev/null
+++ b/scconfig/src/scripts/find_m4.c
@@ -0,0 +1,89 @@
+/*
+    scconfig - m4 binary detection
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+static int test_m4(int logdepth, int fatal, char *bin)
+{
+	char *script =
+		NL "define(baz, foo)"
+		NL "baz bar"
+		NL;
+	char *out, *cmd, *tmpf, *tmpf_esc;
+	(void) fatal;  /* not used */
+
+	tmpf = tempfile_dump(script, ".m4");
+	tmpf_esc = shell_escape_dup(tmpf);
+	cmd = str_concat(" ", bin, tmpf_esc, NULL);
+	free(tmpf_esc);
+
+	run(logdepth, cmd, &out);
+	if (out != NULL) {
+		char *s = out;
+
+		while((*s == ' ') || (*s == '\r') || (*s == '\n'))
+			s++;
+
+		if (strncmp(s, "foo bar", 7) == 0) {
+			unlink(tmpf);
+			free(tmpf);
+			free(out);
+			report("found (%s)\n", bin);
+			logprintf(logdepth, "found (%s)", bin);
+			put("libs/script/m4/bin/presents", strue);
+			put("libs/script/m4/bin/path", bin);
+			return 1;
+		}
+
+		free(out);
+	}
+
+	unlink(tmpf);
+	free(tmpf);
+	free(cmd);
+	return 0;
+}
+
+int find_script_m4(int logdepth, int fatal)
+{
+	char *m4_paths[] = {
+		"/usr/bin/m4",
+		"/usr/local/bin/m4",
+		"/bin/m4",
+		"/usr/bin/gm4",
+		"/usr/local/bin/gm4",
+		"/bin/gm4",
+		NULL
+	};
+	char **s;
+
+	report("Checking for m4 binary... ");
+	logprintf(logdepth, "find_m4: trying to find m4 binary...\n");
+	logdepth++;
+
+	for(s = m4_paths; *s != NULL; s++)
+		if (test_m4(logdepth, fatal, *s))
+			return 0;
+
+	return try_fail(logdepth, "libs/script/m4/bin");
+}
diff --git a/scconfig/src/scripts/find_mawk.c b/scconfig/src/scripts/find_mawk.c
new file mode 100644
index 0000000..50ddecd
--- /dev/null
+++ b/scconfig/src/scripts/find_mawk.c
@@ -0,0 +1,55 @@
+/*
+    scconfig - mawk lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+
+int find_script_mawk(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <stdlib.h>"
+		NL "#include <libmawk.h>"
+		NL "int main() {"
+		NL "	int argc = 1;"
+		NL "	char *argv[] = {"
+		NL "		\"mawk\","
+		NL "		NULL"
+		NL "	};"
+		NL "	libmawk_initialize(argc, argv);"
+		NL "  puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for mawk... ");
+	logprintf(logdepth, "find_mawk: trying to find mawk...\n");
+	logdepth++;
+
+	/* Look at the standard place */
+	if (try_icl(logdepth, "libs/script/mawk", test_c, NULL, "+", "+-lmawk")) return 0;
+
+	return try_fail(logdepth, "libs/script/mawk");
+}
diff --git a/scconfig/src/scripts/find_mruby.c b/scconfig/src/scripts/find_mruby.c
new file mode 100644
index 0000000..702ae2b
--- /dev/null
+++ b/scconfig/src/scripts/find_mruby.c
@@ -0,0 +1,51 @@
+/*
+    scconfig - ruby lib detection
+    Copyright (C) 2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+
+int find_script_mruby(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <mruby.h>"
+		NL "int main() {"
+		NL "	mrb_state *ctx = mrb_open();"
+		NL "	mrb_close(ctx);"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for mruby... ");
+	logprintf(logdepth, "find_mruby: trying to find mruby...\n");
+	logdepth++;
+
+	if (try_icl_pkg_config(logdepth, "libs/script/mruby", test_c, NULL, "mruby-*", get("/arg/mruby-version")))
+		return 0;
+
+	/* Look at the most standard place */
+	if (try_icl(logdepth, "libs/script/mruby", test_c, NULL, NULL, "+-lmruby -lm"))
+		return 0;
+
+	return try_fail(logdepth, "libs/script/ruby");
+}
diff --git a/scconfig/src/scripts/find_perl.c b/scconfig/src/scripts/find_perl.c
new file mode 100644
index 0000000..8e1a7bf
--- /dev/null
+++ b/scconfig/src/scripts/find_perl.c
@@ -0,0 +1,84 @@
+/*
+    scconfig - perl lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+
+int find_script_perl(int logdepth, int fatal)
+{
+	char *cflags, *ldflags, *s;
+	int res;
+	char *test_c =
+		NL "#include <EXTERN.h>"
+		NL "#include <perl.h>"
+		NL "int main() {"
+		NL "	PerlInterpreter *interp;"
+		NL
+		NL "	interp = perl_alloc();"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("sys/class", logdepth, fatal);
+	require("cc/cc", logdepth, fatal);
+	require("/internal/filelist/method", logdepth, fatal);
+
+	report("Checking for perl... ");
+	logprintf(logdepth, "find_perl: trying to find perl...\n");
+	logdepth++;
+
+	res = run(logdepth, "perl -MExtUtils::Embed -e ccopts", &cflags);
+	if (res) {
+		logprintf(logdepth, "perl executable not found or broken (%d) at cflags\n", res);
+		report("FAILED (perl exec fail)\n");
+		return 1;
+	}
+
+	res = run(logdepth, "perl -MExtUtils::Embed -e ldopts", &ldflags);
+	if (res) {
+		logprintf(logdepth, "perl executable not found or broken (%d) aat ldflags\n", res);
+		report("FAILED (perl exec fail)\n");
+		free(cflags);
+		return 1;
+	}
+
+	/* workarounds for windows [TODO] */
+	if (strcmp(get("sys/class"), "win32") == 0) {
+		for(s = cflags; *s != '\0'; s++)
+			if (*s == '\\') *s = '/';
+
+		for(s = ldflags; *s != '\0'; s++)
+			if (*s == '\\') *s = '/';
+
+		/* TODO: do we need to remove double quotes as well? */
+	}
+
+	res = try_icl(logdepth, "libs/script/perl", test_c, NULL, strip(cflags), strip(ldflags));
+
+	free(cflags);
+	free(ldflags);
+
+	if (res)
+		return 0;
+
+	return try_fail(logdepth, "libs/script/perl");
+}
diff --git a/scconfig/src/scripts/find_python.c b/scconfig/src/scripts/find_python.c
new file mode 100644
index 0000000..e29f86f
--- /dev/null
+++ b/scconfig/src/scripts/find_python.c
@@ -0,0 +1,95 @@
+/*
+    scconfig - python lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+
+int find_script_python(int logdepth, int fatal)
+{
+	char *ldflags_base, *cflags, *ldflags;
+	int iswin32, res;
+	char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <Python.h>"
+		NL "int main() {"
+		NL "  char *s;"
+		NL "	Py_Initialize();"
+		NL
+		NL "	s = PY_VERSION;"
+		NL "	if (s[0] != '2') return 1;"
+		NL "	if ((s[2] >= '3') && (s[2] <= '9')) puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_py =
+		NL "import distutils.sysconfig;"
+		NL "print '+-I' + distutils.sysconfig.get_python_inc().replace('\\\\','/')"
+		NL;
+	char *lib_py =
+		NL "import distutils.sysconfig;"
+		NL "print '+-L' + distutils.sysconfig.PREFIX.replace('\\\\','/') + '/libs',;"
+		NL "import sys;"
+		NL "print '-lpython' + str(sys.version_info[0]) + str(sys.version_info[1])"
+		NL;
+
+
+	require("sys/class", logdepth, fatal);
+	iswin32 = strcmp(get("sys/class"), "win32") == 0;
+	if (iswin32)
+		require("libs/lpthread", logdepth, fatal);
+	require("cc/cc", logdepth, fatal);
+	require("/internal/filelist/method", logdepth, fatal);
+
+	report("Checking for python... ");
+	logprintf(logdepth, "find_python: trying to find python...\n");
+	logdepth++;
+
+	if (iswin32)
+		ldflags_base = strclone(get("libs/lpthread"));
+	else
+		ldflags_base = strclone("-L/usr/bin -L/usr/local/bin");
+
+	/* Look at some standard places */
+	if (try_icl(logdepth, "libs/script/python", test_c, NULL, NULL, "+-lpython")) return 0;
+
+	/* Ask python using the python executables on path; use + so both runs and can free out of both */
+	if (run_script(logdepth, "python", inc_py, ".py", &cflags) + run_script(logdepth, "python", lib_py, ".py", &ldflags) == 0)
+		res = try_icl(logdepth, "libs/script/python", test_c, NULL, strip(cflags), strip(ldflags));
+	else
+		res = 0;
+
+	free(cflags);
+	free(ldflags);
+	if (res)
+		return 0;
+
+	/* no luck - try to find by brute force, listing directory names */
+	if (brute_force_include(logdepth, "python", test_c, ldflags_base, "/usr/include") ||
+	    brute_force_include(logdepth, "python", test_c, ldflags_base, "/usr/local/include")) {
+	    free(ldflags_base);
+	    return 0;
+	}
+
+	free(ldflags_base);
+
+	return try_fail(logdepth, "libs/script/python");
+}
diff --git a/scconfig/src/scripts/find_ruby.c b/scconfig/src/scripts/find_ruby.c
new file mode 100644
index 0000000..112695f
--- /dev/null
+++ b/scconfig/src/scripts/find_ruby.c
@@ -0,0 +1,124 @@
+/*
+    scconfig - ruby lib detection
+    Copyright (C) 2009..2015  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+
+static int brute_force(int logdepth, const char *test_c, const char *basedir)
+{
+	char **files, **ifiles, *tmp, *ldflags;
+	int fileno, n, m, ifileno, res;
+
+	res = 0;
+	filelist(logdepth, basedir, &fileno, &files);
+	if (fileno <= 0)
+		return res;
+
+	for(n = 0; (n < fileno) && !res; n++) {
+		tmp = malloc(strlen(basedir) + strlen(files[n]) + 4);
+		sprintf(tmp, "%s/%s", basedir, files[n]);
+		filelist(logdepth+1, tmp, &ifileno, &ifiles);
+		free(tmp);
+		for(m = 0; (m < ifileno) && !res; m++) {
+			tmp = malloc(strlen(basedir) + strlen(files[n]) + strlen(ifiles[m]) + 16);
+			sprintf(tmp, "%s/%s/%s/ruby.h", basedir, files[n], ifiles[m]);
+
+			if (is_file(tmp)) {
+				sprintf(tmp, "+-I%s/%s/%s", basedir, files[n], ifiles[m]);
+				ldflags = malloc(strlen(files[n]) + 16);
+				sprintf(ldflags, "+-lruby%s", files[n]);
+				res = try_icl(logdepth, "libs/script/ruby", test_c, NULL, tmp, ldflags);
+				free(ldflags);
+			}
+			free(tmp);
+		}
+		filelist_free(&ifileno, &ifiles);
+	}
+	filelist_free(&fileno, &files);
+
+	return res;
+}
+
+int find_script_ruby(int logdepth, int fatal)
+{
+	int require18;
+	const char *require18s;
+
+	char *test_c =
+		NL "#include <ruby.h>"
+		NL "int main() {"
+		NL "	ruby_init();"
+		NL "	ruby_finalize();"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *test_c18 =
+		NL "#include <ruby.h>"
+		NL "#include <env.h>"
+		NL "int main() {"
+		NL "	ruby_init();"
+		NL "	ruby_frame->orig_func;"
+		NL "	ruby_finalize();"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for ruby... ");
+	logprintf(logdepth, "find_ruby: trying to find ruby...\n");
+	logdepth++;
+
+	require18s = get("libs/script/ruby/require_18");
+	if (require18s != NULL)
+		require18 = istrue(require18s);
+	else
+		require18 = 0;
+
+	if ((!require18) && (try_icl_pkg_config(logdepth, "libs/script/ruby", test_c, NULL, "ruby-*", get("/arg/ruby-version"))))
+		return 0;
+
+	if (try_icl_pkg_config(logdepth, "libs/script/ruby", test_c18, NULL, "ruby-*", get("/arg/ruby-version")))
+		return 0;
+
+	/* Look at some standard places */
+	if ((!require18) && (try_icl(logdepth, "libs/script/ruby", test_c, NULL, NULL, "+-lruby")))
+		return 0;
+	if (try_icl(logdepth, "libs/script/ruby", test_c18, NULL, NULL, "+-lruby"))
+		return 0;
+
+	require("/internal/filelist/method", logdepth, fatal);
+
+	/* no luck - try to find by brute force, listing directory names */
+	if ((!require18) && (brute_force(logdepth, test_c, "/usr/lib/ruby") ||
+	    brute_force(logdepth, test_c, "/usr/local/lib/ruby"))) {
+	    return 0;
+	}
+	if (brute_force(logdepth, test_c18, "/usr/lib/ruby") ||
+	    brute_force(logdepth, test_c18, "/usr/local/lib/ruby")) {
+	    return 0;
+	}
+
+	return try_fail(logdepth, "libs/script/ruby");
+}
diff --git a/scconfig/src/scripts/find_stutter.c b/scconfig/src/scripts/find_stutter.c
new file mode 100644
index 0000000..605ceb3
--- /dev/null
+++ b/scconfig/src/scripts/find_stutter.c
@@ -0,0 +1,55 @@
+/*
+    scconfig - stutter lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+
+int find_script_stutter(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stutter.h>"
+		NL "int main() {"
+		NL "	varctx *global;"
+		NL "	global = varctx_create(NULL, 256);"
+		NL
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for stutter... ");
+	logprintf(logdepth, "find_stutter: trying to find stutter...\n");
+	logdepth++;
+
+	/* TODO: do we need -ldl? */
+	if (
+			try_icl(logdepth, "libs/script/stutter", test_c, NULL, NULL, "+-lstutter") ||
+			try_icl(logdepth, "libs/script/stutter", test_c, NULL, "-I/usr/include/stutter", "+-lstutter") ||
+			try_icl(logdepth, "libs/script/stutter", test_c, NULL, "-I/usr/local/include/stutter", "+-lstutter")
+			)
+		return 0;
+
+	return try_fail(logdepth, "libs/script/stutter");
+}
diff --git a/scconfig/src/scripts/find_tcl.c b/scconfig/src/scripts/find_tcl.c
new file mode 100644
index 0000000..9b1a141
--- /dev/null
+++ b/scconfig/src/scripts/find_tcl.c
@@ -0,0 +1,132 @@
+/*
+    scconfig - tcl lib detection
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <unistd.h>
+
+
+int find_script_tcl(int logdepth, int fatal)
+{
+	char *out, *temp, *temp2, *cmd, *I, *L, *end, **tclsh;
+
+	char *tclshs[] = {
+		"tclsh",
+		"tclsh85",
+		"tclsh84",
+		"tclsh8.5",
+		"tclsh8.4",
+		NULL
+	};
+
+	char *test_c =
+		NL "#include <tcl.h>"
+		NL "int main() {"
+		NL "	Tcl_Obj *res;"
+		NL "	Tcl_Interp *interp;"
+		NL "	interp = Tcl_CreateInterp();"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *tcl_config =
+			NL "proc tclConfigFile {} {"
+			NL "	set d [info library]"
+			NL "	set f [file join $d \"tclConfig.sh\"]"
+			NL "	if {[file exists $f]} {return $f}"
+			NL ""
+			NL "	set d [file dirname $d]"
+			NL "	set f [file join $d \"tclConfig.sh\"]"
+			NL "	if {[file exists $f]} {return $f}"
+			NL ""
+			NL "	set d [file dirname $d]"
+			NL "	set f [file join $d \"tclConfig.sh\"]"
+			NL "	if {[file exists $f]} {return $f}"
+			NL ""
+			NL "	set d [file dirname $d]"
+			NL "	set f [file join $d \"tclConfig.sh\"]"
+			NL "	if {[file exists $f]} {return $f}"
+			NL "}"
+			NL ""
+			NL "puts [tclConfigFile]"
+			NL;
+
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for tcl... ");
+	logprintf(logdepth, "find_tcl: trying to find tcl...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, "+", "+-ltcl")) return 0;
+	if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, "+", "+-ltcl84")) return 0;
+	if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, "+", "+-ltcl8.4")) return 0;
+	if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, "+-I/usr/include/tcl84", "+-ltcl84")) return 0;
+	if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, "+-I/usr/include/tcl8.4", "+-ltcl8.4")) return 0;
+
+	/* Try the config script */
+	logprintf(logdepth, "running config tcl\n");
+	temp = tempfile_dump(tcl_config, ".tcl");
+	cmd = malloc(strlen(temp) + 16);
+	for(tclsh = tclshs; *tclsh != NULL; tclsh++) {
+		sprintf(cmd, "%s %s", *tclsh, temp);
+		report("Trying: %s\n", cmd);
+		if (run(logdepth+1, cmd, &out) == 0) {
+			free(cmd);
+			cmd = malloc(strlen(out) + 256);
+			sprintf(cmd, "#!/bin/sh\n. %s\necho +$TCL_INCLUDE_SPEC\necho +$TCL_LIB_SPEC\n", out);
+			temp2 = tempfile_dump(cmd, ".sh");
+			free(out);
+			out = malloc(strlen(temp2) + 32);
+			sprintf(out, "chmod +x %s", temp2);
+			system(out);
+			free(out);
+			if (run(logdepth+1, temp2, &out) == 0) {
+				remove(temp2);
+				I = out;
+				L = strchr(I, '\n');
+				if (L != NULL) {
+					*L = '\0';
+					L++;
+					end = strchr(L, '\n');
+					if (end != NULL)
+						*end = '\0';
+				}
+				if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, I, L)) {
+					remove(temp);
+					free(cmd);
+					if (out != NULL)
+						free(out);
+					return 0;
+				}
+			}
+			remove(temp2);
+		}
+	}
+	remove(temp);
+	free(cmd);
+	if (out != NULL)
+		free(out);
+
+	return try_fail(logdepth, "libs/script/tcl");
+}
diff --git a/scconfig/src/scripts/scripts.c b/scconfig/src/scripts/scripts.c
new file mode 100644
index 0000000..27d4602
--- /dev/null
+++ b/scconfig/src/scripts/scripts.c
@@ -0,0 +1,80 @@
+/*
+    scconfig - helper functions for detecting script libs
+    Copyright (C) 2009  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "scripts.h"
+#include <assert.h>
+#include "find.h"
+
+void deps_scripts_init()
+{
+	dep_add("libs/script/gpmi/*",           find_script_gpmi);
+	dep_add("libs/script/tcl/*",            find_script_tcl);
+	dep_add("libs/script/ruby/*",           find_script_ruby);
+	dep_add("libs/script/mruby/*",          find_script_mruby);
+	dep_add("libs/script/python/*",         find_script_python);
+	dep_add("libs/script/perl/*",           find_script_perl);
+	dep_add("libs/script/mawk/*",           find_script_mawk);
+	dep_add("libs/script/lua/*",            find_script_lua);
+	dep_add("libs/script/guile/*",          find_script_guile);
+	dep_add("libs/script/stutter/*",        find_script_stutter);
+	dep_add("libs/script/m4/bin/*",         find_script_m4);
+}
+
+int brute_force_include(int logdepth, const char *language, const char *test_c, const char *ldflags_base, const char *basedir)
+{
+	char **files, *cflags, *ldflags;
+	char nodename[1024], deflink[sizeof(nodename)];
+	int fileno, n, res;
+	size_t llen;
+
+	if (ldflags_base == NULL)
+		ldflags_base = "";
+
+	llen = strlen(language);
+	assert(llen < sizeof(nodename) - 16);
+	sprintf(nodename, "libs/script/%s", language);
+	sprintf(deflink, "-l%s", language);
+
+	res = 0;
+	filelist(logdepth, basedir, &fileno, &files);
+	if (fileno > 0) {
+		for(n = 0; (n < fileno) && !res; n++) {
+			if (strncmp(files[n], language, llen) == 0) {
+				ldflags = malloc(strlen(files[n]) + strlen(ldflags_base) + 16);
+				sprintf(ldflags, "+%s -l%s", ldflags_base, files[n]);
+				cflags = malloc(strlen(files[n]) + strlen(basedir) + 16);
+				sprintf(cflags, "+-I%s/%s", basedir, files[n]);
+				if (try_icl(logdepth, nodename, test_c, NULL, cflags, ldflags) || try_icl(logdepth, nodename, test_c, NULL, cflags, deflink)) {
+					filelist_free(&fileno, &files);
+					free(cflags);
+					free(ldflags);
+					return 1;
+				}
+				free(cflags);
+				free(ldflags);
+			}
+		}
+		filelist_free(&fileno, &files);
+	}
+
+	return res;
+}
diff --git a/scconfig/src/scripts/scripts.h b/scconfig/src/scripts/scripts.h
new file mode 100644
index 0000000..bd8ca2e
--- /dev/null
+++ b/scconfig/src/scripts/scripts.h
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int brute_force_include(int logdepth, const char *language, const char *test_c, const char *ldflags_base, const char *basedir);
+
+
+/* script detection */
+int find_script_gpmi(int logdepth, int fatal);
+int find_script_tcl(int logdepth, int fatal);
+int find_script_ruby(int logdepth, int fatal);
+int find_script_mruby(int logdepth, int fatal);
+int find_script_python(int logdepth, int fatal);
+int find_script_perl(int logdepth, int fatal);
+int find_script_mawk(int logdepth, int fatal);
+int find_script_lua(int logdepth, int fatal);
+int find_script_guile(int logdepth, int fatal);
+int find_script_stutter(int logdepth, int fatal);
+int find_script_m4(int logdepth, int fatal);
diff --git a/scconfig/src/socket/INIT.c b/scconfig/src/socket/INIT.c
new file mode 100644
index 0000000..d2550a9
--- /dev/null
+++ b/scconfig/src/socket/INIT.c
@@ -0,0 +1,2 @@
+	deps_socket_init();
+
diff --git a/scconfig/src/socket/INIT.h b/scconfig/src/socket/INIT.h
new file mode 100644
index 0000000..e8852bf
--- /dev/null
+++ b/scconfig/src/socket/INIT.h
@@ -0,0 +1 @@
+void deps_socket_init();
diff --git a/scconfig/src/socket/Makefile.plugin b/scconfig/src/socket/Makefile.plugin
new file mode 100644
index 0000000..c88154c
--- /dev/null
+++ b/scconfig/src/socket/Makefile.plugin
@@ -0,0 +1,19 @@
+SOCKET_CFLAGS = -DPLUGIN_SOCKET
+SOCKET_OBJS = \
+  $(BIN)/socket/socket.o \
+  $(BIN)/socket/find_select.o \
+  $(BIN)/socket/find_dns.o \
+  $(BIN)/socket/find_socket.o
+
+
+$(BIN)/socket/find_socket.o: $(SRC)/socket/find_socket.c
+	$(CC) $(CFLAGS) -c $(SRC)/socket/find_socket.c -o $(BIN)/socket/find_socket.o
+
+$(BIN)/socket/socket.o: $(SRC)/socket/socket.c
+	$(CC) $(CFLAGS) -c $(SRC)/socket/socket.c -o $(BIN)/socket/socket.o
+
+$(BIN)/socket/find_select.o: $(SRC)/socket/find_select.c
+	$(CC) $(CFLAGS) -c $(SRC)/socket/find_select.c -o $(BIN)/socket/find_select.o
+
+$(BIN)/socket/find_dns.o: $(SRC)/socket/find_dns.c
+	$(CC) $(CFLAGS) -c $(SRC)/socket/find_dns.c -o $(BIN)/socket/find_dns.o
diff --git a/scconfig/src/socket/find_dns.c b/scconfig/src/socket/find_dns.c
new file mode 100644
index 0000000..3a40b40
--- /dev/null
+++ b/scconfig/src/socket/find_dns.c
@@ -0,0 +1,134 @@
+/*
+    scconfig - dns API detection
+    Copyright (C) 2010..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "socket.h"
+
+int find_socket_getaddrinfo(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <string.h>"
+		NL "int main() {"
+		NL "	struct addrinfo req, *ans;"
+		NL "	memset(&req, 0, sizeof(req));"
+		NL "	req.ai_flags    = 0;"
+		NL "	req.ai_family   = PF_INET;"
+		NL "	req.ai_socktype = SOCK_STREAM;"
+		NL "	req.ai_protocol = IPPROTO_TCP;"
+		NL "	SOCK_INIT;"
+		NL "	if (getaddrinfo(\"127.0.0.1\", NULL, &req, &ans) != 0)"
+		NL "		return 1;"
+		NL "	freeaddrinfo(ans);"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_netdb =
+		NL "#include <netdb.h>"
+		NL;
+
+	char *inc_netdb_in =
+		NL "#include <netinet/in.h>"
+		NL "#include <netdb.h>"
+		NL;
+
+	char *inc_win32 = "!"
+		NL "#define _WIN32_WINNT 0x0501"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for getaddrinfo... ");
+	logprintf(logdepth, "find_socket_getaddrinfo: trying to find getaddrinfo()...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/getaddrinfo", inc_netdb, test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/getaddrinfo", inc_netdb_in, test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/getaddrinfo", inc_win32, test_c, "+", "+", 1)) return 0;
+	return 1;
+}
+
+int find_socket_getnameinfo(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <string.h>"
+		NL "int main() {"
+		NL "	struct sockaddr_in sa;"
+		NL "	char *buf;"
+		NL ""
+		NL "	memset(&sa, 0, sizeof(sa));"
+		NL "#ifdef SA_LEN"
+		NL "		sa.SA_LEN        = sizeof(sa);"
+		NL "#endif"
+		NL "	sa.sin_family      = AF_INET;"
+		NL "	sa.sin_addr.s_addr = 0x0100007f;"
+		NL ""
+		NL "	buf = malloc(1024);"
+		NL "	SOCK_INIT;"
+		NL "	if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), buf, 1024, NULL, 0, 0))"
+		NL "		return 1;"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_linux = NL "#include <netdb.h>";
+
+	char *inc_bsd =
+		NL "#include <netdb.h>"
+		NL "#include <netinet/in.h>";
+
+	char *def_bsd = "#define SA_LEN sin_len";
+
+	char *inc_win32 = "!"
+		NL "#define _WIN32_WINNT 0x0501"
+		NL "#include <ws2tcpip.h>"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for getnameinfo... ");
+	logprintf(logdepth, "find_socket_getnameinfo: trying to find getnameinfo()...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/getnameinfo", inc_linux, test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/getnameinfo", inc_bsd,   test_c, "+", "+", 1)) return 0;
+	if (try_socket_def(logdepth, "libs/socket/getnameinfo", inc_bsd, def_bsd, test_c, "+", "+", 1)) {
+		put("libs/socket/getnameinfo/sa_len", "sin_len");
+		return 0;
+	}
+	if (try_socket(logdepth, "libs/socket/getnameinfo", inc_win32, test_c, "+", "+ -lws2_32", 1)) return 0;
+	return 1;
+}
+
diff --git a/scconfig/src/socket/find_select.c b/scconfig/src/socket/find_select.c
new file mode 100644
index 0000000..c3b0258
--- /dev/null
+++ b/scconfig/src/socket/find_select.c
@@ -0,0 +1,105 @@
+/*
+    scconfig - socket API detection
+    Copyright (C) 2010  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "socket.h"
+
+int find_socket_select(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "int main() {"
+		NL "	struct timeval tv;"
+		NL "  tv.tv_sec = 0;"
+		NL "  tv.tv_usec = 1;"
+		NL "	SOCK_INIT;"
+		NL "	select(0, NULL, NULL, NULL, &tv);"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_linux =
+		NL "#include <sys/select.h>"
+		NL;
+
+	char *inc_old =
+		NL "#include <sys/time.h>"
+		NL "#include <sys/types.h>"
+		NL "#include <unistd.h>"
+		NL;
+
+	char *inc_win = NL "#include <winsock2.h>" NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for select... ");
+	logprintf(logdepth, "find_socket_select: trying to find select()...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/select", inc_linux, test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/select", inc_old, test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/select", inc_win, test_c, "+", "+-lws2_32", 1)) return 0;
+
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_poll(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "#include <stdlib.h>"
+		NL "int main() {"
+		NL "	struct pollfd fds[1];"
+		NL "	fds[0].fd = 0;"
+		NL "	fds[0].events = POLLIN;"
+		NL "	SOCK_INIT;"
+		NL "	if (poll(fds, 1, 1) == 0)"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_linux =
+		NL "#include <poll.h>"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for poll... ");
+	logprintf(logdepth, "find_socket_poll: trying to find poll()...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/poll", inc_linux, test_c, "+", "+", 1)) return 0;
+
+	report("not found\n");
+	return 1;
+}
diff --git a/scconfig/src/socket/find_socket.c b/scconfig/src/socket/find_socket.c
new file mode 100644
index 0000000..c822cb3
--- /dev/null
+++ b/scconfig/src/socket/find_socket.c
@@ -0,0 +1,744 @@
+/*
+    scconfig - socket API detection
+    Copyright (C) 2010..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "socket.h"
+
+int find_socket_socket(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	int s;"
+		NL "	SOCK_INIT;"
+		NL "	s = socket(AF_INET, SOCK_STREAM, 0);"
+		NL "	if (s >= 0)"
+		NL "		puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_linux =
+		NL "#include <sys/types.h>"
+		NL "#include <sys/socket.h>"
+		NL;
+
+	char *inc_windows =
+		NL "#include <winsock2.h>"
+		NL "#include <ws2tcpip.h>"
+		NL "#include <windows.h>"
+		NL;
+
+	char *def_empty =
+		NL "#define SOCK_INIT"
+		NL "#define SOCK_UNINIT"
+		NL;
+
+#define DEF_WINDOWS_INIT "do { WSADATA w; WSAStartup(MAKEWORD(2,2), &w); } while(0)"
+#define DEF_WINDOWS_UNINIT "WSACleanup()"
+	char *def_windows =
+		NL "#define SOCK_INIT " DEF_WINDOWS_INIT
+		NL "#define SOCK_UNINIT " DEF_WINDOWS_UNINIT
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for socket... ");
+	logprintf(logdepth, "find_socket_socket: trying to find socket()...\n");
+	logdepth++;
+
+	/* normally init code is empty */
+	put("libs/socket/socket/init_code",   "");
+	put("libs/socket/socket/uninit_code", "");
+
+	/* Look at some standard places */
+	/* on most UNIX systems */
+	if (try_socket_pure(logdepth, "libs/socket/socket", inc_linux, def_empty, test_c, "+", "+", 1)) return 0;
+
+	/* this combination seems to be present on sysV forks */
+	if (try_socket_pure(logdepth, "libs/socket/socket", inc_linux, def_empty, test_c, "+", "+-lsocket -lnsl", 1)) return 0;
+
+	/* Windows has its own socket library (try with WSA init) */
+	if (try_socket_pure(logdepth, "libs/socket/socket", inc_windows, def_windows, test_c, "+", "+-lws2_32", 1)) {
+		put("libs/socket/socket/init_code",   DEF_WINDOWS_INIT);
+		put("libs/socket/socket/uninit_code", DEF_WINDOWS_UNINIT);
+		return 0;
+	}
+
+	/* final fallback: it may be that a sysV-like system has either -lsocket or -lnsl but not both */
+	if (try_socket_pure(logdepth, "libs/socket/socket", inc_linux, def_empty, test_c, "+", "+-lsocket", 1)) return 0;
+	if (try_socket_pure(logdepth, "libs/socket/socket", inc_linux, def_empty, test_c, "+", "+-lnsl", 1)) return 0;
+
+	put("libs/socket/socket/presents", sfalse);
+	report("not found\n");
+
+	return 1;
+}
+
+int find_socket_wsasocket(int logdepth, int fatal)
+{
+	char *test_c =
+		NL "int main() {"
+		NL "	int s;"
+		NL "	SOCK_INIT;"
+		NL "	s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);"
+		NL "	if (s >= 0)"
+		NL "		puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	char *inc_windows =
+		NL "#include <windows.h>"
+		NL "#include <winsock2.h>"
+		NL "#include <ws2tcpip.h>"
+		NL;
+
+	require("cc/cc", logdepth, fatal);
+
+	report("Checking for WSASocket... ");
+	logprintf(logdepth, "find_socket_wsasocket: trying to find socket()...\n");
+	logdepth++;
+
+	/* WSASocket should be specific to win32 socket lib */
+	if (try_socket(logdepth, "libs/socket/wsasocket", inc_windows, test_c, "+", "+-lws2_32", 1)) return 0;
+
+	put("libs/socket/wsasocket/presents", sfalse);
+	report("not found\n");
+
+	return 1;
+}
+
+char *test_c_socketpair =
+		NL "#include <stdio.h>"
+		NL "#include <unistd.h>"
+		NL "int main()"
+		NL "{"
+		NL "	int fd[2];"
+		NL "	int ret, len;"
+		NL "	char s[16];"
+		NL ""
+		NL "	SOCK_INIT;"
+		NL "	ret = socketpair(%s, %s, 0, fd);"
+		NL "	if (ret != 0)"
+		NL "		return 1;"
+		NL "	write(fd[0], \"test1\\n\", 5);"
+		NL "	len = read(fd[1], s, sizeof(s));"
+		NL "	if (len > 0)"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+
+int find_socket_socketpair(int logdepth, int fatal)
+{
+	char *test_c;
+	const char **family;
+	const char *families[] = {"AF_LOCAL", "PF_UNIX", NULL};
+
+	char *inc_linux =
+		NL "#include <sys/types.h>"
+		NL "#include <sys/socket.h>"
+		NL;
+
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for socketpair()... ");
+	logprintf(logdepth, "find_socket_socketpair: trying to find socketpair()...\n");
+	logdepth++;
+
+	test_c = malloc(strlen(test_c_socketpair)+32);
+	for(family = families; *family != NULL; family++) {
+		sprintf(test_c, test_c_socketpair,  *family, "SOCK_STREAM");
+
+		/* Look at some standard places */
+		if (try_socket(logdepth, "libs/socket/socketpair", inc_linux, test_c, "+", "+", 1)) {
+			put("libs/socket/socketpair/af_local", *family);
+			free(test_c);
+			return 0;
+		}
+	}
+	free(test_c);
+
+	put("libs/socket/socketpair/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_types(int logdepth, int fatal)
+{
+	char node[256];
+	struct {
+		char *domain;
+		char *type;
+	} *t, type_combos[] = {
+		{"AF_UNIX",  "SOCK_STREAM"},
+		{"AF_UNIX",  "SOCK_DGRAM"},
+		{"AF_UNIX",  "SOCK_SEQPACKET"},
+		{"AF_INET",  "SOCK_STREAM"},
+		{"AF_INET",  "SOCK_DGRAM"},
+		{"AF_INET",  "SOCK_SEQPACKET"},
+		{"AF_INET6", "SOCK_STREAM"},
+		{"AF_INET6", "SOCK_DGRAM"},
+		{"AF_INET6", "SOCK_SEQPACKET"},
+		{NULL, NULL}
+	};
+	char *test_c_socket =
+		NL "int main() {"
+		NL "	SOCK_INIT;"
+		NL "	if (socket(%s, %s, 0) >= 0) {"
+		NL "		puts(\"OK\");"
+		NL "		return 0;"
+		NL "	}"
+		NL "	SOCK_UNINIT;"
+		NL "	return 1;"
+		NL "}";
+	char *test_c;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for socket domain/type combinations... ");
+
+	test_c = malloc(strlen(test_c_socket)+32);
+	for(t = type_combos; t->domain != NULL; t++) {
+		sprintf(test_c, test_c_socket, t->domain, t->type);
+		sprintf(node, "libs/socket/types/%s/%s", t->domain, t->type);
+		try_socket(logdepth, node, "", test_c, "+", "+", 0);
+	}
+	report("done\n");
+
+	free(test_c);
+	put("libs/socket/types", strue);
+	return 0;
+}
+
+
+int find_socket_ioctlsocket(int logdepth, int fatal)
+{
+	char test_c_temp[] =
+		NL "int main() {"
+		NL "	int s;"
+		NL "	%s nonblocking;"
+		NL "	SOCK_INIT;"
+		NL "	s = socket(AF_INET, SOCK_STREAM, 0);"
+		NL "	ioctl%s(s, 0, &nonblocking);"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[sizeof(test_c_temp)+64];
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+
+	report("Checking for ioctlsocket... ");
+	logprintf(logdepth, "find_socket_ioctlsocket: trying to find ioctlsocket()...\n");
+	logdepth++;
+
+	sprintf(test_c, test_c_temp, "int", "");
+	if (try_socket(logdepth, "libs/socket/ioctl", "", test_c, "+", "+", 0)) {
+		put("libs/socket/ioctl/ioctl", "ioctl");
+		put("libs/socket/ioctl/presents", strue);
+		put("libs/socket/ioctl/argtype", "int");
+		put("libs/socket/ioctlsocket/presents", sfalse);
+		report("OK (ioctl, arg type int)\n");
+		return 0;
+	}
+
+	sprintf(test_c, test_c_temp, "u_long", "socket");
+	if (try_socket(logdepth, "libs/socket/ioctl", "", test_c, "+", "+", 0)) {
+		put("libs/socket/ioctlsocket/presents", strue);
+		put("libs/socket/ioctl/argtype", "u_long");
+		put("libs/socket/ioctl/presents", sfalse);
+		put("libs/socket/ioctl/ioctl", "ioctlsocket");
+		report("OK (ioctlsocket, arg type u_long)\n");
+		return 0;
+	}
+
+	put("libs/socket/ioctlsockets/presents", sfalse);
+	report("ioctl for sockets not found\n");
+	return 1;
+}
+
+int find_socket_fionbio(int logdepth, int fatal)
+{
+	char test_cf[] =
+		NL "int main() {"
+		NL "	int s;"
+		NL "	%s nonblocking = 1;"
+		NL "	SOCK_INIT;"
+		NL "	s = socket(AF_INET, SOCK_STREAM, 0);"
+		NL "	%s(s, FIONBIO, &nonblocking);"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[256];
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal)) /* for SOCK_INIT and implicit includes */
+		return 1;
+	if (require("libs/socket/ioctl/ioctl", logdepth, fatal))
+		return 1;
+
+
+	report("Checking for socket FIONBIO... ");
+	logprintf(logdepth, "find_socket_fionbio: trying to find fionbio()...\n");
+	logdepth++;
+
+	sprintf(test_c, test_cf, get("libs/socket/ioctl/argtype"), get("libs/socket/ioctl/ioctl"));
+	if (try_socket(logdepth, "libs/socket/ioctl/fionbio", "", test_c, "+", "+", 0)) {
+		put("libs/socket/ioctl/fionbio/presents", strue);
+		report("found (win32-style)\n");
+		return 0;
+	}
+
+	sprintf(test_c, test_cf, get("libs/socket/ioctl/argtype"), get("libs/socket/ioctl/ioctl"));
+	if (try_socket(logdepth, "libs/socket/ioctl/fionbio", "#include <sys/ioctl.h>", test_c, "+", "+", 0)) {
+		put("libs/socket/ioctl/fionbio/presents", strue);
+		report("found (UNIX-style)\n");
+		return 0;
+	}
+
+	sprintf(test_c, test_cf, get("libs/socket/ioctl/argtype"), get("libs/socket/ioctl/ioctl"));
+	if (try_socket(logdepth, "libs/socket/ioctl/fionbio", "#define BSD_COMP\\n#include <sys/ioctl.h>", test_c, "+", "+", 0)) {
+		put("libs/socket/ioctl/fionbio/presents", strue);
+		report("found (UNIX-style with BSD_COMP)\n");
+		return 0;
+	}
+
+	put("libs/socket/ioctl/fionbio/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_closesocket(int logdepth, int fatal)
+{
+	char test_c[] =
+		NL "int main() {"
+		NL "	int s;"
+		NL "	SOCK_INIT;"
+		NL "	s = socket(AF_INET, SOCK_STREAM, 0);"
+		NL "	closesocket(s);"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+	report("Checking for closesocket... ");
+	logprintf(logdepth, "find_socket_closesocket: trying to find closesocket()...\n");
+	logdepth++;
+
+	if (try_socket(logdepth, "libs/socket/closesocket", "", test_c, "+", "+", 0)) {
+		put("libs/socket/closesocket/presents", strue);
+		report("found\n");
+		return 0;
+	}
+
+	put("libs/socket/closesocket/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_SHUT(int logdepth, int fatal)
+{
+	char test_c[] =
+		NL "int main() {"
+		NL "	int i;"
+		NL "	i = SHUT_WR + SHUT_RD + SHUT_RDWR;"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+
+	report("Checking for SHUT_* constants... ");
+	logprintf(logdepth, "find_socket_SHUT: trying to find SHUT_* constants...\n");
+	logdepth++;
+
+	if (try_socket(logdepth, "libs/socket/SHUT", "", test_c, "+", "+", 0)) {
+		report("found\n");
+		return 0;
+	}
+
+	put("libs/socket/SHUT/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_SD(int logdepth, int fatal)
+{
+	char test_c[] =
+		NL "int main() {"
+		NL "	int i;"
+		NL "	i = SD_RECEIVE + SD_SEND + SD_BOTH;"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+	report("Checking for SD_* constants... ");
+	logprintf(logdepth, "find_socket_SD: trying to find SD_* constants...\n");
+	logdepth++;
+
+	if (try_socket(logdepth, "libs/socket/SD", "", test_c, "+", "+", 0)) {
+		report("found\n");
+		return 0;
+	}
+
+	put("libs/socket/SD/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_shutdown(int logdepth, int fatal)
+{
+	char test_c[] =
+		NL "int main() {"
+		NL "	int s;"
+		NL "	SOCK_INIT;"
+		NL "	s = socket(AF_INET, SOCK_STREAM, 0);"
+		NL "	shutdown(s, 0);"
+		NL "	puts(\"OK\");"
+		NL "	SOCK_UNINIT;"
+		NL "	return 0;"
+		NL "}"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+
+	report("Checking for shutdown... ");
+	logprintf(logdepth, "find_socket_shutdown: trying to find shutdown()...\n");
+	logdepth++;
+
+	if (try_socket(logdepth, "libs/socket/shutdown", "", test_c, "+", "+", 0)) {
+		report("found\n");
+		return 0;
+	}
+
+	put("libs/socket/shutdown/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_sockaddr_in(int logdepth, int fatal)
+{
+	static char *test_c =
+		NL "#include <stdlib.h>"
+		NL "#include <stdio.h>"
+		NL "#include <string.h>"
+		NL "int main() {"
+		NL "	struct sockaddr_in sa;"
+		NL "	char buff[64];"
+		NL "	int ls, cs, as;"
+		NL ""
+		NL "	SOCK_INIT;"
+		NL "	ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);"
+		NL "	cs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);"
+		NL ""
+		NL "	memset(&sa, 0, sizeof(sa));"
+		NL "	sa.sin_family      = AF_INET;"
+		NL "	sa.sin_addr.s_addr = 0x0100007f;"
+		NL "	sa.sin_port = 4242;"
+		NL "	printf(\"OK\\n\");"
+		NL "	return 0;"
+		NL "}";
+
+	char *inc_db =
+		NL "#include <netdb.h>"
+		NL;
+
+	char *inc_inet =
+		NL "#include <netinet/in.h>"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for sockaddr_in... ");
+	logprintf(logdepth, "find_socket_sockaddr_in: trying to find sockaddr_in...\n");
+	logdepth++;
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/sockaddr_in", "", test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/sockaddr_in", inc_db, test_c, "+", "+", 1)) return 0;
+	if (try_socket(logdepth, "libs/socket/sockaddr_in", inc_inet, test_c, "+", "+", 1)) return 0;
+
+	put("libs/socket/sockaddr_in/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+static int test_lac_port = 48223;
+static char *test_lac =
+	NL "%s"
+	NL "#include <stdlib.h>"
+	NL "#include <stdio.h>"
+	NL "#include <string.h>"
+	NL "int main() {"
+	NL "	struct sockaddr_in sa;"
+	NL "	char buff[64];"
+	NL "	int ls, cs, as;"
+	NL ""
+	NL "	SOCK_INIT;"
+	NL "	ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);"
+	NL "	cs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);"
+	NL ""
+	NL "	memset(&sa, 0, sizeof(sa));"
+	NL "	sa.sin_family      = AF_INET;"
+	NL "	sa.sin_addr.s_addr =  %s;"
+	NL "	sa.sin_port = %d;"
+	NL "	bind(ls, (struct sockaddr *)&sa, sizeof(sa));"
+	NL "	fprintf(stderr, \"l=%%d ls=%%d\\n\", listen(ls, 2), ls);"
+	NL "	fprintf(stderr, \"c=%%d cs=%%d\\n\", connect(cs, (struct sockaddr *)&sa, sizeof(sa)), cs);"
+	NL "	as = accept(ls, NULL, NULL);"
+	NL "	fprintf(stderr, \"as=%%d\\n\", as);"
+	NL "%s"
+	NL "	puts(\"OK\");"
+	NL "	SOCK_UNINIT;"
+	NL "	return 0;"
+	NL "}"
+	NL;
+
+static const char *localhost(void)
+{
+	/* do not depend on any macro for localhost */
+	if (strcmp(get("sys/byte_order"), "LSB") == 0)
+		return "0x0100007f";
+	return "0x7f000001";
+}
+
+int find_socket_lac(int logdepth, int fatal)
+{
+	char test_c[1024];
+
+	char *inc_linux =
+		NL;
+
+	char *inc_dns;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+	if (require("libs/socket/sockaddr_in/presents", logdepth, fatal))
+		return 1;
+
+	if (require("sys/byte_order", logdepth, fatal))
+		return 1;
+
+
+	inc_dns = str_subsn(get("libs/socket/sockaddr_in/includes"));
+
+	report("Checking for lac... ");
+	logprintf(logdepth, "find_socket_lac: trying to find lac...\n");
+	logdepth++;
+
+	sprintf(test_c, test_lac, inc_dns, localhost(), test_lac_port++, "");
+	free(inc_dns);
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/lac", inc_linux, test_c, "+", "+", 1)) return 0;
+
+	put("libs/socket/lac/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_recvsend(int logdepth, int fatal)
+{
+	char test_c[1024];
+
+	char *test_local =
+		NL "*buff = '\\0';"
+		NL "send(cs, \"test\", 4, 0);"
+		NL "recv(as, buff, 4, 0);"
+		NL "buff[4] = '\\0';"
+		NL "fprintf(stderr, \"buff='%s' (should be 'test')\\n\", buff);"
+		NL "if (strcmp(buff, \"test\") != 0)"
+		NL "	return 1;"
+		NL ;
+
+	char *inc_linux =
+		NL;
+
+	char *inc_dns;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+	if (require("libs/socket/sockaddr_in/presents", logdepth, fatal))
+		return 1;
+
+	if (require("sys/byte_order", logdepth, fatal))
+		return 1;
+
+	inc_dns = str_subsn(get("libs/socket/sockaddr_in/includes"));
+
+	report("Checking for recv() and send()... ");
+	logprintf(logdepth, "find_socket_recvsend: trying to find recv() and send()...\n");
+	logdepth++;
+
+	sprintf(test_c, test_lac, inc_dns, localhost(), test_lac_port++, test_local);
+	free(inc_dns);
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/recvsend", inc_linux, test_c, "+", "+", 1)) return 0;
+
+	put("libs/socket/recvsend/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_readwrite(int logdepth, int fatal)
+{
+	char test_c[1024];
+
+	char *test_local =
+		NL "*buff = '\\0';"
+		NL "write(cs, \"test\", 4);"
+		NL "read(as, buff, 4);"
+		NL "buff[4] = '\\0';"
+		NL "fprintf(stderr, \"buff='%s' (should be 'test')\\n\", buff);"
+		NL "if (strcmp(buff, \"test\") != 0)"
+		NL "	return 1;"
+		NL ;
+
+	char *inc_linux =
+		NL;
+
+	char *inc_dns;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/sockaddr_in/presents", logdepth, fatal))
+		return 1;
+	if (require("sys/byte_order", logdepth, fatal))
+		return 1;
+
+	inc_dns = str_subsn(get("libs/socket/sockaddr_in/includes"));
+
+	report("Checking for read() and write()... ");
+	logprintf(logdepth, "find_socket_recvsend: trying to find read() and write()...\n");
+	logdepth++;
+
+	sprintf(test_c, test_lac, inc_dns, localhost(), test_lac_port++, test_local);
+	free(inc_dns);
+
+	/* Look at some standard places */
+	if (try_socket(logdepth, "libs/socket/readwrite", inc_linux, test_c, "+", "+", 1)) return 0;
+
+	put("libs/socket/readwrite/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
+
+int find_socket_ntoh(int logdepth, int fatal)
+{
+	char test_c[] =
+		NL "int main() {"
+		NL "	short int sh;"
+		NL "	long int ln;"
+		NL "	sh = ntohs(htons(42));"
+		NL "	ln = ntohl(htonl(42));"
+		NL "	if ((sh == 42) && (ln == 42))"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char *inc[] = {"#include <arpa/inet.h>", "#include <winsock2.h>", NULL};
+	char **i;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+	if (require("libs/socket/socket/presents", logdepth, fatal))
+		return 1;
+
+
+	report("Checking for ntoh macros... ");
+	logprintf(logdepth, "find_socket_ntoh: trying to find ntoh macros...\n");
+	logdepth++;
+
+	for(i = inc; *i != NULL; i++) {
+		if (try_socket_(logdepth, *i, test_c, "+", "+")) {
+			put("libs/socket/ntoh/presents", strue);
+			put("libs/socket/ntoh/includes", *i);
+			report("OK (%s)\n", *i);
+			return 0;
+		}
+
+		if (try_socket_(logdepth, *i, test_c, "+", "+-lws2_32")) {
+			put("libs/socket/ntoh/presents", strue);
+			put("libs/socket/ntoh/includes", *i);
+			report("OK (%s)\n", *i);
+			return 0;
+		}
+	}
+
+	put("libs/socket/ntoh/presents", sfalse);
+	report("not found\n");
+	return 1;
+}
diff --git a/scconfig/src/socket/socket.c b/scconfig/src/socket/socket.c
new file mode 100644
index 0000000..2f6aa34
--- /dev/null
+++ b/scconfig/src/socket/socket.c
@@ -0,0 +1,170 @@
+/*
+    scconfig - helper functions for detecting sockets
+    Copyright (C) 2009..2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "socket.h"
+#include <assert.h>
+#include "find.h"
+#include "regex.h"
+
+void deps_socket_init()
+{
+	dep_add("libs/socket/socket/*",              find_socket_socket);
+	dep_add("libs/socket/wsasocket/*",           find_socket_wsasocket);
+	dep_add("libs/socket/socketpair/*",          find_socket_socketpair);
+	dep_add("libs/socket/select/*",              find_socket_select);
+	dep_add("libs/socket/poll/*",                find_socket_poll);
+	dep_add("libs/socket/types",                 find_socket_types);
+	dep_add("libs/socket/ioctlsocket/*",         find_socket_ioctlsocket);
+	dep_add("libs/socket/ioctl/*",               find_socket_ioctlsocket);
+	dep_add("libs/socket/ioctl/ioctl",           find_socket_ioctlsocket);
+	dep_add("libs/socket/closesocket/*",         find_socket_closesocket);
+	dep_add("libs/socket/shutdown/*",            find_socket_shutdown);
+	dep_add("libs/socket/SHUT/*",                find_socket_SHUT);
+	dep_add("libs/socket/SD/*",                  find_socket_SD);
+	dep_add("libs/socket/ioctl/fionbio/*",       find_socket_fionbio);
+	dep_add("libs/socket/sockaddr_in/*",         find_socket_sockaddr_in);
+	dep_add("libs/socket/lac/*",                 find_socket_lac);
+	dep_add("libs/socket/recvsend/*",            find_socket_recvsend);
+	dep_add("libs/socket/readwrite/*",           find_socket_readwrite);
+	dep_add("libs/socket/ntoh/*",                find_socket_ntoh);
+
+	dep_add("libs/socket/getaddrinfo/*",         find_socket_getaddrinfo);
+	dep_add("libs/socket/getnameinfo/*",         find_socket_getnameinfo);
+}
+
+int try_socket_(int logdepth, const char *includes_, const char *test_c, const char *cflags, const char *ldflags)
+{
+	char *out = NULL, *s, *includes;
+	char *src;
+	int res;
+	int ilen, slen, n;
+
+	includes = uniq_inc_str(includes_, NULL, "\n", 0);
+	logprintf(logdepth, "trying '%s' and '%s' and '%s'\n", safeNULL(cflags), safeNULL(ldflags), safeNULL(includes));
+	ilen = strlen(includes);
+	slen = strlen(test_c);
+	src = malloc(ilen + slen + 1);
+	memcpy(src, includes, ilen);
+	free(includes);
+	for(s = src+1, n = 1; n < ilen; n++,s++) {
+		if ((s[-1] == '\\') && (s[0] == 'n')) {
+			s[-1] = '\n';
+			s[0]  = '\n';
+		}
+	}
+	memcpy(src+ilen, test_c, slen);
+	src[ilen+slen]='\0';
+	res = compile_run(logdepth+1, src, NULL, cflags, ldflags, &out);
+	free(src);
+	if (res == 0) {
+		if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+			free(out);
+			return 1;
+		}
+	}
+	free(out);
+	return 0;
+}
+
+/* do not append socket cflags/ldflags - required for detecting socket itself (find_socket_socket()) and for select/poll detection */
+int try_socket_pure(int logdepth, const char *prefix, const char *includes_, const char *defs, const char *test_c, const char *cflags, const char *ldflags, int printreport)
+{
+	char *tmp, *itmp, *incdefs;
+	int ilen, ret;
+	char *so;
+	const char *si;
+
+	incdefs = str_concat(includes_, "\n", defs == NULL ? "" : defs, "\n", NULL);
+	ret = try_socket_(logdepth, incdefs, test_c, cflags, ldflags);
+	free(incdefs);
+	if (ret) {
+			char *includes;
+			includes = uniq_inc_str(includes_, NULL, "\n", 0);
+			ilen = strlen(includes);
+
+			if (*cflags == '+')
+				cflags++;
+			if (*ldflags == '+')
+				ldflags++;
+
+			tmp = malloc(strlen(prefix) + 32);
+			sprintf(tmp, "%s/cflags", prefix);
+			put(tmp, cflags);
+			sprintf(tmp, "%s/ldflags", prefix);
+			put(tmp, ldflags);
+			sprintf(tmp, "%s/presents", prefix);
+			put(tmp, "true");
+
+			itmp = malloc(ilen * 2);
+			for(si = includes, so = itmp; *si != '\0'; si++, so++) {
+				if (*si == '\n') {
+					*so = '\\';
+					so++;
+					*so = 'n';
+				}
+				else
+					*so = *si;
+			}
+			*so = '\0';
+			sprintf(tmp, "%s/includes", prefix);
+			put(tmp, itmp);
+			if (printreport)
+				report("OK ('%s' and '%s' and '%s')\n", cflags, ldflags, itmp);
+			free(itmp);
+			free(tmp);
+			free(includes);
+			return 1;
+		}
+	return 0;
+}
+
+int try_socket_def(int logdepth, const char *prefix, const char *includes, char *defs_, const char *test_c, const char *cflags, const char *ldflags, int printreport)
+{
+	const char *sinc, *scflags, *sldflags, *sinit, *suninit;
+	char *ninc, *ncflags, *nldflags, *defs;
+	int res;
+
+	sinc     = get("libs/socket/socket/includes");
+	sinit    = get("libs/socket/socket/init_code");
+	suninit  = get("libs/socket/socket/uninit_code");
+	scflags  = get("libs/socket/socket/cflags");
+	sldflags = get("libs/socket/socket/ldflags");
+	if (*includes == '!')
+		ninc     = str_concat("\n", (includes+1), "\n",  sinc == NULL ? "" : sinc, NULL);
+	else
+		ninc     = str_concat("\n", sinc == NULL ? "" : sinc, "\n", includes, NULL);
+
+	defs     = str_concat("", "\n", ((defs_ == NULL) ? "" : defs_), "\n", "#define SOCK_INIT ", sinit, "\n", "#define SOCK_UNINIT ", suninit, "\n", NULL);
+	ncflags  = str_concat(" ", cflags, " ", scflags, NULL);
+	nldflags = str_concat(" ", ldflags, " ", sldflags, NULL);
+	res = try_socket_pure(logdepth, prefix, ninc, defs, test_c, ncflags, nldflags, printreport);
+	free(ninc);
+	free(ncflags);
+	free(nldflags);
+
+	return res;
+}
+
+int try_socket(int logdepth, const char *prefix, const char *includes, const char *test_c, const char *cflags, const char *ldflags, int printreport)
+{
+	return try_socket_def(logdepth, prefix, includes, NULL, test_c, cflags, ldflags, printreport);
+}
diff --git a/scconfig/src/socket/socket.h b/scconfig/src/socket/socket.h
new file mode 100644
index 0000000..74f4673
--- /dev/null
+++ b/scconfig/src/socket/socket.h
@@ -0,0 +1,40 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int try_socket_(int logdepth, const char *includes, const char *test_c, const char *cflags, const char *ldflags);
+
+/* try test program without fetching any implicits */
+int try_socket_pure(int logdepth, const char *prefix, const char *includes, const char *defs, const char *test_c, const char *cflags, const char *ldflags, int printreport);
+
+/* try test program with socket init/uninit defines, includes, ldflags and cflags appended */
+int try_socket(int logdepth, const char *prefix, const char *includes, const char *test_c, const char *cflags, const char *ldflags, int printreport);
+
+/* try test program with socket init/uninit defines, includes, special defines, ldflags and cflags appended */
+int try_socket_def(int logdepth, const char *prefix, const char *includes, char *defs_, const char *test_c, const char *cflags, const char *ldflags, int printreport);
+
+/* socket API detection */
+int find_socket_socket(int logdepth, int fatal);
+int find_socket_wsasocket(int logdepth, int fatal);
+int find_socket_socketpair(int logdepth, int fatal);
+int find_socket_select(int logdepth, int fatal);
+int find_socket_poll(int logdepth, int fatal);
+int find_socket_types(int logdepth, int fatal);
+int find_socket_ioctlsocket(int logdepth, int fatal);
+int find_socket_closesocket(int logdepth, int fatal);
+int find_socket_SHUT(int logdepth, int fatal);
+int find_socket_SD(int logdepth, int fatal);
+int find_socket_shutdown(int logdepth, int fatal);
+int find_socket_fionbio(int logdepth, int fatal);
+int find_socket_sockaddr_in(int logdepth, int fatal);
+int find_socket_lac(int logdepth, int fatal);
+int find_socket_recvsend(int logdepth, int fatal);
+int find_socket_readwrite(int logdepth, int fatal);
+int find_socket_ntoh(int logdepth, int fatal);
+
+int find_socket_getaddrinfo(int logdepth, int fatal);
+int find_socket_getnameinfo(int logdepth, int fatal);
diff --git a/scconfig/src/sul/INIT.c b/scconfig/src/sul/INIT.c
new file mode 100644
index 0000000..ed61945
--- /dev/null
+++ b/scconfig/src/sul/INIT.c
@@ -0,0 +1,2 @@
+	deps_sul_init();
+
diff --git a/scconfig/src/sul/INIT.h b/scconfig/src/sul/INIT.h
new file mode 100644
index 0000000..abb0fb6
--- /dev/null
+++ b/scconfig/src/sul/INIT.h
@@ -0,0 +1 @@
+void deps_sul_init();
diff --git a/scconfig/src/sul/Makefile.plugin b/scconfig/src/sul/Makefile.plugin
new file mode 100644
index 0000000..8fc79c1
--- /dev/null
+++ b/scconfig/src/sul/Makefile.plugin
@@ -0,0 +1,23 @@
+SUL_CFLAGS = -DPLUGIN_SUL
+SUL_OBJS = \
+  $(BIN)/sul/find_sul.o \
+  $(BIN)/sul/find_glib.o \
+  $(BIN)/sul/find_gettext.o \
+  $(BIN)/sul/find_dbus.o \
+  $(BIN)/sul/find_dmalloc.o
+
+$(BIN)/sul/find_sul.o: $(SRC)/sul/find_sul.c
+	$(CC) $(CFLAGS) -c $(SRC)/sul/find_sul.c -o $(BIN)/sul/find_sul.o
+
+$(BIN)/sul/find_glib.o: $(SRC)/sul/find_glib.c
+	$(CC) $(CFLAGS) -c $(SRC)/sul/find_glib.c -o $(BIN)/sul/find_glib.o
+
+$(BIN)/sul/find_gettext.o: $(SRC)/sul/find_gettext.c
+	$(CC) $(CFLAGS) -c $(SRC)/sul/find_gettext.c -o $(BIN)/sul/find_gettext.o
+
+$(BIN)/sul/find_dbus.o: $(SRC)/sul/find_dbus.c
+	$(CC) $(CFLAGS) -c $(SRC)/sul/find_dbus.c -o $(BIN)/sul/find_dbus.o
+
+$(BIN)/sul/find_dmalloc.o: $(SRC)/sul/find_dmalloc.c
+	$(CC) $(CFLAGS) -c $(SRC)/sul/find_dmalloc.c -o $(BIN)/sul/find_dmalloc.o
+
diff --git a/scconfig/src/sul/find_dbus.c b/scconfig/src/sul/find_dbus.c
new file mode 100644
index 0000000..bbf4839
--- /dev/null
+++ b/scconfig/src/sul/find_dbus.c
@@ -0,0 +1,58 @@
+/*
+    scconfig - sul - software utility libraries
+    Copyright (C) 2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_sul_dbus(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <string.h>"
+		NL "#include <dbus/dbus.h>"
+		NL "int main()"
+		NL "{"
+		NL "	DBusError err;"
+		NL "	dbus_error_init(&err);"
+		NL "	puts(\"OK\");"
+		NL "	return 0;"
+		NL "}";
+	const char *node = "libs/sul/dbus";
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for dbus... ");
+
+	if (try_icl_pkg_config(logdepth, "libs/sul/dbus", test_c, NULL, "dbus-*", get("/arg/dbus-version")))
+		return 0;
+
+	if (try_icl(logdepth, node, test_c, NULL, "-Idbus-1", "-ldbus"))
+		return 0;
+
+	return try_fail(logdepth, node);
+}
diff --git a/scconfig/src/sul/find_dmalloc.c b/scconfig/src/sul/find_dmalloc.c
new file mode 100644
index 0000000..77f0b52
--- /dev/null
+++ b/scconfig/src/sul/find_dmalloc.c
@@ -0,0 +1,56 @@
+/*
+    scconfig - sul - software utility libraries
+    Copyright (C) 2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_sul_dmalloc(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <string.h>"
+		NL "#include <dmalloc.h>"
+		NL "int main()"
+		NL "{"
+		NL "	if (DMALLOC_VERSION_MAJOR) {"
+		NL "		puts(\"OK\");"
+		NL "		return 0;"
+		NL "	}"
+		NL "	return 1;"
+		NL "}";
+	const char *node = "libs/sul/dmalloc";
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for dmalloc... ");
+
+	if (try_icl(logdepth, node, test_c, NULL, "", "-ldmalloc"))
+		return 0;
+
+	return try_fail(logdepth, node);
+}
diff --git a/scconfig/src/sul/find_gettext.c b/scconfig/src/sul/find_gettext.c
new file mode 100644
index 0000000..63044e5
--- /dev/null
+++ b/scconfig/src/sul/find_gettext.c
@@ -0,0 +1,67 @@
+/*
+    scconfig - sul - software utility libraries
+    Copyright (C) 2016  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_sul_gettext(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <stdio.h>"
+		NL "#include <string.h>"
+		NL "#include <locale.h>"
+		NL "#include <libintl.h>"
+		NL "int main()"
+		NL "{"
+		NL "	char *res;"
+		NL "	setlocale(LC_ALL, \"C\");"
+		NL "	bindtextdomain(\"scconfig\", \"/this/does/not/even/exist/\");"
+		NL "	textdomain(\"scc\");"
+		NL "	res = gettext(\"foo\");"
+		NL "	if ((res != NULL) && (strcmp(res, \"foo\") == 0)) {"
+		NL "		puts(\"OK\");"
+		NL "		return 0;"
+		NL "	}"
+		NL "	return 1;"
+		NL "}";
+	const char *node = "libs/sul/gettext";
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for gettext... ");
+
+	/* In the GNU world, this should work without any extra */
+	if (try_icl(logdepth, node, test_c, NULL, "", ""))
+		return 0;
+
+	/* Reported by James on MacOSX */
+	if (try_icl(logdepth, node, test_c, NULL, "-I/usr/local/opt/gettext/include", "-L/usr/local/opt/gettext/lib -lintl"))
+		return 0;
+
+	return try_fail(logdepth, node);
+}
diff --git a/scconfig/src/sul/find_glib.c b/scconfig/src/sul/find_glib.c
new file mode 100644
index 0000000..e10071b
--- /dev/null
+++ b/scconfig/src/sul/find_glib.c
@@ -0,0 +1,68 @@
+/*
+    scconfig - sul - software utility libraries
+    Copyright (C) 2013  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_sul_glib(int logdepth, int fatal)
+{
+	const char *test_c =
+		NL "#include <glib.h>"
+		NL "#include <stdlib.h>"
+		NL
+		NL "int main(int argc, char *argv[])"
+		NL "{"
+		NL "	GArray *a = g_array_new(FALSE, FALSE, sizeof(char *));"
+		NL "	g_array_free(a, FALSE);"
+		NL "	puts(\"OK\");"
+		NL "	return EXIT_SUCCESS;"
+		NL "}";
+	const char *node = "libs/sul/glib";
+	char *cflags;
+	char *ldflags;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for glib... ");
+	logprintf(logdepth, "find_glib: running pkg-config...\n");
+	logdepth++;
+
+	if (run_pkg_config(logdepth, "glib-2.0", &cflags, &ldflags) != 0)
+		return 1;
+
+
+	if (try_icl(logdepth, node, test_c, NULL, cflags, ldflags) == 0) {
+		free(cflags);
+		free(ldflags);
+		return try_fail(logdepth, node);
+	}
+
+	free(cflags);
+	free(ldflags);
+	return 0;
+}
diff --git a/scconfig/src/sul/find_sul.c b/scconfig/src/sul/find_sul.c
new file mode 100644
index 0000000..bc93eb1
--- /dev/null
+++ b/scconfig/src/sul/find_sul.c
@@ -0,0 +1,10 @@
+#include "find_sul.h"
+#include "dep.h"
+
+void deps_sul_init()
+{
+	dep_add("libs/sul/glib/*",     find_sul_glib);
+	dep_add("libs/sul/gettext/*",  find_sul_gettext);
+	dep_add("libs/sul/dmalloc/*",  find_sul_dmalloc);
+	dep_add("libs/sul/dbus/*",     find_sul_dbus);
+}
diff --git a/scconfig/src/sul/find_sul.h b/scconfig/src/sul/find_sul.h
new file mode 100644
index 0000000..42a08a7
--- /dev/null
+++ b/scconfig/src/sul/find_sul.h
@@ -0,0 +1,5 @@
+int find_sul_gettext(int logdepth, int fatal);
+int find_sul_glib(int logdepth, int fatal);
+int find_sul_dmalloc(int logdepth, int fatal);
+int find_sul_dbus(int logdepth, int fatal);
+
diff --git a/scconfig/src/tmpasm/Makefile b/scconfig/src/tmpasm/Makefile
new file mode 100644
index 0000000..068e0f7
--- /dev/null
+++ b/scconfig/src/tmpasm/Makefile
@@ -0,0 +1,17 @@
+CFLAGS = -Wall -g \
+ -I../default -DTMPASM_TESTER \
+ -I../generator
+
+tester: tester.o tmpasm.o debug.o tmpasm_scconfig.o \
+ ../default/db.o ../default/ht.o ../default/str.o ../default/log.o \
+ ../generator/openfiles.o ../default/regex.o ../default/lib_uniqinc.o
+
+tmpasm.o: tmpasm.c tmpasm.h
+
+test: regression/Makefile
+	cd regression && make
+
+regression/Makefile: regression/Makefile.in tester
+	./tester -e < regression/Makefile.in > regression/Makefile
+
+debug.o: debug.c debug.h tmpasm.h
diff --git a/scconfig/src/tmpasm/Makefile.plugin b/scconfig/src/tmpasm/Makefile.plugin
new file mode 100644
index 0000000..0d5d5b6
--- /dev/null
+++ b/scconfig/src/tmpasm/Makefile.plugin
@@ -0,0 +1,15 @@
+TMPASM_OBJS = \
+  $(BIN)/tmpasm/tmpasm.o \
+  $(BIN)/tmpasm/tmpasm_scconfig.o \
+  $(BIN)/generator/openfiles.o
+
+TMPASM_CFLAGS = -I$(SRC)/tmpasm -I$(SRC)/generator
+
+$(BIN)/tmpasm/tmpasm.o: $(SRC)/tmpasm/tmpasm.c $(SRC)/tmpasm/tmpasm.h $(SRC)/default/dep.h $(SRC)/default/log.h $(SRC)/default/regex.h
+	$(CC) $(CFLAGS) -c $(SRC)/tmpasm/tmpasm.c -o $(BIN)/tmpasm/tmpasm.o
+
+$(BIN)/tmpasm/tmpasm_scconfig.o: $(SRC)/tmpasm/tmpasm_scconfig.c $(SRC)/tmpasm/tmpasm.h $(SRC)/default/libs.h $(SRC)/default/log.h $(SRC)/default/regex.h
+	$(CC) $(CFLAGS) -c $(SRC)/tmpasm/tmpasm_scconfig.c -o $(BIN)/tmpasm/tmpasm_scconfig.o
+
+$(BIN)/generator/openfiles.o: $(SRC)/generator/openfiles.c
+	$(CC) $(CFLAGS) -c $(SRC)/generator/openfiles.c -o $(BIN)/generator/openfiles.o
diff --git a/scconfig/src/tmpasm/TODO b/scconfig/src/tmpasm/TODO
new file mode 100644
index 0000000..a31b122
--- /dev/null
+++ b/scconfig/src/tmpasm/TODO
@@ -0,0 +1,12 @@
+- regression test syntax errors and improve syntax error reporting
+	- switch:
+		- data instead of case
+		- multiple defaults
+		- deafult must be at end of the list
+		- case out of switch
+		- default out of switch
+	- forarch
+- tutorial
+	- append
+- [[]] eval? what's the output? -> generate and include scripts (may need mktemp binding)
+- update docs
diff --git a/scconfig/src/tmpasm/debug.c b/scconfig/src/tmpasm/debug.c
new file mode 100644
index 0000000..f56acc9
--- /dev/null
+++ b/scconfig/src/tmpasm/debug.c
@@ -0,0 +1,111 @@
+#include <stdio.h>
+#include "tmpasm.h"
+
+static void indent(FILE *f, int depth)
+{
+	for(;depth > 0; depth--) fputc(' ', f);
+}
+
+static void print_arg(FILE *f, tmpasm_arg_t *a)
+{
+	if (a == NULL) {
+		fprintf(f, "*NULL - broken AST*");
+		return;
+	}
+	if (a->next != NULL) {
+		/* block mode */
+		fprintf(f, "[~");
+		for(;a != NULL; a = a->next) {
+			if (a->is_addr)
+				fprintf(f, "~%s~", a->data);
+			else
+				fprintf(f, "%s", a->data);
+		}
+		fprintf(f, "~]");
+	}
+	else {
+		if (a->is_addr)
+			fprintf(f, "%s", a->data);
+		else
+			fprintf(f, "{%s}", a->data);
+	}
+}
+
+static void print_loc(FILE *f, tmpasm_exec_t *c)
+{
+	if ((c->line != 0) || (c->col != 0))
+		fprintf(f, " [at %d:%d]\n", c->line, c->col);
+	else
+		fprintf(f, "\n");
+}
+
+static void dump(FILE *f, int depth, tmpasm_exec_t *c)
+{
+	tmpasm_case_t *cc;
+	int n;
+	for(; c != NULL; c = c->next) {
+		switch(c->kw) {
+			case KW_NOP:
+				indent(f, depth);
+				fprintf(f, "(NOP)");
+				print_loc(f, c);
+				break;
+			case KW_none:
+				indent(f, depth);
+				fprintf(f, "%s", c->payload.instr.call_name);
+				print_loc(f, c);
+				for(n = 0; n < c->payload.instr.argc; n++) {
+					indent(f, depth+1);
+					fprintf(f, "arg: ");
+					print_arg(f, c->payload.instr.argv[n]);
+					fprintf(f, "\n");
+				}
+				break;
+			case KW_IF:
+				indent(f, depth);
+				fprintf(f, "if ");
+				print_arg(f, c->payload.fc_if.cond);
+				print_loc(f, c);
+				indent(f, depth);
+				fprintf(f, "then:\n");
+				dump(f, depth+1, c->payload.fc_if.code_then);
+				indent(f, depth);
+				fprintf(f, "else:\n");
+				dump(f, depth+1, c->payload.fc_if.code_else);
+				break;
+			case KW_FOREACH:
+				indent(f, depth);
+				fprintf(f, "foreach %s in ", c->payload.fc_foreach.loop_var);
+				print_arg(f, c->payload.fc_foreach.data);
+				print_loc(f, c);
+				dump(f, depth+1, c->payload.fc_foreach.code_body);
+				break;
+			case KW_SWITCH:
+				indent(f, depth);
+				fprintf(f, "switch ");
+				print_arg(f, c->payload.fc_switch.cond);
+				print_loc(f, c);
+				for(cc = c->payload.fc_switch.first; cc != NULL; cc = cc->next) {
+					indent(f, depth+1);
+					if (cc->data != NULL) {
+						fprintf(f, "case ");
+						print_arg(f, cc->data);
+						fprintf(f, "\n");
+					}
+					else
+						printf("default\n");
+					dump(f, depth+2, cc->body);
+				}
+				break;
+			default:
+				indent(f, depth);
+				fprintf(f, "invalid kw ");
+				print_loc(f, c);
+		}
+	}
+}
+
+void tmpasm_dump(tmpasm_t *ctx, FILE *f)
+{
+	dump(f, 0, ctx->code);
+}
diff --git a/scconfig/src/tmpasm/debug.h b/scconfig/src/tmpasm/debug.h
new file mode 100644
index 0000000..7841be1
--- /dev/null
+++ b/scconfig/src/tmpasm/debug.h
@@ -0,0 +1,2 @@
+void tmpasm_dump(tmpasm_t *ctx, FILE *f);
+
diff --git a/scconfig/src/tmpasm/regression/Makefile.in b/scconfig/src/tmpasm/regression/Makefile.in
new file mode 100644
index 0000000..e52bc31
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Makefile.in
@@ -0,0 +1,55 @@
+# list of tests
+put tests [@
+	Tutor01_hello
+	Tutor02_vars
+	Tutor03_blocks
+	Tutor04_if
+	Tutor05_switch
+	Tutor06_foreach
+	Tutor07_sub
+	Tutor08_uniq
+	Tutor09_ui
+	Tutor10_include_redir
+	Tutor11_missing
+	comment
+	foreach
+	if
+	switch
+	test
+	then
+	append
+	err_if_end
+	err_if_else
+	err_excess_end
+	err_switch_end
+	err_switch_nocond
+	err_no_end
+@]
+
+uniq tests
+
+put test_diffs tests
+gsub test_diffs {[ \t\r\n]+} { }
+gsub test_diffs {\\>} {.diff}
+# TODO: replace this with wrap
+sub test_diffs {^ *} {}
+sub test_diffs { *$} {}
+
+print [@### PLEASE DO NOT EDIT THIS FILE, it has been generated from Makefile.in. ###
+
+TESTER=../tester
+
+all: @test_diffs@
+
+# Explicit test rules
+@]
+
+foreach test in tests
+print [@
+ at test@.out: @test at .gasm $(TESTER) Makefile
+	$(TESTER) < @test at .gasm > @test at .out 2>&1
+
+ at test@.diff: @test at .ref @test at .out
+	diff -u @test at .ref @test at .out
+@]
+end
diff --git a/scconfig/src/tmpasm/regression/Tutor01_hello.gasm b/scconfig/src/tmpasm/regression/Tutor01_hello.gasm
new file mode 100644
index 0000000..d37cb2b
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor01_hello.gasm
@@ -0,0 +1,18 @@
+# Comments start with # and end at the end of the line; comments
+# can be started anywhere outside of strings and blocks
+
+# Print will print each argument without appending a newline. An argument
+# is a data, string literals are written in brace. Escape sequences
+# are as usual
+print {hello world!\n}
+
+# Print doesn't have any hidden side effect: no separators printed
+# between arguments
+print {hello} {world!} {\n}
+
+# Print works without an argument as well: it just prints nothing
+print
+
+# instructions are separated by newlines and/or semicolons:
+print {HELLO}; print { WORLD!};;;; print {\n};
+
diff --git a/scconfig/src/tmpasm/regression/Tutor01_hello.ref b/scconfig/src/tmpasm/regression/Tutor01_hello.ref
new file mode 100644
index 0000000..bfb644b
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor01_hello.ref
@@ -0,0 +1,16 @@
+print [at 7:1]
+ arg: {hello world!
+}
+print [at 11:1]
+ arg: {hello}
+ arg: {world!}
+ arg: {
+}
+print [at 14:1]
+print [at 17:1]
+ arg: {HELLO}
+print [at 17:16]
+ arg: { WORLD!}
+print [at 17:36]
+ arg: {
+}
diff --git a/scconfig/src/tmpasm/regression/Tutor02_vars.gasm b/scconfig/src/tmpasm/regression/Tutor02_vars.gasm
new file mode 100644
index 0000000..80a659f
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor02_vars.gasm
@@ -0,0 +1,17 @@
+# Variables are stored in a hash. Variable names follow the normal
+# identifier rules with less restrictions (can start with number)
+# To set the value of a variable, use: put var value
+put myvar {Hello world!\n}
+
+# Referencing the variable is done by using its name
+print myvar
+
+# In most context arguments are data; data can be both string literal and
+# variable reference:
+print {Hello universe! } myvar
+
+# the second var of put is just data, can be string or variable; copying
+# a variable:
+put str {cats raining from the sky}
+put tmp str
+print str {==} tmp {\n}
diff --git a/scconfig/src/tmpasm/regression/Tutor02_vars.ref b/scconfig/src/tmpasm/regression/Tutor02_vars.ref
new file mode 100644
index 0000000..da74207
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor02_vars.ref
@@ -0,0 +1,21 @@
+put [at 4:1]
+ arg: myvar
+ arg: {Hello world!
+}
+print [at 7:1]
+ arg: myvar
+print [at 11:1]
+ arg: {Hello universe! }
+ arg: myvar
+put [at 15:1]
+ arg: str
+ arg: {cats raining from the sky}
+put [at 16:1]
+ arg: tmp
+ arg: str
+print [at 17:1]
+ arg: str
+ arg: {==}
+ arg: tmp
+ arg: {
+}
diff --git a/scconfig/src/tmpasm/regression/Tutor03_blocks.gasm b/scconfig/src/tmpasm/regression/Tutor03_blocks.gasm
new file mode 100644
index 0000000..18003e0
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor03_blocks.gasm
@@ -0,0 +1,31 @@
+# Blocks are special syntax to make it easier to handle large,
+# to-be-printed-verbatim blocks of data, which is essential in a template
+# language. Blocks are enclosed in [$ $], where $ is an arbitrary character
+# that shall be chosen by the programmer, per block; once a characher is
+# chosen, it can not appear in the string. There is no backslash escaping
+# in blocks. Blocks can be used anywhere where strings could be used.
+print [@this is a string@] {\n}
+
+print {--\n}
+
+put myblk [!a block
+of multiline
+data.
+!]
+
+print myblk
+
+# A special feature of the block is inline variable substitution using the same
+# separator character chosen at the opening brace. In the example below
+# myvar is substituted because it is sorrounded by the separator (@ in
+# the first case and $ in the second case). Whitespace and newlines
+# are preserved in the block, even in the inline variable name!
+
+put myvar {world}
+print {--\n}
+print [@ hello @myvar@! @] {\n}
+print {--\n}
+print [$
+	hi @myvar@!
+$]
+print {--\n}
diff --git a/scconfig/src/tmpasm/regression/Tutor03_blocks.ref b/scconfig/src/tmpasm/regression/Tutor03_blocks.ref
new file mode 100644
index 0000000..e091176
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor03_blocks.ref
@@ -0,0 +1,35 @@
+print [at 7:1]
+ arg: {this is a string}
+ arg: {
+}
+print [at 9:1]
+ arg: {--
+}
+put [at 11:1]
+ arg: myblk
+ arg: {a block
+of multiline
+data.
+}
+print [at 16:1]
+ arg: myblk
+put [at 24:1]
+ arg: myvar
+ arg: {world}
+print [at 25:1]
+ arg: {--
+}
+print [at 26:1]
+ arg: [~ hello ~myvar~! ~]
+ arg: {
+}
+print [at 27:1]
+ arg: {--
+}
+print [at 28:1]
+ arg: {
+	hi @myvar@!
+}
+print [at 31:1]
+ arg: {--
+}
diff --git a/scconfig/src/tmpasm/regression/Tutor04_if.gasm b/scconfig/src/tmpasm/regression/Tutor04_if.gasm
new file mode 100644
index 0000000..3b45fa7
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor04_if.gasm
@@ -0,0 +1,31 @@
+put myvar {true}
+
+# The simplest flow control is an if. It takes its first argument and
+# calls the environment to decide if it is true or false. If it's true
+# the "then" branch is executed, if it's false, the "else" branch runs.
+if myvar then
+	print {myvar is true (1)\n}
+else
+	print {myvar is false (1)\n}
+end
+
+
+# it is possible to omit the else branch
+if myvar then
+	print {myvar is true (2)\n}
+end
+
+# the then branch may be empty:
+if myvar then else
+	print {myvar is false (3)\n}
+end
+
+# embedding controls is legal:
+put foo {false}
+if myvar then
+	if foo then
+		print {myvar and bar are true (4)\n}
+	else
+		print {myvar is true, bar is false (4)\n}
+	end
+end
diff --git a/scconfig/src/tmpasm/regression/Tutor04_if.ref b/scconfig/src/tmpasm/regression/Tutor04_if.ref
new file mode 100644
index 0000000..5680761
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor04_if.ref
@@ -0,0 +1,42 @@
+put [at 1:1]
+ arg: myvar
+ arg: {true}
+if myvar [at 6:10]
+then:
+ print [at 7:2]
+  arg: {myvar is true (1)
+}
+else:
+ print [at 9:2]
+  arg: {myvar is false (1)
+}
+if myvar [at 14:10]
+then:
+ print [at 15:2]
+  arg: {myvar is true (2)
+}
+else:
+ (NOP)
+if myvar [at 19:10]
+then:
+ (NOP)
+else:
+ print [at 20:2]
+  arg: {myvar is false (3)
+}
+put [at 24:1]
+ arg: foo
+ arg: {false}
+if myvar [at 25:10]
+then:
+ if foo [at 26:9]
+ then:
+  print [at 27:3]
+   arg: {myvar and bar are true (4)
+}
+ else:
+  print [at 29:3]
+   arg: {myvar is true, bar is false (4)
+}
+else:
+ (NOP)
diff --git a/scconfig/src/tmpasm/regression/Tutor05_switch.gasm b/scconfig/src/tmpasm/regression/Tutor05_switch.gasm
new file mode 100644
index 0000000..6e0aa88
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor05_switch.gasm
@@ -0,0 +1,42 @@
+# Switch is similar to case in posix shell and switch in C. It takes a
+# data argument and matches is against cases until the first match. It
+# executes the code for that match and stops executing the switch (unlike
+# in C, and like in sh, there is no fall-thru). A default case can be
+# defined as a catch-all rule.
+#
+# Scconfig uses regex matching (unlike sh (shell globbing) and C (integer)).
+#
+# The first word after the switch keyword is the string that is matched
+# against case patterns; the first word after a case is the pattern
+# the string is matched against. Each branch must be terminated by an "end",
+# just as the whole switch. Default doesn't have pattern, instructions start
+# immediately.
+
+put myvar {foobar}
+switch myvar
+	case {baz}  put res {1}; print {this is baz.\n}; end;
+	case {^oob} put res {2}; print {did you mean out-of-band?\n}; end;
+	case {^f}   put res {3}; print {starts with f.\n}; end;
+	case {oob}  put res {4}; print {OOB!\n}; end;
+	default     put res {0}; print {none.\n}; end;
+end;
+
+print {result is: } res {\n}
+
+
+# data is data - can be block as well, anywhere, in switch or case:
+put patt {^number$}
+put REF {3}
+switch [@num @res@ ber@]
+	case patt            print {empty\n}; end;
+	case [!^num !REF!!]  print {reference\n}; end;
+end
+
+# one of the uses of switch is to construct an if-then-else that uses
+# matching instead of checking for true/false; the following example
+# demonstrates how an "if cond matches {lob}" is done with switch.
+put cond {blobb}
+switch cond
+	case {lob}   print {"then"\n}; end
+	default      print {"else"\n}; end
+end
diff --git a/scconfig/src/tmpasm/regression/Tutor05_switch.ref b/scconfig/src/tmpasm/regression/Tutor05_switch.ref
new file mode 100644
index 0000000..00ba084
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor05_switch.ref
@@ -0,0 +1,71 @@
+put [at 15:1]
+ arg: myvar
+ arg: {foobar}
+switch myvar [at 16:1]
+ case {baz}
+  put [at 17:14]
+   arg: res
+   arg: {1}
+  print [at 17:27]
+   arg: {this is baz.
+}
+ case {^oob}
+  put [at 18:14]
+   arg: res
+   arg: {2}
+  print [at 18:27]
+   arg: {did you mean out-of-band?
+}
+ case {^f}
+  put [at 19:14]
+   arg: res
+   arg: {3}
+  print [at 19:27]
+   arg: {starts with f.
+}
+ case {oob}
+  put [at 20:14]
+   arg: res
+   arg: {4}
+  print [at 20:27]
+   arg: {OOB!
+}
+ default
+  put [at 21:14]
+   arg: res
+   arg: {0}
+  print [at 21:27]
+   arg: {none.
+}
+print [at 24:1]
+ arg: {result is: }
+ arg: res
+ arg: {
+}
+put [at 28:1]
+ arg: patt
+ arg: {^number$}
+put [at 29:1]
+ arg: REF
+ arg: {3}
+switch [~num ~res~ ber~] [at 30:1]
+ case patt
+  print [at 31:23]
+   arg: {empty
+}
+ case [~^num ~REF~~]
+  print [at 32:23]
+   arg: {reference
+}
+put [at 38:1]
+ arg: cond
+ arg: {blobb}
+switch cond [at 39:1]
+ case {lob}
+  print [at 40:15]
+   arg: {"then"
+}
+ default
+  print [at 41:15]
+   arg: {"else"
+}
diff --git a/scconfig/src/tmpasm/regression/Tutor06_foreach.gasm b/scconfig/src/tmpasm/regression/Tutor06_foreach.gasm
new file mode 100644
index 0000000..c0e57fc
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor06_foreach.gasm
@@ -0,0 +1,35 @@
+# The only loop tmpasm implements is a foreach that iterates on a list.
+# How the list is split into items is up to the environment. In scconfig
+# the list is white space separated by default. The word following foreach
+# must be the name of a variable (that will be set to the next item before
+# each iteration). The word after "in" is data (string, variable, block).
+
+foreach item in {this is a list of words}
+	print item {\n}
+end
+
+# Like any other control, foreach can be nested. The following
+# example will iterate item on foo, bar and baz, printing 3 words
+# for each from a block: {next:}, the item and a newline. The newline
+# is specified as a vairable since \ escaping does not work in blocks.
+put nl {\n}
+foreach item in {foo bar baz}
+	foreach w in [@next: @item@@nl@@]
+		print w
+	end
+end
+
+# Foreach makes a copy of the list before the first iteration. This
+# is relevant if the list is a variable that may change during the
+# loop. The following exmaple takes a list of libs and if -lsdl is
+# present on the list, appends -lsvga to the list and inserts -lm;
+# these changes to "libs" will not alter the loop.
+put libs {-lsdl -ltcl8.4}
+foreach l in libs
+	print {l=} l {\n}
+	switch l
+		case {^-lsdl}   put libs [@-lm @libs@ -lsvga@]; end
+	end
+end
+print {libs=} libs {\n}
+
diff --git a/scconfig/src/tmpasm/regression/Tutor06_foreach.ref b/scconfig/src/tmpasm/regression/Tutor06_foreach.ref
new file mode 100644
index 0000000..f8fccde
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor06_foreach.ref
@@ -0,0 +1,32 @@
+foreach item in {this is a list of words} [at 7:1]
+ print [at 8:2]
+  arg: item
+  arg: {
+}
+put [at 15:1]
+ arg: nl
+ arg: {
+}
+foreach item in {foo bar baz} [at 16:1]
+ foreach w in [~next: ~item~~nl~~] [at 17:2]
+  print [at 18:3]
+   arg: w
+put [at 27:1]
+ arg: libs
+ arg: {-lsdl -ltcl8.4}
+foreach l in libs [at 28:1]
+ print [at 29:2]
+  arg: {l=}
+  arg: l
+  arg: {
+}
+ switch l [at 30:2]
+  case {^-lsdl}
+   put [at 31:19]
+    arg: libs
+    arg: [~-lm ~libs~ -lsvga~]
+print [at 34:1]
+ arg: {libs=}
+ arg: libs
+ arg: {
+}
diff --git a/scconfig/src/tmpasm/regression/Tutor07_sub.gasm b/scconfig/src/tmpasm/regression/Tutor07_sub.gasm
new file mode 100644
index 0000000..ee3f4e2
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor07_sub.gasm
@@ -0,0 +1,31 @@
+# The following regex sub utils are scconfig specific.
+
+# Regex: substitute the first match of a pattern with str in a variable:
+#   sub address pattern str
+
+put myvar {Hello world!\n}
+sub myvar {l} {2}
+print myvar
+
+# Address must resolve to an existing variable, pattern and str are data;
+# this means address can be a string that holds a variable name, it's the
+# same as if it was an addreess:
+sub {myvar} {l} {3}
+print myvar
+
+# Or it can be a block, which makes indirect addressing possible:
+# in [@@pointer@@] the @pointer@ part will be substituted with
+# the value of pointer, which is "myvar".
+put pointer {myvar}
+sub [@@pointer@@] {l} {4}
+print myvar
+
+# Since pattern and str are also data, address and blocks work there as well
+# (but this is _not_ a regex backreference):
+put punctuation {[!?.]}
+sub [@@pointer@@] punctuation [@ PUNCT:@punctuation@@]
+print myvar
+
+# gsub does the same, but substutites all matches, not only the first:
+gsub [@@pointer@@] {o} {_0_}
+print myvar
diff --git a/scconfig/src/tmpasm/regression/Tutor07_sub.ref b/scconfig/src/tmpasm/regression/Tutor07_sub.ref
new file mode 100644
index 0000000..a59ad25
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor07_sub.ref
@@ -0,0 +1,40 @@
+put [at 6:1]
+ arg: myvar
+ arg: {Hello world!
+}
+sub [at 7:1]
+ arg: myvar
+ arg: {l}
+ arg: {2}
+print [at 8:1]
+ arg: myvar
+sub [at 13:1]
+ arg: {myvar}
+ arg: {l}
+ arg: {3}
+print [at 14:1]
+ arg: myvar
+put [at 19:1]
+ arg: pointer
+ arg: {myvar}
+sub [at 20:1]
+ arg: [~~pointer~~]
+ arg: {l}
+ arg: {4}
+print [at 21:1]
+ arg: myvar
+put [at 25:1]
+ arg: punctuation
+ arg: {[!?.]}
+sub [at 26:1]
+ arg: [~~pointer~~]
+ arg: punctuation
+ arg: [~ PUNCT:~punctuation~~]
+print [at 27:1]
+ arg: myvar
+gsub [at 30:1]
+ arg: [~~pointer~~]
+ arg: {o}
+ arg: {_0_}
+print [at 31:1]
+ arg: myvar
diff --git a/scconfig/src/tmpasm/regression/Tutor08_uniq.gasm b/scconfig/src/tmpasm/regression/Tutor08_uniq.gasm
new file mode 100644
index 0000000..bda1026
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor08_uniq.gasm
@@ -0,0 +1,40 @@
+# The following string util is scconfig specific.
+
+# Uniq: filter a list of words and remove duplicate items. This instruction
+# is useful for using text nodes as lists.
+# The simplest syntax is "uniq address" which will do the filtering on
+# the content of a database address. The default separator is \n
+put list [@this
+is
+a
+list
+of
+words,
+a
+list
+of
+words.
+@]
+print {original:\n} list {\n}
+uniq list
+print {uniq:\n} list {\n}
+
+# If the original list needs to be left intact, the alternative syntax is
+# "uniq dest-addr src-addr":
+put foo [@this
+foo
+is
+a
+this
+foo
+@]
+uniq tmp foo
+print {original:\n} foo {\nuniq:\n} tmp {\n}
+
+# Note: the algorithm of uniq is slow, and will not be efficient for very long
+# lists. Uniq preserves the order of words (by their first appearance).
+
+# Sortuniq performs the same action, except it also orders the list using
+# qsort() (so it is even slower on big lists).
+sortuniq tmp foo
+print {\nsortuniq:\n} tmp {\n}
diff --git a/scconfig/src/tmpasm/regression/Tutor08_uniq.ref b/scconfig/src/tmpasm/regression/Tutor08_uniq.ref
new file mode 100644
index 0000000..bf9354d
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor08_uniq.ref
@@ -0,0 +1,59 @@
+put [at 7:1]
+ arg: list
+ arg: {this
+is
+a
+list
+of
+words,
+a
+list
+of
+words.
+}
+print [at 18:1]
+ arg: {original:
+}
+ arg: list
+ arg: {
+}
+uniq [at 19:1]
+ arg: list
+print [at 20:1]
+ arg: {uniq:
+}
+ arg: list
+ arg: {
+}
+put [at 24:1]
+ arg: foo
+ arg: {this
+foo
+is
+a
+this
+foo
+}
+uniq [at 31:1]
+ arg: tmp
+ arg: foo
+print [at 32:1]
+ arg: {original:
+}
+ arg: foo
+ arg: {
+uniq:
+}
+ arg: tmp
+ arg: {
+}
+sortuniq [at 39:1]
+ arg: tmp
+ arg: foo
+print [at 40:1]
+ arg: {
+sortuniq:
+}
+ arg: tmp
+ arg: {
+}
diff --git a/scconfig/src/tmpasm/regression/Tutor09_ui.gasm b/scconfig/src/tmpasm/regression/Tutor09_ui.gasm
new file mode 100644
index 0000000..b4a7730
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor09_ui.gasm
@@ -0,0 +1,15 @@
+# The following user interface utils are scconfig specific.
+
+# The report infrstructure is the main UI in scconfig. It prints
+# messages to the console. The "report" instruction works similar
+# to print, but its output is always the console, immune to
+# redirections and default file output (tmpasm is most commonly
+# used for generating files, so the default output file is not
+# the console but a file being generated)
+put myvar {!\n}
+report {hello } {world} myvar
+report [@hello world at myvar@@]
+
+# If an error is detected during generation of a file, the script should abort.
+# This is a direct call to abort(2).
+abort
diff --git a/scconfig/src/tmpasm/regression/Tutor09_ui.ref b/scconfig/src/tmpasm/regression/Tutor09_ui.ref
new file mode 100644
index 0000000..2873077
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor09_ui.ref
@@ -0,0 +1,11 @@
+put [at 9:1]
+ arg: myvar
+ arg: {!
+}
+report [at 10:1]
+ arg: {hello }
+ arg: {world}
+ arg: myvar
+report [at 11:1]
+ arg: [~hello world~myvar~~]
+abort [at 15:1]
diff --git a/scconfig/src/tmpasm/regression/Tutor10_include_redir.gasm b/scconfig/src/tmpasm/regression/Tutor10_include_redir.gasm
new file mode 100644
index 0000000..0fb1b52
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor10_include_redir.gasm
@@ -0,0 +1,52 @@
+# NOTE: THIS EXAMPLE WILL NOT WORK IN THE ON-LINE WEB VERSION
+
+# These features are scconfig specific.
+
+# There is a default output file coming from the environment; this
+# is the file being generated. In the most common cases this is the
+# only file the script will ever write. Any "print" instruction will
+# write this file by default. However, sometimes it is handy
+# to generate a small misc file during generating a large file. Thus
+# the output file that "print" writes is not hardwired. Instead, there
+# is the default output file and the current output file. Instruction
+# "redir" can change the current output file.
+
+print {this goes to the default output\n}
+
+# redirect to Tutor10.inc; any "print" until the next "redir" will
+# write that file
+redir {Tutor10.inc}
+print {# this is a generated file.}
+print [@
+	print {hello world from my include!\n}
+@]
+
+# switch back to the default output
+redir
+print {back at default output.\n}
+
+
+# Dynamic include: the script may include another script, runtime. When
+# an include instruction is executed, the referred file is open, read,
+# parsed and executed, recusively.
+#
+# Include being dynamic (or runtime) is unusual, but has the following
+# advantages:
+#  - file name for inclusion can be calculated
+#  - conditional include is easily possible without an extra preprocessing layer
+#  - it is possible to generate a script on the fly and include it (sort of eval)
+# Drawbacks:
+#  - it is possible to end up in an infinite loop that will only stop when
+#    resources run out (open fds or memory)
+#  - it is slow, e.g. if the body of a foreach contains include, the whole
+#    read-parse-execute procedure is repeated for each item
+# Redir files are overwritten when first open from an execution.
+
+print {Include:\n}
+include {Tutor10.inc}
+
+# NOTE: the above script works only because "redir" has a side effect:
+# whenever redirection switches away from a file, that file is flushed.
+# This happens even if the new current output is the same as the old
+# current output (no actual switch takes place).
+
diff --git a/scconfig/src/tmpasm/regression/Tutor10_include_redir.ref b/scconfig/src/tmpasm/regression/Tutor10_include_redir.ref
new file mode 100644
index 0000000..c1349f0
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor10_include_redir.ref
@@ -0,0 +1,20 @@
+print [at 14:1]
+ arg: {this goes to the default output
+}
+redir [at 18:1]
+ arg: {Tutor10.inc}
+print [at 19:1]
+ arg: {# this is a generated file.}
+print [at 20:1]
+ arg: {
+	print {hello world from my include!\n}
+}
+redir [at 25:1]
+print [at 26:1]
+ arg: {back at default output.
+}
+print [at 45:1]
+ arg: {Include:
+}
+include [at 46:1]
+ arg: {Tutor10.inc}
diff --git a/scconfig/src/tmpasm/regression/Tutor11_missing.out b/scconfig/src/tmpasm/regression/Tutor11_missing.out
new file mode 100644
index 0000000..0d67ac2
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor11_missing.out
@@ -0,0 +1,10 @@
+if ?a [at 5:7]
+then:
+ print [at 6:2]
+  arg: {empty}
+else:
+ print [at 8:2]
+  arg: {not empty}
+put [at 11:1]
+ arg: b
+ arg: ?a
diff --git a/scconfig/src/tmpasm/regression/Tutor11_missing.ref b/scconfig/src/tmpasm/regression/Tutor11_missing.ref
new file mode 100644
index 0000000..0d67ac2
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/Tutor11_missing.ref
@@ -0,0 +1,10 @@
+if ?a [at 5:7]
+then:
+ print [at 6:2]
+  arg: {empty}
+else:
+ print [at 8:2]
+  arg: {not empty}
+put [at 11:1]
+ arg: b
+ arg: ?a
diff --git a/scconfig/src/tmpasm/regression/append.gasm b/scconfig/src/tmpasm/regression/append.gasm
new file mode 100644
index 0000000..b38a10b
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/append.gasm
@@ -0,0 +1,6 @@
+append tmp {foo}
+append tmp {bar}
+append tmp {baz}
+foreach n in tmp
+	print {-> } n {\n}
+end
diff --git a/scconfig/src/tmpasm/regression/append.ref b/scconfig/src/tmpasm/regression/append.ref
new file mode 100644
index 0000000..b7fea30
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/append.ref
@@ -0,0 +1,15 @@
+append [at 1:1]
+ arg: tmp
+ arg: {foo}
+append [at 2:1]
+ arg: tmp
+ arg: {bar}
+append [at 3:1]
+ arg: tmp
+ arg: {baz}
+foreach n in tmp [at 4:1]
+ print [at 5:2]
+  arg: {-> }
+  arg: n
+  arg: {
+}
diff --git a/scconfig/src/tmpasm/regression/comment.gasm b/scconfig/src/tmpasm/regression/comment.gasm
new file mode 100644
index 0000000..c907351
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/comment.gasm
@@ -0,0 +1,3 @@
+print data1 # this is a comment; until the end of this line
+print data2 # this shall be a second print
+
diff --git a/scconfig/src/tmpasm/regression/comment.ref b/scconfig/src/tmpasm/regression/comment.ref
new file mode 100644
index 0000000..e1d68d1
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/comment.ref
@@ -0,0 +1,4 @@
+print [at 1:1]
+ arg: data1
+print [at 2:1]
+ arg: data2
diff --git a/scconfig/src/tmpasm/regression/err_excess_end.gasm b/scconfig/src/tmpasm/regression/err_excess_end.gasm
new file mode 100644
index 0000000..cace30d
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_excess_end.gasm
@@ -0,0 +1,3 @@
+if {1} then
+end
+end
diff --git a/scconfig/src/tmpasm/regression/err_excess_end.ref b/scconfig/src/tmpasm/regression/err_excess_end.ref
new file mode 100644
index 0000000..73807fb
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_excess_end.ref
@@ -0,0 +1,6 @@
+error: Excess "end" at 3:4
+if {1} [at 1:8]
+then:
+ (NOP)
+else:
+ (NOP)
diff --git a/scconfig/src/tmpasm/regression/err_if_else.gasm b/scconfig/src/tmpasm/regression/err_if_else.gasm
new file mode 100644
index 0000000..318e019
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_if_else.gasm
@@ -0,0 +1,4 @@
+if v
+else
+	print {else}
+end
diff --git a/scconfig/src/tmpasm/regression/err_if_else.ref b/scconfig/src/tmpasm/regression/err_if_else.ref
new file mode 100644
index 0000000..6514b4a
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_if_else.ref
@@ -0,0 +1,6 @@
+error: unexpected 'else' - must be in a 'then' block before an else at 2:5
+if *NULL - broken AST* [at 1:1]
+then:
+ (NOP)
+else:
+ (NOP)
diff --git a/scconfig/src/tmpasm/regression/err_if_end.gasm b/scconfig/src/tmpasm/regression/err_if_end.gasm
new file mode 100644
index 0000000..3acbe6d
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_if_end.gasm
@@ -0,0 +1,2 @@
+if v
+end
diff --git a/scconfig/src/tmpasm/regression/err_if_end.ref b/scconfig/src/tmpasm/regression/err_if_end.ref
new file mode 100644
index 0000000..90b5430
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_if_end.ref
@@ -0,0 +1,6 @@
+error: unexpected "end" in "if" - expected "then" at 2:4
+if v [at 2:1]
+then:
+ (NOP)
+else:
+ (NOP)
diff --git a/scconfig/src/tmpasm/regression/err_no_end.gasm b/scconfig/src/tmpasm/regression/err_no_end.gasm
new file mode 100644
index 0000000..82a1bf0
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_no_end.gasm
@@ -0,0 +1,3 @@
+pritn {foo}
+switch {cond}
+	case {1} print {foo}; end
diff --git a/scconfig/src/tmpasm/regression/err_no_end.ref b/scconfig/src/tmpasm/regression/err_no_end.ref
new file mode 100644
index 0000000..34646b1
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_no_end.ref
@@ -0,0 +1,6 @@
+pritn [at 1:1]
+ arg: {foo}
+switch {cond} [at 2:1]
+ case {1}
+  print [at 3:11]
+   arg: {foo}
diff --git a/scconfig/src/tmpasm/regression/err_switch_end.gasm b/scconfig/src/tmpasm/regression/err_switch_end.gasm
new file mode 100644
index 0000000..2dffb08
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_switch_end.gasm
@@ -0,0 +1,2 @@
+switch
+end
diff --git a/scconfig/src/tmpasm/regression/err_switch_end.ref b/scconfig/src/tmpasm/regression/err_switch_end.ref
new file mode 100644
index 0000000..7747386
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_switch_end.ref
@@ -0,0 +1,2 @@
+error: unexpected end of if switch statement; expected a data at 1:7
+(NOP)
diff --git a/scconfig/src/tmpasm/regression/err_switch_nocond.gasm b/scconfig/src/tmpasm/regression/err_switch_nocond.gasm
new file mode 100644
index 0000000..67520eb
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_switch_nocond.gasm
@@ -0,0 +1,3 @@
+switch
+case data print {foo}; end
+end
diff --git a/scconfig/src/tmpasm/regression/err_switch_nocond.ref b/scconfig/src/tmpasm/regression/err_switch_nocond.ref
new file mode 100644
index 0000000..7747386
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/err_switch_nocond.ref
@@ -0,0 +1,2 @@
+error: unexpected end of if switch statement; expected a data at 1:7
+(NOP)
diff --git a/scconfig/src/tmpasm/regression/foreach.gasm b/scconfig/src/tmpasm/regression/foreach.gasm
new file mode 100644
index 0000000..268aeed
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/foreach.gasm
@@ -0,0 +1,18 @@
+put a {1}
+put foo {example-FOO}
+put bar {example-BAR}
+put baz {example-BAZ}
+put hah {haha}
+
+# should set n to the string foo, the value of bar and the string baz
+# per iteration
+foreach n in [~foo ~bar~ baz~]
+	print {n=} n {\n}
+	print {a11} [@a12 @hah@ a14@] {\n}
+	print {a21} {a22 a23} {\n}
+	print {a31\n}
+end
+print {a41} {a42} {a43} {\n}
+print
+
+
diff --git a/scconfig/src/tmpasm/regression/foreach.ref b/scconfig/src/tmpasm/regression/foreach.ref
new file mode 100644
index 0000000..6e6d67c
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/foreach.ref
@@ -0,0 +1,41 @@
+put [at 1:1]
+ arg: a
+ arg: {1}
+put [at 2:1]
+ arg: foo
+ arg: {example-FOO}
+put [at 3:1]
+ arg: bar
+ arg: {example-BAR}
+put [at 4:1]
+ arg: baz
+ arg: {example-BAZ}
+put [at 5:1]
+ arg: hah
+ arg: {haha}
+foreach n in [~foo ~bar~ baz~] [at 9:1]
+ print [at 10:2]
+  arg: {n=}
+  arg: n
+  arg: {
+}
+ print [at 11:2]
+  arg: {a11}
+  arg: [~a12 ~hah~ a14~]
+  arg: {
+}
+ print [at 12:2]
+  arg: {a21}
+  arg: {a22 a23}
+  arg: {
+}
+ print [at 13:2]
+  arg: {a31
+}
+print [at 15:1]
+ arg: {a41}
+ arg: {a42}
+ arg: {a43}
+ arg: {
+}
+print [at 16:1]
diff --git a/scconfig/src/tmpasm/regression/if.gasm b/scconfig/src/tmpasm/regression/if.gasm
new file mode 100644
index 0000000..19f4f47
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/if.gasm
@@ -0,0 +1,10 @@
+put a 1
+if a then
+	if b then
+		print {then-then}
+	else
+		print {then-else}
+	end
+else
+	print {else}
+end
diff --git a/scconfig/src/tmpasm/regression/if.ref b/scconfig/src/tmpasm/regression/if.ref
new file mode 100644
index 0000000..7290a2b
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/if.ref
@@ -0,0 +1,15 @@
+put [at 1:1]
+ arg: a
+ arg: 1
+if a [at 2:6]
+then:
+ if b [at 3:7]
+ then:
+  print [at 4:3]
+   arg: {then-then}
+ else:
+  print [at 6:3]
+   arg: {then-else}
+else:
+ print [at 9:2]
+  arg: {else}
diff --git a/scconfig/src/tmpasm/regression/switch.gasm b/scconfig/src/tmpasm/regression/switch.gasm
new file mode 100644
index 0000000..75c9d9e
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/switch.gasm
@@ -0,0 +1,8 @@
+put swdata {lol}
+switch swdata
+	case data1            print {1a} {11}; print {1b} 12; print {1c} 13; print {1d} 14; end;
+	case [~data2 ~a~~]    print {2a} 21;   print {2b} 22; print {2c} 23; print {2d} 24; end;
+	default               print {3a} 31;   print {3b} 32; print {3c} 33; print {3d} 34; end;
+end;
+print {i1}
+print {i2}
diff --git a/scconfig/src/tmpasm/regression/switch.ref b/scconfig/src/tmpasm/regression/switch.ref
new file mode 100644
index 0000000..db8c6e4
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/switch.ref
@@ -0,0 +1,47 @@
+put [at 1:1]
+ arg: swdata
+ arg: {lol}
+switch swdata [at 2:1]
+ case data1
+  print [at 3:24]
+   arg: {1a}
+   arg: {11}
+  print [at 3:41]
+   arg: {1b}
+   arg: 12
+  print [at 3:56]
+   arg: {1c}
+   arg: 13
+  print [at 3:71]
+   arg: {1d}
+   arg: 14
+ case [~data2 ~a~~]
+  print [at 4:24]
+   arg: {2a}
+   arg: 21
+  print [at 4:41]
+   arg: {2b}
+   arg: 22
+  print [at 4:56]
+   arg: {2c}
+   arg: 23
+  print [at 4:71]
+   arg: {2d}
+   arg: 24
+ default
+  print [at 5:24]
+   arg: {3a}
+   arg: 31
+  print [at 5:41]
+   arg: {3b}
+   arg: 32
+  print [at 5:56]
+   arg: {3c}
+   arg: 33
+  print [at 5:71]
+   arg: {3d}
+   arg: 34
+print [at 7:1]
+ arg: {i1}
+print [at 8:1]
+ arg: {i2}
diff --git a/scconfig/src/tmpasm/regression/test.gasm b/scconfig/src/tmpasm/regression/test.gasm
new file mode 100644
index 0000000..72b2738
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/test.gasm
@@ -0,0 +1,47 @@
+### set up internal variables ###
+put /local/cflags {-std=c99 -Wall}
+put /local/ldflags {-lm}
+put /local/objs {main.o foo.o bar.o}
+
+# turn off optimization and add -g in debug mode
+if /local/debug then
+	append /local/cflags {-g}
+else
+	append /local/cflags {-O2}
+end
+
+# if somelib is selected, add -I and -l
+isempty /local/r /local/somelib
+invert  /local/r
+if /local/r then
+	append /local/cflags { -I/usr/include/somelib}
+	append /local/ldflags { -lsomelib}
+end
+
+### Generate the Makefile ###
+print [@
+# Makefile generated by scconfig - DO NOT EDIT - please edit Makefile.in
+CFLAGS=@/local/cflags@
+LDFLAGS=@/local/ldflags@
+OBJS=@/local/objs@
+
+all: main
+
+main: $(OBJS)
+	$(CC) $(LDFLAGS)
+
+@]
+
+# loop over each object and generate an explicit rule
+# (we are generating a dumb Makefile that would work with any
+# old version of make)
+foreach /local/o in /local/objs
+	put /local/c /local/o
+	sub /local/c {.o$} {.c}
+	print [@
+@/local/o@: @/local/c@
+	$(CC) -c $(CFLAGS) @/local/c@ -o @/local/o@
+	@]
+end
+
+print {#end\n}
diff --git a/scconfig/src/tmpasm/regression/test.ref b/scconfig/src/tmpasm/regression/test.ref
new file mode 100644
index 0000000..8cc2e5e
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/test.ref
@@ -0,0 +1,62 @@
+put [at 2:1]
+ arg: /local/cflags
+ arg: {-std=c99 -Wall}
+put [at 3:1]
+ arg: /local/ldflags
+ arg: {-lm}
+put [at 4:1]
+ arg: /local/objs
+ arg: {main.o foo.o bar.o}
+if /local/debug [at 7:17]
+then:
+ append [at 8:2]
+  arg: /local/cflags
+  arg: {-g}
+else:
+ append [at 10:2]
+  arg: /local/cflags
+  arg: {-O2}
+isempty [at 14:1]
+ arg: /local/r
+ arg: /local/somelib
+invert [at 15:1]
+ arg: /local/r
+if /local/r [at 16:13]
+then:
+ append [at 17:2]
+  arg: /local/cflags
+  arg: { -I/usr/include/somelib}
+ append [at 18:2]
+  arg: /local/ldflags
+  arg: { -lsomelib}
+else:
+ (NOP)
+print [at 22:1]
+ arg: [~
+# Makefile generated by scconfig - DO NOT EDIT - please edit Makefile.in
+CFLAGS=~/local/cflags~
+LDFLAGS=~/local/ldflags~
+OBJS=~/local/objs~
+
+all: main
+
+main: $(OBJS)
+	$(CC) $(LDFLAGS)
+
+~]
+foreach /local/o in /local/objs [at 38:1]
+ put [at 39:2]
+  arg: /local/c
+  arg: /local/o
+ sub [at 40:2]
+  arg: /local/c
+  arg: {.o$}
+  arg: {.c}
+ print [at 41:2]
+  arg: [~
+~/local/o~: ~/local/c~
+	$(CC) -c $(CFLAGS) ~/local/c~ -o ~/local/o~
+	~]
+print [at 47:1]
+ arg: {#end
+}
diff --git a/scconfig/src/tmpasm/regression/then.gasm b/scconfig/src/tmpasm/regression/then.gasm
new file mode 100644
index 0000000..a2e3828
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/then.gasm
@@ -0,0 +1,6 @@
+# test if without an else
+if cnd then
+	print a1 a2 a3
+end
+
+print a1 a2 a3
diff --git a/scconfig/src/tmpasm/regression/then.ref b/scconfig/src/tmpasm/regression/then.ref
new file mode 100644
index 0000000..680e6c1
--- /dev/null
+++ b/scconfig/src/tmpasm/regression/then.ref
@@ -0,0 +1,12 @@
+if cnd [at 2:8]
+then:
+ print [at 3:2]
+  arg: a1
+  arg: a2
+  arg: a3
+else:
+ (NOP)
+print [at 6:1]
+ arg: a1
+ arg: a2
+ arg: a3
diff --git a/scconfig/src/tmpasm/tester.c b/scconfig/src/tmpasm/tester.c
new file mode 100644
index 0000000..a7007f3
--- /dev/null
+++ b/scconfig/src/tmpasm/tester.c
@@ -0,0 +1,64 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "tmpasm.h"
+#include "tmpasm_scconfig.h"
+#include "debug.h"
+#include "db.h"
+
+tmpasm_t *ctx;
+
+void re_fail(char *s, char c)
+{
+	fprintf(stderr, "Regex error: %s [opcode %o]\n", s, c);
+	abort();
+}
+
+static void do_dump()
+{
+	tmpasm_dump(ctx, stdout);
+}
+
+static void do_exec()
+{
+	if (ctx->dead)
+		fprintf(stderr, "Can not execute the script due to the above compilation error.\n");
+	else
+		tmpasm_execute(ctx);
+}
+
+static void scc_init(void)
+{
+	db_init();
+	db_mkdir("/local");
+	db_cd("/local");
+}
+
+int main(int argc, char *argv[])
+{
+	scc_init();
+	ctx = tmpasm_init(&scc_cb);
+	scc_tmpasm_parse(ctx, NULL, stdin, stdout);
+
+	if (argc > 1) {
+		char *cmd;
+		cmd = argv[1];
+		while(*cmd == '-') cmd++;
+		switch(*cmd) {
+			case 'd': do_dump(); break;
+			case 'e': do_exec(); break;
+		}
+	}
+	else
+		do_dump();
+
+	if (ctx->runtime_error != 0) {
+		const char *fmt = tmpasm_runtime_error_fmt(ctx);
+		fprintf(stderr, "Runtime error at %d:%d: ", ctx->runtime_error_line, ctx->runtime_error_col);
+		fprintf(stderr, fmt, (ctx->runtime_error_data == NULL ? "" : ctx->runtime_error_data));
+		fprintf(stderr, "\n");
+	}
+
+	tmpasm_uninit(ctx);
+	db_uninit();
+	return 0;
+}
diff --git a/scconfig/src/tmpasm/tmpasm.c b/scconfig/src/tmpasm/tmpasm.c
new file mode 100644
index 0000000..798064e
--- /dev/null
+++ b/scconfig/src/tmpasm/tmpasm.c
@@ -0,0 +1,807 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "tmpasm.h"
+#include "debug.h"
+
+#define is_space(c)  (((c) == ' ') || ((c) == '\t'))
+#define is_sep(c)    (((c) == '\n') || ((c) == '\r') || ((c) == ';'))
+#define is_addr(c)   ( (((c) >= '0') && ((c) <= '9')) || (((c) >= 'a') && ((c) <= 'z')) || (((c) >= 'A') && ((c) <= 'Z')) || ((c) == '_') || ((c) == '?') || ((c) == '.') || ((c) == ',')  || ((c) == ',') || ((c) == '-') || ((c) == '/') )
+
+/* this local copy is to make tmpasm compile independently */
+static char *strclone(const char *str)
+{
+	int l;
+	char *ret;
+
+	if (str == NULL)
+		return NULL;
+
+	l   = strlen(str)+1;
+	ret = malloc(l);
+	memcpy(ret, str, l);
+	return ret;
+}
+
+
+#define TOP ctx->st
+
+static const char *kw_names[] = {"-", "if", "then", "else", "end", "foreach", "in", "switch", "case", "default", "nop", NULL };
+
+static tmpasm_kw_t kw_lookup(const char *str)
+{
+	const char **k;
+	tmpasm_kw_t i;
+
+/* slow linear search is enough: we have only a few keywords */
+	for(k = kw_names, i = KW_none; *k != NULL; k++,i++)
+		if (strcmp(*k, str) == 0)
+			return i;
+	return KW_none;
+}
+
+
+tmpasm_exec_t *code_new(tmpasm_kw_t kw)
+{
+	tmpasm_exec_t *c;
+	c = calloc(sizeof(tmpasm_exec_t), 1);
+	c->kw = kw;
+	return c;
+}
+
+/*tmpasm_exec_t *code_end(tmpasm_exec_t *start)
+{
+	while(start->next != NULL)
+		start = start->next;
+	return start;
+}*/
+
+tmpasm_exec_t *code_append(tmpasm_t *ctx, tmpasm_kw_t kw)
+{
+	tmpasm_exec_t *c;
+/*	c = code_end(TOP->code);*/
+	c = TOP->last_code;
+	if (TOP->last_code->kw != KW_NOP) {
+		c->next = code_new(kw);
+		return c->next;
+	}
+	c->kw = kw;
+	return c;
+}
+
+static void error(tmpasm_t *ctx, char c, char *msg)
+{
+	fprintf(stderr, "error: %s at %d:%d\n", msg, ctx->line, ctx->col);
+	if (c != 0)
+		fprintf(stderr, " character last seen: %c\n", c);
+	ctx->dead = 1;
+}
+
+static void push(tmpasm_t *ctx, tmpasm_kw_t kw, tmpasm_state_t st, tmpasm_exec_t *code)
+{
+	tmpasm_stack_t *new;
+	new = calloc(sizeof(tmpasm_stack_t), 1);
+	new->kw = kw;
+	new->state = st;
+	new->next = ctx->st;
+	new->last_code = code;
+	ctx->st = new;
+}
+
+static void pop_(tmpasm_t *ctx, int chk_underfl)
+{
+	tmpasm_stack_t *old;
+	old = ctx->st;
+	ctx->st = old->next;
+
+	/* stack underflow? */
+	if (chk_underfl) {
+		if (TOP == NULL) {
+			error(ctx, 0, "Excess \"end\"");
+			TOP = old;
+			return;
+		}
+	}
+	if (old->argv != NULL)
+		free(old->argv);
+	if (old->argend != NULL)
+		free(old->argend);
+	if (old->arg_used != NULL)
+		free(old->arg_used);
+	if (old->arg_alloced != NULL)
+		free(old->arg_alloced);
+	free(old);
+}
+
+static void pop(tmpasm_t *ctx)
+{
+	pop_(ctx, 1);
+}
+
+
+#define grow(arr, size) arr = realloc(arr, sizeof((arr)[0]) * size)
+
+static void arg_new(tmpasm_t *ctx, int is_addr)
+{
+	if (TOP->args_used >= TOP->args_alloced) {
+		TOP->args_alloced = TOP->args_alloced + 16;
+		grow(TOP->argv, TOP->args_alloced);
+		grow(TOP->argend, TOP->args_alloced);
+		grow(TOP->arg_alloced, TOP->args_alloced);
+		grow(TOP->arg_used, TOP->args_alloced);
+	}
+
+	TOP->arg_alloced[TOP->args_used] = 64;
+	TOP->arg_used[TOP->args_used] = 0;
+
+	TOP->argv[TOP->args_used] = malloc(TOP->arg_alloced[TOP->args_used]+sizeof(tmpasm_arg_t));
+	TOP->argv[TOP->args_used]->is_addr = is_addr;
+	TOP->argv[TOP->args_used]->next = NULL;
+	TOP->argend[TOP->args_used] = TOP->argv[TOP->args_used];
+
+	TOP->args_used++;
+}
+
+
+static void arg_append(tmpasm_t *ctx, char c)
+{
+	int i = TOP->args_used - 1;
+
+	if (TOP->arg_used[i] >= TOP->arg_alloced[i]) {
+		tmpasm_arg_t *prev, *last;
+
+		/* since argend[i] is also in the ->next pointer of the previous item in a block chain, we need to look it up */
+		for(prev = NULL, last = TOP->argv[i]; last->next != NULL; last = last->next)
+			prev = last;
+
+		TOP->arg_alloced[i] += 64;
+		last = realloc(last, TOP->arg_alloced[i]+sizeof(tmpasm_arg_t));
+
+		if (prev == NULL)
+			TOP->argv[i] = last;
+		else
+			prev->next = last;
+
+		TOP->argend[i] = last;
+	}
+	TOP->argend[i]->data[TOP->arg_used[i]] = c;
+	TOP->arg_used[i]++;
+}
+
+static void arg_free(tmpasm_arg_t *a)
+{
+	tmpasm_arg_t *next;
+	if (a == NULL)
+		return;
+	next = a->next;
+	free(a);
+	if (next != NULL)
+		arg_free(next);
+}
+
+static void arg_new_next(tmpasm_t *ctx, int is_addr)
+{
+	tmpasm_arg_t *a;
+	int id;
+
+	arg_append(ctx, '\0');
+
+	id = TOP->args_used - 1;
+	assert(id>=0);
+	TOP->arg_alloced[id] = 64;
+	TOP->arg_used[id] = 0;
+
+	a = malloc(TOP->arg_alloced[id]+sizeof(tmpasm_arg_t));
+	strcpy(a->data, "QWERT");
+	a->is_addr = is_addr;
+	a->next = NULL;
+	TOP->argend[id]->next = a;
+	TOP->argend[id] = a;
+}
+
+static void arg_remove(tmpasm_t *ctx)
+{
+	assert(TOP->args_used == 1);
+	TOP->args_used = 0;
+	TOP->argv[0] = NULL;
+	TOP->argend[0] = NULL;
+	TOP->arg_alloced[0] = 0;
+	TOP->arg_used[0] = 0;
+}
+
+static int arg_is_addr(tmpasm_arg_t *a)
+{
+	return (a->next == NULL) && (a->is_addr);
+}
+
+static void arg_end(tmpasm_t *ctx, int cmd_ctx)
+{
+	tmpasm_arg_t *a;
+	arg_append(ctx, '\0');
+
+	a = TOP->argv[TOP->args_used-1];
+	if (cmd_ctx) {
+		/* when argument ends in a command context (not in a block inline), we
+		   may may need to switch back to command mode; example: after
+		   the cond of an "if cond then"*/
+		switch(TOP->kw) {
+			case KW_IF:
+				TOP->state = ST_PRECMD;
+				break;
+			case KW_FOREACH:
+				if (!arg_is_addr(a)) {
+					error(ctx, 0, "variable of a foreach must be an address");
+					return;
+				}
+				TOP->last_code->payload.fc_foreach.loop_var = strclone(a->data);
+				arg_free(a);
+				arg_remove(ctx);
+				TOP->state = ST_PRECMD;
+				break;
+			case KW_IN:
+				/* pop will free the argv[] array, but not the elements so "a" is safe to use after this line */
+				pop(ctx);
+				/* in foreach context, after the IN-data */
+				TOP->last_code->payload.fc_foreach.data = a;
+
+				/* we are in the body now, TOP is the foreach context, last_code is body */
+				TOP->last_code->payload.fc_foreach.code_body = code_new(KW_NOP);
+				push(ctx, KW_none, ST_PRECMD, TOP->last_code->payload.fc_foreach.code_body);
+				break;
+			case KW_CASE:
+				ctx->st->next->last_code->payload.fc_switch.last->data = a;
+				arg_remove(ctx);
+				push(ctx, KW_none, ST_PRECMD, TOP->last_code);
+				break;
+			case KW_SWITCH:
+				TOP->last_code->payload.fc_switch.cond = a;
+				arg_remove(ctx);
+				TOP->state = ST_PRECMD;
+				break;
+			default:
+				TOP->state = ST_PREDATA;
+		}
+	}
+}
+
+
+
+/* end of statement; update kw state for a composite control kw; for the rest
+   just call the lib */
+static void end_of_statement(tmpasm_t *ctx)
+{
+	switch(TOP->kw) {
+		case KW_none:
+		case KW_THEN:
+		case KW_ELSE:
+		case KW_CASE:
+		case KW_DEFAULT:
+			TOP->last_code->payload.instr.argc = TOP->args_used;
+			TOP->last_code->payload.instr.argv = TOP->argv;
+			TOP->argv = NULL;
+			free(TOP->argend);
+			TOP->argend = NULL;
+			TOP->args_used = 0;
+			TOP->args_alloced = 0;
+			break;
+		default:
+			/* don't mess with the payload */
+			;
+	}
+	TOP->state = ST_PRECMD;
+}
+
+#define loc_update() \
+	do { \
+		TOP->last_code->line = TOP->kwline; \
+		TOP->last_code->col = TOP->kwcol; \
+	} while(0)
+
+static void got_kw(tmpasm_t *ctx, tmpasm_kw_t kw, int terminated)
+{
+	switch(kw) {
+		case KW_END:
+			/* then-else threads have their own subcontext within the if subcontext; end needs to pop the innermost subcontext before terminating the if context */
+			if (TOP->kw == KW_IF) {
+				error(ctx, 0, "unexpected \"end\" in \"if\" - expected \"then\"");
+				goto bind_if_cond;
+			}
+			if ((TOP->kw == KW_ELSE) || (TOP->kw == KW_THEN))
+				pop(ctx);
+
+
+			if (TOP->kw == KW_SWITCH)
+				TOP->kw = KW_none;
+			else {
+				pop(ctx);
+
+			if ((TOP->kw == KW_CASE) || (TOP->kw == KW_DEFAULT))
+				pop(ctx);
+
+			}
+			TOP->state = ST_PRECMD;
+
+				/* have to restore context keyword after these */
+			if (TOP->kw == KW_FOREACH)
+				TOP->kw = KW_none;
+
+			break;
+		case KW_IF:
+			if (terminated) {
+				error(ctx, 0, "unexpected end of if statement; expected a condition");
+				return;
+			}
+			TOP->last_code = code_append(ctx, KW_IF);
+			TOP->last_code->payload.fc_if.code_then = code_new(KW_NOP);
+			TOP->last_code->payload.fc_if.code_else = code_new(KW_NOP);
+			loc_update();
+			TOP->state = ST_PRECMD;
+			/* prepare for reading a condition */
+			push(ctx, KW_IF, ST_PREDATA, TOP->last_code);
+			break;
+		case KW_THEN:
+			/* we are in an if context, right after reading a condition */
+			if (TOP->kw != KW_IF) {
+				error(ctx, 0, "unexpected 'then' - must be in an 'if' after the condition");
+				return;
+			}
+			bind_if_cond:;
+			TOP->last_code->payload.fc_if.cond = TOP->argv[0];
+			loc_update();
+			arg_remove(ctx);
+			push(ctx, KW_THEN, ST_PRECMD, TOP->last_code->payload.fc_if.code_then);
+			break;
+		case KW_ELSE:
+			/* we are in an if context, after and end  */
+			if (TOP->kw != KW_THEN) {
+				error(ctx, 0, "unexpected 'else' - must be in a 'then' block before an else");
+				return;
+			}
+			pop(ctx); /* that was the then branch */
+			push(ctx, KW_ELSE, ST_PRECMD, TOP->last_code->payload.fc_if.code_else);
+			break;
+		case KW_FOREACH:
+			if (terminated) {
+				error(ctx, 0, "unexpected end of if foreach statement; expected an address");
+				return;
+			}
+			TOP->last_code = code_append(ctx, KW_FOREACH);
+			loc_update();
+			TOP->state = ST_PREDATA;
+			TOP->kw = KW_FOREACH;
+			break;
+		case KW_IN:
+			if (TOP->kw != KW_FOREACH)
+				error(ctx, 0, "unexpected \"in\"; should be after the address in foreach");
+			else
+				push(ctx, KW_IN, ST_PREDATA, NULL);
+			break;
+		case KW_SWITCH:
+			if (terminated) {
+				error(ctx, 0, "unexpected end of if switch statement; expected a data");
+				return;
+			}
+			TOP->last_code = code_append(ctx, KW_SWITCH);
+			TOP->state = ST_PREDATA;
+			TOP->kw = KW_SWITCH;
+			loc_update();
+			break;
+		case KW_CASE:
+		case KW_DEFAULT:
+			if (TOP->kw == KW_SWITCH) {
+				tmpasm_case_t *c;
+				c = malloc(sizeof(tmpasm_case_t));
+				c->body = code_new(KW_NOP);
+				c->data = NULL;
+				c->next = NULL;
+				if (TOP->last_code->payload.fc_switch.last == NULL) {
+					TOP->last_code->payload.fc_switch.first = c;
+					TOP->last_code->payload.fc_switch.last = c;
+				}
+				else {
+					TOP->last_code->payload.fc_switch.last->next = c;
+					TOP->last_code->payload.fc_switch.last = c;
+				}
+				if (kw == KW_DEFAULT) {
+					push(ctx, KW_DEFAULT, ST_PRECMD, c->body);
+					push(ctx, KW_none, ST_PRECMD, c->body);
+					c->data = NULL;
+				}
+				else
+					push(ctx, KW_CASE, ST_PREDATA, c->body);
+			}
+			else
+				error(ctx, 0, "unexpected \"case\" or \"default\"; should be in a switch (is the last case terminated by an \"end\"?)");
+			break;
+	default:
+		TOP->last_code = code_append(ctx, KW_none);
+		TOP->last_code->payload.instr.call_name = strclone(TOP->cmd_buff);
+		if (TOP->last_code->payload.instr.call_name != NULL) {
+			TOP->last_code->payload.instr.call = ctx->cb->resolve(ctx, TOP->last_code->payload.instr.call_name);
+			loc_update();
+		}
+		if (terminated)
+			TOP->state = ST_PRECMD;
+		else
+			TOP->state = ST_PREDATA;
+	}
+}
+
+static void comment_start(tmpasm_t *ctx)
+{
+	push(ctx, KW_none, ST_COMMENT, NULL);
+}
+
+int tmpasm_gotchar(tmpasm_t *ctx, char c)
+{
+	if (ctx->dead)
+		return -1;
+	switch(TOP->state) {
+		case ST_COMMENT:
+			if ((c == '\n') || (c == '\r')) {
+				pop(ctx);
+				if (TOP->state == ST_PREDATA)
+					end_of_statement(ctx);
+			}
+			break;
+		case ST_PRECMD:
+			if (c == '#') {
+				comment_start(ctx);
+				break;
+			}
+			if (is_space(c) || is_sep(c))
+				break;
+			TOP->cmdi = 0;
+			TOP->state = ST_CMD;
+			TOP->kwline = ctx->line;
+			TOP->kwcol = ctx->col;
+			/* fall thru */
+		case ST_CMD:
+			/* end of command or keyword */
+			if (is_space(c) || is_sep(c)) {
+				TOP->cmd_buff[TOP->cmdi] = '\0';
+				got_kw(ctx, kw_lookup(TOP->cmd_buff), is_sep(c));
+			}
+			else {
+				TOP->cmd_buff[TOP->cmdi] = c;
+				TOP->cmdi++;
+				if (TOP->cmdi >= sizeof(TOP->cmd_buff))
+					error(ctx, 0, "keyword or instruction name is too long");
+			}
+			break;
+		case ST_PREDATA:
+			if (c == '#') {
+				comment_start(ctx);
+				break;
+			}
+			if (is_space(c))
+				break;
+			if (is_sep(c))
+				end_of_statement(ctx);
+			else if (c == '{') {
+				TOP->state = ST_STRING;
+				arg_new(ctx, 0);
+			}
+			else if (c == '[') {
+				TOP->state = ST_PREBLOCKSEP;
+				arg_new(ctx, 0);
+			}
+			else if (is_addr(c)) {
+				TOP->state = ST_ADDRESS;
+				arg_new(ctx, 1);
+				arg_append(ctx, c);
+			}
+			else
+				error(ctx, c, "unexpected character; expected '{' for starting a string or an address");
+			break;
+		case ST_PREBLOCKSEP:
+			TOP->block_sep = c;
+			TOP->state = ST_BLOCK;
+			break;
+		case ST_BLOCK:
+			if (c == TOP->block_sep)
+				TOP->state = ST_BLOCKSEP;
+			else
+				arg_append(ctx, c);
+			break;
+		case ST_BLOCKSEP:
+			if (c != ']') {
+				arg_new_next(ctx, 1);
+				arg_append(ctx, c);
+				TOP->state = ST_BLOCK_INLINE;
+			}
+			else
+				arg_end(ctx, 1);
+			break;
+		case ST_BLOCK_INLINE:
+			if (c == TOP->block_sep) {
+				arg_new_next(ctx, 0);
+				TOP->state = ST_BLOCK;
+			}
+			else
+				arg_append(ctx, c);
+			break;
+		case ST_STRING:
+			if (c == '}')
+				arg_end(ctx, 1);
+			else if (c == '\\')
+				TOP->state = ST_STRING_ESCAPE;
+			else
+				arg_append(ctx, c);
+			break;
+		case ST_STRING_ESCAPE:
+			{
+			char co;
+			switch(c) {
+				case 'n':  co = '\n'; break;
+				case 'r':  co = '\r'; break;
+				case 't':  co = '\t'; break;
+				case '\\': co = '\\'; break;
+				case 'o':  co = '{';  break;
+				case 'c':  co = '}';  break;
+				default:   co = c;
+			}
+			arg_append(ctx, co);
+			TOP->state = ST_STRING;
+			}
+			break;
+		case ST_ADDRESS:
+			if (is_space(c))
+				arg_end(ctx, 1);
+			else if (is_sep(c)) {
+				arg_end(ctx, 1);
+				end_of_statement(ctx);
+			}
+			else if (is_addr(c))
+				arg_append(ctx, c);
+			else
+				error(ctx, c, "unexpected character; expected next character of the address");
+			break;
+	}
+	if (c == '\n') {
+		ctx->line++;
+		ctx->col = 1;
+	}
+	else
+		ctx->col++;
+	return 0;
+}
+
+tmpasm_t *tmpasm_init(tmpasm_cb_t *cb)
+{
+	tmpasm_t *ctx;
+	ctx = calloc(sizeof(tmpasm_t), 1);
+	ctx->line = 1;
+	ctx->col = 1;
+	ctx->code = code_new(KW_NOP);
+	ctx->cb = cb;
+	push(ctx, KW_none, ST_PRECMD, ctx->code);
+	return ctx;
+}
+
+static void free_exec(tmpasm_exec_t *e)
+{
+	int n;
+	tmpasm_case_t *c, *c_next;
+	tmpasm_exec_t *e_next;
+
+	for(; e != NULL; e = e_next) {
+		e_next = e->next;
+		switch(e->kw) {
+			case KW_none:
+				if (e->payload.instr.call_name != NULL)
+					free(e->payload.instr.call_name);
+				for(n = 0; n < e->payload.instr.argc; n++)
+					arg_free(e->payload.instr.argv[n]);
+				free(e->payload.instr.argv);
+				break;
+			case KW_IF:
+				arg_free(e->payload.fc_if.cond);
+				free_exec(e->payload.fc_if.code_then);
+				free_exec(e->payload.fc_if.code_else);
+				break;
+			case KW_FOREACH:
+				free(e->payload.fc_foreach.loop_var);
+				arg_free(e->payload.fc_foreach.data);
+				free_exec(e->payload.fc_foreach.code_body);
+				break;
+			case KW_SWITCH:
+				arg_free(e->payload.fc_switch.cond);
+				for(c = e->payload.fc_switch.first; c != NULL; c = c_next) {
+					c_next = c->next;
+					if (c->data != NULL)
+						arg_free(c->data);
+					free_exec(c->body);
+					free(c);
+				}
+				break;
+			default:;
+		}
+		free(e);
+	}
+}
+
+void tmpasm_uninit(tmpasm_t *ctx)
+{
+	free_exec(ctx->code);
+	while (ctx->st != NULL)
+		pop_(ctx, 0);
+	if (ctx->runtime_error_data != NULL)
+		free(ctx->runtime_error_data);
+	free(ctx);
+}
+
+/****************** runtime ********************/
+
+static const char *tmpasm_runtime_error_fmts[] = {
+	"success %s",
+	"variable '%s' does not exist",
+	"empty argument (broken AST)%s",
+	"compilation error: control block without an \"end\"; premature end of script%s",
+	"attempt to call unresolved instruction '%s'",
+	NULL
+};
+
+void tmpasm_runtime_error(tmpasm_t *ctx, int code, char *data)
+{
+	ctx->runtime_error = code;
+	if (ctx->runtime_error_data != NULL)
+		free(ctx->runtime_error_data);
+	ctx->runtime_error_data = strclone(data);
+	if (ctx->executing != NULL) {
+		ctx->runtime_error_line = ctx->executing->line;
+		ctx->runtime_error_col  = ctx->executing->col;
+	}
+	else {
+		ctx->runtime_error_line = 0;
+		ctx->runtime_error_col  = 0;
+	}
+}
+
+const char *tmpasm_runtime_error_fmt(tmpasm_t *ctx)
+{
+	if (ctx->runtime_error == 0)
+		return NULL;
+	if ((ctx->runtime_error < 0) && (ctx->cb->runtime_error_fmt != NULL)) {
+		const char *fmt;
+		fmt = ctx->cb->runtime_error_fmt(ctx);
+		if (fmt != NULL)
+			return fmt;
+	}
+	if ((ctx->runtime_error < 0) || ((size_t)ctx->runtime_error > (sizeof(tmpasm_runtime_error_fmts)/sizeof(char *))))
+		return "invalid error code %s";
+	return tmpasm_runtime_error_fmts[ctx->runtime_error];
+}
+
+char *tmpasm_arg2str(tmpasm_t *ctx, tmpasm_arg_t *a, int keep_addr)
+{
+	if (a == NULL) {
+		tmpasm_runtime_error(ctx, 2, NULL);
+		return strclone("");
+	}
+	if (a->next != NULL) {
+		/* block mode */
+		int alloced = 0, used = 0;
+		char *s = NULL;
+		const char *i;
+
+		for(;a != NULL; a = a->next) {
+			int l;
+			if (a->is_addr) {
+				i = ctx->cb->get(ctx, a->data);
+				if (i == NULL) {
+					i = "";
+					tmpasm_runtime_error(ctx, 1, strclone(a->data));
+				}
+			}
+			else
+				i = a->data;
+			l = strlen(i);
+			if (used + l >= alloced) {
+				alloced = used + l + 256;
+				s = realloc(s, alloced);
+			}
+			memcpy(s+used, i, l);
+			used += l;
+		}
+		s[used] = '\0';
+		return s;
+	}
+
+	/* non-block */
+	if (a->is_addr) {
+		const char *i;
+		if (keep_addr)
+			i = a->data;
+		else
+			i = ctx->cb->get(ctx, a->data);
+		if (i == NULL) {
+			i = "";
+			tmpasm_runtime_error(ctx, 1, strclone(a->data));
+		}
+		return strclone(i);
+	}
+
+	return strclone(a->data);
+}
+
+static void execute(tmpasm_t *ctx, tmpasm_exec_t *e)
+{
+	tmpasm_case_t *c;
+	void *state;
+	char *cond, *list;
+	const char *i;
+
+	while((e != NULL) && (ctx->runtime_error == 0)) {
+		ctx->executing = e;
+		switch(e->kw) {
+			case KW_none:
+				if (e->payload.instr.call != NULL)
+					e->payload.instr.call(ctx, e->payload.instr.call_name, e->payload.instr.argc, e->payload.instr.argv);
+				else
+					tmpasm_runtime_error(ctx, 4, e->payload.instr.call_name);
+				break;
+			case KW_IF:
+					cond = tmpasm_arg2str(ctx, e->payload.fc_if.cond, 0);
+					if (ctx->cb->is_true(ctx, cond))
+						execute(ctx, e->payload.fc_if.code_then);
+					else
+						execute(ctx, e->payload.fc_if.code_else);
+					free(cond);
+				break;
+			case KW_FOREACH:
+				list = tmpasm_arg2str(ctx, e->payload.fc_foreach.data, 0);
+				for(i = ctx->cb->first(ctx, &state, list); i != NULL; i = ctx->cb->next(ctx, &state)) {
+					ctx->cb->set(ctx, e->payload.fc_foreach.loop_var, i);
+					execute(ctx, e->payload.fc_foreach.code_body);
+				}
+				free(list);
+				break;
+			case KW_SWITCH:
+				cond = tmpasm_arg2str(ctx, e->payload.fc_switch.cond, 0);
+				for(c = e->payload.fc_switch.first; c != NULL; c = c->next) {
+					char *cv = NULL;
+					if (c->data != NULL)
+						cv = tmpasm_arg2str(ctx, c->data, 0);
+					if ((c->data == NULL) || (ctx->cb->match(ctx, cond, cv))) {
+						execute(ctx, c->body);
+						if (cv != NULL)
+							free(cv);
+						break;
+					}
+					if (cv != NULL)
+						free(cv);
+				}
+				free(cond);
+				break;
+			default:;
+		}
+	e = e->next;
+	}
+}
+
+void tmpasm_execute(tmpasm_t *ctx)
+{
+	if (TOP->next != NULL) {
+		ctx->executing = TOP->next->last_code;
+		tmpasm_runtime_error(ctx, 3, NULL);
+		return;
+	}
+	if ((TOP->state != ST_PRECMD) || (TOP->kw != KW_none)) {
+		ctx->executing = TOP->last_code;
+		tmpasm_runtime_error(ctx, 3, NULL);
+		return;
+	}
+	ctx->runtime_error = 0;
+	if (ctx->runtime_error_data != NULL) {
+		free(ctx->runtime_error_data);
+		ctx->runtime_error_data = NULL;
+	}
+	if (ctx->cb->preexec != NULL)
+		ctx->cb->preexec(ctx);
+	execute(ctx, ctx->code);
+	if (ctx->cb->postexec != NULL)
+		ctx->cb->postexec(ctx);
+}
+
diff --git a/scconfig/src/tmpasm/tmpasm.h b/scconfig/src/tmpasm/tmpasm.h
new file mode 100644
index 0000000..d73b029
--- /dev/null
+++ b/scconfig/src/tmpasm/tmpasm.h
@@ -0,0 +1,178 @@
+#ifndef TMPASM_H
+#define TMPASM_H
+#ifndef TMPASM_INSTR_MAXLEN
+#define TMPASM_INSTR_MAXLEN 32
+#endif
+
+typedef struct tmpasm_s tmpasm_t;
+
+typedef struct tmpasm_arg_s tmpasm_arg_t;
+
+struct tmpasm_arg_s {
+	tmpasm_arg_t *next;    /* block: the resulting string is a list of strings and addresses */
+	char is_addr;          /* 1: arg is a node address; 0: arg is a string immediate */
+	char data[1];          /* arg string - obviously longer than 1 char (but there's not special hack for that in C89), \0 terminated */
+};
+
+/* user specified instruction prototype */
+typedef void tmpasm_instr(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]);
+
+
+typedef struct tmpasm_cb_s {
+	/* return the value of a node at addr - NULL is an error */
+	const char *(*get)(tmpasm_t *ctx, const char *addr);
+
+	/* set the value of a node at addr to data; data may be NULL */
+	void (*set)(tmpasm_t *ctx, const char *addr, const char *data);
+
+	/* return 1 if data is true, 0 otherwise; data may be NULL (if an unknown variable is referenced) */
+	int (*is_true)(tmpasm_t *ctx, const char *data);
+
+	/* return 1 if str matches pat, 0 otherwise; str and pat may be NULL */
+	int (*match)(tmpasm_t *ctx, const char *str, const char *pat);
+
+	/* first iteration over list; return the first element (or NULL to end); the string returned is not free'd by the caller */
+	const char *(*first)(tmpasm_t *ctx, void **state, char *list);
+
+	/* return next element of a list or NULL on end (in which case state shall be also free'd by the caller); the string returned is not free'd by the caller */
+	const char *(*next)(tmpasm_t *ctx, void **state);
+
+	/* resolve an instruction name to a function pointer */
+	tmpasm_instr *(*resolve)(tmpasm_t *ctx, const char *name);
+
+	/* optional: called once before execution of a context starts */
+	void (*preexec)(tmpasm_t *ctx);
+
+	/* optional: called once before execution of a context starts */
+	void (*postexec)(tmpasm_t *ctx);
+
+	/* optional: resolve the current runtime error, called only for negative
+	   error codes; should return a format string with exactly one %s in it
+	   or NULL. */
+	const char *(*runtime_error_fmt)(tmpasm_t *ctx);
+} tmpasm_cb_t;
+
+int tmpasm_gotchar(tmpasm_t *ctx, char c);
+
+tmpasm_t *tmpasm_init(tmpasm_cb_t *cb);
+void tmpasm_uninit(tmpasm_t *ctx);
+
+/* return the string version of an arg in a newly malloc()'d string
+   if keep_addr is non-zero and a is a single address, no get() is run
+   but the address is returned as a string */
+char *tmpasm_arg2str(tmpasm_t *ctx, tmpasm_arg_t *a, int keep_addr);
+
+/* execute the code recursively until it exits */
+void tmpasm_execute(tmpasm_t *ctx);
+
+void tmpasm_runtime_error(tmpasm_t *ctx, int code, char *data);
+const char *tmpasm_runtime_error_fmt(tmpasm_t *ctx);
+
+/* --- internals: not required for normal use --- */
+typedef enum {
+	ST_PRECMD,          /* waiting for a command to start - ignore whitespace */
+	ST_CMD,
+	ST_PREDATA,         /* waiting for data */
+	ST_PREBLOCKSEP,     /* waiting for a block sep when opening a block */
+	ST_BLOCKSEP,        /* found a block sep within the block - either an address or a termination follows */
+	ST_BLOCK,           /* in [@ @] block, text part */
+	ST_BLOCK_INLINE,    /* in [@ @] block, within inline @@ part */
+	ST_STRING,          /* in {} string  */
+	ST_STRING_ESCAPE,   /* in {} string, right after a \ */
+	ST_ADDRESS,         /* shifting address bytes */
+	ST_COMMENT          /* after #, until the next newline */
+} tmpasm_state_t;
+
+typedef enum {
+	KW_none,
+	KW_IF,
+	KW_THEN,
+	KW_ELSE,
+	KW_END,
+	KW_FOREACH,
+	KW_IN,
+	KW_SWITCH,
+	KW_CASE,
+	KW_DEFAULT,
+
+	KW_NOP     /* virtual instruction */
+} tmpasm_kw_t;
+
+/* execution structs */
+typedef struct tmpasm_exec_s tmpasm_exec_t;
+typedef struct tmpasm_case_s tmpasm_case_t;
+
+struct tmpasm_case_s {
+	tmpasm_arg_t *data;
+	tmpasm_exec_t *body;
+	tmpasm_case_t *next;
+};
+
+
+struct tmpasm_exec_s {
+	tmpasm_kw_t kw;        /* kw_none means a hook instruction */
+	union {
+		struct {      /* normal instruction */
+			tmpasm_instr *call;
+			char *call_name; /* temporary */
+			int argc;
+			tmpasm_arg_t **argv;
+		} instr;
+		struct {
+			tmpasm_arg_t *cond;
+			tmpasm_exec_t *code_then;
+			tmpasm_exec_t *code_else;
+		} fc_if;
+		struct {
+			char *loop_var;        /* must be a single address */
+			tmpasm_arg_t *data;    /* what to loop in */
+			tmpasm_exec_t *code_body;
+		} fc_foreach;
+		struct {
+			tmpasm_arg_t *cond;
+			tmpasm_case_t *first;
+			tmpasm_case_t *last;
+		} fc_switch;
+	} payload;
+	int line, col;
+	tmpasm_exec_t *next;
+};
+
+
+/* parser structs */
+typedef struct stack_s tmpasm_stack_t;
+struct stack_s {
+	tmpasm_state_t state;
+/*	tmpasm_state_t kwstate; internal states of composite keywords like switch */
+	char cmd_buff[TMPASM_INSTR_MAXLEN+1];
+	unsigned int cmdi;
+	tmpasm_kw_t kw;
+	char block_sep;
+	int kwcol, kwline;
+
+	int args_used, args_alloced; /* number of arguments in argv[] */
+
+	tmpasm_arg_t **argv;   /* an array of linked lists */
+	tmpasm_arg_t **argend; /* each argv[] is a linked list (for blocks); argend points to the tail */
+	int *arg_alloced;      /* of argend */
+	int *arg_used;         /* of argend */
+
+	tmpasm_exec_t *last_code;     /* tail of the code list */
+
+	tmpasm_stack_t *next;
+};
+
+struct tmpasm_s {
+	tmpasm_stack_t *st;
+	int dead;
+	int col, line;
+	tmpasm_exec_t *code;
+	tmpasm_exec_t *executing; /* points to the code most recently executed (or being executed when in callbacks) */
+	tmpasm_cb_t *cb;
+	int runtime_error;
+	char *runtime_error_data;
+	int runtime_error_line;
+	int runtime_error_col;
+	void *user_data;
+};
+#endif
diff --git a/scconfig/src/tmpasm/tmpasm_scconfig.c b/scconfig/src/tmpasm/tmpasm_scconfig.c
new file mode 100644
index 0000000..358e249
--- /dev/null
+++ b/scconfig/src/tmpasm/tmpasm_scconfig.c
@@ -0,0 +1,552 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "tmpasm.h"
+#include "db.h"
+#include "regex.h"
+#include "openfiles.h"
+#include "libs.h"
+#include "tmpasm_scconfig.h"
+#include "log.h"
+#include "regex.h"
+
+#ifndef TMPASM_PATH
+#define TMPASM_PATH "/tmpasm"
+#endif
+
+#ifndef IFS_PATH
+#define IFS_PATH TMPASM_PATH "/IFS"
+#endif
+
+#ifndef IFS_DEFAULT
+#define IFS_DEFAULT " \t\r\n"
+#endif
+
+#ifndef OFS_PATH
+#define OFS_PATH TMPASM_PATH "/OFS"
+#endif
+
+#ifndef OFS_DEFAULT
+#define OFS_DEFAULT "\n"
+#endif
+
+typedef struct scc_s {
+	openfiles_t ofl;
+	FILE *fout, *default_fout;
+	const char *cwd;
+} scc_t;
+
+static const char *scc_runtime_error_fmts[] = {
+	/* -0  */  "success scc %s",
+	/* -1  */  "\"put\" requires exactly two arguments (got %s)",
+	/* -2  */  "not enough arguments for sub; should be \"sub node pattern str\"%s",
+	/* -3  */  "regex syntax error: %s",
+	/* -4  */  "not enough arguments for uniq; should be \"uniq destnode\" or \"uniq destnode src\"%s",
+	/* -5  */  "redir: too many arguments%s",
+	/* -6  */  "redir: can't open %s",
+	/* -7  */  "exiting due to a previous runtime error occurred in included file %s",
+	/* -8  */  "can't include '%s': can't open file",
+	/* -9  */  "\"put\" requires two or three arguments (got %s)",
+	NULL
+};
+
+static int print_runtime_error(tmpasm_t *ctx, const char *ifn)
+{
+	if (ctx->runtime_error != 0) {
+		const char *fmt = tmpasm_runtime_error_fmt(ctx);
+		fprintf(stderr, "Runtime error at %s %d:%d: ", ifn, ctx->runtime_error_line, ctx->runtime_error_col);
+		fprintf(stderr, fmt, (ctx->runtime_error_data == NULL ? "" : ctx->runtime_error_data));
+		fprintf(stderr, "\n");
+		return -1;
+	}
+	return 0;
+}
+
+/* allocate and build a full path using ud->cwd and fn */
+static char *scc_path(scc_t *ud, const char *fn)
+{
+	if (ud->cwd == NULL)
+		return strclone(fn);
+	return str_concat("", ud->cwd, "/", fn, NULL);
+}
+
+/******** db binding ********/
+
+static const char *scc_get(tmpasm_t *ctx, const char *addr)
+{
+	(void) ctx;  /* not used */
+	if (*addr == '?') {
+		const char *res = get(addr+1);
+		if (res == NULL)
+			return "";
+		return res;
+	}
+	return get(addr);
+}
+
+static void scc_set(tmpasm_t *ctx, const char *addr, const char *data)
+{
+	(void) ctx;  /* not used */
+	put(addr, data);
+}
+
+static int scc_is_true(tmpasm_t *ctx, const char *data)
+{
+	(void) ctx;  /* not used */
+	return ((strcmp(data, "1") == 0) || istrue(data));
+}
+
+static int scc_match(tmpasm_t *ctx, const char *str, const char *pat)
+{
+	(void) ctx;  /* not used */
+	re_comp(pat);
+	return re_exec(str);
+}
+
+static const char *scc_ifs(tmpasm_t *ctx)
+{
+	const char *ifs = get(IFS_PATH);
+	(void) ctx;  /* not used */
+	if (ifs == NULL)
+		return IFS_DEFAULT;
+	return ifs;
+}
+
+static const char *scc_ofs(tmpasm_t *ctx)
+{
+	const char *ofs = get(OFS_PATH);
+	(void) ctx;  /* not used */
+	if (ofs == NULL)
+		return OFS_DEFAULT;
+	return ofs;
+}
+
+static const char *scc_next(tmpasm_t *ctx, void **state)
+{
+	char **s = (char **)state;
+	char *start;
+	const char *IFS;
+
+	IFS = scc_ifs(ctx);
+
+	/* strip leading whitespace */
+	while(chr_inset(**s, IFS)) (*s)++;
+
+	/* at the end of the string, no more tokens */
+	if (**s == '\0')
+		return NULL;
+
+	start = *s;
+
+	/* skip non-whitespace */
+	while(!(chr_inset(**s, IFS)) && (**s != '\0')) (*s)++;
+
+	if (**s != '\0') {
+		**s = '\0';
+		(*s)++;
+	}
+	return start;
+}
+
+
+static const char *scc_first(tmpasm_t *ctx, void **state, char *list)
+{
+	*state = list;
+
+	return scc_next(ctx, state);
+}
+
+
+/******** instructions ********/
+static void instr_put(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	char *addr, *val;
+	(void) iname;  /* not used */
+	if (argc != 2) {
+		char str[16];
+		sprintf(str, "%d", argc);
+		tmpasm_runtime_error(ctx, -1, str);
+		return;
+	}
+	addr = tmpasm_arg2str(ctx, argv[0], 1);
+	val  = tmpasm_arg2str(ctx, argv[1], 0);
+	if (*addr != '\0')
+		put(addr, val);
+	free(addr);
+	free(val);
+}
+
+
+static void instr_append(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	char *addr, *val;
+	char *sep;
+	(void) iname;  /* not used */
+
+	if ((argc < 2) || (argc > 3)) {
+		char str[16];
+		sprintf(str, "%d", argc);
+		tmpasm_runtime_error(ctx, -9, str);
+		return;
+	}
+	addr = tmpasm_arg2str(ctx, argv[0], 1);
+	val  = tmpasm_arg2str(ctx, argv[1], 0);
+	if (argc >= 3)
+		sep = tmpasm_arg2str(ctx, argv[2], 0);
+	else
+		sep = strclone(scc_ofs(ctx));
+	if (*addr != '\0') {
+		append(addr, sep);
+		append(addr, val);
+	}
+	free(addr);
+	free(val);
+	free(sep);
+}
+
+static void instr_report(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	int n;
+	(void) iname;  /* not used */
+	for(n = 0; n < argc; n++) {
+		char *val;
+		val  = tmpasm_arg2str(ctx, argv[n], 0);
+		report("%s", val);
+		free(val);
+	}
+}
+
+static void instr_abort(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	(void) ctx;  /* not used */
+	(void) iname;  /* not used */
+	(void) argc;  /* not used */
+	(void) argv;  /* not used */
+	report("Abort requested by template.\n");
+	abort();
+}
+
+static void instr_sub(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	char *node, *pat, *err, *csub, *buff, *end;
+	const char *start;
+	const char *val;
+	int score, slen, global;
+
+	if (argc < 3) {
+		tmpasm_runtime_error(ctx, -2, NULL);
+		return;
+	}
+
+	node = tmpasm_arg2str(ctx, argv[0], 1);
+	pat  = tmpasm_arg2str(ctx, argv[1], 0);
+	csub = tmpasm_arg2str(ctx, argv[2], 0);
+	global = (*iname == 'g');
+
+	val = get(node);
+	if (val == NULL)
+		val="";
+	err = re_comp(pat);
+	if (err != NULL) {
+		tmpasm_runtime_error(ctx, -3, err);
+		return;
+	}
+
+	slen = strlen(csub);
+	if (global)
+		buff = malloc(strlen(val)*(slen+3)+32); /* big enough for worst case, when every letter and $ and ^ are replaced with sub */
+	else
+		buff = malloc(strlen(val)+slen+32);  /* only one replacement will be done */
+	strcpy(buff, val);
+
+	start = buff;
+	do {
+		score = re_exec(start);
+		if (score == 0)
+			break;
+		end = buff + strlen(buff);
+		if (eopat[0] - bopat[0] != slen) {
+			int mlen = end - eopat[0]+1;
+			if (mlen > 0)
+				memmove((char *)(bopat[0] + slen), eopat[0], mlen);
+		}
+		memcpy((char *)bopat[0], csub, slen);
+		start = bopat[0] + slen;
+	} while(global);
+
+	buff = realloc(buff, strlen(buff)+1);
+	put(node, buff);
+	free(buff);
+	free(node);
+	free(pat);
+	free(csub);
+}
+
+static void instr_uniq(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	char *node, *strlist, *buff;
+
+	if (argc < 1) {
+		tmpasm_runtime_error(ctx, -4, NULL);
+		return;
+	}
+	node = tmpasm_arg2str(ctx, argv[0], 1);
+	if (argc > 1) {
+		strlist = tmpasm_arg2str(ctx, argv[1], 0);
+	}
+	else
+		strlist = strclone(get(node));
+	buff = uniq_inc_str(strlist, scc_ifs(ctx), scc_ofs(ctx), (*iname == 's'));
+	put(node, buff);
+	free(buff);
+	free(strlist);
+	free(node);
+}
+
+static void instr_print(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	int n;
+	scc_t *ud = (scc_t *)ctx->user_data;
+	(void) iname;  /* not used */
+
+	for(n = 0; n < argc; n++) {
+		char *val;
+		val  = tmpasm_arg2str(ctx, argv[n], 0);
+		fprintf(ud->fout, "%s", val);
+		free(val);
+	}
+}
+
+static void instr_print_ternary(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	char *s_cond, *s;
+	scc_t *ud = (scc_t *)ctx->user_data;
+	(void) iname;  /* not used */
+
+	if ((argc < 2) || (argc > 3)) {
+		char str[16];
+		sprintf(str, "%d", argc);
+		tmpasm_runtime_error(ctx, -1, str);
+		return;
+	}
+
+	s_cond  = tmpasm_arg2str(ctx, argv[0], 0);
+
+	if (ctx->cb->is_true(ctx, s_cond))
+		s  = tmpasm_arg2str(ctx, argv[1], 0);
+	else
+		s  = tmpasm_arg2str(ctx, argv[2], 0);
+
+	fprintf(ud->fout, "%s", s);
+
+	free(s_cond);
+	free(s);
+}
+
+static void scc_tmpasm_parse_(tmpasm_t *ctx, const char *cwd, FILE *fin, FILE *default_fout, FILE *fout)
+{
+	scc_t *ud = malloc(sizeof(scc_t));
+	memset(&ud->ofl, 0, sizeof(ud->ofl));
+	ctx->user_data = ud;
+	ud->default_fout = default_fout;
+	ud->fout = fout;
+	ud->cwd = cwd;
+
+	for(;;) {
+		int c;
+		c = fgetc(fin);
+		if (c == EOF)
+			break;
+		tmpasm_gotchar(ctx, c);
+	}
+
+}
+
+void scc_tmpasm_parse(tmpasm_t *ctx, const char *cwd, FILE *fin, FILE *fout)
+{
+	scc_tmpasm_parse_(ctx, cwd, fin, fout, fout);
+}
+
+#ifndef NO_FILE_IO
+static void instr_include(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	scc_t *ud = (scc_t *)ctx->user_data;
+	int n;
+	(void) iname;  /* not used */
+
+	for(n = 0; n < argc; n++) {
+		char *fn, *path;
+		FILE *fin;
+		tmpasm_t *child;
+
+		fn = tmpasm_arg2str(ctx, argv[n], 0);
+		path = scc_path(ud, fn);
+		fin = fopen(path, "r");
+		if (fin == NULL) {
+			tmpasm_runtime_error(ctx, -8, path);
+			free(fn);
+			free(path);
+			return;
+		}
+		child = tmpasm_init(ctx->cb);
+		scc_tmpasm_parse_(child, ud->cwd, fin, ud->default_fout, ud->fout);
+		tmpasm_execute(child);
+		if (print_runtime_error(child, path) != 0)
+			tmpasm_runtime_error(ctx, -7, path);
+		tmpasm_uninit(child);
+		fclose(fin);
+		free(fn);
+		free(path);
+	}
+}
+
+
+static void instr_redir(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	char *path, *fn, *mode;
+	scc_t *ud = (scc_t *)ctx->user_data;
+	(void) iname;  /* not used */
+	fflush(ud->fout);
+	switch(argc) {
+		case 0: ud->fout = ud->default_fout; return;  /* set redirection to default */
+		case 1: mode = strclone("w"); break;
+		case 2: mode = tmpasm_arg2str(ctx, argv[1], 0); break;
+		default:
+			tmpasm_runtime_error(ctx, -5, NULL);
+			return;
+	}
+
+	fn = tmpasm_arg2str(ctx, argv[0], 0);
+	path = scc_path(ud, fn);
+	ud->fout = openfile_open(&ud->ofl, path, mode);
+	if (ud->fout == NULL) {
+		char *err = malloc(strlen(fn) + strlen(path) + strlen(mode) + 16);
+		sprintf(err, "%s (%s) for %s", path, fn, mode);
+		tmpasm_runtime_error(ctx, -6, err);
+		free(err);
+		free(path);
+		return;
+	}
+	free(fn);
+	free(mode);
+	free(path);
+}
+#endif
+
+#ifdef TMPASM_TESTER
+static void instr_unknown(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[])
+{
+	printf("ERROR: unknown instruction '%s'\n", iname);
+}
+#endif
+
+
+/******** interface ********/
+
+tmpasm_instr *scc_resolve(tmpasm_t *ctx, const char *name)
+{
+	(void) ctx;  /* not used */
+/* TODO: make this a hash */
+	if (strcmp(name, "put") == 0)
+		return instr_put;
+	if (strcmp(name, "append") == 0)
+		return instr_append;
+	if (strcmp(name, "print") == 0)
+		return instr_print;
+	if (strcmp(name, "print_ternary") == 0)
+		return instr_print_ternary;
+#ifndef TMPASM_NO_FILE_IO
+	if (strcmp(name, "redir") == 0)
+		return instr_redir;
+	if (strcmp(name, "include") == 0)
+		return instr_include;
+#endif
+	if (strcmp(name, "report") == 0)
+		return instr_report;
+	if (strcmp(name, "abort") == 0)
+		return instr_abort;
+	if (strcmp(name, "uniq") == 0)
+		return instr_uniq;
+	if (strcmp(name, "sortuniq") == 0)
+		return instr_uniq;
+	if ((strcmp(name, "sub") == 0) || (strcmp(name, "gsub") == 0))
+		return instr_sub;
+
+#ifndef TMPASM_TESTER
+	return NULL;
+#else
+	return instr_unknown;
+#endif
+}
+
+
+static const char *scc_err_fmt(tmpasm_t *ctx)
+{
+	int code;
+	code = -ctx->runtime_error;
+
+	if ((code < 0) || ((size_t)code > (sizeof(scc_runtime_error_fmts)/sizeof(char *))))
+		return NULL;
+	return scc_runtime_error_fmts[code];
+}
+
+
+static void scc_preexec(tmpasm_t *ctx)
+{
+	(void) ctx;  /* not used */
+	db_mkdir(TMPASM_PATH);
+}
+
+static void scc_postexec(tmpasm_t *ctx)
+{
+	scc_t *ud = (scc_t *)ctx->user_data;
+	openfile_free(&ud->ofl);
+	free(ud);
+}
+
+tmpasm_cb_t scc_cb = {
+	scc_get, scc_set, scc_is_true, scc_match, scc_first, scc_next,
+	scc_resolve, scc_preexec, scc_postexec, scc_err_fmt
+};
+
+int tmpasm(const char *wdir, const char *input, const char *output)
+{
+	tmpasm_t *ctx;
+	FILE *fin, *fout;
+	int ret;
+	scc_t ud_tmp;
+	char *path;
+
+	ud_tmp.cwd = wdir;
+
+	path = scc_path(&ud_tmp, input);
+	fin = fopen(path, "r");
+	if (fin == NULL) {
+		fprintf(stderr, "ERROR: tmpasm: can not open script '%s' (%s in %s)\n", path, input, wdir);
+		free(path);
+		return -1;
+	}
+	free(path);
+
+	path = scc_path(&ud_tmp, output);
+	fout = fopen(path, "w");
+	if (fout == NULL) {
+		fprintf(stderr, "ERROR: tmpasm: can not open output '%s' (%s in %s)\n", path, output, wdir);
+		free(path);
+		return -1;
+	}
+	free(path);
+
+	ctx = tmpasm_init(&scc_cb);
+	scc_tmpasm_parse_(ctx, wdir, fin, fout, fout);
+	if (!ctx->dead)
+		tmpasm_execute(ctx);
+	fclose(fin);
+	fclose(fout);
+
+	ret = print_runtime_error(ctx, input);
+
+	tmpasm_uninit(ctx);
+	return ret;
+}
+
diff --git a/scconfig/src/tmpasm/tmpasm_scconfig.h b/scconfig/src/tmpasm/tmpasm_scconfig.h
new file mode 100644
index 0000000..e13a933
--- /dev/null
+++ b/scconfig/src/tmpasm/tmpasm_scconfig.h
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include "tmpasm.h"
+
+int tmpasm(const char *wdir, const char *input, const char *output);
+
+void scc_tmpasm_parse(tmpasm_t *ctx, const char *cwd, FILE *fin, FILE *fout);
+
+extern tmpasm_cb_t scc_cb;
+
+
diff --git a/scconfig/src/userpass/INIT.c b/scconfig/src/userpass/INIT.c
new file mode 100644
index 0000000..e4b4425
--- /dev/null
+++ b/scconfig/src/userpass/INIT.c
@@ -0,0 +1,2 @@
+	deps_userpass_init();
+
diff --git a/scconfig/src/userpass/INIT.h b/scconfig/src/userpass/INIT.h
new file mode 100644
index 0000000..124bbf3
--- /dev/null
+++ b/scconfig/src/userpass/INIT.h
@@ -0,0 +1 @@
+void deps_userpass_init();
diff --git a/scconfig/src/userpass/Makefile.plugin b/scconfig/src/userpass/Makefile.plugin
new file mode 100644
index 0000000..6c8d479
--- /dev/null
+++ b/scconfig/src/userpass/Makefile.plugin
@@ -0,0 +1,11 @@
+USERPASS_CFLAGS = -DPLUGIN_USERPASS
+USERPASS_OBJS = \
+  $(BIN)/userpass/find_username.o \
+  $(BIN)/userpass/userpass.o
+
+$(BIN)/userpass/find_username.o: $(SRC)/userpass/find_username.c
+	$(CC) $(CFLAGS) -c $(SRC)/userpass/find_username.c -o $(BIN)/userpass/find_username.o
+
+$(BIN)/userpass/userpass.o: $(SRC)/userpass/userpass.c
+	$(CC) $(CFLAGS) -c $(SRC)/userpass/userpass.c -o $(BIN)/userpass/userpass.o
+
diff --git a/scconfig/src/userpass/find_username.c b/scconfig/src/userpass/find_username.c
new file mode 100644
index 0000000..3e28ac3
--- /dev/null
+++ b/scconfig/src/userpass/find_username.c
@@ -0,0 +1,73 @@
+/*
+    scconfig - user name related API detection
+    Copyright (C) 2012  Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+#include <stdlib.h>
+#include <string.h>
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int find_username_getpw(int logdepth, int fatal, const char *call, const char *arg)
+{
+	char *test_c_ =
+		NL "#include <stdlib.h>"
+		NL "int main() {"
+		NL "	struct passwd *p;"
+		NL "	p = %s(%s);"
+		NL "	if (p != NULL)"
+		NL "		puts(\"OK\");"
+		NL "	return 0;"
+		NL "}"
+		NL;
+	char test_c[512];
+	char node[128];
+	char *cflags = "+";
+	char *ldflags = "+";
+
+	char *inc_default =
+		NL "#include <sys/types.h>"
+		NL "#include <pwd.h>"
+		NL;
+
+	if (require("cc/cc", logdepth, fatal))
+		return 1;
+
+	report("Checking for %s... ", call);
+	logprintf(logdepth, "find_username_getpw: trying to find %s()...\n", call);
+	logdepth++;
+
+	sprintf(test_c, test_c_, call, arg);
+	sprintf(node, "libs/userpass/%s", call);
+	if (try_icl(logdepth, node, test_c, inc_default, cflags, ldflags) == 0)
+		return try_fail(logdepth, node);
+	return 0;
+}
+
+int find_username_getpwuid(int logdepth, int fatal)
+{
+	return find_username_getpw(logdepth, fatal, "getpwuid", "0");
+}
+
+int find_username_getpwnam(int logdepth, int fatal)
+{
+	return find_username_getpw(logdepth, fatal, "getpwnam", "\"root\"");
+}
diff --git a/scconfig/src/userpass/find_username.h b/scconfig/src/userpass/find_username.h
new file mode 100644
index 0000000..ca855f8
--- /dev/null
+++ b/scconfig/src/userpass/find_username.h
@@ -0,0 +1,2 @@
+int find_username_getpwuid(int logdepth, int fatal);
+int find_username_getpwnam(int logdepth, int fatal);
diff --git a/scconfig/src/userpass/userpass.c b/scconfig/src/userpass/userpass.c
new file mode 100644
index 0000000..9f89ec6
--- /dev/null
+++ b/scconfig/src/userpass/userpass.c
@@ -0,0 +1,12 @@
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+#include "find_username.h"
+
+void deps_userpass_init()
+{
+	dep_add("libs/userpass/getpwuid/*",              find_username_getpwuid);
+	dep_add("libs/userpass/getpwnam/*",              find_username_getpwnam);
+}
diff --git a/scconfig/src/util/arg_auto_menu.c b/scconfig/src/util/arg_auto_menu.c
new file mode 100644
index 0000000..a142a78
--- /dev/null
+++ b/scconfig/src/util/arg_auto_menu.c
@@ -0,0 +1,106 @@
+#include <stdlib.h>
+#include <string.h>
+#include "regex.h"
+#include "libs.h"
+#include "db.h"
+#include "menulib/scmenu.h"
+#include "arg_auto_menu.h"
+
+static const char *get_entry_val(const arg_auto_set_t *as)
+{
+	char *path;
+	const char *v, *res;
+
+	if (as->subvals == arg_lib_nodes) {
+		path = str_concat("/", as->subtree, "presents", NULL);
+		v = get(path);
+		if ((v != NULL) && (strcmp(v, sfalse) == 0))
+			res = "disable";
+		else
+			res = "enable";
+		free(path);
+		return res;
+	}
+}
+
+#define next_word(curr, next) \
+	do { \
+		next = strchr(curr, '|'); \
+		if (next != NULL) { \
+			*next = '\0'; \
+			next++; \
+		} \
+	} while(0)
+
+void append_settings_auto_set(scm_menu_entry_t *me, int max, const arg_auto_set_t *as, const char *include, const char *exclude, const char *remove_prefix, int entry_type, void *entry_data)
+{
+	const arg_auto_set_t *a;
+	scm_menu_entry_t *e;
+	int numa, n, ei;
+	char *accept;
+
+	/* count number of all settings, allocate accept[] */
+	numa = 0;
+	for(a = as; a->arg_key != NULL; a++)
+		numa++;
+	accept = calloc(numa, 1);
+
+	/* mark entries included in accept[] */
+	if (include != NULL) {
+		char *all = strclone(include), *next = all, *curr = all;
+		do {
+			next_word(curr, next);
+			re_comp(curr);
+			for(a = as, n = 0; a->arg_key != NULL; a++,n++)
+				if (re_exec(a->arg_key))
+					accept[n] = 1;
+			curr = next;
+		} while((next != NULL) && (*next != '\0'));
+		free(all);
+	}
+	else
+		memset(accept, 1, numa);
+
+	/* mark entries excluded in accept[] */
+	if (exclude != NULL) {
+		char *all = strclone(exclude), *next = all, *curr = all;
+		do {
+			next_word(curr, next);
+			re_comp(curr);
+			for(a = as, n = 0; a->arg_key != NULL; a++,n++)
+				if (re_exec(a->arg_key))
+					accept[n] = 0;
+			curr = next;
+		} while((next != NULL) && (*next != '\0'));
+		free(all);
+	}
+
+	/* find the terminator */
+	for(e = me, ei = 0; e->key != SCM_TERMINATOR; e++, ei++) ;
+
+	re_comp(remove_prefix);
+	printf("exclude:\n");
+	for(n = 0; n < numa; n++) {
+		if (accept[n]) {
+			char *sd;
+			if (re_exec(as[n].arg_key))
+				sd = re_subs_dup("");
+			else
+				sd = strclone(as[n].arg_key);
+
+			e->type = entry_type;
+			e->key = sd;
+			e->value = get_entry_val(&as[n]);
+			e->user_data = &as[n];
+			e->auto_data = entry_data;
+			e++;
+			ei++;
+			if (ei > max - 2)
+				break;
+		}
+	}
+
+	e->type = SCM_TERMINATOR;
+
+	free(accept);
+}
diff --git a/scconfig/src/util/arg_auto_menu.h b/scconfig/src/util/arg_auto_menu.h
new file mode 100644
index 0000000..192900f
--- /dev/null
+++ b/scconfig/src/util/arg_auto_menu.h
@@ -0,0 +1,5 @@
+#include "util/arg_auto_set.h"
+
+/* An optional bridge between auto_set and menulib: create a submenu from an
+   arg_auto_set_t table using regex key matching. */
+void append_settings_auto_set(scm_menu_entry_t *me, int max, const arg_auto_set_t *as, const char *include, const char *exclude, const char *remove_prefix, int entry_type, void *entry_data);
diff --git a/scconfig/src/util/arg_auto_set.c b/scconfig/src/util/arg_auto_set.c
new file mode 100644
index 0000000..df68a53
--- /dev/null
+++ b/scconfig/src/util/arg_auto_set.c
@@ -0,0 +1,95 @@
+/*
+    scconfig - set nodes from tables upon user CLI arguments
+    Copyright (C) 2015   Tibor Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "log.h"
+#include "libs.h"
+#include "db.h"
+#include "arg_auto_set.h"
+
+const arg_auto_set_node_t arg_lib_nodes[] = {
+	{"presents",    sfalse},
+	{"cflags",      ""},
+	{"ldflags",     ""},
+	{NULL, NULL}
+};
+
+const arg_auto_set_node_t arg_true[] = {
+	{"",    strue},
+	{NULL, NULL}
+};
+
+const arg_auto_set_node_t arg_false[] = {
+	{"",    sfalse},
+	{NULL, NULL}
+};
+
+
+int arg_auto_set(const char *key, const char *value, const arg_auto_set_t *table)
+{
+	const arg_auto_set_t *lib;
+	const arg_auto_set_node_t *node;
+
+	for(lib = table; lib->arg_key != NULL; lib++) {
+		if (strcmp(key, lib->arg_key) == 0) {
+			report("Executing lib table: %s\n", lib->arg_key);
+			for(node = lib->subvals; node->name != NULL; node++) {
+				char *s;
+				const char *setval;
+
+				setval = node->value;
+				if (strcmp(setval, "$") == 0)
+					setval = value;
+
+				if (*node->name != '\0') {
+					s = str_concat("/", lib->subtree, node->name, NULL);
+					put(s, setval);
+					free(s);
+				}
+				else
+					put(lib->subtree, setval);
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void arg_auto_print_options(FILE *fout, const char *line_prefix, const char *padding, const arg_auto_set_t *table)
+{
+	const arg_auto_set_t *t;
+	int pl;
+
+	pl = strlen(padding);
+
+	for(t = table; t->arg_key != NULL; t++) {
+		if (*t->help_txt == '$') {
+			int kl = strlen(t->arg_key);
+			if (kl > pl)
+				kl = pl;
+			fprintf(fout, "%s--%s%s%s\n", line_prefix, t->arg_key, padding+kl, t->help_txt+1);
+		}
+		else
+			fprintf(fout, "%s%s\n", line_prefix, t->help_txt);
+	}
+}
diff --git a/scconfig/src/util/arg_auto_set.h b/scconfig/src/util/arg_auto_set.h
new file mode 100644
index 0000000..94e66c2
--- /dev/null
+++ b/scconfig/src/util/arg_auto_set.h
@@ -0,0 +1,53 @@
+#ifndef ARG_AUTO_SET_H
+#define ARG_AUTO_SET_H
+/*
+	Handle a list of --disable-LIBs automatically.
+	The user sets up a table like:
+		static const arg_auto_set_t disable_libs[] = {
+		{"disable-gtk",       "libs/gui/gtk2",         arg_lib_nodes},
+		{"disable-lesstif",   "libs/gui/lesstif2",     arg_lib_nodes},
+		{NULL, NULL, NULL}
+	};
+	and at the end of hook_custom_arg() executes:
+	return arg_auto_set(key, value, disable_libs);
+
+	The call will set all nodes listed in arg_lib_nodes to disable gtk or
+	lesstif.
+
+	Mechanism: this happens before require()s on these nodes; require() will
+	find them already set and won't run the detection. Thus it is suitable for
+	disabling features (but not for enabling them).
+*/
+
+/* A table of node name-values to be set under a subtree */
+typedef struct {
+	const char *name;
+	const char *value;
+} arg_auto_set_node_t;
+
+/* A table of argument->subtree->subtree_values */
+typedef struct {
+	const char *arg_key;                  /* command line argument without the -- prefix */
+	const char *subtree;                  /* subtree path affected, e.g. libs/gui/gtk2 */
+	const arg_auto_set_node_t *subvals;   /* a set of values to be put() under the subtree */
+	const char *help_txt;
+} arg_auto_set_t;
+
+/* node set table for resetting the usual nodes under a library subtree:
+   presents, cflags, ldflags */
+extern const arg_auto_set_node_t arg_lib_nodes[];
+
+/* set the node true or false */
+extern const arg_auto_set_node_t arg_true[];
+extern const arg_auto_set_node_t arg_false[];
+
+/* Execute table: find a match on key and set all subvals of the match */
+int arg_auto_set(const char *key, const char *value, const arg_auto_set_t *table);
+
+/* Print options help from the table, one entry per line; if help text starts
+   with $, replace that with --arg_key and insert padding after that; padding
+   should be a string filled with spaces, as long as the longest argument key
+   plus the separator spaces. */
+void arg_auto_print_options(FILE *fout, const char *line_prefix, const char *padding, const arg_auto_set_t *table);
+
+#endif
diff --git a/scconfig/src/util/ls.c b/scconfig/src/util/ls.c
new file mode 100644
index 0000000..7afcf3a
--- /dev/null
+++ b/scconfig/src/util/ls.c
@@ -0,0 +1,79 @@
+/*
+    scconfig - ls built on dirent
+    Copyright (C) 2009  Szabolcs Nagy
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+		Project page: http://repo.hu/projects/scconfig
+		Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <windows.h>
+static int ls(char *arg) {
+	WIN32_FIND_DATA fd;
+	HANDLE h;
+	char p[MAX_PATH];
+	int i;
+
+	for (i = 0; i+2 < MAX_PATH; i++)
+		if (arg[i])
+			p[i] = arg[i];
+	if (i+2 < MAX_PATH) {
+		p[i] = '\\';
+		p[i+1] = '*';
+		p[i+2] = 0;
+	} else
+		return -1;
+
+	h = FindFirstFile(p, &fd);
+	if (h == INVALID_HANDLE_VALUE)
+		return -1;
+	printf("%s\n", fd.cFileName);
+
+	while (FindNextFile(h, &fd) != 0);
+		printf("%s\n", fd.cFileName);
+
+	FindClose(h);
+	return 0;
+}
+#else
+#include <dirent.h>
+static int ls(char *arg) {
+	DIR *dirp;
+	struct dirent *dp;
+
+	if ((dirp = opendir(arg)) == 0)
+		return -1;
+
+	while ((dp = readdir(dirp)) != 0)
+		printf("%s\n", dp->d_name);
+
+	closedir(dirp);
+	return 0;
+}
+#endif
+
+int main(int argc, char *argv[]) {
+	int i;
+	char *p = ".";
+
+	if (argc > 1)
+		p = argv[1];
+	return ls(p);
+}
+
diff --git a/scconfig/template/debug.tmpasm b/scconfig/template/debug.tmpasm
new file mode 100644
index 0000000..9da2385
--- /dev/null
+++ b/scconfig/template/debug.tmpasm
@@ -0,0 +1,10 @@
+# append debug or production flags to CFLAGS, depending on the global debug flag
+
+if /local/pcb/debug then
+	append /local/pcb/CFLAGS [@ -g -O0 @cc/argstd/Wall@ @]
+else
+	if /local/pcb/symbols then
+		append /local/pcb/CFLAGS [@ -g @]
+	end
+	append /local/pcb/CFLAGS { -O3 -DNDEBUG }
+end
diff --git a/src/Makefile.dep b/src/Makefile.dep
new file mode 100644
index 0000000..073e045
--- /dev/null
+++ b/src/Makefile.dep
@@ -0,0 +1,3679 @@
+### Generated file, do not edit, run make dep ###
+
+../src_plugins/autocrop/autocrop.o: ../src_plugins/autocrop/autocrop.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h move.h \
+ draw.h set.h polygon.h rtree.h plugins.h hid_actions.h dolists.h
+../src_plugins/autoplace/action.o: ../src_plugins/autoplace/action.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/autoplace/autoplace.h \
+ plugins.h set.h global.h hid_actions.h dolists.h
+../src_plugins/autoplace/autoplace.o: \
+ ../src_plugins/autoplace/autoplace.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/autoplace/autoplace.h \
+ box.h global.h misc_util.h compat_misc.h data.h draw.h error.h layer.h \
+ intersect.h rtree.h macro.h mirror.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h move.h mymem.h rats.h remove.h \
+ rotate.h
+../src_plugins/autoroute/action.o: ../src_plugins/autoroute/action.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/autoroute/autoroute.h \
+ action_helper.h global.h plugins.h set.h hid_actions.h dolists.h
+../src_plugins/autoroute/autoroute.o: \
+ ../src_plugins/autoroute/autoroute.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h macro.h ../src_plugins/autoroute/autoroute.h box.h \
+ misc_util.h create.h draw.h error.h find.h heap.h rtree.h misc.h mymem.h \
+ ../src_plugins/autoroute/mtspace.h ../src_plugins/autoroute/vector.h \
+ mymem.h polygon.h rtree.h rats.h remove.h thermal.h undo.h pcb-printf.h \
+ set.h layer.h
+../src_plugins/autoroute/mtspace.o: ../src_plugins/autoroute/mtspace.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h box.h global.h misc_util.h heap.h \
+ rtree.h ../src_plugins/autoroute/mtspace.h \
+ ../src_plugins/autoroute/vector.h
+../src_plugins/autoroute/vector.o: ../src_plugins/autoroute/vector.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/autoroute/vector.h
+../src_plugins/boardflip/boardflip.o: \
+ ../src_plugins/boardflip/boardflip.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h \
+ plugins.h hid_actions.h dolists.h
+../src_plugins/dbus/dbus-pcbmain.o: ../src_plugins/dbus/dbus-pcbmain.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/dbus/dbus-pcbmain.h
+../src_plugins/dbus/dbus.o: ../src_plugins/dbus/dbus.c \
+ ../src_plugins/dbus/dbus-pcbmain.h ../src_plugins/dbus/dbus-introspect.h \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h plugins.h \
+ hid_actions.h event.h compat_misc.h compat_lrealpath.h dolists.h
+../src_plugins/diag/diag.o: ../src_plugins/diag/diag.c ../config.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h layer.h \
+ ../src_plugins/diag/diag_conf.h action_helper.h hid_actions.h plugins.h \
+ conf.h pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h conf.h error.h dolists.h
+../src_plugins/diag/diag_conf.o: ../src_plugins/diag/diag_conf.c conf.h \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf.h
+../src_plugins/distalign/distalign.o: \
+ ../src_plugins/distalign/distalign.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h rats.h \
+ error.h move.h draw.h set.h plugins.h hid_actions.h dolists.h
+../src_plugins/distaligntext/distaligntext.o: \
+ ../src_plugins/distaligntext/distaligntext.c ../config.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h rats.h \
+ error.h move.h draw.h set.h plugins.h hid_actions.h conf_core.h conf.h \
+ pcb-printf.h ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h dolists.h
+../src_plugins/djopt/djopt.o: ../src_plugins/djopt/djopt.c ../config.h \
+ conf_core.h conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h \
+ unit.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h create.h remove.h move.h draw.h undo.h strflags.h find.h \
+ layer.h pcb-printf.h plugins.h hid_flags.h hid_actions.h \
+ ../src_plugins/djopt/djopt_conf.h conf.h dolists.h \
+ ../src_plugins/djopt/djopt_conf_fields.h
+../src_plugins/export_bboard/bboard.o: \
+ ../src_plugins/export_bboard/bboard.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h error.h buffer.h create.h \
+ layer.h plugins.h compat_misc.h misc_util.h hid.h hid_attrib.h \
+ hid_nogui.h hid_draw_helpers.h hid_init.h hid_helper.h
+../src_plugins/export_bom/bom.o: ../src_plugins/export_bom/bom.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h error.h misc.h mymem.h pcb-printf.h plugins.h \
+ compat_misc.h hid.h hid_nogui.h hid_attrib.h hid_helper.h hid_init.h
+../src_plugins/export_dxf/dxf.o: ../src_plugins/export_dxf/dxf.c \
+ ../config.h macro.h global.h const.h macro.h global_typedefs.h \
+ pcb_bool.h unit.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ globalconst.h polyarea.h list_common.h list_line.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h list_arc.h list_text.h list_poly.h \
+ list_pad.h list_pin.h list_rat.h vtonpoint.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h error.h draw.h \
+ hid_draw_helpers.h pcb-printf.h compat_misc.h layer.h hid_attrib.h \
+ hid_flags.h hid_helper.h hid.h hid_init.h plugins.h
+../src_plugins/export_gcode/curve.o: ../src_plugins/export_gcode/curve.c \
+ ../src_plugins/export_gcode/potracelib.h \
+ ../src_plugins/export_gcode/lists.h ../src_plugins/export_gcode/curve.h \
+ ../src_plugins/export_gcode/auxiliary.h ../config.h
+../src_plugins/export_gcode/decompose.o: \
+ ../src_plugins/export_gcode/decompose.c \
+ ../src_plugins/export_gcode/potracelib.h \
+ ../src_plugins/export_gcode/curve.h \
+ ../src_plugins/export_gcode/auxiliary.h ../config.h \
+ ../src_plugins/export_gcode/lists.h ../src_plugins/export_gcode/bitmap.h \
+ ../src_plugins/export_gcode/decompose.h
+../src_plugins/export_gcode/gcode.o: ../src_plugins/export_gcode/gcode.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ plugins.h global.h error.h data.h misc.h mymem.h rats.h hid_helper.h \
+ layer.h compat_misc.h hid.h hid_nogui.h hid_draw_helpers.h \
+ ../src_plugins/export_gcode/gcode.h ../src_plugins/export_gcode/bitmap.h \
+ ../src_plugins/export_gcode/potracelib.h \
+ ../src_plugins/export_gcode/curve.h \
+ ../src_plugins/export_gcode/auxiliary.h \
+ ../src_plugins/export_gcode/trace.h \
+ ../src_plugins/export_gcode/decompose.h pcb-printf.h hid_init.h \
+ hid_attrib.h hid_flags.h hid_color.h dolists.h
+../src_plugins/export_gcode/trace.o: ../src_plugins/export_gcode/trace.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h \
+ ../src_plugins/export_gcode/potracelib.h \
+ ../src_plugins/export_gcode/curve.h \
+ ../src_plugins/export_gcode/auxiliary.h \
+ ../src_plugins/export_gcode/lists.h ../src_plugins/export_gcode/trace.h
+../src_plugins/export_gerber/gerber.o: \
+ ../src_plugins/export_gerber/gerber.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h error.h draw.h layer.h \
+ pcb-printf.h plugins.h hid_helper.h compat_misc.h hid.h hid_nogui.h \
+ hid_draw_helpers.h hid_init.h hid_attrib.h hid_flags.h conf_core.h \
+ conf.h pcb-printf.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h
+../src_plugins/export_ipcd356/ipcd356.o: \
+ ../src_plugins/export_ipcd356/ipcd356.c ../config.h data.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h global.h rats.h error.h find.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h pcb-printf.h netlist.h \
+ conf_core.h conf.h pcb-printf.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ hid.h hid_nogui.h hid_helper.h hid_attrib.h hid_init.h plugins.h
+../src_plugins/export_lpr/lpr.o: ../src_plugins/export_lpr/lpr.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h plugins.h compat_misc.h hid.h \
+ ../src_plugins/export_lpr/../export_ps/ps.h hid_nogui.h hid_init.h \
+ hid_attrib.h hid_actions.h
+../src_plugins/export_nelma/nelma.o: ../src_plugins/export_nelma/nelma.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h error.h data.h global.h layer.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h rats.h plugins.h hid_helper.h \
+ hid.h hid_nogui.h hid_draw_helpers.h hid_init.h hid_attrib.h hid_flags.h \
+ hid_color.h dolists.h
+../src_plugins/export_png/png.o: ../src_plugins/export_png/png.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h error.h misc.h mymem.h layer.h misc_util.h compat_misc.h \
+ plugins.h hid.h hid_nogui.h hid_draw_helpers.h \
+ ../src_plugins/export_png/png.h hid_init.h hid_attrib.h hid_color.h \
+ hid_helper.h hid_flags.h dolists.h
+../src_plugins/export_ps/eps.o: ../src_plugins/export_ps/eps.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h layer.h misc.h mymem.h pcb-printf.h hid.h hid_nogui.h \
+ hid_draw_helpers.h ../src_plugins/export_ps/ps.h hid_init.h hid_attrib.h \
+ hid_helper.h hid_flags.h hid_color.h
+../src_plugins/export_ps/ps.o: ../src_plugins/export_ps/ps.c ../config.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h layer.h error.h draw.h \
+ pcb-printf.h plugins.h hid_helper.h hid.h hid_nogui.h hid_draw_helpers.h \
+ ../src_plugins/export_ps/ps.h draw_fab.h hid_init.h hid_attrib.h \
+ hid_flags.h hid_actions.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h compat_misc.h dolists.h
+../src_plugins/export_svg/svg.o: ../src_plugins/export_svg/svg.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h error.h misc.h mymem.h layer.h misc_util.h compat_misc.h \
+ plugins.h hid.h hid_nogui.h hid_draw_helpers.h hid_init.h hid_attrib.h \
+ hid_color.h hid_helper.h hid_flags.h dolists.h
+../src_plugins/export_test/export_test.o: \
+ ../src_plugins/export_test/export_test.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h error.h misc.h mymem.h pcb-printf.h plugins.h hid.h \
+ hid_nogui.h hid_attrib.h hid_helper.h hid_init.h
+../src_plugins/export_xy/xy.o: ../src_plugins/export_xy/xy.c ../config.h \
+ conf_core.h conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h \
+ unit.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h error.h misc.h mymem.h pcb-printf.h plugins.h \
+ compat_misc.h hid.h hid_nogui.h hid_attrib.h hid_helper.h hid_init.h
+../src_plugins/fontmode/fontmode.o: ../src_plugins/fontmode/fontmode.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h create.h data.h draw.h layer.h misc.h mymem.h move.h remove.h \
+ rtree.h strflags.h undo.h pcb-printf.h plugins.h hid_actions.h \
+ compat_misc.h dolists.h
+../src_plugins/fp_fs/fp_fs.o: ../src_plugins/fp_fs/fp_fs.c ../config.h \
+ mymem.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h paths.h plugins.h \
+ plug_footprint.h vtlibrary.h compat_fs.h compat_misc.h error.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h conf.h conf_core.h compat_inc.h
+../src_plugins/fp_wget/fp_wget.o: ../src_plugins/fp_wget/fp_wget.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/fp_wget/gedasymbols.h \
+ plug_footprint.h vtlibrary.h plugins.h
+../src_plugins/fp_wget/gedasymbols.o: \
+ ../src_plugins/fp_wget/gedasymbols.c ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h hid.h error.h vtroutestyle.h global_element.h list_element.h \
+ ht_element.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_plugins/fp_wget/wget_common.h \
+ ../src_plugins/fp_wget/gedasymbols.h plug_footprint.h vtlibrary.h \
+ plugins.h compat_misc.h
+../src_plugins/fp_wget/wget_common.o: \
+ ../src_plugins/fp_wget/wget_common.c \
+ ../src_plugins/fp_wget/wget_common.h
+../src_plugins/hid_batch/batch.o: ../src_plugins/hid_batch/batch.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h layer.h pcb-printf.h plugins.h \
+ compat_misc.h hid_draw_helpers.h hid_nogui.h hid_actions.h hid_init.h \
+ dolists.h
+../src_plugins/hid_gtk/ghid-cell-renderer-visibility.o: \
+ ../src_plugins/hid_gtk/ghid-cell-renderer-visibility.c \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h conf.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/ghid-cell-renderer-visibility.h
+../src_plugins/hid_gtk/ghid-coord-entry.o: \
+ ../src_plugins/hid_gtk/ghid-coord-entry.c \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h conf.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h pcb-printf.h
+../src_plugins/hid_gtk/ghid-layer-selector.o: \
+ ../src_plugins/hid_gtk/ghid-layer-selector.c \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h conf.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h pcb-printf.h \
+ ../src_plugins/hid_gtk/ghid-cell-renderer-visibility.h
+../src_plugins/hid_gtk/ghid-main-menu.o: \
+ ../src_plugins/hid_gtk/ghid-main-menu.c ../src_3rd/liblihata/tree.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h conf.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/hash.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h pcb-printf.h misc_util.h \
+ error.h ../src_plugins/hid_gtk/gschem_accel_label.h
+../src_plugins/hid_gtk/ghid-propedit.o: \
+ ../src_plugins/hid_gtk/ghid-propedit.c ../src_plugins/hid_gtk/gui.h \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid.h hid_cfg.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ event.h compat_misc.h ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ create.h polygon.h rtree.h
+../src_plugins/hid_gtk/ghid-route-style-selector.o: \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.c global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf_core.h conf.h global.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h ../src_plugins/hid_gtk/gtkhid.h \
+ conf_hid.h ../src_plugins/hid_gtk/gui.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h pcb-printf.h route_style.h \
+ set.h
+../src_plugins/hid_gtk/ghid-search.o: \
+ ../src_plugins/hid_gtk/ghid-search.c ../src_3rd/genlist/gendlist.h \
+ ../src_plugins/hid_gtk/gui.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid.h hid_cfg.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ event.h compat_misc.h ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ create.h ../src_plugins/hid_gtk/ghid-search.h \
+ ../src_plugins/hid_gtk/win_place.h hid_actions.h misc_util.h \
+ ../src_plugins/hid_gtk/ghid-search-tab.h
+../src_plugins/hid_gtk/gschem_accel_label.o: \
+ ../src_plugins/hid_gtk/gschem_accel_label.c \
+ ../src_plugins/hid_gtk/gschem_accel_label.h
+../src_plugins/hid_gtk/gtk_conf_list.o: \
+ ../src_plugins/hid_gtk/gtk_conf_list.c \
+ ../src_plugins/hid_gtk/gtk_conf_list.h conf.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf.h compat_misc.h
+../src_plugins/hid_gtk/gtkhid-gdk.o: ../src_plugins/hid_gtk/gtkhid-gdk.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf_core.h conf.h global.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h crosshair.h clip.h layer.h \
+ ../src_plugins/hid_gtk/gui.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h hid_draw_helpers.h \
+ hid_attrib.h hid_helper.h hid_color.h
+../src_plugins/hid_gtk/gtkhid-main.o: \
+ ../src_plugins/hid_gtk/gtkhid-main.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ action_helper.h crosshair.h error.h ../src_plugins/hid_gtk/gui.h \
+ global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h hid_nogui.h \
+ hid_draw_helpers.h pcb-printf.h plugins.h hid_attrib.h hid_init.h \
+ hid_flags.h hid_actions.h plug_footprint.h vtlibrary.h plug_io.h \
+ misc_util.h layer.h ../src_plugins/hid_gtk/ghid-search.h \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h dolists.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf_fields.h
+../src_plugins/hid_gtk/gui-command-window.o: \
+ ../src_plugins/hid_gtk/gui-command-window.c ../config.h conf_core.h \
+ conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h crosshair.h hid_actions.h
+../src_plugins/hid_gtk/gui-config.o: ../src_plugins/hid_gtk/gui-config.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/win_place.h ../src_plugins/hid_gtk/gtkhid.h \
+ conf_hid.h action_helper.h change.h plug_io.h error.h draw.h \
+ pcb-printf.h set.h hid_attrib.h misc_util.h \
+ ../src_plugins/hid_gtk/gtk_conf_list.h paths.h plug_footprint.h \
+ vtlibrary.h fptr_cast.h ../src_3rd/liblihata/tree.h
+../src_plugins/hid_gtk/gui-dialog-print.o: \
+ ../src_plugins/hid_gtk/gui-dialog-print.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h pcb-printf.h hid_attrib.h \
+ hid_init.h misc_util.h
+../src_plugins/hid_gtk/gui-dialog.o: ../src_plugins/hid_gtk/gui-dialog.c \
+ ../config.h compat_misc.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/hid_gtk/gui.h global.h \
+ hid.h hid_cfg.h ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ event.h ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h
+../src_plugins/hid_gtk/gui-drc-window.o: \
+ ../src_plugins/hid_gtk/gui-drc-window.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ error.h search.h misc_util.h draw.h layer.h pcb-printf.h undo.h set.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/win_place.h \
+ ../src_plugins/hid_gtk/gui-drc-window.h hid_actions.h
+../src_plugins/hid_gtk/gui-keyref-window.o: \
+ ../src_plugins/hid_gtk/gui-keyref-window.c ../config.h \
+ ../src_plugins/hid_gtk/gui.h global.h const.h macro.h global_typedefs.h \
+ pcb_bool.h unit.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ globalconst.h polyarea.h list_common.h list_line.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h list_arc.h list_text.h list_poly.h \
+ list_pad.h list_pin.h list_rat.h vtonpoint.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid.h hid_cfg.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ event.h compat_misc.h ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/win_place.h
+../src_plugins/hid_gtk/gui-library-window.o: \
+ ../src_plugins/hid_gtk/gui-library-window.c ../config.h conf_core.h \
+ conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/win_place.h buffer.h set.h plug_footprint.h \
+ vtlibrary.h ../src_plugins/hid_gtk/gui-library-window.h
+../src_plugins/hid_gtk/gui-log-window.o: \
+ ../src_plugins/hid_gtk/gui-log-window.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf_hid.h ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h \
+ hid_cfg_input.h ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h \
+ misc.h mymem.h ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/win_place.h pcb-printf.h hid_actions.h
+../src_plugins/hid_gtk/gui-misc.o: ../src_plugins/hid_gtk/gui-misc.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h crosshair.h data.h misc.h mymem.h action_helper.h set.h \
+ pcb-printf.h misc_util.h ../src_plugins/hid_gtk/gui.h hid.h hid_cfg.h \
+ hid_cfg_input.h ../src_3rd/liblihata/genht/htip.h hid_cfg.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h
+../src_plugins/hid_gtk/gui-netlist-window.o: \
+ ../src_plugins/hid_gtk/gui-netlist-window.c ../config.h conf_core.h \
+ conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h ../src_plugins/hid_gtk/win_place.h ../src_plugins/hid_gtk/gui.h \
+ hid.h hid_cfg.h hid_cfg_input.h ../src_3rd/liblihata/genht/htip.h \
+ hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h create.h draw.h error.h \
+ find.h mymem.h rats.h remove.h search.h misc_util.h select.h set.h \
+ undo.h hid_actions.h
+../src_plugins/hid_gtk/gui-output-events.o: \
+ ../src_plugins/hid_gtk/gui-output-events.c ../config.h conf_core.h \
+ conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h action_helper.h crosshair.h \
+ draw.h error.h layer.h set.h find.h search.h misc_util.h rats.h
+../src_plugins/hid_gtk/gui-pinout-preview.o: \
+ ../src_plugins/hid_gtk/gui-pinout-preview.c ../config.h conf_core.h \
+ conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h ../src_plugins/hid_gtk/gui.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h copy.h draw.h mymem.h \
+ move.h rotate.h
+../src_plugins/hid_gtk/gui-pinout-window.o: \
+ ../src_plugins/hid_gtk/gui-pinout-window.c ../config.h conf_core.h \
+ conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h ../src_plugins/hid_gtk/gui.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h \
+ ../src_plugins/hid_gtk/win_place.h copy.h draw.h mymem.h move.h rotate.h
+../src_plugins/hid_gtk/gui-top-window.o: \
+ ../src_plugins/hid_gtk/gui-top-window.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h global.h \
+ ../src_plugins/hid_gtk/gtkhid.h conf_hid.h ../src_plugins/hid_gtk/gui.h \
+ hid.h hid_cfg.h hid_cfg_input.h ../src_3rd/liblihata/genht/htip.h \
+ hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h hid_cfg_action.h \
+ action_helper.h buffer.h change.h copy.h create.h crosshair.h draw.h \
+ error.h plug_io.h find.h insert.h line.h mymem.h layer.h move.h \
+ pcb-printf.h polygon.h rtree.h rats.h remove.h rotate.h rubberband.h \
+ search.h misc_util.h select.h set.h undo.h free_atexit.h paths.h \
+ ../src_plugins/hid_gtk/gui-icons-mode-buttons.data \
+ ../src_plugins/hid_gtk/gui-icons-misc.data \
+ ../src_plugins/hid_gtk/win_place.h hid_attrib.h hid_actions.h \
+ hid_flags.h route_style.h
+../src_plugins/hid_gtk/gui-utils.o: ../src_plugins/hid_gtk/gui-utils.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/hid_gtk/gui.h global.h hid.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h misc.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h event.h compat_misc.h \
+ ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h
+../src_plugins/hid_gtk/menu_lht.o: ../src_plugins/hid_gtk/menu_lht.c
+../src_plugins/hid_gtk/win_place.o: ../src_plugins/hid_gtk/win_place.c \
+ ../src_plugins/hid_gtk/win_place.h ../src_plugins/hid_gtk/gui.h global.h \
+ ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid.h hid_cfg.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h data.h global.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h \
+ ../src_plugins/hid_gtk/ghid-coord-entry.h unit.h \
+ ../src_plugins/hid_gtk/ghid-main-menu.h \
+ ../src_plugins/hid_gtk/ghid-layer-selector.h \
+ ../src_plugins/hid_gtk/ghid-route-style-selector.h \
+ ../src_plugins/hid_gtk/gui-pinout-preview.h \
+ ../src_plugins/hid_gtk/ghid-propedit.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ event.h compat_misc.h ../src_plugins/hid_gtk/hid_gtk_conf.h conf.h
+../src_plugins/hid_lesstif/dialogs.o: \
+ ../src_plugins/hid_lesstif/dialogs.c \
+ ../src_plugins/hid_lesstif/xincludes.h ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ compat_misc.h global.h data.h crosshair.h layer.h misc.h mymem.h \
+ pcb-printf.h hid.h ../src_plugins/hid_lesstif/lesstif.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h hid_attrib.h hid_actions.h \
+ hid_init.h ../src_plugins/hid_lesstif/stdarg.h misc_util.h
+../src_plugins/hid_lesstif/library.o: \
+ ../src_plugins/hid_lesstif/library.c \
+ ../src_plugins/hid_lesstif/xincludes.h ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ compat_misc.h global.h data.h misc.h mymem.h set.h buffer.h vtptr.h \
+ plug_footprint.h vtlibrary.h hid.h ../src_plugins/hid_lesstif/lesstif.h \
+ hid_cfg_input.h ../src_3rd/liblihata/genht/htip.h hid_cfg.h \
+ ../src_plugins/hid_lesstif/stdarg.h
+../src_plugins/hid_lesstif/main.o: ../src_plugins/hid_lesstif/main.c \
+ ../src_plugins/hid_lesstif/xincludes.h ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h action_helper.h crosshair.h layer.h mymem.h misc.h \
+ mymem.h pcb-printf.h clip.h event.h error.h plugins.h hid.h hid_nogui.h \
+ hid_draw_helpers.h hid_cfg.h ../src_plugins/hid_lesstif/lesstif.h \
+ hid_cfg_input.h ../src_3rd/liblihata/genht/htip.h hid_cfg.h hid_attrib.h \
+ hid_helper.h hid_init.h hid_color.h hid_extents.h hid_flags.h \
+ hid_actions.h ../src_plugins/hid_lesstif/stdarg.h misc_util.h \
+ compat_misc.h dolists.h
+../src_plugins/hid_lesstif/menu.o: ../src_plugins/hid_lesstif/menu.c \
+ ../src_plugins/hid_lesstif/xincludes.h ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h error.h misc.h mymem.h pcb-printf.h layer.h hid.h \
+ hid_cfg.h hid_cfg_action.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h ../src_plugins/hid_lesstif/lesstif.h \
+ mymem.h paths.h hid_actions.h hid_flags.h \
+ ../src_plugins/hid_lesstif/stdarg.h compat_misc.h
+../src_plugins/hid_lesstif/menu_lht.o: \
+ ../src_plugins/hid_lesstif/menu_lht.c
+../src_plugins/hid_lesstif/netlist.o: \
+ ../src_plugins/hid_lesstif/netlist.c ../config.h \
+ ../src_plugins/hid_lesstif/xincludes.h compat_misc.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h find.h rats.h select.h \
+ undo.h remove.h crosshair.h draw.h hid.h hid_actions.h \
+ ../src_plugins/hid_lesstif/lesstif.h hid_cfg_input.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/htip.h \
+ hid_cfg.h ../src_plugins/hid_lesstif/stdarg.h
+../src_plugins/hid_lesstif/stdarg.o: ../src_plugins/hid_lesstif/stdarg.c \
+ ../src_plugins/hid_lesstif/stdarg.h \
+ ../src_plugins/hid_lesstif/xincludes.h
+../src_plugins/hid_lesstif/styles.o: ../src_plugins/hid_lesstif/styles.c \
+ ../src_plugins/hid_lesstif/xincludes.h ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ compat_misc.h global.h data.h set.h misc.h mymem.h mymem.h pcb-printf.h \
+ hid.h ../src_plugins/hid_lesstif/lesstif.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_cfg.h hid_flags.h \
+ ../src_plugins/hid_lesstif/stdarg.h misc_util.h
+../src_plugins/import_dsn/dsn.o: ../src_plugins/import_dsn/dsn.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h error.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h rats.h buffer.h change.h draw.h \
+ set.h undo.h pcb-printf.h create.h polygon.h rtree.h compat_misc.h hid.h \
+ hid_draw_helpers.h hid_nogui.h hid_actions.h hid_init.h hid_attrib.h \
+ hid_helper.h plugins.h dolists.h
+../src_plugins/import_edif/edif.o: ../src_plugins/import_edif/edif.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h error.h plugins.h \
+ compat_misc.h
+../src_plugins/import_edif/import_edif.o: \
+ ../src_plugins/import_edif/import_edif.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h plugins.h \
+ plug_import.h conf.h pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h netlist.h rats_patch.h plug_io.h
+../src_plugins/import_netlist/import_netlist.o: \
+ ../src_plugins/import_netlist/import_netlist.c ../config.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h plugins.h plug_io.h global.h conf.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h plug_import.h conf_core.h \
+ error.h misc.h mymem.h data.h rats_patch.h compat_misc.h
+../src_plugins/import_sch/import_sch.o: \
+ ../src_plugins/import_sch/import_sch.c ../config.h conf_core.h conf.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h data.h action_helper.h change.h error.h undo.h plugins.h misc.h \
+ mymem.h compat_fs.h pcb-printf.h remove.h rats.h hid_actions.h \
+ ../src_plugins/import_sch/import_sch_conf.h conf.h misc_util.h dolists.h \
+ ../src_plugins/import_sch/import_sch_conf_fields.h
+../src_plugins/io_kicad/io_kicad.o: ../src_plugins/io_kicad/io_kicad.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h plugins.h plug_io.h global.h conf.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h ../src_plugins/io_kicad/write.h \
+ data.h ../src_plugins/io_kicad/read.h
+../src_plugins/io_kicad/read.o: ../src_plugins/io_kicad/read.c \
+ ../src_3rd/gensexpr/gsxl.h ../src_3rd/gensexpr/gensexpr_impl.h \
+ ../src_3rd/gensexpr/gsx_parse.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ compat_misc.h ../config.h plug_io.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/hash.h conf.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/genvector/vtp0.h \
+ list_conf.h error.h data.h ../src_plugins/io_kicad/read.h global.h \
+ layer.h const.h netlist.h create.h polygon.h rtree.h misc.h mymem.h \
+ misc_util.h conf_core.h move.h macro.h
+../src_plugins/io_kicad/uniq_name.o: ../src_plugins/io_kicad/uniq_name.c \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/io_kicad/uniq_name.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h compat_misc.h ../config.h
+../src_plugins/io_kicad/write.o: ../src_plugins/io_kicad/write.c \
+ plug_io.h global.h ../config.h const.h macro.h global_typedefs.h \
+ pcb_bool.h unit.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ globalconst.h polyarea.h list_common.h list_line.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h list_arc.h list_text.h list_poly.h \
+ list_pad.h list_pin.h list_rat.h vtonpoint.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ error.h ../src_plugins/io_kicad/uniq_name.h \
+ ../src_3rd/liblihata/genht/htsp.h data.h ../src_plugins/io_kicad/write.h \
+ global.h layer.h const.h netlist.h misc.h mymem.h
+../src_plugins/io_kicad_legacy/io_kicad_legacy.o: \
+ ../src_plugins/io_kicad_legacy/io_kicad_legacy.c ../config.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h plugins.h plug_io.h global.h conf.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/io_kicad_legacy/write.h data.h
+../src_plugins/io_kicad_legacy/write.o: \
+ ../src_plugins/io_kicad_legacy/write.c plug_io.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ error.h ../src_plugins/io_kicad_legacy/../io_kicad/uniq_name.h \
+ ../src_3rd/liblihata/genht/htsp.h data.h \
+ ../src_plugins/io_kicad_legacy/write.h global.h layer.h const.h \
+ netlist.h misc.h mymem.h
+../src_plugins/io_lihata/common.o: ../src_plugins/io_lihata/common.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h macro.h \
+ ../src_plugins/io_lihata/common.h
+../src_plugins/io_lihata/io_lihata.o: \
+ ../src_plugins/io_lihata/io_lihata.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h plugins.h plug_io.h global.h conf.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h ../src_plugins/io_lihata/read.h \
+ ../src_plugins/io_lihata/write.h ../src_plugins/io_lihata/io_lihata.h \
+ conf.h ../src_plugins/io_lihata/lht_conf.h \
+ ../src_plugins/io_lihata/lht_conf_fields.h
+../src_plugins/io_lihata/read.o: ../src_plugins/io_lihata/read.c \
+ ../src_3rd/liblihata/tree.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/hash.h data.h \
+ global.h plugins.h plug_io.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h strflags.h compat_misc.h macro.h \
+ error.h misc.h mymem.h misc_util.h layer.h create.h vtptr.h \
+ ../src_plugins/io_lihata/common.h polygon.h rtree.h conf_core.h
+../src_plugins/io_lihata/write.o: ../src_plugins/io_lihata/write.c \
+ ../src_3rd/liblihata/tree.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/hash.h data.h \
+ global.h plugins.h plug_io.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h strflags.h compat_misc.h \
+ rats_patch.h hid_actions.h misc_util.h macro.h layer.h \
+ ../src_plugins/io_lihata/common.h ../src_plugins/io_lihata/write_style.h \
+ ../src_3rd/liblhtpers/lhtpers.h ../src_plugins/io_lihata/io_lihata.h \
+ conf.h ../src_plugins/io_lihata/lht_conf.h
+../src_plugins/io_lihata/write_style.o: \
+ ../src_plugins/io_lihata/write_style.c \
+ ../src_plugins/io_lihata/write_style.h ../src_3rd/liblhtpers/lhtpers.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h
+../src_plugins/io_pcb/attribs.o: ../src_plugins/io_pcb/attribs.c conf.h \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf.h misc.h mymem.h compat_misc.h
+../src_plugins/io_pcb/file.o: ../src_plugins/io_pcb/file.c ../config.h \
+ conf_core.h conf.h global.h const.h macro.h global_typedefs.h pcb_bool.h \
+ unit.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h buffer.h change.h create.h crosshair.h data.h error.h \
+ ../src_plugins/io_pcb/file.h plug_io.h hid.h layer.h misc.h mymem.h \
+ move.h mymem.h ../src_plugins/io_pcb/parse_common.h pcb-printf.h \
+ polygon.h rtree.h rats.h remove.h set.h strflags.h compat_fs.h paths.h \
+ rats_patch.h hid_actions.h hid_flags.h ../src_plugins/io_pcb/flags.h \
+ ../src_plugins/io_pcb/attribs.h route_style.h
+../src_plugins/io_pcb/flags.o: ../src_plugins/io_pcb/flags.c strflags.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h ../config.h globalconst.h \
+ global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ ../src_plugins/io_pcb/flags.h const.h macro.h
+../src_plugins/io_pcb/io_pcb.o: ../src_plugins/io_pcb/io_pcb.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h plugins.h \
+ ../src_plugins/io_pcb/parse_common.h plug_io.h global.h conf.h \
+ pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h ../src_plugins/io_pcb/file.h
+../src_plugins/io_pcb/parse_l.o: ../src_plugins/io_pcb/parse_l.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h ../src_plugins/io_pcb/flags.h crosshair.h data.h error.h \
+ ../src_plugins/io_pcb/file.h plug_io.h mymem.h misc.h mymem.h strflags.h \
+ ../src_plugins/io_pcb/parse_common.h ../src_plugins/io_pcb/parse_y.h \
+ create.h plug_footprint.h vtlibrary.h ../src_plugins/io_pcb/attribs.h \
+ compat_misc.h
+../src_plugins/io_pcb/parse_y.o: ../src_plugins/io_pcb/parse_y.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h layer.h create.h data.h error.h ../src_plugins/io_pcb/file.h \
+ plug_io.h mymem.h misc.h mymem.h ../src_plugins/io_pcb/parse_l.h \
+ polygon.h rtree.h remove.h rtree.h strflags.h thermal.h rats_patch.h \
+ ../src_plugins/io_pcb/flags.h route_style.h compat_misc.h \
+ ../src_plugins/io_pcb/parse_y.h
+../src_plugins/jostle/jostle.o: ../src_plugins/jostle/jostle.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h rats.h \
+ polygon.h rtree.h remove.h error.h set.h pcb-printf.h plugins.h \
+ hid_actions.h layer.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h misc_util.h dolists.h
+../src_plugins/lib_gensexpr/lib_gensexpr.o: \
+ ../src_plugins/lib_gensexpr/lib_gensexpr.c plugins.h
+../src_plugins/lib_legacy_func/lib_legacy_func.o: \
+ ../src_plugins/lib_legacy_func/lib_legacy_func.c \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h hid.h error.h vtroutestyle.h global_element.h list_element.h \
+ ht_element.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h action_helper.h \
+ change.h error.h undo.h plugins.h
+../src_plugins/loghid/loghid.o: ../src_plugins/loghid/loghid.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf.h global.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf.h data.h action_helper.h change.h error.h undo.h plugins.h \
+ hid_init.h hid_attrib.h dolists.h
+../src_plugins/mincut/pcb-mincut/graph.o: \
+ ../src_plugins/mincut/pcb-mincut/graph.c \
+ ../src_plugins/mincut/pcb-mincut/graph.h \
+ ../src_plugins/mincut/pcb-mincut/../../../config.h
+../src_plugins/mincut/pcb-mincut/solve.o: \
+ ../src_plugins/mincut/pcb-mincut/solve.c \
+ ../src_plugins/mincut/pcb-mincut/solve.h \
+ ../src_plugins/mincut/pcb-mincut/graph.h \
+ ../src_plugins/mincut/pcb-mincut/../../../config.h compat_misc.h \
+ ../config.h
+../src_plugins/mincut/rats_mincut.o: ../src_plugins/mincut/rats_mincut.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h create.h global.h data.h draw.h \
+ error.h plug_io.h conf.h pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h find.h misc.h mymem.h mymem.h \
+ polygon.h rtree.h rats.h search.h misc_util.h set.h undo.h plugins.h \
+ compat_misc.h ../src_plugins/mincut/pcb-mincut/graph.h \
+ ../src_plugins/mincut/pcb-mincut/../../../config.h \
+ ../src_plugins/mincut/pcb-mincut/solve.h \
+ ../src_plugins/mincut/pcb-mincut/graph.h conf.h \
+ ../src_plugins/mincut/rats_mincut_conf.h stub_mincut.h \
+ ../src_plugins/mincut/rats_mincut_conf_fields.h
+../src_plugins/oldactions/oldactions.o: \
+ ../src_plugins/oldactions/oldactions.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf.h global.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf.h data.h action_helper.h change.h error.h undo.h plugins.h \
+ hid_actions.h plug_footprint.h vtlibrary.h dolists.h
+../src_plugins/polycombine/polycombine.o: \
+ ../src_plugins/polycombine/polycombine.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h macro.h create.h \
+ remove.h hid.h error.h rtree.h polygon.h rtree.h polyarea.h strflags.h \
+ find.h misc.h ../src_3rd/genvector/gds_char.h mymem.h draw.h undo.h \
+ plugins.h hid_actions.h dolists.h
+../src_plugins/polystitch/polystitch.o: \
+ ../src_plugins/polystitch/polystitch.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h macro.h create.h \
+ remove.h hid.h error.h rtree.h draw.h set.h polygon.h rtree.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h plugins.h hid_actions.h \
+ dolists.h
+../src_plugins/propedit/propedit.o: ../src_plugins/propedit/propedit.c \
+ plugins.h ../src_plugins/propedit/props.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_plugins/propedit/propsel.h \
+ hid_actions.h pcb-printf.h ../src_3rd/genvector/gds_char.h error.h \
+ dolists.h
+../src_plugins/propedit/props.o: ../src_plugins/propedit/props.c \
+ ../src_plugins/propedit/props.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_plugins/propedit/propsel.h \
+ compat_misc.h pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/genht/ht.c
+../src_plugins/propedit/propsel.o: ../src_plugins/propedit/propsel.c \
+ data.h global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h \
+ unit.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/propedit/props.h \
+ global.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_plugins/propedit/propsel.h \
+ change.h misc.h ../src_3rd/genvector/gds_char.h mymem.h misc_util.h \
+ compat_misc.h undo.h rotate.h
+../src_plugins/puller/puller.o: ../src_plugins/puller/puller.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h create.h data.h draw.h misc.h mymem.h move.h pcb-printf.h \
+ remove.h rtree.h strflags.h undo.h layer.h plugins.h hid_actions.h \
+ misc_util.h dolists.h
+../src_plugins/query/basic_fnc.o: ../src_plugins/query/basic_fnc.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h \
+ ../src_plugins/query/query_access.h ../src_plugins/query/query.h \
+ obj_any.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genregex/regex_se.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ ../src_plugins/query/fields_sphash.h ../src_plugins/query/query_exec.h
+../src_plugins/query/fields_sphash.o: \
+ ../src_plugins/query/fields_sphash.c
+../src_plugins/query/query.o: ../src_plugins/query/query.c ../config.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h conf.h global.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h conf.h data.h action_helper.h \
+ change.h error.h undo.h plugins.h hid_init.h hid_actions.h compat_misc.h \
+ ../src_plugins/query/query.h obj_any.h ../src_3rd/genregex/regex_se.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ ../src_plugins/query/fields_sphash.h fptr_cast.h
+../src_plugins/query/query_access.o: ../src_plugins/query/query_access.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h \
+ ../src_plugins/query/query_access.h ../src_plugins/query/query.h \
+ obj_any.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genregex/regex_se.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ ../src_plugins/query/fields_sphash.h ../src_plugins/query/query_exec.h \
+ misc.h ../src_3rd/genvector/gds_char.h mymem.h layer.h
+../src_plugins/query/query_act.o: ../src_plugins/query/query_act.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_plugins/query/query.h obj_any.h \
+ ../src_3rd/liblihata/genht/htsi.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genregex/regex_se.h ../src_3rd/genregex/regex_templ.h \
+ ../src_3rd/genregex/regex.h ../src_plugins/query/fields_sphash.h \
+ ../src_plugins/query/query_y.h ../src_plugins/query/query_exec.h set.h \
+ global.h draw.h select.h macro.h dolists.h
+../src_plugins/query/query_exec.o: ../src_plugins/query/query_exec.c \
+ global.h ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h \
+ ../src_plugins/query/query.h obj_any.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genregex/regex_se.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ ../src_plugins/query/fields_sphash.h ../src_plugins/query/query_exec.h \
+ ../src_plugins/query/query_access.h
+../src_plugins/query/query_l.o: ../src_plugins/query/query_l.c global.h \
+ ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h unit.h ../src_plugins/query/query.h \
+ obj_any.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genregex/regex_se.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ ../src_plugins/query/fields_sphash.h ../src_plugins/query/query_y.h \
+ compat_misc.h layer.h
+../src_plugins/query/query_y.o: ../src_plugins/query/query_y.c global.h \
+ ../config.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h unit.h ../src_plugins/query/query.h \
+ obj_any.h ../src_3rd/liblihata/genht/htsi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genregex/regex_se.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ ../src_plugins/query/fields_sphash.h ../src_plugins/query/query_l.h \
+ compat_misc.h
+../src_plugins/renumber/renumber.o: ../src_plugins/renumber/renumber.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h action_helper.h \
+ change.h error.h undo.h misc.h ../src_3rd/genvector/gds_char.h mymem.h \
+ set.h plugins.h hid_actions.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h compat_misc.h pcb-printf.h \
+ dolists.h
+../src_plugins/renumber/renumberblock.o: \
+ ../src_plugins/renumber/renumberblock.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h error.h \
+ change.h conf_core.h conf.h pcb-printf.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h
+../src_plugins/report/report.o: ../src_plugins/report/report.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_plugins/report/report.h global.h crosshair.h data.h drill.h \
+ error.h search.h misc_util.h misc.h mymem.h mymem.h rats.h rtree.h \
+ strflags.h macro.h undo.h find.h draw.h pcb-printf.h plugins.h \
+ hid_actions.h misc_util.h ../src_plugins/report/report_conf.h conf.h \
+ compat_misc.h layer.h ../src_3rd/genregex/regex_sei.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h dolists.h \
+ ../src_plugins/report/report_conf_fields.h
+../src_plugins/shand_cmd/command.o: ../src_plugins/shand_cmd/command.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ global.h action_helper.h buffer.h ../src_plugins/shand_cmd/command.h \
+ data.h error.h plug_io.h mymem.h misc.h mymem.h rats.h set.h plugins.h \
+ hid_actions.h compat_misc.h dolists.h
+../src_plugins/smartdisperse/smartdisperse.o: \
+ ../src_plugins/smartdisperse/smartdisperse.c \
+ ../src_3rd/liblihata/genht/htpi.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/hash.h data.h \
+ global.h hid.h misc.h ../src_3rd/genvector/gds_char.h mymem.h create.h \
+ rtree.h undo.h rats.h error.h move.h draw.h set.h plugins.h \
+ hid_actions.h dolists.h
+../src_plugins/stroke/stroke.o: ../src_plugins/stroke/stroke.c \
+ ../config.h global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf.h global.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf.h conf_core.h data.h crosshair.h stub_stroke.h rotate.h undo.h \
+ set.h error.h misc.h mymem.h plugins.h
+../src_plugins/teardrops/teardrops.o: \
+ ../src_plugins/teardrops/teardrops.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h global.h hid.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h create.h rtree.h undo.h \
+ plugins.h hid_actions.h dolists.h
+../src_plugins/toporouter/toporouter.o: \
+ ../src_plugins/toporouter/toporouter.c \
+ ../src_plugins/toporouter/toporouter.h data.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h macro.h \
+ ../src_plugins/toporouter/../autoroute/autoroute.h global.h box.h \
+ misc_util.h create.h draw.h error.h find.h heap.h rtree.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h mymem.h polygon.h rtree.h rats.h \
+ remove.h thermal.h undo.h ../src_3rd/gts/gts.h pcb-printf.h
+../src_plugins/vendordrill/vendor.o: ../src_plugins/vendordrill/vendor.c \
+ ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_3rd/genregex/regex_sei.h ../src_3rd/genregex/regex_templ.h \
+ ../src_3rd/genregex/regex.h change.h data.h draw.h error.h global.h \
+ set.h undo.h ../src_plugins/vendordrill/vendor.h stub_vendor.h plugins.h \
+ hid_flags.h hid_actions.h hid_cfg.h \
+ ../src_plugins/vendordrill/vendor_conf.h conf.h compat_misc.h \
+ ../src_3rd/liblihata/tree.h dolists.h \
+ ../src_plugins/vendordrill/vendor_conf_fields.h
+../src_3rd/gensexpr/gsx_parse.o: ../src_3rd/gensexpr/gsx_parse.c \
+ ../src_3rd/gensexpr/gsx_parse.h
+../src_3rd/gensexpr/gsxl.o: ../src_3rd/gensexpr/gsxl.c \
+ ../src_3rd/gensexpr/gsxl.h ../src_3rd/gensexpr/gensexpr_impl.h \
+ ../src_3rd/gensexpr/gsx_parse.h ../src_3rd/gensexpr/gensexpr_impl.c
+../src_3rd/genregex/regex.o: ../src_3rd/genregex/regex.c \
+ ../src_3rd/genregex/regex.h
+../src_3rd/genregex/regex_se.o: ../src_3rd/genregex/regex_se.c \
+ ../src_3rd/genregex/regex_templ.c ../src_3rd/genregex/regex_templ.h \
+ ../src_3rd/genregex/regex.h
+../src_3rd/genregex/regex_sei.o: ../src_3rd/genregex/regex_sei.c \
+ ../src_3rd/genregex/regex_templ.c ../src_3rd/genregex/regex_templ.h \
+ ../src_3rd/genregex/regex.h
+../src_3rd/genvector/gds_char.o: ../src_3rd/genvector/gds_char.c \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h \
+ ../src_3rd/genvector/genvector_impl.c
+../src_3rd/genvector/vtp0.o: ../src_3rd/genvector/vtp0.c \
+ ../src_3rd/genvector/vtp0.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h \
+ ../src_3rd/genvector/genvector_impl.c
+../src_3rd/liblhtpers/lhtpers.o: ../src_3rd/liblhtpers/lhtpers.c \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/liblhtpers/lhtpers.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblhtpers/output.c ../src_3rd/liblhtpers/pers_list.c \
+ ../src_3rd/liblhtpers/pers_hash.c ../src_3rd/liblhtpers/pers_text.c \
+ ../src_3rd/liblhtpers/pers_table.c
+../src_3rd/liblihata/dom.o: ../src_3rd/liblihata/dom.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h ../src_3rd/liblihata/hash_str.h
+../src_3rd/liblihata/dom_hash.o: ../src_3rd/liblihata/dom_hash.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h ../src_3rd/liblihata/hash_str.h
+../src_3rd/liblihata/dom_list.o: ../src_3rd/liblihata/dom_list.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h
+../src_3rd/liblihata/dom_table.o: ../src_3rd/liblihata/dom_table.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h ../src_3rd/liblihata/tree.h \
+ ../src_3rd/liblihata/dom.h
+../src_3rd/liblihata/genht/hash.o: ../src_3rd/liblihata/genht/hash.c
+../src_3rd/liblihata/genht/htip.o: ../src_3rd/liblihata/genht/htip.c \
+ ../src_3rd/liblihata/genht/htip.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h ../src_3rd/liblihata/genht/ht.c
+../src_3rd/liblihata/genht/htpi.o: ../src_3rd/liblihata/genht/htpi.c \
+ ../src_3rd/liblihata/genht/htpi.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h ../src_3rd/liblihata/genht/ht.c
+../src_3rd/liblihata/genht/htpp.o: ../src_3rd/liblihata/genht/htpp.c \
+ ../src_3rd/liblihata/genht/htpp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h ../src_3rd/liblihata/genht/ht.c
+../src_3rd/liblihata/genht/htsi.o: ../src_3rd/liblihata/genht/htsi.c \
+ ../src_3rd/liblihata/genht/htsi.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h ../src_3rd/liblihata/genht/ht.c
+../src_3rd/liblihata/genht/htsp.o: ../src_3rd/liblihata/genht/htsp.c \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h ../src_3rd/liblihata/genht/ht.c
+../src_3rd/liblihata/hash_str.o: ../src_3rd/liblihata/hash_str.c
+../src_3rd/liblihata/lihata.o: ../src_3rd/liblihata/lihata.c \
+ ../src_3rd/liblihata/lihata.h
+../src_3rd/liblihata/parser.o: ../src_3rd/liblihata/parser.c \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/lihata.h
+../src_3rd/liblihata/tree.o: ../src_3rd/liblihata/tree.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/tree.h ../src_3rd/liblihata/dom_internal.h
+../src_3rd/liblihata/tree_hash.o: ../src_3rd/liblihata/tree_hash.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h ../src_3rd/liblihata/tree.h \
+ ../src_3rd/liblihata/dom.h
+../src_3rd/liblihata/tree_list.o: ../src_3rd/liblihata/tree_list.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h ../src_3rd/liblihata/tree.h \
+ ../src_3rd/liblihata/dom.h
+../src_3rd/liblihata/tree_path.o: ../src_3rd/liblihata/tree_path.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/tree.h
+../src_3rd/liblihata/tree_symlink.o: ../src_3rd/liblihata/tree_symlink.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/tree.h
+../src_3rd/liblihata/tree_table.o: ../src_3rd/liblihata/tree_table.c \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/dom_internal.h ../src_3rd/liblihata/tree.h \
+ ../src_3rd/liblihata/dom.h
+action_act.o: action_act.c ../config.h error.h action_helper.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid_actions.h undo.h
+action_helper.o: action_helper.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ action_helper.h buffer.h change.h copy.h create.h crosshair.h data.h \
+ draw.h find.h insert.h line.h misc.h mymem.h move.h polygon.h rtree.h \
+ rats.h remove.h rotate.h rubberband.h search.h misc_util.h select.h \
+ set.h undo.h stub_stroke.h funchash_core.h funchash.h \
+ funchash_core_list.h hid_actions.h compat_misc.h layer.h
+buffer.o: buffer.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ action_helper.h buffer.h copy.h create.h crosshair.h data.h plug_io.h \
+ mirror.h misc.h mymem.h misc_util.h polygon.h rtree.h rotate.h remove.h \
+ select.h set.h funchash_core.h funchash.h funchash_core_list.h \
+ compat_misc.h layer.h
+buildin.o: buildin.c plugins.h buildin.h
+change.o: change.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ crosshair.h data.h draw.h misc.h mymem.h mirror.h polygon.h rtree.h \
+ select.h undo.h hid_actions.h layer.h
+change_act.o: change_act.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h funchash_core.h funchash.h funchash_core_list.h action_helper.h \
+ hid_actions.h change.h draw.h search.h misc_util.h misc.h mymem.h set.h \
+ undo.h rubberband.h compat_misc.h layer.h
+clip.o: clip.c ../config.h clip.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+compat_dl.o: compat_dl.c ../config.h compat_dl.h compat_inc.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+compat_fs.o: compat_fs.c ../config.h compat_inc.h compat_fs.h \
+ compat_misc.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h hid.h error.h vtroutestyle.h global_element.h list_element.h \
+ ht_element.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h mymem.h
+compat_lrealpath.o: compat_lrealpath.c ../config.h compat_lrealpath.h \
+ compat_misc.h
+compat_misc.o: compat_misc.c ../config.h compat_misc.h compat_inc.h \
+ global.h const.h macro.h global_typedefs.h pcb_bool.h unit.h \
+ global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h \
+ list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+conf.o: conf.c ../src_3rd/liblihata/genht/hash.h \
+ ../src_3rd/liblihata/tree.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h conf.h global.h ../config.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h conf_core.h conf_hid.h hid_cfg.h \
+ hid_init.h hid_attrib.h misc_util.h paths.h compat_fs.h compat_misc.h \
+ ../src_3rd/genvector/genvector_impl.c
+conf_act.o: conf_act.c global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf_core.h misc.h mymem.h misc_util.h route_style.h
+conf_core.o: conf_core.c conf.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf_core.h conf_core_fields.h
+conf_hid.o: conf_hid.c conf_hid.h conf.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_3rd/liblihata/genht/htpp.h conf_core.h
+conf_internal.o: conf_internal.c
+copy.o: copy.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ create.h data.h draw.h misc.h mymem.h layer.h move.h polygon.h rtree.h \
+ select.h undo.h compat_misc.h
+create.o: create.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ create.h data.h misc.h mymem.h layer.h rtree.h search.h misc_util.h \
+ undo.h plug_io.h stub_vendor.h hid_actions.h paths.h compat_misc.h
+crosshair.o: crosshair.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ box.h misc_util.h crosshair.h data.h draw.h line.h rtree.h search.h \
+ polygon.h misc.h mymem.h hid_actions.h layer.h compat_misc.h
+data.o: data.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+draw.o: draw.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h draw.h misc.h mymem.h rotate.h rtree.h draw_fab.h hid_helper.h \
+ layer.h
+draw_fab.o: draw_fab.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h draw.h drill.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h draw_fab.h polygon.h rtree.h \
+ layer.h
+drill.o: drill.c ../config.h mymem.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+error.o: error.c ../config.h global.h const.h macro.h global_typedefs.h \
+ pcb_bool.h unit.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ globalconst.h polyarea.h list_common.h list_line.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h list_arc.h list_text.h list_poly.h \
+ list_pad.h list_pin.h list_rat.h vtonpoint.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h plug_io.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ compat_misc.h
+event.o: event.c event.h error.h fptr_cast.h
+file_act.o: file_act.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h crosshair.h set.h plug_io.h plug_import.h \
+ buffer.h misc.h mymem.h remove.h create.h draw.h find.h search.h \
+ misc_util.h hid_actions.h hid_attrib.h compat_misc.h
+find.o: find.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h draw.h find.h misc.h mymem.h rtree.h polygon.h search.h \
+ misc_util.h set.h undo.h rats.h plug_io.h hid_actions.h compat_misc.h \
+ layer.h find_geo.c find_lookup.c find_drc.c find_misc.c find_clear.c \
+ find_debug.c find_print.c
+find_act.o: find_act.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h find.h
+fptr_cast.o: fptr_cast.c fptr_cast.h
+free_atexit.o: free_atexit.c
+funchash.o: funchash.c ../src_3rd/liblihata/genht/htpi.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h funchash_core.h funchash.h \
+ funchash_core_list.h macro.h
+gui_act.o: gui_act.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h undo.h funchash_core.h funchash.h \
+ funchash_core_list.h draw.h search.h misc_util.h crosshair.h find.h \
+ set.h misc.h mymem.h stub_stroke.h hid_actions.h hid_init.h \
+ route_style.h layer.h
+heap.o: heap.c ../config.h heap.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+hid_actions.o: hid_actions.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_3rd/liblihata/genht/htsp.h event.h hid_actions.h compat_misc.h
+hid_attrib.o: hid_attrib.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid_attrib.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h misc_util.h pcb-printf.h \
+ compat_fs.h
+hid_cfg.o: hid_cfg.c ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/tree.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/hash.h \
+ hid_cfg.h paths.h compat_misc.h
+hid_cfg_action.o: hid_cfg_action.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid_cfg_action.h hid_cfg.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h hid_actions.h
+hid_cfg_input.o: hid_cfg_input.c ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/tree.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h hid.h error.h vtroutestyle.h global_element.h list_element.h \
+ ht_element.h ../src_3rd/liblihata/genht/ht.h hid_cfg.h hid_cfg_input.h \
+ ../src_3rd/liblihata/genht/htip.h hid_actions.h hid_cfg_action.h
+hid_color.o: hid_color.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid_color.h compat_misc.h \
+ ../src_3rd/liblihata/genht/ht.c
+hid_draw_helpers.o: hid_draw_helpers.c global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h polygon.h rtree.h
+hid_extents.o: hid_extents.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h layer.h hid_draw_helpers.h \
+ hid_helper.h
+hid_flags.o: hid_flags.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h misc.h ../src_3rd/genvector/gds_char.h \
+ mymem.h conf.h pcb-printf.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ hid_flags.h ../src_3rd/liblihata/genht/htsp.h hid_actions.h
+hid_helper.o: hid_helper.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h misc.h ../src_3rd/genvector/gds_char.h \
+ mymem.h layer.h hid_helper.h hid_attrib.h compat_misc.h
+hid_init.o: hid_init.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid_nogui.h compat_dl.h compat_inc.h \
+ misc.h ../src_3rd/genvector/gds_char.h mymem.h plugins.h hid_attrib.h \
+ hid_flags.h misc_util.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h compat_misc.h fptr_cast.h
+hid_nogui.o: hid_nogui.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h compat_misc.h
+ht_element.o: ht_element.c global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/liblihata/genht/ht.c
+insert.o: insert.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ create.h crosshair.h data.h draw.h line.h misc.h mymem.h polygon.h \
+ rtree.h search.h misc_util.h select.h set.h undo.h layer.h
+intersect.o: intersect.c ../config.h intersect.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+layer.o: layer.c global.h ../config.h const.h macro.h global_typedefs.h \
+ pcb_bool.h unit.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ globalconst.h polyarea.h list_common.h list_line.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h list_arc.h list_text.h list_poly.h \
+ list_pad.h list_pin.h list_rat.h vtonpoint.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ layer.h hid_actions.h compat_misc.h
+line.o: line.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h crosshair.h find.h line.h misc.h mymem.h rtree.h layer.h
+list_arc.o: list_arc.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_arc.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_conf.o: list_conf.c conf.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_element.o: list_element.c global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/genlist/gentdlist_impl.c
+list_line.o: list_line.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_pad.o: list_pad.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_pad.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_pin.o: list_pin.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_pin.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_poly.o: list_poly.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_poly.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_rat.o: list_rat.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_rat.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+list_text.o: list_text.c global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ list_text.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ ../src_3rd/genlist/gentdlist_impl.c
+main.o: main.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h buffer.h create.h crosshair.h plug_io.h set.h layer.h misc.h \
+ mymem.h compat_lrealpath.h free_atexit.h polygon.h rtree.h buildin.h \
+ paths.h strflags.h plugins.h plug_footprint.h vtlibrary.h event.h \
+ funchash.h hid_actions.h hid_attrib.h hid_init.h compat_misc.h dolists.h \
+ generated_lists.h
+main_act.o: main_act.c ../config.h action_helper.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h hid_actions.h hid_attrib.h hid_init.h \
+ data.h conf_core.h conf.h pcb-printf.h ../src_3rd/genvector/gds_char.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h
+mirror.o: mirror.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h mirror.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h polygon.h rtree.h
+misc.o: misc.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ box.h misc_util.h crosshair.h data.h plug_io.h misc.h mymem.h move.h \
+ polygon.h rtree.h rotate.h rubberband.h set.h undo.h compat_misc.h \
+ hid_actions.h hid_init.h
+misc_util.o: misc_util.c ../config.h misc_util.h pcb_bool.h unit.h
+move.o: move.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ create.h data.h draw.h misc.h mymem.h move.h polygon.h rtree.h search.h \
+ misc_util.h select.h undo.h hid_actions.h compat_misc.h layer.h
+mymem.o: mymem.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h mymem.h rtree.h rats_patch.h
+netlist.o: netlist.c ../config.h ../src_3rd/genregex/regex_sei.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ action_helper.h global.h const.h macro.h global_typedefs.h pcb_bool.h \
+ unit.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h plug_io.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ find.h mymem.h rats.h create.h rats_patch.h hid_actions.h compat_misc.h \
+ netlist.h
+netlist_act.o: netlist_act.c ../config.h ../src_3rd/genregex/regex_sei.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h \
+ action_helper.h global.h const.h macro.h global_typedefs.h pcb_bool.h \
+ unit.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ polyarea.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h plug_io.h conf.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ find.h mymem.h rats.h create.h rats_patch.h hid_actions.h compat_misc.h \
+ netlist.h
+obj_any.o: obj_any.c obj_any.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h ../config.h globalconst.h \
+ global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h ../src_3rd/genlist/gentdlist_impl.c
+object_act.o: object_act.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h change.h undo.h funchash_core.h funchash.h \
+ funchash_core_list.h search.h misc_util.h move.h draw.h mirror.h \
+ rotate.h set.h copy.h misc.h mymem.h rubberband.h buffer.h remove.h \
+ create.h compat_misc.h layer.h
+paths.o: paths.c ../config.h paths.h error.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ conf_core.h
+pcb-printf.o: pcb-printf.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h
+plug_footprint.o: plug_footprint.c ../config.h plug_footprint.h \
+ vtlibrary.h global_objs.h ../src_3rd/genlist/gendlist.h globalconst.h \
+ global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h plugins.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h conf_core.h conf.h global.h const.h \
+ macro.h list_common.h list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h hid.h error.h vtroutestyle.h global_element.h list_element.h \
+ ht_element.h ../src_3rd/liblihata/genht/ht.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h compat_misc.h
+plug_footprint_act.o: plug_footprint_act.c ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h plug_footprint.h vtlibrary.h
+plug_import.o: plug_import.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ plugins.h plug_import.h
+plug_io.o: plug_io.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ buffer.h change.h create.h data.h plug_io.h misc.h mymem.h remove.h \
+ set.h paths.h rats_patch.h hid_actions.h hid_flags.h plugins.h event.h \
+ compat_misc.h route_style.h compat_fs.h compat_inc.h
+plugins.o: plugins.c plugins.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/genvector/gds_char.h \
+ compat_misc.h
+polygon.o: polygon.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ box.h misc_util.h create.h crosshair.h data.h draw.h polygon.h rtree.h \
+ remove.h search.h set.h thermal.h mymem.h undo.h misc.h layer.h
+polygon1.o: polygon1.c rtree.h global.h ../config.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h heap.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h
+polygon_act.o: polygon_act.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h undo.h funchash_core.h funchash.h \
+ funchash_core_list.h polygon.h rtree.h draw.h search.h misc_util.h \
+ crosshair.h
+rats.o: rats.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ create.h data.h draw.h find.h misc.h mymem.h layer.h polygon.h rtree.h \
+ rats.h search.h misc_util.h undo.h stub_mincut.h route_style.h \
+ compat_misc.h netlist.h
+rats_act.o: rats_act.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h undo.h set.h misc.h mymem.h find.h remove.h \
+ funchash_core.h funchash.h funchash_core_list.h rats.h draw.h
+rats_patch.o: rats_patch.c rats_patch.h global.h ../config.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h create.h data.h buffer.h remove.h copy.h \
+ compat_misc.h
+remove.o: remove.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h draw.h misc.h mymem.h move.h polygon.h rtree.h remove.h select.h \
+ undo.h
+remove_act.o: remove_act.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h action_helper.h set.h remove.h \
+ funchash_core.h funchash.h funchash_core_list.h
+rotate.o: rotate.c ../config.h data.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h draw.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h polygon.h rtree.h rotate.h \
+ rubberband.h search.h misc_util.h select.h set.h undo.h conf_core.h \
+ conf.h pcb-printf.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h
+route_style.o: route_style.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h route_style.h misc.h mymem.h conf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h
+rtree.o: rtree.c ../config.h rtree.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+rubberband.o: rubberband.c ../config.h create.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h data.h misc.h \
+ ../src_3rd/genvector/gds_char.h mymem.h layer.h polygon.h rtree.h
+search.o: search.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ box.h misc_util.h data.h find.h polygon.h rtree.h search.h misc.h \
+ mymem.h layer.h
+select.o: select.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h draw.h search.h misc_util.h select.h undo.h rats.h misc.h mymem.h \
+ find.h layer.h compat_misc.h ../src_3rd/genregex/regex_sei.h \
+ ../src_3rd/genregex/regex_templ.h ../src_3rd/genregex/regex.h
+select_act.o: select_act.c ../config.h conf_core.h conf.h global.h \
+ const.h macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h undo.h funchash_core.h funchash.h \
+ funchash_core_list.h select.h crosshair.h set.h buffer.h draw.h remove.h \
+ copy.h hid_attrib.h compat_misc.h
+set.o: set.c ../config.h conf_core.h conf.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ action_helper.h buffer.h crosshair.h data.h draw.h find.h set.h undo.h \
+ hid_actions.h route_style.h
+strflags.o: strflags.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h strflags.h compat_misc.h
+stub_mincut.o: stub_mincut.c ../config.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h stub_mincut.h
+stub_stroke.o: stub_stroke.c error.h pcb_bool.h
+stub_vendor.o: stub_vendor.c stub_vendor.h ../config.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h
+thermal.o: thermal.c ../config.h misc.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h ../src_3rd/genvector/gds_char.h \
+ mymem.h polygon.h rtree.h thermal.h
+undo.o: undo.c ../config.h buffer.h global.h const.h macro.h \
+ global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h change.h create.h data.h draw.h \
+ insert.h layer.h misc.h ../src_3rd/genvector/gds_char.h mymem.h mirror.h \
+ move.h polygon.h rtree.h remove.h rotate.h search.h misc_util.h set.h \
+ undo.h strflags.h conf_core.h conf.h pcb-printf.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/dom.h \
+ ../src_3rd/liblihata/lihata.h ../src_3rd/liblihata/parser.h \
+ ../src_3rd/liblihata/genht/htsp.h ../src_3rd/liblihata/genht/ht.h \
+ ../src_3rd/genvector/vtp0.h list_conf.h compat_misc.h netlist.h
+undo_act.o: undo_act.c ../config.h conf_core.h conf.h global.h const.h \
+ macro.h global_typedefs.h pcb_bool.h unit.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h globalconst.h polyarea.h list_common.h \
+ list_line.h ../src_3rd/genlist/gentdlist_impl.h \
+ ../src_3rd/genlist/gendlist.h ../src_3rd/genlist/gentdlist_undef.h \
+ list_arc.h list_text.h list_poly.h list_pad.h list_pin.h list_rat.h \
+ vtonpoint.h ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h pcb-printf.h \
+ ../src_3rd/genvector/gds_char.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/dom.h ../src_3rd/liblihata/lihata.h \
+ ../src_3rd/liblihata/parser.h ../src_3rd/liblihata/genht/htsp.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/genvector/vtp0.h list_conf.h \
+ data.h action_helper.h funchash_core.h funchash.h funchash_core_list.h \
+ crosshair.h undo.h polygon.h rtree.h set.h search.h misc_util.h draw.h \
+ misc.h mymem.h layer.h
+unit.o: unit.c ../config.h global.h const.h macro.h global_typedefs.h \
+ pcb_bool.h unit.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ globalconst.h polyarea.h list_common.h list_line.h \
+ ../src_3rd/genlist/gentdlist_impl.h ../src_3rd/genlist/gendlist.h \
+ ../src_3rd/genlist/gentdlist_undef.h list_arc.h list_text.h list_poly.h \
+ list_pad.h list_pin.h list_rat.h vtonpoint.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h hid.h error.h vtroutestyle.h \
+ global_element.h list_element.h ht_element.h \
+ ../src_3rd/liblihata/genht/ht.h ../src_3rd/liblihata/genht/ht_inlines.h \
+ ../src_3rd/liblihata/genht/hash.h compat_misc.h
+vtlibrary.o: vtlibrary.c vtlibrary.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h ../config.h globalconst.h \
+ global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h \
+ ../src_3rd/genvector/genvector_impl.c
+vtonpoint.o: vtonpoint.c vtonpoint.h global_objs.h \
+ ../src_3rd/genlist/gendlist.h ../config.h globalconst.h \
+ global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h \
+ ../src_3rd/genvector/genvector_impl.c
+vtptr.o: vtptr.c vtptr.h global_objs.h ../src_3rd/genlist/gendlist.h \
+ ../config.h globalconst.h global_typedefs.h pcb_bool.h unit.h polyarea.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h \
+ ../src_3rd/genvector/genvector_impl.c
+vtroutestyle.o: vtroutestyle.c vtroutestyle.h unit.h ../config.h \
+ ../src_3rd/genvector/genvector_impl.h \
+ ../src_3rd/genvector/genvector_undef.h \
+ ../src_3rd/genvector/genvector_impl.c
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..800e417
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,333 @@
+# TODO default compiler flags
+put /local/pcb/CFLAGS cc/cflags
+append /local/pcb/CFLAGS cc/fpic
+append /local/pcb/CFLAGS {-I. -I.. -I../src_3rd -I../src_3rd/liblihata -Iicons -DHAVE_CONFIG_H}
+append /local/pcb/LDFLAGS cc/ldflags
+append /local/pcb/LDFLAGS cc/rdynamic
+put /local/pcb/DEPCFLAGS {}
+
+include {../scconfig/template/debug.tmpasm}
+
+# main: objects
+# These have constructs that are not c89 strictly speaking but are
+# effectively c89; typical example is casting a function pointer to a
+# data pointer for dlsym().
+put /local/pcb/OBJS_C99 [@
+	fptr_cast.o
+@]
+
+put /local/pcb/OBJS [@
+	action_helper.o
+	action_act.o
+	buffer.o
+	change.o
+	change_act.o
+	conf.o
+	conf_act.o
+	conf_core.o
+	conf_hid.o
+	conf_internal.o
+	clip.o
+	compat_dl.o
+	compat_fs.o
+	compat_lrealpath.o
+	compat_misc.o
+	copy.o
+	create.o
+	crosshair.o
+	data.o
+	draw.o
+	draw_fab.o
+	drill.o
+	error.o
+	event.o
+	file_act.o
+	find.o
+	find_act.o
+	free_atexit.o
+	funchash.o
+	gui_act.o
+	heap.o
+	hid_actions.o
+	hid_attrib.o
+	hid_cfg.o
+	hid_cfg_action.o
+	hid_cfg_input.o
+	hid_color.o
+	hid_draw_helpers.o
+	hid_extents.o
+	hid_flags.o
+	hid_helper.o
+	hid_init.o
+	hid_nogui.o
+	ht_element.o
+	insert.o
+	intersect.o
+	layer.o
+	line.o
+	list_arc.o
+	list_conf.o
+	list_element.o
+	list_line.o
+	list_text.o
+	list_pad.o
+	list_pin.o
+	list_poly.o
+	list_rat.o
+	main.o
+	main_act.o
+	mirror.o
+	misc.o
+	misc_util.o
+	move.o
+	mymem.o
+	netlist.o
+	netlist_act.o
+	object_act.o
+	obj_any.o
+	paths.o
+	pcb-printf.o
+	plugins.o
+	plug_import.o
+	plug_io.o
+	plug_footprint.o
+	plug_footprint_act.o
+	polygon.o
+	polygon1.o
+	polygon_act.o
+	rats.o
+	rats_act.o
+	rats_patch.o
+	remove.o
+	remove_act.o
+	rotate.o
+	route_style.o
+	rtree.o
+	rubberband.o
+	search.o
+	select.o
+	select_act.o
+	set.o
+	strflags.o
+	stub_mincut.o
+	stub_stroke.o
+	stub_vendor.o
+	thermal.o
+	undo.o
+	undo_act.o
+	unit.o
+	vtlibrary.o
+	vtonpoint.o
+	vtptr.o
+	vtroutestyle.o
+	buildin.o
+	../src_3rd/liblihata/parser.o
+	../src_3rd/liblihata/dom.o
+	../src_3rd/liblihata/dom_list.o
+	../src_3rd/liblihata/dom_hash.o
+	../src_3rd/liblihata/dom_table.o
+	../src_3rd/liblihata/lihata.o
+	../src_3rd/liblihata/hash_str.o
+	../src_3rd/liblihata/tree.o
+	../src_3rd/liblihata/tree_list.o
+	../src_3rd/liblihata/tree_hash.o
+	../src_3rd/liblihata/tree_table.o
+	../src_3rd/liblihata/tree_symlink.o
+	../src_3rd/liblihata/tree_path.o
+	../src_3rd/liblihata/genht/hash.o
+	../src_3rd/liblihata/genht/htsp.o
+	../src_3rd/liblihata/genht/htsi.o
+	../src_3rd/liblihata/genht/htpi.o
+	../src_3rd/liblihata/genht/htip.o
+	../src_3rd/liblihata/genht/htpp.o
+	../src_3rd/liblhtpers/lhtpers.o
+	../src_3rd/genvector/gds_char.o
+	../src_3rd/genvector/vtp0.o
+	../src_3rd/genregex/regex_sei.o
+	../src_3rd/genregex/regex_se.o
+	../src_3rd/genregex/regex.o
+@]
+
+# main: action registrations
+put /local/pcb/ACTION_REG_SRC  {
+	action_act.c buffer.c change_act.c conf_act.c file_act.c find_act.c
+	gui_act.c main_act.c misc.c move.c netlist_act.c
+	object_act.c plugins.c polygon_act.c plug_footprint_act.c rats_act.c
+	rats_patch.c remove_act.c select_act.c undo_act.c
+}
+
+#---- modules ----#
+# extra rules for modules
+put /local/pcb/RULES {}
+put /local/pcb/CLEANFILES {}
+put /local/pcb/CLEANRULES {}
+put /local/pcb/EXEDEPS {}
+put /local/pcb/all {}
+put /local/pcb/buildin_init {}
+put /local/pcb/rules/install_ {}
+put /local/pcb/rules/install {}
+put /local/pcb/rules/linstall {}
+put /local/pcb/rules/uninstall {}
+put /local/pcb/mod/OBJS {}
+put /local/pcb/mod/OBJS_C99 {}
+put /local/pcb/mod/CONF {}
+put /local/pcb/mod/LDFLAGS {}
+put /local/pcb/mod/CFLAGS {}
+put /local/pcb/mod/YACC {}
+put /local/pcb/mod/LEX {}
+
+put /local/pcb/tmpasm/buildin         {../src_plugins/Buildin.tmpasm}
+put /local/pcb/tmpasm/plugin          {../src_plugins/Plugin.tmpasm}
+put /local/pcb/tmpasm/disable         {../src_plugins/Disable.tmpasm}
+put /local/pcb/tmpasm/common_enabled  {../src_plugins/Common_enabled.tmpasm}
+
+include {../src_plugins/plugins_ALL.tmpasm}
+
+append /local/pcb/CFLAGS  /target/libs/sul/glib/cflags
+append /local/pcb/CFLAGS  /target/libs/sul/dmalloc/cflags
+append /local/pcb/LIBS    /target/libs/sul/glib/ldflags
+append /local/pcb/LIBS    /target/libs/sul/dmalloc/ldflags
+
+# ---- logic ----
+
+# Clean up variables
+put /tmpasm/OFS { }
+uniq /local/pcb/OBJS
+uniq /local/pcb/OBJS_C99
+uniq /local/pcb/CFLAGS
+uniq /local/pcb/LDFLAGS
+uniq /local/pcb/LIBS
+uniq /local/pcb/ACTION_REG_SRC
+put /local/pcb/SRCS /local/pcb/OBJS 
+append /local/pcb/SRCS ?/local/pcb/OBJS_C99
+gsub /local/pcb/SRCS {.o } {.c }
+
+# Makefile, static
+print [@
+# *** DO NOT EDIT THIS FILE ***
+# This file has been generated from Makefile.in by configure
+# *** DO NOT EDIT THIS FILE ***
+
+# plugin source
+PLUGDIR=../src_plugins
+
+# plugin source install - so that pcb-rnd runs from source properly
+PLUGIDIR=plugins
+
+# src_3rd dir for the lib_ wrapper plugins
+SRC_3RD_DIR=../src_3rd
+
+@/local/pcb/TOPVARS@
+OBJS=@/local/pcb/OBJS@
+OBJS_C99=@/local/pcb/OBJS_C99@
+SRCS=@/local/pcb/SRCS@
+CFLAGS=@/local/pcb/CFLAGS@
+C89FLAGS=@/local/pcb/c89flags@ @/local/pcb/CFLAGS@
+LDFLAGS=@/local/pcb/LDFLAGS@
+LIBS=@/local/pcb/LIBS@ -lm @?/target/libs/ldl@
+EXEDEPS=@/local/pcb/EXEDEPS@
+CLEANFILES=@/local/pcb/CLEANFILES@
+CLEANRULES=@/local/pcb/CLEANRULES@
+CC=@cc/cc@
+CQUOTE=../scconfig/cquote
+SPHASH_PATH=../src_3rd/sphash
+SPHASH=$(SPHASH_PATH)/sphash
+
+all:
+	make revcheck
+	make all_exe
+
+include ../Makefile.conf
+
+revcheck:
+	cd ../scconfig && ./revtest Rev.stamp < Rev.tab
+
+all_exe: pcb-rnd$(EXE) @/local/pcb/all@
+
+pcb-rnd$(EXE): $(OBJS) $(OBJS_C99) $(EXEDEPS)
+	$(CC) $(OBJS) $(OBJS_C99) -o pcb-rnd$(EXE) $(LDFLAGS) $(LIBS)
+
+
+$(SPHASH_PATH)/sphash: $(SPHASH_PATH)/sphash.c
+	$(CC) -o pcb-rnd$(EXE) $(SPHASH_PATH)/sphash.c -o $(SPHASH_PATH)/sphash
+@]
+
+print [@
+##### module rules begin #####
+
+@/local/pcb/RULES@
+
+##### module rules end #####
+
+pcblib_DATA= \
+	default_font \
+	default.pcb \
+	pcb-menu-lesstif.lht \
+	pcb-menu-gtk.lht \
+	pcb-menu-mkey.lht
+
+
+EXTRA_DIST= \
+	check_icon.data \
+	default_font \
+	default.pcb \
+	pcbtest.sh.in
+
+all-local: pcbtest.sh
+
+generated_lists.h: @/local/pcb/ACTION_REG_SRC@ Makefile
+	AWK=@/host/fstools/awk@ ../scconfig/gen_core_lists.sh @/local/pcb/ACTION_REG_SRC@ > generated_lists.h
+
+conf_core_fields.h: conf_core.h
+	AWK=@/host/fstools/awk@ ../scconfig/gen_conf.sh ../doc-rnd/conf/tree < conf_core.h > conf_core_fields.h
+
+conf_internal.c: pcb-conf.lht
+	$(CQUOTE) -n conf_internal < pcb-conf.lht > conf_internal.c
+
+FORCE:
+
+
+DISTCLEANFILES= core_lists.h
+
+clean: $(CLEANRULES)
+	-rm pcb-rnd $(OBJS) $(OBJS_C99) $(CLEANFILES)
+
+
+install_:
+	$(MKDIR) $(BINDIR) $(DATADIR) $(LIBDIR) $(LIBDIR)/plugins
+	$(CPC) "`pwd`/pcb-rnd" "$(BINDIR)/pcb-rnd"
+	$(CPC) "`pwd`/default.pcb" "$(DATADIR)/default.pcb"
+	$(CPC) "`pwd`/pcb-conf.lht" "$(DATADIR)/pcb-conf.lht"
+	$(CPC) "`pwd`/pcb-menu-lesstif.lht" "$(DATADIR)/pcb-menu-lesstif.lht"
+	$(CPC) "`pwd`/pcb-menu-gtk.lht" "$(DATADIR)/pcb-menu-gtk.lht"
+	$(CPC) "`pwd`/default_font" "$(DATADIR)/default_font"@/local/pcb/rules/install_@
+
+
+install:
+	make install_ CPC="$(CP)"@/local/pcb/rules/install@
+
+linstall:
+	make install_ CPC="$(LN)"@/local/pcb/rules/linstall@
+
+
+uninstall:
+	$(RM) $(BINDIR)/pcb-rnd
+	$(RM) $(DATADIR)/default.pcb
+	$(RM) $(DATADIR)/pcb-menu-lesstif.lht
+	$(RM) $(DATADIR)/pcb-conf.lht
+	$(RM) $(DATADIR)/pcb-menu-gtk.lht
+	$(RM) $(DATADIR)/default_font@/local/pcb/rules/uninstall@
+@]
+
+# generate explicit rules for .c -> .o
+put /local/comp/OBJS /local/pcb/OBJS
+put /local/comp/OBJS_C99 ?/local/pcb/OBJS_C99
+include {../scconfig/Makefile.comp.inc}
+
+# generate deps
+put /local/dep/CFLAGS /local/pcb/CFLAGS
+put /local/dep/SRCS /local/pcb/SRCS
+
+gsub /local/pcb/DEPSRCS {.o } {.c }
+append /local/dep/SRCS /local/pcb/DEPSRCS
+include {../scconfig/Makefile.dep.inc}
diff --git a/src/action_act.c b/src/action_act.c
new file mode 100644
index 0000000..08f021e
--- /dev/null
+++ b/src/action_act.c
@@ -0,0 +1,110 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+#include "config.h"
+
+#include "error.h"
+#include "action_helper.h"
+#include "hid_actions.h"
+#include "undo.h"
+
+/* actions about actions
+ */
+/* --------------------------------------------------------------------------- */
+
+static const char executefile_syntax[] = "ExecuteFile(filename)";
+
+static const char executefile_help[] = "Run actions from the given file.";
+
+/* %start-doc actions ExecuteFile
+
+Lines starting with @code{#} are ignored.
+
+%end-doc */
+
+int ActionExecuteFile(int argc, const char **argv, Coord x, Coord y)
+{
+	FILE *fp;
+	const char *fname;
+	char line[256];
+	int n = 0;
+	char *sp;
+
+	if (argc != 1)
+		AFAIL(executefile);
+
+	fname = argv[0];
+
+	if ((fp = fopen(fname, "r")) == NULL) {
+		fprintf(stderr, _("Could not open actions file \"%s\".\n"), fname);
+		return 1;
+	}
+
+	defer_updates = 1;
+	defer_needs_update = 0;
+	while (fgets(line, sizeof(line), fp) != NULL) {
+		n++;
+		sp = line;
+
+		/* eat the trailing newline */
+		while (*sp && *sp != '\r' && *sp != '\n')
+			sp++;
+		*sp = '\0';
+
+		/* eat leading spaces and tabs */
+		sp = line;
+		while (*sp && (*sp == ' ' || *sp == '\t'))
+			sp++;
+
+		/*
+		 * if we have anything left and its not a comment line
+		 * then execute it
+		 */
+
+		if (*sp && *sp != '#') {
+			/*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp); */
+			hid_parse_actions(sp);
+		}
+	}
+
+	defer_updates = 0;
+	if (defer_needs_update) {
+		IncrementUndoSerialNumber();
+		gui->invalidate_all();
+	}
+	fclose(fp);
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+HID_Action action_action_list[] = {
+	{"ExecuteFile", 0, ActionExecuteFile,
+	 executefile_help, executefile_syntax}
+};
+
+REGISTER_ACTIONS(action_action_list, NULL)
diff --git a/src/action_helper.c b/src/action_helper.c
new file mode 100644
index 0000000..bb754d9
--- /dev/null
+++ b/src/action_helper.c
@@ -0,0 +1,1242 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* action routines for output window
+ */
+
+#include "config.h"
+
+#include "conf_core.h"
+
+#include "action_helper.h"
+#include "buffer.h"
+#include "change.h"
+#include "copy.h"
+#include "create.h"
+#include "crosshair.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "insert.h"
+#include "line.h"
+#include "misc.h"
+#include "move.h"
+#include "polygon.h"
+#include "rats.h"
+#include "remove.h"
+#include "rotate.h"
+#include "rubberband.h"
+#include "search.h"
+#include "select.h"
+#include "set.h"
+#include "undo.h"
+#include "rtree.h"
+#include "stub_stroke.h"
+#include "funchash_core.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+/* --------------------------------------------------------------------------- */
+
+/* %start-doc actions 00delta
+
+Many actions take a @code{delta} parameter as the last parameter,
+which is an amount to change something.  That @code{delta} may include
+units, as an additional parameter, such as @code{Action(Object,5,mm)}.
+If no units are specified, the default is PCB's native units
+(currently 1/100 mil).  Also, if the delta is prefixed by @code{+} or
+ at code{-}, the size is increased or decreased by that amount.
+Otherwise, the size size is set to the given amount.
+
+ at example
+Action(Object,5,mil)
+Action(Object,+0.5,mm)
+Action(Object,-1)
+ at end example
+
+Actions which take a @code{delta} parameter which do not accept all
+these options will specify what they do take.
+
+%end-doc */
+
+/* %start-doc actions 00objects
+
+Many actions act on indicated objects on the board.  They will have
+parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
+what group of objects they act on.  Unless otherwise specified, these
+parameters are defined as follows:
+
+ at table @code
+
+ at item Object
+ at itemx ToggleObject
+Affects the object under the mouse pointer.  If this action is invoked
+from a menu or script, the user will be prompted to click on an
+object, which is then the object affected.
+
+ at item Selected
+ at itemx SelectedObjects
+
+Affects all objects which are currently selected.  At least, all
+selected objects for which the given action makes sense.
+
+ at item SelectedPins
+ at itemx SelectedVias
+ at itemx Selected at var{Type}
+ at itemx @i{etc}
+Affects all objects which are both selected and of the @var{Type} specified.
+
+ at end table
+
+%end-doc */
+
+/*  %start-doc actions 00macros
+
+ at macro pinshapes
+
+Pins, pads, and vias can have various shapes.  All may be round.  Pins
+and pads may be square (obviously "square" pads are usually
+rectangular).  Pins and vias may be octagonal.  When you change a
+shape flag of an element, you actually change all of its pins and
+pads.
+
+Note that the square flag takes precedence over the octagon flag,
+thus, if both the square and octagon flags are set, the object is
+square.  When the square flag is cleared, the pins and pads will be
+either round or, if the octagon flag is set, octagonal.
+
+ at end macro
+
+%end-doc */
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static PointType InsertedPoint;
+LayerTypePtr lastLayer;
+static struct {
+	PolygonTypePtr poly;
+	LineType line;
+} fake;
+
+action_note_t Note;
+int defer_updates = 0;
+int defer_needs_update = 0;
+
+
+static pcb_cardinal_t polyIndex = 0;
+pcb_bool saved_mode = pcb_false;
+
+/* ---------------------------------------------------------------------------
+ * some local routines
+ */
+static void AdjustAttachedBox(void);
+/* ---------------------------------------------------------------------------
+ * Clear warning color from pins/pads
+ */
+void ClearWarnings()
+{
+	conf_core.temp.rat_warn = pcb_false;
+	ALLPIN_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_WARN, pin)) {
+			CLEAR_FLAG(PCB_FLAG_WARN, pin);
+			DrawPin(pin);
+		}
+	}
+	ENDALL_LOOP;
+	VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_WARN, via)) {
+			CLEAR_FLAG(PCB_FLAG_WARN, via);
+			DrawVia(via);
+		}
+	}
+	END_LOOP;
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_WARN, pad)) {
+			CLEAR_FLAG(PCB_FLAG_WARN, pad);
+			DrawPad(pad);
+		}
+	}
+	ENDALL_LOOP;
+	ALLLINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_WARN, line)) {
+			CLEAR_FLAG(PCB_FLAG_WARN, line);
+			DrawLine(layer, line);
+		}
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_WARN, arc)) {
+			CLEAR_FLAG(PCB_FLAG_WARN, arc);
+			DrawArc(layer, arc);
+		}
+	}
+	ENDALL_LOOP;
+	ALLPOLYGON_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_WARN, polygon)) {
+			CLEAR_FLAG(PCB_FLAG_WARN, polygon);
+			DrawPolygon(layer, polygon);
+		}
+	}
+	ENDALL_LOOP;
+
+	Draw();
+}
+
+static void click_cb(hidval hv)
+{
+	if (Note.Click) {
+		notify_crosshair_change(pcb_false);
+		Note.Click = pcb_false;
+		if (Note.Moving && !gui->shift_is_pressed()) {
+			Note.Buffer = conf_core.editor.buffer_number;
+			SetBufferNumber(MAX_BUFFER - 1);
+			ClearBuffer(PASTEBUFFER);
+			AddSelectedToBuffer(PASTEBUFFER, Note.X, Note.Y, pcb_true);
+			SaveUndoSerialNumber();
+			RemoveSelected();
+			SaveMode();
+			saved_mode = pcb_true;
+			SetMode(PCB_MODE_PASTE_BUFFER);
+		}
+		else if (Note.Hit && !gui->shift_is_pressed()) {
+			BoxType box;
+
+			SaveMode();
+			saved_mode = pcb_true;
+			SetMode(gui->control_is_pressed()? PCB_MODE_COPY : PCB_MODE_MOVE);
+			Crosshair.AttachedObject.Ptr1 = Note.ptr1;
+			Crosshair.AttachedObject.Ptr2 = Note.ptr2;
+			Crosshair.AttachedObject.Ptr3 = Note.ptr3;
+			Crosshair.AttachedObject.Type = Note.Hit;
+
+			if (Crosshair.drags != NULL) {
+				free(Crosshair.drags);
+				Crosshair.drags = NULL;
+			}
+			Crosshair.dragx = Note.X;
+			Crosshair.dragy = Note.Y;
+			box.X1 = Note.X + SLOP * pixel_slop;
+			box.X2 = Note.X - SLOP * pixel_slop;
+			box.Y1 = Note.Y + SLOP * pixel_slop;
+			box.Y2 = Note.Y - SLOP * pixel_slop;
+			Crosshair.drags = ListBlock(&box, &Crosshair.drags_len);
+			Crosshair.drags_current = 0;
+			AttachForCopy(Note.X, Note.Y);
+		}
+		else {
+			BoxType box;
+
+			Note.Hit = 0;
+			Note.Moving = pcb_false;
+			SaveUndoSerialNumber();
+			box.X1 = -MAX_COORD;
+			box.Y1 = -MAX_COORD;
+			box.X2 = MAX_COORD;
+			box.Y2 = MAX_COORD;
+			/* unselect first if shift key not down */
+			if (!gui->shift_is_pressed() && SelectBlock(&box, pcb_false))
+				SetChangedFlag(pcb_true);
+			NotifyBlock();
+			Crosshair.AttachedBox.Point1.X = Note.X;
+			Crosshair.AttachedBox.Point1.Y = Note.Y;
+		}
+		notify_crosshair_change(pcb_true);
+	}
+}
+
+void ReleaseMode(void)
+{
+	BoxType box;
+
+	if (Note.Click) {
+		BoxType box;
+
+		box.X1 = -MAX_COORD;
+		box.Y1 = -MAX_COORD;
+		box.X2 = MAX_COORD;
+		box.Y2 = MAX_COORD;
+
+		Note.Click = pcb_false;					/* inhibit timer action */
+		SaveUndoSerialNumber();
+		/* unselect first if shift key not down */
+		if (!gui->shift_is_pressed()) {
+			if (SelectBlock(&box, pcb_false))
+				SetChangedFlag(pcb_true);
+			if (Note.Moving) {
+				Note.Moving = 0;
+				Note.Hit = 0;
+				return;
+			}
+		}
+		/* Restore the SN so that if we select something the deselect/select combo
+		   gets the same SN. */
+		RestoreUndoSerialNumber();
+		if (SelectObject())
+			SetChangedFlag(pcb_true);
+		else
+			IncrementUndoSerialNumber(); /* We didn't select anything new, so, the deselection should get its  own SN. */
+		Note.Hit = 0;
+		Note.Moving = 0;
+	}
+	else if (Note.Moving) {
+		RestoreUndoSerialNumber();
+		NotifyMode();
+		ClearBuffer(PASTEBUFFER);
+		SetBufferNumber(Note.Buffer);
+		Note.Moving = pcb_false;
+		Note.Hit = 0;
+	}
+	else if (Note.Hit) {
+		NotifyMode();
+		Note.Hit = 0;
+	}
+	else if (conf_core.editor.mode == PCB_MODE_ARROW) {
+		box.X1 = Crosshair.AttachedBox.Point1.X;
+		box.Y1 = Crosshair.AttachedBox.Point1.Y;
+		box.X2 = Crosshair.AttachedBox.Point2.X;
+		box.Y2 = Crosshair.AttachedBox.Point2.Y;
+
+		RestoreUndoSerialNumber();
+		if (SelectBlock(&box, pcb_true))
+			SetChangedFlag(pcb_true);
+		else if (Bumped)
+			IncrementUndoSerialNumber();
+		Crosshair.AttachedBox.State = STATE_FIRST;
+	}
+	if (saved_mode)
+		RestoreMode();
+	saved_mode = pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * set new coordinates if in 'RECTANGLE' mode
+ * the cursor shape is also adjusted
+ */
+static void AdjustAttachedBox(void)
+{
+	if (conf_core.editor.mode == PCB_MODE_ARC) {
+		Crosshair.AttachedBox.otherway = gui->shift_is_pressed();
+		return;
+	}
+	switch (Crosshair.AttachedBox.State) {
+	case STATE_SECOND:						/* one corner is selected */
+		{
+			/* update coordinates */
+			Crosshair.AttachedBox.Point2.X = Crosshair.X;
+			Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adjusts the objects which are to be created like attached lines...
+ */
+void AdjustAttachedObjects(void)
+{
+	PointTypePtr pnt;
+	switch (conf_core.editor.mode) {
+		/* update at least an attached block (selection) */
+	case PCB_MODE_NO:
+	case PCB_MODE_ARROW:
+		if (Crosshair.AttachedBox.State) {
+			Crosshair.AttachedBox.Point2.X = Crosshair.X;
+			Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
+		}
+		break;
+
+		/* rectangle creation mode */
+	case PCB_MODE_RECTANGLE:
+	case PCB_MODE_ARC:
+		AdjustAttachedBox();
+		break;
+
+		/* polygon creation mode */
+	case PCB_MODE_POLYGON:
+	case PCB_MODE_POLYGON_HOLE:
+		AdjustAttachedLine();
+		break;
+		/* line creation mode */
+	case PCB_MODE_LINE:
+		if (PCB->RatDraw || conf_core.editor.line_refraction == 0)
+			AdjustAttachedLine();
+		else
+			AdjustTwoLine(conf_core.editor.line_refraction - 1);
+		break;
+		/* point insertion mode */
+	case PCB_MODE_INSERT_POINT:
+		pnt = AdjustInsertPoint();
+		if (pnt)
+			InsertedPoint = *pnt;
+		break;
+	case PCB_MODE_ROTATE:
+		break;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * creates points of a line
+ */
+void NotifyLine(void)
+{
+	int type = PCB_TYPE_NONE;
+	void *ptr1, *ptr2, *ptr3;
+
+	if (!Marked.status || conf_core.editor.local_ref)
+		SetLocalRef(Crosshair.X, Crosshair.Y, pcb_true);
+	switch (Crosshair.AttachedLine.State) {
+	case STATE_FIRST:						/* first point */
+		if (PCB->RatDraw && SearchScreen(Crosshair.X, Crosshair.Y, PCB_TYPE_PAD | PCB_TYPE_PIN, &ptr1, &ptr1, &ptr1) == PCB_TYPE_NONE) {
+			gui->beep();
+			break;
+		}
+		if (conf_core.editor.auto_drc && conf_core.editor.mode == PCB_MODE_LINE) {
+			type = SearchScreen(Crosshair.X, Crosshair.Y, PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_VIA, &ptr1, &ptr2, &ptr3);
+			LookupConnection(Crosshair.X, Crosshair.Y, pcb_true, 1, PCB_FLAG_FOUND);
+		}
+		if (type == PCB_TYPE_PIN || type == PCB_TYPE_VIA) {
+			Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = ((PinTypePtr) ptr2)->X;
+			Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = ((PinTypePtr) ptr2)->Y;
+		}
+		else if (type == PCB_TYPE_PAD) {
+			PadTypePtr pad = (PadTypePtr) ptr2;
+			double d1 = Distance(Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
+			double d2 = Distance(Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
+			double dm = Distance(Crosshair.X, Crosshair.Y, (pad->Point1.X + pad->Point2.X) / 2, (pad->Point1.Y + pad->Point2.Y)/2);
+			if ((dm <= d1) && (dm <= d2)) { /* prefer to snap to the middle of a pin if that's the closest */
+				Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = Crosshair.X;
+				Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
+			}
+			else if (d2 < d1) { /* else select the closest endpoint */
+				Crosshair.AttachedLine.Point1 = Crosshair.AttachedLine.Point2 = pad->Point2;
+			}
+			else {
+				Crosshair.AttachedLine.Point1 = Crosshair.AttachedLine.Point2 = pad->Point1;
+			}
+		}
+		else {
+			Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = Crosshair.X;
+			Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
+		}
+		Crosshair.AttachedLine.State = STATE_SECOND;
+		break;
+
+	case STATE_SECOND:
+		/* fall through to third state too */
+		lastLayer = CURRENT;
+	default:											/* all following points */
+		Crosshair.AttachedLine.State = STATE_THIRD;
+		break;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * create first or second corner of a marked block
+ */
+void NotifyBlock(void)
+{
+	notify_crosshair_change(pcb_false);
+	switch (Crosshair.AttachedBox.State) {
+	case STATE_FIRST:						/* setup first point */
+		Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = Crosshair.X;
+		Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
+		Crosshair.AttachedBox.State = STATE_SECOND;
+		break;
+
+	case STATE_SECOND:						/* setup second point */
+		Crosshair.AttachedBox.State = STATE_THIRD;
+		break;
+	}
+	notify_crosshair_change(pcb_true);
+}
+
+
+/* ---------------------------------------------------------------------------
+ *
+ * does what's appropriate for the current mode setting. This normally
+ * means creation of an object at the current crosshair location.
+ *
+ * new created objects are added to the create undo list of course
+ */
+void NotifyMode(void)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	if (conf_core.temp.rat_warn)
+		ClearWarnings();
+	switch (conf_core.editor.mode) {
+	case PCB_MODE_ARROW:
+		{
+			int test;
+			hidval hv;
+
+			Note.Click = pcb_true;
+			/* do something after click time */
+			gui->add_timer(click_cb, conf_core.editor.click_time, hv);
+
+			/* see if we clicked on something already selected
+			 * (Note.Moving) or clicked on a MOVE_TYPE
+			 * (Note.Hit)
+			 */
+			for (test = (SELECT_TYPES | MOVE_TYPES) & ~PCB_TYPE_RATLINE; test; test &= ~type) {
+				type = SearchScreen(Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
+				if (!Note.Hit && (type & MOVE_TYPES) && !TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr) ptr2)) {
+					Note.Hit = type;
+					Note.ptr1 = ptr1;
+					Note.ptr2 = ptr2;
+					Note.ptr3 = ptr3;
+				}
+				if (!Note.Moving && (type & SELECT_TYPES) && TEST_FLAG(PCB_FLAG_SELECTED, (PinTypePtr) ptr2))
+					Note.Moving = pcb_true;
+				if ((Note.Hit && Note.Moving) || type == PCB_TYPE_NONE)
+					break;
+			}
+			break;
+		}
+
+	case PCB_MODE_VIA:
+		{
+			PinTypePtr via;
+
+			if (!PCB->ViaOn) {
+				Message(PCB_MSG_DEFAULT, _("You must turn via visibility on before\n" "you can place vias\n"));
+				break;
+			}
+			if ((via = CreateNewVia(PCB->Data, Note.X, Note.Y,
+															conf_core.design.via_thickness, 2 * conf_core.design.clearance,
+															0, conf_core.design.via_drilling_hole, NULL, NoFlags())) != NULL) {
+				AddObjectToCreateUndoList(PCB_TYPE_VIA, via, via, via);
+				if (gui->shift_is_pressed())
+					ChangeObjectThermal(PCB_TYPE_VIA, via, via, via, PCB->ThermStyle);
+				IncrementUndoSerialNumber();
+				DrawVia(via);
+				Draw();
+			}
+			break;
+		}
+
+	case PCB_MODE_ARC:
+		{
+			switch (Crosshair.AttachedBox.State) {
+			case STATE_FIRST:
+				Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = Note.X;
+				Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = Note.Y;
+				Crosshair.AttachedBox.State = STATE_SECOND;
+				break;
+
+			case STATE_SECOND:
+			case STATE_THIRD:
+				{
+					ArcTypePtr arc;
+					Coord wx, wy;
+					Angle sa, dir;
+
+					wx = Note.X - Crosshair.AttachedBox.Point1.X;
+					wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
+					if (XOR(Crosshair.AttachedBox.otherway, coord_abs(wy) > coord_abs(wx))) {
+						Crosshair.AttachedBox.Point2.X = Crosshair.AttachedBox.Point1.X + coord_abs(wy) * SGNZ(wx);
+						sa = (wx >= 0) ? 0 : 180;
+#ifdef ARC45
+						if (abs(wy) / 2 >= abs(wx))
+							dir = (SGNZ(wx) == SGNZ(wy)) ? 45 : -45;
+						else
+#endif
+							dir = (SGNZ(wx) == SGNZ(wy)) ? 90 : -90;
+					}
+					else {
+						Crosshair.AttachedBox.Point2.Y = Crosshair.AttachedBox.Point1.Y + coord_abs(wx) * SGNZ(wy);
+						sa = (wy >= 0) ? -90 : 90;
+#ifdef ARC45
+						if (abs(wx) / 2 >= abs(wy))
+							dir = (SGNZ(wx) == SGNZ(wy)) ? -45 : 45;
+						else
+#endif
+							dir = (SGNZ(wx) == SGNZ(wy)) ? -90 : 90;
+						wy = wx;
+					}
+					if (coord_abs(wy) > 0 && (arc = CreateNewArcOnLayer(CURRENT,
+																												Crosshair.AttachedBox.Point2.X,
+																												Crosshair.AttachedBox.Point2.Y,
+																												coord_abs(wy),
+																												coord_abs(wy),
+																												sa,
+																												dir,
+																												conf_core.design.line_thickness,
+																												2 * conf_core.design.clearance,
+																												MakeFlags(conf_core.editor.clear_line ? PCB_FLAG_CLEARLINE : 0)))) {
+						BoxTypePtr bx;
+
+						bx = GetArcEnds(arc);
+						Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = bx->X2;
+						Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = bx->Y2;
+						AddObjectToCreateUndoList(PCB_TYPE_ARC, CURRENT, arc, arc);
+						IncrementUndoSerialNumber();
+						addedLines++;
+						DrawArc(CURRENT, arc);
+						Draw();
+						Crosshair.AttachedBox.State = STATE_THIRD;
+					}
+					break;
+				}
+			}
+			break;
+		}
+	case PCB_MODE_LOCK:
+		{
+			type = SearchScreen(Note.X, Note.Y, PCB_TYPEMASK_LOCK, &ptr1, &ptr2, &ptr3);
+			if (type == PCB_TYPE_ELEMENT) {
+				ElementTypePtr element = (ElementTypePtr) ptr2;
+
+				TOGGLE_FLAG(PCB_FLAG_LOCK, element);
+				PIN_LOOP(element);
+				{
+					TOGGLE_FLAG(PCB_FLAG_LOCK, pin);
+					CLEAR_FLAG(PCB_FLAG_SELECTED, pin);
+				}
+				END_LOOP;
+				PAD_LOOP(element);
+				{
+					TOGGLE_FLAG(PCB_FLAG_LOCK, pad);
+					CLEAR_FLAG(PCB_FLAG_SELECTED, pad);
+				}
+				END_LOOP;
+				CLEAR_FLAG(PCB_FLAG_SELECTED, element);
+				/* always re-draw it since I'm too lazy
+				 * to tell if a selected flag changed
+				 */
+				DrawElement(element);
+				Draw();
+				hid_actionl("Report", "Object", NULL);
+			}
+			else if (type != PCB_TYPE_NONE) {
+				TextTypePtr thing = (TextTypePtr) ptr3;
+				TOGGLE_FLAG(PCB_FLAG_LOCK, thing);
+				if (TEST_FLAG(PCB_FLAG_LOCK, thing)
+						&& TEST_FLAG(PCB_FLAG_SELECTED, thing)) {
+					/* this is not un-doable since LOCK isn't */
+					CLEAR_FLAG(PCB_FLAG_SELECTED, thing);
+					DrawObject(type, ptr1, ptr2);
+					Draw();
+				}
+				hid_actionl("Report", "Object", NULL);
+			}
+			break;
+		}
+	case PCB_MODE_THERMAL:
+		{
+			if (((type = SearchScreen(Note.X, Note.Y, PCB_TYPEMASK_PIN, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					&& !TEST_FLAG(PCB_FLAG_HOLE, (PinTypePtr) ptr3)) {
+				if (gui->shift_is_pressed()) {
+					int tstyle = GET_THERM(INDEXOFCURRENT, (PinTypePtr) ptr3);
+					tstyle++;
+					if (tstyle > 5)
+						tstyle = 1;
+					ChangeObjectThermal(type, ptr1, ptr2, ptr3, tstyle);
+				}
+				else if (GET_THERM(INDEXOFCURRENT, (PinTypePtr) ptr3))
+					ChangeObjectThermal(type, ptr1, ptr2, ptr3, 0);
+				else
+					ChangeObjectThermal(type, ptr1, ptr2, ptr3, PCB->ThermStyle);
+			}
+			break;
+		}
+
+	case PCB_MODE_LINE:
+		/* do update of position */
+		NotifyLine();
+		if (Crosshair.AttachedLine.State != STATE_THIRD)
+			break;
+
+		/* Remove anchor if clicking on start point;
+		 * this means we can't paint 0 length lines
+		 * which could be used for square SMD pads.
+		 * Instead use a very small delta, or change
+		 * the file after saving.
+		 */
+		if (Crosshair.X == Crosshair.AttachedLine.Point1.X && Crosshair.Y == Crosshair.AttachedLine.Point1.Y) {
+			SetMode(PCB_MODE_LINE);
+			break;
+		}
+
+		if (PCB->RatDraw) {
+			RatTypePtr line;
+			if ((line = AddNet())) {
+				addedLines++;
+				AddObjectToCreateUndoList(PCB_TYPE_RATLINE, line, line, line);
+				IncrementUndoSerialNumber();
+				DrawRat(line);
+				Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
+				Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
+				Draw();
+			}
+			break;
+		}
+		else
+			/* create line if both ends are determined && length != 0 */
+		{
+			LineTypePtr line;
+			int maybe_found_flag;
+
+			if (conf_core.editor.line_refraction
+					&& Crosshair.AttachedLine.Point1.X ==
+					Crosshair.AttachedLine.Point2.X
+					&& Crosshair.AttachedLine.Point1.Y ==
+					Crosshair.AttachedLine.Point2.Y
+					&& (Crosshair.AttachedLine.Point2.X != Note.X || Crosshair.AttachedLine.Point2.Y != Note.Y)) {
+				/* We will only need to paint the second line segment.
+				   Since we only check for vias on the first segment,
+				   swap them so the non-empty segment is the first segment. */
+				Crosshair.AttachedLine.Point2.X = Note.X;
+				Crosshair.AttachedLine.Point2.Y = Note.Y;
+			}
+
+			if (conf_core.editor.auto_drc
+					&& !TEST_SILK_LAYER(CURRENT))
+				maybe_found_flag = PCB_FLAG_FOUND;
+			else
+				maybe_found_flag = 0;
+
+			if ((Crosshair.AttachedLine.Point1.X !=
+					 Crosshair.AttachedLine.Point2.X || Crosshair.AttachedLine.Point1.Y != Crosshair.AttachedLine.Point2.Y)
+					&& (line =
+							CreateDrawnLineOnLayer(CURRENT,
+																		 Crosshair.AttachedLine.Point1.X,
+																		 Crosshair.AttachedLine.Point1.Y,
+																		 Crosshair.AttachedLine.Point2.X,
+																		 Crosshair.AttachedLine.Point2.Y,
+																		 conf_core.design.line_thickness,
+																		 2 * conf_core.design.clearance,
+																		 MakeFlags(maybe_found_flag |
+																							 (conf_core.editor.clear_line ? PCB_FLAG_CLEARLINE : 0)))) != NULL) {
+				PinTypePtr via;
+
+				addedLines++;
+				AddObjectToCreateUndoList(PCB_TYPE_LINE, CURRENT, line, line);
+				DrawLine(CURRENT, line);
+				/* place a via if vias are visible, the layer is
+				   in a new group since the last line and there
+				   isn't a pin already here */
+				if (PCB->ViaOn && GetLayerGroupNumberByPointer(CURRENT) !=
+						GetLayerGroupNumberByPointer(lastLayer) &&
+						SearchObjectByLocation(PCB_TYPEMASK_PIN, &ptr1, &ptr2, &ptr3,
+																	 Crosshair.AttachedLine.Point1.X,
+																	 Crosshair.AttachedLine.Point1.Y,
+																	 conf_core.design.via_thickness / 2) ==
+						PCB_TYPE_NONE
+						&& (via =
+								CreateNewVia(PCB->Data,
+														 Crosshair.AttachedLine.Point1.X,
+														 Crosshair.AttachedLine.Point1.Y,
+														 conf_core.design.via_thickness,
+														 2 * conf_core.design.clearance, 0, conf_core.design.via_drilling_hole, NULL, NoFlags())) != NULL) {
+					AddObjectToCreateUndoList(PCB_TYPE_VIA, via, via, via);
+					DrawVia(via);
+				}
+				/* copy the coordinates */
+				Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
+				Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
+				IncrementUndoSerialNumber();
+				lastLayer = CURRENT;
+			}
+			if (conf_core.editor.line_refraction && (Note.X != Crosshair.AttachedLine.Point2.X || Note.Y != Crosshair.AttachedLine.Point2.Y)
+					&& (line =
+							CreateDrawnLineOnLayer(CURRENT,
+																		 Crosshair.AttachedLine.Point2.X,
+																		 Crosshair.AttachedLine.Point2.Y,
+																		 Note.X, Note.Y,
+																		 conf_core.design.line_thickness,
+																		 2 * conf_core.design.clearance,
+																		 MakeFlags((conf_core.editor.auto_drc ? PCB_FLAG_FOUND : 0) |
+																							 (conf_core.editor.clear_line ? PCB_FLAG_CLEARLINE : 0)))) != NULL) {
+				addedLines++;
+				AddObjectToCreateUndoList(PCB_TYPE_LINE, CURRENT, line, line);
+				IncrementUndoSerialNumber();
+				DrawLine(CURRENT, line);
+				/* move to new start point */
+				Crosshair.AttachedLine.Point1.X = Note.X;
+				Crosshair.AttachedLine.Point1.Y = Note.Y;
+				Crosshair.AttachedLine.Point2.X = Note.X;
+				Crosshair.AttachedLine.Point2.Y = Note.Y;
+
+
+				if (conf_core.editor.swap_start_direction) {
+					conf_setf(CFR_DESIGN,"editor/line_refraction", -1, "%d",conf_core.editor.line_refraction ^ 3);
+				}
+			}
+			if (conf_core.editor.orthogonal_moves) {
+				/* set the mark to the new starting point so ortho works as expected and we can draw a perpendicular line from here */
+				Marked.X = Note.X;
+				Marked.Y = Note.Y;
+			}
+			Draw();
+		}
+		break;
+
+	case PCB_MODE_RECTANGLE:
+		/* do update of position */
+		NotifyBlock();
+
+		/* create rectangle if both corners are determined
+		 * and width, height are != 0
+		 */
+		if (Crosshair.AttachedBox.State == STATE_THIRD &&
+				Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
+				Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y) {
+			PolygonTypePtr polygon;
+
+			int flags = PCB_FLAG_CLEARPOLY;
+			if (conf_core.editor.full_poly)
+				flags |= PCB_FLAG_FULLPOLY;
+			if ((polygon = CreateNewPolygonFromRectangle(CURRENT,
+																									 Crosshair.AttachedBox.Point1.X,
+																									 Crosshair.AttachedBox.Point1.Y,
+																									 Crosshair.AttachedBox.Point2.X,
+																									 Crosshair.AttachedBox.Point2.Y, MakeFlags(flags))) != NULL) {
+				AddObjectToCreateUndoList(PCB_TYPE_POLYGON, CURRENT, polygon, polygon);
+				IncrementUndoSerialNumber();
+				DrawPolygon(CURRENT, polygon);
+				Draw();
+			}
+
+			/* reset state to 'first corner' */
+			Crosshair.AttachedBox.State = STATE_FIRST;
+		}
+		break;
+
+	case PCB_MODE_TEXT:
+		{
+			char *string;
+
+			if ((string = gui->prompt_for(_("Enter text:"), "")) != NULL) {
+				if (strlen(string) > 0) {
+					TextTypePtr text;
+					int flag = PCB_FLAG_CLEARLINE;
+
+					if (GetLayerGroupNumberByNumber(INDEXOFCURRENT) == GetLayerGroupNumberByNumber(solder_silk_layer))
+						flag |= PCB_FLAG_ONSOLDER;
+					if ((text = CreateNewText(CURRENT, &PCB->Font, Note.X,
+																		Note.Y, 0, conf_core.design.text_scale, string, MakeFlags(flag))) != NULL) {
+						AddObjectToCreateUndoList(PCB_TYPE_TEXT, CURRENT, text, text);
+						IncrementUndoSerialNumber();
+						DrawText(CURRENT, text);
+						Draw();
+					}
+				}
+				free(string);
+			}
+			break;
+		}
+
+	case PCB_MODE_POLYGON:
+		{
+			PointTypePtr points = Crosshair.AttachedPolygon.Points;
+			pcb_cardinal_t n = Crosshair.AttachedPolygon.PointN;
+
+			/* do update of position; use the 'PCB_MODE_LINE' mechanism */
+			NotifyLine();
+
+			/* check if this is the last point of a polygon */
+			if (n >= 3 && points[0].X == Crosshair.AttachedLine.Point2.X && points[0].Y == Crosshair.AttachedLine.Point2.Y) {
+				hid_actionl("Polygon", "Close", NULL);
+				ClosePolygon();
+				break;
+			}
+
+			/* Someone clicking twice on the same point ('doubleclick'): close polygon */
+			if (n >= 3 && points[n - 1].X == Crosshair.AttachedLine.Point2.X && points[n - 1].Y == Crosshair.AttachedLine.Point2.Y) {
+				hid_actionl("Polygon", "Close", NULL);
+				break;
+			}
+
+			/* create new point if it's the first one or if it's
+			 * different to the last one
+			 */
+			if (!n || points[n - 1].X != Crosshair.AttachedLine.Point2.X || points[n - 1].Y != Crosshair.AttachedLine.Point2.Y) {
+				CreateNewPointInPolygon(&Crosshair.AttachedPolygon, Crosshair.AttachedLine.Point2.X, Crosshair.AttachedLine.Point2.Y);
+
+				/* copy the coordinates */
+				Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
+				Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
+			}
+
+			if (conf_core.editor.orthogonal_moves) {
+				/* set the mark to the new starting point so ortho works */
+				Marked.X = Note.X;
+				Marked.Y = Note.Y;
+			}
+
+			break;
+		}
+
+	case PCB_MODE_POLYGON_HOLE:
+		{
+			switch (Crosshair.AttachedObject.State) {
+				/* first notify, lookup object */
+			case STATE_FIRST:
+				Crosshair.AttachedObject.Type =
+					SearchScreen(Note.X, Note.Y, PCB_TYPE_POLYGON,
+											 &Crosshair.AttachedObject.Ptr1, &Crosshair.AttachedObject.Ptr2, &Crosshair.AttachedObject.Ptr3);
+
+				if (Crosshair.AttachedObject.Type == PCB_TYPE_NONE) {
+					Message(PCB_MSG_DEFAULT, "The first point of a polygon hole must be on a polygon.\n");
+					break; /* don't start doing anything if clicket out of polys */
+				}
+
+				if (TEST_FLAG(PCB_FLAG_LOCK, (PolygonTypePtr)
+											Crosshair.AttachedObject.Ptr2)) {
+					Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+					Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
+					break;
+				}
+				else
+					Crosshair.AttachedObject.State = STATE_SECOND;
+			/* fall thru: first click is also the first point of the poly hole */
+
+				/* second notify, insert new point into object */
+			case STATE_SECOND:
+				{
+					PointTypePtr points = Crosshair.AttachedPolygon.Points;
+					pcb_cardinal_t n = Crosshair.AttachedPolygon.PointN;
+					POLYAREA *original, *new_hole, *result;
+					FlagType Flags;
+
+					/* do update of position; use the 'PCB_MODE_LINE' mechanism */
+					NotifyLine();
+
+					if (conf_core.editor.orthogonal_moves) {
+						/* set the mark to the new starting point so ortho works */
+						Marked.X = Note.X;
+						Marked.Y = Note.Y;
+					}
+
+					/* check if this is the last point of a polygon */
+					if (n >= 3 && points[0].X == Crosshair.AttachedLine.Point2.X && points[0].Y == Crosshair.AttachedLine.Point2.Y) {
+						/* Create POLYAREAs from the original polygon
+						 * and the new hole polygon */
+						original = PolygonToPoly((PolygonType *) Crosshair.AttachedObject.Ptr2);
+						new_hole = PolygonToPoly(&Crosshair.AttachedPolygon);
+
+						/* Subtract the hole from the original polygon shape */
+						poly_Boolean_free(original, new_hole, &result, PBO_SUB);
+
+						/* Convert the resulting polygon(s) into a new set of nodes
+						 * and place them on the page. Delete the original polygon.
+						 */
+						SaveUndoSerialNumber();
+						Flags = ((PolygonType *) Crosshair.AttachedObject.Ptr2)->Flags;
+						PolyToPolygonsOnLayer(PCB->Data, (LayerType *) Crosshair.AttachedObject.Ptr1, result, Flags);
+						RemoveObject(PCB_TYPE_POLYGON,
+												 Crosshair.AttachedObject.Ptr1, Crosshair.AttachedObject.Ptr2, Crosshair.AttachedObject.Ptr3);
+						RestoreUndoSerialNumber();
+						IncrementUndoSerialNumber();
+						Draw();
+
+						/* reset state of attached line */
+						memset(&Crosshair.AttachedPolygon, 0, sizeof(PolygonType));
+						Crosshair.AttachedLine.State = STATE_FIRST;
+						Crosshair.AttachedObject.State = STATE_FIRST;
+						addedLines = 0;
+
+						break;
+					}
+
+					/* create new point if it's the first one or if it's
+					 * different to the last one
+					 */
+					if (!n || points[n - 1].X != Crosshair.AttachedLine.Point2.X || points[n - 1].Y != Crosshair.AttachedLine.Point2.Y) {
+						CreateNewPointInPolygon(&Crosshair.AttachedPolygon,
+																		Crosshair.AttachedLine.Point2.X, Crosshair.AttachedLine.Point2.Y);
+
+						/* copy the coordinates */
+						Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
+						Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
+					}
+					break;
+				}
+			}
+			break;
+		}
+
+	case PCB_MODE_PASTE_BUFFER:
+		{
+			TextType estr[MAX_ELEMENTNAMES];
+			ElementTypePtr e = 0;
+
+			if (gui->shift_is_pressed()) {
+				int type = SearchScreen(Note.X, Note.Y, PCB_TYPE_ELEMENT, &ptr1, &ptr2,
+																&ptr3);
+				if (type == PCB_TYPE_ELEMENT) {
+					e = (ElementTypePtr) ptr1;
+					if (e) {
+						int i;
+
+						memcpy(estr, e->Name, MAX_ELEMENTNAMES * sizeof(TextType));
+						for (i = 0; i < MAX_ELEMENTNAMES; ++i)
+							estr[i].TextString = estr[i].TextString ? pcb_strdup(estr[i].TextString) : NULL;
+						RemoveElement(e);
+					}
+				}
+			}
+			if (CopyPastebufferToLayout(Note.X, Note.Y))
+				SetChangedFlag(pcb_true);
+			if (e) {
+				int type = SearchScreen(Note.X, Note.Y, PCB_TYPE_ELEMENT, &ptr1, &ptr2,
+																&ptr3);
+				if (type == PCB_TYPE_ELEMENT && ptr1) {
+					int i, save_n;
+					e = (ElementTypePtr) ptr1;
+
+					save_n = NAME_INDEX();
+
+					for (i = 0; i < MAX_ELEMENTNAMES; i++) {
+						if (i == save_n)
+							EraseElementName(e);
+						r_delete_entry(PCB->Data->name_tree[i], (BoxType *) & (e->Name[i]));
+						memcpy(&(e->Name[i]), &(estr[i]), sizeof(TextType));
+						e->Name[i].Element = e;
+						SetTextBoundingBox(&PCB->Font, &(e->Name[i]));
+						r_insert_entry(PCB->Data->name_tree[i], (BoxType *) & (e->Name[i]), 0);
+						if (i == save_n)
+							DrawElementName(e);
+					}
+				}
+			}
+			break;
+		}
+
+	case PCB_MODE_REMOVE:
+		if ((type = SearchScreen(Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE) {
+			if (TEST_FLAG(PCB_FLAG_LOCK, (LineTypePtr) ptr2)) {
+				Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+				break;
+			}
+			if (type == PCB_TYPE_ELEMENT) {
+				RubberbandTypePtr ptr;
+				int i;
+
+				Crosshair.AttachedObject.RubberbandN = 0;
+				LookupRatLines(type, ptr1, ptr2, ptr3);
+				ptr = Crosshair.AttachedObject.Rubberband;
+				for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++) {
+					if (PCB->RatOn)
+						EraseRat((RatTypePtr) ptr->Line);
+					if (TEST_FLAG(PCB_FLAG_RUBBEREND, ptr->Line))
+						MoveObjectToRemoveUndoList(PCB_TYPE_RATLINE, ptr->Line, ptr->Line, ptr->Line);
+					else
+						TOGGLE_FLAG(PCB_FLAG_RUBBEREND, ptr->Line);	/* only remove line once */
+					ptr++;
+				}
+			}
+			RemoveObject(type, ptr1, ptr2, ptr3);
+			IncrementUndoSerialNumber();
+			SetChangedFlag(pcb_true);
+		}
+		break;
+
+	case PCB_MODE_ROTATE:
+		RotateScreenObject(Note.X, Note.Y, gui->shift_is_pressed()? (SWAP_IDENT ? 1 : 3)
+											 : (SWAP_IDENT ? 3 : 1));
+		break;
+
+		/* both are almost the same */
+	case PCB_MODE_COPY:
+	case PCB_MODE_MOVE:
+		switch (Crosshair.AttachedObject.State) {
+			/* first notify, lookup object */
+		case STATE_FIRST:
+			{
+				int types = (conf_core.editor.mode == PCB_MODE_COPY) ? COPY_TYPES : MOVE_TYPES;
+
+				Crosshair.AttachedObject.Type =
+					SearchScreen(Note.X, Note.Y, types,
+											 &Crosshair.AttachedObject.Ptr1, &Crosshair.AttachedObject.Ptr2, &Crosshair.AttachedObject.Ptr3);
+				if (Crosshair.AttachedObject.Type != PCB_TYPE_NONE) {
+					if (conf_core.editor.mode == PCB_MODE_MOVE && TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr)
+																											Crosshair.AttachedObject.Ptr2)) {
+						Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+						Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
+					}
+					else
+						AttachForCopy(Note.X, Note.Y);
+				}
+				break;
+			}
+
+			/* second notify, move or copy object */
+		case STATE_SECOND:
+			if (conf_core.editor.mode == PCB_MODE_COPY)
+				CopyObject(Crosshair.AttachedObject.Type,
+									 Crosshair.AttachedObject.Ptr1,
+									 Crosshair.AttachedObject.Ptr2,
+									 Crosshair.AttachedObject.Ptr3, Note.X - Crosshair.AttachedObject.X, Note.Y - Crosshair.AttachedObject.Y);
+			else {
+				MoveObjectAndRubberband(Crosshair.AttachedObject.Type,
+																Crosshair.AttachedObject.Ptr1,
+																Crosshair.AttachedObject.Ptr2,
+																Crosshair.AttachedObject.Ptr3,
+																Note.X - Crosshair.AttachedObject.X, Note.Y - Crosshair.AttachedObject.Y);
+				SetLocalRef(0, 0, pcb_false);
+			}
+			SetChangedFlag(pcb_true);
+
+			/* reset identifiers */
+			Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
+			Crosshair.AttachedObject.State = STATE_FIRST;
+			break;
+		}
+		break;
+
+		/* insert a point into a polygon/line/... */
+	case PCB_MODE_INSERT_POINT:
+		switch (Crosshair.AttachedObject.State) {
+			/* first notify, lookup object */
+		case STATE_FIRST:
+			Crosshair.AttachedObject.Type =
+				SearchScreen(Note.X, Note.Y, INSERT_TYPES,
+										 &Crosshair.AttachedObject.Ptr1, &Crosshair.AttachedObject.Ptr2, &Crosshair.AttachedObject.Ptr3);
+
+			if (Crosshair.AttachedObject.Type != PCB_TYPE_NONE) {
+				if (TEST_FLAG(PCB_FLAG_LOCK, (PolygonTypePtr)
+											Crosshair.AttachedObject.Ptr2)) {
+					Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+					Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
+					break;
+				}
+				else {
+					/* get starting point of nearest segment */
+					if (Crosshair.AttachedObject.Type == PCB_TYPE_POLYGON) {
+						fake.poly = (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
+						polyIndex = GetLowestDistancePolygonPoint(fake.poly, Note.X, Note.Y);
+						fake.line.Point1 = fake.poly->Points[polyIndex];
+						fake.line.Point2 = fake.poly->Points[prev_contour_point(fake.poly, polyIndex)];
+						Crosshair.AttachedObject.Ptr2 = &fake.line;
+
+					}
+					Crosshair.AttachedObject.State = STATE_SECOND;
+					InsertedPoint = *AdjustInsertPoint();
+				}
+			}
+			break;
+
+			/* second notify, insert new point into object */
+		case STATE_SECOND:
+			if (Crosshair.AttachedObject.Type == PCB_TYPE_POLYGON)
+				InsertPointIntoObject(PCB_TYPE_POLYGON,
+															Crosshair.AttachedObject.Ptr1, fake.poly,
+															&polyIndex, InsertedPoint.X, InsertedPoint.Y, pcb_false, pcb_false);
+			else
+				InsertPointIntoObject(Crosshair.AttachedObject.Type,
+															Crosshair.AttachedObject.Ptr1,
+															Crosshair.AttachedObject.Ptr2, &polyIndex, InsertedPoint.X, InsertedPoint.Y, pcb_false, pcb_false);
+			SetChangedFlag(pcb_true);
+
+			/* reset identifiers */
+			Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
+			Crosshair.AttachedObject.State = STATE_FIRST;
+			break;
+		}
+		break;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * !!! no action routine !!!
+ *
+ * event handler to set the cursor according to the X pointer position
+ * called from inside main.c
+ */
+void EventMoveCrosshair(int ev_x, int ev_y)
+{
+	if (mid_stroke)
+		stub_stroke_record(ev_x, ev_y);
+	if (MoveCrosshairAbsolute(ev_x, ev_y)) {
+		/* update object position and cursor location */
+		AdjustAttachedObjects();
+		notify_crosshair_change(pcb_true);
+	}
+}
+
+/* --------------------------------------------------------------------------- */
+/* helper: get route style size for a function and selected object type.
+   size_id: 0=main size; 1=2nd size (drill); 2=clearance */
+int get_style_size(int funcid, Coord * out, int type, int size_id)
+{
+	switch (funcid) {
+	case F_Object:
+		switch (type) {
+		case PCB_TYPE_ELEMENT:					/* we'd set pin/pad properties, so fall thru */
+		case PCB_TYPE_VIA:
+		case PCB_TYPE_PIN:
+			return get_style_size(F_SelectedVias, out, 0, size_id);
+		case PCB_TYPE_PAD:
+			return get_style_size(F_SelectedPads, out, 0, size_id);
+		case PCB_TYPE_LINE:
+			return get_style_size(F_SelectedLines, out, 0, size_id);
+		case PCB_TYPE_ARC:
+			return get_style_size(F_SelectedArcs, out, 0, size_id);
+		}
+		Message(PCB_MSG_DEFAULT, _("Sorry, can't fetch the style of that object type (%x)\n"), type);
+		return -1;
+	case F_SelectedPads:
+		if (size_id != 2)						/* don't mess with pad size */
+			return -1;
+	case F_SelectedVias:
+	case F_SelectedPins:
+	case F_SelectedObjects:
+	case F_Selected:
+	case F_SelectedElements:
+		if (size_id == 0)
+			*out = conf_core.design.via_thickness;
+		else if (size_id == 1)
+			*out = conf_core.design.via_drilling_hole;
+		else
+			*out = conf_core.design.clearance;
+		break;
+	case F_SelectedArcs:
+	case F_SelectedLines:
+		if (size_id == 2)
+			*out = conf_core.design.clearance;
+		else
+			*out = conf_core.design.line_thickness;
+		return 0;
+	case F_SelectedTexts:
+	case F_SelectedNames:
+		Message(PCB_MSG_DEFAULT, _("Sorry, can't change style of every selected object\n"));
+		return -1;
+	}
+	return 0;
+}
diff --git a/src/action_helper.h b/src/action_helper.h
new file mode 100644
index 0000000..f0c42d5
--- /dev/null
+++ b/src/action_helper.h
@@ -0,0 +1,76 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for action routines */
+
+#ifndef	PCB_ACTION_HELPER_H
+#define	PCB_ACTION_HELPER_H
+
+#include "global.h"
+
+#define CLONE_TYPES PCB_TYPE_LINE | PCB_TYPE_ARC | PCB_TYPE_VIA | PCB_TYPE_POLYGON
+
+void ActionAdjustStyle(char *);
+void EventMoveCrosshair(int, int);
+
+void AdjustAttachedObjects(void);
+
+void warpNoWhere(void);
+
+/* In gui-misc.c */
+pcb_bool ActionGetLocation(char *);
+void ActionGetXY(char *);
+
+#define ACTION_ARG(n) (argc > (n) ? argv[n] : NULL)
+
+int get_style_size(int funcid, Coord * out, int type, int size_id);
+
+extern int defer_updates;
+extern int defer_needs_update;
+extern LayerTypePtr lastLayer;
+
+void NotifyLine(void);
+void NotifyBlock(void);
+void NotifyMode(void);
+void ClearWarnings(void);
+
+typedef struct {
+	Coord X, Y;
+	pcb_cardinal_t Buffer;
+	pcb_bool Click;
+	pcb_bool Moving;									/* selected type clicked on */
+	int Hit;											/* move type clicked on */
+	void *ptr1;
+	void *ptr2;
+	void *ptr3;
+} action_note_t;
+
+extern action_note_t Note;
+extern pcb_bool saved_mode;
+
+void ReleaseMode(void);
+
+#endif
diff --git a/src/box.h b/src/box.h
new file mode 100644
index 0000000..30b315b
--- /dev/null
+++ b/src/box.h
@@ -0,0 +1,206 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, box.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* random box-related utilities. */
+
+#ifndef PCB_BOX_H
+#define PCB_BOX_H
+
+#include <assert.h>
+#include "global.h"
+
+#include "misc_util.h"
+
+typedef enum { NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3, NE = 4, SE = 5, SW = 6, NW = 7, ALL = 8
+} direction_t;
+
+/* rotates box 90-degrees cw */
+/* that's a strange rotation! */
+#define ROTATEBOX_CW(box) { Coord t;\
+    t = (box).X1; (box).X1 = -(box).Y2; (box).Y2 = (box).X2;\
+    (box).X2 = -(box).Y1; (box).Y1 = t;\
+}
+#define ROTATEBOX_TO_NORTH(box, dir) do { Coord t;\
+  switch(dir) {\
+  case EAST: \
+   t = (box).X1; (box).X1 = (box).Y1; (box).Y1 = -(box).X2;\
+   (box).X2 = (box).Y2; (box).Y2 = -t; break;\
+  case SOUTH: \
+   t = (box).X1; (box).X1 = -(box).X2; (box).X2 = -t;\
+   t = (box).Y1; (box).Y1 = -(box).Y2; (box).Y2 = -t; break;\
+  case WEST: \
+   t = (box).X1; (box).X1 = -(box).Y2; (box).Y2 = (box).X2;\
+   (box).X2 = -(box).Y1; (box).Y1 = t; break;\
+  case NORTH: break;\
+  default: assert(0);\
+  }\
+  } while (0)
+#define ROTATEBOX_FROM_NORTH(box, dir) do { Coord t;\
+  switch(dir) {\
+  case WEST: \
+   t = (box).X1; (box).X1 = (box).Y1; (box).Y1 = -(box).X2;\
+   (box).X2 = (box).Y2; (box).Y2 = -t; break;\
+  case SOUTH: \
+   t = (box).X1; (box).X1 = -(box).X2; (box).X2 = -t;\
+   t = (box).Y1; (box).Y1 = -(box).Y2; (box).Y2 = -t; break;\
+  case EAST: \
+   t = (box).X1; (box).X1 = -(box).Y2; (box).Y2 = (box).X2;\
+   (box).X2 = -(box).Y1; (box).Y1 = t; break;\
+  case NORTH: break;\
+  default: assert(0);\
+  }\
+  } while (0)
+
+/* to avoid overflow, we calculate centers this way */
+#define CENTER_X(b) ((b).X1 + ((b).X2 - (b).X1)/2)
+#define CENTER_Y(b) ((b).Y1 + ((b).Y2 - (b).Y1)/2)
+/* some useful box utilities. */
+
+typedef struct cheap_point {
+	Coord X, Y;
+} CheapPointType;
+
+
+/* note that boxes are closed on top and left and open on bottom and right. */
+/* this means that top-left corner is in box, *but bottom-right corner is
+ * not*.  */
+static inline PCB_FUNC_UNUSED pcb_bool point_in_box(const BoxType * box, Coord X, Coord Y) 
+{
+	return (X >= box->X1) && (Y >= box->Y1) && (X < box->X2) && (Y < box->Y2);
+}
+
+static inline PCB_FUNC_UNUSED pcb_bool point_in_closed_box(const BoxType * box, Coord X, Coord Y)
+{
+	return (X >= box->X1) && (Y >= box->Y1) && (X <= box->X2) && (Y <= box->Y2);
+}
+
+static inline PCB_FUNC_UNUSED pcb_bool box_is_good(const BoxType * b)
+{
+	return (b->X1 < b->X2) && (b->Y1 < b->Y2);
+}
+
+static inline PCB_FUNC_UNUSED pcb_bool box_intersect(const BoxType * a, const BoxType * b)
+{
+	return (a->X1 < b->X2) && (b->X1 < a->X2) && (a->Y1 < b->Y2) && (b->Y1 < a->Y2);
+}
+
+static inline PCB_FUNC_UNUSED CheapPointType closest_point_in_box(const CheapPointType * from, const BoxType * box)
+{
+	CheapPointType r;
+	assert(box->X1 < box->X2 && box->Y1 < box->Y2);
+	r.X = (from->X < box->X1) ? box->X1 : (from->X > box->X2 - 1) ? box->X2 - 1 : from->X;
+	r.Y = (from->Y < box->Y1) ? box->Y1 : (from->Y > box->Y2 - 1) ? box->Y2 - 1 : from->Y;
+	assert(point_in_box(box, r.X, r.Y));
+	return r;
+}
+
+static inline PCB_FUNC_UNUSED pcb_bool box_in_box(const BoxType * outer, const BoxType * inner)
+{
+	return (outer->X1 <= inner->X1) && (inner->X2 <= outer->X2) && (outer->Y1 <= inner->Y1) && (inner->Y2 <= outer->Y2);
+}
+
+static inline PCB_FUNC_UNUSED BoxType clip_box(const BoxType * box, const BoxType * clipbox)
+{
+	BoxType r;
+	assert(box_intersect(box, clipbox));
+	r.X1 = MAX(box->X1, clipbox->X1);
+	r.X2 = MIN(box->X2, clipbox->X2);
+	r.Y1 = MAX(box->Y1, clipbox->Y1);
+	r.Y2 = MIN(box->Y2, clipbox->Y2);
+	assert(box_in_box(clipbox, &r));
+	return r;
+}
+
+static inline PCB_FUNC_UNUSED BoxType shrink_box(const BoxType * box, Coord amount)
+{
+	BoxType r = *box;
+	r.X1 += amount;
+	r.Y1 += amount;
+	r.X2 -= amount;
+	r.Y2 -= amount;
+	return r;
+}
+
+static inline PCB_FUNC_UNUSED BoxType bloat_box(const BoxType * box, Coord amount)
+{
+	return shrink_box(box, -amount);
+}
+
+/* construct a minimum box that touches the input box at the center */
+static inline PCB_FUNC_UNUSED BoxType box_center(const BoxType * box)
+{
+	BoxType r;
+	r.X1 = box->X1 + (box->X2 - box->X1) / 2;
+	r.X2 = r.X1 + 1;
+	r.Y1 = box->Y1 + (box->Y2 - box->Y1) / 2;
+	r.Y2 = r.Y1 + 1;
+	return r;
+}
+
+/* construct a minimum box that touches the input box at the corner */
+static inline PCB_FUNC_UNUSED BoxType box_corner(const BoxType * box)
+{
+	BoxType r;
+	r.X1 = box->X1;
+	r.X2 = r.X1 + 1;
+	r.Y1 = box->Y1;
+	r.Y2 = r.Y1 + 1;
+	return r;
+}
+
+/* construct a box that holds a single point */
+static inline PCB_FUNC_UNUSED BoxType point_box(Coord X, Coord Y)
+{
+	BoxType r;
+	r.X1 = X;
+	r.X2 = X + 1;
+	r.Y1 = Y;
+	r.Y2 = Y + 1;
+	return r;
+}
+
+/* close a bounding box by pushing its upper right corner */
+static inline PCB_FUNC_UNUSED void close_box(BoxType * r)
+{
+	r->X2++;
+	r->Y2++;
+}
+
+/* return the square of the minimum distance from a point to some point
+ * inside a box.  The box is half-closed!  That is, the top-left corner
+ * is considered in the box, but the bottom-right corner is not. */
+static inline PCB_FUNC_UNUSED double dist2_to_box(const CheapPointType * p, const BoxType * b)
+{
+	CheapPointType r = closest_point_in_box(p, b);
+	return Distance(r.X, r.Y, p->X, p->Y);
+}
+
+#endif /* __BOX_H_INCLUDED__ */
diff --git a/src/buffer.c b/src/buffer.c
new file mode 100644
index 0000000..0d2d0a1
--- /dev/null
+++ b/src/buffer.c
@@ -0,0 +1,1449 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* functions used by paste- and move/copy buffer
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "action_helper.h"
+#include "buffer.h"
+#include "copy.h"
+#include "create.h"
+#include "crosshair.h"
+#include "data.h"
+#include "error.h"
+#include "plug_io.h"
+#include "mirror.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "polygon.h"
+#include "rotate.h"
+#include "remove.h"
+#include "rtree.h"
+#include "select.h"
+#include "set.h"
+#include "funchash_core.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *AddViaToBuffer(PinTypePtr);
+static void *AddLineToBuffer(LayerTypePtr, LineTypePtr);
+static void *AddArcToBuffer(LayerTypePtr, ArcTypePtr);
+static void *AddRatToBuffer(RatTypePtr);
+static void *AddTextToBuffer(LayerTypePtr, TextTypePtr);
+static void *AddPolygonToBuffer(LayerTypePtr, PolygonTypePtr);
+static void *AddElementToBuffer(ElementTypePtr);
+static void *MoveViaToBuffer(PinTypePtr);
+static void *MoveLineToBuffer(LayerTypePtr, LineTypePtr);
+static void *MoveArcToBuffer(LayerTypePtr, ArcTypePtr);
+static void *MoveRatToBuffer(RatTypePtr);
+static void *MoveTextToBuffer(LayerTypePtr, TextTypePtr);
+static void *MovePolygonToBuffer(LayerTypePtr, PolygonTypePtr);
+static void *MoveElementToBuffer(ElementTypePtr);
+static void SwapBuffer(BufferTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static DataTypePtr Dest, Source;
+
+static ObjectFunctionType AddBufferFunctions = {
+	AddLineToBuffer,
+	AddTextToBuffer,
+	AddPolygonToBuffer,
+	AddViaToBuffer,
+	AddElementToBuffer,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	AddArcToBuffer,
+	AddRatToBuffer
+}, MoveBufferFunctions = {
+MoveLineToBuffer,
+		MoveTextToBuffer,
+		MovePolygonToBuffer, MoveViaToBuffer, MoveElementToBuffer, NULL, NULL, NULL, NULL, NULL, MoveArcToBuffer, MoveRatToBuffer};
+
+static int ExtraFlag = 0;
+
+/* ---------------------------------------------------------------------------
+ * copies a via to paste buffer
+ */
+static void *AddViaToBuffer(PinTypePtr Via)
+{
+	return (CreateNewVia(Dest, Via->X, Via->Y, Via->Thickness, Via->Clearance,
+											 Via->Mask, Via->DrillingHole, Via->Name, MaskFlags(Via->Flags, PCB_FLAG_FOUND | ExtraFlag)));
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a rat-line to paste buffer
+ */
+static void *AddRatToBuffer(RatTypePtr Rat)
+{
+	return (CreateNewRat(Dest, Rat->Point1.X, Rat->Point1.Y,
+											 Rat->Point2.X, Rat->Point2.Y, Rat->group1,
+											 Rat->group2, Rat->Thickness, MaskFlags(Rat->Flags, PCB_FLAG_FOUND | ExtraFlag)));
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a line to buffer
+ */
+static void *AddLineToBuffer(LayerTypePtr Layer, LineTypePtr Line)
+{
+	LineTypePtr line;
+	LayerTypePtr layer = &Dest->Layer[GetLayerNumber(Source, Layer)];
+
+	line = CreateNewLineOnLayer(layer, Line->Point1.X, Line->Point1.Y,
+															Line->Point2.X, Line->Point2.Y,
+															Line->Thickness, Line->Clearance, MaskFlags(Line->Flags, PCB_FLAG_FOUND | ExtraFlag));
+	if (line && Line->Number)
+		line->Number = pcb_strdup(Line->Number);
+	return (line);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies an arc to buffer
+ */
+static void *AddArcToBuffer(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	LayerTypePtr layer = &Dest->Layer[GetLayerNumber(Source, Layer)];
+
+	return (CreateNewArcOnLayer(layer, Arc->X, Arc->Y,
+															Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta,
+															Arc->Thickness, Arc->Clearance, MaskFlags(Arc->Flags, PCB_FLAG_FOUND | ExtraFlag)));
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a text to buffer
+ */
+static void *AddTextToBuffer(LayerTypePtr Layer, TextTypePtr Text)
+{
+	LayerTypePtr layer = &Dest->Layer[GetLayerNumber(Source, Layer)];
+
+	return (CreateNewText(layer, &PCB->Font, Text->X, Text->Y,
+												Text->Direction, Text->Scale, Text->TextString, MaskFlags(Text->Flags, ExtraFlag)));
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a polygon to buffer
+ */
+static void *AddPolygonToBuffer(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	LayerTypePtr layer = &Dest->Layer[GetLayerNumber(Source, Layer)];
+	PolygonTypePtr polygon;
+
+	polygon = CreateNewPolygon(layer, Polygon->Flags);
+	CopyPolygonLowLevel(polygon, Polygon);
+
+	/* Update the polygon r-tree. Unlike similarly named functions for
+	 * other objects, CreateNewPolygon does not do this as it creates a
+	 * skeleton polygon object, which won't have correct bounds.
+	 */
+	if (!layer->polygon_tree)
+		layer->polygon_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(layer->polygon_tree, (BoxType *) polygon, 0);
+
+	CLEAR_FLAG(PCB_FLAG_FOUND | ExtraFlag, polygon);
+	return (polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a element to buffer
+ */
+static void *AddElementToBuffer(ElementTypePtr Element)
+{
+	ElementTypePtr element;
+
+	element = GetElementMemory(Dest);
+	CopyElementLowLevel(Dest, element, Element, pcb_false, 0, 0);
+	CLEAR_FLAG(ExtraFlag, element);
+	if (ExtraFlag) {
+		ELEMENTTEXT_LOOP(element);
+		{
+			CLEAR_FLAG(ExtraFlag, text);
+		}
+		END_LOOP;
+		PIN_LOOP(element);
+		{
+			CLEAR_FLAG(PCB_FLAG_FOUND | ExtraFlag, pin);
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			CLEAR_FLAG(PCB_FLAG_FOUND | ExtraFlag, pad);
+		}
+		END_LOOP;
+	}
+	return (element);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a via to paste buffer without allocating memory for the name
+ */
+static void *MoveViaToBuffer(PinType * via)
+{
+	RestoreToPolygon(Source, PCB_TYPE_VIA, via, via);
+
+	r_delete_entry(Source->via_tree, (BoxType *) via);
+	pinlist_remove(via);
+	pinlist_append(&Dest->Via, via);
+
+	CLEAR_FLAG(PCB_FLAG_WARN | PCB_FLAG_FOUND, via);
+
+	if (!Dest->via_tree)
+		Dest->via_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Dest->via_tree, (BoxType *) via, 0);
+	ClearFromPolygon(Dest, PCB_TYPE_VIA, via, via);
+	return via;
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a rat-line to paste buffer
+ */
+static void *MoveRatToBuffer(RatType * rat)
+{
+	r_delete_entry(Source->rat_tree, (BoxType *) rat);
+
+	ratlist_remove(rat);
+	ratlist_append(&Dest->Rat, rat);
+
+	CLEAR_FLAG(PCB_FLAG_FOUND, rat);
+
+	if (!Dest->rat_tree)
+		Dest->rat_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Dest->rat_tree, (BoxType *) rat, 0);
+	return rat;
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a line to buffer
+ */
+static void *MoveLineToBuffer(LayerType * layer, LineType * line)
+{
+	LayerTypePtr lay = &Dest->Layer[GetLayerNumber(Source, layer)];
+
+	RestoreToPolygon(Source, PCB_TYPE_LINE, layer, line);
+	r_delete_entry(layer->line_tree, (BoxType *) line);
+
+	linelist_remove(line);
+	linelist_append(&(lay->Line), line);
+
+	CLEAR_FLAG(PCB_FLAG_FOUND, line);
+
+	if (!lay->line_tree)
+		lay->line_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(lay->line_tree, (BoxType *) line, 0);
+	ClearFromPolygon(Dest, PCB_TYPE_LINE, lay, line);
+	return (line);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves an arc to buffer
+ */
+static void *MoveArcToBuffer(LayerType * layer, ArcType * arc)
+{
+	LayerType *lay = &Dest->Layer[GetLayerNumber(Source, layer)];
+
+	RestoreToPolygon(Source, PCB_TYPE_ARC, layer, arc);
+	r_delete_entry(layer->arc_tree, (BoxType *) arc);
+
+	arclist_remove(arc);
+	arclist_append(&lay->Arc, arc);
+
+	CLEAR_FLAG(PCB_FLAG_FOUND, arc);
+
+	if (!lay->arc_tree)
+		lay->arc_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(lay->arc_tree, (BoxType *) arc, 0);
+	ClearFromPolygon(Dest, PCB_TYPE_ARC, lay, arc);
+	return (arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a text to buffer without allocating memory for the name
+ */
+static void *MoveTextToBuffer(LayerType * layer, TextType * text)
+{
+	LayerType *lay = &Dest->Layer[GetLayerNumber(Source, layer)];
+
+	r_delete_entry(layer->text_tree, (BoxType *) text);
+	RestoreToPolygon(Source, PCB_TYPE_TEXT, layer, text);
+
+	textlist_remove(text);
+	textlist_append(&lay->Text, text);
+
+	if (!lay->text_tree)
+		lay->text_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(lay->text_tree, (BoxType *) text, 0);
+	ClearFromPolygon(Dest, PCB_TYPE_TEXT, lay, text);
+	return (text);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a polygon to buffer. Doesn't allocate memory for the points
+ */
+static void *MovePolygonToBuffer(LayerType * layer, PolygonType * polygon)
+{
+	LayerType *lay = &Dest->Layer[GetLayerNumber(Source, layer)];
+
+	r_delete_entry(layer->polygon_tree, (BoxType *) polygon);
+
+	polylist_remove(polygon);
+	polylist_append(&lay->Polygon, polygon);
+
+	CLEAR_FLAG(PCB_FLAG_FOUND, polygon);
+
+	if (!lay->polygon_tree)
+		lay->polygon_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(lay->polygon_tree, (BoxType *) polygon, 0);
+	return (polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a element to buffer without allocating memory for pins/names
+ */
+static void *MoveElementToBuffer(ElementType * element)
+{
+	/*
+	 * Delete the element from the source (remove it from trees,
+	 * restore to polygons)
+	 */
+	r_delete_element(Source, element);
+
+	elementlist_remove(element);
+	elementlist_append(&Dest->Element, element);
+
+	PIN_LOOP(element);
+	{
+		RestoreToPolygon(Source, PCB_TYPE_PIN, element, pin);
+		CLEAR_FLAG(PCB_FLAG_WARN | PCB_FLAG_FOUND, pin);
+	}
+	END_LOOP;
+	PAD_LOOP(element);
+	{
+		RestoreToPolygon(Source, PCB_TYPE_PAD, element, pad);
+		CLEAR_FLAG(PCB_FLAG_WARN | PCB_FLAG_FOUND, pad);
+	}
+	END_LOOP;
+	SetElementBoundingBox(Dest, element, &PCB->Font);
+	/*
+	 * Now clear the from the polygons in the destination
+	 */
+	PIN_LOOP(element);
+	{
+		ClearFromPolygon(Dest, PCB_TYPE_PIN, element, pin);
+	}
+	END_LOOP;
+	PAD_LOOP(element);
+	{
+		ClearFromPolygon(Dest, PCB_TYPE_PAD, element, pad);
+	}
+	END_LOOP;
+
+	return element;
+}
+
+/* ---------------------------------------------------------------------------
+ * calculates the bounding box of the buffer
+ */
+void SetBufferBoundingBox(BufferTypePtr Buffer)
+{
+	BoxTypePtr box = GetDataBoundingBox(Buffer->Data);
+
+	if (box)
+		Buffer->BoundingBox = *box;
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the contents of the paste buffer
+ */
+void ClearBuffer(BufferTypePtr Buffer)
+{
+	if (Buffer && Buffer->Data) {
+		FreeDataMemory(Buffer->Data);
+		Buffer->Data->pcb = PCB;
+	}
+}
+
+/* ----------------------------------------------------------------------
+ * copies all selected and visible objects to the paste buffer
+ * returns true if any objects have been removed
+ */
+void AddSelectedToBuffer(BufferTypePtr Buffer, Coord X, Coord Y, pcb_bool LeaveSelected)
+{
+	/* switch crosshair off because adding objects to the pastebuffer
+	 * may change the 'valid' area for the cursor
+	 */
+	if (!LeaveSelected)
+		ExtraFlag = PCB_FLAG_SELECTED;
+	notify_crosshair_change(pcb_false);
+	Source = PCB->Data;
+	Dest = Buffer->Data;
+	SelectedOperation(&AddBufferFunctions, pcb_false, PCB_TYPEMASK_ALL);
+
+	/* set origin to passed or current position */
+	if (X || Y) {
+		Buffer->X = X;
+		Buffer->Y = Y;
+	}
+	else {
+		Buffer->X = Crosshair.X;
+		Buffer->Y = Crosshair.Y;
+	}
+	notify_crosshair_change(pcb_true);
+	ExtraFlag = 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * loads element data from file/library into buffer
+ * parse the file with disabled 'PCB mode' (see parser)
+ * returns pcb_false on error
+ * if successful, update some other stuff and reposition the pastebuffer
+ */
+pcb_bool LoadElementToBuffer(BufferTypePtr Buffer, const char *Name)
+{
+	ElementTypePtr element;
+
+	ClearBuffer(Buffer);
+	if (!ParseElement(Buffer->Data, Name)) {
+		if (conf_core.editor.show_solder_side)
+			SwapBuffer(Buffer);
+		SetBufferBoundingBox(Buffer);
+		if (elementlist_length(&Buffer->Data->Element)) {
+			element = elementlist_first(&Buffer->Data->Element);
+			Buffer->X = element->MarkX;
+			Buffer->Y = element->MarkY;
+		}
+		else {
+			Buffer->X = 0;
+			Buffer->Y = 0;
+		}
+		return (pcb_true);
+	}
+
+	/* release memory which might have been acquired */
+	ClearBuffer(Buffer);
+	return (pcb_false);
+}
+
+
+/*---------------------------------------------------------------------------
+ * Searches for the given element by "footprint" name, and loads it
+ * into the buffer.
+ */
+
+/* Returns zero on success, non-zero on error.  */
+int LoadFootprintByName(BufferTypePtr Buffer, const char *Footprint)
+{
+	return !LoadElementToBuffer(Buffer, Footprint);
+}
+
+
+static const char loadfootprint_syntax[] = "LoadFootprint(filename[,refdes,value])";
+
+static const char loadfootprint_help[] = "Loads a single footprint by name.";
+
+/* %start-doc actions LoadFootprint
+
+Loads a single footprint by name, rather than by reference or through
+the library.  If a refdes and value are specified, those are inserted
+into the footprint as well.  The footprint remains in the paste buffer.
+
+%end-doc */
+
+int LoadFootprint(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *name = ACTION_ARG(0);
+	const char *refdes = ACTION_ARG(1);
+	const char *value = ACTION_ARG(2);
+	ElementTypePtr e;
+
+	if (!name)
+		AFAIL(loadfootprint);
+
+	if (LoadFootprintByName(PASTEBUFFER, name))
+		return 1;
+
+	if (elementlist_length(&PASTEBUFFER->Data->Element) == 0) {
+		Message(PCB_MSG_DEFAULT, "Footprint %s contains no elements", name);
+		return 1;
+	}
+	if (elementlist_length(&PASTEBUFFER->Data->Element) > 1) {
+		Message(PCB_MSG_DEFAULT, "Footprint %s contains multiple elements", name);
+		return 1;
+	}
+
+	e = elementlist_first(&PASTEBUFFER->Data->Element);
+
+	if (e->Name[0].TextString)
+		free(e->Name[0].TextString);
+	e->Name[0].TextString = pcb_strdup(name);
+
+	if (e->Name[1].TextString)
+		free(e->Name[1].TextString);
+	e->Name[1].TextString = refdes ? pcb_strdup(refdes) : 0;
+
+	if (e->Name[2].TextString)
+		free(e->Name[2].TextString);
+	e->Name[2].TextString = value ? pcb_strdup(value) : 0;
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+ *
+ * break buffer element into pieces
+ */
+pcb_bool SmashBufferElement(BufferTypePtr Buffer)
+{
+	ElementTypePtr element;
+	pcb_cardinal_t group;
+	LayerTypePtr clayer, slayer;
+
+	if (elementlist_length(&Buffer->Data->Element) != 1) {
+		Message(PCB_MSG_DEFAULT, _("Error!  Buffer doesn't contain a single element\n"));
+		return (pcb_false);
+	}
+	/*
+	 * At this point the buffer should contain just a single element.
+	 * Now we detach the single element from the buffer and then clear the
+	 * buffer, ready to receive the smashed elements.  As a result of detaching
+	 * it the single element is orphaned from the buffer and thus will not be
+	 * free()'d by FreeDataMemory (called via ClearBuffer).  This leaves it
+	 * around for us to smash bits off it.  It then becomes our responsibility,
+	 * however, to free the single element when we're finished with it.
+	 */
+	element = elementlist_first(&Buffer->Data->Element);
+	elementlist_remove(element);
+	ClearBuffer(Buffer);
+	ELEMENTLINE_LOOP(element);
+	{
+		CreateNewLineOnLayer(&Buffer->Data->SILKLAYER,
+												 line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, line->Thickness, 0, NoFlags());
+		if (line)
+			line->Number = pcb_strdup_null(NAMEONPCB_NAME(element));
+	}
+	END_LOOP;
+	ARC_LOOP(element);
+	{
+		CreateNewArcOnLayer(&Buffer->Data->SILKLAYER,
+												arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta, arc->Thickness, 0, NoFlags());
+	}
+	END_LOOP;
+	PIN_LOOP(element);
+	{
+		FlagType f = NoFlags();
+		AddFlags(f, PCB_FLAG_VIA);
+		if (TEST_FLAG(PCB_FLAG_HOLE, pin))
+			AddFlags(f, PCB_FLAG_HOLE);
+
+		CreateNewVia(Buffer->Data, pin->X, pin->Y, pin->Thickness, pin->Clearance, pin->Mask, pin->DrillingHole, pin->Number, f);
+	}
+	END_LOOP;
+	group = GetLayerGroupNumberByNumber(SWAP_IDENT ? solder_silk_layer : component_silk_layer);
+	clayer = &Buffer->Data->Layer[PCB->LayerGroups.Entries[group][0]];
+	group = GetLayerGroupNumberByNumber(SWAP_IDENT ? component_silk_layer : solder_silk_layer);
+	slayer = &Buffer->Data->Layer[PCB->LayerGroups.Entries[group][0]];
+	PAD_LOOP(element);
+	{
+		LineTypePtr line;
+		line = CreateNewLineOnLayer(TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? slayer : clayer,
+																pad->Point1.X, pad->Point1.Y,
+																pad->Point2.X, pad->Point2.Y, pad->Thickness, pad->Clearance, NoFlags());
+		if (line)
+			line->Number = pcb_strdup_null(pad->Number);
+	}
+	END_LOOP;
+	FreeElementMemory(element);
+	RemoveFreeElement(element);
+	return (pcb_true);
+}
+
+/*---------------------------------------------------------------------------
+ *
+ * see if a polygon is a rectangle.  If so, canonicalize it.
+ */
+
+static int polygon_is_rectangle(PolygonTypePtr poly)
+{
+	int i, best;
+	PointType temp[4];
+	if (poly->PointN != 4 || poly->HoleIndexN != 0)
+		return 0;
+	best = 0;
+	for (i = 1; i < 4; i++)
+		if (poly->Points[i].X < poly->Points[best].X || poly->Points[i].Y < poly->Points[best].Y)
+			best = i;
+	for (i = 0; i < 4; i++)
+		temp[i] = poly->Points[(i + best) % 4];
+	if (temp[0].X == temp[1].X)
+		memcpy(poly->Points, temp, sizeof(temp));
+	else {
+		/* reverse them */
+		poly->Points[0] = temp[0];
+		poly->Points[1] = temp[3];
+		poly->Points[2] = temp[2];
+		poly->Points[3] = temp[1];
+	}
+	if (poly->Points[0].X == poly->Points[1].X
+			&& poly->Points[1].Y == poly->Points[2].Y
+			&& poly->Points[2].X == poly->Points[3].X && poly->Points[3].Y == poly->Points[0].Y)
+		return 1;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+ *
+ * convert buffer contents into an element
+ */
+pcb_bool ConvertBufferToElement(BufferTypePtr Buffer)
+{
+	ElementTypePtr Element;
+	pcb_cardinal_t group;
+	pcb_cardinal_t pin_n = 1;
+	pcb_bool hasParts = pcb_false, crooked = pcb_false;
+	int onsolder;
+	pcb_bool warned = pcb_false;
+
+	if (Buffer->Data->pcb == 0)
+		Buffer->Data->pcb = PCB;
+
+	Element = CreateNewElement(PCB->Data, NULL, &PCB->Font, NoFlags(),
+														 NULL, NULL, NULL, PASTEBUFFER->X,
+														 PASTEBUFFER->Y, 0, 100, MakeFlags(SWAP_IDENT ? PCB_FLAG_ONSOLDER : PCB_FLAG_NO), pcb_false);
+	if (!Element)
+		return (pcb_false);
+	VIA_LOOP(Buffer->Data);
+	{
+		char num[8];
+		if (via->Mask < via->Thickness)
+			via->Mask = via->Thickness + 2 * MASKFRAME;
+		if (via->Name)
+			CreateNewPin(Element, via->X, via->Y, via->Thickness,
+									 via->Clearance, via->Mask, via->DrillingHole,
+									 NULL, via->Name, MaskFlags(via->Flags, PCB_FLAG_VIA | PCB_FLAG_FOUND | PCB_FLAG_SELECTED | PCB_FLAG_WARN));
+		else {
+			sprintf(num, "%d", pin_n++);
+			CreateNewPin(Element, via->X, via->Y, via->Thickness,
+									 via->Clearance, via->Mask, via->DrillingHole,
+									 NULL, num, MaskFlags(via->Flags, PCB_FLAG_VIA | PCB_FLAG_FOUND | PCB_FLAG_SELECTED | PCB_FLAG_WARN));
+		}
+		hasParts = pcb_true;
+	}
+	END_LOOP;
+
+	for (onsolder = 0; onsolder < 2; onsolder++) {
+		int silk_layer;
+		int onsolderflag;
+
+		if ((!onsolder) == (!SWAP_IDENT)) {
+			silk_layer = component_silk_layer;
+			onsolderflag = PCB_FLAG_NO;
+		}
+		else {
+			silk_layer = solder_silk_layer;
+			onsolderflag = PCB_FLAG_ONSOLDER;
+		}
+
+#define MAYBE_WARN() \
+	  if (onsolder && !hasParts && !warned) \
+	    { \
+	      warned = pcb_true; \
+	      Message \
+					(PCB_MSG_WARNING, _("Warning: All of the pads are on the opposite\n" \
+		   "side from the component - that's probably not what\n" \
+		   "you wanted\n")); \
+	    } \
+
+		/* get the component-side SM pads */
+		group = GetLayerGroupNumberByNumber(silk_layer);
+		GROUP_LOOP(Buffer->Data, group);
+		{
+			char num[8];
+			LINE_LOOP(layer);
+			{
+				sprintf(num, "%d", pin_n++);
+				CreateNewPad(Element, line->Point1.X,
+										 line->Point1.Y, line->Point2.X,
+										 line->Point2.Y, line->Thickness,
+										 line->Clearance,
+										 line->Thickness + line->Clearance, NULL, line->Number ? line->Number : num, MakeFlags(onsolderflag));
+				MAYBE_WARN();
+				hasParts = pcb_true;
+			}
+			END_LOOP;
+			POLYGON_LOOP(layer);
+			{
+				Coord x1, y1, x2, y2, w, h, t;
+
+				if (!polygon_is_rectangle(polygon)) {
+					crooked = pcb_true;
+					continue;
+				}
+
+				w = polygon->Points[2].X - polygon->Points[0].X;
+				h = polygon->Points[1].Y - polygon->Points[0].Y;
+				t = (w < h) ? w : h;
+				x1 = polygon->Points[0].X + t / 2;
+				y1 = polygon->Points[0].Y + t / 2;
+				x2 = x1 + (w - t);
+				y2 = y1 + (h - t);
+
+				sprintf(num, "%d", pin_n++);
+				CreateNewPad(Element,
+										 x1, y1, x2, y2, t,
+										 2 * conf_core.design.clearance, t + conf_core.design.clearance, NULL, num, MakeFlags(PCB_FLAG_SQUARE | onsolderflag));
+				MAYBE_WARN();
+				hasParts = pcb_true;
+			}
+			END_LOOP;
+		}
+		END_LOOP;
+	}
+
+	/* now add the silkscreen. NOTE: elements must have pads or pins too */
+	LINE_LOOP(&Buffer->Data->SILKLAYER);
+	{
+		if (line->Number && !NAMEONPCB_NAME(Element))
+			NAMEONPCB_NAME(Element) = pcb_strdup(line->Number);
+		CreateNewLineInElement(Element, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, line->Thickness);
+		hasParts = pcb_true;
+	}
+	END_LOOP;
+	ARC_LOOP(&Buffer->Data->SILKLAYER);
+	{
+		CreateNewArcInElement(Element, arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta, arc->Thickness);
+		hasParts = pcb_true;
+	}
+	END_LOOP;
+	if (!hasParts) {
+		DestroyObject(PCB->Data, PCB_TYPE_ELEMENT, Element, Element, Element);
+		Message(PCB_MSG_DEFAULT, _("There was nothing to convert!\n" "Elements must have some silk, pads or pins.\n"));
+		return (pcb_false);
+	}
+	if (crooked)
+		Message(PCB_MSG_DEFAULT, _("There were polygons that can't be made into pins!\n" "So they were not included in the element\n"));
+	Element->MarkX = Buffer->X;
+	Element->MarkY = Buffer->Y;
+	if (SWAP_IDENT)
+		SET_FLAG(PCB_FLAG_ONSOLDER, Element);
+	SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+	ClearBuffer(Buffer);
+	MoveObjectToBuffer(Buffer->Data, PCB->Data, PCB_TYPE_ELEMENT, Element, Element, Element);
+	SetBufferBoundingBox(Buffer);
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * load PCB into buffer
+ * parse the file with enabled 'PCB mode' (see parser)
+ * if successful, update some other stuff
+ */
+pcb_bool LoadLayoutToBuffer(BufferTypePtr Buffer, const char *Filename, const char *fmt)
+{
+	PCBTypePtr newPCB = CreateNewPCB();
+
+	/* new data isn't added to the undo list */
+	if (!ParsePCB(newPCB, Filename, fmt, CFR_invalid)) {
+		/* clear data area and replace pointer */
+		ClearBuffer(Buffer);
+		free(Buffer->Data);
+		Buffer->Data = newPCB->Data;
+		newPCB->Data = NULL;
+		Buffer->X = newPCB->CursorX;
+		Buffer->Y = newPCB->CursorY;
+		RemovePCB(newPCB);
+		Buffer->Data->pcb = PCB;
+		return (pcb_true);
+	}
+
+	/* release unused memory */
+	RemovePCB(newPCB);
+	Buffer->Data->pcb = PCB;
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates the contents of the pastebuffer
+ */
+void RotateBuffer(BufferTypePtr Buffer, pcb_uint8_t Number)
+{
+	/* rotate vias */
+	VIA_LOOP(Buffer->Data);
+	{
+		r_delete_entry(Buffer->Data->via_tree, (BoxType *) via);
+		ROTATE_VIA_LOWLEVEL(via, Buffer->X, Buffer->Y, Number);
+		SetPinBoundingBox(via);
+		r_insert_entry(Buffer->Data->via_tree, (BoxType *) via, 0);
+	}
+	END_LOOP;
+
+	/* elements */
+	ELEMENT_LOOP(Buffer->Data);
+	{
+		RotateElementLowLevel(Buffer->Data, element, Buffer->X, Buffer->Y, Number);
+	}
+	END_LOOP;
+
+	/* all layer related objects */
+	ALLLINE_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->line_tree, (BoxType *) line);
+		RotateLineLowLevel(line, Buffer->X, Buffer->Y, Number);
+		r_insert_entry(layer->line_tree, (BoxType *) line, 0);
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->arc_tree, (BoxType *) arc);
+		RotateArcLowLevel(arc, Buffer->X, Buffer->Y, Number);
+		r_insert_entry(layer->arc_tree, (BoxType *) arc, 0);
+	}
+	ENDALL_LOOP;
+	ALLTEXT_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->text_tree, (BoxType *) text);
+		RotateTextLowLevel(text, Buffer->X, Buffer->Y, Number);
+		r_insert_entry(layer->text_tree, (BoxType *) text, 0);
+	}
+	ENDALL_LOOP;
+	ALLPOLYGON_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->polygon_tree, (BoxType *) polygon);
+		RotatePolygonLowLevel(polygon, Buffer->X, Buffer->Y, Number);
+		r_insert_entry(layer->polygon_tree, (BoxType *) polygon, 0);
+	}
+	ENDALL_LOOP;
+
+	/* finally the origin and the bounding box */
+	ROTATE(Buffer->X, Buffer->Y, Buffer->X, Buffer->Y, Number);
+	RotateBoxLowLevel(&Buffer->BoundingBox, Buffer->X, Buffer->Y, Number);
+}
+
+static void free_rotate(Coord * x, Coord * y, Coord cx, Coord cy, double cosa, double sina)
+{
+	double nx, ny;
+	Coord px = *x - cx;
+	Coord py = *y - cy;
+
+	nx = px * cosa + py * sina;
+	ny = py * cosa - px * sina;
+
+	*x = nx + cx;
+	*y = ny + cy;
+}
+
+void
+FreeRotateElementLowLevel(DataTypePtr Data, ElementTypePtr Element, Coord X, Coord Y, double cosa, double sina, Angle angle)
+{
+	/* solder side objects need a different orientation */
+
+	/* the text subroutine decides by itself if the direction
+	 * is to be corrected
+	 */
+#if 0
+	ELEMENTTEXT_LOOP(Element);
+	{
+		if (Data && Data->name_tree[n])
+			r_delete_entry(Data->name_tree[n], (BoxType *) text);
+		RotateTextLowLevel(text, X, Y, Number);
+	}
+	END_LOOP;
+#endif
+	ELEMENTLINE_LOOP(Element);
+	{
+		free_rotate(&line->Point1.X, &line->Point1.Y, X, Y, cosa, sina);
+		free_rotate(&line->Point2.X, &line->Point2.Y, X, Y, cosa, sina);
+		SetLineBoundingBox(line);
+	}
+	END_LOOP;
+	PIN_LOOP(Element);
+	{
+		/* pre-delete the pins from the pin-tree before their coordinates change */
+		if (Data)
+			r_delete_entry(Data->pin_tree, (BoxType *) pin);
+		RestoreToPolygon(Data, PCB_TYPE_PIN, Element, pin);
+		free_rotate(&pin->X, &pin->Y, X, Y, cosa, sina);
+		SetPinBoundingBox(pin);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		/* pre-delete the pads before their coordinates change */
+		if (Data)
+			r_delete_entry(Data->pad_tree, (BoxType *) pad);
+		RestoreToPolygon(Data, PCB_TYPE_PAD, Element, pad);
+		free_rotate(&pad->Point1.X, &pad->Point1.Y, X, Y, cosa, sina);
+		free_rotate(&pad->Point2.X, &pad->Point2.Y, X, Y, cosa, sina);
+		SetLineBoundingBox((LineType *) pad);
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		free_rotate(&arc->X, &arc->Y, X, Y, cosa, sina);
+		arc->StartAngle = NormalizeAngle(arc->StartAngle + angle);
+	}
+	END_LOOP;
+
+	free_rotate(&Element->MarkX, &Element->MarkY, X, Y, cosa, sina);
+	SetElementBoundingBox(Data, Element, &PCB->Font);
+	ClearFromPolygon(Data, PCB_TYPE_ELEMENT, Element, Element);
+}
+
+void FreeRotateBuffer(BufferTypePtr Buffer, Angle angle)
+{
+	double cosa, sina;
+
+	cosa = cos(angle * M_PI / 180.0);
+	sina = sin(angle * M_PI / 180.0);
+
+	/* rotate vias */
+	VIA_LOOP(Buffer->Data);
+	{
+		r_delete_entry(Buffer->Data->via_tree, (BoxType *) via);
+		free_rotate(&via->X, &via->Y, Buffer->X, Buffer->Y, cosa, sina);
+		SetPinBoundingBox(via);
+		r_insert_entry(Buffer->Data->via_tree, (BoxType *) via, 0);
+	}
+	END_LOOP;
+
+	/* elements */
+	ELEMENT_LOOP(Buffer->Data);
+	{
+		FreeRotateElementLowLevel(Buffer->Data, element, Buffer->X, Buffer->Y, cosa, sina, angle);
+	}
+	END_LOOP;
+
+	/* all layer related objects */
+	ALLLINE_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->line_tree, (BoxType *) line);
+		free_rotate(&line->Point1.X, &line->Point1.Y, Buffer->X, Buffer->Y, cosa, sina);
+		free_rotate(&line->Point2.X, &line->Point2.Y, Buffer->X, Buffer->Y, cosa, sina);
+		SetLineBoundingBox(line);
+		r_insert_entry(layer->line_tree, (BoxType *) line, 0);
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->arc_tree, (BoxType *) arc);
+		free_rotate(&arc->X, &arc->Y, Buffer->X, Buffer->Y, cosa, sina);
+		arc->StartAngle = NormalizeAngle(arc->StartAngle + angle);
+		r_insert_entry(layer->arc_tree, (BoxType *) arc, 0);
+	}
+	ENDALL_LOOP;
+	/* FIXME: rotate text */
+	ALLPOLYGON_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->polygon_tree, (BoxType *) polygon);
+		POLYGONPOINT_LOOP(polygon);
+		{
+			free_rotate(&point->X, &point->Y, Buffer->X, Buffer->Y, cosa, sina);
+		}
+		END_LOOP;
+		SetPolygonBoundingBox(polygon);
+		r_insert_entry(layer->polygon_tree, (BoxType *) polygon, 0);
+	}
+	ENDALL_LOOP;
+
+	SetBufferBoundingBox(Buffer);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static const char freerotatebuffer_syntax[] = "FreeRotateBuffer([Angle])";
+
+static const char freerotatebuffer_help[] =
+	"Rotates the current paste buffer contents by the specified angle.  The\n"
+	"angle is given in degrees.  If no angle is given, the user is prompted\n" "for one.\n";
+
+/* %start-doc actions FreeRotateBuffer
+
+Rotates the contents of the pastebuffer by an arbitrary angle.  If no
+angle is given, the user is prompted for one.
+
+%end-doc */
+
+int ActionFreeRotateBuffer(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *angle_s;
+
+	if (argc < 1)
+		angle_s = gui->prompt_for("Enter Rotation (degrees, CCW):", "0");
+	else
+		angle_s = argv[0];
+
+	notify_crosshair_change(pcb_false);
+	FreeRotateBuffer(PASTEBUFFER, strtod(angle_s, 0));
+	notify_crosshair_change(pcb_true);
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes the buffers by allocating memory
+ */
+void InitBuffers(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_BUFFER; i++)
+		Buffers[i].Data = CreateNewBuffer();
+}
+
+void UninitBuffers(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_BUFFER; i++) {
+		ClearBuffer(Buffers+i);
+		free(Buffers[i].Data);
+	}
+}
+
+void pcb_swap_buffers(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_BUFFER; i++)
+		SwapBuffer(&Buffers[i]);
+	SetCrosshairRangeToBuffer();
+}
+
+void MirrorBuffer(BufferTypePtr Buffer)
+{
+	int i;
+
+	if (elementlist_length(&Buffer->Data->Element)) {
+		Message(PCB_MSG_DEFAULT, _("You can't mirror a buffer that has elements!\n"));
+		return;
+	}
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		LayerTypePtr layer = Buffer->Data->Layer + i;
+		if (textlist_length(&layer->Text)) {
+			Message(PCB_MSG_DEFAULT, _("You can't mirror a buffer that has text!\n"));
+			return;
+		}
+	}
+	/* set buffer offset to 'mark' position */
+	Buffer->X = SWAP_X(Buffer->X);
+	Buffer->Y = SWAP_Y(Buffer->Y);
+	VIA_LOOP(Buffer->Data);
+	{
+		via->X = SWAP_X(via->X);
+		via->Y = SWAP_Y(via->Y);
+	}
+	END_LOOP;
+	ALLLINE_LOOP(Buffer->Data);
+	{
+		line->Point1.X = SWAP_X(line->Point1.X);
+		line->Point1.Y = SWAP_Y(line->Point1.Y);
+		line->Point2.X = SWAP_X(line->Point2.X);
+		line->Point2.Y = SWAP_Y(line->Point2.Y);
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(Buffer->Data);
+	{
+		arc->X = SWAP_X(arc->X);
+		arc->Y = SWAP_Y(arc->Y);
+		arc->StartAngle = SWAP_ANGLE(arc->StartAngle);
+		arc->Delta = SWAP_DELTA(arc->Delta);
+		SetArcBoundingBox(arc);
+	}
+	ENDALL_LOOP;
+	ALLPOLYGON_LOOP(Buffer->Data);
+	{
+		POLYGONPOINT_LOOP(polygon);
+		{
+			point->X = SWAP_X(point->X);
+			point->Y = SWAP_Y(point->Y);
+		}
+		END_LOOP;
+		SetPolygonBoundingBox(polygon);
+	}
+	ENDALL_LOOP;
+	SetBufferBoundingBox(Buffer);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * flip components/tracks from one side to the other
+ */
+static void SwapBuffer(BufferTypePtr Buffer)
+{
+	int j, k;
+	pcb_cardinal_t sgroup, cgroup;
+	LayerType swap;
+
+	ELEMENT_LOOP(Buffer->Data);
+	{
+		r_delete_element(Buffer->Data, element);
+		MirrorElementCoordinates(Buffer->Data, element, 0);
+	}
+	END_LOOP;
+	/* set buffer offset to 'mark' position */
+	Buffer->X = SWAP_X(Buffer->X);
+	Buffer->Y = SWAP_Y(Buffer->Y);
+	VIA_LOOP(Buffer->Data);
+	{
+		r_delete_entry(Buffer->Data->via_tree, (BoxType *) via);
+		via->X = SWAP_X(via->X);
+		via->Y = SWAP_Y(via->Y);
+		SetPinBoundingBox(via);
+		r_insert_entry(Buffer->Data->via_tree, (BoxType *) via, 0);
+	}
+	END_LOOP;
+	ALLLINE_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->line_tree, (BoxType *) line);
+		line->Point1.X = SWAP_X(line->Point1.X);
+		line->Point1.Y = SWAP_Y(line->Point1.Y);
+		line->Point2.X = SWAP_X(line->Point2.X);
+		line->Point2.Y = SWAP_Y(line->Point2.Y);
+		SetLineBoundingBox(line);
+		r_insert_entry(layer->line_tree, (BoxType *) line, 0);
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->arc_tree, (BoxType *) arc);
+		arc->X = SWAP_X(arc->X);
+		arc->Y = SWAP_Y(arc->Y);
+		arc->StartAngle = SWAP_ANGLE(arc->StartAngle);
+		arc->Delta = SWAP_DELTA(arc->Delta);
+		SetArcBoundingBox(arc);
+		r_insert_entry(layer->arc_tree, (BoxType *) arc, 0);
+	}
+	ENDALL_LOOP;
+	ALLPOLYGON_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->polygon_tree, (BoxType *) polygon);
+		POLYGONPOINT_LOOP(polygon);
+		{
+			point->X = SWAP_X(point->X);
+			point->Y = SWAP_Y(point->Y);
+		}
+		END_LOOP;
+		SetPolygonBoundingBox(polygon);
+		r_insert_entry(layer->polygon_tree, (BoxType *) polygon, 0);
+		/* hmmm, how to handle clip */
+	}
+	ENDALL_LOOP;
+	ALLTEXT_LOOP(Buffer->Data);
+	{
+		r_delete_entry(layer->text_tree, (BoxType *) text);
+		text->X = SWAP_X(text->X);
+		text->Y = SWAP_Y(text->Y);
+		TOGGLE_FLAG(PCB_FLAG_ONSOLDER, text);
+		SetTextBoundingBox(&PCB->Font, text);
+		r_insert_entry(layer->text_tree, (BoxType *) text, 0);
+	}
+	ENDALL_LOOP;
+	/* swap silkscreen layers */
+	swap = Buffer->Data->Layer[solder_silk_layer];
+	Buffer->Data->Layer[solder_silk_layer] = Buffer->Data->Layer[component_silk_layer];
+	Buffer->Data->Layer[component_silk_layer] = swap;
+
+	/* swap layer groups when balanced */
+	sgroup = GetLayerGroupNumberByNumber(solder_silk_layer);
+	cgroup = GetLayerGroupNumberByNumber(component_silk_layer);
+	if (PCB->LayerGroups.Number[cgroup] == PCB->LayerGroups.Number[sgroup]) {
+		for (j = k = 0; j < PCB->LayerGroups.Number[sgroup]; j++) {
+			int t1, t2;
+			pcb_cardinal_t cnumber = PCB->LayerGroups.Entries[cgroup][k];
+			pcb_cardinal_t snumber = PCB->LayerGroups.Entries[sgroup][j];
+
+			if (snumber >= max_copper_layer)
+				continue;
+			swap = Buffer->Data->Layer[snumber];
+
+			while (cnumber >= max_copper_layer) {
+				k++;
+				cnumber = PCB->LayerGroups.Entries[cgroup][k];
+			}
+			Buffer->Data->Layer[snumber] = Buffer->Data->Layer[cnumber];
+			Buffer->Data->Layer[cnumber] = swap;
+			k++;
+			/* move the thermal flags with the layers */
+			ALLPIN_LOOP(Buffer->Data);
+			{
+				t1 = TEST_THERM(snumber, pin);
+				t2 = TEST_THERM(cnumber, pin);
+				ASSIGN_THERM(snumber, t2, pin);
+				ASSIGN_THERM(cnumber, t1, pin);
+			}
+			ENDALL_LOOP;
+			VIA_LOOP(Buffer->Data);
+			{
+				t1 = TEST_THERM(snumber, via);
+				t2 = TEST_THERM(cnumber, via);
+				ASSIGN_THERM(snumber, t2, via);
+				ASSIGN_THERM(cnumber, t1, via);
+			}
+			END_LOOP;
+		}
+	}
+	SetBufferBoundingBox(Buffer);
+}
+
+/* ----------------------------------------------------------------------
+ * moves the passed object to the passed buffer and removes it
+ * from its original place
+ */
+void *MoveObjectToBuffer(DataTypePtr Destination, DataTypePtr Src, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	/* setup local identifiers used by move operations */
+	Dest = Destination;
+	Source = Src;
+	return (ObjectOperation(&MoveBufferFunctions, Type, Ptr1, Ptr2, Ptr3));
+}
+
+/* ----------------------------------------------------------------------
+ * Adds the passed object to the passed buffer
+ */
+void *CopyObjectToBuffer(DataTypePtr Destination, DataTypePtr Src, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	/* setup local identifiers used by Add operations */
+	Dest = Destination;
+	Source = Src;
+	return (ObjectOperation(&AddBufferFunctions, Type, Ptr1, Ptr2, Ptr3));
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const char pastebuffer_syntax[] =
+	"PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
+	"PasteBuffer(Rotate, 1..3)\n" "PasteBuffer(Convert|Restore|Mirror)\n" "PasteBuffer(ToLayout, X, Y, units)\n" "PasteBuffer(Save, Filename, [format], [force])";
+
+static const char pastebuffer_help[] = "Various operations on the paste buffer.";
+
+/* %start-doc actions PasteBuffer
+
+There are a number of paste buffers; the actual limit is a
+compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}.  It
+is currently @code{5}.  One of these is the ``current'' paste buffer,
+often referred to as ``the'' paste buffer.
+
+ at table @code
+
+ at item AddSelected
+Copies the selected objects to the current paste buffer.
+
+ at item Clear
+Remove all objects from the current paste buffer.
+
+ at item Convert
+Convert the current paste buffer to an element.  Vias are converted to
+pins, lines are converted to pads.
+
+ at item Restore
+Convert any elements in the paste buffer back to vias and lines.
+
+ at item Mirror
+Flip all objects in the paste buffer vertically (up/down flip).  To mirror
+horizontally, combine this with rotations.
+
+ at item Rotate
+Rotates the current buffer.  The number to pass is 1..3, where 1 means
+90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
+degrees clockwise (270 CCW).
+
+ at item Save
+Saves any elements in the current buffer to the indicated file. If
+format is specified, try to use that file format, else use the default.
+If force is specified, overwrite target, don't ask.
+
+ at item ToLayout
+Pastes any elements in the current buffer to the indicated X, Y
+coordinates in the layout.  The @code{X} and @code{Y} are treated like
+ at code{delta} is for many other objects.  For each, if it's prefixed by
+ at code{+} or @code{-}, then that amount is relative to the last
+location.  Otherwise, it's absolute.  Units can be
+ at code{mil} or @code{mm}; if unspecified, units are PCB's internal
+units, currently 1/100 mil.
+
+
+ at item 1..MAX_BUFFER
+Selects the given buffer to be the current paste buffer.
+
+ at end table
+
+%end-doc */
+
+static int ActionPasteBuffer(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = argc ? argv[0] : "";
+	const char *sbufnum = argc > 1 ? argv[1] : "";
+	const char *fmt = argc > 2 ? argv[2] : NULL;
+	const char *forces = argc > 3 ? argv[3] : NULL;
+	const char *name;
+	static char *default_file = NULL;
+	pcb_bool free_name = pcb_false;
+	int force = (forces != NULL) && ((*forces == '1') || (*forces == 'y') || (*forces == 'Y'));
+
+	notify_crosshair_change(pcb_false);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+			/* clear contents of paste buffer */
+		case F_Clear:
+			ClearBuffer(PASTEBUFFER);
+			break;
+
+			/* copies objects to paste buffer */
+		case F_AddSelected:
+			AddSelectedToBuffer(PASTEBUFFER, 0, 0, pcb_false);
+			break;
+
+			/* converts buffer contents into an element */
+		case F_Convert:
+			ConvertBufferToElement(PASTEBUFFER);
+			break;
+
+			/* break up element for editing */
+		case F_Restore:
+			SmashBufferElement(PASTEBUFFER);
+			break;
+
+			/* Mirror buffer */
+		case F_Mirror:
+			MirrorBuffer(PASTEBUFFER);
+			break;
+
+		case F_Rotate:
+			if (sbufnum) {
+				RotateBuffer(PASTEBUFFER, (pcb_uint8_t) atoi(sbufnum));
+				SetCrosshairRangeToBuffer();
+			}
+			break;
+
+		case F_Save:
+			if (elementlist_length(&PASTEBUFFER->Data->Element) == 0) {
+				Message(PCB_MSG_DEFAULT, _("Buffer has no elements!\n"));
+				break;
+			}
+			free_name = pcb_false;
+			if (argc <= 1) {
+				name = gui->fileselect(_("Save Paste Buffer As ..."),
+															 _("Choose a file to save the contents of the\n"
+																 "paste buffer to.\n"), default_file, ".fp", "footprint", 0);
+
+				if (default_file) {
+					free(default_file);
+					default_file = NULL;
+				}
+				if (name && *name) {
+					default_file = pcb_strdup(name);
+				}
+				free_name = pcb_true;
+			}
+
+			else
+				name = argv[1];
+
+			{
+				FILE *exist;
+
+				if ((!force) && ((exist = fopen(name, "r")))) {
+					fclose(exist);
+					if (gui->confirm_dialog(_("File exists!  Ok to overwrite?"), 0))
+						SaveBufferElements(name, fmt);
+				}
+				else
+					SaveBufferElements(name, fmt);
+
+				if (free_name && name)
+					free((char*)name);
+			}
+			break;
+
+		case F_ToLayout:
+			{
+				static Coord oldx = 0, oldy = 0;
+				Coord x, y;
+				pcb_bool absolute;
+
+				if (argc == 1) {
+					x = y = 0;
+				}
+				else if (argc == 3 || argc == 4) {
+					x = GetValue(ACTION_ARG(1), ACTION_ARG(3), &absolute, NULL);
+					if (!absolute)
+						x += oldx;
+					y = GetValue(ACTION_ARG(2), ACTION_ARG(3), &absolute, NULL);
+					if (!absolute)
+						y += oldy;
+				}
+				else {
+					notify_crosshair_change(pcb_true);
+					AFAIL(pastebuffer);
+				}
+
+				oldx = x;
+				oldy = y;
+				if (CopyPastebufferToLayout(x, y))
+					SetChangedFlag(pcb_true);
+			}
+			break;
+
+			/* set number */
+		default:
+			{
+				int number = atoi(function);
+
+				/* correct number */
+				if (number)
+					SetBufferNumber(number - 1);
+			}
+		}
+	}
+
+	notify_crosshair_change(pcb_true);
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+HID_Action buffer_action_list[] = {
+	{"FreeRotateBuffer", 0, ActionFreeRotateBuffer,
+	 freerotatebuffer_help, freerotatebuffer_syntax}
+	,
+	{"LoadFootprint", 0, LoadFootprint,
+	 loadfootprint_help, loadfootprint_syntax}
+	,
+	{"PasteBuffer", 0, ActionPasteBuffer,
+	 pastebuffer_help, pastebuffer_syntax}
+};
+
+REGISTER_ACTIONS(buffer_action_list, NULL)
diff --git a/src/buffer.h b/src/buffer.h
new file mode 100644
index 0000000..1a0d422
--- /dev/null
+++ b/src/buffer.h
@@ -0,0 +1,56 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for buffer handling routines */
+
+#ifndef	PCB_BUFFER_H
+#define	PCB_BUFFER_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * prototypes
+ */
+void SetBufferBoundingBox(BufferTypePtr);
+void ClearBuffer(BufferTypePtr);
+void AddSelectedToBuffer(BufferTypePtr, Coord, Coord, pcb_bool);
+pcb_bool LoadElementToBuffer(BufferTypePtr, const char *);
+pcb_bool ConvertBufferToElement(BufferTypePtr);
+pcb_bool SmashBufferElement(BufferTypePtr);
+pcb_bool LoadLayoutToBuffer(BufferTypePtr Buffer, const char *Filename, const char *fmt);
+void RotateBuffer(BufferTypePtr, pcb_uint8_t);
+void SelectPasteBuffer(int);
+void pcb_swap_buffers(void);
+void MirrorBuffer(BufferTypePtr);
+void InitBuffers(void);
+void UninitBuffers(void);
+void *MoveObjectToBuffer(DataTypePtr, DataTypePtr, int, void *, void *, void *);
+void *CopyObjectToBuffer(DataTypePtr, DataTypePtr, int, void *, void *, void *);
+
+/* This action is called from ActionElementAddIf() */
+int LoadFootprint(int argc, const char **argv, Coord x, Coord y);
+
+#endif
diff --git a/src/buildin.c.in b/src/buildin.c.in
new file mode 100644
index 0000000..37ebbcf
--- /dev/null
+++ b/src/buildin.c.in
@@ -0,0 +1,14 @@
+print [@
+/**** This file autogenerated by scconfig, do not edit ****/
+#include <stdlib.h>
+#include "plugins.h"
+#include "buildin.h"
+
+@?/local/pcb/buildin_init_extern@
+
+void buildin_init(void)
+{
+	pcb_uninit_t uninit_func;
+@?/local/pcb/buildin_init_code@
+}
+@]
diff --git a/src/buildin.h b/src/buildin.h
new file mode 100644
index 0000000..16315c4
--- /dev/null
+++ b/src/buildin.h
@@ -0,0 +1,2 @@
+/* Run the init code of all buildins. */
+void buildin_init(void);
diff --git a/src/change.c b/src/change.c
new file mode 100644
index 0000000..654e5c8
--- /dev/null
+++ b/src/change.c
@@ -0,0 +1,2746 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* functions used to change object properties
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h>
+
+#include "conf_core.h"
+
+#include "crosshair.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "misc.h"
+#include "mirror.h"
+#include "polygon.h"
+#include "rtree.h"
+#include "select.h"
+#include "undo.h"
+#include "hid_actions.h"
+#include "layer.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *ChangePinSize(ElementTypePtr, PinTypePtr);
+static void *ChangePinClearSize(ElementTypePtr, PinTypePtr);
+static void *ChangePinMaskSize(ElementTypePtr, PinTypePtr);
+static void *ChangePadSize(ElementTypePtr, PadTypePtr);
+static void *ChangePadClearSize(ElementTypePtr, PadTypePtr);
+static void *ChangePadMaskSize(ElementTypePtr, PadTypePtr);
+static void *ChangePin2ndSize(ElementTypePtr, PinTypePtr);
+static void *ChangeElement1stSize(ElementTypePtr);
+static void *ChangeElement2ndSize(ElementTypePtr);
+static void *ChangeViaSize(PinTypePtr);
+static void *ChangeVia2ndSize(PinTypePtr);
+static void *ChangeViaClearSize(PinTypePtr);
+static void *ChangeViaMaskSize(PinTypePtr);
+static void *ChangeLineSize(LayerTypePtr, LineTypePtr);
+static void *ChangeLineClearSize(LayerTypePtr, LineTypePtr);
+static void *ChangePolygonClearSize(LayerTypePtr, PolygonTypePtr);
+static void *ChangeArcSize(LayerTypePtr, ArcTypePtr);
+static void *ChangeArcClearSize(LayerTypePtr, ArcTypePtr);
+static void *ChangeTextSize(LayerTypePtr, TextTypePtr);
+static void *ChangeElementSize(ElementTypePtr);
+static void *ChangeElementNameSize(ElementTypePtr);
+static void *ChangeElementClearSize(ElementTypePtr);
+static void *ChangePinName(ElementTypePtr, PinTypePtr);
+static void *ChangePadName(ElementTypePtr, PadTypePtr);
+static void *ChangePinNum(ElementTypePtr, PinTypePtr);
+static void *ChangePadNum(ElementTypePtr, PadTypePtr);
+static void *ChangeViaName(PinTypePtr);
+static void *ChangeLineName(LayerTypePtr, LineTypePtr);
+static void *ChangeElementName(ElementTypePtr);
+static void *ChangeElementNonetlist(ElementTypePtr);
+static void *ChangeTextName(LayerTypePtr, TextTypePtr);
+static void *ChangeElementSquare(ElementTypePtr);
+static void *SetElementSquare(ElementTypePtr);
+static void *ClrElementSquare(ElementTypePtr);
+static void *ChangeElementOctagon(ElementTypePtr);
+static void *SetElementOctagon(ElementTypePtr);
+static void *ClrElementOctagon(ElementTypePtr);
+static void *ChangeViaSquare(PinTypePtr);
+static void *ChangePinSquare(ElementTypePtr, PinTypePtr);
+static void *SetPinSquare(ElementTypePtr, PinTypePtr);
+static void *ClrPinSquare(ElementTypePtr, PinTypePtr);
+static void *ChangePinOctagon(ElementTypePtr, PinTypePtr);
+static void *SetPinOctagon(ElementTypePtr, PinTypePtr);
+static void *ClrPinOctagon(ElementTypePtr, PinTypePtr);
+static void *ChangeViaOctagon(PinTypePtr);
+static void *SetViaOctagon(PinTypePtr);
+static void *ClrViaOctagon(PinTypePtr);
+static void *ChangePadSquare(ElementTypePtr, PadTypePtr);
+static void *SetPadSquare(ElementTypePtr, PadTypePtr);
+static void *ClrPadSquare(ElementTypePtr, PadTypePtr);
+static void *ChangeViaThermal(PinTypePtr);
+static void *ChangePinThermal(ElementTypePtr, PinTypePtr);
+static void *ChangeLineJoin(LayerTypePtr, LineTypePtr);
+static void *SetLineJoin(LayerTypePtr, LineTypePtr);
+static void *ClrLineJoin(LayerTypePtr, LineTypePtr);
+static void *ChangeArcJoin(LayerTypePtr, ArcTypePtr);
+static void *SetArcJoin(LayerTypePtr, ArcTypePtr);
+static void *ClrArcJoin(LayerTypePtr, ArcTypePtr);
+static void *ChangeTextJoin(LayerTypePtr, TextTypePtr);
+static void *SetTextJoin(LayerTypePtr, TextTypePtr);
+static void *ClrTextJoin(LayerTypePtr, TextTypePtr);
+static void *ChangePolyClear(LayerTypePtr, PolygonTypePtr);
+static void *ChangeArcRadius(LayerTypePtr, ArcTypePtr);
+static void *ChangeArcAngle(LayerTypePtr, ArcTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static Coord Delta;							/* change of size */
+static Coord Absolute;					/* Absolute size */
+static int is_primary;					/* whether the primary parameter should be changed */
+static char *NewName;						/* new name */
+
+static Angle ADelta, AAbsolute;				/* same as above, but for angles */
+
+static ObjectFunctionType ChangeSizeFunctions = {
+	ChangeLineSize,
+	ChangeTextSize,
+	ChangePolyClear,
+	ChangeViaSize,
+	ChangeElementSize,						/* changes silk screen line width */
+	ChangeElementNameSize,
+	ChangePinSize,
+	ChangePadSize,
+	NULL,
+	NULL,
+	ChangeArcSize,
+	NULL
+};
+
+static ObjectFunctionType Change1stSizeFunctions = {
+	ChangeLineSize,
+	ChangeTextSize,
+	ChangePolyClear,
+	ChangeViaSize,
+	ChangeElement1stSize,
+	ChangeElementNameSize,
+	ChangePinSize,
+	ChangePadSize,
+	NULL,
+	NULL,
+	ChangeArcSize,
+	NULL
+};
+
+static ObjectFunctionType Change2ndSizeFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	ChangeVia2ndSize,
+	ChangeElement2ndSize,
+	NULL,
+	ChangePin2ndSize,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeThermalFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	ChangeViaThermal,
+	NULL,
+	NULL,
+	ChangePinThermal,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeClearSizeFunctions = {
+	ChangeLineClearSize,
+	NULL,
+	ChangePolygonClearSize,				/* just to tell the user not to :-) */
+	ChangeViaClearSize,
+	ChangeElementClearSize,
+	NULL,
+	ChangePinClearSize,
+	ChangePadClearSize,
+	NULL,
+	NULL,
+	ChangeArcClearSize,
+	NULL
+};
+
+static ObjectFunctionType ChangeNameFunctions = {
+	ChangeLineName,
+	ChangeTextName,
+	NULL,
+	ChangeViaName,
+	ChangeElementName,
+	NULL,
+	ChangePinName,
+	ChangePadName,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangePinnumFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ChangePinNum,
+	ChangePadNum,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeSquareFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	ChangeViaSquare,
+	ChangeElementSquare,
+	NULL,
+	ChangePinSquare,
+	ChangePadSquare,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeNonetlistFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ChangeElementNonetlist,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeJoinFunctions = {
+	ChangeLineJoin,
+	ChangeTextJoin,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ChangeArcJoin,
+	NULL
+};
+
+static ObjectFunctionType ChangeOctagonFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	ChangeViaOctagon,
+	ChangeElementOctagon,
+	NULL,
+	ChangePinOctagon,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeMaskSizeFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	ChangeViaMaskSize,
+#if 0
+	ChangeElementMaskSize,
+#else
+	NULL,
+#endif
+	NULL,
+	ChangePinMaskSize,
+	ChangePadMaskSize,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType SetSquareFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	SetElementSquare,
+	NULL,
+	SetPinSquare,
+	SetPadSquare,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType SetJoinFunctions = {
+	SetLineJoin,
+	SetTextJoin,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	SetArcJoin,
+	NULL
+};
+
+static ObjectFunctionType SetOctagonFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	SetViaOctagon,
+	SetElementOctagon,
+	NULL,
+	SetPinOctagon,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ClrSquareFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ClrElementSquare,
+	NULL,
+	ClrPinSquare,
+	ClrPadSquare,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ClrJoinFunctions = {
+	ClrLineJoin,
+	ClrTextJoin,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ClrArcJoin,
+	NULL
+};
+
+static ObjectFunctionType ClrOctagonFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	ClrViaOctagon,
+	ClrElementOctagon,
+	NULL,
+	ClrPinOctagon,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static ObjectFunctionType ChangeRadiusFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ChangeArcRadius,
+	NULL
+};
+
+static ObjectFunctionType ChangeAngleFunctions = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	ChangeArcAngle,
+	NULL
+};
+
+
+/* ---------------------------------------------------------------------------
+ * changes the thermal on a via
+ * returns TRUE if changed
+ */
+static void *ChangeViaThermal(PinTypePtr Via)
+{
+	AddObjectToClearPolyUndoList(PCB_TYPE_VIA, Via, Via, Via, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, CURRENT, Via);
+	AddObjectToFlagUndoList(PCB_TYPE_VIA, Via, Via, Via);
+	if (!Delta)										/* remove the thermals */
+		CLEAR_THERM(INDEXOFCURRENT, Via);
+	else
+		ASSIGN_THERM(INDEXOFCURRENT, Delta, Via);
+	AddObjectToClearPolyUndoList(PCB_TYPE_VIA, Via, Via, Via, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, CURRENT, Via);
+	DrawVia(Via);
+	return Via;
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the thermal on a pin
+ * returns TRUE if changed
+ */
+static void *ChangePinThermal(ElementTypePtr element, PinTypePtr Pin)
+{
+	AddObjectToClearPolyUndoList(PCB_TYPE_PIN, element, Pin, Pin, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, CURRENT, Pin);
+	AddObjectToFlagUndoList(PCB_TYPE_PIN, element, Pin, Pin);
+	if (!Delta)										/* remove the thermals */
+		CLEAR_THERM(INDEXOFCURRENT, Pin);
+	else
+		ASSIGN_THERM(INDEXOFCURRENT, Delta, Pin);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PIN, element, Pin, Pin, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, CURRENT, Pin);
+	DrawPin(Pin);
+	return Pin;
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the size of a via
+ * returns TRUE if changed
+ */
+static void *ChangeViaSize(PinTypePtr Via)
+{
+	Coord value = Absolute ? Absolute : Via->Thickness + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via))
+		return (NULL);
+	if (!TEST_FLAG(PCB_FLAG_HOLE, Via) && value <= MAX_PINORVIASIZE &&
+			value >= MIN_PINORVIASIZE && value >= Via->DrillingHole + MIN_PINORVIACOPPER && value != Via->Thickness) {
+		AddObjectToSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+		EraseVia(Via);
+		r_delete_entry(PCB->Data->via_tree, (BoxType *) Via);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Via, Via);
+		if (Via->Mask) {
+			AddObjectToMaskSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+			Via->Mask += value - Via->Thickness;
+		}
+		Via->Thickness = value;
+		SetPinBoundingBox(Via);
+		r_insert_entry(PCB->Data->via_tree, (BoxType *) Via, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+		DrawVia(Via);
+		return (Via);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the drilling hole of a via
+ * returns TRUE if changed
+ */
+static void *ChangeVia2ndSize(PinTypePtr Via)
+{
+	Coord value = (Absolute) ? Absolute : Via->DrillingHole + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via))
+		return (NULL);
+	if (value <= MAX_PINORVIASIZE &&
+			value >= MIN_PINORVIAHOLE && (TEST_FLAG(PCB_FLAG_HOLE, Via) || value <= Via->Thickness - MIN_PINORVIACOPPER)
+			&& value != Via->DrillingHole) {
+		AddObjectTo2ndSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+		EraseVia(Via);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+		Via->DrillingHole = value;
+		if (TEST_FLAG(PCB_FLAG_HOLE, Via)) {
+			AddObjectToSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+			Via->Thickness = value;
+		}
+		ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+		DrawVia(Via);
+		return (Via);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance size of a via
+ * returns TRUE if changed
+ */
+static void *ChangeViaClearSize(PinTypePtr Via)
+{
+	Coord value = (Absolute) ? Absolute : Via->Clearance + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via))
+		return (NULL);
+	value = MIN(MAX_LINESIZE, value);
+	if (value < 0)
+		value = 0;
+	if (Delta < 0 && value < PCB->Bloat * 2)
+		value = 0;
+	if ((Delta > 0 || Absolute) && value < PCB->Bloat * 2)
+		value = PCB->Bloat * 2 + 2;
+	if (Via->Clearance == value)
+		return NULL;
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	AddObjectToClearSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+	EraseVia(Via);
+	r_delete_entry(PCB->Data->via_tree, (BoxType *) Via);
+	Via->Clearance = value;
+	SetPinBoundingBox(Via);
+	r_insert_entry(PCB->Data->via_tree, (BoxType *) Via, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	DrawVia(Via);
+	Via->Element = NULL;
+	return (Via);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * changes the size of a pin
+ * returns TRUE if changed
+ */
+static void *ChangePinSize(ElementTypePtr Element, PinTypePtr Pin)
+{
+	Coord value = (Absolute) ? Absolute : Pin->Thickness + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin))
+		return (NULL);
+	if (!TEST_FLAG(PCB_FLAG_HOLE, Pin) && value <= MAX_PINORVIASIZE &&
+			value >= MIN_PINORVIASIZE && value >= Pin->DrillingHole + MIN_PINORVIACOPPER && value != Pin->Thickness) {
+		AddObjectToSizeUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+		AddObjectToMaskSizeUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+		ErasePin(Pin);
+		r_delete_entry(PCB->Data->pin_tree, &Pin->BoundingBox);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+		Pin->Mask += value - Pin->Thickness;
+		Pin->Thickness = value;
+		/* SetElementBB updates all associated rtrees */
+		SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+		DrawPin(Pin);
+		return (Pin);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance size of a pin
+ * returns TRUE if changed
+ */
+static void *ChangePinClearSize(ElementTypePtr Element, PinTypePtr Pin)
+{
+	Coord value = (Absolute) ? Absolute : Pin->Clearance + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin))
+		return (NULL);
+	value = MIN(MAX_LINESIZE, value);
+	if (value < 0)
+		value = 0;
+	if (Delta < 0 && value < PCB->Bloat * 2)
+		value = 0;
+	if ((Delta > 0 || Absolute) && value < PCB->Bloat * 2)
+		value = PCB->Bloat * 2 + 2;
+	if (Pin->Clearance == value)
+		return NULL;
+	RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+	AddObjectToClearSizeUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+	ErasePin(Pin);
+	r_delete_entry(PCB->Data->pin_tree, &Pin->BoundingBox);
+	Pin->Clearance = value;
+	/* SetElementBB updates all associated rtrees */
+	SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+	DrawPin(Pin);
+	return (Pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the size of a pad
+ * returns TRUE if changed
+ */
+static void *ChangePadSize(ElementTypePtr Element, PadTypePtr Pad)
+{
+	Coord value = (Absolute) ? Absolute : Pad->Thickness + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pad))
+		return (NULL);
+	if (value <= MAX_PADSIZE && value >= MIN_PADSIZE && value != Pad->Thickness) {
+		AddObjectToSizeUndoList(PCB_TYPE_PAD, Element, Pad, Pad);
+		AddObjectToMaskSizeUndoList(PCB_TYPE_PAD, Element, Pad, Pad);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_PAD, Element, Pad);
+		ErasePad(Pad);
+		r_delete_entry(PCB->Data->pad_tree, &Pad->BoundingBox);
+		Pad->Mask += value - Pad->Thickness;
+		Pad->Thickness = value;
+		/* SetElementBB updates all associated rtrees */
+		SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_PAD, Element, Pad);
+		DrawPad(Pad);
+		return (Pad);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance size of a pad
+ * returns TRUE if changed
+ */
+static void *ChangePadClearSize(ElementTypePtr Element, PadTypePtr Pad)
+{
+	Coord value = (Absolute) ? Absolute : Pad->Clearance + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pad))
+		return (NULL);
+	value = MIN(MAX_LINESIZE, value);
+	if (value < 0)
+		value = 0;
+	if (Delta < 0 && value < PCB->Bloat * 2)
+		value = 0;
+	if ((Delta > 0 || Absolute) && value < PCB->Bloat * 2)
+		value = PCB->Bloat * 2 + 2;
+	if (value == Pad->Clearance)
+		return NULL;
+	AddObjectToClearSizeUndoList(PCB_TYPE_PAD, Element, Pad, Pad);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_PAD, Element, Pad);
+	ErasePad(Pad);
+	r_delete_entry(PCB->Data->pad_tree, &Pad->BoundingBox);
+	Pad->Clearance = value;
+	/* SetElementBB updates all associated rtrees */
+	SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_PAD, Element, Pad);
+	DrawPad(Pad);
+	return Pad;
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the drilling hole of all pins of an element
+ * returns TRUE if changed
+ */
+static void *ChangeElement2ndSize(ElementTypePtr Element)
+{
+	pcb_bool changed = pcb_false;
+	Coord value;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		value = (Absolute) ? Absolute : pin->DrillingHole + Delta;
+		if (value <= MAX_PINORVIASIZE &&
+				value >= MIN_PINORVIAHOLE && (TEST_FLAG(PCB_FLAG_HOLE, pin) || value <= pin->Thickness - MIN_PINORVIACOPPER)
+				&& value != pin->DrillingHole) {
+			changed = pcb_true;
+			AddObjectTo2ndSizeUndoList(PCB_TYPE_PIN, Element, pin, pin);
+			ErasePin(pin);
+			RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, pin);
+			pin->DrillingHole = value;
+			if (TEST_FLAG(PCB_FLAG_HOLE, pin)) {
+				AddObjectToSizeUndoList(PCB_TYPE_PIN, Element, pin, pin);
+				pin->Thickness = value;
+			}
+			ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, pin);
+			DrawPin(pin);
+		}
+	}
+	END_LOOP;
+	if (changed)
+		return (Element);
+	else
+		return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes ring dia of all pins of an element
+ * returns TRUE if changed
+ */
+static void *ChangeElement1stSize(ElementTypePtr Element)
+{
+	pcb_bool changed = pcb_false;
+	Coord value;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		value = (Absolute) ? Absolute : pin->DrillingHole + Delta;
+		if (value <= MAX_PINORVIASIZE && value >= pin->DrillingHole + MIN_PINORVIACOPPER && value != pin->Thickness) {
+			changed = pcb_true;
+			AddObjectToSizeUndoList(PCB_TYPE_PIN, Element, pin, pin);
+			ErasePin(pin);
+			RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, pin);
+			pin->Thickness = value;
+			if (TEST_FLAG(PCB_FLAG_HOLE, pin)) {
+				AddObjectToSizeUndoList(PCB_TYPE_PIN, Element, pin, pin);
+				pin->Thickness = value;
+			}
+			ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, pin);
+			DrawPin(pin);
+		}
+	}
+	END_LOOP;
+	if (changed)
+		return (Element);
+	else
+		return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance of all pins of an element
+ * returns TRUE if changed
+ */
+static void *ChangeElementClearSize(ElementTypePtr Element)
+{
+	pcb_bool changed = pcb_false;
+	Coord value;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		value = (Absolute) ? Absolute : pin->Clearance + Delta;
+		if (value <= MAX_PINORVIASIZE &&
+				value >= MIN_PINORVIAHOLE && (TEST_FLAG(PCB_FLAG_HOLE, pin) || value <= pin->Thickness - MIN_PINORVIACOPPER)
+				&& value != pin->Clearance) {
+			changed = pcb_true;
+			AddObjectToClearSizeUndoList(PCB_TYPE_PIN, Element, pin, pin);
+			ErasePin(pin);
+			RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, pin);
+			pin->Clearance = value;
+			if (TEST_FLAG(PCB_FLAG_HOLE, pin)) {
+				AddObjectToSizeUndoList(PCB_TYPE_PIN, Element, pin, pin);
+				pin->Thickness = value;
+			}
+			ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, pin);
+			DrawPin(pin);
+		}
+	}
+	END_LOOP;
+
+	PAD_LOOP(Element);
+	{
+		value = (Absolute) ? Absolute : pad->Clearance + Delta;
+		if (value <= MAX_PINORVIASIZE && value >= MIN_PINORVIAHOLE && value != pad->Clearance) {
+			changed = pcb_true;
+			AddObjectToClearSizeUndoList(PCB_TYPE_PAD, Element, pad, pad);
+			ErasePad(pad);
+			RestoreToPolygon(PCB->Data, PCB_TYPE_PAD, Element, pad);
+			r_delete_entry(PCB->Data->pad_tree, &pad->BoundingBox);
+			pad->Clearance = value;
+			if (TEST_FLAG(PCB_FLAG_HOLE, pad)) {
+				AddObjectToSizeUndoList(PCB_TYPE_PAD, Element, pad, pad);
+				pad->Thickness = value;
+			}
+			/* SetElementBB updates all associated rtrees */
+			SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+
+			ClearFromPolygon(PCB->Data, PCB_TYPE_PAD, Element, pad);
+			DrawPad(pad);
+		}
+	}
+	END_LOOP;
+
+	if (changed)
+		return (Element);
+	else
+		return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the drilling hole of a pin
+ * returns TRUE if changed
+ */
+static void *ChangePin2ndSize(ElementTypePtr Element, PinTypePtr Pin)
+{
+	Coord value = (Absolute) ? Absolute : Pin->DrillingHole + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin))
+		return (NULL);
+	if (value <= MAX_PINORVIASIZE &&
+			value >= MIN_PINORVIAHOLE && (TEST_FLAG(PCB_FLAG_HOLE, Pin) || value <= Pin->Thickness - MIN_PINORVIACOPPER)
+			&& value != Pin->DrillingHole) {
+		AddObjectTo2ndSizeUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+		ErasePin(Pin);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+		Pin->DrillingHole = value;
+		if (TEST_FLAG(PCB_FLAG_HOLE, Pin)) {
+			AddObjectToSizeUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+			Pin->Thickness = value;
+		}
+		ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+		DrawPin(Pin);
+		return (Pin);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the size of a line
+ * returns TRUE if changed
+ */
+static void *ChangeLineSize(LayerTypePtr Layer, LineTypePtr Line)
+{
+	Coord value = (Absolute) ? Absolute : Line->Thickness + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Line))
+		return (NULL);
+	if (value <= MAX_LINESIZE && value >= MIN_LINESIZE && value != Line->Thickness) {
+		AddObjectToSizeUndoList(PCB_TYPE_LINE, Layer, Line, Line);
+		EraseLine(Line);
+		r_delete_entry(Layer->line_tree, (BoxTypePtr) Line);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		Line->Thickness = value;
+		SetLineBoundingBox(Line);
+		r_insert_entry(Layer->line_tree, (BoxTypePtr) Line, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		DrawLine(Layer, Line);
+		return (Line);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance size of a line
+ * returns TRUE if changed
+ */
+static void *ChangeLineClearSize(LayerTypePtr Layer, LineTypePtr Line)
+{
+	Coord value = (Absolute) ? Absolute : Line->Clearance + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Line) || !TEST_FLAG(PCB_FLAG_CLEARLINE, Line))
+		return (NULL);
+	value = MIN(MAX_LINESIZE, MAX(value, PCB->Bloat * 2 + 2));
+	if (value != Line->Clearance) {
+		AddObjectToClearSizeUndoList(PCB_TYPE_LINE, Layer, Line, Line);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		EraseLine(Line);
+		r_delete_entry(Layer->line_tree, (BoxTypePtr) Line);
+		Line->Clearance = value;
+		if (Line->Clearance == 0) {
+			CLEAR_FLAG(PCB_FLAG_CLEARLINE, Line);
+			Line->Clearance = PCB_MIL_TO_COORD(10);
+		}
+		SetLineBoundingBox(Line);
+		r_insert_entry(Layer->line_tree, (BoxTypePtr) Line, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		DrawLine(Layer, Line);
+		return (Line);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * Handle attempts to change the clearance of a polygon.
+ */
+static void *ChangePolygonClearSize(LayerTypePtr Layer, PolygonTypePtr poly)
+{
+	static int shown_this_message = 0;
+	if (!shown_this_message) {
+		gui->confirm_dialog(_("To change the clearance of objects in a polygon, "
+													"change the objects, not the polygon.\n"
+													"Hint: To set a minimum clearance for a group of objects, "
+													"select them all then :MinClearGap(Selected,=10,mil)"), "Ok", NULL);
+		shown_this_message = 1;
+	}
+
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the size of an arc
+ * returns TRUE if changed
+ */
+static void *ChangeArcSize(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	Coord value = (Absolute) ? Absolute : Arc->Thickness + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc))
+		return (NULL);
+	if (value <= MAX_LINESIZE && value >= MIN_LINESIZE && value != Arc->Thickness) {
+		AddObjectToSizeUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+		EraseArc(Arc);
+		r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		Arc->Thickness = value;
+		SetArcBoundingBox(Arc);
+		r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		DrawArc(Layer, Arc);
+		return (Arc);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance size of an arc
+ * returns TRUE if changed
+ */
+static void *ChangeArcClearSize(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	Coord value = (Absolute) ? Absolute : Arc->Clearance + Delta;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc) || !TEST_FLAG(PCB_FLAG_CLEARLINE, Arc))
+		return (NULL);
+	value = MIN(MAX_LINESIZE, MAX(value, PCB->Bloat * 2 + 2));
+	if (value != Arc->Clearance) {
+		AddObjectToClearSizeUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+		EraseArc(Arc);
+		r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		Arc->Clearance = value;
+		if (Arc->Clearance == 0) {
+			CLEAR_FLAG(PCB_FLAG_CLEARLINE, Arc);
+			Arc->Clearance = PCB_MIL_TO_COORD(10);
+		}
+		SetArcBoundingBox(Arc);
+		r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		DrawArc(Layer, Arc);
+		return (Arc);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the radius of an arc (is_primary 0=width or 1=height or 2=both)
+ * returns TRUE if changed
+ */
+static void *ChangeArcRadius(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	Coord value, *dst;
+	void *a0, *a1;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc))
+		return (NULL);
+
+	switch(is_primary) {
+		case 0: dst = &Arc->Width; break;
+		case 1: dst = &Arc->Height; break;
+		case 2:
+			is_primary = 0; a0 = ChangeArcRadius(Layer, Arc);
+			is_primary = 1; a1 = ChangeArcRadius(Layer, Arc);
+			if ((a0 != NULL) || (a1 != NULL))
+				return Arc;
+			return NULL;
+	}
+
+	value = (Absolute) ? Absolute : (*dst) + Delta;
+	value = MIN(MAX_ARCSIZE, MAX(value, MIN_ARCSIZE));
+	if (value != *dst) {
+		AddObjectToChangeRadiiUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+		EraseArc(Arc);
+		r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		*dst = value;
+		SetArcBoundingBox(Arc);
+		r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		DrawArc(Layer, Arc);
+		return (Arc);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the angle of an arc (is_primary 0=start or 1=end)
+ * returns TRUE if changed
+ */
+static void *ChangeArcAngle(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	Angle value, *dst;
+	void *a0, *a1;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc))
+		return (NULL);
+
+	switch(is_primary) {
+		case 0: dst = &Arc->StartAngle; break;
+		case 1: dst = &Arc->Delta; break;
+		case 2:
+			is_primary = 0; a0 = ChangeArcAngle(Layer, Arc);
+			is_primary = 1; a1 = ChangeArcAngle(Layer, Arc);
+			if ((a0 != NULL) || (a1 != NULL))
+				return Arc;
+			return NULL;
+	}
+
+	value = (AAbsolute) ? AAbsolute : (*dst) + ADelta;
+	value = fmod(value, 360.0);
+	if (value < 0)
+		value += 360;
+
+	if (value != *dst) {
+		AddObjectToChangeAnglesUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+		EraseArc(Arc);
+		r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		*dst = value;
+		SetArcBoundingBox(Arc);
+		r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		DrawArc(Layer, Arc);
+		return (Arc);
+	}
+	return (NULL);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * changes the scaling factor of a text object
+ * returns TRUE if changed
+ */
+static void *ChangeTextSize(LayerTypePtr Layer, TextTypePtr Text)
+{
+	int value = Absolute ? PCB_COORD_TO_MIL(Absolute)
+		: Text->Scale + PCB_COORD_TO_MIL(Delta);
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Text))
+		return (NULL);
+	if (value <= MAX_TEXTSCALE && value >= MIN_TEXTSCALE && value != Text->Scale) {
+		AddObjectToSizeUndoList(PCB_TYPE_TEXT, Layer, Text, Text);
+		EraseText(Layer, Text);
+		r_delete_entry(Layer->text_tree, (BoxTypePtr) Text);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+		Text->Scale = value;
+		SetTextBoundingBox(&PCB->Font, Text);
+		r_insert_entry(Layer->text_tree, (BoxTypePtr) Text, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+		DrawText(Layer, Text);
+		return (Text);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the scaling factor of an element's outline
+ * returns TRUE if changed
+ */
+static void *ChangeElementSize(ElementTypePtr Element)
+{
+	Coord value;
+	pcb_bool changed = pcb_false;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	if (PCB->ElementOn)
+		EraseElement(Element);
+	ELEMENTLINE_LOOP(Element);
+	{
+		value = (Absolute) ? Absolute : line->Thickness + Delta;
+		if (value <= MAX_LINESIZE && value >= MIN_LINESIZE && value != line->Thickness) {
+			AddObjectToSizeUndoList(PCB_TYPE_ELEMENT_LINE, Element, line, line);
+			line->Thickness = value;
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		value = (Absolute) ? Absolute : arc->Thickness + Delta;
+		if (value <= MAX_LINESIZE && value >= MIN_LINESIZE && value != arc->Thickness) {
+			AddObjectToSizeUndoList(PCB_TYPE_ELEMENT_ARC, Element, arc, arc);
+			arc->Thickness = value;
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	if (PCB->ElementOn) {
+		DrawElement(Element);
+	}
+	if (changed)
+		return (Element);
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the scaling factor of a elementname object
+ * returns TRUE if changed
+ */
+static void *ChangeElementNameSize(ElementTypePtr Element)
+{
+	int value = Absolute ? PCB_COORD_TO_MIL(Absolute)
+		: DESCRIPTION_TEXT(Element).Scale + PCB_COORD_TO_MIL(Delta);
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, &Element->Name[0]))
+		return (NULL);
+	if (value <= MAX_TEXTSCALE && value >= MIN_TEXTSCALE) {
+		EraseElementName(Element);
+		ELEMENTTEXT_LOOP(Element);
+		{
+			AddObjectToSizeUndoList(PCB_TYPE_ELEMENT_NAME, Element, text, text);
+			r_delete_entry(PCB->Data->name_tree[n], (BoxType *) text);
+			text->Scale = value;
+			SetTextBoundingBox(&PCB->Font, text);
+			r_insert_entry(PCB->Data->name_tree[n], (BoxType *) text, 0);
+		}
+		END_LOOP;
+		DrawElementName(Element);
+		return (Element);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of a via
+ */
+static void *ChangeViaName(PinTypePtr Via)
+{
+	char *old = Via->Name;
+
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Via)) {
+		ErasePinName(Via);
+		Via->Name = NewName;
+		DrawPinName(Via);
+	}
+	else
+		Via->Name = NewName;
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of a pin
+ */
+static void *ChangePinName(ElementTypePtr Element, PinTypePtr Pin)
+{
+	char *old = Pin->Name;
+
+	Element = Element;						/* get rid of 'unused...' warnings */
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pin)) {
+		ErasePinName(Pin);
+		Pin->Name = NewName;
+		DrawPinName(Pin);
+	}
+	else
+		Pin->Name = NewName;
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of a pad
+ */
+static void *ChangePadName(ElementTypePtr Element, PadTypePtr Pad)
+{
+	char *old = Pad->Name;
+
+	Element = Element;						/* get rid of 'unused...' warnings */
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pad)) {
+		ErasePadName(Pad);
+		Pad->Name = NewName;
+		DrawPadName(Pad);
+	}
+	else
+		Pad->Name = NewName;
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the number of a pin
+ */
+static void *ChangePinNum(ElementTypePtr Element, PinTypePtr Pin)
+{
+	char *old = Pin->Number;
+
+	Element = Element;						/* get rid of 'unused...' warnings */
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pin)) {
+		ErasePinName(Pin);
+		Pin->Number = NewName;
+		DrawPinName(Pin);
+	}
+	else
+		Pin->Number = NewName;
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the number of a pad
+ */
+static void *ChangePadNum(ElementTypePtr Element, PadTypePtr Pad)
+{
+	char *old = Pad->Number;
+
+	Element = Element;						/* get rid of 'unused...' warnings */
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pad)) {
+		ErasePadName(Pad);
+		Pad->Number = NewName;
+		DrawPadName(Pad);
+	}
+	else
+		Pad->Number = NewName;
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of a line
+ */
+static void *ChangeLineName(LayerTypePtr Layer, LineTypePtr Line)
+{
+	char *old = Line->Number;
+
+	Layer = Layer;
+	Line->Number = NewName;
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the layout-name of an element
+ */
+
+char *ChangeElementText(PCBType * pcb, DataType * data, ElementTypePtr Element, int which, char *new_name)
+{
+	char *old = Element->Name[which].TextString;
+
+#ifdef DEBUG
+	printf("In ChangeElementText, updating old TextString %s to %s\n", old, new_name);
+#endif
+
+	if (pcb && which == NAME_INDEX())
+		EraseElementName(Element);
+
+	r_delete_entry(data->name_tree[which], &Element->Name[which].BoundingBox);
+
+	Element->Name[which].TextString = new_name;
+	SetTextBoundingBox(&PCB->Font, &Element->Name[which]);
+
+	r_insert_entry(data->name_tree[which], &Element->Name[which].BoundingBox, 0);
+
+	if (pcb && which == NAME_INDEX())
+		DrawElementName(Element);
+
+	return old;
+}
+
+static void *ChangeElementName(ElementTypePtr Element)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, &Element->Name[0]))
+		return (NULL);
+	if (NAME_INDEX() == NAMEONPCB_INDEX) {
+		if (conf_core.editor.unique_names && UniqueElementName(PCB->Data, NewName) != NewName) {
+			Message(PCB_MSG_DEFAULT, _("Error: The name \"%s\" is not unique!\n"), NewName);
+			return ((char *) -1);
+		}
+	}
+
+	return ChangeElementText(PCB, PCB->Data, Element, NAME_INDEX(), NewName);
+}
+
+static void *ChangeElementNonetlist(ElementTypePtr Element)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	TOGGLE_FLAG(PCB_FLAG_NONETLIST, Element);
+	return Element;
+}
+
+/* ---------------------------------------------------------------------------
+ * sets data of a text object and calculates bounding box
+ * memory must have already been allocated
+ * the one for the new string is allocated
+ * returns pcb_true if the string has been changed
+ */
+static void *ChangeTextName(LayerTypePtr Layer, TextTypePtr Text)
+{
+	char *old = Text->TextString;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Text))
+		return (NULL);
+	EraseText(Layer, Text);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	Text->TextString = NewName;
+
+	/* calculate size of the bounding box */
+	SetTextBoundingBox(&PCB->Font, Text);
+	r_insert_entry(Layer->text_tree, (BoxTypePtr) Text, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	DrawText(Layer, Text);
+	return (old);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of a layout; memory has to be already allocated
+ */
+pcb_bool ChangeLayoutName(char *Name)
+{
+	free(PCB->Name);
+	PCB->Name = Name;
+	if (gui != NULL)
+		hid_action("PCBChanged");
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the side of the board an element is on
+ * returns TRUE if done
+ */
+pcb_bool ChangeElementSide(ElementTypePtr Element, Coord yoff)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (pcb_false);
+	EraseElement(Element);
+	AddObjectToMirrorUndoList(PCB_TYPE_ELEMENT, Element, Element, Element, yoff);
+	MirrorElementCoordinates(PCB->Data, Element, yoff);
+	DrawElement(Element);
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of a layer; memory has to be already allocated
+ */
+pcb_bool ChangeLayerName(LayerTypePtr Layer, char *Name)
+{
+	free((char*)CURRENT->Name);
+	CURRENT->Name = Name;
+	hid_action("LayersChanged");
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance flag of a line
+ */
+static void *ChangeLineJoin(LayerTypePtr Layer, LineTypePtr Line)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Line))
+		return (NULL);
+	EraseLine(Line);
+	if (TEST_FLAG(PCB_FLAG_CLEARLINE, Line)) {
+		AddObjectToClearPolyUndoList(PCB_TYPE_LINE, Layer, Line, Line, pcb_false);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	}
+	AddObjectToFlagUndoList(PCB_TYPE_LINE, Layer, Line, Line);
+	TOGGLE_FLAG(PCB_FLAG_CLEARLINE, Line);
+	if (TEST_FLAG(PCB_FLAG_CLEARLINE, Line)) {
+		AddObjectToClearPolyUndoList(PCB_TYPE_LINE, Layer, Line, Line, pcb_true);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	}
+	DrawLine(Layer, Line);
+	return (Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the clearance flag of a line
+ */
+static void *SetLineJoin(LayerTypePtr Layer, LineTypePtr Line)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Line) || TEST_FLAG(PCB_FLAG_CLEARLINE, Line))
+		return (NULL);
+	return ChangeLineJoin(Layer, Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the clearance flag of a line
+ */
+static void *ClrLineJoin(LayerTypePtr Layer, LineTypePtr Line)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Line) || !TEST_FLAG(PCB_FLAG_CLEARLINE, Line))
+		return (NULL);
+	return ChangeLineJoin(Layer, Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance flag of an arc
+ */
+static void *ChangeArcJoin(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc))
+		return (NULL);
+	EraseArc(Arc);
+	if (TEST_FLAG(PCB_FLAG_CLEARLINE, Arc)) {
+		RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		AddObjectToClearPolyUndoList(PCB_TYPE_ARC, Layer, Arc, Arc, pcb_false);
+	}
+	AddObjectToFlagUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+	TOGGLE_FLAG(PCB_FLAG_CLEARLINE, Arc);
+	if (TEST_FLAG(PCB_FLAG_CLEARLINE, Arc)) {
+		ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+		AddObjectToClearPolyUndoList(PCB_TYPE_ARC, Layer, Arc, Arc, pcb_true);
+	}
+	DrawArc(Layer, Arc);
+	return (Arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the clearance flag of an arc
+ */
+static void *SetArcJoin(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc) || TEST_FLAG(PCB_FLAG_CLEARLINE, Arc))
+		return (NULL);
+	return ChangeArcJoin(Layer, Arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the clearance flag of an arc
+ */
+static void *ClrArcJoin(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc) || !TEST_FLAG(PCB_FLAG_CLEARLINE, Arc))
+		return (NULL);
+	return ChangeArcJoin(Layer, Arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance flag of a text
+ */
+static void *ChangeTextJoin(LayerTypePtr Layer, TextTypePtr Text)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Text))
+		return (NULL);
+	EraseText(Layer, Text);
+	if (TEST_FLAG(PCB_FLAG_CLEARLINE, Text)) {
+		AddObjectToClearPolyUndoList(PCB_TYPE_TEXT, Layer, Text, Text, pcb_false);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	}
+	AddObjectToFlagUndoList(PCB_TYPE_LINE, Layer, Text, Text);
+	TOGGLE_FLAG(PCB_FLAG_CLEARLINE, Text);
+	if (TEST_FLAG(PCB_FLAG_CLEARLINE, Text)) {
+		AddObjectToClearPolyUndoList(PCB_TYPE_TEXT, Layer, Text, Text, pcb_true);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	}
+	DrawText(Layer, Text);
+	return (Text);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the clearance flag of a text
+ */
+static void *SetTextJoin(LayerTypePtr Layer, TextTypePtr Text)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Text) || TEST_FLAG(PCB_FLAG_CLEARLINE, Text))
+		return (NULL);
+	return ChangeTextJoin(Layer, Text);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the clearance flag of a text
+ */
+static void *ClrTextJoin(LayerTypePtr Layer, TextTypePtr Text)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Text) || !TEST_FLAG(PCB_FLAG_CLEARLINE, Text))
+		return (NULL);
+	return ChangeTextJoin(Layer, Text);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the square flag of all pins on an element
+ */
+static void *ChangeElementSquare(ElementTypePtr Element)
+{
+	void *ans = NULL;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		ans = ChangePinSquare(Element, pin);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		ans = ChangePadSquare(Element, pad);
+	}
+	END_LOOP;
+	return (ans);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the square flag of all pins on an element
+ */
+static void *SetElementSquare(ElementTypePtr Element)
+{
+	void *ans = NULL;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		ans = SetPinSquare(Element, pin);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		ans = SetPadSquare(Element, pad);
+	}
+	END_LOOP;
+	return (ans);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the square flag of all pins on an element
+ */
+static void *ClrElementSquare(ElementTypePtr Element)
+{
+	void *ans = NULL;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		ans = ClrPinSquare(Element, pin);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		ans = ClrPadSquare(Element, pad);
+	}
+	END_LOOP;
+	return (ans);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the octagon flags of all pins of an element
+ */
+static void *ChangeElementOctagon(ElementTypePtr Element)
+{
+	void *result = NULL;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		ChangePinOctagon(Element, pin);
+		result = Element;
+	}
+	END_LOOP;
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the octagon flags of all pins of an element
+ */
+static void *SetElementOctagon(ElementTypePtr Element)
+{
+	void *result = NULL;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		SetPinOctagon(Element, pin);
+		result = Element;
+	}
+	END_LOOP;
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the octagon flags of all pins of an element
+ */
+static void *ClrElementOctagon(ElementTypePtr Element)
+{
+	void *result = NULL;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Element))
+		return (NULL);
+	PIN_LOOP(Element);
+	{
+		ClrPinOctagon(Element, pin);
+		result = Element;
+	}
+	END_LOOP;
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the square flag of a pad
+ */
+static void *ChangePadSquare(ElementTypePtr Element, PadTypePtr Pad)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pad))
+		return (NULL);
+	ErasePad(Pad);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PAD, Element, Pad, Pad, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_PAD, Element, Pad);
+	AddObjectToFlagUndoList(PCB_TYPE_PAD, Element, Pad, Pad);
+	TOGGLE_FLAG(PCB_FLAG_SQUARE, Pad);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PAD, Element, Pad, Pad, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_PAD, Element, Pad);
+	DrawPad(Pad);
+	return (Pad);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the square flag of a pad
+ */
+static void *SetPadSquare(ElementTypePtr Element, PadTypePtr Pad)
+{
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pad) || TEST_FLAG(PCB_FLAG_SQUARE, Pad))
+		return (NULL);
+
+	return (ChangePadSquare(Element, Pad));
+}
+
+
+/* ---------------------------------------------------------------------------
+ * clears the square flag of a pad
+ */
+static void *ClrPadSquare(ElementTypePtr Element, PadTypePtr Pad)
+{
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pad) || !TEST_FLAG(PCB_FLAG_SQUARE, Pad))
+		return (NULL);
+
+	return (ChangePadSquare(Element, Pad));
+}
+
+
+/* ---------------------------------------------------------------------------
+ * changes the square flag of a via
+ */
+static void *ChangeViaSquare(PinTypePtr Via)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via))
+		return (NULL);
+	EraseVia(Via);
+	AddObjectToClearPolyUndoList(PCB_TYPE_VIA, NULL, Via, Via, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, NULL, Via);
+	AddObjectToFlagUndoList(PCB_TYPE_VIA, NULL, Via, Via);
+	ASSIGN_SQUARE(Absolute, Via);
+	if (Absolute == 0)
+		CLEAR_FLAG(PCB_FLAG_SQUARE, Via);
+	else
+		SET_FLAG(PCB_FLAG_SQUARE, Via);
+	SetPinBoundingBox(Via);
+	AddObjectToClearPolyUndoList(PCB_TYPE_VIA, NULL, Via, Via, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, NULL, Via);
+	DrawVia(Via);
+	return (Via);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the square flag of a pin
+ */
+static void *ChangePinSquare(ElementTypePtr Element, PinTypePtr Pin)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin))
+		return (NULL);
+	ErasePin(Pin);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PIN, Element, Pin, Pin, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+	AddObjectToFlagUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+	ASSIGN_SQUARE(Absolute, Pin);
+	if (Absolute == 0)
+		CLEAR_FLAG(PCB_FLAG_SQUARE, Pin);
+	else
+		SET_FLAG(PCB_FLAG_SQUARE, Pin);
+	SetPinBoundingBox(Pin);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PIN, Element, Pin, Pin, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+	DrawPin(Pin);
+	return (Pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the square flag of a pin
+ */
+static void *SetPinSquare(ElementTypePtr Element, PinTypePtr Pin)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin) || TEST_FLAG(PCB_FLAG_SQUARE, Pin))
+		return (NULL);
+
+	return (ChangePinSquare(Element, Pin));
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the square flag of a pin
+ */
+static void *ClrPinSquare(ElementTypePtr Element, PinTypePtr Pin)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin) || !TEST_FLAG(PCB_FLAG_SQUARE, Pin))
+		return (NULL);
+
+	return (ChangePinSquare(Element, Pin));
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the octagon flag of a via
+ */
+static void *ChangeViaOctagon(PinTypePtr Via)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via))
+		return (NULL);
+	EraseVia(Via);
+	AddObjectToClearPolyUndoList(PCB_TYPE_VIA, Via, Via, Via, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	AddObjectToFlagUndoList(PCB_TYPE_VIA, Via, Via, Via);
+	TOGGLE_FLAG(PCB_FLAG_OCTAGON, Via);
+	AddObjectToClearPolyUndoList(PCB_TYPE_VIA, Via, Via, Via, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	DrawVia(Via);
+	return (Via);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the octagon flag of a via
+ */
+static void *SetViaOctagon(PinTypePtr Via)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via) || TEST_FLAG(PCB_FLAG_OCTAGON, Via))
+		return (NULL);
+
+	return (ChangeViaOctagon(Via));
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the octagon flag of a via
+ */
+static void *ClrViaOctagon(PinTypePtr Via)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via) || !TEST_FLAG(PCB_FLAG_OCTAGON, Via))
+		return (NULL);
+
+	return (ChangeViaOctagon(Via));
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the octagon flag of a pin
+ */
+static void *ChangePinOctagon(ElementTypePtr Element, PinTypePtr Pin)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin))
+		return (NULL);
+	ErasePin(Pin);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PIN, Element, Pin, Pin, pcb_false);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+	AddObjectToFlagUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+	TOGGLE_FLAG(PCB_FLAG_OCTAGON, Pin);
+	AddObjectToClearPolyUndoList(PCB_TYPE_PIN, Element, Pin, Pin, pcb_true);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_PIN, Element, Pin);
+	DrawPin(Pin);
+	return (Pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the octagon flag of a pin
+ */
+static void *SetPinOctagon(ElementTypePtr Element, PinTypePtr Pin)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin) || TEST_FLAG(PCB_FLAG_OCTAGON, Pin))
+		return (NULL);
+
+	return (ChangePinOctagon(Element, Pin));
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the octagon flag of a pin
+ */
+static void *ClrPinOctagon(ElementTypePtr Element, PinTypePtr Pin)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pin) || !TEST_FLAG(PCB_FLAG_OCTAGON, Pin))
+		return (NULL);
+
+	return (ChangePinOctagon(Element, Pin));
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the hole flag of a via
+ */
+pcb_bool ChangeHole(PinTypePtr Via)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Via))
+		return (pcb_false);
+	EraseVia(Via);
+	AddObjectToFlagUndoList(PCB_TYPE_VIA, Via, Via, Via);
+	AddObjectToMaskSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+	r_delete_entry(PCB->Data->via_tree, (BoxType *) Via);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	TOGGLE_FLAG(PCB_FLAG_HOLE, Via);
+
+	if (TEST_FLAG(PCB_FLAG_HOLE, Via)) {
+		/* A tented via becomes an minimally untented hole.  An untented
+		   via retains its mask clearance.  */
+		if (Via->Mask > Via->Thickness) {
+			Via->Mask = (Via->DrillingHole + (Via->Mask - Via->Thickness));
+		}
+		else if (Via->Mask < Via->DrillingHole) {
+			Via->Mask = Via->DrillingHole + 2 * MASKFRAME;
+		}
+	}
+	else {
+		Via->Mask = (Via->Thickness + (Via->Mask - Via->DrillingHole));
+	}
+
+	SetPinBoundingBox(Via);
+	r_insert_entry(PCB->Data->via_tree, (BoxType *) Via, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	DrawVia(Via);
+	Draw();
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the nopaste flag of a pad
+ */
+pcb_bool ChangePaste(PadTypePtr Pad)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Pad))
+		return (pcb_false);
+	ErasePad(Pad);
+	AddObjectToFlagUndoList(PCB_TYPE_PAD, Pad, Pad, Pad);
+	TOGGLE_FLAG(PCB_FLAG_NOPASTE, Pad);
+	DrawPad(Pad);
+	Draw();
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the CLEARPOLY flag of a polygon
+ */
+static void *ChangePolyClear(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, Polygon))
+		return (NULL);
+	AddObjectToClearPolyUndoList(PCB_TYPE_POLYGON, Layer, Polygon, Polygon, pcb_true);
+	AddObjectToFlagUndoList(PCB_TYPE_POLYGON, Layer, Polygon, Polygon);
+	TOGGLE_FLAG(PCB_FLAG_CLEARPOLY, Polygon);
+	InitClip(PCB->Data, Layer, Polygon);
+	DrawPolygon(Layer, Polygon);
+	return (Polygon);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the side of all selected and visible elements
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedElementSide(void)
+{
+	pcb_bool change = pcb_false;
+
+	/* setup identifiers */
+	if (PCB->PinOn && PCB->ElementOn)
+		ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, element)) {
+			change |= ChangeElementSide(element, 0);
+		}
+	}
+	END_LOOP;
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the thermals on all selected and visible pins
+ * and/or vias. Returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedThermals(int types, int therm_style)
+{
+	pcb_bool change = pcb_false;
+
+	Delta = therm_style;
+	change = SelectedOperation(&ChangeThermalFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the size of all selected and visible object types
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedSize(int types, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change = pcb_false;
+
+	/* setup identifiers */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+
+	change = SelectedOperation(&ChangeSizeFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the clearance size of all selected and visible objects
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedClearSize(int types, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change = pcb_false;
+
+	/* setup identifiers */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	if (conf_core.editor.show_mask)
+		change = SelectedOperation(&ChangeMaskSizeFunctions, pcb_false, types);
+	else
+		change = SelectedOperation(&ChangeClearSizeFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* --------------------------------------------------------------------------
+ * changes the 2nd size (drilling hole) of all selected and visible objects
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelected2ndSize(int types, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change = pcb_false;
+
+	/* setup identifiers */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	change = SelectedOperation(&Change2ndSizeFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the clearance flag (join) of all selected and visible lines
+ * and/or arcs. Returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedJoin(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ChangeJoinFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the clearance flag (join) of all selected and visible lines
+ * and/or arcs. Returns pcb_true if anything has changed
+ */
+pcb_bool SetSelectedJoin(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&SetJoinFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the clearance flag (join) of all selected and visible lines
+ * and/or arcs. Returns pcb_true if anything has changed
+ */
+pcb_bool ClrSelectedJoin(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ClrJoinFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the nonetlist-flag of all selected and visible elements
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedNonetlist(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ChangeNonetlistFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+#if 0
+/* ----------------------------------------------------------------------
+ * sets the square-flag of all selected and visible pins or pads
+ * returns pcb_true if anything has changed
+ */
+pcb_bool SetSelectedNonetlist(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&SetNonetlistFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * clears the square-flag of all selected and visible pins or pads
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ClrSelectedNonetlist(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ClrNonetlistFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+#endif
+
+/* ----------------------------------------------------------------------
+ * changes the square-flag of all selected and visible pins or pads
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedSquare(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ChangeSquareFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the angle of all selected and visible object types
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedAngle(int types, int is_start, Angle Difference, pcb_bool fixIt)
+{
+	pcb_bool change = pcb_false;
+
+	/* setup identifiers */
+	AAbsolute = (fixIt) ? Difference : 0;
+	ADelta = Difference;
+	is_primary = is_start;
+
+	change = SelectedOperation(&ChangeAngleFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the radius of all selected and visible object types
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedRadius(int types, int is_start, Angle Difference, pcb_bool fixIt)
+{
+	pcb_bool change = pcb_false;
+
+	/* setup identifiers */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	is_primary = is_start;
+
+	change = SelectedOperation(&ChangeRadiusFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+
+/* ----------------------------------------------------------------------
+ * sets the square-flag of all selected and visible pins or pads
+ * returns pcb_true if anything has changed
+ */
+pcb_bool SetSelectedSquare(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&SetSquareFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * clears the square-flag of all selected and visible pins or pads
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ClrSelectedSquare(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ClrSquareFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the octagon-flag of all selected and visible pins and vias
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedOctagon(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ChangeOctagonFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * sets the octagon-flag of all selected and visible pins and vias
+ * returns pcb_true if anything has changed
+ */
+pcb_bool SetSelectedOctagon(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&SetOctagonFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * clears the octagon-flag of all selected and visible pins and vias
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ClrSelectedOctagon(int types)
+{
+	pcb_bool change = pcb_false;
+
+	change = SelectedOperation(&ClrOctagonFunctions, pcb_false, types);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the hole-flag of all selected and visible vias
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedHole(void)
+{
+	pcb_bool change = pcb_false;
+
+	if (PCB->ViaOn)
+		VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, via))
+			change |= ChangeHole(via);
+	}
+	END_LOOP;
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ----------------------------------------------------------------------
+ * changes the no paste-flag of all selected and visible pads
+ * returns pcb_true if anything has changed
+ */
+pcb_bool ChangeSelectedPaste(void)
+{
+	pcb_bool change = pcb_false;
+
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, pad))
+			change |= ChangePaste(pad);
+	}
+	ENDALL_LOOP;
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * changes the size of the passed object; element size is silk size
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectSize(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	change = (ObjectOperation(&ChangeSizeFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the size of the passed object; element size is pin ring sizes
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObject1stSize(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	change = (ObjectOperation(&Change1stSizeFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the radius of the passed object (e.g. arc width/height)
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectRadius(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int is_x, Coord r, pcb_bool fixIt)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	Absolute = (fixIt) ? r : 0;
+	Delta = r;
+	is_primary = is_x;
+	change = (ObjectOperation(&ChangeRadiusFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the angles of the passed object (e.g. arc start/delta)
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectAngle(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int is_start, Angle a, pcb_bool fixIt)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	AAbsolute = (fixIt) ? a : 0;
+	ADelta = a;
+	is_primary = is_start;
+	change = (ObjectOperation(&ChangeAngleFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance size of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectClearSize(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	if (conf_core.editor.show_mask)
+		change = (ObjectOperation(&ChangeMaskSizeFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	else
+		change = (ObjectOperation(&ChangeClearSizeFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the thermal of the passed object
+ * Returns pcb_true if anything is changed
+ *
+ */
+pcb_bool ChangeObjectThermal(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int therm_type)
+{
+	pcb_bool change;
+
+	Delta = Absolute = therm_type;
+	change = (ObjectOperation(&ChangeThermalFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the 2nd size of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObject2ndSize(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord Difference, pcb_bool fixIt, pcb_bool incundo)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	change = (ObjectOperation(&Change2ndSizeFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		if (incundo)
+			IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the mask size of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectMaskSize(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord Difference, pcb_bool fixIt)
+{
+	pcb_bool change;
+
+	/* setup identifier */
+	Absolute = (fixIt) ? Difference : 0;
+	Delta = Difference;
+	change = (ObjectOperation(&ChangeMaskSizeFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL);
+	if (change) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (change);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the name of the passed object
+ * returns the old name
+ *
+ * The allocated memory isn't freed because the old string is used
+ * by the undo module.
+ */
+void *ChangeObjectName(int Type, void *Ptr1, void *Ptr2, void *Ptr3, char *Name)
+{
+	void *result;
+	/* setup identifier */
+	NewName = Name;
+	result = ObjectOperation(&ChangeNameFunctions, Type, Ptr1, Ptr2, Ptr3);
+	Draw();
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the pin number of the passed object
+ * returns the old name
+ *
+ * The allocated memory isn't freed because the old string is used
+ * by the undo module.
+ */
+void *ChangeObjectPinnum(int Type, void *Ptr1, void *Ptr2, void *Ptr3, char *Name)
+{
+	void *result;
+	/* setup identifier */
+	NewName = Name;
+	result = ObjectOperation(&ChangePinnumFunctions, Type, Ptr1, Ptr2, Ptr3);
+	Draw();
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the clearance-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectJoin(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&ChangeJoinFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the clearance-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool SetObjectJoin(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&SetJoinFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the clearance-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ClrObjectJoin(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&ClrJoinFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the square-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectNonetlist(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&ChangeNonetlistFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the square-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectSquare(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int style)
+{
+	Absolute = style;
+	if (ObjectOperation(&ChangeSquareFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the square-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool SetObjectSquare(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&SetSquareFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the square-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ClrObjectSquare(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&ClrSquareFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the octagon-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ChangeObjectOctagon(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&ChangeOctagonFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the octagon-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool SetObjectOctagon(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&SetOctagonFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * clears the octagon-flag of the passed object
+ * Returns pcb_true if anything is changed
+ */
+pcb_bool ClrObjectOctagon(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (ObjectOperation(&ClrOctagonFunctions, Type, Ptr1, Ptr2, Ptr3) != NULL) {
+		Draw();
+		IncrementUndoSerialNumber();
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * queries the user for a new object name and changes it
+ *
+ * The allocated memory isn't freed because the old string is used
+ * by the undo module.
+ */
+void *QueryInputAndChangeObjectName(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int pinnum)
+{
+	char *name = NULL;
+	char msg[513];
+
+	/* if passed an element name, make it an element reference instead */
+	if (Type == PCB_TYPE_ELEMENT_NAME) {
+		Type = PCB_TYPE_ELEMENT;
+		Ptr2 = Ptr1;
+		Ptr3 = Ptr1;
+	}
+	switch (Type) {
+	case PCB_TYPE_LINE:
+		name = gui->prompt_for(_("Linename:"), EMPTY(((LineTypePtr) Ptr2)->Number));
+		break;
+
+	case PCB_TYPE_VIA:
+		name = gui->prompt_for(_("Vianame:"), EMPTY(((PinTypePtr) Ptr2)->Name));
+		break;
+
+	case PCB_TYPE_PIN:
+		if (pinnum)
+			sprintf(msg, _("%s Pin Number:"), EMPTY(((PinTypePtr) Ptr2)->Number));
+		else
+			sprintf(msg, _("%s Pin Name:"), EMPTY(((PinTypePtr) Ptr2)->Number));
+		name = gui->prompt_for(msg, EMPTY(((PinTypePtr) Ptr2)->Name));
+		break;
+
+	case PCB_TYPE_PAD:
+		if (pinnum)
+			sprintf(msg, _("%s Pad Number:"), EMPTY(((PadTypePtr) Ptr2)->Number));
+		else
+			sprintf(msg, _("%s Pad Name:"), EMPTY(((PadTypePtr) Ptr2)->Number));
+		name = gui->prompt_for(msg, EMPTY(((PadTypePtr) Ptr2)->Name));
+		break;
+
+	case PCB_TYPE_TEXT:
+		name = gui->prompt_for(_("Enter text:"), EMPTY(((TextTypePtr) Ptr2)->TextString));
+		break;
+
+	case PCB_TYPE_ELEMENT:
+		name = gui->prompt_for(_("Elementname:"), EMPTY(ELEMENT_NAME(PCB, (ElementTypePtr) Ptr2)));
+		break;
+	}
+	if (name) {
+		/* NB: ChangeObjectName takes ownership of the passed memory */
+		char *old;
+		if (pinnum)
+			old = (char *) ChangeObjectPinnum(Type, Ptr1, Ptr2, Ptr3, name);
+		else
+			old = (char *) ChangeObjectName(Type, Ptr1, Ptr2, Ptr3, name);
+
+		if (old != (char *) -1) {
+			if (pinnum)
+				AddObjectToChangePinnumUndoList(Type, Ptr1, Ptr2, Ptr3, old);
+			else
+				AddObjectToChangeNameUndoList(Type, Ptr1, Ptr2, Ptr3, old);
+			IncrementUndoSerialNumber();
+		}
+		Draw();
+		return (Ptr3);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the maximum size of a layout
+ * adjusts the scrollbars
+ * releases the saved pixmap if necessary
+ * and adjusts the cursor confinement box
+ */
+void ChangePCBSize(Coord Width, Coord Height)
+{
+	PCB->MaxWidth = Width;
+	PCB->MaxHeight = Height;
+
+	/* crosshair range is different if pastebuffer-mode
+	 * is enabled
+	 */
+	if (conf_core.editor.mode == PCB_MODE_PASTE_BUFFER)
+		SetCrosshairRange(PASTEBUFFER->X - PASTEBUFFER->BoundingBox.X1,
+											PASTEBUFFER->Y - PASTEBUFFER->BoundingBox.Y1,
+											MAX(0,
+													Width - (PASTEBUFFER->BoundingBox.X2 -
+																	 PASTEBUFFER->X)), MAX(0, Height - (PASTEBUFFER->BoundingBox.Y2 - PASTEBUFFER->Y)));
+	else
+		SetCrosshairRange(0, 0, Width, Height);
+
+	if (gui != NULL)
+		hid_action("PCBChanged");
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the mask size of a pad
+ * returns TRUE if changed
+ */
+static void *ChangePadMaskSize(ElementTypePtr Element, PadTypePtr Pad)
+{
+	Coord value = (Absolute) ? Absolute : Pad->Mask + Delta;
+
+	value = MAX(value, 0);
+	if (value == Pad->Mask && Absolute == 0)
+		value = Pad->Thickness;
+	if (value != Pad->Mask) {
+		AddObjectToMaskSizeUndoList(PCB_TYPE_PAD, Element, Pad, Pad);
+		ErasePad(Pad);
+		r_delete_entry(PCB->Data->pad_tree, &Pad->BoundingBox);
+		Pad->Mask = value;
+		SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+		DrawPad(Pad);
+		return (Pad);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the mask size of a pin
+ * returns TRUE if changed
+ */
+static void *ChangePinMaskSize(ElementTypePtr Element, PinTypePtr Pin)
+{
+	Coord value = (Absolute) ? Absolute : Pin->Mask + Delta;
+
+	value = MAX(value, 0);
+	if (value == Pin->Mask && Absolute == 0)
+		value = Pin->Thickness;
+	if (value != Pin->Mask) {
+		AddObjectToMaskSizeUndoList(PCB_TYPE_PIN, Element, Pin, Pin);
+		ErasePin(Pin);
+		r_delete_entry(PCB->Data->pin_tree, &Pin->BoundingBox);
+		Pin->Mask = value;
+		SetElementBoundingBox(PCB->Data, Element, &PCB->Font);
+		DrawPin(Pin);
+		return (Pin);
+	}
+	return (NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * changes the mask size of a via
+ * returns TRUE if changed
+ */
+static void *ChangeViaMaskSize(PinTypePtr Via)
+{
+	Coord value;
+
+	value = (Absolute) ? Absolute : Via->Mask + Delta;
+	value = MAX(value, 0);
+	if (value != Via->Mask) {
+		AddObjectToMaskSizeUndoList(PCB_TYPE_VIA, Via, Via, Via);
+		EraseVia(Via);
+		r_delete_entry(PCB->Data->via_tree, &Via->BoundingBox);
+		Via->Mask = value;
+		SetPinBoundingBox(Via);
+		r_insert_entry(PCB->Data->via_tree, &Via->BoundingBox, 0);
+		DrawVia(Via);
+		return (Via);
+	}
+	return (NULL);
+}
diff --git a/src/change.h b/src/change.h
new file mode 100644
index 0000000..bd65be1
--- /dev/null
+++ b/src/change.h
@@ -0,0 +1,122 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes to change object properties */
+
+#ifndef	PCB_CHANGE_H
+#define	PCB_CHANGE_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some defines
+ */
+#define	CHANGENAME_TYPES        \
+	(PCB_TYPE_VIA | PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_TEXT | PCB_TYPE_ELEMENT | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_LINE)
+
+#define	CHANGESIZE_TYPES        \
+	(PCB_TYPE_POLYGON | PCB_TYPE_VIA | PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_LINE | \
+	 PCB_TYPE_ARC | PCB_TYPE_TEXT | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_ELEMENT)
+
+#define	CHANGE2NDSIZE_TYPES     \
+	(PCB_TYPE_VIA | PCB_TYPE_PIN | PCB_TYPE_ELEMENT)
+
+/* We include polygons here only to inform the user not to do it that way.  */
+#define CHANGECLEARSIZE_TYPES	\
+	(PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_ARC | PCB_TYPE_POLYGON)
+
+#define	CHANGENONETLIST_TYPES     \
+	(PCB_TYPE_ELEMENT)
+
+#define	CHANGESQUARE_TYPES     \
+	(PCB_TYPE_ELEMENT | PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_VIA)
+
+#define	CHANGEOCTAGON_TYPES     \
+	(PCB_TYPE_ELEMENT | PCB_TYPE_PIN | PCB_TYPE_VIA)
+
+#define CHANGEJOIN_TYPES	\
+	(PCB_TYPE_ARC | PCB_TYPE_LINE | PCB_TYPE_TEXT)
+
+#define CHANGETHERMAL_TYPES	\
+	(PCB_TYPE_PIN | PCB_TYPE_VIA)
+
+#define CHANGEMASKSIZE_TYPES    \
+        (PCB_TYPE_PIN | PCB_TYPE_VIA | PCB_TYPE_PAD)
+
+pcb_bool ChangeLayoutName(char *);
+pcb_bool ChangeLayerName(LayerTypePtr, char *);
+pcb_bool ChangeSelectedSize(int, Coord, pcb_bool);
+pcb_bool ChangeSelectedClearSize(int, Coord, pcb_bool);
+pcb_bool ChangeSelected2ndSize(int, Coord, pcb_bool);
+pcb_bool ChangeSelectedMaskSize(int, Coord, pcb_bool);
+pcb_bool ChangeSelectedJoin(int);
+pcb_bool SetSelectedJoin(int);
+pcb_bool ClrSelectedJoin(int);
+pcb_bool ChangeSelectedNonetlist(int);
+pcb_bool ChangeSelectedSquare(int);
+pcb_bool SetSelectedSquare(int);
+pcb_bool ClrSelectedSquare(int);
+pcb_bool ChangeSelectedThermals(int, int);
+pcb_bool ChangeSelectedHole(void);
+pcb_bool ChangeSelectedPaste(void);
+pcb_bool ChangeSelectedOctagon(int);
+pcb_bool SetSelectedOctagon(int);
+pcb_bool ClrSelectedOctagon(int);
+pcb_bool ChangeSelectedElementSide(void);
+pcb_bool ChangeElementSide(ElementTypePtr, Coord);
+pcb_bool ChangeHole(PinTypePtr);
+pcb_bool ChangePaste(PadTypePtr);
+pcb_bool ChangeObjectSize(int, void *, void *, void *, Coord, pcb_bool);
+pcb_bool ChangeObject1stSize(int, void *, void *, void *, Coord, pcb_bool);
+pcb_bool ChangeObjectThermal(int, void *, void *, void *, int);
+pcb_bool ChangeObjectClearSize(int, void *, void *, void *, Coord, pcb_bool);
+pcb_bool ChangeObject2ndSize(int, void *, void *, void *, Coord, pcb_bool, pcb_bool);
+pcb_bool ChangeObjectMaskSize(int, void *, void *, void *, Coord, pcb_bool);
+pcb_bool ChangeObjectJoin(int, void *, void *, void *);
+pcb_bool SetObjectJoin(int, void *, void *, void *);
+pcb_bool ClrObjectJoin(int, void *, void *, void *);
+pcb_bool ChangeObjectNonetlist(int Type, void *Ptr1, void *Ptr2, void *Ptr3);
+pcb_bool ChangeObjectSquare(int, void *, void *, void *, int);
+pcb_bool SetObjectSquare(int, void *, void *, void *);
+pcb_bool ClrObjectSquare(int, void *, void *, void *);
+pcb_bool ChangeObjectOctagon(int, void *, void *, void *);
+pcb_bool SetObjectOctagon(int, void *, void *, void *);
+pcb_bool ClrObjectOctagon(int, void *, void *, void *);
+void *ChangeObjectName(int, void *, void *, void *, char *);
+void *QueryInputAndChangeObjectName(int, void *, void *, void *, int);
+void ChangePCBSize(Coord, Coord);
+void *ChangeObjectPinnum(int Type, void *Ptr1, void *Ptr2, void *Ptr3, char *Name);
+pcb_bool ChangeObjectRadius(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int is_x, Coord r, pcb_bool absolute);
+pcb_bool ChangeObjectAngle(int Type, void *Ptr1, void *Ptr2, void *Ptr3, int is_start, Angle a, pcb_bool absolute);
+pcb_bool ChangeSelectedAngle(int types, int is_start, Angle Difference, pcb_bool fixIt);
+pcb_bool ChangeSelectedRadius(int types, int is_start, Angle Difference, pcb_bool fixIt);
+
+/* Change the specified text on an element, either on the board (give
+   PCB, PCB->Data) or in a buffer (give NULL, Buffer->Data).  The old
+   string is returned, and must be properly freed by the caller.  */
+char *ChangeElementText(PCBType * pcb, DataType * data, ElementTypePtr Element, int which, char *new_name);
+
+#endif
diff --git a/src/change_act.c b/src/change_act.c
new file mode 100644
index 0000000..f80df39
--- /dev/null
+++ b/src/change_act.c
@@ -0,0 +1,1582 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* action routines for output window
+ */
+
+#include <assert.h>
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "funchash_core.h"
+
+#include "action_helper.h"
+#include "hid_actions.h"
+#include "change.h"
+#include "draw.h"
+#include "search.h"
+#include "misc.h"
+#include "set.h"
+#include "error.h"
+#include "undo.h"
+#include "rubberband.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+static void ChangeFlag(const char *, const char *, int, const char *);
+static int ActionChangeSize(int argc, const char **argv, Coord x, Coord y);
+static int ActionChange2ndSize(int argc, const char **argv, Coord x, Coord y);
+
+/* --------------------------------------------------------------------------- */
+
+static const char changeclearsize_syntax[] =
+	"ChangeClearSize(Object, delta|style)\n"
+	"ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta|style)\n"
+	"ChangeClearSize(SelectedLines|SelectedArcs, delta|style)\n" "ChangeClearSize(Selected|SelectedObjects, delta|style)";
+
+static const char changeclearsize_help[] = "Changes the clearance size of objects.";
+
+/* %start-doc actions ChangeClearSize
+
+If the solder mask is currently showing, this action changes the
+solder mask clearance.  If the mask is not showing, this action
+changes the polygon clearance.
+
+%end-doc */
+
+static int ActionChangeClearSize(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *delta = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	pcb_bool absolute;
+	Coord value;
+	int type = PCB_TYPE_NONE;
+	void *ptr1, *ptr2, *ptr3;
+
+	if (function && delta) {
+		int funcid = funchash_get(function, NULL);
+
+		if (funcid == F_Object) {
+			gui->get_coords(_("Select an Object"), &x, &y);
+			type = SearchScreen(x, y, CHANGECLEARSIZE_TYPES, &ptr1, &ptr2, &ptr3);
+		}
+
+		if (strcmp(argv[1], "style") == 0) {
+			if ((type == PCB_TYPE_NONE) || (type == PCB_TYPE_POLYGON))	/* workaround: SearchScreen(CHANGECLEARSIZE_TYPES) wouldn't return elements */
+				type = SearchScreen(x, y, CHANGE2NDSIZE_TYPES, &ptr1, &ptr2, &ptr3);
+			if (get_style_size(funcid, &value, type, 2) != 0)
+				return 1;
+			absolute = 1;
+			value *= 2;
+		}
+		else
+			value = 2 * GetValue(delta, units, &absolute, NULL);
+		switch (funchash_get(function, NULL)) {
+		case F_Object:
+			{
+				if (type != PCB_TYPE_NONE)
+					if (ChangeObjectClearSize(type, ptr1, ptr2, ptr3, value, absolute))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+		case F_SelectedVias:
+			if (ChangeSelectedClearSize(PCB_TYPE_VIA, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_SelectedPads:
+			if (ChangeSelectedClearSize(PCB_TYPE_PAD, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_SelectedPins:
+			if (ChangeSelectedClearSize(PCB_TYPE_PIN, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_SelectedLines:
+			if (ChangeSelectedClearSize(PCB_TYPE_LINE, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_SelectedArcs:
+			if (ChangeSelectedClearSize(PCB_TYPE_ARC, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedClearSize(CHANGECLEARSIZE_TYPES, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changeflag_syntax[] =
+	"ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
+	"ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
+	"ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
+	"ChangeFlag(SelectedElements, flag, value)\n" "flag = square | octagon | thermal | join\n" "value = 0 | 1";
+
+static const char changeflag_help[] = "Sets or clears flags on objects.";
+
+/* %start-doc actions ChangeFlag
+
+Toggles the given flag on the indicated object(s).  The flag may be
+one of the flags listed above (square, octagon, thermal, join).  The
+value may be the number 0 or 1.  If the value is 0, the flag is
+cleared.  If the value is 1, the flag is set.
+
+%end-doc */
+
+static int ActionChangeFlag(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *flag = ACTION_ARG(1);
+	int value = argc > 2 ? atoi(argv[2]) : -1;
+	if (value != 0 && value != 1)
+		AFAIL(changeflag);
+
+	ChangeFlag(function, flag, value, "ChangeFlag");
+	return 0;
+}
+
+
+static void ChangeFlag(const char *what, const char *flag_name, int value,
+											 const char *cmd_name)
+{
+	pcb_bool(*set_object) (int, void *, void *, void *);
+	pcb_bool(*set_selected) (int);
+
+	if (NSTRCMP(flag_name, "square") == 0) {
+		set_object = value ? SetObjectSquare : ClrObjectSquare;
+		set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
+	}
+	else if (NSTRCMP(flag_name, "octagon") == 0) {
+		set_object = value ? SetObjectOctagon : ClrObjectOctagon;
+		set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
+	}
+	else if (NSTRCMP(flag_name, "join") == 0) {
+		/* Note: these are backwards, because the flag is "clear" but
+		   the command is "join".  */
+		set_object = value ? ClrObjectJoin : SetObjectJoin;
+		set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
+	}
+	else {
+		Message(PCB_MSG_DEFAULT, _("%s():  Flag \"%s\" is not valid\n"), cmd_name, flag_name);
+		return;
+	}
+
+	switch (funchash_get(what, NULL)) {
+	case F_Object:
+		{
+			int type;
+			void *ptr1, *ptr2, *ptr3;
+
+			if ((type = SearchScreen(Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+				if (TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr) ptr2))
+					Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+			if (set_object(type, ptr1, ptr2, ptr3))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+
+	case F_SelectedVias:
+		if (set_selected(PCB_TYPE_VIA))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_SelectedPins:
+		if (set_selected(PCB_TYPE_PIN))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_SelectedPads:
+		if (set_selected(PCB_TYPE_PAD))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_SelectedLines:
+		if (set_selected(PCB_TYPE_LINE))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_SelectedTexts:
+		if (set_selected(PCB_TYPE_TEXT))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_SelectedNames:
+		if (set_selected(PCB_TYPE_ELEMENT_NAME))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_SelectedElements:
+		if (set_selected(PCB_TYPE_ELEMENT))
+			SetChangedFlag(pcb_true);
+		break;
+
+	case F_Selected:
+	case F_SelectedObjects:
+		if (set_selected(CHANGESIZE_TYPES))
+			SetChangedFlag(pcb_true);
+		break;
+	}
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changehold_syntax[] = "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
+
+static const char changehold_help[] = "Changes the hole flag of objects.";
+
+/* %start-doc actions ChangeHole
+
+The "hole flag" of a via determines whether the via is a
+plated-through hole (not set), or an unplated hole (set).
+
+%end-doc */
+
+static int ActionChangeHole(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, PCB_TYPE_VIA, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE && ChangeHole((PinTypePtr) ptr3))
+					IncrementUndoSerialNumber();
+				break;
+			}
+
+		case F_SelectedVias:
+		case F_Selected:
+			if (ChangeSelectedHole())
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changepaste_syntax[] = "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
+
+static const char changepaste_help[] = "Changes the no paste flag of objects.";
+
+/* %start-doc actions ChangePaste
+
+The "no paste flag" of a pad determines whether the solderpaste
+ stencil will have an opening for the pad (no set) or if there will be
+ no solderpaste on the pad (set).  This is used for things such as
+ fiducial pads.
+
+%end-doc */
+
+static int ActionChangePaste(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, PCB_TYPE_PAD, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE && ChangePaste((PadTypePtr) ptr3))
+					IncrementUndoSerialNumber();
+				break;
+			}
+
+		case F_SelectedPads:
+		case F_Selected:
+			if (ChangeSelectedPaste())
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+static const char changesizes_syntax[] =
+	"ChangeSizes(Object, delta|style)\n"
+	"ChangeSizes(SelectedObjects|Selected, delta|style)\n"
+	"ChangeSizes(SelectedLines|SelectedPins|SelectedVias, delta|style)\n"
+	"ChangeSizes(SelectedPads|SelectedTexts|SelectedNames, delta|style)\n" "ChangeSizes(SelectedElements, delta|style)";
+
+static const char changesizes_help[] = "Changes all sizes of objects.";
+
+/* %start-doc actions ChangeSize
+
+Call ActionChangeSize, ActionChangeDrillSize and ActionChangeClearSize
+with the same arguments. If any of them did not fail, return success.
+%end-doc */
+
+static int ActionChangeSizes(int argc, const char **argv, Coord x, Coord y)
+{
+	int a, b, c;
+	SaveUndoSerialNumber();
+	a = ActionChangeSize(argc, argv, x, y);
+	RestoreUndoSerialNumber();
+	b = ActionChange2ndSize(argc, argv, x, y);
+	RestoreUndoSerialNumber();
+	c = ActionChangeClearSize(argc, argv, x, y);
+	RestoreUndoSerialNumber();
+	IncrementUndoSerialNumber();
+	return !(!a || !b || !c);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changesize_syntax[] =
+	"ChangeSize(Object, delta|style)\n"
+	"ChangeSize(SelectedObjects|Selected, delta|style)\n"
+	"ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta|style)\n"
+	"ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta|style)\n" "ChangeSize(SelectedElements, delta|style)";
+
+static const char changesize_help[] = "Changes the size of objects.";
+
+/* %start-doc actions ChangeSize
+
+For lines and arcs, this changes the width.  For pins and vias, this
+changes the overall diameter of the copper annulus.  For pads, this
+changes the width and, indirectly, the length.  For texts and names,
+this changes the scaling factor.  For elements, this changes the width
+of the silk layer lines and arcs for this element.
+
+%end-doc */
+
+static int ActionChangeSize(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *delta = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	pcb_bool absolute;								/* indicates if absolute size is given */
+	Coord value;
+	int type = PCB_TYPE_NONE, tostyle = 0;
+	void *ptr1, *ptr2, *ptr3;
+
+
+	if (function && delta) {
+		int funcid = funchash_get(function, NULL);
+
+		if (funcid == F_Object)
+			type = SearchScreen(Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES, &ptr1, &ptr2, &ptr3);
+
+		if (strcmp(argv[1], "style") == 0) {
+			if (get_style_size(funcid, &value, type, 0) != 0)
+				return 1;
+			absolute = 1;
+			tostyle = 1;
+		}
+		else
+			value = GetValue(delta, units, &absolute, NULL);
+		switch (funcid) {
+		case F_Object:
+			{
+				if (type != PCB_TYPE_NONE)
+					if (TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr) ptr2))
+						Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+				if (tostyle) {
+					if (ChangeObject1stSize(type, ptr1, ptr2, ptr3, value, absolute))
+						SetChangedFlag(pcb_true);
+				}
+				else {
+					if (ChangeObjectSize(type, ptr1, ptr2, ptr3, value, absolute))
+						SetChangedFlag(pcb_true);
+				}
+				break;
+			}
+		case F_SelectedVias:
+			if (ChangeSelectedSize(PCB_TYPE_VIA, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (ChangeSelectedSize(PCB_TYPE_PIN, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPads:
+			if (ChangeSelectedSize(PCB_TYPE_PAD, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedArcs:
+			if (ChangeSelectedSize(PCB_TYPE_ARC, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedLines:
+			if (ChangeSelectedSize(PCB_TYPE_LINE, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedTexts:
+			if (ChangeSelectedSize(PCB_TYPE_TEXT, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedNames:
+			if (ChangeSelectedSize(PCB_TYPE_ELEMENT_NAME, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedElements:
+			if (ChangeSelectedSize(PCB_TYPE_ELEMENT, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedSize(CHANGESIZE_TYPES, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changedrillsize_syntax[] =
+	"ChangeDrillSize(Object, delta|style)\n" "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta|style)";
+
+static const char changedrillsize_help[] = "Changes the drilling hole size of objects.";
+
+/* %start-doc actions ChangeDrillSize
+
+%end-doc */
+
+static int ActionChange2ndSize(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *delta = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	int type = PCB_TYPE_NONE;
+	void *ptr1, *ptr2, *ptr3;
+
+	pcb_bool absolute;
+	Coord value;
+
+	if (function && delta) {
+		int funcid = funchash_get(function, NULL);
+
+		if (funcid == F_Object) {
+			gui->get_coords(_("Select an Object"), &x, &y);
+			type = SearchScreen(x, y, CHANGE2NDSIZE_TYPES, &ptr1, &ptr2, &ptr3);
+		}
+
+		if (strcmp(argv[1], "style") == 0) {
+			if (get_style_size(funcid, &value, type, 1) != 0)
+				return 1;
+			absolute = 1;
+		}
+		else
+			value = GetValue(delta, units, &absolute, NULL);
+
+		switch (funchash_get(function, NULL)) {
+		case F_Object:
+			{
+
+				if (type != PCB_TYPE_NONE)
+					if (ChangeObject2ndSize(type, ptr1, ptr2, ptr3, value, absolute, pcb_true))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedVias:
+			if (ChangeSelected2ndSize(PCB_TYPE_VIA, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (ChangeSelected2ndSize(PCB_TYPE_PIN, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelected2ndSize(PCB_TYPEMASK_PIN, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------  */
+
+static const char changepinname_syntax[] = "ChangePinName(ElementName,PinNumber,PinName)";
+
+static const char changepinname_help[] = "Sets the name of a specific pin on a specific element.";
+
+/* %start-doc actions ChangePinName
+
+This can be especially useful for annotating pin names from a
+schematic to the layout without requiring knowledge of the pcb file
+format.
+
+ at example
+ChangePinName(U3, 7, VCC)
+ at end example
+
+%end-doc */
+
+static int ActionChangePinName(int argc, const char **argv, Coord x, Coord y)
+{
+	int changed = 0;
+	const char *refdes, *pinnum, *pinname;
+
+	if (argc != 3) {
+		AFAIL(changepinname);
+	}
+
+	refdes = argv[0];
+	pinnum = argv[1];
+	pinname = argv[2];
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (NSTRCMP(refdes, NAMEONPCB_NAME(element)) == 0) {
+			PIN_LOOP(element);
+			{
+				if (NSTRCMP(pinnum, pin->Number) == 0) {
+					AddObjectToChangeNameUndoList(PCB_TYPE_PIN, NULL, NULL, pin, pin->Name);
+					/*
+					 * Note:  we can't free() pin->Name first because
+					 * it is used in the undo list
+					 */
+					pin->Name = pcb_strdup(pinname);
+					SetChangedFlag(pcb_true);
+					changed = 1;
+				}
+			}
+			END_LOOP;
+
+			PAD_LOOP(element);
+			{
+				if (NSTRCMP(pinnum, pad->Number) == 0) {
+					AddObjectToChangeNameUndoList(PCB_TYPE_PAD, NULL, NULL, pad, pad->Name);
+					/*
+					 * Note:  we can't free() pad->Name first because
+					 * it is used in the undo list
+					 */
+					pad->Name = pcb_strdup(pinname);
+					SetChangedFlag(pcb_true);
+					changed = 1;
+				}
+			}
+			END_LOOP;
+		}
+	}
+	END_LOOP;
+	/*
+	 * done with our action so increment the undo # if we actually
+	 * changed anything
+	 */
+	if (changed) {
+		if (defer_updates)
+			defer_needs_update = 1;
+		else {
+			IncrementUndoSerialNumber();
+			gui->invalidate_all();
+		}
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changename_syntax[] = "ChangeName(Object)\n" "ChangeName(Object|\"Number\")\n" "ChangeName(Layout|Layer)";
+
+static const char changename_help[] = "Sets the name (or pin number) of objects.";
+
+/* %start-doc actions ChangeName
+
+ at table @code
+
+ at item Object
+Changes the name of the element under the cursor.
+
+ at item Layout
+Changes the name of the layout.  This is printed on the fab drawings.
+
+ at item Layer
+Changes the name of the currently active layer.
+
+ at end table
+
+%end-doc */
+
+int ActionChangeName(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *pinnums = ACTION_ARG(1);
+	char *name;
+	int pinnum;
+
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+			/* change the name of an object */
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, CHANGENAME_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE) {
+					SaveUndoSerialNumber();
+					if ((pinnums != NULL) && (strcasecmp(pinnums, "Number") == 0))
+						pinnum = 1;
+					else
+						pinnum = 0;
+					if (QueryInputAndChangeObjectName(type, ptr1, ptr2, ptr3, pinnum)) {
+						SetChangedFlag(pcb_true);
+						if (type == PCB_TYPE_ELEMENT) {
+							RubberbandTypePtr ptr;
+							int i;
+
+							RestoreUndoSerialNumber();
+							Crosshair.AttachedObject.RubberbandN = 0;
+							LookupRatLines(type, ptr1, ptr2, ptr3);
+							ptr = Crosshair.AttachedObject.Rubberband;
+							for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++, ptr++) {
+								if (PCB->RatOn)
+									EraseRat((RatTypePtr) ptr->Line);
+								MoveObjectToRemoveUndoList(PCB_TYPE_RATLINE, ptr->Line, ptr->Line, ptr->Line);
+							}
+							IncrementUndoSerialNumber();
+							Draw();
+						}
+					}
+				}
+				break;
+			}
+
+			/* change the layout's name */
+		case F_Layout:
+			name = gui->prompt_for(_("Enter the layout name:"), EMPTY(PCB->Name));
+			/* NB: ChangeLayoutName takes ownership of the passed memory */
+			if (name && ChangeLayoutName(name))
+				SetChangedFlag(pcb_true);
+			break;
+
+			/* change the name of the active layer */
+		case F_Layer:
+			name = gui->prompt_for(_("Enter the layer name:"), EMPTY(CURRENT->Name));
+			/* NB: ChangeLayerName takes ownership of the passed memory */
+			if (name && ChangeLayerName(CURRENT, name))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changejoin_syntax[] = "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
+
+static const char changejoin_help[] = "Changes the join (clearance through polygons) of objects.";
+
+/* %start-doc actions ChangeJoin
+
+The join flag determines whether a line or arc, drawn to intersect a
+polygon, electrically connects to the polygon or not.  When joined,
+the line/arc is simply drawn over the polygon, making an electrical
+connection.  When not joined, a gap is drawn between the line and the
+polygon, insulating them from each other.
+
+%end-doc */
+
+static int ActionChangeJoin(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, CHANGEJOIN_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (ChangeObjectJoin(type, ptr1, ptr2, ptr3))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedLines:
+			if (ChangeSelectedJoin(PCB_TYPE_LINE))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedArcs:
+			if (ChangeSelectedJoin(PCB_TYPE_ARC))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedJoin(CHANGEJOIN_TYPES))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changenonetlist_syntax[] =
+	"ChangeNonetlist(ToggleObject)\n" "ChangeNonetlist(SelectedElements)\n" "ChangeNonetlist(Selected|SelectedObjects)";
+
+static const char changenonetlist_help[] = "Changes the nonetlist flag of elements.";
+
+/* %start-doc actions ChangeNonetlist
+
+Note that @code{Pins} means both pins and pads.
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionChangeNonetlist(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+		case F_Element:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+				gui->get_coords(_("Select an Element"), &x, &y);
+
+				ptr3 = NULL;
+				type = SearchScreen(x, y, CHANGENONETLIST_TYPES, &ptr1, &ptr2, &ptr3);
+				if (ChangeObjectNonetlist(type, ptr1, ptr2, ptr3))
+					SetChangedFlag(pcb_true);
+				break;
+			}
+		case F_SelectedElements:
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedNonetlist(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+static const char changesquare_syntax[] =
+	"ChangeSquare(ToggleObject)\n" "ChangeSquare(SelectedElements|SelectedPins)\n" "ChangeSquare(Selected|SelectedObjects)";
+
+static const char changesquare_help[] = "Changes the square flag of pins and pads.";
+
+/* %start-doc actions ChangeSquare
+
+Note that @code{Pins} means both pins and pads.
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionChangeSquare(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+
+				ptr3 = NULL;
+				type = SearchScreen(x, y, CHANGESQUARE_TYPES, &ptr1, &ptr2, &ptr3);
+
+				if (ptr3 != NULL) {
+					int qstyle = GET_SQUARE((PinTypePtr) ptr3);
+					qstyle++;
+					if (qstyle > 17)
+						qstyle = 0;
+					if (type != PCB_TYPE_NONE)
+						if (ChangeObjectSquare(type, ptr1, ptr2, ptr3, qstyle))
+							SetChangedFlag(pcb_true);
+				}
+				break;
+			}
+
+		case F_SelectedElements:
+			if (ChangeSelectedSquare(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (ChangeSelectedSquare(PCB_TYPE_PIN | PCB_TYPE_PAD))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedSquare(PCB_TYPE_PIN | PCB_TYPE_PAD))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char setsquare_syntax[] = "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
+
+static const char setsquare_help[] = "sets the square-flag of objects.";
+
+/* %start-doc actions SetSquare
+
+Note that @code{Pins} means pins and pads.
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionSetSquare(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function && *function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, CHANGESQUARE_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (SetObjectSquare(type, ptr1, ptr2, ptr3))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedElements:
+			if (SetSelectedSquare(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (SetSelectedSquare(PCB_TYPE_PIN | PCB_TYPE_PAD))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (SetSelectedSquare(PCB_TYPE_PIN | PCB_TYPE_PAD))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char clearsquare_syntax[] = "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
+
+static const char clearsquare_help[] = "Clears the square-flag of pins and pads.";
+
+/* %start-doc actions ClearSquare
+
+Note that @code{Pins} means pins and pads.
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionClearSquare(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function && *function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, CHANGESQUARE_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (ClrObjectSquare(type, ptr1, ptr2, ptr3))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedElements:
+			if (ClrSelectedSquare(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (ClrSelectedSquare(PCB_TYPE_PIN | PCB_TYPE_PAD))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ClrSelectedSquare(PCB_TYPE_PIN | PCB_TYPE_PAD))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changeoctagon_syntax[] =
+	"ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n" "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
+
+static const char changeoctagon_help[] = "Changes the octagon-flag of pins and vias.";
+
+/* %start-doc actions ChangeOctagon
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionChangeOctagon(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, CHANGEOCTAGON_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (ChangeObjectOctagon(type, ptr1, ptr2, ptr3))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedElements:
+			if (ChangeSelectedOctagon(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (ChangeSelectedOctagon(PCB_TYPE_PIN))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedVias:
+			if (ChangeSelectedOctagon(PCB_TYPE_VIA))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedOctagon(PCB_TYPEMASK_PIN))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char setoctagon_syntax[] = "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
+
+static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
+
+/* %start-doc actions SetOctagon
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionSetOctagon(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, CHANGEOCTAGON_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (SetObjectOctagon(type, ptr1, ptr2, ptr3))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedElements:
+			if (SetSelectedOctagon(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (SetSelectedOctagon(PCB_TYPE_PIN))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedVias:
+			if (SetSelectedOctagon(PCB_TYPE_VIA))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (SetSelectedOctagon(PCB_TYPEMASK_PIN))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char clearoctagon_syntax[] =
+	"ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n" "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
+
+static const char clearoctagon_help[] = "Clears the octagon-flag of pins and vias.";
+
+/* %start-doc actions ClearOctagon
+
+ at pinshapes
+
+%end-doc */
+
+static int ActionClearOctagon(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleObject:
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (ClrObjectOctagon(type, ptr1, ptr2, ptr3))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedElements:
+			if (ClrSelectedOctagon(PCB_TYPE_ELEMENT))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedPins:
+			if (ClrSelectedOctagon(PCB_TYPE_PIN))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_SelectedVias:
+			if (ClrSelectedOctagon(PCB_TYPE_VIA))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ClrSelectedOctagon(PCB_TYPEMASK_PIN))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static const char setthermal_syntax[] = "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
+
+static const char setthermal_help[] =
+	"Set the thermal (on the current layer) of pins or vias to the given style.\n"
+	"Style = 0 means no thermal.\n"
+	"Style = 1 has diagonal fingers with sharp edges.\n"
+	"Style = 2 has horizontal and vertical fingers with sharp edges.\n"
+	"Style = 3 is a solid connection to the plane."
+	"Style = 4 has diagonal fingers with rounded edges.\n" "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
+
+/* %start-doc actions SetThermal
+
+This changes how/whether pins or vias connect to any rectangle or polygon
+on the current layer. The first argument can specify one object, or all
+selected pins, or all selected vias, or all selected pins and vias.
+The second argument specifies the style of connection.
+There are 5 possibilities:
+0 - no connection,
+1 - 45 degree fingers with sharp edges,
+2 - horizontal & vertical fingers with sharp edges,
+3 - solid connection,
+4 - 45 degree fingers with rounded corners,
+5 - horizontal & vertical fingers with rounded corners.
+
+Pins and Vias may have thermals whether or not there is a polygon available
+to connect with. However, they will have no effect without the polygon.
+%end-doc */
+
+static int ActionSetThermal(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *style = ACTION_ARG(1);
+	void *ptr1, *ptr2, *ptr3;
+	int type, kind;
+	int err = 0;
+
+	if (function && *function && style && *style) {
+		pcb_bool absolute;
+
+		kind = GetValue(style, NULL, &absolute, NULL);
+		if (absolute)
+			switch (funchash_get(function, NULL)) {
+			case F_Object:
+				if ((type = SearchScreen(Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE) {
+					ChangeObjectThermal(type, ptr1, ptr2, ptr3, kind);
+					IncrementUndoSerialNumber();
+					Draw();
+				}
+				break;
+			case F_SelectedPins:
+				ChangeSelectedThermals(PCB_TYPE_PIN, kind);
+				break;
+			case F_SelectedVias:
+				ChangeSelectedThermals(PCB_TYPE_VIA, kind);
+				break;
+			case F_Selected:
+			case F_SelectedElements:
+				ChangeSelectedThermals(CHANGETHERMAL_TYPES, kind);
+				break;
+			default:
+				err = 1;
+				break;
+			}
+		else
+			err = 1;
+		if (!err)
+			return 0;
+	}
+
+	AFAIL(setthermal);
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+static const char setflag_syntax[] =
+	"SetFlag(Object|Selected|SelectedObjects, flag)\n"
+	"SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
+	"SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
+	"SetFlag(SelectedElements, flag)\n" "flag = square | octagon | thermal | join";
+
+static const char setflag_help[] = "Sets flags on objects.";
+
+/* %start-doc actions SetFlag
+
+Turns the given flag on, regardless of its previous setting.  See
+ at code{ChangeFlag}.
+
+ at example
+SetFlag(SelectedPins,thermal)
+ at end example
+
+%end-doc */
+
+static int ActionSetFlag(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *flag = ACTION_ARG(1);
+	ChangeFlag(function, flag, 1, "SetFlag");
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char clrflag_syntax[] =
+	"ClrFlag(Object|Selected|SelectedObjects, flag)\n"
+	"ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
+	"ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
+	"ClrFlag(SelectedElements, flag)\n" "flag = square | octagon | thermal | join";
+
+static const char clrflag_help[] = "Clears flags on objects.";
+
+/* %start-doc actions ClrFlag
+
+Turns the given flag off, regardless of its previous setting.  See
+ at code{ChangeFlag}.
+
+ at example
+ClrFlag(SelectedLines,join)
+ at end example
+
+%end-doc */
+
+static int ActionClrFlag(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *flag = ACTION_ARG(1);
+	ChangeFlag(function, flag, 0, "ClrFlag");
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char setvalue_syntax[] = "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
+
+static const char setvalue_help[] = "Change various board-wide values and sizes.";
+
+/* %start-doc actions SetValue
+
+ at table @code
+
+ at item ViaDrillingHole
+Changes the diameter of the drill for new vias.
+
+ at item Grid
+Sets the grid spacing.
+
+ at item Line
+ at item LineSize
+Changes the thickness of new lines.
+
+ at item Via
+ at item ViaSize
+Changes the diameter of new vias.
+
+ at item Text
+ at item TextScale
+Changes the size of new text.
+
+ at end table
+
+%end-doc */
+
+static int ActionSetValue(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *val = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	pcb_bool absolute;								/* flag for 'absolute' value */
+	double value;
+	int err = 0;
+
+	if (function && val) {
+		value = GetValue(val, units, &absolute, NULL);
+		switch (funchash_get(function, NULL)) {
+		case F_ViaDrillingHole:
+			SetViaDrillingHole(absolute ? value : value + conf_core.design.via_drilling_hole, pcb_false);
+			hid_action("RouteStylesChanged");
+			break;
+
+		case F_Grid:
+			if (absolute)
+				SetGrid(value, pcb_false);
+			else {
+				/* On the way down, short against the minimum
+				 * PCB drawing unit */
+				if ((value + PCB->Grid) < 1)
+					SetGrid(1, pcb_false);
+				else if (PCB->Grid == 1)
+					SetGrid(value, pcb_false);
+				else
+					SetGrid(value + PCB->Grid, pcb_false);
+			}
+			break;
+
+		case F_LineSize:
+		case F_Line:
+			SetLineSize(absolute ? value : value + conf_core.design.line_thickness);
+			hid_action("RouteStylesChanged");
+			break;
+
+		case F_Via:
+		case F_ViaSize:
+			SetViaSize(absolute ? value : value + conf_core.design.via_thickness, pcb_false);
+			hid_action("RouteStylesChanged");
+			break;
+
+		case F_Text:
+		case F_TextScale:
+			value /= 45;
+			SetTextScale(absolute ? value : value + conf_core.design.text_scale);
+			break;
+		default:
+			err = 1;
+			break;
+		}
+		if (!err)
+			return 0;
+	}
+
+	AFAIL(setvalue);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changeangle_syntax[] =
+	"ChangeAngle(Object, start|delta|both, delta)\n"
+	"ChangeAngle(SelectedObjects|Selected, start|delta|both, delta)\n"
+	"ChangeAngle(SelectedArcs, start|delta|both, delta)\n";
+static const char changeangle_help[] = "Changes the start angle, delta angle or both angles of an arc.";
+static int ActionChangeAngle(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *prim  = ACTION_ARG(1);
+	const char *delta = ACTION_ARG(2);
+	pcb_bool absolute;								/* indicates if absolute size is given */
+	double value;
+	int type = PCB_TYPE_NONE, which;
+	void *ptr1, *ptr2, *ptr3;
+
+	if (strcasecmp(prim, "start") == 0) which = 0;
+	else if (strcasecmp(prim, "delta") == 0) which = 1;
+	else if (strcasecmp(prim, "both") == 0) which = 2;
+	else {
+		Message(PCB_MSG_ERROR, "Second argument of ChangeAngle must be start, delta or both\n");
+		return -1;
+	}
+
+	if (function && delta) {
+		int funcid = funchash_get(function, NULL);
+
+		if (funcid == F_Object)
+			type = SearchScreen(Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES, &ptr1, &ptr2, &ptr3);
+
+		{ /* convert angle from string */
+			char *end;
+			while(isspace(*delta)) delta++;
+			value = strtod(delta, &end);
+			if (*end != '\0') {
+				Message(PCB_MSG_ERROR, "Invalid numeric (in angle)\n");
+				return -1;
+			}
+			absolute = ((*delta != '-') && (*delta != '+'));
+		}
+		
+		switch (funcid) {
+		case F_Object:
+			{
+				if (type != PCB_TYPE_NONE) {
+					if (TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr) ptr2))
+						Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+					else {
+						if (ChangeObjectAngle(type, ptr1, ptr2, ptr3, which, value, absolute))
+							SetChangedFlag(pcb_true);
+					}
+				}
+				break;
+			}
+
+		case F_SelectedArcs:
+			if (ChangeSelectedAngle(PCB_TYPE_ARC, which, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedAngle(CHANGESIZE_TYPES, which, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char changeradius_syntax[] =
+	"ChangeRadius(Object, width|x|height|y|both, delta)\n"
+	"ChangeRadius(SelectedObjects|Selected, width|x|height|y|both, delta)\n"
+	"ChangeRadius(SelectedArcs, width|x|height|y|both, delta)\n";
+static const char changeradius_help[] = "Changes the width or height (radius) of an arc.";
+static int ActionChangeRadius(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *prim  = ACTION_ARG(1);
+	const char *delta = ACTION_ARG(2);
+	const char *units = ACTION_ARG(3);
+	pcb_bool absolute;								/* indicates if absolute size is given */
+	double value;
+	int type = PCB_TYPE_NONE, which;
+	void *ptr1, *ptr2, *ptr3;
+
+	if ((strcasecmp(prim, "width") == 0) || (strcasecmp(prim, "x") == 0)) which = 0;
+	else if ((strcasecmp(prim, "height") == 0) || (strcasecmp(prim, "y") == 0)) which = 1;
+	else if (strcasecmp(prim, "both") == 0) which = 2;
+	else {
+		Message(PCB_MSG_ERROR, "Second argument of ChangeRadius must be width, x, height, y or both\n");
+		return -1;
+	}
+
+	if (function && delta) {
+		int funcid = funchash_get(function, NULL);
+
+		if (funcid == F_Object)
+			type = SearchScreen(Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES, &ptr1, &ptr2, &ptr3);
+
+		value = GetValue(delta, units, &absolute, NULL);
+
+		switch (funcid) {
+		case F_Object:
+			{
+				if (type != PCB_TYPE_NONE) {
+					if (TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr) ptr2))
+						Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+					else {
+						if (ChangeObjectRadius(type, ptr1, ptr2, ptr3, which, value, absolute))
+							SetChangedFlag(pcb_true);
+					}
+				}
+				break;
+			}
+
+		case F_SelectedArcs:
+			if (ChangeSelectedRadius(PCB_TYPE_ARC, which, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+
+		case F_Selected:
+		case F_SelectedObjects:
+			if (ChangeSelectedRadius(CHANGESIZE_TYPES, which, value, absolute))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+HID_Action change_action_list[] = {
+	{"ChangeAngle", 0, ActionChangeAngle,
+	 changeangle_help, changeangle_syntax}
+	,
+	{"ChangeClearSize", 0, ActionChangeClearSize,
+	 changeclearsize_help, changeclearsize_syntax}
+	,
+	{"ChangeDrillSize", 0, ActionChange2ndSize,
+	 changedrillsize_help, changedrillsize_syntax}
+	,
+	{"ChangeHole", 0, ActionChangeHole,
+	 changehold_help, changehold_syntax}
+	,
+	{"ChangeJoin", 0, ActionChangeJoin,
+	 changejoin_help, changejoin_syntax}
+	,
+	{"ChangeName", 0, ActionChangeName,
+	 changename_help, changename_syntax}
+	,
+	{"ChangePaste", 0, ActionChangePaste,
+	 changepaste_help, changepaste_syntax}
+	,
+	{"ChangePinName", 0, ActionChangePinName,
+	 changepinname_help, changepinname_syntax}
+	,
+	{"ChangeRadius", 0, ActionChangeRadius,
+	 changeradius_help, changeradius_syntax}
+	,
+	{"ChangeSize", 0, ActionChangeSize,
+	 changesize_help, changesize_syntax}
+	,
+	{"ChangeSizes", 0, ActionChangeSizes,
+	 changesizes_help, changesizes_syntax}
+	,
+	{"ChangeNonetlist", 0, ActionChangeNonetlist,
+	 changenonetlist_help, changenonetlist_syntax}
+	,
+	{"ChangeSquare", 0, ActionChangeSquare,
+	 changesquare_help, changesquare_syntax}
+	,
+	{"ChangeOctagon", 0, ActionChangeOctagon,
+	 changeoctagon_help, changeoctagon_syntax}
+	,
+	{"ChangeFlag", 0, ActionChangeFlag,
+	 changeflag_help, changeflag_syntax}
+	,
+	{"ClearSquare", 0, ActionClearSquare,
+	 clearsquare_help, clearsquare_syntax}
+	,
+	{"ClearOctagon", 0, ActionClearOctagon,
+	 clearoctagon_help, clearoctagon_syntax}
+	,
+	{"SetSquare", 0, ActionSetSquare,
+	 setsquare_help, setsquare_syntax}
+	,
+	{"SetOctagon", 0, ActionSetOctagon,
+	 setoctagon_help, setoctagon_syntax}
+	,
+	{"SetThermal", 0, ActionSetThermal,
+	 setthermal_help, setthermal_syntax}
+	,
+	{"SetValue", 0, ActionSetValue,
+	 setvalue_help, setvalue_syntax}
+	,
+	{"SetFlag", 0, ActionSetFlag,
+	 setflag_help, setflag_syntax}
+	,
+	{"ClrFlag", 0, ActionClrFlag,
+	 clrflag_help, clrflag_syntax}
+};
+
+REGISTER_ACTIONS(change_action_list, NULL)
diff --git a/src/check_icon.data b/src/check_icon.data
new file mode 100644
index 0000000..f6b1f45
--- /dev/null
+++ b/src/check_icon.data
@@ -0,0 +1,4 @@
+#define check_icon_width 8
+#define check_icon_height 8
+static unsigned char check_icon_bits[] = {
+   0x80, 0xc0, 0x61, 0x31, 0x1b, 0x0f, 0x06, 0x00};
diff --git a/src/clip.c b/src/clip.c
new file mode 100644
index 0000000..ffb60e5
--- /dev/null
+++ b/src/clip.c
@@ -0,0 +1,111 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 2004 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include "clip.h"
+#include <math.h>
+
+/* Clip the line to the clipBox
+ * return pcb_true if something to be drawn
+ * pcb_false if the whole thing is clipped
+ */
+pcb_bool ClipLine(double minx, double miny, double maxx, double maxy, double *x1, double *y1, double *x2, double *y2, double margin)
+{
+	double d, r;
+
+	minx -= margin;
+	miny -= margin;
+	maxx += margin;
+	maxy += margin;
+
+	/* clip first point on left side */
+	if (*x1 < minx) {
+		if (*x2 < minx)
+			return pcb_false;
+		d = *x2 - *x1;
+		r = (minx - *x1) / d;
+		*x1 = minx;
+		*y1 += r * (*y2 - *y1);
+	}
+	/* clip second point on left side */
+	if (*x2 < minx) {
+		d = *x1 - *x2;
+		r = (minx - *x2) / d;
+		*x2 = minx;
+		*y2 += r * (*y1 - *y2);
+	}
+	/* clip first point on right side */
+	if (*x1 > maxx) {
+		if (*x2 > maxx)
+			return pcb_false;
+		d = *x2 - *x1;
+		r = (maxx - *x1) / d;
+		*x1 = maxx;
+		*y1 += r * (*y2 - *y1);
+	}
+	/* clip second point on right side */
+	if (*x2 > maxx) {
+		d = *x1 - *x2;
+		r = (maxx - *x2) / d;
+		*x2 = maxx;
+		*y2 += r * (*y1 - *y2);
+	}
+
+	/* clip first point on top */
+	if (*y1 < miny) {
+		if (*y2 < miny)
+			return pcb_false;
+		d = *y2 - *y1;
+		r = (miny - *y1) / d;
+		*y1 = miny;
+		*x1 += r * (*x2 - *x1);
+	}
+	/* clip second point on top */
+	if (*y2 < miny) {
+		d = *y1 - *y2;
+		r = (miny - *y2) / d;
+		*y2 = miny;
+		*x2 += r * (*x1 - *x2);
+	}
+	/* clip first point on bottom */
+	if (*y1 > maxy) {
+		if (*y2 > maxy)
+			return pcb_false;
+		d = *y2 - *y1;
+		r = (maxy - *y1) / d;
+		*y1 = maxy;
+		*x1 += r * (*x2 - *x1);
+	}
+	/* clip second point on top */
+	if (*y2 > maxy) {
+		d = *y1 - *y2;
+		r = (maxy - *y2) / d;
+		*y2 = maxy;
+		*x2 += r * (*x1 - *x2);
+	}
+	return pcb_true;
+}
diff --git a/src/clip.h b/src/clip.h
new file mode 100644
index 0000000..b9a3b4b
--- /dev/null
+++ b/src/clip.h
@@ -0,0 +1,44 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 2004 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for inserting points into objects */
+
+#ifndef	PCB_CLIP_H
+#define	PCB_CLIP_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * prototypes
+ */
+
+/* Clip X,Y to the given bounding box, plus a margin.  Returns TRUE if
+   there is something left to be drawn.  */
+pcb_bool ClipLine(double minx, double miny, double maxx, double maxy,
+							double *x1, double *y1, double *x2, double *y2, double margin);
+
+#endif
diff --git a/src/compat_dl.c b/src/compat_dl.c
new file mode 100644
index 0000000..b50f2d7
--- /dev/null
+++ b/src/compat_dl.c
@@ -0,0 +1,68 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2004, 2006 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "compat_dl.h"
+#include "global.h"
+
+#ifdef USE_LOADLIBRARY
+#include <windows.h>
+
+void *dlopen(const char *f, int ATTRIBUTE_UNUSED flag)
+{
+	return LoadLibrary(f);
+}
+
+void dlclose(void *h)
+{
+	FreeLibrary((HINSTANCE) h);
+}
+
+char *dlerror()
+{
+	static LPVOID lpMsgBuf = NULL;
+	DWORD dw;
+
+	/* free the error message buffer */
+	if (lpMsgBuf)
+		LocalFree(lpMsgBuf);
+
+	/* get the error code */
+	dw = GetLastError();
+
+	/* get the corresponding error message */
+	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+								FORMAT_MESSAGE_FROM_SYSTEM |
+								FORMAT_MESSAGE_IGNORE_INSERTS,
+								NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL);
+
+	return (char *) lpMsgBuf;
+}
+
+void *dlsym(void *handle, const char *symbol)
+{
+	return (void *) GetProcAddress((HMODULE) handle, symbol);
+}
+
+
+#endif
diff --git a/src/compat_dl.h b/src/compat_dl.h
new file mode 100644
index 0000000..5b32640
--- /dev/null
+++ b/src/compat_dl.h
@@ -0,0 +1,51 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2004 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_COMPAT_DL_H
+#define PCB_COMPAT_DL_H
+
+#include "config.h"
+#include "compat_inc.h"
+
+#ifdef USE_LOADLIBRARY
+void *dlopen(const char *, int);
+void dlclose(void *);
+char *dlerror(void);
+
+void *dlsym(void *, const char *);
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 2
+#endif
+
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 4
+#endif
+
+#endif
+
+
+#endif /* PCB_COMPAT_DL_H */
diff --git a/src/compat_fs.c b/src/compat_fs.c
new file mode 100644
index 0000000..d625de2
--- /dev/null
+++ b/src/compat_fs.c
@@ -0,0 +1,303 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* misc functions used by several modules */
+
+#include "config.h"
+
+#include "compat_inc.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <math.h>
+#include <assert.h>
+#include "compat_fs.h"
+#include "compat_misc.h"
+
+#include <genvector/gds_char.h>
+
+#include "global.h"
+
+#include "error.h"
+#include "mymem.h"
+
+/* ----------------------------------------------------------------------
+ * returns pointer to current working directory.  If 'path' is not
+ * NULL, then the current working directory is copied to the array
+ * pointed to by 'path'
+ */
+char *GetWorkingDirectory(char *path)
+{
+#if defined(HAVE_GETCWD)
+	return getcwd(path, MAXPATHLEN);
+#elif defined(HAVE__GETCWD)
+	return _getcwd(path, MAXPATHLEN);
+#else
+	/* seems that some BSD releases lack of a prototype for getwd() */
+	return getwd(path);
+#endif
+}
+
+#if defined(USE_MKDIR)
+#	define MKDIR mkdir
+#elif defined(USE__MKDIR)
+#	define MKDIR _mkdir
+#else
+#	error no mkdir() available
+#endif
+int pcb_mkdir(const char *path, int mode)
+{
+#if MKDIR_NUM_ARGS == 1
+	return MKDIR(path);
+#elif MKDIR_NUM_ARGS == 2
+	return MKDIR(path, mode);
+#else
+#	error invalid number of arguments for mkdir
+#endif
+}
+#undef MKDIR
+
+int pcb_file_readable(const char *path)
+{
+	FILE *f;
+	f = fopen(path, "r");
+	if (f != NULL) {
+		fclose(f);
+		return 1;
+	}
+	return 0;
+}
+
+int pcb_spawnvp(const char **argv)
+{
+#if defined(USE_SPAWNVP)
+	int result = _spawnvp(_P_WAIT, argv[0], (const char *const *) argv);
+	if (result == -1)
+		return 1;
+	else
+		return 0;
+#elif defined(USE_FORK_WAIT)
+	int pid;
+	pid = fork();
+	if (pid < 0) {
+		/* error */
+		Message(PCB_MSG_DEFAULT, _("Cannot fork!"));
+		return 1;
+	}
+	else if (pid == 0) {
+		/* Child */
+		execvp(argv[0], (char* const*) argv);
+		exit(1);
+	}
+	else {
+		int rv;
+		/* Parent */
+		wait(&rv);
+	}
+	return 0;
+#else
+#	error Do not know how to run a background process.
+#endif
+}
+
+
+/*
+ * Creates a new temporary file name.  Hopefully the operating system
+ * provides a mkdtemp() function to securely create a temporary
+ * directory with mode 0700.  If so then that directory is created and
+ * the returned string is made up of the directory plus the name
+ * variable.  For example:
+ *
+ * tempfile_name_new ("myfile") might return
+ * "/var/tmp/pcb.123456/myfile".
+ *
+ * If mkdtemp() is not available then 'name' is ignored and the
+ * insecure tmpnam() function is used.
+ *
+ * Files/names created with tempfile_name_new() should be unlinked
+ * with tempfile_unlink to make sure the temporary directory is also
+ * removed when mkdtemp() is used.
+ */
+char *tempfile_name_new(const char *name)
+{
+	char *tmpfile = NULL;
+#ifdef HAVE_MKDTEMP
+#ifdef inline
+	/* Suppress compiler warnings; -Dinline means we are compiling in
+	   --debug with -ansi -pedantic; we do know that mkdtemp exists on the system,
+	   since HAVE_MKDTEMP is set. */
+	char *mkdtemp(char *template);
+#endif
+	const char *tmpdir;
+	char *mytmpdir;
+	size_t len;
+#endif
+
+	assert(name != NULL);
+
+#ifdef HAVE_MKDTEMP
+#define TEMPLATE "pcb.XXXXXXXX"
+
+
+	tmpdir = getenv("TMPDIR");
+
+	/* FIXME -- what about win32? */
+	if (tmpdir == NULL) {
+		tmpdir = "/tmp";
+	}
+
+	mytmpdir = (char *) malloc(sizeof(char) * (strlen(tmpdir) + 1 + strlen(TEMPLATE) + 1));
+	if (mytmpdir == NULL) {
+		fprintf(stderr, "tempfile_name_new(): malloc failed()\n");
+		exit(1);
+	}
+
+	*mytmpdir = '\0';
+	(void) strcat(mytmpdir, tmpdir);
+	(void) strcat(mytmpdir, PCB_DIR_SEPARATOR_S);
+	(void) strcat(mytmpdir, TEMPLATE);
+	if (mkdtemp(mytmpdir) == NULL) {
+		fprintf(stderr, "pcb_spawnvp():  mkdtemp (\"%s\") failed\n", mytmpdir);
+		free(mytmpdir);
+		return NULL;
+	}
+
+
+	len = strlen(mytmpdir) +			/* the temp directory name */
+		1 +													/* the directory sep. */
+		strlen(name) +							/* the file name */
+		1														/* the \0 termination */
+		;
+
+	tmpfile = (char *) malloc(sizeof(char) * len);
+
+	*tmpfile = '\0';
+	(void) strcat(tmpfile, mytmpdir);
+	(void) strcat(tmpfile, PCB_DIR_SEPARATOR_S);
+	(void) strcat(tmpfile, name);
+
+	free(mytmpdir);
+#undef TEMPLATE
+#else
+	/*
+	 * tmpnam() uses a static buffer so pcb_strdup() the result right away
+	 * in case someone decides to create multiple temp names.
+	 */
+	tmpfile = pcb_strdup(tmpnam(NULL));
+#ifdef __WIN32__
+	{
+		/* Guile doesn't like \ separators */
+		char *c;
+		for (c = tmpfile; *c; c++)
+			if (*c == '\\')
+				*c = '/';
+	}
+#endif
+#endif
+
+	return tmpfile;
+}
+
+/*
+ * Unlink a temporary file.  If we have mkdtemp() then our temp file
+ * lives in a temporary directory and we need to remove that directory
+ * too.
+ */
+int tempfile_unlink(char *name)
+{
+#ifdef DEBUG
+	/* SDB says:  Want to keep old temp files for examination when debugging */
+	return 0;
+#endif
+
+#ifdef HAVE_MKDTEMP
+	int e, rc2 = 0;
+	char *dname;
+
+	unlink(name);
+	/* it is possible that the file was never created so it is OK if the
+	   unlink fails */
+
+	/* now figure out the directory name to remove */
+	e = strlen(name) - 1;
+	while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {
+		e--;
+	}
+
+	dname = pcb_strdup(name);
+	dname[e] = '\0';
+
+	/*
+	 * at this point, e *should* point to the end of the directory part
+	 * but lets make sure.
+	 */
+	if (e > 0) {
+		rc2 = rmdir(dname);
+		if (rc2 != 0) {
+			perror(dname);
+		}
+
+	}
+	else {
+		fprintf(stderr, _("tempfile_unlink():  Unable to determine temp directory name from the temp file\n"));
+		fprintf(stderr, "tempfile_unlink():  \"%s\"\n", name);
+		rc2 = -1;
+	}
+
+	/* name was allocated with malloc */
+	free(dname);
+	free(name);
+
+	/*
+	 * FIXME - should also return -1 if the temp file exists and was not
+	 * removed.
+	 */
+	if (rc2 != 0) {
+		return -1;
+	}
+
+#else
+	int rc = unlink(name);
+
+	if (rc != 0) {
+		fprintf(stderr, _("Failed to unlink \"%s\"\n"), name);
+		free(name);
+		return rc;
+	}
+	free(name);
+
+#endif
+
+	return 0;
+}
diff --git a/src/compat_fs.h b/src/compat_fs.h
new file mode 100644
index 0000000..b917d15
--- /dev/null
+++ b/src/compat_fs.h
@@ -0,0 +1,14 @@
+char *GetWorkingDirectory(char *);
+
+int pcb_mkdir(const char *path, int mode);
+int pcb_spawnvp(const char **argv);
+
+/* Return 1 if path is a file that can be opened for read */
+int pcb_file_readable(const char *path);
+
+char *tempfile_name_new(const char *name);
+
+/* remove temporary file and _also_ free the memory for name
+ * (this fact is a little confusing)
+ */
+int tempfile_unlink(char *name);
diff --git a/src/compat_inc.h.in b/src/compat_inc.h.in
new file mode 100644
index 0000000..dc266cf
--- /dev/null
+++ b/src/compat_inc.h.in
@@ -0,0 +1,36 @@
+put /local/pcb/compat_inc [@
+
+@?libs/ldl/includes@
+@?libs/LoadLibrary/includes@
+@?libs/fs/getcwd/includes@
+@?libs/fs/_getcwd/includes@
+@?libs/fs/getwd/includes@
+@?/target/libs/proc/wait/includes@
+@?/target/libs/proc/fork/includes@
+@?/target/libs/proc/_spawnvp/includes@
+@?/target/libs/fs/mkdir/includes@
+@?/target/libs/fs/_mkdir/includes@
+@?/target/libs/fs/mkdtemp/includes@
+@?/target/libs/fs/readdir/includes@
+@?/target/libs/userpass/getpwuid/includes@
+@?/target/libs/math/round/includes@
+@]
+
+put /tmpasm/IFS {\n}
+uniq /local/pcb/compat_inc
+gsub /local/pcb/compat_inc {\\\\n *} {\n}
+
+
+print [@
+/*** DO NOT EDIT - automatically generated by scconfig ***/
+
+/* system-dependent includes, as detected in ./configure */
+
+@/local/pcb/compat_inc@
+
+/* These are required for stat() but are C89. Keeping them here, just in case. */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+@]
+
diff --git a/src/compat_lrealpath.c b/src/compat_lrealpath.c
new file mode 100644
index 0000000..eec8bfa
--- /dev/null
+++ b/src/compat_lrealpath.c
@@ -0,0 +1,156 @@
+/* Libiberty realpath.  Like realpath, but more consistent behavior.
+   Based on gdb_realpath from GDB.
+
+   Copyright 2003 Free Software Foundation, Inc.
+
+   This file is part of the libiberty library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/*
+
+ at deftypefn Replacement {const char*} lrealpath (const char *@var{name})
+
+Given a pointer to a string containing a pathname, returns a canonical
+version of the filename.  Symlinks will be resolved, and ``.'' and ``..''
+components will be simplified.  The returned value will be allocated using
+ at code{malloc}, or @code{NULL} will be returned on a memory allocation error.
+
+ at end deftypefn
+
+*/
+
+#include "config.h"
+#include "compat_lrealpath.h"
+#include <limits.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include "compat_misc.h"
+
+/* On GNU libc systems the declaration is only visible with _GNU_SOURCE.  */
+#if defined(HAVE_CANONICALIZE_FILE_NAME) \
+    && defined(NEED_DECLARATION_CANONICALIZE_FILE_NAME)
+extern char *canonicalize_file_name(const char *);
+#endif
+
+#if defined(HAVE_REALPATH)
+#if defined (PATH_MAX)
+#define REALPATH_LIMIT PATH_MAX
+#else
+#if defined (MAXPATHLEN)
+#define REALPATH_LIMIT MAXPATHLEN
+#endif
+#endif
+#else
+	/* cygwin has realpath, so it won't get here.  */
+#if defined (_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>						/* for GetFullPathName */
+#endif
+#endif
+
+char *lrealpath(const char *filename)
+{
+	/* Method 1: The system has a compile time upper bound on a filename
+	   path.  Use that and realpath() to canonicalize the name.  This is
+	   the most common case.  Note that, if there isn't a compile time
+	   upper bound, you want to avoid realpath() at all costs.  */
+#if defined(REALPATH_LIMIT)
+	{
+		char buf[REALPATH_LIMIT];
+		const char *rp = realpath(filename, buf);
+		if (rp == NULL)
+			rp = filename;
+		return pcb_strdup(rp);
+	}
+	/* REALPATH_LIMIT */
+
+	/* Method 2: The host system (i.e., GNU) has the function
+	   canonicalize_file_name() which malloc's a chunk of memory and
+	   returns that, use that.  */
+#elif defined(HAVE_CANONICALIZE_FILE_NAME)
+	{
+		char *rp = canonicalize_file_name(filename);
+		if (rp == NULL)
+			return pcb_strdup(filename);
+		else
+			return rp;
+	}
+	/* HAVE_CANONICALIZE_FILE_NAME */
+
+	/* Method 3: Now we're getting desperate!  The system doesn't have a
+	   compile time buffer size and no alternative function.  Query the
+	   OS, using pathconf(), for the buffer limit.  Care is needed
+	   though, some systems do not limit PATH_MAX (return -1 for
+	   pathconf()) making it impossible to pass a correctly sized buffer
+	   to realpath() (it could always overflow).  On those systems, we
+	   skip this.  */
+#elif defined (HAVE_REALPATH) && defined (HAVE_UNISTD_H)
+	{
+#ifdef inline
+	/* Suppress compiler warnings; -Dinline means we are compiling in
+	   --debug with -ansi -pedantic; we do know that realpath exists on the system,
+	   because of the the ifdefs. */
+	char *realpath(const char *path, char *resolved_path);
+#endif
+
+		/* Find out the max path size.  */
+		long path_max = pathconf("/", _PC_PATH_MAX);
+		if (path_max > 0) {
+			/* PATH_MAX is bounded.  */
+			char *buf, *rp, *ret;
+			buf = (char *) malloc(path_max);
+			if (buf == NULL)
+				return NULL;
+			rp = realpath(filename, buf);
+			ret = pcb_strdup(rp ? rp : filename);
+			free(buf);
+			return ret;
+		}
+		return NULL;
+	}
+	/* HAVE_REALPATH && HAVE_UNISTD_H */
+
+	/* The MS Windows method.  If we don't have realpath, we assume we
+	   don't have symlinks and just canonicalize to a Windows absolute
+	   path.  GetFullPath converts ../ and ./ in relative paths to
+	   absolute paths, filling in current drive if one is not given
+	   or using the current directory of a specified drive (eg, "E:foo").
+	   It also converts all forward slashes to back slashes.  */
+#elif defined (_WIN32)
+	{
+		char buf[MAX_PATH];
+		char *basename;
+		DWORD len = GetFullPathName(filename, MAX_PATH, buf, &basename);
+		if (len == 0 || len > MAX_PATH - 1)
+			return pcb_strdup(filename);
+		else {
+			/* The file system is case-preserving but case-insensitive,
+			   Canonicalize to lowercase, using the codepage associated
+			   with the process locale.  */
+			CharLowerBuff(buf, len);
+			return pcb_strdup(buf);
+		}
+	}
+#else
+
+	/* This system is a lost cause, just duplicate the filename.  */
+	return pcb_strdup(filename);
+#endif
+}
diff --git a/src/compat_lrealpath.h b/src/compat_lrealpath.h
new file mode 100644
index 0000000..17f611f
--- /dev/null
+++ b/src/compat_lrealpath.h
@@ -0,0 +1,7 @@
+#ifndef PCB_LREALPATH_H
+#define PCB_LREALPATH_H
+
+/* A well-defined realpath () that is always compiled in.  */
+char *lrealpath(const char *);
+
+#endif /* PCB_LREALPATH_H */
diff --git a/src/compat_misc.c b/src/compat_misc.c
new file mode 100644
index 0000000..239610f
--- /dev/null
+++ b/src/compat_misc.c
@@ -0,0 +1,134 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2004, 2006 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <math.h>
+#include "compat_misc.h"
+#include "compat_inc.h"
+#include "global.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* On some old systems random() works better than rand(). Unfortunately
+random() is less portable than rand(), which is C89. By default, just
+use rand(). Later on: scconfig should detect and enable random() if
+we find a system where it really breaks. */
+#ifdef HAVE_RANDOM
+long pcb_rand(void)
+{
+	return (long) random();
+}
+#else
+long pcb_rand(void)
+{
+	return (long) rand();
+}
+#endif
+
+const char *get_user_name(void)
+{
+#ifdef HAVE_GETPWUID
+	static struct passwd *pwentry;
+
+	int len;
+	char *comma, *gecos, *fab_author;
+
+	/* ID the user. */
+	pwentry = getpwuid(getuid());
+	gecos = pwentry->pw_gecos;
+	comma = strchr(gecos, ',');
+	if (comma)
+		len = comma - gecos;
+	else
+		len = strlen(gecos);
+	fab_author = (char *) malloc(len + 1);
+	if (!fab_author) {
+		perror("pcb: out of memory.\n");
+		exit(-1);
+	}
+	memcpy(fab_author, gecos, len);
+	fab_author[len] = 0;
+	return fab_author;
+#else
+	return "Unknown";
+#endif
+}
+
+int pcb_getpid(void)
+{
+	return getpid();
+}
+
+char *pcb_strndup(const char *s, int len)
+{
+	int a, l = strlen(s);
+	char *o;
+
+	a = (len < l) ? len : l;
+	o = malloc(a+1);
+	memcpy(o, s, a);
+	o[a] = '\0';
+	return o;
+}
+
+char *pcb_strdup(const char *s)
+{
+	int l = strlen(s);
+	char *o;
+	o = malloc(l+1);
+	memcpy(o, s, l+1);
+	return o;
+}
+
+#ifdef HAVE_ROUND
+#undef round
+extern double round(double x);
+double pcb_round(double x)
+{
+	return round(x);
+}
+#else
+
+/* Implementation idea borrowed from an old gcc (GPL'd) */
+double pcb_round(double x)
+{
+	double t;
+
+/* We should check for inf here, but inf is not in C89; if we'd have isinf(),
+   we'd have round() as well and we wouldn't be here at all. */
+
+	if (x >= 0.0) {
+		t = ceil(x);
+		if (t - x > 0.5)
+			t -= 1.0;
+		return t;
+	}
+
+	t = ceil(-x);
+	if ((t + x) > 0.5)
+		t -= 1.0;
+	return -t;
+}
+#endif
diff --git a/src/compat_misc.h b/src/compat_misc.h
new file mode 100644
index 0000000..c2d4622
--- /dev/null
+++ b/src/compat_misc.h
@@ -0,0 +1,42 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2004 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_COMPAT_MISC_H
+#define PCB_COMPAT_MISC_H
+
+#include "config.h"
+
+#include <math.h>
+
+long pcb_rand(void);
+
+const char *get_user_name(void);
+int pcb_getpid(void);
+
+char *pcb_strndup(const char *s, int len);
+char *pcb_strdup(const char *s);
+
+#define pcb_strdup_null(x) (((x) != NULL) ? pcb_strdup (x) : NULL)
+
+double pcb_round(double x);
+
+#endif /* PCB_COMPAT_MISC_H */
diff --git a/src/conf.c b/src/conf.c
new file mode 100644
index 0000000..d9be64d
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,1810 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <assert.h>
+#include <genht/hash.h>
+#include <liblihata/tree.h>
+#include <stdarg.h>
+#include "conf.h"
+#include "conf_core.h"
+#include "conf_hid.h"
+#include "hid_cfg.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "misc_util.h"
+#include "error.h"
+#include "paths.h"
+#include "compat_fs.h"
+#include "compat_misc.h"
+#include "error.h"
+
+/* conf list node's name */
+const char *conf_list_name = "pcb-rnd-conf-v1";
+static const char *conf_user_fn = "~/" DOT_PCB_RND "/pcb-conf.lht";
+
+lht_doc_t *conf_root[CFR_max_alloc];
+int conf_root_lock[CFR_max_alloc];
+int conf_lht_dirty[CFR_max_alloc];
+
+htsp_t *conf_fields = NULL;
+const int conf_default_prio[] = {
+/*	CFR_INTERNAL */   100,
+/*	CFR_SYSTEM */     200,
+/*	CFR_DEFAULTPCB */ 300,
+/*	CFR_USER */       400,
+/*	CFR_ENV */        500,
+/*	CFR_PROJECT */    600,
+/*	CFR_DESIGN */     700,
+/*	CFR_CLI */        800,
+0, 0, 0, 0, 0, 0, 0, 0, 0 /* make sure the array is addressable until CFR_max_alloc */
+};
+
+extern const char *conf_internal;
+
+/*static lht_doc_t *conf_plugin;*/
+
+static lht_node_t *conf_lht_get_confroot(lht_node_t *cwd)
+{
+	if (cwd == NULL)
+		return NULL;
+
+	/* if it's a list with a matching name, we found it */
+	if ((cwd->type == LHT_LIST) && (strcmp(cwd->name, conf_list_name) == 0))
+		return cwd;
+
+	/* else it may be the parent-hash of the list */
+	if (cwd->type != LHT_HASH)
+		return NULL;
+
+	return lht_dom_hash_get(cwd, conf_list_name);
+}
+
+
+int conf_load_as(conf_role_t role, const char *fn, int fn_is_text)
+{
+	lht_doc_t *d;
+	if (conf_root_lock[role])
+		return -1;
+	if (conf_root[role] != NULL) {
+		lht_dom_uninit(conf_root[role]);
+		conf_root[role] = NULL;
+	}
+	if (fn_is_text)
+		d = hid_cfg_load_str(fn);
+	else
+		d = hid_cfg_load_lht(fn);
+	if (d == NULL) {
+		FILE *f;
+		char *efn;
+		resolve_path(fn, &efn, 0);
+		f = fopen(efn, "r");
+		if (f != NULL) { /* warn only if the file is there - missing file is normal */
+			Message(PCB_MSG_DEFAULT, "error: failed to load lht config: %s\n", efn);
+			fclose(f);
+		}
+		free(efn);
+		return -1;
+	}
+
+	if ((d->root->type == LHT_LIST) && (strcmp(d->root->name, "pcb-rnd-conf-v1") == 0)) {
+		conf_root[role] = d;
+		return 0;
+	}
+
+	if ((d->root->type == LHT_HASH) && (strcmp(d->root->name, "geda-project-v1") == 0)) {
+		lht_node_t *confroot;
+		confroot = lht_tree_path_(d, d->root, "pcb-rnd-conf-v1", 1, 0, NULL);
+		if ((confroot != NULL)  && (confroot->type == LHT_LIST) && (strcmp(confroot->name, "li:pcb-rnd-conf-v1") == 0)) {
+			conf_root[role] = d;
+			return 0;
+		}
+
+		/* project file with no config root */
+		confroot = lht_dom_node_alloc(LHT_LIST, "pcb-rnd-conf-v1");
+		lht_dom_hash_put(d->root, confroot);
+		conf_root[role] = d;
+		return 0;
+	}
+
+	hid_cfg_error(d->root, "Root node must be either li:pcb-rnd-conf-v1 or ha:geda-project-v1\n");
+
+	if (d != NULL)
+		lht_dom_uninit(d);
+	return -1;
+}
+
+conf_policy_t conf_policy_parse(const char *s)
+{
+	if (strcasecmp(s, "overwrite") == 0)  return  POL_OVERWRITE;
+	if (strcasecmp(s, "prepend") == 0)    return  POL_PREPEND;
+	if (strcasecmp(s, "append") == 0)     return  POL_APPEND;
+	if (strcasecmp(s, "disable") == 0)    return  POL_DISABLE;
+	return POL_invalid;
+}
+
+const char *conf_policy_name(conf_policy_t p)
+{
+	switch(p) {
+		case POL_OVERWRITE: return "overwrite";
+		case POL_PREPEND:   return "prepend";
+		case POL_APPEND:    return "append";
+		case POL_DISABLE:   return "disable";
+		case POL_invalid:   return "(invalid)";
+	}
+	return "(unknown)";
+}
+
+conf_role_t conf_role_parse(const char *s)
+{
+	if (strcasecmp(s, "internal") == 0)   return CFR_INTERNAL;
+	if (strcasecmp(s, "system") == 0)     return CFR_SYSTEM;
+	if (strcasecmp(s, "defaultpcb") == 0) return CFR_DEFAULTPCB;
+	if (strcasecmp(s, "user") == 0)       return CFR_USER;
+	if (strcasecmp(s, "env") == 0)        return CFR_ENV;
+	if (strcasecmp(s, "project") == 0)    return CFR_PROJECT;
+	if (strcasecmp(s, "design") == 0)     return CFR_DESIGN;
+	if (strcasecmp(s, "cli") == 0)        return CFR_CLI;
+	return CFR_invalid;
+}
+
+const char *conf_role_name(conf_role_t r)
+{
+	switch(r) {
+		case CFR_INTERNAL:    return "internal";
+		case CFR_SYSTEM:      return "system";
+		case CFR_DEFAULTPCB:  return "defaultpcb";
+		case CFR_USER:        return "user";
+		case CFR_ENV:         return "env";
+		case CFR_PROJECT:     return "project";
+		case CFR_DESIGN:      return "design";
+		case CFR_CLI:         return "cli";
+		case CFR_file:        return "(file)";
+		case CFR_binary:      return "(binary)";
+
+		case CFR_max_alloc:
+		case CFR_max_real:
+		case CFR_invalid:     return "(invalid role)";
+	}
+	return "(unknown role)";
+}
+
+
+void conf_extract_poliprio(lht_node_t *root, conf_policy_t *gpolicy, long *gprio)
+{
+	long len = strlen(root->name), p = -1;
+	char tmp[128];
+	char *sprio, *end;
+	conf_policy_t pol;
+
+	if (len >= sizeof(tmp)) {
+		hid_cfg_error(root, "Invalid policy-prio '%s', subtree is ignored\n", root->name);
+		return;
+	}
+
+	memcpy(tmp, root->name, len+1);
+
+	/* split and convert prio */
+	sprio = strchr(tmp, '-');
+	if (sprio != NULL) {
+		*sprio = '\0';
+		sprio++;
+		p = strtol(sprio, &end, 10);
+		if ((*end != '\0') || (p < 0)) {
+			hid_cfg_error(root, "Invalid prio in '%s', subtree is ignored\n", root->name);
+			return;
+		}
+	}
+
+	/* convert policy */
+	pol = conf_policy_parse(tmp);
+	if (pol == POL_invalid) {
+		hid_cfg_error(root, "Invalid policy in '%s', subtree is ignored\n", root->name);
+		return;
+	}
+
+	/* by now the syntax is checked; save the output */
+	*gpolicy = pol;
+	if (p >= 0)
+		*gprio = p;
+}
+
+
+int conf_get_policy_prio(lht_node_t *node, conf_policy_t *gpolicy, long *gprio)
+{
+	for(;;node = node->parent) {
+		if (node->parent == node->doc->root) {
+			conf_extract_poliprio(node, gpolicy, gprio);
+			return 0;
+		}
+		if (node->parent == NULL)
+			return -1;
+	}
+}
+
+static int conf_parse_increments(Increments *inc, lht_node_t *node)
+{
+	lht_node_t *val;
+
+	if (node->type != LHT_HASH) {
+		hid_cfg_error(node, "Increments need to be a hash\n");
+		return -1;
+	}
+
+#define incload(field) \
+	val = lht_dom_hash_get(node, #field); \
+	if (val != NULL) {\
+		if (val->type == LHT_TEXT) {\
+			pcb_bool succ; \
+			inc->field = GetValue(val->data.text.value, NULL, NULL, &succ); \
+			if (!succ) \
+				hid_cfg_error(node, "invalid numeric value in increment field " #field ": %s\n", val->data.text.value); \
+		} \
+		else\
+			hid_cfg_error(node, "increment field " #field " needs to be a text node\n"); \
+	}
+
+	incload(grid);
+	incload(grid_min);
+	incload(grid_max);
+	incload(size);
+	incload(size_min);
+	incload(size_max);
+	incload(line);
+	incload(line_min);
+	incload(line_max);
+	incload(clear);
+	incload(clear_min);
+	incload(clear_max);
+
+#undef incload
+	return 0;
+}
+
+const char *conf_get_project_conf_name(const char *project_fn, const char *pcb_fn, const char **try)
+{
+	static char res[MAXPATHLEN];
+	static const char *project_name = "project.lht";
+	FILE *f;
+
+	if (project_fn != NULL) {
+		strncpy(res, project_fn, sizeof(res)-1);
+		res[sizeof(res)-1] = '\0';
+		goto check;
+	}
+
+	if (pcb_fn != NULL) {
+		char *end;
+		/* replace pcb name with project_name and test */
+		strncpy(res, pcb_fn, sizeof(res)-1);
+		res[sizeof(res)-1] = '\0';
+		end = strrchr(res, PCB_DIR_SEPARATOR_C);
+		if (end != NULL) {
+			if (end+1+sizeof(project_name) >= res + sizeof(res))
+				return NULL;
+			strcpy(end+1, project_name);
+			goto check;
+		}
+		/* no path in pcb, try cwd */
+		strcpy(res, project_name);
+		goto check;
+	}
+
+	/* no pcb - no project */
+	*try = "<no pcb file, no path to try>";
+	return NULL;
+
+	check:;
+	*try = res;
+	f = fopen(res, "r");
+	if (f != NULL) {
+		fclose(f);
+		return res;
+	}
+
+	return NULL;
+}
+
+int conf_parse_text(confitem_t *dst, int idx, conf_native_type_t type, const char *text, lht_node_t *err_node)
+{
+	const char *strue[]  = {"true",  "yes",  "on",   "1", NULL};
+	const char *sfalse[] = {"false", "no",   "off",  "0", NULL};
+	const char **s;
+	char *end;
+	long l;
+	int base = 10;
+	double d;
+	const Unit *u;
+
+	switch(type) {
+		case CFN_STRING:
+			dst->string[idx] = text;
+			break;
+		case CFN_BOOLEAN:
+			while(isspace(*text)) text++;
+			for(s = strue; *s != NULL; s++)
+				if (strncasecmp(*s, text, strlen(*s)) == 0) {
+					dst->boolean[idx] = 1;
+					return 0;
+				}
+			for(s = sfalse; *s != NULL; s++)
+				if (strncasecmp(*s, text, strlen(*s)) == 0) {
+					dst->boolean[idx] = 0;
+					return 0;
+				}
+			hid_cfg_error(err_node, "Invalid boolean value: %s\n", text);
+			return -1;
+		case CFN_INTEGER:
+			if ((text[0] == '0') && (text[1] == 'x')) {
+				text += 2;
+				base = 16;
+			}
+			l = strtol(text, &end, base);
+			while(isspace(*end))
+				end++;
+			if (*end == '\0') {
+				dst->integer[idx] = l;
+				return 0;
+			}
+			hid_cfg_error(err_node, "Invalid integer value: %s\n", text);
+			return -1;
+		case CFN_REAL:
+			d = strtod(text, &end);
+			if (*end == '\0') {
+				dst->real[idx] = d;
+				return 0;
+			}
+			hid_cfg_error(err_node, "Invalid numeric value: %s\n", text);
+			return -1;
+		case CFN_COORD:
+			{
+				pcb_bool succ;
+				dst->coord[idx] = GetValue(text, NULL, NULL, &succ);
+				if (!succ)
+					hid_cfg_error(err_node, "Invalid numeric value (coordinate): %s\n", text);
+			}
+			break;
+		case CFN_UNIT:
+			u = get_unit_struct(text);
+			if (u == NULL)
+				hid_cfg_error(err_node, "Invalid unit: %s\n", text);
+			else
+				dst->unit[idx] = u;
+			break;
+		case CFN_INCREMENTS:
+			return conf_parse_increments(&(dst->increments[idx]), err_node);
+		case CFN_COLOR:
+			dst->color[idx] = text; /* let the HID check validity to support flexibility of the format */
+			break;
+		default:
+			/* unknown field type registered in the fields hash: internal error */
+			return -1;
+	}
+	return 0;
+}
+
+int conf_merge_patch_text(conf_native_t *dest, lht_node_t *src, int prio, conf_policy_t pol)
+{
+	if ((pol == POL_DISABLE) || (dest->prop[0].prio > prio))
+		return 0;
+
+	if (dest->random_flags.read_only) {
+		hid_cfg_error(src, "WARNING: not going to overwrite read-only value %s from a config file\n", dest->hash_path);
+		return 0;
+	}
+
+	if (conf_parse_text(&dest->val, 0, dest->type, src->data.text.value, src) == 0) {
+		dest->prop[0].prio = prio;
+		dest->prop[0].src  = src;
+		dest->used         = 1;
+		dest->conf_rev     = conf_rev;
+	}
+	return 0;
+}
+
+static void conf_insert_arr(conf_native_t *dest, int offs)
+{
+#define CASE_MOVE(typ, fld) \
+	case typ: memmove(dest->val.fld+offs, dest->val.fld, dest->used * sizeof(dest->val.fld[0])); return
+
+	memmove(dest->prop+offs, dest->prop, dest->used * sizeof(dest->prop[0]));
+	if (dest->used > 0) {
+		switch(dest->type) {
+			CASE_MOVE(CFN_STRING, string);
+			CASE_MOVE(CFN_BOOLEAN, boolean);
+			CASE_MOVE(CFN_INTEGER, integer);
+			CASE_MOVE(CFN_REAL, real);
+			CASE_MOVE(CFN_COORD, coord);
+			CASE_MOVE(CFN_UNIT, unit);
+			CASE_MOVE(CFN_COLOR, color);
+			CASE_MOVE(CFN_LIST, list);
+			CASE_MOVE(CFN_INCREMENTS, increments);
+		}
+	}
+#undef CASE_MOVE
+	abort(); /* unhandled type */
+}
+
+int conf_merge_patch_array(conf_native_t *dest, lht_node_t *src_lst, int prio, conf_policy_t pol)
+{
+	lht_node_t *s;
+	int res, idx, didx, maxpr;
+
+	if ((pol == POL_DISABLE) || (pol == POL_invalid))
+		return 0;
+
+	if (pol == POL_PREPEND) {
+		for(s = src_lst->data.list.first, maxpr = 0; s != NULL; s = s->next, maxpr++) ;
+
+		if (dest->used+maxpr >= dest->array_size)
+			maxpr = dest->array_size - dest->used - 1;
+
+		conf_insert_arr(dest, maxpr);
+		dest->used += maxpr;
+	}
+/*printf("arr merge prio: %d\n", prio);*/
+	for(s = src_lst->data.list.first, idx = 0; s != NULL; s = s->next, idx++) {
+		if (s->type == LHT_TEXT) {
+			switch(pol) {
+				case POL_PREPEND:
+					if (idx < maxpr)
+						didx = idx;
+					else
+						didx = dest->array_size+1; /* indicate array full */
+					break;
+				case POL_APPEND:
+					didx = dest->used++;
+					break;
+				case POL_OVERWRITE:
+					didx = idx;
+				break;
+				case POL_DISABLE: case POL_invalid: return 0; /* compiler warning */
+			}
+/*printf("   didx: %d / %d '%s'\n", didx, dest->array_size, s->data.text.value);*/
+			if (didx >= dest->array_size) {
+				hid_cfg_error(s, "Array is already full [%d] of [%d] ignored value: '%s' policy=%d\n", dest->used, dest->array_size, s->data.text.value, pol);
+				res = -1;
+				break;
+			}
+
+			if (conf_parse_text(&dest->val, didx, dest->type, s->data.text.value, s) == 0) {
+				dest->prop[didx].prio = prio;
+				dest->prop[didx].src  = s;
+				if (didx >= dest->used)
+					dest->used = didx+1;
+			}
+		}
+		else {
+			hid_cfg_error(s, "List item must be text\n");
+			res = -1;
+		}
+	}
+
+	return res;
+}
+
+int conf_merge_patch_list(conf_native_t *dest, lht_node_t *src_lst, int prio, conf_policy_t pol)
+{
+	lht_node_t *s;
+	int res = 0;
+	conf_listitem_t *i;
+
+	if ((pol == POL_DISABLE) || (pol == POL_invalid))
+		return 0;
+
+	if (pol == POL_OVERWRITE) {
+		/* overwrite the whole list: make it empty then append new elements */
+		while((i = conflist_first(dest->val.list)) != NULL)
+			conflist_remove(i);
+	}
+
+	for(s = src_lst->data.list.first; s != NULL; s = s->next) {
+		if (s->type == LHT_TEXT) {
+			i = calloc(sizeof(conf_listitem_t), 1);
+			i->val.string = &i->payload;
+			i->prop.prio = prio;
+			i->prop.src  = s;
+			if (conf_parse_text(&i->val, 0, CFN_STRING, s->data.text.value, s) != 0) {
+				free(i);
+				continue;
+			}
+
+			switch(pol) {
+				case POL_PREPEND:
+					conflist_insert(dest->val.list, i);
+					dest->used |= 1;
+					break;
+				case POL_APPEND:
+				case POL_OVERWRITE:
+					conflist_append(dest->val.list, i);
+					dest->used |= 1;
+					break;
+				case POL_DISABLE: case POL_invalid: return 0; /* compiler warning */
+			}
+		}
+		else {
+			hid_cfg_error(s, "List item must be text\n");
+			res = -1;
+		}
+	}
+	return res;
+}
+
+int conf_merge_patch_recurse(lht_node_t *sect, int default_prio, conf_policy_t default_policy, const char *path_prefix);
+
+int conf_merge_patch_item(const char *path, lht_node_t *n, int default_prio, conf_policy_t default_policy)
+{
+	conf_native_t *target = conf_get_field(path);
+	int res = 0;
+	switch(n->type) {
+		case LHT_TEXT:
+			if (target == NULL) {
+				if ((strncmp(path, "plugins/", 8) != 0) && (strncmp(path, "utils/", 6) != 0))/* it is normal to have configuration for plugins and utils not loaded - ignore these */
+					hid_cfg_error(n, "conf error: lht->bin conversion: can't find path '%s' - check your lht!\n", path);
+				break;
+			}
+			conf_merge_patch_text(target, n, default_prio, default_policy);
+			break;
+		case LHT_HASH:
+			if (target == NULL) /* no leaf: another level of structs */
+				res |= conf_merge_patch_recurse(n, default_prio, default_policy, path);
+			else /* leaf: pretend it's text so it gets parsed */
+				conf_merge_patch_text(target, n, default_prio, default_policy);
+			break;
+		case LHT_LIST:
+			if (target == NULL)
+				hid_cfg_error(n, "conf error: lht->bin conversion: can't find path '%s' - check your lht; may it be that it should be a hash instead of a list?\n", path);
+			else if (target->type == CFN_LIST)
+				res |= conf_merge_patch_list(target, n, default_prio, default_policy);
+			else if (target->array_size > 1)
+				res |= conf_merge_patch_array(target, n, default_prio, default_policy);
+			else
+				hid_cfg_error(n, "Attempt to initialize a scalar with a list - this node should be a text node\n");
+			break;
+		case LHT_SYMLINK:
+#warning TODO
+			break;
+		case LHT_INVALID_TYPE:
+		case LHT_TABLE:
+			/* intentionally unhandled types */
+			break;
+	}
+	return res;
+}
+
+
+/* merge main subtree of a patch */
+int conf_merge_patch_recurse(lht_node_t *sect, int default_prio, conf_policy_t default_policy, const char *path_prefix)
+{
+	lht_dom_iterator_t it;
+	lht_node_t *n;
+	char path[256], *pathe;
+	char *namee;
+	int nl, ppl = strlen(path_prefix), res = 0;
+
+	memcpy(path, path_prefix, ppl);
+	path[ppl] = '/';
+	pathe = path + ppl + 1;
+
+	for(n = lht_dom_first(&it, sect); n != NULL; n = lht_dom_next(&it)) {
+		nl = strlen(n->name);
+		memcpy(pathe, n->name, nl);
+		namee = pathe+nl;
+		*namee = '\0';
+		res |= conf_merge_patch_item(path, n, default_prio, default_policy);
+	}
+	return res;
+}
+
+int conf_merge_patch(lht_node_t *root, long gprio)
+{
+	conf_policy_t gpolicy;
+	lht_node_t *n;
+	lht_dom_iterator_t it;
+
+	if (root->type != LHT_HASH) {
+		hid_cfg_error(root, "patch root should be a hash\n");
+		return -1;
+	}
+
+	conf_extract_poliprio(root, &gpolicy, &gprio);
+
+	/* iterate over all hashes and insert them recursively */
+	for(n = lht_dom_first(&it, root); n != NULL; n = lht_dom_next(&it))
+		if (n->type == LHT_HASH)
+			conf_merge_patch_recurse(n, gprio, gpolicy, n->name);
+
+	return 0;
+}
+
+typedef struct {
+	long prio;
+	conf_policy_t policy;
+	lht_node_t *subtree;
+} merge_subtree_t;
+
+#define GVT(x) vmst_ ## x
+#define GVT_ELEM_TYPE merge_subtree_t
+#define GVT_SIZE_TYPE size_t
+#define GVT_DOUBLING_THRS 64
+#define GVT_START_SIZE 32
+/*#define GVT_FUNC static*/
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+#include <genvector/genvector_impl.h>
+#include <genvector/genvector_impl.c>
+#include <genvector/genvector_undef.h>
+
+vmst_t merge_subtree; /* automatically initialized to all-zero */
+
+static void add_subtree(int role, lht_node_t *subtree_parent_root, lht_node_t *subtree_root)
+{
+	merge_subtree_t *m;
+
+	m = vmst_alloc_append(&merge_subtree, 1);
+	m->prio = conf_default_prio[role];
+	m->policy = POL_invalid;
+
+	conf_extract_poliprio(subtree_parent_root, &m->policy, &m->prio);
+	m->subtree = subtree_root;
+}
+
+static int mst_prio_cmp(const void *a, const void *b)
+{
+	const merge_subtree_t *s1 = a, *s2 = b;
+	if (s1->prio > s2->prio)
+		return 1;
+	return -1;
+}
+
+int conf_merge_all(const char *path)
+{
+	int n, ret = 0;
+	vmst_truncate(&merge_subtree, 0);
+
+	for(n = 0; n < CFR_max_real; n++) {
+		lht_node_t *cr, *r, *r2;
+		if (conf_root[n] == NULL)
+			continue;
+
+		cr = conf_lht_get_confroot(conf_root[n]->root);
+		if (cr == NULL)
+			continue;
+
+		for(r = cr->data.list.first; r != NULL; r = r->next) {
+			if (path != NULL) {
+				r2 = lht_tree_path_(r->doc, r, path, 1, 0, NULL);
+				if (r2 != NULL)
+					add_subtree(n, r, r2);
+			}
+			else
+				add_subtree(n, r, r);
+		}
+	}
+
+	qsort(merge_subtree.array, vmst_len(&merge_subtree), sizeof(merge_subtree_t), mst_prio_cmp);
+	for(n = 0; n < vmst_len(&merge_subtree); n++) {
+		if (path != NULL)
+			ret |= conf_merge_patch_item(path, merge_subtree.array[n].subtree, merge_subtree.array[n].prio, merge_subtree.array[n].policy);
+		else
+			ret |= conf_merge_patch(merge_subtree.array[n].subtree, merge_subtree.array[n].prio);
+	}
+	return ret;
+}
+
+static void conf_field_clear(conf_native_t *f)
+{
+	if (strncmp(f->hash_path, "temp", 4) == 0)
+		return;
+	if (f->used > 0) {
+#define clr(field) memset(f->val.field, 0, sizeof(*(f->val.field)) * f->used)
+		switch(f->type) {
+			case CFN_STRING:      clr(string); break;
+			case CFN_BOOLEAN:     clr(boolean); break;
+			case CFN_INTEGER:     clr(integer); break;
+			case CFN_REAL:        clr(real); break;
+			case CFN_COORD:       clr(coord); break;
+			case CFN_UNIT:        clr(unit); break;
+			case CFN_COLOR:       clr(color); break;
+			case CFN_INCREMENTS:  clr(increments); break;
+			case CFN_LIST:
+				while(conflist_first(f->val.list)) { /* need to free all items of a list before clearing the main struct */
+					conf_listitem_t *i = conflist_first(f->val.list);
+					conflist_remove(i);
+					free(i);
+				}
+				clr(list);
+				break;
+		}
+		memset(f->prop, 0, sizeof(confprop_t) * f->used);
+#undef clr
+	}
+
+	f->used     = 0;
+	f->conf_rev = conf_rev;
+}
+
+int conf_rev = 0;
+void conf_update(const char *path)
+{
+	conf_native_t *n;
+
+	/* clear memory-bin data first */
+	if (path == NULL) {
+		htsp_entry_t *e;
+		conf_fields_foreach(e) {
+			conf_hid_global_cb((conf_native_t *)e->value, val_change_pre);
+			conf_hid_local_cb((conf_native_t *)e->value, val_change_pre);
+			conf_field_clear(e->value);
+		}
+	}
+	else {
+		n = conf_get_field(path);
+		if (n == NULL) {
+			char *path_, *field;
+			path_ = pcb_strdup(path);
+
+			/* It might be an array element - truncate */
+			field = strrchr(path_, '[');
+			if (field != NULL) {
+				*field = '\0';
+				n = conf_get_field(path_);
+			}
+
+			/* It may be a field of an increment, truncate last portion */
+			if (n == NULL) {
+				field = strrchr(path_, '/');
+				if (field == NULL) {
+					free(path_);
+					return;
+				}
+				*field = '\0';
+				n = conf_get_field(path_);
+				if (n->type != CFN_INCREMENTS)
+					n = NULL;
+			}
+
+			/* if a valid node is found, update it */
+			if (n != NULL)
+				conf_update(path_);
+
+			free(path_);
+			return;
+		}
+		conf_field_clear(n);
+		conf_hid_global_cb(n, val_change_pre);
+		conf_hid_local_cb(n, val_change_pre);
+	}
+
+	/* merge all memory-lht data to memory-bin */
+	conf_merge_all(path);
+	conf_core_postproc();
+
+	if (path == NULL) {
+		htsp_entry_t *e;
+		conf_fields_foreach(e) {
+			conf_hid_local_cb((conf_native_t *)e->value, val_change_post);
+			conf_hid_global_cb((conf_native_t *)e->value, val_change_post);
+		}
+	}
+	else {
+		conf_hid_local_cb(n, val_change_post);
+		conf_hid_global_cb(n, val_change_post);
+	}
+	conf_rev++;
+}
+
+static lht_node_t *conf_lht_get_first_(lht_node_t *cwd)
+{
+	/* assume root is a li and add to the first hash */
+	cwd = conf_lht_get_confroot(cwd);
+	if (cwd == NULL)
+		return NULL;
+	cwd = cwd->data.list.first;
+	if ((cwd == NULL) || (cwd->type != LHT_HASH))
+		return NULL;
+	return cwd;
+}
+
+lht_node_t *conf_lht_get_first(conf_role_t target)
+{
+	assert(target != CFR_invalid);
+	assert(target >= 0);
+	assert(target < CFR_max_alloc);
+	if (conf_root[target] == NULL)
+		return NULL;
+	return conf_lht_get_first_(conf_root[target]->root);
+}
+
+static lht_node_t *conf_lht_get_at_(conf_role_t target, const char *conf_path, const char *lht_path, int create)
+{
+	lht_node_t *n, *r;
+	n = conf_lht_get_first(target);
+	if (n == NULL)
+		return NULL;
+	r = lht_tree_path_(n->doc, n, lht_path, 1, 0, NULL);
+	if ((r == NULL) && (create)) {
+		conf_set_dry(target, conf_path, -1, "", POL_OVERWRITE);
+		r = lht_tree_path_(n->doc, n, lht_path, 1, 0, NULL);
+	}
+	return r;
+}
+
+lht_node_t *conf_lht_get_at(conf_role_t target, const char *path, int create)
+{
+	lht_node_t *r;
+	char *pc, *end;
+	if (conf_root[target] == NULL) {
+		if (!create)
+			return NULL;
+		conf_reset(target, "<conf_lht_get_at>");
+	}
+	end = strchr(path, '[');
+	if (end == NULL) {
+		r = conf_lht_get_at_(target, path, path, create);
+	}
+	else {
+		/* lihata syntax differs from conf syntax in array indexing */
+		pc = pcb_strdup(path);
+		pc[end-path] = '/';
+		end = strchr(pc+(end-path), ']');
+		if (end != NULL)
+			*end = '\0';
+		r = conf_lht_get_at_(target, path, pc, create);
+		free(pc);
+	}
+	return r;
+}
+
+
+void conf_load_all(const char *project_fn, const char *pcb_fn)
+{
+	int i;
+	lht_node_t *dln;
+	const char *pc, *try;
+
+	/* get the lihata node for design/default_layer_name */
+	conf_load_as(CFR_INTERNAL, conf_internal, 1);
+	dln = conf_lht_get_at(CFR_INTERNAL, "design/default_layer_name", 1);
+	assert(dln != NULL);
+	assert(dln->type == LHT_LIST);
+	dln = dln->data.list.first;
+
+	/* Set up default layer names - make sure there are enough layers (over the hardwired ones, if any) */
+	for (i = 0; i < MAX_LAYER; i++) {
+		char buf[20];
+		if (dln == NULL) {
+			sprintf(buf, "signal%d", i + 1);
+			if (conf_set_dry(CFR_INTERNAL, "design/default_layer_name", i, buf, POL_OVERWRITE) != 0)
+				printf("Can't set layer name\n");
+		}
+		else
+			dln = dln->next;
+	}
+
+	/* load config files */
+	conf_load_as(CFR_SYSTEM, PCBSHAREDIR "/pcb-conf.lht", 0);
+	conf_load_as(CFR_USER, conf_user_fn, 0);
+	pc = conf_get_project_conf_name(project_fn, pcb_fn, &try);
+	if (pc != NULL)
+		conf_load_as(CFR_PROJECT, pc, 0);
+	conf_merge_all(NULL);
+
+	/* create the user config (in-memory-lht) if it does not exist on disk;
+	   this is needed so if the user makes config changes from the GUI things
+	   get saved. */
+	if (conf_root[CFR_USER] == NULL)
+		conf_reset(CFR_USER, conf_user_fn);
+}
+
+void conf_load_project(const char *project_fn, const char *pcb_fn)
+{
+	const char *pc, *try;
+
+	assert((project_fn != NULL) || (pcb_fn != NULL));
+
+	pc = conf_get_project_conf_name(project_fn, pcb_fn, &try);
+	if (pc != NULL)
+		if (conf_load_as(CFR_PROJECT, pc, 0) != 0)
+			pc = NULL;
+	if (pc == NULL)
+		conf_reset(CFR_PROJECT, "<conf_load_project>");
+	conf_update(NULL);
+}
+
+void conf_reg_field_(void *value, int array_size, conf_native_type_t type, const char *path, const char *desc, conf_flag_t flags)
+{
+	conf_native_t *node;
+
+	if (conf_fields == NULL) {
+		conf_fields = htsp_alloc(strhash, strkeyeq);
+		assert(conf_fields != NULL);
+	}
+	assert(array_size >= 1);
+
+	assert(htsp_get(conf_fields, path) == NULL);
+
+	node = calloc(sizeof(conf_native_t), 1);
+	node->array_size  = array_size;
+	node->type        = type;
+	node->val.any     = value;
+	node->prop        = calloc(sizeof(confprop_t), array_size);
+	node->description = desc;
+	node->hash_path   = path;
+	node->flags       = flags;
+	vtp0_init(&(node->hid_data));
+
+	htsp_set(conf_fields, (char *)path, node);
+}
+
+void conf_free_native(conf_native_t *node)
+{
+	if (node->type == CFN_LIST) {
+		while(conflist_first(node->val.list) != NULL) {
+			conf_listitem_t *first = conflist_first(node->val.list);
+			conflist_remove(first);
+			free(first);
+		}
+	}
+
+	vtp0_uninit(&(node->hid_data));
+	free(node->prop);
+	free(node);
+}
+
+void conf_unreg_fields(const char *prefix)
+{
+	int len = strlen(prefix);
+	htsp_entry_t *e;
+
+	assert(prefix[len-1] == '/');
+
+	conf_fields_foreach(e) {
+		if (strncmp(e->key, prefix, len) == 0) {
+			conf_free_native(e->value);
+			htsp_delentry(conf_fields, e);
+		}
+	}
+}
+
+conf_native_t *conf_get_field(const char *path)
+{
+	return htsp_get(conf_fields, path);
+}
+
+int conf_set_dry(conf_role_t target, const char *path_, int arr_idx, const char *new_val, conf_policy_t pol)
+{
+	char *path, *basename, *next, *last, *sidx, *increment_field = NULL;
+	conf_native_t *nat;
+	lht_node_t *cwd, *nn;
+	lht_node_type_t ty;
+	int idx = -1;
+
+	/* Remove in overwrite only */
+	if ((new_val == NULL) && (pol != POL_OVERWRITE))
+		return -1;
+
+	path = pcb_strdup(path_);
+	sidx = strrchr(path, '[');
+	if (sidx != NULL) {
+		char *end;
+		*sidx = '\0';
+		sidx++;
+		idx = strtol(sidx, &end, 10);
+		if ((*end != ']') || (strchr(sidx, '/') != NULL)) {
+			free(path);
+			return -1;
+		}
+		if ((arr_idx >= 0) && (arr_idx != idx)) {
+			free(path);
+			return -1;
+		}
+	}
+	else if (arr_idx >= 0)
+		idx = arr_idx;
+
+	nat = conf_get_field(path);
+	if (nat == NULL) {
+		/* might be increments */
+		increment_field = strrchr(path, '/');
+		if (increment_field == NULL) {
+			free(path);
+			return -1;
+		}
+		*increment_field = '\0';
+		nat = conf_get_field(path);
+		if ((nat == NULL) || (nat->type != CFN_INCREMENTS)) {
+			free(path);
+			return -1;
+		}
+		*increment_field = '/';
+		increment_field++;
+	}
+
+
+	if (conf_root[target] == NULL) {
+		free(path);
+		return -1;
+	}
+	if (pol == POL_DISABLE) {
+		free(path);
+		return 0;
+	}
+	if ((pol != POL_OVERWRITE) && (idx >= 0)) {
+		free(path);
+		return -1;
+	}
+
+	cwd = conf_lht_get_first(target);
+	if (cwd == NULL) {
+		free(path);
+		return -1;
+	}
+
+	if (idx >= nat->array_size) {
+		Message(PCB_MSG_DEFAULT, "Error: can't conf_set() %s[%d]: %d is beyond the end of the array (%d)\n", path, idx, idx, nat->array_size);
+		free(path);
+		return -1;
+	}
+
+	basename = strrchr(path, '/');
+	if (basename == NULL) {
+		free(path);
+		return -1;
+	}
+	*basename = '\0';
+	basename++;
+
+	/* create parents if they do not exist */
+	last = next = path;
+	do {
+		next = strchr(last, '/');
+		if (next != NULL)
+			*next = '\0';
+
+		nn = lht_tree_path_(conf_root[target], cwd, last, 1, 0, NULL);
+		if (nn == NULL) {
+			if (new_val == NULL) {
+				free(path);
+				return 0;
+			}
+			if (conf_root_lock[target]) {
+				Message(PCB_MSG_DEFAULT, "WARNING: can't set config item %s because target in-memory lihata does not have the node and is tree-locked\n", path_);
+				free(path);
+				return -1;
+			}
+			/* create a new hash node */
+			nn = lht_dom_node_alloc(LHT_HASH, last);
+			if (lht_dom_hash_put(cwd, nn) != LHTE_SUCCESS) {
+				lht_dom_node_free(nn);
+				free(path);
+				return -1;
+			}
+		}
+		cwd = nn;
+		if (next != NULL)
+			last = next+1;
+	} while(next != NULL);
+
+	/* add the last part of the path, which is either a list or a text node */
+	if ((nat->array_size > 1) || (nat->type == CFN_LIST))
+		ty = LHT_LIST;
+	else
+		ty = LHT_TEXT;
+
+	nn = lht_tree_path_(conf_root[target], cwd, basename, 1, 0, NULL);
+	if (nn == NULL) {
+		if (conf_root_lock[target]) {
+			free(path);
+			return -1;
+		}
+		nn = lht_dom_node_alloc(ty, basename);
+		if (lht_dom_hash_put(cwd, nn) != LHTE_SUCCESS) {
+			lht_dom_node_free(nn);
+			free(path);
+			return -1;
+		}
+	}
+	cwd = nn;
+
+	/* set value */
+	if (ty == LHT_LIST) {
+		lht_err_t err = 0;
+
+		if (new_val != NULL)
+			nn = lht_dom_node_alloc(LHT_TEXT, "");
+
+		if (pol == POL_OVERWRITE) {
+			if (idx == -1) {
+				/* empty the list so that we insert to an empty list which is overwriting the list */
+				while(cwd->data.list.first != NULL)
+					lht_tree_del(cwd->data.list.first);
+				if (new_val != NULL)
+					lht_dom_list_append(cwd, nn);
+			}
+			else {
+				lht_node_t *old = lht_tree_list_nth(cwd, idx);
+				if (old != NULL) {
+					/* the list is large enough already: overwrite the element at idx */
+					if (new_val == NULL) {
+						err = lht_tree_del(old);
+						free(path);
+						return 0;
+					}
+					else
+						err = lht_tree_list_replace_child(cwd, old, nn);
+				}
+				else if (new_val == NULL) {
+					free(path);
+					return 0;
+				}
+				else {
+					int n;
+					lht_node_t *i;
+					/* count members */
+					for (n = 0, i = cwd->data.list.first; i != NULL; i = i->next) n++;
+					/* append just enough elements to get one less than needed */
+					err = 0;
+					for(n = idx - n; n > 0; n--) {
+						lht_node_t *dummy = lht_dom_node_alloc(LHT_TEXT, "");
+						err |= lht_dom_list_append(cwd, dummy);
+					}
+					/* append the new node */
+					err |= lht_dom_list_append(cwd, nn);
+				}
+			}
+		}
+		else if ((pol == POL_PREPEND) || (pol == POL_OVERWRITE))
+			err = lht_dom_list_insert(cwd, nn);
+		else if (pol == POL_APPEND)
+			err = lht_dom_list_append(cwd, nn);
+		if (err != LHTE_SUCCESS) {
+			lht_dom_node_free(nn);
+			free(path);
+			return -1;
+		}
+		cwd = nn;
+	}
+	else {
+		if (idx > 0) {
+			free(path);
+			return -1; /* only lists/array path should have index larger than 0 */
+		}
+		cwd = nn;
+	}
+
+	/* by now cwd is the text node we need to load with the new value; it is
+	   either a text config value under a hash or a list item already allocated */
+	if (cwd->type != LHT_TEXT) {
+		free(path);
+		return -1;
+	}
+
+	if (new_val != NULL) {
+		if (cwd->data.text.value != NULL)
+			free(cwd->data.text.value);
+
+		cwd->data.text.value = pcb_strdup(new_val);
+		cwd->file_name = conf_root[target]->active_file;
+	}
+	else
+		lht_tree_del(cwd);
+
+	conf_lht_dirty[target]++;
+
+	free(path);
+	return 0;
+}
+
+int conf_set(conf_role_t target, const char *path, int arr_idx, const char *new_val, conf_policy_t pol)
+{
+	int res;
+	res = conf_set_dry(target, path, arr_idx, new_val, pol);
+	if (res < 0)
+		return res;
+	conf_update(path);
+	return 0;
+}
+
+int conf_del(conf_role_t target, const char *path, int arr_idx)
+{
+	int res;
+	res = conf_set_dry(target, path, arr_idx, NULL, POL_OVERWRITE);
+	if (res < 0)
+		return res;
+	conf_update(path);
+	return 0;
+}
+
+
+int conf_set_native(conf_native_t *field, int arr_idx, const char *new_val)
+{
+	lht_node_t *node;
+
+	if (arr_idx >= field->used)
+		return -1;
+
+	node = field->prop[arr_idx].src;
+
+	if (node->data.text.value != NULL)
+		free(node->data.text.value);
+
+	node->data.text.value = pcb_strdup(new_val);
+	return 0;
+}
+
+
+int conf_set_from_cli(const char *prefix, const char *arg_, const char *val, const char **why)
+{
+	char *arg = NULL, *op, *s;
+	const char *sc;
+	conf_policy_t pol = POL_OVERWRITE;
+	int ret;
+
+	if (prefix != NULL) {
+		for(sc = arg_; (*sc != '=') && (*sc != '\0'); sc++)
+			if (*sc == '/')
+				arg = pcb_strdup(arg_); /* full path, don't use prefix */
+
+		if (arg == NULL) { /* insert prefix */
+			int pl = strlen(prefix), al = strlen(arg_);
+			while((pl > 0) && (prefix[pl-1] == '/')) pl--;
+			arg = malloc(pl+al+2);
+			memcpy(arg, prefix, pl);
+			arg[pl] = '/';
+			strcpy(arg+pl+1, arg_);
+		}
+	}
+	else
+		arg = pcb_strdup(arg_);
+
+	/* replace any - with _ in the path part; cli accepts dash but the backing C
+	   struct field names don't */
+	for(s = arg, op = NULL; (*s != '\0') && (op == NULL); s++) {
+		if (*s == '=')
+			op = s;
+		else if (*s == '-')
+			*s = '_';
+	}
+
+	/* extract val, if we need to */
+	if (val == NULL) {
+		*why = "";
+		if (op == arg) {
+			free(arg);
+			*why = "value not specified; syntax is path=val";
+			return -1;
+		}
+		if (op == NULL) {
+			conf_native_t *n = conf_get_field(arg);
+			if ((n == NULL) || (n->type != CFN_BOOLEAN)) {
+				free(arg);
+				*why = "value not specified for non-boolean variable; syntax is path=val";
+				return -1;
+			}
+			val = "1"; /* auto-value for boolean */
+		}
+		else {
+			*op = '\0';
+			val = op+1;
+			op--;
+			switch(*op) {
+				case '+': pol = POL_APPEND; *op = '\0'; break;
+				case '^': pol = POL_PREPEND; *op = '\0'; break;
+			}
+		}
+	}
+
+	/* now that we have a clean path (arg) and a value, try to set the config */
+	ret = conf_set(CFR_CLI, arg, -1, val, pol);
+	if (ret != 0)
+		*why = "invalid config path";
+
+	free(arg);
+	return ret;
+}
+
+void conf_parse_arguments(const char *prefix, int *argc, char ***argv)
+{
+	int n, dst;
+
+	for(n = 0, dst = 0; n < *argc; n++,dst++) {
+		char *a = (*argv)[n];
+		const char *why;
+		int try_again = -1;
+
+		if (a[0] == '-') a++;
+		if (a[0] == '-') a++;
+
+		try_again = conf_set_from_cli(prefix, a, NULL, &why);
+		if (try_again && (n < (*argc) - 1)) {
+			try_again = conf_set_from_cli(prefix, a, (*argv)[n+1], &why);
+			if (!try_again)
+				n++;
+		}
+		if (try_again)
+			(*argv)[dst] = (*argv)[n];
+		else
+			dst--;
+	}
+	*argc = dst;
+}
+
+void conf_usage(const char *prefix, void (*print)(const char *name, const char *help))
+{
+	htsp_entry_t *e;
+	int pl = (prefix == NULL ? 0 : strlen(prefix));
+
+	conf_fields_foreach(e) {
+		if ((prefix == NULL) || (strncmp(prefix, e->key, pl) == 0)) {
+			conf_native_t *n = e->value;
+			if (n->flags & CFF_USAGE) {
+				int kl = strlen(n->hash_path);
+				char *s, *name = malloc(kl+32);
+				const char *sc;
+				for(sc = n->hash_path + pl; *sc == '/'; sc++) ;
+				name[0] = '-';
+				name[1] = '-';
+				strcpy(name+2, sc);
+				for(s = name; *s != '\0'; s++)
+					if (*s == '_')
+						*s = '-';
+				switch(n->type) {
+					case CFN_BOOLEAN: /* implicit true */ break;
+					case CFN_STRING:  strcpy(s, " str"); break;
+					case CFN_INTEGER: strcpy(s, " int"); break;
+					case CFN_REAL:    strcpy(s, " num"); break;
+					case CFN_COORD:   strcpy(s, " coord"); break;
+					case CFN_UNIT:    strcpy(s, " unit"); break;
+					case CFN_COLOR:   strcpy(s, " color"); break;
+					case CFN_LIST:
+					case CFN_INCREMENTS:
+						strcpy(s, " ???");
+						break;
+				}
+				print(name, n->description);
+				free(name);
+			}
+		}
+	}
+}
+
+int conf_replace_subtree(conf_role_t dst_role, const char *dst_path, conf_role_t src_role, const char *src_path)
+{
+	lht_node_t *dst = conf_lht_get_at(dst_role, dst_path, 1);
+	lht_node_t *src, *new_src = NULL;
+
+	if (src_role == CFR_binary) {
+		char *name;
+		lht_node_t *ch = NULL;
+		int isarr, i;
+		conf_native_t *n = conf_get_field(src_path);
+
+		if (n == NULL)
+			return -1;
+		name = strrchr(n->hash_path, '/');
+		if (name == NULL)
+			return -1;
+
+		isarr = n->array_size > 1;
+		if (isarr)
+			src = lht_dom_node_alloc(LHT_LIST, name+1);
+
+		for(i = 0; i < n->array_size; i++) {
+			gds_t s;
+
+			gds_init(&s);
+			if (n->type == CFN_LIST) {
+				conf_listitem_t *it;
+				ch = lht_dom_node_alloc(LHT_LIST, name+1);
+				for(it = conflist_first(n->val.list); it != NULL; it = conflist_next(it)) {
+					lht_node_t *txt;
+					txt = lht_dom_node_alloc(LHT_TEXT, "");
+					txt->data.text.value = pcb_strdup(it->payload);
+					lht_dom_list_append(ch, txt);
+				}
+			}
+			else {
+				conf_print_native_field((conf_pfn)pcb_append_printf, &s, 0, &n->val, n->type, n->prop+i, i);
+				ch = lht_dom_node_alloc(LHT_TEXT, isarr ? "" : name+1);
+				ch->data.text.value = s.array;
+				if (isarr)
+					lht_dom_list_append(src, ch);
+				s.array = NULL;
+				gds_uninit(&s);
+			}
+		}
+
+		if (!isarr)
+			src = ch;
+	}
+	else
+		src = conf_lht_get_at(src_role, src_path, 0);
+
+	if ((src == NULL) && (dst != NULL)) {
+		lht_tree_del(dst);
+		return 0;
+	}
+
+	if (dst == NULL)
+		goto err;
+
+	new_src = lht_dom_duptree(src);
+	if (new_src == NULL)
+		goto err;
+
+	if (lht_tree_replace(dst, new_src) != LHTE_SUCCESS)
+		goto err;
+
+	conf_lht_dirty[dst_role]++;
+
+	lht_tree_del(dst);
+	if (src_role == CFR_binary)
+		lht_dom_node_free(src);
+	return 0;
+
+	err:;
+	if (src_role == CFR_binary)
+		lht_dom_node_free(src);
+	if (new_src != NULL)
+		lht_dom_node_free(new_src);
+	return -1;
+}
+
+
+int conf_save_file(const char *project_fn, const char *pcb_fn, conf_role_t role, const char *fn)
+{
+	int fail = 1;
+	lht_node_t *r = conf_lht_get_first(role);
+	const char *try;
+	char *efn;
+
+	/* do not save if there's no change */
+	if (conf_lht_dirty[role] == 0)
+		return 0;
+
+	if (fn == NULL) {
+		switch(role) {
+			case CFR_USER:
+				fn = conf_user_fn;
+				break;
+			case CFR_PROJECT:
+				fn = conf_get_project_conf_name(project_fn, pcb_fn, &try);
+				if (fn == NULL) {
+					Message(PCB_MSG_DEFAULT, "Error: can not save config to project file: %s does not exist - please create an empty file there first\n", try);
+					return -1;
+				}
+				break;
+			default: return -1;
+		}
+	}
+
+	resolve_path(fn, &efn, 0);
+
+	if (r != NULL) {
+		FILE *f;
+		f = fopen(efn, "w");
+
+		if ((f == NULL) && (role == CFR_USER)) {
+			/* create the directory and try again */
+			char *path = pcb_strdup(efn), *end;
+			end = strrchr(path, '/');
+			if (end != NULL) {
+				*end = '\0';
+				if (pcb_mkdir(path, 0755) == 0) {
+					Message(PCB_MSG_DEFAULT, "Created directory %s for saving %s\n", path, fn);
+					f = fopen(efn, "w");
+				}
+				else
+					Message(PCB_MSG_DEFAULT, "Error: failed to create directory %s for saving %s\n", path, efn);
+			}
+			free(path);
+		}
+
+		if (f != NULL) {
+#warning CONF TODO: a project file needs to be loaded, merged, then written (to preserve non-config nodes)
+			lht_dom_export(r->doc->root, f, "");
+			fail = 0;
+			conf_lht_dirty[role] = 0;
+			fclose(f);
+		}
+		else
+			Message(PCB_MSG_DEFAULT, "Error: can't save config to %s - can't open the file for write\n", fn);
+	}
+
+	free(efn);
+	return fail;
+}
+
+int conf_export_to_file(const char *fn, conf_role_t role, const char *conf_path)
+{
+	lht_node_t *at = conf_lht_get_at(role, conf_path, 0);
+	lht_err_t r;
+	FILE *f;
+
+	if (at == NULL)
+		return -1;
+
+	f = fopen(fn, "w");
+	if (f == NULL)
+		return -1;
+
+	r = lht_dom_export(at, f, "");
+	fclose(f);
+	return r;
+}
+
+
+conf_listitem_t *conf_list_first_str(conflist_t *list, const char **item_str, int *idx)
+{
+	conf_listitem_t *item_li;
+	item_li = conflist_first(list);
+	if (item_li == NULL)
+		return NULL;
+	if (item_li->type == CFN_STRING) {
+		*item_str = item_li->val.string[0];
+		return item_li;
+	}
+	return conf_list_next_str(item_li, item_str, idx);
+}
+
+conf_listitem_t *conf_list_next_str(conf_listitem_t *item_li, const char **item_str, int *idx)
+{
+	while((item_li = conflist_next(item_li)) != NULL) {
+		if (item_li->type != CFN_STRING)
+			continue;
+		/* found next string */
+		*item_str = item_li->val.string[0];
+		return item_li;
+	}
+	/* found end of the list */
+	*item_str = NULL;
+	return item_li;
+}
+
+const char *conf_concat_strlist(const conflist_t *lst, gds_t *buff, int *inited, char sep)
+{
+	int n;
+	conf_listitem_t *ci;
+
+	if ((inited == NULL) || (!*inited)) {
+		gds_init(buff);
+		if (inited != NULL)
+			*inited = 1;
+	}
+	else
+		gds_truncate(buff, 0);
+
+	for (n = 0, ci = conflist_first((conflist_t *)lst); ci != NULL; ci = conflist_next(ci), n++) {
+		const char *p = ci->val.string[0];
+		if (ci->type != CFN_STRING)
+			continue;
+		if (n > 0)
+			gds_append(buff, sep);
+		gds_append_str(buff, p);
+	}
+	return buff->array;
+}
+
+void conf_lock(conf_role_t target)
+{
+	conf_root_lock[target] = 1;
+}
+
+void conf_unlock(conf_role_t target)
+{
+	conf_root_lock[target] = 0;
+}
+
+int conf_islocked(conf_role_t target)
+{
+	return conf_root_lock[target];
+}
+
+int conf_isdirty(conf_role_t target)
+{
+	return conf_lht_dirty[target];
+}
+
+void conf_makedirty(conf_role_t target)
+{
+	conf_lht_dirty[target]++;
+}
+conf_role_t conf_lookup_role(const lht_node_t *nd)
+{
+	conf_role_t r;
+	for(r = 0; r < CFR_max_real; r++)
+		if (conf_root[r] == nd->doc)
+			return r;
+
+	return CFR_invalid;
+}
+
+void conf_reset(conf_role_t target, const char *source_fn)
+{
+	lht_node_t *n;
+
+	if (conf_root[target] != NULL)
+		lht_dom_uninit(conf_root[target]);
+
+	conf_root[target] = lht_dom_init();
+	lht_dom_loc_newfile(conf_root[target], source_fn);
+	conf_root[target]->root = lht_dom_node_alloc(LHT_LIST, conf_list_name);
+	conf_root[target]->root->doc = conf_root[target];
+	n = lht_dom_node_alloc(LHT_HASH, "overwrite");
+	lht_dom_list_insert(conf_root[target]->root, n);
+}
+
+/*****************/
+static int needs_braces(const char *s)
+{
+	for(; *s != '\0'; s++)
+		if (!isalnum(*s) && (*s != '_') && (*s != '-') && (*s != '+') && (*s != '/') && (*s != ':') && (*s != '.') && (*s != ',') && (*s != '$') && (*s != '(') && (*s != ')') && (*s != '~'))
+			return 1;
+	return 0;
+}
+
+#define print_str_or_null(pfn, ctx, verbose, chk, out) \
+	do { \
+			if (chk == NULL) { \
+				if (verbose) \
+					ret += pfn(ctx, "<NULL>");\
+			} \
+			else {\
+				if (needs_braces(out)) \
+					ret += pfn(ctx, "{%s}", out); \
+				else \
+					ret += pfn(ctx, "%s", out); \
+			} \
+	} while(0)
+
+int conf_print_native_field(conf_pfn pfn, void *ctx, int verbose, confitem_t *val, conf_native_type_t type, confprop_t *prop, int idx)
+{
+	int ret = 0;
+	switch(type) {
+		case CFN_STRING:  print_str_or_null(pfn, ctx, verbose, val->string[idx], val->string[idx]); break;
+		case CFN_BOOLEAN: ret += pfn(ctx, "%d", val->boolean[idx]); break;
+		case CFN_INTEGER: ret += pfn(ctx, "%ld", val->integer[idx]); break;
+		case CFN_REAL:    ret += pfn(ctx, "%f", val->real[idx]); break;
+		case CFN_COORD:   ret += pfn(ctx, "%$mS", val->coord[idx]); break;
+		case CFN_UNIT:    print_str_or_null(pfn, ctx, verbose, val->unit[idx], val->unit[idx]->suffix); break;
+		case CFN_COLOR:   print_str_or_null(pfn, ctx, verbose, val->color[idx], val->color[idx]); break;
+		case CFN_INCREMENTS:
+			{
+				Increments *i = &val->increments[idx];
+				ret += pfn(ctx, "{ grid=%$mS/%$mS/%$mS size=%$mS/%$mS/%$mS line=%$mS/%$mS/%$mS clear=%$mS/%$mS/%$mS}",
+				i->grid, i->grid_min, i->grid_max,
+				i->size, i->size_min, i->size_max,
+				i->line, i->line_min, i->line_max,
+				i->clear, i->clear_min, i->clear_max);
+			}
+			break;
+		case CFN_LIST:
+			{
+				conf_listitem_t *n;
+				if (conflist_length(val->list) > 0) {
+					ret += pfn(ctx, "{");
+					for(n = conflist_first(val->list); n != NULL; n = conflist_next(n)) {
+						conf_print_native_field(pfn, ctx, verbose, &n->val, n->type, &n->prop, 0);
+						ret += pfn(ctx, ";");
+					}
+					ret += pfn(ctx, "}");
+				}
+				else {
+					if (verbose)
+						ret += pfn(ctx, "<empty list>");
+					else
+						ret += pfn(ctx, "{}");
+				}
+			}
+			break;
+	}
+	if (verbose) {
+		ret += pfn(ctx, " <<prio=%d", prop[idx].prio);
+		if (prop[idx].src != NULL) {
+			ret += pfn(ctx, " from=%s:%d", prop[idx].src->file_name, prop[idx].src->line);
+		}
+		ret += pfn(ctx, ">>");
+	}
+	return ret;
+}
+
+int conf_print_native(conf_pfn pfn, void *ctx, const char * prefix, int verbose, conf_native_t *node)
+{
+	int ret = 0;
+	if ((node->used <= 0) && (!verbose))
+		return 0;
+	if (node->array_size > 1) {
+		int n;
+		if (!verbose)
+			ret += pfn(ctx, "{");
+		for(n = 0; n < node->used; n++) {
+			if (verbose)
+				ret += pfn(ctx, "%s I %s[%d] = ", prefix, node->hash_path, n);
+			ret += conf_print_native_field(pfn, ctx, verbose, &node->val, node->type, node->prop, n);
+			if (verbose)
+				ret += pfn(ctx, " conf_rev=%d\n", node->conf_rev);
+			else
+				ret += pfn(ctx, ";");
+		}
+		if (verbose) {
+			if (node->used == 0)
+				ret += pfn(ctx, "%s I %s[] = <empty>\n", prefix, node->hash_path);
+		}
+		else
+			ret += pfn(ctx, "}");
+	}
+	else {
+		if (verbose)
+			ret += pfn(ctx, "%s I %s = ", prefix, node->hash_path);
+		ret += conf_print_native_field(pfn, ctx, verbose, &node->val, node->type, node->prop, 0);
+		if (verbose)
+			ret += pfn(ctx, " conf_rev=%d\n", node->conf_rev);
+	}
+	return ret;
+}
+
+
+/****************/
+
+void conf_init(void)
+{
+	conf_reset(CFR_ENV, "<environment-variables>");
+	conf_reset(CFR_CLI, "<commandline>");
+	conf_reset(CFR_DESIGN, "<null-design>");
+
+	conf_reset(CFR_file, "<conf_init>");
+	conf_reset(CFR_binary, "<conf_init>");
+}
+
+void conf_uninit(void)
+{
+	int n;
+	htsp_entry_t *e;
+
+	conf_hid_uninit();
+
+	for(n = 0; n < CFR_max_alloc; n++)
+		if (conf_root[n] != NULL)
+			lht_dom_uninit(conf_root[n]);
+
+	conf_fields_foreach(e) {
+		conf_free_native(e->value);
+		htsp_delentry(conf_fields, e);
+	}
+	htsp_free(conf_fields);
+
+	vmst_uninit(&merge_subtree);
+}
+
+void conf_setf(conf_role_t role, const char *path, int idx, const char *fmt, ...)
+{
+	char *tmp;
+	va_list ap;
+
+	va_start(ap, fmt);
+	tmp = pcb_strdup_vprintf(fmt, ap);
+	va_end(ap);
+
+	conf_set(role, path, idx, tmp, POL_OVERWRITE);
+
+	free(tmp);
+}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644
index 0000000..9a33545
--- /dev/null
+++ b/src/conf.h
@@ -0,0 +1,358 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_CONF_H
+#define PCB_CONF_H
+#include "global.h"
+#include "global_typedefs.h"
+#include "pcb-printf.h"
+#include <liblihata/lihata.h>
+#include <liblihata/dom.h>
+#include <genvector/vtp0.h>
+#include "unit.h"
+
+typedef union confitem_u confitem_t ;
+typedef struct conf_listitem_s conf_listitem_t;
+
+#include "list_conf.h"
+
+extern int conf_rev; /* increased by one each time there's a change in any of the config binaries */
+
+typedef enum {
+	POL_PREPEND,
+	POL_APPEND,
+	POL_OVERWRITE,
+	POL_DISABLE,
+	POL_invalid
+} conf_policy_t;
+
+typedef enum { /* bitfield */
+	CFF_USAGE = 1    /* settings should be printed in usage help */
+} conf_flag_t;
+
+
+typedef const char * CFT_STRING;
+typedef int          CFT_BOOLEAN;
+typedef long         CFT_INTEGER;
+typedef double       CFT_REAL;
+typedef Coord        CFT_COORD;
+typedef Unit *       CFT_UNIT;
+typedef char *       CFT_COLOR;
+typedef conflist_t   CFT_LIST;
+typedef Increments   CFT_INCREMENTS;
+
+typedef enum {
+	CFN_STRING,
+	CFN_BOOLEAN,
+	CFN_INTEGER,     /* signed long */
+	CFN_REAL,        /* double */
+	CFN_COORD,
+	CFN_UNIT,
+	CFN_COLOR,
+	CFN_LIST,
+	CFN_INCREMENTS
+} conf_native_type_t;
+
+union confitem_u {
+	const char **string;
+	int *boolean;
+	long *integer;
+	double *real;
+	Coord *coord;
+	const Unit **unit;
+	const char **color;
+	conflist_t *list;
+	Increments *increments;
+	void *any;
+};
+
+typedef struct {
+	int prio;
+	lht_node_t *src;
+} confprop_t;
+
+typedef struct {
+	/* static fields defined by the macros */
+	const char *description;
+	const char *hash_path;     /* points to the hash key once its added in the hash (else: NULL) */
+	int array_size;
+	conf_native_type_t type;
+	conf_flag_t flags;
+	struct {
+		unsigned io_pcb_no_attrib:1;
+		unsigned read_only:1;         /* set by conf_core, has no lihata, should not be overwritten */
+	} random_flags;  /* hack... persistent flags attached by various plugins */
+
+	/* dynamic fields loaded from lihata */
+	confitem_t val;   /* value is always an array (len 1 for the common case)  */
+	confprop_t *prop; /* an array of properties allocated as big as val's array */
+	int used;         /* number of items actually used in the arrays */
+	int conf_rev;     /* last changed rev */
+
+	/* dynamic fields for HIDs storing their data */
+	vtp0_t hid_data;
+	vtp0_t hid_callbacks; /* vector of (const conf_hid_callbacks_t *) */
+} conf_native_t;
+
+
+struct conf_listitem_s {
+	conf_native_type_t type;
+	confitem_t val;   /* value is always an array (len 1 for the common case)  */
+	confprop_t prop; /* an array of properties allocated as big as val's array */
+	const char *payload;
+	gdl_elem_t link;
+};
+
+
+typedef enum {
+	CFR_INTERNAL,
+	CFR_SYSTEM,
+	CFR_DEFAULTPCB, /* default.pcb */
+	CFR_USER,
+	CFR_ENV,        /* env vars */
+	CFR_PROJECT,    /* project specific, from a local file */
+	CFR_DESIGN,     /* from the design file */
+	CFR_CLI,        /* from the command line */
+	CFR_max_real,   /* all the above are real files and should be merged */
+	/* these ones are not real roles and are used in internal communication in the GUI HIDs */
+	CFR_file,       /* custom file */
+	CFR_binary,     /* the in-memory binary representation */
+	CFR_max_alloc,  /* all the above should have a root */
+
+	CFR_invalid
+} conf_role_t;
+
+extern const int conf_default_prio[];
+
+void conf_init(void);
+void conf_uninit(void);
+
+/* Load all config files from disk into memory-lht and run conf_update to
+   get the binary representation updated */
+void conf_load_all(const char *project_fn, const char *pcb_fn);
+
+/* Load a file or a string as a role */
+int conf_load_as(conf_role_t role, const char *fn, int fn_is_text);
+
+/* Load a project file into CFR_PROJECT. Both project_fn and pcb_fn can't be NULL.
+   Leaves an initialized but empty CFR_PROJECT root if no project file was
+   found. Runs conf_update(NULL); */
+void conf_load_project(const char *project_fn, const char *pcb_fn);
+
+/* Update the binary representation from the memory-lht representation */
+void conf_update(const char *path);
+
+conf_native_t *conf_get_field(const char *path);
+void conf_reg_field_(void *value, int array_size, conf_native_type_t type, const char *path, const char *desc, conf_flag_t flags);
+
+void conf_unreg_fields(const char *prefix);
+
+/* Set the value of path[arr_idx] in memory-lht role target to new_val using
+   policy pol. Only lists should be indexed. Indexing can be a [n] suffix on
+   path or a non-negative arr_idx. Updates the in-memory binary as well. If
+   new_val is NULL, the selected subtree is removed from the lihata document. */
+int conf_set(conf_role_t target, const char *path, int arr_idx, const char *new_val, conf_policy_t pol);
+
+/* Remove the subtree of path[arr_idx] in memory-lht role target. Same
+   considerations as in conf_set. */
+int conf_del(conf_role_t target, const char *path, int arr_idx);
+
+/* Same as conf_set, but without updating the binary - useful for multiple
+   conf_set_dry calls and a single all-tree conf_udpate(NULL) for transactions. */
+int conf_set_dry(conf_role_t target, const char *path_, int arr_idx, const char *new_val, conf_policy_t pol);
+
+/* Same as conf_set, but doesn't look up where to set things: change the value of
+   the lihata node backing the native field */
+int conf_set_native(conf_native_t *field, int arr_idx, const char *new_val);
+
+/* Process a command line argument arg_ (if val == NULL) or a pair of command line
+   arguments arg_ and val. In the first case assume arg_ has both a config path
+   and a value (may be implicit true for a boolean). In the second case val
+   is always the value. If prefix is not NULL, the path is prefixed with it.
+   On error always set *why to a const string reason.
+   Returns 0 on success. */
+int conf_set_from_cli(const char *prefix, const char *arg_, const char *val, const char **why);
+
+/* Attempt to consume argv[] using conf_set_from_cli */
+void conf_parse_arguments(const char *prefix, int *argc, char ***argv);
+
+#define conf_reg_field_array(globvar, field, type_name, path, desc, flags) \
+	conf_reg_field_((void *)&globvar.field, (sizeof(globvar.field) / sizeof(globvar.field[0])), type_name, path, desc, flags)
+
+#define conf_reg_field_scalar(globvar, field, type_name, path, desc, flags) \
+	conf_reg_field_((void *)&globvar.field, 1, type_name, path, desc, flags)
+
+/* register a config field, array or scalar, selecting the right macro */
+#define conf_reg_field(globvar,   field,isarray,type_name,cpath,cname, desc, flags) \
+	conf_reg_field_ ## isarray(globvar, field,type_name,cpath "/" cname, desc, flags)
+
+/* convert a policy text to policy value - return POL_invalid on error */
+conf_policy_t conf_policy_parse(const char *s);
+
+/* Return the name of the policy - always a static string, even for invalid roles */
+const char *conf_policy_name(conf_policy_t p);
+
+/* convert a role text to role value - return CFR_invalid on error */
+conf_role_t conf_role_parse(const char *s);
+
+/* Return the name of the role - always a static string, even for invalid roles */
+const char *conf_role_name(conf_role_t r);
+
+/* Lock/unlock the structure of a role. In a locked role value of existing
+   fields may be modified but the structure of the tree is static (can't
+   create or remove nodes). This is useful when an io_ file format supports
+   only a subset of settings: it can build the CFR_DESIGN tree, lock it so
+   settings that it wouldn't know how to save won't appear. NOTE: io_pcb
+   supports all settings via attributes so does not lock. */
+void conf_lock(conf_role_t target);
+void conf_unlock(conf_role_t target);
+
+
+/* replace dst_role:dst_path with a copy of src_role:src_path */
+int conf_replace_subtree(conf_role_t dst_role, const char *dst_path, conf_role_t src_role, const char *src_path);
+
+/* Throw out a subtree (remove all nodes from the lihata representation).
+   Useful for io_ plugins, on CFR_DESIGN, before loading a new file. */
+void conf_reset(conf_role_t target, const char *source_fn);
+
+/* Save an in-memory lihata representation to the disk */
+int conf_save_file(const char *project_fn, const char *pcb_fn, conf_role_t role, const char *fn);
+
+/* Returns whether a given lihata tree is locked */
+int conf_islocked(conf_role_t target);
+
+/* Returns whether a given lihata tree has changed since load or last save */
+int conf_isdirty(conf_role_t target);
+void conf_makedirty(conf_role_t target);
+
+/* all configuration fields ever seen */
+extern htsp_t *conf_fields;
+
+/***** print fields from binary to lihata/text *****/
+
+typedef int (*conf_pfn)(void *ctx, const char *fmt, ...);
+
+/* Prints the value of a node in a form that is suitable for lihata. Prints
+   a single element of an array, but prints lists as lists. Returns
+   the sum of conf_pfn call return values - this is usually the number of
+   bytes printed. */
+int conf_print_native_field(conf_pfn pfn, void *ctx, int verbose, confitem_t *val, conf_native_type_t type, confprop_t *prop, int idx);
+
+/* Prints the value of a node in a form that is suitable for lihata. Prints
+   full arrays. Returns the sum of conf_pfn call return values - this is
+   usually the number of bytes printed. */
+int conf_print_native(conf_pfn pfn, void *ctx, const char * prefix, int verbose, conf_native_t *node);
+
+
+/****** utility ******/
+
+void conf_setf(conf_role_t role, const char *path, int idx, const char *fmt, ...);
+
+#define conf_list_foreach_path_first(res, conf_list, call) \
+do { \
+	conf_listitem_t *__n__; \
+	const conflist_t *__lst1__ = (conf_list); \
+	conflist_t *__lst__ = (conflist_t *)(__lst1__); \
+	if (__lst__ != NULL) { \
+		for(__n__ = conflist_first(__lst__); __n__ != NULL; __n__ = conflist_next(__n__)) { \
+			char *__path__; \
+			const char **__in__ = __n__->val.string; \
+			if (__in__ == NULL) \
+				continue; \
+			resolve_path(*__in__, &__path__, 0); \
+			res = call; \
+			free(__path__); \
+			if (res == 0) \
+				break; \
+		} \
+	} \
+} while(0)
+
+/*	printf("conf_list_foreach_path_first: %s using %s\n", # call, # conf_list); \*/
+
+/* htsp_entry_t *e; */
+#define conf_fields_foreach(e) \
+	for (e = htsp_first(conf_fields); e; e = htsp_next(conf_fields, e))
+
+/* helpers to make the code shorter */
+#define conf_set_design(path, fmt, new_val) \
+	conf_setf(CFR_DESIGN, path, -1, fmt, new_val)
+
+#define conf_set_editor(field, val) \
+	conf_set(CFR_DESIGN, "editor/" #field, -1, val ? "1" : "0", POL_OVERWRITE)
+
+#define conf_toggle_editor(field) \
+	conf_set_editor(field, !conf_core.editor.field)
+
+#define conf_toggle_editor_(sfield, field) \
+	conf_set_editor(sfield, !conf_core.editor.field)
+
+/* For temporary modification/restoration of variables (hack) */
+#define conf_force_set_bool(var, val) *((CFT_BOOLEAN *)(&var)) = val
+#define conf_force_set_str(var, val) *((CFT_STRING *)(&var)) = val
+
+/* get the first config subtree node (it's a hash and its children
+   are "design", "rc", ...) */
+lht_node_t *conf_lht_get_first(conf_role_t target);
+
+/* loop helper */
+conf_listitem_t *conf_list_first_str(conflist_t *list, const char **item_str, int *idx);
+conf_listitem_t *conf_list_next_str(conf_listitem_t *item_li, const char **item_str, int *idx);
+
+/*conf_listitem_t *item;*/
+#define conf_loop_list(list, item, idx) \
+	for (idx = 0, item = conflist_first((conflist_t *)cl); item != NULL; item = conflist_next(item), idx++)
+
+/*conf_listitem_t *item; const char *item_str; */
+#define conf_loop_list_str(list, item_li, item_str, idx) \
+	for (idx = 0, item_li = conf_list_first_str((conflist_t *)list, &item_str, &idx); \
+		item_li != NULL;\
+		item_li = conf_list_next_str(item_li, &item_str, &idx))
+
+const char *conf_concat_strlist(const conflist_t *lst, gds_t *buff, int *inited, char sep);
+
+/* Print usage help for all nodes that have the CFF_USAGE flag and whose
+   path starts with prefix (if prefix != NULL) */
+void conf_usage(const char *prefix, void (*print)(const char *name, const char *help));
+
+/* Determine under which role a node is */
+conf_role_t conf_lookup_role(const lht_node_t *nd);
+
+/* Return the lihata node of a path in target, optionally creating it with the right type */
+lht_node_t *conf_lht_get_at(conf_role_t target, const char *path, int create);
+
+/* Write an existing conf subtree to a file */
+int conf_export_to_file(const char *fn, conf_role_t role, const char *conf_path);
+
+/* Determine the policy and priority of a config lihata node;
+   returns 0 on success but may not fill in both values, caller is
+   responsible for initializing them before the call. */
+int conf_get_policy_prio(lht_node_t *node, conf_policy_t *gpolicy, long *gprio);
+
+/* Parse text and convert the value into native form and store in one of dst
+   fields depending on type */
+int conf_parse_text(confitem_t *dst, int idx, conf_native_type_t type, const char *text, lht_node_t *err_node);
+
+/* Determine the file name of the project file - project_fn and pcb_fn can be NULL */
+const char *conf_get_project_conf_name(const char *project_fn, const char *pcb_fn, const char **out_project_fn);
+
+#endif
diff --git a/src/conf_act.c b/src/conf_act.c
new file mode 100644
index 0000000..6a28aca
--- /dev/null
+++ b/src/conf_act.c
@@ -0,0 +1,310 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This module, debug, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "global.h"
+#include "data.h"
+#include "config.h"
+#include "conf.h"
+#include "conf_core.h"
+#include "error.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "route_style.h"
+
+static const char conf_syntax[] =
+	"conf(set, path, value, [role], [policy]) - change a config setting\n"
+	"conf(toggle, path, [role]) - invert boolean value of a flag; if no role given, overwrite the highest prio config\n"
+	"conf(reset, role) - reset the in-memory lihata of a role\n"
+	"conf(iseq, path, value) - returns whether the value of a conf item matches value (for menu checked's)\n"
+	;
+static const char conf_help[] = "Perform various operations on the configuration tree.";
+
+extern lht_doc_t *conf_root[];
+static inline int conf_iseq_pf(void *ctx, const char *fmt, ...)
+{
+	int res;
+	va_list ap;
+	va_start(ap, fmt);
+	res = pcb_append_vprintf((gds_t *)ctx, fmt, ap);
+	va_end(ap);
+	return res;
+}
+
+static int ActionConf(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *cmd = argc > 0 ? argv[0] : 0;
+
+	if (NSTRCMP(cmd, "set") == 0) {
+		const char *path, *val;
+		conf_policy_t pol = POL_OVERWRITE;
+		conf_role_t role = CFR_invalid;
+		int res;
+
+		if (argc < 3) {
+			Message(PCB_MSG_DEFAULT, "conf(set) needs at least two arguments");
+			return 1;
+		}
+		if (argc > 3) {
+			role = conf_role_parse(argv[3]);
+			if (role == CFR_invalid) {
+				Message(PCB_MSG_DEFAULT, "Invalid role: '%s'", argv[3]);
+				return 1;
+			}
+		}
+		if (argc > 4) {
+			pol = conf_policy_parse(argv[4]);
+			if (pol == POL_invalid) {
+				Message(PCB_MSG_DEFAULT, "Invalid policy: '%s'", argv[4]);
+				return 1;
+			}
+		}
+		path = argv[1];
+		val = argv[2];
+
+		if (role == CFR_invalid) {
+			conf_native_t *n = conf_get_field(argv[1]);
+			if (n == NULL) {
+				Message(PCB_MSG_DEFAULT, "Invalid conf field '%s': no such path\n", argv[1]);
+				return 1;
+			}
+			res = conf_set_native(n, 0, val);
+		}
+		else
+			res = conf_set(role, path, -1, val, pol);
+		if (res != 0) {
+			Message(PCB_MSG_DEFAULT, "conf(set) failed.\n");
+			return 1;
+		}
+	}
+
+	else if (NSTRCMP(cmd, "iseq") == 0) {
+		const char *path, *val;
+		int res;
+		gds_t nval;
+		conf_native_t *n;
+
+		if (argc != 3) {
+			Message(PCB_MSG_ERROR, "conf(iseq) needs two arguments");
+			return -1;
+		}
+		path = argv[1];
+		val = argv[2];
+
+		n = conf_get_field(argv[1]);
+		if (n == NULL) {
+			Message(PCB_MSG_ERROR, "Invalid conf field '%s' in iseq: no such path\n", path);
+			return -1;
+		}
+
+		gds_init(&nval);
+		conf_print_native_field(conf_iseq_pf, &nval, 0, &n->val, n->type, NULL, 0);
+		res = !strcmp(nval.array, val);
+/*		printf("iseq: %s %s==%s %d\n", path, nval.array, val, res);*/
+		gds_uninit(&nval);
+
+		return res;
+	}
+
+	else if (NSTRCMP(cmd, "toggle") == 0) {
+		conf_native_t *n = conf_get_field(argv[1]);
+		const char *new_value;
+		conf_role_t role = CFR_invalid;
+		int res;
+
+		if (n == NULL) {
+			Message(PCB_MSG_DEFAULT, "Invalid conf field '%s': no such path\n", argv[1]);
+			return 1;
+		}
+		if (n->type != CFN_BOOLEAN) {
+			Message(PCB_MSG_DEFAULT, "Can not toggle '%s': not a boolean\n", argv[1]);
+			return 1;
+		}
+		if (n->used != 1) {
+			Message(PCB_MSG_DEFAULT, "Can not toggle '%s': array size should be 1, not %d\n", argv[1], n->used);
+			return 1;
+		}
+		if (argc > 2) {
+			role = conf_role_parse(argv[2]);
+			if (role == CFR_invalid) {
+				Message(PCB_MSG_DEFAULT, "Invalid role: '%s'", argv[2]);
+				return 1;
+			}
+		}
+		if (n->val.boolean[0])
+			new_value = "false";
+		else
+			new_value = "true";
+		if (role == CFR_invalid)
+			res = conf_set_native(n, 0, new_value);
+		else
+			res = conf_set(role, argv[1], -1, new_value, POL_OVERWRITE);
+
+		if (res != 0) {
+			Message(PCB_MSG_DEFAULT, "Can not toggle '%s': failed to set new value\n", argv[1]);
+			return 1;
+		}
+		conf_update(argv[1]);
+	}
+
+	else if (NSTRCMP(cmd, "reset") == 0) {
+		conf_role_t role;
+		role = conf_role_parse(argv[1]);
+		if (role == CFR_invalid) {
+			Message(PCB_MSG_DEFAULT, "Invalid role: '%s'", argv[1]);
+			return 1;
+		}
+		conf_reset(role, "<action>");
+		conf_update(argv[1]);
+	}
+
+	else {
+		Message(PCB_MSG_DEFAULT, "Invalid conf command '%s'\n", argv[0]);
+		return 1;
+	}
+	return 0;
+}
+
+/*------------ get/chk (check flag actions for menus) ------------------*/
+static const char GetStyle_syntax[] = "GetStyle()" ;
+static const char GetStyle_help[] = "Return integer index (>=0) of the currently active style or -1 if no style is selected (== custom style)";
+static int ActionGetStyle(int argc, const char **argv, Coord x, Coord y)
+{
+	return pcb_route_style_lookup(&PCB->RouteStyle, conf_core.design.line_thickness, conf_core.design.via_thickness, conf_core.design.via_drilling_hole, conf_core.design.clearance, NULL);
+}
+
+static const char ChkMode_syntax[] = "ChkMode(expected_mode)" ;
+static const char ChkMode_help[] = "Return 1 if the currently selected mode is the expected_mode";
+static int ActionChkMode(int argc, const char **argv, Coord x, Coord y)
+{
+#warning TODO: convert this to a compile-time hash
+	struct {
+		const char *name;
+		int mode;
+	} *m, modes[] = {
+		{"none", PCB_MODE_NO},
+		{"arc", PCB_MODE_ARC},
+		{"arrow", PCB_MODE_ARROW},
+		{"copy", PCB_MODE_COPY},
+		{"insertpoint", PCB_MODE_INSERT_POINT},
+		{"line", PCB_MODE_LINE},
+		{"lock", PCB_MODE_LOCK},
+		{"move", PCB_MODE_MOVE},
+		{"pastebuffer", PCB_MODE_PASTE_BUFFER},
+		{"polygon", PCB_MODE_POLYGON},
+		{"polygonhole", PCB_MODE_POLYGON_HOLE},
+		{"rectangle", PCB_MODE_RECTANGLE},
+		{"remove", PCB_MODE_REMOVE},
+		{"rotate", PCB_MODE_ROTATE},
+		{"rubberbandmove", PCB_MODE_RUBBERBAND_MOVE},
+		{"text", PCB_MODE_TEXT},
+		{"thermal", PCB_MODE_THERMAL},
+		{"via", PCB_MODE_VIA},
+		{NULL, 0}
+	};
+	assert(argc == 1);
+	for(m = modes; m->name != NULL; m++) {
+		if (strcmp(m->name, argv[0]) == 0)
+			return conf_core.editor.mode == m->mode;
+	}
+	Message(PCB_MSG_DEFAULT, "Unknown mode in ChkMode(): %s\n", argv[1]);
+	abort();
+	return -1;
+}
+
+
+static const char ChkGridSize_syntax[] =
+	"ChkGridSize(expected_size)\n"
+	"ChkGridSize(none)\n"
+	;
+static const char ChkGridSize_help[] = "Return 1 if the currently selected grid matches the expected_size. If argument is \"none\" return 1 if there is no grid.";
+static int ActionChkGridSize(int argc, const char **argv, Coord x, Coord y)
+{
+	assert(argc == 1);
+	if (strcmp(argv[0], "none") == 0)
+		return PCB->Grid <= 300;
+
+	return (PCB->Grid == GetValueEx(argv[0], NULL, NULL, NULL, NULL, NULL));
+}
+
+static const char ChkElementName_syntax[] =
+	"ChkElementName(1) - expect description\n"
+	"ChkElementName(2) - expect refdes\n"
+	"ChkElementName(3) - expect value\n"
+	;
+static const char ChkElementName_help[] = "Return 1 if currently shown element label (name) type matches the expected";
+static int ActionChkElementName(int argc, const char **argv, Coord x, Coord y)
+{
+	int have, expected = argv[0][0] - '0';
+
+	assert(argc == 1);
+	if (conf_core.editor.description) have = 1;
+	else if (conf_core.editor.name_on_pcb) have = 2;
+	else have = 3;
+
+	return expected == have;
+}
+
+static const char ChkGridUnits_syntax[] = "ChkGridUnits(expected)";
+static const char ChkGridUnits_help[] = "Return 1 if currently selected grid unit matches the expected (normally mm or mil)";
+static int ActionChkGridUnits(int argc, const char **argv, Coord x, Coord y)
+{
+	assert(argc == 1);
+	return strcmp(conf_core.editor.grid_unit->suffix, argv[0]) == 0;
+}
+
+static const char ChkBuffer_syntax[] = "ChkBuffer(idx)";
+static const char ChkBuffer_help[] = "Return 1 if currently selected buffer's index matches idx";
+static int ActionChkBuffer(int argc, const char **argv, Coord x, Coord y)
+{
+	int expected = argv[0][0] - '0';
+	assert(argc == 1);
+
+	return (conf_core.editor.buffer_number + 1) == expected;
+}
+
+HID_Action conf_action_list[] = {
+	{"conf", 0, ActionConf,
+	 conf_help, conf_syntax}
+	,
+	{"GetStyle", 0, ActionGetStyle,
+	 GetStyle_help, GetStyle_syntax}
+	,
+	{"ChkMode", 0, ActionChkMode,
+	 ChkMode_help, ChkMode_syntax}
+	,
+	{"ChkGridSize", 0, ActionChkGridSize,
+	 ChkGridSize_help, ChkGridSize_syntax}
+	,
+	{"ChkElementName", 0, ActionChkElementName,
+	 ChkElementName_help, ChkElementName_syntax}
+	,
+	{"ChkGridUnits", 0, ActionChkGridUnits,
+	 ChkGridUnits_help, ChkGridUnits_syntax}
+	,
+	{"ChkBuffer", 0, ActionChkBuffer,
+	 ChkBuffer_help, ChkBuffer_syntax}
+};
+
+REGISTER_ACTIONS(conf_action_list, NULL)
diff --git a/src/conf_core.c b/src/conf_core.c
new file mode 100644
index 0000000..6a85b0b
--- /dev/null
+++ b/src/conf_core.c
@@ -0,0 +1,58 @@
+#include "conf.h"
+#include "conf_core.h"
+conf_core_t conf_core;
+
+void conf_core_init()
+{
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_core, field,isarray,type_name,cpath,cname,desc,flags);
+#include "conf_core_fields.h"
+}
+
+#define conf_clamp_to(type, var, min, max, safe_val) \
+do { \
+	if ((var < min) || (var > max)) \
+		*((type *)(&var)) = safe_val; \
+}	while(0)
+
+#define conf_clamp(type, var, min, max) \
+do { \
+	if (var < min) \
+		*((type *)(&var)) = min; \
+	else if (var > max) \
+		*((type *)(&var)) = max; \
+}	while(0)
+
+static char *get_homedir(void)
+{
+	char *homedir = getenv("HOME");
+	if (homedir == NULL)
+		homedir = getenv("USERPROFILE");
+	return homedir;
+}
+
+static void conf_ro(const char *path)
+{
+	conf_native_t *n = conf_get_field(path);
+	if (n != NULL) {
+		n->used = 1;
+		n->random_flags.read_only = 1;
+	}
+}
+
+void conf_core_postproc()
+{
+	conf_clamp_to(CFT_COORD, conf_core.design.line_thickness, MIN_LINESIZE, MAX_LINESIZE, PCB_MIL_TO_COORD(10));
+	conf_clamp_to(CFT_COORD, conf_core.design.via_thickness, MIN_PINORVIASIZE, MAX_PINORVIASIZE, PCB_MIL_TO_COORD(40));
+	conf_clamp_to(CFT_COORD, conf_core.design.via_drilling_hole, 0, MAX_COORD, DEFAULT_DRILLINGHOLE * conf_core.design.via_thickness / 100);
+	conf_clamp(CFT_COORD, conf_core.design.max_width, MIN_SIZE, MAX_COORD);
+	conf_clamp(CFT_COORD, conf_core.design.max_height, MIN_SIZE, MAX_COORD);
+	conf_force_set_bool(conf_core.rc.have_regex, 1);
+	conf_ro("rc/have_regex");
+
+	conf_force_set_str(conf_core.rc.path.prefix, PCB_PREFIX);   conf_ro("rc/path/prefix");
+	conf_force_set_str(conf_core.rc.path.lib, PCBLIBDIR);       conf_ro("rc/path/lib");
+	conf_force_set_str(conf_core.rc.path.bin, BINDIR);          conf_ro("rc/path/bin");
+	conf_force_set_str(conf_core.rc.path.share, PCBSHAREDIR);   conf_ro("rc/path/share");
+	conf_force_set_str(conf_core.rc.path.home, get_homedir());  conf_ro("rc/path/home");
+}
diff --git a/src/conf_core.h b/src/conf_core.h
new file mode 100644
index 0000000..f2eab5d
--- /dev/null
+++ b/src/conf_core.h
@@ -0,0 +1,200 @@
+#ifndef PCB_CONF_CORE_H
+#define PCB_CONF_CORE_H
+
+#include "conf.h"
+
+/* NOTE: this struct has a strict format because a code generator needs to
+   read it. Please always keep the format (preferably even whitespace style).
+   Use only the CFT_* prefixed types declared in conf.h.
+   */
+
+typedef struct {
+
+	struct temp {
+		CFT_BOOLEAN rat_warn;              /* rats nest has set warnings */
+	} temp;
+
+	const struct editor {
+		CFT_UNIT grid_unit;                /* select whether you draw in mm or mil */
+		CFT_COORD grid;                    /* grid in pcb-units */
+		CFT_INCREMENTS increments_mm;      /* increments (size deltas) when drawing in mil */
+		CFT_INCREMENTS increments_mil;     /* increments (size deltas) when drawing in mil */
+		CFT_REAL zoom;                     /* default zoom */
+		CFT_INTEGER mode;                  /* currently active mode */
+		CFT_INTEGER buffer_number;         /* number of the current buffer */
+		CFT_BOOLEAN clear_line;            /* new lines/arc clear polygons. */
+		CFT_BOOLEAN full_poly;             /* new polygons are full polygons. */
+		CFT_BOOLEAN unique_names;          /* force unique names */
+		CFT_BOOLEAN snap_pin;              /* snap to pins and pads */
+		CFT_BOOLEAN snap_offgrid_line;     /* Snap to certain off-grid points along a line. */
+		CFT_BOOLEAN highlight_on_point;    /* Highlight if crosshair is on endpoints. */
+		CFT_BOOLEAN show_solder_side;      /* mirror output */
+		CFT_BOOLEAN save_last_command;     /* the command entry editline always starts with the last command entered by user in the current session */
+		CFT_INTEGER line_refraction;       /* value for line lookahead setting */
+		CFT_BOOLEAN save_in_tmp;           /* emergency save unsaved PCB data (despite the user clicks don't save) when: user starts a new PCB; user quits pcb-rnd. Does not affect the on-crash emergency save. */
+		CFT_BOOLEAN draw_grid;             /* draw grid points */
+		CFT_BOOLEAN all_direction_lines;   /* enable lines to all directions */
+		CFT_BOOLEAN rubber_band_mode;      /* move, rotate use rubberband connections */
+		CFT_BOOLEAN swap_start_direction;  /* change starting direction after each click */
+		CFT_BOOLEAN show_drc;              /* show drc region on crosshair */
+		CFT_BOOLEAN auto_drc;              /* when set, PCB doesn't let you place copper that violates DRC. */
+		CFT_BOOLEAN show_number;           /* pinout shows number */
+		CFT_BOOLEAN orthogonal_moves;      /* move items orthogonally. */
+		CFT_BOOLEAN reset_after_element;   /* reset connections after each element while saving all connections */
+		CFT_BOOLEAN auto_place;            /* flag which says we should force placement of the windows on startup */
+		CFT_BOOLEAN lock_names;            /* lock down text so they can not be moved or selected */
+		CFT_BOOLEAN only_names;            /* lock down everything else but text so only text objects can be moved or selected */
+		CFT_BOOLEAN thin_draw;             /* if set, objects on the screen are drawn as outlines (lines are drawn as center-lines).  This lets you see line endpoints hidden under pins, for example. */
+		CFT_BOOLEAN thin_draw_poly;        /* if set, polygons on the screen are drawn as outlines. */
+		CFT_BOOLEAN local_ref;             /* use local reference for moves, by setting the mark at the beginning of each move. */
+		CFT_BOOLEAN check_planes;          /* when set, only polygons and their clearances are drawn, to see if polygons have isolated regions. */
+		CFT_BOOLEAN show_mask;             /* show the solder mask layer */
+		CFT_BOOLEAN hide_names;            /* when set, element names are not drawn. */
+		CFT_BOOLEAN description;           /* display element description as element name, instead of value */
+		CFT_BOOLEAN name_on_pcb;           /* display Reference Designator as element name, instead of value */
+		CFT_BOOLEAN fullscreen;            /* hide widgets to make more room for the drawing */
+
+		CFT_INTEGER click_time;            /* default time for click expiration, in ms */
+
+		struct view {
+			CFT_BOOLEAN flip_x;              /* view: flip the board along the X (horizontal) axis */
+			CFT_BOOLEAN flip_y;              /* view: flip the board along the Y (vertical) axis */
+		} view;
+
+		/* these two would need to be moved in the router plugin.... There are two
+		   reasons to keep them here:
+		   - the original pcb and pcb-rnd file formats already have named/numbered flags for these, so io_pcb needs these
+		   - more than one router plugin may share these */
+		CFT_BOOLEAN enable_stroke;         /* Enable libstroke gestures on middle mouse button when non-zero */
+		CFT_BOOLEAN live_routing;          /* autorouter shows tracks in progress */
+
+		/* Keep it here instead of the router plugin: more than one router plugin may share these */
+		CFT_BOOLEAN beep_when_finished;    /* flag if a signal should be produced when searching of  connections is done */
+
+		CFT_INTEGER undo_warning_size;     /* warn the user when undo list exceeds this amount of kilobytes in memory */
+	} editor;
+
+	const struct rc {
+		CFT_INTEGER verbose;
+		CFT_INTEGER backup_interval;       /* time between two backups in seconds */
+		CFT_STRING font_command;           /* commands for file loading... */
+		CFT_STRING file_command;
+		CFT_STRING file_path;
+		CFT_STRING library_shell;
+		CFT_LIST library_search_paths;
+
+		CFT_STRING emergency_name;         /* file name template for emergency save anonymous .pcb files (when pcb-rnd crashes); optional field: %ld --> pid; must be shorter than 240 characters. Don't do emergency save if this item is empty. */
+		CFT_STRING backup_name;            /* file name template for periodic backup anonymous .pcb files; optional fields: %P --> pid */
+
+		CFT_STRING save_command;           /* command to pipe the pcb, footprint or buffer file into, when saving (makes lihata persist impossible) */
+		CFT_BOOLEAN keep_save_backups;     /* a copy is made before a save operation overwrites an existing file; if this setting is true, keep the copy even after a successful save */
+		CFT_LIST default_font_file;        /* name of default font file (list of names to search) */
+		CFT_LIST default_pcb_file;
+
+		CFT_STRING script_filename;        /* PCB Actions script to execute on startup */
+		CFT_STRING action_string;          /* PCB Actions string to execute on startup */
+		CFT_STRING rat_path;
+		CFT_STRING rat_command;
+
+		CFT_LIST preferred_gui;            /* if set, try GUI HIDs in this order when no GUI is explicitly selected */
+
+		/***** automatically set (in postproc) *****/
+		CFT_BOOLEAN have_regex;            /* whether we have regex compiled in */
+		struct path {
+			CFT_STRING prefix;               /* e.g. /usr/local */
+			CFT_STRING lib;                  /* e.g. /usr/lib/pcb-rnd */
+			CFT_STRING bin;                  /* e.g. /usr/bin */
+			CFT_STRING share;                /* e.g. /usr/share/pcb-rnd */
+			CFT_STRING home;                 /* user's home dir, determined run-time */
+			
+			CFT_STRING exec_prefix;          /* exec prefix path (extracted from argv[0]) */
+		} path;
+	} rc;
+
+	const struct design { /* defaults of a new layout */
+		CFT_COORD via_thickness;
+		CFT_COORD via_drilling_hole;
+		CFT_COORD line_thickness;
+		CFT_COORD clearance;
+
+		CFT_COORD max_width;
+		CFT_COORD max_height;
+		CFT_COORD alignment_distance;/* default drc size */
+		CFT_COORD bloat; /* default drc size */
+		CFT_COORD shrink;
+		CFT_COORD min_wid;
+		CFT_COORD min_slk;
+		CFT_COORD min_drill;
+		CFT_COORD min_ring;
+		CFT_INTEGER text_scale;   /* text scaling in % */
+		CFT_REAL poly_isle_area;  /* polygon min area */
+		CFT_STRING default_layer_name[MAX_LAYER];
+		CFT_STRING fab_author;                  /* Full name of author for FAB drawings */
+		CFT_STRING initial_layer_stack;         /* If set, the initial layer stack is set to this */
+
+		CFT_STRING groups;                 /* string with layergroups */
+		CFT_STRING routes;                 /* string with route styles */
+	} design;
+
+/* @path appearance/color */
+	const struct appearance {
+		CFT_COORD rat_thickness;
+		CFT_COORD mark_size;               /* relative marker size */
+		struct loglevels {
+			CFT_STRING   debug_tag;          /* log style tag of debug messages */
+			CFT_BOOLEAN  debug_popup;        /* whether a debug line should pop up the log window */
+			CFT_STRING   info_tag;           /* log style tag of info messages */
+			CFT_BOOLEAN  info_popup;         /* whether an info line should pop up the log window */
+			CFT_STRING   warning_tag;        /* log style tag of warnings */
+			CFT_BOOLEAN  warning_popup;      /* whether a warning should pop up the log window */
+			CFT_STRING   error_tag;          /* log style tag of errors */
+			CFT_BOOLEAN  error_popup;        /* whether an error should pop up the log window */
+		} loglevels;
+		struct color {
+			CFT_COLOR black;
+			CFT_COLOR white;
+			CFT_COLOR background;	/* background and cursor color ... */
+			CFT_COLOR crosshair;							/* different object colors */
+			CFT_COLOR cross;
+			CFT_COLOR via;
+			CFT_COLOR via_selected;
+			CFT_COLOR pin;
+			CFT_COLOR pin_selected;
+			CFT_COLOR pin_name;
+			CFT_COLOR element;
+			CFT_COLOR element_nonetlist;
+			CFT_COLOR rat;
+			CFT_COLOR invisible_objects;
+			CFT_COLOR invisible_mark;
+			CFT_COLOR element_selected;
+			CFT_COLOR rat_selected;
+			CFT_COLOR connected;
+			CFT_COLOR off_limit;
+			CFT_COLOR grid;
+			CFT_COLOR layer[MAX_LAYER];
+			CFT_COLOR layer_selected[MAX_LAYER];
+			CFT_COLOR warn;
+			CFT_COLOR mask;
+		} color;
+		struct pinout {
+			CFT_INTEGER name_length;
+			CFT_REAL zoom;
+			CFT_COORD offset_x;           /* X offset of origin */
+			CFT_COORD offset_y;           /* Y offset of origin */
+			CFT_COORD text_offset_x;      /* X offset of text from pin center */
+			CFT_COORD text_offset_y;      /* Y offset of text from pin center */
+		} pinout;
+		struct messages {
+			CFT_INTEGER char_per_line;   /* width of an output line in characters (used by separator drawing in find.c) */		
+		} messages;
+		struct misc {
+			CFT_INTEGER volume;          /* the speakers volume -100..100 */
+		} misc;
+	} appearance;
+
+} conf_core_t;
+
+extern conf_core_t conf_core;
+void conf_core_init();
+void conf_core_postproc();
+#endif
diff --git a/src/conf_hid.c b/src/conf_hid.c
new file mode 100644
index 0000000..665f83f
--- /dev/null
+++ b/src/conf_hid.c
@@ -0,0 +1,137 @@
+#include "conf_hid.h"
+#include <genht/hash.h>
+#include <genht/htpp.h>
+#include "error.h"
+#include "conf_core.h"
+
+typedef struct {
+	const conf_hid_callbacks_t *cb;
+	conf_hid_id_t id;
+} conf_hid_t;
+
+void *conf_hid_set_data(conf_native_t *cfg, conf_hid_id_t id, void *data)
+{
+	void **old = vtp0_get(&cfg->hid_data, id, 0);
+	vtp0_set(&cfg->hid_data, id, data);
+	return old == NULL ? NULL : *old;
+}
+
+void *conf_hid_get_data(conf_native_t *cfg, conf_hid_id_t id)
+{
+	void **old = vtp0_get(&cfg->hid_data, id, 0);
+	return old == NULL ? NULL : *old;
+}
+
+const conf_hid_callbacks_t *conf_hid_set_cb(conf_native_t *cfg, conf_hid_id_t id, const conf_hid_callbacks_t *cbs)
+{
+	void **old = vtp0_get(&cfg->hid_callbacks, id, 0);
+	vtp0_set(&cfg->hid_callbacks, id, (void *)cbs);
+	return (const conf_hid_callbacks_t *)(old == NULL ? NULL : *old);
+}
+
+
+static int conf_hid_id_next = 0;
+static htpp_t *conf_hid_ids = NULL;
+
+static void conf_hid_init(void)
+{
+	if (conf_hid_ids == NULL)
+		conf_hid_ids = htpp_alloc(ptrhash, ptrkeyeq);
+}
+
+void conf_hid_uninit(void)
+{
+	if (conf_hid_ids != NULL) {
+		htpp_free(conf_hid_ids);
+		conf_hid_ids = NULL;
+	}
+}
+
+conf_hid_id_t conf_hid_reg(const char *cookie, const conf_hid_callbacks_t *cb)
+{
+	conf_hid_t *h;
+
+	if (cookie == NULL)
+		return -1;
+
+	conf_hid_init();
+	if (htpp_getentry(conf_hid_ids, (void *)cookie) != NULL)
+		return -1; /* already registered */
+
+	h = malloc(sizeof(conf_hid_t));
+	h->cb = cb;
+	h->id = conf_hid_id_next++;
+	htpp_set(conf_hid_ids, (void *)cookie, h);
+	return h->id;
+}
+
+void conf_hid_unreg(const char *cookie)
+{
+	htsp_entry_t *e;
+	conf_hid_t *h = htpp_pop(conf_hid_ids, (void *)cookie);
+
+	if (h == NULL)
+		return;
+
+	/* remove local callbacks */
+	conf_fields_foreach(e) {
+		int len;
+		conf_native_t *cfg = e->value;
+		len = vtp0_len(&cfg->hid_callbacks);
+
+		conf_hid_local_cb(cfg, unreg_item);
+
+		/* truncate the list if there are empty items at the end */
+		if (len > h->id) {
+			int last;
+			cfg->hid_callbacks.array[h->id] = NULL;
+			for(last = len-1; last >= 0; last--)
+				if (cfg->hid_callbacks.array[last] != NULL)
+					break;
+			if (last < len)
+				vtp0_truncate(&cfg->hid_callbacks, last+1);
+		}
+	}
+
+	if ((h->cb != NULL) && (h->cb->unreg_item != NULL)) {
+		conf_fields_foreach(e) {
+			conf_native_t *cfg = e->value;
+			h->cb->unreg_item(cfg);
+		}
+	}
+
+	free(h);
+}
+
+typedef void (*cb_t)(conf_native_t *cfg);
+void conf_hid_global_cb_(conf_native_t *item, int offs)
+{
+	htpp_entry_t *e;
+	if (conf_hid_ids == NULL)
+		return;
+	for (e = htpp_first(conf_hid_ids); e; e = htpp_next(conf_hid_ids, e)) {
+		conf_hid_t *h = e->value;
+		const conf_hid_callbacks_t *cbs = h->cb;
+		if (cbs != NULL) {
+			char *s = (char *)&cbs->val_change_pre;
+			cb_t *cb = (cb_t *)(s + offs);
+			if ((*cb) != NULL)
+				(*cb)(item);
+		}
+	}
+}
+
+
+void conf_loglevel_props(enum pcb_message_level level, const char **tag, int *popup)
+{
+	*tag = NULL;
+	*popup = 0;
+	switch(level) {
+		case PCB_MSG_DEBUG:   *tag = conf_core.appearance.loglevels.debug_tag; *popup = conf_core.appearance.loglevels.debug_popup; break;
+		case PCB_MSG_INFO:    *tag = conf_core.appearance.loglevels.info_tag; *popup = conf_core.appearance.loglevels.info_popup; break;
+		case PCB_MSG_WARNING: *tag = conf_core.appearance.loglevels.warning_tag; *popup = conf_core.appearance.loglevels.warning_popup; break;
+		case PCB_MSG_ERROR:   *tag = conf_core.appearance.loglevels.error_tag; *popup = conf_core.appearance.loglevels.error_popup; break;
+			break;
+	}
+}
+
diff --git a/src/conf_hid.h b/src/conf_hid.h
new file mode 100644
index 0000000..ed4d84e
--- /dev/null
+++ b/src/conf_hid.h
@@ -0,0 +1,73 @@
+#ifndef PCB_CONF_HID_H
+#define PCB_CONF_HID_H
+
+#include "conf.h"
+
+typedef struct conf_hid_callbacks_s {
+	/* Called before/after a value of a config item is updated - this doesn't necessarily mean the value actually changes */
+	void (*val_change_pre)(conf_native_t *cfg);
+	void (*val_change_post)(conf_native_t *cfg);
+
+	/* Called when a new config item is added to the database; global-only */
+	void (*new_item_post)(conf_native_t *cfg);
+
+	/* Called during conf_hid_unreg to get hid-data cleaned up */
+	void (*unreg_item)(conf_native_t *cfg);
+} conf_hid_callbacks_t;
+
+typedef int conf_hid_id_t;
+
+/* Set local hid data in a native item; returns the previous value set or NULL */
+void *conf_hid_set_data(conf_native_t *cfg, conf_hid_id_t id, void *data);
+
+/* Returns local hid data in a native item */
+void *conf_hid_get_data(conf_native_t *cfg, conf_hid_id_t id);
+
+/* Set local callbacks in a native item; returns the previous callbacks set or NULL */
+const conf_hid_callbacks_t *conf_hid_set_cb(conf_native_t *cfg, conf_hid_id_t id, const conf_hid_callbacks_t *cbs);
+
+
+/* register a hid with a cookie; this is necessary only if:
+     - the HID wants to store per-config-item hid_data with the above calls
+     - the HID wants to get notified about changes in the config tree using callback functions
+   NOTE: cookie is taken by pointer, the string value does not matter. One pointer
+         can be registered only once.
+   cb holds the global notification callbacks - called when anything changed; it can be NULL.
+   Returns a new HID id that can be used to access hid data, or -1 on error.
+*/
+conf_hid_id_t conf_hid_reg(const char *cookie, const conf_hid_callbacks_t *cb);
+
+/* Unregister a hid; if unreg_item cb is specified, call it on each config item */
+void conf_hid_unreg(const char *cookie);
+
+void conf_hid_uninit(void);
+
+
+/* Call the local callback of a native item */
+#define conf_hid_local_cb(native, cb) \
+do { \
+	int __n__; \
+	for(__n__ = 0; __n__ < vtp0_len(&((native)->hid_callbacks)); __n__++) { \
+		const conf_hid_callbacks_t *cbs = (native)->hid_callbacks.array[__n__]; \
+		if ((cbs != NULL) && (cbs->cb != NULL)) \
+			cbs->cb(native); \
+	} \
+} while(0)
+
+/* Call the local callback of a native item */
+#define conf_hid_global_cb(native, cb) \
+do { \
+	conf_hid_callbacks_t __cbs__; \
+	int __offs__ = ((char *)&(__cbs__.cb)) - ((char *)&(__cbs__)); \
+	conf_hid_global_cb_(native, __offs__); \
+} while(0)
+
+/****** Utility/helper functions  ******/
+/* Looking at the log level, return a log format tag and whether the window
+   should pop up. */
+void conf_loglevel_props(enum pcb_message_level level, const char **tag, int *popup);
+
+/****** Internal  ******/
+void conf_hid_global_cb_(conf_native_t *item, int offs);
+
+#endif
diff --git a/src/const.h b/src/const.h
new file mode 100644
index 0000000..41d33a4
--- /dev/null
+++ b/src/const.h
@@ -0,0 +1,176 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* global source constants */
+
+#ifndef	PCB_CONST_H
+#define	PCB_CONST_H
+
+#include <limits.h>
+#include <math.h>
+
+/* ---------------------------------------------------------------------------
+ * the layer-numbers of the two additional special layers
+ * 'component' and 'solder'. The offset of MAX_LAYER is not added
+ */
+#define	SOLDER_LAYER		0
+#define	COMPONENT_LAYER		1
+
+/* ---------------------------------------------------------------------------
+ * some math constants
+ */
+#ifndef	M_PI
+#define	M_PI			3.14159265358979323846
+#endif
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 		0.707106781	/* 1/sqrt(2) */
+#endif
+#define	PCB_M180			(M_PI/180.0)
+#define PCB_RAD_TO_DEG		(180.0/M_PI)
+#define	PCB_TAN_22_5_DEGREE_2	0.207106781	/* 0.5*tan(22.5) */
+#define PCB_COS_22_5_DEGREE		0.923879533	/* cos(22.5) */
+#define	PCB_TAN_30_DEGREE		0.577350269	/* tan(30) */
+#define	PCB_TAN_60_DEGREE		1.732050808	/* tan(60) */
+#define PCB_LN_2_OVER_2		0.346573590
+
+/* PCB/physical unit conversions */
+#define PCB_COORD_TO_MIL(n)	((n) / 25400.0)
+#define PCB_MIL_TO_COORD(n)	((n) * 25400.0)
+#define PCB_COORD_TO_MM(n)	((n) / 1000000.0)
+#define PCB_MM_TO_COORD(n)	((n) * 1000000.0)
+#define PCB_COORD_TO_INCH(n)	(PCB_COORD_TO_MIL(n) / 1000.0)
+#define PCB_INCH_TO_COORD(n)	(PCB_MIL_TO_COORD(n) * 1000.0)
+#define PCB_COORD_TO_DECIMIL(n)    (PCB_COORD_TO_MIL(n) * 10.0)
+#define PCB_DECIMIL_TO_COORD(n)    (PCB_MIL_TO_COORD(n) / 10.0)
+
+/* These need to be carefully written to avoid overflows, and return
+   a Coord type.  */
+#define PCB_SCALE_TEXT(COORD,TEXTSCALE) ((Coord)((COORD) * ((double)(TEXTSCALE) / 100.0)))
+#define PCB_UNPCB_SCALE_TEXT(COORD,TEXTSCALE) ((Coord)((COORD) * (100.0 / (double)(TEXTSCALE))))
+
+/* ---------------------------------------------------------------------------
+ * modes
+ */
+typedef enum {
+	PCB_MODE_NO              = 0,   /* no mode selected */
+	PCB_MODE_VIA             = 1,   /* draw vias */
+	PCB_MODE_LINE            = 2,   /* draw lines */
+	PCB_MODE_RECTANGLE       = 3,   /* create rectangles */
+	PCB_MODE_POLYGON         = 4,   /* draw filled polygons */
+	PCB_MODE_PASTE_BUFFER    = 5,   /* paste objects from buffer */
+	PCB_MODE_TEXT            = 6,   /* create text objects */
+	PCB_MODE_ROTATE          = 102, /* rotate objects */
+	PCB_MODE_REMOVE          = 103, /* remove objects */
+	PCB_MODE_MOVE            = 104, /* move objects */
+	PCB_MODE_COPY            = 105, /* copy objects */
+	PCB_MODE_INSERT_POINT    = 106, /* insert point into line/polygon */
+	PCB_MODE_RUBBERBAND_MOVE = 107, /* move objects and attached lines */
+	PCB_MODE_THERMAL         = 108, /* toggle thermal layer flag */
+	PCB_MODE_ARC             = 109, /* draw arcs */
+	PCB_MODE_ARROW           = 110, /* selection with arrow mode */
+	PCB_MODE_PAN             = 0,   /* same as no mode */
+	PCB_MODE_LOCK            = 111, /* lock/unlock objects */
+	PCB_MODE_POLYGON_HOLE    = 112  /* cut holes in filled polygons */
+} pcb_mode_t;
+
+/* ---------------------------------------------------------------------------
+ * object flags
+ */
+
+/* %start-doc pcbfile ~objectflags
+ at node Object Flags
+ at section Object Flags
+
+Note that object flags can be given numerically (like @code{0x0147})
+or symbolically (like @code{"found,showname,square"}.  Some numeric
+values are reused for different object types.  The table below lists
+the numeric value followed by the symbolic name.
+%end-doc */
+typedef enum {
+	PCB_FLAG_NO           = 0x00000,
+	PCB_FLAG_PIN          = 0x00001, /*!< If set, this object is a pin.  This flag is for internal use only. */
+	PCB_FLAG_VIA          = 0x00002, /*!< Likewise, for vias. */
+	PCB_FLAG_FOUND        = 0x00004, /*!< If set, this object has been found by @code{FindConnection()}. */
+	PCB_FLAG_HOLE         = 0x00008, /*!< For pins and vias, this flag means that the pin or via is a hole without a copper annulus. */
+	PCB_FLAG_NOPASTE      = 0x00008, /*!< Pad should not receive solderpaste.  This is to support fiducials */
+	PCB_FLAG_RAT          = 0x00010, /*!< If set for a line, indicates that this line is a rat line instead of a copper trace. */
+	PCB_FLAG_PININPOLY    = 0x00010, /*!< For pins and pads, this flag is used internally to indicate that the pin or pad overlaps a polygon on some layer.*/
+	PCB_FLAG_CLEARPOLY    = 0x00010, /*!< For polygons, this flag means that pins and vias will normally clear these polygons (thus, thermals are required for electrical connection).  When clear, polygons will solidly connect to pins and vias. */
+	PCB_FLAG_HIDENAME     = 0x00010, /*!< For elements, when set the name of the element is hidden. */
+	PCB_FLAG_DISPLAYNAME  = 0x00020, /*!< For elements, when set the names of pins are shown. */
+	PCB_FLAG_CLEARLINE    = 0x00020, /*!< For lines and arcs, the line/arc will clear polygons instead of connecting to them. */
+	PCB_FLAG_FULLPOLY     = 0x00020, /*!< For polygons, the full polygon is drawn (i.e. all parts instead of only the biggest one). */
+	PCB_FLAG_SELECTED     = 0x00040, /*!< Set when the object is selected. */
+	PCB_FLAG_ONSOLDER     = 0x00080, /*!< For elements and pads, indicates that they are on the solder side. */
+	PCB_FLAG_AUTO         = 0x00080, /*!< For lines and vias, indicates that these were created by the autorouter. */
+	PCB_FLAG_SQUARE       = 0x00100, /*!< For pins and pads, indicates a square (vs round) pin/pad. */
+	PCB_FLAG_RUBBEREND    = 0x00200, /*!< For lines, used internally for rubber band moves: indicates one end already rubber banding. */
+	PCB_FLAG_WARN         = 0x00200, /*!< For pins, vias, and pads, set to indicate a warning. */
+	PCB_FLAG_USETHERMAL   = 0x00400, /*!< Obsolete, indicates that pins/vias should be drawn with thermal fingers. */
+	PCB_FLAG_ONSILK       = 0x00400, /*!< Obsolete, old files used this to indicate lines drawn on silk. (Used by io_pcb for compatibility.) */
+	PCB_FLAG_OCTAGON      = 0x00800, /*!< Draw pins and vias as octagons. */
+	PCB_FLAG_DRC          = 0x01000, /*!< Set for objects that fail DRC: flag like FOUND flag for DRC checking. */
+	PCB_FLAG_LOCK         = 0x02000, /*!< Set for locked objects. */
+	PCB_FLAG_EDGE2        = 0x04000, /*!< For pads, indicates that the second point is closer to the edge.  For pins, indicates that the pin is closer to a horizontal edge and thus pinout text should be vertical. (Padr.Point2 is closer to outside edge also pinout text for pins is vertical) */
+	PCB_FLAG_VISIT        = 0x08000, /*!< marker to avoid re-visiting an object */
+	PCB_FLAG_NONETLIST    = 0x10000, /* element is not on the netlist and should not interfere with the netlist */
+	PCB_FLAG_MINCUT       = 0x20000, /* used by the mincut short find code */
+	PCB_FLAG_ONPOINT      = 0x40000  /*!< crosshair is on line point or arc point */
+/*	PCB_FLAG_NOCOPY     = (PCB_FLAG_FOUND | CONNECTEDFLAG | PCB_FLAG_ONPOINT)*/
+} pcb_flag_t;
+
+/* ---------------------------------------------------------------------------
+ * object types (bitfield)
+ */
+typedef enum {
+	PCB_TYPE_NONE          = 0x00000, /* special: no object */
+	PCB_TYPE_VIA           = 0x00001,
+	PCB_TYPE_ELEMENT       = 0x00002,
+	PCB_TYPE_LINE          = 0x00004,
+	PCB_TYPE_POLYGON       = 0x00008,
+	PCB_TYPE_TEXT          = 0x00010,
+	PCB_TYPE_RATLINE       = 0x00020,
+
+	PCB_TYPE_PIN           = 0x00100, /* objects that are part */
+	PCB_TYPE_PAD           = 0x00200, /* 'pin' of SMD element */
+	PCB_TYPE_ELEMENT_NAME  = 0x00400, /* of others */
+	PCB_TYPE_POLYGON_POINT = 0x00800,
+	PCB_TYPE_LINE_POINT    = 0x01000,
+	PCB_TYPE_ELEMENT_LINE  = 0x02000,
+	PCB_TYPE_ARC           = 0x04000,
+	PCB_TYPE_ELEMENT_ARC   = 0x08000,
+
+	PCB_TYPE_LOCKED        = 0x10000, /* used to tell search to include locked items. */
+	PCB_TYPE_NET           = 0x20000, /* used to select whole net. */
+
+	/* groups/properties */
+	PCB_TYPEMASK_PIN       = (PCB_TYPE_VIA | PCB_TYPE_PIN),
+	PCB_TYPEMASK_LOCK      = (PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_ARC | PCB_TYPE_POLYGON | PCB_TYPE_ELEMENT | PCB_TYPE_TEXT | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_LOCKED),
+
+	PCB_TYPEMASK_ALL       = (~0)   /* all bits set */
+} pcb_obj_type_t;
+
+#endif
diff --git a/src/copy.c b/src/copy.c
new file mode 100644
index 0000000..d1dddd0
--- /dev/null
+++ b/src/copy.c
@@ -0,0 +1,376 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* functions used to copy pins, elements ...
+ * it's necessary to copy data by calling create... since the base pointer
+ * may change cause of dynamic memory allocation
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdlib.h>
+
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "misc.h"
+#include "layer.h"
+#include "move.h"
+#include "polygon.h"
+#include "rtree.h"
+#include "select.h"
+#include "undo.h"
+#include "compat_misc.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *CopyVia(PinTypePtr);
+static void *CopyLine(LayerTypePtr, LineTypePtr);
+static void *CopyArc(LayerTypePtr, ArcTypePtr);
+static void *CopyText(LayerTypePtr, TextTypePtr);
+static void *CopyPolygon(LayerTypePtr, PolygonTypePtr);
+static void *CopyElement(ElementTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static Coord DeltaX, DeltaY;		/* movement vector */
+static ObjectFunctionType CopyFunctions = {
+	CopyLine,
+	CopyText,
+	CopyPolygon,
+	CopyVia,
+	CopyElement,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	CopyArc,
+	NULL
+};
+
+/* ---------------------------------------------------------------------------
+ * copies data from one polygon to another
+ * 'Dest' has to exist
+ */
+PolygonTypePtr CopyPolygonLowLevel(PolygonTypePtr Dest, PolygonTypePtr Src)
+{
+	pcb_cardinal_t hole = 0;
+	pcb_cardinal_t n;
+
+	for (n = 0; n < Src->PointN; n++) {
+		if (hole < Src->HoleIndexN && n == Src->HoleIndex[hole]) {
+			CreateNewHoleInPolygon(Dest);
+			hole++;
+		}
+		CreateNewPointInPolygon(Dest, Src->Points[n].X, Src->Points[n].Y);
+	}
+	SetPolygonBoundingBox(Dest);
+	Dest->Flags = Src->Flags;
+	CLEAR_FLAG(PCB_FLAG_FOUND, Dest);
+	return (Dest);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies data from one element to another and creates the destination
+ * if necessary
+ */
+ElementTypePtr
+CopyElementLowLevel(DataTypePtr Data, ElementTypePtr Dest, ElementTypePtr Src, pcb_bool uniqueName, Coord dx, Coord dy)
+{
+	int i;
+	/* release old memory if necessary */
+	if (Dest)
+		FreeElementMemory(Dest);
+
+	/* both coordinates and flags are the same */
+	Dest = CreateNewElement(Data, Dest, &PCB->Font,
+													MaskFlags(Src->Flags, PCB_FLAG_FOUND),
+													DESCRIPTION_NAME(Src), NAMEONPCB_NAME(Src),
+													VALUE_NAME(Src), DESCRIPTION_TEXT(Src).X + dx,
+													DESCRIPTION_TEXT(Src).Y + dy,
+													DESCRIPTION_TEXT(Src).Direction,
+													DESCRIPTION_TEXT(Src).Scale, MaskFlags(DESCRIPTION_TEXT(Src).Flags, PCB_FLAG_FOUND), uniqueName);
+
+	/* abort on error */
+	if (!Dest)
+		return (Dest);
+
+	ELEMENTLINE_LOOP(Src);
+	{
+		CreateNewLineInElement(Dest, line->Point1.X + dx,
+													 line->Point1.Y + dy, line->Point2.X + dx, line->Point2.Y + dy, line->Thickness);
+	}
+	END_LOOP;
+	PIN_LOOP(Src);
+	{
+		CreateNewPin(Dest, pin->X + dx, pin->Y + dy, pin->Thickness,
+								 pin->Clearance, pin->Mask, pin->DrillingHole, pin->Name, pin->Number, MaskFlags(pin->Flags, PCB_FLAG_FOUND));
+	}
+	END_LOOP;
+	PAD_LOOP(Src);
+	{
+		CreateNewPad(Dest, pad->Point1.X + dx, pad->Point1.Y + dy,
+								 pad->Point2.X + dx, pad->Point2.Y + dy, pad->Thickness,
+								 pad->Clearance, pad->Mask, pad->Name, pad->Number, MaskFlags(pad->Flags, PCB_FLAG_FOUND));
+	}
+	END_LOOP;
+	ARC_LOOP(Src);
+	{
+		CreateNewArcInElement(Dest, arc->X + dx, arc->Y + dy, arc->Width, arc->Height, arc->StartAngle, arc->Delta, arc->Thickness);
+	}
+	END_LOOP;
+
+	for (i = 0; i < Src->Attributes.Number; i++)
+		CreateNewAttribute(&Dest->Attributes, Src->Attributes.List[i].name, Src->Attributes.List[i].value);
+
+	Dest->MarkX = Src->MarkX + dx;
+	Dest->MarkY = Src->MarkY + dy;
+
+	SetElementBoundingBox(Data, Dest, &PCB->Font);
+	return (Dest);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a via
+ */
+static void *CopyVia(PinTypePtr Via)
+{
+	PinTypePtr via;
+
+	via = CreateNewVia(PCB->Data, Via->X + DeltaX, Via->Y + DeltaY,
+										 Via->Thickness, Via->Clearance, Via->Mask, Via->DrillingHole, Via->Name, MaskFlags(Via->Flags, PCB_FLAG_FOUND));
+	if (!via)
+		return (via);
+	DrawVia(via);
+	AddObjectToCreateUndoList(PCB_TYPE_VIA, via, via, via);
+	return (via);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a line
+ */
+static void *CopyLine(LayerTypePtr Layer, LineTypePtr Line)
+{
+	LineTypePtr line;
+
+	line = CreateDrawnLineOnLayer(Layer, Line->Point1.X + DeltaX,
+																Line->Point1.Y + DeltaY,
+																Line->Point2.X + DeltaX,
+																Line->Point2.Y + DeltaY, Line->Thickness, Line->Clearance, MaskFlags(Line->Flags, PCB_FLAG_FOUND));
+	if (!line)
+		return (line);
+	if (Line->Number)
+		line->Number = pcb_strdup(Line->Number);
+	DrawLine(Layer, line);
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, Layer, line, line);
+	return (line);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies an arc
+ */
+static void *CopyArc(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	ArcTypePtr arc;
+
+	arc = CreateNewArcOnLayer(Layer, Arc->X + DeltaX,
+														Arc->Y + DeltaY, Arc->Width, Arc->Height, Arc->StartAngle,
+														Arc->Delta, Arc->Thickness, Arc->Clearance, MaskFlags(Arc->Flags, PCB_FLAG_FOUND));
+	if (!arc)
+		return (arc);
+	DrawArc(Layer, arc);
+	AddObjectToCreateUndoList(PCB_TYPE_ARC, Layer, arc, arc);
+	return (arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a text
+ */
+static void *CopyText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	TextTypePtr text;
+
+	text = CreateNewText(Layer, &PCB->Font, Text->X + DeltaX,
+											 Text->Y + DeltaY, Text->Direction, Text->Scale, Text->TextString, MaskFlags(Text->Flags, PCB_FLAG_FOUND));
+	DrawText(Layer, text);
+	AddObjectToCreateUndoList(PCB_TYPE_TEXT, Layer, text, text);
+	return (text);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies a polygon
+ */
+static void *CopyPolygon(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	PolygonTypePtr polygon;
+
+	polygon = CreateNewPolygon(Layer, NoFlags());
+	CopyPolygonLowLevel(polygon, Polygon);
+	MovePolygonLowLevel(polygon, DeltaX, DeltaY);
+	if (!Layer->polygon_tree)
+		Layer->polygon_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Layer->polygon_tree, (BoxTypePtr) polygon, 0);
+	InitClip(PCB->Data, Layer, polygon);
+	DrawPolygon(Layer, polygon);
+	AddObjectToCreateUndoList(PCB_TYPE_POLYGON, Layer, polygon, polygon);
+	return (polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies an element onto the PCB.  Then does a draw.
+ */
+static void *CopyElement(ElementTypePtr Element)
+{
+
+#ifdef DEBUG
+	printf("Entered CopyElement, trying to copy element %s\n", Element->Name[1].TextString);
+#endif
+
+	ElementTypePtr element = CopyElementLowLevel(PCB->Data,
+																							 NULL, Element,
+																							 conf_core.editor.unique_names, DeltaX,
+																							 DeltaY);
+
+	/* this call clears the polygons */
+	AddObjectToCreateUndoList(PCB_TYPE_ELEMENT, element, element, element);
+	if (PCB->ElementOn && (FRONT(element) || PCB->InvisibleObjectsOn)) {
+		DrawElementName(element);
+		DrawElementPackage(element);
+	}
+	if (PCB->PinOn) {
+		DrawElementPinsAndPads(element);
+	}
+#ifdef DEBUG
+	printf(" ... Leaving CopyElement.\n");
+#endif
+	return (element);
+}
+
+/* ---------------------------------------------------------------------------
+ * pastes the contents of the buffer to the layout. Only visible objects
+ * are handled by the routine.
+ */
+pcb_bool CopyPastebufferToLayout(Coord X, Coord Y)
+{
+	pcb_cardinal_t i;
+	pcb_bool changed = pcb_false;
+
+#ifdef DEBUG
+	printf("Entering CopyPastebufferToLayout.....\n");
+#endif
+
+	/* set movement vector */
+	DeltaX = X - PASTEBUFFER->X, DeltaY = Y - PASTEBUFFER->Y;
+
+	/* paste all layers */
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		LayerTypePtr sourcelayer = &PASTEBUFFER->Data->Layer[i], destlayer = LAYER_PTR(i);
+
+		if (destlayer->On) {
+			changed = changed || (!LAYER_IS_EMPTY(sourcelayer));
+			LINE_LOOP(sourcelayer);
+			{
+				CopyLine(destlayer, line);
+			}
+			END_LOOP;
+			ARC_LOOP(sourcelayer);
+			{
+				CopyArc(destlayer, arc);
+			}
+			END_LOOP;
+			TEXT_LOOP(sourcelayer);
+			{
+				CopyText(destlayer, text);
+			}
+			END_LOOP;
+			POLYGON_LOOP(sourcelayer);
+			{
+				CopyPolygon(destlayer, polygon);
+			}
+			END_LOOP;
+		}
+	}
+
+	/* paste elements */
+	if (PCB->PinOn && PCB->ElementOn) {
+		ELEMENT_LOOP(PASTEBUFFER->Data);
+		{
+#ifdef DEBUG
+			printf("In CopyPastebufferToLayout, pasting element %s\n", element->Name[1].TextString);
+#endif
+			if (FRONT(element) || PCB->InvisibleObjectsOn) {
+				CopyElement(element);
+				changed = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+
+	/* finally the vias */
+	if (PCB->ViaOn) {
+		changed |= (pinlist_length(&(PASTEBUFFER->Data->Via)) != 0);
+		VIA_LOOP(PASTEBUFFER->Data);
+		{
+			CopyVia(via);
+		}
+		END_LOOP;
+	}
+
+	if (changed) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+
+#ifdef DEBUG
+	printf("  .... Leaving CopyPastebufferToLayout.\n");
+#endif
+
+	return (changed);
+}
+
+/* ---------------------------------------------------------------------------
+ * copies the object identified by its data pointers and the type
+ * the new objects is moved by DX,DY
+ * I assume that the appropriate layer ... is switched on
+ */
+void *CopyObject(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord DX, Coord DY)
+{
+	void *ptr;
+
+	/* setup movement vector */
+	DeltaX = DX;
+	DeltaY = DY;
+
+	/* the subroutines add the objects to the undo-list */
+	ptr = ObjectOperation(&CopyFunctions, Type, Ptr1, Ptr2, Ptr3);
+	IncrementUndoSerialNumber();
+	return (ptr);
+}
diff --git a/src/copy.h b/src/copy.h
new file mode 100644
index 0000000..09aa7b1
--- /dev/null
+++ b/src/copy.h
@@ -0,0 +1,47 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for copy routines */
+
+#ifndef	PCB_COPY_H
+#define	PCB_COPY_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some defines
+ */
+#define	COPY_TYPES              \
+	(PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_TEXT | \
+	PCB_TYPE_ELEMENT | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_POLYGON | PCB_TYPE_ARC)
+
+
+PolygonTypePtr CopyPolygonLowLevel(PolygonTypePtr, PolygonTypePtr);
+ElementTypePtr CopyElementLowLevel(DataTypePtr, ElementTypePtr, ElementTypePtr, pcb_bool, Coord, Coord);
+pcb_bool CopyPastebufferToLayout(Coord, Coord);
+void *CopyObject(int, void *, void *, void *, Coord, Coord);
+
+#endif
diff --git a/src/create.c b/src/create.c
new file mode 100644
index 0000000..87f6300
--- /dev/null
+++ b/src/create.c
@@ -0,0 +1,952 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* functions used to create vias, pins ... */
+
+#include "config.h"
+
+#include <assert.h>
+#include <memory.h>
+#include <setjmp.h>
+#include <stdlib.h>
+
+#include "conf_core.h"
+
+#include "create.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "layer.h"
+#include "rtree.h"
+#include "search.h"
+#include "undo.h"
+#include "plug_io.h"
+#include "stub_vendor.h"
+#include "hid_actions.h"
+#include "paths.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+
+/* current object ID; incremented after each creation of an object */
+static long int ID = 1;
+
+static pcb_bool be_lenient = pcb_false;
+
+/* ----------------------------------------------------------------------
+ * some local prototypes
+ */
+static void AddTextToElement(TextTypePtr, FontTypePtr, Coord, Coord, unsigned, char *, int, FlagType);
+
+/* ---------------------------------------------------------------------------
+ *  Set the lenience mode.
+ */
+
+void CreateBeLenient(pcb_bool v)
+{
+	be_lenient = v;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new paste buffer
+ */
+DataTypePtr CreateNewBuffer(void)
+{
+	DataTypePtr data;
+	data = (DataTypePtr) calloc(1, sizeof(DataType));
+	data->pcb = (PCBTypePtr) PCB;
+	return data;
+}
+
+/* ---------------------------------------------------------------------------
+ * Perhaps PCB should internally just use the Settings colors?  For now,
+ * use this to set PCB colors so the config can reassign PCB colors.
+ */
+void pcb_colors_from_settings(PCBTypePtr ptr)
+{
+	int i;
+
+	/* copy default settings */
+	ptr->ConnectedColor = conf_core.appearance.color.connected;
+	ptr->ElementColor = conf_core.appearance.color.element;
+	ptr->ElementColor_nonetlist = conf_core.appearance.color.element_nonetlist;
+	ptr->RatColor = conf_core.appearance.color.rat;
+	ptr->InvisibleObjectsColor = conf_core.appearance.color.invisible_objects;
+	ptr->InvisibleMarkColor = conf_core.appearance.color.invisible_mark;
+	ptr->ElementSelectedColor = conf_core.appearance.color.element_selected;
+	ptr->RatSelectedColor = conf_core.appearance.color.rat_selected;
+	ptr->PinColor = conf_core.appearance.color.pin;
+	ptr->PinSelectedColor = conf_core.appearance.color.pin_selected;
+	ptr->PinNameColor = conf_core.appearance.color.pin_name;
+	ptr->ViaColor = conf_core.appearance.color.via;
+	ptr->ViaSelectedColor = conf_core.appearance.color.via_selected;
+	ptr->WarnColor = conf_core.appearance.color.warn;
+	ptr->MaskColor = conf_core.appearance.color.mask;
+	for (i = 0; i < MAX_LAYER; i++) {
+		ptr->Data->Layer[i].Color = conf_core.appearance.color.layer[i];
+		ptr->Data->Layer[i].SelectedColor = conf_core.appearance.color.layer_selected[i];
+	}
+	ptr->Data->Layer[component_silk_layer].Color =
+		conf_core.editor.show_solder_side ? conf_core.appearance.color.invisible_objects : conf_core.appearance.color.element;
+	ptr->Data->Layer[component_silk_layer].SelectedColor = conf_core.appearance.color.element_selected;
+	ptr->Data->Layer[solder_silk_layer].Color = conf_core.editor.show_solder_side ? conf_core.appearance.color.element : conf_core.appearance.color.invisible_objects;
+	ptr->Data->Layer[solder_silk_layer].SelectedColor = conf_core.appearance.color.element_selected;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new PCB
+ */
+PCBTypePtr CreateNewPCB_(pcb_bool SetDefaultNames)
+{
+	PCBTypePtr ptr, save;
+	int i;
+
+	/* allocate memory, switch all layers on and copy resources */
+	ptr = (PCBTypePtr) calloc(1, sizeof(PCBType));
+	ptr->Data = CreateNewBuffer();
+	ptr->Data->pcb = (PCBTypePtr) ptr;
+
+	ptr->ThermStyle = 4;
+	ptr->IsleArea = 2.e8;
+	ptr->SilkActive = pcb_false;
+	ptr->RatDraw = pcb_false;
+
+	/* NOTE: we used to set all the pcb flags on ptr here, but we don't need to do that anymore due to the new conf system */
+						/* this is the most useful starting point for now */
+
+	ptr->Grid = conf_core.editor.grid;
+	ParseGroupString(conf_core.design.groups, &ptr->LayerGroups, MAX_LAYER);
+	save = PCB;
+	PCB = ptr;
+	hid_action("RouteStylesChanged");
+	PCB = save;
+	ptr->Zoom = conf_core.editor.zoom;
+	ptr->MaxWidth = conf_core.design.max_width;
+	ptr->MaxHeight = conf_core.design.max_height;
+	ptr->ID = ID++;
+	ptr->ThermScale = 0.5;
+
+	ptr->Bloat = conf_core.design.bloat;
+	ptr->Shrink = conf_core.design.shrink;
+	ptr->minWid = conf_core.design.min_wid;
+	ptr->minSlk = conf_core.design.min_slk;
+	ptr->minDrill = conf_core.design.min_drill;
+	ptr->minRing = conf_core.design.min_ring;
+
+	for (i = 0; i < MAX_LAYER; i++)
+		ptr->Data->Layer[i].Name = pcb_strdup(conf_core.design.default_layer_name[i]);
+
+	CreateDefaultFont(ptr);
+
+	return (ptr);
+}
+
+PCBTypePtr CreateNewPCB()
+{
+	PCBTypePtr old, nw;
+	int dpcb;
+
+	old = PCB;
+
+	PCB = NULL;
+
+	dpcb = -1;
+	pcb_io_err_inhibit_inc();
+	conf_list_foreach_path_first(dpcb, &conf_core.rc.default_pcb_file, LoadPCB(__path__, NULL, pcb_false, 1));
+	pcb_io_err_inhibit_dec();
+
+	if (dpcb == 0) {
+		nw = PCB;
+		if (nw->Filename != NULL) {
+			/* make sure the new PCB doesn't inherit the name of the default pcb */
+			free(nw->Filename);
+			nw->Filename = NULL;
+		}
+	}
+	else {
+		nw = NULL;
+	}
+
+	PCB = old;
+	return nw;
+}
+
+
+/* This post-processing step adds the top and bottom silk layers to a
+ * pre-existing PCB.
+ */
+int CreateNewPCBPost(PCBTypePtr pcb, int use_defaults)
+{
+	/* copy default settings */
+	pcb_colors_from_settings(pcb);
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new via
+ */
+PinTypePtr
+CreateNewVia(DataTypePtr Data,
+						 Coord X, Coord Y,
+						 Coord Thickness, Coord Clearance, Coord Mask, Coord DrillingHole, const char *Name, FlagType Flags)
+{
+	PinTypePtr Via;
+
+	if (!be_lenient) {
+		VIA_LOOP(Data);
+		{
+			if (Distance(X, Y, via->X, via->Y) <= via->DrillingHole / 2 + DrillingHole / 2) {
+				Message(PCB_MSG_DEFAULT, _("%m+Dropping via at %$mD because it's hole would overlap with the via "
+									"at %$mD\n"), conf_core.editor.grid_unit->allow, X, Y, via->X, via->Y);
+				return (NULL);					/* don't allow via stacking */
+			}
+		}
+		END_LOOP;
+	}
+
+	Via = GetViaMemory(Data);
+
+	if (!Via)
+		return (Via);
+	/* copy values */
+	Via->X = X;
+	Via->Y = Y;
+	Via->Thickness = Thickness;
+	Via->Clearance = Clearance;
+	Via->Mask = Mask;
+	Via->DrillingHole = stub_vendorDrillMap(DrillingHole);
+	if (Via->DrillingHole != DrillingHole) {
+		Message(PCB_MSG_DEFAULT, _("%m+Mapped via drill hole to %$mS from %$mS per vendor table\n"),
+						conf_core.editor.grid_unit->allow, Via->DrillingHole, DrillingHole);
+	}
+
+	Via->Name = pcb_strdup_null(Name);
+	Via->Flags = Flags;
+	CLEAR_FLAG(PCB_FLAG_WARN, Via);
+	SET_FLAG(PCB_FLAG_VIA, Via);
+	Via->ID = ID++;
+
+	/*
+	 * don't complain about MIN_PINORVIACOPPER on a mounting hole (pure
+	 * hole)
+	 */
+	if (!TEST_FLAG(PCB_FLAG_HOLE, Via) && (Via->Thickness < Via->DrillingHole + MIN_PINORVIACOPPER)) {
+		Via->Thickness = Via->DrillingHole + MIN_PINORVIACOPPER;
+		Message(PCB_MSG_DEFAULT, _("%m+Increased via thickness to %$mS to allow enough copper"
+							" at %$mD.\n"), conf_core.editor.grid_unit->allow, Via->Thickness, Via->X, Via->Y);
+	}
+
+	pcb_add_via(Data, Via);
+	return (Via);
+}
+
+void pcb_add_via(DataType *Data, PinType *Via)
+{
+	SetPinBoundingBox(Via);
+	if (!Data->via_tree)
+		Data->via_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Data->via_tree, (BoxTypePtr) Via, 0);
+}
+
+struct line_info {
+	Coord X1, X2, Y1, Y2;
+	Coord Thickness;
+	FlagType Flags;
+	LineType test, *ans;
+	jmp_buf env;
+};
+
+static r_dir_t line_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct line_info *i = (struct line_info *) cl;
+
+	if (line->Point1.X == i->X1 && line->Point2.X == i->X2 && line->Point1.Y == i->Y1 && line->Point2.Y == i->Y2) {
+		i->ans = (LineTypePtr) (-1);
+		longjmp(i->env, 1);
+	}
+	/* check the other point order */
+	if (line->Point1.X == i->X1 && line->Point2.X == i->X2 && line->Point1.Y == i->Y1 && line->Point2.Y == i->Y2) {
+		i->ans = (LineTypePtr) (-1);
+		longjmp(i->env, 1);
+	}
+	if (line->Point2.X == i->X1 && line->Point1.X == i->X2 && line->Point2.Y == i->Y1 && line->Point1.Y == i->Y2) {
+		i->ans = (LineTypePtr) - 1;
+		longjmp(i->env, 1);
+	}
+	/* remove unnecessary line points */
+	if (line->Thickness == i->Thickness
+			/* don't merge lines if the clear flags differ  */
+			&& TEST_FLAG(PCB_FLAG_CLEARLINE, line) == TEST_FLAG(PCB_FLAG_CLEARLINE, i)) {
+		if (line->Point1.X == i->X1 && line->Point1.Y == i->Y1) {
+			i->test.Point1.X = line->Point2.X;
+			i->test.Point1.Y = line->Point2.Y;
+			i->test.Point2.X = i->X2;
+			i->test.Point2.Y = i->Y2;
+			if (IsPointOnLine(i->X1, i->Y1, 0.0, &i->test)) {
+				i->ans = line;
+				longjmp(i->env, 1);
+			}
+		}
+		else if (line->Point2.X == i->X1 && line->Point2.Y == i->Y1) {
+			i->test.Point1.X = line->Point1.X;
+			i->test.Point1.Y = line->Point1.Y;
+			i->test.Point2.X = i->X2;
+			i->test.Point2.Y = i->Y2;
+			if (IsPointOnLine(i->X1, i->Y1, 0.0, &i->test)) {
+				i->ans = line;
+				longjmp(i->env, 1);
+			}
+		}
+		else if (line->Point1.X == i->X2 && line->Point1.Y == i->Y2) {
+			i->test.Point1.X = line->Point2.X;
+			i->test.Point1.Y = line->Point2.Y;
+			i->test.Point2.X = i->X1;
+			i->test.Point2.Y = i->Y1;
+			if (IsPointOnLine(i->X2, i->Y2, 0.0, &i->test)) {
+				i->ans = line;
+				longjmp(i->env, 1);
+			}
+		}
+		else if (line->Point2.X == i->X2 && line->Point2.Y == i->Y2) {
+			i->test.Point1.X = line->Point1.X;
+			i->test.Point1.Y = line->Point1.Y;
+			i->test.Point2.X = i->X1;
+			i->test.Point2.Y = i->Y1;
+			if (IsPointOnLine(i->X2, i->Y2, 0.0, &i->test)) {
+				i->ans = line;
+				longjmp(i->env, 1);
+			}
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * creates a new line on a layer and checks for overlap and extension
+ */
+LineTypePtr
+CreateDrawnLineOnLayer(LayerTypePtr Layer,
+											 Coord X1, Coord Y1, Coord X2, Coord Y2, Coord Thickness, Coord Clearance, FlagType Flags)
+{
+	struct line_info info;
+	BoxType search;
+
+	search.X1 = MIN(X1, X2);
+	search.X2 = MAX(X1, X2);
+	search.Y1 = MIN(Y1, Y2);
+	search.Y2 = MAX(Y1, Y2);
+	if (search.Y2 == search.Y1)
+		search.Y2++;
+	if (search.X2 == search.X1)
+		search.X2++;
+	info.X1 = X1;
+	info.X2 = X2;
+	info.Y1 = Y1;
+	info.Y2 = Y2;
+	info.Thickness = Thickness;
+	info.Flags = Flags;
+	info.test.Thickness = 0;
+	info.test.Flags = NoFlags();
+	info.ans = NULL;
+	/* prevent stacking of duplicate lines
+	 * and remove needless intermediate points
+	 * verify that the layer is on the board first!
+	 */
+	if (setjmp(info.env) == 0) {
+		r_search(Layer->line_tree, &search, NULL, line_callback, &info, NULL);
+		return CreateNewLineOnLayer(Layer, X1, Y1, X2, Y2, Thickness, Clearance, Flags);
+	}
+
+	if ((void *) info.ans == (void *) (-1))
+		return NULL;								/* stacked line */
+	/* remove unnecessary points */
+	if (info.ans) {
+		/* must do this BEFORE getting new line memory */
+		MoveObjectToRemoveUndoList(PCB_TYPE_LINE, Layer, info.ans, info.ans);
+		X1 = info.test.Point1.X;
+		X2 = info.test.Point2.X;
+		Y1 = info.test.Point1.Y;
+		Y2 = info.test.Point2.Y;
+	}
+	return CreateNewLineOnLayer(Layer, X1, Y1, X2, Y2, Thickness, Clearance, Flags);
+}
+
+LineTypePtr
+CreateNewLineOnLayer(LayerTypePtr Layer,
+										 Coord X1, Coord Y1, Coord X2, Coord Y2, Coord Thickness, Coord Clearance, FlagType Flags)
+{
+	LineTypePtr Line;
+
+	Line = GetLineMemory(Layer);
+	if (!Line)
+		return (Line);
+	Line->ID = ID++;
+	Line->Flags = Flags;
+	CLEAR_FLAG(PCB_FLAG_RAT, Line);
+	Line->Thickness = Thickness;
+	Line->Clearance = Clearance;
+	Line->Point1.X = X1;
+	Line->Point1.Y = Y1;
+	Line->Point1.ID = ID++;
+	Line->Point2.X = X2;
+	Line->Point2.Y = Y2;
+	Line->Point2.ID = ID++;
+	pcb_add_line_on_layer(Layer, Line);
+	return (Line);
+}
+
+void pcb_add_line_on_layer(LayerType *Layer, LineType *Line)
+{
+	SetLineBoundingBox(Line);
+	if (!Layer->line_tree)
+		Layer->line_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Layer->line_tree, (BoxTypePtr) Line, 0);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * creates a new rat-line
+ */
+RatTypePtr
+CreateNewRat(DataTypePtr Data, Coord X1, Coord Y1,
+						 Coord X2, Coord Y2, pcb_cardinal_t group1, pcb_cardinal_t group2, Coord Thickness, FlagType Flags)
+{
+	RatTypePtr Line = GetRatMemory(Data);
+
+	if (!Line)
+		return (Line);
+
+	Line->ID = ID++;
+	Line->Flags = Flags;
+	SET_FLAG(PCB_FLAG_RAT, Line);
+	Line->Thickness = Thickness;
+	Line->Point1.X = X1;
+	Line->Point1.Y = Y1;
+	Line->Point1.ID = ID++;
+	Line->Point2.X = X2;
+	Line->Point2.Y = Y2;
+	Line->Point2.ID = ID++;
+	Line->group1 = group1;
+	Line->group2 = group2;
+	SetLineBoundingBox((LineTypePtr) Line);
+	if (!Data->rat_tree)
+		Data->rat_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Data->rat_tree, &Line->BoundingBox, 0);
+	return (Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new arc on a layer
+ */
+ArcTypePtr
+CreateNewArcOnLayer(LayerTypePtr Layer,
+										Coord X1, Coord Y1,
+										Coord width, Coord height, Angle sa, Angle dir, Coord Thickness, Coord Clearance, FlagType Flags)
+{
+	ArcTypePtr Arc;
+
+	ARC_LOOP(Layer);
+	{
+		if (arc->X == X1 && arc->Y == Y1 && arc->Width == width &&
+				NormalizeAngle(arc->StartAngle) == NormalizeAngle(sa) && arc->Delta == dir)
+			return (NULL);						/* prevent stacked arcs */
+	}
+	END_LOOP;
+	Arc = GetArcMemory(Layer);
+	if (!Arc)
+		return (Arc);
+
+	Arc->ID = ID++;
+	Arc->Flags = Flags;
+	Arc->Thickness = Thickness;
+	Arc->Clearance = Clearance;
+	Arc->X = X1;
+	Arc->Y = Y1;
+	Arc->Width = width;
+	Arc->Height = height;
+	Arc->StartAngle = sa;
+	Arc->Delta = dir;
+	pcb_add_arc_on_layer(Layer, Arc);
+	return (Arc);
+}
+
+void pcb_add_arc_on_layer(LayerType *Layer, ArcType *Arc)
+{
+	SetArcBoundingBox(Arc);
+	if (!Layer->arc_tree)
+		Layer->arc_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new polygon from the old formats rectangle data
+ */
+PolygonTypePtr CreateNewPolygonFromRectangle(LayerTypePtr Layer, Coord X1, Coord Y1, Coord X2, Coord Y2, FlagType Flags)
+{
+	PolygonTypePtr polygon = CreateNewPolygon(Layer, Flags);
+	if (!polygon)
+		return (polygon);
+
+	CreateNewPointInPolygon(polygon, X1, Y1);
+	CreateNewPointInPolygon(polygon, X2, Y1);
+	CreateNewPointInPolygon(polygon, X2, Y2);
+	CreateNewPointInPolygon(polygon, X1, Y2);
+
+	pcb_add_polygon_on_layer(Layer, polygon);
+	return (polygon);
+}
+
+void pcb_add_polygon_on_layer(LayerType *Layer, PolygonType *polygon)
+{
+	SetPolygonBoundingBox(polygon);
+	if (!Layer->polygon_tree)
+		Layer->polygon_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Layer->polygon_tree, (BoxTypePtr) polygon, 0);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new text on a layer
+ */
+TextTypePtr
+CreateNewText(LayerTypePtr Layer, FontTypePtr PCBFont,
+							Coord X, Coord Y, unsigned Direction, int Scale, char *TextString, FlagType Flags)
+{
+	TextType *text;
+
+	if (TextString == NULL)
+		return NULL;
+
+	text = GetTextMemory(Layer);
+	if (text == NULL)
+		return NULL;
+
+	/* copy values, width and height are set by drawing routine
+	 * because at this point we don't know which symbols are available
+	 */
+	text->X = X;
+	text->Y = Y;
+	text->Direction = Direction;
+	text->Flags = Flags;
+	text->Scale = Scale;
+	text->TextString = pcb_strdup(TextString);
+
+	pcb_add_text_on_layer(Layer, text, PCBFont);
+
+	return (text);
+}
+
+void pcb_add_text_on_layer(LayerType *Layer, TextType *text, FontType *PCBFont)
+{
+	/* calculate size of the bounding box */
+	SetTextBoundingBox(PCBFont, text);
+	text->ID = ID++;
+	if (!Layer->text_tree)
+		Layer->text_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Layer->text_tree, (BoxTypePtr) text, 0);
+}
+
+
+
+/* ---------------------------------------------------------------------------
+ * creates a new polygon on a layer
+ */
+PolygonTypePtr CreateNewPolygon(LayerTypePtr Layer, FlagType Flags)
+{
+	PolygonTypePtr polygon = GetPolygonMemory(Layer);
+
+	/* copy values */
+	polygon->Flags = Flags;
+	polygon->ID = ID++;
+	polygon->Clipped = NULL;
+	polygon->NoHoles = NULL;
+	polygon->NoHolesValid = 0;
+	return (polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new point in a polygon
+ */
+PointTypePtr CreateNewPointInPolygon(PolygonTypePtr Polygon, Coord X, Coord Y)
+{
+	PointTypePtr point = GetPointMemoryInPolygon(Polygon);
+
+	/* copy values */
+	point->X = X;
+	point->Y = Y;
+	point->ID = ID++;
+	return (point);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new hole in a polygon
+ */
+PolygonType *CreateNewHoleInPolygon(PolygonType * Polygon)
+{
+	pcb_cardinal_t *holeindex = GetHoleIndexMemoryInPolygon(Polygon);
+	*holeindex = Polygon->PointN;
+	return Polygon;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates an new element
+ * memory is allocated if needed
+ */
+ElementTypePtr
+CreateNewElement(DataTypePtr Data, ElementTypePtr Element,
+								 FontTypePtr PCBFont,
+								 FlagType Flags,
+								 char *Description, char *NameOnPCB, char *Value,
+								 Coord TextX, Coord TextY, pcb_uint8_t Direction, int TextScale, FlagType TextFlags, pcb_bool uniqueName)
+{
+#ifdef DEBUG
+	printf("Entered CreateNewElement.....\n");
+#endif
+
+	if (!Element)
+		Element = GetElementMemory(Data);
+
+	/* copy values and set additional information */
+	TextScale = MAX(MIN_TEXTSCALE, TextScale);
+	AddTextToElement(&DESCRIPTION_TEXT(Element), PCBFont, TextX, TextY, Direction, Description, TextScale, TextFlags);
+	if (uniqueName)
+		NameOnPCB = UniqueElementName(Data, NameOnPCB);
+	AddTextToElement(&NAMEONPCB_TEXT(Element), PCBFont, TextX, TextY, Direction, NameOnPCB, TextScale, TextFlags);
+	AddTextToElement(&VALUE_TEXT(Element), PCBFont, TextX, TextY, Direction, Value, TextScale, TextFlags);
+	DESCRIPTION_TEXT(Element).Element = Element;
+	NAMEONPCB_TEXT(Element).Element = Element;
+	VALUE_TEXT(Element).Element = Element;
+	Element->Flags = Flags;
+	Element->ID = ID++;
+
+#ifdef DEBUG
+	printf("  .... Leaving CreateNewElement.\n");
+#endif
+
+	return (Element);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new arc in an element
+ */
+ArcTypePtr
+CreateNewArcInElement(ElementTypePtr Element,
+											Coord X, Coord Y, Coord Width, Coord Height, Angle angle, Angle delta, Coord Thickness)
+{
+	ArcType *arc = GetElementArcMemory(Element);
+
+	/* set Delta (0,360], StartAngle in [0,360) */
+	if (delta < 0) {
+		delta = -delta;
+		angle -= delta;
+	}
+	angle = NormalizeAngle(angle);
+	delta = NormalizeAngle(delta);
+	if (delta == 0)
+		delta = 360;
+
+	/* copy values */
+	arc->X = X;
+	arc->Y = Y;
+	arc->Width = Width;
+	arc->Height = Height;
+	arc->StartAngle = angle;
+	arc->Delta = delta;
+	arc->Thickness = Thickness;
+	arc->ID = ID++;
+	return arc;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new line for an element
+ */
+LineTypePtr CreateNewLineInElement(ElementTypePtr Element, Coord X1, Coord Y1, Coord X2, Coord Y2, Coord Thickness)
+{
+	LineType *line;
+
+	if (Thickness == 0)
+		return NULL;
+
+	line = GetElementLineMemory(Element);
+
+	/* copy values */
+	line->Point1.X = X1;
+	line->Point1.Y = Y1;
+	line->Point2.X = X2;
+	line->Point2.Y = Y2;
+	line->Thickness = Thickness;
+	line->Flags = NoFlags();
+	line->ID = ID++;
+	return line;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new pin in an element
+ */
+PinTypePtr
+CreateNewPin(ElementTypePtr Element,
+						 Coord X, Coord Y,
+						 Coord Thickness, Coord Clearance, Coord Mask, Coord DrillingHole, char *Name, char *Number, FlagType Flags)
+{
+	PinTypePtr pin = GetPinMemory(Element);
+
+	/* copy values */
+	pin->X = X;
+	pin->Y = Y;
+	pin->Thickness = Thickness;
+	pin->Clearance = Clearance;
+	pin->Mask = Mask;
+	pin->Name = pcb_strdup_null(Name);
+	pin->Number = pcb_strdup_null(Number);
+	pin->Flags = Flags;
+	CLEAR_FLAG(PCB_FLAG_WARN, pin);
+	SET_FLAG(PCB_FLAG_PIN, pin);
+	pin->ID = ID++;
+	pin->Element = Element;
+
+	/*
+	 * If there is no vendor drill map installed, this will simply
+	 * return DrillingHole.
+	 */
+	pin->DrillingHole = stub_vendorDrillMap(DrillingHole);
+
+	/* Unless we should not map drills on this element, map them! */
+	if (stub_vendorIsElementMappable(Element)) {
+		if (pin->DrillingHole < MIN_PINORVIASIZE) {
+			Message(PCB_MSG_DEFAULT, _("%m+Did not map pin #%s (%s) drill hole because %$mS is below the minimum allowed size\n"),
+							conf_core.editor.grid_unit->allow, UNKNOWN(Number), UNKNOWN(Name), pin->DrillingHole);
+			pin->DrillingHole = DrillingHole;
+		}
+		else if (pin->DrillingHole > MAX_PINORVIASIZE) {
+			Message(PCB_MSG_DEFAULT, _("%m+Did not map pin #%s (%s) drill hole because %$mS is above the maximum allowed size\n"),
+							conf_core.editor.grid_unit->allow, UNKNOWN(Number), UNKNOWN(Name), pin->DrillingHole);
+			pin->DrillingHole = DrillingHole;
+		}
+		else if (!TEST_FLAG(PCB_FLAG_HOLE, pin)
+						 && (pin->DrillingHole > pin->Thickness - MIN_PINORVIACOPPER)) {
+			Message(PCB_MSG_DEFAULT, _("%m+Did not map pin #%s (%s) drill hole because %$mS does not leave enough copper\n"),
+							conf_core.editor.grid_unit->allow, UNKNOWN(Number), UNKNOWN(Name), pin->DrillingHole);
+			pin->DrillingHole = DrillingHole;
+		}
+	}
+	else {
+		pin->DrillingHole = DrillingHole;
+	}
+
+	if (pin->DrillingHole != DrillingHole) {
+		Message(PCB_MSG_DEFAULT, _("%m+Mapped pin drill hole to %$mS from %$mS per vendor table\n"),
+						conf_core.editor.grid_unit->allow, pin->DrillingHole, DrillingHole);
+	}
+
+	return (pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new pad in an element
+ */
+PadTypePtr
+CreateNewPad(ElementTypePtr Element,
+						 Coord X1, Coord Y1, Coord X2,
+						 Coord Y2, Coord Thickness, Coord Clearance, Coord Mask, char *Name, char *Number, FlagType Flags)
+{
+	PadTypePtr pad = GetPadMemory(Element);
+
+	/* copy values */
+	if (X1 > X2 || (X1 == X2 && Y1 > Y2)) {
+		pad->Point1.X = X2;
+		pad->Point1.Y = Y2;
+		pad->Point2.X = X1;
+		pad->Point2.Y = Y1;
+	}
+	else {
+		pad->Point1.X = X1;
+		pad->Point1.Y = Y1;
+		pad->Point2.X = X2;
+		pad->Point2.Y = Y2;
+	}
+	pad->Thickness = Thickness;
+	pad->Clearance = Clearance;
+	pad->Mask = Mask;
+	pad->Name = pcb_strdup_null(Name);
+	pad->Number = pcb_strdup_null(Number);
+	pad->Flags = Flags;
+	CLEAR_FLAG(PCB_FLAG_WARN, pad);
+	pad->ID = ID++;
+	pad->Element = Element;
+	return (pad);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new textobject as part of an element
+ * copies the values to the appropriate text object
+ */
+static void
+AddTextToElement(TextTypePtr Text, FontTypePtr PCBFont,
+								 Coord X, Coord Y, unsigned Direction, char *TextString, int Scale, FlagType Flags)
+{
+	free(Text->TextString);
+	Text->TextString = (TextString && *TextString) ? pcb_strdup(TextString) : NULL;
+	Text->X = X;
+	Text->Y = Y;
+	Text->Direction = Direction;
+	Text->Flags = Flags;
+	Text->Scale = Scale;
+
+	/* calculate size of the bounding box */
+	SetTextBoundingBox(PCBFont, Text);
+	Text->ID = ID++;
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a new line in a symbol
+ */
+LineTypePtr CreateNewLineInSymbol(SymbolTypePtr Symbol, Coord X1, Coord Y1, Coord X2, Coord Y2, Coord Thickness)
+{
+	LineTypePtr line = Symbol->Line;
+
+	/* realloc new memory if necessary and clear it */
+	if (Symbol->LineN >= Symbol->LineMax) {
+		Symbol->LineMax += STEP_SYMBOLLINE;
+		line = (LineTypePtr) realloc(line, Symbol->LineMax * sizeof(LineType));
+		Symbol->Line = line;
+		memset(line + Symbol->LineN, 0, STEP_SYMBOLLINE * sizeof(LineType));
+	}
+
+	/* copy values */
+	line = line + Symbol->LineN++;
+	line->Point1.X = X1;
+	line->Point1.Y = Y1;
+	line->Point2.X = X2;
+	line->Point2.Y = Y2;
+	line->Thickness = Thickness;
+	return (line);
+}
+
+/* ---------------------------------------------------------------------------
+ * parses a file with font information and installs it into the provided PCB
+ * checks directories given as colon separated list by resource fontPath
+ * if the fonts filename doesn't contain a directory component
+ */
+void CreateDefaultFont(PCBTypePtr pcb)
+{
+	int res = -1;
+	pcb_io_err_inhibit_inc();
+	conf_list_foreach_path_first(res, &conf_core.rc.default_font_file, ParseFont(&pcb->Font, __path__));
+	pcb_io_err_inhibit_dec();
+
+	if (res != 0) {
+		const char *s;
+		gds_t buff;
+		s = conf_concat_strlist(&conf_core.rc.default_font_file, &buff, NULL, ':');
+		Message(PCB_MSG_ERROR, _("Can't find font-symbol-file - there won't be font in this design. Searched: '%s'\n"), s);
+		gds_uninit(&buff);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds a new line to the rubberband list of 'Crosshair.AttachedObject'
+ * if Layer == 0  it is a rat line
+ */
+RubberbandTypePtr CreateNewRubberbandEntry(LayerTypePtr Layer, LineTypePtr Line, PointTypePtr MovedPoint)
+{
+	RubberbandTypePtr ptr = GetRubberbandMemory();
+
+	/* we toggle the PCB_FLAG_RUBBEREND of the line to determine if */
+	/* both points are being moved. */
+	TOGGLE_FLAG(PCB_FLAG_RUBBEREND, Line);
+	ptr->Layer = Layer;
+	ptr->Line = Line;
+	ptr->MovedPoint = MovedPoint;
+	return (ptr);
+}
+
+/* ---------------------------------------------------------------------------
+ * Add a new net to the netlist menu
+ */
+LibraryMenuTypePtr CreateNewNet(LibraryTypePtr lib, char *name, const char *style)
+{
+	LibraryMenuTypePtr menu;
+	char temp[64];
+
+	sprintf(temp, "  %s", name);
+	menu = GetLibraryMenuMemory(lib, NULL);
+	menu->Name = pcb_strdup(temp);
+	menu->flag = 1;								/* net is enabled by default */
+	if (style == NULL || NSTRCMP("(unknown)", style) == 0)
+		menu->Style = NULL;
+	else
+		menu->Style = pcb_strdup(style);
+	return (menu);
+}
+
+/* ---------------------------------------------------------------------------
+ * Add a connection to the net
+ */
+LibraryEntryTypePtr CreateNewConnection(LibraryMenuTypePtr net, char *conn)
+{
+	LibraryEntryTypePtr entry = GetLibraryEntryMemory(net);
+
+	entry->ListEntry = pcb_strdup_null(conn);
+	entry->ListEntry_dontfree = 0;
+
+	return (entry);
+}
+
+/* ---------------------------------------------------------------------------
+ * Add an attribute to a list.
+ */
+AttributeTypePtr CreateNewAttribute(AttributeListTypePtr list, const char *name, const char *value)
+{
+	if (list->Number >= list->Max) {
+		list->Max += 10;
+		list->List = (AttributeType *) realloc(list->List, list->Max * sizeof(AttributeType));
+	}
+	list->List[list->Number].name = pcb_strdup_null(name);
+	list->List[list->Number].value = pcb_strdup_null(value);
+	list->Number++;
+	return &list->List[list->Number - 1];
+}
+
+void CreateIDBump(int min_id)
+{
+	if (ID < min_id)
+		ID = min_id;
+}
+
+void CreateIDReset(void)
+{
+	ID = 1;
+}
+
+long int CreateIDGet(void)
+{
+	return ID++;
+}
diff --git a/src/create.h b/src/create.h
new file mode 100644
index 0000000..634da18
--- /dev/null
+++ b/src/create.h
@@ -0,0 +1,81 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for create routines */
+
+#ifndef	PCB_CREATE_H
+#define	PCB_CREATE_H
+
+#include "global.h"
+
+/* TRUE during file loads, for example to allow overlapping vias.
+   FALSE otherwise, to stop the user from doing normally dangerous
+   things.  */
+void CreateBeLenient(pcb_bool);
+
+DataTypePtr CreateNewBuffer(void);
+void pcb_colors_from_settings(PCBTypePtr);
+PCBTypePtr CreateNewPCB_(pcb_bool);
+PCBTypePtr CreateNewPCB();
+/* Called after PCB->Data->LayerN is set.  Returns zero if no errors,
+   else nonzero.  */
+int CreateNewPCBPost(PCBTypePtr, int /* set defaults */ );
+PinTypePtr CreateNewVia(DataTypePtr, Coord, Coord, Coord, Coord, Coord, Coord, const char *, FlagType);
+LineTypePtr CreateDrawnLineOnLayer(LayerTypePtr, Coord, Coord, Coord, Coord, Coord, Coord, FlagType);
+LineTypePtr CreateNewLineOnLayer(LayerTypePtr, Coord, Coord, Coord, Coord, Coord, Coord, FlagType);
+RatTypePtr CreateNewRat(DataTypePtr, Coord, Coord, Coord, Coord, pcb_cardinal_t, pcb_cardinal_t, Coord, FlagType);
+ArcTypePtr CreateNewArcOnLayer(LayerTypePtr, Coord, Coord, Coord, Coord, Angle, Angle, Coord, Coord, FlagType);
+PolygonTypePtr CreateNewPolygonFromRectangle(LayerTypePtr, Coord, Coord, Coord, Coord, FlagType);
+TextTypePtr CreateNewText(LayerTypePtr, FontTypePtr, Coord, Coord, unsigned, int, char *, FlagType);
+PolygonTypePtr CreateNewPolygon(LayerTypePtr, FlagType);
+PointTypePtr CreateNewPointInPolygon(PolygonTypePtr, Coord, Coord);
+PolygonType *CreateNewHoleInPolygon(PolygonType * polygon);
+ElementTypePtr CreateNewElement(DataTypePtr, ElementTypePtr,
+																FontTypePtr, FlagType, char *, char *, char *, Coord, Coord, pcb_uint8_t, int, FlagType, pcb_bool);
+LineTypePtr CreateNewLineInElement(ElementTypePtr, Coord, Coord, Coord, Coord, Coord);
+ArcTypePtr CreateNewArcInElement(ElementTypePtr, Coord, Coord, Coord, Coord, Angle, Angle, Coord);
+PinTypePtr CreateNewPin(ElementTypePtr, Coord, Coord, Coord, Coord, Coord, Coord, char *, char *, FlagType);
+PadTypePtr CreateNewPad(ElementTypePtr, Coord, Coord, Coord, Coord, Coord, Coord, Coord, char *, char *, FlagType);
+LineTypePtr CreateNewLineInSymbol(SymbolTypePtr, Coord, Coord, Coord, Coord, Coord);
+void CreateDefaultFont(PCBTypePtr);
+RubberbandTypePtr CreateNewRubberbandEntry(LayerTypePtr, LineTypePtr, PointTypePtr);
+LibraryMenuTypePtr CreateNewNet(LibraryTypePtr, char *, const char *);
+LibraryEntryTypePtr CreateNewConnection(LibraryMenuTypePtr, char *);
+
+AttributeTypePtr CreateNewAttribute(AttributeListTypePtr list, const char *name, const char *value);
+
+void CreateIDBump(int min_id);
+void CreateIDReset(void);
+long int CreateIDGet(void);
+
+/* Add objects without creating them or making any "sanity modifications" to them */
+void pcb_add_via(DataType *Data, PinType *Via);
+void pcb_add_line_on_layer(LayerType *Layer, LineType *Line);
+void pcb_add_arc_on_layer(LayerType *Layer, ArcType *Arc);
+void pcb_add_polygon_on_layer(LayerType *Layer, PolygonType *polygon);
+void pcb_add_text_on_layer(LayerType *ly, TextType *text, FontType *PCBFont);
+
+#endif
diff --git a/src/crosshair.c b/src/crosshair.c
new file mode 100644
index 0000000..e2c6f85
--- /dev/null
+++ b/src/crosshair.c
@@ -0,0 +1,1241 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  15 Oct 2008 Ineiev: add different crosshair shapes
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* crosshair stuff */
+
+#include "config.h"
+
+#include <memory.h>
+#include <math.h>
+
+#include "conf_core.h"
+
+#include "box.h"
+#include "crosshair.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "line.h"
+#include "rtree.h"
+#include "search.h"
+#include "polygon.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "hid_actions.h"
+#include "layer.h"
+#include "compat_misc.h"
+
+typedef struct {
+	int x, y;
+} point;
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void XORPolygon(PolygonTypePtr, Coord, Coord, int);
+static void XORDrawElement(ElementTypePtr, Coord, Coord);
+static void XORDrawBuffer(BufferTypePtr);
+static void XORDrawInsertPointObject(void);
+static void XORDrawMoveOrCopyObject(void);
+static void XORDrawAttachedLine(Coord, Coord, Coord, Coord, Coord);
+static void XORDrawAttachedArc(Coord);
+
+static void thindraw_moved_pv(PinType * pv, Coord x, Coord y)
+{
+	/* Make a copy of the pin structure, moved to the correct position */
+	PinType moved_pv = *pv;
+	moved_pv.X += x;
+	moved_pv.Y += y;
+
+	gui->thindraw_pcb_pv(Crosshair.GC, Crosshair.GC, &moved_pv, pcb_true, pcb_false);
+}
+
+static void draw_dashed_line(hidGC GC, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+/* TODO: we need a real geo lib... using double here is plain wrong */
+	double dx = x2-x1, dy = y2-y1;
+	double len_squared = dx*dx + dy*dy;
+	int n;
+	const int segs = 11; /* must be odd */
+
+	if (len_squared < 1000000) {
+		/* line too short, just draw it - TODO: magic value; with a proper geo lib this would be gone anyway */
+		gui->draw_line(Crosshair.GC, x1, y1, x2, y2);
+		return;
+	}
+
+	/* first seg is drawn from x1, y1 with no rounding error due to n-1 == 0 */
+	for(n = 1; n < segs; n+=2)
+		gui->draw_line(Crosshair.GC,
+			x1 + (dx * (double)(n-1) / (double)segs), y1 + (dy * (double)(n-1) / (double)segs),
+			x1 + (dx * (double)n / (double)segs), y1 + (dy * (double)n / (double)segs));
+
+
+	/* make sure the last segment is drawn properly to x2 and y2, don't leave
+	   room for rounding errors */
+	gui->draw_line(Crosshair.GC,
+		x2 - (dx / (double)segs), y2 - (dy / (double)segs),
+		x2, y2);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a tmp polygon with coordinates converted to screen system
+ */
+static void XORPolygon(PolygonTypePtr polygon, Coord dx, Coord dy, int dash_last)
+{
+	pcb_cardinal_t i;
+	for (i = 0; i < polygon->PointN; i++) {
+		pcb_cardinal_t next = next_contour_point(polygon, i);
+
+		if (next == 0) { /* last line: sometimes the implicit closing line */
+			if (i == 1) /* corner case: don't draw two lines on top of each other - with XOR it looks bad */
+				continue;
+
+			if (dash_last) {
+				draw_dashed_line(Crosshair.GC,
+									 polygon->Points[i].X + dx,
+									 polygon->Points[i].Y + dy, polygon->Points[next].X + dx, polygon->Points[next].Y + dy);
+				break; /* skip normal line draw below */
+			}
+		}
+
+		/* normal contour line */
+		gui->draw_line(Crosshair.GC,
+								 polygon->Points[i].X + dx,
+								 polygon->Points[i].Y + dy, polygon->Points[next].X + dx, polygon->Points[next].Y + dy);
+	}
+}
+
+/*-----------------------------------------------------------
+ * Draws the outline of an arc
+ */
+static void XORDrawAttachedArc(Coord thick)
+{
+	ArcType arc;
+	BoxTypePtr bx;
+	Coord wx, wy;
+	Angle sa, dir;
+	Coord wid = thick / 2;
+
+	wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
+	wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
+	if (wx == 0 && wy == 0)
+		return;
+	arc.X = Crosshair.AttachedBox.Point1.X;
+	arc.Y = Crosshair.AttachedBox.Point1.Y;
+	if (XOR(Crosshair.AttachedBox.otherway, coord_abs(wy) > coord_abs(wx))) {
+		arc.X = Crosshair.AttachedBox.Point1.X + coord_abs(wy) * SGNZ(wx);
+		sa = (wx >= 0) ? 0 : 180;
+#ifdef ARC45
+		if (coord_abs(wy) >= 2 * coord_abs(wx))
+			dir = (SGNZ(wx) == SGNZ(wy)) ? 45 : -45;
+		else
+#endif
+			dir = (SGNZ(wx) == SGNZ(wy)) ? 90 : -90;
+	}
+	else {
+		arc.Y = Crosshair.AttachedBox.Point1.Y + coord_abs(wx) * SGNZ(wy);
+		sa = (wy >= 0) ? -90 : 90;
+#ifdef ARC45
+		if (coord_abs(wx) >= 2 * coord_abs(wy))
+			dir = (SGNZ(wx) == SGNZ(wy)) ? -45 : 45;
+		else
+#endif
+			dir = (SGNZ(wx) == SGNZ(wy)) ? -90 : 90;
+		wy = wx;
+	}
+	wy = coord_abs(wy);
+	arc.StartAngle = sa;
+	arc.Delta = dir;
+	arc.Width = arc.Height = wy;
+	bx = GetArcEnds(&arc);
+	/*  sa = sa - 180; */
+	gui->draw_arc(Crosshair.GC, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
+	if (wid > pixel_slop) {
+		gui->draw_arc(Crosshair.GC, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
+		gui->draw_arc(Crosshair.GC, bx->X1, bx->Y1, wid, wid, sa, -180 * SGN(dir));
+		gui->draw_arc(Crosshair.GC, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN(dir));
+	}
+}
+
+/*-----------------------------------------------------------
+ * Draws the outline of a line
+ */
+static void XORDrawAttachedLine(Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
+{
+	Coord dx, dy, ox, oy;
+	double h;
+
+	dx = x2 - x1;
+	dy = y2 - y1;
+	if (dx != 0 || dy != 0)
+		h = 0.5 * thick / sqrt(SQUARE(dx) + SQUARE(dy));
+	else
+		h = 0.0;
+	ox = dy * h + 0.5 * SGN(dy);
+	oy = -(dx * h + 0.5 * SGN(dx));
+	gui->draw_line(Crosshair.GC, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
+	if (coord_abs(ox) >= pixel_slop || coord_abs(oy) >= pixel_slop) {
+		Angle angle = atan2(dx, dy) * 57.295779;
+		gui->draw_line(Crosshair.GC, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
+		gui->draw_arc(Crosshair.GC, x1, y1, thick / 2, thick / 2, angle - 180, 180);
+		gui->draw_arc(Crosshair.GC, x2, y2, thick / 2, thick / 2, angle, 180);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the elements of a loaded circuit which is to be merged in
+ */
+static void XORDrawElement(ElementTypePtr Element, Coord DX, Coord DY)
+{
+	/* if no silkscreen, draw the bounding box */
+	if (arclist_length(&Element->Arc) == 0 && linelist_length(&Element->Line) == 0) {
+		gui->draw_line(Crosshair.GC,
+									 DX + Element->BoundingBox.X1,
+									 DY + Element->BoundingBox.Y1, DX + Element->BoundingBox.X1, DY + Element->BoundingBox.Y2);
+		gui->draw_line(Crosshair.GC,
+									 DX + Element->BoundingBox.X1,
+									 DY + Element->BoundingBox.Y2, DX + Element->BoundingBox.X2, DY + Element->BoundingBox.Y2);
+		gui->draw_line(Crosshair.GC,
+									 DX + Element->BoundingBox.X2,
+									 DY + Element->BoundingBox.Y2, DX + Element->BoundingBox.X2, DY + Element->BoundingBox.Y1);
+		gui->draw_line(Crosshair.GC,
+									 DX + Element->BoundingBox.X2,
+									 DY + Element->BoundingBox.Y1, DX + Element->BoundingBox.X1, DY + Element->BoundingBox.Y1);
+	}
+	else {
+		ELEMENTLINE_LOOP(Element);
+		{
+			gui->draw_line(Crosshair.GC, DX + line->Point1.X, DY + line->Point1.Y, DX + line->Point2.X, DY + line->Point2.Y);
+		}
+		END_LOOP;
+
+		/* arc coordinates and angles have to be converted to X11 notation */
+		ARC_LOOP(Element);
+		{
+			gui->draw_arc(Crosshair.GC, DX + arc->X, DY + arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta);
+		}
+		END_LOOP;
+	}
+	/* pin coordinates and angles have to be converted to X11 notation */
+	PIN_LOOP(Element);
+	{
+		thindraw_moved_pv(pin, DX, DY);
+	}
+	END_LOOP;
+
+	/* pads */
+	PAD_LOOP(Element);
+	{
+		if (PCB->InvisibleObjectsOn || (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) != 0) == conf_core.editor.show_solder_side) {
+			/* Make a copy of the pad structure, moved to the correct position */
+			PadType moved_pad = *pad;
+			moved_pad.Point1.X += DX;
+			moved_pad.Point1.Y += DY;
+			moved_pad.Point2.X += DX;
+			moved_pad.Point2.Y += DY;
+
+			gui->thindraw_pcb_pad(Crosshair.GC, &moved_pad, pcb_false, pcb_false);
+		}
+	}
+	END_LOOP;
+	/* mark */
+	gui->draw_line(Crosshair.GC,
+								 Element->MarkX + DX - EMARK_SIZE, Element->MarkY + DY, Element->MarkX + DX, Element->MarkY + DY - EMARK_SIZE);
+	gui->draw_line(Crosshair.GC,
+								 Element->MarkX + DX + EMARK_SIZE, Element->MarkY + DY, Element->MarkX + DX, Element->MarkY + DY - EMARK_SIZE);
+	gui->draw_line(Crosshair.GC,
+								 Element->MarkX + DX - EMARK_SIZE, Element->MarkY + DY, Element->MarkX + DX, Element->MarkY + DY + EMARK_SIZE);
+	gui->draw_line(Crosshair.GC,
+								 Element->MarkX + DX + EMARK_SIZE, Element->MarkY + DY, Element->MarkX + DX, Element->MarkY + DY + EMARK_SIZE);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws all visible and attached objects of the pastebuffer
+ */
+static void XORDrawBuffer(BufferTypePtr Buffer)
+{
+	pcb_cardinal_t i;
+	Coord x, y;
+
+	/* set offset */
+	x = Crosshair.X - Buffer->X;
+	y = Crosshair.Y - Buffer->Y;
+
+	/* draw all visible layers */
+	for (i = 0; i < max_copper_layer + 2; i++)
+		if (PCB->Data->Layer[i].On) {
+			LayerTypePtr layer = &Buffer->Data->Layer[i];
+
+			LINE_LOOP(layer);
+			{
+/*
+				XORDrawAttachedLine(x +line->Point1.X,
+					y +line->Point1.Y, x +line->Point2.X,
+					y +line->Point2.Y, line->Thickness);
+*/
+				gui->draw_line(Crosshair.GC, x + line->Point1.X, y + line->Point1.Y, x + line->Point2.X, y + line->Point2.Y);
+			}
+			END_LOOP;
+			ARC_LOOP(layer);
+			{
+				gui->draw_arc(Crosshair.GC, x + arc->X, y + arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta);
+			}
+			END_LOOP;
+			TEXT_LOOP(layer);
+			{
+				BoxTypePtr box = &text->BoundingBox;
+				gui->draw_rect(Crosshair.GC, x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
+			}
+			END_LOOP;
+			/* the tmp polygon has n+1 points because the first
+			 * and the last one are set to the same coordinates
+			 */
+			POLYGON_LOOP(layer);
+			{
+				XORPolygon(polygon, x, y, 0);
+			}
+			END_LOOP;
+		}
+
+	/* draw elements if visible */
+	if (PCB->PinOn && PCB->ElementOn)
+		ELEMENT_LOOP(Buffer->Data);
+	{
+		if (FRONT(element) || PCB->InvisibleObjectsOn)
+			XORDrawElement(element, x, y);
+	}
+	END_LOOP;
+
+	/* and the vias */
+	if (PCB->ViaOn)
+		VIA_LOOP(Buffer->Data);
+	{
+		thindraw_moved_pv(via, x, y);
+	}
+	END_LOOP;
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the rubberband to insert points into polygons/lines/...
+ */
+static void XORDrawInsertPointObject(void)
+{
+	LineTypePtr line = (LineTypePtr) Crosshair.AttachedObject.Ptr2;
+	PointTypePtr point = (PointTypePtr) Crosshair.AttachedObject.Ptr3;
+
+	if (Crosshair.AttachedObject.Type != PCB_TYPE_NONE) {
+		gui->draw_line(Crosshair.GC, point->X, point->Y, line->Point1.X, line->Point1.Y);
+		gui->draw_line(Crosshair.GC, point->X, point->Y, line->Point2.X, line->Point2.Y);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the attached object while in PCB_MODE_MOVE or PCB_MODE_COPY
+ */
+static void XORDrawMoveOrCopyObject(void)
+{
+	RubberbandTypePtr ptr;
+	pcb_cardinal_t i;
+	Coord dx = Crosshair.X - Crosshair.AttachedObject.X, dy = Crosshair.Y - Crosshair.AttachedObject.Y;
+
+	switch (Crosshair.AttachedObject.Type) {
+	case PCB_TYPE_VIA:
+		{
+			PinTypePtr via = (PinTypePtr) Crosshair.AttachedObject.Ptr1;
+			thindraw_moved_pv(via, dx, dy);
+			break;
+		}
+
+	case PCB_TYPE_LINE:
+		{
+			LineTypePtr line = (LineTypePtr) Crosshair.AttachedObject.Ptr2;
+
+			XORDrawAttachedLine(line->Point1.X + dx, line->Point1.Y + dy, line->Point2.X + dx, line->Point2.Y + dy, line->Thickness);
+			break;
+		}
+
+	case PCB_TYPE_ARC:
+		{
+			ArcTypePtr Arc = (ArcTypePtr) Crosshair.AttachedObject.Ptr2;
+
+			gui->draw_arc(Crosshair.GC, Arc->X + dx, Arc->Y + dy, Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
+			break;
+		}
+
+	case PCB_TYPE_POLYGON:
+		{
+			PolygonTypePtr polygon = (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
+
+			/* the tmp polygon has n+1 points because the first
+			 * and the last one are set to the same coordinates
+			 */
+			XORPolygon(polygon, dx, dy, 0);
+			break;
+		}
+
+	case PCB_TYPE_LINE_POINT:
+		{
+			LineTypePtr line;
+			PointTypePtr point;
+
+			line = (LineTypePtr) Crosshair.AttachedObject.Ptr2;
+			point = (PointTypePtr) Crosshair.AttachedObject.Ptr3;
+			if (point == &line->Point1)
+				XORDrawAttachedLine(point->X + dx, point->Y + dy, line->Point2.X, line->Point2.Y, line->Thickness);
+			else
+				XORDrawAttachedLine(point->X + dx, point->Y + dy, line->Point1.X, line->Point1.Y, line->Thickness);
+			break;
+		}
+
+	case PCB_TYPE_POLYGON_POINT:
+		{
+			PolygonTypePtr polygon;
+			PointTypePtr point;
+			pcb_cardinal_t point_idx, prev, next;
+
+			polygon = (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
+			point = (PointTypePtr) Crosshair.AttachedObject.Ptr3;
+			point_idx = polygon_point_idx(polygon, point);
+
+			/* get previous and following point */
+			prev = prev_contour_point(polygon, point_idx);
+			next = next_contour_point(polygon, point_idx);
+
+			/* draw the two segments */
+			gui->draw_line(Crosshair.GC, polygon->Points[prev].X, polygon->Points[prev].Y, point->X + dx, point->Y + dy);
+			gui->draw_line(Crosshair.GC, point->X + dx, point->Y + dy, polygon->Points[next].X, polygon->Points[next].Y);
+			break;
+		}
+
+	case PCB_TYPE_ELEMENT_NAME:
+		{
+			/* locate the element "mark" and draw an association line from crosshair to it */
+			ElementTypePtr element = (ElementTypePtr) Crosshair.AttachedObject.Ptr1;
+
+			gui->draw_line(Crosshair.GC, element->MarkX, element->MarkY, Crosshair.X, Crosshair.Y);
+			/* fall through to move the text as a box outline */
+		}
+	case PCB_TYPE_TEXT:
+		{
+			TextTypePtr text = (TextTypePtr) Crosshair.AttachedObject.Ptr2;
+			BoxTypePtr box = &text->BoundingBox;
+			gui->draw_rect(Crosshair.GC, box->X1 + dx, box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
+			break;
+		}
+
+		/* pin/pad movements result in moving an element */
+	case PCB_TYPE_PAD:
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_ELEMENT:
+		XORDrawElement((ElementTypePtr) Crosshair.AttachedObject.Ptr2, dx, dy);
+		break;
+	}
+
+	/* draw the attached rubberband lines too */
+	i = Crosshair.AttachedObject.RubberbandN;
+	ptr = Crosshair.AttachedObject.Rubberband;
+	while (i) {
+		PointTypePtr point1, point2;
+
+		if (TEST_FLAG(PCB_FLAG_VIA, ptr->Line)) {
+			/* this is a rat going to a polygon.  do not draw for rubberband */ ;
+		}
+		else if (TEST_FLAG(PCB_FLAG_RUBBEREND, ptr->Line)) {
+			/* 'point1' is always the fix-point */
+			if (ptr->MovedPoint == &ptr->Line->Point1) {
+				point1 = &ptr->Line->Point2;
+				point2 = &ptr->Line->Point1;
+			}
+			else {
+				point1 = &ptr->Line->Point1;
+				point2 = &ptr->Line->Point2;
+			}
+			XORDrawAttachedLine(point1->X, point1->Y, point2->X + dx, point2->Y + dy, ptr->Line->Thickness);
+		}
+		else if (ptr->MovedPoint == &ptr->Line->Point1)
+			XORDrawAttachedLine(ptr->Line->Point1.X + dx,
+													ptr->Line->Point1.Y + dy, ptr->Line->Point2.X + dx, ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
+
+		ptr++;
+		i--;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draws additional stuff that follows the crosshair
+ */
+void DrawAttached(void)
+{
+	switch (conf_core.editor.mode) {
+	case PCB_MODE_VIA:
+		{
+			/* Make a dummy via structure to draw from */
+			PinType via;
+			via.X = Crosshair.X;
+			via.Y = Crosshair.Y;
+			via.Thickness = conf_core.design.via_thickness;
+			via.Clearance = 2 * conf_core.design.clearance;
+			via.DrillingHole = conf_core.design.via_drilling_hole;
+			via.Mask = 0;
+			via.Flags = NoFlags();
+
+			gui->thindraw_pcb_pv(Crosshair.GC, Crosshair.GC, &via, pcb_true, pcb_false);
+
+			if (conf_core.editor.show_drc) {
+				/* XXX: Naughty cheat - use the mask to draw DRC clearance! */
+				via.Mask = conf_core.design.via_thickness + PCB->Bloat * 2;
+				gui->set_color(Crosshair.GC, conf_core.appearance.color.cross);
+				gui->thindraw_pcb_pv(Crosshair.GC, Crosshair.GC, &via, pcb_false, pcb_true);
+				gui->set_color(Crosshair.GC, conf_core.appearance.color.crosshair);
+			}
+			break;
+		}
+
+		/* the attached line is used by both LINEMODE, PCB_MODE_POLYGON and PCB_MODE_POLYGON_HOLE */
+	case PCB_MODE_POLYGON:
+	case PCB_MODE_POLYGON_HOLE:
+		/* draw only if starting point is set */
+		if (Crosshair.AttachedLine.State != STATE_FIRST)
+			gui->draw_line(Crosshair.GC,
+										 Crosshair.AttachedLine.Point1.X,
+										 Crosshair.AttachedLine.Point1.Y, Crosshair.AttachedLine.Point2.X, Crosshair.AttachedLine.Point2.Y);
+
+		/* draw attached polygon only if in PCB_MODE_POLYGON or PCB_MODE_POLYGON_HOLE */
+		if (Crosshair.AttachedPolygon.PointN > 1) {
+			XORPolygon(&Crosshair.AttachedPolygon, 0, 0, 1);
+		}
+		break;
+
+	case PCB_MODE_ARC:
+		if (Crosshair.AttachedBox.State != STATE_FIRST) {
+			XORDrawAttachedArc(conf_core.design.line_thickness);
+			if (conf_core.editor.show_drc) {
+				gui->set_color(Crosshair.GC, conf_core.appearance.color.cross);
+				XORDrawAttachedArc(conf_core.design.line_thickness + 2 * (PCB->Bloat + 1));
+				gui->set_color(Crosshair.GC, conf_core.appearance.color.crosshair);
+			}
+
+		}
+		break;
+
+	case PCB_MODE_LINE:
+		/* draw only if starting point exists and the line has length */
+		if (Crosshair.AttachedLine.State != STATE_FIRST && Crosshair.AttachedLine.draw) {
+			XORDrawAttachedLine(Crosshair.AttachedLine.Point1.X,
+													Crosshair.AttachedLine.Point1.Y,
+													Crosshair.AttachedLine.Point2.X,
+													Crosshair.AttachedLine.Point2.Y, PCB->RatDraw ? 10 : conf_core.design.line_thickness);
+			/* draw two lines ? */
+			if (conf_core.editor.line_refraction)
+				XORDrawAttachedLine(Crosshair.AttachedLine.Point2.X,
+														Crosshair.AttachedLine.Point2.Y,
+														Crosshair.X, Crosshair.Y, PCB->RatDraw ? 10 : conf_core.design.line_thickness);
+			if (conf_core.editor.show_drc) {
+				gui->set_color(Crosshair.GC, conf_core.appearance.color.cross);
+				XORDrawAttachedLine(Crosshair.AttachedLine.Point1.X,
+														Crosshair.AttachedLine.Point1.Y,
+														Crosshair.AttachedLine.Point2.X,
+														Crosshair.AttachedLine.Point2.Y, PCB->RatDraw ? 10 : conf_core.design.line_thickness + 2 * (PCB->Bloat + 1));
+				if (conf_core.editor.line_refraction)
+					XORDrawAttachedLine(Crosshair.AttachedLine.Point2.X,
+															Crosshair.AttachedLine.Point2.Y,
+															Crosshair.X, Crosshair.Y, PCB->RatDraw ? 10 : conf_core.design.line_thickness + 2 * (PCB->Bloat + 1));
+				gui->set_color(Crosshair.GC, conf_core.appearance.color.crosshair);
+			}
+		}
+		break;
+
+	case PCB_MODE_PASTE_BUFFER:
+		XORDrawBuffer(PASTEBUFFER);
+		break;
+
+	case PCB_MODE_COPY:
+	case PCB_MODE_MOVE:
+		XORDrawMoveOrCopyObject();
+		break;
+
+	case PCB_MODE_INSERT_POINT:
+		XORDrawInsertPointObject();
+		break;
+	}
+
+	/* an attached box does not depend on a special mode */
+	if (Crosshair.AttachedBox.State == STATE_SECOND || Crosshair.AttachedBox.State == STATE_THIRD) {
+		Coord x1, y1, x2, y2;
+
+		x1 = Crosshair.AttachedBox.Point1.X;
+		y1 = Crosshair.AttachedBox.Point1.Y;
+		x2 = Crosshair.AttachedBox.Point2.X;
+		y2 = Crosshair.AttachedBox.Point2.Y;
+		gui->draw_rect(Crosshair.GC, x1, y1, x2, y2);
+	}
+}
+
+
+/* --------------------------------------------------------------------------
+ * draw the marker position
+ */
+void DrawMark(void)
+{
+	Coord ms = conf_core.appearance.mark_size;
+
+	/* Mark is not drawn when it is not set */
+	if (!Marked.status)
+		return;
+
+	gui->draw_line(Crosshair.GC, Marked.X - ms, Marked.Y - ms, Marked.X + ms, Marked.Y + ms);
+	gui->draw_line(Crosshair.GC, Marked.X + ms, Marked.Y - ms, Marked.X - ms, Marked.Y + ms);
+}
+
+/* ---------------------------------------------------------------------------
+ * Returns the nearest grid-point to the given Coord
+ */
+Coord GridFit(Coord x, Coord grid_spacing, Coord grid_offset)
+{
+	x -= grid_offset;
+	x = grid_spacing * pcb_round((double) x / grid_spacing);
+	x += grid_offset;
+	return x;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * notify the GUI that data relating to the crosshair is being changed.
+ *
+ * The argument passed is pcb_false to notify "changes are about to happen",
+ * and pcb_true to notify "changes have finished".
+ *
+ * Each call with a 'pcb_false' parameter must be matched with a following one
+ * with a 'pcb_true' parameter. Unmatched 'pcb_true' calls are currently not permitted,
+ * but might be allowed in the future.
+ *
+ * GUIs should not complain if they receive extra calls with 'pcb_true' as parameter.
+ * They should initiate a redraw of the crosshair attached objects - which may
+ * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
+ * location of existing attached drawing.
+ */
+void notify_crosshair_change(pcb_bool changes_complete)
+{
+	if (gui->notify_crosshair_change)
+		gui->notify_crosshair_change(changes_complete);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * notify the GUI that data relating to the mark is being changed.
+ *
+ * The argument passed is pcb_false to notify "changes are about to happen",
+ * and pcb_true to notify "changes have finished".
+ *
+ * Each call with a 'pcb_false' parameter must be matched with a following one
+ * with a 'pcb_true' parameter. Unmatched 'pcb_true' calls are currently not permitted,
+ * but might be allowed in the future.
+ *
+ * GUIs should not complain if they receive extra calls with 'pcb_true' as parameter.
+ * They should initiate a redraw of the mark - which may (if necessary) mean
+ * repainting the whole screen if the GUI hasn't tracked the mark's location.
+ */
+void notify_mark_change(pcb_bool changes_complete)
+{
+	if (gui->notify_mark_change)
+		gui->notify_mark_change(changes_complete);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * Convenience for plugins using the old {Hide,Restore}Crosshair API.
+ * This links up to notify the GUI of the expected changes using the new APIs.
+ *
+ * Use of this old API is deprecated, as the names don't necessarily reflect
+ * what all GUIs may do in response to the notifications. Keeping these APIs
+ * is aimed at easing transition to the newer API, they will emit a harmless
+ * warning at the time of their first use.
+ *
+ */
+void HideCrosshair(void)
+{
+	static pcb_bool warned_old_api = pcb_false;
+	if (!warned_old_api) {
+		Message(PCB_MSG_DEFAULT, _("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
+							"         This API may be removed in a future release of PCB.\n"));
+		warned_old_api = pcb_true;
+	}
+
+	notify_crosshair_change(pcb_false);
+	notify_mark_change(pcb_false);
+}
+
+void RestoreCrosshair(void)
+{
+	static pcb_bool warned_old_api = pcb_false;
+	if (!warned_old_api) {
+		Message(PCB_MSG_DEFAULT, _("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
+							"         This API may be removed in a future release of PCB.\n"));
+		warned_old_api = pcb_true;
+	}
+
+	notify_crosshair_change(pcb_true);
+	notify_mark_change(pcb_true);
+}
+
+/*
+ * Below is the implementation of the "highlight on endpoint" functionality.
+ * This highlights lines and arcs when the crosshair is on of their (two)
+ * endpoints.
+ */
+struct onpoint_search_info {
+	CrosshairType *crosshair;
+	Coord X;
+	Coord Y;
+};
+
+static r_dir_t onpoint_line_callback(const BoxType * box, void *cl)
+{
+	struct onpoint_search_info *info = (struct onpoint_search_info *) cl;
+	CrosshairType *crosshair = info->crosshair;
+	LineType *line = (LineType *) box;
+
+#ifdef DEBUG_ONPOINT
+	printf("X=%ld Y=%ld    X1=%ld Y1=%ld X2=%ld Y2=%ld\n", info->X, info->Y,
+				 line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
+#endif
+	if ((line->Point1.X == info->X && line->Point1.Y == info->Y) || (line->Point2.X == info->X && line->Point2.Y == info->Y)) {
+		OnpointType op;
+		op.type = PCB_TYPE_LINE;
+		op.obj.line = line;
+		vtop_append(&crosshair->onpoint_objs, op);
+		SET_FLAG(PCB_FLAG_ONPOINT, (AnyObjectType *) line);
+		DrawLine(NULL, line);
+		return R_DIR_FOUND_CONTINUE;
+	}
+	else {
+		return R_DIR_NOT_FOUND;
+	}
+}
+
+#define close_enough(v1, v2) (coord_abs((v1)-(v2)) < 10)
+
+static r_dir_t onpoint_arc_callback(const BoxType * box, void *cl)
+{
+	struct onpoint_search_info *info = (struct onpoint_search_info *) cl;
+	CrosshairType *crosshair = info->crosshair;
+	ArcType *arc = (ArcType *) box;
+	Coord p1x, p1y, p2x, p2y;
+
+	p1x = arc->X - arc->Width * cos(TO_RADIANS(arc->StartAngle));
+	p1y = arc->Y + arc->Height * sin(TO_RADIANS(arc->StartAngle));
+	p2x = arc->X - arc->Width * cos(TO_RADIANS(arc->StartAngle + arc->Delta));
+	p2y = arc->Y + arc->Height * sin(TO_RADIANS(arc->StartAngle + arc->Delta));
+
+	/* printf("p1=%ld;%ld p2=%ld;%ld info=%ld;%ld\n", p1x, p1y, p2x, p2y, info->X, info->Y); */
+
+	if ((close_enough(p1x, info->X) && close_enough(p1y, info->Y)) || (close_enough(p2x, info->X) && close_enough(p2y, info->Y))) {
+		OnpointType op;
+		op.type = PCB_TYPE_ARC;
+		op.obj.arc = arc;
+		vtop_append(&crosshair->onpoint_objs, op);
+		SET_FLAG(PCB_FLAG_ONPOINT, (AnyObjectType *) arc);
+		DrawArc(NULL, arc);
+		return R_DIR_FOUND_CONTINUE;
+	}
+	else {
+		return R_DIR_NOT_FOUND;
+	}
+}
+
+void DrawLineOrArc(int type, void *obj)
+{
+	switch (type) {
+	case PCB_TYPE_LINE_POINT:
+		/* Attention: We can use a NULL pointer here for the layer,
+		 * because it is not used in the DrawLine() function anyways.
+		 * ATM DrawLine() only calls AddPart() internally, which invalidates
+		 * the area specified by the line's bounding box.
+		 */
+		DrawLine(NULL, (LineType *) obj);
+		break;
+#if 0
+	case ARCPOINT_TYPE:
+		/* See comment above */
+		DrawArc(NULL, (ArcType *) obj);
+		break;
+#endif
+	}
+}
+
+
+#define op_swap(crosshair) \
+do { \
+	vtop_t __tmp__ = crosshair->onpoint_objs; \
+	crosshair->onpoint_objs = crosshair->old_onpoint_objs; \
+	crosshair->old_onpoint_objs = __tmp__; \
+} while(0)
+
+static void *onpoint_find(vtop_t *vect, void *obj_ptr)
+{
+	int i;
+
+	for (i = 0; i < vect->used; i++) {
+		OnpointType *op = &(vect->array[i]);
+		if (op->obj.any == obj_ptr)
+			return op;
+	}
+	return NULL;
+}
+
+/*
+ * Searches for lines or arcs which have points that are exactly
+ * at the given coordinates and adds them to the crosshair's
+ * object list along with their respective type.
+ */
+static void onpoint_work(CrosshairType * crosshair, Coord X, Coord Y)
+{
+	BoxType SearchBox = point_box(X, Y);
+	struct onpoint_search_info info;
+	int i;
+	pcb_bool redraw = pcb_false;
+
+	op_swap(crosshair);
+
+	/* Do not truncate to 0 because that may free the array */
+	vtop_truncate(&crosshair->onpoint_objs, 1);
+	crosshair->onpoint_objs.used = 0;
+
+
+	info.crosshair = crosshair;
+	info.X = X;
+	info.Y = Y;
+
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *layer = &PCB->Data->Layer[i];
+		/* Only find points of arcs and lines on currently visible layers. */
+		if (!layer->On)
+			continue;
+		r_search(layer->line_tree, &SearchBox, NULL, onpoint_line_callback, &info, NULL);
+		r_search(layer->arc_tree, &SearchBox, NULL, onpoint_arc_callback, &info, NULL);
+	}
+
+	/* Undraw the old objects */
+	for (i = 0; i < crosshair->old_onpoint_objs.used; i++) {
+		OnpointType *op = &crosshair->old_onpoint_objs.array[i];
+
+		/* only remove and redraw those which aren't in the new list */
+		if (onpoint_find(&crosshair->onpoint_objs, op->obj.any) != NULL)
+			continue;
+
+		CLEAR_FLAG(PCB_FLAG_ONPOINT, (AnyObjectType *) op->obj.any);
+		DrawLineOrArc(op->type, op->obj.any);
+		redraw = pcb_true;
+	}
+
+	/* draw the new objects */
+	for (i = 0; i < crosshair->onpoint_objs.used; i++) {
+		OnpointType *op = &crosshair->onpoint_objs.array[i];
+
+		/* only draw those which aren't in the old list */
+		if (onpoint_find(&crosshair->old_onpoint_objs, op->obj.any) != NULL)
+			continue;
+		DrawLineOrArc(op->type, op->obj.any);
+		redraw = pcb_true;
+	}
+
+	if (redraw) {
+		Redraw();
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * Returns the square of the given number
+ */
+static double square(double x)
+{
+	return x * x;
+}
+
+static double crosshair_sq_dist(CrosshairType * crosshair, Coord x, Coord y)
+{
+	return square(x - crosshair->X) + square(y - crosshair->Y);
+}
+
+struct snap_data {
+	CrosshairType *crosshair;
+	double nearest_sq_dist;
+	pcb_bool nearest_is_grid;
+	Coord x, y;
+};
+
+/* Snap to a given location if it is the closest thing we found so far.
+ * If "prefer_to_grid" is set, the passed location will take preference
+ * over a closer grid points we already snapped to UNLESS the user is
+ * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
+ * (including grid points), is always preferred.
+ */
+static void check_snap_object(struct snap_data *snap_data, Coord x, Coord y, pcb_bool prefer_to_grid)
+{
+	double sq_dist;
+
+	sq_dist = crosshair_sq_dist(snap_data->crosshair, x, y);
+	if (sq_dist < snap_data->nearest_sq_dist || (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed())) {
+		snap_data->x = x;
+		snap_data->y = y;
+		snap_data->nearest_sq_dist = sq_dist;
+		snap_data->nearest_is_grid = pcb_false;
+	}
+}
+
+static void check_snap_offgrid_line(struct snap_data *snap_data, Coord nearest_grid_x, Coord nearest_grid_y)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int ans;
+	LineType *line;
+	Coord try_x, try_y;
+	double dx, dy;
+	double dist;
+
+	if (!conf_core.editor.snap_pin)
+		return;
+
+	/* Code to snap at some sensible point along a line */
+	/* Pick the nearest grid-point in the x or y direction
+	 * to align with, then adjust until we hit the line
+	 */
+	ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_LINE, &ptr1, &ptr2, &ptr3);
+
+	if (ans == PCB_TYPE_NONE)
+		return;
+
+	line = (LineType *) ptr2;
+
+	/* Allow snapping to off-grid lines when drawing new lines (on
+	 * the same layer), and when moving a line end-point
+	 * (but don't snap to the same line)
+	 */
+	if ((conf_core.editor.mode != PCB_MODE_LINE || CURRENT != ptr1) &&
+			(conf_core.editor.mode != PCB_MODE_MOVE ||
+			 Crosshair.AttachedObject.Ptr1 != ptr1 ||
+			 Crosshair.AttachedObject.Type != PCB_TYPE_LINE_POINT || Crosshair.AttachedObject.Ptr2 == line))
+		return;
+
+	dx = line->Point2.X - line->Point1.X;
+	dy = line->Point2.Y - line->Point1.Y;
+
+	/* Try snapping along the X axis */
+	if (dy != 0.) {
+		/* Move in the X direction until we hit the line */
+		try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
+		try_y = nearest_grid_y;
+		check_snap_object(snap_data, try_x, try_y, pcb_true);
+	}
+
+	/* Try snapping along the Y axis */
+	if (dx != 0.) {
+		try_x = nearest_grid_x;
+		try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
+		check_snap_object(snap_data, try_x, try_y, pcb_true);
+	}
+
+	if (dx != dy) {								/* If line not parallel with dX = dY direction.. */
+		/* Try snapping diagonally towards the line in the dX = dY direction */
+
+		if (dy == 0)
+			dist = line->Point1.Y - nearest_grid_y;
+		else
+			dist = ((line->Point1.X - nearest_grid_x) - (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
+
+		try_x = nearest_grid_x + dist;
+		try_y = nearest_grid_y + dist;
+
+		check_snap_object(snap_data, try_x, try_y, pcb_true);
+	}
+
+	if (dx != -dy) {							/* If line not parallel with dX = -dY direction.. */
+		/* Try snapping diagonally towards the line in the dX = -dY direction */
+
+		if (dy == 0)
+			dist = nearest_grid_y - line->Point1.Y;
+		else
+			dist = ((line->Point1.X - nearest_grid_x) - (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
+
+		try_x = nearest_grid_x + dist;
+		try_y = nearest_grid_y - dist;
+
+		check_snap_object(snap_data, try_x, try_y, pcb_true);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * recalculates the passed coordinates to fit the current grid setting
+ */
+void FitCrosshairIntoGrid(Coord X, Coord Y)
+{
+	Coord nearest_grid_x, nearest_grid_y;
+	void *ptr1, *ptr2, *ptr3;
+	struct snap_data snap_data;
+	int ans;
+
+	Crosshair.X = PCB_CLAMP(X, Crosshair.MinX, Crosshair.MaxX);
+	Crosshair.Y = PCB_CLAMP(Y, Crosshair.MinY, Crosshair.MaxY);
+
+	if (PCB->RatDraw) {
+		nearest_grid_x = -PCB_MIL_TO_COORD(6);
+		nearest_grid_y = -PCB_MIL_TO_COORD(6);
+	}
+	else {
+		nearest_grid_x = GridFit(Crosshair.X, PCB->Grid, PCB->GridOffsetX);
+		nearest_grid_y = GridFit(Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
+
+		if (Marked.status && conf_core.editor.orthogonal_moves) {
+			Coord dx = Crosshair.X - Marked.X;
+			Coord dy = Crosshair.Y - Marked.Y;
+			if (PCB_ABS(dx) > PCB_ABS(dy))
+				nearest_grid_y = Marked.Y;
+			else
+				nearest_grid_x = Marked.X;
+		}
+
+	}
+
+	snap_data.crosshair = &Crosshair;
+	snap_data.nearest_sq_dist = crosshair_sq_dist(&Crosshair, nearest_grid_x, nearest_grid_y);
+	snap_data.nearest_is_grid = pcb_true;
+	snap_data.x = nearest_grid_x;
+	snap_data.y = nearest_grid_y;
+
+	ans = PCB_TYPE_NONE;
+	if (!PCB->RatDraw)
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_ELEMENT, &ptr1, &ptr2, &ptr3);
+
+	if (ans & PCB_TYPE_ELEMENT) {
+		ElementType *el = (ElementType *) ptr1;
+		check_snap_object(&snap_data, el->MarkX, el->MarkY, pcb_false);
+	}
+
+	ans = PCB_TYPE_NONE;
+	if (PCB->RatDraw || conf_core.editor.snap_pin)
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_PAD, &ptr1, &ptr2, &ptr3);
+
+	/* Avoid self-snapping when moving */
+	if (ans != PCB_TYPE_NONE &&
+			conf_core.editor.mode == PCB_MODE_MOVE && Crosshair.AttachedObject.Type == PCB_TYPE_ELEMENT && ptr1 == Crosshair.AttachedObject.Ptr1)
+		ans = PCB_TYPE_NONE;
+
+	if (ans != PCB_TYPE_NONE &&
+			(conf_core.editor.mode == PCB_MODE_LINE || (conf_core.editor.mode == PCB_MODE_MOVE && Crosshair.AttachedObject.Type == PCB_TYPE_LINE_POINT))) {
+		PadTypePtr pad = (PadTypePtr) ptr2;
+		LayerType *desired_layer;
+		pcb_cardinal_t desired_group;
+		pcb_cardinal_t SLayer, CLayer;
+		int found_our_layer = pcb_false;
+
+		desired_layer = CURRENT;
+		if (conf_core.editor.mode == PCB_MODE_MOVE && Crosshair.AttachedObject.Type == PCB_TYPE_LINE_POINT) {
+			desired_layer = (LayerType *) Crosshair.AttachedObject.Ptr1;
+		}
+
+		/* find layer groups of the component side and solder side */
+		SLayer = GetLayerGroupNumberByNumber(solder_silk_layer);
+		CLayer = GetLayerGroupNumberByNumber(component_silk_layer);
+		desired_group = TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SLayer : CLayer;
+
+		GROUP_LOOP(PCB->Data, desired_group);
+		{
+			if (layer == desired_layer) {
+				found_our_layer = pcb_true;
+				break;
+			}
+		}
+		END_LOOP;
+
+		if (found_our_layer == pcb_false)
+			ans = PCB_TYPE_NONE;
+	}
+
+	if (ans != PCB_TYPE_NONE) {
+		PadType *pad = (PadType *) ptr2;
+		check_snap_object(&snap_data, (pad->Point1.X + pad->Point2.X) / 2, (pad->Point1.Y + pad->Point2.Y) / 2, pcb_true);
+	}
+
+	ans = PCB_TYPE_NONE;
+	if (PCB->RatDraw || conf_core.editor.snap_pin)
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_PIN, &ptr1, &ptr2, &ptr3);
+
+	/* Avoid self-snapping when moving */
+	if (ans != PCB_TYPE_NONE &&
+			conf_core.editor.mode == PCB_MODE_MOVE && Crosshair.AttachedObject.Type == PCB_TYPE_ELEMENT && ptr1 == Crosshair.AttachedObject.Ptr1)
+		ans = PCB_TYPE_NONE;
+
+	if (ans != PCB_TYPE_NONE) {
+		PinType *pin = (PinType *) ptr2;
+		check_snap_object(&snap_data, pin->X, pin->Y, pcb_true);
+	}
+
+	ans = PCB_TYPE_NONE;
+	if (conf_core.editor.snap_pin)
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_VIA, &ptr1, &ptr2, &ptr3);
+
+	/* Avoid snapping vias to any other vias */
+	if (conf_core.editor.mode == PCB_MODE_MOVE && Crosshair.AttachedObject.Type == PCB_TYPE_VIA && (ans & PCB_TYPEMASK_PIN))
+		ans = PCB_TYPE_NONE;
+
+	if (ans != PCB_TYPE_NONE) {
+		PinType *pin = (PinType *) ptr2;
+		check_snap_object(&snap_data, pin->X, pin->Y, pcb_true);
+	}
+
+	ans = PCB_TYPE_NONE;
+	if (conf_core.editor.snap_pin)
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_LINE_POINT, &ptr1, &ptr2, &ptr3);
+
+	if (ans != PCB_TYPE_NONE) {
+		PointType *pnt = (PointType *) ptr3;
+		check_snap_object(&snap_data, pnt->X, pnt->Y, pcb_true);
+	}
+
+	/*
+	 * Snap to offgrid points on lines.
+	 */
+	if (conf_core.editor.snap_offgrid_line)
+		check_snap_offgrid_line(&snap_data, nearest_grid_x, nearest_grid_y);
+
+	ans = PCB_TYPE_NONE;
+	if (conf_core.editor.snap_pin)
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_POLYGON_POINT, &ptr1, &ptr2, &ptr3);
+
+	if (ans != PCB_TYPE_NONE) {
+		PointType *pnt = (PointType *) ptr3;
+		check_snap_object(&snap_data, pnt->X, pnt->Y, pcb_true);
+	}
+
+	if (snap_data.x >= 0 && snap_data.y >= 0) {
+		Crosshair.X = snap_data.x;
+		Crosshair.Y = snap_data.y;
+	}
+
+	if (conf_core.editor.highlight_on_point)
+		onpoint_work(&Crosshair, Crosshair.X, Crosshair.Y);
+
+	if (conf_core.editor.mode == PCB_MODE_ARROW) {
+		ans = SearchScreenGridSlop(Crosshair.X, Crosshair.Y, PCB_TYPE_LINE_POINT, &ptr1, &ptr2, &ptr3);
+		if (ans == PCB_TYPE_NONE)
+			hid_action("PointCursor");
+		else if (!TEST_FLAG(PCB_FLAG_SELECTED, (LineType *) ptr2))
+			hid_actionl("PointCursor", "True", NULL);
+	}
+
+	if (conf_core.editor.mode == PCB_MODE_LINE && Crosshair.AttachedLine.State != STATE_FIRST && conf_core.editor.auto_drc)
+		EnforceLineDRC();
+
+	gui->set_crosshair(Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
+}
+
+/* ---------------------------------------------------------------------------
+ * move crosshair relative (has to be switched off)
+ */
+void MoveCrosshairRelative(Coord DeltaX, Coord DeltaY)
+{
+	FitCrosshairIntoGrid(Crosshair.X + DeltaX, Crosshair.Y + DeltaY);
+}
+
+/* ---------------------------------------------------------------------------
+ * move crosshair absolute
+ * return pcb_true if the crosshair was moved from its existing position
+ */
+pcb_bool MoveCrosshairAbsolute(Coord X, Coord Y)
+{
+	Coord x, y, z;
+	x = Crosshair.X;
+	y = Crosshair.Y;
+	FitCrosshairIntoGrid(X, Y);
+	if (Crosshair.X != x || Crosshair.Y != y) {
+		/* back up to old position to notify the GUI
+		 * (which might want to erase the old crosshair) */
+		z = Crosshair.X;
+		Crosshair.X = x;
+		x = z;
+		z = Crosshair.Y;
+		Crosshair.Y = y;
+		notify_crosshair_change(pcb_false);	/* Our caller notifies when it has done */
+		/* now move forward again */
+		Crosshair.X = x;
+		Crosshair.Y = z;
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the valid range for the crosshair cursor
+ */
+void SetCrosshairRange(Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
+{
+	Crosshair.MinX = MAX(0, MinX);
+	Crosshair.MinY = MAX(0, MinY);
+	Crosshair.MaxX = MIN(PCB->MaxWidth, MaxX);
+	Crosshair.MaxY = MIN(PCB->MaxHeight, MaxY);
+
+	/* force update of position */
+	MoveCrosshairRelative(0, 0);
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes crosshair stuff
+ * clears the struct, allocates to graphical contexts
+ */
+void InitCrosshair(void)
+{
+	Crosshair.GC = gui->make_gc();
+
+	gui->set_color(Crosshair.GC, conf_core.appearance.color.crosshair);
+	gui->set_draw_xor(Crosshair.GC, 1);
+	gui->set_line_cap(Crosshair.GC, Trace_Cap);
+	gui->set_line_width(Crosshair.GC, 1);
+
+	/* set initial shape */
+	Crosshair.shape = Basic_Crosshair_Shape;
+
+	/* set default limits */
+	Crosshair.MinX = Crosshair.MinY = 0;
+	Crosshair.MaxX = PCB->MaxWidth;
+	Crosshair.MaxY = PCB->MaxHeight;
+
+	/* Initialize the onpoint data. */
+	memset(&Crosshair.onpoint_objs, 0, sizeof(vtop_t));
+	memset(&Crosshair.old_onpoint_objs, 0, sizeof(vtop_t));
+
+	/* clear the mark */
+	Marked.status = pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * exits crosshair routines, release GCs
+ */
+void DestroyCrosshair(void)
+{
+	FreePolygonMemory(&Crosshair.AttachedPolygon);
+	gui->destroy_gc(Crosshair.GC);
+}
diff --git a/src/crosshair.h b/src/crosshair.h
new file mode 100644
index 0000000..d06c16e
--- /dev/null
+++ b/src/crosshair.h
@@ -0,0 +1,55 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for crosshair routines */
+
+#ifndef	PCB_CROSSHAIR_H
+#define	PCB_CROSSHAIR_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * all possible states of an attached object
+ */
+#define	STATE_FIRST		0					/* initial state */
+#define	STATE_SECOND	1
+#define	STATE_THIRD		2
+
+Coord GridFit(Coord x, Coord grid_spacing, Coord grid_offset);
+void notify_crosshair_change(pcb_bool changes_complete);
+void notify_mark_change(pcb_bool changes_complete);
+void HideCrosshair(void);
+void RestoreCrosshair(void);
+void DrawAttached(void);
+void DrawMark(void);
+void MoveCrosshairRelative(Coord, Coord);
+pcb_bool MoveCrosshairAbsolute(Coord, Coord);
+void SetCrosshairRange(Coord, Coord, Coord, Coord);
+void InitCrosshair(void);
+void DestroyCrosshair(void);
+void FitCrosshairIntoGrid(Coord, Coord);
+
+#endif
diff --git a/src/data.c b/src/data.c
new file mode 100644
index 0000000..d7ae35c
--- /dev/null
+++ b/src/data.c
@@ -0,0 +1,174 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* just defines common identifiers
+ */
+#include "config.h"
+
+#include "data.h"
+
+/* ---------------------------------------------------------------------------
+ * some shared identifiers
+ */
+
+CrosshairType Crosshair;				/* information about cursor settings */
+MarkType Marked;								/* a cross-hair mark */
+OutputType Output;							/* some widgets ... used for drawing */
+PCBTypePtr PCB;									/* pointer to layout struct */
+
+int LayerStack[MAX_LAYER];			/* determines the layer draw order */
+
+BufferType Buffers[MAX_BUFFER]; /* my buffers */
+pcb_bool Bumped;                /* if the undo serial number has changed */
+
+int addedLines;
+
+
+/* callback based loops */
+void pcb_loop_layers(void *ctx, pcb_layer_cb_t lacb, pcb_line_cb_t lcb, pcb_arc_cb_t acb, pcb_text_cb_t tcb, pcb_poly_cb_t pocb)
+{
+	if ((lacb != NULL) || (lcb != NULL) || (acb != NULL) || (tcb != NULL) || (pocb != NULL)) {
+		LAYER_LOOP(PCB->Data, max_copper_layer + 2);
+		{
+			if (lacb != NULL)
+				if (lacb(ctx, PCB, layer, 1))
+					continue;
+			if (lcb != NULL) {
+				LINE_LOOP(layer);
+				{
+					lcb(ctx, PCB, layer, line);
+				}
+				END_LOOP;
+			}
+
+			if (acb != NULL) {
+				ARC_LOOP(layer);
+				{
+					acb(ctx, PCB, layer, arc);
+				}
+				END_LOOP;
+			}
+
+			if (tcb != NULL) {
+				TEXT_LOOP(layer);
+				{
+					tcb(ctx, PCB, layer, text);
+				}
+				END_LOOP;
+			}
+
+			if (pocb != NULL) {
+				POLYGON_LOOP(layer);
+				{
+					pocb(ctx, PCB, layer, polygon);
+				}
+				END_LOOP;
+			}
+			if (lacb != NULL)
+				lacb(ctx, PCB, layer, 0);
+		}
+		END_LOOP;
+	}
+}
+
+void pcb_loop_elements(void *ctx, pcb_element_cb_t ecb, pcb_eline_cb_t elcb, pcb_earc_cb_t eacb, pcb_etext_cb_t etcb, pcb_epin_cb_t epicb, pcb_epad_cb_t epacb)
+{
+	if ((ecb != NULL) || (elcb != NULL) || (eacb != NULL)  || (etcb != NULL) || (epicb != NULL) || (epacb != NULL)) {
+		ELEMENT_LOOP(PCB->Data);
+		{
+			if (ecb != NULL)
+				if (ecb(ctx, PCB, element, 1))
+					continue;
+
+			if (elcb != NULL) {
+				ELEMENTLINE_LOOP(element);
+				{
+					elcb(ctx, PCB, element, line);
+				}
+				END_LOOP;
+			}
+
+			if (eacb != NULL) {
+				ELEMENTARC_LOOP(element);
+				{
+					eacb(ctx, PCB, element, arc);
+				}
+				END_LOOP;
+			}
+
+			if (etcb != NULL) {
+				ELEMENTTEXT_LOOP(element);
+				{
+					etcb(ctx, PCB, element, text);
+				}
+				END_LOOP;
+			}
+
+			if (epicb != NULL) {
+				PIN_LOOP(element);
+				{
+					epicb(ctx, PCB, element, pin);
+				}
+				END_LOOP;
+			}
+
+
+			if (epacb != NULL) {
+				PAD_LOOP(element);
+				{
+					epacb(ctx, PCB, element, pad);
+				}
+				END_LOOP;
+			}
+
+			if (ecb != NULL)
+				ecb(ctx, PCB, element, 0);
+		}
+		END_LOOP;
+	}
+}
+
+void pcb_loop_vias(void *ctx, pcb_via_cb_t vcb)
+{
+	if (vcb != NULL) {
+		VIA_LOOP(PCB->Data);
+		{
+			vcb(ctx, PCB, via);
+		}
+		END_LOOP;
+	}
+}
+
+void pcb_loop_all(void *ctx,
+	pcb_layer_cb_t lacb, pcb_line_cb_t lcb, pcb_arc_cb_t acb, pcb_text_cb_t tcb, pcb_poly_cb_t pocb,
+	pcb_element_cb_t ecb, pcb_eline_cb_t elcb, pcb_earc_cb_t eacb, pcb_etext_cb_t etcb, pcb_epin_cb_t epicb, pcb_epad_cb_t epacb,
+	pcb_via_cb_t vcb
+	)
+{
+	pcb_loop_layers(ctx, lacb, lcb, acb, tcb, pocb);
+	pcb_loop_elements(ctx, ecb, elcb, eacb, etcb, epicb, epacb);
+	pcb_loop_vias(ctx, vcb);
+}
diff --git a/src/data.h b/src/data.h
new file mode 100644
index 0000000..319e4cb
--- /dev/null
+++ b/src/data.h
@@ -0,0 +1,110 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* common identifiers */
+
+#ifndef	PCB_DATA_H
+#define	PCB_DATA_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some shared identifiers
+ */
+
+
+extern CrosshairType Crosshair;
+
+extern MarkType Marked;
+
+extern OutputType Output;
+
+extern PCBTypePtr PCB;
+
+#define max_group (PCB->Data->LayerN)
+#define max_copper_layer (PCB->Data->LayerN)
+#define solder_silk_layer (max_copper_layer + SOLDER_LAYER)
+#define component_silk_layer (max_copper_layer + COMPONENT_LAYER)
+
+extern BufferType Buffers[MAX_BUFFER];
+
+/*extern	DeviceInfoType	PrintingDevice[];*/
+
+extern char *InputTranslations;
+
+extern int addedLines;
+extern int LayerStack[MAX_LAYER];
+
+extern pcb_bool Bumped;
+
+extern FlagType no_flags;
+
+/****** callback based loops *****/
+
+/* The functions returning int are called once when processing of a new layer
+   or element starts, with enter=1. If they return non-zero, the current layer
+   or element is skipped. If it is not skipped, the function is called once
+   at the end of processing the given layer or element, with enter=0 (and
+   return value ignored).
+
+   Any of the callbacks for any loop function can be NULL.
+   */
+
+/* layer object callbacks */
+typedef int (*pcb_layer_cb_t)(void *ctx, PCBType *pcb, LayerType *layer, int enter);
+typedef void (*pcb_line_cb_t)(void *ctx, PCBType *pcb, LayerType *layer, LineType *line);
+typedef void (*pcb_arc_cb_t)(void *ctx, PCBType *pcb, LayerType *layer, ArcType *arc);
+typedef void (*pcb_text_cb_t)(void *ctx, PCBType *pcb, LayerType *layer, TextType *text);
+typedef void (*pcb_poly_cb_t)(void *ctx, PCBType *pcb, LayerType *layer, PolygonType *poly);
+
+/* element callbacks */
+typedef int (*pcb_element_cb_t)(void *ctx, PCBType *pcb, ElementType *element, int enter);
+typedef void (*pcb_eline_cb_t)(void *ctx, PCBType *pcb, ElementType *element, LineType *line);
+typedef void (*pcb_earc_cb_t)(void *ctx, PCBType *pcb, ElementType *element, ArcType *arc);
+typedef void (*pcb_etext_cb_t)(void *ctx, PCBType *pcb, ElementType *element, TextType *text);
+typedef void (*pcb_epin_cb_t)(void *ctx, PCBType *pcb, ElementType *element, PinType *pin);
+typedef void (*pcb_epad_cb_t)(void *ctx, PCBType *pcb, ElementType *element, PadType *pad);
+
+/* via callbacks */
+typedef void (*pcb_via_cb_t)(void *ctx, PCBType *pcb, PinType *via);
+
+/* Loop over all layer objects on each layer. Layer is the outer loop. */
+void pcb_loop_layers(void *ctx, pcb_layer_cb_t lacb, pcb_line_cb_t lcb, pcb_arc_cb_t acb, pcb_text_cb_t tcb, pcb_poly_cb_t pocb);
+
+/* Loop over all elements and element primitives. Element is the outer loop. */
+void pcb_loop_elements(void *ctx, pcb_element_cb_t ecb, pcb_eline_cb_t elcb, pcb_earc_cb_t eacb, pcb_etext_cb_t etcb, pcb_epin_cb_t epicb, pcb_epad_cb_t epacb);
+
+/* Loop over all vias. */
+void pcb_loop_vias(void *ctx, pcb_via_cb_t vcb);
+
+/* Loop over all design objects. (So all the above three in one call.) */
+void pcb_loop_all(void *ctx,
+	pcb_layer_cb_t lacb, pcb_line_cb_t lcb, pcb_arc_cb_t acb, pcb_text_cb_t tcb, pcb_poly_cb_t pocb,
+	pcb_element_cb_t ecb, pcb_eline_cb_t elcb, pcb_earc_cb_t eacb, pcb_etext_cb_t etcb, pcb_epin_cb_t epicb, pcb_epad_cb_t epacb,
+	pcb_via_cb_t vcb
+);
+
+#endif
diff --git a/src/default.pcb b/src/default.pcb
new file mode 100644
index 0000000..edeee9d
--- /dev/null
+++ b/src/default.pcb
@@ -0,0 +1,47 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 600000 500000]
+
+Grid[2500.0 0 0 1]
+Cursor[2500 62500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Attribute("PCB::grid::unit" "mil")
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/src/default_font b/src/default_font
new file mode 100644
index 0000000..df80afc
--- /dev/null
+++ b/src/default_font
@@ -0,0 +1,773 @@
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 35 0 40 8)
+	SymbolLine(0 0 0 25 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 0 0 10 8)
+	SymbolLine(10 0 10 10 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 15 20 15 8)
+	SymbolLine(15 10 15 30 8)
+	SymbolLine(5 10 5 30 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 5 20 10 8)
+	SymbolLine(5 5 15 5 8)
+	SymbolLine(0 10 5 5 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 30 8)
+	SymbolLine(15 35 20 30 8)
+	SymbolLine(5 35 15 35 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(10 0 10 40 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 5 0 10 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 10 0 8)
+	SymbolLine(10 0 15 5 8)
+	SymbolLine(15 5 15 10 8)
+	SymbolLine(10 15 15 10 8)
+	SymbolLine(5 15 10 15 8)
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(0 40 40 0 8)
+	SymbolLine(35 40 40 35 8)
+	SymbolLine(40 30 40 35 8)
+	SymbolLine(35 25 40 30 8)
+	SymbolLine(30 25 35 25 8)
+	SymbolLine(25 30 30 25 8)
+	SymbolLine(25 30 25 35 8)
+	SymbolLine(25 35 30 40 8)
+	SymbolLine(30 40 35 40 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 25 15 10 8)
+	SymbolLine(5 40 10 40 8)
+	SymbolLine(10 40 20 30 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(5 0 10 0 8)
+	SymbolLine(10 0 15 5 8)
+	SymbolLine(15 5 15 10 8)
+	SymbolLine(0 25 0 35 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 10 10 0 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 35 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 0 5 5 8)
+	SymbolLine(5 5 5 35 8)
+	SymbolLine(0 40 5 35 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 10 20 30 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 20 20 20 8)
+	SymbolLine(10 10 10 30 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 20 20 20 8)
+	SymbolLine(10 10 10 30 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 50 10 40 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 20 20 20 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 40 5 40 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 35 30 5 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 30 20 10 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine( 0  8  8  0 8)
+	SymbolLine( 8  0  8 40 8)
+	SymbolLine( 0 40 15 40 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 25 40 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine( 0  5  5  0 8)
+	SymbolLine( 5  0 15  0 8)
+	SymbolLine(15  0 20  5 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine( 5 40 15 40 8)
+	SymbolLine( 0 35  5 40 8)
+
+	SymbolLine( 5 18 15 18 8)
+	SymbolLine(20  5 20 13 8)
+	SymbolLine(20 23 20 35 8)
+	SymbolLine(20 23 15 18 8)
+	SymbolLine(20 13 15 18 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 25 20 0 8)
+	SymbolLine(0 25 25 25 8)
+	SymbolLine(20 0 20 40 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(0 0 0 20 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(20 20 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15  0 20  5 8)
+	SymbolLine( 5  0 15  0 8)
+	SymbolLine( 0  5  5  0 8)
+	SymbolLine( 0  5  0 35 8)
+	SymbolLine( 0 35  5 40 8)
+	SymbolLine(15 18 20 23 8)
+	SymbolLine( 0 18 15 18 8)
+	SymbolLine( 5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 23 20 35 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine( 5 40 25  0 8)
+	SymbolLine( 0  0 25  0 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine( 0 35  5 40 8)
+	SymbolLine( 0 27  0 35 8)
+	SymbolLine( 0 27  7 20 8)
+	SymbolLine( 7 20 13 20 8)
+	SymbolLine(13 20 20 27 8)
+	SymbolLine(20 27 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine( 5 40 15 40 8)
+	SymbolLine( 0 13  7 20 8)
+	SymbolLine( 0  5  0 13 8)
+	SymbolLine( 0  5  5  0 8)
+	SymbolLine( 5  0 15  0 8)
+	SymbolLine(15  0 20  5 8)
+	SymbolLine(20  5 20 13 8)
+	SymbolLine(13 20 20 13 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(5 40 20 20 8)
+	SymbolLine(20 5 20 20 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(5 20 20 20 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 15 5 15 8)
+	SymbolLine(0 25 5 25 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 40 10 30 8)
+	SymbolLine(10 15 10 20 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 20 10 10 8)
+	SymbolLine(0 20 10 30 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 15 20 15 8)
+	SymbolLine(0 25 20 25 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 10 10 20 8)
+	SymbolLine(0 30 10 20 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 20 10 25 8)
+	SymbolLine(10 35 10 40 8)
+	SymbolLine(0 5 0 10 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 10 8)
+	SymbolLine(10 20 20 10 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine( 0 10  0 40 8)
+	SymbolLine( 0 10  7  0 8)
+	SymbolLine( 7  0 18  0 8)
+	SymbolLine(18  0 25 10 8)
+	SymbolLine(25 10 25 40 8)
+	SymbolLine( 0 20 25 20 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine( 0 40 20 40 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(25 23 25 35 8)
+	SymbolLine(20 18 25 23 8)
+	SymbolLine( 5 18 20 18 8)
+	SymbolLine( 5  0  5 40 8)
+	SymbolLine( 0  0 20  0 8)
+	SymbolLine(20  0 25  5 8)
+	SymbolLine(25  5 25 13 8)
+	SymbolLine(20 18 25 13 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(7 40 20 40 8)
+	SymbolLine(0 33  7 40 8)
+	SymbolLine(0  7  0 33 8)
+	SymbolLine(0  7  7  0 8)
+	SymbolLine(7  0 20  0 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine( 5  0  5 40 8)
+	SymbolLine(18  0 25  7 8)
+	SymbolLine(25  7 25 33 8)
+	SymbolLine(18 40 25 33 8)
+	SymbolLine( 0 40 18 40 8)
+	SymbolLine( 0  0 18  0 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 18 15 18 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(0  0  0 40 8)
+	SymbolLine(0  0 20  0 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0  0  0 40 8)
+	SymbolLine(0  0 20  0 8)
+	SymbolLine(0 18 15 18 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(10 20 20 20 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(25 0 25 40 8)
+	SymbolLine(0 20 25 20 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 0 10 0 8)
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 40 10 40 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine( 7  0 15  0 8)
+	SymbolLine(15  0 15 35 8)
+	SymbolLine(10 40 15 35 8)
+	SymbolLine( 5 40 10 40 8)
+	SymbolLine( 0 35  5 40 8)
+	SymbolLine( 0 35  0 30 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 20 20 0 8)
+	SymbolLine(0 20 20 40 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 40 20 40 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 0 15 20 8)
+	SymbolLine(15 20 30 0 8)
+	SymbolLine(30 0 30 40 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 0 25 40 8)
+	SymbolLine(25 0 25 40 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 5 0 35 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 15 0 8)
+	SymbolLine(15 0 20 5 8)
+	SymbolLine(20 5 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(25 5 25 15 8)
+	SymbolLine(20 20 25 15 8)
+	SymbolLine(5 20 20 20 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine( 0  5  0 35 8)
+	SymbolLine( 0  5  5  0 8)
+	SymbolLine( 5  0 15  0 8)
+	SymbolLine(15  0 20  5 8)
+	SymbolLine(20  5 20 30 8)
+	SymbolLine(10 40 20 30 8)
+	SymbolLine( 5 40 10 40 8)
+	SymbolLine( 0 35  5 40 8)
+	SymbolLine(10 25 20 40 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine( 0  0 20  0 8)
+	SymbolLine(20  0 25  5 8)
+	SymbolLine(25  5 25 15 8)
+	SymbolLine(20 20 25 15 8)
+	SymbolLine( 5 20 20 20 8)
+	SymbolLine( 5  0  5 40 8)
+	SymbolLine(13 20 25 40 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 0 25 5 8)
+	SymbolLine(5 0 20 0 8)
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(0 5 0 15 8)
+	SymbolLine(0 15 5 20 8)
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 0 20 0 8)
+	SymbolLine(10 0 10 40 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 0 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 0 20 35 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine( 0  0 10 40 8)
+	SymbolLine(10 40 20  0 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine( 0  0  0 20 8)
+	SymbolLine( 0 20  5 40 8)
+	SymbolLine( 5 40 15 20 8)
+	SymbolLine(15 20 25 40 8)
+	SymbolLine(25 40 30 20 8)
+	SymbolLine(30 20 30  0 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine( 0 40 25  0 8)
+	SymbolLine( 0  0 25 40 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine( 0  0 10 20 8)
+	SymbolLine(10 20 20  0 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine( 0  0 25  0 8)
+	SymbolLine( 0 40 25  0 8)
+	SymbolLine( 0 40 25 40 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 0 5 0 8)
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 40 5 40 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 5 30 35 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 0 5 0 8)
+	SymbolLine(5 0 5 40 8)
+	SymbolLine(0 40 5 40 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 5 5 0 8)
+	SymbolLine(5 0 10 5 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 40 20 40 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(20 20 20 35 8)
+	SymbolLine(20 35 25 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 25 20 35 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 20 40 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 0 20 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(20 30 20 25 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 5 5 40 8)
+	SymbolLine(5 5 10 0 8)
+	SymbolLine(10 0 15 0 8)
+	SymbolLine(0 20 10 20 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(0 50 5 55 8)
+	SymbolLine(5 55 15 55 8)
+	SymbolLine(15 55 20 50 8)
+	SymbolLine(20 20 20 50 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 40 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 10 0 11 10)
+	SymbolLine(0 25 0 40 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 10 5 11 10)
+	SymbolLine(5 25 5 50 8)
+	SymbolLine(0 55 5 50 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 0 0 40 8)
+	SymbolLine(0 25 15 40 8)
+	SymbolLine(0 25 10 15 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 0 0 35 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 25 5 40 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 40 8)
+	SymbolLine(20 25 25 20 8)
+	SymbolLine(25 20 30 20 8)
+	SymbolLine(30 20 35 25 8)
+	SymbolLine(35 25 35 40 8)
+	SymbolLine(0 20 5 25 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 25 5 40 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 40 8)
+	SymbolLine(0 20 5 25 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 20 35 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 25 5 55 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 20 20 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(25 25 25 35 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(10 40 20 40 8)
+	SymbolLine(5 35 10 40 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 25 20 55 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(5 20 15 20 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(0 25 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 25 5 40 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(10 20 20 20 8)
+	SymbolLine(0 20 5 25 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(20 40 25 35 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 20 20 8)
+	SymbolLine(20 20 25 25 8)
+	SymbolLine(0 35 5 40 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 0 5 35 8)
+	SymbolLine(5 35 10 40 8)
+	SymbolLine(0 15 10 15 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 20 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+	SymbolLine(20 20 20 35 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine( 0 20 10 40 8)
+	SymbolLine(20 20 10 40 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 20 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(5 40 10 40 8)
+	SymbolLine(10 40 15 35 8)
+	SymbolLine(15 20 15 35 8)
+	SymbolLine(15 35 20 40 8)
+	SymbolLine(20 40 25 40 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(30 20 30 35 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 20 0 35 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(20 20 20 50 8)
+	SymbolLine(15 55 20 50 8)
+	SymbolLine(5 55 15 55 8)
+	SymbolLine(0 50 5 55 8)
+	SymbolLine(5 40 15 40 8)
+	SymbolLine(15 40 20 35 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 20 20 20 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 40 20 40 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 5 10 0 8)
+	SymbolLine(5 5 5 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(5 25 5 35 8)
+	SymbolLine(5 35 10 40 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 0 0 40 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 0 5 5 8)
+	SymbolLine(5 5 5 15 8)
+	SymbolLine(5 15 10 20 8)
+	SymbolLine(5 25 10 20 8)
+	SymbolLine(5 25 5 35 8)
+	SymbolLine(0 40 5 35 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 25 5 20 8)
+	SymbolLine(5 20 10 20 8)
+	SymbolLine(10 20 15 25 8)
+	SymbolLine(15 25 20 25 8)
+	SymbolLine(20 25 25 20 8)
+)
+Symbol('@' 12)
+( 
+	SymbolLine(0 0 0 30 8)
+	SymbolLine(0 30 10 40 8)
+	SymbolLine(10 40 40 40 8)
+	SymbolLine(50 25 50 0 8)
+	SymbolLine(50 0 40 -10 8)
+	SymbolLine(40 -10 10 -10 8)
+	SymbolLine(10 -10 0 0 8)
+	SymbolLine(15 10 15 20 8)
+	SymbolLine(15 20 20 25 8)
+	SymbolLine(20 25 30 25 8)
+	SymbolLine(30 25 35 20 8)
+	SymbolLine(35 20 40 25 8)
+	SymbolLine(35 20 35 5 8)
+	SymbolLine(35 10 30 5 8)
+	SymbolLine(20 5 30 5 8)
+	SymbolLine(20 5 15 10 8)
+	SymbolLine(40 25 50 25 8)
+)
diff --git a/src/dolists.h b/src/dolists.h
new file mode 100644
index 0000000..83fa7c6
--- /dev/null
+++ b/src/dolists.h
@@ -0,0 +1,6 @@
+#undef REGISTER_ACTIONS
+#undef REGISTER_ATTRIBUTES
+
+#define REGISTER_ACTIONS(a, cookie) {extern void HIDCONCAT(register_,a)();HIDCONCAT(register_,a)();}
+#define REGISTER_ATTRIBUTES(a, cookie) {extern void HIDCONCAT(register_,a)();HIDCONCAT(register_,a)();}
+
diff --git a/src/draw.c b/src/draw.c
new file mode 100644
index 0000000..687009e
--- /dev/null
+++ b/src/draw.c
@@ -0,0 +1,1713 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2003, 2004 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* drawing routines
+ */
+
+#include "config.h"
+
+#include "conf_core.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "misc.h"
+#include "rotate.h"
+#include "rtree.h"
+#include "draw_fab.h"
+#include "hid_helper.h"
+#include "layer.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+#ifndef MAXINT
+#define MAXINT (((unsigned int)(~0))>>1)
+#endif
+
+#define	SMALL_SMALL_TEXT_SIZE	0
+#define	SMALL_TEXT_SIZE			1
+#define	NORMAL_TEXT_SIZE		2
+#define	LARGE_TEXT_SIZE			3
+#define	N_TEXT_SIZES			4
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static BoxType Block = { MAXINT, MAXINT, -MAXINT, -MAXINT };
+
+static int doing_pinout = 0;
+static pcb_bool doing_assy = pcb_false;
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void DrawEverything(const BoxType *);
+static void DrawPPV(int group, const BoxType *);
+static void DrawLayerGroup(int, const BoxType *);
+static void AddPart(void *);
+static void SetPVColor(PinTypePtr, int);
+static void DrawEMark(ElementTypePtr, Coord, Coord, pcb_bool);
+static void DrawMask(int side, const BoxType *);
+static void DrawPaste(int side, const BoxType *);
+static void DrawRats(const BoxType *);
+static void DrawSilk(int side, const BoxType *);
+
+static void LightenColor(const char *orig, char buf[8], double factor)
+{
+	unsigned int r, g, b;
+
+	if (orig[0] == '#') {
+		sscanf(&orig[1], "%2x%2x%2x", &r, &g, &b);
+		r = MIN(255, r * factor);
+		g = MIN(255, g * factor);
+		b = MIN(255, b * factor);
+	}
+	else {
+		r = 0xff;
+		g = 0xff;
+		b = 0xff;
+	}
+	pcb_snprintf(buf, sizeof("#XXXXXX"), "#%02x%02x%02x", r, g, b);
+}
+
+
+/*--------------------------------------------------------------------------------------
+ * setup color for pin or via
+ */
+static void SetPVColor(PinTypePtr Pin, int Type)
+{
+	char *color;
+	char buf[sizeof("#XXXXXX")];
+
+	if (Type == PCB_TYPE_VIA) {
+		if (!doing_pinout && TEST_FLAG(PCB_FLAG_WARN | PCB_FLAG_SELECTED | PCB_FLAG_FOUND, Pin)) {
+			if (TEST_FLAG(PCB_FLAG_WARN, Pin))
+				color = PCB->WarnColor;
+			else if (TEST_FLAG(PCB_FLAG_SELECTED, Pin))
+				color = PCB->ViaSelectedColor;
+			else
+				color = PCB->ConnectedColor;
+
+			if (TEST_FLAG(PCB_FLAG_ONPOINT, Pin)) {
+				assert(color != NULL);
+				LightenColor(color, buf, 1.75);
+				color = buf;
+			}
+		}
+		else
+			color = PCB->ViaColor;
+	}
+	else {
+		if (!doing_pinout && TEST_FLAG(PCB_FLAG_WARN | PCB_FLAG_SELECTED | PCB_FLAG_FOUND, Pin)) {
+			if (TEST_FLAG(PCB_FLAG_WARN, Pin))
+				color = PCB->WarnColor;
+			else if (TEST_FLAG(PCB_FLAG_SELECTED, Pin))
+				color = PCB->PinSelectedColor;
+			else
+				color = PCB->ConnectedColor;
+
+			if (TEST_FLAG(PCB_FLAG_ONPOINT, Pin)) {
+				assert(color != NULL);
+				LightenColor(color, buf, 1.75);
+				color = buf;
+			}
+		}
+		else
+			color = PCB->PinColor;
+	}
+
+	gui->set_color(Output.fgGC, color);
+}
+
+/*---------------------------------------------------------------------------
+ *  Adds the update rect to the update region
+ */
+static void AddPart(void *b)
+{
+	BoxType *box = (BoxType *) b;
+
+	Block.X1 = MIN(Block.X1, box->X1);
+	Block.X2 = MAX(Block.X2, box->X2);
+	Block.Y1 = MIN(Block.Y1, box->Y1);
+	Block.Y2 = MAX(Block.Y2, box->Y2);
+}
+
+/*
+ * initiate the actual redrawing of the updated area
+ */
+pcb_cardinal_t pcb_draw_inhibit = 0;
+void Draw(void)
+{
+	if (pcb_draw_inhibit)
+		return;
+	if (Block.X1 <= Block.X2 && Block.Y1 <= Block.Y2)
+		gui->invalidate_lr(Block.X1, Block.X2, Block.Y1, Block.Y2);
+
+	/* shrink the update block */
+	Block.X1 = Block.Y1 = MAXINT;
+	Block.X2 = Block.Y2 = -MAXINT;
+}
+
+/* ----------------------------------------------------------------------
+ * redraws all the data by the event handlers
+ */
+void Redraw(void)
+{
+	gui->invalidate_all();
+}
+
+static void _draw_pv_name(PinType * pv)
+{
+	BoxType box;
+	pcb_bool vert;
+	TextType text;
+	char buff[128];
+	const char *pn;
+
+	if (!pv->Name || !pv->Name[0])
+		pn = EMPTY(pv->Number);
+	else
+		pn = EMPTY(conf_core.editor.show_number ? pv->Number : pv->Name);
+
+	if (GET_INTCONN(pv) > 0)
+		pcb_snprintf(buff, sizeof(buff), "%s[%d]", pn, GET_INTCONN(pv));
+	else
+		strcpy(buff, pn);
+	text.TextString = buff;
+
+	vert = TEST_FLAG(PCB_FLAG_EDGE2, pv);
+
+	if (vert) {
+		box.X1 = pv->X - pv->Thickness / 2 + conf_core.appearance.pinout.text_offset_y;
+		box.Y1 = pv->Y - pv->DrillingHole / 2 - conf_core.appearance.pinout.text_offset_x;
+	}
+	else {
+		box.X1 = pv->X + pv->DrillingHole / 2 + conf_core.appearance.pinout.text_offset_x;
+		box.Y1 = pv->Y - pv->Thickness / 2 + conf_core.appearance.pinout.text_offset_y;
+	}
+
+	gui->set_color(Output.fgGC, PCB->PinNameColor);
+
+	text.Flags = NoFlags();
+	/* Set font height to approx 56% of pin thickness */
+	text.Scale = 56 * pv->Thickness / FONT_CAPHEIGHT;
+	text.X = box.X1;
+	text.Y = box.Y1;
+	text.Direction = vert ? 1 : 0;
+
+	if (gui->gui)
+		doing_pinout++;
+	DrawTextLowLevel(&text, 0);
+	if (gui->gui)
+		doing_pinout--;
+}
+
+static void _draw_pv(PinTypePtr pv, pcb_bool draw_hole)
+{
+	if (conf_core.editor.thin_draw)
+		gui->thindraw_pcb_pv(Output.fgGC, Output.fgGC, pv, draw_hole, pcb_false);
+	else
+		gui->fill_pcb_pv(Output.fgGC, Output.bgGC, pv, draw_hole, pcb_false);
+
+	if (!TEST_FLAG(PCB_FLAG_HOLE, pv) && TEST_FLAG(PCB_FLAG_DISPLAYNAME, pv))
+		_draw_pv_name(pv);
+}
+
+static void draw_pin(PinTypePtr pin, pcb_bool draw_hole)
+{
+	SetPVColor(pin, PCB_TYPE_PIN);
+	_draw_pv(pin, draw_hole);
+}
+
+static r_dir_t pin_callback(const BoxType * b, void *cl)
+{
+	draw_pin((PinType *) b, pcb_false);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void draw_via(PinTypePtr via, pcb_bool draw_hole)
+{
+	SetPVColor(via, PCB_TYPE_VIA);
+	_draw_pv(via, draw_hole);
+}
+
+static r_dir_t via_callback(const BoxType * b, void *cl)
+{
+	draw_via((PinType *) b, pcb_false);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void draw_pad_name(PadType * pad)
+{
+	BoxType box;
+	pcb_bool vert;
+	TextType text;
+	char buff[128];
+	const char *pn;
+
+	if (!pad->Name || !pad->Name[0])
+		pn = EMPTY(pad->Number);
+	else
+		pn = conf_core.editor.show_number ? pad->Number : pad->Name;
+
+	if (GET_INTCONN(pad) > 0)
+		pcb_snprintf(buff, sizeof(buff), "%s[%d]", pn, GET_INTCONN(pad));
+	else
+		strcpy(buff, pn);
+	text.TextString = buff;
+
+	/* should text be vertical ? */
+	vert = (pad->Point1.X == pad->Point2.X);
+
+	if (vert) {
+		box.X1 = pad->Point1.X - pad->Thickness / 2;
+		box.Y1 = MAX(pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2;
+		box.X1 += conf_core.appearance.pinout.text_offset_y;
+		box.Y1 -= conf_core.appearance.pinout.text_offset_x;
+	}
+	else {
+		box.X1 = MIN(pad->Point1.X, pad->Point2.X) - pad->Thickness / 2;
+		box.Y1 = pad->Point1.Y - pad->Thickness / 2;
+		box.X1 += conf_core.appearance.pinout.text_offset_x;
+		box.Y1 += conf_core.appearance.pinout.text_offset_y;
+	}
+
+	gui->set_color(Output.fgGC, PCB->PinNameColor);
+
+	text.Flags = NoFlags();
+	/* Set font height to approx 90% of pin thickness */
+	text.Scale = 90 * pad->Thickness / FONT_CAPHEIGHT;
+	text.X = box.X1;
+	text.Y = box.Y1;
+	text.Direction = vert ? 1 : 0;
+
+	DrawTextLowLevel(&text, 0);
+}
+
+static void _draw_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask)
+{
+	if (clear && !mask && pad->Clearance <= 0)
+		return;
+
+	if (conf_core.editor.thin_draw || (clear && conf_core.editor.thin_draw_poly))
+		gui->thindraw_pcb_pad(gc, pad, clear, mask);
+	else
+		gui->fill_pcb_pad(gc, pad, clear, mask);
+}
+
+static void draw_pad(PadType * pad)
+{
+	const char *color = NULL;
+	char buf[sizeof("#XXXXXX")];
+
+	if (doing_pinout)
+		gui->set_color(Output.fgGC, PCB->PinColor);
+	else if (TEST_FLAG(PCB_FLAG_WARN | PCB_FLAG_SELECTED | PCB_FLAG_FOUND, pad)) {
+		if (TEST_FLAG(PCB_FLAG_WARN, pad))
+			color = PCB->WarnColor;
+		else if (TEST_FLAG(PCB_FLAG_SELECTED, pad))
+			color = PCB->PinSelectedColor;
+		else
+			color = PCB->ConnectedColor;
+	}
+	else if (FRONT(pad))
+		color = PCB->PinColor;
+	else
+		color = PCB->InvisibleObjectsColor;
+
+	if (TEST_FLAG(PCB_FLAG_ONPOINT, pad)) {
+		assert(color != NULL);
+		LightenColor(color, buf, 1.75);
+		color = buf;
+	}
+
+	if (color != NULL)
+		gui->set_color(Output.fgGC, color);
+
+	_draw_pad(Output.fgGC, pad, pcb_false, pcb_false);
+
+	if (doing_pinout || TEST_FLAG(PCB_FLAG_DISPLAYNAME, pad))
+		draw_pad_name(pad);
+}
+
+static r_dir_t pad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	int *side = cl;
+
+	if (ON_SIDE(pad, *side))
+		draw_pad(pad);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void draw_element_name(ElementType * element)
+{
+	if ((conf_core.editor.hide_names && gui->gui) || TEST_FLAG(PCB_FLAG_HIDENAME, element))
+		return;
+	if (doing_pinout || doing_assy)
+		gui->set_color(Output.fgGC, PCB->ElementColor);
+	else if (TEST_FLAG(PCB_FLAG_SELECTED, &ELEMENT_TEXT(PCB, element)))
+		gui->set_color(Output.fgGC, PCB->ElementSelectedColor);
+	else if (FRONT(element)) {
+/* TODO: why do we test for Name's flag here? */
+		if (TEST_FLAG(PCB_FLAG_NONETLIST, element))
+			gui->set_color(Output.fgGC, PCB->ElementColor_nonetlist);
+		else
+			gui->set_color(Output.fgGC, PCB->ElementColor);
+	}
+	else
+		gui->set_color(Output.fgGC, PCB->InvisibleObjectsColor);
+
+	DrawTextLowLevel(&ELEMENT_TEXT(PCB, element), PCB->minSlk);
+
+}
+
+static r_dir_t name_callback(const BoxType * b, void *cl)
+{
+	TextTypePtr text = (TextTypePtr) b;
+	ElementTypePtr element = (ElementTypePtr) text->Element;
+	int *side = cl;
+
+	if (TEST_FLAG(PCB_FLAG_HIDENAME, element))
+		return R_DIR_NOT_FOUND;
+
+	if (ON_SIDE(element, *side))
+		draw_element_name(element);
+	return R_DIR_NOT_FOUND;
+}
+
+static void draw_element_pins_and_pads(ElementType * element)
+{
+	PAD_LOOP(element);
+	{
+		if (doing_pinout || doing_assy || FRONT(pad) || PCB->InvisibleObjectsOn)
+			draw_pad(pad);
+	}
+	END_LOOP;
+	PIN_LOOP(element);
+	{
+		draw_pin(pin, pcb_true);
+	}
+	END_LOOP;
+}
+
+static r_dir_t EMark_callback(const BoxType * b, void *cl)
+{
+	ElementTypePtr element = (ElementTypePtr) b;
+
+	DrawEMark(element, element->MarkX, element->MarkY, !FRONT(element));
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t hole_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pv = (PinTypePtr) b;
+	int plated = cl ? *(int *) cl : -1;
+	const char *color;
+	char buf[sizeof("#XXXXXX")];
+
+	if ((plated == 0 && !TEST_FLAG(PCB_FLAG_HOLE, pv)) || (plated == 1 && TEST_FLAG(PCB_FLAG_HOLE, pv)))
+		return R_DIR_FOUND_CONTINUE;
+
+	if (conf_core.editor.thin_draw) {
+		if (!TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+			gui->set_line_cap(Output.fgGC, Round_Cap);
+			gui->set_line_width(Output.fgGC, 0);
+			gui->draw_arc(Output.fgGC, pv->X, pv->Y, pv->DrillingHole / 2, pv->DrillingHole / 2, 0, 360);
+		}
+	}
+	else
+		gui->fill_circle(Output.bgGC, pv->X, pv->Y, pv->DrillingHole / 2);
+
+	if (TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+		if (TEST_FLAG(PCB_FLAG_WARN, pv))
+			color = PCB->WarnColor;
+		else if (TEST_FLAG(PCB_FLAG_SELECTED, pv))
+			color = PCB->ViaSelectedColor;
+		else
+			color = conf_core.appearance.color.black;
+
+		if (TEST_FLAG(PCB_FLAG_ONPOINT, pv)) {
+			assert(color != NULL);
+			LightenColor(color, buf, 1.75);
+			color = buf;
+		}
+		gui->set_color(Output.fgGC, color);
+
+		gui->set_line_cap(Output.fgGC, Round_Cap);
+		gui->set_line_width(Output.fgGC, 0);
+		gui->draw_arc(Output.fgGC, pv->X, pv->Y, pv->DrillingHole / 2, pv->DrillingHole / 2, 0, 360);
+	}
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void DrawHoles(pcb_bool draw_plated, pcb_bool draw_unplated, const BoxType * drawn_area)
+{
+	int plated = -1;
+
+	if (draw_plated && !draw_unplated)
+		plated = 1;
+	if (!draw_plated && draw_unplated)
+		plated = 0;
+
+	r_search(PCB->Data->pin_tree, drawn_area, NULL, hole_callback, &plated, NULL);
+	r_search(PCB->Data->via_tree, drawn_area, NULL, hole_callback, &plated, NULL);
+}
+
+static void _draw_line(LineType * line)
+{
+	gui->set_line_cap(Output.fgGC, Trace_Cap);
+	if (conf_core.editor.thin_draw)
+		gui->set_line_width(Output.fgGC, 0);
+	else
+		gui->set_line_width(Output.fgGC, line->Thickness);
+
+	gui->draw_line(Output.fgGC, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
+}
+
+static void draw_line(LayerType * layer, LineType * line)
+{
+	const char *color;
+	char buf[sizeof("#XXXXXX")];
+
+	if (TEST_FLAG(PCB_FLAG_WARN, line))
+		color = PCB->WarnColor;
+	else if (TEST_FLAG(PCB_FLAG_SELECTED | PCB_FLAG_FOUND, line)) {
+		if (TEST_FLAG(PCB_FLAG_SELECTED, line))
+			color = layer->SelectedColor;
+		else
+			color = PCB->ConnectedColor;
+	}
+	else
+		color = layer->Color;
+
+	if (TEST_FLAG(PCB_FLAG_ONPOINT, line)) {
+		assert(color != NULL);
+		LightenColor(color, buf, 1.75);
+		color = buf;
+	}
+
+	gui->set_color(Output.fgGC, color);
+	_draw_line(line);
+}
+
+static r_dir_t line_callback(const BoxType * b, void *cl)
+{
+	draw_line((LayerType *) cl, (LineType *) b);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t rat_callback(const BoxType * b, void *cl)
+{
+	RatType *rat = (RatType *) b;
+
+	if (TEST_FLAG(PCB_FLAG_SELECTED | PCB_FLAG_FOUND, rat)) {
+		if (TEST_FLAG(PCB_FLAG_SELECTED, rat))
+			gui->set_color(Output.fgGC, PCB->RatSelectedColor);
+		else
+			gui->set_color(Output.fgGC, PCB->ConnectedColor);
+	}
+	else
+		gui->set_color(Output.fgGC, PCB->RatColor);
+
+	if (conf_core.appearance.rat_thickness < 20)
+		rat->Thickness = pixel_slop * conf_core.appearance.rat_thickness;
+	/* rats.c set PCB_FLAG_VIA if this rat goes to a containing poly: draw a donut */
+	if (TEST_FLAG(PCB_FLAG_VIA, rat)) {
+		int w = rat->Thickness;
+
+		if (conf_core.editor.thin_draw)
+			gui->set_line_width(Output.fgGC, 0);
+		else
+			gui->set_line_width(Output.fgGC, w);
+		gui->draw_arc(Output.fgGC, rat->Point1.X, rat->Point1.Y, w * 2, w * 2, 0, 360);
+	}
+	else
+		_draw_line((LineType *) rat);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void _draw_arc(ArcType * arc)
+{
+	if (!arc->Thickness)
+		return;
+
+	if (conf_core.editor.thin_draw)
+		gui->set_line_width(Output.fgGC, 0);
+	else
+		gui->set_line_width(Output.fgGC, arc->Thickness);
+	gui->set_line_cap(Output.fgGC, Trace_Cap);
+
+	gui->draw_arc(Output.fgGC, arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta);
+}
+
+static void draw_arc(LayerType * layer, ArcType * arc)
+{
+	const char *color;
+	char buf[sizeof("#XXXXXX")];
+
+	if (TEST_FLAG(PCB_FLAG_WARN, arc))
+		color = PCB->WarnColor;
+	else if (TEST_FLAG(PCB_FLAG_SELECTED | PCB_FLAG_FOUND, arc)) {
+		if (TEST_FLAG(PCB_FLAG_SELECTED, arc))
+			color = layer->SelectedColor;
+		else
+			color = PCB->ConnectedColor;
+	}
+	else
+		color = layer->Color;
+
+	if (TEST_FLAG(PCB_FLAG_ONPOINT, arc)) {
+		assert(color != NULL);
+		LightenColor(color, buf, 1.75);
+		color = buf;
+	}
+	gui->set_color(Output.fgGC, color);
+	_draw_arc(arc);
+}
+
+static r_dir_t arc_callback(const BoxType * b, void *cl)
+{
+	draw_arc((LayerTypePtr) cl, (ArcTypePtr) b);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void draw_element_package(ElementType * element)
+{
+	/* set color and draw lines, arcs, text and pins */
+	if (doing_pinout || doing_assy)
+		gui->set_color(Output.fgGC, PCB->ElementColor);
+	else if (TEST_FLAG(PCB_FLAG_SELECTED, element))
+		gui->set_color(Output.fgGC, PCB->ElementSelectedColor);
+	else if (FRONT(element))
+		gui->set_color(Output.fgGC, PCB->ElementColor);
+	else
+		gui->set_color(Output.fgGC, PCB->InvisibleObjectsColor);
+
+	/* draw lines, arcs, text and pins */
+	ELEMENTLINE_LOOP(element);
+	{
+		_draw_line(line);
+	}
+	END_LOOP;
+	ARC_LOOP(element);
+	{
+		_draw_arc(arc);
+	}
+	END_LOOP;
+}
+
+static r_dir_t element_callback(const BoxType * b, void *cl)
+{
+	ElementTypePtr element = (ElementTypePtr) b;
+	int *side = cl;
+
+	if (ON_SIDE(element, *side))
+		draw_element_package(element);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* ---------------------------------------------------------------------------
+ * prints assembly drawing.
+ */
+
+static void PrintAssembly(int side, const BoxType * drawn_area)
+{
+	int side_group = GetLayerGroupNumberByNumber(max_copper_layer + side);
+
+	doing_assy = pcb_true;
+	gui->set_draw_faded(Output.fgGC, 1);
+	DrawLayerGroup(side_group, drawn_area);
+	gui->set_draw_faded(Output.fgGC, 0);
+
+	/* draw package */
+	DrawSilk(side, drawn_area);
+	doing_assy = pcb_false;
+}
+
+static void DrawEverything_holes(const BoxType * drawn_area)
+{
+	int plated, unplated;
+	CountHoles(&plated, &unplated, drawn_area);
+
+	if (plated && gui->set_layer("plated-drill", SL(PDRILL, 0), 0)) {
+		DrawHoles(pcb_true, pcb_false, drawn_area);
+		gui->end_layer();
+	}
+
+	if (unplated && gui->set_layer("unplated-drill", SL(UDRILL, 0), 0)) {
+		DrawHoles(pcb_false, pcb_true, drawn_area);
+		gui->end_layer();
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes some identifiers for a new zoom factor and redraws whole screen
+ */
+static void DrawEverything(const BoxType * drawn_area)
+{
+	int i, ngroups, side;
+	int component, solder;
+	/* This is the list of layer groups we will draw.  */
+	int do_group[MAX_LAYER];
+	/* This is the reverse of the order in which we draw them.  */
+	int drawn_groups[MAX_LAYER];
+
+	pcb_bool paste_empty;
+
+	PCB->Data->SILKLAYER.Color = PCB->ElementColor;
+	PCB->Data->BACKSILKLAYER.Color = PCB->InvisibleObjectsColor;
+
+	memset(do_group, 0, sizeof(do_group));
+	for (ngroups = 0, i = 0; i < max_copper_layer; i++) {
+		LayerType *l = LAYER_ON_STACK(i);
+		int group = GetLayerGroupNumberByNumber(LayerStack[i]);
+		if (l->On && !do_group[group]) {
+			do_group[group] = 1;
+			drawn_groups[ngroups++] = group;
+		}
+	}
+
+	component = GetLayerGroupNumberByNumber(component_silk_layer);
+	solder = GetLayerGroupNumberByNumber(solder_silk_layer);
+
+	/*
+	 * first draw all 'invisible' stuff
+	 */
+	if (!conf_core.editor.check_planes
+			&& gui->set_layer("invisible", SL(INVISIBLE, 0), 0)) {
+		side = SWAP_IDENT ? COMPONENT_LAYER : SOLDER_LAYER;
+		if (PCB->ElementOn) {
+			r_search(PCB->Data->element_tree, drawn_area, NULL, element_callback, &side, NULL);
+			r_search(PCB->Data->name_tree[NAME_INDEX()], drawn_area, NULL, name_callback, &side, NULL);
+			DrawLayer(&(PCB->Data->Layer[max_copper_layer + side]), drawn_area);
+		}
+		r_search(PCB->Data->pad_tree, drawn_area, NULL, pad_callback, &side, NULL);
+		gui->end_layer();
+	}
+
+	/* draw all layers in layerstack order */
+	for (i = ngroups - 1; i >= 0; i--) {
+		int group = drawn_groups[i];
+
+		if (gui->set_layer(0, group, 0)) {
+			DrawLayerGroup(group, drawn_area);
+			gui->end_layer();
+		}
+	}
+
+	if (conf_core.editor.check_planes && gui->gui)
+		return;
+
+	/* Draw pins, pads, vias below silk */
+	if (gui->gui)
+		DrawPPV(SWAP_IDENT ? solder : component, drawn_area);
+	else if (!gui->holes_after)
+		DrawEverything_holes(drawn_area);
+
+	/* Draw the solder mask if turned on */
+	if (gui->set_layer("componentmask", SL(MASK, TOP), 0)) {
+		DrawMask(COMPONENT_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->set_layer("soldermask", SL(MASK, BOTTOM), 0)) {
+		DrawMask(SOLDER_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->set_layer("topsilk", SL(SILK, TOP), 0)) {
+		DrawSilk(COMPONENT_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->set_layer("bottomsilk", SL(SILK, BOTTOM), 0)) {
+		DrawSilk(SOLDER_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->holes_after)
+		DrawEverything_holes(drawn_area);
+
+	if (gui->gui) {
+		/* Draw element Marks */
+		if (PCB->PinOn)
+			r_search(PCB->Data->element_tree, drawn_area, NULL, EMark_callback, NULL, NULL);
+		/* Draw rat lines on top */
+		if (gui->set_layer("rats", SL(RATS, 0), 0)) {
+			DrawRats(drawn_area);
+			gui->end_layer();
+		}
+	}
+
+	paste_empty = IsPasteEmpty(COMPONENT_LAYER);
+	if (gui->set_layer("toppaste", SL(PASTE, TOP), paste_empty)) {
+		DrawPaste(COMPONENT_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	paste_empty = IsPasteEmpty(SOLDER_LAYER);
+	if (gui->set_layer("bottompaste", SL(PASTE, BOTTOM), paste_empty)) {
+		DrawPaste(SOLDER_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->set_layer("topassembly", SL(ASSY, TOP), 0)) {
+		PrintAssembly(COMPONENT_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->set_layer("bottomassembly", SL(ASSY, BOTTOM), 0)) {
+		PrintAssembly(SOLDER_LAYER, drawn_area);
+		gui->end_layer();
+	}
+
+	if (gui->set_layer("fab", SL(FAB, 0), 0)) {
+		DrawFab(Output.fgGC);
+		gui->end_layer();
+	}
+}
+
+static void DrawEMark(ElementTypePtr e, Coord X, Coord Y, pcb_bool invisible)
+{
+	Coord mark_size = EMARK_SIZE;
+	if (!PCB->InvisibleObjectsOn && invisible)
+		return;
+
+	if (pinlist_length(&e->Pin) != 0) {
+		PinType *pin0 = pinlist_first(&e->Pin);
+		if (TEST_FLAG(PCB_FLAG_HOLE, pin0))
+			mark_size = MIN(mark_size, pin0->DrillingHole / 2);
+		else
+			mark_size = MIN(mark_size, pin0->Thickness / 2);
+	}
+
+	if (padlist_length(&e->Pad) != 0) {
+		PadType *pad0 = padlist_first(&e->Pad);
+		mark_size = MIN(mark_size, pad0->Thickness / 2);
+	}
+
+	gui->set_color(Output.fgGC, invisible ? PCB->InvisibleMarkColor : PCB->ElementColor);
+	gui->set_line_cap(Output.fgGC, Trace_Cap);
+	gui->set_line_width(Output.fgGC, 0);
+	gui->draw_line(Output.fgGC, X - mark_size, Y, X, Y - mark_size);
+	gui->draw_line(Output.fgGC, X + mark_size, Y, X, Y - mark_size);
+	gui->draw_line(Output.fgGC, X - mark_size, Y, X, Y + mark_size);
+	gui->draw_line(Output.fgGC, X + mark_size, Y, X, Y + mark_size);
+
+	/*
+	 * If an element is locked, place a "L" on top of the "diamond".
+	 * This provides a nice visual indication that it is locked that
+	 * works even for color blind users.
+	 */
+	if (TEST_FLAG(PCB_FLAG_LOCK, e)) {
+		gui->draw_line(Output.fgGC, X, Y, X + 2 * mark_size, Y);
+		gui->draw_line(Output.fgGC, X, Y, X, Y - 4 * mark_size);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * Draws pins pads and vias - Always draws for non-gui HIDs,
+ * otherwise drawing depends on PCB->PinOn and PCB->ViaOn
+ */
+static void DrawPPV(int group, const BoxType * drawn_area)
+{
+	int component_group = GetLayerGroupNumberByNumber(component_silk_layer);
+	int solder_group = GetLayerGroupNumberByNumber(solder_silk_layer);
+	int side;
+
+	if (PCB->PinOn || !gui->gui) {
+		/* draw element pins */
+		r_search(PCB->Data->pin_tree, drawn_area, NULL, pin_callback, NULL, NULL);
+
+		/* draw element pads */
+		if (group == component_group) {
+			side = COMPONENT_LAYER;
+			r_search(PCB->Data->pad_tree, drawn_area, NULL, pad_callback, &side, NULL);
+		}
+
+		if (group == solder_group) {
+			side = SOLDER_LAYER;
+			r_search(PCB->Data->pad_tree, drawn_area, NULL, pad_callback, &side, NULL);
+		}
+	}
+
+	/* draw vias */
+	if (PCB->ViaOn || !gui->gui) {
+		r_search(PCB->Data->via_tree, drawn_area, NULL, via_callback, NULL, NULL);
+		r_search(PCB->Data->via_tree, drawn_area, NULL, hole_callback, NULL, NULL);
+	}
+	if (PCB->PinOn || doing_assy)
+		r_search(PCB->Data->pin_tree, drawn_area, NULL, hole_callback, NULL, NULL);
+}
+
+static r_dir_t clearPin_callback(const BoxType * b, void *cl)
+{
+	PinType *pin = (PinTypePtr) b;
+	if (conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly)
+		gui->thindraw_pcb_pv(Output.pmGC, Output.pmGC, pin, pcb_false, pcb_true);
+	else
+		gui->fill_pcb_pv(Output.pmGC, Output.pmGC, pin, pcb_false, pcb_true);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+struct poly_info {
+	const BoxType *drawn_area;
+	LayerType *layer;
+};
+
+static r_dir_t poly_callback(const BoxType * b, void *cl)
+{
+	struct poly_info *i = cl;
+	PolygonType *polygon = (PolygonType *) b;
+	static const char *color;
+	char buf[sizeof("#XXXXXX")];
+
+	if (!polygon->Clipped)
+		return R_DIR_NOT_FOUND;
+
+	if (TEST_FLAG(PCB_FLAG_WARN, polygon))
+		color = PCB->WarnColor;
+	else if (TEST_FLAG(PCB_FLAG_SELECTED, polygon))
+		color = i->layer->SelectedColor;
+	else if (TEST_FLAG(PCB_FLAG_FOUND, polygon))
+		color = PCB->ConnectedColor;
+	else if (TEST_FLAG(PCB_FLAG_ONPOINT, polygon)) {
+		assert(color != NULL);
+		LightenColor(color, buf, 1.75);
+		color = buf;
+	}
+	else
+		color = i->layer->Color;
+	gui->set_color(Output.fgGC, color);
+
+	if ((gui->thindraw_pcb_polygon != NULL) && (conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly))
+		gui->thindraw_pcb_polygon(Output.fgGC, polygon, i->drawn_area);
+	else
+		gui->fill_pcb_polygon(Output.fgGC, polygon, i->drawn_area);
+
+	/* If checking planes, thin-draw any pieces which have been clipped away */
+	if (gui->thindraw_pcb_polygon != NULL && conf_core.editor.check_planes && !TEST_FLAG(PCB_FLAG_FULLPOLY, polygon)) {
+		PolygonType poly = *polygon;
+
+		for (poly.Clipped = polygon->Clipped->f; poly.Clipped != polygon->Clipped; poly.Clipped = poly.Clipped->f)
+			gui->thindraw_pcb_polygon(Output.fgGC, &poly, i->drawn_area);
+	}
+
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t clearPad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	int *side = cl;
+	if (ON_SIDE(pad, *side) && pad->Mask)
+		_draw_pad(Output.pmGC, pad, pcb_true, pcb_true);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* ---------------------------------------------------------------------------
+ * Draws silk layer.
+ */
+
+static void DrawSilk(int side, const BoxType * drawn_area)
+{
+#if 0
+	/* This code is used when you want to mask silk to avoid exposed
+	   pins and pads.  We decided it was a bad idea to do this
+	   unconditionally, but the code remains.  */
+#endif
+
+#if 0
+	if (gui->poly_before) {
+		gui->use_mask(HID_MASK_BEFORE);
+#endif
+		DrawLayer(LAYER_PTR(max_copper_layer + side), drawn_area);
+		/* draw package */
+		r_search(PCB->Data->element_tree, drawn_area, NULL, element_callback, &side, NULL);
+		r_search(PCB->Data->name_tree[NAME_INDEX()], drawn_area, NULL, name_callback, &side, NULL);
+#if 0
+	}
+
+	gui->use_mask(HID_MASK_CLEAR);
+	r_search(PCB->Data->pin_tree, drawn_area, NULL, clearPin_callback, NULL, NULL);
+	r_search(PCB->Data->via_tree, drawn_area, NULL, clearPin_callback, NULL, NULL);
+	r_search(PCB->Data->pad_tree, drawn_area, NULL, clearPad_callback, &side, NULL);
+
+	if (gui->poly_after) {
+		gui->use_mask(HID_MASK_AFTER);
+		DrawLayer(LAYER_PTR(max_copper_layer + layer), drawn_area);
+		/* draw package */
+		r_search(PCB->Data->element_tree, drawn_area, NULL, element_callback, &side, NULL);
+		r_search(PCB->Data->name_tree[NAME_INDEX()], drawn_area, NULL, name_callback, &side, NULL);
+	}
+	gui->use_mask(HID_MASK_OFF);
+#endif
+}
+
+
+static void DrawMaskBoardArea(int mask_type, const BoxType * drawn_area)
+{
+	/* Skip the mask drawing if the GUI doesn't want this type */
+	if ((mask_type == HID_MASK_BEFORE && !gui->poly_before) || (mask_type == HID_MASK_AFTER && !gui->poly_after))
+		return;
+
+	gui->use_mask(mask_type);
+	gui->set_color(Output.fgGC, PCB->MaskColor);
+	if (drawn_area == NULL)
+		gui->fill_rect(Output.fgGC, 0, 0, PCB->MaxWidth, PCB->MaxHeight);
+	else
+		gui->fill_rect(Output.fgGC, drawn_area->X1, drawn_area->Y1, drawn_area->X2, drawn_area->Y2);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws solder mask layer - this will cover nearly everything
+ */
+static void DrawMask(int side, const BoxType * screen)
+{
+	int thin = conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly;
+
+	if (thin)
+		gui->set_color(Output.pmGC, PCB->MaskColor);
+	else {
+		DrawMaskBoardArea(HID_MASK_BEFORE, screen);
+		gui->use_mask(HID_MASK_CLEAR);
+	}
+
+	r_search(PCB->Data->pin_tree, screen, NULL, clearPin_callback, NULL, NULL);
+	r_search(PCB->Data->via_tree, screen, NULL, clearPin_callback, NULL, NULL);
+	r_search(PCB->Data->pad_tree, screen, NULL, clearPad_callback, &side, NULL);
+
+	if (thin)
+		gui->set_color(Output.pmGC, "erase");
+	else {
+		DrawMaskBoardArea(HID_MASK_AFTER, screen);
+		gui->use_mask(HID_MASK_OFF);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draws solder paste layer for a given side of the board
+ */
+static void DrawPaste(int side, const BoxType * drawn_area)
+{
+	gui->set_color(Output.fgGC, PCB->ElementColor);
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (ON_SIDE(pad, side) && !TEST_FLAG(PCB_FLAG_NOPASTE, pad) && pad->Mask > 0) {
+			if (pad->Mask < pad->Thickness)
+				_draw_pad(Output.fgGC, pad, pcb_true, pcb_true);
+			else
+				_draw_pad(Output.fgGC, pad, pcb_false, pcb_false);
+		}
+	}
+	ENDALL_LOOP;
+}
+
+static void DrawRats(const BoxType * drawn_area)
+{
+	/*
+	 * XXX lesstif allows positive AND negative drawing in HID_MASK_CLEAR.
+	 * XXX gtk only allows negative drawing.
+	 * XXX using the mask here is to get rat transparency
+	 */
+	int can_mask = strcmp(gui->name, "lesstif") == 0;
+
+	if (can_mask)
+		gui->use_mask(HID_MASK_CLEAR);
+	r_search(PCB->Data->rat_tree, drawn_area, NULL, rat_callback, NULL, NULL);
+	if (can_mask)
+		gui->use_mask(HID_MASK_OFF);
+}
+
+static r_dir_t text_callback(const BoxType * b, void *cl)
+{
+	LayerType *layer = cl;
+	TextType *text = (TextType *) b;
+	int min_silk_line;
+
+	if (TEST_FLAG(PCB_FLAG_SELECTED, text))
+		gui->set_color(Output.fgGC, layer->SelectedColor);
+	else
+		gui->set_color(Output.fgGC, layer->Color);
+	if (layer == &PCB->Data->SILKLAYER || layer == &PCB->Data->BACKSILKLAYER)
+		min_silk_line = PCB->minSlk;
+	else
+		min_silk_line = PCB->minWid;
+	DrawTextLowLevel(text, min_silk_line);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+void DrawLayer(LayerTypePtr Layer, const BoxType * screen)
+{
+	struct poly_info info;
+
+	info.drawn_area = screen;
+	info.layer = Layer;
+
+	/* print the non-clearing polys */
+	r_search(Layer->polygon_tree, screen, NULL, poly_callback, &info, NULL);
+
+	if (conf_core.editor.check_planes)
+		return;
+
+	/* draw all visible lines this layer */
+	r_search(Layer->line_tree, screen, NULL, line_callback, Layer, NULL);
+
+	/* draw the layer arcs on screen */
+	r_search(Layer->arc_tree, screen, NULL, arc_callback, Layer, NULL);
+
+	/* draw the layer text on screen */
+	r_search(Layer->text_tree, screen, NULL, text_callback, Layer, NULL);
+
+	/* We should check for gui->gui here, but it's kinda cool seeing the
+	   auto-outline magically disappear when you first add something to
+	   the "outline" layer.  */
+	if (IsLayerEmpty(Layer)
+			&& (strcmp(Layer->Name, "outline") == 0 || strcmp(Layer->Name, "route") == 0)) {
+		gui->set_color(Output.fgGC, Layer->Color);
+		gui->set_line_width(Output.fgGC, PCB->minWid);
+		gui->draw_rect(Output.fgGC, 0, 0, PCB->MaxWidth, PCB->MaxHeight);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draws one layer group.  If the exporter is not a GUI,
+ * also draws the pins / pads / vias in this layer group.
+ */
+static void DrawLayerGroup(int group, const BoxType * drawn_area)
+{
+	int i, rv = 1;
+	int layernum;
+	LayerTypePtr Layer;
+	int n_entries = PCB->LayerGroups.Number[group];
+	pcb_cardinal_t *layers = PCB->LayerGroups.Entries[group];
+
+	for (i = n_entries - 1; i >= 0; i--) {
+		layernum = layers[i];
+		Layer = PCB->Data->Layer + layers[i];
+		if (strcmp(Layer->Name, "outline") == 0 || strcmp(Layer->Name, "route") == 0)
+			rv = 0;
+		if (layernum < max_copper_layer && Layer->On)
+			DrawLayer(Layer, drawn_area);
+	}
+	if (n_entries > 1)
+		rv = 1;
+
+	if (rv && !gui->gui)
+		DrawPPV(group, drawn_area);
+}
+
+static void GatherPVName(PinTypePtr Ptr)
+{
+	BoxType box;
+	pcb_bool vert = TEST_FLAG(PCB_FLAG_EDGE2, Ptr);
+
+	if (vert) {
+		box.X1 = Ptr->X - Ptr->Thickness / 2 + conf_core.appearance.pinout.text_offset_y;
+		box.Y1 = Ptr->Y - Ptr->DrillingHole / 2 - conf_core.appearance.pinout.text_offset_x;
+	}
+	else {
+		box.X1 = Ptr->X + Ptr->DrillingHole / 2 + conf_core.appearance.pinout.text_offset_x;
+		box.Y1 = Ptr->Y - Ptr->Thickness / 2 + conf_core.appearance.pinout.text_offset_y;
+	}
+
+	if (vert) {
+		box.X2 = box.X1;
+		box.Y2 = box.Y1;
+	}
+	else {
+		box.X2 = box.X1;
+		box.Y2 = box.Y1;
+	}
+	AddPart(&box);
+}
+
+static void GatherPadName(PadTypePtr Pad)
+{
+	BoxType box;
+	pcb_bool vert;
+
+	/* should text be vertical ? */
+	vert = (Pad->Point1.X == Pad->Point2.X);
+
+	if (vert) {
+		box.X1 = Pad->Point1.X - Pad->Thickness / 2;
+		box.Y1 = MAX(Pad->Point1.Y, Pad->Point2.Y) + Pad->Thickness / 2;
+		box.X1 += conf_core.appearance.pinout.text_offset_y;
+		box.Y1 -= conf_core.appearance.pinout.text_offset_x;
+		box.X2 = box.X1;
+		box.Y2 = box.Y1;
+	}
+	else {
+		box.X1 = MIN(Pad->Point1.X, Pad->Point2.X) - Pad->Thickness / 2;
+		box.Y1 = Pad->Point1.Y - Pad->Thickness / 2;
+		box.X1 += conf_core.appearance.pinout.text_offset_x;
+		box.Y1 += conf_core.appearance.pinout.text_offset_y;
+		box.X2 = box.X1;
+		box.Y2 = box.Y1;
+	}
+
+	AddPart(&box);
+	return;
+}
+
+/* ---------------------------------------------------------------------------
+ * lowlevel drawing routine for text objects
+ */
+void DrawTextLowLevel(TextTypePtr Text, Coord min_line_width)
+{
+	Coord x = 0;
+	unsigned char *string = (unsigned char *) Text->TextString;
+	pcb_cardinal_t n;
+	FontTypePtr font = &PCB->Font;
+
+	while (string && *string) {
+		/* draw lines if symbol is valid and data is present */
+		if (*string <= MAX_FONTPOSITION && font->Symbol[*string].Valid) {
+			LineTypePtr line = font->Symbol[*string].Line;
+			LineType newline;
+
+			for (n = font->Symbol[*string].LineN; n; n--, line++) {
+				/* create one line, scale, move, rotate and swap it */
+				newline = *line;
+				newline.Point1.X = PCB_SCALE_TEXT(newline.Point1.X + x, Text->Scale);
+				newline.Point1.Y = PCB_SCALE_TEXT(newline.Point1.Y, Text->Scale);
+				newline.Point2.X = PCB_SCALE_TEXT(newline.Point2.X + x, Text->Scale);
+				newline.Point2.Y = PCB_SCALE_TEXT(newline.Point2.Y, Text->Scale);
+				newline.Thickness = PCB_SCALE_TEXT(newline.Thickness, Text->Scale / 2);
+				if (newline.Thickness < min_line_width)
+					newline.Thickness = min_line_width;
+
+				RotateLineLowLevel(&newline, 0, 0, Text->Direction);
+
+				/* the labels of SMD objects on the bottom
+				 * side haven't been swapped yet, only their offset
+				 */
+				if (TEST_FLAG(PCB_FLAG_ONSOLDER, Text)) {
+					newline.Point1.X = SWAP_SIGN_X(newline.Point1.X);
+					newline.Point1.Y = SWAP_SIGN_Y(newline.Point1.Y);
+					newline.Point2.X = SWAP_SIGN_X(newline.Point2.X);
+					newline.Point2.Y = SWAP_SIGN_Y(newline.Point2.Y);
+				}
+				/* add offset and draw line */
+				newline.Point1.X += Text->X;
+				newline.Point1.Y += Text->Y;
+				newline.Point2.X += Text->X;
+				newline.Point2.Y += Text->Y;
+				_draw_line(&newline);
+			}
+
+			/* move on to next cursor position */
+			x += (font->Symbol[*string].Width + font->Symbol[*string].Delta);
+		}
+		else {
+			/* the default symbol is a filled box */
+			BoxType defaultsymbol = PCB->Font.DefaultSymbol;
+			Coord size = (defaultsymbol.X2 - defaultsymbol.X1) * 6 / 5;
+
+			defaultsymbol.X1 = PCB_SCALE_TEXT(defaultsymbol.X1 + x, Text->Scale);
+			defaultsymbol.Y1 = PCB_SCALE_TEXT(defaultsymbol.Y1, Text->Scale);
+			defaultsymbol.X2 = PCB_SCALE_TEXT(defaultsymbol.X2 + x, Text->Scale);
+			defaultsymbol.Y2 = PCB_SCALE_TEXT(defaultsymbol.Y2, Text->Scale);
+
+			RotateBoxLowLevel(&defaultsymbol, 0, 0, Text->Direction);
+
+			/* add offset and draw box */
+			defaultsymbol.X1 += Text->X;
+			defaultsymbol.Y1 += Text->Y;
+			defaultsymbol.X2 += Text->X;
+			defaultsymbol.Y2 += Text->Y;
+			gui->fill_rect(Output.fgGC, defaultsymbol.X1, defaultsymbol.Y1, defaultsymbol.X2, defaultsymbol.Y2);
+
+			/* move on to next cursor position */
+			x += size;
+		}
+		string++;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draw a via object
+ */
+void DrawVia(PinTypePtr Via)
+{
+	AddPart(Via);
+	if (!TEST_FLAG(PCB_FLAG_HOLE, Via) && TEST_FLAG(PCB_FLAG_DISPLAYNAME, Via))
+		DrawViaName(Via);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the name of a via
+ */
+void DrawViaName(PinTypePtr Via)
+{
+	GatherPVName(Via);
+}
+
+/* ---------------------------------------------------------------------------
+ * draw a pin object
+ */
+void DrawPin(PinTypePtr Pin)
+{
+	AddPart(Pin);
+	if ((!TEST_FLAG(PCB_FLAG_HOLE, Pin) && TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pin))
+			|| doing_pinout)
+		DrawPinName(Pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the name of a pin
+ */
+void DrawPinName(PinTypePtr Pin)
+{
+	GatherPVName(Pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * draw a pad object
+ */
+void DrawPad(PadTypePtr Pad)
+{
+	AddPart(Pad);
+	if (doing_pinout || TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pad))
+		DrawPadName(Pad);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the name of a pad
+ */
+void DrawPadName(PadTypePtr Pad)
+{
+	GatherPadName(Pad);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws a line on a layer
+ */
+void DrawLine(LayerTypePtr Layer, LineTypePtr Line)
+{
+	AddPart(Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws a ratline
+ */
+void DrawRat(RatTypePtr Rat)
+{
+	if (conf_core.appearance.rat_thickness < 20)
+		Rat->Thickness = pixel_slop * conf_core.appearance.rat_thickness;
+	/* rats.c set PCB_FLAG_VIA if this rat goes to a containing poly: draw a donut */
+	if (TEST_FLAG(PCB_FLAG_VIA, Rat)) {
+		Coord w = Rat->Thickness;
+
+		BoxType b;
+
+		b.X1 = Rat->Point1.X - w * 2 - w / 2;
+		b.X2 = Rat->Point1.X + w * 2 + w / 2;
+		b.Y1 = Rat->Point1.Y - w * 2 - w / 2;
+		b.Y2 = Rat->Point1.Y + w * 2 + w / 2;
+		AddPart(&b);
+	}
+	else
+		DrawLine(NULL, (LineType *) Rat);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws an arc on a layer
+ */
+void DrawArc(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	AddPart(Arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws a text on a layer
+ */
+void DrawText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	AddPart(Text);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * draws a polygon on a layer
+ */
+void DrawPolygon(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	AddPart(Polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws an element
+ */
+void DrawElement(ElementTypePtr Element)
+{
+	DrawElementPackage(Element);
+	DrawElementName(Element);
+	DrawElementPinsAndPads(Element);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the name of an element
+ */
+void DrawElementName(ElementTypePtr Element)
+{
+	if (TEST_FLAG(PCB_FLAG_HIDENAME, Element))
+		return;
+	DrawText(NULL, &ELEMENT_TEXT(PCB, Element));
+}
+
+/* ---------------------------------------------------------------------------
+ * draws the package of an element
+ */
+void DrawElementPackage(ElementTypePtr Element)
+{
+	ELEMENTLINE_LOOP(Element);
+	{
+		DrawLine(NULL, line);
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		DrawArc(NULL, arc);
+	}
+	END_LOOP;
+}
+
+/* ---------------------------------------------------------------------------
+ * draw pins of an element
+ */
+void DrawElementPinsAndPads(ElementTypePtr Element)
+{
+	PAD_LOOP(Element);
+	{
+		if (doing_pinout || doing_assy || FRONT(pad) || PCB->InvisibleObjectsOn)
+			DrawPad(pad);
+	}
+	END_LOOP;
+	PIN_LOOP(Element);
+	{
+		DrawPin(pin);
+	}
+	END_LOOP;
+}
+
+void EraseFlags(FlagType * f)
+{
+	unknown_flag_t *u, *next;
+	for (u = f->unknowns; u != NULL; u = next) {
+		free(u->str);
+		next = u->next;
+		free(u);
+	}
+	f->unknowns = NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * erase a via
+ */
+void EraseVia(PinTypePtr Via)
+{
+	AddPart(Via);
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Via))
+		EraseViaName(Via);
+	EraseFlags(&Via->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erase a ratline
+ */
+void EraseRat(RatTypePtr Rat)
+{
+	if (TEST_FLAG(PCB_FLAG_VIA, Rat)) {
+		Coord w = Rat->Thickness;
+
+		BoxType b;
+
+		b.X1 = Rat->Point1.X - w * 2 - w / 2;
+		b.X2 = Rat->Point1.X + w * 2 + w / 2;
+		b.Y1 = Rat->Point1.Y - w * 2 - w / 2;
+		b.Y2 = Rat->Point1.Y + w * 2 + w / 2;
+		AddPart(&b);
+	}
+	else
+		EraseLine((LineType *) Rat);
+	EraseFlags(&Rat->Flags);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * erase a via name
+ */
+void EraseViaName(PinTypePtr Via)
+{
+	GatherPVName(Via);
+}
+
+/* ---------------------------------------------------------------------------
+ * erase a pad object
+ */
+void ErasePad(PadTypePtr Pad)
+{
+	AddPart(Pad);
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pad))
+		ErasePadName(Pad);
+	EraseFlags(&Pad->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erase a pad name
+ */
+void ErasePadName(PadTypePtr Pad)
+{
+	GatherPadName(Pad);
+}
+
+/* ---------------------------------------------------------------------------
+ * erase a pin object
+ */
+void ErasePin(PinTypePtr Pin)
+{
+	AddPart(Pin);
+	if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, Pin))
+		ErasePinName(Pin);
+	EraseFlags(&Pin->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erase a pin name
+ */
+void ErasePinName(PinTypePtr Pin)
+{
+	GatherPVName(Pin);
+}
+
+/* ---------------------------------------------------------------------------
+ * erases a line on a layer
+ */
+void EraseLine(LineTypePtr Line)
+{
+	AddPart(Line);
+	EraseFlags(&Line->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erases an arc on a layer
+ */
+void EraseArc(ArcTypePtr Arc)
+{
+	if (!Arc->Thickness)
+		return;
+	AddPart(Arc);
+	EraseFlags(&Arc->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erases a text on a layer
+ */
+void EraseText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	r_delete_entry(Layer->text_tree, (BoxTypePtr)Text);
+	AddPart(Text);
+}
+
+/* ---------------------------------------------------------------------------
+ * erases a polygon on a layer
+ */
+void ErasePolygon(PolygonTypePtr Polygon)
+{
+	AddPart(Polygon);
+	EraseFlags(&Polygon->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erases an element
+ */
+void EraseElement(ElementTypePtr Element)
+{
+	ELEMENTLINE_LOOP(Element);
+	{
+		EraseLine(line);
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		EraseArc(arc);
+	}
+	END_LOOP;
+	EraseElementName(Element);
+	EraseElementPinsAndPads(Element);
+	EraseFlags(&Element->Flags);
+}
+
+/* ---------------------------------------------------------------------------
+ * erases all pins and pads of an element
+ */
+void EraseElementPinsAndPads(ElementTypePtr Element)
+{
+	PIN_LOOP(Element);
+	{
+		ErasePin(pin);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		ErasePad(pad);
+	}
+	END_LOOP;
+}
+
+/* ---------------------------------------------------------------------------
+ * erases the name of an element
+ */
+void EraseElementName(ElementTypePtr Element)
+{
+	if (TEST_FLAG(PCB_FLAG_HIDENAME, Element)) {
+		return;
+	}
+	DrawText(NULL, &ELEMENT_TEXT(PCB, Element));
+}
+
+
+void EraseObject(int type, void *lptr, void *ptr)
+{
+	switch (type) {
+	case PCB_TYPE_VIA:
+	case PCB_TYPE_PIN:
+		ErasePin((PinTypePtr) ptr);
+		break;
+	case PCB_TYPE_TEXT:
+	case PCB_TYPE_ELEMENT_NAME:
+		EraseText((LayerTypePtr) lptr, (TextTypePtr) ptr);
+		break;
+	case PCB_TYPE_POLYGON:
+		ErasePolygon((PolygonTypePtr) ptr);
+		break;
+	case PCB_TYPE_ELEMENT:
+		EraseElement((ElementTypePtr) ptr);
+		break;
+	case PCB_TYPE_LINE:
+	case PCB_TYPE_ELEMENT_LINE:
+	case PCB_TYPE_RATLINE:
+		EraseLine((LineTypePtr) ptr);
+		break;
+	case PCB_TYPE_PAD:
+		ErasePad((PadTypePtr) ptr);
+		break;
+	case PCB_TYPE_ARC:
+	case PCB_TYPE_ELEMENT_ARC:
+		EraseArc((ArcTypePtr) ptr);
+		break;
+	default:
+		Message(PCB_MSG_DEFAULT, "hace: Internal ERROR, trying to erase an unknown type\n");
+	}
+}
+
+
+void DrawObject(int type, void *ptr1, void *ptr2)
+{
+	switch (type) {
+	case PCB_TYPE_VIA:
+		if (PCB->ViaOn)
+			DrawVia((PinTypePtr) ptr2);
+		break;
+	case PCB_TYPE_LINE:
+		if (((LayerTypePtr) ptr1)->On)
+			DrawLine((LayerTypePtr) ptr1, (LineTypePtr) ptr2);
+		break;
+	case PCB_TYPE_ARC:
+		if (((LayerTypePtr) ptr1)->On)
+			DrawArc((LayerTypePtr) ptr1, (ArcTypePtr) ptr2);
+		break;
+	case PCB_TYPE_TEXT:
+		if (((LayerTypePtr) ptr1)->On)
+			DrawText((LayerTypePtr) ptr1, (TextTypePtr) ptr2);
+		break;
+	case PCB_TYPE_POLYGON:
+		if (((LayerTypePtr) ptr1)->On)
+			DrawPolygon((LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
+		break;
+	case PCB_TYPE_ELEMENT:
+		if (PCB->ElementOn && (FRONT((ElementTypePtr) ptr2) || PCB->InvisibleObjectsOn))
+			DrawElement((ElementTypePtr) ptr2);
+		break;
+	case PCB_TYPE_RATLINE:
+		if (PCB->RatOn)
+			DrawRat((RatTypePtr) ptr2);
+		break;
+	case PCB_TYPE_PIN:
+		if (PCB->PinOn)
+			DrawPin((PinTypePtr) ptr2);
+		break;
+	case PCB_TYPE_PAD:
+		if (PCB->PinOn)
+			DrawPad((PadTypePtr) ptr2);
+		break;
+	case PCB_TYPE_ELEMENT_NAME:
+		if (PCB->ElementOn && (FRONT((ElementTypePtr) ptr2) || PCB->InvisibleObjectsOn))
+			DrawElementName((ElementTypePtr) ptr1);
+		break;
+	}
+}
+
+static void draw_element(ElementTypePtr element)
+{
+	draw_element_package(element);
+	draw_element_name(element);
+	draw_element_pins_and_pads(element);
+}
+
+/* ---------------------------------------------------------------------------
+ * HID drawing callback.
+ */
+
+void hid_expose_callback(HID * hid, BoxType * region, void *item)
+{
+	HID *old_gui = gui;
+
+	gui = hid;
+	Output.fgGC = gui->make_gc();
+	Output.bgGC = gui->make_gc();
+	Output.pmGC = gui->make_gc();
+
+	hid->set_color(Output.pmGC, "erase");
+	hid->set_color(Output.bgGC, "drill");
+
+	if (item) {
+		doing_pinout = pcb_true;
+		draw_element((ElementType *) item);
+		doing_pinout = pcb_false;
+	}
+	else
+		DrawEverything(region);
+
+	gui->destroy_gc(Output.fgGC);
+	gui->destroy_gc(Output.bgGC);
+	gui->destroy_gc(Output.pmGC);
+	gui = old_gui;
+}
diff --git a/src/draw.h b/src/draw.h
new file mode 100644
index 0000000..53c9964
--- /dev/null
+++ b/src/draw.h
@@ -0,0 +1,83 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2004 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for drawing routines */
+
+#ifndef	PCB_DRAW_H
+#define	PCB_DRAW_H
+
+#include "global.h"
+
+/* Temporarily inhibid drawing if this is non-zero. A function that calls a
+   lot of other functions that would call Draw() a lot in turn may increase
+   this value before the calls, then decrease it at the end and call Draw().
+   This makes sure the whole block is redrawn only once at the end. */
+extern pcb_cardinal_t pcb_draw_inhibit;
+
+#define pcb_draw_inhibit_inc() pcb_draw_inhibit++
+#define pcb_draw_inhibit_dec() \
+do { \
+	if (pcb_draw_inhibit > 0) \
+		pcb_draw_inhibit--; \
+} while(0) \
+
+void Draw(void);
+void Redraw(void);
+void DrawVia(PinTypePtr);
+void DrawRat(RatTypePtr);
+void DrawViaName(PinTypePtr);
+void DrawPin(PinTypePtr);
+void DrawPinName(PinTypePtr);
+void DrawPad(PadTypePtr);
+void DrawPadName(PadTypePtr);
+void DrawLine(LayerTypePtr, LineTypePtr);
+void DrawArc(LayerTypePtr, ArcTypePtr);
+void DrawText(LayerTypePtr, TextTypePtr);
+void DrawTextLowLevel(TextTypePtr, Coord);
+void DrawPolygon(LayerTypePtr, PolygonTypePtr);
+void DrawElement(ElementTypePtr);
+void DrawElementName(ElementTypePtr);
+void DrawElementPackage(ElementTypePtr);
+void DrawElementPinsAndPads(ElementTypePtr);
+void DrawObject(int, void *, void *);
+void DrawLayer(LayerTypePtr, const BoxType *);
+void EraseVia(PinTypePtr);
+void EraseRat(RatTypePtr);
+void EraseViaName(PinTypePtr);
+void ErasePad(PadTypePtr);
+void ErasePadName(PadTypePtr);
+void ErasePin(PinTypePtr);
+void ErasePinName(PinTypePtr);
+void EraseLine(LineTypePtr);
+void EraseArc(ArcTypePtr);
+void EraseText(LayerTypePtr, TextTypePtr);
+void ErasePolygon(PolygonTypePtr);
+void EraseElement(ElementTypePtr);
+void EraseElementPinsAndPads(ElementTypePtr);
+void EraseElementName(ElementTypePtr);
+void EraseObject(int, void *, void *);
+
+#endif
diff --git a/src/draw_fab.c b/src/draw_fab.c
new file mode 100644
index 0000000..5152279
--- /dev/null
+++ b/src/draw_fab.c
@@ -0,0 +1,297 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2003 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* Change History:
+ * 10/11/96 11:37 AJF Added support for a Text() driver function.
+ * This was done out of a pressing need to force text to be printed on the
+ * silkscreen layer. Perhaps the design is not the best.
+ */
+
+
+/* printing routines
+ */
+#include "config.h"
+
+#include <time.h>
+#include <setjmp.h>
+
+
+#include "data.h"
+#include "draw.h"
+#include "drill.h"
+#include "misc.h"
+#include "draw_fab.h"
+#include "polygon.h"
+#include "layer.h"
+
+/* ---------------------------------------------------------------------------
+ * prints a FAB drawing.
+ */
+
+#define TEXT_SIZE	PCB_MIL_TO_COORD(150)
+#define TEXT_LINE	PCB_MIL_TO_COORD(150)
+#define DRILL_MARK_SIZE	PCB_MIL_TO_COORD(16)
+#define FAB_LINE_W      PCB_MIL_TO_COORD(8)
+
+static void fab_line(hidGC gc, int x1, int y1, int x2, int y2)
+{
+	gui->draw_line(gc, x1, y1, x2, y2);
+}
+
+static void fab_circle(hidGC gc, int x, int y, int r)
+{
+	gui->draw_arc(gc, x, y, r, r, 0, 180);
+	gui->draw_arc(gc, x, y, r, r, 180, 180);
+}
+
+/* align is 0=left, 1=center, 2=right, add 8 for underline */
+static void text_at(hidGC gc, int x, int y, int align, const char *fmt, ...)
+{
+	char tmp[512];
+	int w = 0, i;
+	TextType t;
+	va_list a;
+	FontTypePtr font = &PCB->Font;
+	va_start(a, fmt);
+	vsprintf(tmp, fmt, a);
+	va_end(a);
+	t.Direction = 0;
+	t.TextString = tmp;
+	t.Scale = PCB_COORD_TO_MIL(TEXT_SIZE);	/* pcnt of 100mil base height */
+	t.Flags = NoFlags();
+	t.X = x;
+	t.Y = y;
+	for (i = 0; tmp[i]; i++)
+		w += (font->Symbol[(int) tmp[i]].Width + font->Symbol[(int) tmp[i]].Delta);
+	w = PCB_SCALE_TEXT(w, t.Scale);
+	t.X -= w * (align & 3) / 2;
+	if (t.X < 0)
+		t.X = 0;
+	DrawTextLowLevel(&t, 0);
+	if (align & 8)
+		fab_line(gc, t.X,
+						 t.Y + PCB_SCALE_TEXT(font->MaxHeight, t.Scale) + PCB_MIL_TO_COORD(10),
+						 t.X + w, t.Y + PCB_SCALE_TEXT(font->MaxHeight, t.Scale) + PCB_MIL_TO_COORD(10));
+}
+
+/* Y, +, X, circle, square */
+static void drill_sym(hidGC gc, int idx, int x, int y)
+{
+	int type = idx % 5;
+	int size = idx / 5;
+	int s2 = (size + 1) * DRILL_MARK_SIZE;
+	int i;
+	switch (type) {
+	case 0:											/* Y */ ;
+		fab_line(gc, x, y, x, y + s2);
+		fab_line(gc, x, y, x + s2 * 13 / 15, y - s2 / 2);
+		fab_line(gc, x, y, x - s2 * 13 / 15, y - s2 / 2);
+		for (i = 1; i <= size; i++)
+			fab_circle(gc, x, y, i * DRILL_MARK_SIZE);
+		break;
+	case 1:											/* + */
+		;
+		fab_line(gc, x, y - s2, x, y + s2);
+		fab_line(gc, x - s2, y, x + s2, y);
+		for (i = 1; i <= size; i++) {
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE);
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x - i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+			fab_line(gc, x + i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+		}
+		break;
+	case 2:											/* X */ ;
+		fab_line(gc, x - s2 * 3 / 4, y - s2 * 3 / 4, x + s2 * 3 / 4, y + s2 * 3 / 4);
+		fab_line(gc, x - s2 * 3 / 4, y + s2 * 3 / 4, x + s2 * 3 / 4, y - s2 * 3 / 4);
+		for (i = 1; i <= size; i++) {
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE);
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x - i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+			fab_line(gc, x + i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+		}
+		break;
+	case 3:											/* circle */ ;
+		for (i = 0; i <= size; i++)
+			fab_circle(gc, x, y, (i + 1) * DRILL_MARK_SIZE - DRILL_MARK_SIZE / 2);
+		break;
+	case 4:											/* square */
+		for (i = 1; i <= size + 1; i++) {
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE);
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x - i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+			fab_line(gc, x - i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+			fab_line(gc, x + i * DRILL_MARK_SIZE, y - i * DRILL_MARK_SIZE, x + i * DRILL_MARK_SIZE, y + i * DRILL_MARK_SIZE);
+		}
+		break;
+	}
+}
+
+static int count_drill_lines(DrillInfoTypePtr AllDrills)
+{
+	int n, ds = 0;
+	for (n = AllDrills->DrillN - 1; n >= 0; n--) {
+		DrillTypePtr drill = &(AllDrills->Drill[n]);
+		if (drill->PinCount + drill->ViaCount > drill->UnplatedCount)
+			ds++;
+		if (drill->UnplatedCount)
+			ds++;
+	}
+	return ds;
+}
+
+
+int DrawFab_overhang(void)
+{
+	DrillInfoTypePtr AllDrills = GetDrillInfo(PCB->Data);
+	int ds = count_drill_lines(AllDrills);
+	if (ds < 4)
+		ds = 4;
+	return (ds + 2) * TEXT_LINE;
+}
+
+void DrawFab(hidGC gc)
+{
+	DrillInfoTypePtr AllDrills;
+	int i, n, yoff, total_drills = 0, ds = 0;
+	time_t currenttime;
+	char utcTime[64];
+	AllDrills = GetDrillInfo(PCB->Data);
+	RoundDrillInfo(AllDrills, PCB_MIL_TO_COORD(1));
+	yoff = -TEXT_LINE;
+
+	/* count how many drill description lines will be needed */
+	ds = count_drill_lines(AllDrills);
+
+	/*
+	 * When we only have a few drill sizes we need to make sure the
+	 * drill table header doesn't fall on top of the board info
+	 * section.
+	 */
+	if (ds < 4) {
+		yoff -= (4 - ds) * TEXT_LINE;
+	}
+
+	gui->set_line_width(gc, FAB_LINE_W);
+
+	for (n = AllDrills->DrillN - 1; n >= 0; n--) {
+		int plated_sym = -1, unplated_sym = -1;
+		DrillTypePtr drill = &(AllDrills->Drill[n]);
+		if (drill->PinCount + drill->ViaCount > drill->UnplatedCount)
+			plated_sym = --ds;
+		if (drill->UnplatedCount)
+			unplated_sym = --ds;
+		gui->set_color(gc, PCB->PinColor);
+		for (i = 0; i < drill->PinN; i++)
+			drill_sym(gc, TEST_FLAG(PCB_FLAG_HOLE, drill->Pin[i]) ? unplated_sym : plated_sym, drill->Pin[i]->X, drill->Pin[i]->Y);
+		if (plated_sym != -1) {
+			drill_sym(gc, plated_sym, TEXT_SIZE, yoff + TEXT_SIZE / 4);
+			text_at(gc, PCB_MIL_TO_COORD(1350), yoff, PCB_MIL_TO_COORD(2), "YES");
+			text_at(gc, PCB_MIL_TO_COORD(980), yoff, PCB_MIL_TO_COORD(2), "%d", drill->PinCount + drill->ViaCount - drill->UnplatedCount);
+
+			if (unplated_sym != -1)
+				yoff -= TEXT_LINE;
+		}
+		if (unplated_sym != -1) {
+			drill_sym(gc, unplated_sym, TEXT_SIZE, yoff + TEXT_SIZE / 4);
+			text_at(gc, PCB_MIL_TO_COORD(1400), yoff, PCB_MIL_TO_COORD(2), "NO");
+			text_at(gc, PCB_MIL_TO_COORD(980), yoff, PCB_MIL_TO_COORD(2), "%d", drill->UnplatedCount);
+		}
+		gui->set_color(gc, PCB->ElementColor);
+		text_at(gc, PCB_MIL_TO_COORD(450), yoff, PCB_MIL_TO_COORD(2), "%0.3f", PCB_COORD_TO_INCH(drill->DrillSize) + 0.0004);
+		if (plated_sym != -1 && unplated_sym != -1)
+			text_at(gc, PCB_MIL_TO_COORD(450), yoff + TEXT_LINE, PCB_MIL_TO_COORD(2), "%0.3f", PCB_COORD_TO_INCH(drill->DrillSize) + 0.0004);
+		yoff -= TEXT_LINE;
+		total_drills += drill->PinCount;
+		total_drills += drill->ViaCount;
+	}
+
+	gui->set_color(gc, PCB->ElementColor);
+	text_at(gc, 0, yoff, PCB_MIL_TO_COORD(9), "Symbol");
+	text_at(gc, PCB_MIL_TO_COORD(410), yoff, PCB_MIL_TO_COORD(9), "Diam. (Inch)");
+	text_at(gc, PCB_MIL_TO_COORD(950), yoff, PCB_MIL_TO_COORD(9), "Count");
+	text_at(gc, PCB_MIL_TO_COORD(1300), yoff, PCB_MIL_TO_COORD(9), "Plated?");
+	yoff -= TEXT_LINE;
+	text_at(gc, 0, yoff, 0,
+					"There are %d different drill sizes used in this layout, %d holes total", AllDrills->DrillN, total_drills);
+	/* Create a portable timestamp. */
+	currenttime = time(NULL);
+	{
+		/* avoid gcc complaints */
+		const char *fmt = "%c UTC";
+		strftime(utcTime, sizeof utcTime, fmt, gmtime(&currenttime));
+	}
+	yoff = -TEXT_LINE;
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *l = LAYER_PTR(i);
+		if (l->Name && (linelist_length(&l->Line) || arclist_length(&l->Arc))) {
+			if (strcmp("route", l->Name) == 0)
+				break;
+			if (strcmp("outline", l->Name) == 0)
+				break;
+		}
+	}
+	if (i == max_copper_layer) {
+		gui->set_line_width(gc, PCB_MIL_TO_COORD(10));
+		gui->draw_line(gc, 0, 0, PCB->MaxWidth, 0);
+		gui->draw_line(gc, 0, 0, 0, PCB->MaxHeight);
+		gui->draw_line(gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight);
+		gui->draw_line(gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
+		/*FPrintOutline (); */
+		gui->set_line_width(gc, FAB_LINE_W);
+		text_at(gc, PCB_MIL_TO_COORD(2000), yoff, 0,
+						"Maximum Dimensions: %f mils wide, %f mils high", PCB_COORD_TO_MIL(PCB->MaxWidth), PCB_COORD_TO_MIL(PCB->MaxHeight));
+		text_at(gc, PCB->MaxWidth / 2, PCB->MaxHeight + PCB_MIL_TO_COORD(20), 1,
+						"Board outline is the centerline of this %f mil"
+						" rectangle - 0,0 to %f,%f mils",
+						PCB_COORD_TO_MIL(FAB_LINE_W), PCB_COORD_TO_MIL(PCB->MaxWidth), PCB_COORD_TO_MIL(PCB->MaxHeight));
+	}
+	else {
+		LayerTypePtr layer = LAYER_PTR(i);
+		gui->set_line_width(gc, PCB_MIL_TO_COORD(10));
+		LINE_LOOP(layer);
+		{
+			gui->draw_line(gc, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
+		}
+		END_LOOP;
+		ARC_LOOP(layer);
+		{
+			gui->draw_arc(gc, arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta);
+		}
+		END_LOOP;
+		TEXT_LOOP(layer);
+		{
+			DrawTextLowLevel(text, 0);
+		}
+		END_LOOP;
+		gui->set_line_width(gc, FAB_LINE_W);
+		text_at(gc, PCB->MaxWidth / 2, PCB->MaxHeight + PCB_MIL_TO_COORD(20), 1, "Board outline is the centerline of this path");
+	}
+	yoff -= TEXT_LINE;
+	text_at(gc, PCB_MIL_TO_COORD(2000), yoff, 0, "Date: %s", utcTime);
+	yoff -= TEXT_LINE;
+	text_at(gc, PCB_MIL_TO_COORD(2000), yoff, 0, "Author: %s", pcb_author());
+	yoff -= TEXT_LINE;
+	text_at(gc, PCB_MIL_TO_COORD(2000), yoff, 0, "Title: %s - Fabrication Drawing", UNKNOWN(PCB->Name));
+}
diff --git a/src/draw_fab.h b/src/draw_fab.h
new file mode 100644
index 0000000..957f5c7
--- /dev/null
+++ b/src/draw_fab.h
@@ -0,0 +1,39 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for printing routines */
+
+#ifndef	PCB_PRINT_H
+#define	PCB_PRINT_H
+
+#include <stdio.h>
+
+#include "global.h"
+
+int DrawFab_overhang(void);
+void DrawFab(hidGC gc);
+
+#endif
diff --git a/src/drill.c b/src/drill.c
new file mode 100644
index 0000000..bc910ef
--- /dev/null
+++ b/src/drill.c
@@ -0,0 +1,243 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This module, drill.c, was written and is Copyright (C) 1997 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+#include "config.h"
+
+#include "mymem.h"
+
+/*
+ * some local prototypes
+ */
+static void FillDrill(DrillTypePtr, ElementTypePtr, PinTypePtr);
+static void InitializeDrill(DrillTypePtr, PinTypePtr, ElementTypePtr);
+
+
+static void FillDrill(DrillTypePtr Drill, ElementTypePtr Element, PinTypePtr Pin)
+{
+	pcb_cardinal_t n;
+	ElementTypeHandle ptr;
+	PinTypeHandle pin;
+
+	pin = GetDrillPinMemory(Drill);
+	*pin = Pin;
+	if (Element) {
+		Drill->PinCount++;
+		for (n = Drill->ElementN - 1; n != -1; n--)
+			if (Drill->Element[n] == Element)
+				break;
+		if (n == -1) {
+			ptr = GetDrillElementMemory(Drill);
+			*ptr = Element;
+		}
+	}
+	else
+		Drill->ViaCount++;
+	if (TEST_FLAG(PCB_FLAG_HOLE, Pin))
+		Drill->UnplatedCount++;
+}
+
+static void InitializeDrill(DrillTypePtr drill, PinTypePtr pin, ElementTypePtr element)
+{
+	void *ptr;
+
+	drill->DrillSize = pin->DrillingHole;
+	drill->ElementN = 0;
+	drill->ViaCount = 0;
+	drill->PinCount = 0;
+	drill->UnplatedCount = 0;
+	drill->ElementMax = 0;
+	drill->Element = NULL;
+	drill->PinN = 0;
+	drill->Pin = NULL;
+	drill->PinMax = 0;
+	ptr = (void *) GetDrillPinMemory(drill);
+	*((PinTypeHandle) ptr) = pin;
+	if (element) {
+		ptr = (void *) GetDrillElementMemory(drill);
+		*((ElementTypeHandle) ptr) = element;
+		drill->PinCount = 1;
+	}
+	else
+		drill->ViaCount = 1;
+	if (TEST_FLAG(PCB_FLAG_HOLE, pin))
+		drill->UnplatedCount = 1;
+}
+
+static int DrillQSort(const void *va, const void *vb)
+{
+	DrillType *a = (DrillType *) va;
+	DrillType *b = (DrillType *) vb;
+	return a->DrillSize - b->DrillSize;
+}
+
+DrillInfoTypePtr GetDrillInfo(DataTypePtr top)
+{
+	DrillInfoTypePtr AllDrills;
+	DrillTypePtr Drill = NULL;
+	DrillType savedrill, swapdrill;
+	pcb_bool DrillFound = pcb_false;
+	pcb_bool NewDrill;
+
+	AllDrills = (DrillInfoTypePtr) calloc(1, sizeof(DrillInfoType));
+	ALLPIN_LOOP(top);
+	{
+		if (!DrillFound) {
+			DrillFound = pcb_true;
+			Drill = GetDrillInfoDrillMemory(AllDrills);
+			InitializeDrill(Drill, pin, element);
+		}
+		else {
+			if (Drill->DrillSize == pin->DrillingHole)
+				FillDrill(Drill, element, pin);
+			else {
+				NewDrill = pcb_false;
+				DRILL_LOOP(AllDrills);
+				{
+					if (drill->DrillSize == pin->DrillingHole) {
+						Drill = drill;
+						FillDrill(Drill, element, pin);
+						break;
+					}
+					else if (drill->DrillSize > pin->DrillingHole) {
+						if (!NewDrill) {
+							NewDrill = pcb_true;
+							InitializeDrill(&swapdrill, pin, element);
+							Drill = GetDrillInfoDrillMemory(AllDrills);
+							Drill->DrillSize = pin->DrillingHole + 1;
+							Drill = drill;
+						}
+						savedrill = *drill;
+						*drill = swapdrill;
+						swapdrill = savedrill;
+					}
+				}
+				END_LOOP;
+				if (AllDrills->Drill[AllDrills->DrillN - 1].DrillSize < pin->DrillingHole) {
+					Drill = GetDrillInfoDrillMemory(AllDrills);
+					InitializeDrill(Drill, pin, element);
+				}
+			}
+		}
+	}
+	ENDALL_LOOP;
+	VIA_LOOP(top);
+	{
+		if (!DrillFound) {
+			DrillFound = pcb_true;
+			Drill = GetDrillInfoDrillMemory(AllDrills);
+			Drill->DrillSize = via->DrillingHole;
+			FillDrill(Drill, NULL, via);
+		}
+		else {
+			if (Drill->DrillSize != via->DrillingHole) {
+				DRILL_LOOP(AllDrills);
+				{
+					if (drill->DrillSize == via->DrillingHole) {
+						Drill = drill;
+						FillDrill(Drill, NULL, via);
+						break;
+					}
+				}
+				END_LOOP;
+				if (Drill->DrillSize != via->DrillingHole) {
+					Drill = GetDrillInfoDrillMemory(AllDrills);
+					Drill->DrillSize = via->DrillingHole;
+					FillDrill(Drill, NULL, via);
+				}
+			}
+			else
+				FillDrill(Drill, NULL, via);
+		}
+	}
+	END_LOOP;
+	qsort(AllDrills->Drill, AllDrills->DrillN, sizeof(DrillType), DrillQSort);
+	return (AllDrills);
+}
+
+#define ROUND(x,n) ((int)(((x)+(n)/2)/(n))*(n))
+
+void RoundDrillInfo(DrillInfoTypePtr d, int roundto)
+{
+	unsigned int i = 0;
+
+	while ((d->DrillN > 0) && (i < d->DrillN - 1)) {
+		int diam1 = ROUND(d->Drill[i].DrillSize, roundto);
+		int diam2 = ROUND(d->Drill[i + 1].DrillSize, roundto);
+
+		if (diam1 == diam2) {
+			int ei, ej;
+
+			d->Drill[i].ElementMax = d->Drill[i].ElementN + d->Drill[i + 1].ElementN;
+			if (d->Drill[i].ElementMax) {
+				d->Drill[i].Element = (ElementTypePtr *) realloc(d->Drill[i].Element, d->Drill[i].ElementMax * sizeof(ElementTypePtr));
+
+				for (ei = 0; ei < d->Drill[i + 1].ElementN; ei++) {
+					for (ej = 0; ej < d->Drill[i].ElementN; ej++)
+						if (d->Drill[i].Element[ej] == d->Drill[i + 1].Element[ei])
+							break;
+					if (ej == d->Drill[i].ElementN)
+						d->Drill[i].Element[d->Drill[i].ElementN++]
+							= d->Drill[i + 1].Element[ei];
+				}
+			}
+			free(d->Drill[i + 1].Element);
+			d->Drill[i + 1].Element = NULL;
+
+			d->Drill[i].PinMax = d->Drill[i].PinN + d->Drill[i + 1].PinN;
+			d->Drill[i].Pin = (PinTypePtr *) realloc(d->Drill[i].Pin, d->Drill[i].PinMax * sizeof(PinTypePtr));
+			memcpy(d->Drill[i].Pin + d->Drill[i].PinN, d->Drill[i + 1].Pin, d->Drill[i + 1].PinN * sizeof(PinTypePtr));
+			d->Drill[i].PinN += d->Drill[i + 1].PinN;
+			free(d->Drill[i + 1].Pin);
+			d->Drill[i + 1].Pin = NULL;
+
+			d->Drill[i].PinCount += d->Drill[i + 1].PinCount;
+			d->Drill[i].ViaCount += d->Drill[i + 1].ViaCount;
+			d->Drill[i].UnplatedCount += d->Drill[i + 1].UnplatedCount;
+
+			d->Drill[i].DrillSize = diam1;
+
+			memmove(d->Drill + i + 1, d->Drill + i + 2, (d->DrillN - i - 2) * sizeof(DrillType));
+			d->DrillN--;
+		}
+		else {
+			d->Drill[i].DrillSize = diam1;
+			i++;
+		}
+	}
+}
+
+void FreeDrillInfo(DrillInfoTypePtr Drills)
+{
+	DRILL_LOOP(Drills);
+	{
+		free(drill->Element);
+		free(drill->Pin);
+	}
+	END_LOOP;
+	free(Drills->Drill);
+	free(Drills);
+}
diff --git a/src/drill.h b/src/drill.h
new file mode 100644
index 0000000..27a5ffe
--- /dev/null
+++ b/src/drill.h
@@ -0,0 +1,31 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This module, drill.h, was written and is Copyright (C) 1997 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+DrillInfoTypePtr GetDrillInfo(DataTypePtr);
+void FreeDrillInfo(DrillInfoTypePtr);
+void RoundDrillInfo(DrillInfoTypePtr, int);
diff --git a/src/error.c b/src/error.c
new file mode 100644
index 0000000..c58deb0
--- /dev/null
+++ b/src/error.c
@@ -0,0 +1,195 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* error and debug functions */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include "global.h"
+
+#include "data.h"
+#include "error.h"
+#include "plug_io.h"
+#include "compat_misc.h"
+#include "pcb-printf.h"
+
+#define utf8_dup_string(a,b) *(a) = pcb_strdup(b)
+
+/* ----------------------------------------------------------------------
+ * some external identifiers
+ */
+
+/* the list is already defined for some OS */
+#if !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__linux__) && !defined(__DragonFly__)
+#ifdef USE_SYS_ERRLIST
+extern char *sys_errlist[];			/* array of error messages */
+#endif
+#endif
+
+
+/* ---------------------------------------------------------------------------
+ * output of message in a dialog window or log window
+ */
+void Message(enum pcb_message_level level, const char *Format, ...)
+{
+	va_list args;
+
+	/* TODO(hzeller): do something useful with the level, e.g. color coding. */
+
+	if (gui != NULL) {
+		va_start(args, Format);
+		gui->logv(level, Format, args);
+		va_end(args);
+	}
+
+	va_start(args, Format);
+	pcb_vfprintf(stderr, Format, args);
+	va_end(args);
+}
+
+void pcb_trace(const char *Format, ...)
+{
+#ifndef NDEBUG
+	va_list args;
+	va_start(args, Format);
+	vfprintf(stderr, Format, args);
+	va_end(args);
+#endif
+}
+
+
+/* ---------------------------------------------------------------------------
+ * print standard 'open error'
+ */
+void OpenErrorMessage(const char *Filename)
+{
+	char *utf8 = NULL;
+
+	utf8_dup_string(&utf8, Filename);
+	Message(PCB_MSG_DEFAULT, _("Can't open file\n" "   '%s'\nfopen() returned: '%s'\n"), utf8, strerror(errno));
+	free(utf8);
+}
+
+/* ---------------------------------------------------------------------------
+ * print standard 'popen error'
+ */
+void PopenErrorMessage(const char *Filename)
+{
+	char *utf8 = NULL;
+
+	utf8_dup_string(&utf8, Filename);
+	Message(PCB_MSG_DEFAULT, _("Can't execute command\n" "   '%s'\npopen() returned: '%s'\n"), utf8, strerror(errno));
+	free(utf8);
+}
+
+/* ---------------------------------------------------------------------------
+ * print standard 'opendir'
+ */
+void OpendirErrorMessage(const char *DirName)
+{
+	char *utf8 = NULL;
+
+	utf8_dup_string(&utf8, DirName);
+	Message(PCB_MSG_DEFAULT, _("Can't scan directory\n" "   '%s'\nopendir() returned: '%s'\n"), utf8, strerror(errno));
+	free(utf8);
+}
+
+/* ---------------------------------------------------------------------------
+ * print standard 'chdir error'
+ */
+void ChdirErrorMessage(const char *DirName)
+{
+	char *utf8 = NULL;
+
+	utf8_dup_string(&utf8, DirName);
+	Message(PCB_MSG_DEFAULT, _("Can't change working directory to\n" "   '%s'\nchdir() returned: '%s'\n"), utf8, strerror(errno));
+	free(utf8);
+}
+
+/* ---------------------------------------------------------------------------
+ * output of fatal error message
+ */
+void MyFatal(const char *Format, ...)
+{
+	va_list args;
+
+	va_start(args, Format);
+
+	/* try to save the layout and do some cleanup */
+	EmergencySave();
+	fprintf(stderr, "pcb-rnd (%i): fatal, ", pcb_getpid());
+	vfprintf(stderr, Format, args);
+	fflush(stderr);
+	va_end(args);
+	exit(1);
+}
+
+/* ---------------------------------------------------------------------------
+ * catches signals which abort the program
+ */
+void CatchSignal(int Signal)
+{
+	const char *s;
+
+	switch (Signal) {
+#ifdef SIGHUP
+	case SIGHUP:
+		s = "SIGHUP";
+		break;
+#endif
+	case SIGINT:
+		s = "SIGINT";
+		break;
+#ifdef SIGQUIT
+	case SIGQUIT:
+		s = "SIGQUIT";
+		break;
+#endif
+	case SIGABRT:
+		s = "SIGABRT";
+		break;
+	case SIGTERM:
+		s = "SIGTERM";
+		break;
+	case SIGSEGV:
+		s = "SIGSEGV";
+		break;
+	default:
+		s = "unknown";
+		break;
+	}
+	MyFatal("aborted by %s signal\n", s);
+}
diff --git a/src/error.h b/src/error.h
new file mode 100644
index 0000000..b330fff
--- /dev/null
+++ b/src/error.h
@@ -0,0 +1,59 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for error and debug functions */
+
+#ifndef	PCB_ERROR_H
+#define	PCB_ERROR_H
+
+/* Common return codes. */
+#define	STATUS_OK		0
+#define	STATUS_BREAK	1
+#define	STATUS_ERROR	-1
+
+typedef enum pcb_message_level {
+	/* MSG_DEFAULT is the default level when a message is not converted yet
+	   to any of the levels below. This level will go away once all messages
+	   are converted. Please grep for this and convert the message to the
+	   more specific. */
+	PCB_MSG_DEFAULT = 1,
+
+	PCB_MSG_DEBUG = 0,   /* Debug message. Should probably not be shown in regular operation. */
+	PCB_MSG_INFO,        /* Info message. FYI for the user, no action needed. */
+	PCB_MSG_WARNING,     /* Something the user should probably take note */
+	PCB_MSG_ERROR        /* Couldn't finish an action, needs user attention. */
+} pcb_message_level_t;
+
+void Message(enum pcb_message_level level, const char *Format, ...);
+void MyFatal(const char *Format, ...);
+void OpenErrorMessage(const char *);
+void PopenErrorMessage(const char *);
+void OpendirErrorMessage(const char *);
+void ChdirErrorMessage(const char *);
+void CatchSignal(int);
+void pcb_trace(const char *Format, ...);
+
+#endif
diff --git a/src/event.c b/src/event.c
new file mode 100644
index 0000000..4f0a310
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,174 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "event.h"
+#include "error.h"
+#include "fptr_cast.h"
+
+typedef struct event_s event_t;
+
+struct event_s {
+	event_handler_t *handler;
+	void *user_data;
+	const char *cookie;
+	event_t *next;
+};
+
+event_t *events[EVENT_last];
+
+#define event_valid(ev) (((ev) >= 0) && ((ev) < EVENT_last))
+
+void event_bind(event_id_t ev, event_handler_t * handler, void *user_data, const char *cookie)
+{
+	event_t *e;
+
+	if (!(event_valid(ev)))
+		return;
+
+	e = malloc(sizeof(event_t));
+	e->handler = handler;
+	e->cookie = cookie;
+	e->user_data = user_data;
+
+	/* insert the new event in front of the list */
+	e->next = events[ev];
+	events[ev] = e;
+}
+
+static void event_destroy(event_t * ev)
+{
+	free(ev);
+}
+
+void event_unbind(event_id_t ev, event_handler_t * handler)
+{
+	event_t *prev = NULL, *e, *next;
+	if (!(event_valid(ev)))
+		return;
+	for (e = events[ev]; e != NULL; e = next) {
+		next = e->next;
+		if (e->handler == handler) {
+			event_destroy(e);
+			if (prev == NULL)
+				events[ev] = next;
+			else
+				prev->next = next;
+		}
+		else
+			prev = e;
+	}
+}
+
+void event_unbind_cookie(event_id_t ev, const char *cookie)
+{
+	event_t *prev = NULL, *e, *next;
+	if (!(event_valid(ev)))
+		return;
+	for (e = events[ev]; e != NULL; e = next) {
+		next = e->next;
+		if (e->cookie == cookie) {
+			event_destroy(e);
+			if (prev == NULL)
+				events[ev] = next;
+			else
+				prev->next = next;
+		}
+		else
+		 prev = e;
+	}
+}
+
+
+void event_unbind_allcookie(const char *cookie)
+{
+	event_id_t n;
+	for (n = 0; n < EVENT_last; n++)
+		event_unbind_cookie(n, cookie);
+}
+
+void event(event_id_t ev, const char *fmt, ...)
+{
+	va_list ap;
+	event_arg_t argv[EVENT_MAX_ARG], *a;
+	event_t *e;
+	int argc;
+
+	a = argv;
+	a->type = ARG_INT;
+	a->d.i = ev;
+	argc = 1;
+
+	if (fmt != NULL) {
+		va_start(ap, fmt);
+		for (a++; *fmt != '\0'; fmt++, a++, argc++) {
+			if (argc >= EVENT_MAX_ARG) {
+				Message(PCB_MSG_DEFAULT, "event(): too many arguments\n");
+				break;
+			}
+			switch (*fmt) {
+			case 'i':
+				a->type = ARG_INT;
+				a->d.i = va_arg(ap, int);
+				break;
+			case 'd':
+				a->type = ARG_DOUBLE;
+				a->d.d = va_arg(ap, double);
+				break;
+			case 's':
+				a->type = ARG_STR;
+				a->d.s = va_arg(ap, const char *);
+				break;
+			default:
+				a->type = ARG_INT;
+				a->d.i = 0;
+				Message(PCB_MSG_DEFAULT, "event(): invalid argument type '%c'\n", *fmt);
+				break;
+			}
+		}
+		va_end(ap);
+	}
+
+	for (e = events[ev]; e != NULL; e = e->next)
+		e->handler(e->user_data, argc, (event_arg_t **) & argv);
+}
+
+void events_init(void)
+{
+
+}
+
+void events_uninit(void)
+{
+	int ev;
+	for(ev = 0; ev < EVENT_last; ev++) {
+		event_t *e, *next;
+		for(e = events[ev]; e != NULL; e = next) {
+			next = e->next;
+			fprintf(stderr, "WARNING: events_uninit: event %d still has %p registered for cookie %p\n", ev, pcb_cast_f2d(e->handler), (void *)e->cookie);
+			free(e);
+		}
+	}
+}
+
diff --git a/src/event.h b/src/event.h
new file mode 100644
index 0000000..6d6b9f4
--- /dev/null
+++ b/src/event.h
@@ -0,0 +1,83 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_EVENT_H
+#define PCB_EVENT_H
+typedef enum {
+	EVENT_GUI_INIT,               /* finished initializing the GUI called right before the main loop of the GUI; args: (void) */
+	EVENT_CLI_ENTER,              /* the user pressed enter on a CLI command - called before parsing the line for actions; args: (str commandline) */
+	EVENT_SAVE_PRE,               /* called before saving the design */
+	EVENT_SAVE_POST,              /* called after saving the design */
+	EVENT_LOAD_PRE,               /* called before loading a new design */
+	EVENT_LOAD_POST,              /* called after loading a new design, whether it was successful or not */
+	EVENT_last                    /* not a real event */
+} event_id_t;
+
+/* Maximum number of arguments for an event handler, auto-set argv[0] included */
+#define EVENT_MAX_ARG 16
+
+/* Argument types in event's argv[] */
+typedef enum {
+	ARG_INT,											/* format char: i */
+	ARG_DOUBLE,										/* format char: d */
+	ARG_STR												/* format char: s */
+} event_argtype_t;
+
+
+/* An argument is its type and value */
+typedef struct {
+	event_argtype_t type;
+	union {
+		int i;
+		double d;
+		const char *s;
+	} d;
+} event_arg_t;
+
+/* Initialize the event system */
+void events_init(void);
+
+/* Uninitialize the event system and remove all events */
+void events_uninit(void);
+
+
+/* Event callback prototype; user_data is the same as in event_bind().
+   argv[0] is always an ARG_INT with the event id that triggered the event. */
+typedef void (event_handler_t) (void *user_data, int argc, event_arg_t * argv[]);
+
+/* Bind: add a handler to the call-list of an event; the cookie is also remembered
+   so that mass-unbind is easier later. user_data is passed to the handler. */
+void event_bind(event_id_t ev, event_handler_t * handler, void *user_data, const char *cookie);
+
+/* Unbind: remove a handler from an event */
+void event_unbind(event_id_t ev, event_handler_t * handler);
+
+/* Unbind by cookie: remove all handlers from an event matching the cookie */
+void event_unbind_cookie(event_id_t ev, const char *cookie);
+
+/* Unbind all by cookie: remove all handlers from all events matching the cookie */
+void event_unbind_allcookie(const char *cookie);
+
+/* Event trigger: call all handlers for an event. Fmt is a list of
+   format characters (e.g. i for ARG_INT). */
+void event(event_id_t ev, const char *fmt, ...);
+#endif
diff --git a/src/file_act.c b/src/file_act.c
new file mode 100644
index 0000000..3ce5d80
--- /dev/null
+++ b/src/file_act.c
@@ -0,0 +1,372 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+#include "data.h"
+#include "action_helper.h"
+#include "error.h"
+
+#include "crosshair.h"
+#include "set.h"
+#include "plug_io.h"
+#include "plug_import.h"
+#include "buffer.h"
+#include "misc.h"
+#include "remove.h"
+#include "create.h"
+#include "draw.h"
+#include "find.h"
+#include "search.h"
+#include "hid_actions.h"
+#include "hid_attrib.h"
+#include "compat_misc.h"
+
+/* ---------------------------------------------------------------- */
+static const char execcommand_syntax[] = "ExecCommand(command)";
+
+static const char execcommand_help[] = "Runs a command.";
+
+/* %start-doc actions execcommand
+
+Runs the given command, which is a system executable.
+
+%end-doc */
+
+static int ActionExecCommand(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *command;
+
+	if (argc < 1) {
+		AFAIL(execcommand);
+	}
+
+	command = ACTION_ARG(0);
+
+	if (system(command))
+		return 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char loadfrom_syntax[] = "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename[,format])";
+
+static const char loadfrom_help[] = "Load layout data from a file.";
+
+/* %start-doc actions LoadFrom
+
+This action assumes you know what the filename is.  The various GUIs
+should have a similar @code{Load} action where the filename is
+optional, and will provide their own file selection mechanism to let
+you choose the file name.
+
+ at table @code
+
+ at item Layout
+Loads an entire PCB layout, replacing the current one.
+
+ at item LayoutToBuffer
+Loads an entire PCB layout to the paste buffer.
+
+ at item ElementToBuffer
+Loads the given element file into the paste buffer.  Element files
+contain only a single @code{Element} definition, such as the
+``newlib'' library uses.
+
+ at item Netlist
+Loads a new netlist, replacing any current netlist.
+
+ at item Revert
+Re-loads the current layout from its disk file, reverting any changes
+you may have made.
+
+ at end table
+
+%end-doc */
+
+static int ActionLoadFrom(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function, *name, *format = NULL;
+
+	if (argc < 2)
+		AFAIL(loadfrom);
+
+	function = argv[0];
+	name = argv[1];
+	if (argc > 2)
+		format = argv[2];
+
+	if (strcasecmp(function, "ElementToBuffer") == 0) {
+		notify_crosshair_change(pcb_false);
+		if (LoadElementToBuffer(PASTEBUFFER, name))
+			SetMode(PCB_MODE_PASTE_BUFFER);
+		notify_crosshair_change(pcb_true);
+	}
+
+	else if (strcasecmp(function, "LayoutToBuffer") == 0) {
+		notify_crosshair_change(pcb_false);
+		if (LoadLayoutToBuffer(PASTEBUFFER, name, format))
+			SetMode(PCB_MODE_PASTE_BUFFER);
+		notify_crosshair_change(pcb_true);
+	}
+
+	else if (strcasecmp(function, "Layout") == 0) {
+		if (!PCB->Changed || gui->confirm_dialog(_("OK to override layout data?"), 0))
+			LoadPCB(name, format, pcb_true, 0);
+	}
+
+	else if (strcasecmp(function, "Netlist") == 0) {
+		if (PCB->Netlistname)
+			free(PCB->Netlistname);
+		PCB->Netlistname = StripWhiteSpaceAndDup(name);
+		{
+			int i;
+			for (i = 0; i < NUM_NETLISTS; i++)
+				FreeLibraryMemory(&(PCB->NetlistLib[i]));
+		}
+		if (!ImportNetlist(PCB->Netlistname))
+			pcb_netlist_changed(1);
+	}
+	else if (strcasecmp(function, "Revert") == 0 && PCB->Filename
+					 && (!PCB->Changed || gui->confirm_dialog(_("OK to override changes?"), 0))) {
+		RevertPCB();
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char new_syntax[] = "New([name])";
+
+static const char new_help[] = "Starts a new layout.";
+
+/* %start-doc actions New
+
+If a name is not given, one is prompted for.
+
+%end-doc */
+
+static int ActionNew(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *argument_name = ACTION_ARG(0);
+	char *name = NULL;
+
+	if (!PCB->Changed || gui->confirm_dialog(_("OK to clear layout data?"), 0)) {
+		if (argument_name)
+			name = pcb_strdup(argument_name);
+		else
+			name = gui->prompt_for(_("Enter the layout name:"), "");
+
+		if (!name)
+			return 1;
+
+		notify_crosshair_change(pcb_false);
+		/* do emergency saving
+		 * clear the old struct and allocate memory for the new one
+		 */
+		if (PCB->Changed && conf_core.editor.save_in_tmp)
+			SaveInTMP();
+		RemovePCB(PCB);
+		PCB = CreateNewPCB();
+		CreateNewPCBPost(PCB, 1);
+
+		/* setup the new name and reset some values to default */
+		free(PCB->Name);
+		PCB->Name = name;
+
+		ResetStackAndVisibility();
+		SetCrosshairRange(0, 0, PCB->MaxWidth, PCB->MaxHeight);
+		CenterDisplay(PCB->MaxWidth / 2, PCB->MaxHeight / 2);
+		Redraw();
+
+		if (gui != NULL)
+			hid_action("PCBChanged");
+		notify_crosshair_change(pcb_true);
+		return 0;
+	}
+	return 1;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char saveto_syntax[] =
+	"SaveTo(Layout|LayoutAs,filename)\n"
+	"SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n" "SaveTo(PasteBuffer,filename)";
+
+static const char saveto_help[] = "Saves data to a file.";
+
+/* %start-doc actions SaveTo
+
+ at table @code
+
+ at item Layout
+Saves the current layout.
+
+ at item LayoutAs
+Saves the current layout, and remembers the filename used.
+
+ at item AllConnections
+Save all connections to a file.
+
+ at item AllUnusedPins
+List all unused pins to a file.
+
+ at item ElementConnections
+Save connections to the element at the cursor to a file.
+
+ at item PasteBuffer
+Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
+
+ at end table
+
+%end-doc */
+
+static int ActionSaveTo(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function;
+	const char *name;
+	const char *fmt = NULL;
+
+	function = argv[0];
+	name = argv[1];
+
+	if (strcasecmp(function, "Layout") == 0) {
+		if (SavePCB(PCB->Filename, NULL) == 0)
+			SetChangedFlag(pcb_false);
+		if (gui->notify_filename_changed != NULL)
+			gui->notify_filename_changed();
+		return 0;
+	}
+
+	if ((argc != 2) && (argc != 3))
+		AFAIL(saveto);
+
+	if (argc >= 3)
+		fmt = argv[2];
+
+	if (strcasecmp(function, "LayoutAs") == 0) {
+		if (SavePCB(name, fmt) == 0) {
+			SetChangedFlag(pcb_false);
+			free(PCB->Filename);
+			PCB->Filename = pcb_strdup(name);
+			if (gui->notify_filename_changed != NULL)
+				gui->notify_filename_changed();
+		}
+		return 0;
+	}
+
+	if (strcasecmp(function, "AllConnections") == 0) {
+		FILE *fp;
+		pcb_bool result;
+		if ((fp = CheckAndOpenFile(name, pcb_true, pcb_false, &result, NULL)) != NULL) {
+			LookupConnectionsToAllElements(fp);
+			fclose(fp);
+			SetChangedFlag(pcb_true);
+		}
+		return 0;
+	}
+
+	if (strcasecmp(function, "AllUnusedPins") == 0) {
+		FILE *fp;
+		pcb_bool result;
+		if ((fp = CheckAndOpenFile(name, pcb_true, pcb_false, &result, NULL)) != NULL) {
+			LookupUnusedPins(fp);
+			fclose(fp);
+			SetChangedFlag(pcb_true);
+		}
+		return 0;
+	}
+
+	if (strcasecmp(function, "ElementConnections") == 0) {
+		ElementTypePtr element;
+		void *ptrtmp;
+		FILE *fp;
+		pcb_bool result;
+
+		if ((SearchScreen(Crosshair.X, Crosshair.Y, PCB_TYPE_ELEMENT, &ptrtmp, &ptrtmp, &ptrtmp)) != PCB_TYPE_NONE) {
+			element = (ElementTypePtr) ptrtmp;
+			if ((fp = CheckAndOpenFile(name, pcb_true, pcb_false, &result, NULL)) != NULL) {
+				LookupElementConnections(element, fp);
+				fclose(fp);
+				SetChangedFlag(pcb_true);
+			}
+		}
+		return 0;
+	}
+
+	if (strcasecmp(function, "PasteBuffer") == 0) {
+		return SaveBufferElements(name, fmt);
+	}
+
+	AFAIL(saveto);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char quit_syntax[] = "Quit()";
+
+static const char quit_help[] = "Quits the application after confirming.";
+
+/* %start-doc actions Quit
+
+If you have unsaved changes, you will be prompted to confirm (or
+save) before quitting.
+
+%end-doc */
+
+static int ActionQuit(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *force = ACTION_ARG(0);
+	if (force && strcasecmp(force, "force") == 0) {
+		PCB->Changed = 0;
+		exit(0);
+	}
+	if (!PCB->Changed || gui->close_confirm_dialog() == HID_CLOSE_CONFIRM_OK)
+		QuitApplication();
+	return 1;
+}
+
+
+HID_Action file_action_list[] = {
+	{"ExecCommand", 0, ActionExecCommand,
+	 execcommand_help, execcommand_syntax}
+	,
+	{"LoadFrom", 0, ActionLoadFrom,
+	 loadfrom_help, loadfrom_syntax}
+	,
+	{"New", 0, ActionNew,
+	 new_help, new_syntax}
+	,
+	{"SaveTo", 0, ActionSaveTo,
+	 saveto_help, saveto_syntax}
+	,
+	{"Quit", 0, ActionQuit,
+	 quit_help, quit_syntax}
+};
+
+REGISTER_ACTIONS(file_action_list, NULL)
diff --git a/src/find.c b/src/find.c
new file mode 100644
index 0000000..304e7fa
--- /dev/null
+++ b/src/find.c
@@ -0,0 +1,183 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/*
+ * short description:
+ * - lists for pins and vias, lines, arcs, pads and for polygons are created.
+ *   Every object that has to be checked is added to its list.
+ *   Coarse searching is accomplished with the data rtrees.
+ * - there's no 'speed-up' mechanism for polygons because they are not used
+ *   as often as other objects
+ * - the maximum distance between line and pin ... would depend on the angle
+ *   between them. To speed up computation the limit is set to one half
+ *   of the thickness of the objects (cause of square pins).
+ *
+ * PV:  means pin or via (objects that connect layers)
+ * LO:  all non PV objects (layer objects like lines, arcs, polygons, pads)
+ *
+ * 1. first, the LO or PV at the given coordinates is looked up
+ * 2. all LO connections to that PV are looked up next
+ * 3. lookup of all LOs connected to LOs from (2).
+ *    This step is repeated until no more new connections are found.
+ * 4. lookup all PVs connected to the LOs from (2) and (3)
+ * 5. start again with (1) for all new PVs from (4)
+ *
+ */
+
+/* routines to find connections between pins, vias, lines...
+ */
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <setjmp.h>
+#include <assert.h>
+
+#include "conf_core.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "misc.h"
+#include "rtree.h"
+#include "polygon.h"
+#include "search.h"
+#include "set.h"
+#include "undo.h"
+#include "rats.h"
+#include "plug_io.h"
+#include "hid_actions.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+#undef DEBUG
+
+/* ---------------------------------------------------------------------------
+ * some local macros
+ */
+
+#define	SEPARATE(FP)							\
+	{											\
+		int	i;									\
+		fputc('#', (FP));						\
+		for (i = conf_core.appearance.messages.char_per_line; i; i--)	\
+			fputc('=', (FP));					\
+		fputc('\n', (FP));						\
+	}
+
+#define	PADLIST_ENTRY(L,I)	\
+	(((PadTypePtr *)PadList[(L)].Data)[(I)])
+
+#define	LINELIST_ENTRY(L,I)	\
+	(((LineTypePtr *)LineList[(L)].Data)[(I)])
+
+#define	ARCLIST_ENTRY(L,I)	\
+	(((ArcTypePtr *)ArcList[(L)].Data)[(I)])
+
+#define RATLIST_ENTRY(I)	\
+	(((RatTypePtr *)RatList.Data)[(I)])
+
+#define	POLYGONLIST_ENTRY(L,I)	\
+	(((PolygonTypePtr *)PolygonList[(L)].Data)[(I)])
+
+#define	PVLIST_ENTRY(I)	\
+	(((PinTypePtr *)PVList.Data)[(I)])
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ *
+ * the two 'dummy' structs for PVs and Pads are necessary for creating
+ * connection lists which include the element's name
+ */
+typedef struct {
+	void **Data;									/* pointer to index data */
+	pcb_cardinal_t Location,						/* currently used position */
+	  DrawLocation, Number,				/* number of objects in list */
+	  Size;
+} ListType, *ListTypePtr;
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static Coord Bloat = 0;
+static int TheFlag = PCB_FLAG_FOUND;
+static int OldFlag = PCB_FLAG_FOUND;
+static void *thing_ptr1, *thing_ptr2, *thing_ptr3;
+static int thing_type;
+find_callback_t find_callback = NULL;
+#define make_callback(current_type, current_ptr, from_type, from_ptr, type) \
+	do { \
+		if (find_callback != NULL) \
+			find_callback(current_type, current_ptr, from_type, from_ptr, type); \
+	} while(0)
+
+static pcb_bool User = pcb_false;				/* user action causing this */
+static pcb_bool drc = pcb_false;				/* whether to stop if finding something not found */
+static pcb_bool IsBad = pcb_false;
+static pcb_cardinal_t drcerr_count;		/* count of drc errors */
+static pcb_cardinal_t TotalP, TotalV, NumberOfPads[2];
+static ListType LineList[MAX_LAYER],	/* list of objects to */
+  PolygonList[MAX_LAYER], ArcList[MAX_LAYER], PadList[2], RatList, PVList;
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static pcb_bool LookupLOConnectionsToPVList(pcb_bool);
+static pcb_bool LookupLOConnectionsToLOList(pcb_bool);
+static pcb_bool LookupPVConnectionsToLOList(pcb_bool);
+static pcb_bool LookupPVConnectionsToPVList(void);
+static pcb_bool LookupLOConnectionsToLine(LineTypePtr, pcb_cardinal_t, pcb_bool);
+static pcb_bool LookupLOConnectionsToPad(PadTypePtr, pcb_cardinal_t);
+static pcb_bool LookupLOConnectionsToPolygon(PolygonTypePtr, pcb_cardinal_t);
+static pcb_bool LookupLOConnectionsToArc(ArcTypePtr, pcb_cardinal_t);
+static pcb_bool LookupLOConnectionsToRatEnd(PointTypePtr, pcb_cardinal_t);
+static pcb_bool IsRatPointOnLineEnd(PointTypePtr, LineTypePtr);
+static pcb_bool ArcArcIntersect(ArcTypePtr, ArcTypePtr);
+static pcb_bool PrintElementConnections(ElementTypePtr, FILE *, pcb_bool);
+static pcb_bool ListsEmpty(pcb_bool);
+static pcb_bool DoIt(pcb_bool, pcb_bool);
+static void PrintElementNameList(ElementTypePtr, FILE *);
+static void PrintConnectionElementName(ElementTypePtr, FILE *);
+static void PrintConnectionListEntry(char *, ElementTypePtr, pcb_bool, FILE *);
+static void PrintPadConnections(pcb_cardinal_t, FILE *, pcb_bool);
+static void PrintPinConnections(FILE *, pcb_bool);
+static void DumpList(void);
+static pcb_bool ListStart(int, void *, void *, void *);
+static pcb_bool SetThing(int, void *, void *, void *);
+
+
+#include "find_geo.c"
+#include "find_lookup.c"
+#include "find_drc.c"
+#include "find_misc.c"
+#include "find_clear.c"
+
+#include "find_debug.c"
+#include "find_print.c"
diff --git a/src/find.h b/src/find.h
new file mode 100644
index 0000000..ef09670
--- /dev/null
+++ b/src/find.h
@@ -0,0 +1,96 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes connection search routines */
+
+#ifndef	PCB_FIND_H
+#define	PCB_FIND_H
+
+#include <stdio.h>							/* needed to define 'FILE *' */
+#include "global.h"
+
+typedef enum {
+	FCT_COPPER = 1,								/* copper connection */
+	FCT_INTERNAL = 2,							/* element-internal connection */
+	FCT_RAT = 4,									/* connected by a rat line */
+	FCT_ELEMENT = 8,							/* pin/pad is part of an element whose pins/pads are being listed */
+	FCT_START = 16								/* starting object of a query */
+} found_conn_type_t;
+
+typedef void (*find_callback_t) (int current_type, void *current_ptr, int from_type, void *from_ptr,
+																 found_conn_type_t conn_type);
+
+
+/* if not NULL, this function is called whenever something is found
+   (in LookupConnections for example). The caller should save the original
+   value and set that back around the call, if the callback needs to be changed.
+   */
+extern find_callback_t find_callback;
+
+/* ---------------------------------------------------------------------------
+ * some local defines
+ */
+#define LOOKUP_FIRST	\
+	(PCB_TYPE_PIN | PCB_TYPE_PAD)
+#define LOOKUP_MORE	\
+	(PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_RATLINE | PCB_TYPE_POLYGON | PCB_TYPE_ARC)
+#define SILK_TYPE	\
+	(PCB_TYPE_LINE | PCB_TYPE_ARC | PCB_TYPE_POLYGON)
+
+pcb_bool LineLineIntersect(LineTypePtr, LineTypePtr);
+pcb_bool LineArcIntersect(LineTypePtr, ArcTypePtr);
+pcb_bool PinLineIntersect(PinTypePtr, LineTypePtr);
+pcb_bool LinePadIntersect(LineTypePtr, PadTypePtr);
+pcb_bool ArcPadIntersect(ArcTypePtr, PadTypePtr);
+pcb_bool IsPolygonInPolygon(PolygonTypePtr, PolygonTypePtr);
+void LookupElementConnections(ElementTypePtr, FILE *);
+void LookupConnectionsToAllElements(FILE *);
+void LookupConnection(Coord, Coord, pcb_bool, Coord, int);
+void LookupConnectionByPin(int type, void *ptr1);
+void LookupUnusedPins(FILE *);
+pcb_bool ResetFoundLinesAndPolygons(pcb_bool);
+pcb_bool ResetFoundPinsViasAndPads(pcb_bool);
+pcb_bool ResetConnections(pcb_bool);
+void InitConnectionLookup(void);
+void InitComponentLookup(void);
+void InitLayoutLookup(void);
+void FreeConnectionLookupMemory(void);
+void FreeComponentLookupMemory(void);
+void FreeLayoutLookupMemory(void);
+void RatFindHook(int, void *, void *, void *, pcb_bool, pcb_bool);
+void SaveFindFlag(int);
+void RestoreFindFlag(void);
+int DRCAll(void);
+pcb_bool IsLineInPolygon(LineTypePtr, PolygonTypePtr);
+pcb_bool IsArcInPolygon(ArcTypePtr, PolygonTypePtr);
+pcb_bool IsPadInPolygon(PadTypePtr, PolygonTypePtr);
+
+/* find_clear.c */
+pcb_bool ClearFlagOnPinsViasAndPads(pcb_bool AndDraw, int flag);
+pcb_bool ClearFlagOnLinesAndPolygons(pcb_bool AndDraw, int flag);
+pcb_bool ClearFlagOnAllObjects(pcb_bool AndDraw, int flag);
+
+#endif
diff --git a/src/find_act.c b/src/find_act.c
new file mode 100644
index 0000000..d753960
--- /dev/null
+++ b/src/find_act.c
@@ -0,0 +1,73 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+#include "data.h"
+#include "error.h"
+#include "find.h"
+
+/* -------------------------------------------------------------------------- */
+
+static const char drc_syntax[] = "DRC()";
+
+static const char drc_help[] = "Invoke the DRC check.";
+
+/* %start-doc actions DRC
+
+Note that the design rule check uses the current board rule settings,
+not the current style settings.
+
+%end-doc */
+
+static int ActionDRCheck(int argc, const char **argv, Coord x, Coord y)
+{
+	int count;
+
+	if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview) {
+		Message(PCB_MSG_DEFAULT, _("%m+Rules are minspace %$mS, minoverlap %$mS "
+							"minwidth %$mS, minsilk %$mS\n"
+							"min drill %$mS, min annular ring %$mS\n"),
+						conf_core.editor.grid_unit->allow, PCB->Bloat, PCB->Shrink, PCB->minWid, PCB->minSlk, PCB->minDrill, PCB->minRing);
+	}
+	count = DRCAll();
+	if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview) {
+		if (count == 0)
+			Message(PCB_MSG_DEFAULT, _("No DRC problems found.\n"));
+		else if (count > 0)
+			Message(PCB_MSG_DEFAULT, _("Found %d design rule errors.\n"), count);
+		else
+			Message(PCB_MSG_DEFAULT, _("Aborted DRC after %d design rule errors.\n"), -count);
+	}
+	return 0;
+}
+
+HID_Action find_action_list[] = {
+	{"DRC", 0, ActionDRCheck,
+	 drc_help, drc_syntax}
+};
+
+REGISTER_ACTIONS(find_action_list, NULL)
diff --git a/src/find_clear.c b/src/find_clear.c
new file mode 100644
index 0000000..6068605
--- /dev/null
+++ b/src/find_clear.c
@@ -0,0 +1,148 @@
+/*
+ * PCB, interactive printed circuit board design
+ *
+ * Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Contact addresses for paper mail and Email:
+ * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ * Thomas.Nau at rz.uni-ulm.de
+ */
+
+/*!
+ * \brief Resets all used flags of pins and vias.
+ */
+pcb_bool ClearFlagOnPinsViasAndPads(pcb_bool AndDraw, int flag)
+{
+	pcb_bool change = pcb_false;
+
+	VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, via)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+			CLEAR_FLAG(flag, via);
+			if (AndDraw)
+				DrawVia(via);
+			change = pcb_true;
+		}
+	}
+	END_LOOP;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(flag, pin)) {
+				if (AndDraw)
+					AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+				CLEAR_FLAG(flag, pin);
+				if (AndDraw)
+					DrawPin(pin);
+				change = pcb_true;
+			}
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (TEST_FLAG(flag, pad)) {
+				if (AndDraw)
+					AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+				CLEAR_FLAG(flag, pad);
+				if (AndDraw)
+					DrawPad(pad);
+				change = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	if (change)
+		SetChangedFlag(pcb_true);
+	return change;
+}
+
+/*!
+ * \brief Resets all used flags of LOs.
+ */
+pcb_bool ClearFlagOnLinesAndPolygons(pcb_bool AndDraw, int flag)
+{
+	pcb_bool change = pcb_false;
+
+	RAT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, line)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_RATLINE, line, line, line);
+			CLEAR_FLAG(flag, line);
+			if (AndDraw)
+				DrawRat(line);
+			change = pcb_true;
+		}
+	}
+	END_LOOP;
+	COPPERLINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, line)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_LINE, layer, line, line);
+			CLEAR_FLAG(flag, line);
+			if (AndDraw)
+				DrawLine(layer, line);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	COPPERARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, arc)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_ARC, layer, arc, arc);
+			CLEAR_FLAG(flag, arc);
+			if (AndDraw)
+				DrawArc(layer, arc);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	COPPERPOLYGON_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, polygon)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_POLYGON, layer, polygon, polygon);
+			CLEAR_FLAG(flag, polygon);
+			if (AndDraw)
+				DrawPolygon(layer, polygon);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	if (change)
+		SetChangedFlag(pcb_true);
+	return change;
+}
+
+/*!
+ * \brief Resets all found connections.
+ */
+pcb_bool ClearFlagOnAllObjects(pcb_bool AndDraw, int flag)
+{
+	pcb_bool change = pcb_false;
+
+	change = ClearFlagOnPinsViasAndPads(AndDraw, flag) || change;
+	change = ClearFlagOnLinesAndPolygons(AndDraw, flag) || change;
+
+	return change;
+}
diff --git a/src/find_deadcode.c b/src/find_deadcode.c
new file mode 100644
index 0000000..b7d033a
--- /dev/null
+++ b/src/find_deadcode.c
@@ -0,0 +1,145 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* Dead code - no calls to these functions from anywhere in the code */
+#error do not compile this
+
+
+static int LOT_Linecallback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) && LineLineIntersect(&i->line, line))
+		longjmp(i->env, 1);
+	return 0;
+}
+
+static int LOT_Arccallback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!arc->Thickness)
+		return 0;
+	if (!TEST_FLAG(TheFlag, arc) && LineArcIntersect(&i->line, arc))
+		longjmp(i->env, 1);
+	return 0;
+}
+
+static int LOT_Padcallback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && i->layer == (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER)
+			&& LinePadIntersect(&i->line, pad))
+		longjmp(i->env, 1);
+	return 0;
+}
+
+static pcb_bool PVTouchesLine(LineTypePtr line)
+{
+	struct lo_info info;
+
+	info.line = *line;
+	EXPAND_BOUNDS(&info.line);
+	if (setjmp(info.env) == 0)
+		r_search(PCB->Data->via_tree, (BoxType *) & info.line, NULL, pv_touch_callback, &info, NULL);
+	else
+		return pcb_true;
+	if (setjmp(info.env) == 0)
+		r_search(PCB->Data->pin_tree, (BoxType *) & info.line, NULL, pv_touch_callback, &info, NULL);
+	else
+		return pcb_true;
+
+	return (pcb_false);
+}
+
+static pcb_bool LOTouchesLine(LineTypePtr Line, pcb_cardinal_t LayerGroup)
+{
+	pcb_cardinal_t entry;
+	struct lo_info info;
+
+
+	/* the maximum possible distance */
+
+	info.line = *Line;
+	EXPAND_BOUNDS(&info.line);
+
+	/* loop over all layers of the group */
+	for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+		pcb_cardinal_t layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+
+		/* handle normal layers */
+		if (layer < max_copper_layer) {
+			gdl_iterator_t it;
+			PolygonType *polygon;
+
+			/* find the first line that touches coordinates */
+
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->line_tree, (BoxType *) & info.line, NULL, LOT_Linecallback, &info, NULL);
+			else
+				return (pcb_true);
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->arc_tree, (BoxType *) & info.line, NULL, LOT_Arccallback, &info, NULL);
+			else
+				return (pcb_true);
+
+			/* now check all polygons */
+			polylist_foreach(&(PCB->Data->Layer[layer].Polygon), &it, polygon) {
+				if (!TEST_FLAG(TheFlag, polygon)
+						&& IsLineInPolygon(Line, polygon))
+					return (pcb_true);
+			}
+		}
+		else {
+			/* handle special 'pad' layers */
+			info.layer = layer - max_copper_layer;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pad_tree, &info.line.BoundingBox, NULL, LOT_Padcallback, &info, NULL);
+			else
+				return pcb_true;
+		}
+	}
+	return (pcb_false);
+}
+
+/* returns pcb_true if nothing un-found touches the passed line
+ * returns pcb_false if it would touch something not yet found
+ * doesn't include rat-lines in the search
+ */
+
+pcb_bool lineClear(LineTypePtr line, pcb_cardinal_t group)
+{
+	if (LOTouchesLine(line, group))
+		return (pcb_false);
+	if (PVTouchesLine(line))
+		return (pcb_false);
+	return (pcb_true);
+}
diff --git a/src/find_debug.c b/src/find_debug.c
new file mode 100644
index 0000000..b3ead5d
--- /dev/null
+++ b/src/find_debug.c
@@ -0,0 +1,135 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* find-related debug functions */
+
+/* ---------------------------------------------------------------------------
+ * writes the several names of an element to a file
+ */
+static void PrintElementNameList(ElementTypePtr Element, FILE * FP)
+{
+	fputc('(', FP);
+	PrintQuotedString(FP, (char *) EMPTY(DESCRIPTION_NAME(Element)));
+	fputc(' ', FP);
+	PrintQuotedString(FP, (char *) EMPTY(NAMEONPCB_NAME(Element)));
+	fputc(' ', FP);
+	PrintQuotedString(FP, (char *) EMPTY(VALUE_NAME(Element)));
+	fputc(')', FP);
+	fputc('\n', FP);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes the several names of an element to a file
+ */
+static void PrintConnectionElementName(ElementTypePtr Element, FILE * FP)
+{
+	fputs("Element", FP);
+	PrintElementNameList(Element, FP);
+	fputs("{\n", FP);
+}
+
+/* ---------------------------------------------------------------------------
+ * prints one {pin,pad,via}/element entry of connection lists
+ */
+static void PrintConnectionListEntry(char *ObjName, ElementTypePtr Element, pcb_bool FirstOne, FILE * FP)
+{
+	if (FirstOne) {
+		fputc('\t', FP);
+		PrintQuotedString(FP, ObjName);
+		fprintf(FP, "\n\t{\n");
+	}
+	else {
+		fprintf(FP, "\t\t");
+		PrintQuotedString(FP, ObjName);
+		fputc(' ', FP);
+		if (Element)
+			PrintElementNameList(Element, FP);
+		else
+			fputs("(__VIA__)\n", FP);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * prints all found connections of a pads to file FP
+ * the connections are stacked in 'PadList'
+ */
+static void PrintPadConnections(pcb_cardinal_t Layer, FILE * FP, pcb_bool IsFirst)
+{
+	pcb_cardinal_t i;
+	PadTypePtr ptr;
+
+	if (!PadList[Layer].Number)
+		return;
+
+	/* the starting pad */
+	if (IsFirst) {
+		ptr = PADLIST_ENTRY(Layer, 0);
+		if (ptr != NULL)
+			PrintConnectionListEntry((char *) UNKNOWN(ptr->Name), NULL, pcb_true, FP);
+		else
+			printf("Skipping NULL ptr in 1st part of PrintPadConnections\n");
+	}
+
+	/* we maybe have to start with i=1 if we are handling the
+	 * starting-pad itself
+	 */
+	for (i = IsFirst ? 1 : 0; i < PadList[Layer].Number; i++) {
+		ptr = PADLIST_ENTRY(Layer, i);
+		if (ptr != NULL)
+			PrintConnectionListEntry((char *) EMPTY(ptr->Name), (ElementTypePtr) ptr->Element, pcb_false, FP);
+		else
+			printf("Skipping NULL ptr in 2nd part of PrintPadConnections\n");
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * prints all found connections of a pin to file FP
+ * the connections are stacked in 'PVList'
+ */
+static void PrintPinConnections(FILE * FP, pcb_bool IsFirst)
+{
+	pcb_cardinal_t i;
+	PinTypePtr pv;
+
+	if (!PVList.Number)
+		return;
+
+	if (IsFirst) {
+		/* the starting pin */
+		pv = PVLIST_ENTRY(0);
+		PrintConnectionListEntry((char *) EMPTY(pv->Name), NULL, pcb_true, FP);
+	}
+
+	/* we maybe have to start with i=1 if we are handling the
+	 * starting-pin itself
+	 */
+	for (i = IsFirst ? 1 : 0; i < PVList.Number; i++) {
+		/* get the elements name or assume that its a via */
+		pv = PVLIST_ENTRY(i);
+		PrintConnectionListEntry((char *) EMPTY(pv->Name), (ElementTypePtr) pv->Element, pcb_false, FP);
+	}
+}
diff --git a/src/find_drc.c b/src/find_drc.c
new file mode 100644
index 0000000..7bc0ed7
--- /dev/null
+++ b/src/find_drc.c
@@ -0,0 +1,830 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* DRC related functions */
+
+static void GotoError(void);
+static pcb_bool DRCFind(int What, void *ptr1, void *ptr2, void *ptr3);
+
+static DrcViolationType
+	* pcb_drc_violation_new(const char *title,
+													const char *explanation,
+													Coord x, Coord y,
+													Angle angle,
+													pcb_bool have_measured,
+													Coord measured_value,
+													Coord required_value, int object_count, long int *object_id_list, int *object_type_list)
+{
+	DrcViolationType *violation = (DrcViolationType *) malloc(sizeof(DrcViolationType));
+
+	violation->title = pcb_strdup(title);
+	violation->explanation = pcb_strdup(explanation);
+	violation->x = x;
+	violation->y = y;
+	violation->angle = angle;
+	violation->have_measured = have_measured;
+	violation->measured_value = measured_value;
+	violation->required_value = required_value;
+	violation->object_count = object_count;
+	violation->object_id_list = object_id_list;
+	violation->object_type_list = object_type_list;
+
+	return violation;
+}
+
+static void pcb_drc_violation_free(DrcViolationType * violation)
+{
+	free(violation->title);
+	free(violation->explanation);
+	free(violation);
+}
+
+static gds_t drc_dialog_message;
+static void reset_drc_dialog_message(void)
+{
+	gds_truncate(&drc_dialog_message, 0);
+	if (gui->drc_gui != NULL)
+		gui->drc_gui->reset_drc_dialog_message();
+}
+
+static void append_drc_dialog_message(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	pcb_append_vprintf(&drc_dialog_message, fmt, ap);
+	va_end(ap);
+}
+
+static void GotoError(void);
+
+/*----------------------------------------------------------------------------
+ * Build a list of the of offending items by ID. (Currently just "thing")
+ */
+static void BuildObjectList(int *object_count, long int **object_id_list, int **object_type_list)
+{
+	*object_count = 0;
+	*object_id_list = NULL;
+	*object_type_list = NULL;
+
+	switch (thing_type) {
+	case PCB_TYPE_LINE:
+	case PCB_TYPE_ARC:
+	case PCB_TYPE_POLYGON:
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_VIA:
+	case PCB_TYPE_PAD:
+	case PCB_TYPE_ELEMENT:
+	case PCB_TYPE_RATLINE:
+		*object_count = 1;
+		*object_id_list = (long int *) malloc(sizeof(long int));
+		*object_type_list = (int *) malloc(sizeof(int));
+		**object_id_list = ((AnyObjectType *) thing_ptr3)->ID;
+		**object_type_list = thing_type;
+		return;
+
+	default:
+		fprintf(stderr, _("Internal error in BuildObjectList: unknown object type %i\n"), thing_type);
+	}
+}
+
+
+
+/*----------------------------------------------------------------------------
+ * Locate the coordinatates of offending item (thing)
+ */
+static void LocateError(Coord * x, Coord * y)
+{
+	switch (thing_type) {
+	case PCB_TYPE_LINE:
+		{
+			LineTypePtr line = (LineTypePtr) thing_ptr3;
+			*x = (line->Point1.X + line->Point2.X) / 2;
+			*y = (line->Point1.Y + line->Point2.Y) / 2;
+			break;
+		}
+	case PCB_TYPE_ARC:
+		{
+			ArcTypePtr arc = (ArcTypePtr) thing_ptr3;
+			*x = arc->X;
+			*y = arc->Y;
+			break;
+		}
+	case PCB_TYPE_POLYGON:
+		{
+			PolygonTypePtr polygon = (PolygonTypePtr) thing_ptr3;
+			*x = (polygon->Clipped->contours->xmin + polygon->Clipped->contours->xmax) / 2;
+			*y = (polygon->Clipped->contours->ymin + polygon->Clipped->contours->ymax) / 2;
+			break;
+		}
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_VIA:
+		{
+			PinTypePtr pin = (PinTypePtr) thing_ptr3;
+			*x = pin->X;
+			*y = pin->Y;
+			break;
+		}
+	case PCB_TYPE_PAD:
+		{
+			PadTypePtr pad = (PadTypePtr) thing_ptr3;
+			*x = (pad->Point1.X + pad->Point2.X) / 2;
+			*y = (pad->Point1.Y + pad->Point2.Y) / 2;
+			break;
+		}
+	case PCB_TYPE_ELEMENT:
+		{
+			ElementTypePtr element = (ElementTypePtr) thing_ptr3;
+			*x = element->MarkX;
+			*y = element->MarkY;
+			break;
+		}
+	default:
+		return;
+	}
+}
+
+
+static void append_drc_violation(DrcViolationType * violation)
+{
+	if (gui->drc_gui != NULL) {
+		gui->drc_gui->append_drc_violation(violation);
+	}
+	else {
+		/* Fallback to formatting the violation message as text */
+		append_drc_dialog_message("%s\n", violation->title);
+		append_drc_dialog_message(_("%m+near %$mD\n"), conf_core.editor.grid_unit->allow, violation->x, violation->y);
+		GotoError();
+	}
+
+	if (gui->drc_gui == NULL || gui->drc_gui->log_drc_violations) {
+		Message(PCB_MSG_DEFAULT, _("WARNING!  Design Rule error - %s\n"), violation->title);
+		Message(PCB_MSG_DEFAULT, _("%m+near location %$mD\n"), conf_core.editor.grid_unit->allow, violation->x, violation->y);
+	}
+}
+
+
+/*
+ * message when asked about continuing DRC checks after next
+ * violation is found.
+ */
+#define DRC_CONTINUE _("Press Next to continue DRC checking")
+#define DRC_NEXT _("Next")
+#define DRC_CANCEL _("Cancel")
+
+static int throw_drc_dialog(void)
+{
+	int r;
+
+	if (gui->drc_gui != NULL) {
+		r = gui->drc_gui->throw_drc_dialog();
+	}
+	else {
+		/* Fallback to formatting the violation message as text */
+		append_drc_dialog_message(DRC_CONTINUE);
+		r = gui->confirm_dialog(drc_dialog_message.array, DRC_CANCEL, DRC_NEXT);
+		reset_drc_dialog_message();
+	}
+	return r;
+}
+
+/* DRC clearance callback */
+static r_dir_t drc_callback(DataTypePtr data, LayerTypePtr layer, PolygonTypePtr polygon, int type, void *ptr1, void *ptr2)
+{
+	const char *message;
+	Coord x, y;
+	int object_count;
+	long int *object_id_list;
+	int *object_type_list;
+	DrcViolationType *violation;
+
+	LineTypePtr line = (LineTypePtr) ptr2;
+	ArcTypePtr arc = (ArcTypePtr) ptr2;
+	PinTypePtr pin = (PinTypePtr) ptr2;
+	PadTypePtr pad = (PadTypePtr) ptr2;
+
+	thing_type = type;
+	thing_ptr1 = ptr1;
+	thing_ptr2 = ptr2;
+	thing_ptr3 = ptr2;
+	switch (type) {
+	case PCB_TYPE_LINE:
+		if (line->Clearance < 2 * PCB->Bloat) {
+			AddObjectToFlagUndoList(type, ptr1, ptr2, ptr2);
+			SET_FLAG(TheFlag, line);
+			message = _("Line with insufficient clearance inside polygon\n");
+			goto doIsBad;
+		}
+		break;
+	case PCB_TYPE_ARC:
+		if (arc->Clearance < 2 * PCB->Bloat) {
+			AddObjectToFlagUndoList(type, ptr1, ptr2, ptr2);
+			SET_FLAG(TheFlag, arc);
+			message = _("Arc with insufficient clearance inside polygon\n");
+			goto doIsBad;
+		}
+		break;
+	case PCB_TYPE_PAD:
+		if (pad->Clearance && pad->Clearance < 2 * PCB->Bloat)
+			if (IsPadInPolygon(pad, polygon)) {
+				AddObjectToFlagUndoList(type, ptr1, ptr2, ptr2);
+				SET_FLAG(TheFlag, pad);
+				message = _("Pad with insufficient clearance inside polygon\n");
+				goto doIsBad;
+			}
+		break;
+	case PCB_TYPE_PIN:
+		if (pin->Clearance && pin->Clearance < 2 * PCB->Bloat) {
+			AddObjectToFlagUndoList(type, ptr1, ptr2, ptr2);
+			SET_FLAG(TheFlag, pin);
+			message = _("Pin with insufficient clearance inside polygon\n");
+			goto doIsBad;
+		}
+		break;
+	case PCB_TYPE_VIA:
+		if (pin->Clearance && pin->Clearance < 2 * PCB->Bloat) {
+			AddObjectToFlagUndoList(type, ptr1, ptr2, ptr2);
+			SET_FLAG(TheFlag, pin);
+			message = _("Via with insufficient clearance inside polygon\n");
+			goto doIsBad;
+		}
+		break;
+	default:
+		Message(PCB_MSG_DEFAULT, "hace: Bad Plow object in callback\n");
+	}
+	return R_DIR_NOT_FOUND;
+
+doIsBad:
+	AddObjectToFlagUndoList(PCB_TYPE_POLYGON, layer, polygon, polygon);
+	SET_FLAG(PCB_FLAG_FOUND, polygon);
+	DrawPolygon(layer, polygon);
+	DrawObject(type, ptr1, ptr2);
+	drcerr_count++;
+	LocateError(&x, &y);
+	BuildObjectList(&object_count, &object_id_list, &object_type_list);
+	violation = pcb_drc_violation_new(message, _("Circuits that are too close may bridge during imaging, etching,\n" "plating, or soldering processes resulting in a direct short."), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																		FALSE,	/* MEASUREMENT OF ERROR UNKNOWN */
+																		0,	/* MAGNITUDE OF ERROR UNKNOWN */
+																		PCB->Bloat, object_count, object_id_list, object_type_list);
+	append_drc_violation(violation);
+	pcb_drc_violation_free(violation);
+	free(object_id_list);
+	free(object_type_list);
+	if (!throw_drc_dialog()) {
+		IsBad = pcb_true;
+		return R_DIR_FOUND_CONTINUE;
+	}
+	IncrementUndoSerialNumber();
+	Undo(pcb_true);
+	return R_DIR_NOT_FOUND;
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ * Check for DRC violations
+ * see if the connectivity changes when everything is bloated, or shrunk
+ */
+int DRCAll(void)
+{
+	Coord x, y;
+	int object_count;
+	long int *object_id_list;
+	int *object_type_list;
+	DrcViolationType *violation;
+	int tmpcnt;
+	int nopastecnt = 0;
+
+	reset_drc_dialog_message();
+
+	IsBad = pcb_false;
+	drcerr_count = 0;
+	SaveStackAndVisibility();
+	ResetStackAndVisibility();
+	hid_action("LayersChanged");
+	InitConnectionLookup();
+
+	TheFlag = PCB_FLAG_FOUND | PCB_FLAG_DRC | PCB_FLAG_SELECTED;
+
+	if (ResetConnections(pcb_true)) {
+		IncrementUndoSerialNumber();
+		Draw();
+	}
+
+	User = pcb_false;
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (!TEST_FLAG(PCB_FLAG_DRC, pin)
+					&& DRCFind(PCB_TYPE_PIN, (void *) element, (void *) pin, (void *) pin)) {
+				IsBad = pcb_true;
+				break;
+			}
+		}
+		END_LOOP;
+		if (IsBad)
+			break;
+		PAD_LOOP(element);
+		{
+
+			/* count up how many pads have no solderpaste openings */
+			if (TEST_FLAG(PCB_FLAG_NOPASTE, pad))
+				nopastecnt++;
+
+			if (!TEST_FLAG(PCB_FLAG_DRC, pad)
+					&& DRCFind(PCB_TYPE_PAD, (void *) element, (void *) pad, (void *) pad)) {
+				IsBad = pcb_true;
+				break;
+			}
+		}
+		END_LOOP;
+		if (IsBad)
+			break;
+	}
+	END_LOOP;
+	if (!IsBad)
+		VIA_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_DRC, via)
+				&& DRCFind(PCB_TYPE_VIA, (void *) via, (void *) via, (void *) via)) {
+			IsBad = pcb_true;
+			break;
+		}
+	}
+	END_LOOP;
+
+	TheFlag = (IsBad) ? PCB_FLAG_DRC : (PCB_FLAG_FOUND | PCB_FLAG_DRC | PCB_FLAG_SELECTED);
+	ResetConnections(pcb_false);
+	TheFlag = PCB_FLAG_SELECTED;
+	/* check minimum widths and polygon clearances */
+	if (!IsBad) {
+		COPPERLINE_LOOP(PCB->Data);
+		{
+			/* check line clearances in polygons */
+			PlowsPolygon(PCB->Data, PCB_TYPE_LINE, layer, line, drc_callback);
+			if (IsBad)
+				break;
+			if (line->Thickness < PCB->minWid) {
+				AddObjectToFlagUndoList(PCB_TYPE_LINE, layer, line, line);
+				SET_FLAG(TheFlag, line);
+				DrawLine(layer, line);
+				drcerr_count++;
+				SetThing(PCB_TYPE_LINE, layer, line, line);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Line width is too thin"), _("Process specifications dictate a minimum feature-width\n" "that can reliably be reproduced"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					line->Thickness, PCB->minWid, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+		}
+		ENDALL_LOOP;
+	}
+	if (!IsBad) {
+		COPPERARC_LOOP(PCB->Data);
+		{
+			PlowsPolygon(PCB->Data, PCB_TYPE_ARC, layer, arc, drc_callback);
+			if (IsBad)
+				break;
+			if (arc->Thickness < PCB->minWid) {
+				AddObjectToFlagUndoList(PCB_TYPE_ARC, layer, arc, arc);
+				SET_FLAG(TheFlag, arc);
+				DrawArc(layer, arc);
+				drcerr_count++;
+				SetThing(PCB_TYPE_ARC, layer, arc, arc);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Arc width is too thin"), _("Process specifications dictate a minimum feature-width\n" "that can reliably be reproduced"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					arc->Thickness, PCB->minWid, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+		}
+		ENDALL_LOOP;
+	}
+	if (!IsBad) {
+		ALLPIN_LOOP(PCB->Data);
+		{
+			PlowsPolygon(PCB->Data, PCB_TYPE_PIN, element, pin, drc_callback);
+			if (IsBad)
+				break;
+			if (!TEST_FLAG(PCB_FLAG_HOLE, pin) && pin->Thickness - pin->DrillingHole < 2 * PCB->minRing) {
+				AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+				SET_FLAG(TheFlag, pin);
+				DrawPin(pin);
+				drcerr_count++;
+				SetThing(PCB_TYPE_PIN, element, pin, pin);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Pin annular ring too small"), _("Annular rings that are too small may erode during etching,\n" "resulting in a broken connection"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					(pin->Thickness - pin->DrillingHole) / 2,
+																					PCB->minRing, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+			if (pin->DrillingHole < PCB->minDrill) {
+				AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+				SET_FLAG(TheFlag, pin);
+				DrawPin(pin);
+				drcerr_count++;
+				SetThing(PCB_TYPE_PIN, element, pin, pin);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Pin drill size is too small"), _("Process rules dictate the minimum drill size which can be used"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					pin->DrillingHole, PCB->minDrill, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+		}
+		ENDALL_LOOP;
+	}
+	if (!IsBad) {
+		ALLPAD_LOOP(PCB->Data);
+		{
+			PlowsPolygon(PCB->Data, PCB_TYPE_PAD, element, pad, drc_callback);
+			if (IsBad)
+				break;
+			if (pad->Thickness < PCB->minWid) {
+				AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+				SET_FLAG(TheFlag, pad);
+				DrawPad(pad);
+				drcerr_count++;
+				SetThing(PCB_TYPE_PAD, element, pad, pad);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Pad is too thin"), _("Pads which are too thin may erode during etching,\n" "resulting in a broken or unreliable connection"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					pad->Thickness, PCB->minWid, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+		}
+		ENDALL_LOOP;
+	}
+	if (!IsBad) {
+		VIA_LOOP(PCB->Data);
+		{
+			PlowsPolygon(PCB->Data, PCB_TYPE_VIA, via, via, drc_callback);
+			if (IsBad)
+				break;
+			if (!TEST_FLAG(PCB_FLAG_HOLE, via) && via->Thickness - via->DrillingHole < 2 * PCB->minRing) {
+				AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+				SET_FLAG(TheFlag, via);
+				DrawVia(via);
+				drcerr_count++;
+				SetThing(PCB_TYPE_VIA, via, via, via);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Via annular ring too small"), _("Annular rings that are too small may erode during etching,\n" "resulting in a broken connection"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					(via->Thickness - via->DrillingHole) / 2,
+																					PCB->minRing, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+			if (via->DrillingHole < PCB->minDrill) {
+				AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+				SET_FLAG(TheFlag, via);
+				DrawVia(via);
+				drcerr_count++;
+				SetThing(PCB_TYPE_VIA, via, via, via);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Via drill size is too small"), _("Process rules dictate the minimum drill size which can be used"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					via->DrillingHole, PCB->minDrill, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+				IncrementUndoSerialNumber();
+				Undo(pcb_false);
+			}
+		}
+		END_LOOP;
+	}
+
+	FreeConnectionLookupMemory();
+	TheFlag = PCB_FLAG_FOUND;
+	Bloat = 0;
+
+	/* check silkscreen minimum widths outside of elements */
+	/* XXX - need to check text and polygons too! */
+	TheFlag = PCB_FLAG_SELECTED;
+	if (!IsBad) {
+		SILKLINE_LOOP(PCB->Data);
+		{
+			if (line->Thickness < PCB->minSlk) {
+				SET_FLAG(TheFlag, line);
+				DrawLine(layer, line);
+				drcerr_count++;
+				SetThing(PCB_TYPE_LINE, layer, line, line);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+				violation = pcb_drc_violation_new(_("Silk line is too thin"), _("Process specifications dictate a minimum silkscreen feature-width\n" "that can reliably be reproduced"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					line->Thickness, PCB->minSlk, object_count, object_id_list, object_type_list);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+			}
+		}
+		ENDALL_LOOP;
+	}
+
+	/* check silkscreen minimum widths inside of elements */
+	/* XXX - need to check text and polygons too! */
+	TheFlag = PCB_FLAG_SELECTED;
+	if (!IsBad) {
+		ELEMENT_LOOP(PCB->Data);
+		{
+			tmpcnt = 0;
+			ELEMENTLINE_LOOP(element);
+			{
+				if (line->Thickness < PCB->minSlk)
+					tmpcnt++;
+			}
+			END_LOOP;
+			if (tmpcnt > 0) {
+				const char *title;
+				const char *name;
+				char *buffer;
+				int buflen;
+
+				SET_FLAG(TheFlag, element);
+				DrawElement(element);
+				drcerr_count++;
+				SetThing(PCB_TYPE_ELEMENT, element, element, element);
+				LocateError(&x, &y);
+				BuildObjectList(&object_count, &object_id_list, &object_type_list);
+
+				title = _("Element %s has %i silk lines which are too thin");
+				name = UNKNOWN(NAMEONPCB_NAME(element));
+
+				/* -4 is for the %s and %i place-holders */
+				/* +11 is the max printed length for a 32 bit integer */
+				/* +1 is for the \0 termination */
+				buflen = strlen(title) - 4 + strlen(name) + 11 + 1;
+				buffer = (char *) malloc(buflen);
+				pcb_snprintf(buffer, buflen, title, name, tmpcnt);
+
+				violation = pcb_drc_violation_new(buffer, _("Process specifications dictate a minimum silkscreen\n" "feature-width that can reliably be reproduced"), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																					TRUE,	/* MEASUREMENT OF ERROR KNOWN */
+																					0,	/* MINIMUM OFFENDING WIDTH UNKNOWN */
+																					PCB->minSlk, object_count, object_id_list, object_type_list);
+				free(buffer);
+				append_drc_violation(violation);
+				pcb_drc_violation_free(violation);
+				free(object_id_list);
+				free(object_type_list);
+				if (!throw_drc_dialog()) {
+					IsBad = pcb_true;
+					break;
+				}
+			}
+		}
+		END_LOOP;
+	}
+
+
+	if (IsBad) {
+		IncrementUndoSerialNumber();
+	}
+
+
+	RestoreStackAndVisibility();
+	hid_action("LayersChanged");
+	gui->invalidate_all();
+
+	if (nopastecnt > 0) {
+		Message(PCB_MSG_DEFAULT, _("Warning:  %d pad%s the nopaste flag set.\n"), nopastecnt, nopastecnt > 1 ? "s have" : " has");
+	}
+	return IsBad ? -drcerr_count : drcerr_count;
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ * Check for DRC violations on a single net starting from the pad or pin
+ * sees if the connectivity changes when everything is bloated, or shrunk
+ */
+static pcb_bool DRCFind(int What, void *ptr1, void *ptr2, void *ptr3)
+{
+	Coord x, y;
+	int object_count;
+	long int *object_id_list;
+	int *object_type_list;
+	DrcViolationType *violation;
+
+	if (PCB->Shrink != 0) {
+		Bloat = -PCB->Shrink;
+		TheFlag = PCB_FLAG_DRC | PCB_FLAG_SELECTED;
+		ListStart(What, ptr1, ptr2, ptr3);
+		DoIt(pcb_true, pcb_false);
+		/* ok now the shrunk net has the PCB_FLAG_SELECTED set */
+		DumpList();
+		TheFlag = PCB_FLAG_FOUND;
+		ListStart(What, ptr1, ptr2, ptr3);
+		Bloat = 0;
+		drc = pcb_true;									/* abort the search if we find anything not already found */
+		if (DoIt(pcb_true, pcb_false)) {
+			DumpList();
+			/* make the flag changes undoable */
+			TheFlag = PCB_FLAG_FOUND | PCB_FLAG_SELECTED;
+			ResetConnections(pcb_false);
+			User = pcb_true;
+			drc = pcb_false;
+			Bloat = -PCB->Shrink;
+			TheFlag = PCB_FLAG_SELECTED;
+			ListStart(What, ptr1, ptr2, ptr3);
+			DoIt(pcb_true, pcb_true);
+			DumpList();
+			ListStart(What, ptr1, ptr2, ptr3);
+			TheFlag = PCB_FLAG_FOUND;
+			Bloat = 0;
+			drc = pcb_true;
+			DoIt(pcb_true, pcb_true);
+			DumpList();
+			User = pcb_false;
+			drc = pcb_false;
+			drcerr_count++;
+			LocateError(&x, &y);
+			BuildObjectList(&object_count, &object_id_list, &object_type_list);
+			violation = pcb_drc_violation_new(_("Potential for broken trace"), _("Insufficient overlap between objects can lead to broken tracks\n" "due to registration errors with old wheel style photo-plotters."), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																				FALSE,	/* MEASUREMENT OF ERROR UNKNOWN */
+																				0,	/* MAGNITUDE OF ERROR UNKNOWN */
+																				PCB->Shrink, object_count, object_id_list, object_type_list);
+			append_drc_violation(violation);
+			pcb_drc_violation_free(violation);
+			free(object_id_list);
+			free(object_type_list);
+
+			if (!throw_drc_dialog())
+				return (pcb_true);
+			IncrementUndoSerialNumber();
+			Undo(pcb_true);
+		}
+		DumpList();
+	}
+	/* now check the bloated condition */
+	drc = pcb_false;
+	ResetConnections(pcb_false);
+	TheFlag = PCB_FLAG_FOUND;
+	ListStart(What, ptr1, ptr2, ptr3);
+	Bloat = PCB->Bloat;
+	drc = pcb_true;
+	while (DoIt(pcb_true, pcb_false)) {
+		DumpList();
+		/* make the flag changes undoable */
+		TheFlag = PCB_FLAG_FOUND | PCB_FLAG_SELECTED;
+		ResetConnections(pcb_false);
+		User = pcb_true;
+		drc = pcb_false;
+		Bloat = 0;
+		TheFlag = PCB_FLAG_SELECTED;
+		ListStart(What, ptr1, ptr2, ptr3);
+		DoIt(pcb_true, pcb_true);
+		DumpList();
+		TheFlag = PCB_FLAG_FOUND;
+		ListStart(What, ptr1, ptr2, ptr3);
+		Bloat = PCB->Bloat;
+		drc = pcb_true;
+		DoIt(pcb_true, pcb_true);
+		DumpList();
+		drcerr_count++;
+		LocateError(&x, &y);
+		BuildObjectList(&object_count, &object_id_list, &object_type_list);
+		violation = pcb_drc_violation_new(_("Copper areas too close"), _("Circuits that are too close may bridge during imaging, etching,\n" "plating, or soldering processes resulting in a direct short."), x, y, 0,	/* ANGLE OF ERROR UNKNOWN */
+																			FALSE,	/* MEASUREMENT OF ERROR UNKNOWN */
+																			0,	/* MAGNITUDE OF ERROR UNKNOWN */
+																			PCB->Bloat, object_count, object_id_list, object_type_list);
+		append_drc_violation(violation);
+		pcb_drc_violation_free(violation);
+		free(object_id_list);
+		free(object_type_list);
+		User = pcb_false;
+		drc = pcb_false;
+		if (!throw_drc_dialog())
+			return (pcb_true);
+		IncrementUndoSerialNumber();
+		Undo(pcb_true);
+		/* highlight the rest of the encroaching net so it's not reported again */
+		TheFlag |= PCB_FLAG_SELECTED;
+		Bloat = 0;
+		ListStart(thing_type, thing_ptr1, thing_ptr2, thing_ptr3);
+		DoIt(pcb_true, pcb_true);
+		DumpList();
+		drc = pcb_true;
+		Bloat = PCB->Bloat;
+		ListStart(What, ptr1, ptr2, ptr3);
+	}
+	drc = pcb_false;
+	DumpList();
+	TheFlag = PCB_FLAG_FOUND | PCB_FLAG_SELECTED;
+	ResetConnections(pcb_false);
+	return (pcb_false);
+}
+
+/*----------------------------------------------------------------------------
+ * center the display to show the offending item (thing)
+ */
+static void GotoError(void)
+{
+	Coord X, Y;
+
+	LocateError(&X, &Y);
+
+	switch (thing_type) {
+	case PCB_TYPE_LINE:
+	case PCB_TYPE_ARC:
+	case PCB_TYPE_POLYGON:
+		ChangeGroupVisibility(GetLayerNumber(PCB->Data, (LayerTypePtr) thing_ptr1), pcb_true, pcb_true);
+	}
+	CenterDisplay(X, Y);
+}
diff --git a/src/find_geo.c b/src/find_geo.c
new file mode 100644
index 0000000..fab96dd
--- /dev/null
+++ b/src/find_geo.c
@@ -0,0 +1,696 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* Generic geometric functions used by find.c lookup */
+
+/*
+ * Intersection of line <--> circle:
+ * - calculate the signed distance from the line to the center,
+ *   return false if abs(distance) > R
+ * - get the distance from the line <--> distancevector intersection to
+ *   (X1,Y1) in range [0,1], return pcb_true if 0 <= distance <= 1
+ * - depending on (r > 1.0 or r < 0.0) check the distance of X2,Y2 or X1,Y1
+ *   to X,Y
+ *
+ * Intersection of line <--> line:
+ * - see the description of 'LineLineIntersect()'
+ */
+
+#define EXPAND_BOUNDS(p) if (Bloat > 0) {\
+       (p)->BoundingBox.X1 -= Bloat; \
+       (p)->BoundingBox.X2 += Bloat; \
+       (p)->BoundingBox.Y1 -= Bloat; \
+       (p)->BoundingBox.Y2 += Bloat;}
+
+#define IS_PV_ON_RAT(PV, Rat) \
+	(IsPointOnLineEnd((PV)->X,(PV)->Y, (Rat)))
+
+#define IS_PV_ON_ARC(PV, Arc)	\
+	(TEST_FLAG(PCB_FLAG_SQUARE, (PV)) ? \
+		IsArcInRectangle( \
+			(PV)->X -MAX(((PV)->Thickness+1)/2 +Bloat,0), (PV)->Y -MAX(((PV)->Thickness+1)/2 +Bloat,0), \
+			(PV)->X +MAX(((PV)->Thickness+1)/2 +Bloat,0), (PV)->Y +MAX(((PV)->Thickness+1)/2 +Bloat,0), \
+			(Arc)) : \
+		IsPointOnArc((PV)->X,(PV)->Y,MAX((PV)->Thickness/2.0 + Bloat,0.0), (Arc)))
+
+#define	IS_PV_ON_PAD(PV,Pad) \
+	( IsPointInPad((PV)->X, (PV)->Y, MAX((PV)->Thickness/2 +Bloat,0), (Pad)))
+
+/* reduce arc start angle and delta to 0..360 */
+static void normalize_angles(Angle * sa, Angle * d)
+{
+	if (*d < 0) {
+		*sa += *d;
+		*d = -*d;
+	}
+	if (*d > 360)									/* full circle */
+		*d = 360;
+	*sa = NormalizeAngle(*sa);
+}
+
+static int radius_crosses_arc(double x, double y, ArcTypePtr arc)
+{
+	double alpha = atan2(y - arc->Y, -x + arc->X) * PCB_RAD_TO_DEG;
+	Angle sa = arc->StartAngle, d = arc->Delta;
+
+	normalize_angles(&sa, &d);
+	if (alpha < 0)
+		alpha += 360;
+	if (sa <= alpha)
+		return (sa + d) >= alpha;
+	return (sa + d - 360) >= alpha;
+}
+
+static void get_arc_ends(Coord * box, ArcTypePtr arc)
+{
+	box[0] = arc->X - arc->Width * cos(PCB_M180 * arc->StartAngle);
+	box[1] = arc->Y + arc->Height * sin(PCB_M180 * arc->StartAngle);
+	box[2] = arc->X - arc->Width * cos(PCB_M180 * (arc->StartAngle + arc->Delta));
+	box[3] = arc->Y + arc->Height * sin(PCB_M180 * (arc->StartAngle + arc->Delta));
+}
+
+/* ---------------------------------------------------------------------------
+ * check if two arcs intersect
+ * first we check for circle intersections,
+ * then find the actual points of intersection
+ * and test them to see if they are on arcs
+ *
+ * consider a, the distance from the center of arc 1
+ * to the point perpendicular to the intersecting points.
+ *
+ *  a = (r1^2 - r2^2 + l^2)/(2l)
+ *
+ * the perpendicular distance to the point of intersection
+ * is then
+ *
+ * d = sqrt(r1^2 - a^2)
+ *
+ * the points of intersection would then be
+ *
+ * x = X1 + a/l dx +- d/l dy
+ * y = Y1 + a/l dy -+ d/l dx
+ *
+ * where dx = X2 - X1 and dy = Y2 - Y1
+ *
+ *
+ */
+static pcb_bool ArcArcIntersect(ArcTypePtr Arc1, ArcTypePtr Arc2)
+{
+	double x, y, dx, dy, r1, r2, a, d, l, t, t1, t2, dl;
+	Coord pdx, pdy;
+	Coord box[8];
+
+	t = 0.5 * Arc1->Thickness + Bloat;
+	t2 = 0.5 * Arc2->Thickness;
+	t1 = t2 + Bloat;
+
+	/* too thin arc */
+	if (t < 0 || t1 < 0)
+		return pcb_false;
+
+	/* try the end points first */
+	get_arc_ends(&box[0], Arc1);
+	get_arc_ends(&box[4], Arc2);
+	if (IsPointOnArc(box[0], box[1], t, Arc2)
+			|| IsPointOnArc(box[2], box[3], t, Arc2)
+			|| IsPointOnArc(box[4], box[5], t, Arc1)
+			|| IsPointOnArc(box[6], box[7], t, Arc1))
+		return pcb_true;
+
+	pdx = Arc2->X - Arc1->X;
+	pdy = Arc2->Y - Arc1->Y;
+	dl = Distance(Arc1->X, Arc1->Y, Arc2->X, Arc2->Y);
+	/* concentric arcs, simpler intersection conditions */
+	if (dl < 0.5) {
+		if ((Arc1->Width - t >= Arc2->Width - t2 && Arc1->Width - t <= Arc2->Width + t2)
+				|| (Arc1->Width + t >= Arc2->Width - t2 && Arc1->Width + t <= Arc2->Width + t2)) {
+			Angle sa1 = Arc1->StartAngle, d1 = Arc1->Delta;
+			Angle sa2 = Arc2->StartAngle, d2 = Arc2->Delta;
+			/* NB the endpoints have already been checked,
+			   so we just compare the angles */
+
+			normalize_angles(&sa1, &d1);
+			normalize_angles(&sa2, &d2);
+			/* sa1 == sa2 was caught when checking endpoints */
+			if (sa1 > sa2)
+				if (sa1 < sa2 + d2 || sa1 + d1 - 360 > sa2)
+					return pcb_true;
+			if (sa2 > sa1)
+				if (sa2 < sa1 + d1 || sa2 + d2 - 360 > sa1)
+					return pcb_true;
+		}
+		return pcb_false;
+	}
+	r1 = Arc1->Width;
+	r2 = Arc2->Width;
+	/* arcs centerlines are too far or too near */
+	if (dl > r1 + r2 || dl + r1 < r2 || dl + r2 < r1) {
+		/* check the nearest to the other arc's center point */
+		dx = pdx * r1 / dl;
+		dy = pdy * r1 / dl;
+		if (dl + r1 < r2) {					/* Arc1 inside Arc2 */
+			dx = -dx;
+			dy = -dy;
+		}
+
+		if (radius_crosses_arc(Arc1->X + dx, Arc1->Y + dy, Arc1)
+				&& IsPointOnArc(Arc1->X + dx, Arc1->Y + dy, t, Arc2))
+			return pcb_true;
+
+		dx = -pdx * r2 / dl;
+		dy = -pdy * r2 / dl;
+		if (dl + r2 < r1) {					/* Arc2 inside Arc1 */
+			dx = -dx;
+			dy = -dy;
+		}
+
+		if (radius_crosses_arc(Arc2->X + dx, Arc2->Y + dy, Arc2)
+				&& IsPointOnArc(Arc2->X + dx, Arc2->Y + dy, t1, Arc1))
+			return pcb_true;
+		return pcb_false;
+	}
+
+	l = dl * dl;
+	r1 *= r1;
+	r2 *= r2;
+	a = 0.5 * (r1 - r2 + l) / l;
+	r1 = r1 / l;
+	d = r1 - a * a;
+	/* the circles are too far apart to touch or probably just touch:
+	   check the nearest point */
+	if (d < 0)
+		d = 0;
+	else
+		d = sqrt(d);
+	x = Arc1->X + a * pdx;
+	y = Arc1->Y + a * pdy;
+	dx = d * pdx;
+	dy = d * pdy;
+	if (radius_crosses_arc(x + dy, y - dx, Arc1)
+			&& IsPointOnArc(x + dy, y - dx, t, Arc2))
+		return pcb_true;
+	if (radius_crosses_arc(x + dy, y - dx, Arc2)
+			&& IsPointOnArc(x + dy, y - dx, t1, Arc1))
+		return pcb_true;
+
+	if (radius_crosses_arc(x - dy, y + dx, Arc1)
+			&& IsPointOnArc(x - dy, y + dx, t, Arc2))
+		return pcb_true;
+	if (radius_crosses_arc(x - dy, y + dx, Arc2)
+			&& IsPointOnArc(x - dy, y + dx, t1, Arc1))
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * Tests if point is same as line end point
+ */
+static pcb_bool IsRatPointOnLineEnd(PointTypePtr Point, LineTypePtr Line)
+{
+	if ((Point->X == Line->Point1.X && Point->Y == Line->Point1.Y)
+			|| (Point->X == Line->Point2.X && Point->Y == Line->Point2.Y))
+		return (pcb_true);
+	return (pcb_false);
+}
+
+static void form_slanted_rectangle(PointType p[4], LineTypePtr l)
+/* writes vertices of a squared line */
+{
+	double dwx = 0, dwy = 0;
+	if (l->Point1.Y == l->Point2.Y)
+		dwx = l->Thickness / 2.0;
+	else if (l->Point1.X == l->Point2.X)
+		dwy = l->Thickness / 2.0;
+	else {
+		Coord dX = l->Point2.X - l->Point1.X;
+		Coord dY = l->Point2.Y - l->Point1.Y;
+		double r = Distance(l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
+		dwx = l->Thickness / 2.0 / r * dX;
+		dwy = l->Thickness / 2.0 / r * dY;
+	}
+	p[0].X = l->Point1.X - dwx + dwy;
+	p[0].Y = l->Point1.Y - dwy - dwx;
+	p[1].X = l->Point1.X - dwx - dwy;
+	p[1].Y = l->Point1.Y - dwy + dwx;
+	p[2].X = l->Point2.X + dwx - dwy;
+	p[2].Y = l->Point2.Y + dwy + dwx;
+	p[3].X = l->Point2.X + dwx + dwy;
+	p[3].Y = l->Point2.Y + dwy - dwx;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if two lines intersect
+ * from news FAQ:
+ *
+ *  Let A,B,C,D be 2-space position vectors.  Then the directed line
+ *  segments AB & CD are given by:
+ *
+ *      AB=A+r(B-A), r in [0,1]
+ *      CD=C+s(D-C), s in [0,1]
+ *
+ *  If AB & CD intersect, then
+ *
+ *      A+r(B-A)=C+s(D-C), or
+ *
+ *      XA+r(XB-XA)=XC+s(XD-XC)
+ *      YA+r(YB-YA)=YC+s(YD-YC)  for some r,s in [0,1]
+ *
+ *  Solving the above for r and s yields
+ *
+ *          (YA-YC)(XD-XC)-(XA-XC)(YD-YC)
+ *      r = -----------------------------  (eqn 1)
+ *          (XB-XA)(YD-YC)-(YB-YA)(XD-XC)
+ *
+ *          (YA-YC)(XB-XA)-(XA-XC)(YB-YA)
+ *      s = -----------------------------  (eqn 2)
+ *          (XB-XA)(YD-YC)-(YB-YA)(XD-XC)
+ *
+ *  Let I be the position vector of the intersection point, then
+ *
+ *      I=A+r(B-A) or
+ *
+ *      XI=XA+r(XB-XA)
+ *      YI=YA+r(YB-YA)
+ *
+ *  By examining the values of r & s, you can also determine some
+ *  other limiting conditions:
+ *
+ *      If 0<=r<=1 & 0<=s<=1, intersection exists
+ *          r<0 or r>1 or s<0 or s>1 line segments do not intersect
+ *
+ *      If the denominator in eqn 1 is zero, AB & CD are parallel
+ *      If the numerator in eqn 1 is also zero, AB & CD are coincident
+ *
+ *  If the intersection point of the 2 lines are needed (lines in this
+ *  context mean infinite lines) regardless whether the two line
+ *  segments intersect, then
+ *
+ *      If r>1, I is located on extension of AB
+ *      If r<0, I is located on extension of BA
+ *      If s>1, I is located on extension of CD
+ *      If s<0, I is located on extension of DC
+ *
+ *  Also note that the denominators of eqn 1 & 2 are identical.
+ *
+ */
+pcb_bool LineLineIntersect(LineTypePtr Line1, LineTypePtr Line2)
+{
+	double s, r;
+	double line1_dx, line1_dy, line2_dx, line2_dy, point1_dx, point1_dy;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Line1)) {	/* pretty reckless recursion */
+		PointType p[4];
+		form_slanted_rectangle(p, Line1);
+		return IsLineInQuadrangle(p, Line2);
+	}
+	/* here come only round Line1 because IsLineInQuadrangle()
+	   calls LineLineIntersect() with first argument rounded */
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Line2)) {
+		PointType p[4];
+		form_slanted_rectangle(p, Line2);
+		return IsLineInQuadrangle(p, Line1);
+	}
+	/* now all lines are round */
+
+	/* Check endpoints: this provides a quick exit, catches
+	 *  cases where the "real" lines don't intersect but the
+	 *  thick lines touch, and ensures that the dx/dy business
+	 *  below does not cause a divide-by-zero. */
+	if (IsPointInPad(Line2->Point1.X, Line2->Point1.Y, MAX(Line2->Thickness / 2 + Bloat, 0), (PadTypePtr) Line1)
+			|| IsPointInPad(Line2->Point2.X, Line2->Point2.Y, MAX(Line2->Thickness / 2 + Bloat, 0), (PadTypePtr) Line1)
+			|| IsPointInPad(Line1->Point1.X, Line1->Point1.Y, MAX(Line1->Thickness / 2 + Bloat, 0), (PadTypePtr) Line2)
+			|| IsPointInPad(Line1->Point2.X, Line1->Point2.Y, MAX(Line1->Thickness / 2 + Bloat, 0), (PadTypePtr) Line2))
+		return pcb_true;
+
+	/* setup some constants */
+	line1_dx = Line1->Point2.X - Line1->Point1.X;
+	line1_dy = Line1->Point2.Y - Line1->Point1.Y;
+	line2_dx = Line2->Point2.X - Line2->Point1.X;
+	line2_dy = Line2->Point2.Y - Line2->Point1.Y;
+	point1_dx = Line1->Point1.X - Line2->Point1.X;
+	point1_dy = Line1->Point1.Y - Line2->Point1.Y;
+
+	/* If either line is a point, we have failed already, since the
+	 *   endpoint check above will have caught an "intersection". */
+	if ((line1_dx == 0 && line1_dy == 0)
+			|| (line2_dx == 0 && line2_dy == 0))
+		return pcb_false;
+
+	/* set s to cross product of Line1 and the line
+	 *   Line1.Point1--Line2.Point1 (as vectors) */
+	s = point1_dy * line1_dx - point1_dx * line1_dy;
+
+	/* set r to cross product of both lines (as vectors) */
+	r = line1_dx * line2_dy - line1_dy * line2_dx;
+
+	/* No cross product means parallel lines, or maybe Line2 is
+	 *  zero-length. In either case, since we did a bounding-box
+	 *  check before getting here, the above IsPointInPad() checks
+	 *  will have caught any intersections. */
+	if (r == 0.0)
+		return pcb_false;
+
+	s /= r;
+	r = (point1_dy * line2_dx - point1_dx * line2_dy) / r;
+
+	/* intersection is at least on AB */
+	if (r >= 0.0 && r <= 1.0)
+		return (s >= 0.0 && s <= 1.0);
+
+	/* intersection is at least on CD */
+	/* [removed this case since it always returns pcb_false --asp] */
+	return pcb_false;
+}
+
+/*---------------------------------------------------
+ *
+ * Check for line intersection with an arc
+ *
+ * Mostly this is like the circle/line intersection
+ * found in IsPointOnLine (search.c) see the detailed
+ * discussion for the basics there.
+ *
+ * Since this is only an arc, not a full circle we need
+ * to find the actual points of intersection with the
+ * circle, and see if they are on the arc.
+ *
+ * To do this, we translate along the line from the point Q
+ * plus or minus a distance delta = sqrt(Radius^2 - d^2)
+ * but it's handy to normalize with respect to l, the line
+ * length so a single projection is done (e.g. we don't first
+ * find the point Q
+ *
+ * The projection is now of the form
+ *
+ *      Px = X1 + (r +- r2)(X2 - X1)
+ *      Py = Y1 + (r +- r2)(Y2 - Y1)
+ *
+ * Where r2 sqrt(Radius^2 l^2 - d^2)/l^2
+ * note that this is the variable d, not the symbol d described in IsPointOnLine
+ * (variable d = symbol d * l)
+ *
+ * The end points are hell so they are checked individually
+ */
+pcb_bool LineArcIntersect(LineTypePtr Line, ArcTypePtr Arc)
+{
+	double dx, dy, dx1, dy1, l, d, r, r2, Radius;
+	BoxTypePtr box;
+
+	dx = Line->Point2.X - Line->Point1.X;
+	dy = Line->Point2.Y - Line->Point1.Y;
+	dx1 = Line->Point1.X - Arc->X;
+	dy1 = Line->Point1.Y - Arc->Y;
+	l = dx * dx + dy * dy;
+	d = dx * dy1 - dy * dx1;
+	d *= d;
+
+	/* use the larger diameter circle first */
+	Radius = Arc->Width + MAX(0.5 * (Arc->Thickness + Line->Thickness) + Bloat, 0.0);
+	Radius *= Radius;
+	r2 = Radius * l - d;
+	/* projection doesn't even intersect circle when r2 < 0 */
+	if (r2 < 0)
+		return (pcb_false);
+	/* check the ends of the line in case the projected point */
+	/* of intersection is beyond the line end */
+	if (IsPointOnArc(Line->Point1.X, Line->Point1.Y, MAX(0.5 * Line->Thickness + Bloat, 0.0), Arc))
+		return (pcb_true);
+	if (IsPointOnArc(Line->Point2.X, Line->Point2.Y, MAX(0.5 * Line->Thickness + Bloat, 0.0), Arc))
+		return (pcb_true);
+	if (l == 0.0)
+		return (pcb_false);
+	r2 = sqrt(r2);
+	Radius = -(dx * dx1 + dy * dy1);
+	r = (Radius + r2) / l;
+	if (r >= 0 && r <= 1
+			&& IsPointOnArc(Line->Point1.X + r * dx, Line->Point1.Y + r * dy, MAX(0.5 * Line->Thickness + Bloat, 0.0), Arc))
+		return (pcb_true);
+	r = (Radius - r2) / l;
+	if (r >= 0 && r <= 1
+			&& IsPointOnArc(Line->Point1.X + r * dx, Line->Point1.Y + r * dy, MAX(0.5 * Line->Thickness + Bloat, 0.0), Arc))
+		return (pcb_true);
+	/* check arc end points */
+	box = GetArcEnds(Arc);
+	if (IsPointInPad(box->X1, box->Y1, Arc->Thickness * 0.5 + Bloat, (PadTypePtr) Line))
+		return pcb_true;
+	if (IsPointInPad(box->X2, box->Y2, Arc->Thickness * 0.5 + Bloat, (PadTypePtr) Line))
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if an arc has a connection to a polygon
+ *
+ * - first check if the arc can intersect with the polygon by
+ *   evaluating the bounding boxes
+ * - check the two end points of the arc. If none of them matches
+ * - check all segments of the polygon against the arc.
+ */
+pcb_bool IsArcInPolygon(ArcTypePtr Arc, PolygonTypePtr Polygon)
+{
+	BoxTypePtr Box = (BoxType *) Arc;
+
+	/* arcs with clearance never touch polys */
+	if (TEST_FLAG(PCB_FLAG_CLEARPOLY, Polygon) && TEST_FLAG(PCB_FLAG_CLEARLINE, Arc))
+		return pcb_false;
+	if (!Polygon->Clipped)
+		return pcb_false;
+	if (Box->X1 <= Polygon->Clipped->contours->xmax + Bloat
+			&& Box->X2 >= Polygon->Clipped->contours->xmin - Bloat
+			&& Box->Y1 <= Polygon->Clipped->contours->ymax + Bloat && Box->Y2 >= Polygon->Clipped->contours->ymin - Bloat) {
+		POLYAREA *ap;
+
+		if (!(ap = ArcPoly(Arc, Arc->Thickness + Bloat)))
+			return pcb_false;							/* error */
+		return isects(ap, Polygon, pcb_true);
+	}
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a line has a connection to a polygon
+ *
+ * - first check if the line can intersect with the polygon by
+ *   evaluating the bounding boxes
+ * - check the two end points of the line. If none of them matches
+ * - check all segments of the polygon against the line.
+ */
+pcb_bool IsLineInPolygon(LineTypePtr Line, PolygonTypePtr Polygon)
+{
+	BoxTypePtr Box = (BoxType *) Line;
+	POLYAREA *lp;
+
+	/* lines with clearance never touch polygons */
+	if (TEST_FLAG(PCB_FLAG_CLEARPOLY, Polygon) && TEST_FLAG(PCB_FLAG_CLEARLINE, Line))
+		return pcb_false;
+	if (!Polygon->Clipped)
+		return pcb_false;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Line) && (Line->Point1.X == Line->Point2.X || Line->Point1.Y == Line->Point2.Y)) {
+		Coord wid = (Line->Thickness + Bloat + 1) / 2;
+		Coord x1, x2, y1, y2;
+
+		x1 = MIN(Line->Point1.X, Line->Point2.X) - wid;
+		y1 = MIN(Line->Point1.Y, Line->Point2.Y) - wid;
+		x2 = MAX(Line->Point1.X, Line->Point2.X) + wid;
+		y2 = MAX(Line->Point1.Y, Line->Point2.Y) + wid;
+		return IsRectangleInPolygon(x1, y1, x2, y2, Polygon);
+	}
+	if (Box->X1 <= Polygon->Clipped->contours->xmax + Bloat
+			&& Box->X2 >= Polygon->Clipped->contours->xmin - Bloat
+			&& Box->Y1 <= Polygon->Clipped->contours->ymax + Bloat && Box->Y2 >= Polygon->Clipped->contours->ymin - Bloat) {
+		if (!(lp = LinePoly(Line, Line->Thickness + Bloat)))
+			return FALSE;							/* error */
+		return isects(lp, Polygon, pcb_true);
+	}
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a pad connects to a non-clearing polygon
+ *
+ * The polygon is assumed to already have been proven non-clearing
+ */
+pcb_bool IsPadInPolygon(PadTypePtr pad, PolygonTypePtr polygon)
+{
+	return IsLineInPolygon((LineTypePtr) pad, polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a polygon has a connection to a second one
+ *
+ * First check all points out of P1 against P2 and vice versa.
+ * If both fail check all lines of P1 against the ones of P2
+ */
+pcb_bool IsPolygonInPolygon(PolygonTypePtr P1, PolygonTypePtr P2)
+{
+	if (!P1->Clipped || !P2->Clipped)
+		return pcb_false;
+	assert(P1->Clipped->contours);
+	assert(P2->Clipped->contours);
+
+	/* first check if both bounding boxes intersect. If not, return quickly */
+	if (P1->Clipped->contours->xmin - Bloat > P2->Clipped->contours->xmax ||
+			P1->Clipped->contours->xmax + Bloat < P2->Clipped->contours->xmin ||
+			P1->Clipped->contours->ymin - Bloat > P2->Clipped->contours->ymax ||
+			P1->Clipped->contours->ymax + Bloat < P2->Clipped->contours->ymin)
+		return pcb_false;
+
+	/* first check un-bloated case */
+	if (isects(P1->Clipped, P2, pcb_false))
+		return TRUE;
+
+	/* now the difficult case of bloated */
+	if (Bloat > 0) {
+		PLINE *c;
+		for (c = P1->Clipped->contours; c; c = c->next) {
+			LineType line;
+			VNODE *v = &c->head;
+			if (c->xmin - Bloat <= P2->Clipped->contours->xmax &&
+					c->xmax + Bloat >= P2->Clipped->contours->xmin &&
+					c->ymin - Bloat <= P2->Clipped->contours->ymax && c->ymax + Bloat >= P2->Clipped->contours->ymin) {
+
+				line.Point1.X = v->point[0];
+				line.Point1.Y = v->point[1];
+				line.Thickness = 2 * Bloat;
+				line.Clearance = 0;
+				line.Flags = NoFlags();
+				for (v = v->next; v != &c->head; v = v->next) {
+					line.Point2.X = v->point[0];
+					line.Point2.Y = v->point[1];
+					SetLineBoundingBox(&line);
+					if (IsLineInPolygon(&line, P2))
+						return (pcb_true);
+					line.Point1.X = line.Point2.X;
+					line.Point1.Y = line.Point2.Y;
+				}
+			}
+		}
+	}
+
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * some of the 'pad' routines are the same as for lines because the 'pad'
+ * struct starts with a line struct. See global.h for details
+ */
+pcb_bool LinePadIntersect(LineTypePtr Line, PadTypePtr Pad)
+{
+	return LineLineIntersect((Line), (LineTypePtr) Pad);
+}
+
+pcb_bool ArcPadIntersect(ArcTypePtr Arc, PadTypePtr Pad)
+{
+	return LineArcIntersect((LineTypePtr) (Pad), (Arc));
+}
+
+pcb_bool BoxBoxIntersection(BoxTypePtr b1, BoxTypePtr b2)
+{
+	if (b2->X2 < b1->X1 || b2->X1 > b1->X2)
+		return pcb_false;
+	if (b2->Y2 < b1->Y1 || b2->Y1 > b1->Y2)
+		return pcb_false;
+	return pcb_true;
+}
+
+static pcb_bool PadPadIntersect(PadTypePtr p1, PadTypePtr p2)
+{
+	return LinePadIntersect((LineTypePtr) p1, p2);
+}
+
+static inline pcb_bool PV_TOUCH_PV(PinTypePtr PV1, PinTypePtr PV2)
+{
+	double t1, t2;
+	BoxType b1, b2;
+	int shape1, shape2;
+
+	shape1 = GET_SQUARE(PV1);
+	shape2 = GET_SQUARE(PV2);
+
+	if ((shape1 > 1) || (shape2 > 1)) {
+		POLYAREA *pl1, *pl2;
+		int ret;
+
+		pl1 = PinPoly(PV1, PIN_SIZE(PV1) + Bloat, 0);
+		pl2 = PinPoly(PV2, PIN_SIZE(PV2) + Bloat, 0);
+		ret = Touching(pl1, pl2);
+		poly_Free(&pl1);
+		poly_Free(&pl2);
+		return ret;
+	}
+
+
+	t1 = MAX(PV1->Thickness / 2.0 + Bloat, 0);
+	t2 = MAX(PV2->Thickness / 2.0 + Bloat, 0);
+	if (IsPointOnPin(PV1->X, PV1->Y, t1, PV2)
+			|| IsPointOnPin(PV2->X, PV2->Y, t2, PV1))
+		return pcb_true;
+	if (!TEST_FLAG(PCB_FLAG_SQUARE, PV1) || !TEST_FLAG(PCB_FLAG_SQUARE, PV2))
+		return pcb_false;
+	/* check for square/square overlap */
+	b1.X1 = PV1->X - t1;
+	b1.X2 = PV1->X + t1;
+	b1.Y1 = PV1->Y - t1;
+	b1.Y2 = PV1->Y + t1;
+	t2 = PV2->Thickness / 2.0;
+	b2.X1 = PV2->X - t2;
+	b2.X2 = PV2->X + t2;
+	b2.Y1 = PV2->Y - t2;
+	b2.Y2 = PV2->Y + t2;
+	return BoxBoxIntersection(&b1, &b2);
+}
+
+pcb_bool PinLineIntersect(PinTypePtr PV, LineTypePtr Line)
+{
+	if (TEST_FLAG(PCB_FLAG_SQUARE, PV)) {
+		int shape = GET_SQUARE(PV);
+		if (shape <= 1) {
+			/* the original square case */
+			/* IsLineInRectangle already has Bloat factor */
+			return IsLineInRectangle(PV->X - (PIN_SIZE(PV) + 1) / 2,
+															 PV->Y - (PIN_SIZE(PV) + 1) / 2,
+															 PV->X + (PIN_SIZE(PV) + 1) / 2, PV->Y + (PIN_SIZE(PV) + 1) / 2, Line);
+		}
+
+		{
+			/* shaped pin case */
+			POLYAREA *pl, *lp;
+			int ret;
+
+			pl = PinPoly(PV, PIN_SIZE(PV), 0);
+			lp = LinePoly(Line, Line->Thickness + Bloat);
+			ret = Touching(lp, pl);
+			poly_Free(&pl);
+			poly_Free(&lp);
+			return ret;
+		}
+
+	}
+
+
+	/* the original round pin version */
+	return IsPointInPad(PV->X, PV->Y, MAX(PIN_SIZE(PV) / 2.0 + Bloat, 0.0), (PadTypePtr) Line);
+}
diff --git a/src/find_lookup.c b/src/find_lookup.c
new file mode 100644
index 0000000..f8360b2
--- /dev/null
+++ b/src/find_lookup.c
@@ -0,0 +1,1425 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+static inline r_dir_t r_search_pt(rtree_t * rtree, const PointType * pt,
+															int radius,
+															r_dir_t (*region_in_search) (const BoxType * region, void *cl),
+															r_dir_t (*rectangle_in_region) (const BoxType * box, void *cl), void *closure,
+															int *num_found)
+{
+	BoxType box;
+
+	box.X1 = pt->X - radius;
+	box.X2 = pt->X + radius;
+	box.Y1 = pt->Y - radius;
+	box.Y2 = pt->Y + radius;
+
+	return r_search(rtree, &box, region_in_search, rectangle_in_region, closure, num_found);
+}
+
+
+/* Connection lookup functions */
+
+static pcb_bool ADD_PV_TO_LIST(PinTypePtr Pin, int from_type, void *from_ptr, found_conn_type_t type)
+{
+	if (User)
+		AddObjectToFlagUndoList(Pin->Element ? PCB_TYPE_PIN : PCB_TYPE_VIA, Pin->Element ? Pin->Element : Pin, Pin, Pin);
+	SET_FLAG(TheFlag, Pin);
+	make_callback(PCB_TYPE_PIN, Pin, from_type, from_ptr, type);
+	PVLIST_ENTRY(PVList.Number) = Pin;
+	PVList.Number++;
+#ifdef DEBUG
+	if (PVList.Number > PVList.Size)
+		printf("ADD_PV_TO_LIST overflow! num=%d size=%d\n", PVList.Number, PVList.Size);
+#endif
+	if (drc && !TEST_FLAG(PCB_FLAG_SELECTED, Pin))
+		return (SetThing(PCB_TYPE_PIN, Pin->Element, Pin, Pin));
+	return pcb_false;
+}
+
+static pcb_bool ADD_PAD_TO_LIST(pcb_cardinal_t L, PadTypePtr Pad, int from_type, void *from_ptr, found_conn_type_t type)
+{
+/*fprintf(stderr, "ADD_PAD_TO_LIST cardinal %d %p %d\n", L, Pad, from_type);*/
+	if (User)
+		AddObjectToFlagUndoList(PCB_TYPE_PAD, Pad->Element, Pad, Pad);
+	SET_FLAG(TheFlag, Pad);
+	make_callback(PCB_TYPE_PAD, Pad, from_type, from_ptr, type);
+	PADLIST_ENTRY((L), PadList[(L)].Number) = Pad;
+	PadList[(L)].Number++;
+#ifdef DEBUG
+	if (PadList[(L)].Number > PadList[(L)].Size)
+		printf("ADD_PAD_TO_LIST overflow! lay=%d, num=%d size=%d\n", L, PadList[(L)].Number, PadList[(L)].Size);
+#endif
+	if (drc && !TEST_FLAG(PCB_FLAG_SELECTED, Pad))
+		return (SetThing(PCB_TYPE_PAD, Pad->Element, Pad, Pad));
+	return pcb_false;
+}
+
+static pcb_bool ADD_LINE_TO_LIST(pcb_cardinal_t L, LineTypePtr Ptr, int from_type, void *from_ptr, found_conn_type_t type)
+{
+	if (User)
+		AddObjectToFlagUndoList(PCB_TYPE_LINE, LAYER_PTR(L), (Ptr), (Ptr));
+	SET_FLAG(TheFlag, (Ptr));
+	make_callback(PCB_TYPE_LINE, Ptr, from_type, from_ptr, type);
+	LINELIST_ENTRY((L), LineList[(L)].Number) = (Ptr);
+	LineList[(L)].Number++;
+#ifdef DEBUG
+	if (LineList[(L)].Number > LineList[(L)].Size)
+		printf("ADD_LINE_TO_LIST overflow! lay=%d, num=%d size=%d\n", L, LineList[(L)].Number, LineList[(L)].Size);
+#endif
+	if (drc && !TEST_FLAG(PCB_FLAG_SELECTED, (Ptr)))
+		return (SetThing(PCB_TYPE_LINE, LAYER_PTR(L), (Ptr), (Ptr)));
+	return pcb_false;
+}
+
+static pcb_bool ADD_ARC_TO_LIST(pcb_cardinal_t L, ArcTypePtr Ptr, int from_type, void *from_ptr, found_conn_type_t type)
+{
+	if (User)
+		AddObjectToFlagUndoList(PCB_TYPE_ARC, LAYER_PTR(L), (Ptr), (Ptr));
+	SET_FLAG(TheFlag, (Ptr));
+	make_callback(PCB_TYPE_ARC, Ptr, from_type, from_ptr, type);
+	ARCLIST_ENTRY((L), ArcList[(L)].Number) = (Ptr);
+	ArcList[(L)].Number++;
+#ifdef DEBUG
+	if (ArcList[(L)].Number > ArcList[(L)].Size)
+		printf("ADD_ARC_TO_LIST overflow! lay=%d, num=%d size=%d\n", L, ArcList[(L)].Number, ArcList[(L)].Size);
+#endif
+	if (drc && !TEST_FLAG(PCB_FLAG_SELECTED, (Ptr)))
+		return (SetThing(PCB_TYPE_ARC, LAYER_PTR(L), (Ptr), (Ptr)));
+	return pcb_false;
+}
+
+static pcb_bool ADD_RAT_TO_LIST(RatTypePtr Ptr, int from_type, void *from_ptr, found_conn_type_t type)
+{
+	if (User)
+		AddObjectToFlagUndoList(PCB_TYPE_RATLINE, (Ptr), (Ptr), (Ptr));
+	SET_FLAG(TheFlag, (Ptr));
+	make_callback(PCB_TYPE_RATLINE, Ptr, from_type, from_ptr, type);
+	RATLIST_ENTRY(RatList.Number) = (Ptr);
+	RatList.Number++;
+#ifdef DEBUG
+	if (RatList.Number > RatList.Size)
+		printf("ADD_RAT_TO_LIST overflow! num=%d size=%d\n", RatList.Number, RatList.Size);
+#endif
+	if (drc && !TEST_FLAG(PCB_FLAG_SELECTED, (Ptr)))
+		return (SetThing(PCB_TYPE_RATLINE, (Ptr), (Ptr), (Ptr)));
+	return pcb_false;
+}
+
+static pcb_bool ADD_POLYGON_TO_LIST(pcb_cardinal_t L, PolygonTypePtr Ptr, int from_type, void *from_ptr, found_conn_type_t type)
+{
+	if (User)
+		AddObjectToFlagUndoList(PCB_TYPE_POLYGON, LAYER_PTR(L), (Ptr), (Ptr));
+	SET_FLAG(TheFlag, (Ptr));
+	make_callback(PCB_TYPE_POLYGON, Ptr, from_type, from_ptr, type);
+	POLYGONLIST_ENTRY((L), PolygonList[(L)].Number) = (Ptr);
+	PolygonList[(L)].Number++;
+#ifdef DEBUG
+	if (PolygonList[(L)].Number > PolygonList[(L)].Size)
+		printf("ADD_ARC_TO_LIST overflow! lay=%d, num=%d size=%d\n", L, PolygonList[(L)].Number, PolygonList[(L)].Size);
+#endif
+	if (drc && !TEST_FLAG(PCB_FLAG_SELECTED, (Ptr)))
+		return (SetThing(PCB_TYPE_POLYGON, LAYER_PTR(L), (Ptr), (Ptr)));
+	return pcb_false;
+}
+
+pcb_bool SetThing(int type, void *ptr1, void *ptr2, void *ptr3)
+{
+	thing_ptr1 = ptr1;
+	thing_ptr2 = ptr2;
+	thing_ptr3 = ptr3;
+	thing_type = type;
+	if (type == PCB_TYPE_PIN && ptr1 == NULL) {
+		thing_ptr1 = ptr3;
+		thing_type = PCB_TYPE_VIA;
+	}
+	return pcb_true;
+}
+
+/* ---------------------------------------------------------------------------
+ * releases all allocated memory
+ */
+void FreeLayoutLookupMemory(void)
+{
+	pcb_cardinal_t i;
+
+	for (i = 0; i < max_copper_layer; i++) {
+		free(LineList[i].Data);
+		LineList[i].Data = NULL;
+		free(ArcList[i].Data);
+		ArcList[i].Data = NULL;
+		free(PolygonList[i].Data);
+		PolygonList[i].Data = NULL;
+	}
+	free(PVList.Data);
+	PVList.Data = NULL;
+	free(RatList.Data);
+	RatList.Data = NULL;
+}
+
+void FreeComponentLookupMemory(void)
+{
+/*fprintf(stderr, "PadList free both\n");*/
+	free(PadList[0].Data);
+	PadList[0].Data = NULL;
+	free(PadList[1].Data);
+	PadList[1].Data = NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * allocates memory for component related stacks ...
+ * initializes index and sorts it by X1 and X2
+ */
+void InitComponentLookup(void)
+{
+	pcb_cardinal_t i;
+
+	/* initialize pad data; start by counting the total number
+	 * on each of the two possible layers
+	 */
+	NumberOfPads[COMPONENT_LAYER] = NumberOfPads[SOLDER_LAYER] = 0;
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_ONSOLDER, pad))
+			NumberOfPads[SOLDER_LAYER]++;
+		else
+			NumberOfPads[COMPONENT_LAYER]++;
+	}
+	ENDALL_LOOP;
+	for (i = 0; i < 2; i++) {
+/*fprintf(stderr, "PadList alloc %d: %d\n", i, NumberOfPads[i]);*/
+
+		/* allocate memory for working list */
+		PadList[i].Data = (void **) calloc(NumberOfPads[i], sizeof(PadTypePtr));
+
+		/* clear some struct members */
+		PadList[i].Location = 0;
+		PadList[i].DrawLocation = 0;
+		PadList[i].Number = 0;
+		PadList[i].Size = NumberOfPads[i];
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * allocates memory for component related stacks ...
+ * initializes index and sorts it by X1 and X2
+ */
+void InitLayoutLookup(void)
+{
+	pcb_cardinal_t i;
+
+	/* initialize line arc and polygon data */
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerTypePtr layer = LAYER_PTR(i);
+
+		if (linelist_length(&layer->Line)) {
+			LineList[i].Size = linelist_length(&layer->Line);
+			LineList[i].Data = (void **) calloc(LineList[i].Size, sizeof(LineTypePtr));
+		}
+		if (arclist_length(&layer->Arc)) {
+			ArcList[i].Size = arclist_length(&layer->Arc);
+			ArcList[i].Data = (void **) calloc(ArcList[i].Size, sizeof(ArcTypePtr));
+		}
+		if (polylist_length(&layer->Polygon)) {
+			PolygonList[i].Size = polylist_length(&layer->Polygon);
+			PolygonList[i].Data = (void **) calloc(PolygonList[i].Size, sizeof(PolygonTypePtr));
+		}
+
+		/* clear some struct members */
+		LineList[i].Location = 0;
+		LineList[i].DrawLocation = 0;
+		LineList[i].Number = 0;
+		ArcList[i].Location = 0;
+		ArcList[i].DrawLocation = 0;
+		ArcList[i].Number = 0;
+		PolygonList[i].Location = 0;
+		PolygonList[i].DrawLocation = 0;
+		PolygonList[i].Number = 0;
+	}
+
+	if (PCB->Data->pin_tree)
+		TotalP = PCB->Data->pin_tree->size;
+	else
+		TotalP = 0;
+	if (PCB->Data->via_tree)
+		TotalV = PCB->Data->via_tree->size;
+	else
+		TotalV = 0;
+	/* allocate memory for 'new PV to check' list and clear struct */
+	PVList.Data = (void **) calloc(TotalP + TotalV, sizeof(PinTypePtr));
+	PVList.Size = TotalP + TotalV;
+	PVList.Location = 0;
+	PVList.DrawLocation = 0;
+	PVList.Number = 0;
+	/* Initialize ratline data */
+	RatList.Size = ratlist_length(&PCB->Data->Rat);
+	RatList.Data = (void **) calloc(RatList.Size, sizeof(RatTypePtr));
+	RatList.Location = 0;
+	RatList.DrawLocation = 0;
+	RatList.Number = 0;
+}
+
+struct pv_info {
+	pcb_cardinal_t layer;
+	PinType pv;
+	jmp_buf env;
+};
+
+static r_dir_t LOCtoPVline_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct pv_info *i = (struct pv_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) && PinLineIntersect(&i->pv, line) && !TEST_FLAG(PCB_FLAG_HOLE, &i->pv)) {
+		if (ADD_LINE_TO_LIST(i->layer, line, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPVarc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct pv_info *i = (struct pv_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, arc) && IS_PV_ON_ARC(&i->pv, arc) && !TEST_FLAG(PCB_FLAG_HOLE, &i->pv)) {
+		if (ADD_ARC_TO_LIST(i->layer, arc, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPVpad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct pv_info *i = (struct pv_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && IS_PV_ON_PAD(&i->pv, pad) &&
+			!TEST_FLAG(PCB_FLAG_HOLE, &i->pv) &&
+			ADD_PAD_TO_LIST(TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER, pad, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPVrat_callback(const BoxType * b, void *cl)
+{
+	RatTypePtr rat = (RatTypePtr) b;
+	struct pv_info *i = (struct pv_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, rat) && IS_PV_ON_RAT(&i->pv, rat) && ADD_RAT_TO_LIST(rat, PCB_TYPE_PIN, &i->pv, FCT_RAT))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPVpoly_callback(const BoxType * b, void *cl)
+{
+	PolygonTypePtr polygon = (PolygonTypePtr) b;
+	struct pv_info *i = (struct pv_info *) cl;
+
+	/* if the pin doesn't have a therm and polygon is clearing
+	 * then it can't touch due to clearance, so skip the expensive
+	 * test. If it does have a therm, you still need to test
+	 * because it might not be inside the polygon, or it could
+	 * be on an edge such that it doesn't actually touch.
+	 */
+	if (!TEST_FLAG(TheFlag, polygon) && !TEST_FLAG(PCB_FLAG_HOLE, &i->pv) &&
+			(TEST_THERM(i->layer, &i->pv) || !TEST_FLAG(PCB_FLAG_CLEARPOLY, polygon)
+			 || !i->pv.Clearance)) {
+		double wide = MAX(0.5 * i->pv.Thickness + Bloat, 0);
+		if (TEST_FLAG(PCB_FLAG_SQUARE, &i->pv)) {
+			Coord x1 = i->pv.X - (i->pv.Thickness + 1 + Bloat) / 2;
+			Coord x2 = i->pv.X + (i->pv.Thickness + 1 + Bloat) / 2;
+			Coord y1 = i->pv.Y - (i->pv.Thickness + 1 + Bloat) / 2;
+			Coord y2 = i->pv.Y + (i->pv.Thickness + 1 + Bloat) / 2;
+			if (IsRectangleInPolygon(x1, y1, x2, y2, polygon)
+					&& ADD_POLYGON_TO_LIST(i->layer, polygon, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+				longjmp(i->env, 1);
+		}
+		else if (TEST_FLAG(PCB_FLAG_OCTAGON, &i->pv)) {
+			POLYAREA *oct = OctagonPoly(i->pv.X, i->pv.Y, i->pv.Thickness / 2, GET_SQUARE(&i->pv));
+			if (isects(oct, polygon, pcb_true)
+					&& ADD_POLYGON_TO_LIST(i->layer, polygon, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+				longjmp(i->env, 1);
+		}
+		else if (IsPointInPolygon(i->pv.X, i->pv.Y, wide, polygon)
+						 && ADD_POLYGON_TO_LIST(i->layer, polygon, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a PV is connected to LOs, if it is, the LO is added to
+ * the appropriate list and the 'used' flag is set
+ */
+static pcb_bool LookupLOConnectionsToPVList(pcb_bool AndRats)
+{
+	pcb_cardinal_t layer;
+	struct pv_info info;
+
+	/* loop over all PVs currently on list */
+	while (PVList.Location < PVList.Number) {
+		/* get pointer to data */
+		info.pv = *(PVLIST_ENTRY(PVList.Location));
+		EXPAND_BOUNDS(&info.pv);
+
+		/* check pads */
+		if (setjmp(info.env) == 0)
+			r_search(PCB->Data->pad_tree, (BoxType *) & info.pv, NULL, LOCtoPVpad_callback, &info, NULL);
+		else
+			return pcb_true;
+
+		/* now all lines, arcs and polygons of the several layers */
+		for (layer = 0; layer < max_copper_layer; layer++) {
+			if (LAYER_PTR(layer)->no_drc)
+				continue;
+			info.layer = layer;
+			/* add touching lines */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->line_tree, (BoxType *) & info.pv, NULL, LOCtoPVline_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* add touching arcs */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->arc_tree, (BoxType *) & info.pv, NULL, LOCtoPVarc_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* check all polygons */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->polygon_tree, (BoxType *) & info.pv, NULL, LOCtoPVpoly_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+		/* Check for rat-lines that may intersect the PV */
+		if (AndRats) {
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->rat_tree, (BoxType *) & info.pv, NULL, LOCtoPVrat_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+		PVList.Location++;
+	}
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * find all connections between LO at the current list position and new LOs
+ */
+static pcb_bool LookupLOConnectionsToLOList(pcb_bool AndRats)
+{
+	pcb_bool done;
+	pcb_cardinal_t i, group, layer, ratposition,
+		lineposition[MAX_LAYER], polyposition[MAX_LAYER], arcposition[MAX_LAYER], padposition[2];
+
+	/* copy the current LO list positions; the original data is changed
+	 * by 'LookupPVConnectionsToLOList()' which has to check the same
+	 * list entries plus the new ones
+	 */
+	for (i = 0; i < max_copper_layer; i++) {
+		lineposition[i] = LineList[i].Location;
+		polyposition[i] = PolygonList[i].Location;
+		arcposition[i] = ArcList[i].Location;
+	}
+	for (i = 0; i < 2; i++)
+		padposition[i] = PadList[i].Location;
+	ratposition = RatList.Location;
+
+	/* loop over all new LOs in the list; recurse until no
+	 * more new connections in the layergroup were found
+	 */
+	do {
+		pcb_cardinal_t *position;
+
+		if (AndRats) {
+			position = &ratposition;
+			for (; *position < RatList.Number; (*position)++) {
+				group = RATLIST_ENTRY(*position)->group1;
+				if (LookupLOConnectionsToRatEnd(&(RATLIST_ENTRY(*position)->Point1), group))
+					return (pcb_true);
+				group = RATLIST_ENTRY(*position)->group2;
+				if (LookupLOConnectionsToRatEnd(&(RATLIST_ENTRY(*position)->Point2), group))
+					return (pcb_true);
+			}
+		}
+		/* loop over all layergroups */
+		for (group = 0; group < max_group; group++) {
+			pcb_cardinal_t entry;
+
+			for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) {
+				layer = PCB->LayerGroups.Entries[group][entry];
+
+				/* be aware that the layer number equal max_copper_layer
+				 * and max_copper_layer+1 have a special meaning for pads
+				 */
+				if (layer < max_copper_layer) {
+					/* try all new lines */
+					position = &lineposition[layer];
+					for (; *position < LineList[layer].Number; (*position)++)
+						if (LookupLOConnectionsToLine(LINELIST_ENTRY(layer, *position), group, pcb_true))
+							return (pcb_true);
+
+					/* try all new arcs */
+					position = &arcposition[layer];
+					for (; *position < ArcList[layer].Number; (*position)++)
+						if (LookupLOConnectionsToArc(ARCLIST_ENTRY(layer, *position), group))
+							return (pcb_true);
+
+					/* try all new polygons */
+					position = &polyposition[layer];
+					for (; *position < PolygonList[layer].Number; (*position)++)
+						if (LookupLOConnectionsToPolygon(POLYGONLIST_ENTRY(layer, *position), group))
+							return (pcb_true);
+				}
+				else {
+					/* try all new pads */
+					layer -= max_copper_layer;
+					if (layer > 1) {
+						Message(PCB_MSG_DEFAULT, _("bad layer number %d max_copper_layer=%d in find.c\n"), layer, max_copper_layer);
+						return pcb_false;
+					}
+					position = &padposition[layer];
+					for (; *position < PadList[layer].Number; (*position)++)
+						if (LookupLOConnectionsToPad(PADLIST_ENTRY(layer, *position), group))
+							return (pcb_true);
+				}
+			}
+		}
+
+		/* check if all lists are done; Later for-loops
+		 * may have changed the prior lists
+		 */
+		done = !AndRats || ratposition >= RatList.Number;
+		for (layer = 0; layer < max_copper_layer + 2; layer++) {
+			if (layer < max_copper_layer)
+				done = done &&
+					lineposition[layer] >= LineList[layer].Number
+					&& arcposition[layer] >= ArcList[layer].Number && polyposition[layer] >= PolygonList[layer].Number;
+			else
+				done = done && padposition[layer - max_copper_layer] >= PadList[layer - max_copper_layer].Number;
+		}
+	}
+	while (!done);
+	return (pcb_false);
+}
+
+static r_dir_t pv_pv_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pin = (PinTypePtr) b;
+	struct pv_info *i = (struct pv_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pin) && PV_TOUCH_PV(&i->pv, pin)) {
+		if (TEST_FLAG(PCB_FLAG_HOLE, pin) || TEST_FLAG(PCB_FLAG_HOLE, &i->pv)) {
+			SET_FLAG(PCB_FLAG_WARN, pin);
+			conf_core.temp.rat_warn = pcb_true;
+			if (pin->Element)
+				Message(PCB_MSG_DEFAULT, _("WARNING: Hole too close to pin.\n"));
+			else
+				Message(PCB_MSG_DEFAULT, _("WARNING: Hole too close to via.\n"));
+		}
+		else if (ADD_PV_TO_LIST(pin, PCB_TYPE_PIN, &i->pv, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches for new PVs that are connected to PVs on the list
+ */
+static pcb_bool LookupPVConnectionsToPVList(void)
+{
+	pcb_cardinal_t save_place;
+	struct pv_info info;
+
+
+	/* loop over all PVs on list */
+	save_place = PVList.Location;
+	while (PVList.Location < PVList.Number) {
+		int ic;
+		PinType *orig_pin;
+		/* get pointer to data */
+		orig_pin = (PVLIST_ENTRY(PVList.Location));
+		info.pv = *orig_pin;
+
+		/* Internal connection: if pins in the same element have the same
+		   internal connection group number, they are connected */
+		ic = GET_INTCONN(orig_pin);
+		if ((info.pv.Element != NULL) && (ic > 0)) {
+			ElementType *e = info.pv.Element;
+			PIN_LOOP(e);
+			{
+				if ((orig_pin != pin) && (ic == GET_INTCONN(pin))) {
+					if (!TEST_FLAG(TheFlag, pin))
+						ADD_PV_TO_LIST(pin, PCB_TYPE_PIN, orig_pin, FCT_INTERNAL);
+				}
+			}
+			END_LOOP;
+		}
+
+
+		EXPAND_BOUNDS(&info.pv);
+		if (setjmp(info.env) == 0)
+			r_search(PCB->Data->via_tree, (BoxType *) & info.pv, NULL, pv_pv_callback, &info, NULL);
+		else
+			return pcb_true;
+		if (setjmp(info.env) == 0)
+			r_search(PCB->Data->pin_tree, (BoxType *) & info.pv, NULL, pv_pv_callback, &info, NULL);
+		else
+			return pcb_true;
+		PVList.Location++;
+	}
+	PVList.Location = save_place;
+	return (pcb_false);
+}
+
+struct lo_info {
+	pcb_cardinal_t layer;
+	LineType line;
+	PadType pad;
+	ArcType arc;
+	PolygonType polygon;
+	RatType rat;
+	jmp_buf env;
+};
+
+static r_dir_t pv_line_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pv = (PinTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pv) && PinLineIntersect(pv, &i->line)) {
+		if (TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+			SET_FLAG(PCB_FLAG_WARN, pv);
+			conf_core.temp.rat_warn = pcb_true;
+			Message(PCB_MSG_DEFAULT, _("WARNING: Hole too close to line.\n"));
+		}
+		else if (ADD_PV_TO_LIST(pv, PCB_TYPE_LINE, &i->line, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t pv_pad_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pv = (PinTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pv) && IS_PV_ON_PAD(pv, &i->pad)) {
+		if (TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+			SET_FLAG(PCB_FLAG_WARN, pv);
+			conf_core.temp.rat_warn = pcb_true;
+			Message(PCB_MSG_DEFAULT, _("WARNING: Hole too close to pad.\n"));
+		}
+		else if (ADD_PV_TO_LIST(pv, PCB_TYPE_PAD, &i->pad, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t pv_arc_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pv = (PinTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pv) && IS_PV_ON_ARC(pv, &i->arc)) {
+		if (TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+			SET_FLAG(PCB_FLAG_WARN, pv);
+			conf_core.temp.rat_warn = pcb_true;
+			Message(PCB_MSG_DEFAULT, _("WARNING: Hole touches arc.\n"));
+		}
+		else if (ADD_PV_TO_LIST(pv, PCB_TYPE_ARC, &i->arc, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t pv_poly_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pv = (PinTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	/* note that holes in polygons are ok, so they don't generate warnings. */
+	if (!TEST_FLAG(TheFlag, pv) && !TEST_FLAG(PCB_FLAG_HOLE, pv) &&
+			(TEST_THERM(i->layer, pv) || !TEST_FLAG(PCB_FLAG_CLEARPOLY, &i->polygon) || !pv->Clearance)) {
+		if (TEST_FLAG(PCB_FLAG_SQUARE, pv)) {
+			Coord x1, x2, y1, y2;
+			x1 = pv->X - (PIN_SIZE(pv) + 1 + Bloat) / 2;
+			x2 = pv->X + (PIN_SIZE(pv) + 1 + Bloat) / 2;
+			y1 = pv->Y - (PIN_SIZE(pv) + 1 + Bloat) / 2;
+			y2 = pv->Y + (PIN_SIZE(pv) + 1 + Bloat) / 2;
+			if (IsRectangleInPolygon(x1, y1, x2, y2, &i->polygon)
+					&& ADD_PV_TO_LIST(pv, PCB_TYPE_POLYGON, &i->polygon, FCT_COPPER))
+				longjmp(i->env, 1);
+		}
+		else if (TEST_FLAG(PCB_FLAG_OCTAGON, pv)) {
+			POLYAREA *oct = OctagonPoly(pv->X, pv->Y, PIN_SIZE(pv) / 2, GET_SQUARE(pv));
+			if (isects(oct, &i->polygon, pcb_true) && ADD_PV_TO_LIST(pv, PCB_TYPE_POLYGON, &i->polygon, FCT_COPPER))
+				longjmp(i->env, 1);
+		}
+		else {
+			if (IsPointInPolygon(pv->X, pv->Y, PIN_SIZE(pv) * 0.5 + Bloat, &i->polygon)
+					&& ADD_PV_TO_LIST(pv, PCB_TYPE_POLYGON, &i->polygon, FCT_COPPER))
+				longjmp(i->env, 1);
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t pv_rat_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pv = (PinTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	/* rats can't cause DRC so there is no early exit */
+	if (!TEST_FLAG(TheFlag, pv) && IS_PV_ON_RAT(pv, &i->rat))
+		ADD_PV_TO_LIST(pv, PCB_TYPE_RATLINE, &i->rat, FCT_RAT);
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches for new PVs that are connected to NEW LOs on the list
+ * This routine updates the position counter of the lists too.
+ */
+static pcb_bool LookupPVConnectionsToLOList(pcb_bool AndRats)
+{
+	pcb_cardinal_t layer;
+	struct lo_info info;
+
+	/* loop over all layers */
+	for (layer = 0; layer < max_copper_layer; layer++) {
+		if (LAYER_PTR(layer)->no_drc)
+			continue;
+		/* do nothing if there are no PV's */
+		if (TotalP + TotalV == 0) {
+			LineList[layer].Location = LineList[layer].Number;
+			ArcList[layer].Location = ArcList[layer].Number;
+			PolygonList[layer].Location = PolygonList[layer].Number;
+			continue;
+		}
+
+		/* check all lines */
+		while (LineList[layer].Location < LineList[layer].Number) {
+			info.line = *(LINELIST_ENTRY(layer, LineList[layer].Location));
+			EXPAND_BOUNDS(&info.line);
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->via_tree, (BoxType *) & info.line, NULL, pv_line_callback, &info, NULL);
+			else
+				return pcb_true;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pin_tree, (BoxType *) & info.line, NULL, pv_line_callback, &info, NULL);
+			else
+				return pcb_true;
+			LineList[layer].Location++;
+		}
+
+		/* check all arcs */
+		while (ArcList[layer].Location < ArcList[layer].Number) {
+			info.arc = *(ARCLIST_ENTRY(layer, ArcList[layer].Location));
+			EXPAND_BOUNDS(&info.arc);
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->via_tree, (BoxType *) & info.arc, NULL, pv_arc_callback, &info, NULL);
+			else
+				return pcb_true;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pin_tree, (BoxType *) & info.arc, NULL, pv_arc_callback, &info, NULL);
+			else
+				return pcb_true;
+			ArcList[layer].Location++;
+		}
+
+		/* now all polygons */
+		info.layer = layer;
+		while (PolygonList[layer].Location < PolygonList[layer].Number) {
+			info.polygon = *(POLYGONLIST_ENTRY(layer, PolygonList[layer].Location));
+			EXPAND_BOUNDS(&info.polygon);
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->via_tree, (BoxType *) & info.polygon, NULL, pv_poly_callback, &info, NULL);
+			else
+				return pcb_true;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pin_tree, (BoxType *) & info.polygon, NULL, pv_poly_callback, &info, NULL);
+			else
+				return pcb_true;
+			PolygonList[layer].Location++;
+		}
+	}
+
+	/* loop over all pad-layers */
+	for (layer = 0; layer < 2; layer++) {
+		/* do nothing if there are no PV's */
+		if (TotalP + TotalV == 0) {
+			PadList[layer].Location = PadList[layer].Number;
+			continue;
+		}
+
+		/* check all pads; for a detailed description see
+		 * the handling of lines in this subroutine
+		 */
+		while (PadList[layer].Location < PadList[layer].Number) {
+			info.pad = *(PADLIST_ENTRY(layer, PadList[layer].Location));
+			EXPAND_BOUNDS(&info.pad);
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->via_tree, (BoxType *) & info.pad, NULL, pv_pad_callback, &info, NULL);
+			else
+				return pcb_true;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pin_tree, (BoxType *) & info.pad, NULL, pv_pad_callback, &info, NULL);
+			else
+				return pcb_true;
+			PadList[layer].Location++;
+		}
+	}
+
+	/* do nothing if there are no PV's */
+	if (TotalP + TotalV == 0)
+		RatList.Location = RatList.Number;
+
+	/* check all rat-lines */
+	if (AndRats) {
+		while (RatList.Location < RatList.Number) {
+			info.rat = *(RATLIST_ENTRY(RatList.Location));
+			r_search_pt(PCB->Data->via_tree, &info.rat.Point1, 1, NULL, pv_rat_callback, &info, NULL);
+			r_search_pt(PCB->Data->via_tree, &info.rat.Point2, 1, NULL, pv_rat_callback, &info, NULL);
+			r_search_pt(PCB->Data->pin_tree, &info.rat.Point1, 1, NULL, pv_rat_callback, &info, NULL);
+			r_search_pt(PCB->Data->pin_tree, &info.rat.Point2, 1, NULL, pv_rat_callback, &info, NULL);
+
+			RatList.Location++;
+		}
+	}
+	return (pcb_false);
+}
+
+r_dir_t pv_touch_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pin = (PinTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pin) && PinLineIntersect(pin, &i->line))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoArcLine_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) && LineArcIntersect(line, &i->arc)) {
+		if (ADD_LINE_TO_LIST(i->layer, line, PCB_TYPE_ARC, &i->arc, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoArcArc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!arc->Thickness)
+		return 0;
+	if (!TEST_FLAG(TheFlag, arc) && ArcArcIntersect(&i->arc, arc)) {
+		if (ADD_ARC_TO_LIST(i->layer, arc, PCB_TYPE_ARC, &i->arc, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoArcPad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && i->layer == (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER)
+			&& ArcPadIntersect(&i->arc, pad) && ADD_PAD_TO_LIST(i->layer, pad, PCB_TYPE_ARC, &i->arc, FCT_COPPER))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches all LOs that are connected to the given arc on the given
+ * layergroup. All found connections are added to the list
+ *
+ * the notation that is used is:
+ * Xij means Xj at arc i
+ */
+static pcb_bool LookupLOConnectionsToArc(ArcTypePtr Arc, pcb_cardinal_t LayerGroup)
+{
+	pcb_cardinal_t entry;
+	struct lo_info info;
+
+	info.arc = *Arc;
+	EXPAND_BOUNDS(&info.arc);
+	/* loop over all layers of the group */
+	for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+		pcb_cardinal_t layer;
+
+		layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+
+		/* handle normal layers */
+		if (layer < max_copper_layer) {
+			PolygonType *polygon;
+			gdl_iterator_t it;
+
+			info.layer = layer;
+			/* add arcs */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->line_tree, &info.arc.BoundingBox, NULL, LOCtoArcLine_callback, &info, NULL);
+			else
+				return pcb_true;
+
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->arc_tree, &info.arc.BoundingBox, NULL, LOCtoArcArc_callback, &info, NULL);
+			else
+				return pcb_true;
+
+			/* now check all polygons */
+			polylist_foreach(&(PCB->Data->Layer[layer].Polygon), &it, polygon) {
+				if (!TEST_FLAG(TheFlag, polygon) && IsArcInPolygon(Arc, polygon)
+						&& ADD_POLYGON_TO_LIST(layer, polygon, PCB_TYPE_ARC, Arc, FCT_COPPER))
+					return pcb_true;
+			}
+		}
+		else {
+			info.layer = layer - max_copper_layer;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pad_tree, &info.arc.BoundingBox, NULL, LOCtoArcPad_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+	}
+	return (pcb_false);
+}
+
+static r_dir_t LOCtoLineLine_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) && LineLineIntersect(&i->line, line)) {
+		if (ADD_LINE_TO_LIST(i->layer, line, PCB_TYPE_LINE, &i->line, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoLineArc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!arc->Thickness)
+		return 0;
+	if (!TEST_FLAG(TheFlag, arc) && LineArcIntersect(&i->line, arc)) {
+		if (ADD_ARC_TO_LIST(i->layer, arc, PCB_TYPE_LINE, &i->line, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoLineRat_callback(const BoxType * b, void *cl)
+{
+	RatTypePtr rat = (RatTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, rat)) {
+		if ((rat->group1 == i->layer)
+				&& IsRatPointOnLineEnd(&rat->Point1, &i->line)) {
+			if (ADD_RAT_TO_LIST(rat, PCB_TYPE_LINE, &i->line, FCT_RAT))
+				longjmp(i->env, 1);
+		}
+		else if ((rat->group2 == i->layer)
+						 && IsRatPointOnLineEnd(&rat->Point2, &i->line)) {
+			if (ADD_RAT_TO_LIST(rat, PCB_TYPE_LINE, &i->line, FCT_RAT))
+				longjmp(i->env, 1);
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoLinePad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && i->layer == (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER)
+			&& LinePadIntersect(&i->line, pad) && ADD_PAD_TO_LIST(i->layer, pad, PCB_TYPE_LINE, &i->line, FCT_COPPER))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches all LOs that are connected to the given line on the given
+ * layergroup. All found connections are added to the list
+ *
+ * the notation that is used is:
+ * Xij means Xj at line i
+ */
+static pcb_bool LookupLOConnectionsToLine(LineTypePtr Line, pcb_cardinal_t LayerGroup, pcb_bool PolysTo)
+{
+	pcb_cardinal_t entry;
+	struct lo_info info;
+
+	info.line = *Line;
+	info.layer = LayerGroup;
+	EXPAND_BOUNDS(&info.line)
+		/* add the new rat lines */
+		if (setjmp(info.env) == 0)
+		r_search(PCB->Data->rat_tree, &info.line.BoundingBox, NULL, LOCtoLineRat_callback, &info, NULL);
+	else
+		return pcb_true;
+
+	/* loop over all layers of the group */
+	for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+		pcb_cardinal_t layer;
+
+		layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+
+		/* handle normal layers */
+		if (layer < max_copper_layer) {
+			info.layer = layer;
+			/* add lines */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->line_tree, (BoxType *) & info.line, NULL, LOCtoLineLine_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* add arcs */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->arc_tree, (BoxType *) & info.line, NULL, LOCtoLineArc_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* now check all polygons */
+			if (PolysTo) {
+				gdl_iterator_t it;
+				PolygonType *polygon;
+
+				polylist_foreach(&(PCB->Data->Layer[layer].Polygon), &it, polygon) {
+					if (!TEST_FLAG(TheFlag, polygon) && IsLineInPolygon(Line, polygon)
+							&& ADD_POLYGON_TO_LIST(layer, polygon, PCB_TYPE_LINE, Line, FCT_COPPER))
+						return pcb_true;
+				}
+			}
+		}
+		else {
+			/* handle special 'pad' layers */
+			info.layer = layer - max_copper_layer;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pad_tree, &info.line.BoundingBox, NULL, LOCtoLinePad_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+	}
+	return (pcb_false);
+}
+
+
+struct rat_info {
+	pcb_cardinal_t layer;
+	PointTypePtr Point;
+	jmp_buf env;
+};
+
+static r_dir_t LOCtoRat_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct rat_info *i = (struct rat_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) &&
+			((line->Point1.X == i->Point->X &&
+				line->Point1.Y == i->Point->Y) || (line->Point2.X == i->Point->X && line->Point2.Y == i->Point->Y))) {
+		if (ADD_LINE_TO_LIST(i->layer, line, PCB_TYPE_RATLINE, &i->Point, FCT_RAT))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t PolygonToRat_callback(const BoxType * b, void *cl)
+{
+	PolygonTypePtr polygon = (PolygonTypePtr) b;
+	struct rat_info *i = (struct rat_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, polygon) && polygon->Clipped &&
+			(i->Point->X == polygon->Clipped->contours->head.point[0]) &&
+			(i->Point->Y == polygon->Clipped->contours->head.point[1])) {
+		if (ADD_POLYGON_TO_LIST(i->layer, polygon, PCB_TYPE_RATLINE, &i->Point, FCT_RAT))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct rat_info *i = (struct rat_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && i->layer ==
+			(TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER) &&
+			((pad->Point1.X == i->Point->X && pad->Point1.Y == i->Point->Y) ||
+			 (pad->Point2.X == i->Point->X && pad->Point2.Y == i->Point->Y) ||
+			 ((pad->Point1.X + pad->Point2.X) / 2 == i->Point->X &&
+				(pad->Point1.Y + pad->Point2.Y) / 2 == i->Point->Y)) &&
+			ADD_PAD_TO_LIST(i->layer, pad, PCB_TYPE_RATLINE, &i->Point, FCT_RAT))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches all LOs that are connected to the given rat-line on the given
+ * layergroup. All found connections are added to the list
+ *
+ * the notation that is used is:
+ * Xij means Xj at line i
+ */
+static pcb_bool LookupLOConnectionsToRatEnd(PointTypePtr Point, pcb_cardinal_t LayerGroup)
+{
+	pcb_cardinal_t entry;
+	struct rat_info info;
+
+	info.Point = Point;
+	/* loop over all layers of this group */
+	for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+		pcb_cardinal_t layer;
+
+		layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+		/* handle normal layers
+		   rats don't ever touch
+		   arcs by definition
+		 */
+
+		if (layer < max_copper_layer) {
+			info.layer = layer;
+			if (setjmp(info.env) == 0)
+				r_search_pt(LAYER_PTR(layer)->line_tree, Point, 1, NULL, LOCtoRat_callback, &info, NULL);
+			else
+				return pcb_true;
+			if (setjmp(info.env) == 0)
+				r_search_pt(LAYER_PTR(layer)->polygon_tree, Point, 1, NULL, PolygonToRat_callback, &info, NULL);
+		}
+		else {
+			/* handle special 'pad' layers */
+			info.layer = layer - max_copper_layer;
+			if (setjmp(info.env) == 0)
+				r_search_pt(PCB->Data->pad_tree, Point, 1, NULL, LOCtoPad_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+	}
+	return (pcb_false);
+}
+
+static r_dir_t LOCtoPadLine_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) && LinePadIntersect(line, &i->pad)) {
+		if (ADD_LINE_TO_LIST(i->layer, line, PCB_TYPE_PAD, &i->pad, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPadArc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!arc->Thickness)
+		return 0;
+	if (!TEST_FLAG(TheFlag, arc) && ArcPadIntersect(arc, &i->pad)) {
+		if (ADD_ARC_TO_LIST(i->layer, arc, PCB_TYPE_PAD, &i->pad, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPadPoly_callback(const BoxType * b, void *cl)
+{
+	PolygonTypePtr polygon = (PolygonTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+
+	if (!TEST_FLAG(TheFlag, polygon) && (!TEST_FLAG(PCB_FLAG_CLEARPOLY, polygon) || !i->pad.Clearance)) {
+		if (IsPadInPolygon(&i->pad, polygon) && ADD_POLYGON_TO_LIST(i->layer, polygon, PCB_TYPE_PAD, &i->pad, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPadRat_callback(const BoxType * b, void *cl)
+{
+	RatTypePtr rat = (RatTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, rat)) {
+		if (rat->group1 == i->layer &&
+				((rat->Point1.X == i->pad.Point1.X && rat->Point1.Y == i->pad.Point1.Y) ||
+				 (rat->Point1.X == i->pad.Point2.X && rat->Point1.Y == i->pad.Point2.Y) ||
+				 (rat->Point1.X == (i->pad.Point1.X + i->pad.Point2.X) / 2 &&
+					rat->Point1.Y == (i->pad.Point1.Y + i->pad.Point2.Y) / 2))) {
+			if (ADD_RAT_TO_LIST(rat, PCB_TYPE_PAD, &i->pad, FCT_RAT))
+				longjmp(i->env, 1);
+		}
+		else if (rat->group2 == i->layer &&
+						 ((rat->Point2.X == i->pad.Point1.X && rat->Point2.Y == i->pad.Point1.Y) ||
+							(rat->Point2.X == i->pad.Point2.X && rat->Point2.Y == i->pad.Point2.Y) ||
+							(rat->Point2.X == (i->pad.Point1.X + i->pad.Point2.X) / 2 &&
+							 rat->Point2.Y == (i->pad.Point1.Y + i->pad.Point2.Y) / 2))) {
+			if (ADD_RAT_TO_LIST(rat, PCB_TYPE_PAD, &i->pad, FCT_RAT))
+				longjmp(i->env, 1);
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPadPad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && i->layer == (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER)
+			&& PadPadIntersect(pad, &i->pad) && ADD_PAD_TO_LIST(i->layer, pad, PCB_TYPE_PAD, &i->pad, FCT_COPPER))
+		longjmp(i->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches all LOs that are connected to the given pad on the given
+ * layergroup. All found connections are added to the list
+ */
+static pcb_bool LookupLOConnectionsToPad(PadTypePtr Pad, pcb_cardinal_t LayerGroup)
+{
+	pcb_cardinal_t entry;
+	struct lo_info info;
+	int ic;
+	pcb_bool retv = pcb_false;
+
+	/* Internal connection: if pads in the same element have the same
+	   internal connection group number, they are connected */
+	ic = GET_INTCONN(Pad);
+	if ((Pad->Element != NULL) && (ic > 0)) {
+		ElementType *e = Pad->Element;
+		PadTypePtr orig_pad = Pad;
+		int tlayer = -1;
+
+/*fprintf(stderr, "lg===\n");*/
+		for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+			pcb_cardinal_t layer;
+			layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+/*fprintf(stderr, "lg: %d\n", layer);*/
+			if (layer == COMPONENT_LAYER)
+				tlayer = COMPONENT_LAYER;
+			else if (layer == SOLDER_LAYER)
+				tlayer = SOLDER_LAYER;
+		}
+
+/*fprintf(stderr, "tlayer=%d\n", tlayer);*/
+
+		if (tlayer >= 0) {
+			PAD_LOOP(e);
+			{
+				if ((orig_pad != pad) && (ic == GET_INTCONN(pad))) {
+					int padlayer = TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER;
+/*fprintf(stderr, "layergroup1: %d {%d %d %d} %d \n", tlayer, TEST_FLAG(PCB_FLAG_ONSOLDER, pad), SOLDER_LAYER, COMPONENT_LAYER, padlayer);*/
+					if ((!TEST_FLAG(TheFlag, pad)) && (tlayer != padlayer)) {
+/*fprintf(stderr, "layergroup2\n");*/
+						ADD_PAD_TO_LIST(padlayer, pad, PCB_TYPE_PAD, orig_pad, FCT_INTERNAL);
+						if (LookupLOConnectionsToPad(pad, LayerGroup))
+							retv = pcb_true;
+					}
+				}
+			}
+			END_LOOP;
+		}
+	}
+
+
+	if (!TEST_FLAG(PCB_FLAG_SQUARE, Pad))
+		return (LookupLOConnectionsToLine((LineTypePtr) Pad, LayerGroup, pcb_false));
+
+	info.pad = *Pad;
+	EXPAND_BOUNDS(&info.pad);
+	/* add the new rat lines */
+	info.layer = LayerGroup;
+	if (setjmp(info.env) == 0)
+		r_search(PCB->Data->rat_tree, &info.pad.BoundingBox, NULL, LOCtoPadRat_callback, &info, NULL);
+	else
+		return pcb_true;
+
+	/* loop over all layers of the group */
+	for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+		pcb_cardinal_t layer;
+
+		layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+		/* handle normal layers */
+		if (layer < max_copper_layer) {
+			info.layer = layer;
+			/* add lines */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->line_tree, &info.pad.BoundingBox, NULL, LOCtoPadLine_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* add arcs */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->arc_tree, &info.pad.BoundingBox, NULL, LOCtoPadArc_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* add polygons */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->polygon_tree, &info.pad.BoundingBox, NULL, LOCtoPadPoly_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+		else {
+			/* handle special 'pad' layers */
+			info.layer = layer - max_copper_layer;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pad_tree, (BoxType *) & info.pad, NULL, LOCtoPadPad_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+
+	}
+	return retv;
+}
+
+static r_dir_t LOCtoPolyLine_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, line) && IsLineInPolygon(line, &i->polygon)) {
+		if (ADD_LINE_TO_LIST(i->layer, line, PCB_TYPE_POLYGON, &i->polygon, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPolyArc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!arc->Thickness)
+		return 0;
+	if (!TEST_FLAG(TheFlag, arc) && IsArcInPolygon(arc, &i->polygon)) {
+		if (ADD_ARC_TO_LIST(i->layer, arc, PCB_TYPE_POLYGON, &i->polygon, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return 0;
+}
+
+static r_dir_t LOCtoPolyPad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, pad) && i->layer == (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER)
+			&& IsPadInPolygon(pad, &i->polygon)) {
+		if (ADD_PAD_TO_LIST(i->layer, pad, PCB_TYPE_POLYGON, &i->polygon, FCT_COPPER))
+			longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t LOCtoPolyRat_callback(const BoxType * b, void *cl)
+{
+	RatTypePtr rat = (RatTypePtr) b;
+	struct lo_info *i = (struct lo_info *) cl;
+
+	if (!TEST_FLAG(TheFlag, rat)) {
+		if ((rat->Point1.X == (i->polygon.Clipped->contours->head.point[0]) &&
+				 rat->Point1.Y == (i->polygon.Clipped->contours->head.point[1]) &&
+				 rat->group1 == i->layer) ||
+				(rat->Point2.X == (i->polygon.Clipped->contours->head.point[0]) &&
+				 rat->Point2.Y == (i->polygon.Clipped->contours->head.point[1]) && rat->group2 == i->layer))
+			if (ADD_RAT_TO_LIST(rat, PCB_TYPE_POLYGON, &i->polygon, FCT_RAT))
+				longjmp(i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * looks up LOs that are connected to the given polygon
+ * on the given layergroup. All found connections are added to the list
+ */
+static pcb_bool LookupLOConnectionsToPolygon(PolygonTypePtr Polygon, pcb_cardinal_t LayerGroup)
+{
+	pcb_cardinal_t entry;
+	struct lo_info info;
+
+	if (!Polygon->Clipped)
+		return pcb_false;
+	info.polygon = *Polygon;
+	EXPAND_BOUNDS(&info.polygon);
+	info.layer = LayerGroup;
+	/* check rats */
+	if (setjmp(info.env) == 0)
+		r_search(PCB->Data->rat_tree, (BoxType *) & info.polygon, NULL, LOCtoPolyRat_callback, &info, NULL);
+	else
+		return pcb_true;
+/* loop over all layers of the group */
+	for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) {
+		pcb_cardinal_t layer;
+
+		layer = PCB->LayerGroups.Entries[LayerGroup][entry];
+
+		/* handle normal layers */
+		if (layer < max_copper_layer) {
+			gdl_iterator_t it;
+			PolygonType *polygon;
+
+			/* check all polygons */
+			polylist_foreach(&(PCB->Data->Layer[layer].Polygon), &it, polygon) {
+				if (!TEST_FLAG(TheFlag, polygon)
+						&& IsPolygonInPolygon(polygon, Polygon)
+						&& ADD_POLYGON_TO_LIST(layer, polygon, PCB_TYPE_POLYGON, Polygon, FCT_COPPER))
+					return pcb_true;
+			}
+
+			info.layer = layer;
+			/* check all lines */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->line_tree, (BoxType *) & info.polygon, NULL, LOCtoPolyLine_callback, &info, NULL);
+			else
+				return pcb_true;
+			/* check all arcs */
+			if (setjmp(info.env) == 0)
+				r_search(LAYER_PTR(layer)->arc_tree, (BoxType *) & info.polygon, NULL, LOCtoPolyArc_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+		else {
+			info.layer = layer - max_copper_layer;
+			if (setjmp(info.env) == 0)
+				r_search(PCB->Data->pad_tree, (BoxType *) & info.polygon, NULL, LOCtoPolyPad_callback, &info, NULL);
+			else
+				return pcb_true;
+		}
+	}
+	return (pcb_false);
+}
diff --git a/src/find_misc.c b/src/find_misc.c
new file mode 100644
index 0000000..118074c
--- /dev/null
+++ b/src/find_misc.c
@@ -0,0 +1,476 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+static void DrawNewConnections(void);
+
+/* ---------------------------------------------------------------------------
+ * checks if all lists of new objects are handled
+ */
+static pcb_bool ListsEmpty(pcb_bool AndRats)
+{
+	pcb_bool empty;
+	int i;
+
+	empty = (PVList.Location >= PVList.Number);
+	if (AndRats)
+		empty = empty && (RatList.Location >= RatList.Number);
+	for (i = 0; i < max_copper_layer && empty; i++)
+		empty = empty && LineList[i].Location >= LineList[i].Number
+			&& ArcList[i].Location >= ArcList[i].Number && PolygonList[i].Location >= PolygonList[i].Number;
+	return (empty);
+}
+
+static void reassign_no_drc_flags(void)
+{
+	int layer;
+
+	for (layer = 0; layer < max_copper_layer; layer++) {
+		LayerTypePtr l = LAYER_PTR(layer);
+		l->no_drc = AttributeGet(l, "PCB::skip-drc") != NULL;
+	}
+}
+
+
+/* ---------------------------------------------------------------------------
+ * loops till no more connections are found
+ */
+static pcb_bool DoIt(pcb_bool AndRats, pcb_bool AndDraw)
+{
+	pcb_bool newone = pcb_false;
+	reassign_no_drc_flags();
+	do {
+		/* lookup connections; these are the steps (2) to (4)
+		 * from the description
+		 */
+		newone = LookupPVConnectionsToPVList() ||
+			LookupLOConnectionsToPVList(AndRats) || LookupLOConnectionsToLOList(AndRats) || LookupPVConnectionsToLOList(AndRats);
+		if (AndDraw)
+			DrawNewConnections();
+	}
+	while (!newone && !ListsEmpty(AndRats));
+	if (AndDraw)
+		Draw();
+	return (newone);
+}
+
+/* ---------------------------------------------------------------------------
+ * draws all new connections which have been found since the
+ * routine was called the last time
+ */
+static void DrawNewConnections(void)
+{
+	int i;
+	pcb_cardinal_t position;
+
+	/* decrement 'i' to keep layerstack order */
+	for (i = max_copper_layer - 1; i != -1; i--) {
+		pcb_cardinal_t layer = LayerStack[i];
+
+		if (PCB->Data->Layer[layer].On) {
+			/* draw all new lines */
+			position = LineList[layer].DrawLocation;
+			for (; position < LineList[layer].Number; position++)
+				DrawLine(LAYER_PTR(layer), LINELIST_ENTRY(layer, position));
+			LineList[layer].DrawLocation = LineList[layer].Number;
+
+			/* draw all new arcs */
+			position = ArcList[layer].DrawLocation;
+			for (; position < ArcList[layer].Number; position++)
+				DrawArc(LAYER_PTR(layer), ARCLIST_ENTRY(layer, position));
+			ArcList[layer].DrawLocation = ArcList[layer].Number;
+
+			/* draw all new polygons */
+			position = PolygonList[layer].DrawLocation;
+			for (; position < PolygonList[layer].Number; position++)
+				DrawPolygon(LAYER_PTR(layer), POLYGONLIST_ENTRY(layer, position));
+			PolygonList[layer].DrawLocation = PolygonList[layer].Number;
+		}
+	}
+
+	/* draw all new pads */
+	if (PCB->PinOn)
+		for (i = 0; i < 2; i++) {
+			position = PadList[i].DrawLocation;
+
+			for (; position < PadList[i].Number; position++)
+				DrawPad(PADLIST_ENTRY(i, position));
+			PadList[i].DrawLocation = PadList[i].Number;
+		}
+
+	/* draw all new PVs; 'PVList' holds a list of pointers to the
+	 * sorted array pointers to PV data
+	 */
+	while (PVList.DrawLocation < PVList.Number) {
+		PinTypePtr pv = PVLIST_ENTRY(PVList.DrawLocation);
+
+		if (TEST_FLAG(PCB_FLAG_PIN, pv)) {
+			if (PCB->PinOn)
+				DrawPin(pv);
+		}
+		else if (PCB->ViaOn)
+			DrawVia(pv);
+		PVList.DrawLocation++;
+	}
+	/* draw the new rat-lines */
+	if (PCB->RatOn) {
+		position = RatList.DrawLocation;
+		for (; position < RatList.Number; position++)
+			DrawRat(RATLIST_ENTRY(position));
+		RatList.DrawLocation = RatList.Number;
+	}
+}
+
+/*---------------------------------------------------------------------------
+ * add the starting object to the list of found objects
+ */
+static pcb_bool ListStart(int type, void *ptr1, void *ptr2, void *ptr3)
+{
+	DumpList();
+	switch (type) {
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_VIA:
+		{
+			if (ADD_PV_TO_LIST((PinTypePtr) ptr2, 0, NULL, FCT_START))
+				return pcb_true;
+			break;
+		}
+
+	case PCB_TYPE_RATLINE:
+		{
+			if (ADD_RAT_TO_LIST((RatTypePtr) ptr1, 0, NULL, FCT_START))
+				return pcb_true;
+			break;
+		}
+
+	case PCB_TYPE_LINE:
+		{
+			int layer = GetLayerNumber(PCB->Data,
+																 (LayerTypePtr) ptr1);
+
+			if (ADD_LINE_TO_LIST(layer, (LineTypePtr) ptr2, 0, NULL, FCT_START))
+				return pcb_true;
+			break;
+		}
+
+	case PCB_TYPE_ARC:
+		{
+			int layer = GetLayerNumber(PCB->Data,
+																 (LayerTypePtr) ptr1);
+
+			if (ADD_ARC_TO_LIST(layer, (ArcTypePtr) ptr2, 0, NULL, FCT_START))
+				return pcb_true;
+			break;
+		}
+
+	case PCB_TYPE_POLYGON:
+		{
+			int layer = GetLayerNumber(PCB->Data,
+																 (LayerTypePtr) ptr1);
+
+			if (ADD_POLYGON_TO_LIST(layer, (PolygonTypePtr) ptr2, 0, NULL, FCT_START))
+				return pcb_true;
+			break;
+		}
+
+	case PCB_TYPE_PAD:
+		{
+			PadTypePtr pad = (PadTypePtr) ptr2;
+			if (ADD_PAD_TO_LIST(TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER, pad, 0, NULL, FCT_START))
+				return pcb_true;
+			break;
+		}
+	}
+	return (pcb_false);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * looks up all connections from the object at the given coordinates
+ * the TheFlag (normally 'PCB_FLAG_FOUND') is set for all objects found
+ * the objects are re-drawn if AndDraw is pcb_true
+ * also the action is marked as undoable if AndDraw is pcb_true
+ */
+void LookupConnection(Coord X, Coord Y, pcb_bool AndDraw, Coord Range, int which_flag)
+{
+	void *ptr1, *ptr2, *ptr3;
+	char *name;
+	int type;
+
+	/* check if there are any pins or pads at that position */
+
+	reassign_no_drc_flags();
+
+	type = SearchObjectByLocation(LOOKUP_FIRST, &ptr1, &ptr2, &ptr3, X, Y, Range);
+	if (type == PCB_TYPE_NONE) {
+		type = SearchObjectByLocation(LOOKUP_MORE, &ptr1, &ptr2, &ptr3, X, Y, Range);
+		if (type == PCB_TYPE_NONE)
+			return;
+		if (type & SILK_TYPE) {
+			int laynum = GetLayerNumber(PCB->Data,
+																	(LayerTypePtr) ptr1);
+
+			/* don't mess with non-conducting objects! */
+			if (laynum >= max_copper_layer || ((LayerTypePtr) ptr1)->no_drc)
+				return;
+		}
+	}
+	else {
+		name = ConnectionName(type, ptr1, ptr2);
+		hid_actionl("NetlistShow", name, NULL);
+	}
+
+	TheFlag = which_flag;
+	User = AndDraw;
+	InitConnectionLookup();
+
+	/* now add the object to the appropriate list and start scanning
+	 * This is step (1) from the description
+	 */
+	ListStart(type, ptr1, ptr2, ptr3);
+	DoIt(pcb_true, AndDraw);
+	if (User)
+		IncrementUndoSerialNumber();
+	User = pcb_false;
+
+	/* we are done */
+	if (AndDraw)
+		Draw();
+	if (AndDraw && conf_core.editor.beep_when_finished)
+		gui->beep();
+	FreeConnectionLookupMemory();
+}
+
+void LookupConnectionByPin(int type, void *ptr1)
+{
+	User = 0;
+	InitConnectionLookup();
+	ListStart(type, NULL, ptr1, NULL);
+
+	DoIt(pcb_true, pcb_false);
+
+	FreeConnectionLookupMemory();
+}
+
+
+/* ---------------------------------------------------------------------------
+ * find connections for rats nesting
+ * assumes InitConnectionLookup() has already been done
+ */
+void RatFindHook(int type, void *ptr1, void *ptr2, void *ptr3, pcb_bool undo, pcb_bool AndRats)
+{
+	User = undo;
+	DumpList();
+	ListStart(type, ptr1, ptr2, ptr3);
+	DoIt(AndRats, pcb_false);
+	User = pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * resets all used flags of pins and vias
+ */
+pcb_bool ResetFoundPinsViasAndPads(pcb_bool AndDraw)
+{
+	pcb_bool change = pcb_false;
+
+	VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(TheFlag, via)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+			CLEAR_FLAG(TheFlag, via);
+			if (AndDraw)
+				DrawVia(via);
+			change = pcb_true;
+		}
+	}
+	END_LOOP;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(TheFlag, pin)) {
+				if (AndDraw)
+					AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+				CLEAR_FLAG(TheFlag, pin);
+				if (AndDraw)
+					DrawPin(pin);
+				change = pcb_true;
+			}
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (TEST_FLAG(TheFlag, pad)) {
+				if (AndDraw)
+					AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+				CLEAR_FLAG(TheFlag, pad);
+				if (AndDraw)
+					DrawPad(pad);
+				change = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	if (change)
+		SetChangedFlag(pcb_true);
+	return change;
+}
+
+/* ---------------------------------------------------------------------------
+ * resets all used flags of LOs
+ */
+pcb_bool ResetFoundLinesAndPolygons(pcb_bool AndDraw)
+{
+	pcb_bool change = pcb_false;
+
+	RAT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(TheFlag, line)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_RATLINE, line, line, line);
+			CLEAR_FLAG(TheFlag, line);
+			if (AndDraw)
+				DrawRat(line);
+			change = pcb_true;
+		}
+	}
+	END_LOOP;
+	COPPERLINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(TheFlag, line)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_LINE, layer, line, line);
+			CLEAR_FLAG(TheFlag, line);
+			if (AndDraw)
+				DrawLine(layer, line);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	COPPERARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(TheFlag, arc)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_ARC, layer, arc, arc);
+			CLEAR_FLAG(TheFlag, arc);
+			if (AndDraw)
+				DrawArc(layer, arc);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	COPPERPOLYGON_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(TheFlag, polygon)) {
+			if (AndDraw)
+				AddObjectToFlagUndoList(PCB_TYPE_POLYGON, layer, polygon, polygon);
+			CLEAR_FLAG(TheFlag, polygon);
+			if (AndDraw)
+				DrawPolygon(layer, polygon);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	if (change)
+		SetChangedFlag(pcb_true);
+	return change;
+}
+
+/* ---------------------------------------------------------------------------
+ * resets all found connections
+ */
+pcb_bool ResetConnections(pcb_bool AndDraw)
+{
+	pcb_bool change = pcb_false;
+
+	change = ResetFoundPinsViasAndPads(AndDraw) || change;
+	change = ResetFoundLinesAndPolygons(AndDraw) || change;
+
+	return change;
+}
+
+/*----------------------------------------------------------------------------
+ * Dumps the list contents
+ */
+static void DumpList(void)
+{
+	pcb_cardinal_t i;
+
+	for (i = 0; i < 2; i++) {
+		PadList[i].Number = 0;
+		PadList[i].Location = 0;
+		PadList[i].DrawLocation = 0;
+	}
+
+	PVList.Number = 0;
+	PVList.Location = 0;
+
+	for (i = 0; i < max_copper_layer; i++) {
+		LineList[i].Location = 0;
+		LineList[i].DrawLocation = 0;
+		LineList[i].Number = 0;
+		ArcList[i].Location = 0;
+		ArcList[i].DrawLocation = 0;
+		ArcList[i].Number = 0;
+		PolygonList[i].Location = 0;
+		PolygonList[i].DrawLocation = 0;
+		PolygonList[i].Number = 0;
+	}
+	RatList.Number = 0;
+	RatList.Location = 0;
+	RatList.DrawLocation = 0;
+}
+
+/*----------------------------------------------------------------------------
+ * set up a temporary flag to use
+ */
+void SaveFindFlag(int NewFlag)
+{
+	OldFlag = TheFlag;
+	TheFlag = NewFlag;
+}
+
+/*----------------------------------------------------------------------------
+ * restore flag
+ */
+void RestoreFindFlag(void)
+{
+	TheFlag = OldFlag;
+}
+
+void InitConnectionLookup(void)
+{
+	InitComponentLookup();
+	InitLayoutLookup();
+}
+
+void FreeConnectionLookupMemory(void)
+{
+	FreeComponentLookupMemory();
+	FreeLayoutLookupMemory();
+}
diff --git a/src/find_print.c b/src/find_print.c
new file mode 100644
index 0000000..18d96e9
--- /dev/null
+++ b/src/find_print.c
@@ -0,0 +1,288 @@
+/*
+ *
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* Connection export/output functions */
+
+static pcb_bool PrepareNextLoop(FILE * FP);
+
+/* ---------------------------------------------------------------------------
+ * prints all unused pins of an element to file FP
+ */
+static pcb_bool PrintAndSelectUnusedPinsAndPadsOfElement(ElementTypePtr Element, FILE * FP)
+{
+	pcb_bool first = pcb_true;
+	pcb_cardinal_t number;
+
+	/* check all pins in element */
+
+	PIN_LOOP(Element);
+	{
+		if (!TEST_FLAG(PCB_FLAG_HOLE, pin)) {
+			/* pin might have bee checked before, add to list if not */
+			if (!TEST_FLAG(TheFlag, pin) && FP) {
+				int i;
+				if (ADD_PV_TO_LIST(pin, 0, NULL, 0))
+					return pcb_true;
+				DoIt(pcb_true, pcb_true);
+				number = PadList[COMPONENT_LAYER].Number + PadList[SOLDER_LAYER].Number + PVList.Number;
+				/* the pin has no connection if it's the only
+				 * list entry; don't count vias
+				 */
+				for (i = 0; i < PVList.Number; i++)
+					if (!PVLIST_ENTRY(i)->Element)
+						number--;
+				if (number == 1) {
+					/* output of element name if not already done */
+					if (first) {
+						PrintConnectionElementName(Element, FP);
+						first = pcb_false;
+					}
+
+					/* write name to list and draw selected object */
+					fputc('\t', FP);
+					PrintQuotedString(FP, (char *) EMPTY(pin->Name));
+					fputc('\n', FP);
+					SET_FLAG(PCB_FLAG_SELECTED, pin);
+					DrawPin(pin);
+				}
+
+				/* reset found objects for the next pin */
+				if (PrepareNextLoop(FP))
+					return (pcb_true);
+			}
+		}
+	}
+	END_LOOP;
+
+	/* check all pads in element */
+	PAD_LOOP(Element);
+	{
+		/* lookup pad in list */
+		/* pad might has bee checked before, add to list if not */
+		if (!TEST_FLAG(TheFlag, pad) && FP) {
+			int i;
+			if (ADD_PAD_TO_LIST(TEST_FLAG(PCB_FLAG_ONSOLDER, pad)
+													? SOLDER_LAYER : COMPONENT_LAYER, pad, 0, NULL, 0))
+				return pcb_true;
+			DoIt(pcb_true, pcb_true);
+			number = PadList[COMPONENT_LAYER].Number + PadList[SOLDER_LAYER].Number + PVList.Number;
+			/* the pin has no connection if it's the only
+			 * list entry; don't count vias
+			 */
+			for (i = 0; i < PVList.Number; i++)
+				if (!PVLIST_ENTRY(i)->Element)
+					number--;
+			if (number == 1) {
+				/* output of element name if not already done */
+				if (first) {
+					PrintConnectionElementName(Element, FP);
+					first = pcb_false;
+				}
+
+				/* write name to list and draw selected object */
+				fputc('\t', FP);
+				PrintQuotedString(FP, (char *) EMPTY(pad->Name));
+				fputc('\n', FP);
+
+				SET_FLAG(PCB_FLAG_SELECTED, pad);
+				DrawPad(pad);
+			}
+
+			/* reset found objects for the next pin */
+			if (PrepareNextLoop(FP))
+				return (pcb_true);
+		}
+	}
+	END_LOOP;
+
+	/* print separator if element has unused pins or pads */
+	if (!first) {
+		fputs("}\n\n", FP);
+		SEPARATE(FP);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * resets some flags for looking up the next pin/pad
+ */
+static pcb_bool PrepareNextLoop(FILE * FP)
+{
+	pcb_cardinal_t layer;
+
+	/* reset found LOs for the next pin */
+	for (layer = 0; layer < max_copper_layer; layer++) {
+		LineList[layer].Location = LineList[layer].Number = 0;
+		ArcList[layer].Location = ArcList[layer].Number = 0;
+		PolygonList[layer].Location = PolygonList[layer].Number = 0;
+	}
+
+	/* reset found pads */
+	for (layer = 0; layer < 2; layer++)
+		PadList[layer].Location = PadList[layer].Number = 0;
+
+	/* reset PVs */
+	PVList.Number = PVList.Location = 0;
+	RatList.Number = RatList.Location = 0;
+
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * finds all connections to the pins of the passed element.
+ * The result is written to file FP
+ * Returns pcb_true if operation was aborted
+ */
+static pcb_bool PrintElementConnections(ElementTypePtr Element, FILE * FP, pcb_bool AndDraw)
+{
+	PrintConnectionElementName(Element, FP);
+
+	/* check all pins in element */
+	PIN_LOOP(Element);
+	{
+		/* pin might have been checked before, add to list if not */
+		if (TEST_FLAG(TheFlag, pin)) {
+			PrintConnectionListEntry((char *) EMPTY(pin->Name), NULL, pcb_true, FP);
+			fputs("\t\t__CHECKED_BEFORE__\n\t}\n", FP);
+			continue;
+		}
+		if (ADD_PV_TO_LIST(pin, PCB_TYPE_ELEMENT, Element, FCT_ELEMENT))
+			return pcb_true;
+		DoIt(pcb_true, AndDraw);
+		/* printout all found connections */
+		PrintPinConnections(FP, pcb_true);
+		PrintPadConnections(COMPONENT_LAYER, FP, pcb_false);
+		PrintPadConnections(SOLDER_LAYER, FP, pcb_false);
+		fputs("\t}\n", FP);
+		if (PrepareNextLoop(FP))
+			return (pcb_true);
+	}
+	END_LOOP;
+
+	/* check all pads in element */
+	PAD_LOOP(Element);
+	{
+		pcb_cardinal_t layer;
+		/* pad might have been checked before, add to list if not */
+		if (TEST_FLAG(TheFlag, pad)) {
+			PrintConnectionListEntry((char *) EMPTY(pad->Name), NULL, pcb_true, FP);
+			fputs("\t\t__CHECKED_BEFORE__\n\t}\n", FP);
+			continue;
+		}
+		layer = TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SOLDER_LAYER : COMPONENT_LAYER;
+		if (ADD_PAD_TO_LIST(layer, pad, PCB_TYPE_ELEMENT, Element, FCT_ELEMENT))
+			return pcb_true;
+		DoIt(pcb_true, AndDraw);
+		/* print all found connections */
+		PrintPadConnections(layer, FP, pcb_true);
+		PrintPadConnections(layer == (COMPONENT_LAYER ? SOLDER_LAYER : COMPONENT_LAYER), FP, pcb_false);
+		PrintPinConnections(FP, pcb_false);
+		fputs("\t}\n", FP);
+		if (PrepareNextLoop(FP))
+			return (pcb_true);
+	}
+	END_LOOP;
+	fputs("}\n\n", FP);
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * find all unused pins of all element
+ */
+void LookupUnusedPins(FILE * FP)
+{
+	/* reset all currently marked connections */
+	User = pcb_true;
+	ResetConnections(pcb_true);
+	InitConnectionLookup();
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/* break if abort dialog returned pcb_true;
+		 * passing NULL as filedescriptor discards the normal output
+		 */
+		if (PrintAndSelectUnusedPinsAndPadsOfElement(element, FP))
+			break;
+	}
+	END_LOOP;
+
+	if (conf_core.editor.beep_when_finished)
+		gui->beep();
+	FreeConnectionLookupMemory();
+	IncrementUndoSerialNumber();
+	User = pcb_false;
+	Draw();
+}
+
+/* ---------------------------------------------------------------------------
+ * find all connections to pins within one element
+ */
+void LookupElementConnections(ElementTypePtr Element, FILE * FP)
+{
+	/* reset all currently marked connections */
+	User = pcb_true;
+	TheFlag = PCB_FLAG_FOUND;
+	ResetConnections(pcb_true);
+	InitConnectionLookup();
+	PrintElementConnections(Element, FP, pcb_true);
+	SetChangedFlag(pcb_true);
+	if (conf_core.editor.beep_when_finished)
+		gui->beep();
+	FreeConnectionLookupMemory();
+	IncrementUndoSerialNumber();
+	User = pcb_false;
+	Draw();
+}
+
+
+/* ---------------------------------------------------------------------------
+ * find all connections to pins of all element
+ */
+void LookupConnectionsToAllElements(FILE * FP)
+{
+	/* reset all currently marked connections */
+	User = pcb_false;
+	TheFlag = PCB_FLAG_FOUND;
+	ResetConnections(pcb_false);
+	InitConnectionLookup();
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/* break if abort dialog returned pcb_true */
+		if (PrintElementConnections(element, FP, pcb_false))
+			break;
+		SEPARATE(FP);
+		if (conf_core.editor.reset_after_element && gdl_it_idx(&__it__) != 1)
+			ResetConnections(pcb_false);
+	}
+	END_LOOP;
+	if (conf_core.editor.beep_when_finished)
+		gui->beep();
+	ResetConnections(pcb_false);
+	FreeConnectionLookupMemory();
+	Redraw();
+}
diff --git a/src/fptr_cast.c b/src/fptr_cast.c
new file mode 100644
index 0000000..8bf4dd1
--- /dev/null
+++ b/src/fptr_cast.c
@@ -0,0 +1,37 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <assert.h>
+#include "fptr_cast.h"
+
+void *pcb_cast_f2d(pcb_fptr_t f)
+{
+	assert(sizeof(void *) == sizeof(pcb_fptr_t));
+	return (void *)f;
+}
+
+pcb_fptr_t pcb_cast_d2f(void *d)
+{
+	assert(sizeof(void *) == sizeof(pcb_fptr_t));
+	return (pcb_fptr_t)d;
+}
+
diff --git a/src/fptr_cast.h b/src/fptr_cast.h
new file mode 100644
index 0000000..a3c6892
--- /dev/null
+++ b/src/fptr_cast.h
@@ -0,0 +1,34 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Function pointer <-> data pointer casts with assert on size match. C89
+   does not permit these casts but they practically work on any platform
+   pcb-rnd has the chance to run on. Hide the ugliness behind this API. */
+
+#ifndef PCB_FPTRCAST_H
+#define PCB_FPTRCAST_H
+typedef void (*pcb_fptr_t)();
+
+void *pcb_cast_f2d(pcb_fptr_t f);
+pcb_fptr_t pcb_cast_d2f(void *d);
+
+#endif
diff --git a/src/free_atexit.c b/src/free_atexit.c
new file mode 100644
index 0000000..97dea39
--- /dev/null
+++ b/src/free_atexit.c
@@ -0,0 +1,109 @@
+/*                            COPYRIGHT
+ *
+ *  Copyright (C) 2010 PCB Contributors (see ChangeLog for details)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+/* we need one ID per context - short int with 64k IDs should be enough */
+typedef unsigned int leaky_idx_t;
+
+
+/* This structure should be as big as void *, which should be the natural
+bit-width of the architecture. We allocate extra admin space to be as big
+as this union, to preserve alignment of pointers returned by malloc().
+NOTE: in the special corner case when leaky_idx_t is wider than void * but
+not multiple of it, the alignment will be messed up, potentially causing slower
+memory access. */
+typedef union {
+	leaky_idx_t idx;
+	void *ptr;
+} leaky_admin_t;
+
+static void **free_list = NULL;
+static leaky_idx_t free_size = 0;
+
+
+void *leaky_malloc(size_t size)
+{
+	char *new_memory = malloc(size + sizeof(leaky_admin_t));
+
+	free_list = (void **) realloc(free_list, (free_size + 1) * sizeof(void *));
+	free_list[free_size] = new_memory;
+	*(leaky_idx_t *) new_memory = free_size;
+
+	free_size++;
+	return new_memory + sizeof(leaky_admin_t);
+}
+
+void *leaky_calloc(size_t nmemb, size_t size)
+{
+	size_t size_ = size * nmemb;
+	void *new_memory = leaky_malloc(size_);
+
+	memset(new_memory, 0, size_);
+	return new_memory;
+}
+
+void *leaky_realloc(void *old_memory_, size_t size)
+{
+	char *new_memory;
+	char *old_memory = old_memory_;
+	leaky_idx_t i;
+
+	if (old_memory == NULL)
+		return leaky_malloc(size);
+
+	old_memory -= sizeof(leaky_admin_t);
+
+	i = *(leaky_idx_t *) old_memory;
+
+	new_memory = realloc(old_memory, size + sizeof(leaky_admin_t));
+	free_list[i] = new_memory;
+
+	return new_memory + sizeof(leaky_admin_t);
+}
+
+char *leaky_strdup(const char *src)
+{
+	int len = strlen(src)+1;
+	char *res = leaky_malloc(len);
+	memcpy(res, src, len);
+	return res;
+}
+
+void leaky_uninit(void)
+{
+	int i;
+
+	for (i = 0; i < free_size; i++)
+		free(free_list[i]);
+
+	free(free_list);
+	free_size = 0;
+}
+
+void leaky_init(void)
+{
+	atexit(leaky_uninit);
+}
diff --git a/src/free_atexit.h b/src/free_atexit.h
new file mode 100644
index 0000000..f12466b
--- /dev/null
+++ b/src/free_atexit.h
@@ -0,0 +1,38 @@
+/* This tiny library is to assist cleaning up harmless memory leaks caused
+   by (growing) buffers allocated in static variables in functions. The
+   library provides leaky_ prefixed variants of the common allocation
+   routines. These wrappers will remember all pointers they return and
+   can free all memory used, at the end of the application.
+*/
+
+#include <stdlib.h>
+
+#ifdef NDEBUG
+#define leaky_init()
+#define leaky_uninit()
+#define leaky_malloc(size) malloc(size)
+#define leaky_calloc(nmemb, size) calloc(nmemb, size)
+#define leaky_realloc(old_memory, size) realloc(old_memory, size)
+#define leaky_strdup(str) strdup(str)
+#else
+
+/* set up atexit() hook - can be avoided if leaky_uninit() is called by hand */
+void leaky_init(void);
+
+/* free all allocations */
+void leaky_uninit(void);
+
+/* allocate memory, remember the pointer and free it after exit from the application */
+void *leaky_malloc(size_t size);
+
+/* same as leaky_malloc but this one wraps calloc() */
+void *leaky_calloc(size_t nmemb, size_t size);
+
+/* reallocate memory, remember the new pointer and free it after exit from the application */
+void *leaky_realloc(void *old_memory, size_t size);
+
+/* strdup() using leaky_malloc() */
+char *leaky_strdup(const char *src);
+
+
+#endif
diff --git a/src/funchash.c b/src/funchash.c
new file mode 100644
index 0000000..5cca0a7
--- /dev/null
+++ b/src/funchash.c
@@ -0,0 +1,136 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <genht/htpi.h>
+#include <genht/hash.h>
+#include "funchash_core.h"
+#include "macro.h"
+
+#define action_entry(x) { #x, F_ ## x},
+static funchash_table_t Functions[] = {
+#include "funchash_core_list.h"
+	{"F_END", F_END}
+};
+
+typedef struct {
+	const char *cookie;
+	const char *key;
+	char key_buff[1];
+} fh_key_t;
+
+static htpi_t *funchash;
+
+static int keyeq(const void *a_, const void *b_)
+{
+	const fh_key_t *a = a_, *b = b_;
+	if (a->cookie != b->cookie)
+		return 1;
+	return !strcasecmp(a->key, b->key);
+}
+
+static unsigned fh_hash(const void *key)
+{
+	const fh_key_t *k = key;
+	return strhash_case((char *)k->key) ^ ptrhash((void *)k->cookie);
+}
+
+void funchash_init(void)
+{
+	funchash = htpi_alloc(fh_hash, keyeq);
+	funchash_set_table(Functions, ENTRIES(Functions), NULL);
+}
+
+void funchash_uninit(void)
+{
+	htpi_entry_t *e;
+
+	for (e = htpi_first(funchash); e; e = htpi_next(funchash, e)) {
+		fh_key_t *k = e->key;
+		if (k->cookie != NULL)
+			fprintf(stderr, "WARNING: function not removed: %s::%s\n", k->cookie, k->key);
+		htpi_pop(funchash, e->key);
+		free(e->key);
+	}
+	htpi_free(funchash);
+	funchash = NULL;
+}
+
+void funchash_remove_cookie(const char *cookie)
+{
+	htpi_entry_t *e;
+
+	/* Slow linear search - probably OK, this will run only on uninit */
+	for (e = htpi_first(funchash); e; e = htpi_next(funchash, e)) {
+		fh_key_t *k = e->key;
+		if (k->cookie == cookie) {
+			htpi_pop(funchash, e->key);
+			free(e->key);
+		}
+	}
+}
+
+int funchash_get(const char *key, const char *cookie)
+{
+	fh_key_t new_key;
+	htpi_entry_t *e;
+
+	if (key == NULL)
+		return -1;
+
+	new_key.cookie = cookie;
+	new_key.key = key;
+
+	e = htpi_getentry(funchash, &new_key);
+	if (e == NULL)
+		return -1;
+	return e->value;
+}
+
+int funchash_set(const char *key, int val, const char *cookie)
+{
+	fh_key_t *new_key;
+	int kl;
+
+	if (funchash_get(key, cookie) >= 0)
+		return -1;
+
+	kl = strlen(key);
+	new_key = malloc(sizeof(fh_key_t) + kl);
+	new_key->cookie = cookie;
+	new_key->key = new_key->key_buff;
+	strcpy(new_key->key_buff, key);
+	htpi_set(funchash, new_key, val);
+	return 0;
+}
+
+int funchash_set_table(funchash_table_t *table, int numelem, const char *cookie)
+{
+	int i, rv = 0;
+
+	for (i = 0; i < numelem; i++)
+		rv |= funchash_set(table[i].key, table[i].val, cookie);
+
+	return rv;
+}
diff --git a/src/funchash.h b/src/funchash.h
new file mode 100644
index 0000000..8a613e4
--- /dev/null
+++ b/src/funchash.h
@@ -0,0 +1,47 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Table entry format for funchash_set_table() */
+typedef struct {
+	const char *key;
+	int val;
+} funchash_table_t;
+
+/* Cookie is the namespace so that different modules can use the same
+   function names with different integer IDs without interference. Core
+   should use cookie==NULL. */
+
+/* Resolve a key string into an integer ID */
+int funchash_get(const char *key, const char *cookie);
+
+/* Store key string - integer ID pair */
+int funchash_set(const char *key, int val, const char *cookie);
+
+/* Store multiple key strings - integer ID pairs using a table */
+int funchash_set_table(funchash_table_t *table, int numelem, const char *cookie);
+
+/* Remove all keys inserted for a cookie */
+void funchash_remove_cookie(const char *cookie);
+
+/* Init-uninit the hash */
+void funchash_init(void);
+void funchash_uninit(void);
diff --git a/src/funchash_core.h b/src/funchash_core.h
new file mode 100644
index 0000000..0f0478c
--- /dev/null
+++ b/src/funchash_core.h
@@ -0,0 +1,10 @@
+/* central, auto-generated enum of core function IDs */
+
+#include "funchash.h"
+
+#define action_entry(x) F_ ## x,
+typedef enum {
+#include "funchash_core_list.h"
+F_END
+} FunctionID;
+#undef action_entry
diff --git a/src/funchash_core_list.h b/src/funchash_core_list.h
new file mode 100644
index 0000000..34fddde
--- /dev/null
+++ b/src/funchash_core_list.h
@@ -0,0 +1,126 @@
+/*
+	Central list of function IDs
+	The core and core plugins use these from a single, central hash
+	This list is used to cpp-generate the F_* constants in enum FunctionID
+*/
+action_entry(AddSelected)
+action_entry(All)
+action_entry(AllConnections)
+action_entry(AllRats)
+action_entry(AllUnusedPins)
+action_entry(Arc)
+action_entry(Arrow)
+action_entry(Block)
+action_entry(Description)
+action_entry(Cancel)
+action_entry(Center)
+action_entry(Clear)
+action_entry(ClearAndRedraw)
+action_entry(ClearList)
+action_entry(Close)
+action_entry(Connection)
+action_entry(Convert)
+action_entry(Copy)
+action_entry(CycleClip)
+action_entry(CycleCrosshair)
+action_entry(DeleteRats)
+action_entry(Drag)
+action_entry(DrillReport)
+action_entry(Element)
+action_entry(ElementByName)
+action_entry(ElementConnections)
+action_entry(ElementToBuffer)
+action_entry(Escape)
+action_entry(Find)
+action_entry(FlipElement)
+action_entry(FoundPins)
+action_entry(Grid)
+action_entry(InsertPoint)
+action_entry(Layer)
+action_entry(Layout)
+action_entry(LayoutAs)
+action_entry(LayoutToBuffer)
+action_entry(Line)
+action_entry(LineSize)
+action_entry(Lock)
+action_entry(Mirror)
+action_entry(Move)
+action_entry(NameOnPCB)
+action_entry(Netlist)
+action_entry(NetByName)
+action_entry(None)
+action_entry(Notify)
+action_entry(Object)
+action_entry(ObjectByName)
+action_entry(PasteBuffer)
+action_entry(PadByName)
+action_entry(PinByName)
+action_entry(PinOrPadName)
+action_entry(Pinout)
+action_entry(Polygon)
+action_entry(PolygonHole)
+action_entry(PreviousPoint)
+action_entry(RatsNest)
+action_entry(Rectangle)
+action_entry(Redraw)
+action_entry(Release)
+action_entry(Revert)
+action_entry(Remove)
+action_entry(RemoveSelected)
+action_entry(Report)
+action_entry(Reset)
+action_entry(ResetLinesAndPolygons)
+action_entry(ResetPinsViasAndPads)
+action_entry(Restore)
+action_entry(Rotate)
+action_entry(Save)
+action_entry(Selected)
+action_entry(SelectedArcs)
+action_entry(SelectedElements)
+action_entry(SelectedLines)
+action_entry(SelectedNames)
+action_entry(SelectedObjects)
+action_entry(SelectedPads)
+action_entry(SelectedPins)
+action_entry(SelectedTexts)
+action_entry(SelectedVias)
+action_entry(SelectedRats)
+action_entry(Stroke)
+action_entry(Text)
+action_entry(TextByName)
+action_entry(TextScale)
+action_entry(Thermal)
+action_entry(ToLayout)
+action_entry(ToggleAllDirections)
+action_entry(ToggleAutoDRC)
+action_entry(ToggleClearLine)
+action_entry(ToggleFullPoly)
+action_entry(ToggleGrid)
+action_entry(ToggleHideNames)
+action_entry(ToggleMinCut)
+action_entry(ToggleMask)
+action_entry(ToggleName)
+action_entry(ToggleObject)
+action_entry(ToggleShowDRC)
+action_entry(ToggleLiveRoute)
+action_entry(ToggleRubberBandMode)
+action_entry(ToggleStartDirection)
+action_entry(ToggleSnapPin)
+action_entry(ToggleSnapOffGridLine)
+action_entry(ToggleHighlightOnPoint)
+action_entry(ToggleThindraw)
+action_entry(ToggleLockNames)
+action_entry(ToggleOnlyNames)
+action_entry(ToggleThindrawPoly)
+action_entry(ToggleOrthoMove)
+action_entry(ToggleLocalRef)
+action_entry(ToggleCheckPlanes)
+action_entry(ToggleUniqueNames)
+action_entry(ToggleStroke)
+action_entry(Via)
+action_entry(ViaByName)
+action_entry(Value)
+action_entry(ViaDrillingHole)
+action_entry(ViaSize)
+action_entry(Zoom)
+
diff --git a/src/global.h b/src/global.h
new file mode 100644
index 0000000..7d95344
--- /dev/null
+++ b/src/global.h
@@ -0,0 +1,554 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2004 Thomas Nau
+ *  15 Oct 2008 Ineiev: add different crosshair shapes
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* definition of types */
+
+/* Change History:
+ * 10/11/96 11:37 AJF Added support for a Text() driver function.
+ * This was done out of a pressing need to force text to be printed on the
+ * silkscreen layer. Perhaps the design is not the best.
+ */
+
+#ifndef	PCB_GLOBAL_H
+#define	PCB_GLOBAL_H
+
+#include "config.h"
+
+#include "const.h"
+#include "macro.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "global_typedefs.h"
+#include "global_objs.h"
+#include "list_common.h"
+#include "list_line.h"
+#include "list_arc.h"
+#include "list_text.h"
+#include "list_poly.h"
+#include "list_pad.h"
+#include "list_pin.h"
+#include "list_rat.h"
+#include "vtonpoint.h"
+#include "hid.h"
+#include "polyarea.h"
+#include "vtroutestyle.h"
+
+
+/* Internationalization support. */
+#ifdef ENABLE_NLS
+#	include <libintl.h>
+#	define _(S) gettext(S)
+#	if defined(gettext_noop)
+#		define N_(S) gettext_noop(S)
+#	else
+#		define N_(S) (S)
+#	endif
+#else
+#	define _(S) (S)
+#	define N_(S) (S)
+#	define textdomain(S) (S)
+#	define gettext(S) (S)
+#	define dgettext(D, S) (S)
+#	define dcgettext(D, S, T) (S)
+#	define bindtextdomain(D, Dir) (D)
+#endif /* ENABLE_NLS */
+
+/* This is used by the lexer/parser */
+typedef struct {
+	int ival;
+	Coord bval;
+	double dval;
+	char has_units;
+} PLMeasure;
+
+/* ---------------------------------------------------------------------------
+ * Macros to annotate branch-prediction information.
+ * Taken from GLib 2.16.3 (LGPL 2).G_ / g_ prefixes have
+ * been removed to avoid namespace clashes.
+ */
+
+/* The LIKELY and UNLIKELY macros let the programmer give hints to
+ * the compiler about the expected result of an expression. Some compilers
+ * can use this information for optimizations.
+ *
+ * The PCB_BOOLEAN_EXPR macro is intended to trigger a gcc warning when
+ * putting assignments inside the test.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#	define PCB_BOOLEAN_EXPR(expr)                   \
+ __extension__ ({                             \
+   int _boolean_var_;                         \
+   if (expr)                                  \
+      _boolean_var_ = 1;                      \
+   else                                       \
+      _boolean_var_ = 0;                      \
+   _boolean_var_;                             \
+})
+#	define LIKELY(expr) (__builtin_expect (PCB_BOOLEAN_EXPR(expr), 1))
+#	define UNLIKELY(expr) (__builtin_expect (PCB_BOOLEAN_EXPR(expr), 0))
+#else
+#	define LIKELY(expr) (expr)
+#	define UNLIKELY(expr) (expr)
+#endif
+
+/* ---------------------------------------------------------------------------
+ * Macros to annotate branch-prediction information.
+ * Taken from GLib 2.42.1 (LGPL 2). PCB_ prefixes have
+ * been added to avoid namespace clashes.
+ */
+#define PCB_CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#define PCB_ABS(a)	   (((a) < 0) ? -(a) : (a))
+
+/* ---------------------------------------------------------------------------
+ * some useful values of our widgets
+ */
+typedef struct {								/* holds information about output window */
+	hidGC bgGC,										/* background and foreground; */
+	  fgGC,												/* changed from some routines */
+	  pmGC;												/* depth 1 pixmap GC to store clip */
+} OutputType, *OutputTypePtr;
+
+
+/* ----------------------------------------------------------------------
+ * layer group. A layer group identifies layers which are always switched
+ * on/off together.
+ */
+typedef struct {
+	pcb_cardinal_t Number[MAX_LAYER],		/* number of entries per groups */
+	  Entries[MAX_LAYER][MAX_LAYER + 2];
+} LayerGroupType, *LayerGroupTypePtr;
+
+typedef struct {
+	Coord x, y;
+	Coord width, height;
+} RectangleType, *RectangleTypePtr;
+
+/* ---------------------------------------------------------------------------
+ * the basic object types supported by PCB
+ */
+
+#include "global_element.h"
+
+struct rtree {
+	struct rtree_node *root;
+	int size;											/* number of entries in tree */
+};
+
+struct layer_st {								/* holds information about one layer */
+	const char *Name;		   				/* layer name */
+	linelist_t Line;
+	textlist_t Text;
+	polylist_t Polygon;
+	arclist_t Arc;
+	rtree_t *line_tree, *text_tree, *polygon_tree, *arc_tree;
+	pcb_bool On;											/* visible flag */
+	const char *Color;						/* color */
+	const char *SelectedColor;
+	AttributeListType Attributes;
+	int no_drc;										/* whether to ignore the layer when checking the design rules */
+};
+
+struct data_st {								/* holds all objects */
+	int LayerN;										/* number of layers in this board */
+	pinlist_t Via;
+	elementlist_t Element;
+	ratlist_t Rat;
+	rtree_t *via_tree, *element_tree, *pin_tree, *pad_tree, *name_tree[3],	/* for element names */
+	 *rat_tree;
+	struct PCBType *pcb;
+	LayerType Layer[MAX_LAYER + 2];	/* add 2 silkscreen layers */
+	plug_io_t *loader;
+};
+
+typedef struct {								/* holds drill information */
+	Coord DrillSize;							/* this drill's diameter */
+	pcb_cardinal_t ElementN,						/* the number of elements using this drill size */
+	  ElementMax,									/* max number of elements from malloc() */
+	  PinCount,										/* number of pins drilled this size */
+	  ViaCount,										/* number of vias drilled this size */
+	  UnplatedCount,							/* number of these holes that are unplated */
+	  PinN,												/* number of drill coordinates in the list */
+	  PinMax;											/* max number of coordinates from malloc() */
+	PinTypePtr *Pin;							/* coordinates to drill */
+	ElementTypePtr *Element;			/* a pointer to an array of element pointers */
+} DrillType, *DrillTypePtr;
+
+typedef struct {								/* holds a range of Drill Infos */
+	pcb_cardinal_t DrillN,							/* number of drill sizes */
+	  DrillMax;										/* max number from malloc() */
+	DrillTypePtr Drill;						/* plated holes */
+} DrillInfoType, *DrillInfoTypePtr;
+
+typedef struct LibraryEntryTpye_s  LibraryEntryType, *LibraryEntryTypePtr;
+typedef struct LibraryMenuType_s   LibraryMenuType, *LibraryMenuTypePtr;
+
+/* ---------------------------------------------------------------------------
+ * structure used by library routines
+ */
+struct LibraryEntryTpye_s {
+	const char *ListEntry;				/* the string for the selection box */
+	int ListEntry_dontfree;       /* do not free(ListEntry) if non-zero */
+	/* This used to contain some char *AllocatedMemory, possibly with
+	 * the intention of the following fields pointing into it.
+	 * It was never used that way, so removing for now.
+	 * TODO: re-introduce and actually use it for the following fields?
+	 */
+	const char *Package;	 				/* package */
+	const char *Value;						/* the value field */
+	const char *Description;			/* some descriptive text */
+#if 0
+	fp_type_t Type;
+	void **Tags;									/* an array of void * tag IDs; last tag ID is NULL */
+#endif
+};
+
+/* If the internal flag is set, the only field that is valid is Name,
+   and the struct is allocated with malloc instead of
+   CreateLibraryEntry.  These "internal" entries are used for
+   electrical paths that aren't yet assigned to a real net.  */
+
+struct LibraryMenuType_s {
+	char *Name,										/* name of the menu entry */
+	 *directory,									/* Directory name library elements are from */
+	 *Style;											/* routing style */
+	pcb_cardinal_t EntryN,							/* number of objects */
+	  EntryMax;										/* number of reserved memory locations */
+	LibraryEntryTypePtr Entry;		/* the entries */
+	char flag;										/* used by the netlist window to enable/disable nets */
+	char internal;								/* if set, this is an internal-only entry, not
+																   part of the global netlist. */
+};
+
+typedef struct {
+	pcb_cardinal_t MenuN;								/* number of objects */
+	pcb_cardinal_t MenuMax;							/* number of reserved memory locations */
+	LibraryMenuTypePtr Menu;			/* the entries */
+} LibraryType, *LibraryTypePtr;
+
+enum {
+	NETLIST_INPUT = 0,						/* index of the original netlist as imported */
+	NETLIST_EDITED = 1,						/* index of the netlist produced by applying netlist patches on [NETLIST_INPUT] */
+	NUM_NETLISTS									/* so that we know how many netlists we are dealing with */
+};
+
+
+	/* The PCBType struct holds information about board layout most of which is
+	   |  saved with the layout.  A new PCB layout struct is first initialized
+	   |  with values from the user configurable Settings struct and then reset
+	   |  to the saved layout values when a layout is loaded.
+	   |  This struct is also used for the remove list and for buffer handling
+	 */
+typedef struct PCBType {
+	long ID;											/* see macro.h */
+	char *Name,										/* name of board */
+	 *Filename,										/* name of file (from load) */
+	 *PrintFilename,							/* from print dialog */
+	 *Netlistname,								/* name of netlist file */
+	  ThermStyle;									/* type of thermal to place with thermal tool */
+	pcb_bool Changed,									/* layout has been changed */
+	  ViaOn,											/* visibility flags */
+	  ElementOn, RatOn, InvisibleObjectsOn, PinOn, SilkActive,	/* active layer is actually silk */
+	  RatDraw;										/* we're drawing rats */
+	char *ViaColor,								/* some colors */
+	 *ViaSelectedColor,
+		*PinColor,
+		*PinSelectedColor,
+		*PinNameColor,
+		*ElementColor,
+		*ElementColor_nonetlist,
+		*RatColor,
+		*InvisibleObjectsColor,
+		*InvisibleMarkColor, *ElementSelectedColor, *RatSelectedColor, *ConnectedColor, *WarnColor, *MaskColor;
+	Coord CursorX,									/* cursor position as saved with layout */
+	  CursorY;
+	Coord Bloat,									/* drc sizes saved with layout */
+	  Shrink, minWid, minSlk, minDrill, minRing;
+	Coord GridOffsetX,						/* as saved with layout */
+	  GridOffsetY, MaxWidth,			/* allowed size */
+	  MaxHeight;
+
+	Coord Grid;										/* used grid with offsets */
+	double Zoom,									/* zoom factor */
+	  IsleArea,										/* minimum poly island to retain */
+	  ThermScale;									/* scale factor used with thermals */
+	FontType Font;
+	LayerGroupType LayerGroups;
+	vtroutestyle_t RouteStyle;
+	LibraryType NetlistLib[NUM_NETLISTS];
+	rats_patch_line_t *NetlistPatches, *NetlistPatchLast;
+	AttributeListType Attributes;
+	DataTypePtr Data;							/* entire database */
+
+	pcb_bool is_footprint;						/* If set, the user has loaded a footprint, not a pcb. */
+
+/* netlist states */
+	int netlist_frozen;                /* counter */
+	unsigned netlist_needs_update:1;
+
+} PCBType, *PCBTypePtr;
+
+typedef struct {								/* information about the paste buffer */
+	Coord X, Y;										/* offset */
+	BoxType BoundingBox;
+	DataTypePtr Data;							/* data; not all members of PCBType */
+	/* are used */
+} BufferType, *BufferTypePtr;
+
+/* ---------------------------------------------------------------------------
+ * some types for cursor drawing, setting of block and lines
+ * as well as for merging of elements
+ */
+typedef struct {								/* rubberband lines for element moves */
+	LayerTypePtr Layer;						/* layer that holds the line */
+	LineTypePtr Line;							/* the line itself */
+	PointTypePtr MovedPoint;			/* and finally the point */
+} RubberbandType, *RubberbandTypePtr;
+
+typedef struct {								/* current marked line */
+	PointType Point1,							/* start- and end-position */
+	  Point2;
+	long int State;
+	pcb_bool draw;
+} AttachedLineType, *AttachedLineTypePtr;
+
+typedef struct {								/* currently marked block */
+	PointType Point1,							/* start- and end-position */
+	  Point2;
+	long int State;
+	pcb_bool otherway;
+} AttachedBoxType, *AttachedBoxTypePtr;
+
+typedef struct {								/* currently attached object */
+	Coord X, Y;										/* saved position when PCB_MODE_MOVE */
+	BoxType BoundingBox;
+	long int Type,								/* object type */
+	  State;
+	void *Ptr1,										/* three pointers to data, see */
+	 *Ptr2,												/* search.c */
+	 *Ptr3;
+	pcb_cardinal_t RubberbandN,					/* number of lines in array */
+	  RubberbandMax;
+	RubberbandTypePtr Rubberband;
+} AttachedObjectType, *AttachedObjectTypePtr;
+
+enum crosshair_shape {
+	Basic_Crosshair_Shape = 0,		/*  4-ray */
+	Union_Jack_Crosshair_Shape,		/*  8-ray */
+	Dozen_Crosshair_Shape,				/* 12-ray */
+	Crosshair_Shapes_Number
+};
+
+typedef struct {								/* holds cursor information */
+	hidGC GC,											/* GC for cursor drawing */
+	  AttachGC;										/* and for displaying buffer contents */
+	Coord X, Y,										/* position in PCB coordinates */
+	  MinX, MinY,									/* lowest and highest coordinates */
+	  MaxX, MaxY;
+	AttachedLineType AttachedLine;	/* data of new lines... */
+	AttachedBoxType AttachedBox;
+	PolygonType AttachedPolygon;
+	AttachedObjectType AttachedObject;	/* data of attached objects */
+	enum crosshair_shape shape;		/* shape of crosshair */
+	vtop_t onpoint_objs;
+	vtop_t old_onpoint_objs;
+
+	/* list of object IDs that could have been dragged so that they can be cycled */
+	long int *drags;
+	int drags_len, drags_current;
+	Coord dragx, dragy;						/* the point where drag started */
+} CrosshairType, *CrosshairTypePtr;
+
+typedef struct {
+	pcb_bool status;
+	Coord X, Y;
+} MarkType, *MarkTypePtr;
+
+/* ----------------------------------------------------------------------
+ * pointer to low-level copy, move and rotate functions
+ */
+typedef struct {
+	void *(*Line) (LayerTypePtr, LineTypePtr);
+	void *(*Text) (LayerTypePtr, TextTypePtr);
+	void *(*Polygon) (LayerTypePtr, PolygonTypePtr);
+	void *(*Via) (PinTypePtr);
+	void *(*Element) (ElementTypePtr);
+	void *(*ElementName) (ElementTypePtr);
+	void *(*Pin) (ElementTypePtr, PinTypePtr);
+	void *(*Pad) (ElementTypePtr, PadTypePtr);
+	void *(*LinePoint) (LayerTypePtr, LineTypePtr, PointTypePtr);
+	void *(*Point) (LayerTypePtr, PolygonTypePtr, PointTypePtr);
+	void *(*Arc) (LayerTypePtr, ArcTypePtr);
+	void *(*Rat) (RatTypePtr);
+} ObjectFunctionType, *ObjectFunctionTypePtr;
+
+/* ---------------------------------------------------------------------------
+ * structure used by device drivers
+ */
+
+typedef struct {								/* holds a connection */
+	Coord X, Y;										/* coordinate of connection */
+	long int type;								/* type of object in ptr1 - 3 */
+	void *ptr1, *ptr2;						/* the object of the connection */
+	pcb_cardinal_t group;								/* the layer group of the connection */
+	LibraryMenuType *menu;				/* the netmenu this *SHOULD* belong too */
+} ConnectionType, *ConnectionTypePtr;
+
+struct net_st {								/* holds a net of connections */
+	pcb_cardinal_t ConnectionN,					/* the number of connections contained */
+	  ConnectionMax;							/* max connections from malloc */
+	ConnectionTypePtr Connection;
+	RouteStyleTypePtr Style;
+};
+
+typedef struct {								/* holds a list of nets */
+	pcb_cardinal_t NetN,								/* the number of subnets contained */
+	  NetMax;											/* max subnets from malloc */
+	NetTypePtr Net;
+} NetListType, *NetListTypePtr;
+
+typedef struct {								/* holds a list of net lists */
+	pcb_cardinal_t NetListN,						/* the number of net lists contained */
+	  NetListMax;									/* max net lists from malloc */
+	NetListTypePtr NetList;
+} NetListListType, *NetListListTypePtr;
+
+typedef struct {								/* holds a generic list of pointers */
+	pcb_cardinal_t PtrN,								/* the number of pointers contained */
+	  PtrMax;											/* max subnets from malloc */
+	void **Ptr;
+} PointerListType, *PointerListTypePtr;
+
+typedef struct {
+	pcb_cardinal_t BoxN,								/* the number of boxes contained */
+	  BoxMax;											/* max boxes from malloc */
+	BoxTypePtr Box;
+
+} BoxListType, *BoxListTypePtr;
+
+struct drc_violation_st {
+	char *title;
+	char *explanation;
+	Coord x, y;
+	Angle angle;
+	int have_measured;
+	Coord measured_value;
+	Coord required_value;
+	int object_count;
+	long int *object_id_list;
+	int *object_type_list;
+};
+
+typedef enum {
+	RATP_ADD_CONN,
+	RATP_DEL_CONN,
+	RATP_CHANGE_ATTRIB
+} rats_patch_op_t;
+
+struct rats_patch_line_s {
+	rats_patch_op_t op;
+	char *id;
+	union {
+		char *net_name;
+		char *attrib_name;
+	} arg1;
+	union {
+		char *attrib_val;
+	} arg2;
+
+	rats_patch_line_t *prev, *next;
+};
+
+/* ---------------------------------------------------------------------------
+ * define supported types of undo operations
+ * note these must be separate bits now
+ */
+#define	UNDO_CHANGENAME			0x0001	/* change of names */
+#define	UNDO_MOVE			0x0002		/* moving objects */
+#define	UNDO_REMOVE			0x0004	/* removing objects */
+#define	UNDO_REMOVE_POINT		0x0008	/* removing polygon/... points */
+#define	UNDO_INSERT_POINT		0x0010	/* inserting polygon/... points */
+#define	UNDO_REMOVE_CONTOUR		0x0020	/* removing a contour from a polygon */
+#define	UNDO_INSERT_CONTOUR		0x0040	/* inserting a contour from a polygon */
+#define	UNDO_ROTATE			0x0080	/* rotations */
+#define	UNDO_CREATE			0x0100	/* creation of objects */
+#define	UNDO_MOVETOLAYER		0x0200	/* moving objects to */
+#define	UNDO_FLAG			0x0400		/* toggling SELECTED flag */
+#define	UNDO_CHANGESIZE			0x0800	/* change size of object */
+#define	UNDO_CHANGE2NDSIZE		0x1000	/* change 2ndSize of object */
+#define	UNDO_MIRROR			0x2000	/* change side of board */
+#define	UNDO_CHANGECLEARSIZE		0x4000	/* change clearance size */
+#define	UNDO_CHANGEMASKSIZE		0x8000	/* change mask size */
+#define	UNDO_CHANGEANGLES	       0x10000	/* change arc angles */
+#define	UNDO_LAYERCHANGE	       0x20000	/* layer new/delete/move */
+#define	UNDO_CLEAR		       0x40000	/* clear/restore to polygons */
+#define	UNDO_NETLISTCHANGE	       0x80000	/* netlist change */
+#define	UNDO_CHANGEPINNUM			0x100000	/* change of pin number */
+#define	UNDO_CHANGERADII	    0x200000	/* change arc radii */
+
+
+
+/* ---------------------------------------------------------------------------
+ * add a macro for wrapping RCS ID's in so that ident will still work
+ * but we won't get as many compiler warnings
+ */
+
+#ifndef GCC_VERSION
+#	define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif /* GCC_VERSION */
+
+#if GCC_VERSION > 2007
+#	define ATTRIBUTE_UNUSED __attribute__((unused))
+#else
+#	define ATTRIBUTE_UNUSED
+#endif
+
+/* ---------------------------------------------------------------------------
+ * Macros called by various action routines to show usage or to report
+ * a syntax error and fail
+ */
+#define AUSAGE(x) Message (PCB_MSG_INFO, "Usage:\n%s\n", (x##_syntax))
+#define AFAIL(x) { Message (PCB_MSG_ERROR, "Syntax error.  Usage:\n%s\n", (x##_syntax)); return 1; }
+
+/* Make sure to catch usage of non-portable functions in debug mode */
+#ifndef NDEBUG
+#	undef strdup
+#	undef strndup
+#	undef snprintf
+#	undef round
+#	define strdup      never_use_strdup__use_pcb_strdup
+#	define strndup     never_use_strndup__use_pcb_strndup
+#	define snprintf    never_use_snprintf__use_pcb_snprintf
+#	define round       never_use_round__use_pcb_round
+#endif
+
+#endif /* PCB_GLOBAL_H  */
diff --git a/src/global_element.h b/src/global_element.h
new file mode 100644
index 0000000..beeb072
--- /dev/null
+++ b/src/global_element.h
@@ -0,0 +1,17 @@
+struct element_st {
+	ANYOBJECTFIELDS;
+	TextType Name[MAX_ELEMENTNAMES];	/* the elements names; */
+	/* description text */
+	/* name on PCB second, */
+	/* value third */
+	/* see macro.h */
+	Coord MarkX, MarkY;						/* position mark */
+	pinlist_t Pin;
+	padlist_t Pad;
+	linelist_t Line;
+	arclist_t Arc;
+	BoxType VBox;
+	gdl_elem_t link;
+};
+
+#include "list_element.h"
diff --git a/src/global_objs.h b/src/global_objs.h
new file mode 100644
index 0000000..4ec33d8
--- /dev/null
+++ b/src/global_objs.h
@@ -0,0 +1,210 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2004 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#ifndef GLOBAL_OBJS_H
+#define GLOBAL_OBJS_H
+#include <genlist/gendlist.h>
+#include "config.h"
+#include "globalconst.h"
+#include "global_typedefs.h"
+#include "polyarea.h"
+
+typedef struct {
+	char *name;
+	char *value;
+} AttributeType, *AttributeTypePtr;
+
+struct AttributeListType {
+	int Number, Max;
+	AttributeType *List;
+};
+
+/* ---------------------------------------------------------------------------
+ * Do not change the following definitions even if they're not very
+ * nice.  It allows us to have functions act on these "base types" and
+ * not need to know what kind of actual object they're working on.
+ */
+
+/* Any object that uses the "object flags" defined in const.h, or
+   exists as an object on the pcb, MUST be defined using this as the
+   first fields, either directly or through ANYLINEFIELDS.  */
+#define ANYOBJECTFIELDS			\
+	BoxType		BoundingBox;	\
+	long int	ID;		\
+	FlagType	Flags; \
+	AttributeListType Attributes
+
+	/*  struct LibraryEntryType *net */
+
+/* Lines, pads, and rats all use this so they can be cross-cast.  */
+#define	ANYLINEFIELDS			\
+	ANYOBJECTFIELDS;		\
+	Coord		Thickness,      \
+                        Clearance;      \
+	PointType	Point1,		\
+			Point2
+
+struct BoxType {								/* a bounding box */
+	Coord X1, Y1;									/* upper left */
+	Coord X2, Y2;									/* and lower right corner */
+};
+
+/* Nobody should know about the internals of this except the macros in
+   macros.h that access it.  This structure must be simple-assignable
+   for now.  */
+typedef struct unknown_flag_s unknown_flag_t;
+struct unknown_flag_s {
+	char *str;
+	unknown_flag_t *next;
+};
+
+typedef struct {
+	unsigned long f;							/* generic flags */
+	unsigned char t[(MAX_LAYER + 1) / 2];	/* thermals */
+	unsigned char q;							/* square geometry flag */
+	unsigned char int_conn_grp;
+	unknown_flag_t *unknowns;
+} FlagType, *FlagTypePtr;
+
+
+/* All on-pcb objects (elements, lines, pads, vias, rats, etc) are
+   based on this. */
+typedef struct {
+	ANYOBJECTFIELDS;
+} AnyObjectType, *AnyObjectTypePtr;
+
+typedef struct {								/* a line/polygon point */
+	Coord X, Y, X2, Y2;						/* so Point type can be cast as BoxType */
+	long int ID;
+} PointType, *PointTypePtr;
+
+/* Lines, rats, pads, etc.  */
+typedef struct {
+	ANYLINEFIELDS;
+} AnyLineObjectType, *AnyLineObjectTypePtr;
+
+typedef struct line_st {								/* holds information about one line */
+	ANYLINEFIELDS;
+	char *Number;
+	gdl_elem_t link;  /* a line is in a list: either on a layer or in an element */
+} LineType, *LineTypePtr;
+
+typedef struct text_st {
+	ANYOBJECTFIELDS;
+	int Scale;										/* text scaling in percent */
+	Coord X, Y;										/* origin */
+	pcb_uint8_t Direction;
+	char *TextString;							/* string */
+	void *Element;
+	gdl_elem_t link;              /* a text is in a list of a layer or an element */
+} TextType, *TextTypePtr;
+
+struct polygon_st {							/* holds information about a polygon */
+	ANYOBJECTFIELDS;
+	pcb_cardinal_t PointN,							/* number of points in polygon */
+	  PointMax;										/* max number from malloc() */
+	POLYAREA *Clipped;						/* the clipped region of this polygon */
+	PLINE *NoHoles;								/* the polygon broken into hole-less regions */
+	int NoHolesValid;							/* Is the NoHoles polygon up to date? */
+	PointTypePtr Points;					/* data */
+	pcb_cardinal_t *HoleIndex;					/* Index of hole data within the Points array */
+	pcb_cardinal_t HoleIndexN;					/* number of holes in polygon */
+	pcb_cardinal_t HoleIndexMax;				/* max number from malloc() */
+	gdl_elem_t link;              /* a text is in a list of a layer */
+};
+
+typedef struct arc_st {								/* holds information about arcs */
+	ANYOBJECTFIELDS;
+	Coord Thickness, Clearance;
+	Coord Width, Height,					/* length of axis */
+	  X, Y;												/* center coordinates */
+	Angle StartAngle, Delta;			/* the two limiting angles in degrees */
+	gdl_elem_t link;              /* an arc is in a list: either on a layer or in an element */
+} ArcType, *ArcTypePtr;
+
+typedef struct rat_st {								/* a rat-line */
+	ANYLINEFIELDS;
+	pcb_cardinal_t group1, group2;			/* the layer group each point is on */
+	gdl_elem_t link;              /* an arc is in a list on a design */
+} RatType, *RatTypePtr;
+
+struct pad_st {									/* a SMD pad */
+	ANYLINEFIELDS;
+	Coord Mask;
+	char *Name, *Number;					/* 'Line' */
+	void *Element;
+	void *Spare;
+	gdl_elem_t link;              /* a pad is in a list (element) */
+};
+
+struct pin_st {
+	ANYOBJECTFIELDS;
+	Coord Thickness, Clearance, Mask, DrillingHole;
+	Coord X, Y;										/* center and diameter */
+	char *Name, *Number;
+	void *Element;
+	void *Spare;
+	gdl_elem_t link;              /* a pin is in a list (element) */
+};
+
+/* This is the extents of a Pin or Via, depending on whether it's a
+   hole or not.  */
+#define PIN_SIZE(pinptr) (TEST_FLAG(PCB_FLAG_HOLE, (pinptr)) \
+			  ? (pinptr)->DrillingHole \
+			  : (pinptr)->Thickness)
+
+/* ---------------------------------------------------------------------------
+ * symbol and font related stuff
+ */
+typedef struct symbol_st {								/* a single symbol */
+	LineTypePtr Line;
+	pcb_bool Valid;
+	pcb_cardinal_t LineN,								/* number of lines */
+	  LineMax;
+	Coord Width, Height,					/* size of cell */
+	  Delta;											/* distance to next symbol */
+} SymbolType, *SymbolTypePtr;
+
+typedef struct font_st {								/* complete set of symbols */
+	Coord MaxHeight,							/* maximum cell width and height */
+	  MaxWidth;
+	BoxType DefaultSymbol;				/* the default symbol is a filled box */
+	SymbolType Symbol[MAX_FONTPOSITION + 1];
+	pcb_bool Valid;
+} FontType, *FontTypePtr;
+
+
+/* TODO: this could be replaced with pcb_obj_t */
+typedef struct onpoint_st {
+	int type;
+	union {
+		void *any;
+		LineType *line;
+		ArcType *arc;
+	} obj;
+} OnpointType;
+
+#endif
diff --git a/src/global_typedefs.h b/src/global_typedefs.h
new file mode 100644
index 0000000..e172828
--- /dev/null
+++ b/src/global_typedefs.h
@@ -0,0 +1,49 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GLOBAL_TYPEDEFS_H
+#define GLOBAL_TYPEDEFS_H
+#include "config.h"
+
+typedef struct BoxType BoxType, *BoxTypePtr;
+typedef struct polygon_st PolygonType, *PolygonTypePtr;
+typedef struct pad_st PadType, *PadTypePtr;
+typedef struct pin_st PinType, *PinTypePtr, **PinTypeHandle;
+typedef struct drc_violation_st DrcViolationType, *DrcViolationTypePtr;
+typedef struct rtree rtree_t;
+typedef struct AttributeListType AttributeListType, *AttributeListTypePtr;
+typedef struct rats_patch_line_s rats_patch_line_t;
+typedef struct element_st ElementType, *ElementTypePtr, **ElementTypeHandle;
+typedef struct net_st NetType, *NetTypePtr;
+typedef struct layer_st LayerType, *LayerTypePtr;
+typedef struct data_st  DataType, *DataTypePtr;
+typedef struct plug_io_s plug_io_t;
+
+
+typedef unsigned int pcb_cardinal_t;
+typedef unsigned char pcb_uint8_t;   /* Don't use in new code. */
+
+#include "pcb_bool.h"
+
+#include "unit.h"
+
+#endif
diff --git a/src/globalconst.h b/src/globalconst.h
new file mode 100644
index 0000000..4b942de
--- /dev/null
+++ b/src/globalconst.h
@@ -0,0 +1,119 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* global constants
+ * most of these values are also required by files outside the source tree
+ * (manuals...)
+ */
+
+#ifndef	__GLOBALCONST_INCLUDED__
+#define	__GLOBALCONST_INCLUDED__
+
+#include "config.h"
+
+
+/* frame between the groundplane and the copper or mask - noone seems
+   to remember what these two are for; changing them may have unforeseen
+   side effects. */
+#define	GROUNDPLANEFRAME        PCB_MIL_TO_COORD(15)
+#define MASKFRAME               PCB_MIL_TO_COORD(3)
+
+/* ---------------------------------------------------------------------------
+ * some limit specifications
+ */
+#define LARGE_VALUE		(COORD_MAX / 2 - 1) /* maximum extent of board and elements */
+ 
+#define	MAX_LAYER		16	/* max number of layer, check source */
+					/* code for more changes, a *lot* more changes */
+#define	MIN_LINESIZE		PCB_MIL_TO_COORD(0.01)	/* thickness of lines */
+#define	MAX_LINESIZE		LARGE_VALUE
+#define MIN_ARCSIZE		PCB_MIL_TO_COORD(0.01)
+#define MAX_ARCSIZE		LARGE_VALUE
+#define	MIN_TEXTSCALE		10	/* scaling of text objects in percent */
+#define	MAX_TEXTSCALE		10000
+#define	MIN_PINORVIASIZE	PCB_MIL_TO_COORD(20)	/* size of a pin or via */
+#define	MIN_PINORVIAHOLE	PCB_MIL_TO_COORD(4)	/* size of a pins or vias drilling hole */
+#define	MAX_PINORVIASIZE	LARGE_VALUE
+#define	MIN_PINORVIACOPPER	PCB_MIL_TO_COORD(4)	/* min difference outer-inner diameter */
+#define	MIN_PADSIZE		PCB_MIL_TO_COORD(1)	/* min size of a pad */
+#define	MAX_PADSIZE		LARGE_VALUE   /* max size of a pad */
+#define	MIN_DRC_VALUE		PCB_MIL_TO_COORD(0.1)
+#define	MAX_DRC_VALUE		PCB_MIL_TO_COORD(500)
+#define	MIN_DRC_SILK		PCB_MIL_TO_COORD(1)
+#define	MAX_DRC_SILK		PCB_MIL_TO_COORD(30)
+#define	MIN_DRC_DRILL		PCB_MIL_TO_COORD(1)
+#define	MAX_DRC_DRILL		PCB_MIL_TO_COORD(50)
+#define	MIN_DRC_RING		0
+#define	MAX_DRC_RING		PCB_MIL_TO_COORD(100)
+#define	MIN_GRID		1
+#define	MAX_GRID		PCB_MIL_TO_COORD(1000)
+#define	MAX_FONTPOSITION	255	/* upper limit of characters in my font */
+
+#define	MAX_COORD		LARGE_VALUE	/* coordinate limits */
+#define	MIN_SIZE		PCB_MIL_TO_COORD(10)	/* lowest width and height of the board */
+#define	MAX_BUFFER		5	/* number of pastebuffers */
+					/* additional changes in menu.c are */
+					/* also required to select more buffers */
+
+#define	DEFAULT_DRILLINGHOLE	40	/* default inner/outer ratio for */
+					/* pins/vias in percent */
+
+#if MAX_LINESIZE > MAX_PINORVIASIZE	/* maximum size value */
+#define	MAX_SIZE	MAX_LINESIZE
+#else
+#define	MAX_SIZE	MAX_PINORVIASIZE
+#endif
+
+#ifndef	MAXPATHLEN			/* maximum path length */
+#ifdef	PATH_MAX
+#define	MAXPATHLEN	PATH_MAX
+#else
+#define	MAXPATHLEN	2048
+#endif
+#endif
+
+#define	MAX_LINE_POINT_DISTANCE		0	/* maximum distance when searching */
+						/* line points */
+#define	MAX_POLYGON_POINT_DISTANCE	0	/* maximum distance when searching */
+						/* polygon points */
+#define	MAX_ELEMENTNAMES		3	/* number of supported names of */
+						/* an element */
+#define MAX_NETLIST_LINE_LENGTH		255	/* maximum line length for netlist files */
+#define	MAX_MODESTACK_DEPTH		16	/* maximum depth of mode stack */
+#define	MIN_GRID_DISTANCE		4	/* minimum distance between point */
+						/* to enable grid drawing */
+	/* size of diamond element mark */
+#define EMARK_SIZE	PCB_MIL_TO_COORD (10)
+
+
+/**** Font ***/
+/* These are used in debug draw font rendering (e.g. pin names) and reverse
+   scale calculations (e.g. when report is trying to figure how the font
+   is scaled. Changing these values is not really supported. */
+#define  FONT_CAPHEIGHT    PCB_MIL_TO_COORD (45)   /* (Approximate) capheight size of the default PCB font */
+#define  DEFAULT_CELLSIZE  50                  /* default cell size for symbols */
+
+#endif
diff --git a/src/gui_act.c b/src/gui_act.c
new file mode 100644
index 0000000..ea2b9f1
--- /dev/null
+++ b/src/gui_act.c
@@ -0,0 +1,1273 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+#include "data.h"
+#include "action_helper.h"
+#include "error.h"
+#include "undo.h"
+#include "funchash_core.h"
+
+#include "draw.h"
+#include "search.h"
+#include "crosshair.h"
+#include "find.h"
+#include "set.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "stub_stroke.h"
+#include "hid_actions.h"
+#include "hid_init.h"
+#include "route_style.h"
+#include "layer.h"
+
+/* --------------------------------------------------------------------------- */
+/* Toggle actions are kept for compatibility; new code should use the conf system instead */
+static const char display_syntax[] =
+	"Display(NameOnPCB|Description|Value)\n"
+	"Display(Grid|Redraw)\n"
+	"Display(CycleClip|CycleCrosshair|ToggleAllDirections|ToggleStartDirection)\n"
+	"Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
+	"Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly)\n"
+	"Display(ToggleSnapPin|ToggleSnapOffGridLine|ToggleHighlightOnPoint)\n"
+	"Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
+	"Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
+	"Display(ToggleLiveRoute|LockNames|OnlyNames)\n" "Display(Pinout|PinOrPadName)";
+
+static const char display_help[] = "Several display-related actions.";
+
+/* %start-doc actions Display
+
+ at table @code
+
+ at item NameOnPCB
+ at item Description
+ at item Value
+Specify whether all elements show their name, description, or value.
+
+ at item Redraw
+Redraw the whole board.
+
+ at item ToggleAllDirections
+When clear, lines can be drawn at any angle.  When set, lines are
+restricted to multiples of 45 degrees and requested lines may be
+broken up according to the clip setting.
+
+ at item CycleClip
+Changes the way lines are restricted to 45 degree increments.  The
+various settings are: straight only, orthogonal then angled, and angled
+then orthogonal.  If AllDirections is set, this action disables it.
+
+ at item CycleCrosshair
+Changes crosshair drawing.  Crosshair may accept form of 4-ray,
+8-ray and 12-ray cross.
+
+ at item ToggleRubberBandMode
+If set, moving an object moves all the lines attached to it too.
+
+ at item ToggleStartDirection
+If set, each time you set a point in a line, the Clip toggles between
+orth-angle and angle-ortho.
+
+ at item ToggleUniqueNames
+If set, you will not be permitted to change the name of an element to
+match that of another element.
+
+ at item ToggleSnapPin
+If set, pin centers and pad end points are treated as additional grid
+points that the cursor can snap to.
+
+ at item ToggleSnapOffGridLine
+If set, snap at some sensible point along a line.
+
+ at item ToggleHighlightOnPoint
+If set, highlights lines and arcs when the crosshair is on one of their
+two (end) points.
+
+ at item ToggleLocalRef
+If set, the mark is automatically set to the beginning of any move, so
+you can see the relative distance you've moved.
+
+ at item ToggleThindraw
+If set, objects on the screen are drawn as outlines (lines are drawn
+as center-lines).  This lets you see line endpoints hidden under pins,
+for example.
+
+ at item ToggleThindrawPoly
+If set, polygons on the screen are drawn as outlines.
+
+ at item ToggleShowDRC
+If set, pending objects (i.e. lines you're in the process of drawing)
+will be drawn with an outline showing how far away from other copper
+you need to be.
+
+ at item ToggleLiveRoute
+If set, the progress of the autorouter will be visible on the screen.
+
+ at item ToggleAutoDRC
+If set, you will not be permitted to make connections which violate
+the current DRC and netlist settings.
+
+ at item ToggleCheckPlanes
+If set, lines and arcs aren't drawn, which usually leaves just the
+polygons.  If you also disable all but the layer you're interested in,
+this allows you to check for isolated regions.
+
+ at item ToggleOrthoMove
+If set, the crosshair is only allowed to move orthogonally from its
+previous position.  I.e. you can move an element or line up, down,
+left, or right, but not up+left or down+right.
+
+ at item ToggleName
+Selects whether the pinouts show the pin names or the pin numbers.
+
+ at item ToggleLockNames
+If set, text will ignore left mouse clicks and actions that work on
+objects under the mouse. You can still select text with a lasso (left
+mouse drag) and perform actions on the selection.
+
+ at item ToggleOnlyNames
+If set, only text will be sensitive for mouse clicks and actions that
+work on objects under the mouse. You can still select other objects
+with a lasso (left mouse drag) and perform actions on the selection.
+
+ at item ToggleMask
+Turns the solder mask on or off.
+
+ at item ToggleClearLine
+When set, the clear-line flag causes new lines and arcs to have their
+``clear polygons'' flag set, so they won't be electrically connected
+to any polygons they overlap.
+
+ at item ToggleFullPoly
+When set, the full-poly flag causes new polygons to have their
+``full polygon'' flag set, so all parts of them will be displayed
+instead of only the biggest one.
+
+ at item ToggleGrid
+Resets the origin of the current grid to be wherever the mouse pointer
+is (not where the crosshair currently is).  If you provide two numbers
+after this, the origin is set to that coordinate.
+
+ at item Grid
+Toggles whether the grid is displayed or not.
+
+ at item Pinout
+Causes the pinout of the element indicated by the cursor to be
+displayed, usually in a separate window.
+
+ at item PinOrPadName
+Toggles whether the names of pins, pads, or (yes) vias will be
+displayed.  If the cursor is over an element, all of its pins and pads
+are affected.
+
+ at end table
+
+%end-doc */
+
+static enum crosshair_shape CrosshairShapeIncrement(enum crosshair_shape shape)
+{
+	switch (shape) {
+	case Basic_Crosshair_Shape:
+		shape = Union_Jack_Crosshair_Shape;
+		break;
+	case Union_Jack_Crosshair_Shape:
+		shape = Dozen_Crosshair_Shape;
+		break;
+	case Dozen_Crosshair_Shape:
+		shape = Crosshair_Shapes_Number;
+		break;
+	case Crosshair_Shapes_Number:
+		shape = Basic_Crosshair_Shape;
+		break;
+	}
+	return shape;
+}
+
+static int ActionDisplay(int argc, const char **argv, Coord childX, Coord childY)
+{
+	const char *function, *str_dir;
+	int id;
+	int err = 0;
+
+	function = ACTION_ARG(0);
+	str_dir = ACTION_ARG(1);
+
+	if (function && (!str_dir || !*str_dir)) {
+		switch (id = funchash_get(function, NULL)) {
+
+			/* redraw layout */
+		case F_ClearAndRedraw:
+		case F_Redraw:
+			Redraw();
+			break;
+
+			/* change the displayed name of elements */
+		case F_Value:
+		case F_NameOnPCB:
+		case F_Description:
+			ELEMENT_LOOP(PCB->Data);
+			{
+				EraseElementName(element);
+			}
+			END_LOOP;
+			switch (id) {
+			case F_Value:
+				if (conf_core.editor.description || conf_core.editor.name_on_pcb) {
+					conf_set_editor(description, 0);
+					conf_set_editor(name_on_pcb, 0);
+				}
+				else
+					conf_set_editor(name_on_pcb, 0); /* need to write once so the event is triggered */
+				break;
+			case F_NameOnPCB:
+				if (conf_core.editor.description || !conf_core.editor.name_on_pcb) {
+					conf_set_editor(description, 0);
+					conf_set_editor(name_on_pcb, 1);
+				}
+				else
+					conf_set_editor(name_on_pcb, 1); /* need to write once so the event is triggered */
+				break;
+			case F_Description:
+				if (!conf_core.editor.description || conf_core.editor.name_on_pcb) {
+					conf_set_editor(description, 1);
+					conf_set_editor(name_on_pcb, 0);
+				}
+				else
+					conf_set_editor(name_on_pcb, 0); /* need to write once so the event is triggered */
+				break;
+			}
+			ELEMENT_LOOP(PCB->Data);
+			{
+				DrawElementName(element);
+			}
+			END_LOOP;
+			Draw();
+			break;
+
+			/* toggle line-adjust flag */
+		case F_ToggleAllDirections:
+			conf_toggle_editor(all_direction_lines);
+			AdjustAttachedObjects();
+			break;
+
+		case F_CycleClip:
+			notify_crosshair_change(pcb_false);
+			if (conf_core.editor.all_direction_lines) {
+				conf_toggle_editor(all_direction_lines);
+				conf_setf(CFR_DESIGN,"editor/line_refraction",-1,"%d",0);
+			}
+			else {
+				conf_setf(CFR_DESIGN,"editor/line_refraction",-1,"%d",(conf_core.editor.line_refraction +1) % 3);
+			}
+			AdjustAttachedObjects();
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_CycleCrosshair:
+			notify_crosshair_change(pcb_false);
+			Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
+			if (Crosshair_Shapes_Number == Crosshair.shape)
+				Crosshair.shape = Basic_Crosshair_Shape;
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleRubberBandMode:
+			notify_crosshair_change(pcb_false);
+			conf_toggle_editor(rubber_band_mode);
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleStartDirection:
+			notify_crosshair_change(pcb_false);
+			conf_toggle_editor(swap_start_direction);
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleUniqueNames:
+			conf_toggle_editor(unique_names);
+			break;
+
+		case F_ToggleSnapPin:
+			notify_crosshair_change(pcb_false);
+			conf_toggle_editor(snap_pin);
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleSnapOffGridLine:
+			notify_crosshair_change(pcb_false);
+			conf_toggle_editor(snap_offgrid_line);
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleHighlightOnPoint:
+			notify_crosshair_change(pcb_false);
+			conf_toggle_editor(highlight_on_point);
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleLocalRef:
+			conf_toggle_editor(local_ref);
+			break;
+
+		case F_ToggleThindraw:
+			conf_toggle_editor(thin_draw);
+			Redraw();
+			break;
+
+		case F_ToggleThindrawPoly:
+			conf_toggle_editor(thin_draw_poly);
+			Redraw();
+			break;
+
+		case F_ToggleLockNames:
+			conf_toggle_editor(lock_names);
+			conf_set_editor(only_names, 0);
+			break;
+
+		case F_ToggleOnlyNames:
+			conf_toggle_editor(only_names);
+			conf_set_editor(lock_names, 0);
+			break;
+
+		case F_ToggleHideNames:
+			conf_toggle_editor(hide_names);
+			Redraw();
+			break;
+
+		case F_ToggleStroke:
+			conf_toggle_editor(enable_stroke);
+			break;
+
+		case F_ToggleShowDRC:
+			conf_toggle_editor(show_drc);
+			break;
+
+		case F_ToggleLiveRoute:
+			conf_toggle_editor(live_routing);
+			break;
+
+		case F_ToggleAutoDRC:
+			notify_crosshair_change(pcb_false);
+			conf_toggle_editor(auto_drc);
+			if (conf_core.editor.auto_drc && conf_core.editor.mode == PCB_MODE_LINE) {
+				if (ResetConnections(pcb_true)) {
+					IncrementUndoSerialNumber();
+					Draw();
+				}
+				if (Crosshair.AttachedLine.State != STATE_FIRST)
+					LookupConnection(Crosshair.AttachedLine.Point1.X, Crosshair.AttachedLine.Point1.Y, pcb_true, 1, PCB_FLAG_FOUND);
+			}
+			notify_crosshair_change(pcb_true);
+			break;
+
+		case F_ToggleCheckPlanes:
+			conf_toggle_editor(check_planes);
+			Redraw();
+			break;
+
+		case F_ToggleOrthoMove:
+			conf_toggle_editor(orthogonal_moves);
+			break;
+
+		case F_ToggleName:
+			conf_toggle_editor(show_number);
+			Redraw();
+			break;
+
+		case F_ToggleMask:
+			conf_toggle_editor(show_mask);
+			Redraw();
+			break;
+
+		case F_ToggleClearLine:
+			conf_toggle_editor(clear_line);
+			break;
+
+		case F_ToggleFullPoly:
+			conf_toggle_editor(full_poly);
+			break;
+
+			/* shift grid alignment */
+		case F_ToggleGrid:
+			{
+				Coord oldGrid = PCB->Grid;
+
+				PCB->Grid = 1;
+				if (MoveCrosshairAbsolute(Crosshair.X, Crosshair.Y))
+					notify_crosshair_change(pcb_true);	/* first notify was in MoveCrosshairAbs */
+				SetGrid(oldGrid, pcb_true);
+			}
+			break;
+
+			/* toggle displaying of the grid */
+		case F_Grid:
+			conf_toggle_editor(draw_grid);
+			Redraw();
+			break;
+
+			/* display the pinout of an element */
+		case F_Pinout:
+			{
+				ElementTypePtr element;
+				void *ptrtmp;
+				Coord x, y;
+
+				gui->get_coords(_("Click on an element"), &x, &y);
+				if ((SearchScreen(x, y, PCB_TYPE_ELEMENT, &ptrtmp, &ptrtmp, &ptrtmp)) != PCB_TYPE_NONE) {
+					element = (ElementTypePtr) ptrtmp;
+					gui->show_item(element);
+				}
+				break;
+			}
+
+			/* toggle displaying of pin/pad/via names */
+		case F_PinOrPadName:
+			{
+				void *ptr1, *ptr2, *ptr3;
+				Coord x, y;
+				gui->get_coords(_("Click on an element"), &x, &y);
+
+				switch (SearchScreen(x, y,
+														 PCB_TYPE_ELEMENT | PCB_TYPE_PIN | PCB_TYPE_PAD |
+														 PCB_TYPE_VIA, (void **) &ptr1, (void **) &ptr2, (void **) &ptr3)) {
+				case PCB_TYPE_ELEMENT:
+					PIN_LOOP((ElementTypePtr) ptr1);
+					{
+						if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, pin))
+							ErasePinName(pin);
+						else
+							DrawPinName(pin);
+						AddObjectToFlagUndoList(PCB_TYPE_PIN, ptr1, pin, pin);
+						TOGGLE_FLAG(PCB_FLAG_DISPLAYNAME, pin);
+					}
+					END_LOOP;
+					PAD_LOOP((ElementTypePtr) ptr1);
+					{
+						if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, pad))
+							ErasePadName(pad);
+						else
+							DrawPadName(pad);
+						AddObjectToFlagUndoList(PCB_TYPE_PAD, ptr1, pad, pad);
+						TOGGLE_FLAG(PCB_FLAG_DISPLAYNAME, pad);
+					}
+					END_LOOP;
+					SetChangedFlag(pcb_true);
+					IncrementUndoSerialNumber();
+					Draw();
+					break;
+
+				case PCB_TYPE_PIN:
+					if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, (PinTypePtr) ptr2))
+						ErasePinName((PinTypePtr) ptr2);
+					else
+						DrawPinName((PinTypePtr) ptr2);
+					AddObjectToFlagUndoList(PCB_TYPE_PIN, ptr1, ptr2, ptr3);
+					TOGGLE_FLAG(PCB_FLAG_DISPLAYNAME, (PinTypePtr) ptr2);
+					SetChangedFlag(pcb_true);
+					IncrementUndoSerialNumber();
+					Draw();
+					break;
+
+				case PCB_TYPE_PAD:
+					if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, (PadTypePtr) ptr2))
+						ErasePadName((PadTypePtr) ptr2);
+					else
+						DrawPadName((PadTypePtr) ptr2);
+					AddObjectToFlagUndoList(PCB_TYPE_PAD, ptr1, ptr2, ptr3);
+					TOGGLE_FLAG(PCB_FLAG_DISPLAYNAME, (PadTypePtr) ptr2);
+					SetChangedFlag(pcb_true);
+					IncrementUndoSerialNumber();
+					Draw();
+					break;
+				case PCB_TYPE_VIA:
+					if (TEST_FLAG(PCB_FLAG_DISPLAYNAME, (PinTypePtr) ptr2))
+						EraseViaName((PinTypePtr) ptr2);
+					else
+						DrawViaName((PinTypePtr) ptr2);
+					AddObjectToFlagUndoList(PCB_TYPE_VIA, ptr1, ptr2, ptr3);
+					TOGGLE_FLAG(PCB_FLAG_DISPLAYNAME, (PinTypePtr) ptr2);
+					SetChangedFlag(pcb_true);
+					IncrementUndoSerialNumber();
+					Draw();
+					break;
+				}
+				break;
+			}
+		default:
+			err = 1;
+		}
+	}
+	else if (function && str_dir) {
+		switch (funchash_get(function, NULL)) {
+		case F_ToggleGrid:
+			if (argc > 2) {
+				PCB->GridOffsetX = GetValue(argv[1], NULL, NULL, NULL);
+				PCB->GridOffsetY = GetValue(argv[2], NULL, NULL, NULL);
+				if (conf_core.editor.draw_grid)
+					Redraw();
+			}
+			break;
+
+		default:
+			err = 1;
+			break;
+		}
+	}
+
+	if (!err)
+		return 0;
+
+	AFAIL(display);
+}
+/* --------------------------------------------------------------------------- */
+
+static const char mode_syntax[] =
+	"Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
+	"Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n" "Mode(Notify|Release|Cancel|Stroke)\n" "Mode(Save|Restore)";
+
+static const char mode_help[] = "Change or use the tool mode.";
+
+/* %start-doc actions Mode
+
+ at table @code
+
+ at item Arc
+ at itemx Arrow
+ at itemx Copy
+ at itemx InsertPoint
+ at itemx Line
+ at itemx Lock
+ at itemx Move
+ at itemx None
+ at itemx PasteBuffer
+ at itemx Polygon
+ at itemx Rectangle
+ at itemx Remove
+ at itemx Rotate
+ at itemx Text
+ at itemx Thermal
+ at itemx Via
+Select the indicated tool.
+
+ at item Notify
+Called when you press the mouse button, or move the mouse.
+
+ at item Release
+Called when you release the mouse button.
+
+ at item Cancel
+Cancels any pending tool activity, allowing you to restart elsewhere.
+For example, this allows you to start a new line rather than attach a
+line to the previous line.
+
+ at item Escape
+Similar to Cancel but calling this action a second time will return
+to the Arrow tool.
+
+ at item Stroke
+If your @code{pcb} was built with libstroke, this invokes the stroke
+input method.  If not, this will restart a drawing mode if you were
+drawing, else it will select objects.
+
+ at item Save
+Remembers the current tool.
+
+ at item Restore
+Restores the tool to the last saved tool.
+
+ at end table
+
+%end-doc */
+
+static int ActionMode(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+
+	if (function) {
+		Note.X = Crosshair.X;
+		Note.Y = Crosshair.Y;
+		notify_crosshair_change(pcb_false);
+		switch (funchash_get(function, NULL)) {
+		case F_Arc:
+			SetMode(PCB_MODE_ARC);
+			break;
+		case F_Arrow:
+			SetMode(PCB_MODE_ARROW);
+			break;
+		case F_Copy:
+			SetMode(PCB_MODE_COPY);
+			break;
+		case F_InsertPoint:
+			SetMode(PCB_MODE_INSERT_POINT);
+			break;
+		case F_Line:
+			SetMode(PCB_MODE_LINE);
+			break;
+		case F_Lock:
+			SetMode(PCB_MODE_LOCK);
+			break;
+		case F_Move:
+			SetMode(PCB_MODE_MOVE);
+			break;
+		case F_None:
+			SetMode(PCB_MODE_NO);
+			break;
+		case F_Cancel:
+			{
+				int saved_mode = conf_core.editor.mode;
+				SetMode(PCB_MODE_NO);
+				SetMode(saved_mode);
+			}
+			break;
+		case F_Escape:
+			{
+				switch (conf_core.editor.mode) {
+				case PCB_MODE_VIA:
+				case PCB_MODE_PASTE_BUFFER:
+				case PCB_MODE_TEXT:
+				case PCB_MODE_ROTATE:
+				case PCB_MODE_REMOVE:
+				case PCB_MODE_MOVE:
+				case PCB_MODE_COPY:
+				case PCB_MODE_INSERT_POINT:
+				case PCB_MODE_RUBBERBAND_MOVE:
+				case PCB_MODE_THERMAL:
+				case PCB_MODE_LOCK:
+					SetMode(PCB_MODE_NO);
+					SetMode(PCB_MODE_ARROW);
+					break;
+
+				case PCB_MODE_LINE:
+					if (Crosshair.AttachedLine.State == STATE_FIRST)
+						SetMode(PCB_MODE_ARROW);
+					else {
+						SetMode(PCB_MODE_NO);
+						SetMode(PCB_MODE_LINE);
+					}
+					break;
+
+				case PCB_MODE_RECTANGLE:
+					if (Crosshair.AttachedBox.State == STATE_FIRST)
+						SetMode(PCB_MODE_ARROW);
+					else {
+						SetMode(PCB_MODE_NO);
+						SetMode(PCB_MODE_RECTANGLE);
+					}
+					break;
+
+				case PCB_MODE_POLYGON:
+					if (Crosshair.AttachedLine.State == STATE_FIRST)
+						SetMode(PCB_MODE_ARROW);
+					else {
+						SetMode(PCB_MODE_NO);
+						SetMode(PCB_MODE_POLYGON);
+					}
+					break;
+
+				case PCB_MODE_POLYGON_HOLE:
+					if (Crosshair.AttachedLine.State == STATE_FIRST)
+						SetMode(PCB_MODE_ARROW);
+					else {
+						SetMode(PCB_MODE_NO);
+						SetMode(PCB_MODE_POLYGON_HOLE);
+					}
+					break;
+
+				case PCB_MODE_ARC:
+					if (Crosshair.AttachedBox.State == STATE_FIRST)
+						SetMode(PCB_MODE_ARROW);
+					else {
+						SetMode(PCB_MODE_NO);
+						SetMode(PCB_MODE_ARC);
+					}
+					break;
+
+				case PCB_MODE_ARROW:
+					break;
+
+				default:
+					break;
+				}
+			}
+			break;
+
+		case F_Notify:
+			NotifyMode();
+			break;
+		case F_PasteBuffer:
+			SetMode(PCB_MODE_PASTE_BUFFER);
+			break;
+		case F_Polygon:
+			SetMode(PCB_MODE_POLYGON);
+			break;
+		case F_PolygonHole:
+			SetMode(PCB_MODE_POLYGON_HOLE);
+			break;
+		case F_Release:
+			if ((mid_stroke) && (conf_core.editor.enable_stroke))
+				stub_stroke_finish();
+			else
+				ReleaseMode();
+			break;
+		case F_Remove:
+			SetMode(PCB_MODE_REMOVE);
+			break;
+		case F_Rectangle:
+			SetMode(PCB_MODE_RECTANGLE);
+			break;
+		case F_Rotate:
+			SetMode(PCB_MODE_ROTATE);
+			break;
+		case F_Stroke:
+			if (conf_core.editor.enable_stroke) {
+				stub_stroke_start();
+				break;
+			}
+			/* Handle middle mouse button restarts of drawing mode.  If not in
+			   |  a drawing mode, middle mouse button will select objects.
+			 */
+			if (conf_core.editor.mode == PCB_MODE_LINE && Crosshair.AttachedLine.State != STATE_FIRST) {
+				SetMode(PCB_MODE_LINE);
+			}
+			else if (conf_core.editor.mode == PCB_MODE_ARC && Crosshair.AttachedBox.State != STATE_FIRST)
+				SetMode(PCB_MODE_ARC);
+			else if (conf_core.editor.mode == PCB_MODE_RECTANGLE && Crosshair.AttachedBox.State != STATE_FIRST)
+				SetMode(PCB_MODE_RECTANGLE);
+			else if (conf_core.editor.mode == PCB_MODE_POLYGON && Crosshair.AttachedLine.State != STATE_FIRST)
+				SetMode(PCB_MODE_POLYGON);
+			else {
+				SaveMode();
+				saved_mode = pcb_true;
+				SetMode(PCB_MODE_ARROW);
+				NotifyMode();
+			}
+			break;
+		case F_Text:
+			SetMode(PCB_MODE_TEXT);
+			break;
+		case F_Thermal:
+			SetMode(PCB_MODE_THERMAL);
+			break;
+		case F_Via:
+			SetMode(PCB_MODE_VIA);
+			break;
+
+		case F_Restore:						/* restore the last saved mode */
+			RestoreMode();
+			break;
+
+		case F_Save:								/* save currently selected mode */
+			SaveMode();
+			break;
+		}
+		notify_crosshair_change(pcb_true);
+		return 0;
+	}
+
+	AFAIL(mode);
+}
+
+/* ---------------------------------------------------------------- */
+static const char cycledrag_syntax[] = "CycleDrag()\n";
+
+static const char cycledrag_help[] = "Cycle through which object is being dragged";
+
+#define close_enough(a, b) ((((a)-(b)) > 0) ? ((a)-(b) < (SLOP * pixel_slop)) : ((a)-(b) > -(SLOP * pixel_slop)))
+static int ActionCycleDrag(int argc, const char **argv, Coord x, Coord y)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int over = 0;
+
+	if (Crosshair.drags == NULL)
+		return 0;
+
+	do {
+		Crosshair.drags_current++;
+		if (Crosshair.drags_current >= Crosshair.drags_len) {
+			Crosshair.drags_current = 0;
+			over++;
+		}
+
+		if (SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Crosshair.drags[Crosshair.drags_current], PCB_TYPE_LINE) != PCB_TYPE_NONE) {
+			/* line has two endpoints, check which one is close to the original x;y */
+			LineType *l = ptr2;
+			if (close_enough(Note.X, l->Point1.X) && close_enough(Note.Y, l->Point1.Y)) {
+				Crosshair.AttachedObject.Type = PCB_TYPE_LINE_POINT;
+				Crosshair.AttachedObject.Ptr1 = ptr1;
+				Crosshair.AttachedObject.Ptr2 = ptr2;
+				Crosshair.AttachedObject.Ptr3 = &l->Point1;
+				return 0;
+			}
+			if (close_enough(Note.X, l->Point2.X) && close_enough(Note.Y, l->Point2.Y)) {
+				Crosshair.AttachedObject.Type = PCB_TYPE_LINE_POINT;
+				Crosshair.AttachedObject.Ptr1 = ptr1;
+				Crosshair.AttachedObject.Ptr2 = ptr2;
+				Crosshair.AttachedObject.Ptr3 = &l->Point2;
+				return 0;
+			}
+		}
+		else if (SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Crosshair.drags[Crosshair.drags_current], PCB_TYPE_VIA) != PCB_TYPE_NONE) {
+			Crosshair.AttachedObject.Type = PCB_TYPE_VIA;
+			Crosshair.AttachedObject.Ptr1 = ptr1;
+			Crosshair.AttachedObject.Ptr2 = ptr2;
+			Crosshair.AttachedObject.Ptr3 = ptr3;
+			return 0;
+		}
+		else if (SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Crosshair.drags[Crosshair.drags_current], PCB_TYPE_PAD) != PCB_TYPE_NONE) {
+			Crosshair.AttachedObject.Type = PCB_TYPE_ELEMENT;
+			Crosshair.AttachedObject.Ptr1 = ptr1;
+			Crosshair.AttachedObject.Ptr2 = ptr1;
+			Crosshair.AttachedObject.Ptr3 = ptr1;
+			return 0;
+		}
+		else if (SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Crosshair.drags[Crosshair.drags_current], PCB_TYPE_ARC) != PCB_TYPE_NONE) {
+			Crosshair.AttachedObject.Type = PCB_TYPE_ARC;
+			Crosshair.AttachedObject.Ptr1 = ptr1;
+			Crosshair.AttachedObject.Ptr2 = ptr2;
+			Crosshair.AttachedObject.Ptr3 = ptr3;
+			return 0;
+		}
+
+	} while (over <= 1);
+
+	return -1;
+}
+
+#undef close_enough
+
+/* -------------------------------------------------------------------------- */
+
+static const char message_syntax[] = "Message(message)";
+
+static const char message_help[] = "Writes a message to the log window.";
+
+/* %start-doc actions Message
+
+This action displays a message to the log window.  This action is primarily
+provided for use by other programs which may interface with PCB.  If
+multiple arguments are given, each one is sent to the log window
+followed by a newline.
+
+%end-doc */
+
+static int ActionMessage(int argc, const char **argv, Coord x, Coord y)
+{
+	int i;
+
+	if (argc < 1)
+		AFAIL(message);
+
+	for (i = 0; i < argc; i++) {
+		Message(PCB_MSG_DEFAULT, argv[i]);
+		Message(PCB_MSG_DEFAULT, "\n");
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char togglehidename_syntax[] = "ToggleHideName(Object|SelectedElements)";
+
+static const char togglehidename_help[] = "Toggles the visibility of element names.";
+
+/* %start-doc actions ToggleHideName
+
+If names are hidden you won't see them on the screen and they will not
+appear on the silk layer when you print the layout.
+
+%end-doc */
+
+static int ActionToggleHideName(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function && PCB->ElementOn) {
+		switch (funchash_get(function, NULL)) {
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, PCB_TYPE_ELEMENT, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE) {
+					AddObjectToFlagUndoList(type, ptr1, ptr2, ptr3);
+					EraseElementName((ElementTypePtr) ptr2);
+					TOGGLE_FLAG(PCB_FLAG_HIDENAME, (ElementTypePtr) ptr2);
+					DrawElementName((ElementTypePtr) ptr2);
+					Draw();
+					IncrementUndoSerialNumber();
+				}
+				break;
+			}
+		case F_SelectedElements:
+		case F_Selected:
+			{
+				pcb_bool changed = pcb_false;
+				ELEMENT_LOOP(PCB->Data);
+				{
+					if ((TEST_FLAG(PCB_FLAG_SELECTED, element) || TEST_FLAG(PCB_FLAG_SELECTED, &NAMEONPCB_TEXT(element)))
+							&& (FRONT(element) || PCB->InvisibleObjectsOn)) {
+						AddObjectToFlagUndoList(PCB_TYPE_ELEMENT, element, element, element);
+						EraseElementName(element);
+						TOGGLE_FLAG(PCB_FLAG_HIDENAME, element);
+						DrawElementName(element);
+						changed = pcb_true;
+					}
+				}
+				END_LOOP;
+				if (changed) {
+					Draw();
+					IncrementUndoSerialNumber();
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char markcrosshair_syntax[] = "MarkCrosshair()\n" "MarkCrosshair(Center)";
+
+static const char markcrosshair_help[] = "Set/Reset the Crosshair mark.";
+
+/* %start-doc actions MarkCrosshair
+
+The ``mark'' is a small X-shaped target on the display which is
+treated like a second origin (the normal origin is the upper let
+corner of the board).  The GUI will display a second set of
+coordinates for this mark, which tells you how far you are from it.
+
+If no argument is given, the mark is toggled - disabled if it was
+enabled, or enabled at the current cursor position of disabled.  If
+the @code{Center} argument is given, the mark is moved to the current
+cursor location.
+
+%end-doc */
+
+static int ActionMarkCrosshair(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (!function || !*function) {
+		if (Marked.status) {
+			notify_mark_change(pcb_false);
+			Marked.status = pcb_false;
+			notify_mark_change(pcb_true);
+		}
+		else {
+			notify_mark_change(pcb_false);
+			Marked.status = pcb_false;
+			Marked.status = pcb_true;
+			Marked.X = Crosshair.X;
+			Marked.Y = Crosshair.Y;
+			notify_mark_change(pcb_true);
+		}
+	}
+	else if (funchash_get(function, NULL) == F_Center) {
+		notify_mark_change(pcb_false);
+		Marked.status = pcb_true;
+		Marked.X = Crosshair.X;
+		Marked.Y = Crosshair.Y;
+		notify_mark_change(pcb_true);
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
+
+static const char routestyle_help[] = "Copies the indicated routing style into the current sizes.";
+
+/* %start-doc actions RouteStyle
+
+%end-doc */
+
+static int ActionRouteStyle(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *str = ACTION_ARG(0);
+	RouteStyleType *rts;
+	int number;
+
+	if (str) {
+		char *end;
+		number = strtol(str, &end, 10);
+
+		if (*end != '\0') { /* if not an integer, find by name */
+			int n;
+			number = -1;
+			for(n = 0; n < vtroutestyle_len(&PCB->RouteStyle); n++) {
+				rts = &PCB->RouteStyle.array[n];
+				if (strcasecmp(rts->name, str) == 0) {
+					number = n + 1;
+					break;
+				}
+			}
+		}
+
+		if (number > 0 && number <= vtroutestyle_len(&PCB->RouteStyle)) {
+			rts = &PCB->RouteStyle.array[number - 1];
+			SetLineSize(rts->Thick);
+			SetViaSize(rts->Diameter, pcb_true);
+			SetViaDrillingHole(rts->Hole, pcb_true);
+			SetClearanceWidth(rts->Clearance);
+			hid_action("RouteStylesChanged");
+		}
+		else
+			Message(PCB_MSG_DEFAULT, "Error: invalid route style name or index\n");
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char createmenu_syntax[] = "CreateMenu(path | path, action, mnemonic, accel, tooltip, cookie)";
+static const char createmenu_help[] = "Creates a new menu, popup (only path specified) or submenu (at least path and action are specified)";
+
+/* %start-doc actions RouteStyle
+
+%end-doc */
+
+static int ActionCreateMenu(int argc, const char **argv, Coord x, Coord y)
+{
+	if (gui == NULL) {
+		Message(PCB_MSG_DEFAULT, "Error: can't create menu, there's no GUI hid loaded\n");
+		return 1;
+	}
+
+	if (argc > 0) {
+		gui->create_menu(argv[0], (argc > 1) ? argv[1] : NULL, (argc > 2) ? argv[2] : NULL, (argc > 3) ? argv[3] : NULL, (argc > 4) ? argv[4] : NULL, (argc > 5) ? argv[5] : NULL);
+		return 0;
+	}
+
+	AFAIL(message);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char removemenu_syntax[] = "RemoveMenu(path|cookie)";
+static const char removemenu_help[] = "Recursively removes a new menu, popup (only path specified) or submenu. ";
+
+/* %start-doc actions RouteStyle
+
+%end-doc */
+
+static int ActionRemoveMenu(int argc, const char **argv, Coord x, Coord y)
+{
+	if (gui == NULL) {
+		Message(PCB_MSG_ERROR, "can't remove menu, there's no GUI hid loaded\n");
+		return 1;
+	}
+
+	if (gui->remove_menu == NULL) {
+		Message(PCB_MSG_ERROR, "can't remove menu, the GUI doesn't support it\n");
+		return 1;
+	}
+
+	if (argc > 0) {
+		if (gui->remove_menu(argv[0]) != 0)
+			Message(PCB_MSG_ERROR, "failed to remove some of the menu items\n");
+			return 0;
+	}
+
+	AFAIL(message);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char setsame_syntax[] = "SetSame()";
+
+static const char setsame_help[] = "Sets current layer and sizes to match indicated item.";
+
+/* %start-doc actions SetSame
+
+When invoked over any line, arc, polygon, or via, this changes the
+current layer to be the layer that item is on, and changes the current
+sizes (thickness, clearance, drill, etc) according to that item.
+
+%end-doc */
+static void set_same_(Coord Thick, Coord Diameter, Coord Hole, Coord Clearance, char *Name)
+{
+	int known;
+	known = pcb_route_style_lookup(&PCB->RouteStyle, Thick, Diameter, Hole, Clearance, Name);
+	if (known < 0) {
+		/* unknown style, set properties */
+		if (Thick != 0)     { pcb_custom_route_style.Thick     = Thick;     conf_set_design("design/line_thickness", "%$mS", Thick); }
+		if (Clearance != 0) { pcb_custom_route_style.Clearance = Clearance; conf_set_design("design/clearance", "%$mS", Clearance); }
+		if (Diameter != 0)  { pcb_custom_route_style.Diameter  = Diameter;  conf_set_design("design/via_thickness", "%$mS", Diameter); }
+		if (Hole != 0)      { pcb_custom_route_style.Hole      = Hole;      conf_set_design("design/via_drilling_hole", "%$mS", Hole); }
+	}
+	else
+		pcb_use_route_style_idx(&PCB->RouteStyle, known);
+}
+
+static int ActionSetSame(int argc, const char **argv, Coord x, Coord y)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	LayerTypePtr layer = CURRENT;
+
+	type = SearchScreen(x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
+/* set layer current and size from line or arc */
+	switch (type) {
+	case PCB_TYPE_LINE:
+		notify_crosshair_change(pcb_false);
+		set_same_(((LineTypePtr) ptr2)->Thickness, 0, 0, ((LineTypePtr) ptr2)->Clearance / 2, NULL);
+		layer = (LayerTypePtr) ptr1;
+		if (conf_core.editor.mode != PCB_MODE_LINE)
+			SetMode(PCB_MODE_LINE);
+		notify_crosshair_change(pcb_true);
+		hid_action("RouteStylesChanged");
+		break;
+
+	case PCB_TYPE_ARC:
+		notify_crosshair_change(pcb_false);
+		set_same_(((ArcTypePtr) ptr2)->Thickness, 0, 0, ((ArcTypePtr) ptr2)->Clearance / 2, NULL);
+		layer = (LayerTypePtr) ptr1;
+		if (conf_core.editor.mode != PCB_MODE_ARC)
+			SetMode(PCB_MODE_ARC);
+		notify_crosshair_change(pcb_true);
+		hid_action("RouteStylesChanged");
+		break;
+
+	case PCB_TYPE_POLYGON:
+		layer = (LayerTypePtr) ptr1;
+		break;
+
+	case PCB_TYPE_VIA:
+		notify_crosshair_change(pcb_false);
+		set_same_(0, ((PinTypePtr) ptr2)->Thickness, ((PinTypePtr) ptr2)->DrillingHole, ((PinTypePtr) ptr2)->Clearance / 2, NULL);
+		if (conf_core.editor.mode != PCB_MODE_VIA)
+			SetMode(PCB_MODE_VIA);
+		notify_crosshair_change(pcb_true);
+		hid_action("RouteStylesChanged");
+		break;
+
+	default:
+		return 1;
+	}
+	if (layer != CURRENT) {
+		ChangeGroupVisibility(GetLayerNumber(PCB->Data, layer), pcb_true, pcb_true);
+		Redraw();
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char switchhid_syntax[] = "SwitchHID(lesstif|gtk|batch)";
+
+static const char switchhid_help[] = "Switch to another HID.";
+
+/* %start-doc actions SwitchHID
+
+Switch to another HID.
+
+%end-doc */
+
+static int ActionSwitchHID(int argc, const char **argv, Coord x, Coord y)
+{
+	HID *ng = hid_find_gui(argv[0]);
+	int chg;
+
+	if (ng == NULL) {
+		Message(PCB_MSG_DEFAULT, "No such HID.");
+		return 1;
+	}
+
+	next_gui = ng;
+	chg = PCB->Changed;
+	QuitApplication();
+	PCB->Changed = chg;
+
+	return 0;
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+/* This action is provided for CLI convience */
+static const char fullscreen_syntax[] = "FullScreen(on|off|toggle)\n";
+
+static const char fullscreen_help[] = "Hide widgets to get edit area full screen";
+
+static int FullScreen(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *op = argv == NULL ? NULL : argv[0];
+
+	if ((op == NULL) || (strcmp(op, "toggle") == 0))
+		conf_setf(CFR_DESIGN, "editor/fullscreen", -1, "%d", !conf_core.editor.fullscreen, POL_OVERWRITE);
+	else if (strcmp(op, "on") == 0)
+		conf_set(CFR_DESIGN, "editor/fullscreen", -1, "1", POL_OVERWRITE);
+	else if (strcmp(op, "off") == 0)
+		conf_set(CFR_DESIGN, "editor/fullscreen", -1, "0", POL_OVERWRITE);
+
+
+	return 0;
+}
+
+
+
+HID_Action gui_action_list[] = {
+	{"Display", 0, ActionDisplay,
+	 display_help, display_syntax}
+	,
+	{"CycleDrag", 0, ActionCycleDrag,
+	 cycledrag_help, cycledrag_syntax}
+	,
+	{"FullScreen", 0, FullScreen, fullscreen_help, fullscreen_syntax}
+	,
+	{"MarkCrosshair", 0, ActionMarkCrosshair,
+	 markcrosshair_help, markcrosshair_syntax}
+	,
+	{"Message", 0, ActionMessage,
+	 message_help, message_syntax}
+	,
+	{"Mode", 0, ActionMode,
+	 mode_help, mode_syntax}
+	,
+	{"ToggleHideName", 0, ActionToggleHideName,
+	 togglehidename_help, togglehidename_syntax}
+	,
+	{"SetSame", N_("Select item to use attributes from"), ActionSetSame,
+	 setsame_help, setsame_syntax}
+	,
+	{"RouteStyle", 0, ActionRouteStyle,
+	 routestyle_help, routestyle_syntax}
+	,
+	{"CreateMenu", 0, ActionCreateMenu,
+	 createmenu_help, createmenu_syntax}
+	,
+	{"RemoveMenu", 0, ActionRemoveMenu,
+	 removemenu_help, removemenu_syntax}
+	,
+	{"SwitchHID", 0, ActionSwitchHID,
+	 switchhid_help, switchhid_syntax}
+};
+
+REGISTER_ACTIONS(gui_action_list, NULL)
diff --git a/src/heap.c b/src/heap.c
new file mode 100644
index 0000000..dc8537e
--- /dev/null
+++ b/src/heap.c
@@ -0,0 +1,236 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, heap.c, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* operations on heaps.
+ */
+
+#include <assert.h>
+#include "config.h"
+#include "heap.h"
+
+/* define this for more thorough self-checking of data structures */
+#undef SLOW_ASSERTIONS
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+struct heap_element {
+	cost_t cost;
+	void *data;
+};
+struct heap_struct {
+	struct heap_element *element;
+	int size, max;
+};
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static cost_t MIN_COST = 0;
+
+/* ---------------------------------------------------------------------------
+ * functions.
+ */
+/* helper functions for assertions */
+#ifndef NDEBUG
+#ifdef SLOW_ASSERTIONS
+static int __heap_is_good_slow(heap_t * heap)
+{
+	int i;
+	/* heap condition: key in each node should be smaller than in its children */
+	/* alternatively (and this is what we check): key in each node should be
+	 * larger than (or equal to) key of its parent. */
+	/* note that array element[0] is not used (except as a sentinel) */
+	for (i = 2; i < heap->size; i++)
+		if (heap->element[i].cost < heap->element[i / 2].cost)
+			return 0;
+	return 1;
+}
+#endif /* SLOW_ASSERTIONS */
+static int __heap_is_good(heap_t * heap)
+{
+	return heap && (heap->max == 0 || heap->element) &&
+		(heap->max >= 0) && (heap->size >= 0) && (heap->max == 0 || heap->size < heap->max) &&
+#ifdef SLOW_ASSERTIONS
+		__heap_is_good_slow(heap) &&
+#endif
+		1;
+}
+#endif /* ! NDEBUG */
+
+/* create an empty heap */
+heap_t *heap_create()
+{
+	heap_t *heap;
+	/* initialize MIN_COST if necessary */
+	if (MIN_COST == 0)
+		MIN_COST = -1e23;
+	assert(MIN_COST < 0);
+	/* okay, create empty heap */
+	heap = (heap_t *) calloc(1, sizeof(*heap));
+	assert(heap);
+	assert(__heap_is_good(heap));
+	return heap;
+}
+
+/* destroy a heap */
+void heap_destroy(heap_t ** heap)
+{
+	assert(heap && *heap);
+	assert(__heap_is_good(*heap));
+	if ((*heap)->element)
+		free((*heap)->element);
+	free(*heap);
+	*heap = NULL;
+}
+
+/* free all elements in the heap */
+void heap_free(heap_t * heap, void (*freefunc) (void *))
+{
+	assert(heap);
+	assert(__heap_is_good(heap));
+	for (; heap->size; heap->size--) {
+		if (heap->element[heap->size].data)
+			freefunc(heap->element[heap->size].data);
+	}
+}
+
+/* -- mutation -- */
+static void __upheap(heap_t * heap, int k)
+{
+	struct heap_element v;
+
+	assert(heap && heap->size < heap->max);
+	assert(k <= heap->size);
+
+	v = heap->element[k];
+	heap->element[0].cost = MIN_COST;
+	for (v = heap->element[k]; heap->element[k / 2].cost > v.cost; k = k / 2)
+		heap->element[k] = heap->element[k / 2];
+	heap->element[k] = v;
+}
+
+void heap_insert(heap_t * heap, cost_t cost, void *data)
+{
+	assert(heap && __heap_is_good(heap));
+	assert(cost >= MIN_COST);
+
+	/* determine whether we need to grow the heap */
+	if (heap->size + 1 >= heap->max) {
+		heap->max *= 2;
+		if (heap->max == 0)
+			heap->max = 256;					/* default initial heap size */
+		heap->element = (struct heap_element *) realloc(heap->element, heap->max * sizeof(*heap->element));
+	}
+	heap->size++;
+	assert(heap->size < heap->max);
+	heap->element[heap->size].cost = cost;
+	heap->element[heap->size].data = data;
+	__upheap(heap, heap->size);		/* fix heap condition violation */
+	assert(__heap_is_good(heap));
+	return;
+}
+
+/* this procedure moves down the heap, exchanging the node at position
+ * k with the smaller of its two children as necessary and stopping when
+ * the node at k is smaller than both children or the bottom is reached.
+ */
+static void __downheap(heap_t * heap, int k)
+{
+	struct heap_element v;
+
+	assert(heap && heap->size < heap->max);
+	assert(k <= heap->size);
+
+	v = heap->element[k];
+	while (k <= heap->size / 2) {
+		int j = k + k;
+		if (j < heap->size && heap->element[j].cost > heap->element[j + 1].cost)
+			j++;
+		if (v.cost < heap->element[j].cost)
+			break;
+		heap->element[k] = heap->element[j];
+		k = j;
+	}
+	heap->element[k] = v;
+}
+
+void *heap_remove_smallest(heap_t * heap)
+{
+	struct heap_element v;
+	assert(heap && __heap_is_good(heap));
+	assert(heap->size > 0);
+	assert(heap->max > 1);
+
+	v = heap->element[1];
+	heap->element[1] = heap->element[heap->size--];
+	if (heap->size > 0)
+		__downheap(heap, 1);
+
+	assert(__heap_is_good(heap));
+	return v.data;
+}
+
+void *heap_replace(heap_t * heap, cost_t cost, void *data)
+{
+	assert(heap && __heap_is_good(heap));
+
+	if (heap_is_empty(heap))
+		return data;
+
+	assert(heap->size > 0);
+	assert(heap->max > 1);
+
+	heap->element[0].cost = cost;
+	heap->element[0].data = data;
+	__downheap(heap, 0);					/* ooh, tricky! */
+
+	assert(__heap_is_good(heap));
+	return heap->element[0].data;
+}
+
+/* -- interrogation -- */
+int heap_is_empty(heap_t * heap)
+{
+	assert(__heap_is_good(heap));
+	return heap->size == 0;
+}
+
+/* -- size -- */
+int heap_size(heap_t * heap)
+{
+	assert(__heap_is_good(heap));
+	return heap->size;
+}
diff --git a/src/heap.h b/src/heap.h
new file mode 100644
index 0000000..a959ded
--- /dev/null
+++ b/src/heap.h
@@ -0,0 +1,62 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, heap.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* prototypes for heap routines.
+ */
+
+#ifndef PCB_HEAP_H
+#define PCB_HEAP_H
+
+#include "global.h"
+
+/* type of heap costs */
+typedef double cost_t;
+/* what a heap looks like */
+typedef struct heap_struct heap_t;
+
+/* create an empty heap */
+heap_t *heap_create();
+/* destroy a heap */
+void heap_destroy(heap_t ** heap);
+/* free all elements in a heap */
+void heap_free(heap_t * heap, void (*funcfree) (void *));
+
+/* -- mutation -- */
+void heap_insert(heap_t * heap, cost_t cost, void *data);
+void *heap_remove_smallest(heap_t * heap);
+/* replace the smallest item with a new item and return the smallest item.
+ * (if the new item is the smallest, than return it, instead.) */
+void *heap_replace(heap_t * heap, cost_t cost, void *data);
+
+/* -- interrogation -- */
+int heap_is_empty(heap_t * heap);
+int heap_size(heap_t * heap);
+
+#endif /* PCB_HEAP_H */
diff --git a/src/hid.h b/src/hid.h
new file mode 100644
index 0000000..55f3582
--- /dev/null
+++ b/src/hid.h
@@ -0,0 +1,578 @@
+#ifndef PCB_HID_H
+#define PCB_HID_H
+
+#include <stdarg.h>
+
+#include "config.h"
+#include "error.h"
+#include "global_typedefs.h"
+
+typedef struct HID_Attr_Val_s  HID_Attr_Val;
+typedef struct HID_Attribute_s HID_Attribute;
+
+/* Human Interface Device */
+
+/*
+
+The way the HID layer works is that you instantiate a HID device
+structure, and invoke functions through its members.  Code in the
+common part of PCB may *not* rely on *anything* other than what's
+defined in this file.  Code in the HID layers *may* rely on data and
+functions in the common code (like, board size and such) but it's
+considered bad form to do so when not needed.
+
+Coordinates are ALWAYS in pcb's default resolution (1/100 mil at the
+moment).  Positive X is right, positive Y is down.  Angles are
+degrees, with 0 being right (positive X) and 90 being up (negative Y).
+All zoom, scaling, panning, and conversions are hidden inside the HID
+layers.
+
+The main structure is at the end of this file.
+
+Data structures passed to the HIDs will be copied if the HID needs to
+save them.  Data structures returned from the HIDs must not be freed,
+and may be changed by the HID in response to new information.
+
+*/
+
+/* Like end cap styles.  The cap *always* extends beyond the
+   coordinates given, by half the width of the line.  Beveled ends can
+   used to make octagonal pads by giving the same x,y coordinate
+   twice.  */
+typedef enum {
+	Trace_Cap,									/* This means we're drawing a trace, which has round caps.  */
+	Square_Cap,									/* Square pins or pads. */
+	Round_Cap,									/* Round pins or round-ended pads, thermals.  */
+	Beveled_Cap									/* Octagon pins or bevel-cornered pads.  */
+} EndCapStyle;
+
+/* The HID may need something more than an "int" for colors, timers,
+   etc.  So it passes/returns one of these, which is castable to a
+   variety of things.  */
+typedef union {
+	long lval;
+	void *ptr;
+} hidval;
+
+/* This graphics context is an opaque pointer defined by the HID.  GCs
+   are HID-specific; attempts to use one HID's GC for a different HID
+   will result in a fatal error.  */
+typedef struct hid_gc_struct *hidGC;
+
+#define HIDCONCAT(a,b) a##b
+
+/* This is used to register the action callbacks (for menus and
+   whatnot).  HID assumes the following actions are available for its
+   use:
+	SaveAs(filename);
+	Quit();
+*/
+typedef struct {
+	/* This is matched against action names in the GUI configuration */
+	const char *name;
+	/* If this string is non-NULL, the action needs to know the X,Y
+	   coordinates to act on, and this string may be used to prompt
+	   the user to select a coordinate.  If NULL, the coordinates may
+	   be 0,0 if none are known.  */
+	const char *need_coord_msg;
+	/* Called when the action is triggered.  If this function returns
+	   non-zero, no further actions will be invoked for this key/mouse
+	   event.  */
+	int (*trigger_cb) (int argc, const char **argv, Coord x, Coord y);
+	/* Short description that sometimes accompanies the name.  */
+	const char *description;
+	/* Full allowed syntax; use \n to separate lines.  */
+	const char *syntax;
+} HID_Action;
+
+extern void hid_register_action(const HID_Action *a, const char *cookie, int copy);
+
+extern void hid_register_actions(const HID_Action *a, int, const char *cookie, int copy);
+#define REGISTER_ACTIONS(a, cookie) HIDCONCAT(void register_,a) ()\
+{ hid_register_actions(a, sizeof(a)/sizeof(a[0]), cookie, 0); }
+
+/* Note that PCB expects the gui to provide the following actions:
+
+   PCBChanged();
+   RouteStylesChanged()
+   NetlistChanged()  (but core should call "void NetlistChanged(int);" in netlist.c)
+   LayersChanged()
+   LibraryChanged()
+   Busy()
+ */
+
+extern const char pcbchanged_help[];
+extern const char pcbchanged_syntax[];
+extern const char routestyleschanged_help[];
+extern const char routestyleschanged_syntax[];
+extern const char netlistchanged_help[];
+extern const char netlistchanged_syntax[];
+extern const char layerschanged_help[];
+extern const char layerschanged_syntax[];
+extern const char librarychanged_help[];
+extern const char librarychanged_syntax[];
+
+/* File Watch flags */
+/* Based upon those in dbus/dbus-connection.h */
+typedef enum {
+	PCB_WATCH_READABLE = 1 << 0,
+														 /**< As in POLLIN */
+	PCB_WATCH_WRITABLE = 1 << 1,
+														 /**< As in POLLOUT */
+	PCB_WATCH_ERROR = 1 << 2,	 /**< As in POLLERR */
+	PCB_WATCH_HANGUP = 1 << 3	 /**< As in POLLHUP */
+} PCBWatchFlags;
+
+/* DRC GUI Hooks */
+typedef struct {
+	int log_drc_overview;
+	int log_drc_violations;
+	void (*reset_drc_dialog_message) (void);
+	void (*append_drc_violation) (DrcViolationType * violation);
+	int (*throw_drc_dialog) (void);
+} HID_DRC_GUI;
+
+typedef struct hid_st HID;
+
+/* This is the main HID structure.  */
+struct hid_st {
+	/* The size of this structure.  We use this as a compatibility
+	   check; a HID built with a different hid.h than we're expecting
+	   should have a different size here.  */
+	int struct_size;
+
+	/* The name of this HID.  This should be suitable for
+	   command line options, multi-selection menus, file names,
+	   etc. */
+	const char *name;
+
+	/* Likewise, but allowed to be longer and more descriptive.  */
+	const char *description;
+
+	/* The hid may use this field to store its context. */
+	void *user_context;
+
+	/* If set, this is the GUI HID.  Exactly one of these three flags
+	   must be set; setting "gui" lets the expose callback optimize and
+	   coordinate itself.  */
+	unsigned gui:1;
+
+	/* If set, this is the printer-class HID.  The common part of PCB
+	   may use this to do command-line printing, without having
+	   instantiated any GUI HIDs.  Only one printer HID is normally
+	   defined at a time.  */
+	unsigned printer:1;
+
+	/* If set, this HID provides an export option, and should be used as
+	   part of the File->Export menu option.  Examples are PNG, Gerber,
+	   and EPS exporters.  */
+	unsigned exporter:1;
+
+	/* If set, the redraw code will draw polygons before erasing the
+	   clearances.  */
+	unsigned poly_before:1;
+
+	/* If set, the redraw code will draw polygons after erasing the
+	   clearances.  Note that HIDs may set both of these, in which case
+	   polygons will be drawn twice.  */
+	unsigned poly_after:1;
+
+	/* If set, draw holes after copper, silk and mask, to make sure it
+	   punches through everything. */
+	unsigned holes_after:1;
+
+	/* Returns a set of resources describing options the export or print
+	   HID supports.  In GUI mode, the print/export dialogs use this to
+	   set up the selectable options.  In command line mode, these are
+	   used to interpret command line options.  If n_ret is non-NULL,
+	   the number of attributes is stored there.  */
+	HID_Attribute *(*get_export_options) (int *n_ret_);
+
+	/* Export (or print) the current PCB.  The options given represent
+	   the choices made from the options returned from
+	   get_export_options.  Call with options == NULL to start the
+	   primary GUI (create a main window, print, export, etc)  */
+	void (*do_export) (HID_Attr_Val * options_);
+
+	/* uninit a GUI hid */
+	void (*uninit) (HID *hid);
+
+	/* uninit a GUI hid */
+	void (*do_exit) (HID *hid);
+
+	/* Parse the command line.  Call this early for whatever HID will be
+	   the primary HID, as it will set all the registered attributes.
+	   The HID should remove all arguments, leaving any possible file
+	   names behind.  */
+	void (*parse_arguments) (int *argc_, char ***argv_);
+
+	/* This may be called to ask the GUI to force a redraw of a given area */
+	void (*invalidate_lr) (int left_, int right_, int top_, int bottom_);
+	void (*invalidate_all) (void);
+	void (*notify_crosshair_change) (pcb_bool changes_complete);
+	void (*notify_mark_change) (pcb_bool changes_complete);
+
+	/* During redraw or print/export cycles, this is called once per
+	   layer (or layer group, for copper layers).  If it returns false
+	   (zero), the HID does not want that layer, and none of the drawing
+	   functions should be called.  If it returns pcb_true (nonzero), the
+	   items in that layer [group] should be drawn using the various
+	   drawing functions.  In addition to the MAX_LAYERS copper layer
+	   groups, you may select layers indicated by the macros SL_*
+	   defined above, or any others with an index of -1.  For copper
+	   layer groups, you may pass NULL for name to have a name fetched
+	   from the PCB struct.  The EMPTY argument is a hint - if set, the
+	   layer is empty, if zero it may be non-empty.  */
+	int (*set_layer) (const char *name_, int group_, int _empty);
+
+	/* Tell the GUI the layer last selected has been finished with */
+	void (*end_layer) (void);
+
+	/* Drawing Functions.  Coordinates and distances are ALWAYS in PCB's
+	   default coordinates (1/100 mil at the time this comment was
+	   written).  Angles are always in degrees, with 0 being "right"
+	   (positive X) and 90 being "up" (positive Y).  */
+
+	/* Make an empty graphics context.  */
+	hidGC (*make_gc) (void);
+	void (*destroy_gc) (hidGC gc_);
+
+	/* Special note about the "erase" color: To use this color, you must
+	   use this function to tell the HID when you're using it.  At the
+	   beginning of a layer redraw cycle (i.e. after set_layer), call
+	   use_mask() to redirect output to a buffer.  Draw to the buffer
+	   (using regular HID calls) using regular and "erase" colors.  Then
+	   call use_mask(HID_MASK_OFF) to flush the buffer to the HID.  If
+	   you use the "erase" color when use_mask is disabled, it simply
+	   draws in the background color.  */
+	void (*use_mask) (int use_it_);
+	/* Flush the buffer and return to non-mask operation.  */
+#define HID_MASK_OFF 0
+	/* Polygons being drawn before clears.  */
+#define HID_MASK_BEFORE 1
+	/* Clearances being drawn.  */
+#define HID_MASK_CLEAR 2
+	/* Polygons being drawn after clears.  */
+#define HID_MASK_AFTER 3
+
+	/* Set a color.  Names can be like "red" or "#rrggbb" or special
+	   names like "erase".  *Always* use the "erase" color for removing
+	   ink (like polygon reliefs or thermals), as you cannot rely on
+	   knowing the background color or special needs of the HID.  Always
+	   use the "drill" color to draw holes.  You may assume this is
+	   cheap enough to call inside the redraw callback, but not cheap
+	   enough to call for each item drawn. */
+	void (*set_color) (hidGC gc_, const char *name_);
+
+	/* Set the line style.  While calling this is cheap, calling it with
+	   different values each time may be expensive, so grouping items by
+	   line style is helpful.  */
+	void (*set_line_cap) (hidGC gc_, EndCapStyle style_);
+	void (*set_line_width) (hidGC gc_, Coord width_);
+	void (*set_draw_xor) (hidGC gc_, int xor_);
+	/* Blends 20% or so color with 80% background.  Only used for
+	   assembly drawings so far. */
+	void (*set_draw_faded) (hidGC gc_, int faded_);
+
+	/* The usual drawing functions.  "draw" means to use segments of the
+	   given width, whereas "fill" means to fill to a zero-width
+	   outline.  */
+	void (*draw_line) (hidGC gc_, Coord x1_, Coord y1_, Coord x2_, Coord y2_);
+	void (*draw_arc) (hidGC gc_, Coord cx_, Coord cy_, Coord xradius_, Coord yradius_, Angle start_angle_, Angle delta_angle_);
+	void (*draw_rect) (hidGC gc_, Coord x1_, Coord y1_, Coord x2_, Coord y2_);
+	void (*fill_circle) (hidGC gc_, Coord cx_, Coord cy_, Coord radius_);
+	void (*fill_polygon) (hidGC gc_, int n_coords_, Coord * x_, Coord * y_);
+	void (*fill_pcb_polygon) (hidGC gc_, PolygonType * poly, const BoxType * clip_box);
+	void (*thindraw_pcb_polygon) (hidGC gc_, PolygonType * poly, const BoxType * clip_box);
+	void (*fill_pcb_pad) (hidGC gc_, PadType * pad, pcb_bool clip, pcb_bool mask);
+	void (*thindraw_pcb_pad) (hidGC gc_, PadType * pad, pcb_bool clip, pcb_bool mask);
+	void (*fill_pcb_pv) (hidGC fg_gc, hidGC bg_gc, PinType * pv, pcb_bool drawHole, pcb_bool mask);
+	void (*thindraw_pcb_pv) (hidGC fg_gc, hidGC bg_gc, PinType * pv, pcb_bool drawHole, pcb_bool mask);
+	void (*fill_rect) (hidGC gc_, Coord x1_, Coord y1_, Coord x2_, Coord y2_);
+
+
+	/* This is for the printer.  If you call this for the GUI, xval and
+	   yval are ignored, and a dialog pops up to lead you through the
+	   calibration procedure.  For the printer, if xval and yval are
+	   zero, a calibration page is printed with instructions for
+	   calibrating your printer.  After calibrating, nonzero xval and
+	   yval are passed according to the instructions.  Metric is nonzero
+	   if the user prefers metric units, else inches are used. */
+	void (*calibrate) (double xval_, double yval_);
+
+
+	/* GUI layout functions.  Not used or defined for print/export
+	   HIDs.  */
+
+	/* Temporary */
+	int (*shift_is_pressed) (void);
+	int (*control_is_pressed) (void);
+	int (*mod1_is_pressed) (void);
+	void (*get_coords) (const char *msg_, Coord * x_, Coord * y_);
+
+	/* Sets the crosshair, which may differ from the pointer depending
+	   on grid and pad snap.  Note that the HID is responsible for
+	   hiding, showing, redrawing, etc.  The core just tells it what
+	   coordinates it's actually using.  Note that this routine may
+	   need to know what "pcb units" are so it can display them in mm
+	   or mils accordingly.  If cursor_action is set, the cursor or
+	   screen may be adjusted so that the cursor and the crosshair are
+	   at the same point on the screen.  */
+	void (*set_crosshair) (int x_, int y_, int cursor_action_);
+#define HID_SC_DO_NOTHING	0
+#define HID_SC_WARP_POINTER	1
+#define HID_SC_PAN_VIEWPORT	2
+
+	/* Causes func to be called at some point in the future.  Timers are
+	   only good for *one* call; if you want it to repeat, add another
+	   timer during the callback for the first.  user_data can be
+	   anything, it's just passed to func.  Times are not guaranteed to
+	   be accurate.  */
+	  hidval(*add_timer) (void (*func) (hidval user_data_), unsigned long milliseconds_, hidval user_data_);
+	/* Use this to stop a timer that hasn't triggered yet.  */
+	void (*stop_timer) (hidval timer_);
+
+	/* Causes func to be called when some condition occurs on the file
+	   descriptor passed. Conditions include data for reading, writing,
+	   hangup, and errors. user_data can be anything, it's just passed
+	   to func. */
+	  hidval(*watch_file) (int fd_, unsigned int condition_,
+												 void (*func_) (hidval watch_, int fd_, unsigned int condition_, hidval user_data_),
+												 hidval user_data);
+	/* Use this to stop a file watch. */
+	void (*unwatch_file) (hidval watch_);
+
+	/* Causes func to be called in the mainloop prior to blocking */
+	  hidval(*add_block_hook) (void (*func_) (hidval data_), hidval data_);
+	/* Use this to stop a mainloop block hook. */
+	void (*stop_block_hook) (hidval block_hook_);
+
+	/* Various dialogs */
+
+	/* Log a message to the log window.  */
+	void (*log) (const char *fmt_, ...);
+	void (*logv) (enum pcb_message_level, const char *fmt_, va_list args_);
+
+	/* A generic yes/no dialog.  Returns zero if the cancel button is
+	   pressed, one for the ok button.  If you specify alternate labels
+	   for ..., they are used instead of the default OK/Cancel ones, and
+	   the return value is the index of the label chosen.  You MUST pass
+	   NULL as the last parameter to this.  */
+	int (*confirm_dialog) (const char *msg_, ...);
+
+	/* A close confirmation dialog for unsaved pages, for example, with
+	   options "Close without saving", "Cancel" and "Save". Returns zero
+	   if the close is cancelled, or one if it should proceed. The HID
+	   is responsible for any "Save" action the user may wish before
+	   confirming the close.
+	 */
+	int (*close_confirm_dialog) ();
+#define HID_CLOSE_CONFIRM_CANCEL 0
+#define HID_CLOSE_CONFIRM_OK     1
+
+	/* Just prints text.  */
+	void (*report_dialog) (const char *title_, const char *msg_);
+
+	/* Prompts the user to enter a string, returns the string.  If
+	   default_string isn't NULL, the form is pre-filled with this
+	   value.  "msg" is like "Enter value:".  */
+	char *(*prompt_for) (const char *msg_, const char *default_string_);
+
+	/* Prompts the user for a filename or directory name.  For GUI
+	   HID's this would mean a file select dialog box.  The 'flags'
+	   argument is the bitwise OR of the following values.  */
+#define HID_FILESELECT_READ  0x01
+
+	/* The function calling hid->fileselect will deal with the case
+	   where the selected file already exists.  If not given, then the
+	   gui will prompt with an "overwrite?" prompt.  Only used when
+	   writing.
+	 */
+#define HID_FILESELECT_MAY_NOT_EXIST 0x02
+
+	/* The call is supposed to return a file template (for gerber
+	   output for example) instead of an actual file.  Only used when
+	   writing.
+	 */
+#define HID_FILESELECT_IS_TEMPLATE 0x04
+
+	/* title may be used as a dialog box title.  Ignored if NULL.
+	 *
+	 * descr is a longer help string.  Ignored if NULL.
+	 *
+	 * default_file is the default file name.  Ignored if NULL.
+	 *
+	 * default_ext is the default file extension, like ".pdf".
+	 * Ignored if NULL.
+	 *
+	 * history_tag may be used by the GUI to keep track of file
+	 * history.  Examples would be "board", "vendor", "renumber",
+	 * etc.  If NULL, no specific history is kept.
+	 *
+	 * flags are the bitwise or of the HID_FILESELECT defines above
+	 */
+
+	char *(*fileselect) (const char *title_, const char *descr_,
+											 const char *default_file_, const char *default_ext_, const char *history_tag_, int flags_);
+
+	/* A generic dialog to ask for a set of attributes.  If n_attrs is
+	   zero, attrs(.name) must be NULL terminated.  Returns non-zero if
+	   an error occurred (usually, this means the user cancelled the
+	   dialog or something). title is the title of the dialog box
+	   descr (if not NULL) can be a longer description of what the
+	   attributes are used for.  The HID may choose to ignore it or it
+	   may use it for a tooltip or text in a dialog box, or a help
+	   string.
+	 */
+	int (*attribute_dialog) (HID_Attribute * attrs_,
+													 int n_attrs_, HID_Attr_Val * results_, const char *title_, const char *descr_);
+
+	/* This causes a second window to display, which only shows the
+	   selected item. The expose callback is called twice; once to size
+	   the extents of the item, and once to draw it.  To pass magic
+	   values, pass the address of a variable created for this
+	   purpose.  */
+	void (*show_item) (void *item_);
+
+	/* Something to alert the user.  */
+	void (*beep) (void);
+
+	/* Used by optimizers and autorouter to show progress to the user.
+	   Pass all zeros to flush display and remove any dialogs.
+	   Returns nonzero if the user wishes to cancel the operation.  */
+	int (*progress) (int so_far_, int total_, const char *message_);
+
+	HID_DRC_GUI *drc_gui;
+
+	void (*edit_attributes) (const char *owner, AttributeListType * attrlist_);
+
+	/* Debug drawing support. These APIs must be implemented (non NULL),
+	 * but they do not have to be functional. request_debug_draw can
+	 * return NULL to indicate debug drawing is not permitted.
+	 *
+	 * Debug drawing is not guaranteed to be re-entrant.
+	 * The caller must not nest requests for debug drawing.
+	 */
+
+	/* Request permission for debug drawing
+	 *
+	 * Returns a HID pointer which should be used rather than the global
+	 * gui-> for making drawing calls. If the return value is NULL, then
+	 * permission has been denied, and the drawing must not continue.
+	 */
+	HID *(*request_debug_draw) (void);
+
+	/* Flush pending drawing to the screen
+	 *
+	 * May be implemented as a NOOP if the GUI has chosen to send the
+	 * debug drawing directly to the screen.
+	 */
+	void (*flush_debug_draw) (void);
+
+	/* When finished, the user must inform the GUI to clean up resources
+	 *
+	 * Any remaining rendering will be flushed to the screen.
+	 */
+	void (*finish_debug_draw) (void);
+
+	/* Notification to the GUI around saving the PCB file.
+	 *
+	 * Called with a false parameter before the save, called again
+	 * with pcb_true after the save.
+	 *
+	 * Allows GUIs which watch for file-changes on disk to ignore
+	 * our deliberate changes.
+	 */
+	void (*notify_save_pcb) (const char *filename, pcb_bool done);
+
+	/* Notification to the GUI that the PCB file has been renamed. */
+	void (*notify_filename_changed) (void);
+
+	/* Create a new menu and/or submenus
+	 * menu_path is a / separated path to the new menu (parents are silently created).
+	 * The last non-NULL item is the new menu item.
+	 * action, mnemonic, accel and tip affect the new menu item.
+	 * Cookie is strdup()'d into the lihata tree and can be used later to search
+	 * and remove menu items that are no longer needed.
+	 * If action is NULL, the menu may get submenus.
+	 */
+	void (*create_menu) (const char *menu_path, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie);
+
+	/* Remove a menu recursively */
+	int (*remove_menu) (const char *menu_path);
+
+	/* Optional: print usage string (if accepts command line arguments)
+	   Subtopic:
+	     NULL    print generic help
+	     string  print summary for the topic in string
+	   Return 0 on success.
+	*/
+	int (*usage)(const char *subtopic);
+
+
+	/*** PROPEDIT (optional) ****/
+	/* Optional: start a propedit session: a series of propedit calls will follow
+	   Return 0 on success; non-zero aborts the session.
+	*/
+	int (*propedit_start)(void *pe, int num_props, const char *(*query)(void *pe, const char *cmd, const char *key, const char *val, int idx));
+
+	/* Optional: end a propedit session: all data has been sent, no more; this call
+	   should present and run the user dialog and should return only when the
+	   propedit section can be closed. */
+	void (*propedit_end)(void *pe);
+
+	/* Optional: register a new property
+	   Returns a prop context passed with each value
+	*/
+	void *(*propedit_add_prop)(void *pe, const char *propname, int is_mutable, int num_vals);
+
+	/* Optional: register a new value for a property */
+	void (*propedit_add_value)(void *pe, const char *propname, void *propctx, const char *value, int repeat_cnt);
+
+	/* Optional: register statistical info for a property */
+	void (*propedit_add_stat)(void *pe, const char *propname, void *propctx, const char *most_common, const char *min, const char *max, const char *avg);
+};
+
+/* This function (in the common code) will be called whenever the GUI
+   needs to redraw the screen, print the board, or export a layer.  If
+   item is not NULL, only draw the given item.  Item is only non-NULL
+   if the HID was created via show_item.
+
+   Each time func is called, it should do the following:
+
+   * allocate any colors needed, via get_color.
+
+   * cycle through the layers, calling set_layer for each layer to be
+     drawn, and only drawing elements (all or specified) of desired
+     layers.
+
+   Do *not* assume that the hid that is passed is the GUI hid.  This
+   callback is also used for printing and exporting. */
+struct BoxType;
+void hid_expose_callback(HID * hid_, struct BoxType *region_, void *item_);
+
+/* This is initially set to a "no-gui" gui, and later reset by
+   main. hid_expose_callback also temporarily set it for drawing. */
+extern HID *gui;
+
+/* When not NULL, auto-start the next gui after the current one quits */
+extern HID *next_gui;
+
+/* This is either NULL or points to the current HID that is being called to
+   do the exporting. The gui HIDs set and unset this var.*/
+extern HID *exporter;
+
+/* This is either NULL or points to the current HID_Action that is being
+   called. The action launcher sets and unsets this variable. */
+extern const HID_Action *current_action;
+
+/* The GUI may set this to be approximately the PCB size of a pixel,
+   to allow for near-misses in selection and changes in drawing items
+   smaller than a screen pixel.  */
+extern int pixel_slop;
+
+/* Init and uninit the whole action framework */
+void hid_actions_init(void);
+void hid_actions_uninit(void);
+
+#endif
diff --git a/src/hid_actions.c b/src/hid_actions.c
new file mode 100644
index 0000000..58687be
--- /dev/null
+++ b/src/hid_actions.c
@@ -0,0 +1,517 @@
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <genht/hash.h>
+#include <genht/htsp.h>
+
+#include "error.h"
+#include "event.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+
+/* do not throw "unknown action" warning for these: they are known
+   actions, the GUI HID may register them, but nothing bad happens if
+   they are not registered or not handled by the GUI. */
+static const char *action_no_warn[] = {
+	"LayersChanged", "PointCursor", "LibraryChanged", "RouteStylesChanged",
+	NULL
+};
+static int action_legal_unknown(const char *name)
+{
+	const char **s;
+	for(s = action_no_warn; *s != NULL; s++)
+		if (strcmp(*s, name) == 0)
+			return 1;
+	return 0;
+}
+
+static htsp_t *all_actions = NULL;
+const HID_Action *current_action = NULL;
+
+static const char *check_action_name(const char *s)
+{
+	while (*s)
+		if (isspace((int) *s++) || *s == '(')
+			return (s - 1);
+	return NULL;
+}
+
+typedef struct {
+	const char *cookie;
+	const HID_Action *action;
+} hid_cookie_action_t;
+
+void hid_register_actions(const HID_Action * a, int n, const char *cookie, int copy)
+{
+	int i;
+	hid_cookie_action_t *ca;
+
+	if (all_actions == NULL)
+		all_actions = htsp_alloc(strhash_case, strkeyeq_case);
+
+	for (i = 0; i < n; i++) {
+		if (check_action_name(a[i].name)) {
+			Message(PCB_MSG_DEFAULT, _("ERROR! Invalid action name, " "action \"%s\" not registered.\n"), a[i].name);
+			continue;
+		}
+		if (htsp_get(all_actions, a[i].name) != NULL) {
+			Message(PCB_MSG_DEFAULT, _("ERROR! Invalid action name, " "action \"%s\" is already registered.\n"), a[i].name);
+			continue;
+		}
+		ca = malloc(sizeof(hid_cookie_action_t));
+		ca->cookie = cookie;
+		ca->action = a+i;
+		htsp_set(all_actions, pcb_strdup(a[i].name), ca);
+	}
+}
+
+void hid_register_action(const HID_Action * a, const char *cookie, int copy)
+{
+	hid_register_actions(a, 1, cookie, copy);
+}
+
+void hid_remove_actions(const HID_Action * a, int n)
+{
+	int i;
+
+	if (all_actions == NULL)
+		return;
+
+	for (i = 0; i < n; i++) {
+		htsp_entry_t *e;
+		e = htsp_popentry(all_actions, a[i].name);
+		free(e->key);
+		free(e->value);
+	}
+}
+
+void hid_remove_actions_by_cookie(const char *cookie)
+{
+	htsp_entry_t *e;
+
+	if (all_actions == NULL)
+		return;
+
+	/* Slow linear search - probably OK, this will run only on uninit */
+	for (e = htsp_first(all_actions); e; e = htsp_next(all_actions, e)) {
+		hid_cookie_action_t *ca = e->value;
+		if (ca->cookie == cookie) {
+			htsp_pop(all_actions, e->key);
+			free(e->key);
+			free(e->value);
+		}
+	}
+}
+
+void hid_remove_action(const HID_Action * a)
+{
+	htsp_entry_t *e;
+
+	if (all_actions == NULL)
+		return;
+
+	e = htsp_popentry(all_actions, a->name);
+	if (e != NULL) {
+		free(e->key);
+		free(e->value);
+	}
+}
+
+const HID_Action *hid_find_action(const char *name)
+{
+	hid_cookie_action_t *ca;
+
+	if ((name == NULL) && (all_actions == NULL))
+		return 0;
+
+	ca = htsp_get(all_actions, (char *) name);
+
+	if (ca)
+		return ca->action;
+
+	if (!action_legal_unknown(name))
+		Message(PCB_MSG_DEFAULT, "unknown action `%s'\n", name);
+	return 0;
+}
+
+void print_actions()
+{
+	htsp_entry_t *e;
+
+	fprintf(stderr, "Registered Actions:\n");
+	for (e = htsp_first(all_actions); e; e = htsp_next(all_actions, e)) {
+		hid_cookie_action_t *ca = e->value;
+		if (ca->action->description)
+			fprintf(stderr, "  %s - %s\n", ca->action->name, ca->action->description);
+		else
+			fprintf(stderr, "  %s\n", ca->action->name);
+		if (ca->action->syntax) {
+			const char *bb, *eb;
+			bb = eb = ca->action->syntax;
+			while (1) {
+				for (eb = bb; *eb && *eb != '\n'; eb++);
+				fwrite("    ", 4, 1, stderr);
+				fwrite(bb, eb - bb, 1, stderr);
+				fputc('\n', stderr);
+				if (*eb == 0)
+					break;
+				bb = eb + 1;
+			}
+		}
+	}
+}
+
+static void dump_string(char prefix, const char *str)
+{
+	int eol = 1;
+	while (*str) {
+		if (eol) {
+			putchar(prefix);
+			eol = 0;
+		}
+		putchar(*str);
+		if (*str == '\n')
+			eol = 1;
+		str++;
+	}
+	if (!eol)
+		putchar('\n');
+}
+
+void dump_actions(void)
+{
+	htsp_entry_t *e;
+
+	fprintf(stderr, "Registered Actions:\n");
+	for (e = htsp_first(all_actions); e; e = htsp_next(all_actions, e)) {
+		hid_cookie_action_t *ca = e->value;
+		const char *desc = ca->action->description;
+		const char *synt = ca->action->syntax;
+
+		desc = desc ? desc : "";
+		synt = synt ? synt : "";
+
+		printf("A%s\n", ca->action->name);
+		dump_string('D', desc);
+		dump_string('S', synt);
+	}
+}
+
+int hid_action(const char *name)
+{
+	return hid_actionv(name, 0, 0);
+}
+
+int hid_actionl(const char *name, ...)
+{
+	const char *argv[20];
+	int argc = 0;
+	va_list ap;
+	char *arg;
+
+	va_start(ap, name);
+	while ((arg = va_arg(ap, char *)) != 0)
+		  argv[argc++] = arg;
+	va_end(ap);
+	return hid_actionv(name, argc, argv);
+}
+
+int hid_actionv_(const HID_Action *a, int argc, const char **argv)
+{
+	Coord x = 0, y = 0;
+	int i, ret;
+	const HID_Action *old_action;
+
+	if (a->need_coord_msg)
+		gui->get_coords(_(a->need_coord_msg), &x, &y);
+
+	if (conf_core.rc.verbose) {
+		printf("Action: \033[34m%s(", a->name);
+		for (i = 0; i < argc; i++)
+			printf("%s%s", i ? "," : "", argv[i]);
+		printf(")\033[0m\n");
+	}
+
+	old_action = current_action;
+	current_action = a;
+	ret = current_action->trigger_cb(argc, argv, x, y);
+	current_action = old_action;
+
+	return ret;
+}
+
+int hid_actionv(const char *name, int argc, const char **argv)
+{
+	const HID_Action *a;
+
+	if (!name)
+		return 1;
+
+	a = hid_find_action(name);
+	if (!a) {
+		int i;
+		if (action_legal_unknown(name))
+			return 1;
+		Message(PCB_MSG_DEFAULT, "no action %s(", name);
+		for (i = 0; i < argc; i++)
+			Message(PCB_MSG_DEFAULT, "%s%s", i ? ", " : "", argv[i]);
+		Message(PCB_MSG_DEFAULT, ")\n");
+		return 1;
+	}
+	return hid_actionv_(a, argc, argv);
+}
+
+static int hid_parse_actionstring(const char *rstr, char require_parens)
+{
+	const char **list = NULL;
+	int max = 0;
+	int num;
+	char *str = NULL;
+	const char *sp;
+	char *cp, *aname, *cp2;
+	int maybe_empty = 0;
+	char in_quotes = 0;
+	char parens = 0;
+	int retcode = 0;
+
+	/*fprintf(stderr, "invoke: `%s'\n", rstr); */
+
+	sp = rstr;
+	str = (char *) malloc(strlen(rstr) + 1);
+
+another:
+	num = 0;
+	cp = str;
+
+	/* eat leading spaces and tabs */
+	while (*sp && isspace((int) *sp))
+		sp++;
+
+	if (!*sp) {
+		retcode = 0;
+		goto cleanup;
+	}
+
+	aname = cp;
+
+	/* copy the action name, assumes name does not have a space or '('
+	 * in its name */
+	while (*sp && !isspace((int) *sp) && *sp != '(')
+		*cp++ = *sp++;
+	*cp++ = 0;
+
+	/* skip whitespace */
+	while (*sp && isspace((int) *sp))
+		sp++;
+
+	/*
+	 * we only have an action name, so invoke the action
+	 * with no parameters or event.
+	 */
+	if (!*sp) {
+		retcode = hid_actionv(aname, 0, 0);
+		goto cleanup;
+	}
+
+	/* are we using parenthesis? */
+	if (*sp == '(') {
+		parens = 1;
+		sp++;
+	}
+	else if (require_parens) {
+		Message(PCB_MSG_DEFAULT, _("Syntax error: %s\n"), rstr);
+		Message(PCB_MSG_DEFAULT, _("    expected: Action(arg1, arg2)"));
+		retcode = 1;
+		goto cleanup;
+	}
+
+	/* get the parameters to pass to the action */
+	while (1) {
+		/*
+		 * maybe_empty == 0 means that the last char examined was not a
+		 * ","
+		 */
+		if (!maybe_empty && ((parens && *sp == ')') || (!parens && !*sp))) {
+			retcode = hid_actionv(aname, num, list);
+			if (retcode)
+				goto cleanup;
+
+			/* strip any white space or ';' following the action */
+			if (parens)
+				sp++;
+			while (*sp && (isspace((int) *sp) || *sp == ';'))
+				sp++;
+			goto another;
+		}
+		else if (*sp == 0 && !maybe_empty)
+			break;
+		else {
+			maybe_empty = 0;
+			in_quotes = 0;
+			/*
+			 * if we have more parameters than memory in our array of
+			 * pointers, then either allocate some or grow the array
+			 */
+			if (num >= max) {
+				max += 10;
+				if (list)
+					list = (const char **) realloc(list, max * sizeof(char *));
+				else
+					list = (const char **) malloc(max * sizeof(char *));
+			}
+			/* Strip leading whitespace.  */
+			while (*sp && isspace((int) *sp))
+				sp++;
+			list[num++] = cp;
+
+			/* search for the end of the argument, we want to keep going
+			 * if we are in quotes or the char is not a delimiter
+			 */
+			while (*sp && (in_quotes || ((*sp != ',')
+																	 && (!parens || *sp != ')')
+																	 && (parens || !isspace((int) *sp))))) {
+				/*
+				 * single quotes give literal value inside, including '\'.
+				 * you can't have a single inside single quotes.
+				 * doubles quotes gives literal value inside, but allows escape.
+				 */
+				if ((*sp == '"' || *sp == '\'') && (!in_quotes || *sp == in_quotes)) {
+					in_quotes = in_quotes ? 0 : *sp;
+					sp++;
+					continue;
+				}
+				/* unless within single quotes, \<char> will just be <char> */
+				else if (*sp == '\\' && in_quotes != '\'')
+					sp++;
+				*cp++ = *sp++;
+			}
+			cp2 = cp - 1;
+			*cp++ = 0;
+			if (*sp == ',' || (!parens && isspace((int) *sp))) {
+				maybe_empty = 1;
+				sp++;
+			}
+			/* Strip trailing whitespace.  */
+			for (; isspace((int) *cp2) && cp2 >= list[num - 1]; cp2--)
+				*cp2 = 0;
+		}
+	}
+
+cleanup:
+
+	if (list != NULL)
+		free(list);
+
+	if (str != NULL)
+		free(str);
+
+	return retcode;
+}
+
+int hid_parse_command(const char *str_)
+{
+	event(EVENT_CLI_ENTER, "s", str_);
+	return hid_parse_actionstring(str_, FALSE);
+}
+
+int hid_parse_actions(const char *str_)
+{
+	return hid_parse_actionstring(str_, TRUE);
+}
+
+void hid_actions_init(void)
+{
+
+}
+
+void hid_actions_uninit(void)
+{
+	htsp_entry_t *e;
+
+	if (all_actions == NULL)
+		return;
+
+	for (e = htsp_first(all_actions); e; e = htsp_next(all_actions, e)) {
+		hid_cookie_action_t *ca = e->value;
+		if (ca->cookie != NULL)
+			fprintf(stderr, "WARNING: hid_actions_uninit: action '%s' with cookie '%s' left registered, check your plugins!\n", e->key, ca->cookie);
+		free(e->key);
+		free(e->value);
+	}
+
+	htsp_free(all_actions);
+	all_actions = NULL;
+}
+
+/* trick for the doc extractor */
+#define static
+
+/* %start-doc actions 00macros
+
+ at macro hidaction
+
+This is one of a number of actions which are part of the HID
+interface.  The core functions use these actions to tell the current
+GUI when to change the presented information in response to changes
+that the GUI may not know about.  The user normally does not invoke
+these directly.
+
+ at end macro
+
+%end-doc */
+
+
+static const char pcbchanged_syntax[] = "PCBChanged([revert])";
+static const char pcbchanged_help[] =
+	"Tells the GUI that the whole PCB has changed. The optional \"revert\""
+	"parameter can be used as a hint to the GUI that the same design is being"
+	"reloaded, and that it might keep some viewport settings";
+
+/* %start-doc actions PCBChanged
+
+ at hidaction
+
+%end-doc */
+
+static const char routestyleschanged_syntax[] = "RouteStylesChanged()";
+static const char routestyleschanged_help[] = "Tells the GUI that the routing styles have changed.";
+
+/* %start-doc actions RouteStylesChanged
+
+ at hidaction
+
+%end-doc */
+
+static const char netlistchanged_syntax[] = "NetlistChanged()";
+static const char netlistchanged_help[] = "Tells the GUI that the netlist has changed.";
+
+/* %start-doc actions NetlistChanged
+
+ at hidaction
+
+%end-doc */
+
+static const char layerschanged_syntax[] = "LayersChanged()";
+static const char layerschanged_help[] = "Tells the GUI that the layers have changed.";
+
+/* %start-doc actions LayersChanged
+
+This includes layer names, colors, stacking order, visibility, etc.
+
+ at hidaction
+
+%end-doc */
+
+static const char librarychanged_syntax[] = "LibraryChanged()";
+static const char librarychanged_help[] = "Tells the GUI that the libraries have changed.";
+
+/* %start-doc actions LibraryChanged
+
+ at hidaction
+
+%end-doc */
diff --git a/src/hid_actions.h b/src/hid_actions.h
new file mode 100644
index 0000000..8038211
--- /dev/null
+++ b/src/hid_actions.h
@@ -0,0 +1,35 @@
+#ifndef PCB_HID_ACTIONS_H
+#define PCB_HID_ACTIONS_H
+
+/* These are called from main_act.c */
+void print_actions(void);
+void dump_actions(void);
+
+
+/* HID internal interfaces.  These may ONLY be called from the HID
+   modules, not from the common PCB code.  */
+
+const HID_Action *hid_find_action(const char *name);
+
+extern void hid_remove_actions(const HID_Action * a, int n);
+extern void hid_remove_action(const HID_Action * a);
+extern void hid_remove_actions_by_cookie(const char *cookie);
+
+int hid_action(const char *action_);
+int hid_actionl(const char *action_, ...);	/* NULL terminated */
+int hid_actionv(const char *action_, int argc_, const char **argv_);
+int hid_actionv_(const HID_Action *a, int argc, const char **argv);
+
+/* Parse the given command string into action calls, and call
+   hid_actionv for each action found.  Accepts both "action(arg1,
+   arg2)" and command-style "action arg1 arg2", allowing only one
+   action in the later case.  Returns nonzero if the action handler(s)
+   return nonzero. */
+int hid_parse_command(const char *str_);
+
+/* Parse the given string into action calls, and call
+   hid_actionv for each action found.  Accepts only
+   "action(arg1, arg2)" */
+int hid_parse_actions(const char *str_);
+
+#endif
diff --git a/src/hid_attrib.c b/src/hid_attrib.c
new file mode 100644
index 0000000..df0c58e
--- /dev/null
+++ b/src/hid_attrib.c
@@ -0,0 +1,226 @@
+#include "config.h"
+#include "global.h"
+#include "hid_attrib.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "pcb-printf.h"
+#include "compat_fs.h"
+#include "error.h"
+
+HID_AttrNode *hid_attr_nodes = 0;
+
+void hid_register_attributes(HID_Attribute * a, int n, const char *cookie, int copy)
+{
+	HID_AttrNode *ha;
+
+	/* printf("%d attributes registered\n", n); */
+	ha = (HID_AttrNode *) malloc(sizeof(HID_AttrNode));
+	ha->next = hid_attr_nodes;
+	hid_attr_nodes = ha;
+	ha->attributes = a;
+	ha->n = n;
+	ha->cookie = cookie;
+}
+
+void hid_attributes_uninit(void)
+{
+	HID_AttrNode *ha, *next;
+	for (ha = hid_attr_nodes; ha; ha = next) {
+		next = ha->next;
+		if (ha->cookie != NULL)
+			fprintf(stderr, "WARNING: attribute %s by %s is not uninited, check your plugins' uninit!\n", ha->attributes->name, ha->cookie);
+		free(ha);
+	}
+	hid_attr_nodes = NULL;
+}
+
+void hid_remove_attributes_by_cookie(const char *cookie)
+{
+	HID_AttrNode *ha, *next, *prev = NULL;
+	for (ha = hid_attr_nodes; ha; ha = next) {
+		next = ha->next;
+		if (ha->cookie == cookie) {
+			if (prev == NULL)
+				hid_attr_nodes = next;
+			else
+				prev->next = next;
+			free(ha);
+		}
+		else
+			prev = ha;
+	}
+}
+
+void hid_parse_command_line(int *argc, char ***argv)
+{
+	HID_AttrNode *ha;
+	int i, e, ok;
+
+	for (ha = hid_attr_nodes; ha; ha = ha->next)
+		for (i = 0; i < ha->n; i++) {
+			HID_Attribute *a = ha->attributes + i;
+			switch (a->type) {
+			case HID_Label:
+				break;
+			case HID_Integer:
+				if (a->value)
+					*(int *) a->value = a->default_val.int_value;
+				break;
+			case HID_Coord:
+				if (a->value)
+					*(Coord *) a->value = a->default_val.coord_value;
+				break;
+			case HID_Boolean:
+				if (a->value)
+					*(char *) a->value = a->default_val.int_value;
+				break;
+			case HID_Real:
+				if (a->value)
+					*(double *) a->value = a->default_val.real_value;
+				break;
+			case HID_String:
+				if (a->value)
+					*(const char **) a->value = a->default_val.str_value;
+				break;
+			case HID_Enum:
+				if (a->value)
+					*(int *) a->value = a->default_val.int_value;
+				break;
+			case HID_Mixed:
+				if (a->value) {
+					*(HID_Attr_Val *) a->value = a->default_val;
+			case HID_Unit:
+					if (a->value)
+						*(int *) a->value = a->default_val.int_value;
+					break;
+				}
+				break;
+			default:
+				abort();
+			}
+		}
+
+	while (*argc && (*argv)[0][0] == '-' && (*argv)[0][1] == '-') {
+		int bool_val;
+		int arg_ofs;
+
+		bool_val = 1;
+		arg_ofs = 2;
+	try_no_arg:
+		for (ha = hid_attr_nodes; ha; ha = ha->next)
+			for (i = 0; i < ha->n; i++)
+				if (strcmp((*argv)[0] + arg_ofs, ha->attributes[i].name) == 0) {
+					HID_Attribute *a = ha->attributes + i;
+					char *ep;
+					const Unit *unit;
+					switch (ha->attributes[i].type) {
+					case HID_Label:
+						break;
+					case HID_Integer:
+						if (a->value)
+							*(int *) a->value = strtol((*argv)[1], 0, 0);
+						else
+							a->default_val.int_value = strtol((*argv)[1], 0, 0);
+						(*argc)--;
+						(*argv)++;
+						break;
+					case HID_Coord:
+						if (a->value)
+							*(Coord *) a->value = GetValue((*argv)[1], NULL, NULL, NULL);
+						else
+							a->default_val.coord_value = GetValue((*argv)[1], NULL, NULL, NULL);
+						(*argc)--;
+						(*argv)++;
+						break;
+					case HID_Real:
+						if (a->value)
+							*(double *) a->value = strtod((*argv)[1], 0);
+						else
+							a->default_val.real_value = strtod((*argv)[1], 0);
+						(*argc)--;
+						(*argv)++;
+						break;
+					case HID_String:
+						if (a->value)
+							*(char **) a->value = (*argv)[1];
+						else
+							a->default_val.str_value = (*argv)[1];
+						(*argc)--;
+						(*argv)++;
+						break;
+					case HID_Boolean:
+						if (a->value)
+							*(char *) a->value = bool_val;
+						else
+							a->default_val.int_value = bool_val;
+						break;
+					case HID_Mixed:
+						a->default_val.real_value = strtod((*argv)[1], &ep);
+						goto do_enum;
+					case HID_Enum:
+						ep = (*argv)[1];
+					do_enum:
+						ok = 0;
+						for (e = 0; a->enumerations[e]; e++)
+							if (strcmp(a->enumerations[e], ep) == 0) {
+								ok = 1;
+								a->default_val.int_value = e;
+								a->default_val.str_value = ep;
+								break;
+							}
+						if (!ok) {
+							fprintf(stderr, "ERROR:  \"%s\" is an unknown value for the --%s option\n", (*argv)[1], a->name);
+							exit(1);
+						}
+						(*argc)--;
+						(*argv)++;
+						break;
+					case HID_Path:
+						abort();
+						a->default_val.str_value = (*argv)[1];
+						(*argc)--;
+						(*argv)++;
+						break;
+					case HID_Unit:
+						unit = get_unit_struct((*argv)[1]);
+						if (unit == NULL) {
+							fprintf(stderr, "ERROR:  unit \"%s\" is unknown to pcb (option --%s)\n", (*argv)[1], a->name);
+							exit(1);
+						}
+						a->default_val.int_value = unit->index;
+						a->default_val.str_value = unit->suffix;
+						(*argc)--;
+						(*argv)++;
+						break;
+					}
+					(*argc)--;
+					(*argv)++;
+					ha = 0;
+					goto got_match;
+				}
+		if (bool_val == 1 && strncmp((*argv)[0], "--no-", 5) == 0) {
+			bool_val = 0;
+			arg_ofs = 5;
+			goto try_no_arg;
+		}
+		fprintf(stderr, "unrecognized option: %s\n", (*argv)[0]);
+		exit(1);
+	got_match:;
+	}
+}
+
+void hid_usage_option(const char *name, const char *help)
+{
+	fprintf(stderr, "%-20s %s\n", name, help);
+}
+
+void hid_usage(HID_Attribute * a, int numa)
+{
+	for (; numa > 0; numa--,a++) {
+		const char *help;
+		if (a->help_text == NULL) help = "";
+		else if (a->help_text == ATTR_UNDOCUMENTED) help = "<undocumented>";
+		else help = a->help_text;
+		hid_usage_option(a->name, help);
+	}
+}
diff --git a/src/hid_attrib.h b/src/hid_attrib.h
new file mode 100644
index 0000000..eefc62c
--- /dev/null
+++ b/src/hid_attrib.h
@@ -0,0 +1,63 @@
+#ifndef PCB_HID_ATTRIB_H
+#define PCB_HID_ATTRIB_H
+
+#include "hid.h"
+
+/* Used for HID attributes (exporting and printing, mostly).
+   HA_boolean uses int_value, HA_enum sets int_value to the index and
+   str_value to the enumeration string.  HID_Label just shows the
+   default str_value.  HID_Mixed is a real_value followed by an enum,
+   like 0.5in or 100mm. */
+struct HID_Attr_Val_s {
+	int int_value;
+	const char *str_value;
+	double real_value;
+	Coord coord_value;
+};
+
+enum hids { HID_Label, HID_Integer, HID_Real, HID_String,
+	HID_Boolean, HID_Enum, HID_Mixed, HID_Path,
+	HID_Unit, HID_Coord
+};
+
+struct HID_Attribute_s {
+	const char *name;
+	/* If the help_text is this, usage() won't show this option */
+#define ATTR_UNDOCUMENTED ((char *)(1))
+	const char *help_text;
+	enum hids type;
+	int min_val, max_val;				/* for integer and real */
+	HID_Attr_Val default_val;		/* Also actual value for global attributes.  */
+	const char **enumerations;
+	/* If set, this is used for global attributes (i.e. those set
+	   statically with REGISTER_ATTRIBUTES below) instead of changing
+	   the default_val.  Note that a HID_Mixed attribute must specify a
+	   pointer to HID_Attr_Val here, and HID_Boolean assumes this is
+	   "char *" so the value should be initialized to zero, and may be
+	   set to non-zero (not always one).  */
+	void *value;
+	int hash;										/* for detecting changes. */
+};
+
+extern void hid_register_attributes(HID_Attribute *, int, const char *cookie, int copy);
+#define REGISTER_ATTRIBUTES(a, cookie) HIDCONCAT(void register_,a) ()\
+{ hid_register_attributes(a, sizeof(a)/sizeof(a[0]), cookie, 0); }
+
+/* Remove all attributes registered with the given cookie */
+void hid_remove_attributes_by_cookie(const char *cookie);
+
+/* remove all attributes and free the list */
+void hid_attributes_uninit(void);
+
+typedef struct HID_AttrNode {
+	struct HID_AttrNode *next;
+	HID_Attribute *attributes;
+	int n;
+	const char *cookie;
+} HID_AttrNode;
+
+extern HID_AttrNode *hid_attr_nodes;
+
+void hid_usage(HID_Attribute * a, int numa);
+void hid_usage_option(const char *name, const char *help);
+#endif
diff --git a/src/hid_cfg.c b/src/hid_cfg.c
new file mode 100644
index 0000000..fb3c603
--- /dev/null
+++ b/src/hid_cfg.c
@@ -0,0 +1,516 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <liblihata/lihata.h>
+#include <liblihata/tree.h>
+
+#include "global.h"
+#include "hid_cfg.h"
+#include "error.h"
+#include "paths.h"
+#include "compat_misc.h"
+
+char hid_cfg_error_shared[1024];
+
+typedef struct {
+	hid_cfg_t *hr;
+	create_menu_widget_t cb;
+	void *cb_ctx;
+	lht_node_t *parent;
+	const char *action;
+	const char *mnemonic;
+	const char *accel;
+	const char *tip;
+	const char *cookie;
+	int target_level;
+	int err;
+} create_menu_ctx_t;
+
+static lht_node_t *create_menu_cb(void *ctx, lht_node_t *node, const char *path, int rel_level)
+{
+	create_menu_ctx_t *cmc = ctx;
+	lht_node_t *psub;
+
+/*	printf(" CB: '%s' %p at %d->%d\n", path, node, rel_level, cmc->target_level);*/
+	if (node == NULL) { /* level does not exist, create it */
+		const char *name;
+		name = strrchr(path, '/');
+		if (name != NULL)
+			name++;
+		else
+			name = path;
+
+		if (rel_level <= 1) {
+			/* creating a main menu */
+			char *end, *name = pcb_strdup(path);
+			for(end = name; *end == '/'; end++) ;
+			end = strchr(end, '/');
+			*end = '\0';
+			psub = cmc->parent = hid_cfg_get_menu(cmc->hr, name);
+			free(name);
+		}
+		else
+			psub = hid_cfg_menu_field(cmc->parent, MF_SUBMENU, NULL);
+
+		if (rel_level == cmc->target_level) {
+			node = hid_cfg_create_hash_node(psub, name, "dyn", "1", "m", "cookie", cmc->cookie, cmc->mnemonic, "a", cmc->accel, "tip", cmc->tip, ((cmc->action != NULL) ? "action": NULL), cmc->action, NULL);
+			if (node != NULL)
+				cmc->err = 0;
+		}
+		else
+			node = hid_cfg_create_hash_node(psub, name, "dyn", "1", "cookie", cmc->cookie,  NULL);
+
+		if (node == NULL)
+			return NULL;
+
+		if ((rel_level != cmc->target_level) || (cmc->action == NULL))
+			lht_dom_hash_put(node, lht_dom_node_alloc(LHT_LIST, "submenu"));
+
+		if (node->parent == NULL)
+			lht_dom_list_append(psub, node);
+		else
+			assert(node->parent == psub);
+
+		if (cmc->cb(cmc->cb_ctx, path, name, (rel_level == 1), cmc->parent, node) != 0) {
+			cmc->err = -1;
+			return NULL;
+		}
+	}
+	cmc->parent = node;
+	return node;
+}
+
+int hid_cfg_create_menu(hid_cfg_t *hr, const char *path, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie, create_menu_widget_t cb, void *cb_ctx)
+{
+	const char *name;
+	create_menu_ctx_t cmc;
+
+	cmc.hr = hr;
+	cmc.cb = cb;
+	cmc.cb_ctx = cb_ctx;
+	cmc.parent = NULL;
+	cmc.action = action;
+	cmc.mnemonic = mnemonic;
+	cmc.accel = accel;
+	cmc.tip = tip;
+	cmc.cookie = cookie;
+	cmc.err = -1;
+
+	/* Allow creating new nodes only under certain main paths that correspond to menus */
+	name = path;
+	while(*name == '/') name++;
+
+	if ((strncmp(name, "main_menu/", 10) == 0) || (strncmp(name, "popups/", 7) == 0)) {
+		/* calculate target level */
+		for(cmc.target_level = 0; *name != '\0'; name++) {
+			if (*name == '/') {
+				cmc.target_level++;
+				while(*name == '/') name++;
+				name--;
+			}
+		}
+
+		/* descend and visit each level, create missing levels */
+
+		hid_cfg_get_menu_at(hr, NULL, path, create_menu_cb, &cmc);
+	}
+
+	return cmc.err;
+}
+
+static int hid_cfg_remove_item(hid_cfg_t *hr, lht_node_t *item, int (*gui_remove)(void *ctx, lht_node_t *nd), void *ctx)
+{
+	if (gui_remove(ctx, item) != 0)
+		return -1;
+	lht_tree_del(item);
+	return 0;
+}
+
+
+static int hid_cfg_remove_menu_(hid_cfg_t *hr, lht_node_t *root, int (*gui_remove)(void *ctx, lht_node_t *nd), void *ctx)
+{
+	if (root == NULL)
+		return -1;
+
+	if (root->type == LHT_HASH) {
+		lht_node_t *psub, *n, *next;
+		psub = hid_cfg_menu_field(root, MF_SUBMENU, NULL);
+		if (psub != NULL) { /* remove a whole submenu with all children */
+			int res = 0;
+			for(n = psub->data.list.first; n != NULL; n = next) {
+				next = n->next;
+				if (hid_cfg_remove_menu_(hr, n, gui_remove, ctx) != 0)
+					res = -1;
+			}
+			if (res == 0)
+				res = hid_cfg_remove_item(hr, root, gui_remove, ctx);
+			return res;
+		}
+	}
+
+	if ((root->type != LHT_TEXT) && (root->type != LHT_HASH)) /* allow text for the sep */
+		return -1;
+
+	/* remove a simple menu item */
+	return hid_cfg_remove_item(hr, root, gui_remove, ctx);
+}
+
+int hid_cfg_remove_menu(hid_cfg_t *hr, const char *path, int (*gui_remove)(void *ctx, lht_node_t *nd), void *ctx)
+{
+	return hid_cfg_remove_menu_(hr, hid_cfg_get_menu_at(hr, NULL, path, NULL, NULL), gui_remove, ctx);
+}
+
+
+static int hid_cfg_load_error(lht_doc_t *doc, const char *filename, lht_err_t err)
+{
+	const char *fn;
+	int line, col;
+	lht_dom_loc_active(doc, &fn, &line, &col);
+	Message(PCB_MSG_DEFAULT, "Resource error: %s (%s:%d.%d)*\n", lht_err_str(err), filename, line+1, col+1);
+	return 1;
+}
+
+lht_doc_t *hid_cfg_load_lht(const char *filename)
+{
+	FILE *f;
+	lht_doc_t *doc;
+	int error = 0;
+	char *efn;
+
+	resolve_path(filename, &efn, 0);
+
+	f = fopen(efn, "r");
+	if (f == NULL) {
+		free(efn);
+		return NULL;
+	}
+	doc = lht_dom_init();
+	lht_dom_loc_newfile(doc, efn);
+
+	while(!(feof(f))) {
+		lht_err_t err;
+		int c = fgetc(f);
+		err = lht_dom_parser_char(doc, c);
+		if (err != LHTE_SUCCESS) {
+			if (err != LHTE_STOP) {
+				error = hid_cfg_load_error(doc, efn, err);
+				break;
+			}
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+		}
+	}
+
+	if (error) {
+		lht_dom_uninit(doc);
+		doc = NULL;
+	}
+	fclose(f);
+
+	free(efn);
+	return doc;
+}
+
+lht_doc_t *hid_cfg_load_str(const char *text)
+{
+	lht_doc_t *doc;
+	int error = 0;
+
+	doc = lht_dom_init();
+	lht_dom_loc_newfile(doc, "embedded");
+
+	while(*text != '\0') {
+		lht_err_t err;
+		int c = *text++;
+		err = lht_dom_parser_char(doc, c);
+		if (err != LHTE_SUCCESS) {
+			if (err != LHTE_STOP) {
+				error = hid_cfg_load_error(doc, "internal", err);
+				break;
+			}
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+		}
+	}
+
+	if (error) {
+		lht_dom_uninit(doc);
+		doc = NULL;
+	}
+
+	return doc;
+}
+
+const char *hid_cfg_text_value(lht_doc_t *doc, const char *path)
+{
+	lht_node_t *n = lht_tree_path(doc, "/", path, 1, NULL);
+	if (n == NULL)
+		return NULL;
+	if (n->type != LHT_TEXT) {
+		hid_cfg_error(n, "Error: node %s should be a text node\n", path);
+		return NULL;
+	}
+	return n->data.text.value;
+}
+
+hid_cfg_t *hid_cfg_load(const char *fn, int exact_fn, const char *embedded_fallback)
+{
+	lht_doc_t *doc;
+	hid_cfg_t *hr;
+
+	if (!exact_fn) {
+		/* try different paths to find the menu file inventing its exact name */
+		static const char *hid_cfg_paths_in[] = { "./", PCBSHAREDIR "/", NULL };
+		char **paths = NULL, **p;
+		int fn_len = strlen(fn);
+
+		doc = NULL;
+		resolve_all_paths(hid_cfg_paths_in, paths, fn_len+32);
+		for(p = paths; *p != NULL; p++) {
+			if (doc == NULL) {
+				char *end = *p + strlen(*p);
+				sprintf(end, "pcb-menu-%s.lht", fn);
+				doc = hid_cfg_load_lht(*p);
+				if (doc != NULL)
+					Message(PCB_MSG_DEFAULT, "Loaded menu file '%s'\n", *p);
+			}
+			free(*p);
+		}
+		free(paths);
+	}
+	else
+		doc = hid_cfg_load_lht(fn);
+
+	if (doc == NULL)
+		doc = hid_cfg_load_str(embedded_fallback);
+	if (doc == NULL)
+		return NULL;
+
+	hr = calloc(sizeof(hid_cfg_t), 1); /* make sure the cache is cleared */
+	hr->doc = doc;
+
+	return hr;
+}
+
+/************* "parsing" **************/
+
+lht_node_t *hid_cfg_get_menu_at(hid_cfg_t *hr, lht_node_t *at, const char *menu_path, lht_node_t *(*cb)(void *ctx, lht_node_t *node, const char *path, int rel_level), void *ctx)
+{
+	lht_err_t err;
+	lht_node_t *curr;
+	int level = 0, len = strlen(menu_path);
+	char *next_seg, *path;
+
+ path = malloc(len+4); /* need a few bytes after the end for the ':' */
+ strcpy(path, menu_path);
+
+	next_seg = path;
+	curr = (at == NULL) ? hr->doc->root : at;
+
+	/* Have to descend manually because of the submenu nodes */
+	for(;;) {
+		char *next, *end, save;
+		while(*next_seg == '/') next_seg++;
+		if (curr != hr->doc->root) {
+			if (level > 1) {
+				curr = lht_tree_path_(hr->doc, curr, "submenu", 1, 0, &err);
+				if (curr == NULL)
+					break;
+			}
+		}
+		next = end = strchr(next_seg, '/');
+		if (end == NULL)
+			end = next_seg + strlen(next_seg);
+		
+		if (level > 0)
+			*end = ':';
+		else
+			*end = '\0';
+		end++;
+		save = *end;
+		*end = '\0';
+		
+		curr = lht_tree_path_(hr->doc, curr, next_seg, 1, 0, &err);
+		if (cb != NULL) {
+			end[-1] = '\0';
+			curr = cb(ctx, curr, path, level);
+		}
+
+		*end = save;
+		if (next != NULL) /* restore previous / so that path is a full path */
+			*next = '/';
+		next_seg = next;
+		if ((curr == NULL) || (next_seg == NULL))
+			break;
+		next_seg++;
+		level++;
+	}
+
+	free(path);
+	return curr;
+}
+
+lht_node_t *hid_cfg_get_menu(hid_cfg_t *hr, const char *menu_path)
+{
+	return hid_cfg_get_menu_at(hr, NULL, menu_path, NULL, NULL);
+}
+
+lht_node_t *hid_cfg_menu_field(const lht_node_t *submenu, hid_cfg_menufield_t field, const char **field_name)
+{
+	lht_err_t err;
+	const char *fieldstr = NULL;
+
+	switch(field) {
+		case MF_ACCELERATOR:  fieldstr = "a"; break;
+		case MF_MNEMONIC:     fieldstr = "m"; break;
+		case MF_SUBMENU:      fieldstr = "submenu"; break;
+		case MF_CHECKED:      fieldstr = "checked"; break;
+		case MF_UPDATE_ON:    fieldstr = "update_on"; break;
+		case MF_SENSITIVE:    fieldstr = "sensitive"; break;
+		case MF_TIP:          fieldstr = "tip"; break;
+		case MF_ACTIVE:       fieldstr = "active"; break;
+		case MF_ACTION:       fieldstr = "action"; break;
+		case MF_FOREGROUND:   fieldstr = "foreground"; break;
+		case MF_BACKGROUND:   fieldstr = "background"; break;
+		case MF_FONT:         fieldstr = "font"; break;
+/*		case MF_RADIO:        fieldstr = "radio"; break; */
+	}
+	if (field_name != NULL)
+		*field_name = fieldstr;
+
+	if (fieldstr == NULL)
+		return NULL;
+
+	return lht_tree_path_(submenu->doc, submenu, fieldstr, 1, 0, &err);
+}
+
+const char *hid_cfg_menu_field_str(const lht_node_t *submenu, hid_cfg_menufield_t field)
+{
+	const char *fldname;
+	lht_node_t *n = hid_cfg_menu_field(submenu, field, &fldname);
+
+	if (n == NULL)
+		return NULL;
+	if (n->type != LHT_TEXT) {
+		hid_cfg_error(submenu, "Error: field %s should be a text node\n", fldname);
+		return NULL;
+	}
+	return n->data.text.value;
+}
+
+int hid_cfg_has_submenus(const lht_node_t *submenu)
+{
+	const char *fldname;
+	lht_node_t *n = hid_cfg_menu_field(submenu, MF_SUBMENU, &fldname);
+	if (n == NULL)
+		return 0;
+	if (n->type != LHT_LIST) {
+		hid_cfg_error(submenu, "Error: field %s should be a list (of submenus)\n", fldname);
+		return 0;
+	}
+	return 1;
+}
+
+
+void hid_cfg_extend_hash_nodev(lht_node_t *node, va_list ap)
+{
+	for(;;) {
+		char *cname, *cval;
+		lht_node_t *t;
+
+		cname = va_arg(ap, char *);
+		if (cname == NULL)
+			break;
+		cval = va_arg(ap, char *);
+
+		if (cval != NULL) {
+			t = lht_dom_node_alloc(LHT_TEXT, cname);
+			t->data.text.value = pcb_strdup(cval);
+			lht_dom_hash_put(node, t);
+		}
+	}
+}
+
+void hid_cfg_extend_hash_node(lht_node_t *node, ...)
+{
+	va_list ap;
+	va_start(ap, node);
+	hid_cfg_extend_hash_nodev(node, ap);
+	va_end(ap);
+}
+
+lht_node_t *hid_cfg_create_hash_node(lht_node_t *parent, const char *name, ...)
+{
+	lht_node_t *n;
+	va_list ap;
+
+	if ((parent != NULL) && (parent->type != LHT_LIST))
+		return NULL;
+
+	n = lht_dom_node_alloc(LHT_HASH, name);
+	if (parent != NULL)
+		lht_dom_list_append(parent, n);
+
+	va_start(ap, name);
+	hid_cfg_extend_hash_nodev(n, ap);
+	va_end(ap);
+
+	return n;
+}
+
+lht_node_t *hid_cfg_menu_field_path(const lht_node_t *parent, const char *path)
+{
+	return lht_tree_path_(parent->doc, parent, path, 1, 0, NULL);
+}
+
+int hid_cfg_dfs(lht_node_t *parent, int (*cb)(void *ctx, lht_node_t *n), void *ctx)
+{
+	lht_dom_iterator_t it;
+	lht_node_t *n;
+
+	for(n = lht_dom_first(&it, parent); n != NULL; n = lht_dom_next(&it)) {
+		int ret;
+		ret = cb(ctx, n);
+		if (ret != 0)
+			return ret;
+		if (n->type != LHT_TEXT) {
+			ret = hid_cfg_dfs(n, cb, ctx);
+			if (ret != 0)
+				return ret;
+		}
+	}
+	return 0;
+}
+
+/* extern char hid_cfg_error_shared[]; */
+void hid_cfg_error(const lht_node_t *node, const char *fmt, ...)
+{
+	char *end;
+	va_list ap;
+
+	end = hid_cfg_error_shared + sprintf(hid_cfg_error_shared, "Error in lihata node %s:%d.%d:", node->file_name, node->line, node->col);
+	va_start(ap, fmt);
+	end += vsprintf(end, fmt, ap);
+	va_end(ap);
+	Message(PCB_MSG_DEFAULT, hid_cfg_error_shared);
+}
diff --git a/src/hid_cfg.h b/src/hid_cfg.h
new file mode 100644
index 0000000..7f529a6
--- /dev/null
+++ b/src/hid_cfg.h
@@ -0,0 +1,113 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Helpers for loading and handling lihata HID config files */
+
+#ifndef PCB_HID_CFG_H
+#define PCB_HID_CFG_H
+
+#include <liblihata/dom.h>
+
+typedef struct hid_cfg_s {
+	lht_doc_t *doc;
+} hid_cfg_t;
+
+/* Create a set of resources representing a single menu item
+   If action is NULL, it's a drop-down item that has submenus.
+   The callback is called after the new lihata node is created.
+   NOTE: unlike other cookies, this cookie is strdup()'d. 
+   */
+typedef int (*create_menu_widget_t)(void *ctx, const char *path, const char *name, int is_main, lht_node_t *parent, lht_node_t *menu_item);
+int hid_cfg_create_menu(hid_cfg_t *hr, const char *path, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie, create_menu_widget_t cb, void *cb_ctx);
+
+/* Remove a path recursively; call gui_remove() on leaf paths until the subtree
+   is consumed (should return 0 on success) */
+int hid_cfg_remove_menu(hid_cfg_t *hr, const char *path, int (*gui_remove)(void *ctx, lht_node_t *nd), void *ctx);
+
+/* Search and load the menu res for hidname; if not found, and embedded_fallback
+   is not NULL, parse that string instead. Returns NULL on error */
+hid_cfg_t *hid_cfg_load(const char *fn, int exact_fn, const char *embedded_fallback);
+
+/* Generic, low level lihata loader */
+lht_doc_t *hid_cfg_load_lht(const char *filename);
+lht_doc_t *hid_cfg_load_str(const char *text);
+
+/* Generic, low level lihata text value fetch */
+const char *hid_cfg_text_value(lht_doc_t *doc, const char *path);
+
+lht_node_t *hid_cfg_get_menu(hid_cfg_t *hr, const char *menu_path);
+lht_node_t *hid_cfg_get_menu_at(hid_cfg_t *hr, lht_node_t *at, const char *menu_path, lht_node_t *(*cb)(void *ctx, lht_node_t *node, const char *path, int rel_level), void *ctx);
+
+
+/* Fields are retrieved using this enum so that HIDs don't need to hardwire
+   lihata node names */
+typedef enum {
+	MF_ACCELERATOR,
+	MF_MNEMONIC,
+	MF_SUBMENU,
+	MF_CHECKED,
+	MF_UPDATE_ON,
+	MF_SENSITIVE,
+	MF_TIP,
+	MF_ACTIVE,
+	MF_ACTION,
+	MF_FOREGROUND,
+	MF_BACKGROUND,
+	MF_FONT
+/*	MF_RADIO*/
+} hid_cfg_menufield_t;
+
+/* Return a field of a submenu and optionally fill in field_name with the
+   field name expected in the lihata document (useful for error messages) */
+lht_node_t *hid_cfg_menu_field(const lht_node_t *submenu, hid_cfg_menufield_t field, const char **field_name);
+
+/* Return a lihata node using a relative lihata path from parent - this is
+   just a wrapper around lht_tree_path_ */
+lht_node_t *hid_cfg_menu_field_path(const lht_node_t *parent, const char *path);
+
+/* Return a text field of a submenu; return NULL and generate a Message(PCB_MSG_DEFAULT, ) if
+   the given field is not text */
+const char *hid_cfg_menu_field_str(const lht_node_t *submenu, hid_cfg_menufield_t field);
+
+/* Return non-zero if submenu has further submenus; generate Message(PCB_MSG_DEFAULT, ) if
+   there is a submenu field with the wrong lihata type */
+int hid_cfg_has_submenus(const lht_node_t *submenu);
+
+/* Create a new hash node under parent (optional) and create a flat subtree of
+   text nodes from name,value varargs (NULL terminated). This is a shorthand
+   for creating a menu item in a subtree list. */
+lht_node_t *hid_cfg_create_hash_node(lht_node_t *parent, const char *name, ...);
+
+/* Create a flat subtree of text nodes from name,value varargs (NULL
+   terminated). This is a shorthand for creating a menu item in a
+   subtree list. */
+void hid_cfg_extend_hash_node(lht_node_t *node, ...);
+void hid_cfg_extend_hash_nodev(lht_node_t *node, va_list ap);
+
+/* Search a subtree in depth-first-search manner. Call cb on each node as
+   descending. If cb returns non-zero, stop the search and return that value.
+   Do all this recursively. */
+int hid_cfg_dfs(lht_node_t *parent, int (*cb)(void *ctx, lht_node_t *n), void *ctx);
+
+/* Report an error about a node */
+void hid_cfg_error(const lht_node_t *node, const char *fmt, ...);
+#endif
diff --git a/src/hid_cfg_action.c b/src/hid_cfg_action.c
new file mode 100644
index 0000000..a40e04a
--- /dev/null
+++ b/src/hid_cfg_action.c
@@ -0,0 +1,48 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "hid_cfg_action.h"
+#include "hid_actions.h"
+
+int hid_cfg_action(const lht_node_t *node)
+{
+	if (node == NULL)
+		return -1;
+	switch(node->type) {
+		case LHT_TEXT:
+			return hid_parse_actions(node->data.text.value);
+		case LHT_LIST:
+			for(node = node->data.list.first; node != NULL; node = node->next) {
+				if (node->type == LHT_TEXT) {
+					if (hid_parse_actions(node->data.text.value) != 0)
+						return -1;
+				}
+				else
+					return -1;
+			}
+			return 0;
+		default: ; /* suppress compiler warning: can't handle any other type */
+	}
+	return -1;
+}
diff --git a/src/hid_cfg_action.h b/src/hid_cfg_action.h
new file mode 100644
index 0000000..9b1a2d6
--- /dev/null
+++ b/src/hid_cfg_action.h
@@ -0,0 +1,27 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include "hid_cfg.h"
+/* Run an action node. The node is either a list of text nodes or a text node;
+   returns non-zero on error, the first action that fails in a chain breaks the chain */
+int hid_cfg_action(const lht_node_t *node);
diff --git a/src/hid_cfg_input.c b/src/hid_cfg_input.c
new file mode 100644
index 0000000..7d42988
--- /dev/null
+++ b/src/hid_cfg_input.c
@@ -0,0 +1,459 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <liblihata/lihata.h>
+#include <liblihata/tree.h>
+#include <genht/hash.h>
+#include <genvector/gds_char.h>
+
+#include "global.h"
+#include "hid.h"
+#include "hid_cfg.h"
+#include "hid_cfg_input.h"
+#include "hid_actions.h"
+#include "hid_cfg_action.h"
+#include "error.h"
+
+/* split value into a list of '-' separated words; examine each word
+   and set the bitmask of modifiers */
+static hid_cfg_mod_t parse_mods(const char *value, const char **last, unsigned int vlen)
+{
+	hid_cfg_mod_t m = 0;
+	int press = 0;
+	const char *next;
+
+	while(isspace(*value)) value++;
+
+	if (*value != '<') {
+	for(;;) {
+		if ((vlen >= 5) && (strncasecmp(value, "shift", 5) == 0))        m |= M_Shift;
+		else if ((vlen >= 4) && (strncasecmp(value, "ctrl", 4) == 0))    m |= M_Ctrl;
+		else if ((vlen >= 3) && (strncasecmp(value, "alt", 3) == 0))     m |= M_Alt;
+		else if ((vlen >= 7) && (strncasecmp(value, "release", 7) == 0)) m |= M_Release;
+		else if ((vlen >= 5) && (strncasecmp(value, "press", 5) == 0))   press = 1;
+		else
+			Message(PCB_MSG_DEFAULT, "Unknown modifier: %s\n", value);
+		/* skip to next word */
+		next = strpbrk(value, "<- \t");
+		if (next == NULL)
+			break;
+		if (*next == '<')
+			break;
+		vlen -= (next - value);
+		value = next+1;
+	}
+	}
+
+	if (last != NULL)
+		*last = value;
+
+	if (press && (m & M_Release))
+		Message(PCB_MSG_DEFAULT, "Bogus modifier: both press and release\n");
+
+	return m;
+}
+
+static hid_cfg_mod_t button_name2mask(const char *name)
+{
+	/* All mouse-related resources must be named.  The name is the
+	   mouse button number.  */
+	if (!name)
+		return 0;
+	else if (strcasecmp(name, "left") == 0)   return MB_LEFT;
+	else if (strcasecmp(name, "middle") == 0) return MB_MIDDLE;
+	else if (strcasecmp(name, "right") == 0)  return MB_RIGHT;
+
+	else if (strcasecmp(name, "scroll-up") == 0)     return MB_SCROLL_UP;
+	else if (strcasecmp(name, "scroll-down") == 0)   return MB_SCROLL_DOWN;
+	else if (strcasecmp(name, "scroll-left") == 0)   return MB_SCROLL_UP;
+	else if (strcasecmp(name, "scroll-right") == 0)  return MB_SCROLL_DOWN;
+	else {
+		Message(PCB_MSG_DEFAULT, "Error: unknown mouse button: %s\n", name);
+		return 0;
+	}
+}
+
+static unsigned int keyhash_int(htip_key_t a)      { return murmurhash32(a & 0xFFFF); }
+
+/************************** MOUSE ***************************/
+
+int hid_cfg_mouse_init(hid_cfg_t *hr, hid_cfg_mouse_t *mouse)
+{
+	lht_node_t *btn, *m;
+
+	mouse->mouse = hid_cfg_get_menu(hr, "/mouse");
+
+	if (mouse->mouse == NULL) {
+		Message(PCB_MSG_DEFAULT, "Warning: no /mouse section in the resource file - mouse is disabled\n");
+		return -1;
+	}
+
+	if (mouse->mouse->type != LHT_LIST) {
+		hid_cfg_error(mouse->mouse, "Warning: should be a list - mouse is disabled\n");
+		return -1;
+	}
+
+	if (mouse->mouse_mask == NULL)
+		mouse->mouse_mask = htip_alloc(keyhash_int, htip_keyeq);
+	else
+		htip_clear(mouse->mouse_mask);
+
+	for(btn = mouse->mouse->data.list.first; btn != NULL; btn = btn->next) {
+		hid_cfg_mod_t btn_mask = button_name2mask(btn->name);
+		if (btn_mask == 0) {
+			hid_cfg_error(btn, "unknown mouse button");
+			continue;
+		}
+		if (btn->type != LHT_LIST) {
+			hid_cfg_error(btn, "needs to be a list");
+			continue;
+		}
+		for(m = btn->data.list.first; m != NULL; m = m->next) {
+			hid_cfg_mod_t mod_mask = parse_mods(m->name, NULL, -1);
+			htip_set(mouse->mouse_mask, btn_mask|mod_mask, m);
+		}
+	}
+	return 0;
+}
+
+static lht_node_t *find_best_action(hid_cfg_mouse_t *mouse, hid_cfg_mod_t button_and_mask)
+{
+	lht_node_t *n;
+
+	if (mouse->mouse_mask == NULL)
+		return NULL;
+
+	/* look for exact mod match */
+	n = htip_get(mouse->mouse_mask, button_and_mask);
+	if (n != NULL)
+		return n;
+
+	if (button_and_mask & M_Release) {
+		/* look for plain release for the given button */
+		n = htip_get(mouse->mouse_mask, (button_and_mask & M_ANY) | M_Release);
+		if (n != NULL)
+			return n;
+	}
+
+	return NULL;
+}
+
+void hid_cfg_mouse_action(hid_cfg_mouse_t *mouse, hid_cfg_mod_t button_and_mask)
+{
+	hid_cfg_action(find_best_action(mouse, button_and_mask));
+}
+
+
+/************************** KEYBOARD ***************************/
+int hid_cfg_keys_init(hid_cfg_keys_t *km)
+{
+	htip_init(&km->keys, keyhash_int, htip_keyeq);
+	return 0;
+}
+
+int hid_cfg_keys_uninit(hid_cfg_keys_t *km)
+{
+#warning TODO: recursive free of nodes
+	htip_uninit(&km->keys);
+	return 0;
+}
+
+hid_cfg_keyseq_t *hid_cfg_keys_add_under(hid_cfg_keys_t *km, hid_cfg_keyseq_t *parent, hid_cfg_mod_t mods, unsigned short int key_char, int terminal)
+{
+	hid_cfg_keyseq_t *ns;
+	hid_cfg_keyhash_t addr;
+	htip_t *phash = (parent == NULL) ? &km->keys : &parent->seq_next;
+
+	/* do not grow the tree under actions */
+	if ((parent != NULL) && (parent->action_node != NULL))
+		return NULL;
+
+
+	addr.hash = 0;
+	addr.details.mods = mods;
+	addr.details.key_char = key_char;
+
+	/* already in the tree */
+	ns = htip_get(phash, addr.hash);
+	if (ns != NULL) {
+		if (terminal)
+			return NULL; /* full-path-match is collision */
+		return ns;
+	}
+
+	/* new node on this level */
+	ns = calloc(sizeof(hid_cfg_keyseq_t), 1);
+	if (!terminal)
+		htip_init(&ns->seq_next, keyhash_int, htip_keyeq);
+	htip_set(phash, addr.hash, ns);
+	return ns;
+}
+
+const hid_cfg_keytrans_t hid_cfg_key_default_trans[] = {
+	{ "semicolon", ';'  },
+	{ NULL,        0    },
+};
+
+static unsigned short int translate_key(hid_cfg_keys_t *km, const char *desc, int len)
+{
+	char tmp[256];
+
+	if ((km->auto_chr) && (len == 1))
+			return *desc;
+
+	if (len > sizeof(tmp)-1) {
+		Message(PCB_MSG_DEFAULT, "key sym name too long\n");
+		return 0;
+	}
+	strncpy(tmp, desc, len);
+	tmp[len] = '\0';
+
+	if (km->auto_tr != NULL) {
+		const hid_cfg_keytrans_t *t;
+		for(t = km->auto_tr; t->name != NULL; t++) {
+			if (strcasecmp(tmp, t->name) == 0) {
+				tmp[0] = t->sym;
+				tmp[1] = '\0';
+				len = 1;
+				break;
+			}
+		}
+	}
+
+	return km->translate_key(tmp, len);
+}
+
+static int parse_keydesc(hid_cfg_keys_t *km, const char *keydesc, hid_cfg_mod_t *mods, unsigned short int *key_chars, int arr_len)
+{
+	const char *curr, *next, *last, *k;
+	int slen, len;
+
+	slen = 0;
+	curr = keydesc;
+	do {
+		if (slen >= arr_len)
+			return -1;
+		while(isspace(*curr)) curr++;
+		if (*curr == '\0')
+			break;
+		next = strchr(curr, ';');
+		if (next != NULL) {
+			len = next - curr;
+			while(*next == ';') next++;
+		}
+		else
+			len = strlen(curr);
+
+		mods[slen] = parse_mods(curr, &last, len);
+
+		k = strchr(last, '<');
+		if (k == NULL) {
+			Message(PCB_MSG_DEFAULT, "Missing <key> in the key description: '%s'\n", keydesc);
+			return -1;
+		}
+		len -= k-last;
+		k++; len--;
+		if ((strncmp(k, "key>", 4) != 0) && (strncmp(k, "Key>", 4) != 0)) {
+			Message(PCB_MSG_DEFAULT, "Missing <key> in the key description\n");
+			return -1;
+		}
+		k+=4; len-=4;
+		key_chars[slen] = translate_key(km, k, len);
+
+		if (key_chars[slen] == 0) {
+			char *s;
+			s = malloc(len+1);
+			memcpy(s, k, len);
+			s[len] = '\0';
+			Message(PCB_MSG_DEFAULT, "Unrecognised key symbol in key description: %s\n", s);
+			free(s);
+			return -1;
+		}
+
+		slen++;
+		curr = next;
+	} while(curr != NULL);
+	return slen;
+}
+
+int hid_cfg_keys_add_by_strdesc(hid_cfg_keys_t *km, const char *keydesc, const lht_node_t *action_node, hid_cfg_keyseq_t **out_seq, int out_seq_len)
+{
+	hid_cfg_mod_t mods[HIDCFG_MAX_KEYSEQ_LEN];
+	unsigned short int key_chars[HIDCFG_MAX_KEYSEQ_LEN];
+	hid_cfg_keyseq_t *lasts;
+	int slen, n;
+
+	slen = parse_keydesc(km, keydesc, mods, key_chars, HIDCFG_MAX_KEYSEQ_LEN);
+	if (slen <= 0)
+		return slen;
+
+	if ((out_seq != NULL) && (slen >= out_seq_len))
+		return -1;
+
+/*	printf("KEY insert\n");*/
+
+	lasts = NULL;
+	for(n = 0; n < slen; n++) {
+		hid_cfg_keyseq_t *s;
+		int terminal = (n == slen-1);
+
+/*		printf(" mods=%x sym=%x\n", mods[n], key_chars[n]);*/
+
+		s = hid_cfg_keys_add_under(km, lasts, mods[n], key_chars[n], terminal);
+		if (s == NULL) {
+		printf("  ERROR\n");
+#warning TODO: free stuff?
+			return -1;
+		}
+		if (terminal)
+			s->action_node = action_node;
+
+		if (out_seq != NULL)
+			out_seq[n] = s;
+		lasts = s;
+	}
+
+	return slen;
+}
+
+int hid_cfg_keys_add_by_desc(hid_cfg_keys_t *km, const lht_node_t *keydescn, const lht_node_t *action_node, hid_cfg_keyseq_t **out_seq, int out_seq_len)
+{
+	switch(keydescn->type) {
+		case LHT_TEXT: return hid_cfg_keys_add_by_strdesc(km, keydescn->data.text.value, action_node, out_seq, out_seq_len);
+		case LHT_LIST:
+		{
+			int ret = -1, cnt;
+			lht_node_t *n;
+			for(n = keydescn->data.list.first, cnt = 0; n != NULL; n = n->next, cnt++) {
+				if (n->type != LHT_TEXT)
+					break;
+				if (cnt == 0)
+					ret = hid_cfg_keys_add_by_strdesc(km, n->data.text.value, action_node, out_seq, out_seq_len);
+				else
+					hid_cfg_keys_add_by_strdesc(km, n->data.text.value, action_node, NULL, 0);
+			}
+			return ret;
+		}
+		default:;
+	}
+	return -1;
+}
+
+static void gen_accel(gds_t *s, hid_cfg_keys_t *km, const char *keydesc, int *cnt, const char *sep)
+{
+	hid_cfg_mod_t mods[HIDCFG_MAX_KEYSEQ_LEN];
+	unsigned short int key_chars[HIDCFG_MAX_KEYSEQ_LEN];
+	int slen, n;
+
+	slen = parse_keydesc(km, keydesc, mods, key_chars, HIDCFG_MAX_KEYSEQ_LEN);
+	if (slen <= 0)
+		return;
+
+	if (*cnt > 0)
+		gds_append_str(s, sep);
+
+	for(n = 0; n < slen; n++) {
+		char buff[64];
+
+		if (n > 0)
+			gds_append(s, ' ');
+
+		if (km->key_name(key_chars[n], buff, sizeof(buff)) != 0)
+			strcpy(buff, "<unknown>");
+
+		if (mods[n] & M_Alt)   gds_append_str(s, "Alt-");
+		if (mods[n] & M_Ctrl)  gds_append_str(s, "Ctrl-");
+		if (mods[n] & M_Shift) gds_append_str(s, "Shift-");
+		gds_append_str(s, buff);
+	}
+}
+
+char *hid_cfg_keys_gen_accel(hid_cfg_keys_t *km, const lht_node_t *keydescn, unsigned long mask, const char *sep)
+{
+	gds_t s;
+	int cnt = 0;
+
+	memset(&s, 0, sizeof(s));
+
+	switch(keydescn->type) {
+		case LHT_TEXT:
+			if (mask & 1)
+				gen_accel(&s, km, keydescn->data.text.value, &cnt, sep);
+			break;
+		case LHT_LIST:
+		{
+			int cnt;
+			lht_node_t *n;
+			for(n = keydescn->data.list.first, cnt = 0; n != NULL; n = n->next, cnt++, mask >>= 1) {
+				if (n->type != LHT_TEXT)
+					break;
+				if (!(mask & 1))
+					continue;
+				gen_accel(&s, km, n->data.text.value, &cnt, sep);
+			}
+		}
+		default:;
+	}
+	return s.array;
+}
+
+
+int hid_cfg_keys_input(hid_cfg_keys_t *km, hid_cfg_mod_t mods, unsigned short int key_char, hid_cfg_keyseq_t **seq, int *seq_len)
+{
+	hid_cfg_keyseq_t *ns;
+	hid_cfg_keyhash_t addr;
+	htip_t *phash = (*seq_len == 0) ? &km->keys : &((seq[(*seq_len)-1])->seq_next);
+
+	addr.hash = 0;
+	addr.details.mods = mods;
+	addr.details.key_char = key_char;
+
+	/* already in the tree */
+	ns = htip_get(phash, addr.hash);
+	if (ns == NULL) {
+		(*seq_len) = 0;
+		return -1;
+	}
+
+	seq[*seq_len] = ns;
+	(*seq_len)++;
+
+	/* found a terminal node with an action */
+	if (ns->action_node != NULL) {
+		int len = *seq_len;
+		(*seq_len) = 0;
+		return len;
+	}
+
+	return 0;
+}
+
+int hid_cfg_keys_action(hid_cfg_keyseq_t **seq, int seq_len)
+{
+	if (seq_len < 1)
+		return -1;
+
+	return hid_cfg_action(seq[seq_len-1]->action_node);
+}
diff --git a/src/hid_cfg_input.h b/src/hid_cfg_input.h
new file mode 100644
index 0000000..6646483
--- /dev/null
+++ b/src/hid_cfg_input.h
@@ -0,0 +1,144 @@
+#ifndef PCB_HID_CFG_INPUT_H
+#define PCB_HID_CFG_INPUT_H
+
+#include <liblihata/dom.h>
+#include <genht/htip.h>
+#include "hid_cfg.h"
+
+#define M_Mod0(n)  (1u<<(n))
+typedef enum {
+	M_Shift   = M_Mod0(0),
+	M_Ctrl    = M_Mod0(1),
+	M_Alt     = M_Mod0(2),
+	M_Mod1    = M_Alt,
+	/* M_Mod(3) is M_Mod0(4) */
+	/* M_Mod(4) is M_Mod0(5) */
+	M_Release = M_Mod0(6), /* there might be a random number of modkeys, but hopefully not this many */
+
+	MB_LEFT   = M_Mod0(7),
+	MB_MIDDLE = M_Mod0(8),
+	MB_RIGHT  = M_Mod0(9),
+
+/* scroll wheel */
+	MB_SCROLL_UP     = M_Mod0(10),
+	MB_SCROLL_DOWN   = M_Mod0(11),
+	MB_SCROLL_LEFT   = M_Mod0(12),
+	MB_SCROLL_RIGHT  = M_Mod0(13)
+} hid_cfg_mod_t;
+#undef M_Mod0
+
+#define MB_ANY (MB_LEFT | MB_MIDDLE | MB_RIGHT | MB_SCROLL_UP | MB_SCROLL_DOWN | MB_SCROLL_LEFT | MB_SCROLL_RIGHT)
+#define M_ANY  (M_Release-1)
+
+/************************** MOUSE ***************************/
+
+typedef struct {
+	lht_node_t *mouse;
+	htip_t *mouse_mask;
+} hid_cfg_mouse_t;
+
+int hid_cfg_mouse_init(hid_cfg_t *hr, hid_cfg_mouse_t *mouse);
+void hid_cfg_mouse_action(hid_cfg_mouse_t *mouse, hid_cfg_mod_t button_and_mask);
+
+
+/************************** KEYBOARD ***************************/
+#define HIDCFG_MAX_KEYSEQ_LEN 32
+typedef union hid_cfg_keyhash_u { /* integer hash key */
+	unsigned long hash;
+	struct {
+		unsigned short int mods;          /* of hid_cfg_mod_t */
+		unsigned short int key_char;
+	} details;
+} hid_cfg_keyhash_t;
+
+
+typedef struct hid_cfg_keyseq_s  hid_cfg_keyseq_t;
+struct hid_cfg_keyseq_s {
+	unsigned long int keysym;  /* optional 32 bit long storage the GUI hid should cast to whatever the GUI backend supports */
+
+	const lht_node_t *action_node; /* terminal node: end of sequence, run actions */
+
+	htip_t seq_next; /* ... or if node is NULL, a hash for each key that may follow the current one */
+	hid_cfg_keyseq_t *parent;
+};
+
+/* Translate symbolic name to single-char keysym before processing; useful
+   for shortcuts like "Return -> '\r'" which are otherwise hard to describe
+   in text format */
+typedef struct hid_cfg_keytrans_s {
+		const char *name;
+		char sym;
+} hid_cfg_keytrans_t;
+
+extern const hid_cfg_keytrans_t hid_cfg_key_default_trans[];
+
+/* A complete tree of keyboard shortcuts/hotkeys */
+typedef struct hid_cfg_keys_s {
+	htip_t keys;
+
+	/* translate key sym description (the portion after <Key>) to key_char;
+	   desc is a \0 terminated string, len is only a hint. Should return 0
+	   on error. */
+	unsigned short int (*translate_key)(const char *desc, int len);
+
+	/* convert a key_char to human readable name, copy the string to out/out_len.
+	   Return 0 on success. */
+	int (*key_name)(unsigned short int key_char, char *out, int out_len);
+
+
+	int auto_chr;                       /* if non-zero: don't call translate_key() for 1-char symbols, handle the default way */
+	const hid_cfg_keytrans_t *auto_tr;  /* apply this table before calling translate_key() */
+} hid_cfg_keys_t;
+
+
+/* Initialize a new keyboard context
+   Returns 0 on success.
+*/
+int hid_cfg_keys_init(hid_cfg_keys_t *km);
+
+/* Free km's fields recursively */
+int hid_cfg_keys_uninit(hid_cfg_keys_t *km);
+
+/* Add the next key of a key sequence; key sequences form a tree. A key starting
+   a new key sequence should have parent set NULL, subsequent calls should have
+   parent set to the previously returned keyseq value. Terminal is non-zero if
+   this is the last key of the sequence.
+   Returns NULL on error */
+hid_cfg_keyseq_t *hid_cfg_keys_add_under(hid_cfg_keys_t *km, hid_cfg_keyseq_t *parent, hid_cfg_mod_t mods, unsigned short int key_char, int terminal);
+
+/* Add a new key using a description (read from a lihata file usually)
+   If out_seq is not NULL, load the array with pointers pointing to each
+   key in the sequence, up to out_seq_len.
+   When key desc is a lihata node, it may be a list (multiple keys for the
+   same action). In this case return value and seq are set using the first key.
+   Returns -1 on failure or the length of the sequence.
+*/
+int hid_cfg_keys_add_by_desc(hid_cfg_keys_t *km, const lht_node_t *keydesc, const lht_node_t *action_node, hid_cfg_keyseq_t **out_seq, int out_seq_len);
+int hid_cfg_keys_add_by_strdesc(hid_cfg_keys_t *km, const char *keydesc, const lht_node_t *action_node, hid_cfg_keyseq_t **out_seq, int out_seq_len);
+
+
+/* Allocate a new string and generate a human readable accel-text; mask determines
+   which keys on the list are generated (when multiple key sequences are
+   specified for the same action; from LSB to MSB, at most 32 keys)
+   Caller needs to free the string; returns NULL on error.
+   */
+char *hid_cfg_keys_gen_accel(hid_cfg_keys_t *km, const lht_node_t *keydescn, unsigned long mask, const char *sep);
+
+/* Process next input key stroke.
+   Seq and seq_len must not be NULL as they are the internal state of multi-key
+   processing. Load seq array with pointers pointing to each key in the
+   sequence, up to seq_len.
+   Returns:
+    -1 if the key stroke or sequence is invalid
+     0 if more characters needed to complete the sequence
+     + a positive integer means the lookup succeeded and the return value
+       is the length of the resulting sequence.
+*/
+int hid_cfg_keys_input(hid_cfg_keys_t *km, hid_cfg_mod_t mods, unsigned short int key_char, hid_cfg_keyseq_t **seq, int *seq_len);
+
+/* Run the action for a key sequence looked up by hid_cfg_keys_input().
+   Returns: the result of the action or -1 on error */
+int hid_cfg_keys_action(hid_cfg_keyseq_t **seq, int seq_len);
+
+
+#endif
diff --git a/src/hid_color.c b/src/hid_color.c
new file mode 100644
index 0000000..06d2897
--- /dev/null
+++ b/src/hid_color.c
@@ -0,0 +1,41 @@
+#include "config.h"
+#include "global.h"
+#include "hid_color.h"
+#include <genht/hash.h>
+#include "compat_misc.h"
+
+static hidval invalid_color = { 0 };
+
+#define HT_HAS_CONST_KEY
+typedef char *htsh_key_t;
+typedef const char *htsh_const_key_t;
+typedef hidval htsh_value_t;
+#define HT_INVALID_VALUE invalid_color
+#define HT(x) htsh_ ## x
+#include <genht/ht.h>
+#include <genht/ht.c>
+#undef HT
+
+
+int hid_cache_color(int set, const char *name, hidval * val, void **vcache)
+{
+	htsh_t *cache;
+	htsh_entry_t *e;
+
+	cache = (htsh_t *) * vcache;
+	if (cache == 0) {
+		cache = htsh_alloc(strhash, strkeyeq);
+		*vcache = cache;
+	}
+
+	if (!set) { /* read */
+		e = htsh_getentry(cache, (char *)name);
+		if (e == NULL) /* not found */
+			return 0;
+		memcpy(val, &e->value, sizeof(hidval));
+	}
+	else
+		htsh_set(cache, pcb_strdup(name), *val); /* write */
+
+	return 1;
+}
diff --git a/src/hid_color.h b/src/hid_color.h
new file mode 100644
index 0000000..147c5b9
--- /dev/null
+++ b/src/hid_color.h
@@ -0,0 +1,13 @@
+#ifndef PCB_HID_COLOR_H
+#define PCB_HID_COLOR_H
+
+/* HID internal interfaces.  These may ONLY be called from the HID
+   modules, not from the common PCB code.  */
+
+/* Used to cache color lookups.  If set is zero, it looks up the name
+   and if found sets val and returns nonzero.  If not found, it
+   returns zero.  If set is nonzero, name/val is added to the
+   cache.  */
+int hid_cache_color(int set, const char *name, hidval * val, void **cache);
+
+#endif
diff --git a/src/hid_draw_helpers.c b/src/hid_draw_helpers.c
new file mode 100644
index 0000000..8bd0e29
--- /dev/null
+++ b/src/hid_draw_helpers.c
@@ -0,0 +1,436 @@
+#include "global.h"
+#include "hid.h"
+#include "polygon.h"
+
+static void fill_contour(hidGC gc, PLINE * pl)
+{
+	Coord *x, *y, n, i = 0;
+	VNODE *v;
+
+	n = pl->Count;
+	x = (Coord *) malloc(n * sizeof(*x));
+	y = (Coord *) malloc(n * sizeof(*y));
+
+	for (v = &pl->head; i < n; v = v->next) {
+		x[i] = v->point[0];
+		y[i++] = v->point[1];
+	}
+
+	gui->fill_polygon(gc, n, x, y);
+
+	free(x);
+	free(y);
+}
+
+static void thindraw_contour(hidGC gc, PLINE * pl)
+{
+	VNODE *v;
+	Coord last_x, last_y;
+	Coord this_x, this_y;
+
+	gui->set_line_width(gc, 0);
+	gui->set_line_cap(gc, Round_Cap);
+
+	/* If the contour is round, use an arc drawing routine. */
+	if (pl->is_round) {
+		gui->draw_arc(gc, pl->cx, pl->cy, pl->radius, pl->radius, 0, 360);
+		return;
+	}
+
+	/* Need at least two points in the contour */
+	if (pl->head.next == NULL)
+		return;
+
+	last_x = pl->head.point[0];
+	last_y = pl->head.point[1];
+	v = pl->head.next;
+
+	do {
+		this_x = v->point[0];
+		this_y = v->point[1];
+
+		gui->draw_line(gc, last_x, last_y, this_x, this_y);
+		/* gui->fill_circle (gc, this_x, this_y, 30); */
+
+		last_x = this_x;
+		last_y = this_y;
+	}
+	while ((v = v->next) != pl->head.next);
+}
+
+static void fill_contour_cb(PLINE * pl, void *user_data)
+{
+	hidGC gc = (hidGC) user_data;
+	PLINE *local_pl = pl;
+
+	fill_contour(gc, pl);
+	poly_FreeContours(&local_pl);
+}
+
+static void fill_clipped_contour(hidGC gc, PLINE * pl, const BoxType * clip_box)
+{
+	PLINE *pl_copy;
+	POLYAREA *clip_poly;
+	POLYAREA *piece_poly;
+	POLYAREA *clipped_pieces;
+	POLYAREA *draw_piece;
+	int x;
+
+	clip_poly = RectPoly(clip_box->X1, clip_box->X2, clip_box->Y1, clip_box->Y2);
+	poly_CopyContour(&pl_copy, pl);
+	piece_poly = poly_Create();
+	poly_InclContour(piece_poly, pl_copy);
+	x = poly_Boolean_free(piece_poly, clip_poly, &clipped_pieces, PBO_ISECT);
+	if (x != err_ok || clipped_pieces == NULL)
+		return;
+
+	draw_piece = clipped_pieces;
+	do {
+		/* NB: The polygon won't have any holes in it */
+		fill_contour(gc, draw_piece->contours);
+	}
+	while ((draw_piece = draw_piece->f) != clipped_pieces);
+	poly_Free(&clipped_pieces);
+}
+
+/* If at least 50% of the bounding box of the polygon is on the screen,
+ * lets compute the complete no-holes polygon.
+ */
+#define BOUNDS_INSIDE_CLIP_THRESHOLD 0.5
+static int should_compute_no_holes(PolygonType * poly, const BoxType * clip_box)
+{
+	Coord x1, x2, y1, y2;
+	double poly_bounding_area;
+	double clipped_poly_area;
+
+	/* If there is no passed clip box, compute the whole thing */
+	if (clip_box == NULL)
+		return 1;
+
+	x1 = MAX(poly->BoundingBox.X1, clip_box->X1);
+	x2 = MIN(poly->BoundingBox.X2, clip_box->X2);
+	y1 = MAX(poly->BoundingBox.Y1, clip_box->Y1);
+	y2 = MIN(poly->BoundingBox.Y2, clip_box->Y2);
+
+	/* Check if the polygon is outside the clip box */
+	if ((x2 <= x1) || (y2 <= y1))
+		return 0;
+
+	poly_bounding_area = (double) (poly->BoundingBox.X2 - poly->BoundingBox.X1) *
+		(double) (poly->BoundingBox.Y2 - poly->BoundingBox.Y1);
+
+	clipped_poly_area = (double) (x2 - x1) * (double) (y2 - y1);
+
+	if (clipped_poly_area / poly_bounding_area >= BOUNDS_INSIDE_CLIP_THRESHOLD)
+		return 1;
+
+	return 0;
+}
+
+#undef BOUNDS_INSIDE_CLIP_THRESHOLD
+
+void common_fill_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box)
+{
+	if (!poly->NoHolesValid) {
+		/* If enough of the polygon is on-screen, compute the entire
+		 * NoHoles version and cache it for later rendering, otherwise
+		 * just compute what we need to render now.
+		 */
+		if (should_compute_no_holes(poly, clip_box))
+			ComputeNoHoles(poly);
+		else
+			NoHolesPolygonDicer(poly, clip_box, fill_contour_cb, gc);
+	}
+	if (poly->NoHolesValid && poly->NoHoles) {
+		PLINE *pl;
+
+		for (pl = poly->NoHoles; pl != NULL; pl = pl->next) {
+			if (clip_box == NULL)
+				fill_contour(gc, pl);
+			else
+				fill_clipped_contour(gc, pl, clip_box);
+		}
+	}
+
+	/* Draw other parts of the polygon if fullpoly flag is set */
+	/* NB: No "NoHoles" cache for these */
+	if (TEST_FLAG(PCB_FLAG_FULLPOLY, poly)) {
+		PolygonType p = *poly;
+
+		for (p.Clipped = poly->Clipped->f; p.Clipped != poly->Clipped; p.Clipped = p.Clipped->f)
+			NoHolesPolygonDicer(&p, clip_box, fill_contour_cb, gc);
+	}
+}
+
+static int thindraw_hole_cb(PLINE * pl, void *user_data)
+{
+	hidGC gc = (hidGC) user_data;
+	thindraw_contour(gc, pl);
+	return 0;
+}
+
+void common_thindraw_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box)
+{
+	thindraw_contour(gc, poly->Clipped->contours);
+	PolygonHoles(poly, clip_box, thindraw_hole_cb, gc);
+}
+
+void common_thindraw_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask)
+{
+	Coord w = clear ? (mask ? pad->Mask : pad->Thickness + pad->Clearance)
+		: pad->Thickness;
+	Coord x1, y1, x2, y2;
+	Coord t = w / 2;
+	x1 = pad->Point1.X;
+	y1 = pad->Point1.Y;
+	x2 = pad->Point2.X;
+	y2 = pad->Point2.Y;
+	if (x1 > x2 || y1 > y2) {
+		Coord temp_x = x1;
+		Coord temp_y = y1;
+		x1 = x2;
+		x2 = temp_x;
+		y1 = y2;
+		y2 = temp_y;
+	}
+	gui->set_line_cap(gc, Round_Cap);
+	gui->set_line_width(gc, 0);
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pad)) {
+		/* slanted square pad */
+		double tx, ty, theta;
+
+		if (x1 == x2 && y1 == y2)
+			theta = 0;
+		else
+			theta = atan2(y2 - y1, x2 - x1);
+
+		/* T is a vector half a thickness long, in the direction of
+		   one of the corners.  */
+		tx = t * cos(theta + M_PI / 4) * sqrt(2.0);
+		ty = t * sin(theta + M_PI / 4) * sqrt(2.0);
+
+		gui->draw_line(gc, x1 - tx, y1 - ty, x2 + ty, y2 - tx);
+		gui->draw_line(gc, x2 + ty, y2 - tx, x2 + tx, y2 + ty);
+		gui->draw_line(gc, x2 + tx, y2 + ty, x1 - ty, y1 + tx);
+		gui->draw_line(gc, x1 - ty, y1 + tx, x1 - tx, y1 - ty);
+	}
+	else if (x1 == x2 && y1 == y2) {
+		gui->draw_arc(gc, x1, y1, t, t, 0, 360);
+	}
+	else {
+		/* Slanted round-end pads.  */
+		Coord dx, dy, ox, oy;
+		double h;
+
+		dx = x2 - x1;
+		dy = y2 - y1;
+		h = t / sqrt(SQUARE(dx) + SQUARE(dy));
+		ox = dy * h + 0.5 * SGN(dy);
+		oy = -(dx * h + 0.5 * SGN(dx));
+
+		gui->draw_line(gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
+
+		if (labs(ox) >= pixel_slop || coord_abs(oy) >= pixel_slop) {
+			Angle angle = atan2(dx, dy) * 57.295779;
+			gui->draw_line(gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
+			gui->draw_arc(gc, x1, y1, t, t, angle - 180, 180);
+			gui->draw_arc(gc, x2, y2, t, t, angle, 180);
+		}
+	}
+}
+
+void common_fill_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask)
+{
+	Coord w = clear ? (mask ? pad->Mask : pad->Thickness + pad->Clearance)
+		: pad->Thickness;
+
+	if (pad->Point1.X == pad->Point2.X && pad->Point1.Y == pad->Point2.Y) {
+		if (TEST_FLAG(PCB_FLAG_SQUARE, pad)) {
+			Coord l, r, t, b;
+			l = pad->Point1.X - w / 2;
+			b = pad->Point1.Y - w / 2;
+			r = l + w;
+			t = b + w;
+			gui->fill_rect(gc, l, b, r, t);
+		}
+		else {
+			gui->fill_circle(gc, pad->Point1.X, pad->Point1.Y, w / 2);
+		}
+	}
+	else {
+		gui->set_line_cap(gc, TEST_FLAG(PCB_FLAG_SQUARE, pad) ? Square_Cap : Round_Cap);
+		gui->set_line_width(gc, w);
+
+		gui->draw_line(gc, pad->Point1.X, pad->Point1.Y, pad->Point2.X, pad->Point2.Y);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * draws one polygon
+ * x and y are already in display coordinates
+ * the points are numbered:
+ *
+ *          5 --- 6
+ *         /       \
+ *        4         7
+ *        |         |
+ *        3         0
+ *         \       /
+ *          2 --- 1
+ */
+
+typedef struct {
+	double X, Y;
+} FloatPolyType;
+
+static void draw_square_pin_poly(hidGC gc, Coord X, Coord Y, Coord Thickness, Coord thin_draw, int style)
+{
+	static FloatPolyType p[8] = {
+		{0.5, -PCB_TAN_22_5_DEGREE_2},
+		{PCB_TAN_22_5_DEGREE_2, -0.5},
+		{-PCB_TAN_22_5_DEGREE_2, -0.5},
+		{-0.5, -PCB_TAN_22_5_DEGREE_2},
+		{-0.5, PCB_TAN_22_5_DEGREE_2},
+		{-PCB_TAN_22_5_DEGREE_2, 0.5},
+		{PCB_TAN_22_5_DEGREE_2, 0.5},
+		{0.5, PCB_TAN_22_5_DEGREE_2}
+	};
+	static int special_size = 0;
+	static int scaled_x[8];
+	static int scaled_y[8];
+	Coord polygon_x[9];
+	Coord polygon_y[9];
+	double xm[8], ym[8];
+	int i;
+
+	square_pin_factors(style, xm, ym);
+
+	if (Thickness != special_size) {
+		special_size = Thickness;
+		for (i = 0; i < 8; i++) {
+			scaled_x[i] = p[i].X * special_size;
+			scaled_y[i] = p[i].Y * special_size;
+		}
+	}
+	/* add line offset */
+	for (i = 0; i < 8; i++) {
+		polygon_x[i] = X + scaled_x[i] * xm[i];
+		polygon_y[i] = Y + scaled_y[i] * ym[i];
+	}
+
+	if (thin_draw) {
+		int i;
+		gui->set_line_cap(gc, Round_Cap);
+		gui->set_line_width(gc, 0);
+		polygon_x[8] = X + scaled_x[0] * xm[0];
+		polygon_y[8] = Y + scaled_y[0] * ym[0];
+		for (i = 0; i < 8; i++)
+			gui->draw_line(gc, polygon_x[i], polygon_y[i], polygon_x[i + 1], polygon_y[i + 1]);
+	}
+	else
+		gui->fill_polygon(gc, 8, polygon_x, polygon_y);
+}
+
+static void draw_octagon_poly(hidGC gc, Coord X, Coord Y, Coord Thickness, Coord thin_draw)
+{
+	draw_square_pin_poly(gc, X, Y, Thickness, thin_draw, 17);
+}
+
+
+void common_fill_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType * pv, pcb_bool drawHole, pcb_bool mask)
+{
+	Coord w = mask ? pv->Mask : pv->Thickness;
+	Coord r = w / 2;
+
+	if (TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+		if (mask)
+			gui->fill_circle(bg_gc, pv->X, pv->Y, r);
+		if (drawHole) {
+			gui->fill_circle(bg_gc, pv->X, pv->Y, r);
+			gui->set_line_cap(fg_gc, Round_Cap);
+			gui->set_line_width(fg_gc, 0);
+			gui->draw_arc(fg_gc, pv->X, pv->Y, r, r, 0, 360);
+		}
+		return;
+	}
+
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pv)) {
+		/* use the original code for now */
+		if ((GET_SQUARE(pv) == 0) || (GET_SQUARE(pv) == 1)) {
+			Coord l = pv->X - r;
+			Coord b = pv->Y - r;
+			Coord r = l + w;
+			Coord t = b + w;
+			gui->fill_rect(fg_gc, l, b, r, t);
+		}
+		else
+			draw_square_pin_poly(fg_gc, pv->X, pv->Y, w, pcb_false, GET_SQUARE(pv));
+	}
+	else if (TEST_FLAG(PCB_FLAG_OCTAGON, pv))
+		draw_octagon_poly(fg_gc, pv->X, pv->Y, w, pcb_false);
+	else													/* draw a round pin or via */
+		gui->fill_circle(fg_gc, pv->X, pv->Y, r);
+
+	/* and the drilling hole  (which is always round) */
+	if (drawHole)
+		gui->fill_circle(bg_gc, pv->X, pv->Y, pv->DrillingHole / 2);
+}
+
+void common_thindraw_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType * pv, pcb_bool drawHole, pcb_bool mask)
+{
+	Coord w = mask ? pv->Mask : pv->Thickness;
+	Coord r = w / 2;
+
+	if (TEST_FLAG(PCB_FLAG_HOLE, pv)) {
+		if (mask)
+			gui->draw_arc(fg_gc, pv->X, pv->Y, r, r, 0, 360);
+		if (drawHole) {
+			r = pv->DrillingHole / 2;
+			gui->set_line_cap(bg_gc, Round_Cap);
+			gui->set_line_width(bg_gc, 0);
+			gui->draw_arc(bg_gc, pv->X, pv->Y, r, r, 0, 360);
+		}
+		return;
+	}
+
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pv)) {
+		Coord l = pv->X - r;
+		Coord b = pv->Y - r;
+		Coord r = l + w;
+		Coord t = b + w;
+
+		gui->set_line_cap(fg_gc, Round_Cap);
+		gui->set_line_width(fg_gc, 0);
+		gui->draw_line(fg_gc, r, t, r, b);
+		gui->draw_line(fg_gc, l, t, l, b);
+		gui->draw_line(fg_gc, r, t, l, t);
+		gui->draw_line(fg_gc, r, b, l, b);
+
+	}
+	else if (TEST_FLAG(PCB_FLAG_OCTAGON, pv)) {
+		draw_octagon_poly(fg_gc, pv->X, pv->Y, w, pcb_true);
+	}
+	else {												/* draw a round pin or via */
+
+		gui->set_line_cap(fg_gc, Round_Cap);
+		gui->set_line_width(fg_gc, 0);
+		gui->draw_arc(fg_gc, pv->X, pv->Y, r, r, 0, 360);
+	}
+
+	/* and the drilling hole  (which is always round */
+	if (drawHole) {
+		gui->set_line_cap(bg_gc, Round_Cap);
+		gui->set_line_width(bg_gc, 0);
+		gui->draw_arc(bg_gc, pv->X, pv->Y, pv->DrillingHole / 2, pv->DrillingHole / 2, 0, 360);
+	}
+}
+
+void common_draw_helpers_init(HID * hid)
+{
+	hid->fill_pcb_polygon = common_fill_pcb_polygon;
+	hid->thindraw_pcb_polygon = common_thindraw_pcb_polygon;
+	hid->fill_pcb_pad = common_fill_pcb_pad;
+	hid->thindraw_pcb_pad = common_thindraw_pcb_pad;
+	hid->fill_pcb_pv = common_fill_pcb_pv;
+	hid->thindraw_pcb_pv = common_thindraw_pcb_pv;
+}
diff --git a/src/hid_draw_helpers.h b/src/hid_draw_helpers.h
new file mode 100644
index 0000000..d9b5a48
--- /dev/null
+++ b/src/hid_draw_helpers.h
@@ -0,0 +1,10 @@
+#ifndef PCB_HID_DRAW_HELPERS_H
+#define PCB_HID_DRAW_HELPERS_H
+void common_fill_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box);
+void common_thindraw_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box);
+void common_fill_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask);
+void common_thindraw_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask);
+void common_fill_pcb_pv(hidGC gc, PinType * pv, pcb_bool drawHole, pcb_bool mask);
+void common_thindraw_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType * pv, pcb_bool drawHole, pcb_bool mask);
+void common_draw_helpers_init(HID * hid);
+#endif
diff --git a/src/hid_extents.c b/src/hid_extents.c
new file mode 100644
index 0000000..14f55a9
--- /dev/null
+++ b/src/hid_extents.c
@@ -0,0 +1,188 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "global.h"
+#include "data.h"
+#include "layer.h"
+
+#include "hid.h"
+#include "hid_draw_helpers.h"
+#include "hid_helper.h"
+
+#ifndef MAXINT
+#define MAXINT (((unsigned int)(~0))>>1)
+#endif
+
+static BoxType box;
+
+typedef struct hid_gc_struct {
+	int width;
+} hid_gc_struct;
+
+static int extents_set_layer(const char *name, int group, int empty)
+{
+	int idx = group;
+	if (idx >= 0 && idx < max_group) {
+		idx = PCB->LayerGroups.Entries[idx][0];
+	}
+	if (idx >= 0 && idx < max_copper_layer + 2)
+		return 1;
+	if (idx < 0) {
+		switch (SL_TYPE(idx)) {
+		case SL_INVISIBLE:
+		case SL_MASK:
+		case SL_ASSY:
+			return 0;
+		case SL_SILK:
+		case SL_PDRILL:
+		case SL_UDRILL:
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static hidGC extents_make_gc(void)
+{
+	hidGC rv = (hidGC) malloc(sizeof(hid_gc_struct));
+	memset(rv, 0, sizeof(hid_gc_struct));
+	return rv;
+}
+
+static void extents_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void extents_use_mask(int use_it)
+{
+}
+
+static void extents_set_color(hidGC gc, const char *name)
+{
+}
+
+static void extents_set_line_cap(hidGC gc, EndCapStyle style)
+{
+}
+
+static void extents_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void extents_set_draw_xor(hidGC gc, int xor_)
+{
+}
+
+#define PEX(x,w) if (box.X1 > (x)-(w)) box.X1 = (x)-(w); \
+	if (box.X2 < (x)+(w)) box.X2 = (x)+(w)
+#define PEY(y,w) if (box.Y1 > (y)-(w)) box.Y1 = (y)-(w); \
+	if (box.Y2 < (y)+(w)) box.Y2 = (y)+(w)
+
+static void extents_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	PEX(x1, gc->width);
+	PEY(y1, gc->width);
+	PEX(x2, gc->width);
+	PEY(y2, gc->width);
+}
+
+static void extents_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle end_angle)
+{
+	/* Naive but good enough.  */
+	PEX(cx, width + gc->width);
+	PEY(cy, height + gc->width);
+}
+
+static void extents_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	PEX(x1, gc->width);
+	PEY(y1, gc->width);
+	PEX(x2, gc->width);
+	PEY(y2, gc->width);
+}
+
+static void extents_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	PEX(cx, radius);
+	PEY(cy, radius);
+}
+
+static void extents_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	for (i = 0; i < n_coords; i++) {
+		PEX(x[i], 0);
+		PEY(y[i], 0);
+	}
+}
+
+static void extents_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	PEX(x1, 0);
+	PEY(y1, 0);
+	PEX(x2, 0);
+	PEY(y2, 0);
+}
+
+static HID extents_hid;
+
+void hid_extents_init(void)
+{
+	static pcb_bool initialised = pcb_false;
+
+	if (initialised)
+		return;
+
+	memset(&extents_hid, 0, sizeof(HID));
+
+	common_draw_helpers_init(&extents_hid);
+
+	extents_hid.struct_size = sizeof(HID);
+	extents_hid.name = "extents-extents";
+	extents_hid.description = "used to calculate extents";
+	extents_hid.poly_before = 1;
+
+	extents_hid.set_layer = extents_set_layer;
+	extents_hid.make_gc = extents_make_gc;
+	extents_hid.destroy_gc = extents_destroy_gc;
+	extents_hid.use_mask = extents_use_mask;
+	extents_hid.set_color = extents_set_color;
+	extents_hid.set_line_cap = extents_set_line_cap;
+	extents_hid.set_line_width = extents_set_line_width;
+	extents_hid.set_draw_xor = extents_set_draw_xor;
+	extents_hid.draw_line = extents_draw_line;
+	extents_hid.draw_arc = extents_draw_arc;
+	extents_hid.draw_rect = extents_draw_rect;
+	extents_hid.fill_circle = extents_fill_circle;
+	extents_hid.fill_polygon = extents_fill_polygon;
+	extents_hid.fill_rect = extents_fill_rect;
+
+	initialised = pcb_true;
+}
+
+BoxType *hid_get_extents(void *item)
+{
+	BoxType region;
+
+	/* As this isn't a real "HID", we need to ensure we are initialised. */
+	hid_extents_init();
+
+	box.X1 = MAXINT;
+	box.Y1 = MAXINT;
+	box.X2 = -MAXINT;
+	box.Y2 = -MAXINT;
+
+	region.X1 = -MAXINT;
+	region.Y1 = -MAXINT;
+	region.X2 = MAXINT;
+	region.Y2 = MAXINT;
+	hid_expose_callback(&extents_hid, &region, item);
+
+	return &box;
+}
diff --git a/src/hid_extents.h b/src/hid_extents.h
new file mode 100644
index 0000000..51acdf1
--- /dev/null
+++ b/src/hid_extents.h
@@ -0,0 +1,12 @@
+#ifndef PCB_HID_EXTENTS_H
+#define PCB_HID_EXTENTS_H
+
+/* HID internal interfaces.  These may ONLY be called from the HID
+   modules, not from the common PCB code.  */
+
+/* Convenience function that calls the expose callback for the item,
+   and returns the extents of what was drawn.  */
+BoxType *hid_get_extents(void *item);
+
+
+#endif
diff --git a/src/hid_flags.c b/src/hid_flags.c
new file mode 100644
index 0000000..bf09b42
--- /dev/null
+++ b/src/hid_flags.c
@@ -0,0 +1,104 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "data.h"
+#include "misc.h"
+#include "conf.h"
+
+#include "hid.h"
+#include "hid_flags.h"
+#include "genht/hash.h"
+#include "genht/htsp.h"
+#include "error.h"
+#include "hid_actions.h"
+
+int hid_get_flag(const char *name)
+{
+	const char *cp;
+
+	if (name == NULL)
+		return -1;
+
+	cp = strchr(name, '(');
+	if (cp == NULL) {
+		conf_native_t *n = conf_get_field(name);
+		if (n == NULL)
+			return -1;
+		if ((n->type != CFN_BOOLEAN) || (n->used != 1))
+			return -1;
+		return n->val.boolean[0];
+	}
+	else {
+		const char *end, *s;
+		const char *argv[2];
+		if (cp != NULL) {
+			const HID_Action *a;
+			char buff[256];
+			int len, multiarg;
+			len = cp - name;
+			if (len > sizeof(buff)-1) {
+				Message(PCB_MSG_DEFAULT, "hid_get_flag: action name too long: %s()\n", name);
+				return -1;
+			}
+			memcpy(buff, name, len);
+			buff[len] = '\0';
+			a = hid_find_action(buff);
+			if (!a) {
+				Message(PCB_MSG_DEFAULT, "hid_get_flag: no action %s\n", name);
+				return -1;
+			}
+			cp++;
+			len = strlen(cp);
+			end = NULL;
+			multiarg = 0;
+			for(s = cp; *s != '\0'; s++) {
+				if (*s == ')') {
+					end = s;
+					break;
+				}
+				if (*s == ',')
+					multiarg = 1;
+			}
+			if (!multiarg) {
+				/* faster but limited way for a single arg */
+				if ((len > sizeof(buff)-1) || (end == NULL)) {
+					Message(PCB_MSG_DEFAULT, "hid_get_flag: action arg too long or unterminated: %s\n", name);
+					return -1;
+				}
+				len = end - cp;
+				memcpy(buff, cp, len);
+				buff[len] = '\0';
+				argv[0] = buff;
+				argv[1] = NULL;
+				return hid_actionv_(a, len > 0, argv);
+			}
+			else {
+				/* slower but more generic way */
+				return hid_parse_command(name);
+			}
+		}
+		else {
+			fprintf(stderr, "ERROR: hid_get_flag(%s) - not a path or an action\n", name);
+		}
+	}
+	return -1;
+}
+
+void hid_save_and_show_layer_ons(int *save_array)
+{
+	int i;
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		save_array[i] = PCB->Data->Layer[i].On;
+		PCB->Data->Layer[i].On = 1;
+	}
+}
+
+void hid_restore_layer_ons(int *save_array)
+{
+	int i;
+	for (i = 0; i < max_copper_layer + 2; i++)
+		PCB->Data->Layer[i].On = save_array[i];
+}
diff --git a/src/hid_flags.h b/src/hid_flags.h
new file mode 100644
index 0000000..e6b288f
--- /dev/null
+++ b/src/hid_flags.h
@@ -0,0 +1,18 @@
+#ifndef PCB_HID_FLAGS_H
+#define  PCB_HID_FLAGS_H
+
+/* Use this to temporarily enable all layers, so that they can be
+   exported even if they're not currently visible.  save_array must be
+   MAX_LAYER+2 big. */
+void hid_save_and_show_layer_ons(int *save_array);
+/* Use this to restore them.  */
+void hid_restore_layer_ons(int *save_array);
+
+
+/* Looks up an integer (usually boolean) value by conf path or by running
+   an action (if name has a parenthesis). When an action is run, it has 0
+   or 1 argument only and the return value of the action is returned.
+   On error, returns -1. */
+int hid_get_flag(const char *name);
+
+#endif
diff --git a/src/hid_helper.c b/src/hid_helper.c
new file mode 100644
index 0000000..ea69c76
--- /dev/null
+++ b/src/hid_helper.c
@@ -0,0 +1,125 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 2004 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include "data.h"
+#include "misc.h"
+#include "layer.h"
+#include "hid_helper.h"
+#include "hid_attrib.h"
+#include "compat_misc.h"
+
+const char *layer_type_to_file_name(int idx, int style)
+{
+	int group;
+	int nlayers;
+	const char *single_name;
+
+	switch (idx) {
+	case SL(SILK, TOP):
+		return "topsilk";
+	case SL(SILK, BOTTOM):
+		return "bottomsilk";
+	case SL(MASK, TOP):
+		return "topmask";
+	case SL(MASK, BOTTOM):
+		return "bottommask";
+	case SL(PDRILL, 0):
+		return "plated-drill";
+	case SL(UDRILL, 0):
+		return "unplated-drill";
+	case SL(PASTE, TOP):
+		return "toppaste";
+	case SL(PASTE, BOTTOM):
+		return "bottompaste";
+	case SL(INVISIBLE, 0):
+		return "invisible";
+	case SL(FAB, 0):
+		return "fab";
+	case SL(ASSY, TOP):
+		return "topassembly";
+	case SL(ASSY, BOTTOM):
+		return "bottomassembly";
+	default:
+		group = GetLayerGroupNumberByNumber(idx);
+		nlayers = PCB->LayerGroups.Number[group];
+		single_name = PCB->Data->Layer[idx].Name;
+		if (group == GetLayerGroupNumberByNumber(component_silk_layer)) {
+			if (style == FNS_first || (style == FNS_single && nlayers == 2))
+				return single_name;
+			return "top";
+		}
+		else if (group == GetLayerGroupNumberByNumber(solder_silk_layer)) {
+			if (style == FNS_first || (style == FNS_single && nlayers == 2))
+				return single_name;
+			return "bottom";
+		}
+		else if (nlayers == 1
+						 && (strcmp(PCB->Data->Layer[idx].Name, "route") == 0 || strcmp(PCB->Data->Layer[idx].Name, "outline") == 0)) {
+			return "outline";
+		}
+		else {
+			static char buf[20];
+			if (style == FNS_first || (style == FNS_single && nlayers == 1))
+				return single_name;
+			sprintf(buf, "group%d", group);
+			return buf;
+		}
+		break;
+	}
+}
+
+void derive_default_filename(const char *pcbfile, HID_Attribute * filename_attrib, const char *suffix, char **memory)
+{
+	char *buf;
+	char *pf;
+
+	if (pcbfile == NULL)
+		pf = pcb_strdup("unknown.pcb");
+	else
+		pf = pcb_strdup(pcbfile);
+
+	if (!pf || (memory && filename_attrib->default_val.str_value != *memory))
+		return;
+
+	buf = (char *) malloc(strlen(pf) + strlen(suffix) + 1);
+	if (memory)
+		*memory = buf;
+	if (buf) {
+		size_t bl;
+		strcpy(buf, pf);
+		bl = strlen(buf);
+		if (bl > 4 && strcmp(buf + bl - 4, ".pcb") == 0)
+			buf[bl - 4] = 0;
+		strcat(buf, suffix);
+		if (filename_attrib->default_val.str_value)
+			free((void *) filename_attrib->default_val.str_value);
+		filename_attrib->default_val.str_value = buf;
+	}
+
+	free(pf);
+}
diff --git a/src/hid_helper.h b/src/hid_helper.h
new file mode 100644
index 0000000..45e7e79
--- /dev/null
+++ b/src/hid_helper.h
@@ -0,0 +1,19 @@
+#ifndef PCB_HID_HELPER_H
+#define PCB_HID_HELPER_H
+
+enum File_Name_Style {
+	/* Files for copper layers are named top, groupN, bottom.  */
+	FNS_fixed,
+	/* Groups with multiple layers are named as above, else the single
+	   layer name is used.  */
+	FNS_single,
+	/* The name of the first layer in each group is used.  */
+	FNS_first
+};
+
+/* Returns a filename base that can be used to output the layer.  */
+const char *layer_type_to_file_name(int idx, int style);
+
+void derive_default_filename(const char *pcbfile, HID_Attribute * filename_attrib, const char *suffix, char **memory);
+
+#endif
diff --git a/src/hid_init.c b/src/hid_init.c
new file mode 100644
index 0000000..7747dd7
--- /dev/null
+++ b/src/hid_init.c
@@ -0,0 +1,228 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+#include "hid.h"
+#include "hid_nogui.h"
+
+/* for dlopen() and friends; will also solve all system-dependent includes
+   and provides a dl-compat layer on windows. Also solves the opendir related
+   includes. */
+#include "compat_dl.h"
+
+#include "global.h"
+#include "misc.h"
+#include "plugins.h"
+#include "hid_attrib.h"
+#include "hid_flags.h"
+#include "misc_util.h"
+#include "conf_core.h"
+#include "compat_misc.h"
+#include "compat_inc.h"
+#include "fptr_cast.h"
+
+HID **hid_list = 0;
+int hid_num_hids = 0;
+
+HID *gui = NULL;
+HID *next_gui = NULL;
+HID *exporter = NULL;
+
+int pixel_slop = 1;
+
+static void hid_load_dir(char *dirname)
+{
+	DIR *dir;
+	struct dirent *de;
+
+	dir = opendir(dirname);
+	if (!dir) {
+		free(dirname);
+		return;
+	}
+	while ((de = readdir(dir)) != NULL) {
+		void *sym;
+		pcb_uninit_t (*symv) ();
+		pcb_uninit_t uninit;
+		void *so;
+		char *basename, *path, *symname;
+		struct stat st;
+
+		basename = pcb_strdup(de->d_name);
+		if (strlen(basename) > 3 && strcasecmp(basename + strlen(basename) - 3, ".so") == 0)
+			basename[strlen(basename) - 3] = 0;
+		else if (strlen(basename) > 4 && strcasecmp(basename + strlen(basename) - 4, ".dll") == 0)
+			basename[strlen(basename) - 4] = 0;
+		path = Concat(dirname, PCB_DIR_SEPARATOR_S, de->d_name, NULL);
+
+		if (stat(path, &st) == 0 && (
+/* mingw and win32 do not support S_IXGRP or S_IXOTH */
+#if defined(S_IXGRP)
+																	(st.st_mode & S_IXGRP) ||
+#endif
+#if defined(S_IXOTH)
+																	(st.st_mode & S_IXOTH) ||
+#endif
+																	(st.st_mode & S_IXUSR))
+				&& S_ISREG(st.st_mode)) {
+			if ((so = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL) {
+				fprintf(stderr, "dl_error: %s\n", dlerror());
+			}
+			else {
+				plugin_info_t *inf = plugin_find(basename);
+				if (inf == NULL) {
+					symname = Concat("hid_", basename, "_init", NULL);
+					if ((sym = dlsym(so, symname)) != NULL) {
+						symv = (pcb_uninit_t (*)())pcb_cast_d2f(sym);
+						uninit = symv();
+					}
+					else if ((sym = dlsym(so, "pcb_plugin_init")) != NULL) {
+						symv = (pcb_uninit_t (*)()) pcb_cast_d2f(sym);
+						uninit = symv();
+					}
+					else
+						uninit = NULL;
+					plugin_register(basename, path, so, 1, uninit);
+					free(symname);
+				}
+				else
+					Message(PCB_MSG_ERROR, "Can't load %s because it'd provide plugin %s that is already loaded from %s\n", path, basename, (*inf->path == '<' ? "<buildin>" : inf->path));
+			}
+		}
+		free(basename);
+		free(path);
+	}
+	free(dirname);
+	closedir(dir);
+}
+
+void hid_init()
+{
+	hid_actions_init();
+
+	/* Setup a "nogui" default HID */
+	gui = hid_nogui_get_hid();
+
+#warning TODO: make this configurable
+	hid_load_dir(Concat(conf_core.rc.path.exec_prefix, PCB_DIR_SEPARATOR_S, "lib",
+											PCB_DIR_SEPARATOR_S, "pcb-rnd", PCB_DIR_SEPARATOR_S, "plugins", PCB_DIR_SEPARATOR_S, HOST, NULL));
+	hid_load_dir(Concat(conf_core.rc.path.exec_prefix, PCB_DIR_SEPARATOR_S, "lib",
+											PCB_DIR_SEPARATOR_S, "pcb-rnd", PCB_DIR_SEPARATOR_S, "plugins", NULL));
+
+	/* conf_core.rc.path.home is set by the conf_core immediately on startup */
+	if (conf_core.rc.path.home != NULL) {
+		hid_load_dir(Concat(conf_core.rc.path.home, PCB_DIR_SEPARATOR_S, DOT_PCB_RND, PCB_DIR_SEPARATOR_S, "plugins", PCB_DIR_SEPARATOR_S, HOST, NULL));
+		hid_load_dir(Concat(conf_core.rc.path.home, PCB_DIR_SEPARATOR_S, DOT_PCB_RND, PCB_DIR_SEPARATOR_S, "plugins", NULL));
+	}
+	hid_load_dir(Concat("plugins", PCB_DIR_SEPARATOR_S, HOST, NULL));
+	hid_load_dir(Concat("plugins", NULL));
+}
+
+void hid_uninit(void)
+{
+	if (hid_num_hids > 0) {
+		int i;
+		for (i = hid_num_hids-1; i >= 0; i--) {
+			if (hid_list[i]->uninit != NULL)
+				hid_list[i]->uninit(hid_list[i]);
+		}
+	}
+	free(hid_list);
+
+	hid_actions_uninit();
+	hid_attributes_uninit();
+}
+
+void hid_register_hid(HID * hid)
+{
+	int i;
+	int sz = (hid_num_hids + 2) * sizeof(HID *);
+
+	if (hid->struct_size != sizeof(HID)) {
+		fprintf(stderr, "Warning: hid \"%s\" has an incompatible ABI.\n", hid->name);
+		return;
+	}
+
+	for (i = 0; i < hid_num_hids; i++)
+		if (hid == hid_list[i])
+			return;
+
+	hid_num_hids++;
+	if (hid_list)
+		hid_list = (HID **) realloc(hid_list, sz);
+	else
+		hid_list = (HID **) malloc(sz);
+
+	hid_list[hid_num_hids - 1] = hid;
+	hid_list[hid_num_hids] = 0;
+}
+
+void hid_remove_hid(HID * hid)
+{
+	int i;
+
+	for (i = 0; i < hid_num_hids; i++) {
+		if (hid == hid_list[i]) {
+			hid_list[i] = hid_list[hid_num_hids - 1];
+			hid_list[hid_num_hids - 1] = 0;
+			hid_num_hids--;
+			return;
+		}
+	}
+}
+
+
+HID *hid_find_gui(const char *preference)
+{
+	int i;
+
+	if (preference != NULL) {
+		for (i = 0; i < hid_num_hids; i++)
+			if (!hid_list[i]->printer && !hid_list[i]->exporter && !strcmp(hid_list[i]->name, preference))
+				return hid_list[i];
+		return NULL;
+	}
+
+	for (i = 0; i < hid_num_hids; i++)
+		if (!hid_list[i]->printer && !hid_list[i]->exporter)
+			return hid_list[i];
+
+	fprintf(stderr, "Error: No GUI available.\n");
+	exit(1);
+}
+
+HID *hid_find_printer()
+{
+	int i;
+
+	for (i = 0; i < hid_num_hids; i++)
+		if (hid_list[i]->printer)
+			return hid_list[i];
+
+	return 0;
+}
+
+HID *hid_find_exporter(const char *which)
+{
+	int i;
+
+	for (i = 0; i < hid_num_hids; i++)
+		if (hid_list[i]->exporter && strcmp(which, hid_list[i]->name) == 0)
+			return hid_list[i];
+
+	fprintf(stderr, "Invalid exporter %s, available ones:", which);
+	for (i = 0; i < hid_num_hids; i++)
+		if (hid_list[i]->exporter)
+			fprintf(stderr, " %s", hid_list[i]->name);
+	fprintf(stderr, "\n");
+
+	return 0;
+}
+
+HID **hid_enumerate()
+{
+	return hid_list;
+}
diff --git a/src/hid_init.h b/src/hid_init.h
new file mode 100644
index 0000000..6c679de
--- /dev/null
+++ b/src/hid_init.h
@@ -0,0 +1,47 @@
+#ifndef PCB_HID_INIT_H
+#define PCB_HID_INIT_H
+
+/* NULL terminated list of all static HID structures.  Built by
+   hid_register_hid, used by hid_find_*() and hid_enumerate().  The
+   order in this list is the same as the order of hid_register_hid
+   calls.  */
+extern HID **hid_list;
+
+/* Count of entries in the above.  */
+extern int hid_num_hids;
+
+/* Call this as soon as possible from main().  No other HID calls are
+   valid until this is called.  */
+void hid_init(void);
+
+/* Call this at exit */
+void hid_uninit(void);
+
+/* When PCB runs in interactive mode, this is called to instantiate
+   one GUI HID which happens to be the GUI.  This HID is the one that
+   interacts with the mouse and keyboard.  */
+HID *hid_find_gui(const char *preference);
+
+/* Finds the one printer HID and instantiates it.  */
+HID *hid_find_printer(void);
+
+/* Finds the indicated exporter HID and instantiates it.  */
+HID *hid_find_exporter(const char *);
+
+/* This returns a NULL-terminated array of available HIDs.  The only
+   real reason to use this is to locate all the export-style HIDs. */
+HID **hid_enumerate(void);
+
+/* HID internal interfaces.  These may ONLY be called from the HID
+   modules, not from the common PCB code.  */
+
+/* A HID may use this if it does not need command line arguments in
+   any special format; for example, the Lesstif HID needs to use the
+   Xt parser, but the Postscript HID can use this function.  */
+void hid_parse_command_line(int *argc, char ***argv);
+
+/* Called by the init funcs, used to set up hid_list.  */
+extern void hid_register_hid(HID * hid);
+void hid_remove_hid(HID * hid);
+
+#endif
diff --git a/src/hid_nogui.c b/src/hid_nogui.c
new file mode 100644
index 0000000..b99b996
--- /dev/null
+++ b/src/hid_nogui.c
@@ -0,0 +1,467 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+#include "hid.h"
+#include "compat_misc.h"
+
+/* This is the "gui" that is installed at startup, and is used when
+   there is no other real GUI to use.  For the most part, it just
+   stops the application from (1) crashing randomly, and (2) doing
+   gui-specific things when it shouldn't.  */
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called GUI function %s without having a GUI available.\n", func); abort()
+
+typedef struct hid_gc_struct {
+	int nothing_interesting_here;
+} hid_gc_struct;
+
+static HID_Attribute *nogui_get_export_options(int *n_ret)
+{
+	CRASH("get_export_options");
+	return 0;
+}
+
+static void nogui_do_export(HID_Attr_Val * options)
+{
+	CRASH("do_export");
+}
+
+static void nogui_parse_arguments(int *argc, char ***argv)
+{
+	CRASH("parse_arguments");
+}
+
+static void nogui_invalidate_lr(int l, int r, int t, int b)
+{
+	CRASH("invalidate_lr");
+}
+
+static void nogui_invalidate_all(void)
+{
+	CRASH("invalidate_all");
+}
+
+static int nogui_set_layer(const char *name, int idx, int empty)
+{
+	CRASH("set_layer");
+	return 0;
+}
+
+static void nogui_end_layer(void)
+{
+}
+
+static hidGC nogui_make_gc(void)
+{
+	return 0;
+}
+
+static void nogui_destroy_gc(hidGC gc)
+{
+}
+
+static void nogui_use_mask(int use_it)
+{
+	CRASH("use_mask");
+}
+
+static void nogui_set_color(hidGC gc, const char *name)
+{
+	CRASH("set_color");
+}
+
+static void nogui_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	CRASH("set_line_cap");
+}
+
+static void nogui_set_line_width(hidGC gc, Coord width)
+{
+	CRASH("set_line_width");
+}
+
+static void nogui_set_draw_xor(hidGC gc, int xor_)
+{
+	CRASH("set_draw_xor");
+}
+
+static void nogui_set_draw_faded(hidGC gc, int faded)
+{
+}
+
+static void nogui_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	CRASH("draw_line");
+}
+
+static void nogui_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle end_angle)
+{
+	CRASH("draw_arc");
+}
+
+static void nogui_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	CRASH("draw_rect");
+}
+
+static void nogui_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	CRASH("fill_circle");
+}
+
+static void nogui_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	CRASH("fill_polygon");
+}
+
+static void nogui_fill_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box)
+{
+	CRASH("fill_pcb_polygon");
+}
+
+static void nogui_fill_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask)
+{
+	CRASH("fill_pcb_pad");
+}
+
+static void nogui_thindraw_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask)
+{
+	CRASH("thindraw_pcb_pad");
+}
+
+static void nogui_fill_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType * pad, pcb_bool drawHole, pcb_bool mask)
+{
+	CRASH("fill_pcb_pv");
+}
+
+static void nogui_thindraw_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType * pad, pcb_bool drawHole, pcb_bool mask)
+{
+	CRASH("thindraw_pcb_pv");
+}
+
+static void nogui_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	CRASH("fill_rect");
+}
+
+static void nogui_calibrate(double xval, double yval)
+{
+	CRASH("calibrate");
+}
+
+static int nogui_shift_is_pressed(void)
+{
+	/* This is called from FitCrosshairIntoGrid() when the board is loaded.  */
+	return 0;
+}
+
+static int nogui_control_is_pressed(void)
+{
+	CRASH("control_is_pressed");
+	return 0;
+}
+
+static int nogui_mod1_is_pressed(void)
+{
+	CRASH("mod1_is_pressed");
+	return 0;
+}
+
+static void nogui_get_coords(const char *msg, Coord * x, Coord * y)
+{
+	CRASH("get_coords");
+}
+
+static void nogui_set_crosshair(int x, int y, int action)
+{
+}
+
+static hidval nogui_add_timer(void (*func) (hidval user_data), unsigned long milliseconds, hidval user_data)
+{
+	hidval rv;
+	CRASH("add_timer");
+	rv.lval = 0;
+	return rv;
+}
+
+static void nogui_stop_timer(hidval timer)
+{
+	CRASH("stop_timer");
+}
+
+static hidval nogui_watch_file(int fd, unsigned int condition, void (*func) (hidval watch, int fd, unsigned int condition, hidval user_data), hidval user_data)
+{
+	hidval rv;
+	CRASH("watch_file");
+	rv.lval = 0;
+	return rv;
+}
+
+static void nogui_unwatch_file(hidval watch)
+{
+	CRASH("unwatch_file");
+}
+
+static hidval nogui_add_block_hook(void (*func) (hidval data), hidval data)
+{
+	hidval rv;
+	CRASH("add_block_hook");
+	rv.ptr = NULL;
+	return rv;
+}
+
+static void nogui_stop_block_hook(hidval block_hook)
+{
+	CRASH("stop_block_hook");
+}
+
+static void nogui_log(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	va_end(ap);
+}
+
+static void nogui_logv(enum pcb_message_level level, const char *fmt, va_list ap)
+{
+	switch (level) {
+	case PCB_MSG_DEBUG:
+		printf("D:");
+		break;
+	case PCB_MSG_INFO:
+		printf("I:");
+		break;
+	case PCB_MSG_WARNING:
+		printf("W:");
+		break;
+	case PCB_MSG_ERROR:
+		printf("E:");
+		break;
+	}
+	vprintf(fmt, ap);
+}
+
+/* Return a line of user input text, stripped of any newline characters.
+ * Returns NULL if the user simply presses enter, or otherwise gives no input.
+ */
+#define MAX_LINE_LENGTH 1024
+static char *read_stdin_line(void)
+{
+	static char buf[MAX_LINE_LENGTH];
+	char *s;
+	int i;
+
+	s = fgets(buf, MAX_LINE_LENGTH, stdin);
+	if (s == NULL) {
+		printf("\n");
+		return NULL;
+	}
+
+	/* Strip any trailing newline characters */
+	for (i = strlen(s) - 1; i >= 0; i--)
+		if (s[i] == '\r' || s[i] == '\n')
+			s[i] = '\0';
+
+	if (s[0] == '\0')
+		return NULL;
+
+	return pcb_strdup(s);
+}
+
+#undef MAX_LINE_LENGTH
+
+static int nogui_confirm_dialog(const char *msg, ...)
+{
+	char *answer;
+	int ret = 0;
+	pcb_bool valid_answer = pcb_false;
+	va_list args;
+
+	do {
+		va_start(args, msg);
+		vprintf(msg, args);
+		va_end(args);
+
+		printf(" ? 0=cancel 1 = ok : ");
+
+		answer = read_stdin_line();
+
+		if (answer == NULL)
+			continue;
+
+		if (answer[0] == '0' && answer[1] == '\0') {
+			ret = 0;
+			valid_answer = pcb_true;
+		}
+
+		if (answer[0] == '1' && answer[1] == '\0') {
+			ret = 1;
+			valid_answer = pcb_true;
+		}
+
+		free(answer);
+	}
+	while (!valid_answer);
+	return ret;
+}
+
+static int nogui_close_confirm_dialog()
+{
+	return nogui_confirm_dialog(_("OK to lose data ?"), NULL);
+}
+
+static void nogui_report_dialog(const char *title, const char *msg)
+{
+	printf("--- %s ---\n%s\n", title, msg);
+}
+
+static char *nogui_prompt_for(const char *msg, const char *default_string)
+{
+	char *answer;
+
+	if (default_string)
+		printf("%s [%s] : ", msg, default_string);
+	else
+		printf("%s : ", msg);
+
+	answer = read_stdin_line();
+	if (answer == NULL)
+		return pcb_strdup((default_string != NULL) ? default_string : "");
+	else
+		return pcb_strdup(answer);
+}
+
+/* FIXME - this could use some enhancement to actually use the other
+   args */
+static char *nogui_fileselect(const char *title, const char *descr,
+															const char *default_file, const char *default_ext, const char *history_tag, int flags)
+{
+	char *answer;
+
+	if (default_file)
+		printf("%s [%s] : ", title, default_file);
+	else
+		printf("%s : ", title);
+
+	answer = read_stdin_line();
+	if (answer == NULL)
+		return (default_file != NULL) ? pcb_strdup(default_file) : NULL;
+	else
+		return pcb_strdup(answer);
+}
+
+static int nogui_attribute_dialog(HID_Attribute * attrs, int n_attrs, HID_Attr_Val * results, const char *title, const char *descr)
+{
+	CRASH("attribute_dialog");
+}
+
+static void nogui_show_item(void *item)
+{
+	CRASH("show_item");
+}
+
+static void nogui_beep(void)
+{
+	putchar(7);
+	fflush(stdout);
+}
+
+static int nogui_progress(int so_far, int total, const char *message)
+{
+	return 0;
+}
+
+static HID *nogui_request_debug_draw(void)
+{
+	return NULL;
+}
+
+static void nogui_flush_debug_draw(void)
+{
+}
+
+static void nogui_finish_debug_draw(void)
+{
+}
+
+static void nogui_create_menu(const char *menu, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie)
+{
+}
+
+void common_nogui_init(HID * hid)
+{
+	hid->get_export_options = nogui_get_export_options;
+	hid->do_export = nogui_do_export;
+	hid->parse_arguments = nogui_parse_arguments;
+	hid->invalidate_lr = nogui_invalidate_lr;
+	hid->invalidate_all = nogui_invalidate_all;
+	hid->set_layer = nogui_set_layer;
+	hid->end_layer = nogui_end_layer;
+	hid->make_gc = nogui_make_gc;
+	hid->destroy_gc = nogui_destroy_gc;
+	hid->use_mask = nogui_use_mask;
+	hid->set_color = nogui_set_color;
+	hid->set_line_cap = nogui_set_line_cap;
+	hid->set_line_width = nogui_set_line_width;
+	hid->set_draw_xor = nogui_set_draw_xor;
+	hid->set_draw_faded = nogui_set_draw_faded;
+	hid->draw_line = nogui_draw_line;
+	hid->draw_arc = nogui_draw_arc;
+	hid->draw_rect = nogui_draw_rect;
+	hid->fill_circle = nogui_fill_circle;
+	hid->fill_polygon = nogui_fill_polygon;
+	hid->fill_pcb_polygon = nogui_fill_pcb_polygon;
+	hid->fill_pcb_pad = nogui_fill_pcb_pad;
+	hid->thindraw_pcb_pad = nogui_thindraw_pcb_pad;
+	hid->fill_pcb_pv = nogui_fill_pcb_pv;
+	hid->thindraw_pcb_pv = nogui_thindraw_pcb_pv;
+	hid->fill_rect = nogui_fill_rect;
+	hid->calibrate = nogui_calibrate;
+	hid->shift_is_pressed = nogui_shift_is_pressed;
+	hid->control_is_pressed = nogui_control_is_pressed;
+	hid->mod1_is_pressed = nogui_mod1_is_pressed;
+	hid->get_coords = nogui_get_coords;
+	hid->set_crosshair = nogui_set_crosshair;
+	hid->add_timer = nogui_add_timer;
+	hid->stop_timer = nogui_stop_timer;
+	hid->watch_file = nogui_watch_file;
+	hid->unwatch_file = nogui_unwatch_file;
+	hid->add_block_hook = nogui_add_block_hook;
+	hid->stop_block_hook = nogui_stop_block_hook;
+	hid->log = nogui_log;
+	hid->logv = nogui_logv;
+	hid->confirm_dialog = nogui_confirm_dialog;
+	hid->close_confirm_dialog = nogui_close_confirm_dialog;
+	hid->report_dialog = nogui_report_dialog;
+	hid->prompt_for = nogui_prompt_for;
+	hid->fileselect = nogui_fileselect;
+	hid->attribute_dialog = nogui_attribute_dialog;
+	hid->show_item = nogui_show_item;
+	hid->beep = nogui_beep;
+	hid->progress = nogui_progress;
+	hid->request_debug_draw = nogui_request_debug_draw;
+	hid->flush_debug_draw = nogui_flush_debug_draw;
+	hid->finish_debug_draw = nogui_finish_debug_draw;
+	hid->create_menu = nogui_create_menu;
+}
+
+static HID nogui_hid;
+
+HID *hid_nogui_get_hid(void)
+{
+	memset(&nogui_hid, 0, sizeof(HID));
+
+	nogui_hid.struct_size = sizeof(HID);
+	nogui_hid.name = "nogui";
+	nogui_hid.description = "Default GUI when no other GUI is present.  " "Does nothing.";
+
+	common_nogui_init(&nogui_hid);
+
+	return &nogui_hid;
+}
diff --git a/src/hid_nogui.h b/src/hid_nogui.h
new file mode 100644
index 0000000..eb4e916
--- /dev/null
+++ b/src/hid_nogui.h
@@ -0,0 +1,7 @@
+#ifndef PCB_HID_COMMON_HIDNOGUI_H
+#define PCB_HID_COMMON_HIDNOGUI_H
+
+void common_nogui_init(HID * hid);
+HID *hid_nogui_get_hid(void);
+
+#endif
diff --git a/src/ht_element.c b/src/ht_element.c
new file mode 100644
index 0000000..c77ac15
--- /dev/null
+++ b/src/ht_element.c
@@ -0,0 +1,216 @@
+#include "global.h"
+#include "list_element.h"
+
+#define HT(x) htep_ ## x
+#include <genht/ht.c>
+#undef HT
+
+
+#warning TODO: move these in the big split
+
+/* compare two strings and return 0 if they are equal. NULL == NULL means equal. */
+static int neqs(const char *s1, const char *s2)
+{
+	if ((s1 == NULL) && (s2 == NULL)) return 0;
+	if ((s1 == NULL) || (s2 == NULL)) return 1;
+	return strcmp(s1, s2) != 0;
+}
+
+/* compare two fields and return 0 if they are equal */
+#define neq(s1, s2, f) ((s1)->f != (s2)->f)
+
+#define eoffs(e,ef, s,sf) ((e == NULL) ? (s)->sf : ((s)->sf) - ((e)->ef))
+#define neqox(e1, x1, e2, x2, f) (eoffs(e1, MarkX, x1, f) != eoffs(e2, MarkX, x2, f))
+#define neqoy(e1, y1, e2, y2, f) (eoffs(e1, MarkY, y1, f) != eoffs(e2, MarkY, y2, f))
+
+static inline unsigned h_coord(Coord c)
+{
+	return murmurhash(&(c), sizeof(Coord));
+}
+
+#define h_coordox(e, c) ((e) == NULL ? h_coord(c) : h_coord(c - e->MarkX))
+#define h_coordoy(e, c) ((e) == NULL ? h_coord(c) : h_coord(c - e->MarkY))
+
+#define h_str(s) ((s) == NULL ? 0 : strhash(s))
+
+int pcb_pin_eq(const ElementType *e1, const PinType *p1, const ElementType *e2, const PinType *p2)
+{
+	if (neq(p1, p2, Thickness) || neq(p1, p2, Clearance)) return 0;
+	if (neq(p1, p2, Mask) || neq(p1, p2, DrillingHole)) return 0;
+	if (neqox(e1, p1, e2, p2, X) || neqoy(e1, p1, e2, p2, Y)) return 0;
+	if (neqs(p1->Name, p2->Name)) return 0;
+	if (neqs(p1->Number, p2->Number)) return 0;
+	return 1;
+}
+
+unsigned int pcb_pin_hash(const ElementType *e, const PinType *p)
+{
+	return
+		h_coord(p->Thickness) ^ h_coord(p->Clearance) ^
+		h_coord(p->Mask) ^ h_coord(p->DrillingHole) ^
+		h_coordox(e, p->X) ^ h_coordoy(e, p->Y) ^
+		h_str(p->Name) ^ h_str(p->Number);
+}
+
+int pcb_pad_eq(const ElementType *e1, const PadType *p1, const ElementType *e2, const PadType *p2)
+{
+	if (neq(p1, p2, Thickness) || neq(p1, p2, Clearance)) return 0;
+	if (neqox(e1, p1, e2, p2, Point1.X) || neqoy(e1, p1, e2, p2, Point1.Y)) return 0;
+	if (neqox(e1, p1, e2, p2, Point2.X) || neqoy(e1, p1, e2, p2, Point2.Y)) return 0;
+	if (neq(p1, p2, Mask)) return 0;
+	if (neqs(p1->Name, p2->Name)) return 0;
+	if (neqs(p1->Number, p2->Number)) return 0;
+	return 1;
+}
+
+unsigned int pcb_pad_hash(const ElementType *e, const PadType *p)
+{
+	return
+		h_coord(p->Thickness) ^ h_coord(p->Clearance) ^
+		h_coordox(e, p->Point1.X) ^ h_coordoy(e, p->Point1.Y) ^
+		h_coordox(e, p->Point2.X) ^ h_coordoy(e, p->Point2.Y) ^
+		h_coord(p->Mask) ^
+		h_str(p->Name) ^ h_str(p->Number);
+}
+
+
+int pcb_line_eq(const ElementType *e1, const LineType *l1, const ElementType *e2, const LineType *l2)
+{
+	if (neq(l1, l2, Thickness) || neq(l1, l2, Clearance)) return 0;
+	if (neqox(e1, l1, e2, l2, Point1.X) || neqoy(e1, l1, e2, l2, Point1.Y)) return 0;
+	if (neqox(e1, l1, e2, l2, Point2.X) || neqoy(e1, l1, e2, l2, Point2.Y)) return 0;
+	if (neqs(l1->Number, l2->Number)) return 0;
+	return 1;
+}
+
+
+unsigned int pcb_line_hash(const ElementType *e, const LineType *l)
+{
+	return
+		h_coord(l->Thickness) ^ h_coord(l->Clearance) ^
+		h_coordox(e, l->Point1.X) ^ h_coordoy(e, l->Point1.Y) ^
+		h_coordox(e, l->Point2.X) ^ h_coordoy(e, l->Point2.Y) ^
+		h_str(l->Number);
+}
+
+int pcb_arc_eq(const ElementType *e1, const ArcType *a1, const ElementType *e2, const ArcType *a2)
+{
+	if (neq(a1, a2, Thickness) || neq(a1, a2, Clearance)) return 0;
+	if (neq(a1, a2, Width) || neq(a1, a2, Height)) return 0;
+	if (neqox(e1, a1, e2, a2, X) || neqoy(e1, a1, e2, a2, Y)) return 0;
+	if (neq(a1, a2, StartAngle) || neq(a1, a2, Delta)) return 0;
+
+	return 1;
+}
+
+unsigned int pcb_arc_hash(const ElementType *e, const ArcType *a)
+{
+	return 
+		h_coord(a->Thickness) ^ h_coord(a->Clearance) ^
+		h_coord(a->Width) ^ h_coord(a->Height) ^
+		h_coordox(e, a->X) ^ h_coordoy(e, a->Y) ^
+		h_coord(a->StartAngle) ^ h_coord(a->Delta);
+}
+
+#undef h_coord
+#undef neq
+
+unsigned int pcb_element_hash(const ElementType *e)
+{
+	unsigned int val = 0;
+	gdl_iterator_t it;
+
+	{
+		PinType *p;
+		pinlist_foreach(&e->Pin, &it, p) {
+			val ^= pcb_pin_hash(e, p);
+		}
+	}
+
+	{
+		PadType *p;
+		padlist_foreach(&e->Pad, &it, p) {
+			val ^= pcb_pad_hash(e, p);
+		}
+	}
+
+	{
+		LineType *l;
+		linelist_foreach(&e->Line, &it, l) {
+			val ^= pcb_line_hash(e, l);
+		}
+	}
+
+	{
+		ArcType *a;
+		linelist_foreach(&e->Arc, &it, a) {
+			val ^= pcb_arc_hash(e, a);
+		}
+	}
+
+	return val;
+}
+
+int pcb_element_eq(const ElementType *e1, const ElementType *e2)
+{
+	/* Require the same objects in the same order - bail out at the first mismatch */
+
+	{
+		PinType *p1, *p2;
+		p1 = pinlist_first((pinlist_t *)&e1->Pin);
+		p2 = pinlist_first((pinlist_t *)&e2->Pin);
+		for(;;) {
+			if ((p1 == NULL) && (p2 == NULL))
+				break;
+			if (!pcb_pin_eq(e1, p1, e2, p2))
+				return 0;
+			p1 = pinlist_next(p1);
+			p2 = pinlist_next(p2);
+		}
+	}
+
+	{
+		PadType *p1, *p2;
+		p1 = padlist_first((padlist_t *)&e1->Pad);
+		p2 = padlist_first((padlist_t *)&e2->Pad);
+		for(;;) {
+			if ((p1 == NULL) && (p2 == NULL))
+				break;
+			if (!pcb_pad_eq(e1, p1, e2, p2))
+				return 0;
+			p1 = padlist_next(p1);
+			p2 = padlist_next(p2);
+		}
+	}
+
+	{
+		LineType *l1, *l2;
+		l1 = linelist_first((linelist_t *)&e1->Line);
+		l2 = linelist_first((linelist_t *)&e2->Line);
+		for(;;) {
+			if ((l1 == NULL) && (l2 == NULL))
+				break;
+			if (!pcb_line_eq(e1, l1, e2, l2))
+				return 0;
+			l1 = linelist_next(l1);
+			l2 = linelist_next(l2);
+		}
+	}
+
+	{
+		ArcType *a1, *a2;
+		a1 = arclist_first((arclist_t *)&e1->Arc);
+		a2 = arclist_first((arclist_t *)&e2->Arc);
+		for(;;) {
+			if ((a1 == NULL) && (a2 == NULL))
+				break;
+			if (!pcb_arc_eq(e1, a1, e2, a2))
+				return 0;
+			a1 = arclist_next(a1);
+			a2 = arclist_next(a2);
+		}
+	}
+
+	return 1;
+}
+
diff --git a/src/ht_element.h b/src/ht_element.h
new file mode 100644
index 0000000..2d7b8db
--- /dev/null
+++ b/src/ht_element.h
@@ -0,0 +1,11 @@
+#ifndef PCB_HT_ELEMENT_H
+#define PCB_HT_ELEMENT_H
+
+/* hash instance */
+typedef const ElementType *htep_key_t;
+typedef int htep_value_t;
+#define HT(x) htep_ ## x
+#include <genht/ht.h>
+#undef HT
+
+#endif
diff --git a/src/icons/Makefile.am b/src/icons/Makefile.am
new file mode 100644
index 0000000..23cc5a4
--- /dev/null
+++ b/src/icons/Makefile.am
@@ -0,0 +1,4 @@
+## $Id$
+
+EXTRA_DIST= hand.dat hcurs.dat lcurs.dat lock.dat
+
diff --git a/src/icons/hand.dat b/src/icons/hand.dat
new file mode 100644
index 0000000..74d7b41
--- /dev/null
+++ b/src/icons/hand.dat
@@ -0,0 +1,9 @@
+#define hand_width 21
+#define hand_height 21
+static unsigned char hand_bits[] = {
+   0x00, 0x02, 0x00, 0x20, 0x25, 0x00, 0x50, 0x55, 0x02, 0x50, 0x55, 0x05,
+   0x50, 0x55, 0x05, 0xd3, 0xd4, 0x06, 0x95, 0x48, 0x02, 0x19, 0x40, 0x01,
+   0x12, 0x00, 0x01, 0x04, 0x80, 0x00, 0x08, 0x80, 0x00, 0x10, 0x80, 0x00,
+   0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x38, 0x26, 0x02, 0x48, 0x69, 0x02,
+   0x48, 0xe9, 0x02, 0x38, 0xaf, 0x02, 0x08, 0xa9, 0x03, 0x08, 0x29, 0x03,
+   0x08, 0x29, 0x02};
diff --git a/src/icons/hcurs.dat b/src/icons/hcurs.dat
new file mode 100644
index 0000000..06652c5
--- /dev/null
+++ b/src/icons/hcurs.dat
@@ -0,0 +1,8 @@
+#define hcurs_width 16
+#define hcurs_height 16
+#define hcurs_x_hot 7
+#define hcurs_y_hot 9
+static unsigned char hcurs_bits[] = {
+   0x80, 0x00, 0xc8, 0x09, 0xdc, 0x1d, 0xdc, 0x1d, 0xdc, 0x1d, 0xfc, 0xcf,
+   0x38, 0xee, 0x1f, 0x78, 0x0f, 0x78, 0x07, 0x30, 0x07, 0x30, 0x0e, 0x38,
+   0x1e, 0x3c, 0x3c, 0x1e, 0xf8, 0x0f, 0xf0, 0x0f};
diff --git a/src/icons/lcurs.dat b/src/icons/lcurs.dat
new file mode 100644
index 0000000..c498f9d
--- /dev/null
+++ b/src/icons/lcurs.dat
@@ -0,0 +1,6 @@
+#define lcurs_width 16
+#define lcurs_height 16
+static unsigned char lcurs_bits[] = {
+   0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0x38, 0x1c, 0x1c, 0x3c, 0x1c, 0x38,
+   0x1c, 0x30, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f,
+   0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f};
diff --git a/src/icons/lock.dat b/src/icons/lock.dat
new file mode 100644
index 0000000..928d42b
--- /dev/null
+++ b/src/icons/lock.dat
@@ -0,0 +1,9 @@
+#define lock_width 21
+#define lock_height 21
+static unsigned char lock_bits[] = {
+   0x00, 0x1f, 0x00, 0x80, 0x31, 0x00, 0x80, 0x20, 0x00, 0xc0, 0x60, 0x00,
+   0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xf0, 0xff, 0x01, 0x10, 0x00, 0x01,
+   0xf0, 0xff, 0x01, 0x10, 0x00, 0x01, 0xf0, 0xff, 0x01, 0x10, 0x00, 0x01,
+   0xf0, 0xff, 0x01, 0x00, 0x00, 0x00, 0x84, 0xb1, 0x04, 0x44, 0x8a, 0x04,
+   0x44, 0x8a, 0x02, 0x44, 0x8a, 0x01, 0x44, 0x8a, 0x02, 0x44, 0x8a, 0x04,
+   0x9c, 0xb1, 0x04};
diff --git a/src/insert.c b/src/insert.c
new file mode 100644
index 0000000..fe0f91c
--- /dev/null
+++ b/src/insert.c
@@ -0,0 +1,278 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* functions used to insert points into objects
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "create.h"
+#include "crosshair.h"
+#include "data.h"
+#include "draw.h"
+#include "line.h"
+#include "misc.h"
+#include "polygon.h"
+#include "rtree.h"
+#include "search.h"
+#include "select.h"
+#include "set.h"
+#include "undo.h"
+#include "misc_util.h"
+#include "layer.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *InsertPointIntoLine(LayerTypePtr, LineTypePtr);
+static void *InsertPointIntoPolygon(LayerTypePtr, PolygonTypePtr);
+static void *InsertPointIntoRat(RatTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static Coord InsertX, InsertY;	/* used by local routines as offset */
+static pcb_cardinal_t InsertAt;
+static pcb_bool InsertLast;
+static pcb_bool Forcible;
+static ObjectFunctionType InsertFunctions = {
+	InsertPointIntoLine,
+	NULL,
+	InsertPointIntoPolygon,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	InsertPointIntoRat
+};
+
+/* ---------------------------------------------------------------------------
+ * inserts a point into a rat-line
+ */
+static void *InsertPointIntoRat(RatTypePtr Rat)
+{
+	LineTypePtr newone;
+
+	newone = CreateDrawnLineOnLayer(CURRENT, Rat->Point1.X, Rat->Point1.Y,
+																	InsertX, InsertY, conf_core.design.line_thickness, 2 * conf_core.design.clearance, Rat->Flags);
+	if (!newone)
+		return newone;
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, CURRENT, newone, newone);
+	EraseRat(Rat);
+	DrawLine(CURRENT, newone);
+	newone = CreateDrawnLineOnLayer(CURRENT, Rat->Point2.X, Rat->Point2.Y,
+																	InsertX, InsertY, conf_core.design.line_thickness, 2 * conf_core.design.clearance, Rat->Flags);
+	if (newone) {
+		AddObjectToCreateUndoList(PCB_TYPE_LINE, CURRENT, newone, newone);
+		DrawLine(CURRENT, newone);
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_RATLINE, Rat, Rat, Rat);
+	Draw();
+	return (newone);
+}
+
+/* ---------------------------------------------------------------------------
+ * inserts a point into a line
+ */
+static void *InsertPointIntoLine(LayerTypePtr Layer, LineTypePtr Line)
+{
+	LineTypePtr line;
+	Coord X, Y;
+
+	if (((Line->Point1.X == InsertX) && (Line->Point1.Y == InsertY)) ||
+			((Line->Point2.X == InsertX) && (Line->Point2.Y == InsertY)))
+		return (NULL);
+	X = Line->Point2.X;
+	Y = Line->Point2.Y;
+	AddObjectToMoveUndoList(PCB_TYPE_LINE_POINT, Layer, Line, &Line->Point2, InsertX - X, InsertY - Y);
+	EraseLine(Line);
+	r_delete_entry(Layer->line_tree, (BoxTypePtr) Line);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	Line->Point2.X = InsertX;
+	Line->Point2.Y = InsertY;
+	SetLineBoundingBox(Line);
+	r_insert_entry(Layer->line_tree, (BoxTypePtr) Line, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	DrawLine(Layer, Line);
+	/* we must create after playing with Line since creation may
+	 * invalidate the line pointer
+	 */
+	if ((line = CreateDrawnLineOnLayer(Layer, InsertX, InsertY, X, Y, Line->Thickness, Line->Clearance, Line->Flags))) {
+		AddObjectToCreateUndoList(PCB_TYPE_LINE, Layer, line, line);
+		DrawLine(Layer, line);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, line);
+		/* creation call adds it to the rtree */
+	}
+	Draw();
+	return (line);
+}
+
+/* ---------------------------------------------------------------------------
+ * inserts a point into a polygon
+ */
+static void *InsertPointIntoPolygon(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	PointType save;
+	pcb_cardinal_t n;
+	LineType line;
+
+	if (!Forcible) {
+		/*
+		 * first make sure adding the point is sensible
+		 */
+		line.Thickness = 0;
+		line.Point1 = Polygon->Points[prev_contour_point(Polygon, InsertAt)];
+		line.Point2 = Polygon->Points[InsertAt];
+		if (IsPointOnLine((float) InsertX, (float) InsertY, 0.0, &line))
+			return (NULL);
+	}
+	/*
+	 * second, shift the points up to make room for the new point
+	 */
+	ErasePolygon(Polygon);
+	r_delete_entry(Layer->polygon_tree, (BoxTypePtr) Polygon);
+	save = *CreateNewPointInPolygon(Polygon, InsertX, InsertY);
+	for (n = Polygon->PointN - 1; n > InsertAt; n--)
+		Polygon->Points[n] = Polygon->Points[n - 1];
+
+	/* Shift up indices of any holes */
+	for (n = 0; n < Polygon->HoleIndexN; n++)
+		if (Polygon->HoleIndex[n] > InsertAt || (InsertLast && Polygon->HoleIndex[n] == InsertAt))
+			Polygon->HoleIndex[n]++;
+
+	Polygon->Points[InsertAt] = save;
+	SetChangedFlag(pcb_true);
+	AddObjectToInsertPointUndoList(PCB_TYPE_POLYGON_POINT, Layer, Polygon, &Polygon->Points[InsertAt]);
+
+	SetPolygonBoundingBox(Polygon);
+	r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+	InitClip(PCB->Data, Layer, Polygon);
+	if (Forcible || !RemoveExcessPolygonPoints(Layer, Polygon)) {
+		DrawPolygon(Layer, Polygon);
+		Draw();
+	}
+	return (&Polygon->Points[InsertAt]);
+}
+
+/* ---------------------------------------------------------------------------
+ * inserts point into objects
+ */
+void *InsertPointIntoObject(int Type, void *Ptr1, void *Ptr2, pcb_cardinal_t * Ptr3, Coord DX, Coord DY, pcb_bool Force, pcb_bool insert_last)
+{
+	void *ptr;
+
+	/* setup offset */
+	InsertX = DX;
+	InsertY = DY;
+	InsertAt = *Ptr3;
+	InsertLast = insert_last;
+	Forcible = Force;
+
+	/* the operation insert the points to the undo-list */
+	ptr = ObjectOperation(&InsertFunctions, Type, Ptr1, Ptr2, Ptr3);
+	if (ptr != NULL)
+		IncrementUndoSerialNumber();
+	return (ptr);
+}
+
+/* ---------------------------------------------------------------------------
+ *  adjusts the insert point to make 45 degree lines as necessary
+ */
+PointTypePtr AdjustInsertPoint(void)
+{
+	static PointType InsertedPoint;
+	double m;
+	Coord x, y, m1, m2;
+	LineTypePtr line = (LineTypePtr) Crosshair.AttachedObject.Ptr2;
+
+	if (Crosshair.AttachedObject.State == STATE_FIRST)
+		return NULL;
+	Crosshair.AttachedObject.Ptr3 = &InsertedPoint;
+	if (gui->shift_is_pressed()) {
+		AttachedLineType myline;
+		/* only force 45 degree for nearest point */
+		if (Distance(Crosshair.X, Crosshair.Y, line->Point1.X, line->Point1.Y) <
+				Distance(Crosshair.X, Crosshair.Y, line->Point2.X, line->Point2.Y))
+			myline.Point1 = myline.Point2 = line->Point1;
+		else
+			myline.Point1 = myline.Point2 = line->Point2;
+		FortyFiveLine(&myline);
+		InsertedPoint.X = myline.Point2.X;
+		InsertedPoint.Y = myline.Point2.Y;
+		return &InsertedPoint;
+	}
+	if (conf_core.editor.all_direction_lines) {
+		InsertedPoint.X = Crosshair.X;
+		InsertedPoint.Y = Crosshair.Y;
+		return &InsertedPoint;
+	}
+	if (Crosshair.X == line->Point1.X)
+		m1 = 2;											/* 2 signals infinite slope */
+	else {
+		m = (double) (Crosshair.X - line->Point1.X) / (Crosshair.Y - line->Point1.Y);
+		m1 = 0;
+		if (m > PCB_TAN_30_DEGREE)
+			m1 = (m > PCB_TAN_60_DEGREE) ? 2 : 1;
+		else if (m < -PCB_TAN_30_DEGREE)
+			m1 = (m < -PCB_TAN_60_DEGREE) ? 2 : -1;
+	}
+	if (Crosshair.X == line->Point2.X)
+		m2 = 2;											/* 2 signals infinite slope */
+	else {
+		m = (double) (Crosshair.X - line->Point1.X) / (Crosshair.Y - line->Point1.Y);
+		m2 = 0;
+		if (m > PCB_TAN_30_DEGREE)
+			m2 = (m > PCB_TAN_60_DEGREE) ? 2 : 1;
+		else if (m < -PCB_TAN_30_DEGREE)
+			m2 = (m < -PCB_TAN_60_DEGREE) ? 2 : -1;
+	}
+	if (m1 == m2) {
+		InsertedPoint.X = line->Point1.X;
+		InsertedPoint.Y = line->Point1.Y;
+		return &InsertedPoint;
+	}
+	if (m1 == 2) {
+		x = line->Point1.X;
+		y = line->Point2.Y + m2 * (line->Point1.X - line->Point2.X);
+	}
+	else if (m2 == 2) {
+		x = line->Point2.X;
+		y = line->Point1.Y + m1 * (line->Point2.X - line->Point1.X);
+	}
+	else {
+		x = (line->Point2.Y - line->Point1.Y + m1 * line->Point1.X - m2 * line->Point2.X) / (m1 - m2);
+		y = (m1 * line->Point2.Y - m1 * m2 * line->Point2.X - m2 * line->Point1.Y + m1 * m2 * line->Point1.X) / (m1 - m2);
+	}
+	InsertedPoint.X = x;
+	InsertedPoint.Y = y;
+	return &InsertedPoint;
+}
diff --git a/src/insert.h b/src/insert.h
new file mode 100644
index 0000000..d92799b
--- /dev/null
+++ b/src/insert.h
@@ -0,0 +1,42 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for inserting points into objects */
+
+#ifndef	PCB_INSERT_H
+#define	PCB_INSERT_H
+
+#include "global.h"
+
+#define	INSERT_TYPES	(PCB_TYPE_POLYGON | PCB_TYPE_LINE | PCB_TYPE_RATLINE)
+
+/* ---------------------------------------------------------------------------
+ * prototypes
+ */
+void *InsertPointIntoObject(int, void *, void *, pcb_cardinal_t *, Coord, Coord, pcb_bool, pcb_bool);
+PointTypePtr AdjustInsertPoint(void);
+
+#endif
diff --git a/src/intersect.c b/src/intersect.c
new file mode 100644
index 0000000..4c0bf08
--- /dev/null
+++ b/src/intersect.c
@@ -0,0 +1,267 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, intersect.c, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* rectangle intersection/union routines. */
+
+#include "config.h"
+
+#include <assert.h>
+
+#include "intersect.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static int compareleft(const void *ptr1, const void *ptr2);
+static int compareright(const void *ptr1, const void *ptr2);
+static int comparepos(const void *ptr1, const void *ptr2);
+static int nextpwrof2(int i);
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+typedef struct {
+	Coord left, right;
+	int covered;
+	Coord area;
+} SegmentTreeNode;
+
+typedef struct {
+	SegmentTreeNode *nodes;
+	int size;
+} SegmentTree;
+
+typedef struct {
+	Coord *p;
+	int size;
+} LocationList;
+
+/* ---------------------------------------------------------------------------
+ * Create a sorted list of unique y coords from a BoxList.
+ */
+static LocationList createSortedYList(BoxListTypePtr boxlist)
+{
+	LocationList yCoords;
+	Coord last;
+	int i, n;
+	/* create sorted list of Y coordinates */
+	yCoords.size = 2 * boxlist->BoxN;
+	yCoords.p = (Coord *) calloc(yCoords.size, sizeof(*yCoords.p));
+	for (i = 0; i < boxlist->BoxN; i++) {
+		yCoords.p[2 * i] = boxlist->Box[i].Y1;
+		yCoords.p[2 * i + 1] = boxlist->Box[i].Y2;
+	}
+	qsort(yCoords.p, yCoords.size, sizeof(*yCoords.p), comparepos);
+	/* count uniq y coords */
+	last = 0;
+	for (n = 0, i = 0; i < yCoords.size; i++)
+		if (i == 0 || yCoords.p[i] != last)
+			yCoords.p[n++] = last = yCoords.p[i];
+	yCoords.size = n;
+	return yCoords;
+}
+
+/* ---------------------------------------------------------------------------
+ * Create an empty segment tree from the given sorted list of uniq y coords.
+ */
+static SegmentTree createSegmentTree(Coord * yCoords, int N)
+{
+	SegmentTree st;
+	int i;
+	/* size is twice the nearest larger power of 2 */
+	st.size = 2 * nextpwrof2(N);
+	st.nodes = (SegmentTreeNode *) calloc(st.size, sizeof(*st.nodes));
+	/* initialize the rightmost leaf node */
+	st.nodes[st.size - 1].left = (N > 0) ? yCoords[--N] : 10;
+	st.nodes[st.size - 1].right = st.nodes[st.size - 1].left + 1;
+	/* initialize the rest of the leaf nodes */
+	for (i = st.size - 2; i >= st.size / 2; i--) {
+		st.nodes[i].right = st.nodes[i + 1].left;
+		st.nodes[i].left = (N > 0) ? yCoords[--N] : st.nodes[i].right - 1;
+	}
+	/* initialize the internal nodes */
+	for (; i > 0; i--) {					/* node 0 is not used */
+		st.nodes[i].right = st.nodes[2 * i + 1].right;
+		st.nodes[i].left = st.nodes[2 * i].left;
+	}
+	/* done! */
+	return st;
+}
+
+void insertSegment(SegmentTree * st, int n, Coord Y1, Coord Y2)
+{
+	Coord discriminant;
+	if (st->nodes[n].left >= Y1 && st->nodes[n].right <= Y2) {
+		st->nodes[n].covered++;
+	}
+	else {
+		assert(n < st->size / 2);
+		discriminant = st->nodes[n * 2 + 1 /*right */ ].left;
+		if (Y1 < discriminant)
+			insertSegment(st, n * 2, Y1, Y2);
+		if (discriminant < Y2)
+			insertSegment(st, n * 2 + 1, Y1, Y2);
+	}
+	/* fixup area */
+	st->nodes[n].area = (st->nodes[n].covered > 0) ?
+		(st->nodes[n].right - st->nodes[n].left) : (n >= st->size / 2) ? 0 : st->nodes[n * 2].area + st->nodes[n * 2 + 1].area;
+}
+
+void deleteSegment(SegmentTree * st, int n, Coord Y1, Coord Y2)
+{
+	Coord discriminant;
+	if (st->nodes[n].left >= Y1 && st->nodes[n].right <= Y2) {
+		assert(st->nodes[n].covered);
+		--st->nodes[n].covered;
+	}
+	else {
+		assert(n < st->size / 2);
+		discriminant = st->nodes[n * 2 + 1 /*right */ ].left;
+		if (Y1 < discriminant)
+			deleteSegment(st, n * 2, Y1, Y2);
+		if (discriminant < Y2)
+			deleteSegment(st, n * 2 + 1, Y1, Y2);
+	}
+	/* fixup area */
+	st->nodes[n].area = (st->nodes[n].covered > 0) ?
+		(st->nodes[n].right - st->nodes[n].left) : (n >= st->size / 2) ? 0 : st->nodes[n * 2].area + st->nodes[n * 2 + 1].area;
+}
+
+/* ---------------------------------------------------------------------------
+ * Compute the area of the intersection of the given rectangles; that is,
+ * the area covered by more than one rectangle (counted twice if the area is
+ * covered by three rectangles, three times if covered by four rectangles,
+ * etc.).
+ * Runs in O(N ln N) time.
+ */
+double ComputeIntersectionArea(BoxListTypePtr boxlist)
+{
+	pcb_cardinal_t i;
+	double area = 0.0;
+	/* first get the aggregate area. */
+	for (i = 0; i < boxlist->BoxN; i++)
+		area += (double) (boxlist->Box[i].X2 - boxlist->Box[i].X1) * (double) (boxlist->Box[i].Y2 - boxlist->Box[i].Y1);
+	/* intersection area is aggregate - union. */
+	return area * 0.0001 - ComputeUnionArea(boxlist);
+}
+
+/* ---------------------------------------------------------------------------
+ * Compute the area of the union of the given rectangles.
+ * O(N ln N) time.
+ */
+double ComputeUnionArea(BoxListTypePtr boxlist)
+{
+	BoxTypePtr *rectLeft, *rectRight;
+	pcb_cardinal_t i, j;
+	LocationList yCoords;
+	SegmentTree segtree;
+	Coord lastX;
+	double area = 0.0;
+
+	if (boxlist->BoxN == 0)
+		return 0.0;
+	/* create sorted list of Y coordinates */
+	yCoords = createSortedYList(boxlist);
+	/* now create empty segment tree */
+	segtree = createSegmentTree(yCoords.p, yCoords.size);
+	free(yCoords.p);
+	/* create sorted list of left and right X coordinates of rectangles */
+	rectLeft = (BoxTypePtr *) calloc(boxlist->BoxN, sizeof(*rectLeft));
+	rectRight = (BoxTypePtr *) calloc(boxlist->BoxN, sizeof(*rectRight));
+	for (i = 0; i < boxlist->BoxN; i++) {
+		assert(boxlist->Box[i].X1 <= boxlist->Box[i].X2);
+		assert(boxlist->Box[i].Y1 <= boxlist->Box[i].Y2);
+		rectLeft[i] = rectRight[i] = &boxlist->Box[i];
+	}
+	qsort(rectLeft, boxlist->BoxN, sizeof(*rectLeft), compareleft);
+	qsort(rectRight, boxlist->BoxN, sizeof(*rectRight), compareright);
+	/* sweep through x segments from left to right */
+	i = j = 0;
+	lastX = rectLeft[0]->X1;
+	while (j < boxlist->BoxN) {
+		assert(i <= boxlist->BoxN);
+		/* i will step through rectLeft, j will through rectRight */
+		if (i == boxlist->BoxN || rectRight[j]->X2 < rectLeft[i]->X1) {
+			/* right edge of rectangle */
+			BoxTypePtr b = rectRight[j++];
+			/* check lastX */
+			if (b->X2 != lastX) {
+				assert(lastX < b->X2);
+				area += (double) (b->X2 - lastX) * segtree.nodes[1].area;
+				lastX = b->X2;
+			}
+			/* remove a segment from the segment tree. */
+			deleteSegment(&segtree, 1, b->Y1, b->Y2);
+		}
+		else {
+			/* left edge of rectangle */
+			BoxTypePtr b = rectLeft[i++];
+			/* check lastX */
+			if (b->X1 != lastX) {
+				assert(lastX < b->X1);
+				area += (double) (b->X1 - lastX) * segtree.nodes[1].area;
+				lastX = b->X1;
+			}
+			/* add a segment from the segment tree. */
+			insertSegment(&segtree, 1, b->Y1, b->Y2);
+		}
+	}
+	free(rectLeft);
+	free(rectRight);
+	free(segtree.nodes);
+	return area * 0.0001;
+}
+
+static int compareleft(const void *ptr1, const void *ptr2)
+{
+	BoxTypePtr *b1 = (BoxTypePtr *) ptr1, *b2 = (BoxTypePtr *) ptr2;
+	return (*b1)->X1 - (*b2)->X1;
+}
+
+static int compareright(const void *ptr1, const void *ptr2)
+{
+	BoxTypePtr *b1 = (BoxTypePtr *) ptr1, *b2 = (BoxTypePtr *) ptr2;
+	return (*b1)->X2 - (*b2)->X2;
+}
+
+static int comparepos(const void *ptr1, const void *ptr2)
+{
+	return *((Coord *) ptr1) - *((Coord *) ptr2);
+}
+
+static int nextpwrof2(int n)
+{
+	int r = 1;
+	while (n != 0) {
+		n /= 2;
+		r *= 2;
+	}
+	return r;
+}
diff --git a/src/intersect.h b/src/intersect.h
new file mode 100644
index 0000000..f13665d
--- /dev/null
+++ b/src/intersect.h
@@ -0,0 +1,43 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, intersect.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* prototypes for rectangle intersection/union routines.
+ */
+
+#ifndef PCB_INTERSECT_H
+#define PCB_INTERSECT_H
+
+#include "global.h"
+
+double ComputeIntersectionArea(BoxListTypePtr boxlist);	/* will sort boxlist */
+double ComputeUnionArea(BoxListTypePtr boxlist);
+
+#endif
diff --git a/src/layer.c b/src/layer.c
new file mode 100644
index 0000000..96077a3
--- /dev/null
+++ b/src/layer.c
@@ -0,0 +1,831 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas (pcb-rnd extensions)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "global.h"
+#include "data.h"
+#include "conf_core.h"
+#include "layer.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+
+/*
+ * Used by SaveStackAndVisibility() and
+ * RestoreStackAndVisibility()
+ */
+
+static struct {
+	pcb_bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
+	int LayerStack[MAX_LAYER];
+	pcb_bool LayerOn[MAX_LAYER];
+	int cnt;
+} SavedStack;
+
+
+pcb_bool IsLayerEmpty(LayerTypePtr layer)
+{
+	return LAYER_IS_EMPTY(layer);
+}
+
+pcb_bool IsLayerNumEmpty(int num)
+{
+	return IsLayerEmpty(PCB->Data->Layer + num);
+}
+
+pcb_bool IsLayerGroupEmpty(int num)
+{
+	int i;
+	for (i = 0; i < PCB->LayerGroups.Number[num]; i++)
+		if (!IsLayerNumEmpty(PCB->LayerGroups.Entries[num][i]))
+			return pcb_false;
+	return pcb_true;
+}
+
+/* ----------------------------------------------------------------------
+ * parses the group definition string which is a colon separated list of
+ * comma separated layer numbers (1,2,b:4,6,8,t)
+ */
+int ParseGroupString(const char *s, LayerGroupTypePtr LayerGroup, int LayerN)
+{
+	int group, member, layer;
+	pcb_bool c_set = pcb_false,						/* flags for the two special layers to */
+		s_set = pcb_false;							/* provide a default setting for old formats */
+	int groupnum[MAX_LAYER + 2];
+
+	/* clear struct */
+	memset(LayerGroup, 0, sizeof(LayerGroupType));
+
+	/* Clear assignments */
+	for (layer = 0; layer < MAX_LAYER + 2; layer++)
+		groupnum[layer] = -1;
+
+	/* loop over all groups */
+	for (group = 0; s && *s && group < LayerN; group++) {
+		while (*s && isspace((int) *s))
+			s++;
+
+		/* loop over all group members */
+		for (member = 0; *s; s++) {
+			/* ignore white spaces and get layernumber */
+			while (*s && isspace((int) *s))
+				s++;
+			switch (*s) {
+			case 'c':
+			case 'C':
+			case 't':
+			case 'T':
+				layer = LayerN + COMPONENT_LAYER;
+				c_set = pcb_true;
+				break;
+
+			case 's':
+			case 'S':
+			case 'b':
+			case 'B':
+				layer = LayerN + SOLDER_LAYER;
+				s_set = pcb_true;
+				break;
+
+			default:
+				if (!isdigit((int) *s))
+					goto error;
+				layer = atoi(s) - 1;
+				break;
+			}
+			if (layer > LayerN + MAX(SOLDER_LAYER, COMPONENT_LAYER) || member >= LayerN + 1)
+				goto error;
+			groupnum[layer] = group;
+			LayerGroup->Entries[group][member++] = layer;
+			while (*++s && isdigit((int) *s));
+
+			/* ignore white spaces and check for separator */
+			while (*s && isspace((int) *s))
+				s++;
+			if (!*s || *s == ':')
+				break;
+			if (*s != ',')
+				goto error;
+		}
+		LayerGroup->Number[group] = member;
+		if (*s == ':')
+			s++;
+	}
+	if (!s_set)
+		LayerGroup->Entries[SOLDER_LAYER][LayerGroup->Number[SOLDER_LAYER]++] = LayerN + SOLDER_LAYER;
+	if (!c_set)
+		LayerGroup->Entries[COMPONENT_LAYER][LayerGroup->Number[COMPONENT_LAYER]++] = LayerN + COMPONENT_LAYER;
+
+	for (layer = 0; layer < LayerN && group < LayerN; layer++)
+		if (groupnum[layer] == -1) {
+			LayerGroup->Entries[group][0] = layer;
+			LayerGroup->Number[group] = 1;
+			group++;
+		}
+	return (0);
+
+	/* reset structure on error */
+error:
+	memset(LayerGroup, 0, sizeof(LayerGroupType));
+	return (1);
+}
+
+
+
+/* ---------------------------------------------------------------------------
+ * returns the layer number for the passed pointer
+ */
+int GetLayerNumber(DataTypePtr Data, LayerTypePtr Layer)
+{
+	int i;
+
+	for (i = 0; i < MAX_LAYER + 2; i++)
+		if (Layer == &Data->Layer[i])
+			break;
+	return (i);
+}
+
+/* ---------------------------------------------------------------------------
+ * move layer (number is passed in) to top of layerstack
+ */
+static void PushOnTopOfLayerStack(int NewTop)
+{
+	int i;
+
+	/* ignore silk layers */
+	if (NewTop < max_copper_layer) {
+		/* first find position of passed one */
+		for (i = 0; i < max_copper_layer; i++)
+			if (LayerStack[i] == NewTop)
+				break;
+
+		/* bring this element to the top of the stack */
+		for (; i; i--)
+			LayerStack[i] = LayerStack[i - 1];
+		LayerStack[0] = NewTop;
+	}
+}
+
+
+/* ----------------------------------------------------------------------
+ * changes the visibility of all layers in a group
+ * returns the number of changed layers
+ */
+int ChangeGroupVisibility(int Layer, pcb_bool On, pcb_bool ChangeStackOrder)
+{
+	int group, i, changed = 1;		/* at least the current layer changes */
+
+	/* Warning: these special case values must agree with what gui-top-window.c
+	   |  thinks the are.
+	 */
+
+	if (conf_core.rc.verbose)
+		printf("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n", Layer, On, ChangeStackOrder);
+
+	/* decrement 'i' to keep stack in order of layergroup */
+	if ((group = GetGroupOfLayer(Layer)) < max_group)
+		for (i = PCB->LayerGroups.Number[group]; i;) {
+			int layer = PCB->LayerGroups.Entries[group][--i];
+
+			/* don't count the passed member of the group */
+			if (layer != Layer && layer < max_copper_layer) {
+				PCB->Data->Layer[layer].On = On;
+
+				/* push layer on top of stack if switched on */
+				if (On && ChangeStackOrder)
+					PushOnTopOfLayerStack(layer);
+				changed++;
+			}
+		}
+
+	/* change at least the passed layer */
+	PCB->Data->Layer[Layer].On = On;
+	if (On && ChangeStackOrder)
+		PushOnTopOfLayerStack(Layer);
+
+	/* update control panel and exit */
+	hid_action("LayersChanged");
+	return (changed);
+}
+
+/* ----------------------------------------------------------------------
+ * Given a string description of a layer stack, adjust the layer stack
+ * to correspond.
+*/
+
+void LayerStringToLayerStack(const char *layer_string)
+{
+	static int listed_layers = 0;
+	int l = strlen(layer_string);
+	char **args;
+	int i, argn, lno;
+	int prev_sep = 1;
+	char *s;
+
+	s = pcb_strdup(layer_string);
+	args = (char **) malloc(l * sizeof(char *));
+	argn = 0;
+
+	for (i = 0; i < l; i++) {
+		switch (s[i]) {
+		case ' ':
+		case '\t':
+		case ',':
+		case ';':
+		case ':':
+			prev_sep = 1;
+			s[i] = '\0';
+			break;
+		default:
+			if (prev_sep)
+				args[argn++] = s + i;
+			prev_sep = 0;
+			break;
+		}
+	}
+
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		if (i < max_copper_layer)
+			LayerStack[i] = i;
+		PCB->Data->Layer[i].On = pcb_false;
+	}
+	PCB->ElementOn = pcb_false;
+	PCB->InvisibleObjectsOn = pcb_false;
+	PCB->PinOn = pcb_false;
+	PCB->ViaOn = pcb_false;
+	PCB->RatOn = pcb_false;
+
+	conf_set_editor(show_mask, 0);
+	conf_set_editor(show_solder_side, 0);
+
+	for (i = argn - 1; i >= 0; i--) {
+		if (strcasecmp(args[i], "rats") == 0)
+			PCB->RatOn = pcb_true;
+		else if (strcasecmp(args[i], "invisible") == 0)
+			PCB->InvisibleObjectsOn = pcb_true;
+		else if (strcasecmp(args[i], "pins") == 0)
+			PCB->PinOn = pcb_true;
+		else if (strcasecmp(args[i], "vias") == 0)
+			PCB->ViaOn = pcb_true;
+		else if (strcasecmp(args[i], "elements") == 0 || strcasecmp(args[i], "silk") == 0)
+			PCB->ElementOn = pcb_true;
+		else if (strcasecmp(args[i], "mask") == 0)
+			conf_set_editor(show_mask, 1);
+		else if (strcasecmp(args[i], "solderside") == 0)
+			conf_set_editor(show_solder_side, 1);
+		else if (isdigit((int) args[i][0])) {
+			lno = atoi(args[i]);
+			ChangeGroupVisibility(lno, pcb_true, pcb_true);
+		}
+		else {
+			int found = 0;
+			for (lno = 0; lno < max_copper_layer; lno++)
+				if (strcasecmp(args[i], PCB->Data->Layer[lno].Name) == 0) {
+					ChangeGroupVisibility(lno, pcb_true, pcb_true);
+					found = 1;
+					break;
+				}
+			if (!found) {
+				fprintf(stderr, "Warning: layer \"%s\" not known\n", args[i]);
+				if (!listed_layers) {
+					fprintf(stderr, "Named layers in this board are:\n");
+					listed_layers = 1;
+					for (lno = 0; lno < max_copper_layer; lno++)
+						fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
+					fprintf(stderr, "Also: component, solder, rats, invisible, pins, vias, elements or silk, mask, solderside.\n");
+				}
+			}
+		}
+	}
+}
+
+/* ----------------------------------------------------------------------
+ * lookup the group to which a layer belongs to
+ * returns max_group if no group is found, or is
+ * passed Layer is equal to max_copper_layer
+ */
+int GetGroupOfLayer(int Layer)
+{
+	int group, i;
+
+	if (Layer == max_copper_layer)
+		return max_group;
+	for (group = 0; group < max_group; group++)
+		for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
+			if (PCB->LayerGroups.Entries[group][i] == Layer)
+				return (group);
+	return max_group;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * returns the layergroup number for the passed pointer
+ */
+int GetLayerGroupNumberByPointer(LayerTypePtr Layer)
+{
+	return (GetLayerGroupNumberByNumber(GetLayerNumber(PCB->Data, Layer)));
+}
+
+/* ---------------------------------------------------------------------------
+ * returns the layergroup number for the passed layernumber
+ */
+int GetLayerGroupNumberByNumber(pcb_cardinal_t Layer)
+{
+	int group, entry;
+
+	for (group = 0; group < max_group; group++)
+		for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
+			if (PCB->LayerGroups.Entries[group][entry] == Layer)
+				return (group);
+
+	/* since every layer belongs to a group it is safe to return
+	 * the value without boundary checking
+	 */
+	return (group);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * resets the layerstack setting
+ */
+void ResetStackAndVisibility(void)
+{
+	int comp_group;
+	pcb_cardinal_t i;
+
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		if (i < max_copper_layer)
+			LayerStack[i] = i;
+		PCB->Data->Layer[i].On = pcb_true;
+	}
+	PCB->ElementOn = pcb_true;
+	PCB->InvisibleObjectsOn = pcb_true;
+	PCB->PinOn = pcb_true;
+	PCB->ViaOn = pcb_true;
+	PCB->RatOn = pcb_true;
+
+	/* Bring the component group to the front and make it active.  */
+	comp_group = GetLayerGroupNumberByNumber(component_silk_layer);
+	ChangeGroupVisibility(PCB->LayerGroups.Entries[comp_group][0], 1, 1);
+}
+
+/* ---------------------------------------------------------------------------
+ * saves the layerstack setting
+ */
+void SaveStackAndVisibility(void)
+{
+	pcb_cardinal_t i;
+	static pcb_bool run = pcb_false;
+
+	if (run == pcb_false) {
+		SavedStack.cnt = 0;
+		run = pcb_true;
+	}
+
+	if (SavedStack.cnt != 0) {
+		fprintf(stderr,
+						"SaveStackAndVisibility()  layerstack was already saved and not" "yet restored.  cnt = %d\n", SavedStack.cnt);
+	}
+
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		if (i < max_copper_layer)
+			SavedStack.LayerStack[i] = LayerStack[i];
+		SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
+	}
+	SavedStack.ElementOn = PCB->ElementOn;
+	SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
+	SavedStack.PinOn = PCB->PinOn;
+	SavedStack.ViaOn = PCB->ViaOn;
+	SavedStack.RatOn = PCB->RatOn;
+	SavedStack.cnt++;
+}
+
+/* ---------------------------------------------------------------------------
+ * restores the layerstack setting
+ */
+void RestoreStackAndVisibility(void)
+{
+	pcb_cardinal_t i;
+
+	if (SavedStack.cnt == 0) {
+		fprintf(stderr, "RestoreStackAndVisibility()  layerstack has not" " been saved.  cnt = %d\n", SavedStack.cnt);
+		return;
+	}
+	else if (SavedStack.cnt != 1) {
+		fprintf(stderr, "RestoreStackAndVisibility()  layerstack save count is" " wrong.  cnt = %d\n", SavedStack.cnt);
+	}
+
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		if (i < max_copper_layer)
+			LayerStack[i] = SavedStack.LayerStack[i];
+		PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
+	}
+	PCB->ElementOn = SavedStack.ElementOn;
+	PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
+	PCB->PinOn = SavedStack.PinOn;
+	PCB->ViaOn = SavedStack.ViaOn;
+	PCB->RatOn = SavedStack.RatOn;
+
+	SavedStack.cnt--;
+}
+
+/***********************************************************************
+ * Layer Group Functions
+ */
+
+int MoveLayerToGroup(int layer, int group)
+{
+	int prev, i, j;
+
+	if (layer < 0 || layer > max_copper_layer + 1)
+		return -1;
+	prev = GetLayerGroupNumberByNumber(layer);
+	if ((layer == solder_silk_layer && group == GetLayerGroupNumberByNumber(component_silk_layer))
+			|| (layer == component_silk_layer && group == GetLayerGroupNumberByNumber(solder_silk_layer))
+			|| (group < 0 || group >= max_group) || (prev == group))
+		return prev;
+
+	/* Remove layer from prev group */
+	for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
+		if (PCB->LayerGroups.Entries[prev][i] != layer)
+			PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
+	PCB->LayerGroups.Number[prev]--;
+
+	/* Add layer to new group.  */
+	i = PCB->LayerGroups.Number[group]++;
+	PCB->LayerGroups.Entries[group][i] = layer;
+
+	return group;
+}
+
+char *LayerGroupsToString(LayerGroupTypePtr lg)
+{
+#if MAX_LAYER < 9998
+	/* Allows for layer numbers 0..9999 */
+	static char buf[(MAX_LAYER + 2) * 5 + 1];
+#endif
+	char *cp = buf;
+	char sep = 0;
+	int group, entry;
+	for (group = 0; group < max_group; group++)
+		if (PCB->LayerGroups.Number[group]) {
+			if (sep)
+				*cp++ = ':';
+			sep = 1;
+			for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) {
+				int layer = PCB->LayerGroups.Entries[group][entry];
+				if (layer == component_silk_layer) {
+					*cp++ = 'c';
+				}
+				else if (layer == solder_silk_layer) {
+					*cp++ = 's';
+				}
+				else {
+					sprintf(cp, "%d", layer + 1);
+					while (*++cp);
+				}
+				if (entry != PCB->LayerGroups.Number[group] - 1)
+					*cp++ = ',';
+			}
+		}
+	*cp++ = 0;
+	return buf;
+}
+
+unsigned int pcb_layer_flags(int layer_idx)
+{
+	unsigned int res = 0;
+
+	if (layer_idx == solder_silk_layer)
+		return PCB_LYT_SILK | PCB_LYT_BOTTOM;
+
+	if (layer_idx == component_silk_layer)
+		return PCB_LYT_SILK | PCB_LYT_TOP;
+
+	if (layer_idx > max_copper_layer+2)
+		return 0;
+
+	if (layer_idx < max_copper_layer) {
+		if (!LAYER_IS_OUTLINE(layer_idx)) {
+			/* check whether it's top, bottom or internal */
+			int group, entry;
+			for (group = 0; group < max_group; group++) {
+				if (PCB->LayerGroups.Number[group]) {
+					unsigned int my_group = 0, gf = 0;
+					for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) {
+						int layer = PCB->LayerGroups.Entries[group][entry];
+						if (layer == layer_idx)
+							my_group = 1;
+						if (layer == component_silk_layer)
+							gf |= PCB_LYT_TOP;
+						else if (layer == solder_silk_layer)
+							gf |= PCB_LYT_BOTTOM;
+					}
+					if (my_group) {
+						res |= gf;
+						if (gf == 0)
+							res |= PCB_LYT_INTERN;
+						break; /* stop searching groups */
+					}
+				}
+			}
+			res |= PCB_LYT_COPPER;
+		}
+		else
+			res |= PCB_LYT_OUTLINE;
+	}
+
+	return res;
+}
+
+#define APPEND(n) \
+	do { \
+		if (res != NULL) { \
+			if (used < res_len) { \
+				res[used] = n; \
+				used++; \
+			} \
+		} \
+		else \
+			used++; \
+	} while(0)
+
+int pcb_layer_list(pcb_layer_type_t mask, int *res, int res_len)
+{
+	int n, used = 0;
+
+	for (n = 0; n < MAX_LAYER + 2; n++) {
+		if ((pcb_layer_flags(n) & mask) == mask)
+			APPEND(n);
+	}
+	return used;
+}
+
+int pcb_layer_group_list(pcb_layer_type_t mask, int *res, int res_len)
+{
+	int group, layeri, used = 0;
+	for (group = 0; group < max_group; group++) {
+		for (layeri = 0; layeri < PCB->LayerGroups.Number[group]; layeri++) {
+			int layer = PCB->LayerGroups.Entries[group][layeri];
+			if ((pcb_layer_flags(layer) & mask) == mask) {
+				APPEND(group);
+				goto added; /* do not add a group twice */
+			}
+		}
+		added:;
+	}
+	return used;
+}
+
+int pcb_layer_by_name(const char *name)
+{
+	int n;
+	for (n = 0; n < max_copper_layer + 2; n++)
+		if (strcmp(PCB->Data->Layer[n].Name, name) == 0)
+			return n;
+	return -1;
+}
+
+int pcb_layer_lookup_group(int layer_id)
+{
+	int group, layeri;
+	for (group = 0; group < max_group; group++) {
+		for (layeri = 0; layeri < PCB->LayerGroups.Number[group]; layeri++) {
+			int layer = PCB->LayerGroups.Entries[group][layeri];
+			if (layer == layer_id)
+				return group;
+		}
+	}
+	return -1;
+}
+
+void pcb_layer_add_in_group(int layer_id, int group_id)
+{
+	int glen = PCB->LayerGroups.Number[group_id];
+	PCB->LayerGroups.Entries[group_id][glen] = layer_id;
+	PCB->LayerGroups.Number[group_id]++;
+}
+
+
+void pcb_layers_reset()
+{
+	int n;
+
+	/* reset layer names */
+	for(n = 2; n < MAX_LAYER; n++) {
+		if (PCB->Data->Layer[n].Name != NULL)
+			free((char *)PCB->Data->Layer[n].Name);
+		PCB->Data->Layer[n].Name = pcb_strdup("<pcb_layers_reset>");
+	}
+
+	/* reset layer groups */
+	for(n = 0; n < MAX_LAYER; n++)
+		PCB->LayerGroups.Number[n] = 0;
+
+	/* set up one copper layer on top and one on bottom */
+	PCB->Data->LayerN = 2;
+	PCB->LayerGroups.Number[SOLDER_LAYER] = 1;
+	PCB->LayerGroups.Number[COMPONENT_LAYER] = 1;
+	PCB->LayerGroups.Entries[SOLDER_LAYER][0] = SOLDER_LAYER;
+	PCB->LayerGroups.Entries[COMPONENT_LAYER][0] = COMPONENT_LAYER;
+
+	/* Name top and bottom layers */
+	if (PCB->Data->Layer[COMPONENT_LAYER].Name != NULL)
+		free((char *)PCB->Data->Layer[COMPONENT_LAYER].Name);
+	PCB->Data->Layer[COMPONENT_LAYER].Name = pcb_strdup("<top>");
+
+	if (PCB->Data->Layer[SOLDER_LAYER].Name != NULL)
+		free((char *)PCB->Data->Layer[SOLDER_LAYER].Name);
+	PCB->Data->Layer[SOLDER_LAYER].Name = pcb_strdup("<bottom>");
+}
+
+int pcb_layer_create(pcb_layer_type_t type, pcb_bool reuse_layer, pcb_bool_t reuse_group, const char *lname)
+{
+	int id, grp = -1, found;
+	unsigned int loc  = type & PCB_LYT_ANYWHERE;
+	unsigned int role = type & PCB_LYT_ANYTHING;
+
+	/* look for an existing layer if reuse is enabled */
+	if (reuse_layer) {
+		switch(role) {
+			case PCB_LYT_MASK:
+			case PCB_LYT_PASTE:
+			case PCB_LYT_SILK:
+				return -1; /* do not create silk, paste or mask layers, they are special */
+
+			case PCB_LYT_COPPER:
+				switch(loc) {
+					case PCB_LYT_TOP:    return COMPONENT_LAYER;
+					case PCB_LYT_BOTTOM: return SOLDER_LAYER;
+					case PCB_LYT_INTERN:
+						for(grp = 2; grp < MAX_LAYER; grp++) {
+							if (PCB->LayerGroups.Number[grp] > 0) {
+								id = PCB->LayerGroups.Entries[grp][0];
+								if (strcmp(PCB->Data->Layer[id].Name, "outline") != 0)
+									return id;
+							}
+						}
+						return -1;
+				}
+				break;
+
+			case PCB_LYT_OUTLINE:
+				for(grp = 2; grp < MAX_LAYER; grp++) {
+					if (PCB->LayerGroups.Number[grp] > 0) {
+						id = PCB->LayerGroups.Entries[grp][0];
+						if (strcmp(PCB->Data->Layer[id].Name, "outline") == 0)
+							return id;
+					}
+				}
+				return -1;
+		}
+		return -1;
+	}
+
+	/* Need to create a new layers, look for an existing group first */
+	if (role == PCB_LYT_OUTLINE) {
+		lname = "outline";
+
+		for(id = 0; id < PCB->Data->LayerN; id++)
+			if (strcmp(PCB->Data->Layer[id].Name, lname) == 0)
+				return id; /* force reuse outline */
+
+		/* not found: create a new layer for outline */
+		grp = -1;
+		reuse_group = pcb_false;
+	}
+
+	/* there's only one top and bottom group, always reuse them */
+	if (role == PCB_LYT_COPPER) {
+		switch(loc) {
+			case PCB_LYT_TOP:    grp = COMPONENT_LAYER; reuse_group = 0; break;
+			case PCB_LYT_BOTTOM: grp = SOLDER_LAYER; reuse_group = 0; break;
+		}
+	}
+
+	if (reuse_group) { /* can't use group find here, it depends on existing silks! */
+		switch(role) {
+			case PCB_LYT_MASK:
+			case PCB_LYT_PASTE:
+			case PCB_LYT_SILK:
+				return -1; /* do not create silk, paste or mask layers, they are special */
+
+			case PCB_LYT_COPPER:
+				switch(loc) {
+					case PCB_LYT_TOP:
+					case PCB_LYT_BOTTOM:
+						abort(); /* can't get here */
+					case PCB_LYT_INTERN:
+						/* find the first internal layer */
+						for(found = 0, grp = 2; grp < MAX_LAYER; grp++) {
+							if (PCB->LayerGroups.Number[grp] > 0) {
+								id = PCB->LayerGroups.Entries[grp][0];
+								if (strcmp(PCB->Data->Layer[id].Name, "outline") != 0) {
+									found = 1;
+									break;
+								}
+							}
+						}
+						if (!found)
+							return -1;
+						id = -1;
+				}
+				break;
+			case PCB_LYT_OUTLINE:
+				abort(); /* can't get here */
+		}
+	}
+
+	if (grp < 0) {
+		/* Also need to create a group */
+		for(grp = 0; grp < MAX_LAYER; grp++)
+			if (PCB->LayerGroups.Number[grp] == 0)
+				break;
+		if (grp >= MAX_LAYER)
+			return -2;
+	}
+
+	id = PCB->Data->LayerN++;
+
+	if (lname != NULL) {
+		if (PCB->Data->Layer[id].Name != NULL)
+			free((char *)PCB->Data->Layer[id].Name);
+		PCB->Data->Layer[id].Name = pcb_strdup(lname);
+	}
+
+	/* add layer to group */
+	PCB->LayerGroups.Entries[grp][PCB->LayerGroups.Number[grp]] = id;
+	PCB->LayerGroups.Number[grp]++;
+
+	return id;
+}
+
+/* Temporary hack: silk layers have to be added as the last entry in the top and bottom layer groups, if they are not already in */
+static void hack_in_silks()
+{
+	int sl, cl, found, n;
+
+	sl = SOLDER_LAYER + PCB->Data->LayerN;
+	for(found = 0, n = 0; n < PCB->LayerGroups.Number[SOLDER_LAYER]; n++)
+		if (PCB->LayerGroups.Entries[SOLDER_LAYER][n] == sl)
+			found = 1;
+
+	if (!found) {
+		PCB->LayerGroups.Entries[SOLDER_LAYER][PCB->LayerGroups.Number[SOLDER_LAYER]] = sl;
+		PCB->LayerGroups.Number[SOLDER_LAYER]++;
+		if (PCB->Data->Layer[sl].Name != NULL)
+			free((char *)PCB->Data->Layer[sl].Name);
+		PCB->Data->Layer[sl].Name = pcb_strdup("silk");
+	}
+
+
+	cl = COMPONENT_LAYER + PCB->Data->LayerN;
+	for(found = 0, n = 0; n < PCB->LayerGroups.Number[COMPONENT_LAYER]; n++)
+		if (PCB->LayerGroups.Entries[COMPONENT_LAYER][n] == cl)
+			found = 1;
+
+	if (!found) {
+		PCB->LayerGroups.Entries[COMPONENT_LAYER][PCB->LayerGroups.Number[COMPONENT_LAYER]] = cl;
+		PCB->LayerGroups.Number[COMPONENT_LAYER]++;
+		if (PCB->Data->Layer[cl].Name != NULL)
+			free((char *)PCB->Data->Layer[cl].Name);
+		PCB->Data->Layer[cl].Name = pcb_strdup("silk");
+	}
+}
+
+int pcb_layer_rename(int layer, const char *lname)
+{
+	if (PCB->Data->Layer[layer].Name != NULL)
+		free((char *)PCB->Data->Layer[layer].Name);
+	PCB->Data->Layer[layer].Name = pcb_strdup(lname);
+	return 0;
+}
+
+void pcb_layers_finalize()
+{
+	hack_in_silks();
+}
+
+#undef APPEND
diff --git a/src/layer.h b/src/layer.h
new file mode 100644
index 0000000..962f394
--- /dev/null
+++ b/src/layer.h
@@ -0,0 +1,157 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2006 Thomas Nau
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas (pcb-rnd extensions)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for layer manipulation */
+
+/* Returns pcb_true if the given layer is empty (there are no objects on the layer) */
+pcb_bool IsLayerEmpty(LayerTypePtr);
+pcb_bool IsLayerNumEmpty(int);
+
+/* Returns pcb_true if all layers in a group are empty */
+pcb_bool IsLayerGroupEmpty(int);
+
+
+/************ OLD API - new code should not use these **************/
+
+int ParseGroupString(const char *, LayerGroupTypePtr, int /* LayerN */ );
+
+int GetLayerNumber(DataTypePtr, LayerTypePtr);
+int GetLayerGroupNumberByPointer(LayerTypePtr);
+int GetLayerGroupNumberByNumber(pcb_cardinal_t);
+int GetGroupOfLayer(int);
+int ChangeGroupVisibility(int, pcb_bool, pcb_bool);
+void LayerStringToLayerStack(const char *);
+
+/* Layer Group Functions */
+
+/* Returns group actually moved to (i.e. either group or previous) */
+int MoveLayerToGroup(int layer, int group);
+
+/* Returns pointer to private buffer */
+char *LayerGroupsToString(LayerGroupTypePtr);
+
+#define	LAYER_ON_STACK(n)	(&PCB->Data->Layer[LayerStack[(n)]])
+#define LAYER_PTR(n)            (&PCB->Data->Layer[(n)])
+#define	CURRENT			(PCB->SilkActive ? &PCB->Data->Layer[ \
+				(conf_core.editor.show_solder_side ? solder_silk_layer : component_silk_layer)] \
+				: LAYER_ON_STACK(0))
+#define	INDEXOFCURRENT		(PCB->SilkActive ? \
+				(conf_core.editor.show_solder_side ? solder_silk_layer : component_silk_layer) \
+				: LayerStack[0])
+#define SILKLAYER		Layer[ \
+				(conf_core.editor.show_solder_side ? solder_silk_layer : component_silk_layer)]
+#define BACKSILKLAYER		Layer[ \
+				(conf_core.editor.show_solder_side ? component_silk_layer : solder_silk_layer)]
+
+#define TEST_SILK_LAYER(layer)	(GetLayerNumber (PCB->Data, layer) >= max_copper_layer)
+
+/* These decode the set_layer index. */
+#define SL_TYPE(x) ((x) < 0 ? (x) & 0x0f0 : 0)
+#define SL_SIDE(x) ((x) & 0x00f)
+#define SL_MYSIDE(x) ((((x) & SL_BOTTOM_SIDE)!=0) == (SWAP_IDENT != 0))
+
+#define SL_0_SIDE	0x0000
+#define SL_TOP_SIDE	0x0001
+#define SL_BOTTOM_SIDE	0x0002
+#define SL_SILK		0x0010
+#define SL_MASK		0x0020
+#define SL_PDRILL	0x0030
+#define SL_UDRILL	0x0040
+#define SL_PASTE	0x0050
+#define SL_INVISIBLE	0x0060
+#define SL_FAB		0x0070
+#define SL_ASSY		0x0080
+#define SL_RATS		0x0090
+/* Callers should use this.  */
+#define SL(type,side) (~0xfff | SL_##type | SL_##side##_SIDE)
+
+
+#define LAYER_IS_OUTLINE(idx) (strcmp(PCB->Data->Layer[idx].Name, "route") == 0 || strcmp(PCB->Data->Layer[idx].Name, "outline") == 0)
+
+/************ NEW API - new code should use these **************/
+
+/* Layer type bitfield */
+typedef enum {
+	/* Stack-up (vertical position): */
+	PCB_LYT_TOP      = 0x0001, /* layer is on the top side of the board */
+	PCB_LYT_BOTTOM   = 0x0002, /* layer is on the bottom side of the board */
+	PCB_LYT_INTERN   = 0x0004, /* layer is internal */
+	PCB_LYT_ANYWHERE = 0x00FF, /* MASK: layer is anywhere on the stack */
+
+	/* What the layer consists of */
+	PCB_LYT_COPPER   = 0x0100, /* copper objects */
+	PCB_LYT_SILK     = 0x0200, /* silk objects */
+	PCB_LYT_MASK     = 0x0400, /* solder mask */
+	PCB_LYT_PASTE    = 0x0800, /* paste */
+	PCB_LYT_OUTLINE  = 0x1000, /* outline (contour of the board) */
+	PCB_LYT_ANYTHING = 0xFF00  /* MASK */
+} pcb_layer_type_t;
+
+
+/* returns a bitfield of pcb_layer_type_t; returns 0 if layer_idx is invalid. */
+unsigned int pcb_layer_flags(int layer_idx);
+
+/* List layer IDs that matches mask - write the first res_len items in res,
+   if res is not NULL. Returns:
+    - the total number of layers matching the query, if res is NULL
+    - the number of layers copied into res if res is not NULL
+*/
+int pcb_layer_list(pcb_layer_type_t mask, int *res, int res_len);
+
+/* Same as pcb_layer_list but lists layer groups. A group is matching
+   if any layer in that group matches mask. */
+int pcb_layer_group_list(pcb_layer_type_t mask, int *res, int res_len);
+
+/* Looks up which group a layer is in. Returns group ID. */
+int pcb_layer_lookup_group(int layer_id);
+
+/* Put a layer in a group (the layer should not be in any other group) */
+void pcb_layer_add_in_group(int layer_id, int group_id);
+
+/* Slow linear search for a layer by name */
+int pcb_layer_by_name(const char *name);
+
+/**** layer creation (for load/import code) ****/
+
+/* Reset layers to the bare minimum (double sided board) */
+void pcb_layers_reset();
+
+/* If reuse_layer is false, create a new layer of the given type; if
+   reuse_group is true, try to put the new layer on an existing group.
+   If reuse_layer is 1, first try to return an already exiting layer that
+   matches type and create a new one if that fails.
+   Upon creating a new layer, name it according to lname if it is not NULL
+   Returns a layer index (or -1 on error)
+   Do not create: mask, silk, paste; they are special layers.
+   */
+int pcb_layer_create(pcb_layer_type_t type, pcb_bool reuse_layer, pcb_bool_t reuse_group, const char *lname);
+
+/* Rename an existing layer */
+int pcb_layer_rename(int layer, const char *lname);
+
+/* Needs to be called once at the end, when all layers has been added */
+void pcb_layers_finalize();
diff --git a/src/line.c b/src/line.c
new file mode 100644
index 0000000..f18f518
--- /dev/null
+++ b/src/line.c
@@ -0,0 +1,534 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 2004 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <math.h>
+#include <setjmp.h>
+#include <stdlib.h>
+
+#include "data.h"
+#include "crosshair.h"
+#include "find.h"
+#include "line.h"
+#include "misc.h"
+#include "rtree.h"
+#include "layer.h"
+
+static double drc_lines(PointTypePtr end, pcb_bool way);
+
+/* ---------------------------------------------------------------------------
+ * Adjust the attached line to 45 degrees if necessary
+ */
+void AdjustAttachedLine(void)
+{
+	AttachedLineTypePtr line = &Crosshair.AttachedLine;
+
+	/* I need at least one point */
+	if (line->State == STATE_FIRST)
+		return;
+	/* don't draw outline when ctrl key is pressed */
+	if (conf_core.editor.mode == PCB_MODE_LINE && gui->control_is_pressed()) {
+		line->draw = pcb_false;
+		return;
+	}
+	else
+		line->draw = pcb_true;
+	/* no 45 degree lines required */
+	if (PCB->RatDraw || conf_core.editor.all_direction_lines) {
+		line->Point2.X = Crosshair.X;
+		line->Point2.Y = Crosshair.Y;
+		return;
+	}
+	FortyFiveLine(line);
+}
+
+/* ---------------------------------------------------------------------------
+ * makes the attached line fit into a 45 degree direction
+ *
+ * directions:
+ *
+ *           0
+ *          7 1
+ *         6   2
+ *          5 3
+ *           4
+ */
+void FortyFiveLine(AttachedLineTypePtr Line)
+{
+	Coord dx, dy, min;
+	unsigned direction = 0;
+	double m;
+
+	/* first calculate direction of line */
+	dx = Crosshair.X - Line->Point1.X;
+	dy = Crosshair.Y - Line->Point1.Y;
+
+	if (!dx) {
+		if (!dy)
+			/* zero length line, don't draw anything */
+			return;
+		else
+			direction = dy > 0 ? 0 : 4;
+	}
+	else {
+		m = (double) dy / dx;
+		direction = 2;
+		if (m > PCB_TAN_30_DEGREE)
+			direction = m > PCB_TAN_60_DEGREE ? 0 : 1;
+		else if (m < -PCB_TAN_30_DEGREE)
+			direction = m < -PCB_TAN_60_DEGREE ? 0 : 3;
+	}
+	if (dx < 0)
+		direction += 4;
+
+	dx = coord_abs(dx);
+	dy = coord_abs(dy);
+	min = MIN(dx, dy);
+
+	/* now set up the second pair of coordinates */
+	switch (direction) {
+	case 0:
+	case 4:
+		Line->Point2.X = Line->Point1.X;
+		Line->Point2.Y = Crosshair.Y;
+		break;
+
+	case 2:
+	case 6:
+		Line->Point2.X = Crosshair.X;
+		Line->Point2.Y = Line->Point1.Y;
+		break;
+
+	case 1:
+		Line->Point2.X = Line->Point1.X + min;
+		Line->Point2.Y = Line->Point1.Y + min;
+		break;
+
+	case 3:
+		Line->Point2.X = Line->Point1.X + min;
+		Line->Point2.Y = Line->Point1.Y - min;
+		break;
+
+	case 5:
+		Line->Point2.X = Line->Point1.X - min;
+		Line->Point2.Y = Line->Point1.Y - min;
+		break;
+
+	case 7:
+		Line->Point2.X = Line->Point1.X - min;
+		Line->Point2.Y = Line->Point1.Y + min;
+		break;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ *  adjusts the insert lines to make them 45 degrees as necessary
+ */
+void AdjustTwoLine(pcb_bool way)
+{
+	Coord dx, dy;
+	AttachedLineTypePtr line = &Crosshair.AttachedLine;
+
+	if (Crosshair.AttachedLine.State == STATE_FIRST)
+		return;
+	/* don't draw outline when ctrl key is pressed */
+	if (gui->control_is_pressed()) {
+		line->draw = pcb_false;
+		return;
+	}
+	else
+		line->draw = pcb_true;
+	if (conf_core.editor.all_direction_lines) {
+		line->Point2.X = Crosshair.X;
+		line->Point2.Y = Crosshair.Y;
+		return;
+	}
+	/* swap the modes if shift is held down */
+	if (gui->shift_is_pressed())
+		way = !way;
+	dx = Crosshair.X - line->Point1.X;
+	dy = Crosshair.Y - line->Point1.Y;
+	if (!way) {
+		if (coord_abs(dx) > coord_abs(dy)) {
+			line->Point2.X = Crosshair.X - SGN(dx) * coord_abs(dy);
+			line->Point2.Y = line->Point1.Y;
+		}
+		else {
+			line->Point2.X = line->Point1.X;
+			line->Point2.Y = Crosshair.Y - SGN(dy) * coord_abs(dx);
+		}
+	}
+	else {
+		if (coord_abs(dx) > coord_abs(dy)) {
+			line->Point2.X = line->Point1.X + SGN(dx) * coord_abs(dy);
+			line->Point2.Y = Crosshair.Y;
+		}
+		else {
+			line->Point2.X = Crosshair.X;
+			line->Point2.Y = line->Point1.Y + SGN(dy) * coord_abs(dx);;
+		}
+	}
+}
+
+struct drc_info {
+	LineTypePtr line;
+	pcb_bool solder;
+	jmp_buf env;
+};
+
+static r_dir_t drcVia_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr via = (PinTypePtr) b;
+	struct drc_info *i = (struct drc_info *) cl;
+
+	if (!TEST_FLAG(PCB_FLAG_FOUND, via) && PinLineIntersect(via, i->line))
+		longjmp(i->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t drcPad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct drc_info *i = (struct drc_info *) cl;
+
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) == i->solder && !TEST_FLAG(PCB_FLAG_FOUND, pad) && LinePadIntersect(i->line, pad))
+		longjmp(i->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t drcLine_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct drc_info *i = (struct drc_info *) cl;
+
+	if (!TEST_FLAG(PCB_FLAG_FOUND, line) && LineLineIntersect(line, i->line))
+		longjmp(i->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t drcArc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct drc_info *i = (struct drc_info *) cl;
+
+	if (!TEST_FLAG(PCB_FLAG_FOUND, arc) && LineArcIntersect(i->line, arc))
+		longjmp(i->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* drc_lines() checks for intersectors against two lines and
+ * adjusts the end point until there is no intersection or
+ * it winds up back at the start. If way is pcb_false it checks
+ * an ortho start line with one 45 refraction to reach the endpoint,
+ * otherwise it checks a 45 start, with a ortho refraction to reach endpoint
+ *
+ * It returns the straight-line length of the best answer, and
+ * changes the position of the input end point to the best answer.
+ */
+static double drc_lines(PointTypePtr end, pcb_bool way)
+{
+	double f, s, f2, s2, len, best;
+	Coord dx, dy, temp, last, length;
+	Coord temp2, last2, length2;
+	LineType line1, line2;
+	pcb_cardinal_t group, comp;
+	struct drc_info info;
+	pcb_bool two_lines, x_is_long, blocker;
+	PointType ans;
+
+	f = 1.0;
+	s = 0.5;
+	last = -1;
+	line1.Flags = line2.Flags = NoFlags();
+	line1.Thickness = conf_core.design.line_thickness + 2 * (PCB->Bloat + 1);
+	line2.Thickness = line1.Thickness;
+	line1.Clearance = line2.Clearance = 0;
+	line1.Point1.X = Crosshair.AttachedLine.Point1.X;
+	line1.Point1.Y = Crosshair.AttachedLine.Point1.Y;
+	dy = end->Y - line1.Point1.Y;
+	dx = end->X - line1.Point1.X;
+	if (coord_abs(dx) > coord_abs(dy)) {
+		x_is_long = pcb_true;
+		length = coord_abs(dx);
+	}
+	else {
+		x_is_long = pcb_false;
+		length = coord_abs(dy);
+	}
+	group = GetGroupOfLayer(INDEXOFCURRENT);
+	comp = max_group + 10;				/* this out-of-range group might save a call */
+	if (GetLayerGroupNumberByNumber(solder_silk_layer) == group)
+		info.solder = pcb_true;
+	else {
+		info.solder = pcb_false;
+		comp = GetLayerGroupNumberByNumber(component_silk_layer);
+	}
+	temp = length;
+	/* assume the worst */
+	best = 0.0;
+	ans.X = line1.Point1.X;
+	ans.Y = line1.Point1.Y;
+	while (length != last) {
+		last = length;
+		if (x_is_long) {
+			dx = SGN(dx) * length;
+			dy = end->Y - line1.Point1.Y;
+			length2 = coord_abs(dy);
+		}
+		else {
+			dy = SGN(dy) * length;
+			dx = end->X - line1.Point1.X;
+			length2 = coord_abs(dx);
+		}
+		temp2 = length2;
+		f2 = 1.0;
+		s2 = 0.5;
+		last2 = -1;
+		blocker = pcb_true;
+		while (length2 != last2) {
+			if (x_is_long)
+				dy = SGN(dy) * length2;
+			else
+				dx = SGN(dx) * length2;
+			two_lines = pcb_true;
+			if (coord_abs(dx) > coord_abs(dy) && x_is_long) {
+				line1.Point2.X = line1.Point1.X + (way ? SGN(dx) * coord_abs(dy) : dx - SGN(dx) * coord_abs(dy));
+				line1.Point2.Y = line1.Point1.Y + (way ? dy : 0);
+			}
+			else if (coord_abs(dy) >= coord_abs(dx) && !x_is_long) {
+				line1.Point2.X = line1.Point1.X + (way ? dx : 0);
+				line1.Point2.Y = line1.Point1.Y + (way ? SGN(dy) * coord_abs(dx) : dy - SGN(dy) * coord_abs(dx));
+			}
+			else if (x_is_long) {
+				/* we've changed which axis is long, so only do one line */
+				line1.Point2.X = line1.Point1.X + dx;
+				line1.Point2.Y = line1.Point1.Y + (way ? SGN(dy) * coord_abs(dx) : 0);
+				two_lines = pcb_false;
+			}
+			else {
+				/* we've changed which axis is long, so only do one line */
+				line1.Point2.Y = line1.Point1.Y + dy;
+				line1.Point2.X = line1.Point1.X + (way ? SGN(dx) * coord_abs(dy) : 0);
+				two_lines = pcb_false;
+			}
+			line2.Point1.X = line1.Point2.X;
+			line2.Point1.Y = line1.Point2.Y;
+			if (!two_lines) {
+				line2.Point2.Y = line1.Point2.Y;
+				line2.Point2.X = line1.Point2.X;
+			}
+			else {
+				line2.Point2.X = line1.Point1.X + dx;
+				line2.Point2.Y = line1.Point1.Y + dy;
+			}
+			SetLineBoundingBox(&line1);
+			SetLineBoundingBox(&line2);
+			last2 = length2;
+			if (setjmp(info.env) == 0) {
+				info.line = &line1;
+				r_search(PCB->Data->via_tree, &line1.BoundingBox, NULL, drcVia_callback, &info, NULL);
+				r_search(PCB->Data->pin_tree, &line1.BoundingBox, NULL, drcVia_callback, &info, NULL);
+				if (info.solder || comp == group)
+					r_search(PCB->Data->pad_tree, &line1.BoundingBox, NULL, drcPad_callback, &info, NULL);
+				if (two_lines) {
+					info.line = &line2;
+					r_search(PCB->Data->via_tree, &line2.BoundingBox, NULL, drcVia_callback, &info, NULL);
+					r_search(PCB->Data->pin_tree, &line2.BoundingBox, NULL, drcVia_callback, &info, NULL);
+					if (info.solder || comp == group)
+						r_search(PCB->Data->pad_tree, &line2.BoundingBox, NULL, drcPad_callback, &info, NULL);
+				}
+				GROUP_LOOP(PCB->Data, group);
+				{
+					info.line = &line1;
+					r_search(layer->line_tree, &line1.BoundingBox, NULL, drcLine_callback, &info, NULL);
+					r_search(layer->arc_tree, &line1.BoundingBox, NULL, drcArc_callback, &info, NULL);
+					if (two_lines) {
+						info.line = &line2;
+						r_search(layer->line_tree, &line2.BoundingBox, NULL, drcLine_callback, &info, NULL);
+						r_search(layer->arc_tree, &line2.BoundingBox, NULL, drcArc_callback, &info, NULL);
+					}
+				}
+				END_LOOP;
+				/* no intersector! */
+				blocker = pcb_false;
+				f2 += s2;
+				len = (line2.Point2.X - line1.Point1.X);
+				len *= len;
+				len += (double) (line2.Point2.Y - line1.Point1.Y) * (line2.Point2.Y - line1.Point1.Y);
+				if (len > best) {
+					best = len;
+					ans.X = line2.Point2.X;
+					ans.Y = line2.Point2.Y;
+				}
+#if 0
+				if (f2 > 1.0)
+					f2 = 0.5;
+#endif
+			}
+			else {
+				/* bumped into something, back off */
+				f2 -= s2;
+			}
+			s2 *= 0.5;
+			length2 = MIN(f2 * temp2, temp2);
+		}
+		if (!blocker && ((x_is_long && line2.Point2.X - line1.Point1.X == dx)
+										 || (!x_is_long && line2.Point2.Y - line1.Point1.Y == dy)))
+			f += s;
+		else
+			f -= s;
+		s *= 0.5;
+		length = MIN(f * temp, temp);
+	}
+
+	end->X = ans.X;
+	end->Y = ans.Y;
+	return best;
+}
+
+static void drc_line(PointTypePtr end)
+{
+	struct drc_info info;
+	pcb_cardinal_t group, comp;
+	LineType line;
+	AttachedLineType aline;
+	static PointType last_good; /* internal state of last good endpoint - we cna do thsi cheat, because... */
+
+	/* ... we hardwire the assumption on how a line is drawn: it starts out as a 0 long segment, which is valid: */
+	if ((Crosshair.AttachedLine.Point1.X == Crosshair.X) && (Crosshair.AttachedLine.Point1.Y == Crosshair.Y)) {
+		line.Point1 = line.Point2 = Crosshair.AttachedLine.Point1;
+		goto auto_good;
+	}
+
+	memset(&line, 0, sizeof(line));
+
+	/* check where the line wants to end */
+	aline.Point1.X = Crosshair.AttachedLine.Point1.X;
+	aline.Point1.Y = Crosshair.AttachedLine.Point1.Y;
+	FortyFiveLine(&aline);
+	line.Point1 = aline.Point1;
+	line.Point2 = aline.Point2;
+
+	/* prepare for the intersection search */
+	group = GetGroupOfLayer(INDEXOFCURRENT);
+	comp = max_group + 10;  /* this out-of-range group might save a call */
+	if (GetLayerGroupNumberByNumber(solder_silk_layer) == group)
+		info.solder = pcb_true;
+	else {
+		info.solder = pcb_false;
+		comp = GetLayerGroupNumberByNumber(component_silk_layer);
+	}
+
+	/* search for intersection */
+	SetLineBoundingBox(&line);
+	if (setjmp(info.env) == 0) {
+		info.line = &line;
+		r_search(PCB->Data->via_tree, &line.BoundingBox, NULL, drcVia_callback, &info, NULL);
+		r_search(PCB->Data->pin_tree, &line.BoundingBox, NULL, drcVia_callback, &info, NULL);
+		if (info.solder || comp == group)
+			r_search(PCB->Data->pad_tree, &line.BoundingBox, NULL, drcPad_callback, &info, NULL);
+		GROUP_LOOP(PCB->Data, group);
+		{
+			info.line = &line;
+			r_search(layer->line_tree, &line.BoundingBox, NULL, drcLine_callback, &info, NULL);
+			r_search(layer->arc_tree, &line.BoundingBox, NULL, drcArc_callback, &info, NULL);
+		}
+		END_LOOP;
+		/* no intersector! */
+		auto_good:;
+		last_good.X = end->X = line.Point2.X;
+		last_good.Y = end->Y = line.Point2.Y;
+		return;
+	}
+
+	/* bumped into ans */
+	end->X = last_good.X;
+	end->Y = last_good.Y;
+}
+
+void EnforceLineDRC(void)
+{
+	PointType r45, rs;
+	pcb_bool shift;
+	double r1, r2;
+
+	/* Silence a bogus compiler warning by storing this in a variable */
+	int layer_idx = INDEXOFCURRENT;
+
+	if (gui->mod1_is_pressed() || gui->control_is_pressed() || PCB->RatDraw || layer_idx >= max_copper_layer)
+		return;
+
+	rs.X = r45.X = Crosshair.X;
+	rs.Y = r45.Y = Crosshair.Y;
+
+	if (conf_core.editor.line_refraction != 0) {
+		/* first try starting straight */
+		r1 = drc_lines(&rs, pcb_false);
+		/* then try starting at 45 */
+		r2 = drc_lines(&r45, pcb_true);
+	}
+	else {
+		drc_line(&rs);
+		r45 = rs;
+#define sqr(a) ((a) * (a))
+		r1 = r2 = sqrt(sqr(rs.X - Crosshair.AttachedLine.Point1.X) + sqr(rs.Y - Crosshair.AttachedLine.Point1.Y));
+#undef sqr
+	}
+	/* shift<Key> forces the line lookahead path to refract the alternate way */
+	shift = gui->shift_is_pressed();
+
+	if (XOR(r1 > r2, shift)) {
+		if (conf_core.editor.line_refraction != 0) {
+			if (shift) {
+				if (conf_core.editor.line_refraction !=2)
+					conf_setf(CFR_DESIGN, "editor/line_refraction", -1, "%d", 2);
+			}
+			else{
+				if (conf_core.editor.line_refraction != 1)
+				conf_setf(CFR_DESIGN, "editor/line_refraction", -1, "%d", 1);
+			}
+		}
+		Crosshair.X = rs.X;
+		Crosshair.Y = rs.Y;
+	}
+	else {
+		if (conf_core.editor.line_refraction !=0) {
+			if (shift) {
+				if (conf_core.editor.line_refraction != 1)
+					conf_setf(CFR_DESIGN, "editor/line_refraction", -1, "%d", 1);
+			}
+			else {
+				if (conf_core.editor.line_refraction != 2)
+					conf_setf(CFR_DESIGN, "editor/line_refraction", -1, "%d", 2);
+			}
+		}
+		Crosshair.X = r45.X;
+		Crosshair.Y = r45.Y;
+	}
+}
diff --git a/src/line.h b/src/line.h
new file mode 100644
index 0000000..a62c74a
--- /dev/null
+++ b/src/line.h
@@ -0,0 +1,42 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 2004 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for inserting points into objects */
+
+#ifndef	PCB_LINE_H
+#define	PCB_LINE_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * prototypes
+ */
+void AdjustAttachedLine(void);
+void AdjustTwoLine(pcb_bool);
+void FortyFiveLine(AttachedLineTypePtr);
+void EnforceLineDRC(void);
+#endif
diff --git a/src/list_arc.c b/src/list_arc.c
new file mode 100644
index 0000000..2665245
--- /dev/null
+++ b/src/list_arc.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_arc.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_arc.h b/src/list_arc.h
new file mode 100644
index 0000000..47c244d
--- /dev/null
+++ b/src/list_arc.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_ARC_H
+#define LIST_ARC_H
+
+/* List of Arcs */
+#define TDL(x)      arclist_ ## x
+#define TDL_LIST_T  arclist_t
+#define TDL_ITEM_T  ArcType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define arclist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_common.h b/src/list_common.h
new file mode 100644
index 0000000..1e3c4b2
--- /dev/null
+++ b/src/list_common.h
@@ -0,0 +1,35 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_COMMON_H
+#define LIST_COMMON_H
+
+/* iterate over all items of list and call func(item) */
+#define list_map0(list, itemtype, func) \
+		do { \
+			itemtype *__item__; \
+			gdl_iterator_t it; \
+			linelist_foreach((list), &it, __item__) \
+				func(__item__); \
+		} while(0)
+
+#endif
diff --git a/src/list_conf.c b/src/list_conf.c
new file mode 100644
index 0000000..3750553
--- /dev/null
+++ b/src/list_conf.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define FROM_CONF_C
+#include "conf.h"
+#include "list_conf.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_conf.h b/src/list_conf.h
new file mode 100644
index 0000000..af7a196
--- /dev/null
+++ b/src/list_conf.h
@@ -0,0 +1,49 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_CONF_H
+#define LIST_CONF_H
+
+#include "conf.h"
+
+#include <genlist/gentdlist_undef.h>
+
+#ifdef FROM_CONF_C
+#define TDL_DONT_UNDEF
+#endif
+
+#define TDL(x)      conflist_ ## x
+#define TDL_LIST_T  conflist_t
+#define TDL_LIST_U  conflist_u
+#define TDL_ITEM_T  conf_listitem_t
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define conflist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_element.c b/src/list_element.c
new file mode 100644
index 0000000..3fc17fb
--- /dev/null
+++ b/src/list_element.c
@@ -0,0 +1,30 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+#include "global.h"
+#undef LIST_ELEMENT_H
+#define LIST_ELEMENT_NOINSTANT
+#define TDL_DONT_UNDEF
+#include "list_element.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_element.h b/src/list_element.h
new file mode 100644
index 0000000..5a59e9d
--- /dev/null
+++ b/src/list_element.h
@@ -0,0 +1,79 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_ELEMENT_H
+#define LIST_ELEMENT_H
+
+/* List of Elements */
+#define TDL(x)      elementlist_ ## x
+#define TDL_LIST_T  elementlist_t
+#define TDL_ITEM_T  ElementType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define elementlist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+#include "ht_element.h"
+#include <genht/hash.h>
+
+/* Calculate a hash value using the content of the element. The hash value
+   represents the actual content of an element */
+unsigned int pcb_element_hash(const ElementType *e);
+
+/* Compare two elements and return 1 if they contain the same objects. */
+int pcb_element_eq(const ElementType *e1, const ElementType *e2);
+
+/* Create a new local variable to be used for deduplication */
+#define elementlist_dedup_initializer(state) htep_t *state = NULL;
+
+/* Do a "continue" if an element matching loop_elem has been seen already;
+   Typically this is invoked as the first statement of an elementlist_foreach()
+   loop. */
+#define elementlist_dedup_skip(state, loop_elem) \
+switch(1) { \
+	case 1: { \
+		if (state == NULL) \
+			state = htep_alloc(pcb_element_hash, pcb_element_eq); \
+		if (htep_has(state, loop_elem)) \
+			continue; \
+		htep_set(state, loop_elem, 1); \
+	} \
+}
+
+/* use this after the loop to free all memory used by state */
+#define elementlist_dedup_free(state) \
+	do { \
+		if (state != NULL) { \
+			htep_free(state); \
+			state = NULL; \
+		} \
+	} while(0)
+
+
+#ifndef LIST_ELEMENT_NOINSTANT
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+#endif
+
+#endif
diff --git a/src/list_line.c b/src/list_line.c
new file mode 100644
index 0000000..2ecad37
--- /dev/null
+++ b/src/list_line.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_line.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_line.h b/src/list_line.h
new file mode 100644
index 0000000..498c698
--- /dev/null
+++ b/src/list_line.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_LINE_H
+#define LIST_LINE_H
+
+/* List of Lines */
+#define TDL(x)      linelist_ ## x
+#define TDL_LIST_T  linelist_t
+#define TDL_ITEM_T  LineType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define linelist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_pad.c b/src/list_pad.c
new file mode 100644
index 0000000..24914f7
--- /dev/null
+++ b/src/list_pad.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_pad.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_pad.h b/src/list_pad.h
new file mode 100644
index 0000000..91fcb32
--- /dev/null
+++ b/src/list_pad.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_PAD_H
+#define LIST_PAD_H
+
+/* List of Pads */
+#define TDL(x)      padlist_ ## x
+#define TDL_LIST_T  padlist_t
+#define TDL_ITEM_T  PadType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define padlist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_pin.c b/src/list_pin.c
new file mode 100644
index 0000000..58fa7b0
--- /dev/null
+++ b/src/list_pin.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_pin.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_pin.h b/src/list_pin.h
new file mode 100644
index 0000000..4350231
--- /dev/null
+++ b/src/list_pin.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_PIN_H
+#define LIST_PIN_H
+
+/* List of Pins */
+#define TDL(x)      pinlist_ ## x
+#define TDL_LIST_T  pinlist_t
+#define TDL_ITEM_T  PinType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define pinlist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_poly.c b/src/list_poly.c
new file mode 100644
index 0000000..276c90a
--- /dev/null
+++ b/src/list_poly.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_poly.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_poly.h b/src/list_poly.h
new file mode 100644
index 0000000..91104b1
--- /dev/null
+++ b/src/list_poly.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_POLY_H
+#define LIST_POLY_H
+
+/* List of Lines */
+#define TDL(x)      polylist_ ## x
+#define TDL_LIST_T  polylist_t
+#define TDL_ITEM_T  PolygonType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define polylist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_rat.c b/src/list_rat.c
new file mode 100644
index 0000000..f028a3c
--- /dev/null
+++ b/src/list_rat.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_rat.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_rat.h b/src/list_rat.h
new file mode 100644
index 0000000..8590f6a
--- /dev/null
+++ b/src/list_rat.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_RAT_H
+#define LIST_RAT_H
+
+/* List of Rats */
+#define TDL(x)      ratlist_ ## x
+#define TDL_LIST_T  ratlist_t
+#define TDL_ITEM_T  RatType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define ratlist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/list_text.c b/src/list_text.c
new file mode 100644
index 0000000..b3e3ce8
--- /dev/null
+++ b/src/list_text.c
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define TDL_DONT_UNDEF
+#include "global_objs.h"
+#include "list_text.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/list_text.h b/src/list_text.h
new file mode 100644
index 0000000..3f87d2c
--- /dev/null
+++ b/src/list_text.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LIST_TEXT_H
+#define LIST_TEXT_H
+
+/* List of Text */
+#define TDL(x)      textlist_ ## x
+#define TDL_LIST_T  textlist_t
+#define TDL_ITEM_T  TextType
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define textlist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/macro.h b/src/macro.h
new file mode 100644
index 0000000..c23b7c2
--- /dev/null
+++ b/src/macro.h
@@ -0,0 +1,436 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* some commonly used macros not related to a special C-file
+ * the file is included by global.h after const.h
+ */
+
+#ifndef	PCB_MACRO_H
+#define	PCB_MACRO_H
+
+/* ---------------------------------------------------------------------------
+ * macros to transform coord systems
+ * draw.c uses a different definition of TO_SCREEN
+ */
+#ifndef	SWAP_IDENT
+#define	SWAP_IDENT			conf_core.editor.show_solder_side
+#endif
+
+#define	SWAP_SIGN_X(x)		(x)
+#define	SWAP_SIGN_Y(y)		(-(y))
+#define	SWAP_ANGLE(a)		(-(a))
+#define	SWAP_DELTA(d)		(-(d))
+#define	SWAP_X(x)		(SWAP_SIGN_X(x))
+#define	SWAP_Y(y)		(PCB->MaxHeight +SWAP_SIGN_Y(y))
+
+/* ---------------------------------------------------------------------------
+ * misc macros, some might already be defined by <limits.h>
+ */
+#ifndef	MIN
+#define	MIN(a,b)		((a) < (b) ? (a) : (b))
+#define	MAX(a,b)		((a) > (b) ? (a) : (b))
+#endif
+#ifndef SGN
+#define SGN(a)			((a) >0 ? 1 : ((a) == 0 ? 0 : -1))
+#endif
+#define SGNZ(a)                 ((a) >=0 ? 1 : -1)
+#define MAKEMIN(a,b)            if ((b) < (a)) (a) = (b)
+#define MAKEMAX(a,b)            if ((b) > (a)) (a) = (b)
+
+#define	ENTRIES(x)		(sizeof((x))/sizeof((x)[0]))
+#define	UNKNOWN(a)		((a) && *(a) ? (a) : "(unknown)")
+#define NSTRCMP(a, b)		((a) ? ((b) ? strcmp((a),(b)) : 1) : -1)
+#define	EMPTY(a)		((a) ? (a) : "")
+#define	EMPTY_STRING_P(a)	((a) ? (a)[0]==0 : 1)
+#define XOR(a,b)		(((a) && !(b)) || (!(a) && (b)))
+#define SQUARE(x)		((float) (x) * (float) (x))
+#define TO_RADIANS(degrees)	(PCB_M180 * (degrees))
+
+/* ---------------------------------------------------------------------------
+ * returns the object ID
+ */
+#define	OBJECT_ID(p)		(((AnyObjectTypePtr) p)->ID)
+
+/* ---------------------------------------------------------------------------
+ * access macro for current buffer
+ */
+#define	PASTEBUFFER		(&Buffers[conf_core.editor.buffer_number])
+
+/* ---------------------------------------------------------------------------
+ * some routines for flag setting, clearing, changing and testing
+ */
+#define	SET_FLAG(F,P)		((P)->Flags.f |= (F))
+#define	CLEAR_FLAG(F,P)		((P)->Flags.f &= (~(F)))
+#define	TEST_FLAG(F,P)		((P)->Flags.f & (F) ? 1 : 0)
+#define	TOGGLE_FLAG(F,P)	((P)->Flags.f ^= (F))
+#define	ASSIGN_FLAG(F,V,P)	((P)->Flags.f = ((P)->Flags.f & (~(F))) | ((V) ? (F) : 0))
+#define TEST_FLAGS(F,P)         (((P)->Flags.f & (F)) == (F) ? 1 : 0)
+
+typedef enum {
+	PCB_CHGFLG_CLEAR,
+	PCB_CHGFLG_SET,
+	PCB_CHGFLG_TOGGLE
+} pcb_change_flag_t;
+
+#define CHANGE_FLAG(how, F, P) \
+do { \
+	switch(how) { \
+		case PCB_CHGFLG_CLEAR:  CLEAR_FLAG(F, P); break; \
+		case PCB_CHGFLG_SET:    SET_FLAG(F, P); break; \
+		case PCB_CHGFLG_TOGGLE: TOGGLE_FLAG(F, P); break; \
+	} \
+} while(0)
+
+#define FLAGS_EQUAL(F1,F2)	(memcmp (&F1, &F2, sizeof(FlagType)) == 0)
+
+#define THERMFLAG(L)		(0xf << (4 *((L) % 2)))
+
+#define TEST_THERM(L,P)		((P)->Flags.t[(L)/2] & THERMFLAG(L) ? 1 : 0)
+#define GET_THERM(L,P)		(((P)->Flags.t[(L)/2] >> (4 * ((L) % 2))) & 0xf)
+#define CLEAR_THERM(L,P)	(P)->Flags.t[(L)/2] &= ~THERMFLAG(L)
+#define ASSIGN_THERM(L,V,P)	(P)->Flags.t[(L)/2] = ((P)->Flags.t[(L)/2] & ~THERMFLAG(L)) | ((V)  << (4 * ((L) % 2)))
+
+
+#define GET_SQUARE(P)		((P)->Flags.q)
+#define CLEAR_SQUARE(P)		(P)->Flags.q = 0
+#define ASSIGN_SQUARE(V,P)	(P)->Flags.q = V
+
+
+#define GET_INTCONN(P)		((P)->Flags.int_conn_grp)
+
+extern int mem_any_set(unsigned char *, int);
+#define TEST_ANY_THERMS(P)	mem_any_set((P)->Flags.t, sizeof((P)->Flags.t))
+
+/* ---------------------------------------------------------------------------
+ * access macros for elements name structure
+ */
+#define	DESCRIPTION_INDEX	0
+#define	NAMEONPCB_INDEX		1
+#define	VALUE_INDEX		2
+#define	NAME_INDEX()		(conf_core.editor.name_on_pcb ? NAMEONPCB_INDEX :\
+				(conf_core.editor.description ?		\
+				DESCRIPTION_INDEX : VALUE_INDEX))
+#define	ELEMENT_NAME(p,e)	((e)->Name[NAME_INDEX()].TextString)
+#define	DESCRIPTION_NAME(e)	((e)->Name[DESCRIPTION_INDEX].TextString)
+#define	NAMEONPCB_NAME(e)	((e)->Name[NAMEONPCB_INDEX].TextString)
+#define	VALUE_NAME(e)		((e)->Name[VALUE_INDEX].TextString)
+#define	ELEMENT_TEXT(p,e)	((e)->Name[NAME_INDEX()])
+#define	DESCRIPTION_TEXT(e)	((e)->Name[DESCRIPTION_INDEX])
+#define	NAMEONPCB_TEXT(e)	((e)->Name[NAMEONPCB_INDEX])
+#define	VALUE_TEXT(e)		((e)->Name[VALUE_INDEX])
+
+/* ---------------------------------------------------------------------------
+ *  Determines if text is actually visible
+ */
+#define TEXT_IS_VISIBLE(b, l, t) \
+	((l)->On)
+
+/* ---------------------------------------------------------------------------
+ *  Determines if object is on front or back
+ */
+#define FRONT(o)	\
+	((TEST_FLAG(PCB_FLAG_ONSOLDER, (o)) != 0) == SWAP_IDENT)
+
+/* ---------------------------------------------------------------------------
+ *  Determines if an object is on the given side. side is either SOLDER_LAYER
+ *  or COMPONENT_LAYER.
+ */
+#define ON_SIDE(element, side) \
+        (TEST_FLAG (PCB_FLAG_ONSOLDER, element) == (side == SOLDER_LAYER))
+
+/* ---------------------------------------------------------------------------
+ * some loop shortcuts
+ *
+ * a pointer is created from index addressing because the base pointer
+ * may change when new memory is allocated;
+ *
+ * all data is relative to an objects name 'top' which can be either
+ * PCB or PasteBuffer
+ */
+#define END_LOOP  }} while (0)
+
+#define VIA_LOOP(top) do {                                          \
+  PinType *via;                                                     \
+  gdl_iterator_t __it__;                                            \
+  pinlist_foreach(&(top)->Via, &__it__, via) {
+
+#define DRILL_LOOP(top) do             {               \
+        pcb_cardinal_t        n;                                      \
+        DrillTypePtr    drill;                                  \
+        for (n = 0; (top)->DrillN > 0 && n < (top)->DrillN; n++)                        \
+        {                                                       \
+                drill = &(top)->Drill[n]
+
+#define NETLIST_LOOP(top) do   {                         \
+        pcb_cardinal_t        n;                                      \
+        NetListTypePtr   netlist;                               \
+        for (n = (top)->NetListN-1; n != -1; n--)               \
+        {                                                       \
+                netlist = &(top)->NetList[n]
+
+#define NET_LOOP(top) do   {                             \
+        pcb_cardinal_t        n;                                      \
+        NetTypePtr   net;                                       \
+        for (n = (top)->NetN-1; n != -1; n--)                   \
+        {                                                       \
+                net = &(top)->Net[n]
+
+#define CONNECTION_LOOP(net) do {                         \
+        pcb_cardinal_t        n;                                      \
+        ConnectionTypePtr       connection;                     \
+        for (n = (net)->ConnectionN-1; n != -1; n--)            \
+        {                                                       \
+                connection = & (net)->Connection[n]
+
+#define ELEMENT_LOOP(top) do {                                      \
+  ElementType *element;                                             \
+  gdl_iterator_t __it__;                                            \
+  pinlist_foreach(&(top)->Element, &__it__, element) {
+
+#define RAT_LOOP(top) do {                                          \
+  RatType *line;                                                    \
+  gdl_iterator_t __it__;                                            \
+  ratlist_foreach(&(top)->Rat, &__it__, line) {
+
+#define	ELEMENTTEXT_LOOP(element) do { 	\
+	pcb_cardinal_t	n;				\
+	TextTypePtr	text;				\
+	for (n = MAX_ELEMENTNAMES-1; n != -1; n--)	\
+	{						\
+		text = &(element)->Name[n]
+
+#define	ELEMENTNAME_LOOP(element) do	{ 			\
+	pcb_cardinal_t	n;					\
+	char		*textstring;				\
+	for (n = MAX_ELEMENTNAMES-1; n != -1; n--)		\
+	{							\
+		textstring = (element)->Name[n].TextString
+
+#define PIN_LOOP(element) do {                                      \
+  PinType *pin;                                                     \
+  gdl_iterator_t __it__;                                            \
+  pinlist_foreach(&(element)->Pin, &__it__, pin) {
+
+#define PAD_LOOP(element) do {                                      \
+  PadType *pad;                                                     \
+  gdl_iterator_t __it__;                                            \
+  padlist_foreach(&(element)->Pad, &__it__, pad) {
+
+#define ARC_LOOP(element) do {                                      \
+  ArcType *arc;                                                     \
+  gdl_iterator_t __it__;                                            \
+  linelist_foreach(&(element)->Arc, &__it__, arc) {
+
+#define ELEMENTLINE_LOOP(element) do {                              \
+  LineType *line;                                                   \
+  gdl_iterator_t __it__;                                            \
+  linelist_foreach(&(element)->Line, &__it__, line) {
+
+#define ELEMENTARC_LOOP(element) do {                               \
+  ArcType *arc;                                                     \
+  gdl_iterator_t __it__;                                            \
+  linelist_foreach(&(element)->Arc, &__it__, arc) {
+
+#define LINE_LOOP(layer) do {                                       \
+  LineType *line;                                                   \
+  gdl_iterator_t __it__;                                            \
+  linelist_foreach(&(layer)->Line, &__it__, line) {
+
+#define TEXT_LOOP(layer) do {                                       \
+  TextType *text;                                                   \
+  gdl_iterator_t __it__;                                            \
+  linelist_foreach(&(layer)->Text, &__it__, text) {
+
+#define POLYGON_LOOP(layer) do {                                    \
+  PolygonType *polygon;                                             \
+  gdl_iterator_t __it__;                                            \
+  linelist_foreach(&(layer)->Polygon, &__it__, polygon) {
+
+#define	POLYGONPOINT_LOOP(polygon) do	{	\
+	pcb_cardinal_t			n;		\
+	PointTypePtr	point;				\
+	for (n = (polygon)->PointN-1; n != -1; n--)	\
+	{						\
+		point = &(polygon)->Points[n]
+
+#define ENDALL_LOOP }} while (0); }} while(0)
+
+#define	ALLPIN_LOOP(top)	\
+        ELEMENT_LOOP(top); \
+	        PIN_LOOP(element)\
+
+#define	ALLPAD_LOOP(top) \
+	ELEMENT_LOOP(top); \
+	  PAD_LOOP(element)
+
+#define	ALLLINE_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+		LINE_LOOP(layer)
+
+#define ALLARC_LOOP(top) do {		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l =0; l < max_copper_layer + 2; l++, layer++)		\
+	{ \
+		ARC_LOOP(layer)
+
+#define	ALLPOLYGON_LOOP(top)	do {		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+		POLYGON_LOOP(layer)
+
+#define	COPPERLINE_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer; l++, layer++)	\
+	{ \
+		LINE_LOOP(layer)
+
+#define COPPERARC_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l =0; l < max_copper_layer; l++, layer++)		\
+	{ \
+		ARC_LOOP(layer)
+
+#define	COPPERPOLYGON_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer; l++, layer++)	\
+	{ \
+		POLYGON_LOOP(layer)
+
+#define	SILKLINE_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	layer += max_copper_layer;			\
+	for (l = 0; l < 2; l++, layer++)		\
+	{ \
+		LINE_LOOP(layer)
+
+#define SILKARC_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	layer += max_copper_layer;			\
+	for (l = 0; l < 2; l++, layer++)		\
+	{ \
+		ARC_LOOP(layer)
+
+#define	SILKPOLYGON_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	layer += max_copper_layer;			\
+	for (l = 0; l < 2; l++, layer++)		\
+	{ \
+		POLYGON_LOOP(layer)
+
+#define	ALLTEXT_LOOP(top)	do {		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+		TEXT_LOOP(layer)
+
+#define	VISIBLELINE_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+		if (layer->On)				\
+			LINE_LOOP(layer)
+
+#define	VISIBLEARC_LOOP(top) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+		if (layer->On)				\
+			ARC_LOOP(layer)
+
+#define	VISIBLETEXT_LOOP(board) do	{		\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (board)->Data->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+                TEXT_LOOP(layer);                                      \
+                  if (TEXT_IS_VISIBLE((board), layer, text))
+
+#define	VISIBLEPOLYGON_LOOP(top) do	{	\
+	pcb_cardinal_t		l;			\
+	LayerTypePtr	layer = (top)->Layer;		\
+	for (l = 0; l < max_copper_layer + 2; l++, layer++)	\
+	{ \
+		if (layer->On)				\
+			POLYGON_LOOP(layer)
+
+#define POINTER_LOOP(top) do	{	\
+	pcb_cardinal_t	n;			\
+	void	**ptr;				\
+	for (n = (top)->PtrN-1; n != -1; n--)	\
+	{					\
+		ptr = &(top)->Ptr[n]
+
+#define MENU_LOOP(top)	do {	\
+	pcb_cardinal_t	l;			\
+	LibraryMenuTypePtr menu;		\
+	for (l = (top)->MenuN-1; l != -1; l--)	\
+	{					\
+		menu = &(top)->Menu[l]
+
+#define ENTRY_LOOP(top) do	{	\
+	pcb_cardinal_t	n;			\
+	LibraryEntryTypePtr entry;		\
+	for (n = (top)->EntryN-1; n != -1; n--)	\
+	{					\
+		entry = &(top)->Entry[n]
+
+#define GROUP_LOOP(data, group) do { 	\
+	pcb_cardinal_t entry; \
+        for (entry = 0; entry < ((PCBTypePtr)(data->pcb))->LayerGroups.Number[(group)]; entry++) \
+        { \
+		LayerTypePtr layer;		\
+		pcb_cardinal_t number; 		\
+		number = ((PCBTypePtr)(data->pcb))->LayerGroups.Entries[(group)][entry]; \
+		if (number >= max_copper_layer)	\
+		  continue;			\
+		layer = &data->Layer[number];
+
+#define LAYER_LOOP(data, ml) do { \
+        pcb_cardinal_t n; \
+	for (n = 0; n < ml; n++) \
+	{ \
+	   LayerTypePtr layer = (&data->Layer[(n)]);
+
+
+#define LAYER_IS_EMPTY(layer) LAYER_IS_EMPTY_((layer))
+#define LAYER_IS_EMPTY_(layer) \
+ ((linelist_length(&layer->Line) == 0) && (arclist_length(&layer->Arc) == 0) && (polylist_length(&layer->Polygon) == 0) && (textlist_length(&layer->Text) == 0))
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..efed6b1
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,522 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2004 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* main program, initializes some stuff and handles user input
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <time.h>								/* Seed for srand() */
+#include <locale.h>
+
+#include "data.h"
+#include "buffer.h"
+#include "create.h"
+#include "crosshair.h"
+#include "error.h"
+#include "plug_io.h"
+#include "set.h"
+#include "layer.h"
+#include "misc.h"
+#include "compat_lrealpath.h"
+#include "free_atexit.h"
+#include "polygon.h"
+#include "buildin.h"
+#include "paths.h"
+#include "strflags.h"
+#include "plugins.h"
+#include "plug_footprint.h"
+#include "event.h"
+#include "funchash.h"
+#include "conf.h"
+#include "conf_core.h"
+
+#include "hid_actions.h"
+#include "hid_attrib.h"
+#include "hid_init.h"
+#include "compat_misc.h"
+
+#if ENABLE_NLS
+#include <libintl.h>
+#endif
+
+/* ----------------------------------------------------------------------
+ * initialize signal and error handlers
+ */
+static void InitHandler(void)
+{
+#ifdef PCB_HAVE_SIGHUP
+	signal(SIGHUP, CatchSignal);
+#endif
+#ifdef PCB_HAVE_SIGQUIT
+	signal(SIGQUIT, CatchSignal);
+#endif
+#ifdef PCB_HAVE_SIGTERM
+	signal(SIGTERM, CatchSignal);
+#endif
+#ifdef PCB_HAVE_SIGINT
+	signal(SIGINT, CatchSignal);
+#endif
+
+#ifdef NDEBUG
+/* so that we get a core dump on segfault in debug mode */
+#	ifdef PCB_HAVE_SIGABRT
+		signal(SIGABRT, CatchSignal);
+#	endif
+#	ifdef PCB_HAVE_SIGSEGV
+		signal(SIGSEGV, CatchSignal);
+#	endif
+#endif
+
+	/* calling external program by popen() may cause a PIPE signal,
+	 * so we ignore it
+	 */
+#ifdef SIGPIPE
+	signal(SIGPIPE, SIG_IGN);
+#endif
+}
+
+/* ----------------------------------------------------------------------
+ * Figure out the canonical name of the executed program
+ * and fix up the defaults for various paths
+ */
+
+static void InitPaths(char *argv0)
+{
+	size_t l;
+	int haspath;
+	char *t1, *t2;
+	int found_bindir = 0;
+	char *exec_prefix = NULL;
+	char *bindir = NULL;
+
+
+	/* see if argv0 has enough of a path to let lrealpath give the
+	 * real path.  This should be the case if you invoke pcb with
+	 * something like /usr/local/bin/pcb or ./pcb or ./foo/pcb
+	 * but if you just use pcb and it exists in your path, you'll
+	 * just get back pcb again.
+	 */
+
+#ifndef NO_BINDIR_HEURISTICS
+	{
+		int i;
+		haspath = 0;
+		for (i = 0; i < strlen(argv0); i++) {
+			if (argv0[i] == PCB_DIR_SEPARATOR_C)
+				haspath = 1;
+		}
+	}
+#endif
+
+#ifdef DEBUG
+	printf("InitPaths (%s): haspath = %d\n", argv0, haspath);
+#endif
+
+	if (haspath) {
+#ifndef NO_BINDIR_HEURISTICS
+		bindir = pcb_strdup(lrealpath(argv0));
+#endif
+		found_bindir = 1;
+	}
+	else {
+		char *path, *p, *tmps;
+		struct stat sb;
+		int r;
+
+		tmps = getenv("PATH");
+
+		if (tmps != NULL) {
+			path = pcb_strdup(tmps);
+
+			/* search through the font path for a font file */
+			for (p = strtok(path, PCB_PATH_DELIMETER); p && *p; p = strtok(NULL, PCB_PATH_DELIMETER)) {
+#ifdef DEBUG
+				printf("Looking for %s in %s\n", argv0, p);
+#endif
+				if ((tmps = (char *) malloc((strlen(argv0) + strlen(p) + 2) * sizeof(char))) == NULL) {
+					fprintf(stderr, "InitPaths():  malloc failed\n");
+					exit(1);
+				}
+				sprintf(tmps, "%s%s%s", p, PCB_DIR_SEPARATOR_S, argv0);
+				r = stat(tmps, &sb);
+				if (r == 0) {
+#ifdef DEBUG
+					printf("Found it:  \"%s\"\n", tmps);
+#endif
+					bindir = lrealpath(tmps);
+					found_bindir = 1;
+					free(tmps);
+					break;
+				}
+				free(tmps);
+			}
+			free(path);
+		}
+	}
+
+	if (found_bindir) {
+		/* strip off the executable name leaving only the path */
+		t2 = NULL;
+		t1 = strchr(bindir, PCB_DIR_SEPARATOR_C);
+		while (t1 != NULL && *t1 != '\0') {
+			t2 = t1;
+			t1 = strchr(t2 + 1, PCB_DIR_SEPARATOR_C);
+		}
+		if (t2 != NULL)
+			*t2 = '\0';
+	}
+	else {
+		/* we have failed to find out anything from argv[0] so fall back to the original
+		 * install prefix
+		 */
+		bindir = pcb_strdup(BINDIR);
+	}
+
+	/* now find the path to exec_prefix */
+	l = strlen(bindir) + 1 + strlen(BINDIR_TO_EXECPREFIX) + 1;
+	if ((exec_prefix = (char *) malloc(l * sizeof(char))) == NULL) {
+		fprintf(stderr, "InitPaths():  malloc failed\n");
+		exit(1);
+	}
+	sprintf(exec_prefix, "%s%s%s", bindir, PCB_DIR_SEPARATOR_S, BINDIR_TO_EXECPREFIX);
+	conf_set(CFR_INTERNAL, "rc/path/exec_prefix", -1, exec_prefix, POL_OVERWRITE);
+	free(exec_prefix);
+	free(bindir);
+}
+
+/* ----------------------------------------------------------------------
+ * main program
+ */
+
+#include "dolists.h"
+
+static char **hid_argv_orig;
+
+void pcb_main_uninit(void)
+{
+	if (conf_isdirty(CFR_USER))
+		conf_save_file(NULL, NULL, CFR_USER, NULL);
+
+	UninitBuffers();
+
+	/* Free up memory allocated to the PCB. Why bother when we're about to exit ?
+	 * Because it removes some false positives from heap bug detectors such as
+	 * lib dmalloc.
+	 */
+	FreePCBMemory(PCB);
+	free(PCB);
+	PCB = NULL;
+
+	plugins_uninit();
+	hid_uninit();
+	events_uninit();
+
+	uninit_strflags_buf();
+	uninit_strflags_layerlist();
+
+	fp_uninit();
+	fp_host_uninit();
+	funchash_uninit();
+	free(hid_argv_orig);
+	conf_uninit();
+}
+
+static int arg_match(const char *in, const char *shrt, const char *lng)
+{
+	return ((shrt != NULL) && (strcmp(in, shrt) == 0)) || ((lng != NULL) && (strcmp(in, lng) == 0));
+}
+
+const char *pcb_action_args[] = {
+/*short, -long, action, help */
+	NULL, "-show-actions", "PrintActions()",     "Print all available actions (human readable) and exit",
+	NULL, "-dump-actions", "DumpActions()",      "Print all available actions (script readable) and exit",
+	NULL, "-show-paths",   "PrintPaths()",       "Print all configured paths and exit",
+	NULL, "-dump-config",  "dumpconf(native,1)", "Print the config tree and exit",
+	"V",  "-version",      "PrintVersion()",     "Print version info and exit",
+	NULL, "-copyright",    "PrintCopyright()",   "Print copyright and exit",
+	NULL, NULL, NULL /* terminator */
+};
+
+int main(int argc, char *argv[])
+{
+	enum {
+		DO_SOMETHING,
+		DO_PRINT,
+		DO_EXPORT,
+		DO_GUI
+	} do_what = DO_SOMETHING;
+	int n, hid_argc = 0;
+	char *cmd, *arg, *hid_name = NULL, **hid_argv;
+	const char **cs;
+	const char *main_action = NULL;
+	char *command_line_pcb = NULL;
+
+	hid_argv_orig = hid_argv = calloc(sizeof(char *), argc);
+	/* init application:
+	 * - make program name available for error handlers
+	 * - initialize infrastructure (e.g. the conf system)
+	 * - evaluate options
+	 * - create an empty PCB with default symbols
+	 * - register 'call on exit()' function
+	 */
+
+	/* Minimal conf setup before we do anything else */
+	conf_init();
+	conf_core_init();
+	conf_core_postproc(); /* to get all the paths initialized */
+
+	/* process arguments */
+	for(n = 1; n < argc; n++) {
+		cmd = argv[n];
+		arg = argv[n+1];
+		if (*cmd == '-') {
+			cmd++;
+			if ((strcmp(cmd, "?") == 0) || (strcmp(cmd, "h") == 0) || (strcmp(cmd, "-help") == 0)) {
+				if (arg != NULL) {
+					/* memory leak, but who cares for --help? */
+					main_action = pcb_strdup_printf("PrintUsage(%s)", arg);
+					n++;
+				}
+				else
+					main_action = "PrintUsage()";
+				goto next_arg;
+			}
+			if ((strcmp(cmd, "g") == 0) || (strcmp(cmd, "-gui") == 0)) {
+				do_what = DO_GUI;
+				hid_name = arg;
+				n++;
+				goto next_arg;
+			}
+			if ((strcmp(cmd, "x") == 0) || (strcmp(cmd, "-export") == 0)) {
+				do_what = DO_EXPORT;
+				hid_name = arg;
+				n++;
+				goto next_arg;
+			}
+			if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "-print") == 0)) {
+				do_what = DO_PRINT;
+				goto next_arg;
+			}
+
+			for(cs = pcb_action_args; cs[2] != NULL; cs += 4) {
+				if (arg_match(cmd, cs[0], cs[1])) {
+					if (main_action == NULL)
+						main_action = cs[2];
+					else
+						fprintf(stderr, "Warning: can't execute multiple command line actions, ignoring %s\n", argv[n]);
+					goto next_arg;
+				}
+			}
+			if (arg_match(cmd, "c", "-conf")) {
+				const char *why;
+				n++; /* eat up arg */
+				if (conf_set_from_cli(NULL, arg, NULL, &why) != 0) {
+					fprintf(stderr, "Error: failed to set config %s: %s\n", arg, why);
+					exit(1);
+				}
+				goto next_arg;
+			}
+		}
+
+		/* didn't handle argument, save it for the HID */
+		hid_argv[hid_argc++] = argv[n];
+
+		next_arg:;
+	}
+	conf_load_all(NULL, NULL);
+
+	setbuf(stdout, 0);
+	InitPaths(argv[0]);
+
+	fp_init();
+
+#ifdef LOCALEDIR
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	textdomain(GETTEXT_PACKAGE);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	setlocale(LC_ALL, "");
+#endif
+
+	srand(time(NULL));						/* Set seed for rand() */
+
+	funchash_init();
+	initialize_units();
+	polygon_init();
+
+	/* This must be called before any other atexit functions
+	 * are registered, as it configures an atexit function to
+	 * clean up and free various items of allocated memory,
+	 * and must be the last last atexit function to run.
+	 */
+	leaky_init();
+
+	/* Register a function to be called when the program terminates.
+	 * This makes sure that data is saved even if LEX/YACC routines
+	 * abort the program.
+	 * If the OS doesn't have at least one of them,
+	 * the critical sections will be handled by parse_l.l
+	 */
+	atexit(EmergencySave);
+
+	events_init();
+
+	buildin_init();
+	hid_init();
+	plugins_init();
+
+
+	/* Export pcb from command line if requested.  */
+	switch(do_what) {
+		case DO_PRINT:   exporter = gui = hid_find_printer(); break;
+		case DO_EXPORT:  exporter = gui = hid_find_exporter(hid_name); break;
+		case DO_GUI:
+			gui = hid_find_gui(argv[2]);
+			if (gui == NULL) {
+				Message(PCB_MSG_DEFAULT, "Can't find the gui requested.\n");
+				exit(1);
+			}
+			break;
+		default: {
+			conf_listitem_t *i;
+			int n;
+			const char *g;
+
+			gui = NULL;
+			conf_loop_list_str(&conf_core.rc.preferred_gui, i, g, n) {
+				gui = hid_find_gui(g);
+				if (gui != NULL)
+					break;
+			}
+
+			/* try anything */
+			if (gui == NULL) {
+				Message(PCB_MSG_DEFAULT, "Warning: can't find any of the preferred GUIs, falling back to anything available...\n");
+				gui = hid_find_gui(NULL);
+			}
+		}
+	}
+
+	/* Exit with error if GUI failed to start. */
+	if (!gui)
+		exit(1);
+
+/* Initialize actions only when the gui is already known so only the right
+   one is registered (there can be only one GUI). */
+#include "generated_lists.h"
+
+	/* plugins may have installed their new fields, reinterpret the config
+	   (memory lht -> memory bin) to get the new fields */
+	conf_update(NULL);
+
+	if (main_action != NULL) {
+		hid_parse_command(main_action);
+		exit(0);
+	}
+
+	gui->parse_arguments(&hid_argc, &hid_argv);
+
+	/* Create a new PCB object in memory */
+	PCB = CreateNewPCB();
+
+	if (PCB == NULL) {
+		Message(PCB_MSG_ERROR, "Can't load the default pcb for creating an empty layout\n");
+		exit(1);
+	}
+
+	/* Add silk layers to newly created PCB */
+	CreateNewPCBPost(PCB, 1);
+	if (hid_argc > 0)
+		command_line_pcb = hid_argv[0];
+
+	ResetStackAndVisibility();
+
+	if (gui->gui)
+		InitCrosshair();
+	InitHandler();
+	InitBuffers();
+
+	SetMode(PCB_MODE_ARROW);
+
+	if (command_line_pcb) {
+		/* keep filename even if initial load command failed;
+		 * file might not exist
+		 */
+		if (LoadPCB(command_line_pcb, NULL, pcb_true, 0))
+			PCB->Filename = pcb_strdup(command_line_pcb);
+	}
+
+	if (conf_core.design.initial_layer_stack && conf_core.design.initial_layer_stack[0]) {
+		LayerStringToLayerStack(conf_core.design.initial_layer_stack);
+	}
+
+	/* read the library file and display it if it's not empty
+	 */
+	if (!fp_read_lib_all() && library.data.dir.children.used)
+		hid_action("LibraryChanged");
+
+	if (conf_core.rc.script_filename) {
+		Message(PCB_MSG_DEFAULT, _("Executing startup script file %s\n"), conf_core.rc.script_filename);
+		hid_actionl("ExecuteFile", conf_core.rc.script_filename, NULL);
+	}
+	if (conf_core.rc.action_string) {
+		Message(PCB_MSG_DEFAULT, _("Executing startup action %s\n"), conf_core.rc.action_string);
+		hid_parse_actions(conf_core.rc.action_string);
+	}
+
+	if (gui->printer || gui->exporter) {
+		/* Workaround to fix batch output for non-C locales */
+		setlocale(LC_NUMERIC, "C");
+		gui->do_export(0);
+		exit(0);
+	}
+
+	EnableAutosave();
+
+	/* main loop */
+	do {
+		gui->do_export(0);
+		gui = next_gui;
+		next_gui = NULL;
+		if (gui != NULL) {
+			/* init the next GUI */
+			gui->parse_arguments(&hid_argc, &hid_argv);
+			if (gui->gui)
+				InitCrosshair();
+			SetMode(PCB_MODE_ARROW);
+				hid_action("LibraryChanged");
+		}
+	} while(gui != NULL);
+
+	pcb_main_uninit();
+
+	return (0);
+}
diff --git a/src/main_act.c b/src/main_act.c
new file mode 100644
index 0000000..0ab5d6b
--- /dev/null
+++ b/src/main_act.c
@@ -0,0 +1,271 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+#define Progname "pcb-rnd"
+
+#include "config.h"
+
+#include "action_helper.h"
+#include "hid_actions.h"
+#include "hid_attrib.h"
+#include "hid_init.h"
+#include "hid.h"
+#include "data.h"
+#include "conf_core.h"
+
+
+/* --------------------------------------------------------------------------- */
+
+static const char printactions_syntax[] = "PrintActions()";
+
+static const char printactions_help[] = "Print all actions available.";
+
+int ActionPrintActions(int argc, const char **argv, Coord x, Coord y)
+{
+	print_actions();
+	return 0;
+}
+/* --------------------------------------------------------------------------- */
+
+static const char dumpactions_syntax[] = "DumpActions()";
+
+static const char dumpactions_help[] = "Dump all actions available.";
+
+int ActionDumpActions(int argc, const char **argv, Coord x, Coord y)
+{
+	dump_actions();
+	return 0;
+}
+
+/* print usage lines */
+static inline void u(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	fputc('\n', stderr);
+	va_end(ap);
+}
+
+static const char printusage_syntax[] =
+	"PrintUsage()\n"
+	"PrintUsage(plugin)";
+
+static const char printusage_help[] = "Print command line arguments of pcb-rnd or a plugin loaded.";
+
+static int help0(void)
+{
+	HID **hl = hid_enumerate();
+	int i;
+
+	u("pcb-rnd Printed Circuit Board editing program, http://repo.hu/projects/pcb-rnd");
+	u("For more information, please read the topic help pages:", Progname);
+	u("  %s --help topic");
+	u("Topics are:");
+	u("  invocation           how to run pcb-rnd");
+	u("  main                 main/misc flags (affecting none or all plugins)");
+	for (i = 0; hl[i]; i++)
+		if (hl[i]->usage != NULL)
+			u("  %-20s %s", hl[i]->name, hl[i]->description);
+	return 0;
+}
+
+extern const char *pcb_action_args[];
+static int help_main(void) {
+	const char **cs;
+	for(cs = pcb_action_args; cs[2] != NULL; cs += 4) {
+		fprintf(stderr, "%s [", Progname);
+		if (cs[0] != NULL)
+			fprintf(stderr, "-%s", cs[0]);
+		if ((cs[0] != NULL) && (cs[1] != NULL))
+			fprintf(stderr, "|");
+		if (cs[1] != NULL)
+			fprintf(stderr, "-%s", cs[1]);
+		fprintf(stderr, "]    %s\n", cs[3]);
+	}
+	return 0;
+}
+
+static int help_invoc(void)
+{
+	HID **hl = hid_enumerate();
+	int i;
+	int n_printer = 0, n_gui = 0, n_exporter = 0;
+
+	u("pcb-rnd invocation:");
+	u("");
+	u("%s [main options]                                    See --help main", Progname);
+	u("");
+	u("%s [generics] [--gui GUI] [gui options] <pcb file>   interactive GUI", Progname);
+
+	u("Available GUI hid%s:", n_gui == 1 ? "" : "s");
+	for (i = 0; hl[i]; i++)
+		if (hl[i]->gui)
+			fprintf(stderr, "\t%-8s %s\n", hl[i]->name, hl[i]->description);
+
+	u("\n%s [generics] -p [printing options] <pcb file>\tto print", Progname);
+	u("Available printing hid%s:", n_printer == 1 ? "" : "s");
+	for (i = 0; hl[i]; i++)
+		if (hl[i]->printer)
+			fprintf(stderr, "\t%-8s %s\n", hl[i]->name, hl[i]->description);
+
+	u("\n%s [generics] -x hid [export options] <pcb file>\tto export", Progname);
+	u("Available export hid%s:", n_exporter == 1 ? "" : "s");
+	for (i = 0; hl[i]; i++)
+		if (hl[i]->exporter)
+			fprintf(stderr, "\t%-8s %s\n", hl[i]->name, hl[i]->description);
+
+
+	u("\nGenerics:");
+	u("-c conf/path=value        set the value of a configuration item (in CFR_CLI)");
+
+	return 0;
+}
+
+int ActionPrintUsage(int argc, const char **argv, Coord x, Coord y)
+{
+	u("");
+	if (argc > 0) {
+		HID **hl = hid_enumerate();
+		int i;
+
+		if (strcmp(argv[0], "invocation") == 0)  return help_invoc();
+		if (strcmp(argv[0], "main") == 0)        return help_main();
+
+		for (i = 0; hl[i]; i++) {
+			if ((hl[i]->usage != NULL) && (strcmp(argv[0], hl[i]->name) == 0)) {
+				if (argc > 1)
+					return hl[i]->usage(argv[1]);
+				else
+					return hl[i]->usage(NULL);
+			}
+		}
+		fprintf(stderr, "No help available for %s\n", argv[0]);
+		return -1;
+	}
+	else
+		help0();
+	return 0;
+}
+
+
+/* --------------------------------------------------------------------------- */
+static const char printversion_syntax[] = "PrintVersion()";
+
+static const char printversion_help[] = "Print version.";
+
+int ActionPrintVersion(int argc, const char **argv, Coord x, Coord y)
+{
+	printf("PCB version %s\n", VERSION);
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+static const char printcopyright_syntax[] = "PrintCopyright()";
+
+static const char printcopyright_help[] = "Print copyright notice.";
+
+int ActionPrintCopyright(int argc, const char **argv, Coord x, Coord y)
+{
+	printf("\n"
+				 "                COPYRIGHT for the original pcb program:\n\n"
+				 "    PCB, interactive printed circuit board design\n"
+				 "    Copyright (C) 1994,1995,1996,1997 Thomas Nau\n"
+				 "    Copyright (C) 1998, 1999, 2000 Harry Eaton\n\n");
+	printf("                COPYRIGHT for %s (pcb-rnd) version %s:\n"
+				 "    pcb-rnd, a fork of PCB with random improvements\n"
+				 "    Copyright (C) 2013, 2014, 2015, 2016 Tibor 'Igor2' Palinkas\n\n", Progname, VERSION);
+	printf("    This program is free software; you can redistribute it and/or modify\n"
+				 "    it under the terms of the GNU General Public License as published by\n"
+				 "    the Free Software Foundation; either version 2 of the License, or\n"
+				 "    (at your option) any later version.\n\n");
+	printf("    This program is distributed in the hope that it will be useful,\n"
+				 "    but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+				 "    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+				 "    GNU General Public License for more details.\n\n");
+	printf("    You should have received a copy of the GNU General Public License\n"
+				 "    along with this program; if not, write to the Free Software\n"
+				 "    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+static const char printpaths_syntax[] = "PrintPaths()";
+
+static const char printpaths_help[] = "Print full paths and search paths.";
+
+static void print_list(const conflist_t *cl)
+{
+	int n;
+	conf_listitem_t *ci;
+	const char *p;
+
+	printf(" ");
+	conf_loop_list_str(cl, ci, p, n) {
+		printf("%c%s", (n == 0) ? '"' : ':', p);
+	}
+	printf("\"\n");
+}
+
+int ActionPrintPaths(int argc, const char **argv, Coord x, Coord y)
+{
+	htsp_entry_t *e;
+	conf_fields_foreach(e) {
+		conf_native_t *n = e->value;
+		if ((strncmp(n->hash_path, "rc/path/", 8) == 0) && (n->type == CFN_STRING) && (n->used == 1))
+			printf("%-32s = %s\n", n->hash_path, n->val.string[0]);
+	}
+	printf("rc/default_font_file             ="); print_list(&conf_core.rc.default_font_file);
+	printf("rc/library_search_paths          ="); print_list(&conf_core.rc.library_search_paths);
+	printf("rc/library_shell                 = \"%s\"\n", conf_core.rc.library_shell);
+	return 0;
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+HID_Action main_action_list[] = {
+	{"PrintActions", 0, ActionPrintActions,
+	 printactions_help, printactions_syntax}
+	,
+	{"DumpActions", 0, ActionDumpActions,
+	 dumpactions_help, dumpactions_syntax}
+	,
+	{"PrintUsage", 0, ActionPrintUsage,
+	 printusage_help, printusage_syntax}
+	,
+	{"PrintVersion", 0, ActionPrintVersion,
+	 printversion_help, printversion_syntax}
+	,
+	{"PrintCopyright", 0, ActionPrintCopyright,
+	 printcopyright_help, printcopyright_syntax}
+	,
+	{"PrintPaths", 0, ActionPrintPaths,
+	 printpaths_help, printpaths_syntax}
+};
+
+REGISTER_ACTIONS(main_action_list, NULL)
diff --git a/src/mirror.c b/src/mirror.c
new file mode 100644
index 0000000..6b4c356
--- /dev/null
+++ b/src/mirror.c
@@ -0,0 +1,98 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* functions used to change the mirror flag of an object
+ *
+ * an undo operation is not implemented because it's easy to
+ * recover an object
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "data.h"
+#include "mirror.h"
+#include "misc.h"
+#include "polygon.h"
+
+/* ---------------------------------------------------------------------------
+ * mirrors the coordinates of an element
+ * an additional offset is passed
+ */
+void MirrorElementCoordinates(DataTypePtr Data, ElementTypePtr Element, Coord yoff)
+{
+	r_delete_element(Data, Element);
+	ELEMENTLINE_LOOP(Element);
+	{
+		line->Point1.X = SWAP_X(line->Point1.X);
+		line->Point1.Y = SWAP_Y(line->Point1.Y) + yoff;
+		line->Point2.X = SWAP_X(line->Point2.X);
+		line->Point2.Y = SWAP_Y(line->Point2.Y) + yoff;
+	}
+	END_LOOP;
+	PIN_LOOP(Element);
+	{
+		RestoreToPolygon(Data, PCB_TYPE_PIN, Element, pin);
+		pin->X = SWAP_X(pin->X);
+		pin->Y = SWAP_Y(pin->Y) + yoff;
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		RestoreToPolygon(Data, PCB_TYPE_PAD, Element, pad);
+		pad->Point1.X = SWAP_X(pad->Point1.X);
+		pad->Point1.Y = SWAP_Y(pad->Point1.Y) + yoff;
+		pad->Point2.X = SWAP_X(pad->Point2.X);
+		pad->Point2.Y = SWAP_Y(pad->Point2.Y) + yoff;
+		TOGGLE_FLAG(PCB_FLAG_ONSOLDER, pad);
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		arc->X = SWAP_X(arc->X);
+		arc->Y = SWAP_Y(arc->Y) + yoff;
+		arc->StartAngle = SWAP_ANGLE(arc->StartAngle);
+		arc->Delta = SWAP_DELTA(arc->Delta);
+	}
+	END_LOOP;
+	ELEMENTTEXT_LOOP(Element);
+	{
+		text->X = SWAP_X(text->X);
+		text->Y = SWAP_Y(text->Y) + yoff;
+		TOGGLE_FLAG(PCB_FLAG_ONSOLDER, text);
+	}
+	END_LOOP;
+	Element->MarkX = SWAP_X(Element->MarkX);
+	Element->MarkY = SWAP_Y(Element->MarkY) + yoff;
+
+	/* now toggle the solder-side flag */
+	TOGGLE_FLAG(PCB_FLAG_ONSOLDER, Element);
+	/* this inserts all of the rtree data too */
+	SetElementBoundingBox(Data, Element, &PCB->Font);
+	ClearFromPolygon(Data, PCB_TYPE_ELEMENT, Element, Element);
+}
diff --git a/src/mirror.h b/src/mirror.h
new file mode 100644
index 0000000..9488a84
--- /dev/null
+++ b/src/mirror.h
@@ -0,0 +1,44 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes to change objects mirror flag */
+
+#ifndef	PCB_MIRROR_H
+#define	PCB_MIRROR_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some useful macros and defines
+ */
+#define	MIRROR(object)	TOGGLE_FLAG(MIRRORFLAG, (object))
+
+#define	MIRROR_TYPES	(PCB_TYPE_TEXT | PCB_TYPE_ELEMENT_NAME)
+
+
+void MirrorElementCoordinates(DataTypePtr, ElementTypePtr, Coord);
+
+#endif
diff --git a/src/misc.c b/src/misc.c
new file mode 100644
index 0000000..085d31b
--- /dev/null
+++ b/src/misc.c
@@ -0,0 +1,1310 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* misc functions used by several modules */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+#include <signal.h>
+#include <math.h>
+
+#include "box.h"
+#include "crosshair.h"
+#include "data.h"
+#include "plug_io.h"
+#include "error.h"
+#include "misc.h"
+#include "move.h"
+#include "polygon.h"
+#include "rtree.h"
+#include "rotate.h"
+#include "rubberband.h"
+#include "set.h"
+#include "undo.h"
+#include "compat_misc.h"
+#include "hid_actions.h"
+#include "hid_init.h"
+
+/* forward declarations */
+static char *BumpName(char *);
+static void GetGridLockCoordinates(int, void *, void *, void *, Coord *, Coord *);
+
+/* Local variables */
+
+/* Bring an angle into [0, 360) range */
+Angle NormalizeAngle(Angle a)
+{
+	while (a < 0)
+		a += 360.0;
+	while (a >= 360.0)
+		a -= 360.0;
+	return a;
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the bounding box of a point (which is silly)
+ */
+void SetPointBoundingBox(PointTypePtr Pnt)
+{
+	Pnt->X2 = Pnt->X + 1;
+	Pnt->Y2 = Pnt->Y + 1;
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the bounding box of a pin or via
+ */
+void SetPinBoundingBox(PinTypePtr Pin)
+{
+	Coord width;
+
+	if ((GET_SQUARE(Pin) > 1) && (TEST_FLAG(PCB_FLAG_SQUARE, Pin))) {
+		POLYAREA *p = PinPoly(Pin, PIN_SIZE(Pin), Pin->Clearance);
+		poly_bbox(p, &Pin->BoundingBox);
+		poly_Free(&p);
+	}
+
+	/* the bounding box covers the extent of influence
+	 * so it must include the clearance values too
+	 */
+	width = MAX(Pin->Clearance + PIN_SIZE(Pin), Pin->Mask) / 2;
+
+	/* Adjust for our discrete polygon approximation */
+	width = (double) width *POLY_CIRC_RADIUS_ADJ + 0.5;
+
+	Pin->BoundingBox.X1 = Pin->X - width;
+	Pin->BoundingBox.Y1 = Pin->Y - width;
+	Pin->BoundingBox.X2 = Pin->X + width;
+	Pin->BoundingBox.Y2 = Pin->Y + width;
+	close_box(&Pin->BoundingBox);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the bounding box of a pad
+ */
+void SetPadBoundingBox(PadTypePtr Pad)
+{
+	Coord width;
+	Coord deltax;
+	Coord deltay;
+
+	/* the bounding box covers the extent of influence
+	 * so it must include the clearance values too
+	 */
+	width = (Pad->Thickness + Pad->Clearance + 1) / 2;
+	width = MAX(width, (Pad->Mask + 1) / 2);
+	deltax = Pad->Point2.X - Pad->Point1.X;
+	deltay = Pad->Point2.Y - Pad->Point1.Y;
+
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Pad) && deltax != 0 && deltay != 0) {
+		/* slanted square pad */
+		double theta;
+		Coord btx, bty;
+
+		/* T is a vector half a thickness long, in the direction of
+		   one of the corners.  */
+		theta = atan2(deltay, deltax);
+		btx = width * cos(theta + M_PI / 4) * sqrt(2.0);
+		bty = width * sin(theta + M_PI / 4) * sqrt(2.0);
+
+
+		Pad->BoundingBox.X1 = MIN(MIN(Pad->Point1.X - btx, Pad->Point1.X - bty), MIN(Pad->Point2.X + btx, Pad->Point2.X + bty));
+		Pad->BoundingBox.X2 = MAX(MAX(Pad->Point1.X - btx, Pad->Point1.X - bty), MAX(Pad->Point2.X + btx, Pad->Point2.X + bty));
+		Pad->BoundingBox.Y1 = MIN(MIN(Pad->Point1.Y + btx, Pad->Point1.Y - bty), MIN(Pad->Point2.Y - btx, Pad->Point2.Y + bty));
+		Pad->BoundingBox.Y2 = MAX(MAX(Pad->Point1.Y + btx, Pad->Point1.Y - bty), MAX(Pad->Point2.Y - btx, Pad->Point2.Y + bty));
+	}
+	else {
+		/* Adjust for our discrete polygon approximation */
+		width = (double) width *POLY_CIRC_RADIUS_ADJ + 0.5;
+
+		Pad->BoundingBox.X1 = MIN(Pad->Point1.X, Pad->Point2.X) - width;
+		Pad->BoundingBox.X2 = MAX(Pad->Point1.X, Pad->Point2.X) + width;
+		Pad->BoundingBox.Y1 = MIN(Pad->Point1.Y, Pad->Point2.Y) - width;
+		Pad->BoundingBox.Y2 = MAX(Pad->Point1.Y, Pad->Point2.Y) + width;
+	}
+	close_box(&Pad->BoundingBox);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the bounding box of a line
+ */
+void SetLineBoundingBox(LineTypePtr Line)
+{
+	Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
+
+	/* Adjust for our discrete polygon approximation */
+	width = (double) width *POLY_CIRC_RADIUS_ADJ + 0.5;
+
+	Line->BoundingBox.X1 = MIN(Line->Point1.X, Line->Point2.X) - width;
+	Line->BoundingBox.X2 = MAX(Line->Point1.X, Line->Point2.X) + width;
+	Line->BoundingBox.Y1 = MIN(Line->Point1.Y, Line->Point2.Y) - width;
+	Line->BoundingBox.Y2 = MAX(Line->Point1.Y, Line->Point2.Y) + width;
+	close_box(&Line->BoundingBox);
+	SetPointBoundingBox(&Line->Point1);
+	SetPointBoundingBox(&Line->Point2);
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the bounding box of a polygons
+ */
+void SetPolygonBoundingBox(PolygonTypePtr Polygon)
+{
+	Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
+	Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
+	POLYGONPOINT_LOOP(Polygon);
+	{
+		MAKEMIN(Polygon->BoundingBox.X1, point->X);
+		MAKEMIN(Polygon->BoundingBox.Y1, point->Y);
+		MAKEMAX(Polygon->BoundingBox.X2, point->X);
+		MAKEMAX(Polygon->BoundingBox.Y2, point->Y);
+	}
+	/* boxes don't include the lower right corner */
+	close_box(&Polygon->BoundingBox);
+	END_LOOP;
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the bounding box of an elements
+ */
+void SetElementBoundingBox(DataTypePtr Data, ElementTypePtr Element, FontTypePtr Font)
+{
+	BoxTypePtr box, vbox;
+
+	if (Data && Data->element_tree)
+		r_delete_entry(Data->element_tree, (BoxType *) Element);
+	/* first update the text objects */
+	ELEMENTTEXT_LOOP(Element);
+	{
+		if (Data && Data->name_tree[n])
+			r_delete_entry(Data->name_tree[n], (BoxType *) text);
+		SetTextBoundingBox(Font, text);
+		if (Data && !Data->name_tree[n])
+			Data->name_tree[n] = r_create_tree(NULL, 0, 0);
+		if (Data)
+			r_insert_entry(Data->name_tree[n], (BoxType *) text, 0);
+	}
+	END_LOOP;
+
+	/* do not include the elementnames bounding box which
+	 * is handled separately
+	 */
+	box = &Element->BoundingBox;
+	vbox = &Element->VBox;
+	box->X1 = box->Y1 = MAX_COORD;
+	box->X2 = box->Y2 = 0;
+	ELEMENTLINE_LOOP(Element);
+	{
+		SetLineBoundingBox(line);
+		MAKEMIN(box->X1, line->Point1.X - (line->Thickness + 1) / 2);
+		MAKEMIN(box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
+		MAKEMIN(box->X1, line->Point2.X - (line->Thickness + 1) / 2);
+		MAKEMIN(box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
+		MAKEMAX(box->X2, line->Point1.X + (line->Thickness + 1) / 2);
+		MAKEMAX(box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
+		MAKEMAX(box->X2, line->Point2.X + (line->Thickness + 1) / 2);
+		MAKEMAX(box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		SetArcBoundingBox(arc);
+		MAKEMIN(box->X1, arc->BoundingBox.X1);
+		MAKEMIN(box->Y1, arc->BoundingBox.Y1);
+		MAKEMAX(box->X2, arc->BoundingBox.X2);
+		MAKEMAX(box->Y2, arc->BoundingBox.Y2);
+	}
+	END_LOOP;
+	*vbox = *box;
+	PIN_LOOP(Element);
+	{
+		if (Data && Data->pin_tree)
+			r_delete_entry(Data->pin_tree, (BoxType *) pin);
+		SetPinBoundingBox(pin);
+		if (Data) {
+			if (!Data->pin_tree)
+				Data->pin_tree = r_create_tree(NULL, 0, 0);
+			r_insert_entry(Data->pin_tree, (BoxType *) pin, 0);
+		}
+		MAKEMIN(box->X1, pin->BoundingBox.X1);
+		MAKEMIN(box->Y1, pin->BoundingBox.Y1);
+		MAKEMAX(box->X2, pin->BoundingBox.X2);
+		MAKEMAX(box->Y2, pin->BoundingBox.Y2);
+		MAKEMIN(vbox->X1, pin->X - pin->Thickness / 2);
+		MAKEMIN(vbox->Y1, pin->Y - pin->Thickness / 2);
+		MAKEMAX(vbox->X2, pin->X + pin->Thickness / 2);
+		MAKEMAX(vbox->Y2, pin->Y + pin->Thickness / 2);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		if (Data && Data->pad_tree)
+			r_delete_entry(Data->pad_tree, (BoxType *) pad);
+		SetPadBoundingBox(pad);
+		if (Data) {
+			if (!Data->pad_tree)
+				Data->pad_tree = r_create_tree(NULL, 0, 0);
+			r_insert_entry(Data->pad_tree, (BoxType *) pad, 0);
+		}
+		MAKEMIN(box->X1, pad->BoundingBox.X1);
+		MAKEMIN(box->Y1, pad->BoundingBox.Y1);
+		MAKEMAX(box->X2, pad->BoundingBox.X2);
+		MAKEMAX(box->Y2, pad->BoundingBox.Y2);
+		MAKEMIN(vbox->X1, MIN(pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
+		MAKEMIN(vbox->Y1, MIN(pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
+		MAKEMAX(vbox->X2, MAX(pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
+		MAKEMAX(vbox->Y2, MAX(pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
+	}
+	END_LOOP;
+	/* now we set the PCB_FLAG_EDGE2 of the pad if Point2
+	 * is closer to the outside edge than Point1
+	 */
+	PAD_LOOP(Element);
+	{
+		if (pad->Point1.Y == pad->Point2.Y) {
+			/* horizontal pad */
+			if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
+				SET_FLAG(PCB_FLAG_EDGE2, pad);
+			else
+				CLEAR_FLAG(PCB_FLAG_EDGE2, pad);
+		}
+		else {
+			/* vertical pad */
+			if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
+				SET_FLAG(PCB_FLAG_EDGE2, pad);
+			else
+				CLEAR_FLAG(PCB_FLAG_EDGE2, pad);
+		}
+	}
+	END_LOOP;
+
+	/* mark pins with component orientation */
+	if ((box->X2 - box->X1) > (box->Y2 - box->Y1)) {
+		PIN_LOOP(Element);
+		{
+			SET_FLAG(PCB_FLAG_EDGE2, pin);
+		}
+		END_LOOP;
+	}
+	else {
+		PIN_LOOP(Element);
+		{
+			CLEAR_FLAG(PCB_FLAG_EDGE2, pin);
+		}
+		END_LOOP;
+	}
+	close_box(box);
+	close_box(vbox);
+	if (Data && !Data->element_tree)
+		Data->element_tree = r_create_tree(NULL, 0, 0);
+	if (Data)
+		r_insert_entry(Data->element_tree, box, 0);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates the bounding box of a text object
+ */
+void SetTextBoundingBox(FontTypePtr FontPtr, TextTypePtr Text)
+{
+	SymbolTypePtr symbol = FontPtr->Symbol;
+	unsigned char *s = (unsigned char *) Text->TextString;
+	int i;
+	int space;
+
+	Coord minx, miny, maxx, maxy, tx;
+	Coord min_final_radius;
+	Coord min_unscaled_radius;
+	pcb_bool first_time = pcb_true;
+
+	minx = miny = maxx = maxy = tx = 0;
+
+	/* Calculate the bounding box based on the larger of the thicknesses
+	 * the text might clamped at on silk or copper layers.
+	 */
+	min_final_radius = MAX(PCB->minWid, PCB->minSlk) / 2;
+
+	/* Pre-adjust the line radius for the fact we are initially computing the
+	 * bounds of the un-scaled text, and the thickness clamping applies to
+	 * scaled text.
+	 */
+	min_unscaled_radius = PCB_UNPCB_SCALE_TEXT(min_final_radius, Text->Scale);
+
+	/* calculate size of the bounding box */
+	for (; s && *s; s++) {
+		if (*s <= MAX_FONTPOSITION && symbol[*s].Valid) {
+			LineTypePtr line = symbol[*s].Line;
+			for (i = 0; i < symbol[*s].LineN; line++, i++) {
+				/* Clamp the width of text lines at the minimum thickness.
+				 * NB: Divide 4 in thickness calculation is comprised of a factor
+				 *     of 1/2 to get a radius from the center-line, and a factor
+				 *     of 1/2 because some stupid reason we render our glyphs
+				 *     at half their defined stroke-width.
+				 */
+				Coord unscaled_radius = MAX(min_unscaled_radius, line->Thickness / 4);
+
+				if (first_time) {
+					minx = maxx = line->Point1.X;
+					miny = maxy = line->Point1.Y;
+					first_time = pcb_false;
+				}
+
+				minx = MIN(minx, line->Point1.X - unscaled_radius + tx);
+				miny = MIN(miny, line->Point1.Y - unscaled_radius);
+				minx = MIN(minx, line->Point2.X - unscaled_radius + tx);
+				miny = MIN(miny, line->Point2.Y - unscaled_radius);
+				maxx = MAX(maxx, line->Point1.X + unscaled_radius + tx);
+				maxy = MAX(maxy, line->Point1.Y + unscaled_radius);
+				maxx = MAX(maxx, line->Point2.X + unscaled_radius + tx);
+				maxy = MAX(maxy, line->Point2.Y + unscaled_radius);
+			}
+			space = symbol[*s].Delta;
+		}
+		else {
+			BoxType *ds = &FontPtr->DefaultSymbol;
+			Coord w = ds->X2 - ds->X1;
+
+			minx = MIN(minx, ds->X1 + tx);
+			miny = MIN(miny, ds->Y1);
+			minx = MIN(minx, ds->X2 + tx);
+			miny = MIN(miny, ds->Y2);
+			maxx = MAX(maxx, ds->X1 + tx);
+			maxy = MAX(maxy, ds->Y1);
+			maxx = MAX(maxx, ds->X2 + tx);
+			maxy = MAX(maxy, ds->Y2);
+
+			space = w / 5;
+		}
+		tx += symbol[*s].Width + space;
+	}
+
+	/* scale values */
+	minx = PCB_SCALE_TEXT(minx, Text->Scale);
+	miny = PCB_SCALE_TEXT(miny, Text->Scale);
+	maxx = PCB_SCALE_TEXT(maxx, Text->Scale);
+	maxy = PCB_SCALE_TEXT(maxy, Text->Scale);
+
+	/* set upper-left and lower-right corner;
+	 * swap coordinates if necessary (origin is already in 'swapped')
+	 * and rotate box
+	 */
+
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, Text)) {
+		Text->BoundingBox.X1 = Text->X + minx;
+		Text->BoundingBox.Y1 = Text->Y - miny;
+		Text->BoundingBox.X2 = Text->X + maxx;
+		Text->BoundingBox.Y2 = Text->Y - maxy;
+		RotateBoxLowLevel(&Text->BoundingBox, Text->X, Text->Y, (4 - Text->Direction) & 0x03);
+	}
+	else {
+		Text->BoundingBox.X1 = Text->X + minx;
+		Text->BoundingBox.Y1 = Text->Y + miny;
+		Text->BoundingBox.X2 = Text->X + maxx;
+		Text->BoundingBox.Y2 = Text->Y + maxy;
+		RotateBoxLowLevel(&Text->BoundingBox, Text->X, Text->Y, Text->Direction);
+	}
+
+	/* the bounding box covers the extent of influence
+	 * so it must include the clearance values too
+	 */
+	Text->BoundingBox.X1 -= PCB->Bloat;
+	Text->BoundingBox.Y1 -= PCB->Bloat;
+	Text->BoundingBox.X2 += PCB->Bloat;
+	Text->BoundingBox.Y2 += PCB->Bloat;
+	close_box(&Text->BoundingBox);
+}
+
+/* ---------------------------------------------------------------------------
+ * returns pcb_true if data area is empty
+ */
+pcb_bool IsDataEmpty(DataTypePtr Data)
+{
+	pcb_bool hasNoObjects;
+	pcb_cardinal_t i;
+
+	hasNoObjects = (pinlist_length(&Data->Via) == 0);
+	hasNoObjects &= (elementlist_length(&Data->Element) == 0);
+	for (i = 0; i < max_copper_layer + 2; i++)
+		hasNoObjects = hasNoObjects && LAYER_IS_EMPTY(&(Data->Layer[i]));
+	return (hasNoObjects);
+}
+
+int FlagIsDataEmpty(int parm)
+{
+	int i = IsDataEmpty(PCB->Data);
+	return parm ? !i : i;
+}
+
+/* FLAG(DataEmpty,FlagIsDataEmpty,0) */
+/* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
+
+pcb_bool IsPasteEmpty(int side)
+{
+	pcb_bool paste_empty = pcb_true;
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (ON_SIDE(pad, side) && !TEST_FLAG(PCB_FLAG_NOPASTE, pad) && pad->Mask > 0) {
+			paste_empty = pcb_false;
+			break;
+		}
+	}
+	ENDALL_LOOP;
+	return paste_empty;
+}
+
+
+typedef struct {
+	int nplated;
+	int nunplated;
+} HoleCountStruct;
+
+static r_dir_t hole_counting_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pin = (PinTypePtr) b;
+	HoleCountStruct *hcs = (HoleCountStruct *) cl;
+	if (TEST_FLAG(PCB_FLAG_HOLE, pin))
+		hcs->nunplated++;
+	else
+		hcs->nplated++;
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* ---------------------------------------------------------------------------
+ * counts the number of plated and unplated holes in the design within
+ * a given area of the board. To count for the whole board, pass NULL
+ * within_area.
+ */
+void CountHoles(int *plated, int *unplated, const BoxType * within_area)
+{
+	HoleCountStruct hcs = { 0, 0 };
+
+	r_search(PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs, NULL);
+	r_search(PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs, NULL);
+
+	if (plated != NULL)
+		*plated = hcs.nplated;
+	if (unplated != NULL)
+		*unplated = hcs.nunplated;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * gets minimum and maximum coordinates
+ * returns NULL if layout is empty
+ */
+BoxTypePtr GetDataBoundingBox(DataTypePtr Data)
+{
+	static BoxType box;
+	/* FIX ME: use r_search to do this much faster */
+
+	/* preset identifiers with highest and lowest possible values */
+	box.X1 = box.Y1 = MAX_COORD;
+	box.X2 = box.Y2 = -MAX_COORD;
+
+	/* now scan for the lowest/highest X and Y coordinate */
+	VIA_LOOP(Data);
+	{
+		box.X1 = MIN(box.X1, via->X - via->Thickness / 2);
+		box.Y1 = MIN(box.Y1, via->Y - via->Thickness / 2);
+		box.X2 = MAX(box.X2, via->X + via->Thickness / 2);
+		box.Y2 = MAX(box.Y2, via->Y + via->Thickness / 2);
+	}
+	END_LOOP;
+	ELEMENT_LOOP(Data);
+	{
+		box.X1 = MIN(box.X1, element->BoundingBox.X1);
+		box.Y1 = MIN(box.Y1, element->BoundingBox.Y1);
+		box.X2 = MAX(box.X2, element->BoundingBox.X2);
+		box.Y2 = MAX(box.Y2, element->BoundingBox.Y2);
+		{
+			TextTypePtr text = &NAMEONPCB_TEXT(element);
+			box.X1 = MIN(box.X1, text->BoundingBox.X1);
+			box.Y1 = MIN(box.Y1, text->BoundingBox.Y1);
+			box.X2 = MAX(box.X2, text->BoundingBox.X2);
+			box.Y2 = MAX(box.Y2, text->BoundingBox.Y2);
+		};
+	}
+	END_LOOP;
+	ALLLINE_LOOP(Data);
+	{
+		box.X1 = MIN(box.X1, line->Point1.X - line->Thickness / 2);
+		box.Y1 = MIN(box.Y1, line->Point1.Y - line->Thickness / 2);
+		box.X1 = MIN(box.X1, line->Point2.X - line->Thickness / 2);
+		box.Y1 = MIN(box.Y1, line->Point2.Y - line->Thickness / 2);
+		box.X2 = MAX(box.X2, line->Point1.X + line->Thickness / 2);
+		box.Y2 = MAX(box.Y2, line->Point1.Y + line->Thickness / 2);
+		box.X2 = MAX(box.X2, line->Point2.X + line->Thickness / 2);
+		box.Y2 = MAX(box.Y2, line->Point2.Y + line->Thickness / 2);
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(Data);
+	{
+		box.X1 = MIN(box.X1, arc->BoundingBox.X1);
+		box.Y1 = MIN(box.Y1, arc->BoundingBox.Y1);
+		box.X2 = MAX(box.X2, arc->BoundingBox.X2);
+		box.Y2 = MAX(box.Y2, arc->BoundingBox.Y2);
+	}
+	ENDALL_LOOP;
+	ALLTEXT_LOOP(Data);
+	{
+		box.X1 = MIN(box.X1, text->BoundingBox.X1);
+		box.Y1 = MIN(box.Y1, text->BoundingBox.Y1);
+		box.X2 = MAX(box.X2, text->BoundingBox.X2);
+		box.Y2 = MAX(box.Y2, text->BoundingBox.Y2);
+	}
+	ENDALL_LOOP;
+	ALLPOLYGON_LOOP(Data);
+	{
+		box.X1 = MIN(box.X1, polygon->BoundingBox.X1);
+		box.Y1 = MIN(box.Y1, polygon->BoundingBox.Y1);
+		box.X2 = MAX(box.X2, polygon->BoundingBox.X2);
+		box.Y2 = MAX(box.Y2, polygon->BoundingBox.Y2);
+	}
+	ENDALL_LOOP;
+	return (IsDataEmpty(Data) ? NULL : &box);
+}
+
+/* ---------------------------------------------------------------------------
+ * centers the displayed PCB around the specified point (X,Y)
+ */
+void CenterDisplay(Coord X, Coord Y)
+{
+	Coord save_grid = PCB->Grid;
+	PCB->Grid = 1;
+	if (MoveCrosshairAbsolute(X, Y))
+		notify_crosshair_change(pcb_true);
+	gui->set_crosshair(Crosshair.X, Crosshair.Y, HID_SC_WARP_POINTER);
+	PCB->Grid = save_grid;
+}
+
+/* ---------------------------------------------------------------------------
+ * transforms symbol coordinates so that the left edge of each symbol
+ * is at the zero position. The y coordinates are moved so that min(y) = 0
+ *
+ */
+void SetFontInfo(FontTypePtr Ptr)
+{
+	pcb_cardinal_t i, j;
+	SymbolTypePtr symbol;
+	LineTypePtr line;
+	Coord totalminy = MAX_COORD;
+
+	/* calculate cell with and height (is at least DEFAULT_CELLSIZE)
+	 * maximum cell width and height
+	 * minimum x and y position of all lines
+	 */
+	Ptr->MaxWidth = DEFAULT_CELLSIZE;
+	Ptr->MaxHeight = DEFAULT_CELLSIZE;
+	for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++) {
+		Coord minx, miny, maxx, maxy;
+
+		/* next one if the index isn't used or symbol is empty (SPACE) */
+		if (!symbol->Valid || !symbol->LineN)
+			continue;
+
+		minx = miny = MAX_COORD;
+		maxx = maxy = 0;
+		for (line = symbol->Line, j = symbol->LineN; j; j--, line++) {
+			minx = MIN(minx, line->Point1.X);
+			miny = MIN(miny, line->Point1.Y);
+			minx = MIN(minx, line->Point2.X);
+			miny = MIN(miny, line->Point2.Y);
+			maxx = MAX(maxx, line->Point1.X);
+			maxy = MAX(maxy, line->Point1.Y);
+			maxx = MAX(maxx, line->Point2.X);
+			maxy = MAX(maxy, line->Point2.Y);
+		}
+
+		/* move symbol to left edge */
+		for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
+			MOVE_LINE_LOWLEVEL(line, -minx, 0);
+
+		/* set symbol bounding box with a minimum cell size of (1,1) */
+		symbol->Width = maxx - minx + 1;
+		symbol->Height = maxy + 1;
+
+		/* check total min/max  */
+		Ptr->MaxWidth = MAX(Ptr->MaxWidth, symbol->Width);
+		Ptr->MaxHeight = MAX(Ptr->MaxHeight, symbol->Height);
+		totalminy = MIN(totalminy, miny);
+	}
+
+	/* move coordinate system to the upper edge (lowest y on screen) */
+	for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
+		if (symbol->Valid) {
+			symbol->Height -= totalminy;
+			for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
+				MOVE_LINE_LOWLEVEL(line, 0, -totalminy);
+		}
+
+	/* setup the box for the default symbol */
+	Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
+	Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
+	Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
+}
+
+Coord GetNum(char **s, const char *default_unit)
+{
+	/* Read value */
+	Coord ret_val = GetValueEx(*s, NULL, NULL, NULL, default_unit, NULL);
+	/* Advance pointer */
+	while (isalnum(**s) || **s == '.')
+		(*s)++;
+	return ret_val;
+}
+
+/* ---------------------------------------------------------------------------
+ * quits application
+ */
+extern void pcb_main_uninit(void);
+void QuitApplication(void)
+{
+	/*
+	 * save data if necessary.  It not needed, then don't trigger EmergencySave
+	 * via our atexit() registering of EmergencySave().  We presumably wanted to
+	 * exit here and thus it is not an emergency.
+	 */
+	if (PCB->Changed && conf_core.editor.save_in_tmp)
+		EmergencySave();
+	else
+		DisableEmergencySave();
+
+	if (gui->do_exit == NULL) {
+		pcb_main_uninit();
+		exit(0);
+	}
+	else
+		gui->do_exit(gui);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates a filename from a template
+ * %f is replaced by the filename
+ * %p by the searchpath
+ */
+char *EvaluateFilename(const char *Template, const char *Path, const char *Filename, const char *Parameter)
+{
+	gds_t command;
+	const char *p;
+
+	if (conf_core.rc.verbose) {
+		printf("EvaluateFilename:\n");
+		printf("\tTemplate: \033[33m%s\033[0m\n", Template);
+		printf("\tPath: \033[33m%s\033[0m\n", Path);
+		printf("\tFilename: \033[33m%s\033[0m\n", Filename);
+		printf("\tParameter: \033[33m%s\033[0m\n", Parameter);
+	}
+
+	gds_init(&command);
+
+	for (p = Template; p && *p; p++) {
+		/* copy character or add string to command */
+		if (*p == '%' && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
+			switch (*(++p)) {
+			case 'a':
+				gds_append_str(&command, Parameter);
+				break;
+			case 'f':
+				gds_append_str(&command, Filename);
+				break;
+			case 'p':
+				gds_append_str(&command, Path);
+				break;
+			}
+		else
+			gds_append(&command, *p);
+	}
+
+	if (conf_core.rc.verbose)
+		printf("EvaluateFilename: \033[32m%s\033[0m\n", command.array);
+
+	return command.array;
+}
+
+/* ---------------------------------------------------------------------------
+ * returns a pointer to an objects bounding box;
+ * data is valid until the routine is called again
+ */
+BoxTypePtr GetObjectBoundingBox(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	switch (Type) {
+	case PCB_TYPE_LINE:
+	case PCB_TYPE_ARC:
+	case PCB_TYPE_TEXT:
+	case PCB_TYPE_POLYGON:
+	case PCB_TYPE_PAD:
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_ELEMENT_NAME:
+		return (BoxType *) Ptr2;
+	case PCB_TYPE_VIA:
+	case PCB_TYPE_ELEMENT:
+		return (BoxType *) Ptr1;
+	case PCB_TYPE_POLYGON_POINT:
+	case PCB_TYPE_LINE_POINT:
+		return (BoxType *) Ptr3;
+	default:
+		Message(PCB_MSG_DEFAULT, "Request for bounding box of unsupported type %d\n", Type);
+		return (BoxType *) Ptr2;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * computes the bounding box of an arc
+ */
+void SetArcBoundingBox(ArcTypePtr Arc)
+{
+	double ca1, ca2, sa1, sa2;
+	double minx, maxx, miny, maxy;
+	Angle ang1, ang2;
+	Coord width;
+
+	/* first put angles into standard form:
+	 *  ang1 < ang2, both angles between 0 and 720 */
+	Arc->Delta = PCB_CLAMP(Arc->Delta, -360, 360);
+
+	if (Arc->Delta > 0) {
+		ang1 = NormalizeAngle(Arc->StartAngle);
+		ang2 = NormalizeAngle(Arc->StartAngle + Arc->Delta);
+	}
+	else {
+		ang1 = NormalizeAngle(Arc->StartAngle + Arc->Delta);
+		ang2 = NormalizeAngle(Arc->StartAngle);
+	}
+	if (ang1 > ang2)
+		ang2 += 360;
+	/* Make sure full circles aren't treated as zero-length arcs */
+	if (Arc->Delta == 360 || Arc->Delta == -360)
+		ang2 = ang1 + 360;
+
+	/* calculate sines, cosines */
+	sa1 = sin(PCB_M180 * ang1);
+	ca1 = cos(PCB_M180 * ang1);
+	sa2 = sin(PCB_M180 * ang2);
+	ca2 = cos(PCB_M180 * ang2);
+
+	minx = MIN(ca1, ca2);
+	maxx = MAX(ca1, ca2);
+	miny = MIN(sa1, sa2);
+	maxy = MAX(sa1, sa2);
+
+	/* Check for extreme angles */
+	if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360))
+		maxx = 1;
+	if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450))
+		maxy = 1;
+	if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540))
+		minx = -1;
+	if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630))
+		miny = -1;
+
+	/* Finally, calculate bounds, converting sane geometry into pcb geometry */
+	Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
+	Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
+	Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
+	Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
+
+	width = (Arc->Thickness + Arc->Clearance) / 2;
+
+	/* Adjust for our discrete polygon approximation */
+	width = (double) width *MAX(POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
+
+	Arc->BoundingBox.X1 -= width;
+	Arc->BoundingBox.X2 += width;
+	Arc->BoundingBox.Y1 -= width;
+	Arc->BoundingBox.Y2 += width;
+	close_box(&Arc->BoundingBox);
+}
+
+BoxTypePtr GetArcEnds(ArcTypePtr Arc)
+{
+	static BoxType box;
+	box.X1 = Arc->X - Arc->Width * cos(Arc->StartAngle * PCB_M180);
+	box.Y1 = Arc->Y + Arc->Height * sin(Arc->StartAngle * PCB_M180);
+	box.X2 = Arc->X - Arc->Width * cos((Arc->StartAngle + Arc->Delta) * PCB_M180);
+	box.Y2 = Arc->Y + Arc->Height * sin((Arc->StartAngle + Arc->Delta) * PCB_M180);
+	return &box;
+}
+
+/* doesn't these belong in change.c ?? */
+void ChangeArcAngles(LayerTypePtr Layer, ArcTypePtr a, Angle new_sa, Angle new_da)
+{
+	if (new_da >= 360) {
+		new_da = 360;
+		new_sa = 0;
+	}
+	RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, a);
+	r_delete_entry(Layer->arc_tree, (BoxTypePtr) a);
+	AddObjectToChangeAnglesUndoList(PCB_TYPE_ARC, a, a, a);
+	a->StartAngle = new_sa;
+	a->Delta = new_da;
+	SetArcBoundingBox(a);
+	r_insert_entry(Layer->arc_tree, (BoxTypePtr) a, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, a);
+}
+
+
+void ChangeArcRadii(LayerTypePtr Layer, ArcTypePtr a, Coord new_width, Coord new_height)
+{
+	RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, a);
+	r_delete_entry(Layer->arc_tree, (BoxTypePtr) a);
+	AddObjectToChangeRadiiUndoList(PCB_TYPE_ARC, a, a, a);
+	a->Width = new_width;
+	a->Height = new_height;
+	SetArcBoundingBox(a);
+	r_insert_entry(Layer->arc_tree, (BoxTypePtr) a, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, a);
+}
+
+static char *BumpName(char *Name)
+{
+	int num;
+	char c, *start;
+	static char temp[256];
+
+	start = Name;
+	/* seek end of string */
+	while (*Name != 0)
+		Name++;
+	/* back up to potential number */
+	for (Name--; isdigit((int) *Name); Name--);
+	Name++;
+	if (*Name)
+		num = atoi(Name) + 1;
+	else
+		num = 1;
+	c = *Name;
+	*Name = 0;
+	sprintf(temp, "%s%d", start, num);
+	/* if this is not our string, put back the blown character */
+	if (start != temp)
+		*Name = c;
+	return (temp);
+}
+
+/*
+ * make a unique name for the name on board
+ * this can alter the contents of the input string
+ */
+char *UniqueElementName(DataTypePtr Data, char *Name)
+{
+	pcb_bool unique = pcb_true;
+	/* null strings are ok */
+	if (!Name || !*Name)
+		return (Name);
+
+	for (;;) {
+		ELEMENT_LOOP(Data);
+		{
+			if (NAMEONPCB_NAME(element) && NSTRCMP(NAMEONPCB_NAME(element), Name) == 0) {
+				Name = BumpName(Name);
+				unique = pcb_false;
+				break;
+			}
+		}
+		END_LOOP;
+		if (unique)
+			return (Name);
+		unique = pcb_true;
+	}
+}
+
+static void GetGridLockCoordinates(int type, void *ptr1, void *ptr2, void *ptr3, Coord * x, Coord * y)
+{
+	switch (type) {
+	case PCB_TYPE_VIA:
+		*x = ((PinTypePtr) ptr2)->X;
+		*y = ((PinTypePtr) ptr2)->Y;
+		break;
+	case PCB_TYPE_LINE:
+		*x = ((LineTypePtr) ptr2)->Point1.X;
+		*y = ((LineTypePtr) ptr2)->Point1.Y;
+		break;
+	case PCB_TYPE_TEXT:
+	case PCB_TYPE_ELEMENT_NAME:
+		*x = ((TextTypePtr) ptr2)->X;
+		*y = ((TextTypePtr) ptr2)->Y;
+		break;
+	case PCB_TYPE_ELEMENT:
+		*x = ((ElementTypePtr) ptr2)->MarkX;
+		*y = ((ElementTypePtr) ptr2)->MarkY;
+		break;
+	case PCB_TYPE_POLYGON:
+		*x = ((PolygonTypePtr) ptr2)->Points[0].X;
+		*y = ((PolygonTypePtr) ptr2)->Points[0].Y;
+		break;
+
+	case PCB_TYPE_LINE_POINT:
+	case PCB_TYPE_POLYGON_POINT:
+		*x = ((PointTypePtr) ptr3)->X;
+		*y = ((PointTypePtr) ptr3)->Y;
+		break;
+	case PCB_TYPE_ARC:
+		{
+			BoxTypePtr box;
+
+			box = GetArcEnds((ArcTypePtr) ptr2);
+			*x = box->X1;
+			*y = box->Y1;
+			break;
+		}
+	}
+}
+
+void AttachForCopy(Coord PlaceX, Coord PlaceY)
+{
+	BoxTypePtr box;
+	Coord mx = 0, my = 0;
+
+	Crosshair.AttachedObject.RubberbandN = 0;
+	if (!conf_core.editor.snap_pin) {
+		/* dither the grab point so that the mark, center, etc
+		 * will end up on a grid coordinate
+		 */
+		GetGridLockCoordinates(Crosshair.AttachedObject.Type,
+													 Crosshair.AttachedObject.Ptr1,
+													 Crosshair.AttachedObject.Ptr2, Crosshair.AttachedObject.Ptr3, &mx, &my);
+		mx = GridFit(mx, PCB->Grid, PCB->GridOffsetX) - mx;
+		my = GridFit(my, PCB->Grid, PCB->GridOffsetY) - my;
+	}
+	Crosshair.AttachedObject.X = PlaceX - mx;
+	Crosshair.AttachedObject.Y = PlaceY - my;
+	if (!Marked.status || conf_core.editor.local_ref)
+		SetLocalRef(PlaceX - mx, PlaceY - my, pcb_true);
+	Crosshair.AttachedObject.State = STATE_SECOND;
+
+	/* get boundingbox of object and set cursor range */
+	box = GetObjectBoundingBox(Crosshair.AttachedObject.Type,
+														 Crosshair.AttachedObject.Ptr1, Crosshair.AttachedObject.Ptr2, Crosshair.AttachedObject.Ptr3);
+	SetCrosshairRange(Crosshair.AttachedObject.X - box->X1,
+										Crosshair.AttachedObject.Y - box->Y1,
+										PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
+										PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
+
+	/* get all attached objects if necessary */
+	if ((conf_core.editor.mode != PCB_MODE_COPY) && conf_core.editor.rubber_band_mode)
+		LookupRubberbandLines(Crosshair.AttachedObject.Type,
+													Crosshair.AttachedObject.Ptr1, Crosshair.AttachedObject.Ptr2, Crosshair.AttachedObject.Ptr3);
+	if (conf_core.editor.mode != PCB_MODE_COPY &&
+			(Crosshair.AttachedObject.Type == PCB_TYPE_ELEMENT ||
+			 Crosshair.AttachedObject.Type == PCB_TYPE_VIA ||
+			 Crosshair.AttachedObject.Type == PCB_TYPE_LINE || Crosshair.AttachedObject.Type == PCB_TYPE_LINE_POINT))
+		LookupRatLines(Crosshair.AttachedObject.Type,
+									 Crosshair.AttachedObject.Ptr1, Crosshair.AttachedObject.Ptr2, Crosshair.AttachedObject.Ptr3);
+}
+
+/* This just fills in a FlagType with current flags.  */
+FlagType MakeFlags(unsigned int flags)
+{
+	FlagType rv;
+	memset(&rv, 0, sizeof(rv));
+	rv.f = flags;
+	return rv;
+}
+
+/* This converts old flag bits (from saved PCB files) to new format.  */
+FlagType OldFlags(unsigned int flags)
+{
+	FlagType rv;
+	int i, f;
+	memset(&rv, 0, sizeof(rv));
+	/* If we move flag bits around, this is where we map old bits to them.  */
+	rv.f = flags & 0xffff;
+	f = 0x10000;
+	for (i = 0; i < 8; i++) {
+		/* use the closest thing to the old thermal style */
+		if (flags & f)
+			rv.t[i / 2] |= (1 << (4 * (i % 2)));
+		f <<= 1;
+	}
+	return rv;
+}
+
+FlagType AddFlags(FlagType flag, unsigned int flags)
+{
+	flag.f |= flags;
+	return flag;
+}
+
+FlagType MaskFlags(FlagType flag, unsigned int flags)
+{
+	flag.f &= ~flags;
+	return flag;
+}
+
+char *AttributeGetFromList(AttributeListType * list, const char *name)
+{
+	int i;
+	for (i = 0; i < list->Number; i++)
+		if (strcmp(name, list->List[i].name) == 0)
+			return list->List[i].value;
+	return NULL;
+}
+
+int AttributePutToList(AttributeListType * list, const char *name, const char *value, int replace)
+{
+	int i;
+
+	/* If we're allowed to replace an existing attribute, see if we
+	   can.  */
+	if (replace) {
+		for (i = 0; i < list->Number; i++)
+			if (strcmp(name, list->List[i].name) == 0) {
+				free(list->List[i].value);
+				list->List[i].value = pcb_strdup_null(value);
+				return 1;
+			}
+	}
+
+	/* At this point, we're going to need to add a new attribute to the
+	   list.  See if there's room.  */
+	if (list->Number >= list->Max) {
+		list->Max += 10;
+		list->List = (AttributeType *) realloc(list->List, list->Max * sizeof(AttributeType));
+	}
+
+	/* Now add the new attribute.  */
+	i = list->Number;
+	list->List[i].name = pcb_strdup_null(name);
+	list->List[i].value = pcb_strdup_null(value);
+	list->Number++;
+	return 0;
+}
+
+int AttributeRemoveFromList(AttributeListType * list, const char *name)
+{
+	int i, j, found = 0;
+	for (i = 0; i < list->Number; i++)
+		if (strcmp(name, list->List[i].name) == 0) {
+			free(list->List[i].name);
+			free(list->List[i].value);
+			found++;
+			for (j = i; j < list->Number - i; j++)
+				list->List[j] = list->List[j + 1];
+			list->Number--;
+		}
+	return found;
+}
+
+void r_delete_element(DataType * data, ElementType * element)
+{
+	r_delete_entry(data->element_tree, (BoxType *) element);
+	PIN_LOOP(element);
+	{
+		r_delete_entry(data->pin_tree, (BoxType *) pin);
+	}
+	END_LOOP;
+	PAD_LOOP(element);
+	{
+		r_delete_entry(data->pad_tree, (BoxType *) pad);
+	}
+	END_LOOP;
+	ELEMENTTEXT_LOOP(element);
+	{
+		r_delete_entry(data->name_tree[n], (BoxType *) text);
+	}
+	END_LOOP;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * Returns a string that has a bunch of information about the program.
+ * Can be used for things like "about" dialog boxes.
+ */
+
+char *GetInfoString(void)
+{
+	HID **hids;
+	int i;
+	static gds_t info;
+	static int first_time = 1;
+
+#define TAB "    "
+
+	if (first_time) {
+		first_time = 0;
+		gds_append_str(&info, "This is PCB-rnd " VERSION " (" REVISION ")" "\n an interactive\n");
+		gds_append_str(&info, "printed circuit board editor\n");
+		gds_append_str(&info, "PCB-rnd forked from PCB version.");
+		gds_append_str(&info, "\n\n" "PCB is by harry eaton and others\n\n");
+		gds_append_str(&info, "\nPCB-rnd adds a collection of\n");
+		gds_append_str(&info, "useful-looking random patches.\n");
+		gds_append_str(&info, "\n");
+		gds_append_str(&info, "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n");
+		gds_append_str(&info, "Copyright (C) harry eaton 1998-2007\n");
+		gds_append_str(&info, "Copyright (C) C. Scott Ananian 2001\n");
+		gds_append_str(&info, "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
+		gds_append_str(&info, "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
+		gds_append_str(&info, "Copyright (C) Tibor Palinkas 2013-2016 (pcb-rnd patches)\n\n");
+		gds_append_str(&info, "It is licensed under the terms of the GNU\n");
+		gds_append_str(&info, "General Public License version 2\n");
+		gds_append_str(&info, "See the LICENSE file for more information\n\n");
+		gds_append_str(&info, "For more information see:\n\n");
+		gds_append_str(&info, "PCB-rnd homepage: http://repo.hu/projects/pcb-rnd\n");
+		gds_append_str(&info, "PCB homepage: http://pcb.geda-project.org\n");
+		gds_append_str(&info, "gEDA homepage: http://www.geda-project.org\n");
+		gds_append_str(&info, "gEDA Wiki: http://wiki.geda-project.org\n\n");
+
+		gds_append_str(&info, "----- Compile Time Options -----\n");
+		hids = hid_enumerate();
+		gds_append_str(&info, "GUI:\n");
+		for (i = 0; hids[i]; i++) {
+			if (hids[i]->gui) {
+				gds_append_str(&info, TAB);
+				gds_append_str(&info, hids[i]->name);
+				gds_append_str(&info, " : ");
+				gds_append_str(&info, hids[i]->description);
+				gds_append_str(&info, "\n");
+			}
+		}
+
+		gds_append_str(&info, "Exporters:\n");
+		for (i = 0; hids[i]; i++) {
+			if (hids[i]->exporter) {
+				gds_append_str(&info, TAB);
+				gds_append_str(&info, hids[i]->name);
+				gds_append_str(&info, " : ");
+				gds_append_str(&info, hids[i]->description);
+				gds_append_str(&info, "\n");
+			}
+		}
+
+		gds_append_str(&info, "Printers:\n");
+		for (i = 0; hids[i]; i++) {
+			if (hids[i]->printer) {
+				gds_append_str(&info, TAB);
+				gds_append_str(&info, hids[i]->name);
+				gds_append_str(&info, " : ");
+				gds_append_str(&info, hids[i]->description);
+				gds_append_str(&info, "\n");
+			}
+		}
+	}
+#undef TAB
+
+	return info.array;
+}
+
+const char *pcb_author(void)
+{
+	if (conf_core.design.fab_author && conf_core.design.fab_author[0])
+		return conf_core.design.fab_author;
+	else
+		return get_user_name();
+}
+
+
+/* ---------------------------------------------------------------------------
+ * Returns a best guess about the orientation of an element.  The
+ * value corresponds to the rotation; a difference is the right value
+ * to pass to RotateElementLowLevel.  However, the actual value is no
+ * indication of absolute rotation; only relative rotation is
+ * meaningful.
+ */
+
+int ElementOrientation(ElementType * e)
+{
+	Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
+	pcb_bool found_pin1 = 0;
+	pcb_bool found_pin2 = 0;
+
+	/* in case we don't find pin 1 or 2, make sure we have initialized these variables */
+	pin1x = 0;
+	pin1y = 0;
+	pin2x = 0;
+	pin2y = 0;
+
+	PIN_LOOP(e);
+	{
+		if (NSTRCMP(pin->Number, "1") == 0) {
+			pin1x = pin->X;
+			pin1y = pin->Y;
+			found_pin1 = 1;
+		}
+		else if (NSTRCMP(pin->Number, "2") == 0) {
+			pin2x = pin->X;
+			pin2y = pin->Y;
+			found_pin2 = 1;
+		}
+	}
+	END_LOOP;
+
+	PAD_LOOP(e);
+	{
+		if (NSTRCMP(pad->Number, "1") == 0) {
+			pin1x = (pad->Point1.X + pad->Point2.X) / 2;
+			pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
+			found_pin1 = 1;
+		}
+		else if (NSTRCMP(pad->Number, "2") == 0) {
+			pin2x = (pad->Point1.X + pad->Point2.X) / 2;
+			pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
+			found_pin2 = 1;
+		}
+	}
+	END_LOOP;
+
+	if (found_pin1 && found_pin2) {
+		dx = pin2x - pin1x;
+		dy = pin2y - pin1y;
+	}
+	else if (found_pin1 && (pin1x || pin1y)) {
+		dx = pin1x;
+		dy = pin1y;
+	}
+	else if (found_pin2 && (pin2x || pin2y)) {
+		dx = pin2x;
+		dy = pin2y;
+	}
+	else
+		return 0;
+
+	if (coord_abs(dx) > coord_abs(dy))
+		return dx > 0 ? 0 : 2;
+	return dy > 0 ? 3 : 1;
+}
+
+int ActionListRotations(int argc, const char **argv, Coord x, Coord y)
+{
+	ELEMENT_LOOP(PCB->Data);
+	{
+		printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
+	}
+	END_LOOP;
+
+	return 0;
+}
+
+HID_Action misc_action_list[] = {
+	{"ListRotations", 0, ActionListRotations,
+	 0, 0}
+	,
+};
+
+REGISTER_ACTIONS(misc_action_list, NULL)
diff --git a/src/misc.h b/src/misc.h
new file mode 100644
index 0000000..0cb37e5
--- /dev/null
+++ b/src/misc.h
@@ -0,0 +1,103 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for misc routines - PCB data type dependent ones */
+
+#ifndef	PCB_MISC_H
+#define	PCB_MISC_H
+
+#include <stdlib.h>
+#include "global.h"
+#include <genvector/gds_char.h>
+#include "mymem.h"
+
+const char *pcb_author(void);
+
+Angle NormalizeAngle(Angle a);
+
+void r_delete_element(DataTypePtr, ElementTypePtr);
+void SetLineBoundingBox(LineTypePtr);
+void SetArcBoundingBox(ArcTypePtr);
+void SetPointBoundingBox(PointTypePtr);
+void SetPinBoundingBox(PinTypePtr);
+void SetPadBoundingBox(PadTypePtr);
+void SetPolygonBoundingBox(PolygonTypePtr);
+void SetElementBoundingBox(DataTypePtr, ElementTypePtr, FontTypePtr);
+pcb_bool IsDataEmpty(DataTypePtr);
+pcb_bool IsPasteEmpty(int);
+void CountHoles(int *, int *, const BoxType *);
+BoxTypePtr GetDataBoundingBox(DataTypePtr);
+void CenterDisplay(Coord, Coord);
+void SetFontInfo(FontTypePtr);
+Coord GetNum(char **s, const char *default_unit);
+
+void QuitApplication(void);
+char *EvaluateFilename(const char *, const char *, const char *, const char *);
+void SetTextBoundingBox(FontTypePtr, TextTypePtr);
+
+void SaveOutputWindow(void);
+
+BoxTypePtr GetObjectBoundingBox(int, void *, void *, void *);
+void ResetStackAndVisibility(void);
+void SaveStackAndVisibility(void);
+void RestoreStackAndVisibility(void);
+BoxTypePtr GetArcEnds(ArcTypePtr);
+void ChangeArcAngles(LayerTypePtr, ArcTypePtr, Angle, Angle);
+void ChangeArcRadii(LayerTypePtr, ArcTypePtr, Coord, Coord);
+char *UniqueElementName(DataTypePtr, char *);
+void AttachForCopy(Coord, Coord);
+
+/* Returns NULL if the name isn't found, else the value for that named
+   attribute.  */
+char *AttributeGetFromList(AttributeListType * list, const char *name);
+/* Adds an attribute to the list.  If the attribute already exists,
+   whether it's replaced or a second copy added depends on
+   REPLACE.  Returns non-zero if an existing attribute was replaced.  */
+int AttributePutToList(AttributeListType * list, const char *name, const char *value, int replace);
+/* Simplistic version: Takes a pointer to an object, looks up attributes in it.  */
+#define AttributeGet(OBJ,name) AttributeGetFromList (&(OBJ->Attributes), name)
+/* Simplistic version: Takes a pointer to an object, sets attributes in it.  */
+#define AttributePut(OBJ,name,value) AttributePutToList (&(OBJ->Attributes), name, value, 1)
+/* Remove an attribute by name; returns number of items removed  */
+int AttributeRemoveFromList(AttributeListType * list, const char *name);
+/* Simplistic version of Remove.  */
+#define AttributeRemove(OBJ, name) AttributeRemoveFromList (&(OBJ->Attributes), name)
+
+/* For passing modified flags to other functions. */
+FlagType MakeFlags(unsigned int);
+FlagType OldFlags(unsigned int);
+FlagType AddFlags(FlagType, unsigned int);
+FlagType MaskFlags(FlagType, unsigned int);
+#define		NoFlags() MakeFlags(0)
+
+/* Returns a string with info about this copy of pcb. */
+char *GetInfoString(void);
+
+/* Return a relative rotation for an element, useful only for
+   comparing two similar footprints.  */
+int ElementOrientation(ElementType * e);
+
+#endif /* PCB_MISC_H */
diff --git a/src/misc_util.c b/src/misc_util.c
new file mode 100644
index 0000000..593096a
--- /dev/null
+++ b/src/misc_util.c
@@ -0,0 +1,181 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* misc functions used by several modules
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include "misc_util.h"
+#include "unit.h"
+
+/* Distance() should be used so that there is only one
+ *  place to deal with overflow/precision errors
+ */
+double Distance(double x1, double y1, double x2, double y2)
+{
+	double delta_x = (x2 - x1);
+	double delta_y = (y2 - y1);
+	return sqrt(delta_x * delta_x + delta_y * delta_y);
+}
+
+/* Distance2() should be used so that there is only one
+ *  place to deal with overflow/precision errors
+ */
+double Distance2(double x1, double y1, double x2, double y2)
+{
+	double delta_x = (x2 - x1);
+	double delta_y = (y2 - y1);
+	return delta_x * delta_x + delta_y * delta_y;
+}
+
+/* Get Value returns a numeric value passed from the string and sets the
+ * pcb_bool variable absolute to false if it leads with a +/- character
+ */
+double GetValue(const char *val, const char *units, pcb_bool * absolute, pcb_bool *success)
+{
+	return GetValueEx(val, units, absolute, NULL, "cmil", success);
+}
+
+double GetValueEx(const char *val, const char *units, pcb_bool * absolute, UnitList extra_units, const char *default_unit, pcb_bool *success)
+{
+	double value;
+	int n = -1;
+	pcb_bool scaled = 0;
+	pcb_bool dummy;
+
+	/* Allow NULL to be passed for absolute */
+	if (absolute == NULL)
+		absolute = &dummy;
+
+	/* if the first character is a sign we have to add the
+	 * value to the current one
+	 */
+	if (*val == '=') {
+		*absolute = pcb_true;
+		sscanf(val + 1, "%lf%n", &value, &n);
+		n++;
+	}
+	else {
+		if (isdigit((int) *val))
+			*absolute = pcb_true;
+		else
+			*absolute = pcb_false;
+		sscanf(val, "%lf%n", &value, &n);
+	}
+	if (n <= 0)
+		goto fail;
+
+	if (!units && n > 0)
+		units = val + n;
+
+	while (units && isspace(*units))
+		units++;
+
+	if (units && *units) {
+		int i, unit_ok = 0;
+		const Unit *unit = get_unit_struct(units);
+		if (unit != NULL) {
+			value = unit_to_coord(unit, value);
+			scaled = 1;
+			unit_ok = 1;
+		}
+		if (extra_units) {
+			for (i = 0; *extra_units[i].suffix; ++i) {
+				if (strncmp(units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0) {
+					value *= extra_units[i].scale;
+					if (extra_units[i].flags & UNIT_PERCENT)
+						value /= 100.0;
+					scaled = 1;
+					unit_ok = 1;
+				}
+			}
+		}
+		if ((!unit_ok) && (success != NULL)) /* there was something after the number but it doesn't look like a valid unit */
+			goto fail;
+	}
+
+	/* Apply default unit */
+	if (!scaled && default_unit && *default_unit) {
+		int i;
+		const Unit *unit = get_unit_struct(default_unit);
+		if (extra_units)
+			for (i = 0; *extra_units[i].suffix; ++i)
+				if (strcmp(extra_units[i].suffix, default_unit) == 0) {
+					value *= extra_units[i].scale;
+					if (extra_units[i].flags & UNIT_PERCENT)
+						value /= 100.0;
+					scaled = 1;
+				}
+		if (!scaled && unit != NULL)
+			value = unit_to_coord(unit, value);
+	}
+
+	if (success != NULL)
+		*success = 1;
+	return value;
+
+	fail:;
+	if (success != NULL)
+		*success = 0;
+	return 0;
+}
+
+char *Concat(const char *first, ...)
+{
+	char *rv;
+	int len;
+	va_list a;
+
+	len = strlen(first);
+	rv = (char *) malloc(len + 1);
+	strcpy(rv, first);
+
+	va_start(a, first);
+	while (1) {
+		const char *s = va_arg(a, const char *);
+		if (!s)
+			break;
+		len += strlen(s);
+		rv = (char *) realloc(rv, len + 1);
+		strcat(rv, s);
+	}
+	va_end(a);
+	return rv;
+}
+
+int mem_any_set(unsigned char *ptr, int bytes)
+{
+	while (bytes--)
+		if (*ptr++)
+			return 1;
+	return 0;
+}
diff --git a/src/misc_util.h b/src/misc_util.h
new file mode 100644
index 0000000..46ce46f
--- /dev/null
+++ b/src/misc_util.h
@@ -0,0 +1,51 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for misc routines - independent of PCB data types */
+
+#ifndef	PCB_MISC_UTIL_H
+#define	PCB_MISC_UTIL_H
+
+#include "pcb_bool.h"
+
+double Distance(double x1, double y1, double x2, double y2);
+double Distance2(double x1, double y1, double x2, double y2);	/* distance square */
+
+enum unitflags { UNIT_PERCENT = 1 };
+
+typedef struct {
+	const char *suffix;
+	double scale;
+	enum unitflags flags;
+} UnitList[];
+
+double GetValue(const char *, const char *, pcb_bool *, pcb_bool *success);
+double GetValueEx(const char *, const char *, pcb_bool *, UnitList, const char *, pcb_bool *success);
+
+char *Concat(const char *, ...);	/* end with NULL */
+int mem_any_set(unsigned char *ptr, int bytes);
+
+#endif
diff --git a/src/move.c b/src/move.c
new file mode 100644
index 0000000..ebdd440
--- /dev/null
+++ b/src/move.c
@@ -0,0 +1,1051 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* functions used to move pins, elements ...
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <setjmp.h>
+#include <stdlib.h>
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "misc.h"
+#include "move.h"
+#include "polygon.h"
+#include "rtree.h"
+#include "search.h"
+#include "select.h"
+#include "undo.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *MoveElementName(ElementTypePtr);
+static void *MoveElement(ElementTypePtr);
+static void *MoveVia(PinTypePtr);
+static void *MoveLine(LayerTypePtr, LineTypePtr);
+static void *MoveArc(LayerTypePtr, ArcTypePtr);
+static void *MoveText(LayerTypePtr, TextTypePtr);
+static void *MovePolygon(LayerTypePtr, PolygonTypePtr);
+static void *MoveLinePoint(LayerTypePtr, LineTypePtr, PointTypePtr);
+static void *MovePolygonPoint(LayerTypePtr, PolygonTypePtr, PointTypePtr);
+static void *MoveLineToLayer(LayerTypePtr, LineTypePtr);
+static void *MoveArcToLayer(LayerTypePtr, ArcTypePtr);
+static void *MoveRatToLayer(RatTypePtr);
+static void *MoveTextToLayer(LayerTypePtr, TextTypePtr);
+static void *MovePolygonToLayer(LayerTypePtr, PolygonTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static Coord DeltaX, DeltaY;		/* used by local routines as offset */
+static LayerTypePtr Dest;
+static pcb_bool MoreToCome;
+static ObjectFunctionType MoveFunctions = {
+	MoveLine,
+	MoveText,
+	MovePolygon,
+	MoveVia,
+	MoveElement,
+	MoveElementName,
+	NULL,
+	NULL,
+	MoveLinePoint,
+	MovePolygonPoint,
+	MoveArc,
+	NULL
+}, MoveToLayerFunctions = {
+MoveLineToLayer, MoveTextToLayer, MovePolygonToLayer, NULL, NULL, NULL, NULL, NULL, NULL, NULL, MoveArcToLayer, MoveRatToLayer};
+
+/* ---------------------------------------------------------------------------
+ * moves a element by +-X and +-Y
+ */
+void MoveElementLowLevel(DataTypePtr Data, ElementTypePtr Element, Coord DX, Coord DY)
+{
+	if (Data)
+		r_delete_entry(Data->element_tree, (BoxType *) Element);
+	ELEMENTLINE_LOOP(Element);
+	{
+		MOVE_LINE_LOWLEVEL(line, DX, DY);
+	}
+	END_LOOP;
+	PIN_LOOP(Element);
+	{
+		if (Data) {
+			r_delete_entry(Data->pin_tree, (BoxType *) pin);
+			RestoreToPolygon(Data, PCB_TYPE_PIN, Element, pin);
+		}
+		MOVE_PIN_LOWLEVEL(pin, DX, DY);
+		if (Data) {
+			r_insert_entry(Data->pin_tree, (BoxType *) pin, 0);
+			ClearFromPolygon(Data, PCB_TYPE_PIN, Element, pin);
+		}
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		if (Data) {
+			r_delete_entry(Data->pad_tree, (BoxType *) pad);
+			RestoreToPolygon(Data, PCB_TYPE_PAD, Element, pad);
+		}
+		MOVE_PAD_LOWLEVEL(pad, DX, DY);
+		if (Data) {
+			r_insert_entry(Data->pad_tree, (BoxType *) pad, 0);
+			ClearFromPolygon(Data, PCB_TYPE_PAD, Element, pad);
+		}
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		MOVE_ARC_LOWLEVEL(arc, DX, DY);
+	}
+	END_LOOP;
+	ELEMENTTEXT_LOOP(Element);
+	{
+		if (Data && Data->name_tree[n])
+			r_delete_entry(PCB->Data->name_tree[n], (BoxType *) text);
+		MOVE_TEXT_LOWLEVEL(text, DX, DY);
+		if (Data && Data->name_tree[n])
+			r_insert_entry(PCB->Data->name_tree[n], (BoxType *) text, 0);
+	}
+	END_LOOP;
+	MOVE_BOX_LOWLEVEL(&Element->BoundingBox, DX, DY);
+	MOVE_BOX_LOWLEVEL(&Element->VBox, DX, DY);
+	MOVE(Element->MarkX, Element->MarkY, DX, DY);
+	if (Data)
+		r_insert_entry(Data->element_tree, (BoxType *) Element, 0);
+}
+
+/* ----------------------------------------------------------------------
+ * moves all names of an element to a new position
+ */
+static void *MoveElementName(ElementTypePtr Element)
+{
+	if (PCB->ElementOn && (FRONT(Element) || PCB->InvisibleObjectsOn)) {
+		EraseElementName(Element);
+		ELEMENTTEXT_LOOP(Element);
+		{
+			if (PCB->Data->name_tree[n])
+				r_delete_entry(PCB->Data->name_tree[n], (BoxType *) text);
+			MOVE_TEXT_LOWLEVEL(text, DeltaX, DeltaY);
+			if (PCB->Data->name_tree[n])
+				r_insert_entry(PCB->Data->name_tree[n], (BoxType *) text, 0);
+		}
+		END_LOOP;
+		DrawElementName(Element);
+		Draw();
+	}
+	else {
+		ELEMENTTEXT_LOOP(Element);
+		{
+			if (PCB->Data->name_tree[n])
+				r_delete_entry(PCB->Data->name_tree[n], (BoxType *) text);
+			MOVE_TEXT_LOWLEVEL(text, DeltaX, DeltaY);
+			if (PCB->Data->name_tree[n])
+				r_insert_entry(PCB->Data->name_tree[n], (BoxType *) text, 0);
+		}
+		END_LOOP;
+	}
+	return (Element);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves an element
+ */
+static void *MoveElement(ElementTypePtr Element)
+{
+	pcb_bool didDraw = pcb_false;
+
+	if (PCB->ElementOn && (FRONT(Element) || PCB->InvisibleObjectsOn)) {
+		EraseElement(Element);
+		MoveElementLowLevel(PCB->Data, Element, DeltaX, DeltaY);
+		DrawElementName(Element);
+		DrawElementPackage(Element);
+		didDraw = pcb_true;
+	}
+	else {
+		if (PCB->PinOn)
+			EraseElementPinsAndPads(Element);
+		MoveElementLowLevel(PCB->Data, Element, DeltaX, DeltaY);
+	}
+	if (PCB->PinOn) {
+		DrawElementPinsAndPads(Element);
+		didDraw = pcb_true;
+	}
+	if (didDraw)
+		Draw();
+	return (Element);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a via
+ */
+static void *MoveVia(PinTypePtr Via)
+{
+	r_delete_entry(PCB->Data->via_tree, (BoxType *) Via);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	MOVE_VIA_LOWLEVEL(Via, DeltaX, DeltaY);
+	if (PCB->ViaOn)
+		EraseVia(Via);
+	r_insert_entry(PCB->Data->via_tree, (BoxType *) Via, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, Via, Via);
+	if (PCB->ViaOn) {
+		DrawVia(Via);
+		Draw();
+	}
+	return (Via);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a line
+ */
+static void *MoveLine(LayerTypePtr Layer, LineTypePtr Line)
+{
+	if (Layer->On)
+		EraseLine(Line);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	r_delete_entry(Layer->line_tree, (BoxType *) Line);
+	MOVE_LINE_LOWLEVEL(Line, DeltaX, DeltaY);
+	r_insert_entry(Layer->line_tree, (BoxType *) Line, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	if (Layer->On) {
+		DrawLine(Layer, Line);
+		Draw();
+	}
+	return (Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves an arc
+ */
+static void *MoveArc(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+	r_delete_entry(Layer->arc_tree, (BoxType *) Arc);
+	if (Layer->On) {
+		EraseArc(Arc);
+		MOVE_ARC_LOWLEVEL(Arc, DeltaX, DeltaY);
+		DrawArc(Layer, Arc);
+		Draw();
+	}
+	else {
+		MOVE_ARC_LOWLEVEL(Arc, DeltaX, DeltaY);
+	}
+	r_insert_entry(Layer->arc_tree, (BoxType *) Arc, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+	return (Arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a text object
+ */
+static void *MoveText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	RestoreToPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	r_delete_entry(Layer->text_tree, (BoxType *) Text);
+	if (Layer->On) {
+		EraseText(Layer, Text);
+		MOVE_TEXT_LOWLEVEL(Text, DeltaX, DeltaY);
+		DrawText(Layer, Text);
+		Draw();
+	}
+	else
+		MOVE_TEXT_LOWLEVEL(Text, DeltaX, DeltaY);
+	r_insert_entry(Layer->text_tree, (BoxType *) Text, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	return (Text);
+}
+
+/* ---------------------------------------------------------------------------
+ * low level routine to move a polygon
+ */
+void MovePolygonLowLevel(PolygonTypePtr Polygon, Coord DeltaX, Coord DeltaY)
+{
+	POLYGONPOINT_LOOP(Polygon);
+	{
+		MOVE(point->X, point->Y, DeltaX, DeltaY);
+	}
+	END_LOOP;
+	MOVE_BOX_LOWLEVEL(&Polygon->BoundingBox, DeltaX, DeltaY);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a polygon
+ */
+static void *MovePolygon(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	if (Layer->On) {
+		ErasePolygon(Polygon);
+	}
+	r_delete_entry(Layer->polygon_tree, (BoxType *) Polygon);
+	MovePolygonLowLevel(Polygon, DeltaX, DeltaY);
+	r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+	InitClip(PCB->Data, Layer, Polygon);
+	if (Layer->On) {
+		DrawPolygon(Layer, Polygon);
+		Draw();
+	}
+	return (Polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves one end of a line
+ */
+static void *MoveLinePoint(LayerTypePtr Layer, LineTypePtr Line, PointTypePtr Point)
+{
+	if (Layer) {
+		if (Layer->On)
+			EraseLine(Line);
+		RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		r_delete_entry(Layer->line_tree, &Line->BoundingBox);
+		MOVE(Point->X, Point->Y, DeltaX, DeltaY);
+		SetLineBoundingBox(Line);
+		r_insert_entry(Layer->line_tree, &Line->BoundingBox, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		if (Layer->On) {
+			DrawLine(Layer, Line);
+			Draw();
+		}
+		return (Line);
+	}
+	else {												/* must be a rat */
+
+		if (PCB->RatOn)
+			EraseRat((RatTypePtr) Line);
+		r_delete_entry(PCB->Data->rat_tree, &Line->BoundingBox);
+		MOVE(Point->X, Point->Y, DeltaX, DeltaY);
+		SetLineBoundingBox(Line);
+		r_insert_entry(PCB->Data->rat_tree, &Line->BoundingBox, 0);
+		if (PCB->RatOn) {
+			DrawRat((RatTypePtr) Line);
+			Draw();
+		}
+		return (Line);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a polygon-point
+ */
+static void *MovePolygonPoint(LayerTypePtr Layer, PolygonTypePtr Polygon, PointTypePtr Point)
+{
+	if (Layer->On) {
+		ErasePolygon(Polygon);
+	}
+	r_delete_entry(Layer->polygon_tree, (BoxType *) Polygon);
+	MOVE(Point->X, Point->Y, DeltaX, DeltaY);
+	SetPolygonBoundingBox(Polygon);
+	r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+	RemoveExcessPolygonPoints(Layer, Polygon);
+	InitClip(PCB->Data, Layer, Polygon);
+	if (Layer->On) {
+		DrawPolygon(Layer, Polygon);
+		Draw();
+	}
+	return (Point);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a line between layers; lowlevel routines
+ */
+static void *MoveLineToLayerLowLevel(LayerType * Source, LineType * line, LayerType * Destination)
+{
+	r_delete_entry(Source->line_tree, (BoxType *) line);
+
+	linelist_remove(line);
+	linelist_append(&(Destination->Line), line);
+
+	if (!Destination->line_tree)
+		Destination->line_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Destination->line_tree, (BoxType *) line, 0);
+	return line;
+}
+
+/* ---------------------------------------------------------------------------
+ * moves an arc between layers; lowlevel routines
+ */
+static void *MoveArcToLayerLowLevel(LayerType * Source, ArcType * arc, LayerType * Destination)
+{
+	r_delete_entry(Source->arc_tree, (BoxType *) arc);
+
+	arclist_remove(arc);
+	arclist_append(&Destination->Arc, arc);
+
+	if (!Destination->arc_tree)
+		Destination->arc_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Destination->arc_tree, (BoxType *) arc, 0);
+	return arc;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * moves an arc between layers
+ */
+static void *MoveArcToLayer(LayerType * Layer, ArcType * Arc)
+{
+	ArcTypePtr newone;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Arc)) {
+		Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+		return NULL;
+	}
+	if (Dest == Layer && Layer->On) {
+		DrawArc(Layer, Arc);
+		Draw();
+	}
+	if (((long int) Dest == -1) || Dest == Layer)
+		return (Arc);
+	AddObjectToMoveToLayerUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_ARC, Layer, Arc);
+	if (Layer->On)
+		EraseArc(Arc);
+	newone = (ArcTypePtr) MoveArcToLayerLowLevel(Layer, Arc, Dest);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_ARC, Dest, Arc);
+	if (Dest->On)
+		DrawArc(Dest, newone);
+	Draw();
+	return (newone);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a line between layers
+ */
+static void *MoveRatToLayer(RatType * Rat)
+{
+	LineTypePtr newone;
+	/*Coord X1 = Rat->Point1.X, Y1 = Rat->Point1.Y;
+	   Coord X1 = Rat->Point1.X, Y1 = Rat->Point1.Y;
+	   if PCB_FLAG_VIA
+	   if we're on a pin, add a thermal
+	   else make a via and a wire, but 0-length wire not good
+	   else as before */
+
+	newone = CreateNewLineOnLayer(Dest, Rat->Point1.X, Rat->Point1.Y,
+																Rat->Point2.X, Rat->Point2.Y, conf_core.design.line_thickness, 2 * conf_core.design.clearance, Rat->Flags);
+	if (conf_core.editor.clear_line)
+		conf_set_editor(clear_line, 1);
+	if (!newone)
+		return (NULL);
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, Dest, newone, newone);
+	if (PCB->RatOn)
+		EraseRat(Rat);
+	MoveObjectToRemoveUndoList(PCB_TYPE_RATLINE, Rat, Rat, Rat);
+	DrawLine(Dest, newone);
+	Draw();
+	return (newone);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a line between layers
+ */
+
+struct via_info {
+	Coord X, Y;
+	jmp_buf env;
+};
+
+static r_dir_t moveline_callback(const BoxType * b, void *cl)
+{
+	struct via_info *i = (struct via_info *) cl;
+	PinTypePtr via;
+
+	if ((via =
+			 CreateNewVia(PCB->Data, i->X, i->Y,
+										conf_core.design.via_thickness, 2 * conf_core.design.clearance, PCB_FLAG_NO, conf_core.design.via_drilling_hole, NULL, NoFlags())) != NULL) {
+		AddObjectToCreateUndoList(PCB_TYPE_VIA, via, via, via);
+		DrawVia(via);
+	}
+	longjmp(i->env, 1);
+}
+
+static void *MoveLineToLayer(LayerType * Layer, LineType * Line)
+{
+	struct via_info info;
+	BoxType sb;
+	LineTypePtr newone;
+	void *ptr1, *ptr2, *ptr3;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Line)) {
+		Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+		return NULL;
+	}
+	if (Dest == Layer && Layer->On) {
+		DrawLine(Layer, Line);
+		Draw();
+	}
+	if (((long int) Dest == -1) || Dest == Layer)
+		return (Line);
+
+	AddObjectToMoveToLayerUndoList(PCB_TYPE_LINE, Layer, Line, Line);
+	if (Layer->On)
+		EraseLine(Line);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+	newone = (LineTypePtr) MoveLineToLayerLowLevel(Layer, Line, Dest);
+	Line = NULL;
+	ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Dest, newone);
+	if (Dest->On)
+		DrawLine(Dest, newone);
+	Draw();
+	if (!PCB->ViaOn || MoreToCome ||
+			GetLayerGroupNumberByPointer(Layer) ==
+			GetLayerGroupNumberByPointer(Dest) || TEST_SILK_LAYER(Layer) || TEST_SILK_LAYER(Dest))
+		return (newone);
+	/* consider via at Point1 */
+	sb.X1 = newone->Point1.X - newone->Thickness / 2;
+	sb.X2 = newone->Point1.X + newone->Thickness / 2;
+	sb.Y1 = newone->Point1.Y - newone->Thickness / 2;
+	sb.Y2 = newone->Point1.Y + newone->Thickness / 2;
+	if ((SearchObjectByLocation(PCB_TYPEMASK_PIN, &ptr1, &ptr2, &ptr3,
+															newone->Point1.X, newone->Point1.Y, conf_core.design.via_thickness / 2) == PCB_TYPE_NONE)) {
+		info.X = newone->Point1.X;
+		info.Y = newone->Point1.Y;
+		if (setjmp(info.env) == 0)
+			r_search(Layer->line_tree, &sb, NULL, moveline_callback, &info, NULL);
+	}
+	/* consider via at Point2 */
+	sb.X1 = newone->Point2.X - newone->Thickness / 2;
+	sb.X2 = newone->Point2.X + newone->Thickness / 2;
+	sb.Y1 = newone->Point2.Y - newone->Thickness / 2;
+	sb.Y2 = newone->Point2.Y + newone->Thickness / 2;
+	if ((SearchObjectByLocation(PCB_TYPEMASK_PIN, &ptr1, &ptr2, &ptr3,
+															newone->Point2.X, newone->Point2.Y, conf_core.design.via_thickness / 2) == PCB_TYPE_NONE)) {
+		info.X = newone->Point2.X;
+		info.Y = newone->Point2.Y;
+		if (setjmp(info.env) == 0)
+			r_search(Layer->line_tree, &sb, NULL, moveline_callback, &info, NULL);
+	}
+	Draw();
+	return (newone);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a text object between layers; lowlevel routines
+ */
+static void *MoveTextToLayerLowLevel(LayerType * Source, TextType * text, LayerType * Destination)
+{
+	RestoreToPolygon(PCB->Data, PCB_TYPE_TEXT, Source, text);
+	r_delete_entry(Source->text_tree, (BoxType *) text);
+
+	textlist_remove(text);
+	textlist_append(&Destination->Text, text);
+
+	if (GetLayerGroupNumberByNumber(solder_silk_layer) == GetLayerGroupNumberByPointer(Destination))
+		SET_FLAG(PCB_FLAG_ONSOLDER, text);
+	else
+		CLEAR_FLAG(PCB_FLAG_ONSOLDER, text);
+
+	/* re-calculate the bounding box (it could be mirrored now) */
+	SetTextBoundingBox(&PCB->Font, text);
+	if (!Destination->text_tree)
+		Destination->text_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Destination->text_tree, (BoxType *) text, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_TEXT, Destination, text);
+
+	return text;
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a text object between layers
+ */
+static void *MoveTextToLayer(LayerType * layer, TextType * text)
+{
+	if (TEST_FLAG(PCB_FLAG_LOCK, text)) {
+		Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+		return NULL;
+	}
+	if (Dest != layer) {
+		AddObjectToMoveToLayerUndoList(PCB_TYPE_TEXT, layer, text, text);
+		if (layer->On)
+			EraseText(layer, text);
+		text = MoveTextToLayerLowLevel(layer, text, Dest);
+		if (Dest->On)
+			DrawText(Dest, text);
+		if (layer->On || Dest->On)
+			Draw();
+	}
+	return text;
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a polygon between layers; lowlevel routines
+ */
+static void *MovePolygonToLayerLowLevel(LayerType * Source, PolygonType * polygon, LayerType * Destination)
+{
+	r_delete_entry(Source->polygon_tree, (BoxType *) polygon);
+
+	polylist_remove(polygon);
+	polylist_append(&Destination->Polygon, polygon);
+
+	if (!Destination->polygon_tree)
+		Destination->polygon_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(Destination->polygon_tree, (BoxType *) polygon, 0);
+
+	return polygon;
+}
+
+struct mptlc {
+	pcb_cardinal_t snum, dnum;
+	int type;
+	PolygonTypePtr polygon;
+} mptlc;
+
+r_dir_t mptl_pin_callback(const BoxType * b, void *cl)
+{
+	struct mptlc *d = (struct mptlc *) cl;
+	PinTypePtr pin = (PinTypePtr) b;
+	if (!TEST_THERM(d->snum, pin) || !IsPointInPolygon(pin->X, pin->Y, pin->Thickness + pin->Clearance + 2, d->polygon))
+		return R_DIR_NOT_FOUND;
+	if (d->type == PCB_TYPE_PIN)
+		AddObjectToFlagUndoList(PCB_TYPE_PIN, pin->Element, pin, pin);
+	else
+		AddObjectToFlagUndoList(PCB_TYPE_VIA, pin, pin, pin);
+	ASSIGN_THERM(d->dnum, GET_THERM(d->snum, pin), pin);
+	CLEAR_THERM(d->snum, pin);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* ---------------------------------------------------------------------------
+ * moves a polygon between layers
+ */
+static void *MovePolygonToLayer(LayerType * Layer, PolygonType * Polygon)
+{
+	PolygonTypePtr newone;
+	struct mptlc d;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, Polygon)) {
+		Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+		return NULL;
+	}
+	if (((long int) Dest == -1) || (Layer == Dest))
+		return (Polygon);
+	AddObjectToMoveToLayerUndoList(PCB_TYPE_POLYGON, Layer, Polygon, Polygon);
+	if (Layer->On)
+		ErasePolygon(Polygon);
+	/* Move all of the thermals with the polygon */
+	d.snum = GetLayerNumber(PCB->Data, Layer);
+	d.dnum = GetLayerNumber(PCB->Data, Dest);
+	d.polygon = Polygon;
+	d.type = PCB_TYPE_PIN;
+	r_search(PCB->Data->pin_tree, &Polygon->BoundingBox, NULL, mptl_pin_callback, &d, NULL);
+	d.type = PCB_TYPE_VIA;
+	r_search(PCB->Data->via_tree, &Polygon->BoundingBox, NULL, mptl_pin_callback, &d, NULL);
+	newone = (struct polygon_st *) MovePolygonToLayerLowLevel(Layer, Polygon, Dest);
+	InitClip(PCB->Data, Dest, newone);
+	if (Dest->On) {
+		DrawPolygon(Dest, newone);
+		Draw();
+	}
+	return (newone);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves the object identified by its data pointers and the type
+ * not we don't bump the undo serial number
+ */
+void *MoveObject(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord DX, Coord DY)
+{
+	void *result;
+	/* setup offset */
+	DeltaX = DX;
+	DeltaY = DY;
+	AddObjectToMoveUndoList(Type, Ptr1, Ptr2, Ptr3, DX, DY);
+	result = ObjectOperation(&MoveFunctions, Type, Ptr1, Ptr2, Ptr3);
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves the object identified by its data pointers and the type
+ * as well as all attached rubberband lines
+ */
+void *MoveObjectAndRubberband(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord DX, Coord DY)
+{
+	RubberbandTypePtr ptr;
+	void *ptr2;
+
+	pcb_draw_inhibit_inc();
+
+	/* setup offset */
+	DeltaX = DX;
+	DeltaY = DY;
+	if (DX == 0 && DY == 0) {
+		int n;
+
+		/* first clear any marks that we made in the line flags */
+		for(n = 0, ptr = Crosshair.AttachedObject.Rubberband; n < Crosshair.AttachedObject.RubberbandN; n++, ptr++)
+			CLEAR_FLAG(PCB_FLAG_RUBBEREND, ptr->Line);
+
+		return (NULL);
+	}
+
+	/* move all the lines... and reset the counter */
+	ptr = Crosshair.AttachedObject.Rubberband;
+	while (Crosshair.AttachedObject.RubberbandN) {
+		/* first clear any marks that we made in the line flags */
+		CLEAR_FLAG(PCB_FLAG_RUBBEREND, ptr->Line);
+		AddObjectToMoveUndoList(PCB_TYPE_LINE_POINT, ptr->Layer, ptr->Line, ptr->MovedPoint, DX, DY);
+		MoveLinePoint(ptr->Layer, ptr->Line, ptr->MovedPoint);
+		Crosshair.AttachedObject.RubberbandN--;
+		ptr++;
+	}
+
+	AddObjectToMoveUndoList(Type, Ptr1, Ptr2, Ptr3, DX, DY);
+	ptr2 = ObjectOperation(&MoveFunctions, Type, Ptr1, Ptr2, Ptr3);
+	IncrementUndoSerialNumber();
+
+	pcb_draw_inhibit_dec();
+	Draw();
+
+	return (ptr2);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves the object identified by its data pointers and the type
+ * to a new layer without changing it's position
+ */
+void *MoveObjectToLayer(int Type, void *Ptr1, void *Ptr2, void *Ptr3, LayerTypePtr Target, pcb_bool enmasse)
+{
+	void *result;
+
+	/* setup global identifiers */
+	Dest = Target;
+	MoreToCome = enmasse;
+	result = ObjectOperation(&MoveToLayerFunctions, Type, Ptr1, Ptr2, Ptr3);
+	IncrementUndoSerialNumber();
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves the selected objects to a new layer without changing their
+ * positions
+ */
+pcb_bool MoveSelectedObjectsToLayer(LayerTypePtr Target)
+{
+	pcb_bool changed;
+
+	/* setup global identifiers */
+	Dest = Target;
+	MoreToCome = pcb_true;
+	changed = SelectedOperation(&MoveToLayerFunctions, pcb_true, PCB_TYPEMASK_ALL);
+	/* passing pcb_true to above operation causes Undoserial to auto-increment */
+	return (changed);
+}
+
+/* ---------------------------------------------------------------------------
+ * moves the selected layers to a new index in the layer list.
+ */
+
+static void move_one_thermal(int old_index, int new_index, PinType * pin)
+{
+	int t1 = 0, i;
+	int oi = old_index, ni = new_index;
+
+	if (old_index != -1)
+		t1 = GET_THERM(old_index, pin);
+
+	if (oi == -1)
+		oi = MAX_LAYER - 1;					/* inserting a layer */
+	if (ni == -1)
+		ni = MAX_LAYER - 1;					/* deleting a layer */
+
+	if (oi < ni) {
+		for (i = oi; i < ni; i++)
+			ASSIGN_THERM(i, GET_THERM(i + 1, pin), pin);
+	}
+	else {
+		for (i = oi; i > ni; i--)
+			ASSIGN_THERM(i, GET_THERM(i - 1, pin), pin);
+	}
+
+	if (new_index != -1)
+		ASSIGN_THERM(new_index, t1, pin);
+	else
+		ASSIGN_THERM(ni, 0, pin);
+}
+
+static void move_all_thermals(int old_index, int new_index)
+{
+	VIA_LOOP(PCB->Data);
+	{
+		move_one_thermal(old_index, new_index, via);
+	}
+	END_LOOP;
+
+	ALLPIN_LOOP(PCB->Data);
+	{
+		move_one_thermal(old_index, new_index, pin);
+	}
+	ENDALL_LOOP;
+}
+
+static int LastLayerInComponentGroup(int layer)
+{
+	int cgroup = GetLayerGroupNumberByNumber(max_group + COMPONENT_LAYER);
+	int lgroup = GetLayerGroupNumberByNumber(layer);
+	if (cgroup == lgroup && PCB->LayerGroups.Number[lgroup] == 2)
+		return 1;
+	return 0;
+}
+
+static int LastLayerInSolderGroup(int layer)
+{
+	int sgroup = GetLayerGroupNumberByNumber(max_group + SOLDER_LAYER);
+	int lgroup = GetLayerGroupNumberByNumber(layer);
+	if (sgroup == lgroup && PCB->LayerGroups.Number[lgroup] == 2)
+		return 1;
+	return 0;
+}
+
+int MoveLayer(int old_index, int new_index)
+{
+	int groups[MAX_LAYER + 2], l, g;
+	LayerType saved_layer;
+	int saved_group;
+
+	AddLayerChangeToUndoList(old_index, new_index);
+	IncrementUndoSerialNumber();
+
+	if (old_index < -1 || old_index >= max_copper_layer) {
+		Message(PCB_MSG_DEFAULT, "Invalid old layer %d for move: must be -1..%d\n", old_index, max_copper_layer - 1);
+		return 1;
+	}
+	if (new_index < -1 || new_index > max_copper_layer || new_index >= MAX_LAYER) {
+		Message(PCB_MSG_DEFAULT, "Invalid new layer %d for move: must be -1..%d\n", new_index, max_copper_layer);
+		return 1;
+	}
+	if (old_index == new_index)
+		return 0;
+
+	if (new_index == -1 && LastLayerInComponentGroup(old_index)) {
+		gui->confirm_dialog("You can't delete the last top-side layer\n", "Ok", NULL);
+		return 1;
+	}
+
+	if (new_index == -1 && LastLayerInSolderGroup(old_index)) {
+		gui->confirm_dialog("You can't delete the last bottom-side layer\n", "Ok", NULL);
+		return 1;
+	}
+
+	for (g = 0; g < MAX_LAYER + 2; g++)
+		groups[g] = -1;
+
+	for (g = 0; g < MAX_LAYER; g++)
+		for (l = 0; l < PCB->LayerGroups.Number[g]; l++)
+			groups[PCB->LayerGroups.Entries[g][l]] = g;
+
+	if (old_index == -1) {
+		LayerTypePtr lp;
+		if (max_copper_layer == MAX_LAYER) {
+			Message(PCB_MSG_DEFAULT, "No room for new layers\n");
+			return 1;
+		}
+		/* Create a new layer at new_index. */
+		lp = &PCB->Data->Layer[new_index];
+		memmove(&PCB->Data->Layer[new_index + 1],
+						&PCB->Data->Layer[new_index], (max_copper_layer - new_index + 2) * sizeof(LayerType));
+		memmove(&groups[new_index + 1], &groups[new_index], (max_copper_layer - new_index + 2) * sizeof(int));
+		max_copper_layer++;
+		memset(lp, 0, sizeof(LayerType));
+		lp->On = 1;
+		lp->Name = pcb_strdup("New Layer");
+		lp->Color = conf_core.appearance.color.layer[new_index];
+		lp->SelectedColor = conf_core.appearance.color.layer_selected[new_index];
+		for (l = 0; l < max_copper_layer; l++)
+			if (LayerStack[l] >= new_index)
+				LayerStack[l]++;
+		LayerStack[max_copper_layer - 1] = new_index;
+	}
+	else if (new_index == -1) {
+		/* Delete the layer at old_index */
+		memmove(&PCB->Data->Layer[old_index],
+						&PCB->Data->Layer[old_index + 1], (max_copper_layer - old_index + 2 - 1) * sizeof(LayerType));
+		memset(&PCB->Data->Layer[max_copper_layer + 1], 0, sizeof(LayerType));
+		memmove(&groups[old_index], &groups[old_index + 1], (max_copper_layer - old_index + 2 - 1) * sizeof(int));
+		for (l = 0; l < max_copper_layer; l++)
+			if (LayerStack[l] == old_index)
+				memmove(LayerStack + l, LayerStack + l + 1, (max_copper_layer - l - 1) * sizeof(LayerStack[0]));
+		max_copper_layer--;
+		for (l = 0; l < max_copper_layer; l++)
+			if (LayerStack[l] > old_index)
+				LayerStack[l]--;
+	}
+	else {
+		/* Move an existing layer */
+		memcpy(&saved_layer, &PCB->Data->Layer[old_index], sizeof(LayerType));
+		saved_group = groups[old_index];
+		if (old_index < new_index) {
+			memmove(&PCB->Data->Layer[old_index], &PCB->Data->Layer[old_index + 1], (new_index - old_index) * sizeof(LayerType));
+			memmove(&groups[old_index], &groups[old_index + 1], (new_index - old_index) * sizeof(int));
+		}
+		else {
+			memmove(&PCB->Data->Layer[new_index + 1], &PCB->Data->Layer[new_index], (old_index - new_index) * sizeof(LayerType));
+			memmove(&groups[new_index + 1], &groups[new_index], (old_index - new_index) * sizeof(int));
+		}
+		memcpy(&PCB->Data->Layer[new_index], &saved_layer, sizeof(LayerType));
+		groups[new_index] = saved_group;
+	}
+
+	move_all_thermals(old_index, new_index);
+
+	for (g = 0; g < MAX_LAYER; g++)
+		PCB->LayerGroups.Number[g] = 0;
+	for (l = 0; l < max_copper_layer + 2; l++) {
+		int i;
+		g = groups[l];
+		if (g >= 0) {
+			i = PCB->LayerGroups.Number[g]++;
+			PCB->LayerGroups.Entries[g][i] = l;
+		}
+	}
+
+	for (g = 0; g < MAX_LAYER; g++)
+		if (PCB->LayerGroups.Number[g] == 0) {
+			memmove(&PCB->LayerGroups.Number[g],
+							&PCB->LayerGroups.Number[g + 1], (MAX_LAYER - g - 1) * sizeof(PCB->LayerGroups.Number[g]));
+			memmove(&PCB->LayerGroups.Entries[g],
+							&PCB->LayerGroups.Entries[g + 1], (MAX_LAYER - g - 1) * sizeof(PCB->LayerGroups.Entries[g]));
+		}
+
+	hid_action("LayersChanged");
+	gui->invalidate_all();
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char movelayer_syntax[] = "MoveLayer(old,new)";
+
+static const char movelayer_help[] = "Moves/Creates/Deletes Layers.";
+
+/* %start-doc actions MoveLayer
+
+Moves a layer, creates a new layer, or deletes a layer.
+
+ at table @code
+
+ at item old
+The is the layer number to act upon.  Allowed values are:
+ at table @code
+
+ at item c
+Currently selected layer.
+
+ at item -1
+Create a new layer.
+
+ at item number
+An existing layer number.
+
+ at end table
+
+ at item new
+Specifies where to move the layer to.  Allowed values are:
+ at table @code
+ at item -1
+Deletes the layer.
+
+ at item up
+Moves the layer up.
+
+ at item down
+Moves the layer down.
+
+ at item c
+Creates a new layer.
+
+ at end table
+
+ at end table
+
+%end-doc */
+
+int MoveLayerAction(int argc, const char **argv, Coord x, Coord y)
+{
+	int old_index, new_index;
+	int new_top = -1;
+
+	if (argc != 2) {
+		Message(PCB_MSG_DEFAULT, "Usage; MoveLayer(old,new)");
+		return 1;
+	}
+
+	if (strcmp(argv[0], "c") == 0)
+		old_index = INDEXOFCURRENT;
+	else
+		old_index = atoi(argv[0]);
+
+	if (strcmp(argv[1], "c") == 0) {
+		new_index = INDEXOFCURRENT;
+		if (new_index < 0)
+			new_index = 0;
+	}
+	else if (strcmp(argv[1], "up") == 0) {
+		new_index = INDEXOFCURRENT - 1;
+		if (new_index < 0)
+			return 1;
+		new_top = new_index;
+	}
+	else if (strcmp(argv[1], "down") == 0) {
+		new_index = INDEXOFCURRENT + 1;
+		if (new_index >= max_copper_layer)
+			return 1;
+		new_top = new_index;
+	}
+	else
+		new_index = atoi(argv[1]);
+
+	if (MoveLayer(old_index, new_index))
+		return 1;
+
+	if (new_index == -1) {
+		new_top = old_index;
+		if (new_top >= max_copper_layer)
+			new_top--;
+		new_index = new_top;
+	}
+	if (old_index == -1)
+		new_top = new_index;
+
+	if (new_top != -1)
+		ChangeGroupVisibility(new_index, 1, 1);
+
+	return 0;
+}
+
+HID_Action move_action_list[] = {
+	{"MoveLayer", 0, MoveLayerAction,
+	 movelayer_help, movelayer_syntax}
+};
+
+REGISTER_ACTIONS(move_action_list, NULL)
diff --git a/src/move.h b/src/move.h
new file mode 100644
index 0000000..e90e398
--- /dev/null
+++ b/src/move.h
@@ -0,0 +1,106 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for move routines */
+
+#ifndef	PCB_MOVE_H
+#define	PCB_MOVE_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some useful transformation macros and constants
+ */
+#define	MOVE(xs,ys,deltax,deltay)							\
+	{														\
+		((xs) += (deltax));									\
+		((ys) += (deltay));									\
+	}
+#define	MOVE_BOX_LOWLEVEL(b,dx,dy)		\
+	{									\
+		MOVE((b)->X1,(b)->Y1,(dx),(dy))	\
+		MOVE((b)->X2,(b)->Y2,(dx),(dy))	\
+	}
+#define	MOVE_VIA_LOWLEVEL(v,dx,dy) \
+        { \
+	        MOVE((v)->X,(v)->Y,(dx),(dy)) \
+		MOVE_BOX_LOWLEVEL(&((v)->BoundingBox),(dx),(dy));		\
+	}
+#define	MOVE_PIN_LOWLEVEL(p,dx,dy) \
+	{ \
+		MOVE((p)->X,(p)->Y,(dx),(dy)) \
+		MOVE_BOX_LOWLEVEL(&((p)->BoundingBox),(dx),(dy));		\
+	}
+
+#define	MOVE_ARC_LOWLEVEL(a,dx,dy) \
+	{ \
+		MOVE((a)->X,(a)->Y,(dx),(dy)) \
+		MOVE_BOX_LOWLEVEL(&((a)->BoundingBox),(dx),(dy));		\
+	}
+/* Rather than mode the line bounding box, we set it so the point bounding
+ * boxes are updated too.
+ */
+#define	MOVE_LINE_LOWLEVEL(l,dx,dy)							\
+	{									\
+		MOVE((l)->Point1.X,(l)->Point1.Y,(dx),(dy))			\
+		MOVE((l)->Point2.X,(l)->Point2.Y,(dx),(dy))			\
+		SetLineBoundingBox ((l)); \
+	}
+#define	MOVE_PAD_LOWLEVEL(p,dx,dy)	\
+	{									\
+		MOVE((p)->Point1.X,(p)->Point1.Y,(dx),(dy))			\
+		MOVE((p)->Point2.X,(p)->Point2.Y,(dx),(dy))			\
+		SetPadBoundingBox ((p)); \
+	}
+#define	MOVE_TEXT_LOWLEVEL(t,dx,dy)								\
+	{															\
+		MOVE_BOX_LOWLEVEL(&((t)->BoundingBox),(dx),(dy));		\
+		MOVE((t)->X, (t)->Y, (dx), (dy));						\
+	}
+
+#define	MOVE_TYPES	\
+	(PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_TEXT | PCB_TYPE_ELEMENT | PCB_TYPE_ELEMENT_NAME |	\
+	PCB_TYPE_POLYGON | PCB_TYPE_POLYGON_POINT | PCB_TYPE_LINE_POINT | PCB_TYPE_ARC)
+#define	MOVETOLAYER_TYPES	\
+	(PCB_TYPE_LINE | PCB_TYPE_TEXT | PCB_TYPE_POLYGON | PCB_TYPE_RATLINE | PCB_TYPE_ARC)
+
+
+/* ---------------------------------------------------------------------------
+ * prototypes
+ */
+void MovePolygonLowLevel(PolygonTypePtr, Coord, Coord);
+void MoveElementLowLevel(DataTypePtr, ElementTypePtr, Coord, Coord);
+void *MoveObject(int, void *, void *, void *, Coord, Coord);
+void *MoveObjectToLayer(int, void *, void *, void *, LayerTypePtr, pcb_bool);
+void *MoveObjectAndRubberband(int, void *, void *, void *, Coord, Coord);
+pcb_bool MoveSelectedObjectsToLayer(LayerTypePtr);
+
+/* index is 0..MAX_LAYER-1.  If old_index is -1, a new layer is
+   inserted at that index.  If new_index is -1, the specified layer is
+   deleted.  Returns non-zero on error, zero if OK.  */
+int MoveLayer(int old_index, int new_index);
+
+#endif
diff --git a/src/mymem.c b/src/mymem.c
new file mode 100644
index 0000000..e851545
--- /dev/null
+++ b/src/mymem.c
@@ -0,0 +1,765 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* memory management functions
+ */
+
+#include "config.h"
+
+#include <memory.h>
+
+#include "data.h"
+#include "mymem.h"
+#include "rtree.h"
+#include "rats_patch.h"
+
+/* ---------------------------------------------------------------------------
+ * local prototypes
+ */
+/* memset object to 0, but keep the link field */
+#define reset_obj_mem(type, obj) \
+do { \
+	gdl_elem_t __lnk__ = obj->link; \
+	memset(obj, 0, sizeof(type)); \
+	obj->link = __lnk__; \
+} while(0) \
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a rubberband connection, allocates memory if necessary
+ */
+RubberbandTypePtr GetRubberbandMemory(void)
+{
+	RubberbandTypePtr ptr = Crosshair.AttachedObject.Rubberband;
+
+	/* realloc new memory if necessary and clear it */
+	if (Crosshair.AttachedObject.RubberbandN >= Crosshair.AttachedObject.RubberbandMax) {
+		Crosshair.AttachedObject.RubberbandMax += STEP_RUBBERBAND;
+		ptr = (RubberbandTypePtr) realloc(ptr, Crosshair.AttachedObject.RubberbandMax * sizeof(RubberbandType));
+		Crosshair.AttachedObject.Rubberband = ptr;
+		memset(ptr + Crosshair.AttachedObject.RubberbandN, 0, STEP_RUBBERBAND * sizeof(RubberbandType));
+	}
+	return (ptr + Crosshair.AttachedObject.RubberbandN++);
+}
+
+void **GetPointerMemory(PointerListTypePtr list)
+{
+	void **ptr = list->Ptr;
+
+	/* realloc new memory if necessary and clear it */
+	if (list->PtrN >= list->PtrMax) {
+		list->PtrMax = STEP_POINT + (2 * list->PtrMax);
+		ptr = (void **) realloc(ptr, list->PtrMax * sizeof(void *));
+		list->Ptr = ptr;
+		memset(ptr + list->PtrN, 0, (list->PtrMax - list->PtrN) * sizeof(void *));
+	}
+	return (ptr + list->PtrN++);
+}
+
+void FreePointerListMemory(PointerListTypePtr list)
+{
+	free(list->Ptr);
+	memset(list, 0, sizeof(PointerListType));
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a box, allocates memory if necessary
+ */
+BoxTypePtr GetBoxMemory(BoxListTypePtr Boxes)
+{
+	BoxTypePtr box = Boxes->Box;
+
+	/* realloc new memory if necessary and clear it */
+	if (Boxes->BoxN >= Boxes->BoxMax) {
+		Boxes->BoxMax = STEP_POINT + (2 * Boxes->BoxMax);
+		box = (BoxTypePtr) realloc(box, Boxes->BoxMax * sizeof(BoxType));
+		Boxes->Box = box;
+		memset(box + Boxes->BoxN, 0, (Boxes->BoxMax - Boxes->BoxN) * sizeof(BoxType));
+	}
+	return (box + Boxes->BoxN++);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a connection, allocates memory if necessary
+ */
+ConnectionTypePtr GetConnectionMemory(NetTypePtr Net)
+{
+	ConnectionTypePtr con = Net->Connection;
+
+	/* realloc new memory if necessary and clear it */
+	if (Net->ConnectionN >= Net->ConnectionMax) {
+		Net->ConnectionMax += STEP_POINT;
+		con = (ConnectionTypePtr) realloc(con, Net->ConnectionMax * sizeof(ConnectionType));
+		Net->Connection = con;
+		memset(con + Net->ConnectionN, 0, STEP_POINT * sizeof(ConnectionType));
+	}
+	return (con + Net->ConnectionN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a subnet, allocates memory if necessary
+ */
+NetTypePtr GetNetMemory(NetListTypePtr Netlist)
+{
+	NetTypePtr net = Netlist->Net;
+
+	/* realloc new memory if necessary and clear it */
+	if (Netlist->NetN >= Netlist->NetMax) {
+		Netlist->NetMax += STEP_POINT;
+		net = (NetTypePtr) realloc(net, Netlist->NetMax * sizeof(NetType));
+		Netlist->Net = net;
+		memset(net + Netlist->NetN, 0, STEP_POINT * sizeof(NetType));
+	}
+	return (net + Netlist->NetN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a net list, allocates memory if necessary
+ */
+NetListTypePtr GetNetListMemory(NetListListTypePtr Netlistlist)
+{
+	NetListTypePtr netlist = Netlistlist->NetList;
+
+	/* realloc new memory if necessary and clear it */
+	if (Netlistlist->NetListN >= Netlistlist->NetListMax) {
+		Netlistlist->NetListMax += STEP_POINT;
+		netlist = (NetListTypePtr) realloc(netlist, Netlistlist->NetListMax * sizeof(NetListType));
+		Netlistlist->NetList = netlist;
+		memset(netlist + Netlistlist->NetListN, 0, STEP_POINT * sizeof(NetListType));
+	}
+	return (netlist + Netlistlist->NetListN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a pin, allocates memory if necessary
+ */
+PinType *GetPinMemory(ElementType * element)
+{
+	PinType *new_obj;
+
+	new_obj = calloc(sizeof(PinType), 1);
+	pinlist_append(&element->Pin, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreePin(PinType * data)
+{
+	pinlist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a pad, allocates memory if necessary
+ */
+PadType *GetPadMemory(ElementType * element)
+{
+	PadType *new_obj;
+
+	new_obj = calloc(sizeof(PadType), 1);
+	padlist_append(&element->Pad, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreePad(PadType * data)
+{
+	padlist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a via, allocates memory if necessary
+ */
+PinType *GetViaMemory(DataType * data)
+{
+	PinType *new_obj;
+
+	new_obj = calloc(sizeof(PinType), 1);
+	pinlist_append(&data->Via, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreeVia(PinType * data)
+{
+	pinlist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a Rat, allocates memory if necessary
+ */
+RatType *GetRatMemory(DataType * data)
+{
+	RatType *new_obj;
+
+	new_obj = calloc(sizeof(RatType), 1);
+	ratlist_append(&data->Rat, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreeRat(RatType * data)
+{
+	ratlist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a line, allocates memory if necessary
+ */
+LineType *GetLineMemory(LayerType * layer)
+{
+	LineType *new_obj;
+
+	new_obj = calloc(sizeof(LineType), 1);
+	linelist_append(&layer->Line, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreeLine(LineType * data)
+{
+	linelist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for an arc, allocates memory if necessary
+ */
+ArcTypePtr GetArcMemory(LayerType * layer)
+{
+	ArcType *new_obj;
+
+	new_obj = calloc(sizeof(ArcType), 1);
+	arclist_append(&layer->Arc, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreeArc(ArcType * data)
+{
+	arclist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a text object, allocates memory if necessary
+ */
+TextTypePtr GetTextMemory(LayerType * layer)
+{
+	TextType *new_obj;
+
+	new_obj = calloc(sizeof(TextType), 1);
+	textlist_append(&layer->Text, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreeText(TextType * data)
+{
+	textlist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a polygon object, allocates memory if necessary
+ */
+PolygonType *GetPolygonMemory(LayerType * layer)
+{
+	PolygonType *new_obj;
+
+	new_obj = calloc(sizeof(PolygonType), 1);
+	polylist_append(&layer->Polygon, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreePolygon(PolygonType * data)
+{
+	polylist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * gets the next slot for a point in a polygon struct, allocates memory
+ * if necessary
+ */
+PointTypePtr GetPointMemoryInPolygon(PolygonTypePtr Polygon)
+{
+	PointTypePtr points = Polygon->Points;
+
+	/* realloc new memory if necessary and clear it */
+	if (Polygon->PointN >= Polygon->PointMax) {
+		Polygon->PointMax += STEP_POLYGONPOINT;
+		points = (PointTypePtr) realloc(points, Polygon->PointMax * sizeof(PointType));
+		Polygon->Points = points;
+		memset(points + Polygon->PointN, 0, STEP_POLYGONPOINT * sizeof(PointType));
+	}
+	return (points + Polygon->PointN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * gets the next slot for a point in a polygon struct, allocates memory
+ * if necessary
+ */
+pcb_cardinal_t *GetHoleIndexMemoryInPolygon(PolygonTypePtr Polygon)
+{
+	pcb_cardinal_t *holeindex = Polygon->HoleIndex;
+
+	/* realloc new memory if necessary and clear it */
+	if (Polygon->HoleIndexN >= Polygon->HoleIndexMax) {
+		Polygon->HoleIndexMax += STEP_POLYGONHOLEINDEX;
+		holeindex = (pcb_cardinal_t *) realloc(holeindex, Polygon->HoleIndexMax * sizeof(int));
+		Polygon->HoleIndex = holeindex;
+		memset(holeindex + Polygon->HoleIndexN, 0, STEP_POLYGONHOLEINDEX * sizeof(int));
+	}
+	return (holeindex + Polygon->HoleIndexN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for an element, allocates memory if necessary
+ */
+ElementType *GetElementMemory(DataType * data)
+{
+	ElementType *new_obj;
+
+	new_obj = calloc(sizeof(ElementType), 1);
+	elementlist_append(&data->Element, new_obj);
+
+	return new_obj;
+}
+
+void RemoveFreeElement(ElementType * data)
+{
+	elementlist_remove(data);
+	free(data);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a library menu, allocates memory if necessary
+ */
+LibraryMenuTypePtr GetLibraryMenuMemory(LibraryTypePtr lib, int *idx)
+{
+	LibraryMenuTypePtr menu = lib->Menu;
+
+	/* realloc new memory if necessary and clear it */
+	if (lib->MenuN >= lib->MenuMax) {
+		lib->MenuMax += STEP_LIBRARYMENU;
+		menu = (LibraryMenuTypePtr) realloc(menu, lib->MenuMax * sizeof(LibraryMenuType));
+		lib->Menu = menu;
+		memset(menu + lib->MenuN, 0, STEP_LIBRARYMENU * sizeof(LibraryMenuType));
+	}
+	if (idx != NULL)
+		*idx = lib->MenuN;
+	return (menu + lib->MenuN++);
+}
+
+void DeleteLibraryMenuMemory(LibraryTypePtr lib, int menuidx)
+{
+	LibraryMenuTypePtr menu = lib->Menu;
+
+	if (menu[menuidx].Name != NULL)
+		free(menu[menuidx].Name);
+	if (menu[menuidx].directory != NULL)
+		free(menu[menuidx].directory);
+
+	lib->MenuN--;
+	memmove(menu + menuidx, menu + menuidx + 1, sizeof(LibraryMenuType) * (lib->MenuN - menuidx));
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a library entry, allocates memory if necessary
+ */
+LibraryEntryTypePtr GetLibraryEntryMemory(LibraryMenuTypePtr Menu)
+{
+	LibraryEntryTypePtr entry = Menu->Entry;
+
+	/* realloc new memory if necessary and clear it */
+	if (Menu->EntryN >= Menu->EntryMax) {
+		Menu->EntryMax += STEP_LIBRARYENTRY;
+		entry = (LibraryEntryTypePtr) realloc(entry, Menu->EntryMax * sizeof(LibraryEntryType));
+		Menu->Entry = entry;
+		memset(entry + Menu->EntryN, 0, STEP_LIBRARYENTRY * sizeof(LibraryEntryType));
+	}
+	return (entry + Menu->EntryN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a DrillElement, allocates memory if necessary
+ */
+ElementTypeHandle GetDrillElementMemory(DrillTypePtr Drill)
+{
+	ElementTypePtr *element;
+
+	element = Drill->Element;
+
+	/* realloc new memory if necessary and clear it */
+	if (Drill->ElementN >= Drill->ElementMax) {
+		Drill->ElementMax += STEP_ELEMENT;
+		element = (ElementTypePtr *) realloc(element, Drill->ElementMax * sizeof(ElementTypeHandle));
+		Drill->Element = element;
+		memset(element + Drill->ElementN, 0, STEP_ELEMENT * sizeof(ElementTypeHandle));
+	}
+	return (element + Drill->ElementN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a DrillPoint, allocates memory if necessary
+ */
+PinTypeHandle GetDrillPinMemory(DrillTypePtr Drill)
+{
+	PinTypePtr *pin;
+
+	pin = Drill->Pin;
+
+	/* realloc new memory if necessary and clear it */
+	if (Drill->PinN >= Drill->PinMax) {
+		Drill->PinMax += STEP_POINT;
+		pin = (PinTypePtr *) realloc(pin, Drill->PinMax * sizeof(PinTypeHandle));
+		Drill->Pin = pin;
+		memset(pin + Drill->PinN, 0, STEP_POINT * sizeof(PinTypeHandle));
+	}
+	return (pin + Drill->PinN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * get next slot for a Drill, allocates memory if necessary
+ */
+DrillTypePtr GetDrillInfoDrillMemory(DrillInfoTypePtr DrillInfo)
+{
+	DrillTypePtr drill = DrillInfo->Drill;
+
+	/* realloc new memory if necessary and clear it */
+	if (DrillInfo->DrillN >= DrillInfo->DrillMax) {
+		DrillInfo->DrillMax += STEP_DRILL;
+		drill = (DrillTypePtr) realloc(drill, DrillInfo->DrillMax * sizeof(DrillType));
+		DrillInfo->Drill = drill;
+		memset(drill + DrillInfo->DrillN, 0, STEP_DRILL * sizeof(DrillType));
+	}
+	return (drill + DrillInfo->DrillN++);
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by a polygon
+ */
+void FreePolygonMemory(PolygonType * polygon)
+{
+	if (polygon == NULL)
+		return;
+
+	free(polygon->Points);
+	free(polygon->HoleIndex);
+
+	if (polygon->Clipped)
+		poly_Free(&polygon->Clipped);
+	poly_FreeContours(&polygon->NoHoles);
+
+	reset_obj_mem(PolygonType, polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by a box list
+ */
+void FreeBoxListMemory(BoxListTypePtr Boxlist)
+{
+	if (Boxlist) {
+		free(Boxlist->Box);
+		memset(Boxlist, 0, sizeof(BoxListType));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by a net
+ */
+void FreeNetListMemory(NetListTypePtr Netlist)
+{
+	if (Netlist) {
+		NET_LOOP(Netlist);
+		{
+			FreeNetMemory(net);
+		}
+		END_LOOP;
+		free(Netlist->Net);
+		memset(Netlist, 0, sizeof(NetListType));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by a net list
+ */
+void FreeNetListListMemory(NetListListTypePtr Netlistlist)
+{
+	if (Netlistlist) {
+		NETLIST_LOOP(Netlistlist);
+		{
+			FreeNetListMemory(netlist);
+		}
+		END_LOOP;
+		free(Netlistlist->NetList);
+		memset(Netlistlist, 0, sizeof(NetListListType));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by a subnet
+ */
+void FreeNetMemory(NetTypePtr Net)
+{
+	if (Net) {
+		free(Net->Connection);
+		memset(Net, 0, sizeof(NetType));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by an attribute list
+ */
+static void FreeAttributeListMemory(AttributeListTypePtr list)
+{
+	int i;
+
+	for (i = 0; i < list->Number; i++) {
+		free(list->List[i].name);
+		free(list->List[i].value);
+	}
+	free(list->List);
+	list->List = NULL;
+	list->Max = 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * frees memory used by an element
+ */
+void FreeElementMemory(ElementType * element)
+{
+	if (element == NULL)
+		return;
+
+	ELEMENTNAME_LOOP(element);
+	{
+		free(textstring);
+	}
+	END_LOOP;
+	PIN_LOOP(element);
+	{
+		free(pin->Name);
+		free(pin->Number);
+	}
+	END_LOOP;
+	PAD_LOOP(element);
+	{
+		free(pad->Name);
+		free(pad->Number);
+	}
+	END_LOOP;
+
+	list_map0(&element->Pin, PinType, RemoveFreePin);
+	list_map0(&element->Pad, PadType, RemoveFreePad);
+	list_map0(&element->Line, LineType, RemoveFreeLine);
+	list_map0(&element->Arc,  ArcType,  RemoveFreeArc);
+
+	FreeAttributeListMemory(&element->Attributes);
+	reset_obj_mem(ElementType, element);
+}
+
+/* ---------------------------------------------------------------------------
+ * free memory used by PCB
+ */
+void FreePCBMemory(PCBType * pcb)
+{
+	int i;
+
+	if (pcb == NULL)
+		return;
+
+	free(pcb->Name);
+	free(pcb->Filename);
+	free(pcb->PrintFilename);
+	rats_patch_destroy(pcb);
+	FreeDataMemory(pcb->Data);
+	free(pcb->Data);
+	/* release font symbols */
+	for (i = 0; i <= MAX_FONTPOSITION; i++)
+		free(pcb->Font.Symbol[i].Line);
+	for (i = 0; i < NUM_NETLISTS; i++)
+		FreeLibraryMemory(&(pcb->NetlistLib[i]));
+	vtroutestyle_uninit(&pcb->RouteStyle);
+	FreeAttributeListMemory(&pcb->Attributes);
+	/* clear struct */
+	memset(pcb, 0, sizeof(PCBType));
+}
+
+/* ---------------------------------------------------------------------------
+ * free memory used by data struct
+ */
+void FreeDataMemory(DataType * data)
+{
+	LayerTypePtr layer;
+	int i;
+
+	if (data == NULL)
+		return;
+
+	VIA_LOOP(data);
+	{
+		free(via->Name);
+	}
+	END_LOOP;
+	list_map0(&data->Via, PinType, RemoveFreeVia);
+	ELEMENT_LOOP(data);
+	{
+		FreeElementMemory(element);
+	}
+	END_LOOP;
+	list_map0(&data->Element, ElementType, RemoveFreeElement);
+	list_map0(&data->Rat, RatType, RemoveFreeRat);
+
+	for (layer = data->Layer, i = 0; i < MAX_LAYER + 2; layer++, i++) {
+		FreeAttributeListMemory(&layer->Attributes);
+		TEXT_LOOP(layer);
+		{
+			free(text->TextString);
+		}
+		END_LOOP;
+		if (layer->Name)
+			free((char*)layer->Name);
+		LINE_LOOP(layer);
+		{
+			if (line->Number)
+				free(line->Number);
+		}
+		END_LOOP;
+
+		list_map0(&layer->Line, LineType, RemoveFreeLine);
+		list_map0(&layer->Arc,  ArcType,  RemoveFreeArc);
+		list_map0(&layer->Text, TextType, RemoveFreeText);
+		POLYGON_LOOP(layer);
+		{
+			FreePolygonMemory(polygon);
+		}
+		END_LOOP;
+		list_map0(&layer->Polygon, PolygonType, RemoveFreePolygon);
+		if (layer->line_tree)
+			r_destroy_tree(&layer->line_tree);
+		if (layer->arc_tree)
+			r_destroy_tree(&layer->arc_tree);
+		if (layer->text_tree)
+			r_destroy_tree(&layer->text_tree);
+		if (layer->polygon_tree)
+			r_destroy_tree(&layer->polygon_tree);
+	}
+
+	if (data->element_tree)
+		r_destroy_tree(&data->element_tree);
+	for (i = 0; i < MAX_ELEMENTNAMES; i++)
+		if (data->name_tree[i])
+			r_destroy_tree(&data->name_tree[i]);
+	if (data->via_tree)
+		r_destroy_tree(&data->via_tree);
+	if (data->pin_tree)
+		r_destroy_tree(&data->pin_tree);
+	if (data->pad_tree)
+		r_destroy_tree(&data->pad_tree);
+	if (data->rat_tree)
+		r_destroy_tree(&data->rat_tree);
+	/* clear struct */
+	memset(data, 0, sizeof(DataType));
+}
+
+/* ---------------------------------------------------------------------------
+ * releases the memory that's allocated by the library
+ */
+void FreeLibraryMemory(LibraryTypePtr lib)
+{
+	MENU_LOOP(lib);
+	{
+		ENTRY_LOOP(menu);
+		{
+			if (!entry->ListEntry_dontfree)
+				free((char*)entry->ListEntry);
+		}
+		END_LOOP;
+		free(menu->Entry);
+		free(menu->Name);
+		free(menu->directory);
+	}
+	END_LOOP;
+	free(lib->Menu);
+
+	/* clear struct */
+	memset(lib, 0, sizeof(LibraryType));
+}
+
+/* ---------------------------------------------------------------------------
+ * strips leading and trailing blanks from the passed string and
+ * returns a pointer to the new 'duped' one or NULL if the old one
+ * holds only white space characters
+ */
+char *StripWhiteSpaceAndDup(const char *S)
+{
+	const char *p1, *p2;
+	char *copy;
+	size_t length;
+
+	if (!S || !*S)
+		return (NULL);
+
+	/* strip leading blanks */
+	for (p1 = S; *p1 && isspace((int) *p1); p1++);
+
+	/* strip trailing blanks and get string length */
+	length = strlen(p1);
+	for (p2 = p1 + length - 1; length && isspace((int) *p2); p2--, length--);
+
+	/* string is not empty -> allocate memory */
+	if (length) {
+		copy = (char *) realloc(NULL, length + 1);
+		strncpy(copy, p1, length);
+		copy[length] = '\0';
+		return (copy);
+	}
+	else
+		return (NULL);
+}
+
+LineType *GetElementLineMemory(ElementType *Element)
+{
+	LineType *line = calloc(sizeof(LineType), 1);
+	linelist_append(&Element->Line, line);
+
+	return line;
+}
+
+ArcType *GetElementArcMemory(ElementType *Element)
+{
+	ArcType *arc = calloc(sizeof(ArcType), 1);
+
+	arclist_append(&Element->Arc, arc);
+	return arc;
+}
diff --git a/src/mymem.h b/src/mymem.h
new file mode 100644
index 0000000..49a9ff3
--- /dev/null
+++ b/src/mymem.h
@@ -0,0 +1,111 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for memory routines
+ */
+
+#ifndef	PCB_MYMEM_H
+#define	PCB_MYMEM_H
+
+#include "config.h"
+
+#include <stdlib.h>
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * number of additional objects that are allocated with one system call
+ */
+#define	STEP_ELEMENT		50
+#define STEP_DRILL		30
+#define STEP_POINT		100
+#define	STEP_SYMBOLLINE		10
+#define	STEP_SELECTORENTRY	128
+#define	STEP_REMOVELIST		500
+#define	STEP_UNDOLIST		500
+#define	STEP_POLYGONPOINT	10
+#define	STEP_POLYGONHOLEINDEX	10
+#define	STEP_LIBRARYMENU	10
+#define	STEP_LIBRARYENTRY	20
+#define	STEP_RUBBERBAND		100
+
+/* ---------------------------------------------------------------------------
+ * some memory types
+ */
+
+RubberbandTypePtr GetRubberbandMemory(void);
+PinTypePtr GetPinMemory(ElementTypePtr);
+PadTypePtr GetPadMemory(ElementTypePtr);
+PinTypePtr GetViaMemory(DataTypePtr);
+LineTypePtr GetLineMemory(LayerTypePtr);
+ArcTypePtr GetArcMemory(LayerTypePtr);
+RatTypePtr GetRatMemory(DataTypePtr);
+TextTypePtr GetTextMemory(LayerTypePtr);
+PolygonTypePtr GetPolygonMemory(LayerTypePtr);
+PointTypePtr GetPointMemoryInPolygon(PolygonTypePtr);
+pcb_cardinal_t *GetHoleIndexMemoryInPolygon(PolygonTypePtr);
+ElementTypePtr GetElementMemory(DataTypePtr);
+BoxTypePtr GetBoxMemory(BoxListTypePtr);
+ConnectionTypePtr GetConnectionMemory(NetTypePtr);
+NetTypePtr GetNetMemory(NetListTypePtr);
+NetListTypePtr GetNetListMemory(NetListListTypePtr);
+LibraryMenuTypePtr GetLibraryMenuMemory(LibraryTypePtr, int *idx);
+LibraryEntryTypePtr GetLibraryEntryMemory(LibraryMenuTypePtr);
+ElementTypeHandle GetDrillElementMemory(DrillTypePtr);
+PinTypeHandle GetDrillPinMemory(DrillTypePtr);
+DrillTypePtr GetDrillInfoDrillMemory(DrillInfoTypePtr);
+void **GetPointerMemory(PointerListTypePtr);
+void FreePolygonMemory(PolygonTypePtr);
+void FreeElementMemory(ElementTypePtr);
+void FreePCBMemory(PCBTypePtr);
+void FreeBoxListMemory(BoxListTypePtr);
+void FreeNetListListMemory(NetListListTypePtr);
+void FreeNetListMemory(NetListTypePtr);
+void FreeNetMemory(NetTypePtr);
+void FreeDataMemory(DataTypePtr);
+void FreeLibraryMemory(LibraryTypePtr);
+void FreePointerListMemory(PointerListTypePtr);
+void DeleteLibraryMenuMemory(LibraryTypePtr lib, int menuidx);
+char *StripWhiteSpaceAndDup(const char *);
+
+void RemoveFreeArc(ArcType * data);
+void RemoveFreeLine(LineType * data);
+void RemoveFreeText(TextType * data);
+void RemoveFreePolygon(PolygonType * data);
+void RemoveFreePin(PinType * data);
+void RemoveFreePad(PadType * data);
+void RemoveFreeVia(PinType * data);
+void RemoveFreeElement(ElementType * data);
+void RemoveFreeRat(RatType * data);
+
+/* Allocate element-objects */
+LineType *GetElementLineMemory(ElementType *Element);
+ArcType *GetElementArcMemory(ElementType *Element);
+
+#ifndef HAVE_LIBDMALLOC
+#define malloc(x) calloc(1,(x))
+#endif
+
+#endif
diff --git a/src/netlist.c b/src/netlist.c
new file mode 100644
index 0000000..0d4a3c1
--- /dev/null
+++ b/src/netlist.c
@@ -0,0 +1,257 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* generic netlist operations
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <genregex/regex_sei.h>
+
+#include "action_helper.h"
+#include "data.h"
+#include "error.h"
+#include "plug_io.h"
+#include "find.h"
+#include "mymem.h"
+#include "rats.h"
+#include "create.h"
+#include "rats_patch.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+#include "netlist.h"
+
+/*
+  int    PCB->NetlistLib[n].MenuN
+  char * PCB->NetlistLib[n].Menu[i].Name
+     [0] == '*' (ok for rats) or ' ' (skip for rats)
+     [1] == unused
+     [2..] actual name
+  char * PCB->NetlistLib[n].Menu[i].Style
+  int    PCB->NetlistLib[n].Menu[i].EntryN
+  char * PCB->NetlistLib[n].Menu[i].Entry[j].ListEntry
+*/
+
+void pcb_netlist_changed(int force_unfreeze)
+{
+	if (force_unfreeze)
+		PCB->netlist_frozen = 0;
+	if (PCB->netlist_frozen)
+		PCB->netlist_needs_update = 1;
+	else {
+		PCB->netlist_needs_update = 0;
+		hid_action("NetlistChanged");
+	}
+}
+
+LibraryMenuTypePtr pcb_netnode_to_netname(const char *nodename)
+{
+	int i, j;
+	/*printf("nodename [%s]\n", nodename); */
+	for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
+		for (j = 0; j < PCB->NetlistLib[NETLIST_EDITED].Menu[i].EntryN; j++) {
+			if (strcmp(PCB->NetlistLib[NETLIST_EDITED].Menu[i].Entry[j].ListEntry, nodename) == 0) {
+				/*printf(" in [%s]\n", PCB->NetlistLib.Menu[i].Name); */
+				return &(PCB->NetlistLib[NETLIST_EDITED].Menu[i]);
+			}
+		}
+	}
+	return 0;
+}
+
+LibraryMenuTypePtr pcb_netname_to_netname(const char *netname)
+{
+	int i;
+
+	if ((netname[0] == '*' || netname[0] == ' ') && netname[1] == ' ') {
+		/* Looks like we were passed an internal netname, skip the prefix */
+		netname += 2;
+	}
+	for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
+		if (strcmp(PCB->NetlistLib[NETLIST_EDITED].Menu[i].Name + 2, netname) == 0) {
+			return &(PCB->NetlistLib[NETLIST_EDITED].Menu[i]);
+		}
+	}
+	return 0;
+}
+
+int pcb_pin_name_to_xy(LibraryEntryType * pin, Coord *x, Coord *y)
+{
+	ConnectionType conn;
+	if (!SeekPad(pin, &conn, pcb_false))
+		return 1;
+	switch (conn.type) {
+	case PCB_TYPE_PIN:
+		*x = ((PinType *) (conn.ptr2))->X;
+		*y = ((PinType *) (conn.ptr2))->Y;
+		return 0;
+	case PCB_TYPE_PAD:
+		*x = ((PadType *) (conn.ptr2))->Point1.X;
+		*y = ((PadType *) (conn.ptr2))->Point1.Y;
+		return 0;
+	}
+	return 1;
+}
+
+void pcb_netlist_find(LibraryMenuType * net, LibraryEntryType * pin)
+{
+	Coord x, y;
+	if (pcb_pin_name_to_xy(net->Entry, &x, &y))
+		return;
+	LookupConnection(x, y, 1, 1, PCB_FLAG_FOUND);
+}
+
+void pcb_netlist_select(LibraryMenuType * net, LibraryEntryType * pin)
+{
+	Coord x, y;
+	if (pcb_pin_name_to_xy(net->Entry, &x, &y))
+		return;
+	LookupConnection(x, y, 1, 1, PCB_FLAG_SELECTED);
+}
+
+void pcb_netlist_rats(LibraryMenuType * net, LibraryEntryType * pin)
+{
+	net->Name[0] = ' ';
+	net->flag = 1;
+	pcb_netlist_changed(0);
+}
+
+void pcb_netlist_norats(LibraryMenuType * net, LibraryEntryType * pin)
+{
+	net->Name[0] = '*';
+	net->flag = 0;
+	pcb_netlist_changed(0);
+}
+
+/* The primary purpose of this action is to remove the netlist
+   completely so that a new one can be loaded, usually via a gsch2pcb
+   style script.  */
+void pcb_netlist_clear(LibraryMenuType * net, LibraryEntryType * pin)
+{
+	LibraryType *netlist = (LibraryType *) & PCB->NetlistLib;
+	int ni, pi;
+
+	if (net == 0) {
+		/* Clear the entire netlist. */
+		for (ni = 0; ni < NUM_NETLISTS; ni++)
+			FreeLibraryMemory(&(PCB->NetlistLib[ni]));
+	}
+	else if (pin == 0) {
+		/* Remove a net from the netlist. */
+		ni = net - netlist->Menu;
+		if (ni >= 0 && ni < netlist->MenuN) {
+			/* if there is exactly one item, MenuN is 1 and ni is 0 */
+			if (netlist->MenuN - ni > 1)
+				memmove(net, net + 1, (netlist->MenuN - ni - 1) * sizeof(*net));
+			netlist->MenuN--;
+		}
+	}
+	else {
+		/* Remove a pin from the given net.  Note that this may leave an
+		   empty net, which is different than removing the net
+		   (above).  */
+		pi = pin - net->Entry;
+		if (pi >= 0 && pi < net->EntryN) {
+			/* if there is exactly one item, MenuN is 1 and ni is 0 */
+			if (net->EntryN - pi > 1)
+				memmove(pin, pin + 1, (net->EntryN - pi - 1) * sizeof(*pin));
+			net->EntryN--;
+		}
+	}
+	pcb_netlist_changed(0);
+}
+
+void pcb_netlist_style(LibraryMenuType * net, const char *style)
+{
+	free(net->Style);
+	net->Style = pcb_strdup_null((char *) style);
+}
+
+LibraryMenuTypePtr pcb_netlist_find_net4pinname(PCBTypePtr pcb, const char *pin)
+{
+	int n;
+
+	for (n = 0; n < pcb->NetlistLib[NETLIST_EDITED].MenuN; n++) {
+		LibraryMenuTypePtr menu = &pcb->NetlistLib[NETLIST_EDITED].Menu[n];
+		int p;
+		for (p = 0; p < menu->EntryN; p++) {
+			LibraryEntryTypePtr entry = &menu->Entry[p];
+			if (strcmp(entry->ListEntry, pin) == 0)
+				return menu;
+		}
+	}
+	return NULL;
+}
+
+static LibraryMenuTypePtr pcb_netlist_find_net4pin_any(PCBTypePtr pcb, const char *ename, const char *pname)
+{
+	char pinname[256];
+	int len;
+
+	if ((ename == NULL) || (pname == NULL))
+		return NULL;
+
+	len = pcb_snprintf(pinname, sizeof(pinname), "%s-%s", ename, pname);
+	if (len >= sizeof(pinname))
+		return NULL;
+
+	return pcb_netlist_find_net4pinname(pcb, pinname);
+}
+
+LibraryMenuTypePtr pcb_netlist_find_net4pin(PCBTypePtr pcb, const PinType *pin)
+{
+	const ElementType *e = pin->Element;
+
+	if (e == NULL)
+		return NULL;
+
+	return pcb_netlist_find_net4pin_any(pcb, e->Name[NAMEONPCB_INDEX].TextString, pin->Number);
+}
+
+
+LibraryMenuTypePtr pcb_netlist_find_net4pad(PCBTypePtr pcb, const PadType *pad)
+{
+	const ElementType *e = pad->Element;
+
+	if (e == NULL)
+		return NULL;
+
+	return pcb_netlist_find_net4pin_any(pcb, e->Name[NAMEONPCB_INDEX].TextString, pad->Number);
+}
+
+pcb_cardinal_t pcb_netlist_net_idx(PCBTypePtr pcb, LibraryMenuType *net)
+{
+	LibraryMenuType *first = &pcb->NetlistLib[NETLIST_EDITED].Menu[0];
+	LibraryMenuType *last  = &pcb->NetlistLib[NETLIST_EDITED].Menu[pcb->NetlistLib[NETLIST_EDITED].MenuN-1];
+	
+	if ((net < first) || (net > last))
+		return PCB_NETLIST_INVALID_INDEX;
+
+	return net - first;
+}
diff --git a/src/netlist.h b/src/netlist.h
new file mode 100644
index 0000000..c473435
--- /dev/null
+++ b/src/netlist.h
@@ -0,0 +1,63 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* generic netlist operations */
+#include "global.h"
+
+void pcb_netlist_changed(int force_unfreeze);
+LibraryMenuTypePtr pcb_netnode_to_netname(const char *nodename);
+LibraryMenuTypePtr pcb_netname_to_netname(const char *netname);
+
+int pcb_pin_name_to_xy(LibraryEntryType *pin, Coord *x, Coord *y);
+void pcb_netlist_find(LibraryMenuType *net, LibraryEntryType *pin);
+void pcb_netlist_select(LibraryMenuType *net, LibraryEntryType *pin);
+void pcb_netlist_rats(LibraryMenuType *net, LibraryEntryType *pin);
+void pcb_netlist_norats(LibraryMenuType *net, LibraryEntryType *pin);
+void pcb_netlist_clear(LibraryMenuType *net, LibraryEntryType *pin);
+void pcb_netlist_style(LibraryMenuType *net, const char *style);
+
+/* Return the net entry for a pin name (slow search). The pin name is
+   like "U101-5", so element's refdes, dash, pin number */
+LibraryMenuTypePtr pcb_netlist_find_net4pinname(PCBTypePtr pcb, const char *pinname);
+
+/* Same as pcb_netlist_find_net4pinname but with pin pointer */
+LibraryMenuTypePtr pcb_netlist_find_net4pin(PCBTypePtr pcb, const PinType *pin);
+LibraryMenuTypePtr pcb_netlist_find_net4pad(PCBTypePtr pcb, const PadType *pad);
+
+
+/* Evaluate to const char * name of the network; lmt is (LibraryMenuType *) */
+#define pcb_netlist_name(lmt) ((const char *)((lmt)->Name+2))
+
+/* Evaluate to 0 or 1 depending on whether the net is marked with *; lmt is (LibraryMenuType *) */
+#define pcb_netlist_is_bad(lmt) (((lmt)->Name[0]) == '*')
+
+
+/* Return the index of the net or PCB_NETLIST_INVALID_INDEX if the net is not
+   on the netlist. NOTE: indices returned are valid only until the first
+   netlist change! */
+pcb_cardinal_t pcb_netlist_net_idx(PCBTypePtr pcb, LibraryMenuType *net);
+
+#define PCB_NETLIST_INVALID_INDEX ((pcb_cardinal_t)(-1))
diff --git a/src/netlist_act.c b/src/netlist_act.c
new file mode 100644
index 0000000..5880e4c
--- /dev/null
+++ b/src/netlist_act.c
@@ -0,0 +1,348 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* generic netlist operations
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <genregex/regex_sei.h>
+
+#include "action_helper.h"
+#include "data.h"
+#include "error.h"
+#include "plug_io.h"
+#include "find.h"
+#include "mymem.h"
+#include "rats.h"
+#include "create.h"
+#include "rats_patch.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+#include "netlist.h"
+
+static int pcb_netlist_swap()
+{
+	char *pins[3] = { NULL, NULL, NULL };
+	int next = 0, n;
+	int ret = -1;
+	LibraryMenuTypePtr nets[2];
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_SELECTED, pin)) {
+				int le, lp;
+
+				if (next > 2) {
+					Message(PCB_MSG_DEFAULT, "Exactly two pins should be selected for swap (more than 2 selected at the moment)\n");
+					goto quit;
+				}
+
+				le = strlen(element->Name[1].TextString);
+				lp = strlen(pin->Number);
+				pins[next] = malloc(le + lp + 2);
+				sprintf(pins[next], "%s-%s", element->Name[1].TextString, pin->Number);
+				next++;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	if (next < 2) {
+		Message(PCB_MSG_DEFAULT, "Exactly two pins should be selected for swap (less than 2 selected at the moment)\n");
+		goto quit;
+	}
+
+
+	nets[0] = pcb_netlist_find_net4pinname(PCB, pins[0]);
+	nets[1] = pcb_netlist_find_net4pinname(PCB, pins[1]);
+	if ((nets[0] == NULL) || (nets[1] == NULL)) {
+		Message(PCB_MSG_DEFAULT, "That pin is not on a net.\n");
+		goto quit;
+	}
+	if (nets[0] == nets[1]) {
+		Message(PCB_MSG_DEFAULT, "Those two pins are on the same net, can't swap them.\n");
+		goto quit;
+	}
+
+
+	rats_patch_append_optimize(PCB, RATP_DEL_CONN, pins[0], nets[0]->Name + 2, NULL);
+	rats_patch_append_optimize(PCB, RATP_DEL_CONN, pins[1], nets[1]->Name + 2, NULL);
+	rats_patch_append_optimize(PCB, RATP_ADD_CONN, pins[0], nets[1]->Name + 2, NULL);
+	rats_patch_append_optimize(PCB, RATP_ADD_CONN, pins[1], nets[0]->Name + 2, NULL);
+
+	/* TODO: not very efficient to regenerate the whole list... */
+	rats_patch_make_edited(PCB);
+	ret = 0;
+
+quit:;
+	for (n = 0; n < 3; n++)
+		if (pins[n] != NULL)
+			free(pins[n]);
+	return ret;
+}
+
+
+/* The primary purpose of this action is to rebuild a netlist from a
+   script, in conjunction with the clear action above.  */
+static int pcb_netlist_add(const char *netname, const char *pinname)
+{
+	int ni, pi;
+	LibraryType *netlist = &PCB->NetlistLib[NETLIST_EDITED];
+	LibraryMenuType *net = NULL;
+	LibraryEntryType *pin = NULL;
+
+	for (ni = 0; ni < netlist->MenuN; ni++)
+		if (strcmp(netlist->Menu[ni].Name + 2, netname) == 0) {
+			net = &(netlist->Menu[ni]);
+			break;
+		}
+	if (net == NULL) {
+		net = CreateNewNet(netlist, (char *) netname, NULL);
+	}
+
+	for (pi = 0; pi < net->EntryN; pi++)
+		if (strcmp(net->Entry[pi].ListEntry, pinname) == 0) {
+			pin = &(net->Entry[pi]);
+			break;
+		}
+	if (pin == NULL) {
+		pin = CreateNewConnection(net, (char *) pinname);
+		rats_patch_append_optimize(PCB, RATP_ADD_CONN, pin->ListEntry, net->Name + 2, NULL);
+	}
+
+	pcb_netlist_changed(0);
+	return 0;
+}
+
+static const char netlist_syntax[] =
+	"Net(find|select|rats|norats|clear[,net[,pin]])\n" "Net(freeze|thaw|forcethaw)\n" "Net(swap)\n" "Net(add,net,pin)";
+
+static const char netlist_help[] = "Perform various actions on netlists.";
+
+/* %start-doc actions Netlist
+
+Each of these actions apply to a specified set of nets.  @var{net} and
+ at var{pin} are patterns which match one or more nets or pins; these
+patterns may be full names or regular expressions.  If an exact match
+is found, it is the only match; if no exact match is found,
+ at emph{then} the pattern is tried as a regular expression.
+
+If neither @var{net} nor @var{pin} are specified, all nets apply.  If
+ at var{net} is specified but not @var{pin}, all nets matching @var{net}
+apply.  If both are specified, nets which match @var{net} and contain
+a pin matching @var{pin} apply.
+
+ at table @code
+
+ at item find
+Nets which apply are marked @emph{found} and are drawn in the
+ at code{connected-color} color.
+
+ at item select
+Nets which apply are selected.
+
+ at item rats
+Nets which apply are marked as available for the rats nest.
+
+ at item norats
+Nets which apply are marked as not available for the rats nest.
+
+ at item clear
+Clears the netlist.
+
+ at item add
+Add the given pin to the given netlist, creating either if needed.
+
+ at item swap
+Swap the connections one end of two selected rats and pins.
+
+ at item sort
+Called after a list of add's, this sorts the netlist.
+
+ at item freeze
+ at itemx thaw
+ at itemx forcethaw
+Temporarily prevents changes to the netlist from being reflected in
+the GUI.  For example, if you need to make multiple changes, you
+freeze the netlist, make the changes, then thaw it.  Note that
+freeze/thaw requests may nest, with the netlist being fully thawed
+only when all pending freezes are thawed.  You can bypass the nesting
+by using forcethaw, which resets the freeze count and immediately
+updates the GUI.
+
+ at end table
+
+%end-doc */
+
+typedef void (*NFunc) (LibraryMenuType *, LibraryEntryType *);
+
+static int ActionNetlist(int argc, const char **argv, Coord x, Coord y)
+{
+	NFunc func;
+	int i, j;
+	LibraryMenuType *net;
+	LibraryEntryType *pin;
+	int net_found = 0;
+	int pin_found = 0;
+	int use_re = 0;
+	re_sei_t *regex;
+
+	if (!PCB)
+		return 1;
+	if (argc == 0) {
+		Message(PCB_MSG_DEFAULT, netlist_syntax);
+		return 1;
+	}
+	if (strcasecmp(argv[0], "find") == 0)
+		func = pcb_netlist_find;
+	else if (strcasecmp(argv[0], "select") == 0)
+		func = pcb_netlist_select;
+	else if (strcasecmp(argv[0], "rats") == 0)
+		func = pcb_netlist_rats;
+	else if (strcasecmp(argv[0], "norats") == 0)
+		func = pcb_netlist_norats;
+	else if (strcasecmp(argv[0], "clear") == 0) {
+		func = pcb_netlist_clear;
+		if (argc == 1) {
+			pcb_netlist_clear(NULL, NULL);
+			return 0;
+		}
+	}
+	else if (strcasecmp(argv[0], "style") == 0)
+		func = (NFunc) pcb_netlist_style;
+	else if (strcasecmp(argv[0], "swap") == 0)
+		return pcb_netlist_swap();
+	else if (strcasecmp(argv[0], "add") == 0) {
+		/* Add is different, because the net/pin won't already exist.  */
+		return pcb_netlist_add(ACTION_ARG(1), ACTION_ARG(2));
+	}
+	else if (strcasecmp(argv[0], "sort") == 0) {
+		pcb_sort_netlist();
+		return 0;
+	}
+	else if (strcasecmp(argv[0], "freeze") == 0) {
+		PCB->netlist_frozen++;
+		return 0;
+	}
+	else if (strcasecmp(argv[0], "thaw") == 0) {
+		if (PCB->netlist_frozen > 0) {
+			PCB->netlist_frozen--;
+			if (PCB->netlist_needs_update)
+				pcb_netlist_changed(0);
+		}
+		return 0;
+	}
+	else if (strcasecmp(argv[0], "forcethaw") == 0) {
+		PCB->netlist_frozen = 0;
+		if (PCB->netlist_needs_update)
+			pcb_netlist_changed(0);
+		return 0;
+	}
+	else {
+		Message(PCB_MSG_DEFAULT, netlist_syntax);
+		return 1;
+	}
+
+	if (argc > 1) {
+		use_re = 1;
+		for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
+			net = PCB->NetlistLib[NETLIST_EDITED].Menu + i;
+			if (strcasecmp(argv[1], net->Name + 2) == 0)
+				use_re = 0;
+		}
+		if (use_re) {
+			regex = re_sei_comp(argv[1]);
+			if (re_sei_errno(regex) != 0) {
+				Message(PCB_MSG_DEFAULT, _("regexp error: %s\n"), re_error_str(re_sei_errno(regex)));
+				re_sei_free(regex);
+				return (1);
+			}
+		}
+	}
+
+	for (i = PCB->NetlistLib[NETLIST_EDITED].MenuN - 1; i >= 0; i--) {
+		net = PCB->NetlistLib[NETLIST_EDITED].Menu + i;
+
+		if (argc > 1) {
+			if (use_re) {
+				if (re_sei_exec(regex, net->Name + 2) == 0)
+					continue;
+			}
+			else {
+				if (strcasecmp(net->Name + 2, argv[1]))
+					continue;
+			}
+		}
+		net_found = 1;
+
+		pin = 0;
+		if (func == (NFunc) pcb_netlist_style) {
+			pcb_netlist_style(net, ACTION_ARG(2));
+		}
+		else if (argc > 2) {
+			int l = strlen(argv[2]);
+			for (j = net->EntryN - 1; j >= 0; j--)
+				if (strcasecmp(net->Entry[j].ListEntry, argv[2]) == 0
+						|| (strncasecmp(net->Entry[j].ListEntry, argv[2], l) == 0 && net->Entry[j].ListEntry[l] == '-')) {
+					pin = net->Entry + j;
+					pin_found = 1;
+					func(net, pin);
+				}
+		}
+		else
+			func(net, 0);
+	}
+
+	if (argc > 2 && !pin_found) {
+		gui->log("Net %s has no pin %s\n", argv[1], argv[2]);
+		return 1;
+	}
+	else if (!net_found) {
+		gui->log("No net named %s\n", argv[1]);
+	}
+
+	if (use_re)
+		re_sei_free(regex);
+
+	return 0;
+}
+
+HID_Action netlist_action_list[] = {
+	{"net", 0, ActionNetlist,
+	 netlist_help, netlist_syntax}
+	,
+	{"netlist", 0, ActionNetlist,
+	 netlist_help, netlist_syntax}
+};
+
+REGISTER_ACTIONS(netlist_action_list, NULL)
diff --git a/src/obj_any.c b/src/obj_any.c
new file mode 100644
index 0000000..6db6dcf
--- /dev/null
+++ b/src/obj_any.c
@@ -0,0 +1,27 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* object model - type+union that can represent any object */
+
+#define TDL_DONT_UNDEF
+#include "obj_any.h"
+#include <genlist/gentdlist_impl.c>
diff --git a/src/obj_any.h b/src/obj_any.h
new file mode 100644
index 0000000..761fabd
--- /dev/null
+++ b/src/obj_any.h
@@ -0,0 +1,120 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* object model - type+union that can represent any object */
+
+#ifndef PCB_OBJ_H
+#define PCB_OBJ_H
+
+#include "global_objs.h"
+#include "global_typedefs.h"
+
+/* Can be used as a bitfield */
+typedef enum pcb_objtype_e {
+	PCB_OBJ_VOID      = 0x000000,
+
+	PCB_OBJ_POINT     = 0x000001,
+	PCB_OBJ_LINE      = 0x000002,
+	PCB_OBJ_TEXT      = 0x000004,
+	PCB_OBJ_POLYGON   = 0x000008,
+	PCB_OBJ_ARC       = 0x000010,
+	PCB_OBJ_RAT       = 0x000020,
+	PCB_OBJ_PAD       = 0x000040,
+	PCB_OBJ_PIN       = 0x000080,
+	PCB_OBJ_VIA       = 0x000100,
+	PCB_OBJ_ELEMENT   = 0x000200,
+
+	/* more abstract objects */
+	PCB_OBJ_NET       = 0x100001,
+	PCB_OBJ_LAYER     = 0x100002,
+
+	/* temporary, for backward compatibility */
+	PCB_OBJ_ELINE     = 0x200001,
+	PCB_OBJ_EARC      = 0x200002,
+	PCB_OBJ_ETEXT     = 0x200004,
+
+	/* combinations, groups, masks */
+	PCB_OBJ_CLASS_MASK= 0xF00000,
+	PCB_OBJ_CLASS_OBJ = 0x000000, /* anything with common object fields (AnyObjectType) */
+	PCB_OBJ_ANY       = 0xFFFFFF
+} pcb_objtype_t;
+
+/* which elem of the parent union is active */
+typedef enum pcb_parenttype_e {
+	PCB_PARENT_INVALID = 0,  /* invalid or unknown */
+	PCB_PARENT_LAYER,        /* object is on a layer */
+	PCB_PARENT_ELEMENT,      /* object is part of an element */
+	PCB_PARENT_DATA          /* global objects like via */
+} pcb_parenttype_t;
+
+
+/* class is e.g. PCB_OBJ_CLASS_OBJ */
+#define PCB_OBJ_IS_CLASS(type, class)  (((type) & PCB_OBJ_CLASS_MASK) == (class))
+
+
+typedef struct pcb_obj_s pcb_obj_t;
+
+struct pcb_obj_s {
+	pcb_objtype_t type;
+	union {
+		void         *any;
+		AnyObjectType *anyobj;
+		PointType    *point;
+		LineType     *line;
+		TextType     *text;
+		PolygonType  *polygon;
+		ArcType      *arc;
+		RatType      *rat;
+		PadType      *pad;
+		PinType      *pin;
+		PinType      *via;
+		ElementType  *element;
+		NetType      *net;
+		LayerType    *layer;
+	} data;
+
+	pcb_parenttype_t parent_type;
+	union {
+		void         *any;
+		LayerType    *layer;
+		DataType     *data;
+		ElementType  *element;
+	} parent;
+	gdl_elem_t link;
+};
+
+
+/* List of objects */
+#define TDL(x)      pcb_objlist_ ## x
+#define TDL_LIST_T  pcb_objlist_t
+#define TDL_ITEM_T  pcb_obj_t
+#define TDL_FIELD   link
+#define TDL_SIZE_T  size_t
+#define TDL_FUNC
+
+#define pcb_objlist_foreach(list, iterator, loop_elem) \
+	gdl_foreach_((&((list)->lst)), (iterator), (loop_elem))
+
+#include <genlist/gentdlist_impl.h>
+#include <genlist/gentdlist_undef.h>
+
+#endif
diff --git a/src/object_act.c b/src/object_act.c
new file mode 100644
index 0000000..96cd237
--- /dev/null
+++ b/src/object_act.c
@@ -0,0 +1,1024 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "funchash_core.h"
+
+#include "search.h"
+#include "move.h"
+#include "draw.h"
+#include "mirror.h"
+#include "rotate.h"
+#include "set.h"
+#include "copy.h"
+#include "misc.h"
+#include "rubberband.h"
+#include "buffer.h"
+#include "remove.h"
+#include "create.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+/* --------------------------------------------------------------------------- */
+
+static ElementType *element_cache = NULL;
+
+static ElementType *find_element_by_refdes(const char *refdes)
+{
+	if (element_cache && NAMEONPCB_NAME(element_cache)
+			&& strcmp(NAMEONPCB_NAME(element_cache), refdes) == 0)
+		return element_cache;
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (NAMEONPCB_NAME(element)
+				&& strcmp(NAMEONPCB_NAME(element), refdes) == 0) {
+			element_cache = element;
+			return element_cache;
+		}
+	}
+	END_LOOP;
+	return NULL;
+}
+
+static AttributeType *lookup_attr(AttributeListTypePtr list, const char *name)
+{
+	int i;
+	for (i = 0; i < list->Number; i++)
+		if (strcmp(list->List[i].name, name) == 0)
+			return &list->List[i];
+	return NULL;
+}
+
+static void delete_attr(AttributeListTypePtr list, AttributeType * attr)
+{
+	int idx = attr - list->List;
+	if (idx < 0 || idx >= list->Number)
+		return;
+	if (list->Number - idx > 1)
+		memmove(attr, attr + 1, (list->Number - idx - 1) * sizeof(AttributeType));
+	list->Number--;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char attributes_syntax[] = "Attributes(Layout|Layer|Element)\n" "Attributes(Layer,layername)";
+
+static const char attributes_help[] =
+	"Let the user edit the attributes of the layout, current or given\n" "layer, or selected element.";
+
+/* %start-doc actions Attributes
+
+This just pops up a dialog letting the user edit the attributes of the
+pcb, an element, or a layer.
+
+%end-doc */
+
+
+static int ActionAttributes(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *layername = ACTION_ARG(1);
+	char *buf;
+
+	if (!function)
+		AFAIL(attributes);
+
+	if (!gui->edit_attributes) {
+		Message(PCB_MSG_DEFAULT, _("This GUI doesn't support Attribute Editing\n"));
+		return 1;
+	}
+
+	switch (funchash_get(function, NULL)) {
+	case F_Layout:
+		{
+			gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
+			return 0;
+		}
+
+	case F_Layer:
+		{
+			LayerType *layer = CURRENT;
+			if (layername) {
+				int i;
+				layer = NULL;
+				for (i = 0; i < max_copper_layer; i++)
+					if (strcmp(PCB->Data->Layer[i].Name, layername) == 0) {
+						layer = &(PCB->Data->Layer[i]);
+						break;
+					}
+				if (layer == NULL) {
+					Message(PCB_MSG_DEFAULT, _("No layer named %s\n"), layername);
+					return 1;
+				}
+			}
+			buf = (char *) malloc(strlen(layer->Name) + strlen("Layer X Attributes"));
+			sprintf(buf, "Layer %s Attributes", layer->Name);
+			gui->edit_attributes(buf, &(layer->Attributes));
+			free(buf);
+			return 0;
+		}
+
+	case F_Element:
+		{
+			int n_found = 0;
+			ElementType *e = NULL;
+			ELEMENT_LOOP(PCB->Data);
+			{
+				if (TEST_FLAG(PCB_FLAG_SELECTED, element)) {
+					e = element;
+					n_found++;
+				}
+			}
+			END_LOOP;
+			if (n_found > 1) {
+				Message(PCB_MSG_DEFAULT, _("Too many elements selected\n"));
+				return 1;
+			}
+			if (n_found == 0) {
+				void *ptrtmp;
+				gui->get_coords(_("Click on an element"), &x, &y);
+				if ((SearchScreen(x, y, PCB_TYPE_ELEMENT, &ptrtmp, &ptrtmp, &ptrtmp)) != PCB_TYPE_NONE)
+					e = (ElementTypePtr) ptrtmp;
+				else {
+					Message(PCB_MSG_DEFAULT, _("No element found there\n"));
+					return 1;
+				}
+			}
+
+			if (NAMEONPCB_NAME(e)) {
+				buf = (char *) malloc(strlen(NAMEONPCB_NAME(e)) + strlen("Element X Attributes"));
+				sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
+			}
+			else {
+				buf = pcb_strdup("Unnamed Element Attributes");
+			}
+			gui->edit_attributes(buf, &(e->Attributes));
+			free(buf);
+			break;
+		}
+
+	default:
+		AFAIL(attributes);
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char disperseelements_syntax[] = "DisperseElements(All|Selected)";
+
+static const char disperseelements_help[] = "Disperses elements.";
+
+/* %start-doc actions DisperseElements
+
+Normally this is used when starting a board, by selecting all elements
+and then dispersing them.  This scatters the elements around the board
+so that you can pick individual ones, rather than have all the
+elements at the same 0,0 coordinate and thus impossible to choose
+from.
+
+%end-doc */
+
+#define GAP PCB_MIL_TO_COORD(100)
+
+static int ActionDisperseElements(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	Coord minx = GAP, miny = GAP, maxy = GAP, dx, dy;
+	int all = 0, bad = 0;
+
+	if (!function || !*function) {
+		bad = 1;
+	}
+	else {
+		switch (funchash_get(function, NULL)) {
+		case F_All:
+			all = 1;
+			break;
+
+		case F_Selected:
+			all = 0;
+			break;
+
+		default:
+			bad = 1;
+		}
+	}
+
+	if (bad) {
+		AFAIL(disperseelements);
+	}
+
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/*
+		 * If we want to disperse selected elements, maybe we need smarter
+		 * code here to avoid putting components on top of others which
+		 * are not selected.  For now, I'm assuming that this is typically
+		 * going to be used either with a brand new design or a scratch
+		 * design holding some new components
+		 */
+		if (!TEST_FLAG(PCB_FLAG_LOCK, element) && (all || TEST_FLAG(PCB_FLAG_SELECTED, element))) {
+
+			/* figure out how much to move the element */
+			dx = minx - element->BoundingBox.X1;
+
+			/* snap to the grid */
+			dx -= (element->MarkX + dx) % PCB->Grid;
+
+			/*
+			 * and add one grid size so we make sure we always space by GAP or
+			 * more
+			 */
+			dx += PCB->Grid;
+
+			/* Figure out if this row has room.  If not, start a new row */
+			if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth) {
+				miny = maxy + GAP;
+				minx = GAP;
+			}
+
+			/* figure out how much to move the element */
+			dx = minx - element->BoundingBox.X1;
+			dy = miny - element->BoundingBox.Y1;
+
+			/* snap to the grid */
+			dx -= (element->MarkX + dx) % PCB->Grid;
+			dx += PCB->Grid;
+			dy -= (element->MarkY + dy) % PCB->Grid;
+			dy += PCB->Grid;
+
+			/* move the element */
+			MoveElementLowLevel(PCB->Data, element, dx, dy);
+
+			/* and add to the undo list so we can undo this operation */
+			AddObjectToMoveUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element, dx, dy);
+
+			/* keep track of how tall this row is */
+			minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
+			if (maxy < element->BoundingBox.Y2) {
+				maxy = element->BoundingBox.Y2;
+			}
+		}
+
+	}
+	END_LOOP;
+
+	/* done with our action so increment the undo # */
+	IncrementUndoSerialNumber();
+
+	Redraw();
+	SetChangedFlag(pcb_true);
+
+	return 0;
+}
+
+#undef GAP
+
+/* -------------------------------------------------------------------------- */
+
+static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
+
+static const char flip_help[] = "Flip an element to the opposite side of the board.";
+
+/* %start-doc actions Flip
+
+Note that the location of the element will be symmetric about the
+cursor location; i.e. if the part you are pointing at will still be at
+the same spot once the element is on the other side.  When flipping
+multiple elements, this retains their positions relative to each
+other, not their absolute positions on the board.
+
+%end-doc */
+
+static int ActionFlip(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	ElementTypePtr element;
+	void *ptrtmp;
+	int err = 0;
+
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_Object:
+			if ((SearchScreen(x, y, PCB_TYPE_ELEMENT, &ptrtmp, &ptrtmp, &ptrtmp)) != PCB_TYPE_NONE) {
+				element = (ElementTypePtr) ptrtmp;
+				ChangeElementSide(element, 2 * Crosshair.Y - PCB->MaxHeight);
+				IncrementUndoSerialNumber();
+				Draw();
+			}
+			break;
+		case F_Selected:
+		case F_SelectedElements:
+			ChangeSelectedElementSide();
+			break;
+		default:
+			err = 1;
+			break;
+		}
+		if (!err)
+			return 0;
+	}
+
+	AFAIL(flip);
+}
+/* --------------------------------------------------------------------------- */
+
+static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
+
+static const char moveobject_help[] = "Moves the object under the crosshair.";
+
+/* %start-doc actions MoveObject
+
+The @code{X} and @code{Y} are treated like @code{delta} is for many
+other objects.  For each, if it's prefixed by @code{+} or @code{-},
+then that amount is relative.  Otherwise, it's absolute.  Units can be
+ at code{mil} or @code{mm}; if unspecified, units are PCB's internal
+units, currently 1/100 mil.
+
+%end-doc */
+
+static int ActionMoveObject(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *x_str = ACTION_ARG(0);
+	const char *y_str = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	Coord nx, ny;
+	pcb_bool absolute1, absolute2;
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	ny = GetValue(y_str, units, &absolute1, NULL);
+	nx = GetValue(x_str, units, &absolute2, NULL);
+
+	type = SearchScreen(x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
+	if (type == PCB_TYPE_NONE) {
+		Message(PCB_MSG_DEFAULT, _("Nothing found under crosshair\n"));
+		return 1;
+	}
+	if (absolute1)
+		nx -= x;
+	if (absolute2)
+		ny -= y;
+	Crosshair.AttachedObject.RubberbandN = 0;
+	if (conf_core.editor.rubber_band_mode)
+		LookupRubberbandLines(type, ptr1, ptr2, ptr3);
+	if (type == PCB_TYPE_ELEMENT)
+		LookupRatLines(type, ptr1, ptr2, ptr3);
+	MoveObjectAndRubberband(type, ptr1, ptr2, ptr3, nx, ny);
+	SetChangedFlag(pcb_true);
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char movetocurrentlayer_syntax[] = "MoveToCurrentLayer(Object|SelectedObjects)";
+
+static const char movetocurrentlayer_help[] = "Moves objects to the current layer.";
+
+/* %start-doc actions MoveToCurrentLayer
+
+Note that moving an element from a component layer to a solder layer,
+or from solder to component, won't automatically flip it.  Use the
+ at code{Flip()} action to do that.
+
+%end-doc */
+
+static int ActionMoveToCurrentLayer(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, MOVETOLAYER_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE)
+					if (MoveObjectToLayer(type, ptr1, ptr2, ptr3, CURRENT, pcb_false))
+						SetChangedFlag(pcb_true);
+				break;
+			}
+
+		case F_SelectedObjects:
+		case F_Selected:
+			if (MoveSelectedObjectsToLayer(CURRENT))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------- */
+static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
+
+static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
+
+/* %start-doc actions elementlist
+
+ at table @code
+
+ at item Start
+Indicates the start of an element list; call this before any Need
+actions.
+
+ at item Need
+Searches the board for an element with a matching refdes.
+
+If found, the value and footprint are updated.
+
+If not found, a new element is created with the given footprint and value.
+
+ at item Done
+Compares the list of elements needed since the most recent
+ at code{start} with the list of elements actually on the board.  Any
+elements that weren't listed are selected, so that the user may delete
+them.
+
+ at end table
+
+%end-doc */
+
+static int number_of_footprints_not_found;
+
+static int parse_layout_attribute_units(const char *name, int def)
+{
+	const char *as = AttributeGet(PCB, name);
+	if (!as)
+		return def;
+	return GetValue(as, NULL, NULL, NULL);
+}
+
+static int ActionElementList(int argc, const char **argv, Coord x, Coord y)
+{
+	ElementType *e = NULL;
+	const char *refdes, *value, *footprint;
+	const char *args[3];
+	const char *function = argv[0];
+	char *old;
+
+#ifdef DEBUG
+	printf("Entered ActionElementList, executing function %s\n", function);
+#endif
+
+	if (strcasecmp(function, "start") == 0) {
+		ELEMENT_LOOP(PCB->Data);
+		{
+			CLEAR_FLAG(PCB_FLAG_FOUND, element);
+		}
+		END_LOOP;
+		element_cache = NULL;
+		number_of_footprints_not_found = 0;
+		return 0;
+	}
+
+	if (strcasecmp(function, "done") == 0) {
+		ELEMENT_LOOP(PCB->Data);
+		{
+			if (TEST_FLAG(PCB_FLAG_FOUND, element)) {
+				CLEAR_FLAG(PCB_FLAG_FOUND, element);
+			}
+			else if (!EMPTY_STRING_P(NAMEONPCB_NAME(element))) {
+				/* Unnamed elements should remain untouched */
+				SET_FLAG(PCB_FLAG_SELECTED, element);
+			}
+		}
+		END_LOOP;
+		if (number_of_footprints_not_found > 0)
+			gui->confirm_dialog("Not all requested footprints were found.\n" "See the message log for details", "Ok", NULL);
+		return 0;
+	}
+
+	if (strcasecmp(function, "need") != 0)
+		AFAIL(elementlist);
+
+	if (argc != 4)
+		AFAIL(elementlist);
+
+	argc--;
+	argv++;
+
+	refdes = ACTION_ARG(0);
+	footprint = ACTION_ARG(1);
+	value = ACTION_ARG(2);
+
+	args[0] = footprint;
+	args[1] = refdes;
+	args[2] = value;
+
+#ifdef DEBUG
+	printf("  ... footprint = %s\n", footprint);
+	printf("  ... refdes = %s\n", refdes);
+	printf("  ... value = %s\n", value);
+#endif
+
+	e = find_element_by_refdes(refdes);
+
+	if (!e) {
+		Coord nx, ny, d;
+
+#ifdef DEBUG
+		printf("  ... Footprint not on board, need to add it.\n");
+#endif
+		/* Not on board, need to add it. */
+		if (LoadFootprint(argc, args, x, y)) {
+			number_of_footprints_not_found++;
+			return 1;
+		}
+
+		nx = PCB->MaxWidth / 2;
+		ny = PCB->MaxHeight / 2;
+		d = MIN(PCB->MaxWidth, PCB->MaxHeight) / 10;
+
+		nx = parse_layout_attribute_units("import::newX", nx);
+		ny = parse_layout_attribute_units("import::newY", ny);
+		d = parse_layout_attribute_units("import::disperse", d);
+
+		if (d > 0) {
+			nx += pcb_rand() % (d * 2) - d;
+			ny += pcb_rand() % (d * 2) - d;
+		}
+
+		if (nx < 0)
+			nx = 0;
+		if (nx >= PCB->MaxWidth)
+			nx = PCB->MaxWidth - 1;
+		if (ny < 0)
+			ny = 0;
+		if (ny >= PCB->MaxHeight)
+			ny = PCB->MaxHeight - 1;
+
+		/* Place components onto center of board. */
+		if (CopyPastebufferToLayout(nx, ny))
+			SetChangedFlag(pcb_true);
+	}
+
+	else if (e && strcmp(DESCRIPTION_NAME(e), footprint) != 0) {
+#ifdef DEBUG
+		printf("  ... Footprint on board, but different from footprint loaded.\n");
+#endif
+		int er, pr, i;
+		Coord mx, my;
+		ElementType *pe;
+
+		/* Different footprint, we need to swap them out.  */
+		if (LoadFootprint(argc, args, x, y)) {
+			number_of_footprints_not_found++;
+			return 1;
+		}
+
+		er = ElementOrientation(e);
+		pe = elementlist_first(&(PASTEBUFFER->Data->Element));
+		if (!FRONT(e))
+			MirrorElementCoordinates(PASTEBUFFER->Data, pe, pe->MarkY * 2 - PCB->MaxHeight);
+		pr = ElementOrientation(pe);
+
+		mx = e->MarkX;
+		my = e->MarkY;
+
+		if (er != pr)
+			RotateElementLowLevel(PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er - pr + 4) % 4);
+
+		for (i = 0; i < MAX_ELEMENTNAMES; i++) {
+			pe->Name[i].X = e->Name[i].X - mx + pe->MarkX;
+			pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY;
+			pe->Name[i].Direction = e->Name[i].Direction;
+			pe->Name[i].Scale = e->Name[i].Scale;
+		}
+
+		RemoveElement(e);
+
+		if (CopyPastebufferToLayout(mx, my))
+			SetChangedFlag(pcb_true);
+	}
+
+	/* Now reload footprint */
+	element_cache = NULL;
+	e = find_element_by_refdes(refdes);
+
+	old = ChangeElementText(PCB, PCB->Data, e, NAMEONPCB_INDEX, pcb_strdup(refdes));
+	if (old)
+		free(old);
+	old = ChangeElementText(PCB, PCB->Data, e, VALUE_INDEX, pcb_strdup(value));
+	if (old)
+		free(old);
+
+	SET_FLAG(PCB_FLAG_FOUND, e);
+
+#ifdef DEBUG
+	printf(" ... Leaving ActionElementList.\n");
+#endif
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------- */
+static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
+
+static const char elementsetattr_help[] = "Sets or clears an element-specific attribute.";
+
+/* %start-doc actions elementsetattr
+
+If a value is specified, the named attribute is added (if not already
+present) or changed (if it is) to the given value.  If the value is
+not specified, the given attribute is removed if present.
+
+%end-doc */
+
+static int ActionElementSetAttr(int argc, const char **argv, Coord x, Coord y)
+{
+	ElementType *e = NULL;
+	const char *refdes, *name, *value;
+	AttributeType *attr;
+
+	if (argc < 2) {
+		AFAIL(elementsetattr);
+	}
+
+	refdes = argv[0];
+	name = argv[1];
+	value = ACTION_ARG(2);
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (NSTRCMP(refdes, NAMEONPCB_NAME(element)) == 0) {
+			e = element;
+			break;
+		}
+	}
+	END_LOOP;
+
+	if (!e) {
+		Message(PCB_MSG_DEFAULT, _("Cannot change attribute of %s - element not found\n"), refdes);
+		return 1;
+	}
+
+	attr = lookup_attr(&e->Attributes, name);
+
+	if (attr && value) {
+		free(attr->value);
+		attr->value = pcb_strdup(value);
+	}
+	if (attr && !value) {
+		delete_attr(&e->Attributes, attr);
+	}
+	if (!attr && value) {
+		CreateNewAttribute(&e->Attributes, name, value);
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
+
+static const char ripup_help[] = "Ripup auto-routed tracks, or convert an element to parts.";
+
+/* %start-doc actions RipUp
+
+ at table @code
+
+ at item All
+Removes all lines and vias which were created by the autorouter.
+
+ at item Selected
+Removes all selected lines and vias which were created by the
+autorouter.
+
+ at item Element
+Converts the element under the cursor to parts (vias and lines).  Note
+that this uses the highest numbered paste buffer.
+
+ at end table
+
+%end-doc */
+
+static int ActionRipUp(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	pcb_bool changed = pcb_false;
+
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_All:
+			ALLLINE_LOOP(PCB->Data);
+			{
+				if (TEST_FLAG(PCB_FLAG_AUTO, line) && !TEST_FLAG(PCB_FLAG_LOCK, line)) {
+					RemoveObject(PCB_TYPE_LINE, layer, line, line);
+					changed = pcb_true;
+				}
+			}
+			ENDALL_LOOP;
+			ALLARC_LOOP(PCB->Data);
+			{
+				if (TEST_FLAG(PCB_FLAG_AUTO, arc) && !TEST_FLAG(PCB_FLAG_LOCK, arc)) {
+					RemoveObject(PCB_TYPE_ARC, layer, arc, arc);
+					changed = pcb_true;
+				}
+			}
+			ENDALL_LOOP;
+			VIA_LOOP(PCB->Data);
+			{
+				if (TEST_FLAG(PCB_FLAG_AUTO, via) && !TEST_FLAG(PCB_FLAG_LOCK, via)) {
+					RemoveObject(PCB_TYPE_VIA, via, via, via);
+					changed = pcb_true;
+				}
+			}
+			END_LOOP;
+
+			if (changed) {
+				IncrementUndoSerialNumber();
+				SetChangedFlag(pcb_true);
+			}
+			break;
+		case F_Selected:
+			VISIBLELINE_LOOP(PCB->Data);
+			{
+				if (TEST_FLAGS(PCB_FLAG_AUTO | PCB_FLAG_SELECTED, line)
+						&& !TEST_FLAG(PCB_FLAG_LOCK, line)) {
+					RemoveObject(PCB_TYPE_LINE, layer, line, line);
+					changed = pcb_true;
+				}
+			}
+			ENDALL_LOOP;
+			if (PCB->ViaOn)
+				VIA_LOOP(PCB->Data);
+			{
+				if (TEST_FLAGS(PCB_FLAG_AUTO | PCB_FLAG_SELECTED, via)
+						&& !TEST_FLAG(PCB_FLAG_LOCK, via)) {
+					RemoveObject(PCB_TYPE_VIA, via, via, via);
+					changed = pcb_true;
+				}
+			}
+			END_LOOP;
+			if (changed) {
+				IncrementUndoSerialNumber();
+				SetChangedFlag(pcb_true);
+			}
+			break;
+		case F_Element:
+			{
+				void *ptr1, *ptr2, *ptr3;
+
+				if (SearchScreen(Crosshair.X, Crosshair.Y, PCB_TYPE_ELEMENT, &ptr1, &ptr2, &ptr3) != PCB_TYPE_NONE) {
+					Note.Buffer = conf_core.editor.buffer_number;
+					SetBufferNumber(MAX_BUFFER - 1);
+					ClearBuffer(PASTEBUFFER);
+					CopyObjectToBuffer(PASTEBUFFER->Data, PCB->Data, PCB_TYPE_ELEMENT, ptr1, ptr2, ptr3);
+					SmashBufferElement(PASTEBUFFER);
+					PASTEBUFFER->X = 0;
+					PASTEBUFFER->Y = 0;
+					SaveUndoSerialNumber();
+					EraseObject(PCB_TYPE_ELEMENT, ptr1, ptr1);
+					MoveObjectToRemoveUndoList(PCB_TYPE_ELEMENT, ptr1, ptr2, ptr3);
+					RestoreUndoSerialNumber();
+					CopyPastebufferToLayout(0, 0);
+					SetBufferNumber(Note.Buffer);
+					SetChangedFlag(pcb_true);
+				}
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------  */
+
+static const char minmaskgap_syntax[] = "MinMaskGap(delta)\n" "MinMaskGap(Selected, delta)";
+
+static const char minmaskgap_help[] = "Ensures the mask is a minimum distance from pins and pads.";
+
+/* %start-doc actions MinMaskGap
+
+Checks all specified pins and/or pads, and increases the mask if
+needed to ensure a minimum distance between the pin or pad edge and
+the mask edge.
+
+%end-doc */
+
+static int ActionMinMaskGap(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *delta = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	pcb_bool absolute;
+	Coord value;
+	int flags;
+
+	if (!function)
+		return 1;
+	if (strcasecmp(function, "Selected") == 0)
+		flags = PCB_FLAG_SELECTED;
+	else {
+		units = delta;
+		delta = function;
+		flags = 0;
+	}
+	value = 2 * GetValue(delta, units, &absolute, NULL);
+
+	SaveUndoSerialNumber();
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (!TEST_FLAGS(flags, pin))
+				continue;
+			if (pin->Mask < pin->Thickness + value) {
+				ChangeObjectMaskSize(PCB_TYPE_PIN, element, pin, 0, pin->Thickness + value, 1);
+				RestoreUndoSerialNumber();
+			}
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (!TEST_FLAGS(flags, pad))
+				continue;
+			if (pad->Mask < pad->Thickness + value) {
+				ChangeObjectMaskSize(PCB_TYPE_PAD, element, pad, 0, pad->Thickness + value, 1);
+				RestoreUndoSerialNumber();
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	VIA_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAGS(flags, via))
+			continue;
+		if (via->Mask && via->Mask < via->Thickness + value) {
+			ChangeObjectMaskSize(PCB_TYPE_VIA, via, 0, 0, via->Thickness + value, 1);
+			RestoreUndoSerialNumber();
+		}
+	}
+	END_LOOP;
+	RestoreUndoSerialNumber();
+	IncrementUndoSerialNumber();
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------  */
+
+static const char mincleargap_syntax[] = "MinClearGap(delta)\n" "MinClearGap(Selected, delta)";
+
+static const char mincleargap_help[] = "Ensures that polygons are a minimum distance from objects.";
+
+/* %start-doc actions MinClearGap
+
+Checks all specified objects, and increases the polygon clearance if
+needed to ensure a minimum distance between their edges and the
+polygon edges.
+
+%end-doc */
+
+static int ActionMinClearGap(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	const char *delta = ACTION_ARG(1);
+	const char *units = ACTION_ARG(2);
+	pcb_bool absolute;
+	Coord value;
+	int flags;
+
+	if (!function)
+		return 1;
+	if (strcasecmp(function, "Selected") == 0)
+		flags = PCB_FLAG_SELECTED;
+	else {
+		units = delta;
+		delta = function;
+		flags = 0;
+	}
+	value = 2 * GetValue(delta, units, &absolute, NULL);
+
+	SaveUndoSerialNumber();
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (!TEST_FLAGS(flags, pin))
+				continue;
+			if (pin->Clearance < value) {
+				ChangeObjectClearSize(PCB_TYPE_PIN, element, pin, 0, value, 1);
+				RestoreUndoSerialNumber();
+			}
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (!TEST_FLAGS(flags, pad))
+				continue;
+			if (pad->Clearance < value) {
+				ChangeObjectClearSize(PCB_TYPE_PAD, element, pad, 0, value, 1);
+				RestoreUndoSerialNumber();
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	VIA_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAGS(flags, via))
+			continue;
+		if (via->Clearance < value) {
+			ChangeObjectClearSize(PCB_TYPE_VIA, via, 0, 0, value, 1);
+			RestoreUndoSerialNumber();
+		}
+	}
+	END_LOOP;
+	ALLLINE_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAGS(flags, line))
+			continue;
+		if (line->Clearance < value) {
+			ChangeObjectClearSize(PCB_TYPE_LINE, layer, line, 0, value, 1);
+			RestoreUndoSerialNumber();
+		}
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAGS(flags, arc))
+			continue;
+		if (arc->Clearance < value) {
+			ChangeObjectClearSize(PCB_TYPE_ARC, layer, arc, 0, value, 1);
+			RestoreUndoSerialNumber();
+		}
+	}
+	ENDALL_LOOP;
+	RestoreUndoSerialNumber();
+	IncrementUndoSerialNumber();
+	return 0;
+}
+
+
+HID_Action object_action_list[] = {
+	{"Attributes", 0, ActionAttributes,
+	 attributes_help, attributes_syntax}
+	,
+	{"DisperseElements", 0, ActionDisperseElements,
+	 disperseelements_help, disperseelements_syntax}
+	,
+	{"Flip", N_("Click on Object or Flip Point"), ActionFlip,
+	 flip_help, flip_syntax}
+	,
+	{"MoveObject", N_("Select an Object"), ActionMoveObject,
+	 moveobject_help, moveobject_syntax}
+	,
+	{"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
+	 movetocurrentlayer_help, movetocurrentlayer_syntax}
+	,
+	{"ElementList", 0, ActionElementList,
+	 elementlist_help, elementlist_syntax}
+	,
+	{"ElementSetAttr", 0, ActionElementSetAttr,
+	 elementsetattr_help, elementsetattr_syntax}
+	,
+	{"RipUp", 0, ActionRipUp,
+	 ripup_help, ripup_syntax}
+	,
+	{"MinMaskGap", 0, ActionMinMaskGap,
+	 minmaskgap_help, minmaskgap_syntax}
+	,
+	{"MinClearGap", 0, ActionMinClearGap,
+	 mincleargap_help, mincleargap_syntax}
+};
+
+REGISTER_ACTIONS(object_action_list, NULL)
diff --git a/src/paths.c b/src/paths.c
new file mode 100644
index 0000000..56044f6
--- /dev/null
+++ b/src/paths.c
@@ -0,0 +1,87 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "paths.h"
+#include "error.h"
+#include "conf.h"
+#include "conf_core.h"
+
+void resolve_paths(const char **in, char **out, int numpaths, unsigned int extra_room)
+{
+	const char *subst_to;
+	int subst_offs;
+	for (; numpaths > 0; numpaths--, in++, out++) {
+		if (*in != NULL) {
+			if (**in == '~') {
+				int l1, l2;
+				if (conf_core.rc.path.home == NULL) {
+					Message(PCB_MSG_DEFAULT, "can't resolve home dir required for path %s\n", *in);
+					exit(1);
+				}
+				subst_to = conf_core.rc.path.home;
+				subst_offs = 1;
+				replace:;
+				/* avoid Concat() here to reduce dependencies for external tools */
+				l1 = strlen(subst_to);
+				l2 = strlen((*in) + 1);
+				*out = malloc(l1 + l2 + 4 + extra_room);
+				sprintf(*out, "%s%s", subst_to, (*in) + subst_offs);
+			}
+			else if (**in == '$') {
+				if ((*in)[1] == '(') {
+					char *end = strchr((*in)+2, ')');
+					if (end != NULL) {
+						char hash_path[128];
+						int len = end - (*in);
+						if (len < sizeof(hash_path)-1) {
+							conf_native_t *cn;
+							const char *si;
+							char *so;
+							int n;
+
+							(*in) += 2;
+							len -= 2;
+							for(si = *in, so = hash_path, n=0; n < len; n++,si++,so++) {
+								if (*si == '.')
+									*so = '/';
+								else
+									*so = *si;
+							}
+							*so = 0;
+							cn = conf_get_field(hash_path);
+							if ((cn != NULL) && (cn->type == CFN_STRING)) {
+								subst_to = cn->val.string[0];
+								subst_offs = len+1;
+								goto replace;
+							}
+						}
+					}
+					Message(PCB_MSG_DEFAULT, "can't resolve conf-hash dir required for path %s\n", *in);
+					*out = NULL;
+				}
+				else
+					*out = NULL;
+			}
+			else {
+				*out = malloc(strlen(*in) + 1 + extra_room);
+				strcpy(*out, *in);
+			}
+		}
+		else
+			*out = NULL;
+	}
+}
+
+void resolve_path(const char *in, char **out, unsigned int extra_room)
+{
+	resolve_paths(&in, out, 1, extra_room);
+}
+
+char *resolve_path_inplace(char *in, unsigned int extra_room)
+{
+	char *out;
+	resolve_path(in, &out, extra_room);
+	free(in);
+	return out;
+}
diff --git a/src/paths.h b/src/paths.h
new file mode 100644
index 0000000..140ff18
--- /dev/null
+++ b/src/paths.h
@@ -0,0 +1,23 @@
+
+/* Allocate *out and copy the path from in to out, replacing ~ with conf_core.rc.path.home
+   If extra_room is non-zero, allocate this many bytes extra for each slot;
+   this leaves some room to append a file name. */
+void resolve_path(const char *in, char **out, unsigned int extra_room);
+
+/* Same as resolve_path, but it returns the pointer to the new path and calls
+   free() on in */
+char *resolve_path_inplace(char *in, unsigned int extra_room);
+
+
+/* Resolve all paths from a in[] into out[](should be large enough) */
+void resolve_paths(const char **in, char **out, int numpaths, unsigned int extra_room);
+
+/* Resolve all paths from a char *in[] into a freshly allocated char **out */
+#define resolve_all_paths(in, out, extra_room) \
+do { \
+	int __numpath__ = sizeof(in) / sizeof(char *); \
+	if (__numpath__ > 0) { \
+		out = malloc(sizeof(char *) * __numpath__); \
+		resolve_paths(in, out, __numpath__, extra_room); \
+	} \
+} while(0)
diff --git a/src/pcb-conf.lht b/src/pcb-conf.lht
new file mode 100644
index 0000000..92e1b20
--- /dev/null
+++ b/src/pcb-conf.lht
@@ -0,0 +1,250 @@
+li:pcb-rnd-conf-v1 {
+	ha:overwrite {
+		ha:editor {
+			grid_unit = mil
+			grid = 25 mil
+			ha:increments_mm {
+				grid=0.1mm
+				grid_min=0.01mm
+				grid_max=1mm
+				size=0.2mm
+				size_min=0.01mm
+				size_max=0.5mm
+				line=0.1mm
+				line_min=0.005mm
+				line_max=9.5mm
+				clear=0.05mm
+				clear_min=0.005mm
+				clear_max=0.5mm
+			}
+			ha:increments_mil {
+				grid=5 mil
+				grid_min=1 mil
+				grid_max=25 mil
+				size=10 mil
+				size_min = 1 mil
+				size_max = 10 mil
+				line=5 mil
+				line_min = 0.5 mil
+				line_max = 10 mil
+				clear=2 mil
+				clear_min = 0.5 mil
+				clear_max = 10 mil
+			}
+			zoom = 1
+			mode = 110
+			buffer_number = 0
+			clear_line = 1
+			full_poly = 0
+			unique_names = 0
+			snap_pin = 1
+			snap_offgrid_line = 1
+			highlight_on_point = 0
+			show_solder_side = 0
+			save_last_command = 0
+			save_in_tmp = 0
+			draw_grid = 1
+			all_direction_lines = 0
+			line_refraction = 1
+			rubber_band_mode = 0
+			swap_start_direction = 0
+			show_drc = 0
+			auto_drc = 0
+			show_number = 0
+			orthogonal_moves = 0
+			reset_after_element = 1
+			auto_place = 0
+			lock_names = 0
+			only_names = 0
+			thin_draw = 0
+			thin_draw_poly = 0
+			local_ref = 0
+			check_planes = 0
+			show_mask = 0
+			hide_names = 0
+			description = 0
+			name_on_pcb = 0
+			enable_stroke = 0
+			live_routing = 0
+			beep_when_finished = 0
+			click_time = 200
+			ha:view {
+				flip_x = 0
+				flip_y = 0
+			}
+			undo_warning_size = 1024
+			fullscreen = 0
+		} # editor
+
+		ha:rc {
+			verbose = 0
+			backup_interval = 60
+#			font_command = {}
+#			file_command = {}
+#			file_path = {}
+			library_shell = {}
+
+			emergency_name = {PCB.%ld.save}
+			backup_name = {PCB.%P.backup}
+
+			li:library_search_paths = {
+				../pcblib
+				~/pcblib/
+				$(rc.path.share)/pcblib
+			}
+
+			keep_save_backups = false
+			save_command = {}
+
+			li:default_font_file = {
+				{./default_font}
+				{$(rc.path.share)/default_font}
+			}
+			li:default_pcb_file = {
+				{./default.pcb}
+				{$(rc.path.share)/default.pcb}
+			}
+
+#			script_filename = {}
+#			action_string = {}
+#			rat_path = {}
+#			rat_command = {}
+			li:preferred_gui = { gtk; lesstif }
+		} # rc
+
+		ha:design {
+#			via_thickness = 0
+#			via_drilling_hole  = 0
+#			line_thickness = 0
+#			clearance = 0
+#			max_width = 0
+#			max_height = 0
+#			alignment_distance = 0
+#			bloat = 0
+#			shrink = 0
+#			min_wid = 0
+#			min_slk = 0
+#			min_drill = 0
+#			min_ring = 0
+			text_scale = 100
+#			poly_isle_area = 0
+#			li:default_layer_name = {layername1; layername2}
+#			background_image = {}
+#			fab_author = {}
+#			initial_layer_stack = {}
+			groups = {}
+			routes = {}
+		} # design
+
+		ha:appearance {
+			rat_thickness = 0.25 mm
+			mark_size = 50 mil
+
+			ha:loglevels {
+				debug_tag      = {}
+				debug_popup    = false
+				info_tag       = {}
+				info_popup     = false
+				warning_tag    = {<B>}
+				warning_popup  = true
+				error_tag      = {<R>}
+				error_popup    = true
+			}
+			ha:color {
+				black = {#000000}
+				white = {#ffffff}
+				background = {#e5e5e5}
+				crosshair = {#ff0000}
+				cross = {#cdcd00}
+				via = {#7f7f7f}
+				via_selected = {#00ffff}
+				pin = {#4d4d4d}
+				pin_selected = {#00ffff}
+				pin_name = {#ff0000}
+				element = {#000000}
+				element_nonetlist = {#777777}
+				rat = {#b8860b}
+				invisible_objects = {#cccccc}
+				invisible_mark = {#cccccc}
+				element_selected = {#00ffff}
+				rat_selected = {#00ffff}
+				connected = {#00ff00}
+				off_limit = {#cccccc}
+				grid = {#ff0000}
+				li:layer = {
+					{#8b2323}; {#3a5fcd}; {#104e8b}; {#cd3700}; {#548b54};
+					{#8b7355}; {#00868b}; {#228b22}; {#8b2323}; {#3a5fcd};
+					{#104e8b}; {#cd3700}; {#548b54}; {#8b7355}; {#00868b};
+					{#228b22};
+				}
+				li:layer_selected = {
+					{#00ffff}; {#00ffff}; {#00ffff}; {#00ffff}; {#00ffff};
+					{#00ffff}; {#00ffff}; {#00ffff}; {#00ffff}; {#00ffff};
+					{#00ffff}; {#00ffff}; {#00ffff}; {#00ffff}; {#00ffff};
+					{#00ffff};
+				}
+				warn = {#ff8000}
+				mask = {#ff0000}
+			}
+			ha:pinout {
+#				name_length = 0
+#				zoom = 0
+				offset_x = 25400
+				offset_y = 25400
+				text_offset_x = 0.1 mm
+				text_offset_y = 0.1 mm
+			}
+			ha:messages {
+#				char_per_line = 0
+			}
+#			ha:misc {
+#				volume = 0
+#			} misc;
+		} # appearance;
+
+		ha:plugins {
+			ha:io_lihata {
+				aux_pcb_pattern = {%D.%B.lht.pcb}
+			}
+			ha:mincut {
+				enable = 1
+			}
+			ha:djopt {
+				auto_only = 1
+			}
+			ha:vendor {
+				enable = 0
+			}
+			ha:import_sch {
+				gnetlist_program = gnetlist
+				make_program = make
+			}
+			ha:hid_gtk {
+				compact_horizontal = 1
+				compact_vertical = 1
+				use_command_window = 0
+				history_size = 16
+				ha:local_grid {
+					enable = 0
+					radius = 16
+				}
+				ha:global_grid {
+					sparse = 0
+					min_dist_px = 4
+				}
+			}
+			ha:report {
+				columns = 8
+			}
+		} # plugins
+
+		ha:utils {
+			ha:gsch2pcb-rnd {
+				remove_unfound_elements = 1
+				quiet_mode = 0
+				verbose = 0
+			}
+		} # utils
+	} # main
+} # root
+
diff --git a/src/pcb-menu-gtk.lht b/src/pcb-menu-gtk.lht
new file mode 100644
index 0000000..c5f2331
--- /dev/null
+++ b/src/pcb-menu-gtk.lht
@@ -0,0 +1,607 @@
+ha:{
+	li:mouse {
+		li:left {
+			li:press            = { Mode(Notify) }
+			li:press-shift      = { Mode(Notify) }
+			li:press-ctrl       = { Mode(Save); Mode(None); Mode(Restore); Mode(Notify) }
+			li:press-shift-ctrl = { Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore) }
+			li:release          = { Mode(Release) }
+			li:release-shift    = { Mode(Release) }
+			li:release-ctrl     = { Mode(Release) }
+			li:release-shift-ctrl = { Mode(Release) }
+		}
+		li:right {
+			li:press         = { Pan(1) }
+			li:release       = { Pan(0) }
+			li:press-shift   = { Popup(popup1) }
+			li:press-ctrl    = { Display(CycleCrosshair) }
+		}
+		li:middle {
+			li:press               = { Mode(Stroke) }
+			li:release             = { Mode(Release) }
+			li:press-ctrl          = { Mode(Save); Mode(Copy); Mode(Notify) }
+			li:release-ctrl        = { Mode(Notify); Mode(Restore); }
+			li:press-shift-ctrl    = { conf(toggle, editor/rubber_band_mode, design); Mode(Save); Mode(Move); Mode(Notify) }
+			li:release-shift-ctrl  = { Mode(Notify); Mode(Restore); conf(toggle, editor/rubber_band_mode, design) }
+		}
+		li:scroll-up {
+			li:press        = { Zoom(0.8) }
+			li:press-shift  = { Scroll(up) }
+			li:press-ctrl   = { Scroll(left) }
+		}
+		li:scroll-down {
+			li:press       = { Zoom(1.25) }
+			li:press-shift = { Scroll(down) }
+			li:press-ctrl  = { Scroll(right) }
+		}
+# If you want zoom to center, do this instead.
+		#ha:scroll-up = { li:{} = {Zoom(0.8); Center()} }
+		#ha:scroll-down = { li:{} = {Zoom(1.25); Center()} }
+	}
+	
+	li:main_menu {
+		### File Menu
+		ha:File {
+			m=F
+			li:submenu {
+				ha:Save Layout                       = { m=S; a={Ctrl<Key>s};        action=Save(Layout); tip=Saves current layout }
+				ha:Save Layout As...                 = { m=A; a={Shift Ctrl<Key>s};  action=Save(LayoutAs); tip=Saves current layout into a new file }
+				-
+				ha:Revert                            = {                             action=Load(Revert,none); tip=Revert to the layout stored on disk }
+				-
+				ha:Import Schematics                 = {                             action=Import() }
+				ha:Load layout                       = {                             action=Load(Layout); tip=Load a layout from a file }
+				ha:Load element data to paste-buffer = {                             li:action={PasteBuffer(Clear); Load(ElementTobuffer)} }
+				ha:Load layout data to paste-buffer  = {                             li:action={PasteBuffer(Clear); Load(LayoutTobuffer)} }
+				ha:Load netlist file                 = {                             action=Load(Netlist) }
+				ha:Load vendor resource file         = {                             action=LoadVendorFrom() }
+				-
+				ha:Save connection data of {
+					li:submenu {
+						ha:a single element                  = {                  li:action={ GetXY(Click to set the element mark <>); Save(ElementConnections)}}
+						ha:all elements                      = {                  action=Save(AllConnections) }
+						ha:unused pins                       = {                  action=Save(AllUnusedPins) }
+						ha:netlist patch for back annotation = {a=Alt Ctrl<Key>b; action=SavePatch() }
+					}
+				}
+				-
+				ha:Print layout...      = {               action=Print()}
+				ha:Export layout...     = {               action=Export()}
+				ha:Maintenance {
+					li:submenu {
+						ha:Calibrate Printer...           = { action=PrintCalibrate() }
+						ha:Re-scan the footprint library  = { action=fp_rehash() }
+					}
+				}
+				-
+				ha:Start New Layout     = { a=Ctrl<Key>n; action=New() }
+				-
+				ha:Preferences...       = {               action=DoWindows(Preferences)}
+				-
+				ha:Quit Program         = { a=Ctrl<Key>q; action=Quit() }
+			}
+		}
+
+		ha:Edit {
+			m=E
+			li:submenu {
+				ha:Undo last operation          = { a=<Key>u;           action=Undo() }
+				ha:Redo last undone operation   = { a=Shift<Key>r;      action=Redo() }
+				ha:Clear undo-buffer            = { a=Shift Ctrl<Key>u; action=Undo(ClearList) }
+				-
+				ha:Cut selection to buffer      = { a=Ctrl<Key>x;       li:action={ GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer) } }
+				ha:Copy selection to buffer     = { a=Ctrl<Key>c;       li:action={ GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Unselect(All); Mode(PasteBuffer) } }
+				ha:Paste buffer to layout       = { a=Ctrl<Key>v;       action=Mode(PasteBuffer) }
+				-
+				ha:Unselect all                 = { a=Shift Alt<Key>a;  action=Unselect(All) }
+				ha:Select all visible           = { a=Alt<Key>a;        action=Select(All) }
+				-
+				ha:Edit name of {
+					li:submenu {
+						ha:text on layout             = { a=<Key>n;           action=ChangeName(Object) }
+						ha:pin on layout              = { a=Shift Ctrl<Key>n; action=ChangeName(Object, Number) }
+						ha:layout                     = {                     action=ChangeName(Layout) }
+						ha:active layer               = {                     action=ChangeName(Layer) }
+					}
+				}
+				ha:Edit attributes of {
+					li:submenu {
+						ha:Layout                     = { action=Attributes(Layout) }
+						ha:CurrentLayer               = { action=Attributes(Layer) }
+						ha:Element                    = { action=Attributes(Element) }
+					}
+				}
+				ha:Change flags {
+					li:submenu {
+						ha:Nonetlist                  = { a=Alt<Key>n; action=ChangeNonetlist(Element) }
+					}
+				}
+				ha:Route Styles {
+					li:submenu {
+						@routestyles
+						-
+						ha:Edit...                    = { action=AdjustStyle(0) }
+					}
+				}
+			}
+		} # Edit
+
+		ha:View {
+			m=V
+			li:submenu {
+				ha:Enable visible grid        = { checked=editor/draw_grid; action=conf(toggle, editor/draw_grid, design) }
+				ha:Enable local grid          = { checked=plugins/hid_gtk/local_grid/enable; action=conf(toggle, plugins/hid_gtk/local_grid/enable, design) }
+				ha:Grid units {
+					li:submenu {
+						ha:mil                    = { checked=ChkGridUnits(mil); action=SetUnits(mil); update_on={editor/grid_unit} }
+						ha:mm                     = { checked=ChkGridUnits(mm); action=SetUnits(mm); update_on={editor/grid_unit} }
+					}
+				}
+				ha:Grid size = {
+					li:submenu {
+						ha:No Grid                = { checked=ChkGridSize(none); action=SetValue(Grid,1); update_on={editor/grid} }
+						-
+						ha:0.1 mil                = { checked=ChkGridSize(0.1mil); li:action={SetUnits(mil); SetValue(Grid,0.1mil)}; update_on={editor/grid}}
+						ha:1 mil                  = { checked=ChkGridSize(1mil); li:action={SetUnits(mil); SetValue(Grid,1mil)}; update_on={editor/grid} }
+						ha:5 mil                  = { checked=ChkGridSize(5mil); li:action={SetUnits(mil); SetValue(Grid,5mil)}; update_on={editor/grid} }
+						ha:10 mil                 = { checked=ChkGridSize(10mil); li:action={SetUnits(mil); SetValue(Grid,10mil)}; update_on={editor/grid} }
+						ha:25 mil                 = { checked=ChkGridSize(25mil); li:action={SetUnits(mil); SetValue(Grid,25mil)}; update_on={editor/grid} }
+						ha:50 mil                 = { checked=ChkGridSize(50mil); li:action={SetUnits(mil); SetValue(Grid,50mil)}; update_on={editor/grid} }
+						ha:100 mil                = { checked=ChkGridSize(100mil); li:action={SetUnits(mil); SetValue(Grid,100mil)}; update_on={editor/grid} }
+						-
+						ha:0.01 mm                = { checked=ChkGridSize(0.01mm); li:action={SetUnits(mm); SetValue(Grid,0.01mm)}; update_on={editor/grid} }
+						ha:0.05 mm                = { checked=ChkGridSize(0.05mm); li:action={SetUnits(mm); SetValue(Grid,0.05mm)}; update_on={editor/grid} }
+						ha:0.1 mm                 = { checked=ChkGridSize(0.10mm); li:action={SetUnits(mm); SetValue(Grid,0.1mm)}; update_on={editor/grid} }
+						ha:0.25 mm                = { checked=ChkGridSize(0.25mm); li:action={SetUnits(mm); SetValue(Grid,0.25mm)}; update_on={editor/grid} }
+						ha:0.5 mm                 = { checked=ChkGridSize(0.50mm); li:action={SetUnits(mm); SetValue(Grid,0.5mm)}; update_on={editor/grid} }
+						ha:1 mm                   = { checked=ChkGridSize(1mm); li:action={SetUnits(mm); SetValue(Grid,1mm)}; update_on={editor/grid} }
+						-
+						ha:Grid -5mil             = { a=Shift<Key>g; action=SetValue(Grid,-5,mil) }
+						ha:Grid +5mil             = { a=<Key>g;  action=SetValue(Grid,+5,mil) }
+						ha:Grid -0.05mm           = { a=Shift Ctrl<Key>g; action=SetValue(Grid,-0.05,mm) }
+						ha:Grid +0.05mm           = { a=Ctrl<Key>g; action=SetValue(Grid,+0.05,mm) }
+					}
+				}
+				ha:Grid properties = {
+					li:submenu {
+						ha:Enable local grid          = { checked=plugins/hid_gtk/local_grid/enable; action=conf(toggle, plugins/hid_gtk/local_grid/enable, design) }
+						-
+						ha:local grid radius 4   =  { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 4)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 4, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) }; update_on={} }
+						ha:local grid radius 8    = { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 8)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 8, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) }; update_on={} }
+						ha:local grid radius 16   = { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 16)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 16, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) }; update_on={} }
+						ha:local grid radius 32   = { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 32)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 32, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) }; update_on={} }
+						-
+						ha:sparse global grid     = { checked=plugins/hid_gtk/global_grid/sparse; action=conf(toggle, plugins/hid_gtk/global_grid/sparse, design); update_on={} }
+						ha:global grid density 4  = { checked={conf(iseq, plugins/hid_gtk/global_grid/min_dist_px, 4)}; li:action={conf(set, plugins/hid_gtk/global_grid/min_dist_px, 4, design); conf(set, plugins/hid_gtk/local_grid/enable, 0, design) }; update_on={} }
+						ha:global grid density 8  = { checked={conf(iseq, plugins/hid_gtk/global_grid/min_dist_px, 8)}; li:action={conf(set, plugins/hid_gtk/global_grid/min_dist_px, 8, design); conf(set, plugins/hid_gtk/local_grid/enable, 0, design) }; update_on={} }
+						ha:global grid density 16 = { checked={conf(iseq, plugins/hid_gtk/global_grid/min_dist_px, 16)}; li:action={conf(set, plugins/hid_gtk/global_grid/min_dist_px, 16, design); conf(set, plugins/hid_gtk/local_grid/enable, 0, design) }; update_on={} }
+					}
+				}
+				ha:Realign grid               = { li:action={GetXY(Click to set the grid origin); Display(ToggleGrid) } }
+				-
+				ha:Displayed element name {
+					li:submenu {
+						ha:Description            = { checked=ChkElementName(1); action=Display(Description); update_on={editor/description} }
+						ha:Reference Designator   = { checked=ChkElementName(2); action=Display(NameOnPCB); update_on={editor/name_on_pcb} }
+						ha:Value                  = { checked=ChkElementName(3); action=Display(Value); update_on={editor/name_on_pcb} }
+					}
+				}
+				ha:Enable Pinout shows number = { checked=editor/show_number; action=conf(toggle, editor/show_number, design) }
+				ha:Pins/Via show Name/Number  = { a=<Key>d; action=Display(PinOrPadName) }
+				ha:Zoom In 20%                = { m=Z; a=<Key>z; action=Zoom(-1.2) }
+				ha:Zoom Out 20%               = { m=O; a=Shift<Key>z; action=Zoom(+1.2) }
+				ha:More zooms and view changes {
+					li:submenu {
+						ha:Zoom Max               = { m=M; a=<Key>v; action=Zoom() }
+						ha:Zoom In 2X             = { action=Zoom(-2) }
+						ha:Zoom Out 2X            = { action=Zoom(+2) }
+						ha:Zoom to 0.1mil/px      = { action={Zoom(=0.1mil)} }
+						ha:Zoom to 0.01mm/px      = { action={Zoom(=0.01mm)} }
+						ha:Zoom to 1mil/px        = { action={Zoom(=1mil)} }
+						ha:Zoom to 0.05mm/px      = { action={Zoom(=0.05mm)} }
+						ha:Zoom to 2.5mil/px      = { action={Zoom(=2.5mil)} }
+						ha:Zoom to 0.1mm/px       = { action={Zoom(=0.1mm)} }
+						ha:Zoom to 10mil/px       = { action={Zoom(=10mil)} }
+						ha:Zoom In 20% and center = { m=Z; li:action={Zoom(-1.2); Center()} }
+						ha:Zoom Out 20% and center= { m=O; li:action={Zoom(+1.2); Center()} }
+						ha:Flip up/down           = { checked=editor/view/flip_y; a=<Key>Tab; action=SwapSides(V) }
+						ha:Flip left/right        = { checked=editor/view/flip_x; a=Shift<Key>Tab; action=SwapSides(H) }
+						ha:Spin 180 degrees       = { a=Ctrl<Key>Tab; action=SwapSides(R) }
+						ha:Swap Sides             = { a=Ctrl Shift<Key>Tab; action=SwapSides() }
+						ha:Center cursor          = { a=<Key>c; action=Center() }
+					}
+				}
+				-
+				ha:Shown Layers {
+					li:submenu {
+						@layerview
+						-
+						ha:Edit Layer Groups      = { action=EditLayerGroups() }
+					}
+				}
+				ha:Current Layer {
+					li:submenu {
+						anon2=@layerpick
+						-
+						ha:Delete current layer   = { action=MoveLayer(c,-1) }
+						ha:Add new layer          = { action=MoveLayer(-1,c) }
+						ha:Move current layer up  = { action=MoveLayer(c,up) }
+						ha:Move current layer down= { action=MoveLayer(c,down) }
+					}
+				}
+				ha:Full screen                = { checked=editor/fullscreen; a=<Key>\\;  action=fullscreen(toggle) }
+			}
+		} # View
+
+		ha:Settings = {
+			m=S
+			li:submenu {
+				ha:'All-direction' lines            = { checked=editor/all_direction_lines; a=<Key>.; action=conf(toggle, editor/all_direction_lines, design) }
+				ha:Auto swap line start angle       = { checked=editor/swap_start_direction; action=conf(toggle, editor/swap_start_direction, design) }
+				ha:Orthogonal moves                 = { checked=editor/orthogonal_moves; action=conf(toggle, editor/orthogonal_moves, design) }
+				ha:Crosshair snaps to pins and pads = { checked=editor/snap_pin; action=conf(toggle, editor/snap_pin, design) }
+				ha:Crosshair snaps to off-grid points on lines = { checked=editor/snap_offgrid_line; action=conf(toggle, editor/snap_offgrid_line, design) }
+				ha:Crosshair shows DRC clearance    = { checked=editor/show_drc; action=conf(toggle, editor/show_drc, design) }
+				ha:Auto enforce DRC clearance       = { checked=editor/auto_drc; action=conf(toggle, editor/auto_drc, design) }
+				ha:Lock Names                       = { checked=editor/lock_names; action=conf(toggle, editor/lock_names, design) }
+				ha:Only Names                       = { checked=editor/only_names; action=conf(toggle, editor/only_names, design) }
+				ha:Hide Names                       = { checked=editor/hide_names; action=conf(toggle, editor/hide_names, design) }
+				ha:Mincut on shorts                 = { checked=plugins/mincut/enable; action=conf(toggle, plugins/mincut/enable, design) }
+				ha:Libstroke gestures on middle button = { checked=editor/enable_stroke; action=conf(toggle, editor/enable_stroke, design) }
+				-
+				ha:Rubber band mode                 = { checked=editor/rubber_band_mode; action=conf(toggle, editor/rubber_band_mode, design) }
+				ha:Require unique element names     = { checked=editor/unique_names; action=conf(toggle, editor/unique_names, design) }
+				ha:Auto-zero delta measurements     = { checked=editor/local_ref; action=conf(toggle, editor/local_ref, design) }
+				ha:New lines, arcs clear polygons   = { checked=editor/clear_line; action=conf(toggle, editor/clear_line, design) }
+				ha:New polygons are full ones       = { checked=editor/full_poly; action=conf(toggle, editor/full_poly, design) }
+				ha:Show autorouter trials           = { checked=editor/live_routing; action=conf(toggle, editor/live_routing, design) }
+				ha:Highlighting on line, arc points = { checked=editor/highlight_on_point; action=conf(toggle, editor/highlight_on_point, design) }
+				ha:Thin draw                        = { checked=editor/thin_draw; a=<Key>|;  action=conf(toggle, editor/thin_draw, design) }
+				ha:Thin draw poly                   = { checked=editor/thin_draw_poly; a=Ctrl Shift<Key>p; action=conf(toggle, editor/thin_draw_poly, design) }
+				ha:Check polygons                   = { checked=editor/check_planes; action=conf(toggle, editor/check_planes, design) }
+				-
+				ha:Vendor drill mapping             = { checked=plugins/vendor/enable; action=conf(toggle, plugins/vendor/enable, design) }
+				ha:Import New Elements at = {
+					m=I
+					li:submenu {
+						ha:Center          = { m=C; action=Import(setnewpoint,center) }
+						ha:Mark            = { m=M; action=Import(setnewpoint,mark) }
+						ha:Crosshair       = { m=h; action=Import(setnewpoint) }
+						-
+						ha:Set Dispersion  = { m=D; action=Import(setdisperse) }
+					}
+				}
+			}
+		} #Settings
+
+		ha:Select {
+			m=l
+			li:submenu {
+				ha:Select all visible objects      = { action=Select(All) }
+				ha:Select all connected objects    = { action=Select(Connection) }
+				-
+				ha:Unselect all objects            = { action=Unselect(All) }
+				ha:unselect all connected objects  = { action=Unselect(Connection) }
+				-
+				ha:Select by name {
+					li:submenu {
+						ha:All objects           = { active=rc/have_regex; action=Select(ObjectByName) }
+						ha:Elements              = { active=rc/have_regex; action=Select(ElementByName) }
+						ha:Pads                  = { active=rc/have_regex; action=Select(PadByName) }
+						ha:Pins                  = { active=rc/have_regex; action=Select(PinByName) }
+						ha:Text                  = { active=rc/have_regex; action=Select(TextByName) }
+						ha:Vias                  = { active=rc/have_regex; action=Select(ViaByName) }
+					}
+				}
+#TODO: this is gtk-specific
+				ha:Advanced search and select= { action=DoWindows(search) }
+				-
+				ha:Auto-place selected elements          = { a=Ctrl<Key>p; action=AutoPlaceSelected() }
+				ha:Disperse all elements                 = { action=DisperseElements(All) }
+				ha:Disperse selected elements            = { action=DisperseElements(Selected) }
+				-
+				ha:Move selected elements to other side  = { a=Shift<Key>b; action=Flip(SelectedElements) }
+				ha:Move selected to current layer        = { a=Shift<Key>m; action=MoveToCurrentLayer(Selected) }
+				ha:Remove selected objects               = { a=Shift<Key>Delete; action=RemoveSelected() }
+				ha:Convert selection to element          = { action=Select(Convert) }
+				-
+				ha:Optimize selected rats                = { li:action={DeleteRats(SelectedRats); AddRats(SelectedRats) } }
+				ha:Auto-route selected rats              = { a=Alt<Key>r; action=AutoRoute(SelectedRats) }
+				ha:Rip up selected auto-routed tracks    = { action=RipUp(Selected) }
+				-
+				ha:Edit properties of selected...        = { a=Ctrl<Key>e; action=PropEdit(Selected) }
+				ha:Change size of selected objects {
+					li:submenu {
+						ha:Lines -10 mil = { li:action={ChangeSize(SelectedLines,-10,mil); ChangeSize(SelectedArcs,-10,mil)} }
+						ha:Lines +10 mil = { li:action={ChangeSize(SelectedLines,+10,mil); ChangeSize(SelectedArcs,+10,mil)} }
+						ha:Pads -10 mil  = { action=ChangeSize(SelectedPads,-10,mil) }
+						ha:Pads +10 mil  = { action=ChangeSize(SelectedPads,+10,mil) }
+						ha:Pins -10 mil  = { action=ChangeSize(SelectedPins,-10,mil) }
+						ha:Pins +10 mil  = { action=ChangeSize(SelectedPins,+10,mil) }
+						ha:Texts -10 mil = { action=ChangeSize(SelectedTexts,-10,mil) }
+						ha:Texts +10 mil = { action=ChangeSize(SelectedTexts,+10,mil) }
+						ha:Vias -10 mil  = { action=ChangeSize(SelectedVias,-10,mil) }
+						ha:Vias +10 mil  = { action=ChangeSize(SelectedVias,+10,mil) }
+					}
+				}
+				-
+				ha:Change drilling hole of selected objects {
+					li:submenu {
+						ha:Vias -10 mil = { action=ChangeDrillSize(SelectedVias,-10,mil) }
+						ha:Vias +10 mil = { action=ChangeDrillSize(SelectedVias,+10,mil) }
+						ha:Pins -10 mil = { action=ChangeDrillSize(SelectedPins,-10,mil) }
+						ha:Pins +10 mil = { action=ChangeDrillSize(SelectedPins,+10,mil) }
+					}
+				}
+				-
+				ha:Change square-flag of selected objects {
+					li:submenu {
+						ha:Elements  = { action=ChangeSquare(SelectedElements) }
+						ha:Pins      = { action=ChangeSquare(SelectedPins) }
+					}
+				}
+				ha:Cycle object being dragged = { a=<Key>x; action=CycleDrag() }
+			}
+		} # Select
+
+		ha:Buffer {
+			m=B
+			li:submenu {
+				ha:Cut selection to buffer = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)} }
+				ha:Paste buffer to layout  = { action=Mode(PasteBuffer) }
+				-
+				ha:Rotate buffer 90 deg CCW   = { a=Shift<Key>F7; li:action={Mode(PasteBuffer); PasteBuffer(Rotate,1)} }
+				ha:Rotate buffer 90 deg CW    = { li:action={Mode(PasteBuffer); PasteBuffer(Rotate,3)} }
+				ha:Arbitrarily Rotate Buffer  = { li:action={Mode(PasteBuffer); FreeRotateBuffer()} }
+				ha:Mirror buffer (up/down)    = { li:action={Mode(PasteBuffer); PasteBuffer(Mirror)} }
+				ha:Mirror buffer (left/right) = { li:action={Mode(PasteBuffer); PasteBuffer(Rotate,1); PasteBuffer(Mirror); PasteBuffer(Rotate,3)} }
+				-
+				ha:Clear buffer                    = { action=PasteBuffer(Clear) }
+				ha:Convert buffer to element       = { action=PasteBuffer(Convert) }
+				ha:Break buffer elements to pieces = { action=PasteBuffer(Restore) }
+				ha:Save buffer elements to file    = { action=Save(PasteBuffer) }
+				-
+				ha:Select Buffer \#1 = { checked=ChkBuffer(1); m=1; a=Shift<Key>1; action=PasteBuffer(1); update_on={editor/buffer_number} }
+				ha:Select Buffer \#2 = { checked=ChkBuffer(2); m=2; a=Shift<Key>2; action=PasteBuffer(2); update_on={editor/buffer_number} }
+				ha:Select Buffer \#3 = { checked=ChkBuffer(3); m=3; a=Shift<Key>3; action=PasteBuffer(3); update_on={editor/buffer_number} }
+				ha:Select Buffer \#4 = { checked=ChkBuffer(4); m=4; a=Shift<Key>4; action=PasteBuffer(4); update_on={editor/buffer_number} }
+				ha:Select Buffer \#5 = { checked=ChkBuffer(5); m=5; a=Shift<Key>5; action=PasteBuffer(5); update_on={editor/buffer_number} }
+			}
+		} # Buffer
+
+		ha:Connects = {
+			m=C
+			li:submenu {
+				ha:Lookup connection to object   = { a=Ctrl<Key>f; li:action={GetXY(Click on the object); Connection(Find)} }
+				ha:Reset scanned pads/pins/vias  = { li:action={Connection(ResetPinsViasAndPads); Display(Redraw)} }
+				ha:Reset scanned lines/polygons  = { li:action={Connection(ResetLinesAndPolygons); Display(Redraw)} }
+				ha:Reset all connections         = { a=Shift<Key>f; li:action={Connection(Reset); Display(Redraw)} }
+				-
+				ha:Optimize rats nest            = { a=<Key>o; li:action={Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(AllRats); Atomic(Block)} }
+				ha:Erase rats nest               = { a=<Key>e; action=DeleteRats(AllRats) }
+				ha:Erase selected rats           = { a=Shift<Key>e; action=DeleteRats(SelectedRats) }
+				-
+				ha:Auto-route selected rats      = { action=AutoRoute(Selected) }
+				ha:Auto-route all rats           = { action=AutoRoute(AllRats) }
+				ha:Rip up all auto-routed tracks = { action=RipUp(All) }
+				-
+				ha:Optimize routed tracks {
+					li:submenu {
+						ha:Auto-Optimize        = { a={Shift<Key>=}; action=djopt(auto) }
+						ha:Debumpify            = { action=djopt(debumpify) }
+						ha:Unjaggy              = { action=djopt(unjaggy) }
+						ha:Vianudge             = { action=djopt(vianudge) }
+						ha:Viatrim              = { action=djopt(viatrim) }
+						ha:Ortho pull           = { action=djopt(orthopull) }
+						ha:Simple optimization  = { a={<Key>=}; action=djopt(simple) }
+						ha:Miter                = { action=djopt(miter) }
+						ha:Puller               = { a=<Key>y; action=Puller() }
+						ha:Global Puller {
+							li:submenu {
+								ha:Selected  = { action=GlobalPuller(selected) }
+								ha:Found     = { action=GlobalPuller(found) }
+								ha:All       = { action=GlobalPuller() }
+							}
+						}
+						-
+						ha:Only autorouted nets = { checked=plugins/djopt/auto_only; action=conf(toggle, plugins/djopt/auto_only, design) }
+					}
+				}
+				-
+				ha:Design Rule Checker = { action=DRC() }
+				-
+				ha:Apply vendor drill mapping = { action=ApplyVendor() }
+				-
+				ha:Design changes (back annotation) {
+					li:submenu {
+						ha:Swap nets on two selected pins  = { a=Shift<Key>x; action=net(swap) }
+						ha:Replace footprint               = { a=Alt Shift<Key>f; action=ReplaceFootprint() }
+					}
+				}
+			}
+		} # Connects
+	
+		ha:Plugins {
+			m=P
+			li:submenu {
+				ha:Manage plugins... = { a=Alt<Key>p; action=ManagePlugins() }
+			}
+		} # Plugins
+
+		ha:Info = {
+			m=I
+			li:submenu {
+				ha:Generate object report  = { a=Ctrl<Key>r; action=ReportObject() }
+				ha:Generate drill summary  = { action=Report(DrillReport) }
+				ha:Report found pins\/pads = { action=Report(FoundPins) }
+				ha:Key Bindings {
+					li:submenu {
+						ha:Remove                       = { a=<Key>Delete; li:action={Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore)} }
+						ha:Remove Selected              = { a=<Key>BackSpace; action=RemoveSelected() }
+						ha:Remove Connected             = { a=Shift<Key>BackSpace; li:action={Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)} }
+						ha:Remove Connected             = { li:action={Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)} }
+						ha:Set Same                     = { a=<Key>a; action=SetSame() }
+						ha:Flip Object                  = { a=<Key>b; action=Flip(Object) }
+						ha:Find Connections             = { a=<Key>f; li:action={Connection(Reset); Connection(Find)} }
+						ha:ToggleHideName Object        = { a=<Key>h; action=ToggleHideName(Object) }
+						ha:ToggleHideName SelectedElement = { a=Shift<Key>h; action=ToggleHideName(SelectedElements) }
+						ha:ChangeHole Object            = { a=Ctrl<Key>h; action=ChangeHole(Object) }
+						ha:ChangeJoin Object            = { a=<Key>j; action=ChangeJoin(Object) }
+						ha:ChangeJoin SelectedObject    = { a=Shift<Key>j; action=ChangeJoin(SelectedObjects) }
+						ha:Clear Object +2 mil          = { a=<Key>k; action=ChangeClearSize(Object,+2,mil) }
+						ha:Clear Object -2 mil          = { a=Shift<Key>k; action=ChangeClearSize(Object,-2,mil) }
+						ha:Clear Selected +2 mil        = { a=Ctrl<Key>k; action=ChangeClearSize(SelectedObjects,+2,mil) }
+						ha:Clear Selected -2 mil        = { a=Shift Ctrl<Key>k; action=ChangeClearSize(SelectedObjects,-2,mil) }
+						ha:Line Tool size +5 mil        = { a=<Key>l; action=SetValue(LineSize,+5,mil) }
+						ha:Line Tool size -5 mil        = { a=Shift<Key>l; action=SetValue(LineSize,-5,mil) }
+						ha:Move Object to current layer = { a=<Key>m; action=MoveToCurrentLayer(Object) }
+						ha:MarkCrosshair                = { a=Ctrl<Key>m; action=MarkCrosshair() }
+						ha:Select shortest rat          = { a=Shift<Key>n; action=AddRats(Close) }
+						ha:AddRats to selected pins     = { a=Shift<Key>o; li:action={Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(SelectedRats); Atomic(Block)} }
+						ha:ChangeOctagon Object         = { a=Ctrl<Key>o; action=ChangeOctagon(Object) }
+						ha:Polygon PreviousPoint        = { a=<Key>p; action=Polygon(PreviousPoint) }
+						ha:Polygon Close                = { a=Shift<Key>p; action=Polygon(Close) }
+						ha:ChangeSquare Object          = { a=<Key>q; action=ChangeSquare(ToggleObject) }
+						ha:ChangeSizes to Route style   = { a=Shift<Key>y; action=ChangeSizes(Object,style,mil) }
+						ha:ChangeSize +5 mil            = { a=<Key>s; action=ChangeSize(Object,+5,mil) }
+						ha:ChangeSize -5 mil            = { a=Shift<Key>s; action=ChangeSize(Object,-5,mil) }
+						ha:ChangeDrill +5 mil           = { a=Alt<Key>s; action=ChangeDrillSize(Object,+5,mil) }
+						ha:ChangeDrill -5 mil           = { a=Alt Shift<Key>s; action=ChangeDrillSize(Object,-5,mil) }
+						ha:Text Tool scale +10 mil      = { a=<Key>t; action=SetValue(TextScale,+10,mil) }
+						ha:Text Tool scale -10 mil      = { a=Shift<Key>t; action=SetValue(TextScale,-10,mil) }
+						ha:Via Tool size +5 mil         = { a=Shift<Key>v; action=SetValue(ViaSize,+5,mil) }
+						ha:Via Tool size -5 mil         = { a=Shift Ctrl<Key>v; action=SetValue(ViaSize,-5,mil) }
+						ha:Via Tool drill +5 mil        = { a=Alt<Key>v; action=SetValue(ViaDrillingHole,+5,mil) }
+						ha:Via Tool drill -5 mil        = { a=Alt Shift<Key>v; action=SetValue(ViaDrillingHole,-5,mil) }
+						ha:AddRats Selected             = { a=Shift<Key>w; action=AddRats(SelectedRats) }
+						ha:Add All Rats                 = { a=<Key>w; action=AddRats(AllRats) }
+						ha:Cycle Clip                   = { a=<Key>/; action=Display(CycleClip) }
+						ha:Arrow Mode                   = { a=<Key>space; checked=ChkMode(arrow); action=Mode(Arrow); update_on={editor/mode} }
+						ha:Temp Arrow ON                = { a=<Key>[; li:action={Mode(Save); Mode(Arrow); Mode(Notify)} }
+						ha:Temp Arrow OFF               = { a=<Key>]; li:action={Mode(Release); Mode(Restore)} }
+						-
+						ha:Step Up                      = { a=<Key>Up; action=Cursor(Warp,0,1,grid) }
+						ha:Step Down                    = { a=<Key>Down; action=Cursor(Warp,0,-1,grid) }
+						ha:Step Left                    = { a=<Key>Left; action=Cursor(Warp,-1,0,grid) }
+						ha:Step Right                   = { a=<Key>Right; action=Cursor(Warp,1,0,grid) }
+						ha:Step +Up                     = { a=Shift<Key>Up; action=Cursor(Pan,0,50,view) }
+						ha:Step +Down                   = { a=Shift<Key>Down; action=Cursor(Pan,0,-50,view) }
+						ha:Step +Left                   = { a=Shift<Key>Left; action=Cursor(Pan,-50,0,view) }
+						ha:Step +Right                  = { a=Shift<Key>Right; action=Cursor(Pan,50,0,view) }
+						ha:Click                        = { a=<Key>Enter; li:action={Mode(Notify); Mode(Release)} }
+						-
+						ha:layer keys {
+							li:submenu {
+								ha:Select Layer 1           = { a=<Key>1; action=SelectLayer(1) }
+								ha:Select Layer 2           = { a=<Key>2; action=SelectLayer(2) }
+								ha:Select Layer 3           = { a=<Key>3; action=SelectLayer(3) }
+								ha:Select Layer 4           = { a=<Key>4; action=SelectLayer(4) }
+								ha:Select Layer 5           = { a=<Key>5; action=SelectLayer(5) }
+								ha:Select Layer 6           = { a=<Key>6; action=SelectLayer(6) }
+								ha:Select Layer 7           = { a=<Key>7; action=SelectLayer(7) }
+								ha:Select Layer 8           = { a=<Key>8; action=SelectLayer(8) }
+								ha:Select Layer 9           = { a=<Key>9; action=SelectLayer(9) }
+								ha:Select Layer 10          = { a=<Key>0; action=SelectLayer(10) }
+								ha:Select Layer 11          = { a=Alt<Key>1; action=SelectLayer(11) }
+								ha:Select Layer 12          = { a=Alt<Key>2; action=SelectLayer(12) }
+								ha:Select Layer 13          = { a=Alt<Key>3; action=SelectLayer(13) }
+								ha:Select Layer 14          = { a=Alt<Key>4; action=SelectLayer(14) }
+								ha:Select Layer 15          = { a=Alt<Key>5; action=SelectLayer(15) }
+								ha:Select Layer 16          = { a=Alt<Key>6; action=SelectLayer(16) }
+								ha:Select Layer 17          = { a=Alt<Key>7; action=SelectLayer(17) }
+								ha:Select Layer 18          = { a=Alt<Key>8; action=SelectLayer(18) }
+								ha:Select Layer 19          = { a=Alt<Key>9; action=SelectLayer(19) }
+								ha:Select Layer 20          = { a=Alt<Key>0; action=SelectLayer(20) }
+								-
+								ha:Toggle Layer 1           = { a=Ctrl<Key>1; action=ToggleView(1) }
+								ha:Toggle Layer 2           = { a=Ctrl<Key>2; action=ToggleView(2) }
+								ha:Toggle Layer 3           = { a=Ctrl<Key>3; action=ToggleView(3) }
+								ha:Toggle Layer 4           = { a=Ctrl<Key>4; action=ToggleView(4) }
+								ha:Toggle Layer 5           = { a=Ctrl<Key>5; action=ToggleView(5) }
+								ha:Toggle Layer 6           = { a=Ctrl<Key>6; action=ToggleView(6) }
+								ha:Toggle Layer 7           = { a=Ctrl<Key>7; action=ToggleView(7) }
+								ha:Toggle Layer 8           = { a=Ctrl<Key>8; action=ToggleView(8) }
+								ha:Toggle Layer 9           = { a=Ctrl<Key>9; action=ToggleView(9) }
+								ha:Toggle Layer 10          = { a=Ctrl<Key>0; action=ToggleView(10) }
+								ha:Toggle Layer 11          = { a=Ctrl-Alt<Key>1; action=ToggleView(11) }
+								ha:Toggle Layer 12          = { a=Ctrl-Alt<Key>2; action=ToggleView(12) }
+								ha:Toggle Layer 13          = { a=Ctrl-Alt<Key>3; action=ToggleView(13) }
+								ha:Toggle Layer 14          = { a=Ctrl-Alt<Key>4; action=ToggleView(14) }
+								ha:Toggle Layer 15          = { a=Ctrl-Alt<Key>5; action=ToggleView(15) }
+								ha:Toggle Layer 16          = { a=Ctrl-Alt<Key>6; action=ToggleView(16) }
+								ha:Toggle Layer 17          = { a=Ctrl-Alt<Key>7; action=ToggleView(17) }
+								ha:Toggle Layer 18          = { a=Ctrl-Alt<Key>8; action=ToggleView(18) }
+								ha:Toggle Layer 19          = { a=Ctrl-Alt<Key>9; action=ToggleView(19) }
+								ha:Toggle Layer 20          = { a=Ctrl-Alt<Key>0; action=ToggleView(20) }
+							}
+						} # layer keys
+					}
+				}
+			}
+		} # Info
+
+		ha:Window {
+			m=W
+			li:submenu {
+				ha:Library        = { a=<Key>i; action=DoWindows(Library) }
+				ha:Message Log    = { action=DoWindows(Log) }
+				ha:DRC Check      = { action=DoWindows(DRC) }
+				ha:Netlist        = { action=DoWindows(Netlist) }
+				ha:Command Entry  = { a={<Key>:}; action=Command() }
+				ha:Pinout         = { a=Shift<Key>d; action=Display(Pinout) }
+				-
+				ha:About...       = { action=About() }
+			}
+		} # Window
+	} # main menu
+
+	li:popups {
+		ha:popup1 {
+			li:submenu {
+				ha:Operations on selections {
+					li:submenu {
+						ha:Unselect all objects               = { action=Unselect(All) }
+						ha:Remove selected objects            = { action=RemoveSelected() }
+						ha:Copy selection to buffer           = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Mode(PasteBuffer)} }
+						ha:Cut selection to buffer            = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)} }
+						ha:Convert selection to element       = { action=Select(Convert) }
+						ha:Auto place selected elements       = { action=AutoPlaceSelected() }
+						ha:Autoroute selected elements        = { action=AutoRoute(SelectedRats) }
+						ha:Rip up selected auto-routed tracks = { action=RipUp(Selected) }
+					}
+				}
+				ha:Operations on this location {
+					li:submenu {
+						ha:Generate object report = { li:action={GetXY(Click on the object); Report(Object)} }
+					}
+				}
+				-
+				ha:Undo last operation          = { action=Undo() }
+				ha:Redo last undone operation   = { action=Redo() }
+				-
+				ha:Tools {
+					li:submenu {
+						ha:None           = { checked=ChkMode(none); action=Mode(None); update_on={editor/mode} }
+						ha:Via            = { checked=ChkMode(via); a=<Key>F1; action=Mode(Via); update_on={editor/mode} }
+						ha:Line           = { checked=ChkMode(line); a=<Key>F2; action=Mode(Line); update_on={editor/mode} }
+						ha:Arc            = { checked=ChkMode(arc); a=<Key>F3; action=Mode(Arc); update_on={editor/mode} }
+						ha:Text           = { checked=ChkMode(text); a=<Key>F4; action=Mode(Text); update_on={editor/mode} }
+						ha:Rectangle      = { checked=ChkMode(rectangle); a=<Key>F5; action=Mode(Rectangle); update_on={editor/mode} }
+						ha:Polygon        = { checked=ChkMode(polygon); a=<Key>F6; action=Mode(Polygon); update_on={editor/mode} }
+						ha:Polygon Hole   = { checked=ChkMode(polygonhole); action=Mode(PolygonHole); update_on={editor/mode} }
+						ha:Buffer         = { checked=ChkMode(pastebuffer); a=<Key>F7; action=Mode(PasteBuffer); update_on={editor/mode} }
+						ha:Remove         = { checked=ChkMode(remove); a=<Key>F8; action=Mode(Remove); update_on={editor/mode} }
+						ha:Rotate         = { checked=ChkMode(rotate); a=<Key>F9; action=Mode(Rotate); update_on={editor/mode} }
+						ha:Thermal        = { checked=ChkMode(thermal); a=<Key>F10; action=Mode(Thermal); update_on={editor/mode} }
+						ha:Arrow          = { checked=ChkMode(arrow); a=<Key>F11; action=Mode(Arrow); update_on={editor/mode} }
+						ha:Insert Point   = { checked=ChkMode(insertpoint); a=<Key>Insert; action=Mode(InsertPoint); update_on={editor/mode} }
+						ha:Move           = { checked=ChkMode(move); action=Mode(Move); update_on={editor/mode} }
+						ha:Copy           = { checked=ChkMode(copy); action=Mode(Copy); update_on={editor/mode} }
+						ha:Lock           = { checked=ChkMode(lock); a=<Key>F12; action=Mode(Lock); update_on={editor/mode} }
+						ha:Cancel         = { a=<Key>Escape; action=Mode(Escape) }
+					}
+				}
+			}
+		} # popup1
+	} # popups
+} # root
diff --git a/src/pcb-menu-lesstif.lht b/src/pcb-menu-lesstif.lht
new file mode 100644
index 0000000..053954b
--- /dev/null
+++ b/src/pcb-menu-lesstif.lht
@@ -0,0 +1,499 @@
+ha:{
+	li:mouse {
+		li:left {
+			li:press         = { Mode(Notify) }
+			li:press-shift   = { Mode(Notify) }
+			li:release       = { Mode(Release) }
+			li:release-shift = { Mode(Release) }
+		}
+		li:right {
+			li:press         = { Mode(Save); Mode(Rotate); Mode(Notify); Mode(Release); Mode(Restore); }
+			li:press-ctrl    = { Display(CycleCrosshair) }
+		}
+		li:middle {
+			li:press         = { Pan(1) }
+			li:release       = { Pan(0) }
+			li:press-ctrl    = { Pan(thumb,1) }
+			li:release-ctrl  = { Pan(thumb,0) }
+		}
+		li:scroll-up {
+			li:press         = { Zoom(0.8) }
+		}
+		li:scroll-down {
+			li:press         = { Zoom(1.25) }
+		}
+		#If you want zoom to center, do this instead.
+		#in scroll-up:
+		#li:press = {Zoom(0.8); Center()}
+		#in scroll-down:
+		#li:perss = {Zoom(1.25); Center()}
+	}
+
+	li:main_menu {
+		ha:File = {
+			li:submenu {
+				ha:About... = { action=About() }
+				ha:Save layout = { m=S; a=Ctrl<Key>s; action=Save(Layout) }
+				ha:Save layout as... = { m=A; a=Shift Ctrl<Key>s; action=Save(LayoutAs) }
+				ha:Revert = { action=Load(Revert,none) }
+				ha:Import Schematics = { action=Import() }
+				ha:Load layout = { action=Load(Layout) }
+				ha:Load element data to paste-buffer = { li:action={PasteBuffer(Clear); Load(ElementTobuffer)} }
+				ha:Load layout data to paste-buffer = { li:action={PasteBuffer(Clear); Load(LayoutTobuffer)} }
+				ha:Load netlist file = { action=Load(Netlist) }
+				ha:Load vendor resource file = { action=LoadVendor() }
+				ha:Print layout... = { action=Print() }
+				ha:Export layout... = { action=Export() }
+				ha:Maintenance {
+					li:submenu {
+						ha:Calibrate Printer...           = { action=PrintCalibrate() }
+						ha:Re-scan the footprint library  = { action=fp_rehash() }
+					}
+				}
+				-
+				ha:Save connection data of... = { foreground=grey50; sensitive=false }
+				ha: a single element = { li:action={GetXY(Click to set the element mark <>); Save(ElementConnections)} }
+				ha: all elements = { action=Save(AllConnections) }
+				ha: unused pins = { action=Save(AllUnusedPins) }
+				-
+				ha:Start new layout = { a=Ctrl<Key>n; action=New() }
+				-
+				ha:Quit Program = { m=Q; a=Ctrl<Key>q; action=Quit() }
+			}
+		} # File
+
+		ha:View {
+			li:submenu {
+				ha:Flip up/down = { checked=editor/view/flip_y; a=<Key>Tab; action=SwapSides(V) }
+				ha:Flip left/right = { checked=editor/view/flip_x; a=Shift<Key>Tab; action=SwapSides(H) }
+				ha:Spin 180� = { a=Ctrl<Key>Tab; action=SwapSides(R) }
+				ha:Swap Sides = { a=Ctrl Shift<Key>Tab; action=SwapSides() }
+				ha:Center cursor = { a=<Key>c; action=Center() }
+				ha:Show soldermask = { checked=showmask; action=Display(ToggleMask) }
+				-
+				ha:Displayed element-name... = { foreground=grey50; sensitive=false }
+				ha:Description = { checked=ChkElementName(1); action=Display(Description) }
+				ha:Reference Designator = { checked=ChkElementName(2); action=Display(NameOnPCB) }
+				ha:Value = { checked=ChkElementName(3); action=Display(Value) }
+				ha:Lock Names = { checked=editor/lock_names; action=conf(toggle, editor/lock_names, design) }
+				ha:Only Names = { checked=editor/only_names; action=conf(toggle, editor/only_names, design) }
+				ha:Hide Names = { checked=editor/hide_names; action=conf(toggle, editor/hide_names, design) }
+				ha:Mincut on shorts = { checked=plugins/mincut/enable; action=conf(toggle, plugins/mincut/enable, design) }
+				-
+				ha:Pinout shows number = { checked=editor/show_number; action=conf(toggle, editor/show_number, design) }
+				ha:Open pinout menu = { a=Shift<Key>d; action=Display(Pinout) }
+				ha:Zoom {
+					li:submenu {
+						ha:Zoom In 2X = { action=Zoom(-2) }
+						ha:Zoom In 20% = { m=Z; a=<Key>z; action=Zoom(-1.2) }
+						ha:Zoom Out 20% = { m=O; a=Shift<Key>z; action=Zoom(+1.2); }
+						ha:Zoom Out 2X = { action=Zoom(+2) }
+						ha:Zoom Max = { m=M; a=<Key>v; action=Zoom() }
+						ha:Zoom Toggle = { a=<Key>`; action=Zoom(Toggle) }
+						-
+						ha:Zoom to 0.1mil\/px = { action={Zoom(=0.1mil)} }
+						ha:Zoom to 0.01mm\/px = { action={Zoom(=0.01mil)} }
+						ha:Zoom to 1mil\/px = { action={Zoom(=1mil)} }
+						ha:Zoom to 0.05mm\/px = { action={Zoom(=0.05mm)} }
+						ha:Zoom to 2.5mil\/px = { action={Zoom(=2.5mil)} }
+						ha:Zoom to 0.1mm\/px = { action={Zoom(=0.1mm)} }
+						ha:Zoom to 10mil\/px = { action={Zoom(=10mil)} }
+					}
+				}
+				ha:Grid {
+					li:submenu {
+						ha:mil = { checked=ChkGridUnits(mil); action=SetUnits(mil) }
+						ha:mm = { checked=ChkGridUnits(mm); action=SetUnits(mm) }
+						ha:Display grid = { checked=editor/draw_grid; action=conf(toggle, editor/draw_grid, design) }
+						ha:Realign grid = { li:action={GetXY(Click to set the grid origin); Display(ToggleGrid)} }
+						ha:No Grid = { checked=ChkGridSize(none); action=SetValue(Grid,1) }
+						-
+						ha:0.1 mil = { checked=ChkGridSize(0.1mil); li:action={SetUnits(mil); SetValue(Grid,0.1mil)} }
+						ha:1 mil = { checked=ChkGridSize(1mil); li:action={SetUnits(mil); SetValue(Grid,1mil)} }
+						ha:5 mil = { checked=ChkGridSize(5mil); li:action={SetUnits(mil); SetValue(Grid,5mil)} }
+						ha:10 mil = { checked=ChkGridSize(10mil); li:action={SetUnits(mil); SetValue(Grid,10mil)} }
+						ha:25 mil = { checked=ChkGridSize(25mil); li:action={SetUnits(mil); SetValue(Grid,25mil)} }
+						ha:50 mil = { checked=ChkGridSize(50mil); li:action={SetUnits(mil); SetValue(Grid,50mil)} }
+						ha:100 mil = { checked=ChkGridSize(100mil); li:action={SetUnits(mil); SetValue(Grid,100mil)} }
+						-
+						ha:0.01 mm = { checked=ChkGridSize(0.01mm); li:action={SetUnits(mm); SetValue(Grid,0.01mm)} }
+						ha:0.05 mm = { checked=ChkGridSize(0.05mm); li:action={SetUnits(mm); SetValue(Grid,0.05mm)} }
+						ha:0.1 mm = { checked=ChkGridSize(0.10mm); li:action={SetUnits(mm); SetValue(Grid,0.1mm)} }
+						ha:0.25 mm = { checked=ChkGridSize(0.25mm); li:action={SetUnits(mm); SetValue(Grid,0.25mm)} }
+						ha:0.5 mm = { checked=ChkGridSize(0.50mm); li:action={SetUnits(mm); SetValue(Grid,0.5mm)} }
+						ha:1 mm = { checked=ChkGridSize(1mm); li:action={SetUnits(mm); SetValue(Grid,1mm)} }
+						-
+						ha:Grid -5mil = { a=Shift<Key>g; action=SetValue(Grid,-5,mil) }
+						ha:Grid +5mil = { a=<Key>g; action=SetValue(Grid,+5,mil) }
+						ha:Grid -0.05mm = { a=Shift Ctrl<Key>g; action=SetValue(Grid,-0.05,mm) }
+						ha:Grid +0.05mm = { a=Ctrl<Key>g; action=SetValue(Grid,+0.05,mm) }
+					}
+				}
+				-
+				ha:Shown Layers {
+					li:submenu {
+						@layerview
+						-
+						ha:Edit Layer Groups = { action=EditLayerGroups() }
+					}
+				}
+				ha:Current Layer {
+					li:submenu {
+						@layerpick
+						-
+						ha:Delete current layer = { action=MoveLayer(c,-1) }
+						ha:Add new layer = { action=MoveLayer(-1,c) }
+						ha:Move current layer up = { action=MoveLayer(c,up) }
+						ha:Move current layer down = { action=MoveLayer(c,down) }
+					}
+				}
+			}
+		} # View
+
+		ha:Edit {
+			li:submenu {
+				ha:Undo last operation = { a=<Key>u; action=Undo() }
+				ha:Redo last undone operation = { a=Shift<Key>r; action=Redo() }
+				ha:Clear undo-buffer = { a=Shift Ctrl<Key>u; action=Undo(ClearList); }
+				-
+				ha:Cut selection to buffer = { a=Ctrl<Key>x; li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)} }
+				ha:Copy selection to buffer = { a=Ctrl<Key>c; li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Mode(PasteBuffer)} }
+				ha:Paste buffer to layout = { a=Ctrl<Key>v; action=Mode(PasteBuffer) }
+				-
+				ha:Unselect all = { a=Shift Alt<Key>a; action=Unselect(All) }
+				ha:Select all visible = { a=Alt<Key>a; action=Select(All) }
+				-
+				ha:Edit Names... = { foreground=grey50; sensitive=false }
+				ha: Change text on layout = { a=<Key>n; action=ChangeName(Object) }
+				ha: Change text on layout = { a=Shift Ctrl<Key>n; action=ChangeName(Object, Number) }
+				ha: Edit name of layout = { action=ChangeName(Layout) }
+				ha: Edit name of active layer = { action=ChangeName(Layer) }
+				ha:Edit Attributes... = { foreground=grey50; sensitive=false }
+				ha: Layout = { action=Attributes(Layout) }
+				ha: CurrentLayer = { action=Attributes(Layer) }
+				ha: Element = { action=Attributes(Element) }
+				-
+				ha:Board Sizes = { action=AdjustSizes() }
+				ha:Route Styles {
+					li:submenu {
+						@routestyles
+						-
+						ha:Edit... = { action=AdjustStyle(0) }
+					}
+				}
+			}
+		} # Edit
+
+		ha:Tools {
+			li:submenu {
+				ha:None = { checked=ChkMode(none); action=Mode(None) }
+				ha:Via = { checked=ChkMode(via); a=<Key>F1; action=Mode(Via) }
+				ha:Line = { checked=ChkMode(line); a=<Key>F2; action=Mode(Line) }
+				ha:Arc = { checked=ChkMode(arc); a=<Key>F3; action=Mode(Arc) }
+				ha:Text = { checked=ChkMode(text); a=<Key>F4; action=Mode(Text) }
+				ha:Rectangle = { checked=ChkMode(rectangle); a=<Key>F5; action=Mode(Rectangle) }
+				ha:Polygon = { checked=ChkMode(polygon); a=<Key>F6; action=Mode(Polygon) }
+				ha:Polygon Hole = { checked=ChkMode(polygonhole); action=Mode(PolygonHole); }
+				ha:Buffer = { checked=ChkMode(pastebuffer); a=<Key>F7; action=Mode(PasteBuffer) }
+				ha:Remove = { checked=ChkMode(remove); a=<Key>F8; action=Mode(Remove) }
+				ha:Rotate = { checked=ChkMode(rotate); a=<Key>F9; action=Mode(Rotate) }
+				ha:Thermal = { checked=ChkMode(thermal); a=<Key>F10; action=Mode(Thermal) }
+				ha:Arrow = { checked=ChkMode(arrow); a=<Key>F11; action=Mode(Arrow) }
+				ha:Insert Point = { checked=ChkMode(insertpoint); a=<Key>Insert; action=Mode(InsertPoint) }
+				ha:Move = { checked=ChkMode(move); action=Mode(Move) }
+				ha:Copy = { checked=ChkMode(copy); action=Mode(Copy) }
+				ha:Lock = { checked=ChkMode(lock); a=<Key>F12; action=Mode(Lock) }
+				ha:Cancel = { a=<Key>Escape; action=Mode(Cancel) }
+				-
+				ha:Command = { a={<Key>:}; action=Command() }
+			}
+		} # Tools
+
+		ha:Settings {
+			li:submenu {
+				ha:Layer groups = { foreground=grey50; sensitive=false }
+				ha:Edit layer groupings = { action=EditLayerGroups() }
+				-
+				ha:'All-direction' lines = { checked=editor/all_direction_lines; a=<Key>.; action=conf(toggle, editor/all_direction_lines, design) }
+				ha:Auto swap line start angle = { checked=editor/swap_start_direction; action=conf(toggle, editor/swap_start_direction, design) }
+				ha:Orthogonal moves = { checked=editor/orthogonal_moves; action=conf(toggle, editor/orthogonal_moves, design) }
+				ha:Crosshair snaps to pins and pads = { checked=editor/snap_pin; action=conf(toggle, editor/snap_pin, design) }
+				ha:Crosshair snaps to off-grid points on lines = { checked=editor/snap_offgrid_line; action=conf(toggle, editor/snap_offgrid_line, design) }
+				ha:Crosshair shows DRC clearance = { checked=editor/show_drc; action=conf(toggle, editor/show_drc, design) }
+				ha:Auto enforce DRC clearance = { checked=editor/auto_drc; action=conf(toggle, editor/auto_drc, design) }
+				-
+				ha:Rubber band mode = { checked=editor/rubber_band_mode; action=conf(toggle, editor/rubber_band_mode, design) }
+				ha:Require unique element names = { checked=editor/unique_names; action=conf(toggle, editor/unique_names, design) }
+				ha:Auto-zero delta measurements = { checked=editor/local_ref; action=conf(toggle, editor/local_ref, design) }
+				ha:New lines, arcs clear polygons = { checked=editor/clear_line; action=conf(toggle, editor/clear_line, design) }
+				ha:New polygons are full ones = { checked=editor/full_poly; action=conf(toggle, editor/full_poly, design) }
+				ha:Show autorouter trials = { checked=editor/live_routing; action=conf(toggle, editor/live_routing, design) }
+				ha:Highlighting on line, arc points = { checked=editor/highlight_on_point; action=conf(toggle, editor/highlight_on_point, design) }
+				ha:Thin draw = { checked=editor/thin_draw; a=<Key>|; action=conf(toggle, editor/thin_draw, design) }
+				ha:Thin draw poly = { checked=editor/thin_drawpoly; a=Ctrl Shift<Key>p; action=conf(toggle, editor/thin_draw_poly, design) }
+				ha:Check polygons = { checked=editor/check_planes; action=conf(toggle, editor/check_planes, design) }
+				-
+				ha:Pinout shows number = { checked=editor/show_number; action=conf(toggle, editor/show_number, design) }
+				ha:Pins/Via show Name/Number = { a=<Key>d; action=Display(PinOrPadName) }
+				ha:Enable vendor drill mapping = { checked=plugins/vendor/enable; action=conf(toggle, plugins/vendor/enable, design) }
+				ha:Import Settings {
+					li:submenu {
+						ha:New elements added at... = { foreground=grey50; sensitive=false }
+						ha: Center = { action=Import(setnewpoint,center) }
+						ha: Mark = { action=Import(setnewpoint,mark) }
+						ha: Crosshair = { action=Import(setnewpoint) }
+						-
+						ha:Set Dispersion = { action=Import(setdisperse) }
+					}
+				}
+			}
+		} # Settings
+
+		ha:Select {
+			li:submenu {
+				ha:Select all visible objects = { action=Select(All) }
+				ha:Select all connected objects = { action=Select(Connection) }
+				-
+				ha:Unselect all objects = { action=Unselect(All) }
+				ha:unselect all connected objects = { action=Unselect(Connection) }
+				-
+				ha:Select by name = { foreground=grey50; sensitive=false }
+				ha:All objects = { active=rc/have_regex; action=Select(ObjectByName) }
+				ha:Elements = { active=rc/have_regex; action=Select(ElementByName) }
+				ha:Pads = { active=rc/have_regex; action=Select(PadByName) }
+				ha:Pins = { active=rc/have_regex; action=Select(PinByName) }
+				ha:Text Objects = { active=rc/have_regex; action=Select(TextByName) }
+				ha:Vias = { active=rc/have_regex; action=Select(ViaByName) }
+				-
+				ha:Auto-place selected elements = { a=Ctrl<Key>p; action=AutoPlaceSelected() }
+				ha:Disperse all elements = { action=DisperseElements(All) }
+				ha:Move selected elements to other side = { a=Shift<Key>b; action=Flip(SelectedElements) }
+				ha:Move selected to current layer = { a=Shift<Key>m; action=MoveToCurrentLayer(Selected) }
+				ha:Delete selected objects = { a=<Key>Delete; action=Delete(Selected) }
+				ha:Convert selection to element = { action=Select(Convert) }
+				-
+				ha:Optimize selected rats = { li:action={DeleteRats(SelectedRats); AddRats(SelectedRats)} }
+				ha:Auto-route selected rats = { a=Alt<Key>r; action=AutoRoute(SelectedRats) }
+				ha:Rip up selected auto-routed tracks = { action=RipUp(Selected) }
+				-
+				ha:Change size of selected objects = { foreground=grey50; sensitive=false }
+				ha:ChangeSizes to Route style = { a=Shift<Key>y; action=ChangeSizes(Object,style,mil); }
+				ha:Lines -10 mil = { li:action={ChangeSize(SelectedLines,-10,mil); ChangeSize(SelectedArcs,-10,mil)} }
+				ha:Lines +10 mil = { li:action={ChangeSize(SelectedLines,+10,mil); ChangeSize(SelectedArcs,+10,mil)} }
+				ha:Pads -10 mil = { action=ChangeSize(SelectedPads,-10,mil) }
+				ha:Pads +10 mil = { action=ChangeSize(SelectedPads,+10,mil) }
+				ha:Pins -10 mil = { action=ChangeSize(SelectedPins,-10,mil) }
+				ha:Pins +10 mil = { action=ChangeSize(SelectedPins,+10,mil) }
+				ha:Texts -10 mil = { action=ChangeSize(SelectedTexts,-10,mil) }
+				ha:Texts +10 mil = { action=ChangeSize(SelectedTexts,+10,mil) }
+				ha:Vias -10 mil = { action=ChangeSize(SelectedVias,-10,mil) }
+				ha:Vias +10 mil = { action=ChangeSize(SelectedVias,+10,mil) }
+				-
+				ha:Change drilling hole of selected objects = { foreground=grey50; sensitive=false }
+				ha:Vias -10 mil = { action=ChangeDrillSize(SelectedVias,-10,mil) }
+				ha:Vias +10 mil = { action=ChangeDrillSize(SelectedVias,+10,mil) }
+				ha:Pins -10 mil = { action=ChangeDrillSize(SelectedPins,-10,mil) }
+				ha:Pins +10 mil = { action=ChangeDrillSize(SelectedPins,+10,mil) }
+				-
+				ha:Change square-flag of selected objects = { foreground=grey50; sensitive=false }
+				ha:Elements = { action=ChangeSquare(SelectedElements) }
+				ha:Pins = { action=ChangeSquare(SelectedPins) }
+			}
+		} # Select
+
+
+		ha:Buffer {
+			li:submenu {
+				ha:Copy selection to buffer = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Mode(PasteBuffer)} }
+				ha:Cut selection to buffer = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)} }
+				ha:Paste buffer to layout = { action=Mode(PasteBuffer) }
+				-
+				ha:Rotate buffer 90 deg CCW = { a=Shift<Key>F7; li:action={Mode(PasteBuffer); PasteBuffer(Rotate,1)} }
+				ha:Rotate buffer 90 deg CW = { li:action={Mode(PasteBuffer); PasteBuffer(Rotate,3)} }
+				ha:Arbitrarily Rotate Buffer = { li:action={Mode(PasteBuffer); FreeRotateBuffer()} }
+				ha:Mirror buffer (up/down) = { li:action={Mode(PasteBuffer); PasteBuffer(Mirror)} }
+				ha:Mirror buffer (left/right) = { li:action={Mode(PasteBuffer); PasteBuffer(Rotate,1); PasteBuffer(Mirror); PasteBuffer(Rotate,3)} }
+				-
+				ha:Clear buffer = { action=PasteBuffer(Clear) }
+				ha:Convert buffer to element = { action=PasteBuffer(Convert) }
+				ha:Break buffer elements to pieces = { action=PasteBuffer(Restore) }
+				ha:Save buffer elements to file = { action=Save(PasteBuffer) }
+				-
+				ha:Select current buffer = { foreground=grey50; sensitive=false }
+				ha:\#1 = { checked=ChkBuffer(1); a=Shift<Key>1; action=PasteBuffer(1) }
+				ha:\#2 = { checked=ChkBuffer(2); a=Shift<Key>2; action=PasteBuffer(2) }
+				ha:\#3 = { checked=ChkBuffer(3); a=Shift<Key>3; action=PasteBuffer(3) }
+				ha:\#4 = { checked=ChkBuffer(4); a=Shift<Key>4; action=PasteBuffer(4) }
+				ha:\#5 = { checked=ChkBuffer(5); a=Shift<Key>5; action=PasteBuffer(5) }
+			}
+		} # Buffer
+
+
+		ha:Connects {
+			li:submenu {
+				ha:Lookup connection to object = { a=Ctrl<Key>f; li:action={GetXY(Click on the object); Connection(Find)} }
+				ha:Reset scanned pads/pins/vias = { li:action={Connection(ResetPinsViasAndPads); Display(Redraw)} }
+				ha:Reset scanned lines/polygons = { li:action={Connection(ResetLinesAndPolygons); Display(Redraw)} }
+				ha:Reset all connections = { a=Shift<Key>f; li:action={Connection(Reset); Display(Redraw)} }
+				-
+				ha:Optimize rats-nest = { a=<Key>o; li:action={Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(AllRats); Atomic(Block)} }
+				ha:Erase rats-nest = { a=<Key>e; action=DeleteRats(AllRats) }
+				ha:Erase selected rats = { a=Shift<Key>e; action=DeleteRats(SelectedRats) }
+				-
+				ha:Auto-route selected rats = { action=AutoRoute(Selected) }
+				ha:Auto-route all rats = { action=AutoRoute(AllRats) }
+				ha:Rip up all auto-routed tracks = { action=RipUp(All) }
+				-
+				ha:Auto-Optimize = { a={Shift<Key>=}; action=djopt(auto) }
+				ha:Debumpify = { action=djopt(debumpify) }
+				ha:Unjaggy = { action=djopt(unjaggy) }
+				ha:Vianudge = { action=djopt(vianudge) }
+				ha:Viatrim = { action=djopt(viatrim) }
+				ha:Orthopull = { action=djopt(orthopull) }
+				ha:SimpleOpts = { a={<Key>=}; action=djopt(simple) }
+				ha:Miter = { action=djopt(miter) }
+				ha:Puller = { a=<Key>y; action=Puller() }
+				ha:Global Puller = {
+					li:submenu {
+						ha:Selected = { action=GlobalPuller(selected) }
+						ha:Found = { action=GlobalPuller(found) }
+						ha:All = { action=GlobalPuller() }
+					}
+				}
+				ha:Only autorouted nets = { checked=plugins/djopt/auto_only; action=conf(toggle, plugins/djopt/auto_only, design) }
+				-
+				ha:Design Rule Checker = { action=DRC() }
+				-
+				ha:Apply vendor drill mapping = { action=ApplyVendor() }
+			}
+		} # Connects
+
+		ha:Plugins = {
+			li:submenu {
+				ha:Manage plugins... = { a=Alt<Key>p; action=ManagePlugins() }
+			}
+		} # Plugins
+
+
+		ha:Info {
+			li:submenu {
+				ha:Generate object report = { a=Ctrl<Key>r; action=ReportObject() }
+				ha:Generate drill summary = { action=Report(DrillReport) }
+				ha:Report found pins/pads = { action=Report(FoundPins) }
+				ha:Report net length = { a=<Key>r; action=Report(NetLength) }
+				ha:Key Bindings {
+					li:submenu {
+						ha:Remove = { a=<Key>BackSpace; action=Delete(Selected) }
+						ha:Remove Connected = { a=Shift<Key>BackSpace; li:action={Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)} }
+						ha:Remove Connected = { a=Shift<Key>Delete; li:action={Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)} }
+						ha:Set Same = { a=<Key>a; action=SetSame() }
+						ha:Flip Object = { a=<Key>b; action=Flip(Object) }
+						ha:Find Connections = { a=<Key>f; li:action={Connection(Reset); Connection(Find)} }
+						ha:ToggleHideName Object = { a=<Key>h; action=ToggleHideName(Object) }
+						ha:ToggleHideName SelectedElement = { a=Shift<Key>h; action=ToggleHideName(SelectedElements) }
+						ha:ChangeHole Object = { a=Ctrl<Key>h; action=ChangeHole(Object) }
+						ha:ChangeJoin Object = { a=<Key>j; action=ChangeJoin(Object) }
+						ha:ChangeJoin SelectedObject = { a=Shift<Key>j; action=ChangeJoin(SelectedObjects) }
+						ha:Clear Object +2 mil = { a=<Key>k; action=ChangeClearSize(Object,+2,mil) }
+						ha:Clear Object -2 mil = { a=Shift<Key>k; action=ChangeClearSize(Object,-2,mil) }
+						ha:Clear Selected +2 mil = { a=Ctrl<Key>k; action=ChangeClearSize(SelectedObjects,+2,mil) }
+						ha:Clear Selected -2 mil = { a=Shift Ctrl<Key>k; action=ChangeClearSize(SelectedObjects,-2,mil) }
+						ha:Line Tool size +5 mil = { a=<Key>l; action=SetValue(LineSize,+5,mil) }
+						ha:Line Tool size -5 mil = { a=Shift<Key>l; action=SetValue(LineSize,-5,mil) }
+						ha:Move Object to current layer = { a=<Key>m; action=MoveToCurrentLayer(Object) }
+						ha:MarkCrosshair = { a=Ctrl<Key>m; action=MarkCrosshair() }
+						ha:Select shortest rat = { a=Shift<Key>n; action=AddRats(Close) }
+						ha:AddRats to selected pins = { a=Shift<Key>o; li:action={Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(SelectedRats); Atomic(Block)} }
+						ha:ChangeOctagon Object = { a=Ctrl<Key>o; action=ChangeOctagon(Object) }
+						ha:Polygon PreviousPoint = { a=<Key>p; action=Polygon(PreviousPoint) }
+						ha:Polygon Close = { a=Shift<Key>p; action=Polygon(Close) }
+						ha:ChangeSquare Object = { a=<Key>q; action=ChangeSquare(Object) }
+						ha:ChangeSize +5 mil = { a=<Key>s; action=ChangeSize(Object,+5,mil) }
+						ha:ChangeSize -5 mil = { a=Shift<Key>s; action=ChangeSize(Object,-5,mil) }
+						ha:ChangeDrill +5 mil = { a=Alt<Key>s; action=ChangeDrillSize(Object,+5,mil) }
+						ha:ChangeDrill -5 mil = { a=Alt Shift<Key>s; action=ChangeDrillSize(Object,-5,mil) }
+						ha:Text Tool scale +10 mil = { a=<Key>t; action=SetValue(TextScale,+10,mil) }
+						ha:Text Tool scale -10 mil = { a=Shift<Key>t; action=SetValue(TextScale,-10,mil) }
+						ha:Via Tool size +5 mil = { a=Shift<Key>v; action=SetValue(ViaSize,+5,mil) }
+						ha:Via Tool size -5 mil = { a=Shift Ctrl<Key>v; action=SetValue(ViaSize,-5,mil) }
+						ha:Via Tool drill +5 mil = { a=Alt<Key>v; action=SetValue(ViaDrillingHole,+5,mil) }
+						ha:Via Tool drill -5 mil = { a=Alt Shift<Key>v; action=SetValue(ViaDrillingHole,-5,mil) }
+						ha:AddRats Selected = { a=Shift<Key>w; action=AddRats(SelectedRats) }
+						ha:Add All Rats = { a=<Key>w; action=AddRats(AllRats) }
+						ha:Undo = { a=Alt<Key>z; action=Undo() }
+						ha:Cycle Clip = { a=<Key>/; action=Display(CycleClip) }
+						ha:Arrow = { a=<Key>space; checked=ChkMode(arrow); action=Mode(Arrow) }
+						ha:Temp Arrow ON = { a=<Key>[; li:action={Mode(Save); Mode(Arrow); Mode(Notify)} }
+						ha:Temp Arrow OFF = { a=<Key>]; li:action={Mode(Release); Mode(Restore)} }
+						ha:Step Up = { a=<Key>Up; action=Cursor(Warp,0,1,grid) }
+						ha:Step Down = { a=<Key>Down; action=Cursor(Warp,0,-1,grid) }
+						ha:Step Left = { a=<Key>Left; action=Cursor(Warp,-1,0,grid) }
+						ha:Step Right = { a=<Key>Right; action=Cursor(Warp,1,0,grid) }
+						ha:Step +Up = { a=Shift<Key>Up; action=Cursor(Pan,0,50,view) }
+						ha:Step +Down = { a=Shift<Key>Down; action=Cursor(Pan,0,-50,view) }
+						ha:Step +Left = { a=Shift<Key>Left; action=Cursor(Pan,-50,0,view) }
+						ha:Step +Right = { a=Shift<Key>Right; action=Cursor(Pan,50,0,view) }
+						ha:"Click" = { a=<Key>Enter; li:action={Mode(Notify); Mode(Release)} }
+						-
+						ha:layer keys {
+							li:submenu {
+								ha:Select Layer 1           = { a=<Key>1; action=SelectLayer(1) }
+								ha:Select Layer 2           = { a=<Key>2; action=SelectLayer(2) }
+								ha:Select Layer 3           = { a=<Key>3; action=SelectLayer(3) }
+								ha:Select Layer 4           = { a=<Key>4; action=SelectLayer(4) }
+								ha:Select Layer 5           = { a=<Key>5; action=SelectLayer(5) }
+								ha:Select Layer 6           = { a=<Key>6; action=SelectLayer(6) }
+								ha:Select Layer 7           = { a=<Key>7; action=SelectLayer(7) }
+								ha:Select Layer 8           = { a=<Key>8; action=SelectLayer(8) }
+								ha:Select Layer 9           = { a=<Key>9; action=SelectLayer(9) }
+								ha:Select Layer 10          = { a=<Key>0; action=SelectLayer(10) }
+								ha:Select Layer 11          = { a=Alt<Key>1; action=SelectLayer(11) }
+								ha:Select Layer 12          = { a=Alt<Key>2; action=SelectLayer(12) }
+								ha:Select Layer 13          = { a=Alt<Key>3; action=SelectLayer(13) }
+								ha:Select Layer 14          = { a=Alt<Key>4; action=SelectLayer(14) }
+								ha:Select Layer 15          = { a=Alt<Key>5; action=SelectLayer(15) }
+								ha:Select Layer 16          = { a=Alt<Key>6; action=SelectLayer(16) }
+								ha:Select Layer 17          = { a=Alt<Key>7; action=SelectLayer(17) }
+								ha:Select Layer 18          = { a=Alt<Key>8; action=SelectLayer(18) }
+								ha:Select Layer 19          = { a=Alt<Key>9; action=SelectLayer(19) }
+								ha:Select Layer 20          = { a=Alt<Key>0; action=SelectLayer(20) }
+								-
+								ha:Toggle Layer 1           = { a=Ctrl<Key>1; action=ToggleView(1) }
+								ha:Toggle Layer 2           = { a=Ctrl<Key>2; action=ToggleView(2) }
+								ha:Toggle Layer 3           = { a=Ctrl<Key>3; action=ToggleView(3) }
+								ha:Toggle Layer 4           = { a=Ctrl<Key>4; action=ToggleView(4) }
+								ha:Toggle Layer 5           = { a=Ctrl<Key>5; action=ToggleView(5) }
+								ha:Toggle Layer 6           = { a=Ctrl<Key>6; action=ToggleView(6) }
+								ha:Toggle Layer 7           = { a=Ctrl<Key>7; action=ToggleView(7) }
+								ha:Toggle Layer 8           = { a=Ctrl<Key>8; action=ToggleView(8) }
+								ha:Toggle Layer 9           = { a=Ctrl<Key>9; action=ToggleView(9) }
+								ha:Toggle Layer 10          = { a=Ctrl<Key>0; action=ToggleView(10) }
+								ha:Toggle Layer 11          = { a=Ctrl-Alt<Key>1; action=ToggleView(11) }
+								ha:Toggle Layer 12          = { a=Ctrl-Alt<Key>2; action=ToggleView(12) }
+								ha:Toggle Layer 13          = { a=Ctrl-Alt<Key>3; action=ToggleView(13) }
+								ha:Toggle Layer 14          = { a=Ctrl-Alt<Key>4; action=ToggleView(14) }
+								ha:Toggle Layer 15          = { a=Ctrl-Alt<Key>5; action=ToggleView(15) }
+								ha:Toggle Layer 16          = { a=Ctrl-Alt<Key>6; action=ToggleView(16) }
+								ha:Toggle Layer 17          = { a=Ctrl-Alt<Key>7; action=ToggleView(17) }
+								ha:Toggle Layer 18          = { a=Ctrl-Alt<Key>8; action=ToggleView(18) }
+								ha:Toggle Layer 19          = { a=Ctrl-Alt<Key>9; action=ToggleView(19) }
+								ha:Toggle Layer 20          = { a=Ctrl-Alt<Key>0; action=ToggleView(20) }
+							}
+						} # layer keys
+					}
+				} # Key Bindings
+			}
+		} # Info
+
+		ha:Window {
+			li:submenu {
+				ha:Board Layout = { action=DoWindows(Layout) }
+				ha:Library = { action=DoWindows(Library) }
+				ha:Message Log = { action=DoWindows(Log) }
+				ha:Netlist = { action=DoWindows(Netlist) }
+				ha:Pinout = { a=Shift<Key>d; action=Display(Pinout) }
+			}
+		} # Window
+
+	} # main_menu
+} #root
diff --git a/src/pcb-menu-mkey.lht b/src/pcb-menu-mkey.lht
new file mode 100644
index 0000000..1fefb33
--- /dev/null
+++ b/src/pcb-menu-mkey.lht
@@ -0,0 +1,604 @@
+ha:{
+	li:mouse {
+		li:left {
+			li:press            = { Mode(Notify) }
+			li:press-shift      = { Mode(Notify) }
+			li:press-ctrl       = { Mode(Save); Mode(None); Mode(Restore); Mode(Notify) }
+			li:press-shift-ctrl = { Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore) }
+			li:release          = { Mode(Release) }
+			li:release-shift    = { Mode(Release) }
+			li:release-ctrl     = { Mode(Release) }
+			li:release-shift-ctrl = { Mode(Release) }
+		}
+		li:right {
+			li:press         = { Pan(1) }
+			li:release       = { Pan(0) }
+			li:press-shift   = { Popup(popup1) }
+			li:press-ctrl    = { Display(CycleCrosshair) }
+		}
+		li:middle {
+			li:press               = { Mode(Stroke) }
+			li:release             = { Mode(Release) }
+			li:press-ctrl          = { Mode(Save); Mode(Copy); Mode(Notify) }
+			li:release-ctrl        = { Mode(Notify); Mode(Restore); }
+			li:press-shift-ctrl    = { Display(ToggleRubberbandMode); Mode(Save); Mode(Move); Mode(Notify) }
+			li:release-shift-ctrl  = { Mode(Notify); Mode(Restore); Display(ToggleRubberbandMode) }
+		}
+		li:scroll-up {
+			li:press        = { Zoom(0.8) }
+			li:press-shift  = { Scroll(up) }
+			li:press-ctrl   = { Scroll(left) }
+		}
+		li:scroll-down {
+			li:press       = { Zoom(1.25) }
+			li:press-shift = { Scroll(down) }
+			li:press-ctrl  = { Scroll(right) }
+		}
+# If you want zoom to center, do this instead.
+		#ha:scroll-up = { li:{} = {Zoom(0.8); Center()} }
+		#ha:scroll-down = { li:{} = {Zoom(1.25); Center()} }
+	}
+	
+	li:main_menu {
+		### File Menu
+		ha:File {
+			m=F
+			li:submenu {
+				ha:Save Layout                       = { m=S; li:a={{<key>f;<key>s}; {Ctrl<Key>s};};        action=Save(Layout); tip=Saves current layout }
+				ha:Save Layout As...                 = { m=A; li:a={{<key>f;<key>a}; {Shift Ctrl<Key>s};};  action=Save(LayoutAs); tip=Saves current layout into a new file }
+				-
+				ha:Revert                            = { a={<key>f;<key>r};          action=Load(Revert,none); tip=Revert to the layout stored on disk }
+				-
+				ha:Import Schematics                 = {                             action=Import() }
+				ha:Load layout                       = { a={<key>f;<key>o};          action=Load(Layout); tip=Load a layout from a file }
+				ha:Load element data to paste-buffer = {                             li:action={PasteBuffer(Clear); Load(ElementTobuffer)} }
+				ha:Load layout data to paste-buffer  = {                             li:action={PasteBuffer(Clear); Load(LayoutTobuffer)} }
+				ha:Load netlist file                 = {                             action=Load(Netlist) }
+				ha:Load vendor resource file         = {                             action=LoadVendorFrom() }
+				-
+				ha:Save connection data of {
+					li:submenu {
+						ha:a single element                  = {                  li:action={ GetXY(Click to set the element mark <>); Save(ElementConnections)}}
+						ha:all elements                      = {                  action=Save(AllConnections) }
+						ha:unused pins                       = {                  action=Save(AllUnusedPins) }
+						ha:netlist patch for back annotation = {a=Alt Ctrl<Key>b; action=SavePatch() }
+					}
+				}
+				-
+				ha:Print layout...      = { a={<key>f;<key>p}; action=Print()}
+				ha:Export layout...     = { a={<key>f;<key>i}; action=Export()}
+				ha:Maintenance {
+					li:submenu {
+						ha:Calibrate Printer...           = { action=PrintCalibrate() }
+						ha:Re-scan the footprint library  = { action=fp_rehash() }
+					}
+				}
+				-
+				ha:Start New Layout     = { li:a={{<key>f;<key>n}; {Ctrl<Key>n};}; action=New() }
+				-
+				ha:Preferences...       = {               action=DoWindows(Preferences)}
+				-
+				ha:Quit Program         = { li:a={{<key>f;<key>c}; {Ctrl<Key>q};}; action=Quit() }
+			}
+		}
+
+		ha:Edit {
+			m=E
+			li:submenu {
+				ha:Undo last operation          = { li:a={{<key>e; Shift<Key>u}; {Ctrl<Key>z}; {<Key>u};};        action=Undo() }
+				ha:Redo last undone operation   = { li:a={{<key>e; Shift<Key>r}; {Ctrl<Key>y}; {Shift<Key>r};};   action=Redo() }
+				ha:Clear undo-buffer            = { a=Shift Ctrl<Key>u; action=Undo(ClearList) }
+				-
+				ha:Cut selection to buffer      = { a=Ctrl<Key>x;                            li:action={ GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer) } }
+				ha:Copy selection to buffer     = { li:a={{<key>e; <key>c}; {Ctrl<Key>c;};}  li:action={ GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Unselect(All); Mode(PasteBuffer) } }
+				ha:Paste buffer to layout       = { a=Ctrl<Key>v;       action=Mode(PasteBuffer) }
+				-
+				ha:Unselect all                 = { a=Shift Alt<Key>a;  action=Unselect(All) }
+				ha:Select all visible           = { a=Alt<Key>a;        action=Select(All) }
+				-
+				ha:Edit name of {
+					li:submenu {
+						ha:text on layout             = { li:a={{<key>e;<key>x}; {<Key>n};}; action=ChangeName(Object) }
+						ha:pin on layout              = { a=Shift Ctrl<Key>n;                action=ChangeName(Object, Number) }
+						ha:layout                     = {                                    action=ChangeName(Layout) }
+						ha:active layer               = {                                    action=ChangeName(Layer) }
+					}
+				}
+				ha:Edit attributes of {
+					li:submenu {
+						ha:Layout                     = { action=Attributes(Layout) }
+						ha:CurrentLayer               = { action=Attributes(Layer) }
+						ha:Element                    = { action=Attributes(Element) }
+					}
+				}
+				ha:Change flags {
+					li:submenu {
+						ha:Nonetlist                  = { a=Alt<Key>n; action=ChangeNonetlist(Element) }
+					}
+				}
+				ha:Route Styles {
+					li:submenu {
+						@routestyles
+						-
+						ha:Edit...                    = { action=AdjustStyle(0) }
+					}
+				}
+			}
+		} # Edit
+
+		ha:View {
+			m=V
+			li:submenu {
+				ha:Enable visible grid        = { checked=editor/draw_grid; action=conf(toggle, editor/draw_grid, design) }
+				ha:Enable local grid          = { checked=plugins/hid_gtk/local_grid/enable; action=conf(toggle, plugins/hid_gtk/local_grid/enable, design) }
+				ha:Grid units {
+					li:submenu {
+						ha:mil                    = { checked=ChkGridUnits(mil); action=SetUnits(mil); update_on={editor/grid_unit} }
+						ha:mm                     = { checked=ChkGridUnits(mm); action=SetUnits(mm); update_on={editor/grid_unit} }
+					}
+				}
+				ha:Grid size = {
+					li:submenu {
+						ha:No Grid                = { checked=ChkGridSize(none); action=SetValue(Grid,1); update_on={editor/grid} }
+						-
+						ha:0.1 mil                = { checked=ChkGridSize(0.1mil); li:action={SetUnits(mil); SetValue(Grid,0.1mil)}; update_on={editor/grid} }
+						ha:1 mil                  = { checked=ChkGridSize(1mil); li:action={SetUnits(mil); SetValue(Grid,1mil)}; update_on={editor/grid} }
+						ha:5 mil                  = { checked=ChkGridSize(5mil); li:action={SetUnits(mil); SetValue(Grid,5mil)}; update_on={editor/grid} }
+						ha:10 mil                 = { checked=ChkGridSize(10mil); li:action={SetUnits(mil); SetValue(Grid,10mil)}; update_on={editor/grid} }
+						ha:25 mil                 = { checked=ChkGridSize(25mil); li:action={SetUnits(mil); SetValue(Grid,25mil)}; update_on={editor/grid} }
+						ha:50 mil                 = { checked=ChkGridSize(50mil); li:action={SetUnits(mil); SetValue(Grid,50mil)}; update_on={editor/grid} }
+						ha:100 mil                = { checked=ChkGridSize(100mil); li:action={SetUnits(mil); SetValue(Grid,100mil)}; update_on={editor/grid} }
+						-
+						ha:0.01 mm                = { checked=ChkGridSize(0.01mm); li:action={SetUnits(mm); SetValue(Grid,0.01mm)}; update_on={editor/grid} }
+						ha:0.05 mm                = { checked=ChkGridSize(0.05mm); li:action={SetUnits(mm); SetValue(Grid,0.05mm)}; update_on={editor/grid} }
+						ha:0.1 mm                 = { checked=ChkGridSize(0.10mm); li:action={SetUnits(mm); SetValue(Grid,0.1mm)}; update_on={editor/grid} }
+						ha:0.25 mm                = { checked=ChkGridSize(0.25mm); li:action={SetUnits(mm); SetValue(Grid,0.25mm)}; update_on={editor/grid} }
+						ha:0.5 mm                 = { checked=ChkGridSize(0.50mm); li:action={SetUnits(mm); SetValue(Grid,0.5mm)}; update_on={editor/grid} }
+						ha:1 mm                   = { checked=ChkGridSize(1mm); li:action={SetUnits(mm); SetValue(Grid,1mm)}; update_on={editor/grid} }
+						-
+						ha:Grid -5mil             = { li:a={{<key>[};{Shift<Key>g};}; action=SetValue(Grid,-5,mil) }
+						ha:Grid +5mil             = { li:a={{<key>]};{<Key>g};};  action=SetValue(Grid,+5,mil) }
+						ha:Grid -0.05mm           = { a=Shift Ctrl<Key>g; action=SetValue(Grid,-0.05,mm) }
+						ha:Grid +0.05mm           = { a=Ctrl<Key>g; action=SetValue(Grid,+0.05,mm) }
+					}
+				}
+				ha:Grid properties = {
+					li:submenu {
+						ha:Enable local grid          = { checked=plugins/hid_gtk/local_grid/enable; action=conf(toggle, plugins/hid_gtk/local_grid/enable, design) }
+						-
+						ha:local grid radius 4   =  { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 4)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 4, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) } }
+						ha:local grid radius 8    = { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 8)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 8, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) } }
+						ha:local grid radius 16   = { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 16)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 16, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) } }
+						ha:local grid radius 32   = { checked={conf(iseq, plugins/hid_gtk/local_grid/radius, 32)}; li:action={conf(set, plugins/hid_gtk/local_grid/radius, 32, design); conf(set, plugins/hid_gtk/local_grid/enable, 1, design) } }
+						-
+						ha:sparse global grid     = { checked=plugins/hid_gtk/global_grid/sparse; action=conf(toggle, plugins/hid_gtk/global_grid/sparse, design) }
+						ha:global grid density 4  = { checked={conf(iseq, plugins/hid_gtk/global_grid/min_dist_px, 4)}; li:action={conf(set, plugins/hid_gtk/global_grid/min_dist_px, 4, design); conf(set, plugins/hid_gtk/local_grid/enable, 0, design) } }
+						ha:global grid density 8  = { checked={conf(iseq, plugins/hid_gtk/global_grid/min_dist_px, 8)}; li:action={conf(set, plugins/hid_gtk/global_grid/min_dist_px, 8, design); conf(set, plugins/hid_gtk/local_grid/enable, 0, design) } }
+						ha:global grid density 16 = { checked={conf(iseq, plugins/hid_gtk/global_grid/min_dist_px, 16)}; li:action={conf(set, plugins/hid_gtk/global_grid/min_dist_px, 16, design); conf(set, plugins/hid_gtk/local_grid/enable, 0, design) } }
+					}
+				}
+				ha:Realign grid               = { li:action={GetXY(Click to set the grid origin); Display(ToggleGrid) } }
+				-
+				ha:Displayed element name {
+					li:submenu {
+						ha:Description            = { checked=ChkElementName(1); action=Display(Description); update_on={editor/description} }
+						ha:Reference Designator   = { checked=ChkElementName(2); action=Display(NameOnPCB); update_on={editor/name_on_pcb} }
+						ha:Value                  = { checked=ChkElementName(3); action=Display(Value); update_on={editor/name_on_pcb} }
+					}
+				}
+				ha:Enable Pinout shows number = { checked=editor/show_number; action=conf(toggle, editor/show_number, design) }
+				ha:Pins/Via show Name/Number  = { a=<Key>d; action=Display(PinOrPadName) }
+				ha:Zoom In 20%                = { m=Z; a=<Key>z; action=Zoom(-1.2) }
+				ha:Zoom Out 20%               = { m=O; a=Shift<Key>z; action=Zoom(+1.2) }
+				ha:More zooms and view changes {
+					li:submenu {
+						ha:Zoom Max               = { li:a={{<key>v;<key>f}; {<key>v;<key>e};}; m=M; action=Zoom() }
+						ha:Zoom In 2X             = { a={<key>v;<key>i}; action=Zoom(-2) }
+						ha:Zoom Out 2X            = { a={<key>v;<key>o}; action=Zoom(+2) }
+						ha:Zoom to 0.1mil/px      = { action={Zoom(=0.1mil)} }
+						ha:Zoom to 0.01mm/px      = { action={Zoom(=0.01mm)} }
+						ha:Zoom to 1mil/px        = { action={Zoom(=1mil)} }
+						ha:Zoom to 0.05mm/px      = { action={Zoom(=0.05mm)} }
+						ha:Zoom to 2.5mil/px      = { action={Zoom(=2.5mil)} }
+						ha:Zoom to 0.1mm/px       = { action={Zoom(=0.1mm)} }
+						ha:Zoom to 10mil/px       = { action={Zoom(=10mil)} }
+						ha:Zoom In 20% and center = { m=Z; li:action={Zoom(-1.2); Center()} }
+						ha:Zoom Out 20% and center= { m=O; li:action={Zoom(+1.2); Center()} }
+						ha:Flip up/down           = { checked=editor/view/flip_y; a=<Key>Tab; action=SwapSides(V) }
+						ha:Flip left/right        = { checked=editor/view/flip_x; a=Shift<Key>Tab; action=SwapSides(H) }
+						ha:Spin 180 degrees       = { a=Ctrl<Key>Tab; action=SwapSides(R) }
+						ha:Swap Sides             = { a=Ctrl Shift<Key>Tab; action=SwapSides() }
+						ha:Center cursor          = { a=<Key>c; action=Center() }
+					}
+				}
+				-
+				ha:Shown Layers {
+					li:submenu {
+						@layerview
+						-
+						ha:Edit Layer Groups      = { action=EditLayerGroups() }
+					}
+				}
+				ha:Current Layer {
+					li:submenu {
+						anon2=@layerpick
+						-
+						ha:Delete current layer   = { action=MoveLayer(c,-1) }
+						ha:Add new layer          = { action=MoveLayer(-1,c) }
+						ha:Move current layer up  = { action=MoveLayer(c,up) }
+						ha:Move current layer down= { action=MoveLayer(c,down) }
+					}
+				}
+				ha:Full screen                = { checked=editor/fullscreen; a=<Key>\\;  action=fullscreen(toggle) }
+			}
+		} # View
+
+		ha:Settings = {
+			m=S
+			li:submenu {
+				ha:'All-direction' lines            = { checked=editor/alldirection_lines; a=<Key>.; action=conf(toggle, editor/all_direction_lines, design) }
+				ha:Auto swap line start angle       = { checked=editor/swap_start_direction; action=conf(toggle, editor/swap_start_direction, design) }
+				ha:Orthogonal moves                 = { checked=editor/orthogonal_moves; action=conf(toggle, editor/orthogonal_moves, design) }
+				ha:Crosshair snaps to pins and pads = { checked=editor/snap_pin; action=conf(toggle, editor/snap_pin, design) }
+				ha:Crosshair snaps to off-grid points on lines = { checked=editor/snap_offgrid_line; action=conf(toggle, editor/snap_offgrid_line, design) }
+				ha:Crosshair shows DRC clearance    = { checked=editor/show_drc; action=conf(toggle, editor/show_drc, design) }
+				ha:Auto enforce DRC clearance       = { checked=editor/auto_drc; action=conf(toggle, editor/auto_drc, design) }
+				ha:Lock Names                       = { checked=editor/lock_names; action=conf(toggle, editor/lock_names, design) }
+				ha:Only Names                       = { checked=editor/only_names; action=conf(toggle, editor/only_names, design) }
+				ha:Hide Names                       = { checked=editor/hide_names; action=conf(toggle, editor/hide_names, design) }
+				ha:Mincut on shorts                 = { checked=enablemincut; action=Display(ToggleMinCut) }
+				ha:Libstroke gestures on middle button = { checked=editor/enable_stroke; action=conf(toggle, editor/enable_stroke, design) }
+				-
+				ha:Rubber band mode                 = { checked=editor/rubber_band_mode; action=conf(toggle, editor/rubber_band_mode, design) }
+				ha:Require unique element names     = { checked=editor/unique_names; action=conf(toggle, editor/unique_names, design) }
+				ha:Auto-zero delta measurements     = { checked=editor/local_ref; action=conf(toggle, editor/local_ref, design) }
+				ha:New lines, arcs clear polygons   = { checked=editor/clear_line; action=conf(toggle, editor/clear_line, design) }
+				ha:New polygons are full ones       = { checked=editor/full_poly; action=conf(toggle, editor/full_poly, design) }
+				ha:Show autorouter trials           = { checked=editor/live_routing; action=conf(toggle, editor/live_routing, design) }
+				ha:Highlighting on line, arc points = { checked=editor/highlight_on_point; action=conf(toggle, editor/highlight_on_point, design) }
+				ha:Thin draw                        = { checked=editor/thin_draw; a=<Key>|;  action=conf(toggle, editor/thin_draw, design) }
+				ha:Thin draw poly                   = { checked=editor/thin_drawpoly; a=Ctrl Shift<Key>p; action=conf(toggle, editor/thin_draw_poly, design) }
+				ha:Check polygons                   = { checked=editor/check_planes; action=conf(toggle, editor/check_planes, design) }
+				-
+				ha:Vendor drill mapping             = { checked=plugins/vendor/enable; action=conf(toggle, plugins/vendor/enable, design) }
+				ha:Import New Elements at = {
+					m=I
+					li:submenu {
+						ha:Center          = { m=C; action=Import(setnewpoint,center) }
+						ha:Mark            = { m=M; action=Import(setnewpoint,mark) }
+						ha:Crosshair       = { m=h; action=Import(setnewpoint) }
+						-
+						ha:Set Dispersion  = { m=D; action=Import(setdisperse) }
+					}
+				}
+			}
+		} #Settings
+
+		ha:Select {
+			m=l
+			li:submenu {
+				ha:Select all visible objects      = { action=Select(All) }
+				ha:Select all connected objects    = { action=Select(Connection) }
+				-
+				ha:Unselect all objects            = { action=Unselect(All) }
+				ha:unselect all connected objects  = { action=Unselect(Connection) }
+				-
+				ha:Select by name {
+					li:submenu {
+						ha:All objects           = { active=rc/have_regex; action=Select(ObjectByName) }
+						ha:Elements              = { active=rc/have_regex; action=Select(ElementByName) }
+						ha:Pads                  = { active=rc/have_regex; action=Select(PadByName) }
+						ha:Pins                  = { active=rc/have_regex; action=Select(PinByName) }
+						ha:Text                  = { active=rc/have_regex; action=Select(TextByName) }
+						ha:Vias                  = { active=rc/have_regex; action=Select(ViaByName) }
+					}
+				}
+				-
+				ha:Auto-place selected elements          = { a=Ctrl<Key>p; action=AutoPlaceSelected() }
+				ha:Disperse all elements                 = { action=DisperseElements(All) }
+				ha:Disperse selected elements            = { action=DisperseElements(Selected) }
+				-
+				ha:Move selected elements to other side  = { a=Shift<Key>b; action=Flip(SelectedElements) }
+				ha:Move selected to current layer        = { a=Shift<Key>m; action=MoveToCurrentLayer(Selected) }
+				ha:Remove selected objects               = { a=Shift<Key>Delete; action=RemoveSelected() }
+				ha:Convert selection to element          = { action=Select(Convert) }
+				-
+				ha:Optimize selected rats                = { li:action={DeleteRats(SelectedRats); AddRats(SelectedRats) } }
+				ha:Auto-route selected rats              = { a=Alt<Key>r; action=AutoRoute(SelectedRats) }
+				ha:Rip up selected auto-routed tracks    = { action=RipUp(Selected) }
+				-
+				ha:Change size of selected objects {
+					li:submenu {
+						ha:Lines -10 mil = { li:action={ChangeSize(SelectedLines,-10,mil); ChangeSize(SelectedArcs,-10,mil)} }
+						ha:Lines +10 mil = { li:action={ChangeSize(SelectedLines,+10,mil); ChangeSize(SelectedArcs,+10,mil)} }
+						ha:Pads -10 mil  = { action=ChangeSize(SelectedPads,-10,mil) }
+						ha:Pads +10 mil  = { action=ChangeSize(SelectedPads,+10,mil) }
+						ha:Pins -10 mil  = { action=ChangeSize(SelectedPins,-10,mil) }
+						ha:Pins +10 mil  = { action=ChangeSize(SelectedPins,+10,mil) }
+						ha:Texts -10 mil = { action=ChangeSize(SelectedTexts,-10,mil) }
+						ha:Texts +10 mil = { action=ChangeSize(SelectedTexts,+10,mil) }
+						ha:Vias -10 mil  = { action=ChangeSize(SelectedVias,-10,mil) }
+						ha:Vias +10 mil  = { action=ChangeSize(SelectedVias,+10,mil) }
+					}
+				}
+				-
+				ha:Change drilling hole of selected objects {
+					li:submenu {
+						ha:Vias -10 mil = { action=ChangeDrillSize(SelectedVias,-10,mil) }
+						ha:Vias +10 mil = { action=ChangeDrillSize(SelectedVias,+10,mil) }
+						ha:Pins -10 mil = { action=ChangeDrillSize(SelectedPins,-10,mil) }
+						ha:Pins +10 mil = { action=ChangeDrillSize(SelectedPins,+10,mil) }
+					}
+				}
+				-
+				ha:Change square-flag of selected objects {
+					li:submenu {
+						ha:Elements  = { action=ChangeSquare(SelectedElements) }
+						ha:Pins      = { action=ChangeSquare(SelectedPins) }
+					}
+				}
+				ha:Cycle object being dragged = { a=<Key>x; action=CycleDrag() }
+			}
+		} # Select
+
+		ha:Buffer {
+			m=B
+			li:submenu {
+				ha:Cut selection to buffer = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)} }
+				ha:Paste buffer to layout  = { action=Mode(PasteBuffer) }
+				-
+				ha:Rotate buffer 90 deg CCW   = { a={{<key>e; <key>r}; {Shift<Key>F7};}; li:action={Mode(PasteBuffer); PasteBuffer(Rotate,1)} }
+				ha:Rotate buffer 90 deg CW    = { li:action={Mode(PasteBuffer); PasteBuffer(Rotate,3)} }
+				ha:Arbitrarily Rotate Buffer  = { li:action={Mode(PasteBuffer); FreeRotateBuffer()} }
+				ha:Mirror buffer (up/down)    = { a={<key>e; <key>i}; li:action={Mode(PasteBuffer); PasteBuffer(Mirror)} }
+				ha:Mirror buffer (left/right) = { li:action={Mode(PasteBuffer); PasteBuffer(Rotate,1); PasteBuffer(Mirror); PasteBuffer(Rotate,3)} }
+				-
+				ha:Clear buffer                    = { action=PasteBuffer(Clear) }
+				ha:Convert buffer to element       = { action=PasteBuffer(Convert) }
+				ha:Break buffer elements to pieces = { action=PasteBuffer(Restore) }
+				ha:Save buffer elements to file    = { action=Save(PasteBuffer) }
+				-
+				ha:Select Buffer \#1 = { checked=ChkBuffer(1); m=1; a=Shift<Key>1; action=PasteBuffer(1); update_on={editor/buffer_number} }
+				ha:Select Buffer \#2 = { checked=ChkBuffer(2); m=2; a=Shift<Key>2; action=PasteBuffer(2); update_on={editor/buffer_number} }
+				ha:Select Buffer \#3 = { checked=ChkBuffer(3); m=3; a=Shift<Key>3; action=PasteBuffer(3); update_on={editor/buffer_number} }
+				ha:Select Buffer \#4 = { checked=ChkBuffer(4); m=4; a=Shift<Key>4; action=PasteBuffer(4); update_on={editor/buffer_number} }
+				ha:Select Buffer \#5 = { checked=ChkBuffer(5); m=5; a=Shift<Key>5; action=PasteBuffer(5); update_on={editor/buffer_number} }
+			}
+		} # Buffer
+
+		ha:Connects = {
+			m=C
+			li:submenu {
+				ha:Lookup connection to object   = { a=Ctrl<Key>f; li:action={GetXY(Click on the object); Connection(Find)} }
+				ha:Reset scanned pads/pins/vias  = { li:action={Connection(ResetPinsViasAndPads); Display(Redraw)} }
+				ha:Reset scanned lines/polygons  = { li:action={Connection(ResetLinesAndPolygons); Display(Redraw)} }
+				ha:Reset all connections         = { a=Shift<Key>f; li:action={Connection(Reset); Display(Redraw)} }
+				-
+				ha:Optimize rats nest            = { a=<Key>o; li:action={Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(AllRats); Atomic(Block)} }
+				ha:Erase rats nest               = { action=DeleteRats(AllRats) }
+				ha:Erase selected rats           = { a=Shift<Key>e; action=DeleteRats(SelectedRats) }
+				-
+				ha:Auto-route selected rats      = { action=AutoRoute(Selected) }
+				ha:Auto-route all rats           = { action=AutoRoute(AllRats) }
+				ha:Rip up all auto-routed tracks = { action=RipUp(All) }
+				-
+				ha:Optimize routed tracks {
+					li:submenu {
+						ha:Auto-Optimize        = { a={Shift<Key>=}; action=djopt(auto) }
+						ha:Debumpify            = { action=djopt(debumpify) }
+						ha:Unjaggy              = { action=djopt(unjaggy) }
+						ha:Vianudge             = { action=djopt(vianudge) }
+						ha:Viatrim              = { action=djopt(viatrim) }
+						ha:Ortho pull           = { action=djopt(orthopull) }
+						ha:Simple optimization  = { a={<Key>=}; action=djopt(simple) }
+						ha:Miter                = { action=djopt(miter) }
+						ha:Puller               = { a=<Key>y; action=Puller() }
+						ha:Global Puller {
+							li:submenu {
+								ha:Selected  = { action=GlobalPuller(selected) }
+								ha:Found     = { action=GlobalPuller(found) }
+								ha:All       = { action=GlobalPuller() }
+							}
+						}
+						-
+						ha:Only autorouted nets = { checked=plugins/djopt/auto_only; action=conf(toggle, plugins/djopt/auto_only, design) }
+					}
+				}
+				-
+				ha:Design Rule Checker = { action=DRC() }
+				-
+				ha:Apply vendor drill mapping = { action=ApplyVendor() }
+				-
+				ha:Design changes (back annotation) {
+					li:submenu {
+						ha:Swap nets on two selected pins  = { a=Shift<Key>x; action=net(swap) }
+						ha:Replace footprint               = { a=Alt Shift<Key>f; action=ReplaceFootprint() }
+					}
+				}
+			}
+		} # Connects
+	
+		ha:Plugins {
+			m=P
+			li:submenu {
+				ha:Manage plugins... = { a=Alt<Key>p; action=ManagePlugins() }
+			}
+		} # Plugins
+
+		ha:Info = {
+			m=I
+			li:submenu {
+				ha:Generate object report  = { a=Ctrl<Key>r; action=ReportObject() }
+				ha:Generate drill summary  = { action=Report(DrillReport) }
+				ha:Report found pins\/pads = { action=Report(FoundPins) }
+				ha:Key Bindings {
+					li:submenu {
+						ha:Remove                       = { li:a={{<key>e;<key>d};{<Key>Delete};}; li:action={Mode(Save); Mode(Remove); Mode(Notify); Mode(Restore)} }
+						ha:Remove Selected              = { a=<Key>BackSpace; action=RemoveSelected() }
+						ha:Remove Connected             = { a=Shift<Key>BackSpace; li:action={Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)} }
+						ha:Remove Connected             = { li:action={Atomic(Save); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Restore); Connection(Find); Atomic(Restore); Select(Connection); Atomic(Restore); RemoveSelected(); Atomic(Restore); Connection(Reset); Atomic(Restore); Unselect(All); Atomic(Block)} }
+						ha:Set Same                     = { action=SetSame() }
+						ha:Flip Object                  = { a=<Key>b; action=Flip(Object) }
+						ha:Find Connections             = { li:action={Connection(Reset); Connection(Find)} }
+						ha:ToggleHideName Object        = { action=ToggleHideName(Object) }
+						ha:ToggleHideName SelectedElement = { a=Shift<Key>h; action=ToggleHideName(SelectedElements) }
+						ha:ChangeHole Object            = { a=Ctrl<Key>h; action=ChangeHole(Object) }
+						ha:ChangeJoin Object            = { a=<Key>j; action=ChangeJoin(Object) }
+						ha:ChangeJoin SelectedObject    = { a=Shift<Key>j; action=ChangeJoin(SelectedObjects) }
+						ha:Clear Object +2 mil          = { a=<Key>k; action=ChangeClearSize(Object,+2,mil) }
+						ha:Clear Object -2 mil          = { a=Shift<Key>k; action=ChangeClearSize(Object,-2,mil) }
+						ha:Clear Selected +2 mil        = { a=Ctrl<Key>k; action=ChangeClearSize(SelectedObjects,+2,mil) }
+						ha:Clear Selected -2 mil        = { a=Shift Ctrl<Key>k; action=ChangeClearSize(SelectedObjects,-2,mil) }
+						ha:Line Tool size +5 mil        = { a=<Key>l; action=SetValue(LineSize,+5,mil) }
+						ha:Line Tool size -5 mil        = { a=Shift<Key>l; action=SetValue(LineSize,-5,mil) }
+						ha:Move Object to current layer = { a=<Key>m; action=MoveToCurrentLayer(Object) }
+						ha:MarkCrosshair                = { a=Ctrl<Key>m; action=MarkCrosshair() }
+						ha:Select shortest rat          = { a=Shift<Key>n; action=AddRats(Close) }
+						ha:AddRats to selected pins     = { a=Shift<Key>o; li:action={Atomic(Save); DeleteRats(AllRats); Atomic(Restore); AddRats(SelectedRats); Atomic(Block)} }
+						ha:ChangeOctagon Object         = { a=Ctrl<Key>o; action=ChangeOctagon(Object) }
+						ha:Polygon PreviousPoint        = { a=<Key>p; action=Polygon(PreviousPoint) }
+						ha:Polygon Close                = { a=Shift<Key>p; action=Polygon(Close) }
+						ha:ChangeSquare Object          = { a=<Key>q; action=ChangeSquare(ToggleObject) }
+						ha:ChangeSizes to Route style   = { a=Shift<Key>y; action=ChangeSizes(Object,style,mil) }
+						ha:ChangeSize +5 mil            = { a=<Key>s; action=ChangeSize(Object,+5,mil) }
+						ha:ChangeSize -5 mil            = { a=Shift<Key>s; action=ChangeSize(Object,-5,mil) }
+						ha:ChangeDrill +5 mil           = { a=Alt<Key>s; action=ChangeDrillSize(Object,+5,mil) }
+						ha:ChangeDrill -5 mil           = { a=Alt Shift<Key>s; action=ChangeDrillSize(Object,-5,mil) }
+						ha:Text Tool scale +10 mil      = { a=<Key>t; action=SetValue(TextScale,+10,mil) }
+						ha:Text Tool scale -10 mil      = { a=Shift<Key>t; action=SetValue(TextScale,-10,mil) }
+						ha:Via Tool size +5 mil         = { a=Shift<Key>v; action=SetValue(ViaSize,+5,mil) }
+						ha:Via Tool size -5 mil         = { a=Shift Ctrl<Key>v; action=SetValue(ViaSize,-5,mil) }
+						ha:Via Tool drill +5 mil        = { a=Alt<Key>v; action=SetValue(ViaDrillingHole,+5,mil) }
+						ha:Via Tool drill -5 mil        = { a=Alt Shift<Key>v; action=SetValue(ViaDrillingHole,-5,mil) }
+						ha:AddRats Selected             = { a=Shift<Key>w; action=AddRats(SelectedRats) }
+						ha:Add All Rats                 = { a=<Key>w; action=AddRats(AllRats) }
+						ha:Cycle Clip                   = { a=<Key>/; action=Display(CycleClip) }
+						ha:Arrow Mode                   = { a=<Key>space; checked=ChkMode(arrow); action=Mode(Arrow); update_on={editor/mode} }
+						ha:Temp Arrow ON                = { li:action={Mode(Save); Mode(Arrow); Mode(Notify)} }
+						ha:Temp Arrow OFF               = { li:action={Mode(Release); Mode(Restore)} }
+						-
+						ha:Step Up                      = { a=<Key>Up; action=Cursor(Warp,0,1,grid) }
+						ha:Step Down                    = { a=<Key>Down; action=Cursor(Warp,0,-1,grid) }
+						ha:Step Left                    = { a=<Key>Left; action=Cursor(Warp,-1,0,grid) }
+						ha:Step Right                   = { a=<Key>Right; action=Cursor(Warp,1,0,grid) }
+						ha:Step +Up                     = { a=Shift<Key>Up; action=Cursor(Pan,0,50,view) }
+						ha:Step +Down                   = { a=Shift<Key>Down; action=Cursor(Pan,0,-50,view) }
+						ha:Step +Left                   = { a=Shift<Key>Left; action=Cursor(Pan,-50,0,view) }
+						ha:Step +Right                  = { a=Shift<Key>Right; action=Cursor(Pan,50,0,view) }
+						ha:Click                        = { a=<Key>Enter; li:action={Mode(Notify); Mode(Release)} }
+						-
+						ha:layer keys {
+							li:submenu {
+								ha:Select Layer 1           = { a=<Key>1; action=SelectLayer(1) }
+								ha:Select Layer 2           = { a=<Key>2; action=SelectLayer(2) }
+								ha:Select Layer 3           = { a=<Key>3; action=SelectLayer(3) }
+								ha:Select Layer 4           = { a=<Key>4; action=SelectLayer(4) }
+								ha:Select Layer 5           = { a=<Key>5; action=SelectLayer(5) }
+								ha:Select Layer 6           = { a=<Key>6; action=SelectLayer(6) }
+								ha:Select Layer 7           = { a=<Key>7; action=SelectLayer(7) }
+								ha:Select Layer 8           = { a=<Key>8; action=SelectLayer(8) }
+								ha:Select Layer 9           = { a=<Key>9; action=SelectLayer(9) }
+								ha:Select Layer 10          = { a=<Key>0; action=SelectLayer(10) }
+								ha:Select Layer 11          = { a=Alt<Key>1; action=SelectLayer(11) }
+								ha:Select Layer 12          = { a=Alt<Key>2; action=SelectLayer(12) }
+								ha:Select Layer 13          = { a=Alt<Key>3; action=SelectLayer(13) }
+								ha:Select Layer 14          = { a=Alt<Key>4; action=SelectLayer(14) }
+								ha:Select Layer 15          = { a=Alt<Key>5; action=SelectLayer(15) }
+								ha:Select Layer 16          = { a=Alt<Key>6; action=SelectLayer(16) }
+								ha:Select Layer 17          = { a=Alt<Key>7; action=SelectLayer(17) }
+								ha:Select Layer 18          = { a=Alt<Key>8; action=SelectLayer(18) }
+								ha:Select Layer 19          = { a=Alt<Key>9; action=SelectLayer(19) }
+								ha:Select Layer 20          = { a=Alt<Key>0; action=SelectLayer(20) }
+								-
+								ha:Toggle Layer 1           = { a=Ctrl<Key>1; action=ToggleView(1) }
+								ha:Toggle Layer 2           = { a=Ctrl<Key>2; action=ToggleView(2) }
+								ha:Toggle Layer 3           = { a=Ctrl<Key>3; action=ToggleView(3) }
+								ha:Toggle Layer 4           = { a=Ctrl<Key>4; action=ToggleView(4) }
+								ha:Toggle Layer 5           = { a=Ctrl<Key>5; action=ToggleView(5) }
+								ha:Toggle Layer 6           = { a=Ctrl<Key>6; action=ToggleView(6) }
+								ha:Toggle Layer 7           = { a=Ctrl<Key>7; action=ToggleView(7) }
+								ha:Toggle Layer 8           = { a=Ctrl<Key>8; action=ToggleView(8) }
+								ha:Toggle Layer 9           = { a=Ctrl<Key>9; action=ToggleView(9) }
+								ha:Toggle Layer 10          = { a=Ctrl<Key>0; action=ToggleView(10) }
+								ha:Toggle Layer 11          = { a=Ctrl-Alt<Key>1; action=ToggleView(11) }
+								ha:Toggle Layer 12          = { a=Ctrl-Alt<Key>2; action=ToggleView(12) }
+								ha:Toggle Layer 13          = { a=Ctrl-Alt<Key>3; action=ToggleView(13) }
+								ha:Toggle Layer 14          = { a=Ctrl-Alt<Key>4; action=ToggleView(14) }
+								ha:Toggle Layer 15          = { a=Ctrl-Alt<Key>5; action=ToggleView(15) }
+								ha:Toggle Layer 16          = { a=Ctrl-Alt<Key>6; action=ToggleView(16) }
+								ha:Toggle Layer 17          = { a=Ctrl-Alt<Key>7; action=ToggleView(17) }
+								ha:Toggle Layer 18          = { a=Ctrl-Alt<Key>8; action=ToggleView(18) }
+								ha:Toggle Layer 19          = { a=Ctrl-Alt<Key>9; action=ToggleView(19) }
+								ha:Toggle Layer 20          = { a=Ctrl-Alt<Key>0; action=ToggleView(20) }
+							}
+						} # layer keys
+					}
+				}
+			}
+		} # Info
+
+		ha:Window {
+			m=W
+			li:submenu {
+				ha:Library        = { a={<Key>a;<Key>c} action=DoWindows(Library) }
+				ha:Message Log    = { action=DoWindows(Log) }
+				ha:DRC Check      = { action=DoWindows(DRC) }
+				ha:Netlist        = { action=DoWindows(Netlist) }
+				ha:Command Entry  = { a={<Key>:}; action=Command() }
+				ha:Pinout         = { a=Shift<Key>d; action=Display(Pinout) }
+				-
+				ha:About...       = { a={<key>h;<key>a}; action=About() }
+			}
+		} # Window
+	} # main menu
+
+	li:popups {
+		ha:popup1 {
+			li:submenu {
+				ha:Operations on selections {
+					li:submenu {
+						ha:Unselect all objects               = { action=Unselect(All) }
+						ha:Remove selected objects            = { action=RemoveSelected() }
+						ha:Copy selection to buffer           = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); Mode(PasteBuffer)} }
+						ha:Cut selection to buffer            = { li:action={GetXY(Click to set the snap point for this buffer); PasteBuffer(Clear); PasteBuffer(AddSelected); RemoveSelected(); Mode(PasteBuffer)} }
+						ha:Convert selection to element       = { action=Select(Convert) }
+						ha:Auto place selected elements       = { action=AutoPlaceSelected() }
+						ha:Autoroute selected elements        = { action=AutoRoute(SelectedRats) }
+						ha:Rip up selected auto-routed tracks = { action=RipUp(Selected) }
+					}
+				}
+				ha:Operations on this location {
+					li:submenu {
+						ha:Generate object report = { li:action={GetXY(Click on the object); Report(Object)} }
+					}
+				}
+				-
+				ha:Undo last operation          = { action=Undo() }
+				ha:Redo last undone operation   = { action=Redo() }
+				-
+				ha:Tools {
+					li:submenu {
+						ha:None           = { checked=ChkMode(none); action=Mode(None); update_on={editor/mode} }
+						ha:Via            = { checked=ChkMode(via); a=<Key>F1; action=Mode(Via); update_on={editor/mode} }
+						ha:Line           = { checked=ChkMode(line); li:a={{<Key>a;<Key>n}; {<Key>a;<Key>l}; {<Key>F2};}; action=Mode(Line); update_on={editor/mode} }
+						ha:Arc            = { checked=ChkMode(arc); li:a={{<key>a;<key>r}; {<Key>F3};}; action=Mode(Arc); update_on={editor/mode} }
+						ha:Text           = { checked=ChkMode(text); li:a={{<Key>a;<key>t}; {<Key>F4};}; action=Mode(Text); update_on={editor/mode} }
+						ha:Rectangle      = { checked=ChkMode(rectangle); li:a={{<key>a;<key>p}; {<key>a;<key>v}; {<Key>F5};}; action=Mode(Rectangle); update_on={editor/mode} }
+						ha:Polygon        = { checked=ChkMode(polygon); a=<Key>F6; action=Mode(Polygon); update_on={editor/mode} }
+						ha:Polygon Hole   = { checked=ChkMode(polygonhole); action=Mode(PolygonHole); update_on={editor/mode} }
+						ha:Buffer         = { checked=ChkMode(pastebuffer); a=<Key>F7; action=Mode(PasteBuffer); update_on={editor/mode} }
+						ha:Remove         = { checked=ChkMode(remove); a=<Key>F8; action=Mode(Remove); update_on={editor/mode} }
+						ha:Rotate         = { checked=ChkMode(rotate); a=<Key>F9; action=Mode(Rotate); update_on={editor/mode} }
+						ha:Thermal        = { checked=ChkMode(thermal); a=<Key>F10; action=Mode(Thermal); update_on={editor/mode} }
+						ha:Arrow          = { checked=ChkMode(arrow); a=<Key>F11; action=Mode(Arrow); update_on={editor/mode} }
+						ha:Insert Point   = { checked=ChkMode(insertpoint); a=<Key>Insert; action=Mode(InsertPoint); update_on={editor/mode} }
+						ha:Move           = { checked=ChkMode(move); action=Mode(Move); update_on={editor/mode} }
+						ha:Copy           = { checked=ChkMode(copy); action=Mode(Copy); update_on={editor/mode} }
+						ha:Lock           = { checked=ChkMode(lock); li:a={{<key>e;<key>l}; {<key>e;Shift<key>l}; {<Key>F12};}; action=Mode(Lock); update_on={editor/mode} }
+						ha:Cancel         = { a=<Key>Escape; action=Mode(Escape); }
+					}
+				}
+			}
+		} # popup1
+	} # popups
+} # root
diff --git a/src/pcb-printf.c b/src/pcb-printf.c
new file mode 100644
index 0000000..4d706c7
--- /dev/null
+++ b/src/pcb-printf.c
@@ -0,0 +1,875 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2011 Andrew Poelstra
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
+ *  asp11 at sfu.ca
+ *
+ */
+
+/*! \file <pcb-printf.c>
+ *  \brief Implementation of printf wrapper to output pcb coords and angles
+ *  \par Description
+ *  For details of all supported specifiers, see the comment at the
+ *  top of pcb-printf.h
+ */
+
+#include <locale.h>
+#include "config.h"
+#include "global.h"
+
+#include "pcb-printf.h"
+
+const char *pcb_printf_slot[PCB_PRINTF_SLOT_max] =
+{
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 8 user formats */
+	"%mr",      /* original unitless cmil file format coord */
+	"%.07$$mS"  /* safe file format coord */
+};
+
+static int min_sig_figs(double d)
+{
+	char buf[50];
+	int rv;
+
+	if (d == 0)
+		return 0;
+
+	/* Normalize to x.xxxx... form */
+	if (d < 0)
+		d *= -1;
+	while (d >= 10)
+		d /= 10;
+	while (d < 1)
+		d *= 10;
+
+	rv = sprintf(buf, "%g", d);
+	return rv;
+}
+
+/* Truncate trailing 0's from str */
+static void do_trunc0(char *str)
+{
+	char *end = str + strlen(str) - 1;
+	while((end > str) && (*end == '0') && (end[-1] != '.')) {
+		*end = '\0';
+		end--;
+	}
+}
+
+
+/* Create sprintf specifier, using default_prec no precision is given */
+static inline void make_printf_spec(char *printf_spec_new, const char *printf_spec, int precision, int *trunc0)
+{
+	int i = 0;
+
+	while (printf_spec[i] == '%' || isdigit(printf_spec[i]) ||
+				 printf_spec[i] == '-' || printf_spec[i] == '+' || printf_spec[i] == '#' || printf_spec[i] == '0')
+		++i;
+
+	if (printf_spec[i] == '.') {
+		if ((printf_spec[i+1] == '0') && (isdigit(printf_spec[i+2])))
+			*trunc0 = 1;
+		sprintf(printf_spec_new, ", %sf", printf_spec);
+	}
+	else
+		sprintf(printf_spec_new, ", %s.%df", printf_spec, precision);
+}
+
+
+/* sprintf a value */
+#define sprintf_lc_safe(is_file_mode, out, fmt, val) \
+do { \
+	if (is_file_mode) { \
+		setlocale(LC_ALL, "C"); \
+		sprintf(out, fmt, val); \
+		setlocale(LC_ALL, ""); \
+	} \
+	else \
+		sprintf(out, fmt, val); \
+	if (trunc0) \
+		do_trunc0(filemode_buff); \
+} while(0)
+
+/* append a suffix, with or without space */
+static int inline append_suffix(gds_t *dest, enum e_suffix suffix_type, const char *suffix)
+{
+	switch (suffix_type) {
+	case NO_SUFFIX:
+		break;
+	case SUFFIX:
+		if (gds_append(dest, ' ') != 0) return -1;
+		/* deliberate fall-thru */
+	case FILE_MODE:
+		if (gds_append_str(dest, suffix) != 0) return -1;
+		break;
+	}
+	return 0;
+}
+
+
+/* \brief Internal coord-to-string converter for pcb-printf
+ * \par Function Description
+ * Converts a (group of) measurement(s) to a comma-delimited
+ * string, with appropriate units. If more than one coord is
+ * given, the list is enclosed in parens to make the scope of
+ * the unit suffix clear.
+ *
+ * \param [in] dest         Append the output to this dynamic string
+ * \param [in] coord        Array of coords to convert
+ * \param [in] n_coords     Number of coords in array
+ * \param [in] printf_spec  printf sub-specifier to use with %f
+ * \param [in] e_allow      Bitmap of units the function may use
+ * \param [in] suffix_type  Whether to add a suffix
+ *
+ * \return 0 on success, -1 on error
+ */
+static int CoordsToString(gds_t *dest, Coord coord[], int n_coords, const gds_t *printf_spec_, enum e_allow allow,
+														 enum e_suffix suffix_type)
+{
+	char filemode_buff[128]; /* G_ASCII_DTOSTR_BUF_SIZE */
+	char printf_spec_new_local[256];
+	double *value, value_local[32];
+	enum e_family family;
+	const char *suffix;
+	int i, n, retval = -1, trunc0 = 0;
+	const char *printf_spec = printf_spec_->array;
+	char *printf_spec_new;
+
+	if (n_coords > (sizeof(value_local) / sizeof(value_local[0]))) {
+		value = malloc(n_coords * sizeof *value);
+		if (value == NULL)
+			return -1;
+	}
+	else
+		value = value_local;
+
+	if (allow == 0)
+		allow = ALLOW_ALL;
+
+	i = printf_spec_->used + 64;
+	if (i > sizeof(printf_spec_new_local))
+		printf_spec_new = malloc(i);
+	else
+		printf_spec_new = printf_spec_new_local;
+
+	/* Check our freedom in choosing units */
+	if ((allow & ALLOW_IMPERIAL) == 0)
+		family = METRIC;
+	else if ((allow & ALLOW_METRIC) == 0)
+		family = IMPERIAL;
+	else {
+		int met_votes = 0, imp_votes = 0;
+
+		for (i = 0; i < n_coords; ++i)
+			if (min_sig_figs(PCB_COORD_TO_MIL(coord[i])) < min_sig_figs(PCB_COORD_TO_MM(coord[i])))
+				++imp_votes;
+			else
+				++met_votes;
+
+		if (imp_votes > met_votes)
+			family = IMPERIAL;
+		else
+			family = METRIC;
+	}
+
+	/* Set base unit */
+	for (i = 0; i < n_coords; ++i) {
+		switch (family) {
+		case METRIC:
+			value[i] = PCB_COORD_TO_MM(coord[i]);
+			break;
+		case IMPERIAL:
+			value[i] = PCB_COORD_TO_MIL(coord[i]);
+			break;
+		}
+	}
+
+	/* Determine scale factor -- find smallest unit that brings
+	 * the whole group above unity */
+	for (n = 0; n < get_n_units(); ++n) {
+		if ((Units[n].allow & allow) != 0 && (Units[n].family == family)) {
+			int n_above_one = 0;
+
+			for (i = 0; i < n_coords; ++i)
+				if (fabs(value[i] * Units[n].scale_factor) > 1)
+					++n_above_one;
+			if (n_above_one == n_coords)
+				break;
+		}
+	}
+	/* If nothing worked, wind back to the smallest allowable unit */
+	if (n == get_n_units()) {
+		do {
+			--n;
+		} while ((Units[n].allow & allow) == 0 || Units[n].family != family);
+	}
+
+	/* Apply scale factor */
+	suffix = Units[n].suffix;
+	for (i = 0; i < n_coords; ++i)
+		value[i] = value[i] * Units[n].scale_factor;
+
+	make_printf_spec(printf_spec_new, printf_spec, Units[n].default_prec, &trunc0);
+
+	/* Actually sprintf the values in place
+	 *  (+ 2 skips the ", " for first value) */
+	if (n_coords > 1)
+		if (gds_append(dest,  '(') != 0)
+			goto err;
+
+	sprintf_lc_safe((suffix_type == FILE_MODE), filemode_buff, printf_spec_new + 2, value[0]);
+	if (gds_append_str(dest, filemode_buff) != 0)
+		goto err;
+
+	for (i = 1; i < n_coords; ++i) {
+		sprintf_lc_safe((suffix_type == FILE_MODE), filemode_buff, printf_spec_new, value[i]);
+		if (gds_append_str(dest, filemode_buff) != 0)
+			goto err;
+	}
+	if (n_coords > 1)
+		if (gds_append(dest, ')') != 0) goto err;
+
+
+	/* Append suffix */
+	if (value[0] != 0 || n_coords > 1)
+		if (append_suffix(dest, suffix_type, suffix) != 0)
+			goto err;
+
+	retval = 0;
+err:;
+	if (printf_spec_new != printf_spec_new_local)
+		free(printf_spec_new);
+
+	if (value != value_local)
+		free(value);
+	return retval;
+}
+
+typedef struct {
+	int           score_factor;
+
+	enum e_allow  base;
+	double        down_limit;
+	enum e_allow  down;
+	double        up_limit;
+	enum e_allow  up;
+
+	/* persistent, calculated once */
+	const Unit    *base_unit, *down_unit, *up_unit;
+} human_coord_t;
+
+/* Conversion preference table */
+static human_coord_t human_coord[] = {
+	{2, ALLOW_MM,  0.001, ALLOW_UM, 1000.0, ALLOW_M     ,NULL,NULL,NULL},
+	{1, ALLOW_MIL, 0,     0,        1000.0, ALLOW_IN    ,NULL,NULL,NULL}
+};
+#define NUM_HUMAN_COORD (sizeof(human_coord) / sizeof(human_coord[0]))
+
+static inline int try_human_coord(Coord coord, const Unit *unit, double down_limit, double up_limit, int score_factor, double *value, unsigned int *best, const char **suffix)
+{
+	double v, frac;
+	long int digs, zeros;
+	unsigned int score;
+
+	/* convert the value to the proposed unit */
+	if (unit->family == METRIC)
+		v = PCB_COORD_TO_MM(coord);
+	else
+		v = PCB_COORD_TO_MIL(coord);
+	v = v * unit->scale_factor;
+
+	/* Check if neighbour units are better */
+	if ((down_limit > 0) && (v < down_limit))
+		return -1;
+	if ((up_limit > 0) && (v > up_limit))
+		return +1;
+
+	/* count trailing zeros after the decimal point up to 8 digits */
+	frac = v - floor(v);
+	digs = frac * 100000000.0;
+	if (digs != 0)
+		for(zeros = 0; (digs % 10) == 0; zeros++, digs /= 10);
+	else
+		zeros = 8;
+
+	/* score is higher for more zeroes */
+	score = score_factor + 8 * zeros;
+
+/*	printf("try: %s '%.8f' -> %.8f %d score=%d\n", unit->suffix, v, frac, zeros, score);*/
+
+	/* update the best score */
+	if (score > *best) {
+		*value = v;
+		*best = score;
+		*suffix = unit->suffix;
+	}
+
+	return 0;
+}
+
+/* Same as CoordsToString but take only one coord and print it in human readable format */
+static int CoordsToHumanString(gds_t *dest, Coord coord, const gds_t *printf_spec_, enum e_suffix suffix_type)
+{
+	char filemode_buff[128]; /* G_ASCII_DTOSTR_BUF_SIZE */
+	char printf_spec_new_local[256];
+	char *printf_spec_new;
+	int i, retval = -1, trunc0;
+	const char *printf_spec = printf_spec_->array;
+	const char *suffix;
+	double value;
+	unsigned int best_score = 0;
+
+	i = printf_spec_->used + 64;
+	if (i > sizeof(printf_spec_new_local))
+		printf_spec_new = malloc(i);
+	else
+		printf_spec_new = printf_spec_new_local;
+
+	/* cache unit lookup */
+	if (human_coord[0].base_unit == NULL) {
+		for(i = 0; i < NUM_HUMAN_COORD; i++) {
+			human_coord[i].base_unit = get_unit_struct_by_allow(human_coord[i].base);
+			human_coord[i].down_unit = get_unit_struct_by_allow(human_coord[i].down);
+			human_coord[i].up_unit = get_unit_struct_by_allow(human_coord[i].up);
+		}
+	}
+
+	for(i = 0; i < NUM_HUMAN_COORD; i++) {
+		int res;
+		/* try the base units first */
+		res = try_human_coord(coord, human_coord[i].base_unit, human_coord[i].down_limit, human_coord[i].up_limit, human_coord[i].score_factor,&value, &best_score, &suffix);
+		if (res < 0)
+			try_human_coord(coord, human_coord[i].down_unit, 0, 0, human_coord[i].score_factor, &value, &best_score, &suffix);
+		else if (res > 0)
+			try_human_coord(coord, human_coord[i].up_unit, 0, 0, human_coord[i].score_factor,&value, &best_score, &suffix);
+	}
+
+	make_printf_spec(printf_spec_new, printf_spec, 8, &trunc0);
+	sprintf_lc_safe((suffix_type == FILE_MODE), filemode_buff, printf_spec_new + 2, value);
+	if (gds_append_str(dest, filemode_buff) != 0)
+		goto err;
+
+	if (value != 0) {
+		if (suffix_type == NO_SUFFIX)
+			suffix_type = SUFFIX;
+		if (append_suffix(dest, suffix_type, suffix) != 0)
+			goto err;
+	}
+
+	err:;
+	if (printf_spec_new != printf_spec_new_local)
+		free(printf_spec_new);
+	return retval;
+}
+
+/* \brief Main low level pcb-printf function
+ * \par Function Description
+ * This is a printf wrapper that accepts new format specifiers to
+ * output pcb coords as various units. See the comment at the top
+ * of pcb-printf.h for full details.
+ *
+ * \param [in] string Append anything new at the end of this dynamic string (must be initialized before the call)
+ * \param [in] fmt    Format specifier
+ * \param [in] args   Arguments to specifier
+ *
+ * \return 0 on success
+ */
+int pcb_append_vprintf(gds_t *string, const char *fmt, va_list args)
+{
+	gds_t spec;
+	char tmp[128]; /* large enough for rendering a long long int */
+	int tmplen, retval = -1, slot_recursion = 0;
+	char *free_fmt = NULL;
+	enum e_allow mask = ALLOW_ALL;
+
+	gds_init(&spec);
+
+	new_fmt:;
+	while (*fmt) {
+		enum e_suffix suffix = NO_SUFFIX;
+
+		if (*fmt == '%') {
+			const char *ext_unit = "";
+			Coord value[10];
+			int count, i;
+
+			gds_truncate(&spec, 0);
+
+			/* Get printf sub-specifiers */
+			if (gds_append(&spec, *fmt++) != 0) goto err;
+			while (isdigit(*fmt) || *fmt == '.' || *fmt == ' ' || *fmt == '*'
+						 || *fmt == '#' || *fmt == 'l' || *fmt == 'L' || *fmt == 'h'
+						 || *fmt == '+' || *fmt == '-' || *fmt == '[') {
+				if (*fmt == '*') {
+					char itmp[32];
+					int ilen;
+					ilen = sprintf(itmp, "%d", va_arg(args, int));
+					if (gds_append_len(&spec, itmp, ilen) != 0) goto err;
+					fmt++;
+				}
+				else
+					if (gds_append(&spec, *fmt++) != 0) goto err;
+			}
+
+			/* check if specifier is %[] */
+			if ((gds_len(&spec) > 2) && (spec.array[1] == '[')) {
+				int slot = atoi(spec.array+2);
+				gds_t new_spec;
+				if ((slot < 0) || (slot > PCB_PRINTF_SLOT_max)) {
+					fprintf(stderr, "Internal error: invalid printf slot addressed: '%s'\n", spec.array);
+					abort();
+				}
+				slot_recursion++;
+				if (slot_recursion > 16) {
+					fprintf(stderr, "Internal error: printf slot recursion too deep on addressed: '%s'\n", spec.array);
+					abort();
+				}
+				if (pcb_printf_slot[slot] == NULL) {
+					fprintf(stderr, "Internal error: printf empty slot reference: '%s'\n", spec.array);
+					abort();
+				}
+				gds_init(&new_spec);
+				gds_append_str(&new_spec, pcb_printf_slot[slot]);
+				if (*fmt == ']')
+					fmt++;
+				gds_append_str(&new_spec, fmt);
+				if (free_fmt != NULL)
+					free(free_fmt);
+				fmt = free_fmt = new_spec.array;
+				memset(&new_spec, 0, sizeof(new_spec));
+				gds_truncate(&spec, 0);
+				goto new_fmt;
+			}
+			else
+				slot_recursion = 0;
+
+			/* Get our sub-specifiers */
+			if (*fmt == '#') {
+				mask = ALLOW_CMIL;			/* This must be pcb's base unit */
+				fmt++;
+			}
+			if (*fmt == '$') {
+				suffix = SUFFIX;
+				fmt++;
+				if (*fmt == '$') {
+					fmt++;
+					suffix = FILE_MODE;
+				}
+			}
+			/* Tack full specifier onto specifier */
+			if (*fmt != 'm')
+				if (gds_append(&spec, *fmt) != 0) goto err;
+			switch (*fmt) {
+				/* Printf specs */
+			case 'o':
+			case 'i':
+			case 'd':
+			case 'u':
+			case 'x':
+			case 'X':
+				if (spec.array[1] == 'l') {
+#if 0
+/* C89 does not have long long int - best to avoid %lld */
+					if (spec.array[2] == 'l')
+						tmplen = sprintf(tmp, spec.array, va_arg(args, long long));
+					else
+#endif
+						tmplen = sprintf(tmp, spec.array, va_arg(args, long));
+				}
+				else {
+					tmplen = sprintf(tmp, spec.array, va_arg(args, int));
+				}
+				if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+				break;
+			case 'e':
+			case 'E':
+			case 'f':
+			case 'g':
+			case 'G':
+				if (strchr(spec.array, '*')) {
+					int prec = va_arg(args, int);
+					tmplen = sprintf(tmp, spec.array, va_arg(args, double), prec);
+				}
+				else
+					tmplen = sprintf(tmp, spec.array, va_arg(args, double));
+				if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+				break;
+			case 'c':
+				if (spec.array[1] == 'l' && sizeof(int) <= sizeof(wchar_t))
+					tmplen = sprintf(tmp, spec.array, va_arg(args, wchar_t));
+				else
+					tmplen = sprintf(tmp, spec.array, va_arg(args, int));
+				if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+				break;
+			case 's':
+				if (spec.array[0] == 'l') {
+					/* TODO: convert wchar to char and append it */
+					fprintf(stderr, "Internal error: appending %%ls is not supported\n");
+					abort();
+				}
+				else {
+					const char *s = va_arg(args, const char *);
+					if (s == NULL) s = "(null)";
+					if (gds_append_str(string, s) != 0) goto err;
+				}
+				break;
+			case 'n':
+				/* Depending on gcc settings, this will probably break with
+				 *  some silly "can't put %n in writable data space" message */
+				tmplen = sprintf(tmp, spec.array, va_arg(args, int *));
+				if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+				break;
+			case 'p':
+				tmplen = sprintf(tmp, spec.array, va_arg(args, void *));
+				if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+				break;
+			case '%':
+				if (gds_append(string, '%') != 0) goto err;
+				break;
+				/* Our specs */
+			case 'm':
+				++fmt;
+				if (*fmt == '*')
+					ext_unit = va_arg(args, const char *);
+				if (*fmt != '+' && *fmt != 'a' && *fmt != 'A')
+					value[0] = va_arg(args, Coord);
+				count = 1;
+				switch (*fmt) {
+				case 'I':
+					if (CoordsToString(string, value, 1, &spec, ALLOW_NM, NO_SUFFIX) != 0) goto err;
+					break;
+				case 's':
+					if (CoordsToString(string, value, 1, &spec, ALLOW_MM | ALLOW_MIL, suffix) != 0) goto err;
+					break;
+				case 'S':
+					if (CoordsToString(string, value, 1, &spec, mask & ALLOW_NATURAL, suffix) != 0) goto err;
+					break;
+				case 'H':
+					if (CoordsToHumanString(string, value[0], &spec, suffix) != 0) goto err;
+					break;
+				case 'M':
+					if (CoordsToString(string, value, 1, &spec, mask & ALLOW_METRIC, suffix) != 0) goto err;
+					break;
+				case 'L':
+					if (CoordsToString(string, value, 1, &spec, mask & ALLOW_IMPERIAL, suffix) != 0) goto err;
+					break;
+				case 'k':
+					if (CoordsToString(string, value, 1, &spec, mask & ALLOW_DMIL, suffix) != 0) goto err;
+					break;
+				case 'r':
+					if (CoordsToString(string, value, 1, &spec, ALLOW_READABLE, NO_SUFFIX) != 0) goto err;
+					break;
+					/* All these fallthroughs are deliberate */
+				case '9':
+					value[count++] = va_arg(args, Coord);
+				case '8':
+					value[count++] = va_arg(args, Coord);
+				case '7':
+					value[count++] = va_arg(args, Coord);
+				case '6':
+					value[count++] = va_arg(args, Coord);
+				case '5':
+					value[count++] = va_arg(args, Coord);
+				case '4':
+					value[count++] = va_arg(args, Coord);
+				case '3':
+					value[count++] = va_arg(args, Coord);
+				case '2':
+				case 'D':
+					value[count++] = va_arg(args, Coord);
+					if (CoordsToString(string, value, count, &spec, mask & ALLOW_ALL, suffix) != 0) goto err;
+					break;
+				case 'd':
+					value[1] = va_arg(args, Coord);
+					if (CoordsToString(string, value, 2, &spec, ALLOW_MM | ALLOW_MIL, suffix) != 0) goto err;
+					break;
+				case '*':
+					{
+						int found = 0;
+						for (i = 0; i < get_n_units(); ++i) {
+							if (strcmp(ext_unit, Units[i].suffix) == 0) {
+								if (CoordsToString(string, value, 1, &spec, Units[i].allow, suffix) != 0) goto err;
+								found = 1;
+								break;
+							}
+						}
+						if (!found)
+							if (CoordsToString(string, value, 1, &spec, mask & ALLOW_ALL, suffix) != 0) goto err;
+					}
+					break;
+				case 'a':
+					if (gds_append_len(&spec, ".0f", 3) != 0) goto err;
+					if (suffix == SUFFIX)
+						if (gds_append_len(&spec, " deg", 4) != 0) goto err;
+					tmplen = sprintf(tmp, spec.array, (double) va_arg(args, Angle));
+					if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+					break;
+				case 'A':
+					if (gds_append_len(&spec, ".0f", 3) != 0) goto err;
+					/* if (suffix == SUFFIX)
+						if (gds_append_len(&spec, " deg", 4) != 0) goto err;*/
+					tmplen = sprintf(tmp, spec.array, 10*((double) va_arg(args, Angle))); /* kicad legacy needs decidegrees */
+					if (gds_append_len(string, tmp, tmplen) != 0) goto err;
+					break;
+				case '+':
+					mask = va_arg(args, enum e_allow);
+					break;
+				default:
+					{
+						int found = 0;
+						for (i = 0; i < get_n_units(); ++i) {
+							if (*fmt == Units[i].printf_code) {
+								if (CoordsToString(string, value, 1, &spec, Units[i].allow, suffix) != 0) goto err;
+								found = 1;
+								break;
+							}
+						}
+						if (!found)
+							if (CoordsToString(string, value, 1, &spec, ALLOW_ALL, suffix) != 0) goto err;
+					}
+					break;
+				}
+				break;
+			}
+		}
+		else
+			if (gds_append(string, *fmt) != 0) goto err;
+		++fmt;
+	}
+
+	retval = 0;
+err:;
+	gds_uninit(&spec);
+	if (free_fmt != NULL)
+		free(free_fmt);
+
+	return retval;
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to a string
+ *
+ * \param [in] string  Pointer to string to output into
+ * \param [in] fmt     Format specifier
+ *
+ * \return The length of the written string
+ */
+int pcb_sprintf(char *string, const char *fmt, ...)
+{
+	gds_t str;
+	va_list args;
+
+	gds_init(&str);
+	va_start(args, fmt);
+
+	/* pretend the string is already allocated to something huge; this doesn't
+	   make the code less safe but saves a copy */
+	str.array = string;
+	str.alloced = 1<<31;
+	str.no_realloc = 1;
+
+	pcb_append_vprintf(&str, fmt, args);
+
+	va_end(args);
+	return str.used;
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to a string with a size limit
+ *
+ * \param [in] string  Pointer to string to output into
+ * \param [in] len     Maximum length
+ * \param [in] fmt     Format specifier
+ *
+ * \return The length of the written string
+ */
+int pcb_snprintf(char *string, size_t len, const char *fmt, ...)
+{
+	gds_t str;
+	va_list args;
+
+	gds_init(&str);
+	va_start(args, fmt);
+
+	str.array = string;
+	str.alloced = len;
+	str.no_realloc = 1;
+
+	pcb_append_vprintf(&str, fmt, args);
+
+	va_end(args);
+	return str.used;
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to a file
+ *
+ * \param [in] fh   File to output to
+ * \param [in] fmt  Format specifier
+ *
+ * \return The length of the written string
+ */
+int pcb_fprintf(FILE * fh, const char *fmt, ...)
+{
+	int rv;
+	va_list args;
+
+	va_start(args, fmt);
+	rv = pcb_vfprintf(fh, fmt, args);
+	va_end(args);
+
+	return rv;
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to a file
+ *
+ * \param [in] fh   File to output to
+ * \param [in] fmt  Format specifier
+ * \param [in] args   Arguments to specifier
+ *
+ * \return The length of the written string
+ */
+int pcb_vfprintf(FILE * fh, const char *fmt, va_list args)
+{
+	int rv;
+	gds_t str;
+	gds_init(&str);
+
+	if (fh == NULL)
+		rv = -1;
+	else {
+		pcb_append_vprintf(&str, fmt, args);
+		rv = fprintf(fh, "%s", str.array);
+	}
+
+	gds_uninit(&str);
+	return rv;
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to stdout
+ *
+ * \param [in] fmt  Format specifier
+ *
+ * \return The length of the written string
+ */
+int pcb_printf(const char *fmt, ...)
+{
+	int rv;
+	gds_t str;
+	va_list args;
+
+	gds_init(&str);
+	va_start(args, fmt);
+
+	pcb_append_vprintf(&str, fmt, args);
+	rv = printf("%s", str.array);
+
+	va_end(args);
+	gds_uninit(&str);
+	return rv;
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to a newly allocated string
+ *
+ * \param [in] fmt  Format specifier
+ *
+ * \return The newly allocated string. Must be freed with free.
+ */
+char *pcb_strdup_printf(const char *fmt, ...)
+{
+	gds_t str;
+	va_list args;
+
+	gds_init(&str);
+
+	va_start(args, fmt);
+	pcb_append_vprintf(&str, fmt, args);
+	va_end(args);
+
+	return str.array; /* no other allocation has been made */
+}
+
+/* \brief Wrapper for pcb_append_vprintf that outputs to a string
+ *
+ * \param [in] string  Pointer to string to output into
+ * \param [in] fmt     Format specifier
+ *
+ * \return return the new string; must be free()'d later
+ */
+char *pcb_strdup_vprintf(const char *fmt, va_list args)
+{
+	gds_t str;
+	gds_init(&str);
+
+	pcb_append_vprintf(&str, fmt, args);
+
+	return str.array; /* no other allocation has been made */
+}
+
+
+/* \brief Wrapper for pcb_append_vprintf that appends to a string using vararg API
+ *
+ * \param [in] str  Existing dynamic string
+ * \param [in] fmt  Format specifier
+ *
+ * \return 0 on success
+ */
+int pcb_append_printf(gds_t *str, const char *fmt, ...)
+{
+	int retval;
+
+	va_list args;
+	va_start(args, fmt);
+	retval = pcb_append_vprintf(str, fmt, args);
+	va_end(args);
+
+	return retval;
+}
+
+
+char *pcb_strdup_subst(const char *template, int (*cb)(gds_t *s, const char **input))
+{
+	gds_t s;
+	const char *curr, *next;
+
+	gds_init(&s);
+	for(curr = template;;) {
+		next = strchr(curr, '%');
+		if (next == NULL) {
+			gds_append_str(&s, curr);
+			return s.array;
+		}
+		if (next > curr)
+			gds_append_len(&s, curr, next-curr);
+		next++;
+		switch(*next) {
+			case '%':
+				gds_append(&s, '%');
+				curr = next+1;
+				break;
+			default:
+				if (cb(&s, &next) != 0) {
+					/* keep the directive intact */
+					gds_append(&s, '%');
+				}
+				curr = next;
+		}
+	}
+	abort(); /* can't get here */
+}
diff --git a/src/pcb-printf.h b/src/pcb-printf.h
new file mode 100644
index 0000000..1a8808c
--- /dev/null
+++ b/src/pcb-printf.h
@@ -0,0 +1,126 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2011 Andrew Poelstra
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
+ *  asp11 at sfu.ca
+ *
+ */
+
+/* This file defines a wrapper around sprintf, that
+ *  defines new specifiers that take pcb Coord objects
+ *  as input.
+ *
+ * There is a fair bit of nasty (repetitious) code in
+ *  here, but I feel the gain in clarity for output
+ *  code elsewhere in the project will make it worth
+ *  it.
+ *
+ * The new specifiers are:
+ *   %mI    outout a raw internal coordinate without any suffix
+ *   %mm    output a measure in mm
+ *   %mM    output a measure in scaled (mm/um) metric
+ *   %ml    output a measure in mil
+ *   %mk    output a measure in decimil (kicad legacy format)
+ *   %mL    output a measure in scaled (mil/in) imperial
+ *   %ms    output a measure in most natural mm/mil units
+ *   %mS    output a measure in most natural scaled units
+ *   %mH    output a measure in most human readable scaled units
+ *   %md    output a pair of measures in most natural mm/mil units
+ *   %mD    output a pair of measures in most natural scaled units
+ *   %m3    output 3 measures in most natural scaled units
+ *     ...
+ *   %m9    output 9 measures in most natural scaled units
+ *   %m*    output a measure with unit given as an additional
+ *          const char* parameter
+ *   %m+    accepts an e_allow parameter that masks all subsequent
+ *          "natural" (S/D/3/.../9) specifiers to only use certain
+ *          units
+ *   %mr    output a measure in a unit readable by parse_l.l
+ *          (outputs in centimil without units - compatibility with mainline)
+ *   %ma    output an angle in degrees (expects degrees)
+ *
+ * These accept the usual printf modifiers for %f, as well as
+ *     $    output a unit suffix after the measure (with space between measure and unit)
+ *     $$   output a unit suffix after the measure (without space)
+ *     .n   number of digits after the decimal point (the usual %f modifier)
+ *     .0n  where n is a digit; same as %.n, but truncates trailing zeros
+ *     [n]  use stored format n
+ *     #    prevents all scaling for %mS/D/1/.../9 (this should
+ *          ONLY be used for debug code since its output exposes
+ *          pcb's base units).
+ *
+ * The usual printf(3) precision and length modifiers should work with
+ * any format specifier that outputs coords, e.g. %.3mm will output in
+ * mm up to 3 decimal digits after the decimal point.
+ *
+ * KNOWN ISSUES:
+ *   No support for %zu size_t printf spec
+ */
+
+#ifndef	PCB_PCB_PRINTF_H
+#define	PCB_PCB_PRINTF_H
+
+#include <stdio.h>
+#include <genvector/gds_char.h>
+#include "unit.h"
+
+void initialize_units();
+
+int pcb_fprintf(FILE * f, const char *fmt, ...);
+int pcb_vfprintf(FILE * f, const char *fmt, va_list args);
+int pcb_sprintf(char *string, const char *fmt, ...);
+int pcb_snprintf(char *string, size_t len, const char *fmt, ...);
+int pcb_printf(const char *fmt, ...);
+char *pcb_strdup_printf(const char *fmt, ...);
+char *pcb_strdup_vprintf(const char *fmt, va_list args);
+
+int pcb_append_printf(gds_t *str, const char *fmt, ...);
+int pcb_append_vprintf(gds_t *string, const char *fmt, va_list args);
+
+/* Predefined slots (macros): %[n] will use the nth string from this list.
+   The first 8 are user-definable. */
+typedef enum {
+	PCB_PRINTF_SLOT_USER0,
+	PCB_PRINTF_SLOT_USER1,
+	PCB_PRINTF_SLOT_USER2,
+	PCB_PRINTF_SLOT_USER3,
+	PCB_PRINTF_SLOT_USER4,
+	PCB_PRINTF_SLOT_USER5,
+	PCB_PRINTF_SLOT_USER6,
+	PCB_PRINTF_SLOT_USER7,
+	PCB_PRINTF_SLOT_FF_ORIG_COORD, /* %[8] original .pcb file format coord prints */
+	PCB_PRINTF_SLOT_FF_SAFE_COORD, /* %[9] safe .pcb file format coord print that doesn't lose precision */
+	PCB_PRINTF_SLOT_max
+} pcb_printf_slot_idx_t;
+extern const char *pcb_printf_slot[PCB_PRINTF_SLOT_max];
+
+/* strdup and return a template; also attempt to replace printf-like formatting
+   directives (e.g. %P) using an user provided callback function. The callback
+   function needs to recognize the directive at **input (pointing to the byte
+   after the %) and append the substitution to s and increase *input to point
+   beyond the format directive. The callback returns 0 on success or -1
+   on unknown directive (whcih will be copied verbatim). %% will always
+   be translated into a single %, without calling cb.
+*/
+char *pcb_strdup_subst(const char *template, int (*cb)(gds_t *s, const char **input));
+
+#endif
diff --git a/src/pcb_bool.h b/src/pcb_bool.h
new file mode 100644
index 0000000..77f51a4
--- /dev/null
+++ b/src/pcb_bool.h
@@ -0,0 +1,31 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_BOOL_H
+#define PCB_BOOL_H
+/* Because stdbool is not c89 */
+typedef int pcb_bool;
+typedef enum pcb_bool_e {
+	pcb_false = 0,
+	pcb_true = 1
+} pcb_bool_t;
+#endif
diff --git a/src/plug_footprint.c b/src/plug_footprint.c
new file mode 100644
index 0000000..f7bf28c
--- /dev/null
+++ b/src/plug_footprint.c
@@ -0,0 +1,405 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+
+#include "plug_footprint.h"
+#include "plugins.h"
+
+#include <genht/htsp.h>
+#include <genht/hash.h>
+#include "conf_core.h"
+#include "plugins.h"
+#include "error.h"
+#include "compat_misc.h"
+
+plug_fp_t *plug_fp_chain = NULL;
+library_t library;
+
+int fp_dupname(const char *name, char **basename, char **params)
+{
+	char *newname, *s;
+
+	*basename = newname = pcb_strdup(name);
+	s = strchr(newname, '(');
+	if (s == NULL) {
+		*params = NULL;
+		return 0;
+	}
+
+	/* split at '(' */
+	*s = '\0';
+	*params = s + 1;
+	s = *params + strlen(*params) - 1;
+
+	/* strip ')' */
+	if (*s == ')')
+		*s = '\0';
+
+	return 1;
+}
+
+static htsp_t *fp_tags = NULL;
+
+const void *fp_tag(const char *tag, int alloc)
+{
+	htsp_entry_t *e;
+	static char *counter = NULL;
+
+	if (fp_tags == NULL)
+		fp_tags = htsp_alloc(strhash, strkeyeq);
+	e = htsp_getentry(fp_tags, tag);
+	if ((e == NULL) && alloc) {
+		htsp_set(fp_tags, pcb_strdup(tag), (void *) counter);
+		counter++;
+		e = htsp_getentry(fp_tags, tag);
+	}
+	return e == NULL ? NULL : e->key;
+}
+
+void fp_init()
+{
+	library.type = LIB_DIR;
+	library.name = pcb_strdup("/");  /* All names are eventually free()'d */
+}
+
+void fp_uninit()
+{
+	htsp_entry_t *e;
+	fp_free_children(&library);
+	if (fp_tags != NULL) {
+		for (e = htsp_first(fp_tags); e; e = htsp_next(fp_tags, e))
+			free(e->key);
+		htsp_free(fp_tags);
+		fp_tags = NULL;
+	}
+}
+
+const char *fp_tagname(const void *tagid)
+{
+	return (char *) tagid;
+}
+
+FILE *fp_fopen(const char *path, const char *name, fp_fopen_ctx_t *fctx)
+{
+	FILE *res = NULL;
+	HOOK_CALL(plug_fp_t, plug_fp_chain, fopen, res, != NULL, (self, path, name, fctx));
+	return res;
+}
+
+void fp_fclose(FILE * f, fp_fopen_ctx_t *fctx)
+{
+	if (fctx->backend->fclose != NULL)
+		fctx->backend->fclose(fctx->backend, f, fctx);
+}
+
+library_t *fp_append_entry(library_t *parent, const char *name, fp_type_t type, void *tags[])
+{
+	library_t *entry;   /* Pointer to individual menu entry */
+
+	assert(parent->type == LIB_DIR);
+	entry = get_library_memory(parent);
+	if (entry == NULL)
+		return NULL;
+
+	if (type == PCB_FP_PARAMETRIC) {
+		/* concat name and () */
+		/* do not use pcb_strdup_printf or Concat here, do not increase gsch2pcb-rnd deps */
+		int nl = strlen(name);
+		entry->name = malloc(nl+3);
+		memcpy(entry->name, name, nl);
+		strcpy(entry->name+nl, "()");
+	}
+	else
+		entry->name = pcb_strdup(name);
+
+	entry->type = LIB_FOOTPRINT;
+	entry->data.fp.type = type;
+	entry->data.fp.tags = tags;
+	entry->data.fp.loc_info = NULL;
+	entry->data.fp.backend_data = NULL;
+	return entry;
+}
+
+library_t *fp_lib_search_len(library_t *dir, const char *name, int name_len)
+{
+	library_t *l;
+	int n;
+
+	if (dir->type != LIB_DIR)
+		return NULL;
+
+	for(n = 0, l = dir->data.dir.children.array; n < dir->data.dir.children.used; n++, l++)
+		if (strncmp(l->name, name, name_len) == 0)
+			return l;
+
+	return NULL;
+}
+
+library_t *fp_lib_search(library_t *dir, const char *name)
+{
+	library_t *l;
+	int n;
+
+	if (dir->type != LIB_DIR)
+		return NULL;
+
+	for(n = 0, l = dir->data.dir.children.array; n < dir->data.dir.children.used; n++, l++)
+		if (strcmp(l->name, name) == 0)
+			return l;
+
+	return NULL;
+}
+
+
+library_t *fp_mkdir_len(library_t *parent, const char *name, int name_len)
+{
+	library_t *l = get_library_memory(parent);
+
+	if (name_len > 0)
+		l->name = pcb_strndup(name, name_len);
+	else
+		l->name = pcb_strdup(name);
+	l->parent = parent;
+	l->type = LIB_DIR;
+	vtlib_init(&l->data.dir.children);
+	return l;
+}
+
+library_t *fp_mkdir_p(const char *path)
+{
+	library_t *l, *parent = NULL;
+	const char *next;
+
+	/* search for the last existing dir in the path */
+
+	while(*path == '/') path++;
+	for(parent = l = &library; l != NULL; parent = l,path = next) {
+		next = strchr(path, '/');
+		if (next == NULL)
+			l = fp_lib_search(l, path);
+		else
+			l = fp_lib_search_len(l, path, next-path);
+
+		/* skip path sep */
+		if (next != NULL) {
+			while(*next == '/') next++;
+			if (*next == '\0')
+				next = NULL;
+		}
+
+		/* last elem of the path */
+		if (next == NULL) {
+			if (l != NULL)
+				return l;
+			break; /* found a non-existing node */
+		}
+
+		if (l == NULL)
+			break;
+	}
+
+	/* by now path points to the first non-existing dir, under parent */
+	for(;path != NULL; path = next) {
+		next = strchr(path, '/');
+		parent = fp_mkdir_len(parent, path, next-path);
+		if (next != NULL) {
+			while(*next == '/') next++;
+			if (*next == '\0')
+				next = NULL;
+		}
+	}
+
+	return parent;
+}
+
+void fp_sort_children(library_t *parent)
+{
+/*	int i;
+	qsort(lib->Menu, lib->MenuN, sizeof(lib->Menu[0]), netlist_sort);
+	for (i = 0; i < lib->MenuN; i++)
+		qsort(lib->Menu[i].Entry, lib->Menu[i].EntryN, sizeof(lib->Menu[i].Entry[0]), netnode_sort);*/
+}
+
+void fp_free_entry(library_t *l)
+{
+	switch(l->type) {
+		case LIB_DIR:
+			fp_free_children(l);
+			vtlib_uninit(&(l->data.dir.children));
+			break;
+		case LIB_FOOTPRINT:
+			if (l->data.fp.loc_info != NULL)
+				free(l->data.fp.loc_info);
+			if (l->data.fp.tags != NULL)
+				free(l->data.fp.tags);
+			break;
+		case LIB_INVALID: break; /* suppress compiler warning */
+	}
+	if (l->name != NULL) {
+		free(l->name);
+		l->name = NULL;
+	}
+	l->type = LIB_INVALID;
+}
+
+void fp_free_children(library_t *parent)
+{
+	int n;
+	library_t *l;
+
+	assert(parent->type == LIB_DIR);
+
+	for(n = 0, l = parent->data.dir.children.array; n < parent->data.dir.children.used; n++, l++)
+		fp_free_entry(l);
+
+	vtlib_truncate(&(parent->data.dir.children), 0);
+}
+
+
+void fp_rmdir(library_t *dir)
+{
+	library_t *l, *parent = dir->parent;
+	int n;
+	fp_free_entry(dir);
+	if (parent != NULL) {
+		for(n = 0, l = parent->data.dir.children.array; n < parent->data.dir.children.used; n++,l++) {
+			if (l == dir) {
+				vtlib_remove(&(parent->data.dir.children), n, 1);
+				break;
+			}
+		}
+	}
+}
+
+/* Debug functions */
+void fp_dump_dir(library_t *dir, int level)
+{
+	library_t *l;
+	int n, p;
+
+	for(n = 0, l = dir->data.dir.children.array; n < dir->data.dir.children.used; n++, l++) {
+		for(p = 0; p < level; p++)
+			putchar(' ');
+		if (l->type == LIB_DIR) {
+			printf("%s/\n", l->name);
+			fp_dump_dir(l, level+1);
+		}
+		else if (l->type == LIB_FOOTPRINT)
+			printf("%s\n", l->name);
+		else
+			printf("*INVALID*\n");
+	}
+}
+
+void fp_dump()
+{
+	fp_dump_dir(&library, 0);
+}
+
+/* This function loads the newlib footprints into the Library.
+ * It examines all directories pointed to by the search paths
+ * (normally Settings.LibraryTree).
+ * In each directory specified there, it looks both in that directory,
+ * as well as *one* level down.  It calls the subfunction
+ * fp_fs_load_dir to put the footprints into PCB's internal
+ * datastructures.
+ */
+static int fp_read_lib_all_(const char *searchpath)
+{
+	char toppath[MAXPATHLEN + 1];	/* String holding abs path to top level library dir */
+	char *libpaths;								/* String holding list of library paths to search */
+	char *p;											/* Helper string used in iteration */
+	int n_footprints = 0;					/* Running count of footprints found */
+	int res;
+
+	/* Initialize path, working by writing 0 into every byte. */
+	memset(toppath, 0, sizeof toppath);
+
+	/* Additional loop to allow for multiple 'newlib' style library directories
+	 * called out in Settings.LibraryTree
+	 */
+	libpaths = pcb_strdup(searchpath);
+	for (p = strtok(libpaths, PCB_PATH_DELIMETER); p && *p; p = strtok(NULL, PCB_PATH_DELIMETER)) {
+		/* remove trailing path delimiter */
+		strncpy(toppath, p, sizeof(toppath) - 1);
+
+#ifdef DEBUG
+		printf("In ParseLibraryTree, looking for newlib footprints inside top level directory %s ... \n", toppath);
+#endif
+
+		/* Next read in any footprints in the top level dir */
+		res = -1;
+		HOOK_CALL(plug_fp_t, plug_fp_chain, load_dir, res, >= 0, (self, toppath));
+		if (res >= 0)
+			n_footprints += res;
+		else
+			Message(PCB_MSG_DEFAULT, "Warning: footprint library list error on %s\n", toppath);
+	}
+
+#ifdef DEBUG
+	printf("Leaving ParseLibraryTree, found %d footprints.\n", n_footprints);
+#endif
+
+	free(libpaths);
+	return n_footprints;
+}
+
+static gds_t fpds_paths;
+static int fpds_inited = 0;
+
+const char *fp_default_search_path(void)
+{
+	return conf_concat_strlist(&conf_core.rc.library_search_paths, &fpds_paths, &fpds_inited, ':');
+}
+
+int fp_host_uninit(void)
+{
+	if (fpds_inited)
+		gds_uninit(&fpds_paths);
+	return 0;
+}
+
+int fp_read_lib_all(void)
+{
+	FILE *resultFP = NULL;
+
+	/* List all footprint libraries.  Then sort the whole
+	 * library.
+	 */
+	if (fp_read_lib_all_(fp_default_search_path()) > 0 || resultFP != NULL) {
+		fp_sort_children(&library);
+		return 0;
+	}
+
+	return (1);
+}
+
+int fp_rehash(void)
+{
+	fp_free_children(&library);
+	return fp_read_lib_all();
+}
diff --git a/src/plug_footprint.h b/src/plug_footprint.h
new file mode 100644
index 0000000..98db060
--- /dev/null
+++ b/src/plug_footprint.h
@@ -0,0 +1,90 @@
+#ifndef PCB_PLUG_FOOTPRINT_H
+#define PCB_PLUG_FOOTPRINT_H
+
+#include <stdio.h>
+#include "vtlibrary.h"
+
+typedef struct plug_fp_s plug_fp_t;
+
+typedef struct {
+	plug_fp_t *backend;
+	union {
+		int i;
+		void *p;
+	} field[4];
+} fp_fopen_ctx_t;
+
+/* hook bindings, see below */
+FILE *fp_fopen(const char *path, const char *name, fp_fopen_ctx_t *fctx);
+void fp_fclose(FILE * f, fp_fopen_ctx_t *fctx);
+
+/* duplicates the name and splits it into a basename and params;
+   params is NULL if the name is not parametric (and "" if parameter list is empty)
+   returns 1 if name is parametric, 0 if file element.
+   The caller shall free only *basename at the end.
+   */
+int fp_dupname(const char *name, char **basename, char **params);
+
+/**** tag management ****/
+/* Resolve a tag name to an unique void * ID; create unknown tag if alloc != 0 */
+const void *fp_tag(const char *tag, int alloc);
+
+/* Resolve a tag ID to a tag name */
+const char *fp_tagname(const void *tagid);
+
+/* init/uninit the footprint lib, free tag key memory */
+void fp_init();
+void fp_uninit();
+
+/**************************** API definition *********************************/
+struct plug_fp_s {
+	plug_fp_t *next;
+	void *plugin_data;
+
+	/* returns the number of footprints loaded into the library or -1 on
+	   error; next in chain is run only on error. */
+	int (*load_dir)(plug_fp_t *ctx, const char *path);
+
+/* Open a footprint for reading; if the footprint is parametric, it's run
+   prefixed with libshell (or executed directly, if libshell is NULL).
+   If name is not an absolute path, search_path is searched for the first match.
+   The user has to supply a state integer that will be used by pcb_fp_fclose().
+   Must fill in fctx->backend, may use any other field of fctx as well.
+ */
+	FILE *(*fopen)(plug_fp_t *ctx, const char *path, const char *name, fp_fopen_ctx_t *fctx);
+
+/* Close the footprint file opened by pcb_fp_fopen(). */
+	void (*fclose)(plug_fp_t *ctx, FILE * f, fp_fopen_ctx_t *fctx);
+};
+
+extern plug_fp_t *plug_fp_chain;
+
+
+/* Optional pcb-rnd-side glue for some implementations */
+
+extern library_t library; /* the footprint library */
+
+#define get_library_memory(parent) vtlib_alloc_append(((parent) == NULL ? &library.data.dir.children : &(parent)->data.dir.children), 1);
+
+void fp_free_children(library_t *parent);
+void fp_sort_children(library_t *parent);
+void fp_rmdir(library_t *dir);
+library_t *fp_mkdir_p(const char *path);
+library_t *fp_mkdir_len(library_t *parent, const char *name, int name_len);
+library_t *fp_lib_search(library_t *dir, const char *name);
+
+/* Append a menu entry in the tree */
+library_t *fp_append_entry(library_t *parent, const char *name, fp_type_t type, void *tags[]);
+
+/* walk through all lib paths and build the library menu */
+int fp_read_lib_all(void);
+
+
+const char *fp_default_search_path(void);
+
+int fp_host_uninit(void);
+
+/* rescan/reload all footprints in the library cache */
+int fp_rehash(void);
+
+#endif
diff --git a/src/plug_footprint_act.c b/src/plug_footprint_act.c
new file mode 100644
index 0000000..9ceaddc
--- /dev/null
+++ b/src/plug_footprint_act.c
@@ -0,0 +1,45 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This module, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "hid.h"
+#include "plug_footprint.h"
+
+static const char fp_rehash_syntax[] = "fp_rehash()" ;
+static const char fp_rehash_help[] = "Flush the library index; rescan all library search paths and rebuild the library index. Useful if there are changes in the library during a pcb-rnd session.";
+static int Action_fp_rehash(int argc, const char **argv, Coord x, Coord y)
+{
+	fp_rehash();
+	return 0;
+}
+
+
+HID_Action conf_plug_footprint_list[] = {
+	{"fp_rehash", 0, Action_fp_rehash,
+	 fp_rehash_help, fp_rehash_syntax}
+};
+
+REGISTER_ACTIONS(conf_plug_footprint_list, NULL)
diff --git a/src/plug_import.c b/src/plug_import.c
new file mode 100644
index 0000000..0968beb
--- /dev/null
+++ b/src/plug_import.c
@@ -0,0 +1,121 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
+ *  Copyright (C) 2015,2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This used to be file.c; it's a hook based plugin API now */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <locale.h>
+
+#include "global.h"
+
+#include <time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "plugins.h"
+#include "plug_import.h"
+#include "error.h"
+
+
+plug_import_t *plug_import_chain = NULL;
+
+typedef struct {
+	plug_import_t *plug;
+	int prio;
+} find_t;
+
+/* Find the plugin that offers the highest write prio for the format */
+static plug_import_t *find_importer(unsigned int aspects, FILE *f, const char *filename)
+{
+	find_t available[32]; /* wish we had more than 32 import plugins... */
+	int n, len = 0, best = 0, bestidx = -1;
+
+#define cb_append(pl, pr) \
+	do { \
+		rewind(f); \
+		if (pr > 0) { \
+			assert(len < sizeof(available)/sizeof(available[0])); \
+			available[len].plug = pl; \
+			available[len++].prio = pr; \
+		} \
+	} while(0)
+
+	HOOK_CALL_ALL(plug_import_t, plug_import_chain, fmt_support_prio, cb_append,   (self, aspects, f, filename));
+	if (len == 0)
+		return NULL;
+
+	for(n = 0; n < len; n++) {
+		if (available[n].prio > best) {
+			bestidx = n;
+			best = available[n].prio;
+		}
+	}
+
+	if (best == 0)
+		return NULL;
+
+	return available[bestidx].plug;
+#undef cb_append
+}
+
+
+int pcb_import(char *filename, unsigned int aspect)
+{
+	plug_import_t *plug;
+	FILE *fp;
+
+	if (!filename) {
+		Message(PCB_MSG_DEFAULT, "Error: need a file name for ImportNetlist()\n");
+		return (1); /* nothing to do */
+	}
+	fp = fopen(filename, "r");
+
+	plug = find_importer(aspect, fp, filename);
+	if (plug == NULL) {
+		if (fp != NULL) {
+			Message(PCB_MSG_DEFAULT, "Error: can't find a suitable netlist parser for %s\n", filename);
+			fclose(fp);
+		}
+		else
+			Message(PCB_MSG_DEFAULT, "Error: can't find a suitable netlist parser for %s - might be related: can't open %s for reading\n", filename, filename);
+		return 1;
+	}
+
+	if (fp != NULL)
+		fclose(fp);
+
+	return plug->import(plug, aspect, filename);
+}
+
+int ImportNetlist(char *filename)
+{
+	return pcb_import(filename, IMPORT_ASPECT_NETLIST);
+}
diff --git a/src/plug_import.h b/src/plug_import.h
new file mode 100644
index 0000000..941c3d5
--- /dev/null
+++ b/src/plug_import.h
@@ -0,0 +1,62 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#ifndef PCB_PLUG_IMPORT_H
+#define PCB_PLUG_IMPORT_H
+
+#include "global.h"
+#include "conf.h"
+
+/**************************** API definition *********************************/
+
+typedef enum plug_import_aspect_e { /* bitfield of aspects that can be imported */
+	IMPORT_ASPECT_NETLIST = 1
+} plug_import_aspect_t;
+
+typedef struct plug_import_s plug_import_t;
+struct plug_import_s {
+	plug_import_t *next;
+	void *plugin_data;
+
+	/* Check if the plugin supports format fmt. Return 0 if not supported or
+	   an integer priority if supported. The higher the prio is the more likely
+	   the plugin gets the next operation on the file. Base prio should be 100
+	   for native formats. Return non-0 only if all aspects are supported. */
+	int (*fmt_support_prio)(plug_import_t *ctx, unsigned int aspects, FILE *f, const char *filename);
+
+	/* Perform the import; return 0 on success */
+	int (*import)(plug_import_t *ctx, unsigned int aspects, const char *fn);
+};
+
+extern plug_import_t *plug_import_chain;
+
+/********** hook wrappers **********/
+int pcb_import(char *filename, unsigned int aspect);
+int ImportNetlist(char *);
+
+
+#endif
diff --git a/src/plug_io.c b/src/plug_io.c
new file mode 100644
index 0000000..352356c
--- /dev/null
+++ b/src/plug_io.c
@@ -0,0 +1,935 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
+ *  Copyright (C) 2015,2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* This used to be file.c; some of the code landed in the io_pcb plugin,
+   the format-independent parts ended up here. */
+
+/* file save, load, merge ... routines */
+
+#warning TODO: do not hardwire this, make a function to decide
+#define DEFAULT_FMT "pcb"
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <locale.h>
+#include "global.h"
+
+#include <dirent.h>
+#include <time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buffer.h"
+#include "change.h"
+#include "create.h"
+#include "data.h"
+#include "error.h"
+#include "plug_io.h"
+#include "misc.h"
+#include "remove.h"
+#include "set.h"
+#include "paths.h"
+#include "rats_patch.h"
+#include "hid_actions.h"
+#include "hid_flags.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "event.h"
+#include "compat_misc.h"
+#include "route_style.h"
+#include "compat_fs.h"
+
+/* for opendir */
+#include "compat_inc.h"
+
+plug_io_t *plug_io_chain = NULL;
+int pcb_io_err_inhibit = 0;
+
+static void plug_io_err(int res, const char *what, const char *filename)
+{
+	if (pcb_io_err_inhibit)
+		return;
+	if (res != 0) {
+		const char *reason = "", *comment = "";
+		if (plug_io_chain != NULL) {
+			if (filename == NULL) {
+				reason = "none of io plugins could successfully write the file";
+				filename = "";
+			}
+			else {
+				FILE *f;
+				reason = "none of io plugins could successfully read file";
+				f = fopen(filename, "r");
+				if (f != NULL) {
+					fclose(f);
+					comment = "(unknown/invalid file format?)";
+				}
+				else
+					comment = "(can not open the file for reading)";
+			}
+		}
+		else {
+			reason = "no io plugin loaded, I don't know any file format";
+			if (filename == NULL)
+				filename = "";
+		}
+		Message(PCB_MSG_DEFAULT, "IO error during %s: %s %s %s\n", what, reason, filename, comment);
+	}
+}
+
+int ParsePCB(PCBTypePtr Ptr, const char *Filename, const char *fmt, int load_settings)
+{
+	int res = -1;
+
+	if (load_settings)
+		event(EVENT_LOAD_PRE, "s", Filename);
+
+	Ptr->Data->loader = NULL;
+
+	if (fmt != NULL) {
+		pcb_find_io_t available[PCB_IO_MAX_FORMATS];
+		int len, n;
+		len = pcb_find_io(available, sizeof(available)/sizeof(available[0]), PCB_IOT_PCB, 0, fmt);
+		if (len <= 0) {
+			Message(PCB_MSG_DEFAULT, "Error: can't find a IO_ plugin to load a PCB using format %s\n", fmt);
+			return -1;
+		}
+		for(n = 0; n < len; n++) {
+			if (available[0].plug->parse_pcb == NULL)
+				continue;
+			res = available[0].plug->parse_pcb(available[0].plug, Ptr, Filename, load_settings);
+			if (res == 0) {
+				Ptr->Data->loader = available[0].plug;
+				break;
+			}
+		}
+	}
+	else /* try all parsers until we find one that works */
+		HOOK_CALL_DO(plug_io_t, plug_io_chain, parse_pcb, res, == 0, (self, Ptr, Filename, load_settings), if (Ptr->Data->loader == NULL) Ptr->Data->loader = self);
+
+	if ((res == 0) && (load_settings))
+		conf_load_project(NULL, Filename);
+
+	if (load_settings)
+		event(EVENT_LOAD_POST, "si", Filename, res);
+
+	plug_io_err(res, "load pcb", Filename);
+	return res;
+}
+
+int ParseElement(DataTypePtr Ptr, const char *name)
+{
+	int res = -1;
+
+	Ptr->loader = NULL;
+	HOOK_CALL_DO(plug_io_t, plug_io_chain, parse_element, res, == 0, (self, Ptr, name), Ptr->loader = self);
+
+	plug_io_err(res, "load element", name);
+	return res;
+}
+
+int ParseFont(FontTypePtr Ptr, char *Filename)
+{
+	int res = -1;
+	HOOK_CALL(plug_io_t, plug_io_chain, parse_font, res, == 0, (self, Ptr, Filename));
+
+	plug_io_err(res, "load font", Filename);
+	return res;
+}
+
+static int find_prio_cmp(const void *p1, const void *p2)
+{
+	const pcb_find_io_t *f1 = p1, *f2 = p2;
+	if (f1->prio < f2->prio)
+		return 1;
+	return -1;
+}
+
+int pcb_find_io(pcb_find_io_t *available, int avail_len, plug_iot_t typ, int is_wr, const char *fmt)
+{
+	int len = 0;
+
+#define cb_append(pl, pr) \
+	do { \
+		if (pr > 0) { \
+			assert(len < avail_len ); \
+			available[len].plug = pl; \
+			if (fmt == NULL) \
+				available[len].prio = pl->save_preference_prio; \
+			else \
+				available[len].prio = pr; \
+			len++; \
+		} \
+	} while(0)
+
+	HOOK_CALL_ALL(plug_io_t, plug_io_chain, fmt_support_prio, cb_append, (self, typ, is_wr, (fmt == NULL ? self->default_fmt : fmt)));
+
+	if (len > 0)
+		qsort(available, len, sizeof(available[0]), find_prio_cmp);
+#undef cb_append
+
+	return len;
+}
+
+/* Find the plugin that offers the highest write prio for the format */
+static plug_io_t *find_writer(plug_iot_t typ, const char *fmt)
+{
+	pcb_find_io_t available[PCB_IO_MAX_FORMATS];
+	int len;
+
+	if (fmt == NULL) {
+#warning TODO: make this configurable, default to lihata
+		fmt = "pcb";
+	}
+
+	len = pcb_find_io(available, sizeof(available)/sizeof(available[0]), typ, 1, fmt);
+
+	if (len == 0)
+		return NULL;
+
+	return available[0].plug;
+}
+
+
+int WriteBuffer(FILE *f, BufferType *buff, const char *fmt)
+{
+	int res, newfmt = 0;
+	plug_io_t *p = find_writer(PCB_IOT_BUFFER, fmt);
+
+	if (p != NULL) {
+		res = p->write_buffer(p, f, buff);
+		newfmt = 1;
+	}
+
+/*	if ((res == 0) && (newfmt))
+		PCB->Data->loader = p;*/
+
+	plug_io_err(res, "write buffer", NULL);
+	return res;
+}
+
+int WriteElementData(FILE *f, DataTypePtr e, const char *fmt)
+{
+	int res, newfmt = 0;
+	plug_io_t *p = e->loader;
+
+	if ((p == NULL) || ((fmt != NULL) && (*fmt != '\0'))) {
+		p = find_writer(PCB_IOT_FOOTPRINT, fmt);
+		newfmt = 1;
+	}
+
+	if (p != NULL)
+		res = p->write_element(p, f, e);
+
+	if ((res == 0) && (newfmt))
+		e->loader = p;
+
+	plug_io_err(res, "write element", NULL);
+	return res;
+}
+
+static int pcb_write_pcb(FILE *f, const char *old_filename, const char *new_filename, const char *fmt, pcb_bool emergency)
+{
+	int res, newfmt = 0;
+	plug_io_t *p = PCB->Data->loader;
+
+	if ((p == NULL) || ((fmt != NULL) && (*fmt != '\0'))) {
+		p = find_writer(PCB_IOT_PCB, fmt);
+		newfmt = 1;
+	}
+
+	if (p != NULL) {
+		event(EVENT_SAVE_PRE, "s", fmt);
+		res = p->write_pcb(p, f, old_filename, new_filename, emergency);
+		event(EVENT_SAVE_POST, "si", fmt, res);
+	}
+
+	if ((res == 0) && (newfmt))
+		PCB->Data->loader = p;
+
+	plug_io_err(res, "write pcb", NULL);
+	return res;
+}
+
+/* ---------------------------------------------------------------------------
+ * load PCB
+ * parse the file with enabled 'PCB mode' (see parser)
+ * if successful, update some other stuff
+ *
+ * If revert is pcb_true, we pass "revert" as a parameter
+ * to the HID's PCBChanged action.
+ */
+static int real_load_pcb(const char *Filename, const char *fmt, pcb_bool revert, pcb_bool require_font, int how)
+{
+	const char *unit_suffix;
+	char *new_filename;
+	PCBTypePtr newPCB = CreateNewPCB_(pcb_false);
+	PCBTypePtr oldPCB;
+	conf_role_t settings_dest;
+#ifdef DEBUG
+	double elapsed;
+	clock_t start, end;
+
+	start = clock();
+#endif
+
+	resolve_path(Filename, &new_filename, 0);
+
+	oldPCB = PCB;
+	PCB = newPCB;
+
+	/* mark the default font invalid to know if the file has one */
+	newPCB->Font.Valid = pcb_false;
+
+	switch(how) {
+		case 0: settings_dest = CFR_DESIGN; break;
+		case 1: settings_dest = CFR_DEFAULTPCB; break;
+		case 2: settings_dest = CFR_invalid; break;
+		default: abort();
+	}
+
+	/* new data isn't added to the undo list */
+	if (!ParsePCB(PCB, new_filename, fmt, settings_dest)) {
+		RemovePCB(oldPCB);
+
+		CreateNewPCBPost(PCB, 0);
+		ResetStackAndVisibility();
+
+		if (how == 0) {
+			/* update cursor location */
+			Crosshair.X = PCB_CLAMP(PCB->CursorX, 0, PCB->MaxWidth);
+			Crosshair.Y = PCB_CLAMP(PCB->CursorY, 0, PCB->MaxHeight);
+
+			/* update cursor confinement and output area (scrollbars) */
+			ChangePCBSize(PCB->MaxWidth, PCB->MaxHeight);
+		}
+
+		/* enable default font if necessary */
+		if (!PCB->Font.Valid) {
+			if (require_font)
+				Message(PCB_MSG_DEFAULT, _("File '%s' has no font information, using default font\n"), new_filename);
+			PCB->Font.Valid = pcb_true;
+		}
+
+		/* clear 'changed flag' */
+		SetChangedFlag(pcb_false);
+		PCB->Filename = new_filename;
+		/* just in case a bad file saved file is loaded */
+
+		/* Use attribute PCB::grid::unit as unit, if we can */
+		unit_suffix = AttributeGet(PCB, "PCB::grid::unit");
+		if (unit_suffix && *unit_suffix) {
+			const Unit *new_unit = get_unit_struct(unit_suffix);
+			if (new_unit)
+				conf_set(settings_dest, "editor/grid_unit", -1, unit_suffix, POL_OVERWRITE);
+		}
+		AttributePut(PCB, "PCB::grid::unit", conf_core.editor.grid_unit->suffix);
+
+		pcb_sort_netlist();
+		rats_patch_make_edited(PCB);
+
+/* set route style to the first one, if the current one doesn't
+   happen to match any.  This way, "revert" won't change the route style. */
+		if (hid_get_flag("GetStyle()") < 0)
+			pcb_use_route_style_idx(&PCB->RouteStyle, 0);
+
+		if (((how == 0) || (revert)) && (gui != NULL)) {
+			if (revert)
+				hid_actionl("PCBChanged", "revert", NULL);
+			else
+				hid_action("PCBChanged");
+		}
+
+#ifdef DEBUG
+		end = clock();
+		elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
+		gui->log("Loading file %s took %f seconds of CPU time\n", new_filename, elapsed);
+#endif
+
+		return (0);
+	}
+
+	PCB = oldPCB;
+	if (PCB == NULL) {
+		/* bozo: we are trying to revert back to a non-existing pcb... create one to avoid a segfault */
+		PCB = CreateNewPCB_(pcb_false);
+		if (PCB == NULL) {
+			Message(PCB_MSG_DEFAULT, "FATAL: can't create a new empty pcb!");
+			exit(1);
+		}
+	}
+
+	if (gui != NULL)
+		hid_action("PCBChanged");
+
+	/* release unused memory */
+	RemovePCB(newPCB);
+
+	return (1);
+}
+
+
+#if !defined(HAS_ATEXIT)
+/* ---------------------------------------------------------------------------
+ * some local identifiers for OS without an atexit() call
+ */
+static char *TMPFilename = NULL;
+#endif
+
+/* ---------------------------------------------------------------------------
+ * Flag helper functions
+ */
+
+#define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
+
+/* --------------------------------------------------------------------------- */
+
+static int string_cmp(const char *a, const char *b)
+{
+	while (*a && *b) {
+		if (isdigit((int) *a) && isdigit((int) *b)) {
+			int ia = atoi(a);
+			int ib = atoi(b);
+			if (ia != ib)
+				return ia - ib;
+			while (isdigit((int) *a) && *(a + 1))
+				a++;
+			while (isdigit((int) *b) && *(b + 1))
+				b++;
+		}
+		else if (tolower((int) *a) != tolower((int) *b))
+			return tolower((int) *a) - tolower((int) *b);
+		a++;
+		b++;
+	}
+	if (*a)
+		return 1;
+	if (*b)
+		return -1;
+	return 0;
+}
+
+static int netlist_sort_offset = 0;
+
+static int netlist_sort(const void *va, const void *vb)
+{
+	const LibraryMenuType *am = (const LibraryMenuType *) va;
+	const LibraryMenuType *bm = (const LibraryMenuType *) vb;
+	const char *a = am->Name;
+	const char *b = bm->Name;
+	if (*a == '~')
+		a++;
+	if (*b == '~')
+		b++;
+	return string_cmp(a, b);
+}
+
+static int netnode_sort(const void *va, const void *vb)
+{
+	const LibraryEntryType *am = (const LibraryEntryType *) va;
+	const LibraryEntryType *bm = (const LibraryEntryType *) vb;
+	const char *a = am->ListEntry;
+	const char *b = bm->ListEntry;
+	return string_cmp(a, b);
+}
+
+void sort_library(LibraryTypePtr lib)
+{
+	int i;
+	qsort(lib->Menu, lib->MenuN, sizeof(lib->Menu[0]), netlist_sort);
+	for (i = 0; i < lib->MenuN; i++)
+		qsort(lib->Menu[i].Entry, lib->Menu[i].EntryN, sizeof(lib->Menu[i].Entry[0]), netnode_sort);
+}
+
+void pcb_sort_netlist()
+{
+	netlist_sort_offset = 2;
+	sort_library(&(PCB->NetlistLib[NETLIST_INPUT]));
+	netlist_sort_offset = 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * opens a file and check if it exists
+ */
+FILE *CheckAndOpenFile(const char *Filename, pcb_bool Confirm, pcb_bool AllButton, pcb_bool * WasAllButton, pcb_bool * WasCancelButton)
+{
+	FILE *fp = NULL;
+	struct stat buffer;
+	char message[MAXPATHLEN + 80];
+	int response;
+
+	if (Filename && *Filename) {
+		if (!stat(Filename, &buffer) && Confirm) {
+			sprintf(message, _("File '%s' exists, use anyway?"), Filename);
+			if (WasAllButton)
+				*WasAllButton = pcb_false;
+			if (WasCancelButton)
+				*WasCancelButton = pcb_false;
+			if (AllButton)
+				response = gui->confirm_dialog(message, "Cancel", "Ok", AllButton ? "Sequence OK" : 0);
+			else
+				response = gui->confirm_dialog(message, "Cancel", "Ok", "Sequence OK");
+
+			switch (response) {
+			case 2:
+				if (WasAllButton)
+					*WasAllButton = pcb_true;
+				break;
+			case 0:
+				if (WasCancelButton)
+					*WasCancelButton = pcb_true;
+			}
+		}
+		if ((fp = fopen(Filename, "w")) == NULL)
+			OpenErrorMessage(Filename);
+	}
+	return (fp);
+}
+
+/* ---------------------------------------------------------------------------
+ * opens a file for saving connection data
+ */
+FILE *OpenConnectionDataFile(void)
+{
+	char *fname;
+	FILE *fp;
+	static char *default_file = NULL;
+	pcb_bool result;									/* not used */
+
+	/* CheckAndOpenFile deals with the case where fname already exists */
+	fname = gui->fileselect(_("Save Connection Data As ..."),
+													_("Choose a file to save all connection data to."), default_file, ".net", "connection_data", 0);
+	if (fname == NULL)
+		return NULL;
+
+	if (default_file != NULL) {
+		free(default_file);
+		default_file = NULL;
+	}
+
+	if (fname && *fname)
+		default_file = pcb_strdup(fname);
+
+	fp = CheckAndOpenFile(fname, pcb_true, pcb_false, &result, NULL);
+	free(fname);
+
+	return fp;
+}
+
+/* ---------------------------------------------------------------------------
+ * save elements in the current buffer
+ */
+int SaveBufferElements(const char *Filename, const char *fmt)
+{
+	int result;
+
+	if (SWAP_IDENT)
+		pcb_swap_buffers();
+	result = WritePipe(Filename, pcb_false, fmt);
+	if (SWAP_IDENT)
+		pcb_swap_buffers();
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * save PCB
+ */
+int SavePCB(const char *file, const char *fmt)
+{
+	int retcode;
+
+	if (gui->notify_save_pcb == NULL)
+		return WritePipe(file, pcb_true, fmt);
+
+	gui->notify_save_pcb(file, pcb_false);
+	retcode = WritePipe(file, pcb_true, fmt);
+	gui->notify_save_pcb(file, pcb_true);
+
+	return retcode;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * Load PCB
+ */
+int LoadPCB(const char *file, const char *fmt, pcb_bool require_font, int how)
+{
+	return real_load_pcb(file, fmt, pcb_false, require_font, how);
+}
+
+/* ---------------------------------------------------------------------------
+ * Revert PCB
+ */
+int RevertPCB(void)
+{
+	return real_load_pcb(PCB->Filename, NULL, pcb_true, pcb_true, pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes the quoted string created by another subroutine
+ */
+void PrintQuotedString(FILE * FP, const char *S)
+{
+	const char *start;
+
+	fputc('"', FP);
+	for(start = S; *S != '\0'; S++) {
+		if (*S == '"' || *S == '\\') {
+			if (start != S)
+				fwrite(start, S-start, 1, FP);
+			fputc('\\', FP);
+			fputc(*S, FP);
+			start = S+1;
+		}
+	}
+
+	if (start != S)
+		fwrite(start, S-start, 1, FP);
+
+	fputc('"', FP);
+}
+
+/* ---------------------------------------------------------------------------
+ * saves the layout in a temporary file
+ * this is used for fatal errors and does not call the program specified
+ * in 'saveCommand' for safety reasons
+ */
+void SaveInTMP(void)
+{
+	char filename[256];
+
+	/* memory might have been released before this function is called */
+	if (PCB && PCB->Changed && (conf_core.rc.emergency_name != NULL) && (*conf_core.rc.emergency_name != '\0')) {
+		sprintf(filename, conf_core.rc.emergency_name, (long int)pcb_getpid());
+		Message(PCB_MSG_DEFAULT, _("Trying to save your layout in '%s'\n"), filename);
+		WritePCBFile(filename, pcb_true, DEFAULT_FMT, pcb_true);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * front-end for 'SaveInTMP()'
+ * just makes sure that the routine is only called once
+ */
+static pcb_bool dont_save_any_more = pcb_false;
+void EmergencySave(void)
+{
+
+	if (!dont_save_any_more) {
+		dont_save_any_more = pcb_true;
+		SaveInTMP();
+	}
+}
+
+void DisableEmergencySave(void)
+{
+	dont_save_any_more = pcb_true;
+}
+
+/* ----------------------------------------------------------------------
+ * Callback for the autosave
+ */
+
+static hidval backup_timer;
+
+/*
+ * If the backup interval is > 0 then set another timer.  Otherwise
+ * we do nothing and it is up to the GUI to call EnableAutosave()
+ * after setting conf_core.rc.backup_interval > 0 again.
+ */
+static void backup_cb(hidval data)
+{
+	backup_timer.ptr = NULL;
+	Backup();
+	if (conf_core.rc.backup_interval > 0 && gui->add_timer)
+		backup_timer = gui->add_timer(backup_cb, 1000 * conf_core.rc.backup_interval, data);
+}
+
+void EnableAutosave(void)
+{
+	hidval x;
+
+	x.ptr = NULL;
+
+	/* If we already have a timer going, then cancel it out */
+	if (backup_timer.ptr != NULL && gui->stop_timer)
+		gui->stop_timer(backup_timer);
+
+	backup_timer.ptr = NULL;
+	/* Start up a new timer */
+	if (conf_core.rc.backup_interval > 0 && gui->add_timer)
+		backup_timer = gui->add_timer(backup_cb, 1000 * conf_core.rc.backup_interval, x);
+}
+
+int pcb_build_fn_cb(gds_t *s, const char **input)
+{
+	char buff[20];
+
+	switch(**input) {
+		case 'P':
+			sprintf(buff, "%.8i", pcb_getpid());
+			gds_append_str(s, buff);
+			(*input)++;
+			return 0;
+		case 'F':
+			gds_append_str(s, (PCB->Filename != NULL) ? PCB->Filename : "no_file_name");
+			(*input)++;
+			return 0;
+		case 'B':
+			if (PCB->Filename != NULL) {
+				char *bn = strrchr(PCB->Filename, '/');
+				if (bn != NULL)
+					bn++;
+				else
+					bn = PCB->Filename;
+				gds_append_str(s, bn);
+			}
+			else
+				gds_append_str(s, "no_file_name");
+			(*input)++;
+			return 0;
+		case 'D':
+			if (PCB->Filename != NULL) {
+				char *bn = strrchr(PCB->Filename, '/');
+				if (bn != NULL)
+					gds_append_len(s, PCB->Filename, bn-PCB->Filename+1);
+				else
+					gds_append_str(s, "./");
+			}
+			else
+				gds_append_str(s, "./");
+			(*input)++;
+			return 0;
+		case 'N':
+			gds_append_str(s, (PCB->Name != NULL) ? PCB->Name : "no_name");
+			(*input)++;
+			return 0;
+		case 'T':
+			sprintf(buff, "%lu", (unsigned long int)time(NULL));
+			gds_append_str(s, buff);
+			(*input)++;
+			return 0;
+	}
+	return -1;
+}
+
+static char *build_fn(const char *template)
+{
+	return pcb_strdup_subst(template, pcb_build_fn_cb);
+}
+
+/* ---------------------------------------------------------------------------
+ * creates backup file.  The default is to use the pcb file name with
+ * a "-" appended (like "foo.pcb-") and if we don't have a pcb file name
+ * then use the template in conf_core.rc.backup_name
+ */
+void Backup(void)
+{
+	char *filename = NULL;
+
+	if (PCB && PCB->Filename) {
+		filename = (char *) malloc(sizeof(char) * (strlen(PCB->Filename) + 2));
+		if (filename == NULL) {
+			fprintf(stderr, "Backup():  malloc failed\n");
+			exit(1);
+		}
+		sprintf(filename, "%s-", PCB->Filename);
+	}
+	else {
+		/* conf_core.rc.backup_name has %.8i which  will be replaced by the process ID */
+		filename = build_fn(conf_core.rc.backup_name);
+		if (filename == NULL) {
+			fprintf(stderr, "Backup(): can't build file name for a backup\n");
+			exit(1);
+		}
+	}
+
+	WritePCBFile(filename, pcb_true, DEFAULT_FMT, pcb_true);
+	free(filename);
+}
+
+#if !defined(HAS_ATEXIT)
+/* ---------------------------------------------------------------------------
+ * makes a temporary copy of the data. This is useful for systems which
+ * doesn't support calling functions on exit. We use this to save the data
+ * before LEX and YACC functions are called because they are able to abort
+ * the program.
+ */
+void SaveTMPData(void)
+{
+	char *fn = build_fn(conf_core.rc.emergency_name);
+	WritePCBFile(fn, pcb_true, DEFAULT_FMT, pcb_true);
+	if (TMPFilename != NULL)
+		free(TMPFilename);
+	TMPFilename = fn;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes the temporary copy of the data file
+ */
+void RemoveTMPData(void)
+{
+	if (TMPFilename != NULL)
+		unlink(TMPFilename);
+}
+#endif
+
+/* Write the pcb file, a footprint or a buffer */
+static int pcb_write_file(FILE *fp, pcb_bool thePcb, const char *old_path, const char *new_path, const char *fmt, pcb_bool emergency)
+{
+	if (thePcb) {
+		if (PCB->is_footprint)
+			return WriteElementData(fp, PCB->Data, fmt);
+		return pcb_write_pcb(fp, old_path, new_path, fmt, emergency);
+	}
+	return WriteBuffer(fp, PASTEBUFFER, fmt);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes PCB to file
+ */
+int WritePCBFile(const char *Filename, pcb_bool thePcb, const char *fmt, pcb_bool emergency)
+{
+	FILE *fp;
+	int result, overwrite;
+	char *fn_tmp = NULL;
+
+	overwrite = pcb_file_readable(Filename);
+	if (overwrite) {
+		int len = strlen(Filename);
+		fn_tmp = malloc(len+8);
+		memcpy(fn_tmp, Filename, len);
+		strcpy(fn_tmp+len, ".old");
+		if (rename(Filename, fn_tmp) != 0) {
+			if (emergency) {
+				/* Try an alternative emergency file */
+				strcpy(fn_tmp+len, ".emr");
+				Filename = fn_tmp;
+			}
+			else {
+				Message(PCB_MSG_ERROR, "Can't rename %s to %s before save\n", Filename, fn_tmp);
+				return -1;
+			}
+		}
+	}
+
+	if ((fp = fopen(Filename, "w")) == NULL) {
+		OpenErrorMessage(Filename);
+		return (STATUS_ERROR);
+	}
+
+	result = pcb_write_file(fp, thePcb, fn_tmp, Filename, fmt, emergency);
+
+	fclose(fp);
+	if (fn_tmp != NULL) {
+		if ((result == 0) && (!conf_core.rc.keep_save_backups))
+			unlink(fn_tmp);
+		free(fn_tmp);
+	}
+	return (result);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes to pipe using the command defined by conf_core.rc.save_command
+ * %f are replaced by the passed filename
+ */
+int WritePipe(const char *Filename, pcb_bool thePcb, const char *fmt)
+{
+	FILE *fp;
+	int result;
+	const char *p;
+	static gds_t command;
+
+	if (EMPTY_STRING_P(conf_core.rc.save_command))
+		return WritePCBFile(Filename, thePcb, fmt, pcb_false);
+
+	/* setup commandline */
+	gds_truncate(&command,0);
+	for (p = conf_core.rc.save_command; *p; p++) {
+		/* copy character if not special or add string to command */
+		if (!(p[0] == '%' && p[1] == 'f'))
+			gds_append(&command, *p);
+		else {
+			gds_append_str(&command, Filename);
+
+			/* skip the character */
+			p++;
+		}
+	}
+	printf("write to pipe \"%s\"\n", command.array);
+	if ((fp = popen(command.array, "w")) == NULL) {
+		PopenErrorMessage(command.array);
+		return (STATUS_ERROR);
+	}
+
+	result = pcb_write_file(fp, thePcb, NULL, NULL, fmt, pcb_false);
+
+	return (pclose(fp) ? STATUS_ERROR : result);
+}
+
+
+int pcb_io_list(pcb_io_formats_t *out, plug_iot_t typ, int wr, int do_digest)
+{
+	pcb_find_io_t available[PCB_IO_MAX_FORMATS];
+	int n;
+
+	memset(out, 0, sizeof(pcb_io_formats_t));
+	out->len = pcb_find_io(available, sizeof(available)/sizeof(available[0]), typ, 1, NULL);
+
+	if (out->len == 0)
+		return 0;
+
+	for(n = 0; n < out->len; n++)
+		out->plug[n] = available[n].plug;
+
+	if (do_digest) {
+		for(n = 0; n < out->len; n++)
+			out->digest[n] = pcb_strdup_printf("%s (%s)", out->plug[n]->default_fmt, out->plug[n]->description);
+		out->digest[n] = NULL;
+	}
+	return out->len;
+}
+
+void pcb_io_list_free(pcb_io_formats_t *out)
+{
+	int n;
+
+	for(n = 0; n < out->len; n++) {
+		if (out->digest[n] != NULL) {
+			free(out->digest[n]);
+			out->digest[n] = NULL;
+		}
+	}
+}
diff --git a/src/plug_io.h b/src/plug_io.h
new file mode 100644
index 0000000..d28c08c
--- /dev/null
+++ b/src/plug_io.h
@@ -0,0 +1,170 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#ifndef PCB_PLUG_IO_H
+#define PCB_PLUG_IO_H
+
+#include "global.h"
+#include "conf.h"
+
+typedef enum { /* I/O type bitmask; each bit is one thing to save or load, not all formats support all things */
+	PCB_IOT_PCB        = 1,
+	PCB_IOT_FOOTPRINT  = 2,
+	PCB_IOT_FONT       = 4,
+	PCB_IOT_BUFFER     = 8
+} plug_iot_t;
+
+/**************************** API definition *********************************/
+struct plug_io_s {
+	plug_io_t *next;
+	void *plugin_data;
+
+	/* Check if the plugin supports format fmt, for writing (if wr != 0) or for
+	   reading (if wr == 0) for I/O type typ. Return 0 if not supported or an
+	   integer priority if supported. The higher the prio is the more likely
+	   the plugin gets the next operation on the file. Base prio should be
+	   100 for native formats.
+	   NOTE: if any format can be chosen for output, the user will have to pick
+	         one; for ordering plugins in that case, the format-neutral
+	         save_preference_prio field is used.
+	   */
+	int (*fmt_support_prio)(plug_io_t *ctx, plug_iot_t typ, int wr, const char *fmt);
+
+	/* Attempt to load a pcb design from Filename to Ptr.
+	   Conf subtree at settings_dest is replaced by settings loaded from the
+	   file unless it's CFR_invalid.
+	   Return 0 on success. */
+	int (*parse_pcb)(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest);
+
+	/* Attempt to load an element from Filename to Ptr. Return 0 on success. */
+	int (*parse_element)(plug_io_t *ctx, DataTypePtr Ptr, const char *name);
+
+	/* Attempt to load fonts from a file. Return 0 on success. */
+	int (*parse_font)(plug_io_t *ctx, FontTypePtr Ptr, const char *Filename);
+
+
+	/* Write the buffer to a file. Return 0 on success. */
+	int (*write_buffer)(plug_io_t *ctx, FILE *f, BufferType *buff);
+
+	/* Write element data to a file. Return 0 on success. */
+	int (*write_element)(plug_io_t *ctx, FILE *f, DataTypePtr e);
+
+	/* Write PCB to f; there's a copy of the file we are going to
+	   "overwrite", named in old_filename and the new file name we are
+	   using is new_filename. The file names are NULL if we are saving
+	   into a pipe. If emergency is true, do the safest save possible,
+	   don't mind formatting and extras.
+	   Return 0 on success. */
+	int (*write_pcb)(plug_io_t *ctx, FILE *f, const char *old_filename, const char *new_filename, pcb_bool emergency);
+
+	const char *default_fmt;
+	const char *description;
+
+	int save_preference_prio; /* all available save plugins are sorted by this before presenting them to the user to choose one */
+};
+extern plug_io_t *plug_io_chain;
+
+
+/********** hook wrappers **********/
+int ParsePCB(PCBTypePtr Ptr, const char *Filename, const char *fmt, int load_settings);
+int ParseElement(DataTypePtr Ptr, const char *name);
+int ParseFont(FontTypePtr Ptr, char *Filename);
+int WriteBuffer(FILE *f, BufferType *buff, const char *fmt);
+int WriteElementData(FILE *f, DataTypePtr e, const char *fmt);
+
+/********** common function used to be part of file.[ch] and friends **********/
+FILE *CheckAndOpenFile(const char *, pcb_bool, pcb_bool, pcb_bool *, pcb_bool *);
+FILE *OpenConnectionDataFile(void);
+int SavePCB(const char *, const char *fmt);
+int LoadPCB(const char *name, const char *fmt, pcb_bool, int how); /* how: 0=normal pcb; 1=default.pcb, 2=misc (do not load settings) */
+void EnableAutosave(void);
+void Backup(void);
+void SaveInTMP(void);
+void EmergencySave(void);
+void DisableEmergencySave(void);
+int RevertPCB(void);
+int SaveBufferElements(const char *, const char *fmt);
+void pcb_sort_netlist(void);
+void PrintQuotedString(FILE *, const char *);
+void sort_library(LibraryTypePtr lib);
+void set_some_route_style();
+int WritePCBFile(const char *Filename, pcb_bool thePcb, const char *fmt, pcb_bool emergency);
+int WritePipe(const char *, pcb_bool, const char *fmt);
+
+#ifndef HAS_ATEXIT
+void SaveTMPData(void);
+void RemoveTMPData(void);
+#endif
+
+/********** helpers **********/
+
+/* wish we had more than 32 IO plugins... */
+#define PCB_IO_MAX_FORMATS 32
+
+/* A list of format plugins available for a given purpose */
+typedef struct {
+	int len;
+	const plug_io_t *plug[PCB_IO_MAX_FORMATS+1];
+	char *digest[PCB_IO_MAX_FORMATS+1];           /* string that contains the format identifier and the description */
+} pcb_io_formats_t;
+
+/* Search all io plugins to see if typ/wr is supported. Return an ordered
+   list in out. If do_digest is non-zero, fill in the digest field. Returns
+   number of suitable io plugins. Call pcb_io_list_free() on out when it is not
+   needed anymore. */
+int pcb_io_list(pcb_io_formats_t *out, plug_iot_t typ, int wr, int do_digest);
+void pcb_io_list_free(pcb_io_formats_t *out);
+
+extern int pcb_io_err_inhibit;
+#define pcb_io_err_inhibit_inc() pcb_io_err_inhibit++
+#define pcb_io_err_inhibit_dec() \
+do { \
+	if (pcb_io_err_inhibit > 0) \
+		pcb_io_err_inhibit--; \
+} while(0)
+
+/* Find all plugins that can handle typ/is_wr and build an ordered list in available[],
+   highest prio first. If fmt is NULL, use the default fmt for each plugin.
+   Return the length of the array. */
+typedef struct {
+	plug_io_t *plug;
+	int prio;
+} pcb_find_io_t;
+int pcb_find_io(pcb_find_io_t *available, int avail_len, plug_iot_t typ, int is_wr, const char *fmt);
+
+
+/* generic file name template substitution callbacks for pcb_strdup_subst:
+    %P    pid
+    %F    load-time file name of the current pcb
+    %B    basename (load-time file name of the current pcb without path)
+    %D    dirname (load-time file path of the current pcb, without file name, with trailing slash, might be ./)
+    %N    name of the current pcb
+    %T    wall time (Epoch)
+*/
+int pcb_build_fn_cb(gds_t *s, const char **input);
+
+#endif
diff --git a/src/plugins.c b/src/plugins.c
new file mode 100644
index 0000000..0208493
--- /dev/null
+++ b/src/plugins.c
@@ -0,0 +1,141 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2015 Tibor 'Igor2' Palinkas
+ *
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "plugins.h"
+
+/* for the action */
+#include "global.h"
+#include "config.h"
+#include "genvector/gds_char.h"
+#include "compat_misc.h"
+
+plugin_info_t *plugins = NULL;
+
+plugin_info_t *plugin_find(const char *name)
+{
+	plugin_info_t *i;
+	for(i = plugins; i != NULL; i = i->next)
+		if (strcmp(i->name, name) == 0)
+			return i;
+	return NULL;
+}
+
+void plugin_register(const char *name, const char *path, void *handle, int dynamic_loaded, void (*uninit)(void))
+{
+	plugin_info_t *i = malloc(sizeof(plugin_info_t));
+
+	i->name = pcb_strdup(name);
+	i->path = pcb_strdup(path);
+	i->handle = handle;
+	i->dynamic_loaded = dynamic_loaded;
+	i->uninit = uninit;
+
+	i->next = plugins;
+	plugins = i;
+}
+
+void plugins_init(void)
+{
+}
+
+
+void plugins_uninit(void)
+{
+	plugin_info_t *i, *next;
+	for(i = plugins; i != NULL; i = next) {
+		next = i->next;
+		free(i->name);
+		free(i->path);
+		if (i->uninit != NULL)
+			i->uninit();
+		free(i);
+	}
+	plugins = NULL;
+}
+
+
+
+/* ---------------------------------------------------------------- */
+static const char manageplugins_syntax[] = "ManagePlugins()\n";
+
+static const char manageplugins_help[] = "Manage plugins dialog.";
+
+static int ActionManagePlugins(int argc, const char **argv, Coord x, Coord y)
+{
+	plugin_info_t *i;
+	int nump = 0, numb = 0;
+	gds_t str;
+
+	gds_init(&str);
+
+	for (i = plugins; i != NULL; i = i->next)
+		if (i->dynamic_loaded)
+			nump++;
+		else
+			numb++;
+
+	gds_append_str(&str, "Plugins loaded:\n");
+	if (nump > 0) {
+		for (i = plugins; i != NULL; i = i->next) {
+			if (i->dynamic_loaded) {
+				gds_append(&str, ' ');
+				gds_append_str(&str, i->name);
+				gds_append(&str, ' ');
+				gds_append_str(&str, i->path);
+				gds_append(&str, '\n');
+			}
+		}
+	}
+	else
+		gds_append_str(&str, " (none)\n");
+
+	gds_append_str(&str, "\n\nBuildins:\n");
+	if (numb > 0) {
+		for (i = plugins; i != NULL; i = i->next) {
+			if (!i->dynamic_loaded) {
+				gds_append(&str, ' ');
+				gds_append_str(&str, i->name);
+				gds_append(&str, '\n');
+			}
+		}
+	}
+	else
+		gds_append_str(&str, " (none)\n");
+
+	gds_append_str(&str, "\n\nNOTE: this is the alpha version, can only list plugins/buildins\n");
+	gui->report_dialog("Manage plugins", str.array);
+	gds_uninit(&str);
+	return 0;
+}
+
+
+HID_Action plugins_action_list[] = {
+	{"ManagePlugins", 0, ActionManagePlugins,
+	 manageplugins_help, manageplugins_syntax}
+};
+
+REGISTER_ACTIONS(plugins_action_list, NULL)
diff --git a/src/plugins.h b/src/plugins.h
new file mode 100644
index 0000000..0838d69
--- /dev/null
+++ b/src/plugins.h
@@ -0,0 +1,108 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2015 Tibor 'Igor2' Palinkas
+ *
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_RND_PLUGINS_H
+#define PCB_RND_PLUGINS_H
+
+typedef struct plugin_info_s plugin_info_t;
+
+typedef void (*pcb_uninit_t)(void);
+
+struct plugin_info_s {
+	char *name;
+	char *path;
+	void *handle;
+	int dynamic_loaded;
+	pcb_uninit_t uninit;
+	plugin_info_t *next;
+};
+
+extern plugin_info_t *plugins;
+
+/* Init the plugin system */
+void plugins_init(void);
+
+/* Uninit each plugin then uninit the plugin system */
+void plugins_uninit(void);
+
+/* Register a new plugin (or buildin) */
+void plugin_register(const char *name, const char *path, void *handle, int dynamic, pcb_uninit_t uninit);
+
+/* Find a plugin by name */
+plugin_info_t *plugin_find(const char *name);
+
+/* Hook based plugin generics; plugins that implement a common API should use
+   HOOK_REGISTER with an api struct. The core should run the plugins using
+   HOOK_CALL */
+
+#define HOOK_CALL_DO(chain_type, chain, func, res, accept, funcargs, do_on_success) \
+do { \
+	chain_type *self; \
+	for(self = (chain); self != NULL; self = self->next) { \
+		if (self->func == NULL) \
+			continue; \
+		res = self->func funcargs; \
+		if (res accept) {\
+			do_on_success; \
+			break; \
+		} \
+	} \
+} while(0)
+
+#define HOOK_CALL(chain_type, chain, func, res, accept, funcargs) \
+	HOOK_CALL_DO(chain_type, chain, func, res, accept, funcargs, (void)0)
+
+#define HOOK_CALL_ALL(chain_type, chain, func, cb, funcargs) \
+do { \
+	chain_type *self; \
+	for(self = (chain); self != NULL; self = self->next) { \
+		int res; \
+		if (self->func == NULL) \
+			continue; \
+		res = self->func funcargs; \
+		cb(self, res); \
+	} \
+} while(0)
+
+#define HOOK_REGISTER(chain_type, chain, hstruct) \
+do { \
+	(hstruct)->next = chain; \
+	chain = (hstruct); \
+} while(0)
+
+
+#define HOOK_UNREGISTER(chain_type, chain, hstruct) \
+do { \
+	chain_type *__n__, *__prev__ = NULL, *__h__ = (hstruct); \
+	for(__n__ = chain; __n__ != NULL; __n__ = __n__->next) { \
+		if ((__n__ == __h__) && (__prev__ != NULL)) \
+			__prev__->next = __n__->next; \
+		__prev__ = __n__; \
+	} \
+	if (chain == __n__) \
+		chain = chain->next; \
+} while(0)
+
+#endif
diff --git a/src/polyarea.h b/src/polyarea.h
new file mode 100644
index 0000000..fa0801f
--- /dev/null
+++ b/src/polyarea.h
@@ -0,0 +1,180 @@
+/*
+      poly_Boolean: a polygon clip library
+      Copyright (C) 1997  Alexey Nikitin, Michael Leonov
+      leonov at propro.iis.nsk.su
+
+      This library is free software; you can redistribute it and/or
+      modify it under the terms of the GNU Library General Public
+      License as published by the Free Software Foundation; either
+      version 2 of the License, or (at your option) any later version.
+
+      This library is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+      Library General Public License for more details.
+
+      You should have received a copy of the GNU Library General Public
+      License along with this library; if not, write to the Free
+      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+      polyarea.h
+      (C) 1997 Alexey Nikitin, Michael Leonov
+      (C) 1997 Klamer Schutte (minor patches)
+*/
+
+#ifndef	PCB_POLYAREA_H
+#define	PCB_POLYAREA_H
+
+typedef int BOOLp;
+
+#ifndef FALSE
+enum {
+	FALSE = 0,
+	TRUE = 1
+};
+#endif
+
+#define PLF_DIR 1
+#define PLF_INV 0
+#define PLF_MARK 1
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef max
+#define max(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+
+typedef Coord vertex[2];				/* longing point representation of
+																   coordinates */
+typedef vertex Vector;
+
+#define VertexEqu(a,b) (memcmp((a),(b),sizeof(Vector))==0)
+#define VertexCpy(a,b) memcpy((a),(b),sizeof(Vector))
+
+
+extern Vector vect_zero;
+
+enum {
+	err_no_memory = 2,
+	err_bad_parm = 3,
+	err_ok = 0
+};
+
+
+typedef struct CVCList CVCList;
+typedef struct VNODE VNODE;
+struct CVCList {
+	double angle;
+	VNODE *parent;
+	CVCList *prev, *next, *head;
+	char poly, side;
+};
+struct VNODE {
+	VNODE *next, *prev, *shared;
+	struct {
+		unsigned int status:3;
+		unsigned int mark:1;
+	} Flags;
+	CVCList *cvc_prev;
+	CVCList *cvc_next;
+	Vector point;
+};
+
+typedef struct PLINE PLINE;
+struct PLINE {
+	Coord xmin, ymin, xmax, ymax;
+	PLINE *next;
+	VNODE head;
+	unsigned int Count;
+	double area;
+	rtree_t *tree;
+	pcb_bool is_round;
+	Coord cx, cy;
+	Coord radius;
+	struct {
+		unsigned int status:3;
+		unsigned int orient:1;
+	} Flags;
+};
+
+PLINE *poly_NewContour(Vector v);
+
+void poly_IniContour(PLINE * c);
+void poly_ClrContour(PLINE * c);	/* clears list of vertices */
+void poly_DelContour(PLINE ** c);
+
+BOOLp poly_CopyContour(PLINE ** dst, PLINE * src);
+
+void poly_PreContour(PLINE * c, BOOLp optimize);	/* prepare contour */
+void poly_InvContour(PLINE * c);	/* invert contour */
+
+VNODE *poly_CreateNode(Vector v);
+
+void poly_InclVertex(VNODE * after, VNODE * node);
+void poly_ExclVertex(VNODE * node);
+
+/**********************************************************************/
+
+typedef struct POLYAREA POLYAREA;
+struct POLYAREA {
+	POLYAREA *f, *b;
+	PLINE *contours;
+	rtree_t *contour_tree;
+};
+
+BOOLp poly_M_Copy0(POLYAREA ** dst, const POLYAREA * srcfst);
+void poly_M_Incl(POLYAREA ** list, POLYAREA * a);
+
+BOOLp poly_Copy0(POLYAREA ** dst, const POLYAREA * src);
+BOOLp poly_Copy1(POLYAREA * dst, const POLYAREA * src);
+
+BOOLp poly_InclContour(POLYAREA * p, PLINE * c);
+BOOLp poly_ExclContour(POLYAREA * p, PLINE * c);
+
+
+BOOLp poly_ChkContour(PLINE * a);
+
+BOOLp poly_CheckInside(POLYAREA * c, Vector v0);
+BOOLp Touching(POLYAREA * p1, POLYAREA * p2);
+
+/**********************************************************************/
+
+/* tools for clipping */
+
+/* checks whether point lies within contour
+independently of its orientation */
+
+int poly_InsideContour(PLINE * c, Vector v);
+int poly_ContourInContour(PLINE * poly, PLINE * inner);
+POLYAREA *poly_Create(void);
+
+void poly_Free(POLYAREA ** p);
+void poly_Init(POLYAREA * p);
+void poly_FreeContours(PLINE ** pl);
+BOOLp poly_Valid(POLYAREA * p);
+
+enum PolygonBooleanOperation {
+	PBO_UNITE,
+	PBO_ISECT,
+	PBO_SUB,
+	PBO_XOR
+};
+
+double vect_dist2(Vector v1, Vector v2);
+double vect_det2(Vector v1, Vector v2);
+double vect_len2(Vector v1);
+
+int vect_inters2(Vector A, Vector B, Vector C, Vector D, Vector S1, Vector S2);
+
+int poly_Boolean(const POLYAREA * a, const POLYAREA * b, POLYAREA ** res, int action);
+int poly_Boolean_free(POLYAREA * a, POLYAREA * b, POLYAREA ** res, int action);
+int poly_AndSubtract_free(POLYAREA * a, POLYAREA * b, POLYAREA ** aandb, POLYAREA ** aminusb);
+int SavePOLYAREA(POLYAREA * PA, char *fname);
+
+/* calculate the bounding box of a POLYAREA and save result in b */
+void poly_bbox(POLYAREA * p, BoxType * b);
+
+#endif /* PCB_POLYAREA_H */
diff --git a/src/polygon.c b/src/polygon.c
new file mode 100644
index 0000000..1b3f86c
--- /dev/null
+++ b/src/polygon.c
@@ -0,0 +1,1846 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2010 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/*
+
+Here's a brief tour of the data and life of a polygon, courtesy of Ben
+Jackson:
+
+A PCB PolygonType contains an array of points outlining the polygon.
+This is what is manipulated by the UI and stored in the saved PCB.
+
+A PolygonType also contains a POLYAREA called 'Clipped' which is
+computed dynamically by InitClip every time a board is loaded.  The
+point array is converted to a POLYAREA by original_poly and then holes
+are cut in it by clearPoly.  After that it is maintained dynamically
+as parts are added, moved or removed (this is why sometimes bugs can
+be fixed by just re-loading the board).
+
+A POLYAREA consists of a linked list of PLINE structures.  The head of
+that list is POLYAREA.contours.  The first contour is an outline of a
+filled region.  All of the subsequent PLINEs are holes cut out of that
+first contour.  POLYAREAs are in a doubly-linked list and each member
+of the list is an independent (non-overlapping) area with its own
+outline and holes.  The function biggest() finds the largest POLYAREA
+so that PolygonType.Clipped points to that shape.  The rest of the
+polygon still exists, it's just ignored when turning the polygon into
+copper.
+
+The first POLYAREA in PolygonType.Clipped is what is used for the vast
+majority of Polygon related tests.  The basic logic for an
+intersection is "is the target shape inside POLYAREA.contours and NOT
+fully enclosed in any of POLYAREA.contours.next... (the holes)".
+
+The polygon dicer (NoHolesPolygonDicer and r_NoHolesPolygonDicer)
+emits a series of "simple" PLINE shapes.  That is, the PLINE isn't
+linked to any other "holes" oulines).  That's the meaning of the first
+test in r_NoHolesPolygonDicer.  It is testing to see if the PLINE
+contour (the first, making it a solid outline) has a valid next
+pointer (which would point to one or more holes).  The dicer works by
+recursively chopping the polygon in half through the first hole it
+sees (which is guaranteed to eliminate at least that one hole).  The
+dicer output is used for HIDs which cannot render things with holes
+(which would require erasure).
+
+*/
+
+/* special polygon editing routines
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <assert.h>
+#include <math.h>
+#include <memory.h>
+#include <setjmp.h>
+
+#include "box.h"
+#include "create.h"
+#include "crosshair.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "polygon.h"
+#include "remove.h"
+#include "rtree.h"
+#include "search.h"
+#include "set.h"
+#include "thermal.h"
+#include "undo.h"
+#include "misc.h"
+#include "layer.h"
+
+#define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5  : (x) - 0.5)))
+
+#define UNSUBTRACT_BLOAT 10
+#define SUBTRACT_PIN_VIA_BATCH_SIZE 100
+#define SUBTRACT_LINE_BATCH_SIZE 20
+
+static double rotate_circle_seg[4];
+
+void polygon_init(void)
+{
+	double cos_ang = cos(2.0 * M_PI / POLY_CIRC_SEGS_F);
+	double sin_ang = sin(2.0 * M_PI / POLY_CIRC_SEGS_F);
+
+	rotate_circle_seg[0] = cos_ang;
+	rotate_circle_seg[1] = -sin_ang;
+	rotate_circle_seg[2] = sin_ang;
+	rotate_circle_seg[3] = cos_ang;
+}
+
+pcb_cardinal_t polygon_point_idx(PolygonTypePtr polygon, PointTypePtr point)
+{
+	assert(point >= polygon->Points);
+	assert(point <= polygon->Points + polygon->PointN);
+	return ((char *) point - (char *) polygon->Points) / sizeof(PointType);
+}
+
+/* Find contour number: 0 for outer, 1 for first hole etc.. */
+pcb_cardinal_t polygon_point_contour(PolygonTypePtr polygon, pcb_cardinal_t point)
+{
+	pcb_cardinal_t i;
+	pcb_cardinal_t contour = 0;
+
+	for (i = 0; i < polygon->HoleIndexN; i++)
+		if (point >= polygon->HoleIndex[i])
+			contour = i + 1;
+	return contour;
+}
+
+pcb_cardinal_t next_contour_point(PolygonTypePtr polygon, pcb_cardinal_t point)
+{
+	pcb_cardinal_t contour;
+	pcb_cardinal_t this_contour_start;
+	pcb_cardinal_t next_contour_start;
+
+	contour = polygon_point_contour(polygon, point);
+
+	this_contour_start = (contour == 0) ? 0 : polygon->HoleIndex[contour - 1];
+	next_contour_start = (contour == polygon->HoleIndexN) ? polygon->PointN : polygon->HoleIndex[contour];
+
+	/* Wrap back to the start of the contour we're in if we pass the end */
+	if (++point == next_contour_start)
+		point = this_contour_start;
+
+	return point;
+}
+
+pcb_cardinal_t prev_contour_point(PolygonTypePtr polygon, pcb_cardinal_t point)
+{
+	pcb_cardinal_t contour;
+	pcb_cardinal_t prev_contour_end;
+	pcb_cardinal_t this_contour_end;
+
+	contour = polygon_point_contour(polygon, point);
+
+	prev_contour_end = (contour == 0) ? 0 : polygon->HoleIndex[contour - 1];
+	this_contour_end = (contour == polygon->HoleIndexN) ? polygon->PointN - 1 : polygon->HoleIndex[contour] - 1;
+
+	/* Wrap back to the start of the contour we're in if we pass the end */
+	if (point == prev_contour_end)
+		point = this_contour_end;
+	else
+		point--;
+
+	return point;
+}
+
+static void add_noholes_polyarea(PLINE * pline, void *user_data)
+{
+	PolygonType *poly = (PolygonType *) user_data;
+
+	/* Prepend the pline into the NoHoles linked list */
+	pline->next = poly->NoHoles;
+	poly->NoHoles = pline;
+}
+
+void ComputeNoHoles(PolygonType * poly)
+{
+	poly_FreeContours(&poly->NoHoles);
+	if (poly->Clipped)
+		NoHolesPolygonDicer(poly, NULL, add_noholes_polyarea, poly);
+	else
+		printf("Compute_noholes caught poly->Clipped = NULL\n");
+	poly->NoHolesValid = 1;
+}
+
+static POLYAREA *biggest(POLYAREA * p)
+{
+	POLYAREA *n, *top = NULL;
+	PLINE *pl;
+	rtree_t *tree;
+	double big = -1;
+	if (!p)
+		return NULL;
+	n = p;
+	do {
+#if 0
+		if (n->contours->area < PCB->IsleArea) {
+			n->b->f = n->f;
+			n->f->b = n->b;
+			poly_DelContour(&n->contours);
+			if (n == p)
+				p = n->f;
+			n = n->f;
+			if (!n->contours) {
+				free(n);
+				return NULL;
+			}
+		}
+#endif
+		if (n->contours->area > big) {
+			top = n;
+			big = n->contours->area;
+		}
+	}
+	while ((n = n->f) != p);
+	assert(top);
+	if (top == p)
+		return p;
+	pl = top->contours;
+	tree = top->contour_tree;
+	top->contours = p->contours;
+	top->contour_tree = p->contour_tree;
+	p->contours = pl;
+	p->contour_tree = tree;
+	assert(pl);
+	assert(p->f);
+	assert(p->b);
+	return p;
+}
+
+POLYAREA *ContourToPoly(PLINE * contour)
+{
+	POLYAREA *p;
+	poly_PreContour(contour, TRUE);
+	assert(contour->Flags.orient == PLF_DIR);
+	if (!(p = poly_Create()))
+		return NULL;
+	poly_InclContour(p, contour);
+	assert(poly_Valid(p));
+	return p;
+}
+
+static POLYAREA *original_poly(PolygonType * p)
+{
+	PLINE *contour = NULL;
+	POLYAREA *np = NULL;
+	pcb_cardinal_t n;
+	Vector v;
+	int hole = 0;
+
+	if ((np = poly_Create()) == NULL)
+		return NULL;
+
+	/* first make initial polygon contour */
+	for (n = 0; n < p->PointN; n++) {
+		/* No current contour? Make a new one starting at point */
+		/*   (or) Add point to existing contour */
+
+		v[0] = p->Points[n].X;
+		v[1] = p->Points[n].Y;
+		if (contour == NULL) {
+			if ((contour = poly_NewContour(v)) == NULL)
+				return NULL;
+		}
+		else {
+			poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+		}
+
+		/* Is current point last in contour? If so process it. */
+		if (n == p->PointN - 1 || (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1)) {
+			poly_PreContour(contour, TRUE);
+
+			/* make sure it is a positive contour (outer) or negative (hole) */
+			if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
+				poly_InvContour(contour);
+			assert(contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
+
+			poly_InclContour(np, contour);
+			contour = NULL;
+			assert(poly_Valid(np));
+
+			hole++;
+		}
+	}
+	return biggest(np);
+}
+
+POLYAREA *PolygonToPoly(PolygonType * p)
+{
+	return original_poly(p);
+}
+
+POLYAREA *RectPoly(Coord x1, Coord x2, Coord y1, Coord y2)
+{
+	PLINE *contour = NULL;
+	Vector v;
+
+	/* Return NULL for zero or negatively sized rectangles */
+	if (x2 <= x1 || y2 <= y1)
+		return NULL;
+
+	v[0] = x1;
+	v[1] = y1;
+	if ((contour = poly_NewContour(v)) == NULL)
+		return NULL;
+	v[0] = x2;
+	v[1] = y1;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	v[0] = x2;
+	v[1] = y2;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	v[0] = x1;
+	v[1] = y2;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	return ContourToPoly(contour);
+}
+
+/* set up x and y multiplier for an octa poly, depending on square pin style */
+void square_pin_factors(int style, double *xm, double *ym)
+{
+	int i;
+	const double factor = 2.0;
+
+	/* reset multipliers */
+	for (i = 0; i < 8; i++) {
+		xm[i] = 1;
+		ym[i] = 1;
+	}
+
+	style--;
+	if (style & 1)
+		xm[0] = xm[1] = xm[6] = xm[7] = factor;
+	if (style & 2)
+		xm[2] = xm[3] = xm[4] = xm[5] = factor;
+	if (style & 4)
+		ym[4] = ym[5] = ym[6] = ym[7] = factor;
+	if (style & 8)
+		ym[0] = ym[1] = ym[2] = ym[3] = factor;
+}
+
+
+POLYAREA *OctagonPoly(Coord x, Coord y, Coord radius, int style)
+{
+	PLINE *contour = NULL;
+	Vector v;
+	double xm[8], ym[8];
+
+	square_pin_factors(style, xm, ym);
+
+#warning TODO: rewrite this to use the same table as the square/oct pin draw function
+	/* point 7 */
+	v[0] = x + ROUND(radius * 0.5) * xm[7];
+	v[1] = y + ROUND(radius * PCB_TAN_22_5_DEGREE_2) * ym[7];
+	if ((contour = poly_NewContour(v)) == NULL)
+		return NULL;
+	/* point 6 */
+	v[0] = x + ROUND(radius * PCB_TAN_22_5_DEGREE_2) * xm[6];
+	v[1] = y + ROUND(radius * 0.5) * ym[6];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	/* point 5 */
+	v[0] = x - ROUND(radius * PCB_TAN_22_5_DEGREE_2) * xm[5];
+	v[1] = y + ROUND(radius * 0.5) * ym[5];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	/* point 4 */
+	v[0] = x - ROUND(radius * 0.5) * xm[4];
+	v[1] = y + ROUND(radius * PCB_TAN_22_5_DEGREE_2) * ym[4];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	/* point 3 */
+	v[0] = x - ROUND(radius * 0.5) * xm[3];
+	v[1] = y - ROUND(radius * PCB_TAN_22_5_DEGREE_2) * ym[3];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	/* point 2 */
+	v[0] = x - ROUND(radius * PCB_TAN_22_5_DEGREE_2) * xm[2];
+	v[1] = y - ROUND(radius * 0.5) * ym[2];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	/* point 1 */
+	v[0] = x + ROUND(radius * PCB_TAN_22_5_DEGREE_2) * xm[1];
+	v[1] = y - ROUND(radius * 0.5) * ym[1];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	/* point 0 */
+	v[0] = x + ROUND(radius * 0.5) * xm[0];
+	v[1] = y - ROUND(radius * PCB_TAN_22_5_DEGREE_2) * ym[0];
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	return ContourToPoly(contour);
+}
+
+/* add vertices in a fractional-circle starting from v
+ * centered at X, Y and going counter-clockwise
+ * does not include the first point
+ * last argument is 1 for a full circle
+ * 2 for a half circle
+ * or 4 for a quarter circle
+ */
+void frac_circle(PLINE * c, Coord X, Coord Y, Vector v, int range)
+{
+	double e1, e2, t1;
+	int i;
+
+	poly_InclVertex(c->head.prev, poly_CreateNode(v));
+	/* move vector to origin */
+	e1 = (v[0] - X) * POLY_CIRC_RADIUS_ADJ;
+	e2 = (v[1] - Y) * POLY_CIRC_RADIUS_ADJ;
+
+	/* NB: the caller adds the last vertex, hence the -1 */
+	range = POLY_CIRC_SEGS / range - 1;
+	for (i = 0; i < range; i++) {
+		/* rotate the vector */
+		t1 = rotate_circle_seg[0] * e1 + rotate_circle_seg[1] * e2;
+		e2 = rotate_circle_seg[2] * e1 + rotate_circle_seg[3] * e2;
+		e1 = t1;
+		v[0] = X + ROUND(e1);
+		v[1] = Y + ROUND(e2);
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+	}
+}
+
+/* create a circle approximation from lines */
+POLYAREA *CirclePoly(Coord x, Coord y, Coord radius)
+{
+	PLINE *contour;
+	Vector v;
+
+	if (radius <= 0)
+		return NULL;
+	v[0] = x + radius;
+	v[1] = y;
+	if ((contour = poly_NewContour(v)) == NULL)
+		return NULL;
+	frac_circle(contour, x, y, v, 1);
+	contour->is_round = TRUE;
+	contour->cx = x;
+	contour->cy = y;
+	contour->radius = radius;
+	return ContourToPoly(contour);
+}
+
+/* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
+POLYAREA *RoundRect(Coord x1, Coord x2, Coord y1, Coord y2, Coord t)
+{
+	PLINE *contour = NULL;
+	Vector v;
+
+	assert(x2 > x1);
+	assert(y2 > y1);
+	v[0] = x1 - t;
+	v[1] = y1;
+	if ((contour = poly_NewContour(v)) == NULL)
+		return NULL;
+	frac_circle(contour, x1, y1, v, 4);
+	v[0] = x2;
+	v[1] = y1 - t;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	frac_circle(contour, x2, y1, v, 4);
+	v[0] = x2 + t;
+	v[1] = y2;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	frac_circle(contour, x2, y2, v, 4);
+	v[0] = x1;
+	v[1] = y2 + t;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	frac_circle(contour, x1, y2, v, 4);
+	return ContourToPoly(contour);
+}
+
+#define ARC_ANGLE 5
+static POLYAREA *ArcPolyNoIntersect(ArcType * a, Coord thick)
+{
+	PLINE *contour = NULL;
+	POLYAREA *np = NULL;
+	Vector v;
+	BoxType *ends;
+	int i, segs;
+	double ang, da, rx, ry;
+	long half;
+	double radius_adj;
+
+	if (thick <= 0)
+		return NULL;
+	if (a->Delta < 0) {
+		a->StartAngle += a->Delta;
+		a->Delta = -a->Delta;
+	}
+	half = (thick + 1) / 2;
+	ends = GetArcEnds(a);
+	/* start with inner radius */
+	rx = MAX(a->Width - half, 0);
+	ry = MAX(a->Height - half, 0);
+	segs = 1;
+	if (thick > 0)
+		segs = MAX(segs, a->Delta * M_PI / 360 *
+							 sqrt(sqrt((double) rx * rx + (double) ry * ry) / POLY_ARC_MAX_DEVIATION / 2 / thick));
+	segs = MAX(segs, a->Delta / ARC_ANGLE);
+
+	ang = a->StartAngle;
+	da = (1.0 * a->Delta) / segs;
+	radius_adj = (M_PI * da / 360) * (M_PI * da / 360) / 2;
+	v[0] = a->X - rx * cos(ang * PCB_M180);
+	v[1] = a->Y + ry * sin(ang * PCB_M180);
+	if ((contour = poly_NewContour(v)) == NULL)
+		return 0;
+	for (i = 0; i < segs - 1; i++) {
+		ang += da;
+		v[0] = a->X - rx * cos(ang * PCB_M180);
+		v[1] = a->Y + ry * sin(ang * PCB_M180);
+		poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	}
+	/* find last point */
+	ang = a->StartAngle + a->Delta;
+	v[0] = a->X - rx * cos(ang * PCB_M180) * (1 - radius_adj);
+	v[1] = a->Y + ry * sin(ang * PCB_M180) * (1 - radius_adj);
+	/* add the round cap at the end */
+	frac_circle(contour, ends->X2, ends->Y2, v, 2);
+	/* and now do the outer arc (going backwards) */
+	rx = (a->Width + half) * (1 + radius_adj);
+	ry = (a->Width + half) * (1 + radius_adj);
+	da = -da;
+	for (i = 0; i < segs; i++) {
+		v[0] = a->X - rx * cos(ang * PCB_M180);
+		v[1] = a->Y + ry * sin(ang * PCB_M180);
+		poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+		ang += da;
+	}
+	/* now add other round cap */
+	ang = a->StartAngle;
+	v[0] = a->X - rx * cos(ang * PCB_M180) * (1 - radius_adj);
+	v[1] = a->Y + ry * sin(ang * PCB_M180) * (1 - radius_adj);
+	frac_circle(contour, ends->X1, ends->Y1, v, 2);
+	/* now we have the whole contour */
+	if (!(np = ContourToPoly(contour)))
+		return NULL;
+	return np;
+}
+
+#define MIN_CLEARANCE_BEFORE_BISECT 10.
+POLYAREA *ArcPoly(ArcType * a, Coord thick)
+{
+	double delta;
+	ArcType seg1, seg2;
+	POLYAREA *tmp1, *tmp2, *res;
+
+	delta = (a->Delta < 0) ? -a->Delta : a->Delta;
+
+	/* If the arc segment would self-intersect, we need to construct it as the union of
+	   two non-intersecting segments */
+
+	if (2 * M_PI * a->Width * (1. - (double) delta / 360.) - thick < MIN_CLEARANCE_BEFORE_BISECT) {
+		int half_delta = a->Delta / 2;
+
+		seg1 = seg2 = *a;
+		seg1.Delta = half_delta;
+		seg2.Delta -= half_delta;
+		seg2.StartAngle += half_delta;
+
+		tmp1 = ArcPolyNoIntersect(&seg1, thick);
+		tmp2 = ArcPolyNoIntersect(&seg2, thick);
+		poly_Boolean_free(tmp1, tmp2, &res, PBO_UNITE);
+		return res;
+	}
+
+	return ArcPolyNoIntersect(a, thick);
+}
+
+POLYAREA *LinePoly(LineType * L, Coord thick)
+{
+	PLINE *contour = NULL;
+	POLYAREA *np = NULL;
+	Vector v;
+	double d, dx, dy;
+	long half;
+	LineType _l = *L, *l = &_l;
+
+	if (thick <= 0)
+		return NULL;
+	half = (thick + 1) / 2;
+	d = sqrt(SQUARE(l->Point1.X - l->Point2.X) + SQUARE(l->Point1.Y - l->Point2.Y));
+	if (!TEST_FLAG(PCB_FLAG_SQUARE, l))
+		if (d == 0)									/* line is a point */
+			return CirclePoly(l->Point1.X, l->Point1.Y, half);
+	if (d != 0) {
+		d = half / d;
+		dx = (l->Point1.Y - l->Point2.Y) * d;
+		dy = (l->Point2.X - l->Point1.X) * d;
+	}
+	else {
+		dx = half;
+		dy = 0;
+	}
+	if (TEST_FLAG(PCB_FLAG_SQUARE, l)) {	/* take into account the ends */
+		l->Point1.X -= dy;
+		l->Point1.Y += dx;
+		l->Point2.X += dy;
+		l->Point2.Y -= dx;
+	}
+	v[0] = l->Point1.X - dx;
+	v[1] = l->Point1.Y - dy;
+	if ((contour = poly_NewContour(v)) == NULL)
+		return 0;
+	v[0] = l->Point2.X - dx;
+	v[1] = l->Point2.Y - dy;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, l))
+		poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	else
+		frac_circle(contour, l->Point2.X, l->Point2.Y, v, 2);
+	v[0] = l->Point2.X + dx;
+	v[1] = l->Point2.Y + dy;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	v[0] = l->Point1.X + dx;
+	v[1] = l->Point1.Y + dy;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, l))
+		poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	else
+		frac_circle(contour, l->Point1.X, l->Point1.Y, v, 2);
+	/* now we have the line contour */
+	if (!(np = ContourToPoly(contour)))
+		return NULL;
+	return np;
+}
+
+/* make a rounded-corner rectangle */
+POLYAREA *SquarePadPoly(PadType * pad, Coord clear)
+{
+	PLINE *contour = NULL;
+	POLYAREA *np = NULL;
+	Vector v;
+	double d;
+	double tx, ty;
+	double cx, cy;
+	PadType _t = *pad, *t = &_t;
+	PadType _c = *pad, *c = &_c;
+	int halfthick = (pad->Thickness + 1) / 2;
+	int halfclear = (clear + 1) / 2;
+
+	d = sqrt(SQUARE(pad->Point1.X - pad->Point2.X) + SQUARE(pad->Point1.Y - pad->Point2.Y));
+	if (d != 0) {
+		double a = halfthick / d;
+		tx = (t->Point1.Y - t->Point2.Y) * a;
+		ty = (t->Point2.X - t->Point1.X) * a;
+		a = halfclear / d;
+		cx = (c->Point1.Y - c->Point2.Y) * a;
+		cy = (c->Point2.X - c->Point1.X) * a;
+
+		t->Point1.X -= ty;
+		t->Point1.Y += tx;
+		t->Point2.X += ty;
+		t->Point2.Y -= tx;
+		c->Point1.X -= cy;
+		c->Point1.Y += cx;
+		c->Point2.X += cy;
+		c->Point2.Y -= cx;
+	}
+	else {
+		tx = halfthick;
+		ty = 0;
+		cx = halfclear;
+		cy = 0;
+
+		t->Point1.Y += tx;
+		t->Point2.Y -= tx;
+		c->Point1.Y += cx;
+		c->Point2.Y -= cx;
+	}
+
+	v[0] = c->Point1.X - tx;
+	v[1] = c->Point1.Y - ty;
+	if ((contour = poly_NewContour(v)) == NULL)
+		return 0;
+	frac_circle(contour, (t->Point1.X - tx), (t->Point1.Y - ty), v, 4);
+
+	v[0] = t->Point2.X - cx;
+	v[1] = t->Point2.Y - cy;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	frac_circle(contour, (t->Point2.X - tx), (t->Point2.Y - ty), v, 4);
+
+	v[0] = c->Point2.X + tx;
+	v[1] = c->Point2.Y + ty;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	frac_circle(contour, (t->Point2.X + tx), (t->Point2.Y + ty), v, 4);
+
+	v[0] = t->Point1.X + cx;
+	v[1] = t->Point1.Y + cy;
+	poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+	frac_circle(contour, (t->Point1.X + tx), (t->Point1.Y + ty), v, 4);
+
+	/* now we have the line contour */
+	if (!(np = ContourToPoly(contour)))
+		return NULL;
+	return np;
+}
+
+/* clear np1 from the polygon */
+static int Subtract(POLYAREA * np1, PolygonType * p, pcb_bool fnp)
+{
+	POLYAREA *merged = NULL, *np = np1;
+	int x;
+	assert(np);
+	assert(p);
+	if (!p->Clipped) {
+		if (fnp)
+			poly_Free(&np);
+		return 1;
+	}
+	assert(poly_Valid(p->Clipped));
+	assert(poly_Valid(np));
+	if (fnp)
+		x = poly_Boolean_free(p->Clipped, np, &merged, PBO_SUB);
+	else {
+		x = poly_Boolean(p->Clipped, np, &merged, PBO_SUB);
+		poly_Free(&p->Clipped);
+	}
+	assert(!merged || poly_Valid(merged));
+	if (x != err_ok) {
+		fprintf(stderr, "Error while clipping PBO_SUB: %d\n", x);
+		poly_Free(&merged);
+		p->Clipped = NULL;
+		if (p->NoHoles)
+			printf("Just leaked in Subtract\n");
+		p->NoHoles = NULL;
+		return -1;
+	}
+	p->Clipped = biggest(merged);
+	assert(!p->Clipped || poly_Valid(p->Clipped));
+	if (!p->Clipped)
+		Message(PCB_MSG_DEFAULT, "Polygon cleared out of existence near (%d, %d)\n",
+						(p->BoundingBox.X1 + p->BoundingBox.X2) / 2, (p->BoundingBox.Y1 + p->BoundingBox.Y2) / 2);
+	return 1;
+}
+
+/* create a polygon of the pin clearance */
+POLYAREA *PinPoly(PinType * pin, Coord thick, Coord clear)
+{
+	int size;
+
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+		if (GET_SQUARE(pin) <= 1) {
+			size = (thick + 1) / 2;
+			return RoundRect(pin->X - size, pin->X + size, pin->Y - size, pin->Y + size, (clear + 1) / 2);
+		}
+		else {
+			size = (thick + clear + 1) / 2;
+			return OctagonPoly(pin->X, pin->Y, size + size, GET_SQUARE(pin));
+		}
+
+	}
+	else {
+		size = (thick + clear + 1) / 2;
+		if (TEST_FLAG(PCB_FLAG_OCTAGON, pin)) {
+			return OctagonPoly(pin->X, pin->Y, size + size, GET_SQUARE(pin));
+		}
+	}
+	return CirclePoly(pin->X, pin->Y, size);
+}
+
+POLYAREA *BoxPolyBloated(BoxType * box, Coord bloat)
+{
+	return RectPoly(box->X1 - bloat, box->X2 + bloat, box->Y1 - bloat, box->Y2 + bloat);
+}
+
+/* return the clearance polygon for a pin */
+static POLYAREA *pin_clearance_poly(pcb_cardinal_t layernum, PCBTypePtr pcb, PinType * pin)
+{
+	POLYAREA *np;
+	if (TEST_THERM(layernum, pin))
+		np = ThermPoly(pcb, pin, layernum);
+	else
+		np = PinPoly(pin, PIN_SIZE(pin), pin->Clearance);
+	return np;
+}
+
+/* remove the pin clearance from the polygon */
+static int SubtractPin(DataType * d, PinType * pin, LayerType * l, PolygonType * p)
+{
+	POLYAREA *np;
+	pcb_cardinal_t i;
+
+	if (pin->Clearance == 0)
+		return 0;
+	i = GetLayerNumber(d, l);
+	np = pin_clearance_poly(i, d->pcb, pin);
+
+	if (TEST_THERM(i, pin)) {
+		if (!np)
+			return 0;
+	}
+	else {
+		if (!np)
+			return -1;
+	}
+
+	return Subtract(np, p, TRUE);
+}
+
+static int SubtractLine(LineType * line, PolygonType * p)
+{
+	POLYAREA *np;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, line))
+		return 0;
+	if (!(np = LinePoly(line, line->Thickness + line->Clearance)))
+		return -1;
+	return Subtract(np, p, pcb_true);
+}
+
+static int SubtractArc(ArcType * arc, PolygonType * p)
+{
+	POLYAREA *np;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, arc))
+		return 0;
+	if (!(np = ArcPoly(arc, arc->Thickness + arc->Clearance)))
+		return -1;
+	return Subtract(np, p, pcb_true);
+}
+
+static int SubtractText(TextType * text, PolygonType * p)
+{
+	POLYAREA *np;
+	const BoxType *b = &text->BoundingBox;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, text))
+		return 0;
+	if (!(np = RoundRect(b->X1 + PCB->Bloat, b->X2 - PCB->Bloat, b->Y1 + PCB->Bloat, b->Y2 - PCB->Bloat, PCB->Bloat)))
+		return -1;
+	return Subtract(np, p, pcb_true);
+}
+
+static int SubtractPad(PadType * pad, PolygonType * p)
+{
+	POLYAREA *np = NULL;
+
+	if (pad->Clearance == 0)
+		return 0;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pad)) {
+		if (!(np = SquarePadPoly(pad, pad->Thickness + pad->Clearance)))
+			return -1;
+	}
+	else {
+		if (!(np = LinePoly((LineType *) pad, pad->Thickness + pad->Clearance)))
+			return -1;
+	}
+	return Subtract(np, p, pcb_true);
+}
+
+struct cpInfo {
+	const BoxType *other;
+	DataType *data;
+	LayerType *layer;
+	PolygonType *polygon;
+	pcb_bool solder;
+	POLYAREA *accumulate;
+	int batch_size;
+	jmp_buf env;
+};
+
+static void subtract_accumulated(struct cpInfo *info, PolygonTypePtr polygon)
+{
+	if (info->accumulate == NULL)
+		return;
+	Subtract(info->accumulate, polygon, pcb_true);
+	info->accumulate = NULL;
+	info->batch_size = 0;
+}
+
+static r_dir_t pin_sub_callback(const BoxType * b, void *cl)
+{
+	PinTypePtr pin = (PinTypePtr) b;
+	struct cpInfo *info = (struct cpInfo *) cl;
+	PolygonTypePtr polygon;
+	POLYAREA *np;
+	POLYAREA *merged;
+	pcb_cardinal_t i;
+
+	/* don't subtract the object that was put back! */
+	if (b == info->other)
+		return R_DIR_NOT_FOUND;
+	polygon = info->polygon;
+
+	if (pin->Clearance == 0)
+		return R_DIR_NOT_FOUND;
+	i = GetLayerNumber(info->data, info->layer);
+	if (TEST_THERM(i, pin)) {
+		np = ThermPoly((PCBTypePtr) (info->data->pcb), pin, i);
+		if (!np)
+			return R_DIR_FOUND_CONTINUE;
+	}
+	else {
+		np = PinPoly(pin, PIN_SIZE(pin), pin->Clearance);
+		if (!np)
+			longjmp(info->env, 1);
+	}
+
+	poly_Boolean_free(info->accumulate, np, &merged, PBO_UNITE);
+	info->accumulate = merged;
+
+	info->batch_size++;
+
+	if (info->batch_size == SUBTRACT_PIN_VIA_BATCH_SIZE)
+		subtract_accumulated(info, polygon);
+
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t arc_sub_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	struct cpInfo *info = (struct cpInfo *) cl;
+	PolygonTypePtr polygon;
+
+	/* don't subtract the object that was put back! */
+	if (b == info->other)
+		return R_DIR_NOT_FOUND;
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, arc))
+		return R_DIR_NOT_FOUND;
+	polygon = info->polygon;
+	if (SubtractArc(arc, polygon) < 0)
+		longjmp(info->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t pad_sub_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct cpInfo *info = (struct cpInfo *) cl;
+	PolygonTypePtr polygon;
+
+	/* don't subtract the object that was put back! */
+	if (b == info->other)
+		return R_DIR_NOT_FOUND;
+	if (pad->Clearance == 0)
+		return R_DIR_NOT_FOUND;
+	polygon = info->polygon;
+	if (XOR(TEST_FLAG(PCB_FLAG_ONSOLDER, pad), !info->solder)) {
+		if (SubtractPad(pad, polygon) < 0)
+			longjmp(info->env, 1);
+		return R_DIR_FOUND_CONTINUE;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t line_sub_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct cpInfo *info = (struct cpInfo *) cl;
+	PolygonTypePtr polygon;
+	POLYAREA *np;
+	POLYAREA *merged;
+
+	/* don't subtract the object that was put back! */
+	if (b == info->other)
+		return R_DIR_NOT_FOUND;
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, line))
+		return R_DIR_NOT_FOUND;
+	polygon = info->polygon;
+
+	if (!(np = LinePoly(line, line->Thickness + line->Clearance)))
+		longjmp(info->env, 1);
+
+	poly_Boolean_free(info->accumulate, np, &merged, PBO_UNITE);
+	info->accumulate = merged;
+	info->batch_size++;
+
+	if (info->batch_size == SUBTRACT_LINE_BATCH_SIZE)
+		subtract_accumulated(info, polygon);
+
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t text_sub_callback(const BoxType * b, void *cl)
+{
+	TextTypePtr text = (TextTypePtr) b;
+	struct cpInfo *info = (struct cpInfo *) cl;
+	PolygonTypePtr polygon;
+
+	/* don't subtract the object that was put back! */
+	if (b == info->other)
+		return R_DIR_NOT_FOUND;
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, text))
+		return R_DIR_NOT_FOUND;
+	polygon = info->polygon;
+	if (SubtractText(text, polygon) < 0)
+		longjmp(info->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static int Group(DataTypePtr Data, pcb_cardinal_t layer)
+{
+	pcb_cardinal_t i, j;
+	for (i = 0; i < max_group; i++)
+		for (j = 0; j < ((PCBType *) (Data->pcb))->LayerGroups.Number[i]; j++)
+			if (layer == ((PCBType *) (Data->pcb))->LayerGroups.Entries[i][j])
+				return i;
+	return i;
+}
+
+static int clearPoly(DataTypePtr Data, LayerTypePtr Layer, PolygonType * polygon, const BoxType * here, Coord expand)
+{
+	int r = 0, seen;
+	BoxType region;
+	struct cpInfo info;
+	pcb_cardinal_t group;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARPOLY, polygon)
+			|| GetLayerNumber(Data, Layer) >= max_copper_layer)
+		return 0;
+	group = Group(Data, GetLayerNumber(Data, Layer));
+	info.solder = (group == Group(Data, solder_silk_layer));
+	info.data = Data;
+	info.other = here;
+	info.layer = Layer;
+	info.polygon = polygon;
+	if (here)
+		region = clip_box(here, &polygon->BoundingBox);
+	else
+		region = polygon->BoundingBox;
+	region = bloat_box(&region, expand);
+
+	if (setjmp(info.env) == 0) {
+		r = 0;
+		info.accumulate = NULL;
+		info.batch_size = 0;
+		if (info.solder || group == Group(Data, component_silk_layer)) {
+			r_search(Data->pad_tree, &region, NULL, pad_sub_callback, &info, &seen);
+			r += seen;
+		}
+		GROUP_LOOP(Data, group);
+		{
+			r_search(layer->line_tree, &region, NULL, line_sub_callback, &info, &seen);
+			r += seen;
+			subtract_accumulated(&info, polygon);
+			r_search(layer->arc_tree, &region, NULL, arc_sub_callback, &info, &seen);
+			r += seen;
+			r_search(layer->text_tree, &region, NULL, text_sub_callback, &info, &seen);
+			r += seen;
+		}
+		END_LOOP;
+		r_search(Data->via_tree, &region, NULL, pin_sub_callback, &info, &seen);
+		r += seen;
+		r_search(Data->pin_tree, &region, NULL, pin_sub_callback, &info, &seen);
+		r += seen;
+		subtract_accumulated(&info, polygon);
+	}
+	polygon->NoHolesValid = 0;
+	return r;
+}
+
+static int Unsubtract(POLYAREA * np1, PolygonType * p)
+{
+	POLYAREA *merged = NULL, *np = np1;
+	POLYAREA *orig_poly, *clipped_np;
+	int x;
+	assert(np);
+	assert(p && p->Clipped);
+
+	orig_poly = original_poly(p);
+
+	x = poly_Boolean_free(np, orig_poly, &clipped_np, PBO_ISECT);
+	if (x != err_ok) {
+		fprintf(stderr, "Error while clipping PBO_ISECT: %d\n", x);
+		poly_Free(&clipped_np);
+		goto fail;
+	}
+
+	x = poly_Boolean_free(p->Clipped, clipped_np, &merged, PBO_UNITE);
+	if (x != err_ok) {
+		fprintf(stderr, "Error while clipping PBO_UNITE: %d\n", x);
+		poly_Free(&merged);
+		goto fail;
+	}
+	p->Clipped = biggest(merged);
+	assert(!p->Clipped || poly_Valid(p->Clipped));
+	return 1;
+
+fail:
+	p->Clipped = NULL;
+	if (p->NoHoles)
+		printf("Just leaked in Unsubtract\n");
+	p->NoHoles = NULL;
+	return 0;
+}
+
+static int UnsubtractPin(PinType * pin, LayerType * l, PolygonType * p)
+{
+	POLYAREA *np;
+
+	/* overlap a bit to prevent gaps from rounding errors */
+	np = BoxPolyBloated(&pin->BoundingBox, UNSUBTRACT_BLOAT * 400000);
+
+	if (!np)
+		return 0;
+	if (!Unsubtract(np, p))
+		return 0;
+
+	clearPoly(PCB->Data, l, p, (const BoxType *) pin, 2 * UNSUBTRACT_BLOAT * 400000);
+	return 1;
+}
+
+static int UnsubtractArc(ArcType * arc, LayerType * l, PolygonType * p)
+{
+	POLYAREA *np;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, arc))
+		return 0;
+
+	/* overlap a bit to prevent gaps from rounding errors */
+	np = BoxPolyBloated(&arc->BoundingBox, UNSUBTRACT_BLOAT);
+
+	if (!np)
+		return 0;
+	if (!Unsubtract(np, p))
+		return 0;
+	clearPoly(PCB->Data, l, p, (const BoxType *) arc, 2 * UNSUBTRACT_BLOAT);
+	return 1;
+}
+
+static int UnsubtractLine(LineType * line, LayerType * l, PolygonType * p)
+{
+	POLYAREA *np;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, line))
+		return 0;
+
+	/* overlap a bit to prevent notches from rounding errors */
+	np = BoxPolyBloated(&line->BoundingBox, UNSUBTRACT_BLOAT);
+
+	if (!np)
+		return 0;
+	if (!Unsubtract(np, p))
+		return 0;
+	clearPoly(PCB->Data, l, p, (const BoxType *) line, 2 * UNSUBTRACT_BLOAT);
+	return 1;
+}
+
+static int UnsubtractText(TextType * text, LayerType * l, PolygonType * p)
+{
+	POLYAREA *np;
+
+	if (!TEST_FLAG(PCB_FLAG_CLEARLINE, text))
+		return 0;
+
+	/* overlap a bit to prevent notches from rounding errors */
+	np = BoxPolyBloated(&text->BoundingBox, UNSUBTRACT_BLOAT);
+
+	if (!np)
+		return -1;
+	if (!Unsubtract(np, p))
+		return 0;
+	clearPoly(PCB->Data, l, p, (const BoxType *) text, 2 * UNSUBTRACT_BLOAT);
+	return 1;
+}
+
+static int UnsubtractPad(PadType * pad, LayerType * l, PolygonType * p)
+{
+	POLYAREA *np;
+
+	/* overlap a bit to prevent notches from rounding errors */
+	np = BoxPolyBloated(&pad->BoundingBox, UNSUBTRACT_BLOAT);
+
+	if (!np)
+		return 0;
+	if (!Unsubtract(np, p))
+		return 0;
+	clearPoly(PCB->Data, l, p, (const BoxType *) pad, 2 * UNSUBTRACT_BLOAT);
+	return 1;
+}
+
+static pcb_bool inhibit = pcb_false;
+
+int InitClip(DataTypePtr Data, LayerTypePtr layer, PolygonType * p)
+{
+	if (inhibit)
+		return 0;
+	if (p->Clipped)
+		poly_Free(&p->Clipped);
+	p->Clipped = original_poly(p);
+	poly_FreeContours(&p->NoHoles);
+	if (!p->Clipped)
+		return 0;
+	assert(poly_Valid(p->Clipped));
+	if (TEST_FLAG(PCB_FLAG_CLEARPOLY, p))
+		clearPoly(Data, layer, p, NULL, 0);
+	else
+		p->NoHolesValid = 0;
+	return 1;
+}
+
+/* --------------------------------------------------------------------------
+ * remove redundant polygon points. Any point that lies on the straight
+ * line between the points on either side of it is redundant.
+ * returns pcb_true if any points are removed
+ */
+pcb_bool RemoveExcessPolygonPoints(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	PointTypePtr p;
+	pcb_cardinal_t n, prev, next;
+	LineType line;
+	pcb_bool changed = pcb_false;
+
+	if (Undoing())
+		return (pcb_false);
+
+	for (n = 0; n < Polygon->PointN; n++) {
+		prev = prev_contour_point(Polygon, n);
+		next = next_contour_point(Polygon, n);
+		p = &Polygon->Points[n];
+
+		line.Point1 = Polygon->Points[prev];
+		line.Point2 = Polygon->Points[next];
+		line.Thickness = 0;
+		if (IsPointOnLine(p->X, p->Y, 0.0, &line)) {
+			RemoveObject(PCB_TYPE_POLYGON_POINT, Layer, Polygon, p);
+			changed = pcb_true;
+		}
+	}
+	return (changed);
+}
+
+/* ---------------------------------------------------------------------------
+ * returns the index of the polygon point which is the end
+ * point of the segment with the lowest distance to the passed
+ * coordinates
+ */
+pcb_cardinal_t GetLowestDistancePolygonPoint(PolygonTypePtr Polygon, Coord X, Coord Y)
+{
+	double mindistance = (double) MAX_COORD * MAX_COORD;
+	PointTypePtr ptr1, ptr2;
+	pcb_cardinal_t n, result = 0;
+
+	/* we calculate the distance to each segment and choose the
+	 * shortest distance. If the closest approach between the
+	 * given point and the projected line (i.e. the segment extended)
+	 * is not on the segment, then the distance is the distance
+	 * to the segment end point.
+	 */
+
+	for (n = 0; n < Polygon->PointN; n++) {
+		double u, dx, dy;
+		ptr1 = &Polygon->Points[prev_contour_point(Polygon, n)];
+		ptr2 = &Polygon->Points[n];
+
+		dx = ptr2->X - ptr1->X;
+		dy = ptr2->Y - ptr1->Y;
+		if (dx != 0.0 || dy != 0.0) {
+			/* projected intersection is at P1 + u(P2 - P1) */
+			u = ((X - ptr1->X) * dx + (Y - ptr1->Y) * dy) / (dx * dx + dy * dy);
+
+			if (u < 0.0) {						/* ptr1 is closest point */
+				u = SQUARE(X - ptr1->X) + SQUARE(Y - ptr1->Y);
+			}
+			else if (u > 1.0) {				/* ptr2 is closest point */
+				u = SQUARE(X - ptr2->X) + SQUARE(Y - ptr2->Y);
+			}
+			else {										/* projected intersection is closest point */
+				u = SQUARE(X - ptr1->X * (1.0 - u) - u * ptr2->X) + SQUARE(Y - ptr1->Y * (1.0 - u) - u * ptr2->Y);
+			}
+			if (u < mindistance) {
+				mindistance = u;
+				result = n;
+			}
+		}
+	}
+	return (result);
+}
+
+/* ---------------------------------------------------------------------------
+ * go back to the  previous point of the polygon
+ */
+void GoToPreviousPoint(void)
+{
+	switch (Crosshair.AttachedPolygon.PointN) {
+		/* do nothing if mode has just been entered */
+	case 0:
+		break;
+
+		/* reset number of points and 'PCB_MODE_LINE' state */
+	case 1:
+		Crosshair.AttachedPolygon.PointN = 0;
+		Crosshair.AttachedLine.State = STATE_FIRST;
+		addedLines = 0;
+		break;
+
+		/* back-up one point */
+	default:
+		{
+			PointTypePtr points = Crosshair.AttachedPolygon.Points;
+			pcb_cardinal_t n = Crosshair.AttachedPolygon.PointN - 2;
+
+			Crosshair.AttachedPolygon.PointN--;
+			Crosshair.AttachedLine.Point1.X = points[n].X;
+			Crosshair.AttachedLine.Point1.Y = points[n].Y;
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * close polygon if possible
+ */
+void ClosePolygon(void)
+{
+	pcb_cardinal_t n = Crosshair.AttachedPolygon.PointN;
+
+	/* check number of points */
+	if (n >= 3) {
+		/* if 45 degree lines are what we want do a quick check
+		 * if closing the polygon makes sense
+		 */
+		if (!conf_core.editor.all_direction_lines) {
+			Coord dx, dy;
+
+			dx = coord_abs(Crosshair.AttachedPolygon.Points[n - 1].X - Crosshair.AttachedPolygon.Points[0].X);
+			dy = coord_abs(Crosshair.AttachedPolygon.Points[n - 1].Y - Crosshair.AttachedPolygon.Points[0].Y);
+			if (!(dx == 0 || dy == 0 || dx == dy)) {
+				Message(PCB_MSG_ERROR, _("Cannot close polygon because 45 degree lines are requested.\n"));
+				return;
+			}
+		}
+		CopyAttachedPolygonToLayer();
+		Draw();
+	}
+	else
+		Message(PCB_MSG_DEFAULT, _("A polygon has to have at least 3 points\n"));
+}
+
+/* ---------------------------------------------------------------------------
+ * moves the data of the attached (new) polygon to the current layer
+ */
+void CopyAttachedPolygonToLayer(void)
+{
+	PolygonTypePtr polygon;
+	int saveID;
+
+	/* move data to layer and clear attached struct */
+	polygon = CreateNewPolygon(CURRENT, NoFlags());
+	saveID = polygon->ID;
+	*polygon = Crosshair.AttachedPolygon;
+	polygon->ID = saveID;
+	SET_FLAG(PCB_FLAG_CLEARPOLY, polygon);
+	if (conf_core.editor.full_poly)
+		SET_FLAG(PCB_FLAG_FULLPOLY, polygon);
+	memset(&Crosshair.AttachedPolygon, 0, sizeof(PolygonType));
+	SetPolygonBoundingBox(polygon);
+	if (!CURRENT->polygon_tree)
+		CURRENT->polygon_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(CURRENT->polygon_tree, (BoxType *) polygon, 0);
+	InitClip(PCB->Data, CURRENT, polygon);
+	DrawPolygon(CURRENT, polygon);
+	SetChangedFlag(pcb_true);
+
+	/* reset state of attached line */
+	Crosshair.AttachedLine.State = STATE_FIRST;
+	addedLines = 0;
+
+	/* add to undo list */
+	AddObjectToCreateUndoList(PCB_TYPE_POLYGON, CURRENT, polygon, polygon);
+	IncrementUndoSerialNumber();
+}
+
+/* find polygon holes in range, then call the callback function for
+ * each hole. If the callback returns non-zero, stop
+ * the search.
+ */
+int
+PolygonHoles(PolygonType * polygon, const BoxType * range, int (*callback) (PLINE * contour, void *user_data), void *user_data)
+{
+	POLYAREA *pa = polygon->Clipped;
+	PLINE *pl;
+	/* If this hole is so big the polygon doesn't exist, then it's not
+	 * really a hole.
+	 */
+	if (!pa)
+		return 0;
+	for (pl = pa->contours->next; pl; pl = pl->next) {
+		if (range != NULL && (pl->xmin > range->X2 || pl->xmax < range->X1 || pl->ymin > range->Y2 || pl->ymax < range->Y1))
+			continue;
+		if (callback(pl, user_data)) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+struct plow_info {
+	int type;
+	void *ptr1, *ptr2;
+	LayerTypePtr layer;
+	DataTypePtr data;
+	r_dir_t (*callback) (DataTypePtr, LayerTypePtr, PolygonTypePtr, int, void *, void *);
+};
+
+static r_dir_t subtract_plow(DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon, int type, void *ptr1, void *ptr2)
+{
+	if (!Polygon->Clipped)
+		return 0;
+	switch (type) {
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_VIA:
+		SubtractPin(Data, (PinTypePtr) ptr2, Layer, Polygon);
+		Polygon->NoHolesValid = 0;
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_LINE:
+		SubtractLine((LineTypePtr) ptr2, Polygon);
+		Polygon->NoHolesValid = 0;
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_ARC:
+		SubtractArc((ArcTypePtr) ptr2, Polygon);
+		Polygon->NoHolesValid = 0;
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_PAD:
+		SubtractPad((PadTypePtr) ptr2, Polygon);
+		Polygon->NoHolesValid = 0;
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_TEXT:
+		SubtractText((TextTypePtr) ptr2, Polygon);
+		Polygon->NoHolesValid = 0;
+		return R_DIR_FOUND_CONTINUE;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t add_plow(DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon, int type, void *ptr1, void *ptr2)
+{
+	switch (type) {
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_VIA:
+		UnsubtractPin((PinTypePtr) ptr2, Layer, Polygon);
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_LINE:
+		UnsubtractLine((LineTypePtr) ptr2, Layer, Polygon);
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_ARC:
+		UnsubtractArc((ArcTypePtr) ptr2, Layer, Polygon);
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_PAD:
+		UnsubtractPad((PadTypePtr) ptr2, Layer, Polygon);
+		return R_DIR_FOUND_CONTINUE;
+	case PCB_TYPE_TEXT:
+		UnsubtractText((TextTypePtr) ptr2, Layer, Polygon);
+		return R_DIR_FOUND_CONTINUE;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t plow_callback(const BoxType * b, void *cl)
+{
+	struct plow_info *plow = (struct plow_info *) cl;
+	PolygonTypePtr polygon = (PolygonTypePtr) b;
+
+	if (TEST_FLAG(PCB_FLAG_CLEARPOLY, polygon))
+		return plow->callback(plow->data, plow->layer, polygon, plow->type, plow->ptr1, plow->ptr2);
+	return R_DIR_NOT_FOUND;
+}
+
+int
+PlowsPolygon(DataType * Data, int type, void *ptr1, void *ptr2,
+						 r_dir_t (*call_back) (DataTypePtr data, LayerTypePtr lay, PolygonTypePtr poly, int type, void *ptr1, void *ptr2))
+{
+	BoxType sb = ((PinTypePtr) ptr2)->BoundingBox;
+	int r = 0, seen;
+	struct plow_info info;
+
+	info.type = type;
+	info.ptr1 = ptr1;
+	info.ptr2 = ptr2;
+	info.data = Data;
+	info.callback = call_back;
+	switch (type) {
+	case PCB_TYPE_PIN:
+	case PCB_TYPE_VIA:
+		if (type == PCB_TYPE_PIN || ptr1 == ptr2 || ptr1 == NULL) {
+			LAYER_LOOP(Data, max_copper_layer);
+			{
+				info.layer = layer;
+				r_search(layer->polygon_tree, &sb, NULL, plow_callback, &info, &seen);
+				r += seen;
+			}
+			END_LOOP;
+		}
+		else {
+			GROUP_LOOP(Data, GetLayerGroupNumberByNumber(GetLayerNumber(Data, ((LayerTypePtr) ptr1))));
+			{
+				info.layer = layer;
+				r_search(layer->polygon_tree, &sb, NULL, plow_callback, &info, &seen);
+				r += seen;
+			}
+			END_LOOP;
+		}
+		break;
+	case PCB_TYPE_LINE:
+	case PCB_TYPE_ARC:
+	case PCB_TYPE_TEXT:
+		/* the cast works equally well for lines and arcs */
+		if (!TEST_FLAG(PCB_FLAG_CLEARLINE, (LineTypePtr) ptr2))
+			return 0;
+		/* silk doesn't plow */
+		if (GetLayerNumber(Data, (LayerTypePtr) ptr1) >= max_copper_layer)
+			return 0;
+		GROUP_LOOP(Data, GetLayerGroupNumberByNumber(GetLayerNumber(Data, ((LayerTypePtr) ptr1))));
+		{
+			info.layer = layer;
+			r_search(layer->polygon_tree, &sb, NULL, plow_callback, &info, &seen);
+			r += seen;
+		}
+		END_LOOP;
+		break;
+	case PCB_TYPE_PAD:
+		{
+			pcb_cardinal_t group = GetLayerGroupNumberByNumber(TEST_FLAG(PCB_FLAG_ONSOLDER, (PadType *) ptr2) ?
+																									 solder_silk_layer : component_silk_layer);
+			GROUP_LOOP(Data, group);
+			{
+				info.layer = layer;
+				r_search(layer->polygon_tree, &sb, NULL, plow_callback, &info, &seen);
+				r += seen;
+			}
+			END_LOOP;
+		}
+		break;
+
+	case PCB_TYPE_ELEMENT:
+		{
+			PIN_LOOP((ElementType *) ptr1);
+			{
+				PlowsPolygon(Data, PCB_TYPE_PIN, ptr1, pin, call_back);
+			}
+			END_LOOP;
+			PAD_LOOP((ElementType *) ptr1);
+			{
+				PlowsPolygon(Data, PCB_TYPE_PAD, ptr1, pad, call_back);
+			}
+			END_LOOP;
+		}
+		break;
+	}
+	return r;
+}
+
+void RestoreToPolygon(DataType * Data, int type, void *ptr1, void *ptr2)
+{
+	if (type == PCB_TYPE_POLYGON)
+		InitClip(PCB->Data, (LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
+	else
+		PlowsPolygon(Data, type, ptr1, ptr2, add_plow);
+}
+
+void ClearFromPolygon(DataType * Data, int type, void *ptr1, void *ptr2)
+{
+	if (type == PCB_TYPE_POLYGON)
+		InitClip(PCB->Data, (LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
+	else
+		PlowsPolygon(Data, type, ptr1, ptr2, subtract_plow);
+}
+
+pcb_bool isects(POLYAREA * a, PolygonTypePtr p, pcb_bool fr)
+{
+	POLYAREA *x;
+	pcb_bool ans;
+	ans = Touching(a, p->Clipped);
+	/* argument may be register, so we must copy it */
+	x = a;
+	if (fr)
+		poly_Free(&x);
+	return ans;
+}
+
+
+pcb_bool IsPointInPolygon(Coord X, Coord Y, Coord r, PolygonTypePtr p)
+{
+	POLYAREA *c;
+	Vector v;
+	v[0] = X;
+	v[1] = Y;
+	if (poly_CheckInside(p->Clipped, v))
+		return pcb_true;
+
+	if (TEST_FLAG(PCB_FLAG_FULLPOLY, p)) {
+		PolygonType tmp = *p;
+
+		/* Check all clipped-away regions that are now drawn because of full-poly */
+		for (tmp.Clipped = p->Clipped->f; tmp.Clipped != p->Clipped; tmp.Clipped = tmp.Clipped->f)
+			if (poly_CheckInside(tmp.Clipped, v))
+				return pcb_true;
+	}
+
+	if (r < 1)
+		return pcb_false;
+	if (!(c = CirclePoly(X, Y, r)))
+		return pcb_false;
+	return isects(c, p, pcb_true);
+}
+
+
+pcb_bool IsPointInPolygonIgnoreHoles(Coord X, Coord Y, PolygonTypePtr p)
+{
+	Vector v;
+	v[0] = X;
+	v[1] = Y;
+	return poly_InsideContour(p->Clipped->contours, v);
+}
+
+pcb_bool IsRectangleInPolygon(Coord X1, Coord Y1, Coord X2, Coord Y2, PolygonTypePtr p)
+{
+	POLYAREA *s;
+	if (!(s = RectPoly(min(X1, X2), max(X1, X2), min(Y1, Y2), max(Y1, Y2))))
+		return pcb_false;
+	return isects(s, p, pcb_true);
+}
+
+/* NB: This function will free the passed POLYAREA.
+ *     It must only be passed a single POLYAREA (pa->f == pa->b == pa)
+ */
+static void r_NoHolesPolygonDicer(POLYAREA * pa, void (*emit) (PLINE *, void *), void *user_data)
+{
+	PLINE *p = pa->contours;
+
+	if (!pa->contours->next) {		/* no holes */
+		pa->contours = NULL;				/* The callback now owns the contour */
+		/* Don't bother removing it from the POLYAREA's rtree
+		   since we're going to free the POLYAREA below anyway */
+		emit(p, user_data);
+		poly_Free(&pa);
+		return;
+	}
+	else {
+		POLYAREA *poly2, *left, *right;
+
+		/* make a rectangle of the left region slicing through the middle of the first hole */
+		poly2 = RectPoly(p->xmin, (p->next->xmin + p->next->xmax) / 2, p->ymin, p->ymax);
+		poly_AndSubtract_free(pa, poly2, &left, &right);
+		if (left) {
+			POLYAREA *cur, *next;
+			cur = left;
+			do {
+				next = cur->f;
+				cur->f = cur->b = cur;	/* Detach this polygon piece */
+				r_NoHolesPolygonDicer(cur, emit, user_data);
+				/* NB: The POLYAREA was freed by its use in the recursive dicer */
+			}
+			while ((cur = next) != left);
+		}
+		if (right) {
+			POLYAREA *cur, *next;
+			cur = right;
+			do {
+				next = cur->f;
+				cur->f = cur->b = cur;	/* Detach this polygon piece */
+				r_NoHolesPolygonDicer(cur, emit, user_data);
+				/* NB: The POLYAREA was freed by its use in the recursive dicer */
+			}
+			while ((cur = next) != right);
+		}
+	}
+}
+
+void NoHolesPolygonDicer(PolygonTypePtr p, const BoxType * clip, void (*emit) (PLINE *, void *), void *user_data)
+{
+	POLYAREA *main_contour, *cur, *next;
+
+	main_contour = poly_Create();
+	/* copy the main poly only */
+	poly_Copy1(main_contour, p->Clipped);
+	/* clip to the bounding box */
+	if (clip) {
+		POLYAREA *cbox = RectPoly(clip->X1, clip->X2, clip->Y1, clip->Y2);
+		poly_Boolean_free(main_contour, cbox, &main_contour, PBO_ISECT);
+	}
+	if (main_contour == NULL)
+		return;
+	/* Now dice it up.
+	 * NB: Could be more than one piece (because of the clip above)
+	 */
+	cur = main_contour;
+	do {
+		next = cur->f;
+		cur->f = cur->b = cur;			/* Detach this polygon piece */
+		r_NoHolesPolygonDicer(cur, emit, user_data);
+		/* NB: The POLYAREA was freed by its use in the recursive dicer */
+	}
+	while ((cur = next) != main_contour);
+}
+
+/* make a polygon split into multiple parts into multiple polygons */
+pcb_bool MorphPolygon(LayerTypePtr layer, PolygonTypePtr poly)
+{
+	POLYAREA *p, *start;
+	pcb_bool many = pcb_false;
+	FlagType flags;
+
+	if (!poly->Clipped || TEST_FLAG(PCB_FLAG_LOCK, poly))
+		return pcb_false;
+	if (poly->Clipped->f == poly->Clipped)
+		return pcb_false;
+	ErasePolygon(poly);
+	start = p = poly->Clipped;
+	/* This is ugly. The creation of the new polygons can cause
+	 * all of the polygon pointers (including the one we're called
+	 * with to change if there is a realloc in GetPolygonMemory().
+	 * That will invalidate our original "poly" argument, potentially
+	 * inside the loop. We need to steal the Clipped pointer and
+	 * hide it from the Remove call so that it still exists while
+	 * we do this dirty work.
+	 */
+	poly->Clipped = NULL;
+	if (poly->NoHoles)
+		printf("Just leaked in MorpyPolygon\n");
+	poly->NoHoles = NULL;
+	flags = poly->Flags;
+	RemovePolygon(layer, poly);
+	inhibit = pcb_true;
+	do {
+		VNODE *v;
+		PolygonTypePtr newone;
+
+		if (p->contours->area > PCB->IsleArea) {
+			newone = CreateNewPolygon(layer, flags);
+			if (!newone)
+				return pcb_false;
+			many = pcb_true;
+			v = &p->contours->head;
+			CreateNewPointInPolygon(newone, v->point[0], v->point[1]);
+			for (v = v->next; v != &p->contours->head; v = v->next)
+				CreateNewPointInPolygon(newone, v->point[0], v->point[1]);
+			newone->BoundingBox.X1 = p->contours->xmin;
+			newone->BoundingBox.X2 = p->contours->xmax + 1;
+			newone->BoundingBox.Y1 = p->contours->ymin;
+			newone->BoundingBox.Y2 = p->contours->ymax + 1;
+			AddObjectToCreateUndoList(PCB_TYPE_POLYGON, layer, newone, newone);
+			newone->Clipped = p;
+			p = p->f;									/* go to next pline */
+			newone->Clipped->b = newone->Clipped->f = newone->Clipped;	/* unlink from others */
+			r_insert_entry(layer->polygon_tree, (BoxType *) newone, 0);
+			DrawPolygon(layer, newone);
+		}
+		else {
+			POLYAREA *t = p;
+
+			p = p->f;
+			poly_DelContour(&t->contours);
+			free(t);
+		}
+	}
+	while (p != start);
+	inhibit = pcb_false;
+	IncrementUndoSerialNumber();
+	return many;
+}
+
+void debug_pline(PLINE * pl)
+{
+	VNODE *v;
+	pcb_fprintf(stderr, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n", pl->xmin, pl->xmax, pl->ymin, pl->ymax);
+	v = &pl->head;
+	while (v) {
+		pcb_fprintf(stderr, "\t\tvnode: %#mD\n", v->point[0], v->point[1]);
+		v = v->next;
+		if (v == &pl->head)
+			break;
+	}
+}
+
+void debug_polyarea(POLYAREA * p)
+{
+	PLINE *pl;
+
+	fprintf(stderr, "POLYAREA %p\n", (void *)p);
+	for (pl = p->contours; pl; pl = pl->next)
+		debug_pline(pl);
+}
+
+void debug_polygon(PolygonType * p)
+{
+	pcb_cardinal_t i;
+	POLYAREA *pa;
+	fprintf(stderr, "POLYGON %p  %d pts\n", (void *)p, p->PointN);
+	for (i = 0; i < p->PointN; i++)
+		pcb_fprintf(stderr, "\t%d: %#mD\n", i, p->Points[i].X, p->Points[i].Y);
+	if (p->HoleIndexN) {
+		fprintf(stderr, "%d holes, starting at indices\n", p->HoleIndexN);
+		for (i = 0; i < p->HoleIndexN; i++)
+			fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
+	}
+	else
+		fprintf(stderr, "it has no holes\n");
+	pa = p->Clipped;
+	while (pa) {
+		debug_polyarea(pa);
+		pa = pa->f;
+		if (pa == p->Clipped)
+			break;
+	}
+}
+
+/* Convert a POLYAREA (and all linked POLYAREA) to
+ * raw PCB polygons on the given layer.
+ */
+void PolyToPolygonsOnLayer(DataType * Destination, LayerType * Layer, POLYAREA * Input, FlagType Flags)
+{
+	PolygonType *Polygon;
+	POLYAREA *pa;
+	PLINE *pline;
+	VNODE *node;
+	pcb_bool outer;
+
+	if (Input == NULL)
+		return;
+
+	pa = Input;
+	do {
+		Polygon = CreateNewPolygon(Layer, Flags);
+
+		pline = pa->contours;
+		outer = pcb_true;
+
+		do {
+			if (!outer)
+				CreateNewHoleInPolygon(Polygon);
+			outer = pcb_false;
+
+			node = &pline->head;
+			do {
+				CreateNewPointInPolygon(Polygon, node->point[0], node->point[1]);
+			}
+			while ((node = node->next) != &pline->head);
+
+		}
+		while ((pline = pline->next) != NULL);
+
+		InitClip(Destination, Layer, Polygon);
+		SetPolygonBoundingBox(Polygon);
+		if (!Layer->polygon_tree)
+			Layer->polygon_tree = r_create_tree(NULL, 0, 0);
+		r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+
+		DrawPolygon(Layer, Polygon);
+		/* add to undo list */
+		AddObjectToCreateUndoList(PCB_TYPE_POLYGON, Layer, Polygon, Polygon);
+	}
+	while ((pa = pa->f) != Input);
+
+	SetChangedFlag(pcb_true);
+}
diff --git a/src/polygon.h b/src/polygon.h
new file mode 100644
index 0000000..35562b8
--- /dev/null
+++ b/src/polygon.h
@@ -0,0 +1,90 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for polygon editing routines */
+
+#ifndef	PCB_POLYGON_H
+#define	PCB_POLYGON_H
+
+#include "global.h"
+#include "rtree.h"
+
+/* Implementation constants */
+
+#define POLY_CIRC_SEGS 40
+#define POLY_CIRC_SEGS_F ((float)POLY_CIRC_SEGS)
+
+/* adjustment to make the segments outline the circle rather than connect
+ * points on the circle: 1 - cos (\alpha / 2) < (\alpha / 2) ^ 2 / 2
+ */
+#define POLY_CIRC_RADIUS_ADJ (1.0 + M_PI / POLY_CIRC_SEGS_F * \
+                                    M_PI / POLY_CIRC_SEGS_F / 2.0)
+
+/* polygon diverges from modelled arc no more than MAX_ARC_DEVIATION * thick */
+#define POLY_ARC_MAX_DEVIATION 0.02
+
+/* Prototypes */
+
+void polygon_init(void);
+pcb_cardinal_t polygon_point_idx(PolygonTypePtr polygon, PointTypePtr point);
+pcb_cardinal_t polygon_point_contour(PolygonTypePtr polygon, pcb_cardinal_t point);
+pcb_cardinal_t prev_contour_point(PolygonTypePtr polygon, pcb_cardinal_t point);
+pcb_cardinal_t next_contour_point(PolygonTypePtr polygon, pcb_cardinal_t point);
+pcb_cardinal_t GetLowestDistancePolygonPoint(PolygonTypePtr, Coord, Coord);
+pcb_bool RemoveExcessPolygonPoints(LayerTypePtr, PolygonTypePtr);
+void GoToPreviousPoint(void);
+void ClosePolygon(void);
+void CopyAttachedPolygonToLayer(void);
+int PolygonHoles(PolygonType * ptr, const BoxType * range, int (*callback) (PLINE *, void *user_data), void *user_data);
+int PlowsPolygon(DataType *, int, void *, void *,
+								 r_dir_t (*callback) (DataTypePtr, LayerTypePtr, PolygonTypePtr, int, void *, void *));
+void ComputeNoHoles(PolygonType * poly);
+POLYAREA *ContourToPoly(PLINE *);
+POLYAREA *PolygonToPoly(PolygonType *);
+POLYAREA *RectPoly(Coord x1, Coord x2, Coord y1, Coord y2);
+POLYAREA *CirclePoly(Coord x, Coord y, Coord radius);
+POLYAREA *OctagonPoly(Coord x, Coord y, Coord radius, int style);
+POLYAREA *LinePoly(LineType * l, Coord thick);
+POLYAREA *ArcPoly(ArcType * l, Coord thick);
+POLYAREA *PinPoly(PinType * l, Coord thick, Coord clear);
+POLYAREA *BoxPolyBloated(BoxType * box, Coord radius);
+void frac_circle(PLINE *, Coord, Coord, Vector, int);
+int InitClip(DataType * d, LayerType * l, PolygonType * p);
+void RestoreToPolygon(DataType *, int, void *, void *);
+void ClearFromPolygon(DataType *, int, void *, void *);
+
+pcb_bool IsPointInPolygon(Coord, Coord, Coord, PolygonTypePtr);
+pcb_bool IsPointInPolygonIgnoreHoles(Coord, Coord, PolygonTypePtr);
+pcb_bool IsRectangleInPolygon(Coord, Coord, Coord, Coord, PolygonTypePtr);
+pcb_bool isects(POLYAREA *, PolygonTypePtr, pcb_bool);
+pcb_bool MorphPolygon(LayerTypePtr, PolygonTypePtr);
+void NoHolesPolygonDicer(PolygonType * p, const BoxType * clip, void (*emit) (PLINE *, void *), void *user_data);
+void PolyToPolygonsOnLayer(DataType *, LayerType *, POLYAREA *, FlagType);
+
+void square_pin_factors(int style, double *xm, double *ym);
+
+
+#endif
diff --git a/src/polygon1.c b/src/polygon1.c
new file mode 100644
index 0000000..c78e656
--- /dev/null
+++ b/src/polygon1.c
@@ -0,0 +1,3190 @@
+/*
+       polygon clipping functions. harry eaton implemented the algorithm
+       described in "A Closed Set of Algorithms for Performing Set
+       Operations on Polygonal Regions in the Plane" which the original
+       code did not do. I also modified it for integer coordinates
+       and faster computation. The license for this modified copy was
+       switched to the GPL per term (3) of the original LGPL license.
+       Copyright (C) 2006 harry eaton
+ 
+   based on:
+       poly_Boolean: a polygon clip library
+       Copyright (C) 1997  Alexey Nikitin, Michael Leonov
+       (also the authors of the paper describing the actual algorithm)
+       leonov at propro.iis.nsk.su
+
+   in turn based on:
+       nclip: a polygon clip library
+       Copyright (C) 1993  Klamer Schutte
+ 
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public
+       License as published by the Free Software Foundation; either
+       version 2 of the License, or (at your option) any later version.
+ 
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       General Public License for more details.
+ 
+       You should have received a copy of the GNU General Public
+       License along with this program; if not, write to the Free
+       Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+      polygon1.c
+      (C) 1997 Alexey Nikitin, Michael Leonov
+      (C) 1993 Klamer Schutte
+
+      all cases where original (Klamer Schutte) code is present
+      are marked
+*/
+
+#include	<assert.h>
+#include	<stdlib.h>
+#include	<stdio.h>
+#include	<setjmp.h>
+#include	<math.h>
+#include	<string.h>
+
+#include "rtree.h"
+#include "heap.h"
+#include "pcb-printf.h"
+
+#define ROUND(a) (long)((a) > 0 ? ((a) + 0.5) : ((a) - 0.5))
+
+#define EPSILON (1E-8)
+#define IsZero(a, b) (fabs((a) - (b)) < EPSILON)
+
+/*********************************************************************/
+/*              L o n g   V e c t o r   S t u f f                    */
+/*********************************************************************/
+
+#define Vcopy(a,b) {(a)[0]=(b)[0];(a)[1]=(b)[1];}
+int vect_equal(Vector v1, Vector v2);
+void vect_init(Vector v, double x, double y);
+void vect_sub(Vector res, Vector v2, Vector v3);
+
+void vect_min(Vector res, Vector v2, Vector v3);
+void vect_max(Vector res, Vector v2, Vector v3);
+
+double vect_dist2(Vector v1, Vector v2);
+double vect_det2(Vector v1, Vector v2);
+double vect_len2(Vector v1);
+
+int vect_inters2(Vector A, Vector B, Vector C, Vector D, Vector S1, Vector S2);
+
+/* note that a vertex v's Flags.status represents the edge defined by
+ * v to v->next (i.e. the edge is forward of v)
+ */
+#define ISECTED 3
+#define UNKNWN  0
+#define INSIDE  1
+#define OUTSIDE 2
+#define SHARED 3
+#define SHARED2 4
+
+#define TOUCHES 99
+
+#define NODE_LABEL(n)  ((n)->Flags.status)
+#define LABEL_NODE(n,l) ((n)->Flags.status = (l))
+
+#define error(code)  longjmp(*(e), code)
+
+#define MemGet(ptr, type) \
+  if (UNLIKELY (((ptr) = (type *)malloc(sizeof(type))) == NULL))	\
+    error(err_no_memory);
+
+#undef DEBUG_LABEL
+#undef DEBUG_ALL_LABELS
+#undef DEBUG_JUMP
+#undef DEBUG_GATHER
+#undef DEBUG_ANGLE
+#undef DEBUG
+#ifdef DEBUG
+#include <stdarg.h>
+static void DEBUGP(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	pcb_vfprintf(stderr, fmt, ap);
+	va_end(ap);
+}
+#else
+static void DEBUGP(const char *fmt, ...) { }
+#endif
+
+/* ///////////////////////////////////////////////////////////////////////////// * /
+/ *  2-Dimensional stuff
+/ * ///////////////////////////////////////////////////////////////////////////// */
+
+#define Vsub2(r,a,b)	{(r)[0] = (a)[0] - (b)[0]; (r)[1] = (a)[1] - (b)[1];}
+#define Vadd2(r,a,b)	{(r)[0] = (a)[0] + (b)[0]; (r)[1] = (a)[1] + (b)[1];}
+#define Vsca2(r,a,s)	{(r)[0] = (a)[0] * (s); (r)[1] = (a)[1] * (s);}
+#define Vcpy2(r,a)		{(r)[0] = (a)[0]; (r)[1] = (a)[1];}
+#define Vequ2(a,b)		((a)[0] == (b)[0] && (a)[1] == (b)[1])
+#define Vadds(r,a,b,s)	{(r)[0] = ((a)[0] + (b)[0]) * (s); (r)[1] = ((a)[1] + (b)[1]) * (s);}
+#define Vswp2(a,b) { long t; \
+	t = (a)[0], (a)[0] = (b)[0], (b)[0] = t; \
+	t = (a)[1], (a)[1] = (b)[1], (b)[1] = t; \
+}
+
+#ifdef DEBUG
+static char *theState(VNODE * v);
+
+static void pline_dump(VNODE * v)
+{
+	VNODE *s, *n;
+
+	s = v;
+	do {
+		n = v->next;
+		pcb_fprintf(stderr, "Line [%#mS %#mS %#mS %#mS 10 10 \"%s\"]\n",
+								v->point[0], v->point[1], n->point[0], n->point[1], theState(v));
+	}
+	while ((v = v->next) != s);
+}
+
+static void poly_dump(POLYAREA * p)
+{
+	POLYAREA *f = p;
+	PLINE *pl;
+
+	do {
+		pl = p->contours;
+		do {
+			pline_dump(&pl->head);
+			fprintf(stderr, "NEXT PLINE\n");
+		}
+		while ((pl = pl->next) != NULL);
+		fprintf(stderr, "NEXT POLY\n");
+	}
+	while ((p = p->f) != f);
+}
+#endif
+
+/***************************************************************/
+/* routines for processing intersections */
+
+/*
+node_add
+ (C) 1993 Klamer Schutte
+ (C) 1997 Alexey Nikitin, Michael Leonov
+ (C) 2006 harry eaton
+
+ returns a bit field in new_point that indicates where the
+ point was.
+ 1 means a new node was created and inserted
+ 4 means the intersection was not on the dest point
+*/
+static VNODE *node_add_single(VNODE * dest, Vector po)
+{
+	VNODE *p;
+
+	if (vect_equal(po, dest->point))
+		return dest;
+	if (vect_equal(po, dest->next->point))
+		return dest->next;
+	p = poly_CreateNode(po);
+	if (p == NULL)
+		return NULL;
+	p->cvc_prev = p->cvc_next = NULL;
+	p->Flags.status = UNKNWN;
+	return p;
+}																/* node_add */
+
+#define ISECT_BAD_PARAM (-1)
+#define ISECT_NO_MEMORY (-2)
+
+
+/*
+new_descriptor
+  (C) 2006 harry eaton
+*/
+static CVCList *new_descriptor(VNODE * a, char poly, char side)
+{
+	CVCList *l = (CVCList *) malloc(sizeof(CVCList));
+	Vector v;
+	register double ang, dx, dy;
+
+	if (!l)
+		return NULL;
+	l->head = NULL;
+	l->parent = a;
+	l->poly = poly;
+	l->side = side;
+	l->next = l->prev = l;
+	if (side == 'P')							/* previous */
+		vect_sub(v, a->prev->point, a->point);
+	else													/* next */
+		vect_sub(v, a->next->point, a->point);
+	/* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
+	 * It still has the same monotonic sort result
+	 * and is far less expensive to compute than the real angle.
+	 */
+	if (vect_equal(v, vect_zero)) {
+		if (side == 'P') {
+			if (a->prev->cvc_prev == (CVCList *) - 1)
+				a->prev->cvc_prev = a->prev->cvc_next = NULL;
+			poly_ExclVertex(a->prev);
+			vect_sub(v, a->prev->point, a->point);
+		}
+		else {
+			if (a->next->cvc_prev == (CVCList *) - 1)
+				a->next->cvc_prev = a->next->cvc_next = NULL;
+			poly_ExclVertex(a->next);
+			vect_sub(v, a->next->point, a->point);
+		}
+	}
+	assert(!vect_equal(v, vect_zero));
+	dx = fabs((double) v[0]);
+	dy = fabs((double) v[1]);
+	ang = dy / (dy + dx);
+	/* now move to the actual quadrant */
+	if (v[0] < 0 && v[1] >= 0)
+		ang = 2.0 - ang;						/* 2nd quadrant */
+	else if (v[0] < 0 && v[1] < 0)
+		ang += 2.0;									/* 3rd quadrant */
+	else if (v[0] >= 0 && v[1] < 0)
+		ang = 4.0 - ang;						/* 4th quadrant */
+	l->angle = ang;
+	assert(ang >= 0.0 && ang <= 4.0);
+#ifdef DEBUG_ANGLE
+	DEBUGP("node on %c at %#mD assigned angle %g on side %c\n", poly, a->point[0], a->point[1], ang, side);
+#endif
+	return l;
+}
+
+/*
+insert_descriptor
+  (C) 2006 harry eaton
+
+   argument a is a cross-vertex node.
+   argument poly is the polygon it comes from ('A' or 'B')
+   argument side is the side this descriptor goes on ('P' for previous
+   'N' for next.
+   argument start is the head of the list of cvclists
+*/
+static CVCList *insert_descriptor(VNODE * a, char poly, char side, CVCList * start)
+{
+	CVCList *l, *newone, *big, *small;
+
+	if (!(newone = new_descriptor(a, poly, side)))
+		return NULL;
+	/* search for the CVCList for this point */
+	if (!start) {
+		start = newone;							/* return is also new, so we know where start is */
+		start->head = newone;				/* circular list */
+		return newone;
+	}
+	else {
+		l = start;
+		do {
+			assert(l->head);
+			if (l->parent->point[0] == a->point[0]
+					&& l->parent->point[1] == a->point[1]) {	/* this CVCList is at our point */
+				start = l;
+				newone->head = l->head;
+				break;
+			}
+			if (l->head->parent->point[0] == start->parent->point[0]
+					&& l->head->parent->point[1] == start->parent->point[1]) {
+				/* this seems to be a new point */
+				/* link this cvclist to the list of all cvclists */
+				for (; l->head != newone; l = l->next)
+					l->head = newone;
+				newone->head = start;
+				return newone;
+			}
+			l = l->head;
+		}
+		while (1);
+	}
+	assert(start);
+	l = big = small = start;
+	do {
+		if (l->next->angle < l->angle) {	/* find start/end of list */
+			small = l->next;
+			big = l;
+		}
+		else if (newone->angle >= l->angle && newone->angle <= l->next->angle) {
+			/* insert new cvc if it lies between existing points */
+			newone->prev = l;
+			newone->next = l->next;
+			l->next = l->next->prev = newone;
+			return newone;
+		}
+	}
+	while ((l = l->next) != start);
+	/* didn't find it between points, it must go on an end */
+	if (big->angle <= newone->angle) {
+		newone->prev = big;
+		newone->next = big->next;
+		big->next = big->next->prev = newone;
+		return newone;
+	}
+	assert(small->angle >= newone->angle);
+	newone->next = small;
+	newone->prev = small->prev;
+	small->prev = small->prev->next = newone;
+	return newone;
+}
+
+/*
+node_add_point
+ (C) 1993 Klamer Schutte
+ (C) 1997 Alexey Nikitin, Michael Leonov
+
+ return 1 if new node in b, 2 if new node in a and 3 if new node in both
+*/
+
+static VNODE *node_add_single_point(VNODE * a, Vector p)
+{
+	VNODE *next_a, *new_node;
+
+	next_a = a->next;
+
+	new_node = node_add_single(a, p);
+	assert(new_node != NULL);
+
+	new_node->cvc_prev = new_node->cvc_next = (CVCList *) - 1;
+
+	if (new_node == a || new_node == next_a)
+		return NULL;
+
+	return new_node;
+}																/* node_add_point */
+
+/*
+node_label
+ (C) 2006 harry eaton
+*/
+static unsigned int node_label(VNODE * pn)
+{
+	CVCList *first_l, *l;
+	char this_poly;
+	int region = UNKNWN;
+
+	assert(pn);
+	assert(pn->cvc_next);
+	this_poly = pn->cvc_next->poly;
+	/* search counter-clockwise in the cross vertex connectivity (CVC) list
+	 *
+	 * check for shared edges (that could be prev or next in the list since the angles are equal)
+	 * and check if this edge (pn -> pn->next) is found between the other poly's entry and exit
+	 */
+	if (pn->cvc_next->angle == pn->cvc_next->prev->angle)
+		l = pn->cvc_next->prev;
+	else
+		l = pn->cvc_next->next;
+
+	first_l = l;
+	while ((l->poly == this_poly) && (l != first_l->prev))
+		l = l->next;
+	assert(l->poly != this_poly);
+
+	assert(l && l->angle >= 0 && l->angle <= 4.0);
+	if (l->poly != this_poly) {
+		if (l->side == 'P') {
+			if (l->parent->prev->point[0] == pn->next->point[0] && l->parent->prev->point[1] == pn->next->point[1]) {
+				region = SHARED2;
+				pn->shared = l->parent->prev;
+			}
+			else
+				region = INSIDE;
+		}
+		else {
+			if (l->angle == pn->cvc_next->angle) {
+				assert(l->parent->next->point[0] == pn->next->point[0] && l->parent->next->point[1] == pn->next->point[1]);
+				region = SHARED;
+				pn->shared = l->parent;
+			}
+			else
+				region = OUTSIDE;
+		}
+	}
+	if (region == UNKNWN) {
+		for (l = l->next; l != pn->cvc_next; l = l->next) {
+			if (l->poly != this_poly) {
+				if (l->side == 'P')
+					region = INSIDE;
+				else
+					region = OUTSIDE;
+				break;
+			}
+		}
+	}
+	assert(region != UNKNWN);
+	assert(NODE_LABEL(pn) == UNKNWN || NODE_LABEL(pn) == region);
+	LABEL_NODE(pn, region);
+	if (region == SHARED || region == SHARED2)
+		return UNKNWN;
+	return region;
+}																/* node_label */
+
+/*
+ add_descriptors
+ (C) 2006 harry eaton
+*/
+static CVCList *add_descriptors(PLINE * pl, char poly, CVCList * list)
+{
+	VNODE *node = &pl->head;
+
+	do {
+		if (node->cvc_prev) {
+			assert(node->cvc_prev == (CVCList *) - 1 && node->cvc_next == (CVCList *) - 1);
+			list = node->cvc_prev = insert_descriptor(node, poly, 'P', list);
+			if (!node->cvc_prev)
+				return NULL;
+			list = node->cvc_next = insert_descriptor(node, poly, 'N', list);
+			if (!node->cvc_next)
+				return NULL;
+		}
+	}
+	while ((node = node->next) != &pl->head);
+	return list;
+}
+
+static inline void cntrbox_adjust(PLINE * c, Vector p)
+{
+	c->xmin = min(c->xmin, p[0]);
+	c->xmax = max(c->xmax, p[0] + 1);
+	c->ymin = min(c->ymin, p[1]);
+	c->ymax = max(c->ymax, p[1] + 1);
+}
+
+/* some structures for handling segment intersections using the rtrees */
+
+typedef struct seg {
+	BoxType box;
+	VNODE *v;
+	PLINE *p;
+	int intersected;
+} seg;
+
+typedef struct _insert_node_task insert_node_task;
+
+struct _insert_node_task {
+	insert_node_task *next;
+	seg *node_seg;
+	VNODE *new_node;
+};
+
+typedef struct info {
+	double m, b;
+	rtree_t *tree;
+	VNODE *v;
+	struct seg *s;
+	jmp_buf *env, sego, *touch;
+	int need_restart;
+	insert_node_task *node_insert_list;
+} info;
+
+typedef struct contour_info {
+	PLINE *pa;
+	jmp_buf restart;
+	jmp_buf *getout;
+	int need_restart;
+	insert_node_task *node_insert_list;
+} contour_info;
+
+
+/*
+ * adjust_tree()
+ * (C) 2006 harry eaton
+ * This replaces the segment in the tree with the two new segments after
+ * a vertex has been added
+ */
+static int adjust_tree(rtree_t * tree, struct seg *s)
+{
+	struct seg *q;
+
+	q = (seg *) malloc(sizeof(struct seg));
+	if (!q)
+		return 1;
+	q->intersected = 0;
+	q->v = s->v;
+	q->p = s->p;
+	q->box.X1 = min(q->v->point[0], q->v->next->point[0]);
+	q->box.X2 = max(q->v->point[0], q->v->next->point[0]) + 1;
+	q->box.Y1 = min(q->v->point[1], q->v->next->point[1]);
+	q->box.Y2 = max(q->v->point[1], q->v->next->point[1]) + 1;
+	r_insert_entry(tree, (const BoxType *) q, 1);
+	q = (seg *) malloc(sizeof(struct seg));
+	if (!q)
+		return 1;
+	q->intersected = 0;
+	q->v = s->v->next;
+	q->p = s->p;
+	q->box.X1 = min(q->v->point[0], q->v->next->point[0]);
+	q->box.X2 = max(q->v->point[0], q->v->next->point[0]) + 1;
+	q->box.Y1 = min(q->v->point[1], q->v->next->point[1]);
+	q->box.Y2 = max(q->v->point[1], q->v->next->point[1]) + 1;
+	r_insert_entry(tree, (const BoxType *) q, 1);
+	r_delete_entry(tree, (const BoxType *) s);
+	return 0;
+}
+
+/*
+ * seg_in_region()
+ * (C) 2006, harry eaton
+ * This prunes the search for boxes that don't intersect the segment.
+ */
+static r_dir_t seg_in_region(const BoxType * b, void *cl)
+{
+	struct info *i = (struct info *) cl;
+	double y1, y2;
+	/* for zero slope the search is aligned on the axis so it is already pruned */
+	if (i->m == 0.)
+		return R_DIR_FOUND_CONTINUE;
+	y1 = i->m * b->X1 + i->b;
+	y2 = i->m * b->X2 + i->b;
+	if (min(y1, y2) >= b->Y2)
+		return R_DIR_NOT_FOUND;
+	if (max(y1, y2) < b->Y1)
+		return R_DIR_NOT_FOUND;
+	return R_DIR_FOUND_CONTINUE;											/* might intersect */
+}
+
+/* Prepend a deferred node-insertion task to a list */
+static insert_node_task *prepend_insert_node_task(insert_node_task * list, seg * seg, VNODE * new_node)
+{
+	insert_node_task *task = (insert_node_task *) malloc(sizeof(*task));
+	task->node_seg = seg;
+	task->new_node = new_node;
+	task->next = list;
+	return task;
+}
+
+/*
+ * seg_in_seg()
+ * (C) 2006 harry eaton
+ * This routine checks if the segment in the tree intersect the search segment.
+ * If it does, the plines are marked as intersected and the point is marked for
+ * the cvclist. If the point is not already a vertex, a new vertex is inserted
+ * and the search for intersections starts over at the beginning.
+ * That is potentially a significant time penalty, but it does solve the snap rounding
+ * problem. There are efficient algorithms for finding intersections with snap
+ * rounding, but I don't have time to implement them right now.
+ */
+static r_dir_t seg_in_seg(const BoxType * b, void *cl)
+{
+	struct info *i = (struct info *) cl;
+	struct seg *s = (struct seg *) b;
+	Vector s1, s2;
+	int cnt;
+	VNODE *new_node;
+
+	/* When new nodes are added at the end of a pass due to an intersection
+	 * the segments may be altered. If either segment we're looking at has
+	 * already been intersected this pass, skip it until the next pass.
+	 */
+	if (s->intersected || i->s->intersected)
+		return R_DIR_NOT_FOUND;
+
+	cnt = vect_inters2(s->v->point, s->v->next->point, i->v->point, i->v->next->point, s1, s2);
+	if (!cnt)
+		return R_DIR_NOT_FOUND;
+	if (i->touch)									/* if checking touches one find and we're done */
+		longjmp(*i->touch, TOUCHES);
+	i->s->p->Flags.status = ISECTED;
+	s->p->Flags.status = ISECTED;
+	for (; cnt; cnt--) {
+		pcb_bool done_insert_on_i = pcb_false;
+		new_node = node_add_single_point(i->v, cnt > 1 ? s2 : s1);
+		if (new_node != NULL) {
+#ifdef DEBUG_INTERSECT
+			DEBUGP("new intersection on segment \"i\" at %#mD\n", cnt > 1 ? s2[0] : s1[0], cnt > 1 ? s2[1] : s1[1]);
+#endif
+			i->node_insert_list = prepend_insert_node_task(i->node_insert_list, i->s, new_node);
+			i->s->intersected = 1;
+			done_insert_on_i = pcb_true;
+		}
+		new_node = node_add_single_point(s->v, cnt > 1 ? s2 : s1);
+		if (new_node != NULL) {
+#ifdef DEBUG_INTERSECT
+			DEBUGP("new intersection on segment \"s\" at %#mD\n", cnt > 1 ? s2[0] : s1[0], cnt > 1 ? s2[1] : s1[1]);
+#endif
+			i->node_insert_list = prepend_insert_node_task(i->node_insert_list, s, new_node);
+			s->intersected = 1;
+			return R_DIR_NOT_FOUND;									/* Keep looking for intersections with segment "i" */
+		}
+		/* Skip any remaining r_search hits against segment i, as any further
+		 * intersections will be rejected until the next pass anyway.
+		 */
+		if (done_insert_on_i)
+			longjmp(*i->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static void *make_edge_tree(PLINE * pb)
+{
+	struct seg *s;
+	VNODE *bv;
+	rtree_t *ans = r_create_tree(NULL, 0, 0);
+	bv = &pb->head;
+	do {
+		s = (seg *) malloc(sizeof(struct seg));
+		s->intersected = 0;
+		if (bv->point[0] < bv->next->point[0]) {
+			s->box.X1 = bv->point[0];
+			s->box.X2 = bv->next->point[0] + 1;
+		}
+		else {
+			s->box.X2 = bv->point[0] + 1;
+			s->box.X1 = bv->next->point[0];
+		}
+		if (bv->point[1] < bv->next->point[1]) {
+			s->box.Y1 = bv->point[1];
+			s->box.Y2 = bv->next->point[1] + 1;
+		}
+		else {
+			s->box.Y2 = bv->point[1] + 1;
+			s->box.Y1 = bv->next->point[1];
+		}
+		s->v = bv;
+		s->p = pb;
+		r_insert_entry(ans, (const BoxType *) s, 1);
+	}
+	while ((bv = bv->next) != &pb->head);
+	return (void *) ans;
+}
+
+static r_dir_t get_seg(const BoxType * b, void *cl)
+{
+	struct info *i = (struct info *) cl;
+	struct seg *s = (struct seg *) b;
+	if (i->v == s->v) {
+		i->s = s;
+		return R_DIR_CANCEL; /* found */
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/*
+ * intersect() (and helpers)
+ * (C) 2006, harry eaton
+ * This uses an rtree to find A-B intersections. Whenever a new vertex is
+ * added, the search for intersections is re-started because the rounding
+ * could alter the topology otherwise. 
+ * This should use a faster algorithm for snap rounding intersection finding.
+ * The best algorithm is probably found in:
+ *
+ * "Improved output-sensitive snap rounding," John Hershberger, Proceedings
+ * of the 22nd annual symposium on Computational geometry, 2006, pp 357-366.
+ * http://doi.acm.org/10.1145/1137856.1137909
+ *
+ * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby would
+ * probably work as well.
+ *
+ */
+
+static r_dir_t contour_bounds_touch(const BoxType * b, void *cl)
+{
+	contour_info *c_info = (contour_info *) cl;
+	PLINE *pa = c_info->pa;
+	PLINE *pb = (PLINE *) b;
+	PLINE *rtree_over;
+	PLINE *looping_over;
+	VNODE *av;										/* node iterators */
+	struct info info;
+	BoxType box;
+	jmp_buf restart;
+
+	/* Have seg_in_seg return to our desired location if it touches */
+	info.env = &restart;
+	info.touch = c_info->getout;
+	info.need_restart = 0;
+	info.node_insert_list = c_info->node_insert_list;
+
+	/* Pick which contour has the fewer points, and do the loop
+	 * over that. The r_tree makes hit-testing against a contour
+	 * faster, so we want to do that on the bigger contour.
+	 */
+	if (pa->Count < pb->Count) {
+		rtree_over = pb;
+		looping_over = pa;
+	}
+	else {
+		rtree_over = pa;
+		looping_over = pb;
+	}
+
+	av = &looping_over->head;
+	do {													/* Loop over the nodes in the smaller contour */
+		r_dir_t rres;
+		/* check this edge for any insertions */
+		double dx;
+		info.v = av;
+		/* compute the slant for region trimming */
+		dx = av->next->point[0] - av->point[0];
+		if (dx == 0)
+			info.m = 0;
+		else {
+			info.m = (av->next->point[1] - av->point[1]) / dx;
+			info.b = av->point[1] - info.m * av->point[0];
+		}
+		box.X2 = (box.X1 = av->point[0]) + 1;
+		box.Y2 = (box.Y1 = av->point[1]) + 1;
+
+		/* fill in the segment in info corresponding to this node */
+		rres = r_search(looping_over->tree, &box, NULL, get_seg, &info, NULL);
+		assert(rres == R_DIR_CANCEL);
+
+		/* If we're going to have another pass anyway, skip this */
+		if (info.s->intersected && info.node_insert_list != NULL)
+			continue;
+
+		if (setjmp(restart))
+			continue;
+
+		/* NB: If this actually hits anything, we are teleported back to the beginning */
+		info.tree = rtree_over->tree;
+		if (info.tree) {
+			int seen;
+			r_search(info.tree, &info.s->box, seg_in_region, seg_in_seg, &info, &seen);
+			if (UNLIKELY(seen))
+				assert(0);							/* XXX: Memory allocation failure */
+		}
+	}
+	while ((av = av->next) != &looping_over->head);
+
+	c_info->node_insert_list = info.node_insert_list;
+	if (info.need_restart)
+		c_info->need_restart = 1;
+	return R_DIR_NOT_FOUND;
+}
+
+static int intersect_impl(jmp_buf * jb, POLYAREA * b, POLYAREA * a, int add)
+{
+	POLYAREA *t;
+	PLINE *pa;
+	contour_info c_info;
+	int need_restart = 0;
+	insert_node_task *task;
+	c_info.need_restart = 0;
+	c_info.node_insert_list = NULL;
+
+	/* Search the r-tree of the object with most contours
+	 * We loop over the contours of "a". Swap if necessary.
+	 */
+	if (a->contour_tree->size > b->contour_tree->size) {
+		t = b;
+		b = a;
+		a = t;
+	}
+
+	for (pa = a->contours; pa; pa = pa->next) {	/* Loop over the contours of POLYAREA "a" */
+		BoxType sb;
+		jmp_buf out;
+		int retval;
+
+		c_info.getout = NULL;
+		c_info.pa = pa;
+
+		if (!add) {
+			retval = setjmp(out);
+			if (retval) {
+				/* The intersection test short-circuited back here,
+				 * we need to clean up, then longjmp to jb */
+				longjmp(*jb, retval);
+			}
+			c_info.getout = &out;
+		}
+
+		sb.X1 = pa->xmin;
+		sb.Y1 = pa->ymin;
+		sb.X2 = pa->xmax + 1;
+		sb.Y2 = pa->ymax + 1;
+
+		r_search(b->contour_tree, &sb, NULL, contour_bounds_touch, &c_info, NULL);
+		if (c_info.need_restart)
+			need_restart = 1;
+	}
+
+	/* Process any deferred node insertions */
+	task = c_info.node_insert_list;
+	while (task != NULL) {
+		insert_node_task *next = task->next;
+
+		/* Do insertion */
+		task->new_node->prev = task->node_seg->v;
+		task->new_node->next = task->node_seg->v->next;
+		task->node_seg->v->next->prev = task->new_node;
+		task->node_seg->v->next = task->new_node;
+		task->node_seg->p->Count++;
+
+		cntrbox_adjust(task->node_seg->p, task->new_node->point);
+		if (adjust_tree(task->node_seg->p->tree, task->node_seg))
+			assert(0);								/* XXX: Memory allocation failure */
+
+		need_restart = 1;						/* Any new nodes could intersect */
+
+		free(task);
+		task = next;
+	}
+
+	return need_restart;
+}
+
+static int intersect(jmp_buf * jb, POLYAREA * b, POLYAREA * a, int add)
+{
+	int call_count = 1;
+	while (intersect_impl(jb, b, a, add))
+		call_count++;
+	return 0;
+}
+
+static void M_POLYAREA_intersect(jmp_buf * e, POLYAREA * afst, POLYAREA * bfst, int add)
+{
+	POLYAREA *a = afst, *b = bfst;
+	PLINE *curcA, *curcB;
+	CVCList *the_list = NULL;
+
+	if (a == NULL || b == NULL)
+		error(err_bad_parm);
+	do {
+		do {
+			if (a->contours->xmax >= b->contours->xmin &&
+					a->contours->ymax >= b->contours->ymin &&
+					a->contours->xmin <= b->contours->xmax && a->contours->ymin <= b->contours->ymax) {
+				if (UNLIKELY(intersect(e, a, b, add)))
+					error(err_no_memory);
+			}
+		}
+		while (add && (a = a->f) != afst);
+		for (curcB = b->contours; curcB != NULL; curcB = curcB->next)
+			if (curcB->Flags.status == ISECTED) {
+				the_list = add_descriptors(curcB, 'B', the_list);
+				if (UNLIKELY(the_list == NULL))
+					error(err_no_memory);
+			}
+	}
+	while (add && (b = b->f) != bfst);
+	do {
+		for (curcA = a->contours; curcA != NULL; curcA = curcA->next)
+			if (curcA->Flags.status == ISECTED) {
+				the_list = add_descriptors(curcA, 'A', the_list);
+				if (UNLIKELY(the_list == NULL))
+					error(err_no_memory);
+			}
+	}
+	while (add && (a = a->f) != afst);
+}																/* M_POLYAREA_intersect */
+
+static inline int cntrbox_inside(PLINE * c1, PLINE * c2)
+{
+	assert(c1 != NULL && c2 != NULL);
+	return ((c1->xmin >= c2->xmin) && (c1->ymin >= c2->ymin) && (c1->xmax <= c2->xmax) && (c1->ymax <= c2->ymax));
+}
+
+/*****************************************************************/
+/* Routines for making labels */
+
+static r_dir_t count_contours_i_am_inside(const BoxType * b, void *cl)
+{
+	PLINE *me = (PLINE *) cl;
+	PLINE *check = (PLINE *) b;
+
+	if (poly_ContourInContour(check, me))
+		return R_DIR_FOUND_CONTINUE;
+	return R_DIR_NOT_FOUND;
+}
+
+/* cntr_in_M_POLYAREA
+returns poly is inside outfst ? TRUE : FALSE */
+static int cntr_in_M_POLYAREA(PLINE * poly, POLYAREA * outfst, BOOLp test)
+{
+	POLYAREA *outer = outfst;
+	heap_t *heap;
+
+	assert(poly != NULL);
+	assert(outer != NULL);
+
+	heap = heap_create();
+	do {
+		if (cntrbox_inside(poly, outer->contours))
+			heap_insert(heap, outer->contours->area, (void *) outer);
+	}
+	/* if checking touching, use only the first polygon */
+	while (!test && (outer = outer->f) != outfst);
+	/* we need only check the smallest poly container
+	 * but we must loop in case the box container is not
+	 * the poly container */
+	do {
+		int cnt;
+
+		if (heap_is_empty(heap))
+			break;
+		outer = (POLYAREA *) heap_remove_smallest(heap);
+
+		r_search(outer->contour_tree, (BoxType *) poly, NULL, count_contours_i_am_inside, poly, &cnt);
+		switch (cnt) {
+		case 0:										/* Didn't find anything in this piece, Keep looking */
+			break;
+		case 1:										/* Found we are inside this piece, and not any of its holes */
+			heap_destroy(&heap);
+			return TRUE;
+		case 2:										/* Found inside a hole in the smallest polygon so far. No need to check the other polygons */
+			heap_destroy(&heap);
+			return FALSE;
+		default:
+			printf("Something strange here\n");
+			break;
+		}
+	}
+	while (1);
+	heap_destroy(&heap);
+	return FALSE;
+}																/* cntr_in_M_POLYAREA */
+
+#ifdef DEBUG
+
+static char *theState(VNODE * v)
+{
+	static char u[] = "UNKNOWN";
+	static char i[] = "INSIDE";
+	static char o[] = "OUTSIDE";
+	static char s[] = "SHARED";
+	static char s2[] = "SHARED2";
+
+	switch (NODE_LABEL(v)) {
+	case INSIDE:
+		return i;
+	case OUTSIDE:
+		return o;
+	case SHARED:
+		return s;
+	case SHARED2:
+		return s2;
+	default:
+		return u;
+	}
+}
+
+#ifdef DEBUG_ALL_LABELS
+static void print_labels(PLINE * a)
+{
+	VNODE *c = &a->head;
+
+	do {
+		DEBUGP("%#mD->%#mD labeled %s\n", c->point[0], c->point[1], c->next->point[0], c->next->point[1], theState(c));
+	}
+	while ((c = c->next) != &a->head);
+}
+#endif
+#endif
+
+/*
+label_contour
+ (C) 2006 harry eaton
+ (C) 1993 Klamer Schutte
+ (C) 1997 Alexey Nikitin, Michael Leonov
+*/
+
+static BOOLp label_contour(PLINE * a)
+{
+	VNODE *cur = &a->head;
+	VNODE *first_labelled = NULL;
+	int label = UNKNWN;
+
+	do {
+		if (cur->cvc_next) {				/* examine cross vertex */
+			label = node_label(cur);
+			if (first_labelled == NULL)
+				first_labelled = cur;
+			continue;
+		}
+
+		if (first_labelled == NULL)
+			continue;
+
+		/* This labels nodes which aren't cross-connected */
+		assert(label == INSIDE || label == OUTSIDE);
+		LABEL_NODE(cur, label);
+	}
+	while ((cur = cur->next) != first_labelled);
+#ifdef DEBUG_ALL_LABELS
+	print_labels(a);
+	DEBUGP("\n\n");
+#endif
+	return FALSE;
+}																/* label_contour */
+
+static BOOLp cntr_label_POLYAREA(PLINE * poly, POLYAREA * ppl, BOOLp test)
+{
+	assert(ppl != NULL && ppl->contours != NULL);
+	if (poly->Flags.status == ISECTED) {
+		label_contour(poly);				/* should never get here when BOOLp is pcb_true */
+	}
+	else if (cntr_in_M_POLYAREA(poly, ppl, test)) {
+		if (test)
+			return TRUE;
+		poly->Flags.status = INSIDE;
+	}
+	else {
+		if (test)
+			return pcb_false;
+		poly->Flags.status = OUTSIDE;
+	}
+	return FALSE;
+}																/* cntr_label_POLYAREA */
+
+static BOOLp M_POLYAREA_label_separated(PLINE * afst, POLYAREA * b, BOOLp touch)
+{
+	PLINE *curc = afst;
+
+	for (curc = afst; curc != NULL; curc = curc->next) {
+		if (cntr_label_POLYAREA(curc, b, touch) && touch)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static BOOLp M_POLYAREA_label(POLYAREA * afst, POLYAREA * b, BOOLp touch)
+{
+	POLYAREA *a = afst;
+	PLINE *curc;
+
+	assert(a != NULL);
+	do {
+		for (curc = a->contours; curc != NULL; curc = curc->next)
+			if (cntr_label_POLYAREA(curc, b, touch)) {
+				if (touch)
+					return TRUE;
+			}
+	}
+	while (!touch && (a = a->f) != afst);
+	return FALSE;
+}
+
+/****************************************************************/
+
+/* routines for temporary storing resulting contours */
+static void InsCntr(jmp_buf * e, PLINE * c, POLYAREA ** dst)
+{
+	POLYAREA *newp;
+
+	if (*dst == NULL) {
+		MemGet(*dst, POLYAREA);
+		(*dst)->f = (*dst)->b = *dst;
+		newp = *dst;
+	}
+	else {
+		MemGet(newp, POLYAREA);
+		newp->f = *dst;
+		newp->b = (*dst)->b;
+		newp->f->b = newp->b->f = newp;
+	}
+	newp->contours = c;
+	newp->contour_tree = r_create_tree(NULL, 0, 0);
+	r_insert_entry(newp->contour_tree, (BoxTypePtr) c, 0);
+	c->next = NULL;
+}																/* InsCntr */
+
+static void
+PutContour(jmp_buf * e, PLINE * cntr, POLYAREA ** contours, PLINE ** holes,
+					 POLYAREA * owner, POLYAREA * parent, PLINE * parent_contour)
+{
+	assert(cntr != NULL);
+	assert(cntr->Count > 2);
+	cntr->next = NULL;
+
+	if (cntr->Flags.orient == PLF_DIR) {
+		if (owner != NULL)
+			r_delete_entry(owner->contour_tree, (BoxType *) cntr);
+		InsCntr(e, cntr, contours);
+	}
+	/* put hole into temporary list */
+	else {
+		/* if we know this belongs inside the parent, put it there now */
+		if (parent_contour) {
+			cntr->next = parent_contour->next;
+			parent_contour->next = cntr;
+			if (owner != parent) {
+				if (owner != NULL)
+					r_delete_entry(owner->contour_tree, (BoxType *) cntr);
+				r_insert_entry(parent->contour_tree, (BoxType *) cntr, 0);
+			}
+		}
+		else {
+			cntr->next = *holes;
+			*holes = cntr;						/* let cntr be 1st hole in list */
+			/* We don't insert the holes into an r-tree,
+			 * they just form a linked list */
+			if (owner != NULL)
+				r_delete_entry(owner->contour_tree, (BoxType *) cntr);
+		}
+	}
+}																/* PutContour */
+
+static inline void remove_contour(POLYAREA * piece, PLINE * prev_contour, PLINE * contour, int remove_rtree_entry)
+{
+	if (piece->contours == contour)
+		piece->contours = contour->next;
+	else if (prev_contour != NULL) {
+		assert(prev_contour->next == contour);
+		prev_contour->next = contour->next;
+	}
+
+	contour->next = NULL;
+
+	if (remove_rtree_entry)
+		r_delete_entry(piece->contour_tree, (BoxType *) contour);
+}
+
+struct polyarea_info {
+	BoxType BoundingBox;
+	POLYAREA *pa;
+};
+
+static r_dir_t heap_it(const BoxType * b, void *cl)
+{
+	heap_t *heap = (heap_t *) cl;
+	struct polyarea_info *pa_info = (struct polyarea_info *) b;
+	PLINE *p = pa_info->pa->contours;
+	if (p->Count == 0)
+		return R_DIR_NOT_FOUND;										/* how did this happen? */
+	heap_insert(heap, p->area, pa_info);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+struct find_inside_info {
+	jmp_buf jb;
+	PLINE *want_inside;
+	PLINE *result;
+};
+
+static r_dir_t find_inside(const BoxType * b, void *cl)
+{
+	struct find_inside_info *info = (struct find_inside_info *) cl;
+	PLINE *check = (PLINE *) b;
+	/* Do test on check to see if it inside info->want_inside */
+	/* If it is: */
+	if (check->Flags.orient == PLF_DIR) {
+		return R_DIR_NOT_FOUND;
+	}
+	if (poly_ContourInContour(info->want_inside, check)) {
+		info->result = check;
+		longjmp(info->jb, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static void InsertHoles(jmp_buf * e, POLYAREA * dest, PLINE ** src)
+{
+	POLYAREA *curc;
+	PLINE *curh, *container;
+	heap_t *heap;
+	rtree_t *tree;
+	int i;
+	int num_polyareas = 0;
+	struct polyarea_info *all_pa_info, *pa_info;
+
+	if (*src == NULL)
+		return;											/* empty hole list */
+	if (dest == NULL)
+		error(err_bad_parm);				/* empty contour list */
+
+	/* Count dest polyareas */
+	curc = dest;
+	do {
+		num_polyareas++;
+	}
+	while ((curc = curc->f) != dest);
+
+	/* make a polyarea info table */
+	/* make an rtree of polyarea info table */
+	all_pa_info = (struct polyarea_info *) malloc(sizeof(struct polyarea_info) * num_polyareas);
+	tree = r_create_tree(NULL, 0, 0);
+	i = 0;
+	curc = dest;
+	do {
+		all_pa_info[i].BoundingBox.X1 = curc->contours->xmin;
+		all_pa_info[i].BoundingBox.Y1 = curc->contours->ymin;
+		all_pa_info[i].BoundingBox.X2 = curc->contours->xmax;
+		all_pa_info[i].BoundingBox.Y2 = curc->contours->ymax;
+		all_pa_info[i].pa = curc;
+		r_insert_entry(tree, (const BoxType *) &all_pa_info[i], 0);
+		i++;
+	}
+	while ((curc = curc->f) != dest);
+
+	/* loop through the holes and put them where they belong */
+	while ((curh = *src) != NULL) {
+		*src = curh->next;
+
+		container = NULL;
+		/* build a heap of all of the polys that the hole is inside its bounding box */
+		heap = heap_create();
+		r_search(tree, (BoxType *) curh, NULL, heap_it, heap, NULL);
+		if (heap_is_empty(heap)) {
+#ifndef NDEBUG
+#ifdef DEBUG
+			poly_dump(dest);
+#endif
+#endif
+			poly_DelContour(&curh);
+			error(err_bad_parm);
+		}
+		/* Now search the heap for the container. If there was only one item
+		 * in the heap, assume it is the container without the expense of
+		 * proving it.
+		 */
+		pa_info = (struct polyarea_info *) heap_remove_smallest(heap);
+		if (heap_is_empty(heap)) {	/* only one possibility it must be the right one */
+			assert(poly_ContourInContour(pa_info->pa->contours, curh));
+			container = pa_info->pa->contours;
+		}
+		else {
+			do {
+				if (poly_ContourInContour(pa_info->pa->contours, curh)) {
+					container = pa_info->pa->contours;
+					break;
+				}
+				if (heap_is_empty(heap))
+					break;
+				pa_info = (struct polyarea_info *) heap_remove_smallest(heap);
+			}
+			while (1);
+		}
+		heap_destroy(&heap);
+		if (container == NULL) {
+			/* bad input polygons were given */
+#ifndef NDEBUG
+#ifdef DEBUG
+			poly_dump(dest);
+#endif
+#endif
+			curh->next = NULL;
+			poly_DelContour(&curh);
+			error(err_bad_parm);
+		}
+		else {
+			/* Need to check if this new hole means we need to kick out any old ones for reprocessing */
+			while (1) {
+				struct find_inside_info info;
+				PLINE *prev;
+
+				info.want_inside = curh;
+
+				/* Set jump return */
+				if (setjmp(info.jb)) {
+					/* Returned here! */
+				}
+				else {
+					info.result = NULL;
+					/* Rtree search, calling back a routine to longjmp back with data about any hole inside the added one */
+					/*   Be sure not to bother jumping back to report the main contour! */
+					r_search(pa_info->pa->contour_tree, (BoxType *) curh, NULL, find_inside, &info, NULL);
+
+					/* Nothing found? */
+					break;
+				}
+
+				/* We need to find the contour before it, so we can update its next pointer */
+				prev = container;
+				while (prev->next != info.result) {
+					prev = prev->next;
+				}
+
+				/* Remove hole from the contour */
+				remove_contour(pa_info->pa, prev, info.result, TRUE);
+
+				/* Add hole as the next on the list to be processed in this very function */
+				info.result->next = *src;
+				*src = info.result;
+			}
+			/* End check for kicked out holes */
+
+			/* link at front of hole list */
+			curh->next = container->next;
+			container->next = curh;
+			r_insert_entry(pa_info->pa->contour_tree, (BoxTypePtr) curh, 0);
+
+		}
+	}
+	r_destroy_tree(&tree);
+	free(all_pa_info);
+}																/* InsertHoles */
+
+
+/****************************************************************/
+/* routines for collecting result */
+
+typedef enum {
+	FORW, BACKW
+} DIRECTION;
+
+/* Start Rule */
+typedef int (*S_Rule) (VNODE *, DIRECTION *);
+
+/* Jump Rule  */
+typedef int (*J_Rule) (char, VNODE *, DIRECTION *);
+
+static int UniteS_Rule(VNODE * cur, DIRECTION * initdir)
+{
+	*initdir = FORW;
+	return (NODE_LABEL(cur) == OUTSIDE) || (NODE_LABEL(cur) == SHARED);
+}
+
+static int IsectS_Rule(VNODE * cur, DIRECTION * initdir)
+{
+	*initdir = FORW;
+	return (NODE_LABEL(cur) == INSIDE) || (NODE_LABEL(cur) == SHARED);
+}
+
+static int SubS_Rule(VNODE * cur, DIRECTION * initdir)
+{
+	*initdir = FORW;
+	return (NODE_LABEL(cur) == OUTSIDE) || (NODE_LABEL(cur) == SHARED2);
+}
+
+static int XorS_Rule(VNODE * cur, DIRECTION * initdir)
+{
+	if (cur->Flags.status == INSIDE) {
+		*initdir = BACKW;
+		return TRUE;
+	}
+	if (cur->Flags.status == OUTSIDE) {
+		*initdir = FORW;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static int IsectJ_Rule(char p, VNODE * v, DIRECTION * cdir)
+{
+	assert(*cdir == FORW);
+	return (v->Flags.status == INSIDE || v->Flags.status == SHARED);
+}
+
+static int UniteJ_Rule(char p, VNODE * v, DIRECTION * cdir)
+{
+	assert(*cdir == FORW);
+	return (v->Flags.status == OUTSIDE || v->Flags.status == SHARED);
+}
+
+static int XorJ_Rule(char p, VNODE * v, DIRECTION * cdir)
+{
+	if (v->Flags.status == INSIDE) {
+		*cdir = BACKW;
+		return TRUE;
+	}
+	if (v->Flags.status == OUTSIDE) {
+		*cdir = FORW;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static int SubJ_Rule(char p, VNODE * v, DIRECTION * cdir)
+{
+	if (p == 'B' && v->Flags.status == INSIDE) {
+		*cdir = BACKW;
+		return TRUE;
+	}
+	if (p == 'A' && v->Flags.status == OUTSIDE) {
+		*cdir = FORW;
+		return TRUE;
+	}
+	if (v->Flags.status == SHARED2) {
+		if (p == 'A')
+			*cdir = FORW;
+		else
+			*cdir = BACKW;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/* return the edge that comes next.
+ * if the direction is BACKW, then we return the next vertex
+ * so that prev vertex has the flags for the edge
+ *
+ * returns pcb_true if an edge is found, pcb_false otherwise
+ */
+static int jump(VNODE ** cur, DIRECTION * cdir, J_Rule rule)
+{
+	CVCList *d, *start;
+	VNODE *e;
+	DIRECTION newone;
+
+	if (!(*cur)->cvc_prev) {			/* not a cross-vertex */
+		if (*cdir == FORW ? (*cur)->Flags.mark : (*cur)->prev->Flags.mark)
+			return FALSE;
+		return TRUE;
+	}
+#ifdef DEBUG_JUMP
+	DEBUGP("jump entering node at %$mD\n", (*cur)->point[0], (*cur)->point[1]);
+#endif
+	if (*cdir == FORW)
+		d = (*cur)->cvc_prev->prev;
+	else
+		d = (*cur)->cvc_next->prev;
+	start = d;
+	do {
+		e = d->parent;
+		if (d->side == 'P')
+			e = e->prev;
+		newone = *cdir;
+		if (!e->Flags.mark && rule(d->poly, e, &newone)) {
+			if ((d->side == 'N' && newone == FORW) || (d->side == 'P' && newone == BACKW)) {
+#ifdef DEBUG_JUMP
+				if (newone == FORW)
+					DEBUGP("jump leaving node at %#mD\n", e->next->point[0], e->next->point[1]);
+				else
+					DEBUGP("jump leaving node at %#mD\n", e->point[0], e->point[1]);
+#endif
+				*cur = d->parent;
+				*cdir = newone;
+				return TRUE;
+			}
+		}
+	}
+	while ((d = d->prev) != start);
+	return FALSE;
+}
+
+static int Gather(VNODE * start, PLINE ** result, J_Rule v_rule, DIRECTION initdir)
+{
+	VNODE *cur = start, *newn;
+	DIRECTION dir = initdir;
+#ifdef DEBUG_GATHER
+	DEBUGP("gather direction = %d\n", dir);
+#endif
+	assert(*result == NULL);
+	do {
+		/* see where to go next */
+		if (!jump(&cur, &dir, v_rule))
+			break;
+		/* add edge to polygon */
+		if (!*result) {
+			*result = poly_NewContour(cur->point);
+			if (*result == NULL)
+				return err_no_memory;
+		}
+		else {
+			if ((newn = poly_CreateNode(cur->point)) == NULL)
+				return err_no_memory;
+			poly_InclVertex((*result)->head.prev, newn);
+		}
+#ifdef DEBUG_GATHER
+		DEBUGP("gather vertex at %#mD\n", cur->point[0], cur->point[1]);
+#endif
+		/* Now mark the edge as included.  */
+		newn = (dir == FORW ? cur : cur->prev);
+		newn->Flags.mark = 1;
+		/* for SHARED edge mark both */
+		if (newn->shared)
+			newn->shared->Flags.mark = 1;
+
+		/* Advance to the next edge.  */
+		cur = (dir == FORW ? cur->next : cur->prev);
+	}
+	while (1);
+	return err_ok;
+}																/* Gather */
+
+static void Collect1(jmp_buf * e, VNODE * cur, DIRECTION dir, POLYAREA ** contours, PLINE ** holes, J_Rule j_rule)
+{
+	PLINE *p = NULL;							/* start making contour */
+	int errc = err_ok;
+	if ((errc = Gather(dir == FORW ? cur : cur->next, &p, j_rule, dir)) != err_ok) {
+		if (p != NULL)
+			poly_DelContour(&p);
+		error(errc);
+	}
+	if (!p)
+		return;
+	poly_PreContour(p, TRUE);
+	if (p->Count > 2) {
+#ifdef DEBUG_GATHER
+		DEBUGP("adding contour with %d vertices and direction %c\n", p->Count, p->Flags.orient ? 'F' : 'B');
+#endif
+		PutContour(e, p, contours, holes, NULL, NULL, NULL);
+	}
+	else {
+		/* some sort of computation error ? */
+#ifdef DEBUG_GATHER
+		DEBUGP("Bad contour! Not enough points!!\n");
+#endif
+		poly_DelContour(&p);
+	}
+}
+
+static void Collect(jmp_buf * e, PLINE * a, POLYAREA ** contours, PLINE ** holes, S_Rule s_rule, J_Rule j_rule)
+{
+	VNODE *cur, *other;
+	DIRECTION dir;
+
+	cur = &a->head;
+	do {
+		if (s_rule(cur, &dir) && cur->Flags.mark == 0)
+			Collect1(e, cur, dir, contours, holes, j_rule);
+		other = cur;
+		if ((other->cvc_prev && jump(&other, &dir, j_rule)))
+			Collect1(e, other, dir, contours, holes, j_rule);
+	}
+	while ((cur = cur->next) != &a->head);
+}																/* Collect */
+
+
+static int
+cntr_Collect(jmp_buf * e, PLINE ** A, POLYAREA ** contours, PLINE ** holes,
+						 int action, POLYAREA * owner, POLYAREA * parent, PLINE * parent_contour)
+{
+	PLINE *tmprev;
+
+	if ((*A)->Flags.status == ISECTED) {
+		switch (action) {
+		case PBO_UNITE:
+			Collect(e, *A, contours, holes, UniteS_Rule, UniteJ_Rule);
+			break;
+		case PBO_ISECT:
+			Collect(e, *A, contours, holes, IsectS_Rule, IsectJ_Rule);
+			break;
+		case PBO_XOR:
+			Collect(e, *A, contours, holes, XorS_Rule, XorJ_Rule);
+			break;
+		case PBO_SUB:
+			Collect(e, *A, contours, holes, SubS_Rule, SubJ_Rule);
+			break;
+		};
+	}
+	else {
+		switch (action) {
+		case PBO_ISECT:
+			if ((*A)->Flags.status == INSIDE) {
+				tmprev = *A;
+				/* disappear this contour (rtree entry removed in PutContour) */
+				*A = tmprev->next;
+				tmprev->next = NULL;
+				PutContour(e, tmprev, contours, holes, owner, NULL, NULL);
+				return TRUE;
+			}
+			break;
+		case PBO_XOR:
+			if ((*A)->Flags.status == INSIDE) {
+				tmprev = *A;
+				/* disappear this contour (rtree entry removed in PutContour) */
+				*A = tmprev->next;
+				tmprev->next = NULL;
+				poly_InvContour(tmprev);
+				PutContour(e, tmprev, contours, holes, owner, NULL, NULL);
+				return TRUE;
+			}
+			/* break; *//* Fall through (I think this is correct! pcjc2) */
+		case PBO_UNITE:
+		case PBO_SUB:
+			if ((*A)->Flags.status == OUTSIDE) {
+				tmprev = *A;
+				/* disappear this contour (rtree entry removed in PutContour) */
+				*A = tmprev->next;
+				tmprev->next = NULL;
+				PutContour(e, tmprev, contours, holes, owner, parent, parent_contour);
+				return TRUE;
+			}
+			break;
+		}
+	}
+	return FALSE;
+}																/* cntr_Collect */
+
+static void M_B_AREA_Collect(jmp_buf * e, POLYAREA * bfst, POLYAREA ** contours, PLINE ** holes, int action)
+{
+	POLYAREA *b = bfst;
+	PLINE **cur, **next, *tmp;
+
+	assert(b != NULL);
+	do {
+		for (cur = &b->contours; *cur != NULL; cur = next) {
+			next = &((*cur)->next);
+			if ((*cur)->Flags.status == ISECTED)
+				continue;
+
+			if ((*cur)->Flags.status == INSIDE)
+				switch (action) {
+				case PBO_XOR:
+				case PBO_SUB:
+					poly_InvContour(*cur);
+				case PBO_ISECT:
+					tmp = *cur;
+					*cur = tmp->next;
+					next = cur;
+					tmp->next = NULL;
+					tmp->Flags.status = UNKNWN;
+					PutContour(e, tmp, contours, holes, b, NULL, NULL);
+					break;
+				case PBO_UNITE:
+					break;								/* nothing to do - already included */
+				}
+			else if ((*cur)->Flags.status == OUTSIDE)
+				switch (action) {
+				case PBO_XOR:
+				case PBO_UNITE:
+					/* include */
+					tmp = *cur;
+					*cur = tmp->next;
+					next = cur;
+					tmp->next = NULL;
+					tmp->Flags.status = UNKNWN;
+					PutContour(e, tmp, contours, holes, b, NULL, NULL);
+					break;
+				case PBO_ISECT:
+				case PBO_SUB:
+					break;								/* do nothing, not included */
+				}
+		}
+	}
+	while ((b = b->f) != bfst);
+}
+
+
+static inline int contour_is_first(POLYAREA * a, PLINE * cur)
+{
+	return (a->contours == cur);
+}
+
+
+static inline int contour_is_last(PLINE * cur)
+{
+	return (cur->next == NULL);
+}
+
+
+static inline void remove_polyarea(POLYAREA ** list, POLYAREA * piece)
+{
+	/* If this item was the start of the list, advance that pointer */
+	if (*list == piece)
+		*list = (*list)->f;
+
+	/* But reset it to NULL if it wraps around and hits us again */
+	if (*list == piece)
+		*list = NULL;
+
+	piece->b->f = piece->f;
+	piece->f->b = piece->b;
+	piece->f = piece->b = piece;
+}
+
+static void M_POLYAREA_separate_isected(jmp_buf * e, POLYAREA ** pieces, PLINE ** holes, PLINE ** isected)
+{
+	POLYAREA *a = *pieces;
+	POLYAREA *anext;
+	PLINE *curc, *next, *prev;
+	int finished;
+
+	if (a == NULL)
+		return;
+
+	/* TODO: STASH ENOUGH INFORMATION EARLIER ON, SO WE CAN REMOVE THE INTERSECTED
+	   CONTOURS WITHOUT HAVING TO WALK THE FULL DATA-STRUCTURE LOOKING FOR THEM. */
+
+	do {
+		int hole_contour = 0;
+		int is_outline = 1;
+
+		anext = a->f;
+		finished = (anext == *pieces);
+
+		prev = NULL;
+		for (curc = a->contours; curc != NULL; curc = next, is_outline = 0) {
+			int is_first = contour_is_first(a, curc);
+			int is_last = contour_is_last(curc);
+			int isect_contour = (curc->Flags.status == ISECTED);
+
+			next = curc->next;
+
+			if (isect_contour || hole_contour) {
+
+				/* Reset the intersection flags, since we keep these pieces */
+				if (curc->Flags.status != ISECTED)
+					curc->Flags.status = UNKNWN;
+
+				remove_contour(a, prev, curc, !(is_first && is_last));
+
+				if (isect_contour) {
+					/* Link into the list of intersected contours */
+					curc->next = *isected;
+					*isected = curc;
+				}
+				else if (hole_contour) {
+					/* Link into the list of holes */
+					curc->next = *holes;
+					*holes = curc;
+				}
+				else {
+					assert(0);
+				}
+
+				if (is_first && is_last) {
+					remove_polyarea(pieces, a);
+					poly_Free(&a);				/* NB: Sets a to NULL */
+				}
+
+			}
+			else {
+				/* Note the item we just didn't delete as the next
+				   candidate for having its "next" pointer adjusted.
+				   Saves walking the contour list when we delete one. */
+				prev = curc;
+			}
+
+			/* If we move or delete an outer contour, we need to move any holes
+			   we wish to keep within that contour to the holes list. */
+			if (is_outline && isect_contour)
+				hole_contour = 1;
+
+		}
+
+		/* If we deleted all the pieces of the polyarea, *pieces is NULL */
+	}
+	while ((a = anext), *pieces != NULL && !finished);
+}
+
+
+struct find_inside_m_pa_info {
+	jmp_buf jb;
+	POLYAREA *want_inside;
+	PLINE *result;
+};
+
+static r_dir_t find_inside_m_pa(const BoxType * b, void *cl)
+{
+	struct find_inside_m_pa_info *info = (struct find_inside_m_pa_info *) cl;
+	PLINE *check = (PLINE *) b;
+	/* Don't report for the main contour */
+	if (check->Flags.orient == PLF_DIR)
+		return R_DIR_NOT_FOUND;
+	/* Don't look at contours marked as being intersected */
+	if (check->Flags.status == ISECTED)
+		return R_DIR_NOT_FOUND;
+	if (cntr_in_M_POLYAREA(check, info->want_inside, FALSE)) {
+		info->result = check;
+		longjmp(info->jb, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+
+static void M_POLYAREA_update_primary(jmp_buf * e, POLYAREA ** pieces, PLINE ** holes, int action, POLYAREA * bpa)
+{
+	POLYAREA *a = *pieces;
+	POLYAREA *b;
+	POLYAREA *anext;
+	PLINE *curc, *next, *prev;
+	BoxType box;
+	/* int inv_inside = 0; */
+	int del_inside = 0;
+	int del_outside = 0;
+	int finished;
+
+	if (a == NULL)
+		return;
+
+	switch (action) {
+	case PBO_ISECT:
+		del_outside = 1;
+		break;
+	case PBO_UNITE:
+	case PBO_SUB:
+		del_inside = 1;
+		break;
+	case PBO_XOR:								/* NOT IMPLEMENTED OR USED */
+		/* inv_inside = 1; */
+		assert(0);
+		break;
+	}
+
+	box = *((BoxType *) bpa->contours);
+	b = bpa;
+	while ((b = b->f) != bpa) {
+		BoxType *b_box = (BoxType *) b->contours;
+		MAKEMIN(box.X1, b_box->X1);
+		MAKEMIN(box.Y1, b_box->Y1);
+		MAKEMAX(box.X2, b_box->X2);
+		MAKEMAX(box.Y2, b_box->Y2);
+	}
+
+	if (del_inside) {
+
+		do {
+			anext = a->f;
+			finished = (anext == *pieces);
+
+			/* Test the outer contour first, as we may need to remove all children */
+
+			/* We've not yet split intersected contours out, just ignore them */
+			if (a->contours->Flags.status != ISECTED &&
+					/* Pre-filter on bounding box */
+					((a->contours->xmin >= box.X1) && (a->contours->ymin >= box.Y1)
+					 && (a->contours->xmax <= box.X2)
+					 && (a->contours->ymax <= box.Y2)) &&
+					/* Then test properly */
+					cntr_in_M_POLYAREA(a->contours, bpa, FALSE)) {
+
+				/* Delete this contour, all children -> holes queue */
+
+				/* Delete the outer contour */
+				curc = a->contours;
+				remove_contour(a, NULL, curc, FALSE);	/* Rtree deleted in poly_Free below */
+				/* a->contours now points to the remaining holes */
+				poly_DelContour(&curc);
+
+				if (a->contours != NULL) {
+					/* Find the end of the list of holes */
+					curc = a->contours;
+					while (curc->next != NULL)
+						curc = curc->next;
+
+					/* Take the holes and prepend to the holes queue */
+					curc->next = *holes;
+					*holes = a->contours;
+					a->contours = NULL;
+				}
+
+				remove_polyarea(pieces, a);
+				poly_Free(&a);					/* NB: Sets a to NULL */
+
+				continue;
+			}
+
+			/* Loop whilst we find INSIDE contours to delete */
+			while (1) {
+				struct find_inside_m_pa_info info;
+				PLINE *prev;
+
+				info.want_inside = bpa;
+
+				/* Set jump return */
+				if (setjmp(info.jb)) {
+					/* Returned here! */
+				}
+				else {
+					info.result = NULL;
+					/* r-tree search, calling back a routine to longjmp back with
+					 * data about any hole inside the B polygon.
+					 * NB: Does not jump back to report the main contour!
+					 */
+					r_search(a->contour_tree, &box, NULL, find_inside_m_pa, &info, NULL);
+
+					/* Nothing found? */
+					break;
+				}
+
+				/* We need to find the contour before it, so we can update its next pointer */
+				prev = a->contours;
+				while (prev->next != info.result) {
+					prev = prev->next;
+				}
+
+				/* Remove hole from the contour */
+				remove_contour(a, prev, info.result, TRUE);
+				poly_DelContour(&info.result);
+			}
+			/* End check for deleted holes */
+
+			/* If we deleted all the pieces of the polyarea, *pieces is NULL */
+		}
+		while ((a = anext), *pieces != NULL && !finished);
+
+		return;
+	}
+	else {
+		/* This path isn't optimised for speed */
+	}
+
+	do {
+		int hole_contour = 0;
+		int is_outline = 1;
+
+		anext = a->f;
+		finished = (anext == *pieces);
+
+		prev = NULL;
+		for (curc = a->contours; curc != NULL; curc = next, is_outline = 0) {
+			int is_first = contour_is_first(a, curc);
+			int is_last = contour_is_last(curc);
+			int del_contour = 0;
+
+			next = curc->next;
+
+			if (del_outside)
+				del_contour = curc->Flags.status != ISECTED && !cntr_in_M_POLYAREA(curc, bpa, FALSE);
+
+			/* Skip intersected contours */
+			if (curc->Flags.status == ISECTED) {
+				prev = curc;
+				continue;
+			}
+
+			/* Reset the intersection flags, since we keep these pieces */
+			curc->Flags.status = UNKNWN;
+
+			if (del_contour || hole_contour) {
+
+				remove_contour(a, prev, curc, !(is_first && is_last));
+
+				if (del_contour) {
+					/* Delete the contour */
+					poly_DelContour(&curc);	/* NB: Sets curc to NULL */
+				}
+				else if (hole_contour) {
+					/* Link into the list of holes */
+					curc->next = *holes;
+					*holes = curc;
+				}
+				else {
+					assert(0);
+				}
+
+				if (is_first && is_last) {
+					remove_polyarea(pieces, a);
+					poly_Free(&a);				/* NB: Sets a to NULL */
+				}
+
+			}
+			else {
+				/* Note the item we just didn't delete as the next
+				   candidate for having its "next" pointer adjusted.
+				   Saves walking the contour list when we delete one. */
+				prev = curc;
+			}
+
+			/* If we move or delete an outer contour, we need to move any holes
+			   we wish to keep within that contour to the holes list. */
+			if (is_outline && del_contour)
+				hole_contour = 1;
+
+		}
+
+		/* If we deleted all the pieces of the polyarea, *pieces is NULL */
+	}
+	while ((a = anext), *pieces != NULL && !finished);
+}
+
+static void
+M_POLYAREA_Collect_separated(jmp_buf * e, PLINE * afst, POLYAREA ** contours, PLINE ** holes, int action, BOOLp maybe)
+{
+	PLINE **cur, **next;
+
+	for (cur = &afst; *cur != NULL; cur = next) {
+		next = &((*cur)->next);
+		/* if we disappear a contour, don't advance twice */
+		if (cntr_Collect(e, cur, contours, holes, action, NULL, NULL, NULL))
+			next = cur;
+	}
+}
+
+static void M_POLYAREA_Collect(jmp_buf * e, POLYAREA * afst, POLYAREA ** contours, PLINE ** holes, int action, BOOLp maybe)
+{
+	POLYAREA *a = afst;
+	POLYAREA *parent = NULL;			/* Quiet compiler warning */
+	PLINE **cur, **next, *parent_contour;
+
+	assert(a != NULL);
+	while ((a = a->f) != afst);
+	/* now the non-intersect parts are collected in temp/holes */
+	do {
+		if (maybe && a->contours->Flags.status != ISECTED)
+			parent_contour = a->contours;
+		else
+			parent_contour = NULL;
+
+		/* Take care of the first contour - so we know if we
+		 * can shortcut reparenting some of its children
+		 */
+		cur = &a->contours;
+		if (*cur != NULL) {
+			next = &((*cur)->next);
+			/* if we disappear a contour, don't advance twice */
+			if (cntr_Collect(e, cur, contours, holes, action, a, NULL, NULL)) {
+				parent = (*contours)->b;	/* InsCntr inserts behind the head */
+				next = cur;
+			}
+			else
+				parent = a;
+			cur = next;
+		}
+		for (; *cur != NULL; cur = next) {
+			next = &((*cur)->next);
+			/* if we disappear a contour, don't advance twice */
+			if (cntr_Collect(e, cur, contours, holes, action, a, parent, parent_contour))
+				next = cur;
+		}
+	}
+	while ((a = a->f) != afst);
+}
+
+/* determine if two polygons touch or overlap */
+BOOLp Touching(POLYAREA * a, POLYAREA * b)
+{
+	jmp_buf e;
+	int code;
+
+	if ((code = setjmp(e)) == 0) {
+#ifdef DEBUG
+		if (!poly_Valid(a))
+			return -1;
+		if (!poly_Valid(b))
+			return -1;
+#endif
+		M_POLYAREA_intersect(&e, a, b, pcb_false);
+
+		if (M_POLYAREA_label(a, b, TRUE))
+			return TRUE;
+		if (M_POLYAREA_label(b, a, TRUE))
+			return TRUE;
+	}
+	else if (code == TOUCHES)
+		return TRUE;
+	return FALSE;
+}
+
+/* the main clipping routines */
+int poly_Boolean(const POLYAREA * a_org, const POLYAREA * b_org, POLYAREA ** res, int action)
+{
+	POLYAREA *a = NULL, *b = NULL;
+
+	if (!poly_M_Copy0(&a, a_org) || !poly_M_Copy0(&b, b_org))
+		return err_no_memory;
+
+	return poly_Boolean_free(a, b, res, action);
+}																/* poly_Boolean */
+
+/* just like poly_Boolean but frees the input polys */
+int poly_Boolean_free(POLYAREA * ai, POLYAREA * bi, POLYAREA ** res, int action)
+{
+	POLYAREA *a = ai, *b = bi;
+	PLINE *a_isected = NULL;
+	PLINE *p, *holes = NULL;
+	jmp_buf e;
+	int code;
+
+	*res = NULL;
+
+	if (!a) {
+		switch (action) {
+		case PBO_XOR:
+		case PBO_UNITE:
+			*res = bi;
+			return err_ok;
+		case PBO_SUB:
+		case PBO_ISECT:
+			if (b != NULL)
+				poly_Free(&b);
+			return err_ok;
+		}
+	}
+	if (!b) {
+		switch (action) {
+		case PBO_SUB:
+		case PBO_XOR:
+		case PBO_UNITE:
+			*res = ai;
+			return err_ok;
+		case PBO_ISECT:
+			if (a != NULL)
+				poly_Free(&a);
+			return err_ok;
+		}
+	}
+
+	if ((code = setjmp(e)) == 0) {
+#ifdef DEBUG
+		assert(poly_Valid(a));
+		assert(poly_Valid(b));
+#endif
+
+		/* intersect needs to make a list of the contours in a and b which are intersected */
+		M_POLYAREA_intersect(&e, a, b, TRUE);
+
+		/* We could speed things up a lot here if we only processed the relevant contours */
+		/* NB: Relevant parts of a are labeled below */
+		M_POLYAREA_label(b, a, FALSE);
+
+		*res = a;
+		M_POLYAREA_update_primary(&e, res, &holes, action, b);
+		M_POLYAREA_separate_isected(&e, res, &holes, &a_isected);
+		M_POLYAREA_label_separated(a_isected, b, FALSE);
+		M_POLYAREA_Collect_separated(&e, a_isected, res, &holes, action, FALSE);
+		M_B_AREA_Collect(&e, b, res, &holes, action);
+		poly_Free(&b);
+
+		/* free a_isected */
+		while ((p = a_isected) != NULL) {
+			a_isected = p->next;
+			poly_DelContour(&p);
+		}
+
+		InsertHoles(&e, *res, &holes);
+	}
+	/* delete holes if any left */
+	while ((p = holes) != NULL) {
+		holes = p->next;
+		poly_DelContour(&p);
+	}
+
+	if (code) {
+		poly_Free(res);
+		return code;
+	}
+	assert(!*res || poly_Valid(*res));
+	return code;
+}																/* poly_Boolean_free */
+
+static void clear_marks(POLYAREA * p)
+{
+	POLYAREA *n = p;
+	PLINE *c;
+	VNODE *v;
+
+	do {
+		for (c = n->contours; c; c = c->next) {
+			v = &c->head;
+			do {
+				v->Flags.mark = 0;
+			}
+			while ((v = v->next) != &c->head);
+		}
+	}
+	while ((n = n->f) != p);
+}
+
+/* compute the intersection and subtraction (divides "a" into two pieces)
+ * and frees the input polys. This assumes that bi is a single simple polygon.
+ */
+int poly_AndSubtract_free(POLYAREA * ai, POLYAREA * bi, POLYAREA ** aandb, POLYAREA ** aminusb)
+{
+	POLYAREA *a = ai, *b = bi;
+	PLINE *p, *holes = NULL;
+	jmp_buf e;
+	int code;
+
+	*aandb = NULL;
+	*aminusb = NULL;
+
+	if ((code = setjmp(e)) == 0) {
+
+#ifdef DEBUG
+		if (!poly_Valid(a))
+			return -1;
+		if (!poly_Valid(b))
+			return -1;
+#endif
+		M_POLYAREA_intersect(&e, a, b, TRUE);
+
+		M_POLYAREA_label(a, b, FALSE);
+		M_POLYAREA_label(b, a, FALSE);
+
+		M_POLYAREA_Collect(&e, a, aandb, &holes, PBO_ISECT, FALSE);
+		InsertHoles(&e, *aandb, &holes);
+		assert(poly_Valid(*aandb));
+		/* delete holes if any left */
+		while ((p = holes) != NULL) {
+			holes = p->next;
+			poly_DelContour(&p);
+		}
+		holes = NULL;
+		clear_marks(a);
+		clear_marks(b);
+		M_POLYAREA_Collect(&e, a, aminusb, &holes, PBO_SUB, FALSE);
+		InsertHoles(&e, *aminusb, &holes);
+		poly_Free(&a);
+		poly_Free(&b);
+		assert(poly_Valid(*aminusb));
+	}
+	/* delete holes if any left */
+	while ((p = holes) != NULL) {
+		holes = p->next;
+		poly_DelContour(&p);
+	}
+
+
+	if (code) {
+		poly_Free(aandb);
+		poly_Free(aminusb);
+		return code;
+	}
+	assert(!*aandb || poly_Valid(*aandb));
+	assert(!*aminusb || poly_Valid(*aminusb));
+	return code;
+}																/* poly_AndSubtract_free */
+
+static inline int cntrbox_pointin(PLINE * c, Vector p)
+{
+	return (p[0] >= c->xmin && p[1] >= c->ymin && p[0] <= c->xmax && p[1] <= c->ymax);
+
+}
+
+static inline int node_neighbours(VNODE * a, VNODE * b)
+{
+	return (a == b) || (a->next == b) || (b->next == a) || (a->next == b->next);
+}
+
+VNODE *poly_CreateNode(Vector v)
+{
+	VNODE *res;
+	Coord *c;
+
+	assert(v);
+	res = (VNODE *) calloc(1, sizeof(VNODE));
+	if (res == NULL)
+		return NULL;
+	/* bzero (res, sizeof (VNODE) - sizeof(Vector)); */
+	c = res->point;
+	*c++ = *v++;
+	*c = *v;
+	return res;
+}
+
+void poly_IniContour(PLINE * c)
+{
+	if (c == NULL)
+		return;
+	/* bzero (c, sizeof(PLINE)); */
+	c->head.next = c->head.prev = &c->head;
+	c->xmin = c->ymin = 0x7fffffff;
+	c->xmax = c->ymax = 0x80000000;
+	c->is_round = FALSE;
+	c->cx = 0;
+	c->cy = 0;
+	c->radius = 0;
+}
+
+PLINE *poly_NewContour(Vector v)
+{
+	PLINE *res;
+
+	res = (PLINE *) calloc(1, sizeof(PLINE));
+	if (res == NULL)
+		return NULL;
+
+	poly_IniContour(res);
+
+	if (v != NULL) {
+		Vcopy(res->head.point, v);
+		cntrbox_adjust(res, v);
+	}
+
+	return res;
+}
+
+void poly_ClrContour(PLINE * c)
+{
+	VNODE *cur;
+
+	assert(c != NULL);
+	while ((cur = c->head.next) != &c->head) {
+		poly_ExclVertex(cur);
+		free(cur);
+	}
+	poly_IniContour(c);
+}
+
+void poly_DelContour(PLINE ** c)
+{
+	VNODE *cur, *prev;
+
+	if (*c == NULL)
+		return;
+	for (cur = (*c)->head.prev; cur != &(*c)->head; cur = prev) {
+		prev = cur->prev;
+		if (cur->cvc_next != NULL) {
+			free(cur->cvc_next);
+			free(cur->cvc_prev);
+		}
+		free(cur);
+	}
+	if ((*c)->head.cvc_next != NULL) {
+		free((*c)->head.cvc_next);
+		free((*c)->head.cvc_prev);
+	}
+	/* FIXME -- strict aliasing violation.  */
+	if ((*c)->tree) {
+		rtree_t *r = (*c)->tree;
+		r_destroy_tree(&r);
+	}
+	free(*c), *c = NULL;
+}
+
+void poly_PreContour(PLINE * C, BOOLp optimize)
+{
+	double area = 0;
+	VNODE *p, *c;
+	Vector p1, p2;
+
+	assert(C != NULL);
+
+	if (optimize) {
+		for (c = (p = &C->head)->next; c != &C->head; c = (p = c)->next) {
+			/* if the previous node is on the same line with this one, we should remove it */
+			Vsub2(p1, c->point, p->point);
+			Vsub2(p2, c->next->point, c->point);
+			/* If the product below is zero then
+			 * the points on either side of c 
+			 * are on the same line!
+			 * So, remove the point c
+			 */
+
+			if (vect_det2(p1, p2) == 0) {
+				poly_ExclVertex(c);
+				free(c);
+				c = p;
+			}
+		}
+	}
+	C->Count = 0;
+	C->xmin = C->xmax = C->head.point[0];
+	C->ymin = C->ymax = C->head.point[1];
+
+	p = (c = &C->head)->prev;
+	if (c != p) {
+		do {
+			/* calculate area for orientation */
+			area += (double) (p->point[0] - c->point[0]) * (p->point[1] + c->point[1]);
+			cntrbox_adjust(C, c->point);
+			C->Count++;
+		}
+		while ((c = (p = c)->next) != &C->head);
+	}
+	C->area = PCB_ABS(area);
+	if (C->Count > 2)
+		C->Flags.orient = ((area < 0) ? PLF_INV : PLF_DIR);
+	C->tree = (rtree_t *) make_edge_tree(C);
+}																/* poly_PreContour */
+
+static r_dir_t flip_cb(const BoxType * b, void *cl)
+{
+	struct seg *s = (struct seg *) b;
+	s->v = s->v->prev;
+	return R_DIR_FOUND_CONTINUE;
+}
+
+void poly_InvContour(PLINE * c)
+{
+	VNODE *cur, *next;
+	int r;
+
+	assert(c != NULL);
+	cur = &c->head;
+	do {
+		next = cur->next;
+		cur->next = cur->prev;
+		cur->prev = next;
+		/* fix the segment tree */
+	}
+	while ((cur = next) != &c->head);
+	c->Flags.orient ^= 1;
+	if (c->tree) {
+		r_search(c->tree, NULL, NULL, flip_cb, NULL, &r);
+		assert(r == c->Count);
+	}
+}
+
+void poly_ExclVertex(VNODE * node)
+{
+	assert(node != NULL);
+	if (node->cvc_next) {
+		free(node->cvc_next);
+		free(node->cvc_prev);
+	}
+	node->prev->next = node->next;
+	node->next->prev = node->prev;
+}
+
+void poly_InclVertex(VNODE * after, VNODE * node)
+{
+	double a, b;
+	assert(after != NULL);
+	assert(node != NULL);
+
+	node->prev = after;
+	node->next = after->next;
+	after->next = after->next->prev = node;
+	/* remove points on same line */
+	if (node->prev->prev == node)
+		return;											/* we don't have 3 points in the poly yet */
+	a = (node->point[1] - node->prev->prev->point[1]);
+	a *= (node->prev->point[0] - node->prev->prev->point[0]);
+	b = (node->point[0] - node->prev->prev->point[0]);
+	b *= (node->prev->point[1] - node->prev->prev->point[1]);
+	if (fabs(a - b) < EPSILON) {
+		VNODE *t = node->prev;
+		t->prev->next = node;
+		node->prev = t->prev;
+		free(t);
+	}
+}
+
+BOOLp poly_CopyContour(PLINE ** dst, PLINE * src)
+{
+	VNODE *cur, *newnode;
+
+	assert(src != NULL);
+	*dst = NULL;
+	*dst = poly_NewContour(src->head.point);
+	if (*dst == NULL)
+		return FALSE;
+
+	(*dst)->Count = src->Count;
+	(*dst)->Flags.orient = src->Flags.orient;
+	(*dst)->xmin = src->xmin, (*dst)->xmax = src->xmax;
+	(*dst)->ymin = src->ymin, (*dst)->ymax = src->ymax;
+	(*dst)->area = src->area;
+
+	for (cur = src->head.next; cur != &src->head; cur = cur->next) {
+		if ((newnode = poly_CreateNode(cur->point)) == NULL)
+			return FALSE;
+		/* newnode->Flags = cur->Flags; */
+		poly_InclVertex((*dst)->head.prev, newnode);
+	}
+	(*dst)->tree = (rtree_t *) make_edge_tree(*dst);
+	return TRUE;
+}
+
+/**********************************************************************/
+/* polygon routines */
+
+BOOLp poly_Copy0(POLYAREA ** dst, const POLYAREA * src)
+{
+	*dst = NULL;
+	if (src != NULL)
+		*dst = (POLYAREA *) calloc(1, sizeof(POLYAREA));
+	if (*dst == NULL)
+		return FALSE;
+	(*dst)->contour_tree = r_create_tree(NULL, 0, 0);
+
+	return poly_Copy1(*dst, src);
+}
+
+BOOLp poly_Copy1(POLYAREA * dst, const POLYAREA * src)
+{
+	PLINE *cur, **last = &dst->contours;
+
+	*last = NULL;
+	dst->f = dst->b = dst;
+
+	for (cur = src->contours; cur != NULL; cur = cur->next) {
+		if (!poly_CopyContour(last, cur))
+			return FALSE;
+		r_insert_entry(dst->contour_tree, (BoxTypePtr) * last, 0);
+		last = &(*last)->next;
+	}
+	return TRUE;
+}
+
+void poly_M_Incl(POLYAREA ** list, POLYAREA * a)
+{
+	if (*list == NULL)
+		a->f = a->b = a, *list = a;
+	else {
+		a->f = *list;
+		a->b = (*list)->b;
+		(*list)->b = (*list)->b->f = a;
+	}
+}
+
+BOOLp poly_M_Copy0(POLYAREA ** dst, const POLYAREA * srcfst)
+{
+	const POLYAREA *src = srcfst;
+	POLYAREA *di;
+
+	*dst = NULL;
+	if (src == NULL)
+		return FALSE;
+	do {
+		if ((di = poly_Create()) == NULL || !poly_Copy1(di, src))
+			return FALSE;
+		poly_M_Incl(dst, di);
+	}
+	while ((src = src->f) != srcfst);
+	return TRUE;
+}
+
+BOOLp poly_InclContour(POLYAREA * p, PLINE * c)
+{
+	PLINE *tmp;
+
+	if ((c == NULL) || (p == NULL))
+		return FALSE;
+	if (c->Flags.orient == PLF_DIR) {
+		if (p->contours != NULL)
+			return FALSE;
+		p->contours = c;
+	}
+	else {
+		if (p->contours == NULL)
+			return FALSE;
+		/* link at front of hole list */
+		tmp = p->contours->next;
+		p->contours->next = c;
+		c->next = tmp;
+	}
+	r_insert_entry(p->contour_tree, (BoxTypePtr) c, 0);
+	return TRUE;
+}
+
+typedef struct pip {
+	int f;
+	Vector p;
+	jmp_buf env;
+} pip;
+
+
+static r_dir_t crossing(const BoxType * b, void *cl)
+{
+	struct seg *s = (struct seg *) b;
+	struct pip *p = (struct pip *) cl;
+
+	if (s->v->point[1] <= p->p[1]) {
+		if (s->v->next->point[1] > p->p[1]) {
+			Vector v1, v2;
+			pcb_long64_t cross;
+			Vsub2(v1, s->v->next->point, s->v->point);
+			Vsub2(v2, p->p, s->v->point);
+			cross = (pcb_long64_t) v1[0] * v2[1] - (pcb_long64_t) v2[0] * v1[1];
+			if (cross == 0) {
+				p->f = 1;
+				longjmp(p->env, 1);
+			}
+			if (cross > 0)
+				p->f += 1;
+		}
+	}
+	else {
+		if (s->v->next->point[1] <= p->p[1]) {
+			Vector v1, v2;
+			pcb_long64_t cross;
+			Vsub2(v1, s->v->next->point, s->v->point);
+			Vsub2(v2, p->p, s->v->point);
+			cross = (pcb_long64_t) v1[0] * v2[1] - (pcb_long64_t) v2[0] * v1[1];
+			if (cross == 0) {
+				p->f = 1;
+				longjmp(p->env, 1);
+			}
+			if (cross < 0)
+				p->f -= 1;
+		}
+	}
+	return R_DIR_FOUND_CONTINUE;
+}
+
+int poly_InsideContour(PLINE * c, Vector p)
+{
+	struct pip info;
+	BoxType ray;
+
+	if (!cntrbox_pointin(c, p))
+		return FALSE;
+	info.f = 0;
+	info.p[0] = ray.X1 = p[0];
+	info.p[1] = ray.Y1 = p[1];
+	ray.X2 = 0x7fffffff;
+	ray.Y2 = p[1] + 1;
+	if (setjmp(info.env) == 0)
+		r_search(c->tree, &ray, NULL, crossing, &info, NULL);
+	return info.f;
+}
+
+BOOLp poly_CheckInside(POLYAREA * p, Vector v0)
+{
+	PLINE *cur;
+
+	if ((p == NULL) || (v0 == NULL) || (p->contours == NULL))
+		return FALSE;
+	cur = p->contours;
+	if (poly_InsideContour(cur, v0)) {
+		for (cur = cur->next; cur != NULL; cur = cur->next)
+			if (poly_InsideContour(cur, v0))
+				return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOLp poly_M_CheckInside(POLYAREA * p, Vector v0)
+{
+	POLYAREA *cur;
+
+	if ((p == NULL) || (v0 == NULL))
+		return FALSE;
+	cur = p;
+	do {
+		if (poly_CheckInside(cur, v0))
+			return TRUE;
+	}
+	while ((cur = cur->f) != p);
+	return FALSE;
+}
+
+static double dot(Vector A, Vector B)
+{
+	return (double) A[0] * (double) B[0] + (double) A[1] * (double) B[1];
+}
+
+/* Compute whether point is inside a triangle formed by 3 other pints */
+/* Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html */
+static int point_in_triangle(Vector A, Vector B, Vector C, Vector P)
+{
+	Vector v0, v1, v2;
+	double dot00, dot01, dot02, dot11, dot12;
+	double invDenom;
+	double u, v;
+
+	/* Compute vectors */
+	v0[0] = C[0] - A[0];
+	v0[1] = C[1] - A[1];
+	v1[0] = B[0] - A[0];
+	v1[1] = B[1] - A[1];
+	v2[0] = P[0] - A[0];
+	v2[1] = P[1] - A[1];
+
+	/* Compute dot products */
+	dot00 = dot(v0, v0);
+	dot01 = dot(v0, v1);
+	dot02 = dot(v0, v2);
+	dot11 = dot(v1, v1);
+	dot12 = dot(v1, v2);
+
+	/* Compute barycentric coordinates */
+	invDenom = 1. / (dot00 * dot11 - dot01 * dot01);
+	u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+	v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+	/* Check if point is in triangle */
+	return (u > 0.0) && (v > 0.0) && (u + v < 1.0);
+}
+
+
+/* Returns the dot product of Vector A->B, and a vector
+ * orthogonal to Vector C->D. The result is not normalised, so will be
+ * weighted by the magnitude of the C->D vector.
+ */
+static double dot_orthogonal_to_direction(Vector A, Vector B, Vector C, Vector D)
+{
+	Vector l1, l2, l3;
+	l1[0] = B[0] - A[0];
+	l1[1] = B[1] - A[1];
+	l2[0] = D[0] - C[0];
+	l2[1] = D[1] - C[1];
+
+	l3[0] = -l2[1];
+	l3[1] = l2[0];
+
+	return dot(l1, l3);
+}
+
+/* Algorithm from http://www.exaflop.org/docs/cgafaq/cga2.html
+ *
+ * "Given a simple polygon, find some point inside it. Here is a method based
+ * on the proof that there exists an internal diagonal, in [O'Rourke, 13-14].
+ * The idea is that the midpoint of a diagonal is interior to the polygon.
+ *
+ * 1.  Identify a convex vertex v; let its adjacent vertices be a and b.
+ * 2.  For each other vertex q do:
+ * 2a. If q is inside avb, compute distance to v (orthogonal to ab).
+ * 2b. Save point q if distance is a new min.
+ * 3.  If no point is inside, return midpoint of ab, or centroid of avb.
+ * 4.  Else if some point inside, qv is internal: return its midpoint."
+ *
+ * [O'Rourke]: Computational Geometry in C (2nd Ed.)
+ *             Joseph O'Rourke, Cambridge University Press 1998,
+ *             ISBN 0-521-64010-5 Pbk, ISBN 0-521-64976-5 Hbk
+ */
+static void poly_ComputeInteriorPoint(PLINE * poly, Vector v)
+{
+	VNODE *pt1, *pt2, *pt3, *q;
+	VNODE *min_q = NULL;
+	double dist;
+	double min_dist = 0.0;
+	double dir = (poly->Flags.orient == PLF_DIR) ? 1. : -1;
+
+	/* Find a convex node on the polygon */
+	pt1 = &poly->head;
+	do {
+		double dot_product;
+
+		pt2 = pt1->next;
+		pt3 = pt2->next;
+
+		dot_product = dot_orthogonal_to_direction(pt1->point, pt2->point, pt3->point, pt2->point);
+
+		if (dot_product * dir > 0.)
+			break;
+	}
+	while ((pt1 = pt1->next) != &poly->head);
+
+	/* Loop over remaining vertices */
+	q = pt3;
+	do {
+		/* Is current vertex "q" outside pt1 pt2 pt3 triangle? */
+		if (!point_in_triangle(pt1->point, pt2->point, pt3->point, q->point))
+			continue;
+
+		/* NO: compute distance to pt2 (v) orthogonal to pt1 - pt3) */
+		/*     Record minimum */
+		dist = dot_orthogonal_to_direction(q->point, pt2->point, pt1->point, pt3->point);
+		if (min_q == NULL || dist < min_dist) {
+			min_dist = dist;
+			min_q = q;
+		}
+	}
+	while ((q = q->next) != pt2);
+
+	/* Were any "q" found inside pt1 pt2 pt3? */
+	if (min_q == NULL) {
+		/* NOT FOUND: Return midpoint of pt1 pt3 */
+		v[0] = (pt1->point[0] + pt3->point[0]) / 2;
+		v[1] = (pt1->point[1] + pt3->point[1]) / 2;
+	}
+	else {
+		/* FOUND: Return mid point of min_q, pt2 */
+		v[0] = (pt2->point[0] + min_q->point[0]) / 2;
+		v[1] = (pt2->point[1] + min_q->point[1]) / 2;
+	}
+}
+
+
+/* NB: This function assumes the caller _knows_ the contours do not
+ *     intersect. If the contours intersect, the result is undefined.
+ *     It will return the correct result if the two contours share
+ *     common points between their contours. (Identical contours
+ *     are treated as being inside each other).
+ */
+int poly_ContourInContour(PLINE * poly, PLINE * inner)
+{
+	Vector point;
+	assert(poly != NULL);
+	assert(inner != NULL);
+	if (cntrbox_inside(inner, poly)) {
+		/* We need to prove the "inner" contour is not outside
+		 * "poly" contour. If it is outside, we can return.
+		 */
+		if (!poly_InsideContour(poly, inner->head.point))
+			return 0;
+
+		poly_ComputeInteriorPoint(inner, point);
+		return poly_InsideContour(poly, point);
+	}
+	return 0;
+}
+
+void poly_Init(POLYAREA * p)
+{
+	p->f = p->b = p;
+	p->contours = NULL;
+	p->contour_tree = r_create_tree(NULL, 0, 0);
+}
+
+POLYAREA *poly_Create(void)
+{
+	POLYAREA *res;
+
+	if ((res = (POLYAREA *) malloc(sizeof(POLYAREA))) != NULL)
+		poly_Init(res);
+	return res;
+}
+
+void poly_FreeContours(PLINE ** pline)
+{
+	PLINE *pl;
+
+	while ((pl = *pline) != NULL) {
+		*pline = pl->next;
+		poly_DelContour(&pl);
+	}
+}
+
+void poly_Free(POLYAREA ** p)
+{
+	POLYAREA *cur;
+
+	if (*p == NULL)
+		return;
+	for (cur = (*p)->f; cur != *p; cur = (*p)->f) {
+		poly_FreeContours(&cur->contours);
+		r_destroy_tree(&cur->contour_tree);
+		cur->f->b = cur->b;
+		cur->b->f = cur->f;
+		free(cur);
+	}
+	poly_FreeContours(&cur->contours);
+	r_destroy_tree(&cur->contour_tree);
+	free(*p), *p = NULL;
+}
+
+static BOOLp inside_sector(VNODE * pn, Vector p2)
+{
+	Vector cdir, ndir, pdir;
+	int p_c, n_c, p_n;
+
+	assert(pn != NULL);
+	vect_sub(cdir, p2, pn->point);
+	vect_sub(pdir, pn->point, pn->prev->point);
+	vect_sub(ndir, pn->next->point, pn->point);
+
+	p_c = vect_det2(pdir, cdir) >= 0;
+	n_c = vect_det2(ndir, cdir) >= 0;
+	p_n = vect_det2(pdir, ndir) >= 0;
+
+	if ((p_n && p_c && n_c) || ((!p_n) && (p_c || n_c)))
+		return TRUE;
+	else
+		return FALSE;
+}																/* inside_sector */
+
+/* returns TRUE if bad contour */
+BOOLp poly_ChkContour(PLINE * a)
+{
+	VNODE *a1, *a2, *hit1, *hit2;
+	Vector i1, i2;
+	int icnt;
+
+	assert(a != NULL);
+	a1 = &a->head;
+	do {
+		a2 = a1;
+		do {
+			if (!node_neighbours(a1, a2) && (icnt = vect_inters2(a1->point, a1->next->point, a2->point, a2->next->point, i1, i2)) > 0) {
+				if (icnt > 1)
+					return TRUE;
+
+				if (vect_dist2(i1, a1->point) < EPSILON)
+					hit1 = a1;
+				else if (vect_dist2(i1, a1->next->point) < EPSILON)
+					hit1 = a1->next;
+				else
+					hit1 = NULL;
+
+				if (vect_dist2(i1, a2->point) < EPSILON)
+					hit2 = a2;
+				else if (vect_dist2(i1, a2->next->point) < EPSILON)
+					hit2 = a2->next;
+				else
+					hit2 = NULL;
+
+				if ((hit1 == NULL) && (hit2 == NULL)) {
+					/* If the intersection didn't land on an end-point of either
+					 * line, we know the lines cross and we return TRUE.
+					 */
+					return TRUE;
+				}
+				else if (hit1 == NULL) {
+					/* An end-point of the second line touched somewhere along the
+					   length of the first line. Check where the second line leads. */
+					if (inside_sector(hit2, a1->point) != inside_sector(hit2, a1->next->point))
+						return TRUE;
+				}
+				else if (hit2 == NULL) {
+					/* An end-point of the first line touched somewhere along the
+					   length of the second line. Check where the first line leads. */
+					if (inside_sector(hit1, a2->point) != inside_sector(hit1, a2->next->point))
+						return TRUE;
+				}
+				else {
+					/* Both lines intersect at an end-point. Check where they lead. */
+					if (inside_sector(hit1, hit2->prev->point) != inside_sector(hit1, hit2->next->point))
+						return TRUE;
+				}
+			}
+		}
+		while ((a2 = a2->next) != &a->head);
+	}
+	while ((a1 = a1->next) != &a->head);
+	return FALSE;
+}
+
+void poly_bbox(POLYAREA * p, BoxType * b)
+{
+	PLINE *n;
+	/*int cnt;*/
+
+	n = p->contours;
+	b->X1 = b->X2 = n->xmin;
+	b->Y1 = b->Y2 = n->ymin;
+
+	for (/*cnt = 0*/; /*cnt < 2 */ n != NULL; n = n->next) {
+		if (n->xmin < b->X1)
+			b->X1 = n->xmin;
+		if (n->ymin < b->Y1)
+			b->Y1 = n->ymin;
+		if (n->xmax > b->X2)
+			b->X2 = n->xmax;
+		if (n->ymax > b->Y2)
+			b->Y2 = n->ymax;
+/*		if (n == p->contours)
+			cnt++;*/
+	}
+}
+
+
+BOOLp poly_Valid(POLYAREA * p)
+{
+	PLINE *c;
+
+	if ((p == NULL) || (p->contours == NULL))
+		return FALSE;
+
+	if (p->contours->Flags.orient == PLF_INV || poly_ChkContour(p->contours)) {
+#ifndef NDEBUG
+		VNODE *v, *n;
+		DEBUGP("Invalid Outer PLINE\n");
+		if (p->contours->Flags.orient == PLF_INV)
+			DEBUGP("failed orient\n");
+		if (poly_ChkContour(p->contours))
+			DEBUGP("failed self-intersection\n");
+		v = &p->contours->head;
+		do {
+			n = v->next;
+			pcb_fprintf(stderr, "Line [%#mS %#mS %#mS %#mS 100 100 \"\"]\n", v->point[0], v->point[1], n->point[0], n->point[1]);
+		}
+		while ((v = v->next) != &p->contours->head);
+#endif
+		return FALSE;
+	}
+	for (c = p->contours->next; c != NULL; c = c->next) {
+		if (c->Flags.orient == PLF_DIR || poly_ChkContour(c) || !poly_ContourInContour(p->contours, c)) {
+#ifndef NDEBUG
+			VNODE *v, *n;
+			DEBUGP("Invalid Inner PLINE orient = %d\n", c->Flags.orient);
+			if (c->Flags.orient == PLF_DIR)
+				DEBUGP("failed orient\n");
+			if (poly_ChkContour(c))
+				DEBUGP("failed self-intersection\n");
+			if (!poly_ContourInContour(p->contours, c))
+				DEBUGP("failed containment\n");
+			v = &c->head;
+			do {
+				n = v->next;
+				pcb_fprintf(stderr, "Line [%#mS %#mS %#mS %#mS 100 100 \"\"]\n", v->point[0], v->point[1], n->point[0], n->point[1]);
+			}
+			while ((v = v->next) != &c->head);
+#endif
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+
+Vector vect_zero = { (long) 0, (long) 0 };
+
+/*********************************************************************/
+/*             L o n g   V e c t o r   S t u f f                     */
+/*********************************************************************/
+
+void vect_init(Vector v, double x, double y)
+{
+	v[0] = (long) x;
+	v[1] = (long) y;
+}																/* vect_init */
+
+#define Vzero(a)   ((a)[0] == 0. && (a)[1] == 0.)
+
+#define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
+
+int vect_equal(Vector v1, Vector v2)
+{
+	return (v1[0] == v2[0] && v1[1] == v2[1]);
+}																/* vect_equal */
+
+
+void vect_sub(Vector res, Vector v1, Vector v2)
+{
+Vsub(res, v1, v2)}							/* vect_sub */
+
+void vect_min(Vector v1, Vector v2, Vector v3)
+{
+	v1[0] = (v2[0] < v3[0]) ? v2[0] : v3[0];
+	v1[1] = (v2[1] < v3[1]) ? v2[1] : v3[1];
+}																/* vect_min */
+
+void vect_max(Vector v1, Vector v2, Vector v3)
+{
+	v1[0] = (v2[0] > v3[0]) ? v2[0] : v3[0];
+	v1[1] = (v2[1] > v3[1]) ? v2[1] : v3[1];
+}																/* vect_max */
+
+double vect_len2(Vector v)
+{
+	return ((double) v[0] * v[0] + (double) v[1] * v[1]);	/* why sqrt? only used for compares */
+}
+
+double vect_dist2(Vector v1, Vector v2)
+{
+	double dx = v1[0] - v2[0];
+	double dy = v1[1] - v2[1];
+
+	return (dx * dx + dy * dy);		/* why sqrt */
+}
+
+/* value has sign of angle between vectors */
+double vect_det2(Vector v1, Vector v2)
+{
+	return (((double) v1[0] * v2[1]) - ((double) v2[0] * v1[1]));
+}
+
+static double vect_m_dist(Vector v1, Vector v2)
+{
+	double dx = v1[0] - v2[0];
+	double dy = v1[1] - v2[1];
+	double dd = (dx * dx + dy * dy);	/* sqrt */
+
+	if (dx > 0)
+		return +dd;
+	if (dx < 0)
+		return -dd;
+	if (dy > 0)
+		return +dd;
+	return -dd;
+}																/* vect_m_dist */
+
+/*
+vect_inters2
+ (C) 1993 Klamer Schutte
+ (C) 1997 Michael Leonov, Alexey Nikitin
+*/
+
+int vect_inters2(Vector p1, Vector p2, Vector q1, Vector q2, Vector S1, Vector S2)
+{
+	double s, t, deel;
+	double rpx, rpy, rqx, rqy;
+
+	if (max(p1[0], p2[0]) < min(q1[0], q2[0]) ||
+			max(q1[0], q2[0]) < min(p1[0], p2[0]) || max(p1[1], p2[1]) < min(q1[1], q2[1]) || max(q1[1], q2[1]) < min(p1[1], p2[1]))
+		return 0;
+
+	rpx = p2[0] - p1[0];
+	rpy = p2[1] - p1[1];
+	rqx = q2[0] - q1[0];
+	rqy = q2[1] - q1[1];
+
+	deel = rpy * rqx - rpx * rqy;	/* -vect_det(rp,rq); */
+
+	/* coordinates are 30-bit integers so deel will be exactly zero
+	 * if the lines are parallel
+	 */
+
+	if (deel == 0) {							/* parallel */
+		double dc1, dc2, d1, d2, h;	/* Check to see whether p1-p2 and q1-q2 are on the same line */
+		Vector hp1, hq1, hp2, hq2, q1p1, q1q2;
+
+		Vsub2(q1p1, q1, p1);
+		Vsub2(q1q2, q1, q2);
+
+
+		/* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
+		if (vect_det2(q1p1, q1q2) != 0)
+			return 0;
+		dc1 = 0;										/* m_len(p1 - p1) */
+
+		dc2 = vect_m_dist(p1, p2);
+		d1 = vect_m_dist(p1, q1);
+		d2 = vect_m_dist(p1, q2);
+
+/* Sorting the independent points from small to large */
+		Vcpy2(hp1, p1);
+		Vcpy2(hp2, p2);
+		Vcpy2(hq1, q1);
+		Vcpy2(hq2, q2);
+		if (dc1 > dc2) {						/* hv and h are used as help-variable. */
+			Vswp2(hp1, hp2);
+			h = dc1, dc1 = dc2, dc2 = h;
+		}
+		if (d1 > d2) {
+			Vswp2(hq1, hq2);
+			h = d1, d1 = d2, d2 = h;
+		}
+
+/* Now the line-pieces are compared */
+
+		if (dc1 < d1) {
+			if (dc2 < d1)
+				return 0;
+			if (dc2 < d2) {
+				Vcpy2(S1, hp2);
+				Vcpy2(S2, hq1);
+			}
+			else {
+				Vcpy2(S1, hq1);
+				Vcpy2(S2, hq2);
+			};
+		}
+		else {
+			if (dc1 > d2)
+				return 0;
+			if (dc2 < d2) {
+				Vcpy2(S1, hp1);
+				Vcpy2(S2, hp2);
+			}
+			else {
+				Vcpy2(S1, hp1);
+				Vcpy2(S2, hq2);
+			};
+		}
+		return (Vequ2(S1, S2) ? 1 : 2);
+	}
+	else {												/* not parallel */
+		/*
+		 * We have the lines:
+		 * l1: p1 + s(p2 - p1)
+		 * l2: q1 + t(q2 - q1)
+		 * And we want to know the intersection point.
+		 * Calculate t:
+		 * p1 + s(p2-p1) = q1 + t(q2-q1)
+		 * which is similar to the two equations:
+		 * p1x + s * rpx = q1x + t * rqx
+		 * p1y + s * rpy = q1y + t * rqy
+		 * Multiplying these by rpy resp. rpx gives:
+		 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
+		 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
+		 * Subtracting these gives:
+		 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
+		 * So t can be isolated:
+		 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
+		 * and s can be found similarly
+		 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
+		 */
+
+		if (Vequ2(q1, p1) || Vequ2(q1, p2)) {
+			S1[0] = q1[0];
+			S1[1] = q1[1];
+		}
+		else if (Vequ2(q2, p1) || Vequ2(q2, p2)) {
+			S1[0] = q2[0];
+			S1[1] = q2[1];
+		}
+		else {
+			s = (rqy * (p1[0] - q1[0]) + rqx * (q1[1] - p1[1])) / deel;
+			if (s < 0 || s > 1.)
+				return 0;
+			t = (rpy * (p1[0] - q1[0]) + rpx * (q1[1] - p1[1])) / deel;
+			if (t < 0 || t > 1.)
+				return 0;
+
+			S1[0] = q1[0] + ROUND(t * rqx);
+			S1[1] = q1[1] + ROUND(t * rqy);
+		}
+		return 1;
+	}
+}																/* vect_inters2 */
+
+/* how about expanding polygons so that edges can be arcs rather than
+ * lines. Consider using the third coordinate to store the radius of the
+ * arc. The arc would pass through the vertex points. Positive radius
+ * would indicate the arc bows left (center on right of P1-P2 path)
+ * negative radius would put the center on the other side. 0 radius
+ * would mean the edge is a line instead of arc
+ * The intersections of the two circles centered at the vertex points
+ * would determine the two possible arc centers. If P2.x > P1.x then
+ * the center with smaller Y is selected for positive r. If P2.y > P1.y
+ * then the center with greater X is selected for positive r.
+ *
+ * the vec_inters2() routine would then need to handle line-line
+ * line-arc and arc-arc intersections.
+ *
+ * perhaps reverse tracing the arc would require look-ahead to check
+ * for arcs
+ */
diff --git a/src/polygon_act.c b/src/polygon_act.c
new file mode 100644
index 0000000..0a5fa09
--- /dev/null
+++ b/src/polygon_act.c
@@ -0,0 +1,144 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "action_helper.h"
+#include "undo.h"
+#include "funchash_core.h"
+
+#include "polygon.h"
+#include "draw.h"
+#include "search.h"
+#include "crosshair.h"
+
+/* --------------------------------------------------------------------------- */
+
+static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
+
+static const char morphpolygon_help[] = "Converts dead polygon islands into separate polygons.";
+
+/* %start-doc actions MorphPolygon
+
+If a polygon is divided into unconnected "islands", you can use
+this command to convert the otherwise disappeared islands into
+separate polygons. Be sure the cursor is over a portion of the
+polygon that remains visible. Very small islands that may flake
+off are automatically deleted.
+
+%end-doc */
+
+static int ActionMorphPolygon(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_Object:
+			{
+				int type;
+				void *ptr1, *ptr2, *ptr3;
+
+				gui->get_coords(_("Select an Object"), &x, &y);
+				if ((type = SearchScreen(x, y, PCB_TYPE_POLYGON, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE) {
+					MorphPolygon((LayerType *) ptr1, (PolygonType *) ptr3);
+					Draw();
+					IncrementUndoSerialNumber();
+				}
+				break;
+			}
+		case F_Selected:
+		case F_SelectedObjects:
+			ALLPOLYGON_LOOP(PCB->Data);
+			{
+				if (TEST_FLAG(PCB_FLAG_SELECTED, polygon))
+					MorphPolygon(layer, polygon);
+			}
+			ENDALL_LOOP;
+			Draw();
+			IncrementUndoSerialNumber();
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
+
+static const char polygon_help[] = "Some polygon related stuff.";
+
+/* %start-doc actions Polygon
+
+Polygons need a special action routine to make life easier.
+
+ at table @code
+
+ at item Close
+Creates the final segment of the polygon.  This may fail if clipping
+to 45 degree lines is switched on, in which case a warning is issued.
+
+ at item PreviousPoint
+Resets the newly entered corner to the previous one. The Undo action
+will call Polygon(PreviousPoint) when appropriate to do so.
+
+ at end table
+
+%end-doc */
+
+static int ActionPolygon(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function && conf_core.editor.mode == PCB_MODE_POLYGON) {
+		notify_crosshair_change(pcb_false);
+		switch (funchash_get(function, NULL)) {
+			/* close open polygon if possible */
+		case F_Close:
+			ClosePolygon();
+			break;
+
+			/* go back to the previous point */
+		case F_PreviousPoint:
+			GoToPreviousPoint();
+			break;
+		}
+		notify_crosshair_change(pcb_true);
+	}
+	return 0;
+}
+
+
+HID_Action polygon_action_list[] = {
+	{"MorphPolygon", 0, ActionMorphPolygon,
+	 morphpolygon_help, morphpolygon_syntax}
+	,
+	{"Polygon", 0, ActionPolygon,
+	 polygon_help, polygon_syntax}
+};
+
+REGISTER_ACTIONS(polygon_action_list, NULL)
diff --git a/src/rats.c b/src/rats.c
new file mode 100644
index 0000000..efad7a1
--- /dev/null
+++ b/src/rats.c
@@ -0,0 +1,921 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* rats nest routines */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "misc.h"
+#include "layer.h"
+#include "polygon.h"
+#include "rats.h"
+#include "search.h"
+#include "undo.h"
+#include "stub_mincut.h"
+#include "route_style.h"
+#include "compat_misc.h"
+#include "netlist.h"
+
+#define TRIEDFIRST 0x1
+#define BESTFOUND 0x2
+
+/* ---------------------------------------------------------------------------
+ * some forward declarations
+ */
+static pcb_bool FindPad(const char *, const char *, ConnectionType *, pcb_bool);
+static pcb_bool ParseConnection(const char *, char *, char *);
+static pcb_bool DrawShortestRats(NetListTypePtr,
+														 void (*)(register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr));
+static pcb_bool GatherSubnets(NetListTypePtr, pcb_bool, pcb_bool);
+static pcb_bool CheckShorts(LibraryMenuTypePtr);
+static void TransferNet(NetListTypePtr, NetTypePtr, NetTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static pcb_bool badnet = pcb_false;
+static pcb_cardinal_t SLayer, CLayer;	/* layer group holding solder/component side */
+
+/* ---------------------------------------------------------------------------
+ * parse a connection description from a string
+ * puts the element name in the string and the pin number in
+ * the number.  If a valid connection is found, it returns the
+ * number of characters processed from the string, otherwise
+ * it returns 0
+ */
+static pcb_bool ParseConnection(const char *InString, char *ElementName, char *PinNum)
+{
+	int i, j;
+
+	/* copy element name portion */
+	for (j = 0; InString[j] != '\0' && InString[j] != '-'; j++)
+		ElementName[j] = InString[j];
+	if (InString[j] == '-') {
+		for (i = j; i > 0 && ElementName[i - 1] >= 'a'; i--);
+		ElementName[i] = '\0';
+		for (i = 0, j++; InString[j] != '\0'; i++, j++)
+			PinNum[i] = InString[j];
+		PinNum[i] = '\0';
+		return (pcb_false);
+	}
+	else {
+		ElementName[j] = '\0';
+		Message(PCB_MSG_DEFAULT, _("Bad net-list format encountered near: \"%s\"\n"), ElementName);
+		return (pcb_true);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * Find a particular pad from an element name and pin number
+ */
+static pcb_bool FindPad(const char *ElementName, const char *PinNum, ConnectionType * conn, pcb_bool Same)
+{
+	ElementTypePtr element;
+	gdl_iterator_t it;
+	PadType *pad;
+	PinType *pin;
+
+	if ((element = SearchElementByName(PCB->Data, ElementName)) == NULL)
+		return pcb_false;
+
+	padlist_foreach(&element->Pad, &it, pad) {
+		if (NSTRCMP(PinNum, pad->Number) == 0 && (!Same || !TEST_FLAG(PCB_FLAG_DRC, pad))) {
+			conn->type = PCB_TYPE_PAD;
+			conn->ptr1 = element;
+			conn->ptr2 = pad;
+			conn->group = TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? SLayer : CLayer;
+
+			if (TEST_FLAG(PCB_FLAG_EDGE2, pad)) {
+				conn->X = pad->Point2.X;
+				conn->Y = pad->Point2.Y;
+			}
+			else {
+				conn->X = pad->Point1.X;
+				conn->Y = pad->Point1.Y;
+			}
+			return pcb_true;
+		}
+	}
+
+	padlist_foreach(&element->Pin, &it, pin) {
+		if (!TEST_FLAG(PCB_FLAG_HOLE, pin) && pin->Number && NSTRCMP(PinNum, pin->Number) == 0 && (!Same || !TEST_FLAG(PCB_FLAG_DRC, pin))) {
+			conn->type = PCB_TYPE_PIN;
+			conn->ptr1 = element;
+			conn->ptr2 = pin;
+			conn->group = SLayer;			/* any layer will do */
+			conn->X = pin->X;
+			conn->Y = pin->Y;
+			return pcb_true;
+		}
+	}
+
+	return pcb_false;
+}
+
+/*--------------------------------------------------------------------------
+ * parse a netlist menu entry and locate the corresponding pad
+ * returns pcb_true if found, and fills in Connection information
+ */
+pcb_bool SeekPad(LibraryEntryType * entry, ConnectionType * conn, pcb_bool Same)
+{
+	int j;
+	char ElementName[256];
+	char PinNum[256];
+
+	if (ParseConnection(entry->ListEntry, ElementName, PinNum))
+		return (pcb_false);
+	for (j = 0; PinNum[j] != '\0'; j++);
+	if (j == 0) {
+		Message(PCB_MSG_DEFAULT, _("Error! Netlist file is missing pin!\n" "white space after \"%s-\"\n"), ElementName);
+		badnet = pcb_true;
+	}
+	else {
+		if (FindPad(ElementName, PinNum, conn, Same))
+			return (pcb_true);
+		if (Same)
+			return (pcb_false);
+		if (PinNum[j - 1] < '0' || PinNum[j - 1] > '9') {
+			Message(PCB_MSG_DEFAULT, "WARNING! Pin number ending with '%c'"
+							" encountered in netlist file\n" "Probably a bad netlist file format\n", PinNum[j - 1]);
+		}
+	}
+	Message(PCB_MSG_DEFAULT, _("Can't find %s pin %s called for in netlist.\n"), ElementName, PinNum);
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * Read the library-netlist build a pcb_true Netlist structure
+ */
+
+NetListTypePtr ProcNetlist(LibraryTypePtr net_menu)
+{
+	ConnectionTypePtr connection;
+	ConnectionType LastPoint;
+	NetTypePtr net;
+	static NetListTypePtr Wantlist = NULL;
+
+	if (!net_menu->MenuN)
+		return (NULL);
+	FreeNetListMemory(Wantlist);
+	free(Wantlist);
+	badnet = pcb_false;
+
+	/* find layer groups of the component side and solder side */
+	SLayer = GetLayerGroupNumberByNumber(solder_silk_layer);
+	CLayer = GetLayerGroupNumberByNumber(component_silk_layer);
+
+	Wantlist = (NetListTypePtr) calloc(1, sizeof(NetListType));
+	if (Wantlist) {
+		ALLPIN_LOOP(PCB->Data);
+		{
+			pin->Spare = NULL;
+			CLEAR_FLAG(PCB_FLAG_DRC, pin);
+		}
+		ENDALL_LOOP;
+		ALLPAD_LOOP(PCB->Data);
+		{
+			pad->Spare = NULL;
+			CLEAR_FLAG(PCB_FLAG_DRC, pad);
+		}
+		ENDALL_LOOP;
+		MENU_LOOP(net_menu);
+		{
+			if (menu->Name[0] == '*' || menu->flag == 0) {
+				badnet = pcb_true;
+				continue;
+			}
+			net = GetNetMemory(Wantlist);
+			if (menu->Style) {
+				int idx = pcb_route_style_lookup(&PCB->RouteStyle, 0, 0, 0, 0, menu->Style);
+				if (idx >= 0)
+					net->Style = PCB->RouteStyle.array+idx;
+			}
+			else											/* default to NULL if none found */
+				net->Style = NULL;
+			ENTRY_LOOP(menu);
+			{
+				if (SeekPad(entry, &LastPoint, pcb_false)) {
+					if (TEST_FLAG(PCB_FLAG_DRC, (PinTypePtr) LastPoint.ptr2))
+						Message(PCB_MSG_DEFAULT, _
+										("Error! Element %s pin %s appears multiple times in the netlist file.\n"),
+										NAMEONPCB_NAME((ElementTypePtr) LastPoint.ptr1),
+										(LastPoint.type ==
+										 PCB_TYPE_PIN) ? ((PinTypePtr) LastPoint.ptr2)->Number : ((PadTypePtr) LastPoint.ptr2)->Number);
+					else {
+						connection = GetConnectionMemory(net);
+						*connection = LastPoint;
+						/* indicate expect net */
+						connection->menu = menu;
+						/* mark as visited */
+						SET_FLAG(PCB_FLAG_DRC, (PinTypePtr) LastPoint.ptr2);
+						if (LastPoint.type == PCB_TYPE_PIN)
+							((PinTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
+						else
+							((PadTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
+					}
+				}
+				else
+					badnet = pcb_true;
+				/* check for more pins with the same number */
+				for (; SeekPad(entry, &LastPoint, pcb_true);) {
+					connection = GetConnectionMemory(net);
+					*connection = LastPoint;
+					/* indicate expect net */
+					connection->menu = menu;
+					/* mark as visited */
+					SET_FLAG(PCB_FLAG_DRC, (PinTypePtr) LastPoint.ptr2);
+					if (LastPoint.type == PCB_TYPE_PIN)
+						((PinTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
+					else
+						((PadTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
+				}
+			}
+			END_LOOP;
+		}
+		END_LOOP;
+	}
+	/* clear all visit marks */
+	ALLPIN_LOOP(PCB->Data);
+	{
+		CLEAR_FLAG(PCB_FLAG_DRC, pin);
+	}
+	ENDALL_LOOP;
+	ALLPAD_LOOP(PCB->Data);
+	{
+		CLEAR_FLAG(PCB_FLAG_DRC, pad);
+	}
+	ENDALL_LOOP;
+	return (Wantlist);
+}
+
+/*
+ * copy all connections from one net into another
+ * and then remove the first net from its netlist
+ */
+static void TransferNet(NetListTypePtr Netl, NetTypePtr SourceNet, NetTypePtr DestNet)
+{
+	ConnectionTypePtr conn;
+
+	/* It would be worth checking if SourceNet is NULL here to avoid a segfault. Seb James. */
+	CONNECTION_LOOP(SourceNet);
+	{
+		conn = GetConnectionMemory(DestNet);
+		*conn = *connection;
+	}
+	END_LOOP;
+	DestNet->Style = SourceNet->Style;
+	/* free the connection memory */
+	FreeNetMemory(SourceNet);
+	/* remove SourceNet from its netlist */
+	*SourceNet = Netl->Net[--(Netl->NetN)];
+	/* zero out old garbage */
+	memset(&Netl->Net[Netl->NetN], 0, sizeof(NetType));
+}
+
+static pcb_bool CheckShorts(LibraryMenuTypePtr theNet)
+{
+	pcb_bool newone, warn = pcb_false;
+	PointerListTypePtr generic = (PointerListTypePtr) calloc(1, sizeof(PointerListType));
+	/* the first connection was starting point so
+	 * the menu is always non-null
+	 */
+	void **menu = GetPointerMemory(generic);
+
+	*menu = theNet;
+	ALLPIN_LOOP(PCB->Data);
+	{
+		ElementType *e = pin->Element;
+/* TODO: should be: !TEST_FLAG(PCB_FLAG_NONETLIST, (ElementType *)pin->Element)*/
+		if ((TEST_FLAG(PCB_FLAG_DRC, pin)) && (!(e->Flags.f & PCB_FLAG_NONETLIST))) {
+			warn = pcb_true;
+			if (!pin->Spare) {
+				Message(PCB_MSG_DEFAULT, _("Warning! Net \"%s\" is shorted to %s pin %s\n"),
+								&theNet->Name[2], UNKNOWN(NAMEONPCB_NAME(element)), UNKNOWN(pin->Number));
+				stub_rat_found_short(pin, NULL, &theNet->Name[2]);
+				continue;
+			}
+			newone = pcb_true;
+			POINTER_LOOP(generic);
+			{
+				if (*ptr == pin->Spare) {
+					newone = pcb_false;
+					break;
+				}
+			}
+			END_LOOP;
+			if (newone) {
+				menu = GetPointerMemory(generic);
+				*menu = pin->Spare;
+				Message(PCB_MSG_DEFAULT, _("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
+								&theNet->Name[2], &((LibraryMenuTypePtr) (pin->Spare))->Name[2]);
+				stub_rat_found_short(pin, NULL, &theNet->Name[2]);
+			}
+		}
+	}
+	ENDALL_LOOP;
+	ALLPAD_LOOP(PCB->Data);
+	{
+		ElementType *e = pad->Element;
+/* TODO: should be: !TEST_FLAG(PCB_FLAG_NONETLIST, (ElementType *)pad->Element)*/
+		if ((TEST_FLAG(PCB_FLAG_DRC, pad)) && (!(e->Flags.f & PCB_FLAG_NONETLIST)) && (!(e->Name->Flags.f & PCB_FLAG_NONETLIST))) {
+			warn = pcb_true;
+			if (!pad->Spare) {
+				Message(PCB_MSG_DEFAULT, _("Warning! Net \"%s\" is shorted  to %s pad %s\n"),
+								&theNet->Name[2], UNKNOWN(NAMEONPCB_NAME(element)), UNKNOWN(pad->Number));
+				stub_rat_found_short(NULL, pad, &theNet->Name[2]);
+				continue;
+			}
+			newone = pcb_true;
+			POINTER_LOOP(generic);
+			{
+				if (*ptr == pad->Spare) {
+					newone = pcb_false;
+					break;
+				}
+			}
+			END_LOOP;
+			if (newone) {
+				menu = GetPointerMemory(generic);
+				*menu = pad->Spare;
+				Message(PCB_MSG_DEFAULT, _("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
+								&theNet->Name[2], &((LibraryMenuTypePtr) (pad->Spare))->Name[2]);
+				stub_rat_found_short(NULL, pad, &theNet->Name[2]);
+			}
+		}
+	}
+	ENDALL_LOOP;
+	FreePointerListMemory(generic);
+	free(generic);
+	return (warn);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * Determine existing interconnections of the net and gather into sub-nets
+ *
+ * initially the netlist has each connection in its own individual net
+ * afterwards there can be many fewer nets with multiple connections each
+ */
+static pcb_bool GatherSubnets(NetListTypePtr Netl, pcb_bool NoWarn, pcb_bool AndRats)
+{
+	NetTypePtr a, b;
+	ConnectionTypePtr conn;
+	pcb_cardinal_t m, n;
+	pcb_bool Warned = pcb_false;
+
+	for (m = 0; Netl->NetN > 0 && m < Netl->NetN; m++) {
+		a = &Netl->Net[m];
+		ResetConnections(pcb_false);
+		RatFindHook(a->Connection[0].type, a->Connection[0].ptr1, a->Connection[0].ptr2, a->Connection[0].ptr2, pcb_false, AndRats);
+		/* now anybody connected to the first point has PCB_FLAG_DRC set */
+		/* so move those to this subnet */
+		CLEAR_FLAG(PCB_FLAG_DRC, (PinTypePtr) a->Connection[0].ptr2);
+		for (n = m + 1; n < Netl->NetN; n++) {
+			b = &Netl->Net[n];
+			/* There can be only one connection in net b */
+			if (TEST_FLAG(PCB_FLAG_DRC, (PinTypePtr) b->Connection[0].ptr2)) {
+				CLEAR_FLAG(PCB_FLAG_DRC, (PinTypePtr) b->Connection[0].ptr2);
+				TransferNet(Netl, b, a);
+				/* back up since new subnet is now at old index */
+				n--;
+			}
+		}
+		/* now add other possible attachment points to the subnet */
+		/* e.g. line end-points and vias */
+		/* don't add non-manhattan lines, the auto-router can't route to them */
+		ALLLINE_LOOP(PCB->Data);
+		{
+			if (TEST_FLAG(PCB_FLAG_DRC, line)
+					&& ((line->Point1.X == line->Point2.X)
+							|| (line->Point1.Y == line->Point2.Y))) {
+				conn = GetConnectionMemory(a);
+				conn->X = line->Point1.X;
+				conn->Y = line->Point1.Y;
+				conn->type = PCB_TYPE_LINE;
+				conn->ptr1 = layer;
+				conn->ptr2 = line;
+				conn->group = GetLayerGroupNumberByPointer(layer);
+				conn->menu = NULL;			/* agnostic view of where it belongs */
+				conn = GetConnectionMemory(a);
+				conn->X = line->Point2.X;
+				conn->Y = line->Point2.Y;
+				conn->type = PCB_TYPE_LINE;
+				conn->ptr1 = layer;
+				conn->ptr2 = line;
+				conn->group = GetLayerGroupNumberByPointer(layer);
+				conn->menu = NULL;
+			}
+		}
+		ENDALL_LOOP;
+		/* add polygons so the auto-router can see them as targets */
+		ALLPOLYGON_LOOP(PCB->Data);
+		{
+			if (TEST_FLAG(PCB_FLAG_DRC, polygon)) {
+				conn = GetConnectionMemory(a);
+				/* make point on a vertex */
+				conn->X = polygon->Clipped->contours->head.point[0];
+				conn->Y = polygon->Clipped->contours->head.point[1];
+				conn->type = PCB_TYPE_POLYGON;
+				conn->ptr1 = layer;
+				conn->ptr2 = polygon;
+				conn->group = GetLayerGroupNumberByPointer(layer);
+				conn->menu = NULL;			/* agnostic view of where it belongs */
+			}
+		}
+		ENDALL_LOOP;
+		VIA_LOOP(PCB->Data);
+		{
+			if (TEST_FLAG(PCB_FLAG_DRC, via)) {
+				conn = GetConnectionMemory(a);
+				conn->X = via->X;
+				conn->Y = via->Y;
+				conn->type = PCB_TYPE_VIA;
+				conn->ptr1 = via;
+				conn->ptr2 = via;
+				conn->group = SLayer;
+			}
+		}
+		END_LOOP;
+		if (!NoWarn)
+			Warned |= CheckShorts(a->Connection[0].menu);
+	}
+	ResetConnections(pcb_false);
+	return (Warned);
+}
+
+/* ---------------------------------------------------------------------------
+ * Draw a rat net (tree) having the shortest lines
+ * this also frees the subnet memory as they are consumed
+ *
+ * Note that the Netl we are passed is NOT the main netlist - it's the
+ * connectivity for ONE net.  It represents the CURRENT connectivity
+ * state for the net, with each Netl->Net[N] representing one
+ * copper-connected subset of the net.
+ */
+
+static pcb_bool
+DrawShortestRats(NetListTypePtr Netl,
+								 void (*funcp) (register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr))
+{
+	RatTypePtr line;
+	register float distance, temp;
+	register ConnectionTypePtr conn1, conn2, firstpoint, secondpoint;
+	PolygonTypePtr polygon;
+	pcb_bool changed = pcb_false;
+	pcb_bool havepoints;
+	pcb_cardinal_t n, m, j;
+	NetTypePtr next, subnet, theSubnet = NULL;
+
+	/* This is just a sanity check, to make sure we're passed
+	 * *something*.
+	 */
+	if (!Netl || Netl->NetN < 1)
+		return pcb_false;
+
+	/*
+	 * Everything inside the NetList Netl should be connected together.
+	 * Each Net in Netl is a group of Connections which are already
+	 * connected together somehow, either by real wires or by rats we've
+	 * already drawn.  Each Connection is a vertex within that blob of
+	 * connected items.  This loop finds the closest vertex pairs between
+	 * each blob and draws rats that merge the blobs until there's just
+	 * one big blob.
+	 *
+	 * Just to clarify, with some examples:
+	 *
+	 * Each Netl is one full net from a netlist, like from gnetlist.
+	 * Each Netl->Net[N] is a subset of that net that's already
+	 * physically connected on the pcb.
+	 *
+	 * So a new design with no traces yet, would have a huge list of Net[N],
+	 * each with one pin in it.
+	 *
+	 * A fully routed design would have one Net[N] with all the pins
+	 * (for that net) in it.
+	 */
+
+	/*
+	 * We keep doing this do/while loop until everything's connected.
+	 * I.e. once per rat we add.
+	 */
+	distance = 0.0;
+	havepoints = pcb_true;						/* so we run the loop at least once */
+	while (Netl->NetN > 1 && havepoints) {
+		/* This is the top of the "find one rat" logic.  */
+		havepoints = pcb_false;
+		firstpoint = secondpoint = NULL;
+
+		/* Test Net[0] vs Net[N] for N=1..max.  Find the shortest
+		   distance between any two points in different blobs.  */
+		subnet = &Netl->Net[0];
+		for (j = 1; j < Netl->NetN; j++) {
+			/*
+			 * Scan between Net[0] blob (subnet) and Net[N] blob (next).
+			 * Note the shortest distance we find.
+			 */
+			next = &Netl->Net[j];
+			for (n = subnet->ConnectionN - 1; n != -1; n--) {
+				conn1 = &subnet->Connection[n];
+				for (m = next->ConnectionN - 1; m != -1; m--) {
+					conn2 = &next->Connection[m];
+					/*
+					 * At this point, conn1 and conn2 are two pins in
+					 * different blobs of the same net.  See how far
+					 * apart they are, and if they're "closer" than what
+					 * we already have.
+					 */
+
+					/*
+					 * Prefer to connect Connections over polygons to the
+					 * polygons (ie assume the user wants a via to a plane,
+					 * not a daisy chain).  Further prefer to pick an existing
+					 * via in the Net to make that connection.
+					 */
+					if (conn1->type == PCB_TYPE_POLYGON &&
+							(polygon = (PolygonTypePtr) conn1->ptr2) &&
+							!(distance == 0 &&
+								firstpoint && firstpoint->type == PCB_TYPE_VIA) && IsPointInPolygonIgnoreHoles(conn2->X, conn2->Y, polygon)) {
+						distance = 0;
+						firstpoint = conn2;
+						secondpoint = conn1;
+						theSubnet = next;
+						havepoints = pcb_true;
+					}
+					else if (conn2->type == PCB_TYPE_POLYGON &&
+									 (polygon = (PolygonTypePtr) conn2->ptr2) &&
+									 !(distance == 0 &&
+										 firstpoint && firstpoint->type == PCB_TYPE_VIA) && IsPointInPolygonIgnoreHoles(conn1->X, conn1->Y, polygon)) {
+						distance = 0;
+						firstpoint = conn1;
+						secondpoint = conn2;
+						theSubnet = next;
+						havepoints = pcb_true;
+					}
+					else if ((temp = SQUARE(conn1->X - conn2->X) + SQUARE(conn1->Y - conn2->Y)) < distance || !firstpoint) {
+						distance = temp;
+						firstpoint = conn1;
+						secondpoint = conn2;
+						theSubnet = next;
+						havepoints = pcb_true;
+					}
+				}
+			}
+		}
+
+		/*
+		 * If HAVEPOINTS is pcb_true, we've found a pair of points in two
+		 * separate blobs of the net, and need to connect them together.
+		 */
+		if (havepoints) {
+			if (funcp) {
+				(*funcp) (firstpoint, secondpoint, subnet->Style);
+			}
+			else {
+				/* found the shortest distance subnet, draw the rat */
+				if ((line = CreateNewRat(PCB->Data,
+																 firstpoint->X, firstpoint->Y,
+																 secondpoint->X, secondpoint->Y,
+																 firstpoint->group, secondpoint->group, conf_core.appearance.rat_thickness, NoFlags())) != NULL) {
+					if (distance == 0)
+						SET_FLAG(PCB_FLAG_VIA, line);
+					AddObjectToCreateUndoList(PCB_TYPE_RATLINE, line, line, line);
+					DrawRat(line);
+					changed = pcb_true;
+				}
+			}
+
+			/* copy theSubnet into the current subnet */
+			TransferNet(Netl, theSubnet, subnet);
+		}
+	}
+
+	/* presently nothing to do with the new subnet */
+	/* so we throw it away and free the space */
+	FreeNetMemory(&Netl->Net[--(Netl->NetN)]);
+	/* Sadly adding a rat line messes up the sorted arrays in connection finder */
+	/* hace: perhaps not necessarily now that they aren't stored in normal layers */
+	if (changed) {
+		FreeConnectionLookupMemory();
+		InitConnectionLookup();
+	}
+	return (changed);
+}
+
+
+/* ---------------------------------------------------------------------------
+ *  AddAllRats puts the rats nest into the layout from the loaded netlist
+ *  if SelectedOnly is pcb_true, it will only draw rats to selected pins and pads
+ */
+pcb_bool
+AddAllRats(pcb_bool SelectedOnly,
+					 void (*funcp) (register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr))
+{
+	NetListTypePtr Nets, Wantlist;
+	NetTypePtr lonesome;
+	ConnectionTypePtr onepin;
+	pcb_bool changed, Warned = pcb_false;
+
+	/* the netlist library has the text form
+	 * ProcNetlist fills in the Netlist
+	 * structure the way the final routing
+	 * is supposed to look
+	 */
+	Wantlist = ProcNetlist(&(PCB->NetlistLib[NETLIST_EDITED]));
+	if (!Wantlist) {
+		Message(PCB_MSG_DEFAULT, _("Can't add rat lines because no netlist is loaded.\n"));
+		return (pcb_false);
+	}
+	changed = pcb_false;
+	/* initialize finding engine */
+	InitConnectionLookup();
+	SaveFindFlag(PCB_FLAG_DRC);
+	Nets = (NetListTypePtr) calloc(1, sizeof(NetListType));
+	/* now we build another netlist (Nets) for each
+	 * net in Wantlist that shows how it actually looks now,
+	 * then fill in any missing connections with rat lines.
+	 *
+	 * we first assume each connection is separate
+	 * (no routing), then gather them into groups
+	 * if the net is all routed, the new netlist (Nets)
+	 * will have only one net entry.
+	 * Note that DrawShortestRats consumes all nets
+	 * from Nets, so *Nets is empty after the
+	 * DrawShortestRats call
+	 */
+	NET_LOOP(Wantlist);
+	{
+		CONNECTION_LOOP(net);
+		{
+			if (!SelectedOnly || TEST_FLAG(PCB_FLAG_SELECTED, (PinTypePtr) connection->ptr2)) {
+				lonesome = GetNetMemory(Nets);
+				onepin = GetConnectionMemory(lonesome);
+				*onepin = *connection;
+				lonesome->Style = net->Style;
+			}
+		}
+		END_LOOP;
+		Warned |= GatherSubnets(Nets, SelectedOnly, pcb_true);
+		if (Nets->NetN > 0)
+			changed |= DrawShortestRats(Nets, funcp);
+	}
+	END_LOOP;
+	FreeNetListMemory(Nets);
+	free(Nets);
+	FreeConnectionLookupMemory();
+	RestoreFindFlag();
+	if (funcp)
+		return (pcb_true);
+
+	if (Warned || changed) {
+		stub_rat_proc_shorts();
+		Draw();
+	}
+
+	if (Warned)
+		conf_core.temp.rat_warn = pcb_true;
+
+	if (changed) {
+		IncrementUndoSerialNumber();
+		if (ratlist_length(&PCB->Data->Rat) > 0) {
+			Message(PCB_MSG_DEFAULT, "%d rat line%s remaining\n", ratlist_length(&PCB->Data->Rat), ratlist_length(&PCB->Data->Rat) > 1 ? "s" : "");
+		}
+		return (pcb_true);
+	}
+	if (!SelectedOnly && !Warned) {
+		if (!ratlist_length(&PCB->Data->Rat) && !badnet)
+			Message(PCB_MSG_DEFAULT, _("Congratulations!!\n" "The layout is complete and has no shorted nets.\n"));
+		else
+			Message(PCB_MSG_DEFAULT, _("Nothing more to add, but there are\n"
+								"either rat-lines in the layout, disabled nets\n" "in the net-list, or missing components\n"));
+	}
+	return (pcb_false);
+}
+
+/* XXX: This is copied in large part from AddAllRats above; for
+ * maintainability, AddAllRats probably wants to be tweaked to use this
+ * version of the code so that we don't have duplication. */
+NetListListType CollectSubnets(pcb_bool SelectedOnly)
+{
+	NetListListType result = { 0, 0, NULL };
+	NetListTypePtr Nets, Wantlist;
+	NetTypePtr lonesome;
+	ConnectionTypePtr onepin;
+
+	/* the netlist library has the text form
+	 * ProcNetlist fills in the Netlist
+	 * structure the way the final routing
+	 * is supposed to look
+	 */
+	Wantlist = ProcNetlist(&(PCB->NetlistLib[NETLIST_EDITED]));
+	if (!Wantlist) {
+		Message(PCB_MSG_DEFAULT, _("Can't add rat lines because no netlist is loaded.\n"));
+		return result;
+	}
+	/* initialize finding engine */
+	InitConnectionLookup();
+	SaveFindFlag(PCB_FLAG_DRC);
+	/* now we build another netlist (Nets) for each
+	 * net in Wantlist that shows how it actually looks now,
+	 * then fill in any missing connections with rat lines.
+	 *
+	 * we first assume each connection is separate
+	 * (no routing), then gather them into groups
+	 * if the net is all routed, the new netlist (Nets)
+	 * will have only one net entry.
+	 * Note that DrawShortestRats consumes all nets
+	 * from Nets, so *Nets is empty after the
+	 * DrawShortestRats call
+	 */
+	NET_LOOP(Wantlist);
+	{
+		Nets = GetNetListMemory(&result);
+		CONNECTION_LOOP(net);
+		{
+			if (!SelectedOnly || TEST_FLAG(PCB_FLAG_SELECTED, (PinTypePtr) connection->ptr2)) {
+				lonesome = GetNetMemory(Nets);
+				onepin = GetConnectionMemory(lonesome);
+				*onepin = *connection;
+				lonesome->Style = net->Style;
+			}
+		}
+		END_LOOP;
+		/* Note that AndRats is *FALSE* here! */
+		GatherSubnets(Nets, SelectedOnly, pcb_false);
+	}
+	END_LOOP;
+	FreeConnectionLookupMemory();
+	RestoreFindFlag();
+	return result;
+}
+
+/*
+ * Check to see if a particular name is the name of an already existing rats
+ * line
+ */
+static int rat_used(char *name)
+{
+	if (name == NULL)
+		return -1;
+
+	MENU_LOOP(&(PCB->NetlistLib[NETLIST_EDITED]));
+	{
+		if (menu->Name && (strcmp(menu->Name, name) == 0))
+			return 1;
+	}
+	END_LOOP;
+
+	return 0;
+}
+
+	/* These next two functions moved from the original netlist.c as part of the
+	   |  gui code separation for the Gtk port.
+	 */
+RatTypePtr AddNet(void)
+{
+	static int ratDrawn = 0;
+	char name1[256], *name2;
+	pcb_cardinal_t group1, group2;
+	char ratname[20];
+	int found;
+	void *ptr1, *ptr2, *ptr3;
+	LibraryMenuTypePtr menu;
+	LibraryEntryTypePtr entry;
+
+	if (Crosshair.AttachedLine.Point1.X == Crosshair.AttachedLine.Point2.X
+			&& Crosshair.AttachedLine.Point1.Y == Crosshair.AttachedLine.Point2.Y)
+		return (NULL);
+
+	found = SearchObjectByLocation(PCB_TYPE_PAD | PCB_TYPE_PIN, &ptr1, &ptr2, &ptr3,
+																 Crosshair.AttachedLine.Point1.X, Crosshair.AttachedLine.Point1.Y, 5);
+	if (found == PCB_TYPE_NONE) {
+		Message(PCB_MSG_DEFAULT, _("No pad/pin under rat line\n"));
+		return (NULL);
+	}
+	if (NAMEONPCB_NAME((ElementTypePtr) ptr1) == NULL || *NAMEONPCB_NAME((ElementTypePtr) ptr1) == 0) {
+		Message(PCB_MSG_DEFAULT, _("You must name the starting element first\n"));
+		return (NULL);
+	}
+
+	/* will work for pins to since the FLAG is common */
+	group1 = (TEST_FLAG(PCB_FLAG_ONSOLDER, (PadTypePtr) ptr2) ?
+						GetLayerGroupNumberByNumber(solder_silk_layer) : GetLayerGroupNumberByNumber(component_silk_layer));
+	strcpy(name1, ConnectionName(found, ptr1, ptr2));
+	found = SearchObjectByLocation(PCB_TYPE_PAD | PCB_TYPE_PIN, &ptr1, &ptr2, &ptr3,
+																 Crosshair.AttachedLine.Point2.X, Crosshair.AttachedLine.Point2.Y, 5);
+	if (found == PCB_TYPE_NONE) {
+		Message(PCB_MSG_DEFAULT, _("No pad/pin under rat line\n"));
+		return (NULL);
+	}
+	if (NAMEONPCB_NAME((ElementTypePtr) ptr1) == NULL || *NAMEONPCB_NAME((ElementTypePtr) ptr1) == 0) {
+		Message(PCB_MSG_DEFAULT, _("You must name the ending element first\n"));
+		return (NULL);
+	}
+	group2 = (TEST_FLAG(PCB_FLAG_ONSOLDER, (PadTypePtr) ptr2) ?
+						GetLayerGroupNumberByNumber(solder_silk_layer) : GetLayerGroupNumberByNumber(component_silk_layer));
+	name2 = ConnectionName(found, ptr1, ptr2);
+
+	menu = pcb_netnode_to_netname(name1);
+	if (menu) {
+		if (pcb_netnode_to_netname(name2)) {
+			Message(PCB_MSG_DEFAULT, _("Both connections already in netlist - cannot merge nets\n"));
+			return (NULL);
+		}
+		entry = GetLibraryEntryMemory(menu);
+		entry->ListEntry = pcb_strdup(name2);
+		entry->ListEntry_dontfree = 0;
+		pcb_netnode_to_netname(name2);
+		goto ratIt;
+	}
+	/* ok, the first name did not belong to a net */
+	menu = pcb_netnode_to_netname(name2);
+	if (menu) {
+		entry = GetLibraryEntryMemory(menu);
+		entry->ListEntry = pcb_strdup(name1);
+		entry->ListEntry_dontfree = 0;
+		pcb_netnode_to_netname(name1);
+		goto ratIt;
+	}
+
+	/*
+	 * neither belong to a net, so create a new one.
+	 *
+	 * before creating a new rats here, we need to search
+	 * for a unique name.
+	 */
+	sprintf(ratname, "  ratDrawn%i", ++ratDrawn);
+	while (rat_used(ratname)) {
+		sprintf(ratname, "  ratDrawn%i", ++ratDrawn);
+	}
+
+	menu = GetLibraryMenuMemory(&(PCB->NetlistLib[NETLIST_EDITED]), NULL);
+	menu->Name = pcb_strdup(ratname);
+	entry = GetLibraryEntryMemory(menu);
+	entry->ListEntry = pcb_strdup(name1);
+	entry->ListEntry_dontfree = 0;
+
+	entry = GetLibraryEntryMemory(menu);
+	entry->ListEntry = pcb_strdup(name2);
+	entry->ListEntry_dontfree = 0;
+	menu->flag = 1;
+
+ratIt:
+	pcb_netlist_changed(0);
+	return (CreateNewRat(PCB->Data, Crosshair.AttachedLine.Point1.X,
+											 Crosshair.AttachedLine.Point1.Y,
+											 Crosshair.AttachedLine.Point2.X,
+											 Crosshair.AttachedLine.Point2.Y, group1, group2, conf_core.appearance.rat_thickness, NoFlags()));
+}
+
+
+char *ConnectionName(int type, void *ptr1, void *ptr2)
+{
+	static char name[256];
+	char *num;
+
+	switch (type) {
+	case PCB_TYPE_PIN:
+		num = ((PinTypePtr) ptr2)->Number;
+		break;
+	case PCB_TYPE_PAD:
+		num = ((PadTypePtr) ptr2)->Number;
+		break;
+	default:
+		return (NULL);
+	}
+	strcpy(name, UNKNOWN(NAMEONPCB_NAME((ElementTypePtr) ptr1)));
+	strcat(name, "-");
+	strcat(name, UNKNOWN(num));
+	return (name);
+}
diff --git a/src/rats.h b/src/rats.h
new file mode 100644
index 0000000..7629721
--- /dev/null
+++ b/src/rats.h
@@ -0,0 +1,45 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *	rats.c, rats.h Copyright (C) 1997, harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for rats routines */
+
+#ifndef	PCB_RATS_H
+#define	PCB_RATS_H
+
+#include "global.h"
+
+RatTypePtr AddNet(void);
+char *ConnectionName(int, void *, void *);
+
+pcb_bool AddAllRats(pcb_bool, void (*)(register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr));
+pcb_bool SeekPad(LibraryEntryTypePtr, ConnectionTypePtr, pcb_bool);
+
+NetListTypePtr ProcNetlist(LibraryTypePtr);
+NetListListType CollectSubnets(pcb_bool);
+
+#endif
diff --git a/src/rats_act.c b/src/rats_act.c
new file mode 100644
index 0000000..b870af2
--- /dev/null
+++ b/src/rats_act.c
@@ -0,0 +1,225 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "action_helper.h"
+#include "error.h"
+#include "undo.h"
+#include "set.h"
+#include "misc.h"
+#include "find.h"
+#include "remove.h"
+#include "funchash_core.h"
+
+#include "rats.h"
+#include "draw.h"
+
+/* --------------------------------------------------------------------------- */
+
+static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
+
+static const char addrats_help[] = "Add one or more rat lines to the board.";
+
+/* %start-doc actions AddRats
+
+ at table @code
+
+ at item AllRats
+Create rat lines for all loaded nets that aren't already connected on
+with copper.
+
+ at item SelectedRats
+Similarly, but only add rat lines for nets connected to selected pins
+and pads.
+
+ at item Close
+Selects the shortest unselected rat on the board.
+
+ at end table
+
+%end-doc */
+
+static int ActionAddRats(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	RatTypePtr shorty;
+	float len, small;
+
+	if (function) {
+		if (conf_core.temp.rat_warn)
+			ClearWarnings();
+		switch (funchash_get(function, NULL)) {
+		case F_AllRats:
+			if (AddAllRats(pcb_false, NULL))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_SelectedRats:
+		case F_Selected:
+			if (AddAllRats(pcb_true, NULL))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_Close:
+			small = SQUARE(MAX_COORD);
+			shorty = NULL;
+			RAT_LOOP(PCB->Data);
+			{
+				if (TEST_FLAG(PCB_FLAG_SELECTED, line))
+					continue;
+				len = SQUARE(line->Point1.X - line->Point2.X) + SQUARE(line->Point1.Y - line->Point2.Y);
+				if (len < small) {
+					small = len;
+					shorty = line;
+				}
+			}
+			END_LOOP;
+			if (shorty) {
+				AddObjectToFlagUndoList(PCB_TYPE_RATLINE, shorty, shorty, shorty);
+				SET_FLAG(PCB_FLAG_SELECTED, shorty);
+				DrawRat(shorty);
+				Draw();
+				CenterDisplay((shorty->Point2.X + shorty->Point1.X) / 2, (shorty->Point2.Y + shorty->Point1.Y) / 2);
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char connection_syntax[] = "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
+
+static const char connection_help[] = "Searches connections of the object at the cursor position.";
+
+/* %start-doc actions Connection
+
+Connections found with this action will be highlighted in the
+``connected-color'' color and will have the ``found'' flag set.
+
+ at table @code
+
+ at item Find
+The net under the cursor is ``found''.
+
+ at item ResetLinesAndPolygons
+Any ``found'' lines and polygons are marked ``not found''.
+
+ at item ResetPinsAndVias
+Any ``found'' pins and vias are marked ``not found''.
+
+ at item Reset
+All ``found'' objects are marked ``not found''.
+
+ at end table
+
+%end-doc */
+
+static int ActionConnection(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		switch (funchash_get(function, NULL)) {
+		case F_Find:
+			{
+				gui->get_coords(_("Click on a connection"), &x, &y);
+				LookupConnection(x, y, pcb_true, 1, PCB_FLAG_FOUND);
+				break;
+			}
+
+		case F_ResetLinesAndPolygons:
+			if (ResetFoundLinesAndPolygons(pcb_true)) {
+				IncrementUndoSerialNumber();
+				Draw();
+			}
+			break;
+
+		case F_ResetPinsViasAndPads:
+			if (ResetFoundPinsViasAndPads(pcb_true)) {
+				IncrementUndoSerialNumber();
+				Draw();
+			}
+			break;
+
+		case F_Reset:
+			if (ResetConnections(pcb_true)) {
+				IncrementUndoSerialNumber();
+				Draw();
+			}
+			break;
+		}
+		return 0;
+	}
+
+	AFAIL(connection);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char deleterats_syntax[] = "DeleteRats(AllRats|Selected|SelectedRats)";
+
+static const char deleterats_help[] = "Delete rat lines.";
+
+/* %start-doc actions DeleteRats
+
+%end-doc */
+
+static int ActionDeleteRats(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		if (conf_core.temp.rat_warn)
+			ClearWarnings();
+		switch (funchash_get(function, NULL)) {
+		case F_AllRats:
+			if (DeleteRats(pcb_false))
+				SetChangedFlag(pcb_true);
+			break;
+		case F_SelectedRats:
+		case F_Selected:
+			if (DeleteRats(pcb_true))
+				SetChangedFlag(pcb_true);
+			break;
+		}
+	}
+	return 0;
+}
+
+
+HID_Action rats_action_list[] = {
+	{"AddRats", 0, ActionAddRats,
+	 addrats_help, addrats_syntax}
+	,
+	{"Connection", 0, ActionConnection,
+	 connection_help, connection_syntax}
+	,
+	{"DeleteRats", 0, ActionDeleteRats,
+	 deleterats_help, deleterats_syntax}
+};
+
+REGISTER_ACTIONS(rats_action_list, NULL)
diff --git a/src/rats_patch.c b/src/rats_patch.c
new file mode 100644
index 0000000..878eb40
--- /dev/null
+++ b/src/rats_patch.c
@@ -0,0 +1,500 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2015 Tibor 'Igor2' Palinkas
+ *
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "rats_patch.h"
+#include "genht/htsp.h"
+#include "genht/hash.h"
+#include "create.h"
+
+#include "config.h"
+#include "data.h"
+#include "error.h"
+#include "buffer.h"
+#include "remove.h"
+#include "copy.h"
+#include "compat_misc.h"
+
+static void rats_patch_remove(PCBTypePtr pcb, rats_patch_line_t * n, int do_free);
+
+const char *pcb_netlist_names[NUM_NETLISTS] = {
+	"input",
+	"edited"
+};
+
+void rats_patch_append(PCBTypePtr pcb, rats_patch_op_t op, const char *id, const char *a1, const char *a2)
+{
+	rats_patch_line_t *n;
+
+	n = malloc(sizeof(rats_patch_line_t));
+
+	n->op = op;
+	n->id = pcb_strdup(id);
+	n->arg1.net_name = pcb_strdup(a1);
+	if (a2 != NULL)
+		n->arg2.attrib_val = pcb_strdup(a2);
+	else
+		n->arg2.attrib_val = NULL;
+
+	/* link in */
+	n->prev = pcb->NetlistPatchLast;
+	if (pcb->NetlistPatches != NULL) {
+		pcb->NetlistPatchLast->next = n;
+		pcb->NetlistPatchLast = n;
+	}
+	else
+		pcb->NetlistPatchLast = pcb->NetlistPatches = n;
+	n->next = NULL;
+}
+
+static void rats_patch_free_fields(rats_patch_line_t *n)
+{
+	if (n->id != NULL)
+		free(n->id);
+	if (n->arg1.net_name != NULL)
+		free(n->arg1.net_name);
+	if (n->arg2.attrib_val != NULL)
+		free(n->arg2.attrib_val);
+}
+
+void rats_patch_destroy(PCBTypePtr pcb)
+{
+	rats_patch_line_t *n, *next;
+
+	for(n = pcb->NetlistPatches; n != NULL; n = next) {
+		next = n->next;
+		rats_patch_free_fields(n);
+		free(n);
+	}
+}
+
+void rats_patch_append_optimize(PCBTypePtr pcb, rats_patch_op_t op, const char *id, const char *a1, const char *a2)
+{
+	rats_patch_op_t seek_op;
+	rats_patch_line_t *n;
+
+	switch (op) {
+	case RATP_ADD_CONN:
+		seek_op = RATP_DEL_CONN;
+		break;
+	case RATP_DEL_CONN:
+		seek_op = RATP_ADD_CONN;
+		break;
+	case RATP_CHANGE_ATTRIB:
+		seek_op = RATP_CHANGE_ATTRIB;
+		break;
+	}
+
+	/* keep the list clean: remove the last operation that becomes obsolete by the new one */
+	for (n = pcb->NetlistPatchLast; n != NULL; n = n->prev) {
+		if ((n->op == seek_op) && (strcmp(n->id, id) == 0)) {
+			switch (op) {
+			case RATP_CHANGE_ATTRIB:
+				if (strcmp(n->arg1.attrib_name, a1) != 0)
+					break;
+				rats_patch_remove(pcb, n, 1);
+				goto quit;
+			case RATP_ADD_CONN:
+			case RATP_DEL_CONN:
+				if (strcmp(n->arg1.net_name, a1) != 0)
+					break;
+				rats_patch_remove(pcb, n, 1);
+				goto quit;
+			}
+		}
+	}
+
+quit:;
+	rats_patch_append(pcb, op, id, a1, a2);
+}
+
+/* Unlink n from the list; if do_free is non-zero, also free fields and n */
+static void rats_patch_remove(PCBTypePtr pcb, rats_patch_line_t * n, int do_free)
+{
+	/* if we are the first or last... */
+	if (n == pcb->NetlistPatches)
+		pcb->NetlistPatches = n->next;
+	if (n == pcb->NetlistPatchLast)
+		pcb->NetlistPatchLast = n->prev;
+
+	/* extra ifs, just in case n is already unlinked */
+	if (n->prev != NULL)
+		n->prev->next = n->next;
+	if (n->next != NULL)
+		n->next->prev = n->prev;
+
+	if (do_free) {
+		rats_patch_free_fields(n);
+		free(n);
+	}
+}
+
+static void netlist_free(LibraryTypePtr dst)
+{
+	int n, p;
+
+	if (dst->Menu == NULL)
+		return;
+
+	for (n = 0; n < dst->MenuN; n++) {
+		LibraryMenuTypePtr dmenu = &dst->Menu[n];
+		free(dmenu->Name);
+		for (p = 0; p < dmenu->EntryN; p++) {
+			LibraryEntryTypePtr dentry = &dmenu->Entry[p];
+			free((char*)dentry->ListEntry);
+		}
+		free(dmenu->Entry);
+	}
+	free(dst->Menu);
+	dst->Menu = NULL;
+}
+
+static void netlist_copy(LibraryTypePtr dst, LibraryTypePtr src)
+{
+	int n, p;
+	dst->MenuMax = dst->MenuN = src->MenuN;
+	if (src->MenuN != 0) {
+		dst->Menu = calloc(sizeof(LibraryMenuType), dst->MenuN);
+		for (n = 0; n < src->MenuN; n++) {
+			LibraryMenuTypePtr smenu = &src->Menu[n];
+			LibraryMenuTypePtr dmenu = &dst->Menu[n];
+/*		fprintf(stderr, "Net %d name='%s': ", n, smenu->Name+2);*/
+			dmenu->Name = pcb_strdup(smenu->Name);
+			dmenu->EntryMax = dmenu->EntryN = smenu->EntryN;
+			dmenu->Entry = calloc(sizeof(LibraryEntryType), dmenu->EntryN);
+			dmenu->flag = smenu->flag;
+			dmenu->internal = smenu->internal;
+			for (p = 0; p < smenu->EntryN; p++) {
+			LibraryEntryTypePtr sentry = &smenu->Entry[p];
+				LibraryEntryTypePtr dentry = &dmenu->Entry[p];
+				dentry->ListEntry = pcb_strdup(sentry->ListEntry);
+				dentry->ListEntry_dontfree = 0;
+/*			fprintf (stderr, " '%s'/%p", dentry->ListEntry, dentry->ListEntry);*/
+			}
+/*		fprintf(stderr, "\n");*/
+		}
+	}
+	else
+		dst->Menu = NULL;
+}
+
+
+int rats_patch_apply_conn(PCBTypePtr pcb, rats_patch_line_t * patch, int del)
+{
+	int n;
+
+	for (n = 0; n < pcb->NetlistLib[NETLIST_EDITED].MenuN; n++) {
+		LibraryMenuTypePtr menu = &pcb->NetlistLib[NETLIST_EDITED].Menu[n];
+		if (strcmp(menu->Name + 2, patch->arg1.net_name) == 0) {
+			int p;
+			for (p = 0; p < menu->EntryN; p++) {
+				LibraryEntryTypePtr entry = &menu->Entry[p];
+/*				fprintf (stderr, "C'%s'/%p '%s'/%p\n", entry->ListEntry, entry->ListEntry, patch->id, patch->id);*/
+				if (strcmp(entry->ListEntry, patch->id) == 0) {
+					if (del) {
+						/* want to delete and it's on the list */
+						memmove(&menu->Entry[p], &menu->Entry[p + 1], (menu->EntryN - p - 1) * sizeof(LibraryEntryType));
+						menu->EntryN--;
+						return 0;
+					}
+					/* want to add, and pin is on the list -> already added */
+					return 1;
+				}
+			}
+			/* If we got here, pin is not on the list */
+			if (del)
+				return 1;
+
+			/* Wanted to add, let's add it */
+			CreateNewConnection(menu, (char *) patch->id);
+			return 0;
+		}
+	}
+
+	/* couldn't find the net: create it */
+	{
+		LibraryMenuType *net = NULL;
+		net = CreateNewNet(&pcb->NetlistLib[NETLIST_EDITED], patch->arg1.net_name, NULL);
+		if (net == NULL)
+			return 1;
+		CreateNewConnection(net, (char *) patch->id);
+	}
+	return 0;
+}
+
+
+int rats_patch_apply(PCBTypePtr pcb, rats_patch_line_t * patch)
+{
+	switch (patch->op) {
+	case RATP_ADD_CONN:
+		return rats_patch_apply_conn(pcb, patch, 0);
+	case RATP_DEL_CONN:
+		return rats_patch_apply_conn(pcb, patch, 1);
+	case RATP_CHANGE_ATTRIB:
+#warning TODO: just check wheter it is still valid
+		break;
+	}
+	return 0;
+}
+
+void rats_patch_make_edited(PCBTypePtr pcb)
+{
+	rats_patch_line_t *n;
+
+	netlist_free(&(pcb->NetlistLib[NETLIST_EDITED]));
+	netlist_copy(&(pcb->NetlistLib[NETLIST_EDITED]), &(pcb->NetlistLib[NETLIST_INPUT]));
+	for (n = pcb->NetlistPatches; n != NULL; n = n->next)
+		rats_patch_apply(pcb, n);
+}
+
+static LibraryMenuTypePtr rats_patch_find_net(PCBTypePtr pcb, const char *netname, int listidx)
+{
+	int n;
+
+	for (n = 0; n < pcb->NetlistLib[listidx].MenuN; n++) {
+		LibraryMenuTypePtr menu = &pcb->NetlistLib[listidx].Menu[n];
+		if (strcmp(menu->Name + 2, netname) == 0)
+			return menu;
+	}
+	return NULL;
+}
+
+int rats_patch_export(PCBType *pcb, rats_patch_line_t *pat, pcb_bool need_info_lines, void (*cb)(void *ctx, pcb_rats_patch_export_ev_t ev, const char *netn, const char *key, const char *val), void *ctx)
+{
+	rats_patch_line_t *n;
+
+	if (need_info_lines) {
+		htsp_t *seen;
+		seen = htsp_alloc(strhash, strkeyeq);
+
+		/* have to print net_info lines */
+		for (n = pat; n != NULL; n = n->next) {
+			switch (n->op) {
+			case RATP_ADD_CONN:
+			case RATP_DEL_CONN:
+				if (htsp_get(seen, n->arg1.net_name) == NULL) {
+					LibraryMenuTypePtr net;
+					int p;
+
+					net = rats_patch_find_net(pcb, n->arg1.net_name, NETLIST_INPUT);
+/*					printf("net: '%s' %p\n", n->arg1.net_name, (void *)net);*/
+					if (net != NULL) {
+						htsp_set(seen, n->arg1.net_name, net);
+						cb(ctx, PCB_RPE_INFO_BEGIN, n->arg1.net_name, NULL, NULL);
+						for (p = 0; p < net->EntryN; p++) {
+							LibraryEntryTypePtr entry = &net->Entry[p];
+							cb(ctx, PCB_RPE_INFO_TERMINAL, n->arg1.net_name, NULL, entry->ListEntry);
+						}
+						cb(ctx, PCB_RPE_INFO_END, n->arg1.net_name, NULL, NULL);
+
+					}
+				}
+			case RATP_CHANGE_ATTRIB:
+				break;
+			}
+		}
+		htsp_free(seen);
+	}
+
+	/* action lines */
+	for (n = pat; n != NULL; n = n->next) {
+		switch (n->op) {
+		case RATP_ADD_CONN:
+			cb(ctx, PCB_RPE_CONN_ADD, n->arg1.net_name, NULL, n->id);
+			break;
+		case RATP_DEL_CONN:
+			cb(ctx, PCB_RPE_CONN_DEL, n->arg1.net_name, NULL, n->id);
+			break;
+		case RATP_CHANGE_ATTRIB:
+			cb(ctx, PCB_RPE_ATTR_CHG, n->id, n->arg1.attrib_name, n->arg2.attrib_val);
+			break;
+		}
+	}
+	return 0;
+}
+
+typedef struct {
+	FILE *f;
+	const char *q, *po, *pc, *line_prefix;
+} fexport_t;
+
+static void fexport_cb(void *ctx_, pcb_rats_patch_export_ev_t ev, const char *netn, const char *key, const char *val)
+{
+	fexport_t *ctx = ctx_;
+	switch(ev) {
+		case PCB_RPE_INFO_BEGIN:     fprintf(ctx->f, "%snet_info%s%s%s%s", ctx->line_prefix, ctx->po, ctx->q, netn, ctx->q); break;
+		case PCB_RPE_INFO_TERMINAL:  fprintf(ctx->f, " %s%s%s", ctx->q, val, ctx->q); break;
+		case PCB_RPE_INFO_END:       fprintf(ctx->f, "%s\n", ctx->pc); break;
+		case PCB_RPE_CONN_ADD:       fprintf(ctx->f, "%sadd_conn%s%s%s%s %s%s%s%s\n", ctx->line_prefix, ctx->po, ctx->q, val, ctx->q, ctx->q, netn, ctx->q, ctx->pc); break;
+		case PCB_RPE_CONN_DEL:       fprintf(ctx->f, "%sdel_conn%s%s%s%s %s%s%s%s\n", ctx->line_prefix, ctx->po, ctx->q, val, ctx->q, ctx->q, netn, ctx->q, ctx->pc); break;
+		case PCB_RPE_ATTR_CHG:
+			fprintf(ctx->f, "%schange_attrib%s%s%s%s %s%s%s %s%s%s%s\n",
+				ctx->line_prefix, ctx->po,
+				ctx->q, netn, ctx->q,
+				ctx->q, key, ctx->q,
+				ctx->q, val, ctx->q,
+				ctx->pc);
+	}
+}
+
+int rats_patch_fexport(PCBTypePtr pcb, FILE *f, int fmt_pcb)
+{
+	fexport_t ctx;
+	if (fmt_pcb) {
+		ctx.q = "\"";
+		ctx.po = "(";
+		ctx.pc = ")";
+		ctx.line_prefix = "\t";
+	}
+	else {
+		ctx.q = "";
+		ctx.po = " ";
+		ctx.pc = "";
+		ctx.line_prefix = "";
+	}
+	ctx.f = f;
+	return rats_patch_export(pcb, pcb->NetlistPatches, !fmt_pcb, fexport_cb, &ctx);
+}
+
+
+/* ---------------------------------------------------------------- */
+static const char replacefootprint_syntax[] = "ReplaceFootprint()\n";
+
+static const char replacefootprint_help[] = "Replace the footprint of the selected components with the footprint specified.";
+
+static int ActionReplaceFootprint(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *a[4];
+	const char *fpname;
+	int found = 0;
+
+	/* check if we have elements selected and quit if not */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, element)) {
+			found = 1;
+			break;
+		}
+	}
+	END_LOOP;
+
+	if (!(found)) {
+		Message(PCB_MSG_DEFAULT, "ReplaceFootprint works on selected elements, please select elements first!\n");
+		return 1;
+	}
+
+	/* fetch the name of the new footprint */
+	if (argc == 0) {
+		fpname = gui->prompt_for("Footprint name", "");
+		if (fpname == NULL) {
+			Message(PCB_MSG_DEFAULT, "No footprint name supplied\n");
+			return 1;
+		}
+	}
+	else
+		fpname = argv[0];
+
+	/* check if the footprint is available */
+	a[0] = fpname;
+	a[1] = NULL;
+	if (LoadFootprint(1, a, x, y) != 0) {
+		Message(PCB_MSG_DEFAULT, "Can't load footprint %s\n", fpname);
+		return 1;
+	}
+
+
+	/* action: replace selected elements */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, element)) {
+			a[0] = fpname;
+			a[1] = element->Name[1].TextString;
+			a[2] = element->Name[2].TextString;
+			a[3] = NULL;
+			LoadFootprint(3, a, element->MarkX, element->MarkY);
+			CopyPastebufferToLayout(element->MarkX, element->MarkY);
+			rats_patch_append_optimize(PCB, RATP_CHANGE_ATTRIB, a[1], "footprint", fpname);
+			RemoveElement(element);
+		}
+	}
+	END_LOOP;
+	return 0;
+}
+
+static const char savepatch_syntax[] = "SavePatch(filename)";
+
+static const char savepatch_help[] = "Save netlist patch for back annotation.";
+
+/* %start-doc actions SavePatch
+Save netlist patch for back annotation.
+%end-doc */
+static int ActionSavePatch(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *fn;
+	FILE *f;
+
+	if (argc < 1) {
+		char *default_file;
+
+		if (PCB->Filename != NULL) {
+			char *end;
+			int len;
+			len = strlen(PCB->Filename);
+			default_file = malloc(len + 8);
+			memcpy(default_file, PCB->Filename, len + 1);
+			end = strrchr(default_file, '.');
+			if ((end == NULL) || (strcasecmp(end, ".pcb") != 0))
+				end = default_file + len;
+			strcpy(end, ".bap");
+		}
+		else
+			default_file = pcb_strdup("unnamed.bap");
+
+		fn = gui->fileselect(_("Save netlist patch as ..."),
+												 _("Choose a file to save netlist patch to\n"
+													 "for back annotation\n"), default_file, ".bap", "patch", 0);
+
+		free(default_file);
+	}
+	else
+		fn = argv[0];
+	f = fopen(fn, "w");
+	if (f == NULL) {
+		Message(PCB_MSG_DEFAULT, "Can't open netlist patch file %s for writing\n", fn);
+		return 1;
+	}
+	rats_patch_fexport(PCB, f, 0);
+	fclose(f);
+	return 0;
+}
+
+HID_Action rats_patch_action_list[] = {
+	{"ReplaceFootprint", 0, ActionReplaceFootprint,
+	 replacefootprint_help, replacefootprint_syntax}
+	,
+	{"SavePatch", 0, ActionSavePatch,
+	 savepatch_help, savepatch_syntax}
+};
+
+REGISTER_ACTIONS(rats_patch_action_list, NULL)
diff --git a/src/rats_patch.h b/src/rats_patch.h
new file mode 100644
index 0000000..cb97fe3
--- /dev/null
+++ b/src/rats_patch.h
@@ -0,0 +1,76 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2013..2015 Tibor 'Igor2' Palinkas
+ * 
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Change History:
+ * Started 6/10/97
+ * Added support for minimum length rat lines 6/13/97
+ * rat lines to nearest line/via 8/29/98
+ * support for netlist window 10/24/98
+ */
+
+#include "global.h"
+
+/* Single-word netlist class names */
+const char *pcb_netlist_names[NUM_NETLISTS];
+
+/* Allocate and append a patch line to the patch list */
+void rats_patch_append(PCBTypePtr pcb, rats_patch_op_t op, const char *id, const char *a1, const char *a2);
+
+/* Free the patch list and all memory claimed by patch list items */
+void rats_patch_destroy(PCBTypePtr pcb);
+
+/* Same as rats_patch_append() but also optimize previous entries so that
+   redundant entries are removed */
+void rats_patch_append_optimize(PCBTypePtr pcb, rats_patch_op_t op, const char *id, const char *a1, const char *a2);
+
+/* Create [NETLIST_EDITED] from [NETLIST_INPUT] applying the patch */
+void rats_patch_make_edited(PCBTypePtr pcb);
+
+/* apply a single patch record on [NETLIST_EDITED]; returns non-zero on failure */
+int rats_patch_apply(PCBTypePtr pcb, rats_patch_line_t * patch);
+
+/**** exporter ****/
+
+/* Special text exporter:
+   save all patch lines as an ordered list of text lines
+   if fmt is non-zero, generate pcb savefile compatible lines, else generate a back annotation patch */
+int rats_patch_fexport(PCBTypePtr pcb, FILE * f, int fmt_pcb);
+
+/* Generic, callback based exporter: */
+
+                          /* EVENT ARGUMENTS */
+typedef enum {            /* netn is always the net name, unless specified otherwise */
+	PCB_RPE_INFO_BEGIN,     /* netn */
+	PCB_RPE_INFO_TERMINAL,  /* netn; val is the terminal pin/pad name */
+	PCB_RPE_INFO_END,       /* netn */
+
+	PCB_RPE_CONN_ADD,       /* netn; val is the terminal pin/pad name */
+	PCB_RPE_CONN_DEL,       /* netn; val is the terminal pin/pad name */
+	PCB_RPE_ATTR_CHG        /* netn; key is the attribute name, val is the new attribute value */
+} pcb_rats_patch_export_ev_t;
+
+/* Call cb() for each item to output; PCB_PTRE_INFO* is calculated/called only
+   if need_info_lines is true; the pcb pointer is used for looking up connections */
+int rats_patch_export(PCBType *pcb, rats_patch_line_t *pat, pcb_bool need_info_lines, void (*cb)(void *ctx, pcb_rats_patch_export_ev_t ev, const char *netn, const char *key, const char *val), void *ctx);
diff --git a/src/remove.c b/src/remove.c
new file mode 100644
index 0000000..e81f9c2
--- /dev/null
+++ b/src/remove.c
@@ -0,0 +1,561 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* functions used to remove vias, pins ... */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <setjmp.h>
+#include <memory.h>
+
+#include "data.h"
+#include "draw.h"
+#include "misc.h"
+#include "move.h"
+#include "polygon.h"
+#include "remove.h"
+#include "rtree.h"
+#include "select.h"
+#include "undo.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *DestroyVia(PinTypePtr);
+static void *DestroyRat(RatTypePtr);
+static void *DestroyLine(LayerTypePtr, LineTypePtr);
+static void *DestroyArc(LayerTypePtr, ArcTypePtr);
+static void *DestroyText(LayerTypePtr, TextTypePtr);
+static void *DestroyPolygon(LayerTypePtr, PolygonTypePtr);
+static void *DestroyElement(ElementTypePtr);
+static void *RemoveVia(PinTypePtr);
+static void *RemoveRat(RatTypePtr);
+static void *DestroyPolygonPoint(LayerTypePtr, PolygonTypePtr, PointTypePtr);
+static void *RemovePolygonContour(LayerTypePtr, PolygonTypePtr, pcb_cardinal_t);
+static void *RemovePolygonPoint(LayerTypePtr, PolygonTypePtr, PointTypePtr);
+static void *RemoveLinePoint(LayerTypePtr, LineTypePtr, PointTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+static ObjectFunctionType RemoveFunctions = {
+	RemoveLine,
+	RemoveText,
+	RemovePolygon,
+	RemoveVia,
+	RemoveElement,
+	NULL,
+	NULL,
+	NULL,
+	RemoveLinePoint,
+	RemovePolygonPoint,
+	RemoveArc,
+	RemoveRat
+};
+
+static ObjectFunctionType DestroyFunctions = {
+	DestroyLine,
+	DestroyText,
+	DestroyPolygon,
+	DestroyVia,
+	DestroyElement,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	DestroyPolygonPoint,
+	DestroyArc,
+	DestroyRat
+};
+
+static DataTypePtr DestroyTarget;
+static pcb_bool Bulk = pcb_false;
+
+/* ---------------------------------------------------------------------------
+ * remove PCB
+ */
+void RemovePCB(PCBTypePtr Ptr)
+{
+	ClearUndoList(pcb_true);
+	FreePCBMemory(Ptr);
+	free(Ptr);
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys a via
+ */
+static void *DestroyVia(PinTypePtr Via)
+{
+	r_delete_entry(DestroyTarget->via_tree, (BoxTypePtr) Via);
+	free(Via->Name);
+
+	RemoveFreeVia(Via);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys a line from a layer
+ */
+static void *DestroyLine(LayerTypePtr Layer, LineTypePtr Line)
+{
+	r_delete_entry(Layer->line_tree, (BoxTypePtr) Line);
+	free(Line->Number);
+
+	RemoveFreeLine(Line);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys an arc from a layer
+ */
+static void *DestroyArc(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc);
+
+	RemoveFreeArc(Arc);
+
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys a polygon from a layer
+ */
+static void *DestroyPolygon(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	r_delete_entry(Layer->polygon_tree, (BoxTypePtr) Polygon);
+	FreePolygonMemory(Polygon);
+
+	RemoveFreePolygon(Polygon);
+
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a polygon-point from a polygon and destroys the data
+ */
+static void *DestroyPolygonPoint(LayerTypePtr Layer, PolygonTypePtr Polygon, PointTypePtr Point)
+{
+	pcb_cardinal_t point_idx;
+	pcb_cardinal_t i;
+	pcb_cardinal_t contour;
+	pcb_cardinal_t contour_start, contour_end, contour_points;
+
+	point_idx = polygon_point_idx(Polygon, Point);
+	contour = polygon_point_contour(Polygon, point_idx);
+	contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1];
+	contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN : Polygon->HoleIndex[contour];
+	contour_points = contour_end - contour_start;
+
+	if (contour_points <= 3)
+		return RemovePolygonContour(Layer, Polygon, contour);
+
+	r_delete_entry(Layer->polygon_tree, (BoxType *) Polygon);
+
+	/* remove point from list, keep point order */
+	for (i = point_idx; i < Polygon->PointN - 1; i++)
+		Polygon->Points[i] = Polygon->Points[i + 1];
+	Polygon->PointN--;
+
+	/* Shift down indices of any holes */
+	for (i = 0; i < Polygon->HoleIndexN; i++)
+		if (Polygon->HoleIndex[i] > point_idx)
+			Polygon->HoleIndex[i]--;
+
+	SetPolygonBoundingBox(Polygon);
+	r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+	InitClip(PCB->Data, Layer, Polygon);
+	return (Polygon);
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys a text from a layer
+ */
+static void *DestroyText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	free(Text->TextString);
+	r_delete_entry(Layer->text_tree, (BoxTypePtr) Text);
+
+	RemoveFreeText(Text);
+
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys a element
+ */
+static void *DestroyElement(ElementTypePtr Element)
+{
+	if (DestroyTarget->element_tree)
+		r_delete_entry(DestroyTarget->element_tree, (BoxType *) Element);
+	if (DestroyTarget->pin_tree) {
+		PIN_LOOP(Element);
+		{
+			r_delete_entry(DestroyTarget->pin_tree, (BoxType *) pin);
+		}
+		END_LOOP;
+	}
+	if (DestroyTarget->pad_tree) {
+		PAD_LOOP(Element);
+		{
+			r_delete_entry(DestroyTarget->pad_tree, (BoxType *) pad);
+		}
+		END_LOOP;
+	}
+	ELEMENTTEXT_LOOP(Element);
+	{
+		if (DestroyTarget->name_tree[n])
+			r_delete_entry(DestroyTarget->name_tree[n], (BoxType *) text);
+	}
+	END_LOOP;
+	FreeElementMemory(Element);
+
+	RemoveFreeElement(Element);
+
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * destroys a rat
+ */
+static void *DestroyRat(RatTypePtr Rat)
+{
+	if (DestroyTarget->rat_tree)
+		r_delete_entry(DestroyTarget->rat_tree, &Rat->BoundingBox);
+
+	RemoveFreeRat(Rat);
+	return NULL;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * removes a via
+ */
+static void *RemoveVia(PinTypePtr Via)
+{
+	/* erase from screen and memory */
+	if (PCB->ViaOn) {
+		EraseVia(Via);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_VIA, Via, Via, Via);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a rat
+ */
+static void *RemoveRat(RatTypePtr Rat)
+{
+	/* erase from screen and memory */
+	if (PCB->RatOn) {
+		EraseRat(Rat);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_RATLINE, Rat, Rat, Rat);
+	return NULL;
+}
+
+struct rlp_info {
+	jmp_buf env;
+	LineTypePtr line;
+	PointTypePtr point;
+};
+static r_dir_t remove_point(const BoxType * b, void *cl)
+{
+	LineType *line = (LineType *) b;
+	struct rlp_info *info = (struct rlp_info *) cl;
+	if (line == info->line)
+		return R_DIR_NOT_FOUND;
+	if ((line->Point1.X == info->point->X)
+			&& (line->Point1.Y == info->point->Y)) {
+		info->line = line;
+		info->point = &line->Point1;
+		longjmp(info->env, 1);
+	}
+	else if ((line->Point2.X == info->point->X)
+					 && (line->Point2.Y == info->point->Y)) {
+		info->line = line;
+		info->point = &line->Point2;
+		longjmp(info->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a line point, or a line if the selected point is the end
+ */
+static void *RemoveLinePoint(LayerTypePtr Layer, LineTypePtr Line, PointTypePtr Point)
+{
+	PointType other;
+	struct rlp_info info;
+	if (&Line->Point1 == Point)
+		other = Line->Point2;
+	else
+		other = Line->Point1;
+	info.line = Line;
+	info.point = Point;
+	if (setjmp(info.env) == 0) {
+		r_search(Layer->line_tree, (const BoxType *) Point, NULL, remove_point, &info, NULL);
+		return RemoveLine(Layer, Line);
+	}
+	MoveObject(PCB_TYPE_LINE_POINT, Layer, info.line, info.point, other.X - Point->X, other.Y - Point->Y);
+	return (RemoveLine(Layer, Line));
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a line from a layer
+ */
+void *RemoveLine(LayerTypePtr Layer, LineTypePtr Line)
+{
+	/* erase from screen */
+	if (Layer->On) {
+		EraseLine(Line);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_LINE, Layer, Line, Line);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes an arc from a layer
+ */
+void *RemoveArc(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	/* erase from screen */
+	if (Layer->On) {
+		EraseArc(Arc);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_ARC, Layer, Arc, Arc);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a text from a layer
+ */
+void *RemoveText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	/* erase from screen */
+	if (Layer->On) {
+		EraseText(Layer, Text);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_TEXT, Layer, Text, Text);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a polygon from a layer
+ */
+void *RemovePolygon(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	/* erase from screen */
+	if (Layer->On) {
+		ErasePolygon(Polygon);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_POLYGON, Layer, Polygon, Polygon);
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a contour from a polygon.
+ * If removing the outer contour, it removes the whole polygon.
+ */
+static void *RemovePolygonContour(LayerTypePtr Layer, PolygonTypePtr Polygon, pcb_cardinal_t contour)
+{
+	pcb_cardinal_t contour_start, contour_end, contour_points;
+	pcb_cardinal_t i;
+
+	if (contour == 0)
+		return RemovePolygon(Layer, Polygon);
+
+	if (Layer->On) {
+		ErasePolygon(Polygon);
+		if (!Bulk)
+			Draw();
+	}
+
+	/* Copy the polygon to the undo list */
+	AddObjectToRemoveContourUndoList(PCB_TYPE_POLYGON, Layer, Polygon);
+
+	contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1];
+	contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN : Polygon->HoleIndex[contour];
+	contour_points = contour_end - contour_start;
+
+	/* remove points from list, keep point order */
+	for (i = contour_start; i < Polygon->PointN - contour_points; i++)
+		Polygon->Points[i] = Polygon->Points[i + contour_points];
+	Polygon->PointN -= contour_points;
+
+	/* remove hole from list and shift down remaining indices */
+	for (i = contour; i < Polygon->HoleIndexN; i++)
+		Polygon->HoleIndex[i - 1] = Polygon->HoleIndex[i] - contour_points;
+	Polygon->HoleIndexN--;
+
+	InitClip(PCB->Data, Layer, Polygon);
+	/* redraw polygon if necessary */
+	if (Layer->On) {
+		DrawPolygon(Layer, Polygon);
+		if (!Bulk)
+			Draw();
+	}
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes a polygon-point from a polygon
+ */
+static void *RemovePolygonPoint(LayerTypePtr Layer, PolygonTypePtr Polygon, PointTypePtr Point)
+{
+	pcb_cardinal_t point_idx;
+	pcb_cardinal_t i;
+	pcb_cardinal_t contour;
+	pcb_cardinal_t contour_start, contour_end, contour_points;
+
+	point_idx = polygon_point_idx(Polygon, Point);
+	contour = polygon_point_contour(Polygon, point_idx);
+	contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1];
+	contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN : Polygon->HoleIndex[contour];
+	contour_points = contour_end - contour_start;
+
+	if (contour_points <= 3)
+		return RemovePolygonContour(Layer, Polygon, contour);
+
+	if (Layer->On)
+		ErasePolygon(Polygon);
+
+	/* insert the polygon-point into the undo list */
+	AddObjectToRemovePointUndoList(PCB_TYPE_POLYGON_POINT, Layer, Polygon, point_idx);
+	r_delete_entry(Layer->polygon_tree, (BoxType *) Polygon);
+
+	/* remove point from list, keep point order */
+	for (i = point_idx; i < Polygon->PointN - 1; i++)
+		Polygon->Points[i] = Polygon->Points[i + 1];
+	Polygon->PointN--;
+
+	/* Shift down indices of any holes */
+	for (i = 0; i < Polygon->HoleIndexN; i++)
+		if (Polygon->HoleIndex[i] > point_idx)
+			Polygon->HoleIndex[i]--;
+
+	SetPolygonBoundingBox(Polygon);
+	r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+	RemoveExcessPolygonPoints(Layer, Polygon);
+	InitClip(PCB->Data, Layer, Polygon);
+
+	/* redraw polygon if necessary */
+	if (Layer->On) {
+		DrawPolygon(Layer, Polygon);
+		if (!Bulk)
+			Draw();
+	}
+	return NULL;
+}
+
+/* ---------------------------------------------------------------------------
+ * removes an element
+ */
+void *RemoveElement(ElementTypePtr Element)
+{
+	/* erase from screen */
+	if ((PCB->ElementOn || PCB->PinOn) && (FRONT(Element) || PCB->InvisibleObjectsOn)) {
+		EraseElement(Element);
+		if (!Bulk)
+			Draw();
+	}
+	MoveObjectToRemoveUndoList(PCB_TYPE_ELEMENT, Element, Element, Element);
+	return NULL;
+}
+
+/* ----------------------------------------------------------------------
+ * removes all selected and visible objects
+ * returns pcb_true if any objects have been removed
+ */
+pcb_bool RemoveSelected(void)
+{
+	Bulk = pcb_true;
+	if (SelectedOperation(&RemoveFunctions, pcb_false, PCB_TYPEMASK_ALL)) {
+		IncrementUndoSerialNumber();
+		Draw();
+		Bulk = pcb_false;
+		return (pcb_true);
+	}
+	Bulk = pcb_false;
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * remove object as referred by pointers and type,
+ * allocated memory is passed to the 'remove undo' list
+ */
+void *RemoveObject(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	void *ptr = ObjectOperation(&RemoveFunctions, Type, Ptr1, Ptr2, Ptr3);
+	return (ptr);
+}
+
+/* ---------------------------------------------------------------------------
+ * DeleteRats - deletes rat lines only
+ * can delete all rat lines, or only selected one
+ */
+
+pcb_bool DeleteRats(pcb_bool selected)
+{
+	pcb_bool changed = pcb_false;
+	Bulk = pcb_true;
+	RAT_LOOP(PCB->Data);
+	{
+		if ((!selected) || TEST_FLAG(PCB_FLAG_SELECTED, line)) {
+			changed = pcb_true;
+			RemoveRat(line);
+		}
+	}
+	END_LOOP;
+	Bulk = pcb_false;
+	if (changed) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (changed);
+}
+
+/* ---------------------------------------------------------------------------
+ * remove object as referred by pointers and type
+ * allocated memory is destroyed assumed to already be erased
+ */
+void *DestroyObject(DataTypePtr Target, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	DestroyTarget = Target;
+	return (ObjectOperation(&DestroyFunctions, Type, Ptr1, Ptr2, Ptr3));
+}
diff --git a/src/remove.h b/src/remove.h
new file mode 100644
index 0000000..5d04d85
--- /dev/null
+++ b/src/remove.h
@@ -0,0 +1,53 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for remove routines */
+
+#ifndef	PCB_REMOVE_H
+#define	PCB_REMOVE_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some constants
+ */
+#define REMOVE_TYPES            \
+	(PCB_TYPE_VIA | PCB_TYPE_LINE_POINT | PCB_TYPE_LINE | PCB_TYPE_TEXT | PCB_TYPE_ELEMENT |	\
+	PCB_TYPE_POLYGON_POINT | PCB_TYPE_POLYGON | PCB_TYPE_RATLINE | PCB_TYPE_ARC)
+
+void *RemoveLine(LayerTypePtr, LineTypePtr);
+void *RemoveArc(LayerTypePtr, ArcTypePtr);
+void *RemovePolygon(LayerTypePtr, PolygonTypePtr);
+void *RemoveText(LayerTypePtr, TextTypePtr);
+void *RemoveElement(ElementTypePtr);
+void ClearRemoveList(void);
+void RemovePCB(PCBTypePtr);
+pcb_bool RemoveSelected(void);
+pcb_bool DeleteRats(pcb_bool);
+void *RemoveObject(int, void *, void *, void *);
+void *DestroyObject(DataTypePtr, int, void *, void *, void *);
+
+#endif
diff --git a/src/remove_act.c b/src/remove_act.c
new file mode 100644
index 0000000..c0657d2
--- /dev/null
+++ b/src/remove_act.c
@@ -0,0 +1,107 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "data.h"
+#include "action_helper.h"
+#include "set.h"
+#include "remove.h"
+#include "funchash_core.h"
+
+/* --------------------------------------------------------------------------- */
+
+static const char delete_syntax[] = "Delete(Object|Selected)\n" "Delete(AllRats|SelectedRats)";
+
+static const char delete_help[] = "Delete stuff.";
+
+/* %start-doc actions Delete
+
+%end-doc */
+
+static int ActionDelete(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	int id = funchash_get(function, NULL);
+
+	Note.X = Crosshair.X;
+	Note.Y = Crosshair.Y;
+
+	if (id == -1) {								/* no arg */
+		if (RemoveSelected() == pcb_false)
+			id = F_Object;
+	}
+
+	switch (id) {
+	case F_Object:
+		SaveMode();
+		SetMode(PCB_MODE_REMOVE);
+		NotifyMode();
+		RestoreMode();
+		break;
+	case F_Selected:
+		RemoveSelected();
+		break;
+	case F_AllRats:
+		if (DeleteRats(pcb_false))
+			SetChangedFlag(pcb_true);
+		break;
+	case F_SelectedRats:
+		if (DeleteRats(pcb_true))
+			SetChangedFlag(pcb_true);
+		break;
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char removeselected_syntax[] = "RemoveSelected()";
+
+static const char removeselected_help[] = "Removes any selected objects.";
+
+/* %start-doc actions RemoveSelected
+
+%end-doc */
+
+static int ActionRemoveSelected(int argc, const char **argv, Coord x, Coord y)
+{
+	if (RemoveSelected())
+		SetChangedFlag(pcb_true);
+	return 0;
+}
+
+
+HID_Action remove_action_list[] = {
+	{"Delete", 0, ActionDelete,
+	 delete_help, delete_syntax}
+	,
+	{"RemoveSelected", 0, ActionRemoveSelected,
+	 removeselected_help, removeselected_syntax}
+	,
+};
+
+REGISTER_ACTIONS(remove_action_list, NULL)
diff --git a/src/rotate.c b/src/rotate.c
new file mode 100644
index 0000000..a54e51d
--- /dev/null
+++ b/src/rotate.c
@@ -0,0 +1,389 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* functions used to rotate pins, elements ...
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "misc.h"
+#include "polygon.h"
+#include "rotate.h"
+#include "rtree.h"
+#include "rubberband.h"
+#include "search.h"
+#include "select.h"
+#include "set.h"
+#include "undo.h"
+#include "conf_core.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void *RotateText(LayerTypePtr, TextTypePtr);
+static void *RotateArc(LayerTypePtr, ArcTypePtr);
+static void *RotateElement(ElementTypePtr);
+static void *RotateElementName(ElementTypePtr);
+static void *RotateLinePoint(LayerTypePtr, LineTypePtr, PointTypePtr);
+
+/* ----------------------------------------------------------------------
+ * some local identifiers
+ */
+static Coord CenterX, CenterY;	/* center of rotation */
+static unsigned Number;					/* number of rotations */
+static ObjectFunctionType RotateFunctions = {
+	NULL,
+	RotateText,
+	NULL,
+	NULL,
+	RotateElement,
+	RotateElementName,
+	NULL,
+	NULL,
+	RotateLinePoint,
+	NULL,
+	RotateArc,
+	NULL
+};
+
+/* ---------------------------------------------------------------------------
+ * rotates a point in 90 degree steps
+ */
+void RotatePointLowLevel(PointTypePtr Point, Coord X, Coord Y, unsigned Number)
+{
+	ROTATE(Point->X, Point->Y, X, Y, Number);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates a line in 90 degree steps
+ */
+void RotateLineLowLevel(LineTypePtr Line, Coord X, Coord Y, unsigned Number)
+{
+	ROTATE(Line->Point1.X, Line->Point1.Y, X, Y, Number);
+	ROTATE(Line->Point2.X, Line->Point2.Y, X, Y, Number);
+	/* keep horizontal, vertical Point2 > Point1 */
+	if (Line->Point1.X == Line->Point2.X) {
+		if (Line->Point1.Y > Line->Point2.Y) {
+			Coord t;
+			t = Line->Point1.Y;
+			Line->Point1.Y = Line->Point2.Y;
+			Line->Point2.Y = t;
+		}
+	}
+	else if (Line->Point1.Y == Line->Point2.Y) {
+		if (Line->Point1.X > Line->Point2.X) {
+			Coord t;
+			t = Line->Point1.X;
+			Line->Point1.X = Line->Point2.X;
+			Line->Point2.X = t;
+		}
+	}
+	/* instead of rotating the bounding box, the call updates both end points too */
+	SetLineBoundingBox(Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates a text in 90 degree steps
+ * only the bounding box is rotated, text rotation itself
+ * is done by the drawing routines
+ */
+void RotateTextLowLevel(TextTypePtr Text, Coord X, Coord Y, unsigned Number)
+{
+	pcb_uint8_t number;
+
+	number = TEST_FLAG(PCB_FLAG_ONSOLDER, Text) ? (4 - Number) & 3 : Number;
+	RotateBoxLowLevel(&Text->BoundingBox, X, Y, Number);
+	ROTATE(Text->X, Text->Y, X, Y, Number);
+
+	/* set new direction, 0..3,
+	 * 0-> to the right, 1-> straight up,
+	 * 2-> to the left, 3-> straight down
+	 */
+	Text->Direction = ((Text->Direction + number) & 0x03);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates a polygon in 90 degree steps
+ */
+void RotatePolygonLowLevel(PolygonTypePtr Polygon, Coord X, Coord Y, unsigned Number)
+{
+	POLYGONPOINT_LOOP(Polygon);
+	{
+		ROTATE(point->X, point->Y, X, Y, Number);
+	}
+	END_LOOP;
+	RotateBoxLowLevel(&Polygon->BoundingBox, X, Y, Number);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates a text object and redraws it
+ */
+static void *RotateText(LayerTypePtr Layer, TextTypePtr Text)
+{
+	EraseText(Layer, Text);
+	RestoreToPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	r_delete_entry(Layer->text_tree, (BoxTypePtr) Text);
+	RotateTextLowLevel(Text, CenterX, CenterY, Number);
+	r_insert_entry(Layer->text_tree, (BoxTypePtr) Text, 0);
+	ClearFromPolygon(PCB->Data, PCB_TYPE_TEXT, Layer, Text);
+	DrawText(Layer, Text);
+	Draw();
+	return (Text);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates an arc
+ */
+void RotateArcLowLevel(ArcTypePtr Arc, Coord X, Coord Y, unsigned Number)
+{
+	Coord save;
+
+	/* add Number*90 degrees (i.e., Number quarter-turns) */
+	Arc->StartAngle = NormalizeAngle(Arc->StartAngle + Number * 90);
+	ROTATE(Arc->X, Arc->Y, X, Y, Number);
+
+	/* now change width and height */
+	if (Number == 1 || Number == 3) {
+		save = Arc->Width;
+		Arc->Width = Arc->Height;
+		Arc->Height = save;
+	}
+	RotateBoxLowLevel(&Arc->BoundingBox, X, Y, Number);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotate an element in 90 degree steps
+ */
+void RotateElementLowLevel(DataTypePtr Data, ElementTypePtr Element, Coord X, Coord Y, unsigned Number)
+{
+	/* solder side objects need a different orientation */
+
+	/* the text subroutine decides by itself if the direction
+	 * is to be corrected
+	 */
+	ELEMENTTEXT_LOOP(Element);
+	{
+		if (Data && Data->name_tree[n])
+			r_delete_entry(Data->name_tree[n], (BoxType *) text);
+		RotateTextLowLevel(text, X, Y, Number);
+	}
+	END_LOOP;
+	ELEMENTLINE_LOOP(Element);
+	{
+		RotateLineLowLevel(line, X, Y, Number);
+	}
+	END_LOOP;
+	PIN_LOOP(Element);
+	{
+		/* pre-delete the pins from the pin-tree before their coordinates change */
+		if (Data)
+			r_delete_entry(Data->pin_tree, (BoxType *) pin);
+		RestoreToPolygon(Data, PCB_TYPE_PIN, Element, pin);
+		ROTATE_PIN_LOWLEVEL(pin, X, Y, Number);
+	}
+	END_LOOP;
+	PAD_LOOP(Element);
+	{
+		/* pre-delete the pads before their coordinates change */
+		if (Data)
+			r_delete_entry(Data->pad_tree, (BoxType *) pad);
+		RestoreToPolygon(Data, PCB_TYPE_PAD, Element, pad);
+		ROTATE_PAD_LOWLEVEL(pad, X, Y, Number);
+	}
+	END_LOOP;
+	ARC_LOOP(Element);
+	{
+		RotateArcLowLevel(arc, X, Y, Number);
+	}
+	END_LOOP;
+	ROTATE(Element->MarkX, Element->MarkY, X, Y, Number);
+	/* SetElementBoundingBox reenters the rtree data */
+	SetElementBoundingBox(Data, Element, &PCB->Font);
+	ClearFromPolygon(Data, PCB_TYPE_ELEMENT, Element, Element);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates a line's point
+ */
+static void *RotateLinePoint(LayerTypePtr Layer, LineTypePtr Line, PointTypePtr Point)
+{
+	EraseLine(Line);
+	if (Layer) {
+		RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		r_delete_entry(Layer->line_tree, (BoxTypePtr) Line);
+	}
+	else
+		r_delete_entry(PCB->Data->rat_tree, (BoxTypePtr) Line);
+	RotatePointLowLevel(Point, CenterX, CenterY, Number);
+	SetLineBoundingBox(Line);
+	if (Layer) {
+		r_insert_entry(Layer->line_tree, (BoxTypePtr) Line, 0);
+		ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, Layer, Line);
+		DrawLine(Layer, Line);
+	}
+	else {
+		r_insert_entry(PCB->Data->rat_tree, (BoxTypePtr) Line, 0);
+		DrawRat((RatTypePtr) Line);
+	}
+	Draw();
+	return (Line);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates an arc
+ */
+static void *RotateArc(LayerTypePtr Layer, ArcTypePtr Arc)
+{
+	EraseArc(Arc);
+	r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc);
+	RotateArcLowLevel(Arc, CenterX, CenterY, Number);
+	r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0);
+	DrawArc(Layer, Arc);
+	Draw();
+	return (Arc);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates an element
+ */
+static void *RotateElement(ElementTypePtr Element)
+{
+	EraseElement(Element);
+	RotateElementLowLevel(PCB->Data, Element, CenterX, CenterY, Number);
+	DrawElement(Element);
+	Draw();
+	return (Element);
+}
+
+/* ----------------------------------------------------------------------
+ * rotates the name of an element
+ */
+static void *RotateElementName(ElementTypePtr Element)
+{
+	EraseElementName(Element);
+	ELEMENTTEXT_LOOP(Element);
+	{
+		r_delete_entry(PCB->Data->name_tree[n], (BoxType *) text);
+		RotateTextLowLevel(text, CenterX, CenterY, Number);
+		r_insert_entry(PCB->Data->name_tree[n], (BoxType *) text, 0);
+	}
+	END_LOOP;
+	DrawElementName(Element);
+	Draw();
+	return (Element);
+}
+
+/* ---------------------------------------------------------------------------
+ * rotates a box in 90 degree steps
+ */
+void RotateBoxLowLevel(BoxTypePtr Box, Coord X, Coord Y, unsigned Number)
+{
+	Coord x1 = Box->X1, y1 = Box->Y1, x2 = Box->X2, y2 = Box->Y2;
+
+	ROTATE(x1, y1, X, Y, Number);
+	ROTATE(x2, y2, X, Y, Number);
+	Box->X1 = MIN(x1, x2);
+	Box->Y1 = MIN(y1, y2);
+	Box->X2 = MAX(x1, x2);
+	Box->Y2 = MAX(y1, y2);
+}
+
+/* ----------------------------------------------------------------------
+ * rotates an objects at the cursor position as identified by its ID
+ * the center of rotation is determined by the current cursor location
+ */
+void *RotateObject(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord X, Coord Y, unsigned Steps)
+{
+	RubberbandTypePtr ptr;
+	void *ptr2;
+	pcb_bool changed = pcb_false;
+
+	/* setup default  global identifiers */
+	Number = Steps;
+	CenterX = X;
+	CenterY = Y;
+
+	/* move all the rubberband lines... and reset the counter */
+	ptr = Crosshair.AttachedObject.Rubberband;
+	while (Crosshair.AttachedObject.RubberbandN) {
+		changed = pcb_true;
+		CLEAR_FLAG(PCB_FLAG_RUBBEREND, ptr->Line);
+		AddObjectToRotateUndoList(PCB_TYPE_LINE_POINT, ptr->Layer, ptr->Line, ptr->MovedPoint, CenterX, CenterY, Steps);
+		EraseLine(ptr->Line);
+		if (ptr->Layer) {
+			RestoreToPolygon(PCB->Data, PCB_TYPE_LINE, ptr->Layer, ptr->Line);
+			r_delete_entry(ptr->Layer->line_tree, (BoxType *) ptr->Line);
+		}
+		else
+			r_delete_entry(PCB->Data->rat_tree, (BoxType *) ptr->Line);
+		RotatePointLowLevel(ptr->MovedPoint, CenterX, CenterY, Steps);
+		SetLineBoundingBox(ptr->Line);
+		if (ptr->Layer) {
+			r_insert_entry(ptr->Layer->line_tree, (BoxType *) ptr->Line, 0);
+			ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, ptr->Layer, ptr->Line);
+			DrawLine(ptr->Layer, ptr->Line);
+		}
+		else {
+			r_insert_entry(PCB->Data->rat_tree, (BoxType *) ptr->Line, 0);
+			DrawRat((RatTypePtr) ptr->Line);
+		}
+		Crosshair.AttachedObject.RubberbandN--;
+		ptr++;
+	}
+	AddObjectToRotateUndoList(Type, Ptr1, Ptr2, Ptr3, CenterX, CenterY, Number);
+	ptr2 = ObjectOperation(&RotateFunctions, Type, Ptr1, Ptr2, Ptr3);
+	changed |= (ptr2 != NULL);
+	if (changed) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+	return (ptr2);
+}
+
+void RotateScreenObject(Coord X, Coord Y, unsigned Steps)
+{
+	int type;
+	void *ptr1, *ptr2, *ptr3;
+	if ((type = SearchScreen(X, Y, ROTATE_TYPES, &ptr1, &ptr2, &ptr3)) != PCB_TYPE_NONE) {
+		if (TEST_FLAG(PCB_FLAG_LOCK, (ArcTypePtr) ptr2)) {
+			Message(PCB_MSG_DEFAULT, _("Sorry, the object is locked\n"));
+			return;
+		}
+		Crosshair.AttachedObject.RubberbandN = 0;
+		if (conf_core.editor.rubber_band_mode)
+			LookupRubberbandLines(type, ptr1, ptr2, ptr3);
+		if (type == PCB_TYPE_ELEMENT)
+			LookupRatLines(type, ptr1, ptr2, ptr3);
+		RotateObject(type, ptr1, ptr2, ptr3, X, Y, Steps);
+		SetChangedFlag(pcb_true);
+	}
+}
diff --git a/src/rotate.h b/src/rotate.h
new file mode 100644
index 0000000..5c75b3b
--- /dev/null
+++ b/src/rotate.h
@@ -0,0 +1,103 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for transform routines */
+
+#ifndef	PCB_ROTATE_H
+#define	PCB_ROTATE_H
+
+#include "global.h"
+
+/* ---------------------------------------------------------------------------
+ * some useful transformation macros and constants
+ */
+#define	ROTATE(x,y,x0,y0,n)							\
+	{												\
+		Coord	dx = (x)-(x0),					\
+					dy = (y)-(y0);					\
+													\
+		switch(n & 0x03)									\
+		{											\
+			case 1:		(x)=(x0)+dy; (y)=(y0)-dx;	\
+						break;						\
+			case 2:		(x)=(x0)-dx; (y)=(y0)-dy;	\
+						break;						\
+			case 3:		(x)=(x0)-dy; (y)=(y0)+dx;	\
+						break;						\
+			default:	break;						\
+		}											\
+	}
+
+/* Rotate pin shape style by n_in * 90 degrees */
+#define PIN_ROTATE(p,n_in) \
+do { \
+	int _n_; \
+	for(_n_ = n_in;_n_>0;_n_--) { \
+		int _old_, _nw_ = 0; \
+		_old_ = GET_SQUARE(p); \
+		if ((_old_ > 1) && (_old_ < 17)) { \
+			_old_--; \
+			if (_old_ & 1) \
+				_nw_ |= 8; \
+			if (_old_ & 8) \
+				_nw_ |= 2; \
+			if (_old_ & 2) \
+				_nw_ |= 4; \
+			if (_old_ & 4) \
+				_nw_ |= 1; \
+			GET_SQUARE(p) = _nw_+1; \
+		} \
+	} \
+} while(0)
+
+#define	ROTATE_VIA_LOWLEVEL(v,x0,y0,n)	\
+do { \
+	ROTATE((v)->X,(v)->Y,(x0),(y0),(n)); \
+	PIN_ROTATE(v, (n)); \
+} while(0)
+
+#define	ROTATE_PIN_LOWLEVEL(p,x0,y0,n)	\
+do { \
+	ROTATE((p)->X,(p)->Y,(x0),(y0),(n)); \
+	PIN_ROTATE((p), (n)); \
+} while(0)
+
+#define	ROTATE_PAD_LOWLEVEL(p,x0,y0,n)	\
+	RotateLineLowLevel(((LineTypePtr) (p)),(x0),(y0),(n))
+
+#define	ROTATE_TYPES	(PCB_TYPE_ELEMENT | PCB_TYPE_TEXT | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_ARC)
+
+
+void RotateLineLowLevel(LineTypePtr, Coord, Coord, unsigned);
+void RotateArcLowLevel(ArcTypePtr, Coord, Coord, unsigned);
+void RotateBoxLowLevel(BoxTypePtr, Coord, Coord, unsigned);
+void RotateTextLowLevel(TextTypePtr, Coord, Coord, unsigned);
+void RotatePolygonLowLevel(PolygonTypePtr, Coord, Coord, unsigned);
+void RotateElementLowLevel(DataTypePtr, ElementTypePtr, Coord, Coord, unsigned);
+void *RotateObject(int, void *, void *, void *, Coord, Coord, unsigned);
+void RotateScreenObject(Coord, Coord, unsigned);
+
+#endif
diff --git a/src/route_style.c b/src/route_style.c
new file mode 100644
index 0000000..8ea0b97
--- /dev/null
+++ b/src/route_style.c
@@ -0,0 +1,183 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+#include "config.h"
+#include "global.h"
+#include "pcb-printf.h"
+#include "genvector/gds_char.h"
+#include "route_style.h"
+#include "misc.h"
+#include "error.h"
+#include "conf.h"
+
+RouteStyleType pcb_custom_route_style;
+
+/*! \brief Serializes the route style list 
+ *  \par Function Description
+ *  Right now n_styles should always be set to NUM_STYLES,
+ *  since that is the number of route styles ParseRouteString()
+ *  expects to parse.
+ */
+char *make_route_string(vtroutestyle_t *styles)
+{
+	gds_t str;
+	int i;
+
+	gds_init(&str);
+	for (i = 0; i < vtroutestyle_len(styles); ++i) {
+		pcb_append_printf(&str, "%s,%mc,%mc,%mc,%mc", styles->array[i].name,
+																				 styles->array[i].Thick, styles->array[i].Diameter,
+																				 styles->array[i].Hole, styles->array[i].Clearance);
+		if (i > 0)
+			gds_append(&str, ':');
+	}
+	return str.array; /* this is the only allocation made, return this and don't uninit */
+}
+
+/* ----------------------------------------------------------------------
+ * parses the routes definition string which is a colon separated list of
+ * comma separated Name, Dimension, Dimension, Dimension, Dimension
+ * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
+ */
+int ParseRoutingString1(char **str, RouteStyleTypePtr routeStyle, const char *default_unit)
+{
+	char *s = *str;
+	char Name[256];
+	int i, len;
+
+	while (*s && isspace((int) *s))
+		s++;
+	for (i = 0; *s && *s != ','; i++)
+		Name[i] = *s++;
+	Name[i] = '\0';
+	len = strlen(Name);
+	if (len > sizeof(routeStyle->name)-1) {
+		memcpy(routeStyle->name, Name, sizeof(routeStyle->name)-1);
+		routeStyle->name[sizeof(routeStyle->name)-1] = '\0';
+		Message(PCB_MSG_DEFAULT, "Route style name '%s' too long, truncated to '%s'\n", Name, routeStyle->name);
+	}
+	else
+		strcpy(routeStyle->name, Name);
+	if (!isdigit((int) *++s))
+		goto error;
+	routeStyle->Thick = GetNum(&s, default_unit);
+	while (*s && isspace((int) *s))
+		s++;
+	if (*s++ != ',')
+		goto error;
+	while (*s && isspace((int) *s))
+		s++;
+	if (!isdigit((int) *s))
+		goto error;
+	routeStyle->Diameter = GetNum(&s, default_unit);
+	while (*s && isspace((int) *s))
+		s++;
+	if (*s++ != ',')
+		goto error;
+	while (*s && isspace((int) *s))
+		s++;
+	if (!isdigit((int) *s))
+		goto error;
+	routeStyle->Hole = GetNum(&s, default_unit);
+	/* for backwards-compatibility, we use a 10-mil default
+	 * for styles which omit the clearance specification. */
+	if (*s != ',')
+		routeStyle->Clearance = PCB_MIL_TO_COORD(10);
+	else {
+		s++;
+		while (*s && isspace((int) *s))
+			s++;
+		if (!isdigit((int) *s))
+			goto error;
+		routeStyle->Clearance = GetNum(&s, default_unit);
+		while (*s && isspace((int) *s))
+			s++;
+	}
+
+	*str = s;
+	return 0;
+	error:;
+		*str = s;
+		return -1;
+}
+
+int ParseRouteString(char *s, vtroutestyle_t *styles, const char *default_unit)
+{
+	int n;
+
+	vtroutestyle_truncate(styles, 0);
+	for(n = 0;;n++) {
+		vtroutestyle_enlarge(styles, n+1);
+		if (ParseRoutingString1(&s, &styles->array[n], default_unit) != 0) {
+			n--;
+			break;
+		}
+		while (*s && isspace((int) *s))
+			s++;
+		if (*s == '\0')
+			break;
+		if (*s++ != ':') {
+			vtroutestyle_truncate(styles, 0);
+			return -1;
+		}
+	}
+	vtroutestyle_truncate(styles, n+1);
+	return 0;
+}
+
+void pcb_use_route_style(RouteStyleType * rst)
+{
+	conf_set_design("design/line_thickness", "%$mS", rst->Thick);
+	conf_set_design("design/via_thickness", "%$mS", rst->Diameter);
+	conf_set_design("design/via_drilling_hole", "%$mS", rst->Hole);
+	conf_set_design("design/clearance", "%$mS", rst->Clearance);
+}
+
+int pcb_use_route_style_idx(vtroutestyle_t *styles, int idx)
+{
+	if ((idx < 0) || (idx >= vtroutestyle_len(styles)))
+		return -1;
+	pcb_use_route_style(styles->array+idx);
+	return 0;
+}
+
+#define cmp(a,b) (((a) != 0) && (coord_abs((a)-(b)) > 32))
+#define cmps(a,b) (((a) != NULL) && (strcmp((a), (b)) != 0))
+int pcb_route_style_lookup(vtroutestyle_t *styles, Coord Thick, Coord Diameter, Coord Hole, Coord Clearance, char *Name)
+{
+	int n;
+	for (n = 0; n < vtroutestyle_len(styles); n++) {
+		if (cmp(Thick, styles->array[n].Thick)) continue;
+		if (cmp(Diameter, styles->array[n].Diameter)) continue;
+		if (cmp(Hole, styles->array[n].Hole)) continue;
+		if (cmp(Clearance, styles->array[n].Clearance)) continue;
+		if (cmps(Name, styles->array[n].name)) continue;
+		return n;
+	}
+	return -1;
+}
+#undef cmp
+
diff --git a/src/route_style.h b/src/route_style.h
new file mode 100644
index 0000000..6657c03
--- /dev/null
+++ b/src/route_style.h
@@ -0,0 +1,50 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2004 Thomas Nau
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* Parse a single route string into one RouteStyleTypePtr slot. Returns 0 on success.  */
+int ParseRoutingString1(char **str, RouteStyleTypePtr routeStyle, const char *default_unit);
+
+/* Parse a ':' separated list of route strings into a styles vector
+   The vector is initialized before the call. On error the vector is left empty
+   (but still initialized). Returns 0 on success. */
+int ParseRouteString(char *s, vtroutestyle_t *styles, const char *default_unit);
+
+char *make_route_string(vtroutestyle_t *styles);
+
+/* Set design configuration (the pen we draw with) to a given route style */
+void pcb_use_route_style(RouteStyleType *rst);
+
+/* Same as pcb_use_route_style() but uses one of the styles in a vector;
+   returns -1 if idx is out of bounds, 0 on success. */
+int pcb_use_route_style_idx(vtroutestyle_t *styles, int idx);
+
+/* Compare supplied parameters to each style in the vector and return the index
+   of the first matching style. All non-0 parameters need to match to accept
+   a style. Return -1 on no match. */
+int pcb_route_style_lookup(vtroutestyle_t *styles, Coord Thick, Coord Diameter, Coord Hole, Coord Clearance, char *Name);
+
+extern RouteStyleType pcb_custom_route_style;
diff --git a/src/rtree.c b/src/rtree.c
new file mode 100644
index 0000000..308abd4
--- /dev/null
+++ b/src/rtree.c
@@ -0,0 +1,999 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001,2002,2003,2004 harry eaton
+ *
+ *  this file, rtree.c, was written and is
+ *  Copyright (c) 2004, harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* implements r-tree structures.
+ * these should be much faster for the auto-router
+ * because the recursive search is much more efficient
+ * and that's where the auto-router spends all its time.
+ */
+#include "config.h"
+
+#include <assert.h>
+#include <setjmp.h>
+
+#include "rtree.h"
+
+#define SLOW_ASSERTS
+/* All rectangles are closed on the bottom left and open on the
+ * top right. i.e. they contain one corner point, but not the other.
+ * This requires that the corner points not be equal!
+ */
+
+/* the number of entries in each rtree node
+ * 4 - 7 seem to be pretty good settings
+ */
+#define M_SIZE 6
+
+/* it seems that sorting the leaf order slows us down
+ * but sometimes gives better routes
+ */
+#undef SORT
+#define SORT_NONLEAF
+
+#define DELETE_BY_POINTER
+
+typedef struct {
+	const BoxType *bptr;					/* pointer to the box */
+	BoxType bounds;								/* copy of the box for locality of reference */
+} Rentry;
+
+struct rtree_node {
+	BoxType box;									/* bounds rectangle of this node */
+	struct rtree_node *parent;		/* parent of this node, NULL = root */
+	struct {
+		unsigned is_leaf:1;					/* this is a leaf node */
+		unsigned manage:31;					/* pcb_true==should free 'rect.bptr' if node is destroyed */
+	} flags;
+	union {
+		struct rtree_node *kids[M_SIZE + 1];	/* when not leaf */
+		Rentry rects[M_SIZE + 1];		/* when leaf */
+	} u;
+};
+
+#ifndef NDEBUG
+#ifdef SLOW_ASSERTS
+static int __r_node_is_good(struct rtree_node *node)
+{
+	int i, flag = 1;
+	int kind = -1;
+	pcb_bool last = pcb_false;
+
+	if (node == NULL)
+		return 1;
+	for (i = 0; i < M_SIZE; i++) {
+		if (node->flags.is_leaf) {
+			if (!node->u.rects[i].bptr) {
+				last = pcb_true;
+				continue;
+			}
+			/* check that once one entry is empty, all the rest are too */
+			if (node->u.rects[i].bptr && last)
+				assert(0);
+			/* check that the box makes sense */
+			if (node->box.X1 > node->box.X2)
+				assert(0);
+			if (node->box.Y1 > node->box.Y2)
+				assert(0);
+			/* check that bounds is the same as the pointer */
+			if (node->u.rects[i].bounds.X1 != node->u.rects[i].bptr->X1)
+				assert(0);
+			if (node->u.rects[i].bounds.Y1 != node->u.rects[i].bptr->Y1)
+				assert(0);
+			if (node->u.rects[i].bounds.X2 != node->u.rects[i].bptr->X2)
+				assert(0);
+			if (node->u.rects[i].bounds.Y2 != node->u.rects[i].bptr->Y2)
+				assert(0);
+			/* check that entries are within node bounds */
+			if (node->u.rects[i].bounds.X1 < node->box.X1)
+				assert(0);
+			if (node->u.rects[i].bounds.X2 > node->box.X2)
+				assert(0);
+			if (node->u.rects[i].bounds.Y1 < node->box.Y1)
+				assert(0);
+			if (node->u.rects[i].bounds.Y2 > node->box.Y2)
+				assert(0);
+		}
+		else {
+			if (!node->u.kids[i]) {
+				last = pcb_true;
+				continue;
+			}
+			/* make sure all children are the same type */
+			if (kind == -1)
+				kind = node->u.kids[i]->flags.is_leaf;
+			else if (kind != node->u.kids[i]->flags.is_leaf)
+				assert(0);
+			/* check that once one entry is empty, all the rest are too */
+			if (node->u.kids[i] && last)
+				assert(0);
+			/* check that entries are within node bounds */
+			if (node->u.kids[i]->box.X1 < node->box.X1)
+				assert(0);
+			if (node->u.kids[i]->box.X2 > node->box.X2)
+				assert(0);
+			if (node->u.kids[i]->box.Y1 < node->box.Y1)
+				assert(0);
+			if (node->u.kids[i]->box.Y2 > node->box.Y2)
+				assert(0);
+		}
+		flag <<= 1;
+	}
+	/* check that we're completely in the parent's bounds */
+	if (node->parent) {
+		if (node->parent->box.X1 > node->box.X1)
+			assert(0);
+		if (node->parent->box.X2 < node->box.X2)
+			assert(0);
+		if (node->parent->box.Y1 > node->box.Y1)
+			assert(0);
+		if (node->parent->box.Y2 < node->box.Y2)
+			assert(0);
+	}
+	/* make sure overflow is empty */
+	if (!node->flags.is_leaf && node->u.kids[i])
+		assert(0);
+	if (node->flags.is_leaf && node->u.rects[i].bptr)
+		assert(0);
+	return 1;
+}
+
+/* check the whole tree from this node down for consistency */
+static pcb_bool __r_tree_is_good(struct rtree_node *node)
+{
+	int i;
+
+	if (!node)
+		return 1;
+	if (!__r_node_is_good(node))
+		assert(0);
+	if (node->flags.is_leaf)
+		return 1;
+	for (i = 0; i < M_SIZE; i++) {
+		if (!__r_tree_is_good(node->u.kids[i]))
+			return 0;
+	}
+	return 1;
+}
+#endif
+#endif
+
+#ifndef NDEBUG
+/* print out the tree */
+void __r_dump_tree(struct rtree_node *node, int depth)
+{
+	int i, j;
+	static int count;
+	static double area;
+
+	if (depth == 0) {
+		area = 0;
+		count = 0;
+	}
+	area += (node->box.X2 - node->box.X1) * (double) (node->box.Y2 - node->box.Y1);
+	count++;
+	for (i = 0; i < depth; i++)
+		printf("  ");
+	if (!node->flags.is_leaf) {
+		printf("p=0x%p node X(%d, %d) Y(%d, %d)\n", (void *) node, node->box.X1, node->box.X2, node->box.Y1, node->box.Y2);
+	}
+	else {
+		printf("p=0x%p leaf manage(%02x) X(%d, %d) Y(%d, %d)\n", (void *) node,
+					 node->flags.manage, node->box.X1, node->box.X2, node->box.Y1, node->box.Y2);
+		for (j = 0; j < M_SIZE; j++) {
+			if (!node->u.rects[j].bptr)
+				break;
+			area +=
+				(node->u.rects[j].bounds.X2 -
+				 node->u.rects[j].bounds.X1) * (double) (node->u.rects[j].bounds.Y2 - node->u.rects[j].bounds.Y1);
+			count++;
+			for (i = 0; i < depth + 1; i++)
+				printf("  ");
+			printf("entry 0x%p X(%d, %d) Y(%d, %d)\n",
+						 (void *) (node->u.rects[j].bptr),
+						 node->u.rects[j].bounds.X1, node->u.rects[j].bounds.X2, node->u.rects[j].bounds.Y1, node->u.rects[j].bounds.Y2);
+		}
+		return;
+	}
+	for (i = 0; i < M_SIZE; i++)
+		if (node->u.kids[i])
+			__r_dump_tree(node->u.kids[i], depth + 1);
+	if (depth == 0)
+		printf("average box area is %g\n", area / count);
+}
+#endif
+
+/* Sort the children or entries of a node
+ * according to the largest side.
+ */
+#ifdef SORT
+static int cmp_box(const BoxType * a, const BoxType * b)
+{
+	/* compare two box coordinates so that the __r_search
+	 * will fail at the earliest comparison possible.
+	 * It needs to see the biggest X1 first, then the
+	 * smallest X2, the biggest Y1 and smallest Y2
+	 */
+	if (a->X1 < b->X1)
+		return 0;
+	if (a->X1 > b->X1)
+		return 1;
+	if (a->X2 > b->X2)
+		return 0;
+	if (a->X2 < b->X2)
+		return 1;
+	if (a->Y1 < b->Y1)
+		return 0;
+	if (a->Y1 > b->Y1)
+		return 1;
+	if (a->Y2 > b->Y2)
+		return 0;
+	return 1;
+}
+
+static void sort_node(struct rtree_node *node)
+{
+	if (node->flags.is_leaf) {
+		register Rentry *r, *i, temp;
+
+		for (r = &node->u.rects[1]; r->bptr; r++) {
+			temp = *r;
+			i = r - 1;
+			while (i >= &node->u.rects[0]) {
+				if (cmp_box(&i->bounds, &r->bounds))
+					break;
+				*(i + 1) = *i;
+				i--;
+			}
+			*(i + 1) = temp;
+		}
+	}
+#ifdef SORT_NONLEAF
+	else {
+		register struct rtree_node **r, **i, *temp;
+
+		for (r = &node->u.kids[1]; *r; r++) {
+			temp = *r;
+			i = r - 1;
+			while (i >= &node->u.kids[0]) {
+				if (cmp_box(&(*i)->box, &(*r)->box))
+					break;
+				*(i + 1) = *i;
+				i--;
+			}
+			*(i + 1) = temp;
+		}
+	}
+#endif
+}
+#else
+#define sort_node(x)
+#endif
+
+/* set the node bounds large enough to encompass all
+ * of the children's rectangles
+ */
+static void adjust_bounds(struct rtree_node *node)
+{
+	int i;
+
+	assert(node);
+	assert(node->u.kids[0]);
+	if (node->flags.is_leaf) {
+		node->box = node->u.rects[0].bounds;
+		for (i = 1; i < M_SIZE + 1; i++) {
+			if (!node->u.rects[i].bptr)
+				return;
+			MAKEMIN(node->box.X1, node->u.rects[i].bounds.X1);
+			MAKEMAX(node->box.X2, node->u.rects[i].bounds.X2);
+			MAKEMIN(node->box.Y1, node->u.rects[i].bounds.Y1);
+			MAKEMAX(node->box.Y2, node->u.rects[i].bounds.Y2);
+		}
+	}
+	else {
+		node->box = node->u.kids[0]->box;
+		for (i = 1; i < M_SIZE + 1; i++) {
+			if (!node->u.kids[i])
+				return;
+			MAKEMIN(node->box.X1, node->u.kids[i]->box.X1);
+			MAKEMAX(node->box.X2, node->u.kids[i]->box.X2);
+			MAKEMIN(node->box.Y1, node->u.kids[i]->box.Y1);
+			MAKEMAX(node->box.Y2, node->u.kids[i]->box.Y2);
+		}
+	}
+}
+
+/* create an r-tree from an unsorted list of boxes.
+ * the r-tree will keep pointers into 
+ * it, so don't free the box list until you've called r_destroy_tree.
+ * if you set 'manage' to pcb_true, r_destroy_tree will free your boxlist.
+ */
+rtree_t *r_create_tree(const BoxType * boxlist[], int N, int manage)
+{
+	rtree_t *rtree;
+	struct rtree_node *node;
+	int i;
+
+	assert(N >= 0);
+	rtree = (rtree_t *) calloc(1, sizeof(*rtree));
+	/* start with a single empty leaf node */
+	node = (struct rtree_node *) calloc(1, sizeof(*node));
+	node->flags.is_leaf = 1;
+	node->parent = NULL;
+	rtree->root = node;
+	/* simple, just insert all of the boxes! */
+	for (i = 0; i < N; i++) {
+		assert(boxlist[i]);
+		r_insert_entry(rtree, boxlist[i], manage);
+	}
+#ifdef SLOW_ASSERTS
+	assert(__r_tree_is_good(rtree->root));
+#endif
+	return rtree;
+}
+
+static void __r_destroy_tree(struct rtree_node *node)
+{
+	int i, flag = 1;
+
+	if (node->flags.is_leaf)
+		for (i = 0; i < M_SIZE; i++) {
+			if (!node->u.rects[i].bptr)
+				break;
+			if (node->flags.manage & flag)
+				free((void *) node->u.rects[i].bptr);
+			flag = flag << 1;
+		}
+	else
+		for (i = 0; i < M_SIZE; i++) {
+			if (!node->u.kids[i])
+				break;
+			__r_destroy_tree(node->u.kids[i]);
+		}
+	free(node);
+}
+
+/* free the memory associated with an rtree. */
+void r_destroy_tree(rtree_t ** rtree)
+{
+
+	__r_destroy_tree((*rtree)->root);
+	free(*rtree);
+	*rtree = NULL;
+}
+
+typedef struct {
+	r_dir_t (*check_it) (const BoxType * region, void *cl);
+	r_dir_t (*found_it) (const BoxType * box, void *cl);
+	void *closure;
+	int cancel;
+} r_arg;
+
+/* most of the auto-routing time is spent in this routine
+ * so some careful thought has been given to maximizing the speed
+ *
+ */
+int __r_search(struct rtree_node *node, const BoxType * query, r_arg * arg)
+{
+	r_dir_t res;
+
+	assert(node);
+	/** assert that starting_region is well formed */
+	assert(query->X1 < query->X2 && query->Y1 < query->Y2);
+	assert(node->box.X1 < query->X2 && node->box.X2 > query->X1 && node->box.Y1 < query->Y2 && node->box.Y2 > query->Y1);
+#ifdef SLOW_ASSERTS
+	/** assert that node is well formed */
+	assert(__r_node_is_good(node));
+#endif
+	/* the check for bounds is done before entry. This saves the overhead
+	 * of building/destroying the stack frame for each bounds that fails
+	 * to intersect, which is the most common condition.
+	 */
+	if (node->flags.is_leaf) {
+		register int i;
+		if (arg->found_it) {				/* test this once outside of loop */
+			register int seen = 0;
+			for (i = 0; node->u.rects[i].bptr; i++) {
+				if ((node->u.rects[i].bounds.X1 < query->X2) &&
+						(node->u.rects[i].bounds.X2 > query->X1) &&
+						(node->u.rects[i].bounds.Y1 < query->Y2) &&
+						(node->u.rects[i].bounds.Y2 > query->Y1)) {
+					res = arg->found_it(node->u.rects[i].bptr, arg->closure);
+					if (res == R_DIR_CANCEL) {
+						arg->cancel = 1;
+						return seen;
+					}
+					if (res != R_DIR_NOT_FOUND)
+						seen++;
+				}
+			}
+			return seen;
+		}
+		else {
+			register int seen = 0;
+			for (i = 0; node->u.rects[i].bptr; i++) {
+				if ((node->u.rects[i].bounds.X1 < query->X2) &&
+						(node->u.rects[i].bounds.X2 > query->X1) &&
+						(node->u.rects[i].bounds.Y1 < query->Y2) && (node->u.rects[i].bounds.Y2 > query->Y1))
+					seen++;
+			}
+			return seen;
+		}
+	}
+
+	/* not a leaf, recurse on lower nodes */
+	if (arg->check_it != NULL) {
+		int seen = 0;
+		struct rtree_node **n;
+		for (n = &node->u.kids[0]; *n; n++) {
+			if ((*n)->box.X1 >= query->X2 ||
+					(*n)->box.X2 <= query->X1 ||
+					(*n)->box.Y1 >= query->Y2 || (*n)->box.Y2 <= query->Y1)
+				continue;
+			res = arg->check_it(&(*n)->box, arg->closure);
+			if (res == R_DIR_CANCEL) {
+				arg->cancel = 1;
+				return seen;
+			}
+			if (!res)
+				continue;
+			seen += __r_search(*n, query, arg);
+			if (arg->cancel)
+				break;
+		}
+		return seen;
+	}
+	else {
+		int seen = 0;
+		struct rtree_node **n;
+		for (n = &node->u.kids[0]; *n; n++) {
+			if ((*n)->box.X1 >= query->X2 || (*n)->box.X2 <= query->X1 || (*n)->box.Y1 >= query->Y2 || (*n)->box.Y2 <= query->Y1)
+				continue;
+			seen  += __r_search(*n, query, arg);
+			if (arg->cancel)
+				break;
+		}
+		return seen;
+	}
+}
+
+/* Parameterized search in the rtree.
+ * Sets num_found to the number of rectangles found.
+ * calls found_rectangle for each intersection seen
+ * and calls check_region with the current sub-region
+ * to see whether deeper searching is desired
+ * Returns how the search ended.
+ */
+r_dir_t
+r_search(rtree_t * rtree, const BoxType * query,
+				 r_dir_t (*check_region) (const BoxType * region, void *cl),
+				 r_dir_t (*found_rectangle) (const BoxType * box, void *cl), void *cl,
+				 int *num_found)
+{
+	r_arg arg;
+	int res = 0;
+
+	arg.cancel = 0;
+
+	if (!rtree || rtree->size < 1)
+		goto ret;
+	if (query) {
+#ifdef SLOW_ASSERTS
+		assert(__r_tree_is_good(rtree->root));
+#endif
+#ifdef DEBUG
+		if (query->X2 <= query->X1 || query->Y2 <= query->Y1)
+			goto ret;
+#endif
+		/* check this box. If it's not touched we're done here */
+		if (rtree->root->box.X1 >= query->X2 ||
+				rtree->root->box.X2 <= query->X1 || rtree->root->box.Y1 >= query->Y2 || rtree->root->box.Y2 <= query->Y1)
+			goto ret;
+		arg.check_it = check_region;
+		arg.found_it = found_rectangle;
+		arg.closure = cl;
+
+		res = __r_search(rtree->root, query, &arg);
+	}
+	else {
+		arg.check_it = check_region;
+		arg.found_it = found_rectangle;
+		arg.closure = cl;
+		res = __r_search(rtree->root, &rtree->root->box, &arg);
+	}
+
+ret:;
+	if (num_found != NULL)
+		*num_found = res;
+	if (arg.cancel)
+		return R_DIR_CANCEL;
+	if (res == 0)
+		return R_DIR_NOT_FOUND;
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/*------ r_region_is_empty ------*/
+static r_dir_t __r_region_is_empty_rect_in_reg(const BoxType * box, void *cl)
+{
+	jmp_buf *envp = (jmp_buf *) cl;
+	longjmp(*envp, 1);						/* found one! */
+}
+
+/* return 0 if there are any rectangles in the given region. */
+int r_region_is_empty(rtree_t * rtree, const BoxType * region)
+{
+	jmp_buf env;
+	int r;
+
+	if (setjmp(env))
+		return 0;
+	r_search(rtree, region, NULL, __r_region_is_empty_rect_in_reg, &env, &r);
+	assert(r == 0);								/* otherwise longjmp would have been called */
+	return 1;											/* no rectangles found */
+}
+
+struct centroid {
+	float x, y, area;
+};
+
+/* split the node into two nodes putting clusters in each
+ * use the k-means clustering algorithm
+ */
+struct rtree_node *find_clusters(struct rtree_node *node)
+{
+	float total_a, total_b;
+	float a_X, a_Y, b_X, b_Y;
+	pcb_bool belong[M_SIZE + 1];
+	struct centroid center[M_SIZE + 1];
+	int clust_a, clust_b, tries;
+	int a_manage = 0, b_manage = 0;
+	int i, old_ax, old_ay, old_bx, old_by;
+	struct rtree_node *new_node;
+	BoxType *b;
+
+	for (i = 0; i < M_SIZE + 1; i++) {
+		if (node->flags.is_leaf)
+			b = &(node->u.rects[i].bounds);
+		else
+			b = &(node->u.kids[i]->box);
+		center[i].x = 0.5 * (b->X1 + b->X2);
+		center[i].y = 0.5 * (b->Y1 + b->Y2);
+		/* adding 1 prevents zero area */
+		center[i].area = 1. + (float) (b->X2 - b->X1) * (float) (b->Y2 - b->Y1);
+	}
+	/* starting 'A' cluster center */
+	a_X = center[0].x;
+	a_Y = center[0].y;
+	/* starting 'B' cluster center */
+	b_X = center[M_SIZE].x;
+	b_Y = center[M_SIZE].y;
+	/* don't allow the same cluster centers */
+	if (b_X == a_X && b_Y == a_Y) {
+		b_X += 10000;
+		a_Y -= 10000;
+	}
+	for (tries = 0; tries < M_SIZE; tries++) {
+		old_ax = (int) a_X;
+		old_ay = (int) a_Y;
+		old_bx = (int) b_X;
+		old_by = (int) b_Y;
+		clust_a = clust_b = 0;
+		for (i = 0; i < M_SIZE + 1; i++) {
+			float dist1, dist2;
+
+			dist1 = SQUARE(a_X - center[i].x) + SQUARE(a_Y - center[i].y);
+			dist2 = SQUARE(b_X - center[i].x) + SQUARE(b_Y - center[i].y);
+			if (dist1 * (clust_a + M_SIZE / 2) < dist2 * (clust_b + M_SIZE / 2)) {
+				belong[i] = pcb_true;
+				clust_a++;
+			}
+			else {
+				belong[i] = pcb_false;
+				clust_b++;
+			}
+		}
+		/* kludge to fix degenerate cases */
+		if (clust_a == M_SIZE + 1)
+			belong[M_SIZE / 2] = pcb_false;
+		else if (clust_b == M_SIZE + 1)
+			belong[M_SIZE / 2] = pcb_true;
+		/* compute new center of gravity of clusters */
+		total_a = total_b = 0;
+		a_X = a_Y = b_X = b_Y = 0;
+		for (i = 0; i < M_SIZE + 1; i++) {
+			if (belong[i]) {
+				a_X += center[i].x * center[i].area;
+				a_Y += center[i].y * center[i].area;
+				total_a += center[i].area;
+			}
+			else {
+				b_X += center[i].x * center[i].area;
+				b_Y += center[i].y * center[i].area;
+				total_b += center[i].area;
+			}
+		}
+		a_X /= total_a;
+		a_Y /= total_a;
+		b_X /= total_b;
+		b_Y /= total_b;
+		if (old_ax == (int) a_X && old_ay == (int) a_Y && old_bx == (int) b_X && old_by == (int) b_Y)
+			break;
+	}
+	/* Now 'belong' has the partition map */
+	new_node = (struct rtree_node *) calloc(1, sizeof(*new_node));
+	new_node->parent = node->parent;
+	new_node->flags.is_leaf = node->flags.is_leaf;
+	clust_a = clust_b = 0;
+	if (node->flags.is_leaf) {
+		int flag, a_flag, b_flag;
+		flag = a_flag = b_flag = 1;
+		for (i = 0; i < M_SIZE + 1; i++) {
+			if (belong[i]) {
+				node->u.rects[clust_a++] = node->u.rects[i];
+				if (node->flags.manage & flag)
+					a_manage |= a_flag;
+				a_flag <<= 1;
+			}
+			else {
+				new_node->u.rects[clust_b++] = node->u.rects[i];
+				if (node->flags.manage & flag)
+					b_manage |= b_flag;
+				b_flag <<= 1;
+			}
+			flag <<= 1;
+		}
+	}
+	else {
+		for (i = 0; i < M_SIZE + 1; i++) {
+			if (belong[i])
+				node->u.kids[clust_a++] = node->u.kids[i];
+			else {
+				node->u.kids[i]->parent = new_node;
+				new_node->u.kids[clust_b++] = node->u.kids[i];
+			}
+		}
+	}
+	node->flags.manage = a_manage;
+	new_node->flags.manage = b_manage;
+	assert(clust_a != 0);
+	assert(clust_b != 0);
+	if (node->flags.is_leaf)
+		for (; clust_a < M_SIZE + 1; clust_a++)
+			node->u.rects[clust_a].bptr = NULL;
+	else
+		for (; clust_a < M_SIZE + 1; clust_a++)
+			node->u.kids[clust_a] = NULL;
+	adjust_bounds(node);
+	sort_node(node);
+	adjust_bounds(new_node);
+	sort_node(new_node);
+	return (new_node);
+}
+
+/* split a node according to clusters
+ */
+static void split_node(struct rtree_node *node)
+{
+	int i;
+	struct rtree_node *new_node;
+
+	assert(node);
+	assert(node->flags.is_leaf ? (void *) node->u.rects[M_SIZE].bptr : (void *) node->u.kids[M_SIZE]);
+	new_node = find_clusters(node);
+	if (node->parent == NULL) {		/* split root node */
+		struct rtree_node *second;
+
+		second = (struct rtree_node *) calloc(1, sizeof(*second));
+		*second = *node;
+		if (!second->flags.is_leaf)
+			for (i = 0; i < M_SIZE; i++)
+				if (second->u.kids[i])
+					second->u.kids[i]->parent = second;
+		node->flags.is_leaf = 0;
+		node->flags.manage = 0;
+		second->parent = new_node->parent = node;
+		node->u.kids[0] = new_node;
+		node->u.kids[1] = second;
+		for (i = 2; i < M_SIZE + 1; i++)
+			node->u.kids[i] = NULL;
+		adjust_bounds(node);
+		sort_node(node);
+#ifdef SLOW_ASSERTS
+		assert(__r_tree_is_good(node));
+#endif
+		return;
+	}
+	for (i = 0; i < M_SIZE; i++)
+		if (!node->parent->u.kids[i])
+			break;
+	node->parent->u.kids[i] = new_node;
+#ifdef SLOW_ASSERTS
+	assert(__r_node_is_good(node));
+	assert(__r_node_is_good(new_node));
+#endif
+	if (i < M_SIZE) {
+#ifdef SLOW_ASSERTS
+		assert(__r_node_is_good(node->parent));
+#endif
+		sort_node(node->parent);
+		return;
+	}
+	split_node(node->parent);
+}
+
+static inline int contained(struct rtree_node *node, const BoxType * query)
+{
+	if (node->box.X1 > query->X1 || node->box.X2 < query->X2 || node->box.Y1 > query->Y1 || node->box.Y2 < query->Y2)
+		return 0;
+	return 1;
+}
+
+
+static inline double penalty(struct rtree_node *node, const BoxType * query)
+{
+	double score;
+
+	/* Compute the area penalty for inserting here and return.
+	 * The penalty is the increase in area necessary
+	 * to include the query->
+	 */
+	score = (MAX(node->box.X2, query->X2) - MIN(node->box.X1, query->X1));
+	score *= (MAX(node->box.Y2, query->Y2) - MIN(node->box.Y1, query->Y1));
+	score -= (double) (node->box.X2 - node->box.X1) * (double) (node->box.Y2 - node->box.Y1);
+	return score;
+}
+
+static void __r_insert_node(struct rtree_node *node, const BoxType * query, int manage, pcb_bool force)
+{
+
+#ifdef SLOW_ASSERTS
+	assert(__r_node_is_good(node));
+#endif
+	/* Ok, at this point we must already enclose the query or we're forcing
+	 * this node to expand to enclose it, so if we're a leaf, simply store
+	 * the query here
+	 */
+
+	if (node->flags.is_leaf) {
+		register int i;
+
+		if (UNLIKELY(manage)) {
+			register int flag = 1;
+
+			for (i = 0; i < M_SIZE; i++) {
+				if (!node->u.rects[i].bptr)
+					break;
+				flag <<= 1;
+			}
+			node->flags.manage |= flag;
+		}
+		else {
+			for (i = 0; i < M_SIZE; i++)
+				if (!node->u.rects[i].bptr)
+					break;
+		}
+		/* the node always has an extra space available */
+		node->u.rects[i].bptr = query;
+		node->u.rects[i].bounds = *query;
+		/* first entry in node determines initial bounding box */
+		if (i == 0)
+			node->box = *query;
+		else if (force) {
+			MAKEMIN(node->box.X1, query->X1);
+			MAKEMAX(node->box.X2, query->X2);
+			MAKEMIN(node->box.Y1, query->Y1);
+			MAKEMAX(node->box.Y2, query->Y2);
+		}
+		if (i < M_SIZE) {
+			sort_node(node);
+			return;
+		}
+		/* we must split the node */
+		split_node(node);
+		return;
+	}
+	else {
+		int i;
+		struct rtree_node *best_node;
+		double score, best_score;
+
+		if (force) {
+			MAKEMIN(node->box.X1, query->X1);
+			MAKEMAX(node->box.X2, query->X2);
+			MAKEMIN(node->box.Y1, query->Y1);
+			MAKEMAX(node->box.Y2, query->Y2);
+		}
+
+		/* this node encloses it, but it's not a leaf, so descend the tree */
+
+		/* First check if any children actually encloses it */
+		assert(node->u.kids[0]);
+		for (i = 0; i < M_SIZE; i++) {
+			if (!node->u.kids[i])
+				break;
+			if (contained(node->u.kids[i], query)) {
+				__r_insert_node(node->u.kids[i], query, manage, pcb_false);
+				sort_node(node);
+				return;
+			}
+		}
+
+		/* see if there is room for a new leaf node */
+		if (node->u.kids[0]->flags.is_leaf && i < M_SIZE) {
+			struct rtree_node *new_node;
+			new_node = (struct rtree_node *) calloc(1, sizeof(*new_node));
+			new_node->parent = node;
+			new_node->flags.is_leaf = pcb_true;
+			node->u.kids[i] = new_node;
+			new_node->u.rects[0].bptr = query;
+			new_node->u.rects[0].bounds = *query;
+			new_node->box = *query;
+			if (UNLIKELY(manage))
+				new_node->flags.manage = 1;
+			sort_node(node);
+			return;
+		}
+
+		/* Ok, so we're still here - look for the best child to push it into */
+		best_score = penalty(node->u.kids[0], query);
+		best_node = node->u.kids[0];
+		for (i = 1; i < M_SIZE; i++) {
+			if (!node->u.kids[i])
+				break;
+			score = penalty(node->u.kids[i], query);
+			if (score < best_score) {
+				best_score = score;
+				best_node = node->u.kids[i];
+			}
+		}
+		__r_insert_node(best_node, query, manage, pcb_true);
+		sort_node(node);
+		return;
+	}
+}
+
+void r_insert_entry(rtree_t * rtree, const BoxType * which, int man)
+{
+	assert(which);
+	assert(which->X1 <= which->X2);
+	assert(which->Y1 <= which->Y2);
+	/* recursively search the tree for the best leaf node */
+	assert(rtree->root);
+	__r_insert_node(rtree->root, which, man,
+									rtree->root->box.X1 > which->X1
+									|| rtree->root->box.X2 < which->X2 || rtree->root->box.Y1 > which->Y1 || rtree->root->box.Y2 < which->Y2);
+	rtree->size++;
+}
+
+pcb_bool __r_delete(struct rtree_node *node, const BoxType * query)
+{
+	int i, flag, mask, a;
+
+	/* the tree might be inconsistent during delete */
+	if (query->X1 < node->box.X1 || query->Y1 < node->box.Y1 || query->X2 > node->box.X2 || query->Y2 > node->box.Y2)
+		return pcb_false;
+	if (!node->flags.is_leaf) {
+		for (i = 0; i < M_SIZE; i++) {
+			/* if this is us being removed, free and copy over */
+			if (node->u.kids[i] == (struct rtree_node *) query) {
+				free((void *) query);
+				for (; i < M_SIZE; i++) {
+					node->u.kids[i] = node->u.kids[i + 1];
+					if (!node->u.kids[i])
+						break;
+				}
+				/* nobody home here now ? */
+				if (!node->u.kids[0]) {
+					if (!node->parent) {
+						/* wow, the root is empty! */
+						node->flags.is_leaf = 1;
+						/* changing type of node, be sure it's all zero */
+						for (i = 1; i < M_SIZE + 1; i++)
+							node->u.rects[i].bptr = NULL;
+						return pcb_true;
+					}
+					return (__r_delete(node->parent, &node->box));
+				}
+				else
+					/* propagate boundary adjust upward */
+					while (node) {
+						adjust_bounds(node);
+						node = node->parent;
+					}
+				return pcb_true;
+			}
+			if (node->u.kids[i]) {
+				if (__r_delete(node->u.kids[i], query))
+					return pcb_true;
+			}
+			else
+				break;
+		}
+		return pcb_false;
+	}
+	/* leaf node here */
+	mask = 0;
+	a = 1;
+	for (i = 0; i < M_SIZE; i++) {
+#ifdef DELETE_BY_POINTER
+		if (!node->u.rects[i].bptr || node->u.rects[i].bptr == query)
+#else
+		if (node->u.rects[i].bounds.X1 == query->X1 &&
+				node->u.rects[i].bounds.X2 == query->X2 &&
+				node->u.rects[i].bounds.Y1 == query->Y1 && node->u.rects[i].bounds.Y2 == query->Y2)
+#endif
+			break;
+		mask |= a;
+		a <<= 1;
+	}
+	if (!node->u.rects[i].bptr)
+		return pcb_false;								/* not at this leaf */
+	if (node->flags.manage & a) {
+		free((void *) node->u.rects[i].bptr);
+		node->u.rects[i].bptr = NULL;
+	}
+	/* squeeze the manage flags together */
+	flag = node->flags.manage & mask;
+	mask = (~mask) << 1;
+	node->flags.manage = flag | ((node->flags.manage & mask) >> 1);
+	/* remove the entry */
+	for (; i < M_SIZE; i++) {
+		node->u.rects[i] = node->u.rects[i + 1];
+		if (!node->u.rects[i].bptr)
+			break;
+	}
+	if (!node->u.rects[0].bptr) {
+		if (node->parent)
+			__r_delete(node->parent, &node->box);
+		return pcb_true;
+	}
+	else
+		/* propagate boundary adjustment upward */
+		while (node) {
+			adjust_bounds(node);
+			node = node->parent;
+		}
+	return pcb_true;
+}
+
+pcb_bool r_delete_entry(rtree_t * rtree, const BoxType * box)
+{
+	pcb_bool r;
+
+	assert(box);
+	assert(rtree);
+	r = __r_delete(rtree->root, box);
+	if (r)
+		rtree->size--;
+#ifdef SLOW_ASSERTS
+	assert(__r_tree_is_good(rtree->root));
+#endif
+	return r;
+}
diff --git a/src/rtree.h b/src/rtree.h
new file mode 100644
index 0000000..f4d48a2
--- /dev/null
+++ b/src/rtree.h
@@ -0,0 +1,83 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, rtree.h, was written and is
+ *  Copyright (c) 2004 harry eaton, it's based on C. Scott's kdtree.h template
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+
+/* prototypes for r-tree routines.
+ */
+
+#ifndef PCB_RTREE_H
+#define PCB_RTREE_H
+
+#include "global.h"
+
+/* callback direction to the search engine */
+typedef enum r_dir_e {
+	R_DIR_NOT_FOUND = 0,         /* object not found or not accepted */
+	R_DIR_FOUND_CONTINUE = 1,    /* object found or accepted, go on searching */
+	R_DIR_CANCEL                 /* cancel the search and return immediately */
+} r_dir_t;
+
+/* create an rtree from the list of boxes.  if 'manage' is pcb_true, then
+ * the tree will take ownership of 'boxlist' and free it when the tree
+ * is destroyed. */
+rtree_t *r_create_tree(const BoxType * boxlist[], int N, int manage);
+/* destroy an rtree */
+void r_destroy_tree(rtree_t ** rtree);
+
+pcb_bool r_delete_entry(rtree_t * rtree, const BoxType * which);
+void r_insert_entry(rtree_t * rtree, const BoxType * which, int manage);
+
+/* generic search routine */
+/* region_in_search should return pcb_true if "what you're looking for" is
+ * within the specified region; regions, like rectangles, are closed on
+ * top and left and open on bottom and right.
+ * rectangle_in_region should return pcb_true if the given rectangle is
+ * "what you're looking for".
+ * The search will find all rectangles matching the criteria given
+ * by region_in_search and rectangle_in_region and return a count of
+ * how many things rectangle_in_region returned pcb_true for. closure is
+ * used to abort the search if desired from within rectangel_in_region
+ * Look at the implementation of r_region_is_empty for how to
+ * abort the search if that is the desired behavior.
+ */
+
+r_dir_t r_search(rtree_t * rtree, const BoxType * starting_region,
+						 r_dir_t (*region_in_search) (const BoxType * region, void *cl),
+						 r_dir_t (*rectangle_in_region) (const BoxType * box, void *cl), void *closure,
+						 int *num_found);
+
+/* -- special-purpose searches build upon r_search -- */
+/* return 0 if there are any rectangles in the given region. */
+int r_region_is_empty(rtree_t * rtree, const BoxType * region);
+
+void __r_dump_tree(struct rtree_node *, int);
+
+#endif
diff --git a/src/rubberband.c b/src/rubberband.c
new file mode 100644
index 0000000..14a9512
--- /dev/null
+++ b/src/rubberband.c
@@ -0,0 +1,492 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* functions used by 'rubberband moves' */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <math.h>
+
+#include "create.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "layer.h"
+#include "polygon.h"
+#include "rtree.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void CheckPadForRubberbandConnection(PadTypePtr);
+static void CheckPinForRubberbandConnection(PinTypePtr);
+static void CheckLinePointForRubberbandConnection(LayerTypePtr, LineTypePtr, PointTypePtr, pcb_bool);
+static void CheckPolygonForRubberbandConnection(LayerTypePtr, PolygonTypePtr);
+static void CheckLinePointForRat(LayerTypePtr, PointTypePtr);
+static r_dir_t rubber_callback(const BoxType * b, void *cl);
+
+struct rubber_info {
+	Coord radius;
+	Coord X, Y;
+	LineTypePtr line;
+	BoxType box;
+	LayerTypePtr layer;
+};
+
+static r_dir_t rubber_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct rubber_info *i = (struct rubber_info *) cl;
+	double x, y, rad, dist1, dist2;
+	Coord t;
+	int touches = 0;
+
+	t = line->Thickness / 2;
+
+	if (TEST_FLAG(PCB_FLAG_LOCK, line))
+		return R_DIR_NOT_FOUND;
+	if (line == i->line)
+		return R_DIR_NOT_FOUND;
+	/*
+	 * Check to see if the line touches a rectangular region.
+	 * To do this we need to look for the intersection of a circular
+	 * region and a rectangular region.
+	 */
+	if (i->radius == 0) {
+		int found = 0;
+
+		if (line->Point1.X + t >= i->box.X1 && line->Point1.X - t <= i->box.X2
+				&& line->Point1.Y + t >= i->box.Y1 && line->Point1.Y - t <= i->box.Y2) {
+			if (((i->box.X1 <= line->Point1.X) &&
+					 (line->Point1.X <= i->box.X2)) || ((i->box.Y1 <= line->Point1.Y) && (line->Point1.Y <= i->box.Y2))) {
+				/*
+				 * The circle is positioned such that the closest point
+				 * on the rectangular region boundary is not at a corner
+				 * of the rectangle.  i.e. the shortest line from circle
+				 * center to rectangle intersects the rectangle at 90
+				 * degrees.  In this case our first test is sufficient
+				 */
+				touches = 1;
+			}
+			else {
+				/*
+				 * Now we must check the distance from the center of the
+				 * circle to the corners of the rectangle since the
+				 * closest part of the rectangular region is the corner.
+				 */
+				x = MIN(coord_abs(i->box.X1 - line->Point1.X), coord_abs(i->box.X2 - line->Point1.X));
+				x *= x;
+				y = MIN(coord_abs(i->box.Y1 - line->Point1.Y), coord_abs(i->box.Y2 - line->Point1.Y));
+				y *= y;
+				x = x + y - (t * t);
+
+				if (x <= 0)
+					touches = 1;
+			}
+			if (touches) {
+				CreateNewRubberbandEntry(i->layer, line, &line->Point1);
+				found++;
+			}
+		}
+		if (line->Point2.X + t >= i->box.X1 && line->Point2.X - t <= i->box.X2
+				&& line->Point2.Y + t >= i->box.Y1 && line->Point2.Y - t <= i->box.Y2) {
+			if (((i->box.X1 <= line->Point2.X) &&
+					 (line->Point2.X <= i->box.X2)) || ((i->box.Y1 <= line->Point2.Y) && (line->Point2.Y <= i->box.Y2))) {
+				touches = 1;
+			}
+			else {
+				x = MIN(coord_abs(i->box.X1 - line->Point2.X), coord_abs(i->box.X2 - line->Point2.X));
+				x *= x;
+				y = MIN(coord_abs(i->box.Y1 - line->Point2.Y), coord_abs(i->box.Y2 - line->Point2.Y));
+				y *= y;
+				x = x + y - (t * t);
+
+				if (x <= 0)
+					touches = 1;
+			}
+			if (touches) {
+				CreateNewRubberbandEntry(i->layer, line, &line->Point2);
+				found++;
+			}
+		}
+		return found ? R_DIR_FOUND_CONTINUE : R_DIR_NOT_FOUND;
+	}
+	/* circular search region */
+	if (i->radius < 0)
+		rad = 0;										/* require exact match */
+	else
+		rad = SQUARE(i->radius + t);
+
+	x = (i->X - line->Point1.X);
+	x *= x;
+	y = (i->Y - line->Point1.Y);
+	y *= y;
+	dist1 = x + y - rad;
+
+	x = (i->X - line->Point2.X);
+	x *= x;
+	y = (i->Y - line->Point2.Y);
+	y *= y;
+	dist2 = x + y - rad;
+
+	if (dist1 > 0 && dist2 > 0)
+		return R_DIR_NOT_FOUND;
+
+#ifdef CLOSEST_ONLY							/* keep this to remind me */
+	if (dist1 < dist2)
+		CreateNewRubberbandEntry(i->layer, line, &line->Point1);
+	else
+		CreateNewRubberbandEntry(i->layer, line, &line->Point2);
+#else
+	if (dist1 <= 0)
+		CreateNewRubberbandEntry(i->layer, line, &line->Point1);
+	if (dist2 <= 0)
+		CreateNewRubberbandEntry(i->layer, line, &line->Point2);
+#endif
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks all visible lines which belong to the same layergroup as the
+ * passed pad. If one of the endpoints of the line lays inside the pad,
+ * the line is added to the 'rubberband' list
+ */
+static void CheckPadForRubberbandConnection(PadTypePtr Pad)
+{
+	Coord half = Pad->Thickness / 2;
+	pcb_cardinal_t i, group;
+	struct rubber_info info;
+
+	info.box.X1 = MIN(Pad->Point1.X, Pad->Point2.X) - half;
+	info.box.Y1 = MIN(Pad->Point1.Y, Pad->Point2.Y) - half;
+	info.box.X2 = MAX(Pad->Point1.X, Pad->Point2.X) + half;
+	info.box.Y2 = MAX(Pad->Point1.Y, Pad->Point2.Y) + half;
+	info.radius = 0;
+	info.line = NULL;
+	i = TEST_FLAG(PCB_FLAG_ONSOLDER, Pad) ? solder_silk_layer : component_silk_layer;
+	group = GetLayerGroupNumberByNumber(i);
+
+	/* check all visible layers in the same group */
+	GROUP_LOOP(PCB->Data, group);
+	{
+		/* check all visible lines of the group member */
+		info.layer = layer;
+		if (info.layer->On) {
+			r_search(info.layer->line_tree, &info.box, NULL, rubber_callback, &info, NULL);
+		}
+	}
+	END_LOOP;
+}
+
+struct rinfo {
+	int type;
+	pcb_cardinal_t group;
+	PinTypePtr pin;
+	PadTypePtr pad;
+	PointTypePtr point;
+};
+
+static r_dir_t rat_callback(const BoxType * box, void *cl)
+{
+	RatTypePtr rat = (RatTypePtr) box;
+	struct rinfo *i = (struct rinfo *) cl;
+
+	switch (i->type) {
+	case PCB_TYPE_PIN:
+		if (rat->Point1.X == i->pin->X && rat->Point1.Y == i->pin->Y)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point1);
+		else if (rat->Point2.X == i->pin->X && rat->Point2.Y == i->pin->Y)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point2);
+		break;
+	case PCB_TYPE_PAD:
+		if (rat->Point1.X == i->pad->Point1.X && rat->Point1.Y == i->pad->Point1.Y && rat->group1 == i->group)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point1);
+		else if (rat->Point2.X == i->pad->Point1.X && rat->Point2.Y == i->pad->Point1.Y && rat->group2 == i->group)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point2);
+		else if (rat->Point1.X == i->pad->Point2.X && rat->Point1.Y == i->pad->Point2.Y && rat->group1 == i->group)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point1);
+		else if (rat->Point2.X == i->pad->Point2.X && rat->Point2.Y == i->pad->Point2.Y && rat->group2 == i->group)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point2);
+		else
+			if (rat->Point1.X == (i->pad->Point1.X + i->pad->Point2.X) / 2 &&
+					rat->Point1.Y == (i->pad->Point1.Y + i->pad->Point2.Y) / 2 && rat->group1 == i->group)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point1);
+		else
+			if (rat->Point2.X == (i->pad->Point1.X + i->pad->Point2.X) / 2 &&
+					rat->Point2.Y == (i->pad->Point1.Y + i->pad->Point2.Y) / 2 && rat->group2 == i->group)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point2);
+		break;
+	case PCB_TYPE_LINE_POINT:
+		if (rat->group1 == i->group && rat->Point1.X == i->point->X && rat->Point1.Y == i->point->Y)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point1);
+		else if (rat->group2 == i->group && rat->Point2.X == i->point->X && rat->Point2.Y == i->point->Y)
+			CreateNewRubberbandEntry(NULL, (LineTypePtr) rat, &rat->Point2);
+		break;
+	default:
+		Message(PCB_MSG_DEFAULT, "hace: bad rubber-rat lookup callback\n");
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static void CheckPadForRat(PadTypePtr Pad)
+{
+	struct rinfo info;
+	pcb_cardinal_t i;
+
+	i = TEST_FLAG(PCB_FLAG_ONSOLDER, Pad) ? solder_silk_layer : component_silk_layer;
+	info.group = GetLayerGroupNumberByNumber(i);
+	info.pad = Pad;
+	info.type = PCB_TYPE_PAD;
+
+	r_search(PCB->Data->rat_tree, &Pad->BoundingBox, NULL, rat_callback, &info, NULL);
+}
+
+static void CheckPinForRat(PinTypePtr Pin)
+{
+	struct rinfo info;
+
+	info.type = PCB_TYPE_PIN;
+	info.pin = Pin;
+	r_search(PCB->Data->rat_tree, &Pin->BoundingBox, NULL, rat_callback, &info, NULL);
+}
+
+static void CheckLinePointForRat(LayerTypePtr Layer, PointTypePtr Point)
+{
+	struct rinfo info;
+	info.group = GetLayerGroupNumberByPointer(Layer);
+	info.point = Point;
+	info.type = PCB_TYPE_LINE_POINT;
+
+	r_search(PCB->Data->rat_tree, (BoxType *) Point, NULL, rat_callback, &info, NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * checks all visible lines. If one of the endpoints of the line lays
+ * inside the pin, the line is added to the 'rubberband' list
+ *
+ * Square pins are handled as if they were round. Speed
+ * and readability is more important then the few %
+ * of failures that are immediately recognized
+ */
+static void CheckPinForRubberbandConnection(PinTypePtr Pin)
+{
+	struct rubber_info info;
+	pcb_cardinal_t n;
+	Coord t = Pin->Thickness / 2;
+
+	info.box.X1 = Pin->X - t;
+	info.box.X2 = Pin->X + t;
+	info.box.Y1 = Pin->Y - t;
+	info.box.Y2 = Pin->Y + t;
+	info.line = NULL;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Pin))
+		info.radius = 0;
+	else {
+		info.radius = t;
+		info.X = Pin->X;
+		info.Y = Pin->Y;
+	}
+
+	for (n = 0; n < max_copper_layer; n++) {
+		info.layer = LAYER_PTR(n);
+		r_search(info.layer->line_tree, &info.box, NULL, rubber_callback, &info, NULL);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * checks all visible lines which belong to the same group as the passed line.
+ * If one of the endpoints of the line lays * inside the passed line,
+ * the scanned line is added to the 'rubberband' list
+ */
+static void CheckLinePointForRubberbandConnection(LayerTypePtr Layer, LineTypePtr Line, PointTypePtr LinePoint, pcb_bool Exact)
+{
+	pcb_cardinal_t group;
+	struct rubber_info info;
+	Coord t = Line->Thickness / 2;
+
+	/* lookup layergroup and check all visible lines in this group */
+	info.radius = Exact ? -1 : MAX(Line->Thickness / 2, 1);
+	info.box.X1 = LinePoint->X - t;
+	info.box.X2 = LinePoint->X + t;
+	info.box.Y1 = LinePoint->Y - t;
+	info.box.Y2 = LinePoint->Y + t;
+	info.line = Line;
+	info.X = LinePoint->X;
+	info.Y = LinePoint->Y;
+	group = GetLayerGroupNumberByPointer(Layer);
+	GROUP_LOOP(PCB->Data, group);
+	{
+		/* check all visible lines of the group member */
+		if (layer->On) {
+			info.layer = layer;
+			r_search(layer->line_tree, &info.box, NULL, rubber_callback, &info, NULL);
+		}
+	}
+	END_LOOP;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks all visible lines which belong to the same group as the passed polygon.
+ * If one of the endpoints of the line lays inside the passed polygon,
+ * the scanned line is added to the 'rubberband' list
+ */
+static void CheckPolygonForRubberbandConnection(LayerTypePtr Layer, PolygonTypePtr Polygon)
+{
+	pcb_cardinal_t group;
+
+	/* lookup layergroup and check all visible lines in this group */
+	group = GetLayerGroupNumberByPointer(Layer);
+	GROUP_LOOP(PCB->Data, group);
+	{
+		if (layer->On) {
+			Coord thick;
+
+			/* the following code just stupidly compares the endpoints
+			 * of the lines
+			 */
+			LINE_LOOP(layer);
+			{
+				if (TEST_FLAG(PCB_FLAG_LOCK, line))
+					continue;
+				if (TEST_FLAG(PCB_FLAG_CLEARLINE, line))
+					continue;
+				thick = (line->Thickness + 1) / 2;
+				if (IsPointInPolygon(line->Point1.X, line->Point1.Y, thick, Polygon))
+					CreateNewRubberbandEntry(layer, line, &line->Point1);
+				if (IsPointInPolygon(line->Point2.X, line->Point2.Y, thick, Polygon))
+					CreateNewRubberbandEntry(layer, line, &line->Point2);
+			}
+			END_LOOP;
+		}
+	}
+	END_LOOP;
+}
+
+/* ---------------------------------------------------------------------------
+ * lookup all lines that are connected to an object and save the
+ * data to 'Crosshair.AttachedObject.Rubberband'
+ * lookup is only done for visible layers
+ */
+void LookupRubberbandLines(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+
+	/* the function is only supported for some types
+	 * check all visible lines;
+	 * it is only necessary to check if one of the endpoints
+	 * is connected
+	 */
+	switch (Type) {
+	case PCB_TYPE_ELEMENT:
+		{
+			ElementTypePtr element = (ElementTypePtr) Ptr1;
+
+			/* square pins are handled as if they are round. Speed
+			 * and readability is more important then the few %
+			 * of failures that are immediately recognized
+			 */
+			PIN_LOOP(element);
+			{
+				CheckPinForRubberbandConnection(pin);
+			}
+			END_LOOP;
+			PAD_LOOP(element);
+			{
+				CheckPadForRubberbandConnection(pad);
+			}
+			END_LOOP;
+			break;
+		}
+
+	case PCB_TYPE_LINE:
+		{
+			LayerTypePtr layer = (LayerTypePtr) Ptr1;
+			LineTypePtr line = (LineTypePtr) Ptr2;
+			if (GetLayerNumber(PCB->Data, layer) < max_copper_layer) {
+				CheckLinePointForRubberbandConnection(layer, line, &line->Point1, pcb_false);
+				CheckLinePointForRubberbandConnection(layer, line, &line->Point2, pcb_false);
+			}
+			break;
+		}
+
+	case PCB_TYPE_LINE_POINT:
+		if (GetLayerNumber(PCB->Data, (LayerTypePtr) Ptr1) < max_copper_layer)
+			CheckLinePointForRubberbandConnection((LayerTypePtr) Ptr1, (LineTypePtr) Ptr2, (PointTypePtr) Ptr3, pcb_true);
+		break;
+
+	case PCB_TYPE_VIA:
+		CheckPinForRubberbandConnection((PinTypePtr) Ptr1);
+		break;
+
+	case PCB_TYPE_POLYGON:
+		if (GetLayerNumber(PCB->Data, (LayerTypePtr) Ptr1) < max_copper_layer)
+			CheckPolygonForRubberbandConnection((LayerTypePtr) Ptr1, (PolygonTypePtr) Ptr2);
+		break;
+	}
+}
+
+void LookupRatLines(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	switch (Type) {
+	case PCB_TYPE_ELEMENT:
+		{
+			ElementTypePtr element = (ElementTypePtr) Ptr1;
+
+			PIN_LOOP(element);
+			{
+				CheckPinForRat(pin);
+			}
+			END_LOOP;
+			PAD_LOOP(element);
+			{
+				CheckPadForRat(pad);
+			}
+			END_LOOP;
+			break;
+		}
+
+	case PCB_TYPE_LINE:
+		{
+			LayerTypePtr layer = (LayerTypePtr) Ptr1;
+			LineTypePtr line = (LineTypePtr) Ptr2;
+
+			CheckLinePointForRat(layer, &line->Point1);
+			CheckLinePointForRat(layer, &line->Point2);
+			break;
+		}
+
+	case PCB_TYPE_LINE_POINT:
+		CheckLinePointForRat((LayerTypePtr) Ptr1, (PointTypePtr) Ptr3);
+		break;
+
+	case PCB_TYPE_VIA:
+		CheckPinForRat((PinTypePtr) Ptr1);
+		break;
+	}
+}
diff --git a/src/rubberband.h b/src/rubberband.h
new file mode 100644
index 0000000..d0e04d3
--- /dev/null
+++ b/src/rubberband.h
@@ -0,0 +1,38 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for rubberband routines
+ */
+
+#ifndef	PCB_RUBBERBAND_H
+#define	PCB_RUBBERBAND_H
+
+#include "global.h"
+
+void LookupRubberbandLines(int, void *, void *, void *);
+void LookupRatLines(int, void *, void *, void *);
+
+#endif
diff --git a/src/search.c b/src/search.c
new file mode 100644
index 0000000..0a654ae
--- /dev/null
+++ b/src/search.c
@@ -0,0 +1,1396 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* search routines
+ * some of the functions use dummy parameters
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <math.h>
+
+#include "box.h"
+#include "data.h"
+#include "error.h"
+#include "find.h"
+#include "polygon.h"
+#include "rtree.h"
+#include "search.h"
+#include "misc.h"
+#include "layer.h"
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+static double PosX, PosY;				/* search position for subroutines */
+static Coord SearchRadius;
+static BoxType SearchBox;
+static LayerTypePtr SearchLayer;
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes.  The first parameter includes PCB_TYPE_LOCKED if we
+ * want to include locked types in the search.
+ */
+static pcb_bool SearchLineByLocation(int, LayerTypePtr *, LineTypePtr *, LineTypePtr *);
+static pcb_bool SearchArcByLocation(int, LayerTypePtr *, ArcTypePtr *, ArcTypePtr *);
+static pcb_bool SearchRatLineByLocation(int, RatTypePtr *, RatTypePtr *, RatTypePtr *);
+static pcb_bool SearchTextByLocation(int, LayerTypePtr *, TextTypePtr *, TextTypePtr *);
+static pcb_bool SearchPolygonByLocation(int, LayerTypePtr *, PolygonTypePtr *, PolygonTypePtr *);
+static pcb_bool SearchPinByLocation(int, ElementTypePtr *, PinTypePtr *, PinTypePtr *);
+static pcb_bool SearchPadByLocation(int, ElementTypePtr *, PadTypePtr *, PadTypePtr *, pcb_bool);
+static pcb_bool SearchViaByLocation(int, PinTypePtr *, PinTypePtr *, PinTypePtr *);
+static pcb_bool SearchElementNameByLocation(int, ElementTypePtr *, TextTypePtr *, TextTypePtr *, pcb_bool);
+static pcb_bool SearchLinePointByLocation(int, LayerTypePtr *, LineTypePtr *, PointTypePtr *);
+static pcb_bool SearchPointByLocation(int, LayerTypePtr *, PolygonTypePtr *, PointTypePtr *);
+static pcb_bool SearchElementByLocation(int, ElementTypePtr *, ElementTypePtr *, ElementTypePtr *, pcb_bool);
+
+/* ---------------------------------------------------------------------------
+ * searches a via
+ */
+struct ans_info {
+	void **ptr1, **ptr2, **ptr3;
+	pcb_bool BackToo;
+	double area;
+	int locked;										/* This will be zero or PCB_FLAG_LOCK */
+};
+
+static r_dir_t pinorvia_callback(const BoxType * box, void *cl)
+{
+	struct ans_info *i = (struct ans_info *) cl;
+	PinTypePtr pin = (PinTypePtr) box;
+	AnyObjectType *ptr1 = pin->Element ? pin->Element : pin;
+
+	if (TEST_FLAG(i->locked, ptr1))
+		return R_DIR_NOT_FOUND;
+
+	if (!IsPointOnPin(PosX, PosY, SearchRadius, pin))
+		return R_DIR_NOT_FOUND;
+	*i->ptr1 = ptr1;
+	*i->ptr2 = *i->ptr3 = pin;
+	return R_DIR_CANCEL; /* found, stop searching */
+}
+
+static pcb_bool SearchViaByLocation(int locked, PinTypePtr * Via, PinTypePtr * Dummy1, PinTypePtr * Dummy2)
+{
+	struct ans_info info;
+
+	/* search only if via-layer is visible */
+	if (!PCB->ViaOn)
+		return pcb_false;
+
+	info.ptr1 = (void **) Via;
+	info.ptr2 = (void **) Dummy1;
+	info.ptr3 = (void **) Dummy2;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	if (r_search(PCB->Data->via_tree, &SearchBox, NULL, pinorvia_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches a pin
+ * starts with the newest element
+ */
+static pcb_bool SearchPinByLocation(int locked, ElementTypePtr * Element, PinTypePtr * Pin, PinTypePtr * Dummy)
+{
+	struct ans_info info;
+
+	/* search only if pin-layer is visible */
+	if (!PCB->PinOn)
+		return pcb_false;
+	info.ptr1 = (void **) Element;
+	info.ptr2 = (void **) Pin;
+	info.ptr3 = (void **) Dummy;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	if (r_search(PCB->Data->pin_tree, &SearchBox, NULL, pinorvia_callback, &info, NULL)  != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+static r_dir_t pad_callback(const BoxType * b, void *cl)
+{
+	PadTypePtr pad = (PadTypePtr) b;
+	struct ans_info *i = (struct ans_info *) cl;
+	AnyObjectType *ptr1 = pad->Element;
+
+	if (TEST_FLAG(i->locked, ptr1))
+		return R_DIR_NOT_FOUND;
+
+	if (FRONT(pad) || i->BackToo) {
+		if (IsPointInPad(PosX, PosY, SearchRadius, pad)) {
+			*i->ptr1 = ptr1;
+			*i->ptr2 = *i->ptr3 = pad;
+			return R_DIR_CANCEL; /* found */
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches a pad
+ * starts with the newest element
+ */
+static pcb_bool SearchPadByLocation(int locked, ElementTypePtr * Element, PadTypePtr * Pad, PadTypePtr * Dummy, pcb_bool BackToo)
+{
+	struct ans_info info;
+
+	/* search only if pin-layer is visible */
+	if (!PCB->PinOn)
+		return (pcb_false);
+	info.ptr1 = (void **) Element;
+	info.ptr2 = (void **) Pad;
+	info.ptr3 = (void **) Dummy;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+	info.BackToo = (BackToo && PCB->InvisibleObjectsOn);
+	if (r_search(PCB->Data->pad_tree, &SearchBox, NULL, pad_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches ordinary line on the SearchLayer
+ */
+
+struct line_info {
+	LineTypePtr *Line;
+	PointTypePtr *Point;
+	double least;
+	int locked;
+};
+
+static r_dir_t line_callback(const BoxType * box, void *cl)
+{
+	struct line_info *i = (struct line_info *) cl;
+	LineTypePtr l = (LineTypePtr) box;
+
+	if (TEST_FLAG(i->locked, l))
+		return R_DIR_NOT_FOUND;
+
+	if (!IsPointInPad(PosX, PosY, SearchRadius, (PadTypePtr) l))
+		return R_DIR_NOT_FOUND;
+	*i->Line = l;
+	*i->Point = (PointTypePtr) l;
+
+	return R_DIR_CANCEL; /* found what we were looking for */
+}
+
+
+static pcb_bool SearchLineByLocation(int locked, LayerTypePtr * Layer, LineTypePtr * Line, LineTypePtr * Dummy)
+{
+	struct line_info info;
+
+	info.Line = Line;
+	info.Point = (PointTypePtr *) Dummy;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	*Layer = SearchLayer;
+	if (r_search(SearchLayer->line_tree, &SearchBox, NULL, line_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+
+	return pcb_false;
+}
+
+static r_dir_t rat_callback(const BoxType * box, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) box;
+	struct ans_info *i = (struct ans_info *) cl;
+
+	if (TEST_FLAG(i->locked, line))
+		return R_DIR_NOT_FOUND;
+
+	if (TEST_FLAG(PCB_FLAG_VIA, line) ?
+			(Distance(line->Point1.X, line->Point1.Y, PosX, PosY) <=
+			 line->Thickness * 2 + SearchRadius) : IsPointOnLine(PosX, PosY, SearchRadius, line)) {
+		*i->ptr1 = *i->ptr2 = *i->ptr3 = line;
+		return R_DIR_CANCEL;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches rat lines if they are visible
+ */
+static pcb_bool SearchRatLineByLocation(int locked, RatTypePtr * Line, RatTypePtr * Dummy1, RatTypePtr * Dummy2)
+{
+	struct ans_info info;
+
+	info.ptr1 = (void **) Line;
+	info.ptr2 = (void **) Dummy1;
+	info.ptr3 = (void **) Dummy2;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	if (r_search(PCB->Data->rat_tree, &SearchBox, NULL, rat_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches arc on the SearchLayer
+ */
+struct arc_info {
+	ArcTypePtr *Arc, *Dummy;
+	int locked;
+};
+
+static r_dir_t arc_callback(const BoxType * box, void *cl)
+{
+	struct arc_info *i = (struct arc_info *) cl;
+	ArcTypePtr a = (ArcTypePtr) box;
+
+	if (TEST_FLAG(i->locked, a))
+		return R_DIR_NOT_FOUND;
+
+	if (!IsPointOnArc(PosX, PosY, SearchRadius, a))
+		return 0;
+	*i->Arc = a;
+	*i->Dummy = a;
+	return R_DIR_CANCEL; /* found */
+}
+
+
+static pcb_bool SearchArcByLocation(int locked, LayerTypePtr * Layer, ArcTypePtr * Arc, ArcTypePtr * Dummy)
+{
+	struct arc_info info;
+
+	info.Arc = Arc;
+	info.Dummy = Dummy;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	*Layer = SearchLayer;
+	if (r_search(SearchLayer->arc_tree, &SearchBox, NULL, arc_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+static r_dir_t text_callback(const BoxType * box, void *cl)
+{
+	TextTypePtr text = (TextTypePtr) box;
+	struct ans_info *i = (struct ans_info *) cl;
+
+	if (TEST_FLAG(i->locked, text))
+		return R_DIR_NOT_FOUND;
+
+	if (POINT_IN_BOX(PosX, PosY, &text->BoundingBox)) {
+		*i->ptr2 = *i->ptr3 = text;
+		return R_DIR_CANCEL; /* found */
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches text on the SearchLayer
+ */
+static pcb_bool SearchTextByLocation(int locked, LayerTypePtr * Layer, TextTypePtr * Text, TextTypePtr * Dummy)
+{
+	struct ans_info info;
+
+	*Layer = SearchLayer;
+	info.ptr2 = (void **) Text;
+	info.ptr3 = (void **) Dummy;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	if (r_search(SearchLayer->text_tree, &SearchBox, NULL, text_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+static r_dir_t polygon_callback(const BoxType * box, void *cl)
+{
+	PolygonTypePtr polygon = (PolygonTypePtr) box;
+	struct ans_info *i = (struct ans_info *) cl;
+
+	if (TEST_FLAG(i->locked, polygon))
+		return R_DIR_NOT_FOUND;
+
+	if (IsPointInPolygon(PosX, PosY, SearchRadius, polygon)) {
+		*i->ptr2 = *i->ptr3 = polygon;
+		return R_DIR_CANCEL; /* found */
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * searches a polygon on the SearchLayer
+ */
+static pcb_bool SearchPolygonByLocation(int locked, LayerTypePtr * Layer, PolygonTypePtr * Polygon, PolygonTypePtr * Dummy)
+{
+	struct ans_info info;
+
+	*Layer = SearchLayer;
+	info.ptr2 = (void **) Polygon;
+	info.ptr3 = (void **) Dummy;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+
+	if (r_search(SearchLayer->polygon_tree, &SearchBox, NULL, polygon_callback, &info, NULL) != R_DIR_NOT_FOUND)
+		return pcb_true;
+	return pcb_false;
+}
+
+static r_dir_t linepoint_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	struct line_info *i = (struct line_info *) cl;
+	r_dir_t ret_val = R_DIR_NOT_FOUND;
+	double d;
+
+	if (TEST_FLAG(i->locked, line))
+		return R_DIR_NOT_FOUND;
+
+	/* some stupid code to check both points */
+	d = Distance(PosX, PosY, line->Point1.X, line->Point1.Y);
+	if (d < i->least) {
+		i->least = d;
+		*i->Line = line;
+		*i->Point = &line->Point1;
+		ret_val = R_DIR_FOUND_CONTINUE;
+	}
+
+	d = Distance(PosX, PosY, line->Point2.X, line->Point2.Y);
+	if (d < i->least) {
+		i->least = d;
+		*i->Line = line;
+		*i->Point = &line->Point2;
+		ret_val = R_DIR_FOUND_CONTINUE;
+	}
+	return ret_val;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches a line-point on all the search layer
+ */
+static pcb_bool SearchLinePointByLocation(int locked, LayerTypePtr * Layer, LineTypePtr * Line, PointTypePtr * Point)
+{
+	struct line_info info;
+	*Layer = SearchLayer;
+	info.Line = Line;
+	info.Point = Point;
+	*Point = NULL;
+	info.least = MAX_LINE_POINT_DISTANCE + SearchRadius;
+	info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+	if (r_search(SearchLayer->line_tree, &SearchBox, NULL, linepoint_callback, &info, NULL))
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches a polygon-point on all layers that are switched on
+ * in layerstack order
+ */
+static pcb_bool SearchPointByLocation(int locked, LayerTypePtr * Layer, PolygonTypePtr * Polygon, PointTypePtr * Point)
+{
+	double d, least;
+	pcb_bool found = pcb_false;
+
+	least = SearchRadius + MAX_POLYGON_POINT_DISTANCE;
+	*Layer = SearchLayer;
+	POLYGON_LOOP(*Layer);
+	{
+		POLYGONPOINT_LOOP(polygon);
+		{
+			d = Distance(point->X, point->Y, PosX, PosY);
+			if (d < least) {
+				least = d;
+				*Polygon = polygon;
+				*Point = point;
+				found = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	if (found)
+		return (pcb_true);
+	return (pcb_false);
+}
+
+static r_dir_t name_callback(const BoxType * box, void *cl)
+{
+	TextTypePtr text = (TextTypePtr) box;
+	struct ans_info *i = (struct ans_info *) cl;
+	ElementTypePtr element = (ElementTypePtr) text->Element;
+	double newarea;
+
+	if (TEST_FLAG(i->locked, text))
+		return R_DIR_NOT_FOUND;
+
+	if ((FRONT(element) || i->BackToo) && !TEST_FLAG(PCB_FLAG_HIDENAME, element) && POINT_IN_BOX(PosX, PosY, &text->BoundingBox)) {
+		/* use the text with the smallest bounding box */
+		newarea = (text->BoundingBox.X2 - text->BoundingBox.X1) * (double) (text->BoundingBox.Y2 - text->BoundingBox.Y1);
+		if (newarea < i->area) {
+			i->area = newarea;
+			*i->ptr1 = element;
+			*i->ptr2 = *i->ptr3 = text;
+		}
+		return R_DIR_FOUND_CONTINUE;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches the name of an element
+ * the search starts with the last element and goes back to the beginning
+ */
+static pcb_bool
+SearchElementNameByLocation(int locked, ElementTypePtr * Element, TextTypePtr * Text, TextTypePtr * Dummy, pcb_bool BackToo)
+{
+	struct ans_info info;
+
+	/* package layer have to be switched on */
+	if (PCB->ElementOn) {
+		info.ptr1 = (void **) Element;
+		info.ptr2 = (void **) Text;
+		info.ptr3 = (void **) Dummy;
+		info.area = SQUARE(MAX_COORD);
+		info.BackToo = (BackToo && PCB->InvisibleObjectsOn);
+		info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+		if (r_search(PCB->Data->name_tree[NAME_INDEX()], &SearchBox, NULL, name_callback, &info, NULL))
+			return pcb_true;
+	}
+	return (pcb_false);
+}
+
+static r_dir_t element_callback(const BoxType * box, void *cl)
+{
+	ElementTypePtr element = (ElementTypePtr) box;
+	struct ans_info *i = (struct ans_info *) cl;
+	double newarea;
+
+	if (TEST_FLAG(i->locked, element))
+		return R_DIR_NOT_FOUND;
+
+	if ((FRONT(element) || i->BackToo) && POINT_IN_BOX(PosX, PosY, &element->VBox)) {
+		/* use the element with the smallest bounding box */
+		newarea = (element->VBox.X2 - element->VBox.X1) * (double) (element->VBox.Y2 - element->VBox.Y1);
+		if (newarea < i->area) {
+			i->area = newarea;
+			*i->ptr1 = *i->ptr2 = *i->ptr3 = element;
+			return R_DIR_FOUND_CONTINUE;
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches an element
+ * the search starts with the last element and goes back to the beginning
+ * if more than one element matches, the smallest one is taken
+ */
+static pcb_bool
+SearchElementByLocation(int locked, ElementTypePtr * Element, ElementTypePtr * Dummy1, ElementTypePtr * Dummy2, pcb_bool BackToo)
+{
+	struct ans_info info;
+
+	/* Both package layers have to be switched on */
+	if (PCB->ElementOn && PCB->PinOn) {
+		info.ptr1 = (void **) Element;
+		info.ptr2 = (void **) Dummy1;
+		info.ptr3 = (void **) Dummy2;
+		info.area = SQUARE(MAX_COORD);
+		info.BackToo = (BackToo && PCB->InvisibleObjectsOn);
+		info.locked = (locked & PCB_TYPE_LOCKED) ? 0 : PCB_FLAG_LOCK;
+		if (r_search(PCB->Data->element_tree, &SearchBox, NULL, element_callback, &info, NULL))
+			return pcb_true;
+	}
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a point is on a pin
+ */
+pcb_bool IsPointOnPin(Coord X, Coord Y, Coord Radius, PinTypePtr pin)
+{
+	Coord t = PIN_SIZE(pin) / 2;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+		BoxType b;
+
+		b.X1 = pin->X - t;
+		b.X2 = pin->X + t;
+		b.Y1 = pin->Y - t;
+		b.Y2 = pin->Y + t;
+		if (IsPointInBox(X, Y, &b, Radius))
+			return pcb_true;
+	}
+	else if (Distance(pin->X, pin->Y, X, Y) <= Radius + t)
+		return pcb_true;
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a rat-line end is on a PV
+ */
+pcb_bool IsPointOnLineEnd(Coord X, Coord Y, RatTypePtr Line)
+{
+	if (((X == Line->Point1.X) && (Y == Line->Point1.Y)) || ((X == Line->Point2.X) && (Y == Line->Point2.Y)))
+		return (pcb_true);
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a line intersects with a PV
+ *
+ * let the point be (X,Y) and the line (X1,Y1)(X2,Y2)
+ * the length of the line is
+ *
+ *   L = ((X2-X1)^2 + (Y2-Y1)^2)^0.5
+ *
+ * let Q be the point of perpendicular projection of (X,Y) onto the line
+ *
+ *   QX = X1 + D1*(X2-X1) / L
+ *   QY = Y1 + D1*(Y2-Y1) / L
+ *
+ * with (from vector geometry)
+ *
+ *        (Y1-Y)(Y1-Y2)+(X1-X)(X1-X2)
+ *   D1 = ---------------------------
+ *                     L
+ *
+ *   D1 < 0   Q is on backward extension of the line
+ *   D1 > L   Q is on forward extension of the line
+ *   else     Q is on the line
+ *
+ * the signed distance from (X,Y) to Q is
+ *
+ *        (Y2-Y1)(X-X1)-(X2-X1)(Y-Y1)
+ *   D2 = ----------------------------
+ *                     L
+ *
+ * Finally, D1 and D2 are orthogonal, so we can sum them easily
+ * by Pythagorean theorem.
+ */
+pcb_bool IsPointOnLine(Coord X, Coord Y, Coord Radius, LineTypePtr Line)
+{
+	double D1, D2, L;
+
+	/* Get length of segment */
+	L = Distance(Line->Point1.X, Line->Point1.Y, Line->Point2.X, Line->Point2.Y);
+	if (L < 0.1)
+		return Distance(X, Y, Line->Point1.X, Line->Point1.Y) < Radius + Line->Thickness / 2;
+
+	/* Get distance from (X1, Y1) to Q (on the line) */
+	D1 = ((double) (Y - Line->Point1.Y) * (Line->Point2.Y - Line->Point1.Y)
+				+ (double) (X - Line->Point1.X) * (Line->Point2.X - Line->Point1.X)) / L;
+	/* Translate this into distance to Q from segment */
+	if (D1 < 0)
+		D1 = -D1;
+	else if (D1 > L)
+		D1 -= L;
+	else
+		D1 = 0;
+	/* Get distance from (X, Y) to Q */
+	D2 = ((double) (X - Line->Point1.X) * (Line->Point2.Y - Line->Point1.Y)
+				- (double) (Y - Line->Point1.Y) * (Line->Point2.X - Line->Point1.X)) / L;
+	/* Total distance is then the Pythagorean sum of these */
+	return sqrt(D1 * D1 + D2 * D2) <= Radius + Line->Thickness / 2;
+}
+
+static int is_point_on_line(Coord px, Coord py, Coord lx1, Coord ly1, Coord lx2, Coord ly2)
+{
+	/* ohh well... let's hope the optimizer does something clever with inlining... */
+	LineType l;
+	l.Point1.X = lx1;
+	l.Point1.Y = ly1;
+	l.Point2.X = lx2;
+	l.Point2.Y = ly2;
+	l.Thickness = 1;
+	return IsPointOnLine(px, py, 1, &l);
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a line crosses a rectangle
+ */
+pcb_bool IsLineInRectangle(Coord X1, Coord Y1, Coord X2, Coord Y2, LineTypePtr Line)
+{
+	LineType line;
+
+	/* first, see if point 1 is inside the rectangle */
+	/* in case the whole line is inside the rectangle */
+	if (X1 < Line->Point1.X && X2 > Line->Point1.X && Y1 < Line->Point1.Y && Y2 > Line->Point1.Y)
+		return (pcb_true);
+	/* construct a set of dummy lines and check each of them */
+	line.Thickness = 0;
+	line.Flags = NoFlags();
+
+	/* upper-left to upper-right corner */
+	line.Point1.Y = line.Point2.Y = Y1;
+	line.Point1.X = X1;
+	line.Point2.X = X2;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	/* upper-right to lower-right corner */
+	line.Point1.X = X2;
+	line.Point1.Y = Y1;
+	line.Point2.Y = Y2;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	/* lower-right to lower-left corner */
+	line.Point1.Y = Y2;
+	line.Point1.X = X1;
+	line.Point2.X = X2;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	/* lower-left to upper-left corner */
+	line.Point2.X = X1;
+	line.Point1.Y = Y1;
+	line.Point2.Y = Y2;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	return (pcb_false);
+}
+
+static int /*checks if a point (of null radius) is in a slanted rectangle */ IsPointInQuadrangle(PointType p[4], PointTypePtr l)
+{
+	Coord dx, dy, x, y;
+	double prod0, prod1;
+
+	dx = p[1].X - p[0].X;
+	dy = p[1].Y - p[0].Y;
+	x = l->X - p[0].X;
+	y = l->Y - p[0].Y;
+	prod0 = (double) x *dx + (double) y *dy;
+	x = l->X - p[1].X;
+	y = l->Y - p[1].Y;
+	prod1 = (double) x *dx + (double) y *dy;
+	if (prod0 * prod1 <= 0) {
+		dx = p[1].X - p[2].X;
+		dy = p[1].Y - p[2].Y;
+		prod0 = (double) x *dx + (double) y *dy;
+		x = l->X - p[2].X;
+		y = l->Y - p[2].Y;
+		prod1 = (double) x *dx + (double) y *dy;
+		if (prod0 * prod1 <= 0)
+			return pcb_true;
+	}
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if a line crosses a quadrangle: almost copied from IsLineInRectangle()
+ * Note: actually this quadrangle is a slanted rectangle
+ */
+pcb_bool IsLineInQuadrangle(PointType p[4], LineTypePtr Line)
+{
+	LineType line;
+
+	/* first, see if point 1 is inside the rectangle */
+	/* in case the whole line is inside the rectangle */
+	if (IsPointInQuadrangle(p, &(Line->Point1)))
+		return pcb_true;
+	if (IsPointInQuadrangle(p, &(Line->Point2)))
+		return pcb_true;
+	/* construct a set of dummy lines and check each of them */
+	line.Thickness = 0;
+	line.Flags = NoFlags();
+
+	/* upper-left to upper-right corner */
+	line.Point1.X = p[0].X;
+	line.Point1.Y = p[0].Y;
+	line.Point2.X = p[1].X;
+	line.Point2.Y = p[1].Y;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	/* upper-right to lower-right corner */
+	line.Point1.X = p[2].X;
+	line.Point1.Y = p[2].Y;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	/* lower-right to lower-left corner */
+	line.Point2.X = p[3].X;
+	line.Point2.Y = p[3].Y;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	/* lower-left to upper-left corner */
+	line.Point1.X = p[0].X;
+	line.Point1.Y = p[0].Y;
+	if (LineLineIntersect(&line, Line))
+		return (pcb_true);
+
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * checks if an arc crosses a square
+ */
+pcb_bool IsArcInRectangle(Coord X1, Coord Y1, Coord X2, Coord Y2, ArcTypePtr Arc)
+{
+	LineType line;
+
+	/* construct a set of dummy lines and check each of them */
+	line.Thickness = 0;
+	line.Flags = NoFlags();
+
+	/* upper-left to upper-right corner */
+	line.Point1.Y = line.Point2.Y = Y1;
+	line.Point1.X = X1;
+	line.Point2.X = X2;
+	if (LineArcIntersect(&line, Arc))
+		return (pcb_true);
+
+	/* upper-right to lower-right corner */
+	line.Point1.X = line.Point2.X = X2;
+	line.Point1.Y = Y1;
+	line.Point2.Y = Y2;
+	if (LineArcIntersect(&line, Arc))
+		return (pcb_true);
+
+	/* lower-right to lower-left corner */
+	line.Point1.Y = line.Point2.Y = Y2;
+	line.Point1.X = X1;
+	line.Point2.X = X2;
+	if (LineArcIntersect(&line, Arc))
+		return (pcb_true);
+
+	/* lower-left to upper-left corner */
+	line.Point1.X = line.Point2.X = X1;
+	line.Point1.Y = Y1;
+	line.Point2.Y = Y2;
+	if (LineArcIntersect(&line, Arc))
+		return (pcb_true);
+
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * Check if a circle of Radius with center at (X, Y) intersects a Pad.
+ * Written to enable arbitrary pad directions; for rounded pads, too.
+ */
+pcb_bool IsPointInPad(Coord X, Coord Y, Coord Radius, PadTypePtr Pad)
+{
+	double r, Sin, Cos;
+	Coord x;
+
+	/* Also used from line_callback with line type smaller than pad type;
+	   use the smallest common subset; ->Thickness is still ok. */
+	Coord t2 = (Pad->Thickness + 1) / 2, range;
+	AnyLineObjectType pad = *(AnyLineObjectType *) Pad;
+
+
+	/* series of transforms saving range */
+	/* move Point1 to the origin */
+	X -= pad.Point1.X;
+	Y -= pad.Point1.Y;
+
+	pad.Point2.X -= pad.Point1.X;
+	pad.Point2.Y -= pad.Point1.Y;
+	/* so, pad.Point1.X = pad.Point1.Y = 0; */
+
+	/* rotate round (0, 0) so that Point2 coordinates be (r, 0) */
+	r = Distance(0, 0, pad.Point2.X, pad.Point2.Y);
+	if (r < .1) {
+		Cos = 1;
+		Sin = 0;
+	}
+	else {
+		Sin = pad.Point2.Y / r;
+		Cos = pad.Point2.X / r;
+	}
+	x = X;
+	X = X * Cos + Y * Sin;
+	Y = Y * Cos - x * Sin;
+	/* now pad.Point2.X = r; pad.Point2.Y = 0; */
+
+	/* take into account the ends */
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Pad)) {
+		r += Pad->Thickness;
+		X += t2;
+	}
+	if (Y < 0)
+		Y = -Y;											/* range value is evident now */
+
+	if (TEST_FLAG(PCB_FLAG_SQUARE, Pad)) {
+		if (X <= 0) {
+			if (Y <= t2)
+				range = -X;
+			else
+				return Radius > Distance(0, t2, X, Y);
+		}
+		else if (X >= r) {
+			if (Y <= t2)
+				range = X - r;
+			else
+				return Radius > Distance(r, t2, X, Y);
+		}
+		else
+			range = Y - t2;
+	}
+	else {												/*Rounded pad: even more simple */
+
+		if (X <= 0)
+			return (Radius + t2) > Distance(0, 0, X, Y);
+		else if (X >= r)
+			return (Radius + t2) > Distance(r, 0, X, Y);
+		else
+			range = Y - t2;
+	}
+	return range < Radius;
+}
+
+pcb_bool IsPointInBox(Coord X, Coord Y, BoxTypePtr box, Coord Radius)
+{
+	Coord width, height, range;
+
+	/* NB: Assumes box has point1 with numerically lower X and Y coordinates */
+
+	/* Compute coordinates relative to Point1 */
+	X -= box->X1;
+	Y -= box->Y1;
+
+	width = box->X2 - box->X1;
+	height = box->Y2 - box->Y1;
+
+	if (X <= 0) {
+		if (Y < 0)
+			return Radius > Distance(0, 0, X, Y);
+		else if (Y > height)
+			return Radius > Distance(0, height, X, Y);
+		else
+			range = -X;
+	}
+	else if (X >= width) {
+		if (Y < 0)
+			return Radius > Distance(width, 0, X, Y);
+		else if (Y > height)
+			return Radius > Distance(width, height, X, Y);
+		else
+			range = X - width;
+	}
+	else {
+		if (Y < 0)
+			range = -Y;
+		else if (Y > height)
+			range = Y - height;
+		else
+			return pcb_true;
+	}
+
+	return range < Radius;
+}
+
+/* TODO: this code is BROKEN in the case of non-circular arcs,
+ *       and in the case that the arc thickness is greater than
+ *       the radius.
+ */
+pcb_bool IsPointOnArc(Coord X, Coord Y, Coord Radius, ArcTypePtr Arc)
+{
+	/* Calculate angle of point from arc center */
+	double p_dist = Distance(X, Y, Arc->X, Arc->Y);
+	double p_cos = (X - Arc->X) / p_dist;
+	Angle p_ang = acos(p_cos) * PCB_RAD_TO_DEG;
+	Angle ang1, ang2;
+
+	/* Convert StartAngle, Delta into bounding angles in [0, 720) */
+	if (Arc->Delta > 0) {
+		ang1 = NormalizeAngle(Arc->StartAngle);
+		ang2 = NormalizeAngle(Arc->StartAngle + Arc->Delta);
+	}
+	else {
+		ang1 = NormalizeAngle(Arc->StartAngle + Arc->Delta);
+		ang2 = NormalizeAngle(Arc->StartAngle);
+	}
+	if (ang1 > ang2)
+		ang2 += 360;
+	/* Make sure full circles aren't treated as zero-length arcs */
+	if (Arc->Delta == 360 || Arc->Delta == -360)
+		ang2 = ang1 + 360;
+
+	if (Y > Arc->Y)
+		p_ang = -p_ang;
+	p_ang += 180;
+
+	/* Check point is outside arc range, check distance from endpoints */
+	if (ang1 >= p_ang || ang2 <= p_ang) {
+		Coord ArcX, ArcY;
+
+		ArcX = Arc->X + Arc->Width * cos((Arc->StartAngle + 180) / PCB_RAD_TO_DEG);
+		ArcY = Arc->Y - Arc->Width * sin((Arc->StartAngle + 180) / PCB_RAD_TO_DEG);
+		if (Distance(X, Y, ArcX, ArcY) < Radius + Arc->Thickness / 2)
+			return pcb_true;
+
+		ArcX = Arc->X + Arc->Width * cos((Arc->StartAngle + Arc->Delta + 180) / PCB_RAD_TO_DEG);
+		ArcY = Arc->Y - Arc->Width * sin((Arc->StartAngle + Arc->Delta + 180) / PCB_RAD_TO_DEG);
+		if (Distance(X, Y, ArcX, ArcY) < Radius + Arc->Thickness / 2)
+			return pcb_true;
+		return pcb_false;
+	}
+	/* If point is inside the arc range, just compare it to the arc */
+	return fabs(Distance(X, Y, Arc->X, Arc->Y) - Arc->Width) < Radius + Arc->Thickness / 2;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches for any kind of object or for a set of object types
+ * the calling routine passes two pointers to allocated memory for storing
+ * the results.
+ * A type value is returned too which is PCB_TYPE_NONE if no objects has been found.
+ * A set of object types is passed in.
+ * The object is located by it's position.
+ *
+ * The layout is checked in the following order:
+ *   polygon-point, pin, via, line, text, elementname, polygon, element
+ *
+ * Note that if Type includes PCB_TYPE_LOCKED, then the search includes
+ * locked items.  Otherwise, locked items are ignored.
+ */
+int SearchObjectByLocation(unsigned Type, void **Result1, void **Result2, void **Result3, Coord X, Coord Y, Coord Radius)
+{
+	void *r1, *r2, *r3;
+	void **pr1 = &r1, **pr2 = &r2, **pr3 = &r3;
+	int i;
+	double HigherBound = 0;
+	int HigherAvail = PCB_TYPE_NONE;
+	int locked = Type & PCB_TYPE_LOCKED;
+	/* setup variables used by local functions */
+	PosX = X;
+	PosY = Y;
+	SearchRadius = Radius;
+	if (Radius) {
+		SearchBox.X1 = X - Radius;
+		SearchBox.Y1 = Y - Radius;
+		SearchBox.X2 = X + Radius;
+		SearchBox.Y2 = Y + Radius;
+	}
+	else {
+		SearchBox = point_box(X, Y);
+	}
+
+	if (conf_core.editor.lock_names) {
+		Type &= ~(PCB_TYPE_ELEMENT_NAME | PCB_TYPE_TEXT);
+	}
+	if (conf_core.editor.hide_names) {
+		Type &= ~PCB_TYPE_ELEMENT_NAME;
+	}
+	if (conf_core.editor.only_names) {
+		Type &= (PCB_TYPE_ELEMENT_NAME | PCB_TYPE_TEXT);
+	}
+	if (conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly) {
+		Type &= ~PCB_TYPE_POLYGON;
+	}
+
+	if (Type & PCB_TYPE_RATLINE && PCB->RatOn &&
+			SearchRatLineByLocation(locked, (RatTypePtr *) Result1, (RatTypePtr *) Result2, (RatTypePtr *) Result3))
+		return (PCB_TYPE_RATLINE);
+
+	if (Type & PCB_TYPE_VIA && SearchViaByLocation(locked, (PinTypePtr *) Result1, (PinTypePtr *) Result2, (PinTypePtr *) Result3))
+		return (PCB_TYPE_VIA);
+
+	if (Type & PCB_TYPE_PIN && SearchPinByLocation(locked, (ElementTypePtr *) pr1, (PinTypePtr *) pr2, (PinTypePtr *) pr3))
+		HigherAvail = PCB_TYPE_PIN;
+
+	if (!HigherAvail && Type & PCB_TYPE_PAD &&
+			SearchPadByLocation(locked, (ElementTypePtr *) pr1, (PadTypePtr *) pr2, (PadTypePtr *) pr3, pcb_false))
+		HigherAvail = PCB_TYPE_PAD;
+
+	if (!HigherAvail && Type & PCB_TYPE_ELEMENT_NAME &&
+			SearchElementNameByLocation(locked, (ElementTypePtr *) pr1, (TextTypePtr *) pr2, (TextTypePtr *) pr3, pcb_false)) {
+		BoxTypePtr box = &((TextTypePtr) r2)->BoundingBox;
+		HigherBound = (double) (box->X2 - box->X1) * (double) (box->Y2 - box->Y1);
+		HigherAvail = PCB_TYPE_ELEMENT_NAME;
+	}
+
+	if (!HigherAvail && Type & PCB_TYPE_ELEMENT &&
+			SearchElementByLocation(locked, (ElementTypePtr *) pr1, (ElementTypePtr *) pr2, (ElementTypePtr *) pr3, pcb_false)) {
+		BoxTypePtr box = &((ElementTypePtr) r1)->BoundingBox;
+		HigherBound = (double) (box->X2 - box->X1) * (double) (box->Y2 - box->Y1);
+		HigherAvail = PCB_TYPE_ELEMENT;
+	}
+
+	for (i = -1; i < max_copper_layer + 1; i++) {
+		if (i < 0)
+			SearchLayer = &PCB->Data->SILKLAYER;
+		else if (i < max_copper_layer)
+			SearchLayer = LAYER_ON_STACK(i);
+		else {
+			SearchLayer = &PCB->Data->BACKSILKLAYER;
+			if (!PCB->InvisibleObjectsOn)
+				continue;
+		}
+		if (SearchLayer->On) {
+			if ((HigherAvail & (PCB_TYPE_PIN | PCB_TYPE_PAD)) == 0 &&
+					Type & PCB_TYPE_POLYGON_POINT &&
+					SearchPointByLocation(locked, (LayerTypePtr *) Result1, (PolygonTypePtr *) Result2, (PointTypePtr *) Result3))
+				return (PCB_TYPE_POLYGON_POINT);
+
+			if ((HigherAvail & (PCB_TYPE_PIN | PCB_TYPE_PAD)) == 0 &&
+					Type & PCB_TYPE_LINE_POINT &&
+					SearchLinePointByLocation(locked, (LayerTypePtr *) Result1, (LineTypePtr *) Result2, (PointTypePtr *) Result3))
+				return (PCB_TYPE_LINE_POINT);
+
+			if ((HigherAvail & (PCB_TYPE_PIN | PCB_TYPE_PAD)) == 0 && Type & PCB_TYPE_LINE
+					&& SearchLineByLocation(locked, (LayerTypePtr *) Result1, (LineTypePtr *) Result2, (LineTypePtr *) Result3))
+				return (PCB_TYPE_LINE);
+
+			if ((HigherAvail & (PCB_TYPE_PIN | PCB_TYPE_PAD)) == 0 && Type & PCB_TYPE_ARC &&
+					SearchArcByLocation(locked, (LayerTypePtr *) Result1, (ArcTypePtr *) Result2, (ArcTypePtr *) Result3))
+				return (PCB_TYPE_ARC);
+
+			if ((HigherAvail & (PCB_TYPE_PIN | PCB_TYPE_PAD)) == 0 && Type & PCB_TYPE_TEXT
+					&& SearchTextByLocation(locked, (LayerTypePtr *) Result1, (TextTypePtr *) Result2, (TextTypePtr *) Result3))
+				return (PCB_TYPE_TEXT);
+
+			if (Type & PCB_TYPE_POLYGON &&
+					SearchPolygonByLocation(locked, (LayerTypePtr *) Result1, (PolygonTypePtr *) Result2, (PolygonTypePtr *) Result3)) {
+				if (HigherAvail) {
+					BoxTypePtr box = &(*(PolygonTypePtr *) Result2)->BoundingBox;
+					double area = (double) (box->X2 - box->X1) * (double) (box->X2 - box->X1);
+					if (HigherBound < area)
+						break;
+					else
+						return (PCB_TYPE_POLYGON);
+				}
+				else
+					return (PCB_TYPE_POLYGON);
+			}
+		}
+	}
+	/* return any previously found objects */
+	if (HigherAvail & PCB_TYPE_PIN) {
+		*Result1 = r1;
+		*Result2 = r2;
+		*Result3 = r3;
+		return (PCB_TYPE_PIN);
+	}
+
+	if (HigherAvail & PCB_TYPE_PAD) {
+		*Result1 = r1;
+		*Result2 = r2;
+		*Result3 = r3;
+		return (PCB_TYPE_PAD);
+	}
+
+	if (HigherAvail & PCB_TYPE_ELEMENT_NAME) {
+		*Result1 = r1;
+		*Result2 = r2;
+		*Result3 = r3;
+		return (PCB_TYPE_ELEMENT_NAME);
+	}
+
+	if (HigherAvail & PCB_TYPE_ELEMENT) {
+		*Result1 = r1;
+		*Result2 = r2;
+		*Result3 = r3;
+		return (PCB_TYPE_ELEMENT);
+	}
+
+	/* search the 'invisible objects' last */
+	if (!PCB->InvisibleObjectsOn)
+		return (PCB_TYPE_NONE);
+
+	if (Type & PCB_TYPE_PAD &&
+			SearchPadByLocation(locked, (ElementTypePtr *) Result1, (PadTypePtr *) Result2, (PadTypePtr *) Result3, pcb_true))
+		return (PCB_TYPE_PAD);
+
+	if (Type & PCB_TYPE_ELEMENT_NAME &&
+			SearchElementNameByLocation(locked, (ElementTypePtr *) Result1, (TextTypePtr *) Result2, (TextTypePtr *) Result3, pcb_true))
+		return (PCB_TYPE_ELEMENT_NAME);
+
+	if (Type & PCB_TYPE_ELEMENT &&
+			SearchElementByLocation(locked, (ElementTypePtr *) Result1, (ElementTypePtr *) Result2, (ElementTypePtr *) Result3, pcb_true))
+		return (PCB_TYPE_ELEMENT);
+
+	return (PCB_TYPE_NONE);
+}
+
+/* ---------------------------------------------------------------------------
+ * searches for a object by it's unique ID. It doesn't matter if
+ * the object is visible or not. The search is performed on a PCB, a
+ * buffer or on the remove list.
+ * The calling routine passes two pointers to allocated memory for storing
+ * the results.
+ * A type value is returned too which is PCB_TYPE_NONE if no objects has been found.
+ */
+int SearchObjectByID(DataTypePtr Base, void **Result1, void **Result2, void **Result3, int ID, int type)
+{
+	if (type == PCB_TYPE_LINE || type == PCB_TYPE_LINE_POINT) {
+		ALLLINE_LOOP(Base);
+		{
+			if (line->ID == ID) {
+				*Result1 = (void *) layer;
+				*Result2 = *Result3 = (void *) line;
+				return (PCB_TYPE_LINE);
+			}
+			if (line->Point1.ID == ID) {
+				*Result1 = (void *) layer;
+				*Result2 = (void *) line;
+				*Result3 = (void *) &line->Point1;
+				return (PCB_TYPE_LINE_POINT);
+			}
+			if (line->Point2.ID == ID) {
+				*Result1 = (void *) layer;
+				*Result2 = (void *) line;
+				*Result3 = (void *) &line->Point2;
+				return (PCB_TYPE_LINE_POINT);
+			}
+		}
+		ENDALL_LOOP;
+	}
+	if (type == PCB_TYPE_ARC) {
+		ALLARC_LOOP(Base);
+		{
+			if (arc->ID == ID) {
+				*Result1 = (void *) layer;
+				*Result2 = *Result3 = (void *) arc;
+				return (PCB_TYPE_ARC);
+			}
+		}
+		ENDALL_LOOP;
+	}
+
+	if (type == PCB_TYPE_TEXT) {
+		ALLTEXT_LOOP(Base);
+		{
+			if (text->ID == ID) {
+				*Result1 = (void *) layer;
+				*Result2 = *Result3 = (void *) text;
+				return (PCB_TYPE_TEXT);
+			}
+		}
+		ENDALL_LOOP;
+	}
+
+	if (type == PCB_TYPE_POLYGON || type == PCB_TYPE_POLYGON_POINT) {
+		ALLPOLYGON_LOOP(Base);
+		{
+			if (polygon->ID == ID) {
+				*Result1 = (void *) layer;
+				*Result2 = *Result3 = (void *) polygon;
+				return (PCB_TYPE_POLYGON);
+			}
+			if (type == PCB_TYPE_POLYGON_POINT)
+				POLYGONPOINT_LOOP(polygon);
+			{
+				if (point->ID == ID) {
+					*Result1 = (void *) layer;
+					*Result2 = (void *) polygon;
+					*Result3 = (void *) point;
+					return (PCB_TYPE_POLYGON_POINT);
+				}
+			}
+			END_LOOP;
+		}
+		ENDALL_LOOP;
+	}
+	if (type == PCB_TYPE_VIA) {
+		VIA_LOOP(Base);
+		{
+			if (via->ID == ID) {
+				*Result1 = *Result2 = *Result3 = (void *) via;
+				return (PCB_TYPE_VIA);
+			}
+		}
+		END_LOOP;
+	}
+
+	if (type == PCB_TYPE_RATLINE || type == PCB_TYPE_LINE_POINT) {
+		RAT_LOOP(Base);
+		{
+			if (line->ID == ID) {
+				*Result1 = *Result2 = *Result3 = (void *) line;
+				return (PCB_TYPE_RATLINE);
+			}
+			if (line->Point1.ID == ID) {
+				*Result1 = (void *) NULL;
+				*Result2 = (void *) line;
+				*Result3 = (void *) &line->Point1;
+				return (PCB_TYPE_LINE_POINT);
+			}
+			if (line->Point2.ID == ID) {
+				*Result1 = (void *) NULL;
+				*Result2 = (void *) line;
+				*Result3 = (void *) &line->Point2;
+				return (PCB_TYPE_LINE_POINT);
+			}
+		}
+		END_LOOP;
+	}
+
+	if (type == PCB_TYPE_ELEMENT || type == PCB_TYPE_PAD || type == PCB_TYPE_PIN
+			|| type == PCB_TYPE_ELEMENT_LINE || type == PCB_TYPE_ELEMENT_NAME || type == PCB_TYPE_ELEMENT_ARC)
+		/* check pins and elementnames too */
+		ELEMENT_LOOP(Base);
+	{
+		if (element->ID == ID) {
+			*Result1 = *Result2 = *Result3 = (void *) element;
+			return (PCB_TYPE_ELEMENT);
+		}
+		if (type == PCB_TYPE_ELEMENT_LINE)
+			ELEMENTLINE_LOOP(element);
+		{
+			if (line->ID == ID) {
+				*Result1 = (void *) element;
+				*Result2 = *Result3 = (void *) line;
+				return (PCB_TYPE_ELEMENT_LINE);
+			}
+		}
+		END_LOOP;
+		if (type == PCB_TYPE_ELEMENT_ARC)
+			ARC_LOOP(element);
+		{
+			if (arc->ID == ID) {
+				*Result1 = (void *) element;
+				*Result2 = *Result3 = (void *) arc;
+				return (PCB_TYPE_ELEMENT_ARC);
+			}
+		}
+		END_LOOP;
+		if (type == PCB_TYPE_ELEMENT_NAME)
+			ELEMENTTEXT_LOOP(element);
+		{
+			if (text->ID == ID) {
+				*Result1 = (void *) element;
+				*Result2 = *Result3 = (void *) text;
+				return (PCB_TYPE_ELEMENT_NAME);
+			}
+		}
+		END_LOOP;
+		if (type == PCB_TYPE_PIN)
+			PIN_LOOP(element);
+		{
+			if (pin->ID == ID) {
+				*Result1 = (void *) element;
+				*Result2 = *Result3 = (void *) pin;
+				return (PCB_TYPE_PIN);
+			}
+		}
+		END_LOOP;
+		if (type == PCB_TYPE_PAD)
+			PAD_LOOP(element);
+		{
+			if (pad->ID == ID) {
+				*Result1 = (void *) element;
+				*Result2 = *Result3 = (void *) pad;
+				return (PCB_TYPE_PAD);
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	Message(PCB_MSG_DEFAULT, "hace: Internal error, search for ID %d failed\n", ID);
+	return (PCB_TYPE_NONE);
+}
+
+/* ---------------------------------------------------------------------------
+ * searches for an element by its board name.
+ * The function returns a pointer to the element, NULL if not found
+ */
+ElementTypePtr SearchElementByName(DataTypePtr Base, const char *Name)
+{
+	ElementTypePtr result = NULL;
+
+	ELEMENT_LOOP(Base);
+	{
+		if (element->Name[1].TextString && NSTRCMP(element->Name[1].TextString, Name) == 0) {
+			result = element;
+			return (result);
+		}
+	}
+	END_LOOP;
+	return result;
+}
+
+/* ---------------------------------------------------------------------------
+ * searches the cursor position for the type
+ */
+int SearchScreen(Coord X, Coord Y, int Type, void **Result1, void **Result2, void **Result3)
+{
+	int ans;
+
+	ans = SearchObjectByLocation(Type, Result1, Result2, Result3, X, Y, SLOP * pixel_slop);
+	return (ans);
+}
+
+/* ---------------------------------------------------------------------------
+ * searches the cursor position for the type
+ */
+int SearchScreenGridSlop(Coord X, Coord Y, int Type, void **Result1, void **Result2, void **Result3)
+{
+	int ans;
+
+	ans = SearchObjectByLocation(Type, Result1, Result2, Result3, X, Y, PCB->Grid / 2);
+	return (ans);
+}
+
+int lines_intersect(Coord ax1, Coord ay1, Coord ax2, Coord ay2, Coord bx1, Coord by1, Coord bx2, Coord by2)
+{
+/* TODO: this should be long double if Coord is 64 bits */
+	double ua, xi, yi, X1, Y1, X2, Y2, X3, Y3, X4, Y4, tmp;
+	int is_a_pt, is_b_pt;
+
+	/* degenerate cases: a line is actually a point */
+	is_a_pt = (ax1 == ax2) && (ay1 == ay2);
+	is_b_pt = (bx1 == bx2) && (by1 == by2);
+
+	if (is_a_pt && is_b_pt)
+		return (ax1 == bx1) && (ay1 == by1);
+
+	if (is_a_pt)
+		return is_point_on_line(ax1, ay1, bx1, by1, bx2, by2);
+	if (is_b_pt)
+		return is_point_on_line(bx1, by1, ax1, ay1, ax2, ay2);
+
+	/* maths from http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ */
+
+	X1 = ax1;
+	X2 = ax2;
+	X3 = bx1;
+	X4 = bx2;
+	Y1 = ay1;
+	Y2 = ay2;
+	Y3 = by1;
+	Y4 = by2;
+
+	tmp = ((Y4 - Y3) * (X2 - X1) - (X4 - X3) * (Y2 - Y1));
+	ua = ((X4 - X3) * (Y1 - Y3) - (Y4 - Y3) * (X1 - X3)) / tmp;
+/*	ub = ((X2 - X1) * (Y1 - Y3) - (Y2 - Y1) * (X1 - X3)) / tmp;*/
+	xi = X1 + ua * (X2 - X1);
+	yi = Y1 + ua * (Y2 - Y1);
+
+#define check(e1, e2, i) \
+	if (e1 < e2) { \
+		if ((i < e1) || (i > e2)) \
+			return 0; \
+	} \
+	else { \
+		if ((i > e1) || (i < e2)) \
+			return 0; \
+	}
+
+	check(ax1, ax2, xi);
+	check(bx1, bx2, xi);
+	check(ay1, ay2, yi);
+	check(by1, by2, yi);
+	return 1;
+}
diff --git a/src/search.h b/src/search.h
new file mode 100644
index 0000000..e7f0aaf
--- /dev/null
+++ b/src/search.h
@@ -0,0 +1,162 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for search routines */
+
+#ifndef	PCB_SEARCH_H
+#define	PCB_SEARCH_H
+
+#include "global.h"
+#include "misc_util.h"
+
+int lines_intersect(Coord ax1, Coord ay1, Coord ax2, Coord ay2, Coord bx1, Coord by1, Coord bx2, Coord by2);
+
+#define SLOP 5
+/* ---------------------------------------------------------------------------
+ * some useful macros
+ */
+/* ---------------------------------------------------------------------------
+ * some define to check for 'type' in box
+ */
+#define	POINT_IN_BOX(x,y,b)	\
+	(IS_BOX_NEGATIVE(b) ?  \
+	((x) >= (b)->X2 && (x) <= (b)->X1 && (y) >= (b)->Y2 && (y) <= (b)->Y1) \
+	: \
+	((x) >= (b)->X1 && (x) <= (b)->X2 && (y) >= (b)->Y1 && (y) <= (b)->Y2))
+
+#define	VIA_OR_PIN_IN_BOX(v,b) \
+	POINT_IN_BOX((v)->X,(v)->Y,(b))
+
+#define	LINE_IN_BOX(l,b)	\
+	(POINT_IN_BOX((l)->Point1.X,(l)->Point1.Y,(b)) &&	\
+	POINT_IN_BOX((l)->Point2.X,(l)->Point2.Y,(b)))
+
+#define	PAD_IN_BOX(p,b)	LINE_IN_BOX((LineTypePtr)(p),(b))
+
+#define	BOX_IN_BOX(b1,b)	\
+	((b1)->X1 >= (b)->X1 && (b1)->X2 <= (b)->X2 &&	\
+	((b1)->Y1 >= (b)->Y1 && (b1)->Y2 <= (b)->Y2))
+
+#define	TEXT_IN_BOX(t,b)	\
+	(BOX_IN_BOX(&((t)->BoundingBox), (b)))
+
+#define	POLYGON_IN_BOX(p,b)	\
+	(BOX_IN_BOX(&((p)->BoundingBox), (b)))
+
+#define	ELEMENT_IN_BOX(e,b)	\
+	(BOX_IN_BOX(&((e)->BoundingBox), (b)))
+
+#define ARC_IN_BOX(a,b)		\
+	(BOX_IN_BOX(&((a)->BoundingBox), (b)))
+
+/* == the same but accept if any part of the object touches the box == */
+#define POINT_IN_CIRCLE(x, y, cx, cy, r) \
+	(Distance2(x, y, cx, cy) <= (double)(r) * (double)(r))
+
+#define CIRCLE_TOUCHES_BOX(cx, cy, r, b) \
+	(    POINT_IN_BOX((cx)-(r),(cy),(b)) || POINT_IN_BOX((cx)+(r),(cy),(b)) || POINT_IN_BOX((cx),(cy)-(r),(b)) || POINT_IN_BOX((cx),(cy)+(r),(b)) \
+	  || POINT_IN_CIRCLE((b)->X1, (b)->Y1, (cx), (cy), (r)) || POINT_IN_CIRCLE((b)->X2, (b)->Y1, (cx), (cy), (r)) || POINT_IN_CIRCLE((b)->X1, (b)->Y2, (cx), (cy), (r)) || POINT_IN_CIRCLE((b)->X2, (b)->Y2, (cx), (cy), (r)))
+
+#define	VIA_OR_PIN_TOUCHES_BOX(v,b) \
+	CIRCLE_TOUCHES_BOX((v)->X,(v)->Y,((v)->Thickness + (v)->DrillingHole/2),(b))
+
+#define	LINE_TOUCHES_BOX(l,b)	\
+	(    lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X1, (b)->Y1, (b)->X2, (b)->Y1) \
+	  || lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X1, (b)->Y1, (b)->X1, (b)->Y2) \
+	  || lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X2, (b)->Y2, (b)->X1, (b)->Y2) \
+	  || lines_intersect((l)->Point1.X,(l)->Point1.Y,(l)->Point2.X,(l)->Point2.Y, (b)->X2, (b)->Y2, (b)->X2, (b)->Y1) \
+	  || LINE_IN_BOX((l), (b)))
+
+#define	PAD_TOUCHES_BOX(p,b)	LINE_TOUCHES_BOX((LineTypePtr)(p),(b))
+
+/* a corner of either box is within the other, or edges cross */
+#define	BOX_TOUCHES_BOX(b1,b2)	\
+	(   POINT_IN_BOX((b1)->X1,(b1)->Y1,b2) || POINT_IN_BOX((b1)->X1,(b1)->Y2,b2) || POINT_IN_BOX((b1)->X2,(b1)->Y1,b2) || POINT_IN_BOX((b1)->X2,(b1)->Y2,b2)  \
+	 || POINT_IN_BOX((b2)->X1,(b2)->Y1,b1) || POINT_IN_BOX((b2)->X1,(b2)->Y2,b1) || POINT_IN_BOX((b2)->X2,(b2)->Y1,b1) || POINT_IN_BOX((b2)->X2,(b2)->Y2,b1)  \
+	 || lines_intersect((b1)->X1,(b1)->Y1, (b1)->X2,(b1)->Y1, (b2)->X1,(b2)->Y1, (b2)->X1,(b2)->Y2) \
+	 || lines_intersect((b2)->X1,(b2)->Y1, (b2)->X2,(b2)->Y1, (b1)->X1,(b1)->Y1, (b1)->X1,(b1)->Y2))
+
+#define	TEXT_TOUCHES_BOX(t,b)	\
+	(BOX_TOUCHES_BOX(&((t)->BoundingBox), (b)))
+
+#define	POLYGON_TOUCHES_BOX(p,b)	\
+	(BOX_TOUCHES_BOX(&((p)->BoundingBox), (b)))
+
+#define	ELEMENT_TOUCHES_BOX(e,b)	\
+	(BOX_TOUCHES_BOX(&((e)->BoundingBox), (b)))
+
+#define ARC_TOUCHES_BOX(a,b)		\
+	(BOX_TOUCHES_BOX(&((a)->BoundingBox), (b)))
+
+
+/* == the combination of *_IN_* and *_TOUCHES_*: use IN for positive boxes == */
+#define IS_BOX_NEGATIVE(b) (((b)->X2 < (b)->X1) || ((b)->Y2 < (b)->Y1))
+
+#define	BOX_NEAR_BOX(b1,b) \
+	(IS_BOX_NEGATIVE(b) ? BOX_TOUCHES_BOX(b1,b) : BOX_IN_BOX(b1,b))
+
+#define	VIA_OR_PIN_NEAR_BOX(v,b) \
+	(IS_BOX_NEGATIVE(b) ? VIA_OR_PIN_TOUCHES_BOX(v,b) : VIA_OR_PIN_IN_BOX(v,b))
+
+#define	LINE_NEAR_BOX(l,b) \
+	(IS_BOX_NEGATIVE(b) ? LINE_TOUCHES_BOX(l,b) : LINE_IN_BOX(l,b))
+
+#define	PAD_NEAR_BOX(p,b) \
+	(IS_BOX_NEGATIVE(b) ? PAD_TOUCHES_BOX(p,b) : PAD_IN_BOX(p,b))
+
+#define	TEXT_NEAR_BOX(t,b) \
+	(IS_BOX_NEGATIVE(b) ? TEXT_TOUCHES_BOX(t,b) : TEXT_IN_BOX(t,b))
+
+#define	POLYGON_NEAR_BOX(p,b) \
+	(IS_BOX_NEGATIVE(b) ? POLYGON_TOUCHES_BOX(p,b) : POLYGON_IN_BOX(p,b))
+
+#define	ELEMENT_NEAR_BOX(e,b) \
+	(IS_BOX_NEGATIVE(b) ? ELEMENT_TOUCHES_BOX(e,b) : ELEMENT_IN_BOX(e,b))
+
+#define ARC_NEAR_BOX(a,b) \
+	(IS_BOX_NEGATIVE(b) ? ARC_TOUCHES_BOX(a,b) : ARC_IN_BOX(a,b))
+
+
+
+/* ---------------------------------------------------------------------------
+ * prototypes
+ */
+pcb_bool IsPointOnLine(Coord, Coord, Coord, LineTypePtr);
+pcb_bool IsPointOnPin(Coord, Coord, Coord, PinTypePtr);
+pcb_bool IsPointOnArc(Coord, Coord, Coord, ArcTypePtr);
+pcb_bool IsPointOnLineEnd(Coord, Coord, RatTypePtr);
+pcb_bool IsLineInRectangle(Coord, Coord, Coord, Coord, LineTypePtr);
+pcb_bool IsLineInQuadrangle(PointType p[4], LineTypePtr Line);
+pcb_bool IsArcInRectangle(Coord, Coord, Coord, Coord, ArcTypePtr);
+pcb_bool IsPointInPad(Coord, Coord, Coord, PadTypePtr);
+pcb_bool IsPointInBox(Coord, Coord, BoxTypePtr, Coord);
+int SearchObjectByLocation(unsigned, void **, void **, void **, Coord, Coord, Coord);
+int SearchScreen(Coord, Coord, int, void **, void **, void **);
+int SearchScreenGridSlop(Coord, Coord, int, void **, void **, void **);
+int SearchObjectByID(DataTypePtr, void **, void **, void **, int, int);
+ElementTypePtr SearchElementByName(DataTypePtr, const char *);
+
+#endif
diff --git a/src/select.c b/src/select.c
new file mode 100644
index 0000000..75dcbfe
--- /dev/null
+++ b/src/select.c
@@ -0,0 +1,964 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* select routines
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "search.h"
+#include "select.h"
+#include "undo.h"
+#include "rats.h"
+#include "misc.h"
+#include "find.h"
+#include "layer.h"
+#include "compat_misc.h"
+
+#include <genregex/regex_sei.h>
+
+void pcb_select_element(ElementType *element, pcb_change_flag_t how, int redraw)
+{
+	/* select all pins and names of the element */
+	PIN_LOOP(element);
+	{
+		AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+		CHANGE_FLAG(how, PCB_FLAG_SELECTED, pin);
+	}
+	END_LOOP;
+	PAD_LOOP(element);
+	{
+		AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+		CHANGE_FLAG(how, PCB_FLAG_SELECTED, pad);
+	}
+	END_LOOP;
+	ELEMENTTEXT_LOOP(element);
+	{
+		AddObjectToFlagUndoList(PCB_TYPE_ELEMENT_NAME, element, text, text);
+		CHANGE_FLAG(how, PCB_FLAG_SELECTED, text);
+	}
+	END_LOOP;
+	AddObjectToFlagUndoList(PCB_TYPE_ELEMENT, element, element, element);
+	CHANGE_FLAG(how, PCB_FLAG_SELECTED, element);
+
+	if (redraw) {
+		if (PCB->ElementOn && ((TEST_FLAG(PCB_FLAG_ONSOLDER, element) != 0) == SWAP_IDENT || PCB->InvisibleObjectsOn))
+			if (PCB->ElementOn) {
+				DrawElementName(element);
+				DrawElementPackage(element);
+			}
+		if (PCB->PinOn)
+			DrawElementPinsAndPads(element);
+	}
+}
+
+void pcb_select_element_name(ElementType *element, pcb_change_flag_t how, int redraw)
+{
+	/* select all names of the element */
+	ELEMENTTEXT_LOOP(element);
+	{
+		AddObjectToFlagUndoList(PCB_TYPE_ELEMENT_NAME, element, text, text);
+		CHANGE_FLAG(how, PCB_FLAG_SELECTED, text);
+	}
+	END_LOOP;
+
+	if (redraw)
+		DrawElementName(element);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * toggles the selection of any kind of object
+ * the different types are defined by search.h
+ */
+pcb_bool SelectObject(void)
+{
+	void *ptr1, *ptr2, *ptr3;
+	LayerTypePtr layer;
+	int type;
+
+	pcb_bool changed = pcb_true;
+
+	type = SearchScreen(Crosshair.X, Crosshair.Y, SELECT_TYPES, &ptr1, &ptr2, &ptr3);
+	if (type == PCB_TYPE_NONE || TEST_FLAG(PCB_FLAG_LOCK, (PinTypePtr) ptr2))
+		return (pcb_false);
+	switch (type) {
+	case PCB_TYPE_VIA:
+		AddObjectToFlagUndoList(PCB_TYPE_VIA, ptr1, ptr1, ptr1);
+		TOGGLE_FLAG(PCB_FLAG_SELECTED, (PinTypePtr) ptr1);
+		DrawVia((PinTypePtr) ptr1);
+		break;
+
+	case PCB_TYPE_LINE:
+		{
+			LineType *line = (LineTypePtr) ptr2;
+
+			layer = (LayerTypePtr) ptr1;
+			AddObjectToFlagUndoList(PCB_TYPE_LINE, ptr1, ptr2, ptr2);
+			TOGGLE_FLAG(PCB_FLAG_SELECTED, line);
+			DrawLine(layer, line);
+			break;
+		}
+
+	case PCB_TYPE_RATLINE:
+		{
+			RatTypePtr rat = (RatTypePtr) ptr2;
+
+			AddObjectToFlagUndoList(PCB_TYPE_RATLINE, ptr1, ptr1, ptr1);
+			TOGGLE_FLAG(PCB_FLAG_SELECTED, rat);
+			DrawRat(rat);
+			break;
+		}
+
+	case PCB_TYPE_ARC:
+		{
+			ArcType *arc = (ArcTypePtr) ptr2;
+
+			layer = (LayerTypePtr) ptr1;
+			AddObjectToFlagUndoList(PCB_TYPE_ARC, ptr1, ptr2, ptr2);
+			TOGGLE_FLAG(PCB_FLAG_SELECTED, arc);
+			DrawArc(layer, arc);
+			break;
+		}
+
+	case PCB_TYPE_TEXT:
+		{
+			TextType *text = (TextTypePtr) ptr2;
+
+			layer = (LayerTypePtr) ptr1;
+			AddObjectToFlagUndoList(PCB_TYPE_TEXT, ptr1, ptr2, ptr2);
+			TOGGLE_FLAG(PCB_FLAG_SELECTED, text);
+			DrawText(layer, text);
+			break;
+		}
+
+	case PCB_TYPE_POLYGON:
+		{
+			PolygonType *poly = (PolygonTypePtr) ptr2;
+
+			layer = (LayerTypePtr) ptr1;
+			AddObjectToFlagUndoList(PCB_TYPE_POLYGON, ptr1, ptr2, ptr2);
+			TOGGLE_FLAG(PCB_FLAG_SELECTED, poly);
+			DrawPolygon(layer, poly);
+			/* changing memory order no longer effects draw order */
+			break;
+		}
+
+	case PCB_TYPE_PIN:
+		AddObjectToFlagUndoList(PCB_TYPE_PIN, ptr1, ptr2, ptr2);
+		TOGGLE_FLAG(PCB_FLAG_SELECTED, (PinTypePtr) ptr2);
+		DrawPin((PinTypePtr) ptr2);
+		break;
+
+	case PCB_TYPE_PAD:
+		AddObjectToFlagUndoList(PCB_TYPE_PAD, ptr1, ptr2, ptr2);
+		TOGGLE_FLAG(PCB_FLAG_SELECTED, (PadTypePtr) ptr2);
+		DrawPad((PadTypePtr) ptr2);
+		break;
+
+	case PCB_TYPE_ELEMENT_NAME:
+		pcb_select_element_name((ElementType *) ptr1, PCB_CHGFLG_TOGGLE, 1);
+		break;
+
+	case PCB_TYPE_ELEMENT:
+		pcb_select_element((ElementType *) ptr1, PCB_CHGFLG_TOGGLE, 1);
+		break;
+	}
+	Draw();
+	IncrementUndoSerialNumber();
+	return (changed);
+}
+
+/* ----------------------------------------------------------------------
+ * selects/unselects or lists visible objects within the passed box
+ * If len is NULL:
+ *  Flag determines if the block is to be selected or unselected
+ *  returns non-NULL if the state of any object has changed
+ * if len is non-NULL:
+ *  returns a list of object IDs matched the search and loads len with the
+ *  length of the list. Returns NULL on no match.
+ */
+static long int *ListBlock_(BoxTypePtr Box, pcb_bool Flag, int *len)
+{
+	int changed = 0;
+	int used = 0, alloced = 0;
+	long int *list = NULL;
+
+/* append an object to the return list OR set the flag if there's no list */
+#define append(undo_type, p1, obj) \
+do { \
+	if (len == NULL) { \
+		AddObjectToFlagUndoList (undo_type, p1, obj, obj); \
+		ASSIGN_FLAG (PCB_FLAG_SELECTED, Flag, obj); \
+	} \
+	else { \
+		if (used >= alloced) { \
+			alloced += 64; \
+			list = realloc(list, sizeof(*list) * alloced); \
+		} \
+		list[used] = obj->ID; \
+		used++; \
+	} \
+	changed = 1; \
+} while(0)
+
+	if (IS_BOX_NEGATIVE(Box) && ((Box->X1 == Box->X2) || (Box->Y2 == Box->Y1))) {
+		if (len != NULL)
+			*len = 0;
+		return NULL;
+	}
+
+	if (PCB->RatOn || !Flag)
+		RAT_LOOP(PCB->Data);
+	{
+		if (LINE_NEAR_BOX((LineTypePtr) line, Box) && !TEST_FLAG(PCB_FLAG_LOCK, line) && TEST_FLAG(PCB_FLAG_SELECTED, line) != Flag) {
+			append(PCB_TYPE_RATLINE, line, line);
+			if (PCB->RatOn)
+				DrawRat(line);
+		}
+	}
+	END_LOOP;
+
+	/* check layers */
+	LAYER_LOOP(PCB->Data, max_copper_layer + 2);
+	{
+		if (layer == &PCB->Data->SILKLAYER) {
+			if (!(PCB->ElementOn || !Flag))
+				continue;
+		}
+		else if (layer == &PCB->Data->BACKSILKLAYER) {
+			if (!(PCB->InvisibleObjectsOn || !Flag))
+				continue;
+		}
+		else if (!(layer->On || !Flag))
+			continue;
+
+		LINE_LOOP(layer);
+		{
+			if (LINE_NEAR_BOX(line, Box)
+					&& !TEST_FLAG(PCB_FLAG_LOCK, line)
+					&& TEST_FLAG(PCB_FLAG_SELECTED, line) != Flag) {
+				append(PCB_TYPE_LINE, layer, line);
+				if (layer->On)
+					DrawLine(layer, line);
+			}
+		}
+		END_LOOP;
+		ARC_LOOP(layer);
+		{
+			if (ARC_NEAR_BOX(arc, Box)
+					&& !TEST_FLAG(PCB_FLAG_LOCK, arc)
+					&& TEST_FLAG(PCB_FLAG_SELECTED, arc) != Flag) {
+				append(PCB_TYPE_ARC, layer, arc);
+				if (layer->On)
+					DrawArc(layer, arc);
+			}
+		}
+		END_LOOP;
+		TEXT_LOOP(layer);
+		{
+			if (!Flag || TEXT_IS_VISIBLE(PCB, layer, text)) {
+				if (TEXT_NEAR_BOX(text, Box)
+						&& !TEST_FLAG(PCB_FLAG_LOCK, text)
+						&& TEST_FLAG(PCB_FLAG_SELECTED, text) != Flag) {
+					append(PCB_TYPE_TEXT, layer, text);
+					if (TEXT_IS_VISIBLE(PCB, layer, text))
+						DrawText(layer, text);
+				}
+			}
+		}
+		END_LOOP;
+		POLYGON_LOOP(layer);
+		{
+			if (POLYGON_NEAR_BOX(polygon, Box)
+					&& !TEST_FLAG(PCB_FLAG_LOCK, polygon)
+					&& TEST_FLAG(PCB_FLAG_SELECTED, polygon) != Flag) {
+				append(PCB_TYPE_POLYGON, layer, polygon);
+				if (layer->On)
+					DrawPolygon(layer, polygon);
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	/* elements */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		{
+			pcb_bool gotElement = pcb_false;
+			if ((PCB->ElementOn || !Flag)
+					&& !TEST_FLAG(PCB_FLAG_LOCK, element)
+					&& ((TEST_FLAG(PCB_FLAG_ONSOLDER, element) != 0) == SWAP_IDENT || PCB->InvisibleObjectsOn)) {
+				if (BOX_NEAR_BOX(&ELEMENT_TEXT(PCB, element).BoundingBox, Box)
+						&& !TEST_FLAG(PCB_FLAG_LOCK, &ELEMENT_TEXT(PCB, element))
+						&& TEST_FLAG(PCB_FLAG_SELECTED, &ELEMENT_TEXT(PCB, element)) != Flag) {
+					/* select all names of element */
+					ELEMENTTEXT_LOOP(element);
+					{
+						append(PCB_TYPE_ELEMENT_NAME, element, text);
+					}
+					END_LOOP;
+					if (PCB->ElementOn)
+						DrawElementName(element);
+				}
+				if ((PCB->PinOn || !Flag) && ELEMENT_NEAR_BOX(element, Box))
+					if (TEST_FLAG(PCB_FLAG_SELECTED, element) != Flag) {
+						append(PCB_TYPE_ELEMENT, element, element);
+						PIN_LOOP(element);
+						{
+							if (TEST_FLAG(PCB_FLAG_SELECTED, pin) != Flag) {
+								append(PCB_TYPE_PIN, element, pin);
+								if (PCB->PinOn)
+									DrawPin(pin);
+							}
+						}
+						END_LOOP;
+						PAD_LOOP(element);
+						{
+							if (TEST_FLAG(PCB_FLAG_SELECTED, pad) != Flag) {
+								append(PCB_TYPE_PAD, element, pad);
+								if (PCB->PinOn)
+									DrawPad(pad);
+							}
+						}
+						END_LOOP;
+						if (PCB->PinOn)
+							DrawElement(element);
+						gotElement = pcb_true;
+					}
+			}
+			if ((PCB->PinOn || !Flag) && !TEST_FLAG(PCB_FLAG_LOCK, element) && !gotElement) {
+				PIN_LOOP(element);
+				{
+					if ((VIA_OR_PIN_NEAR_BOX(pin, Box)
+							 && TEST_FLAG(PCB_FLAG_SELECTED, pin) != Flag)) {
+						append(PCB_TYPE_PIN, element, pin);
+						if (PCB->PinOn)
+							DrawPin(pin);
+					}
+				}
+				END_LOOP;
+				PAD_LOOP(element);
+				{
+					if (PAD_NEAR_BOX(pad, Box)
+							&& TEST_FLAG(PCB_FLAG_SELECTED, pad) != Flag
+							&& (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) == SWAP_IDENT || PCB->InvisibleObjectsOn || !Flag)) {
+						append(PCB_TYPE_PAD, element, pad);
+						if (PCB->PinOn)
+							DrawPad(pad);
+					}
+				}
+				END_LOOP;
+			}
+		}
+	}
+	END_LOOP;
+	/* end with vias */
+	if (PCB->ViaOn || !Flag)
+		VIA_LOOP(PCB->Data);
+	{
+		if (VIA_OR_PIN_NEAR_BOX(via, Box)
+				&& !TEST_FLAG(PCB_FLAG_LOCK, via)
+				&& TEST_FLAG(PCB_FLAG_SELECTED, via) != Flag) {
+			append(PCB_TYPE_VIA, via, via);
+			if (PCB->ViaOn)
+				DrawVia(via);
+		}
+	}
+	END_LOOP;
+
+	if (changed) {
+		Draw();
+		IncrementUndoSerialNumber();
+	}
+
+	if (len == NULL) {
+		static long int non_zero;
+		return (changed ? &non_zero : NULL);
+	}
+	else {
+		*len = used;
+		return list;
+	}
+}
+
+#undef append
+
+/* ----------------------------------------------------------------------
+ * selects/unselects all visible objects within the passed box
+ * Flag determines if the block is to be selected or unselected
+ * returns pcb_true if the state of any object has changed
+ */
+pcb_bool SelectBlock(BoxTypePtr Box, pcb_bool Flag)
+{
+	/* do not list, set flag */
+	return (ListBlock_(Box, Flag, NULL) == NULL) ? pcb_false : pcb_true;
+}
+
+/* ----------------------------------------------------------------------
+ * List all visible objects within the passed box
+ */
+long int *ListBlock(BoxTypePtr Box, int *len)
+{
+	return ListBlock_(Box, 1, len);
+}
+
+/* ----------------------------------------------------------------------
+ * performs several operations on the passed object
+ */
+void *ObjectOperation(ObjectFunctionTypePtr F, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	switch (Type) {
+	case PCB_TYPE_LINE:
+		if (F->Line)
+			return (F->Line((LayerTypePtr) Ptr1, (LineTypePtr) Ptr2));
+		break;
+
+	case PCB_TYPE_ARC:
+		if (F->Arc)
+			return (F->Arc((LayerTypePtr) Ptr1, (ArcTypePtr) Ptr2));
+		break;
+
+	case PCB_TYPE_LINE_POINT:
+		if (F->LinePoint)
+			return (F->LinePoint((LayerTypePtr) Ptr1, (LineTypePtr) Ptr2, (PointTypePtr) Ptr3));
+		break;
+
+	case PCB_TYPE_TEXT:
+		if (F->Text)
+			return (F->Text((LayerTypePtr) Ptr1, (TextTypePtr) Ptr2));
+		break;
+
+	case PCB_TYPE_POLYGON:
+		if (F->Polygon)
+			return (F->Polygon((LayerTypePtr) Ptr1, (PolygonTypePtr) Ptr2));
+		break;
+
+	case PCB_TYPE_POLYGON_POINT:
+		if (F->Point)
+			return (F->Point((LayerTypePtr) Ptr1, (PolygonTypePtr) Ptr2, (PointTypePtr) Ptr3));
+		break;
+
+	case PCB_TYPE_VIA:
+		if (F->Via)
+			return (F->Via((PinTypePtr) Ptr1));
+		break;
+
+	case PCB_TYPE_ELEMENT:
+		if (F->Element)
+			return (F->Element((ElementTypePtr) Ptr1));
+		break;
+
+	case PCB_TYPE_PIN:
+		if (F->Pin)
+			return (F->Pin((ElementTypePtr) Ptr1, (PinTypePtr) Ptr2));
+		break;
+
+	case PCB_TYPE_PAD:
+		if (F->Pad)
+			return (F->Pad((ElementTypePtr) Ptr1, (PadTypePtr) Ptr2));
+		break;
+
+	case PCB_TYPE_ELEMENT_NAME:
+		if (F->ElementName)
+			return (F->ElementName((ElementTypePtr) Ptr1));
+		break;
+
+	case PCB_TYPE_RATLINE:
+		if (F->Rat)
+			return (F->Rat((RatTypePtr) Ptr1));
+		break;
+	}
+	return (NULL);
+}
+
+/* ----------------------------------------------------------------------
+ * performs several operations on selected objects which are also visible
+ * The lowlevel procedures are passed together with additional information
+ * resets the selected flag if requested
+ * returns pcb_true if anything has changed
+ */
+pcb_bool SelectedOperation(ObjectFunctionTypePtr F, pcb_bool Reset, int type)
+{
+	pcb_bool changed = pcb_false;
+
+	/* check lines */
+	if (type & PCB_TYPE_LINE && F->Line)
+		VISIBLELINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, line)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_LINE, layer, line, line);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, line);
+			}
+			F->Line(layer, line);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+
+	/* check arcs */
+	if (type & PCB_TYPE_ARC && F->Arc)
+		VISIBLEARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, arc)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_ARC, layer, arc, arc);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, arc);
+			}
+			F->Arc(layer, arc);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+
+	/* check text */
+	if (type & PCB_TYPE_TEXT && F->Text)
+		ALLTEXT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, text) && TEXT_IS_VISIBLE(PCB, layer, text)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_TEXT, layer, text, text);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, text);
+			}
+			F->Text(layer, text);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+
+	/* check polygons */
+	if (type & PCB_TYPE_POLYGON && F->Polygon)
+		VISIBLEPOLYGON_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, polygon)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_POLYGON, layer, polygon, polygon);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, polygon);
+			}
+			F->Polygon(layer, polygon);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+
+	/* elements silkscreen */
+	if (type & PCB_TYPE_ELEMENT && PCB->ElementOn && F->Element)
+		ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, element)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_ELEMENT, element, element, element);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, element);
+			}
+			F->Element(element);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	if (type & PCB_TYPE_ELEMENT_NAME && PCB->ElementOn && F->ElementName)
+		ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, &ELEMENT_TEXT(PCB, element))) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_ELEMENT_NAME, element, &ELEMENT_TEXT(PCB, element), &ELEMENT_TEXT(PCB, element));
+				CLEAR_FLAG(PCB_FLAG_SELECTED, &ELEMENT_TEXT(PCB, element));
+			}
+			F->ElementName(element);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+
+	if (type & PCB_TYPE_PIN && PCB->PinOn && F->Pin)
+		ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_SELECTED, pin)) {
+				if (Reset) {
+					AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+					CLEAR_FLAG(PCB_FLAG_SELECTED, pin);
+				}
+				F->Pin(element, pin);
+				changed = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	if (type & PCB_TYPE_PAD && PCB->PinOn && F->Pad)
+		ELEMENT_LOOP(PCB->Data);
+	{
+		PAD_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_SELECTED, pad)) {
+				if (Reset) {
+					AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+					CLEAR_FLAG(PCB_FLAG_SELECTED, pad);
+				}
+				F->Pad(element, pad);
+				changed = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	/* process vias */
+	if (type & PCB_TYPE_VIA && PCB->ViaOn && F->Via)
+		VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, via)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, via);
+			}
+			F->Via(via);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	/* and rat-lines */
+	if (type & PCB_TYPE_RATLINE && PCB->RatOn && F->Rat)
+		RAT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, line)) {
+			if (Reset) {
+				AddObjectToFlagUndoList(PCB_TYPE_RATLINE, line, line, line);
+				CLEAR_FLAG(PCB_FLAG_SELECTED, line);
+			}
+			F->Rat(line);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	if (Reset && changed)
+		IncrementUndoSerialNumber();
+	return (changed);
+}
+
+/* ----------------------------------------------------------------------
+ * selects/unselects all objects which were found during a connection scan
+ * Flag determines if they are to be selected or unselected
+ * returns pcb_true if the state of any object has changed
+ *
+ * text objects and elements cannot be selected by this routine
+ */
+pcb_bool SelectConnection(pcb_bool Flag)
+{
+	pcb_bool changed = pcb_false;
+
+	if (PCB->RatOn)
+		RAT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, line)) {
+			AddObjectToFlagUndoList(PCB_TYPE_RATLINE, line, line, line);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, line);
+			DrawRat(line);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+
+	VISIBLELINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, line) && !TEST_FLAG(PCB_FLAG_LOCK, line)) {
+			AddObjectToFlagUndoList(PCB_TYPE_LINE, layer, line, line);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, line);
+			DrawLine(layer, line);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	VISIBLEARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, arc) && !TEST_FLAG(PCB_FLAG_LOCK, arc)) {
+			AddObjectToFlagUndoList(PCB_TYPE_ARC, layer, arc, arc);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, arc);
+			DrawArc(layer, arc);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	VISIBLEPOLYGON_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, polygon) && !TEST_FLAG(PCB_FLAG_LOCK, polygon)) {
+			AddObjectToFlagUndoList(PCB_TYPE_POLYGON, layer, polygon, polygon);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, polygon);
+			DrawPolygon(layer, polygon);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+
+	if (PCB->PinOn && PCB->ElementOn) {
+		ALLPIN_LOOP(PCB->Data);
+		{
+			if (!TEST_FLAG(PCB_FLAG_LOCK, element) && TEST_FLAG(PCB_FLAG_FOUND, pin)) {
+				AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+				ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, pin);
+				DrawPin(pin);
+				changed = pcb_true;
+			}
+		}
+		ENDALL_LOOP;
+		ALLPAD_LOOP(PCB->Data);
+		{
+			if (!TEST_FLAG(PCB_FLAG_LOCK, element) && TEST_FLAG(PCB_FLAG_FOUND, pad)) {
+				AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+				ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, pad);
+				DrawPad(pad);
+				changed = pcb_true;
+			}
+		}
+		ENDALL_LOOP;
+	}
+
+	if (PCB->ViaOn)
+		VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, via) && !TEST_FLAG(PCB_FLAG_LOCK, via)) {
+			AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, via);
+			DrawVia(via);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	return (changed);
+}
+
+/* ---------------------------------------------------------------------------
+ * selects objects as defined by Type by name;
+ * it's a case insensitive match
+ * returns pcb_true if any object has been selected
+ */
+#define REGEXEC(arg) \
+	(method == SM_REGEX ? regexec_match_all(regex, (arg)) : strlst_match(pat, (arg)))
+
+static int regexec_match_all(re_sei_t *preg, const char *string)
+{
+	return !!re_sei_exec(preg, string);
+}
+
+/* case insensitive match of each element in the array pat against name
+   returns 1 if any of them matched */
+static int strlst_match(const char **pat, const char *name)
+{
+	for (; *pat != NULL; pat++)
+		if (strcasecmp(*pat, name) == 0)
+			return 1;
+	return 0;
+}
+
+pcb_bool SelectObjectByName(int Type, const char *name_pattern, pcb_bool Flag, search_method_t method)
+{
+	pcb_bool changed = pcb_false;
+	const char **pat = NULL;
+	char *pattern_copy = NULL;
+	re_sei_t *regex;
+
+	if (method == SM_REGEX) {
+		/* compile the regular expression */
+		regex = re_sei_comp(name_pattern);
+		if (re_sei_errno(regex) != 0) {
+			Message(PCB_MSG_DEFAULT, _("regexp error: %s\n"), re_error_str(re_sei_errno(regex)));
+			re_sei_free(regex);
+			return (pcb_false);
+		}
+	}
+	else {
+		char *s, *next;
+		int n, w;
+
+		/* We're going to mess with the delimiters. Create a copy. */
+		pattern_copy = pcb_strdup(name_pattern);
+
+		/* count the number of patterns */
+		for (s = pattern_copy, w = 0; *s != '\0'; s++) {
+			if (*s == '|')
+				w++;
+		}
+
+		pat = malloc((w + 2) * sizeof(char *));	/* make room for the NULL too */
+		for (s = pattern_copy, n = 0; s != NULL; s = next) {
+			char *end;
+/*fprintf(stderr, "S: '%s'\n", s, next);*/
+			while (isspace(*s))
+				s++;
+			next = strchr(s, '|');
+			if (next != NULL) {
+				*next = '\0';
+				next++;
+			}
+			end = s + strlen(s) - 1;
+			while ((end >= s) && (isspace(*end))) {
+				*end = '\0';
+				end--;
+			}
+			if (*s != '\0') {
+				pat[n] = s;
+				n++;
+			}
+		}
+		pat[n] = NULL;
+	}
+
+	/* loop over all visible objects with names */
+	if (Type & PCB_TYPE_TEXT)
+		ALLTEXT_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_LOCK, text)
+				&& TEXT_IS_VISIBLE(PCB, layer, text)
+				&& text->TextString && REGEXEC(text->TextString)
+				&& TEST_FLAG(PCB_FLAG_SELECTED, text) != Flag) {
+			AddObjectToFlagUndoList(PCB_TYPE_TEXT, layer, text, text);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, text);
+			DrawText(layer, text);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+
+	if (PCB->ElementOn && (Type & PCB_TYPE_ELEMENT))
+		ELEMENT_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_LOCK, element)
+				&& ((TEST_FLAG(PCB_FLAG_ONSOLDER, element) != 0) == SWAP_IDENT || PCB->InvisibleObjectsOn)
+				&& TEST_FLAG(PCB_FLAG_SELECTED, element) != Flag) {
+			const char* name = ELEMENT_NAME(PCB, element);
+			if (name && REGEXEC(name)) {
+				AddObjectToFlagUndoList(PCB_TYPE_ELEMENT, element, element, element);
+				ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, element);
+				PIN_LOOP(element);
+				{
+					AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+					ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, pin);
+				}
+				END_LOOP;
+				PAD_LOOP(element);
+				{
+					AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+					ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, pad);
+				}
+				END_LOOP;
+				ELEMENTTEXT_LOOP(element);
+				{
+					AddObjectToFlagUndoList(PCB_TYPE_ELEMENT_NAME, element, text, text);
+					ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, text);
+				}
+				END_LOOP;
+				DrawElementName(element);
+				DrawElement(element);
+				changed = pcb_true;
+			}
+		}
+	}
+	END_LOOP;
+	if (PCB->PinOn && (Type & PCB_TYPE_PIN))
+		ALLPIN_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_LOCK, element)
+				&& pin->Name && REGEXEC(pin->Name)
+				&& TEST_FLAG(PCB_FLAG_SELECTED, pin) != Flag) {
+			AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, pin);
+			DrawPin(pin);
+			changed = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	if (PCB->PinOn && (Type & PCB_TYPE_PAD))
+		ALLPAD_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_LOCK, element)
+				&& ((TEST_FLAG(PCB_FLAG_ONSOLDER, pad) != 0) == SWAP_IDENT || PCB->InvisibleObjectsOn)
+				&& TEST_FLAG(PCB_FLAG_SELECTED, pad) != Flag)
+			if (pad->Name && REGEXEC(pad->Name)) {
+				AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+				ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, pad);
+				DrawPad(pad);
+				changed = pcb_true;
+			}
+	}
+	ENDALL_LOOP;
+	if (PCB->ViaOn && (Type & PCB_TYPE_VIA))
+		VIA_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_LOCK, via)
+				&& via->Name && REGEXEC(via->Name) && TEST_FLAG(PCB_FLAG_SELECTED, via) != Flag) {
+			AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+			ASSIGN_FLAG(PCB_FLAG_SELECTED, Flag, via);
+			DrawVia(via);
+			changed = pcb_true;
+		}
+	}
+	END_LOOP;
+	if (Type & PCB_TYPE_NET) {
+		InitConnectionLookup();
+		changed = ResetConnections(pcb_true) || changed;
+
+		MENU_LOOP(&(PCB->NetlistLib[NETLIST_EDITED]));
+		{
+			pcb_cardinal_t i;
+			LibraryEntryType *entry;
+			ConnectionType conn;
+
+			/* Name[0] and Name[1] are special purpose, not the actual name */
+			if (menu->Name && menu->Name[0] != '\0' && menu->Name[1] != '\0' && REGEXEC(menu->Name + 2)) {
+				for (i = menu->EntryN, entry = menu->Entry; i; i--, entry++)
+					if (SeekPad(entry, &conn, pcb_false))
+						RatFindHook(conn.type, conn.ptr1, conn.ptr2, conn.ptr2, pcb_true, pcb_true);
+			}
+		}
+		END_LOOP;
+
+		changed = SelectConnection(Flag) || changed;
+		changed = ResetConnections(pcb_false) || changed;
+		FreeConnectionLookupMemory();
+	}
+
+	if (method == SM_REGEX)
+		re_sei_free(regex);
+
+	if (changed) {
+		IncrementUndoSerialNumber();
+		Draw();
+	}
+	if (pat != NULL)
+		free(pat);
+	if (pattern_copy != NULL)
+		free(pattern_copy);
+	return (changed);
+}
diff --git a/src/select.h b/src/select.h
new file mode 100644
index 0000000..1dcf424
--- /dev/null
+++ b/src/select.h
@@ -0,0 +1,58 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for select routines */
+
+#ifndef	PCB_SELECT_H
+#define	PCB_SELECT_H
+
+#include "global.h"
+
+#define SELECT_TYPES	\
+	(PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_TEXT | PCB_TYPE_POLYGON | PCB_TYPE_ELEMENT |	\
+	 PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_RATLINE | PCB_TYPE_ARC)
+
+pcb_bool SelectObject(void);
+pcb_bool SelectBlock(BoxTypePtr, pcb_bool);
+long int *ListBlock(BoxTypePtr Box, int *len);
+pcb_bool SelectedOperation(ObjectFunctionTypePtr, pcb_bool, int);
+void *ObjectOperation(ObjectFunctionTypePtr, int, void *, void *, void *);
+pcb_bool SelectConnection(pcb_bool);
+
+typedef enum {
+	SM_REGEX = 0,
+	SM_LIST = 1
+} search_method_t;
+
+pcb_bool SelectObjectByName(int, const char *, pcb_bool, search_method_t);
+
+/* New API */
+
+/* Change the selection of an element or element name (these have side effects) */
+void pcb_select_element(ElementType *element, pcb_change_flag_t how, int redraw);
+void pcb_select_element_name(ElementType *element, pcb_change_flag_t how, int redraw);
+
+#endif
diff --git a/src/select_act.c b/src/select_act.c
new file mode 100644
index 0000000..35e7ff0
--- /dev/null
+++ b/src/select_act.c
@@ -0,0 +1,388 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "action_helper.h"
+#include "error.h"
+#include "undo.h"
+#include "funchash_core.h"
+
+#include "select.h"
+#include "crosshair.h"
+#include "set.h"
+#include "buffer.h"
+#include "draw.h"
+#include "remove.h"
+#include "copy.h"
+#include "hid_attrib.h"
+#include "compat_misc.h"
+
+/* --------------------------------------------------------------------------- */
+/* Ask the user for a search pattern */
+static char *gui_get_pat(search_method_t * method)
+{
+	const char *methods[] = { "regexp", "list of names", NULL };
+	HID_Attribute attrs[2];
+#define nattr sizeof(attrs)/sizeof(attrs[0])
+	static HID_Attr_Val results[nattr] = { 0 };
+
+	memset(attrs, 0, sizeof(attrs));
+
+	attrs[0].name = "Pattern";
+	attrs[0].help_text = "Name/refdes pattern";
+	attrs[0].type = HID_String;
+	attrs[0].default_val.str_value = results[0].str_value;
+
+	attrs[1].name = "Method";
+	attrs[1].help_text = "method of search: either regular expression or a list of full names separated by |";
+	attrs[1].type = HID_Enum;
+	attrs[1].enumerations = methods;
+	attrs[1].default_val.int_value = results[1].int_value;
+
+	gui->attribute_dialog(attrs, nattr, results, "Find element", "Find element by name");
+
+	*method = results[1].int_value;
+	if (results[0].str_value == NULL)
+		return NULL;
+	return pcb_strdup(results[0].str_value);
+#undef nattr
+}
+
+
+/* --------------------------------------------------------------------------- */
+
+static const char select_syntax[] =
+	"Select(Object|ToggleObject)\n"
+	"Select(All|Block|Connection)\n"
+	"Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
+	"Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
+	"Select(TextByName|ViaByName|NetByName)\n" "Select(TextByName|ViaByName|NetByName, Name)\n" "Select(Convert)";
+
+static const char select_help[] = "Toggles or sets the selection.";
+
+/* %start-doc actions Select
+
+ at table @code
+
+ at item ElementByName
+ at item ObjectByName
+ at item PadByName
+ at item PinByName
+ at item TextByName
+ at item ViaByName
+ at item NetByName
+
+These all rely on having a regular expression parser built into
+ at code{pcb}.  If the name is not specified then the user is prompted
+for a pattern, and all objects that match the pattern and are of the
+type specified are selected.
+
+ at item Object
+ at item ToggleObject
+Selects the object under the cursor.
+
+ at item Block
+Selects all objects in a rectangle indicated by the cursor.
+
+ at item All
+Selects all objects on the board.
+
+ at item Connection
+Selects all connections with the ``found'' flag set.
+
+ at item Convert
+Converts the selected objects to an element.  This uses the highest
+numbered paste buffer.
+
+ at end table
+
+%end-doc */
+
+static int ActionSelect(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		int type;
+
+		switch (funchash_get(function, NULL)) { /* select objects by their names */
+		case F_ElementByName:
+			type = PCB_TYPE_ELEMENT;
+			goto commonByName;
+		case F_ObjectByName:
+			type = PCB_TYPEMASK_ALL;
+			goto commonByName;
+		case F_PadByName:
+			type = PCB_TYPE_PAD;
+			goto commonByName;
+		case F_PinByName:
+			type = PCB_TYPE_PIN;
+			goto commonByName;
+		case F_TextByName:
+			type = PCB_TYPE_TEXT;
+			goto commonByName;
+		case F_ViaByName:
+			type = PCB_TYPE_VIA;
+			goto commonByName;
+		case F_NetByName:
+			type = PCB_TYPE_NET;
+			goto commonByName;
+
+		commonByName:
+			{
+				const char *pattern = ACTION_ARG(1);
+				search_method_t method = SM_REGEX;
+
+				if (pattern || (pattern = gui_get_pat(&method)) != NULL) {
+					if (SelectObjectByName(type, pattern, pcb_true, method))
+						SetChangedFlag(pcb_true);
+					if (ACTION_ARG(1) == NULL)
+						free((char*)pattern);
+				}
+				break;
+			}
+
+			/* select a single object */
+		case F_ToggleObject:
+		case F_Object:
+			if (SelectObject())
+				SetChangedFlag(pcb_true);
+			break;
+
+			/* all objects in block */
+		case F_Block:
+			{
+				BoxType box;
+
+				box.X1 = MIN(Crosshair.AttachedBox.Point1.X, Crosshair.AttachedBox.Point2.X);
+				box.Y1 = MIN(Crosshair.AttachedBox.Point1.Y, Crosshair.AttachedBox.Point2.Y);
+				box.X2 = MAX(Crosshair.AttachedBox.Point1.X, Crosshair.AttachedBox.Point2.X);
+				box.Y2 = MAX(Crosshair.AttachedBox.Point1.Y, Crosshair.AttachedBox.Point2.Y);
+				notify_crosshair_change(pcb_false);
+				NotifyBlock();
+				if (Crosshair.AttachedBox.State == STATE_THIRD && SelectBlock(&box, pcb_true)) {
+					SetChangedFlag(pcb_true);
+					Crosshair.AttachedBox.State = STATE_FIRST;
+				}
+				notify_crosshair_change(pcb_true);
+				break;
+			}
+
+			/* select all visible objects */
+		case F_All:
+			{
+				BoxType box;
+
+				box.X1 = -MAX_COORD;
+				box.Y1 = -MAX_COORD;
+				box.X2 = MAX_COORD;
+				box.Y2 = MAX_COORD;
+				if (SelectBlock(&box, pcb_true))
+					SetChangedFlag(pcb_true);
+				break;
+			}
+
+			/* all found connections */
+		case F_Connection:
+			if (SelectConnection(pcb_true)) {
+				Draw();
+				IncrementUndoSerialNumber();
+				SetChangedFlag(pcb_true);
+			}
+			break;
+
+		case F_Convert:
+			{
+				Coord x, y;
+				Note.Buffer = conf_core.editor.buffer_number;
+				SetBufferNumber(MAX_BUFFER - 1);
+				ClearBuffer(PASTEBUFFER);
+				gui->get_coords(_("Select the Element's Mark Location"), &x, &y);
+				x = GridFit(x, PCB->Grid, PCB->GridOffsetX);
+				y = GridFit(y, PCB->Grid, PCB->GridOffsetY);
+				AddSelectedToBuffer(PASTEBUFFER, x, y, pcb_true);
+				SaveUndoSerialNumber();
+				RemoveSelected();
+				ConvertBufferToElement(PASTEBUFFER);
+				RestoreUndoSerialNumber();
+				CopyPastebufferToLayout(x, y);
+				SetBufferNumber(Note.Buffer);
+			}
+			break;
+
+		default:
+			AFAIL(select);
+			break;
+		}
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char unselect_syntax[] =
+	"Unselect(All|Block|Connection)\n"
+	"Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
+	"Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
+	"Unselect(TextByName|ViaByName)\n" "Unselect(TextByName|ViaByName, Name)\n";
+
+static const char unselect_help[] = "Unselects the object at the pointer location or the specified objects.";
+
+/* %start-doc actions Unselect
+
+ at table @code
+
+ at item All
+Unselect all objects.
+
+ at item Block
+Unselect all objects in a rectangle given by the cursor.
+
+ at item Connection
+Unselect all connections with the ``found'' flag set.
+
+ at item ElementByName
+ at item ObjectByName
+ at item PadByName
+ at item PinByName
+ at item TextByName
+ at item ViaByName
+
+These all rely on having a regular expression parser built into
+ at code{pcb}.  If the name is not specified then the user is prompted
+for a pattern, and all objects that match the pattern and are of the
+type specified are unselected.
+
+
+ at end table
+
+%end-doc */
+
+static int ActionUnselect(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (function) {
+		int type;
+		switch (funchash_get(function, NULL)) {
+			/* select objects by their names */
+		case F_ElementByName:
+			type = PCB_TYPE_ELEMENT;
+			goto commonByName;
+		case F_ObjectByName:
+			type = PCB_TYPEMASK_ALL;
+			goto commonByName;
+		case F_PadByName:
+			type = PCB_TYPE_PAD;
+			goto commonByName;
+		case F_PinByName:
+			type = PCB_TYPE_PIN;
+			goto commonByName;
+		case F_TextByName:
+			type = PCB_TYPE_TEXT;
+			goto commonByName;
+		case F_ViaByName:
+			type = PCB_TYPE_VIA;
+			goto commonByName;
+		case F_NetByName:
+			type = PCB_TYPE_NET;
+			goto commonByName;
+
+		commonByName:
+			{
+				const char *pattern = ACTION_ARG(1);
+				search_method_t method = SM_REGEX;
+
+				if (pattern || (pattern = gui_get_pat(&method)) != NULL) {
+					if (SelectObjectByName(type, pattern, pcb_false, method))
+						SetChangedFlag(pcb_true);
+					if (ACTION_ARG(1) == NULL)
+						free((char*)pattern);
+				}
+				break;
+			}
+
+			/* all objects in block */
+		case F_Block:
+			{
+				BoxType box;
+
+				box.X1 = MIN(Crosshair.AttachedBox.Point1.X, Crosshair.AttachedBox.Point2.X);
+				box.Y1 = MIN(Crosshair.AttachedBox.Point1.Y, Crosshair.AttachedBox.Point2.Y);
+				box.X2 = MAX(Crosshair.AttachedBox.Point1.X, Crosshair.AttachedBox.Point2.X);
+				box.Y2 = MAX(Crosshair.AttachedBox.Point1.Y, Crosshair.AttachedBox.Point2.Y);
+				notify_crosshair_change(pcb_false);
+				NotifyBlock();
+				if (Crosshair.AttachedBox.State == STATE_THIRD && SelectBlock(&box, pcb_false)) {
+					SetChangedFlag(pcb_true);
+					Crosshair.AttachedBox.State = STATE_FIRST;
+				}
+				notify_crosshair_change(pcb_true);
+				break;
+			}
+
+			/* unselect all visible objects */
+		case F_All:
+			{
+				BoxType box;
+
+				box.X1 = -MAX_COORD;
+				box.Y1 = -MAX_COORD;
+				box.X2 = MAX_COORD;
+				box.Y2 = MAX_COORD;
+				if (SelectBlock(&box, pcb_false))
+					SetChangedFlag(pcb_true);
+				break;
+			}
+
+			/* all found connections */
+		case F_Connection:
+			if (SelectConnection(pcb_false)) {
+				Draw();
+				IncrementUndoSerialNumber();
+				SetChangedFlag(pcb_true);
+			}
+			break;
+
+		default:
+			AFAIL(unselect);
+			break;
+
+		}
+	}
+	return 0;
+}
+
+HID_Action select_action_list[] = {
+	{"Select", 0, ActionSelect,
+	 select_help, select_syntax}
+	,
+	{"Unselect", 0, ActionUnselect,
+	 unselect_help, unselect_syntax}
+};
+
+REGISTER_ACTIONS(select_action_list, NULL)
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 0000000..225adbf
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,278 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+/* routines to update widgets and global settings
+ * (except output window and dialogs)
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "action_helper.h"
+#include "buffer.h"
+#include "crosshair.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "set.h"
+#include "undo.h"
+#include "hid_actions.h"
+#include "route_style.h"
+
+static int mode_position = 0;
+static int mode_stack[MAX_MODESTACK_DEPTH];
+
+/* ---------------------------------------------------------------------------
+ * sets cursor grid with respect to grid offset values
+ */
+void SetGrid(Coord Grid, pcb_bool align)
+{
+	if (Grid >= 1 && Grid <= MAX_GRID) {
+		if (align) {
+			PCB->GridOffsetX = Crosshair.X % Grid;
+			PCB->GridOffsetY = Crosshair.Y % Grid;
+		}
+		PCB->Grid = Grid;
+		conf_set_design("editor/grid", "%$mS", Grid);
+		if (conf_core.editor.draw_grid)
+			Redraw();
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets a new line thickness
+ */
+void SetLineSize(Coord Size)
+{
+	if (Size >= MIN_LINESIZE && Size <= MAX_LINESIZE) {
+		conf_set_design("design/line_thickness", "%$mS", Size);
+		if (conf_core.editor.auto_drc)
+			FitCrosshairIntoGrid(Crosshair.X, Crosshair.Y);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets a new via thickness
+ */
+void SetViaSize(Coord Size, pcb_bool Force)
+{
+	if (Force || (Size <= MAX_PINORVIASIZE && Size >= MIN_PINORVIASIZE && Size >= conf_core.design.via_drilling_hole + MIN_PINORVIACOPPER)) {
+		conf_set_design("design/via_thickness", "%$mS", Size);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets a new via drilling hole
+ */
+void SetViaDrillingHole(Coord Size, pcb_bool Force)
+{
+	if (Force || (Size <= MAX_PINORVIASIZE && Size >= MIN_PINORVIAHOLE && Size <= conf_core.design.via_thickness - MIN_PINORVIACOPPER)) {
+		conf_set_design("design/via_drilling_hole", "%$mS", Size);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets a clearance width
+ */
+void SetClearanceWidth(Coord Width)
+{
+	if (Width <= MAX_LINESIZE) {
+		conf_set_design("design/clearance", "%$mS", Width);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets a text scaling
+ */
+void SetTextScale(int Scale)
+{
+	if (Scale <= MAX_TEXTSCALE && Scale >= MIN_TEXTSCALE) {
+		conf_set_design("design/text_scale", "%d", Scale);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets or resets changed flag and redraws status
+ */
+void SetChangedFlag(pcb_bool New)
+{
+	if (PCB->Changed != New) {
+		PCB->Changed = New;
+
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets the crosshair range to the current buffer extents 
+ */
+void SetCrosshairRangeToBuffer(void)
+{
+	if (conf_core.editor.mode == PCB_MODE_PASTE_BUFFER) {
+		SetBufferBoundingBox(PASTEBUFFER);
+		SetCrosshairRange(PASTEBUFFER->X - PASTEBUFFER->BoundingBox.X1,
+											PASTEBUFFER->Y - PASTEBUFFER->BoundingBox.Y1,
+											PCB->MaxWidth -
+											(PASTEBUFFER->BoundingBox.X2 - PASTEBUFFER->X),
+											PCB->MaxHeight - (PASTEBUFFER->BoundingBox.Y2 - PASTEBUFFER->Y));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * sets a new buffer number
+ */
+void SetBufferNumber(int Number)
+{
+	if (Number >= 0 && Number < MAX_BUFFER) {
+		conf_set_design("editor/buffer_number", "%d", Number);
+
+		/* do an update on the crosshair range */
+		SetCrosshairRangeToBuffer();
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ */
+
+void SaveMode(void)
+{
+	mode_stack[mode_position] = conf_core.editor.mode;
+	if (mode_position < MAX_MODESTACK_DEPTH - 1)
+		mode_position++;
+}
+
+void RestoreMode(void)
+{
+	if (mode_position == 0) {
+		Message(PCB_MSG_DEFAULT, "hace: underflow of restore mode\n");
+		return;
+	}
+	SetMode(mode_stack[--mode_position]);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * set a new mode and update X cursor
+ */
+void SetMode(int Mode)
+{
+	char sMode[32];
+	static pcb_bool recursing = pcb_false;
+	/* protect the cursor while changing the mode
+	 * perform some additional stuff depending on the new mode
+	 * reset 'state' of attached objects
+	 */
+	if (recursing)
+		return;
+	recursing = pcb_true;
+	notify_crosshair_change(pcb_false);
+	addedLines = 0;
+	Crosshair.AttachedObject.Type = PCB_TYPE_NONE;
+	Crosshair.AttachedObject.State = STATE_FIRST;
+	Crosshair.AttachedPolygon.PointN = 0;
+	if (PCB->RatDraw) {
+		if (Mode == PCB_MODE_ARC || Mode == PCB_MODE_RECTANGLE ||
+				Mode == PCB_MODE_VIA || Mode == PCB_MODE_POLYGON ||
+				Mode == PCB_MODE_POLYGON_HOLE || Mode == PCB_MODE_TEXT || Mode == PCB_MODE_INSERT_POINT || Mode == PCB_MODE_THERMAL) {
+			Message(PCB_MSG_DEFAULT, _("That mode is NOT allowed when drawing ratlines!\n"));
+			Mode = PCB_MODE_NO;
+		}
+	}
+	if (conf_core.editor.mode == PCB_MODE_LINE && Mode == PCB_MODE_ARC && Crosshair.AttachedLine.State != STATE_FIRST) {
+		Crosshair.AttachedLine.State = STATE_FIRST;
+		Crosshair.AttachedBox.State = STATE_SECOND;
+		Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = Crosshair.AttachedLine.Point1.X;
+		Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = Crosshair.AttachedLine.Point1.Y;
+		AdjustAttachedObjects();
+	}
+	else if (conf_core.editor.mode == PCB_MODE_ARC && Mode == PCB_MODE_LINE && Crosshair.AttachedBox.State != STATE_FIRST) {
+		Crosshair.AttachedBox.State = STATE_FIRST;
+		Crosshair.AttachedLine.State = STATE_SECOND;
+		Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = Crosshair.AttachedBox.Point1.X;
+		Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = Crosshair.AttachedBox.Point1.Y;
+		sprintf(sMode, "%d", Mode);
+		conf_set(CFR_DESIGN, "editor/mode", -1, sMode, POL_OVERWRITE);
+		AdjustAttachedObjects();
+	}
+	else {
+		if (conf_core.editor.mode == PCB_MODE_ARC || conf_core.editor.mode == PCB_MODE_LINE)
+			SetLocalRef(0, 0, pcb_false);
+		Crosshair.AttachedBox.State = STATE_FIRST;
+		Crosshair.AttachedLine.State = STATE_FIRST;
+		if (Mode == PCB_MODE_LINE && conf_core.editor.auto_drc) {
+			if (ResetConnections(pcb_true)) {
+				IncrementUndoSerialNumber();
+				Draw();
+			}
+		}
+	}
+
+	sprintf(sMode, "%d", Mode);
+	conf_set(CFR_DESIGN, "editor/mode", -1, sMode, POL_OVERWRITE);
+
+	if (Mode == PCB_MODE_PASTE_BUFFER)
+		/* do an update on the crosshair range */
+		SetCrosshairRangeToBuffer();
+	else
+		SetCrosshairRange(0, 0, PCB->MaxWidth, PCB->MaxHeight);
+
+	recursing = pcb_false;
+
+	/* force a crosshair grid update because the valid range
+	 * may have changed
+	 */
+	MoveCrosshairRelative(0, 0);
+	notify_crosshair_change(pcb_true);
+}
+
+void SetLocalRef(Coord X, Coord Y, pcb_bool Showing)
+{
+	static MarkType old;
+	static int count = 0;
+
+	if (Showing) {
+		notify_mark_change(pcb_false);
+		if (count == 0)
+			old = Marked;
+		Marked.X = X;
+		Marked.Y = Y;
+		Marked.status = pcb_true;
+		count++;
+		notify_mark_change(pcb_true);
+	}
+	else if (count > 0) {
+		notify_mark_change(pcb_false);
+		count = 0;
+		Marked = old;
+		notify_mark_change(pcb_true);
+	}
+}
diff --git a/src/set.h b/src/set.h
new file mode 100644
index 0000000..39518bd
--- /dev/null
+++ b/src/set.h
@@ -0,0 +1,50 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for update routines */
+
+#ifndef	PCB_SET_H
+#define	PCB_SET_H
+
+#include "global.h"
+
+void SetTextScale(int);
+void SetGrid(Coord, pcb_bool);
+void SetZoom(double);
+void SetLineSize(Coord);
+void SetViaSize(Coord, pcb_bool);
+void SetViaDrillingHole(Coord, pcb_bool);
+void SetClearanceWidth(Coord);
+void SetChangedFlag(pcb_bool);
+void SetBufferNumber(int);
+void SetMode(int);
+void SetCrosshairRangeToBuffer(void);
+void SetLocalRef(Coord, Coord, pcb_bool);
+void RedrawZoom(Coord, Coord);
+void SaveMode(void);
+void RestoreMode(void);
+
+#endif
diff --git a/src/strflags.c b/src/strflags.c
new file mode 100644
index 0000000..f86efce
--- /dev/null
+++ b/src/strflags.c
@@ -0,0 +1,540 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2005 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+#include "strflags.h"
+#include "compat_misc.h"
+
+/* Because all the macros expect it, that's why.  */
+typedef struct {
+	FlagType Flags;
+} FlagHolder;
+
+/* Be careful to list more specific flags first, followed by general
+ * flags, when two flags use the same bit.  For example, "onsolder" is
+ * for elements only, while "auto" is for everything else.  They use
+ * the same bit, but onsolder is listed first so that elements will
+ * use it and not auto.
+ *
+ * Thermals are handled separately, as they're layer-selective.
+ */
+
+#define N(x) x, sizeof(x)-1
+FlagBitsType pcb_object_flagbits[] = {
+	{PCB_FLAG_PIN, N("pin"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_VIA, N("via"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_FOUND, N("found"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_HOLE, N("hole"), PCB_TYPEMASK_PIN},
+	{PCB_FLAG_RAT, N("rat"), PCB_TYPE_RATLINE},
+	{PCB_FLAG_PININPOLY, N("pininpoly"), PCB_TYPEMASK_PIN | PCB_TYPE_PAD},
+	{PCB_FLAG_CLEARPOLY, N("clearpoly"), PCB_TYPE_POLYGON},
+	{PCB_FLAG_HIDENAME, N("hidename"), PCB_TYPE_ELEMENT},
+	{PCB_FLAG_DISPLAYNAME, N("showname"), PCB_TYPE_ELEMENT},
+	{PCB_FLAG_CLEARLINE, N("clearline"), PCB_TYPE_LINE | PCB_TYPE_ARC | PCB_TYPE_TEXT},
+	{PCB_FLAG_SELECTED, N("selected"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_ONSOLDER, N("onsolder"), PCB_TYPE_ELEMENT | PCB_TYPE_PAD | PCB_TYPE_TEXT},
+	{PCB_FLAG_AUTO, N("auto"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_SQUARE, N("square"), PCB_TYPEMASK_PIN | PCB_TYPE_PAD},
+	{PCB_FLAG_RUBBEREND, N("rubberend"), PCB_TYPE_LINE | PCB_TYPE_ARC},
+	{PCB_FLAG_WARN, N("warn"), PCB_TYPEMASK_PIN | PCB_TYPE_PAD},
+	{PCB_FLAG_USETHERMAL, N("usetherm"), PCB_TYPEMASK_PIN | PCB_TYPE_LINE | PCB_TYPE_ARC},
+	{PCB_FLAG_OCTAGON, N("octagon"), PCB_TYPEMASK_PIN | PCB_TYPE_PAD},
+	{PCB_FLAG_DRC, N("drc"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_LOCK, N("lock"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_EDGE2, N("edge2"), PCB_TYPEMASK_ALL},
+	{PCB_FLAG_FULLPOLY, N("fullpoly"), PCB_TYPE_POLYGON},
+	{PCB_FLAG_NOPASTE, N("nopaste"), PCB_TYPE_PAD},
+	{PCB_FLAG_NONETLIST, N("nonetlist"), PCB_TYPEMASK_ALL}
+};
+#undef N
+
+const int pcb_object_flagbits_len = ENTRIES(pcb_object_flagbits);
+
+
+/*
+ * This helper function maintains a small list of buffers which are
+ * used by flags_to_string().  Each buffer is allocated from the heap,
+ * but the caller must not free them (they get realloced when they're
+ * reused, but never completely freed).
+ */
+
+static struct {
+	char *ptr;
+	int len;
+} buffers[10];
+static int bufptr = 0;
+static char *alloc_buf(int len)
+{
+#define B buffers[bufptr]
+	len++;
+	bufptr = (bufptr + 1) % 10;
+	if (B.len < len) {
+		if (B.ptr)
+			B.ptr = (char *) realloc(B.ptr, len);
+		else
+			B.ptr = (char *) malloc(len);
+		B.len = len;
+	}
+	return B.ptr;
+#undef B
+}
+
+void uninit_strflags_buf(void)
+{
+	int n;
+	for(n = 0; n < 10; n++) {
+		if (buffers[n].ptr != NULL) {
+			free(buffers[n].ptr);
+			buffers[n].ptr = NULL;
+		}
+	}
+}
+
+/*
+ * This set of routines manages a list of layer-specific flags.
+ * Callers should call grow_layer_list(0) to reset the list, and
+ * set_layer_list(layer,1) to set bits in the layer list.  The results
+ * are stored in layers[], which has num_layers valid entries.
+ */
+
+static char *layers = 0;
+static int max_layers = 0, num_layers = 0;
+
+static void grow_layer_list(int num)
+{
+	if (layers == 0) {
+		layers = (char *) calloc(num > 0 ? num : 1, 1);
+		max_layers = num;
+	}
+	else if (num > max_layers) {
+		max_layers = num;
+		layers = (char *) realloc(layers, max_layers);
+	}
+	if (num > num_layers)
+		memset(layers + num_layers, 0, num - num_layers - 1);
+	num_layers = num;
+	return;
+}
+
+void uninit_strflags_layerlist(void)
+{
+	if (layers != NULL) {
+		free(layers);
+		layers = NULL;
+		num_layers = max_layers = 0;
+	}
+}
+
+static inline void set_layer_list(int layer, int v)
+{
+	if (layer >= num_layers)
+		grow_layer_list(layer + 1);
+	layers[layer] = v;
+}
+
+/*
+ * These next two convert between layer lists and strings.
+ * parse_layer_list() is passed a pointer to a string, and parses a
+ * list of integer which reflect layers to be flagged.  It returns a
+ * pointer to the first character following the list.  The syntax of
+ * the list is a paren-surrounded, comma-separated list of integers
+ * and/or pairs of integers separated by a dash (like "(1,2,3-7)").
+ * Spaces and other punctuation are not allowed.  The results are
+ * stored in layers[] defined above.
+ *
+ * print_layer_list() does the opposite - it uses the flags set in
+ * layers[] to build a string that represents them, using the syntax
+ * above.
+ *
+ */
+
+/* Returns a pointer to the first character past the list. */
+static const char *parse_layer_list(const char *bp, int (*error) (const char *))
+{
+	const char *orig_bp = bp;
+	int l = 0, range = -1;
+	int value = 1;
+
+	grow_layer_list(0);
+	while (*bp) {
+		if (*bp == '+')
+			value = 2;
+		else if (*bp == 'S')
+			value = 3;
+		else if (*bp == 'X')
+			value = 4;
+		else if (*bp == 't')
+			value = 5;
+		else if (*bp == ')' || *bp == ',' || *bp == '-') {
+			if (range == -1)
+				range = l;
+			while (range <= l)
+				set_layer_list(range++, value);
+			if (*bp == '-')
+				range = l;
+			else
+				range = -1;
+			value = 1;
+			l = 0;
+		}
+
+		else if (isdigit((int) *bp))
+			l = l * 10 + (*bp - '0');
+
+		else if (error) {
+			const char *fmt = "Syntax error parsing layer list \"%.*s\" at %c";
+			char *msg = alloc_buf(strlen(fmt) + strlen(orig_bp));
+			sprintf(msg, fmt, bp - orig_bp + 5, orig_bp, *bp);
+			error(msg);
+			error = NULL;
+		}
+
+		if (*bp == ')')
+			return bp + 1;
+
+		bp++;
+	}
+	return bp;
+}
+
+/* Number of character the value "i" requires when printed. */
+static int printed_int_length(int i, int j)
+{
+	int rv;
+
+	if (i < 10)
+		return 1 + (j ? 1 : 0);
+	if (i < 100)
+		return 2 + (j ? 1 : 0);
+
+	for (rv = 1; i >= 10; rv++)
+		i /= 10;
+	return rv + (j ? 1 : 0);
+}
+
+/* Returns a pointer to an internal buffer which is overwritten with
+   each new call.  */
+static char *print_layer_list()
+{
+	static char *buf = 0;
+	static int buflen = 0;
+	int len, i, j;
+	char *bp;
+
+	len = 2;
+	for (i = 0; i < num_layers; i++)
+		if (layers[i])
+			len += 1 + printed_int_length(i, layers[i]);
+	if (buflen < len) {
+		if (buf)
+			buf = (char *) realloc(buf, len);
+		else
+			buf = (char *) malloc(len);
+		buflen = len;
+	}
+
+	bp = buf;
+	*bp++ = '(';
+
+	for (i = 0; i < num_layers; i++)
+		if (layers[i]) {
+			/* 0 0 1 1 1 0 0 */
+			/*     i     j   */
+			for (j = i + 1; j < num_layers && layers[j] == 1; j++);
+			if (j > i + 2) {
+				sprintf(bp, "%d-%d,", i, j - 1);
+				i = j - 1;
+			}
+			else
+				switch (layers[i]) {
+				case 1:
+					sprintf(bp, "%d,", i);
+					break;
+				case 2:
+					sprintf(bp, "%d+,", i);
+					break;
+				case 3:
+					sprintf(bp, "%dS,", i);
+					break;
+				case 4:
+					sprintf(bp, "%dX,", i);
+					break;
+				case 5:
+				default:
+					sprintf(bp, "%dt,", i);
+					break;
+				}
+			bp += strlen(bp);
+		}
+	bp[-1] = ')';
+	*bp = 0;
+	return buf;
+}
+
+/*
+ * Ok, now the two entry points to this file.  The first, string_to_flags,
+ * is passed a string (usually from parse_y.y) and returns a "set of flags".
+ * In theory, this can be anything, but for now it's just an integer.  Later
+ * it might be a structure, for example.
+ *
+ * Currently, there is no error handling :-P
+ */
+
+static int error_ignore(const char *msg)
+{																/* do nothing */
+	return 0;
+}
+
+static FlagType empty_flags;
+
+FlagType
+common_string_to_flags(const char *flagstring, int (*error) (const char *msg), FlagBitsType * flagbits, int n_flagbits)
+{
+	const char *fp, *ep;
+	int flen;
+	FlagHolder rv;
+	int i;
+
+	rv.Flags = empty_flags;
+
+	if (error == 0)
+		error = error_ignore;
+
+	if (flagstring == NULL)
+		return empty_flags;
+
+	fp = ep = flagstring;
+
+	if (*fp == '"')
+		ep = ++fp;
+
+	while (*ep && *ep != '"') {
+		int found = 0;
+
+		for (ep = fp; *ep && *ep != ',' && *ep != '"' && *ep != '('; ep++);
+		flen = ep - fp;
+		if (*ep == '(')
+			ep = parse_layer_list(ep + 1, error);
+
+		if (flen == 7 && memcmp(fp, "thermal", 7) == 0) {
+			for (i = 0; i < MAX_LAYER && i < num_layers; i++)
+				if (layers[i])
+					ASSIGN_THERM(i, layers[i], &rv);
+		}
+		else if (flen == 5 && memcmp(fp, "shape", 5) == 0) {
+			rv.Flags.q = atoi(fp + 6);
+		}
+		else if (flen == 7 && memcmp(fp, "intconn", 7) == 0) {
+			rv.Flags.int_conn_grp = atoi(fp + 8);
+		}
+		else {
+			for (i = 0; i < n_flagbits; i++)
+				if (flagbits[i].nlen == flen && memcmp(flagbits[i].name, fp, flen) == 0) {
+					found = 1;
+					SET_FLAG(flagbits[i].mask, &rv);
+					break;
+				}
+			if (!found) {
+				const char *fmt = "Unknown flag: \"%.*s\" ignored";
+				unknown_flag_t *u;
+				char *msg;
+				const char *s;
+
+				/* include () */
+				s = fp + flen;
+				if (*s == '(') {
+					while (*s != ')') {
+						flen++;
+						s++;
+					}
+				}
+				if (*s == ')')
+					flen++;
+
+				msg = alloc_buf(strlen(fmt) + flen);
+				sprintf(msg, fmt, flen, fp);
+				error(msg);
+
+				u = malloc(sizeof(unknown_flag_t));
+				u->str = pcb_strndup(fp, flen);
+				u->next = NULL;
+				/* need to append, to keep order of flags */
+				if (rv.Flags.unknowns != NULL) {
+					unknown_flag_t *n;
+					for (n = rv.Flags.unknowns; n->next != NULL; n = n->next);
+					n->next = u;
+				}
+				else
+					rv.Flags.unknowns = u;
+			}
+		}
+		fp = ep + 1;
+	}
+	return rv.Flags;
+}
+
+FlagType string_to_flags(const char *flagstring, int (*error) (const char *msg))
+{
+	return common_string_to_flags(flagstring, error, pcb_object_flagbits, ENTRIES(pcb_object_flagbits));
+}
+
+
+/*
+ * Given a set of flags for a given type of object, return a string
+ * which reflects those flags.  The only requirement is that this
+ * string be parseable by string_to_flags.
+ *
+ * Note that this function knows a little about what kinds of flags
+ * will be automatically set by parsing, so it won't (for example)
+ * include the "via" flag for PCB_TYPE_VIAs because it knows those get
+ * forcibly set when vias are parsed.
+ */
+
+char *common_flags_to_string(FlagType flags, int object_type, FlagBitsType * flagbits, int n_flagbits)
+{
+	int len;
+	int i;
+	FlagHolder fh, savef;
+	char *buf, *bp;
+	unknown_flag_t *u;
+
+	fh.Flags = flags;
+
+#ifndef FLAG_TEST
+	switch (object_type) {
+	case PCB_TYPE_VIA:
+		CLEAR_FLAG(PCB_FLAG_VIA, &fh);
+		break;
+	case PCB_TYPE_RATLINE:
+		CLEAR_FLAG(PCB_FLAG_RAT, &fh);
+		break;
+	case PCB_TYPE_PIN:
+		CLEAR_FLAG(PCB_FLAG_PIN, &fh);
+		break;
+	}
+#endif
+
+	savef = fh;
+
+	len = 3;											/* for "()\0" */
+
+	for (i = 0; i < n_flagbits; i++)
+
+		if ((flagbits[i].object_types & object_type)
+				&& (TEST_FLAG(flagbits[i].mask, &fh))) {
+
+			len += flagbits[i].nlen + 1;
+			CLEAR_FLAG(flagbits[i].mask, &fh);
+		}
+
+	if (TEST_ANY_THERMS(&fh)) {
+		len += sizeof("thermal()");
+		for (i = 0; i < MAX_LAYER; i++)
+			if (TEST_THERM(i, &fh))
+				len += printed_int_length(i, GET_THERM(i, &fh)) + 1;
+	}
+
+	if (flags.q > 0) {
+		len += sizeof("shape(.)");
+		if (flags.q > 9)
+			len += 2;
+	}
+
+	if (flags.int_conn_grp > 0) {
+		len += sizeof("intconn(.)");
+		if (flags.q > 9)
+			len++;
+		if (flags.q > 99)
+			len++;
+	}
+
+	for (u = flags.unknowns; u != NULL; u = u->next)
+		len += strlen(u->str) + 1;
+
+	bp = buf = alloc_buf(len + 2);
+
+	*bp++ = '"';
+
+	fh = savef;
+	for (i = 0; i < n_flagbits; i++)
+		if (flagbits[i].object_types & object_type && (TEST_FLAG(flagbits[i].mask, &fh))) {
+			if (bp != buf + 1)
+				*bp++ = ',';
+			strcpy(bp, flagbits[i].name);
+			bp += flagbits[i].nlen;
+			CLEAR_FLAG(flagbits[i].mask, &fh);
+		}
+
+	if (TEST_ANY_THERMS(&fh)) {
+		if (bp != buf + 1)
+			*bp++ = ',';
+		strcpy(bp, "thermal");
+		bp += strlen("thermal");
+		grow_layer_list(0);
+		for (i = 0; i < MAX_LAYER; i++)
+			if (TEST_THERM(i, &fh))
+				set_layer_list(i, GET_THERM(i, &fh));
+		strcpy(bp, print_layer_list());
+		bp += strlen(bp);
+	}
+
+	if (flags.q > 0) {
+		if (bp != buf + 1)
+			*bp++ = ',';
+		bp += sprintf(bp, "shape(%d)", flags.q);
+	}
+
+	if (flags.int_conn_grp > 0) {
+		if (bp != buf + 1)
+			*bp++ = ',';
+		bp += sprintf(bp, "intconn(%d)", flags.int_conn_grp);
+	}
+
+	for (u = flags.unknowns; u != NULL; u = u->next) {
+		int len;
+		len = strlen(u->str);
+		if (bp != buf + 1)
+			*bp++ = ',';
+		memcpy(bp, u->str, len);
+		bp += len;
+	}
+
+	*bp++ = '"';
+	*bp = 0;
+	return buf;
+}
+
+char *flags_to_string(FlagType flags, int object_type)
+{
+	return common_flags_to_string(flags, object_type, pcb_object_flagbits, ENTRIES(pcb_object_flagbits));
+}
diff --git a/src/strflags.h b/src/strflags.h
new file mode 100644
index 0000000..d7a6664
--- /dev/null
+++ b/src/strflags.h
@@ -0,0 +1,77 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2005 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+#ifndef PCB_STRFLAGS_H
+#define PCB_STRFLAGS_H
+
+/* for flagtype */
+#include "global_objs.h"
+
+typedef struct {
+
+	/* This is the bit that we're setting.  */
+	int mask;
+
+	/* The name used in the output file.  */
+	const char *name;
+	int nlen;
+
+	/* If set, this entry won't be output unless the object type is one
+	   of these.  */
+	int object_types;
+
+} FlagBitsType;
+
+/* All flags natively known by the core */
+extern FlagBitsType pcb_object_flagbits[];
+extern const int pcb_object_flagbits_len;
+
+/* The purpose of this interface is to make the file format able to
+   handle more than 32 flags, and to hide the internal details of
+   flags from the file format.  */
+
+/* When passed a string, parse it and return an appropriate set of
+   flags.  Errors cause error() to be called with a suitable message;
+   if error is NULL, errors are ignored.  */
+FlagType string_to_flags(const char *flagstring, int (*error) (const char *msg));
+
+/* Given a set of flags for a given object type, return a string which
+   can be output to a file.  The returned pointer must not be
+   freed.  */
+char *flags_to_string(FlagType flags, int object_type);
+
+/* Same as above, but for pcb flags.  */
+FlagType string_to_pcbflags(const char *flagstring, int (*error) (const char *msg));
+char *pcbflags_to_string(FlagType flags);
+
+void uninit_strflags_buf(void);
+void uninit_strflags_layerlist(void);
+
+/* io_pcb() needs this for historic reasons */
+FlagType common_string_to_flags(const char *flagstring, int (*error) (const char *msg), FlagBitsType * flagbits, int n_flagbits);
+char *common_flags_to_string(FlagType flags, int object_type, FlagBitsType * flagbits, int n_flagbits);
+
+#endif
diff --git a/src/stub_mincut.c b/src/stub_mincut.c
new file mode 100644
index 0000000..2704ba0
--- /dev/null
+++ b/src/stub_mincut.c
@@ -0,0 +1,43 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "stub_mincut.h"
+
+static void stub_rat_proc_shorts_dummy(void)
+{
+}
+
+static void stub_rat_found_short_dummy(PinType * pin, PadType * pad, const char *with_net)
+{
+	/* original behavior: just warn at random pins/pads */
+	if (pin != NULL)
+		SET_FLAG(PCB_FLAG_WARN, pin);
+	if (pad != NULL)
+		SET_FLAG(PCB_FLAG_WARN, pad);
+
+	stub_rat_proc_shorts_dummy();
+}
+
+void (*stub_rat_found_short)(PinType * pin, PadType * pad, const char *with_net) = stub_rat_found_short_dummy;
+void (*stub_rat_proc_shorts)(void) = stub_rat_proc_shorts_dummy;
diff --git a/src/stub_mincut.h b/src/stub_mincut.h
new file mode 100644
index 0000000..fe53425
--- /dev/null
+++ b/src/stub_mincut.h
@@ -0,0 +1,25 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+extern void (*stub_rat_found_short)(PinType * pin, PadType * pad, const char *with_net);
+extern void (*stub_rat_proc_shorts)(void);
+
diff --git a/src/stub_stroke.c b/src/stub_stroke.c
new file mode 100644
index 0000000..d026b9b
--- /dev/null
+++ b/src/stub_stroke.c
@@ -0,0 +1,38 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include "error.h"
+#include "pcb_bool.h"
+
+pcb_bool mid_stroke = pcb_false;
+
+static void stub_stroke_record_dummy(int ev_x, int ev_y)
+{
+}
+
+static void stub_stroke_start_dummy(void)
+{
+	Message(PCB_MSG_DEFAULT, "Can not use libstroke: not compiled as a buildin and not loaded as a plugin\n");
+}
+
+void (*stub_stroke_record)(int ev_x, int ev_y) = stub_stroke_record_dummy;
+void (*stub_stroke_start)(void) = stub_stroke_start_dummy;
+void (*stub_stroke_finish)(void) = stub_stroke_start_dummy;
diff --git a/src/stub_stroke.h b/src/stub_stroke.h
new file mode 100644
index 0000000..3325306
--- /dev/null
+++ b/src/stub_stroke.h
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+extern pcb_bool mid_stroke;
+extern void (*stub_stroke_record)(int ev_x, int ev_y);
+extern void (*stub_stroke_start)(void);
+extern void (*stub_stroke_finish)(void);
diff --git a/src/stub_vendor.c b/src/stub_vendor.c
new file mode 100644
index 0000000..3a93544
--- /dev/null
+++ b/src/stub_vendor.c
@@ -0,0 +1,38 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "stub_vendor.h"
+
+static int stub_vendorDrillMap_dummy(int dia)
+{
+	return dia;
+}
+
+static pcb_bool stub_vendorIsElementMappable_dummy(ElementTypePtr e)
+{
+	return pcb_false;
+}
+
+int (*stub_vendorDrillMap)(int) = stub_vendorDrillMap_dummy;
+pcb_bool (*stub_vendorIsElementMappable)(ElementTypePtr) = stub_vendorIsElementMappable_dummy;
+
+
diff --git a/src/stub_vendor.h b/src/stub_vendor.h
new file mode 100644
index 0000000..7772374
--- /dev/null
+++ b/src/stub_vendor.h
@@ -0,0 +1,32 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_STUB_VENDOR_H
+#define PCB_STUB_VENDOR_H
+
+#include "config.h"
+#include "global.h"
+
+extern int (*stub_vendorDrillMap)(int);
+extern pcb_bool (*stub_vendorIsElementMappable)(ElementTypePtr);
+
+#endif /* __VENDOR_H__ */
diff --git a/src/thermal.c b/src/thermal.c
new file mode 100644
index 0000000..be022e2
--- /dev/null
+++ b/src/thermal.c
@@ -0,0 +1,438 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
+ *
+ *  this file, thermal.c was written by and is
+ *  (C) Copyright 2006, harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* negative thermal finger polygons */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "misc.h"
+#include "polygon.h"
+#include "thermal.h"
+
+static PCBTypePtr pcb;
+
+struct cent {
+	Coord x, y;
+	Coord s, c;
+	char style;
+	POLYAREA *p;
+};
+
+static POLYAREA *diag_line(Coord X, Coord Y, Coord l, Coord w, pcb_bool rt)
+{
+	PLINE *c;
+	Vector v;
+	Coord x1, x2, y1, y2;
+
+	if (rt) {
+		x1 = (l - w) * M_SQRT1_2;
+		x2 = (l + w) * M_SQRT1_2;
+		y1 = x1;
+		y2 = x2;
+	}
+	else {
+		x2 = -(l - w) * M_SQRT1_2;
+		x1 = -(l + w) * M_SQRT1_2;
+		y1 = -x1;
+		y2 = -x2;
+	}
+
+	v[0] = X + x1;
+	v[1] = Y + y2;
+	if ((c = poly_NewContour(v)) == NULL)
+		return NULL;
+	v[0] = X - x2;
+	v[1] = Y - y1;
+	poly_InclVertex(c->head.prev, poly_CreateNode(v));
+	v[0] = X - x1;
+	v[1] = Y - y2;
+	poly_InclVertex(c->head.prev, poly_CreateNode(v));
+	v[0] = X + x2;
+	v[1] = Y + y1;
+	poly_InclVertex(c->head.prev, poly_CreateNode(v));
+	return ContourToPoly(c);
+}
+
+static POLYAREA *square_therm(PinTypePtr pin, pcb_cardinal_t style)
+{
+	POLYAREA *p, *p2;
+	PLINE *c;
+	Vector v;
+	Coord d, in, out;
+
+	switch (style) {
+	case 1:
+		d = pcb->ThermScale * pin->Clearance * M_SQRT1_2;
+		out = (pin->Thickness + pin->Clearance) / 2;
+		in = pin->Thickness / 2;
+		/* top (actually bottom since +y is down) */
+		v[0] = pin->X - in + d;
+		v[1] = pin->Y + in;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[0] = pin->X + in - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X + out - d;
+		v[1] = pin->Y + out;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X - out + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		p = ContourToPoly(c);
+		/* right */
+		v[0] = pin->X + in;
+		v[1] = pin->Y + in - d;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[1] = pin->Y - in + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X + out;
+		v[1] = pin->Y - out + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[1] = pin->Y + out - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		p2 = ContourToPoly(c);
+		p->f = p2;
+		p2->b = p;
+		/* left */
+		v[0] = pin->X - in;
+		v[1] = pin->Y - in + d;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[1] = pin->Y + in - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X - out;
+		v[1] = pin->Y + out - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[1] = pin->Y - out + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		p2 = ContourToPoly(c);
+		p->f->f = p2;
+		p2->b = p->f;
+		/* bottom (actually top since +y is down) */
+		v[0] = pin->X + in - d;
+		v[1] = pin->Y - in;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[0] = pin->X - in + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X - out + d;
+		v[1] = pin->Y - out;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X + out - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		p2 = ContourToPoly(c);
+		p->f->f->f = p2;
+		p2->f = p;
+		p2->b = p->f->f;
+		p->b = p2;
+		return p;
+	case 4:
+		{
+			LineType l;
+			l.Flags = NoFlags();
+			d = pin->Thickness / 2 - pcb->ThermScale * pin->Clearance;
+			out = pin->Thickness / 2 + pin->Clearance / 4;
+			in = pin->Clearance / 2;
+			/* top */
+			l.Point1.X = pin->X - d;
+			l.Point2.Y = l.Point1.Y = pin->Y + out;
+			l.Point2.X = pin->X + d;
+			p = LinePoly(&l, in);
+			/* right */
+			l.Point1.X = l.Point2.X = pin->X + out;
+			l.Point1.Y = pin->Y - d;
+			l.Point2.Y = pin->Y + d;
+			p2 = LinePoly(&l, in);
+			p->f = p2;
+			p2->b = p;
+			/* bottom */
+			l.Point1.X = pin->X - d;
+			l.Point2.Y = l.Point1.Y = pin->Y - out;
+			l.Point2.X = pin->X + d;
+			p2 = LinePoly(&l, in);
+			p->f->f = p2;
+			p2->b = p->f;
+			/* left */
+			l.Point1.X = l.Point2.X = pin->X - out;
+			l.Point1.Y = pin->Y - d;
+			l.Point2.Y = pin->Y + d;
+			p2 = LinePoly(&l, in);
+			p->f->f->f = p2;
+			p2->b = p->f->f;
+			p->b = p2;
+			p2->f = p;
+			return p;
+		}
+	default:											/* style 2 and 5 */
+		d = 0.5 * pcb->ThermScale * pin->Clearance;
+		if (style == 5)
+			d += d;
+		out = (pin->Thickness + pin->Clearance) / 2;
+		in = pin->Thickness / 2;
+		/* topright */
+		v[0] = pin->X + in;
+		v[1] = pin->Y + in;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[1] = pin->Y + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 2) {
+			v[0] = pin->X + out;
+			poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		}
+		else
+			frac_circle(c, v[0] + pin->Clearance / 4, v[1], v, 2);
+		v[1] = pin->Y + in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		/* pivot 1/4 circle to next point */
+		frac_circle(c, pin->X + in, pin->Y + in, v, 4);
+		v[0] = pin->X + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 2) {
+			poly_InclVertex(c->head.prev, poly_CreateNode(v));
+			v[1] = pin->Y + in;
+			poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		}
+		else
+			frac_circle(c, v[0], v[1] - pin->Clearance / 4, v, 2);
+		p = ContourToPoly(c);
+		/* bottom right */
+		v[0] = pin->X + in;
+		v[1] = pin->Y - d;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[1] = pin->Y - in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 2) {
+			v[1] = pin->Y - out;
+			poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		}
+		else
+			frac_circle(c, v[0], v[1] - pin->Clearance / 4, v, 2);
+		v[0] = pin->X + in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		/* pivot 1/4 circle to next point */
+		frac_circle(c, pin->X + in, pin->Y - in, v, 4);
+		v[1] = pin->Y - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 5)
+			frac_circle(c, v[0] - pin->Clearance / 4, v[1], v, 2);
+		p2 = ContourToPoly(c);
+		p->f = p2;
+		p2->b = p;
+		/* bottom left */
+		v[0] = pin->X - d;
+		v[1] = pin->Y - in;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[0] = pin->X - in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[1] = pin->Y - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 2) {
+			v[0] = pin->X - out;
+			poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		}
+		else
+			frac_circle(c, v[0] - pin->Clearance / 4, v[1], v, 2);
+		v[1] = pin->Y - in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		/* pivot 1/4 circle to next point */
+		frac_circle(c, pin->X - in, pin->Y - in, v, 4);
+		v[0] = pin->X - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 5)
+			frac_circle(c, v[0], v[1] + pin->Clearance / 4, v, 2);
+		p2 = ContourToPoly(c);
+		p->f->f = p2;
+		p2->b = p->f;
+		/* top left */
+		v[0] = pin->X - d;
+		v[1] = pin->Y + out;
+		if ((c = poly_NewContour(v)) == NULL)
+			return NULL;
+		v[0] = pin->X - in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		/* pivot 1/4 circle to next point (x-out, y+in) */
+		frac_circle(c, pin->X - in, pin->Y + in, v, 4);
+		v[1] = pin->Y + d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 2) {
+			v[0] = pin->X - in;
+			poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		}
+		else
+			frac_circle(c, v[0] + pin->Clearance / 4, v[1], v, 2);
+		v[1] = pin->Y + in;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		v[0] = pin->X - d;
+		poly_InclVertex(c->head.prev, poly_CreateNode(v));
+		if (style == 5)
+			frac_circle(c, v[0], v[1] + pin->Clearance / 4, v, 2);
+		p2 = ContourToPoly(c);
+		p->f->f->f = p2;
+		p2->f = p;
+		p2->b = p->f->f;
+		p->b = p2;
+		return p;
+	}
+}
+
+static POLYAREA *oct_therm(PinTypePtr pin, pcb_cardinal_t style)
+{
+	POLYAREA *p, *p2, *m;
+	Coord t = 0.5 * pcb->ThermScale * pin->Clearance;
+	Coord w = pin->Thickness + pin->Clearance;
+
+	p = OctagonPoly(pin->X, pin->Y, w, GET_SQUARE(pin));
+	p2 = OctagonPoly(pin->X, pin->Y, pin->Thickness, GET_SQUARE(pin));
+	/* make full clearance ring */
+	poly_Boolean_free(p, p2, &m, PBO_SUB);
+	switch (style) {
+	default:
+	case 1:
+		p = diag_line(pin->X, pin->Y, w, t, pcb_true);
+		poly_Boolean_free(m, p, &p2, PBO_SUB);
+		p = diag_line(pin->X, pin->Y, w, t, pcb_false);
+		poly_Boolean_free(p2, p, &m, PBO_SUB);
+		return m;
+	case 2:
+		p = RectPoly(pin->X - t, pin->X + t, pin->Y - w, pin->Y + w);
+		poly_Boolean_free(m, p, &p2, PBO_SUB);
+		p = RectPoly(pin->X - w, pin->X + w, pin->Y - t, pin->Y + t);
+		poly_Boolean_free(p2, p, &m, PBO_SUB);
+		return m;
+		/* fix me add thermal style 4 */
+	case 5:
+		{
+			Coord t = pin->Thickness / 2;
+			POLYAREA *q;
+			/* cheat by using the square therm's rounded parts */
+			p = square_therm(pin, style);
+			q = RectPoly(pin->X - t, pin->X + t, pin->Y - t, pin->Y + t);
+			poly_Boolean_free(p, q, &p2, PBO_UNITE);
+			poly_Boolean_free(m, p2, &p, PBO_ISECT);
+			return p;
+		}
+	}
+}
+
+/* ThermPoly returns a POLYAREA having all of the clearance that when
+ * subtracted from the plane create the desired thermal fingers.
+ * Usually this is 4 disjoint regions.
+ *
+ */
+POLYAREA *ThermPoly(PCBTypePtr p, PinTypePtr pin, pcb_cardinal_t laynum)
+{
+	ArcType a;
+	POLYAREA *pa, *arc;
+	pcb_cardinal_t style = GET_THERM(laynum, pin);
+
+	if (style == 3)
+		return NULL;								/* solid connection no clearance */
+	pcb = p;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pin))
+		return square_therm(pin, style);
+	if (TEST_FLAG(PCB_FLAG_OCTAGON, pin))
+		return oct_therm(pin, style);
+	/* must be circular */
+	switch (style) {
+	case 1:
+	case 2:
+		{
+			POLYAREA *m;
+			Coord t = (pin->Thickness + pin->Clearance) / 2;
+			Coord w = 0.5 * pcb->ThermScale * pin->Clearance;
+			pa = CirclePoly(pin->X, pin->Y, t);
+			arc = CirclePoly(pin->X, pin->Y, pin->Thickness / 2);
+			/* create a thin ring */
+			poly_Boolean_free(pa, arc, &m, PBO_SUB);
+			/* fix me needs error checking */
+			if (style == 2) {
+				/* t is the theoretically required length, but we use twice that
+				 * to avoid descritisation errors in our circle approximation.
+				 */
+				pa = RectPoly(pin->X - t * 2, pin->X + t * 2, pin->Y - w, pin->Y + w);
+				poly_Boolean_free(m, pa, &arc, PBO_SUB);
+				pa = RectPoly(pin->X - w, pin->X + w, pin->Y - t * 2, pin->Y + t * 2);
+			}
+			else {
+				/* t is the theoretically required length, but we use twice that
+				 * to avoid descritisation errors in our circle approximation.
+				 */
+				pa = diag_line(pin->X, pin->Y, t * 2, w, pcb_true);
+				poly_Boolean_free(m, pa, &arc, PBO_SUB);
+				pa = diag_line(pin->X, pin->Y, t * 2, w, pcb_false);
+			}
+			poly_Boolean_free(arc, pa, &m, PBO_SUB);
+			return m;
+		}
+
+
+	default:
+		a.X = pin->X;
+		a.Y = pin->Y;
+		a.Height = a.Width = pin->Thickness / 2 + pin->Clearance / 4;
+		a.Thickness = 1;
+		a.Clearance = pin->Clearance / 2;
+		a.Flags = NoFlags();
+		a.Delta = 90 - (a.Clearance * (1. + 2. * pcb->ThermScale) * 180) / (M_PI * a.Width);
+		a.StartAngle = 90 - a.Delta / 2 + (style == 4 ? 0 : 45);
+		pa = ArcPoly(&a, a.Clearance);
+		if (!pa)
+			return NULL;
+		a.StartAngle += 90;
+		arc = ArcPoly(&a, a.Clearance);
+		if (!arc)
+			return NULL;
+		pa->f = arc;
+		arc->b = pa;
+		a.StartAngle += 90;
+		arc = ArcPoly(&a, a.Clearance);
+		if (!arc)
+			return NULL;
+		pa->f->f = arc;
+		arc->b = pa->f;
+		a.StartAngle += 90;
+		arc = ArcPoly(&a, a.Clearance);
+		if (!arc)
+			return NULL;
+		pa->b = arc;
+		pa->f->f->f = arc;
+		arc->b = pa->f->f;
+		arc->f = pa;
+		pa->b = arc;
+		return pa;
+	}
+}
diff --git a/src/thermal.h b/src/thermal.h
new file mode 100644
index 0000000..35480e2
--- /dev/null
+++ b/src/thermal.h
@@ -0,0 +1,45 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for thermal routines
+ *
+ * Thermals are normal lines on the layout. The only thing unique
+ * about them is that they have the PCB_FLAG_USETHERMAL set so that they
+ * can be identified as thermals. It is handy for pcb to automatically
+ * make adjustments to the thermals when the user performs certain
+ * operations, and the functions in thermal.h help implement that.
+ */
+
+#ifndef	PCB_THERMAL_H
+#define	PCB_THERMAL_H
+
+#include <stdlib.h>
+#include "global.h"
+#include "mymem.h"
+
+POLYAREA *ThermPoly(PCBTypePtr, PinTypePtr, pcb_cardinal_t);
+
+#endif
diff --git a/src/undo.c b/src/undo.c
new file mode 100644
index 0000000..39a214d
--- /dev/null
+++ b/src/undo.c
@@ -0,0 +1,1669 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* functions used to undo operations
+ *
+ * Description:
+ * There are two lists which hold
+ *   - information about a command
+ *   - data of removed objects
+ * Both lists are organized as first-in-last-out which means that the undo
+ * list can always use the last entry of the remove list.
+ * A serial number is incremented whenever an operation is completed.
+ * An operation itself may consist of several basic instructions.
+ * E.g.: removing all selected objects is one operation with exactly one
+ * serial number even if the remove function is called several times.
+ *
+ * a lock flag ensures that no infinite loops occur
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <memory.h>
+
+#include "buffer.h"
+#include "change.h"
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "insert.h"
+#include "layer.h"
+#include "misc.h"
+#include "mirror.h"
+#include "move.h"
+#include "polygon.h"
+#include "remove.h"
+#include "rotate.h"
+#include "rtree.h"
+#include "search.h"
+#include "set.h"
+#include "undo.h"
+#include "strflags.h"
+#include "conf_core.h"
+#include "compat_misc.h"
+#include "netlist.h"
+
+static pcb_bool between_increment_and_restore = pcb_false;
+static pcb_bool added_undo_between_increment_and_restore = pcb_false;
+
+/* ---------------------------------------------------------------------------
+ * some local data types
+ */
+typedef struct {								/* information about a change command */
+	char *Name;
+} ChangeNameType, *ChangeNameTypePtr;
+
+typedef struct {								/* information about a move command */
+	Coord DX, DY;									/* movement vector */
+} MoveType, *MoveTypePtr;
+
+typedef struct {								/* information about removed polygon points */
+	Coord X, Y;										/* data */
+	int ID;
+	pcb_cardinal_t Index;								/* index in a polygons array of points */
+	pcb_bool last_in_contour;					/* Whether the point was the last in its contour */
+} RemovedPointType, *RemovedPointTypePtr;
+
+typedef struct {								/* information about rotation */
+	Coord CenterX, CenterY;				/* center of rotation */
+	pcb_cardinal_t Steps;								/* number of steps */
+} RotateType, *RotateTypePtr;
+
+typedef struct {								/* information about moves between layers */
+	pcb_cardinal_t OriginalLayer;				/* the index of the original layer */
+} MoveToLayerType, *MoveToLayerTypePtr;
+
+typedef struct {								/* information about layer changes */
+	int old_index;
+	int new_index;
+} LayerChangeType, *LayerChangeTypePtr;
+
+typedef struct {								/* information about poly clear/restore */
+	pcb_bool Clear;										/* pcb_true was clear, pcb_false was restore */
+	LayerTypePtr Layer;
+} ClearPolyType, *ClearPolyTypePtr;
+
+typedef struct {
+	Angle angle[2];
+} AngleChangeType;
+
+typedef struct {								/* information about netlist lib changes */
+	LibraryTypePtr old;
+	LibraryTypePtr lib;
+} NetlistChangeType, *NetlistChangeTypePtr;
+
+typedef struct {								/* holds information about an operation */
+	int Serial,										/* serial number of operation */
+	  Type,												/* type of operation */
+	  Kind,												/* type of object with given ID */
+	  ID;													/* object ID */
+	union {												/* some additional information */
+		ChangeNameType ChangeName;
+		MoveType Move;
+		RemovedPointType RemovedPoint;
+		RotateType Rotate;
+		MoveToLayerType MoveToLayer;
+		FlagType Flags;
+		Coord Size;
+		LayerChangeType LayerChange;
+		ClearPolyType ClearPoly;
+		NetlistChangeType NetlistChange;
+		long int CopyID;
+		AngleChangeType AngleChange;
+	} Data;
+} UndoListType, *UndoListTypePtr;
+
+/* ---------------------------------------------------------------------------
+ * some local variables
+ */
+static DataTypePtr RemoveList = NULL;	/* list of removed objects */
+static UndoListTypePtr UndoList = NULL;	/* list of operations */
+static int Serial = 1,					/* serial number */
+	SavedSerial;
+static size_t UndoN, RedoN,			/* number of entries */
+  UndoMax;
+static pcb_bool Locked = pcb_false;			/* do not add entries if */
+static pcb_bool andDraw = pcb_true;
+										/* flag is set; prevents from */
+										/* infinite loops */
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static UndoListTypePtr GetUndoSlot(int, int, int);
+static void DrawRecoveredObject(int, void *, void *, void *);
+static pcb_bool UndoRotate(UndoListTypePtr);
+static pcb_bool UndoChangeName(UndoListTypePtr);
+static pcb_bool UndoCopyOrCreate(UndoListTypePtr);
+static pcb_bool UndoMove(UndoListTypePtr);
+static pcb_bool UndoRemove(UndoListTypePtr);
+static pcb_bool UndoRemovePoint(UndoListTypePtr);
+static pcb_bool UndoInsertPoint(UndoListTypePtr);
+static pcb_bool UndoRemoveContour(UndoListTypePtr);
+static pcb_bool UndoInsertContour(UndoListTypePtr);
+static pcb_bool UndoMoveToLayer(UndoListTypePtr);
+static pcb_bool UndoFlag(UndoListTypePtr);
+static pcb_bool UndoMirror(UndoListTypePtr);
+static pcb_bool UndoChangeSize(UndoListTypePtr);
+static pcb_bool UndoChange2ndSize(UndoListTypePtr);
+static pcb_bool UndoChangeAngles(UndoListTypePtr);
+static pcb_bool UndoChangeRadii(UndoListTypePtr);
+static pcb_bool UndoChangeClearSize(UndoListTypePtr);
+static pcb_bool UndoChangeMaskSize(UndoListTypePtr);
+static pcb_bool UndoClearPoly(UndoListTypePtr);
+static int PerformUndo(UndoListTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * adds a command plus some data to the undo list
+ */
+static UndoListTypePtr GetUndoSlot(int CommandType, int ID, int Kind)
+{
+	UndoListTypePtr ptr;
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	size_t limit = ((size_t)conf_core.editor.undo_warning_size) * 1024;
+
+#ifdef DEBUG_ID
+	if (SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, ID, Kind) == PCB_TYPE_NONE)
+		Message(PCB_MSG_DEFAULT, "hace: ID (%d) and Type (%x) mismatch in AddObject...\n", ID, Kind);
+#endif
+
+	/* allocate memory */
+	if (UndoN >= UndoMax) {
+		size_t size;
+
+		UndoMax += STEP_UNDOLIST;
+		size = UndoMax * sizeof(UndoListType);
+		UndoList = (UndoListTypePtr) realloc(UndoList, size);
+		memset(&UndoList[UndoN], 0, STEP_REMOVELIST * sizeof(UndoListType));
+
+		/* ask user to flush the table because of it's size */
+		if (size > limit) {
+			size_t l2;
+			l2 = (size / limit + 1) * limit;
+			Message(PCB_MSG_DEFAULT, _("Size of 'undo-list' exceeds %li kb\n"), (long) (l2 >> 10));
+		}
+	}
+
+	/* free structures from the pruned redo list */
+
+	for (ptr = &UndoList[UndoN]; RedoN; ptr++, RedoN--)
+		switch (ptr->Type) {
+		case UNDO_CHANGENAME:
+		case UNDO_CHANGEPINNUM:
+			free(ptr->Data.ChangeName.Name);
+			break;
+		case UNDO_REMOVE:
+			type = SearchObjectByID(RemoveList, &ptr1, &ptr2, &ptr3, ptr->ID, ptr->Kind);
+			if (type != PCB_TYPE_NONE) {
+				DestroyObject(RemoveList, type, ptr1, ptr2, ptr3);
+			}
+			break;
+		default:
+			break;
+		}
+
+	if (between_increment_and_restore)
+		added_undo_between_increment_and_restore = pcb_true;
+
+	/* copy typefield and serial number to the list */
+	ptr = &UndoList[UndoN++];
+	ptr->Type = CommandType;
+	ptr->Kind = Kind;
+	ptr->ID = ID;
+	ptr->Serial = Serial;
+	return (ptr);
+}
+
+/* ---------------------------------------------------------------------------
+ * redraws the recovered object
+ */
+static void DrawRecoveredObject(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (Type & (PCB_TYPE_LINE | PCB_TYPE_TEXT | PCB_TYPE_POLYGON | PCB_TYPE_ARC)) {
+		LayerTypePtr layer;
+
+		layer = LAYER_PTR(GetLayerNumber(RemoveList, (LayerTypePtr) Ptr1));
+		DrawObject(Type, (void *) layer, Ptr2);
+	}
+	else
+		DrawObject(Type, Ptr1, Ptr2);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a 'rotate' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoRotate(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		RotateObject(type, ptr1, ptr2, ptr3,
+								 Entry->Data.Rotate.CenterX, Entry->Data.Rotate.CenterY, (4 - Entry->Data.Rotate.Steps) & 0x03);
+		Entry->Data.Rotate.Steps = (4 - Entry->Data.Rotate.Steps) & 0x03;
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a clear/restore poly operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoClearPoly(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		if (Entry->Data.ClearPoly.Clear)
+			RestoreToPolygon(PCB->Data, type, Entry->Data.ClearPoly.Layer, ptr3);
+		else
+			ClearFromPolygon(PCB->Data, type, Entry->Data.ClearPoly.Layer, ptr3);
+		Entry->Data.ClearPoly.Clear = !Entry->Data.ClearPoly.Clear;
+		return pcb_true;
+	}
+	return pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a 'change name' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoChangeName(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		Entry->Data.ChangeName.Name = (char *) (ChangeObjectName(type, ptr1, ptr2, ptr3, Entry->Data.ChangeName.Name));
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a 'change oinnum' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoChangePinnum(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		Entry->Data.ChangeName.Name = (char *) (ChangeObjectPinnum(type, ptr1, ptr2, ptr3, Entry->Data.ChangeName.Name));
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a 2ndSize change operation
+ */
+static pcb_bool UndoChange2ndSize(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	Coord swap;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		swap = ((PinTypePtr) ptr2)->DrillingHole;
+		if (andDraw)
+			EraseObject(type, ptr1, ptr2);
+		((PinTypePtr) ptr2)->DrillingHole = Entry->Data.Size;
+		Entry->Data.Size = swap;
+		DrawObject(type, ptr1, ptr2);
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a ChangeAngles change operation
+ */
+static pcb_bool UndoChangeAngles(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	double old_sa, old_da;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type == PCB_TYPE_ARC) {
+		LayerTypePtr Layer = (LayerTypePtr) ptr1;
+		ArcTypePtr a = (ArcTypePtr) ptr2;
+		r_delete_entry(Layer->arc_tree, (BoxTypePtr) a);
+		old_sa = a->StartAngle;
+		old_da = a->Delta;
+		if (andDraw)
+			EraseObject(type, Layer, a);
+		a->StartAngle = Entry->Data.AngleChange.angle[0];
+		a->Delta = Entry->Data.AngleChange.angle[1];
+		SetArcBoundingBox(a);
+		r_insert_entry(Layer->arc_tree, (BoxTypePtr) a, 0);
+		Entry->Data.AngleChange.angle[0] = old_sa;
+		Entry->Data.AngleChange.angle[1] = old_da;
+		DrawObject(type, ptr1, a);
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a ChangeRadii change operation
+ */
+static pcb_bool UndoChangeRadii(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	Coord old_w, old_h;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type == PCB_TYPE_ARC) {
+		LayerTypePtr Layer = (LayerTypePtr) ptr1;
+		ArcTypePtr a = (ArcTypePtr) ptr2;
+		r_delete_entry(Layer->arc_tree, (BoxTypePtr) a);
+		old_w = a->Width;
+		old_h = a->Height;
+		if (andDraw)
+			EraseObject(type, Layer, a);
+		a->Width = Entry->Data.Move.DX;
+		a->Height = Entry->Data.Move.DY;
+		SetArcBoundingBox(a);
+		r_insert_entry(Layer->arc_tree, (BoxTypePtr) a, 0);
+		Entry->Data.Move.DX = old_w;
+		Entry->Data.Move.DY = old_h;
+		DrawObject(type, ptr1, a);
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a clearance size change operation
+ */
+static pcb_bool UndoChangeClearSize(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	Coord swap;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		swap = ((PinTypePtr) ptr2)->Clearance;
+		RestoreToPolygon(PCB->Data, type, ptr1, ptr2);
+		if (andDraw)
+			EraseObject(type, ptr1, ptr2);
+		((PinTypePtr) ptr2)->Clearance = Entry->Data.Size;
+		ClearFromPolygon(PCB->Data, type, ptr1, ptr2);
+		Entry->Data.Size = swap;
+		if (andDraw)
+			DrawObject(type, ptr1, ptr2);
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a mask size change operation
+ */
+static pcb_bool UndoChangeMaskSize(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	Coord swap;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type & (PCB_TYPE_VIA | PCB_TYPE_PIN | PCB_TYPE_PAD)) {
+		swap = (type == PCB_TYPE_PAD ? ((PadTypePtr) ptr2)->Mask : ((PinTypePtr) ptr2)->Mask);
+		if (andDraw)
+			EraseObject(type, ptr1, ptr2);
+		if (type == PCB_TYPE_PAD)
+			((PadTypePtr) ptr2)->Mask = Entry->Data.Size;
+		else
+			((PinTypePtr) ptr2)->Mask = Entry->Data.Size;
+		Entry->Data.Size = swap;
+		if (andDraw)
+			DrawObject(type, ptr1, ptr2);
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a Size change operation
+ */
+static pcb_bool UndoChangeSize(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3, *ptr1e;
+	int type;
+	Coord swap;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+		if (type == PCB_TYPE_ELEMENT_NAME)
+			ptr1e = NULL;
+		else
+			ptr1e = ptr1;
+
+	if (type != PCB_TYPE_NONE) {
+		/* Wow! can any object be treated as a pin type for size change?? */
+		/* pins, vias, lines, and arcs can. Text can't but it has it's own mechanism */
+		swap = ((PinTypePtr) ptr2)->Thickness;
+		RestoreToPolygon(PCB->Data, type, ptr1, ptr2);
+		if ((andDraw) && (ptr1e != NULL))
+			EraseObject(type, ptr1e, ptr2);
+		((PinTypePtr) ptr2)->Thickness = Entry->Data.Size;
+		Entry->Data.Size = swap;
+		ClearFromPolygon(PCB->Data, type, ptr1, ptr2);
+		if (andDraw)
+			DrawObject(type, ptr1, ptr2);
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a FLAG change operation
+ */
+static pcb_bool UndoFlag(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr1e, *ptr2, *ptr3;
+	int type;
+	FlagType swap;
+	int must_redraw;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		FlagType f1, f2;
+		PinTypePtr pin = (PinTypePtr) ptr2;
+
+		if ((type == PCB_TYPE_ELEMENT) || (type == PCB_TYPE_ELEMENT_NAME))
+			ptr1e = NULL;
+		else
+			ptr1e = ptr1;
+
+		swap = pin->Flags;
+
+		must_redraw = 0;
+		f1 = MaskFlags(pin->Flags, ~DRAW_FLAGS);
+		f2 = MaskFlags(Entry->Data.Flags, ~DRAW_FLAGS);
+
+		if (!FLAGS_EQUAL(f1, f2))
+			must_redraw = 1;
+
+		if (andDraw && must_redraw && (ptr1e != NULL))
+			EraseObject(type, ptr1e, ptr2);
+
+		pin->Flags = Entry->Data.Flags;
+
+		Entry->Data.Flags = swap;
+
+		if (andDraw && must_redraw)
+			DrawObject(type, ptr1, ptr2);
+		return (pcb_true);
+	}
+	Message(PCB_MSG_DEFAULT, "hace Internal error: Can't find ID %d type %08x\n", Entry->ID, Entry->Kind);
+	Message(PCB_MSG_DEFAULT, "for UndoFlag Operation. Previous flags: %s\n", flags_to_string(Entry->Data.Flags, 0));
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a mirror operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoMirror(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type == PCB_TYPE_ELEMENT) {
+		ElementTypePtr element = (ElementTypePtr) ptr3;
+		if (andDraw)
+			EraseElement(element);
+		MirrorElementCoordinates(PCB->Data, element, Entry->Data.Move.DY);
+		if (andDraw)
+			DrawElement(element);
+		return (pcb_true);
+	}
+	Message(PCB_MSG_DEFAULT, "hace Internal error: UndoMirror on object type %d\n", type);
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a 'copy' or 'create' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoCopyOrCreate(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		if (!RemoveList)
+			RemoveList = CreateNewBuffer();
+		if (andDraw)
+			EraseObject(type, ptr1, ptr2);
+		/* in order to make this re-doable we move it to the RemoveList */
+		MoveObjectToBuffer(RemoveList, PCB->Data, type, ptr1, ptr2, ptr3);
+		Entry->Type = UNDO_REMOVE;
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an object from a 'move' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoMove(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		MoveObject(type, ptr1, ptr2, ptr3, -Entry->Data.Move.DX, -Entry->Data.Move.DY);
+		Entry->Data.Move.DX *= -1;
+		Entry->Data.Move.DY *= -1;
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ----------------------------------------------------------------------
+ * recovers an object from a 'remove' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoRemove(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(RemoveList, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		if (andDraw)
+			DrawRecoveredObject(type, ptr1, ptr2, ptr3);
+		MoveObjectToBuffer(PCB->Data, RemoveList, type, ptr1, ptr2, ptr3);
+		Entry->Type = UNDO_CREATE;
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ----------------------------------------------------------------------
+ * recovers an object from a 'move to another layer' operation
+ * returns pcb_true if anything has been recovered
+ */
+static pcb_bool UndoMoveToLayer(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type, swap;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
+	if (type != PCB_TYPE_NONE) {
+		swap = GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1);
+		MoveObjectToLayer(type, ptr1, ptr2, ptr3, LAYER_PTR(Entry->Data.MoveToLayer.OriginalLayer), pcb_true);
+		Entry->Data.MoveToLayer.OriginalLayer = swap;
+		return (pcb_true);
+	}
+	return (pcb_false);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers a removed polygon point
+ * returns pcb_true on success
+ */
+static pcb_bool UndoRemovePoint(UndoListTypePtr Entry)
+{
+	LayerTypePtr layer;
+	PolygonTypePtr polygon;
+	void *ptr3;
+	int type;
+
+	/* lookup entry (polygon not point was saved) by it's ID */
+	assert(Entry->Kind == PCB_TYPE_POLYGON);
+	type = SearchObjectByID(PCB->Data, (void **) &layer, (void **) &polygon, &ptr3, Entry->ID, Entry->Kind);
+	switch (type) {
+	case PCB_TYPE_POLYGON:						/* restore the removed point */
+		{
+			/* recover the point */
+			if (andDraw && layer->On)
+				ErasePolygon(polygon);
+			InsertPointIntoObject(PCB_TYPE_POLYGON, layer, polygon,
+														&Entry->Data.RemovedPoint.Index,
+														Entry->Data.RemovedPoint.X,
+														Entry->Data.RemovedPoint.Y, pcb_true, Entry->Data.RemovedPoint.last_in_contour);
+
+			polygon->Points[Entry->Data.RemovedPoint.Index].ID = Entry->Data.RemovedPoint.ID;
+			if (andDraw && layer->On)
+				DrawPolygon(layer, polygon);
+			Entry->Type = UNDO_INSERT_POINT;
+			Entry->ID = Entry->Data.RemovedPoint.ID;
+			Entry->Kind = PCB_TYPE_POLYGON_POINT;
+			return (pcb_true);
+		}
+
+	default:
+		return (pcb_false);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an inserted polygon point
+ * returns pcb_true on success
+ */
+static pcb_bool UndoInsertPoint(UndoListTypePtr Entry)
+{
+	LayerTypePtr layer;
+	PolygonTypePtr polygon;
+	PointTypePtr pnt;
+	int type;
+	pcb_cardinal_t point_idx;
+	pcb_cardinal_t hole;
+	pcb_bool last_in_contour = pcb_false;
+
+	assert(Entry->Kind == PCB_TYPE_POLYGON_POINT);
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(PCB->Data, (void **) &layer, (void **) &polygon, (void **) &pnt, Entry->ID, Entry->Kind);
+	switch (type) {
+	case PCB_TYPE_POLYGON_POINT:			/* removes an inserted polygon point */
+		{
+			if (andDraw && layer->On)
+				ErasePolygon(polygon);
+
+			/* Check whether this point was at the end of its contour.
+			 * If so, we need to flag as such when re-adding the point
+			 * so it goes back in the correct place
+			 */
+			point_idx = polygon_point_idx(polygon, pnt);
+			for (hole = 0; hole < polygon->HoleIndexN; hole++)
+				if (point_idx == polygon->HoleIndex[hole] - 1)
+					last_in_contour = pcb_true;
+			if (point_idx == polygon->PointN - 1)
+				last_in_contour = pcb_true;
+			Entry->Data.RemovedPoint.last_in_contour = last_in_contour;
+
+			Entry->Data.RemovedPoint.X = pnt->X;
+			Entry->Data.RemovedPoint.Y = pnt->Y;
+			Entry->Data.RemovedPoint.ID = pnt->ID;
+			Entry->ID = polygon->ID;
+			Entry->Kind = PCB_TYPE_POLYGON;
+			Entry->Type = UNDO_REMOVE_POINT;
+			Entry->Data.RemovedPoint.Index = point_idx;
+			DestroyObject(PCB->Data, PCB_TYPE_POLYGON_POINT, layer, polygon, pnt);
+			if (andDraw && layer->On)
+				DrawPolygon(layer, polygon);
+			return (pcb_true);
+		}
+
+	default:
+		return (pcb_false);
+	}
+}
+
+static pcb_bool UndoSwapCopiedObject(UndoListTypePtr Entry)
+{
+	void *ptr1, *ptr2, *ptr3;
+	void *ptr1b, *ptr2b, *ptr3b;
+	AnyObjectType *obj, *obj2;
+	int type;
+	long int swap_id;
+
+	/* lookup entry by it's ID */
+	type = SearchObjectByID(RemoveList, &ptr1, &ptr2, &ptr3, Entry->Data.CopyID, Entry->Kind);
+	if (type == PCB_TYPE_NONE)
+		return pcb_false;
+
+	type = SearchObjectByID(PCB->Data, &ptr1b, &ptr2b, &ptr3b, Entry->ID, Entry->Kind);
+	if (type == PCB_TYPE_NONE)
+		return FALSE;
+
+	obj = (AnyObjectType *) ptr2;
+	obj2 = (AnyObjectType *) ptr2b;
+
+	swap_id = obj->ID;
+	obj->ID = obj2->ID;
+	obj2->ID = swap_id;
+
+	MoveObjectToBuffer(RemoveList, PCB->Data, type, ptr1b, ptr2b, ptr3b);
+
+	if (andDraw)
+		DrawRecoveredObject(Entry->Kind, ptr1, ptr2, ptr3);
+
+	obj = (AnyObjectType *) MoveObjectToBuffer(PCB->Data, RemoveList, type, ptr1, ptr2, ptr3);
+	if (Entry->Kind == PCB_TYPE_POLYGON)
+		InitClip(PCB->Data, (LayerTypePtr) ptr1b, (PolygonType *) obj);
+	return (pcb_true);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an removed polygon point
+ * returns pcb_true on success
+ */
+static pcb_bool UndoRemoveContour(UndoListTypePtr Entry)
+{
+	assert(Entry->Kind == PCB_TYPE_POLYGON);
+	return UndoSwapCopiedObject(Entry);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an inserted polygon point
+ * returns pcb_true on success
+ */
+static pcb_bool UndoInsertContour(UndoListTypePtr Entry)
+{
+	assert(Entry->Kind == PCB_TYPE_POLYGON);
+	return UndoSwapCopiedObject(Entry);
+}
+
+/* ---------------------------------------------------------------------------
+ * undo a layer change
+ * returns pcb_true on success
+ */
+static pcb_bool UndoLayerChange(UndoListTypePtr Entry)
+{
+	LayerChangeTypePtr l = &Entry->Data.LayerChange;
+	int tmp;
+
+	tmp = l->new_index;
+	l->new_index = l->old_index;
+	l->old_index = tmp;
+
+	if (MoveLayer(l->old_index, l->new_index))
+		return pcb_false;
+	else
+		return pcb_true;
+}
+
+/* ---------------------------------------------------------------------------
+ * undo a netlist change
+ * returns pcb_true on success
+ */
+static pcb_bool UndoNetlistChange(UndoListTypePtr Entry)
+{
+	NetlistChangeTypePtr l = &Entry->Data.NetlistChange;
+	unsigned int i, j;
+	LibraryTypePtr lib, saved;
+
+	lib = l->lib;
+	saved = l->old;
+
+	/* iterate over each net */
+	for (i = 0; i < lib->MenuN; i++) {
+		free(lib->Menu[i].Name);
+		free(lib->Menu[i].directory);
+		free(lib->Menu[i].Style);
+
+		/* iterate over each pin on the net */
+		for (j = 0; j < lib->Menu[i].EntryN; j++) {
+			if (!lib->Menu[i].Entry[j].ListEntry_dontfree)
+				free((char*)lib->Menu[i].Entry[j].ListEntry);
+
+			free((char*)lib->Menu[i].Entry[j].Package);
+			free((char*)lib->Menu[i].Entry[j].Value);
+			free((char*)lib->Menu[i].Entry[j].Description);
+		}
+	}
+
+	free(lib->Menu);
+
+	*lib = *saved;
+
+	pcb_netlist_changed(0);
+	return pcb_true;
+}
+
+/* ---------------------------------------------------------------------------
+ * undo of any 'hard to recover' operation
+ *
+ * returns the bitfield for the types of operations that were undone
+ */
+int Undo(pcb_bool draw)
+{
+	UndoListTypePtr ptr;
+	int Types = 0;
+	int unique;
+	pcb_bool error_undoing = pcb_false;
+
+	unique = conf_core.editor.unique_names;
+	conf_force_set_bool(conf_core.editor.unique_names, 0);
+
+	andDraw = draw;
+
+	if (Serial == 0) {
+		Message(PCB_MSG_DEFAULT, _("ERROR: Attempt to Undo() with Serial == 0\n" "       Please save your work and report this bug.\n"));
+		return 0;
+	}
+
+	if (UndoN == 0) {
+		Message(PCB_MSG_DEFAULT, _("Nothing to undo - buffer is empty\n"));
+		return 0;
+	}
+
+	Serial--;
+
+	ptr = &UndoList[UndoN - 1];
+
+	if (ptr->Serial > Serial) {
+		Message(PCB_MSG_DEFAULT, _("ERROR: Bad undo serial number %d in undo stack - expecting %d or lower\n"
+							"       Please save your work and report this bug.\n"), ptr->Serial, Serial);
+
+	/* It is likely that the serial number got corrupted through some bad
+		 * use of the SaveUndoSerialNumber() / RestoreUndoSerialNumber() APIs.
+		 *
+		 * Reset the serial number to be consistent with that of the last
+		 * operation on the undo stack in the hope that this might clear
+		 * the problem and  allow the user to hit Undo again.
+		 */
+		Serial = ptr->Serial + 1;
+		return 0;
+	}
+
+	LockUndo();										/* lock undo module to prevent from loops */
+
+	/* Loop over all entries with the correct serial number */
+	for (; UndoN && ptr->Serial == Serial; ptr--, UndoN--, RedoN++) {
+		int undid = PerformUndo(ptr);
+		if (undid == 0)
+			error_undoing = pcb_true;
+		Types |= undid;
+	}
+
+	UnlockUndo();
+
+	if (error_undoing)
+		Message(PCB_MSG_DEFAULT, _("ERROR: Failed to undo some operations\n"));
+
+	if (Types && andDraw)
+		Draw();
+
+	/* restore the unique flag setting */
+	conf_force_set_bool(conf_core.editor.unique_names, unique);
+
+	return Types;
+}
+
+static int PerformUndo(UndoListTypePtr ptr)
+{
+	switch (ptr->Type) {
+	case UNDO_CHANGENAME:
+		if (UndoChangeName(ptr))
+			return (UNDO_CHANGENAME);
+		break;
+
+	case UNDO_CHANGEPINNUM:
+		if (UndoChangePinnum(ptr))
+			return (UNDO_CHANGEPINNUM);
+		break;
+
+	case UNDO_CREATE:
+		if (UndoCopyOrCreate(ptr))
+			return (UNDO_CREATE);
+		break;
+
+	case UNDO_MOVE:
+		if (UndoMove(ptr))
+			return (UNDO_MOVE);
+		break;
+
+	case UNDO_REMOVE:
+		if (UndoRemove(ptr))
+			return (UNDO_REMOVE);
+		break;
+
+	case UNDO_REMOVE_POINT:
+		if (UndoRemovePoint(ptr))
+			return (UNDO_REMOVE_POINT);
+		break;
+
+	case UNDO_INSERT_POINT:
+		if (UndoInsertPoint(ptr))
+			return (UNDO_INSERT_POINT);
+		break;
+
+	case UNDO_REMOVE_CONTOUR:
+		if (UndoRemoveContour(ptr))
+			return (UNDO_REMOVE_CONTOUR);
+		break;
+
+	case UNDO_INSERT_CONTOUR:
+		if (UndoInsertContour(ptr))
+			return (UNDO_INSERT_CONTOUR);
+		break;
+
+	case UNDO_ROTATE:
+		if (UndoRotate(ptr))
+			return (UNDO_ROTATE);
+		break;
+
+	case UNDO_CLEAR:
+		if (UndoClearPoly(ptr))
+			return (UNDO_CLEAR);
+		break;
+
+	case UNDO_MOVETOLAYER:
+		if (UndoMoveToLayer(ptr))
+			return (UNDO_MOVETOLAYER);
+		break;
+
+	case UNDO_FLAG:
+		if (UndoFlag(ptr))
+			return (UNDO_FLAG);
+		break;
+
+	case UNDO_CHANGESIZE:
+		if (UndoChangeSize(ptr))
+			return (UNDO_CHANGESIZE);
+		break;
+
+	case UNDO_CHANGECLEARSIZE:
+		if (UndoChangeClearSize(ptr))
+			return (UNDO_CHANGECLEARSIZE);
+		break;
+
+	case UNDO_CHANGEMASKSIZE:
+		if (UndoChangeMaskSize(ptr))
+			return (UNDO_CHANGEMASKSIZE);
+		break;
+
+	case UNDO_CHANGE2NDSIZE:
+		if (UndoChange2ndSize(ptr))
+			return (UNDO_CHANGE2NDSIZE);
+		break;
+
+	case UNDO_CHANGEANGLES:
+		if (UndoChangeAngles(ptr))
+			return (UNDO_CHANGEANGLES);
+		break;
+
+	case UNDO_CHANGERADII:
+		if (UndoChangeRadii(ptr))
+			return (UNDO_CHANGERADII);
+		break;
+
+	case UNDO_LAYERCHANGE:
+		if (UndoLayerChange(ptr))
+			return (UNDO_LAYERCHANGE);
+		break;
+
+	case UNDO_NETLISTCHANGE:
+		if (UndoNetlistChange(ptr))
+			return (UNDO_NETLISTCHANGE);
+		break;
+
+	case UNDO_MIRROR:
+		if (UndoMirror(ptr))
+			return (UNDO_MIRROR);
+		break;
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * redo of any 'hard to recover' operation
+ *
+ * returns the number of operations redone
+ */
+int Redo(pcb_bool draw)
+{
+	UndoListTypePtr ptr;
+	int Types = 0;
+	pcb_bool error_undoing = pcb_false;
+
+	andDraw = draw;
+
+	if (RedoN == 0) {
+		Message(PCB_MSG_DEFAULT, _("Nothing to redo. Perhaps changes have been made since last undo\n"));
+		return 0;
+	}
+
+	ptr = &UndoList[UndoN];
+
+	if (ptr->Serial < Serial) {
+		Message(PCB_MSG_DEFAULT, _("ERROR: Bad undo serial number %d in redo stack - expecting %d or higher\n"
+							"       Please save your work and report this bug.\n"), ptr->Serial, Serial);
+
+		/* It is likely that the serial number got corrupted through some bad
+		 * use of the SaveUndoSerialNumber() / RestoreUndoSerialNumber() APIs.
+		 *
+		 * Reset the serial number to be consistent with that of the first
+		 * operation on the redo stack in the hope that this might clear
+		 * the problem and  allow the user to hit Redo again.
+		 */
+		Serial = ptr->Serial;
+		return 0;
+	}
+
+	LockUndo();										/* lock undo module to prevent from loops */
+
+	/* and loop over all entries with the correct serial number */
+	for (; RedoN && ptr->Serial == Serial; ptr++, UndoN++, RedoN--) {
+		int undid = PerformUndo(ptr);
+		if (undid == 0)
+			error_undoing = pcb_true;
+		Types |= undid;
+	}
+
+	/* Make next serial number current */
+	Serial++;
+
+	UnlockUndo();
+
+	if (error_undoing)
+		Message(PCB_MSG_DEFAULT, _("ERROR: Failed to redo some operations\n"));
+
+	if (Types && andDraw)
+		Draw();
+
+	return Types;
+}
+
+/* ---------------------------------------------------------------------------
+ * restores the serial number of the undo list
+ */
+void RestoreUndoSerialNumber(void)
+{
+	if (added_undo_between_increment_and_restore)
+		Message(PCB_MSG_DEFAULT, _("ERROR: Operations were added to the Undo stack with an incorrect serial number\n"));
+	between_increment_and_restore = pcb_false;
+	added_undo_between_increment_and_restore = pcb_false;
+	Serial = SavedSerial;
+}
+
+/* ---------------------------------------------------------------------------
+ * saves the serial number of the undo list
+ */
+void SaveUndoSerialNumber(void)
+{
+	Bumped = pcb_false;
+	between_increment_and_restore = pcb_false;
+	added_undo_between_increment_and_restore = pcb_false;
+	SavedSerial = Serial;
+}
+
+/* ---------------------------------------------------------------------------
+ * increments the serial number of the undo list
+ * it's not done automatically because some operations perform more
+ * than one request with the same serial #
+ */
+void IncrementUndoSerialNumber(void)
+{
+	if (!Locked) {
+		/* Set the changed flag if anything was added prior to this bump */
+		if (UndoN > 0 && UndoList[UndoN - 1].Serial == Serial)
+			SetChangedFlag(pcb_true);
+		Serial++;
+		Bumped = pcb_true;
+		between_increment_and_restore = pcb_true;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * releases memory of the undo- and remove list
+ */
+void ClearUndoList(pcb_bool Force)
+{
+	UndoListTypePtr undo;
+
+	if (UndoN && (Force || gui->confirm_dialog("OK to clear 'undo' buffer?", 0))) {
+		/* release memory allocated by objects in undo list */
+		for (undo = UndoList; UndoN; undo++, UndoN--) {
+			if ((undo->Type == UNDO_CHANGENAME) || (undo->Type == UNDO_CHANGEPINNUM))
+				free(undo->Data.ChangeName.Name);
+		}
+		free(UndoList);
+		UndoList = NULL;
+		if (RemoveList) {
+			FreeDataMemory(RemoveList);
+			free(RemoveList);
+			RemoveList = NULL;
+		}
+
+		/* reset some counters */
+		UndoN = UndoMax = RedoN = 0;
+	}
+
+	/* reset counter in any case */
+	Serial = 1;
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of clearpoly objects
+ */
+void AddObjectToClearPolyUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3, pcb_bool clear)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CLEAR, OBJECT_ID(Ptr3), Type);
+		undo->Data.ClearPoly.Clear = clear;
+		undo->Data.ClearPoly.Layer = (LayerTypePtr) Ptr1;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of mirrored objects
+ */
+void AddObjectToMirrorUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord yoff)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_MIRROR, OBJECT_ID(Ptr3), Type);
+		undo->Data.Move.DY = yoff;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of rotated objects
+ */
+void AddObjectToRotateUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord CenterX, Coord CenterY, pcb_uint8_t Steps)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_ROTATE, OBJECT_ID(Ptr3), Type);
+		undo->Data.Rotate.CenterX = CenterX;
+		undo->Data.Rotate.CenterY = CenterY;
+		undo->Data.Rotate.Steps = Steps;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of removed objects and removes it from
+ * the current PCB
+ */
+void MoveObjectToRemoveUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (Locked)
+		return;
+
+	if (!RemoveList)
+		RemoveList = CreateNewBuffer();
+
+	GetUndoSlot(UNDO_REMOVE, OBJECT_ID(Ptr3), Type);
+	MoveObjectToBuffer(RemoveList, PCB->Data, Type, Ptr1, Ptr2, Ptr3);
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of removed polygon/... points
+ */
+void AddObjectToRemovePointUndoList(int Type, void *Ptr1, void *Ptr2, pcb_cardinal_t index)
+{
+	UndoListTypePtr undo;
+	PolygonTypePtr polygon = (PolygonTypePtr) Ptr2;
+	pcb_cardinal_t hole;
+	pcb_bool last_in_contour = pcb_false;
+
+	if (!Locked) {
+		switch (Type) {
+		case PCB_TYPE_POLYGON_POINT:
+			{
+				/* save the ID of the parent object; else it will be
+				 * impossible to recover the point
+				 */
+				undo = GetUndoSlot(UNDO_REMOVE_POINT, OBJECT_ID(polygon), PCB_TYPE_POLYGON);
+				undo->Data.RemovedPoint.X = polygon->Points[index].X;
+				undo->Data.RemovedPoint.Y = polygon->Points[index].Y;
+				undo->Data.RemovedPoint.ID = polygon->Points[index].ID;
+				undo->Data.RemovedPoint.Index = index;
+
+				/* Check whether this point was at the end of its contour.
+				 * If so, we need to flag as such when re-adding the point
+				 * so it goes back in the correct place
+				 */
+				for (hole = 0; hole < polygon->HoleIndexN; hole++)
+					if (index == polygon->HoleIndex[hole] - 1)
+						last_in_contour = pcb_true;
+				if (index == polygon->PointN - 1)
+					last_in_contour = pcb_true;
+				undo->Data.RemovedPoint.last_in_contour = last_in_contour;
+			}
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of inserted polygon/... points
+ */
+void AddObjectToInsertPointUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (!Locked)
+		GetUndoSlot(UNDO_INSERT_POINT, OBJECT_ID(Ptr3), Type);
+}
+
+static void CopyObjectToUndoList(int undo_type, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	UndoListTypePtr undo;
+	AnyObjectType *copy;
+
+	if (Locked)
+		return;
+
+	if (!RemoveList)
+		RemoveList = CreateNewBuffer();
+
+	undo = GetUndoSlot(undo_type, OBJECT_ID(Ptr2), Type);
+	copy = (AnyObjectType *) CopyObjectToBuffer(RemoveList, PCB->Data, Type, Ptr1, Ptr2, Ptr3);
+	undo->Data.CopyID = copy->ID;
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of removed contours
+ * (Actually just takes a copy of the whole polygon to restore)
+ */
+void AddObjectToRemoveContourUndoList(int Type, LayerType * Layer, PolygonType * Polygon)
+{
+	CopyObjectToUndoList(UNDO_REMOVE_CONTOUR, Type, Layer, Polygon, NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of insert contours
+ * (Actually just takes a copy of the whole polygon to restore)
+ */
+void AddObjectToInsertContourUndoList(int Type, LayerType * Layer, PolygonType * Polygon)
+{
+	CopyObjectToUndoList(UNDO_INSERT_CONTOUR, Type, Layer, Polygon, NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of moved objects
+ */
+void AddObjectToMoveUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord DX, Coord DY)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_MOVE, OBJECT_ID(Ptr3), Type);
+		undo->Data.Move.DX = DX;
+		undo->Data.Move.DY = DY;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with changed names
+ */
+void AddObjectToChangeNameUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3, char *OldName)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGENAME, OBJECT_ID(Ptr3), Type);
+		undo->Data.ChangeName.Name = OldName;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with changed pinnums
+ */
+void AddObjectToChangePinnumUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3, char *OldName)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGEPINNUM, OBJECT_ID(Ptr3), Type);
+		undo->Data.ChangeName.Name = OldName;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects moved to another layer
+ */
+void AddObjectToMoveToLayerUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_MOVETOLAYER, OBJECT_ID(Ptr3), Type);
+		undo->Data.MoveToLayer.OriginalLayer = GetLayerNumber(PCB->Data, (LayerTypePtr) Ptr1);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of created objects
+ */
+void AddObjectToCreateUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	if (!Locked)
+		GetUndoSlot(UNDO_CREATE, OBJECT_ID(Ptr3), Type);
+	ClearFromPolygon(PCB->Data, Type, Ptr1, Ptr2);
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with flags changed
+ */
+void AddObjectToFlagUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_FLAG, OBJECT_ID(Ptr2), Type);
+		undo->Data.Flags = ((PinTypePtr) Ptr2)->Flags;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with Size changes
+ */
+void AddObjectToSizeUndoList(int Type, void *ptr1, void *ptr2, void *ptr3)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGESIZE, OBJECT_ID(ptr2), Type);
+		switch (Type) {
+		case PCB_TYPE_PIN:
+		case PCB_TYPE_VIA:
+			undo->Data.Size = ((PinTypePtr) ptr2)->Thickness;
+			break;
+		case PCB_TYPE_LINE:
+		case PCB_TYPE_ELEMENT_LINE:
+			undo->Data.Size = ((LineTypePtr) ptr2)->Thickness;
+			break;
+		case PCB_TYPE_TEXT:
+		case PCB_TYPE_ELEMENT_NAME:
+			undo->Data.Size = ((TextTypePtr) ptr2)->Scale;
+			break;
+		case PCB_TYPE_PAD:
+			undo->Data.Size = ((PadTypePtr) ptr2)->Thickness;
+			break;
+		case PCB_TYPE_ARC:
+		case PCB_TYPE_ELEMENT_ARC:
+			undo->Data.Size = ((ArcTypePtr) ptr2)->Thickness;
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with Size changes
+ */
+void AddObjectToClearSizeUndoList(int Type, void *ptr1, void *ptr2, void *ptr3)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGECLEARSIZE, OBJECT_ID(ptr2), Type);
+		switch (Type) {
+		case PCB_TYPE_PIN:
+		case PCB_TYPE_VIA:
+			undo->Data.Size = ((PinTypePtr) ptr2)->Clearance;
+			break;
+		case PCB_TYPE_LINE:
+			undo->Data.Size = ((LineTypePtr) ptr2)->Clearance;
+			break;
+		case PCB_TYPE_PAD:
+			undo->Data.Size = ((PadTypePtr) ptr2)->Clearance;
+			break;
+		case PCB_TYPE_ARC:
+			undo->Data.Size = ((ArcTypePtr) ptr2)->Clearance;
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with Size changes
+ */
+void AddObjectToMaskSizeUndoList(int Type, void *ptr1, void *ptr2, void *ptr3)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGEMASKSIZE, OBJECT_ID(ptr2), Type);
+		switch (Type) {
+		case PCB_TYPE_PIN:
+		case PCB_TYPE_VIA:
+			undo->Data.Size = ((PinTypePtr) ptr2)->Mask;
+			break;
+		case PCB_TYPE_PAD:
+			undo->Data.Size = ((PadTypePtr) ptr2)->Mask;
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of objects with 2ndSize changes
+ */
+void AddObjectTo2ndSizeUndoList(int Type, void *ptr1, void *ptr2, void *ptr3)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGE2NDSIZE, OBJECT_ID(ptr2), Type);
+		if (Type == PCB_TYPE_PIN || Type == PCB_TYPE_VIA)
+			undo->Data.Size = ((PinTypePtr) ptr2)->DrillingHole;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of changed angles.  Note that you must
+ * call this before changing the angles, passing the new start/delta.
+ */
+void AddObjectToChangeAnglesUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	UndoListTypePtr undo;
+	ArcTypePtr a = (ArcTypePtr) Ptr3;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGEANGLES, OBJECT_ID(Ptr3), Type);
+		undo->Data.AngleChange.angle[0] = a->StartAngle;
+		undo->Data.AngleChange.angle[1] = a->Delta;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of changed radii.  Note that you must
+ * call this before changing the radii, passing the new width/height.
+ */
+void AddObjectToChangeRadiiUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+	UndoListTypePtr undo;
+	ArcTypePtr a = (ArcTypePtr) Ptr3;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_CHANGERADII, OBJECT_ID(Ptr3), Type);
+		undo->Data.Move.DX = a->Width;
+		undo->Data.Move.DY = a->Height;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds a layer change (new, delete, move) to the undo list.
+ */
+void AddLayerChangeToUndoList(int old_index, int new_index)
+{
+	UndoListTypePtr undo;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_LAYERCHANGE, 0, 0);
+		undo->Data.LayerChange.old_index = old_index;
+		undo->Data.LayerChange.new_index = new_index;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * adds a netlist change to the undo list
+ */
+void AddNetlistLibToUndoList(LibraryTypePtr lib)
+{
+	UndoListTypePtr undo;
+	unsigned int i, j;
+	LibraryTypePtr old;
+
+	if (!Locked) {
+		undo = GetUndoSlot(UNDO_NETLISTCHANGE, 0, 0);
+		/* keep track of where the data needs to go */
+		undo->Data.NetlistChange.lib = lib;
+
+		/* and what the old data is that we'll need to restore */
+		undo->Data.NetlistChange.old = (LibraryTypePtr) malloc(sizeof(LibraryTypePtr));
+		old = undo->Data.NetlistChange.old;
+		old->MenuN = lib->MenuN;
+		old->MenuMax = lib->MenuMax;
+		old->Menu = (LibraryMenuTypePtr) malloc(old->MenuMax * sizeof(LibraryMenuType));
+		if (old->Menu == NULL) {
+			fprintf(stderr, "malloc() failed in AddNetlistLibToUndoList\n");
+			exit(1);
+		}
+
+		/* iterate over each net */
+		for (i = 0; i < lib->MenuN; i++) {
+			old->Menu[i].EntryN = lib->Menu[i].EntryN;
+			old->Menu[i].EntryMax = lib->Menu[i].EntryMax;
+
+			old->Menu[i].Name = lib->Menu[i].Name ? pcb_strdup(lib->Menu[i].Name) : NULL;
+
+			old->Menu[i].directory = lib->Menu[i].directory ? pcb_strdup(lib->Menu[i].directory) : NULL;
+
+			old->Menu[i].Style = lib->Menu[i].Style ? pcb_strdup(lib->Menu[i].Style) : NULL;
+
+
+			old->Menu[i].Entry = (LibraryEntryTypePtr) malloc(old->Menu[i].EntryMax * sizeof(LibraryEntryType));
+			if (old->Menu[i].Entry == NULL) {
+				fprintf(stderr, "malloc() failed in AddNetlistLibToUndoList\n");
+				exit(1);
+			}
+
+			/* iterate over each pin on the net */
+			for (j = 0; j < lib->Menu[i].EntryN; j++) {
+
+				old->Menu[i].Entry[j].ListEntry = lib->Menu[i].Entry[j].ListEntry ? pcb_strdup(lib->Menu[i].Entry[j].ListEntry) : NULL;
+				old->Menu[i].Entry[j].ListEntry_dontfree = 0;
+
+				old->Menu[i].Entry[j].Package = lib->Menu[i].Entry[j].Package ? pcb_strdup(lib->Menu[i].Entry[j].Package) : NULL;
+
+				old->Menu[i].Entry[j].Value = lib->Menu[i].Entry[j].Value ? pcb_strdup(lib->Menu[i].Entry[j].Value) : NULL;
+
+				old->Menu[i].Entry[j].Description =
+					lib->Menu[i].Entry[j].Description ? pcb_strdup(lib->Menu[i].Entry[j].Description) : NULL;
+
+
+			}
+		}
+
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * set lock flag
+ */
+void LockUndo(void)
+{
+	Locked = pcb_true;
+}
+
+/* ---------------------------------------------------------------------------
+ * reset lock flag
+ */
+void UnlockUndo(void)
+{
+	Locked = pcb_false;
+}
+
+/* ---------------------------------------------------------------------------
+ * return undo lock state
+ */
+pcb_bool Undoing(void)
+{
+	return (Locked);
+}
+
+#ifndef NDEBUG
+static const char *undo_type2str(int type)
+{
+	static char buff[32];
+	switch(type) {
+		case UNDO_CHANGENAME: return "changename";
+		case UNDO_MOVE: return "move";
+		case UNDO_REMOVE: return "remove";
+		case UNDO_REMOVE_POINT: return "remove_point";
+		case UNDO_INSERT_POINT: return "insert_point";
+		case UNDO_REMOVE_CONTOUR: return "remove_contour";
+		case UNDO_INSERT_CONTOUR: return "insert_contour";
+		case UNDO_ROTATE: return "rotate";
+		case UNDO_CREATE: return "create";
+		case UNDO_MOVETOLAYER: return "movetolayer";
+		case UNDO_FLAG: return "flag";
+		case UNDO_CHANGESIZE: return "changesize";
+		case UNDO_CHANGE2NDSIZE: return "change2ndsize";
+		case UNDO_MIRROR: return "mirror";
+		case UNDO_CHANGECLEARSIZE: return "chngeclearsize";
+		case UNDO_CHANGEMASKSIZE: return "changemasksize";
+		case UNDO_CHANGEANGLES: return "changeangles";
+		case UNDO_CHANGERADII: return "changeradii";
+		case UNDO_LAYERCHANGE: return "layerchange";
+		case UNDO_CLEAR: return "clear";
+		case UNDO_NETLISTCHANGE: return "netlistchange";
+		case UNDO_CHANGEPINNUM: return "changepinnum";
+	}
+	sprintf(buff, "Unknown %d", type);
+	return buff;
+}
+
+void undo_dump()
+{
+	size_t n;
+	int last_serial = -2;
+	for(n = 0; n < UndoN; n++) {
+		if (last_serial != UndoList[n].Serial) {
+			printf("--- serial=%d\n", UndoList[n].Serial);
+			last_serial = UndoList[n].Serial;
+		}
+		printf(" type=%s kind=%d ID=%d\n", undo_type2str(UndoList[n].Type), UndoList[n].Kind, UndoList[n].ID);
+	}
+}
+
+#endif
diff --git a/src/undo.h b/src/undo.h
new file mode 100644
index 0000000..d09c35a
--- /dev/null
+++ b/src/undo.h
@@ -0,0 +1,76 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for undo routines */
+
+#ifndef	PCB_UNDO_H
+#define	PCB_UNDO_H
+
+#include "global.h"
+
+#define DRAW_FLAGS	(PCB_FLAG_RAT | PCB_FLAG_SELECTED \
+			| PCB_FLAG_HIDENAME | PCB_FLAG_HOLE | PCB_FLAG_OCTAGON | PCB_FLAG_FOUND | PCB_FLAG_CLEARLINE)
+
+											/* different layers */
+
+int Undo(pcb_bool);
+int Redo(pcb_bool);
+void IncrementUndoSerialNumber(void);
+void SaveUndoSerialNumber(void);
+void RestoreUndoSerialNumber(void);
+void ClearUndoList(pcb_bool);
+void MoveObjectToRemoveUndoList(int, void *, void *, void *);
+void AddObjectToRemovePointUndoList(int, void *, void *, pcb_cardinal_t);
+void AddObjectToInsertPointUndoList(int, void *, void *, void *);
+void AddObjectToRemoveContourUndoList(int, LayerType *, PolygonType *);
+void AddObjectToInsertContourUndoList(int, LayerType *, PolygonType *);
+void AddObjectToMoveUndoList(int, void *, void *, void *, Coord, Coord);
+void AddObjectToChangeNameUndoList(int, void *, void *, void *, char *);
+void AddObjectToChangePinnumUndoList(int, void *, void *, void *, char *);
+void AddObjectToRotateUndoList(int, void *, void *, void *, Coord, Coord, pcb_uint8_t);
+void AddObjectToCreateUndoList(int, void *, void *, void *);
+void AddObjectToMirrorUndoList(int, void *, void *, void *, Coord);
+void AddObjectToMoveToLayerUndoList(int, void *, void *, void *);
+void AddObjectToFlagUndoList(int, void *, void *, void *);
+void AddObjectToSizeUndoList(int, void *, void *, void *);
+void AddObjectTo2ndSizeUndoList(int, void *, void *, void *);
+void AddObjectToClearSizeUndoList(int, void *, void *, void *);
+void AddObjectToMaskSizeUndoList(int, void *, void *, void *);
+void AddObjectToChangeAnglesUndoList(int, void *, void *, void *);
+void AddObjectToChangeRadiiUndoList(int, void *, void *, void *);
+void AddObjectToClearPolyUndoList(int, void *, void *, void *, pcb_bool);
+void AddLayerChangeToUndoList(int, int);
+void AddNetlistLibToUndoList(LibraryTypePtr);
+void LockUndo(void);
+void UnlockUndo(void);
+pcb_bool Undoing(void);
+
+/* Publish actions - these may be useful for other actions */
+int ActionUndo(int argc, const char **argv, Coord x, Coord y);
+int ActionRedo(int argc, const char **argv, Coord x, Coord y);
+int ActionAtomic(int argc, const char **argv, Coord x, Coord y);
+
+#endif
diff --git a/src/undo_act.c b/src/undo_act.c
new file mode 100644
index 0000000..8ec715c
--- /dev/null
+++ b/src/undo_act.c
@@ -0,0 +1,299 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include "data.h"
+#include "action_helper.h"
+#include "error.h"
+#include "funchash_core.h"
+
+#include "crosshair.h"
+#include "undo.h"
+#include "polygon.h"
+#include "set.h"
+#include "search.h"
+#include "draw.h"
+#include "misc.h"
+#include "layer.h"
+
+/* --------------------------------------------------------------------------- */
+
+static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
+
+static const char atomic_help[] = "Save or restore the undo serial number.";
+
+/* %start-doc actions Atomic
+
+This action allows making multiple-action bindings into an atomic
+operation that will be undone by a single Undo command.  For example,
+to optimize rat lines, you'd delete the rats and re-add them.  To
+group these into a single undo, you'd want the deletions and the
+additions to have the same undo serial number.  So, you @code{Save},
+delete the rats, @code{Restore}, add the rats - using the same serial
+number as the deletes, then @code{Block}, which checks to see if the
+deletions or additions actually did anything.  If not, the serial
+number is set to the saved number, as there's nothing to undo.  If
+something did happen, the serial number is incremented so that these
+actions are counted as a single undo step.
+
+ at table @code
+
+ at item Save
+Saves the undo serial number.
+
+ at item Restore
+Returns it to the last saved number.
+
+ at item Close
+Sets it to 1 greater than the last save.
+
+ at item Block
+Does a Restore if there was nothing to undo, else does a Close.
+
+ at end table
+
+%end-doc */
+
+int ActionAtomic(int argc, const char **argv, Coord x, Coord y)
+{
+	if (argc != 1)
+		AFAIL(atomic);
+
+	switch (funchash_get(argv[0], NULL)) {
+	case F_Save:
+		SaveUndoSerialNumber();
+		break;
+	case F_Restore:
+		RestoreUndoSerialNumber();
+		break;
+	case F_Close:
+		RestoreUndoSerialNumber();
+		IncrementUndoSerialNumber();
+		break;
+	case F_Block:
+		RestoreUndoSerialNumber();
+		if (Bumped)
+			IncrementUndoSerialNumber();
+		break;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char undo_syntax[] = "Undo()\n" "Undo(ClearList)";
+
+static const char undo_help[] = "Undo recent changes.";
+
+/* %start-doc actions Undo
+
+The unlimited undo feature of @code{Pcb} allows you to recover from
+most operations that materially affect you work.  Calling
+ at code{Undo()} without any parameter recovers from the last (non-undo)
+operation. @code{ClearList} is used to release the allocated
+memory. @code{ClearList} is called whenever a new layout is started or
+loaded. See also @code{Redo} and @code{Atomic}.
+
+Note that undo groups operations by serial number; changes with the
+same serial number will be undone (or redone) as a group.  See
+ at code{Atomic}.
+
+%end-doc */
+
+int ActionUndo(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	if (!function || !*function) {
+		/* don't allow undo in the middle of an operation */
+		if (conf_core.editor.mode != PCB_MODE_POLYGON_HOLE && Crosshair.AttachedObject.State != STATE_FIRST)
+			return 1;
+		if (Crosshair.AttachedBox.State != STATE_FIRST && conf_core.editor.mode != PCB_MODE_ARC)
+			return 1;
+		/* undo the last operation */
+
+		notify_crosshair_change(pcb_false);
+		if ((conf_core.editor.mode == PCB_MODE_POLYGON || conf_core.editor.mode == PCB_MODE_POLYGON_HOLE) && Crosshair.AttachedPolygon.PointN) {
+			GoToPreviousPoint();
+			notify_crosshair_change(pcb_true);
+			return 0;
+		}
+		/* move anchor point if undoing during line creation */
+		if (conf_core.editor.mode == PCB_MODE_LINE) {
+			if (Crosshair.AttachedLine.State == STATE_SECOND) {
+				if (conf_core.editor.auto_drc)
+					Undo(pcb_true);						/* undo the connection find */
+				Crosshair.AttachedLine.State = STATE_FIRST;
+				SetLocalRef(0, 0, pcb_false);
+				notify_crosshair_change(pcb_true);
+				return 0;
+			}
+			if (Crosshair.AttachedLine.State == STATE_THIRD) {
+				int type;
+				void *ptr1, *ptr3, *ptrtmp;
+				LineTypePtr ptr2;
+				/* this search is guaranteed to succeed */
+				SearchObjectByLocation(PCB_TYPE_LINE | PCB_TYPE_RATLINE, &ptr1,
+															 &ptrtmp, &ptr3, Crosshair.AttachedLine.Point1.X, Crosshair.AttachedLine.Point1.Y, 0);
+				ptr2 = (LineTypePtr) ptrtmp;
+
+				/* save both ends of line */
+				Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
+				Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
+				if ((type = Undo(pcb_true)))
+					SetChangedFlag(pcb_true);
+				/* check that the undo was of the right type */
+				if ((type & UNDO_CREATE) == 0) {
+					/* wrong undo type, restore anchor points */
+					Crosshair.AttachedLine.Point2.X = Crosshair.AttachedLine.Point1.X;
+					Crosshair.AttachedLine.Point2.Y = Crosshair.AttachedLine.Point1.Y;
+					notify_crosshair_change(pcb_true);
+					return 0;
+				}
+				/* move to new anchor */
+				Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
+				Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
+				/* check if an intermediate point was removed */
+				if (type & UNDO_REMOVE) {
+					/* this search should find the restored line */
+					SearchObjectByLocation(PCB_TYPE_LINE | PCB_TYPE_RATLINE, &ptr1,
+																 &ptrtmp, &ptr3, Crosshair.AttachedLine.Point2.X, Crosshair.AttachedLine.Point2.Y, 0);
+					ptr2 = (LineTypePtr) ptrtmp;
+					if (conf_core.editor.auto_drc) {
+						/* undo loses PCB_FLAG_FOUND */
+						SET_FLAG(PCB_FLAG_FOUND, ptr2);
+						DrawLine(CURRENT, ptr2);
+					}
+					Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
+					Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
+				}
+				FitCrosshairIntoGrid(Crosshair.X, Crosshair.Y);
+				AdjustAttachedObjects();
+				if (--addedLines == 0) {
+					Crosshair.AttachedLine.State = STATE_SECOND;
+					lastLayer = CURRENT;
+				}
+				else {
+					/* this search is guaranteed to succeed too */
+					SearchObjectByLocation(PCB_TYPE_LINE | PCB_TYPE_RATLINE, &ptr1,
+																 &ptrtmp, &ptr3, Crosshair.AttachedLine.Point1.X, Crosshair.AttachedLine.Point1.Y, 0);
+					ptr2 = (LineTypePtr) ptrtmp;
+					lastLayer = (LayerTypePtr) ptr1;
+				}
+				notify_crosshair_change(pcb_true);
+				return 0;
+			}
+		}
+		if (conf_core.editor.mode == PCB_MODE_ARC) {
+			if (Crosshair.AttachedBox.State == STATE_SECOND) {
+				Crosshair.AttachedBox.State = STATE_FIRST;
+				notify_crosshair_change(pcb_true);
+				return 0;
+			}
+			if (Crosshair.AttachedBox.State == STATE_THIRD) {
+				void *ptr1, *ptr2, *ptr3;
+				BoxTypePtr bx;
+				/* guaranteed to succeed */
+				SearchObjectByLocation(PCB_TYPE_ARC, &ptr1, &ptr2, &ptr3,
+															 Crosshair.AttachedBox.Point1.X, Crosshair.AttachedBox.Point1.Y, 0);
+				bx = GetArcEnds((ArcTypePtr) ptr2);
+				Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = bx->X1;
+				Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = bx->Y1;
+				AdjustAttachedObjects();
+				if (--addedLines == 0)
+					Crosshair.AttachedBox.State = STATE_SECOND;
+			}
+		}
+		/* undo the last destructive operation */
+		if (Undo(pcb_true))
+			SetChangedFlag(pcb_true);
+	}
+	else if (function) {
+		switch (funchash_get(function, NULL)) {
+			/* clear 'undo objects' list */
+		case F_ClearList:
+			ClearUndoList(pcb_false);
+			break;
+		}
+	}
+	notify_crosshair_change(pcb_true);
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char redo_syntax[] = "Redo()";
+
+static const char redo_help[] = "Redo recent \"undo\" operations.";
+
+/* %start-doc actions Redo
+
+This routine allows you to recover from the last undo command.  You
+might want to do this if you thought that undo was going to revert
+something other than what it actually did (in case you are confused
+about which operations are un-doable), or if you have been backing up
+through a long undo list and over-shoot your stopping point.  Any
+change that is made since the undo in question will trim the redo
+list.  For example if you add ten lines, then undo three of them you
+could use redo to put them back, but if you move a line on the board
+before performing the redo, you will lose the ability to "redo" the
+three "undone" lines.
+
+%end-doc */
+
+int ActionRedo(int argc, const char **argv, Coord x, Coord y)
+{
+	if (((conf_core.editor.mode == PCB_MODE_POLYGON ||
+				conf_core.editor.mode == PCB_MODE_POLYGON_HOLE) && Crosshair.AttachedPolygon.PointN) || Crosshair.AttachedLine.State == STATE_SECOND)
+		return 1;
+	notify_crosshair_change(pcb_false);
+	if (Redo(pcb_true)) {
+		SetChangedFlag(pcb_true);
+		if (conf_core.editor.mode == PCB_MODE_LINE && Crosshair.AttachedLine.State != STATE_FIRST) {
+			LineType *line = linelist_last(&CURRENT->Line);
+			Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = line->Point2.X;
+			Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
+			addedLines++;
+		}
+	}
+	notify_crosshair_change(pcb_true);
+	return 0;
+}
+
+
+HID_Action undo_action_list[] = {
+	{"Atomic", 0, ActionAtomic,
+	 atomic_help, atomic_syntax}
+	,
+	{"Undo", 0, ActionUndo,
+	 undo_help, undo_syntax}
+	,
+	{"Redo", 0, ActionRedo,
+	 redo_help, redo_syntax}
+};
+
+REGISTER_ACTIONS(undo_action_list, NULL)
diff --git a/src/unit.c b/src/unit.c
new file mode 100644
index 0000000..563ef77
--- /dev/null
+++ b/src/unit.c
@@ -0,0 +1,281 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2011 Andrew Poelstra
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
+ *  asp11 at sfu.ca
+ *
+ */
+#include "config.h"
+#include <stdlib.h>
+#include <locale.h>
+#include "global.h"
+#include "compat_misc.h"
+#include "unit.h"
+
+/* Helper macros for tables */
+#define PCB_MM_TO_COORD3(a,b,c)		PCB_MM_TO_COORD (a), PCB_MM_TO_COORD (b), PCB_MM_TO_COORD (c)
+#define PCB_MIL_TO_COORD3(a,b,c)		PCB_MIL_TO_COORD (a), PCB_MIL_TO_COORD (b), PCB_MIL_TO_COORD (c)
+#define PCB_MM_TO_COORD5(a,b,c,d,e)		PCB_MM_TO_COORD (a), PCB_MM_TO_COORD (b), PCB_MM_TO_COORD (c),	\
+                              		PCB_MM_TO_COORD (d), PCB_MM_TO_COORD (e)
+#define PCB_MIL_TO_COORD5(a,b,c,d,e)	PCB_MIL_TO_COORD (a), PCB_MIL_TO_COORD (b), PCB_MIL_TO_COORD (c),	\
+                               		PCB_MIL_TO_COORD (d), PCB_MIL_TO_COORD (e)
+
+/* These should be kept in order of smallest scale_factor
+ * to largest -- the code uses this ordering when finding
+ * the best scale to use for a group of measures */
+Unit Units[] = {
+	{0, "km", NULL, 'k', 0.000001, METRIC, ALLOW_KM, 5,
+	 0.00005, 0.0005, 0.0025, 0.05, 0.25,
+	 {""}},
+	{0, "m", NULL, 'f', 0.001, METRIC, ALLOW_M, 5,
+	 0.0005, 0.005, 0.025, 0.5, 2.5,
+	 {""}},
+	{0, "cm", NULL, 'e', 0.1, METRIC, ALLOW_CM, 5,
+	 0.005, 0.05, 0.25, 5, 25,
+	 {""}},
+	{0, "mm", NULL, 'm', 1, METRIC, ALLOW_MM, 4,
+	 0.005, 0.05, 0.25, 5, 25,
+	 {""}},
+	{0, "um", NULL, 'u', 1000, METRIC, ALLOW_UM, 2,
+	 0.005, 0.05, 0.25, 5, 25,
+	 {""}},
+	{0, "nm", NULL, 'n', 1000000, METRIC, ALLOW_NM, 0,
+	 5, 50, 2500, 5000, 25000,
+	 {""}},
+
+	{0, "in", NULL, 'i', 0.001, IMPERIAL, ALLOW_IN, 5,
+	 0.1, 1.0, 5.0, 25, 100,
+	 {"inch"}},
+	{0, "mil", NULL, 'l', 1, IMPERIAL, ALLOW_MIL, 2,
+	 0.1, 1.0, 10, 100, 1000,
+	 {""}},
+	{0, "dmil", NULL, 'k', 10, IMPERIAL, ALLOW_DMIL, 1,	/* kicad legacy decimil unit */
+	 0.1, 1.0, 10, 100, 1000,				/* wild guess at factors */
+	 {""}},
+	{0, "cmil", NULL, 'c', 100, IMPERIAL, ALLOW_CMIL, 0,
+	 1, 10, 100, 1000, 10000,
+	 {"pcb"}}
+};
+
+#define N_UNITS ((int) (sizeof Units / sizeof Units[0]))
+/* \brief Initialize non-static data for pcb-printf
+ * \par Function Description
+ * Assigns each unit its index for quick access through the
+ * main units array, and internationalize the units for GUI
+ * display.
+ */
+void initialize_units()
+{
+	int i;
+	for (i = 0; i < N_UNITS; ++i) {
+		Units[i].index = i;
+		Units[i].in_suffix = _(Units[i].suffix);
+	}
+}
+
+/* This list -must- contain all printable units from the above list */
+/* For now I have just copy/pasted the same values for all metric
+ * units and the same values for all imperial ones */
+Increments increments[] = {
+	/* TABLE FORMAT   |  default  |  min  |  max
+	 *          grid  |           |       |
+	 *          size  |           |       |
+	 *          line  |           |       |
+	 *         clear  |           |       |
+	 */
+	{"km", PCB_MM_TO_COORD3(0.1, 0.01, 1.0),
+	 PCB_MM_TO_COORD3(0.2, 0.01, 0.5),
+	 PCB_MM_TO_COORD3(0.1, 0.005, 0.5),
+	 PCB_MM_TO_COORD3(0.05, 0.005, 0.5)},
+	{"m", PCB_MM_TO_COORD3(0.1, 0.01, 1.0),
+	 PCB_MM_TO_COORD3(0.2, 0.01, 0.5),
+	 PCB_MM_TO_COORD3(0.1, 0.005, 0.5),
+	 PCB_MM_TO_COORD3(0.05, 0.005, 0.5)},
+	{"cm", PCB_MM_TO_COORD3(0.1, 0.01, 1.0),
+	 PCB_MM_TO_COORD3(0.2, 0.01, 0.5),
+	 PCB_MM_TO_COORD3(0.1, 0.005, 0.5),
+	 PCB_MM_TO_COORD3(0.05, 0.005, 0.5)},
+	{"mm", PCB_MM_TO_COORD3(0.1, 0.01, 1.0),
+	 PCB_MM_TO_COORD3(0.2, 0.01, 0.5),
+	 PCB_MM_TO_COORD3(0.1, 0.005, 0.5),
+	 PCB_MM_TO_COORD3(0.05, 0.005, 0.5)},
+	{"um", PCB_MM_TO_COORD3(0.1, 0.01, 1.0),
+	 PCB_MM_TO_COORD3(0.2, 0.01, 0.5),
+	 PCB_MM_TO_COORD3(0.1, 0.005, 0.5),
+	 PCB_MM_TO_COORD3(0.05, 0.005, 0.5)},
+	{"nm", PCB_MM_TO_COORD3(0.1, 0.01, 1.0),
+	 PCB_MM_TO_COORD3(0.2, 0.01, 0.5),
+	 PCB_MM_TO_COORD3(0.1, 0.005, 0.5),
+	 PCB_MM_TO_COORD3(0.05, 0.005, 0.5)},
+
+	{"cmil", PCB_MIL_TO_COORD3(5, 1, 25),
+	 PCB_MIL_TO_COORD3(10, 1, 10),
+	 PCB_MIL_TO_COORD3(5, 0.5, 10),
+	 PCB_MIL_TO_COORD3(2, 0.5, 10)},
+	{"dmil", PCB_MIL_TO_COORD3(5, 1, 25), /* kicad legacy decimil unit */
+	 PCB_MIL_TO_COORD3(10, 1, 10),
+	 PCB_MIL_TO_COORD3(5, 0.5, 10),
+	 PCB_MIL_TO_COORD3(2, 0.5, 10)},
+	{"mil", PCB_MIL_TO_COORD3(5, 1, 25),
+	 PCB_MIL_TO_COORD3(10, 1, 10),
+	 PCB_MIL_TO_COORD3(5, 0.5, 10),
+	 PCB_MIL_TO_COORD3(2, 0.5, 10)},
+	{"in", PCB_MIL_TO_COORD3(5, 1, 25),
+	 PCB_MIL_TO_COORD3(10, 1, 10),
+	 PCB_MIL_TO_COORD3(5, 0.5, 10),
+	 PCB_MIL_TO_COORD3(2, 0.5, 10)},
+};
+
+#define N_INCREMENTS (sizeof increments / sizeof increments[0])
+
+/* \brief Obtain a unit object from its suffix
+ * \par Function Description
+ * Looks up a given suffix in the main units array. Internationalized
+ * unit suffixes are not supported, though pluralized units are, for
+ * backward-compatibility.
+ *
+ * \param [in] const_suffix   The suffix to look up
+ *
+ * \return A const pointer to the Unit struct, or NULL if none was found
+ */
+const Unit *get_unit_struct(const char *suffix)
+{
+	int i;
+	int s_len = 0;
+
+	/* Determine bounds */
+	while (isspace(*suffix))
+		suffix++;
+	while (isalnum(suffix[s_len]))
+		s_len++;
+
+	/* Also understand plural suffixes: "inches", "mils" */
+	if (s_len > 2) {
+		if (suffix[s_len - 2] == 'e' && suffix[s_len - 1] == 's')
+			s_len -= 2;
+		else if (suffix[s_len - 1] == 's')
+			s_len -= 1;
+	}
+
+	/* Do lookup */
+	if (s_len > 0)
+		for (i = 0; i < N_UNITS; ++i)
+			if (strncmp(suffix, Units[i].suffix, s_len) == 0 || strncmp(suffix, Units[i].alias[0], s_len) == 0)
+				return &Units[i];
+
+	return NULL;
+}
+
+const Unit *get_unit_struct_by_allow(enum e_allow allow)
+{
+	int i;
+	for (i = 0; i < N_UNITS; ++i)
+		if (Units[i].allow == allow)
+			return &Units[i];
+
+	return NULL;
+}
+
+/* ACCESSORS */
+/* \brief Returns the master unit list. This may not be modified. */
+const Unit *get_unit_list(void)
+{
+	return Units;
+}
+
+/* \brief Returns the unit by its index */
+const Unit *get_unit_by_idx(int idx)
+{
+	if ((idx < 0) || (idx >= N_UNITS))
+		return NULL;
+	return Units + idx;
+}
+
+/* \brief Returns the length of the master unit list. */
+int get_n_units(void)
+{
+	return N_UNITS;
+}
+
+/* \brief Convert a pcb coord to the given unit
+ *
+ * \param [in] unit  The unit to convert to
+ * \param [in] x     The quantity to convert
+ *
+ * \return The converted measure
+ */
+double coord_to_unit(const Unit * unit, Coord x)
+{
+	double base;
+	if (unit == NULL)
+		return -1;
+	base = unit->family == METRIC ? PCB_COORD_TO_MM(1)
+		: PCB_COORD_TO_MIL(1);
+	return x * unit->scale_factor * base;
+}
+
+/* \brief Convert a given unit to pcb coords
+ *
+ * \param [in] unit  The unit to convert from
+ * \param [in] x     The quantity to convert
+ *
+ * \return The converted measure
+ */
+Coord unit_to_coord(const Unit * unit, double x)
+{
+	double base;
+	if (unit == NULL)
+		return -1;
+	base = unit->family == METRIC ? PCB_MM_TO_COORD(x)
+		: PCB_MIL_TO_COORD(x);
+	return pcb_round(base/unit->scale_factor);
+}
+
+/* \brief Return how many PCB-internal-Coord-unit a unit translates to
+ *
+ * \param [in] unit  The unit to convert
+ *
+ * \return The converted measure
+ */
+double unit_to_factor(const Unit * unit)
+{
+	return 1.0 / coord_to_unit(unit, 1);
+}
+
+/* \brief Obtain an increment object from its suffix
+ * \par Function Description
+ * Looks up a given suffix in the main increments array. Internationalized
+ * unit suffixes are not supported, nor are pluralized units.
+ *
+ * \param [in] suffix   The suffix to look up
+ *
+ * \return A const pointer to the Increments struct, or NULL if none was found
+ */
+Increments *get_increments_struct(const char *suffix)
+{
+	int i;
+	/* Do lookup */
+	for (i = 0; i < N_INCREMENTS; ++i)
+		if (strcmp(suffix, increments[i].suffix) == 0)
+			return &increments[i];
+	return NULL;
+}
diff --git a/src/unit.h b/src/unit.h
new file mode 100644
index 0000000..e79ee50
--- /dev/null
+++ b/src/unit.h
@@ -0,0 +1,124 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2011 Andrew Poelstra
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
+ *  asp11 at sfu.ca
+ *
+ */
+
+#ifndef	PCB_UNIT_H
+#define	PCB_UNIT_H
+
+#include "config.h"
+
+/* typedef ... Coord;				pcb base unit, typedef'd in config.h */
+typedef double Angle;						/* degrees */
+
+enum e_allow {
+	NO_PRINT = 0,									/* suffixes we can read but not print (i.e., "inch") */
+	ALLOW_NM = 1,
+	ALLOW_UM = 2,
+	ALLOW_MM = 4,
+	ALLOW_CM = 8,
+	ALLOW_M = 16,
+	ALLOW_KM = 32,
+
+	ALLOW_CMIL = 1024,
+	ALLOW_MIL = 2048,
+	ALLOW_IN = 4096,
+
+	ALLOW_DMIL = 8192, /* for kicad legacy decimil units */
+
+	ALLOW_METRIC = ALLOW_NM | ALLOW_UM | ALLOW_MM | ALLOW_CM | ALLOW_M | ALLOW_KM,
+	ALLOW_IMPERIAL = ALLOW_DMIL | ALLOW_CMIL | ALLOW_MIL | ALLOW_IN,
+	/* This is all units allowed in parse_l.l */
+#if 0
+	ALLOW_READABLE = ALLOW_NM | ALLOW_UM | ALLOW_MM | ALLOW_M | ALLOW_KM | ALLOW_CMIL | ALLOW_MIL | ALLOW_IN,
+#else
+	ALLOW_READABLE = ALLOW_CMIL,
+#endif
+
+	/* Used for pcb-printf %mS - should not include unusual units like km, cmil and dmil */
+	ALLOW_NATURAL = ALLOW_NM | ALLOW_UM | ALLOW_MM | ALLOW_M | ALLOW_MIL | ALLOW_IN,
+
+	ALLOW_ALL = ~0
+};
+
+enum e_family { METRIC, IMPERIAL };
+enum e_suffix { NO_SUFFIX, SUFFIX, FILE_MODE };
+
+struct unit {
+	int index;										/* Index into Unit[] list */
+	const char *suffix;
+	const char *in_suffix;				/* internationalized suffix */
+	char printf_code;
+	double scale_factor;
+	enum e_family family;
+	enum e_allow allow;
+	int default_prec;
+	/* used for gui spinboxes */
+	double step_tiny;
+	double step_small;
+	double step_medium;
+	double step_large;
+	double step_huge;
+	/* aliases -- right now we only need 1 ("inch"->"in"), add as needed */
+	const char *alias[1];
+};
+
+struct increments {
+	const char *suffix;
+	/* key g and <shift>g value  */
+	Coord grid;
+	Coord grid_min;
+	Coord grid_max;
+	/* key s and <shift>s value  */
+	Coord size;
+	Coord size_min;
+	Coord size_max;
+	/* key l and <shift>l value  */
+	Coord line;
+	Coord line_min;
+	Coord line_max;
+	/* key k and <shift>k value  */
+	Coord clear;
+	Coord clear_min;
+	Coord clear_max;
+};
+
+typedef struct unit Unit;
+typedef struct increments Increments;
+extern Unit Units[];
+extern Increments increments[];
+
+const Unit *get_unit_struct(const char *suffix);
+const Unit *get_unit_struct_by_allow(enum e_allow allow);
+const Unit *get_unit_list(void);
+const Unit *get_unit_by_idx(int idx);
+int get_n_units(void);
+double coord_to_unit(const Unit *, Coord);
+double unit_to_factor(const Unit * unit);
+Coord unit_to_coord(const Unit *, double);
+Increments *get_increments_struct(const char *suffix);
+
+
+#endif
diff --git a/src/vtlibrary.c b/src/vtlibrary.c
new file mode 100644
index 0000000..675a414
--- /dev/null
+++ b/src/vtlibrary.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vtlibrary.h"
+#include <genvector/genvector_impl.c>
diff --git a/src/vtlibrary.h b/src/vtlibrary.h
new file mode 100644
index 0000000..8e6ea71
--- /dev/null
+++ b/src/vtlibrary.h
@@ -0,0 +1,74 @@
+#include <stdlib.h>
+#include <string.h>
+#include "global_objs.h"
+
+typedef enum {
+	LIB_INVALID,
+	LIB_DIR,
+	LIB_FOOTPRINT
+} library_type_t;
+
+typedef enum {
+	PCB_FP_INVALID,
+	PCB_FP_DIR,       /* used temporarily during the mapping - a finalized tree wouldn't have this */
+	PCB_FP_FILE,
+	PCB_FP_PARAMETRIC
+} fp_type_t;
+
+
+typedef struct library_s library_t;
+
+/* Elem=library_t; init=none */
+
+/* all public symbols are wrapped in GVT() - see vt_t(7) */
+#define GVT(x) vtlib_ ## x
+
+/* Array elem type - see vt_t(7) */
+#define GVT_ELEM_TYPE library_t
+
+/* Type that represents array lengths - see vt_t(7) */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the array grows */
+#define GVT_DOUBLING_THRS 64
+
+/* Initial array size when the first element is written */
+#define GVT_START_SIZE 8
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+/* Enable this to set all new bytes ever allocated to this value - see
+   vt_set_new_bytes_to(7) */
+#define GVT_SET_NEW_BYTES_TO 0
+
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator - see vt_allocation(7) */
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
+
+/* An element of a library: either a directory or a footprint */
+struct library_s {
+	char *name;            /* visible name */
+	library_type_t type;
+	library_t *parent;
+
+	union {
+		struct { /* type == LIB_DIR */
+			vtlib_t children;
+		} dir;
+		struct { /* type == LIB_FOOTPRINT */
+			char *loc_info;
+			void *backend_data;
+			fp_type_t type;
+			void **tags;        /* an array of void * tag IDs; last tag ID is NULL */
+		} fp;
+	} data;
+} ;
+
diff --git a/src/vtonpoint.c b/src/vtonpoint.c
new file mode 100644
index 0000000..fdff202
--- /dev/null
+++ b/src/vtonpoint.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vtonpoint.h"
+#include <genvector/genvector_impl.c>
diff --git a/src/vtonpoint.h b/src/vtonpoint.h
new file mode 100644
index 0000000..db9d352
--- /dev/null
+++ b/src/vtonpoint.h
@@ -0,0 +1,64 @@
+#include <stdlib.h>
+#include <string.h>
+#include "global_objs.h"
+
+/* Elem=OnpointType; init=none */
+
+/* all public symbols are wrapped in GVT() - see vt_t(7) */
+#define GVT(x) vtop_ ## x
+
+/* Array elem type - see vt_t(7) */
+#define GVT_ELEM_TYPE OnpointType
+
+/* Type that represents array lengths - see vt_t(7) */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the array grows */
+#define GVT_DOUBLING_THRS 64
+
+/* Initial array size when the first element is written */
+#define GVT_START_SIZE 8
+
+/* Optional terminator; when present, it is always appended at the end - see
+   vt_term(7)*/
+/* #define GVT_TERM '\0' */
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+/* Enable this to set all new bytes ever allocated to this value - see
+   vt_set_new_bytes_to(7) */
+/* #define GVT_SET_NEW_BYTES_TO 0 */
+
+/* Enable GVT_INIT_ELEM_FUNC and an user configured function is called
+   for each new element allocated (even between used and alloced).
+   See vt_init_elem(7) */
+/*#define GVT_INIT_ELEM_FUNC*/
+
+/* Enable GVT_ELEM_CONSTRUCTOR and an user configured function is called
+   for each element that is getting within the range of ->used.
+   See vt_construction(7) */
+/*#define GVT_ELEM_CONSTRUCTOR */
+
+/* Enable GVT_ELEM_DESTRUCTOR and an user configured function is called
+   for each element that was once constructed and now getting beyond ->used.
+   See vt_construction(7) */
+/*#define GVT_ELEM_DESTRUCTOR */
+
+/* Enable GVT_ELEM_COPY and an user configured function is called
+   for copying elements into the array.
+   See vt_construction(7) */
+/*#define GVT_ELEM_COPY */
+
+/* Optional extra fields in the vector struct - see vt_user_fields(7) */
+/* #define GVT_USER_FIELDS int foo; char bar[12]; */
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator - see vt_allocation(7) */
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
diff --git a/src/vtptr.c b/src/vtptr.c
new file mode 100644
index 0000000..8cbcee9
--- /dev/null
+++ b/src/vtptr.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vtptr.h"
+#include <genvector/genvector_impl.c>
diff --git a/src/vtptr.h b/src/vtptr.h
new file mode 100644
index 0000000..a940a8b
--- /dev/null
+++ b/src/vtptr.h
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+#include <string.h>
+#include "global_objs.h"
+
+typedef void *ptr_t;
+
+/* Elem=void *; init=none */
+
+/* all public symbols are wrapped in GVT() - see vt_t(7) */
+#define GVT(x) vtptr_ ## x
+
+/* Array elem type - see vt_t(7) */
+#define GVT_ELEM_TYPE ptr_t
+
+/* Type that represents array lengths - see vt_t(7) */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the array grows */
+#define GVT_DOUBLING_THRS 256
+
+/* Initial array size when the first element is written */
+#define GVT_START_SIZE 32
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator - see vt_allocation(7) */
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
diff --git a/src/vtroutestyle.c b/src/vtroutestyle.c
new file mode 100644
index 0000000..5705dbd
--- /dev/null
+++ b/src/vtroutestyle.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vtroutestyle.h"
+#include <genvector/genvector_impl.c>
diff --git a/src/vtroutestyle.h b/src/vtroutestyle.h
new file mode 100644
index 0000000..0dcaeb6
--- /dev/null
+++ b/src/vtroutestyle.h
@@ -0,0 +1,74 @@
+#include <stdlib.h>
+#include <string.h>
+#include "unit.h"
+
+/* Elem=RouteStyle; init=0 */
+
+typedef struct {
+	Coord Thick,       /* line thickness */
+	  Diameter,        /* via diameter */
+	  Hole,            /* via drill hole */
+	  Clearance;       /* min. separation from other nets */
+	char name[32];     /* fixed length name to save malloc/free */
+/*	int index;*/
+} RouteStyleType, *RouteStyleTypePtr;
+
+/* all public symbols are wrapped in GVT() - see vt_t(7) */
+#define GVT(x) vtroutestyle_ ## x
+#define HAVE_VTROUTESTYLE_T
+
+/* Array elem type - see vt_t(7) */
+#define GVT_ELEM_TYPE RouteStyleType
+
+/* Type that represents array lengths - see vt_t(7) */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the array grows */
+#define GVT_DOUBLING_THRS 16
+
+/* Initial array size when the first element is written */
+#define GVT_START_SIZE 4
+
+/* Optional terminator; when present, it is always appended at the end - see
+   vt_term(7)*/
+/* #define GVT_TERM '\0' */
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+/* Enable this to set all new bytes ever allocated to this value - see
+   vt_set_new_bytes_to(7) */
+#define GVT_SET_NEW_BYTES_TO 0
+
+/* Enable GVT_INIT_ELEM_FUNC and an user configured function is called
+   for each new element allocated (even between used and alloced).
+   See vt_init_elem(7) */
+/*#define GVT_INIT_ELEM_FUNC*/
+
+/* Enable GVT_ELEM_CONSTRUCTOR and an user configured function is called
+   for each element that is getting within the range of ->used.
+   See vt_construction(7) */
+/*#define GVT_ELEM_CONSTRUCTOR */
+
+/* Enable GVT_ELEM_DESTRUCTOR and an user configured function is called
+   for each element that was once constructed and now getting beyond ->used.
+   See vt_construction(7) */
+/*#define GVT_ELEM_DESTRUCTOR */
+
+/* Enable GVT_ELEM_COPY and an user configured function is called
+   for copying elements into the array.
+   See vt_construction(7) */
+/*#define GVT_ELEM_COPY */
+
+/* Optional extra fields in the vector struct - see vt_user_fields(7) */
+/* #define GVT_USER_FIELDS int foo; char bar[12]; */
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator - see vt_allocation(7) */
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
diff --git a/src_3rd/Makefile.conf b/src_3rd/Makefile.conf
new file mode 100644
index 0000000..4773e2e
--- /dev/null
+++ b/src_3rd/Makefile.conf
@@ -0,0 +1 @@
+# placeholder for genvector
diff --git a/src_3rd/README b/src_3rd/README
new file mode 100644
index 0000000..5327d15
--- /dev/null
+++ b/src_3rd/README
@@ -0,0 +1 @@
+3rd party software libs
diff --git a/src_3rd/genlist/Makefile b/src_3rd/genlist/Makefile
new file mode 100644
index 0000000..920e006
--- /dev/null
+++ b/src_3rd/genlist/Makefile
@@ -0,0 +1,17 @@
+CFLAGS = -I. -I..
+#CFLAGS = -Wall -g -I. -I..
+
+OBJS = genlistalloc.o genadlist.o
+
+all: genlist.a
+
+genlist.a: $(OBJS)
+	ar r genlist.a $(OBJS)
+
+example_ad: example_ad.o $(OBJS)
+
+example_d: example_d.c gendlist.h
+
+clean:
+	rm $(OBJS) ; true
+
diff --git a/src_3rd/genlist/example_ad.c b/src_3rd/genlist/example_ad.c
new file mode 100644
index 0000000..bf2d38f
--- /dev/null
+++ b/src_3rd/genlist/example_ad.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <string.h>
+#include "genadlist.h"
+#include "../regression/common_gadl.h"
+
+int n;
+item_t *i, *a, *b;
+gdl_iterator_t it;
+
+int main()
+{
+	list1 = gadl_list_init(NULL, sizeof(item_t), NULL, NULL);
+	init_fill(list1, 5);
+	dump_list(list1, "INIT");
+
+	return 0;
+}
diff --git a/src_3rd/genlist/example_d.c b/src_3rd/genlist/example_d.c
new file mode 100644
index 0000000..19ad8b3
--- /dev/null
+++ b/src_3rd/genlist/example_d.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <string.h>
+#include "gendlist.h"
+#include "../regression/common_gdl.h"
+
+int n;
+item_t *i, *a, *b;
+gdl_iterator_t it;
+
+int main()
+{
+	init_fill(&list1, 5);
+	dump_list1("INIT");
+
+	return 0;
+}
diff --git a/src_3rd/genlist/genadlist.c b/src_3rd/genlist/genadlist.c
new file mode 100644
index 0000000..d325aed
--- /dev/null
+++ b/src_3rd/genlist/genadlist.c
@@ -0,0 +1,335 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: genlist {at} igor2.repo.hu
+Project page: http://repo.hu/projects/genlist
+*/
+
+#include <string.h>
+#include <assert.h>
+#include <genlist/genadlist.h>
+
+gadl_list_t *gadl_list_init(gadl_list_t *list, int elem_size, const gla_t *allocator, void *alloc_ctx)
+{
+	if (allocator == NULL)
+		allocator = &gla_malloc;
+
+	if (list == NULL) {
+		list = allocator->alloc(alloc_ctx, sizeof(gadl_list_t));
+		list->dyn_alloced = 1;
+	}
+	else
+		list->dyn_alloced = 0;
+
+	list->allocator = *allocator;
+	list->alloc_ctx = alloc_ctx;
+	list->elem_size = elem_size;
+
+	memset(&(list->dlist), 0, sizeof(list->dlist));
+	return list;
+}
+
+/* an elem may have two relation to lists:
+ - (linked) member if a list:
+     e.parent=list->dlist and e.next or e.prev set
+ - weak conn to a list: allocated for a list or was last member of a list, but
+   currently is not member of any list:
+     e.parent=list->dlist and e.next=e.prev set=NULL
+ Elements remember their last list connection. Such a weak conn can not
+ be broken. Weak conn should not be dereferenced, tho: the list may have
+ been free()'d meanwhile.
+*/
+
+#define elem_on_list(elem) (elem->linked)
+
+#define detach(list, elem) \
+do { \
+	elem->linked = 0; \
+	elem->link.allocator = list->allocator; \
+} while(0)
+
+#define preattach(elem) \
+do { \
+	elem->linked = 1; \
+	elem->link.e.parent = elem->link.e.prev = elem->link.e.next = NULL; \
+} while(0)
+
+static gadl_elem_t *gadl_new_elem(gadl_list_t *list)
+{
+	gadl_elem_t *elem;
+	elem = list->allocator.alloc(list->alloc_ctx, sizeof(gadl_elem_t) + list->elem_size - 1);
+	if (elem == NULL)
+		return NULL;
+	elem->link.e.prev = elem->link.e.next = NULL;
+	detach(list, elem);
+	elem->size = list->elem_size;
+	return elem;
+}
+
+void *gadl_new(gadl_list_t *list)
+{
+	gadl_elem_t *elem = gadl_new_elem(list);
+	return &elem->data;
+}
+
+void *gadl_new0(gadl_list_t *list)
+{
+	gadl_elem_t *elem = gadl_new_elem(list);
+	if (elem != NULL)
+		memset(elem->data, 0, list->elem_size);
+	return &elem->data;
+}
+
+static gadl_elem_t tmp_elem;
+static gadl_list_t tmp_list;
+#define elem_of(item)      (gadl_elem_t *)(((char *)(item)) - ((char *)&(tmp_elem.data) - (char *)&(tmp_elem)))
+#define list_of(elem)      (gadl_list_t *)(((char *)(elem->link.e.parent)) - ((char *)&(tmp_list.dlist) - (char *)&(tmp_list)))
+
+void *gadl_next_nth(void *item, int n)
+{
+	gadl_elem_t *elem = elem_of(item);
+	gadl_elem_t *res;
+
+	gdl_next_nth(elem->link.e.parent, elem, n, &res);
+	if (res == NULL)
+		return NULL;
+	return &res->data;
+}
+
+void *gadl_prev_nth(void *item, int n)
+{
+	gadl_elem_t *elem = elem_of(item);
+	gadl_elem_t *res;
+
+	gdl_prev_nth(elem->link.e.parent, elem, n, &res);
+	if (res == NULL)
+		return NULL;
+	return &res->data;
+}
+
+void *gadl_nth(gadl_list_t *list, int n)
+{
+	gadl_elem_t *res;
+	gdl_nth(&(list->dlist), n, &res);
+	if (res == NULL)
+		return NULL;
+	return &(res->data);
+}
+
+int gadl_index(void *item)
+{
+	gadl_elem_t *elem = elem_of(item);
+	gadl_list_t *list = list_of(elem);
+	int res;
+
+	if (!elem_on_list(elem))
+		return -1;
+
+	gdl_index(&(list->dlist), elem, link.e, &res);
+	return res;
+}
+
+static void gadl_remove_elem(gadl_elem_t *elem, int destroy, gadl_destruct_t dfunc)
+{
+	gadl_list_t *orig_list = list_of(elem);
+
+	if ((destroy) && (dfunc != NULL))
+		dfunc(&elem->data);
+
+	if (elem_on_list(elem)) {
+		gdl_remove(&(orig_list->dlist), elem, link.e);
+	}
+
+	if (destroy)
+		orig_list->allocator.free(orig_list->alloc_ctx, elem);
+	else
+		detach(orig_list, elem);
+}
+
+void gadl_free(void *item)
+{
+	gadl_elem_t *elem = elem_of(item);
+
+	gadl_remove_elem(elem, 1, NULL);
+}
+
+void gadl_destroy(void *item, gadl_destruct_t dfunc)
+{
+	gadl_elem_t *elem = elem_of(item);
+
+	gadl_remove_elem(elem, 1, dfunc);
+}
+
+void gadl_remove(void *item)
+{
+	gadl_elem_t *elem = elem_of(item);
+
+	gadl_remove_elem(elem, 0, NULL);
+}
+
+void gadl_append(gadl_list_t *list, void *item)
+{
+	gadl_elem_t *elem = elem_of(item);
+
+	gadl_remove_elem(elem, 0, NULL);
+
+	preattach(elem);
+	gdl_append(&(list->dlist), elem, link.e);
+}
+
+void gadl_insert(gadl_list_t *list, void *item)
+{
+	gadl_elem_t *elem = elem_of(item);
+
+	gadl_remove_elem(elem, 0, NULL);
+
+	preattach(elem);
+	gdl_insert(&(list->dlist), elem, link.e);
+}
+
+
+void gadl_insert_before(void *at_item, void *item)
+{
+	gadl_elem_t *at_elem = elem_of(at_item);
+	gadl_elem_t *elem    = elem_of(item);
+	gadl_list_t *list    = list_of(at_elem);
+
+	assert(elem_on_list(at_elem));
+
+	gadl_remove_elem(elem, 0, NULL);
+
+	preattach(elem);
+	gdl_insert_before(&(list->dlist), at_elem, elem, link.e);
+}
+
+void gadl_insert_after(void *at_item, void *item)
+{
+	gadl_elem_t *at_elem = elem_of(at_item);
+	gadl_elem_t *elem    = elem_of(item);
+	gadl_list_t *list    = list_of(at_elem);
+
+	assert(elem_on_list(at_elem));
+
+	gadl_remove_elem(elem, 0, NULL);
+
+	preattach(elem);
+	gdl_insert_after(&(list->dlist), at_elem, elem, link.e);
+}
+
+void gadl_swap(void *itema, void *itemb)
+{
+	gadl_elem_t *elema = elem_of(itema);
+	gadl_elem_t *elemb = elem_of(itemb);
+	gadl_list_t *lista = list_of(elema);
+	gadl_list_t *listb = list_of(elemb);
+
+	assert(elem_on_list(elema));
+	assert(elem_on_list(elemb));
+	assert(lista == listb);
+
+	gdl_swap(&(lista->dlist), elema, elemb, link.e);
+}
+
+void gadl_reverse(gadl_list_t *list)
+{
+	gdl_reverse(&(list->dlist));
+}
+
+void *gadl_find(gadl_list_t *list, void *data, gadl_cmp_t cmpfunc)
+{
+	gadl_elem_t *i;
+	for(i = list->dlist.first; i != NULL; i = gdl_next(&(list->dlist), i))
+		if (cmpfunc(i->data, data) == 0)
+			return &(i->data);
+	return NULL;
+}
+
+void *gadl_find_next(void *item, void *data, gadl_cmp_t cmpfunc)
+{
+	gadl_elem_t *i = elem_of(item);
+	gadl_list_t *list = list_of(i);
+
+	for(i = gdl_next(&(list->dlist), i); i != NULL; i = gdl_next(&(list->dlist), i))
+		if (cmpfunc(i->data, data) == 0)
+			return &(i->data);
+	return NULL;
+}
+
+void *gadl_ctxfind(gadl_list_t *list, void *data, gadl_ctxcmp_t cmpfunc, void *ctx)
+{
+	gadl_elem_t *i;
+	for(i = list->dlist.first; i != NULL; i = gdl_next(&(list->dlist), i))
+		if (cmpfunc(ctx, i->data, data) == 0)
+			return &(i->data);
+	return NULL;
+}
+
+void *gadl_ctxfind_next(void *item, void *data, gadl_ctxcmp_t cmpfunc, void *ctx)
+{
+	gadl_elem_t *i = elem_of(item);
+	gadl_list_t *list = list_of(i);
+
+	for(i = gdl_next(&(list->dlist), i); i != NULL; i = gdl_next(&(list->dlist), i))
+		if (cmpfunc(ctx, i->data, data) == 0)
+			return &(i->data);
+	return NULL;
+}
+
+
+int gadl_length(gadl_list_t *list)
+{
+	return list->dlist.length;
+}
+
+void *gadl_first(gadl_list_t *list)
+{
+	gadl_elem_t *i = list->dlist.first;
+
+	if (i == NULL)
+		return NULL;
+
+	return &(i->data);
+}
+
+void *gadl_last(gadl_list_t *list)
+{
+	gadl_elem_t *i = list->dlist.last;
+
+	if (i == NULL)
+		return NULL;
+
+	return &(i->data);
+}
+
+gadl_list_t *gadl_parent(void *item)
+{
+	gadl_elem_t *elem = elem_of(item);
+	if (!elem->linked)
+		return NULL;
+	return list_of(elem);
+}
diff --git a/src_3rd/genlist/genadlist.h b/src_3rd/genlist/genadlist.h
new file mode 100644
index 0000000..3f5606d
--- /dev/null
+++ b/src_3rd/genlist/genadlist.h
@@ -0,0 +1,126 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: genlist {at} igor2.repo.hu
+Project page: http://repo.hu/projects/genlist
+*/
+
+#ifndef GENADLIST_H
+#define GENADLIST_H
+#include <stddef.h>
+#include <genlist/gendlist.h>
+#include <genlist/genlistalloc.h>
+
+typedef struct gadl_list_s {
+	gdl_list_t dlist;
+	gla_t allocator;
+
+	size_t elem_size;
+
+	void *alloc_ctx, *user_ctx;
+
+	unsigned int dyn_alloced:1;
+} gadl_list_t;
+
+typedef struct {
+	union {
+		gdl_elem_t e;
+		gla_t allocator;
+	} link;
+	char linked;
+	ptrdiff_t size;
+	char data[1];
+} gadl_elem_t;
+
+
+typedef struct gadl_iterator_s {
+	gadl_elem_t *loop_elem, *prev, *next;
+	size_t count;
+} gadl_iterator_t;
+
+
+typedef void (gadl_destruct_t)(void *item);
+
+gadl_list_t *gadl_list_init(gadl_list_t *list, int elem_size, const gla_t *allocator, void *alloc_ctx);
+
+void *gadl_new(gadl_list_t *list);
+void *gadl_new0(gadl_list_t *list);
+
+void gadl_free(void *item);
+void gadl_destroy(void *item, gadl_destruct_t dfunc);
+
+void *gadl_next_nth(void *item, int n);
+void *gadl_prev_nth(void *item, int n);
+
+void *gadl_nth(gadl_list_t *list, int n);
+int gadl_index(void *item);
+
+void gadl_append(gadl_list_t *list, void *item);
+void gadl_insert(gadl_list_t *list, void *item);
+
+void gadl_insert_before(void *at_item, void *item);
+void gadl_insert_after(void *at_item, void *item);
+
+void gadl_remove(void *item);
+
+void gadl_reverse(gadl_list_t *list);
+
+
+
+typedef int (*gadl_cmp_t)(void *listitem, void *data);
+void *gadl_find(gadl_list_t *list, void *data, gadl_cmp_t cmpfunc);
+void *gadl_find_next(void *item, void *data, gadl_cmp_t cmpfunc);
+
+typedef int (*gadl_ctxcmp_t)(void *ctx, void *listitem, void *data);
+void *gadl_ctxfind(gadl_list_t *list, void *data, gadl_ctxcmp_t cmpfunc, void *ctx);
+void *gadl_ctxfind_next(void *item, void *data, gadl_ctxcmp_t cmpfunc, void *ctx);
+
+
+#define gadl_next(item)  gdl_next(((gadl_elem_t *)(item))->parent, item)
+#define gadl_prev(item)  gdl_prev(((gadl_elem_t *)(item))->parent, item)
+
+
+#define gadl_foreach(list, iterator, loop_item) gadl_foreach_((list), (iterator), (loop_item))
+#define gadl_foreach_(list, iterator, loop_item) \
+	for( \
+		iterator->loop_elem = (gadl_elem_t *)list->dlist.first, iterator->prev = NULL, iterator->next = gdl_next(&(list->dlist), iterator->loop_elem), iterator->count = 0, loop_item = (void *)&(iterator->loop_elem->data); \
+		iterator->loop_elem != NULL; \
+		iterator->loop_elem = iterator->next, iterator->next = gdl_next(&(list->dlist), iterator->next), iterator->count++, loop_item = (void *)&(iterator->loop_elem->data) \
+	)
+
+#define gadl_it_idx(iterator) ((iterator)->count)
+
+int gadl_length(gadl_list_t *list);
+
+void *gadl_first(gadl_list_t *list);
+void *gadl_last(gadl_list_t *list);
+gadl_list_t *gadl_parent(void *item);
+
+void gadl_swap(void *itema, void *itemb);
+
+#endif
diff --git a/src_3rd/genlist/gendlist.h b/src_3rd/genlist/gendlist.h
new file mode 100644
index 0000000..32e5437
--- /dev/null
+++ b/src_3rd/genlist/gendlist.h
@@ -0,0 +1,395 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: genlist {at} igor2.repo.hu
+Project page: http://repo.hu/projects/genlist
+*/
+
+#ifndef GENDLIST_H
+#define GENDLIST_H
+#include <stdlib.h>
+#include <assert.h>
+
+typedef struct gdl_list_s gdl_list_t;
+typedef struct gdl_elem_s gdl_elem_t;
+
+struct gdl_list_s {
+	size_t length;
+	void *first, *last;
+	int link_offs;
+};
+
+struct gdl_elem_s {
+	gdl_list_t *parent;
+	void *prev, *next;
+};
+
+typedef struct gdl_iterator_s {
+	size_t count;
+	void *prev, *next;
+} gdl_iterator_t;
+
+
+#define gdl_link_offs(elem, linkfield) \
+	int __link_offs__ = ((char *)(&(elem->linkfield)) - (char *)elem);
+
+#define gdl_elem_field(list, item)  ((gdl_elem_t *)((char *)(item) + (list)->link_offs))
+
+#define gdl_length(list)          ((list)->length)
+#define gdl_first(list)           ((list)->first)
+#define gdl_last(list)            ((list)->last)
+#define gdl_parent(list, item)    ((item == NULL) ? NULL : gdl_elem_field(list, item)->parent)
+
+
+#define gdl_next(list, item)   gdl_next_((list), (item))
+#define gdl_next_(list, item)  ((item == NULL) ? NULL : gdl_elem_field(list, item)->next)
+
+#define gdl_prev(list, item)   gdl_prev_((list), (item))
+#define gdl_prev_(list, item)  ((item == NULL) ? NULL : gdl_elem_field(list, item)->prev)
+
+#define gdl_next_nth(list, item, n, result)   gdl_next_nth_((list), (item), (n), (result))
+#define gdl_next_nth_(list, item, n, result) \
+do { \
+	void *__i__; \
+	size_t __c__ = n; \
+	for(__i__ = item; (__c__) > 0 && (__i__ != NULL); __i__ = gdl_next(list, __i__)) __c__--; \
+	*result = __i__; \
+} while(0)
+
+#define gdl_prev_nth(list, item, n, result)   gdl_prev_nth_((list), (item), (n), (result))
+#define gdl_prev_nth_(list, item, n, result) \
+do { \
+	void *__i__; \
+	size_t __c__ = n; \
+	for(__i__ = item; (__c__) > 0 && (__i__ != NULL); __i__ = gdl_prev(list, __i__)) __c__--; \
+	*result = __i__; \
+} while(0)
+
+
+#define gdl_append(list, elem, linkfield) gdl_append_((list), (elem), linkfield)
+#define gdl_append_(list, elem, linkfield) \
+do { \
+	gdl_link_offs(elem, linkfield); \
+	assert(elem->linkfield.parent == NULL); \
+	elem->linkfield.parent = list; \
+	if (list->first == NULL) { \
+		assert(list->length == 0); \
+		assert(list->last == NULL); \
+		list->first = elem; \
+		list->link_offs = __link_offs__; \
+	} \
+	else \
+		assert(list->link_offs == __link_offs__); \
+	if (list->last != NULL) { \
+		gdl_elem_field(list, list->last)->next = elem; \
+		elem->linkfield.prev = list->last; \
+	} \
+	list->last = elem; \
+	elem->linkfield.next = NULL; \
+	list->length++; \
+} while(0)
+
+#define gdl_insert(list, elem, linkfield) gdl_insert_((list), (elem), linkfield)
+#define gdl_insert_(list, elem, linkfield) \
+do { \
+	gdl_link_offs(elem, linkfield); \
+	assert(elem->linkfield.parent == NULL); \
+	elem->linkfield.parent = list; \
+	if (list->last == NULL) { \
+		assert(list->length == 0); \
+		assert(list->first == NULL); \
+		list->last = elem; \
+		list->link_offs = __link_offs__; \
+	} \
+	else \
+		assert(list->link_offs == __link_offs__); \
+	if (list->first != NULL) { \
+		gdl_elem_field(list, list->first)->prev = elem; \
+		elem->linkfield.next = list->first; \
+	} \
+	list->first = elem; \
+	elem->linkfield.prev = NULL; \
+	list->length++; \
+} while(0)
+
+#define gdl_insert_before(list, at_elem, elem, linkfield) gdl_insert_before_((list), (at_elem), (elem), linkfield)
+#define gdl_insert_before_(list, at_elem, elem, linkfield) \
+do { \
+	void *__prev__; \
+	gdl_link_offs(elem, linkfield); \
+	assert(elem->linkfield.parent == NULL); \
+	if ((at_elem == NULL) || (gdl_elem_field(list, at_elem)->prev == NULL)) { \
+		gdl_insert_(list, elem, linkfield); \
+		elem->linkfield.parent = list; \
+		break; \
+	} \
+	elem->linkfield.parent = list; \
+	assert(list->link_offs == __link_offs__); \
+	__prev__ = gdl_elem_field(list, at_elem)->prev; \
+	gdl_elem_field(list, __prev__)->next = elem; \
+	gdl_elem_field(list, at_elem)->prev = elem; \
+	elem->linkfield.prev = __prev__; \
+	elem->linkfield.next = at_elem; \
+	list->length++; \
+} while(0)
+
+#define gdl_insert_after(list, at_elem, elem, linkfield) gdl_insert_after_((list), (at_elem), (elem), linkfield)
+#define gdl_insert_after_(list, at_elem, elem, linkfield) \
+do { \
+	void *__next__; \
+	gdl_link_offs(elem, linkfield); \
+	assert(elem->linkfield.parent == NULL); \
+	elem->linkfield.parent = list; \
+	if ((at_elem == NULL) || (gdl_elem_field(list, at_elem)->next == NULL)) { \
+		gdl_append_(list, elem, linkfield); \
+		break; \
+	} \
+	assert(list->link_offs == __link_offs__); \
+	__next__ = gdl_elem_field(list, at_elem)->next; \
+	gdl_elem_field(list, __next__)->prev = elem; \
+	gdl_elem_field(list, at_elem)->next = elem; \
+	elem->linkfield.prev = at_elem; \
+	elem->linkfield.next = __next__; \
+	list->length++; \
+} while(0)
+
+#define gdl_remove(list, elem, linkfield) gdl_remove_((list), (elem), linkfield)
+#define gdl_remove_(list, elem, linkfield) \
+do { \
+	void *__next__; \
+	void *__prev__; \
+	gdl_link_offs(elem, linkfield); \
+	assert(elem->linkfield.parent == list); \
+	assert(list->link_offs == __link_offs__); \
+	__prev__ = gdl_elem_field(list, elem)->prev; \
+	__next__ = gdl_elem_field(list, elem)->next; \
+	if (__prev__ != NULL) { \
+		assert(list->first != elem); \
+		gdl_elem_field(list, __prev__)->next = __next__; \
+	} \
+	else { \
+		assert(list->first == elem); \
+		list->first = __next__;  \
+	} \
+	if (__next__ != NULL) { \
+		assert(list->last != elem); \
+		gdl_elem_field(list, __next__)->prev = __prev__; \
+	} \
+	else { \
+		assert(list->last == elem); \
+		list->last = __prev__;  \
+	} \
+	list->length--; \
+	gdl_elem_field(list, elem)->prev = NULL; \
+	gdl_elem_field(list, elem)->next = NULL; \
+	gdl_elem_field(list, elem)->parent = NULL; \
+} while(0)
+
+#define gdl_swap(list, elema, elemb, linkfield) gdl_swap_((list), (elema), (elemb), linkfield)
+#define gdl_swap_(list, elema, elemb, linkfield) \
+do { \
+	void *__tmp__, *__prev__, *__next__; \
+	gdl_link_offs(elema, linkfield); \
+	assert(elema->linkfield.parent == list); \
+	assert(elemb->linkfield.parent == list); \
+	assert(list->link_offs == __link_offs__); \
+	if (elema == elemb) \
+		break; \
+	if (list->first == elema) \
+		list->first = elemb; \
+	else if (list->first == elemb) \
+		list->first = elema; \
+	if (list->last == elema) \
+		list->last = elemb; \
+	else if (list->last == elemb) \
+		list->last = elema; \
+	if (gdl_elem_field(list, elema)->next == elemb) { \
+		gdl_swap_neigh_(list, elema, elemb, linkfield); \
+		break; \
+	} \
+	if (gdl_elem_field(list, elemb)->next == elema) { \
+		gdl_swap_neigh_(list, elemb, elema, linkfield); \
+		break; \
+	} \
+	__prev__ = gdl_elem_field(list, elema)->prev; \
+	if (__prev__ != NULL) \
+		gdl_elem_field(list, __prev__)->next = elemb; \
+	__prev__ = gdl_elem_field(list, elemb)->prev; \
+	if (__prev__ != NULL) \
+		gdl_elem_field(list, __prev__)->next = elema; \
+	__next__ = gdl_elem_field(list, elema)->next; \
+	if (__next__ != NULL)\
+		gdl_elem_field(list, __next__)->prev = elemb; \
+	__next__ = gdl_elem_field(list, elemb)->next; \
+	if (__next__ != NULL) \
+		gdl_elem_field(list, __next__)->prev = elema; \
+	__tmp__ = gdl_elem_field(list, elema)->prev; \
+	gdl_elem_field(list, elema)->prev = gdl_elem_field(list, elemb)->prev; \
+	gdl_elem_field(list, elemb)->prev = __tmp__; \
+	__tmp__ = gdl_elem_field(list, elema)->next; \
+	gdl_elem_field(list, elema)->next = gdl_elem_field(list, elemb)->next; \
+	gdl_elem_field(list, elemb)->next = __tmp__; \
+} while(0)
+
+/* internal: swap a,b if they are neighbours, in this order */
+#define gdl_swap_neigh_(list, elema, elemb, linkfield) \
+do { \
+	void *__prev__, *__next__; \
+	__prev__ = gdl_elem_field(list, elema)->prev; \
+	__next__ = gdl_elem_field(list, elemb)->next; \
+	gdl_elem_field(list, elemb)->prev = __prev__; \
+	gdl_elem_field(list, elemb)->next = elema; \
+	gdl_elem_field(list, elema)->prev = elemb; \
+	gdl_elem_field(list, elema)->next = __next__; \
+	if (__prev__ != NULL) \
+		gdl_elem_field(list, __prev__)->next = elemb; \
+	if (__next__ != NULL) \
+		gdl_elem_field(list, __next__)->prev = elema; \
+} while(0)
+
+
+#define gdl_reverse(list) gdl_reverse_((list))
+#define gdl_reverse_(list) \
+do { \
+	void *__i__, *__tmp__, *__next__; \
+	for(__i__ = list->first;  __i__ != NULL; __i__ = __next__) {\
+		__next__ = gdl_next(list, __i__); \
+		__tmp__ = gdl_elem_field(list, __i__)->prev; \
+		gdl_elem_field(list, __i__)->prev = gdl_elem_field(list, __i__)->next; \
+		gdl_elem_field(list, __i__)->next = __tmp__; \
+	} \
+	__tmp__ = list->first; \
+	list->first = list->last; \
+	list->last = __tmp__; \
+} while(0)
+
+#define gdl_foreach(list, iterator, loop_elem) gdl_foreach_((list), (iterator), (loop_elem))
+#define gdl_foreach_(list, iterator, loop_elem) \
+	for( \
+		loop_elem = list->first, iterator->prev = NULL, iterator->next = gdl_next(list, loop_elem), iterator->count = 0; \
+		loop_elem != NULL; \
+		loop_elem = iterator->next, iterator->next = gdl_next(list, iterator->next), iterator->count++ \
+	)
+
+#define gdl_it_idx(iterator) ((iterator)->count)
+
+#define gdl_nth(list, index, res_elem)  gdl_nth_((list), (index), (res_elem))
+#define gdl_nth_(list, index, res_elem) \
+do { \
+	size_t __n__; \
+	void *__res__; \
+	for(__n__ = 0, __res__ = list->first; __n__ < index; __n__++, __res__=gdl_next(list, __res__)) ; \
+	*res_elem = __res__; \
+} while(0)
+
+
+#define gdl_index(list, elem, linkfield, result) gdl_index_((list), (elem), linkfield, (result))
+#define gdl_index_(list, elem, linkfield, result) \
+do { \
+	size_t __count__ = 0; \
+	void *__i__; \
+	gdl_link_offs(elem, linkfield); \
+	assert(elem->linkfield.parent == list); \
+	assert(list->link_offs == __link_offs__); \
+	for(__i__ = elem; __i__ != NULL; __i__ = gdl_prev(list, __i__)) __count__++; \
+	*(result) = __count__ - 1;  \
+} while(0)
+
+
+#define gdl_check(list) gdl_check_((list))
+#define gdl_check_(list) \
+do { \
+	void *__prev__, *__i__; \
+	size_t __count__; \
+	for( \
+		__i__ = list->first, __count__ = 0, __prev__ = 0; \
+		__i__ != NULL; \
+		__prev__ = __i__, __i__ = gdl_next(list, __i__), __count__++ \
+	) {\
+		assert(gdl_prev(list, __i__) == __prev__); \
+	} \
+	assert(list->length == __count__); \
+	assert(list->last == __prev__); \
+} while(0)
+
+
+#define gdl_find(list, data, compar, result) gdl_find_((list), (data), (compar), (result))
+#define gdl_find_(list, data, compar, result) \
+do { \
+	void *__i__, *__r__ = NULL; \
+	for(__i__ = list->first; __i__ != NULL; __i__ = gdl_next(list, __i__)) { \
+		if (compar(__i__, data) == 0) { \
+			__r__ = __i__; \
+			break; \
+		} \
+	} \
+	*result = __r__; \
+} while(0);
+
+#define gdl_find_next(list, elem, data, compar, result) gdl_find_next_((list), (elem), (data), (compar), (result))
+#define gdl_find_next_(list, elem, data, compar, result) \
+do { \
+	void *__i__, *__r__ = NULL; \
+	assert(gdl_elem_field(list, elem)->parent == list); \
+	for(__i__ = gdl_next(list, elem); __i__ != NULL; __i__ = gdl_next(list, __i__)) { \
+		if (compar(__i__, data) == 0) { \
+			__r__ = __i__; \
+			break; \
+		} \
+	} \
+	*result = __r__; \
+} while(0);
+
+#define gdl_ctxfind(list, data, compar, ctx, result) gdl_ctxfind_((list), (data), (compar), (ctx), (result))
+#define gdl_ctxfind_(list, data, compar, ctx, result) \
+do { \
+	void *__i__, *__r__ = NULL; \
+	for(__i__ = list->first; __i__ != NULL; __i__ = gdl_next(list, __i__)) { \
+		if (compar(ctx, __i__, data) == 0) { \
+			__r__ = __i__; \
+			break; \
+		} \
+	} \
+	*result = __r__; \
+} while(0);
+
+#define gdl_ctxfind_next(list, elem, data, compar, ctx, result) gdl_ctxfind_next_((list), (elem), (data), (compar), (ctx), (result))
+#define gdl_ctxfind_next_(list, elem, data, compar, ctx, result) \
+do { \
+	void *__i__, *__r__ = NULL; \
+	assert(gdl_elem_field(list, elem)->parent == list); \
+	for(__i__ = gdl_next(list, elem); __i__ != NULL; __i__ = gdl_next(list, __i__)) { \
+		if (compar(ctx, __i__, data) == 0) { \
+			__r__ = __i__; \
+			break; \
+		} \
+	} \
+	*result = __r__; \
+} while(0);
+
+#endif
diff --git a/src_3rd/genlist/genlistalloc.c b/src_3rd/genlist/genlistalloc.c
new file mode 100644
index 0000000..ca3621f
--- /dev/null
+++ b/src_3rd/genlist/genlistalloc.c
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: genlist {at} igor2.repo.hu
+Project page: http://repo.hu/projects/genlist
+*/
+
+#include "genlistalloc.h"
+
+static void *gla_malloc_alloc(void *alloc_ctx, size_t size)
+{
+	return malloc(size);
+}
+
+static void gla_malloc_free(void *alloc_ctx, void *ptr)
+{
+	free(ptr);
+}
+
+
+const gla_t gla_malloc = { gla_malloc_alloc, gla_malloc_free };
diff --git a/src_3rd/genlist/genlistalloc.h b/src_3rd/genlist/genlistalloc.h
new file mode 100644
index 0000000..b648d22
--- /dev/null
+++ b/src_3rd/genlist/genlistalloc.h
@@ -0,0 +1,45 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: genlist {at} igor2.repo.hu
+Project page: http://repo.hu/projects/genlist
+*/
+
+#ifndef GENLISTALLOC_H
+#define GENLISTALLOC_H
+
+#include <stdlib.h>
+
+typedef struct gla_s {
+	void *(*alloc)(void *alloc_ctx, size_t size);
+	void (*free)(void *alloc_ctx, void *ptr);
+} gla_t;
+
+extern const gla_t gla_malloc;
+
+#endif
diff --git a/src_3rd/genlist/gentdlist_impl.c b/src_3rd/genlist/gentdlist_impl.c
new file mode 100644
index 0000000..9caac7f
--- /dev/null
+++ b/src_3rd/genlist/gentdlist_impl.c
@@ -0,0 +1,136 @@
+#include "gendlist.h"
+
+/******** pointer return functions *******/
+#undef assert
+#define assert(x) \
+	do { \
+		if (!(x)) \
+			return NULL; \
+	} while(0)
+
+
+TDL_FUNC TDL_ITEM_T *TDL(next)(TDL_ITEM_T *item)    { return gdl_next(item->TDL_FIELD.parent, item); }
+TDL_FUNC TDL_ITEM_T *TDL(prev)(TDL_ITEM_T *item)    { return gdl_prev(item->TDL_FIELD.parent, item); }
+TDL_FUNC TDL_LIST_T *TDL(parent)(TDL_ITEM_T *item)  { return (TDL_LIST_T *)(item->TDL_FIELD.parent); }
+TDL_FUNC TDL_ITEM_T *TDL(first)(TDL_LIST_T *list)   { return gdl_first(&list->lst); }
+TDL_FUNC TDL_ITEM_T *TDL(last)(TDL_LIST_T *list)    { return gdl_last(&list->lst); }
+
+TDL_FUNC TDL_ITEM_T *TDL(next_nth)(TDL_ITEM_T *item, TDL_SIZE_T n)
+{
+	TDL_ITEM_T *res;
+	gdl_next_nth(item->TDL_FIELD.parent, item, n, &res);
+	return res;
+}
+
+TDL_FUNC TDL_ITEM_T *TDL(prev_nth)(TDL_ITEM_T *item, TDL_SIZE_T n)
+{
+	TDL_ITEM_T *res;
+	gdl_prev_nth(item->TDL_FIELD.parent, item, n, &res);
+	return res;
+}
+
+TDL_FUNC TDL_ITEM_T *TDL(nth)(TDL_LIST_T *list, TDL_SIZE_T n)
+{
+	TDL_ITEM_T *res;
+	gdl_nth(&list->lst, n, &res);
+	return res;
+}
+
+TDL_FUNC TDL_ITEM_T *TDL(find)(TDL_LIST_T *list, void *data, TDL(cmp_t) compar)
+{
+	TDL_ITEM_T *res;
+	gdl_find(&list->lst, data, compar, &res);
+	return res;
+}
+
+TDL_FUNC TDL_ITEM_T *TDL(find_next)(TDL_ITEM_T *last, void *data, TDL(cmp_t) compar)
+{
+	TDL_ITEM_T *res;
+	gdl_find_next(last->TDL_FIELD.parent, last, data, compar, &res);
+	return res;
+}
+
+TDL_FUNC TDL_ITEM_T *TDL(ctxfind)(TDL_LIST_T *list, void *data, TDL(ctxcmp_t) compar, void *ctx)
+{
+	TDL_ITEM_T *res;
+	gdl_ctxfind(&list->lst, data, compar, ctx, &res);
+	return res;
+}
+
+TDL_FUNC TDL_ITEM_T *TDL(ctxfind_next)(TDL_ITEM_T *last, void *data, TDL(ctxcmp_t) compar, void *ctx)
+{
+	TDL_ITEM_T *res;
+	gdl_ctxfind_next(last->TDL_FIELD.parent, last, data, compar, ctx, &res);
+	return res;
+}
+
+/******** integer return functions *******/
+#undef assert
+#define assert(x) \
+	do { \
+		if (!(x)) \
+			return -1; \
+	} while(0)
+
+TDL_FUNC int TDL(append)(TDL_LIST_T *list, TDL_ITEM_T *item)
+{
+	gdl_append(&list->lst, item, TDL_FIELD);
+	return 0;
+}
+
+TDL_FUNC int TDL(insert)(TDL_LIST_T *list, TDL_ITEM_T *item)
+{
+	gdl_insert(&list->lst, item, TDL_FIELD);
+	return 0;
+}
+
+TDL_FUNC int TDL(insert_before)(TDL_LIST_T *list, TDL_ITEM_T *item_at, TDL_ITEM_T *item)
+{
+	gdl_insert_before(&list->lst, item_at, item, TDL_FIELD);
+	return 0;
+}
+
+TDL_FUNC int TDL(insert_after)(TDL_LIST_T *list, TDL_ITEM_T *item_at, TDL_ITEM_T *item)
+{
+	gdl_insert_after(&list->lst, item_at, item, TDL_FIELD);
+	return 0;
+}
+
+TDL_FUNC int TDL(remove)(TDL_ITEM_T *item)
+{
+	if (item->TDL_FIELD.parent == 0)
+		return -1;
+	gdl_remove(item->TDL_FIELD.parent, item, TDL_FIELD);
+	return 0;
+}
+
+TDL_FUNC int TDL(swap)(TDL_ITEM_T *itema, TDL_ITEM_T *itemb)
+{
+	gdl_swap(itema->TDL_FIELD.parent, itema, itemb, TDL_FIELD);
+	return 0;
+}
+
+TDL_FUNC int TDL(check)(TDL_LIST_T *list)
+{
+	gdl_check(&list->lst);
+	return 0;
+}
+
+TDL_FUNC TDL_SIZE_T TDL(length)(TDL_LIST_T *list)
+{
+	return gdl_length(&list->lst);
+}
+
+TDL_FUNC TDL_SIZE_T TDL(index)(TDL_ITEM_T *item)
+{
+	TDL_SIZE_T res;
+	gdl_index(item->TDL_FIELD.parent, item, TDL_FIELD, &res);
+	return res;
+}
+
+/******** void functions *******/
+#undef assert
+TDL_FUNC void TDL(reverse)(TDL_LIST_T *list)  { gdl_reverse(&list->lst); }
+
+
+
diff --git a/src_3rd/genlist/gentdlist_impl.h b/src_3rd/genlist/gentdlist_impl.h
new file mode 100644
index 0000000..0291ec5
--- /dev/null
+++ b/src_3rd/genlist/gentdlist_impl.h
@@ -0,0 +1,41 @@
+#include "gendlist.h"
+
+#ifndef TDL_LIST_U
+#define TDL_LIST_U
+#endif
+
+typedef union TDL_LIST_U {
+	gdl_list_t lst;
+} TDL_LIST_T;
+
+TDL_FUNC TDL_ITEM_T *TDL(next)(TDL_ITEM_T *item);
+TDL_FUNC TDL_ITEM_T *TDL(prev)(TDL_ITEM_T *item);
+TDL_FUNC TDL_ITEM_T *TDL(next_nth)(TDL_ITEM_T *item, TDL_SIZE_T n);
+TDL_FUNC TDL_ITEM_T *TDL(prev_nth)(TDL_ITEM_T *item, TDL_SIZE_T n);
+TDL_FUNC TDL_ITEM_T *TDL(nth)(TDL_LIST_T *list, TDL_SIZE_T n);
+TDL_FUNC TDL_LIST_T *TDL(parent)(TDL_ITEM_T *item);
+
+TDL_FUNC TDL_ITEM_T *TDL(first)(TDL_LIST_T *list);
+TDL_FUNC TDL_ITEM_T *TDL(last)(TDL_LIST_T *list);
+
+typedef int (*TDL(cmp_t))(TDL_ITEM_T *item, void *data);
+typedef int (*TDL(ctxcmp_t))(void *ctx, TDL_ITEM_T *item, void *data);
+TDL_FUNC TDL_ITEM_T *TDL(find)(TDL_LIST_T *list, void *data, TDL(cmp_t) compar);
+TDL_FUNC TDL_ITEM_T *TDL(find_next)(TDL_ITEM_T *last, void *data, TDL(cmp_t) compar);
+TDL_FUNC TDL_ITEM_T *TDL(ctxfind)(TDL_LIST_T *list, void *data, TDL(ctxcmp_t) compar, void *ctx);
+TDL_FUNC TDL_ITEM_T *TDL(ctxfind_next)(TDL_ITEM_T *last, void *data, TDL(ctxcmp_t) compar, void *ctx);
+
+TDL_FUNC int TDL(append)(TDL_LIST_T *list, TDL_ITEM_T *item);
+TDL_FUNC int TDL(insert)(TDL_LIST_T *list, TDL_ITEM_T *item);
+TDL_FUNC int TDL(insert_before)(TDL_LIST_T *list, TDL_ITEM_T *item_at, TDL_ITEM_T *item);
+TDL_FUNC int TDL(insert_after)(TDL_LIST_T *list, TDL_ITEM_T *item_at, TDL_ITEM_T *item);
+TDL_FUNC int TDL(remove)(TDL_ITEM_T *item);
+TDL_FUNC int TDL(swap)(TDL_ITEM_T *itema, TDL_ITEM_T *itemb);
+TDL_FUNC int TDL(check)(TDL_LIST_T *list);
+TDL_FUNC TDL_SIZE_T TDL(length)(TDL_LIST_T *list);
+TDL_FUNC TDL_SIZE_T TDL(index)(TDL_ITEM_T *item);
+
+
+TDL_FUNC void TDL(reverse)(TDL_LIST_T *list);
+
+
diff --git a/src_3rd/genlist/gentdlist_undef.h b/src_3rd/genlist/gentdlist_undef.h
new file mode 100644
index 0000000..ff053fc
--- /dev/null
+++ b/src_3rd/genlist/gentdlist_undef.h
@@ -0,0 +1,9 @@
+#ifndef TDL_DONT_UNDEF
+#	undef TDL
+#	undef TDL_LIST_T
+# undef TDL_LIST_U
+#	undef TDL_ITEM_T
+#	undef TDL_SIZE_T
+#	undef TDL_FIELD
+#	undef TDL_FUNC
+#endif
diff --git a/src_3rd/genregex/Makefile b/src_3rd/genregex/Makefile
new file mode 100644
index 0000000..858e67d
--- /dev/null
+++ b/src_3rd/genregex/Makefile
@@ -0,0 +1,35 @@
+#CFLAGS = -Wall -ansi -pedantic -g
+CFLAGS = -g
+LDFLAGS =
+
+OBJS = tester.o regex.o  regex_se.o regex_sei.o regex_be.o regex_bei.o \
+       regex_st.o regex_sti.o regex_bt.o regex_bti.o
+
+tester: $(OBJS)
+
+tester.o: tester.c regex_se.h regex_be.h regex_st.h regex_bt.h regex_templ.h regex.h tester_main.c
+
+regex.o: regex.c regex.h
+
+regex_se.o: regex_se.c regex_se.h   regex_templ.c regex_templ.h regex.h
+
+regex_sei.o: regex_sei.c regex_sei.h   regex_templ.c regex_templ.h regex.h
+
+regex_be.o: regex_be.c regex_be.h   regex_templ.c regex_templ.h regex.h
+
+regex_bei.o: regex_bei.c regex_bei.h   regex_templ.c regex_templ.h regex.h
+
+regex_st.o: regex_st.c regex_st.h   regex_templ.c regex_templ.h regex.h
+
+regex_sti.o: regex_sti.c regex_sti.h   regex_templ.c regex_templ.h regex.h
+
+regex_bt.o: regex_bt.c regex_bt.h   regex_templ.c regex_templ.h regex.h
+
+regex_bti.o: regex_bti.c regex_bti.h   regex_templ.c regex_templ.h regex.h
+
+test:
+	cd ../regression; make all
+
+clean:
+	rm $(OBJS) tester 2>/dev/null ; true
+
diff --git a/src_3rd/genregex/regex.c b/src_3rd/genregex/regex.c
new file mode 100644
index 0000000..c272b24
--- /dev/null
+++ b/src_3rd/genregex/regex.c
@@ -0,0 +1,25 @@
+#include "regex.h"
+
+static const char *msg[] = {
+	"success",
+	"No previous regular expression",
+	"Missing ]",
+	"Empty closure",
+	"Illegal closure before + or *",
+	"Too many \\(\\) pairs",
+	"Null pattern inside \\(\\)",
+	"Unmatched \\)",
+	"Null pattern inside \\<\\>",
+	"Cyclical reference",
+	"Undetermined reference",
+	"Unmatched \\(",
+	"closure: bad nfa.",
+	"re_exec: bad nfa."
+};
+
+const char *re_error_str(re_error_t code)
+{
+	if ((code < 0) || (code >= (sizeof(msg) / sizeof(char *))))
+		return "Unknown/invalid error";
+	return msg[code];
+}
diff --git a/src_3rd/genregex/regex.h b/src_3rd/genregex/regex.h
new file mode 100644
index 0000000..b252cbc
--- /dev/null
+++ b/src_3rd/genregex/regex.h
@@ -0,0 +1,25 @@
+#ifndef GENREGEX_H
+#define GENREGEX_H
+
+typedef enum re_error_e {
+	RE_ERR_SUCCESS = 0,
+
+/* re_*_comp: */
+	RE_ERR_NOPREV,
+	RE_ERR_NOCLCL,
+	RE_ERR_EMPTYCLO,
+	RE_ERR_BADCLO,
+	RE_ERR_STACK,
+	RE_ERR_EMPTYTAG,
+	RE_ERR_UNMATCHTAGC,
+	RE_ERR_EMPTYWORD,
+	RE_ERR_CYCREF,
+	RE_ERR_UNTERMREF,
+	RE_ERR_UNMATCHTAGO,
+	RE_ERR_CLOBADNFA,
+	RE_ERR_BADNFA
+} re_error_t;
+
+const char *re_error_str(re_error_t code);
+
+#endif
diff --git a/src_3rd/genregex/regex_be.c b/src_3rd/genregex/regex_be.c
new file mode 100644
index 0000000..86c94c4
--- /dev/null
+++ b/src_3rd/genregex/regex_be.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_be_ ## name
+#define RE_EXTEND 1
+#define RE_BIN_API 1
+#define RE_ICASE 0
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_be.h b/src_3rd/genregex/regex_be.h
new file mode 100644
index 0000000..a0ae963
--- /dev/null
+++ b/src_3rd/genregex/regex_be.h
@@ -0,0 +1,6 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_be_ ## name
+#define RE_BIN_API 1
+#include "regex_templ.h"
+#undef RE_BIN_API
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_bei.c b/src_3rd/genregex/regex_bei.c
new file mode 100644
index 0000000..2cedbef
--- /dev/null
+++ b/src_3rd/genregex/regex_bei.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_bei_ ## name
+#define RE_EXTEND 1
+#define RE_BIN_API 1
+#define RE_ICASE 1
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_bei.h b/src_3rd/genregex/regex_bei.h
new file mode 100644
index 0000000..db6d007
--- /dev/null
+++ b/src_3rd/genregex/regex_bei.h
@@ -0,0 +1,6 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_bei_ ## name
+#define RE_BIN_API 1
+#include "regex_templ.h"
+#undef RE_BIN_API
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_bt.c b/src_3rd/genregex/regex_bt.c
new file mode 100644
index 0000000..e604515
--- /dev/null
+++ b/src_3rd/genregex/regex_bt.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_bt_ ## name
+#define RE_EXTEND 0
+#define RE_BIN_API 1
+#define RE_ICASE 0
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_bt.h b/src_3rd/genregex/regex_bt.h
new file mode 100644
index 0000000..ad125a7
--- /dev/null
+++ b/src_3rd/genregex/regex_bt.h
@@ -0,0 +1,6 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_bt_ ## name
+#define RE_BIN_API 1
+#include "regex_templ.h"
+#undef RE_BIN_API
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_bti.c b/src_3rd/genregex/regex_bti.c
new file mode 100644
index 0000000..cc2e3c6
--- /dev/null
+++ b/src_3rd/genregex/regex_bti.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_bti_ ## name
+#define RE_EXTEND 0
+#define RE_BIN_API 1
+#define RE_ICASE 1
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_bti.h b/src_3rd/genregex/regex_bti.h
new file mode 100644
index 0000000..b004e19
--- /dev/null
+++ b/src_3rd/genregex/regex_bti.h
@@ -0,0 +1,6 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_bti_ ## name
+#define RE_BIN_API 1
+#include "regex_templ.h"
+#undef RE_BIN_API
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_se.c b/src_3rd/genregex/regex_se.c
new file mode 100644
index 0000000..b2e6d74
--- /dev/null
+++ b/src_3rd/genregex/regex_se.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_se_ ## name
+#define RE_EXTEND 1
+#define RE_BIN_API 0
+#define RE_ICASE 0
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_se.h b/src_3rd/genregex/regex_se.h
new file mode 100644
index 0000000..7d21806
--- /dev/null
+++ b/src_3rd/genregex/regex_se.h
@@ -0,0 +1,4 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_se_ ## name
+#include "regex_templ.h"
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_sei.c b/src_3rd/genregex/regex_sei.c
new file mode 100644
index 0000000..9d54a81
--- /dev/null
+++ b/src_3rd/genregex/regex_sei.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_sei_ ## name
+#define RE_EXTEND 1
+#define RE_BIN_API 0
+#define RE_ICASE 1
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_sei.h b/src_3rd/genregex/regex_sei.h
new file mode 100644
index 0000000..d278061
--- /dev/null
+++ b/src_3rd/genregex/regex_sei.h
@@ -0,0 +1,4 @@
+/* Extended regex with string API, case insensitive */
+#define RE_PRFX(name) re_sei_ ## name
+#include "regex_templ.h"
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_st.c b/src_3rd/genregex/regex_st.c
new file mode 100644
index 0000000..b75942d
--- /dev/null
+++ b/src_3rd/genregex/regex_st.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_st_ ## name
+#define RE_EXTEND 0
+#define RE_BIN_API 0
+#define RE_ICASE 0
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_st.h b/src_3rd/genregex/regex_st.h
new file mode 100644
index 0000000..630c035
--- /dev/null
+++ b/src_3rd/genregex/regex_st.h
@@ -0,0 +1,4 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_st_ ## name
+#include "regex_templ.h"
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_sti.c b/src_3rd/genregex/regex_sti.c
new file mode 100644
index 0000000..d70e32d
--- /dev/null
+++ b/src_3rd/genregex/regex_sti.c
@@ -0,0 +1,7 @@
+#define RE_PRFX(name) re_sti_ ## name
+#define RE_EXTEND 0
+#define RE_BIN_API 0
+#define RE_ICASE 1
+
+#include "regex_templ.c"
+
diff --git a/src_3rd/genregex/regex_sti.h b/src_3rd/genregex/regex_sti.h
new file mode 100644
index 0000000..37a6277
--- /dev/null
+++ b/src_3rd/genregex/regex_sti.h
@@ -0,0 +1,4 @@
+/* Extended regex with string API */
+#define RE_PRFX(name) re_sti_ ## name
+#include "regex_templ.h"
+#undef RE_PRFX
diff --git a/src_3rd/genregex/regex_templ.c b/src_3rd/genregex/regex_templ.c
new file mode 100644
index 0000000..d335958
--- /dev/null
+++ b/src_3rd/genregex/regex_templ.c
@@ -0,0 +1,831 @@
+/*
+ * regex - Regular expression pattern matching  and replacement
+ *
+ * By:  Ozan S. Yigit (oz)
+ *      Dept. of Computer Science
+ *      York University
+ *
+ * These routines are the PUBLIC DOMAIN equivalents of regex
+ * routines as found in 4.nBSD UN*X, with minor extensions.
+ *
+ * These routines are derived from various implementations found
+ * in software tools books, and Conroy's grep. They are NOT derived
+ * from licensed/restricted software.
+ * For more interesting/academic/complicated implementations,
+ * see Henry Spencer's regexp routines, or GNU Emacs pattern
+ * matching module.
+ *
+ * const correctness patch by Tibor 'Igor2' Palinkas in 2009..2010
+ * tailored to minetd use by Tibor 'Igor2' Palinkas in 2011
+ * generalized for genregex by Tibor 'Igor2' Palinkas in 2012
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "regex_templ.h"
+#include "regex.h"
+
+#if RE_BIN_API == 1
+#define SRCLEN , int srclen
+#define BIN_OR_EMPTY(p)  , p
+#define PATLEN , int patlen
+#define PATLEN_ , patlen
+#else
+#define SRCLEN
+#define BIN_OR_EMPTY(p)
+#define PATLEN
+#define PATLEN_
+#endif
+
+#if RE_ICASE == 1
+#	include <ctype.h>
+#	define re_tolower(c) tolower(c)
+#else
+#	define re_tolower(c) c
+#endif
+
+#define MAXTAG  10
+
+enum opcode_e {
+	NOP = 0,
+	END = 0,
+
+	CHR = 1,   /* specific char         */
+	ANY = 2,   /* any character:   .    */
+	CCL = 3,   /* character class: []   */
+	BOL = 4,   /* begin-of-line:   ^    */
+	EOL = 5,   /* end-of-line:     $    */
+	BOT = 6,   /* begin-of-tag:    \(   */
+	EOT = 7,   /* end-of-tag:      \)   */
+	BOW = 8,   /* begin-of-word:   \<   */
+	EOW = 9,   /* end-of-word:     \>   */
+	REF = 10,  /* backref:         \1   */
+	CLO = 11   /* closure               */
+};
+
+/*
+ * The following defines are not meant to be changeable.
+ * They are for readability only.
+ */
+#define MAXCHR	128
+#define CHRBIT	8
+#define BITBLK	MAXCHR/CHRBIT
+#define BLKIND	0170
+#define BITIND	07
+
+#define ASCIIB	0177
+
+#ifdef NO_UCHAR
+typedef char CHAR;
+#else
+typedef unsigned char CHAR;
+#endif
+
+static CHAR bitarr[] = {1,2,4,8,16,32,64,128};
+
+struct RE_PRFX(s) {
+	CHAR *nfa;                        /* precompiled regex bytecode */
+	int nfa_used, nfa_alloced;
+
+	CHAR bittab[BITBLK];             /* bit table for CCL */
+                                   /* pre-set bits...   */
+
+	/* tags */
+	const char *bol;
+	const char *bopat[MAXTAG];
+	const char *eopat[MAXTAG];
+
+	/* dest buffer for subst and backref */
+	char *dst;
+	int dst_used, dst_alloced;
+
+	/* error details */
+	re_error_t error;
+	char fail_op;
+};
+
+static void
+chset(RE_PRFX(t) *r, CHAR c)
+{
+	r->bittab[(CHAR) ((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
+}
+
+#define badpat(x)  do { r->error = x; goto error; } while(0)
+
+#define grow(r, buf, delta) \
+	if (r->buf ## _used + delta >= r->buf ## _alloced) { \
+		r->buf ## _alloced += 256 + delta; \
+		r->buf = realloc(r->buf, r->buf ## _alloced); \
+	}
+
+#define store(x) \
+	do { \
+		grow(r, nfa, 1); \
+		*mp++ = x; \
+	} while(0)
+
+
+
+RE_PRFX(t) *RE_PRFX(comp)(const char *pat)
+{
+	int  tagstk[MAXTAG];             /* subpat tag stack..*/
+	int  sta;                        /* status of lastpat */
+
+	register const char *p;         /* pattern pointer   */
+	register CHAR *mp;              /* nfa pointer       */
+	register CHAR *lp;              /* saved pointer..   */
+	register CHAR *sp;              /* another one..     */
+
+	register int tagi = 0;          /* tag stack index   */
+	register int tagc = 1;          /* actual tag count  */
+
+	register int n;
+	register CHAR mask;             /* xor mask -CCL/NCL */
+	int c1, c2;
+
+	RE_PRFX(t) *r;
+
+
+	r = calloc(1, sizeof(RE_PRFX(t)));
+	sta = NOP;
+	grow(r, nfa, 1);
+	sp = mp = r->nfa;
+
+	if (!pat || !*pat) {
+		if (sta)
+			return NULL;
+		else
+			badpat(RE_ERR_NOPREV);
+	}
+	sta = NOP;
+
+	for (p = pat; *p; p++) {
+		lp = mp;
+		switch(*p) {
+
+		case '.':               /* match any char..  */
+			store(ANY);
+			break;
+
+		case '^':               /* match beginning.. */
+			if (p == pat)
+				store(BOL);
+			else {
+				store(CHR);
+				store(re_tolower(*p));
+			}
+			break;
+
+		case '$':               /* match endofline.. */
+			if (!*(p+1))
+				store(EOL);
+			else {
+				store(CHR);
+				store(re_tolower(*p));
+			}
+			break;
+
+		case '[':               /* match char class..*/
+			store(CCL);
+
+			if (*++p == '^') {
+				mask = 0377;
+				p++;
+			}
+			else
+				mask = 0;
+
+			if (*p == '-')        /* real dash */
+				chset(r, *p++);
+			if (*p == ']')        /* real brac */
+				chset(r, *p++);
+			while (*p && *p != ']') {
+				if (*p == '-' && *(p+1) && *(p+1) != ']') {
+					p++;
+					c1 = *(p-2) + 1;
+					c2 = *p++;
+					while (c1 <= c2)
+						chset(r, re_tolower((CHAR)c1++));
+				}
+#if RE_EXTEND == 1
+				else if (*p == '\\') {
+					p++;
+					switch (*p) {
+						case 'n': chset(r, '\n'); break;
+						case 'r': chset(r, '\r'); break;
+						case 't': chset(r, '\t'); break;
+						case '\\': chset(r, '\\'); break;
+						case '\0': p--; chset(r, '\\'); break;
+						default:  chset(r, *p);
+					}
+					p++;
+				}
+#endif
+				else
+					chset(r, re_tolower(*p++));
+			}
+			if (!*p)
+				badpat(RE_ERR_NOCLCL);
+
+			for (n = 0; n < BITBLK; r->bittab[n++] = (char) 0)
+				store(mask ^ r->bittab[n]);
+	
+			break;
+
+		case '*':               /* match 0 or more.. */
+		case '+':               /* match 1 or more.. */
+			if (p == pat)
+				badpat(RE_ERR_EMPTYCLO);
+			lp = sp;              /* previous opcode */
+			if (*lp == CLO)       /* equivalence..   */
+				break;
+			switch(*lp) {
+
+			case BOL:
+			case BOT:
+			case EOT:
+			case BOW:
+			case EOW:
+			case REF:
+				badpat(RE_ERR_BADCLO);
+			default:
+				break;
+			}
+
+			if (*p == '+')
+				for (sp = mp; lp < sp; lp++)
+					store(*lp);
+
+			store(END);
+			store(END);
+			sp = mp;
+			while (--mp > lp)
+				*mp = mp[-1];
+			store(CLO);
+			mp = sp;
+			break;
+
+		case '\\':              /* tags, backrefs .. */
+			switch(*++p) {
+
+			case '(':
+				if (tagc < MAXTAG) {
+					tagstk[++tagi] = tagc;
+					store(BOT);
+					store(tagc++);
+				}
+				else
+					badpat(RE_ERR_STACK);
+				break;
+			case ')':
+				if (*sp == BOT)
+					badpat(RE_ERR_EMPTYTAG);
+				if (tagi > 0) {
+					store(EOT);
+					store(tagstk[tagi--]);
+				}
+				else
+					badpat(RE_ERR_UNMATCHTAGC);
+				break;
+			case '<':
+				store(BOW);
+				break;
+			case '>':
+				if (*sp == BOW)
+					badpat(RE_ERR_EMPTYWORD);
+				store(EOW);
+				break;
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				n = *p-'0';
+				if (tagi > 0 && tagstk[tagi] == n)
+					badpat(RE_ERR_CYCREF);
+				if (tagc > n) {
+					store(REF);
+					store(n);
+				}
+				else
+					badpat(RE_ERR_UNTERMREF);
+				break;
+#if RE_EXTEND == 1
+			case 'b':
+				store(CHR);
+				store('\b');
+				break;
+			case 'n':
+				store(CHR);
+				store('\n');
+				break;
+			case 'f':
+				store(CHR);
+				store('\f');
+				break;
+			case 'r':
+				store(CHR);
+				store('\r');
+				break;
+			case 't':
+				store(CHR);
+				store('\t');
+				break;
+#endif
+			default:
+				store(CHR);
+				store(re_tolower(*p));
+			}
+			break;
+
+		default :               /* an ordinary char  */
+			store(CHR);
+			store(re_tolower(*p));
+			break;
+		}
+		sp = lp;
+	}
+	if (tagi > 0)
+		badpat(RE_ERR_UNMATCHTAGO);
+	store(END);
+
+	return r;
+error:;
+	return r;
+}
+
+
+static const char *pmatch(RE_PRFX(t) *r, const char *, const char *, CHAR *, int *);
+
+#if RE_BIN_API == 1
+#define end(s)   ((s) >= lpend)
+#else
+#define end(s)   (*(s) == '\0')
+#endif
+/*
+ * re_exec:
+ * 	execute nfa to find a match.
+ *
+ *	special cases: (nfa[0])
+ *		BOL
+ *			Match only once, starting from the
+ *			beginning.
+ *		CHR
+ *			First locate the character without
+ *			calling pmatch, and if found, call
+ *			pmatch for the remaining string.
+ *		END
+ *			re_comp failed, poor luser did not
+ *			check for it. Fail fast.
+ *
+ *	If a match is found, bopat[0] and eopat[0] are set
+ *	to the beginning and the end of the matched fragment,
+ *	respectively.
+ *
+ */
+
+int RE_PRFX(exec)(RE_PRFX(t) *r, const char *lp SRCLEN)
+{
+	register CHAR c;
+	register const char *ep = 0;
+	register CHAR *ap = r->nfa;
+	int score = 1;
+#if RE_BIN_API == 1
+	const char *lpend = lp + srclen;
+#else
+	const char *lpend = NULL;
+#endif
+
+	r->bol = lp;
+
+	r->bopat[0] = 0;
+	r->bopat[1] = 0;
+	r->bopat[2] = 0;
+	r->bopat[3] = 0;
+	r->bopat[4] = 0;
+	r->bopat[5] = 0;
+	r->bopat[6] = 0;
+	r->bopat[7] = 0;
+	r->bopat[8] = 0;
+	r->bopat[9] = 0;
+	r->error = 0;
+	r->fail_op = 0;
+
+	switch(*ap) {
+
+	case BOL:                 /* anchored: match from BOL only */
+		ep = pmatch(r, lp, lpend, ap, &score);
+		break;
+	case CHR:                 /* ordinary char: locate it fast */
+		c = *(ap+1);
+		while (!end(lp) && re_tolower(*lp) != c)
+			lp++;
+		if (end(lp))            /* if EOS, fail, else fall thru. */
+			return 0;
+	default:                  /* regular matching all the way. */
+		do {
+			if ((ep = pmatch(r, lp, lpend, ap, &score)))
+				break;
+#if RE_BIN_API == 1
+		} while (!end(++lp));
+#else
+		} while (!end(lp++));
+#endif
+
+		break;
+	case END:                 /* munged automaton. fail always */
+		return 0;
+	}
+	if (!ep)
+		return 0;
+
+	r->bopat[0] = lp;
+	r->eopat[0] = ep;
+	return score;
+}
+
+/*
+ * pmatch: internal routine for the hard part
+ *
+ *	This code is partly snarfed from an early grep written by
+ *	David Conroy. The backref and tag stuff, and various other
+ *	innovations are by oz.
+ *
+ *	special case optimizations: (nfa[n], nfa[n+1])
+ *		CLO ANY
+ *			We KNOW .* will match everything upto the
+ *			end of line. Thus, directly go to the end of
+ *			line, without recursive pmatch calls. As in
+ *			the other closure cases, the remaining pattern
+ *			must be matched by moving backwards on the
+ *			string recursively, to find a match for xy
+ *			(x is ".*" and y is the remaining pattern)
+ *			where the match satisfies the LONGEST match for
+ *			x followed by a match for y.
+ *		CLO CHR
+ *			We can again scan the string forward for the
+ *			single char and at the point of failure, we
+ *			execute the remaining nfa recursively, same as
+ *			above.
+ *
+ *	At the end of a successful match, bopat[n] and eopat[n]
+ *	are set to the beginning and end of subpatterns matched
+ *	by tagged expressions (n = 1 to 9).
+ *
+ */
+
+/*
+ * character classification table for word boundary operators BOW
+ * and EOW. the reason for not using ctype macros is that we can
+ * let the user add into our own table. see re_modw. This table
+ * is not in the bitset form, since we may wish to extend it in the
+ * future for other character classifications. 
+ *
+ *	TRUE for 0-9 A-Z a-z _
+ */
+static CHAR chrtyp[MAXCHR] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+	0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 0, 0, 0, 0, 0
+};
+
+#define inascii(x)    (0177&(x))
+#define iswordc(x)    chrtyp[inascii(x)]
+#define isinset(x,y)  ((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
+
+/*
+ * skip values for CLO XXX to skip past the closure
+ */
+
+#define ANYSKIP 2   /* [CLO] ANY END ...         */
+#define CHRSKIP 3   /* [CLO] CHR chr END ...     */
+#define CCLSKIP 18  /* [CLO] CCL 16bytes END ... */
+
+static const char *pmatch(RE_PRFX(t) *r, const char *lp, const char *lpend, CHAR *ap, int *score)
+{
+	register int op, c, n;
+	register const char *e;       /* extra pointer for CLO */
+	register const char *bp;      /* beginning of subpat.. */
+	register const char *ep;      /* ending of subpat.. */
+	const char *are;              /* to save the line ptr. */
+
+	while ((op = *ap++) != END)
+		switch(op) {
+
+		case CHR:
+			if (re_tolower(*lp++) != *ap++)
+				return 0;
+			(*score) += 100;
+			break;
+		case ANY:
+			if (end(lp++))
+				return 0;
+			(*score)++;
+			break;
+		case CCL:
+			if (end(lp))
+				return 0;
+			c = re_tolower(*lp++);
+			if (!isinset(ap,c))
+				return 0;
+			ap += BITBLK;
+			(*score) += 2;
+			break;
+		case BOL:
+			if (lp != r->bol)
+				return 0;
+			(*score) += 10;
+			break;
+		case EOL:
+			if (!end(lp))
+				return 0;
+			(*score) += 10;
+			break;
+		case BOT:
+			r->bopat[*ap++] = lp;
+			break;
+		case EOT:
+			r->eopat[*ap++] = lp;
+			break;
+ 		case BOW:
+			if ((lp!=r->bol && iswordc(lp[-1])) || !iswordc(*lp))
+				return 0;
+			(*score) += 5;
+			break;
+		case EOW:
+			if (lp==r->bol || !iswordc(lp[-1]) || (!end(lp) && iswordc(*lp)))
+				return 0;
+			(*score) += 5;
+			break;
+		case REF:
+			n = *ap++;
+			bp = r->bopat[n];
+			ep = r->eopat[n];
+			while (bp < ep) {
+				if (*bp++ != *lp++)
+					return 0;
+			(*score) += 2;
+			}
+			break;
+		case CLO:
+			are = lp;
+			switch(*ap) {
+
+			case ANY:
+				while (!end(lp))
+					lp++;
+				n = ANYSKIP;
+				(*score)++;
+				break;
+			case CHR:
+				c = *(ap+1);
+				while (!end(lp) && c == re_tolower(*lp))
+					lp++;
+				n = CHRSKIP;
+				(*score) += 100;
+				break;
+			case CCL:
+				while ((c = re_tolower(*lp)) && isinset(ap+1,c))
+					lp++;
+				n = CCLSKIP;
+				(*score) += 2;
+				break;
+			default:
+				r->error = RE_ERR_CLOBADNFA;
+				r->fail_op = *ap;
+				return 0;
+			}
+
+			ap += n;
+
+			while (lp >= are) {
+				e = pmatch(r, lp, lpend, ap, score);
+				if (e)
+					return e;
+				--lp;
+			}
+			return 0;
+		default:
+			r->error = RE_ERR_BADNFA;
+			r->fail_op = op;
+			return 0;
+		}
+	return lp;
+}
+
+/*
+ * re_modw:
+ *	add new characters into the word table to change re_exec's
+ *	understanding of what a word should look like. Note that we
+ *	only accept additions into the word definition.
+ *
+ *	If the string parameter is 0 or null string, the table is
+ *	reset back to the default containing A-Z a-z 0-9 _. [We use
+ *	the compact bitset representation for the default table]
+ */
+
+static CHAR deftab[16] = {
+	0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207,
+	0376, 0377, 0377, 007 
+};
+
+void RE_PRFX(modw)(RE_PRFX(t) *r, char *s)
+{
+	register int i;
+
+	if (!s || !*s) {
+		for (i = 0; i < MAXCHR; i++)
+			if (!isinset(deftab,i))
+				iswordc(i) = 0;
+	}
+	else
+		while(*s)
+			iswordc(*s++) = 1;
+}
+
+
+static void dappend(RE_PRFX(t) *r,char c)
+{
+	grow(r, dst, 1);
+	r->dst[r->dst_used++] = c;
+}
+
+static void dappend_multi(RE_PRFX(t) *r, const char *s, int len)
+{
+	grow(r, dst, len);
+	memcpy(r->dst + r->dst_used, s, len);
+	r->dst_used += len;
+}
+
+/*
+ * re_subs:
+ *	substitute backreferences in src with the matched portions
+ *	result goes in a dynamic allocated dst.
+ *
+ *	\digit	substitute a subpattern, with the given	tag number.
+ *		Tags are numbered from 1 to 9. If the particular
+ *		tagged subpattern does not exist, null is substituted.
+ *
+ *	\0	substitute the entire matched pattern.
+ *
+ * returns length of dst or -1 on error. dst points to a buffer thayt
+ * is reused across subs() sessions.
+ */
+int RE_PRFX(backref)(RE_PRFX(t) *r, char **dst, const char *src SRCLEN)
+{
+	register char c;
+	register int  pin;
+	register const char *bp;
+	register const char *ep;
+#if RE_BIN_API == 1
+	const char *src_end = src + srclen;
+#endif
+
+
+	if (dst != NULL) {
+		*dst = 0;
+		r->dst_used = 0;
+	}
+
+	if (!*src || !r->bopat[0])
+		return 0;
+
+#if RE_BIN_API == 1
+	while (src < src_end) {
+		c = *src++;
+#else
+	while ((c = *src++)) {
+#endif
+		switch(c) {
+
+		case '\\':
+			c = *src++;
+			if (c >= '0' && c <= '9') {
+				pin = c - '0';
+				break;
+			}
+			
+		default:
+			dappend(r, c);
+			continue;
+		}
+
+		if ((bp = r->bopat[pin]) && (ep = r->eopat[pin])) {
+			while (*bp && bp < ep)
+				dappend(r, *bp++);
+			if (bp < ep)
+				return -1;
+		}
+	}
+
+	if (dst != NULL) {
+#if RE_BIN_API != 1
+		dappend(r, '\0');
+#endif
+		*dst = r->dst;
+	}
+	return r->dst_used;
+}
+
+
+int RE_PRFX(subst)(RE_PRFX(t) *r, char **dst, const char *src SRCLEN, const char *pat PATLEN, int first_only)
+{
+	const char *s, *s_end, *s_rest; /* src pointers */
+	int cnt;
+
+	s_rest = s = src;
+
+#if RE_BIN_API == 1
+	s_end = s + srclen;
+#else
+	for(s_end = s; *s_end != '\0'; s_end++) ;
+#endif
+
+	cnt = 0;
+	r->dst_used = 0;
+
+	for(;;) {
+		/* find next match */
+		if (RE_PRFX(exec)(r, s BIN_OR_EMPTY(s_end - s))) {
+			cnt++;
+			s_rest = r->eopat[0];
+			/* append source until the match starts */
+			dappend_multi(r, s, r->bopat[0] - s);
+			/* append substituted part */
+			RE_PRFX(backref)(r, NULL, pat PATLEN_);
+
+			if (first_only)
+				goto last;
+			s = r->eopat[0];
+		}
+		else {
+			/* no more matches, copy the rest of the string */
+			goto last;
+		}
+	}
+
+
+last:;
+	/* append the rest */
+		if (s_end - s_rest > 0)
+			dappend_multi(r, s_rest, s_end - s_rest);
+#if RE_BIN_API != 1
+		dappend(r, '\0');
+#endif
+		*dst = r->dst;
+		return r->dst_used;
+}
+
+
+void RE_PRFX(free)(RE_PRFX(t) *re)
+{
+	if (re->dst != NULL)
+		free(re->dst);
+	if (re->nfa != NULL)
+		free(re->nfa);
+	free(re);
+}
+
+
+re_error_t RE_PRFX(errno)(RE_PRFX(t) *re)
+{
+	return re->error;
+}
+
+int RE_PRFX(has_error)(RE_PRFX(t) *re)
+{
+	return re->error != 0;
+}
+
+#define setout(dest, val) \
+	if ((dest) != NULL) \
+		*(dest) = val;
+
+int RE_PRFX(tag)(RE_PRFX(t) *re, int tagid, char **begin, char **end)
+{
+	if ((tagid < 0) || (tagid > MAXTAG)) {
+		setout(begin, NULL);
+		setout(end, NULL);
+		return -1;
+	}
+
+	setout(begin, (char *)re->bopat[tagid]);
+	setout(end, (char *)re->eopat[tagid]);
+	return 0;
+}
+
diff --git a/src_3rd/genregex/regex_templ.h b/src_3rd/genregex/regex_templ.h
new file mode 100644
index 0000000..b7bb384
--- /dev/null
+++ b/src_3rd/genregex/regex_templ.h
@@ -0,0 +1,24 @@
+#include "regex.h"
+
+#if RE_BIN_API == 1
+#define SRCLEN , int srclen
+#define PATLEN , int patlen
+#else
+#define SRCLEN
+#define PATLEN
+#endif
+
+typedef struct RE_PRFX(s) RE_PRFX(t);
+
+RE_PRFX(t) *RE_PRFX(comp)(const char *pat);
+extern int RE_PRFX(exec)(RE_PRFX(t) *re, const char *str SRCLEN);
+extern void RE_PRFX(modw)(RE_PRFX(t) *re, char *wpat);
+extern int RE_PRFX(backref)(RE_PRFX(t) *re, char **dst, const char *src SRCLEN);
+extern int RE_PRFX(subst)(RE_PRFX(t) *r, char **dst, const char *src SRCLEN, const char *pat PATLEN, int first_only);
+extern int RE_PRFX(tag)(RE_PRFX(t) *re, int tagid, char **begin, char **end);
+extern void RE_PRFX(free)(RE_PRFX(t) *re);
+extern re_error_t RE_PRFX(errno)(RE_PRFX(t) *re);
+extern int RE_PRFX(has_error)(RE_PRFX(t) *re);
+
+#undef SRCLEN
+#undef PATLEN
diff --git a/src_3rd/genregex/tester.c b/src_3rd/genregex/tester.c
new file mode 100644
index 0000000..2bfa2ce
--- /dev/null
+++ b/src_3rd/genregex/tester.c
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+void strip(char *s)
+{
+	char *end;
+	
+	end = s + strlen(s) - 1;
+	while((end >= s) && ((*end == '\n') || (*end == '\r'))) {
+		*end = '\0';
+		end--;
+	}
+}
+
+int backref = 0;
+
+/* -- string -- */
+#include "regex_se.h"
+#define PRFX(name) re_se_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+#include "regex_sei.h"
+#define PRFX(name) re_sei_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+#include "regex_st.h"
+#define PRFX(name) re_st_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+#include "regex_sti.h"
+#define PRFX(name) re_sti_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+/* -- binary -- */
+#define BIN_API
+
+#include "regex_be.h"
+#define PRFX(name) re_be_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+#include "regex_bei.h"
+#define PRFX(name) re_bei_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+#include "regex_bt.h"
+#define PRFX(name) re_bt_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+#include "regex_bti.h"
+#define PRFX(name) re_bti_ ## name
+#include "tester_main.c"
+#undef PRFX
+
+int main(int argc, char *argv[])
+{
+	if (argc < 2) {
+		fprintf(stderr, "Need an argument: regex type: se, sei, be, bei, st, sti, bt or bti\n");
+		exit(1);
+	}
+	if ((argv[2] != NULL) && (*argv[2] == 'b'))
+		backref=1;
+	if (strcmp(argv[1], "se") == 0)   return re_se_main();
+	if (strcmp(argv[1], "sei") == 0)  return re_sei_main();
+	if (strcmp(argv[1], "be") == 0)   return re_be_main();
+	if (strcmp(argv[1], "bei") == 0)  return re_bei_main();
+	if (strcmp(argv[1], "st") == 0)   return re_st_main();
+	if (strcmp(argv[1], "sti") == 0)  return re_sti_main();
+	if (strcmp(argv[1], "bt") == 0)   return re_bt_main();
+	if (strcmp(argv[1], "bti") == 0)  return re_bti_main();
+	fprintf(stderr, "Unknown regex type %s\n", argv[1]);
+	return 1;
+}
diff --git a/src_3rd/genregex/tester_main.c b/src_3rd/genregex/tester_main.c
new file mode 100644
index 0000000..3b65288
--- /dev/null
+++ b/src_3rd/genregex/tester_main.c
@@ -0,0 +1,81 @@
+#ifdef BIN_API
+#define LINELEN ,linelen
+#define SUBSLEN ,subslen
+#else
+#define LINELEN
+#define SUBSLEN
+#endif
+
+int PRFX(main)()
+{
+	char line[1024];
+	char subs[1024];
+	int subslen;
+	int linelen;
+	PRFX(t) *r;
+
+/* input header is match and subst in the first 2 lines */
+	fgets(line, sizeof(line), stdin);
+	strip(line);
+	fgets(subs, sizeof(subs), stdin);
+
+	/* compile regex */
+	r = PRFX(comp)(line);
+	if (PRFX(has_error)(r)) {
+		/* use stdout instead of stderr: error message should be compared to the reference */
+		printf("*** error: [%d] '%s'\n", PRFX(errno)(r), re_error_str(PRFX(errno)(r)));
+		PRFX(free)(r);
+		return 0;
+	}
+
+	/* clean up subs */
+	strip(subs);
+	if ((*subs == '\n') || (*subs == '\r'))
+		*subs = '\0';
+	subslen = strlen(subs);
+
+	while(!(feof(stdin))) {
+		*line = '\0';
+		fgets(line, sizeof(line), stdin);
+		strip(line);
+		linelen=strlen(line);
+#ifdef BIN_API
+	line[linelen]='1';
+#endif
+		if ((PRFX(exec)(r, line LINELEN)) || (*subs != '\0')) {
+			if (*subs != '\0') {
+				char *dst;
+				int len;
+				if (backref)
+					len = PRFX(backref)(r, &dst, subs SUBSLEN);
+				else
+					len = PRFX(subst)(r, &dst, line LINELEN, subs SUBSLEN, 0);
+				if (dst == NULL) {
+					dst = "<null>";
+					len = 6;
+				}
+#ifdef BIN_API
+				{
+					char copy[8192];
+					assert(len < sizeof(copy) - 1);
+					memcpy(copy, dst, len);
+					copy[len] = '\0';
+					printf("%s\n", copy);
+				}
+#else
+				printf("%s\n", dst);
+#endif
+			}
+			else {
+				/* no subs, just match; remove binary watchdog char from the end */
+				line[linelen] = '\0';
+				printf("%s\n", line);
+			}
+		}
+	}
+
+	PRFX(free)(r);
+	return 0;
+}
+#undef LINELEN
+#undef SUBSLEN
diff --git a/src_3rd/gensexpr/Makefile b/src_3rd/gensexpr/Makefile
new file mode 100644
index 0000000..1ee8227
--- /dev/null
+++ b/src_3rd/gensexpr/Makefile
@@ -0,0 +1,23 @@
+CFLAGS = -I..
+CFLAGS += -Wall -g -ansi -pedantic
+
+OBJS = gsxl.o gsxnl.o gsx_parse.o
+COMMON_DEPS = gensexpr_impl.c gensexpr_impl.h gsx_parse.h
+
+all: gensexpr.a
+
+gensexpr.a: $(OBJS)
+	ar rv gensexpr.a $(OBJS)
+
+gsxl.o: gsxl.c gsxl.h $(COMMON_DEPS)
+	$(CC) -c $(CFLAGS) gsxl.c -o gsxl.o
+
+gsxnl.o: gsxnl.c gsxnl.h $(COMMON_DEPS)
+	$(CC) -c $(CFLAGS) gsxnl.c -o gsxnl.o
+
+gsx_parse.o: gsx_parse.c gsx_parse.h
+	$(CC) -c $(CFLAGS) gsx_parse.c -o gsx_parse.o
+
+clean:
+	-rm $(OBJS) gensexpr.a
+
diff --git a/src_3rd/gensexpr/gensexpr_impl.c b/src_3rd/gensexpr/gensexpr_impl.c
new file mode 100644
index 0000000..8228d88
--- /dev/null
+++ b/src_3rd/gensexpr/gensexpr_impl.c
@@ -0,0 +1,312 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: gensexpr {at} igor2.repo.hu
+Project page: http://repo.hu/projects/gensexpr
+*/
+
+#include <string.h>
+
+static void *gsx_dummy_malloc(GSX(dom_t) *dom, size_t size)
+{
+	return malloc(size);
+}
+
+static void gsx_dummy_free(GSX(dom_t) *dom, void *data)
+{
+	free(data);
+}
+
+#define alloc_node(ctx) ctx->malloc(ctx, ctx->node_size)
+
+/* Abuse the str field for storing the last children for fast append */
+#define last(nd) (nd)->str
+
+static char *gsx_strdup(const char *s)
+{
+	int len = strlen(s)+1;
+	char *res;
+	res = malloc(len);
+	memcpy(res, s, len);
+	return res;
+}
+
+#ifdef GENSEXPR_WANT_LOC
+static void setloc(GSX(node_t) *nd, gsx_parse_t *parse) {
+	nd->offs = parse->offs;
+	nd->line = parse->line;
+	nd->col = parse->col;
+}
+#else
+static void setloc(GSX(node_t) *nd, gsx_parse_t *parse) { }
+#endif
+
+static void GSX(parser_ev)(gsx_parse_t *pctx, gsx_parse_event_t ev, const char *data)
+{
+	GSX(dom_t) *ctx = (GSX(dom_t) *)pctx->user_ctx;
+	GSX(node_t) *nd;
+
+	switch(ev) {
+		case GSX_EV_OPEN:
+			if (ctx->root == NULL) {
+				ctx->root = ctx->parse_current = alloc_node(ctx);
+				memset(ctx->root, 0, sizeof(GSX(node_t)));
+				setloc(ctx->root, &ctx->parse);
+			}
+			else {
+				nd = alloc_node(ctx);
+				memset(nd, 0, sizeof(GSX(node_t)));
+				setloc(nd, &ctx->parse);
+				nd->parent = ctx->parse_current;
+				ctx->parse_current = nd;
+				goto append;
+			}
+			break;
+		case GSX_EV_CLOSE:
+			last(ctx->parse_current) = NULL;
+			ctx->parse_current = ctx->parse_current->parent;
+			break;
+
+		case GSX_EV_ATOM:
+			nd = alloc_node(ctx);
+			memset(nd, 0, sizeof(GSX(node_t)));
+			setloc(nd, &ctx->parse);
+			nd->parent = ctx->parse_current;
+			nd->str = gsx_strdup(data);
+
+			append:;
+			/* append at parent - single linked list with a pointer to the end (last()) */
+			if (nd->parent == NULL)
+				break;
+			if (nd->parent->children != NULL)
+				((GSX(node_t *))(last(nd->parent)))->next = nd;
+			else
+				nd->parent->children = nd;
+			last(nd->parent) = (char *)nd;
+			break;
+
+		case GSX_EV_ERROR:
+			if (ctx->parse_current != NULL)
+				last(ctx->parse_current) = NULL;
+			break;
+	}
+}
+
+void GSX(init_)(GSX(dom_t) *dom, size_t node_size)
+{
+	dom->root = NULL;
+	dom->node_size = node_size;
+	dom->malloc = gsx_dummy_malloc;
+	dom->free = gsx_dummy_free;
+
+	dom->dump_allow_dq = 1;
+	dom->dump_allow_sq = 0;
+
+	gsx_parse_init(&dom->parse);
+	dom->parse.user_ctx = dom;
+	dom->parse.cb = GSX(parser_ev);
+	dom->parse_current = NULL;
+}
+
+GSX(dom_t) *GSX(alloc_)(size_t node_size)
+{
+	GSX(dom_t) *dom = malloc(sizeof(GSX(dom_t)));
+	if (dom == NULL)
+		return NULL;
+	GSX(init_)(dom, node_size);
+	return dom;
+}
+
+void GSX(node_free)(GSX(dom_t) *dom, GSX(node_t) *node)
+{
+	GSX(node_t) *n, *next;
+	for(n = node->children; n != NULL; n = next) {
+		next = n->next;
+		GSX(node_free)(dom, n);
+	}
+	dom->free(dom, node->str);
+	dom->free(dom, node);
+}
+
+void GSX(uninit)(GSX(dom_t) *dom)
+{
+	if (dom->root != NULL)
+		GSX(node_free)(dom, dom->root);
+	gsx_parse_uninit(&dom->parse);
+}
+
+void GSX(free)(GSX(dom_t) *dom)
+{
+	GSX(uninit)(dom);
+	free(dom);
+}
+
+gsx_parse_res_t GSX(parse_char)(GSX(dom_t) *dom, int chr)
+{
+	return gsx_parse_char(&dom->parse, chr);
+}
+
+void GSX(dump_subtree)(GSX(dom_t) *dom, GSX(node_t) *node, void (*write)(void *wctx, const char *str), void *wctx)
+{
+	int compact = (node->str != NULL) && (node->children != NULL);
+
+	/* Special case: empty terminal node */
+	if ((node->str == NULL) && (node->children == NULL)) {
+		write(wctx, "()");
+		return;
+	}
+
+	if (compact)
+		write(wctx, "(");
+
+	if (node->str != NULL) {
+		int has_sq = 0, has_dq = 0, has_spec = 0;
+		char *s;
+
+		for(s = node->str; *s != '\0'; s++) {
+			switch(*s) {
+				case '\'': has_sq = 1; continue;
+				case '"':  has_dq = 1; continue;
+				case ')':  has_spec = 1; continue;
+			}
+			if ((*s > ' ') && (*s < 127))
+				continue;
+			has_spec = 1;
+		}
+
+		if (!has_sq && !has_dq && !has_spec) /* safe */
+			write(wctx, node->str);
+		else if ((!has_dq) && (dom->dump_allow_dq)) { /* try double quote */
+			write(wctx, "\"");
+			write(wctx, node->str);
+			write(wctx, "\"");
+		}
+		else if ((!has_sq) && (dom->dump_allow_sq)) { /* try double quote */
+			write(wctx, "\'");
+			write(wctx, node->str);
+			write(wctx, "\'");
+		}
+		else { /* all fail - have to use quote and escaping */
+			char qc[2], tmp;
+			char *s, *from;
+			if (dom->dump_allow_dq) *qc = '"';
+			else if (dom->dump_allow_sq) *qc = '\'';
+			else *qc = 0;
+			
+			if (*qc != 0) {
+				qc[1] = '\0';
+				write(wctx, qc);
+			}
+			for(from = s = node->str; *s != '\0'; s++) {
+				if ((*s == *qc) || (*s < ' ') || (*s == '\\') || (*s >= 127)) {
+					if (s > from) {
+						tmp = *s;
+						*s = '\0';
+						write(wctx, from);
+						*s = tmp;
+					}
+					write(wctx, "\\");
+					from = s;
+					s++;
+				}
+			}
+			if (s > from)
+				write(wctx, from);
+			if (*qc != 0)
+				write(wctx, qc);
+		}
+	}
+
+	if (node->children != NULL) {
+		GSX(node_t) *n;
+		int i;
+
+		if (!compact)
+			write(wctx, "(");
+		for(i = compact, n = node->children; n != NULL; i++, n = n->next) {
+			if (i != 0)
+				write(wctx, " ");
+			GSX(dump_subtree)(dom, n, write, wctx);
+		}
+		if (!compact)
+			write(wctx, ")");
+	}
+
+	if (compact)
+		write(wctx, ")");
+
+}
+
+void GSX(dump_tree)(GSX(dom_t) *dom, void (*write)(void *wctx, const char *str), void *wctx)
+{
+	if (dom->root != NULL)
+		GSX(dump_subtree)(dom, dom->root, write, wctx);
+}
+
+void GSX(compact_subtree)(GSX(dom_t) *dom, GSX(node_t) *nd)
+{
+	GSX(node_t) *old_ch, *n;
+
+	for(n = nd->children; n != NULL; n = n->next)
+		GSX(compact_subtree)(dom, n);
+
+	if ((nd->str == NULL) && (nd->children != NULL) && (nd->children->str != NULL)) {
+		old_ch = nd->children;
+		nd->str = nd->children->str;
+		nd->children = old_ch->next;
+		for(n = old_ch->next; n != NULL; n = n->next)
+			n->parent = nd;
+		dom->free(dom, old_ch);
+	}
+}
+
+void GSX(compact_tree)(GSX(dom_t) *dom)
+{
+	if (dom->root != NULL)
+		GSX(compact_subtree)(dom, dom->root);
+}
+
+GSX(node_t) *GSX(nth)(GSX(node_t) *node, int idx)
+{
+	if ((idx == 0) && (node->str != NULL)) /* compact tree has 0th arg in node */
+		return node;
+
+	node = node->children;
+
+	if (node->str != NULL) /* compact tree has 0th arg in node */
+		idx--;
+
+	while(idx > 0) {
+		if (node == NULL)
+			return NULL;
+		idx--;
+		node = node->next;
+	}
+
+	return node;
+}
diff --git a/src_3rd/gensexpr/gensexpr_impl.h b/src_3rd/gensexpr/gensexpr_impl.h
new file mode 100644
index 0000000..a41ee52
--- /dev/null
+++ b/src_3rd/gensexpr/gensexpr_impl.h
@@ -0,0 +1,58 @@
+#ifndef GENSEXPR_H
+#define GENSEXPR_H
+
+#include <stdlib.h>
+
+#include <gensexpr/gsx_parse.h>
+
+typedef struct GSX(node_s) GSX(node_t);
+typedef struct GSX(dom_s)  GSX(dom_t);
+
+struct GSX(node_s) {
+	char *str;
+	GSX(node_t) *parent, *children, *next;
+#ifdef GENSEXPR_WANT_LOC
+	size_t offs, line, col;
+#endif
+};
+
+struct GSX(dom_s) {
+	GSX(node_t) *root;
+	size_t node_size; /* including gsx_node_t and user data */
+
+	/* dump configuration */
+	unsigned dump_allow_sq:1; /* whether to allow using single quote */
+	unsigned dump_allow_dq:1; /* whether to allow using single quote */
+
+	/* optional custom allocator */
+	void *(*malloc)(GSX(dom_t) *dom, size_t size);
+	void (*free)(GSX(dom_t) *dom, void *data);
+
+	void *user_ctx;     /* optional: used by the caller */
+
+	/* Internal */
+	gsx_parse_t parse;
+	GSX(node_t) *parse_current;
+};
+
+/* Note: the init/alloc macros are in the implementation headers */
+void GSX(uninit)(GSX(dom_t) *dom);
+void GSX(free)(GSX(dom_t) *dom);
+
+gsx_parse_res_t GSX(parse_char)(GSX(dom_t) *dom, int chr);
+
+void GSX(dump_subtree)(GSX(dom_t) *dom, GSX(node_t) *node, void (*write)(void *wctx, const char *str), void *wctx);
+void GSX(dump_tree)(GSX(dom_t) *dom, void (*write)(void *wctx, const char *str), void *wctx);
+
+void GSX(node_free)(GSX(dom_t) *dom, GSX(node_t) *node);
+
+void GSX(compact_subtree)(GSX(dom_t) *dom, GSX(node_t) *nd);
+void GSX(compact_tree)(GSX(dom_t) *dom);
+
+GSX(node_t) *GSX(nth)(GSX(node_t) *node, int idx);
+
+/* low level / internal use */
+void GSX(init_)(GSX(dom_t) *dom, size_t node_size);
+GSX(dom_t) *GSX(alloc_)(size_t node_size);
+
+#endif
diff --git a/src_3rd/gensexpr/gsx_parse.c b/src_3rd/gensexpr/gsx_parse.c
new file mode 100644
index 0000000..69aa187
--- /dev/null
+++ b/src_3rd/gensexpr/gsx_parse.c
@@ -0,0 +1,233 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: gensexpr {at} igor2.repo.hu
+Project page: http://repo.hu/projects/gensexpr
+*/
+
+/* Event parser */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "gsx_parse.h"
+
+enum {
+	ST_NORMAL,
+	ST_ATOM, /* unquoted atom */
+	ST_DQ,   /* in double quote */
+	ST_SQ,   /* in single quote */
+	ST_DQBS, /* in double quote, previous char was a backslash */
+	ST_SQBS, /* in single quote, previous char was a backslash */
+	ST_QEND, /* quote end - need to be followed by a whitepsace or ) */
+	ST_ERR,  /* error condition (stopped parsing) */
+	ST_EOE   /* end-of-file condition (stopped parsing) */
+};
+
+static void update_loc(gsx_parse_t *ctx, int chr)
+{
+	ctx->offs++;
+	if (chr == '\n') {
+		ctx->col = 0;
+		ctx->line++;
+	}
+	else
+		ctx->col++;
+}
+
+#define error(ctx, chr, errmsg) \
+do { \
+	ctx->cb(ctx, GSX_EV_ERROR, errmsg); \
+	ctx->pstate = ST_ERR; \
+	update_loc(ctx, chr); \
+	return GSX_RES_ERROR; \
+} while(0)
+
+static void flush_atom(gsx_parse_t *ctx)
+{
+	ctx->atom[ctx->used] = '\0';
+	ctx->cb(ctx, GSX_EV_ATOM, ctx->atom);
+	ctx->used = 0;
+}
+
+int gsx_parse_char(gsx_parse_t *ctx, int chr)
+{
+	int qc, stbs;
+
+	if (ctx->pstate == ST_ERR) {
+		update_loc(ctx, chr);
+		return GSX_RES_ERROR;
+	}
+
+	if (ctx->pstate == ST_EOE) {
+		update_loc(ctx, chr);
+		return GSX_RES_EOE;
+	}
+
+	if (chr == EOF) /* we are not in ST_EOE if we got here */
+			error(ctx, chr, "premature end of expression");
+
+	switch(ctx->pstate) {
+		case ST_DQBS:
+			ctx->pstate = ST_DQ;
+			goto append;
+
+		case ST_SQBS:
+			ctx->pstate = ST_SQ;
+			goto append;
+
+		case ST_DQ:
+			qc = '"';
+			stbs = ST_DQBS;
+			goto quote;
+
+		case ST_SQ:
+			qc = '\'';
+			stbs = ST_SQBS;
+			quote:;
+			if (chr == qc)
+				ctx->pstate = ST_QEND;
+			else if (chr == '\\')
+				ctx->pstate = stbs;
+			else
+				goto append;
+			break;
+
+		case ST_QEND:
+			if (isspace(chr)) {
+				if (ctx->depth == 0)
+					goto err_atom0;
+				ctx->pstate = ST_NORMAL;
+				flush_atom(ctx);
+				break;
+			}
+			if (chr == ')') {
+				if (ctx->depth == 0)
+					goto err_atom0;
+				flush_atom(ctx);
+				goto close;
+			}
+			error(ctx, chr, "quoted atom ends in non-quote-char");
+
+		case ST_ATOM:
+			if (isspace(chr)) {
+				if (ctx->depth == 0)
+					goto err_atom0;
+				ctx->pstate = ST_NORMAL;
+				flush_atom(ctx);
+				break;
+			}
+			if (chr == ')') {
+				if (ctx->depth == 0)
+					goto err_atom0;
+				flush_atom(ctx);
+				goto close;
+			}
+			if ((chr == '(') || (chr == '"') || (chr == '\''))
+				error(ctx, chr, "unquoted atom with special character in it");
+			goto append;
+
+		case ST_NORMAL:
+			if (isspace(chr))
+				break; /* excess whitepsace between atoms */
+
+			switch(chr) {
+				case '"':
+					ctx->pstate = ST_DQ;
+					break;
+
+				case '\'':
+					ctx->pstate = ST_SQ;
+					break;
+
+				case '(':
+					ctx->cb(ctx, GSX_EV_OPEN, "(");
+					ctx->depth++;
+					break;
+
+				case ')':
+					close:;
+					ctx->depth--;
+					ctx->cb(ctx, GSX_EV_CLOSE, ")");
+					if (ctx->depth == 0) {
+						ctx->pstate = ST_EOE;
+						update_loc(ctx, chr);
+						return GSX_RES_EOE;
+					}
+					ctx->pstate = ST_NORMAL;
+					break;
+
+				default: /* normal character - start of a new, unquoted atom */
+					ctx->pstate = ST_ATOM;
+					goto append;
+			}
+			break;
+			
+	}
+
+	update_loc(ctx, chr);
+	return GSX_RES_NEXT;
+
+	append:;
+	if (ctx->used >= ctx->alloced) {
+		/* growth strategy */
+		if (ctx->alloced < 1024)
+			ctx->alloced += 128;
+		else if (ctx->alloced > 1024*1024)
+			ctx->alloced += 1024*1024;
+		else
+			ctx->alloced *= 2;
+
+		/* realloc, make sure there's 1 byte of extra room for the \0 */
+		ctx->atom = realloc(ctx->atom, ctx->alloced+1);
+	}
+	ctx->atom[ctx->used] = chr;
+	ctx->used++;
+	update_loc(ctx, chr);
+	return GSX_RES_NEXT;
+
+	err_atom0:
+	error(ctx, ' ', "Expression not wrapped in ()");
+}
+
+
+void gsx_parse_init(gsx_parse_t *ctx)
+{
+	ctx->offs = ctx->line = ctx->col = ctx->depth = 0;
+
+	ctx->atom = NULL;
+	ctx->used = ctx->alloced = 0;
+	ctx->pstate = ST_NORMAL;
+}
+
+void gsx_parse_uninit(gsx_parse_t *ctx)
+{
+	free(ctx->atom);
+	ctx->atom = NULL;
+	ctx->used = ctx->alloced = 0;
+}
diff --git a/src_3rd/gensexpr/gsx_parse.h b/src_3rd/gensexpr/gsx_parse.h
new file mode 100644
index 0000000..b7bc477
--- /dev/null
+++ b/src_3rd/gensexpr/gsx_parse.h
@@ -0,0 +1,32 @@
+typedef enum gsx_parse_event_e {
+	GSX_EV_OPEN,
+	GSX_EV_CLOSE,
+	GSX_EV_ATOM,
+	GSX_EV_ERROR
+} gsx_parse_event_t;
+
+typedef struct gsx_parse_s gsx_parse_t;
+
+struct gsx_parse_s {
+	void (*cb)(gsx_parse_t *ctx, gsx_parse_event_t ev, const char *data);
+	void *user_ctx; /* filled in by the caller */
+
+	/* location */
+	size_t offs, line, col, depth;
+
+	/* internal states */
+	char *atom;
+	int used, alloced;
+	unsigned char pstate; /* parser state */
+
+};
+
+typedef enum gsx_parse_res_e {
+	GSX_RES_NEXT,  /* can insert next character of the stream */
+	GSX_RES_ERROR, /* parse error, can't proceed, don't read more */
+	GSX_RES_EOE    /* end of expression, don't read more */
+} gsx_parse_res_t;
+
+void gsx_parse_init(gsx_parse_t *ctx);
+void gsx_parse_uninit(gsx_parse_t *ctx);
+int gsx_parse_char(gsx_parse_t *ctx, int chr);
diff --git a/src_3rd/gensexpr/gsxl.c b/src_3rd/gensexpr/gsxl.c
new file mode 100644
index 0000000..b561479
--- /dev/null
+++ b/src_3rd/gensexpr/gsxl.c
@@ -0,0 +1,7 @@
+#include <gensexpr/gsxl.h>
+
+#define GSX(x) gsxl_ ## x
+#define GENSEXPR_WANT_LOC
+
+#include <gensexpr/gensexpr_impl.c>
+
diff --git a/src_3rd/gensexpr/gsxl.h b/src_3rd/gensexpr/gsxl.h
new file mode 100644
index 0000000..adda8e8
--- /dev/null
+++ b/src_3rd/gensexpr/gsxl.h
@@ -0,0 +1,26 @@
+#ifndef GENSEXPR_LOC_H
+#define GENSEXPR_LOC_H
+
+#define GSX(x) gsxl_ ## x
+#define GENSEXPR_WANT_LOC
+
+#include <gensexpr/gensexpr_impl.h>
+
+/* Initialize a gsx*_dom_t *dom with an user struct type; the first field of
+   the user struct must be gsx*_node_t */
+#define gsxl_init(dom, struct_type) gsxl_init_(dom, sizeof(struct_type))
+
+/* Allocate and initialize a dom with an user struct type; the first field of
+   the user struct must be gsx_node_t. Evaluates to a (gsx_dom_t *); uses
+   malloc() for the allocation */
+#define gsxl_alloc(dom, struct_type) gsxl_alloc_(sizeof(struct_type))
+
+#define gsxl_children(node)   ((void *)(((gsxl_node_t *)(node))->children))
+#define gsxl_parent(node)     ((void *)(((gsxl_node_t *)(node))->parent))
+#define gsxl_next(node)       ((void *)(((gsxl_node_t *)(node))->next))
+
+
+#undef GSX
+#undef GENSEXPR_WANT_LOC
+
+#endif
diff --git a/src_3rd/gensexpr/gsxnl.c b/src_3rd/gensexpr/gsxnl.c
new file mode 100644
index 0000000..4d368e8
--- /dev/null
+++ b/src_3rd/gensexpr/gsxnl.c
@@ -0,0 +1,6 @@
+#include <gensexpr/gsxnl.h>
+
+#define GSX(x) gsxnl_ ## x
+#undef GENSEXPR_WANT_LOC
+
+#include <gensexpr/gensexpr_impl.c>
diff --git a/src_3rd/gensexpr/gsxnl.h b/src_3rd/gensexpr/gsxnl.h
new file mode 100644
index 0000000..77e1a38
--- /dev/null
+++ b/src_3rd/gensexpr/gsxnl.h
@@ -0,0 +1,24 @@
+#ifndef GENSEXPR_NOLOC_H
+#define GENSEXPR_NOLOC_H
+
+#define GSX(x) gsxnl_ ## x
+#undef GENSEXPR_WANT_LOC
+
+#include <gensexpr/gensexpr_impl.h>
+
+/* Initialize a gsx*_dom_t *dom with an user struct type; the first field of
+   the user struct must be gsx*_node_t */
+#define gsxnl_init(dom, struct_type) gsxnl_init_(dom, sizeof(struct_type))
+
+/* Allocate and initialize a dom with an user struct type; the first field of
+   the user struct must be gsx_node_t. Evaluates to a (gsx_dom_t *); uses
+   malloc() for the allocation */
+#define gsxnl_alloc(dom, struct_type) gsxnl_alloc_(sizeof(struct_type))
+
+#define gsxnl_children(node)   ((void *)(((gsxnl_node_t *)(node))->children))
+#define gsxnl_parent(node)     ((void *)(((gsxnl_node_t *)(node))->parent))
+#define gsxnl_next(node)       ((void *)(((gsxnl_node_t *)(node))->next))
+
+#undef GSX
+
+#endif
diff --git a/src_3rd/genvector/Makefile b/src_3rd/genvector/Makefile
new file mode 100644
index 0000000..e77fc2e
--- /dev/null
+++ b/src_3rd/genvector/Makefile
@@ -0,0 +1,53 @@
+#CFLAGS = -I.. -O3 -Wall -ansi -pedantic
+CFLAGS = -I.. -g
+OBJS = gds_char.o gds_wchar.o vti0.o vts0.o vtp0.o vtii.o
+IMPL = genvector_impl.c genvector_impl.h genvector_undef.h
+
+all: genvector.a
+
+genvector.a: $(OBJS)
+	ar ur genvector.a $(OBJS)
+
+gds_char.o: gds_char.c gds_char.h $(IMPL)
+	$(CC) -c $(CFLAGS) $*.c -o $@
+
+gds_wchar.o: gds_wchar.c gds_wchar.h $(IMPL)
+	$(CC) -c $(CFLAGS) $*.c -o $@
+
+vti0.o: vti0.c vti0.h $(IMPL)
+	$(CC) -c $(CFLAGS) $*.c -o $@
+
+vts0.o: vts0.c vts0.h $(IMPL)
+	$(CC) -c $(CFLAGS) $*.c -o $@
+
+vtp0.o: vtp0.c vtp0.h $(IMPL)
+	$(CC) -c $(CFLAGS) $*.c -o $@
+
+vtii.o: vtii.c vtii.h $(IMPL)
+	$(CC) -c $(CFLAGS) $*.c -o $@
+
+clean:
+	rm $(OBJS) genvector.a 2>/dev/null ; true
+
+include ../Makefile.conf
+
+install_:
+	mkdir -p "$(INCDIR)" "$(LIBDIR)"
+	$(CP) "$(PWD)/gds_char.h" "$(INCDIR)/gds_char.h"
+	$(CP) "$(PWD)/gds_wchar.h" "$(INCDIR)/gds_wchar.h"
+	$(CP) "$(PWD)/genvector_impl.h" "$(INCDIR)/genvector_impl.h"
+	$(CP) "$(PWD)/genvector_undef.h" "$(INCDIR)/genvector_undef.h"
+	$(CP) "$(PWD)/vti0.h" "$(INCDIR)/vti0.h"
+	$(CP) "$(PWD)/vtii.h" "$(INCDIR)/vtii.h"
+	$(CP) "$(PWD)/genvector.a" "$(LIBDIR)/genvector.a"
+
+uninstall:
+	rm $(LIBDIR)/genvector.a
+	rm $(INCDIR)/gds_*.h $(INCDIR)/genvector*.h $(INCDIR)/vt*.h
+
+install:
+	make install_ CP="cp" PWD=`pwd`
+
+linstall:
+	make install_ CP="ln -s" PWD=`pwd`
+
diff --git a/src_3rd/genvector/gds_char.c b/src_3rd/genvector/gds_char.c
new file mode 100644
index 0000000..6360cfb
--- /dev/null
+++ b/src_3rd/genvector/gds_char.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include <genvector/gds_char.h>
+#include <genvector/genvector_impl.c>
diff --git a/src_3rd/genvector/gds_char.h b/src_3rd/genvector/gds_char.h
new file mode 100644
index 0000000..b8401ef
--- /dev/null
+++ b/src_3rd/genvector/gds_char.h
@@ -0,0 +1,57 @@
+#ifndef GDS_CHAR_H
+#define GDS_CHAR_H
+#include <stdlib.h>
+#include <string.h>
+
+/* all public symbols are wrapped in GDS() */
+#define GVT(x) gds_ ## x
+
+/* Character type - a string consists of elements of this type */
+#define GVT_ELEM_TYPE char
+
+/* Type that represents string lengths */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the string grows */
+#define GVT_DOUBLING_THRS 4096
+
+/* Initial string size when the first element is written */
+#define GVT_START_SIZE 32
+
+/* Optional terminator; when present, it is always appended at the end */
+#define GVT_TERM '\0'
+
+/* Optional strlen(); if there's no strlen and there's GVT_TERM, append()
+   implements a loop to determine input string length. If neither of these
+   is #defined, there's no append(). */
+#define GVT_STRLEN(s) strlen(s)
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+/* An extra no_realloc field; when it is set to non-zero by the user, no
+   realloc() is called (any attempt to grow the array fails) */
+#define GVT_OPTIONAL_NO_REALLOC
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator */
+#ifndef GVT_CHAR_ALLOC
+#	define GVT_REALLOC(gds, ptr, size) realloc(ptr, size)
+#else
+#	define GVT_REALLOC(gds, ptr, size) GVT_CHAR_REALLOC(gds, ptr, size)
+#endif
+
+#ifndef GVT_CHAR_FREE
+#	define GVT_FREE(gds, ptr) free(ptr)
+#else
+#	define GVT_FREE(gds, ptr) GVT_CHAR_FREE(gds, ptr)
+#endif
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
+
+/* rename macros for convenience */
+#define gds_append_str  gds_append_array
+#endif
diff --git a/src_3rd/genvector/gds_wchar.c b/src_3rd/genvector/gds_wchar.c
new file mode 100644
index 0000000..d75dd85
--- /dev/null
+++ b/src_3rd/genvector/gds_wchar.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include <genvector/gds_wchar.h>
+#include <genvector/genvector_impl.c>
diff --git a/src_3rd/genvector/gds_wchar.h b/src_3rd/genvector/gds_wchar.h
new file mode 100644
index 0000000..1ab5c5a
--- /dev/null
+++ b/src_3rd/genvector/gds_wchar.h
@@ -0,0 +1,66 @@
+#ifndef GDS_CHAR_H
+#define GDS_CHAR_H
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <wchar.h>
+
+/* all public symbols are wrapped in GDS() */
+#define GVT(x) wgds_ ## x
+
+/* Character type - a string consists of elements of this type */
+#define GVT_ELEM_TYPE wchar_t
+
+/* Type that represents string lengths */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the string grows */
+#define GVT_DOUBLING_THRS 1024
+
+/* Initial string size when the first element is written */
+#define GVT_START_SIZE 32
+
+/* Optional terminator; when present, it is always appended at the end */
+#define GVT_TERM ((wchar_t)(0))
+
+/* Optional strlen(); if there's no strlen and there's GVT_TERM, append()
+   implements a loop to determine input string length. If neither of these
+   is #defined, there's no append(). */
+
+#if __STDC_VERSION__ >= 199901L
+#	define GVT_STRLEN(s) wcslen(s)
+#else
+/* implement a loop becuase GVT_TERM is defined */
+#endif
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+/* An extra no_realloc field; when it is set to non-zero by the user, no
+   realloc() is called (any attempt to grow the array fails) */
+#define GVT_OPTIONAL_NO_REALLOC
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator */
+#ifndef GVT_WCHAR_ALLOC
+#	define GVT_REALLOC(gds, ptr, size) realloc(ptr, size)
+#else
+#	define GVT_REALLOC(gds, ptr, size) GVT_WCHAR_REALLOC(gds, ptr, size)
+#endif
+
+#ifndef GVT_WCHAR_FREE
+#	define GVT_FREE(gds, ptr) free(ptr)
+#else
+#	define GVT_FREE(gds, ptr) GVT_WCHAR_FREE(gds, ptr)
+#endif
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
+
+/* rename macros for convenience */
+#define wgds_append_str  wgds_append_array
+
+#endif
diff --git a/src_3rd/genvector/genvector_impl.c b/src_3rd/genvector/genvector_impl.c
new file mode 100644
index 0000000..88768b2
--- /dev/null
+++ b/src_3rd/genvector/genvector_impl.c
@@ -0,0 +1,498 @@
+/*
+Copyright (c) 2016 Tibor 'Igor2' Palinkas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the Author nor the names of contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+Contact: genvector {at} igor2.repo.hu
+Project page: http://repo.hu/projects/genvector
+*/
+
+#ifdef GVT_TERM
+#	define GVT_TERMSIZE sizeof(GVT_TERM)
+#else
+#	define GVT_TERMSIZE 0
+#endif
+
+GVT_FUNC void GVT(init)(GVTTYPE *vect)
+{
+	vect->used = vect->alloced = 0;
+	vect->array = NULL;
+
+#ifdef GVT_OPTIONAL_NO_REALLOC
+	vect->no_realloc = 0;
+#endif
+
+#ifdef GVT_TERM
+	/* If we have a temrinator, an initialized array is zero length, but allocated
+	   and terminated. */
+	GVT(resize)(vect, 1);
+	vect->array[0] = GVT_TERM;
+#endif
+}
+
+GVT_FUNC GVT_SIZE_TYPE GVT(len)(GVTTYPE *vect)
+{
+	return vect->used;
+}
+
+
+static void GVT(init_block)(GVTTYPE *vect, GVT_ELEM_TYPE *arr, GVT_SIZE_TYPE from, GVT_SIZE_TYPE to)
+{
+#ifdef GVT_SET_NEW_BYTES_TO
+	memset(arr+from, GVT_SET_NEW_BYTES_TO, (to-from) * sizeof(GVT_ELEM_TYPE));
+#endif
+#ifdef GVT_INIT_ELEM_FUNC
+	if (vect->init_elem != NULL) {
+		GVT_SIZE_TYPE n;
+		for(n = from; n < to; n++)
+			vect->init_elem(vect, arr+n);
+	}
+#endif
+}
+
+static int GVT(construct_block)(GVTTYPE *vect, GVT_ELEM_TYPE *arr, GVT_SIZE_TYPE from, GVT_SIZE_TYPE to)
+{
+	int res = 0;
+#ifdef GVT_ELEM_CONSTRUCTOR
+	if (vect->elem_constructor != NULL) {
+		GVT_SIZE_TYPE n;
+		for(n = from; n < to; n++)
+			res |= vect->elem_constructor(vect, arr+n);
+	}
+#endif
+	return res;
+}
+
+static void GVT(destruct_block)(GVTTYPE *vect, GVT_ELEM_TYPE *arr, GVT_SIZE_TYPE from, GVT_SIZE_TYPE to)
+{
+#ifdef GVT_ELEM_CONSTRUCTOR
+	if (vect->elem_destructor != NULL) {
+		GVT_SIZE_TYPE n;
+		for(n = from; n < to; n++)
+			vect->elem_destructor(vect, arr+n);
+	}
+#endif
+}
+
+GVT_FUNC int GVT(resize)(GVTTYPE *vect, GVT_SIZE_TYPE new_size)
+{
+	GVT_SIZE_TYPE nall;
+	GVT_ELEM_TYPE *narr;
+
+#ifdef GVT_OPTIONAL_NO_REALLOC
+	if (vect->no_realloc) {
+		if (new_size <= vect->alloced) {
+			if (new_size < vect->used)
+				vect->used = new_size;
+			return 0;
+		}
+		return -1;
+	}
+#endif
+
+	if (new_size == 0) {
+		GVT(destruct_block)(vect, vect->array, 0, vect->used);
+		GVT_FREE(vect, vect->array);
+		vect->alloced = vect->used = 0;
+		vect->array = NULL;
+		return 0;
+	}
+
+	if (new_size > vect->alloced) {
+		/* grow */
+		if (new_size < GVT_DOUBLING_THRS) {
+			nall = vect->alloced;
+			if (nall < GVT_START_SIZE)
+				nall = GVT_START_SIZE;
+			while(nall < new_size)
+				nall *= 2;
+		}
+		else
+			nall = new_size + GVT_START_SIZE;
+	}
+	else if (new_size < vect->used) {
+		/* shrink */
+		GVT(destruct_block)(vect, vect->array, new_size, vect->used);
+
+		nall = new_size;
+		if (nall < GVT_START_SIZE)
+			nall = GVT_START_SIZE;
+	}
+	else {
+		/* no need to resize */
+		return 0;
+	}
+
+	narr = GVT_REALLOC(vect, vect->array, nall * sizeof(GVT_ELEM_TYPE));
+	if (!(new_size > vect->alloced)) {
+		/* allocation failed for grow: error */
+		if (narr == NULL) 
+			return -1;
+	}
+	else {
+		/* allocation failed for shrink: very unlikely, but we can live with it, just keep the original pointer */
+		if (narr == NULL) 
+			narr = vect->array;
+	}
+
+	if (new_size > vect->alloced)
+		GVT(init_block)(vect, narr, vect->used, nall);
+
+	vect->alloced = nall;
+	vect->array = narr;
+	if (new_size < vect->used)
+		vect->used = new_size;
+
+	return 0;
+}
+
+GVT_FUNC int GVT(uninit)(GVTTYPE *vect)
+{
+	return GVT(resize)(vect, 0);
+}
+
+static int GVT(copy_block)(GVTTYPE *vect, GVT_ELEM_TYPE *dst, const GVT_ELEM_TYPE *src, GVT_SIZE_TYPE len)
+{
+#ifdef GVT_ELEM_COPY
+	if (vect->elem_copy != NULL) {
+		for(; len > 0; len--) {
+			if (vect->elem_copy(vect, dst, src) != 0)
+				return -1;
+			dst++;
+			src++;
+		}
+		return 0;
+	}
+#endif
+	memcpy(dst, src, len * sizeof(GVT_ELEM_TYPE));
+	return 0;
+}
+
+GVT_FUNC int GVT(append_len)(GVTTYPE *vect, const GVT_ELEM_TYPE *src, GVT_SIZE_TYPE len)
+{
+	int res;
+
+	if (len == 0)
+		return 0;
+
+	res = GVT(resize)(vect, vect->used + len + GVT_TERMSIZE);
+	if (res == 0) {
+		if (GVT(copy_block)(vect, vect->array + vect->used, src, len) != 0)
+			return -1;
+/*		GVT(construct_block)(vect, vect->array, vect->used, vect->used+len);*/
+		vect->used += len;
+#ifdef GVT_TERM
+		vect->array[vect->used] = GVT_TERM;
+#endif
+	}
+	return res;
+}
+
+GVT_FUNC int GVT(append)(GVTTYPE *vect, GVT_ELEM_TYPE src)
+{
+	return GVT(append_len)(vect, &src, 1);
+}
+
+GVT_FUNC GVT_ELEM_TYPE *GVT(alloc_append)(GVTTYPE *vect, int num_elems)
+{
+	GVT_ELEM_TYPE *ret = NULL;
+	int res;
+
+	res = GVT(resize)(vect, vect->used + num_elems + GVT_TERMSIZE);
+	if (res == 0) {
+		GVT(init_block)(vect, vect->array, vect->used, vect->used+num_elems);
+		if (GVT(construct_block)(vect, vect->array, vect->used, vect->used+num_elems) == 0) {
+			ret = vect->array + vect->used;
+			vect->used += num_elems;
+		}
+#ifdef GVT_TERM
+		vect->array[vect->used] = GVT_TERM;
+#endif
+	}
+	return ret;
+}
+
+#ifdef GVT_NEED_APPEND_ARRAY
+GVT_FUNC int GVT(append_array)(GVTTYPE *vect, const GVT_ELEM_TYPE *src)
+{
+	GVT_SIZE_TYPE len;
+
+#ifdef GVT_STRLEN
+	len = GVT_STRLEN(src);
+#else
+	{
+		const GVT_ELEM_TYPE *s;
+		len = 0;
+		for(s = src; *s != GVT_TERM; s++)
+			len++;
+	}
+#endif
+
+	return GVT(append_len)(vect, src, len);
+}
+#undef GVT_NEED_APPEND_ARRAY
+#endif
+
+GVT_FUNC int GVT(truncate)(GVTTYPE *vect, GVT_SIZE_TYPE len)
+{
+	int res;
+
+	if (len > vect->used)
+		return -1;
+	if (len == vect->used)
+		return 0;
+
+	res = GVT(resize)(vect, len + GVT_TERMSIZE);
+	if (res != 0)
+		return -1;
+
+	vect->used = len;
+#ifdef GVT_TERM
+		vect->array[len] = GVT_TERM;
+#endif
+	return 0;
+}
+
+
+GVT_FUNC int GVT(concat)(GVTTYPE *dst, const GVTTYPE *src)
+{
+	return GVT(append_len)(dst, src->array, src->used);
+}
+
+GVT_FUNC int GVT(in_bound)(GVTTYPE *vect, GVT_SIZE_TYPE idx)
+{
+	return idx < vect->used;
+}
+
+static int GVT(enlarge_)(GVTTYPE *vect, GVT_SIZE_TYPE idx, GVT_SIZE_TYPE init_idx)
+{
+	GVT_SIZE_TYPE max = vect->alloced;
+	if (idx+GVT_TERMSIZE < vect->used)
+		return 0;
+	if (idx+GVT_TERMSIZE >= vect->alloced) {
+		if (GVT(resize)(vect, idx+GVT_TERMSIZE+1) != 0)
+			return -1;
+	}
+
+	if (init_idx+1 < max)
+		max = init_idx+1;
+	GVT(init_block)(vect, vect->array, vect->used, max);
+	if (GVT(construct_block)(vect, vect->array, vect->used, init_idx+1) != 0)
+		return -1;
+	vect->used = idx+1;
+#ifdef GVT_TERM
+	vect->array[idx+1] = GVT_TERM;
+#endif
+	return 0;
+}
+
+GVT_FUNC int GVT(enlarge)(GVTTYPE *vect, GVT_SIZE_TYPE idx)
+{
+	return GVT(enlarge_)(vect, idx, idx);
+}
+
+
+GVT_FUNC int GVT(set_ptr)(GVTTYPE *vect, GVT_SIZE_TYPE idx, GVT_ELEM_TYPE *src)
+{
+#ifdef GVT_ELEM_CONSTRUCTOR
+	GVT_SIZE_TYPE old_used = vect->used;
+#endif
+
+	if (idx+GVT_TERMSIZE >= vect->used) {
+		/* enlarge if needed, do not initialize the elem we are going to set */
+		if (GVT(enlarge_)(vect, idx, idx-1) != 0)
+			return -1;
+	}
+	else {
+		/* no need to enlarge - destruct the elem we are going to overwrite */
+#ifdef GVT_ELEM_DESTRUCTOR
+		if (vect->elem_destructor != NULL)
+			vect->elem_destructor(vect, vect->array+idx);
+#endif
+	}
+
+#ifdef GVT_ELEM_COPY
+	if (vect->elem_copy != NULL) {
+		if (vect->elem_copy(vect, &(vect->array[idx]), src) != 0) {
+			vect->used = old_used;
+			return -1;
+		}
+		return 0;
+	}
+#endif
+
+	vect->array[idx] = *src;
+	return 0;
+}
+
+GVT_FUNC int GVT(set)(GVTTYPE *vect, GVT_SIZE_TYPE idx, GVT_ELEM_TYPE src)
+{
+	return GVT(set_ptr)(vect, idx, &src);
+}
+
+GVT_FUNC GVT_ELEM_TYPE *GVT(get)(GVTTYPE *vect, GVT_SIZE_TYPE idx, int alloc)
+{
+	if (idx >= vect->used) {
+		if (!alloc)
+			return NULL;
+	}
+	if (GVT(enlarge_)(vect, idx, idx) != 0)
+		return NULL;
+	return &(vect->array[idx]);
+}
+
+GVT_FUNC int GVT(remove)(GVTTYPE *vect, GVT_SIZE_TYPE from_idx, GVT_SIZE_TYPE num_elems)
+{
+	GVT_SIZE_TYPE len;
+
+	/* can't start deleting beyond the end */
+	if (from_idx >= vect->used)
+		return -1;
+
+	/* accept deletion longer than the array but stop at the end */
+	if (from_idx+num_elems > vect->used)
+		num_elems = vect->used - from_idx;
+
+	/* if we end up trying to delete 0 elements, we are done */
+	if (num_elems == 0)
+		return 0;
+
+	GVT(destruct_block)(vect, vect->array, from_idx, from_idx+num_elems);
+	len = vect->used - (from_idx+num_elems);
+	if (len != 0)
+		memmove(vect->array+from_idx, vect->array+from_idx+num_elems, (len) * sizeof(GVT_ELEM_TYPE));
+	vect->used -= num_elems;
+
+#ifdef GVT_TERM
+	vect->array[vect->used] = GVT_TERM;
+#endif
+	return GVT(resize)(vect, vect->used + GVT_TERMSIZE);
+}
+
+GVT_FUNC int GVT(remove_bw)(GVTTYPE *vect, GVT_SIZE_TYPE to_idx, GVT_SIZE_TYPE num_elems)
+{
+	/* do not attempt to start deletion beyond the end of the array */
+	if (to_idx >= vect->used)
+		return -1;
+
+	if (to_idx < num_elems)
+		return GVT(remove)(vect, 0, to_idx+1);
+	return GVT(remove)(vect, to_idx - num_elems + 1, num_elems);
+}
+
+GVT_FUNC int GVT(copy)(GVTTYPE *dst_vect, GVT_SIZE_TYPE dst_idx, GVTTYPE *src_vect, GVT_SIZE_TYPE src_idx, GVT_SIZE_TYPE num_elems)
+{
+	GVT_SIZE_TYPE newsize;
+
+	/* src can't start beyond the array */
+	if (src_idx >= src_vect->used)
+		return -1;
+
+	/* nop-copy */
+	if ((dst_vect->array == src_vect->array) && (dst_idx == src_idx))
+		return 0;
+
+	/* truncate num elems at the end of src */
+	if ((src_idx+num_elems-1) >= src_vect->used)
+		num_elems = src_vect->used - src_idx;
+
+	/* copying 0 elements is easy */
+	if (num_elems == 0)
+		return 0;
+
+	newsize = dst_idx+num_elems-1;
+
+	/* make sure destination vector is large enough */
+	if (newsize >= dst_vect->used) {
+		if (GVT(resize)(dst_vect, newsize+GVT_TERMSIZE+1) != 0)
+			return -1;
+#ifdef GVT_TERM
+		dst_vect->array[newsize+1] = GVT_TERM;
+#endif
+	}
+
+	/* if dst_idx is beyond dst's used, there will be some emelents that need
+	   implicit initialization */
+	if (dst_idx > dst_vect->used) {
+		GVT(init_block)(dst_vect, dst_vect->array, dst_vect->used, dst_idx);
+		if (GVT(construct_block)(dst_vect, dst_vect->array, dst_vect->used, dst_idx) != 0)
+			return -1;
+	}
+
+
+#ifdef GVT_ELEM_COPY
+	/* the slow way: have to copy element by element */
+	if (dst_vect->elem_copy != NULL) {
+		GVT_SIZE_TYPE n;
+
+		if ((dst_vect->array == src_vect->array) && (dst_idx > src_idx)) {
+			/* Backward copy to avoid losing elements in the overlap */
+			for(n = num_elems; n > 0; n--) {
+#ifdef GVT_ELEM_DESTRUCTOR
+				if ((dst_vect->elem_destructor != NULL) && (n+dst_idx-1 < dst_vect->used))
+					dst_vect->elem_destructor(dst_vect, dst_vect->array+n+dst_idx-1);
+#endif
+				if (dst_vect->elem_copy(dst_vect, &(dst_vect->array[n+dst_idx-1]), &(src_vect->array[n+src_idx-1])) != 0)
+					return -1;
+			}
+		}
+		else {
+			/* Normal forward copy */
+			for(n = 0; n < num_elems; n++) {
+#ifdef GVT_ELEM_DESTRUCTOR
+				if ((dst_vect->elem_destructor != NULL) && (n+dst_idx < dst_vect->used))
+					dst_vect->elem_destructor(dst_vect, dst_vect->array+n+dst_idx);
+#endif
+				if (dst_vect->elem_copy(dst_vect, &(dst_vect->array[n+dst_idx]), &(src_vect->array[n+src_idx])) != 0)
+					return -1;
+			}
+		}
+
+
+		goto update_used;
+	}
+#endif
+
+#ifdef GVT_ELEM_DESTRUCTOR
+	/* need to destruct elements that are goung to be overwritten */
+	if (dst_vect->elem_destructor != NULL) {
+		GVT_SIZE_TYPE n;
+		for(n = dst_idx; (n < num_elems) && (n < dst_vect->used); n++)
+			dst_vect->elem_destructor(dst_vect, dst_vect->array+n);
+	}
+#endif
+
+	/* the fast way: use memmove() */
+	memmove(&(dst_vect->array[dst_idx]), &(src_vect->array[src_idx]), num_elems * sizeof(GVT_ELEM_TYPE));
+
+#ifdef GVT_ELEM_COPY
+	update_used:;
+#endif
+	if (dst_idx+num_elems > dst_vect->used)
+		dst_vect->used = dst_idx+num_elems;
+	return 0;
+}
+
diff --git a/src_3rd/genvector/genvector_impl.h b/src_3rd/genvector/genvector_impl.h
new file mode 100644
index 0000000..a12e0f0
--- /dev/null
+++ b/src_3rd/genvector/genvector_impl.h
@@ -0,0 +1,67 @@
+#define GVTTYPE GVT(t)
+
+typedef struct GVT(s) GVTTYPE;
+
+
+struct GVT(s) {
+	GVT_SIZE_TYPE used, alloced;
+	GVT_ELEM_TYPE *array;
+#ifdef GVT_INIT_ELEM_FUNC
+	void (*init_elem)(GVTTYPE *vect, GVT_ELEM_TYPE *elem);
+#endif
+#ifdef GVT_ELEM_CONSTRUCTOR
+	int (*elem_constructor)(GVTTYPE *vect, GVT_ELEM_TYPE *elem);
+#endif
+#ifdef GVT_ELEM_DESTRUCTOR
+	void (*elem_destructor)(GVTTYPE *vect, GVT_ELEM_TYPE *elem);
+#endif
+#ifdef GVT_ELEM_COPY
+	int (*elem_copy)(GVTTYPE *dst_vect, GVT_ELEM_TYPE *dst, const GVT_ELEM_TYPE *src);
+#endif
+#ifdef GVT_USER_FIELDS
+	GVT_USER_FIELDS
+#endif
+#ifdef GVT_OPTIONAL_NO_REALLOC
+	char no_realloc;
+#endif
+};
+
+/* Append is possible only if we can determine the length of the input string,
+   either because there's an existing strlen() or because we can count
+   characters looking for a terminator */
+#ifdef GVT_STRLEN
+#	define GVT_NEED_APPEND_ARRAY
+#else
+#	ifdef GVT_TERM
+#		define GVT_NEED_APPEND_ARRAY
+#	endif
+#endif
+
+GVT_FUNC void GVT(init)(GVTTYPE *vect);
+GVT_FUNC int GVT(uninit)(GVTTYPE *vect);
+
+GVT_FUNC GVT_SIZE_TYPE GVT(len)(GVTTYPE *vect);
+GVT_FUNC int GVT(in_bound)(GVTTYPE *vect, GVT_SIZE_TYPE idx);
+
+GVT_FUNC GVT_ELEM_TYPE *GVT(get)(GVTTYPE *vect, GVT_SIZE_TYPE idx, int alloc);
+GVT_FUNC int GVT(set)(GVTTYPE *vect, GVT_SIZE_TYPE idx, GVT_ELEM_TYPE src);
+GVT_FUNC int GVT(set_ptr)(GVTTYPE *vect, GVT_SIZE_TYPE idx, GVT_ELEM_TYPE *src);
+
+GVT_FUNC int GVT(resize)(GVTTYPE *vect, GVT_SIZE_TYPE new_size);
+GVT_FUNC int GVT(truncate)(GVTTYPE *vect, GVT_SIZE_TYPE len);
+GVT_FUNC int GVT(enlarge)(GVTTYPE *vect, GVT_SIZE_TYPE idx);
+
+GVT_FUNC int GVT(append)(GVTTYPE *vect, GVT_ELEM_TYPE src);
+GVT_FUNC int GVT(append_len)(GVTTYPE *vect, const GVT_ELEM_TYPE *src, GVT_SIZE_TYPE len);
+GVT_FUNC int GVT(concat)(GVTTYPE *dst, const GVTTYPE *src);
+GVT_FUNC int GVT(copy)(GVTTYPE *dst_vect, GVT_SIZE_TYPE dst_idx, GVTTYPE *src_vect, GVT_SIZE_TYPE src_idx, GVT_SIZE_TYPE num_elems);
+GVT_FUNC GVT_ELEM_TYPE *GVT(alloc_append)(GVTTYPE *vect, int num_elems);
+
+GVT_FUNC int GVT(remove)(GVTTYPE *vect, GVT_SIZE_TYPE from_idx, GVT_SIZE_TYPE num_elems);
+GVT_FUNC int GVT(remove_bw)(GVTTYPE *vect, GVT_SIZE_TYPE to_idx, GVT_SIZE_TYPE num_elems);
+
+#ifdef GVT_NEED_APPEND_ARRAY
+GVT_FUNC int GVT(append_array)(GVTTYPE *vect, const GVT_ELEM_TYPE *src);
+#endif
+
+
diff --git a/src_3rd/genvector/genvector_undef.h b/src_3rd/genvector/genvector_undef.h
new file mode 100644
index 0000000..6ffec84
--- /dev/null
+++ b/src_3rd/genvector/genvector_undef.h
@@ -0,0 +1,15 @@
+#ifndef GVT_DONT_UNDEF
+#	undef GVT
+#	undef GVTTYPE
+#	undef GVT_TERMSIZE
+#	undef GVT_ELEM_TYPE
+#	undef GVT_SIZE_TYPE
+#	undef GVT_DOUBLING_THRS
+#	undef GVT_START_SIZE
+#	undef GVT_TERM
+#	undef GVT_STRLEN
+# undef GVT_REALLOC
+# undef GVT_FREE
+# undef GVT_USER_FIELDS
+# undef GVT_NEED_APPEND_ARRAY
+#endif
diff --git a/src_3rd/genvector/vti0.c b/src_3rd/genvector/vti0.c
new file mode 100644
index 0000000..007ddab
--- /dev/null
+++ b/src_3rd/genvector/vti0.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vti0.h"
+#include <genvector/genvector_impl.c>
diff --git a/src_3rd/genvector/vti0.h b/src_3rd/genvector/vti0.h
new file mode 100644
index 0000000..26386af
--- /dev/null
+++ b/src_3rd/genvector/vti0.h
@@ -0,0 +1,73 @@
+#ifndef VTI0_H
+#define VTI0_H
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Elem=int; init=0
+   Int vector, all newly allocated bytes are set to 0 */
+
+/* all public symbols are wrapped in GVT() - see vt_t(7) */
+#define GVT(x) vti0_ ## x
+
+/* Array elem type - see vt_t(7) */
+#define GVT_ELEM_TYPE int
+
+/* Type that represents array lengths - see vt_t(7) */
+#define GVT_SIZE_TYPE size_t
+
+/* Below this length, always double allocation size when the array grows */
+#define GVT_DOUBLING_THRS 4096
+
+/* Initial array size when the first element is written */
+#define GVT_START_SIZE 32
+
+/* Optional terminator; when present, it is always appended at the end - see
+   vt_term(7)*/
+/* #define GVT_TERM '\0' */
+
+/* Optional prefix for function definitions (e.g. static inline) */
+#define GVT_FUNC
+
+/* Enable this to set all new bytes ever allocated to this value - see
+   vt_set_new_bytes_to(7) */
+#define GVT_SET_NEW_BYTES_TO 0
+
+/* Enable GVT_INIT_ELEM_FUNC and an user configured function is called
+   for each new element allocated (even between used and alloced).
+   See vt_init_elem(7) */
+/*#define GVT_INIT_ELEM_FUNC*/
+
+/* Enable GVT_ELEM_CONSTRUCTOR and an user configured function is called
+   for each element that is getting within the range of ->used.
+   See vt_construction(7) */
+/*#define GVT_ELEM_CONSTRUCTOR */
+
+/* Enable GVT_ELEM_DESTRUCTOR and an user configured function is called
+   for each element that was once constructed and now getting beyong ->used.
+   See vt_construction(7) */
+/*#define GVT_ELEM_DESTRUCTOR */
+
+/* Enable GVT_ELEM_COPY and an user configured function is called
+   for copying elements into the array.
+   See vt_construction(7) */
+/*#define GVT_ELEM_COPY */
+
+/* Optional extra fields in the vector struct - see vt_user_fields(7) */
+/* #define GVT_USER_FIELDS int foo; char bar[12]; */
+
+/* An extra no_realloc field; when it is set to non-zero by the user, no
+   realloc() is called (any attempt to grow the array fails) */
+/* #define GVT_OPTIONAL_NO_REALLOC */
+
+/* Include the actual header implementation */
+#include <genvector/genvector_impl.h>
+
+/* Memory allocator - see vt_allocation(7) */
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+/* clean up #defines */
+#include <genvector/genvector_undef.h>
+
+#endif
diff --git a/src_3rd/genvector/vtii.c b/src_3rd/genvector/vtii.c
new file mode 100644
index 0000000..a96dfa9
--- /dev/null
+++ b/src_3rd/genvector/vtii.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vtii.h"
+#include <genvector/genvector_impl.c>
diff --git a/src_3rd/genvector/vtii.h b/src_3rd/genvector/vtii.h
new file mode 100644
index 0000000..17e42ba
--- /dev/null
+++ b/src_3rd/genvector/vtii.h
@@ -0,0 +1,30 @@
+#ifndef VTII_H
+#define VTII_H
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Elem=int; init=function
+   int vector, there's an init function for the newly allocated elements (but no
+   memory cleaning other than that); useful if new elements need to be set
+   to some non-zero initial value (e.g. -1). Set up function pointer
+   ->init_elem  */
+
+/* For documentation on the settings, check vti0.h */
+
+#define GVT(x) vtii_ ## x
+#define GVT_ELEM_TYPE int
+#define GVT_SIZE_TYPE size_t
+#define GVT_DOUBLING_THRS 4096
+#define GVT_START_SIZE 32
+#define GVT_FUNC
+#define GVT_INIT_ELEM_FUNC
+
+#include <genvector/genvector_impl.h>
+
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+#include <genvector/genvector_undef.h>
+
+#endif
diff --git a/src_3rd/genvector/vtp0.c b/src_3rd/genvector/vtp0.c
new file mode 100644
index 0000000..96b3189
--- /dev/null
+++ b/src_3rd/genvector/vtp0.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vtp0.h"
+#include <genvector/genvector_impl.c>
diff --git a/src_3rd/genvector/vtp0.h b/src_3rd/genvector/vtp0.h
new file mode 100644
index 0000000..01f804e
--- /dev/null
+++ b/src_3rd/genvector/vtp0.h
@@ -0,0 +1,30 @@
+#ifndef VTP0_H
+#define VTP0_H
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Elem=void *; init=NULL
+   pointer vector with new elements initialized to NULL.
+*/
+
+/* For documentation on the settings, check vti0.h */
+
+typedef void * vtp0_ptr_t;
+
+#define GVT(x) vtp0_ ## x
+#define GVT_ELEM_TYPE vtp0_ptr_t
+#define GVT_SIZE_TYPE size_t
+#define GVT_DOUBLING_THRS 4096
+#define GVT_START_SIZE 32
+#define GVT_FUNC
+#define GVT_SET_NEW_BYTES_TO 0
+
+#include <genvector/genvector_impl.h>
+
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+#include <genvector/genvector_undef.h>
+
+#endif
diff --git a/src_3rd/genvector/vts0.c b/src_3rd/genvector/vts0.c
new file mode 100644
index 0000000..93b8f59
--- /dev/null
+++ b/src_3rd/genvector/vts0.c
@@ -0,0 +1,3 @@
+#define GVT_DONT_UNDEF
+#include "vts0.h"
+#include <genvector/genvector_impl.c>
diff --git a/src_3rd/genvector/vts0.h b/src_3rd/genvector/vts0.h
new file mode 100644
index 0000000..7e68ba0
--- /dev/null
+++ b/src_3rd/genvector/vts0.h
@@ -0,0 +1,30 @@
+#ifndef VTS0_H
+#define VTS0_H
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Elem=char *; init=NULL
+   string vector with new elements initialized to NULL.
+*/
+
+/* For documentation on the settings, check vti0.h */
+
+typedef char * vts0_str_t;
+
+#define GVT(x) vts0_ ## x
+#define GVT_ELEM_TYPE vts0_str_t
+#define GVT_SIZE_TYPE size_t
+#define GVT_DOUBLING_THRS 4096
+#define GVT_START_SIZE 32
+#define GVT_FUNC
+#define GVT_SET_NEW_BYTES_TO 0
+
+#include <genvector/genvector_impl.h>
+
+#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
+#define GVT_FREE(vect, ptr)           free(ptr)
+
+#include <genvector/genvector_undef.h>
+
+#endif
diff --git a/src_3rd/liblhtpers/Makefile b/src_3rd/liblhtpers/Makefile
new file mode 100644
index 0000000..991915f
--- /dev/null
+++ b/src_3rd/liblhtpers/Makefile
@@ -0,0 +1,20 @@
+ROOT=..
+CFLAGS = -Wall -g -I$(ROOT) -I$(ROOT)/liblihata
+OBJS_LIB = lhtpers.o
+OBJS_TESTER = 
+OBJS = $(OBJS_LIB)
+
+all:$(OBJS_LIB)
+
+# dependencies
+
+include Makefile.dep
+
+depend:
+	@echo "# Generated by \"make depend\"" > Makefile.dep
+	gcc $(CFLAGS) -MM `echo $(OBJS) | sed "s/\.o/.c/g"` >> Makefile.dep
+
+clean:
+	-rm $(OBJS) test_simple $(OBJS) 2>/dev/null
+
+include $(ROOT)/Makefile.common
diff --git a/src_3rd/liblhtpers/Makefile.dep b/src_3rd/liblhtpers/Makefile.dep
new file mode 100644
index 0000000..eff0538
--- /dev/null
+++ b/src_3rd/liblhtpers/Makefile.dep
@@ -0,0 +1,6 @@
+# Generated by "make depend"
+lhtpers.o: lhtpers.c ../liblihata/genht/htsp.h ../liblihata/genht/ht.h \
+ ../liblihata/genht/ht_inlines.h ../liblihata/genht/hash.h lhtpers.h \
+ ../liblihata/dom.h ../liblihata/lihata.h ../liblihata/parser.h \
+ ../liblihata/genht/htsp.h output.c pers_list.c pers_hash.c pers_text.c \
+ pers_table.c
diff --git a/src_3rd/liblhtpers/lhtpers.c b/src_3rd/liblhtpers/lhtpers.c
new file mode 100644
index 0000000..d99eca8
--- /dev/null
+++ b/src_3rd/liblhtpers/lhtpers.c
@@ -0,0 +1,793 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <genht/htsp.h>
+#include <genht/hash.h>
+#include "lhtpers.h"
+
+
+static int debugprintf_dummy(const char *fmt, ...) { return 0; }
+#define debug_printf debugprintf_dummy
+
+
+#if 0
+#	define ilprintf fprintf
+#else
+static int ilprintf_dummy(FILE *f, ...) { return 0; }
+#	define ilprintf ilprintf_dummy
+#endif
+
+#define is_descendable(nd) ((nd->type == LHT_LIST) || (nd->type == LHT_HASH) || (nd->type == LHT_TABLE))
+
+static void pb_append(lht_persbuf_t *pb, char c)
+{
+	if (pb->used >= pb->alloced) {
+		pb->alloced += 16;
+		pb->s = realloc(pb->s, pb->alloced);
+	}
+	pb->s[pb->used] = c;
+	pb->used++;
+}
+
+static void pb_insert(lht_persbuf_t *pb, char c)
+{
+	if (pb->used >= pb->alloced) {
+		pb->alloced += 16;
+		pb->s = realloc(pb->s, pb->alloced);
+	}
+	if (pb->used > 0)
+		memmove(pb->s+1, pb->s, pb->used);
+	pb->s[0] = c;
+	pb->used++;
+}
+
+static void pb_reset(lht_persbuf_t *pb)
+{
+	pb->used = 0;
+}
+
+static void pb_free(lht_persbuf_t *pb)
+{
+	if (pb->s != NULL)
+		free(pb->s);
+	pb->used = pb->alloced = 0;
+	pb->s = NULL;
+}
+
+static char *pb_gets(lht_persbuf_t *pb)
+{
+	if (pb->used == 0)
+		return "";
+	if ((pb->used == 0) || (pb->s[pb->used-1] != '\0'))
+		pb_append(pb, '\0');
+	return pb->s;
+}
+
+char *loc_accept[LHT_LOC_max] = {
+	" \t\r\n",
+	" \t\r\n",
+	" \t\r\n",
+	NULL,
+	" \t\r\n",
+	"\r\n;"
+};
+
+void copy_style(lht_perstyle_t *dst, lht_perstyle_t *src)
+{
+	int n;
+
+	for(n = 0; n < LHT_LOC_max; n++)
+		if (dst->buff[n].s != NULL)
+			free(dst->buff[n].s);
+
+	memcpy(dst, src, sizeof(lht_perstyle_t));
+
+	for(n = 0; n < LHT_LOC_max; n++) {
+		if (src->buff[n].used > 0) {
+			dst->buff[n].alloced = dst->buff[n].used = src->buff[n].used;
+			dst->buff[n].s = malloc(dst->buff[n].alloced);
+			memcpy(dst->buff[n].s, src->buff[n].s, src->buff[n].used);
+		}
+		else {
+			dst->buff[n].used = dst->buff[n].alloced = 0;
+			dst->buff[n].s = NULL;
+		}
+	}
+	dst->name = lht_strdup(src->name);
+	dst->text_data = lht_strdup(src->text_data);
+}
+
+typedef struct {
+	/* generic section */
+	unsigned int del_tree, silent_copy_tree;
+	lht_node_type_t closing_type;
+	lht_perstyle_t last_style[LHT_SYMLINK+1];
+	unsigned just_entered:1;        /* set to 1 before descending, reset to 0 right after the descend */
+	unsigned inited:1;              /* set to 0 before descending, set to 1 if the level got initialized */
+	lht_node_t *current_node;       /* in-memory version of the node currently being processed */
+
+	/* type specific section */
+	lht_node_t *nd;
+	long trow, tcol;
+	htsp_t ha_unseen;
+	unsigned table_inrow:1;
+} inst_t;
+
+typedef struct lht_pers_s {
+	unsigned finished:1;
+	unsigned error:1;
+	
+	lht_perstyle_t *curr;
+	FILE *outf;
+	const lhtpers_ev_t *events;
+
+	lht_doc_t *ind;    /* input document we are trying to save */
+	lht_node_t **inn;  /* current node in the input document */
+	inst_t *inst;      /* input doc per level state */
+	int inn_used, inn_alloced;
+} lht_pers_t;
+
+typedef enum {
+	PF_PROCEED,
+	PF_INHIBIT,      /* do not print this one node */
+	PF_SILENT_COPY   /* copy the on-disk subtree without further callbacks */
+} pre_fin_ctrl_t;
+
+typedef struct {
+	pre_fin_ctrl_t (*pre_fin)(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st);
+	void (*post_fin)(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st);
+	void (*pre_close)(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st);
+} hook_t;
+
+static hook_t hooks[LHT_SYMLINK+1];
+
+/**** input doc node stack ****/
+static inst_t *inn_push(lht_pers_t *p, lht_node_t *n, lht_node_type_t closing_type)
+{
+	if (p->inn_used >= p->inn_alloced) {
+		p->inn_alloced += 32;
+		p->inn = realloc(p->inn, sizeof(lht_node_t *) * p->inn_alloced);
+		p->inst = realloc(p->inst, sizeof(inst_t) * p->inn_alloced);
+	}
+	p->inn[p->inn_used] = n;
+	memset(p->inst+p->inn_used, 0, sizeof(inst_t));
+	p->inst[p->inn_used].closing_type = closing_type;
+	p->inst[p->inn_used].just_entered = 1;
+	p->inn_used++;
+	return &p->inst[p->inn_used-1];
+}
+
+static inst_t *inn_pop(lht_pers_t *p)
+{
+	if (p->inn_used > 0)
+		p->inn_used--;
+	if (p->inn_used > 0)
+		return &p->inst[p->inn_used-1];
+	else
+		return NULL;
+}
+
+static lht_node_t *inn_peek(lht_pers_t *p, int back, inst_t **st)
+{
+	int offs = p->inn_used - back - 1;
+	if ((p->inn != NULL) && (offs >= 0)) {
+		if (st != NULL)
+			*st = p->inst + offs;
+		return p->inn[offs];
+	}
+	if (st != NULL)
+		*st = NULL;
+	return NULL;
+}
+
+/**** single-node output functions ****/
+
+/* print a name backslash-quoting all } characters */
+static void quote_fprint_name(FILE *f, const char *name)
+{
+	const char *next = name, *last = name;
+	for(;;) {
+		next = strchr(last, '}');
+		if (next == NULL)
+			break;
+		fwrite(last, next-last, 1, f);
+		fprintf(f, "\\}");
+		next++;
+		last = next;
+	}
+	fprintf(f, "%s", last);
+}
+
+static void out_text(FILE *outf, lht_perstyle_t *sp, const char *name, const char *value)
+{
+	int val_brace_handled = 0, noname = 0;
+
+	if (value == NULL)
+		value = "";
+
+	if (((sp->ename) || (!sp->name_braced)) && (value != NULL) && (*value != '\0') && ((name == NULL) || (*name == '\0'))) {
+		/* special case: no name given */
+		if (sp->val_brace) {
+			sp->name_braced = 1;
+			sp->val_brace = 0;
+			val_brace_handled = 1;
+		}
+/*printf("texthack: name:|%s|%s|%s| val:|%s|%s|%s|\n", pb_gets(&sp->buff[LHT_LOC_NAME_PRE]), name, pb_gets(&sp->buff[LHT_LOC_NAME_POST]), pb_gets(&sp->buff[LHT_LOC_VAL_PRE]), value, pb_gets(&sp->buff[LHT_LOC_VAL_POST]));*/
+		name = value;
+		value = "";
+		sp->has_eq = 0;
+		noname = 1;
+	}
+	else if ((sp->ename == 0) && (sp->name_braced) && (*name == '\0') && (*value != '\0')) {
+		/* special case: only a single {val} */
+		name = value;
+		value = "";
+		val_brace_handled = 1;
+		sp->has_eq = 0;
+		noname = 1;
+	}
+
+	if ((!val_brace_handled) && (!sp->name_braced) && (*name != '\0') && (lht_need_brace(LHT_TEXT, name, noname ? 0 : 1)))
+		sp->name_braced = 1;
+
+	if ((!val_brace_handled) && (!sp->val_brace) && (*value != '\0') && (lht_need_brace(LHT_TEXT, value, 0)))
+		sp->val_brace = 1;
+
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_NAME_PRE]));
+	if ((sp->etype) || (sp->type != LHT_TEXT))
+		fprintf(outf, "%s:", lht_type_id(sp->type));
+	if (sp->name_braced)
+		fprintf(outf, "{");
+	quote_fprint_name(outf, name);
+	if (sp->name_braced)
+		fprintf(outf, "}");
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_NAME_POST]));
+	if (sp->has_eq)
+		fprintf(outf, "=");
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_VAL_PRE]));
+	if (sp->val_brace)
+		fprintf(outf, "{");
+	quote_fprint_name(outf, value);
+	if (sp->val_brace)
+		fprintf(outf, "}");
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_VAL_POST]));
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_TERM]));
+}
+
+static int out_open_chk_etype(lht_perstyle_t *sp, lht_node_type_t nt, lht_node_type_t parent_nt)
+{
+	if (!sp->etype) {
+		/* no explicit type requested, check if we absolutely have to use one */
+
+		if (parent_nt == LHT_TABLE) {
+			if (nt != LHT_LIST)
+				return 1;
+		}
+		else if (nt != LHT_TEXT)
+			return 1;
+	}
+	return sp->etype;
+}
+
+static int out_open_chk_brace(lht_perstyle_t *sp, lht_node_type_t nt, const char *sep)
+{
+	if ((nt == LHT_TEXT) || (nt == LHT_SYMLINK))
+		return 0;
+	if (strchr(sep, '{') == 0)
+		return 1;
+	return 0;
+}
+
+static void out_open(FILE *outf, lht_perstyle_t *sp, lht_node_type_t nt, const char *name)
+{
+	const char *tmp1, *tmp2;
+
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_NAME_PRE]));
+	if (sp->name_braced)
+		fprintf(outf, "{");
+	if (sp->etype)
+		fprintf(outf, "%s:", lht_type_id(nt));
+	quote_fprint_name(outf, name);
+	if (sp->name_braced)
+		fprintf(outf, "}");
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_NAME_POST]));
+	if (sp->has_eq)
+		fprintf(outf, "=");
+
+	tmp1 = pb_gets(&sp->buff[LHT_LOC_VAL_PRE]);
+	tmp2 = pb_gets(&sp->buff[LHT_LOC_VAL_POST]);
+	fprintf(outf, "%s", tmp1);
+	if (out_open_chk_brace(sp, nt, tmp1) && out_open_chk_brace(sp, nt, tmp2))
+		fprintf(outf, " {\n");
+
+/* These have different meaning here and precede real list/hash value: */
+	fprintf(outf, "%s", tmp2);
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_TERM]));
+}
+
+static void out_close(FILE *outf, lht_perstyle_t *sp, lht_node_type_t nt, const char *name)
+{
+	fprintf(outf, "%s}%s", pb_gets(&sp->buff[LHT_LOC_NAME_PRE]), pb_gets(&sp->buff[LHT_LOC_VAL_POST]));
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_TERM]));
+}
+
+static void reset_style(lht_perstyle_t *sp)
+{
+	int n;
+
+	if (sp->name != NULL) {
+		free(sp->name);
+		sp->name = NULL;
+	}
+
+	if (sp->text_data != NULL) {
+		free(sp->text_data);
+		sp->text_data = NULL;
+	}
+
+	for(n = 0; n < LHT_LOC_max; n++)
+		pb_reset(&sp->buff[n]);
+	sp->type = LHT_INVALID_TYPE;
+	sp->loc = LHT_LOC_NAME_PRE;
+	sp->has_eq = 0;
+	sp->val_brace = 0;
+	sp->etype = 0;
+	sp->ename = 0;
+	sp->bumped_invalid_char = 0;
+	sp->seen_closing_brc = 0;
+	sp->composite_open = 0;
+	sp->composite_close = 0;
+	sp->name_braced = 0;
+	sp->valid = 0;
+	sp->need_indent = 0;
+	sp->know_indent = 0;
+}
+
+void reset_all_styles(inst_t *p)
+{
+	int n;
+	for(n = 0; n < sizeof(p->last_style) /sizeof(p->last_style[0]); n++)
+		reset_style(&p->last_style[n]);
+}
+
+static lht_perstyle_t *lhtpers_push(lht_perstyle_t *sp)
+{
+	lht_perstyle_t *n = calloc(sizeof(lht_perstyle_t), 1);
+	n->parent = sp;
+	return n;
+}
+
+static lht_perstyle_t *lhtpers_pop(lht_perstyle_t *sp)
+{
+	lht_perstyle_t *ret = sp->parent;
+	reset_style(sp);
+	free(sp);
+	return ret;
+}
+
+#define LHT_CLOSING 0x100
+
+static void finish(lht_pers_t *p, lht_perstyle_t *sp)
+{
+	int delayed_push = 0, delayed_pop = 0;
+	lht_node_t *parent = NULL;
+	inst_t *st;
+	int inhibit = 0, silent_copy = 0;
+
+	sp->valid = 1;
+	sp->need_indent = 0;
+	sp->know_indent = 1;
+
+	parent = inn_peek(p, 0, &st);
+	if (parent != NULL) {
+		if ((sp->type != LHT_CLOSING) && (hooks[parent->type].pre_fin != NULL)  && (sp->parent != NULL) && (st->del_tree == 0) && (st->silent_copy_tree == 0)) {
+			pre_fin_ctrl_t req = hooks[parent->type].pre_fin(p, sp, parent, st);
+			if (req == PF_INHIBIT) {
+				if (is_descendable(sp))
+					st->del_tree = 1;
+				inhibit = 1;
+			}
+			else if (req == PF_SILENT_COPY)  {
+				if (is_descendable(sp))
+					st->silent_copy_tree = 1;
+				silent_copy = 1;
+			}
+		}
+	}
+
+	if ((st != NULL) && (st->del_tree))
+		inhibit = 1;
+	else if ((st != NULL) && (st->silent_copy_tree))
+		silent_copy = 1;
+
+	if ((sp->type == LHT_TEXT) || (sp->type == LHT_SYMLINK)) {
+		if (silent_copy) {
+			out_text(p->outf, sp, sp->name, sp->text_data);
+		}
+		else {
+			if (!inhibit) {
+				if ((hooks[sp->type].pre_fin == NULL) || (hooks[sp->type].pre_fin(p, sp, (st == NULL ? NULL : st->current_node), st) != PF_INHIBIT))
+					out_text(p->outf, sp, sp->name, sp->text_data);
+			}
+		}
+	}
+
+	if (sp->type == LHT_CLOSING) {
+		if (((st == NULL) || ((st->del_tree == 0) && (st->silent_copy_tree == 0))) && (p->inn != NULL)) {
+			inst_t *st2 = NULL;
+			lht_node_t *parent2;
+			parent2 = inn_peek(p, 0, &st2); /* (was: THIS IS A CHEAT, assuming we still have that old itme on the stack after the pop) */
+			ilprintf(p->outf, "##closing type: %d\n", st2->closing_type);
+			if (hooks[st2->closing_type].pre_close != NULL)
+				hooks[st2->closing_type].pre_close(p, sp, parent2, st2);
+		}
+		else if (st != NULL) {
+			if (st->del_tree > 0) {
+				st->del_tree--;
+				if (st->del_tree == 1) /* reached the original level in a close -> release the deltree */
+					st->del_tree = 0;
+			}
+			else if (st->silent_copy_tree > 0) {
+				st->silent_copy_tree--;
+				if (st->silent_copy_tree == 1) /* reached the original level in a close -> release the deltree */
+					st->silent_copy_tree = 0;
+			}
+		}
+		if (!inhibit)
+			out_close(p->outf, sp, sp->type, sp->name);
+	}
+
+	if ((sp->type == LHT_LIST) || (sp->type == LHT_HASH) || (sp->type == LHT_TABLE)) {
+		if ((st != NULL) && (st->del_tree > 0))
+			st->del_tree++;
+		if ((st != NULL) && (st->silent_copy_tree > 0))
+			st->silent_copy_tree++;
+		if (!inhibit)
+			out_open(p->outf, sp, sp->type, sp->name);
+		delayed_push = 1;
+	}
+
+	if ((parent != NULL) && (hooks[parent->type].post_fin != NULL) && (!inhibit)) {
+		inst_t *st2 = NULL;
+		lht_node_t *parent2;
+		parent2 = inn_peek(p, 1, &st2);
+		if (parent2 != NULL)
+			hooks[parent2->type].post_fin(p, sp, parent2, st2);
+	}
+
+	reset_style(sp);
+
+	if (delayed_push)
+		p->curr = lhtpers_push(sp);
+
+	if (delayed_pop)
+		p->curr = lhtpers_pop(sp);
+}
+
+/**** multi-node (subtree) output functions ****/
+#include "output.c"
+
+/**** parsing ****/
+char *evname[]={
+"LHT_OPEN",
+"LHT_CLOSE",
+"LHT_TEXTDATA",
+"LHT_COMMENT",
+"LHT_EOF",
+"LHT_ERROR"
+};
+
+#include "pers_list.c"
+#include "pers_hash.c"
+#include "pers_text.c"
+#include "pers_table.c"
+
+static hook_t hooks[LHT_SYMLINK+1] = {
+	{/* LHT_INVALID_TYPE */  NULL, NULL, NULL},
+	{/* LHT_TEXT */          pers_event_te_pre, NULL, NULL},
+	{/* LHT_LIST */          pers_event_li_pre, pers_event_li_post, pers_event_li_close},
+	{/* LHT_HASH */          pers_event_ha_pre, pers_event_ha_post, pers_event_ha_close},
+	{/* LHT_TABLE */         pers_event_ta_pre, pers_event_ta_post, pers_event_ta_close},
+	{/* LHT_SYMLINK */       pers_event_te_pre, NULL, NULL}
+};
+
+static void pers_event(lht_parse_t *ctx, lht_event_t ev, lht_node_type_t nt, const char *name, const char *value)
+{
+	lht_pers_t *p = (lht_pers_t *)ctx->user_data;
+	lht_perstyle_t *sp = p->curr;
+
+
+	debug_printf("event %s %d\n", evname[ev], ev);
+	switch (ev) {
+		case LHT_OPEN:
+			sp->type = nt;
+			sp->name = lht_strdup(name);
+			sp->loc = LHT_LOC_VAL_POST;
+			sp->composite_open = 1;
+
+			if (p->ind == NULL)
+				break;
+
+			/* descend */
+			if (p->inn == NULL) { /* root node */
+				if ((p->ind->root->type != nt) || (strcmp(p->ind->root->name, name) != 0)) {
+					p->error = 1;
+					printf("ROOT MISMATCH\n");
+				}
+				else
+					inn_push(p, p->ind->root, nt);
+			}
+			break;
+		case LHT_CLOSE:
+			debug_printf("type=%d\n", sp->type);
+			if ((sp->type != LHT_CLOSING) && (sp->type != LHT_INVALID_TYPE)) {
+				finish(p, sp);
+				reset_style(sp);
+				sp = p->curr;
+			}
+			sp->type = LHT_CLOSING;
+			sp->loc = LHT_LOC_VAL_POST;
+			sp->composite_close = 1;
+			break;
+		case LHT_TEXTDATA:
+			sp->type = nt;
+			sp->name = lht_strdup(name);
+
+#if 0
+			/* DISABLED: at the moment the shadow parser seems to follow this correctly */
+			/* strip leading whitespace if not braced */
+			if (!sp->val_brace)
+				while((*value == ' ') || (*value == '\t')) {
+					pb_append(&sp->buff[LHT_LOC_VAL_PRE], *value);
+					value++;
+				}
+#endif
+
+			sp->text_data = lht_strdup(value);
+
+			/* strip trailing whitespace if not braced */
+			if (!sp->val_brace)
+			{
+				char *end;
+				end = sp->text_data + strlen(sp->text_data) - 1;
+				
+				while((end >= sp->text_data) && ((*end == ' ') || (*end == '\t'))) {
+					pb_insert(&sp->buff[LHT_LOC_VAL_POST], *end);
+					*end = '\0';
+					end--;
+				}
+			}
+
+			sp->loc = LHT_LOC_VAL_POST;
+			sp->seen_closing_brc = 0;
+			break;
+		case LHT_COMMENT:
+			break;
+		case LHT_EOF:
+			p->finished = 1;
+			break;
+		case LHT_ERROR:
+			break;
+	}
+
+}
+
+lht_err_t lhtpers_fsave_as(const lhtpers_ev_t *events, lht_doc_t *doc, FILE *inf, FILE *outf, const char *infn, char **errmsg)
+{
+	int error = 0;
+	lht_pers_t pectx;
+	lht_parse_t par;
+	int col = 1, line = 1;
+	int etype, ename;
+	int last_c = 0, c = 0;
+
+	if (infn == NULL)
+		infn = "(unknown)";
+
+	lht_parser_init(&par);
+	memset(&pectx, 0, sizeof(pectx));
+	pectx.curr   = calloc(sizeof(lht_perstyle_t), 1);
+	pectx.outf   = outf;
+	pectx.ind    = doc;
+	pectx.events = events;
+	par.user_data = &pectx;
+	par.event = pers_event;
+
+	if (inf == NULL) { /* there's no on-disk version, need to generate one from scratch */
+		lht_perstyle_t sp;
+		memset(&sp, 0, sizeof(sp));
+		reset_style(&sp);
+		sp.valid = 1;
+		sp.need_indent = 0;
+		sp.know_indent = 1;
+		insert_subtree(&pectx, &sp, &sp, doc->root);
+		goto err;
+	}
+
+
+	/* parse char by char */
+	while(!(feof(inf))) {
+		lht_err_t err;
+		lht_pstate_t pst;
+
+		last_c = c;
+		c = fgetc(inf);
+
+		col++;
+		if (c == '\n') {
+			col = 1;
+			line++;
+		}
+
+/*		debug_printf("= %d '%c' in %d:%d\n", lht_parser_get_state(&par), c, line, col); */
+		if ((pectx.curr->composite_close) && (c == '}'))
+			finish(&pectx, pectx.curr);
+
+		err = lht_parser_char(&par, c);
+
+		if (pectx.error) {
+			error = -5;
+			goto err;
+		}
+
+		if (pectx.curr == NULL) {
+			error = -2; /* broken input, popped too much */
+			goto err;
+		}
+
+		pst = lht_parser_get_state(&par, &etype, &ename);
+		pectx.curr->etype |= etype;
+		pectx.curr->ename |= ename;
+		debug_printf("  %x '%c'\n", pst, c);
+
+
+		if (pst == LHT_ST_COMMENT) {
+			if (pectx.curr->buff[LHT_LOC_NAME_PRE].used > 0) {
+				fprintf(outf, "%s", pb_gets(&pectx.curr->buff[LHT_LOC_NAME_PRE]));
+				reset_style(pectx.curr);
+			}
+			fputc(c, outf);
+			goto skip_normal;
+		}
+
+		if ((c == '\r') || (c == '\n'))
+			pectx.curr->pending_open = 0;
+
+		/* special rules */
+		switch(pectx.curr->loc) {
+			case LHT_LOC_TERM:
+				if ((c == '\r') || (c == '\n') || (c == ';')) {
+					pb_append(&(pectx.curr->buff[pectx.curr->loc]), c);
+					goto skip_normal;
+				}
+				else
+					finish(&pectx, pectx.curr);
+				break;
+
+
+			case LHT_LOC_VAL_POST:
+				if ((c == '{') && ((pectx.curr->type == LHT_LIST) || (pectx.curr->type == LHT_HASH) || (pectx.curr->type == LHT_TABLE))) {
+					pectx.curr->pending_open = 1;
+				}
+
+				if ((c == ' ') || (c == '\t') || ((pectx.curr->composite_open) && (c == '{'))) {
+					pb_append(&(pectx.curr->buff[pectx.curr->loc]), c);
+					pectx.curr->composite_open = 0;
+				}
+				else if ((c == '\r') || (c == '\n') || (c == ';')) {
+					pectx.curr->loc = LHT_LOC_TERM;
+					pb_append(&(pectx.curr->buff[pectx.curr->loc]), c);
+				}
+				else if (c == '}') {
+					if (!pectx.curr->seen_closing_brc)
+						pectx.curr->seen_closing_brc = 1;
+					else
+						break;
+				}
+				else if (pectx.curr->pending_open) {
+					/* a li:, ha: or ta: open in this same line */
+					finish(&pectx, pectx.curr);
+					break; /* first char of a new object: normal processing */
+				}
+				goto skip_normal;
+			case LHT_LOC_NAME_PRE:
+				if (c == '{')
+					pectx.curr->name_braced = 1;
+				else if (c == '=') {
+					pectx.curr->loc = LHT_LOC_VAL_PRE;
+					pectx.curr->has_eq = 1;
+					goto skip_normal;
+				}
+				break;
+			case LHT_LOC_NAME_POST:
+				if (c == '=') {
+					pectx.curr->loc++;
+					pectx.curr->has_eq = 1;
+					goto skip_normal;
+				}
+				if (c == '{') {
+					if (last_c != ':')
+						goto brc_start;
+					else
+						pectx.curr->name_braced = 1;
+				}
+				break;
+			case LHT_LOC_VAL_PRE:
+				if (c == '{') {
+					brc_start:;
+					pectx.curr->loc = LHT_LOC_VAL_IGNORE;
+					pectx.curr->val_brace = 1;
+					goto skip_normal;
+				}
+				if ((c != ' ') && (c != '\t')) {
+					pectx.curr->loc = LHT_LOC_VAL_IGNORE;
+					goto skip_normal;
+				}
+				break;
+			case LHT_LOC_VAL_IGNORE:
+				goto skip_normal;
+			default: break;
+		}
+
+		/* normal rules to determine if we are still at that location */
+		if (strchr(loc_accept[pectx.curr->loc], c) == NULL) {
+			if (!pectx.curr->bumped_invalid_char) {
+				pectx.curr->loc++;
+				pectx.curr->bumped_invalid_char = 1;
+			}
+		}
+		else
+			pb_append(&(pectx.curr->buff[pectx.curr->loc]), c);
+
+		skip_normal:;
+
+		if (err != LHTE_SUCCESS) {
+			if (err != LHTE_STOP) {
+				if (errmsg != NULL) {
+					const char *msg;
+
+					msg = lht_err_str(err);
+					*errmsg = malloc(strlen(msg) + strlen(infn) + 128);
+					sprintf(*errmsg, "%s (%s:%d.%d)\n", msg, infn, line+1, col+1);
+				}
+				error = 1;
+			}
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+		}
+	}
+
+	if (pectx.curr->composite_close)
+		finish(&pectx, pectx.curr);
+
+	{
+		int c;
+		while((c = fgetc(inf)) != EOF)
+			fputc(c, outf);
+	}
+
+err:;
+	free(pectx.inn);
+	lht_parser_uninit(&par);
+	return error;
+}
+
+lht_err_t lhtpers_save_as(const lhtpers_ev_t *events, lht_doc_t *doc, const char *infn, const char *outfn, char **errmsg)
+{
+	FILE *inf, *outf;
+	lht_err_t res;
+
+	outf = fopen(outfn, "wb");
+	if (outf == NULL)
+		return LHTE_NOT_FOUND;
+	inf  = fopen(infn, "rb");
+
+	res = lhtpers_fsave_as(events, doc, inf, outf, infn, errmsg);
+
+	if (inf != NULL)
+		fclose(inf);
+	fclose(outf);
+
+	return res;
+}
+
diff --git a/src_3rd/liblhtpers/lhtpers.h b/src_3rd/liblhtpers/lhtpers.h
new file mode 100644
index 0000000..11f0090
--- /dev/null
+++ b/src_3rd/liblhtpers/lhtpers.h
@@ -0,0 +1,105 @@
+#include <liblihata/dom.h>
+
+/**** Style ****/
+typedef struct {
+	char *s;
+	int used, alloced;
+} lht_persbuf_t;
+
+typedef enum {
+	LHT_LOC_NAME_PRE,
+	LHT_LOC_NAME_POST,
+	LHT_LOC_VAL_PRE,
+	LHT_LOC_VAL_IGNORE,
+	LHT_LOC_VAL_POST,
+	LHT_LOC_TERM,
+	LHT_LOC_max
+} lht_perslht_loc_t;
+
+/* "persistent style" */
+typedef struct lht_perstyle_s lht_perstyle_t;
+struct lht_perstyle_s {
+	/* style - also can be static output conf */
+	lht_persbuf_t buff[LHT_LOC_max];  /* extra whitespace between tokens */
+	unsigned has_eq:1;           /* there's an equation sign between name and value */
+	unsigned val_brace:1;        /* value is braced */
+	unsigned etype:1;            /* explicit type prefix even for te: (or li: in table) */
+	unsigned ename:1;            /* explicit name */
+	unsigned name_braced:1;      /* name is braced */
+
+	/* volatile data */
+	lht_perstyle_t *parent;
+
+	lht_perslht_loc_t loc;
+
+	lht_node_type_t type;
+	char *name;
+	char *text_data;
+
+	/* misc */
+	unsigned bumped_invalid_char:1;
+	unsigned seen_closing_brc:1;
+	unsigned composite_open:1;
+	unsigned composite_close:1;
+
+	unsigned pending_open:1; /* open of li, ha or ta without \n seen */
+
+	unsigned know_indent:1;          /* styled output: we have already calculated the value of need_indent */
+	unsigned need_indent:1;          /* styled output: any of the buff[]s have a * in them */
+	unsigned need_indent_name_pre:1;
+	unsigned need_indent_val_pre:1;
+	unsigned need_indent_val_post:1;
+	unsigned need_indent_term:1;
+
+	unsigned valid:1;  /* 1 if the style is complete */
+};
+
+/**** Output formatting table for new nodes ****/
+typedef struct lhtpers_rule_s lhtpers_rule_t;
+
+struct lhtpers_rule_s {
+	const char **path;                 /* reverse-path match */
+	const lht_perstyle_t *style;       /* styler to use for the current node */
+	lhtpers_rule_t *hash_order;        /* if node is a hash, print children in this order (only first element of the hash should be considered) */
+};
+
+/**** User callbacks (events) ****/
+typedef enum {
+	LHTPERS_DISK,    /* keep the disk-value intact */
+	LHTPERS_MEM,     /* use the value from the in-memory document */
+	LHTPERS_INHIBIT  /* inhibit printing this node */
+} lhtpers_ev_res_t;
+
+typedef struct {
+	/* Called for each text node */
+	lhtpers_ev_res_t (*text)(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_node, const char *ondisk_value);
+
+	/* Called for each child of a list that is empty in-memory but not empty
+	   on-disk; default return: LHTPERS_INHIBIT (so new on-disk version matches
+	   the empty list in-memory); LHTPERS_DISK copies/keeps the on-disk subtree */
+	lhtpers_ev_res_t (*list_empty)(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_parent_node);
+
+	/* Called for a list-child that is present on disk but is removed in-mem.
+	   default return: LHTPERS_INHIBIT (so new on-disk version matches
+	   the list in-memory); LHTPERS_DISK copies/keeps the on-disk child */
+	lhtpers_ev_res_t (*list_elem_removed)(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_parent_node);
+
+	void *ev_ctx;
+
+	lhtpers_rule_t **output_rules; /* an array of rule table pointers */
+	lht_perstyle_t *output_default_style[LHT_SYMLINK+1];
+} lhtpers_ev_t;
+
+/**** Persistent-format save ****/
+lht_err_t lhtpers_save_as(const lhtpers_ev_t *events, lht_doc_t *doc, const char *infn, const char *outfn, char **errmsg);
+lht_err_t lhtpers_fsave_as(const lhtpers_ev_t *events, lht_doc_t *doc, FILE *inf, FILE *outf, const char *infn, char **errmsg);
+
+
+/**** style helper ****/
+/* When this is the path of an ordered hash rule, the corresponding style's
+   NAME_PRE buffer is printed in case no later rules ran from the list, else
+   it is ignored. It is useful to inject a string upon early end-of-the-list. */
+extern const char *lhtpers_early_end[];
+
+/* Find the first rule that matches parent-path of subtree */
+lhtpers_rule_t *lhtpers_rule_find(lhtpers_rule_t *table, lht_node_t *subtree);
diff --git a/src_3rd/liblhtpers/output.c b/src_3rd/liblhtpers/output.c
new file mode 100644
index 0000000..602ae7d
--- /dev/null
+++ b/src_3rd/liblhtpers/output.c
@@ -0,0 +1,428 @@
+static void insert_subtree_by_rules(lht_pers_t *p, const char *ind, lht_node_t *subtree);
+static void export_subtree_by_rule(lht_pers_t *p, const char *ind, lht_node_t *subtree, lhtpers_rule_t *rule);
+
+const char *lhtpers_early_end[] = { "\x01", "\xff", NULL };
+
+static void kill_nl(char *s, int *len)
+{
+	int n;
+	char *i = s, *o = s;
+
+	for(n = 0; n < *len; n++,i++) {
+		if (*i != '\n') {
+			*o = *i;
+			o++;
+		}
+	}
+	*len = n;
+}
+
+static void indent(lht_perstyle_t *sp, lht_perslht_loc_t loc, const char *ind)
+{
+	if (ind != NULL) {
+		lht_persbuf_t p;
+		int l1, l2;
+		const char *s2;
+
+
+		l2 = sp->buff[loc].used;
+		s2 = sp->buff[loc].s;
+		if ((l2 > 0) && (s2[0] == '*')) { /* truncate leading '*' that requested the contatenation we are doing */
+			l1 = strlen(ind);
+			s2++;
+			l2--;
+			p.alloced = l1 + l2;
+			p.s = malloc(p.alloced);
+			memcpy(p.s, ind, l1);
+			kill_nl(p.s, &l1);
+			if (l2 > 0)
+				memcpy(p.s+l1, s2, l2);
+			p.used = l1 + l2;
+		}
+		else if ((l2 > 1) && (s2[l2-2] == '*')) { /* truncate trailing '*' that requested the contatenation we are doing */
+/*			printf("TRAIL\n");*/
+			l1 = strlen(ind);
+			l2 -= 2;
+			p.alloced = l1 + l2 + 1;
+			p.s = malloc(p.alloced);
+			memcpy(p.s, s2, l2);
+			if (l2 > 0) {
+				memcpy(p.s+l2, ind, l1);
+				kill_nl(p.s+l2, &l1);
+			}
+			p.used = l1 + l2;
+		}
+		else
+			return;
+		if (sp->buff[loc].s != NULL)
+			free(sp->buff[loc].s);
+		memcpy(&sp->buff[loc], &p, sizeof(p));
+	}
+	else {
+		/* indent using the first char of the prefix or space */
+		if (sp->buff[loc].used > 0)
+			pb_append(&sp->buff[loc], sp->buff[LHT_LOC_NAME_PRE].s[0]);
+		else
+			pb_append(&sp->buff[loc], ' ');
+	}
+}
+
+/**** export with known style ****/
+static void export_open(FILE *outf, lht_perstyle_t *sp, lht_node_type_t nt, const char *name, lht_node_t *nd)
+{
+	int etype = out_open_chk_etype(sp, nt, nd->parent == NULL ? LHT_INVALID_TYPE : nd->parent->type);
+	const char *tmp;
+	int name_braced = sp->name_braced;
+
+	if ((!name_braced) && (lht_need_brace(nt, name, 1)))
+		name_braced = 1;
+
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_NAME_PRE]));
+	if (name_braced)
+		fprintf(outf, "{");
+	if (etype)
+		fprintf(outf, "%s:", lht_type_id(nt));
+	quote_fprint_name(outf, name);
+	if (name_braced)
+		fprintf(outf, "}");
+	fprintf(outf, "%s", pb_gets(&sp->buff[LHT_LOC_NAME_POST]));
+	if (sp->has_eq)
+		fprintf(outf, "=");
+
+	tmp = pb_gets(&sp->buff[LHT_LOC_VAL_PRE]);
+	fprintf(outf, "%s", tmp);
+	if (out_open_chk_brace(sp, nt, tmp))
+		fprintf(outf, " {\n");
+}
+
+static void export_close(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *subtree)
+{
+	const char *name_pre = pb_gets(&sp->buff[LHT_LOC_NAME_PRE]);
+	const char *term = pb_gets(&sp->buff[LHT_LOC_TERM]);
+
+	/* inherited indentation shouldn't have a \n - let the user add it in TERM if it's needed */
+	/* assume inherited indentation \n's would be leading */
+	while(*name_pre == '\n') name_pre++;
+
+	/* else do the more expensive kill_nl */
+	if (strchr(name_pre, '\n') != NULL) {
+		int len = strlen(name_pre);
+		char *tmp = malloc(len+1);
+		memcpy(tmp, name_pre, len);
+		kill_nl(tmp, &len);
+		tmp[len] = '\0';
+		fprintf(p->outf, "%s", tmp);
+		free(tmp);
+	}
+	else
+		fprintf(p->outf, "%s", name_pre);
+	if ((strchr(name_pre, '}') == NULL) && (strchr(term, '}') == NULL))
+		fprintf(p->outf, "}");
+	fprintf(p->outf, "%s", term);
+}
+
+static void export_list_with_style(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *subtree)
+{
+	const char *ind = pb_gets(&sp->buff[LHT_LOC_NAME_PRE]);
+	lht_node_t *n;
+
+	export_open(p->outf, sp, subtree->type, subtree->name, subtree);
+	for(n = subtree->data.list.first; n != NULL; n = n->next)
+		insert_subtree_by_rules(p, ind, n);
+	export_close(p, sp, subtree);
+}
+
+static void export_hash_with_style(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *subtree)
+{
+	const char *ind = pb_gets(&sp->buff[LHT_LOC_NAME_PRE]);
+	lht_node_t *n;
+	lht_dom_iterator_t it;
+
+	export_open(p->outf, sp, subtree->type, subtree->name, subtree);
+	for(n = lht_dom_first(&it, subtree); n != NULL; n = lht_dom_next(&it))
+		insert_subtree_by_rules(p, ind, n);
+	export_close(p, sp, subtree);
+}
+
+static void export_text_with_style(lht_pers_t *p, lht_perstyle_t *sp_, lht_node_t *subtree)
+{
+	char *c, *postv = pb_gets(&sp_->buff[LHT_LOC_VAL_POST]);
+	lht_perstyle_t sp;
+
+	memcpy(&sp, sp_, sizeof(sp));
+	sp.type = subtree->type;
+
+	c = strchr(postv, '{');
+	if (c != NULL)
+		*c = '\0';
+	out_text(p->outf, &sp, subtree->name, subtree->data.text.value);
+	if (c != NULL)
+		*c = '{';
+}
+
+static void export_table_with_style(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *subtree)
+{
+	/* no idea how to do this yet */
+	const char *pre = pb_gets(&sp->buff[LHT_LOC_NAME_PRE]);
+	lht_dom_export(subtree, p->outf, pre);
+}
+
+/**** export by rules ****/
+static int name_match(const char *name, const char *pat)
+{
+	const char *n, *p;
+	for(n = name, p = pat;; n++,p++) {
+		switch(*p) {
+			case '*':   return 1;
+			case '\0':  return *n == '\0';
+		}
+		if (*n != *p)
+			return 0;
+	}
+}
+
+lhtpers_rule_t *lhtpers_rule_find(lhtpers_rule_t *table, lht_node_t *subtree)
+{
+	for(;table->path != NULL; table++) {
+		const char **p;
+		lht_node_t *n;
+
+/*printf("rmatch:\n");*/
+		/* ascend and follow the rule paths */
+		for(n = subtree, p = table->path; (n != NULL) && (*p != NULL); n = n->parent, p++) {
+			lht_node_type_t want_type;
+			const char *rp = *p;
+
+			if ((rp[0] == '*') && (rp[1] == '\0'))
+				return table; /* found a rule ending in * */
+			if ((rp[0] == '^') && (rp[1] == '\0')) {
+				if (n->parent == NULL)
+					return table; /* found a rule ending in root anchor */
+				continue;
+			}
+
+			/* assume prefixed names in *p */
+			assert(rp[2] == ':');
+			switch(rp[0]) {
+				case 't':
+					if (rp[1] == 'e')
+						want_type = LHT_TEXT;
+					else
+						want_type = LHT_TABLE;
+					break;
+				case 'l': want_type = LHT_LIST; break;
+				case 'h': want_type = LHT_HASH; break;
+				case 's': want_type = LHT_SYMLINK; break;
+				default:
+					goto mismatch;
+			}
+/*printf(" %d==%d %s~%s\n", n->type, want_type, n->name, rp+3);*/
+			if ((want_type != n->type) || (!name_match(n->name, rp+3)))
+				goto mismatch;
+		}
+		mismatch:;
+	}
+	return NULL;
+}
+
+static lhtpers_rule_t *lhtpers_rule_find_any(lhtpers_rule_t **tables, lht_node_t *subtree)
+{
+	lhtpers_rule_t **n, *res;
+	for(n = tables; *n != NULL; n++) {
+		res = lhtpers_rule_find(*n, subtree);
+		if (res != NULL)
+			return res;
+	}
+	return NULL;
+}
+
+static void export_hash_ordered(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *subtree, lhtpers_rule_t *rule)
+{
+	lhtpers_rule_t *r;
+	const char *ind = pb_gets(&sp->buff[LHT_LOC_NAME_PRE]);
+	htsp_t unseen;
+	lht_dom_iterator_t it;
+	lht_node_t *n;
+	htsp_entry_t *e;
+	const char *early_end_str = NULL;
+
+	/* build a hash of items unseen while exporting by order */
+	htsp_init(&unseen, strhash, strkeyeq);
+	for(n = lht_dom_first(&it, subtree); n != NULL; n = lht_dom_next(&it))
+		htsp_set(&unseen, n->name, n);
+
+	/* keep the order described in the rule */
+	export_open(p->outf, sp, subtree->type, subtree->name, subtree);
+	for(r = rule->hash_order; r->path != NULL; r++) {
+		if (r->path == lhtpers_early_end) {
+			early_end_str = r->style->buff[LHT_LOC_NAME_PRE].s;
+			continue;
+		}
+		n = lht_dom_hash_get(subtree, (*r->path)+3);
+		if (n == NULL)
+			continue;
+
+		htsp_pop(&unseen, n->name);
+
+		if (n->type != LHT_INVALID_TYPE) {
+			early_end_str = NULL;
+			if (r->style == NULL)
+				insert_subtree_by_rules(p, ind, n);
+			else
+				export_subtree_by_rule(p, ind, n, r);
+		}
+	}
+
+	if (early_end_str != NULL)
+		fprintf(p->outf, "%s", early_end_str);
+
+	/* write all items that were not in the order-style-list */
+	for(e = htsp_first(&unseen); e; e = htsp_next(&unseen, e))
+		insert_subtree_by_rules(p, ind, e->value);
+	htsp_uninit(&unseen);
+
+/*	out_close(p->outf, (lht_perstyle_t *)rule->style, subtree->type, subtree->name);*/
+	export_close(p, sp, subtree);
+}
+
+/* Default styles for the case there's no style provided */
+#define PB_BEGIN     {"* ", 3, 3}
+#define PB_EMPTY     {"", 1, 1}
+#define PB_SEMICOLON {";", 2, 2}
+#define PB_SPACE     {" ", 2, 2}
+#define PB_LBRACE    {"{", 2, 2}
+#define PB_LBRACENL  {"{\n", 3, 3}
+#define PB_RBRACENL  {"}\n", 3, 3}
+#define PB_NEWLINE   {"\n", 2, 2}
+
+static lht_perstyle_t default_struct = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACENL, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       1,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t default_text = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_SPACE, PB_EMPTY, PB_EMPTY, PB_NEWLINE},
+	/* has_eq */      1,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+
+static void export_subtree_by_rule(lht_pers_t *p, const char *ind, lht_node_t *subtree, lhtpers_rule_t *rule)
+{
+	lht_perstyle_t sp, *isp;
+	int hash_order = 0;
+	
+	if (rule == NULL) {
+		isp = p->events->output_default_style[subtree->type];
+		if (isp == NULL) {
+			if ((subtree->type == LHT_TEXT) || (subtree->type == LHT_SYMLINK))
+				isp = &default_text;
+			else
+				isp = &default_struct;
+		}
+	}
+	else {
+		isp = (lht_perstyle_t *)rule->style;
+		hash_order = (rule->hash_order != NULL);
+	}
+
+	if (!isp->know_indent) {
+		isp->need_indent_name_pre = (strchr(isp->buff[LHT_LOC_NAME_PRE].s, '*') != NULL);
+		isp->need_indent_val_pre  = (strchr(isp->buff[LHT_LOC_VAL_PRE].s, '*') != NULL);
+		isp->need_indent_val_post = (strchr(isp->buff[LHT_LOC_VAL_POST].s, '*') != NULL);
+		isp->need_indent_term     = (strchr(isp->buff[LHT_LOC_TERM].s, '*') != NULL);
+		isp->need_indent = (isp->need_indent_name_pre || isp->need_indent_val_pre || isp->need_indent_val_post || isp->need_indent_term);
+	}
+
+	if ((isp->buff[LHT_LOC_NAME_PRE].used > 0) && (isp->need_indent)) {
+		memset(&sp, 0, sizeof(sp));
+		copy_style(&sp, isp);
+		sp.type = subtree->type;
+		if (isp->need_indent_name_pre) indent(&sp, LHT_LOC_NAME_PRE, ind);
+		if (isp->need_indent_val_pre)  indent(&sp, LHT_LOC_VAL_PRE, ind);
+		if (isp->need_indent_val_post) indent(&sp, LHT_LOC_VAL_POST, ind);
+		if (isp->need_indent_term)     indent(&sp, LHT_LOC_TERM, ind);
+		isp = &sp;
+	}
+
+	switch(subtree->type) {
+		case LHT_HASH:
+			if (hash_order)
+				export_hash_ordered(p, isp, subtree, rule);
+			else
+				export_hash_with_style(p, isp, subtree);
+			break;
+
+		case LHT_TEXT:
+		case LHT_SYMLINK: export_text_with_style(p, isp, subtree);  break;
+		case LHT_LIST:    export_list_with_style(p, isp, subtree);  break;
+		case LHT_TABLE:   export_table_with_style(p, isp, subtree); break;
+
+		case LHT_INVALID_TYPE:
+			break;
+	}
+
+	if (isp == &sp)
+		reset_style(&sp);
+}
+
+static void insert_subtree_by_rules(lht_pers_t *p, const char *ind, lht_node_t *subtree)
+{
+	lhtpers_rule_t *r = lhtpers_rule_find_any(p->events->output_rules, subtree);
+	export_subtree_by_rule(p, ind, subtree, r); /* output by rule */
+}
+
+
+/**** entry point ****/
+
+
+/* Insert a new subtree; try to use sp_ if it is valid (it should be the style
+   last seen for a same-typed node in this context) or else alt_sp (should be
+   the style of the parent) */
+static void insert_subtree(lht_pers_t *p, lht_perstyle_t *sp_, lht_perstyle_t *alt_sp, lht_node_t *subtree)
+{
+	lht_perstyle_t sp, *spp;
+
+	if (!sp_->valid) { /* no style picked up in this context - improvise */
+		/* use sp_ if it has indentation, else try the alternative (parent's) sp */
+		int need_ind = 0;
+
+		spp = sp_;
+		if (spp->buff[LHT_LOC_NAME_PRE].used < 1) {
+			spp = alt_sp;
+			need_ind = 1;
+		}
+		memset(&sp, 0, sizeof(sp));
+		copy_style(&sp, spp);
+		sp.type = subtree->type;
+
+		if (need_ind)
+			indent(&sp, LHT_LOC_NAME_PRE, NULL);
+		pb_append(&sp.buff[LHT_LOC_NAME_PRE], '\0');
+
+		{
+			const char *ind = pb_gets(&sp.buff[LHT_LOC_NAME_PRE]);
+
+			insert_subtree_by_rules(p, ind, subtree);
+		}
+
+		reset_style(&sp);
+		return;
+	}
+
+	memset(&sp, 0, sizeof(sp));
+	copy_style(&sp, sp_);
+	sp.type = subtree->type;
+
+	pb_append(&sp.buff[LHT_LOC_NAME_PRE], '\0');
+
+	insert_subtree_by_rules(p, pb_gets(&sp.buff[LHT_LOC_NAME_PRE]), subtree);
+	reset_style(&sp);
+}
diff --git a/src_3rd/liblhtpers/pers_hash.c b/src_3rd/liblhtpers/pers_hash.c
new file mode 100644
index 0000000..ed45786
--- /dev/null
+++ b/src_3rd/liblhtpers/pers_hash.c
@@ -0,0 +1,86 @@
+/* use htsp because lihata already uses that */
+
+/* called for each on-disk node whose parent is a hash, before printing the node */
+static pre_fin_ctrl_t pers_event_ha_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	/* first call on a new list after a descend (or empty in-memory list)*/
+	if (!parent_st->inited) {
+		lht_dom_iterator_t it;
+		lht_node_t *n;
+		htsp_init(&parent_st->ha_unseen, strhash, strkeyeq);
+
+		for(n = lht_dom_first(&it, indoc_parent); n != NULL; n = lht_dom_next(&it))
+			htsp_set(&parent_st->ha_unseen, n->name, n);
+		parent_st->closing_type = LHT_HASH;
+		parent_st->inited = 1;
+		reset_all_styles(parent_st);
+	}
+
+	/* remember the style of the last children seen */
+	copy_style(&parent_st->last_style[sp->type], sp);
+
+	/* find the same node in the in-memory doc */
+	parent_st->current_node = lht_dom_hash_get(indoc_parent, sp->name);
+
+	/* if the on-disk node is not present in the in-memory hash, inhibit it */
+	if (sp->name != NULL) {
+		if (parent_st->current_node == NULL)
+			return PF_INHIBIT;
+	}
+
+	/* presents both on-disk and in-memory; keep it and mark it as seen
+	   (remove from the unseen hash) */
+	htsp_pop(&parent_st->ha_unseen, sp->name);
+
+	/* if the new node is a list or hash or table... */
+	if (is_descendable(sp)) {
+		/* ... we need to descend we'll skip to next in-memory after ascending */
+		ilprintf(p->outf, "##descend1-ha at=%s\n", sp->name);
+		parent_st = inn_push(p, parent_st->current_node, sp->type);
+	}
+
+	return PF_PROCEED;
+}
+
+/* called for each on-disk node whose parent is a hash, after printing the node */
+static void pers_event_ha_post(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	ilprintf(p->outf, "#post-ha at %s\n", indoc_parent->name);
+
+	if (sp->type == LHT_CLOSING) {
+		/* Closed the list, need to ascend */
+		parent_st = inn_pop(p);
+
+		ilprintf(p->outf, "##ascend1-ha now at %s\n", ((parent_st != NULL) && (parent_st->current_node != NULL)) ? parent_st->current_node->name : "<unknown>");
+	}
+}
+
+/* called for each on-disk hash right before priting the closing brace */
+static void pers_event_ha_close(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	htsp_entry_t *e, *nexte;
+
+	ilprintf(p->outf, "#hash end\n");
+
+	if (!parent_st->inited) { /* corner case: have to fill in an empty hash */
+		lht_dom_iterator_t it;
+		lht_node_t *n;
+
+		for(n = lht_dom_first(&it, indoc_parent); n != NULL; n = lht_dom_next(&it)) {
+			ilprintf(p->outf, "##INSERT2 %s ind='%s'\n", n->name, sp->buff[LHT_LOC_NAME_PRE].s);
+			insert_subtree(p, &parent_st->last_style[n->type], sp, n);
+		}
+		return;
+	}
+
+	/* insert all remaining unseen items */
+	for (e = htsp_first(&parent_st->ha_unseen); e; e = nexte) {
+		lht_node_t *n = e->value;
+		ilprintf(p->outf, "#insert '%s'\n", n->name);
+		insert_subtree(p, &parent_st->last_style[n->type], sp, n);
+		nexte = htsp_next(&parent_st->ha_unseen, e);
+/*		htsp_delentry(&parent_st->ha_unseen, e); - no need to; no dynamic allocation */
+	}
+	htsp_uninit(&parent_st->ha_unseen);
+	reset_all_styles(parent_st);
+}
diff --git a/src_3rd/liblhtpers/pers_list.c b/src_3rd/liblhtpers/pers_list.c
new file mode 100644
index 0000000..b35090d
--- /dev/null
+++ b/src_3rd/liblhtpers/pers_list.c
@@ -0,0 +1,128 @@
+/* called for each on-disk node whose parent is a list, before printing the node */
+static pre_fin_ctrl_t pers_event_li_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	/* first call on a new list after a descend (or empty in-memory list)*/
+	if ((parent_st->nd == NULL) && (!parent_st->inited)) {
+		parent_st->nd = indoc_parent->data.list.first;
+		if (parent_st->nd != NULL)
+			ilprintf(p->outf, "#FIRST '%s'\n", parent_st->nd->name);
+		parent_st->just_entered = 0;
+		parent_st->inited = 1;
+		parent_st->closing_type = LHT_LIST;
+		reset_all_styles(parent_st);
+	}
+
+	ilprintf(p->outf, "#[%d] pre at %s/%s next=%s\n", p->inn_used, indoc_parent->name, sp->name, parent_st->nd != NULL ? parent_st->nd->name : "<none>");
+
+	/* remember the style of the last children seen */
+	copy_style(&parent_st->last_style[sp->type], sp);
+
+	/* empty in-memory list: any children on-disk is to be removed */
+	if (parent_st->nd == NULL) {
+		ilprintf(p->outf, "##REMOVE1 %s\n", sp->name);
+		if (p->events->list_empty != NULL)
+			if (p->events->list_empty(p->events->ev_ctx, sp, inn_peek(p, 1, NULL)) == LHTPERS_DISK)
+				return PF_SILENT_COPY;
+		return PF_INHIBIT;
+	}
+
+	/* if on-disk name doesn't match in-memory name, first look if it's because
+	   the on-disk name is deleted from the list */
+	if (strcmp(sp->name, parent_st->nd->name) != 0) {
+		lht_node_t *n;
+		int found = 0;
+		for(n = parent_st->nd; n != NULL; n = n->next) {
+			if (strcmp(sp->name, n->name) == 0) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			ilprintf(p->outf, "##REMOVE2 '%s'\n", sp->name);
+			if (p->events->list_elem_removed != NULL)
+				if (p->events->list_elem_removed(p->events->ev_ctx, sp, parent_st->nd) == LHTPERS_DISK)
+					return PF_SILENT_COPY;
+			return PF_INHIBIT;
+		}
+	}
+
+	/* parent_st->nd is the next node in-memory, if it doesn't match the node
+	   we are about to write back from disk: it is a new node we need to insert */
+	while(strcmp(sp->name, parent_st->nd->name) != 0) {
+		ilprintf(p->outf, "##INSERT1 %s (!=%s)\n", parent_st->nd->name, sp->name);
+		insert_subtree(p, &parent_st->last_style[parent_st->nd->type], sp, parent_st->nd);
+		parent_st->nd = parent_st->nd->next;
+	}
+
+	/* we are almost in sync: same name, but different type! */
+	if ((strcmp(sp->name, parent_st->nd->name) == 0) && (sp->type != parent_st->nd->type)) {
+		ilprintf(p->outf, "###type mismatch, replace\n");
+		/* inhibit printing the on-disk node; after finishing with that, it's
+		   normal operation: the current in-memory node will look like a new
+		   node and will be printed */
+		return PF_INHIBIT;
+	}
+
+	parent_st->current_node = parent_st->nd;
+
+	if (parent_st->nd != NULL) {
+		/* if the new node is a list or hash or table... */
+		if (is_descendable(sp)) {
+			/* ... we need to descend we'll skip to next in-memory after ascending */
+			ilprintf(p->outf, "##descend1 next=%s\n", parent_st->nd->name);
+			parent_st = inn_push(p, parent_st->nd, sp->type);
+		}
+		else {
+			/* new node is a plain text, skip to next on the in-memory tree */
+			parent_st->nd = parent_st->nd->next;
+		}
+	}
+
+	return PF_PROCEED;
+}
+
+/* called for each on-disk node whose parent is a list, after printing the node */
+static void pers_event_li_post(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	ilprintf(p->outf, "#post at %s\n", indoc_parent->name);
+
+	if (sp->type == LHT_CLOSING) {
+		/* Closed the list, need to ascend */
+		parent_st = inn_pop(p);
+
+		ilprintf(p->outf, "##ascend1 now at %s\n", ((parent_st != NULL) && (parent_st->nd != NULL)) ? parent_st->nd->name : "<unknown>");
+
+		/* we are back one level up, try to skip to next node as the disk reader
+		   does that too */
+		if ((parent_st != NULL) && (parent_st->nd != NULL))
+			parent_st->nd = parent_st->nd->next;
+	}
+}
+
+/* called for each on-disk list right before priting the closing brace */
+static void pers_event_li_close(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	ilprintf(p->outf, "#CLOSE\n");
+
+	/* Corner case: if the on-disk list is empty, we didn't have the chance
+	   to initialize our new level yet; what we need to do is simple anyway:
+	   just dump the list from the in-memory doc */
+	if (parent_st->just_entered) {
+		lht_node_t *n;
+		reset_all_styles(parent_st);
+		for(n = indoc_parent->data.list.first; n != NULL; n = n->next)
+			insert_subtree(p, &parent_st->last_style[n->type], sp, n);
+	}
+	else {
+		/* go through the remaining items of the in-memory list; they are sure not on
+		   the on-disk list because that list has ended so print them */
+		while(parent_st->nd != NULL) {
+			ilprintf(p->outf, "##INSERT2 %s ind='%s'\n", parent_st->nd->name, sp->buff[LHT_LOC_NAME_PRE].s);
+			insert_subtree(p, &parent_st->last_style[parent_st->nd->type], sp, parent_st->nd);
+			parent_st->nd = parent_st->nd->next;
+		}
+	}
+
+	reset_all_styles(parent_st);
+}
+
diff --git a/src_3rd/liblhtpers/pers_table.c b/src_3rd/liblhtpers/pers_table.c
new file mode 100644
index 0000000..22fb749
--- /dev/null
+++ b/src_3rd/liblhtpers/pers_table.c
@@ -0,0 +1,182 @@
+/********** ROW **********/
+
+void insert_trow(lht_pers_t *p, lht_perstyle_t *sp, lht_perstyle_t *alt, lht_node_t *indoc_parent, int row)
+{
+	lht_node_t dnd;
+	int n;
+
+	memset(&dnd, 0, sizeof(dnd));
+	dnd.type = LHT_LIST;
+	dnd.parent = indoc_parent;
+
+	export_open(p->outf, sp, LHT_LIST, "", &dnd);
+
+	for(n = 0; n < indoc_parent->data.table.cols; n++) {
+		lht_node_t *nd = lht_dom_table_cell(indoc_parent, row, n);
+		insert_subtree(p, sp, sp, nd);
+	}
+
+	export_close(p, sp, &dnd);
+
+}
+
+/* called for each on-disk node whose parent is a table, before printing the node */
+static pre_fin_ctrl_t pers_event_trow_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	inst_t *new_parent_st;
+
+	/* first call on a new list after a descend (or empty in-memory list)*/
+	if (!parent_st->inited) {
+		parent_st->trow = 0;
+		if (parent_st->nd != NULL)
+			ilprintf(p->outf, "#FIRST '%s'\n", parent_st->nd->name);
+		parent_st->just_entered = 0;
+		parent_st->inited = 1;
+		parent_st->closing_type = LHT_TABLE;
+		reset_all_styles(parent_st);
+	}
+
+	ilprintf(p->outf, "#[%d] pre at %s/%s next_row=%ld\n", p->inn_used, indoc_parent->name, sp->name, parent_st->trow);
+
+	/* remember the style of the last children seen */
+	copy_style(&parent_st->last_style[LHT_TABLE], sp);
+
+	/* empty in-memory list: any children on-disk is to be removed */
+	if (parent_st->trow >= indoc_parent->data.table.rows) {
+		ilprintf(p->outf, "##REMOVE1 %s\n", sp->name);
+/*
+		if (p->events->table_empty != NULL)
+			if (p->events->table_empty(p->events->ev_ctx, sp, inn_peek(p, 1, NULL)) == LHTPERS_DISK)
+				return PF_SILENT_COPY;
+*/
+		return PF_INHIBIT;
+	}
+
+	ilprintf(p->outf, "##descend1 next_row=%d\n", parent_st->trow);
+	new_parent_st = inn_push(p, indoc_parent, LHT_TABLE);
+	new_parent_st->table_inrow = 1;
+	new_parent_st->inited = 1;
+	new_parent_st->trow = parent_st->trow;
+	new_parent_st->tcol = -1;
+
+	return PF_PROCEED;
+}
+
+/* called for each on-disk node whose parent is a table, after printing the table row */
+static void pers_event_trow_post(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	ilprintf(p->outf, "#post at %s\n", indoc_parent->name);
+	if (sp->type == LHT_CLOSING) {
+		/* Closed the table, need to ascend */
+		parent_st = inn_pop(p);
+
+		ilprintf(p->outf, "##ascend1 now at %s\n", ((parent_st != NULL) && (parent_st->nd != NULL)) ? parent_st->nd->name : "<unknown>");
+
+		/* we are back one level up, try to skip to next node as the disk reader
+		   does that too */
+		if ((parent_st != NULL) && (parent_st->trow <= indoc_parent->data.table.rows))
+			parent_st->trow++;
+	}
+}
+
+/* called for each on-disk list right before priting the table closing brace */
+static void pers_event_trow_close(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	int n;
+	ilprintf(p->outf, "#CLOSE\n");
+
+	/* Corner case: if the on-disk table is empty, we didn't have the chance
+	   to initialize our new level yet; what we need to do is simple anyway:
+	   just dump the list from the in-memory doc */
+	if (parent_st->just_entered) {
+		reset_all_styles(parent_st);
+		for(n = 0; n < indoc_parent->data.table.rows; n++)
+			insert_trow(p, &parent_st->last_style[LHT_TABLE], sp, indoc_parent, n);
+	}
+	else {
+		/* go through the remaining items of the in-memory list; they are sure not on
+		   the on-disk list because that list has ended so print them */
+		for(n = parent_st->trow; n < indoc_parent->data.table.rows; n++) {
+			ilprintf(p->outf, "##INSERT2 %d\n", n);
+			insert_trow(p, &parent_st->last_style[LHT_TABLE], sp, indoc_parent, n);
+		}
+	}
+
+	reset_all_styles(parent_st);
+}
+
+/********** COL **********/
+/* called for each on-disk node whose parent is a table row, before printing the node */
+static pre_fin_ctrl_t pers_event_tcol_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	lht_node_t *nd = lht_dom_table_cell(indoc_parent, parent_st->trow, parent_st->tcol);
+
+	/* different type: output the new subtree, omit the original */
+	if (sp->type != nd->type) {
+		ilprintf(p->outf, "###type mismatch, replace\n");
+		insert_subtree(p, &parent_st->last_style[nd->type], sp, nd);
+		parent_st->tcol++;
+		return PF_INHIBIT;
+	}
+
+	/* remember the style of the last children seen */
+	copy_style(&parent_st->last_style[sp->type], sp);
+
+	/* same type, different name: fix up sp */
+	if (strcmp(nd->name, sp->name) != 0) {
+		free(sp->name);
+		sp->name = lht_strdup(nd->name);
+	}
+
+	/* set this before descending to the cell data so cell data handlers
+	   know where we are in the memory-doc tree */
+	parent_st->current_node = nd;
+
+	return PF_PROCEED;
+}
+
+/* called for each on-disk node whose parent is a table row, after printing the table col */
+static void pers_event_tcol_post(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	parent_st->tcol++;
+}
+
+/* called for each on-disk list right before priting the row closing brace */
+static void pers_event_tcol_close(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	int n;
+	/* print all the remaining nodes */
+	for(n = parent_st->tcol; n < indoc_parent->data.table.cols; n++) {
+		lht_node_t *nd = lht_dom_table_cell(indoc_parent, parent_st->trow, n);
+		insert_subtree(p, &parent_st->last_style[nd->type], sp, nd);
+	}
+	parent_st->table_inrow = 0;
+}
+
+/********** DISPATCH **********/
+
+/* called for each on-disk node whose parent is a table, before printing the node */
+static pre_fin_ctrl_t pers_event_ta_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	if ((!parent_st->inited) || (!parent_st->table_inrow))
+		return pers_event_trow_pre(p, sp, indoc_parent, parent_st);
+	return pers_event_tcol_pre(p, sp, indoc_parent, parent_st);
+}
+
+static void pers_event_ta_post(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	inst_t *st;
+	inn_peek(p, 0, &st);
+	if (st->table_inrow)
+		pers_event_tcol_post(p, sp, indoc_parent, st);
+	else
+		pers_event_trow_post(p, sp, indoc_parent, st);
+}
+
+static void pers_event_ta_close(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
+{
+	if (parent_st->table_inrow)
+		pers_event_tcol_close(p, sp, indoc_parent, parent_st);
+	else
+		pers_event_trow_close(p, sp, indoc_parent, parent_st);
+}
diff --git a/src_3rd/liblhtpers/pers_text.c b/src_3rd/liblhtpers/pers_text.c
new file mode 100644
index 0000000..ef55d90
--- /dev/null
+++ b/src_3rd/liblhtpers/pers_text.c
@@ -0,0 +1,34 @@
+/* called for each on-disk node that is a text or symlink, before printing the node */
+static pre_fin_ctrl_t pers_event_te_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_node, inst_t *parent_st)
+{
+	lhtpers_ev_res_t r;
+
+	ilprintf(p->outf, "#text '%s' indoc='%s'\n", sp->name, indoc_node == NULL ? "<no node>" : indoc_node->name);
+
+	/* check if the values are equivalent and replace the on-disk variant if they are not */
+	if (p->events->text == NULL) {
+		/* default method: case sensitive string comparison */
+		if (strcmp(sp->text_data, indoc_node->data.text.value) == 0)
+			r = LHTPERS_DISK;
+		else
+			r = LHTPERS_MEM;
+	}
+	else if (indoc_node != NULL)
+		r = p->events->text(p->events->ev_ctx, sp, indoc_node, sp->text_data);
+	else
+		r = LHTPERS_DISK; /* no in-memory doc */
+
+	switch(r) {
+		case LHTPERS_DISK:
+			return PF_PROCEED;
+		case LHTPERS_MEM:
+			free(sp->text_data);
+			sp->text_data = lht_strdup(indoc_node->data.text.value);
+			break;
+		case LHTPERS_INHIBIT:
+			return PF_INHIBIT;
+	}
+
+	/* can get here only if the user call messed up the return value */
+	return PF_PROCEED;
+}
diff --git a/src_3rd/liblhtpers/tests/Makefile b/src_3rd/liblhtpers/tests/Makefile
new file mode 100644
index 0000000..3c99e4a
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/Makefile
@@ -0,0 +1,26 @@
+ROOT=../..
+CFLAGS = -Wall -g -I$(ROOT)
+LIHATA=$(ROOT)/liblihata/parser.o $(ROOT)/liblihata/liblihata.a $(ROOT)/liblihata/genht/hash.o
+OBJS_LIB = ../lhtpers.o
+OBJS_TESTER = perstest.o
+OBJS = $(OBJS_LIB) $(OBJS_TESTER) $(LIHATA)
+
+all: perstest
+
+# dependencies
+
+include Makefile.dep
+
+perstest: $(OBJS)
+
+test:
+	@cd roundtrip && make test
+
+depend:
+	@echo "# Generated by \"make depend\"" > Makefile.dep
+	gcc $(CFLAGS) -MM `echo $(OBJS) | sed "s/\.o/.c/g"` >> Makefile.dep
+
+clean:
+	-rm $(OBJS) test_simple $(OBJS) 2>/dev/null
+
+include $(ROOT)/Makefile.common
diff --git a/src_3rd/liblhtpers/tests/Makefile.dep b/src_3rd/liblhtpers/tests/Makefile.dep
new file mode 100644
index 0000000..e69de29
diff --git a/src_3rd/liblhtpers/tests/edit/li1.lht b/src_3rd/liblhtpers/tests/edit/li1.lht
new file mode 100644
index 0000000..3e28322
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1.lht
@@ -0,0 +1,14 @@
+# comment
+li:ja {
+	k1=t1
+	k2=t1
+ li:2nd  {		
+	key1=val1
+	li:empty {
+	}
+	key2  =	{val2}  ;
+	valonly 
+ } 
+	k3=t1
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/li1a.lht b/src_3rd/liblhtpers/tests/edit/li1a.lht
new file mode 100644
index 0000000..7e699f7
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1a.lht
@@ -0,0 +1,13 @@
+# remove an item
+li:ja {
+	k1=t1
+ li:2nd  {		
+	key1=val1
+	li:empty {
+	}
+	key2  =	{val2}  ;
+	valonly 
+ } 
+	k3=t1
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/li1b.lht b/src_3rd/liblhtpers/tests/edit/li1b.lht
new file mode 100644
index 0000000..03da35f
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1b.lht
@@ -0,0 +1,12 @@
+# remove items
+li:ja {
+	k1=t1
+ li:2nd  {		
+	key1=val1
+	li:empty {
+	}
+	key2  =	{val2}  ;
+	valonly 
+ } 
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/li1c.lht b/src_3rd/liblhtpers/tests/edit/li1c.lht
new file mode 100644
index 0000000..1ede441
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1c.lht
@@ -0,0 +1,13 @@
+# deep remove item
+li:ja {
+	k1=t1
+	k2=t1
+ li:2nd  {		
+	key1=val1
+	li:empty {
+	}
+	key2  =	{val2}  ;
+ } 
+	k3=t1
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/li1d.lht b/src_3rd/liblhtpers/tests/edit/li1d.lht
new file mode 100644
index 0000000..164e307
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1d.lht
@@ -0,0 +1,20 @@
+# add items
+li:ja {
+	k1=t1
+NEW=1
+	k2=t1
+ li:2nd  {		
+	key1=val1
+NEW=2
+	li:empty {
+NEW=3
+	}
+	key2  =	{val2}  ;
+NEW=4
+	valonly 
+	NEW=5
+ } 
+	k3=t1
+NEW=6
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/li1e.lht b/src_3rd/liblhtpers/tests/edit/li1e.lht
new file mode 100644
index 0000000..9573137
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1e.lht
@@ -0,0 +1,13 @@
+# overwrite list with text
+li:ja {
+	k1=t1
+	k2=t1
+ li:2nd  {		
+	key1=val1
+empty=none
+	key2  =	{val2}  ;
+	valonly 
+ } 
+	k3=t1
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/li1f.lht b/src_3rd/liblhtpers/tests/edit/li1f.lht
new file mode 100644
index 0000000..b6a19e5
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/li1f.lht
@@ -0,0 +1,5 @@
+# delete subtree and some nodes
+li:ja {
+	k1=t1
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/lit.lht b/src_3rd/liblhtpers/tests/edit/lit.lht
new file mode 100644
index 0000000..3e28322
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/lit.lht
@@ -0,0 +1,14 @@
+# comment
+li:ja {
+	k1=t1
+	k2=t1
+ li:2nd  {		
+	key1=val1
+	li:empty {
+	}
+	key2  =	{val2}  ;
+	valonly 
+ } 
+	k3=t1
+	k4=t1
+}
diff --git a/src_3rd/liblhtpers/tests/edit/lita.lht b/src_3rd/liblhtpers/tests/edit/lita.lht
new file mode 100644
index 0000000..4bc3c0f
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/edit/lita.lht
@@ -0,0 +1,14 @@
+# replace the value of each item and see if formatting is preserved
+li:ja {
+	k1=T1
+	k2=T1
+ li:2nd  {		
+	key1=Val1
+	li:empty {
+	}
+	key2  =	{Val2}  ;
+	Valonly 
+ } 
+	k3=T1
+	k4=T1
+}
diff --git a/src_3rd/liblhtpers/tests/list.lht b/src_3rd/liblhtpers/tests/list.lht
new file mode 100644
index 0000000..c35d407
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/list.lht
@@ -0,0 +1,8 @@
+# comment
+li:ja {
+ li:lo  {		
+	key1=val1
+	key2  =	{val2}  ;
+	valonly 
+ } 
+}
diff --git a/src_3rd/liblhtpers/tests/perstest.c b/src_3rd/liblhtpers/tests/perstest.c
new file mode 100644
index 0000000..11ce20a
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/perstest.c
@@ -0,0 +1,151 @@
+#include <stdlib.h>
+#include <string.h>
+#include <liblhtpers/lhtpers.h>
+
+#define PB_BEGIN     {"* ", 3, 3}
+#define PB_BEGINNL   {"\n *", 4, 4}
+#define PB_EMPTY     {"", 1, 1}
+#define PB_SEMICOLON {";", 2, 2}
+#define PB_SPACE     {" ", 2, 2}
+#define PB_LBRACE    {"{", 2, 2}
+#define PB_LBRACENL  {"{\n", 3, 3}
+#define PB_LBRACEI   {"{\n *", 5, 5}
+#define PB_RBRACE    {"}", 2, 2}
+#define PB_RBRACENL  {"}\n", 3, 3}
+#define PB_RBRACESC  {"};", 3, 3}
+#define PB_NEWLINE   {"\n", 2, 2}
+#define PB_DEFAULT   {NULL, 0, 0}
+
+lht_perstyle_t style_inline = {
+	/* buff */        {PB_SPACE, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_SEMICOLON},
+	/* has_eq */      1,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+lht_perstyle_t style_newline = {
+	/* buff */        {PB_BEGIN, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_NEWLINE},
+	/* has_eq */      1,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+lht_perstyle_t style_istruct = {
+	/* buff */        {PB_SPACE, PB_SPACE, PB_LBRACE, PB_EMPTY, PB_EMPTY, PB_RBRACESC},
+	/* has_eq */      1,
+	/* val_brace */   1,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+lht_perstyle_t style_struct = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACE, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+lht_perstyle_t style_structi = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACEI, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+lht_perstyle_t style_nlstruct = {
+	/* buff */        {PB_BEGINNL, PB_SPACE, PB_LBRACENL, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+lhtpers_rule_t r_ilists[] = {
+	{(const char *[]){"te:*", "ha:flags", "*", NULL},       &style_inline, NULL},
+	{(const char *[]){"te:*", "ha:attributes", "*", NULL},  &style_newline, NULL},
+	{NULL, NULL, NULL}
+};
+
+lhtpers_rule_t r_line[] = {
+	{(const char *[]){"te:x1", "ha:line.*", "*", NULL},         &style_inline, NULL},
+	{(const char *[]){"te:y1", "ha:line.*", "*", NULL},         &style_inline, NULL},
+	{(const char *[]){"te:x2", "ha:line.*", "*", NULL},         &style_inline, NULL},
+	{(const char *[]){"te:y2", "ha:line.*", "*", NULL},         &style_inline, NULL},
+	{(const char *[]){"te:thickness", "ha:line.*", "*", NULL},  &style_inline, NULL},
+	{(const char *[]){"te:clearance", "ha:line.*", "*", NULL},  &style_inline, NULL},
+	{(const char *[]){"ha:flags", "*", "ha:line.*", NULL},      &style_istruct, NULL},
+	{(const char *[]){"ha:attributes", "ha:line.*", "*", NULL}, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+lhtpers_rule_t r_data[] = {
+	{(const char *[]){"ha:line.*", "*", NULL}, &style_structi, r_line, NULL},
+	{(const char *[]){"te:*", "ta:*", "*", NULL}, &style_inline, NULL, NULL},
+
+	{NULL, NULL, NULL}
+};
+
+lhtpers_rule_t *rules[] = {
+	r_data, r_ilists, r_line, NULL
+};
+
+
+static lhtpers_ev_res_t check_text(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_node, const char *ondisk_value)
+{
+	/* if node name starts with num, do a numeric comparison and keep on-disk format if the double values match */
+	if (strncmp(inmem_node->name, "num", 3) == 0) {
+		double mem= strtod(inmem_node->data.text.value, NULL), disk = strtod(ondisk_value, NULL);
+		if (mem != disk)
+			return LHTPERS_MEM;
+	}
+	if (strcmp(inmem_node->data.text.value, ondisk_value) != 0)
+		return LHTPERS_MEM;
+
+	return LHTPERS_DISK;
+}
+
+static lhtpers_ev_res_t check_list_empty(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_parent_node)
+{
+	return LHTPERS_INHIBIT;
+}
+
+static lhtpers_ev_res_t check_list_elem_removed(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_parent_node)
+{
+	return LHTPERS_INHIBIT;
+}
+
+int main(int argc, char *argv[])
+{
+	lht_doc_t *inp = NULL;
+	lhtpers_ev_t events;
+	char out[256];
+
+	memset(&events, 0, sizeof(events));
+	events.text = check_text;
+	events.list_empty = check_list_empty;
+	events.list_elem_removed = check_list_elem_removed;
+	events.output_rules = rules;
+	sprintf(out, "%s.out", argv[1]);
+
+	if (argv[2] != NULL) {
+		char *errmsg;
+		inp = lht_dom_load(argv[2], &errmsg);
+		if (inp == NULL) {
+			fprintf(stderr, "Can't open input doc %s: %s\n", argv[2], errmsg);
+			return 1;
+		}
+	}
+
+	lht_err_t e = lhtpers_save_as(&events, inp, argv[1], out, NULL);
+	return 0;
+}
diff --git a/src_3rd/liblhtpers/tests/roundtrip/Makefile b/src_3rd/liblhtpers/tests/roundtrip/Makefile
new file mode 100644
index 0000000..dfe708f
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/roundtrip/Makefile
@@ -0,0 +1,32 @@
+SRC=../../..
+ORIG=$(SRC)/liblihata/regression
+
+# list of the original lihata test documents that do not contain error
+TESTS_ORIG= \
+  $(ORIG)/empty.lht \
+  $(ORIG)/excess_closing_braces.lht \
+  $(ORIG)/hash.lht \
+  $(ORIG)/list.lht \
+  $(ORIG)/list_del_nth.lht \
+  $(ORIG)/list_empty.lht \
+  $(ORIG)/list_first_symlink.lht \
+  $(ORIG)/list_missing_last_semicolon.lht \
+  $(ORIG)/list_nthname.lht \
+  $(ORIG)/merge_hash.lht \
+  $(ORIG)/nodes_for_testing.lht \
+  $(ORIG)/revpath.lht \
+  $(ORIG)/stream.lht \
+  $(ORIG)/sy.lht \
+  $(ORIG)/symlink.lht \
+  $(ORIG)/symlink_root_node.lht \
+  $(ORIG)/ta.lht \
+  $(ORIG)/table.lht \
+  $(ORIG)/table_corners.lht \
+  $(ORIG)/table_named_cell.lht \
+  $(ORIG)/text.lht \
+  $(ORIG)/text_root_node.lht \
+  $(ORIG)/tiny.lht
+
+test:
+	@./test.sh $(TESTS_ORIG) *.lht
+
diff --git a/src_3rd/liblhtpers/tests/roundtrip/list.lht b/src_3rd/liblhtpers/tests/roundtrip/list.lht
new file mode 100644
index 0000000..c35d407
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/roundtrip/list.lht
@@ -0,0 +1,8 @@
+# comment
+li:ja {
+ li:lo  {		
+	key1=val1
+	key2  =	{val2}  ;
+	valonly 
+ } 
+}
diff --git a/src_3rd/liblhtpers/tests/roundtrip/test.sh b/src_3rd/liblhtpers/tests/roundtrip/test.sh
new file mode 100755
index 0000000..9632817
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/roundtrip/test.sh
@@ -0,0 +1,18 @@
+for lht in $*
+do
+	../perstest $lht
+	diff -u $lht $lht.out 
+	if test $? = 0
+	then
+		rm $lht.out
+	else
+		fail="$fail $lht"
+	fi
+done
+
+if test -z "$fail"
+then
+	echo "*** All good. QC PASS. ***"
+else
+	echo "Failed: $fail"
+fi
\ No newline at end of file
diff --git a/src_3rd/liblhtpers/tests/roundtrip/text.lht b/src_3rd/liblhtpers/tests/roundtrip/text.lht
new file mode 100644
index 0000000..a71a8db
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/roundtrip/text.lht
@@ -0,0 +1,17 @@
+li:root = {
+	key1=val1
+	key2 =val2;
+	key3={val3}
+	key4=val4;
+	key5 {val5}
+	{key6} val6
+	anon1
+	{anon2}
+	anon3;
+	empty={}
+	empty2=
+	{} {}
+	=
+	te:{foo}
+	key7= spaces ;
+}
diff --git a/src_3rd/liblhtpers/tests/roundtrip/text2.lht b/src_3rd/liblhtpers/tests/roundtrip/text2.lht
new file mode 100644
index 0000000..0e15bbb
--- /dev/null
+++ b/src_3rd/liblhtpers/tests/roundtrip/text2.lht
@@ -0,0 +1,15 @@
+#c { m	t }
+	key1  =	{va    l1};
+	# ju jj
+	key2 {va    l2};
+
+	foo;bar;
+
+	k1=v1
+	k2 
+	k3  
+
+	key1=val1
+	key2  =	{val2}  ;
+	valonly
+
diff --git a/src_3rd/liblihata/Makefile b/src_3rd/liblihata/Makefile
new file mode 100644
index 0000000..2441678
--- /dev/null
+++ b/src_3rd/liblihata/Makefile
@@ -0,0 +1,69 @@
+ROOT=..
+CFLAGS = -Wall -g -I..
+OBJS_PARSER = parser.o lihata.o
+OBJS_DOM = hash_str.o dom.o dom_hash.o dom_list.o dom_table.o genht/htsp.o
+OBJS_TREE = tree.o tree_list.o tree_hash.o tree_table.o tree_path.o tree_symlink.o
+OBJS = test_parser.o test_dom.o test_tree.o  $(OBJS_PARSER) $(OBJS_DOM) $(OBJS_TREE)
+TESTERS = test_parser test_dom test_tree
+LIBS = liblihata.a
+include $(ROOT)/Makefile.conf
+
+
+all: $(TESTERS) $(LIBS)
+
+test_parser: test_parser.o $(OBJS_PARSER)
+	$(CC) $(LDFLAGS) test_parser.o $(OBJS_PARSER) -o $@
+
+test_dom: test_dom.o $(OBJS_DOM) $(OBJS_PARSER)
+	$(CC) $(LDFLAGS) test_dom.o $(OBJS_DOM) $(OBJS_PARSER) -o $@
+
+test_tree: test_tree.o $(OBJS_TREE) $(OBJS_DOM) $(OBJS_PARSER)
+	$(CC) $(LDFLAGS) test_tree.o $(OBJS_TREE) $(OBJS_DOM) $(OBJS_PARSER) -o $@
+
+liblihata.a: $(OBJS_DOM) $(OBJS_PARSER) $(OBJS_TREE)
+	ar rvu $@ $(OBJS_DOM) $(OBJS_PARSER) $(OBJS_TREE)
+	ranlib $@
+
+genht/htsp.o:
+	$(CC) $(CFLAGS) -c genht/htsp.c -o genht/htsp.o
+
+# dependencies
+
+include Makefile.dep
+
+depend:
+	@echo "# Generated by \"make depend\"" > Makefile.dep
+	gcc $(CFLAGS) -MM `echo $(OBJS) | sed "s/\.o/.c/g"` | sed "s%^htsp.o%genht/htsp.o%" >> Makefile.dep
+
+ns: liblihata.a
+	@echo Namespace pollution in objects:
+	@nm liblihata.a | awk '/ [ABCDGNRSTVW] / { if (($$(NF) ~ "^lht_") || ($$(NF) ~ "^hts._")) next; print $$0}'
+
+test:
+	cd regression && make
+
+clean:
+	-rm $(OBJS) $(TESTERS) $(LIBS) 2>/dev/null
+
+dist-clean: clean
+
+install_:
+	mkdir -p $(LIBDIR) $(INCDIR)
+	$(CP) $(PWD)/liblihata.a $(LIBDIR)/liblihata.a
+	$(CP) $(PWD)/dom.h $(INCDIR)/dom.h
+	$(CP) $(PWD)/hash_str.h $(INCDIR)/hash_str.h
+	$(CP) $(PWD)/lihata.h $(INCDIR)/lihata.h
+	$(CP) $(PWD)/parser.h $(INCDIR)/parser.h
+	$(CP) $(PWD)/tree.h $(INCDIR)/tree.h
+
+uninstall:
+	rm $(LIBDIR)/liblihata.a
+	rm -rf $(INCDIR)
+
+install:
+	make install_ CP="cp" PWD=`pwd`
+
+linstall:
+	make install_ CP="ln -sf" PWD=`pwd`
+
+include $(ROOT)/Makefile.common
diff --git a/src_3rd/liblihata/Makefile.dep b/src_3rd/liblihata/Makefile.dep
new file mode 100644
index 0000000..863b74d
--- /dev/null
+++ b/src_3rd/liblihata/Makefile.dep
@@ -0,0 +1,34 @@
+# Generated by "make depend"
+test_parser.o: test_parser.c parser.h lihata.h
+test_dom.o: test_dom.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h
+test_tree.o: test_tree.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h tree.h ../liblihata/dom.h
+parser.o: parser.c parser.h lihata.h
+lihata.o: lihata.c lihata.h
+hash_str.o: hash_str.c
+dom.o: dom.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h dom_internal.h hash_str.h
+dom_hash.o: dom_hash.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h dom_internal.h hash_str.h
+dom_list.o: dom_list.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h dom_internal.h
+dom_table.o: dom_table.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h dom_internal.h tree.h ../liblihata/dom.h
+genht/htsp.o: genht/htsp.c genht/htsp.h genht/ht.h genht/ht_inlines.h \
+ genht/ht.c
+tree.o: tree.c ../liblihata/dom.h ../liblihata/lihata.h \
+ ../liblihata/parser.h ../liblihata/genht/htsp.h ../liblihata/genht/ht.h \
+ ../liblihata/genht/ht_inlines.h ../liblihata/tree.h
+tree_list.o: tree_list.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h dom_internal.h
+tree_hash.o: tree_hash.c dom.h lihata.h parser.h genht/htsp.h genht/ht.h \
+ genht/ht_inlines.h dom_internal.h
+tree_table.o: tree_table.c dom.h lihata.h parser.h genht/htsp.h \
+ genht/ht.h genht/ht_inlines.h dom_internal.h
+tree_path.o: tree_path.c ../liblihata/dom.h ../liblihata/lihata.h \
+ ../liblihata/parser.h ../liblihata/genht/htsp.h ../liblihata/genht/ht.h \
+ ../liblihata/genht/ht_inlines.h ../liblihata/tree.h
+tree_symlink.o: tree_symlink.c ../liblihata/dom.h ../liblihata/lihata.h \
+ ../liblihata/parser.h ../liblihata/genht/htsp.h ../liblihata/genht/ht.h \
+ ../liblihata/genht/ht_inlines.h ../liblihata/tree.h
diff --git a/src_3rd/liblihata/Makefile.module b/src_3rd/liblihata/Makefile.module
new file mode 100644
index 0000000..7f7daa1
--- /dev/null
+++ b/src_3rd/liblihata/Makefile.module
@@ -0,0 +1,4 @@
+# module Makefile
+
+$(ROOT)/liblihata/liblihata.a:
+	cd $(ROOT)/liblihata && make all
diff --git a/src_3rd/liblihata/dom.c b/src_3rd/liblihata/dom.c
new file mode 100644
index 0000000..3dbfd6f
--- /dev/null
+++ b/src_3rd/liblihata/dom.c
@@ -0,0 +1,801 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Gabor Horvath (HvG)
+    Copyright (C) 2013, 2016  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains the DOM parser, uses the generic API.
+*/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "dom.h"
+#include "dom_internal.h"
+#include "hash_str.h"
+
+/* set location info of the node */
+static void set_loc(lht_node_t *node, const char *file_name, int line, int col)
+{
+	node->file_name = file_name;
+	node->line = line;
+	node->col = col;
+}
+
+/* append a child node to a parent - for most types we find out if it's impossible
+   before we start processing an elaborate subtree. Tree is an exception. */
+static lht_err_t append(lht_node_t *parent, lht_node_t *child)
+{
+	switch (parent->type) {
+		case LHT_LIST:
+			return lht_dom_list_append(parent, child);
+		case LHT_HASH:
+			return lht_dom_hash_put(parent, child);
+		case LHT_TABLE:
+			child->parent = parent;
+			return LHTE_SUCCESS; /* we do the actual job in post-process */
+
+/* text can't have children; if we got here, the dom parser got confused */
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+			return LHTE_INTERNAL_PARSER;
+
+/* these types shall not show up in the tree */
+		case LHT_INVALID_TYPE:
+			return LHTE_INTERNAL_PARSER;
+
+/* no default: the compiler can emit a warning for unhandled cases... */
+	}
+
+	/* ... and fall back here */
+	return LHTE_INTERNAL_PARSER;
+}
+
+/* post process child after it's been closed. For table rows, this is the
+   place where we have enough info to insert the row in the table. */
+static lht_err_t post_process(lht_doc_t *p)
+{
+	lht_err_t err;
+	lht_node_t *row_list;
+
+	if (p->active->parent == NULL) {
+		p->active = p->active->parent;
+		return LHTE_SUCCESS;
+	}
+
+	switch (p->active->parent->type) {
+		case LHT_TABLE:
+			row_list = p->active;
+			err = lht_dom_table_post_process(p->active->parent, row_list);
+			/* the row list is not in use any more, free it */
+			p->active = p->active->parent;
+			lht_dom_node_free(row_list);
+			return err;
+		/* no post processing required, but set active back to the parent */
+		case LHT_LIST:
+		case LHT_HASH:
+			p->active = p->active->parent;
+			return LHTE_SUCCESS;
+
+/* text can't have children; if we got here, the dom parser got confused */
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+			return LHTE_INTERNAL_PARSER;
+
+/* these types shall not show up in the tree */
+		case LHT_INVALID_TYPE:
+			return LHTE_INTERNAL_PARSER;
+
+/* no default: the compiler can emit a warning for unhandled cases... */
+	}
+
+	/* ... and fall back here */
+	return LHTE_INTERNAL_PARSER;
+}
+
+#define append_root_or_child() \
+	do { \
+		if (p->root != NULL) {\
+			lht_err_t err; \
+			err = append(p->active, newnode); \
+			if (err != LHTE_SUCCESS) { \
+				p->perror = err; \
+				p->finished = 1; \
+			} \
+		} \
+		else { \
+			p->root = newnode; \
+		} \
+	} while(0)
+
+static void event(lht_parse_t *ctx, lht_event_t ev, lht_node_type_t nt, const char *name, const char *value)
+{
+	lht_node_t *newnode;
+
+	lht_doc_t *p = (lht_doc_t *)ctx->user_data;
+
+	switch (ev) {
+		case LHT_OPEN:
+			newnode = lht_dom_node_alloc(nt, name);
+			newnode->doc = p;
+			set_loc(newnode, p->active_file, ctx->line, ctx->col);
+			append_root_or_child();
+			p->active = newnode;
+			break;
+		case LHT_CLOSE:
+			post_process(p);
+			break;
+		case LHT_TEXTDATA:
+			newnode = lht_dom_node_alloc(nt, name);
+			newnode->doc = p;
+			set_loc(newnode, p->active_file, ctx->line, ctx->col);
+			if (p->active == NULL)
+				p->active = p->root;
+			append_root_or_child();
+			newnode->data.text.value = lht_strdup(value);
+			break;
+		case LHT_COMMENT:
+			break;
+		case LHT_EOF:
+			p->finished = 1;
+			break;
+		case LHT_ERROR:
+			break;
+	}
+}
+
+#undef append_root_or_child
+
+lht_node_t *lht_dom_node_alloc(lht_node_type_t type, const char *name)
+{
+	lht_node_t *newnode;
+
+	if ((type <= LHT_INVALID_TYPE) || (type > LHT_SYMLINK))
+		return NULL;
+
+	newnode = calloc(1, sizeof(lht_node_t));
+	if (newnode == NULL)
+		return NULL;
+
+	if (name == NULL)
+		name = "";
+
+	newnode->type = type;
+	newnode->name = lht_strdup(name);
+
+	/* some types will require extra setup */
+	switch(type) {
+		case LHT_HASH:
+			lht_dom_hash_init(newnode);
+			break;
+		default:
+			/* no extra, suppress compiler warnings */
+			;
+	}
+
+	return newnode;
+}
+
+lht_doc_t *lht_dom_init(void)
+{
+	lht_doc_t *doc = malloc(sizeof(lht_doc_t));
+	doc->root = NULL;
+	doc->detach_doc = 0;
+	doc->unlinked = lht_dom_node_alloc(LHT_LIST, "<unlinked>");
+	doc->unlinked->doc = doc;
+	doc->active = NULL;
+	doc->active_file = NULL;
+	doc->err_line = -1;
+	doc->err_col = -1;
+	doc->file_names = htsp_alloc(lht_str_keyhash, lht_str_keyeq);
+	doc->p = malloc(sizeof(lht_parse_t));
+
+	lht_parser_init(doc->p);
+	doc->p->event = event;
+	doc->p->user_data = doc;
+
+	doc->finished = 0;
+	doc->perror = LHTE_SUCCESS;
+
+	return doc;
+}
+
+void lht_dom_node_free(lht_node_t *node)
+{
+	switch (node->type) {
+		case LHT_LIST:
+			lht_dom_flist(node);
+			break;
+		case LHT_TABLE:
+			lht_dom_ftable(node);
+			break;
+		case LHT_HASH:
+			lht_dom_fhash(node);
+			break;
+
+/* text and symlink can't have children */
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+			free(node->data.text.value);
+			break;
+
+/* we don't need to do anything with the following types */
+		case LHT_INVALID_TYPE:
+			return;
+	}
+	free(node->name);
+	free(node);
+}
+
+/* uninit/free the parser if it's active */
+static void dom_parser_uninit(lht_doc_t *doc)
+{
+	if (doc->p != NULL) {
+		doc->err_line = doc->p->line;
+		doc->err_col = doc->p->col;
+		lht_parser_uninit(doc->p);
+		free(doc->p);
+		doc->p = NULL;
+	}
+}
+
+void lht_dom_uninit(lht_doc_t *doc)
+{
+	htsp_entry_t *e;
+
+	dom_parser_uninit(doc);
+	if (doc->root != NULL)
+		lht_dom_node_free(doc->root);
+	lht_dom_node_free(doc->unlinked);
+
+	for (e = htsp_first(doc->file_names); e != NULL; e = htsp_next(doc->file_names, e))
+		free(e->key);
+	htsp_free(doc->file_names);
+
+	free(doc);
+}
+
+lht_err_t lht_dom_parser_char(lht_doc_t *doc, int c)
+{
+	lht_err_t ret;
+
+	if (doc->finished) {
+		if (doc->perror == LHTE_SUCCESS)
+			return LHTE_STOP;
+		else
+			return doc->perror;
+	}
+
+	ret = lht_parser_char(doc->p, c);
+
+	if (ret != LHTE_SUCCESS) {
+		doc->finished = 1;
+		dom_parser_uninit(doc);
+	}
+	return ret;
+}
+
+
+void lht_dom_pnode(lht_node_t *node, FILE *outf, const char *prefix)
+{
+	fprintf(outf, "%s%s:{%s} ", prefix, lht_type_id(node->type), node->name);
+
+	if (node->parent == NULL)
+		fprintf(outf, "[root] ");
+
+	if (node->doc != NULL) {
+		if (node->parent == node->doc->unlinked)
+			fprintf(outf, "[unlinked] ");
+		if (node->doc->detach_doc)
+			fprintf(outf, "[detached] ");
+	}
+	else
+		fprintf(outf, "[nodoc] ");
+
+	switch (node->type) {
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+			fprintf(outf, "{%s}\n", node->data.text.value);
+			break;
+		case LHT_LIST:
+			if (node->data.list.first != NULL)
+				fprintf(outf, "FIRST %s:{%s} ", lht_type_id(node->data.list.first->type), node->data.list.first->name);
+			if (node->data.list.last != NULL)
+				fprintf(outf, "LAST %s:{%s}", lht_type_id(node->data.list.last->type), node->data.list.last->name);
+			fprintf(outf, "\n");
+			break;
+		case LHT_TABLE:
+			fprintf(outf, "cols: %d, rows: %d\n", node->data.table.cols, node->data.table.rows);
+			break;
+		case LHT_HASH:
+			fprintf(outf, "entries: %d\n", node->data.hash.tbl->used);
+			break;
+		case LHT_INVALID_TYPE:
+			fprintf(outf, "ERROR: invalid type.\n");
+			break;
+		default:
+			fprintf(outf, "ERROR: unknown type.\n");
+			break;
+	}
+}
+
+
+struct lht_dom_indent_s {
+	int prefix_len, alloced;
+	const char *prefix;
+	char *s;
+};
+#define INDENT_GROWTH 64
+
+static void indent_alloc(lht_dom_indent_t *ind, int levels)
+{
+	ind->alloced = levels + ind->prefix_len;
+	ind->s = malloc(levels + ind->prefix_len + 1);
+	memset(ind->s, ' ', levels);
+	memcpy(ind->s + levels, ind->prefix, ind->prefix_len + 1);
+}
+
+static void indent_free(lht_dom_indent_t *ind)
+{
+	free(ind->s);
+	ind->alloced = 0;
+}
+
+static void indent_setup(lht_dom_indent_t *ind, const char *prefix)
+{
+	ind->prefix_len = strlen(prefix);
+	ind->alloced = 0;
+	ind->prefix = prefix;
+	indent_alloc(ind, INDENT_GROWTH);
+}
+
+const char *lht_dom_indent_get(lht_dom_indent_t *ind, int level)
+{
+	return (ind)->s + (ind)->alloced - (ind)->prefix_len - (level);
+}
+
+static void ptree(lht_node_t *node, FILE *outf, lht_dom_indent_t *ind, int level)
+{
+	const char *prefix;
+
+	/* set up prefix according to level */
+	if (level + ind->prefix_len > ind->alloced) {
+		indent_free(ind);
+		indent_alloc(ind, level + INDENT_GROWTH);
+	}
+	prefix = lht_dom_indent_get(ind, level);
+
+	/* print the current node */
+	lht_dom_pnode(node, outf, prefix);
+
+	/* print children */
+	switch (node->type) {
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+			break; /* no children nodes */
+		case LHT_LIST:
+			lht_dom_plist(ptree, node, outf, ind, level+1);
+			break;
+		case LHT_TABLE:
+			lht_dom_ptable(ptree, node, outf, ind, level+1);
+			break;
+		case LHT_HASH:
+			lht_dom_phash(ptree, node, outf, ind, level+1);
+			break;
+		case LHT_INVALID_TYPE:
+			fprintf(outf, "ERROR: invalid type.\n");
+			break;
+		default:
+			fprintf(outf, "ERROR: unknown type.\n");
+			break;
+	}
+}
+
+void lht_dom_ptree(lht_node_t *node, FILE *outf, const char *prefix)
+{
+	lht_dom_indent_t ind;
+	indent_setup(&ind, prefix);
+	ptree(node, outf, &ind, 0);
+	indent_free(&ind);
+}
+
+int lht_need_brace(lht_node_type_t type, const char *txt, int is_name)
+{
+	const char *s;
+	int trailing_ws = 0;
+
+	if ((txt == NULL) || (*txt == '\0')) {
+		if ((is_name) && (type != LHT_TEXT))
+			return 0; /* empty name without {} is ok except for text where te: is usually implicit */
+		return 1;
+	}
+
+	/* leading whitespace */
+	if ((*txt == ' ') || (*txt == '\t'))
+		return 1;
+
+	/* control chars in text */
+	for(s = txt; *s != '\0'; s++) {
+		if (is_name && (*s == '/'))
+			return 1;
+		if ((*s == '\\') || (*s == ':') || (*s == ';') || (*s == '\n') || (*s == '\r') || (*s == '{') || (*s == '}') || (*s == '='))
+			return 1;
+		if ((*s == ' ') || (*s == '\t')) {
+			if (is_name)
+				return 1;
+			trailing_ws = 1;
+		}
+		else
+			trailing_ws = 0;
+	}
+
+	/* trailing whitespace */
+	if (trailing_ws)
+		return 1;
+
+	/* nothing to protect */
+	return 0;
+}
+
+static void lht_dom_export_(lht_node_t *node, FILE *outf, lht_dom_indent_t *ind, int level, int intable, lht_dom_export_style_t exp_style)
+{
+	const char *prefix, *nb1, *nb2, *vb1, *vb2, *val, *eq, *eq_normal = "= ";
+	lht_node_t *subnode;
+	lht_dom_iterator_t it;
+	int row, col;
+	lht_dom_export_style_t style;
+
+	if (level + ind->prefix_len > ind->alloced) {
+		indent_free(ind);
+		indent_alloc(ind, level + INDENT_GROWTH);
+	}
+	prefix = lht_dom_indent_get(ind, level);
+
+	style = exp_style;
+	if ((style & LHT_STY_BRACE_NAME) || lht_need_brace(node->type, node->name, 1)) {
+		nb1 = "{";
+		nb2 = "}";
+	}
+	else
+		nb1 = nb2 = "";
+	eq = "";
+
+	vb1 = vb2 = "";
+
+	switch (node->type) {
+		case LHT_TEXT:
+			if (style & LHT_STY_EQ_TE)
+				eq = eq_normal;
+			val = (node->data.text.value == NULL) ? "" : node->data.text.value;
+			if ((style & LHT_STY_BRACE_TE) || lht_need_brace(node->type, val, 0)) {
+				vb1 = "{";
+				vb2 = "}";
+			}
+			if (!intable)
+				fprintf(outf, "%s", prefix);
+			if (strlen(node->name) > 0)
+				fprintf(outf, "%s%s%s %s%s%s%s", nb1, node->name, nb2,  eq,  vb1, val, vb2);
+			else
+				fprintf(outf, "%s%s%s", vb1, val, vb2);
+			if (!intable)
+				fprintf(outf, "\n");
+			break;
+		case LHT_SYMLINK:
+			if (style & LHT_STY_EQ_SY)
+				eq = eq_normal;
+			val = (node->data.symlink.value == NULL) ? "" : node->data.symlink.value;
+			if ((style & LHT_STY_BRACE_SY) || lht_need_brace(node->type, val, 0)) {
+				vb1 = "{";
+				vb2 = "}";
+			}
+			fprintf(outf, "%s%s%s:%s%s %s%s%s%s", prefix, nb1, lht_type_id(node->type), node->name, nb2,  eq,  vb1, val, vb2);
+			if (!intable)
+				fprintf(outf, "\n");
+			break;
+		case LHT_LIST:
+			if (style & LHT_STY_EQ_LI)
+				eq = eq_normal;
+			else
+		case LHT_HASH:
+			if (style & LHT_STY_EQ_HA)
+				eq = eq_normal;
+			if (!intable)
+				fprintf(outf, "%s%s%s:%s%s %s{\n", prefix, nb1, lht_type_id(node->type), node->name, nb2, eq);
+			else
+				fprintf(outf, "%s%s:%s%s %s{ ", nb1, lht_type_id(node->type), node->name, nb2, eq);
+			for (subnode = lht_dom_first(&it, node); subnode != NULL; subnode = lht_dom_next(&it)) {
+				lht_dom_export_(subnode, outf, ind, level + 1, intable, exp_style);
+				if (intable)
+					fprintf(outf, "; ");
+			}
+			if (!intable)
+				fprintf(outf, "%s", prefix);
+			fprintf(outf, "}");
+			if (!intable)
+				fprintf(outf, "\n");
+			break;
+		case LHT_TABLE:
+			if (style & LHT_STY_EQ_TA)
+				eq = eq_normal;
+			fprintf(outf, "%s%s%s:%s%s %s{\n", prefix, nb1, lht_type_id(node->type), node->name, nb2, eq);
+			row = col = 0;
+			if (style & LHT_STY_EQ_TA_ROW)
+				eq = eq_normal;
+			for (subnode = lht_dom_first(&it, node); subnode != NULL; subnode = lht_dom_next(&it)) {
+				if (col == 0) {
+					if (strlen(node->data.table.row_names[row]) > 0) {
+						if (lht_need_brace(node->type, node->data.table.row_names[row], 1))
+							fprintf(outf, "%s{%s} %s{ ", lht_dom_indent_get(ind, level + 1), node->data.table.row_names[row], eq);
+						else
+							fprintf(outf, "%s%s %s{ ", lht_dom_indent_get(ind, level + 1), node->data.table.row_names[row], eq);
+					}
+					else
+						fprintf(outf, "%s{ ", lht_dom_indent_get(ind, level + 1));
+				}
+				lht_dom_export_(subnode, outf, ind, 0, 1, exp_style);
+				if (col == node->data.table.cols - 1) {
+					fprintf(outf, " }\n");
+					col = -1;
+					row++;
+				}
+				else {
+					fprintf(outf, "; ");
+				}
+				col++;
+			}
+			fprintf(outf, "%s}\n", prefix);
+			break;
+		case LHT_INVALID_TYPE:
+			fprintf(outf, "ERROR: invalid type.\n");
+			break;
+		default:
+			fprintf(outf, "ERROR: unknown type.\n");
+			break;
+	}
+}
+
+lht_err_t lht_dom_export_style(lht_node_t *node, FILE *outf, const char *prefix, lht_dom_export_style_t exp_style)
+{
+	lht_dom_indent_t ind;
+	indent_setup(&ind, prefix);
+	lht_dom_export_(node, outf, &ind, 0, 0, exp_style);
+	indent_free(&ind);
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_dom_export(lht_node_t *node, FILE *outf, const char *prefix)
+{
+	return lht_dom_export_style(node, outf, prefix, LHT_STY_EQ_TE | LHT_STY_EQ_SY);
+}
+
+lht_node_t *lht_dom_first(lht_dom_iterator_t *it, lht_node_t *parent)
+{
+	it->parent = parent;
+	switch(parent->type) {
+		case LHT_LIST:    return lht_dom_list_first(it, parent);
+		case LHT_HASH:    return lht_dom_hash_first(it, parent);
+		case LHT_TABLE:   return lht_dom_table_first(it, parent);
+		case LHT_TEXT:    return NULL;
+		case LHT_SYMLINK: return NULL;
+		case LHT_INVALID_TYPE: return NULL;
+	}
+	return NULL;
+}
+
+/* returns the next child using an iterator set up by lht_dom_first() */
+lht_node_t *lht_dom_next(lht_dom_iterator_t *it)
+{
+	switch(it->parent->type) {
+		case LHT_LIST:    return lht_dom_list_next(it);
+		case LHT_HASH:    return lht_dom_hash_next(it);
+		case LHT_TABLE:   return lht_dom_table_next(it);
+		case LHT_TEXT:    return NULL;
+		case LHT_SYMLINK: return NULL;
+		case LHT_INVALID_TYPE: return NULL;
+	}
+	return NULL;
+}
+
+
+/* === location manipulation === */
+int lht_dom_loc_newfile(lht_doc_t *doc, const char *name)
+{
+	htsp_entry_t *e;
+	doc->p->line = 0;
+	doc->p->col  = 0;
+	e = htsp_getentry(doc->file_names, (char *)name);
+	if (e == NULL) {
+		const char *active_name;
+		active_name = lht_strdup(name);
+		htsp_set(doc->file_names, (char *)active_name, doc);
+		doc->active_file = active_name;
+		return 0;
+	}
+	doc->active_file = e->key;
+	return 1;
+}
+
+void lht_dom_loc_active(lht_doc_t *doc, const char **fn, int *line, int *col)
+{
+	if (fn != NULL)
+		*fn = doc->active_file;
+	if (line != NULL)
+		*line = doc->p == NULL ? doc->err_line : doc->p->line;
+	if (col != NULL)
+		*col = doc->p == NULL ? doc->err_col : doc->p->col;
+}
+
+static int lht_dom_loc_move_(lht_doc_t *doc, lht_node_t *node)
+{
+	const char *local_name;
+	int new_fn = 0;
+	lht_dom_iterator_t it;
+
+	if (doc != NULL) {
+		if (node->file_name != NULL) {
+			local_name = htsp_get(doc->file_names, (char *)node->file_name);
+			if (local_name == NULL) {
+				local_name = lht_strdup(node->file_name);
+				htsp_set(doc->file_names, (char *)local_name, doc);
+				new_fn = 1;
+			}
+			node->file_name = local_name;
+		}
+	}
+	node->doc = doc;
+
+	/* depth-first move all children */
+	for(node = lht_dom_first(&it, node); node != NULL; node = lht_dom_next(&it))
+		lht_dom_loc_move_(doc, node);
+
+	return new_fn;
+}
+
+int lht_dom_loc_move(lht_doc_t *doc, lht_node_t *node)
+{
+	lht_doc_t *old_doc;
+	int new_fn, auto_free;
+
+	old_doc = node->doc;
+	if (old_doc != NULL)
+		auto_free = ((old_doc->root == node) && (old_doc->detach_doc));
+	else
+		auto_free = 0;
+
+	new_fn = lht_dom_loc_move_(doc, node);
+
+	if (auto_free) {
+		old_doc->root = NULL;
+		lht_dom_uninit(old_doc);
+	}
+	return new_fn;
+}
+
+
+lht_err_t lht_dom_loc_detach(lht_node_t *node)
+{
+	lht_doc_t *doc;
+
+	if ((node->doc != NULL) && (node->parent != NULL))
+		return LHTE_NOT_DETACHED;
+
+	doc = lht_dom_init();
+	doc->detach_doc = 1;
+	lht_dom_loc_move(doc, node);
+	doc->root = node;
+	return LHTE_SUCCESS;
+}
+
+/* === helpers === */
+lht_doc_t *lht_dom_load_stream(FILE *f, const char *fn, char **errmsg)
+{
+	lht_doc_t *doc;
+	int error = 0;
+
+
+	/* set up for parsing */
+	doc = lht_dom_init();
+	lht_dom_loc_newfile(doc, fn);
+
+	/* parse char by char */
+	while(!(feof(f))) {
+		lht_err_t err;
+		int c = fgetc(f);
+		err = lht_dom_parser_char(doc, c);
+		if (err != LHTE_SUCCESS) {
+			if (err != LHTE_STOP) {
+				if (errmsg != NULL) {
+					const char *fn;
+					int line, col;
+					const char *msg;
+
+					lht_dom_loc_active(doc, &fn, &line, &col);
+					msg = lht_err_str(err);
+					*errmsg = malloc(strlen(msg) + strlen(fn) + 128);
+					sprintf(*errmsg, "%s (%s:%d.%d)\n", msg, fn, line+1, col+1);
+				}
+				error = 1;
+			}
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+		}
+	}
+
+
+	if (error) {
+		lht_dom_uninit(doc);
+		return NULL;
+	}
+
+	return doc;
+}
+
+
+lht_doc_t *lht_dom_load(const char *fn, char **errmsg)
+{
+	FILE *f;
+	lht_doc_t *doc;
+
+	/* open the file or exit */
+	f = fopen(fn, "r");
+	if (f == NULL) {
+		if (errmsg != NULL) {
+			*errmsg = malloc(strlen(fn) + 128);
+			sprintf(*errmsg, "can't open '%s' for read\n", fn);
+		}
+		return NULL;
+	}
+
+	doc = lht_dom_load_stream(f, fn, errmsg);
+
+	fclose(f);
+	return doc;
+}
+
+lht_node_t *lht_dom_duptree(lht_node_t *src)
+{
+	lht_dom_iterator_t it;
+	lht_node_t *dst, *n;
+
+	dst = lht_dom_node_alloc(src->type, src->name);
+
+	switch(src->type) {
+		case LHT_INVALID_TYPE:
+			return NULL;
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+			dst->data.text.value = lht_strdup(src->data.text.value);
+			break; /* no children, don't copy them */
+		case LHT_LIST:
+			for(n = lht_dom_first(&it, src); n != NULL; n = lht_dom_next(&it))
+				lht_dom_list_append(dst, lht_dom_duptree(n));
+			break;
+		case LHT_HASH:
+			for(n = lht_dom_first(&it, src); n != NULL; n = lht_dom_next(&it))
+				lht_dom_hash_put(dst, lht_dom_duptree(n));
+			break;
+		case LHT_TABLE:
+			lht_dom_duptable(dst, src);
+			break;
+	}
+
+	return dst; 
+}
diff --git a/src_3rd/liblihata/dom.h b/src_3rd/liblihata/dom.h
new file mode 100644
index 0000000..b4ce177
--- /dev/null
+++ b/src_3rd/liblihata/dom.h
@@ -0,0 +1,214 @@
+#ifndef LHT_DOM
+#define LHT_DOM
+#include <stdio.h>
+#include "lihata.h"
+#include "parser.h"
+#include "genht/htsp.h"
+
+typedef struct lht_doc_s lht_doc_t;
+typedef struct lht_node_s lht_node_t;
+
+typedef struct lht_text_s lht_text_t;
+typedef struct lht_list_s lht_list_t;
+typedef struct lht_hash_s lht_hash_t;
+typedef struct lht_table_s lht_table_t;
+
+struct lht_doc_s {
+	/* -- public -- */
+	lht_node_t *root;
+	lht_node_t *unlinked; /* must be a LHT_LIST and holds unlinked subtrees */
+	int detach_doc;       /* 1 if the doc was created by a node detach call (root is the detached node) */
+
+	/* file names for location lookups - data could be a bool; data (pointer) points to doc instead */
+	htsp_t *file_names;
+
+	/* -- internal, for the dom parser -- */
+	lht_node_t *active; /* last node seen on this level (level-determinator node) */
+	lht_parse_t *p;
+	const char *active_file; /* index of the file being parsed */
+	int finished; /* status flag: 0 = parsing in progress; 1 = parsing is broken or finished */
+	lht_err_t perror; /* parse error when finished != 0 */
+	int err_col, err_line;  /* last line:col saved from the parser after an error (the parser struct is already free'd when this data is needed) */
+};
+
+struct lht_text_s {
+	char *value;
+};
+
+struct lht_list_s {
+	/* For easier appending to the end and inserting to the beginning of the list. */
+	lht_node_t *first, *last;
+};
+
+struct lht_hash_s {
+	htsp_t *tbl;
+};
+
+struct lht_table_s {
+	int cols;           /* number of columns (number of items in row) */
+	int cols_alloced;   /* size of each row array allocated (in number of nodes) */
+	int rows;           /* number of rows (lists) in use */
+	int rows_alloced;   /* size of the array (in rows) allocated */
+	lht_node_t ***r; /* a row array of node ptr arrays */
+	char **row_names;   /* we need to store row names, it's not guaranteed that they are anonymous */
+};
+
+struct lht_node_s {
+	lht_node_type_t type;
+	char *name;
+	union {
+		lht_text_t text;
+		lht_list_t list;
+		lht_hash_t hash;
+		lht_table_t table;
+		lht_text_t symlink;   /* symlink is just text that we interpret */
+	} data;
+	/* Used for storage within a single linked list only.
+	   Can be NULL when not used (e.g. for single text node). */
+	lht_node_t *next;
+
+	/* tree: parent; children are stored in form of list/hash/table elements */
+	lht_node_t *parent;
+	lht_doc_t *doc;
+
+	/* location */
+	const char *file_name;
+	int line, col;
+
+	/* a lihata dom may be used as a generic data type, this is where the
+	   user/caller may store its data */
+	void *user_data;
+};
+
+
+lht_doc_t *lht_dom_init(void);
+lht_err_t lht_dom_parser_char(lht_doc_t *doc, int c);
+void lht_dom_uninit(lht_doc_t *doc);
+
+/* Initialize a new document and load a file in root. The call will
+   block until the file is loaded. Returns NULL on error and allocates
+   and sets *errmsg (if errmsg != NULL).
+
+   The stream version parses an already open FILE * and uses fn only
+   for location info. */
+lht_doc_t *lht_dom_load(const char *fn, char **errmsg);
+lht_doc_t *lht_dom_load_stream(FILE *f, const char *fn, char **errmsg);
+
+/* --- loop iterators --- */
+/* Order of iteration is specific to the parent node type:
+    - list: in order from first to last
+    - hash: in random order, each key only once
+    - table: column first
+
+   Iteration is done only on the first level (it's not a BFS or DFS).
+   When there are no more children NULL is returned. Iterators
+   don't allocate memory.
+
+   WARNING: No write operation should be performed on parent while there are
+   active iterations in progress!
+*/
+
+typedef struct lht_dom_iterator_s {
+	/* read-only: */
+	lht_node_t *parent;
+
+	/* private: */
+	union {
+		lht_node_t *list_next;
+		htsp_entry_t *hash_e;
+		struct {
+			int row, col;
+		} tbl;
+	} i;
+} lht_dom_iterator_t;
+
+/* returns the first child of parent and sets up the iterator (does not allocate it) */
+lht_node_t *lht_dom_first(lht_dom_iterator_t *it, lht_node_t *parent);
+
+/* returns the next child using an iterator set up by lht_dom_first() */
+lht_node_t *lht_dom_next(lht_dom_iterator_t *it);
+
+
+/* --- export --- */
+typedef enum {
+	LHT_STY_EQ_TE       = 0x0001,  /* put = in te: lines */
+	LHT_STY_EQ_LI       = 0x0002,  /* put = in li: lines */
+	LHT_STY_EQ_HA       = 0x0004,  /* put = in ha: lines */
+	LHT_STY_EQ_TA       = 0x0008,  /* put = in ta: lines */
+	LHT_STY_EQ_SY       = 0x0010,  /* put = in sy: lines */
+	LHT_STY_EQ_TA_ROW   = 0x0020,  /* put = in ta: row lines */
+	LHT_STY_BRACE_NAME  = 0x0100,  /* force bracing names even if no special chars in them */
+	LHT_STY_BRACE_TE    = 0x0200,  /* force bracing te: value even if no special chars in value */
+	LHT_STY_BRACE_SY    = 0x0400   /* force bracing sy: value even if no special chars in value */
+} lht_dom_export_style_t;
+
+/* prints a node recursively to outf, each line prefixed with prefix, in lihata format */
+lht_err_t lht_dom_export_style(lht_node_t *node, FILE *outf, const char *prefix, lht_dom_export_style_t exp_style);
+lht_err_t lht_dom_export(lht_node_t *node, FILE *outf, const char *prefix);
+
+/* returns non-zero if txt needs {} around it while exporting */
+int lht_need_brace(lht_node_type_t type, const char *txt, int is_name);
+
+
+/* --- debug/diagnostic functions --- */
+
+/* prints a node (no recursion) to outf, each line prefixed with prefix */
+void lht_dom_pnode(lht_node_t *node, FILE *outf, const char *prefix);
+
+/* prints a node recursively to outf, each line prefixed with prefix */
+void lht_dom_ptree(lht_node_t *node, FILE *outf, const char *prefix);
+
+/* --- location manipulation --- */
+
+/* announces the start of a new file (from the next character passed to the parser);
+   sets file name to name and line:col to 0:0
+   returns whether the location was already known in this doc */
+int lht_dom_loc_newfile(lht_doc_t *doc, const char *name);
+
+/* return the current location of the parser (useful for error reporting); line and col are counted from 0 */
+void lht_dom_loc_active(lht_doc_t *doc, const char **fn, int *line, int *col);
+
+/* --- low level --- */
+
+/* allocate a floating node of a given type. */
+lht_node_t *lht_dom_node_alloc(lht_node_type_t type, const char *name);
+
+/* duplicate a subtree into a newly allocated floating subtree */
+lht_node_t *lht_dom_duptree(lht_node_t *src);
+
+/* free node and its children recursively - does not unlink the node from the
+   tree. WARNING: calling this without detaching/unlinking first may leave invalid
+   pointers around; in most applications calling lht_tree_del() is a better
+   choice. */
+void lht_dom_node_free(lht_node_t *node);
+
+
+/* --- list accessors --- */
+
+/* append a detached node to the end of the list */
+lht_err_t lht_dom_list_append(lht_node_t *dest, lht_node_t *node);
+
+/* insert a detached node before the first element of the list */
+lht_err_t lht_dom_list_insert(lht_node_t *dest, lht_node_t *node);
+
+/* returns the number of elements on the list */
+int lht_dom_list_len(lht_node_t *list);
+
+
+/* --- hash accessors --- */
+/* insert detached node in the hash table */
+lht_err_t lht_dom_hash_put(lht_node_t *hash, lht_node_t *node);
+
+/* find the node by name (or return NULL) */
+lht_node_t *lht_dom_hash_get(const lht_node_t *hash, const char *name);
+
+
+/* --- table accessors --- */
+/* returns the node located at row;col or NULL if out of bounds; col;row are counted from 0 */
+lht_node_t *lht_dom_table_cell(lht_node_t *table, int row, int col);
+
+/* Copies the content of src_nonempty to dst_empty; dst_empty must be an
+   empty table created with lht_dom_node_alloc(LHT_TABLE, ...) */
+int lht_dom_duptable(lht_node_t *dst_empty, lht_node_t *src_nonempty);
+
+#endif
diff --git a/src_3rd/liblihata/dom_hash.c b/src_3rd/liblihata/dom_hash.c
new file mode 100644
index 0000000..19f76f7
--- /dev/null
+++ b/src_3rd/liblihata/dom_hash.c
@@ -0,0 +1,90 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains the DOM parser for the hash node type.
+*/
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "dom.h"
+#include "dom_internal.h"
+#include "hash_str.h"
+
+lht_err_t lht_dom_hash_put(lht_node_t *hash, lht_node_t *child)
+{
+	assert(hash->type == LHT_HASH);
+	assert(child->parent == NULL);
+
+	if (htsp_getentry(hash->data.hash.tbl, child->name) != NULL)
+		return LHTE_DUPLICATE_KEY;
+
+	if (child->doc != hash->doc)
+		lht_dom_loc_move(hash->doc, child);
+
+	child->parent = hash;
+
+	htsp_set(hash->data.hash.tbl, child->name, child);
+	return LHTE_SUCCESS;
+}
+
+lht_node_t *lht_dom_hash_get(const lht_node_t *hash, const char *name)
+{
+	assert(hash->type == LHT_HASH);
+	return htsp_get(hash->data.hash.tbl, (char *)name);
+}
+
+void lht_dom_hash_init(lht_node_t *hash)
+{
+	hash->data.hash.tbl = htsp_alloc(lht_str_keyhash, lht_str_keyeq);
+}
+
+void lht_dom_phash(lht_dom_ptcb ptree, lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level)
+{
+	htsp_entry_t *e;
+	for (e = htsp_first(node->data.hash.tbl); e != NULL; e = htsp_next(node->data.hash.tbl, e))
+		ptree(e->value, outf, ind_, level);
+}
+
+void lht_dom_fhash(lht_node_t *node)
+{
+	htsp_entry_t *e;
+	for (e = htsp_first(node->data.hash.tbl); e != NULL; e = htsp_next(node->data.hash.tbl, e))
+		lht_dom_node_free(e->value);
+	htsp_free(node->data.hash.tbl);
+}
+
+lht_node_t *lht_dom_hash_first(lht_dom_iterator_t *it, lht_node_t *parent)
+{
+	it->i.hash_e = htsp_first(parent->data.hash.tbl);
+	if (it->i.hash_e == NULL)
+		return NULL;
+	return it->i.hash_e->value;
+}
+
+lht_node_t *lht_dom_hash_next(lht_dom_iterator_t *it)
+{
+	it->i.hash_e = htsp_next(it->parent->data.hash.tbl, it->i.hash_e);
+	if (it->i.hash_e == NULL)
+		return NULL;
+	return it->i.hash_e->value;
+}
+
diff --git a/src_3rd/liblihata/dom_internal.h b/src_3rd/liblihata/dom_internal.h
new file mode 100644
index 0000000..4ceb2c1
--- /dev/null
+++ b/src_3rd/liblihata/dom_internal.h
@@ -0,0 +1,50 @@
+/* API intended for internal use only - calls with side effects, assumptions
+   or other dangerous features. */
+
+#define LHT_TABLE_GROW_ROWS 16
+#define LHT_TABLE_GROW_COLS 16
+
+/* node type specific initializers */
+void lht_dom_hash_init(lht_node_t *hash);
+
+/* return the prefix for indentation without growing it */
+typedef struct lht_dom_indent_s lht_dom_indent_t;
+const char *lht_dom_indent_get(lht_dom_indent_t *ind, int level);
+
+/* node type specific subtree print */
+typedef void (*lht_dom_ptcb)(lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level);
+void lht_dom_phash(lht_dom_ptcb ptree, lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level);
+void lht_dom_plist(lht_dom_ptcb ptree, lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level);
+void lht_dom_ptable(lht_dom_ptcb ptree, lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level);
+
+void lht_dom_flist(lht_node_t *node);
+void lht_dom_ftable(lht_node_t *node);
+void lht_dom_fhash(lht_node_t *node);
+
+lht_err_t lht_dom_table_post_process(lht_node_t *dest, lht_node_t *row_list);
+
+lht_node_t *lht_dom_list_first(lht_dom_iterator_t *it, lht_node_t *parent);
+lht_node_t *lht_dom_list_next(lht_dom_iterator_t *it);
+lht_node_t *lht_dom_hash_first(lht_dom_iterator_t *it, lht_node_t *parent);
+lht_node_t *lht_dom_hash_next(lht_dom_iterator_t *it);
+lht_node_t *lht_dom_table_first(lht_dom_iterator_t *it, lht_node_t *parent);
+lht_node_t *lht_dom_table_next(lht_dom_iterator_t *it);
+
+/* detach node from its current document and move it into a newly allocated document */
+lht_err_t lht_dom_loc_detach(lht_node_t *node);
+
+/* table grow */
+lht_err_t lht_dom_table_grow_row(lht_node_t *table);
+lht_err_t lht_dom_table_grow_col(lht_node_t *table);
+
+/* move a node recursively from its current doc into tdoc. This will affect
+   the following lht_node_t fields:
+    - file_name (content won't change but the pointer will)
+    - doc (will point to tdoc)
+   Furthermore all file names found during the move will be introduced in
+   the location file name hash of tdoc, but will not deleted from the original
+   doc. The node is _not_ detached/unlinked from its parent!
+
+   Shall be called after detaching/unlinking the node.
+*/
+int lht_dom_loc_move(lht_doc_t *tdoc, lht_node_t *node);
diff --git a/src_3rd/liblihata/dom_list.c b/src_3rd/liblihata/dom_list.c
new file mode 100644
index 0000000..6bb00ee
--- /dev/null
+++ b/src_3rd/liblihata/dom_list.c
@@ -0,0 +1,122 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Gabor Horvath (HvG)
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains the DOM parser for the list node type.
+*/
+
+#include <assert.h>
+#include "dom.h"
+#include "dom_internal.h"
+
+lht_err_t lht_dom_list_append(lht_node_t *dest, lht_node_t *node)
+{
+	assert(dest->type == LHT_LIST);
+	assert(node->parent == NULL);
+
+	if (node->doc != dest->doc)
+		lht_dom_loc_move(dest->doc, node);
+
+	node->parent = dest;
+	if (dest->data.list.first == NULL) {
+		assert(dest->data.list.last == NULL);
+		dest->data.list.first = node;
+	}
+	else {
+		assert(dest->data.list.last != NULL);
+		dest->data.list.last->next = node;
+	}
+	dest->data.list.last = node;
+	node->next = NULL;
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_dom_list_insert(lht_node_t *dest, lht_node_t *node)
+{
+	assert(dest->type == LHT_LIST);
+	assert(node->parent == NULL);
+
+	if (node->doc != dest->doc)
+		lht_dom_loc_move(dest->doc, node);
+
+	node->parent = dest;
+	if (dest->data.list.first == NULL) {
+		assert(dest->data.list.last == NULL);
+		dest->data.list.last = node;
+		node->next = NULL;
+	}
+	else {
+		assert(dest->data.list.last != NULL);
+		node->next = dest->data.list.first;
+	}
+	dest->data.list.first = node;
+
+	return LHTE_SUCCESS;
+}
+
+void lht_dom_plist(lht_dom_ptcb ptree, lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level)
+{
+	lht_node_t *n;
+	for (n = node->data.list.first; n != NULL; n = n->next)
+		ptree(n, outf, ind_, level);
+}
+
+void lht_dom_flist(lht_node_t *node)
+{
+	lht_node_t *n, *m;
+	for (n = node->data.list.first; n != NULL; n = m) {
+		m = n->next;
+		lht_dom_node_free(n);
+	}
+}
+
+
+lht_node_t *lht_dom_list_first(lht_dom_iterator_t *it, lht_node_t *parent)
+{
+	if (parent->data.list.first != NULL)
+		it->i.list_next = parent->data.list.first->next;
+	else
+		it->i.list_next = NULL;
+	return parent->data.list.first;
+}
+
+lht_node_t *lht_dom_list_next(lht_dom_iterator_t *it)
+{
+	lht_node_t *ret;
+	ret = it->i.list_next;
+	if (it->i.list_next != NULL)
+		it->i.list_next = it->i.list_next->next;
+	return ret;
+}
+
+
+int lht_dom_list_len(lht_node_t *node)
+{
+	int result = 0;
+
+	assert(node->type == LHT_LIST);
+
+	for (node = node->data.list.first; node != NULL; node = node->next)
+		result++;
+
+	return result;
+}
diff --git a/src_3rd/liblihata/dom_table.c b/src_3rd/liblihata/dom_table.c
new file mode 100644
index 0000000..d67e88d
--- /dev/null
+++ b/src_3rd/liblihata/dom_table.c
@@ -0,0 +1,244 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Gabor Horvath (HvG)
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains the DOM parser for the table node type.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dom.h"
+#include "dom_internal.h"
+#include "tree.h"
+
+lht_err_t lht_dom_table_post_process(lht_node_t *dest, lht_node_t *row_list)
+{
+	lht_table_t *t;
+	assert(dest->type == LHT_TABLE);
+	assert(row_list->type == LHT_LIST);
+	t = &dest->data.table;
+
+	if (t->rows >= t->rows_alloced) {
+		if (t->rows == 0)
+			t->cols = t->cols_alloced = lht_dom_list_len(row_list);
+		t->rows_alloced += LHT_TABLE_GROW_ROWS;
+		t->row_names = realloc(t->row_names, t->rows_alloced * sizeof(char *));
+		t->r = realloc(t->r, t->rows_alloced * t->cols_alloced * sizeof(lht_node_t *));
+	}
+	t->row_names[t->rows] = row_list->name;
+	row_list->name = NULL;
+	if (t->cols == 0) {
+		t->r = NULL; /*  malloc(0) not guaranteed to return NULL */
+		if (row_list->data.list.first != NULL) /* we expect an empty list */
+			abort();
+	}
+	else {
+		int i;
+		lht_node_t **row, *node, *next;
+		row = t->r[t->rows] = malloc(t->cols * sizeof(lht_node_t *));
+		for (i = 0, node = row_list->data.list.first; node != NULL; node = next, i++) {
+			if (i >= t->cols)
+				abort();
+			row[i] = node;
+			next = node->next;
+			node->parent = dest;
+			node->next = NULL;
+		}
+		if (i != t->cols)
+			abort();
+		/* empty the list - one node can be only at one place in the tree and
+		   our list items are all in table cells now */
+		row_list->data.list.first = row_list->data.list.last = NULL;
+	}
+
+
+	t->rows++;
+	return LHTE_SUCCESS;
+}
+
+
+void lht_dom_ptable(lht_dom_ptcb ptree, lht_node_t *node, FILE *outf, lht_dom_indent_t *ind_, int level)
+{
+	int i, j;
+	const char *prefix;
+	lht_table_t *t = &node->data.table;
+
+	prefix = lht_dom_indent_get(ind_, level);
+	for (i = 0; i < t->rows; i++) {
+		fprintf(outf, "%sRow %d:{%s}\n", prefix, i, t->row_names[i]);
+		for (j = 0; j < t->cols; j++)
+			ptree(t->r[i][j], outf, ind_, level+1);
+	}
+}
+
+void lht_dom_ftable(lht_node_t *node)
+{
+	lht_node_t *n;
+	lht_table_t *t = &node->data.table;
+	int i, j;
+
+	for (i = 0; i < t->rows; i++) {
+		for (j = 0; j < t->cols; j++) {
+			n = t->r[i][j];
+			if (n != NULL)
+				lht_dom_node_free(n);
+		}
+		if (t->r[i] != NULL)
+			free(t->r[i]);
+	}
+#warning TODO: related: write an empty-table regression test
+	if (t->row_names != NULL) {
+		for (i = 0; i < t->rows; i++)
+			free(t->row_names[i]);
+		free(t->row_names);
+	}
+	if (t->r != NULL)
+		free(t->r);
+}
+
+int lht_dom_duptable(lht_node_t *dst_empty, lht_node_t *src_nonempty)
+{
+	lht_table_t *dst = &dst_empty->data.table, *src = &src_nonempty->data.table;
+	int i, j, err = 0;
+
+	/* dup/alloc administrative fields */
+	dst->r = malloc(sizeof(lht_node_t **) * src->rows);
+	if (dst->r == 0)
+		return 1;
+	dst->rows_alloced = dst->rows = src->rows;
+	dst->cols_alloced = dst->cols = src->cols;
+	dst->row_names = malloc(sizeof(char *) * src->rows);
+	if (dst->row_names == 0)
+		return 1;
+
+	/* dup rows */
+	for (i = 0; i < src->rows; i++) {
+		dst->r[i] = malloc(sizeof(lht_node_t *) * src->cols);
+		if (src->row_names[i] != NULL) {
+			dst->row_names[i] = lht_strdup(src->row_names[i]);
+			if (dst->row_names[i] == NULL)
+				err++;
+		}
+		else
+			dst->row_names[i] = NULL;
+
+		/* dup cols */
+		for (j = 0; j < src->cols; j++) {
+			dst->r[i][j] = NULL;
+			if (src->r[i][j] != NULL) {
+				dst->r[i][j] = lht_dom_duptree(src->r[i][j]);
+				if (dst->r[i][j] == NULL)
+					err++;
+			}
+		}
+	}
+	return err;
+}
+
+lht_node_t *lht_dom_table_next(lht_dom_iterator_t *it)
+{
+	lht_node_t *ret;
+
+	ret = lht_dom_table_cell(it->parent, it->i.tbl.row, it->i.tbl.col);
+	it->i.tbl.col++;
+	if (it->i.tbl.col >= it->parent->data.table.cols) {
+		it->i.tbl.col = 0;
+		it->i.tbl.row++;
+	}
+
+	return ret;
+}
+
+
+lht_node_t *lht_dom_table_first(lht_dom_iterator_t *it, lht_node_t *parent)
+{
+	it->i.tbl.row = 0;
+	it->i.tbl.col = 0;
+	return lht_dom_table_next(it);
+}
+
+
+lht_err_t lht_dom_table_grow_row(lht_node_t *table)
+{
+	lht_table_t *t;
+	lht_node_t ***temp_r = NULL;
+	char **temp_row_names = NULL;
+
+	assert(table->type == LHT_TABLE);
+	t = &table->data.table;
+
+	if (t->rows < t->rows_alloced)
+		return LHTE_SUCCESS; /* growing is unnecessary */
+
+	t->rows_alloced += LHT_TABLE_GROW_ROWS;
+
+	temp_r = realloc(t->r, t->rows_alloced * sizeof(lht_node_t **));
+	temp_row_names = realloc(t->row_names, t->rows_alloced * sizeof(char *));
+
+	if ((temp_r == NULL) || (temp_row_names == NULL)) {
+		t->rows_alloced -= LHT_TABLE_GROW_ROWS;
+		return LHTE_OUT_OF_MEM;
+	}
+
+	t->r = temp_r;
+	t->row_names = temp_row_names;
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_dom_table_grow_col(lht_node_t *table)
+{
+	lht_table_t *t;
+	int i;
+
+	assert(table->type == LHT_TABLE);
+	t = &table->data.table;
+
+	if (t->cols < t->cols_alloced)
+		return LHTE_SUCCESS; /* growing is unnecessary */
+
+	t->cols_alloced += LHT_TABLE_GROW_COLS;
+
+	for (i = 0; i < t->rows; i++) {
+		lht_node_t **new_row;
+		new_row = realloc(t->r[i], t->cols_alloced * sizeof(lht_node_t *));
+		if (t->r[i] == NULL) {
+			t->cols_alloced -= LHT_TABLE_GROW_COLS;
+			return LHTE_OUT_OF_MEM;
+		}
+		t->r[i] = new_row;
+	}
+
+	return LHTE_SUCCESS;
+}
+
+lht_node_t *lht_dom_table_cell(lht_node_t *table, int row, int col)
+{
+	assert(table->type == LHT_TABLE);
+
+	if ((row >= table->data.table.rows) || (col >= table->data.table.cols))
+		return NULL;
+	if ((row < 0) || (col < 0))
+		return NULL;
+
+	return table->data.table.r[row][col];
+}
diff --git a/src_3rd/liblihata/genht/LICENSE b/src_3rd/liblihata/genht/LICENSE
new file mode 100644
index 0000000..85265b0
--- /dev/null
+++ b/src_3rd/liblihata/genht/LICENSE
@@ -0,0 +1 @@
+Public domain
diff --git a/src_3rd/liblihata/genht/Makefile b/src_3rd/liblihata/genht/Makefile
new file mode 100644
index 0000000..ae0a095
--- /dev/null
+++ b/src_3rd/liblihata/genht/Makefile
@@ -0,0 +1,47 @@
+# use -Dinline if compiling with c89
+INCDIR=$(install_root)/usr/include/genht
+LIBDIR=$(install_root)/usr/lib
+
+CFLAGS=-Wall -pedantic -g
+
+BIN=mainsi
+OBJS=htss.o htsp.o htsi.o htip.o htpp.o htpi.o hash.o siphash24.o
+
+all: $(BIN) $(OBJS) genht_std.a genht_std.so
+
+clean:
+	rm -f $(BIN) *.o
+
+%.o:%.c
+	$(CC) -o $@ -c -fPIC $< $(CFLAGS)
+
+mainsi: mainsi.o htsi.o
+	$(CC) -o $@ mainsi.o htsi.o $(LDFLAGS)
+
+genht_std.so: $(OBJS)
+	$(CC) -shared -dynamic -rdynamic -o $@ $(OBJS)
+
+genht_std.a: $(OBJS)
+	ar rvu $@ $(OBJS)
+
+install_: genht_std.so genht_std.a
+	mkdir -p $(INCDIR) $(LIBDIR)
+	$(CP) `pwd`/hash.h $(INCDIR)/hash.h
+	$(CP) `pwd`/ht.h $(INCDIR)/ht.h
+	$(CP) `pwd`/ht_inlines.h $(INCDIR)/ht_inlines.h
+	$(CP) `pwd`/htip.h $(INCDIR)/htip.h
+	$(CP) `pwd`/htsi.h $(INCDIR)/htsi.h
+	$(CP) `pwd`/htsp.h $(INCDIR)/htsp.h
+	$(CP) `pwd`/htss.h $(INCDIR)/htss.h
+	$(CP) `pwd`/genht_std.so $(LIBDIR)/genht_std.so
+	$(CP) `pwd`/genht_std.a $(LIBDIR)/genht_std.a
+
+uninstall:
+	rm -rf $(INCDIR)
+	rm $(LIBDIR)/genht_std.so  $(LIBDIR)/genht_std.a
+
+install:
+	make install_ CP="cp"
+
+linstall:
+	make install_ CP="ln -s"
diff --git a/src_3rd/liblihata/genht/hash.c b/src_3rd/liblihata/genht/hash.c
new file mode 100644
index 0000000..6f73e5a
--- /dev/null
+++ b/src_3rd/liblihata/genht/hash.c
@@ -0,0 +1,158 @@
+#include <string.h>
+#include <ctype.h>
+
+/* assumes sizeof(unsigned)==4 */
+
+#define rot(x, k) (((x)<<(k)) ^ ((x)>>(32-(k))))
+
+/* mix 3 32 bit values reversibly */
+#define mix(a, b, c) { \
+	a -= c;  a ^= rot(c, 4);  c += b; \
+	b -= a;  b ^= rot(a, 6);  a += c; \
+	c -= b;  c ^= rot(b, 8);  b += a; \
+	a -= c;  a ^= rot(c, 16);  c += b; \
+	b -= a;  b ^= rot(a, 19);  a += c; \
+	c -= b;  c ^= rot(b, 4);  b += a; \
+}
+
+/* final mixing of 3 32-bit values (a, b, c) into c */
+#define final(a, b, c) { \
+	c ^= b; c -= rot(b, 14); \
+	a ^= c; a -= rot(c, 11); \
+	b ^= a; b -= rot(a, 25); \
+	c ^= b; c -= rot(b, 16); \
+	a ^= c; a -= rot(c, 4); \
+	b ^= a; b -= rot(a, 14); \
+	c ^= b; c -= rot(b, 24); \
+}
+
+#define SEED 0x9e3779b9
+
+/* not for strings: does unaligned access and reads past the end of key */
+/* bob jenkins: lookup 3 */
+unsigned jenhash(const void *key, unsigned len) {
+	unsigned a, b, c;
+	const unsigned *k = (const unsigned *)key;
+	
+	a = b = c = SEED;
+	while (len > 12) {
+		a += *k++;
+		b += *k++;
+		c += *k++;
+		mix(a, b, c)
+		len -= 12;
+	}
+	switch (len) {
+		case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+		case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+		case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+		case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+		case 8 : b+=k[1]; a+=k[0]; break;
+		case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+		case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+		case 5 : b+=k[1]&0xff; a+=k[0]; break;
+		case 4 : a+=k[0]; break;
+		case 3 : a+=k[0]&0xffffff; break;
+		case 2 : a+=k[0]&0xffff; break;
+		case 1 : a+=k[0]&0xff; break;
+	}
+	final(a, b, c)
+	return c;
+}
+
+unsigned jenhash32(unsigned k) {
+	unsigned a, b, c;
+	
+	a = b = c = SEED;
+	a += k;
+	final(a, b, c)
+	return c;
+}
+
+/* austin appleby: murmur 2 */
+unsigned murmurhash(const void *key, unsigned len) {
+	enum { m = 0x5bd1e995, r = 24 };
+	unsigned h = SEED ^ len;
+	const unsigned char *data = (const unsigned char *)key;
+
+	while(len >= 4) {
+		unsigned k = *(unsigned *)data;
+		k *= m;
+		k ^= k >> r;
+		k *= m;
+		h *= m;
+		h ^= k;
+		data += 4;
+		len -= 4;
+	}
+	switch(len) {
+	case 3: h ^= data[2] << 16;
+	case 2: h ^= data[1] << 8;
+	case 1: h ^= data[0];
+	        h *= m;
+	}
+	h ^= h >> 13;
+	h *= m;
+	h ^= h >> 15;
+	return h;
+}
+
+unsigned murmurhash32(unsigned k) {
+	return murmurhash(&k, 4);
+}
+
+/* simple hash for aligned pointers */
+unsigned ptrhash(const void *k) {
+	const unsigned long n = (const unsigned long)k;
+	return (n >> 2) ^ (n >> 12);
+}
+
+/* simple string hash */
+unsigned strhash(const char *key) {
+	const unsigned char *p = (const unsigned char *)key;
+	unsigned h = SEED;
+
+	while (*p)
+		h += (h << 2) + *p++;
+	return h;
+}
+
+/* simple string hash, case-insensitive */
+unsigned strhash_case(const char *key) {
+	const unsigned char *p = (const unsigned char *)key;
+	unsigned h = SEED;
+
+	while (*p)
+		h += (h << 2) + tolower(*p++);
+	return h;
+}
+
+
+/* case sensitive string keyeq */
+int strkeyeq(const char *a, const char *b)
+{
+	return !strcmp(a, b);
+}
+
+/* case insensitive string keyeq */
+int strkeyeq_case(const char *a, const char *b)
+{
+	return !strcasecmp(a, b);
+}
+
+/* pointer match for htp*_t */
+int ptrkeyeq(const void *a, const void *b)
+{
+	return a == b;
+}
+
+long int longhash(long int l)
+{
+	return l;
+}
+
+int longkeyeq(long a, long b)
+{
+	return a == b;
+}
+
diff --git a/src_3rd/liblihata/genht/hash.h b/src_3rd/liblihata/genht/hash.h
new file mode 100644
index 0000000..83d51ee
--- /dev/null
+++ b/src_3rd/liblihata/genht/hash.h
@@ -0,0 +1,30 @@
+/* assumes sizeof(unsigned)==4 */
+
+/* not for strings: does unaligned access and reads past the end of key */
+/* bob jenkins: lookup 3 */
+unsigned jenhash(const void *key, unsigned len);
+unsigned jenhash32(unsigned k);
+
+/* austin appleby: murmur 2 */
+unsigned murmurhash(const void *key, unsigned len);
+unsigned murmurhash32(unsigned k);
+
+
+/* simple hash for aligned pointers */
+unsigned ptrhash(const void *k);
+
+/* simple string hash - case sensitive and case-insensitive */
+unsigned strhash(const char *k);
+unsigned strhash_case(const char *key);
+
+/* string keyeq functions - case sensitive and case-insensitive */
+int strkeyeq(const char *a, const char *b);
+int strkeyeq_case(const char *a, const char *b);
+
+/* pointer match for htp*_t */
+int ptrkeyeq(const void *a, const void *b);
+
+
+/* long (int) */
+long int longhash(long int l);
+int longkeyeq(long a, long b);
diff --git a/src_3rd/liblihata/genht/ht.c b/src_3rd/liblihata/genht/ht.c
new file mode 100644
index 0000000..23f691f
--- /dev/null
+++ b/src_3rd/liblihata/genht/ht.c
@@ -0,0 +1,239 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef inline
+/* make sure inline and static are empty so all calls become linkable functions */
+#undef inline
+#define inline
+#ifdef static
+#undef static
+#endif
+#define static
+#include "ht_inlines.h"
+#undef static
+#endif
+
+#ifndef HT_INVALID_VALUE
+#define HT_INVALID_VALUE 0
+#endif
+
+#define HT_MINSIZE 8
+#define HT_MAXSIZE (1U << 31)
+
+#define JUMP(i, j) i += j++
+#define JUMP_FIRST(i, j) j = 1, i += j++
+
+/* generic functions, useful if ht_entry_t changes */
+
+static inline void setused(HT(entry_t) *entry) {
+	entry->flag = 1;
+}
+
+static inline void setdeleted(HT(entry_t) *entry) {
+	entry->flag = -1;
+}
+
+static inline unsigned int entryhash(const HT(entry_t) *entry) {
+	return entry->hash;
+}
+
+void HT(init)(HT(t) *ht, unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))) {
+	ht->mask = HT_MINSIZE - 1;
+	ht->fill = 0;
+	ht->used = 0;
+	ht->table = genht_calloc(ht, ht->mask + 1, sizeof(HT(entry_t)));
+	assert(ht->table);
+	ht->keyhash = keyhash;
+	ht->keyeq = keyeq;
+}
+
+void HT(uninit)(HT(t) *ht) {
+	genht_free(ht, ht->table);
+	ht->table = NULL;
+}
+
+HT(t) *HT(alloc)(unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))) {
+	HT(t) *ht = genht_malloc(NULL, sizeof(HT(t)));
+
+	assert(ht);
+	HT(init)(ht, keyhash, keyeq);
+	return ht;
+}
+
+void HT(clear)(HT(t) *ht) {
+	HT(uninit)(ht);
+	HT(init)(ht, ht->keyhash, ht->keyeq);
+}
+
+void HT(free)(HT(t) *ht) {
+	HT(uninit)(ht);
+	genht_free(NULL, ht);
+}
+
+/* one lookup function to rule them all */
+static HT(entry_t) *lookup(HT(t) *ht, HT(const_key_t) key, unsigned int hash) {
+	unsigned int mask = ht->mask;
+	unsigned int i = hash;
+	unsigned int j;
+	HT(entry_t) *table = ht->table;
+	HT(entry_t) *entry = table + (i & mask);
+	HT(entry_t) *free_entry; /* first deleted entry for insert */
+
+	if (HT(isempty)(entry))
+		return entry;
+	else if (HT(isdeleted)(entry))
+		free_entry = entry;
+	else if (entryhash(entry) == hash && ht->keyeq(entry->key, key))
+		return entry;
+	else
+		free_entry = NULL;
+	for (JUMP_FIRST(i, j); ; JUMP(i, j)) {
+		entry = table + (i & mask);
+		if (HT(isempty)(entry))
+			return (free_entry == NULL) ? entry : free_entry;
+		else if (HT(isdeleted)(entry)) {
+			if (free_entry == NULL)
+				free_entry = entry;
+		} else if (entryhash(entry) == hash && ht->keyeq(entry->key, key))
+			return entry;
+	}
+}
+
+/* for copy and resize: no deleted entries in ht, the lookedup key is not in ht */
+static HT(entry_t) *cleanlookup(HT(t) *ht, unsigned int hash) {
+	unsigned int mask = ht->mask;
+	unsigned int i = hash;
+	unsigned int j;
+	HT(entry_t) *table = ht->table;
+	HT(entry_t) *entry = table + (i & mask);
+
+	if (HT(isempty)(entry))
+		return entry;
+	for (JUMP_FIRST(i, j); ; JUMP(i, j)) {
+		entry = table + (i & mask);
+		if (HT(isempty)(entry))
+			return entry;
+	}
+}
+
+HT(t) *HT(copy)(const HT(t) *ht) {
+	HT(t) *newht;
+	HT(entry_t) *entry;
+	unsigned int used = ht->used;
+
+	newht = genht_malloc(NULL, sizeof(HT(t)));
+	assert(newht);
+	*newht = *ht;
+	newht->fill = used;
+	newht->table = genht_calloc(ht, newht->mask + 1, sizeof(HT(entry_t)));
+	assert(newht->table);
+	for (entry = ht->table; used > 0; entry++)
+		if (HT(isused)(entry)) {
+			used--;
+			*cleanlookup(newht, entryhash(entry)) = *entry;
+		}
+	return newht;
+}
+
+void HT(resize)(HT(t) *ht, unsigned int hint) {
+	unsigned int newsize;
+	unsigned int used = ht->used;
+	HT(entry_t) *oldtable = ht->table;
+	HT(entry_t) *entry;
+
+	if (hint < used << 1)
+		hint = used << 1;
+	if (hint > HT_MAXSIZE)
+		hint = HT_MAXSIZE;
+	for (newsize = HT_MINSIZE; newsize < hint; newsize <<= 1);
+	ht->table = genht_calloc(ht, newsize, sizeof(HT(entry_t)));
+	assert(ht->table);
+	ht->mask = newsize - 1;
+	ht->fill = ht->used;
+	for (entry = oldtable; used > 0; entry++)
+		if (HT(isused)(entry)) {
+			used--;
+			*cleanlookup(ht, entryhash(entry)) = *entry;
+		}
+	genht_free(ht, oldtable);
+}
+
+int HT(has)(HT(t) *ht, HT(const_key_t) key) {
+	HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key));
+
+	return HT(isused)(entry);
+}
+
+HT(value_t) HT(get)(HT(t) *ht, HT(const_key_t) key) {
+	HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key));
+
+	return HT(isused)(entry) ? entry->value : HT_INVALID_VALUE;
+}
+
+HT(entry_t) *HT(getentry)(HT(t) *ht, HT(const_key_t) key) {
+	HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key));
+
+	return HT(isused)(entry) ? entry : NULL;
+}
+
+/* fill threshold = 3/4 */
+static inline void checkfill(HT(t) *ht) {
+	if (ht->fill > ht->mask - (ht->mask >> 2) || ht->fill > ht->used << 2)
+		HT(resize)(ht, ht->used << (ht->used > 1 << 16 ? 1 : 2));
+}
+
+HT(entry_t) *HT(insert)(HT(t) *ht, HT(key_t) key, HT(value_t) value) {
+	unsigned int hash = ht->keyhash(key);
+	HT(entry_t) *entry = lookup(ht, key, hash);
+
+	if (HT(isused)(entry))
+		return entry;
+	if (HT(isempty)(entry))
+		ht->fill++;
+	ht->used++;
+	entry->hash = hash;
+	entry->key = key;
+	entry->value = value;
+	setused(entry);
+	checkfill(ht);
+	return NULL;
+}
+
+void HT(set)(HT(t) *ht, HT(key_t) key, HT(value_t) value) {
+	HT(entry_t) *entry = HT(insert)(ht, key, value);
+
+	if (entry)
+		entry->value = value;
+}
+
+HT(value_t) HT(pop)(HT(t) *ht, HT(const_key_t) key) {
+	HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key));
+	HT(value_t) v;
+
+	if (!HT(isused)(entry))
+		return HT_INVALID_VALUE;
+	ht->used--;
+	v = entry->value;
+	setdeleted(entry);
+	return v;
+}
+
+HT(entry_t) *HT(popentry)(HT(t) *ht, HT(const_key_t) key) {
+	HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key));
+
+	if (HT(isused)(entry)) {
+		ht->used--;
+		setdeleted(entry);
+		return entry;
+	}
+	return NULL;
+}
+
+void HT(delentry)(HT(t) *ht, HT(entry_t) *entry) {
+	if (!HT(isused)(entry))
+		return;
+	ht->used--;
+	setdeleted(entry);
+}
+
+#undef HT_INVALID_VALUE
diff --git a/src_3rd/liblihata/genht/ht.h b/src_3rd/liblihata/genht/ht.h
new file mode 100644
index 0000000..90c80c6
--- /dev/null
+++ b/src_3rd/liblihata/genht/ht.h
@@ -0,0 +1,105 @@
+/* open addressing hash table */
+/* no malloc checks (out of memory == segfault), max size is 1 << 31 */
+/* an entry pointer is valid until the next insertion or resize */
+/*
+typedef void *HT(key_t);
+typedef void *HT(value_t);
+
+Plus optionally, for key const correctness:
+
+typedef void *HT(const key_t);
+#define HT_HAS_CONST_KEY
+
+*/
+
+#ifndef HT_HAS_CONST_KEY
+	typedef HT(key_t) HT(const_key_t);
+#else
+#	undef HT_HAS_CONST_KEY
+#endif
+
+typedef struct {
+	int flag;
+	unsigned int hash;
+	HT(key_t) key;
+	HT(value_t) value;
+} HT(entry_t);
+
+typedef struct {
+	unsigned int mask;
+	unsigned int fill;
+	unsigned int used;
+	HT(entry_t) *table;
+
+	unsigned int (*keyhash)(HT(const_key_t));
+	int (*keyeq)(HT(const_key_t), HT(const_key_t));
+#ifdef GENHT_USER_FIELDS
+	GENHT_USER_FIELDS
+#endif
+} HT(t);
+
+HT(t) *HT(alloc)(unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t)));
+void HT(init)(HT(t) *ht, unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t)));
+void HT(free)(HT(t) *ht);
+void HT(uninit)(HT(t) *ht);
+void HT(clear)(HT(t) *ht);
+HT(t) *HT(copy)(const HT(t) *ht);
+/* new size is 2^n >= hint */
+void HT(resize)(HT(t) *ht, unsigned int hint);
+
+/* ht[key] is used */
+int HT(has)(HT(t) *ht, HT(const_key_t) key);
+/* value of ht[key] or 0 if key is not used */
+HT(value_t) HT(get)(HT(t) *ht, HT(const_key_t) key);
+/* entry of ht[key] or NULL if key is not used */
+HT(entry_t) *HT(getentry)(HT(t) *ht, HT(const_key_t) key);
+/* ht[key] = value */
+void HT(set)(HT(t) *ht, HT(key_t) key, HT(value_t) value);
+/* if key is used then return ht[key] else ht[key] = value and return NULL */
+/* (the value of the returned used entry can be modified) */
+HT(entry_t) *HT(insert)(HT(t) *ht, HT(key_t) key, HT(value_t) value);
+/* delete key and return ht[key] or 0 if key is not used */
+HT(value_t) HT(pop)(HT(t) *ht, HT(const_key_t) key);
+/* delete key and return ht[key] or NULL if key is not used */
+/* (the returned deleted entry can be used to free key,value resources) */
+HT(entry_t) *HT(popentry)(HT(t) *ht, HT(const_key_t) key);
+/* delete entry (useful for destructive iteration) */
+void HT(delentry)(HT(t) *ht, HT(entry_t) *entry);
+
+
+/* User application can override malloc/realloc/free by defining these macros: */
+#ifndef genht_malloc
+#define genht_malloc(ht, size) malloc(size)
+#endif
+#ifndef genht_calloc
+#define genht_calloc(ht, size1, size2) calloc(size1, size2)
+#endif
+#ifndef genht_realloc
+#define genht_realloc(ht, ptr, size) realloc(ptr, size)
+#endif
+#ifndef genht_free
+#define genht_free(ht, ptr) free(ptr)
+#endif
+
+
+#ifdef inline
+
+/* helper functions */
+unsigned int HT(length)(const HT(t) *ht);
+unsigned int HT(fill)(const HT(t) *ht);
+unsigned int HT(size)(const HT(t) *ht);
+
+/* for any entry exactly one returns true */
+int HT(isused)(const HT(entry_t) *entry);
+int HT(isempty)(const HT(entry_t) *entry);
+int HT(isdeleted)(const HT(entry_t) *entry);
+
+/* first used (useful for iteration) */
+HT(entry_t) *HT(first)(const HT(t) *ht);
+
+/* next used (useful for iteration) */
+HT(entry_t) *HT(next)(const HT(t) *ht, HT(entry_t) *entry);
+
+#else
+#include "ht_inlines.h"
+#endif
diff --git a/src_3rd/liblihata/genht/ht_inlines.h b/src_3rd/liblihata/genht/ht_inlines.h
new file mode 100644
index 0000000..c78ad0e
--- /dev/null
+++ b/src_3rd/liblihata/genht/ht_inlines.h
@@ -0,0 +1,28 @@
+/* helper functions */
+static inline unsigned int HT(length)(const HT(t) *ht) {return ht->used;}
+static inline unsigned int HT(fill)(const HT(t) *ht) {return ht->fill;}
+static inline unsigned int HT(size)(const HT(t) *ht) {return ht->mask + 1;}
+
+/* for any entry exactly one returns true */
+static inline int HT(isused)(const HT(entry_t) *entry) {return entry->flag > 0;}
+static inline int HT(isempty)(const HT(entry_t) *entry) {return entry->flag == 0;}
+static inline int HT(isdeleted)(const HT(entry_t) *entry) {return entry->flag < 0;}
+
+/* first used (useful for iteration) */
+static inline HT(entry_t) *HT(first)(const HT(t) *ht)
+{
+	HT(entry_t) *entry = 0;
+
+	if (ht->used)
+		for (entry = ht->table; !HT(isused)(entry); entry++);
+	return entry;
+}
+
+/* next used (useful for iteration) */
+static inline HT(entry_t) *HT(next)(const HT(t) *ht, HT(entry_t) *entry)
+{
+	while (++entry != ht->table + ht->mask + 1)
+		if (HT(isused)(entry))
+			return entry;
+	return 0;
+}
diff --git a/src_3rd/liblihata/genht/htip.c b/src_3rd/liblihata/genht/htip.c
new file mode 100644
index 0000000..652a86f
--- /dev/null
+++ b/src_3rd/liblihata/genht/htip.c
@@ -0,0 +1,10 @@
+#include "htip.h"
+#define HT(x) htip_ ## x
+#include "ht.c"
+
+int htip_keyeq(htip_key_t a, htip_key_t b)
+{
+	return a == b;
+}
+
+#undef HT
diff --git a/src_3rd/liblihata/genht/htip.h b/src_3rd/liblihata/genht/htip.h
new file mode 100644
index 0000000..af3c576
--- /dev/null
+++ b/src_3rd/liblihata/genht/htip.h
@@ -0,0 +1,12 @@
+#ifndef GENHT_HTIP_H
+#define GENHT_HTIP_H
+
+typedef long int htip_key_t;
+typedef void *htip_value_t;
+#define HT(x) htip_ ## x
+#include "ht.h"
+#undef HT
+
+int htip_keyeq(htip_key_t a, htip_key_t b);
+
+#endif
diff --git a/src_3rd/liblihata/genht/htpi.c b/src_3rd/liblihata/genht/htpi.c
new file mode 100644
index 0000000..048a5ad
--- /dev/null
+++ b/src_3rd/liblihata/genht/htpi.c
@@ -0,0 +1,4 @@
+#include "htpi.h"
+#define HT(x) htpi_ ## x
+#include "ht.c"
+#undef HT
diff --git a/src_3rd/liblihata/genht/htpi.h b/src_3rd/liblihata/genht/htpi.h
new file mode 100644
index 0000000..af7b237
--- /dev/null
+++ b/src_3rd/liblihata/genht/htpi.h
@@ -0,0 +1,12 @@
+#ifndef GENHT_HTPI_H
+#define GENHT_HTPI_H
+
+#define HT_HAS_CONST_KEY
+typedef void *htpi_key_t;
+typedef const void *htpi_const_key_t;
+typedef int htpi_value_t;
+#define HT(x) htpi_ ## x
+#include "ht.h"
+#undef HT
+
+#endif
diff --git a/src_3rd/liblihata/genht/htpp.c b/src_3rd/liblihata/genht/htpp.c
new file mode 100644
index 0000000..9ffc083
--- /dev/null
+++ b/src_3rd/liblihata/genht/htpp.c
@@ -0,0 +1,4 @@
+#include "htpp.h"
+#define HT(x) htpp_ ## x
+#include "ht.c"
+#undef HT
diff --git a/src_3rd/liblihata/genht/htpp.h b/src_3rd/liblihata/genht/htpp.h
new file mode 100644
index 0000000..91c212f
--- /dev/null
+++ b/src_3rd/liblihata/genht/htpp.h
@@ -0,0 +1,12 @@
+#ifndef GENHT_HTPP_H
+#define GENHT_HTPP_H
+
+#define HT_HAS_CONST_KEY
+typedef void *htpp_key_t;
+typedef const void *htpp_const_key_t;
+typedef void *htpp_value_t;
+#define HT(x) htpp_ ## x
+#include "ht.h"
+#undef HT
+
+#endif
diff --git a/src_3rd/liblihata/genht/htsi.c b/src_3rd/liblihata/genht/htsi.c
new file mode 100644
index 0000000..c1a6c6f
--- /dev/null
+++ b/src_3rd/liblihata/genht/htsi.c
@@ -0,0 +1,4 @@
+#include "htsi.h"
+#define HT(x) htsi_ ## x
+#include "ht.c"
+#undef HT
diff --git a/src_3rd/liblihata/genht/htsi.h b/src_3rd/liblihata/genht/htsi.h
new file mode 100644
index 0000000..2f7f29c
--- /dev/null
+++ b/src_3rd/liblihata/genht/htsi.h
@@ -0,0 +1,12 @@
+#ifndef GENHT_HTSI_H
+#define GENHT_HTSI_H
+
+#define HT_HAS_CONST_KEY
+typedef char *htsi_key_t;
+typedef const char *htsi_const_key_t;
+typedef int htsi_value_t;
+#define HT(x) htsi_ ## x
+#include "ht.h"
+#undef HT
+
+#endif
diff --git a/src_3rd/liblihata/genht/htsp.c b/src_3rd/liblihata/genht/htsp.c
new file mode 100644
index 0000000..4bb5956
--- /dev/null
+++ b/src_3rd/liblihata/genht/htsp.c
@@ -0,0 +1,4 @@
+#include "htsp.h"
+#define HT(x) htsp_ ## x
+#include "ht.c"
+#undef HT
diff --git a/src_3rd/liblihata/genht/htsp.h b/src_3rd/liblihata/genht/htsp.h
new file mode 100644
index 0000000..bc68724
--- /dev/null
+++ b/src_3rd/liblihata/genht/htsp.h
@@ -0,0 +1,12 @@
+#ifndef GENHT_HTSP_H
+#define GENHT_HTSP_H
+
+#define HT_HAS_CONST_KEY
+typedef char *htsp_key_t;
+typedef const char *htsp_const_key_t;
+typedef void *htsp_value_t;
+#define HT(x) htsp_ ## x
+#include "ht.h"
+#undef HT
+
+#endif
diff --git a/src_3rd/liblihata/genht/htss.c b/src_3rd/liblihata/genht/htss.c
new file mode 100644
index 0000000..33ce27e
--- /dev/null
+++ b/src_3rd/liblihata/genht/htss.c
@@ -0,0 +1,4 @@
+#include "htss.h"
+#define HT(x) htss_ ## x
+#include "ht.c"
+#undef HT
diff --git a/src_3rd/liblihata/genht/htss.h b/src_3rd/liblihata/genht/htss.h
new file mode 100644
index 0000000..2d01b8a
--- /dev/null
+++ b/src_3rd/liblihata/genht/htss.h
@@ -0,0 +1,12 @@
+#ifndef GENHT_HTSS_H
+#define GENHT_HTSS_H
+
+#define HT_HAS_CONST_KEY
+typedef char *htss_key_t;
+typedef const char *htss_const_key_t;
+typedef char *htss_value_t;
+#define HT(x) htss_ ## x
+#include "ht.h"
+#undef HT
+
+#endif
diff --git a/src_3rd/liblihata/genht/mainsi.c b/src_3rd/liblihata/genht/mainsi.c
new file mode 100644
index 0000000..89335be
--- /dev/null
+++ b/src_3rd/liblihata/genht/mainsi.c
@@ -0,0 +1,62 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include "htsi.h"
+
+static unsigned int keyhash(char *key) {
+	unsigned char *p = (unsigned char *)key;
+	unsigned int hash = 0;
+
+	while (*p)
+		hash += (hash << 2) + *p++;
+	return hash;
+}
+
+static int keyeq(char *a, char *b) {
+	char *pa = (char *)a;
+	char *pb = (char *)b;
+
+	for (; *pa == *pb; pa++, pb++)
+		if (*pa == '\0')
+			return 1;
+	return 0;
+}
+
+int main() {
+	htsi_t *ht;
+	htsi_entry_t *e;
+
+	ht = htsi_alloc(keyhash, keyeq);
+	htsi_set(ht, "a", 1);
+	htsi_set(ht, "b", 2);
+	htsi_set(ht, "asdf", -3);
+	htsi_set(ht, "qw", 4);
+	htsi_set(ht, "v", 5);
+	htsi_set(ht, "df", 6);
+	htsi_set(ht, "x", 7);
+	if (!htsi_has(ht, "a"))
+		puts("ERR: has a");
+	if (htsi_has(ht, "1"))
+		puts("ERR: has 1");
+	if (htsi_insert(ht, "y", 8))
+		puts("ERR: insert y");
+	if (htsi_insert(ht, "x", 9)->value != 7)
+		puts("ERR: insert x");
+	if (htsi_pop(ht, "b") != 2 || htsi_getentry(ht, "b"))
+		puts("ERR: pop b");
+	if (htsi_popentry(ht, "b"))
+		puts("ERR: pope b");
+	if (htsi_popentry(ht, "c"))
+		puts("ERR: pope c");
+	for (e = htsi_first(ht); e; e = htsi_next(ht, e)) {
+		if (htsi_get(ht, e->key) != e->value)
+			printf("ERR %s %d\n", e->key, e->value);
+		printf("%s %d\n", e->key, e->value);
+	}
+	htsi_clear(ht);
+	for (e = htsi_first(ht); e; e = htsi_next(ht, e))
+		puts("ERR: clear");
+	htsi_free(ht);
+	return 0;
+}
diff --git a/src_3rd/liblihata/genht/siphash24.c b/src_3rd/liblihata/genht/siphash24.c
new file mode 100644
index 0000000..414189e
--- /dev/null
+++ b/src_3rd/liblihata/genht/siphash24.c
@@ -0,0 +1,75 @@
+/* based on SipHash reference C implementation (CC0) */
+#include <stddef.h>
+#include <stdint.h>
+
+#define cROUNDS 2
+#define dROUNDS 4
+
+#define ROTL(x,b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define U8TO64_LE(p)            \
+  (((uint64_t)((p)[0])      ) | \
+   ((uint64_t)((p)[1]) <<  8) | \
+   ((uint64_t)((p)[2]) << 16) | \
+   ((uint64_t)((p)[3]) << 24) | \
+   ((uint64_t)((p)[4]) << 32) | \
+   ((uint64_t)((p)[5]) << 40) | \
+   ((uint64_t)((p)[6]) << 48) | \
+   ((uint64_t)((p)[7]) << 56))
+
+#define SIPROUND                                        \
+  do {                                                  \
+    v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+    v2 += v3; v3=ROTL(v3,16); v3 ^= v2;                 \
+    v0 += v3; v3=ROTL(v3,21); v3 ^= v0;                 \
+    v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+  } while(0)
+
+uint64_t siphash(const uint8_t *in, size_t len, const uint8_t *k)
+{
+  /* "somepseudorandomlygeneratedbytes" */
+  uint64_t v0 = 0x736f6d6570736575ULL;
+  uint64_t v1 = 0x646f72616e646f6dULL;
+  uint64_t v2 = 0x6c7967656e657261ULL;
+  uint64_t v3 = 0x7465646279746573ULL;
+  uint64_t b;
+  uint64_t k0 = U8TO64_LE( k );
+  uint64_t k1 = U8TO64_LE( k + 8 );
+  uint64_t m;
+  int i;
+  const uint8_t *end = in + len - len%8;
+  const int left = len%8;
+  b = (uint64_t)len << 56;
+  v3 ^= k1;
+  v2 ^= k0;
+  v1 ^= k1;
+  v0 ^= k0;
+
+  for ( ; in != end; in += 8 )
+  {
+    m = U8TO64_LE( in );
+    v3 ^= m;
+    for( i=0; i<cROUNDS; ++i ) SIPROUND;
+    v0 ^= m;
+  }
+
+  switch( left )
+  {
+  case 7: b |= ( ( uint64_t )in[ 6] )  << 48;
+  case 6: b |= ( ( uint64_t )in[ 5] )  << 40;
+  case 5: b |= ( ( uint64_t )in[ 4] )  << 32;
+  case 4: b |= ( ( uint64_t )in[ 3] )  << 24;
+  case 3: b |= ( ( uint64_t )in[ 2] )  << 16;
+  case 2: b |= ( ( uint64_t )in[ 1] )  <<  8;
+  case 1: b |= ( ( uint64_t )in[ 0] ); break;
+  case 0: break;
+  }
+
+  v3 ^= b;
+  for( i=0; i<cROUNDS; ++i ) SIPROUND;
+  v0 ^= b;
+  v2 ^= 0xff;
+  for( i=0; i<dROUNDS; ++i ) SIPROUND;
+
+  return v0 ^ v1 ^ v2  ^ v3;
+}
diff --git a/src_3rd/liblihata/genht/siphash24.h b/src_3rd/liblihata/genht/siphash24.h
new file mode 100644
index 0000000..25dec45
--- /dev/null
+++ b/src_3rd/liblihata/genht/siphash24.h
@@ -0,0 +1,4 @@
+#include <stddef.h>
+#include <stdint.h>
+/* hash (in,len) using 16byte secret k so collision attacks are hard */
+uint64_t siphash(const uint8_t *in, size_t len, const uint8_t *k);
diff --git a/src_3rd/liblihata/hash_str.c b/src_3rd/liblihata/hash_str.c
new file mode 100644
index 0000000..361a0e2
--- /dev/null
+++ b/src_3rd/liblihata/hash_str.c
@@ -0,0 +1,17 @@
+#include <string.h>
+
+int lht_str_keyeq(const char *k1, const char *k2)
+{
+	return !strcmp(k1, k2);
+}
+
+/* simple string hash */
+#define SEED 0x9e3779b9
+unsigned lht_str_keyhash(const char *key) {
+	unsigned char *p = (unsigned char *)key;
+	unsigned h = SEED;
+
+	while (*p)
+		h += (h << 2) + *p++;
+	return h;
+}
diff --git a/src_3rd/liblihata/hash_str.h b/src_3rd/liblihata/hash_str.h
new file mode 100644
index 0000000..26e9ab5
--- /dev/null
+++ b/src_3rd/liblihata/hash_str.h
@@ -0,0 +1,3 @@
+/* for internal use: shared hash utility functions for string hashes */
+int lht_str_keyeq(const char *k1, const char *k2);
+unsigned lht_str_keyhash(const char *key);
diff --git a/src_3rd/liblihata/lihata.c b/src_3rd/liblihata/lihata.c
new file mode 100644
index 0000000..a01d57f
--- /dev/null
+++ b/src_3rd/liblihata/lihata.c
@@ -0,0 +1,133 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file provides generic API for the most basic operations common to
+   the event parser, the DOM parser, the tree functions and most probably
+   even to the application code.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include "lihata.h"
+
+#define str_arr_size(a) (sizeof(a) / sizeof(const char *))
+
+static const char *lht_type_names[] = {
+	"invalid_type",
+	"text",
+	"list",
+	"hash",
+	"table",
+	"symlink"
+};
+static const char *lht_type_ids[] = {
+	"??",
+	"te",
+	"li",
+	"ha",
+	"ta",
+	"sy"
+};
+#define lht_num_types str_arr_size(lht_type_names)
+
+const char *lht_type_name(lht_node_type_t type_)
+{
+	int type = (int)type_; /* it's not guaranteed that type is signed */
+	if ((type < 0) || (type >= lht_num_types))
+		return NULL;
+	return lht_type_names[type];
+}
+
+const char *lht_type_id(lht_node_type_t type_)
+{
+	int type = (int)type_; /* it's not guaranteed that type is signed */
+	if ((type < 0) || (type >= lht_num_types))
+		return NULL;
+	return lht_type_ids[type];
+}
+
+
+static const char *lht_err_names[] = {
+	"success: parser has finished, stop sending characters",
+	"success",
+	"internal error: parse state stack underflow",
+	"internal error: invalid state after BSTRING is closed",
+	"internal error: TVALUE finished in non-textual context",
+	"invalid character (\0) in the stream",
+	"unexpected EOF after backslash",
+	"unexpected EOF in the middle of a brace string",
+	"unexpected EOF in the middle of an open node",
+	"unexpected EOF while waiting for a value",
+	"unexpected EOF while waiting for or processing a text value",
+	"invalid node type or unprotected colon as the 3rd character of a name",
+	"unexpected colon in id (may require protection)",
+	"can not escape before non-textual value",
+	"invalid character between name and value",
+	"invalid/unprotected character in text content",
+	"li, ha or ta with no body",
+	"internal parser error",
+	"table row is not a list",
+	"table rows with different number of columns",
+	"duplicate key in hash",
+	"node is not part of any document",
+	"broken document",
+	"path not found",
+	"path: can't resolve relative path without a CWD",
+	"path not found: child nodes don't have children",
+	"path error: invalid integer in list or table addressing",
+	"path: invalid node on path",
+	"path: symlink recursion too deep",
+	"path: underspecified table address: should be row/col, not row only",
+	"node not found",
+	"boundary check error: out of bounds",
+	"merge failed: type mismatch",
+	"merge failed: symlink mismatch",
+	"merge failed: empty symlink",
+	"merge failed: table column mismatch",
+	"merge failed: cyclic merge request",
+	"out of memory",
+	"operation would render the document invalid",
+	"node is not detached from its document"
+};
+#define lht_num_perrs str_arr_size(lht_err_names)
+
+const char *lht_err_str(lht_err_t pe_)
+{
+	int pe = (int)pe_; /* ANSI C allows enum to be signed or unsigned */
+	pe++;
+	if ((pe < 0) || (pe > lht_num_perrs))
+		return "invalid/unknown error";
+	return lht_err_names[pe];
+}
+
+char *lht_strdup(const char *s)
+{
+	size_t len;
+	char *n;
+
+	if (s == NULL)
+		return NULL;
+
+	len = strlen(s);
+	n = malloc(len+1);
+	memcpy(n, s, len+1);
+	return n;
+}
diff --git a/src_3rd/liblihata/lihata.h b/src_3rd/liblihata/lihata.h
new file mode 100644
index 0000000..f14138b
--- /dev/null
+++ b/src_3rd/liblihata/lihata.h
@@ -0,0 +1,69 @@
+#ifndef LHT_H
+#define LHT_H
+
+typedef enum lht_node_type_e {
+	LHT_INVALID_TYPE = 0,
+	LHT_TEXT,
+	LHT_LIST,
+	LHT_HASH,
+	LHT_TABLE,
+	LHT_SYMLINK
+} lht_node_type_t;
+
+/* returns the textual name or 2-char ID of a lihata type or NULL on error;
+   LHT_INVALID_TYPE is not an error. */
+const char *lht_type_name(lht_node_type_t type);
+const char *lht_type_id(lht_node_type_t type);
+
+/* parser constants common to both parsers */
+/* parser errors */
+typedef enum lht_err_e {
+	LHTE_STOP = -1,
+	LHTE_SUCCESS = 0,
+	LHTE_STACK_UNDERFLOW,
+	LHTE_INV_STATE_BSTRING,
+	LHTE_INV_STATE_TVALUE,
+	LHTE_NUL_CHAR,
+	LHTE_EOF_BACKSLASH,
+	LHTE_EOF_BSTRING,
+	LHTE_EOF_NODE,
+	LHTE_EOF_VALUE,
+	LHTE_EOF_TVALUE,
+	LHTE_INVALID_TYPE,
+	LHTE_COLON,
+	LHTE_ESCALHTE_NONTEXT,
+	LHTE_INVALID_CHAR_PREVALUE,
+	LHTE_INV_TEXT,
+	LHTE_NO_BODY,
+	LHTE_INTERNAL_PARSER,
+	LHTE_TABLE_ROW_TYPE,
+	LHTE_TABLE_COLS,
+	LHTE_DUPLICATE_KEY,
+	LHTE_NOT_IN_DOC,
+	LHTE_BROKEN_DOC,
+	LHTE_PATH_NOT_FOUND,
+	LHTE_PATH_UNKNOWN_CWD,
+	LHTE_PATH_CHILD_OF_TEXT,
+	LHTE_PATH_INT,
+	LHTE_PATH_INVALID_NODE,
+	LHTE_PATH_SYMLINK_TOO_DEEP,
+	LHTE_PATH_UNDERSPEC_TABLE,
+	LHTE_NOT_FOUND,
+	LHTE_BOUNDARY,
+	LHTE_MERGE_TYPE_MISMATCH,
+	LHTE_MERGE_SYMLINK_MISMATCH,
+	LHTE_MERGE_EMPTY_SYMLINK,
+	LHTE_MERGE_TABLECOLS_MISMATCH,
+	LHTE_MERGE_CYCLIC,
+	LHTE_OUT_OF_MEM,
+	LHTE_WOULD_RESULT_INVALID_DOC,
+	LHTE_NOT_DETACHED
+} lht_err_t;
+
+/* returns textual description of a parser error */
+const char *lht_err_str(lht_err_t pe);
+
+/* Safe (NULL results NULL), portable (C89) strdup; segfaults if malloc() fails. */
+char *lht_strdup(const char *s);
+
+#endif
diff --git a/src_3rd/liblihata/parser.c b/src_3rd/liblihata/parser.c
new file mode 100644
index 0000000..631b430
--- /dev/null
+++ b/src_3rd/liblihata/parser.c
@@ -0,0 +1,469 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file provides generic API for the most basic operations common to
+   the event parser and the DOM parser.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "parser.h"
+
+
+#if 0
+#define TRACE fprintf(stderr, "%d '%c'/%d ty=%d\n", sp->state, c, c, sp->type)
+#else
+#define TRACE
+#endif
+
+#define grow_(out, arr, used, alloced, type, growth) \
+	do { \
+		if ((used) >= (alloced)) { \
+			(alloced) += (growth); \
+			(arr) = realloc((arr), sizeof(type) * (alloced)); \
+		} \
+		(out) = (arr)+(used); \
+		(used)++; \
+	} while(0)
+
+#define grow_stack(out) grow_(out, ctx->stack, ctx->sused, ctx->salloced, lht_pstack_t, 32)
+#define grow_token(out) grow_(out, ctx->token, ctx->tused, ctx->talloced, char, 256)
+
+/* append a character to the current token */
+#define append(c) \
+	do { \
+		char *s; \
+		grow_token(s); \
+		*s = c; \
+	} while(0)
+
+#define delete()  ctx->tused=0
+
+/* push new state on top of the stack */
+#define push2(new_state, new_type, new_str, expl_name, expl_type) \
+	do { \
+		lht_pstack_t *_push_sp; \
+		grow_stack(_push_sp); \
+		_push_sp->state = new_state; \
+		_push_sp->type = new_type; \
+		_push_sp->explicit_type = expl_type; \
+		_push_sp->explicit_name = expl_name; \
+		if (new_str == NULL) \
+			_push_sp->str = NULL; \
+		else \
+			_push_sp->str = lht_strdup(new_str); \
+	} while(0)
+
+/* Can not call push2 without an advanced gcc warning about strdup getting NULL */
+#define push(new_state, new_type) \
+	do { \
+		lht_pstack_t *_push_sp; \
+		grow_stack(_push_sp); \
+		_push_sp->state = new_state; \
+		_push_sp->type = new_type; \
+		_push_sp->str = NULL; \
+		_push_sp->explicit_type = 0; \
+		_push_sp->explicit_name = 0; \
+	} while(0)
+
+/* low level pop: remove/free topmost entry without any checks or error reporting */
+#define pop_(_pop_sp) \
+	do { \
+		if (_pop_sp->str != NULL) \
+			free(_pop_sp->str);\
+		ctx->sused--; \
+	} while(0);
+
+/* high level pop: remove top (current) state and return to previous;
+   set outsp to the previous state as well. Report error on underflow. */
+#define pop(outsp) \
+	do { \
+		lht_pstack_t *_pop_sp = ctx->stack+ctx->sused-1; \
+		pop_(_pop_sp); \
+		if (ctx->sused < 0) \
+			error(LHTE_STACK_UNDERFLOW); \
+		outsp = ctx->stack+ctx->sused-1; \
+	} while(0)
+
+/* parse error */
+#define error(code) \
+	do { \
+		ctx->event(ctx, LHT_ERROR, LHT_INVALID_TYPE, lht_err_str(code), NULL); \
+		sp->state = LHT_ST_DEAD; \
+		return code; \
+	} while(0)
+
+/* state stack: new nodes will get their own entry that is popped when the node ends */
+struct lht_pstack_s {
+	lht_pstate_t state;
+	lht_node_type_t type;
+	char *str;
+	unsigned explicit_type:1;
+	unsigned explicit_name:1;
+};
+
+
+void lht_parser_init(lht_parse_t *ctx)
+{
+	/* reset only lihata part of the struct */
+	memset(&(ctx->line), 0, sizeof(lht_parse_t) - ((char *)&(ctx->line) - (char *)ctx));
+	push(LHT_ST_BODY, LHT_TEXT);
+}
+
+void lht_parser_uninit(lht_parse_t *ctx)
+{
+	while(ctx->sused > 0) {
+		lht_pstack_t *sp = ctx->stack+ctx->sused-1;
+		pop_(sp);
+	}
+
+	if (ctx->token != NULL)
+		free(ctx->token);
+
+	if (ctx->stack != NULL)
+		free(ctx->stack);
+
+	/* defensive: just in case the ctx is still reused somehow, this guarantees a crash on null pointer access which is better than silently using an already uninitialized parser */
+	ctx->salloced = ctx->talloced = 16;
+	ctx->sused = ctx->tused = 0;
+}
+
+static lht_node_type_t type_lookup(char c1, char c2)
+{
+	if ((c1 == 't') && (c2 == 'e')) return LHT_TEXT;
+	if ((c1 == 'l') && (c2 == 'i')) return LHT_LIST;
+	if ((c1 == 'h') && (c2 == 'a')) return LHT_HASH;
+	if ((c1 == 't') && (c2 == 'a')) return LHT_TABLE;
+	if ((c1 == 's') && (c2 == 'y')) return LHT_SYMLINK;
+	return LHT_INVALID_TYPE;
+}
+
+/* strip trailing white space from token */
+static void tstrip_trailing_ws(lht_parse_t *ctx)
+{
+	/* array instead of pointer - to avoid ptr-- at the end that would point before the array in extreme case, which is UB in C99 */
+	while((ctx->tused > 0) && ((ctx->token[ctx->tused - 1] == ' ') || (ctx->token[ctx->tused - 1] == '\t')))
+		ctx->tused--;
+}
+
+#define update_sp   sp = ctx->stack + ctx->sused - 1;
+#define is_text(ty)  (((ty) == LHT_TEXT) || ((ty) == LHT_SYMLINK))
+#define in_root(ctx) ((ctx)->sused == 1)
+
+#define set_default_implicit_type(sp_SDIT, ptype) \
+do { \
+ ((sp_SDIT)->type) = (ptype == LHT_TABLE ? LHT_LIST : LHT_TEXT); \
+	(sp_SDIT)->explicit_type = 0; \
+} while(0)
+
+#define newline_admin() \
+	do { \
+		ctx->col = 0; \
+		ctx->line++; \
+	} while(0)
+
+/* insert a character in the stream while keeping proper line/col numbering */
+#define insert_char(c) \
+	do { \
+		int oldl = ctx->line; \
+		int oldc = ctx->col; \
+		lht_parser_char(ctx, c); \
+		ctx->col = oldc; \
+		ctx->line = oldl; \
+	} while(0)
+
+
+lht_err_t lht_parser_char(lht_parse_t *ctx, int c)
+{
+	lht_pstack_t *sp;
+
+	update_sp;
+	TRACE;
+	ctx->col++;
+
+	if (c == '\0')
+		error(LHTE_NUL_CHAR);
+	switch(sp->state) {
+		case LHT_ST_DEAD: return LHTE_STOP; /* Hah, kicking the dead horse? */
+		case LHT_ST_BACKSLASH: /* next character is literal */
+			if (c == EOF)
+				error(LHTE_EOF_BACKSLASH); /* can not escape the EOF */
+			append(c);
+			pop(sp);
+			break;
+		case LHT_ST_COMMENT: /* comment: read line up to \n */
+			switch(c) {
+				case '\n':
+					newline_admin();
+				case '\r':
+				case EOF:
+					append('\0');
+					ctx->event(ctx, LHT_COMMENT, LHT_INVALID_TYPE, ctx->token, NULL);
+					delete();
+					pop(sp);
+					if (c == EOF)
+						goto valid_eof; /* last line being a comment, without newline at the end */
+					break;
+				default:
+					append(c);
+			}
+			break;
+		case LHT_ST_BSTRING:    /* brace string: read until the closing } */
+			switch(c) {
+				case EOF:
+					error(LHTE_EOF_BSTRING);
+				case '\\':
+					push(LHT_ST_BACKSLASH, LHT_INVALID_TYPE);
+					break;
+				case '}':
+					pop(sp);
+					switch(sp->state) {  /* there are only two contexts for a brace string: */
+						case LHT_ST_BODY: goto finish_nodename_braced;
+						case LHT_ST_PREVALUE: goto got_string;
+						default:
+							error(LHTE_INV_STATE_BSTRING);
+					}
+				default:
+					append(c);
+			}
+			break;
+		case LHT_ST_BODY:
+			switch(c) {
+				case EOF:
+					goto valid_eof; /* eof in body is valid if body is above the root */
+				case '\n':
+					newline_admin();
+				case ';':
+					/* separator in body means the end of a non-brace-protected string */
+					if (ctx->tused > 0) {
+						push(LHT_ST_TVALUE, sp->type); /* got_string will pop */
+						update_sp;
+						tstrip_trailing_ws(ctx);
+						goto got_string;
+					}
+					/* ignore extra separators */
+					break;
+				case ' ':
+				case '\t':
+					if (ctx->tused > 0)
+						append(c);
+					/* ... or ignore leading whitespace */
+					break;
+				case '\\':
+					push(LHT_ST_BACKSLASH, LHT_INVALID_TYPE);
+					break;
+				case '=':
+					tstrip_trailing_ws(ctx);
+					if (is_text(sp->type))
+						sp->explicit_name = 1;
+					goto finish_nodename;
+				case '}':
+					/* end of a brace enclosed value list */
+					insert_char('\n');  /* insert a separator before the '}', closing any open record allowing single-liners */
+					pop(sp);
+					ctx->event(ctx, LHT_CLOSE, LHT_INVALID_TYPE, NULL, NULL);
+					if (ctx->sused <= 1) /* closing the root node means: stop parsing */
+						goto implicit_eof;
+					else
+						set_default_implicit_type(sp, sp[-1].type); /* if this node is not the root, set back implicit type as parent requires */
+					break;
+				case '{': /* "{" in bstring means a protected node name or anonymous string value, if we already have a name */
+					if ((ctx->tused != 0) && (is_text(sp->type))) { /* non-empty name and brace means a brace-protected value */
+							tstrip_trailing_ws(ctx);
+							goto finish_nodename_start_bstring;
+					}
+					else if (is_text(sp->type)) /* empty name and brace-open means it's going to be a string protection; whether it's a name or a value will be figured at the end */
+						push(LHT_ST_BSTRING, LHT_INVALID_TYPE);
+					else
+						goto finish_nodename_start_body; /* not a text node, not a symlink, still does not have a name - anonymous node with body */
+					break;
+				case ':':
+					if ((ctx->tused == 2) && (sp->explicit_type == 0)) { /* unprotected colon in the 3rd character is a type separator */
+						sp->type = type_lookup(ctx->token[0], ctx->token[1]);
+						if (sp->type == LHT_INVALID_TYPE)
+							error(LHTE_INVALID_TYPE);
+						sp->explicit_type = 1;
+						delete();
+					}
+					else
+						error(LHTE_COLON); /* do not allow unprotected colons */
+					break;
+				case '#':
+					push(LHT_ST_COMMENT, LHT_INVALID_TYPE);
+					break;
+				default:
+					append(c);
+			}
+			break;
+		case LHT_ST_PREVALUE:
+			switch(c) {
+				case EOF:
+					/* corner case: anonymous text in root, without termination - we wouldn't be able to decide whether it's a valid EOF or the file is truncated; better to require the termination */
+					error(LHTE_EOF_VALUE); /* EOF while waiting for value is bad */
+				case '\n':
+					newline_admin();
+				case '\r':
+				case ';':
+					/* text node with only one text (with or without =)*/
+					if (is_text(sp->type))
+						tstrip_trailing_ws(ctx);
+					goto got_string;
+				case ' ':
+				case '\t':
+				case '=':
+					break; /* ignore whitespace between name and value */
+				case '{':
+					if (is_text(sp->type)) { /* for a text value brace is protection */
+						delete();
+						push(LHT_ST_BSTRING, LHT_INVALID_TYPE);
+					}
+					else { /* for anything else it's just BODY */
+						ctx->event(ctx, LHT_OPEN, sp->type, sp->str, NULL);
+						sp->state = LHT_ST_BODY;
+						set_default_implicit_type(sp, sp[-1].type);
+					}
+					break;
+				case '\\':
+					if (is_text(sp->type)) { /* text can start with an escaped character */
+						push(LHT_ST_TVALUE, sp->type);
+						push(LHT_ST_BACKSLASH, LHT_INVALID_TYPE);
+					}
+					else /* but nothing else can - we need a "{" */
+						error(LHTE_ESCALHTE_NONTEXT);
+					break;
+				case '}':
+					error(LHTE_INV_TEXT);
+				default:
+					if (is_text(sp->type)) { /* anything else is already a text value for text ... */
+						append(c);
+						push(LHT_ST_TVALUE, sp->type);
+					}
+					else /* or an error since we expect "{" */
+						error(LHTE_INVALID_CHAR_PREVALUE);
+			}
+			break;
+		case LHT_ST_TVALUE: /* simple, unprotected text value */
+			switch(c) {
+				case EOF:
+					error(LHTE_EOF_TVALUE); /* text value is requires valid termination, otherwise we wouldn't be able to decide if it's truncated */
+				case '\\':
+					push(LHT_ST_BACKSLASH, LHT_INVALID_TYPE);
+					break;
+				case '\n':
+					newline_admin();
+				case '\r':
+				case ';':
+					pop(sp);
+					append('\0');
+					if (is_text(sp->type)) {
+						tstrip_trailing_ws(ctx);
+						goto got_string;
+					}
+					error(LHTE_INV_STATE_TVALUE); /* TVALUE in non-text-type?! */
+				case '}':
+					/* unprotected } in string means closing of the parent node; cheapest solution is to "fix it" by inserting a \n */
+					insert_char('\n');
+					insert_char('}');
+					break;
+				case '{':
+				case '=':
+				case ':':
+					error(LHTE_INV_TEXT); /* these should be protected */
+					break;
+				default:
+					append(c);
+			}
+	}
+
+	return LHTE_SUCCESS;
+
+finish_nodename_braced:;
+	if ((ctx->tused > 2) && (ctx->token[2] == ':')) { /* token long enough to have an explicit type */
+		lht_node_type_t ty = type_lookup(ctx->token[0], ctx->token[1]);
+		if (ty != LHT_INVALID_TYPE) { /* braced name with a valid type: strip type */
+			append('\0');
+			sp->type = ty;
+			sp->explicit_name = 1;
+			sp->explicit_type = 1;
+			push2(LHT_ST_PREVALUE, sp->type, ctx->token+3, sp->explicit_name, sp->explicit_type);
+			delete();
+			return LHTE_SUCCESS;
+		}
+	}
+	/* else assume implicit "te:", because we didn't have a valid type prefix: */
+
+finish_nodename:;
+	append('\0');
+	push2(LHT_ST_PREVALUE, sp->type, ctx->token, sp->explicit_name, 0);
+	delete();
+	return LHTE_SUCCESS;
+finish_nodename_start_body:;
+finish_nodename_start_bstring:;
+	insert_char('=');
+	insert_char('{');
+	return LHTE_SUCCESS;
+valid_eof:;
+	ctx->col--; /* eof is not really a character, didn't add to column */
+	if (!in_root(ctx))
+		error(LHTE_EOF_NODE);
+implicit_eof:;
+	ctx->event(ctx, LHT_EOF, LHT_INVALID_TYPE, NULL, NULL);
+	sp->state = LHT_ST_DEAD;
+	return LHTE_STOP;
+got_string:;
+	append('\0');
+
+	if (sp->explicit_name) { /* we do have a name */
+		if (*ctx->token == '\0')
+			ctx->event(ctx, LHT_TEXTDATA, sp->type & 0x7f,  sp->str, ""); /* no token means empty node */
+		else
+			ctx->event(ctx, LHT_TEXTDATA, sp->type & 0x7f,  sp->str, ctx->token);
+	}
+	else if (is_text(sp->type)) { /* we do not have a name for sure */
+		if (*ctx->token == '\0')
+			ctx->event(ctx, LHT_TEXTDATA, sp->type, "", sp->str); /* empty token means the one we had earlier must be an anonymous text content */
+		else
+			ctx->event(ctx, LHT_TEXTDATA, sp->type, sp->str == NULL ? "" : sp->str, ctx->token);
+	}
+	else
+		error(LHTE_NO_BODY);
+	delete();
+	pop(sp);
+	set_default_implicit_type(sp, sp->type);
+
+	/* restore implicit node to anonymous */
+	sp->explicit_name = 0;
+	return LHTE_SUCCESS;
+}
+
+lht_pstate_t lht_parser_get_state(lht_parse_t *ctx, int *explicit_type, int *explicit_name)
+{
+	lht_pstack_t *sp;
+	update_sp;
+	if (explicit_type != NULL)
+		*explicit_type = sp->explicit_type;
+	if (explicit_name != NULL)
+		*explicit_name = sp->explicit_name;
+	return sp->state;
+}
+
diff --git a/src_3rd/liblihata/parser.h b/src_3rd/liblihata/parser.h
new file mode 100644
index 0000000..1f107f4
--- /dev/null
+++ b/src_3rd/liblihata/parser.h
@@ -0,0 +1,62 @@
+#include "lihata.h"
+
+/* states of the parser state machine - internal, not needed for ordinary uses */
+typedef enum lht_pstate_e {
+	LHT_ST_DEAD,        /* got an error or finished (valid eof) - any new byte will be ignored */
+	LHT_ST_BODY,        /* body of a list, hash or table; also "body" of a file before the root node */
+	LHT_ST_BACKSLASH,   /* next character is literal */
+	LHT_ST_BSTRING,     /* brace string */
+	LHT_ST_PREVALUE,    /* reading { before value starts */
+	LHT_ST_TVALUE,      /* text value without braces until a sep */
+	LHT_ST_COMMENT      /* reading comment */
+} lht_pstate_t;
+
+typedef struct lht_pstack_s  lht_pstack_t;
+
+typedef enum lht_event_e {
+	LHT_OPEN,            /* open event on node types li, ha and ta; ->value is NULL; ->name is never NULL (anonymous nodes are empty strings) */
+	LHT_CLOSE,           /* close event on node types li, ha and ta; ->name and ->value are NULL, ->nt is invalid */
+	LHT_TEXTDATA,        /* open+close+data on node types te and sy; ->value is the text value */
+	LHT_COMMENT,         /* a single line of comment (out-of-band); ->name is the comment text, ->value is NULL, -> nt is invalid */
+	LHT_EOF,             /* valid end-of-file; ->name is NULL, ->nt is invalid; received on an actual eof or most often earlier, when the root node is closed */
+	LHT_ERROR            /* ->name is the error message, ->value is NULL, ->nt is invalid */
+} lht_event_t;
+
+typedef struct lht_parse_s lht_parse_t;
+struct lht_parse_s {
+	/* public read-write for the caller */
+	void *user_data;  /* caller can attach data to the context; useful in callbacks */
+	void (*event)(lht_parse_t *ctx, lht_event_t ev, lht_node_type_t nt, const char *name, const char *value);  /* called back by the parser */
+
+	/* read-only public states */
+	int line, col;
+
+	/* private internal states */
+	char *token;
+	int tused, talloced;
+
+	lht_pstack_t *stack;
+	int sused, salloced;
+};
+
+/* Initialize parser context - leaves "public read-write for the caller" data
+   untouched. */
+void lht_parser_init(lht_parse_t *ctx);
+
+/* Pass next character to the parser. Repeat until (including) the EOF, as long
+   as LHTE_SUCCESS (0) is returned. If the parser finishes (root node is closed),
+   LHTE_STOP is returned. On error one of the LHTE_* error codes is returned first,
+   then LHTE_STOP on subsequent calls. */
+lht_err_t lht_parser_char(lht_parse_t *ctx, int c);
+
+/* Free all resources allocated during the parse (does not touch
+   "public read-write for the caller" data. The context is left in
+   an invalid state that most probably causes a crash if
+   lht_parser_char() is called with it. It can be reused (even
+   with the same user data) after a lht_parser_init() call. */
+void lht_parser_uninit(lht_parse_t *ctx);
+
+
+/* Internal */
+lht_pstate_t lht_parser_get_state(lht_parse_t *ctx, int *explicit_type, int *explicit_name);
+
diff --git a/src_3rd/liblihata/regression/Eval.awk b/src_3rd/liblihata/regression/Eval.awk
new file mode 100644
index 0000000..ef9e9ce
--- /dev/null
+++ b/src_3rd/liblihata/regression/Eval.awk
@@ -0,0 +1,7 @@
+/: ok/ { pass++ }
+END {
+	if (pass > 0)
+		print "*** QC PASS (" int(pass) " test cases - the full list is in Tests.log) ***"
+	else
+		print "*** QC PASS (no change in test cases or tester - no actions taken) ***"
+}
diff --git a/src_3rd/liblihata/regression/Makefile b/src_3rd/liblihata/regression/Makefile
new file mode 100644
index 0000000..173833a
--- /dev/null
+++ b/src_3rd/liblihata/regression/Makefile
@@ -0,0 +1,44 @@
+ETESTER=../test_parser
+DTESTER=../test_dom
+TTESTER=../test_tree
+LOG=Tests.log
+
+all: Makefile.test
+	@echo "Normal" > $(LOG)
+	@make -f Makefile.test ETESTER=$(ETESTER) DTESTER=$(DTESTER) TTESTER=$(TTESTER) LOG=$(LOG)
+
+# This rule will work only on modern UNIX systems (GNU/Linux)
+# but this is not a big loss: it's for developers to check
+# memory leaks using valgrind, which is not available on
+# too many systems anyway.
+valg: Makefile.test
+	@echo "Valgrind" > $(LOG)
+	@make -f Makefile.test valg ETESTER=$(ETESTER) DTESTER=$(DTESTER) TTESTER=$(TTESTER) LOG=$(LOG)
+	@echo "*** Valgrind result summary (only leaky tests listed): ***"
+	@grep -H "ERROR SUMMARY:" *.evalg *.dvalg | grep -v  "ERROR SUMMARY: 0 errors" ; true
+
+force:
+	@make clean && make all
+
+Makefile.test: Makefile Makegen.sh *.tts *.lht Tests.exclude
+	./Makegen.sh Tests.exclude > Makefile.test
+
+.SUFFIXES: .REF .lht .tts
+
+.lht.REF: Makefile.test
+	@echo "" > $(LOG)
+	@make -f Makefile.test $@ ETESTER=$(ETESTER) DTESTER=$(DTESTER) TTESTER=$(TTESTER) LOG=$(LOG)
+
+.tts.REF: Makefile.test
+	@echo "" > $(LOG)
+	@make -f Makefile.test $@ ETESTER=$(ETESTER) DTESTER=$(DTESTER) TTESTER=$(TTESTER) LOG=$(LOG)
+
+leak:
+	grep -i lost *valg | awk -F '[:]' '{ print $$1 }' | uniq
+
+
+clean: Makefile.test
+	@make -f Makefile.test $@ ETESTER=$(ETESTER) DTESTER=$(DTESTER) TTESTER=$(TTESTER) LOG=$(LOG)
+
+Tests.exclude:
+	echo "" > Tests.exclude
diff --git a/src_3rd/liblihata/regression/Makefile.test b/src_3rd/liblihata/regression/Makefile.test
new file mode 100644
index 0000000..f702b0e
--- /dev/null
+++ b/src_3rd/liblihata/regression/Makefile.test
@@ -0,0 +1,1480 @@
+all: empty.ediff empty_type_decl.ediff err_brace_in_text.ediff err_eof_body.ediff err_eof_comment.ediff err_eof_comment2.ediff err_nobody.ediff excess_closing_braces.ediff hash.ediff list.ediff list_del_nth.ediff list_empty.ediff list_first_symlink.ediff list_missing_last_semicolon.ediff list_nthname.ediff merge_hash.ediff nodes_for_testing.ediff quoting.ediff revpath.ediff stream.ediff sy.ediff symlink.ediff symlink_root_node.ediff ta.ediff table.ediff table_corners.ediff table_named_ce [...]
+	@awk -f Eval.awk < $(LOG)
+
+valg: out/empty.valg out/empty_type_decl.valg out/err_brace_in_text.valg out/err_eof_body.valg out/err_eof_comment.valg out/err_eof_comment2.valg out/err_nobody.valg out/excess_closing_braces.valg out/hash.valg out/list.valg out/list_del_nth.valg out/list_empty.valg out/list_first_symlink.valg out/list_missing_last_semicolon.valg out/list_nthname.valg out/merge_hash.valg out/nodes_for_testing.valg out/quoting.valg out/revpath.valg out/stream.valg out/sy.valg out/symlink.valg out/symlink_ [...]
+
+clean:
+	@rm $(LOG)  out/empty.eout out/empty.dout out/empty.valg out/empty_type_decl.eout out/empty_type_decl.dout out/empty_type_decl.valg out/err_brace_in_text.eout out/err_brace_in_text.dout out/err_brace_in_text.valg out/err_eof_body.eout out/err_eof_body.dout out/err_eof_body.valg out/err_eof_comment.eout out/err_eof_comment.dout out/err_eof_comment.valg out/err_eof_comment2.eout out/err_eof_comment2.dout out/err_eof_comment2.valg out/err_nobody.eout out/err_nobody.dout out/err_nobody.valg [...]
+
+############## lht (parser/dom) rules ##############
+
+# empty
+out/empty.eout: empty.lht $(ETESTER)
+	@$(ETESTER) < empty.lht >$@
+
+out/empty.dout: empty.lht $(DTESTER)
+	@$(DTESTER) < empty.lht >$@
+
+empty.ediff: out/empty.eout ref/empty.eref
+	@diff -u ref/empty.eref out/empty.eout
+	@echo "ev  empty": ok. >> $(LOG)
+
+empty.ddiff: out/empty.dout ref/empty.dref
+	@diff -u ref/empty.dref out/empty.dout
+	@echo "dom empty": ok. >> $(LOG)
+
+
+# empty_type_decl
+out/empty_type_decl.eout: empty_type_decl.lht $(ETESTER)
+	@$(ETESTER) < empty_type_decl.lht >$@
+
+out/empty_type_decl.dout: empty_type_decl.lht $(DTESTER)
+	@$(DTESTER) < empty_type_decl.lht >$@
+
+empty_type_decl.ediff: out/empty_type_decl.eout ref/empty_type_decl.eref
+	@diff -u ref/empty_type_decl.eref out/empty_type_decl.eout
+	@echo "ev  empty_type_decl": ok. >> $(LOG)
+
+empty_type_decl.ddiff: out/empty_type_decl.dout ref/empty_type_decl.dref
+	@diff -u ref/empty_type_decl.dref out/empty_type_decl.dout
+	@echo "dom empty_type_decl": ok. >> $(LOG)
+
+
+# err_brace_in_text
+out/err_brace_in_text.eout: err_brace_in_text.lht $(ETESTER)
+	@$(ETESTER) < err_brace_in_text.lht >$@
+
+out/err_brace_in_text.dout: err_brace_in_text.lht $(DTESTER)
+	@$(DTESTER) < err_brace_in_text.lht >$@
+
+err_brace_in_text.ediff: out/err_brace_in_text.eout ref/err_brace_in_text.eref
+	@diff -u ref/err_brace_in_text.eref out/err_brace_in_text.eout
+	@echo "ev  err_brace_in_text": ok. >> $(LOG)
+
+err_brace_in_text.ddiff: out/err_brace_in_text.dout ref/err_brace_in_text.dref
+	@diff -u ref/err_brace_in_text.dref out/err_brace_in_text.dout
+	@echo "dom err_brace_in_text": ok. >> $(LOG)
+
+
+# err_eof_body
+out/err_eof_body.eout: err_eof_body.lht $(ETESTER)
+	@$(ETESTER) < err_eof_body.lht >$@
+
+out/err_eof_body.dout: err_eof_body.lht $(DTESTER)
+	@$(DTESTER) < err_eof_body.lht >$@
+
+err_eof_body.ediff: out/err_eof_body.eout ref/err_eof_body.eref
+	@diff -u ref/err_eof_body.eref out/err_eof_body.eout
+	@echo "ev  err_eof_body": ok. >> $(LOG)
+
+err_eof_body.ddiff: out/err_eof_body.dout ref/err_eof_body.dref
+	@diff -u ref/err_eof_body.dref out/err_eof_body.dout
+	@echo "dom err_eof_body": ok. >> $(LOG)
+
+
+# err_eof_comment
+out/err_eof_comment.eout: err_eof_comment.lht $(ETESTER)
+	@$(ETESTER) < err_eof_comment.lht >$@
+
+out/err_eof_comment.dout: err_eof_comment.lht $(DTESTER)
+	@$(DTESTER) < err_eof_comment.lht >$@
+
+err_eof_comment.ediff: out/err_eof_comment.eout ref/err_eof_comment.eref
+	@diff -u ref/err_eof_comment.eref out/err_eof_comment.eout
+	@echo "ev  err_eof_comment": ok. >> $(LOG)
+
+err_eof_comment.ddiff: out/err_eof_comment.dout ref/err_eof_comment.dref
+	@diff -u ref/err_eof_comment.dref out/err_eof_comment.dout
+	@echo "dom err_eof_comment": ok. >> $(LOG)
+
+
+# err_eof_comment2
+out/err_eof_comment2.eout: err_eof_comment2.lht $(ETESTER)
+	@$(ETESTER) < err_eof_comment2.lht >$@
+
+out/err_eof_comment2.dout: err_eof_comment2.lht $(DTESTER)
+	@$(DTESTER) < err_eof_comment2.lht >$@
+
+err_eof_comment2.ediff: out/err_eof_comment2.eout ref/err_eof_comment2.eref
+	@diff -u ref/err_eof_comment2.eref out/err_eof_comment2.eout
+	@echo "ev  err_eof_comment2": ok. >> $(LOG)
+
+err_eof_comment2.ddiff: out/err_eof_comment2.dout ref/err_eof_comment2.dref
+	@diff -u ref/err_eof_comment2.dref out/err_eof_comment2.dout
+	@echo "dom err_eof_comment2": ok. >> $(LOG)
+
+
+# err_nobody
+out/err_nobody.eout: err_nobody.lht $(ETESTER)
+	@$(ETESTER) < err_nobody.lht >$@
+
+out/err_nobody.dout: err_nobody.lht $(DTESTER)
+	@$(DTESTER) < err_nobody.lht >$@
+
+err_nobody.ediff: out/err_nobody.eout ref/err_nobody.eref
+	@diff -u ref/err_nobody.eref out/err_nobody.eout
+	@echo "ev  err_nobody": ok. >> $(LOG)
+
+err_nobody.ddiff: out/err_nobody.dout ref/err_nobody.dref
+	@diff -u ref/err_nobody.dref out/err_nobody.dout
+	@echo "dom err_nobody": ok. >> $(LOG)
+
+
+# excess_closing_braces
+out/excess_closing_braces.eout: excess_closing_braces.lht $(ETESTER)
+	@$(ETESTER) < excess_closing_braces.lht >$@
+
+out/excess_closing_braces.dout: excess_closing_braces.lht $(DTESTER)
+	@$(DTESTER) < excess_closing_braces.lht >$@
+
+excess_closing_braces.ediff: out/excess_closing_braces.eout ref/excess_closing_braces.eref
+	@diff -u ref/excess_closing_braces.eref out/excess_closing_braces.eout
+	@echo "ev  excess_closing_braces": ok. >> $(LOG)
+
+excess_closing_braces.ddiff: out/excess_closing_braces.dout ref/excess_closing_braces.dref
+	@diff -u ref/excess_closing_braces.dref out/excess_closing_braces.dout
+	@echo "dom excess_closing_braces": ok. >> $(LOG)
+
+
+# hash
+out/hash.eout: hash.lht $(ETESTER)
+	@$(ETESTER) < hash.lht >$@
+
+out/hash.dout: hash.lht $(DTESTER)
+	@$(DTESTER) < hash.lht >$@
+
+hash.ediff: out/hash.eout ref/hash.eref
+	@diff -u ref/hash.eref out/hash.eout
+	@echo "ev  hash": ok. >> $(LOG)
+
+hash.ddiff: out/hash.dout ref/hash.dref
+	@diff -u ref/hash.dref out/hash.dout
+	@echo "dom hash": ok. >> $(LOG)
+
+
+# list
+out/list.eout: list.lht $(ETESTER)
+	@$(ETESTER) < list.lht >$@
+
+out/list.dout: list.lht $(DTESTER)
+	@$(DTESTER) < list.lht >$@
+
+list.ediff: out/list.eout ref/list.eref
+	@diff -u ref/list.eref out/list.eout
+	@echo "ev  list": ok. >> $(LOG)
+
+list.ddiff: out/list.dout ref/list.dref
+	@diff -u ref/list.dref out/list.dout
+	@echo "dom list": ok. >> $(LOG)
+
+
+# list_del_nth
+out/list_del_nth.eout: list_del_nth.lht $(ETESTER)
+	@$(ETESTER) < list_del_nth.lht >$@
+
+out/list_del_nth.dout: list_del_nth.lht $(DTESTER)
+	@$(DTESTER) < list_del_nth.lht >$@
+
+list_del_nth.ediff: out/list_del_nth.eout ref/list_del_nth.eref
+	@diff -u ref/list_del_nth.eref out/list_del_nth.eout
+	@echo "ev  list_del_nth": ok. >> $(LOG)
+
+list_del_nth.ddiff: out/list_del_nth.dout ref/list_del_nth.dref
+	@diff -u ref/list_del_nth.dref out/list_del_nth.dout
+	@echo "dom list_del_nth": ok. >> $(LOG)
+
+
+# list_empty
+out/list_empty.eout: list_empty.lht $(ETESTER)
+	@$(ETESTER) < list_empty.lht >$@
+
+out/list_empty.dout: list_empty.lht $(DTESTER)
+	@$(DTESTER) < list_empty.lht >$@
+
+list_empty.ediff: out/list_empty.eout ref/list_empty.eref
+	@diff -u ref/list_empty.eref out/list_empty.eout
+	@echo "ev  list_empty": ok. >> $(LOG)
+
+list_empty.ddiff: out/list_empty.dout ref/list_empty.dref
+	@diff -u ref/list_empty.dref out/list_empty.dout
+	@echo "dom list_empty": ok. >> $(LOG)
+
+
+# list_first_symlink
+out/list_first_symlink.eout: list_first_symlink.lht $(ETESTER)
+	@$(ETESTER) < list_first_symlink.lht >$@
+
+out/list_first_symlink.dout: list_first_symlink.lht $(DTESTER)
+	@$(DTESTER) < list_first_symlink.lht >$@
+
+list_first_symlink.ediff: out/list_first_symlink.eout ref/list_first_symlink.eref
+	@diff -u ref/list_first_symlink.eref out/list_first_symlink.eout
+	@echo "ev  list_first_symlink": ok. >> $(LOG)
+
+list_first_symlink.ddiff: out/list_first_symlink.dout ref/list_first_symlink.dref
+	@diff -u ref/list_first_symlink.dref out/list_first_symlink.dout
+	@echo "dom list_first_symlink": ok. >> $(LOG)
+
+
+# list_missing_last_semicolon
+out/list_missing_last_semicolon.eout: list_missing_last_semicolon.lht $(ETESTER)
+	@$(ETESTER) < list_missing_last_semicolon.lht >$@
+
+out/list_missing_last_semicolon.dout: list_missing_last_semicolon.lht $(DTESTER)
+	@$(DTESTER) < list_missing_last_semicolon.lht >$@
+
+list_missing_last_semicolon.ediff: out/list_missing_last_semicolon.eout ref/list_missing_last_semicolon.eref
+	@diff -u ref/list_missing_last_semicolon.eref out/list_missing_last_semicolon.eout
+	@echo "ev  list_missing_last_semicolon": ok. >> $(LOG)
+
+list_missing_last_semicolon.ddiff: out/list_missing_last_semicolon.dout ref/list_missing_last_semicolon.dref
+	@diff -u ref/list_missing_last_semicolon.dref out/list_missing_last_semicolon.dout
+	@echo "dom list_missing_last_semicolon": ok. >> $(LOG)
+
+
+# list_nthname
+out/list_nthname.eout: list_nthname.lht $(ETESTER)
+	@$(ETESTER) < list_nthname.lht >$@
+
+out/list_nthname.dout: list_nthname.lht $(DTESTER)
+	@$(DTESTER) < list_nthname.lht >$@
+
+list_nthname.ediff: out/list_nthname.eout ref/list_nthname.eref
+	@diff -u ref/list_nthname.eref out/list_nthname.eout
+	@echo "ev  list_nthname": ok. >> $(LOG)
+
+list_nthname.ddiff: out/list_nthname.dout ref/list_nthname.dref
+	@diff -u ref/list_nthname.dref out/list_nthname.dout
+	@echo "dom list_nthname": ok. >> $(LOG)
+
+
+# merge_hash
+out/merge_hash.eout: merge_hash.lht $(ETESTER)
+	@$(ETESTER) < merge_hash.lht >$@
+
+out/merge_hash.dout: merge_hash.lht $(DTESTER)
+	@$(DTESTER) < merge_hash.lht >$@
+
+merge_hash.ediff: out/merge_hash.eout ref/merge_hash.eref
+	@diff -u ref/merge_hash.eref out/merge_hash.eout
+	@echo "ev  merge_hash": ok. >> $(LOG)
+
+merge_hash.ddiff: out/merge_hash.dout ref/merge_hash.dref
+	@diff -u ref/merge_hash.dref out/merge_hash.dout
+	@echo "dom merge_hash": ok. >> $(LOG)
+
+
+# nodes_for_testing
+out/nodes_for_testing.eout: nodes_for_testing.lht $(ETESTER)
+	@$(ETESTER) < nodes_for_testing.lht >$@
+
+out/nodes_for_testing.dout: nodes_for_testing.lht $(DTESTER)
+	@$(DTESTER) < nodes_for_testing.lht >$@
+
+nodes_for_testing.ediff: out/nodes_for_testing.eout ref/nodes_for_testing.eref
+	@diff -u ref/nodes_for_testing.eref out/nodes_for_testing.eout
+	@echo "ev  nodes_for_testing": ok. >> $(LOG)
+
+nodes_for_testing.ddiff: out/nodes_for_testing.dout ref/nodes_for_testing.dref
+	@diff -u ref/nodes_for_testing.dref out/nodes_for_testing.dout
+	@echo "dom nodes_for_testing": ok. >> $(LOG)
+
+
+# quoting
+out/quoting.eout: quoting.lht $(ETESTER)
+	@$(ETESTER) < quoting.lht >$@
+
+out/quoting.dout: quoting.lht $(DTESTER)
+	@$(DTESTER) < quoting.lht >$@
+
+quoting.ediff: out/quoting.eout ref/quoting.eref
+	@diff -u ref/quoting.eref out/quoting.eout
+	@echo "ev  quoting": ok. >> $(LOG)
+
+quoting.ddiff: out/quoting.dout ref/quoting.dref
+	@diff -u ref/quoting.dref out/quoting.dout
+	@echo "dom quoting": ok. >> $(LOG)
+
+
+# revpath
+out/revpath.eout: revpath.lht $(ETESTER)
+	@$(ETESTER) < revpath.lht >$@
+
+out/revpath.dout: revpath.lht $(DTESTER)
+	@$(DTESTER) < revpath.lht >$@
+
+revpath.ediff: out/revpath.eout ref/revpath.eref
+	@diff -u ref/revpath.eref out/revpath.eout
+	@echo "ev  revpath": ok. >> $(LOG)
+
+revpath.ddiff: out/revpath.dout ref/revpath.dref
+	@diff -u ref/revpath.dref out/revpath.dout
+	@echo "dom revpath": ok. >> $(LOG)
+
+
+# stream
+out/stream.eout: stream.lht $(ETESTER)
+	@$(ETESTER) < stream.lht >$@
+
+out/stream.dout: stream.lht $(DTESTER)
+	@$(DTESTER) < stream.lht >$@
+
+stream.ediff: out/stream.eout ref/stream.eref
+	@diff -u ref/stream.eref out/stream.eout
+	@echo "ev  stream": ok. >> $(LOG)
+
+stream.ddiff: out/stream.dout ref/stream.dref
+	@diff -u ref/stream.dref out/stream.dout
+	@echo "dom stream": ok. >> $(LOG)
+
+
+# sy
+out/sy.eout: sy.lht $(ETESTER)
+	@$(ETESTER) < sy.lht >$@
+
+out/sy.dout: sy.lht $(DTESTER)
+	@$(DTESTER) < sy.lht >$@
+
+sy.ediff: out/sy.eout ref/sy.eref
+	@diff -u ref/sy.eref out/sy.eout
+	@echo "ev  sy": ok. >> $(LOG)
+
+sy.ddiff: out/sy.dout ref/sy.dref
+	@diff -u ref/sy.dref out/sy.dout
+	@echo "dom sy": ok. >> $(LOG)
+
+
+# symlink
+out/symlink.eout: symlink.lht $(ETESTER)
+	@$(ETESTER) < symlink.lht >$@
+
+out/symlink.dout: symlink.lht $(DTESTER)
+	@$(DTESTER) < symlink.lht >$@
+
+symlink.ediff: out/symlink.eout ref/symlink.eref
+	@diff -u ref/symlink.eref out/symlink.eout
+	@echo "ev  symlink": ok. >> $(LOG)
+
+symlink.ddiff: out/symlink.dout ref/symlink.dref
+	@diff -u ref/symlink.dref out/symlink.dout
+	@echo "dom symlink": ok. >> $(LOG)
+
+
+# symlink_root_node
+out/symlink_root_node.eout: symlink_root_node.lht $(ETESTER)
+	@$(ETESTER) < symlink_root_node.lht >$@
+
+out/symlink_root_node.dout: symlink_root_node.lht $(DTESTER)
+	@$(DTESTER) < symlink_root_node.lht >$@
+
+symlink_root_node.ediff: out/symlink_root_node.eout ref/symlink_root_node.eref
+	@diff -u ref/symlink_root_node.eref out/symlink_root_node.eout
+	@echo "ev  symlink_root_node": ok. >> $(LOG)
+
+symlink_root_node.ddiff: out/symlink_root_node.dout ref/symlink_root_node.dref
+	@diff -u ref/symlink_root_node.dref out/symlink_root_node.dout
+	@echo "dom symlink_root_node": ok. >> $(LOG)
+
+
+# ta
+out/ta.eout: ta.lht $(ETESTER)
+	@$(ETESTER) < ta.lht >$@
+
+out/ta.dout: ta.lht $(DTESTER)
+	@$(DTESTER) < ta.lht >$@
+
+ta.ediff: out/ta.eout ref/ta.eref
+	@diff -u ref/ta.eref out/ta.eout
+	@echo "ev  ta": ok. >> $(LOG)
+
+ta.ddiff: out/ta.dout ref/ta.dref
+	@diff -u ref/ta.dref out/ta.dout
+	@echo "dom ta": ok. >> $(LOG)
+
+
+# table
+out/table.eout: table.lht $(ETESTER)
+	@$(ETESTER) < table.lht >$@
+
+out/table.dout: table.lht $(DTESTER)
+	@$(DTESTER) < table.lht >$@
+
+table.ediff: out/table.eout ref/table.eref
+	@diff -u ref/table.eref out/table.eout
+	@echo "ev  table": ok. >> $(LOG)
+
+table.ddiff: out/table.dout ref/table.dref
+	@diff -u ref/table.dref out/table.dout
+	@echo "dom table": ok. >> $(LOG)
+
+
+# table_corners
+out/table_corners.eout: table_corners.lht $(ETESTER)
+	@$(ETESTER) < table_corners.lht >$@
+
+out/table_corners.dout: table_corners.lht $(DTESTER)
+	@$(DTESTER) < table_corners.lht >$@
+
+table_corners.ediff: out/table_corners.eout ref/table_corners.eref
+	@diff -u ref/table_corners.eref out/table_corners.eout
+	@echo "ev  table_corners": ok. >> $(LOG)
+
+table_corners.ddiff: out/table_corners.dout ref/table_corners.dref
+	@diff -u ref/table_corners.dref out/table_corners.dout
+	@echo "dom table_corners": ok. >> $(LOG)
+
+
+# table_named_cell
+out/table_named_cell.eout: table_named_cell.lht $(ETESTER)
+	@$(ETESTER) < table_named_cell.lht >$@
+
+out/table_named_cell.dout: table_named_cell.lht $(DTESTER)
+	@$(DTESTER) < table_named_cell.lht >$@
+
+table_named_cell.ediff: out/table_named_cell.eout ref/table_named_cell.eref
+	@diff -u ref/table_named_cell.eref out/table_named_cell.eout
+	@echo "ev  table_named_cell": ok. >> $(LOG)
+
+table_named_cell.ddiff: out/table_named_cell.dout ref/table_named_cell.dref
+	@diff -u ref/table_named_cell.dref out/table_named_cell.dout
+	@echo "dom table_named_cell": ok. >> $(LOG)
+
+
+# text
+out/text.eout: text.lht $(ETESTER)
+	@$(ETESTER) < text.lht >$@
+
+out/text.dout: text.lht $(DTESTER)
+	@$(DTESTER) < text.lht >$@
+
+text.ediff: out/text.eout ref/text.eref
+	@diff -u ref/text.eref out/text.eout
+	@echo "ev  text": ok. >> $(LOG)
+
+text.ddiff: out/text.dout ref/text.dref
+	@diff -u ref/text.dref out/text.dout
+	@echo "dom text": ok. >> $(LOG)
+
+
+# text_root_node
+out/text_root_node.eout: text_root_node.lht $(ETESTER)
+	@$(ETESTER) < text_root_node.lht >$@
+
+out/text_root_node.dout: text_root_node.lht $(DTESTER)
+	@$(DTESTER) < text_root_node.lht >$@
+
+text_root_node.ediff: out/text_root_node.eout ref/text_root_node.eref
+	@diff -u ref/text_root_node.eref out/text_root_node.eout
+	@echo "ev  text_root_node": ok. >> $(LOG)
+
+text_root_node.ddiff: out/text_root_node.dout ref/text_root_node.dref
+	@diff -u ref/text_root_node.dref out/text_root_node.dout
+	@echo "dom text_root_node": ok. >> $(LOG)
+
+
+# tiny
+out/tiny.eout: tiny.lht $(ETESTER)
+	@$(ETESTER) < tiny.lht >$@
+
+out/tiny.dout: tiny.lht $(DTESTER)
+	@$(DTESTER) < tiny.lht >$@
+
+tiny.ediff: out/tiny.eout ref/tiny.eref
+	@diff -u ref/tiny.eref out/tiny.eout
+	@echo "ev  tiny": ok. >> $(LOG)
+
+tiny.ddiff: out/tiny.dout ref/tiny.dref
+	@diff -u ref/tiny.dref out/tiny.dout
+	@echo "dom tiny": ok. >> $(LOG)
+
+############## tts (tree tester) rules ##############
+
+# dom_hash_get
+out/dom_hash_get.tout: dom_hash_get.tts $(TTESTER) nodes_for_testing.lht
+	@$(TTESTER) < dom_hash_get.tts >$@
+
+dom_hash_get.tdiff: out/dom_hash_get.tout ref/dom_hash_get.tref
+	@diff -u ref/dom_hash_get.tref out/dom_hash_get.tout
+	@echo "tts dom_hash_get": ok. >> $(LOG)
+
+
+# dom_hash_put
+out/dom_hash_put.tout: dom_hash_put.tts $(TTESTER) hash.lht
+	@$(TTESTER) < dom_hash_put.tts >$@
+
+dom_hash_put.tdiff: out/dom_hash_put.tout ref/dom_hash_put.tref
+	@diff -u ref/dom_hash_put.tref out/dom_hash_put.tout
+	@echo "tts dom_hash_put": ok. >> $(LOG)
+
+
+# dom_iterate
+out/dom_iterate.tout: dom_iterate.tts $(TTESTER) list.lht ta.lht tiny.lht
+	@$(TTESTER) < dom_iterate.tts >$@
+
+dom_iterate.tdiff: out/dom_iterate.tout ref/dom_iterate.tref
+	@diff -u ref/dom_iterate.tref out/dom_iterate.tout
+	@echo "tts dom_iterate": ok. >> $(LOG)
+
+
+# dom_list_append
+out/dom_list_append.tout: dom_list_append.tts $(TTESTER) list.lht
+	@$(TTESTER) < dom_list_append.tts >$@
+
+dom_list_append.tdiff: out/dom_list_append.tout ref/dom_list_append.tref
+	@diff -u ref/dom_list_append.tref out/dom_list_append.tout
+	@echo "tts dom_list_append": ok. >> $(LOG)
+
+
+# dom_list_insert
+out/dom_list_insert.tout: dom_list_insert.tts $(TTESTER) list.lht
+	@$(TTESTER) < dom_list_insert.tts >$@
+
+dom_list_insert.tdiff: out/dom_list_insert.tout ref/dom_list_insert.tref
+	@diff -u ref/dom_list_insert.tref out/dom_list_insert.tout
+	@echo "tts dom_list_insert": ok. >> $(LOG)
+
+
+# dom_list_len
+out/dom_list_len.tout: dom_list_len.tts $(TTESTER) list.lht list_empty.lht
+	@$(TTESTER) < dom_list_len.tts >$@
+
+dom_list_len.tdiff: out/dom_list_len.tout ref/dom_list_len.tref
+	@diff -u ref/dom_list_len.tref out/dom_list_len.tout
+	@echo "tts dom_list_len": ok. >> $(LOG)
+
+
+# dom_load
+out/dom_load.tout: dom_load.tts $(TTESTER)
+	@$(TTESTER) < dom_load.tts >$@
+
+dom_load.tdiff: out/dom_load.tout ref/dom_load.tref
+	@diff -u ref/dom_load.tref out/dom_load.tout
+	@echo "tts dom_load": ok. >> $(LOG)
+
+
+# dom_node_free
+out/dom_node_free.tout: dom_node_free.tts $(TTESTER)
+	@$(TTESTER) < dom_node_free.tts >$@
+
+dom_node_free.tdiff: out/dom_node_free.tout ref/dom_node_free.tref
+	@diff -u ref/dom_node_free.tref out/dom_node_free.tout
+	@echo "tts dom_node_free": ok. >> $(LOG)
+
+
+# export
+out/export.tout: export.tts $(TTESTER) merge_hash.lht text.lht table.lht table_corners.lht
+	@$(TTESTER) < export.tts >$@
+
+export.tdiff: out/export.tout ref/export.tref
+	@diff -u ref/export.tref out/export.tout
+	@echo "tts export": ok. >> $(LOG)
+
+
+# list
+out/list.tout: list.tts $(TTESTER) list.lht
+	@$(TTESTER) < list.tts >$@
+
+list.tdiff: out/list.tout ref/list.tref
+	@diff -u ref/list.tref out/list.tout
+	@echo "tts list": ok. >> $(LOG)
+
+
+# list_del_nth
+out/list_del_nth.tout: list_del_nth.tts $(TTESTER) list_del_nth.lht
+	@$(TTESTER) < list_del_nth.tts >$@
+
+list_del_nth.tdiff: out/list_del_nth.tout ref/list_del_nth.tref
+	@diff -u ref/list_del_nth.tref out/list_del_nth.tout
+	@echo "tts list_del_nth": ok. >> $(LOG)
+
+
+# list_detach_child
+out/list_detach_child.tout: list_detach_child.tts $(TTESTER) list.lht
+	@$(TTESTER) < list_detach_child.tts >$@
+
+list_detach_child.tdiff: out/list_detach_child.tout ref/list_detach_child.tref
+	@diff -u ref/list_detach_child.tref out/list_detach_child.tout
+	@echo "tts list_detach_child": ok. >> $(LOG)
+
+
+# list_detach_nth
+out/list_detach_nth.tout: list_detach_nth.tts $(TTESTER) list.lht
+	@$(TTESTER) < list_detach_nth.tts >$@
+
+list_detach_nth.tdiff: out/list_detach_nth.tout ref/list_detach_nth.tref
+	@diff -u ref/list_detach_nth.tref out/list_detach_nth.tout
+	@echo "tts list_detach_nth": ok. >> $(LOG)
+
+
+# list_nthname
+out/list_nthname.tout: list_nthname.tts $(TTESTER) list_nthname.lht
+	@$(TTESTER) < list_nthname.tts >$@
+
+list_nthname.tdiff: out/list_nthname.tout ref/list_nthname.tref
+	@diff -u ref/list_nthname.tref out/list_nthname.tout
+	@echo "tts list_nthname": ok. >> $(LOG)
+
+
+# list_replace_child
+out/list_replace_child.tout: list_replace_child.tts $(TTESTER) list.lht
+	@$(TTESTER) < list_replace_child.tts >$@
+
+list_replace_child.tdiff: out/list_replace_child.tout ref/list_replace_child.tref
+	@diff -u ref/list_replace_child.tref out/list_replace_child.tout
+	@echo "tts list_replace_child": ok. >> $(LOG)
+
+
+# merge_hash_recurse
+out/merge_hash_recurse.tout: merge_hash_recurse.tts $(TTESTER) merge_hash.lht
+	@$(TTESTER) < merge_hash_recurse.tts >$@
+
+merge_hash_recurse.tdiff: out/merge_hash_recurse.tout ref/merge_hash_recurse.tref
+	@diff -u ref/merge_hash_recurse.tref out/merge_hash_recurse.tout
+	@echo "tts merge_hash_recurse": ok. >> $(LOG)
+
+
+# merge_hash_simple
+out/merge_hash_simple.tout: merge_hash_simple.tts $(TTESTER) merge_hash.lht
+	@$(TTESTER) < merge_hash_simple.tts >$@
+
+merge_hash_simple.tdiff: out/merge_hash_simple.tout ref/merge_hash_simple.tref
+	@diff -u ref/merge_hash_simple.tref out/merge_hash_simple.tout
+	@echo "tts merge_hash_simple": ok. >> $(LOG)
+
+
+# merge_hash_sybad
+out/merge_hash_sybad.tout: merge_hash_sybad.tts $(TTESTER) merge_hash.lht
+	@$(TTESTER) < merge_hash_sybad.tts >$@
+
+merge_hash_sybad.tdiff: out/merge_hash_sybad.tout ref/merge_hash_sybad.tref
+	@diff -u ref/merge_hash_sybad.tref out/merge_hash_sybad.tout
+	@echo "tts merge_hash_sybad": ok. >> $(LOG)
+
+
+# merge_hash_sygood
+out/merge_hash_sygood.tout: merge_hash_sygood.tts $(TTESTER) merge_hash.lht
+	@$(TTESTER) < merge_hash_sygood.tts >$@
+
+merge_hash_sygood.tdiff: out/merge_hash_sygood.tout ref/merge_hash_sygood.tref
+	@diff -u ref/merge_hash_sygood.tref out/merge_hash_sygood.tout
+	@echo "tts merge_hash_sygood": ok. >> $(LOG)
+
+
+# merge_list
+out/merge_list.tout: merge_list.tts $(TTESTER) list.lht
+	@$(TTESTER) < merge_list.tts >$@
+
+merge_list.tdiff: out/merge_list.tout ref/merge_list.tref
+	@diff -u ref/merge_list.tref out/merge_list.tout
+	@echo "tts merge_list": ok. >> $(LOG)
+
+
+# merge_table
+out/merge_table.tout: merge_table.tts $(TTESTER) table.lht
+	@$(TTESTER) < merge_table.tts >$@
+
+merge_table.tdiff: out/merge_table.tout ref/merge_table.tref
+	@diff -u ref/merge_table.tref out/merge_table.tout
+	@echo "tts merge_table": ok. >> $(LOG)
+
+
+# merge_text
+out/merge_text.tout: merge_text.tts $(TTESTER) text.lht
+	@$(TTESTER) < merge_text.tts >$@
+
+merge_text.tdiff: out/merge_text.tout ref/merge_text.tref
+	@diff -u ref/merge_text.tref out/merge_text.tout
+	@echo "tts merge_text": ok. >> $(LOG)
+
+
+# node_is_under
+out/node_is_under.tout: node_is_under.tts $(TTESTER) table_corners.lht
+	@$(TTESTER) < node_is_under.tts >$@
+
+node_is_under.tdiff: out/node_is_under.tout ref/node_is_under.tref
+	@diff -u ref/node_is_under.tref out/node_is_under.tout
+	@echo "tts node_is_under": ok. >> $(LOG)
+
+
+# path
+out/path.tout: path.tts $(TTESTER) revpath.lht sy.lht
+	@$(TTESTER) < path.tts >$@
+
+path.tdiff: out/path.tout ref/path.tref
+	@diff -u ref/path.tref out/path.tout
+	@echo "tts path": ok. >> $(LOG)
+
+
+# revpath
+out/revpath.tout: revpath.tts $(TTESTER) revpath.lht
+	@$(TTESTER) < revpath.tts >$@
+
+revpath.tdiff: out/revpath.tout ref/revpath.tref
+	@diff -u ref/revpath.tref out/revpath.tout
+	@echo "tts revpath": ok. >> $(LOG)
+
+
+# symlink_is_broken
+out/symlink_is_broken.tout: symlink_is_broken.tts $(TTESTER) symlink.lht
+	@$(TTESTER) < symlink_is_broken.tts >$@
+
+symlink_is_broken.tdiff: out/symlink_is_broken.tout ref/symlink_is_broken.tref
+	@diff -u ref/symlink_is_broken.tref out/symlink_is_broken.tout
+	@echo "tts symlink_is_broken": ok. >> $(LOG)
+
+
+# ta_del_c
+out/ta_del_c.tout: ta_del_c.tts $(TTESTER) ta.lht
+	@$(TTESTER) < ta_del_c.tts >$@
+
+ta_del_c.tdiff: out/ta_del_c.tout ref/ta_del_c.tref
+	@diff -u ref/ta_del_c.tref out/ta_del_c.tout
+	@echo "tts ta_del_c": ok. >> $(LOG)
+
+
+# ta_del_r
+out/ta_del_r.tout: ta_del_r.tts $(TTESTER) ta.lht
+	@$(TTESTER) < ta_del_r.tts >$@
+
+ta_del_r.tdiff: out/ta_del_r.tout ref/ta_del_r.tref
+	@diff -u ref/ta_del_r.tref out/ta_del_r.tout
+	@echo "tts ta_del_r": ok. >> $(LOG)
+
+
+# ta_ins_c
+out/ta_ins_c.tout: ta_ins_c.tts $(TTESTER) ta.lht
+	@$(TTESTER) < ta_ins_c.tts >$@
+
+ta_ins_c.tdiff: out/ta_ins_c.tout ref/ta_ins_c.tref
+	@diff -u ref/ta_ins_c.tref out/ta_ins_c.tout
+	@echo "tts ta_ins_c": ok. >> $(LOG)
+
+
+# ta_ins_c2
+out/ta_ins_c2.tout: ta_ins_c2.tts $(TTESTER) ta.lht
+	@$(TTESTER) < ta_ins_c2.tts >$@
+
+ta_ins_c2.tdiff: out/ta_ins_c2.tout ref/ta_ins_c2.tref
+	@diff -u ref/ta_ins_c2.tref out/ta_ins_c2.tout
+	@echo "tts ta_ins_c2": ok. >> $(LOG)
+
+
+# ta_ins_r
+out/ta_ins_r.tout: ta_ins_r.tts $(TTESTER) ta.lht
+	@$(TTESTER) < ta_ins_r.tts >$@
+
+ta_ins_r.tdiff: out/ta_ins_r.tout ref/ta_ins_r.tref
+	@diff -u ref/ta_ins_r.tref out/ta_ins_r.tout
+	@echo "tts ta_ins_r": ok. >> $(LOG)
+
+
+# ta_ins_r2
+out/ta_ins_r2.tout: ta_ins_r2.tts $(TTESTER) ta.lht
+	@$(TTESTER) < ta_ins_r2.tts >$@
+
+ta_ins_r2.tdiff: out/ta_ins_r2.tout ref/ta_ins_r2.tref
+	@diff -u ref/ta_ins_r2.tref out/ta_ins_r2.tout
+	@echo "tts ta_ins_r2": ok. >> $(LOG)
+
+
+# table_detach_cell
+out/table_detach_cell.tout: table_detach_cell.tts $(TTESTER) table_corners.lht
+	@$(TTESTER) < table_detach_cell.tts >$@
+
+table_detach_cell.tdiff: out/table_detach_cell.tout ref/table_detach_cell.tref
+	@diff -u ref/table_detach_cell.tref out/table_detach_cell.tout
+	@echo "tts table_detach_cell": ok. >> $(LOG)
+
+
+# table_detach_child
+out/table_detach_child.tout: table_detach_child.tts $(TTESTER) table_corners.lht
+	@$(TTESTER) < table_detach_child.tts >$@
+
+table_detach_child.tdiff: out/table_detach_child.tout ref/table_detach_child.tref
+	@diff -u ref/table_detach_child.tref out/table_detach_child.tout
+	@echo "tts table_detach_child": ok. >> $(LOG)
+
+
+# table_find_cell
+out/table_find_cell.tout: table_find_cell.tts $(TTESTER) table_corners.lht revpath.lht
+	@$(TTESTER) < table_find_cell.tts >$@
+
+table_find_cell.tdiff: out/table_find_cell.tout ref/table_find_cell.tref
+	@diff -u ref/table_find_cell.tref out/table_find_cell.tout
+	@echo "tts table_find_cell": ok. >> $(LOG)
+
+
+# table_named_cell
+out/table_named_cell.tout: table_named_cell.tts $(TTESTER) table_named_cell.lht
+	@$(TTESTER) < table_named_cell.tts >$@
+
+table_named_cell.tdiff: out/table_named_cell.tout ref/table_named_cell.tref
+	@diff -u ref/table_named_cell.tref out/table_named_cell.tout
+	@echo "tts table_named_cell": ok. >> $(LOG)
+
+
+# table_read
+out/table_read.tout: table_read.tts $(TTESTER) table.lht
+	@$(TTESTER) < table_read.tts >$@
+
+table_read.tdiff: out/table_read.tout ref/table_read.tref
+	@diff -u ref/table_read.tref out/table_read.tout
+	@echo "tts table_read": ok. >> $(LOG)
+
+
+# table_replace_cell
+out/table_replace_cell.tout: table_replace_cell.tts $(TTESTER) table_corners.lht
+	@$(TTESTER) < table_replace_cell.tts >$@
+
+table_replace_cell.tdiff: out/table_replace_cell.tout ref/table_replace_cell.tref
+	@diff -u ref/table_replace_cell.tref out/table_replace_cell.tout
+	@echo "tts table_replace_cell": ok. >> $(LOG)
+
+
+# table_replace_child
+out/table_replace_child.tout: table_replace_child.tts $(TTESTER) table_corners.lht
+	@$(TTESTER) < table_replace_child.tts >$@
+
+table_replace_child.tdiff: out/table_replace_child.tout ref/table_replace_child.tref
+	@diff -u ref/table_replace_child.tref out/table_replace_child.tout
+	@echo "tts table_replace_child": ok. >> $(LOG)
+
+
+# tree_detach
+out/tree_detach.tout: tree_detach.tts $(TTESTER) list.lht
+	@$(TTESTER) < tree_detach.tts >$@
+
+tree_detach.tdiff: out/tree_detach.tout ref/tree_detach.tref
+	@diff -u ref/tree_detach.tref out/tree_detach.tout
+	@echo "tts tree_detach": ok. >> $(LOG)
+
+
+# tree_dup_hash
+out/tree_dup_hash.tout: tree_dup_hash.tts $(TTESTER) merge_hash.lht
+	@$(TTESTER) < tree_dup_hash.tts >$@
+
+tree_dup_hash.tdiff: out/tree_dup_hash.tout ref/tree_dup_hash.tref
+	@diff -u ref/tree_dup_hash.tref out/tree_dup_hash.tout
+	@echo "tts tree_dup_hash": ok. >> $(LOG)
+
+
+# tree_dup_list
+out/tree_dup_list.tout: tree_dup_list.tts $(TTESTER) list.lht
+	@$(TTESTER) < tree_dup_list.tts >$@
+
+tree_dup_list.tdiff: out/tree_dup_list.tout ref/tree_dup_list.tref
+	@diff -u ref/tree_dup_list.tref out/tree_dup_list.tout
+	@echo "tts tree_dup_list": ok. >> $(LOG)
+
+
+# tree_dup_sy
+out/tree_dup_sy.tout: tree_dup_sy.tts $(TTESTER) sy.lht
+	@$(TTESTER) < tree_dup_sy.tts >$@
+
+tree_dup_sy.tdiff: out/tree_dup_sy.tout ref/tree_dup_sy.tref
+	@diff -u ref/tree_dup_sy.tref out/tree_dup_sy.tout
+	@echo "tts tree_dup_sy": ok. >> $(LOG)
+
+
+# tree_dup_table
+out/tree_dup_table.tout: tree_dup_table.tts $(TTESTER) table_corners.lht
+	@$(TTESTER) < tree_dup_table.tts >$@
+
+tree_dup_table.tdiff: out/tree_dup_table.tout ref/tree_dup_table.tref
+	@diff -u ref/tree_dup_table.tref out/tree_dup_table.tout
+	@echo "tts tree_dup_table": ok. >> $(LOG)
+
+
+# tree_has_symlink
+out/tree_has_symlink.tout: tree_has_symlink.tts $(TTESTER) symlink.lht table.lht symlink_root_node.lht
+	@$(TTESTER) < tree_has_symlink.tts >$@
+
+tree_has_symlink.tdiff: out/tree_has_symlink.tout ref/tree_has_symlink.tref
+	@diff -u ref/tree_has_symlink.tref out/tree_has_symlink.tout
+	@echo "tts tree_has_symlink": ok. >> $(LOG)
+
+
+# tree_unlink
+out/tree_unlink.tout: tree_unlink.tts $(TTESTER) list.lht
+	@$(TTESTER) < tree_unlink.tts >$@
+
+tree_unlink.tdiff: out/tree_unlink.tout ref/tree_unlink.tref
+	@diff -u ref/tree_unlink.tref out/tree_unlink.tout
+	@echo "tts tree_unlink": ok. >> $(LOG)
+
+############## common rules ##############
+empty.REF: empty.lht 
+	@$(ETESTER) < empty.lht >ref/empty.eref
+	@$(DTESTER) < empty.lht >ref/empty.dref
+	@echo "*** WARNING: missing reference ref/empty.eref and .dref have been generated. Please validate ***"
+
+out/empty.valg: empty.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=empty.evalg $(ETESTER) < empty.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=empty.dvalg $(DTESTER) < empty.lht > /dev/null
+
+empty_type_decl.REF: empty_type_decl.lht 
+	@$(ETESTER) < empty_type_decl.lht >ref/empty_type_decl.eref
+	@$(DTESTER) < empty_type_decl.lht >ref/empty_type_decl.dref
+	@echo "*** WARNING: missing reference ref/empty_type_decl.eref and .dref have been generated. Please validate ***"
+
+out/empty_type_decl.valg: empty_type_decl.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=empty_type_decl.evalg $(ETESTER) < empty_type_decl.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=empty_type_decl.dvalg $(DTESTER) < empty_type_decl.lht > /dev/null
+
+err_brace_in_text.REF: err_brace_in_text.lht 
+	@$(ETESTER) < err_brace_in_text.lht >ref/err_brace_in_text.eref
+	@$(DTESTER) < err_brace_in_text.lht >ref/err_brace_in_text.dref
+	@echo "*** WARNING: missing reference ref/err_brace_in_text.eref and .dref have been generated. Please validate ***"
+
+out/err_brace_in_text.valg: err_brace_in_text.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_brace_in_text.evalg $(ETESTER) < err_brace_in_text.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_brace_in_text.dvalg $(DTESTER) < err_brace_in_text.lht > /dev/null
+
+err_eof_body.REF: err_eof_body.lht 
+	@$(ETESTER) < err_eof_body.lht >ref/err_eof_body.eref
+	@$(DTESTER) < err_eof_body.lht >ref/err_eof_body.dref
+	@echo "*** WARNING: missing reference ref/err_eof_body.eref and .dref have been generated. Please validate ***"
+
+out/err_eof_body.valg: err_eof_body.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_eof_body.evalg $(ETESTER) < err_eof_body.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_eof_body.dvalg $(DTESTER) < err_eof_body.lht > /dev/null
+
+err_eof_comment.REF: err_eof_comment.lht 
+	@$(ETESTER) < err_eof_comment.lht >ref/err_eof_comment.eref
+	@$(DTESTER) < err_eof_comment.lht >ref/err_eof_comment.dref
+	@echo "*** WARNING: missing reference ref/err_eof_comment.eref and .dref have been generated. Please validate ***"
+
+out/err_eof_comment.valg: err_eof_comment.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_eof_comment.evalg $(ETESTER) < err_eof_comment.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_eof_comment.dvalg $(DTESTER) < err_eof_comment.lht > /dev/null
+
+err_eof_comment2.REF: err_eof_comment2.lht 
+	@$(ETESTER) < err_eof_comment2.lht >ref/err_eof_comment2.eref
+	@$(DTESTER) < err_eof_comment2.lht >ref/err_eof_comment2.dref
+	@echo "*** WARNING: missing reference ref/err_eof_comment2.eref and .dref have been generated. Please validate ***"
+
+out/err_eof_comment2.valg: err_eof_comment2.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_eof_comment2.evalg $(ETESTER) < err_eof_comment2.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_eof_comment2.dvalg $(DTESTER) < err_eof_comment2.lht > /dev/null
+
+err_nobody.REF: err_nobody.lht 
+	@$(ETESTER) < err_nobody.lht >ref/err_nobody.eref
+	@$(DTESTER) < err_nobody.lht >ref/err_nobody.dref
+	@echo "*** WARNING: missing reference ref/err_nobody.eref and .dref have been generated. Please validate ***"
+
+out/err_nobody.valg: err_nobody.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_nobody.evalg $(ETESTER) < err_nobody.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=err_nobody.dvalg $(DTESTER) < err_nobody.lht > /dev/null
+
+excess_closing_braces.REF: excess_closing_braces.lht 
+	@$(ETESTER) < excess_closing_braces.lht >ref/excess_closing_braces.eref
+	@$(DTESTER) < excess_closing_braces.lht >ref/excess_closing_braces.dref
+	@echo "*** WARNING: missing reference ref/excess_closing_braces.eref and .dref have been generated. Please validate ***"
+
+out/excess_closing_braces.valg: excess_closing_braces.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=excess_closing_braces.evalg $(ETESTER) < excess_closing_braces.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=excess_closing_braces.dvalg $(DTESTER) < excess_closing_braces.lht > /dev/null
+
+hash.REF: hash.lht 
+	@$(ETESTER) < hash.lht >ref/hash.eref
+	@$(DTESTER) < hash.lht >ref/hash.dref
+	@echo "*** WARNING: missing reference ref/hash.eref and .dref have been generated. Please validate ***"
+
+out/hash.valg: hash.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=hash.evalg $(ETESTER) < hash.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=hash.dvalg $(DTESTER) < hash.lht > /dev/null
+
+list.REF: list.lht list.tts
+	@$(ETESTER) < list.lht >ref/list.eref
+	@$(DTESTER) < list.lht >ref/list.dref
+	@echo "*** WARNING: missing reference ref/list.eref and .dref have been generated. Please validate ***"
+	@$(TTESTER) < list.tts >ref/list.tref
+	@echo "*** WARNING: missing reference ref/list.tref has been generated. Please validate ***"
+
+out/list.valg: list.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list.evalg $(ETESTER) < list.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list.dvalg $(DTESTER) < list.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list.tvalg $(TTESTER) < list.tts > /dev/null
+
+list_del_nth.REF: list_del_nth.lht list_del_nth.tts
+	@$(ETESTER) < list_del_nth.lht >ref/list_del_nth.eref
+	@$(DTESTER) < list_del_nth.lht >ref/list_del_nth.dref
+	@echo "*** WARNING: missing reference ref/list_del_nth.eref and .dref have been generated. Please validate ***"
+	@$(TTESTER) < list_del_nth.tts >ref/list_del_nth.tref
+	@echo "*** WARNING: missing reference ref/list_del_nth.tref has been generated. Please validate ***"
+
+out/list_del_nth.valg: list_del_nth.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_del_nth.evalg $(ETESTER) < list_del_nth.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_del_nth.dvalg $(DTESTER) < list_del_nth.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_del_nth.tvalg $(TTESTER) < list_del_nth.tts > /dev/null
+
+list_empty.REF: list_empty.lht 
+	@$(ETESTER) < list_empty.lht >ref/list_empty.eref
+	@$(DTESTER) < list_empty.lht >ref/list_empty.dref
+	@echo "*** WARNING: missing reference ref/list_empty.eref and .dref have been generated. Please validate ***"
+
+out/list_empty.valg: list_empty.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_empty.evalg $(ETESTER) < list_empty.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_empty.dvalg $(DTESTER) < list_empty.lht > /dev/null
+
+list_first_symlink.REF: list_first_symlink.lht 
+	@$(ETESTER) < list_first_symlink.lht >ref/list_first_symlink.eref
+	@$(DTESTER) < list_first_symlink.lht >ref/list_first_symlink.dref
+	@echo "*** WARNING: missing reference ref/list_first_symlink.eref and .dref have been generated. Please validate ***"
+
+out/list_first_symlink.valg: list_first_symlink.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_first_symlink.evalg $(ETESTER) < list_first_symlink.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_first_symlink.dvalg $(DTESTER) < list_first_symlink.lht > /dev/null
+
+list_missing_last_semicolon.REF: list_missing_last_semicolon.lht 
+	@$(ETESTER) < list_missing_last_semicolon.lht >ref/list_missing_last_semicolon.eref
+	@$(DTESTER) < list_missing_last_semicolon.lht >ref/list_missing_last_semicolon.dref
+	@echo "*** WARNING: missing reference ref/list_missing_last_semicolon.eref and .dref have been generated. Please validate ***"
+
+out/list_missing_last_semicolon.valg: list_missing_last_semicolon.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_missing_last_semicolon.evalg $(ETESTER) < list_missing_last_semicolon.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_missing_last_semicolon.dvalg $(DTESTER) < list_missing_last_semicolon.lht > /dev/null
+
+list_nthname.REF: list_nthname.lht list_nthname.tts
+	@$(ETESTER) < list_nthname.lht >ref/list_nthname.eref
+	@$(DTESTER) < list_nthname.lht >ref/list_nthname.dref
+	@echo "*** WARNING: missing reference ref/list_nthname.eref and .dref have been generated. Please validate ***"
+	@$(TTESTER) < list_nthname.tts >ref/list_nthname.tref
+	@echo "*** WARNING: missing reference ref/list_nthname.tref has been generated. Please validate ***"
+
+out/list_nthname.valg: list_nthname.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_nthname.evalg $(ETESTER) < list_nthname.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_nthname.dvalg $(DTESTER) < list_nthname.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_nthname.tvalg $(TTESTER) < list_nthname.tts > /dev/null
+
+merge_hash.REF: merge_hash.lht 
+	@$(ETESTER) < merge_hash.lht >ref/merge_hash.eref
+	@$(DTESTER) < merge_hash.lht >ref/merge_hash.dref
+	@echo "*** WARNING: missing reference ref/merge_hash.eref and .dref have been generated. Please validate ***"
+
+out/merge_hash.valg: merge_hash.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_hash.evalg $(ETESTER) < merge_hash.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_hash.dvalg $(DTESTER) < merge_hash.lht > /dev/null
+
+nodes_for_testing.REF: nodes_for_testing.lht 
+	@$(ETESTER) < nodes_for_testing.lht >ref/nodes_for_testing.eref
+	@$(DTESTER) < nodes_for_testing.lht >ref/nodes_for_testing.dref
+	@echo "*** WARNING: missing reference ref/nodes_for_testing.eref and .dref have been generated. Please validate ***"
+
+out/nodes_for_testing.valg: nodes_for_testing.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=nodes_for_testing.evalg $(ETESTER) < nodes_for_testing.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=nodes_for_testing.dvalg $(DTESTER) < nodes_for_testing.lht > /dev/null
+
+quoting.REF: quoting.lht 
+	@$(ETESTER) < quoting.lht >ref/quoting.eref
+	@$(DTESTER) < quoting.lht >ref/quoting.dref
+	@echo "*** WARNING: missing reference ref/quoting.eref and .dref have been generated. Please validate ***"
+
+out/quoting.valg: quoting.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=quoting.evalg $(ETESTER) < quoting.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=quoting.dvalg $(DTESTER) < quoting.lht > /dev/null
+
+revpath.REF: revpath.lht revpath.tts
+	@$(ETESTER) < revpath.lht >ref/revpath.eref
+	@$(DTESTER) < revpath.lht >ref/revpath.dref
+	@echo "*** WARNING: missing reference ref/revpath.eref and .dref have been generated. Please validate ***"
+	@$(TTESTER) < revpath.tts >ref/revpath.tref
+	@echo "*** WARNING: missing reference ref/revpath.tref has been generated. Please validate ***"
+
+out/revpath.valg: revpath.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=revpath.evalg $(ETESTER) < revpath.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=revpath.dvalg $(DTESTER) < revpath.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=revpath.tvalg $(TTESTER) < revpath.tts > /dev/null
+
+stream.REF: stream.lht 
+	@$(ETESTER) < stream.lht >ref/stream.eref
+	@$(DTESTER) < stream.lht >ref/stream.dref
+	@echo "*** WARNING: missing reference ref/stream.eref and .dref have been generated. Please validate ***"
+
+out/stream.valg: stream.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=stream.evalg $(ETESTER) < stream.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=stream.dvalg $(DTESTER) < stream.lht > /dev/null
+
+sy.REF: sy.lht 
+	@$(ETESTER) < sy.lht >ref/sy.eref
+	@$(DTESTER) < sy.lht >ref/sy.dref
+	@echo "*** WARNING: missing reference ref/sy.eref and .dref have been generated. Please validate ***"
+
+out/sy.valg: sy.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=sy.evalg $(ETESTER) < sy.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=sy.dvalg $(DTESTER) < sy.lht > /dev/null
+
+symlink.REF: symlink.lht 
+	@$(ETESTER) < symlink.lht >ref/symlink.eref
+	@$(DTESTER) < symlink.lht >ref/symlink.dref
+	@echo "*** WARNING: missing reference ref/symlink.eref and .dref have been generated. Please validate ***"
+
+out/symlink.valg: symlink.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=symlink.evalg $(ETESTER) < symlink.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=symlink.dvalg $(DTESTER) < symlink.lht > /dev/null
+
+symlink_root_node.REF: symlink_root_node.lht 
+	@$(ETESTER) < symlink_root_node.lht >ref/symlink_root_node.eref
+	@$(DTESTER) < symlink_root_node.lht >ref/symlink_root_node.dref
+	@echo "*** WARNING: missing reference ref/symlink_root_node.eref and .dref have been generated. Please validate ***"
+
+out/symlink_root_node.valg: symlink_root_node.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=symlink_root_node.evalg $(ETESTER) < symlink_root_node.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=symlink_root_node.dvalg $(DTESTER) < symlink_root_node.lht > /dev/null
+
+ta.REF: ta.lht 
+	@$(ETESTER) < ta.lht >ref/ta.eref
+	@$(DTESTER) < ta.lht >ref/ta.dref
+	@echo "*** WARNING: missing reference ref/ta.eref and .dref have been generated. Please validate ***"
+
+out/ta.valg: ta.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta.evalg $(ETESTER) < ta.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta.dvalg $(DTESTER) < ta.lht > /dev/null
+
+table.REF: table.lht 
+	@$(ETESTER) < table.lht >ref/table.eref
+	@$(DTESTER) < table.lht >ref/table.dref
+	@echo "*** WARNING: missing reference ref/table.eref and .dref have been generated. Please validate ***"
+
+out/table.valg: table.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table.evalg $(ETESTER) < table.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table.dvalg $(DTESTER) < table.lht > /dev/null
+
+table_corners.REF: table_corners.lht 
+	@$(ETESTER) < table_corners.lht >ref/table_corners.eref
+	@$(DTESTER) < table_corners.lht >ref/table_corners.dref
+	@echo "*** WARNING: missing reference ref/table_corners.eref and .dref have been generated. Please validate ***"
+
+out/table_corners.valg: table_corners.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_corners.evalg $(ETESTER) < table_corners.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_corners.dvalg $(DTESTER) < table_corners.lht > /dev/null
+
+table_named_cell.REF: table_named_cell.lht table_named_cell.tts
+	@$(ETESTER) < table_named_cell.lht >ref/table_named_cell.eref
+	@$(DTESTER) < table_named_cell.lht >ref/table_named_cell.dref
+	@echo "*** WARNING: missing reference ref/table_named_cell.eref and .dref have been generated. Please validate ***"
+	@$(TTESTER) < table_named_cell.tts >ref/table_named_cell.tref
+	@echo "*** WARNING: missing reference ref/table_named_cell.tref has been generated. Please validate ***"
+
+out/table_named_cell.valg: table_named_cell.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_named_cell.evalg $(ETESTER) < table_named_cell.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_named_cell.dvalg $(DTESTER) < table_named_cell.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_named_cell.tvalg $(TTESTER) < table_named_cell.tts > /dev/null
+
+text.REF: text.lht 
+	@$(ETESTER) < text.lht >ref/text.eref
+	@$(DTESTER) < text.lht >ref/text.dref
+	@echo "*** WARNING: missing reference ref/text.eref and .dref have been generated. Please validate ***"
+
+out/text.valg: text.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=text.evalg $(ETESTER) < text.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=text.dvalg $(DTESTER) < text.lht > /dev/null
+
+text_root_node.REF: text_root_node.lht 
+	@$(ETESTER) < text_root_node.lht >ref/text_root_node.eref
+	@$(DTESTER) < text_root_node.lht >ref/text_root_node.dref
+	@echo "*** WARNING: missing reference ref/text_root_node.eref and .dref have been generated. Please validate ***"
+
+out/text_root_node.valg: text_root_node.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=text_root_node.evalg $(ETESTER) < text_root_node.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=text_root_node.dvalg $(DTESTER) < text_root_node.lht > /dev/null
+
+tiny.REF: tiny.lht 
+	@$(ETESTER) < tiny.lht >ref/tiny.eref
+	@$(DTESTER) < tiny.lht >ref/tiny.dref
+	@echo "*** WARNING: missing reference ref/tiny.eref and .dref have been generated. Please validate ***"
+
+out/tiny.valg: tiny.lht $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tiny.evalg $(ETESTER) < tiny.lht > /dev/null
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tiny.dvalg $(DTESTER) < tiny.lht > /dev/null
+
+dom_hash_get.REF:  dom_hash_get.tts
+	@$(TTESTER) < dom_hash_get.tts >ref/dom_hash_get.tref
+	@echo "*** WARNING: missing reference ref/dom_hash_get.tref has been generated. Please validate ***"
+
+out/dom_hash_get.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_hash_get.tvalg $(TTESTER) < dom_hash_get.tts > /dev/null
+
+dom_hash_put.REF:  dom_hash_put.tts
+	@$(TTESTER) < dom_hash_put.tts >ref/dom_hash_put.tref
+	@echo "*** WARNING: missing reference ref/dom_hash_put.tref has been generated. Please validate ***"
+
+out/dom_hash_put.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_hash_put.tvalg $(TTESTER) < dom_hash_put.tts > /dev/null
+
+dom_iterate.REF:  dom_iterate.tts
+	@$(TTESTER) < dom_iterate.tts >ref/dom_iterate.tref
+	@echo "*** WARNING: missing reference ref/dom_iterate.tref has been generated. Please validate ***"
+
+out/dom_iterate.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_iterate.tvalg $(TTESTER) < dom_iterate.tts > /dev/null
+
+dom_list_append.REF:  dom_list_append.tts
+	@$(TTESTER) < dom_list_append.tts >ref/dom_list_append.tref
+	@echo "*** WARNING: missing reference ref/dom_list_append.tref has been generated. Please validate ***"
+
+out/dom_list_append.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_list_append.tvalg $(TTESTER) < dom_list_append.tts > /dev/null
+
+dom_list_insert.REF:  dom_list_insert.tts
+	@$(TTESTER) < dom_list_insert.tts >ref/dom_list_insert.tref
+	@echo "*** WARNING: missing reference ref/dom_list_insert.tref has been generated. Please validate ***"
+
+out/dom_list_insert.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_list_insert.tvalg $(TTESTER) < dom_list_insert.tts > /dev/null
+
+dom_list_len.REF:  dom_list_len.tts
+	@$(TTESTER) < dom_list_len.tts >ref/dom_list_len.tref
+	@echo "*** WARNING: missing reference ref/dom_list_len.tref has been generated. Please validate ***"
+
+out/dom_list_len.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_list_len.tvalg $(TTESTER) < dom_list_len.tts > /dev/null
+
+dom_load.REF:  dom_load.tts
+	@$(TTESTER) < dom_load.tts >ref/dom_load.tref
+	@echo "*** WARNING: missing reference ref/dom_load.tref has been generated. Please validate ***"
+
+out/dom_load.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_load.tvalg $(TTESTER) < dom_load.tts > /dev/null
+
+dom_node_free.REF:  dom_node_free.tts
+	@$(TTESTER) < dom_node_free.tts >ref/dom_node_free.tref
+	@echo "*** WARNING: missing reference ref/dom_node_free.tref has been generated. Please validate ***"
+
+out/dom_node_free.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=dom_node_free.tvalg $(TTESTER) < dom_node_free.tts > /dev/null
+
+export.REF:  export.tts
+	@$(TTESTER) < export.tts >ref/export.tref
+	@echo "*** WARNING: missing reference ref/export.tref has been generated. Please validate ***"
+
+out/export.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=export.tvalg $(TTESTER) < export.tts > /dev/null
+
+list_detach_child.REF:  list_detach_child.tts
+	@$(TTESTER) < list_detach_child.tts >ref/list_detach_child.tref
+	@echo "*** WARNING: missing reference ref/list_detach_child.tref has been generated. Please validate ***"
+
+out/list_detach_child.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_detach_child.tvalg $(TTESTER) < list_detach_child.tts > /dev/null
+
+list_detach_nth.REF:  list_detach_nth.tts
+	@$(TTESTER) < list_detach_nth.tts >ref/list_detach_nth.tref
+	@echo "*** WARNING: missing reference ref/list_detach_nth.tref has been generated. Please validate ***"
+
+out/list_detach_nth.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_detach_nth.tvalg $(TTESTER) < list_detach_nth.tts > /dev/null
+
+list_replace_child.REF:  list_replace_child.tts
+	@$(TTESTER) < list_replace_child.tts >ref/list_replace_child.tref
+	@echo "*** WARNING: missing reference ref/list_replace_child.tref has been generated. Please validate ***"
+
+out/list_replace_child.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=list_replace_child.tvalg $(TTESTER) < list_replace_child.tts > /dev/null
+
+merge_hash_recurse.REF:  merge_hash_recurse.tts
+	@$(TTESTER) < merge_hash_recurse.tts >ref/merge_hash_recurse.tref
+	@echo "*** WARNING: missing reference ref/merge_hash_recurse.tref has been generated. Please validate ***"
+
+out/merge_hash_recurse.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_hash_recurse.tvalg $(TTESTER) < merge_hash_recurse.tts > /dev/null
+
+merge_hash_simple.REF:  merge_hash_simple.tts
+	@$(TTESTER) < merge_hash_simple.tts >ref/merge_hash_simple.tref
+	@echo "*** WARNING: missing reference ref/merge_hash_simple.tref has been generated. Please validate ***"
+
+out/merge_hash_simple.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_hash_simple.tvalg $(TTESTER) < merge_hash_simple.tts > /dev/null
+
+merge_hash_sybad.REF:  merge_hash_sybad.tts
+	@$(TTESTER) < merge_hash_sybad.tts >ref/merge_hash_sybad.tref
+	@echo "*** WARNING: missing reference ref/merge_hash_sybad.tref has been generated. Please validate ***"
+
+out/merge_hash_sybad.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_hash_sybad.tvalg $(TTESTER) < merge_hash_sybad.tts > /dev/null
+
+merge_hash_sygood.REF:  merge_hash_sygood.tts
+	@$(TTESTER) < merge_hash_sygood.tts >ref/merge_hash_sygood.tref
+	@echo "*** WARNING: missing reference ref/merge_hash_sygood.tref has been generated. Please validate ***"
+
+out/merge_hash_sygood.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_hash_sygood.tvalg $(TTESTER) < merge_hash_sygood.tts > /dev/null
+
+merge_list.REF:  merge_list.tts
+	@$(TTESTER) < merge_list.tts >ref/merge_list.tref
+	@echo "*** WARNING: missing reference ref/merge_list.tref has been generated. Please validate ***"
+
+out/merge_list.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_list.tvalg $(TTESTER) < merge_list.tts > /dev/null
+
+merge_table.REF:  merge_table.tts
+	@$(TTESTER) < merge_table.tts >ref/merge_table.tref
+	@echo "*** WARNING: missing reference ref/merge_table.tref has been generated. Please validate ***"
+
+out/merge_table.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_table.tvalg $(TTESTER) < merge_table.tts > /dev/null
+
+merge_text.REF:  merge_text.tts
+	@$(TTESTER) < merge_text.tts >ref/merge_text.tref
+	@echo "*** WARNING: missing reference ref/merge_text.tref has been generated. Please validate ***"
+
+out/merge_text.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=merge_text.tvalg $(TTESTER) < merge_text.tts > /dev/null
+
+node_is_under.REF:  node_is_under.tts
+	@$(TTESTER) < node_is_under.tts >ref/node_is_under.tref
+	@echo "*** WARNING: missing reference ref/node_is_under.tref has been generated. Please validate ***"
+
+out/node_is_under.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=node_is_under.tvalg $(TTESTER) < node_is_under.tts > /dev/null
+
+path.REF:  path.tts
+	@$(TTESTER) < path.tts >ref/path.tref
+	@echo "*** WARNING: missing reference ref/path.tref has been generated. Please validate ***"
+
+out/path.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=path.tvalg $(TTESTER) < path.tts > /dev/null
+
+symlink_is_broken.REF:  symlink_is_broken.tts
+	@$(TTESTER) < symlink_is_broken.tts >ref/symlink_is_broken.tref
+	@echo "*** WARNING: missing reference ref/symlink_is_broken.tref has been generated. Please validate ***"
+
+out/symlink_is_broken.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=symlink_is_broken.tvalg $(TTESTER) < symlink_is_broken.tts > /dev/null
+
+ta_del_c.REF:  ta_del_c.tts
+	@$(TTESTER) < ta_del_c.tts >ref/ta_del_c.tref
+	@echo "*** WARNING: missing reference ref/ta_del_c.tref has been generated. Please validate ***"
+
+out/ta_del_c.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta_del_c.tvalg $(TTESTER) < ta_del_c.tts > /dev/null
+
+ta_del_r.REF:  ta_del_r.tts
+	@$(TTESTER) < ta_del_r.tts >ref/ta_del_r.tref
+	@echo "*** WARNING: missing reference ref/ta_del_r.tref has been generated. Please validate ***"
+
+out/ta_del_r.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta_del_r.tvalg $(TTESTER) < ta_del_r.tts > /dev/null
+
+ta_ins_c.REF:  ta_ins_c.tts
+	@$(TTESTER) < ta_ins_c.tts >ref/ta_ins_c.tref
+	@echo "*** WARNING: missing reference ref/ta_ins_c.tref has been generated. Please validate ***"
+
+out/ta_ins_c.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta_ins_c.tvalg $(TTESTER) < ta_ins_c.tts > /dev/null
+
+ta_ins_c2.REF:  ta_ins_c2.tts
+	@$(TTESTER) < ta_ins_c2.tts >ref/ta_ins_c2.tref
+	@echo "*** WARNING: missing reference ref/ta_ins_c2.tref has been generated. Please validate ***"
+
+out/ta_ins_c2.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta_ins_c2.tvalg $(TTESTER) < ta_ins_c2.tts > /dev/null
+
+ta_ins_r.REF:  ta_ins_r.tts
+	@$(TTESTER) < ta_ins_r.tts >ref/ta_ins_r.tref
+	@echo "*** WARNING: missing reference ref/ta_ins_r.tref has been generated. Please validate ***"
+
+out/ta_ins_r.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta_ins_r.tvalg $(TTESTER) < ta_ins_r.tts > /dev/null
+
+ta_ins_r2.REF:  ta_ins_r2.tts
+	@$(TTESTER) < ta_ins_r2.tts >ref/ta_ins_r2.tref
+	@echo "*** WARNING: missing reference ref/ta_ins_r2.tref has been generated. Please validate ***"
+
+out/ta_ins_r2.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=ta_ins_r2.tvalg $(TTESTER) < ta_ins_r2.tts > /dev/null
+
+table_detach_cell.REF:  table_detach_cell.tts
+	@$(TTESTER) < table_detach_cell.tts >ref/table_detach_cell.tref
+	@echo "*** WARNING: missing reference ref/table_detach_cell.tref has been generated. Please validate ***"
+
+out/table_detach_cell.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_detach_cell.tvalg $(TTESTER) < table_detach_cell.tts > /dev/null
+
+table_detach_child.REF:  table_detach_child.tts
+	@$(TTESTER) < table_detach_child.tts >ref/table_detach_child.tref
+	@echo "*** WARNING: missing reference ref/table_detach_child.tref has been generated. Please validate ***"
+
+out/table_detach_child.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_detach_child.tvalg $(TTESTER) < table_detach_child.tts > /dev/null
+
+table_find_cell.REF:  table_find_cell.tts
+	@$(TTESTER) < table_find_cell.tts >ref/table_find_cell.tref
+	@echo "*** WARNING: missing reference ref/table_find_cell.tref has been generated. Please validate ***"
+
+out/table_find_cell.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_find_cell.tvalg $(TTESTER) < table_find_cell.tts > /dev/null
+
+table_read.REF:  table_read.tts
+	@$(TTESTER) < table_read.tts >ref/table_read.tref
+	@echo "*** WARNING: missing reference ref/table_read.tref has been generated. Please validate ***"
+
+out/table_read.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_read.tvalg $(TTESTER) < table_read.tts > /dev/null
+
+table_replace_cell.REF:  table_replace_cell.tts
+	@$(TTESTER) < table_replace_cell.tts >ref/table_replace_cell.tref
+	@echo "*** WARNING: missing reference ref/table_replace_cell.tref has been generated. Please validate ***"
+
+out/table_replace_cell.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_replace_cell.tvalg $(TTESTER) < table_replace_cell.tts > /dev/null
+
+table_replace_child.REF:  table_replace_child.tts
+	@$(TTESTER) < table_replace_child.tts >ref/table_replace_child.tref
+	@echo "*** WARNING: missing reference ref/table_replace_child.tref has been generated. Please validate ***"
+
+out/table_replace_child.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=table_replace_child.tvalg $(TTESTER) < table_replace_child.tts > /dev/null
+
+tree_detach.REF:  tree_detach.tts
+	@$(TTESTER) < tree_detach.tts >ref/tree_detach.tref
+	@echo "*** WARNING: missing reference ref/tree_detach.tref has been generated. Please validate ***"
+
+out/tree_detach.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_detach.tvalg $(TTESTER) < tree_detach.tts > /dev/null
+
+tree_dup_hash.REF:  tree_dup_hash.tts
+	@$(TTESTER) < tree_dup_hash.tts >ref/tree_dup_hash.tref
+	@echo "*** WARNING: missing reference ref/tree_dup_hash.tref has been generated. Please validate ***"
+
+out/tree_dup_hash.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_dup_hash.tvalg $(TTESTER) < tree_dup_hash.tts > /dev/null
+
+tree_dup_list.REF:  tree_dup_list.tts
+	@$(TTESTER) < tree_dup_list.tts >ref/tree_dup_list.tref
+	@echo "*** WARNING: missing reference ref/tree_dup_list.tref has been generated. Please validate ***"
+
+out/tree_dup_list.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_dup_list.tvalg $(TTESTER) < tree_dup_list.tts > /dev/null
+
+tree_dup_sy.REF:  tree_dup_sy.tts
+	@$(TTESTER) < tree_dup_sy.tts >ref/tree_dup_sy.tref
+	@echo "*** WARNING: missing reference ref/tree_dup_sy.tref has been generated. Please validate ***"
+
+out/tree_dup_sy.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_dup_sy.tvalg $(TTESTER) < tree_dup_sy.tts > /dev/null
+
+tree_dup_table.REF:  tree_dup_table.tts
+	@$(TTESTER) < tree_dup_table.tts >ref/tree_dup_table.tref
+	@echo "*** WARNING: missing reference ref/tree_dup_table.tref has been generated. Please validate ***"
+
+out/tree_dup_table.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_dup_table.tvalg $(TTESTER) < tree_dup_table.tts > /dev/null
+
+tree_has_symlink.REF:  tree_has_symlink.tts
+	@$(TTESTER) < tree_has_symlink.tts >ref/tree_has_symlink.tref
+	@echo "*** WARNING: missing reference ref/tree_has_symlink.tref has been generated. Please validate ***"
+
+out/tree_has_symlink.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_has_symlink.tvalg $(TTESTER) < tree_has_symlink.tts > /dev/null
+
+tree_unlink.REF:  tree_unlink.tts
+	@$(TTESTER) < tree_unlink.tts >ref/tree_unlink.tref
+	@echo "*** WARNING: missing reference ref/tree_unlink.tref has been generated. Please validate ***"
+
+out/tree_unlink.valg: $(ETESTER)
+	valgrind -v --show-reachable=yes --leak-check=full --log-file=tree_unlink.tvalg $(TTESTER) < tree_unlink.tts > /dev/null
+
+############## build testers ##############
+$(ETESTER) $(DTESTER) $(TTESTER):
+	cd .. && make
diff --git a/src_3rd/liblihata/regression/Makegen.sh b/src_3rd/liblihata/regression/Makegen.sh
new file mode 100755
index 0000000..cbfc66c
--- /dev/null
+++ b/src_3rd/liblihata/regression/Makegen.sh
@@ -0,0 +1,193 @@
+#!/bin/sh
+
+# make sure sort order is the same
+export LANG=C
+
+# This will not work if:
+#  - there's no ls, /bin/sh or awk installed (but the output is provided so only new tests are affected, can be done manually)
+#  - test names contain space (they won't)
+#  - instead of setting RS to "[ \t\r\n]" and deal with records, we need to read file names from fields - some awk implementations won't allow regex in RS
+ls *.lht *.tts | sort | awk -v "exfn=$1" '
+	BEGIN {
+		lht_names = 0;
+		tts_names = 0;
+		out_dir = "out/"
+		ref_dir = "ref/"
+		valg = "valgrind -v --show-reachable=yes --leak-check=full"
+		while((getline < exfn) > 0)
+			EXCL[$1]++
+	}
+
+	/.lht$/ {
+		if ($1 in EXCL)
+			next
+		name=$0
+		sub(".lht$", "", name)
+		LHT_NAME[lht_names] = name;
+		LHTS[name]++
+		lht_names++
+		next
+	}
+
+	/.tts$/ {
+		if ($1 in EXCL)
+			next
+		name=$0
+		sub(".tts$", "", name)
+		TTS_NAME[tts_names] = name;
+		TTSS[name]++
+		tts_names++
+		next
+	}
+
+	{
+		print "WARNING: unknown test suffix " $0 " - not generating test rule for that" > "/dev/stderr"
+	}
+
+	# calculate lht dependencies of a tts file by reading all load_doc commands
+	function tts_deps(fn    ,deps, DEPS)
+	{
+		close(fn)
+		deps = ""
+		while((getline < fn) > 0) {
+			if ($1 == "load_doc") {
+				if (!($3 in DEPS)) {
+					deps = deps " " $3
+					DEPS[$3]++
+				}
+			}
+		}
+		close(fn)
+		return deps
+	}
+
+	# generate rules for .lht: run parser and dom parser tests
+	function lht_rule(name) {
+		print ""
+		print "# " name
+		print out_dir name ".eout: " name ".lht $(ETESTER)"
+		print "	@$(ETESTER) < " name ".lht >$@"
+		print ""
+		print out_dir name ".dout: " name ".lht $(DTESTER)"
+		print "	@$(DTESTER) < " name ".lht >$@"
+		print ""
+		print name ".ediff: " out_dir name ".eout " ref_dir name ".eref"
+		print "	@diff -u " ref_dir name ".eref " out_dir name ".eout"
+		print "	@echo \"ev  " name "\": ok. >> $(LOG)"
+		print ""
+		print name ".ddiff: " out_dir name ".dout " ref_dir name ".dref"
+		print "	@diff -u " ref_dir name ".dref " out_dir name ".dout"
+		print "	@echo \"dom " name "\": ok. >> $(LOG)"
+		print ""
+	}
+
+# generate tree_test rules for a .tts file
+	function tts_rule(name) {
+		print ""
+		print "# " name
+		print out_dir name ".tout: " name ".tts $(TTESTER)" tts_deps(name ".tts")
+		print "	@$(TTESTER) < " name ".tts >$@"
+		print ""
+		print name ".tdiff: " out_dir name ".tout " ref_dir name ".tref"
+		print "	@diff -u " ref_dir name ".tref " out_dir name ".tout"
+		print "	@echo \"tts " name "\": ok. >> $(LOG)"
+		print ""
+	}
+
+# generate common rules for tests, depending on whether name is
+# a .lht and/or a .tts test
+	function common_rule(name) {
+		if (name in COMMON_SEEN)
+			return
+
+		COMMON_SEEN[name]++
+
+		print name ".REF: "   ((name in LHTS) ? name ".lht" : ""), ((name in TTSS) ? name ".tts" : "")
+		if (name in LHTS) {
+			print "	@$(ETESTER) < " name ".lht >" ref_dir name ".eref"
+			print "	@$(DTESTER) < " name ".lht >" ref_dir name ".dref"
+			print "	@echo \"*** WARNING: missing reference " ref_dir name ".eref and .dref have been generated. Please validate ***\""
+		}
+		if (name in TTSS) {
+			print "	@$(TTESTER) < " name ".tts >" ref_dir name ".tref"
+			print "	@echo \"*** WARNING: missing reference " ref_dir name ".tref has been generated. Please validate ***\""
+		}
+		print ""
+
+		if (name in LHTS)
+			lht = name ".lht "
+		else
+			lht = ""
+		print out_dir name ".valg: " lht "$(ETESTER)"
+		if (name in LHTS) {
+			print "	" valg " --log-file=" name ".evalg $(ETESTER) < " name ".lht > /dev/null"
+			print "	" valg " --log-file=" name ".dvalg $(DTESTER) < " name ".lht > /dev/null"
+		}
+		if (name in TTSS)
+			print "	" valg " --log-file=" name ".tvalg $(TTESTER) < " name ".tts > /dev/null"
+
+		print ""
+	}
+
+
+# generate all rules, starting with the invoke-all ones
+	END {
+		printf("all:");
+
+		for(n = 0; n < lht_names; n++)
+			printf(" %s.ediff", LHT_NAME[n]);
+
+# run dom test only after evtests - the errors found by the first batch will
+# break the tree and cause errors in the dom batch as well
+		for(n = 0; n < lht_names; n++)
+			printf(" %s.ddiff", LHT_NAME[n]);
+
+		for(n = 0; n < tts_names; n++)
+			printf(" %s.tdiff", TTS_NAME[n]);
+
+		print ""
+		print "	@awk -f Eval.awk < $(LOG)"
+		print ""
+
+		printf("valg:");
+		for(n = 0; n < lht_names; n++)
+			printf(" %s%s.valg", out_dir, LHT_NAME[n]);
+		for(n = 0; n < tts_names; n++)
+			printf(" %s%s.valg", out_dir, TTS_NAME[n]);
+		print ""
+		print ""
+
+
+		print "clean:"
+		printf("	@rm $(LOG) ")
+		for(n = 0; n < lht_names; n++) {
+			printf(" %s%s.eout", out_dir, LHT_NAME[n]);
+			printf(" %s%s.dout", out_dir, LHT_NAME[n]);
+			printf(" %s%s.valg", out_dir, LHT_NAME[n]);
+		}
+		for(n = 0; n < tts_names; n++) {
+			printf(" %s%s.tout", out_dir, TTS_NAME[n]);
+		}
+		print " 2>/dev/null ; true"
+		print ""
+
+		print "############## lht (parser/dom) rules ##############"
+		for(n = 0; n < lht_names; n++)
+			lht_rule(LHT_NAME[n])
+
+		print "############## tts (tree tester) rules ##############"
+		for(n = 0; n < tts_names; n++)
+			tts_rule(TTS_NAME[n])
+
+		print "############## common rules ##############"
+		for(n = 0; n < lht_names; n++)
+			common_rule(LHT_NAME[n])
+		for(n = 0; n < tts_names; n++)
+			common_rule(TTS_NAME[n])
+
+		print "############## build testers ##############"
+		print "$(ETESTER) $(DTESTER) $(TTESTER):"
+		print "	cd .. && make"
+
+	}
+'
diff --git a/src_3rd/liblihata/regression/dom_hash_get.tts b/src_3rd/liblihata/regression/dom_hash_get.tts
new file mode 100644
index 0000000..a60d7c6
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_hash_get.tts
@@ -0,0 +1,16 @@
+load_doc D1 nodes_for_testing.lht
+root N1 D1
+echo The document:
+print_doc D1
+
+echo -
+echo Node with key "0":
+dom_hash_get N1 N2 0
+print_node N2
+
+echo -
+echo Node with invalid key:
+dom_hash_get N1 N3 11
+print_node N3
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/dom_hash_put.tts b/src_3rd/liblihata/regression/dom_hash_put.tts
new file mode 100644
index 0000000..5a93146
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_hash_put.tts
@@ -0,0 +1,26 @@
+load_doc D1 hash.lht
+root N1 D1
+new_text N2 "to_be_put"
+echo Doc before put:
+print_doc D1
+echo Node before put:
+print_node N2
+
+dom_hash_put N1 N2
+
+echo -
+echo Doc after put:
+print_doc D1
+echo Node after put:
+print_node N2
+
+new_text N3 "to_be_put_again"
+echo Node before put:
+print_node N3
+
+dom_hash_put N1 N3
+
+# since this node won't be part of the hash, it should be free'd
+dom_node_free N3
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/dom_iterate.tts b/src_3rd/liblihata/regression/dom_iterate.tts
new file mode 100644
index 0000000..2e7b389
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_iterate.tts
@@ -0,0 +1,40 @@
+load_doc D1 list.lht
+echo list.lht:
+print_doc D1
+
+root N1 D1
+echo root:
+print_node N1
+
+echo Children of root:
+dom_iterate N1
+
+echo -
+
+load_doc D2 ta.lht
+echo ta.lht:
+print_doc D2
+
+root N2 D2
+echo root:
+print_node N2
+
+echo Children of root:
+dom_iterate N2
+
+echo -
+
+load_doc D3 tiny.lht
+echo tiny.lht:
+print_doc D3
+
+root N3 D3
+echo root:
+print_node N3
+
+echo Children of root:
+dom_iterate N3
+
+free_doc D1
+free_doc D2
+free_doc D3
diff --git a/src_3rd/liblihata/regression/dom_list_append.tts b/src_3rd/liblihata/regression/dom_list_append.tts
new file mode 100644
index 0000000..1bf163e
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_list_append.tts
@@ -0,0 +1,17 @@
+load_doc D1 list.lht
+root N1 D1
+new_text N2 "to_be_appended"
+echo Doc before append:
+print_doc D1
+echo Node before append:
+print_node N2
+
+dom_list_append N1 N2
+
+echo -
+echo Doc after append:
+print_doc D1
+echo Node after append:
+print_node N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/dom_list_insert.tts b/src_3rd/liblihata/regression/dom_list_insert.tts
new file mode 100644
index 0000000..18b1465
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_list_insert.tts
@@ -0,0 +1,17 @@
+load_doc D1 list.lht
+root N1 D1
+new_text N2 "to_be_inserted"
+echo Doc before insert:
+print_doc D1
+echo Node before insert:
+print_node N2
+
+dom_list_insert N1 N2
+
+echo -
+echo Doc after insert:
+print_doc D1
+echo Node after insert:
+print_node N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/dom_list_len.tts b/src_3rd/liblihata/regression/dom_list_len.tts
new file mode 100644
index 0000000..b36a907
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_list_len.tts
@@ -0,0 +1,10 @@
+load_doc D1 list.lht
+root N1 D1
+dom_list_len N1
+
+load_doc D2 list_empty.lht
+root N2 D2
+dom_list_len N2
+
+free_doc D1
+free_doc D2
diff --git a/src_3rd/liblihata/regression/dom_load.tts b/src_3rd/liblihata/regression/dom_load.tts
new file mode 100644
index 0000000..3a4bacb
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_load.tts
@@ -0,0 +1,7 @@
+dom_load D1 
+print_doc D1
+echo -
+dom_load D2 table.lht
+print_doc D2
+
+free_doc D2
diff --git a/src_3rd/liblihata/regression/dom_node_free.tts b/src_3rd/liblihata/regression/dom_node_free.tts
new file mode 100644
index 0000000..5c15f3f
--- /dev/null
+++ b/src_3rd/liblihata/regression/dom_node_free.tts
@@ -0,0 +1,4 @@
+echo Creating a new, detached text node:
+new_text N2 "detached"
+print_node N2
+dom_node_free N2
diff --git a/src_3rd/liblihata/regression/empty.lht b/src_3rd/liblihata/regression/empty.lht
new file mode 100644
index 0000000..e69de29
diff --git a/src_3rd/liblihata/regression/empty_type_decl.lht b/src_3rd/liblihata/regression/empty_type_decl.lht
new file mode 100644
index 0000000..32f8e28
--- /dev/null
+++ b/src_3rd/liblihata/regression/empty_type_decl.lht
@@ -0,0 +1 @@
+li:{te: li: ha: ta:}
diff --git a/src_3rd/liblihata/regression/err_brace_in_text.lht b/src_3rd/liblihata/regression/err_brace_in_text.lht
new file mode 100644
index 0000000..a135530
--- /dev/null
+++ b/src_3rd/liblihata/regression/err_brace_in_text.lht
@@ -0,0 +1,5 @@
+# this document is invalid: the first te:{} is an explicitly anonymous text node
+# and the first } would be the contet - however, it's impossible to decide
+# how many }s are part of the content. Instead, unprotected } in text node is
+# error.
+li:{te:{}}}}}
diff --git a/src_3rd/liblihata/regression/err_eof_body.lht b/src_3rd/liblihata/regression/err_eof_body.lht
new file mode 100644
index 0000000..bb0a7a2
--- /dev/null
+++ b/src_3rd/liblihata/regression/err_eof_body.lht
@@ -0,0 +1,2 @@
+li:root {
+	key=value;
diff --git a/src_3rd/liblihata/regression/err_eof_comment.lht b/src_3rd/liblihata/regression/err_eof_comment.lht
new file mode 100644
index 0000000..12cf8fd
--- /dev/null
+++ b/src_3rd/liblihata/regression/err_eof_comment.lht
@@ -0,0 +1,3 @@
+li:root {
+	key=value;
+# unterminated comment with list left open
diff --git a/src_3rd/liblihata/regression/err_eof_comment2.lht b/src_3rd/liblihata/regression/err_eof_comment2.lht
new file mode 100644
index 0000000..22083f2
--- /dev/null
+++ b/src_3rd/liblihata/regression/err_eof_comment2.lht
@@ -0,0 +1,4 @@
+li:root {
+	key=value;
+}
+# unterminated comment (valid) - will be ingored because of root is closed
diff --git a/src_3rd/liblihata/regression/err_nobody.lht b/src_3rd/liblihata/regression/err_nobody.lht
new file mode 100644
index 0000000..c8727b1
--- /dev/null
+++ b/src_3rd/liblihata/regression/err_nobody.lht
@@ -0,0 +1,3 @@
+li:{
+	li:name
+}
diff --git a/src_3rd/liblihata/regression/excess_closing_braces.lht b/src_3rd/liblihata/regression/excess_closing_braces.lht
new file mode 100644
index 0000000..2360895
--- /dev/null
+++ b/src_3rd/liblihata/regression/excess_closing_braces.lht
@@ -0,0 +1,2 @@
+# this document is valid: characters are ignored after closing the root node.
+li:{te:{}{}}}}
diff --git a/src_3rd/liblihata/regression/export.tts b/src_3rd/liblihata/regression/export.tts
new file mode 100644
index 0000000..72a6423
--- /dev/null
+++ b/src_3rd/liblihata/regression/export.tts
@@ -0,0 +1,26 @@
+# use table_ins_row to test lihata 'delete row' functionality
+
+
+echo -----merge_hash.lht-----
+load_doc D1 merge_hash.lht
+root N1 D1
+export N1
+free_doc D1
+
+echo -----text.lht-----
+load_doc D1 text.lht
+root N1 D1
+export N1
+free_doc D1
+
+echo -----table.lht-----
+load_doc D1 table.lht
+root N1 D1
+export N1
+free_doc D1
+
+echo -----table_corners.lht-----
+load_doc D1 table_corners.lht
+root N1 D1
+export N1
+free_doc D1
diff --git a/src_3rd/liblihata/regression/hash.lht b/src_3rd/liblihata/regression/hash.lht
new file mode 100644
index 0000000..f8b8c8d
--- /dev/null
+++ b/src_3rd/liblihata/regression/hash.lht
@@ -0,0 +1,8 @@
+ha:{
+	ha:empty {
+	}
+	ha:non-empty {
+		key1=val1
+		key2=val2
+	}
+}
diff --git a/src_3rd/liblihata/regression/list.lht b/src_3rd/liblihata/regression/list.lht
new file mode 100644
index 0000000..b6217de
--- /dev/null
+++ b/src_3rd/liblihata/regression/list.lht
@@ -0,0 +1,8 @@
+li:{
+	li:empty {
+	}
+	li:non-empty {
+		key1=val1
+		key2=val2
+	}
+}
diff --git a/src_3rd/liblihata/regression/list.tts b/src_3rd/liblihata/regression/list.tts
new file mode 100644
index 0000000..db13734
--- /dev/null
+++ b/src_3rd/liblihata/regression/list.tts
@@ -0,0 +1,36 @@
+load_doc D2 list.lht
+
+echo root:
+root N1 D2
+print_tree N1
+
+echo first list:
+list_child N1
+print_tree N1
+
+echo second list:
+list_next N1
+equ_node N2 N1
+print_tree N2
+
+
+echo nth 0:
+list_nth N2 0
+print_tree N2
+
+echo nth 1:
+equ_node N2 N1
+list_nth N2 1
+print_tree N2
+
+echo nth 2:
+equ_node N2 N1
+list_nth N2 2
+print_tree N2
+
+echo nth -1:
+equ_node N2 N1
+list_nth N2 -1
+print_tree N2
+
+free_doc D2
diff --git a/src_3rd/liblihata/regression/list_del_nth.lht b/src_3rd/liblihata/regression/list_del_nth.lht
new file mode 100644
index 0000000..d574208
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_del_nth.lht
@@ -0,0 +1,9 @@
+li:{
+	li:empty {
+	}
+	li:non-empty {
+		a=val1
+		b=val2
+		c=val3
+	}
+}
diff --git a/src_3rd/liblihata/regression/list_del_nth.tts b/src_3rd/liblihata/regression/list_del_nth.tts
new file mode 100644
index 0000000..5b2fb40
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_del_nth.tts
@@ -0,0 +1,70 @@
+load_doc D2 list_del_nth.lht
+
+echo root:
+root N1 D2
+print_tree N1
+
+echo second list:
+list_child N1
+list_next N1
+print_tree N1
+
+echo delete 0th item from first list:
+root N2 D2
+list_child N2
+list_del_nth N2 0
+print_tree N2
+
+echo delete 1st item from first list:
+root N2 D2
+list_child N2
+list_del_nth N2 1
+print_tree N2
+
+echo delete -1st item from first list:
+root N2 D2
+list_child N2
+list_del_nth N2 -1
+print_tree N2
+
+echo delete 0th item from second list, repeated 4x:
+root N2 D2
+list_child N2
+list_next N2
+print_tree N2
+list_del_nth N2 0
+print_tree N2
+list_del_nth N2 0
+print_tree N2
+list_del_nth N2 0
+print_tree N2
+list_del_nth N2 0
+print_tree N2
+
+echo delete last item from second list, repeated 4x:
+free_doc D2
+load_doc D2 list_del_nth.lht
+root N2 D2
+list_child N2
+list_next N2
+print_tree N2
+list_del_nth N2 2
+print_tree N2
+list_del_nth N2 1
+print_tree N2
+list_del_nth N2 0
+print_tree N2
+list_del_nth N2 0
+print_tree N2
+
+echo delete middle item from second list:
+free_doc D2
+load_doc D2 list_del_nth.lht
+root N2 D2
+list_child N2
+list_next N2
+print_tree N2
+list_del_nth N2 1
+print_tree N2
+
+free_doc D2
diff --git a/src_3rd/liblihata/regression/list_detach_child.tts b/src_3rd/liblihata/regression/list_detach_child.tts
new file mode 100644
index 0000000..f07bdbc
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_detach_child.tts
@@ -0,0 +1,48 @@
+echo Trying to detach an existing node in the list:
+
+load_doc D1 list.lht
+
+root N1 D1
+list_child N1
+list_next N1
+
+equ_node N2 N1
+list_child N2
+
+echo --- Before detaching
+print_tree N1
+
+list_detach_child N1 N2
+
+echo --- After detaching
+print_tree N1
+
+echo Detaching the last node from the list:
+
+equ_node N2 N1
+list_child N2
+
+echo --- Before detaching
+print_tree N1
+
+list_detach_child N1 N2
+
+echo --- After detaching
+print_tree N1
+
+
+echo Trying to detach from an empty list:
+
+equ_node N2 N1
+list_child N2
+
+echo --- Before detaching
+print_tree N1
+
+list_detach_child N1 N2
+
+echo --- After detaching
+print_tree N1
+
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/list_detach_nth.tts b/src_3rd/liblihata/regression/list_detach_nth.tts
new file mode 100644
index 0000000..4dc3d47
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_detach_nth.tts
@@ -0,0 +1,51 @@
+load_doc D1 list.lht
+
+root N1 D1
+equ_node N2 N1
+list_child N2
+
+echo The root tree before detaching:
+print_tree N1
+echo The root node before detaching:
+print_node N1
+echo The node should be detached before action:
+print_node N2
+
+echo -----
+list_detach_nth N1 0
+echo -----
+
+echo The root tree after detaching:
+print_tree N1
+echo The root node after detaching:
+print_node N1
+echo The detached node:
+print_node N2
+
+echo =====
+equ_node N2 N1
+list_child N2
+
+echo The root tree before detaching:
+print_tree N1
+echo The root node before detaching:
+print_node N1
+echo The node should be detached before action:
+print_node N2
+
+echo -----
+list_detach_nth N1 0
+echo -----
+
+echo The root tree after detaching:
+print_tree N1
+echo The root node after detaching:
+print_node N1
+echo The detached node:
+print_node N2
+
+echo =====
+echo Trying to detach from empty tree:
+list_detach_nth N1 0
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/list_empty.lht b/src_3rd/liblihata/regression/list_empty.lht
new file mode 100644
index 0000000..1288231
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_empty.lht
@@ -0,0 +1,2 @@
+li:empty {
+}
diff --git a/src_3rd/liblihata/regression/list_first_symlink.lht b/src_3rd/liblihata/regression/list_first_symlink.lht
new file mode 100644
index 0000000..8d79383
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_first_symlink.lht
@@ -0,0 +1,19 @@
+# implicit type for list is text; chaning to any other type should not affect the implicit type of later nodes
+li:root {
+li:x {
+    sy:foo = bar;
+    bar1 = baz;
+    li:hah = {};
+    bar2 = baz;
+    ha:heh = {};
+    bar3 = baz;
+}
+ha:x {
+    sy:foo = bar;
+    bar1 = baz;
+    li:hah = {};
+    bar2 = baz;
+    ha:heh = {};
+    bar3 = baz;
+}
+}
diff --git a/src_3rd/liblihata/regression/list_missing_last_semicolon.lht b/src_3rd/liblihata/regression/list_missing_last_semicolon.lht
new file mode 100644
index 0000000..fe4784d
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_missing_last_semicolon.lht
@@ -0,0 +1 @@
+li:x {foo = bar}
diff --git a/src_3rd/liblihata/regression/list_nthname.lht b/src_3rd/liblihata/regression/list_nthname.lht
new file mode 100644
index 0000000..70ad7b3
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_nthname.lht
@@ -0,0 +1,12 @@
+li:{
+	li:empty {
+	}
+	li:non-empty {
+		a=val1
+		b=val2
+		a=val3
+		a=val4
+		c=val5
+		a=val6
+	}
+}
diff --git a/src_3rd/liblihata/regression/list_nthname.tts b/src_3rd/liblihata/regression/list_nthname.tts
new file mode 100644
index 0000000..b43c6c1
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_nthname.tts
@@ -0,0 +1,48 @@
+load_doc D2 list_nthname.lht
+
+echo root:
+root N1 D2
+print_tree N1
+
+
+echo second list:
+list_child N1
+list_next N1
+print_tree N1
+
+echo 0th a:
+equ_node N2 N1
+list_nthname N2 0 a
+print_tree N2
+
+echo 1st a:
+equ_node N2 N1
+list_nthname N2 1 a
+print_tree N2
+
+echo 2nd a:
+equ_node N2 N1
+list_nthname N2 2 a
+print_tree N2
+
+echo 3rd a:
+equ_node N2 N1
+list_nthname N2 3 a
+print_tree N2
+
+echo 4th a:
+equ_node N2 N1
+list_nthname N2 4 a
+print_tree N2
+
+echo 100th a:
+equ_node N2 N1
+list_nthname N2 100 a
+print_tree N2
+
+echo -1st a:
+equ_node N2 N1
+list_nthname N2 -1 a
+print_tree N2
+
+free_doc D2
diff --git a/src_3rd/liblihata/regression/list_replace_child.tts b/src_3rd/liblihata/regression/list_replace_child.tts
new file mode 100644
index 0000000..1944e4e
--- /dev/null
+++ b/src_3rd/liblihata/regression/list_replace_child.tts
@@ -0,0 +1,44 @@
+echo Trying to replace an existing node in the list:
+
+load_doc D1 list.lht
+
+root N1 D1
+list_child N1
+list_next N1
+
+equ_node N2 N1
+list_child N2
+
+new_text N3 for_replacing
+
+echo --- Before replacing
+print_tree N1
+
+list_replace_child N1 N2 N3
+
+echo --- After replacing
+print_tree N1
+
+
+free_doc D1
+
+echo Trying to replace a node that is not even on the list
+load_doc D1 list.lht
+
+root N1 D1
+list_child N1
+
+equ_node N2 N1
+list_child N2
+
+echo --- Before replacing
+print_tree N1
+
+new_text N3 for_replacing
+list_replace_child N1 N2 N3
+
+echo --- After replacing
+print_tree N1
+
+dom_node_free N3
+free_doc D1
diff --git a/src_3rd/liblihata/regression/merge_hash.lht b/src_3rd/liblihata/regression/merge_hash.lht
new file mode 100644
index 0000000..332d4ff
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_hash.lht
@@ -0,0 +1,28 @@
+ha:root {
+	ha:dst {
+		key1=val1
+		key2=val2
+		sy:lnk=/
+		li:recurse {r1; r2; r3}
+	}
+
+	ha:src1 {
+		key11=val11
+		key12=val12
+	}
+
+	ha:src2 {
+		key21=val21
+		sy:lnk=/
+	}
+
+	ha:src3 {
+		key31=val31
+		sy:lnk=foo
+	}
+
+	ha:src4 {
+		key41=val41
+		li:recurse {R40; R41}
+	}
+}
diff --git a/src_3rd/liblihata/regression/merge_hash_recurse.tts b/src_3rd/liblihata/regression/merge_hash_recurse.tts
new file mode 100644
index 0000000..89b750d
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_hash_recurse.tts
@@ -0,0 +1,18 @@
+load_doc D1 merge_hash.lht
+root N1 D1
+root N2 D1
+
+echo Original 1:
+print_tree N1
+
+path N1 /dst
+path N2 /src4
+
+tree_merge N1 N2
+
+echo After merg:
+
+root N1 D1
+print_tree N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/merge_hash_simple.tts b/src_3rd/liblihata/regression/merge_hash_simple.tts
new file mode 100644
index 0000000..adc6b0f
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_hash_simple.tts
@@ -0,0 +1,18 @@
+load_doc D1 merge_hash.lht
+root N1 D1
+root N2 D1
+
+echo Original 1:
+print_tree N1
+
+path N1 /dst
+path N2 /src1
+
+tree_merge N1 N2
+
+echo After merg:
+
+root N1 D1
+print_tree N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/merge_hash_sybad.tts b/src_3rd/liblihata/regression/merge_hash_sybad.tts
new file mode 100644
index 0000000..e334884
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_hash_sybad.tts
@@ -0,0 +1,18 @@
+load_doc D1 merge_hash.lht
+root N1 D1
+root N2 D1
+
+echo Original 1:
+print_tree N1
+
+path N1 /dst
+path N2 /src3
+
+tree_merge N1 N2
+
+echo After merg:
+
+root N1 D1
+print_tree N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/merge_hash_sygood.tts b/src_3rd/liblihata/regression/merge_hash_sygood.tts
new file mode 100644
index 0000000..b64e277
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_hash_sygood.tts
@@ -0,0 +1,17 @@
+load_doc D1 merge_hash.lht
+root N1 D1
+root N2 D1
+
+echo Original 1:
+print_tree N1
+
+path N1 /dst
+path N2 /src2
+
+tree_merge N1 N2
+
+echo After merg:
+
+root N1 D1
+print_tree N1
+free_doc D1
diff --git a/src_3rd/liblihata/regression/merge_list.tts b/src_3rd/liblihata/regression/merge_list.tts
new file mode 100644
index 0000000..5944b4d
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_list.tts
@@ -0,0 +1,30 @@
+load_doc D1 list.lht
+load_doc D2 list.lht
+root N1 D1
+root N2 D2
+
+echo Original 1:
+print_tree N1
+echo Original 2:
+print_tree N2
+
+list_child N1
+list_next N1
+list_child N2
+list_next N2
+
+
+tree_merge N1 N2
+echo After merging the two non-empty lists:
+
+
+root N1 D1
+root N2 D2
+
+echo Original 1:
+print_tree N1
+echo Original 2:
+print_tree N2
+
+free_doc D1
+free_doc D2
diff --git a/src_3rd/liblihata/regression/merge_table.tts b/src_3rd/liblihata/regression/merge_table.tts
new file mode 100644
index 0000000..28b6c5f
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_table.tts
@@ -0,0 +1,30 @@
+load_doc D1 table.lht
+load_doc D2 table.lht
+root N1 D1
+root N2 D2
+
+echo Original 1:
+print_tree N1
+echo Original 2:
+print_tree N2
+
+list_child N1
+list_next N1
+list_child N2
+list_next N2
+
+
+tree_merge N1 N2
+echo After merging the two non-empty tables:
+
+
+root N1 D1
+root N2 D2
+
+echo Original 1:
+print_tree N1
+echo Original 2:
+print_tree N2
+
+free_doc D1
+free_doc D2
diff --git a/src_3rd/liblihata/regression/merge_text.tts b/src_3rd/liblihata/regression/merge_text.tts
new file mode 100644
index 0000000..72bf647
--- /dev/null
+++ b/src_3rd/liblihata/regression/merge_text.tts
@@ -0,0 +1,17 @@
+load_doc D1 text.lht
+root N1 D1
+
+echo Original:
+print_tree N1
+
+list_child N1
+equ_node N2 N1
+list_next N2
+
+
+tree_merge N1 N2
+echo After merging key1,key2:
+root N1 D1
+print_tree N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/node_is_under.tts b/src_3rd/liblihata/regression/node_is_under.tts
new file mode 100644
index 0000000..4199d27
--- /dev/null
+++ b/src_3rd/liblihata/regression/node_is_under.tts
@@ -0,0 +1,12 @@
+load_doc D1 table_corners.lht
+
+root N1 D1
+root N2 D1
+path N2 /0
+
+node_is_under N2 N1
+node_is_under N1 N2
+node_is_under N1 N1
+node_is_under N2 N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/nodes_for_testing.lht b/src_3rd/liblihata/regression/nodes_for_testing.lht
new file mode 100644
index 0000000..ab298a8
--- /dev/null
+++ b/src_3rd/liblihata/regression/nodes_for_testing.lht
@@ -0,0 +1,13 @@
+ha:nodes_for_testing {
+    0 = zero
+    1 = one
+    2 = two
+    3 = three
+    4 = four
+    5 = five
+    6 = six
+    7 = seven
+    8 = eight
+    9 = nine
+    10 = ten
+}
diff --git a/src_3rd/liblihata/regression/path.tts b/src_3rd/liblihata/regression/path.tts
new file mode 100644
index 0000000..7006de1
--- /dev/null
+++ b/src_3rd/liblihata/regression/path.tts
@@ -0,0 +1,35 @@
+# look up not-the-first element of a list thenconstruct the reverse path
+# should get the same path
+
+load_doc D1 revpath.lht
+root N2 D1
+print_node N2
+root N3 D1
+path N3 /lst/0
+print_node N3
+path N3 /lst/1
+print_node N3
+path N3 /lst/2
+print_node N3
+path N3 /lst/3
+print_node N3
+
+#doesn't exist, error message (OK)
+path N3 /lst/4
+print_node N3
+
+echo ---
+
+load_doc D2 sy.lht
+root N0 D2
+print_node N0
+root N1 D2
+path N1 /0
+print_node N1
+
+#exists, but error message
+path N1 /1
+print_node N1
+
+free_doc D1
+free_doc D2
diff --git a/src_3rd/liblihata/regression/ref/dom_hash_get.tref b/src_3rd/liblihata/regression/ref/dom_hash_get.tref
new file mode 100644
index 0000000..f4832db
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_hash_get.tref
@@ -0,0 +1,19 @@
+The document:
+  ha:{nodes_for_testing} [root] entries: 11
+   te:{0} {zero}
+   te:{1} {one}
+   te:{2} {two}
+   te:{3} {three}
+   te:{4} {four}
+   te:{5} {five}
+   te:{6} {six}
+   te:{7} {seven}
+   te:{8} {eight}
+   te:{9} {nine}
+   te:{10} {ten}
+-
+Node with key "0":
+  te:{0} {zero}
+-
+Node with invalid key:
+  * node is NULL *
diff --git a/src_3rd/liblihata/regression/ref/dom_hash_put.tref b/src_3rd/liblihata/regression/ref/dom_hash_put.tref
new file mode 100644
index 0000000..4cade0f
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_hash_put.tref
@@ -0,0 +1,21 @@
+Doc before put:
+  ha:{} [root] entries: 2
+   ha:{empty} entries: 0
+   ha:{non-empty} entries: 2
+    te:{key1} {val1}
+    te:{key2} {val2}
+Node before put:
+  te:{} [root] [nodoc] {"to_be_put"}
+-
+Doc after put:
+  ha:{} [root] entries: 3
+   ha:{empty} entries: 0
+   ha:{non-empty} entries: 2
+    te:{key1} {val1}
+    te:{key2} {val2}
+   te:{} {"to_be_put"}
+Node after put:
+  te:{} {"to_be_put"}
+Node before put:
+  te:{} [root] [nodoc] {"to_be_put_again"}
+Error during dom_hash_put: duplicate key in hash
diff --git a/src_3rd/liblihata/regression/ref/dom_iterate.tref b/src_3rd/liblihata/regression/ref/dom_iterate.tref
new file mode 100644
index 0000000..cb2084f
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_iterate.tref
@@ -0,0 +1,70 @@
+list.lht:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+root:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+Children of root:
+  li:{empty} 
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+-
+ta.lht:
+  ha:{} [root] entries: 5
+   ta:{empty} cols: 0, rows: 0
+   ta:{table3} cols: 1, rows: 4
+    Row 0:{}
+     te:{} {row0}
+    Row 1:{}
+     te:{} {row1}
+    Row 2:{}
+     te:{} {row2}
+    Row 3:{}
+     te:{} {row3}
+   ta:{table4} cols: 4, rows: 4
+    Row 0:{}
+     te:{} {row0col0}
+     te:{} {row0col1}
+     te:{} {row0col2}
+     te:{} {row0col3}
+    Row 1:{}
+     te:{} {row1col0}
+     te:{} {row1col1}
+     te:{} {row1col2}
+     te:{} {row1col3}
+    Row 2:{}
+     te:{} {row2col0}
+     te:{} {row2col1}
+     te:{} {row2col2}
+     te:{} {row2col3}
+    Row 3:{}
+     te:{} {row3col0}
+     te:{} {row3col1}
+     te:{} {row3col2}
+     te:{} {row3col3}
+   ta:{table1} cols: 1, rows: 1
+    Row 0:{}
+     te:{} {row0}
+   ta:{table2} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {row0col0}
+     te:{} {row0col1}
+    Row 1:{}
+     te:{} {row1col0}
+     te:{} {row1col1}
+root:
+  ha:{} [root] entries: 5
+Children of root:
+  ta:{empty} cols: 0, rows: 0
+  ta:{table3} cols: 1, rows: 4
+  ta:{table4} cols: 4, rows: 4
+  ta:{table1} cols: 1, rows: 1
+  ta:{table2} cols: 2, rows: 2
+-
+tiny.lht:
+  te:{} [root] {}
+root:
+  te:{} [root] {}
+Children of root:
+  * node has no children *
diff --git a/src_3rd/liblihata/regression/ref/dom_list_append.tref b/src_3rd/liblihata/regression/ref/dom_list_append.tref
new file mode 100644
index 0000000..94495e7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_list_append.tref
@@ -0,0 +1,18 @@
+Doc before append:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+Node before append:
+  te:{} [root] [nodoc] {"to_be_appended"}
+-
+Doc after append:
+  li:{} [root] FIRST li:{empty} LAST te:{}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+   te:{} {"to_be_appended"}
+Node after append:
+  te:{} {"to_be_appended"}
diff --git a/src_3rd/liblihata/regression/ref/dom_list_insert.tref b/src_3rd/liblihata/regression/ref/dom_list_insert.tref
new file mode 100644
index 0000000..9a94905
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_list_insert.tref
@@ -0,0 +1,18 @@
+Doc before insert:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+Node before insert:
+  te:{} [root] [nodoc] {"to_be_inserted"}
+-
+Doc after insert:
+  li:{} [root] FIRST te:{} LAST li:{non-empty}
+   te:{} {"to_be_inserted"}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+Node after insert:
+  te:{} {"to_be_inserted"}
diff --git a/src_3rd/liblihata/regression/ref/dom_list_len.tref b/src_3rd/liblihata/regression/ref/dom_list_len.tref
new file mode 100644
index 0000000..b2f97df
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_list_len.tref
@@ -0,0 +1,2 @@
+Len of the list: 2
+Len of the list: 0
diff --git a/src_3rd/liblihata/regression/ref/dom_load.tref b/src_3rd/liblihata/regression/ref/dom_load.tref
new file mode 100644
index 0000000..2c0727c
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_load.tref
@@ -0,0 +1,12 @@
+dom_load: can't open '' for read
+print_doc: doc was NULL
+-
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {11}
+     te:{} {12}
+    Row 1:{}
+     te:{} {21}
+     te:{} {22}
diff --git a/src_3rd/liblihata/regression/ref/dom_node_free.tref b/src_3rd/liblihata/regression/ref/dom_node_free.tref
new file mode 100644
index 0000000..3d7c749
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/dom_node_free.tref
@@ -0,0 +1,2 @@
+Creating a new, detached text node:
+  te:{} [root] [nodoc] {"detached"}
diff --git a/src_3rd/liblihata/regression/ref/empty.dref b/src_3rd/liblihata/regression/ref/empty.dref
new file mode 100644
index 0000000..2b867af
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/empty.dref
@@ -0,0 +1 @@
+* empty document *
diff --git a/src_3rd/liblihata/regression/ref/empty.eref b/src_3rd/liblihata/regression/ref/empty.eref
new file mode 100644
index 0000000..6938324
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/empty.eref
@@ -0,0 +1 @@
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/empty_type_decl.dref b/src_3rd/liblihata/regression/ref/empty_type_decl.dref
new file mode 100644
index 0000000..df64842
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/empty_type_decl.dref
@@ -0,0 +1 @@
+* error: unexpected colon in id (may require protection) (<stdin>:1.12)*
diff --git a/src_3rd/liblihata/regression/ref/empty_type_decl.eref b/src_3rd/liblihata/regression/ref/empty_type_decl.eref
new file mode 100644
index 0000000..0a1f2c6
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/empty_type_decl.eref
@@ -0,0 +1,2 @@
+list 
+ *ERROR* 'unexpected colon in id (may require protection)' (at 1:12)
diff --git a/src_3rd/liblihata/regression/ref/err_brace_in_text.dref b/src_3rd/liblihata/regression/ref/err_brace_in_text.dref
new file mode 100644
index 0000000..5eecbcb
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_brace_in_text.dref
@@ -0,0 +1 @@
+* error: invalid/unprotected character in text content (<stdin>:5.11)*
diff --git a/src_3rd/liblihata/regression/ref/err_brace_in_text.eref b/src_3rd/liblihata/regression/ref/err_brace_in_text.eref
new file mode 100644
index 0000000..264dd82
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_brace_in_text.eref
@@ -0,0 +1,6 @@
+# ' this document is invalid: the first te:{} is an explicitly anonymous text node'
+# ' and the first } would be the contet - however, it's impossible to decide'
+# ' how many }s are part of the content. Instead, unprotected } in text node is'
+# ' error.'
+list 
+ *ERROR* 'invalid/unprotected character in text content' (at 5:11)
diff --git a/src_3rd/liblihata/regression/ref/err_eof_body.dref b/src_3rd/liblihata/regression/ref/err_eof_body.dref
new file mode 100644
index 0000000..77b4574
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_eof_body.dref
@@ -0,0 +1 @@
+* error: unexpected EOF in the middle of an open node (<stdin>:3.1)*
diff --git a/src_3rd/liblihata/regression/ref/err_eof_body.eref b/src_3rd/liblihata/regression/ref/err_eof_body.eref
new file mode 100644
index 0000000..7d2933d
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_eof_body.eref
@@ -0,0 +1,3 @@
+list root
+ text 'key' = 'value'
+ *ERROR* 'unexpected EOF in the middle of an open node' (at 3:1)
diff --git a/src_3rd/liblihata/regression/ref/err_eof_comment.dref b/src_3rd/liblihata/regression/ref/err_eof_comment.dref
new file mode 100644
index 0000000..9499cd8
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_eof_comment.dref
@@ -0,0 +1 @@
+* error: unexpected EOF in the middle of an open node (<stdin>:4.1)*
diff --git a/src_3rd/liblihata/regression/ref/err_eof_comment.eref b/src_3rd/liblihata/regression/ref/err_eof_comment.eref
new file mode 100644
index 0000000..2f954a2
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_eof_comment.eref
@@ -0,0 +1,4 @@
+list root
+ text 'key' = 'value'
+ # ' unterminated comment with list left open'
+ *ERROR* 'unexpected EOF in the middle of an open node' (at 4:1)
diff --git a/src_3rd/liblihata/regression/ref/err_eof_comment2.dref b/src_3rd/liblihata/regression/ref/err_eof_comment2.dref
new file mode 100644
index 0000000..16dc3ab
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_eof_comment2.dref
@@ -0,0 +1,2 @@
+li:{root} [root] FIRST te:{key} LAST te:{key}
+ te:{key} {value}
diff --git a/src_3rd/liblihata/regression/ref/err_eof_comment2.eref b/src_3rd/liblihata/regression/ref/err_eof_comment2.eref
new file mode 100644
index 0000000..a1a064a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_eof_comment2.eref
@@ -0,0 +1,3 @@
+list root
+ text 'key' = 'value'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/err_nobody.dref b/src_3rd/liblihata/regression/ref/err_nobody.dref
new file mode 100644
index 0000000..e476331
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_nobody.dref
@@ -0,0 +1 @@
+* error: li, ha or ta with no body (<stdin>:3.1)*
diff --git a/src_3rd/liblihata/regression/ref/err_nobody.eref b/src_3rd/liblihata/regression/ref/err_nobody.eref
new file mode 100644
index 0000000..ce7b72a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/err_nobody.eref
@@ -0,0 +1,2 @@
+list 
+ *ERROR* 'li, ha or ta with no body' (at 3:1)
diff --git a/src_3rd/liblihata/regression/ref/excess_closing_braces.dref b/src_3rd/liblihata/regression/ref/excess_closing_braces.dref
new file mode 100644
index 0000000..710b6fa
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/excess_closing_braces.dref
@@ -0,0 +1,2 @@
+li:{} [root] FIRST te:{} LAST te:{}
+ te:{} {}
diff --git a/src_3rd/liblihata/regression/ref/excess_closing_braces.eref b/src_3rd/liblihata/regression/ref/excess_closing_braces.eref
new file mode 100644
index 0000000..a39083f
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/excess_closing_braces.eref
@@ -0,0 +1,4 @@
+# ' this document is valid: characters are ignored after closing the root node.'
+list 
+ text '' = ''
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/export.tref b/src_3rd/liblihata/regression/ref/export.tref
new file mode 100644
index 0000000..7f1b6b3
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/export.tref
@@ -0,0 +1,68 @@
+-----merge_hash.lht-----
+  ha:root {
+   ha:src1 {
+    key11 = val11
+    key12 = val12
+   }
+   ha:src2 {
+    sy:lnk = /
+    key21 = val21
+   }
+   ha:dst {
+    sy:lnk = /
+    key1 = val1
+    key2 = val2
+    li:recurse {
+     r1
+     r2
+     r3
+    }
+   }
+   ha:src3 {
+    key31 = val31
+    sy:lnk = foo
+   }
+   ha:src4 {
+    li:recurse {
+     R40
+     R41
+    }
+    key41 = val41
+   }
+  }
+-----text.lht-----
+  li:root {
+   key1 = val1
+   key2 = val2
+   key3 = val3
+   key4 = val4
+   key5 = val5
+   key6 = val6
+   anon1
+   anon2
+   anon3
+   empty = {}
+   empty2 = {}
+   {}
+   {}
+  }
+-----table.lht-----
+  li: {
+   ta:empty {
+   }
+   ta:non-empty {
+    { 11; 12 }
+    { 21; 22 }
+   }
+  }
+-----table_corners.lht-----
+  li: {
+   ta:empty {
+   }
+   ta:non-empty {
+    rowname0 { 00; 01 }
+    rowname1 { 10; 11 }
+    rowname2 { 20; li:21 { a; b; c; d; } }
+    rowname3 { 30; ha:31 { c = 3; d = 4; a = 1; b = 2; } }
+   }
+  }
diff --git a/src_3rd/liblihata/regression/ref/hash.dref b/src_3rd/liblihata/regression/ref/hash.dref
new file mode 100644
index 0000000..e7ce44b
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/hash.dref
@@ -0,0 +1,5 @@
+ha:{} [root] entries: 2
+ ha:{empty} entries: 0
+ ha:{non-empty} entries: 2
+  te:{key1} {val1}
+  te:{key2} {val2}
diff --git a/src_3rd/liblihata/regression/ref/hash.eref b/src_3rd/liblihata/regression/ref/hash.eref
new file mode 100644
index 0000000..5554513
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/hash.eref
@@ -0,0 +1,6 @@
+hash 
+ hash empty
+ hash non-empty
+  text 'key1' = 'val1'
+  text 'key2' = 'val2'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list.dref b/src_3rd/liblihata/regression/ref/list.dref
new file mode 100644
index 0000000..46384b6
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list.dref
@@ -0,0 +1,5 @@
+li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+ li:{empty} 
+ li:{non-empty} FIRST te:{key1} LAST te:{key2}
+  te:{key1} {val1}
+  te:{key2} {val2}
diff --git a/src_3rd/liblihata/regression/ref/list.eref b/src_3rd/liblihata/regression/ref/list.eref
new file mode 100644
index 0000000..3bbdf5a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list.eref
@@ -0,0 +1,6 @@
+list 
+ list empty
+ list non-empty
+  text 'key1' = 'val1'
+  text 'key2' = 'val2'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list.tref b/src_3rd/liblihata/regression/ref/list.tref
new file mode 100644
index 0000000..f05d7fd
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list.tref
@@ -0,0 +1,20 @@
+root:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+first list:
+  li:{empty} 
+second list:
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+   te:{key1} {val1}
+   te:{key2} {val2}
+nth 0:
+  te:{key1} {val1}
+nth 1:
+  te:{key2} {val2}
+nth 2:
+  * node is NULL *
+nth -1:
+  * node is NULL *
diff --git a/src_3rd/liblihata/regression/ref/list_del_nth.dref b/src_3rd/liblihata/regression/ref/list_del_nth.dref
new file mode 100644
index 0000000..58effe4
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_del_nth.dref
@@ -0,0 +1,6 @@
+li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+ li:{empty} 
+ li:{non-empty} FIRST te:{a} LAST te:{c}
+  te:{a} {val1}
+  te:{b} {val2}
+  te:{c} {val3}
diff --git a/src_3rd/liblihata/regression/ref/list_del_nth.eref b/src_3rd/liblihata/regression/ref/list_del_nth.eref
new file mode 100644
index 0000000..0651bbf
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_del_nth.eref
@@ -0,0 +1,7 @@
+list 
+ list empty
+ list non-empty
+  text 'a' = 'val1'
+  text 'b' = 'val2'
+  text 'c' = 'val3'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list_del_nth.tref b/src_3rd/liblihata/regression/ref/list_del_nth.tref
new file mode 100644
index 0000000..d408042
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_del_nth.tref
@@ -0,0 +1,55 @@
+root:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{a} LAST te:{c}
+    te:{a} {val1}
+    te:{b} {val2}
+    te:{c} {val3}
+second list:
+  li:{non-empty} FIRST te:{a} LAST te:{c}
+   te:{a} {val1}
+   te:{b} {val2}
+   te:{c} {val3}
+delete 0th item from first list:
+list_del_nth: boundary check error: out of bounds
+  li:{empty} 
+delete 1st item from first list:
+list_del_nth: boundary check error: out of bounds
+  li:{empty} 
+delete -1st item from first list:
+list_del_nth: boundary check error: out of bounds
+  li:{empty} 
+delete 0th item from second list, repeated 4x:
+  li:{non-empty} FIRST te:{a} LAST te:{c}
+   te:{a} {val1}
+   te:{b} {val2}
+   te:{c} {val3}
+  li:{non-empty} FIRST te:{b} LAST te:{c}
+   te:{b} {val2}
+   te:{c} {val3}
+  li:{non-empty} FIRST te:{c} LAST te:{c}
+   te:{c} {val3}
+  li:{non-empty} 
+list_del_nth: boundary check error: out of bounds
+  li:{non-empty} 
+delete last item from second list, repeated 4x:
+  li:{non-empty} FIRST te:{a} LAST te:{c}
+   te:{a} {val1}
+   te:{b} {val2}
+   te:{c} {val3}
+  li:{non-empty} FIRST te:{a} LAST te:{b}
+   te:{a} {val1}
+   te:{b} {val2}
+  li:{non-empty} FIRST te:{a} LAST te:{a}
+   te:{a} {val1}
+  li:{non-empty} 
+list_del_nth: boundary check error: out of bounds
+  li:{non-empty} 
+delete middle item from second list:
+  li:{non-empty} FIRST te:{a} LAST te:{c}
+   te:{a} {val1}
+   te:{b} {val2}
+   te:{c} {val3}
+  li:{non-empty} FIRST te:{a} LAST te:{c}
+   te:{a} {val1}
+   te:{c} {val3}
diff --git a/src_3rd/liblihata/regression/ref/list_detach_child.tref b/src_3rd/liblihata/regression/ref/list_detach_child.tref
new file mode 100644
index 0000000..fdd990e
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_detach_child.tref
@@ -0,0 +1,22 @@
+Trying to detach an existing node in the list:
+--- Before detaching
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+   te:{key1} {val1}
+   te:{key2} {val2}
+list_detach_child: done
+--- After detaching
+  li:{non-empty} FIRST te:{key2} LAST te:{key2}
+   te:{key2} {val2}
+Detaching the last node from the list:
+--- Before detaching
+  li:{non-empty} FIRST te:{key2} LAST te:{key2}
+   te:{key2} {val2}
+list_detach_child: done
+--- After detaching
+  li:{non-empty} 
+Trying to detach from an empty list:
+--- Before detaching
+  li:{non-empty} 
+Error during list_detach_child: boundary check error: out of bounds
+--- After detaching
+  li:{non-empty} 
diff --git a/src_3rd/liblihata/regression/ref/list_detach_nth.tref b/src_3rd/liblihata/regression/ref/list_detach_nth.tref
new file mode 100644
index 0000000..3df6fc4
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_detach_nth.tref
@@ -0,0 +1,44 @@
+The root tree before detaching:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+The root node before detaching:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+The node should be detached before action:
+  li:{empty} 
+-----
+list_detach_nth: done
+-----
+The root tree after detaching:
+  li:{} [root] FIRST li:{non-empty} LAST li:{non-empty}
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+The root node after detaching:
+  li:{} [root] FIRST li:{non-empty} LAST li:{non-empty}
+The detached node:
+  li:{empty} [root] [detached] 
+=====
+The root tree before detaching:
+  li:{} [root] FIRST li:{non-empty} LAST li:{non-empty}
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+The root node before detaching:
+  li:{} [root] FIRST li:{non-empty} LAST li:{non-empty}
+The node should be detached before action:
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+-----
+list_detach_nth: done
+-----
+The root tree after detaching:
+  li:{} [root] 
+The root node after detaching:
+  li:{} [root] 
+The detached node:
+  li:{non-empty} [root] [detached] FIRST te:{key1} LAST te:{key2}
+=====
+Trying to detach from empty tree:
+list_detach_nth: NULL was returned
diff --git a/src_3rd/liblihata/regression/ref/list_empty.dref b/src_3rd/liblihata/regression/ref/list_empty.dref
new file mode 100644
index 0000000..f425fae
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_empty.dref
@@ -0,0 +1 @@
+li:{empty} [root] 
diff --git a/src_3rd/liblihata/regression/ref/list_empty.eref b/src_3rd/liblihata/regression/ref/list_empty.eref
new file mode 100644
index 0000000..47f3b91
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_empty.eref
@@ -0,0 +1,2 @@
+list empty
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list_first_symlink.dref b/src_3rd/liblihata/regression/ref/list_first_symlink.dref
new file mode 100644
index 0000000..da428b2
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_first_symlink.dref
@@ -0,0 +1,15 @@
+li:{root} [root] FIRST li:{x} LAST ha:{x}
+ li:{x} FIRST sy:{foo} LAST te:{bar3}
+  sy:{foo} {bar}
+  te:{bar1} {baz}
+  li:{hah} 
+  te:{bar2} {baz}
+  ha:{heh} entries: 0
+  te:{bar3} {baz}
+ ha:{x} entries: 6
+  te:{bar2} {baz}
+  te:{bar3} {baz}
+  li:{hah} 
+  sy:{foo} {bar}
+  ha:{heh} entries: 0
+  te:{bar1} {baz}
diff --git a/src_3rd/liblihata/regression/ref/list_first_symlink.eref b/src_3rd/liblihata/regression/ref/list_first_symlink.eref
new file mode 100644
index 0000000..f10cd5e
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_first_symlink.eref
@@ -0,0 +1,17 @@
+# ' implicit type for list is text; chaning to any other type should not affect the implicit type of later nodes'
+list root
+ list x
+  symlink 'foo' = 'bar'
+  text 'bar1' = 'baz'
+  list hah
+  text 'bar2' = 'baz'
+  hash heh
+  text 'bar3' = 'baz'
+ hash x
+  symlink 'foo' = 'bar'
+  text 'bar1' = 'baz'
+  list hah
+  text 'bar2' = 'baz'
+  hash heh
+  text 'bar3' = 'baz'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list_missing_last_semicolon.dref b/src_3rd/liblihata/regression/ref/list_missing_last_semicolon.dref
new file mode 100644
index 0000000..3e53755
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_missing_last_semicolon.dref
@@ -0,0 +1,2 @@
+li:{x} [root] FIRST te:{foo} LAST te:{foo}
+ te:{foo} {bar}
diff --git a/src_3rd/liblihata/regression/ref/list_missing_last_semicolon.eref b/src_3rd/liblihata/regression/ref/list_missing_last_semicolon.eref
new file mode 100644
index 0000000..c0e8896
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_missing_last_semicolon.eref
@@ -0,0 +1,3 @@
+list x
+ text 'foo' = 'bar'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list_nthname.dref b/src_3rd/liblihata/regression/ref/list_nthname.dref
new file mode 100644
index 0000000..412b614
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_nthname.dref
@@ -0,0 +1,9 @@
+li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+ li:{empty} 
+ li:{non-empty} FIRST te:{a} LAST te:{a}
+  te:{a} {val1}
+  te:{b} {val2}
+  te:{a} {val3}
+  te:{a} {val4}
+  te:{c} {val5}
+  te:{a} {val6}
diff --git a/src_3rd/liblihata/regression/ref/list_nthname.eref b/src_3rd/liblihata/regression/ref/list_nthname.eref
new file mode 100644
index 0000000..129ff0a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_nthname.eref
@@ -0,0 +1,10 @@
+list 
+ list empty
+ list non-empty
+  text 'a' = 'val1'
+  text 'b' = 'val2'
+  text 'a' = 'val3'
+  text 'a' = 'val4'
+  text 'c' = 'val5'
+  text 'a' = 'val6'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/list_nthname.tref b/src_3rd/liblihata/regression/ref/list_nthname.tref
new file mode 100644
index 0000000..49b1394
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_nthname.tref
@@ -0,0 +1,32 @@
+root:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{a} LAST te:{a}
+    te:{a} {val1}
+    te:{b} {val2}
+    te:{a} {val3}
+    te:{a} {val4}
+    te:{c} {val5}
+    te:{a} {val6}
+second list:
+  li:{non-empty} FIRST te:{a} LAST te:{a}
+   te:{a} {val1}
+   te:{b} {val2}
+   te:{a} {val3}
+   te:{a} {val4}
+   te:{c} {val5}
+   te:{a} {val6}
+0th a:
+  te:{a} {val1}
+1st a:
+  te:{a} {val3}
+2nd a:
+  te:{a} {val4}
+3rd a:
+  te:{a} {val6}
+4th a:
+  * node is NULL *
+100th a:
+  * node is NULL *
+-1st a:
+  * node is NULL *
diff --git a/src_3rd/liblihata/regression/ref/list_replace_child.tref b/src_3rd/liblihata/regression/ref/list_replace_child.tref
new file mode 100644
index 0000000..b82b93b
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/list_replace_child.tref
@@ -0,0 +1,16 @@
+Trying to replace an existing node in the list:
+--- Before replacing
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+   te:{key1} {val1}
+   te:{key2} {val2}
+list_replace_child: done
+--- After replacing
+  li:{non-empty} FIRST te:{} LAST te:{key2}
+   te:{} {for_replacing}
+   te:{key2} {val2}
+Trying to replace a node that is not even on the list
+--- Before replacing
+  li:{empty} 
+Error during list_replace_child: boundary check error: out of bounds
+--- After replacing
+  li:{empty} 
diff --git a/src_3rd/liblihata/regression/ref/merge_hash.dref b/src_3rd/liblihata/regression/ref/merge_hash.dref
new file mode 100644
index 0000000..5efbd6f
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_hash.dref
@@ -0,0 +1,23 @@
+ha:{root} [root] entries: 5
+ ha:{src1} entries: 2
+  te:{key11} {val11}
+  te:{key12} {val12}
+ ha:{src2} entries: 2
+  sy:{lnk} {/}
+  te:{key21} {val21}
+ ha:{dst} entries: 4
+  sy:{lnk} {/}
+  te:{key1} {val1}
+  te:{key2} {val2}
+  li:{recurse} FIRST te:{} LAST te:{}
+   te:{} {r1}
+   te:{} {r2}
+   te:{} {r3}
+ ha:{src3} entries: 2
+  te:{key31} {val31}
+  sy:{lnk} {foo}
+ ha:{src4} entries: 2
+  li:{recurse} FIRST te:{} LAST te:{}
+   te:{} {R40}
+   te:{} {R41}
+  te:{key41} {val41}
diff --git a/src_3rd/liblihata/regression/ref/merge_hash.eref b/src_3rd/liblihata/regression/ref/merge_hash.eref
new file mode 100644
index 0000000..3c63669
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_hash.eref
@@ -0,0 +1,24 @@
+hash root
+ hash dst
+  text 'key1' = 'val1'
+  text 'key2' = 'val2'
+  symlink 'lnk' = '/'
+  list recurse
+   text '' = 'r1'
+   text '' = 'r2'
+   text '' = 'r3'
+ hash src1
+  text 'key11' = 'val11'
+  text 'key12' = 'val12'
+ hash src2
+  text 'key21' = 'val21'
+  symlink 'lnk' = '/'
+ hash src3
+  text 'key31' = 'val31'
+  symlink 'lnk' = 'foo'
+ hash src4
+  text 'key41' = 'val41'
+  list recurse
+   text '' = 'R40'
+   text '' = 'R41'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/merge_hash_recurse.tref b/src_3rd/liblihata/regression/ref/merge_hash_recurse.tref
new file mode 100644
index 0000000..a8ce6f3
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_hash_recurse.tref
@@ -0,0 +1,46 @@
+Original 1:
+  ha:{root} [root] entries: 5
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 4
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
+After merg:
+  ha:{root} [root] entries: 4
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 5
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
diff --git a/src_3rd/liblihata/regression/ref/merge_hash_simple.tref b/src_3rd/liblihata/regression/ref/merge_hash_simple.tref
new file mode 100644
index 0000000..971d320
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_hash_simple.tref
@@ -0,0 +1,47 @@
+Original 1:
+  ha:{root} [root] entries: 5
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 4
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
+After merg:
+  ha:{root} [root] entries: 4
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 6
+    te:{key11} {val11}
+    te:{key12} {val12}
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
diff --git a/src_3rd/liblihata/regression/ref/merge_hash_sybad.tref b/src_3rd/liblihata/regression/ref/merge_hash_sybad.tref
new file mode 100644
index 0000000..11acd3a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_hash_sybad.tref
@@ -0,0 +1,49 @@
+Original 1:
+  ha:{root} [root] entries: 5
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 4
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
+merge error: merge failed: symlink mismatch
+After merg:
+  ha:{root} [root] entries: 5
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 4
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
diff --git a/src_3rd/liblihata/regression/ref/merge_hash_sygood.tref b/src_3rd/liblihata/regression/ref/merge_hash_sygood.tref
new file mode 100644
index 0000000..1b68eb9
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_hash_sygood.tref
@@ -0,0 +1,46 @@
+Original 1:
+  ha:{root} [root] entries: 5
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 4
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
+After merg:
+  ha:{root} [root] entries: 4
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{dst} entries: 5
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+    te:{key21} {val21}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
diff --git a/src_3rd/liblihata/regression/ref/merge_list.tref b/src_3rd/liblihata/regression/ref/merge_list.tref
new file mode 100644
index 0000000..22f31ba
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_list.tref
@@ -0,0 +1,24 @@
+Original 1:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+Original 2:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+After merging the two non-empty lists:
+Original 1:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+Original 2:
+  li:{} [root] FIRST li:{empty} LAST li:{empty}
+   li:{empty} 
diff --git a/src_3rd/liblihata/regression/ref/merge_table.tref b/src_3rd/liblihata/regression/ref/merge_table.tref
new file mode 100644
index 0000000..e2c8ca1
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_table.tref
@@ -0,0 +1,40 @@
+Original 1:
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {11}
+     te:{} {12}
+    Row 1:{}
+     te:{} {21}
+     te:{} {22}
+Original 2:
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {11}
+     te:{} {12}
+    Row 1:{}
+     te:{} {21}
+     te:{} {22}
+After merging the two non-empty tables:
+Original 1:
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 4
+    Row 0:{}
+     te:{} {11}
+     te:{} {12}
+    Row 1:{}
+     te:{} {21}
+     te:{} {22}
+    Row 2:{}
+     te:{} {11}
+     te:{} {12}
+    Row 3:{}
+     te:{} {21}
+     te:{} {22}
+Original 2:
+  li:{} [root] FIRST ta:{empty} LAST ta:{empty}
+   ta:{empty} cols: 0, rows: 0
diff --git a/src_3rd/liblihata/regression/ref/merge_text.tref b/src_3rd/liblihata/regression/ref/merge_text.tref
new file mode 100644
index 0000000..a6dc3d6
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/merge_text.tref
@@ -0,0 +1,29 @@
+Original:
+  li:{root} [root] FIRST te:{key1} LAST te:{}
+   te:{key1} {val1}
+   te:{key2} {val2}
+   te:{key3} {val3}
+   te:{key4} {val4}
+   te:{key5} {val5}
+   te:{key6} {val6}
+   te:{} {anon1}
+   te:{} {anon2}
+   te:{} {anon3}
+   te:{empty} {}
+   te:{empty2} {}
+   te:{} {}
+   te:{} {}
+After merging key1,key2:
+  li:{root} [root] FIRST te:{key1} LAST te:{}
+   te:{key1} {val1val2}
+   te:{key3} {val3}
+   te:{key4} {val4}
+   te:{key5} {val5}
+   te:{key6} {val6}
+   te:{} {anon1}
+   te:{} {anon2}
+   te:{} {anon3}
+   te:{empty} {}
+   te:{empty2} {}
+   te:{} {}
+   te:{} {}
diff --git a/src_3rd/liblihata/regression/ref/node_is_under.tref b/src_3rd/liblihata/regression/ref/node_is_under.tref
new file mode 100644
index 0000000..dbd9159
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/node_is_under.tref
@@ -0,0 +1,4 @@
+Node 'empty' is under node ''
+Node '' is NOT under node 'empty'
+Node '' is NOT under node ''
+Node 'empty' is NOT under node 'empty'
diff --git a/src_3rd/liblihata/regression/ref/nodes_for_testing.dref b/src_3rd/liblihata/regression/ref/nodes_for_testing.dref
new file mode 100644
index 0000000..a4a4258
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/nodes_for_testing.dref
@@ -0,0 +1,12 @@
+ha:{nodes_for_testing} [root] entries: 11
+ te:{0} {zero}
+ te:{1} {one}
+ te:{2} {two}
+ te:{3} {three}
+ te:{4} {four}
+ te:{5} {five}
+ te:{6} {six}
+ te:{7} {seven}
+ te:{8} {eight}
+ te:{9} {nine}
+ te:{10} {ten}
diff --git a/src_3rd/liblihata/regression/ref/nodes_for_testing.eref b/src_3rd/liblihata/regression/ref/nodes_for_testing.eref
new file mode 100644
index 0000000..577027a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/nodes_for_testing.eref
@@ -0,0 +1,13 @@
+hash nodes_for_testing
+ text '0' = 'zero'
+ text '1' = 'one'
+ text '2' = 'two'
+ text '3' = 'three'
+ text '4' = 'four'
+ text '5' = 'five'
+ text '6' = 'six'
+ text '7' = 'seven'
+ text '8' = 'eight'
+ text '9' = 'nine'
+ text '10' = 'ten'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/path.tref b/src_3rd/liblihata/regression/ref/path.tref
new file mode 100644
index 0000000..757126e
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/path.tref
@@ -0,0 +1,11 @@
+  ha:{root} [root] entries: 2
+  te:{key} {0}
+  te:{key} {1}
+  te:{key} {2}
+  te:{key} {3}
+Path error: path not found
+  * node is NULL *
+---
+  li:{rt} [root] FIRST te:{text} LAST sy:{symlink}
+  te:{text} {textnode}
+  li:{rt} [root] FIRST te:{text} LAST sy:{symlink}
diff --git a/src_3rd/liblihata/regression/ref/quoting.dref b/src_3rd/liblihata/regression/ref/quoting.dref
new file mode 100644
index 0000000..8e3fdd7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/quoting.dref
@@ -0,0 +1,18 @@
+li:{root} [root] FIRST te:{te:brace} LAST ta:{table spc}
+ te:{te:brace} {foo}
+ te:{TE:text node} {this has TE: in name}
+ te:{text node} {hello world}
+ sy:{sym link} {hello world}
+ li:{list node} FIRST te:{} LAST te:{}
+  te:{} {hello}
+  te:{} {world}
+ ha:{hash node} entries: 2
+  te:{1} {hello}
+  te:{2} {world}
+ ta:{table spc} cols: 2, rows: 2
+  Row 0:{}
+   te:{} {a}
+   te:{} {b}
+  Row 1:{}
+   te:{} {1}
+   te:{} {2}
diff --git a/src_3rd/liblihata/regression/ref/quoting.eref b/src_3rd/liblihata/regression/ref/quoting.eref
new file mode 100644
index 0000000..7f2dd25
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/quoting.eref
@@ -0,0 +1,19 @@
+list root
+ text 'te:brace' = 'foo'
+ text 'TE:text node' = 'this has TE: in name'
+ text 'text node' = 'hello world'
+ symlink 'sym link' = 'hello world'
+ list list node
+  text '' = 'hello'
+  text '' = 'world'
+ hash hash node
+  text '1' = 'hello'
+  text '2' = 'world'
+ table table spc
+  list 
+   text '' = 'a'
+   text '' = 'b'
+  list 
+   text '' = '1'
+   text '' = '2'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/revpath.dref b/src_3rd/liblihata/regression/ref/revpath.dref
new file mode 100644
index 0000000..b7e0410
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/revpath.dref
@@ -0,0 +1,13 @@
+ha:{root} [root] entries: 2
+ li:{lst} FIRST te:{key} LAST te:{key}
+  te:{key} {0}
+  te:{key} {1}
+  te:{key} {2}
+  te:{key} {3}
+ ta:{tbl} cols: 2, rows: 2
+  Row 0:{}
+   te:{} {00}
+   te:{} {01}
+  Row 1:{}
+   te:{} {10}
+   te:{} {11}
diff --git a/src_3rd/liblihata/regression/ref/revpath.eref b/src_3rd/liblihata/regression/ref/revpath.eref
new file mode 100644
index 0000000..a89c491
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/revpath.eref
@@ -0,0 +1,14 @@
+hash root
+ list lst
+  text 'key' = '0'
+  text 'key' = '1'
+  text 'key' = '2'
+  text 'key' = '3'
+ table tbl
+  list 
+   text '' = '00'
+   text '' = '01'
+  list 
+   text '' = '10'
+   text '' = '11'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/revpath.tref b/src_3rd/liblihata/regression/ref/revpath.tref
new file mode 100644
index 0000000..b044d16
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/revpath.tref
@@ -0,0 +1,22 @@
+--- list ---
+  te:{key} {3}
+lht_tree_list_find_node: 3
+lht_tree_path_build: /lst/3
+  te:{key} {2}
+lht_tree_list_find_node: 2
+lht_tree_path_build: /lst/2
+  te:{key} {1}
+lht_tree_list_find_node: 1
+lht_tree_path_build: /lst/1
+  te:{key} {0}
+lht_tree_list_find_node: 0
+lht_tree_path_build: /lst/0
+--- table ---
+  te:{} {00}
+lht_tree_path_build: /tbl/0/0
+  te:{} {01}
+lht_tree_path_build: /tbl/0/1
+  te:{} {10}
+lht_tree_path_build: /tbl/1/0
+  te:{} {11}
+lht_tree_path_build: /tbl/1/1
diff --git a/src_3rd/liblihata/regression/ref/stream.dref b/src_3rd/liblihata/regression/ref/stream.dref
new file mode 100644
index 0000000..b097656
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/stream.dref
@@ -0,0 +1,7 @@
+li:{x} [root] FIRST sy:{foo} LAST te:{bar3}
+ sy:{foo} {bar}
+ te:{bar1} {baz}
+ li:{hah} 
+ te:{bar2} {baz}
+ ha:{heh} entries: 0
+ te:{bar3} {baz}
diff --git a/src_3rd/liblihata/regression/ref/stream.eref b/src_3rd/liblihata/regression/ref/stream.eref
new file mode 100644
index 0000000..eb2440a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/stream.eref
@@ -0,0 +1,9 @@
+# ' multiple roots; implicit eof is generated after the first and the second is ignored'
+list x
+ symlink 'foo' = 'bar'
+ text 'bar1' = 'baz'
+ list hah
+ text 'bar2' = 'baz'
+ hash heh
+ text 'bar3' = 'baz'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/sy.dref b/src_3rd/liblihata/regression/ref/sy.dref
new file mode 100644
index 0000000..85d5b23
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/sy.dref
@@ -0,0 +1,3 @@
+li:{rt} [root] FIRST te:{text} LAST sy:{symlink}
+ te:{text} {textnode}
+ sy:{symlink} {/}
diff --git a/src_3rd/liblihata/regression/ref/sy.eref b/src_3rd/liblihata/regression/ref/sy.eref
new file mode 100644
index 0000000..cd1fbd2
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/sy.eref
@@ -0,0 +1,4 @@
+list rt
+ text 'text' = 'textnode'
+ symlink 'symlink' = '/'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/symlink.dref b/src_3rd/liblihata/regression/ref/symlink.dref
new file mode 100644
index 0000000..8d6d44f
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/symlink.dref
@@ -0,0 +1,16 @@
+li:{root} [root] FIRST sy:{key1} LAST sy:{symlink_broken}
+ sy:{key1} {val1}
+ sy:{key2} {val2}
+ sy:{key3} {val3}
+ sy:{key4} {val4}
+ sy:{key5} {val5}
+ sy:{key6} {val6}
+ sy:{} {anon1}
+ sy:{} {anon2}
+ sy:{} {anon3}
+ sy:{empty} {}
+ sy:{empty2} {}
+ sy:{} {}
+ sy:{} {}
+ sy:{symlink_ok} {/}
+ sy:{symlink_broken} {/3478783}
diff --git a/src_3rd/liblihata/regression/ref/symlink.eref b/src_3rd/liblihata/regression/ref/symlink.eref
new file mode 100644
index 0000000..c9f6347
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/symlink.eref
@@ -0,0 +1,17 @@
+list root
+ symlink 'key1' = 'val1'
+ symlink 'key2' = 'val2'
+ symlink 'key3' = 'val3'
+ symlink 'key4' = 'val4'
+ symlink 'key5' = 'val5'
+ symlink 'key6' = 'val6'
+ symlink '' = 'anon1'
+ symlink '' = 'anon2'
+ symlink '' = 'anon3'
+ symlink 'empty' = ''
+ symlink 'empty2' = ''
+ symlink '' = ''
+ symlink '' = ''
+ symlink 'symlink_ok' = '/'
+ symlink 'symlink_broken' = '/3478783'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/symlink_is_broken.tref b/src_3rd/liblihata/regression/ref/symlink_is_broken.tref
new file mode 100644
index 0000000..5d74018
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/symlink_is_broken.tref
@@ -0,0 +1,2 @@
+Symlink is OK.
+Symlink is broken.
diff --git a/src_3rd/liblihata/regression/ref/symlink_root_node.dref b/src_3rd/liblihata/regression/ref/symlink_root_node.dref
new file mode 100644
index 0000000..8120f9a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/symlink_root_node.dref
@@ -0,0 +1 @@
+sy:{foo} [root] {bar}
diff --git a/src_3rd/liblihata/regression/ref/symlink_root_node.eref b/src_3rd/liblihata/regression/ref/symlink_root_node.eref
new file mode 100644
index 0000000..5a9c799
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/symlink_root_node.eref
@@ -0,0 +1,3 @@
+# ' on parse level this is valid; some tree walk would result in 'bar: not found''
+symlink 'foo' = 'bar'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/ta.dref b/src_3rd/liblihata/regression/ref/ta.dref
new file mode 100644
index 0000000..8b82d25
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta.dref
@@ -0,0 +1,42 @@
+ha:{} [root] entries: 5
+ ta:{empty} cols: 0, rows: 0
+ ta:{table3} cols: 1, rows: 4
+  Row 0:{}
+   te:{} {row0}
+  Row 1:{}
+   te:{} {row1}
+  Row 2:{}
+   te:{} {row2}
+  Row 3:{}
+   te:{} {row3}
+ ta:{table4} cols: 4, rows: 4
+  Row 0:{}
+   te:{} {row0col0}
+   te:{} {row0col1}
+   te:{} {row0col2}
+   te:{} {row0col3}
+  Row 1:{}
+   te:{} {row1col0}
+   te:{} {row1col1}
+   te:{} {row1col2}
+   te:{} {row1col3}
+  Row 2:{}
+   te:{} {row2col0}
+   te:{} {row2col1}
+   te:{} {row2col2}
+   te:{} {row2col3}
+  Row 3:{}
+   te:{} {row3col0}
+   te:{} {row3col1}
+   te:{} {row3col2}
+   te:{} {row3col3}
+ ta:{table1} cols: 1, rows: 1
+  Row 0:{}
+   te:{} {row0}
+ ta:{table2} cols: 2, rows: 2
+  Row 0:{}
+   te:{} {row0col0}
+   te:{} {row0col1}
+  Row 1:{}
+   te:{} {row1col0}
+   te:{} {row1col1}
diff --git a/src_3rd/liblihata/regression/ref/ta.eref b/src_3rd/liblihata/regression/ref/ta.eref
new file mode 100644
index 0000000..9afb988
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta.eref
@@ -0,0 +1,43 @@
+hash 
+ table empty
+ table table1
+  list 
+   text '' = 'row0'
+ table table2
+  list 
+   text '' = 'row0col0'
+   text '' = 'row0col1'
+  list 
+   text '' = 'row1col0'
+   text '' = 'row1col1'
+ table table3
+  list 
+   text '' = 'row0'
+  list 
+   text '' = 'row1'
+  list 
+   text '' = 'row2'
+  list 
+   text '' = 'row3'
+ table table4
+  list 
+   text '' = 'row0col0'
+   text '' = 'row0col1'
+   text '' = 'row0col2'
+   text '' = 'row0col3'
+  list 
+   text '' = 'row1col0'
+   text '' = 'row1col1'
+   text '' = 'row1col2'
+   text '' = 'row1col3'
+  list 
+   text '' = 'row2col0'
+   text '' = 'row2col1'
+   text '' = 'row2col2'
+   text '' = 'row2col3'
+  list 
+   text '' = 'row3col0'
+   text '' = 'row3col1'
+   text '' = 'row3col2'
+   text '' = 'row3col3'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/ta_del_c.tref b/src_3rd/liblihata/regression/ref/ta_del_c.tref
new file mode 100644
index 0000000..68e06a5
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta_del_c.tref
@@ -0,0 +1,169 @@
+===== Original, untouched doc at the beginning
+  ha:{} [root] entries: 5
+   ta:{empty} cols: 0, rows: 0
+   ta:{table3} cols: 1, rows: 4
+    Row 0:{}
+     te:{} {row0}
+    Row 1:{}
+     te:{} {row1}
+    Row 2:{}
+     te:{} {row2}
+    Row 3:{}
+     te:{} {row3}
+   ta:{table4} cols: 4, rows: 4
+    Row 0:{}
+     te:{} {row0col0}
+     te:{} {row0col1}
+     te:{} {row0col2}
+     te:{} {row0col3}
+    Row 1:{}
+     te:{} {row1col0}
+     te:{} {row1col1}
+     te:{} {row1col2}
+     te:{} {row1col3}
+    Row 2:{}
+     te:{} {row2col0}
+     te:{} {row2col1}
+     te:{} {row2col2}
+     te:{} {row2col3}
+    Row 3:{}
+     te:{} {row3col0}
+     te:{} {row3col1}
+     te:{} {row3col2}
+     te:{} {row3col3}
+   ta:{table1} cols: 1, rows: 1
+    Row 0:{}
+     te:{} {row0}
+   ta:{table2} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {row0col0}
+     te:{} {row0col1}
+    Row 1:{}
+     te:{} {row1col0}
+     te:{} {row1col1}
+===== Original table1
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+===== table_del_col N1 -1 (invalid)
+table_del_col: boundary check error: out of bounds
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+===== table_del_col N1 1 (invalid)
+table_del_col: boundary check error: out of bounds
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+===== table_del_col N1 0 (valid)
+  ta:{table1} cols: 0, rows: 1
+   Row 0:{}
+===== table_del_col N1 0 (invalid)
+table_del_col: boundary check error: out of bounds
+  ta:{table1} cols: 0, rows: 1
+   Row 0:{}
+===== Original empty
+  ta:{empty} cols: 0, rows: 0
+===== table_del_col N2 0 (invalid)
+table_del_col: boundary check error: out of bounds
+  ta:{empty} cols: 0, rows: 0
+===== table2 before dels:
+  ta:{table2} cols: 2, rows: 2
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+===== After valid dels in table2:
+  ta:{table2} cols: 0, rows: 2
+   Row 0:{}
+   Row 1:{}
+===== table3 before del:
+  ta:{table3} cols: 1, rows: 4
+   Row 0:{}
+    te:{} {row0}
+   Row 1:{}
+    te:{} {row1}
+   Row 2:{}
+    te:{} {row2}
+   Row 3:{}
+    te:{} {row3}
+===== After valid del in table3:
+  ta:{table3} cols: 0, rows: 4
+   Row 0:{}
+   Row 1:{}
+   Row 2:{}
+   Row 3:{}
+===== table4 before dels:
+  ta:{table4} cols: 4, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+===== table_del_col N5 -1 (invalid)
+table_del_col: boundary check error: out of bounds
+===== table_del_col N5 4 (invalid)
+table_del_col: boundary check error: out of bounds
+===== table4 after invalid dels:
+  ta:{table4} cols: 4, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+===== table4 after valid dels:
+  ta:{table4} cols: 0, rows: 4
+   Row 0:{}
+   Row 1:{}
+   Row 2:{}
+   Row 3:{}
+===== The document after deleting all cols:
+  ha:{} [root] entries: 5
+   ta:{empty} cols: 0, rows: 0
+   ta:{table3} cols: 0, rows: 4
+    Row 0:{}
+    Row 1:{}
+    Row 2:{}
+    Row 3:{}
+   ta:{table4} cols: 0, rows: 4
+    Row 0:{}
+    Row 1:{}
+    Row 2:{}
+    Row 3:{}
+   ta:{table1} cols: 0, rows: 1
+    Row 0:{}
+   ta:{table2} cols: 0, rows: 2
+    Row 0:{}
+    Row 1:{}
diff --git a/src_3rd/liblihata/regression/ref/ta_del_r.tref b/src_3rd/liblihata/regression/ref/ta_del_r.tref
new file mode 100644
index 0000000..0d9e9a7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta_del_r.tref
@@ -0,0 +1,146 @@
+===== Original, untouched doc at the beginning
+  ha:{} [root] entries: 5
+   ta:{empty} cols: 0, rows: 0
+   ta:{table3} cols: 1, rows: 4
+    Row 0:{}
+     te:{} {row0}
+    Row 1:{}
+     te:{} {row1}
+    Row 2:{}
+     te:{} {row2}
+    Row 3:{}
+     te:{} {row3}
+   ta:{table4} cols: 4, rows: 4
+    Row 0:{}
+     te:{} {row0col0}
+     te:{} {row0col1}
+     te:{} {row0col2}
+     te:{} {row0col3}
+    Row 1:{}
+     te:{} {row1col0}
+     te:{} {row1col1}
+     te:{} {row1col2}
+     te:{} {row1col3}
+    Row 2:{}
+     te:{} {row2col0}
+     te:{} {row2col1}
+     te:{} {row2col2}
+     te:{} {row2col3}
+    Row 3:{}
+     te:{} {row3col0}
+     te:{} {row3col1}
+     te:{} {row3col2}
+     te:{} {row3col3}
+   ta:{table1} cols: 1, rows: 1
+    Row 0:{}
+     te:{} {row0}
+   ta:{table2} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {row0col0}
+     te:{} {row0col1}
+    Row 1:{}
+     te:{} {row1col0}
+     te:{} {row1col1}
+===== Original table1
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+===== table_del_row N1 -1 (invalid)
+table_del_row: boundary check error: out of bounds
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+===== table_del_row N1 1 (invalid)
+table_del_row: boundary check error: out of bounds
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+===== table_del_row N1 0 (valid)
+  ta:{table1} cols: 1, rows: 0
+===== table_del_row N1 0 (invalid)
+table_del_row: boundary check error: out of bounds
+  ta:{table1} cols: 1, rows: 0
+===== Original empty
+  ta:{empty} cols: 0, rows: 0
+===== table_del_row N2 0 (invalid)
+table_del_row: boundary check error: out of bounds
+  ta:{empty} cols: 0, rows: 0
+===== table2 before dels:
+  ta:{table2} cols: 2, rows: 2
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+===== After valid dels in table2:
+  ta:{table2} cols: 2, rows: 0
+===== table3 before dels:
+  ta:{table3} cols: 1, rows: 4
+   Row 0:{}
+    te:{} {row0}
+   Row 1:{}
+    te:{} {row1}
+   Row 2:{}
+    te:{} {row2}
+   Row 3:{}
+    te:{} {row3}
+===== After valid dels in table3:
+  ta:{table2} cols: 2, rows: 0
+===== table4 before dels:
+  ta:{table4} cols: 4, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+===== table_del_row N5 -1 (invalid)
+table_del_row: boundary check error: out of bounds
+===== table_del_row N5 4 (invalid)
+table_del_row: boundary check error: out of bounds
+===== table4 after invalid dels:
+  ta:{table4} cols: 4, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+===== table4 after valid dels:
+  ta:{table4} cols: 4, rows: 0
+===== The document after deleting all rows:
+  ha:{} [root] entries: 5
+   ta:{empty} cols: 0, rows: 0
+   ta:{table3} cols: 1, rows: 0
+   ta:{table4} cols: 4, rows: 0
+   ta:{table1} cols: 1, rows: 0
+   ta:{table2} cols: 2, rows: 0
diff --git a/src_3rd/liblihata/regression/ref/ta_ins_c.tref b/src_3rd/liblihata/regression/ref/ta_ins_c.tref
new file mode 100644
index 0000000..aeca27d
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta_ins_c.tref
@@ -0,0 +1,53 @@
+-----test 1-----
+insert one column to empty table
+**before**
+  ta:{empty} cols: 0, rows: 0
+**after**
+  ta:{empty} cols: 1, rows: 0
+-----test 2-----
+add one row
+**before**
+  ta:{empty} cols: 1, rows: 0
+**after**
+  ta:{empty} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {(null)}
+-----test 3-----
+insert column (parameter 0)
+**before**
+  ta:{empty} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {AAA}
+**after**
+  ta:{empty} cols: 2, rows: 1
+   Row 0:{}
+    te:{} {BBB}
+    te:{} {AAA}
+-----test 4-----
+insert column (parameter 2, this is the last row)
+**before**
+  ta:{empty} cols: 2, rows: 1
+   Row 0:{}
+    te:{} {BBB}
+    te:{} {AAA}
+**after**
+  ta:{empty} cols: 3, rows: 1
+   Row 0:{}
+    te:{} {BBB}
+    te:{} {AAA}
+    te:{} {CCC}
+-----test 5-----
+insert column (parameter 1)
+**before**
+  ta:{empty} cols: 3, rows: 1
+   Row 0:{}
+    te:{} {BBB}
+    te:{} {AAA}
+    te:{} {CCC}
+**after**
+  ta:{empty} cols: 4, rows: 1
+   Row 0:{}
+    te:{} {BBB}
+    te:{} {DDD}
+    te:{} {AAA}
+    te:{} {CCC}
diff --git a/src_3rd/liblihata/regression/ref/ta_ins_c2.tref b/src_3rd/liblihata/regression/ref/ta_ins_c2.tref
new file mode 100644
index 0000000..d26639a
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta_ins_c2.tref
@@ -0,0 +1,174 @@
+-----test 1-----
+insert column (parameter 1)
+**before**
+  ta:{table4} cols: 4, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+**after**
+  ta:{table4} cols: 5, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {(null)}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {(null)}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {(null)}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {(null)}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+-----test 2-----
+insert column (parameter 0)
+**before**
+  ta:{table4} cols: 5, rows: 4
+   Row 0:{}
+    te:{} {row0col0}
+    te:{} {(null)}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {row1col0}
+    te:{} {(null)}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {row2col0}
+    te:{} {(null)}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {row3col0}
+    te:{} {(null)}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+**after**
+  ta:{table4} cols: 6, rows: 4
+   Row 0:{}
+    te:{} {(null)}
+    te:{} {row0col0}
+    te:{} {(null)}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {(null)}
+    te:{} {row1col0}
+    te:{} {(null)}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {(null)}
+    te:{} {row2col0}
+    te:{} {(null)}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {(null)}
+    te:{} {row3col0}
+    te:{} {(null)}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+-----test 3-----
+insert column (parameter 6, last row)
+**before**
+  ta:{table4} cols: 6, rows: 4
+   Row 0:{}
+    te:{} {(null)}
+    te:{} {row0col0}
+    te:{} {(null)}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+   Row 1:{}
+    te:{} {(null)}
+    te:{} {row1col0}
+    te:{} {(null)}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+   Row 2:{}
+    te:{} {(null)}
+    te:{} {row2col0}
+    te:{} {(null)}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+   Row 3:{}
+    te:{} {(null)}
+    te:{} {row3col0}
+    te:{} {(null)}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+**after**
+  ta:{table4} cols: 7, rows: 4
+   Row 0:{}
+    te:{} {(null)}
+    te:{} {row0col0}
+    te:{} {(null)}
+    te:{} {row0col1}
+    te:{} {row0col2}
+    te:{} {row0col3}
+    te:{} {(null)}
+   Row 1:{}
+    te:{} {(null)}
+    te:{} {row1col0}
+    te:{} {(null)}
+    te:{} {row1col1}
+    te:{} {row1col2}
+    te:{} {row1col3}
+    te:{} {(null)}
+   Row 2:{}
+    te:{} {(null)}
+    te:{} {row2col0}
+    te:{} {(null)}
+    te:{} {row2col1}
+    te:{} {row2col2}
+    te:{} {row2col3}
+    te:{} {(null)}
+   Row 3:{}
+    te:{} {(null)}
+    te:{} {row3col0}
+    te:{} {(null)}
+    te:{} {row3col1}
+    te:{} {row3col2}
+    te:{} {row3col3}
+    te:{} {(null)}
diff --git a/src_3rd/liblihata/regression/ref/ta_ins_r.tref b/src_3rd/liblihata/regression/ref/ta_ins_r.tref
new file mode 100644
index 0000000..3e85ff1
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta_ins_r.tref
@@ -0,0 +1,16 @@
+-----test 1----
+insert one row to empty table
+  ta:{empty} cols: 0, rows: 0
+  te:{} [root] [nodoc] {text1}
+  ta:{empty} cols: 1, rows: 1
+  ta:{empty} cols: 1, rows: 1
+  te:{} {(null)}
+after replace
+  te:{} {text1}
+  ta:{empty} cols: 1, rows: 1
+-----test 2-----
+the length of table is 0 so we could not insert element to 1st position
+  ta:{empty} cols: 0, rows: 0
+table_ins_row: boundary check error: out of bounds
+  ta:{empty} cols: 0, rows: 0
+tts terminated
diff --git a/src_3rd/liblihata/regression/ref/ta_ins_r2.tref b/src_3rd/liblihata/regression/ref/ta_ins_r2.tref
new file mode 100644
index 0000000..15a707d
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/ta_ins_r2.tref
@@ -0,0 +1,102 @@
+-----test 1-----
+insert one row to table (parameter 0)
+  ta:{table1} cols: 1, rows: 1
+   Row 0:{}
+    te:{} {row0}
+  ta:{table1} cols: 1, rows: 2
+   Row 0:{}
+    te:{} {(null)}
+   Row 1:{}
+    te:{} {row0}
+  ta:{table1} cols: 1, rows: 2
+   Row 0:{}
+    te:{} {insertedcell00}
+   Row 1:{}
+    te:{} {row0}
+-----test 2-----
+insert one row to table (parameter 1)
+**before**
+  ta:{table1} cols: 1, rows: 2
+   Row 0:{}
+    te:{} {insertedcell00}
+   Row 1:{}
+    te:{} {row0}
+**after**
+  ta:{table1} cols: 1, rows: 3
+   Row 0:{}
+    te:{} {insertedcell00}
+   Row 1:{}
+    te:{} {insertedcell10}
+   Row 2:{}
+    te:{} {row0}
+-----test 3-----
+insert one row to table (parameter "row count")
+**before**
+  ta:{table1} cols: 1, rows: 3
+   Row 0:{}
+    te:{} {insertedcell00}
+   Row 1:{}
+    te:{} {insertedcell10}
+   Row 2:{}
+    te:{} {row0}
+**after**
+  ta:{table1} cols: 1, rows: 4
+   Row 0:{}
+    te:{} {insertedcell00}
+   Row 1:{}
+    te:{} {insertedcell10}
+   Row 2:{}
+    te:{} {row0}
+   Row 3:{}
+    te:{} {insertedcell30}
+-----test 4-----
+insert one row to table (parameter 0)
+**before**
+  ta:{table1} cols: 1, rows: 4
+   Row 0:{}
+    te:{} {insertedcell00}
+   Row 1:{}
+    te:{} {insertedcell10}
+   Row 2:{}
+    te:{} {row0}
+   Row 3:{}
+    te:{} {insertedcell30}
+**after**
+  ta:{table1} cols: 1, rows: 5
+   Row 0:{}
+    te:{} {shiftedrows}
+   Row 1:{}
+    te:{} {insertedcell00}
+   Row 2:{}
+    te:{} {insertedcell10}
+   Row 3:{}
+    te:{} {row0}
+   Row 4:{}
+    te:{} {insertedcell30}
+-----test 5-----
+insert one row to table (parameter 2)  (to test it can shift more than one row)
+  ta:{table1} cols: 1, rows: 5
+   Row 0:{}
+    te:{} {shiftedrows}
+   Row 1:{}
+    te:{} {insertedcell00}
+   Row 2:{}
+    te:{} {insertedcell10}
+   Row 3:{}
+    te:{} {row0}
+   Row 4:{}
+    te:{} {insertedcell30}
+  ta:{table1} cols: 1, rows: 6
+   Row 0:{}
+    te:{} {shiftedrows}
+   Row 1:{}
+    te:{} {insertedcell00}
+   Row 2:{}
+    te:{} {(null)}
+   Row 3:{}
+    te:{} {insertedcell10}
+   Row 4:{}
+    te:{} {row0}
+   Row 5:{}
+    te:{} {insertedcell30}
+tts terminated
diff --git a/src_3rd/liblihata/regression/ref/table.dref b/src_3rd/liblihata/regression/ref/table.dref
new file mode 100644
index 0000000..e18d7d5
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table.dref
@@ -0,0 +1,9 @@
+li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+ ta:{empty} cols: 0, rows: 0
+ ta:{non-empty} cols: 2, rows: 2
+  Row 0:{}
+   te:{} {11}
+   te:{} {12}
+  Row 1:{}
+   te:{} {21}
+   te:{} {22}
diff --git a/src_3rd/liblihata/regression/ref/table.eref b/src_3rd/liblihata/regression/ref/table.eref
new file mode 100644
index 0000000..b38a4a6
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table.eref
@@ -0,0 +1,10 @@
+list 
+ table empty
+ table non-empty
+  list 
+   text '' = '11'
+   text '' = '12'
+  list 
+   text '' = '21'
+   text '' = '22'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/table_corners.dref b/src_3rd/liblihata/regression/ref/table_corners.dref
new file mode 100644
index 0000000..f309ebb
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_corners.dref
@@ -0,0 +1,23 @@
+li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+ ta:{empty} cols: 0, rows: 0
+ ta:{non-empty} cols: 2, rows: 4
+  Row 0:{rowname0}
+   te:{} {00}
+   te:{} {01}
+  Row 1:{rowname1}
+   te:{} {10}
+   te:{} {11}
+  Row 2:{rowname2}
+   te:{} {20}
+   li:{21} FIRST te:{} LAST te:{}
+    te:{} {a}
+    te:{} {b}
+    te:{} {c}
+    te:{} {d}
+  Row 3:{rowname3}
+   te:{} {30}
+   ha:{31} entries: 4
+    te:{c} {3}
+    te:{d} {4}
+    te:{a} {1}
+    te:{b} {2}
diff --git a/src_3rd/liblihata/regression/ref/table_corners.eref b/src_3rd/liblihata/regression/ref/table_corners.eref
new file mode 100644
index 0000000..7e42ab4
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_corners.eref
@@ -0,0 +1,24 @@
+list 
+ table empty
+ table non-empty
+  list rowname0
+   text '' = '00'
+   text '' = '01'
+  list rowname1
+   text '' = '10'
+   text '' = '11'
+  list rowname2
+   text '' = '20'
+   list 21
+    text '' = 'a'
+    text '' = 'b'
+    text '' = 'c'
+    text '' = 'd'
+  list rowname3
+   text '' = '30'
+   hash 31
+    text 'a' = '1'
+    text 'b' = '2'
+    text 'c' = '3'
+    text 'd' = '4'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/table_detach_cell.tref b/src_3rd/liblihata/regression/ref/table_detach_cell.tref
new file mode 100644
index 0000000..5dfe20e
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_detach_cell.tref
@@ -0,0 +1,304 @@
+Untouched, original tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {00}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+-
+table_detach_cell: 0;0 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  te:{} [root] [detached] {00}
+-
+table_detach_cell: 0;1 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  te:{} [root] [detached] {01}
+-
+table_detach_cell: 1;0 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  te:{} [root] [detached] {10}
+-
+table_detach_cell: 1;1 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  te:{} [root] [detached] {11}
+-
+table_detach_cell: 2;0 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  te:{} [root] [detached] {20}
+-
+table_detach_cell: 2;1 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  li:{21} [root] [detached] FIRST te:{} LAST te:{}
+   te:{} [detached] {a}
+   te:{} [detached] {b}
+   te:{} [detached] {c}
+   te:{} [detached] {d}
+-
+table_detach_cell: 3;0 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {(null)}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+node:
+  te:{} [root] [detached] {30}
+-
+table_detach_cell: 3;1 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {(null)}
+    te:{} {(null)}
+node:
+  ha:{31} [root] [detached] entries: 4
+   te:{c} [detached] {3}
+   te:{d} [detached] {4}
+   te:{a} [detached] {1}
+   te:{b} [detached] {2}
+-
+Trying to detach a cell again:
+table_detach_cell: 3;1 success
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {(null)}
+    te:{} {(null)}
+node:
+  te:{} [root] [detached] {(null)}
+-
+table_detach_cell: error: boundary check error: out of bounds
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {(null)}
+    te:{} {(null)}
+node:
+  * node is NULL *
+-
+table_detach_cell: error: boundary check error: out of bounds
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {(null)}
+    te:{} {(null)}
+node:
+  * node is NULL *
+-
+table_detach_cell: error: boundary check error: out of bounds
+tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 1:{rowname1}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 2:{rowname2}
+    te:{} {(null)}
+    te:{} {(null)}
+   Row 3:{rowname3}
+    te:{} {(null)}
+    te:{} {(null)}
+node:
+  * node is NULL *
+-
+Empty table, before trying to detach:
+  ta:{empty} cols: 0, rows: 0
+-
+table_detach_cell: error: boundary check error: out of bounds
+tree:
+  ta:{empty} cols: 0, rows: 0
+node:
+  * node is NULL *
diff --git a/src_3rd/liblihata/regression/ref/table_detach_child.tref b/src_3rd/liblihata/regression/ref/table_detach_child.tref
new file mode 100644
index 0000000..6d35235
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_detach_child.tref
@@ -0,0 +1,58 @@
+Before detaching:
+The doc:
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 4
+    Row 0:{rowname0}
+     te:{} {00}
+     te:{} {01}
+    Row 1:{rowname1}
+     te:{} {10}
+     te:{} {11}
+    Row 2:{rowname2}
+     te:{} {20}
+     li:{21} FIRST te:{} LAST te:{}
+      te:{} {a}
+      te:{} {b}
+      te:{} {c}
+      te:{} {d}
+    Row 3:{rowname3}
+     te:{} {30}
+     ha:{31} entries: 4
+      te:{c} {3}
+      te:{d} {4}
+      te:{a} {1}
+      te:{b} {2}
+The node:
+  te:{} {00}
+-
+After detaching:
+The doc:
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 4
+    Row 0:{rowname0}
+     te:{} {(null)}
+     te:{} {01}
+    Row 1:{rowname1}
+     te:{} {10}
+     te:{} {11}
+    Row 2:{rowname2}
+     te:{} {20}
+     li:{21} FIRST te:{} LAST te:{}
+      te:{} {a}
+      te:{} {b}
+      te:{} {c}
+      te:{} {d}
+    Row 3:{rowname3}
+     te:{} {30}
+     ha:{31} entries: 4
+      te:{c} {3}
+      te:{d} {4}
+      te:{a} {1}
+      te:{b} {2}
+The node:
+  te:{} [unlinked] {00}
+-
+Trying to detach again:
+table_detach_child error: node not found
diff --git a/src_3rd/liblihata/regression/ref/table_find_cell.tref b/src_3rd/liblihata/regression/ref/table_find_cell.tref
new file mode 100644
index 0000000..099fef4
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_find_cell.tref
@@ -0,0 +1,13 @@
+  te:{} {00}
+Cell was found in row 0, col 0.
+Cell wasn't found in table.
+Cell wasn't found in table.
+  ha:{31} entries: 4
+Cell was found in row 3, col 1.
+Cell wasn't found in table.
+Cell wasn't found in table.
+  te:{} {10}
+Cell was found in row 1, col 0.
+Cell wasn't found in table.
+Cell wasn't found in table.
+Cell wasn't found in table.
diff --git a/src_3rd/liblihata/regression/ref/table_named_cell.dref b/src_3rd/liblihata/regression/ref/table_named_cell.dref
new file mode 100644
index 0000000..95f1665
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_named_cell.dref
@@ -0,0 +1,26 @@
+li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+ ta:{empty} cols: 0, rows: 0
+ ta:{non-empty} cols: 2, rows: 5
+  Row 0:{row0}
+   te:{col0} {00}
+   te:{col1} {01 }
+  Row 1:{row1}
+   te:{col0} {10}
+   te:{col1} {11 }
+  Row 2:{row2}
+   te:{col0} {20}
+   li:{col1} FIRST te:{cell0} LAST te:{cell3}
+    te:{cell0} {a}
+    te:{cell1} {b}
+    te:{cell2} {c}
+    te:{cell3} {d }
+  Row 3:{row3}
+   te:{col0} {30}
+   ha:{col1} entries: 4
+    te:{c} {3}
+    te:{d} {4 }
+    te:{a} {1}
+    te:{b} {2}
+  Row 4:{row0}
+   te:{col0} {0a}
+   te:{col0} {0b }
diff --git a/src_3rd/liblihata/regression/ref/table_named_cell.eref b/src_3rd/liblihata/regression/ref/table_named_cell.eref
new file mode 100644
index 0000000..2e896b6
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_named_cell.eref
@@ -0,0 +1,27 @@
+list 
+ table empty
+ table non-empty
+  list row0
+   text 'col0' = '00'
+   text 'col1' = '01 '
+  list row1
+   text 'col0' = '10'
+   text 'col1' = '11 '
+  list row2
+   text 'col0' = '20'
+   list col1
+    text 'cell0' = 'a'
+    text 'cell1' = 'b'
+    text 'cell2' = 'c'
+    text 'cell3' = 'd '
+  list row3
+   text 'col0' = '30'
+   hash col1
+    text 'a' = '1'
+    text 'b' = '2'
+    text 'c' = '3'
+    text 'd' = '4 '
+  list row0
+   text 'col0' = '0a'
+   text 'col0' = '0b '
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/table_named_cell.tref b/src_3rd/liblihata/regression/ref/table_named_cell.tref
new file mode 100644
index 0000000..b9ad061
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_named_cell.tref
@@ -0,0 +1,31 @@
+The doc:
+  ta:{non-empty} cols: 2, rows: 5
+   Row 0:{row0}
+    te:{col0} {00}
+    te:{col1} {01 }
+   Row 1:{row1}
+    te:{col0} {10}
+    te:{col1} {11 }
+   Row 2:{row2}
+    te:{col0} {20}
+    li:{col1} FIRST te:{cell0} LAST te:{cell3}
+     te:{cell0} {a}
+     te:{cell1} {b}
+     te:{cell2} {c}
+     te:{cell3} {d }
+   Row 3:{row3}
+    te:{col0} {30}
+    ha:{col1} entries: 4
+     te:{c} {3}
+     te:{d} {4 }
+     te:{a} {1}
+     te:{b} {2}
+   Row 4:{row0}
+    te:{col0} {0a}
+    te:{col0} {0b }
+Node row0-0-col0-0:
+  te:{col0} {00}
+Node row0-1-col0-1:
+  te:{col0} {0b }
+Node row0-2-col0-2:
+  * node is NULL *
diff --git a/src_3rd/liblihata/regression/ref/table_read.tref b/src_3rd/liblihata/regression/ref/table_read.tref
new file mode 100644
index 0000000..3ed2d45
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_read.tref
@@ -0,0 +1,36 @@
+root:
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 2
+    Row 0:{}
+     te:{} {11}
+     te:{} {12}
+    Row 1:{}
+     te:{} {21}
+     te:{} {22}
+first table:
+  ta:{empty} cols: 0, rows: 0
+Reading from empty table:
+1) item (0,0)
+  * node is NULL *
+2) item (1,1)
+  * node is NULL *
+3) item (-1, -1)
+  * node is NULL *
+second table:
+  ta:{non-empty} cols: 2, rows: 2
+Reading from filled table:
+1) item (0,0)
+  te:{} {11}
+2) item (0,1)
+  te:{} {12}
+3) item (1,0)
+  te:{} {21}
+4) item (1,1)
+  te:{} {22}
+5) item (-1, -1)
+  * node is NULL *
+6) item (1,2)
+  * node is NULL *
+7) item (2,1)
+  * node is NULL *
diff --git a/src_3rd/liblihata/regression/ref/table_replace_cell.tref b/src_3rd/liblihata/regression/ref/table_replace_cell.tref
new file mode 100644
index 0000000..e6119f7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_replace_cell.tref
@@ -0,0 +1,103 @@
+Untouched, original tree:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {00}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+---
+Replacing a valid cell (cell 0, 0)
+table_replace_cell: 0/0 success
+res:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {for_replacing}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+old:
+  te:{} [unlinked] {00}
+---
+Trying to replace out of boundaries (cell 2, 2)
+table_replace_cell: error: boundary check error: out of bounds
+res:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {for_replacing}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+old:
+  * node is NULL *
+---
+Trying to replace an already replaced cell (cell 0, 0)
+table_replace_cell: 0/0 success
+res:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {can_replace}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+old:
+  te:{} [unlinked] {for_replacing}
diff --git a/src_3rd/liblihata/regression/ref/table_replace_child.tref b/src_3rd/liblihata/regression/ref/table_replace_child.tref
new file mode 100644
index 0000000..f394aef
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/table_replace_child.tref
@@ -0,0 +1,79 @@
+--- Original table:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {00}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+table_replace_child: done
+--- After replacing (0, 0) cell:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {for_replacing}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    li:{21} FIRST te:{} LAST te:{}
+     te:{} {a}
+     te:{} {b}
+     te:{} {c}
+     te:{} {d}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+table_replace_child: done
+--- After replacing (2, 1) cell:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {for_replacing}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    te:{} {for_replacing}
+   Row 3:{rowname3}
+    te:{} {30}
+    ha:{31} entries: 4
+     te:{c} {3}
+     te:{d} {4}
+     te:{a} {1}
+     te:{b} {2}
+table_replace_child: done
+---After replacing (3, 1) cell:
+  ta:{non-empty} cols: 2, rows: 4
+   Row 0:{rowname0}
+    te:{} {for_replacing}
+    te:{} {01}
+   Row 1:{rowname1}
+    te:{} {10}
+    te:{} {11}
+   Row 2:{rowname2}
+    te:{} {20}
+    te:{} {for_replacing}
+   Row 3:{rowname3}
+    te:{} {30}
+    te:{} {for_replacing}
diff --git a/src_3rd/liblihata/regression/ref/text.dref b/src_3rd/liblihata/regression/ref/text.dref
new file mode 100644
index 0000000..dc7dd6b
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/text.dref
@@ -0,0 +1,14 @@
+li:{root} [root] FIRST te:{key1} LAST te:{}
+ te:{key1} {val1}
+ te:{key2} {val2}
+ te:{key3} {val3}
+ te:{key4} {val4}
+ te:{key5} {val5}
+ te:{key6} {val6}
+ te:{} {anon1}
+ te:{} {anon2}
+ te:{} {anon3}
+ te:{empty} {}
+ te:{empty2} {}
+ te:{} {}
+ te:{} {}
diff --git a/src_3rd/liblihata/regression/ref/text.eref b/src_3rd/liblihata/regression/ref/text.eref
new file mode 100644
index 0000000..7e9b3e4
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/text.eref
@@ -0,0 +1,15 @@
+list root
+ text 'key1' = 'val1'
+ text 'key2' = 'val2'
+ text 'key3' = 'val3'
+ text 'key4' = 'val4'
+ text 'key5' = 'val5'
+ text 'key6' = 'val6'
+ text '' = 'anon1'
+ text '' = 'anon2'
+ text '' = 'anon3'
+ text 'empty' = ''
+ text 'empty2' = ''
+ text '' = ''
+ text '' = ''
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/text_root_node.dref b/src_3rd/liblihata/regression/ref/text_root_node.dref
new file mode 100644
index 0000000..8bc3766
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/text_root_node.dref
@@ -0,0 +1 @@
+te:{foo} [root] {bar}
diff --git a/src_3rd/liblihata/regression/ref/text_root_node.eref b/src_3rd/liblihata/regression/ref/text_root_node.eref
new file mode 100644
index 0000000..6f6d5b7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/text_root_node.eref
@@ -0,0 +1,2 @@
+text 'foo' = 'bar'
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/tiny.dref b/src_3rd/liblihata/regression/ref/tiny.dref
new file mode 100644
index 0000000..a3c50f9
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tiny.dref
@@ -0,0 +1 @@
+te:{} [root] {}
diff --git a/src_3rd/liblihata/regression/ref/tiny.eref b/src_3rd/liblihata/regression/ref/tiny.eref
new file mode 100644
index 0000000..089c3f7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tiny.eref
@@ -0,0 +1,2 @@
+text '' = ''
+*EOF*
diff --git a/src_3rd/liblihata/regression/ref/tree_detach.tref b/src_3rd/liblihata/regression/ref/tree_detach.tref
new file mode 100644
index 0000000..b72812b
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_detach.tref
@@ -0,0 +1,32 @@
+The tree before detaching:
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+   te:{key1} {val1}
+   te:{key2} {val2}
+The node before detaching:
+  te:{key1} {val1}
+tree_detach: done
+---
+The tree after detaching:
+  li:{non-empty} FIRST te:{key2} LAST te:{key2}
+   te:{key2} {val2}
+The node after detaching:
+  te:{key1} [root] [detached] {val1}
+========
+The tree before detaching:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+The node before detaching:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+tree_detach: done
+---
+The tree after detaching:
+  li:{} [root] [detached] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} [detached] 
+   li:{non-empty} [detached] FIRST te:{key1} LAST te:{key2}
+    te:{key1} [detached] {val1}
+    te:{key2} [detached] {val2}
+The node after detaching:
+  li:{} [root] [detached] FIRST li:{empty} LAST li:{non-empty}
diff --git a/src_3rd/liblihata/regression/ref/tree_dup_hash.tref b/src_3rd/liblihata/regression/ref/tree_dup_hash.tref
new file mode 100644
index 0000000..6d1036c
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_dup_hash.tref
@@ -0,0 +1,48 @@
+---orig
+  ha:{root} [root] entries: 5
+   ha:{src1} entries: 2
+    te:{key11} {val11}
+    te:{key12} {val12}
+   ha:{src2} entries: 2
+    sy:{lnk} {/}
+    te:{key21} {val21}
+   ha:{dst} entries: 4
+    sy:{lnk} {/}
+    te:{key1} {val1}
+    te:{key2} {val2}
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {r1}
+     te:{} {r2}
+     te:{} {r3}
+   ha:{src3} entries: 2
+    te:{key31} {val31}
+    sy:{lnk} {foo}
+   ha:{src4} entries: 2
+    li:{recurse} FIRST te:{} LAST te:{}
+     te:{} {R40}
+     te:{} {R41}
+    te:{key41} {val41}
+---new
+  ha:{root} [root] [nodoc] entries: 5
+   ha:{src1} [nodoc] entries: 2
+    te:{key11} [nodoc] {val11}
+    te:{key12} [nodoc] {val12}
+   ha:{src2} [nodoc] entries: 2
+    sy:{lnk} [nodoc] {/}
+    te:{key21} [nodoc] {val21}
+   ha:{dst} [nodoc] entries: 4
+    sy:{lnk} [nodoc] {/}
+    te:{key1} [nodoc] {val1}
+    te:{key2} [nodoc] {val2}
+    li:{recurse} [nodoc] FIRST te:{} LAST te:{}
+     te:{} [nodoc] {r1}
+     te:{} [nodoc] {r2}
+     te:{} [nodoc] {r3}
+   ha:{src3} [nodoc] entries: 2
+    te:{key31} [nodoc] {val31}
+    sy:{lnk} [nodoc] {foo}
+   ha:{src4} [nodoc] entries: 2
+    li:{recurse} [nodoc] FIRST te:{} LAST te:{}
+     te:{} [nodoc] {R40}
+     te:{} [nodoc] {R41}
+    te:{key41} [nodoc] {val41}
diff --git a/src_3rd/liblihata/regression/ref/tree_dup_list.tref b/src_3rd/liblihata/regression/ref/tree_dup_list.tref
new file mode 100644
index 0000000..a2dd7a7
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_dup_list.tref
@@ -0,0 +1,12 @@
+---orig
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key1} LAST te:{key2}
+    te:{key1} {val1}
+    te:{key2} {val2}
+---new
+  li:{} [root] [nodoc] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} [nodoc] 
+   li:{non-empty} [nodoc] FIRST te:{key1} LAST te:{key2}
+    te:{key1} [nodoc] {val1}
+    te:{key2} [nodoc] {val2}
diff --git a/src_3rd/liblihata/regression/ref/tree_dup_sy.tref b/src_3rd/liblihata/regression/ref/tree_dup_sy.tref
new file mode 100644
index 0000000..a0b0077
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_dup_sy.tref
@@ -0,0 +1,8 @@
+---orig
+  li:{rt} [root] FIRST te:{text} LAST sy:{symlink}
+   te:{text} {textnode}
+   sy:{symlink} {/}
+---new
+  li:{rt} [root] [nodoc] FIRST te:{text} LAST sy:{symlink}
+   te:{text} [nodoc] {textnode}
+   sy:{symlink} [nodoc] {/}
diff --git a/src_3rd/liblihata/regression/ref/tree_dup_table.tref b/src_3rd/liblihata/regression/ref/tree_dup_table.tref
new file mode 100644
index 0000000..2597393
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_dup_table.tref
@@ -0,0 +1,48 @@
+---orig
+  li:{} [root] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} cols: 0, rows: 0
+   ta:{non-empty} cols: 2, rows: 4
+    Row 0:{rowname0}
+     te:{} {00}
+     te:{} {01}
+    Row 1:{rowname1}
+     te:{} {10}
+     te:{} {11}
+    Row 2:{rowname2}
+     te:{} {20}
+     li:{21} FIRST te:{} LAST te:{}
+      te:{} {a}
+      te:{} {b}
+      te:{} {c}
+      te:{} {d}
+    Row 3:{rowname3}
+     te:{} {30}
+     ha:{31} entries: 4
+      te:{c} {3}
+      te:{d} {4}
+      te:{a} {1}
+      te:{b} {2}
+---new
+  li:{} [root] [nodoc] FIRST ta:{empty} LAST ta:{non-empty}
+   ta:{empty} [nodoc] cols: 0, rows: 0
+   ta:{non-empty} [nodoc] cols: 2, rows: 4
+    Row 0:{rowname0}
+     te:{} [root] [nodoc] {00}
+     te:{} [root] [nodoc] {01}
+    Row 1:{rowname1}
+     te:{} [root] [nodoc] {10}
+     te:{} [root] [nodoc] {11}
+    Row 2:{rowname2}
+     te:{} [root] [nodoc] {20}
+     li:{21} [root] [nodoc] FIRST te:{} LAST te:{}
+      te:{} [nodoc] {a}
+      te:{} [nodoc] {b}
+      te:{} [nodoc] {c}
+      te:{} [nodoc] {d}
+    Row 3:{rowname3}
+     te:{} [root] [nodoc] {30}
+     ha:{31} [root] [nodoc] entries: 4
+      te:{c} [nodoc] {3}
+      te:{d} [nodoc] {4}
+      te:{a} [nodoc] {1}
+      te:{b} [nodoc] {2}
diff --git a/src_3rd/liblihata/regression/ref/tree_has_symlink.tref b/src_3rd/liblihata/regression/ref/tree_has_symlink.tref
new file mode 100644
index 0000000..0261be3
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_has_symlink.tref
@@ -0,0 +1,3 @@
+Tree has symlink(s).
+Tree doesn't have symlink.
+Tree has symlink(s).
diff --git a/src_3rd/liblihata/regression/ref/tree_unlink.tref b/src_3rd/liblihata/regression/ref/tree_unlink.tref
new file mode 100644
index 0000000..fb2373e
--- /dev/null
+++ b/src_3rd/liblihata/regression/ref/tree_unlink.tref
@@ -0,0 +1,29 @@
+The tree before unlinking:
+  li:{non-empty} FIRST te:{key1} LAST te:{key2}
+   te:{key1} {val1}
+   te:{key2} {val2}
+The node before unlinking:
+  te:{key1} {val1}
+tree_unlink: done
+---
+The tree after unlinking:
+  li:{non-empty} FIRST te:{key2} LAST te:{key2}
+   te:{key2} {val2}
+The node after unlinking:
+  te:{key1} [unlinked] [detached] {val1}
+========
+The tree before unlinking:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key2} LAST te:{key2}
+    te:{key2} {val2}
+The node before unlinking:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+Error during tree_unlink: operation would render the document invalid
+The tree after unlinking:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
+   li:{empty} 
+   li:{non-empty} FIRST te:{key2} LAST te:{key2}
+    te:{key2} {val2}
+The node after unlinking:
+  li:{} [root] FIRST li:{empty} LAST li:{non-empty}
diff --git a/src_3rd/liblihata/regression/revpath.lht b/src_3rd/liblihata/regression/revpath.lht
new file mode 100644
index 0000000..3a30b6c
--- /dev/null
+++ b/src_3rd/liblihata/regression/revpath.lht
@@ -0,0 +1,12 @@
+ha:root {
+	li:lst {
+		key=0
+		key=1
+		key=2
+		key=3
+	}
+	ta:tbl {
+		{00; 01}
+		{10; 11}
+	}
+}
diff --git a/src_3rd/liblihata/regression/revpath.tts b/src_3rd/liblihata/regression/revpath.tts
new file mode 100644
index 0000000..5417b3c
--- /dev/null
+++ b/src_3rd/liblihata/regression/revpath.tts
@@ -0,0 +1,63 @@
+# look up not-the-first element of a list thenconstruct the reverse path
+# should get the same path
+
+load_doc D1 revpath.lht
+root N2 D1
+root N3 D1
+path N3 /lst
+
+echo --- list ---
+
+root N1 D1
+path N1 /lst/3
+print_node N1
+list_find_node N3 N1
+path_build N2 N1
+
+
+root N1 D1
+path N1 /lst/2
+print_node N1
+list_find_node N3 N1
+path_build N2 N1
+
+root N1 D1
+path N1 /lst/1
+print_node N1
+list_find_node N3 N1
+path_build N2 N1
+
+root N1 D1
+path N1 /lst/0
+print_node N1
+list_find_node N3 N1
+path_build N2 N1
+
+echo --- table ---
+root N3 D1
+path N3 /tbl
+
+root N1 D1
+path N1 /tbl/0/0
+print_node N1
+path_build N2 N1
+
+
+root N1 D1
+path N1 /tbl/0/1
+print_node N1
+path_build N2 N1
+
+root N1 D1
+path N1 /tbl/1/0
+print_node N1
+path_build N2 N1
+
+
+root N1 D1
+path N1 /tbl/1/1
+print_node N1
+path_build N2 N1
+
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/stream.lht b/src_3rd/liblihata/regression/stream.lht
new file mode 100644
index 0000000..81f105e
--- /dev/null
+++ b/src_3rd/liblihata/regression/stream.lht
@@ -0,0 +1,17 @@
+# multiple roots; implicit eof is generated after the first and the second is ignored
+li:x {
+    sy:foo = bar;
+    bar1 = baz;
+    li:hah = {};
+    bar2 = baz;
+    ha:heh = {};
+    bar3 = baz;
+}
+ha:x {
+    sy:foo = bar;
+    bar1 = baz;
+    li:hah = {};
+    bar2 = baz;
+    ha:heh = {};
+    bar3 = baz;
+}
diff --git a/src_3rd/liblihata/regression/sy.lht b/src_3rd/liblihata/regression/sy.lht
new file mode 100644
index 0000000..a867b62
--- /dev/null
+++ b/src_3rd/liblihata/regression/sy.lht
@@ -0,0 +1,4 @@
+li:rt = {
+	te:text = {textnode}
+	sy:symlink = {/}
+}
diff --git a/src_3rd/liblihata/regression/symlink.lht b/src_3rd/liblihata/regression/symlink.lht
new file mode 100644
index 0000000..4d04b2d
--- /dev/null
+++ b/src_3rd/liblihata/regression/symlink.lht
@@ -0,0 +1,17 @@
+li:root = {
+	sy:key1=val1
+	sy:key2 =val2;
+	sy:key3={val3}
+	sy:key4=val4;
+	sy:key5 {val5}
+	sy:{key6} val6
+	sy:anon1
+	sy:{anon2}
+	sy:anon3;
+	sy:empty={}
+	sy:empty2=
+	sy:{} {}
+	sy:=
+	sy:symlink_ok = {/}
+	sy:symlink_broken = {/3478783}
+}
diff --git a/src_3rd/liblihata/regression/symlink_is_broken.tts b/src_3rd/liblihata/regression/symlink_is_broken.tts
new file mode 100644
index 0000000..976edeb
--- /dev/null
+++ b/src_3rd/liblihata/regression/symlink_is_broken.tts
@@ -0,0 +1,8 @@
+load_doc D1 symlink.lht
+root N1 D1
+root N2 D1
+path_nofollow N2 /13
+symlink_is_broken N1 N2
+path_nofollow N2 /14
+symlink_is_broken N1 N2
+free_doc D1
diff --git a/src_3rd/liblihata/regression/symlink_root_node.lht b/src_3rd/liblihata/regression/symlink_root_node.lht
new file mode 100644
index 0000000..fe75400
--- /dev/null
+++ b/src_3rd/liblihata/regression/symlink_root_node.lht
@@ -0,0 +1,2 @@
+# on parse level this is valid; some tree walk would result in 'bar: not found'
+sy:foo=bar
diff --git a/src_3rd/liblihata/regression/ta.lht b/src_3rd/liblihata/regression/ta.lht
new file mode 100644
index 0000000..889ae8f
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta.lht
@@ -0,0 +1,22 @@
+ha:{
+	ta:empty{}
+	ta:table1 {
+		{row0}
+	}
+	ta:table2 {
+		{row0col0; row0col1}
+		{row1col0; row1col1}
+	}
+	ta:table3 {
+		{row0}
+		{row1}
+		{row2}
+		{row3}
+	}
+	ta:table4 {
+		{row0col0; row0col1; row0col2; row0col3}
+		{row1col0; row1col1; row1col2; row1col3}
+		{row2col0; row2col1; row2col2; row2col3}
+		{row3col0; row3col1; row3col2; row3col3}
+	}
+}
diff --git a/src_3rd/liblihata/regression/ta_del_c.tts b/src_3rd/liblihata/regression/ta_del_c.tts
new file mode 100644
index 0000000..4f12a70
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta_del_c.tts
@@ -0,0 +1,81 @@
+load_doc D1 ta.lht
+
+echo ===== Original, untouched doc at the beginning
+root N1 D1
+print_tree N1
+
+echo ===== Original table1
+path N1 table1
+print_tree N1
+
+# invalid col idx:
+echo ===== table_del_col N1 -1 (invalid)
+table_del_col N1 -1
+print_tree N1
+
+# non-existing col idx:
+echo ===== table_del_col N1 1 (invalid)
+table_del_col N1 1
+print_tree N1
+
+# it's OK:
+echo ===== table_del_col N1 0 (valid)
+table_del_col N1 0
+print_tree N1
+
+# table is empty, not OK:
+echo ===== table_del_col N1 0 (invalid)
+table_del_col N1 0
+print_tree N1
+
+echo ===== Original empty
+root N2 D1
+path N2 empty
+print_tree N2
+
+# table is empty, not OK:
+echo ===== table_del_col N2 0 (invalid)
+table_del_col N2 0
+print_tree N2
+
+# valid dels till empty doc:
+echo ===== table2 before dels:
+root N3 D1
+path N3 table2
+print_tree N3
+table_del_col N3 0
+table_del_col N3 0
+echo ===== After valid dels in table2:
+print_tree N3
+
+echo ===== table3 before del:
+root N4 D1
+path N4 table3
+print_tree N4
+table_del_col N4 0
+echo ===== After valid del in table3:
+print_tree N4
+
+#delete last table, with some invalid tries
+echo ===== table4 before dels:
+root N5 D1
+path N5 table4
+print_tree N5
+echo ===== table_del_col N5 -1 (invalid)
+table_del_col N5 -1
+echo ===== table_del_col N5 4 (invalid)
+table_del_col N5 4
+echo ===== table4 after invalid dels:
+print_tree N5
+table_del_col N5 0
+table_del_col N5 0
+table_del_col N5 0
+table_del_col N5 0
+echo ===== table4 after valid dels:
+print_tree N5
+
+echo ===== The document after deleting all cols:
+root N6 D1
+print_tree N6
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/ta_del_r.tts b/src_3rd/liblihata/regression/ta_del_r.tts
new file mode 100644
index 0000000..c371302
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta_del_r.tts
@@ -0,0 +1,84 @@
+load_doc D1 ta.lht
+
+echo ===== Original, untouched doc at the beginning
+root N1 D1
+print_tree N1
+
+echo ===== Original table1
+path N1 table1
+print_tree N1
+
+# invalid row idx:
+echo ===== table_del_row N1 -1 (invalid)
+table_del_row N1 -1
+print_tree N1
+
+# non-existing row idx:
+echo ===== table_del_row N1 1 (invalid)
+table_del_row N1 1
+print_tree N1
+
+# it's OK:
+echo ===== table_del_row N1 0 (valid)
+table_del_row N1 0
+print_tree N1
+
+# table is empty, not OK:
+echo ===== table_del_row N1 0 (invalid)
+table_del_row N1 0
+print_tree N1
+
+echo ===== Original empty
+root N2 D1
+path N2 empty
+print_tree N2
+
+# table is empty, not OK:
+echo ===== table_del_row N2 0 (invalid)
+table_del_row N2 0
+print_tree N2
+
+# valid dels till empty doc:
+echo ===== table2 before dels:
+root N3 D1
+path N3 table2
+print_tree N3
+table_del_row N3 0
+table_del_row N3 0
+echo ===== After valid dels in table2:
+print_tree N3
+
+echo ===== table3 before dels:
+root N4 D1
+path N4 table3
+print_tree N4
+table_del_row N4 0
+table_del_row N4 0
+table_del_row N4 0
+table_del_row N4 0
+echo ===== After valid dels in table3:
+print_tree N3
+
+#delete last table, with some invalid tries
+echo ===== table4 before dels:
+root N5 D1
+path N5 table4
+print_tree N5
+echo ===== table_del_row N5 -1 (invalid)
+table_del_row N5 -1
+echo ===== table_del_row N5 4 (invalid)
+table_del_row N5 4
+echo ===== table4 after invalid dels:
+print_tree N5
+table_del_row N5 0
+table_del_row N5 0
+table_del_row N5 0
+table_del_row N5 0
+echo ===== table4 after valid dels:
+print_tree N5
+
+echo ===== The document after deleting all rows:
+root N6 D1
+print_tree N6
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/ta_ins_c.tts b/src_3rd/liblihata/regression/ta_ins_c.tts
new file mode 100644
index 0000000..32dffd6
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta_ins_c.tts
@@ -0,0 +1,93 @@
+# use table_ins_col to test lihata 'insert new column' functionality
+
+echo -----test 1-----
+	echo insert one column to empty table
+	
+	load_doc D1 ta.lht
+	
+	root N1 D1
+	
+	path N1 empty
+	
+	echo **before**
+	print_tree N1
+	
+	table_ins_col N1 0
+	
+	echo **after**
+	
+	print_tree N1
+
+echo -----test 2-----
+	echo add one row
+	
+	echo **before**
+	print_tree N1
+	
+	table_ins_row N1 0
+	
+	echo **after**
+	
+	print_tree N1
+
+echo -----test 3-----
+	echo insert column (parameter 0)
+	
+	echo **before**
+	
+	new_text N5 AAA
+	equ_node N2 N1
+	table_cell N2 0 0
+	replace N2 N5
+	
+	print_tree N1
+	
+	echo **after**
+	
+	
+	table_ins_col N1 0
+	
+	new_text N5 BBB
+	equ_node N2 N1
+	table_cell N2 0 0
+	replace N2 N5
+	
+	print_tree N1
+
+echo -----test 4-----
+	echo insert column (parameter 2, this is the last row)
+	
+	echo **before**
+	
+	print_tree N1
+	
+	echo **after**
+	
+	table_ins_col N1 2
+	
+	new_text N5 CCC
+	equ_node N2 N1
+	table_cell N2 0 2
+	replace N2 N5
+	
+	print_tree N1
+
+echo -----test 5-----
+	echo insert column (parameter 1)
+	
+	echo **before**
+	
+	print_tree N1
+	
+	echo **after**
+	
+	table_ins_col N1 1
+	
+	new_text N5 DDD
+	equ_node N2 N1
+	table_cell N2 0 1
+	replace N2 N5
+	
+	print_tree N1
+	
+#free_doc D1
diff --git a/src_3rd/liblihata/regression/ta_ins_c2.tts b/src_3rd/liblihata/regression/ta_ins_c2.tts
new file mode 100644
index 0000000..3a83eb8
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta_ins_c2.tts
@@ -0,0 +1,45 @@
+# use table_ins_col to test lihata 'insert new column' functionality
+
+echo -----test 1-----
+	echo insert column (parameter 1)
+	
+	load_doc D1 ta.lht
+	
+	root N1 D1
+	
+	path N1 table4
+	
+	echo **before**
+	print_tree N1
+	
+	table_ins_col N1 1
+	
+	echo **after**
+	
+	print_tree N1
+
+echo -----test 2-----
+	echo insert column (parameter 0)
+	
+	echo **before**
+	print_tree N1
+	
+	table_ins_col N1 0
+	
+	echo **after**
+	
+	print_tree N1
+
+echo -----test 3-----
+	echo insert column (parameter 6, last row)
+	
+	echo **before**
+	print_tree N1
+	
+	table_ins_col N1 6
+	
+	echo **after**
+	
+	print_tree N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/ta_ins_r.tts b/src_3rd/liblihata/regression/ta_ins_r.tts
new file mode 100644
index 0000000..b1324fd
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta_ins_r.tts
@@ -0,0 +1,57 @@
+# use table_ins_row to test lihata 'insert new row' functionality
+# test cases:
+#   insert new row to empty table              with parameter 0  and 1
+
+echo -----test 1----
+	echo insert one row to empty table
+
+	load_doc D1 ta.lht
+
+	root N1 D1
+	path N1 empty
+	print_node N1
+
+	table_ins_row N1 0
+	table_ins_col N1 0
+	
+	new_text N5 text1 
+	
+	print_node N5
+
+	equ_node N2 N1
+
+	print_node N1
+	print_node N2
+
+
+	table_cell N2 0 0
+
+
+	print_node N2
+
+	replace N2 N5
+	echo after replace
+	equ_node N2 N1
+	table_cell N2 0 0
+	print_node N2
+
+	print_node N1
+
+echo -----test 2-----
+	echo the length of table is 0 so we could not insert element to 1st position
+
+	load_doc D2 ta.lht
+
+	root N1 D2
+	path N1 empty
+	print_node N1
+
+	table_ins_row N1 1   # this will not be succesful 
+	print_node N1
+
+
+echo tts terminated
+
+
+free_doc D1
+free_doc D2
diff --git a/src_3rd/liblihata/regression/ta_ins_r2.tts b/src_3rd/liblihata/regression/ta_ins_r2.tts
new file mode 100644
index 0000000..78993c4
--- /dev/null
+++ b/src_3rd/liblihata/regression/ta_ins_r2.tts
@@ -0,0 +1,99 @@
+# use table_ins_row to test lihata 'insert new row' functionality
+# test cases:
+#   insert new row to table contains one row   with parameter 0 and 1 (and row count) and 0
+#                     table contains two row   with parameter 0 and 1 and row count 
+
+echo -----test 1-----
+	echo insert one row to table (parameter 0)
+
+	load_doc D1 ta.lht
+
+	root N1 D1
+	path N1 table1
+	print_tree N1
+
+	table_ins_row N1 0
+	print_tree N1
+
+	new_text N5 insertedcell00
+	
+	equ_node N2 N1
+	
+	table_cell N2 0 0
+	
+	replace N2 N5
+	
+	print_tree N1
+	
+
+echo -----test 2-----
+	echo insert one row to table (parameter 1)
+
+	echo **before**
+
+	print_tree N1
+
+	table_ins_row N1 1
+
+	new_text N5 insertedcell10
+	equ_node N2 N1
+	table_cell N2 1 0
+	replace N2 N5
+	
+
+	echo **after**
+
+
+	print_tree N1
+
+echo -----test 3-----
+	echo insert one row to table (parameter "row count")
+
+	echo **before**
+	
+	print_tree N1
+
+	table_ins_row N1 3     #  "row count" = 3
+
+	new_text N5 insertedcell30
+	equ_node N2 N1
+	table_cell N2 3 0
+	replace N2 N5
+	
+	echo **after**
+
+	print_tree N1
+
+
+echo -----test 4-----
+	echo insert one row to table (parameter 0)
+
+
+	echo **before**
+	
+	print_tree N1
+
+	table_ins_row N1 0
+
+	new_text N5 shiftedrows
+	equ_node N2 N1
+	table_cell N2 0 0
+	replace N2 N5
+	
+	echo **after**
+	
+	print_tree N1
+
+echo -----test 5-----
+	echo insert one row to table (parameter 2)  (to test it can shift more than one row)
+
+	print_tree N1
+
+	table_ins_row N1 2
+
+	print_tree N1
+
+echo tts terminated
+
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/table.lht b/src_3rd/liblihata/regression/table.lht
new file mode 100644
index 0000000..e208a08
--- /dev/null
+++ b/src_3rd/liblihata/regression/table.lht
@@ -0,0 +1,9 @@
+li:{
+	ta:empty {
+	}
+
+	ta:non-empty {
+		{11; 12}
+		{21; 22}
+	}
+}
diff --git a/src_3rd/liblihata/regression/table_corners.lht b/src_3rd/liblihata/regression/table_corners.lht
new file mode 100644
index 0000000..cb2bc8e
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_corners.lht
@@ -0,0 +1,11 @@
+li:{
+	ta:empty {
+	}
+
+	ta:non-empty {
+		rowname0={00; 01}
+		rowname1={te:10; 11}
+		rowname2={20; li:21={a;b;c;d}}
+		rowname3={30; ha:31={a=1;b=2;c=3;d=4}}
+	}
+}
diff --git a/src_3rd/liblihata/regression/table_detach_cell.tts b/src_3rd/liblihata/regression/table_detach_cell.tts
new file mode 100644
index 0000000..5dbab5c
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_detach_cell.tts
@@ -0,0 +1,100 @@
+# test with a lihata doc in which the root is a li
+load_doc D1 table_corners.lht
+# N1 is a non-empty, N2 is an empty table
+root N1 D1
+path N1 /1
+root N2 D1
+path N2 /0
+
+echo Untouched, original tree:
+print_tree N1
+echo -
+
+table_detach_cell N1 0 0 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 0 1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 1 0 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 1 1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 2 0 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 2 1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 3 0 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 3 1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+
+echo Trying to detach a cell again:
+table_detach_cell N1 3 1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+
+# Detaching out boundaries:
+table_detach_cell N1 4 1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 1 4 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+table_detach_cell N1 -1 -1 N8
+echo tree:
+print_tree N1
+echo node:
+print_tree N8
+echo -
+
+# Detaching in empty table:
+echo Empty table, before trying to detach:
+print_tree N2
+echo -
+table_detach_cell N2 0 0 N8
+echo tree:
+print_tree N2
+echo node:
+print_tree N8
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/table_detach_child.tts b/src_3rd/liblihata/regression/table_detach_child.tts
new file mode 100644
index 0000000..71b7170
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_detach_child.tts
@@ -0,0 +1,26 @@
+load_doc D1 table_corners.lht
+root N1 D1
+path N1 /1
+root N2 D1
+path N2 /1/0/0
+
+echo Before detaching:
+echo The doc:
+print_doc D1
+echo The node:
+print_node N2
+
+echo -
+table_detach_child N1 N2
+
+echo After detaching:
+echo The doc:
+print_doc D1
+echo The node:
+print_node N2
+
+echo -
+echo Trying to detach again:
+table_detach_child N1 N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/table_find_cell.tts b/src_3rd/liblihata/regression/table_find_cell.tts
new file mode 100644
index 0000000..03ea648
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_find_cell.tts
@@ -0,0 +1,47 @@
+# test with a lihata doc in which the root is a li
+load_doc D1 table_corners.lht
+# N1 is a non-empty, N2 is an empty table
+root N1 D1
+path N1 /1
+root N2 D1
+path N2 /0
+
+# N3 is a cell within the table
+root N3 D1
+path N3 /1/0/0
+print_node N3
+
+table_find_cell N1 N3
+table_find_cell N2 N3
+
+# searching one table in another
+table_find_cell N1 N2
+
+
+# another cell within the table
+path N3 /1/3/1
+print_node N3
+table_find_cell N1 N3
+table_find_cell N2 N3
+table_find_cell N1 N2
+
+# test with a lihata doc in which the root is a ha
+load_doc D2 revpath.lht
+# N4 is a non-empty table
+root N4 D2
+path N4 /tbl
+
+# N5 is a cell within the table
+root N5 D2
+path N5 /tbl/1/0
+print_node N5
+
+table_find_cell N4 N5
+
+# searching cells within a foreign table
+table_find_cell N4 N3
+table_find_cell N1 N5
+table_find_cell N2 N5
+
+free_doc D1
+free_doc D2
diff --git a/src_3rd/liblihata/regression/table_named_cell.lht b/src_3rd/liblihata/regression/table_named_cell.lht
new file mode 100644
index 0000000..b831ff5
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_named_cell.lht
@@ -0,0 +1,12 @@
+li:{
+	ta:empty {
+	}
+
+	ta:non-empty {
+		row0 = { col0 = 00; col1 = 01 }
+		row1 = { col0 = 10; col1 = 11 }
+		row2 = { col0 = 20; li:col1 = { cell0 = a; cell1 = b; cell2 = c; cell3 = d } }
+		row3 = { col0 = 30; ha:col1 = { a = 1; b = 2; c = 3; d = 4 } }
+		row0 = { col0 = 0a; col0 = 0b }
+	}
+}
diff --git a/src_3rd/liblihata/regression/table_named_cell.tts b/src_3rd/liblihata/regression/table_named_cell.tts
new file mode 100644
index 0000000..b87a0a6
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_named_cell.tts
@@ -0,0 +1,20 @@
+load_doc D1 table_named_cell.lht
+root N1 D1
+path N1 /1
+echo The doc:
+print_tree N1
+
+
+table_named_cell N1 row0 0 col0 0 N2
+echo Node row0-0-col0-0:
+print_node N2
+
+table_named_cell N1 row0 1 col0 1 N3
+echo Node row0-1-col0-1:
+print_node N3
+
+table_named_cell N1 row0 2 col0 2 N4
+echo Node row0-2-col0-2:
+print_node N4
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/table_read.tts b/src_3rd/liblihata/regression/table_read.tts
new file mode 100644
index 0000000..a06b6c3
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_read.tts
@@ -0,0 +1,72 @@
+load_doc D1 table.lht
+
+echo root:
+root N1 D1
+print_tree N1
+
+echo first table:
+list_child N1
+print_node N1
+
+equ_node N2 N1
+
+echo Reading from empty table:
+echo 1) item (0,0)
+table_cell N1 0 0
+print_node N1
+
+echo 2) item (1,1)
+equ_node N1 N2
+table_cell N1 1 1
+print_node N1
+
+echo 3) item (-1, -1)
+equ_node N1 N2
+table_cell N1 -1 -1
+print_node N1
+
+echo second table:
+root N1 D1
+list_child N1
+list_next N1
+print_node N1
+
+equ_node N2 N1
+
+echo Reading from filled table:
+
+echo 1) item (0,0)
+table_cell N1 0 0
+print_node N1
+
+echo 2) item (0,1)
+equ_node N1 N2
+table_cell N1 0 1
+print_node N1
+
+echo 3) item (1,0)
+equ_node N1 N2
+table_cell N1 1 0
+print_node N1
+
+echo 4) item (1,1)
+equ_node N1 N2
+table_cell N1 1 1
+print_node N1
+
+echo 5) item (-1, -1)
+equ_node N1 N2
+table_cell N1 -1 -1
+print_node N1
+
+echo 6) item (1,2)
+equ_node N1 N2
+table_cell N1 1 2
+print_node N1
+
+echo 7) item (2,1)
+equ_node N1 N2
+table_cell N1 2 1
+print_node N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/table_replace_cell.tts b/src_3rd/liblihata/regression/table_replace_cell.tts
new file mode 100644
index 0000000..2112df4
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_replace_cell.tts
@@ -0,0 +1,43 @@
+# test with a lihata doc in which the root is a li
+load_doc D1 table_corners.lht
+
+# N1 is a non-empty, N2 is an empty table
+root N1 D1
+path N1 /1
+root N2 D1
+path N2 /0
+
+echo Untouched, original tree:
+print_tree N1
+echo ---
+
+new_text N3 for_replacing
+
+echo Replacing a valid cell (cell 0, 0)
+table_replace_cell N1 0 0 N3 N8
+echo res:
+print_tree N1
+echo old:
+print_tree N8
+echo ---
+
+new_text N4 cannot_replace
+
+echo Trying to replace out of boundaries (cell 2, 2)
+table_replace_cell N1 2 2 N4 N8
+echo res:
+print_tree N1
+echo old:
+print_tree N8
+echo ---
+
+new_text N5 can_replace
+
+echo Trying to replace an already replaced cell (cell 0, 0)
+table_replace_cell N1 0 0 N5 N8
+echo res:
+print_tree N1
+echo old:
+print_tree N8
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/table_replace_child.tts b/src_3rd/liblihata/regression/table_replace_child.tts
new file mode 100644
index 0000000..0075382
--- /dev/null
+++ b/src_3rd/liblihata/regression/table_replace_child.tts
@@ -0,0 +1,40 @@
+# test with a lihata doc in which the root is a li
+load_doc D1 table_corners.lht
+# N1 is a non-empty, N2 is an empty table
+root N1 D1
+path N1 /1
+root N2 D1
+path N2 /0
+
+echo --- Original table:
+print_tree N1
+
+equ_node N3 N1
+table_cell N3 0 0
+
+new_text N4 for_replacing
+
+table_replace_child N1 N3 N4
+
+echo --- After replacing (0, 0) cell:
+print_tree N1
+
+equ_node N3 N1
+table_cell N3 2 1
+
+new_text N4 for_replacing
+table_replace_child N1 N3 N4
+
+echo --- After replacing (2, 1) cell:
+print_tree N1
+
+equ_node N3 N1
+table_cell N3 3 1
+
+new_text N4 for_replacing
+table_replace_child N1 N3 N4
+
+echo ---After replacing (3, 1) cell:
+print_tree N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/text.lht b/src_3rd/liblihata/regression/text.lht
new file mode 100644
index 0000000..a4cb503
--- /dev/null
+++ b/src_3rd/liblihata/regression/text.lht
@@ -0,0 +1,15 @@
+li:root = {
+	key1=val1
+	key2 =val2;
+	key3={val3}
+	key4=val4;
+	key5 {val5}
+	{key6} val6
+	anon1
+	{anon2}
+	anon3;
+	empty={}
+	empty2=
+	{} {}
+	=
+}
diff --git a/src_3rd/liblihata/regression/text_root_node.lht b/src_3rd/liblihata/regression/text_root_node.lht
new file mode 100644
index 0000000..f43c41e
--- /dev/null
+++ b/src_3rd/liblihata/regression/text_root_node.lht
@@ -0,0 +1 @@
+te:foo=bar
diff --git a/src_3rd/liblihata/regression/tiny.lht b/src_3rd/liblihata/regression/tiny.lht
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/src_3rd/liblihata/regression/tiny.lht
@@ -0,0 +1 @@
+{}
diff --git a/src_3rd/liblihata/regression/tree_detach.tts b/src_3rd/liblihata/regression/tree_detach.tts
new file mode 100644
index 0000000..8dcad52
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_detach.tts
@@ -0,0 +1,40 @@
+load_doc D1 list.lht
+
+root N1 D1
+list_child N1
+list_next N1
+
+equ_node N2 N1
+list_child N2
+
+echo The tree before detaching:
+print_tree N1
+echo The node before detaching:
+print_node N2
+
+tree_detach N2
+
+echo ---
+echo The tree after detaching:
+print_tree N1
+echo The node after detaching:
+print_node N2
+
+echo ========
+
+load_doc D1 list.lht
+root N1 D1
+echo The tree before detaching:
+print_tree N1
+echo The node before detaching:
+print_node N1
+
+tree_detach N1
+
+echo ---
+echo The tree after detaching:
+print_tree N1
+echo The node after detaching:
+print_node N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/tree_dup_hash.tts b/src_3rd/liblihata/regression/tree_dup_hash.tts
new file mode 100644
index 0000000..558819e
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_dup_hash.tts
@@ -0,0 +1,13 @@
+load_doc D1 merge_hash.lht
+
+root N1 D1
+tree_dup N2 N1
+
+echo ---orig
+print_tree N1
+echo ---new
+print_tree N2
+
+tree_del N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/tree_dup_list.tts b/src_3rd/liblihata/regression/tree_dup_list.tts
new file mode 100644
index 0000000..2d07546
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_dup_list.tts
@@ -0,0 +1,13 @@
+load_doc D1 list.lht
+
+root N1 D1
+tree_dup N2 N1
+
+echo ---orig
+print_tree N1
+echo ---new
+print_tree N2
+
+tree_del N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/tree_dup_sy.tts b/src_3rd/liblihata/regression/tree_dup_sy.tts
new file mode 100644
index 0000000..942c67c
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_dup_sy.tts
@@ -0,0 +1,13 @@
+load_doc D1 sy.lht
+
+root N1 D1
+tree_dup N2 N1
+
+echo ---orig
+print_tree N1
+echo ---new
+print_tree N2
+
+tree_del N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/tree_dup_table.tts b/src_3rd/liblihata/regression/tree_dup_table.tts
new file mode 100644
index 0000000..caae268
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_dup_table.tts
@@ -0,0 +1,13 @@
+load_doc D1 table_corners.lht
+
+root N1 D1
+tree_dup N2 N1
+
+echo ---orig
+print_tree N1
+echo ---new
+print_tree N2
+
+tree_del N2
+
+free_doc D1
diff --git a/src_3rd/liblihata/regression/tree_has_symlink.tts b/src_3rd/liblihata/regression/tree_has_symlink.tts
new file mode 100644
index 0000000..3bebbf3
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_has_symlink.tts
@@ -0,0 +1,14 @@
+load_doc D1 symlink.lht
+root N1 D1
+tree_has_symlink N1
+free_doc D1
+
+load_doc D1 table.lht
+root N1 D1
+tree_has_symlink N1
+free_doc D1
+
+load_doc D1 symlink_root_node.lht
+root N1 D1
+tree_has_symlink N1
+free_doc D1
diff --git a/src_3rd/liblihata/regression/tree_unlink.tts b/src_3rd/liblihata/regression/tree_unlink.tts
new file mode 100644
index 0000000..615f5f0
--- /dev/null
+++ b/src_3rd/liblihata/regression/tree_unlink.tts
@@ -0,0 +1,37 @@
+load_doc D1 list.lht
+
+root N1 D1
+list_child N1
+list_next N1
+
+equ_node N2 N1
+list_child N2
+
+echo The tree before unlinking:
+print_tree N1
+echo The node before unlinking:
+print_node N2
+
+tree_unlink N2
+
+echo ---
+echo The tree after unlinking:
+print_tree N1
+echo The node after unlinking:
+print_node N2
+
+echo ========
+
+root N1 D1
+echo The tree before unlinking:
+print_tree N1
+echo The node before unlinking:
+print_node N1
+
+tree_unlink N1
+echo The tree after unlinking:
+print_tree N1
+echo The node after unlinking:
+print_node N1
+
+free_doc D1
diff --git a/src_3rd/liblihata/test_dom.c b/src_3rd/liblihata/test_dom.c
new file mode 100644
index 0000000..fb2d084
--- /dev/null
+++ b/src_3rd/liblihata/test_dom.c
@@ -0,0 +1,41 @@
+/* This file is placed in the public domain by its
+   author Tibor 'Igor2' Palinkas in 2013. Feel free to
+   copy and extend it in case you need DOM. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "dom.h"
+
+int main()
+{
+	lht_doc_t *doc;
+	int error = 0;
+
+	doc = lht_dom_init();
+	lht_dom_loc_newfile(doc, "<stdin>");
+	while(!(feof(stdin))) {
+		lht_err_t err;
+		int c = getchar();
+		err = lht_dom_parser_char(doc, c);
+		if (err != LHTE_SUCCESS) {
+			if (err != LHTE_STOP) {
+				const char *fn;
+				int line, col;
+				lht_dom_loc_active(doc, &fn, &line, &col);
+				printf("* error: %s (%s:%d.%d)*\n", lht_err_str(err), fn, line+1, col+1);
+				error = 1;
+			}
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+		}
+	}
+
+	if (!error) {
+		if (doc->root == NULL)
+			printf("* empty document *\n");
+		else
+			lht_dom_ptree(doc->root, stdout, "");
+	}
+
+	lht_dom_uninit(doc);
+	return 0;
+}
diff --git a/src_3rd/liblihata/test_parser.c b/src_3rd/liblihata/test_parser.c
new file mode 100644
index 0000000..ee2a7f0
--- /dev/null
+++ b/src_3rd/liblihata/test_parser.c
@@ -0,0 +1,64 @@
+/* This file is placed in the public domain by its
+   author Tibor 'Igor2' Palinkas in 2013. Feel free to
+   copy and extend it in case you need event based lihata
+   parsing. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "parser.h"
+
+static void spaces(int num)
+{
+	for(;num;num--)
+		putchar(' ');
+}
+
+void event(lht_parse_t *ctx, lht_event_t ev, lht_node_type_t nt, const char *name, const char *value)
+{
+	int *level = ctx->user_data;
+
+	switch(ev) {
+		case LHT_OPEN:
+			spaces(*level);
+			printf("%s %s\n", lht_type_name(nt), name);
+			(*level)++;
+			break;
+		case LHT_CLOSE:
+			(*level)--;
+			break;
+		case LHT_TEXTDATA:
+			spaces(*level);
+			printf("%s '%s' = '%s'\n", lht_type_name(nt), name, value);
+			break;
+		case LHT_COMMENT:
+			spaces(*level);
+			printf("# '%s'\n", name);
+			break;
+		case LHT_EOF:
+			spaces(*level);
+			printf("*EOF*\n");
+			break;
+		case LHT_ERROR:
+			spaces(*level);
+			printf("*ERROR* '%s' (at %d:%d)\n", name, ctx->line+1, ctx->col+1);
+			break;
+	}
+}
+
+int main()
+{
+	lht_parse_t p;
+	int level = 0;
+
+	lht_parser_init(&p);
+	p.event = event;
+	p.user_data = &level;
+
+	while(!(feof(stdin))) {
+		int c = getchar();
+		if (lht_parser_char(&p, c))
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+	}
+	lht_parser_uninit(&p);
+	return 0;
+}
diff --git a/src_3rd/liblihata/test_tree.c b/src_3rd/liblihata/test_tree.c
new file mode 100644
index 0000000..cbacdf5
--- /dev/null
+++ b/src_3rd/liblihata/test_tree.c
@@ -0,0 +1,955 @@
+/*
+    liblihata - list/hash/table format, tree tester tool
+    Copyright (C) 2013, 2016  Tibor 'Igor2' Palinkas
+    Copyright (C) 2013  Gabor Horvath (HvG)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains a minimalistic interpreter for varying our tests
+   on the lht_tree* functionality.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include "dom.h"
+#include "tree.h"
+
+lht_doc_t *doc[32];
+lht_node_t *node[32];
+int lineno;
+
+void load_file(lht_doc_t **doc, const char *fn)
+{
+	FILE *f;
+
+	f = fopen(fn, "r");
+	if (f == NULL) {
+		fprintf(stderr, "can not open file '%s' for read\n", fn);
+		exit(1);
+	}
+	*doc = lht_dom_init();
+	while(!(feof(f))) {
+		lht_err_t err;
+		int c = fgetc(f);
+		err = lht_dom_parser_char(*doc, c);
+		if (err != LHTE_SUCCESS) {
+			if (err != LHTE_STOP) {
+				fprintf(stderr, "* error in '%s': %s *\n", fn, lht_err_str(err));
+				exit(1);
+			}
+			break; /* error or stop, do not read anymore (would get LHTE_STOP without any processing all the time) */
+		}
+	}
+	fclose(f);
+}
+
+static void shift_ws(char **args)
+{
+	while((**args != '\0') && isspace(**args)) (*args)++;
+}
+
+/* Shift next argument as string, up to the _first_ whitespace. This means
+   if the first character is a whitespace, empty string is returned. The
+   string returned shall be free'd by the caller. */
+static char *shift_str(char **args)
+{
+	char *s, *start;
+	int len;
+
+	start = *args;
+	while((**args != '\0') && (!isspace(**args))) (*args)++;
+	len = *args - start;
+	s = malloc(len+1);
+	memcpy(s, start, len);
+	s[len] = '\0';
+	return s;
+}
+
+static int shift_int(char **args)
+{
+	char *end;
+	int n;
+
+	n = strtol(*args, &end, 10);
+	if ((*end != '\0') && (*end != ' ') && (*end != '\t')) {
+		fprintf(stderr, "expected an integer, got something else ('%s')\n", *args);
+		exit(1);
+	}
+
+	*args = end;
+	return n;
+}
+
+static int shift_id(char **args, char *type, char idchar, int arrsize)
+{
+	char *end;
+	int id;
+
+	shift_ws(args);
+	if (**args == '\0') {
+		fprintf(stderr, "expected a %s ID, got nothing\n", type);
+		exit(1);
+	}
+	if (**args != idchar) {
+		fprintf(stderr, "expected a %s ID, got something else ('%s'; ID problem)\n", type, *args);
+		exit(1);
+	}
+	(*args)++;
+
+	id = strtol(*args, &end, 10);
+	if ((*end != '\0') && (*end != ' ') && (*end != '\t')) {
+		fprintf(stderr, "expected a %s ID, got something else ('%s'; integer problem)\n", type, *args);
+		exit(1);
+	}
+
+	if ((id < 0) || (id >= arrsize)) {
+		fprintf(stderr, "document ID is out of range ('%s')\n", *args);
+		exit(1);
+	}
+
+	*args = end;
+	return id;
+}
+
+static lht_doc_t **shift_doc(char **args)
+{
+	return doc + shift_id(args, "document", 'D', sizeof(doc) / sizeof(lht_doc_t *));
+}
+
+static lht_node_t **shift_node(char **args)
+{
+	return node + shift_id(args, "node", 'N', sizeof(node) / sizeof(lht_node_t *));
+}
+
+void cmd_load_doc(char *args)
+{
+	lht_doc_t **doc;
+
+	doc = shift_doc(&args);
+	shift_ws(&args);
+	load_file(doc, args);
+}
+
+void cmd_print_doc(char *args)
+{
+	lht_doc_t **doc;
+
+	doc = shift_doc(&args);
+	if (*doc == NULL) {
+		printf("print_doc: doc was NULL\n");
+		return;
+	}
+	if ((*doc)->root == NULL)
+			printf("  * empty document *\n");
+		else
+			lht_dom_ptree((*doc)->root, stdout, "  ");
+}
+
+void cmd_print_node(char *args)
+{
+	lht_node_t **node;
+
+	node = shift_node(&args);
+	if ((*node) == NULL)
+			printf("  * node is NULL *\n");
+		else
+			lht_dom_pnode(*node, stdout, "  ");
+}
+
+void cmd_new_text(char *args)
+{
+	lht_node_t **node;
+	char *s;
+
+	node = shift_node(&args);
+	shift_ws(&args);
+	s = shift_str(&args);
+	*node = lht_dom_node_alloc(LHT_TEXT, "");
+	(*node)->data.text.value = s;
+}
+
+void cmd_print_tree(char *args)
+{
+	lht_node_t **node;
+
+	node = shift_node(&args);
+	if ((*node) == NULL)
+			printf("  * node is NULL *\n");
+		else
+			lht_dom_ptree(*node, stdout, "  ");
+}
+
+void cmd_export(char *args)
+{
+	lht_node_t **node;
+	lht_err_t err;
+
+	node = shift_node(&args);
+	if ((*node) == NULL) {
+		printf("  * node is NULL *\n");
+		return;
+	}
+
+	err = lht_dom_export(*node, stdout,"  ");
+	if (err != LHTE_SUCCESS)
+		printf("export: %s\n", lht_err_str(err));
+}
+
+void cmd_free_doc(char *args)
+{
+	lht_doc_t **doc;
+
+	doc = shift_doc(&args);
+	lht_dom_uninit(*doc);
+}
+
+void cmd_root(char *args)
+{
+	lht_node_t **node;
+	lht_doc_t **doc;
+
+	node = shift_node(&args);
+	doc = shift_doc(&args);
+	*node = (*doc)->root;
+}
+
+void cmd_list_next(char *args)
+{
+	lht_node_t **node;
+
+	node = shift_node(&args);
+	if ((*node)->parent == NULL) {
+		fprintf(stderr, "node is not in a list (root node)\n");
+		exit(1);
+	}
+	if ((*node)->parent->type != LHT_LIST) {
+		fprintf(stderr, "node is not in a list\n");
+		exit(1);
+	}
+	(*node) = (*node)->next;
+}
+
+void cmd_replace(char *args)
+{
+	lht_node_t **dst, **src;
+	shift_ws(&args);
+	dst = shift_node(&args);
+	src = shift_node(&args);
+
+	if ((*dst)->parent == NULL) {
+		fprintf(stderr, "target node is not in a document (detached, floating)\n");
+		exit(1);
+	}
+	if ((*src)->parent != NULL) {
+		fprintf(stderr, "source node is not detached\n");
+		exit(1);
+	}
+
+
+	lht_tree_replace(*dst, *src);
+}
+
+void cmd_list_child(char *args)
+{
+	lht_node_t **node;
+
+	node = shift_node(&args);
+	if ((*node)->type != LHT_LIST) {
+		fprintf(stderr, "node is not a list\n");
+		exit(1);
+	}
+	(*node) = (*node)->data.list.first;
+}
+
+void cmd_list_nth(char *args)
+{
+	lht_node_t **node;
+	int n;
+
+	node = shift_node(&args);
+	shift_ws(&args);
+	n = shift_int(&args);
+	*node = lht_tree_list_nth(*node, n);
+}
+
+void cmd_list_del_nth(char *args)
+{
+	lht_node_t **node;
+	int n;
+	lht_err_t err;
+
+	node = shift_node(&args);
+	shift_ws(&args);
+	n = shift_int(&args);
+
+	err = lht_tree_list_del_nth(*node, n);
+	if (err != LHTE_SUCCESS)
+		printf("list_del_nth: %s\n", lht_err_str(err));
+}
+
+void cmd_list_del_child(char *args)
+{
+	lht_node_t **list, **child;
+
+	list = shift_node(&args);
+	child = shift_node(&args);
+	printf("list_del_child: %d\n", lht_tree_list_del_child(*list, *child));
+	*child = NULL;
+}
+
+void cmd_list_nthname(char *args)
+{
+	lht_node_t **node;
+	int n;
+
+	node = shift_node(&args);
+	shift_ws(&args);
+	n = shift_int(&args);
+	shift_ws(&args);
+	*node = lht_tree_list_nthname(*node, n, args);
+}
+
+
+void cmd_list_find_node(char *args)
+{
+	lht_node_t **list;
+	lht_node_t **node;
+	int res;
+
+	list = shift_node(&args);
+	assert(list != NULL);
+	shift_ws(&args);
+	node = shift_node(&args);
+	assert(node != NULL);
+	shift_ws(&args);
+
+	res = lht_tree_list_find_node(*list, *node);
+	printf("lht_tree_list_find_node: %d\n", res);
+}
+
+
+void cmd_table_cell(char *args)
+{
+	lht_node_t **node;
+	int line, col;
+
+	node = shift_node(&args);
+	shift_ws(&args);
+	line = shift_int(&args);
+	shift_ws(&args);
+	col = shift_int(&args);
+	*node = lht_dom_table_cell(*node, line, col);
+}
+
+void cmd_tree_merge(char *args)
+{
+	lht_node_t **dst, **src;
+	lht_err_t err;
+
+	dst = shift_node(&args);
+	src = shift_node(&args);
+	err = lht_tree_merge(*dst, *src);
+	if (err != LHTE_SUCCESS)
+		printf("merge error: %s\n", lht_err_str(err));
+	else
+		*src = NULL;
+}
+
+#define table_op_int(name) \
+void cmd_table_ ## name(char *args) \
+{ \
+	lht_node_t **node; \
+	int i; \
+	lht_err_t err; \
+	node = shift_node(&args); \
+	shift_ws(&args); \
+	i = shift_int(&args); \
+	err = lht_tree_table_ ## name(*node, i); \
+	if (err == LHTE_SUCCESS) \
+		return; \
+	fprintf(stdout, "table_" #name ": %s\n", lht_err_str(err)); \
+}
+
+table_op_int(ins_col);
+table_op_int(ins_row);
+table_op_int(del_col);
+table_op_int(del_row);
+
+void cmd_path_(char *args, int follow_sy)
+{
+	lht_node_t **dst;
+	lht_err_t err;
+
+	dst = shift_node(&args);
+	assert(dst != NULL);
+	shift_ws(&args);
+
+	*dst = lht_tree_path_((*dst)->doc, *dst, args, follow_sy, 0, &err);
+	if (err != LHTE_SUCCESS)
+		printf("Path error: %s\n", lht_err_str(err));
+}
+
+void cmd_path(char *args)
+{
+	cmd_path_(args, 1);
+}
+
+
+
+void cmd_path_build(char *args)
+{
+	lht_node_t **cwd;
+	lht_node_t **node;
+	char *res;
+	lht_err_t err;
+
+	cwd = shift_node(&args);
+	assert(cwd != NULL);
+	shift_ws(&args);
+	node = shift_node(&args);
+	assert(node != NULL);
+	shift_ws(&args);
+
+	res = lht_tree_path_build(*cwd, *node, &err);
+	if (err != LHTE_SUCCESS)
+		printf("lht_tree_path_build: broken document\n");
+	else
+		printf("lht_tree_path_build: %s\n", res);
+	free(res);
+}
+
+
+void cmd_path_nofollow(char *args)
+{
+	cmd_path_(args, 0);
+}
+
+void cmd_equ_node(char *args)
+{
+	lht_node_t **dst, **src;
+
+	dst = shift_node(&args);
+	src = shift_node(&args);
+
+	*dst = *src;
+}
+
+void cmd_equ_doc(char *args)
+{
+	lht_doc_t **dst, **src;
+
+	dst = shift_doc(&args);
+	src = shift_doc(&args);
+
+	*dst = *src;
+}
+
+void cmd_echo(char *args)
+{
+	printf("%s\n", args);
+}
+
+void cmd_tree_has_symlink(char *args)
+{
+	lht_node_t **node;
+	int result;
+	char yes[] = "Tree has symlink(s).\n";
+	char no[] = "Tree doesn't have symlink.\n";
+	node = shift_node(&args);
+	result = lht_tree_has_symlink(*node, 0);
+	if (result)
+		printf("%s", yes);
+	else
+		printf("%s", no);
+}
+
+void cmd_symlink_is_broken(char *args)
+{
+	lht_node_t **parent, **symlink;
+	int result;
+	char yes[] = "Symlink is broken.\n";
+	char no[] = "Symlink is OK.\n";
+	parent = shift_node(&args);
+	symlink = shift_node(&args);
+	result = lht_tree_symlink_is_broken(*parent, *symlink);
+	if (result)
+		printf("%s", yes);
+	else
+		printf("%s", no);
+}
+
+void cmd_table_find_cell(char *args)
+{
+	lht_node_t **table, **cell;
+	int result, row, col;
+	table = shift_node(&args);
+	cell = shift_node(&args);
+	result = lht_tree_table_find_cell(*table, *cell, &row, &col);
+	if (result)
+		printf("Cell was found in row %d, col %d.\n", row, col);
+	else
+		printf("%s", "Cell wasn't found in table.\n");
+}
+
+void cmd_table_detach_cell(char *args)
+{
+	lht_node_t **table;
+	lht_node_t *result;
+	lht_node_t **out;
+	int row, col;
+	lht_err_t err;
+	table = shift_node(&args);
+	row = shift_int(&args);
+	col = shift_int(&args);
+	out = shift_node(&args);
+	result = lht_tree_table_detach_cell(*table, row, col, &err);
+	if (err != LHTE_SUCCESS)
+		printf("table_detach_cell: error: %s\n", lht_err_str(err));
+	else
+		printf("table_detach_cell: %d;%d success\n", row, col);
+	*out = result;
+}
+
+void cmd_table_replace_cell(char *args)
+{
+	lht_node_t **table, **source, **out;
+	lht_node_t *result;
+	int row, col;
+	lht_err_t err;
+	table = shift_node(&args);
+	row = shift_int(&args);
+	col = shift_int(&args);
+	source = shift_node(&args);
+	out = shift_node(&args);
+
+	result = lht_tree_table_replace_cell(*table, row, col, &err, *source);
+	if (err != LHTE_SUCCESS)
+		printf("table_replace_cell: error: %s\n", lht_err_str(err));
+	else
+		printf("table_replace_cell: %d/%d success\n", row, col);
+	*out = result;
+}
+
+void cmd_table_detach_child(char *args)
+{
+	lht_node_t **table, **child;
+	lht_err_t err;
+
+	table = shift_node(&args);
+	child = shift_node(&args);
+
+	err = lht_tree_table_detach_child(*table, *child);
+
+	if (err != LHTE_SUCCESS)
+		printf("table_detach_child error: %s\n", lht_err_str(err));
+}
+
+void cmd_node_is_under(char *args)
+{
+	lht_node_t **source, **parent;
+	int result;
+	char *srcname = "", *prntname = "";
+
+	source = shift_node(&args);
+	parent = shift_node(&args);
+	if ((*source)->name != NULL)
+		srcname = (*source)->name;
+	if ((*parent)->name != NULL)
+		prntname = (*parent)->name;
+	result = lht_tree_is_under(*source, *parent);
+	if (result)
+		printf("Node '%s' is under node '%s'\n", srcname, prntname);
+	else
+		printf("Node '%s' is NOT under node '%s'\n", srcname, prntname);
+}
+
+void cmd_list_replace_child(char *args)
+{
+	lht_node_t **list, **current, **new;
+	lht_err_t result;
+
+	list = shift_node(&args);
+	current = shift_node(&args);
+	new = shift_node(&args);
+	result = lht_tree_list_replace_child(*list, *current, *new);
+	if (result == LHTE_SUCCESS)
+		printf("list_replace_child: done\n");
+	else
+		printf("Error during list_replace_child: %s\n", lht_err_str(result));
+}
+
+void cmd_list_detach_child(char *args)
+{
+	lht_node_t **list, **node;
+	lht_err_t result;
+
+	list = shift_node(&args);
+	node = shift_node(&args);
+	result = lht_tree_list_detach_child(*list, *node);
+	if (result == LHTE_SUCCESS)
+		printf("list_detach_child: done\n");
+	else
+		printf("Error during list_detach_child: %s\n", lht_err_str(result));
+}
+
+void cmd_table_replace_child(char *args)
+{
+	lht_node_t **table, **current, **new;
+	lht_err_t result;
+
+	table = shift_node(&args);
+	current = shift_node(&args);
+	new = shift_node(&args);
+	result = lht_tree_table_replace_child(*table, *current, *new);
+	if (result == LHTE_SUCCESS)
+		printf("table_replace_child: done\n");
+	else
+		printf("Error during table_replace_child: %s\n", lht_err_str(result));
+}
+
+void cmd_tree_unlink(char *args)
+{
+	lht_node_t **node;
+	lht_err_t result;
+
+	node = shift_node(&args);
+	result = lht_tree_unlink(*node);
+	if (result == LHTE_SUCCESS)
+		printf("tree_unlink: done\n");
+	else
+		printf("Error during tree_unlink: %s\n", lht_err_str(result));
+}
+
+void cmd_tree_detach(char *args)
+{
+	lht_node_t **node;
+	lht_err_t result;
+
+	node = shift_node(&args);
+	result = lht_tree_detach(*node);
+	if (result == LHTE_SUCCESS)
+		printf("tree_detach: done\n");
+	else
+		printf("Error during tree_detach: %s\n", lht_err_str(result));
+}
+
+void cmd_list_detach_nth(char *args)
+{
+	lht_node_t **node;
+	lht_node_t *result;
+	int nth;
+
+	node = shift_node(&args);
+	nth = shift_int(&args);
+	result = lht_tree_list_detach_nth(*node, nth);
+	if (result != NULL)
+		printf("list_detach_nth: done\n");
+	else
+		printf("list_detach_nth: NULL was returned\n");
+}
+
+void cmd_table_named_cell(char *args)
+{
+	lht_node_t **table, **dest;
+	char *rowname, *colname;
+	int row, col;
+
+	table = shift_node(&args);
+	shift_ws(&args);
+	rowname = shift_str(&args);
+	row = shift_int(&args);
+	shift_ws(&args);
+	colname = shift_str(&args);
+	col = shift_int(&args);
+	dest = shift_node(&args);
+	*dest = lht_tree_table_named_cell(*table, rowname, row, colname, col);
+	free(rowname);
+	free(colname);
+}
+
+void cmd_dom_load(char *args)
+{
+	char *docname;
+	lht_doc_t **d;
+	char *errmsg;
+
+	d = shift_doc(&args);
+	shift_ws(&args);
+	docname = shift_str(&args);
+
+	*d = lht_dom_load(docname, &errmsg);
+	if (*d == NULL) {
+		printf("dom_load: %s", errmsg);
+		free(errmsg);
+	}
+	free(docname);
+}
+
+void cmd_dom_iterate(char *args)
+{
+	lht_node_t **parent;
+	lht_dom_iterator_t it;
+	lht_node_t *child;
+
+	parent = shift_node(&args);
+	child = lht_dom_first(&it, *parent);
+
+	if (child == NULL) {
+		printf("  * node has no children *\n");
+		return;
+	}
+
+	while (child != NULL) {
+		lht_dom_pnode(child, stdout, "  ");
+		child = lht_dom_next(&it);
+	}
+}
+
+void cmd_dom_node_free(char *args)
+{
+	lht_node_t **node;
+
+	node = shift_node(&args);
+	lht_dom_node_free(*node);
+}
+
+void cmd_dom_list_append(char *args)
+{
+	lht_node_t **dest, **node;
+	lht_err_t err;
+
+	dest = shift_node(&args);
+	node = shift_node(&args);
+
+	err = lht_dom_list_append(*dest, *node);
+	if (err != LHTE_SUCCESS)
+		printf("Error during dom_list_append: %s\n", lht_err_str(err));
+}
+
+void cmd_dom_list_insert(char *args)
+{
+	lht_node_t **dest, **node;
+	lht_err_t err;
+
+	dest = shift_node(&args);
+	node = shift_node(&args);
+
+	err = lht_dom_list_insert(*dest, *node);
+	if (err != LHTE_SUCCESS)
+		printf("Error during dom_list_insert: %s\n", lht_err_str(err));
+}
+
+void cmd_dom_list_len(char *args)
+{
+	lht_node_t **node;
+	int len;
+
+	node = shift_node(&args);
+	len = lht_dom_list_len(*node);
+	printf("Len of the list: %d\n", len);
+}
+
+void cmd_dom_hash_put(char *args)
+{
+	lht_node_t **dest, **node;
+	lht_err_t result;
+
+	dest = shift_node(&args);
+	node = shift_node(&args);
+
+	result = lht_dom_hash_put(*dest, *node);
+
+	if (result != LHTE_SUCCESS)
+		printf("Error during dom_hash_put: %s\n", lht_err_str(result));
+}
+
+void cmd_dom_hash_get(char *args)
+{
+	lht_node_t **parent, **node;
+	char *key;
+
+	parent = shift_node(&args);
+	node = shift_node(&args);
+	shift_ws(&args);
+	key = shift_str(&args);
+
+	*node = lht_dom_hash_get(*parent, key);
+	free(key);
+}
+
+void cmd_tree_del(char *args)
+{
+	lht_node_t **node;
+	lht_err_t err;
+
+	node = shift_node(&args);
+	if (node == NULL) {
+		printf("Error in tree_del: attempt to delete NULL\n");
+		return;
+	}
+
+	err = lht_tree_del(*node);
+	if (err != LHTE_SUCCESS)
+		printf("Error in tree_del: %s\n", lht_err_str(err));
+	else
+		*node = NULL;
+}
+
+void cmd_tree_dup(char *args)
+{
+	lht_node_t **dst, **src;
+	lht_err_t err;
+
+	dst = shift_node(&args);
+	if (dst == NULL) {
+		printf("Error in tree_dup: attempt to dup into NULL\n");
+		return;
+	}
+
+	src = shift_node(&args);
+	if (src == NULL) {
+		printf("Error in tree_dup: attempt to dup NULL\n");
+		return;
+	}
+
+	*dst = lht_dom_duptree(*src);
+	if (*dst == NULL)
+		printf("Error in tree_dup\n");
+}
+
+
+typedef struct {
+	const char *name;
+	void (*cmd)(char *args);
+} cmd_t;
+
+cmd_t cmds[] = {
+	{"load_doc", cmd_load_doc},                      /* Dtarget filename */
+	{"free_doc", cmd_free_doc},                      /* Dtarget */
+	{"print_doc", cmd_print_doc},                    /* Dsource */
+	{"print_node", cmd_print_node},                  /* Nsource */
+	{"print_tree", cmd_print_tree},                  /* Nsource */
+	{"export", cmd_export},                          /* Nsource */
+	{"replace", cmd_replace},                        /* Ndest Nsource */
+	{"new_text", cmd_new_text},                      /* Ntarget text */
+	{"list_next", cmd_list_next},                    /* Ntarget */
+	{"list_child", cmd_list_child},                  /* Ntarget */
+	{"list_nth", cmd_list_nth},                      /* Ntarget int */
+	{"list_nthname", cmd_list_nthname},              /* Ntarget int name */
+	{"list_del_nth", cmd_list_del_nth},              /* Nlist int */
+	{"list_del_child", cmd_list_del_child},          /* Nlist Nchild */
+	{"list_find_node", cmd_list_find_node},          /* Nlist, Nchild */
+	{"table_cell", cmd_table_cell},                  /* Ntable line col */
+	{"table_ins_row", cmd_table_ins_row},            /* Ntable int */
+	{"table_ins_col", cmd_table_ins_col},            /* Ntable int */
+	{"table_del_row", cmd_table_del_row},            /* Ntable int */
+	{"table_del_col", cmd_table_del_col},            /* Ntable int */
+	{"table_find_cell", cmd_table_find_cell},        /* Ntable, Nchild */
+	{"table_detach_cell", cmd_table_detach_cell},    /* Ntable row col Nold */
+	{"table_replace_cell", cmd_table_replace_cell},  /* Ntable row col Nsource Nold */
+	{"table_detach_child", cmd_table_detach_child},  /* Ntable Nchild */
+	{"tree_merge", cmd_tree_merge},                  /* Ndst Nsrc */
+	{"path", cmd_path},                              /* Ndest str */
+	{"path_nofollow", cmd_path_nofollow},            /* Ndest str */
+	{"path_build", cmd_path_build},                  /* Ncwd Nnode */
+	{"echo", cmd_echo},                              /* string */
+	{"root", cmd_root},                              /* Ntarget Dsource */
+	{"equ_node", cmd_equ_node},                      /* Ntarget Nsource */
+	{"equ_doc",  cmd_equ_node},                      /* Dtarget Dsource */
+	{"tree_has_symlink", cmd_tree_has_symlink},      /* Nsrc */
+	{"symlink_is_broken", cmd_symlink_is_broken},    /* Nparent, Nsy */
+	{"node_is_under", cmd_node_is_under},            /* Nsource, Nparent */
+	{"list_replace_child", cmd_list_replace_child},  /* Nlist, Ncurrent, Nnew */
+	{"list_detach_child", cmd_list_detach_child},    /* Nlist, Nnode */
+	{"table_replace_child", cmd_table_replace_child},/* Ntable, Ncurrent, Nnew */
+	{"tree_unlink", cmd_tree_unlink},                /* Nnode */
+	{"tree_detach", cmd_tree_detach},                /* Nnode */
+	{"list_detach_nth", cmd_list_detach_nth},        /* Nlist, int */
+	{"table_named_cell", cmd_table_named_cell},      /* Ntable string int string int Ndest */
+	{"dom_load", cmd_dom_load},                      /* Dtarget filename */
+	{"dom_iterate", cmd_dom_iterate},                /* Nnode */
+	{"dom_node_free", cmd_dom_node_free},            /* Nnode */
+	{"dom_list_append", cmd_dom_list_append},        /* Nlist Nnode */
+	{"dom_list_insert", cmd_dom_list_insert},        /* Nlist Nnode */
+	{"dom_list_len", cmd_dom_list_len},              /* Nlist */
+	{"dom_hash_put", cmd_dom_hash_put},              /* Nhash Nnode */
+	{"dom_hash_get", cmd_dom_hash_get},              /* Nhash Nnode key */
+	{"tree_del", cmd_tree_del},                      /* Nnode */
+	{"tree_dup", cmd_tree_dup},                      /* Nnode, Nnode */
+	{NULL, NULL}
+};
+
+void command(char *line)
+{
+	char *args;
+	cmd_t *c;
+
+	args = strpbrk(line, " \t");
+	if (args != NULL) {
+		*args = '\0';
+		args++;
+	}
+	shift_ws(&args);
+	for(c = cmds; c->name != NULL; c++) {
+		if (strcmp(c->name, line) == 0) {
+			c->cmd(args);
+			return;
+		}
+	}
+	fprintf(stderr, "Invalid command %s\n", line);
+	exit(1);
+}
+
+int main()
+{
+	lineno = 0;
+	memset(doc, 0, sizeof(doc));
+	memset(node, 0, sizeof(node));
+
+	while(!(feof(stdin))) {
+		char line[1024], *l, *end;
+		lineno++;
+		*line = '\0';
+		fgets(line, sizeof(line)-1, stdin);
+		l = line;
+		while(isspace(*l)) l++;
+		switch(*l) {
+			case '\0':
+			case '#':
+			case '\n':
+			case '\r':
+				break;
+			default:
+				/* strip trailing whitespace */
+				end = l + strlen(l) - 1;
+				while((*end == '\r') || (*end == '\n')) {
+					*end = '\0';
+					if (end == l)
+						break;
+					end--;
+				}
+				command(l);
+		}
+	}
+
+	return 0;
+}
diff --git a/src_3rd/liblihata/tree.c b/src_3rd/liblihata/tree.c
new file mode 100644
index 0000000..7c4d1b7
--- /dev/null
+++ b/src_3rd/liblihata/tree.c
@@ -0,0 +1,262 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Gabor Horvath (HvG)
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains type-independent tree walk helpers.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "liblihata/dom.h"
+#include "liblihata/tree.h"
+#include "dom_internal.h"
+
+lht_err_t lht_tree_detach(lht_node_t *node)
+{
+	if (node->parent == NULL) {
+		if (node->doc == NULL) /* a floating subtree is considered detached */
+			return LHTE_SUCCESS;
+		if (node->doc->detach_doc) /* root of a detach-doc - already detached */
+			return LHTE_SUCCESS;
+
+		/* detaching the root of an user managed document */
+		return lht_dom_loc_detach(node);
+	}
+
+	switch(node->parent->type) {
+		case LHT_LIST:    return lht_tree_list_detach_child(node->parent, node);
+		case LHT_HASH:    return lht_tree_hash_detach_child(node->parent, node);
+		case LHT_TABLE:   return lht_tree_table_detach_child(node->parent, node);
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+		case LHT_INVALID_TYPE:
+			break; /* return broken doc */
+	}
+	return LHTE_BROKEN_DOC;
+}
+
+lht_err_t lht_tree_replace(lht_node_t *node, lht_node_t *newn)
+{
+	switch(node->parent->type) {
+		case LHT_LIST:    return lht_tree_list_replace_child(node->parent, node, newn);
+		case LHT_HASH:    return lht_tree_hash_replace_child(node->parent, node, newn);
+		case LHT_TABLE:   return lht_tree_table_replace_child(node->parent, node, newn);
+		case LHT_TEXT:
+		case LHT_SYMLINK:
+		case LHT_INVALID_TYPE:
+			break; /* return broken doc */
+	}
+	return LHTE_BROKEN_DOC;
+}
+
+
+
+lht_err_t lht_tree_unlink(lht_node_t *node)
+{
+	lht_err_t err;
+
+	if (node->doc == NULL)
+		return LHTE_NOT_IN_DOC;  /* not attached to a document */
+
+	if (node->parent == NULL)
+		return LHTE_WOULD_RESULT_INVALID_DOC; /* can't leave doc without a root */
+
+	if (node->parent == node->doc->unlinked)
+		return LHTE_SUCCESS;  /* already on the unlink list */
+
+	err = lht_tree_detach(node);
+	if (err != LHTE_SUCCESS)
+		return err;
+
+	return lht_dom_list_append(node->doc->unlinked, node);
+}
+
+
+lht_err_t lht_tree_del(lht_node_t *node)
+{
+	lht_err_t err;
+
+	/* can't delete the root - the caller should uninit the doc instead */
+	if ((node->doc != NULL) && (node == node->doc->root))
+		return LHTE_WOULD_RESULT_INVALID_DOC;
+
+	err = lht_tree_detach(node);
+	if (err != LHTE_SUCCESS)
+		return err;
+
+	if (node->doc != NULL)
+		lht_dom_uninit(node->doc); /* free the whole detached doc */
+	else
+		lht_dom_node_free(node); /* floating node, there's no doc to free */
+	return LHTE_SUCCESS;
+}
+
+#define merge_err_(_err_) \
+	do { \
+		if (err != NULL) \
+			*err = _err_; \
+		return 0; \
+	} while(0)
+
+/* tests whether a lht_tree_merge(dst, src) would fail. */
+int lht_tree_can_merge(lht_node_t *dst, lht_node_t *src, lht_err_t *err)
+{
+	if (dst == src)
+		merge_err_(LHTE_MERGE_CYCLIC);
+
+	if (dst->type != src->type)
+		merge_err_(LHTE_MERGE_TYPE_MISMATCH);
+
+	if (lht_tree_is_under(dst, src))
+		merge_err_(LHTE_MERGE_CYCLIC);
+
+	switch(dst->type) {
+		case LHT_INVALID_TYPE:
+			merge_err_(LHTE_BROKEN_DOC);
+		case LHT_TEXT:
+		case LHT_LIST:
+			/* always can be merged */
+			break;
+		case LHT_SYMLINK:
+			if ((*dst->data.symlink.value == '\0') || (*src->data.symlink.value == '\0'))
+				merge_err_(LHTE_MERGE_EMPTY_SYMLINK);
+			if (strcmp(dst->data.symlink.value, src->data.symlink.value) != 0)
+				merge_err_(LHTE_MERGE_SYMLINK_MISMATCH);
+			break;
+		case LHT_TABLE:
+			if (dst->data.table.cols != src->data.table.cols)
+				merge_err_(LHTE_MERGE_TABLECOLS_MISMATCH);
+			break;
+		case LHT_HASH:
+			{
+				lht_node_t *d, *s;
+				lht_dom_iterator_t it;
+				for(s = lht_dom_first(&it, src); s != NULL; s = lht_dom_next(&it)) {
+					d = lht_dom_hash_get(dst, s->name);
+					if (d != NULL) {
+						/* hash collision */
+						int ret;
+						ret = lht_tree_can_merge(d, s, err);
+						if (ret == 0)
+							return 0;
+					}
+				}
+			}
+			break;
+	}
+
+	if (err != NULL)
+		*err = LHTE_SUCCESS;
+
+	return 1;
+}
+
+
+/* Merges two nodes assuming all checks has been done and they can be merged.
+   Also assume that src is already detached and can be free'd */
+static lht_err_t lht_tree_merge_(lht_node_t *dst, lht_node_t *src)
+{
+	lht_dom_iterator_t it;
+	lht_node_t *s, *d;
+	int r;
+
+	switch(dst->type) {
+		case LHT_INVALID_TYPE:
+			return LHTE_BROKEN_DOC;
+		case LHT_TEXT:
+			{
+				char *s;
+				int l1, l2;
+				l1 = strlen(dst->data.text.value);
+				l2 = strlen(src->data.text.value);
+				s = malloc(l1+l2+1);
+				memcpy(s, dst->data.text.value, l1);
+				memcpy(s+l1, src->data.text.value, l2+1);
+				free(dst->data.text.value);
+				dst->data.text.value = s;
+			}
+			break;
+		case LHT_LIST:
+			for(s = lht_dom_first(&it, src); s != NULL; s = lht_dom_next(&it)) {
+				lht_tree_detach(s); /* removing an item from the list from the iterator is a special case that works since r584 */
+				lht_dom_list_append(dst, s);
+			}
+			break;
+		case LHT_SYMLINK:
+			/* symlink text match */
+			break;
+		case LHT_TABLE:
+			for(r = 0; r < src->data.table.rows; r++) {
+				int c, dr;
+
+				dr = dst->data.table.rows;
+				if (lht_tree_table_ins_row_(dst, dr) != 0)
+					return LHTE_OUT_OF_MEM;
+				dst->data.table.row_names[dr] = src->data.table.row_names[r];
+				src->data.table.row_names[r] = NULL;
+				for (c = 0; c < dst->data.table.cols; c++) {
+					dst->data.table.r[dr][c] = src->data.table.r[r][c];
+					dst->data.table.r[dr][c]->doc = dst->doc;
+					dst->data.table.r[dr][c]->parent = dst;
+				}
+			}
+			src->data.table.rows = 0;
+			src->data.table.cols = 0;
+			break;
+		case LHT_HASH:
+			for(s = lht_dom_first(&it, src); s != NULL; s = lht_dom_next(&it)) {
+				lht_tree_detach(s);
+				d = lht_dom_hash_get(dst, s->name);
+				if (d != NULL) {
+					/* hash collision */
+					int ret;
+					ret = lht_tree_merge_(d, s);
+					if (ret != LHTE_SUCCESS)
+						return ret;
+				}
+				else
+					lht_dom_hash_put(dst, s);
+			}
+			break;
+	}
+	lht_dom_node_free(src);
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_tree_merge(lht_node_t *dst, lht_node_t *src)
+{
+	lht_err_t err;
+	if (!lht_tree_can_merge(dst, src, &err))
+		return err;
+
+	lht_tree_detach(src);
+	return lht_tree_merge_(dst, src);
+}
+
+
+int lht_tree_is_under(lht_node_t *node, lht_node_t *anc)
+{
+	for(node = node->parent; node != NULL; node = node->parent)
+		if (node == anc)
+			return 1;
+	return 0;
+}
diff --git a/src_3rd/liblihata/tree.h b/src_3rd/liblihata/tree.h
new file mode 100644
index 0000000..dfcb180
--- /dev/null
+++ b/src_3rd/liblihata/tree.h
@@ -0,0 +1,164 @@
+#ifndef LIHATA_TREE_H
+#define LIHATA_TREE_H
+#include "liblihata/dom.h"
+
+/* --- generic --- */
+
+/* return non-zero if anc is an ancestor of node */
+int lht_tree_is_under(lht_node_t *node, lht_node_t *anc);
+
+/* Unlink the node from under its parent; the node is placed on the unlinked list of the document
+   Unlinking means the node is floating within the document; it's not linked
+   in the tree but is not forgotten. Unlinked nodes of a document is freed
+   when the document is freed.
+*/
+lht_err_t lht_tree_unlink(lht_node_t *node);
+
+/* Detach the node from under its parent; the node is left floating:
+   not being part of any document. Warning: detaching large subtrees
+   can be slow due to detaching of location info for the whole subtree. */
+lht_err_t lht_tree_detach(lht_node_t *node);
+
+/* Detach the node from under its parent; the node is left floating:
+   not being part of any document. Put (already detached) newn
+   in the same place in parent - this means:
+    - same place in list (order will not change) 
+    - same row:col in table
+    - in hash node will be detached and newn will be added (there's no order in hash)
+   */
+lht_err_t lht_tree_replace(lht_node_t *node, lht_node_t *newn);
+
+
+/* Detach the node from under its parent and free it recursively */
+lht_err_t lht_tree_del(lht_node_t *node);
+
+/* resolve a path and return the matching node. cwd is used if path is relative.
+   Symlinks:
+     - followed along the path until the last portion of the path
+     - if follow_sy is zero, the resulting node is returned as-is even if it is a symlink
+     - if follow_sy is non-zero and the resulting node would be a symlink, search is restarted using the value of the symlink
+   The _ suffixed call expects cwd to be a node and exposes depth (should be 0 if the call is not the follow-up for a symlink lookup)
+   Error code is stored in *err if err is not NULL.
+*/
+lht_node_t *lht_tree_path(lht_doc_t *doc, const char *cwd, const char *path, int follow_sy, lht_err_t *err);
+lht_node_t *lht_tree_path_(lht_doc_t *doc, const lht_node_t *cwd, const char *path_, int follow_sy, int depth, lht_err_t *err);
+
+/* Builds the relative path of node from cwd to node; if cwd is NULL, the
+   absolute path is built (same as if cwd is the root). Path is malloc()'d
+   and the caller is responsible for free()'ing it. Returns NULL if
+   cwd is not NULL and node is not under cwd. */
+char *lht_tree_path_build(lht_node_t *cwd, lht_node_t *node, lht_err_t *err);
+
+/* checks whether a symlink is broken, relative to cwd. The document the
+   check is done on is cwd's document if cwd is not NULL, or sy's document
+   if cwd is NULL.
+   Returns:
+    1 if it is broken
+    0 if it is valid (points to a node that can be retrieved by the symlink)
+    -1 if sy is not a symlink.
+*/
+int lht_tree_symlink_is_broken(lht_node_t *cwd, lht_node_t *sy);
+
+
+/* checks whether a subtree has symlinks; if chk_broken is non-zero, also
+   check for broken symlinks. If tree itself is a symlink, it is not
+   checked, only its children. Returns:
+    0 there is no symlink in the subtree
+    1 there is at least one symlink in the subtree and chk_broken == 0
+    1 there is at least one broken symlink in the subtree and chk_broken != 0
+*/
+int lht_tree_has_symlink(lht_node_t *tree, int chk_broken);
+
+
+/* merge src node into dst; if the merge would fail, no modification is made to
+   dst or src or their children. If the merge is successful, src is and subnodes
+   are deleted from their original tree and the pointer to src becomes invalid. */
+lht_err_t lht_tree_merge(lht_node_t *dst, lht_node_t *src);
+
+/* --- LIST --- */
+/* returns the nth element of a list (slow), or NULL if n is
+   out of bounds; n counts from 0 */
+lht_node_t *lht_tree_list_nth(const lht_node_t *list, int n);
+
+/* returns the nth name match on the list (e.g. "the 2nd node which is called foo";
+   very slow), or NULL if n is out of bounds; n counts from 0 */
+lht_node_t *lht_tree_list_nthname(const lht_node_t *list, int n, const char *name);
+
+/* delete/detach the nth element of a list (slow); returns 0 on success; n counts from 0*/
+lht_err_t lht_tree_list_del_nth(lht_node_t *list, int n);
+lht_node_t *lht_tree_list_detach_nth(lht_node_t *list, int n);
+
+/* delete/detach the node from a list (slow); returns 0 on success */
+lht_err_t lht_tree_list_del_child(lht_node_t *list, lht_node_t *node);
+lht_err_t lht_tree_list_detach_child(lht_node_t *list, lht_node_t *node);
+
+/* replace node with (already detached) newn on a list (keeping order of nodes) */
+lht_err_t lht_tree_list_replace_child(lht_node_t *list, lht_node_t *node, lht_node_t *newn);
+
+/* linear search for node in a list; returns index if found, -1 if not found. */
+int lht_tree_list_find_node(lht_node_t *list, lht_node_t *node);
+
+
+/* --- HASH --- */
+
+/* returns the number of elements in the hash */
+int lht_tree_hash_len(lht_node_t *list);
+
+/* del/detach node from a hash (by ptr) */
+lht_err_t lht_tree_hash_del_child(lht_node_t *hash, lht_node_t *node);
+lht_err_t lht_tree_hash_detach_child(lht_node_t *hash, lht_node_t *node);
+
+/* replace node with (already detached) newn - they may have different names */
+lht_err_t lht_tree_hash_replace_child(lht_node_t *hash, lht_node_t *node, lht_node_t *newn);
+
+
+/* --- TABLE --- */
+/* all row and col arguments count from 0 */
+
+/* insert a row before base_row; base_row may be (number-of-rows) to
+   append at the end of the table. Cells are anon empty strings;
+   returns LHTE_SUCCESS, LHTE_BOUNDARY or LHTE_OUT_OF_MEM */
+lht_err_t lht_tree_table_ins_row(lht_node_t *table, int base_row);
+
+
+/* insert a column before base_col; base_col may be (number-of-cols) to
+   append at the end of the table. Cells are anon empty strings;
+   returns LHTE_SUCCESS, LHTE_BOUNDARY or LHTE_OUT_OF_MEM */
+lht_err_t lht_tree_table_ins_col(lht_node_t *table, int base_col);
+
+/* delete the rth row of a table (freeing up all cells of that row);
+   return LHTE_SUCCESS or LHTE_BOUNDARY */
+lht_err_t lht_tree_table_del_row(lht_node_t *table, int r);
+
+/* delete the cth col of a table (freeing up all cells of that col);
+   returns LHTE_SUCCESS or LHTE_BOUNDARY */
+lht_err_t lht_tree_table_del_col(lht_node_t *table, int c);
+
+/* linear search for node in table; returns 1 if found, 0 if not found. When
+   found row and col are set. */
+int lht_tree_table_find_cell(lht_node_t *table, lht_node_t *node, int *row, int *col);
+
+/* return the cell, that matches:
+   - row_name != NULL: (row_num)th row that matches name row_name
+   - row_name == NULL: (row_num)th row
+   and
+   - col_name != NULL: (col_num)th col that matches name col_name
+   - col_name == NULL: (col_num)th col
+   Return NULL if not found
+*/
+lht_node_t *lht_tree_table_named_cell(const lht_node_t *table, const char *row_name, int row_num, const char *col_name, int col_num);
+
+/* detach a cell from a table by replacing it with the anonymous empty text node */
+lht_node_t *lht_tree_table_detach_cell(lht_node_t *table, int row, int col, lht_err_t *err);
+lht_err_t lht_tree_table_detach_child(lht_node_t *table, lht_node_t *node);
+
+
+/* low level row allocator: leaves the cells and row-name uninitialized */
+lht_err_t lht_tree_table_ins_row_(lht_node_t *table, int base_row);
+
+/* low level child replace; newn should be detached */
+lht_err_t lht_tree_table_replace_child(lht_node_t *table, lht_node_t *existing, lht_node_t *newn);
+lht_node_t *lht_tree_table_replace_cell(lht_node_t *table, int row, int col, lht_err_t *err, lht_node_t *newn);
+
+
+#endif
diff --git a/src_3rd/liblihata/tree_hash.c b/src_3rd/liblihata/tree_hash.c
new file mode 100644
index 0000000..51f90e4
--- /dev/null
+++ b/src_3rd/liblihata/tree_hash.c
@@ -0,0 +1,73 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains advanced tree walk helpers for hashes.
+*/
+#include <stdlib.h>
+#include <assert.h>
+#include "dom.h"
+#include "dom_internal.h"
+#include "tree.h"
+
+lht_err_t lht_tree_hash_detach_child(lht_node_t *parent, lht_node_t *node)
+{
+	lht_node_t *old;
+
+	if (node->parent != parent) {
+/*#warning TODO: this is a new error?*/
+/*#warning TODO: this is a new error?*/
+	}
+
+	old = htsp_pop(parent->data.hash.tbl, node->name);
+	assert(node == old);
+	node->parent = NULL;
+	lht_dom_loc_detach(node);
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_tree_hash_del_child(lht_node_t *parent, lht_node_t *node)
+{
+	lht_err_t err;
+	err = lht_tree_hash_detach_child(parent, node);
+	if (err == LHTE_SUCCESS)
+		lht_dom_node_free(node);
+	return err;
+}
+
+int lht_tree_hash_len(lht_node_t *list)
+{
+	return htsp_length(list->data.hash.tbl);
+}
+
+lht_err_t lht_tree_hash_replace_child(lht_node_t *hash, lht_node_t *node, lht_node_t *newn)
+{
+	lht_err_t ret;
+
+	if ((newn != NULL) && (newn->parent != NULL))
+		return LHTE_NOT_DETACHED;
+
+	ret = lht_tree_unlink(node);
+
+	if (ret != LHTE_SUCCESS)
+		return ret;
+	return lht_dom_hash_put(hash, newn);
+}
diff --git a/src_3rd/liblihata/tree_list.c b/src_3rd/liblihata/tree_list.c
new file mode 100644
index 0000000..8eb982f
--- /dev/null
+++ b/src_3rd/liblihata/tree_list.c
@@ -0,0 +1,205 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Gabor Horvath (HvG)
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains advanced tree walk helpers for lists.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "dom.h"
+#include "dom_internal.h"
+#include "tree.h"
+
+lht_node_t *lht_tree_list_nth(const lht_node_t *list, int n)
+{
+	const lht_node_t *result = NULL;
+
+	assert(list->type == LHT_LIST);
+	if (n < 0)
+		return NULL;
+
+	for (result = list->data.list.first; result != NULL && n > 0; result = result->next)
+		n--;
+
+	return (lht_node_t *)result;
+}
+
+
+lht_node_t *lht_tree_list_nthname(const lht_node_t *list, int n, const char *name)
+{
+	const lht_node_t *result = NULL;
+
+	assert(list->type == LHT_LIST);
+	if (n < 0)
+		return NULL;
+
+	for (result = list->data.list.first; result != NULL; result = result->next) {
+		if (strcmp(result->name, name) == 0) {
+			n--;
+			if (n < 0)
+				break;
+		}
+	}
+
+	return (lht_node_t *)result;
+}
+
+static void list_replace(lht_node_t *list, lht_node_t *node, lht_node_t *prev, lht_node_t *newn, int unlink)
+{
+	if (newn == NULL) {
+		/* detach: remove from the list */
+		if (node == list->data.list.first)
+			list->data.list.first = node->next;
+		if (node == list->data.list.last)
+			list->data.list.last = prev;
+
+		if (prev != NULL)
+			prev->next = node->next;
+	}
+	else {
+		/* replace */
+		if (node == list->data.list.first)
+			list->data.list.first = newn;
+		if (node == list->data.list.last)
+			list->data.list.last = newn;
+
+		if (prev != NULL)
+			prev->next = newn;
+
+		newn->next = node->next;
+		newn->parent = node->parent;
+	}
+
+	node->next = NULL;
+	node->parent = NULL;
+	if (unlink) {
+		lht_dom_list_append(node->doc->unlinked, node);
+		node->parent = node->doc->unlinked;
+	}
+	else
+		lht_dom_loc_detach(node);
+}
+
+lht_node_t *lht_tree_list_detach_nth(lht_node_t *list, int n)
+{
+	lht_node_t *node = NULL, *prev = NULL;
+
+	assert(list->type == LHT_LIST);
+	if (n < 0)
+		return NULL;
+
+	for (node = list->data.list.first; (node != NULL) && (n > 0); prev = node, node = node->next)
+		n--;
+
+	if ((n > 0) || (node == NULL))
+		return NULL;
+
+	list_replace(list, node, prev, NULL, 0);
+
+	return node;
+}
+
+/* delete the nth element of a list (slow); returns 0 on success */
+lht_err_t lht_tree_list_del_nth(lht_node_t *list, int n)
+{
+	lht_node_t *node = lht_tree_list_detach_nth(list, n);
+
+	if (node == NULL)
+		return LHTE_BOUNDARY;
+
+	/* remove the whole detached document */
+	lht_dom_uninit(node->doc);
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_tree_list_replace_child_(lht_node_t *list, lht_node_t *node, lht_node_t *newn, int unlink)
+{
+	lht_node_t *tmp = NULL;
+	lht_node_t *prev = NULL;
+
+	assert(list->type == LHT_LIST);
+
+	if ((newn != NULL) && (newn->parent != NULL))
+		return LHTE_NOT_DETACHED;
+
+	if ((newn != NULL) && (newn->doc != list->doc))
+		lht_dom_loc_move(list->doc, newn);
+
+	for (tmp = list->data.list.first; tmp != NULL; prev = tmp, tmp = tmp->next)
+		if (tmp == node)
+			break;
+
+	if (tmp == NULL) {
+		if (prev == NULL)
+			return LHTE_BOUNDARY;
+		else if (node == NULL)
+			return LHTE_BOUNDARY;
+
+		return LHTE_NOT_FOUND;
+	}
+
+	list_replace(list, node, prev, newn, unlink);
+
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_tree_list_replace_child(lht_node_t *list, lht_node_t *node, lht_node_t *newn)
+{
+	return lht_tree_list_replace_child_(list, node, newn, 1);
+}
+
+lht_err_t lht_tree_list_detach_child(lht_node_t *list, lht_node_t *node)
+{
+	return lht_tree_list_replace_child_(list, node, NULL, 0);
+}
+
+/* delete the node from a list (slow); returns 0 on success */
+lht_err_t lht_tree_list_del_child(lht_node_t *list, lht_node_t *node)
+{
+	lht_err_t ret;
+
+	ret = lht_tree_list_detach_child(list, node);
+	if (ret == LHTE_SUCCESS) {
+		/* remove the whole detached document */
+		lht_dom_uninit(node->doc);
+	}
+	return ret;
+}
+
+/* linear search for node in a list; returns index if found, -1 if not found. */
+int lht_tree_list_find_node(lht_node_t *list, lht_node_t *node)
+{
+	int idx = 0;
+	lht_node_t *tmp;
+
+	assert(list->type == LHT_LIST);
+
+	if (list->type == LHT_LIST) {
+		for (tmp = list->data.list.first; tmp != NULL; tmp = tmp->next) {
+			if (tmp == node)
+				return idx;
+			idx++;
+		}
+	}
+	return -1;
+}
diff --git a/src_3rd/liblihata/tree_path.c b/src_3rd/liblihata/tree_path.c
new file mode 100644
index 0000000..92999bc
--- /dev/null
+++ b/src_3rd/liblihata/tree_path.c
@@ -0,0 +1,325 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains path helpers (tree descend).
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "liblihata/dom.h"
+#include "liblihata/tree.h"
+
+#define LHT_SYMLINK_MAX_RECURSION 64
+
+/* return the value of a symlink; if it's the empty string, return "." instead */
+#define SYMLINK_VAL(_sy_) (*(_sy_)->data.symlink.value == '\0' ? "." : (_sy_)->data.symlink.value)
+
+#define path_err(_err_, code) \
+	do { \
+		if (_err_ != NULL) \
+			*_err_ = (code); \
+		return NULL; \
+	} while(0) \
+
+#define reset_err(_err_) \
+	do { \
+		if (_err_ != NULL) \
+			*_err_ = LHTE_SUCCESS; \
+	} while(0) \
+
+/* find the first sep in str (like strchr), but ignore those seps
+   that are backslash protected */
+static char *find_sep(char *str, char sep)
+{
+	char *s;
+	if (*str == sep)
+		return str;
+	if (*str == '\0')
+		return NULL;
+	for(s = str+1; *s != '\0'; s++) {
+		if ((s[-1] != '\\') && (s[0] == sep))
+			return s;
+	}
+	return NULL;
+}
+
+/*
+ * Used to separate name:snum
+ * Input: char* _str_ (which is modified to have ':' replace with '\0'
+ * Output: const char* _first_, _second_, pointing to the places in _str_.
+ * If there is no colon, _first_ is NULL.
+ */
+#define split_at_colon(_str_, _first_, _second_)	\
+	do { \
+		char *colon_pos; \
+    colon_pos = find_sep(_str_, ':'); \
+		if (colon_pos != NULL) { \
+			*colon_pos = '\0'; \
+			_second_ = colon_pos + 1;	\
+			_first_ = _str_; \
+			if (*_second_ == '\0') \
+				_second_ = "0";	\
+		}	\
+		else { \
+			_second_ = _str_; \
+			_first_ = NULL;	\
+		} \
+	} while(0)
+
+/* parse path segment "child" and return the child node of current
+   that matches (or NULL if not found) */
+static const lht_node_t *path_descend(const lht_node_t *current, char *child, lht_err_t *err, char **tbl, int follow_sy, int *depth)
+{
+	const char *name, *snum;
+	char *end;
+	int num;
+
+	switch(current->type) {
+		case LHT_INVALID_TYPE:
+			path_err(err, LHTE_PATH_INVALID_NODE);
+		case LHT_TEXT:
+			path_err(err, LHTE_PATH_CHILD_OF_TEXT);
+		case LHT_SYMLINK:
+			/* resolve symlink into current */
+			(*depth)++;
+			current = lht_tree_path_(current->doc, current->parent, SYMLINK_VAL(current), follow_sy, *depth, err);
+			if (current == NULL)
+				return NULL;
+			/* go on searching for child under the new node */
+			return path_descend(current, child, err, tbl, follow_sy, depth);
+		case LHT_LIST:
+			split_at_colon(child, name, snum);
+			num = strtol(snum, &end, 10);
+			if (*end != '\0')
+				path_err(err, LHTE_PATH_INT);
+			if (name == NULL)
+				return lht_tree_list_nth(current, num);
+			return lht_tree_list_nthname(current, num, name);
+		case LHT_HASH:
+			return lht_dom_hash_get(current, child);
+		case LHT_TABLE:
+			/* tables are shifted in two phases because of the separator between row and col is the same as the path separator */
+			if (*tbl == NULL) {
+				*tbl = child;
+				return current;
+			}
+			else {
+				const char *rname, *srnum, *cname, *scnum;
+				int rnum, cnum;
+
+				split_at_colon(*tbl, rname, srnum);
+				rnum = strtol(srnum, &end, 10);
+				if (*end != '\0')
+					path_err(err, LHTE_PATH_INT);
+
+				split_at_colon(child, cname, scnum);
+				cnum = strtol(scnum, &end, 10);
+				if (*end != '\0')
+					path_err(err, LHTE_PATH_INT);
+
+				*tbl = NULL;
+				return lht_tree_table_named_cell(current, rname, rnum, cname, cnum);
+			}
+	}
+	path_err(err, LHTE_PATH_INVALID_NODE);
+}
+
+/* split up path and follow each segment - return the node it led to */
+static const lht_node_t *path_follow(const lht_node_t *from, char *path, lht_err_t *err, int follow_sy, int *depth)
+{
+	char *next;
+	char *tbl = NULL;
+
+	do {
+		next = find_sep(path, '/');
+		if (next != NULL) {
+			*next = '\0';
+			next++;
+		}
+		if (*path != '\0') {
+			if (strcmp(path, ".") == 0) {
+				if (from->parent != NULL)
+					from = from->parent;
+			}
+			if (strcmp(path, "..") == 0) {
+				if (from->parent != NULL)
+					from = from->parent;
+				if (from->parent != NULL)
+					from = from->parent;
+			}
+			else  {
+				from = path_descend(from, path, err, &tbl, follow_sy, depth);
+				if ((from == NULL) && (tbl == NULL)) /* child not found */
+						path_err(err, LHTE_PATH_NOT_FOUND);
+			}
+			/* else do nothing: . is the current node in from */
+		}
+		path = next;
+	} while(next != NULL);
+	if (tbl != NULL)
+		path_err(err, LHTE_PATH_UNDERSPEC_TABLE);
+	return from;
+}
+
+lht_node_t *lht_tree_path_(lht_doc_t *doc, const lht_node_t *cwd, const char *path_, int follow_sy, int depth, lht_err_t *err)
+{
+	const lht_node_t *current;
+	char *path = lht_strdup(path_);
+
+	reset_err(err);
+
+	if (*path == '/')
+		current = path_follow(doc->root, path+1, err, follow_sy, &depth); /* absolute path */
+	else
+		current = path_follow(cwd, path, err, follow_sy, &depth);   /* relative path (to cwd) */
+
+	free(path);
+
+	/* follow symlink if required */
+	if ((follow_sy) && (current != NULL) && (current->type == LHT_SYMLINK)) {
+		if (depth >= LHT_SYMLINK_MAX_RECURSION)
+				path_err(err, LHTE_PATH_SYMLINK_TOO_DEEP);
+		return lht_tree_path_(doc, current->parent, SYMLINK_VAL(current), follow_sy, depth+1, err);
+	}
+
+	return (lht_node_t *)current;
+}
+
+lht_node_t *lht_tree_path(lht_doc_t *doc, const char *cwd_, const char *path, int follow_sy, lht_err_t *err)
+{
+	const lht_node_t *cwd_node;
+
+	reset_err(err);
+
+	if (*path != '/') {
+		char *cwd;
+		int depth = 0;
+
+		if (cwd_ == NULL)
+			path_err(err, LHTE_PATH_UNKNOWN_CWD);
+		/* relative to cwd */
+		if (*cwd_ == '/')
+			cwd_++;
+		cwd = lht_strdup(cwd_);
+
+		cwd_node = path_follow(doc->root, cwd, err, follow_sy, &depth);
+		free(cwd);
+		if (cwd_node == NULL)
+			path_err(err, LHTE_PATH_NOT_FOUND);
+	}
+	else
+		cwd_node = NULL;
+
+	return lht_tree_path_(doc, cwd_node, path, follow_sy, 0, err);
+}
+
+char *lht_tree_path_build(lht_node_t *cwd, lht_node_t *node, lht_err_t *err)
+{
+	lht_node_t *start, *tmp;
+	int len = 0; /* no need to allocate for the terminating '\0': it will be in place of the last "/" */
+	char *path, *path_start;
+	char buff[64];
+	int row, col;
+
+	if ((cwd != NULL) && (cwd == node))
+		return lht_strdup(".");
+
+	if ((cwd == NULL) && (node->parent == NULL))
+		return lht_strdup("/");
+
+	start = cwd != NULL ? cwd : node->doc->root;
+
+	for (tmp = node; tmp != NULL && tmp != start; tmp = tmp->parent) {
+		switch (tmp->parent->type) {
+			case LHT_TEXT:
+			case LHT_HASH:
+			case LHT_SYMLINK:
+				len += strlen(tmp->name) + 1; /* this last is for '/' */
+				break;
+			case LHT_LIST:
+				sprintf(buff, "%d", lht_tree_list_find_node(tmp->parent, tmp));
+				len += strlen(buff) + 1;
+				break;
+			case LHT_TABLE:
+				lht_tree_table_find_cell(tmp->parent, tmp, &row, &col);
+				sprintf(buff, "%d/%d", row, col);
+				len += strlen(buff) + 1;
+				break;
+			case LHT_INVALID_TYPE:
+				if (err != NULL)
+					*err = LHTE_BROKEN_DOC;
+				return NULL;
+				break;
+		}
+	}
+
+	if (tmp == NULL)
+		return NULL; /* not found as child of cwd */
+	else if (tmp->parent == NULL)
+		len++; /* final '\0' for absolute path */
+
+	path_start = path = malloc(len);
+	path += len - 1;
+	*path = 0;
+	for (tmp = node; (tmp->parent != NULL) && (tmp != cwd); tmp = tmp->parent) {
+		switch (tmp->parent->type) {
+			case LHT_TEXT:
+			case LHT_HASH:
+			case LHT_SYMLINK:
+				len = strlen(tmp->name);
+				path -= len;
+				memcpy(path, tmp->name, len);
+				break;
+			case LHT_LIST:
+				sprintf(buff, "%d", lht_tree_list_find_node(tmp->parent, tmp));
+				len = strlen(buff);
+				path -= len;
+				memcpy(path, buff, len);
+				break;
+			case LHT_TABLE:
+				lht_tree_table_find_cell(tmp->parent, tmp, &row, &col);
+				sprintf(buff, "%d/%d", row, col);
+				len = strlen(buff);
+				path -= len;
+				memcpy(path, buff, len);
+				break;
+			case LHT_INVALID_TYPE:
+				if (err != NULL)
+					*err = LHTE_BROKEN_DOC;
+				return NULL;
+				break;
+		}
+		if (tmp == start)
+			break;
+		path--;
+		*path = '/';
+	}
+
+/* we must have reached the beginning of the string */
+	assert(path == path_start);
+
+	if (err != NULL)
+		*err = LHTE_SUCCESS;
+
+	return path_start;
+}
diff --git a/src_3rd/liblihata/tree_symlink.c b/src_3rd/liblihata/tree_symlink.c
new file mode 100644
index 0000000..187b8cb
--- /dev/null
+++ b/src_3rd/liblihata/tree_symlink.c
@@ -0,0 +1,82 @@
+/*
+    liblihata - list/hash/table format, symlink helpers
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains non-transparent symlink helpers.
+*/
+#include <stdlib.h>
+#include "liblihata/dom.h"
+#include "liblihata/tree.h"
+
+int lht_tree_symlink_is_broken(lht_node_t *cwd, lht_node_t *sy)
+{
+	lht_doc_t *doc;
+	lht_node_t *result;
+
+	if (sy->type != LHT_SYMLINK)
+		return -1;
+
+	doc = cwd->doc != NULL ? cwd->doc : sy->doc;
+	result = lht_tree_path_(doc, cwd, sy->data.text.value, 1, 0, NULL);
+
+	if (result == NULL)
+		return 1;
+
+	cwd = result;
+	return 0;
+}
+
+int lht_tree_has_symlink_(lht_node_t *n, int chk_broken)
+{
+	if (n->type == LHT_SYMLINK) {
+		/* if we need to check for symlink only, return when the first is found */
+		if (chk_broken == 0)
+			return 1;
+
+		/* else we need to test if it is broken - return on the first broken symlink */
+		if (lht_tree_symlink_is_broken(n->parent, n))
+			return 1;
+	}
+	return 0;
+}
+
+int lht_tree_has_symlink(lht_node_t *tree, int chk_broken)
+{
+	lht_dom_iterator_t it;
+	lht_node_t *n;
+
+	if (lht_tree_has_symlink_(tree, chk_broken))
+		return 1;
+
+	/* a depth-first-search for symlinks or broken symlinks */
+	for(n = lht_dom_first(&it, tree); n != NULL; n = lht_dom_next(&it)) {
+
+		if (lht_tree_has_symlink_(n, chk_broken))
+			return 1;
+
+		/* if we are still here, we need to descend */
+		if ((n->type != LHT_TEXT) && (lht_tree_has_symlink(n, chk_broken) != 0))
+				return 1;
+	}
+
+	/* finished the DFS without any hit */
+	return 0;
+}
diff --git a/src_3rd/liblihata/tree_table.c b/src_3rd/liblihata/tree_table.c
new file mode 100644
index 0000000..ada76f9
--- /dev/null
+++ b/src_3rd/liblihata/tree_table.c
@@ -0,0 +1,343 @@
+/*
+    liblihata - list/hash/table format, parser lib
+    Copyright (C) 2013  Gabor Horvath (HvG)
+    Copyright (C) 2013  Tibor 'Igor2' Palinkas
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file contains advanced tree walk helpers for tables.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "dom.h"
+#include "dom_internal.h"
+#include "tree.h"
+
+static lht_node_t *lht_tree_emptyanon_text(lht_node_t *parent)
+{
+	lht_node_t *result;
+	assert(parent->type == LHT_TABLE);
+
+	result = lht_dom_node_alloc(LHT_TEXT, "");
+	if (result != NULL) {
+		result->parent = parent;
+		result->doc = parent->doc;
+	}
+	return result;
+}
+
+lht_err_t lht_tree_table_ins_row_(lht_node_t *table, int base_row)
+{
+	lht_table_t *t;
+	lht_err_t err;
+
+	assert(table->type == LHT_TABLE);
+	t = &table->data.table;
+
+	if ((base_row < 0) || (base_row > t->rows))
+		return LHTE_BOUNDARY;
+
+	if ((t->rows == 0) && (t->cols == 0)) {
+		err = lht_dom_table_grow_row(table);
+		if (err != LHTE_SUCCESS)
+			return err;
+		err = lht_dom_table_grow_col(table);
+		if (err != LHTE_SUCCESS)
+			return err;
+	}
+
+	if (t->rows == t->rows_alloced) {
+		err = lht_dom_table_grow_row(table);
+		if (err != LHTE_SUCCESS)
+			return err;
+		if (t->cols == 0) {
+			err = lht_dom_table_grow_col(table);
+			if (err != LHTE_SUCCESS)
+				return err;
+		}
+	}
+
+	memmove(t->row_names+base_row+1, t->row_names+base_row, (t->rows - base_row) * sizeof(char *));
+	memmove(t->r+base_row+1, t->r+base_row, (t->rows - base_row) * sizeof(lht_node_t **));
+
+	t->r[base_row] = malloc(t->cols * sizeof(lht_node_t *));
+	if (t->r[base_row] == NULL)
+		return LHTE_OUT_OF_MEM;
+
+	t->rows++;
+
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_tree_table_ins_row(lht_node_t *table, int base_row)
+{
+	int ret, j;
+	lht_table_t *t;
+
+	ret = lht_tree_table_ins_row_(table, base_row);
+	if (ret != LHTE_SUCCESS)
+		return ret;
+
+	t = &table->data.table;
+
+	t->row_names[base_row] = lht_strdup("");
+	for (j = 0; j < t->cols; j++)
+		t->r[base_row][j] = lht_tree_emptyanon_text(table);
+	return LHTE_SUCCESS;
+}
+
+lht_err_t lht_tree_table_ins_col(lht_node_t *table, int base_col)
+{
+	lht_table_t *t;
+	int i;
+	lht_err_t err;
+
+	assert(table->type == LHT_TABLE);
+	t = &table->data.table;
+
+	if ((base_col < 0) || (base_col > t->cols))
+		return LHTE_BOUNDARY;
+
+	if (t->cols == t->cols_alloced) {
+		if (t->rows == 0) {
+			err = lht_dom_table_grow_row(table);
+			if (err != LHTE_SUCCESS)
+				return err;
+			err = lht_dom_table_grow_col(table);
+			if (err != LHTE_SUCCESS)
+				return err;
+		}
+	}
+
+#warning TODO: why do we have col grow implementation here? Cannot grow_col() handle this? If this one is resolved, it may be that GROW_ macros can be moved back to dom_table.c and #include "dom_internal.h" can be removed from this file.
+	if (t->cols == t->cols_alloced) {
+		t->cols_alloced += LHT_TABLE_GROW_COLS;
+		for (i = 0; i < t->rows; i++) {
+			t->r[i] = realloc(t->r[i], t->cols_alloced * sizeof(lht_node_t *));
+			if (t->r[i] == NULL) {
+				t->cols_alloced -= LHT_TABLE_GROW_COLS;
+				return LHTE_OUT_OF_MEM;
+			}
+		}
+	}
+	t->cols++;
+
+	for (i = 0; i < t->rows; i++) {
+		memmove(t->r[i] + base_col + 1, t->r[i] + base_col, (t->cols - base_col) * sizeof(lht_node_t *));
+		t->r[i][base_col] = lht_tree_emptyanon_text(table);
+	}
+
+	return LHTE_SUCCESS;
+}
+
+
+/* delete the rth row of a table (freeing up all cells of that row) */
+lht_err_t lht_tree_table_del_row(lht_node_t *table, int r)
+{
+	lht_table_t *t;
+	int j;
+
+	assert(table->type == LHT_TABLE);
+	t = &table->data.table;
+
+	if ((r < 0) || (r >= t->rows))
+		return LHTE_BOUNDARY;
+
+	free(t->row_names[r]);
+	t->row_names[r] = NULL;
+	memmove(t->row_names+r, t->row_names+r+1, (t->rows - r - 1) * sizeof(char *));
+
+	for (j = 0; j < t->cols; j++) {
+		lht_dom_node_free(t->r[r][j]);
+		t->r[r][j] = NULL;
+	}
+	free(t->r[r]);
+	t->r[r] = NULL;
+	memmove(t->r+r, t->r+r+1, (t->rows - r - 1) * sizeof(lht_node_t **));
+
+	t->rows--;
+
+	return LHTE_SUCCESS;
+}
+
+/* delete the cth col of a table (freeing up all cells of that col) */
+lht_err_t lht_tree_table_del_col(lht_node_t *table, int c)
+{
+	lht_table_t *t;
+	int i;
+
+	assert(table->type == LHT_TABLE);
+	t = &table->data.table;
+
+	if ((c < 0) || (c >= t->cols))
+		return LHTE_BOUNDARY;
+
+	for (i = 0; i < t->rows; i++) {
+		lht_dom_node_free(t->r[i][c]);
+		t->r[i][c] = NULL;
+		memmove(&(t->r[i][c]), &(t->r[i][c+1]), (t->cols - c - 1) * sizeof(lht_node_t *));
+	}
+
+	t->cols--;
+
+	return LHTE_SUCCESS;
+}
+
+int lht_tree_table_find_cell(lht_node_t *table, lht_node_t *node, int *row, int *col)
+{
+	int r, c;
+	lht_table_t *t = &(table->data.table);
+
+	assert(table->type == LHT_TABLE);
+
+	for(r = 0; r < t->rows; r++) {
+		lht_node_t **rw = t->r[r];
+		for(c = 0; c < t->cols; c++) {
+			if (rw[c] == node) {
+				if (row != NULL) *row = r;
+				if (col != NULL) *col = c;
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+lht_node_t *lht_tree_table_named_cell(const lht_node_t *table, const char *row_name, int row_num, const char *col_name, int col_num)
+{
+	int n;
+	lht_node_t * const *row;
+	const lht_table_t *t = &(table->data.table);
+
+	assert(table->type == LHT_TABLE);
+
+	if (row_name == NULL) {
+		/* no name is given: go the cheap way and retrieve the row pointer by idx */
+		if ((row_num < 0) || (row_num >= t->rows))
+			return NULL;
+		row = t->r[row_num];
+	}
+	else {
+		/* name and num: count matching names */
+		row = NULL;
+		for(n = 0; n < t->rows; n++) {
+			if (strcmp(t->row_names[n], row_name) == 0) {
+				if (row_num == 0) {
+					row = t->r[n];
+					break;
+				}
+				row_num--;
+			}
+		}
+		if (row == NULL)
+			return NULL;
+	}
+
+	/* we have a row pointer - check for the col */
+	if (col_name == NULL) {
+		/* no name is given: go the cheap way and retrieve the cell pointer by idx */
+		if ((col_num < 0) || (col_num >= t->cols))
+			return NULL;
+		return (lht_node_t *)row[col_num];
+	}
+
+	/* name and num: count matching names */
+	for(n = 0; n < t->cols; n++) {
+		if (strcmp(row[n]->name, col_name) == 0) {
+			if (col_num == 0)
+				return (lht_node_t *)row[n];
+			col_num--;
+		}
+	}
+
+	/* row found, name:num col not found */
+	return NULL;
+}
+
+lht_node_t *lht_tree_table_replace_cell_(lht_node_t *table, int row, int col, lht_err_t *err, lht_node_t *newn, int unlink)
+{
+	lht_node_t *ret;
+
+	if ((row < 0) || (row >= table->data.table.rows) || (col < 0) || (col >= table->data.table.cols)) {
+		if (err != NULL)
+			*err = LHTE_BOUNDARY;
+		return NULL;
+	}
+	ret = table->data.table.r[row][col];
+
+	if (newn == NULL) {
+		table->data.table.r[row][col] = lht_tree_emptyanon_text(table);
+	}
+	else {
+		if (newn->parent != NULL) {
+			if (err != NULL)
+					*err = LHTE_NOT_DETACHED;
+			return NULL;
+		}
+		newn->parent = table;
+		if (newn->doc != table->doc)
+			lht_dom_loc_move(table->doc, newn);
+
+		table->data.table.r[row][col] = newn;
+	}
+
+	ret->parent = NULL;
+	if (unlink) {
+		/* manual unlink - ret is safely an ex-child of the table */
+		lht_dom_list_append(ret->doc->unlinked, ret);
+		ret->parent = ret->doc->unlinked;
+	}
+	else {
+		lht_dom_loc_detach(ret);
+	}
+
+	if (err != NULL)
+		*err = LHTE_SUCCESS;
+	return ret;
+}
+
+lht_node_t *lht_tree_table_replace_cell(lht_node_t *table, int row, int col, lht_err_t *err, lht_node_t *newn)
+{
+	return lht_tree_table_replace_cell_(table, row, col, err, newn, 1);
+}
+
+lht_node_t *lht_tree_table_detach_cell(lht_node_t *table, int row, int col, lht_err_t *err)
+{
+	return lht_tree_table_replace_cell_(table, row, col, err, NULL, 0);
+}
+
+
+lht_err_t lht_tree_table_replace_child(lht_node_t *table, lht_node_t *existing, lht_node_t *newn)
+{
+	int row, col;
+	lht_err_t err;
+
+	if (lht_tree_table_find_cell(table, existing, &row, &col) == 1) {
+		lht_tree_table_replace_cell_(table, row, col, &err, newn, 1);
+		return err;
+	}
+	return LHTE_NOT_FOUND;
+}
+
+
+lht_err_t lht_tree_table_detach_child(lht_node_t *table, lht_node_t *node)
+{
+	return lht_tree_table_replace_child(table, node, NULL);
+}
diff --git a/src_3rd/qparse/Makefile b/src_3rd/qparse/Makefile
new file mode 100644
index 0000000..7951293
--- /dev/null
+++ b/src_3rd/qparse/Makefile
@@ -0,0 +1,10 @@
+#CFLAGS = -Wall -Wextra -g
+CFLAGS = -g
+
+example: example.o qparse.o
+
+qparse.o: qparse.c qparse.h
+	$(CC) -c qparse.c -o qparse.o
+
+clean:
+	rm qparse.o example example.o ; true
diff --git a/src_3rd/qparse/example.c b/src_3rd/qparse/example.c
new file mode 100644
index 0000000..19d5a20
--- /dev/null
+++ b/src_3rd/qparse/example.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <string.h>
+#include "qparse.h"
+
+/* Read lines of text from stdin and split them in fields using qparse. */
+
+int main()
+{
+	char s[1024];
+	while(fgets(s, sizeof(s), stdin) != NULL) {
+		int n, argc;
+		char *end, **argv;
+
+		/* remove trailing newline (if we don't we just get an extra empty field at the end) */
+		for(end = s + strlen(s) - 1; (end >= s) && ((*end == '\r') || (*end == '\n')); end--)
+			*end = '\0';
+
+		/* split and print fields */
+		printf("Splitting '%s':\n", s);
+		argc = qparse(s, &argv);
+		for(n = 0; n < argc; n++)
+			printf(" [%d] '%s'\n", n, argv[n]);
+		qparse_free(argc, &argv);
+	}
+	return 0;
+}
diff --git a/src_3rd/qparse/qparse.c b/src_3rd/qparse/qparse.c
new file mode 100644
index 0000000..c0d9895
--- /dev/null
+++ b/src_3rd/qparse/qparse.c
@@ -0,0 +1,142 @@
+/*
+    libgpmi - General Package/Module Interface - qparse package
+    Copyright (C) 2006-2007 Tibor 'Igor2' Palinkas
+  
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+  
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+  
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum qp_state_e {
+	qp_normal,
+	qp_dquote,
+} qp_state_t;
+
+#define qpush(chr) \
+	{ \
+	 if (buff_used >= buff_len) { \
+	 	buff_len += 128; \
+		buff = realloc(buff, buff_len); \
+	 } \
+	 buff[buff_used] = chr; \
+	 buff_used++; \
+	}
+
+#define qbackslash(chr) \
+	switch(chr) { \
+		case 'n': qpush('\n'); break; \
+		case 'r': qpush('\r'); break; \
+		case 't': qpush('\t'); break; \
+		case 'b': qpush('\b'); break; \
+		case '"': qpush('"'); break; \
+		case ' ': qpush(' '); break; \
+		case '\0': break; \
+		default: \
+			qpush('\\'); \
+			qpush(chr); \
+	}
+
+#define qnext() \
+	{ \
+		if (argc >= allocated) { \
+			allocated += 8; \
+			argv = realloc(argv, sizeof(char *) * allocated); \
+		} \
+		buff[buff_used] = '\0'; \
+		argv[argc] = strdup(buff); \
+		argc++; \
+		*buff = '\0'; \
+		buff_used = 0; \
+	}
+
+
+int qparse(const char *input, char **argv_ret[])
+{
+	int argc;
+	int allocated;
+	qp_state_t state;
+	const char *s;
+	char *buff;
+	int buff_len, buff_used;
+	char **argv;
+
+	argc      = 0;
+	allocated = 0;
+	argv      = NULL;
+	state     = qp_normal;
+
+	buff_len  = 128;
+	buff_used = 0;
+	buff      = malloc(buff_len);
+
+	for(s = input; *s != '\0'; s++) {
+		switch (state) {
+			case qp_normal:
+				switch (*s) {
+					case '\\':
+						s++;
+						qbackslash(*s);
+						break;
+					case '"':
+						state = qp_dquote;
+						break;
+					case ' ':
+					case '\t':
+					case '\n':
+					case '\r':
+						qnext();
+						break;
+					default:
+						qpush(*s);
+				}
+				/* End of qp_normal */
+				break;
+			case qp_dquote:
+				switch (*s) {
+					case '\\':
+						s++;
+						qbackslash(*s);
+						break;
+					case '"':
+						state = qp_normal;
+						break;
+					default:
+						qpush(*s);
+				}
+				/* End of qp_dquote */
+				break;
+		}
+	}
+
+	qnext();
+
+	if (buff != NULL)
+		free(buff);
+
+	*argv_ret = argv;
+	return argc;
+}
+
+void qparse_free(int argc, char **argv_ret[])
+{
+	int n;
+
+	for (n = 0; n < argc; n++)
+		free((*argv_ret)[n]);
+
+	free(*argv_ret);
+	*argv_ret = NULL;
+}
diff --git a/src_3rd/qparse/qparse.h b/src_3rd/qparse/qparse.h
new file mode 100644
index 0000000..21e65e3
--- /dev/null
+++ b/src_3rd/qparse/qparse.h
@@ -0,0 +1,9 @@
+/* Split input into a list of strings in argv_ret[] using whitepsace as field
+   separator. It supports double quoted fields and backslash as escape character.
+   Returns the number of arguments. argv_ret should be free'd using
+   qparse_free(). */
+int qparse(const char *input, char **argv_ret[]);
+
+/* Free an argv_ret array allocated by qparse. */
+void qparse_free(int argc, char **argv_ret[]);
+
diff --git a/src_3rd/sphash/Makefile b/src_3rd/sphash/Makefile
new file mode 100644
index 0000000..91d3633
--- /dev/null
+++ b/src_3rd/sphash/Makefile
@@ -0,0 +1,44 @@
+CFLAGS = -Wall -g -ansi -pedantic
+VERBOSE = -v
+
+test: test.o test_hash.o teste_hash.o testd_hash.o
+
+test_hash.c test_hash.h: sphash
+	./sphash $(VERBOSE) --prefix test --out test_hash < words
+
+teste_hash.c teste_hash.h: sphash
+	./sphash $(VERBOSE) --multi --prefix teste --out teste_hash < wenums
+
+testd_hash.c testd_hash.h: sphash
+	./sphash $(VERBOSE) --aux "int i; double d;" --prefix testd --out testd_hash < denum
+
+test_hash.o: test_hash.c test_hash.h
+	$(CC) -c $(CFLAGS) test_hash.c -o test_hash.o
+
+teste_hash.o: teste_hash.c teste_hash.h
+	$(CC) -c $(CFLAGS) teste_hash.c -o teste_hash.o
+
+test.o: test.c test_hash.h
+	$(CC) -c $(CFLAGS) test.c -o test.o
+
+sphash: sphash.c
+	$(CC) $(CFLAGS) $(LDFLAGS) sphash.c -o sphash
+
+install_: sphash
+	mkdir -p $(install_root)/usr/bin/
+	$(CP) `pwd`/sphash $(install_root)/usr/bin/sphash
+
+install:
+	make install_ CP=cp
+
+linstall:
+	make install_ CP="ln -s"
+
+uninstall:
+	rm $(install_root)/usr/bin/sphash
+
+deb:
+	fakeroot debian/rules binary
+
+clean:
+	rm -f *.o test_hash.c test_hash.h sphash test
diff --git a/src_3rd/sphash/TODO b/src_3rd/sphash/TODO
new file mode 100644
index 0000000..90a24ec
--- /dev/null
+++ b/src_3rd/sphash/TODO
@@ -0,0 +1,3 @@
+- no preifx shouldn't use test_ as prefix
+- documentation (manual page)
+- better regression testing
diff --git a/src_3rd/sphash/debian/changelog b/src_3rd/sphash/debian/changelog
new file mode 100644
index 0000000..6f275f8
--- /dev/null
+++ b/src_3rd/sphash/debian/changelog
@@ -0,0 +1,51 @@
+sphash (1.0.1-r33) unstable; urgency=low
+
+  * Fix: replace any non-alnum with _ in enum values
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Sat, 09 Mar 2013 07:28:27 +0100
+
+sphash (1.0.1-r31) unstable; urgency=low
+
+  * Add: is_valid functions are generated
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Sat, 01 Oct 2011 10:50:50 +0000
+
+sphash (1.0.1-r29) unstable; urgency=low
+
+  * Add: optional multi-enum feature
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Sat, 01 Oct 2011 10:25:25 +0000
+
+sphash (1.0.1-r22) unstable; urgency=low
+
+  * Add: keyname lookup
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Fri, 30 Sep 2011 10:38:37 +0000
+
+sphash (1.0.1-r20) unstable; urgency=low
+
+  * Fix: generate enum instead of defines
+  * Add: generate SPHASH_INVALID value and SPHASH_SIZE constant
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Fri, 30 Sep 2011 05:44:20 +0000
+
+sphash (1.0.1-r15) unstable; urgency=low
+
+  * Fix: don't generate entries for NULL
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Fri, 30 Sep 2011 05:23:03 +0000
+
+sphash (1.0.1-r11) unstable; urgency=low
+
+  * Add: add banner in generated files
+  * Add: -O sets overrun
+  * Fix: start experimenting with much smaller hashes
+
+ -- Tibor Palinkas <sphash at igor2.repo.hu>  Fri, 30 Sep 2011 05:05:13 +0000
+
+sphash (1.0.0-r9296) unstable; urgency=low
+
+  * Initial debian package
+
+ -- Tibor Palinkas <tpalinkas at allied-visions.de>  Wed, 29 Oct 2008 13:50:29 +0100
+
diff --git a/src_3rd/sphash/debian/control b/src_3rd/sphash/debian/control
new file mode 100644
index 0000000..3884fab
--- /dev/null
+++ b/src_3rd/sphash/debian/control
@@ -0,0 +1,12 @@
+Source: sphash
+Section: utils
+Priority: optional
+Maintainer: Tibor Palinkas <tpalinkas at allied-visions.de>
+
+Package: sphash
+Architecture: any
+Depends:
+Recommends: 
+Description: simple compile time hash
+ Calculate a collision-free hash table with the corresponding hash function
+ compile time for a set of commands.
diff --git a/src_3rd/sphash/debian/copyright b/src_3rd/sphash/debian/copyright
new file mode 100644
index 0000000..1f1cc0c
--- /dev/null
+++ b/src_3rd/sphash/debian/copyright
@@ -0,0 +1,3 @@
+All files are copyrighted by  (C) 2008, 2011, 2013, 2014, 2016 Tibor Palinkas
+and are licensed under the GPL version 2.0 or later.
+
diff --git a/src_3rd/sphash/debian/rules b/src_3rd/sphash/debian/rules
new file mode 100755
index 0000000..07d10ad
--- /dev/null
+++ b/src_3rd/sphash/debian/rules
@@ -0,0 +1,56 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# This file is public domain software, originally written by Joey Hess. 
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+pkg_base=sphash
+
+build:
+
+clean:
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp
+	# Add here commands to clean up after the build process.
+	$(MAKE) clean
+
+	dh_clean
+
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k
+	dh_installdirs
+
+	# Add here commands to install the package into debian/<packagename>
+	echo "Installing to `pwd`/debian/sphash"
+	install_root=`pwd`/debian/sphash prefix=/usr make install
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+	dh_testdir
+	dh_testroot
+	dh_installchangelogs
+	dh_installdocs
+	dh_installexamples
+
+	dh_install -p$(pkg_base) --sourcedir=debian/sphash usr
+
+	dh_link
+#	dh_strip
+	dh_compress
+	dh_fixperms
+	dh_installdeb
+	dh_shlibdeps
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb 
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/src_3rd/sphash/sphash.c b/src_3rd/sphash/sphash.c
new file mode 100644
index 0000000..9635241
--- /dev/null
+++ b/src_3rd/sphash/sphash.c
@@ -0,0 +1,584 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  sphash -- simple, (almost) perfect hash, generated compile time
+ *  Copyright (C) 2008, 2011, 2013, 2014, 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  THE ABOVE LICENSE DOES NOT AFFECT THE OUTPUT GENERATED RUNNING THE
+ *  PROGRAM: the output is the property of the person who ran the program.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdarg.h>
+
+char *aux_fields = NULL;
+char **data = NULL;
+char **aux_data = NULL;
+int *values  = NULL;
+int *got  = NULL;
+
+int data_num = 0;
+int hash_size;
+
+int overrun = 1000;
+int debug_level = 0;
+
+int SPHASH_SEED0 = 1;
+int SPHASH_SEED1 = 0;
+int SPHASH_SEED2 = 0;
+
+int SPHASH_SEED0_best = -1; /* to */
+int SPHASH_SEED1_best = -1; /* from */
+int SPHASH_SEED2_best = -1; /* seed */
+
+int shortest = 1000;
+
+int last;
+
+int multi = 0;
+
+#define sphash_function(acc, c, n)		acc += (acc << SPHASH_SEED2) + (c)
+char *sphash_function_str = "#define sphash_function(acc, c, n)		acc += (acc << SPHASH_SEED2) + (c)";
+
+int lprintf(int level, const char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	if (level > debug_level)
+		return 0;
+
+	va_start(ap, fmt);
+	ret = vprintf(fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+char *banner = "/* Generated by sphash - do not modify */\n";
+
+/* C89-portable strdup */
+static char *sphash_strdup(const char *s)
+{
+	char *r;
+	size_t len;
+
+	if (s == NULL)
+		return NULL;
+
+	len = strlen(s)+1;
+	r = malloc(len);
+	memcpy(r, s, len);
+	return r;
+}
+
+typedef struct multi_s multi_t;
+struct multi_s {
+	char *s;
+	char *s_id;
+	int value;
+	multi_t *next;
+	multi_t *children;
+};
+
+multi_t *multi_enums = NULL;
+
+multi_t *multi_alloc(const char *s, int value, multi_t **linkin)
+{
+	multi_t *m;
+	char *i;
+
+	m = malloc(sizeof(multi_t));
+	m->s = sphash_strdup(s);
+	m->s_id = sphash_strdup(s);
+	for(i = m->s_id; *i != '\0'; i++) {
+		if (!isalnum(*i)) {
+			*i = '_';
+		}
+	}
+	m->value = value;
+	m->children = NULL;
+	if (linkin != NULL) {
+		m->next = *linkin;
+		*linkin = m;
+	}
+	else
+		m->next = NULL;
+	return m;
+}
+
+int load_strings(FILE *f)
+{
+	int allocated;
+	char line[1024], *s;
+	int n;
+	multi_t *parent;
+	int enum_num, keys;
+
+	allocated = 0;
+	enum_num = keys = 0;
+
+	while(!(feof(f))) {
+		next:;
+		*line = '\0';
+		fgets(line, sizeof(line), f);
+		s = line;
+		if (*s != '\0') {
+			s[strlen(s)-1] = '\0';
+			/* "parse" multi-file */
+			if ((multi) && (*s != ' ') && (*s != '\t')) {
+				parent = multi_alloc(s, -2, &multi_enums);
+				enum_num++;
+				continue;
+			}
+			if (multi) {
+				while((*s == ' ') || (*s == '\t'))
+					s++;
+			}
+			
+			keys++;
+			/* don't add something twice */
+			for(n = 0; n < data_num; n++) {
+				if (strcmp(data[n], s) == 0) {
+					if (multi) /* for multi-enums we want duplicate items to show up in per-enum lists */
+						multi_alloc(s, n, &(parent->children));
+					goto next;
+				}
+			}
+	
+			/* grow and insert */
+			if (data_num >= allocated) {
+				allocated += 32;
+				data = realloc(data, sizeof(char *) * allocated);
+				if (aux_fields != NULL)
+					aux_data = realloc(aux_data, sizeof(char *) * allocated);
+			}
+
+			/* add in central array for hasing and in multi-enum */
+			data[data_num] = sphash_strdup(s);
+			if (aux_fields != NULL) {
+				char *end = strchr(data[data_num], '\t');
+				if (end != NULL) {
+					*end = '\0';
+					end++;
+					while(*end == '\t') end++;
+					aux_data[data_num] = end;
+				}
+				else
+					aux_data[data_num] = NULL;
+			}
+			if (multi)
+					multi_alloc(s, data_num, &(parent->children));
+			data_num++;
+		}
+	}
+	if (multi)
+		lprintf(1, "[load_strings] Read %d unique (%d total) keys for %d enums.\n", data_num, keys, enum_num);
+	else
+		lprintf(1, "[load_strings] Read %d unique (%d total) keys.\n", data_num, keys);
+	fclose(f);
+	return 0;
+}
+
+unsigned int sphash(char *str)
+{
+	unsigned int n, a;
+	char *s;
+
+	a = 0;
+	last = 0;
+	
+	for(n = 0, s = str; (*s != '\0') && (n<SPHASH_SEED1) ; n++,s++);
+	
+	for(; (*s != '\0') && (n<SPHASH_SEED0) ; n++,s++) {
+		sphash_function(a, *s, n);
+	}
+	return a;
+}
+
+/* Returns the number of matching strings */
+int hash_degree()
+{
+	unsigned int n, val, wrong;
+
+	wrong = 0;
+
+/*	for(n=0; n<hash_size; n++) {
+		values[n] = -1;
+		got[n] = 0;
+	}*/
+	memset(got, 0, sizeof(int) * hash_size);
+
+	for(n=0; n<data_num; n++) {
+		val = sphash(data[n]);
+		lprintf(4, " '%s' -> %d %d\n", data[n], val, val % hash_size);
+		val = val % hash_size;
+		values[n] = val;
+		if (got[val] != 0)
+			wrong++;
+		else
+			got[val]++;
+		if (wrong > 4)
+			break;
+	}
+
+	return wrong;
+}
+
+int optimize_hash_function()
+{
+	int degree, best, len, v;
+
+	/* Some sort of GP should be here instead of this (and more SEEDS): */
+best = 10000;
+for(SPHASH_SEED0 = 4; SPHASH_SEED0 < 64; SPHASH_SEED0+=2) {
+	v = SPHASH_SEED0 - shortest - 1;
+	if (v < 0)
+		v = 0;
+	for(SPHASH_SEED1 = SPHASH_SEED0 - 1; SPHASH_SEED1 >= v; SPHASH_SEED1--) {
+		for(SPHASH_SEED2 = 0; SPHASH_SEED2 < 8; SPHASH_SEED2++) {
+			degree = hash_degree();
+			lprintf(3, "%d %d %d hash_size=%d => %d\n", SPHASH_SEED0, SPHASH_SEED1, SPHASH_SEED2, hash_size, degree);
+			if (degree < best)
+				best = degree;
+			if (degree == 0) {
+				len = SPHASH_SEED0 - SPHASH_SEED1;
+				if (len < shortest) {
+					shortest = len;
+					SPHASH_SEED0_best = SPHASH_SEED0;
+					SPHASH_SEED1_best = SPHASH_SEED1;
+					SPHASH_SEED2_best = SPHASH_SEED2;
+				}
+			}
+		}
+	}
+}
+
+/*	if (best == 5)
+		best=12345;*/
+
+	return best;
+}
+
+#define minmax(v, min, max) \
+	do { \
+		if (v < min) \
+			min = v; \
+		if (v > max) \
+			max = v; \
+	} while(0)
+
+void dump_file(char *file_name_, char *prefix)
+{
+	FILE *f;
+	int n,m;
+	int l = strlen(file_name_);
+	multi_t *en, *item;
+	char *s, *file_name = malloc(l + 4), *file_name__;
+	memcpy(file_name, file_name_, l);
+	file_name[l+0] = '.';
+	file_name[l+1] = 'c';
+	file_name[l+2] = '\0';
+
+	f = fopen(file_name, "w");
+	assert(f != NULL);
+	fprintf(f, banner);
+	fprintf(f, "#include <stdlib.h>\n");
+	fprintf(f, "#include <string.h>\n");
+	if (aux_fields != NULL) {
+		file_name[l+1] = 'h';
+		fprintf(f, "#include \"%s\"\n", file_name);
+		file_name[l+1] = 'c';
+	}
+	fprintf(f, "\n");
+
+	fprintf(f, "#define SPHASH_SEED0 %d\n", SPHASH_SEED0);
+	fprintf(f, "#define SPHASH_SEED1 %d\n", SPHASH_SEED1);
+	fprintf(f, "#define SPHASH_SEED2 %d\n", SPHASH_SEED2);
+	fprintf(f, "%s\n\n", sphash_function_str);
+
+	fprintf(f, "char *sphash_%s_strings[] = {\n", prefix);
+	for(n=0; n<hash_size; n++) {
+		for(m=0; m<hash_size; m++) {
+			if ((m < data_num) && (values[m] == n)) {
+				fprintf(f, "	\"%s\", /* %d */\n", data[m], m);
+				goto ok;
+			}
+		}
+		fprintf(f, "	\"\",\n");
+		ok:;
+	}
+	fprintf(f, "	NULL\n};\n\n");
+
+	fprintf(f, "int sphash_%s_nums[] = {\n", prefix);
+	for(n=0; n<hash_size; n++) {
+		for(m=0; m<hash_size; m++) {
+			if ((m < data_num) &&(values[m] == n)) {
+				fprintf(f, "	%d,\n", m);
+				goto ok2;
+			}
+		}
+		fprintf(f, "	-1, \n");
+		ok2:;
+	}
+	fprintf(f, "	-1\n};\n\n");
+
+	fprintf(f, "int %s_sphash(const char *str)\n", prefix);
+	fprintf(f, "{\n");
+	fprintf(f, "	unsigned int n, a;\n");
+	fprintf(f, "	const char *s;\n");
+	fprintf(f, "\n");
+	fprintf(f, "	for(n = 0, s = str; (*s != '\\0') && (n<SPHASH_SEED1) ; n++,s++);");
+	fprintf(f, "	a = 0;\n");
+	fprintf(f, "	for(; (*s != '\\0') && (n<SPHASH_SEED0) ; n++,s++) {\n");
+	fprintf(f, "		sphash_function(a, *s, n);\n");
+	fprintf(f, "	}\n");
+	fprintf(f, "	a = a %% %d;\n", hash_size);
+	fprintf(f, "	if (strcmp(sphash_%s_strings[a], str) == 0)\n", prefix);
+	fprintf(f, "		return sphash_%s_nums[a];\n", prefix);
+	fprintf(f, "	return -1;\n");
+	fprintf(f, "}\n\n");
+
+
+	fprintf(f, "static int keynames[] = {\n");
+	for(m=0; m<data_num-1; m++) {
+		fprintf(f, "	% -5d, /* %s */\n", values[m], data[m]);
+	}
+	fprintf(f, "	% -5d  /* %s */\n};\n", values[m], data[m]);
+
+	if (aux_fields != NULL) {
+		fprintf(f, "\n%s_aux_t %s_aux[] = {\n", prefix, prefix);
+		for(m=0; m<data_num-1; m++)
+			fprintf(f, "	{%s},\n", aux_data[m]);
+		fprintf(f, "	{%s}\n", aux_data[m]);
+		fprintf(f, "};\n\n");
+	}
+
+	if (!multi) {
+		if (aux_fields == NULL)
+			fprintf(f, "typedef int %s_keys_t;\n", prefix);
+		fprintf(f, "const char *%s_keyname(%s_keys_t keyid)\n", prefix, prefix);
+	}
+	else
+		fprintf(f, "const char *%s_keyname(int keyid)\n", prefix);
+	fprintf(f, "{\n");
+	fprintf(f, "	if ((keyid < 0) || (keyid > %d))\n		return NULL;\n", data_num);
+	fprintf(f, "	return sphash_%s_strings[keynames[keyid]];\n", prefix);
+	fprintf(f, "}\n\n");
+
+	if (multi) {
+		for(en = multi_enums; en != NULL; en = en->next) {
+			fprintf(f, "int %s_%s_isvalid(int keyid)\n", prefix, en->s);
+			fprintf(f, "{\n");
+			item = en->children;
+			fprintf(f, "	return");
+			/* We can't expect linear range because of all the reuses - let the compiler optimize */
+			fprintf(f, " (keyid == %d)", item->value);
+			for(item = item->next; item != NULL; item = item->next)
+				fprintf(f, " || (keyid == %d)", item->value);
+			fprintf(f, ";\n");
+			fprintf(f, "}\n\n");
+		}
+	}
+
+	fprintf(f, "#undef SPHASH_SEED0\n");
+	fprintf(f, "#undef SPHASH_SEED1\n");
+	fprintf(f, "#undef SPHASH_SEED2\n");
+	fprintf(f, "#undef sphash_function\n");
+	fclose(f);
+
+	file_name[l+1] = 'h';
+	f = fopen(file_name, "w");
+	fprintf(f, banner);
+	file_name__ = sphash_strdup(file_name_);
+	for(s = file_name__; *s != '\0'; s++) {
+		if (((*s >= 'a') && (*s <= 'z')) || ((*s >= 'A') && (*s <= 'Z')) || ((*s >= '0') && (*s <= '9')))
+			continue;
+		*s = '_';
+	}
+	fprintf(f, "#ifndef SPHASH_%s_H\n", file_name__);
+	fprintf(f, "#define SPHASH_%s_H\n", file_name__);
+	free(file_name__);
+	fprintf(f, "int %s_sphash(const char *str);\n\n", prefix);
+	fprintf(f, "/* total number of keys: */\n");
+	fprintf(f, "#define %s_SPHASH_SIZE %d\n\n", prefix, data_num);
+
+	if (multi) {
+		fprintf(f, "/* universal invalid key: */\n");
+		fprintf(f, "#define %s_SPHASH_INVALID (-1)\n\n", prefix);
+		for(en = multi_enums; en != NULL; en = en->next) {
+			unsigned int min = -1, max = 0, numitems = 0;
+			fprintf(f, "typedef enum %s_%s_keys_e {\n", prefix, en->s);
+			for(item = en->children; item != NULL; item = item->next) {
+				fprintf(f, "	%s_%s_%s = %d,\n", prefix, en->s, item->s_id, item->value);
+				minmax(item->value, min, max);
+				numitems++;
+			}
+			fprintf(f, "	%s_%s_SPHASH_INVALID = -1\n", prefix, en->s);
+			fprintf(f, "} %s_%s_keys_t;\n\n", prefix, en->s);
+			fprintf(f, "typedef enum %s_%s_props_e {\n", prefix, en->s);
+			fprintf(f, "	%s_%s_SPHASH_MINVAL = %d,\n", prefix, en->s, min);
+			fprintf(f, "	%s_%s_SPHASH_MAXVAL = %d,\n", prefix, en->s, max);
+			fprintf(f, "	%s_%s_SPHASH_RANGE = %d,\n", prefix, en->s, max-min+1);
+			fprintf(f, "	%s_%s_SPHASH_NUMITEMS = %d\n", prefix, en->s, numitems);
+			fprintf(f, "} %s_%s_props_t;\n", prefix, en->s);
+			fprintf(f, "int %s_%s_isvalid(int keyid); /* returns 1 if keyid is in the above enum or 0 if not */\n", prefix, en->s);
+			fprintf(f, "#define %s_%s_keyid2idx(keyid)  ((keyid)-%d)\n", prefix, en->s, min);
+			fprintf(f, "#define %s_%s_idx2keyid(idx)    ((idx)+%d)\n\n\n", prefix, en->s, min);
+		}
+		fprintf(f, "\nconst char *%s_keyname(int keyid);\n", prefix);
+	}
+	else {
+		unsigned int min = -1, max = 0, numitems = 0;
+		fprintf(f, "/* integer values of string keys: */\n");
+		fprintf(f, "typedef enum %s_keys_e {\n", prefix);
+		for(n=0; n<hash_size; n++) {
+			for(m=0; m<hash_size; m++) {
+				if ((m < data_num) &&(values[m] == n)) {
+					char *s;
+					for(s = data[m]; *s != '\0'; s++)
+						if (!isalnum(*s))
+							*s = '_';
+
+					fprintf(f, "	%s_%s = %d,\n", prefix, data[m], m);
+					minmax(m, min, max);
+					numitems++;
+					break;
+				}
+			}
+		}
+		fprintf(f, "	%s_SPHASH_INVALID = -1\n", prefix);
+		fprintf(f, "} %s_keys_t;\n\n", prefix);
+		fprintf(f, "typedef enum %s_props_e {\n", prefix);
+		fprintf(f, "	%s_SPHASH_MINVAL = %d,\n", prefix, min);
+		fprintf(f, "	%s_SPHASH_MAXVAL = %d,\n", prefix, max);
+		fprintf(f, "	%s_SPHASH_RANGE = %d,\n", prefix, max-min+1);
+		fprintf(f, "	%s_SPHASH_NUMITEMS = %d\n", prefix, numitems);
+		fprintf(f, "} %s_props_t;\n", prefix);
+		fprintf(f, "\nconst char *%s_keyname(%s_keys_t keyid);\n", prefix, prefix);
+		fprintf(f, "#define %s_keyid2idx(keyid)  ((keyid)-%d)\n", prefix, min);
+		fprintf(f, "#define %s_idx2keyid(idx)    ((idx)+%d)\n", prefix, min);
+
+	}
+
+	if (aux_fields != NULL) {
+		fprintf(f, "\ntypedef struct %s_aux_e {\n", prefix);
+		fprintf(f, "%s\n", aux_fields);
+		fprintf(f, "} %s_aux_t;\n", prefix);
+		fprintf(f, "extern %s_aux_t %s_aux[];\n\n", prefix, prefix);
+	}
+	fprintf(f, "#endif\n");
+	fclose(f);
+}
+
+int main(int argc, char *argv[])
+{
+	int best, n;
+	FILE *f;
+	char *fn = NULL, *cmd;
+	char *prefix = "test";
+	char *outfn  = "test_hash";
+	char *end;
+
+	n = 1;
+	while(argv[n] != NULL) {
+		cmd = argv[n];
+		if (*cmd == '-') {
+			while(*cmd == '-') cmd++;
+			switch(*cmd) {
+				case 'p': /* --prefix */
+					n++;
+					prefix = argv[n];
+					break;
+				case 'o': /* --out */
+					n++;
+					outfn = argv[n];
+					break;
+				case 'm': /* --multi */
+					multi=1;
+					break;
+				case 'a': /* --aux */
+					n++;
+					aux_fields = argv[n];
+					break;
+				case 'v': /* --verbose */
+					debug_level++;
+					break;
+				case 'O': /* --overrun */
+					n++;
+					overrun = strtol(argv[n], &end, 10);
+					if (*end != '\0') {
+						fprintf(stderr, "Expected integer for -O\n");
+						return 1;
+					}
+			}
+		}
+		else {
+			fn = argv[n];
+		}
+		n++;
+	}
+
+	if (fn != NULL) {
+		f = fopen(fn, "r");
+		if (f == NULL) {
+			printf("Can't open file '%s'\n", fn);
+			return -1;
+		}
+	}
+	else
+		f = stdin;
+
+	if (load_strings(f) < 0)
+		return 1;
+
+	values = calloc(sizeof(int), data_num*overrun);
+	got = calloc(sizeof(int), data_num*overrun);
+
+	hash_size = 1;
+
+	for(hash_size = data_num; hash_size < data_num * overrun; hash_size *= 1.25) {
+		best = optimize_hash_function();
+		lprintf(2, "Hash_size: %d best=%d\n", hash_size, best);
+		if (best <= 4) {
+			break;
+		}
+	}
+
+
+	for(; hash_size < data_num * overrun; hash_size += 8) {
+		best = optimize_hash_function();
+		lprintf(2, "Hash_size: %d best=%d (shortest=%d)\n", hash_size, best, shortest);
+		if (best == 0) {
+			if (shortest < 40) {
+				SPHASH_SEED0 = SPHASH_SEED0_best;
+				SPHASH_SEED1 = SPHASH_SEED1_best;
+				SPHASH_SEED2 = SPHASH_SEED2_best;
+				hash_degree();
+				lprintf(3, "Seeds: %d %d %d, size=%d/%d\n", SPHASH_SEED0, SPHASH_SEED1, SPHASH_SEED2, hash_size, data_num);
+				dump_file(outfn, prefix);
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
diff --git a/src_3rd/sphash/test.c b/src_3rd/sphash/test.c
new file mode 100644
index 0000000..c58e60d
--- /dev/null
+++ b/src_3rd/sphash/test.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <string.h>
+#include "test_hash.h"
+
+int main()
+{
+	char s[128];
+	while(!(feof(stdin))) {
+		*s = '\0';
+		fgets(s, sizeof(s), stdin);
+		s[strlen(s)-1] = '\0';
+		printf("'%s' is %d is '%s'\n", s, test_sphash(s), test_keyname(test_sphash(s)));
+	}
+	return 0;
+}
diff --git a/src_3rd/sphash/wenums b/src_3rd/sphash/wenums
new file mode 100644
index 0000000..77c66ee
--- /dev/null
+++ b/src_3rd/sphash/wenums
@@ -0,0 +1,14 @@
+protocol
+ tcp
+ udp
+ control
+policy
+ allow
+ deny
+resolv
+ enable
+ disable
+stderr
+ ignore
+ control
+ merge
diff --git a/src_3rd/sphash/words b/src_3rd/sphash/words
new file mode 100644
index 0000000..79c984d
--- /dev/null
+++ b/src_3rd/sphash/words
@@ -0,0 +1,9 @@
+hello
+world
+this
+file
+contains
+a
+few
+short
+words
diff --git a/src_plugins/Buildin.tmpasm b/src_plugins/Buildin.tmpasm
new file mode 100644
index 0000000..7002ad2
--- /dev/null
+++ b/src_plugins/Buildin.tmpasm
@@ -0,0 +1,25 @@
+# tmpasm script for compiling a plugin_src/ module as a buildin
+# Requires variables before the include:
+#  /local/pcb/mod       basename of the module (e.g. autoplace)
+#  /local/pcb/mod/OBJS  full path of all object files
+#  /local/pcb/mod/OBJS_C99  full path of all object files for non-c89
+#  /local/pcb/mod/CONF  config file name
+
+append /local/pcb/buildin_init_extern    [@extern pcb_uninit_t hid_@/local/pcb/mod at _init();@] {\n}
+
+append /local/pcb/buildin_init_code    [@
+	uninit_func = hid_@/local/pcb/mod at _init();
+	plugin_register("@/local/pcb/mod@", "<@/local/pcb/mod@>", NULL, 0, uninit_func);
+@]  {\n}
+
+append /local/pcb/OBJS            ?/local/pcb/mod/OBJS
+append /local/pcb/OBJS_C99        ?/local/pcb/mod/OBJS_C99
+append /local/pcb/LDFLAGS         /local/pcb/mod/LDFLAGS
+append /local/pcb/CFLAGS          /local/pcb/mod/CFLAGS
+append /local/pcb/RULES [@
+
+mod_@/local/pcb/mod@: all
+
+@]
+
+include /local/pcb/tmpasm/common_enabled
diff --git a/src_plugins/Common_enabled.tmpasm b/src_plugins/Common_enabled.tmpasm
new file mode 100644
index 0000000..25f4d15
--- /dev/null
+++ b/src_plugins/Common_enabled.tmpasm
@@ -0,0 +1,118 @@
+# explicit rules: .y -> .c
+#  do not assume old yacc to work to different file names, do the generation
+#  in a separate directory to allow parallel compilation with -j
+switch /local/pcb/mod/YACC
+	case {^$} end
+	default
+		foreach /local/n in /local/pcb/mod/YACC
+			put /local/bn /local/n
+			sub {/local/bn} {^.*/} {}
+			put /local/dn /local/n
+			sub {/local/dn} {/[^/]*$} {}
+
+			if /local/pcb/want_parsgen
+			then
+				append /local/pcb/RULES [@
+# yacc for @/local/pcb/mod@
+@/local/n at .c @/local/n at .h: @/local/n at .y
+	cd @/local/dn@ && bison --defines=@/local/bn at .h --output=@/local/bn at .c --report-file=@/local/bn at .output -d @/local/bn at .y
+@]
+			else
+				append /local/pcb/RULES [@
+# dummy yacc for @/local/pcb/mod@
+@/local/n at .c @/local/n at .h:
+	echo "skipping yacc..."
+@]
+			end
+		end
+	end
+end
+
+# explicit rules: .l -> .c
+#  do not assume old lex to work to different file names, do the generation
+#  in a separate directory to allow parallel compilation with -j
+switch /local/pcb/mod/LEX
+	case {^$} end
+	default
+		foreach /local/n in /local/pcb/mod/LEX
+			if /local/pcb/want_parsgen
+			then
+				put /local/bn /local/n
+				sub {/local/bn} {^.*/} {}
+				put /local/dn /local/n
+				sub {/local/dn} {/[^/]*$} {}
+
+				append /local/pcb/RULES [@
+# lex for @/local/pcb/mod@
+@/local/n at .c @/local/n at .h: @/local/n at .l
+	cd @/local/dn@ && flex --outfile=@/local/bn at .c --header-file=@/local/bn at .h @/local/bn at .l
+@]
+			else
+				append /local/pcb/RULES [@
+# dummy lex for @/local/pcb/mod@
+@/local/n at .c:
+	echo "skipping flex..."
+@]
+			end
+		end
+	end
+end
+
+switch /local/pcb/mod/CONF
+	case {^$} end
+	default
+		put /local/pcb/mod/CONFOUT /local/pcb/mod/CONF
+		sub {/local/pcb/mod/CONFOUT} {.h$} {_fields.h}
+		append /local/pcb/CLEANFILES /local/pcb/mod/CONFOUT
+		append /local/pcb/RULES [@
+# conf generation for @/local/pcb/mod@ '@/local/pcb/mod/CONF@'
+@/local/pcb/mod/CONFOUT@: @/local/pcb/mod/CONF@
+	AWK=@/host/fstools/awk@ ../scconfig/gen_conf.sh < @/local/pcb/mod/CONF@ > @/local/pcb/mod/CONFOUT@
+@]
+		end
+end
+
+# explicit rules: .sphash -> .c
+# Space separated list of .sphash input files
+# Optional: aux data fileds:   $(PLUGDIR)/query/consts.sphash::long:int:val;
+switch ?/local/pcb/mod/SPHASH
+	case {^$} end
+	default
+		foreach /local/nn in /local/pcb/mod/SPHASH
+			put /local/n  /local/nn
+			sub {/local/n} {::.*$} {}
+			put /local/bn /local/n
+			sub {/local/bn} {.[^.]*$} {}
+			put /local/pr /local/bn
+			sub {/local/pr} {^.*/} {}
+			switch /local/nn
+				case {::}
+					put /local/aux /local/nn
+					sub {/local/aux} {^.*::} {}
+					gsub {/local/aux} {:} { }
+					sub {/local/aux} {^} {--aux "}
+					append {/local/aux} {"}
+					end
+				default
+					put /local/aux {}
+					end
+			end
+			append /local/pcb/RULES [@
+# sphash for @/local/pcb/mod@
+@/local/bn at _sphash.c @/local/bn at _sphash.h: $(SPHASH) @/local/n@
+	$(SPHASH) @/local/aux@ --prefix @/local/pcb/mod at _@/local/pr@ --out @/local/bn at _sphash < @/local/n@
+@]
+		end
+	end
+end
+
+put /local/pcb/mod/OBJS {}
+put /local/pcb/mod/OBJS_C99 {}
+put /local/pcb/mod/CONF {}
+put /local/pcb/mod/LDFLAGS {}
+put /local/pcb/mod/CFLAGS {}
+put /local/pcb/mod/YACC {}
+put /local/pcb/mod/LEX {}
+put /local/pcb/mod/SPHASH {}
+put /local/pcb/mod {}
+
diff --git a/src_plugins/Disable.tmpasm b/src_plugins/Disable.tmpasm
new file mode 100644
index 0000000..0df02c0
--- /dev/null
+++ b/src_plugins/Disable.tmpasm
@@ -0,0 +1,14 @@
+# tmpasm script for disable a plugin_src/ module 
+# Requires variables before the include:
+#  /local/pcb/mod/OBJS  full path of all object files
+
+# append all objects to the source list used only for dependencies
+# NOTE: .o suffixes will be replaced with .c before generating the dep
+append /local/pcb/DEPSRCS            /local/pcb/mod/OBJS
+
+put /local/pcb/mod/OBJS {}
+put /local/pcb/mod/OBJS_C99 {}
+put /local/pcb/mod/CONF {}
+put /local/pcb/mod/LDFLAGS {}
+put /local/pcb/mod/CFLAGS {}
+put /local/pcb/mod {}
diff --git a/src_plugins/Plugin.tmpasm b/src_plugins/Plugin.tmpasm
new file mode 100644
index 0000000..f1be523
--- /dev/null
+++ b/src_plugins/Plugin.tmpasm
@@ -0,0 +1,46 @@
+# tmpasm script for compiling a plugin_src/ module as a plugin
+# Requires variables before the include:
+#  /local/pcb/mod       basename of the module (e.g. autoplace)
+#  /local/pcb/mod/OBJS  full path of all object files
+#  /local/pcb/mod/OBJS_C99  full path of all object files for non-c89
+
+# clean up input vars
+uniq /local/pcb/mod/OBJS
+uniq /local/pcb/mod/OBJS_C99
+uniq /local/pcb/mod/CFLAGS
+uniq /local/pcb/mod/LDFLAGS
+uniq /local/pcb/mod/LIBS
+
+# generate .c -> .o rules in /local/comp/output
+put /local/comp/OBJS ?/local/pcb/mod/OBJS
+put /local/comp/OBJS_C99 ?/local/pcb/mod/OBJS_C99
+put /local/comp/CFLAGS /local/pcb/mod/CFLAGS
+include {../scconfig/Makefile.comp_var.inc}
+
+append /local/pcb/all   [@ $(PLUGIDIR)/@/local/pcb/mod at .so @]
+
+append /local/pcb/rules/install_ [@
+	$(CPC) "`pwd`/$(PLUGDIR)/@/local/pcb/mod@/@/local/pcb/mod at .so" "$(LIBDIR)/plugins/@/local/pcb/mod at .so"@]
+
+append /local/pcb/CLEANFILES [@ $(PLUGDIR)/@/local/pcb/mod@/@/local/pcb/mod at .so @/local/pcb/mod/OBJS@  @/local/pcb/mod/OBJS_C99@ @]
+
+append /local/pcb/RULES [@
+
+### Module @/local/pcb/mod@: plugin ###
+
+$(PLUGDIR)/@/local/pcb/mod@/@/local/pcb/mod at .so: @/local/pcb/mod/OBJS@ @/local/pcb/mod/OBJS_C99@
+	$(CC) $(LDFLAGS) @/local/pcb/mod/LDFLAGS@ -shared @cc/rdynamic@ -o $(PLUGDIR)/@/local/pcb/mod@/@/local/pcb/mod at .so @/local/pcb/mod/OBJS@ @/local/pcb/mod/OBJS_C99@
+
+mod_@/local/pcb/mod@: $(PLUGIDIR)/@/local/pcb/mod at .so
+
+$(PLUGIDIR)/@/local/pcb/mod at .so: $(PLUGDIR)/@/local/pcb/mod@/@/local/pcb/mod at .so
+	$(MKDIR) $(PLUGIDIR)
+	$(CP) $(PLUGDIR)/@/local/pcb/mod@/@/local/pcb/mod at .so $(PLUGIDIR)/@/local/pcb/mod at .so
+
+# module .c -> .o rules
+@/local/comp/output@
+
+### Module @/local/pcb/mod@ end ###
+@]
+
+include /local/pcb/tmpasm/common_enabled
diff --git a/src_plugins/README b/src_plugins/README
new file mode 100644
index 0000000..bbb3d41
--- /dev/null
+++ b/src_plugins/README
@@ -0,0 +1,12 @@
+This directory hosts core plugins. They are feature, exporter and hid plugins.
+
+Each subdirectory is a standalone plugin that can be selected to be a
+(static linked) building, a (dynamic linked) plugin or not compiled at
+all. The selection is made in ./configure.
+
+HID plugins are user interfaces; exporter plugins convert the current design
+to an alien format; feature plugins create actions or otherwise add
+functionality to the system independent of which HID is running.
+
+Some of the plugins depend on ../src_3rd and all of them depend on
+core (../src).
diff --git a/src_plugins/autocrop/Makefile b/src_plugins/autocrop/Makefile
new file mode 100644
index 0000000..a41e18e
--- /dev/null
+++ b/src_plugins/autocrop/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_autocrop
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/autocrop/Plug.tmpasm b/src_plugins/autocrop/Plug.tmpasm
new file mode 100644
index 0000000..d1980d6
--- /dev/null
+++ b/src_plugins/autocrop/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {autocrop}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/autocrop/autocrop.o @]
+
+switch /local/pcb/autocrop/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/autocrop/README b/src_plugins/autocrop/README
new file mode 100644
index 0000000..1d1688d
--- /dev/null
+++ b/src_plugins/autocrop/README
@@ -0,0 +1,5 @@
+Reduce the board dimensions to just enclose the elements.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/autocrop/autocrop.c b/src_plugins/autocrop/autocrop.c
new file mode 100644
index 0000000..2cea1a1
--- /dev/null
+++ b/src_plugins/autocrop/autocrop.c
@@ -0,0 +1,247 @@
+/*!
+ * \file autocrop.c
+ *
+ * \brief Autocrop plug-in for PCB.
+ * Reduce the board dimensions to just enclose the elements.
+ *
+ * \author Copyright (C) 2007 Ben Jackson <ben at ben.com> based on teardrops.c by
+ * Copyright (C) 2006 DJ Delorie <dj at delorie.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * From: Ben Jackson <bjj at saturn.home.ben.com>
+ * To: geda-user at moria.seul.org
+ * Date: Mon, 19 Feb 2007 13:30:58 -0800
+ * Subject: Autocrop() plugin for PCB
+ *
+ * As a "learn PCB internals" project I've written an Autocrop() plugin.
+ * It finds the extents of your existing board and resizes the board to
+ * contain only your parts plus a margin.
+ *
+ * There are some issues that I can't resolve from a plugin:
+ *
+ * <ol>
+ * <li> Board size has no Undo function, so while Undo will put your objects
+ * back where they started, the board size has to be replaced manually.
+ * <li> There is no 'edge clearance' DRC paramater, so I used 5*line spacing.
+ * <li> Moving a layout with lots of objects and polygons is slow due to the
+ * repeated clearing/unclearing of the polygons as things move.  Undo is
+ * slower than moving because every individual move is drawn (instead of
+ * one redraw at the end).
+ * </ol>
+ *
+ * Original source was: http://ad7gd.net/geda/autocrop.c
+ *
+ * Run it by typing `:Autocrop()' in the gui, or by binding Autocrop() to a key.
+ *
+ * --
+ * Ben Jackson AD7GD
+ * <ben at ben.com>
+ * http://www.ben.com/
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "move.h"
+#include "draw.h"
+#include "set.h"
+#include "polygon.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+static void *MyMoveViaLowLevel(DataType * Data, PinType * Via, Coord dx, Coord dy)
+{
+	if (Data) {
+		RestoreToPolygon(Data, PCB_TYPE_VIA, Via, Via);
+		r_delete_entry(Data->via_tree, (BoxType *) Via);
+	}
+	MOVE_VIA_LOWLEVEL(Via, dx, dy);
+	if (Data) {
+		r_insert_entry(Data->via_tree, (BoxType *) Via, 0);
+		ClearFromPolygon(Data, PCB_TYPE_VIA, Via, Via);
+	}
+	return Via;
+}
+
+static void *MyMoveLineLowLevel(DataType * Data, LayerType * Layer, LineType * Line, Coord dx, Coord dy)
+{
+	if (Data) {
+		RestoreToPolygon(Data, PCB_TYPE_LINE, Layer, Line);
+		r_delete_entry(Layer->line_tree, (BoxType *) Line);
+	}
+	MOVE_LINE_LOWLEVEL(Line, dx, dy);
+	if (Data) {
+		r_insert_entry(Layer->line_tree, (BoxType *) Line, 0);
+		ClearFromPolygon(Data, PCB_TYPE_LINE, Layer, Line);
+	}
+	return Line;
+}
+
+static void *MyMoveArcLowLevel(DataType * Data, LayerType * Layer, ArcType * Arc, Coord dx, Coord dy)
+{
+	if (Data) {
+		RestoreToPolygon(Data, PCB_TYPE_ARC, Layer, Arc);
+		r_delete_entry(Layer->arc_tree, (BoxType *) Arc);
+	}
+	MOVE_ARC_LOWLEVEL(Arc, dx, dy);
+	if (Data) {
+		r_insert_entry(Layer->arc_tree, (BoxType *) Arc, 0);
+		ClearFromPolygon(Data, PCB_TYPE_ARC, Layer, Arc);
+	}
+	return Arc;
+}
+
+static void *MyMovePolygonLowLevel(DataType * Data, LayerType * Layer, PolygonType * Polygon, Coord dx, Coord dy)
+{
+	if (Data) {
+		r_delete_entry(Layer->polygon_tree, (BoxType *) Polygon);
+	}
+	/* move.c actually only moves points, note no Data/Layer args */
+	MovePolygonLowLevel(Polygon, dx, dy);
+	if (Data) {
+		r_insert_entry(Layer->polygon_tree, (BoxType *) Polygon, 0);
+		InitClip(Data, Layer, Polygon);
+	}
+	return Polygon;
+}
+
+static void *MyMoveTextLowLevel(LayerType * Layer, TextType * Text, Coord dx, Coord dy)
+{
+	if (Layer)
+		r_delete_entry(Layer->text_tree, (BoxType *) Text);
+	MOVE_TEXT_LOWLEVEL(Text, dx, dy);
+	if (Layer)
+		r_insert_entry(Layer->text_tree, (BoxType *) Text, 0);
+	return Text;
+}
+
+/*!
+ * \brief Move everything.
+ *
+ * Call our own 'MyMove*LowLevel' where they don't exist in move.c.
+ * This gets very slow if there are large polygons present, since every
+ * element move re-clears the poly, followed by the polys moving and
+ * re-clearing everything again.
+ */
+static void MoveAll(Coord dx, Coord dy)
+{
+	ELEMENT_LOOP(PCB->Data);
+	{
+		MoveElementLowLevel(PCB->Data, element, dx, dy);
+		AddObjectToMoveUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element, dx, dy);
+	}
+	END_LOOP;
+	VIA_LOOP(PCB->Data);
+	{
+		MyMoveViaLowLevel(PCB->Data, via, dx, dy);
+		AddObjectToMoveUndoList(PCB_TYPE_VIA, NULL, NULL, via, dx, dy);
+	}
+	END_LOOP;
+	ALLLINE_LOOP(PCB->Data);
+	{
+		MyMoveLineLowLevel(PCB->Data, layer, line, dx, dy);
+		AddObjectToMoveUndoList(PCB_TYPE_LINE, NULL, NULL, line, dx, dy);
+	}
+	ENDALL_LOOP;
+	ALLARC_LOOP(PCB->Data);
+	{
+		MyMoveArcLowLevel(PCB->Data, layer, arc, dx, dy);
+		AddObjectToMoveUndoList(PCB_TYPE_ARC, NULL, NULL, arc, dx, dy);
+	}
+	ENDALL_LOOP;
+	ALLTEXT_LOOP(PCB->Data);
+	{
+		MyMoveTextLowLevel(layer, text, dx, dy);
+		AddObjectToMoveUndoList(PCB_TYPE_TEXT, NULL, NULL, text, dx, dy);
+	}
+	ENDALL_LOOP;
+	ALLPOLYGON_LOOP(PCB->Data);
+	{
+		/*
+		 * XXX MovePolygonLowLevel does not mean "no gui" like
+		 * XXX MoveElementLowLevel, it doesn't even handle layer
+		 * XXX tree activity.
+		 */
+		MyMovePolygonLowLevel(PCB->Data, layer, polygon, dx, dy);
+		AddObjectToMoveUndoList(PCB_TYPE_POLYGON, NULL, NULL, polygon, dx, dy);
+	}
+	ENDALL_LOOP;
+}
+
+static int autocrop(int argc, const char **argv, Coord x, Coord y)
+{
+	Coord dx, dy, pad;
+	BoxType *box;
+
+	box = GetDataBoundingBox(PCB->Data);	/* handy! */
+	if (!box || (box->X1 == box->X2 || box->Y1 == box->Y2)) {
+		/* board would become degenerate */
+		return 0;
+	}
+	/*
+	 * Now X1/Y1 are the distance to move the left/top edge
+	 * (actually moving all components to the left/up) such that
+	 * the exact edge of the leftmost/topmost component would touch
+	 * the edge.  Reduce the move by the edge relief requirement XXX
+	 * and expand the board by the same amount.
+	 */
+	pad = PCB->minWid * 5;				/* XXX real edge clearance */
+	dx = -box->X1 + pad;
+	dy = -box->Y1 + pad;
+	box->X2 += pad;
+	box->Y2 += pad;
+	/*
+	 * Round move to keep components grid-aligned, then translate the
+	 * upper coordinates into the new space.
+	 */
+	dx -= dx % (long) PCB->Grid;
+	dy -= dy % (long) PCB->Grid;
+	box->X2 += dx;
+	box->Y2 += dy;
+	/*
+	 * Avoid touching any data if there's nothing to do.
+	 */
+	if (dx == 0 && dy == 0 && PCB->MaxWidth == box->X2 && PCB->MaxHeight == box->Y2) {
+		return 0;
+	}
+	/* Resize -- XXX cannot be undone */
+	PCB->MaxWidth = box->X2;
+	PCB->MaxHeight = box->Y2;
+	MoveAll(dx, dy);
+	IncrementUndoSerialNumber();
+	Redraw();
+	SetChangedFlag(1);
+	return 0;
+}
+
+static HID_Action autocrop_action_list[] = {
+	{"autocrop", NULL, autocrop, NULL, NULL}
+};
+
+char *autocrop_cookie = "autocrop plugin";
+
+REGISTER_ACTIONS(autocrop_action_list, autocrop_cookie)
+
+static void hid_autocrop_uninit(void)
+{
+	hid_remove_actions_by_cookie(autocrop_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_autocrop_init()
+{
+	REGISTER_ACTIONS(autocrop_action_list, autocrop_cookie);
+	return hid_autocrop_uninit;
+}
diff --git a/src_plugins/autoplace/Makefile b/src_plugins/autoplace/Makefile
new file mode 100644
index 0000000..dfcba41
--- /dev/null
+++ b/src_plugins/autoplace/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_autoplace
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/autoplace/Plug.tmpasm b/src_plugins/autoplace/Plug.tmpasm
new file mode 100644
index 0000000..4ab464f
--- /dev/null
+++ b/src_plugins/autoplace/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {autoplace}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/autoplace/autoplace.o $(PLUGDIR)/autoplace/action.o @]
+
+switch /local/pcb/autoplace/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/autoplace/README b/src_plugins/autoplace/README
new file mode 100644
index 0000000..ca55b4f
--- /dev/null
+++ b/src_plugins/autoplace/README
@@ -0,0 +1,5 @@
+Automatically place elements.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/autoplace/action.c b/src_plugins/autoplace/action.c
new file mode 100644
index 0000000..0f1e87d
--- /dev/null
+++ b/src_plugins/autoplace/action.c
@@ -0,0 +1,76 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "autoplace.h"
+#include "plugins.h"
+#include "set.h"
+#include "hid_actions.h"
+
+static const char autoplace_syntax[] = "AutoPlaceSelected()";
+
+static const char autoplace_help[] = "Auto-place selected components.";
+
+/* %start-doc actions AutoPlaceSelected
+
+Attempts to re-arrange the selected components such that the nets
+connecting them are minimized.  Note that you cannot undo this.
+
+%end-doc */
+
+static int ActionAutoPlaceSelected(int argc, const char **argv, Coord x, Coord y)
+{
+	hid_action("Busy");
+	if (gui->confirm_dialog(_("Auto-placement can NOT be undone.\n" "Do you want to continue anyway?\n"), 0)) {
+		if (AutoPlaceSelected())
+			SetChangedFlag(pcb_true);
+	}
+	return 0;
+}
+
+static const char *autoplace_cookie = "autoplace plugin";
+
+HID_Action autoplace_action_list[] = {
+	{"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
+	 autoplace_help, autoplace_syntax}
+	,
+};
+
+REGISTER_ACTIONS(autoplace_action_list, autoplace_cookie)
+
+static void hid_autoplace_uninit(void)
+{
+	hid_remove_actions_by_cookie(autoplace_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_autoplace_init(void)
+{
+	REGISTER_ACTIONS(autoplace_action_list, autoplace_cookie)
+	return hid_autoplace_uninit;
+}
diff --git a/src_plugins/autoplace/autoplace.c b/src_plugins/autoplace/autoplace.c
new file mode 100644
index 0000000..14086ed
--- /dev/null
+++ b/src_plugins/autoplace/autoplace.c
@@ -0,0 +1,763 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/*
+ * This moduel, autoplace.c, was written by and is
+ * Copyright (c) 2001 C. Scott Ananian
+ */
+
+/* functions used to autoplace elements.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <math.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include "global.h"
+
+#include "autoplace.h"
+#include "box.h"
+#include "compat_misc.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "layer.h"
+#include "intersect.h"
+#include "rtree.h"
+#include "macro.h"
+#include "mirror.h"
+#include "misc.h"
+#include "move.h"
+#include "mymem.h"
+#include "rats.h"
+#include "remove.h"
+#include "rotate.h"
+
+#define EXPANDRECTXY(r1, x1, y1, x2, y2) { \
+  r1->X1=MIN(r1->X1, x1); r1->Y1=MIN(r1->Y1, y1); \
+  r1->X2=MAX(r1->X2, x2); r1->Y2=MAX(r1->Y2, y2); \
+}
+#define EXPANDRECT(r1, r2) EXPANDRECTXY(r1, r2->X1, r2->Y1, r2->X2, r2->Y2)
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static double ComputeCost(NetListTypePtr Nets, double T0, double T);
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+const struct {
+	double via_cost;
+	double congestion_penalty;		/* penalty length / unit area */
+	double overlap_penalty_min;		/* penalty length / unit area at start */
+	double overlap_penalty_max;		/* penalty length / unit area at end */
+	double out_of_bounds_penalty;	/* assessed for each component oob */
+	double overall_area_penalty;	/* penalty length / unit area */
+	double matching_neighbor_bonus;	/* length bonus per same-type neigh. */
+	double aligned_neighbor_bonus;	/* length bonus per aligned neigh. */
+	double oriented_neighbor_bonus;	/* length bonus per same-rot neigh. */
+#if 0
+	double pin_alignment_bonus;		/* length bonus per exact alignment */
+	double bound_alignment_bonus;	/* length bonus per exact alignment */
+#endif
+	double m;											/* annealing stage cutoff constant */
+	double gamma;									/* annealing schedule constant */
+	int good_ratio;								/* ratio of moves to good moves for halting */
+	pcb_bool fast;										/* ignore SMD/pin conflicts */
+	Coord large_grid_size;				/* snap perturbations to this grid when T is high */
+	Coord small_grid_size;				/* snap to this grid when T is small. */
+}
+/* wire cost is manhattan distance (in mils), thus 1 inch = 1000 */ CostParameter =
+{
+	3e3,													/* via cost */
+		2e-2,												/* congestion penalty */
+		1e-2,												/* initial overlap penalty */
+		1e2,												/* final overlap penalty */
+		1e3,												/* out of bounds penalty */
+		1e0,												/* penalty for total area used */
+		1e0,												/* subtract 1000 from cost for every same-type neighbor */
+		1e0,												/* subtract 1000 from cost for every aligned neighbor */
+		1e0,												/* subtract 1000 from cost for every same-rotation neighbor */
+		20,													/* move on when each module has been profitably moved 20 times */
+		0.75,												/* annealing schedule constant: 0.85 */
+		40,													/* halt when there are 60 times as many moves as good moves */
+		pcb_false,											/* don't ignore SMD/pin conflicts */
+		PCB_MIL_TO_COORD(100),					/* coarse grid is 100 mils */
+		PCB_MIL_TO_COORD(10),						/* fine grid is 10 mils */
+};
+
+typedef struct {
+	ElementTypePtr *element;
+	pcb_cardinal_t elementN;
+} ElementPtrListType;
+
+enum ewhich { SHIFT, ROTATE, EXCHANGE };
+
+typedef struct {
+	ElementTypePtr element;
+	enum ewhich which;
+	Coord DX, DY;									/* for shift */
+	unsigned rotate;							/* for rotate/flip */
+	ElementTypePtr other;					/* for exchange */
+} PerturbationType;
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+
+/* ---------------------------------------------------------------------------
+ * Update the X, Y and group position information stored in the NetList after
+ * elements have possibly been moved, rotated, flipped, etc.
+ */
+static void UpdateXY(NetListTypePtr Nets)
+{
+	pcb_cardinal_t SLayer, CLayer;
+	pcb_cardinal_t i, j;
+	/* find layer groups of the component side and solder side */
+	SLayer = GetLayerGroupNumberByNumber(solder_silk_layer);
+	CLayer = GetLayerGroupNumberByNumber(component_silk_layer);
+	/* update all nets */
+	for (i = 0; i < Nets->NetN; i++) {
+		for (j = 0; j < Nets->Net[i].ConnectionN; j++) {
+			ConnectionTypePtr c = &(Nets->Net[i].Connection[j]);
+			switch (c->type) {
+			case PCB_TYPE_PAD:
+				c->group = TEST_FLAG(PCB_FLAG_ONSOLDER, (ElementTypePtr) c->ptr1)
+					? SLayer : CLayer;
+				c->X = ((PadTypePtr) c->ptr2)->Point1.X;
+				c->Y = ((PadTypePtr) c->ptr2)->Point1.Y;
+				break;
+			case PCB_TYPE_PIN:
+				c->group = SLayer;			/* any layer will do */
+				c->X = ((PinTypePtr) c->ptr2)->X;
+				c->Y = ((PinTypePtr) c->ptr2)->Y;
+				break;
+			default:
+				Message(PCB_MSG_DEFAULT, "Odd connection type encountered in " "UpdateXY");
+				break;
+			}
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * Create a list of selected elements.
+ */
+static PointerListType collectSelectedElements()
+{
+	PointerListType list = { 0, 0, NULL };
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, element)) {
+			ElementTypePtr *epp = (ElementTypePtr *) GetPointerMemory(&list);
+			*epp = element;
+		}
+	}
+	END_LOOP;
+	return list;
+}
+
+#if 0														/* only for debugging box lists */
+#include "create.h"
+/* makes a line on the solder layer surrounding all boxes in blist */
+static void showboxes(BoxListTypePtr blist)
+{
+	pcb_cardinal_t i;
+	LayerTypePtr SLayer = &(PCB->Data->Layer[solder_silk_layer]);
+	for (i = 0; i < blist->BoxN; i++) {
+		CreateNewLineOnLayer(SLayer, blist->Box[i].X1, blist->Box[i].Y1, blist->Box[i].X2, blist->Box[i].Y1, 1, 1, 0);
+		CreateNewLineOnLayer(SLayer, blist->Box[i].X1, blist->Box[i].Y2, blist->Box[i].X2, blist->Box[i].Y2, 1, 1, 0);
+		CreateNewLineOnLayer(SLayer, blist->Box[i].X1, blist->Box[i].Y1, blist->Box[i].X1, blist->Box[i].Y2, 1, 1, 0);
+		CreateNewLineOnLayer(SLayer, blist->Box[i].X2, blist->Box[i].Y1, blist->Box[i].X2, blist->Box[i].Y2, 1, 1, 0);
+	}
+}
+#endif
+
+/* ---------------------------------------------------------------------------
+ * Helper function to compute "closest neighbor" for a box in a rtree.
+ * The closest neighbor on a certain side is the closest one in a trapezoid
+ * emanating from that side.
+ */
+/*------ r_find_neighbor ------*/
+struct r_neighbor_info {
+	const BoxType *neighbor;
+	BoxType trap;
+	direction_t search_dir;
+};
+#define ROTATEBOX(box) { Coord t;\
+    t = (box).X1; (box).X1 = - (box).Y1; (box).Y1 = t;\
+    t = (box).X2; (box).X2 = - (box).Y2; (box).Y2 = t;\
+    t = (box).X1; (box).X1 =   (box).X2; (box).X2 = t;\
+}
+/* helper methods for __r_find_neighbor */
+static r_dir_t __r_find_neighbor_reg_in_sea(const BoxType * region, void *cl)
+{
+	struct r_neighbor_info *ni = (struct r_neighbor_info *) cl;
+	BoxType query = *region;
+	ROTATEBOX_TO_NORTH(query, ni->search_dir);
+	/*  ______________ __ trap.y1     __
+	 *  \            /               |__| query rect.
+	 *   \__________/  __ trap.y2
+	 *   |          |
+	 *   trap.x1    trap.x2   sides at 45-degree angle
+	 */
+	if ((query.Y2 > ni->trap.Y1) && (query.Y1 < ni->trap.Y2) && (query.X2 + ni->trap.Y2 > ni->trap.X1 + query.Y1) && (query.X1 + query.Y1 < ni->trap.X2 + ni->trap.Y2))
+		return R_DIR_FOUND_CONTINUE;
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t __r_find_neighbor_rect_in_reg(const BoxType * box, void *cl)
+{
+	struct r_neighbor_info *ni = (struct r_neighbor_info *) cl;
+	BoxType query = *box;
+	int r;
+	ROTATEBOX_TO_NORTH(query, ni->search_dir);
+	/*  ______________ __ trap.y1     __
+	 *  \            /               |__| query rect.
+	 *   \__________/  __ trap.y2
+	 *   |          |
+	 *   trap.x1    trap.x2   sides at 45-degree angle
+	 */
+	r = (query.Y2 > ni->trap.Y1) && (query.Y1 < ni->trap.Y2) &&
+		(query.X2 + ni->trap.Y2 > ni->trap.X1 + query.Y1) && (query.X1 + query.Y1 < ni->trap.X2 + ni->trap.Y2);
+	r = r && (query.Y2 <= ni->trap.Y2);
+	if (r) {
+		ni->trap.Y1 = query.Y2;
+		ni->neighbor = box;
+	}
+	return r ? R_DIR_FOUND_CONTINUE : R_DIR_NOT_FOUND;
+}
+
+/* main r_find_neighbor routine.  Returns NULL if no neighbor in the
+ * requested direction. */
+static const BoxType *r_find_neighbor(rtree_t * rtree, const BoxType * box, direction_t search_direction)
+{
+	struct r_neighbor_info ni;
+	BoxType bbox;
+
+	ni.neighbor = NULL;
+	ni.trap = *box;
+	ni.search_dir = search_direction;
+
+	bbox.X1 = bbox.Y1 = 0;
+	bbox.X2 = PCB->MaxWidth;
+	bbox.Y2 = PCB->MaxHeight;
+	/* rotate so that we can use the 'north' case for everything */
+	ROTATEBOX_TO_NORTH(bbox, search_direction);
+	ROTATEBOX_TO_NORTH(ni.trap, search_direction);
+	/* shift Y's such that trap contains full bounds of trapezoid */
+	ni.trap.Y2 = ni.trap.Y1;
+	ni.trap.Y1 = bbox.Y1;
+	/* do the search! */
+	r_search(rtree, NULL, __r_find_neighbor_reg_in_sea, __r_find_neighbor_rect_in_reg, &ni, NULL);
+	return ni.neighbor;
+}
+
+/* ---------------------------------------------------------------------------
+ * Compute cost function.
+ *  note that area overlap cost is correct for SMD devices: SMD devices on
+ *  opposite sides of the board don't overlap.
+ *
+ * Algorithms follow those described in sections 4.1 of
+ *  "Placement and Routing of Electronic Modules" edited by Michael Pecht
+ *  Marcel Dekker, Inc. 1993.  ISBN: 0-8247-8916-4 TK7868.P7.P57 1993
+ */
+static double ComputeCost(NetListTypePtr Nets, double T0, double T)
+{
+	double W = 0;									/* wire cost */
+	double delta1 = 0;						/* wire congestion penalty function */
+	double delta2 = 0;						/* module overlap penalty function */
+	double delta3 = 0;						/* out of bounds penalty */
+	double delta4 = 0;						/* alignment bonus */
+	double delta5 = 0;						/* total area penalty */
+	pcb_cardinal_t i, j;
+	Coord minx, maxx, miny, maxy;
+	pcb_bool allpads, allsameside;
+	pcb_cardinal_t thegroup;
+	BoxListType bounds = { 0, 0, NULL };	/* save bounding rectangles here */
+	BoxListType solderside = { 0, 0, NULL };	/* solder side component bounds */
+	BoxListType componentside = { 0, 0, NULL };	/* component side bounds */
+	/* make sure the NetList have the proper updated X and Y coords */
+	UpdateXY(Nets);
+	/* wire length term.  approximated by half-perimeter of minimum
+	 * rectangle enclosing the net.  Note that we penalize vias in
+	 * all-SMD nets by making the rectangle a cube and weighting
+	 * the "layer height" of the net. */
+	for (i = 0; i < Nets->NetN; i++) {
+		NetTypePtr n = &Nets->Net[i];
+		if (n->ConnectionN < 2)
+			continue;									/* no cost to go nowhere */
+		minx = maxx = n->Connection[0].X;
+		miny = maxy = n->Connection[0].Y;
+		thegroup = n->Connection[0].group;
+		allpads = (n->Connection[0].type == PCB_TYPE_PAD);
+		allsameside = pcb_true;
+		for (j = 1; j < n->ConnectionN; j++) {
+			ConnectionTypePtr c = &(n->Connection[j]);
+			MAKEMIN(minx, c->X);
+			MAKEMAX(maxx, c->X);
+			MAKEMIN(miny, c->Y);
+			MAKEMAX(maxy, c->Y);
+			if (c->type != PCB_TYPE_PAD)
+				allpads = pcb_false;
+			if (c->group != thegroup)
+				allsameside = pcb_false;
+		}
+		/* save bounding rectangle */
+		{
+			BoxTypePtr box = GetBoxMemory(&bounds);
+			box->X1 = minx;
+			box->Y1 = miny;
+			box->X2 = maxx;
+			box->Y2 = maxy;
+		}
+		/* okay, add half-perimeter to cost! */
+		W += PCB_COORD_TO_MIL(maxx - minx) + PCB_COORD_TO_MIL(maxy - miny) + ((allpads && !allsameside) ? CostParameter.via_cost : 0);
+	}
+	/* now compute penalty function Wc which is proportional to
+	 * amount of overlap and congestion. */
+	/* delta1 is congestion penalty function */
+	delta1 = CostParameter.congestion_penalty * sqrt(fabs(ComputeIntersectionArea(&bounds)));
+#if 0
+	printf("Wire Congestion Area: %f\n", ComputeIntersectionArea(&bounds));
+#endif
+	/* free bounding rectangles */
+	FreeBoxListMemory(&bounds);
+	/* now collect module areas (bounding rect of pins/pads) */
+	/* two lists for solder side / component side. */
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		BoxListTypePtr thisside;
+		BoxListTypePtr otherside;
+		BoxTypePtr box;
+		BoxTypePtr lastbox = NULL;
+		Coord thickness;
+		Coord clearance;
+		if (TEST_FLAG(PCB_FLAG_ONSOLDER, element)) {
+			thisside = &solderside;
+			otherside = &componentside;
+		}
+		else {
+			thisside = &componentside;
+			otherside = &solderside;
+		}
+		box = GetBoxMemory(thisside);
+		/* protect against elements with no pins/pads */
+		if (pinlist_length(&element->Pin) == 0 && padlist_length(&element->Pad) == 0)
+			continue;
+		/* initialize box so that it will take the dimensions of
+		 * the first pin/pad */
+		box->X1 = MAX_COORD;
+		box->Y1 = MAX_COORD;
+		box->X2 = -MAX_COORD;
+		box->Y2 = -MAX_COORD;
+		PIN_LOOP(element);
+		{
+			thickness = pin->Thickness / 2;
+			clearance = pin->Clearance * 2;
+		EXPANDRECTXY(box,
+									 pin->X - (thickness + clearance),
+									 pin->Y - (thickness + clearance), pin->X + (thickness + clearance), pin->Y + (thickness + clearance))}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			thickness = pad->Thickness / 2;
+			clearance = pad->Clearance * 2;
+		EXPANDRECTXY(box,
+									 MIN(pad->Point1.X,
+												 pad->Point2.X) - (thickness +
+																						 clearance),
+									 MIN(pad->Point1.Y,
+												 pad->Point2.Y) - (thickness +
+																						 clearance),
+									 MAX(pad->Point1.X,
+												 pad->Point2.X) + (thickness + clearance), MAX(pad->Point1.Y, pad->Point2.Y) + (thickness + clearance))}
+		END_LOOP;
+		/* add a box for each pin to the "opposite side":
+		 * surface mount components can't sit on top of pins */
+		if (!CostParameter.fast)
+			PIN_LOOP(element);
+		{
+			box = GetBoxMemory(otherside);
+			thickness = pin->Thickness / 2;
+			clearance = pin->Clearance * 2;
+			/* we ignore clearance here */
+			/* (otherwise pins don't fit next to each other) */
+			box->X1 = pin->X - thickness;
+			box->Y1 = pin->Y - thickness;
+			box->X2 = pin->X + thickness;
+			box->Y2 = pin->Y + thickness;
+			/* speed hack! coalesce with last box if we can */
+			if (lastbox != NULL &&
+					((lastbox->X1 == box->X1 &&
+						lastbox->X2 == box->X2 &&
+						MIN(labs(lastbox->Y1 - box->Y2),
+								labs(box->Y1 - lastbox->Y2)) <
+						clearance) || (lastbox->Y1 == box->Y1
+													 && lastbox->Y2 == box->Y2
+													 && MIN(labs(lastbox->X1 - box->X2), labs(box->X1 - lastbox->X2)) < clearance))) {
+				EXPANDRECT(lastbox, box);
+				otherside->BoxN--;
+			}
+			else
+				lastbox = box;
+		}
+		END_LOOP;
+		/* assess out of bounds penalty */
+		if (element->VBox.X1 < 0 || element->VBox.Y1 < 0 || element->VBox.X2 > PCB->MaxWidth || element->VBox.Y2 > PCB->MaxHeight)
+			delta3 += CostParameter.out_of_bounds_penalty;
+	}
+	END_LOOP;
+	/* compute intersection area of module areas box list */
+	delta2 = sqrt(fabs(ComputeIntersectionArea(&solderside) +
+										 ComputeIntersectionArea(&componentside))) *
+		(CostParameter.overlap_penalty_min + (1 - (T / T0)) * CostParameter.overlap_penalty_max);
+#if 0
+	printf("Module Overlap Area (solder): %f\n", ComputeIntersectionArea(&solderside));
+	printf("Module Overlap Area (component): %f\n", ComputeIntersectionArea(&componentside));
+#endif
+	FreeBoxListMemory(&solderside);
+	FreeBoxListMemory(&componentside);
+	/* reward pin/pad x/y alignment */
+	/* score higher if pins/pads belong to same *type* of component */
+	/* XXX: subkey should be *distance* from thing aligned with, so that
+	 * aligning to something far away isn't profitable */
+	{
+		/* create r tree */
+		PointerListType seboxes = { 0, 0, NULL }
+		, ceboxes = {
+		0, 0, NULL};
+		struct ebox {
+			BoxType box;
+			ElementTypePtr element;
+		};
+		direction_t dir[4] = { NORTH, EAST, SOUTH, WEST };
+		struct ebox **boxpp, *boxp;
+		rtree_t *rt_s, *rt_c;
+		int factor;
+		ELEMENT_LOOP(PCB->Data);
+		{
+			boxpp = (struct ebox **)
+				GetPointerMemory(TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? &seboxes : &ceboxes);
+			*boxpp = (struct ebox *) malloc(sizeof(**boxpp));
+			if (*boxpp == NULL) {
+				fprintf(stderr, "malloc() failed in ComputeCost\n");
+				exit(1);
+			}
+
+			(*boxpp)->box = element->VBox;
+			(*boxpp)->element = element;
+		}
+		END_LOOP;
+		rt_s = r_create_tree((const BoxType **) seboxes.Ptr, seboxes.PtrN, 1);
+		rt_c = r_create_tree((const BoxType **) ceboxes.Ptr, ceboxes.PtrN, 1);
+		FreePointerListMemory(&seboxes);
+		FreePointerListMemory(&ceboxes);
+		/* now, for each element, find its neighbor on all four sides */
+		delta4 = 0;
+		for (i = 0; i < 4; i++)
+			ELEMENT_LOOP(PCB->Data);
+		{
+			boxp = (struct ebox *)
+				r_find_neighbor(TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? rt_s : rt_c, &element->VBox, dir[i]);
+			/* score bounding box alignments */
+			if (!boxp)
+				continue;
+			factor = 1;
+			if (element->Name[0].TextString &&
+					boxp->element->Name[0].TextString && 0 == NSTRCMP(element->Name[0].TextString, boxp->element->Name[0].TextString)) {
+				delta4 += CostParameter.matching_neighbor_bonus;
+				factor++;
+			}
+			if (element->Name[0].Direction == boxp->element->Name[0].Direction)
+				delta4 += factor * CostParameter.oriented_neighbor_bonus;
+			if (element->VBox.X1 == boxp->element->VBox.X1 ||
+					element->VBox.X1 == boxp->element->VBox.X2 ||
+					element->VBox.X2 == boxp->element->VBox.X1 ||
+					element->VBox.X2 == boxp->element->VBox.X2 ||
+					element->VBox.Y1 == boxp->element->VBox.Y1 ||
+					element->VBox.Y1 == boxp->element->VBox.Y2 ||
+					element->VBox.Y2 == boxp->element->VBox.Y1 || element->VBox.Y2 == boxp->element->VBox.Y2)
+				delta4 += factor * CostParameter.aligned_neighbor_bonus;
+		}
+		END_LOOP;
+		/* free k-d tree memory */
+		r_destroy_tree(&rt_s);
+		r_destroy_tree(&rt_c);
+	}
+	/* penalize total area used by this layout */
+	{
+		Coord minX = MAX_COORD, minY = MAX_COORD;
+		Coord maxX = -MAX_COORD, maxY = -MAX_COORD;
+		ELEMENT_LOOP(PCB->Data);
+		{
+			MAKEMIN(minX, element->VBox.X1);
+			MAKEMIN(minY, element->VBox.Y1);
+			MAKEMAX(maxX, element->VBox.X2);
+			MAKEMAX(maxY, element->VBox.Y2);
+		}
+		END_LOOP;
+		if (minX < maxX && minY < maxY)
+			delta5 = CostParameter.overall_area_penalty * sqrt(PCB_COORD_TO_MIL(maxX - minX) * PCB_COORD_TO_MIL(maxY - minY));
+	}
+	if (T == 5) {
+		T = W + delta1 + delta2 + delta3 - delta4 + delta5;
+		printf("cost components are %.3f %.3f %.3f %.3f %.3f %.3f\n",
+					 W / T, delta1 / T, delta2 / T, delta3 / T, -delta4 / T, delta5 / T);
+	}
+	/* done! */
+	return W + (delta1 + delta2 + delta3 - delta4 + delta5);
+}
+
+/* ---------------------------------------------------------------------------
+ * Perturb:
+ *  1) flip SMD from solder side to component side or vice-versa.
+ *  2) rotate component 90, 180, or 270 degrees.
+ *  3) shift component random + or - amount in random direction.
+ *     (magnitude of shift decreases over time)
+ *  -- Only perturb selected elements (need count/list of selected?) --
+ */
+PerturbationType createPerturbation(PointerListTypePtr selected, double T)
+{
+	PerturbationType pt = { 0 };
+	/* pick element to perturb */
+	pt.element = (ElementTypePtr) selected->Ptr[pcb_rand() % selected->PtrN];
+	/* exchange, flip/rotate or shift? */
+	switch (pcb_rand() % ((selected->PtrN > 1) ? 3 : 2)) {
+	case 0:
+		{														/* shift! */
+			Coord grid;
+			double scaleX = PCB_CLAMP(sqrt(T), PCB_MIL_TO_COORD(2.5), PCB->MaxWidth / 3);
+			double scaleY = PCB_CLAMP(sqrt(T), PCB_MIL_TO_COORD(2.5), PCB->MaxHeight / 3);
+			pt.which = SHIFT;
+			pt.DX = scaleX * 2 * ((((double) pcb_rand()) / RAND_MAX) - 0.5);
+			pt.DY = scaleY * 2 * ((((double) pcb_rand()) / RAND_MAX) - 0.5);
+			/* snap to grid. different grids for "high" and "low" T */
+			grid = (T > PCB_MIL_TO_COORD(10)) ? CostParameter.large_grid_size : CostParameter.small_grid_size;
+			/* (round away from zero) */
+			pt.DX = ((pt.DX / grid) + SGN(pt.DX)) * grid;
+			pt.DY = ((pt.DY / grid) + SGN(pt.DY)) * grid;
+			/* limit DX/DY so we don't fall off board */
+			pt.DX = MAX(pt.DX, -pt.element->VBox.X1);
+			pt.DX = MIN(pt.DX, PCB->MaxWidth - pt.element->VBox.X2);
+			pt.DY = MAX(pt.DY, -pt.element->VBox.Y1);
+			pt.DY = MIN(pt.DY, PCB->MaxHeight - pt.element->VBox.Y2);
+			/* all done but the movin' */
+			break;
+		}
+	case 1:
+		{														/* flip/rotate! */
+			/* only flip if it's an SMD component */
+			pcb_bool isSMD = padlist_length(&(pt.element->Pad)) != 0;
+			pt.which = ROTATE;
+			pt.rotate = isSMD ? (pcb_rand() & 3) : (1 + (pcb_rand() % 3));
+			/* 0 - flip; 1-3, rotate. */
+			break;
+		}
+	case 2:
+		{														/* exchange! */
+			pt.which = EXCHANGE;
+			pt.other = (ElementTypePtr)
+				selected->Ptr[pcb_rand() % (selected->PtrN - 1)];
+			if (pt.other == pt.element)
+				pt.other = (ElementTypePtr) selected->Ptr[selected->PtrN - 1];
+			/* don't allow exchanging a solderside-side SMD component
+			 * with a non-SMD component. */
+			if ((pinlist_length(&(pt.element->Pin)) != 0 /* non-SMD */  &&
+					 TEST_FLAG(PCB_FLAG_ONSOLDER, pt.other)) || (pinlist_length(&pt.other->Pin) != 0 /* non-SMD */  &&
+																									TEST_FLAG(PCB_FLAG_ONSOLDER, pt.element)))
+				return createPerturbation(selected, T);
+			break;
+		}
+	default:
+		assert(0);
+	}
+	return pt;
+}
+
+void doPerturb(PerturbationType * pt, pcb_bool undo)
+{
+	Coord bbcx, bbcy;
+	/* compute center of element bounding box */
+	bbcx = (pt->element->VBox.X1 + pt->element->VBox.X2) / 2;
+	bbcy = (pt->element->VBox.Y1 + pt->element->VBox.Y2) / 2;
+	/* do exchange, shift or flip/rotate */
+	switch (pt->which) {
+	case SHIFT:
+		{
+			Coord DX = pt->DX, DY = pt->DY;
+			if (undo) {
+				DX = -DX;
+				DY = -DY;
+			}
+			MoveElementLowLevel(PCB->Data, pt->element, DX, DY);
+			return;
+		}
+	case ROTATE:
+		{
+			unsigned b = pt->rotate;
+			if (undo)
+				b = (4 - b) & 3;
+			/* 0 - flip; 1-3, rotate. */
+			if (b)
+				RotateElementLowLevel(PCB->Data, pt->element, bbcx, bbcy, b);
+			else {
+				Coord y = pt->element->VBox.Y1;
+				MirrorElementCoordinates(PCB->Data, pt->element, 0);
+				/* mirroring moves the element.  move it back. */
+				MoveElementLowLevel(PCB->Data, pt->element, 0, y - pt->element->VBox.Y1);
+			}
+			return;
+		}
+	case EXCHANGE:
+		{
+			/* first exchange positions */
+			Coord x1 = pt->element->VBox.X1;
+			Coord y1 = pt->element->VBox.Y1;
+			Coord x2 = pt->other->BoundingBox.X1;
+			Coord y2 = pt->other->BoundingBox.Y1;
+			MoveElementLowLevel(PCB->Data, pt->element, x2 - x1, y2 - y1);
+			MoveElementLowLevel(PCB->Data, pt->other, x1 - x2, y1 - y2);
+			/* then flip both elements if they are on opposite sides */
+			if (TEST_FLAG(PCB_FLAG_ONSOLDER, pt->element) != TEST_FLAG(PCB_FLAG_ONSOLDER, pt->other)) {
+				PerturbationType mypt;
+				mypt.element = pt->element;
+				mypt.which = ROTATE;
+				mypt.rotate = 0;				/* flip */
+				doPerturb(&mypt, undo);
+				mypt.element = pt->other;
+				doPerturb(&mypt, undo);
+			}
+			/* done */
+			return;
+		}
+	default:
+		assert(0);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * Auto-place selected components.
+ */
+pcb_bool AutoPlaceSelected(void)
+{
+	NetListTypePtr Nets;
+	PointerListType Selected = { 0, 0, NULL };
+	PerturbationType pt;
+	double C0, T0;
+	pcb_bool changed = pcb_false;
+
+	/* (initial netlist processing copied from AddAllRats) */
+	/* the netlist library has the text form
+	 * ProcNetlist fills in the Netlist
+	 * structure the way the final routing
+	 * is supposed to look
+	 */
+	Nets = ProcNetlist(&(PCB->NetlistLib[NETLIST_EDITED]));
+	if (!Nets) {
+		Message(PCB_MSG_DEFAULT, _("Can't add rat lines because no netlist is loaded.\n"));
+		goto done;
+	}
+
+	Selected = collectSelectedElements();
+	if (Selected.PtrN == 0) {
+		Message(PCB_MSG_DEFAULT, _("No elements selected to autoplace.\n"));
+		goto done;
+	}
+
+	/* simulated annealing */
+	{															/* compute T0 by doing a random series of moves. */
+		const int TRIALS = 10;
+		const double Tx = PCB_MIL_TO_COORD(300), P = 0.95;
+		double Cs = 0.0;
+		int i;
+		C0 = ComputeCost(Nets, Tx, Tx);
+		for (i = 0; i < TRIALS; i++) {
+			pt = createPerturbation(&Selected, PCB_INCH_TO_COORD(1));
+			doPerturb(&pt, pcb_false);
+			Cs += fabs(ComputeCost(Nets, Tx, Tx) - C0);
+			doPerturb(&pt, pcb_true);
+		}
+		T0 = -(Cs / TRIALS) / log(P);
+		printf("Initial T: %f\n", T0);
+	}
+	/* now anneal in earnest */
+	{
+		double T = T0;
+		long steps = 0;
+		int good_moves = 0, moves = 0;
+		const int good_move_cutoff = CostParameter.m * Selected.PtrN;
+		const int move_cutoff = 2 * good_move_cutoff;
+		printf("Starting cost is %.0f\n", ComputeCost(Nets, T0, 5));
+		C0 = ComputeCost(Nets, T0, T);
+		while (1) {
+			double Cprime;
+			pt = createPerturbation(&Selected, T);
+			doPerturb(&pt, pcb_false);
+			Cprime = ComputeCost(Nets, T0, T);
+			if (Cprime < C0) {				/* good move! */
+				C0 = Cprime;
+				good_moves++;
+				steps++;
+			}
+			else if ((pcb_rand() / (double) RAND_MAX) < exp(MIN(MAX(-20, (C0 - Cprime) / T), 20))) {
+				/* not good but keep it anyway */
+				C0 = Cprime;
+				steps++;
+			}
+			else
+				doPerturb(&pt, pcb_true);		/* undo last change */
+			moves++;
+			/* are we at the end of a stage? */
+			if (good_moves >= good_move_cutoff || moves >= move_cutoff) {
+				printf("END OF STAGE: COST %.0f\t" "GOOD_MOVES %d\tMOVES %d\t" "T: %.1f\n", C0, good_moves, moves, T);
+				/* is this the end? */
+				if (T < 5 || good_moves < moves / CostParameter.good_ratio)
+					break;
+				/* nope, adjust T and continue */
+				moves = good_moves = 0;
+				T *= CostParameter.gamma;
+				/* cost is T dependent, so recompute */
+				C0 = ComputeCost(Nets, T0, T);
+			}
+		}
+		changed = (steps > 0);
+	}
+done:
+	if (changed) {
+		DeleteRats(pcb_false);
+		AddAllRats(pcb_false, NULL);
+		Redraw();
+	}
+	FreePointerListMemory(&Selected);
+	return (changed);
+}
diff --git a/src_plugins/autoplace/autoplace.h b/src_plugins/autoplace/autoplace.h
new file mode 100644
index 0000000..19b894f
--- /dev/null
+++ b/src_plugins/autoplace/autoplace.h
@@ -0,0 +1,40 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, autoplace.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* prototypes for autoplace routines */
+
+#ifndef PCB_AUTOPLACE_H
+#define PCB_AUTOPLACE_H
+
+#include "global.h"
+
+pcb_bool AutoPlaceSelected(void);
+
+#endif
diff --git a/src_plugins/autoroute/Makefile b/src_plugins/autoroute/Makefile
new file mode 100644
index 0000000..380ab39
--- /dev/null
+++ b/src_plugins/autoroute/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_autoroute
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/autoroute/Plug.tmpasm b/src_plugins/autoroute/Plug.tmpasm
new file mode 100644
index 0000000..ab0e9cd
--- /dev/null
+++ b/src_plugins/autoroute/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {autoroute}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/autoroute/autoroute.o $(PLUGDIR)/autoroute/mtspace.o $(PLUGDIR)/autoroute/action.o $(PLUGDIR)/autoroute/vector.o @]
+
+switch /local/pcb/autoroute/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/autoroute/README b/src_plugins/autoroute/README
new file mode 100644
index 0000000..f40347e
--- /dev/null
+++ b/src_plugins/autoroute/README
@@ -0,0 +1,5 @@
+Automatically route selected or all rats. This is the original autorouter.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/autoroute/action.c b/src_plugins/autoroute/action.c
new file mode 100644
index 0000000..b5eccac
--- /dev/null
+++ b/src_plugins/autoroute/action.c
@@ -0,0 +1,103 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "autoroute.h"
+#include "action_helper.h"
+#include "plugins.h"
+#include "set.h"
+#include "hid_actions.h"
+
+/* action routines for the autorouter
+ */
+
+static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
+
+static const char autoroute_help[] = "Auto-route some or all rat lines.";
+
+/* %start-doc actions AutoRoute
+
+ at table @code
+
+ at item AllRats
+Attempt to autoroute all rats.
+
+ at item SelectedRats
+Attempt to autoroute the selected rats.
+
+ at end table
+
+Before autorouting, it's important to set up a few things.  First,
+make sure any layers you aren't using are disabled, else the
+autorouter may use them.  Next, make sure the current line and via
+styles are set accordingly.  Last, make sure "new lines clear
+polygons" is set, in case you eventually want to add a copper pour.
+
+Autorouting takes a while.  During this time, the program may not be
+responsive.
+
+%end-doc */
+
+static int ActionAutoRoute(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ACTION_ARG(0);
+	hid_action("Busy");
+	if (function) {								/* one parameter */
+		if (strcmp(function, "AllRats") == 0) {
+			if (AutoRoute(pcb_false))
+				SetChangedFlag(pcb_true);
+		}
+		else if ((strcmp(function, "SelectedRats") == 0) || (strcmp(function, "Selected") == 0)) {
+			if (AutoRoute(pcb_true))
+				SetChangedFlag(pcb_true);
+		}
+	}
+	return 0;
+}
+
+static const char *autoroute_cookie = "autoroute plugin";
+
+HID_Action autoroute_action_list[] = {
+	{"AutoRoute", 0, ActionAutoRoute,
+	 autoroute_help, autoroute_syntax}
+	,
+};
+
+REGISTER_ACTIONS(autoroute_action_list, autoroute_cookie)
+
+static void hid_autoroute_uninit(void)
+{
+	hid_remove_actions_by_cookie(autoroute_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_autoroute_init(void)
+{
+	REGISTER_ACTIONS(autoroute_action_list, autoroute_cookie)
+	return hid_autoroute_uninit;
+}
diff --git a/src_plugins/autoroute/autoroute.c b/src_plugins/autoroute/autoroute.c
new file mode 100644
index 0000000..e7f7c54
--- /dev/null
+++ b/src_plugins/autoroute/autoroute.c
@@ -0,0 +1,4720 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, autoroute.c, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian
+ *  Copyright (c) 2006 harry eaton
+ *  Copyright (c) 2009 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+/* functions used to autoroute nets.
+ */
+/*
+ *-------------------------------------------------------------------
+ * This file implements a rectangle-expansion router, based on
+ * "A Method for Gridless Routing of Printed Circuit Boards" by
+ * A. C. Finch, K. J. Mackenzie, G. J. Balsdon, and G. Symonds in the
+ * 1985 Proceedings of the 22nd ACM/IEEE Design Automation Conference.
+ * This reference is available from the ACM Digital Library at
+ * http://www.acm.org/dl for those with institutional or personal
+ * access to it.  It's also available from your local engineering
+ * library.
+ *
+ * The code is much closer to what is described in the paper now,
+ * in that expansion areas can grow from corners and in all directions
+ * at once. Previously, these were emulated with discrete boxes moving
+ * in the cardinal directions. With the new method, there are fewer but
+ * larger expansion boxes that one might do a better job of routing in.
+ *--------------------------------------------------------------------
+ */
+#define NET_HEAP 1
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+
+#include <assert.h>
+#include <setjmp.h>
+
+#include "data.h"
+#include "macro.h"
+#include "autoroute.h"
+#include "box.h"
+#include "create.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "heap.h"
+#include "rtree.h"
+#include "misc.h"
+#include "mtspace.h"
+#include "mymem.h"
+#include "polygon.h"
+#include "rats.h"
+#include "remove.h"
+#include "thermal.h"
+#include "undo.h"
+#include "vector.h"
+#include "pcb-printf.h"
+#include "set.h"
+#include "layer.h"
+
+/* #defines to enable some debugging output */
+/*
+#define ROUTE_VERBOSE
+*/
+
+/*
+#define ROUTE_DEBUG
+//#define DEBUG_SHOW_ROUTE_BOXES
+#define DEBUG_SHOW_EXPANSION_BOXES
+//#define DEBUG_SHOW_EDGES
+//#define DEBUG_SHOW_VIA_BOXES
+#define DEBUG_SHOW_TARGETS
+#define DEBUG_SHOW_SOURCES
+//#define DEBUG_SHOW_ZIGZAG
+*/
+
+static direction_t directionIncrement(direction_t dir)
+{
+	switch (dir) {
+	case NORTH:
+		dir = EAST;
+		break;
+	case EAST:
+		dir = SOUTH;
+		break;
+	case SOUTH:
+		dir = WEST;
+		break;
+	case WEST:
+		dir = NE;
+		break;
+	case NE:
+		dir = SE;
+		break;
+	case SE:
+		dir = SW;
+		break;
+	case SW:
+		dir = NW;
+		break;
+	case NW:
+		dir = ALL;
+		break;
+	case ALL:
+		dir = NORTH;
+		break;
+	}
+	return dir;
+}
+
+#ifdef ROUTE_DEBUG
+HID *ddraw = NULL;
+static hidGC ar_gc = 0;
+#endif
+
+#define EXPENSIVE 3e28
+/* round up "half" thicknesses */
+#define HALF_THICK(x) (((x)+1)/2)
+/* a styles maximum bloat is its clearance plus the larger of its via radius
+ * or line half-thickness. */
+#define BLOAT(style)\
+	((style)->Clearance + HALF_THICK((style)->Thick))
+/* conflict penalty is less for traces laid down during previous pass than
+ * it is for traces already laid down in this pass. */
+#define CONFLICT_LEVEL(rb)\
+	(((rb)->flags.is_odd==AutoRouteParameters.is_odd) ?\
+	 HI_CONFLICT : LO_CONFLICT )
+#define CONFLICT_PENALTY(rb)\
+	((CONFLICT_LEVEL(rb)==HI_CONFLICT ? \
+	 AutoRouteParameters.ConflictPenalty : \
+	 CONFLICT_LEVEL(rb)==LO_CONFLICT ? \
+	 AutoRouteParameters.LastConflictPenalty : 1) * (rb)->pass)
+
+#define _NORTH 1
+#define _EAST 2
+#define _SOUTH 4
+#define _WEST 8
+
+#define LIST_LOOP(init, which, x) do {\
+     routebox_t *__next_one__ = (init);\
+   x = NULL;\
+   if (!__next_one__)\
+     assert(__next_one__);\
+   else\
+   while (!x  || __next_one__ != (init)) {\
+     x = __next_one__;\
+     /* save next one first in case the command modifies or frees it */\
+     __next_one__ = x->which.next
+#define FOREACH_SUBNET(net, p) do {\
+  routebox_t *_pp_;\
+  /* fail-fast: check subnet_processed flags */\
+  LIST_LOOP(net, same_net, p); \
+  assert(!p->flags.subnet_processed);\
+  END_LOOP;\
+  /* iterate through *distinct* subnets */\
+  LIST_LOOP(net, same_net, p); \
+  if (!p->flags.subnet_processed) {\
+    LIST_LOOP(p, same_subnet, _pp_);\
+    _pp_->flags.subnet_processed=1;\
+    END_LOOP
+#define END_FOREACH(net, p) \
+  }; \
+  END_LOOP;\
+  /* reset subnet_processed flags */\
+  LIST_LOOP(net, same_net, p); \
+  p->flags.subnet_processed=0;\
+  END_LOOP;\
+} while (0)
+#define SWAP(t, f, s) do { t a=s; s=f; f=a; } while (0)
+/* notes:
+ * all rectangles are assumed to be closed on the top and left and
+ * open on the bottom and right.   That is, they include their top-left
+ * corner but don't include their bottom and right edges.
+ *
+ * expansion regions are always half-closed.  This means that when
+ * tracing paths, you must steer clear of the bottom and right edges.,
+ * because these are not actually in the allowed box.
+ *
+ * All routeboxes *except* EXPANSION_AREAS now have their "box" bloated by
+ * their particular required clearance. This simplifies the tree searching.
+ * the "sbox" contains the unbloated box.
+ */
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+
+/* enumerated type for conflict levels */
+typedef enum { NO_CONFLICT = 0, LO_CONFLICT = 1, HI_CONFLICT = 2 } conflict_t;
+
+typedef struct routebox_list {
+	struct routebox *next, *prev;
+} routebox_list;
+
+typedef enum etype { PAD, PIN, VIA, VIA_SHADOW, LINE, OTHER, EXPANSION_AREA, PLANE, THERMAL } etype;
+
+typedef struct routebox {
+	BoxType box, sbox;
+	union {
+		PadTypePtr pad;
+		PinTypePtr pin;
+		PinTypePtr via;
+		struct routebox *via_shadow;	/* points to the via in r-tree which
+																	 * points to the PinType in the PCB. */
+		LineTypePtr line;
+		void *generic;							/* 'other' is polygon, arc, text */
+		struct routebox *expansion_area;	/* previous expansion area in search */
+	} parent;
+	unsigned short group;
+	unsigned short layer;
+	etype type;
+	struct {
+		unsigned nonstraight:1;
+		unsigned fixed:1;
+		/* for searches */
+		unsigned source:1;
+		unsigned target:1;
+		/* rects on same net as source and target don't need clearance areas */
+		unsigned nobloat:1;
+		/* mark circular pins, so that we be sure to connect them up properly */
+		unsigned circular:1;
+		/* we sometimes create routeboxen that don't actually belong to a
+		 * r-tree yet -- make sure refcount of homelesss is set properly */
+		unsigned homeless:1;
+		/* was this nonfixed obstacle generated on an odd or even pass? */
+		unsigned is_odd:1;
+		/* fixed route boxes that have already been "routed through" in this
+		 * search have their "touched" flag set. */
+		unsigned touched:1;
+		/* this is a status bit for iterating through *different* subnets */
+		unsigned subnet_processed:1;
+		/* some expansion_areas represent via candidates */
+		unsigned is_via:1;
+		/* mark non-straight lines which go from bottom-left to upper-right,
+		 * instead of from upper-left to bottom-right. */
+		unsigned bl_to_ur:1;
+		/* mark polygons which are "transparent" for via-placement; that is,
+		 * vias through the polygon will automatically be given a clearance
+		 * and will not electrically connect to the polygon. */
+		unsigned clear_poly:1;
+		/* this marks "conflicting" routes that must be torn up to obtain
+		 * a correct routing.  This flag allows us to return a correct routing
+		 * even if the user cancels auto-route after a non-final pass. */
+		unsigned is_bad:1;
+		/* for assertion that 'box' is never changed after creation */
+		unsigned inited:1;
+		/* indicate this expansion ares is a thermal between the pin and plane */
+		unsigned is_thermal;
+	} flags;
+	/* indicate the direction an expansion box came from */
+	cost_t cost;
+	CheapPointType cost_point;
+	/* reference count for homeless routeboxes; free when refcount==0 */
+	int refcount;
+	/* when routing with conflicts, we keep a record of what we're
+	 * conflicting with.
+	 */
+	vector_t *conflicts_with;
+	/* route style of the net associated with this routebox */
+	RouteStyleType *style;
+	/* congestion values for the edges of an expansion box */
+	unsigned char n, e, s, w;
+	/* what pass this this track was laid down on */
+	unsigned char pass;
+	/* the direction this came from, if any */
+	direction_t came_from;
+	/* circular lists with connectivity information. */
+	routebox_list same_net, same_subnet, original_subnet, different_net;
+	union {
+		PinType *via;
+		LineType *line;
+	} livedraw_obj;
+} routebox_t;
+
+typedef struct routedata {
+	int max_styles;
+	/* one rtree per layer *group */
+	rtree_t *layergrouptree[MAX_LAYER];	/* no silkscreen layers here =) */
+	/* root pointer into connectivity information */
+	routebox_t *first_net;
+	/* default routing style */
+	RouteStyleType defaultstyle;
+	/* style structures */
+	RouteStyleType **styles; /* [max_styles+1] */
+	/* what is the maximum bloat (clearance+line half-width or
+	 * clearance+via_radius) for any style we've seen? */
+	Coord max_bloat;
+	Coord max_keep;
+	mtspace_t *mtspace;
+} routedata_t;
+
+typedef struct edge_struct {
+	routebox_t *rb;								/* path expansion edges are real routeboxen. */
+	CheapPointType cost_point;
+	cost_t cost_to_point;					/* from source */
+	cost_t cost;									/* cached edge cost */
+	routebox_t *mincost_target;		/* minimum cost from cost_point to any target */
+	vetting_t *work;							/* for via search edges */
+	direction_t expand_dir;
+	struct {
+		/* this indicates that this 'edge' is a via candidate. */
+		unsigned is_via:1;
+		/* record "conflict level" of via candidates, in case we need to split
+		 * them later. */
+		conflict_t via_conflict_level:2;
+		/* when "routing with conflicts", sometimes edge is interior. */
+		unsigned is_interior:1;
+		/* this is a fake edge used to defer searching for via spaces */
+		unsigned via_search:1;
+		/* this is a via edge in a plane where the cost point moves for free */
+		unsigned in_plane:1;
+	} flags;
+} edge_t;
+
+static struct {
+	/* net style parameters */
+	RouteStyleType *style;
+	/* the present bloat */
+	Coord bloat;
+	/* cost parameters */
+	cost_t ViaCost,								/* additional "length" cost for using a via */
+	  LastConflictPenalty,				/* length mult. for routing over last pass' trace */
+	  ConflictPenalty,						/* length multiplier for routing over another trace */
+	  JogPenalty,									/* additional "length" cost for changing direction */
+	  CongestionPenalty,					/* (rational) length multiplier for routing in */
+	  NewLayerPenalty,						/* penalty for routing on a previously unused layer */
+	  MinPenalty;									/* smallest Direction Penalty */
+	/* maximum conflict incidence before calling it "no path found" */
+	int hi_conflict;
+	/* are vias allowed? */
+	pcb_bool use_vias;
+	/* is this an odd or even pass? */
+	pcb_bool is_odd;
+	/* permit conflicts? */
+	pcb_bool with_conflicts;
+	/* is this a final "smoothing" pass? */
+	pcb_bool is_smoothing;
+	/* rip up nets regardless of conflicts? */
+	pcb_bool rip_always;
+	pcb_bool last_smooth;
+	unsigned char pass;
+} AutoRouteParameters;
+
+struct routeone_state {
+	/* heap of all candidate expansion edges */
+	heap_t *workheap;
+	/* information about the best path found so far. */
+	routebox_t *best_path, *best_target;
+	cost_t best_cost;
+};
+
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static routebox_t *CreateExpansionArea(const BoxType * area, pcb_cardinal_t group,
+																			 routebox_t * parent, pcb_bool relax_edge_requirements, edge_t * edge);
+
+static cost_t edge_cost(const edge_t * e, const cost_t too_big);
+static void best_path_candidate(struct routeone_state *s, edge_t * e, routebox_t * best_target);
+
+static BoxType edge_to_box(const routebox_t * rb, direction_t expand_dir);
+
+static void add_or_destroy_edge(struct routeone_state *s, edge_t * e);
+
+static void
+RD_DrawThermal(routedata_t * rd, Coord X, Coord Y, pcb_cardinal_t group, pcb_cardinal_t layer, routebox_t * subnet, pcb_bool is_bad);
+static void ResetSubnet(routebox_t * net);
+#ifdef ROUTE_DEBUG
+static int showboxen = -2;
+static int aabort = 0;
+static void showroutebox(routebox_t * rb);
+#endif
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+/* group number of groups that hold surface mount pads */
+static pcb_cardinal_t front, back;
+static pcb_bool usedGroup[MAX_LAYER];
+static int x_cost[MAX_LAYER], y_cost[MAX_LAYER];
+static pcb_bool is_layer_group_active[MAX_LAYER];
+static int ro = 0;
+static int smoothes = 1;
+static int passes = 12;
+static int routing_layers = 0;
+static float total_wire_length = 0;
+static int total_via_count = 0;
+
+/* assertion helper for routeboxen */
+#ifndef NDEBUG
+static int __routebox_is_good(routebox_t * rb)
+{
+	assert(rb && (rb->group < max_group) &&
+				 (rb->box.X1 <= rb->box.X2) && (rb->box.Y1 <= rb->box.Y2) &&
+				 (rb->flags.homeless ?
+					(rb->box.X1 != rb->box.X2) || (rb->box.Y1 != rb->box.Y2) : (rb->box.X1 != rb->box.X2) && (rb->box.Y1 != rb->box.Y2)));
+	assert((rb->flags.source ? rb->flags.nobloat : 1) &&
+				 (rb->flags.target ? rb->flags.nobloat : 1) &&
+				 (rb->flags.homeless ? !rb->flags.touched : rb->refcount == 0) && (rb->flags.touched ? rb->type != EXPANSION_AREA : 1));
+	assert((rb->flags.is_odd ? (!rb->flags.fixed) &&
+					(rb->type == VIA || rb->type == VIA_SHADOW || rb->type == LINE || rb->type == PLANE) : 1));
+	assert(rb->flags.clear_poly ? ((rb->type == OTHER || rb->type == PLANE) && rb->flags.fixed && !rb->flags.homeless) : 1);
+	assert(rb->flags.inited);
+/* run through conflict list showing none are homeless, targets or sources */
+	if (rb->conflicts_with) {
+		int i;
+		for (i = 0; i < vector_size(rb->conflicts_with); i++) {
+			routebox_t *c = vector_element(rb->conflicts_with, i);
+			assert(!c->flags.homeless && !c->flags.source && !c->flags.target && !c->flags.fixed);
+		}
+	}
+	assert(rb->style != NULL && rb->style != NULL);
+	assert(rb->type == EXPANSION_AREA
+				 || (rb->same_net.next && rb->same_net.prev && rb->same_subnet.next
+						 && rb->same_subnet.prev && rb->original_subnet.next
+						 && rb->original_subnet.prev && rb->different_net.next && rb->different_net.prev));
+	return 1;
+}
+
+static int __edge_is_good(edge_t * e)
+{
+	assert(e && e->rb && __routebox_is_good(e->rb));
+	assert((e->rb->flags.homeless ? e->rb->refcount > 0 : 1));
+	assert((0 <= e->expand_dir) && (e->expand_dir < 9)
+				 && (e->flags.is_interior ? (e->expand_dir == ALL && e->rb->conflicts_with) : 1));
+	assert((e->flags.is_via ? e->rb->flags.is_via : 1)
+				 && (e->flags.via_conflict_level >= 0 && e->flags.via_conflict_level <= 2)
+				 && (e->flags.via_conflict_level != 0 ? e->flags.is_via : 1));
+	assert((e->cost_to_point >= 0) && e->cost >= 0);
+	return 1;
+}
+
+int no_planes(const BoxType * b, void *cl)
+{
+	routebox_t *rb = (routebox_t *) b;
+	if (rb->type == PLANE)
+		return 0;
+	return 1;
+}
+#endif /* !NDEBUG */
+
+/*---------------------------------------------------------------------
+ * route utility functions.
+ */
+
+enum boxlist { NET, SUBNET, ORIGINAL, DIFFERENT_NET };
+static struct routebox_list *__select_list(routebox_t * r, enum boxlist which)
+{
+	assert(r);
+	switch (which) {
+	default:
+		assert(0);
+	case NET:
+		return &(r->same_net);
+	case SUBNET:
+		return &(r->same_subnet);
+	case ORIGINAL:
+		return &(r->original_subnet);
+	case DIFFERENT_NET:
+		return &(r->different_net);
+	}
+}
+
+static void InitLists(routebox_t * r)
+{
+	static enum boxlist all[] = { NET, SUBNET, ORIGINAL, DIFFERENT_NET }
+	, *p;
+	for (p = all; p < all + (sizeof(all) / sizeof(*p)); p++) {
+		struct routebox_list *rl = __select_list(r, *p);
+		rl->prev = rl->next = r;
+	}
+}
+
+static void MergeNets(routebox_t * a, routebox_t * b, enum boxlist which)
+{
+	struct routebox_list *al, *bl, *anl, *bnl;
+	routebox_t *an, *bn;
+	assert(a && b);
+	assert(a != b);
+	assert(a->type != EXPANSION_AREA);
+	assert(b->type != EXPANSION_AREA);
+	al = __select_list(a, which);
+	bl = __select_list(b, which);
+	assert(al && bl);
+	an = al->next;
+	bn = bl->next;
+	assert(an && bn);
+	anl = __select_list(an, which);
+	bnl = __select_list(bn, which);
+	assert(anl && bnl);
+	bl->next = an;
+	anl->prev = b;
+	al->next = bn;
+	bnl->prev = a;
+}
+
+static void RemoveFromNet(routebox_t * a, enum boxlist which)
+{
+	struct routebox_list *al, *anl, *apl;
+	routebox_t *an, *ap;
+	assert(a);
+	al = __select_list(a, which);
+	assert(al);
+	an = al->next;
+	ap = al->prev;
+	if (an == a || ap == a)
+		return;											/* not on any list */
+	assert(an && ap);
+	anl = __select_list(an, which);
+	apl = __select_list(ap, which);
+	assert(anl && apl);
+	anl->prev = ap;
+	apl->next = an;
+	al->next = al->prev = a;
+	return;
+}
+
+static void init_const_box(routebox_t * rb, Coord X1, Coord Y1, Coord X2, Coord Y2, Coord clearance)
+{
+	BoxType *bp = (BoxType *) & rb->box;	/* note discarding const! */
+	assert(!rb->flags.inited);
+	assert(X1 <= X2 && Y1 <= Y2);
+	bp->X1 = X1 - clearance;
+	bp->Y1 = Y1 - clearance;
+	bp->X2 = X2 + clearance;
+	bp->Y2 = Y2 + clearance;
+	bp = (BoxType *) & rb->sbox;
+	bp->X1 = X1;
+	bp->Y1 = Y1;
+	bp->X2 = X2;
+	bp->Y2 = Y2;
+	rb->flags.inited = 1;
+}
+
+static inline BoxType shrink_routebox(const routebox_t * rb)
+{
+	return rb->sbox;
+}
+
+static inline cost_t box_area(const BoxType b)
+{
+	cost_t ans = b.X2 - b.X1;
+	return ans * (b.Y2 - b.Y1);
+}
+
+static inline CheapPointType closest_point_in_routebox(const CheapPointType * from, const routebox_t * rb)
+{
+	return closest_point_in_box(from, &rb->sbox);
+}
+
+static inline pcb_bool point_in_shrunk_box(const routebox_t * box, Coord X, Coord Y)
+{
+	BoxType b = shrink_routebox(box);
+	return point_in_box(&b, X, Y);
+}
+
+/*---------------------------------------------------------------------
+ * routedata initialization functions.
+ */
+
+static routebox_t *AddPin(PointerListType layergroupboxes[], PinTypePtr pin, pcb_bool is_via, RouteStyleType * style)
+{
+	routebox_t **rbpp, *lastrb = NULL;
+	int i, ht;
+	/* a pin cuts through every layer group */
+	for (i = 0; i < max_group; i++) {
+		rbpp = (routebox_t **) GetPointerMemory(&layergroupboxes[i]);
+		*rbpp = (routebox_t *) malloc(sizeof(**rbpp));
+		memset((void *) *rbpp, 0, sizeof(**rbpp));
+		(*rbpp)->group = i;
+		ht = HALF_THICK(MAX(pin->Thickness, pin->DrillingHole));
+		init_const_box(*rbpp,
+									 /*X1 */ pin->X - ht,
+									 /*Y1 */ pin->Y - ht,
+									 /*X2 */ pin->X + ht,
+									 /*Y2 */ pin->Y + ht, style->Clearance);
+		/* set aux. properties */
+		if (is_via) {
+			(*rbpp)->type = VIA;
+			(*rbpp)->parent.via = pin;
+		}
+		else {
+			(*rbpp)->type = PIN;
+			(*rbpp)->parent.pin = pin;
+		}
+		(*rbpp)->flags.fixed = 1;
+		(*rbpp)->came_from = ALL;
+		(*rbpp)->style = style;
+		(*rbpp)->flags.circular = !TEST_FLAG(PCB_FLAG_SQUARE, pin);
+		/* circular lists */
+		InitLists(*rbpp);
+		/* link together */
+		if (lastrb) {
+			MergeNets(*rbpp, lastrb, NET);
+			MergeNets(*rbpp, lastrb, SUBNET);
+			MergeNets(*rbpp, lastrb, ORIGINAL);
+		}
+		lastrb = *rbpp;
+	}
+	return lastrb;
+}
+
+static routebox_t *AddPad(PointerListType layergroupboxes[], ElementTypePtr element, PadTypePtr pad, RouteStyleType * style)
+{
+	Coord halfthick;
+	routebox_t **rbpp;
+	int layergroup = (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? back : front);
+	assert(0 <= layergroup && layergroup < max_group);
+	assert(PCB->LayerGroups.Number[layergroup] > 0);
+	rbpp = (routebox_t **) GetPointerMemory(&layergroupboxes[layergroup]);
+	assert(rbpp);
+	*rbpp = (routebox_t *) malloc(sizeof(**rbpp));
+	assert(*rbpp);
+	memset(*rbpp, 0, sizeof(**rbpp));
+	(*rbpp)->group = layergroup;
+	halfthick = HALF_THICK(pad->Thickness);
+	init_const_box(*rbpp,
+								 /*X1 */ MIN(pad->Point1.X, pad->Point2.X) - halfthick,
+								 /*Y1 */ MIN(pad->Point1.Y, pad->Point2.Y) - halfthick,
+								 /*X2 */ MAX(pad->Point1.X, pad->Point2.X) + halfthick,
+								 /*Y2 */ MAX(pad->Point1.Y, pad->Point2.Y) + halfthick,
+								 style->Clearance);
+	/* kludge for non-manhattan pads (which are not allowed at present) */
+	if (pad->Point1.X != pad->Point2.X && pad->Point1.Y != pad->Point2.Y)
+		(*rbpp)->flags.nonstraight = 1;
+	/* set aux. properties */
+	(*rbpp)->type = PAD;
+	(*rbpp)->parent.pad = pad;
+	(*rbpp)->flags.fixed = 1;
+	(*rbpp)->came_from = ALL;
+	(*rbpp)->style = style;
+	/* circular lists */
+	InitLists(*rbpp);
+	return *rbpp;
+}
+
+static routebox_t *AddLine(PointerListType layergroupboxes[], int layergroup, LineTypePtr line,
+													 LineTypePtr ptr, RouteStyleType * style)
+{
+	routebox_t **rbpp;
+	assert(layergroupboxes && line);
+	assert(0 <= layergroup && layergroup < max_group);
+	assert(PCB->LayerGroups.Number[layergroup] > 0);
+
+	rbpp = (routebox_t **) GetPointerMemory(&layergroupboxes[layergroup]);
+	*rbpp = (routebox_t *) malloc(sizeof(**rbpp));
+	memset(*rbpp, 0, sizeof(**rbpp));
+	(*rbpp)->group = layergroup;
+	init_const_box(*rbpp,
+								 /*X1 */ MIN(line->Point1.X,
+														 line->Point2.X) - HALF_THICK(line->Thickness),
+								 /*Y1 */ MIN(line->Point1.Y,
+														 line->Point2.Y) - HALF_THICK(line->Thickness),
+								 /*X2 */ MAX(line->Point1.X,
+														 line->Point2.X) + HALF_THICK(line->Thickness),
+								 /*Y2 */ MAX(line->Point1.Y,
+														 line->Point2.Y) + HALF_THICK(line->Thickness), style->Clearance);
+	/* kludge for non-manhattan lines */
+	if (line->Point1.X != line->Point2.X && line->Point1.Y != line->Point2.Y) {
+		(*rbpp)->flags.nonstraight = 1;
+		(*rbpp)->flags.bl_to_ur =
+			(MIN(line->Point1.X, line->Point2.X) == line->Point1.X) != (MIN(line->Point1.Y, line->Point2.Y) == line->Point1.Y);
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_ZIGZAG)
+		showroutebox(*rbpp);
+#endif
+	}
+	/* set aux. properties */
+	(*rbpp)->type = LINE;
+	(*rbpp)->parent.line = ptr;
+	(*rbpp)->flags.fixed = 1;
+	(*rbpp)->came_from = ALL;
+	(*rbpp)->style = style;
+	/* circular lists */
+	InitLists(*rbpp);
+	return *rbpp;
+}
+
+static routebox_t *AddIrregularObstacle(PointerListType layergroupboxes[],
+																				Coord X1, Coord Y1,
+																				Coord X2, Coord Y2, pcb_cardinal_t layergroup, void *parent, RouteStyleType * style)
+{
+	routebox_t **rbpp;
+	Coord keep = style->Clearance;
+	assert(layergroupboxes && parent);
+	assert(X1 <= X2 && Y1 <= Y2);
+	assert(0 <= layergroup && layergroup < max_group);
+	assert(PCB->LayerGroups.Number[layergroup] > 0);
+
+	rbpp = (routebox_t **) GetPointerMemory(&layergroupboxes[layergroup]);
+	*rbpp = (routebox_t *) malloc(sizeof(**rbpp));
+	memset(*rbpp, 0, sizeof(**rbpp));
+	(*rbpp)->group = layergroup;
+	init_const_box(*rbpp, X1, Y1, X2, Y2, keep);
+	(*rbpp)->flags.nonstraight = 1;
+	(*rbpp)->type = OTHER;
+	(*rbpp)->parent.generic = parent;
+	(*rbpp)->flags.fixed = 1;
+	(*rbpp)->style = style;
+	/* circular lists */
+	InitLists(*rbpp);
+	return *rbpp;
+}
+
+static routebox_t *AddPolygon(PointerListType layergroupboxes[], pcb_cardinal_t layer, PolygonTypePtr polygon, RouteStyleType * style)
+{
+	int is_not_rectangle = 1;
+	int layergroup = GetLayerGroupNumberByNumber(layer);
+	routebox_t *rb;
+	assert(0 <= layergroup && layergroup < max_group);
+	rb = AddIrregularObstacle(layergroupboxes,
+														polygon->BoundingBox.X1,
+														polygon->BoundingBox.Y1,
+														polygon->BoundingBox.X2, polygon->BoundingBox.Y2, layergroup, polygon, style);
+	if (polygon->PointN == 4 &&
+			polygon->HoleIndexN == 0 &&
+			(polygon->Points[0].X == polygon->Points[1].X ||
+			 polygon->Points[0].Y == polygon->Points[1].Y) &&
+			(polygon->Points[1].X == polygon->Points[2].X ||
+			 polygon->Points[1].Y == polygon->Points[2].Y) &&
+			(polygon->Points[2].X == polygon->Points[3].X ||
+			 polygon->Points[2].Y == polygon->Points[3].Y) &&
+			(polygon->Points[3].X == polygon->Points[0].X || polygon->Points[3].Y == polygon->Points[0].Y))
+		is_not_rectangle = 0;
+	rb->flags.nonstraight = is_not_rectangle;
+	rb->layer = layer;
+	rb->came_from = ALL;
+	if (TEST_FLAG(PCB_FLAG_CLEARPOLY, polygon)) {
+		rb->flags.clear_poly = 1;
+		if (!is_not_rectangle)
+			rb->type = PLANE;
+	}
+	return rb;
+}
+
+static void AddText(PointerListType layergroupboxes[], pcb_cardinal_t layergroup, TextTypePtr text, RouteStyleType * style)
+{
+	AddIrregularObstacle(layergroupboxes,
+											 text->BoundingBox.X1, text->BoundingBox.Y1,
+											 text->BoundingBox.X2, text->BoundingBox.Y2, layergroup, text, style);
+}
+
+static routebox_t *AddArc(PointerListType layergroupboxes[], pcb_cardinal_t layergroup, ArcTypePtr arc, RouteStyleType * style)
+{
+	return AddIrregularObstacle(layergroupboxes,
+															arc->BoundingBox.X1, arc->BoundingBox.Y1,
+															arc->BoundingBox.X2, arc->BoundingBox.Y2, layergroup, arc, style);
+}
+
+struct rb_info {
+	BoxType query;
+	routebox_t *winner;
+	jmp_buf env;
+};
+
+static r_dir_t __found_one_on_lg(const BoxType * box, void *cl)
+{
+	struct rb_info *inf = (struct rb_info *) cl;
+	routebox_t *rb = (routebox_t *) box;
+	BoxType sb;
+
+	if (rb->flags.nonstraight)
+		return R_DIR_NOT_FOUND;
+	sb = shrink_box(&rb->box, rb->style->Clearance);
+	if (inf->query.X1 >= sb.X2 || inf->query.X2 <= sb.X1 || inf->query.Y1 >= sb.Y2 || inf->query.Y2 <= sb.Y1)
+		return R_DIR_NOT_FOUND;
+	inf->winner = rb;
+	if (rb->type == PLANE)
+		return R_DIR_FOUND_CONTINUE;										/* keep looking for something smaller if a plane was found */
+	longjmp(inf->env, 1);
+	return R_DIR_NOT_FOUND;
+}
+
+static routebox_t *FindRouteBoxOnLayerGroup(routedata_t * rd, Coord X, Coord Y, pcb_cardinal_t layergroup)
+{
+	struct rb_info info;
+	info.winner = NULL;
+	info.query = point_box(X, Y);
+	if (setjmp(info.env) == 0)
+		r_search(rd->layergrouptree[layergroup], &info.query, NULL, __found_one_on_lg, &info, NULL);
+	return info.winner;
+}
+
+#ifdef ROUTE_DEBUG_VERBOSE
+static void DumpRouteBox(routebox_t * rb)
+{
+	pcb_printf("RB: %#mD-%#mD l%d; ", rb->box.X1, rb->box.Y1, rb->box.X2, rb->box.Y2, (int) rb->group);
+	switch (rb->type) {
+	case PAD:
+		printf("PAD[%s %s] ", rb->parent.pad->Name, rb->parent.pad->Number);
+		break;
+	case PIN:
+		printf("PIN[%s %s] ", rb->parent.pin->Name, rb->parent.pin->Number);
+		break;
+	case VIA:
+		if (!rb->parent.via)
+			break;
+		printf("VIA[%s %s] ", rb->parent.via->Name, rb->parent.via->Number);
+		break;
+	case LINE:
+		printf("LINE ");
+		break;
+	case OTHER:
+		printf("OTHER ");
+		break;
+	case EXPANSION_AREA:
+		printf("EXPAREA ");
+		break;
+	default:
+		printf("UNKNOWN ");
+		break;
+	}
+	if (rb->flags.nonstraight)
+		printf("(nonstraight) ");
+	if (rb->flags.fixed)
+		printf("(fixed) ");
+	if (rb->flags.source)
+		printf("(source) ");
+	if (rb->flags.target)
+		printf("(target) ");
+	if (rb->flags.homeless)
+		printf("(homeless) ");
+	printf("\n");
+}
+#endif
+
+static routedata_t *CreateRouteData()
+{
+	NetListListType Nets;
+	PointerListType layergroupboxes[MAX_LAYER];
+	BoxType bbox;
+	routedata_t *rd;
+	int group, i;
+
+	/* check which layers are active first */
+	routing_layers = 0;
+	for (group = 0; group < max_group; group++) {
+		for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
+			/* layer must be 1) not silk (ie, < max_copper_layer) and 2) on */
+			if ((PCB->LayerGroups.Entries[group][i] < max_copper_layer) && PCB->Data->Layer[PCB->LayerGroups.Entries[group][i]].On) {
+				routing_layers++;
+				is_layer_group_active[group] = pcb_true;
+				break;
+			}
+			else
+				is_layer_group_active[group] = pcb_false;
+	}
+	/* if via visibility is turned off, don't use them */
+	AutoRouteParameters.use_vias = routing_layers > 1 && PCB->ViaOn;
+	front = GetLayerGroupNumberByNumber(component_silk_layer);
+	back = GetLayerGroupNumberByNumber(solder_silk_layer);
+	/* determine preferred routing direction on each group */
+	for (i = 0; i < max_group; i++) {
+		if (i != back && i != front) {
+			x_cost[i] = (i & 1) ? 2 : 1;
+			y_cost[i] = (i & 1) ? 1 : 2;
+		}
+		else if (i == back) {
+			x_cost[i] = 4;
+			y_cost[i] = 2;
+		}
+		else {
+			x_cost[i] = 2;
+			y_cost[i] = 2;
+		}
+	}
+	/* create routedata */
+	rd = (routedata_t *) malloc(sizeof(*rd));
+	memset((void *) rd, 0, sizeof(*rd));
+
+	rd->max_styles = vtroutestyle_len(&PCB->RouteStyle);
+/*	rd->layergrouptree = calloc(sizeof(rd->layergrouptree[0]), rd->max_layers);*/
+	rd->styles = calloc(sizeof(rd->styles[0]), rd->max_styles);
+
+	/* create default style */
+	rd->defaultstyle.Thick = conf_core.design.line_thickness;
+	rd->defaultstyle.Diameter = conf_core.design.via_thickness;
+	rd->defaultstyle.Hole = conf_core.design.via_drilling_hole;
+	rd->defaultstyle.Clearance = conf_core.design.clearance;
+	rd->max_bloat = BLOAT(&rd->defaultstyle);
+	rd->max_keep = conf_core.design.clearance;
+	/* create styles structures */
+	bbox.X1 = bbox.Y1 = 0;
+	bbox.X2 = PCB->MaxWidth;
+	bbox.Y2 = PCB->MaxHeight;
+	for (i = 0; i < rd->max_styles + 1; i++) {
+		RouteStyleType *style = (i < rd->max_styles) ? &PCB->RouteStyle.array[i] : &rd->defaultstyle;
+		rd->styles[i] = style;
+	}
+
+	/* initialize pointerlisttype */
+	for (i = 0; i < max_group; i++) {
+		layergroupboxes[i].Ptr = NULL;
+		layergroupboxes[i].PtrN = 0;
+		layergroupboxes[i].PtrMax = 0;
+		GROUP_LOOP(PCB->Data, i);
+		{
+			if (linelist_length(&layer->Line) || arclist_length(&layer->Arc))
+				usedGroup[i] = pcb_true;
+			else
+				usedGroup[i] = pcb_false;
+		}
+		END_LOOP;
+	}
+	usedGroup[front] = pcb_true;
+	usedGroup[back] = pcb_true;
+	/* add the objects in the netlist first.
+	 * then go and add all other objects that weren't already added
+	 *
+	 * this saves on searching the trees to find the nets
+	 */
+	/* use the PCB_FLAG_DRC to mark objects as they are entered */
+	ResetConnections(pcb_false);
+	Nets = CollectSubnets(pcb_false);
+	{
+		routebox_t *last_net = NULL;
+		NETLIST_LOOP(&Nets);
+		{
+			routebox_t *last_in_net = NULL;
+			NET_LOOP(netlist);
+			{
+				routebox_t *last_in_subnet = NULL;
+				int j;
+
+				for (j = 0; j < rd->max_styles; j++)
+					if (net->Style == rd->styles[j])
+						break;
+				CONNECTION_LOOP(net);
+				{
+					routebox_t *rb = NULL;
+					SET_FLAG(PCB_FLAG_DRC, (PinTypePtr) connection->ptr2);
+					if (connection->type == PCB_TYPE_LINE) {
+						LineType *line = (LineType *) connection->ptr2;
+
+						/* lines are listed at each end, so skip one */
+						/* this should probably by a macro named "BUMP_LOOP" */
+						n--;
+
+						/* dice up non-straight lines into many tiny obstacles */
+						if (line->Point1.X != line->Point2.X && line->Point1.Y != line->Point2.Y) {
+							LineType fake_line = *line;
+							Coord dx = (line->Point2.X - line->Point1.X);
+							Coord dy = (line->Point2.Y - line->Point1.Y);
+							int segs = MAX(PCB_ABS(dx),
+														 PCB_ABS(dy)) / (4 * BLOAT(rd->styles[j]) + 1);
+							int qq;
+							segs = PCB_CLAMP(segs, 1, 32);	/* don't go too crazy */
+							dx /= segs;
+							dy /= segs;
+							for (qq = 0; qq < segs - 1; qq++) {
+								fake_line.Point2.X = fake_line.Point1.X + dx;
+								fake_line.Point2.Y = fake_line.Point1.Y + dy;
+								if (fake_line.Point2.X == line->Point2.X && fake_line.Point2.Y == line->Point2.Y)
+									break;
+								rb = AddLine(layergroupboxes, connection->group, &fake_line, line, rd->styles[j]);
+								if (last_in_subnet && rb != last_in_subnet)
+									MergeNets(last_in_subnet, rb, ORIGINAL);
+								if (last_in_net && rb != last_in_net)
+									MergeNets(last_in_net, rb, NET);
+								last_in_subnet = last_in_net = rb;
+								fake_line.Point1 = fake_line.Point2;
+							}
+							fake_line.Point2 = line->Point2;
+							rb = AddLine(layergroupboxes, connection->group, &fake_line, line, rd->styles[j]);
+						}
+						else {
+							rb = AddLine(layergroupboxes, connection->group, line, line, rd->styles[j]);
+						}
+					}
+					else
+						switch (connection->type) {
+						case PCB_TYPE_PAD:
+							rb = AddPad(layergroupboxes, (ElementType *) connection->ptr1, (PadType *) connection->ptr2, rd->styles[j]);
+							break;
+						case PCB_TYPE_PIN:
+							rb = AddPin(layergroupboxes, (PinType *) connection->ptr2, pcb_false, rd->styles[j]);
+							break;
+						case PCB_TYPE_VIA:
+							rb = AddPin(layergroupboxes, (PinType *) connection->ptr2, pcb_true, rd->styles[j]);
+							break;
+						case PCB_TYPE_POLYGON:
+							rb =
+								AddPolygon(layergroupboxes,
+													 GetLayerNumber(PCB->Data, (LayerType *) connection->ptr1),
+													 (struct polygon_st *) connection->ptr2, rd->styles[j]);
+							break;
+						}
+					assert(rb);
+					/* update circular connectivity lists */
+					if (last_in_subnet && rb != last_in_subnet)
+						MergeNets(last_in_subnet, rb, ORIGINAL);
+					if (last_in_net && rb != last_in_net)
+						MergeNets(last_in_net, rb, NET);
+					last_in_subnet = last_in_net = rb;
+					rd->max_bloat = MAX(rd->max_bloat, BLOAT(rb->style));
+					rd->max_keep = MAX(rd->max_keep, rb->style->Clearance);
+				}
+				END_LOOP;
+			}
+			END_LOOP;
+			if (last_net && last_in_net)
+				MergeNets(last_net, last_in_net, DIFFERENT_NET);
+			last_net = last_in_net;
+		}
+		END_LOOP;
+		rd->first_net = last_net;
+	}
+	FreeNetListListMemory(&Nets);
+
+	/* reset all nets to "original" connectivity (which we just set) */
+	{
+		routebox_t *net;
+		LIST_LOOP(rd->first_net, different_net, net);
+		ResetSubnet(net);
+		END_LOOP;
+	}
+
+	/* add pins and pads of elements */
+	ALLPIN_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_DRC, pin))
+			CLEAR_FLAG(PCB_FLAG_DRC, pin);
+		else
+			AddPin(layergroupboxes, pin, pcb_false, rd->styles[rd->max_styles]);
+	}
+	ENDALL_LOOP;
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_DRC, pad))
+			CLEAR_FLAG(PCB_FLAG_DRC, pad);
+		else
+			AddPad(layergroupboxes, element, pad, rd->styles[rd->max_styles]);
+	}
+	ENDALL_LOOP;
+	/* add all vias */
+	VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_DRC, via))
+			CLEAR_FLAG(PCB_FLAG_DRC, via);
+		else
+			AddPin(layergroupboxes, via, pcb_true, rd->styles[rd->max_styles]);
+	}
+	END_LOOP;
+
+	for (i = 0; i < max_copper_layer; i++) {
+		int layergroup = GetLayerGroupNumberByNumber(i);
+		/* add all (non-rat) lines */
+		LINE_LOOP(LAYER_PTR(i));
+		{
+			if (TEST_FLAG(PCB_FLAG_DRC, line)) {
+				CLEAR_FLAG(PCB_FLAG_DRC, line);
+				continue;
+			}
+			/* dice up non-straight lines into many tiny obstacles */
+			if (line->Point1.X != line->Point2.X && line->Point1.Y != line->Point2.Y) {
+				LineType fake_line = *line;
+				Coord dx = (line->Point2.X - line->Point1.X);
+				Coord dy = (line->Point2.Y - line->Point1.Y);
+				int segs = MAX(PCB_ABS(dx), PCB_ABS(dy)) / (4 * rd->max_bloat + 1);
+				int qq;
+				segs = PCB_CLAMP(segs, 1, 32);	/* don't go too crazy */
+				dx /= segs;
+				dy /= segs;
+				for (qq = 0; qq < segs - 1; qq++) {
+					fake_line.Point2.X = fake_line.Point1.X + dx;
+					fake_line.Point2.Y = fake_line.Point1.Y + dy;
+					if (fake_line.Point2.X == line->Point2.X && fake_line.Point2.Y == line->Point2.Y)
+						break;
+					AddLine(layergroupboxes, layergroup, &fake_line, line, rd->styles[rd->max_styles]);
+					fake_line.Point1 = fake_line.Point2;
+				}
+				fake_line.Point2 = line->Point2;
+				AddLine(layergroupboxes, layergroup, &fake_line, line, rd->styles[rd->max_styles]);
+			}
+			else {
+				AddLine(layergroupboxes, layergroup, line, line, rd->styles[rd->max_styles]);
+			}
+		}
+		END_LOOP;
+		/* add all polygons */
+		POLYGON_LOOP(LAYER_PTR(i));
+		{
+			if (TEST_FLAG(PCB_FLAG_DRC, polygon))
+				CLEAR_FLAG(PCB_FLAG_DRC, polygon);
+			else
+				AddPolygon(layergroupboxes, i, polygon, rd->styles[rd->max_styles]);
+		}
+		END_LOOP;
+		/* add all copper text */
+		TEXT_LOOP(LAYER_PTR(i));
+		{
+			AddText(layergroupboxes, layergroup, text, rd->styles[rd->max_styles]);
+		}
+		END_LOOP;
+		/* add all arcs */
+		ARC_LOOP(LAYER_PTR(i));
+		{
+			AddArc(layergroupboxes, layergroup, arc, rd->styles[rd->max_styles]);
+		}
+		END_LOOP;
+	}
+
+	/* create r-trees from pointer lists */
+	for (i = 0; i < max_group; i++) {
+		/* create the r-tree */
+		rd->layergrouptree[i] = r_create_tree((const BoxType **) layergroupboxes[i].Ptr, layergroupboxes[i].PtrN, 1);
+	}
+
+	if (AutoRouteParameters.use_vias) {
+		rd->mtspace = mtspace_create();
+
+		/* create "empty-space" structures for via placement (now that we know
+		 * appropriate clearances for all the fixed elements) */
+		for (i = 0; i < max_group; i++) {
+			POINTER_LOOP(&layergroupboxes[i]);
+			{
+				routebox_t *rb = (routebox_t *) * ptr;
+				if (!rb->flags.clear_poly)
+					mtspace_add(rd->mtspace, &rb->box, FIXED, rb->style->Clearance);
+			}
+			END_LOOP;
+		}
+	}
+	/* free pointer lists */
+	for (i = 0; i < max_group; i++)
+		FreePointerListMemory(&layergroupboxes[i]);
+	/* done! */
+	return rd;
+}
+
+void DestroyRouteData(routedata_t ** rd)
+{
+	int i;
+	for (i = 0; i < max_group; i++)
+		r_destroy_tree(&(*rd)->layergrouptree[i]);
+	if (AutoRouteParameters.use_vias)
+		mtspace_destroy(&(*rd)->mtspace);
+/*	free((*rd)->layergrouptree);*/
+	free((*rd)->styles);
+	free(*rd);
+	*rd = NULL;
+}
+
+/*-----------------------------------------------------------------
+ * routebox reference counting.
+ */
+
+/* increment the reference count on a routebox. */
+static void RB_up_count(routebox_t * rb)
+{
+	assert(rb->flags.homeless);
+	rb->refcount++;
+}
+
+/* decrement the reference count on a routebox, freeing if this box becomes
+ * unused. */
+static void RB_down_count(routebox_t * rb)
+{
+	assert(rb->type == EXPANSION_AREA);
+	assert(rb->flags.homeless);
+	assert(rb->refcount > 0);
+	if (--rb->refcount == 0) {
+		if (rb->parent.expansion_area->flags.homeless)
+			RB_down_count(rb->parent.expansion_area);
+		free(rb);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * Rectangle-expansion routing code.
+ */
+
+static void ResetSubnet(routebox_t * net)
+{
+	routebox_t *rb;
+	/* reset connectivity of everything on this net */
+	LIST_LOOP(net, same_net, rb);
+	rb->same_subnet = rb->original_subnet;
+	END_LOOP;
+}
+
+static inline cost_t cost_to_point_on_layer(const CheapPointType * p1, const CheapPointType * p2, pcb_cardinal_t point_layer)
+{
+	cost_t x_dist = p1->X - p2->X, y_dist = p1->Y - p2->Y, r;
+	x_dist *= x_cost[point_layer];
+	y_dist *= y_cost[point_layer];
+	/* cost is proportional to orthogonal distance. */
+	r = PCB_ABS(x_dist) + PCB_ABS(y_dist);
+	if (p1->X != p2->X && p1->Y != p2->Y)
+		r += AutoRouteParameters.JogPenalty;
+	return r;
+}
+
+static cost_t cost_to_point(const CheapPointType * p1, pcb_cardinal_t point_layer1, const CheapPointType * p2, pcb_cardinal_t point_layer2)
+{
+	cost_t r = cost_to_point_on_layer(p1, p2, point_layer1);
+	/* apply via cost penalty if layers differ */
+	if (point_layer1 != point_layer2)
+		r += AutoRouteParameters.ViaCost;
+	return r;
+}
+
+/* return the minimum *cost* from a point to a box on any layer.
+ * It's safe to return a smaller than minimum cost
+ */
+static cost_t cost_to_layerless_box(const CheapPointType * p, pcb_cardinal_t point_layer, const BoxType * b)
+{
+	CheapPointType p2 = closest_point_in_box(p, b);
+	register cost_t c1, c2;
+
+	c1 = p2.X - p->X;
+	c2 = p2.Y - p->Y;
+
+	c1 = PCB_ABS(c1);
+	c2 = PCB_ABS(c2);
+	if (c1 < c2)
+		return c1 * AutoRouteParameters.MinPenalty + c2;
+	else
+		return c2 * AutoRouteParameters.MinPenalty + c1;
+}
+
+/* get to actual pins/pad target coordinates */
+pcb_bool TargetPoint(CheapPointType * nextpoint, const routebox_t * target)
+{
+	if (target->type == PIN) {
+		nextpoint->X = target->parent.pin->X;
+		nextpoint->Y = target->parent.pin->Y;
+		return pcb_true;
+	}
+	else if (target->type == PAD) {
+		if (labs(target->parent.pad->Point1.X - nextpoint->X) < labs(target->parent.pad->Point2.X - nextpoint->X))
+			nextpoint->X = target->parent.pad->Point1.X;
+		else
+			nextpoint->X = target->parent.pad->Point2.X;
+		if (labs(target->parent.pad->Point1.Y - nextpoint->Y) < labs(target->parent.pad->Point2.Y - nextpoint->Y))
+			nextpoint->Y = target->parent.pad->Point1.Y;
+		else
+			nextpoint->Y = target->parent.pad->Point2.Y;
+		return pcb_true;
+	}
+	else {
+		nextpoint->X = CENTER_X(target->sbox);
+		nextpoint->Y = CENTER_Y(target->sbox);
+	}
+	return pcb_false;
+}
+
+/* return the *minimum cost* from a point to a route box, including possible
+ * via costs if the route box is on a different layer.
+ * assume routbox is bloated unless it is an expansion area
+ */
+static cost_t cost_to_routebox(const CheapPointType * p, pcb_cardinal_t point_layer, const routebox_t * rb)
+{
+	register cost_t trial = 0;
+	CheapPointType p2 = closest_point_in_routebox(p, rb);
+	if (!usedGroup[point_layer] || !usedGroup[rb->group])
+		trial = AutoRouteParameters.NewLayerPenalty;
+	if ((p2.X - p->X) * (p2.Y - p->Y) != 0)
+		trial += AutoRouteParameters.JogPenalty;
+	/* special case for defered via searching */
+	if (point_layer > max_group || point_layer == rb->group)
+		return trial + PCB_ABS(p2.X - p->X) + PCB_ABS(p2.Y - p->Y);
+	/* if this target is only a via away, then the via is cheaper than the congestion */
+	if (p->X == p2.X && p->Y == p2.Y)
+		return trial + 1;
+	trial += AutoRouteParameters.ViaCost;
+	trial += PCB_ABS(p2.X - p->X) + PCB_ABS(p2.Y - p->Y);
+	return trial;
+}
+
+
+static BoxType bloat_routebox(routebox_t * rb)
+{
+	BoxType r;
+	Coord clearance;
+	assert(__routebox_is_good(rb));
+
+	if (rb->flags.nobloat)
+		return rb->sbox;
+
+	/* Obstacle exclusion zones get bloated by the larger of
+	 * the two required clearances plus half the track width.
+	 */
+	clearance = MAX(AutoRouteParameters.style->Clearance, rb->style->Clearance);
+	r = bloat_box(&rb->sbox, clearance + HALF_THICK(AutoRouteParameters.style->Thick));
+	return r;
+}
+
+
+#ifdef ROUTE_DEBUG							/* only for debugging expansion areas */
+
+ typedef short pcb_dimension_t;
+/* makes a line on the solder layer silk surrounding the box */
+static void showbox(BoxType b, pcb_dimension_t thickness, int group)
+{
+	LineTypePtr line;
+	LayerTypePtr SLayer = LAYER_PTR(group);
+	if (showboxen < -1)
+		return;
+	if (showboxen != -1 && showboxen != group)
+		return;
+
+	if (ddraw != NULL) {
+		ddraw->set_line_width(ar_gc, thickness);
+		ddraw->set_line_cap(ar_gc, Trace_Cap);
+		ddraw->set_color(ar_gc, SLayer->Color);
+
+		ddraw->draw_line(ar_gc, b.X1, b.Y1, b.X2, b.Y1);
+		ddraw->draw_line(ar_gc, b.X1, b.Y2, b.X2, b.Y2);
+		ddraw->draw_line(ar_gc, b.X1, b.Y1, b.X1, b.Y2);
+		ddraw->draw_line(ar_gc, b.X2, b.Y1, b.X2, b.Y2);
+	}
+
+#if 1
+	if (b.Y1 == b.Y2 || b.X1 == b.X2)
+		thickness = 5;
+	line = CreateNewLineOnLayer(LAYER_PTR(component_silk_layer), b.X1, b.Y1, b.X2, b.Y1, thickness, 0, MakeFlags(0));
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, LAYER_PTR(component_silk_layer), line, line);
+	if (b.Y1 != b.Y2) {
+		line = CreateNewLineOnLayer(LAYER_PTR(component_silk_layer), b.X1, b.Y2, b.X2, b.Y2, thickness, 0, MakeFlags(0));
+		AddObjectToCreateUndoList(PCB_TYPE_LINE, LAYER_PTR(component_silk_layer), line, line);
+	}
+	line = CreateNewLineOnLayer(LAYER_PTR(component_silk_layer), b.X1, b.Y1, b.X1, b.Y2, thickness, 0, MakeFlags(0));
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, LAYER_PTR(component_silk_layer), line, line);
+	if (b.X1 != b.X2) {
+		line = CreateNewLineOnLayer(LAYER_PTR(component_silk_layer), b.X2, b.Y1, b.X2, b.Y2, thickness, 0, MakeFlags(0));
+		AddObjectToCreateUndoList(PCB_TYPE_LINE, LAYER_PTR(component_silk_layer), line, line);
+	}
+#endif
+}
+#endif
+
+#if defined(ROUTE_DEBUG)
+static void showedge(edge_t * e)
+{
+	BoxType *b = (BoxType *) e->rb;
+
+	if (ddraw == NULL)
+		return;
+
+	ddraw->set_line_cap(ar_gc, Trace_Cap);
+	ddraw->set_line_width(ar_gc, 1);
+	ddraw->set_color(ar_gc, Settings.MaskColor);
+
+	switch (e->expand_dir) {
+	case NORTH:
+		ddraw->draw_line(ar_gc, b->X1, b->Y1, b->X2, b->Y1);
+		break;
+	case SOUTH:
+		ddraw->draw_line(ar_gc, b->X1, b->Y2, b->X2, b->Y2);
+		break;
+	case WEST:
+		ddraw->draw_line(ar_gc, b->X1, b->Y1, b->X1, b->Y2);
+		break;
+	case EAST:
+		ddraw->draw_line(ar_gc, b->X2, b->Y1, b->X2, b->Y2);
+		break;
+	default:
+		break;
+	}
+}
+#endif
+
+#if defined(ROUTE_DEBUG)
+static void showroutebox(routebox_t * rb)
+{
+	showbox(rb->sbox, rb->flags.source ? 20 : (rb->flags.target ? 10 : 1), rb->flags.is_via ? component_silk_layer : rb->group);
+}
+#endif
+
+/* return a "parent" of this edge which immediately precedes it in the route.*/
+static routebox_t *route_parent(routebox_t * rb)
+{
+	while (rb->flags.homeless && !rb->flags.is_via && !rb->flags.is_thermal) {
+		assert(rb->type == EXPANSION_AREA);
+		rb = rb->parent.expansion_area;
+		assert(rb);
+	}
+	return rb;
+}
+
+static vector_t *path_conflicts(routebox_t * rb, routebox_t * conflictor, pcb_bool branch)
+{
+	if (branch)
+		rb->conflicts_with = vector_duplicate(rb->conflicts_with);
+	else if (!rb->conflicts_with)
+		rb->conflicts_with = vector_create();
+	vector_append(rb->conflicts_with, conflictor);
+	return rb->conflicts_with;
+}
+
+/* Touch everything (except fixed) on each net found
+ * in the conflicts vector. If the vector is different
+ * from the last one touched, untouch the last batch
+ * and touch the new one. Always call with touch=1
+ * (except for recursive call). Call with NULL, 1 to
+ * clear the last batch touched.
+ *
+ * touched items become invisible to current path
+ * so we don't encounter the same conflictor more
+ * than once
+ */
+
+static void touch_conflicts(vector_t * conflicts, int touch)
+{
+	static vector_t *last = NULL;
+	static int last_size = 0;
+	int i, n;
+	i = 0;
+	if (touch) {
+		if (last && conflicts != last)
+			touch_conflicts(last, 0);
+		if (!conflicts)
+			return;
+		last = conflicts;
+		i = last_size;
+	}
+	n = vector_size(conflicts);
+	for (; i < n; i++) {
+		routebox_t *rb = (routebox_t *) vector_element(conflicts, i);
+		routebox_t *p;
+		LIST_LOOP(rb, same_net, p);
+		if (!p->flags.fixed)
+			p->flags.touched = touch;
+		END_LOOP;
+	}
+	if (!touch) {
+		last = NULL;
+		last_size = 0;
+	}
+	else
+		last_size = n;
+}
+
+/* return a "parent" of this edge which resides in a r-tree somewhere */
+/* -- actually, this "parent" *may* be a via box, which doesn't live in
+ * a r-tree. -- */
+static routebox_t *nonhomeless_parent(routebox_t * rb)
+{
+	return route_parent(rb);
+}
+
+/* some routines to find the minimum *cost* from a cost point to
+ * a target (any target) */
+struct mincost_target_closure {
+	const CheapPointType *CostPoint;
+	pcb_cardinal_t CostPointLayer;
+	routebox_t *nearest;
+	cost_t nearest_cost;
+};
+static r_dir_t __region_within_guess(const BoxType * region, void *cl)
+{
+	struct mincost_target_closure *mtc = (struct mincost_target_closure *) cl;
+	cost_t cost_to_region;
+	if (mtc->nearest == NULL)
+		return R_DIR_FOUND_CONTINUE;
+	cost_to_region = cost_to_layerless_box(mtc->CostPoint, mtc->CostPointLayer, region);
+	assert(cost_to_region >= 0);
+	/* if no guess yet, all regions are "close enough" */
+	/* note that cost is *strictly more* than minimum distance, so we'll
+	 * always search a region large enough. */
+	return (cost_to_region < mtc->nearest_cost) ? R_DIR_FOUND_CONTINUE : R_DIR_NOT_FOUND;
+}
+
+static r_dir_t __found_new_guess(const BoxType * box, void *cl)
+{
+	struct mincost_target_closure *mtc = (struct mincost_target_closure *) cl;
+	routebox_t *guess = (routebox_t *) box;
+	cost_t cost_to_guess = cost_to_routebox(mtc->CostPoint, mtc->CostPointLayer, guess);
+	assert(cost_to_guess >= 0);
+	/* if this is cheaper than previous guess... */
+	if (cost_to_guess < mtc->nearest_cost) {
+		mtc->nearest = guess;
+		mtc->nearest_cost = cost_to_guess;	/* this is our new guess! */
+		return R_DIR_FOUND_CONTINUE;
+	}
+	else
+		return R_DIR_NOT_FOUND;										/* not less expensive than our last guess */
+}
+
+/* target_guess is our guess at what the nearest target is, or NULL if we
+ * just plum don't have a clue. */
+static routebox_t *mincost_target_to_point(const CheapPointType * CostPoint,
+																					 pcb_cardinal_t CostPointLayer, rtree_t * targets, routebox_t * target_guess)
+{
+	struct mincost_target_closure mtc;
+	assert(target_guess == NULL || target_guess->flags.target);	/* this is a target, right? */
+	mtc.CostPoint = CostPoint;
+	mtc.CostPointLayer = CostPointLayer;
+	mtc.nearest = target_guess;
+	if (mtc.nearest)
+		mtc.nearest_cost = cost_to_routebox(mtc.CostPoint, mtc.CostPointLayer, mtc.nearest);
+	else
+		mtc.nearest_cost = EXPENSIVE;
+	r_search(targets, NULL, __region_within_guess, __found_new_guess, &mtc, NULL);
+	assert(mtc.nearest != NULL && mtc.nearest_cost >= 0);
+	assert(mtc.nearest->flags.target);	/* this is a target, right? */
+	return mtc.nearest;
+}
+
+/* create edge from field values */
+/* mincost_target_guess can be NULL */
+static edge_t *CreateEdge(routebox_t * rb,
+													Coord CostPointX, Coord CostPointY,
+													cost_t cost_to_point, routebox_t * mincost_target_guess, direction_t expand_dir, rtree_t * targets)
+{
+	edge_t *e;
+	assert(__routebox_is_good(rb));
+	e = (edge_t *) malloc(sizeof(*e));
+	memset((void *) e, 0, sizeof(*e));
+	assert(e);
+	e->rb = rb;
+	if (rb->flags.homeless)
+		RB_up_count(rb);
+	e->cost_point.X = CostPointX;
+	e->cost_point.Y = CostPointY;
+	e->cost_to_point = cost_to_point;
+	e->flags.via_search = 0;
+	/* if this edge is created in response to a target, use it */
+	if (targets)
+		e->mincost_target = mincost_target_to_point(&e->cost_point, rb->group, targets, mincost_target_guess);
+	else
+		e->mincost_target = mincost_target_guess;
+	e->expand_dir = expand_dir;
+	assert(e->rb && e->mincost_target);	/* valid edge? */
+	assert(!e->flags.is_via || e->expand_dir == ALL);
+	/* cost point should be on edge (unless this is a plane/via/conflict edge) */
+#if 0
+	assert(rb->type == PLANE || rb->conflicts_with != NULL || rb->flags.is_via
+				 || rb->flags.is_thermal
+				 || ((expand_dir == NORTH || expand_dir == SOUTH) ? rb->sbox.X1 <=
+						 CostPointX && CostPointX < rb->sbox.X2 && CostPointY == (expand_dir == NORTH ? rb->sbox.Y1 : rb->sbox.Y2 - 1) :
+						 /* expand_dir==EAST || expand_dir==WEST */
+						 rb->sbox.Y1 <= CostPointY && CostPointY < rb->sbox.Y2 &&
+						 CostPointX == (expand_dir == EAST ? rb->sbox.X2 - 1 : rb->sbox.X1)));
+#endif
+	assert(__edge_is_good(e));
+	/* done */
+	return e;
+}
+
+/* create edge, using previous edge to fill in defaults. */
+/* most of the work here is in determining a new cost point */
+static edge_t *CreateEdge2(routebox_t * rb, direction_t expand_dir,
+													 edge_t * previous_edge, rtree_t * targets, routebox_t * guess)
+{
+	BoxType thisbox;
+	CheapPointType thiscost, prevcost;
+	cost_t d;
+
+	assert(rb && previous_edge);
+	/* okay, find cheapest costpoint to costpoint of previous edge */
+	thisbox = edge_to_box(rb, expand_dir);
+	prevcost = previous_edge->cost_point;
+	/* find point closest to target */
+	thiscost = closest_point_in_box(&prevcost, &thisbox);
+	/* compute cost-to-point */
+	d = cost_to_point_on_layer(&prevcost, &thiscost, rb->group);
+	/* add in jog penalty */
+	if (previous_edge->expand_dir != expand_dir)
+		d += AutoRouteParameters.JogPenalty;
+	/* okay, new edge! */
+	return CreateEdge(rb, thiscost.X, thiscost.Y,
+										previous_edge->cost_to_point + d, guess ? guess : previous_edge->mincost_target, expand_dir, targets);
+}
+
+/* create via edge, using previous edge to fill in defaults. */
+static edge_t *CreateViaEdge(const BoxType * area, pcb_cardinal_t group,
+														 routebox_t * parent, edge_t * previous_edge,
+														 conflict_t to_site_conflict, conflict_t through_site_conflict, rtree_t * targets)
+{
+	routebox_t *rb;
+	CheapPointType costpoint;
+	cost_t d;
+	edge_t *ne;
+	cost_t scale[3];
+
+	scale[0] = 1;
+	scale[1] = AutoRouteParameters.LastConflictPenalty;
+	scale[2] = AutoRouteParameters.ConflictPenalty;
+
+	assert(box_is_good(area));
+	assert(AutoRouteParameters.with_conflicts || (to_site_conflict == NO_CONFLICT && through_site_conflict == NO_CONFLICT));
+	rb = CreateExpansionArea(area, group, parent, pcb_true, previous_edge);
+	rb->flags.is_via = 1;
+	rb->came_from = ALL;
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_VIA_BOXES)
+	showroutebox(rb);
+#endif /* ROUTE_DEBUG && DEBUG_SHOW_VIA_BOXES */
+	/* for planes, choose a point near the target */
+	if (previous_edge->flags.in_plane) {
+		routebox_t *target;
+		CheapPointType pnt;
+		/* find a target near this via box */
+		pnt.X = CENTER_X(*area);
+		pnt.Y = CENTER_Y(*area);
+		target = mincost_target_to_point(&pnt, rb->group, targets, previous_edge->mincost_target);
+		/* now find point near the target */
+		pnt.X = CENTER_X(target->box);
+		pnt.Y = CENTER_Y(target->box);
+		costpoint = closest_point_in_routebox(&pnt, rb);
+		/* we moved from the previous cost point through the plane which is free travel */
+		d = (scale[through_site_conflict] * cost_to_point(&costpoint, group, &costpoint, previous_edge->rb->group));
+		ne = CreateEdge(rb, costpoint.X, costpoint.Y, previous_edge->cost_to_point + d, target, ALL, NULL);
+		ne->mincost_target = target;
+	}
+	else {
+		routebox_t *target;
+		target = previous_edge->mincost_target;
+		costpoint = closest_point_in_routebox(&previous_edge->cost_point, rb);
+		d =
+			(scale[to_site_conflict] *
+			 cost_to_point_on_layer(&costpoint, &previous_edge->cost_point,
+															previous_edge->rb->group)) +
+			(scale[through_site_conflict] * cost_to_point(&costpoint, group, &costpoint, previous_edge->rb->group));
+		/* if the target is just this via away, then this via is cheaper */
+		if (target->group == group && point_in_shrunk_box(target, costpoint.X, costpoint.Y))
+			d -= AutoRouteParameters.ViaCost / 2;
+		ne =
+			CreateEdge(rb, costpoint.X, costpoint.Y, previous_edge->cost_to_point + d, previous_edge->mincost_target, ALL, targets);
+	}
+	ne->flags.is_via = 1;
+	ne->flags.via_conflict_level = to_site_conflict;
+	assert(__edge_is_good(ne));
+	return ne;
+}
+
+/* create "interior" edge for routing with conflicts */
+/* Presently once we "jump inside" the conflicting object
+ * we consider it a routing highway to travel inside since
+ * it will become available if the conflict is elliminated.
+ * That is why we ignore the interior_edge argument.
+ */
+static edge_t *CreateEdgeWithConflicts(const BoxType * interior_edge,
+																			 routebox_t * container, edge_t * previous_edge,
+																			 cost_t cost_penalty_to_box, rtree_t * targets)
+{
+	routebox_t *rb;
+	CheapPointType costpoint;
+	cost_t d;
+	edge_t *ne;
+	assert(interior_edge && container && previous_edge && targets);
+	assert(!container->flags.homeless);
+	assert(AutoRouteParameters.with_conflicts);
+	assert(container->flags.touched == 0);
+	assert(previous_edge->rb->group == container->group);
+	/* use the caller's idea of what this box should be */
+	rb = CreateExpansionArea(interior_edge, previous_edge->rb->group, previous_edge->rb, pcb_true, previous_edge);
+	path_conflicts(rb, container, pcb_true);	/* crucial! */
+	costpoint = closest_point_in_box(&previous_edge->cost_point, interior_edge);
+	d = cost_to_point_on_layer(&costpoint, &previous_edge->cost_point, previous_edge->rb->group);
+	d *= cost_penalty_to_box;
+	d += previous_edge->cost_to_point;
+	ne = CreateEdge(rb, costpoint.X, costpoint.Y, d, NULL, ALL, targets);
+	ne->flags.is_interior = 1;
+	assert(__edge_is_good(ne));
+	return ne;
+}
+
+static void KillEdge(void *edge)
+{
+	edge_t *e = (edge_t *) edge;
+	assert(e);
+	if (e->rb->flags.homeless)
+		RB_down_count(e->rb);
+	if (e->flags.via_search)
+		mtsFreeWork(&e->work);
+	free(e);
+}
+
+static void DestroyEdge(edge_t ** e)
+{
+	assert(e && *e);
+	KillEdge(*e);
+	*e = NULL;
+}
+
+/* cost function for an edge. */
+static cost_t edge_cost(const edge_t * e, const cost_t too_big)
+{
+	cost_t penalty = e->cost_to_point;
+	if (e->rb->flags.is_thermal || e->rb->type == PLANE)
+		return penalty;							/* thermals are cheap */
+	if (penalty > too_big)
+		return penalty;
+
+	/* cost_to_routebox adds in our via correction, too. */
+	return penalty + cost_to_routebox(&e->cost_point, e->rb->group, e->mincost_target);
+}
+
+/* given an edge of a box, return a box containing exactly the points on that
+ * edge.  Note that the return box is treated as closed; that is, the bottom and
+ * right "edges" consist of points (just barely) not in the (half-open) box. */
+static BoxType edge_to_box(const routebox_t * rb, direction_t expand_dir)
+{
+	BoxType b = shrink_routebox(rb);
+	/* narrow box down to just the appropriate edge */
+	switch (expand_dir) {
+	case NORTH:
+		b.Y2 = b.Y1 + 1;
+		break;
+	case EAST:
+		b.X1 = b.X2 - 1;
+		break;
+	case SOUTH:
+		b.Y1 = b.Y2 - 1;
+		break;
+	case WEST:
+		b.X2 = b.X1 + 1;
+		break;
+	default:
+		assert(0);
+	}
+	/* done! */
+	return b;
+}
+
+struct broken_boxes {
+	BoxType left, center, right;
+	pcb_bool is_valid_left, is_valid_center, is_valid_right;
+};
+
+static struct broken_boxes break_box_edge(const BoxType * original, direction_t which_edge, routebox_t * breaker)
+{
+	BoxType origbox, breakbox;
+	struct broken_boxes result;
+
+	assert(original && breaker);
+
+	origbox = *original;
+	breakbox = bloat_routebox(breaker);
+	ROTATEBOX_TO_NORTH(origbox, which_edge);
+	ROTATEBOX_TO_NORTH(breakbox, which_edge);
+	result.right.Y1 = result.center.Y1 = result.left.Y1 = origbox.Y1;
+	result.right.Y2 = result.center.Y2 = result.left.Y2 = origbox.Y1 + 1;
+	/* validity of breaker is not important because the boxes are marked invalid */
+	/*assert (breakbox.X1 <= origbox.X2 && breakbox.X2 >= origbox.X1); */
+	/* left edge piece */
+	result.left.X1 = origbox.X1;
+	result.left.X2 = breakbox.X1;
+	/* center (ie blocked) edge piece */
+	result.center.X1 = MAX(breakbox.X1, origbox.X1);
+	result.center.X2 = MIN(breakbox.X2, origbox.X2);
+	/* right edge piece */
+	result.right.X1 = breakbox.X2;
+	result.right.X2 = origbox.X2;
+	/* validity: */
+	result.is_valid_left = (result.left.X1 < result.left.X2);
+	result.is_valid_center = (result.center.X1 < result.center.X2);
+	result.is_valid_right = (result.right.X1 < result.right.X2);
+	/* rotate back */
+	ROTATEBOX_FROM_NORTH(result.left, which_edge);
+	ROTATEBOX_FROM_NORTH(result.center, which_edge);
+	ROTATEBOX_FROM_NORTH(result.right, which_edge);
+	/* done */
+	return result;
+}
+
+#ifndef NDEBUG
+static int share_edge(const BoxType * child, const BoxType * parent)
+{
+	return
+		(child->X1 == parent->X2 || child->X2 == parent->X1 ||
+		 child->Y1 == parent->Y2 || child->Y2 == parent->Y1) &&
+		((parent->X1 <= child->X1 && child->X2 <= parent->X2) || (parent->Y1 <= child->Y1 && child->Y2 <= parent->Y2));
+}
+
+static int edge_intersect(const BoxType * child, const BoxType * parent)
+{
+	return (child->X1 <= parent->X2) && (child->X2 >= parent->X1) && (child->Y1 <= parent->Y2) && (child->Y2 >= parent->Y1);
+}
+#endif
+
+/* area is the expansion area, on layer group 'group'. 'parent' is the
+ * immediately preceding expansion area, for backtracing. 'lastarea' is
+ * the last expansion area created, we string these together in a loop
+ * so we can remove them all easily at the end. */
+static routebox_t *CreateExpansionArea(const BoxType * area, pcb_cardinal_t group,
+																			 routebox_t * parent, pcb_bool relax_edge_requirements, edge_t * src_edge)
+{
+	routebox_t *rb = (routebox_t *) malloc(sizeof(*rb));
+	memset((void *) rb, 0, sizeof(*rb));
+	assert(area && parent);
+	init_const_box(rb, area->X1, area->Y1, area->X2, area->Y2, 0);
+	rb->group = group;
+	rb->type = EXPANSION_AREA;
+	/* should always share edge or overlap with parent */
+	assert(relax_edge_requirements ? box_intersect(&rb->sbox, &parent->sbox)
+				 : share_edge(&rb->sbox, &parent->sbox));
+	rb->parent.expansion_area = route_parent(parent);
+	rb->cost_point = closest_point_in_box(&rb->parent.expansion_area->cost_point, area);
+	rb->cost =
+		rb->parent.expansion_area->cost +
+		cost_to_point_on_layer(&rb->parent.expansion_area->cost_point, &rb->cost_point, rb->group);
+	assert(relax_edge_requirements ? edge_intersect(&rb->sbox, &parent->sbox)
+				 : share_edge(&rb->sbox, &parent->sbox));
+	if (rb->parent.expansion_area->flags.homeless)
+		RB_up_count(rb->parent.expansion_area);
+	rb->flags.homeless = 1;
+	rb->flags.nobloat = 1;
+	rb->style = AutoRouteParameters.style;
+	rb->conflicts_with = parent->conflicts_with;
+/* we will never link an EXPANSION_AREA into the nets because they
+ * are *ONLY* used for path searching. No need to call  InitLists ()
+ */
+	rb->came_from = src_edge->expand_dir;
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_EXPANSION_BOXES)
+	showroutebox(rb);
+#endif /* ROUTE_DEBUG && DEBUG_SHOW_EXPANSION_BOXES */
+	return rb;
+}
+
+/*------ Expand ------*/
+struct E_result {
+	routebox_t *parent;
+	routebox_t *n, *e, *s, *w;
+	Coord keep, bloat;
+	BoxType inflated, orig;
+	int done;
+};
+
+/* test method for Expand()
+ * this routebox potentially is a blocker limiting expansion
+ * if this is so, we limit the inflate box so another exactly
+ * like it wouldn't be seen. We do this while keep the inflated
+ * box as large as possible.
+ */
+static r_dir_t __Expand_this_rect(const BoxType * box, void *cl)
+{
+	struct E_result *res = (struct E_result *) cl;
+	routebox_t *rb = (routebox_t *) box;
+	BoxType rbox;
+	Coord dn, de, ds, dw, bloat;
+
+	/* we don't see conflicts already encountered */
+	if (rb->flags.touched)
+		return R_DIR_NOT_FOUND;
+
+	/* The inflated box outer edges include its own
+	 * track width plus its own clearance.
+	 *
+	 * To check for intersection, we need to expand
+	 * anything with greater clearance by its excess
+	 * clearance.
+	 *
+	 * If something has nobloat then we need to shrink
+	 * the inflated box back and see if it still touches.
+	 */
+
+	if (rb->flags.nobloat) {
+		rbox = rb->sbox;
+		bloat = res->bloat;
+		if (rbox.X2 <= res->inflated.X1 + bloat ||
+				rbox.X1 >= res->inflated.X2 - bloat || rbox.Y1 >= res->inflated.Y2 - bloat || rbox.Y2 <= res->inflated.Y1 + bloat)
+			return R_DIR_NOT_FOUND;									/* doesn't touch */
+	}
+	else {
+		if (rb->style->Clearance > res->keep)
+			rbox = bloat_box(&rb->sbox, rb->style->Clearance - res->keep);
+		else
+			rbox = rb->sbox;
+
+		if (rbox.X2 <= res->inflated.X1 || rbox.X1 >= res->inflated.X2
+				|| rbox.Y1 >= res->inflated.Y2 || rbox.Y2 <= res->inflated.Y1)
+			return R_DIR_NOT_FOUND;									/* doesn't touch */
+		bloat = 0;
+	}
+
+	/* this is an intersecting box; it has to jump through a few more hoops */
+	if (rb == res->parent || rb->parent.expansion_area == res->parent)
+		return R_DIR_NOT_FOUND;										/* don't see what we came from */
+
+	/* if we are expanding a source edge, don't let other sources
+	 * or their expansions stop us.
+	 */
+#if 1
+	if (res->parent->flags.source)
+		if (rb->flags.source || (rb->type == EXPANSION_AREA && rb->parent.expansion_area->flags.source))
+			return R_DIR_NOT_FOUND;
+#endif
+
+	/* we ignore via expansion boxes because maybe its
+	 * cheaper to get there without the via through
+	 * the path we're exploring  now.
+	 */
+	if (rb->flags.is_via && rb->type == EXPANSION_AREA)
+		return R_DIR_NOT_FOUND;
+
+	if (rb->type == PLANE) {			/* expanding inside a plane is not good */
+		if (rbox.X1 < res->orig.X1 && rbox.X2 > res->orig.X2 && rbox.Y1 < res->orig.Y1 && rbox.Y2 > res->orig.Y2) {
+			res->inflated = bloat_box(&res->orig, res->bloat);
+			return R_DIR_FOUND_CONTINUE;
+		}
+	}
+	/* calculate the distances from original box to this blocker */
+	dn = de = ds = dw = 0;
+	if (!(res->done & _NORTH) && rbox.Y1 <= res->orig.Y1 && rbox.Y2 > res->inflated.Y1)
+		dn = res->orig.Y1 - rbox.Y2;
+	if (!(res->done & _EAST) && rbox.X2 >= res->orig.X2 && rbox.X1 < res->inflated.X2)
+		de = rbox.X1 - res->orig.X2;
+	if (!(res->done & _SOUTH) && rbox.Y2 >= res->orig.Y2 && rbox.Y1 < res->inflated.Y2)
+		ds = rbox.Y1 - res->orig.Y2;
+	if (!(res->done & _WEST) && rbox.X1 <= res->orig.X1 && rbox.X2 > res->inflated.X1)
+		dw = res->orig.X1 - rbox.X2;
+	if (dn <= 0 && de <= 0 && ds <= 0 && dw <= 0)
+		return R_DIR_FOUND_CONTINUE;
+	/* now shrink the inflated box to the largest blocking direction */
+	if (dn >= de && dn >= ds && dn >= dw) {
+		res->inflated.Y1 = rbox.Y2 - bloat;
+		res->n = rb;
+	}
+	else if (de >= ds && de >= dw) {
+		res->inflated.X2 = rbox.X1 + bloat;
+		res->e = rb;
+	}
+	else if (ds >= dw) {
+		res->inflated.Y2 = rbox.Y1 + bloat;
+		res->s = rb;
+	}
+	else {
+		res->inflated.X1 = rbox.X2 - bloat;
+		res->w = rb;
+	}
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static pcb_bool boink_box(routebox_t * rb, struct E_result *res, direction_t dir)
+{
+	Coord bloat;
+	if (rb->style->Clearance > res->keep)
+		bloat = res->keep - rb->style->Clearance;
+	else
+		bloat = 0;
+	if (rb->flags.nobloat)
+		bloat = res->bloat;
+	switch (dir) {
+	case NORTH:
+	case SOUTH:
+		if (rb->sbox.X2 <= res->inflated.X1 + bloat || rb->sbox.X1 >= res->inflated.X2 - bloat)
+			return pcb_false;
+		return pcb_true;
+	case EAST:
+	case WEST:
+		if (rb->sbox.Y1 >= res->inflated.Y2 - bloat || rb->sbox.Y2 <= res->inflated.Y1 + bloat)
+			return pcb_false;
+		return pcb_true;
+		break;
+	default:
+		assert(0);
+	}
+	return pcb_false;
+}
+
+/* main Expand routine.
+ *
+ * The expansion probe edge includes the clearance and half thickness
+ * as the search is performed in order to see everything relevant.
+ * The result is backed off by this amount before being returned.
+ * Targets (and other no-bloat routeboxes) go all the way to touching.
+ * This is accomplished by backing off the probe edge when checking
+ * for touch against such an object. Usually the expanding edge
+ * bumps into neighboring pins on the same device that require a
+ * clearance, preventing seeing a target immediately. Rather than await
+ * another expansion to actually touch the target, the edge breaker code
+ * looks past the clearance to see these targets even though they
+ * weren't actually touched in the expansion.
+ */
+struct E_result *Expand(rtree_t * rtree, edge_t * e, const BoxType * box)
+{
+	static struct E_result ans;
+	int noshrink;									/* bit field of which edges to not shrink */
+
+	ans.bloat = AutoRouteParameters.bloat;
+	ans.orig = *box;
+	ans.n = ans.e = ans.s = ans.w = NULL;
+
+	/* the inflated box must be bloated in all directions that it might
+	 * hit something in order to guarantee that we see object in the
+	 * tree it might hit. The tree holds objects bloated by their own
+	 * clearance so we are guaranteed to honor that.
+	 */
+	switch (e->expand_dir) {
+	case ALL:
+		ans.inflated.X1 = (e->rb->came_from == EAST ? ans.orig.X1 : 0);
+		ans.inflated.Y1 = (e->rb->came_from == SOUTH ? ans.orig.Y1 : 0);
+		ans.inflated.X2 = (e->rb->came_from == WEST ? ans.orig.X2 : PCB->MaxWidth);
+		ans.inflated.Y2 = (e->rb->came_from == NORTH ? ans.orig.Y2 : PCB->MaxHeight);
+		if (e->rb->came_from == NORTH)
+			ans.done = noshrink = _SOUTH;
+		else if (e->rb->came_from == EAST)
+			ans.done = noshrink = _WEST;
+		else if (e->rb->came_from == SOUTH)
+			ans.done = noshrink = _NORTH;
+		else if (e->rb->came_from == WEST)
+			ans.done = noshrink = _EAST;
+		else
+			ans.done = noshrink = 0;
+		break;
+	case NORTH:
+		ans.done = _SOUTH + _EAST + _WEST;
+		noshrink = _SOUTH;
+		ans.inflated.X1 = box->X1 - ans.bloat;
+		ans.inflated.X2 = box->X2 + ans.bloat;
+		ans.inflated.Y2 = box->Y2;
+		ans.inflated.Y1 = 0;				/* far north */
+		break;
+	case NE:
+		ans.done = _SOUTH + _WEST;
+		noshrink = 0;
+		ans.inflated.X1 = box->X1 - ans.bloat;
+		ans.inflated.X2 = PCB->MaxWidth;
+		ans.inflated.Y2 = box->Y2 + ans.bloat;
+		ans.inflated.Y1 = 0;
+		break;
+	case EAST:
+		ans.done = _NORTH + _SOUTH + _WEST;
+		noshrink = _WEST;
+		ans.inflated.Y1 = box->Y1 - ans.bloat;
+		ans.inflated.Y2 = box->Y2 + ans.bloat;
+		ans.inflated.X1 = box->X1;
+		ans.inflated.X2 = PCB->MaxWidth;
+		break;
+	case SE:
+		ans.done = _NORTH + _WEST;
+		noshrink = 0;
+		ans.inflated.X1 = box->X1 - ans.bloat;
+		ans.inflated.X2 = PCB->MaxWidth;
+		ans.inflated.Y2 = PCB->MaxHeight;
+		ans.inflated.Y1 = box->Y1 - ans.bloat;
+		break;
+	case SOUTH:
+		ans.done = _NORTH + _EAST + _WEST;
+		noshrink = _NORTH;
+		ans.inflated.X1 = box->X1 - ans.bloat;
+		ans.inflated.X2 = box->X2 + ans.bloat;
+		ans.inflated.Y1 = box->Y1;
+		ans.inflated.Y2 = PCB->MaxHeight;
+		break;
+	case SW:
+		ans.done = _NORTH + _EAST;
+		noshrink = 0;
+		ans.inflated.X1 = 0;
+		ans.inflated.X2 = box->X2 + ans.bloat;
+		ans.inflated.Y2 = PCB->MaxHeight;
+		ans.inflated.Y1 = box->Y1 - ans.bloat;
+		break;
+	case WEST:
+		ans.done = _NORTH + _SOUTH + _EAST;
+		noshrink = _EAST;
+		ans.inflated.Y1 = box->Y1 - ans.bloat;
+		ans.inflated.Y2 = box->Y2 + ans.bloat;
+		ans.inflated.X1 = 0;
+		ans.inflated.X2 = box->X2;
+		break;
+	case NW:
+		ans.done = _SOUTH + _EAST;
+		noshrink = 0;
+		ans.inflated.X1 = 0;
+		ans.inflated.X2 = box->X2 + ans.bloat;
+		ans.inflated.Y2 = box->Y2 + ans.bloat;
+		ans.inflated.Y1 = 0;
+		break;
+	default:
+		noshrink = ans.done = 0;
+		assert(0);
+	}
+	ans.keep = e->rb->style->Clearance;
+	ans.parent = nonhomeless_parent(e->rb);
+	r_search(rtree, &ans.inflated, NULL, __Expand_this_rect, &ans, NULL);
+/* because the overlaping boxes are found in random order, some blockers
+ * may have limited edges prematurely, so we check if the blockers realy
+ * are blocking, and make another try if not
+ */
+	if (ans.n && !boink_box(ans.n, &ans, NORTH))
+		ans.inflated.Y1 = 0;
+	else
+		ans.done |= _NORTH;
+	if (ans.e && !boink_box(ans.e, &ans, EAST))
+		ans.inflated.X2 = PCB->MaxWidth;
+	else
+		ans.done |= _EAST;
+	if (ans.s && !boink_box(ans.s, &ans, SOUTH))
+		ans.inflated.Y2 = PCB->MaxHeight;
+	else
+		ans.done |= _SOUTH;
+	if (ans.w && !boink_box(ans.w, &ans, WEST))
+		ans.inflated.X1 = 0;
+	else
+		ans.done |= _WEST;
+	if (ans.done != _NORTH + _EAST + _SOUTH + _WEST) {
+		r_search(rtree, &ans.inflated, NULL, __Expand_this_rect, &ans, NULL);
+	}
+	if ((noshrink & _NORTH) == 0)
+		ans.inflated.Y1 += ans.bloat;
+	if ((noshrink & _EAST) == 0)
+		ans.inflated.X2 -= ans.bloat;
+	if ((noshrink & _SOUTH) == 0)
+		ans.inflated.Y2 -= ans.bloat;
+	if ((noshrink & _WEST) == 0)
+		ans.inflated.X1 += ans.bloat;
+	return &ans;
+}
+
+/* blocker_to_heap puts the blockers into a heap so they
+ * can be retrieved in clockwise order. If a blocker
+ * is also a target, it gets put into the vector too.
+ * It returns 1 for any fixed blocker that is not part
+ * of this net and zero otherwise.
+ */
+static int blocker_to_heap(heap_t * heap, routebox_t * rb, BoxType * box, direction_t dir)
+{
+	BoxType b = rb->sbox;
+	if (rb->style->Clearance > AutoRouteParameters.style->Clearance)
+		b = bloat_box(&b, rb->style->Clearance - AutoRouteParameters.style->Clearance);
+	b = clip_box(&b, box);
+	assert(box_is_good(&b));
+	/* we want to look at the blockers clockwise around the box */
+	switch (dir) {
+		/* we need to use the other coordinate fraction to resolve
+		 * ties since we want the shorter of the furthest
+		 * first.
+		 */
+	case NORTH:
+		heap_insert(heap, b.X1 - b.X1 / (b.X2 + 1.0), rb);
+		break;
+	case EAST:
+		heap_insert(heap, b.Y1 - b.Y1 / (b.Y2 + 1.0), rb);
+		break;
+	case SOUTH:
+		heap_insert(heap, -(b.X2 + b.X1 / (b.X2 + 1.0)), rb);
+		break;
+	case WEST:
+		heap_insert(heap, -(b.Y2 + b.Y1 / (b.Y2 + 1.0)), rb);
+		break;
+	default:
+		assert(0);
+	}
+	if (rb->flags.fixed && !rb->flags.target && !rb->flags.source)
+		return 1;
+	return 0;
+}
+
+/* this creates an EXPANSION_AREA to bridge small gaps or,
+ * (more commonly) create a supper-thin box to provide a
+ * home for an expansion edge.
+ */
+static routebox_t *CreateBridge(const BoxType * area, routebox_t * parent, direction_t dir)
+{
+	routebox_t *rb = (routebox_t *) malloc(sizeof(*rb));
+	memset((void *) rb, 0, sizeof(*rb));
+	assert(area && parent);
+	init_const_box(rb, area->X1, area->Y1, area->X2, area->Y2, 0);
+	rb->group = parent->group;
+	rb->type = EXPANSION_AREA;
+	rb->came_from = dir;
+	rb->cost_point = closest_point_in_box(&parent->cost_point, area);
+	rb->cost = parent->cost + cost_to_point_on_layer(&parent->cost_point, &rb->cost_point, rb->group);
+	rb->parent.expansion_area = route_parent(parent);
+	if (rb->parent.expansion_area->flags.homeless)
+		RB_up_count(rb->parent.expansion_area);
+	rb->flags.homeless = 1;
+	rb->flags.nobloat = 1;
+	rb->style = parent->style;
+	rb->conflicts_with = parent->conflicts_with;
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_EDGES)
+	showroutebox(rb);
+#endif
+	return rb;
+}
+
+/* moveable_edge prepares the new search edges based on the
+ * starting box, direction and blocker if any.
+ */
+void
+moveable_edge(vector_t * result, const BoxType * box, direction_t dir,
+							routebox_t * rb,
+							routebox_t * blocker, edge_t * e, rtree_t * targets,
+							struct routeone_state *s, rtree_t * tree, vector_t * area_vec)
+{
+	BoxType b;
+	assert(box_is_good(box));
+	b = *box;
+	/* for the cardinal directions, move the box to overlap the
+	 * the parent by 1 unit. Corner expansions overlap more
+	 * and their starting boxes are pre-prepared.
+	 * Check if anything is headed off the board edges
+	 */
+	switch (dir) {
+	default:
+		break;
+	case NORTH:
+		b.Y2 = b.Y1;
+		b.Y1--;
+		if (b.Y1 <= AutoRouteParameters.bloat)
+			return;										/* off board edge */
+		break;
+	case EAST:
+		b.X1 = b.X2;
+		b.X2++;
+		if (b.X2 >= PCB->MaxWidth - AutoRouteParameters.bloat)
+			return;										/* off board edge */
+		break;
+	case SOUTH:
+		b.Y1 = b.Y2;
+		b.Y2++;
+		if (b.Y2 >= PCB->MaxHeight - AutoRouteParameters.bloat)
+			return;										/* off board edge */
+		break;
+	case WEST:
+		b.X2 = b.X1;
+		b.X1--;
+		if (b.X1 <= AutoRouteParameters.bloat)
+			return;										/* off board edge */
+		break;
+	case NE:
+		if (b.Y1 <= AutoRouteParameters.bloat + 1 && b.X2 >= PCB->MaxWidth - AutoRouteParameters.bloat - 1)
+			return;										/* off board edge */
+		if (b.Y1 <= AutoRouteParameters.bloat + 1)
+			dir = EAST;								/* north off board edge */
+		if (b.X2 >= PCB->MaxWidth - AutoRouteParameters.bloat - 1)
+			dir = NORTH;							/* east off board edge */
+		break;
+	case SE:
+		if (b.Y2 >= PCB->MaxHeight - AutoRouteParameters.bloat - 1 && b.X2 >= PCB->MaxWidth - AutoRouteParameters.bloat - 1)
+			return;										/* off board edge */
+		if (b.Y2 >= PCB->MaxHeight - AutoRouteParameters.bloat - 1)
+			dir = EAST;								/* south off board edge */
+		if (b.X2 >= PCB->MaxWidth - AutoRouteParameters.bloat - 1)
+			dir = SOUTH;							/* east off board edge */
+		break;
+	case SW:
+		if (b.Y2 >= PCB->MaxHeight - AutoRouteParameters.bloat - 1 && b.X1 <= AutoRouteParameters.bloat + 1)
+			return;										/* off board edge */
+		if (b.Y2 >= PCB->MaxHeight - AutoRouteParameters.bloat - 1)
+			dir = WEST;								/* south off board edge */
+		if (b.X1 <= AutoRouteParameters.bloat + 1)
+			dir = SOUTH;							/* west off board edge */
+		break;
+	case NW:
+		if (b.Y1 <= AutoRouteParameters.bloat + 1 && b.X1 <= AutoRouteParameters.bloat + 1)
+			return;										/* off board edge */
+		if (b.Y1 <= AutoRouteParameters.bloat + 1)
+			dir = WEST;								/* north off board edge */
+		if (b.X1 <= AutoRouteParameters.bloat + 1)
+			dir = NORTH;							/* west off board edge */
+		break;
+	}
+
+	if (!blocker) {
+		edge_t *ne;
+		routebox_t *nrb = CreateBridge(&b, rb, dir);
+		/* move the cost point in corner expansions
+		 * these boxes are bigger, so move close to the target
+		 */
+		if (dir == NE || dir == SE || dir == SW || dir == NW) {
+			CheapPointType p;
+			p = closest_point_in_box(&nrb->cost_point, &e->mincost_target->sbox);
+			p = closest_point_in_box(&p, &b);
+			nrb->cost += cost_to_point_on_layer(&p, &nrb->cost_point, nrb->group);
+			nrb->cost_point = p;
+		}
+		ne = CreateEdge(nrb, nrb->cost_point.X, nrb->cost_point.Y, nrb->cost, NULL, dir, targets);
+		vector_append(result, ne);
+	}
+	else if (AutoRouteParameters.with_conflicts && !blocker->flags.target
+					 && !blocker->flags.fixed && !blocker->flags.touched && !blocker->flags.source && blocker->type != EXPANSION_AREA) {
+		edge_t *ne;
+		routebox_t *nrb;
+		/* make a bridge to the edge of the blocker
+		 * in all directions from there
+		 */
+		switch (dir) {
+		case NORTH:
+			b.Y1 = blocker->sbox.Y2 - 1;
+			break;
+		case EAST:
+			b.X2 = blocker->sbox.X1 + 1;
+			break;
+		case SOUTH:
+			b.Y2 = blocker->sbox.Y1 + 1;
+			break;
+		case WEST:
+			b.X1 = blocker->sbox.X2 - 1;
+			break;
+		default:
+			assert(0);
+		}
+		if (!box_is_good(&b))
+			return;										/* how did this happen ? */
+		nrb = CreateBridge(&b, rb, dir);
+		r_insert_entry(tree, &nrb->box, 1);
+		vector_append(area_vec, nrb);
+		nrb->flags.homeless = 0;		/* not homeless any more */
+		/* mark this one as conflicted */
+		path_conflicts(nrb, blocker, pcb_true);
+		/* and make an expansion edge */
+		nrb->cost_point = closest_point_in_box(&nrb->cost_point, &blocker->sbox);
+		nrb->cost +=
+			cost_to_point_on_layer(&nrb->parent.expansion_area->cost_point, &nrb->cost_point, nrb->group) * CONFLICT_PENALTY(blocker);
+
+		ne = CreateEdge(nrb, nrb->cost_point.X, nrb->cost_point.Y, nrb->cost, NULL, ALL, targets);
+		ne->flags.is_interior = 1;
+		vector_append(result, ne);
+	}
+#if 1
+	else if (blocker->type == EXPANSION_AREA) {
+		if (blocker->cost < rb->cost || blocker->cost <= rb->cost +
+				cost_to_point_on_layer(&blocker->cost_point, &rb->cost_point, rb->group))
+			return;
+		if (blocker->conflicts_with || rb->conflicts_with)
+			return;
+		/* does the blocker overlap this routebox ?? */
+		/* does this re-parenting operation leave a memory leak? */
+		if (blocker->parent.expansion_area->flags.homeless)
+			RB_down_count(blocker->parent.expansion_area);
+		blocker->parent.expansion_area = rb;
+		return;
+	}
+#endif
+	else if (blocker->flags.target) {
+		routebox_t *nrb;
+		edge_t *ne;
+		b = bloat_box(&b, 1);
+		if (!box_intersect(&b, &blocker->sbox)) {
+			/* if the expansion edge stopped before touching, expand the bridge */
+			switch (dir) {
+			case NORTH:
+				b.Y1 -= AutoRouteParameters.bloat + 1;
+				break;
+			case EAST:
+				b.X2 += AutoRouteParameters.bloat + 1;
+				break;
+			case SOUTH:
+				b.Y2 += AutoRouteParameters.bloat + 1;
+				break;
+			case WEST:
+				b.X1 -= AutoRouteParameters.bloat + 1;
+				break;
+			default:
+				assert(0);
+			}
+		}
+		assert(box_intersect(&b, &blocker->sbox));
+		b = shrink_box(&b, 1);
+		nrb = CreateBridge(&b, rb, dir);
+		r_insert_entry(tree, &nrb->box, 1);
+		vector_append(area_vec, nrb);
+		nrb->flags.homeless = 0;		/* not homeless any more */
+		ne = CreateEdge(nrb, nrb->cost_point.X, nrb->cost_point.Y, nrb->cost, blocker, dir, NULL);
+		best_path_candidate(s, ne, blocker);
+		DestroyEdge(&ne);
+	}
+}
+
+struct break_info {
+	heap_t *heap;
+	routebox_t *parent;
+	BoxType box;
+	direction_t dir;
+	pcb_bool ignore_source;
+};
+
+static r_dir_t __GatherBlockers(const BoxType * box, void *cl)
+{
+	routebox_t *rb = (routebox_t *) box;
+	struct break_info *bi = (struct break_info *) cl;
+	BoxType b;
+
+	if (bi->parent == rb || rb->flags.touched || bi->parent->parent.expansion_area == rb)
+		return R_DIR_NOT_FOUND;
+	if (rb->flags.source && bi->ignore_source)
+		return R_DIR_NOT_FOUND;
+	b = rb->sbox;
+	if (rb->style->Clearance > AutoRouteParameters.style->Clearance)
+		b = bloat_box(&b, rb->style->Clearance - AutoRouteParameters.style->Clearance);
+	if (b.X2 <= bi->box.X1 || b.X1 >= bi->box.X2 || b.Y1 >= bi->box.Y2 || b.Y2 <= bi->box.Y1)
+		return R_DIR_NOT_FOUND;
+	if (blocker_to_heap(bi->heap, rb, &bi->box, bi->dir))
+		return R_DIR_FOUND_CONTINUE;
+	return R_DIR_NOT_FOUND;
+}
+
+/* shrink the box to the last limit for the previous direction,
+ * i.e. if dir is SOUTH, then this means fixing up an EAST leftover
+ * edge, which would be the southern most edge for that example.
+ */
+static inline BoxType previous_edge(Coord last, direction_t i, const BoxType * b)
+{
+	BoxType db = *b;
+	switch (i) {
+	case EAST:
+		db.X1 = last;
+		break;
+	case SOUTH:
+		db.Y1 = last;
+		break;
+	case WEST:
+		db.X2 = last;
+		break;
+	default:
+		Message(PCB_MSG_DEFAULT, "previous edge bogus direction!");
+		assert(0);
+	}
+	return db;
+}
+
+/* Break all the edges of the box that need breaking, handling
+ * targets as they are found, and putting any moveable edges
+ * in the return vector.
+ */
+vector_t *BreakManyEdges(struct routeone_state * s, rtree_t * targets, rtree_t * tree,
+												 vector_t * area_vec, struct E_result * ans, routebox_t * rb, edge_t * e)
+{
+	struct break_info bi;
+	vector_t *edges;
+	heap_t *heap[4];
+	Coord first, last;
+	Coord bloat;
+	direction_t dir;
+	routebox_t fake;
+
+	edges = vector_create();
+	bi.ignore_source = rb->parent.expansion_area->flags.source;
+	bi.parent = rb;
+	/* we add 2 to the bloat.
+	 * 1 will get us to the actual blocker that Expand() hit
+	 * but 1 more is needed because the new expansion edges
+	 * move out by 1 so they don't overlap their parents
+	 * this extra expansion could "trap" the edge if
+	 * there is a blocker 2 units from the original rb,
+	 * it is 1 unit from the new expansion edge which
+	 * would prevent expansion. So we want to break the
+	 * edge on it now to avoid the trap.
+	 */
+
+	bloat = AutoRouteParameters.bloat + 2;
+	/* for corner expansion, we need to have a fake blocker
+	 * to prevent expansion back where we came from since
+	 * we still need to break portions of all 4 edges
+	 */
+	if (e->expand_dir == NE || e->expand_dir == SE || e->expand_dir == SW || e->expand_dir == NW) {
+		BoxType *fb = (BoxType *) & fake.sbox;
+		memset(&fake, 0, sizeof(fake));
+		*fb = e->rb->sbox;
+		fake.flags.fixed = 1;				/* this stops expansion there */
+		fake.type = LINE;
+		fake.style = AutoRouteParameters.style;
+#ifndef NDEBUG
+		/* the routbox_is_good checker wants a lot more! */
+		fake.flags.inited = 1;
+		fb = (BoxType *) & fake.box;
+		*fb = e->rb->sbox;
+		fake.same_net.next = fake.same_net.prev = &fake;
+		fake.same_subnet.next = fake.same_subnet.prev = &fake;
+		fake.original_subnet.next = fake.original_subnet.prev = &fake;
+		fake.different_net.next = fake.different_net.prev = &fake;
+#endif
+	}
+	/* gather all of the blockers in heaps so they can be accessed
+	 * in clockwise order, which allows finding corners that can
+	 * be expanded.
+	 */
+	for (dir = NORTH; dir <= WEST; dir = directionIncrement(dir)) {
+		int tmp;
+		/* don't break the edge we came from */
+		if (e->expand_dir != ((dir + 2) % 4)) {
+			heap[dir] = heap_create();
+			bi.box = bloat_box(&rb->sbox, bloat);
+			bi.heap = heap[dir];
+			bi.dir = dir;
+			/* convert to edge */
+			switch (dir) {
+			case NORTH:
+				bi.box.Y2 = bi.box.Y1 + bloat + 1;
+				/* for corner expansion, block the start edges and
+				 * limit the blocker search to only the new edge segment
+				 */
+				if (e->expand_dir == SE || e->expand_dir == SW)
+					blocker_to_heap(heap[dir], &fake, &bi.box, dir);
+				if (e->expand_dir == SE)
+					bi.box.X1 = e->rb->sbox.X2;
+				if (e->expand_dir == SW)
+					bi.box.X2 = e->rb->sbox.X1;
+				r_search(tree, &bi.box, NULL, __GatherBlockers, &bi, &tmp);
+				rb->n = tmp;
+				break;
+			case EAST:
+				bi.box.X1 = bi.box.X2 - bloat - 1;
+				/* corner, same as above */
+				if (e->expand_dir == SW || e->expand_dir == NW)
+					blocker_to_heap(heap[dir], &fake, &bi.box, dir);
+				if (e->expand_dir == SW)
+					bi.box.Y1 = e->rb->sbox.Y2;
+				if (e->expand_dir == NW)
+					bi.box.Y2 = e->rb->sbox.Y1;
+				r_search(tree, &bi.box, NULL, __GatherBlockers, &bi, &tmp);
+				rb->e = tmp;
+				break;
+			case SOUTH:
+				bi.box.Y1 = bi.box.Y2 - bloat - 1;
+				/* corner, same as above */
+				if (e->expand_dir == NE || e->expand_dir == NW)
+					blocker_to_heap(heap[dir], &fake, &bi.box, dir);
+				if (e->expand_dir == NE)
+					bi.box.X1 = e->rb->sbox.X2;
+				if (e->expand_dir == NW)
+					bi.box.X2 = e->rb->sbox.X1;
+				r_search(tree, &bi.box, NULL, __GatherBlockers, &bi, &tmp);
+				rb->s = tmp;
+				break;
+			case WEST:
+				bi.box.X2 = bi.box.X1 + bloat + 1;
+				/* corner, same as above */
+				if (e->expand_dir == NE || e->expand_dir == SE)
+					blocker_to_heap(heap[dir], &fake, &bi.box, dir);
+				if (e->expand_dir == SE)
+					bi.box.Y1 = e->rb->sbox.Y2;
+				if (e->expand_dir == NE)
+					bi.box.Y2 = e->rb->sbox.Y1;
+				r_search(tree, &bi.box, NULL, __GatherBlockers, &bi, &tmp);
+				rb->w = tmp;
+				break;
+			default:
+				assert(0);
+			}
+		}
+		else
+			heap[dir] = NULL;
+	}
+#if 1
+	rb->cost += (rb->n + rb->e + rb->s + rb->w) * AutoRouteParameters.CongestionPenalty / box_area(rb->sbox);
+#endif
+/* now handle the blockers:
+ * Go around the expansion area clockwise (North->East->South->West)
+ * pulling blockers from the heap (which makes them come out in the right
+ * order). Break the edges on the blocker and make the segments and corners
+ * moveable as possible.
+ */
+	first = last = -1;
+	for (dir = NORTH; dir <= WEST; dir = directionIncrement(dir)) {
+		if (heap[dir] && !heap_is_empty(heap[dir])) {
+			/* pull the very first one out of the heap outside of the
+			 * heap loop because it is special; it can be part of a corner
+			 */
+			routebox_t *blk = (routebox_t *) heap_remove_smallest(heap[dir]);
+			BoxType b = rb->sbox;
+			struct broken_boxes broke = break_box_edge(&b, dir, blk);
+			if (broke.is_valid_left) {
+				/* if last > 0, then the previous edge had a segment
+				 * joining this one, so it forms a valid corner expansion
+				 */
+				if (last > 0) {
+					/* make a corner expansion */
+					BoxType db = b;
+					switch (dir) {
+					case EAST:
+						/* possible NE expansion */
+						db.X1 = last;
+						db.Y2 = MIN(db.Y2, broke.left.Y2);
+						break;
+					case SOUTH:
+						/* possible SE expansion */
+						db.Y1 = last;
+						db.X1 = MAX(db.X1, broke.left.X1);
+						break;
+					case WEST:
+						/* possible SW expansion */
+						db.X2 = last;
+						db.Y1 = MAX(db.Y1, broke.left.Y1);
+						break;
+					default:
+						assert(0);
+						break;
+					}
+					moveable_edge(edges, &db, (direction_t) (dir + 3), rb, NULL, e, targets, s, NULL, NULL);
+				}
+				else if (dir == NORTH) {	/* north is start, so nothing "before" it */
+					/* save for a possible corner once we've
+					 * finished circling the box
+					 */
+					first = MAX(b.X1, broke.left.X2);
+				}
+				else {
+					/* this is just a boring straight expansion
+					 * since the orthogonal segment was blocked
+					 */
+					moveable_edge(edges, &broke.left, dir, rb, NULL, e, targets, s, NULL, NULL);
+				}
+			}													/* broke.is_valid_left */
+			else if (last > 0) {
+				/* if the last one didn't become a corner,
+				 * we still want to expand it straight out
+				 * in the direction of the previous edge,
+				 * which it belongs to.
+				 */
+				BoxType db = previous_edge(last, dir, &rb->sbox);
+				moveable_edge(edges, &db, (direction_t) (dir - 1), rb, NULL, e, targets, s, NULL, NULL);
+			}
+			if (broke.is_valid_center && !blk->flags.source)
+				moveable_edge(edges, &broke.center, dir, rb, blk, e, targets, s, tree, area_vec);
+			/* this is the heap extraction loop. We break out
+			 * if there's nothing left in the heap, but if we * are blocked all the way to the far edge, we can
+			 * just leave stuff in the heap when it is destroyed
+			 */
+			while (broke.is_valid_right) {
+				/* move the box edge to the next potential free point */
+				switch (dir) {
+				case NORTH:
+					last = b.X1 = MAX(broke.right.X1, b.X1);
+					break;
+				case EAST:
+					last = b.Y1 = MAX(broke.right.Y1, b.Y1);
+					break;
+				case SOUTH:
+					last = b.X2 = MIN(broke.right.X2, b.X2);
+					break;
+				case WEST:
+					last = b.Y2 = MIN(broke.right.Y2, b.Y2);
+					break;
+				default:
+					assert(0);
+				}
+				if (heap_is_empty(heap[dir]))
+					break;
+				blk = (routebox_t *) heap_remove_smallest(heap[dir]);
+				broke = break_box_edge(&b, dir, blk);
+				if (broke.is_valid_left)
+					moveable_edge(edges, &broke.left, dir, rb, NULL, e, targets, s, NULL, NULL);
+				if (broke.is_valid_center && !blk->flags.source)
+					moveable_edge(edges, &broke.center, dir, rb, blk, e, targets, s, tree, area_vec);
+			}
+			if (!broke.is_valid_right)
+				last = -1;
+		}
+		else {											/* if (heap[dir]) */
+
+			/* nothing touched this edge! Expand the whole edge unless
+			 * (1) it hit the board edge or (2) was the source of our expansion
+			 *
+			 * for this case (of hitting nothing) we give up trying for corner
+			 * expansions because it is likely that they're not possible anyway
+			 */
+			if ((e->expand_dir == ALL ? e->rb->came_from : e->expand_dir) != ((dir + 2) % 4)) {
+				/* ok, we are not going back on ourselves, and the whole edge seems free */
+				moveable_edge(edges, &rb->sbox, dir, rb, NULL, e, targets, s, NULL, NULL);
+			}
+
+			if (last > 0) {
+				/* expand the leftover from the prior direction */
+				BoxType db = previous_edge(last, dir, &rb->sbox);
+				moveable_edge(edges, &db, (direction_t) (dir - 1), rb, NULL, e, targets, s, NULL, NULL);
+			}
+			last = -1;
+		}
+	}															/* for loop */
+	/* finally, check for the NW corner now that we've come full circle */
+	if (first > 0 && last > 0) {
+		BoxType db = rb->sbox;
+		db.X2 = first;
+		db.Y2 = last;
+		moveable_edge(edges, &db, NW, rb, NULL, e, targets, s, NULL, NULL);
+	}
+	else {
+		if (first > 0) {
+			BoxType db = rb->sbox;
+			db.X2 = first;
+			moveable_edge(edges, &db, NORTH, rb, NULL, e, targets, s, NULL, NULL);
+		}
+		else if (last > 0) {
+			BoxType db = rb->sbox;
+			db.Y2 = last;
+			moveable_edge(edges, &db, WEST, rb, NULL, e, targets, s, NULL, NULL);
+		}
+	}
+	/* done with all expansion edges of this box */
+	for (dir = NORTH; dir <= WEST; dir = directionIncrement(dir)) {
+		if (heap[dir])
+			heap_destroy(&heap[dir]);
+	}
+	return edges;
+}
+
+static routebox_t *rb_source(routebox_t * rb)
+{
+	while (rb && !rb->flags.source) {
+		assert(rb->type == EXPANSION_AREA);
+		rb = rb->parent.expansion_area;
+	}
+	assert(rb);
+	return rb;
+}
+
+/* ------------ */
+
+struct foib_info {
+	const BoxType *box;
+	routebox_t *intersect;
+	jmp_buf env;
+};
+
+static r_dir_t foib_rect_in_reg(const BoxType * box, void *cl)
+{
+	struct foib_info *foib = (struct foib_info *) cl;
+	BoxType rbox;
+	routebox_t *rb = (routebox_t *) box;
+	if (rb->flags.touched)
+		return R_DIR_NOT_FOUND;
+/*  if (rb->type == EXPANSION_AREA && !rb->flags.is_via)*/
+	/*   return R_DIR_NOT_FOUND; */
+	rbox = bloat_routebox(rb);
+	if (!box_intersect(&rbox, foib->box))
+		return R_DIR_NOT_FOUND;
+	/* this is an intersector! */
+	foib->intersect = (routebox_t *) box;
+	longjmp(foib->env, 1);				/* skip to the end! */
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static routebox_t *FindOneInBox(rtree_t * rtree, routebox_t * rb)
+{
+	struct foib_info foib;
+	BoxType r;
+
+	r = rb->sbox;
+	foib.box = &r;
+	foib.intersect = NULL;
+
+	if (setjmp(foib.env) == 0)
+		r_search(rtree, &r, NULL, foib_rect_in_reg, &foib, NULL);
+	return foib.intersect;
+}
+
+struct therm_info {
+	routebox_t *plane;
+	BoxType query;
+	jmp_buf env;
+};
+static r_dir_t ftherm_rect_in_reg(const BoxType * box, void *cl)
+{
+	routebox_t *rbox = (routebox_t *) box;
+	struct therm_info *ti = (struct therm_info *) cl;
+	BoxType sq, sb;
+
+	if (rbox->type != PIN && rbox->type != VIA && rbox->type != VIA_SHADOW)
+		return R_DIR_NOT_FOUND;
+	if (rbox->group != ti->plane->group)
+		return R_DIR_NOT_FOUND;
+
+	sb = shrink_routebox(rbox);
+	switch (rbox->type) {
+	case PIN:
+		sq = shrink_box(&ti->query, rbox->parent.pin->Thickness);
+		if (!box_intersect(&sb, &sq))
+			return R_DIR_NOT_FOUND;
+		sb.X1 = rbox->parent.pin->X;
+		sb.Y1 = rbox->parent.pin->Y;
+		break;
+	case VIA:
+		if (rbox->flags.fixed) {
+			sq = shrink_box(&ti->query, rbox->parent.via->Thickness);
+			sb.X1 = rbox->parent.pin->X;
+			sb.Y1 = rbox->parent.pin->Y;
+		}
+		else {
+			sq = shrink_box(&ti->query, rbox->style->Diameter);
+			sb.X1 = CENTER_X(sb);
+			sb.Y1 = CENTER_Y(sb);
+		}
+		if (!box_intersect(&sb, &sq))
+			return R_DIR_NOT_FOUND;
+		break;
+	case VIA_SHADOW:
+		sq = shrink_box(&ti->query, rbox->style->Diameter);
+		if (!box_intersect(&sb, &sq))
+			return R_DIR_NOT_FOUND;
+		sb.X1 = CENTER_X(sb);
+		sb.Y1 = CENTER_Y(sb);
+		break;
+	default:
+		assert(0);
+	}
+	ti->plane = rbox;
+	longjmp(ti->env, 1);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+/* check for a pin or via target that a polygon can just use a thermal to connect to */
+routebox_t *FindThermable(rtree_t * rtree, routebox_t * rb)
+{
+	struct therm_info info;
+
+	info.plane = rb;
+	info.query = shrink_routebox(rb);
+
+	if (setjmp(info.env) == 0) {
+		r_search(rtree, &info.query, NULL, ftherm_rect_in_reg, &info, NULL);
+		return NULL;
+	}
+	return info.plane;
+}
+
+/*--------------------------------------------------------------------
+ * Route-tracing code: once we've got a path of expansion boxes, trace
+ * a line through them to actually create the connection.
+ */
+static void RD_DrawThermal(routedata_t * rd, Coord X, Coord Y, pcb_cardinal_t group, pcb_cardinal_t layer, routebox_t * subnet, pcb_bool is_bad)
+{
+	routebox_t *rb;
+	rb = (routebox_t *) malloc(sizeof(*rb));
+	memset((void *) rb, 0, sizeof(*rb));
+	init_const_box(rb, X, Y, X + 1, Y + 1, 0);
+	rb->group = group;
+	rb->layer = layer;
+	rb->flags.fixed = 0;
+	rb->flags.is_bad = is_bad;
+	rb->flags.is_odd = AutoRouteParameters.is_odd;
+	rb->flags.circular = 0;
+	rb->style = AutoRouteParameters.style;
+	rb->type = THERMAL;
+	InitLists(rb);
+	MergeNets(rb, subnet, NET);
+	MergeNets(rb, subnet, SUBNET);
+	/* add it to the r-tree, this may be the whole route! */
+	r_insert_entry(rd->layergrouptree[rb->group], &rb->box, 1);
+	rb->flags.homeless = 0;
+}
+
+static void RD_DrawVia(routedata_t * rd, Coord X, Coord Y, Coord radius, routebox_t * subnet, pcb_bool is_bad)
+{
+	routebox_t *rb, *first_via = NULL;
+	int i;
+	int ka = AutoRouteParameters.style->Clearance;
+	PinType *live_via = NULL;
+
+	if (conf_core.editor.live_routing) {
+		live_via = CreateNewVia(PCB->Data, X, Y, radius * 2,
+														2 * AutoRouteParameters.style->Clearance, 0, AutoRouteParameters.style->Hole, NULL, MakeFlags(0));
+		if (live_via != NULL)
+			DrawVia(live_via);
+	}
+
+	/* a via cuts through every layer group */
+	for (i = 0; i < max_group; i++) {
+		if (!is_layer_group_active[i])
+			continue;
+		rb = (routebox_t *) malloc(sizeof(*rb));
+		memset((void *) rb, 0, sizeof(*rb));
+		init_const_box(rb,
+									 /*X1 */ X - radius, /*Y1 */ Y - radius,
+									 /*X2 */ X + radius + 1, /*Y2 */ Y + radius + 1, ka);
+		rb->group = i;
+		rb->flags.fixed = 0;				/* indicates that not on PCB yet */
+		rb->flags.is_odd = AutoRouteParameters.is_odd;
+		rb->flags.is_bad = is_bad;
+		rb->came_from = ALL;
+		rb->flags.circular = pcb_true;
+		rb->style = AutoRouteParameters.style;
+		rb->pass = AutoRouteParameters.pass;
+		if (first_via == NULL) {
+			rb->type = VIA;
+			rb->parent.via = NULL;		/* indicates that not on PCB yet */
+			first_via = rb;
+			/* only add the first via to mtspace, not the shadows too */
+			mtspace_add(rd->mtspace, &rb->box, rb->flags.is_odd ? ODD : EVEN, rb->style->Clearance);
+		}
+		else {
+			rb->type = VIA_SHADOW;
+			rb->parent.via_shadow = first_via;
+		}
+		InitLists(rb);
+		/* add these to proper subnet. */
+		MergeNets(rb, subnet, NET);
+		MergeNets(rb, subnet, SUBNET);
+		assert(__routebox_is_good(rb));
+		/* and add it to the r-tree! */
+		r_insert_entry(rd->layergrouptree[rb->group], &rb->box, 1);
+		rb->flags.homeless = 0;			/* not homeless anymore */
+		rb->livedraw_obj.via = live_via;
+	}
+}
+
+static void
+RD_DrawLine(routedata_t * rd,
+						Coord X1, Coord Y1, Coord X2,
+						Coord Y2, Coord halfthick, pcb_cardinal_t group, routebox_t * subnet, pcb_bool is_bad, pcb_bool is_45)
+{
+	/* we hold the line in a queue to concatenate segments that
+	 * ajoin one another. That reduces the number of things in
+	 * the trees and allows conflict boxes to be larger, both of
+	 * which are really useful.
+	 */
+	static Coord qX1 = -1, qY1, qX2, qY2;
+	static Coord qhthick;
+	static pcb_cardinal_t qgroup;
+	static pcb_bool qis_45, qis_bad;
+	static routebox_t *qsn;
+
+	routebox_t *rb;
+	Coord ka = AutoRouteParameters.style->Clearance;
+
+	/* don't draw zero-length segments. */
+	if (X1 == X2 && Y1 == Y2)
+		return;
+	if (qX1 == -1) {							/* first ever */
+		qX1 = X1;
+		qY1 = Y1;
+		qX2 = X2;
+		qY2 = Y2;
+		qhthick = halfthick;
+		qgroup = group;
+		qis_45 = is_45;
+		qis_bad = is_bad;
+		qsn = subnet;
+		return;
+	}
+	/* Check if the lines concatenat. We only check the
+	 * normal expected nextpoint=lastpoint condition
+	 */
+	if (X1 == qX2 && Y1 == qY2 && qhthick == halfthick && qgroup == group) {
+		if (qX1 == qX2 && X1 == X2) {	/* everybody on the same X here */
+			qY2 = Y2;
+			return;
+		}
+		if (qY1 == qY2 && Y1 == Y2) {	/* same Y all around */
+			qX2 = X2;
+			return;
+		}
+	}
+	/* dump the queue, no match here */
+	if (qX1 == -1)
+		return;											/* but not this! */
+	rb = (routebox_t *) malloc(sizeof(*rb));
+	memset((void *) rb, 0, sizeof(*rb));
+	assert(is_45 ? (PCB_ABS(qX2 - qX1) == PCB_ABS(qY2 - qY1))	/* line must be 45-degrees */
+				 : (qX1 == qX2 || qY1 == qY2) /* line must be ortho */ );
+	init_const_box(rb,
+								 /*X1 */ MIN(qX1, qX2) - qhthick,
+								 /*Y1 */ MIN(qY1, qY2) - qhthick,
+								 /*X2 */ MAX(qX1, qX2) + qhthick + 1,
+								 /*Y2 */ MAX(qY1, qY2) + qhthick + 1, ka);
+	rb->group = qgroup;
+	rb->type = LINE;
+	rb->parent.line = NULL;				/* indicates that not on PCB yet */
+	rb->flags.fixed = 0;					/* indicates that not on PCB yet */
+	rb->flags.is_odd = AutoRouteParameters.is_odd;
+	rb->flags.is_bad = qis_bad;
+	rb->came_from = ALL;
+	rb->flags.homeless = 0;				/* we're putting this in the tree */
+	rb->flags.nonstraight = qis_45;
+	rb->flags.bl_to_ur = ((qX2 >= qX1 && qY2 <= qY1)
+												|| (qX2 <= qX1 && qY2 >= qY1));
+	rb->style = AutoRouteParameters.style;
+	rb->pass = AutoRouteParameters.pass;
+	InitLists(rb);
+	/* add these to proper subnet. */
+	MergeNets(rb, qsn, NET);
+	MergeNets(rb, qsn, SUBNET);
+	assert(__routebox_is_good(rb));
+	/* and add it to the r-tree! */
+	r_insert_entry(rd->layergrouptree[rb->group], &rb->box, 1);
+
+	if (conf_core.editor.live_routing) {
+		LayerType *layer = LAYER_PTR(PCB->LayerGroups.Entries[rb->group][0]);
+		LineType *line = CreateNewLineOnLayer(layer, qX1, qY1, qX2, qY2,
+																					2 * qhthick, 0, MakeFlags(0));
+		rb->livedraw_obj.line = line;
+		if (line != NULL)
+			DrawLine(layer, line);
+	}
+
+	/* and to the via space structures */
+	if (AutoRouteParameters.use_vias)
+		mtspace_add(rd->mtspace, &rb->box, rb->flags.is_odd ? ODD : EVEN, rb->style->Clearance);
+	usedGroup[rb->group] = pcb_true;
+	/* and queue this one */
+	qX1 = X1;
+	qY1 = Y1;
+	qX2 = X2;
+	qY2 = Y2;
+	qhthick = halfthick;
+	qgroup = group;
+	qis_45 = is_45;
+	qis_bad = is_bad;
+	qsn = subnet;
+}
+
+static pcb_bool
+RD_DrawManhattanLine(routedata_t * rd,
+										 const BoxType * box1, const BoxType * box2,
+										 CheapPointType start, CheapPointType end,
+										 Coord halfthick, pcb_cardinal_t group, routebox_t * subnet, pcb_bool is_bad, pcb_bool last_was_x)
+{
+	CheapPointType knee = start;
+	if (end.X == start.X) {
+		RD_DrawLine(rd, start.X, start.Y, end.X, end.Y, halfthick, group, subnet, is_bad, pcb_false);
+		return pcb_false;
+	}
+	else if (end.Y == start.Y) {
+		RD_DrawLine(rd, start.X, start.Y, end.X, end.Y, halfthick, group, subnet, is_bad, pcb_false);
+		return pcb_true;
+	}
+	/* find where knee belongs */
+	if (point_in_box(box1, end.X, start.Y)
+			|| point_in_box(box2, end.X, start.Y)) {
+		knee.X = end.X;
+		knee.Y = start.Y;
+	}
+	else {
+		knee.X = start.X;
+		knee.Y = end.Y;
+	}
+	if ((knee.X == end.X && !last_was_x) && (point_in_box(box1, start.X, end.Y)
+																					 || point_in_box(box2, start.X, end.Y))) {
+		knee.X = start.X;
+		knee.Y = end.Y;
+	}
+	assert(AutoRouteParameters.is_smoothing || point_in_closed_box(box1, knee.X, knee.Y)
+				 || point_in_closed_box(box2, knee.X, knee.Y));
+
+	if (1 || !AutoRouteParameters.is_smoothing) {
+		/* draw standard manhattan paths */
+		RD_DrawLine(rd, start.X, start.Y, knee.X, knee.Y, halfthick, group, subnet, is_bad, pcb_false);
+		RD_DrawLine(rd, knee.X, knee.Y, end.X, end.Y, halfthick, group, subnet, is_bad, pcb_false);
+	}
+	else {
+		/* draw 45-degree path across knee */
+		Coord len45 = MIN(PCB_ABS(start.X - end.X), PCB_ABS(start.Y - end.Y));
+		CheapPointType kneestart = knee, kneeend = knee;
+		if (kneestart.X == start.X)
+			kneestart.Y += (kneestart.Y > start.Y) ? -len45 : len45;
+		else
+			kneestart.X += (kneestart.X > start.X) ? -len45 : len45;
+		if (kneeend.X == end.X)
+			kneeend.Y += (kneeend.Y > end.Y) ? -len45 : len45;
+		else
+			kneeend.X += (kneeend.X > end.X) ? -len45 : len45;
+		RD_DrawLine(rd, start.X, start.Y, kneestart.X, kneestart.Y, halfthick, group, subnet, is_bad, pcb_false);
+		RD_DrawLine(rd, kneestart.X, kneestart.Y, kneeend.X, kneeend.Y, halfthick, group, subnet, is_bad, pcb_true);
+		RD_DrawLine(rd, kneeend.X, kneeend.Y, end.X, end.Y, halfthick, group, subnet, is_bad, pcb_false);
+	}
+	return (knee.X != end.X);
+}
+
+/* for smoothing, don't pack traces to min clearance gratuitously */
+#if 0
+static void add_clearance(CheapPointType * nextpoint, const BoxType * b)
+{
+	if (nextpoint->X == b->X1) {
+		if (nextpoint->X + AutoRouteParameters.style->Clearance < (b->X1 + b->X2) / 2)
+			nextpoint->X += AutoRouteParameters.style->Clearance;
+		else
+			nextpoint->X = (b->X1 + b->X2) / 2;
+	}
+	else if (nextpoint->X == b->X2) {
+		if (nextpoint->X - AutoRouteParameters.style->Clearance > (b->X1 + b->X2) / 2)
+			nextpoint->X -= AutoRouteParameters.style->Clearance;
+		else
+			nextpoint->X = (b->X1 + b->X2) / 2;
+	}
+	else if (nextpoint->Y == b->Y1) {
+		if (nextpoint->Y + AutoRouteParameters.style->Clearance < (b->Y1 + b->Y2) / 2)
+			nextpoint->Y += AutoRouteParameters.style->Clearance;
+		else
+			nextpoint->Y = (b->Y1 + b->Y2) / 2;
+	}
+	else if (nextpoint->Y == b->Y2) {
+		if (nextpoint->Y - AutoRouteParameters.style->Clearance > (b->Y1 + b->Y2) / 2)
+			nextpoint->Y -= AutoRouteParameters.style->Clearance;
+		else
+			nextpoint->Y = (b->Y1 + b->Y2) / 2;
+	}
+}
+#endif
+
+/* This back-traces the expansion boxes along the best path
+ * it draws the lines that will make the actual path.
+ * during refinement passes, it should try to maximize the area
+ * for other tracks so routing completion is easier.
+ *
+ * during smoothing passes, it should try to make a better path,
+ * possibly using diagonals, etc. The path boxes are larger on
+ * average now so there is more possiblity to decide on a nice
+ * path. Any combination of lines and arcs is possible, so long
+ * as they don't poke more than half thick outside the path box.
+ */
+
+static void TracePath(routedata_t * rd, routebox_t * path, const routebox_t * target, routebox_t * subnet, pcb_bool is_bad)
+{
+	pcb_bool last_x = pcb_false;
+	Coord halfwidth = HALF_THICK(AutoRouteParameters.style->Thick);
+	Coord radius = HALF_THICK(AutoRouteParameters.style->Diameter);
+	CheapPointType lastpoint, nextpoint;
+	routebox_t *lastpath;
+	BoxType b;
+
+	assert(subnet->style == AutoRouteParameters.style);
+	/*XXX: because we round up odd thicknesses, there's the possibility that
+	 * a connecting line end-point might be 0.005 mil off the "real" edge.
+	 * don't worry about this because line *thicknesses* are always >= 0.01 mil. */
+
+	/* if we start with a thermal the target was a plane
+	 * or the target was a pin and the source a plane
+	 * in which case this thermal is the whole path
+	 */
+	if (path->flags.is_thermal) {
+		/* the target was a plane, so we need to find a good spot for the via
+		 * now. It's logical to place it close to the source box which
+		 * is where we're utlimately headed on this path. However, it
+		 * must reside in the plane as well as the via area too.
+		 */
+		nextpoint.X = CENTER_X(path->sbox);
+		nextpoint.Y = CENTER_Y(path->sbox);
+		if (path->parent.expansion_area->flags.is_via) {
+			TargetPoint(&nextpoint, rb_source(path));
+			/* nextpoint is the middle of the source terminal now */
+			b = clip_box(&path->sbox, &path->parent.expansion_area->sbox);
+			nextpoint = closest_point_in_box(&nextpoint, &b);
+			/* now it's in the via and plane near the source */
+		}
+		else {											/* no via coming, target must have been a pin */
+
+			assert(target->type == PIN);
+			TargetPoint(&nextpoint, target);
+		}
+		assert(point_in_box(&path->sbox, nextpoint.X, nextpoint.Y));
+		RD_DrawThermal(rd, nextpoint.X, nextpoint.Y, path->group, path->layer, subnet, is_bad);
+	}
+	else {
+		/* start from best place of target box */
+		lastpoint.X = CENTER_X(target->sbox);
+		lastpoint.Y = CENTER_Y(target->sbox);
+		TargetPoint(&lastpoint, target);
+		if (AutoRouteParameters.last_smooth && box_in_box(&path->sbox, &target->sbox))
+			path = path->parent.expansion_area;
+		b = path->sbox;
+		if (path->flags.circular)
+			b = shrink_box(&b, MIN(b.X2 - b.X1, b.Y2 - b.Y1) / 5);
+		nextpoint = closest_point_in_box(&lastpoint, &b);
+		if (AutoRouteParameters.last_smooth)
+			RD_DrawLine(rd, lastpoint.X, lastpoint.Y, nextpoint.X, nextpoint.Y, halfwidth, path->group, subnet, is_bad, TRUE);
+		else
+			last_x = RD_DrawManhattanLine(rd, &target->sbox, &path->sbox,
+																		lastpoint, nextpoint, halfwidth, path->group, subnet, is_bad, last_x);
+	}
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_ROUTE_BOXES)
+	showroutebox(path);
+#if defined(ROUTE_VERBOSE)
+	pcb_printf("TRACEPOINT start %#mD\n", nextpoint.X, nextpoint.Y);
+#endif
+#endif
+
+	do {
+		lastpoint = nextpoint;
+		lastpath = path;
+		assert(path->type == EXPANSION_AREA);
+		path = path->parent.expansion_area;
+		b = path->sbox;
+		if (path->flags.circular)
+			b = shrink_box(&b, MIN(b.X2 - b.X1, b.Y2 - b.Y1) / 5);
+		assert(b.X1 != b.X2 && b.Y1 != b.Y2);	/* need someplace to put line! */
+		/* find point on path perimeter closest to last point */
+		/* if source terminal, try to hit a good place */
+		nextpoint = closest_point_in_box(&lastpoint, &b);
+#if 0
+		/* leave more clearance if this is a smoothing pass */
+		if (AutoRouteParameters.is_smoothing && (nextpoint.X != lastpoint.X || nextpoint.Y != lastpoint.Y))
+			add_clearance(&nextpoint, &b);
+#endif
+		if (path->flags.source && path->type != PLANE)
+			TargetPoint(&nextpoint, path);
+		assert(point_in_box(&lastpath->box, lastpoint.X, lastpoint.Y));
+		assert(point_in_box(&path->box, nextpoint.X, nextpoint.Y));
+#if defined(ROUTE_DEBUG_VERBOSE)
+		printf("TRACEPATH: ");
+		DumpRouteBox(path);
+		pcb_printf("TRACEPATH: point %#mD to point %#mD layer %d\n",
+							 lastpoint.X, lastpoint.Y, nextpoint.X, nextpoint.Y, path->group);
+#endif
+
+		/* draw orthogonal lines from lastpoint to nextpoint */
+		/* knee is placed in lastpath box */
+		/* should never cause line to leave union of lastpath/path boxes */
+		if (AutoRouteParameters.last_smooth)
+			RD_DrawLine(rd, lastpoint.X, lastpoint.Y, nextpoint.X, nextpoint.Y, halfwidth, path->group, subnet, is_bad, TRUE);
+		else
+			last_x = RD_DrawManhattanLine(rd, &lastpath->sbox, &path->sbox,
+																		lastpoint, nextpoint, halfwidth, path->group, subnet, is_bad, last_x);
+		if (path->flags.is_via) {		/* if via, then add via */
+#ifdef ROUTE_VERBOSE
+			printf(" (vias)");
+#endif
+			assert(point_in_box(&path->box, nextpoint.X, nextpoint.Y));
+			RD_DrawVia(rd, nextpoint.X, nextpoint.Y, radius, subnet, is_bad);
+		}
+
+		assert(lastpath->flags.is_via || path->group == lastpath->group);
+
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_ROUTE_BOXES)
+		showroutebox(path);
+#endif /* ROUTE_DEBUG && DEBUG_SHOW_ROUTE_BOXES */
+		/* if this is connected to a plane, draw the thermal */
+		if (path->flags.is_thermal || path->type == PLANE)
+			RD_DrawThermal(rd, lastpoint.X, lastpoint.Y, path->group, path->layer, subnet, is_bad);
+		/* when one hop from the source, make an extra path in *this* box */
+		if (path->type == EXPANSION_AREA && path->parent.expansion_area->flags.source && path->parent.expansion_area->type != PLANE) {
+			/* find special point on source (if it exists) */
+			if (TargetPoint(&lastpoint, path->parent.expansion_area)) {
+				lastpoint = closest_point_in_routebox(&lastpoint, path);
+				b = shrink_routebox(path);
+#if 0
+				if (AutoRouteParameters.is_smoothing)
+					add_clearance(&lastpoint, &b);
+#else
+				if (AutoRouteParameters.last_smooth)
+					RD_DrawLine(rd, lastpoint.X, lastpoint.Y, nextpoint.X, nextpoint.Y, halfwidth, path->group, subnet, is_bad, TRUE);
+				else
+#endif
+					last_x = RD_DrawManhattanLine(rd, &b, &b, nextpoint, lastpoint, halfwidth, path->group, subnet, is_bad, last_x);
+#if defined(ROUTE_DEBUG_VERBOSE)
+				printf("TRACEPATH: ");
+				DumpRouteBox(path);
+				pcb_printf
+					("TRACEPATH: (to source) point %#mD to point %#mD layer %d\n",
+					 nextpoint.X, nextpoint.Y, lastpoint.X, lastpoint.Y, path->group);
+#endif
+
+				nextpoint = lastpoint;
+			}
+		}
+	}
+	while (!path->flags.source);
+	/* flush the line queue */
+	RD_DrawLine(rd, -1, 0, 0, 0, 0, 0, NULL, pcb_false, pcb_false);
+
+	if (conf_core.editor.live_routing)
+		Draw();
+
+#ifdef ROUTE_DEBUG
+	if (ddraw != NULL)
+		ddraw->flush_debug_draw();
+#endif
+}
+
+/* create a fake "edge" used to defer via site searching. */
+static void
+CreateSearchEdge(struct routeone_state *s, vetting_t * work, edge_t * parent,
+								 routebox_t * rb, conflict_t conflict, rtree_t * targets, pcb_bool in_plane)
+{
+	routebox_t *target;
+	BoxType b;
+	cost_t cost;
+	assert(__routebox_is_good(rb));
+	/* find the cheapest target */
+#if 0
+	target = mincost_target_to_point(&parent->cost_point, max_group + 1, targets, parent->mincost_target);
+#else
+	target = parent->mincost_target;
+#endif
+	b = shrink_routebox(target);
+	cost = parent->cost_to_point + AutoRouteParameters.ViaCost + cost_to_layerless_box(&rb->cost_point, 0, &b);
+	if (cost < s->best_cost) {
+		edge_t *ne;
+		ne = (edge_t *) malloc(sizeof(*ne));
+		memset((void *) ne, 0, sizeof(*ne));
+		assert(ne);
+		ne->flags.via_search = 1;
+		ne->flags.in_plane = in_plane;
+		ne->rb = rb;
+		if (rb->flags.homeless)
+			RB_up_count(rb);
+		ne->work = work;
+		ne->mincost_target = target;
+		ne->flags.via_conflict_level = conflict;
+		ne->cost_to_point = parent->cost_to_point;
+		ne->cost_point = parent->cost_point;
+		ne->cost = cost;
+		heap_insert(s->workheap, ne->cost, ne);
+	}
+	else {
+		mtsFreeWork(&work);
+	}
+}
+
+static void add_or_destroy_edge(struct routeone_state *s, edge_t * e)
+{
+	e->cost = edge_cost(e, s->best_cost);
+	assert(__edge_is_good(e));
+	assert(is_layer_group_active[e->rb->group]);
+	if (e->cost < s->best_cost)
+		heap_insert(s->workheap, e->cost, e);
+	else
+		DestroyEdge(&e);
+}
+
+static void best_path_candidate(struct routeone_state *s, edge_t * e, routebox_t * best_target)
+{
+	e->cost = edge_cost(e, EXPENSIVE);
+	if (s->best_path == NULL || e->cost < s->best_cost) {
+#if defined(ROUTE_DEBUG) && defined (ROUTE_VERBOSE)
+		printf("New best path seen! cost = %f\n", e->cost);
+#endif
+		/* new best path! */
+		if (s->best_path && s->best_path->flags.homeless)
+			RB_down_count(s->best_path);
+		s->best_path = e->rb;
+		s->best_target = best_target;
+		s->best_cost = e->cost;
+		assert(s->best_cost >= 0);
+		/* don't free this when we destroy edge! */
+		if (s->best_path->flags.homeless)
+			RB_up_count(s->best_path);
+	}
+}
+
+
+/* vectors for via site candidates (see mtspace.h) */
+struct routeone_via_site_state {
+	vector_t *free_space_vec;
+	vector_t *lo_conflict_space_vec;
+	vector_t *hi_conflict_space_vec;
+};
+
+void
+add_via_sites(struct routeone_state *s,
+							struct routeone_via_site_state *vss,
+							mtspace_t * mtspace, routebox_t * within,
+							conflict_t within_conflict_level, edge_t * parent_edge, rtree_t * targets, Coord shrink, pcb_bool in_plane)
+{
+	Coord radius, clearance;
+	vetting_t *work;
+	BoxType region = shrink_routebox(within);
+	shrink_box(&region, shrink);
+
+	radius = HALF_THICK(AutoRouteParameters.style->Diameter);
+	clearance = AutoRouteParameters.style->Clearance;
+	assert(AutoRouteParameters.use_vias);
+	/* XXX: need to clip 'within' to shrunk_pcb_bounds, because when
+	   XXX: routing with conflicts may poke over edge. */
+
+	/* ask for a via box near our cost_point first */
+	work = mtspace_query_rect(mtspace, &region, radius, clearance,
+														NULL, vss->free_space_vec,
+														vss->lo_conflict_space_vec,
+														vss->hi_conflict_space_vec,
+														AutoRouteParameters.is_odd, AutoRouteParameters.with_conflicts, &parent_edge->cost_point);
+	if (!work)
+		return;
+	CreateSearchEdge(s, work, parent_edge, within, within_conflict_level, targets, in_plane);
+}
+
+void
+do_via_search(edge_t * search, struct routeone_state *s,
+							struct routeone_via_site_state *vss, mtspace_t * mtspace, rtree_t * targets)
+{
+	int i, j, count = 0;
+	Coord radius, clearance;
+	vetting_t *work;
+	routebox_t *within;
+	conflict_t within_conflict_level;
+
+	radius = HALF_THICK(AutoRouteParameters.style->Diameter);
+	clearance = AutoRouteParameters.style->Clearance;
+	work = mtspace_query_rect(mtspace, NULL, 0, 0,
+														search->work, vss->free_space_vec,
+														vss->lo_conflict_space_vec,
+														vss->hi_conflict_space_vec, AutoRouteParameters.is_odd, AutoRouteParameters.with_conflicts, NULL);
+	within = search->rb;
+	within_conflict_level = search->flags.via_conflict_level;
+	for (i = 0; i < 3; i++) {
+		vector_t *v =
+			(i == NO_CONFLICT ? vss->free_space_vec :
+			 i == LO_CONFLICT ? vss->lo_conflict_space_vec : i == HI_CONFLICT ? vss->hi_conflict_space_vec : NULL);
+		assert(v);
+		while (!vector_is_empty(v)) {
+			BoxType cliparea;
+			BoxType *area = (BoxType *) vector_remove_last(v);
+			if (!(i == NO_CONFLICT || AutoRouteParameters.with_conflicts)) {
+				free(area);
+				continue;
+			}
+			/* answers are bloated by radius + clearance */
+			cliparea = shrink_box(area, radius + clearance);
+			close_box(&cliparea);
+			free(area);
+			assert(box_is_good(&cliparea));
+			count++;
+			for (j = 0; j < max_group; j++) {
+				edge_t *ne;
+				if (j == within->group || !is_layer_group_active[j])
+					continue;
+				ne = CreateViaEdge(&cliparea, j, within, search, within_conflict_level, (conflict_t) i, targets);
+				add_or_destroy_edge(s, ne);
+			}
+		}
+	}
+	/* prevent freeing of work when this edge is destroyed */
+	search->flags.via_search = 0;
+	if (!work)
+		return;
+	CreateSearchEdge(s, work, search, within, within_conflict_level, targets, search->flags.in_plane);
+	assert(vector_is_empty(vss->free_space_vec));
+	assert(vector_is_empty(vss->lo_conflict_space_vec));
+	assert(vector_is_empty(vss->hi_conflict_space_vec));
+}
+
+/* vector of expansion areas to be eventually removed from r-tree
+ * this is a global for troubleshooting
+ */
+vector_t *area_vec;
+
+/* some routines for use in gdb while debugging */
+#if defined(ROUTE_DEBUG)
+static void list_conflicts(routebox_t * rb)
+{
+	int i, n;
+	if (!rb->conflicts_with)
+		return;
+	n = vector_size(rb->conflicts_with);
+	for (i = 0; i < n; i++)
+		printf("%p, ", (void *)vector_element(rb->conflicts_with, i));
+}
+
+static void show_area_vec(int lay)
+{
+	int n, save;
+
+	if (!area_vec)
+		return;
+	save = showboxen;
+	showboxen = lay;
+	for (n = 0; n < vector_size(area_vec); n++) {
+		routebox_t *rb = (routebox_t *) vector_element(area_vec, n);
+		showroutebox(rb);
+	}
+	showboxen = save;
+}
+
+static pcb_bool net_id(routebox_t * rb, long int id)
+{
+	routebox_t *p;
+	LIST_LOOP(rb, same_net, p);
+	if (p->flags.source && p->parent.pad->ID == id)
+		return pcb_true;
+	END_LOOP;
+	return pcb_false;
+}
+
+static void trace_parents(routebox_t * rb)
+{
+	while (rb && rb->type == EXPANSION_AREA) {
+		printf(" %p ->", (void *)rb);
+		rb = rb->parent.expansion_area;
+	}
+	if (rb)
+		printf(" %p is source\n", (void *)rb);
+	else
+		printf("NULL!\n");
+}
+
+static void show_one(routebox_t * rb)
+{
+	int save = showboxen;
+	showboxen = -1;
+	showroutebox(rb);
+	showboxen = save;
+}
+
+static void show_path(routebox_t * rb)
+{
+	while (rb && rb->type == EXPANSION_AREA) {
+		show_one(rb);
+		rb = rb->parent.expansion_area;
+	}
+	show_one(rb);
+}
+
+static void show_sources(routebox_t * rb)
+{
+	routebox_t *p;
+	if (!rb->flags.source && !rb->flags.target) {
+		printf("start with a source or target please\n");
+		return;
+	}
+	LIST_LOOP(rb, same_net, p);
+	if (p->flags.source)
+		show_one(p);
+	END_LOOP;
+}
+
+#endif
+
+static r_dir_t __conflict_source(const BoxType * box, void *cl)
+{
+	routebox_t *rb = (routebox_t *) box;
+	if (rb->flags.touched || rb->flags.fixed)
+		return R_DIR_NOT_FOUND;
+	else {
+		routebox_t *dis = (routebox_t *) cl;
+		path_conflicts(dis, rb, pcb_false);
+		touch_conflicts(dis->conflicts_with, 1);
+	}
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static void source_conflicts(rtree_t * tree, routebox_t * rb)
+{
+	if (!AutoRouteParameters.with_conflicts)
+		return;
+	r_search(tree, &rb->sbox, NULL, __conflict_source, rb, NULL);
+	touch_conflicts(NULL, 1);
+}
+
+struct routeone_status {
+	pcb_bool found_route;
+	int route_had_conflicts;
+	cost_t best_route_cost;
+	pcb_bool net_completely_routed;
+};
+
+
+static struct routeone_status RouteOne(routedata_t * rd, routebox_t * from, routebox_t * to, int max_edges)
+{
+	struct routeone_status result;
+	routebox_t *p;
+	int seen, i;
+	const BoxType **target_list;
+	int num_targets;
+	rtree_t *targets;
+	/* vector of source edges for filtering */
+	vector_t *source_vec;
+	/* working vector */
+	vector_t *edge_vec;
+
+	struct routeone_state s;
+	struct routeone_via_site_state vss;
+
+	assert(rd && from);
+	result.route_had_conflicts = 0;
+	/* no targets on to/from net need clearance areas */
+	LIST_LOOP(from, same_net, p);
+	p->flags.nobloat = 1;
+	END_LOOP;
+	/* set 'source' flags */
+	LIST_LOOP(from, same_subnet, p);
+	if (!p->flags.nonstraight)
+		p->flags.source = 1;
+	END_LOOP;
+
+	/* count up the targets */
+	num_targets = 0;
+	seen = 0;
+	/* remove source/target flags from non-straight obstacles, because they
+	 * don't fill their bounding boxes and so connecting to them
+	 * after we've routed is problematic.  Better solution? */
+	if (to) {											/* if we're routing to a specific target */
+		if (!to->flags.source) {		/* not already connected */
+			/* check that 'to' and 'from' are on the same net */
+			seen = 0;
+#ifndef NDEBUG
+			LIST_LOOP(from, same_net, p);
+			if (p == to)
+				seen = 1;
+			END_LOOP;
+#endif
+			assert(seen);							/* otherwise from and to are on different nets! */
+			/* set target flags only on 'to's subnet */
+			LIST_LOOP(to, same_subnet, p);
+			if (!p->flags.nonstraight && is_layer_group_active[p->group]) {
+				p->flags.target = 1;
+				num_targets++;
+			}
+			END_LOOP;
+		}
+	}
+	else {
+		/* all nodes on the net but not connected to from are targets */
+		LIST_LOOP(from, same_net, p);
+		if (!p->flags.source && is_layer_group_active[p->group]
+				&& !p->flags.nonstraight) {
+			p->flags.target = 1;
+			num_targets++;
+		}
+		END_LOOP;
+	}
+
+	/* if no targets, then net is done!  reset flags and return. */
+	if (num_targets == 0) {
+		LIST_LOOP(from, same_net, p);
+		p->flags.source = p->flags.target = p->flags.nobloat = 0;
+		END_LOOP;
+		result.found_route = pcb_false;
+		result.net_completely_routed = pcb_true;
+		result.best_route_cost = 0;
+		result.route_had_conflicts = 0;
+
+		return result;
+	}
+	result.net_completely_routed = pcb_false;
+
+	/* okay, there's stuff to route */
+	assert(!from->flags.target);
+	assert(num_targets > 0);
+	/* create list of target pointers and from that a r-tree of targets */
+	target_list = (const BoxType **) malloc(num_targets * sizeof(*target_list));
+	i = 0;
+	LIST_LOOP(from, same_net, p);
+	if (p->flags.target) {
+		target_list[i++] = &p->box;
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_TARGETS)
+		showroutebox(p);
+#endif
+	}
+	END_LOOP;
+	targets = r_create_tree((const BoxType **) target_list, i, 0);
+	assert(i <= num_targets);
+	free(target_list);
+
+	source_vec = vector_create();
+	/* touch the source subnet to prepare check for conflictors */
+	LIST_LOOP(from, same_subnet, p);
+	p->flags.touched = 1;
+	END_LOOP;
+	LIST_LOOP(from, same_subnet, p);
+	{
+		/* we need the test for 'source' because this box may be nonstraight */
+		if (p->flags.source && is_layer_group_active[p->group]) {
+			CheapPointType cp;
+			edge_t *e;
+			BoxType b = shrink_routebox(p);
+
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_SOURCES)
+			showroutebox(p);
+#endif
+			/* may expand in all directions from source; center edge cost point. */
+			/* note that planes shouldn't really expand, but we need an edge */
+
+			cp.X = CENTER_X(b);
+			cp.Y = CENTER_Y(b);
+			e = CreateEdge(p, cp.X, cp.Y, 0, NULL, ALL, targets);
+			cp = closest_point_in_box(&cp, &e->mincost_target->sbox);
+			cp = closest_point_in_box(&cp, &b);
+			e->cost_point = cp;
+			p->cost_point = cp;
+			source_conflicts(rd->layergrouptree[p->group], p);
+			vector_append(source_vec, e);
+		}
+	}
+	END_LOOP;
+	LIST_LOOP(from, same_subnet, p);
+	p->flags.touched = 0;
+	END_LOOP;
+	/* break source edges; some edges may be too near obstacles to be able
+	 * to exit from. */
+
+	/* okay, main expansion-search routing loop. */
+	/* set up the initial activity heap */
+	s.workheap = heap_create();
+	assert(s.workheap);
+	while (!vector_is_empty(source_vec)) {
+		edge_t *e = (edge_t *) vector_remove_last(source_vec);
+		assert(is_layer_group_active[e->rb->group]);
+		e->cost = edge_cost(e, EXPENSIVE);
+		heap_insert(s.workheap, e->cost, e);
+	}
+	vector_destroy(&source_vec);
+	/* okay, process items from heap until it is empty! */
+	s.best_path = NULL;
+	s.best_cost = EXPENSIVE;
+	area_vec = vector_create();
+	edge_vec = vector_create();
+	vss.free_space_vec = vector_create();
+	vss.lo_conflict_space_vec = vector_create();
+	vss.hi_conflict_space_vec = vector_create();
+	while (!heap_is_empty(s.workheap)) {
+		edge_t *e = (edge_t *) heap_remove_smallest(s.workheap);
+#ifdef ROUTE_DEBUG
+		if (aabort)
+			goto dontexpand;
+#endif
+		/* don't bother expanding this edge if the minimum possible edge cost
+		 * is already larger than the best edge cost we've found. */
+		if (s.best_path && e->cost >= s.best_cost) {
+			heap_free(s.workheap, KillEdge);
+			goto dontexpand;					/* skip this edge */
+		}
+		/* surprisingly it helps to give up and not try too hard to find
+		 * a route! This is not only faster, but results in better routing.
+		 * who would have guessed?
+		 */
+		if (seen++ > max_edges)
+			goto dontexpand;
+		assert(__edge_is_good(e));
+		/* mark or unmark conflictors as needed */
+		touch_conflicts(e->rb->conflicts_with, 1);
+		if (e->flags.via_search) {
+			do_via_search(e, &s, &vss, rd->mtspace, targets);
+			goto dontexpand;
+		}
+		/* we should never add edges on inactive layer groups to the heap. */
+		assert(is_layer_group_active[e->rb->group]);
+#if defined(ROUTE_DEBUG) && defined(DEBUG_SHOW_EXPANSION_BOXES)
+		/*showedge (e); */
+#endif
+		if (e->rb->flags.is_thermal) {
+			best_path_candidate(&s, e, e->mincost_target);
+			goto dontexpand;
+		}
+		/* for a plane, look for quick connections with thermals or vias */
+		if (e->rb->type == PLANE) {
+			routebox_t *pin = FindThermable(targets, e->rb);
+			if (pin) {
+				BoxType b = shrink_routebox(pin);
+				edge_t *ne;
+				routebox_t *nrb;
+				assert(pin->flags.target);
+				nrb = CreateExpansionArea(&b, e->rb->group, e->rb, pcb_true, e);
+				nrb->flags.is_thermal = 1;
+				/* moving through the plane is free */
+				e->cost_point.X = b.X1;
+				e->cost_point.Y = b.Y1;
+				ne = CreateEdge2(nrb, e->expand_dir, e, NULL, pin);
+				best_path_candidate(&s, ne, pin);
+				DestroyEdge(&ne);
+			}
+			else {
+				/* add in possible via sites in plane */
+				if (AutoRouteParameters.use_vias && e->cost + AutoRouteParameters.ViaCost < s.best_cost) {
+					/* we need a giant thermal */
+					routebox_t *nrb = CreateExpansionArea(&e->rb->sbox, e->rb->group, e->rb,
+																								pcb_true, e);
+					edge_t *ne = CreateEdge2(nrb, e->expand_dir, e, NULL,
+																	 e->mincost_target);
+					nrb->flags.is_thermal = 1;
+					add_via_sites(&s, &vss, rd->mtspace, nrb, NO_CONFLICT, ne, targets, e->rb->style->Diameter, pcb_true);
+				}
+			}
+			goto dontexpand;					/* planes only connect via thermals */
+		}
+		if (e->flags.is_via) {			/* special case via */
+			routebox_t *intersecting;
+			assert(AutoRouteParameters.use_vias);
+			assert(e->expand_dir == ALL);
+			assert(vector_is_empty(edge_vec));
+			/* if there is already something here on this layer (like an
+			 * EXPANSION_AREA), then we don't want to expand from here
+			 * at least not inside the expansion area. A PLANE on the
+			 * other hand may be a target, or not.
+			 */
+			intersecting = FindOneInBox(rd->layergrouptree[e->rb->group], e->rb);
+
+			if (intersecting && intersecting->flags.target && intersecting->type == PLANE) {
+				/* we have hit a plane */
+				edge_t *ne;
+				routebox_t *nrb;
+				BoxType b = shrink_routebox(e->rb);
+				/* limit via region to that inside the plane */
+				clip_box(&b, &intersecting->sbox);
+				nrb = CreateExpansionArea(&b, e->rb->group, e->rb, pcb_true, e);
+				nrb->flags.is_thermal = 1;
+				ne = CreateEdge2(nrb, e->expand_dir, e, NULL, intersecting);
+				best_path_candidate(&s, ne, intersecting);
+				DestroyEdge(&ne);
+				goto dontexpand;
+			}
+			else if (intersecting == NULL) {
+				/* this via candidate is in an open area; add it to r-tree as
+				 * an expansion area */
+				assert(e->rb->type == EXPANSION_AREA && e->rb->flags.is_via);
+				/*assert (!r_search (rd->layergrouptree[e->rb->group],
+				   &e->rb->box, NULL, no_planes,0));
+				 */
+				r_insert_entry(rd->layergrouptree[e->rb->group], &e->rb->box, 1);
+				e->rb->flags.homeless = 0;	/* not homeless any more */
+				/* add to vector of all expansion areas in r-tree */
+				vector_append(area_vec, e->rb);
+				/* mark reset refcount to 0, since this is not homeless any more. */
+				e->rb->refcount = 0;
+				/* go ahead and expand this edge! */
+			}
+			else if (1)
+				goto dontexpand;
+			else if (0) {							/* XXX: disabling this causes no via
+																   collisions. */
+				BoxType a = bloat_routebox(intersecting), b;
+				edge_t *ne;
+				int i, j;
+				/* something intersects this via candidate.  split via candidate
+				 * into pieces and add these pieces to the workheap. */
+				for (i = 0; i < 3; i++) {
+					for (j = 0; j < 3; j++) {
+						b = shrink_routebox(e->rb);
+						switch (i) {
+						case 0:
+							b.X2 = MIN(b.X2, a.X1);
+							break;						/* left */
+						case 1:
+							b.X1 = MAX(b.X1, a.X1);
+							b.X2 = MIN(b.X2, a.X2);
+							break;						/*c */
+						case 2:
+							b.X1 = MAX(b.X1, a.X2);
+							break;						/* right */
+						default:
+							assert(0);
+						}
+						switch (j) {
+						case 0:
+							b.Y2 = MIN(b.Y2, a.Y1);
+							break;						/* top */
+						case 1:
+							b.Y1 = MAX(b.Y1, a.Y1);
+							b.Y2 = MIN(b.Y2, a.Y2);
+							break;						/*c */
+						case 2:
+							b.Y1 = MAX(b.Y1, a.Y2);
+							break;						/* bottom */
+						default:
+							assert(0);
+						}
+						/* skip if this box is not valid */
+						if (!(b.X1 < b.X2 && b.Y1 < b.Y2))
+							continue;
+						if (i == 1 && j == 1) {
+							/* this bit of the via space is obstructed. */
+							if (intersecting->type == EXPANSION_AREA || intersecting->flags.fixed)
+								continue;				/* skip this bit, it's already been done. */
+							/* create an edge with conflicts, if enabled */
+							if (!AutoRouteParameters.with_conflicts)
+								continue;
+							ne = CreateEdgeWithConflicts(&b, intersecting, e, 1
+																					 /*cost penalty to box */
+																					 , targets);
+							add_or_destroy_edge(&s, ne);
+						}
+						else {
+							/* if this is not the intersecting piece, create a new
+							 * (hopefully unobstructed) via edge and add it back to the
+							 * workheap. */
+							ne = CreateViaEdge(&b, e->rb->group, e->rb->parent.expansion_area, e, e->flags.via_conflict_level, NO_CONFLICT
+																 /* value here doesn't matter */
+																 , targets);
+							add_or_destroy_edge(&s, ne);
+						}
+					}
+				}
+				goto dontexpand;
+			}
+			/* between the time these edges are inserted and the
+			 * time they are processed, new expansion boxes (which
+			 * conflict with these edges) may be added to the graph!
+			 * w.o vias this isn't a problem because the broken box
+			 * is not homeless. */
+		}
+		if (1) {
+			routebox_t *nrb;
+			struct E_result *ans;
+			BoxType b;
+			vector_t *broken;
+			if (e->flags.is_interior) {
+				assert(AutoRouteParameters.with_conflicts);	/* no interior edges unless
+																										   routing with conflicts! */
+				assert(e->rb->conflicts_with);
+				b = e->rb->sbox;
+				switch (e->rb->came_from) {
+				case NORTH:
+					b.Y2 = b.Y1 + 1;
+					b.X1 = CENTER_X(b);
+					b.X2 = b.X1 + 1;
+					break;
+				case EAST:
+					b.X1 = b.X2 - 1;
+					b.Y1 = CENTER_Y(b);
+					b.Y2 = b.Y1 + 1;
+					break;
+				case SOUTH:
+					b.Y1 = b.Y2 - 1;
+					b.X1 = CENTER_X(b);
+					b.X2 = b.X1 + 1;
+					break;
+				case WEST:
+					b.X2 = b.X1 + 1;
+					b.Y1 = CENTER_Y(b);
+					b.Y2 = b.Y1 + 1;
+					break;
+				default:
+					assert(0);
+				}
+			}
+			/* sources may not expand to their own edges because of
+			 * adjacent blockers.
+			 */
+			else if (e->rb->flags.source)
+				b = box_center(&e->rb->sbox);
+			else
+				b = e->rb->sbox;
+			ans = Expand(rd->layergrouptree[e->rb->group], e, &b);
+			if (!box_intersect(&ans->inflated, &ans->orig))
+				goto dontexpand;
+#if 0
+			/* skip if it didn't actually expand */
+			if (ans->inflated.X1 >= e->rb->sbox.X1 &&
+					ans->inflated.X2 <= e->rb->sbox.X2 && ans->inflated.Y1 >= e->rb->sbox.Y1 && ans->inflated.Y2 <= e->rb->sbox.Y2)
+				goto dontexpand;
+#endif
+
+			if (!box_is_good(&ans->inflated))
+				goto dontexpand;
+			nrb = CreateExpansionArea(&ans->inflated, e->rb->group, e->rb, pcb_true, e);
+			r_insert_entry(rd->layergrouptree[nrb->group], &nrb->box, 1);
+			vector_append(area_vec, nrb);
+			nrb->flags.homeless = 0;	/* not homeless any more */
+			broken = BreakManyEdges(&s, targets, rd->layergrouptree[nrb->group], area_vec, ans, nrb, e);
+			while (!vector_is_empty(broken)) {
+				edge_t *ne = (edge_t *) vector_remove_last(broken);
+				add_or_destroy_edge(&s, ne);
+			}
+			vector_destroy(&broken);
+
+			/* add in possible via sites in nrb */
+			if (AutoRouteParameters.use_vias && !e->rb->flags.is_via && e->cost + AutoRouteParameters.ViaCost < s.best_cost)
+				add_via_sites(&s, &vss, rd->mtspace, nrb, NO_CONFLICT, e, targets, 0, pcb_false);
+			goto dontexpand;
+		}
+	dontexpand:
+		DestroyEdge(&e);
+	}
+	touch_conflicts(NULL, 1);
+	heap_destroy(&s.workheap);
+	r_destroy_tree(&targets);
+	assert(vector_is_empty(edge_vec));
+	vector_destroy(&edge_vec);
+
+	/* we should have a path in best_path now */
+	if (s.best_path) {
+		routebox_t *rb;
+#ifdef ROUTE_VERBOSE
+		printf("%d:%d RC %.0f", ro++, seen, s.best_cost);
+#endif
+		result.found_route = pcb_true;
+		result.best_route_cost = s.best_cost;
+		/* determine if the best path had conflicts */
+		result.route_had_conflicts = 0;
+		if (AutoRouteParameters.with_conflicts && s.best_path->conflicts_with) {
+			while (!vector_is_empty(s.best_path->conflicts_with)) {
+				rb = (routebox_t *) vector_remove_last(s.best_path->conflicts_with);
+				rb->flags.is_bad = 1;
+				result.route_had_conflicts++;
+			}
+		}
+#ifdef ROUTE_VERBOSE
+		if (result.route_had_conflicts)
+			printf(" (%d conflicts)", result.route_had_conflicts);
+#endif
+		if (result.route_had_conflicts < AutoRouteParameters.hi_conflict) {
+			/* back-trace the path and add lines/vias to r-tree */
+			TracePath(rd, s.best_path, s.best_target, from, result.route_had_conflicts);
+			MergeNets(from, s.best_target, SUBNET);
+		}
+		else {
+#ifdef ROUTE_VERBOSE
+			printf(" (too many in fact)");
+#endif
+			result.found_route = pcb_false;
+		}
+#ifdef ROUTE_VERBOSE
+		printf("\n");
+#endif
+	}
+	else {
+#ifdef ROUTE_VERBOSE
+		printf("%d:%d NO PATH FOUND.\n", ro++, seen);
+#endif
+		result.best_route_cost = s.best_cost;
+		result.found_route = pcb_false;
+	}
+	/* now remove all expansion areas from the r-tree. */
+	while (!vector_is_empty(area_vec)) {
+		routebox_t *rb = (routebox_t *) vector_remove_last(area_vec);
+		assert(!rb->flags.homeless);
+		if (rb->conflicts_with && rb->parent.expansion_area->conflicts_with != rb->conflicts_with)
+			vector_destroy(&rb->conflicts_with);
+		r_delete_entry(rd->layergrouptree[rb->group], &rb->box);
+	}
+	vector_destroy(&area_vec);
+	/* clean up; remove all 'source', 'target', and 'nobloat' flags */
+	LIST_LOOP(from, same_net, p);
+	if (p->flags.source && p->conflicts_with)
+		vector_destroy(&p->conflicts_with);
+	p->flags.touched = p->flags.source = p->flags.target = p->flags.nobloat = 0;
+	END_LOOP;
+
+	vector_destroy(&vss.free_space_vec);
+	vector_destroy(&vss.lo_conflict_space_vec);
+	vector_destroy(&vss.hi_conflict_space_vec);
+
+	return result;
+}
+
+static void InitAutoRouteParameters(int pass, RouteStyleType * style, pcb_bool with_conflicts, pcb_bool is_smoothing, pcb_bool lastpass)
+{
+	int i;
+	/* routing style */
+	AutoRouteParameters.style = style;
+	AutoRouteParameters.bloat = style->Clearance + HALF_THICK(style->Thick);
+	/* costs */
+	AutoRouteParameters.ViaCost = PCB_INCH_TO_COORD(3.5) + style->Diameter * (is_smoothing ? 80 : 30);
+	AutoRouteParameters.LastConflictPenalty = (400 * pass / passes + 2) / (pass + 1);
+	AutoRouteParameters.ConflictPenalty = 4 * AutoRouteParameters.LastConflictPenalty;
+	AutoRouteParameters.JogPenalty = 1000 * (is_smoothing ? 20 : 4);
+	AutoRouteParameters.CongestionPenalty = 1e6;
+	AutoRouteParameters.MinPenalty = EXPENSIVE;
+	for (i = 0; i < max_group; i++) {
+		if (is_layer_group_active[i]) {
+			AutoRouteParameters.MinPenalty = MIN(x_cost[i], AutoRouteParameters.MinPenalty);
+			AutoRouteParameters.MinPenalty = MIN(y_cost[i], AutoRouteParameters.MinPenalty);
+		}
+	}
+	AutoRouteParameters.NewLayerPenalty = is_smoothing ? 0.5 * EXPENSIVE : 10 * AutoRouteParameters.ViaCost;
+	/* other */
+	AutoRouteParameters.hi_conflict = MAX(8 * (passes - pass + 1), 6);
+	AutoRouteParameters.is_odd = (pass & 1);
+	AutoRouteParameters.with_conflicts = with_conflicts;
+	AutoRouteParameters.is_smoothing = is_smoothing;
+	AutoRouteParameters.rip_always = is_smoothing;
+	AutoRouteParameters.last_smooth = 0;	/*lastpass; */
+	AutoRouteParameters.pass = pass + 1;
+}
+
+#ifndef NDEBUG
+r_dir_t bad_boy(const BoxType * b, void *cl)
+{
+	routebox_t *box = (routebox_t *) b;
+	if (box->type == EXPANSION_AREA)
+		return R_DIR_FOUND_CONTINUE;
+	return R_DIR_NOT_FOUND;
+}
+
+pcb_bool no_expansion_boxes(routedata_t * rd)
+{
+	int i;
+	BoxType big;
+	big.X1 = 0;
+	big.X2 = MAX_COORD;
+	big.Y1 = 0;
+	big.Y2 = MAX_COORD;
+	for (i = 0; i < max_group; i++) {
+		if (r_search(rd->layergrouptree[i], &big, NULL, bad_boy, NULL, NULL))
+			return pcb_false;
+	}
+	return pcb_true;
+}
+#endif
+
+static void ripout_livedraw_obj(routebox_t * rb)
+{
+	if (rb->type == LINE && rb->livedraw_obj.line) {
+		LayerType *layer = LAYER_PTR(PCB->LayerGroups.Entries[rb->group][0]);
+		EraseLine(rb->livedraw_obj.line);
+		DestroyObject(PCB->Data, PCB_TYPE_LINE, layer, rb->livedraw_obj.line, NULL);
+		rb->livedraw_obj.line = NULL;
+	}
+	if (rb->type == VIA && rb->livedraw_obj.via) {
+		EraseVia(rb->livedraw_obj.via);
+		DestroyObject(PCB->Data, PCB_TYPE_VIA, rb->livedraw_obj.via, NULL, NULL);
+		rb->livedraw_obj.via = NULL;
+	}
+}
+
+static r_dir_t ripout_livedraw_obj_cb(const BoxType * b, void *cl)
+{
+	routebox_t *box = (routebox_t *) b;
+	ripout_livedraw_obj(box);
+	return R_DIR_NOT_FOUND;
+}
+
+struct routeall_status {
+	/* --- for completion rate statistics ---- */
+	int total_subnets;
+	/* total subnets routed without conflicts */
+	int routed_subnets;
+	/* total subnets routed with conflicts */
+	int conflict_subnets;
+	/* net failted entirely */
+	int failed;
+	/* net was ripped */
+	int ripped;
+	int total_nets_routed;
+};
+
+static double calculate_progress(double this_heap_item, double this_heap_size, struct routeall_status *ras)
+{
+	double total_passes = passes + smoothes + 1;	/* + 1 is the refinement pass */
+	double this_pass = AutoRouteParameters.pass - 1;	/* Number passes from zero */
+	double heap_fraction = (double) (ras->routed_subnets + ras->conflict_subnets + ras->failed) / (double) ras->total_subnets;
+	double pass_fraction = (this_heap_item + heap_fraction) / this_heap_size;
+	double process_fraction = (this_pass + pass_fraction) / total_passes;
+
+	return process_fraction;
+}
+
+struct routeall_status RouteAll(routedata_t * rd)
+{
+	struct routeall_status ras;
+	struct routeone_status ros;
+	pcb_bool rip;
+	int request_cancel;
+#ifdef NET_HEAP
+	heap_t *net_heap;
+#endif
+	heap_t *this_pass, *next_pass, *tmp;
+	routebox_t *net, *p, *pp;
+	cost_t total_net_cost, last_cost = 0, this_cost = 0;
+	int i;
+	int this_heap_size;
+	int this_heap_item;
+
+	/* initialize heap for first pass;
+	 * do smallest area first; that makes
+	 * the subsequent costs more representative */
+	this_pass = heap_create();
+	next_pass = heap_create();
+#ifdef NET_HEAP
+	net_heap = heap_create();
+#endif
+	LIST_LOOP(rd->first_net, different_net, net);
+	{
+		double area;
+		BoxType bb = shrink_routebox(net);
+		LIST_LOOP(net, same_net, p);
+		{
+			MAKEMIN(bb.X1, p->sbox.X1);
+			MAKEMIN(bb.Y1, p->sbox.Y1);
+			MAKEMAX(bb.X2, p->sbox.X2);
+			MAKEMAX(bb.Y2, p->sbox.Y2);
+		}
+		END_LOOP;
+		area = (double) (bb.X2 - bb.X1) * (bb.Y2 - bb.Y1);
+		heap_insert(this_pass, area, net);
+	}
+	END_LOOP;
+
+	ras.total_nets_routed = 0;
+	/* refinement/finishing passes */
+	for (i = 0; i <= passes + smoothes; i++) {
+#ifdef ROUTE_VERBOSE
+		if (i > 0 && i <= passes)
+			printf("--------- STARTING REFINEMENT PASS %d ------------\n", i);
+		else if (i > passes)
+			printf("--------- STARTING SMOOTHING PASS %d -------------\n", i - passes);
+#endif
+		ras.total_subnets = ras.routed_subnets = ras.conflict_subnets = ras.failed = ras.ripped = 0;
+		assert(heap_is_empty(next_pass));
+
+		this_heap_size = heap_size(this_pass);
+		for (this_heap_item = 0; !heap_is_empty(this_pass); this_heap_item++) {
+#ifdef ROUTE_DEBUG
+			if (aabort)
+				break;
+#endif
+			net = (routebox_t *) heap_remove_smallest(this_pass);
+			InitAutoRouteParameters(i, net->style, i < passes, i > passes, i == passes + smoothes);
+			if (i > 0) {
+				/* rip up all unfixed traces in this net ? */
+				if (AutoRouteParameters.rip_always)
+					rip = pcb_true;
+				else {
+					rip = pcb_false;
+					LIST_LOOP(net, same_net, p);
+					if (p->flags.is_bad) {
+						rip = pcb_true;
+						break;
+					}
+					END_LOOP;
+				}
+
+				LIST_LOOP(net, same_net, p);
+				p->flags.is_bad = 0;
+				if (!p->flags.fixed) {
+#ifndef NDEBUG
+					pcb_bool del;
+#endif
+					assert(!p->flags.homeless);
+					if (rip) {
+						RemoveFromNet(p, NET);
+						RemoveFromNet(p, SUBNET);
+					}
+					if (AutoRouteParameters.use_vias && p->type != VIA_SHADOW && p->type != PLANE) {
+						mtspace_remove(rd->mtspace, &p->box, p->flags.is_odd ? ODD : EVEN, p->style->Clearance);
+						if (!rip)
+							mtspace_add(rd->mtspace, &p->box, p->flags.is_odd ? EVEN : ODD, p->style->Clearance);
+					}
+					if (rip) {
+						if (conf_core.editor.live_routing)
+							ripout_livedraw_obj(p);
+#ifndef NDEBUG
+						del =
+#endif
+							r_delete_entry(rd->layergrouptree[p->group], &p->box);
+#ifndef NDEBUG
+						assert(del);
+#endif
+					}
+					else {
+						p->flags.is_odd = AutoRouteParameters.is_odd;
+					}
+				}
+				END_LOOP;
+				if (conf_core.editor.live_routing)
+					Draw();
+				/* reset to original connectivity */
+				if (rip) {
+					ras.ripped++;
+					ResetSubnet(net);
+				}
+				else {
+					heap_insert(next_pass, 0, net);
+					continue;
+				}
+			}
+			/* count number of subnets */
+			FOREACH_SUBNET(net, p);
+			ras.total_subnets++;
+			END_FOREACH(net, p);
+			/* the first subnet doesn't require routing. */
+			ras.total_subnets--;
+			/* and re-route! */
+			total_net_cost = 0;
+			/* only route that which isn't fully routed */
+#ifdef ROUTE_DEBUG
+			if (ras.total_subnets == 0 || aabort)
+#else
+			if (ras.total_subnets == 0)
+#endif
+			{
+				heap_insert(next_pass, 0, net);
+				continue;
+			}
+
+			/* the loop here ensures that we get to all subnets even if
+			 * some of them are unreachable from the first subnet. */
+			LIST_LOOP(net, same_net, p);
+			{
+#ifdef NET_HEAP
+				BoxType b = shrink_routebox(p);
+				/* using a heap allows us to start from smaller objects and
+				 * end at bigger ones. also prefer to start at planes, then pads */
+				heap_insert(net_heap, (float) (b.X2 - b.X1) *
+#if defined(ROUTE_RANDOMIZED)
+										(0.3 + pcb_rand() / (RAND_MAX + 1.0)) *
+#endif
+										(b.Y2 - b.Y1) * (p->type == PLANE ? -1 : (p->type == PAD ? 1 : 10)), p);
+			}
+			END_LOOP;
+			ros.net_completely_routed = 0;
+			while (!heap_is_empty(net_heap)) {
+				p = (routebox_t *) heap_remove_smallest(net_heap);
+#endif
+				if (!p->flags.fixed || p->flags.subnet_processed || p->type == OTHER)
+					continue;
+
+				while (!ros.net_completely_routed) {
+					double percent;
+
+					assert(no_expansion_boxes(rd));
+					/* FIX ME: the number of edges to examine should be in autoroute parameters
+					 * i.e. the 2000 and 800 hard-coded below should be controllable by the user
+					 */
+					ros = RouteOne(rd, p, NULL, ((AutoRouteParameters.is_smoothing ? 2000 : 800) * (i + 1)) * routing_layers);
+					total_net_cost += ros.best_route_cost;
+					if (ros.found_route) {
+						if (ros.route_had_conflicts)
+							ras.conflict_subnets++;
+						else {
+							ras.routed_subnets++;
+							ras.total_nets_routed++;
+						}
+					}
+					else {
+						if (!ros.net_completely_routed)
+							ras.failed++;
+						/* don't bother trying any other source in this subnet */
+						LIST_LOOP(p, same_subnet, pp);
+						pp->flags.subnet_processed = 1;
+						END_LOOP;
+						break;
+					}
+					/* note that we can infer nothing about ras.total_subnets based
+					 * on the number of calls to RouteOne, because we may be unable
+					 * to route a net from a particular starting point, but perfectly
+					 * able to route it from some other. */
+					percent = calculate_progress(this_heap_item, this_heap_size, &ras);
+					request_cancel = gui->progress(percent * 100., 100, _("Autorouting tracks"));
+					if (request_cancel) {
+						ras.total_nets_routed = 0;
+						ras.conflict_subnets = 0;
+						Message(PCB_MSG_DEFAULT, "Autorouting cancelled\n");
+						goto out;
+					}
+				}
+			}
+#ifndef NET_HEAP
+			END_LOOP;
+#endif
+			if (!ros.net_completely_routed)
+				net->flags.is_bad = 1;	/* don't skip this the next round */
+
+			/* Route easiest nets from this pass first on next pass.
+			 * This works best because it's likely that the hardest
+			 * is the last one routed (since it has the most obstacles)
+			 * but it will do no good to rip it up and try it again
+			 * without first changing any of the other routes
+			 */
+			heap_insert(next_pass, total_net_cost, net);
+			if (total_net_cost < EXPENSIVE)
+				this_cost += total_net_cost;
+			/* reset subnet_processed flags */
+			LIST_LOOP(net, same_net, p);
+			{
+				p->flags.subnet_processed = 0;
+			}
+			END_LOOP;
+		}
+		/* swap this_pass and next_pass and do it all over again! */
+		ro = 0;
+		assert(heap_is_empty(this_pass));
+		tmp = this_pass;
+		this_pass = next_pass;
+		next_pass = tmp;
+#if defined(ROUTE_DEBUG) || defined (ROUTE_VERBOSE)
+		printf
+			("END OF PASS %d: %d/%d subnets routed without conflicts at cost %.0f, %d conflicts, %d failed %d ripped\n",
+			 i, ras.routed_subnets, ras.total_subnets, this_cost, ras.conflict_subnets, ras.failed, ras.ripped);
+#endif
+#ifdef ROUTE_DEBUG
+		if (aabort)
+			break;
+#endif
+		/* if no conflicts found, skip directly to smoothing pass! */
+		if (ras.conflict_subnets == 0 && ras.routed_subnets == ras.total_subnets && i <= passes)
+			i = passes - (smoothes ? 0 : 1);
+		/* if no changes in a smoothing round, then we're done */
+		if (this_cost == last_cost && i > passes && i < passes + smoothes)
+			i = passes + smoothes - 1;
+		last_cost = this_cost;
+		this_cost = 0;
+	}
+
+	Message(PCB_MSG_DEFAULT, "%d of %d nets successfully routed.\n", ras.routed_subnets, ras.total_subnets);
+
+out:
+	heap_destroy(&this_pass);
+	heap_destroy(&next_pass);
+#ifdef NET_HEAP
+	heap_destroy(&net_heap);
+#endif
+
+	/* no conflicts should be left at the end of the process. */
+	assert(ras.conflict_subnets == 0);
+
+	return ras;
+}
+
+struct fpin_info {
+	PinTypePtr pin;
+	Coord X, Y;
+	jmp_buf env;
+};
+
+static r_dir_t fpin_rect(const BoxType * b, void *cl)
+{
+	PinTypePtr pin = (PinTypePtr) b;
+	struct fpin_info *info = (struct fpin_info *) cl;
+	if (pin->X == info->X && pin->Y == info->Y) {
+		info->pin = (PinTypePtr) b;
+		longjmp(info->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static int FindPin(const BoxType * box, PinTypePtr * pin)
+{
+	struct fpin_info info;
+
+	info.pin = NULL;
+	info.X = box->X1;
+	info.Y = box->Y1;
+	if (setjmp(info.env) == 0)
+		r_search(PCB->Data->pin_tree, box, NULL, fpin_rect, &info, NULL);
+	else {
+		*pin = info.pin;
+		return PCB_TYPE_PIN;
+	}
+	if (setjmp(info.env) == 0)
+		r_search(PCB->Data->via_tree, box, NULL, fpin_rect, &info, NULL);
+	else {
+		*pin = info.pin;
+		return PCB_TYPE_VIA;
+	}
+	*pin = NULL;
+	return PCB_TYPE_NONE;
+}
+
+
+/* paths go on first 'on' layer in group */
+/* returns 'pcb_true' if any paths were added. */
+pcb_bool IronDownAllUnfixedPaths(routedata_t * rd)
+{
+	pcb_bool changed = pcb_false;
+	LayerTypePtr layer;
+	routebox_t *net, *p;
+	int i;
+	LIST_LOOP(rd->first_net, different_net, net);
+	{
+		LIST_LOOP(net, same_net, p);
+		{
+			if (!p->flags.fixed) {
+				/* find first on layer in this group */
+				assert(PCB->LayerGroups.Number[p->group] > 0);
+				assert(is_layer_group_active[p->group]);
+				for (i = 0, layer = NULL; i < PCB->LayerGroups.Number[p->group]; i++) {
+					layer = LAYER_PTR(PCB->LayerGroups.Entries[p->group][i]);
+					if (layer->On)
+						break;
+				}
+				assert(layer && layer->On);	/*at least one layer must be on in this group! */
+				assert(p->type != EXPANSION_AREA);
+				if (p->type == LINE) {
+					Coord halfwidth = HALF_THICK(p->style->Thick);
+					double th = halfwidth * 2 + 1;
+					BoxType b;
+					assert(p->parent.line == NULL);
+					/* orthogonal; thickness is 2*halfwidth */
+					/* flip coordinates, if bl_to_ur */
+					b = p->sbox;
+					total_wire_length += sqrt((b.X2 - b.X1 - th) * (b.X2 - b.X1 - th) + (b.Y2 - b.Y1 - th) * (b.Y2 - b.Y1 - th));
+					b = shrink_box(&b, halfwidth);
+					if (b.X2 == b.X1 + 1)
+						b.X2 = b.X1;
+					if (b.Y2 == b.Y1 + 1)
+						b.Y2 = b.Y1;
+					if (p->flags.bl_to_ur) {
+						Coord t;
+						t = b.X1;
+						b.X1 = b.X2;
+						b.X2 = t;
+					}
+					/* using CreateDrawn instead of CreateNew concatenates sequential lines */
+					p->parent.line = CreateDrawnLineOnLayer
+						(layer, b.X1, b.Y1, b.X2, b.Y2,
+						 p->style->Thick, p->style->Clearance * 2, MakeFlags(PCB_FLAG_AUTO | (conf_core.editor.clear_line ? PCB_FLAG_CLEARLINE : 0)));
+
+					if (p->parent.line) {
+						AddObjectToCreateUndoList(PCB_TYPE_LINE, layer, p->parent.line, p->parent.line);
+						changed = pcb_true;
+					}
+				}
+				else if (p->type == VIA || p->type == VIA_SHADOW) {
+					routebox_t *pp = (p->type == VIA_SHADOW) ? p->parent.via_shadow : p;
+					Coord radius = HALF_THICK(pp->style->Diameter);
+					BoxType b = shrink_routebox(p);
+					total_via_count++;
+					assert(pp->type == VIA);
+					if (pp->parent.via == NULL) {
+						assert(b.X1 + radius == b.X2 - radius);
+						assert(b.Y1 + radius == b.Y2 - radius);
+						pp->parent.via =
+							CreateNewVia(PCB->Data, b.X1 + radius,
+													 b.Y1 + radius,
+													 pp->style->Diameter, 2 * pp->style->Clearance, 0, pp->style->Hole, NULL, MakeFlags(PCB_FLAG_AUTO));
+						assert(pp->parent.via);
+						if (pp->parent.via) {
+							AddObjectToCreateUndoList(PCB_TYPE_VIA, pp->parent.via, pp->parent.via, pp->parent.via);
+							changed = pcb_true;
+						}
+					}
+					assert(pp->parent.via);
+					if (p->type == VIA_SHADOW) {
+						p->type = VIA;
+						p->parent.via = pp->parent.via;
+					}
+				}
+				else if (p->type == THERMAL)
+					/* nothing to do because, the via might not be there yet */ ;
+				else
+					assert(0);
+			}
+		}
+		END_LOOP;
+		/* loop again to place all the thermals now that the vias are down */
+		LIST_LOOP(net, same_net, p);
+		{
+			if (p->type == THERMAL) {
+				PinTypePtr pin = NULL;
+				/* thermals are alread a single point search, no need to shrink */
+				int type = FindPin(&p->box, &pin);
+				if (pin) {
+					AddObjectToClearPolyUndoList(type, pin->Element ? pin->Element : pin, pin, pin, pcb_false);
+					RestoreToPolygon(PCB->Data, PCB_TYPE_VIA, LAYER_PTR(p->layer), pin);
+					AddObjectToFlagUndoList(type, pin->Element ? pin->Element : pin, pin, pin);
+					ASSIGN_THERM(p->layer, PCB->ThermStyle, pin);
+					AddObjectToClearPolyUndoList(type, pin->Element ? pin->Element : pin, pin, pin, pcb_true);
+					ClearFromPolygon(PCB->Data, PCB_TYPE_VIA, LAYER_PTR(p->layer), pin);
+					changed = pcb_true;
+				}
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	return changed;
+}
+
+pcb_bool AutoRoute(pcb_bool selected)
+{
+	pcb_bool changed = pcb_false;
+	routedata_t *rd;
+	int i;
+
+	total_wire_length = 0;
+	total_via_count = 0;
+
+#ifdef ROUTE_DEBUG
+	ddraw = gui->request_debug_draw();
+	if (ddraw != NULL) {
+		ar_gc = ddraw->make_gc();
+		ddraw->set_line_cap(ar_gc, Round_Cap);
+	}
+#endif
+
+	for (i = 0; i < vtroutestyle_len(&PCB->RouteStyle); i++) {
+		if (PCB->RouteStyle.array[i].Thick == 0 ||
+				PCB->RouteStyle.array[i].Diameter == 0 || PCB->RouteStyle.array[i].Hole == 0 || PCB->RouteStyle.array[i].Clearance == 0) {
+			Message(PCB_MSG_DEFAULT, "You must define proper routing styles\n" "before auto-routing.\n");
+			return (pcb_false);
+		}
+	}
+	if (ratlist_length(&PCB->Data->Rat) == 0)
+		return (pcb_false);
+	SaveFindFlag(PCB_FLAG_DRC);
+	rd = CreateRouteData();
+
+	if (1) {
+		routebox_t *net, *rb, *last;
+		int i = 0;
+		/* count number of rats selected */
+		RAT_LOOP(PCB->Data);
+		{
+			if (!selected || TEST_FLAG(PCB_FLAG_SELECTED, line))
+				i++;
+		}
+		END_LOOP;
+#ifdef ROUTE_VERBOSE
+		printf("%d nets!\n", i);
+#endif
+		if (i == 0)
+			goto donerouting;					/* nothing to do here */
+		/* if only one rat selected, do things the quick way. =) */
+		if (i == 1) {
+			RAT_LOOP(PCB->Data);
+			if (!selected || TEST_FLAG(PCB_FLAG_SELECTED, line)) {
+				/* look up the end points of this rat line */
+				routebox_t *a;
+				routebox_t *b;
+				a = FindRouteBoxOnLayerGroup(rd, line->Point1.X, line->Point1.Y, line->group1);
+				b = FindRouteBoxOnLayerGroup(rd, line->Point2.X, line->Point2.Y, line->group2);
+				assert(a != NULL && b != NULL);
+				assert(a->style == b->style);
+/*
+	      if (a->type != PAD && b->type == PAD)
+	        {
+	          routebox_t *t = a;
+		  a = b;
+		  b = t;
+	        }
+*/
+				/* route exactly one net, without allowing conflicts */
+				InitAutoRouteParameters(0, a->style, pcb_false, pcb_true, pcb_true);
+				/* hace planes work better as sources than targets */
+				changed = RouteOne(rd, a, b, 150000).found_route || changed;
+				goto donerouting;
+			}
+			END_LOOP;
+		}
+		/* otherwise, munge the netlists so that only the selected rats
+		 * get connected. */
+		/* first, separate all sub nets into separate nets */
+		/* note that this code works because LIST_LOOP is clever enough not to
+		 * be fooled when the list is changing out from under it. */
+		last = NULL;
+		LIST_LOOP(rd->first_net, different_net, net);
+		{
+			FOREACH_SUBNET(net, rb);
+			{
+				if (last) {
+					last->different_net.next = rb;
+					rb->different_net.prev = last;
+				}
+				last = rb;
+			}
+			END_FOREACH(net, rb);
+			LIST_LOOP(net, same_net, rb);
+			{
+				rb->same_net = rb->same_subnet;
+			}
+			END_LOOP;
+			/* at this point all nets are equal to their subnets */
+		}
+		END_LOOP;
+		if (last) {
+			last->different_net.next = rd->first_net;
+			rd->first_net->different_net.prev = last;
+		}
+
+		/* now merge only those subnets connected by a rat line */
+		RAT_LOOP(PCB->Data);
+		if (!selected || TEST_FLAG(PCB_FLAG_SELECTED, line)) {
+			/* look up the end points of this rat line */
+			routebox_t *a;
+			routebox_t *b;
+			a = FindRouteBoxOnLayerGroup(rd, line->Point1.X, line->Point1.Y, line->group1);
+			b = FindRouteBoxOnLayerGroup(rd, line->Point2.X, line->Point2.Y, line->group2);
+			if (!a || !b) {
+#ifdef DEBUG_STALE_RATS
+				AddObjectToFlagUndoList(PCB_TYPE_RATLINE, line, line, line);
+				ASSIGN_FLAG(PCB_FLAG_SELECTED, pcb_true, line);
+				DrawRat(line, 0);
+#endif /* DEBUG_STALE_RATS */
+				Message(PCB_MSG_DEFAULT, "The rats nest is stale! Aborting autoroute...\n");
+				goto donerouting;
+			}
+			/* merge subnets into a net! */
+			MergeNets(a, b, NET);
+		}
+		END_LOOP;
+		/* now 'different_net' may point to too many different nets.  Reset. */
+		LIST_LOOP(rd->first_net, different_net, net);
+		{
+			if (!net->flags.touched) {
+				LIST_LOOP(net, same_net, rb);
+				rb->flags.touched = 1;
+				END_LOOP;
+			}
+			else											/* this is not a "different net"! */
+				RemoveFromNet(net, DIFFERENT_NET);
+		}
+		END_LOOP;
+		/* reset "touched" flag */
+		LIST_LOOP(rd->first_net, different_net, net);
+		{
+			LIST_LOOP(net, same_net, rb);
+			{
+				assert(rb->flags.touched);
+				rb->flags.touched = 0;
+			}
+			END_LOOP;
+		}
+		END_LOOP;
+	}
+	/* okay, rd's idea of netlist now corresponds to what we want routed */
+	/* auto-route all nets */
+	changed = (RouteAll(rd).total_nets_routed > 0) || changed;
+donerouting:
+	gui->progress(0, 0, NULL);
+	if (conf_core.editor.live_routing) {
+		int i;
+		BoxType big = { 0, 0, MAX_COORD, MAX_COORD };
+		for (i = 0; i < max_group; i++) {
+			r_search(rd->layergrouptree[i], &big, NULL, ripout_livedraw_obj_cb, NULL, NULL);
+		}
+	}
+#ifdef ROUTE_DEBUG
+	if (ddraw != NULL)
+		ddraw->finish_debug_draw();
+#endif
+
+	if (changed)
+		changed = IronDownAllUnfixedPaths(rd);
+	Message(PCB_MSG_DEFAULT, "Total added wire length = %$mS, %d vias added\n", (Coord) total_wire_length, total_via_count);
+	DestroyRouteData(&rd);
+	if (changed) {
+		SaveUndoSerialNumber();
+
+		/* optimize rats, we've changed connectivity a lot. */
+		DeleteRats(pcb_false /*all rats */ );
+		RestoreUndoSerialNumber();
+		AddAllRats(pcb_false /*all rats */ , NULL);
+		RestoreUndoSerialNumber();
+
+		IncrementUndoSerialNumber();
+
+		Redraw();
+	}
+	RestoreFindFlag();
+#if defined (ROUTE_DEBUG)
+	aabort = 0;
+#endif
+	return (changed);
+}
diff --git a/src_plugins/autoroute/autoroute.h b/src_plugins/autoroute/autoroute.h
new file mode 100644
index 0000000..fef51f5
--- /dev/null
+++ b/src_plugins/autoroute/autoroute.h
@@ -0,0 +1,42 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, autoroute.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* prototypes for autoroute routines
+ */
+
+#ifndef PCB_AUTOROUTE_H
+#define PCB_AUTOROUTE_H
+
+#include "global.h"
+
+pcb_bool AutoRoute(pcb_bool);
+
+#endif
diff --git a/src_plugins/autoroute/mtspace.c b/src_plugins/autoroute/mtspace.c
new file mode 100644
index 0000000..30ad953
--- /dev/null
+++ b/src_plugins/autoroute/mtspace.c
@@ -0,0 +1,510 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, mtspace.c, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *  Copyright (c) 2006 harry eaton.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* implementation for "empty space" routines (needed for via-space tracking
+ * in the auto-router.
+ */
+
+#include "config.h"
+
+#include "global.h"
+
+#include <assert.h>
+#include <setjmp.h>
+
+#include "box.h"
+#include "heap.h"
+#include "rtree.h"
+#include "mtspace.h"
+#include "vector.h"
+
+/* mtspace data structures are built on r-trees. */
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+
+typedef struct mtspacebox {
+	const BoxType box;
+	Coord clearance;								/* the smallest clearance around this box */
+} mtspacebox_t;
+
+/* this is an mtspace_t */
+struct mtspace {
+	/* rtrees keeping track of regions expanded by their required clearance. */
+	/* one for fixed, even, and odd */
+	rtree_t *ftree, *etree, *otree;
+};
+
+typedef union {
+	vector_t *v;
+	heap_t *h;
+} heap_or_vector;
+
+/* this is a vetting_t */
+struct vetting {
+	heap_or_vector untested;
+	heap_or_vector no_fix;
+	heap_or_vector no_hi;
+	heap_or_vector hi_candidate;
+	Coord radius;
+	Coord clearance;
+	CheapPointType desired;
+};
+
+#define SPECIAL 823157
+
+mtspacebox_t *mtspace_create_box(const BoxType * box, Coord clearance)
+{
+	mtspacebox_t *mtsb;
+	assert(box_is_good(box));
+	mtsb = (mtspacebox_t *) malloc(sizeof(*mtsb));
+	/* the box was sent to us pre-bloated by the clearance amount */
+	*((BoxTypePtr) & mtsb->box) = *box;
+	mtsb->clearance = clearance;
+	assert(box_is_good(&mtsb->box));
+	return mtsb;
+}
+
+/* create an "empty space" representation */
+mtspace_t *mtspace_create(void)
+{
+	mtspace_t *mtspace;
+
+	/* create mtspace data structure */
+	mtspace = (mtspace_t *) malloc(sizeof(*mtspace));
+	mtspace->ftree = r_create_tree(NULL, 0, 0);
+	mtspace->etree = r_create_tree(NULL, 0, 0);
+	mtspace->otree = r_create_tree(NULL, 0, 0);
+	/* done! */
+	return mtspace;
+}
+
+/* destroy an "empty space" representation. */
+void mtspace_destroy(mtspace_t ** mtspacep)
+{
+	assert(mtspacep);
+	r_destroy_tree(&(*mtspacep)->ftree);
+	r_destroy_tree(&(*mtspacep)->etree);
+	r_destroy_tree(&(*mtspacep)->otree);
+	free(*mtspacep);
+	*mtspacep = NULL;
+}
+
+struct mts_info {
+	Coord clearance;
+	BoxType box;
+	rtree_t *tree;
+	jmp_buf env;
+};
+
+static r_dir_t mts_remove_one(const BoxType * b, void *cl)
+{
+	struct mts_info *info = (struct mts_info *) cl;
+	mtspacebox_t *box = (mtspacebox_t *) b;
+
+	/* there can be duplicate boxes, we just remove one */
+	/* the info box is pre-bloated, so just check equality */
+	if (b->X1 == info->box.X1 && b->X2 == info->box.X2 &&
+			b->Y1 == info->box.Y1 && b->Y2 == info->box.Y2 && box->clearance == info->clearance) {
+		r_delete_entry(info->tree, b);
+		longjmp(info->env, 1);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+rtree_t *which_tree(mtspace_t * mtspace, mtspace_type_t which)
+{
+	switch (which) {
+	case FIXED:
+		return mtspace->ftree;
+	case EVEN:
+		return mtspace->etree;
+	default:
+		return mtspace->otree;
+	}
+}
+
+/* add a space-filler to the empty space representation.  */
+void mtspace_add(mtspace_t * mtspace, const BoxType * box, mtspace_type_t which, Coord clearance)
+{
+	mtspacebox_t *filler = mtspace_create_box(box, clearance);
+	r_insert_entry(which_tree(mtspace, which), (const BoxType *) filler, 1);
+}
+
+/* remove a space-filler from the empty space representation. */
+void mtspace_remove(mtspace_t * mtspace, const BoxType * box, mtspace_type_t which, Coord clearance)
+{
+	struct mts_info cl;
+	BoxType small_search;
+
+	cl.clearance = clearance;
+	cl.box = *box;
+	cl.tree = which_tree(mtspace, which);
+	small_search = box_center(box);
+	if (setjmp(cl.env) == 0) {
+		r_search(cl.tree, &small_search, NULL, mts_remove_one, &cl, NULL);
+		assert(0);									/* didn't find it?? */
+	}
+}
+
+struct query_closure {
+	BoxType *cbox;
+	heap_or_vector checking;
+	heap_or_vector touching;
+	CheapPointType *desired;
+	Coord radius, clearance;
+	jmp_buf env;
+	pcb_bool touch_is_vec;
+};
+
+static inline void heap_append(heap_t * heap, CheapPointType * desired, BoxType * newone)
+{
+	CheapPointType p = *desired;
+	assert(desired);
+	closest_point_in_box(&p, newone);
+	heap_insert(heap, PCB_ABS(p.X - desired->X) + (p.Y - desired->Y), newone);
+}
+
+static inline void append(struct query_closure *qc, BoxType * newone)
+{
+	if (qc->desired)
+		heap_append(qc->checking.h, qc->desired, newone);
+	else
+		vector_append(qc->checking.v, newone);
+}
+
+/* we found some space filler that may intersect this query.
+ * First check if it does intersect, then break it into
+ * overlaping regions that don't intersect this box.
+ */
+static r_dir_t query_one(const BoxType * box, void *cl)
+{
+	struct query_closure *qc = (struct query_closure *) cl;
+	mtspacebox_t *mtsb = (mtspacebox_t *) box;
+	Coord shrink;
+	assert(box_intersect(qc->cbox, &mtsb->box));
+	/* we need to satisfy the larger of the two clearances */
+	if (qc->clearance > mtsb->clearance)
+		shrink = mtsb->clearance;
+	else
+		shrink = qc->clearance;
+	/* if we shrink qc->box by this amount and it doesn't intersect
+	 * then we didn't actually touch this box */
+	if (qc->cbox->X1 + shrink >= mtsb->box.X2 ||
+			qc->cbox->X2 - shrink <= mtsb->box.X1 || qc->cbox->Y1 + shrink >= mtsb->box.Y2 || qc->cbox->Y2 - shrink <= mtsb->box.Y1)
+		return R_DIR_NOT_FOUND;
+	/* ok, we do touch this box, now create up to 4 boxes that don't */
+	if (mtsb->box.Y1 > qc->cbox->Y1 + shrink) {	/* top region exists */
+		Coord Y1 = qc->cbox->Y1;
+		Coord Y2 = mtsb->box.Y1 + shrink;
+		if (Y2 - Y1 >= 2 * (qc->radius + qc->clearance)) {
+			BoxType *newone = (BoxType *) malloc(sizeof(BoxType));
+			newone->X1 = qc->cbox->X1;
+			newone->X2 = qc->cbox->X2;
+			newone->Y1 = Y1;
+			newone->Y2 = Y2;
+			assert(newone->Y2 < qc->cbox->Y2);
+			append(qc, newone);
+		}
+	}
+	if (mtsb->box.Y2 < qc->cbox->Y2 - shrink) {	/* bottom region exists */
+		Coord Y1 = mtsb->box.Y2 - shrink;
+		Coord Y2 = qc->cbox->Y2;
+		if (Y2 - Y1 >= 2 * (qc->radius + qc->clearance)) {
+			BoxType *newone = (BoxType *) malloc(sizeof(BoxType));
+			newone->X1 = qc->cbox->X1;
+			newone->X2 = qc->cbox->X2;
+			newone->Y2 = qc->cbox->Y2;
+			newone->Y1 = Y1;
+			assert(newone->Y1 > qc->cbox->Y1);
+			append(qc, newone);
+		}
+	}
+	if (mtsb->box.X1 > qc->cbox->X1 + shrink) {	/* left region exists */
+		Coord X1 = qc->cbox->X1;
+		Coord X2 = mtsb->box.X1 + shrink;
+		if (X2 - X1 >= 2 * (qc->radius + qc->clearance)) {
+			BoxType *newone;
+			newone = (BoxType *) malloc(sizeof(BoxType));
+			newone->Y1 = qc->cbox->Y1;
+			newone->Y2 = qc->cbox->Y2;
+			newone->X1 = qc->cbox->X1;
+			newone->X2 = X2;
+			assert(newone->X2 < qc->cbox->X2);
+			append(qc, newone);
+		}
+	}
+	if (mtsb->box.X2 < qc->cbox->X2 - shrink) {	/* right region exists */
+		Coord X1 = mtsb->box.X2 - shrink;
+		Coord X2 = qc->cbox->X2;
+		if (X2 - X1 >= 2 * (qc->radius + qc->clearance)) {
+			BoxType *newone = (BoxType *) malloc(sizeof(BoxType));
+			newone->Y1 = qc->cbox->Y1;
+			newone->Y2 = qc->cbox->Y2;
+			newone->X2 = qc->cbox->X2;
+			newone->X1 = X1;
+			assert(newone->X1 > qc->cbox->X1);
+			append(qc, newone);
+		}
+	}
+	if (qc->touching.v) {
+		if (qc->touch_is_vec || !qc->desired)
+			vector_append(qc->touching.v, qc->cbox);
+		else
+			heap_append(qc->touching.h, qc->desired, qc->cbox);
+	}
+	else
+		free(qc->cbox);							/* done with this one */
+	longjmp(qc->env, 1);
+	return R_DIR_FOUND_CONTINUE;											/* never reached */
+}
+
+/* qloop takes a vector (or heap) of regions to check (checking) if they don't intersect
+ * anything. If a region does intersect something, it is broken into
+ * pieces that don't intersect that thing (if possible) which are
+ * put back into the vector/heap of regions to check.
+ * qloop returns pcb_false when it finds the first empty region
+ * it returns pcb_true if it has exhausted the region vector/heap and never
+ * found an empty area.
+ */
+static void qloop(struct query_closure *qc, rtree_t * tree, heap_or_vector res, pcb_bool is_vec)
+{
+	BoxType *cbox;
+	int n;
+
+	while (!(qc->desired ? heap_is_empty(qc->checking.h) : vector_is_empty(qc->checking.v))) {
+		cbox = qc->desired ? (BoxTypePtr) heap_remove_smallest(qc->checking.h) : (BoxTypePtr) vector_remove_last(qc->checking.v);
+		if (setjmp(qc->env) == 0) {
+			assert(box_is_good(cbox));
+			qc->cbox = cbox;
+			r_search(tree, cbox, NULL, query_one, qc, &n);
+			assert(n == 0);
+			/* nothing intersected with this tree, put it in the result vector */
+			if (is_vec)
+				vector_append(res.v, cbox);
+			else {
+				if (qc->desired)
+					heap_append(res.h, qc->desired, cbox);
+				else
+					vector_append(res.v, cbox);
+			}
+			return;										/* found one - perhaps one answer is good enough */
+		}
+	}
+}
+
+/* free the memory used by the vetting structure */
+void mtsFreeWork(vetting_t ** w)
+{
+	vetting_t *work = (*w);
+	if (work->desired.X != -SPECIAL || work->desired.Y != -SPECIAL) {
+		heap_free(work->untested.h, free);
+		heap_destroy(&work->untested.h);
+		heap_free(work->no_fix.h, free);
+		heap_destroy(&work->no_fix.h);
+		heap_free(work->no_hi.h, free);
+		heap_destroy(&work->no_hi.h);
+		heap_free(work->hi_candidate.h, free);
+		heap_destroy(&work->hi_candidate.h);
+	}
+	else {
+		while (!vector_is_empty(work->untested.v))
+			free(vector_remove_last(work->untested.v));
+		vector_destroy(&work->untested.v);
+		while (!vector_is_empty(work->no_fix.v))
+			free(vector_remove_last(work->no_fix.v));
+		vector_destroy(&work->no_fix.v);
+		while (!vector_is_empty(work->no_hi.v))
+			free(vector_remove_last(work->no_hi.v));
+		vector_destroy(&work->no_hi.v);
+		while (!vector_is_empty(work->hi_candidate.v))
+			free(vector_remove_last(work->hi_candidate.v));
+		vector_destroy(&work->hi_candidate.v);
+	}
+	free(work);
+	(*w) = NULL;
+}
+
+
+/* returns some empty spaces in 'region' (or former narrowed regions)
+ * that may hold a feature with the specified radius and clearance
+ * It tries first to find Completely empty regions (which are appended
+ * to the free_space_vec vector). If that fails, it looks for regions
+ * filled only by objects generated by the previous pass (which are
+ * appended to the lo_conflict_space_vec vector). Then it looks for
+ * regions that are filled by objects generated during *this* pass
+ * (which  are appended to the hi_conflict_space_vec vector). The
+ * current pass identity is given by 'is_odd'.  As soon as one completely
+ * free region is found, it returns with that answer. It saves partially
+ * searched regions in vectors "untested", "no_fix", "no_hi", and
+ * "hi_candidate" which can be passed to future calls of this function
+ * to search harder for such regions if the computation becomes
+ * necessary.
+ */
+vetting_t *mtspace_query_rect(mtspace_t * mtspace, const BoxType * region,
+															Coord radius, Coord clearance,
+															vetting_t * work,
+															vector_t * free_space_vec,
+															vector_t * lo_conflict_space_vec,
+															vector_t * hi_conflict_space_vec, pcb_bool is_odd, pcb_bool with_conflicts, CheapPointType * desired)
+{
+	struct query_closure qc;
+
+	/* pre-assertions */
+	assert(free_space_vec);
+	assert(lo_conflict_space_vec);
+	assert(hi_conflict_space_vec);
+	/* search out to anything that might matter */
+	if (region) {
+		BoxType *cbox;
+		assert(work == NULL);
+		assert(box_is_good(region));
+		assert(vector_is_empty(free_space_vec));
+		assert(vector_is_empty(lo_conflict_space_vec));
+		assert(vector_is_empty(hi_conflict_space_vec));
+		work = (vetting_t *) malloc(sizeof(vetting_t));
+		work->clearance = clearance;
+		work->radius = radius;
+		cbox = (BoxType *) malloc(sizeof(BoxType));
+		*cbox = bloat_box(region, clearance + radius);
+		if (desired) {
+			work->untested.h = heap_create();
+			work->no_fix.h = heap_create();
+			work->hi_candidate.h = heap_create();
+			work->no_hi.h = heap_create();
+			assert(work->untested.h && work->no_fix.h && work->no_hi.h && work->hi_candidate.h);
+			heap_insert(work->untested.h, 0, cbox);
+			work->desired = *desired;
+		}
+		else {
+			work->untested.v = vector_create();
+			work->no_fix.v = vector_create();
+			work->hi_candidate.v = vector_create();
+			work->no_hi.v = vector_create();
+			assert(work->untested.v && work->no_fix.v && work->no_hi.v && work->hi_candidate.v);
+			vector_append(work->untested.v, cbox);
+			work->desired.X = work->desired.Y = -SPECIAL;
+		}
+		return work;
+	}
+	qc.clearance = work->clearance;
+	qc.radius = work->radius;
+	if (work->desired.X == -SPECIAL && work->desired.Y == -SPECIAL)
+		qc.desired = NULL;
+	else
+		qc.desired = &work->desired;
+	/* do the query */
+	do {
+		heap_or_vector temporary;
+		temporary.v = free_space_vec;
+
+		/* search the fixed object tree discarding any intersections
+		 * and placing empty regions in the no_fix vector.
+		 */
+		qc.checking = work->untested;
+		qc.touching.v = NULL;
+		qloop(&qc, mtspace->ftree, work->no_fix, pcb_false);
+		/* search the hi-conflict tree placing intersectors in the
+		 * hi_candidate vector (if conflicts are allowed) and
+		 * placing empty regions in the no_hi vector.
+		 */
+		qc.checking.v = work->no_fix.v;
+		qc.touching.v = with_conflicts ? work->hi_candidate.v : NULL;
+		qc.touch_is_vec = pcb_false;
+		qloop(&qc, is_odd ? mtspace->otree : mtspace->etree, work->no_hi, pcb_false);
+		/* search the lo-conflict tree placing intersectors in the
+		 * lo-conflict answer vector (if conflicts allowed) and
+		 * placing emptry regions in the free-space answer vector.
+		 */
+		qc.checking = work->no_hi;
+/* XXX lo_conflict_space_vec will be treated like a heap! */
+		qc.touching.v = (with_conflicts ? lo_conflict_space_vec : NULL);
+		qc.touch_is_vec = pcb_true;
+		qloop(&qc, is_odd ? mtspace->etree : mtspace->otree, temporary, pcb_true);
+
+		/* qloop (&qc, is_odd ? mtspace->etree : mtspace->otree, (heap_or_vector)free_space_vec, pcb_true); */
+		if (!vector_is_empty(free_space_vec)) {
+			if (qc.desired) {
+				if (heap_is_empty(work->untested.h))
+					break;
+			}
+			else {
+				if (vector_is_empty(work->untested.v))
+					break;
+			}
+			return work;
+		}
+		/* finally check the hi-conflict intersectors against the
+		 * lo-conflict tree discarding intersectors (two types of conflict is real bad)
+		 * and placing empty regions in the hi-conflict answer vector.
+		 */
+		if (with_conflicts) {
+			heap_or_vector temporary;
+			temporary.v = hi_conflict_space_vec;
+
+			qc.checking = work->hi_candidate;
+			qc.touching.v = NULL;
+			qloop(&qc, is_odd ? mtspace->etree : mtspace->otree, temporary, pcb_true);
+
+			/* qloop (&qc, is_odd ? mtspace->etree : mtspace->otree, */
+			/*   (heap_or_vector)hi_conflict_space_vec, pcb_true); */
+		}
+	}
+	while (!(qc.desired ? heap_is_empty(work->untested.h) : vector_is_empty(work->untested.v)));
+	if (qc.desired) {
+		if (heap_is_empty(work->no_fix.h) && heap_is_empty(work->no_hi.h) && heap_is_empty(work->hi_candidate.h)) {
+			mtsFreeWork(&work);
+			return NULL;
+		}
+	}
+	else {
+		if (vector_is_empty(work->no_fix.v) && vector_is_empty(work->no_hi.v) && vector_is_empty(work->hi_candidate.v)) {
+			mtsFreeWork(&work);
+			return NULL;
+		}
+	}
+	return work;
+}
+
+int mtsBoxCount(vetting_t * w)
+{
+#if 0
+	int ans;
+	ans = 3 * vector_size(w->untested);
+	ans += 2 * vector_size(w->no_fix);
+	ans += vector_size(w->no_hi);
+	ans += vector_size(w->hi_candidate);
+	return ans;
+#endif
+	return 100;
+}
diff --git a/src_plugins/autoroute/mtspace.h b/src_plugins/autoroute/mtspace.h
new file mode 100644
index 0000000..6f62dc5
--- /dev/null
+++ b/src_plugins/autoroute/mtspace.h
@@ -0,0 +1,77 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, mtspace.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* prototypes for "empty space" routines (needed for via-space tracking
+ * in the auto-router.
+ */
+
+#ifndef PCB_MTSPACE_H
+#define PCB_MTSPACE_H
+
+/* mtspace data structures are built on r-trees. */
+
+#include "global.h"
+#include "vector.h"							/* for vector_t in mtspace_query_rect prototype */
+
+typedef struct mtspace mtspace_t;
+typedef enum { FIXED, ODD, EVEN } mtspace_type_t;
+typedef struct vetting vetting_t;
+
+/* create an "empty space" representation with a shrunken boundary */
+mtspace_t *mtspace_create(void);
+/* destroy an "empty space" representation. */
+void mtspace_destroy(mtspace_t ** mtspacep);
+
+/* -- mutation -- */
+
+/* add a space-filler to the empty space representation.  The given box
+ * should *not* be bloated; it should be "true".  The feature will fill
+ * *at least* a radius of clearance around it;
+ */
+void mtspace_add(mtspace_t * mtspace, const BoxType * box, mtspace_type_t which, Coord clearance);
+/* remove a space-filler from the empty space representation.  The given box
+ * should *not* be bloated; it should be "true".  The feature will fill
+ * *at least* a radius of clearance around it;
+ */
+void mtspace_remove(mtspace_t * mtspace, const BoxType * box, mtspace_type_t which, Coord clearance);
+
+
+vetting_t *mtspace_query_rect(mtspace_t * mtspace, const BoxType * region,
+															Coord radius, Coord clearance,
+															vetting_t * work,
+															vector_t * free_space_vec,
+															vector_t * lo_conflict_space_vec,
+															vector_t * hi_conflict_space_vec, pcb_bool is_odd, pcb_bool with_conflicts, CheapPointType * desired);
+
+void mtsFreeWork(vetting_t **);
+int mtsBoxCount(vetting_t *);
+
+#endif /* ! PCB_MTSPACE_H */
diff --git a/src_plugins/autoroute/vector.c b/src_plugins/autoroute/vector.c
new file mode 100644
index 0000000..5bc3158
--- /dev/null
+++ b/src_plugins/autoroute/vector.c
@@ -0,0 +1,213 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, vector.c, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* operations on vectors. */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+#include "vector.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+
+/* ---------------------------------------------------------------------------
+ * some local types
+ */
+struct vector_struct {
+	vector_element_t *element;
+	int size, max;
+};
+
+/* ---------------------------------------------------------------------------
+ * some local identifiers
+ */
+
+/* ---------------------------------------------------------------------------
+ * functions.
+ */
+
+/* helper function for assertions */
+#ifndef NDEBUG
+static int __vector_is_good(vector_t * vector)
+{
+	return vector && (vector->max == 0 || vector->element) &&
+		(vector->max >= 0) && (vector->size >= 0) && (vector->size <= vector->max) && 1;
+}
+#endif /* !NDEBUG */
+
+/* create an empty vector */
+vector_t *vector_create()
+{
+	vector_t *vector;
+	/* okay, create empty vector */
+	vector = (vector_t *) calloc(1, sizeof(*vector));
+	assert(vector);
+	assert(__vector_is_good(vector));
+	return vector;
+}
+
+/* destroy a vector */
+void vector_destroy(vector_t ** vector)
+{
+	assert(vector && *vector);
+	assert(__vector_is_good(*vector));
+	if ((*vector)->element)
+		free((*vector)->element);
+	free(*vector);
+	*vector = NULL;
+}
+
+/* -- interrogation -- */
+int vector_is_empty(vector_t * vector)
+{
+	assert(__vector_is_good(vector));
+	return (vector->size == 0);
+}
+
+int vector_size(vector_t * vector)
+{
+	assert(__vector_is_good(vector));
+	return (vector->size);
+}
+
+vector_element_t vector_element(vector_t * vector, int N)
+{
+	assert(__vector_is_good(vector));
+	assert(N < vector->size);
+	return vector->element[N];
+}
+
+/* return the first element of the vector. */
+vector_element_t vector_element_first(vector_t * vector)
+{
+	assert(__vector_is_good(vector));
+	assert(vector->size > 0);
+	return vector_element(vector, 0);
+}
+
+/* return the last element of the vector. */
+vector_element_t vector_element_last(vector_t * vector)
+{
+	assert(__vector_is_good(vector));
+	assert(vector->size > 0);
+	return vector_element(vector, vector->size - 1);
+}
+
+/* -- mutation -- */
+/* add data to end of vector */
+void vector_append(vector_t * vector, vector_element_t data)
+{
+	vector_insert_many(vector, vector->size, &data, 1);
+}
+
+void vector_append_many(vector_t * vector, vector_element_t data[], int count)
+{
+	vector_insert_many(vector, vector->size, data, count);
+}
+
+void vector_append_vector(vector_t * vector, vector_t * other_vector)
+{
+	vector_append_many(vector, other_vector->element, other_vector->size);
+}
+
+void vector_insert(vector_t * vector, int N, vector_element_t data)
+{
+	vector_insert_many(vector, N, &data, 1);
+}
+
+/* add data at specified position of vector */
+void vector_insert_many(vector_t * vector, int N, vector_element_t data[], int count)
+{
+	assert(__vector_is_good(vector));
+	assert(N <= vector->size);
+	if (count == 0)
+		return;
+	assert(data && count > 0);
+	if (vector->size + count > vector->max) {
+		vector->max = MAX(32, MAX(vector->size + count, vector->max * 2));
+		vector->element = (void **) realloc(vector->element, vector->max * sizeof(*vector->element));
+	}
+	memmove(vector->element + N + count, vector->element + N, (vector->size - N) * sizeof(*vector->element));
+	memmove(vector->element + N, data, count * sizeof(*data));
+	vector->size += count;
+	assert(__vector_is_good(vector));
+}
+
+vector_t *vector_duplicate(vector_t * orig)
+{
+	vector_t *newone = vector_create();
+	if (!orig)
+		return newone;
+	newone->element = (void **) malloc(orig->max * sizeof(*orig->element));
+	newone->max = orig->max;
+	newone->size = orig->size;
+	memcpy(newone->element, orig->element, orig->size * sizeof(vector_element_t));
+	assert(__vector_is_good(newone));
+	return newone;
+}
+
+/* return and delete the *last* element of vector */
+vector_element_t vector_remove_last(vector_t * vector)
+{
+	assert(vector->size > 0);
+	return vector_remove(vector, vector->size - 1);
+}
+
+/* return and delete data at specified position of vector */
+vector_element_t vector_remove(vector_t * vector, int N)
+{
+	vector_element_t old;
+	assert(__vector_is_good(vector));
+	assert(N < vector->size);
+	old = vector->element[N];
+	memmove(vector->element + N, vector->element + N + 1, (vector->size - (N + 1)) * sizeof(*vector->element));
+	vector->size--;
+	assert(__vector_is_good(vector));
+	return old;
+}
+
+/* replace the data at the specified position with the given data.
+ * returns the old data. */
+vector_element_t vector_replace(vector_t * vector, vector_element_t data, int N)
+{
+	vector_element_t old;
+	assert(__vector_is_good(vector));
+	assert(N < vector->size);
+	old = vector->element[N];
+	vector->element[N] = data;
+	assert(__vector_is_good(vector));
+	return old;
+}
diff --git a/src_plugins/autoroute/vector.h b/src_plugins/autoroute/vector.h
new file mode 100644
index 0000000..785d2d4
--- /dev/null
+++ b/src_plugins/autoroute/vector.h
@@ -0,0 +1,76 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1998,1999,2000,2001 harry eaton
+ *
+ *  this file, vector.h, was written and is
+ *  Copyright (c) 2001 C. Scott Ananian.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+
+/* prototypes for vector routines.
+ */
+
+#ifndef PCB_VECTOR_H
+#define PCB_VECTOR_H
+
+/* what a vector looks like */
+typedef struct vector_struct vector_t;
+/* what data in a vector looks like */
+typedef void *vector_element_t;
+
+/* create an empty vector */
+vector_t *vector_create();
+/* destroy a vector */
+void vector_destroy(vector_t ** vector);
+/* copy a vector */
+vector_t *vector_duplicate(vector_t * vector);
+
+/* -- interrogation -- */
+int vector_is_empty(vector_t * vector);
+int vector_size(vector_t * vector);
+vector_element_t vector_element(vector_t * vector, int N);
+vector_element_t vector_element_first(vector_t * vector);
+vector_element_t vector_element_last(vector_t * vector);
+
+/* -- mutation -- */
+/* add data to end of vector */
+void vector_append(vector_t * vector, vector_element_t data);
+/* add multiple elements to end of vector */
+void vector_append_many(vector_t * vector, vector_element_t data[], int count);
+/* add a vector of elements to the end of vector */
+void vector_append_vector(vector_t * vector, vector_t * other_vector);
+/* add data at specified position of vector */
+void vector_insert(vector_t * vector, int N, vector_element_t data);
+/* add multiple elements at specified position of vector */
+void vector_insert_many(vector_t * vector, int N, vector_element_t data[], int count);
+/* return and delete the *last* element of vector */
+vector_element_t vector_remove_last(vector_t * vector);
+/* return and delete data at specified position of vector */
+vector_element_t vector_remove(vector_t * vector, int N);
+/* replace the data at the specified position with the given data.
+ * returns the old data. */
+vector_element_t vector_replace(vector_t * vector, vector_element_t data, int N);
+
+#endif /* PCB_VECTOR_H */
diff --git a/src_plugins/boardflip/Makefile b/src_plugins/boardflip/Makefile
new file mode 100644
index 0000000..03c3cbd
--- /dev/null
+++ b/src_plugins/boardflip/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_boardflip
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/boardflip/Plug.tmpasm b/src_plugins/boardflip/Plug.tmpasm
new file mode 100644
index 0000000..b13f294
--- /dev/null
+++ b/src_plugins/boardflip/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {boardflip}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/boardflip/boardflip.o @]
+
+switch /local/pcb/boardflip/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/boardflip/README b/src_plugins/boardflip/README
new file mode 100644
index 0000000..43e7ddb
--- /dev/null
+++ b/src_plugins/boardflip/README
@@ -0,0 +1,6 @@
+All objects on the board are up-down flipped.
+
+#state: WIP
+#lstate: doesn't update rtrees
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/boardflip/boardflip.c b/src_plugins/boardflip/boardflip.c
new file mode 100644
index 0000000..83ae8c7
--- /dev/null
+++ b/src_plugins/boardflip/boardflip.c
@@ -0,0 +1,183 @@
+/*!
+ * \file boardflip.c
+ *
+ * \brief BoardFlip plug-in for PCB.
+ *
+ * \author Copyright (C) 2008 DJ Delorie <dj at delorie.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * Original source was: http://www.delorie.com/pcb/boardflip.c
+ *
+ * Usage: Boardflip()
+ *
+ * All objects on the board are up-down flipped.
+ *
+ * Command line board flipping:
+ *
+ * pcb --action-string "BoardFlip() SaveTo(LayoutAs,$OUTFILE) Quit()" $INFILE
+ *
+ * To flip the board physically, use BoardFlip(sides)
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+
+/* Things that need to be flipped:
+
+  lines
+  text
+  polygons
+  arcs
+  vias
+  elements
+    elementlines
+    elementarcs
+    pins
+    pads
+  rats
+*/
+
+#define FLIP(y) (y) = h - (y)
+#define NEG(y) (y) = - (y)
+
+static int boardflip(int argc, const char **argv, Coord x, Coord y)
+{
+	int h = PCB->MaxHeight;
+	int sides = 0;
+
+	if (argc > 0 && strcasecmp(argv[0], "sides") == 0)
+		sides = 1;
+	printf("argc %d argv %s sides %d\n", argc, argc > 0 ? argv[0] : "", sides);
+	LAYER_LOOP(PCB->Data, max_copper_layer + 2);
+	{
+		LINE_LOOP(layer);
+		{
+			FLIP(line->Point1.Y);
+			FLIP(line->Point2.Y);
+		}
+		END_LOOP;
+		TEXT_LOOP(layer);
+		{
+			FLIP(text->Y);
+			TOGGLE_FLAG(PCB_FLAG_ONSOLDER, text);
+		}
+		END_LOOP;
+		POLYGON_LOOP(layer);
+		{
+			int i, j;
+			POLYGONPOINT_LOOP(polygon);
+			{
+				FLIP(point->Y);
+			}
+			END_LOOP;
+			i = 0;
+			j = polygon->PointN - 1;
+			while (i < j) {
+				PointType p = polygon->Points[i];
+				polygon->Points[i] = polygon->Points[j];
+				polygon->Points[j] = p;
+				i++;
+				j--;
+			}
+			InitClip(PCB->Data, layer, polygon);
+		}
+		END_LOOP;
+		ARC_LOOP(layer);
+		{
+			FLIP(arc->Y);
+			NEG(arc->StartAngle);
+			NEG(arc->Delta);
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	VIA_LOOP(PCB->Data);
+	{
+		FLIP(via->Y);
+	}
+	END_LOOP;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		FLIP(element->MarkY);
+		if (sides)
+			TOGGLE_FLAG(PCB_FLAG_ONSOLDER, element);
+		ELEMENTTEXT_LOOP(element);
+		{
+			FLIP(text->Y);
+			TOGGLE_FLAG(PCB_FLAG_ONSOLDER, text);
+		}
+		END_LOOP;
+		ELEMENTLINE_LOOP(element);
+		{
+			FLIP(line->Point1.Y);
+			FLIP(line->Point2.Y);
+		}
+		END_LOOP;
+		ELEMENTARC_LOOP(element);
+		{
+			FLIP(arc->Y);
+			NEG(arc->StartAngle);
+			NEG(arc->Delta);
+		}
+		END_LOOP;
+		PIN_LOOP(element);
+		{
+			FLIP(pin->Y);
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			FLIP(pad->Point1.Y);
+			FLIP(pad->Point2.Y);
+			if (sides)
+				TOGGLE_FLAG(PCB_FLAG_ONSOLDER, pad);
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	RAT_LOOP(PCB->Data);
+	{
+		FLIP(line->Point1.Y);
+		FLIP(line->Point2.Y);
+	}
+	END_LOOP;
+	return 0;
+}
+
+static HID_Action boardflip_action_list[] = {
+	{"BoardFlip", NULL, boardflip, NULL, NULL}
+};
+
+char *boardflip_cookie = "boardflip plugin";
+
+REGISTER_ACTIONS(boardflip_action_list, boardflip_cookie)
+
+static void hid_boardflip_uninit(void)
+{
+	hid_remove_actions_by_cookie(boardflip_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_boardflip_init()
+{
+	REGISTER_ACTIONS(boardflip_action_list, boardflip_cookie);
+	return hid_boardflip_uninit;
+}
+
diff --git a/src_plugins/dbus/HACKING b/src_plugins/dbus/HACKING
new file mode 100644
index 0000000..8abe052
--- /dev/null
+++ b/src_plugins/dbus/HACKING
@@ -0,0 +1,5 @@
+Needs scconfig detection. Does anyone use dbus?
+
+Makefile:
+	EXTRA_DIST += dbus.xml
+	DISTCLEAN_FILES += dbus-introspect.h \
diff --git a/src_plugins/dbus/Makefile b/src_plugins/dbus/Makefile
new file mode 100644
index 0000000..61529b2
--- /dev/null
+++ b/src_plugins/dbus/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_dbus
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/dbus/Plug.tmpasm b/src_plugins/dbus/Plug.tmpasm
new file mode 100644
index 0000000..c52231c
--- /dev/null
+++ b/src_plugins/dbus/Plug.tmpasm
@@ -0,0 +1,28 @@
+put /local/pcb/mod {dbus}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/dbus/dbus.o $(PLUGDIR)/dbus/dbus-pcbmain.o @]
+
+switch /local/pcb/dbus/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/CFLAGS   /target/libs/sul/dbus/cflags
+		put /local/pcb/mod/LDFLAGS  /target/libs/sul/dbus/ldflags
+
+		put /local/tmp { $(PLUGDIR)/dbus/dbus-introspect.tmp }
+		append /local/pcb/RULES [@
+# DBUS xml embedded
+$(PLUGDIR)/dbus/dbus-introspect.h: $(PLUGDIR)/dbus/dbus.xml
+	echo '/* AUTOMATICALLY GENERATED FROM dbus.xml DO NOT EDIT */' > @/local/tmp@
+	echo "static char *pcb_dbus_introspect_xml ="  > @/local/tmp@
+	sed 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$$/"/' < $(PLUGDIR)/dbus/dbus.xml >> @/local/tmp@
+	echo ";" >> @/local/tmp@
+	mv @/local/tmp@ $(PLUGDIR)/dbus/dbus-introspect.h
+@]
+
+	end
+end
+
+switch /local/pcb/dbus/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/dbus/README b/src_plugins/dbus/README
new file mode 100644
index 0000000..45a4ec4
--- /dev/null
+++ b/src_plugins/dbus/README
@@ -0,0 +1,6 @@
+Remote control PCB using DBUS.
+
+#state: WIP
+#lstate: needs to install the xml?
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/dbus/dbus-pcbmain.c b/src_plugins/dbus/dbus-pcbmain.c
new file mode 100644
index 0000000..fc318c4
--- /dev/null
+++ b/src_plugins/dbus/dbus-pcbmain.c
@@ -0,0 +1,310 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-pcbmain.c PCB HID main loop integration
+ *
+ * Adapted from dbus-gmain.c from dbus-glib bindings:
+ * Copyright (C) 2002, 2003 CodeFactory AB
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#include "config.h"
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <stdio.h>
+
+#include "global.h"
+#include "dbus-pcbmain.h"
+
+typedef struct _IOWatchHandler IOWatchHandler;
+typedef struct _TimeoutHandler TimeoutHandler;
+
+struct _IOWatchHandler {
+	DBusWatch *dbus_watch;
+	hidval pcb_watch;
+};
+
+struct _TimeoutHandler {
+	DBusTimeout *dbus_timeout;
+	hidval pcb_timer;
+	int interval;
+};
+
+
+static void block_hook_cb(hidval data)
+{
+	DBusConnection *connection = (DBusConnection *) data.ptr;
+	if (dbus_connection_get_dispatch_status(connection) != DBUS_DISPATCH_DATA_REMAINS)
+		return;
+
+	/* TODO: IS THIS NEEDED? */
+	/* dbus_connection_ref (connection); */
+
+	/* Only dispatch once - we don't want to starve other mainloop users */
+	dbus_connection_dispatch(connection);
+
+	/* dbus_connection_unref (connection); */
+	return;
+}
+
+static void io_watch_handler_dbus_freed(void *data)
+{
+	IOWatchHandler *handler;
+	handler = (IOWatchHandler *) data;
+
+	/* Remove the watch registered with the HID */
+	if (gui != NULL)
+		gui->unwatch_file(handler->pcb_watch);
+	free(handler);
+}
+
+
+void io_watch_handler_cb(hidval pcb_watch, int fd, unsigned int condition, hidval data)
+{
+	IOWatchHandler *handler;
+	unsigned int dbus_condition = 0;
+
+	handler = (IOWatchHandler *) data.ptr;
+
+	/* TODO: IS THIS NEEDED? */
+	/* if (connection)
+	   dbus_connection_ref (connection); */
+
+	if (condition & PCB_WATCH_READABLE)
+		dbus_condition |= DBUS_WATCH_READABLE;
+	if (condition & PCB_WATCH_WRITABLE)
+		dbus_condition |= DBUS_WATCH_WRITABLE;
+	if (condition & PCB_WATCH_ERROR)
+		dbus_condition |= DBUS_WATCH_ERROR;
+	if (condition & PCB_WATCH_HANGUP)
+		dbus_condition |= DBUS_WATCH_HANGUP;
+
+	/* We don't touch the handler after this, because DBus
+	 * may have disabled the watch and thus killed the handler
+	 */
+	dbus_watch_handle(handler->dbus_watch, dbus_condition);
+	handler = NULL;
+
+	/*if (connection)
+	   dbus_connection_unref (connection); */
+
+	return;
+}
+
+
+static void timeout_handler_dbus_freed(void *data)
+{
+	TimeoutHandler *handler;
+	handler = (TimeoutHandler *) data;
+
+	/* Remove the timeout registered with the HID */
+	gui->stop_timer(handler->pcb_timer);
+	free(handler);
+}
+
+
+void timeout_handler_cb(hidval data)
+{
+	TimeoutHandler *handler;
+	handler = (TimeoutHandler *) data.ptr;
+
+	/* Re-add the timeout, as PCB will remove the current one
+	   Do this before calling to dbus, incase DBus removes the timeout.
+	   We can't touch handler after libdbus has been run for this reason. */
+	handler->pcb_timer = gui->add_timer(timeout_handler_cb, handler->interval, data);
+
+	dbus_timeout_handle(handler->dbus_timeout);
+}
+
+
+static dbus_bool_t watch_add(DBusWatch * dbus_watch, void *data)
+{
+	IOWatchHandler *handler;
+	int fd;
+	unsigned int pcb_condition;
+	unsigned int dbus_flags;
+	hidval temp;
+
+	/* We won't create a watch until it becomes enabled. */
+	if (!dbus_watch_get_enabled(dbus_watch))
+		return TRUE;
+
+	dbus_flags = dbus_watch_get_flags(dbus_watch);
+
+	pcb_condition = PCB_WATCH_ERROR | PCB_WATCH_HANGUP;
+	if (dbus_flags & DBUS_WATCH_READABLE)
+		pcb_condition |= PCB_WATCH_READABLE;
+	if (dbus_flags & DBUS_WATCH_WRITABLE)
+		pcb_condition |= PCB_WATCH_READABLE;
+
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+	fd = dbus_watch_get_unix_fd(dbus_watch);
+#else
+	fd = dbus_watch_get_fd(dbus_watch);
+#endif
+
+	handler = (IOWatchHandler *) malloc(sizeof(IOWatchHandler));
+	temp.ptr = (void *) handler;
+	handler->dbus_watch = dbus_watch;
+	handler->pcb_watch = gui->watch_file(fd, pcb_condition, io_watch_handler_cb, temp);
+
+	dbus_watch_set_data(dbus_watch, handler, io_watch_handler_dbus_freed);
+	return TRUE;
+}
+
+static void watch_remove(DBusWatch * dbus_watch, void *data)
+{
+	/* Free the associated data. Its destroy callback removes the watch */
+	dbus_watch_set_data(dbus_watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch * dbus_watch, void *data)
+{
+	/* Simply add/remove the watch completely */
+	if (dbus_watch_get_enabled(dbus_watch))
+		watch_add(dbus_watch, data);
+	else
+		watch_remove(dbus_watch, data);
+}
+
+
+static dbus_bool_t timeout_add(DBusTimeout * timeout, void *data)
+{
+	TimeoutHandler *handler;
+	hidval temp;
+
+	/* We can't create a timeout without a GUI */
+	if (gui == NULL)
+		return TRUE;
+
+	/* We won't create a timeout until it becomes enabled. */
+	if (!dbus_timeout_get_enabled(timeout))
+		return TRUE;
+
+	/*FIXME: Need to store the interval, as PCB requires us
+	   to manually re-add the timer each time it expires.
+	   This is non-ideal, and hopefully can be changed? */
+
+	handler = (TimeoutHandler *) malloc(sizeof(TimeoutHandler));
+	temp.ptr = (void *) handler;
+	handler->dbus_timeout = timeout;
+	handler->interval = dbus_timeout_get_interval(timeout);
+	handler->pcb_timer = gui->add_timer(timeout_handler_cb, handler->interval, temp);
+
+	dbus_timeout_set_data(timeout, handler, timeout_handler_dbus_freed);
+	return TRUE;
+}
+
+static void timeout_remove(DBusTimeout * timeout, void *data)
+{
+	/* Free the associated data. Its destroy callback removes the timer */
+	dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout * timeout, void *data)
+{
+	/* Simply add/remove the timeout completely */
+	if (dbus_timeout_get_enabled(timeout))
+		timeout_add(timeout, data);
+	else
+		timeout_remove(timeout, data);
+}
+
+void dispatch_status_changed(DBusConnection * conn, DBusDispatchStatus new_status, void *data)
+{
+	/* TODO: Can use this eventually to add one-shot idle work-functions to dispatch
+	   remaining IO. It could possibly replace the block_hook polling mechanism.
+	   (We could use a one-shot block_book to dispatch the work though.)
+
+	   *** NO DISPATCHING TO BE DONE INSIDE THIS FUNCTION *** */
+}
+
+/* END INTERNALS */
+
+
+/**
+ * Sets the watch and timeout functions of a #DBusConnection
+ * to integrate the connection with the GUI HID's main loop.
+ *
+ * @param connection the connection
+ */
+void pcb_dbus_connection_setup_with_mainloop(DBusConnection * connection)
+{
+	/* ConnectionSetup *cs; */
+	hidval temp;
+
+	/* FIXME we never free the slot, so its refcount just keeps growing,
+	 * which is kind of broken.
+	 */
+	/* dbus_connection_allocate_data_slot (&connection_slot);
+	   if (connection_slot < 0)
+	   goto nomem; */
+
+#if 0
+	cs = connection_setup_new(connection);
+
+	if (!dbus_connection_set_data(connection, connection_slot, cs, (DBusFreeFunction) connection_setup_free))
+		goto nomem;
+#endif
+
+	if (!dbus_connection_set_watch_functions(connection, watch_add, watch_remove, watch_toggled, NULL, NULL))
+/*                                            cs, NULL))*/
+		goto nomem;
+
+	if (!dbus_connection_set_timeout_functions(connection, timeout_add, timeout_remove, timeout_toggled, NULL, NULL))
+/*                                              cs, NULL))*/
+		goto nomem;
+
+	dbus_connection_set_dispatch_status_function(connection, dispatch_status_changed, NULL, NULL);
+/*                                                cs, NULL);*/
+
+	/* Register a new mainloop hook to mop up any unfinished IO. */
+	temp.ptr = (void *) connection;
+	gui->add_block_hook(block_hook_cb, temp);
+
+	return;
+nomem:
+	fprintf(stderr, "Not enough memory to set up DBusConnection for use with PCB\n");
+}
+
+void pcb_dbus_connection_finish_with_mainloop(DBusConnection * connection)
+{
+	/*ConnectionSetup *cs;
+
+	   cs = dbus_connection_get_data (connection, connection_slot );
+
+	   Replace the stored data with NULL, thus freeing the old data
+	   DBus will call the function connection_setup_free() which we registered earlier
+	   dbus_connection_set_data (connection, connection_slot, NULL, NULL );
+
+	   dbus_connection_free_data_slot( &connection_slot ); */
+
+	if (!dbus_connection_set_watch_functions(connection, NULL, NULL, NULL, NULL, NULL))
+		goto nomem;
+
+	if (!dbus_connection_set_timeout_functions(connection, NULL, NULL, NULL, NULL, NULL))
+		goto nomem;
+
+	dbus_connection_set_dispatch_status_function(connection, NULL, NULL, NULL);
+	return;
+nomem:
+	fprintf(stderr, "Not enough memory when cleaning up DBusConnection mainloop integration\n");
+
+}
diff --git a/src_plugins/dbus/dbus-pcbmain.h b/src_plugins/dbus/dbus-pcbmain.h
new file mode 100644
index 0000000..1029f86
--- /dev/null
+++ b/src_plugins/dbus/dbus-pcbmain.h
@@ -0,0 +1,29 @@
+/*
+ * PCB, an interactive printed circuit board editor
+ * D-Bus IPC logic
+ * Copyright (C) 2006 University of Cambridge
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef PCB_DBUS_PCBMAIN_H
+#define PCB_DBUS_PCBMAIN_H
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+void pcb_dbus_connection_setup_with_mainloop(DBusConnection * connection);
+void pcb_dbus_connection_finish_with_mainloop(DBusConnection * connection);
+
+#endif /* !PCB_DBUS_PCBMAIN_H */
diff --git a/src_plugins/dbus/dbus.c b/src_plugins/dbus/dbus.c
new file mode 100644
index 0000000..e305ba9
--- /dev/null
+++ b/src_plugins/dbus/dbus.c
@@ -0,0 +1,387 @@
+/*
+ * PCB, an interactive printed circuit board editor
+ * D-Bus IPC logic
+ * Copyright (C) 2006 University of Cambridge
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ *  D-Bus code originally derrived from example-service.c in the dbus-glib bindings
+ */
+
+#warning scconfig TODO: detect this in scconfig:
+/* Define to 1 if you have the `dbus_watch_get_unix_fd' function. */
+/* #undef HAVE_DBUS_WATCH_GET_UNIX_FD */
+
+
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <string.h>
+
+#include "dbus-pcbmain.h"
+#include "dbus-introspect.h"
+#include "global.h"
+#include "data.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "event.h"
+#include "compat_misc.h"
+
+/* For lrealpath */
+#include "compat_lrealpath.h"
+
+
+#define PCB_DBUS_CANONICAL_NAME    "org.seul.geda.pcb"
+#define PCB_DBUS_OBJECT_PATH       "/org/seul/geda/pcb"
+#define PCB_DBUS_INTERFACE         "org.seul.geda.pcb"
+#define PCB_DBUS_ACTIONS_INTERFACE "org.seul.geda.pcb.actions"
+
+const char *dbus_cookie = "dbus plugin";
+
+static DBusConnection *pcb_dbus_conn;
+
+static DBusHandlerResult handle_get_filename(DBusConnection * connection, DBusMessage * message, void *data)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusHandlerResult result;
+	char *filename;
+
+	result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	/* TODO: Should check the message signature matches what we expect? */
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL) {
+		fprintf(stderr, "pcb_dbus: Couldn't create reply message\n");
+		return result;
+	}
+	dbus_message_iter_init_append(reply, &iter);
+
+	if (PCB->Filename)
+		filename = lrealpath(PCB->Filename);
+	else
+		filename = NULL;
+
+	if (filename == NULL) {
+#ifdef DEBUG
+		fprintf(stderr, "pcb_dbus: DEBUG: Couldn't get working filename, assuming none\n");
+#endif
+		filename = pcb_strdup("");
+		if (filename == NULL) {
+			fprintf(stderr, "pcb_dbus: Couldn't pcb_strdup( \"\" ) for the filename\n");
+			goto out;
+		}
+	}
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &filename)) {
+		fprintf(stderr, "pcb_dbus: Couldn't append return filename string to message reply, Out Of Memory!\n");
+		free(filename);
+		goto out;
+	}
+	free(filename);
+	if (!dbus_connection_send(connection, reply, NULL)) {
+		fprintf(stderr, "pcb_dbus: Couldn't send message, Out Of Memory!\n");
+		goto out;
+	}
+	result = DBUS_HANDLER_RESULT_HANDLED;
+
+out:
+	dbus_message_unref(reply);
+	return result;
+}
+
+
+static DBusHandlerResult handle_exec_action(DBusConnection * connection, DBusMessage * message, void *data)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusHandlerResult result;
+	DBusError err;
+	dbus_uint32_t retval;
+	char *action_name;
+	char **argv;
+	int argc;
+#ifdef DEBUG
+	int i;
+#endif
+
+	result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	/* TODO: Should check the message signature matches what we expect? */
+
+	/* initialise the error struct */
+	dbus_error_init(&err);
+
+	/* DON'T FREE action_name, as it belongs to DBUS,
+	 * DO    FREE argv, using dbus_free_string_array()
+	 */
+	argv = NULL;
+	if (!dbus_message_get_args(message,
+														 &err,
+														 DBUS_TYPE_STRING, &action_name,
+														 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &argv, &argc, DBUS_TYPE_INVALID)) {
+		fprintf(stderr, "Failed to read method arguments\n");
+		if (argv)
+			dbus_free_string_array(argv);
+		return result;
+	}
+
+#ifdef DEBUG
+	fprintf(stderr, "pcb_dbus: DEBUG: Executing action: %s(", action_name);
+	if (argc > 0)
+		fprintf(stderr, " \"%s\"", argv[0]);
+	for (i = 1; i < argc; i++)
+		fprintf(stderr, ", \"%s\"", argv[i]);
+	fprintf(stderr, " )\n");
+#endif
+
+	/* TODO: Proper return value from actions */
+	hid_actionv(action_name, argc, (const char**)argv);
+	retval = 0;
+
+	dbus_free_string_array(argv);
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL) {
+		fprintf(stderr, "pcb_dbus: Couldn't create reply message\n");
+		return result;
+	}
+	dbus_message_iter_init_append(reply, &iter);
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &retval)) {
+		fprintf(stderr, "pcb_dbus: Couldn't sent message, Out Of Memory!\n");
+		goto out;
+	}
+
+	if (!dbus_connection_send(connection, reply, NULL)) {
+		fprintf(stderr, "pcb_dbus: Couldn't send message, Out Of Memory!\n");
+		goto out;
+	}
+
+	result = DBUS_HANDLER_RESULT_HANDLED;
+out:
+	dbus_message_unref(reply);
+	return result;
+}
+
+
+static DBusHandlerResult handle_introspect(DBusConnection * connection, DBusMessage * message, void *data)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusHandlerResult result;
+
+	result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL) {
+		fprintf(stderr, "pcb_dbus: Couldn't create reply message\n");
+		return result;
+	}
+	dbus_message_iter_init_append(reply, &iter);
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &pcb_dbus_introspect_xml)) {
+		fprintf(stderr, "pcb_dbus: Couldn't add introspect XML to message return\n");
+		goto out;
+	}
+	if (!dbus_connection_send(pcb_dbus_conn, reply, NULL)) {
+		fprintf(stderr, "pcb_dbus: Couldn't queue reply message for sending\n");
+		goto out;
+	}
+	result = DBUS_HANDLER_RESULT_HANDLED;
+out:
+	dbus_message_unref(reply);
+	return result;
+}
+
+static void unregister_dbus_handler(DBusConnection * connection, void *data)
+{
+}
+
+
+static DBusHandlerResult handle_dbus_message(DBusConnection * connection, DBusMessage * message, void *data)
+{
+	int msg_type;
+	msg_type = dbus_message_get_type(message);
+
+	switch (msg_type) {
+	case DBUS_MESSAGE_TYPE_METHOD_CALL:
+		{
+			const char *method_name;
+			const char *interface_name;
+
+			method_name = dbus_message_get_member(message);
+			if (method_name == NULL) {
+				fprintf(stderr, "pcb_dbus: Method had no name specified\n");
+				break;
+			}
+
+			interface_name = dbus_message_get_interface(message);
+			if (interface_name == NULL) {
+				fprintf(stderr, "pcb_dbus: Method had no interface specified\n");
+				break;
+			}
+
+			if (strcmp(interface_name, PCB_DBUS_INTERFACE) == 0) {
+				if (strcmp(method_name, "GetFilename") == 0) {
+					return handle_get_filename(connection, message, data);
+				}
+				fprintf(stderr, "pcb_dbus: Interface '%s' has no method '%s'\n", interface_name, method_name);
+				break;
+			}
+			else if (strcmp(interface_name, PCB_DBUS_ACTIONS_INTERFACE) == 0) {
+				if (strcmp(method_name, "ExecAction") == 0) {
+					return handle_exec_action(connection, message, data);
+				}
+				fprintf(stderr, "pcb_dbus: Interface '%s' has no method '%s'\n", interface_name, method_name);
+				break;
+			}
+			else if (strcmp(interface_name, DBUS_INTERFACE_INTROSPECTABLE) == 0) {
+				if (strcmp(method_name, "Introspect") == 0) {
+					return handle_introspect(connection, message, data);
+				}
+				fprintf(stderr, "pcb_dbus: Interface '%s' has no method '%s'\n", interface_name, method_name);
+				break;
+			}
+			else {
+				fprintf(stderr, "pcb_dbus: Interface '%s' was not recognised\n", interface_name);
+				break;
+			}
+		}
+		break;
+
+	case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+		fprintf(stderr, "pcb_dbus: DBUG: Method return message\n");
+		/* WON'T ACTUALLY BE ANY UNLESS WE MAKE AN ASYNCRONOUS CALL? */
+		break;
+
+	case DBUS_MESSAGE_TYPE_ERROR:
+		fprintf(stderr, "pcb_dbus: DEBUG: Error message\n");
+		/* HOPE NOT! */
+		break;
+
+	case DBUS_MESSAGE_TYPE_SIGNAL:
+		fprintf(stderr, "pcb_dbus: DEBUG: Signal message\n");
+		/* NONE AT PRESENT */
+		break;
+
+	default:
+		fprintf(stderr, "pcb_dbus: DEBUG: Message type wasn't one we know about!\n");
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static void pcb_dbus_setup(void)
+{
+	DBusError err;
+	int ret;
+	const DBusObjectPathVTable object_vtable = {
+		unregister_dbus_handler,
+		handle_dbus_message,
+		NULL, NULL, NULL, NULL
+	};
+
+	/* Initialise the error variable */
+	dbus_error_init(&err);
+
+	/* Connect to the bus */
+	pcb_dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
+	if (dbus_error_is_set(&err)) {
+		fprintf(stderr, "pcb_dbus: DBus connection Error (%s)\n", err.message);
+		dbus_error_free(&err);
+	}
+	if (pcb_dbus_conn == NULL)
+		return;
+
+	/* Request the canonical name for PCB on the bus */
+	ret = dbus_bus_request_name(pcb_dbus_conn, PCB_DBUS_CANONICAL_NAME, DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
+	if (dbus_error_is_set(&err)) {
+		fprintf(stderr, "pcb_dbus: DBus name error (%s)\n", err.message);
+		dbus_error_free(&err);
+	}
+	if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER && ret != DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
+		fprintf(stderr, "pcb_dbus: Couldn't gain ownership or queued ownership of the canonical DBus name\n");
+		return;
+	}
+
+	if (!dbus_connection_register_object_path(pcb_dbus_conn, PCB_DBUS_OBJECT_PATH, &object_vtable, NULL	/* void * user_data */
+			)) {
+		fprintf(stderr, "pcb_dbus: Couldn't register DBUS handler for %s\n", PCB_DBUS_OBJECT_PATH);
+		return;
+	}
+
+	/* Setup intergration with the pcb mainloop */
+	pcb_dbus_connection_setup_with_mainloop(pcb_dbus_conn);
+
+/*  dbus_error_free(&err); */
+	return;
+}
+
+
+static void pcb_dbus_finish(void)
+{
+	DBusError err;
+
+	/* Initialise the error variable */
+	dbus_error_init(&err);
+
+	/* TODO: Could emit a "goodbye" signal here? */
+
+	dbus_connection_flush(pcb_dbus_conn);
+
+	dbus_connection_unregister_object_path(pcb_dbus_conn, PCB_DBUS_OBJECT_PATH);
+
+	dbus_bus_release_name(pcb_dbus_conn, PCB_DBUS_CANONICAL_NAME, &err);
+
+	dbus_error_free(&err);
+
+	pcb_dbus_connection_finish_with_mainloop(pcb_dbus_conn);
+
+	dbus_connection_close(pcb_dbus_conn);
+	dbus_connection_unref(pcb_dbus_conn);
+
+	/* Call DBus shutdown. This doesn't work with shared connections,
+	   only private ones (like we took out earlier).
+	   If any future module / plugin to PCB wants to use DBus too,
+	   we must remove this call. DBus will get shut-down when the app exits. */
+	dbus_shutdown();
+}
+
+static void dbus_gui_init(void *user_data, int argc, event_arg_t * argv[])
+{
+
+	/* this can not be done from init, before the gui starts, as it needs
+	to register fd watches in the GUI. Also won't play well together with GUI
+	switches... */
+
+	pcb_dbus_setup();
+}
+
+static void hid_dbus_uninit(void)
+{
+	pcb_dbus_finish();
+	event_unbind_allcookie(dbus_cookie);
+/*	hid_remove_actions_by_cookie(dbus_cookie);*/
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_dbus_init(void)
+{
+/*	REGISTER_ACTIONS(debug_action_list, dbus_cookie)*/
+	event_bind(EVENT_GUI_INIT, dbus_gui_init, NULL, dbus_cookie);
+	return hid_dbus_uninit;
+}
diff --git a/src_plugins/dbus/dbus.mk b/src_plugins/dbus/dbus.mk
new file mode 100644
index 0000000..ae3ee70
--- /dev/null
+++ b/src_plugins/dbus/dbus.mk
@@ -0,0 +1,11 @@
+append /local/pcb/OBJS {dbus-pcbmain.o dbus.o}
+
+append /local/pcb/RULES  [@
+# dbus
+dbus-introspect.h : dbus.xml Makefile
+	echo '/* AUTOMATICALLY GENERATED FROM dbus.xml DO NOT EDIT */' > $@.tmp
+	echo "static const char *pcb_dbus_introspect_xml ="  > $@.tmp
+	sed 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$$/"/' < dbus.xml >> $@.tmp
+	echo ";" >> $@.tmp
+	mv $@.tmp $@
+@]
diff --git a/src_plugins/dbus/dbus.xml b/src_plugins/dbus/dbus.xml
new file mode 100644
index 0000000..34a1621
--- /dev/null
+++ b/src_plugins/dbus/dbus.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Should name="/org/geda/seul/pcb" ? -->
+<node>
+  <!-- This section isn't needed for the glib binding
+       but is convenient for our raw binding          -->
+  <interface name="org.freedesktop.DBus.Introspectable">
+    <method name="Introspect">
+      <arg direction="out" type="s" name="data" />
+    </method>
+  </interface>
+  <!-- End section not needed for glib binding        -->
+  <interface name="org.seul.geda.pcb">
+    <method name="GetFilename">
+      <arg direction="out" type="s" />
+    </method>
+  </interface>
+  <interface name="org.seul.geda.pcb.actions">
+    <method name="ExecAction">
+      <arg direction="in" type="s" name="action" />
+      <arg direction="in" type="as" name="args" />
+      <arg direction="out" type="u" />
+    </method>
+  </interface>
+</node>
+
diff --git a/src_plugins/diag/Makefile b/src_plugins/diag/Makefile
new file mode 100644
index 0000000..91238a7
--- /dev/null
+++ b/src_plugins/diag/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_diag
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/diag/Plug.tmpasm b/src_plugins/diag/Plug.tmpasm
new file mode 100644
index 0000000..9ad2744
--- /dev/null
+++ b/src_plugins/diag/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {diag}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/diag/diag.o $(PLUGDIR)/diag/diag_conf.o @]
+
+switch /local/pcb/diag/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/diag/README b/src_plugins/diag/README
new file mode 100644
index 0000000..0ac1e33
--- /dev/null
+++ b/src_plugins/diag/README
@@ -0,0 +1,8 @@
+Actions for pcb-rnd core diagnostics, intended for developers. These are
+not in core because end users normally don't need these. As a plugin, due
+to dynamic loading, it can be dropped on an existing pcb-rnd installation
+with minimal risk of scaring away a reproducible bug.
+
+#state: works
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/diag/diag.c b/src_plugins/diag/diag.c
new file mode 100644
index 0000000..1430f0b
--- /dev/null
+++ b/src_plugins/diag/diag.c
@@ -0,0 +1,204 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This module, debug, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "layer.h"
+#include "diag_conf.h"
+#include "action_helper.h"
+#include "hid_actions.h"
+#include "plugins.h"
+#include "conf.h"
+#include "error.h"
+
+static const char dump_conf_syntax[] =
+	"dumpconf(native, [verbose], [prefix]) - dump the native (binary) config tree to stdout\n"
+	"dumpconf(lihata, role, [prefix]) - dump in-memory lihata representation of a config tree\n"
+
+	;
+
+static const char dump_conf_help[] = "Perform various operations on the configuration tree.";
+
+extern lht_doc_t *conf_root[];
+static int ActionDumpConf(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *cmd = argc > 0 ? argv[0] : NULL;
+
+	if (NSTRCMP(cmd, "native") == 0) {
+		int verbose;
+		const char *prefix = "";
+		if (argc > 1)
+			verbose = atoi(argv[1]);
+		if (argc > 2)
+			prefix = argv[2];
+		conf_dump(stdout, prefix, verbose, NULL);
+	}
+
+	else if (NSTRCMP(cmd, "lihata") == 0) {
+		conf_role_t role;
+		const char *prefix = "";
+		if (argc <= 1) {
+			Message(PCB_MSG_DEFAULT, "conf(dumplht) needs a role");
+			return 1;
+		}
+		role = conf_role_parse(argv[1]);
+		if (role == CFR_invalid) {
+			Message(PCB_MSG_DEFAULT, "Invalid role: '%s'", argv[1]);
+			return 1;
+		}
+		if (argc > 2)
+			prefix = argv[2];
+		if (conf_root[role] != NULL)
+			lht_dom_export(conf_root[role]->root, stdout, prefix);
+		else
+			printf("%s <empty>\n", prefix);
+	}
+
+	else {
+		Message(PCB_MSG_ERROR, "Invalid conf command '%s'\n", argv[0]);
+		return 1;
+	}
+	return 0;
+}
+
+static const char eval_conf_syntax[] =
+	"EvalConf(path) - evaluate a config path in different config sources to figure how it ended up in the native database\n"
+	;
+
+static const char eval_conf_help[] = "Perform various operations on the configuration tree.";
+
+static int ActionEvalConf(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *path = argc > 0 ? argv[0] : NULL;
+	conf_native_t *nat;
+	int role;
+
+	if (path == NULL) {
+		Message(PCB_MSG_ERROR, "EvalConf needs a path\n");
+		return 1;
+	}
+
+	nat = conf_get_field(path);
+	if (nat == NULL) {
+		Message(PCB_MSG_ERROR, "EvalConf: invalid path %s - no such config setting\n", path);
+		return 1;
+	}
+
+	printf("Conf node %s\n", path);
+	for(role = 0; role < CFR_max_real; role++) {
+		lht_node_t *n;
+		printf(" Role: %s\n", conf_role_name(role));
+		n = conf_lht_get_at(role, path, 0);
+		if (n != NULL) {
+			conf_policy_t pol = -1;
+			long prio = conf_default_prio[role];
+
+
+			if (conf_get_policy_prio(n, &pol, &prio) == 0)
+				printf("  * policy=%s\n  * prio=%ld\n", conf_policy_name(pol), prio);
+
+			if (n->file_name != NULL)
+				printf("  * from=%s:%d.%d\n", n->file_name, n->line, n->col);
+			else
+				printf("  * from=(unknown)\n");
+
+			lht_dom_export(n, stdout, "  ");
+		}
+		else
+			printf("  * not present\n");
+	}
+
+	printf(" Native:\n");
+	conf_print_native((conf_pfn)pcb_fprintf, stdout, "  ", 1, nat);
+
+	return 0;
+}
+
+static const char dump_layers_syntax[] =
+	"dumplayers()\n"
+	;
+
+static const char dump_layers_help[] = "Print info about each layer";
+
+extern lht_doc_t *conf_root[];
+static int ActionDumpLayers(int argc, const char **argv, Coord x, Coord y)
+{
+	int g, n, used, arr[128]; /* WARNING: this assumes we won't have more than 128 layers */
+
+	printf("Max: theoretical=%d current_board=%d\n", MAX_LAYER+2, max_copper_layer);
+	for(n = 0; n < MAX_LAYER+2; n++) {
+		int grp = GetGroupOfLayer(n);
+		printf(" [%d] %04x group=%d %s\n", n, pcb_layer_flags(n), grp, PCB->Data->Layer[n].Name);
+	}
+
+	/* query by logical layer: any bottom copper */
+	used = pcb_layer_list(PCB_LYT_COPPER | PCB_LYT_BOTTOM, arr, sizeof(arr)/sizeof(arr[0]));
+	printf("All %d bottom copper layers are:\n", used);
+	for(n = 0; n < used; n++) {
+		int layer_id = arr[n];
+		printf(" [%d] %s \n", layer_id, PCB->Data->Layer[layer_id].Name);
+	}
+
+	/* query by groups (physical layers): any copper in group */
+	used = pcb_layer_group_list(PCB_LYT_COPPER, arr, sizeof(arr)/sizeof(arr[0]));
+	printf("All %d groups containing copper layers are:\n", used);
+	for(g = 0; g < used; g++) {
+		int group_id = arr[g];
+		printf(" group %d\n", group_id);
+		for(n = 0; n < PCB->LayerGroups.Number[group_id]; n++) {
+			int layer_id = PCB->LayerGroups.Entries[group_id][n];
+			printf("  [%d] %s\n", layer_id, PCB->Data->Layer[layer_id].Name);
+		}
+	}
+
+	return 0;
+}
+
+
+HID_Action diag_action_list[] = {
+	{"dumpconf", 0, ActionDumpConf,
+	 dump_conf_help, dump_conf_syntax},
+	{"dumplayers", 0, ActionDumpLayers,
+	 dump_layers_help, dump_layers_syntax},
+	{"EvalConf", 0, ActionEvalConf,
+	 eval_conf_help, eval_conf_syntax}
+};
+
+static const char *diag_cookie = "debug plugin";
+
+REGISTER_ACTIONS(diag_action_list, diag_cookie)
+
+static void hid_diag_uninit(void)
+{
+	hid_remove_actions_by_cookie(diag_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_diag_init(void)
+{
+	REGISTER_ACTIONS(diag_action_list, diag_cookie)
+	return hid_diag_uninit;
+}
diff --git a/src_plugins/diag/diag_conf.c b/src_plugins/diag/diag_conf.c
new file mode 100644
index 0000000..43f7933
--- /dev/null
+++ b/src_plugins/diag/diag_conf.c
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "conf.h"
+
+void conf_dump(FILE *f, const char *prefix, int verbose, const char *match_prefix)
+{
+	htsp_entry_t *e;
+	int pl;
+
+	if (match_prefix != NULL)
+		pl = strlen(match_prefix);
+
+	for (e = htsp_first(conf_fields); e; e = htsp_next(conf_fields, e)) {
+		conf_native_t *node = (conf_native_t *)e->value;
+		if (match_prefix != NULL) {
+			if (strncmp(node->hash_path, match_prefix, pl) != 0)
+				continue;
+		}
+		conf_print_native((conf_pfn)pcb_fprintf, f, prefix, verbose, node);
+	}
+}
diff --git a/src_plugins/diag/diag_conf.h b/src_plugins/diag/diag_conf.h
new file mode 100644
index 0000000..b9a5f93
--- /dev/null
+++ b/src_plugins/diag/diag_conf.h
@@ -0,0 +1,4 @@
+
+/* Print all configuration items to f, prefixing each line with prefix
+   If match_prefix is not NULL, print only items with matching path prefix */
+void conf_dump(FILE *f, const char *prefix, int verbose, const char *match_prefix);
diff --git a/src_plugins/distalign/Makefile b/src_plugins/distalign/Makefile
new file mode 100644
index 0000000..d547c2a
--- /dev/null
+++ b/src_plugins/distalign/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_distalign
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/distalign/Plug.tmpasm b/src_plugins/distalign/Plug.tmpasm
new file mode 100644
index 0000000..29f6bc7
--- /dev/null
+++ b/src_plugins/distalign/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {distalign}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/distalign/distalign.o @]
+
+switch /local/pcb/distalign/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/distalign/README b/src_plugins/distalign/README
new file mode 100644
index 0000000..25afa5b
--- /dev/null
+++ b/src_plugins/distalign/README
@@ -0,0 +1,8 @@
+Introducing Align() and Distribute(), which work much like the similarly
+named functions in Visio. Given that PCB does not have the concept of
+"first selected object" to draw on, the reference points can be selected
+by arguments.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/distalign/distalign.c b/src_plugins/distalign/distalign.c
new file mode 100644
index 0000000..ce340dd
--- /dev/null
+++ b/src_plugins/distalign/distalign.c
@@ -0,0 +1,643 @@
+/*!
+ * \file distalign.c
+ *
+ * \brief distalign plug-in for PCB.
+ * Functions to distribute (evenly spread out) and align PCB elements.
+ *
+ * \author Copyright (C) 2007 Ben Jackson <ben at ben.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * From: Ben Jackson <bjj at saturn.home.ben.com>
+ * To: geda-user at moria.seul.org
+ * Date: Sat, 24 Feb 2007 22:13:51 -0800
+ * Subject: The amazing Align/Distribute plugin
+ *
+ * Ok, having "finished" the smartdisperse plugin, I moved on to phase 2
+ * of my "I'm sick of moving LEDs by hand" master plan.
+ *
+ * Introducing Align() and Distribute(), which work much like the
+ * similarly named functions in Visio.
+ * Given that PCB does not have the concept of "first selected object"
+ * to draw on, the reference points can be selected by arguments.
+ *
+ * Both functions always work on all selected elements[*].
+ *
+ * Some examples:
+ * <table noborder>
+ * <tr><td>
+ * :Align(X)
+ * </td><td>
+ * Objects are moved left/right to align their Marks with the Mark of
+ * the topmost selected object. All objects remain on or move to the
+ * current grid.
+ * </td></tr><tr><td>
+ * :Align(Y,Centers)
+ * </td><td>
+ * Now objects are aligned up/down to their geometric centers rather
+ * than their marks. The reference point is the center of the leftmost
+ * object.
+ * </td></tr><tr><td>
+ * :Align(Y,Tops,Average,Gridless)
+ * </td><td>
+ * Feeling bold, you align the tops all selected objects to the average
+ * of their top edges and let them wander off the grid to exactly the
+ * average of their tops.
+ * </td></tr><tr><td>
+ * :Align(X,Marks,Crosshair)
+ * </td><td>
+ * None of the objects are where you want them, so you move the
+ * crosshair to a likely spot and cause them all to move their X
+ * coordinates to your new X location.
+ * </td></tr><tr><td>
+ * :Align(X,Marks,Last)
+ * </td><td>
+ * As above, but instead of the crosshair you just override the default
+ * "First" to "Last" so the reference object is bottommost instead of
+ * topmost.
+ * </table>
+ *
+ * Now you have them in a neat line, but they're all clumped!
+ *
+ * <table noborder>
+ * <tr><td>
+ * :Distribute(Y)
+ * </td><td>
+ * Objects are spread out evenly from the first (uppermost) to last
+ * using their marks as the reference point.
+ * </td></tr><tr><td>
+ * :Distribute(X,Lefts,Crosshair,Last)
+ * </td><td>
+ * You move your crosshair to the left edge of the target area, which
+ * will be the leftmost edge of the leftmost object. The objects are
+ * spread between your crosshair and the original location of the Last
+ * (rightmost) object.
+ * </td></tr><tr><td>
+ * :Distribute(Y,Gaps)
+ * </td><td>
+ * You have chosen to select many oddly sized objects, and instead of
+ * their centers being spread out evenly, you want the space (or "gaps")
+ * to be even.
+ *
+ * You get tricky and bind `Align(X,Marks,Crosshair)' to a key.
+ * Now you can select an object and hit your key and the object will
+ * warp to the same X coordinate as your cursor.
+ *
+ * Original source was:  http://ad7gd.net/geda/distalign.c
+ *
+ * [*] If it has any flaws, it is that you can't operate non-element
+ * objects, though some melding of autocrop (which knows how to do such
+ * things) and distalign could produce such a capability.
+ *
+ * Ben Jackson AD7GD <ben at ben.com>
+ *
+ * http://www.ben.com/
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "rats.h"
+#include "error.h"
+#include "move.h"
+#include "draw.h"
+#include "set.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+#define ARG(n) (argc > (n) ? argv[n] : 0)
+
+static const char align_syntax[] =
+	"Align(X/Y, [Lefts/Rights/Tops/Bottoms/Centers/Marks, [First/Last/Crosshair/Average[, Gridless]]])";
+
+static const char distribute_syntax[] =
+	"Distribute(Y, [Lefts/Rights/Tops/Bottoms/Centers/Marks/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]])";
+
+enum {
+	K_X,
+	K_Y,
+	K_Lefts,
+	K_Rights,
+	K_Tops,
+	K_Bottoms,
+	K_Centers,
+	K_Marks,
+	K_Gaps,
+	K_First,
+	K_Last,
+	K_Average,
+	K_Crosshair,
+	K_Gridless,
+	K_none,
+	K_align,
+	K_distribute
+};
+
+static const char *keywords[] = {
+	/*[K_X] */ "X",
+	/*[K_Y] */ "Y",
+	/*[K_Lefts] */ "Lefts",
+	/*[K_Rights] */ "Rights",
+	/*[K_Tops] */ "Tops",
+	/*[K_Bottoms] */ "Bottoms",
+	/*[K_Centers] */ "Centers",
+	/*[K_Marks] */ "Marks",
+	/*[K_Gaps] */ "Gaps",
+	/*[K_First] */ "First",
+	/*[K_Last] */ "Last",
+	/*[K_Average] */ "Average",
+	/*[K_Crosshair] */ "Crosshair",
+	/*[K_Gridless] */ "Gridless",
+};
+
+static int keyword(const char *s)
+{
+	int i;
+
+	if (!s) {
+		return K_none;
+	}
+	for (i = 0; i < ENTRIES(keywords); ++i) {
+		if (keywords[i] && strcasecmp(s, keywords[i]) == 0)
+			return i;
+	}
+	return -1;
+}
+
+
+/* this macro produces a function in X or Y that switches on 'point' */
+#define COORD(DIR)						\
+static inline Coord		        			\
+coord ## DIR(ElementType *element, int point)			\
+{								\
+	switch (point) {					\
+	case K_Marks:						\
+		return element->Mark ## DIR;			\
+	case K_Lefts:						\
+	case K_Tops:						\
+		return element->BoundingBox.DIR ## 1;		\
+	case K_Rights:						\
+	case K_Bottoms:						\
+		return element->BoundingBox.DIR ## 2;		\
+	case K_Centers:						\
+	case K_Gaps:						\
+		return (element->BoundingBox.DIR ## 1 +		\
+		       element->BoundingBox.DIR ## 2) / 2;	\
+	}							\
+	return 0;						\
+}
+
+COORD(X)
+	COORD(Y)
+
+/* return the element coordinate associated with the given internal point */
+		 static Coord coord(ElementType * element, int dir, int point)
+{
+	if (dir == K_X)
+		return coordX(element, point);
+	else
+		return coordY(element, point);
+}
+
+static struct element_by_pos {
+	ElementType *element;
+	Coord pos;
+	Coord width;
+} *elements_by_pos;
+
+static int nelements_by_pos;
+
+static int cmp_ebp(const void *a, const void *b)
+{
+	const struct element_by_pos *ea = a;
+	const struct element_by_pos *eb = b;
+
+	return ea->pos - eb->pos;
+}
+
+/*!
+ * Find all selected objects, then order them in order by coordinate in
+ * the 'dir' axis. This is used to find the "First" and "Last" elements
+ * and also to choose the distribution order.
+ *
+ * For alignment, first and last are in the orthogonal axis (imagine if
+ * you were lining up letters in a sentence, aligning *vertically* to the
+ * first letter means selecting the first letter *horizontally*).
+ *
+ * For distribution, first and last are in the distribution axis.
+ */
+static int sort_elements_by_pos(int op, int dir, int point)
+{
+	int nsel = 0;
+
+	if (nelements_by_pos)
+		return nelements_by_pos;
+	if (op == K_align)
+		dir = dir == K_X ? K_Y : K_X;	/* see above */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, element))
+			continue;
+		nsel++;
+	}
+	END_LOOP;
+	if (!nsel)
+		return 0;
+	elements_by_pos = malloc(nsel * sizeof(*elements_by_pos));
+	nelements_by_pos = nsel;
+	nsel = 0;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, element))
+			continue;
+		elements_by_pos[nsel].element = element;
+		elements_by_pos[nsel++].pos = coord(element, dir, point);
+	}
+	END_LOOP;
+	qsort(elements_by_pos, nelements_by_pos, sizeof(*elements_by_pos), cmp_ebp);
+	return nelements_by_pos;
+}
+
+static void free_elements_by_pos(void)
+{
+	if (nelements_by_pos) {
+		free(elements_by_pos);
+		elements_by_pos = NULL;
+		nelements_by_pos = 0;
+	}
+}
+
+/*!
+ * \brief Find the reference coordinate from the specified points of all
+ * selected elements.
+ */
+static Coord reference_coord(int op, int x, int y, int dir, int point, int reference)
+{
+	Coord q;
+	int nsel;
+
+	q = 0;
+	switch (reference) {
+	case K_Crosshair:
+		if (dir == K_X)
+			q = x;
+		else
+			q = y;
+		break;
+	case K_Average:							/* the average among selected elements */
+		nsel = 0;
+		q = 0;
+		ELEMENT_LOOP(PCB->Data);
+		{
+			if (!TEST_FLAG(PCB_FLAG_SELECTED, element))
+				continue;
+			q += coord(element, dir, point);
+			nsel++;
+		}
+		END_LOOP;
+		if (nsel)
+			q /= nsel;
+		break;
+	case K_First:								/* first or last in the orthogonal direction */
+	case K_Last:
+		if (!sort_elements_by_pos(op, dir, point)) {
+			q = 0;
+			break;
+		}
+		if (reference == K_First) {
+			q = coord(elements_by_pos[0].element, dir, point);
+		}
+		else {
+			q = coord(elements_by_pos[nelements_by_pos - 1].element, dir, point);
+		}
+		break;
+	}
+	return q;
+}
+
+/*!
+ * Align(X, [Lefts/Rights/Centers/Marks, [First/Last/Crosshair/Average[, Gridless]]])\n
+ * Align(Y, [Tops/Bottoms/Centers/Marks, [First/Last/Crosshair/Average[, Gridless]]])
+ *
+ * X or Y - Select which axis will move, other is untouched. \n
+ * Lefts, Rights, \n
+ * Tops, Bottoms, \n
+ * Centers, Marks - Pick alignment point within each element. \n
+ * First, Last, \n
+ * Crosshair, \n
+ * Average - Alignment reference, First=Topmost/Leftmost, \n
+ * Last=Bottommost/Rightmost, Average or Crosshair point \n
+ * Gridless - Do not force results to align to prevailing grid. \n
+ *
+ * Defaults are Marks, First.
+ */
+static int align(int argc, const char **argv, Coord x, Coord y)
+{
+	int dir;
+	int point;
+	int reference;
+	int gridless;
+	Coord q;
+	int changed = 0;
+
+	if (argc < 1 || argc > 4) {
+		AFAIL(align);
+	}
+	/* parse direction arg */
+	switch ((dir = keyword(ARG(0)))) {
+	case K_X:
+	case K_Y:
+		break;
+	default:
+		AFAIL(align);
+	}
+	/* parse point (within each element) which will be aligned */
+	switch ((point = keyword(ARG(1)))) {
+	case K_Centers:
+	case K_Marks:
+		break;
+	case K_Lefts:
+	case K_Rights:
+		if (dir == K_Y) {
+			AFAIL(align);
+		}
+		break;
+	case K_Tops:
+	case K_Bottoms:
+		if (dir == K_X) {
+			AFAIL(align);
+		}
+		break;
+	case K_none:
+		point = K_Marks;						/* default value */
+		break;
+	default:
+		AFAIL(align);
+	}
+	/* parse reference which will determine alignment coordinates */
+	switch ((reference = keyword(ARG(2)))) {
+	case K_First:
+	case K_Last:
+	case K_Average:
+	case K_Crosshair:
+		break;
+	case K_none:
+		reference = K_First;				/* default value */
+		break;
+	default:
+		AFAIL(align);
+	}
+	/* optionally work off the grid (solar cells!) */
+	switch (keyword(ARG(3))) {
+	case K_Gridless:
+		gridless = 1;
+		break;
+	case K_none:
+		gridless = 0;
+		break;
+	default:
+		AFAIL(align);
+	}
+	/* find the final alignment coordinate using the above options */
+	q = reference_coord(K_align, Crosshair.X, Crosshair.Y, dir, point, reference);
+	/* move all selected elements to the new coordinate */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		Coord p, dp, dx, dy;
+
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, element))
+			continue;
+		/* find delta from reference point to reference point */
+		p = coord(element, dir, point);
+		dp = q - p;
+		/* ...but if we're gridful, keep the mark on the grid */
+		if (!gridless) {
+			dp -= (coord(element, dir, K_Marks) + dp) % (long) (PCB->Grid);
+		}
+		if (dp) {
+			/* move from generic to X or Y */
+			dx = dy = dp;
+			if (dir == K_X)
+				dy = 0;
+			else
+				dx = 0;
+			MoveElementLowLevel(PCB->Data, element, dx, dy);
+			AddObjectToMoveUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element, dx, dy);
+			changed = 1;
+		}
+	}
+	END_LOOP;
+	if (changed) {
+		IncrementUndoSerialNumber();
+		Redraw();
+		SetChangedFlag(1);
+	}
+	free_elements_by_pos();
+	return 0;
+}
+
+/*!
+ * Distribute(X, [Lefts/Rights/Centers/Marks/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) \n
+ * Distribute(Y, [Tops/Bottoms/Centers/Marks/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) \n
+ * \n
+ * As with align, plus: \n
+ * \n
+ * Gaps - Make gaps even rather than spreading points evenly. \n
+ * First, Last, \n
+ * Crosshair - Two arguments specifying both ends of the distribution,
+ * they can't both be the same. \n
+ * \n
+ * Defaults are Marks, First, Last \n
+ * \n
+ * Distributed elements always retain the same relative order they had
+ * before they were distributed. \n
+ */
+static int distribute(int argc, const char **argv, Coord x, Coord y)
+{
+	int dir;
+	int point;
+	int refa, refb;
+	int gridless;
+	Coord s, e, slack;
+	int divisor;
+	int changed = 0;
+	int i;
+
+	if (argc < 1 || argc == 3 || argc > 4) {
+		AFAIL(distribute);
+	}
+	/* parse direction arg */
+	switch ((dir = keyword(ARG(0)))) {
+	case K_X:
+	case K_Y:
+		break;
+	default:
+		AFAIL(distribute);
+	}
+	/* parse point (within each element) which will be distributed */
+	switch ((point = keyword(ARG(1)))) {
+	case K_Centers:
+	case K_Marks:
+	case K_Gaps:
+		break;
+	case K_Lefts:
+	case K_Rights:
+		if (dir == K_Y) {
+			AFAIL(distribute);
+		}
+		break;
+	case K_Tops:
+	case K_Bottoms:
+		if (dir == K_X) {
+			AFAIL(distribute);
+		}
+		break;
+	case K_none:
+		point = K_Marks;						/* default value */
+		break;
+	default:
+		AFAIL(distribute);
+	}
+	/* parse reference which will determine first distribution coordinate */
+	switch ((refa = keyword(ARG(2)))) {
+	case K_First:
+	case K_Last:
+	case K_Average:
+	case K_Crosshair:
+		break;
+	case K_none:
+		refa = K_First;							/* default value */
+		break;
+	default:
+		AFAIL(distribute);
+	}
+	/* parse reference which will determine final distribution coordinate */
+	switch ((refb = keyword(ARG(3)))) {
+	case K_First:
+	case K_Last:
+	case K_Average:
+	case K_Crosshair:
+		break;
+	case K_none:
+		refb = K_Last;							/* default value */
+		break;
+	default:
+		AFAIL(distribute);
+	}
+	if (refa == refb) {
+		AFAIL(distribute);
+	}
+	/* optionally work off the grid (solar cells!) */
+	switch (keyword(ARG(4))) {
+	case K_Gridless:
+		gridless = 1;
+		break;
+	case K_none:
+		gridless = 0;
+		break;
+	default:
+		AFAIL(distribute);
+	}
+	/* build list of elements in orthogonal axis order */
+	sort_elements_by_pos(K_distribute, dir, point);
+	/* find the endpoints given the above options */
+	s = reference_coord(K_distribute, x, y, dir, point, refa);
+	e = reference_coord(K_distribute, x, y, dir, point, refb);
+	slack = e - s;
+	/* use this divisor to calculate spacing (for 1 elt, avoid 1/0) */
+	divisor = (nelements_by_pos > 1) ? (nelements_by_pos - 1) : 1;
+	/* even the gaps instead of the edges or whatnot */
+	/* find the "slack" in the row */
+	if (point == K_Gaps) {
+		Coord w;
+
+		/* subtract all the "widths" from the slack */
+		for (i = 0; i < nelements_by_pos; ++i) {
+			ElementType *element = elements_by_pos[i].element;
+			/* coord doesn't care if I mix Lefts/Tops */
+			w = elements_by_pos[i].width = coord(element, dir, K_Rights) - coord(element, dir, K_Lefts);
+			/* Gaps distribution is on centers, so half of
+			 * first and last element don't count */
+			if (i == 0 || i == nelements_by_pos - 1) {
+				w /= 2;
+			}
+			slack -= w;
+		}
+		/* slack could be negative */
+	}
+	/* move all selected elements to the new coordinate */
+	for (i = 0; i < nelements_by_pos; ++i) {
+		ElementType *element = elements_by_pos[i].element;
+		Coord p, q, dp, dx, dy;
+
+		/* find reference point for this element */
+		q = s + slack * i / divisor;
+		/* find delta from reference point to reference point */
+		p = coord(element, dir, point);
+		dp = q - p;
+		/* ...but if we're gridful, keep the mark on the grid */
+		if (!gridless) {
+			dp -= (coord(element, dir, K_Marks) + dp) % (long) (PCB->Grid);
+		}
+		if (dp) {
+			/* move from generic to X or Y */
+			dx = dy = dp;
+			if (dir == K_X)
+				dy = 0;
+			else
+				dx = 0;
+			MoveElementLowLevel(PCB->Data, element, dx, dy);
+			AddObjectToMoveUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element, dx, dy);
+			changed = 1;
+		}
+		/* in gaps mode, accumulate part widths */
+		if (point == K_Gaps) {
+			/* move remaining half of our element */
+			s += elements_by_pos[i].width / 2;
+			/* move half of next element */
+			if (i < nelements_by_pos - 1)
+				s += elements_by_pos[i + 1].width / 2;
+		}
+	}
+	if (changed) {
+		IncrementUndoSerialNumber();
+		Redraw();
+		SetChangedFlag(1);
+	}
+	free_elements_by_pos();
+	return 0;
+}
+
+static HID_Action distalign_action_list[] = {
+	{"distribute", NULL, distribute, "Distribute Elements", distribute_syntax},
+	{"align", NULL, align, "Align Elements", align_syntax}
+};
+
+static char *distalign_cookie = "distalign plugin";
+
+REGISTER_ACTIONS(distalign_action_list, distalign_cookie)
+
+static void hid_distalign_uninit(void)
+{
+	hid_remove_actions_by_cookie(distalign_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_distalign_init()
+{
+	REGISTER_ACTIONS(distalign_action_list, distalign_cookie);
+	return hid_distalign_uninit;
+}
+
+
diff --git a/src_plugins/distaligntext/Makefile b/src_plugins/distaligntext/Makefile
new file mode 100644
index 0000000..c76b4db
--- /dev/null
+++ b/src_plugins/distaligntext/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_distaligntext
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/distaligntext/Plug.tmpasm b/src_plugins/distaligntext/Plug.tmpasm
new file mode 100644
index 0000000..0cdb005
--- /dev/null
+++ b/src_plugins/distaligntext/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {distaligntext}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/distaligntext/distaligntext.o @]
+
+switch /local/pcb/distaligntext/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/distaligntext/README b/src_plugins/distaligntext/README
new file mode 100644
index 0000000..8ebd157
--- /dev/null
+++ b/src_plugins/distaligntext/README
@@ -0,0 +1,5 @@
+Same as distalign, operates on text objects.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/distaligntext/distaligntext.c b/src_plugins/distaligntext/distaligntext.c
new file mode 100644
index 0000000..41d113e
--- /dev/null
+++ b/src_plugins/distaligntext/distaligntext.c
@@ -0,0 +1,637 @@
+/*!
+ * \file distaligntext.c
+ *
+ * \brief distaligntext plug-in for PCB.
+ *
+ * \author Copyright (C) 2012 Dan White <dan at whiteaudio.com>
+ * Functions to distribute (evenly spread out) and align PCB text.
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Substantially from distalign.c
+ * Copyright (C) 2007 Ben Jackson <ben at ben.com>
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ *
+ * Modifications and internal differences are significant enough warrant
+ * a new related plugin.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "rats.h"
+#include "error.h"
+#include "move.h"
+#include "draw.h"
+#include "set.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "conf_core.h"
+
+#define ARG(n) (argc > (n) ? argv[n] : 0)
+
+static const char aligntext_syntax[] =
+	"AlignText(X/Y, [Lefts/Rights/Tops/Bottoms/Centers, [First/Last/Crosshair/Average[, Gridless]]])";
+
+static const char distributetext_syntax[] =
+	"DistributeText(Y, [Lefts/Rights/Tops/Bottoms/Centers/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]])";
+
+enum {
+	K_X,
+	K_Y,
+	K_Lefts,
+	K_Rights,
+	K_Tops,
+	K_Bottoms,
+	K_Centers,
+	K_Gaps,
+	K_First,
+	K_Last,
+	K_Average,
+	K_Crosshair,
+	K_Gridless,
+	K_none,
+	K_aligntext,
+	K_distributetext
+};
+
+static const char *keywords[] = {
+	/* [K_X] */ "X",
+	/* [K_Y] */ "Y",
+	/* [K_Lefts] */ "Lefts",
+	/* [K_Rights] */ "Rights",
+	/* [K_Tops] */ "Tops",
+	/* [K_Bottoms] */ "Bottoms",
+	/* [K_Centers] */ "Centers",
+	/* [K_Gaps] */ "Gaps",
+	/* [K_First] */ "First",
+	/* [K_Last] */ "Last",
+	/* [K_Average] */ "Average",
+	/* [K_Crosshair] */ "Crosshair",
+	/* [K_Gridless] */ "Gridless",
+};
+
+static int keyword(const char *s)
+{
+	int i;
+
+	if (!s) {
+		return K_none;
+	}
+	for (i = 0; i < ENTRIES(keywords); ++i) {
+		if (keywords[i] && strcasecmp(s, keywords[i]) == 0)
+			return i;
+	}
+	return -1;
+}
+
+
+/* this macro produces a function in X or Y that switches on 'point' */
+#define COORD(DIR)						\
+static inline Coord				        	\
+coord ## DIR(TextType *text, int point)		        	\
+{								\
+	switch (point) {					\
+	case K_Lefts:						\
+	case K_Tops:						\
+		return text->BoundingBox.DIR ## 1;		\
+	case K_Rights:						\
+	case K_Bottoms:						\
+		return text->BoundingBox.DIR ## 2;		\
+	case K_Centers:						\
+	case K_Gaps:						\
+		return (text->BoundingBox.DIR ## 1 +		\
+		       text->BoundingBox.DIR ## 2) / 2;	        \
+	}							\
+	return 0;						\
+}
+
+COORD(X)
+	COORD(Y)
+
+/*!
+ * Return the text coordinate associated with the given internal point.
+ */
+		 static Coord coord(TextType * text, int dir, int point)
+{
+	if (dir == K_X)
+		return coordX(text, point);
+	else
+		return coordY(text, point);
+}
+
+static struct text_by_pos {
+	TextType *text;
+	Coord pos;
+	Coord width;
+	int type;
+} *texts_by_pos;
+
+static int ntexts_by_pos;
+
+static int cmp_tbp(const void *a, const void *b)
+{
+	const struct text_by_pos *ta = a;
+	const struct text_by_pos *tb = b;
+
+	return ta->pos - tb->pos;
+}
+
+/*!
+ * Find all selected text objects, then order them in order by coordinate in
+ * the 'dir' axis.  This is used to find the "First" and "Last" elements
+ * and also to choose the distribution order.
+ *
+ * For alignment, first and last are in the orthogonal axis (imagine if
+ * you were lining up letters in a sentence, aligning *vertically* to the
+ * first letter means selecting the first letter *horizontally*).
+ *
+ * For distribution, first and last are in the distribution axis.
+ */
+static int sort_texts_by_pos(int op, int dir, int point)
+{
+	int nsel = 0;
+
+	if (ntexts_by_pos)
+		return ntexts_by_pos;
+	if (op == K_aligntext)
+		dir = dir == K_X ? K_Y : K_X;	/* see above */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		TextType *text;
+		text = &(element)->Name[NAME_INDEX()];
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, text))
+			continue;
+		nsel++;
+	}
+	END_LOOP;
+	ALLTEXT_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, text))
+			continue;
+		nsel++;
+	}
+	ENDALL_LOOP;
+	if (!nsel)
+		return 0;
+	texts_by_pos = malloc(nsel * sizeof(*texts_by_pos));
+	ntexts_by_pos = nsel;
+	nsel = 0;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		TextType *text;
+		text = &(element)->Name[NAME_INDEX()];
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, text))
+			continue;
+		texts_by_pos[nsel].text = text;
+		texts_by_pos[nsel].type = PCB_TYPE_ELEMENT_NAME;
+		texts_by_pos[nsel++].pos = coord(text, dir, point);
+	}
+	END_LOOP;
+	ALLTEXT_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, text))
+			continue;
+		texts_by_pos[nsel].text = text;
+		texts_by_pos[nsel].type = PCB_TYPE_TEXT;
+		texts_by_pos[nsel++].pos = coord(text, dir, point);
+	}
+	ENDALL_LOOP;
+	qsort(texts_by_pos, ntexts_by_pos, sizeof(*texts_by_pos), cmp_tbp);
+	return ntexts_by_pos;
+}
+
+static void free_texts_by_pos(void)
+{
+	if (ntexts_by_pos) {
+		free(texts_by_pos);
+		texts_by_pos = NULL;
+		ntexts_by_pos = 0;
+	}
+}
+
+
+/*!
+ * Find the reference coordinate from the specified points of all
+ * selected text.
+ */
+static Coord reference_coord(int op, int x, int y, int dir, int point, int reference)
+{
+	Coord q;
+	int i, nsel;
+
+	q = 0;
+	switch (reference) {
+	case K_Crosshair:
+		if (dir == K_X)
+			q = x;
+		else
+			q = y;
+		break;
+	case K_Average:							/* the average among selected text */
+		nsel = ntexts_by_pos;
+		for (i = 0; i < ntexts_by_pos; ++i) {
+			q += coord(texts_by_pos[i].text, dir, point);
+		}
+		if (nsel)
+			q /= nsel;
+		break;
+	case K_First:								/* first or last in the orthogonal direction */
+	case K_Last:
+		if (!sort_texts_by_pos(op, dir, point)) {
+			q = 0;
+			break;
+		}
+		if (reference == K_First) {
+			q = coord(texts_by_pos[0].text, dir, point);
+		}
+		else {
+			q = coord(texts_by_pos[ntexts_by_pos - 1].text, dir, point);
+		}
+		break;
+	}
+	return q;
+}
+
+
+/*!
+ * AlignText(X, [Lefts/Rights/Centers, [First/Last/Crosshair/Average[, Gridless]]])\n
+ * AlignText(Y, [Tops/Bottoms/Centers, [First/Last/Crosshair/Average[, Gridless]]])
+ *
+ * X or Y - Select which axis will move, other is untouched. \n
+ * Lefts, Rights, \n
+ * Tops, Bottoms, \n
+ * Centers - Pick alignment point within each element. \n
+ * NB: text objects have no Mark. \n
+ * First, Last, \n
+ * Crosshair, \n
+ * Average - Alignment reference, First=Topmost/Leftmost, \n
+ * Last=Bottommost/Rightmost, Average or Crosshair point \n
+ * Gridless - Do not force results to align to prevailing grid. \n
+ *
+ * Defaults are Lefts/Tops, First
+ */
+static int aligntext(int argc, const char **argv, Coord x, Coord y)
+{
+	int dir;
+	int point;
+	int reference;
+	int gridless;
+	Coord q;
+	Coord p, dp, dx, dy;
+	int changed = 0;
+
+	if (argc < 1 || argc > 4) {
+		AFAIL(aligntext);
+	}
+	/* parse direction arg */
+	switch ((dir = keyword(ARG(0)))) {
+	case K_X:
+	case K_Y:
+		break;
+	default:
+		AFAIL(aligntext);
+	}
+	/* parse point (within each element) which will be aligned */
+	switch ((point = keyword(ARG(1)))) {
+	case K_Centers:
+		break;
+	case K_Lefts:
+	case K_Rights:
+		if (dir == K_Y) {
+			AFAIL(aligntext);
+		}
+		break;
+	case K_Tops:
+	case K_Bottoms:
+		if (dir == K_X) {
+			AFAIL(aligntext);
+		}
+		break;
+	case K_none:									/* default value */
+		if (dir == K_X) {
+			point = K_Lefts;
+		}
+		else {
+			point = K_Tops;
+		}
+		break;
+	default:
+		AFAIL(aligntext);
+	}
+	/* parse reference which will determine alignment coordinates */
+	switch ((reference = keyword(ARG(2)))) {
+	case K_First:
+	case K_Last:
+	case K_Average:
+	case K_Crosshair:
+		break;
+	case K_none:
+		reference = K_First;				/* default value */
+		break;
+	default:
+		AFAIL(aligntext);
+	}
+	/* optionally work off the grid (solar cells!) */
+	switch (keyword(ARG(3))) {
+	case K_Gridless:
+		gridless = 1;
+		break;
+	case K_none:
+		gridless = 0;
+		break;
+	default:
+		AFAIL(aligntext);
+	}
+	SaveUndoSerialNumber();
+	/* find the final alignment coordinate using the above options */
+	q = reference_coord(K_aligntext, Crosshair.X, Crosshair.Y, dir, point, reference);
+	/* move all selected elements to the new coordinate */
+	/* selected text part of an element */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		TextType *text;
+		text = &(element)->Name[NAME_INDEX()];
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, text))
+			continue;
+		/* find delta from reference point to reference point */
+		p = coord(text, dir, point);
+		dp = q - p;
+		/* ...but if we're gridful, keep the mark on the grid */
+		/* TODO re-enable for text, need textcoord()
+		   if (!gridless)
+		   {
+		   dp -= (coord (text, dir, K_Marks) + dp) % (long) (PCB->Grid);
+		   }
+		 */
+		if (dp) {
+			/* move from generic to X or Y */
+			dx = dy = dp;
+			if (dir == K_X)
+				dy = 0;
+			else
+				dx = 0;
+			MoveObject(PCB_TYPE_ELEMENT_NAME, element, text, text, dx, dy);
+			changed = 1;
+		}
+	}
+	END_LOOP;
+	/* Selected bare text objects */
+	ALLTEXT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, text)) {
+			/* find delta from reference point to reference point */
+			p = coord(text, dir, point);
+			dp = q - p;
+			/* ...but if we're gridful, keep the mark on the grid */
+			/* TODO re-enable for text, need textcoord()
+			   if (!gridless)
+			   {
+			   dp -= (coord (text, dir, K_Marks) + dp) % (long) (PCB->Grid);
+			   }
+			 */
+			if (dp) {
+				/* move from generic to X or Y */
+				dx = dy = dp;
+				if (dir == K_X)
+					dy = 0;
+				else
+					dx = 0;
+				MoveObject(PCB_TYPE_TEXT, layer, text, text, dx, dy);
+				changed = 1;
+			}
+		}
+	}
+	ENDALL_LOOP;
+	if (changed) {
+		RestoreUndoSerialNumber();
+		IncrementUndoSerialNumber();
+		Redraw();
+		SetChangedFlag(pcb_true);
+	}
+	free_texts_by_pos();
+	return 0;
+}
+
+/*!
+ * DistributeText(X, [Lefts/Rights/Centers/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) \n
+ * DistributeText(Y, [Tops/Bottoms/Centers/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) \n
+ * \n
+ * As with align, plus: \n
+ * \n
+ * Gaps - Make gaps even rather than spreading points evenly. \n
+ * First, Last, \n
+ * Crosshair - Two arguments specifying both ends of the distribution,
+ * they can't both be the same. \n
+ * \n
+ * Defaults are Lefts/Tops, First, Last \n
+ * \n
+ * Distributed texts always retain the same relative order they had
+ * before they were distributed. \n
+ */
+static int distributetext(int argc, const char **argv, Coord x, Coord y)
+{
+	int dir;
+	int point;
+	int refa, refb;
+	int gridless;
+	Coord s, e, slack;
+	int divisor;
+	int changed = 0;
+	int i;
+
+	if (argc < 1 || argc == 3 || argc > 4) {
+		AFAIL(distributetext);
+	}
+	/* parse direction arg */
+	switch ((dir = keyword(ARG(0)))) {
+	case K_X:
+	case K_Y:
+		break;
+	default:
+		AFAIL(distributetext);
+	}
+	/* parse point (within each element) which will be distributed */
+	switch ((point = keyword(ARG(1)))) {
+	case K_Centers:
+	case K_Gaps:
+		break;
+	case K_Lefts:
+	case K_Rights:
+		if (dir == K_Y) {
+			AFAIL(distributetext);
+		}
+		break;
+	case K_Tops:
+	case K_Bottoms:
+		if (dir == K_X) {
+			AFAIL(distributetext);
+		}
+		break;
+	case K_none:									/* default value */
+		if (dir == K_X) {
+			point = K_Lefts;
+		}
+		else {
+			point = K_Tops;
+		}
+		break;
+	default:
+		AFAIL(distributetext);
+	}
+	/* parse reference which will determine first distribution coordinate */
+	switch ((refa = keyword(ARG(2)))) {
+	case K_First:
+	case K_Last:
+	case K_Average:
+	case K_Crosshair:
+		break;
+	case K_none:
+		refa = K_First;							/* default value */
+		break;
+	default:
+		AFAIL(distributetext);
+	}
+	/* parse reference which will determine final distribution coordinate */
+	switch ((refb = keyword(ARG(3)))) {
+	case K_First:
+	case K_Last:
+	case K_Average:
+	case K_Crosshair:
+		break;
+	case K_none:
+		refb = K_Last;							/* default value */
+		break;
+	default:
+		AFAIL(distributetext);
+	}
+	if (refa == refb) {
+		AFAIL(distributetext);
+	}
+	/* optionally work off the grid (solar cells!) */
+	switch (keyword(ARG(4))) {
+	case K_Gridless:
+		gridless = 1;
+		break;
+	case K_none:
+		gridless = 0;
+		break;
+	default:
+		AFAIL(distributetext);
+	}
+	SaveUndoSerialNumber();
+	/* build list of texts in orthogonal axis order */
+	sort_texts_by_pos(K_distributetext, dir, point);
+	/* find the endpoints given the above options */
+	s = reference_coord(K_distributetext, x, y, dir, point, refa);
+	e = reference_coord(K_distributetext, x, y, dir, point, refb);
+	slack = e - s;
+	/* use this divisor to calculate spacing (for 1 elt, avoid 1/0) */
+	divisor = (ntexts_by_pos > 1) ? (ntexts_by_pos - 1) : 1;
+	/* even the gaps instead of the edges or whatnot */
+	/* find the "slack" in the row */
+	if (point == K_Gaps) {
+		Coord w;
+
+		/* subtract all the "widths" from the slack */
+		for (i = 0; i < ntexts_by_pos; ++i) {
+			TextType *text = texts_by_pos[i].text;
+
+			/* coord doesn't care if I mix Lefts/Tops */
+			w = texts_by_pos[i].width = coord(text, dir, K_Rights) - coord(text, dir, K_Lefts);
+			/* Gaps distribution is on centers, so half of
+			 * first and last text don't count */
+			if (i == 0 || i == ntexts_by_pos - 1) {
+				w /= 2;
+			}
+			slack -= w;
+		}
+		/* slack could be negative */
+	}
+	/* move all selected texts to the new coordinate */
+	for (i = 0; i < ntexts_by_pos; ++i) {
+		TextType *text = texts_by_pos[i].text;
+		int type = texts_by_pos[i].type;
+		Coord p, q, dp, dx, dy;
+
+		/* find reference point for this text */
+		q = s + slack * i / divisor;
+		/* find delta from reference point to reference point */
+		p = coord(text, dir, point);
+		dp = q - p;
+		/* ...but if we're gridful, keep the mark on the grid */
+		/* TODO re-enable grid
+		   if (! gridless)
+		   {
+		   dp -= (coord (text, dir, K_Marks) + dp) % (long) (PCB->Grid);
+		   }
+		 */
+		if (dp) {
+			/* move from generic to X or Y */
+			dx = dy = dp;
+			if (dir == K_X)
+				dy = 0;
+			else
+				dx = 0;
+			/* need to know if the text is part of an element,
+			 * all are PCB_TYPE_TEXT, but text associated with an
+			 * element is also PCB_TYPE_ELEMENT_NAME.  For undo, this is
+			 * significant in search.c: SearchObjectByID.
+			 *
+			 * MoveObject() is better as in aligntext(), but we
+			 * didn't keep the element reference when sorting.
+			 */
+			MOVE_TEXT_LOWLEVEL(text, dx, dy);
+			AddObjectToMoveUndoList(type, NULL, NULL, text, dx, dy);
+			changed = 1;
+		}
+		/* in gaps mode, accumulate part widths */
+		if (point == K_Gaps) {
+			/* move remaining half of our text */
+			s += texts_by_pos[i].width / 2;
+			/* move half of next text */
+			if (i < ntexts_by_pos - 1)
+				s += texts_by_pos[i + 1].width / 2;
+		}
+	}
+	if (changed) {
+		RestoreUndoSerialNumber();
+		IncrementUndoSerialNumber();
+		Redraw();
+		SetChangedFlag(pcb_true);
+	}
+	free_texts_by_pos();
+	return 0;
+}
+
+static HID_Action distaligntext_action_list[] = {
+	{"distributetext", NULL, distributetext, "Distribute Text Elements", distributetext_syntax},
+	{"aligntext", NULL, aligntext, "Align Text Elements", aligntext_syntax}
+};
+
+char *distaligntext_cookie = "distaligntext plugin";
+
+REGISTER_ACTIONS(distaligntext_action_list, distaligntext_cookie)
+
+static void hid_distaligntext_uninit(void)
+{
+	hid_remove_actions_by_cookie(distaligntext_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_distaligntext_init()
+{
+	REGISTER_ACTIONS(distaligntext_action_list, distaligntext_cookie);
+	return hid_distaligntext_uninit;
+}
diff --git a/src_plugins/djopt/Makefile b/src_plugins/djopt/Makefile
new file mode 100644
index 0000000..5751c57
--- /dev/null
+++ b/src_plugins/djopt/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_djopt
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/djopt/Plug.tmpasm b/src_plugins/djopt/Plug.tmpasm
new file mode 100644
index 0000000..553645b
--- /dev/null
+++ b/src_plugins/djopt/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {djopt}
+append /local/pcb/mod/OBJS [@ $(PLUGDIR)/djopt/djopt.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/djopt/djopt_conf.h}
+
+switch /local/pcb/djopt/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/djopt/README b/src_plugins/djopt/README
new file mode 100644
index 0000000..3a092d0
--- /dev/null
+++ b/src_plugins/djopt/README
@@ -0,0 +1,5 @@
+Various board optimization algorithms.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/djopt/djopt.c b/src_plugins/djopt/djopt.c
new file mode 100644
index 0000000..a8a0fa6
--- /dev/null
+++ b/src_plugins/djopt/djopt.c
@@ -0,0 +1,2702 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2003 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+
+#include <memory.h>
+#include <limits.h>
+
+
+#include "data.h"
+#include "create.h"
+#include "remove.h"
+#include "move.h"
+#include "draw.h"
+#include "undo.h"
+#include "strflags.h"
+#include "find.h"
+#include "layer.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_flags.h"
+#include "hid_actions.h"
+#include "djopt_conf.h"
+
+conf_djopt_t conf_djopt;
+
+static const char *djopt_cookie = "djopt";
+
+#ifndef HAVE_RINT
+#define rint(x)  (ceil((x) - 0.5))
+#endif
+
+#define dprintf if(0)pcb_printf
+
+#define selected(x) TEST_FLAG (PCB_FLAG_SELECTED, (x))
+#define autorouted(x) TEST_FLAG (PCB_FLAG_AUTO, (x))
+
+#define SB (PCB->Bloat+1)
+
+/* must be 2^N-1 */
+#define INC 7
+
+#define O_HORIZ		0x10
+#define O_VERT		0x20
+#define LEFT		0x11
+#define RIGHT		0x12
+#define UP		0x24
+#define DOWN		0x28
+#define DIAGONAL	0xf0
+#define ORIENT(x) ((x) & 0xf0)
+#define DIRECT(x) ((x) & 0x0f)
+
+/* Manhattan length of the longest "freckle" */
+#define LONGEST_FRECKLE	2
+
+struct line_s;
+
+typedef struct corner_s {
+	int layer;
+	struct corner_s *next;
+	int x, y;
+	int net;
+	PinType *via;
+	PadType *pad;
+	PinType *pin;
+	int miter;
+	int n_lines;
+	struct line_s **lines;
+} corner_s;
+
+typedef struct line_s {
+	int layer;
+	struct line_s *next;
+	corner_s *s, *e;
+	LineType *line;
+	char is_pad;
+} line_s;
+
+typedef struct rect_s {
+	int x1, y1, x2, y2;
+} rect_s;
+
+#define DELETE(q) (q)->layer = 0xdeadbeef
+#define DELETED(q) ((q)->layer == 0xdeadbeef)
+
+static corner_s *corners, *next_corner = 0;
+static line_s *lines;
+
+static int layer_groupings[MAX_LAYER];
+static char layer_type[MAX_LAYER];
+#define LT_COMPONENT 1
+#define LT_SOLDER 2
+
+static const char *element_name_for(corner_s * c)
+{
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (pin == c->pin)
+				return element->Name[1].TextString;
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (pad == c->pad)
+				return element->Name[1].TextString;
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	return "unknown";
+}
+
+static const char *corner_name(corner_s * c)
+{
+	static char buf[4][100];
+	static int bn = 0;
+	char *bp;
+	bn = (bn + 1) % 4;
+
+	if (c->net == 0xf1eef1ee) {
+		sprintf(buf[bn], "\033[31m[%p freed corner]\033[0m", (void *) c);
+		return buf[bn];
+	}
+
+	sprintf(buf[bn], "\033[%dm[%p ", (c->pin || c->pad || c->via) ? 33 : 34, (void *) c);
+	bp = buf[bn] + strlen(buf[bn]);
+
+	if (c->pin)
+		pcb_sprintf(bp, "pin %s:%s at %#mD", element_name_for(c), c->pin->Number, c->x, c->y);
+	else if (c->via)
+		pcb_sprintf(bp, "via at %#mD", c->x, c->y);
+	else if (c->pad) {
+		pcb_sprintf(bp, "pad %s:%s at %#mD %#mD-%#mD",
+								element_name_for(c), c->pad->Number, c->x, c->y,
+								c->pad->Point1.X, c->pad->Point1.Y, c->pad->Point2.X, c->pad->Point2.Y);
+	}
+	else
+		pcb_sprintf(bp, "at %#mD", c->x, c->y);
+	sprintf(bp + strlen(bp), " n%d l%d]\033[0m", c->n_lines, c->layer);
+	return buf[bn];
+}
+
+static int solder_layer, component_layer;
+
+static void dj_abort(const char *msg, ...)
+{
+	va_list a;
+	va_start(a, msg);
+	vprintf(msg, a);
+	va_end(a);
+	fflush(stdout);
+	abort();
+}
+
+#if 1
+#define check(c,l)
+#else
+#define check(c,l) check2(__LINE__,c,l)
+static void check2(int srcline, corner_s * c, line_s * l)
+{
+	int saw_c = 0, saw_l = 0;
+	corner_s *cc;
+	line_s *ll;
+	int i;
+
+	for (cc = corners; cc; cc = cc->next) {
+		if (DELETED(cc))
+			continue;
+		if (cc == c)
+			saw_c = 1;
+		for (i = 0; i < cc->n_lines; i++)
+			if (cc->lines[i]->s != cc && cc->lines[i]->e != cc)
+				dj_abort("check:%d: cc has line without backref\n", srcline);
+		if (cc->via && (cc->x != cc->via->X || cc->y != cc->via->Y))
+			dj_abort("check:%d: via not at corner\n", srcline);
+		if (cc->pin && (cc->x != cc->pin->X || cc->y != cc->pin->Y))
+			dj_abort("check:%d: pin not at corner\n", srcline);
+	}
+	if (c && !saw_c)
+		dj_abort("check:%d: corner not in corners list\n", srcline);
+	for (ll = lines; ll; ll = ll->next) {
+		if (DELETED(ll))
+			continue;
+		if (ll == l)
+			saw_l = 1;
+		for (i = 0; i < ll->s->n_lines; i++)
+			if (ll->s->lines[i] == ll)
+				break;
+		if (i == ll->s->n_lines)
+			dj_abort("check:%d: ll->s has no backref\n", srcline);
+		for (i = 0; i < ll->e->n_lines; i++)
+			if (ll->e->lines[i] == ll)
+				break;
+		if (i == ll->e->n_lines)
+			dj_abort("check:%d: ll->e has no backref\n", srcline);
+		if (!ll->is_pad
+				&& (ll->s->x != ll->line->Point1.X
+						|| ll->s->y != ll->line->Point1.Y || ll->e->x != ll->line->Point2.X || ll->e->y != ll->line->Point2.Y)) {
+			pcb_printf("line: %#mD to %#mD  pcbline: %#mD to %#mD\n",
+								 ll->s->x, ll->s->y,
+								 ll->e->x, ll->e->y, ll->line->Point1.X, ll->line->Point1.Y, ll->line->Point2.X, ll->line->Point2.Y);
+			dj_abort("check:%d: line doesn't match pcbline\n", srcline);
+		}
+	}
+	if (l && !saw_l)
+		dj_abort("check:%d: line not in lines list\n", srcline);
+}
+
+#endif
+
+#define SWAP(a,b) { a^=b; b^=a; a^=b; }
+
+static int gridsnap(Coord n)
+{
+	if (n <= 0)
+		return 0;
+	return n - n % (conf_core.editor.grid);
+}
+
+/* Avoid commonly used names. */
+
+static int djabs(int x)
+{
+	return x > 0 ? x : -x;
+}
+
+static int djmax(int x, int y)
+{
+	return x > y ? x : y;
+}
+
+static int djmin(int x, int y)
+{
+	return x < y ? x : y;
+}
+
+/*
+ * Find distance between 2 points.  We use floating point math here
+ * because we can fairly easily overflow a 32 bit integer here.  In
+ * fact it only takes 0.46" to do so.
+ */
+static int dist(int x1, int y1, int x2, int y2)
+{
+	double dx1, dy1, dx2, dy2, d;
+
+	dx1 = (double) x1;
+	dy1 = (double) y1;
+	dx2 = (double) x2;
+	dy2 = (double) y2;
+
+	d = sqrt((dx1 - dx2) * (dx1 - dx2) + (dy1 - dy2) * (dy1 - dy2));
+	d = rint(d);
+
+	return (int) d;
+}
+
+static int line_length(line_s * l)
+{
+	if (l->s->x == l->e->x)
+		return djabs(l->s->y - l->e->y);
+	if (l->s->y == l->e->y)
+		return djabs(l->s->x - l->e->x);
+	return dist(l->s->x, l->s->y, l->e->x, l->e->y);
+}
+
+static int dist_ltp2(int dx, int y, int y1, int y2)
+{
+	if (y1 > y2)
+		SWAP(y1, y2);
+	if (y < y1)
+		return dist(dx, y, 0, y1);
+	if (y > y2)
+		return dist(dx, y, 0, y2);
+	return djabs(dx);
+}
+
+int sqr(int a)
+{
+	return a * a;
+}
+
+static int intersecting_layers(int l1, int l2)
+{
+	if (l1 == -1 || l2 == -1)
+		return 1;
+	if (l1 == l2)
+		return 1;
+	if (layer_groupings[l1] == layer_groupings[l2])
+		return 1;
+	return 0;
+}
+
+static int dist_line_to_point(line_s * l, corner_s * c)
+{
+	double len, r, d;
+	/* We can do this quickly if l is vertical or horizontal.  */
+	if (l->s->x == l->e->x)
+		return dist_ltp2(l->s->x - c->x, c->y, l->s->y, l->e->y);
+	if (l->s->y == l->e->y)
+		return dist_ltp2(l->s->y - c->y, c->x, l->s->x, l->e->x);
+
+	/* Do it the hard way.  See comments for IsPointOnLine() in search.c */
+	len = sqrt(sqr(l->s->x - l->e->x) + sqr(l->s->y - l->e->y));
+	if (len == 0)
+		return dist(l->s->x, l->s->y, c->x, c->y);
+	r = (l->s->y - c->y) * (l->s->y - l->e->y) + (l->s->x - c->x) * (l->s->x - l->e->x);
+	r /= len * len;
+	if (r < 0)
+		return dist(l->s->x, l->s->y, c->x, c->y);
+	if (r > 1)
+		return dist(l->e->x, l->e->y, c->x, c->y);
+	d = (l->e->y - l->s->y) * (c->x * l->s->x) + (l->e->x - l->s->x) * (c->y - l->s->y);
+	return (int) (d / len);
+}
+
+static int line_orient(line_s * l, corner_s * c)
+{
+	int x1, y1, x2, y2;
+	if (c == l->s) {
+		x1 = l->s->x;
+		y1 = l->s->y;
+		x2 = l->e->x;
+		y2 = l->e->y;
+	}
+	else {
+		x1 = l->e->x;
+		y1 = l->e->y;
+		x2 = l->s->x;
+		y2 = l->s->y;
+	}
+	if (x1 == x2) {
+		if (y1 < y2)
+			return DOWN;
+		return UP;
+	}
+	else if (y1 == y2) {
+		if (x1 < x2)
+			return RIGHT;
+		return LEFT;
+	}
+	return DIAGONAL;
+}
+
+#if 0
+/* Not used */
+static corner_s *common_corner(line_s * l1, line_s * l2)
+{
+	if (l1->s == l2->s || l1->s == l2->e)
+		return l1->s;
+	if (l1->e == l2->s || l1->e == l2->e)
+		return l1->e;
+	dj_abort("common_corner: no common corner found\n");
+	return NULL;
+}
+#endif
+
+static corner_s *other_corner(line_s * l, corner_s * c)
+{
+	if (l->s == c)
+		return l->e;
+	if (l->e == c)
+		return l->s;
+	dj_abort("other_corner: neither corner passed\n");
+	return NULL;
+}
+
+static corner_s *find_corner_if(int x, int y, int l)
+{
+	corner_s *c;
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		if (c->x != x || c->y != y)
+			continue;
+		if (!(c->layer == -1 || intersecting_layers(c->layer, l)))
+			continue;
+		return c;
+	}
+	return 0;
+}
+
+static corner_s *find_corner(int x, int y, int l)
+{
+	corner_s *c;
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		if (c->x != x || c->y != y)
+			continue;
+		if (!(c->layer == -1 || intersecting_layers(c->layer, l)))
+			continue;
+		return c;
+	}
+	c = (corner_s *) malloc(sizeof(corner_s));
+	c->next = corners;
+	corners = c;
+	c->x = x;
+	c->y = y;
+	c->net = 0;
+	c->via = 0;
+	c->pad = 0;
+	c->pin = 0;
+	c->layer = l;
+	c->n_lines = 0;
+	c->lines = (line_s **) malloc(INC * sizeof(line_s *));
+	return c;
+}
+
+static void add_line_to_corner(line_s * l, corner_s * c)
+{
+	int n;
+	n = (c->n_lines + 1 + INC) & ~INC;
+	c->lines = (line_s **) realloc(c->lines, n * sizeof(line_s *));
+	c->lines[c->n_lines] = l;
+	c->n_lines++;
+	dprintf("add_line_to_corner %#mD\n", c->x, c->y);
+}
+
+static LineType *create_pcb_line(int layer, int x1, int y1, int x2, int y2, int thick, int clear, FlagType flags)
+{
+	char *from, *to;
+	LineType *nl;
+	LayerType *lyr = LAYER_PTR(layer);
+
+	from = (char *) linelist_first(&lyr->Line);
+	nl = CreateNewLineOnLayer(PCB->Data->Layer + layer, x1, y1, x2, y2, thick, clear, flags);
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, lyr, nl, nl);
+
+	to = (char *) linelist_first(&lyr->Line);
+	if (from != to) {
+		line_s *lp;
+		for (lp = lines; lp; lp = lp->next) {
+			if (DELETED(lp))
+				continue;
+			if ((char *) (lp->line) >= from && (char *) (lp->line) <= from + linelist_length(&lyr->Line) * sizeof(LineType))
+				lp->line = (LineType *) ((char *) (lp->line) + (to - from));
+		}
+	}
+	return nl;
+}
+
+static void new_line(corner_s * s, corner_s * e, int layer, LineType * example)
+{
+	line_s *ls;
+
+	if (layer >= max_copper_layer)
+		dj_abort("layer %d\n", layer);
+
+	if (example == NULL)
+		dj_abort("NULL example passed to new_line()\n", layer);
+
+	if (s->x == e->x && s->y == e->y)
+		return;
+
+	ls = (line_s *) malloc(sizeof(line_s));
+	ls->next = lines;
+	lines = ls;
+	ls->is_pad = 0;
+	ls->s = s;
+	ls->e = e;
+	ls->layer = layer;
+#if 0
+	if ((example->Point1.X == s->x && example->Point1.Y == s->y && example->Point2.X == e->x && example->Point2.Y == e->y)
+			|| (example->Point2.X == s->x && example->Point2.Y == s->y && example->Point1.X == e->x && example->Point1.Y == e->y)) {
+		ls->line = example;
+	}
+	else
+#endif
+	{
+		LineType *nl;
+		dprintf
+			("New line \033[35m%#mD to %#mD from l%d t%#mS c%#mS f%s\033[0m\n",
+			 s->x, s->y, e->x, e->y, layer, example->Thickness, example->Clearance, flags_to_string(example->Flags, PCB_TYPE_LINE));
+		nl = create_pcb_line(layer, s->x, s->y, e->x, e->y, example->Thickness, example->Clearance, example->Flags);
+
+		if (!nl)
+			dj_abort("can't create new line!");
+		ls->line = nl;
+	}
+	add_line_to_corner(ls, s);
+	add_line_to_corner(ls, e);
+	check(s, ls);
+	check(e, ls);
+}
+
+#if 0
+/* Not used */
+static int c_orth_to(corner_s * c, line_s * l, int o)
+{
+	int i, o2;
+	int rv = 0;
+	for (i = 0; i < c->n_lines; i++) {
+		if (c->lines[i] == l)
+			continue;
+		o2 = line_orient(c->lines[i], c);
+		if (ORIENT(o) == ORIENT(o2) || o2 == DIAGONAL)
+			return 0;
+		rv++;
+	}
+	return rv;
+}
+#endif
+
+static line_s *other_line(corner_s * c, line_s * l)
+{
+	int i;
+	line_s *rv = 0;
+	if (c->pin || c->pad || c->via)
+		return 0;
+	for (i = 0; i < c->n_lines; i++) {
+		if (c->lines[i] == l)
+			continue;
+		if (rv)
+			return 0;
+		rv = c->lines[i];
+	}
+	return rv;
+}
+
+static void empty_rect(rect_s * rect)
+{
+	rect->x1 = rect->y1 = INT_MAX;
+	rect->x2 = rect->y2 = INT_MIN;
+}
+
+static void add_point_to_rect(rect_s * rect, int x, int y, int w)
+{
+	if (rect->x1 > x - w)
+		rect->x1 = x - w;
+	if (rect->x2 < x + w)
+		rect->x2 = x + w;
+	if (rect->y1 > y - w)
+		rect->y1 = y - w;
+	if (rect->y2 < y + w)
+		rect->y2 = y + w;
+}
+
+static void add_line_to_rect(rect_s * rect, line_s * l)
+{
+	add_point_to_rect(rect, l->s->x, l->s->y, 0);
+	add_point_to_rect(rect, l->e->x, l->e->y, 0);
+}
+
+static int pin_in_rect(rect_s * r, int x, int y, int w)
+{
+	if (x < r->x1 && x + w < r->x1)
+		return 0;
+	if (x > r->x2 && x - w > r->x2)
+		return 0;
+	if (y < r->y1 && y + w < r->y1)
+		return 0;
+	if (y > r->y2 && y - w > r->y2)
+		return 0;
+	return 1;
+}
+
+static int line_in_rect(rect_s * r, line_s * l)
+{
+	rect_s lr;
+	empty_rect(&lr);
+	add_point_to_rect(&lr, l->s->x, l->s->y, l->line->Thickness / 2);
+	add_point_to_rect(&lr, l->e->x, l->e->y, l->line->Thickness / 2);
+	dprintf("line_in_rect %#mD-%#mD vs %#mD-%#mD\n", r->x1, r->y1, r->x2, r->y2, lr.x1, lr.y1, lr.x2, lr.y2);
+	/* simple intersection of rectangles */
+	if (lr.x1 < r->x1)
+		lr.x1 = r->x1;
+	if (lr.x2 > r->x2)
+		lr.x2 = r->x2;
+	if (lr.y1 < r->y1)
+		lr.y1 = r->y1;
+	if (lr.y2 > r->y2)
+		lr.y2 = r->y2;
+	if (lr.x1 < lr.x2 && lr.y1 < lr.y2)
+		return 1;
+	return 0;
+}
+
+static int corner_radius(corner_s * c)
+{
+	int diam = 0;
+	int i;
+	if (c->pin)
+		diam = djmax(c->pin->Thickness, diam);
+	if (c->via)
+		diam = djmax(c->via->Thickness, diam);
+	for (i = 0; i < c->n_lines; i++)
+		if (c->lines[i]->line)
+			diam = djmax(c->lines[i]->line->Thickness, diam);
+	diam = (diam + 1) / 2;
+	return diam;
+}
+
+#if 0
+/* Not used */
+static int corner_layer(corner_s * c)
+{
+	if (c->pin || c->via)
+		return -1;
+	if (c->n_lines < 1)
+		return -1;
+	return c->lines[0]->layer;
+}
+#endif
+
+static void add_corner_to_rect_if(rect_s * rect, corner_s * c, rect_s * e)
+{
+	int diam = corner_radius(c);
+	if (!pin_in_rect(e, c->x, c->y, diam))
+		return;
+	if (c->x < e->x1 && c->y < e->y1 && dist(c->x, c->y, e->x1, e->y1) > diam)
+		return;
+	if (c->x > e->x2 && c->y < e->y1 && dist(c->x, c->y, e->x2, e->y1) > diam)
+		return;
+	if (c->x < e->x1 && c->y > e->y2 && dist(c->x, c->y, e->x1, e->y2) > diam)
+		return;
+	if (c->x > e->x2 && c->y > e->y2 && dist(c->x, c->y, e->x2, e->y2) > diam)
+		return;
+
+	/*pcb_printf("add point %#mD diam %#mS\n", c->x, c->y, diam); */
+	add_point_to_rect(rect, c->x, c->y, diam);
+}
+
+static void remove_line(line_s * l)
+{
+	int i, j;
+	LayerType *layer = &(PCB->Data->Layer[l->layer]);
+
+	check(0, 0);
+
+	if (l->line)
+		RemoveLine(layer, l->line);
+
+	DELETE(l);
+
+	for (i = 0, j = 0; i < l->s->n_lines; i++)
+		if (l->s->lines[i] != l)
+			l->s->lines[j++] = l->s->lines[i];
+	l->s->n_lines = j;
+
+	for (i = 0, j = 0; i < l->e->n_lines; i++)
+		if (l->e->lines[i] != l)
+			l->e->lines[j++] = l->e->lines[i];
+	l->e->n_lines = j;
+	check(0, 0);
+}
+
+static void move_line_to_layer(line_s * l, int layer)
+{
+	LayerType *ls, *ld;
+
+	ls = LAYER_PTR(l->layer);
+	ld = LAYER_PTR(layer);
+
+	MoveObjectToLayer(PCB_TYPE_LINE, ls, l->line, 0, ld, 0);
+	l->layer = layer;
+}
+
+static void remove_via_at(corner_s * c)
+{
+	RemoveObject(PCB_TYPE_VIA, c->via, 0, 0);
+	c->via = 0;
+}
+
+static void remove_corner(corner_s * c2)
+{
+	corner_s *c;
+	dprintf("remove corner %s\n", corner_name(c2));
+	if (corners == c2)
+		corners = c2->next;
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		if (c->next == c2)
+			c->next = c2->next;
+	}
+	if (next_corner == c2)
+		next_corner = c2->next;
+	free(c2->lines);
+	c2->lines = 0;
+	DELETE(c2);
+}
+
+static void merge_corners(corner_s * c1, corner_s * c2)
+{
+	int i;
+	if (c1 == c2)
+		abort();
+	dprintf("merge corners %s %s\n", corner_name(c1), corner_name(c2));
+	for (i = 0; i < c2->n_lines; i++) {
+		add_line_to_corner(c2->lines[i], c1);
+		if (c2->lines[i]->s == c2)
+			c2->lines[i]->s = c1;
+		if (c2->lines[i]->e == c2)
+			c2->lines[i]->e = c1;
+	}
+	if (c1->via && c2->via)
+		remove_via_at(c2);
+	else if (c2->via)
+		c1->via = c2->via;
+	if (c2->pad)
+		c1->pad = c2->pad;
+	if (c2->pin)
+		c1->pin = c2->pin;
+	if (c2->layer != c1->layer)
+		c1->layer = -1;
+
+	remove_corner(c2);
+}
+
+static void move_corner(corner_s * c, int x, int y)
+{
+	PinType *via;
+	int i;
+	corner_s *pad;
+
+	check(c, 0);
+	if (c->pad || c->pin)
+		dj_abort("move_corner: has pin or pad\n");
+	dprintf("move_corner %p from %#mD to %#mD\n", (void *) c, c->x, c->y, x, y);
+	pad = find_corner_if(x, y, c->layer);
+	c->x = x;
+	c->y = y;
+	via = c->via;
+	if (via) {
+		MoveObject(PCB_TYPE_VIA, via, via, via, x - via->X, y - via->Y);
+		dprintf("via move %#mD to %#mD\n", via->X, via->Y, x, y);
+	}
+	for (i = 0; i < c->n_lines; i++) {
+		LineTypePtr tl = c->lines[i]->line;
+		if (tl) {
+			if (c->lines[i]->s == c) {
+				MoveObject(PCB_TYPE_LINE_POINT, LAYER_PTR(c->lines[i]->layer), tl, &tl->Point1, x - (tl->Point1.X), y - (tl->Point1.Y));
+			}
+			else {
+				MoveObject(PCB_TYPE_LINE_POINT, LAYER_PTR(c->lines[i]->layer), tl, &tl->Point2, x - (tl->Point2.X), y - (tl->Point2.Y));
+			}
+			dprintf("Line %p moved to %#mD %#mD\n", (void *) tl, tl->Point1.X, tl->Point1.Y, tl->Point2.X, tl->Point2.Y);
+		}
+	}
+	if (pad && pad != c)
+		merge_corners(c, pad);
+	else
+		for (i = 0; i < c->n_lines; i++) {
+			if (c->lines[i]->s->x == c->lines[i]->e->x && c->lines[i]->s->y == c->lines[i]->e->y) {
+				corner_s *c2 = other_corner(c->lines[i], c);
+				dprintf("move_corner: removing line %#mD %#mD %p %p\n", c->x, c->y, c2->x, c2->y, (void *) c, (void *) c2);
+
+				remove_line(c->lines[i]);
+				if (c != c2)
+					merge_corners(c, c2);
+				check(c, 0);
+				i--;
+				break;
+			}
+		}
+	gui->progress(0, 0, 0);
+	check(c, 0);
+}
+
+static int any_line_selected()
+{
+	line_s *l;
+	for (l = lines; l; l = l->next) {
+		if (DELETED(l))
+			continue;
+		if (l->line && selected(l->line))
+			return 1;
+	}
+	return 0;
+}
+
+static int trim_step(int s, int l1, int l2)
+{
+	dprintf("trim %d %d %d\n", s, l1, l2);
+	if (s > l1)
+		s = l1;
+	if (s > l2)
+		s = l2;
+	if (s != l1 && s != l2)
+		s = gridsnap(s);
+	return s;
+}
+
+static int canonicalize_line(line_s * l);
+
+static int split_line(line_s * l, corner_s * c)
+{
+	int i;
+	LineType *pcbline;
+	line_s *ls;
+
+	if (!intersecting_layers(l->layer, c->layer))
+		return 0;
+	if (l->is_pad)
+		return 0;
+	if (c->pad) {
+		dprintf("split on pad!\n");
+		if (l->s->pad == c->pad || l->e->pad == c->pad)
+			return 0;
+		dprintf("splitting...\n");
+	}
+
+	check(c, l);
+	pcbline = create_pcb_line(l->layer, c->x, c->y, l->e->x, l->e->y, l->line->Thickness, l->line->Clearance, l->line->Flags);
+	if (pcbline == 0)
+		return 0;										/* already a line there */
+
+	check(c, l);
+
+	dprintf("split line from %#mD to %#mD at %#mD\n", l->s->x, l->s->y, l->e->x, l->e->y, c->x, c->y);
+	ls = (line_s *) malloc(sizeof(line_s));
+
+	ls->next = lines;
+	lines = ls;
+	ls->is_pad = 0;
+	ls->s = c;
+	ls->e = l->e;
+	ls->line = pcbline;
+	ls->layer = l->layer;
+	for (i = 0; i < l->e->n_lines; i++)
+		if (l->e->lines[i] == l)
+			l->e->lines[i] = ls;
+	l->e = c;
+	add_line_to_corner(l, c);
+	add_line_to_corner(ls, c);
+
+	MoveObject(PCB_TYPE_LINE_POINT, LAYER_PTR(l->layer), l->line, &l->line->Point2,
+						 c->x - (l->line->Point2.X), c->y - (l->line->Point2.Y));
+
+	return 1;
+}
+
+static int canonicalize_line(line_s * l)
+{
+	/* This could be faster */
+	corner_s *c;
+	if (l->s->x == l->e->x) {
+		int y1 = l->s->y;
+		int y2 = l->e->y;
+		int x1 = l->s->x - l->line->Thickness / 2;
+		int x2 = l->s->x + l->line->Thickness / 2;
+		if (y1 > y2) {
+			int t = y1;
+			y1 = y2;
+			y2 = t;
+		}
+		for (c = corners; c; c = c->next) {
+			if (DELETED(c))
+				continue;
+			if ((y1 < c->y && c->y < y2)
+					&& intersecting_layers(l->layer, c->layer)) {
+				if (c->x != l->s->x && c->x < x2 && c->x > x1 && !(c->pad || c->pin)) {
+					move_corner(c, l->s->x, c->y);
+				}
+				if (c->x == l->s->x) {
+					/* FIXME: if the line is split, we have to re-canonicalize
+					   both segments. */
+					return split_line(l, c);
+				}
+			}
+		}
+	}
+	else if (l->s->y == l->e->y) {
+		int x1 = l->s->x;
+		int x2 = l->e->x;
+		int y1 = l->s->y - l->line->Thickness / 2;
+		int y2 = l->s->y + l->line->Thickness / 2;
+		if (x1 > x2) {
+			int t = x1;
+			x1 = x2;
+			x2 = t;
+		}
+		for (c = corners; c; c = c->next) {
+			if (DELETED(c))
+				continue;
+			if ((x1 < c->x && c->x < x2)
+					&& intersecting_layers(l->layer, c->layer)) {
+				if (c->y != l->s->y && c->y < y2 && c->y > y1 && !(c->pad || c->pin)) {
+					move_corner(c, c->x, l->s->y);
+				}
+				if (c->y == l->s->y) {
+					/* FIXME: Likewise.  */
+					return split_line(l, c);
+				}
+			}
+		}
+	}
+	else {
+		/* diagonal lines.  Let's try to split them at pins/vias
+		   anyway.  */
+		int x1 = l->s->x;
+		int x2 = l->e->x;
+		int y1 = l->s->y;
+		int y2 = l->e->y;
+		if (x1 > x2) {
+			int t = x1;
+			x1 = x2;
+			x2 = t;
+		}
+		if (y1 > y2) {
+			int t = y1;
+			y1 = y2;
+			y2 = t;
+		}
+		for (c = corners; c; c = c->next) {
+			if (DELETED(c))
+				continue;
+			if (!c->via && !c->pin)
+				continue;
+			if ((x1 < c->x && c->x < x2)
+					&& (y1 < c->y && c->y < y2)
+					&& intersecting_layers(l->layer, c->layer)) {
+				int th = c->pin ? c->pin->Thickness : c->via->Thickness;
+				th /= 2;
+				if (dist(l->s->x, l->s->y, c->x, c->y) > th
+						&& dist(l->e->x, l->e->y, c->x, c->y) > th && PinLineIntersect(c->pin ? c->pin : c->via, l->line)) {
+					return split_line(l, c);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* Make sure all vias are at line end points */
+static int canonicalize_lines()
+{
+	int changes = 0;
+	int count;
+	line_s *l;
+	while (1) {
+		count = 0;
+		for (l = lines; l; l = l->next) {
+			if (DELETED(l))
+				continue;
+			count += canonicalize_line(l);
+		}
+		changes += count;
+		if (count == 0)
+			break;
+	}
+	return changes;
+}
+
+static int simple_optimize_corner(corner_s * c)
+{
+	int i;
+	int rv = 0;
+
+	check(c, 0);
+	if (c->via) {
+		/* see if no via is needed */
+		if (selected(c->via))
+			dprintf("via check: line[0] layer %d at %#mD nl %d\n", c->lines[0]->layer, c->x, c->y, c->n_lines);
+		/* We can't delete vias that connect to power planes, or vias
+		   that aren't tented (assume they're test points).  */
+		if (!TEST_ANY_THERMS(c->via)
+				&& c->via->Mask == 0) {
+			for (i = 1; i < c->n_lines; i++) {
+				if (selected(c->via))
+					dprintf("           line[%d] layer %d %#mD to %#mD\n",
+									i, c->lines[i]->layer, c->lines[i]->s->x, c->lines[i]->s->y, c->lines[i]->e->x, c->lines[i]->e->y);
+				if (c->lines[i]->layer != c->lines[0]->layer)
+					break;
+			}
+			if (i == c->n_lines) {
+				if (selected(c->via))
+					dprintf("           remove it\n");
+				remove_via_at(c);
+				rv++;
+			}
+		}
+	}
+
+	check(c, 0);
+	if (c->n_lines == 2 && !c->via) {
+		/* see if it is an unneeded corner */
+		int o = line_orient(c->lines[0], c);
+		corner_s *c2 = other_corner(c->lines[1], c);
+		corner_s *c0 = other_corner(c->lines[0], c);
+		if (o == line_orient(c->lines[1], c2) && o != DIAGONAL) {
+			dprintf("straight %#mD to %#mD to %#mD\n", c0->x, c0->y, c->x, c->y, c2->x, c2->y);
+			if (selected(c->lines[0]->line))
+				SET_FLAG(PCB_FLAG_SELECTED, c->lines[1]->line);
+			if (selected(c->lines[1]->line))
+				SET_FLAG(PCB_FLAG_SELECTED, c->lines[0]->line);
+			move_corner(c, c2->x, c2->y);
+		}
+	}
+	check(c, 0);
+	if (c->n_lines == 1 && !c->via) {
+		corner_s *c0 = other_corner(c->lines[0], c);
+		if (abs(c->x - c0->x) + abs(c->y - c0->y) <= LONGEST_FRECKLE) {
+			/*
+			 * Remove this line, as it is a "freckle".  A freckle is an extremely
+			 * short line (around 0.01 thou) that is unconnected at one end.
+			 * Freckles are almost insignificantly small, but are annoying as
+			 * they prevent the mitering optimiser from working.
+			 * Freckles sometimes arise because of a bug in the autorouter that
+			 * causes it to create small overshoots (typically 0.01 thou) at the
+			 * intersections of vertical and horizontal lines. These overshoots
+			 * are converted to freckles as a side effect of canonicalize_line().
+			 * Note that canonicalize_line() is not at fault, the bug is in the
+			 * autorouter creating overshoots.
+			 * The autorouter bug arose some time between the 20080202 and 20091103
+			 * releases.
+			 * This code is probably worth keeping even when the autorouter bug is
+			 * fixed, as "freckles" could conceivably arise in other ways.
+			 */
+			dprintf("freckle %#mD to %#mD\n", c->x, c->y, c0->x, c0->y);
+			move_corner(c, c0->x, c0->y);
+		}
+	}
+	check(c, 0);
+	return rv;
+}
+
+/* We always run these */
+static int simple_optimizations()
+{
+	corner_s *c;
+	int rv = 0;
+
+	/* Look for corners that aren't */
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		if (c->pad || c->pin)
+			continue;
+		rv += simple_optimize_corner(c);
+	}
+	return rv;
+}
+
+static int is_hole(corner_s * c)
+{
+	return c->pin || c->pad || c->via;
+}
+
+static int orthopull_1(corner_s * c, int fdir, int rdir, int any_sel)
+{
+	static corner_s **cs = 0;
+	static int cm = 0;
+	static line_s **ls = 0;
+	static int lm = 0;
+	int i, li, ln, cn, snap;
+	line_s *l = 0;
+	corner_s *c2, *cb;
+	int adir = 0, sdir = 0, pull;
+	int saw_sel = 0, saw_auto = 0;
+	int max, len = 0, r1 = 0, r2;
+	rect_s rr;
+	int edir = 0, done;
+
+	if (cs == 0) {
+		cs = (corner_s **) malloc(10 * sizeof(corner_s));
+		cm = 10;
+		ls = (line_s **) malloc(10 * sizeof(line_s));
+		lm = 10;
+	}
+
+	for (i = 0; i < c->n_lines; i++) {
+		int o = line_orient(c->lines[i], c);
+		if (o == rdir)
+			return 0;
+	}
+
+	switch (fdir) {
+	case RIGHT:
+		adir = DOWN;
+		sdir = UP;
+		break;
+	case DOWN:
+		adir = RIGHT;
+		sdir = LEFT;
+		break;
+	default:
+		dj_abort("fdir not right or down\n");
+	}
+
+	c2 = c;
+	cn = 0;
+	ln = 0;
+	pull = 0;
+	while (c2) {
+		if (c2->pad || c2->pin || c2->n_lines < 2)
+			return 0;
+		if (cn >= cm) {
+			cm = cn + 10;
+			cs = (corner_s **) realloc(cs, cm * sizeof(corner_s));
+		}
+		cs[cn++] = c2;
+		r2 = corner_radius(c2);
+		if (r1 < r2)
+			r1 = r2;
+		l = 0;
+		for (i = 0; i < c2->n_lines; i++) {
+			int o = line_orient(c2->lines[i], c2);
+			if (o == DIAGONAL)
+				return 0;
+			if (o == fdir) {
+				if (l)
+					return 0;							/* we don't support overlapping lines yet */
+				l = c2->lines[i];
+			}
+			if (o == rdir && c2->lines[i] != ls[ln - 1])
+				return 0;								/* likewise */
+			if (o == adir)
+				pull++;
+			if (o == sdir)
+				pull--;
+		}
+		if (!l)
+			break;
+		if (selected(l->line))
+			saw_sel = 1;
+		if (autorouted(l->line))
+			saw_auto = 1;
+		if (ln >= lm) {
+			lm = ln + 10;
+			ls = (line_s **) realloc(ls, lm * sizeof(line_s));
+		}
+		ls[ln++] = l;
+		c2 = other_corner(l, c2);
+	}
+	if (cn < 2 || pull == 0)
+		return 0;
+	if (any_sel && !saw_sel)
+		return 0;
+	if (!any_sel && conf_djopt.plugins.djopt.auto_only && !saw_auto)
+		return 0;
+
+	/* Ok, now look for other blockages. */
+
+	empty_rect(&rr);
+	add_point_to_rect(&rr, c->x, c->y, corner_radius(c));
+	add_point_to_rect(&rr, c2->x, c2->y, corner_radius(c2));
+
+	if (fdir == RIGHT && pull < 0)
+		edir = UP;
+	else if (fdir == RIGHT && pull > 0)
+		edir = DOWN;
+	else if (fdir == DOWN && pull < 0)
+		edir = LEFT;
+	else if (fdir == DOWN && pull > 0)
+		edir = RIGHT;
+
+	max = -1;
+	for (i = 0; i < cn; i++)
+		for (li = 0; li < cs[i]->n_lines; li++) {
+			if (line_orient(cs[i]->lines[li], cs[i]) != edir)
+				continue;
+			len = line_length(cs[i]->lines[li]);
+			if (max > len || max == -1)
+				max = len;
+		}
+	dprintf("c %s %4#mD  cn %d pull %3d  max %4#mS\n", fdir == RIGHT ? "right" : "down ", c->x, c->y, cn, pull, max);
+
+	switch (edir) {
+	case UP:
+		rr.y1 = c->y - r1 - max;
+		break;
+	case DOWN:
+		rr.y2 = c->y + r1 + max;
+		break;
+	case LEFT:
+		rr.x1 = c->x - r1 - max;
+		break;
+	case RIGHT:
+		rr.x2 = c->x + r1 + max;
+		break;
+	}
+	rr.x1 -= SB + 1;
+	rr.x2 += SB + 1;
+	rr.y1 -= SB + 1;
+	rr.y2 += SB + 1;
+
+	snap = 0;
+	for (cb = corners; cb; cb = cb->next) {
+		int sep;
+		if (DELETED(cb))
+			continue;
+		r1 = corner_radius(cb);
+		if (cb->net == c->net && !cb->pad)
+			continue;
+		if (!pin_in_rect(&rr, cb->x, cb->y, r1))
+			continue;
+		switch (edir) {
+#define ECHK(X,Y,LT) \
+	  for (i=0; i<cn; i++) \
+	    { \
+	      if (!intersecting_layers(cs[i]->layer, cb->layer)) \
+		continue; \
+	      r2 = corner_radius(cs[i]); \
+	      if (cb->X + r1 <= cs[i]->X - r2 - SB - 1) \
+		continue; \
+	      if (cb->X - r1 >= cs[i]->X + r2 + SB + 1) \
+		continue; \
+	      if (cb->Y LT cs[i]->Y) \
+		continue; \
+	      sep = djabs(cb->Y - cs[i]->Y) - r1 - r2 - SB - 1; \
+	      if (max > sep) \
+		{ max = sep; snap = 1; }\
+	    } \
+	  for (i=0; i<ln; i++) \
+	    { \
+	      if (!intersecting_layers(ls[i]->layer, cb->layer)) \
+		continue; \
+	      if (cb->X <= cs[i]->X || cb->X >= cs[i+1]->X) \
+		continue; \
+	      sep = (djabs(cb->Y - cs[i]->Y) - ls[i]->line->Thickness/2 \
+		     - r1 - SB - 1); \
+	      if (max > sep) \
+		{ max = sep; snap = 1; }\
+	    }
+		case UP:
+			ECHK(x, y, >=);
+			break;
+		case DOWN:
+			ECHK(x, y, <=);
+			break;
+		case LEFT:
+			ECHK(y, x, >=);
+			break;
+		case RIGHT:
+			ECHK(y, x, <=);
+			break;
+		}
+	}
+
+	/* We must now check every line segment against our corners.  */
+	for (l = lines; l; l = l->next) {
+		int o, x1, x2, y1, y2;
+		if (DELETED(l))
+			continue;
+		dprintf("check line %#mD to %#mD\n", l->s->x, l->s->y, l->e->x, l->e->y);
+		if (l->s->net == c->net) {
+			dprintf("  same net\n");
+			continue;
+		}
+		o = line_orient(l, 0);
+		/* We don't need to check perpendicular lines, because their
+		   corners already take care of it.  */
+		if ((fdir == RIGHT && (o == UP || o == DOWN))
+				|| (fdir == DOWN && (o == RIGHT || o == LEFT))) {
+			dprintf("  perpendicular\n");
+			continue;
+		}
+
+		/* Choose so that x1,y1 is closest to corner C */
+		if ((fdir == RIGHT && l->s->x < l->e->x)
+				|| (fdir == DOWN && l->s->y < l->e->y)) {
+			x1 = l->s->x;
+			y1 = l->s->y;
+			x2 = l->e->x;
+			y2 = l->e->y;
+		}
+		else {
+			x1 = l->e->x;
+			y1 = l->e->y;
+			x2 = l->s->x;
+			y2 = l->s->y;
+		}
+
+		/* Eliminate all lines outside our range */
+		if ((fdir == RIGHT && (x2 < c->x || x1 > c2->x))
+				|| (fdir == DOWN && (y2 < c->y || y1 > c2->y))) {
+			dprintf("  outside our range\n");
+			continue;
+		}
+
+		/* Eliminate all lines on the wrong side of us */
+		if ((edir == UP && y1 > c->y && y2 > c->y)
+				|| (edir == DOWN && y1 < c->y && y2 < c->y)
+				|| (edir == LEFT && x1 > c->x && x2 > c->x)
+				|| (edir == RIGHT && x1 < c->x && x2 < c->x)) {
+			dprintf("  wrong side\n");
+			continue;
+		}
+
+		/* For now, cheat on diagonals */
+		switch (edir) {
+		case RIGHT:
+			if (x1 > x2)
+				x1 = x2;
+			break;
+		case LEFT:
+			if (x1 < x2)
+				x1 = x2;
+			break;
+		case DOWN:
+			if (y1 > y2)
+				y1 = y2;
+			break;
+		case UP:
+			if (y1 < y2)
+				y1 = y2;
+			break;
+		}
+
+		/* Ok, now see how far we can get for each of our corners. */
+		for (i = 0; i < cn; i++) {
+			int r = l->line->Thickness + SB + corner_radius(cs[i]) + 1;
+			int len = 0;
+			if ((fdir == RIGHT && (x2 < cs[i]->x || x1 > cs[i]->x))
+					|| (fdir == DOWN && (y2 < cs[i]->y || y1 > cs[i]->y)))
+				continue;
+			if (!intersecting_layers(cs[i]->layer, l->layer))
+				continue;
+			switch (edir) {
+			case RIGHT:
+				len = x1 - c->x;
+				break;
+			case LEFT:
+				len = c->x - x1;
+				break;
+			case DOWN:
+				len = y1 - c->y;
+				break;
+			case UP:
+				len = c->y - y1;
+				break;
+			}
+			len -= r;
+			dprintf("  len is %#mS vs corner at %#mD\n", len, cs[i]->x, cs[i]->y);
+			if (len <= 0)
+				return 0;
+			if (max > len)
+				max = len;
+		}
+
+	}
+
+	/* We must make sure that if a segment isn't being completely
+	   removed, that any vias and/or pads don't overlap.  */
+	done = 0;
+	while (!done) {
+		done = 1;
+		for (i = 0; i < cn; i++)
+			for (li = 0; li < cs[i]->n_lines; li++) {
+				line_s *l = cs[i]->lines[li];
+				corner_s *oc = other_corner(l, cs[i]);
+				if (line_orient(l, cs[i]) != edir)
+					continue;
+				len = line_length(l);
+				if (!oc->pad || !cs[i]->via) {
+					if (!is_hole(l->s) || !is_hole(l->e))
+						continue;
+					if (len == max)
+						continue;
+				}
+				len -= corner_radius(l->s);
+				len -= corner_radius(l->e);
+				len -= SB + 1;
+				if (max > len) {
+					max = len;
+					done = 0;
+				}
+			}
+	}
+
+	if (max <= 0)
+		return 0;
+	switch (edir) {
+	case UP:
+		len = c->y - max;
+		break;
+	case DOWN:
+		len = c->y + max;
+		break;
+	case LEFT:
+		len = c->x - max;
+		break;
+	case RIGHT:
+		len = c->x + max;
+		break;
+	}
+	if (snap && max > conf_core.editor.grid) {
+		if (pull < 0)
+			len += conf_core.editor.grid - 1;
+		len = gridsnap(len);
+	}
+	if ((fdir == RIGHT && len == cs[0]->y) || (fdir == DOWN && len == cs[0]->x))
+		return 0;
+	for (i = 0; i < cn; i++) {
+		if (fdir == RIGHT) {
+			max = len - cs[i]->y;
+			move_corner(cs[i], cs[i]->x, len);
+		}
+		else {
+			max = len - cs[i]->x;
+			move_corner(cs[i], len, cs[i]->y);
+		}
+	}
+	return max * pull;
+}
+
+static int orthopull()
+{
+	/* Look for straight runs which could be moved to reduce total trace
+	   length.  */
+	int any_sel = any_line_selected();
+	corner_s *c;
+	int rv = 0;
+
+	for (c = corners; c;) {
+		if (DELETED(c))
+			continue;
+		if (c->pin || c->pad) {
+			c = c->next;
+			continue;
+		}
+		next_corner = c;
+		rv += orthopull_1(c, RIGHT, LEFT, any_sel);
+		if (c != next_corner) {
+			c = next_corner;
+			continue;
+		}
+		rv += orthopull_1(c, DOWN, UP, any_sel);
+		if (c != next_corner) {
+			c = next_corner;
+			continue;
+		}
+		c = c->next;
+	}
+	if (rv)
+		pcb_printf("orthopull: %ml mils saved\n", rv);
+	return rv;
+}
+
+static int debumpify()
+{
+	/* Look for "U" shaped traces we can shorten (or eliminate) */
+	int rv = 0;
+	int any_selected = any_line_selected();
+	line_s *l, *l1, *l2;
+	corner_s *c, *c1, *c2;
+	rect_s rr, rp;
+	int o, o1, o2, step, w;
+	for (l = lines; l; l = l->next) {
+		if (DELETED(l))
+			continue;
+		if (!l->line)
+			continue;
+		if (any_selected && !selected(l->line))
+			continue;
+		if (!any_selected && conf_djopt.plugins.djopt.auto_only && !autorouted(l->line))
+			continue;
+		if (l->s->pin || l->s->pad || l->e->pin || l->e->pad)
+			continue;
+		o = line_orient(l, 0);
+		if (o == DIAGONAL)
+			continue;
+		l1 = other_line(l->s, l);
+		if (!l1)
+			continue;
+		o1 = line_orient(l1, l->s);
+		l2 = other_line(l->e, l);
+		if (!l2)
+			continue;
+		o2 = line_orient(l2, l->e);
+		if (ORIENT(o) == ORIENT(o1) || o1 != o2 || o1 == DIAGONAL)
+			continue;
+
+		dprintf("\nline: %#mD to %#mD\n", l->s->x, l->s->y, l->e->x, l->e->y);
+		w = l->line->Thickness / 2 + SB + 1;
+		empty_rect(&rr);
+		add_line_to_rect(&rr, l1);
+		add_line_to_rect(&rr, l2);
+		if (rr.x1 != l->s->x && rr.x1 != l->e->x)
+			rr.x1 -= w;
+		if (rr.x2 != l->s->x && rr.x2 != l->e->x)
+			rr.x2 += w;
+		if (rr.y1 != l->s->y && rr.y1 != l->e->y)
+			rr.y1 -= w;
+		if (rr.y2 != l->s->y && rr.y2 != l->e->y)
+			rr.y2 += w;
+		dprintf("range: x %#mS..%#mS y %#mS..%#mS\n", rr.x1, rr.x2, rr.y1, rr.y2);
+
+		c1 = other_corner(l1, l->s);
+		c2 = other_corner(l2, l->e);
+
+		empty_rect(&rp);
+		for (c = corners; c; c = c->next) {
+			if (DELETED(c))
+				continue;
+			if (c->net != l->s->net && intersecting_layers(c->layer, l->s->layer))
+				add_corner_to_rect_if(&rp, c, &rr);
+		}
+		if (rp.x1 == INT_MAX) {
+			rp.x1 = rr.x2;
+			rp.x2 = rr.x1;
+			rp.y1 = rr.y2;
+			rp.y2 = rr.y1;
+		}
+		dprintf("pin r: x %#mS..%#mS y %#mS..%#mS\n", rp.x1, rp.x2, rp.y1, rp.y2);
+
+		switch (o1) {
+		case LEFT:
+			step = l->s->x - rp.x2 - w;
+			step = gridsnap(step);
+			if (step > l->s->x - c1->x)
+				step = l->s->x - c1->x;
+			if (step > l->s->x - c2->x)
+				step = l->s->x - c2->x;
+			if (step > 0) {
+				dprintf("left step %#mS at %#mD\n", step, l->s->x, l->s->y);
+				move_corner(l->s, l->s->x - step, l->s->y);
+				move_corner(l->e, l->e->x - step, l->e->y);
+				rv += step;
+			}
+			break;
+		case RIGHT:
+			step = rp.x1 - l->s->x - w;
+			step = gridsnap(step);
+			if (step > c1->x - l->s->x)
+				step = c1->x - l->s->x;
+			if (step > c2->x - l->s->x)
+				step = c2->x - l->s->x;
+			if (step > 0) {
+				dprintf("right step %#mS at %#mD\n", step, l->s->x, l->s->y);
+				move_corner(l->s, l->s->x + step, l->s->y);
+				move_corner(l->e, l->e->x + step, l->e->y);
+				rv += step;
+			}
+			break;
+		case UP:
+			if (rp.y2 == INT_MIN)
+				rp.y2 = rr.y1;
+			step = trim_step(l->s->y - rp.y2 - w, l->s->y - c1->y, l->s->y - c2->y);
+			if (step > 0) {
+				dprintf("up step %#mS at %#mD\n", step, l->s->x, l->s->y);
+				move_corner(l->s, l->s->x, l->s->y - step);
+				move_corner(l->e, l->e->x, l->e->y - step);
+				rv += step;
+			}
+			break;
+		case DOWN:
+			step = rp.y1 - l->s->y - w;
+			step = gridsnap(step);
+			if (step > c1->y - l->s->y)
+				step = c1->y - l->s->y;
+			if (step > c2->y - l->s->y)
+				step = c2->y - l->s->y;
+			if (step > 0) {
+				dprintf("down step %#mS at %#mD\n", step, l->s->x, l->s->y);
+				move_corner(l->s, l->s->x, l->s->y + step);
+				move_corner(l->e, l->e->x, l->e->y + step);
+				rv += step;
+			}
+			break;
+		}
+		check(0, l);
+	}
+
+	rv += simple_optimizations();
+	if (rv)
+		pcb_printf("debumpify: %ml mils saved\n", rv / 50);
+	return rv;
+}
+
+static int simple_corner(corner_s * c)
+{
+	int o1, o2;
+	if (c->pad || c->pin || c->via)
+		return 0;
+	if (c->n_lines != 2)
+		return 0;
+	o1 = line_orient(c->lines[0], c);
+	o2 = line_orient(c->lines[1], c);
+	if (ORIENT(o1) == ORIENT(o2))
+		return 0;
+	if (ORIENT(o1) == DIAGONAL || ORIENT(o2) == DIAGONAL)
+		return 0;
+	return 1;
+}
+
+static int unjaggy_once()
+{
+	/* Look for sequences of simple corners we can reduce. */
+	int rv = 0;
+	corner_s *c, *c0, *c1, *cc;
+	int l, w, sel = any_line_selected();
+	int o0, o1, s0, s1;
+	rect_s rr, rp;
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		if (!simple_corner(c))
+			continue;
+		if (!c->lines[0]->line || !c->lines[1]->line)
+			continue;
+		if (sel && !(selected(c->lines[0]->line)
+								 || selected(c->lines[1]->line)))
+			continue;
+		if (!sel && conf_djopt.plugins.djopt.auto_only && !(autorouted(c->lines[0]->line)
+																		 || autorouted(c->lines[1]->line)))
+			continue;
+		dprintf("simple at %#mD\n", c->x, c->y);
+
+		c0 = other_corner(c->lines[0], c);
+		o0 = line_orient(c->lines[0], c);
+		s0 = simple_corner(c0);
+
+		c1 = other_corner(c->lines[1], c);
+		o1 = line_orient(c->lines[1], c);
+		s1 = simple_corner(c1);
+
+		if (!s0 && !s1)
+			continue;
+		dprintf("simples at %#mD\n", c->x, c->y);
+
+		w = 1;
+		for (l = 0; l < c0->n_lines; l++)
+			if (c0->lines[l] != c->lines[0]
+					&& c0->lines[l]->layer == c->lines[0]->layer) {
+				int o = line_orient(c0->lines[l], c0);
+				if (o == o1)
+					w = 0;
+			}
+		for (l = 0; l < c1->n_lines; l++)
+			if (c1->lines[l] != c->lines[0]
+					&& c1->lines[l]->layer == c->lines[0]->layer) {
+				int o = line_orient(c1->lines[l], c1);
+				if (o == o0)
+					w = 0;
+			}
+		if (!w)
+			continue;
+		dprintf("orient ok\n");
+
+		w = c->lines[0]->line->Thickness / 2 + SB + 1;
+		empty_rect(&rr);
+		add_line_to_rect(&rr, c->lines[0]);
+		add_line_to_rect(&rr, c->lines[1]);
+		if (c->x != rr.x1)
+			rr.x1 -= w;
+		else
+			rr.x2 += w;
+		if (c->y != rr.y1)
+			rr.y1 -= w;
+		else
+			rr.y2 += w;
+
+		empty_rect(&rp);
+		for (cc = corners; cc; cc = cc->next) {
+			if (DELETED(cc))
+				continue;
+			if (cc->net != c->net && intersecting_layers(cc->layer, c->layer))
+				add_corner_to_rect_if(&rp, cc, &rr);
+		}
+		dprintf("rp x %#mS..%#mS  y %#mS..%#mS\n", rp.x1, rp.x2, rp.y1, rp.y2);
+		if (rp.x1 <= rp.x2)					/* something triggered */
+			continue;
+
+		dprintf("unjaggy at %#mD layer %d\n", c->x, c->y, c->layer);
+		if (c->x == c0->x)
+			move_corner(c, c1->x, c0->y);
+		else
+			move_corner(c, c0->x, c1->y);
+		rv++;
+		check(c, 0);
+	}
+	rv += simple_optimizations();
+	check(c, 0);
+	return rv;
+}
+
+static int unjaggy()
+{
+	int i, r = 0, j;
+	for (i = 0; i < 100; i++) {
+		j = unjaggy_once();
+		if (j == 0)
+			break;
+		r += j;
+	}
+	if (r)
+		printf("%d unjagg%s    \n", r, r == 1 ? "y" : "ies");
+	return r;
+}
+
+static int vianudge()
+{
+	/* Look for vias with all lines leaving the same way, try to nudge
+	   via to eliminate one or more of them. */
+	int rv = 0;
+	corner_s *c, *c2, *c3;
+	line_s *l;
+	unsigned char directions[MAX_LAYER];
+	unsigned char counts[MAX_LAYER];
+
+	memset(directions, 0, sizeof(directions));
+	memset(counts, 0, sizeof(counts));
+
+	for (c = corners; c; c = c->next) {
+		int o, i, vr, cr, oboth;
+		int len = 0, saved = 0;
+
+		if (DELETED(c))
+			continue;
+
+		if (!c->via)
+			continue;
+
+		memset(directions, 0, sizeof(directions));
+		memset(counts, 0, sizeof(counts));
+
+		for (i = 0; i < c->n_lines; i++) {
+			o = line_orient(c->lines[i], c);
+			counts[c->lines[i]->layer]++;
+			directions[c->lines[i]->layer] |= o;
+		}
+		for (o = 0, i = 0; i < max_copper_layer; i++)
+			if (counts[i] == 1) {
+				o = directions[i];
+				break;
+			}
+		switch (o) {
+		case LEFT:
+		case RIGHT:
+			oboth = LEFT | RIGHT;
+			break;
+		case UP:
+		case DOWN:
+			oboth = UP | DOWN;
+			break;
+		default:
+			continue;
+		}
+		for (i = 0; i < max_copper_layer; i++)
+			if (counts[i] && directions[i] != o && directions[i] != oboth)
+				goto vianudge_continue;
+
+		c2 = 0;
+		for (i = 0; i < c->n_lines; i++) {
+			int ll = line_length(c->lines[i]);
+			if (line_orient(c->lines[i], c) != o) {
+				saved--;
+				continue;
+			}
+			saved++;
+			if (c2 == 0 || len > ll) {
+				len = ll;
+				c2 = other_corner(c->lines[i], c);
+			}
+		}
+		if (c2->pad || c2->pin || c2->via)
+			continue;
+
+		/* Now look for clearance in the new position */
+		vr = c->via->Thickness / 2 + SB + 1;
+		for (c3 = corners; c3; c3 = c3->next) {
+			if (DELETED(c3))
+				continue;
+			if ((c3->net != c->net && (c3->pin || c3->via)) || c3->pad) {
+				cr = corner_radius(c3);
+				if (dist(c2->x, c2->y, c3->x, c3->y) < vr + cr)
+					goto vianudge_continue;
+			}
+		}
+		for (l = lines; l; l = l->next) {
+			if (DELETED(l))
+				continue;
+			if (l->s->net != c->net) {
+				int ld = dist_line_to_point(l, c2);
+				if (ld < l->line->Thickness / 2 + vr)
+					goto vianudge_continue;
+			}
+		}
+
+		/* at this point, we know we can move it */
+
+		dprintf("vianudge: nudging via at %#mD by %#mS saving %#mS\n", c->x, c->y, len, saved);
+		rv += len * saved;
+		move_corner(c, c2->x, c2->y);
+
+		check(c, 0);
+
+	vianudge_continue:
+		continue;
+	}
+
+	if (rv)
+		pcb_printf("vianudge: %ml mils saved\n", rv);
+	return rv;
+}
+
+static int viatrim()
+{
+	/* Look for traces that can be moved to the other side of the board,
+	   to reduce the number of vias needed.  For now, we look for simple
+	   lines, not multi-segmented lines.  */
+	line_s *l, *l2;
+	int i, rv = 0, vrm = 0;
+	int any_sel = any_line_selected();
+
+	for (l = lines; l; l = l->next) {
+		rect_s r;
+		int my_layer, other_layer;
+
+		if (DELETED(l))
+			continue;
+		if (!l->s->via)
+			continue;
+		if (!l->e->via)
+			continue;
+		if (any_sel && !selected(l->line))
+			continue;
+		if (!any_sel && conf_djopt.plugins.djopt.auto_only && !autorouted(l->line))
+			continue;
+
+		my_layer = l->layer;
+		other_layer = -1;
+		dprintf("line %p on layer %d from %#mD to %#mD\n", (void *) l, l->layer, l->s->x, l->s->y, l->e->x, l->e->y);
+		for (i = 0; i < l->s->n_lines; i++)
+			if (l->s->lines[i] != l) {
+				if (other_layer == -1) {
+					other_layer = l->s->lines[i]->layer;
+					dprintf("noting other line %p on layer %d\n", (void *) (l->s->lines[i]), my_layer);
+				}
+				else if (l->s->lines[i]->layer != other_layer) {
+					dprintf("saw other line %p on layer %d (not %d)\n", (void *) (l->s->lines[i]), l->s->lines[i]->layer, my_layer);
+					other_layer = -1;
+					goto viatrim_other_corner;
+				}
+			}
+	viatrim_other_corner:
+		if (other_layer == -1)
+			for (i = 0; i < l->e->n_lines; i++)
+				if (l->e->lines[i] != l) {
+					if (other_layer == -1) {
+						other_layer = l->s->lines[i]->layer;
+						dprintf("noting other line %p on layer %d\n", (void *) (l->s->lines[i]), my_layer);
+					}
+					else if (l->e->lines[i]->layer != other_layer) {
+						dprintf("saw end line on layer %d (not %d)\n", l->e->lines[i]->layer, other_layer);
+						goto viatrim_continue;
+					}
+				}
+
+		/* Now see if any other line intersects us.  We don't need to
+		   check corners, because they'd either be pins/vias and
+		   already conflict, or pads, which we'll check here anyway.  */
+		empty_rect(&r);
+		add_point_to_rect(&r, l->s->x, l->s->y, l->line->Thickness);
+		add_point_to_rect(&r, l->e->x, l->e->y, l->line->Thickness);
+
+		for (l2 = lines; l2; l2 = l2->next) {
+			if (DELETED(l2))
+				continue;
+			if (l2->s->net != l->s->net && l2->layer == other_layer) {
+				dprintf("checking other line %#mD to %#mD\n", l2->s->x, l2->s->y, l2->e->x, l2->e->y);
+				if (line_in_rect(&r, l2)) {
+					dprintf("line from %#mD to %#mD in the way\n", l2->s->x, l2->s->y, l2->e->x, l2->e->y);
+					goto viatrim_continue;
+				}
+			}
+		}
+
+		if (l->layer == other_layer)
+			continue;
+		move_line_to_layer(l, other_layer);
+		rv++;
+
+	viatrim_continue:
+		continue;
+	}
+	vrm = simple_optimizations();
+	if (rv > 0)
+		printf("viatrim: %d traces moved, %d vias removed\n", rv, vrm);
+	return rv + vrm;
+}
+
+static int automagic()
+{
+	int more = 1, oldmore = 0;
+	int toomany = 100;
+	while (more != oldmore && --toomany) {
+		oldmore = more;
+		more += debumpify();
+		more += unjaggy();
+		more += orthopull();
+		more += vianudge();
+		more += viatrim();
+	}
+	return more - 1;
+}
+
+static int miter()
+{
+	corner_s *c;
+	int done, progress;
+	int sel = any_line_selected();
+	int saved = 0;
+
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		c->miter = 0;
+		if (c->n_lines == 2 && !c->via && !c->pin && !c->via) {
+			int o1 = line_orient(c->lines[0], c);
+			int o2 = line_orient(c->lines[1], c);
+			if (ORIENT(o1) != ORIENT(o2)
+					&& o1 != DIAGONAL && o2 != DIAGONAL && c->lines[0]->line->Thickness == c->lines[1]->line->Thickness)
+				c->miter = -1;
+		}
+	}
+
+	done = 0;
+	progress = 1;
+	while (!done && progress) {
+		done = 1;
+		progress = 0;
+		for (c = corners; c; c = c->next) {
+			if (DELETED(c))
+				continue;
+			if (c->miter == -1) {
+				int max = line_length(c->lines[0]);
+				int len = line_length(c->lines[1]);
+				int bloat;
+				int ref, dist;
+				corner_s *closest_corner = 0, *c2, *oc1, *oc2;
+				int mx = 0, my = 0, x, y;
+				int o1 = line_orient(c->lines[0], c);
+				int o2 = line_orient(c->lines[1], c);
+
+				if (c->pad || c->pin || c->via) {
+					c->miter = 0;
+					progress = 1;
+					continue;
+				}
+
+				oc1 = other_corner(c->lines[0], c);
+				oc2 = other_corner(c->lines[1], c);
+#if 0
+				if (oc1->pad)
+					oc1 = 0;
+				if (oc2->pad)
+					oc2 = 0;
+#endif
+
+				if ((sel && !(selected(c->lines[0]->line)
+											|| selected(c->lines[1]->line)))
+						|| (!sel && conf_djopt.plugins.djopt.auto_only && !(autorouted(c->lines[0]->line)
+																						 || autorouted(c->lines[1]->line)))) {
+					c->miter = 0;
+					progress = 1;
+					continue;
+				}
+
+				if (max > len)
+					max = len;
+				switch (o1) {
+				case LEFT:
+					mx = -1;
+					break;
+				case RIGHT:
+					mx = 1;
+					break;
+				case UP:
+					my = -1;
+					break;
+				case DOWN:
+					my = 1;
+					break;
+				}
+				switch (o2) {
+				case LEFT:
+					mx = -1;
+					break;
+				case RIGHT:
+					mx = 1;
+					break;
+				case UP:
+					my = -1;
+					break;
+				case DOWN:
+					my = 1;
+					break;
+				}
+				ref = c->x * mx + c->y * my;
+				dist = max;
+
+				bloat = (c->lines[0]->line->Thickness / 2 + SB + 1) * 3 / 2;
+
+				for (c2 = corners; c2; c2 = c2->next) {
+					if (DELETED(c2))
+						continue;
+					if (c2 != c && c2 != oc1 && c2 != oc2
+							&& c->x * mx <= c2->x * mx
+							&& c->y * my <= c2->y * my && c->net != c2->net && intersecting_layers(c->layer, c2->layer)) {
+						int cr = corner_radius(c2);
+						len = c2->x * mx + c2->y * my - ref - cr - bloat;
+						if (c->x != c2->x && c->y != c2->y)
+							len -= cr;
+						if (len < dist || (len == dist && c->miter != -1)) {
+							dist = len;
+							closest_corner = c2;
+						}
+					}
+				}
+
+				if (closest_corner && closest_corner->miter == -1) {
+					done = 0;
+					continue;
+				}
+
+#if 0
+				if (dist < conf_core.editor.grid) {
+					c->miter = 0;
+					progress = 1;
+					continue;
+				}
+
+				dist -= dist % conf_core.editor.grid;
+#endif
+				if (dist <= 0) {
+					c->miter = 0;
+					progress = 1;
+					continue;
+				}
+
+				x = c->x;
+				y = c->y;
+				switch (o1) {
+				case LEFT:
+					x -= dist;
+					break;
+				case RIGHT:
+					x += dist;
+					break;
+				case UP:
+					y -= dist;
+					break;
+				case DOWN:
+					y += dist;
+					break;
+				}
+				c2 = find_corner(x, y, c->layer);
+				if (c2 != other_corner(c->lines[0], c))
+					split_line(c->lines[0], c2);
+				x = c->x;
+				y = c->y;
+				switch (o2) {
+				case LEFT:
+					x -= dist;
+					break;
+				case RIGHT:
+					x += dist;
+					break;
+				case UP:
+					y -= dist;
+					break;
+				case DOWN:
+					y += dist;
+					break;
+				}
+				move_corner(c, x, y);
+				c->miter = 0;
+				c2->miter = 0;
+				progress = 1;
+				saved++;
+			}
+		}
+	}
+	return saved;
+}
+
+static void classify_corner(corner_s * c, int this_net)
+{
+	int i;
+	if (c->net == this_net)
+		return;
+	c->net = this_net;
+	for (i = 0; i < c->n_lines; i++)
+		classify_corner(other_corner(c->lines[i], c), this_net);
+}
+
+static void classify_nets()
+{
+	static int this_net = 1;
+	corner_s *c;
+
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		if (c->net)
+			continue;
+		classify_corner(c, this_net);
+		this_net++;
+	}
+}
+
+#if 0
+/* Not used */
+static void dump_all()
+{
+	corner_s *c;
+	line_s *l;
+	for (c = corners; c; c = c->next) {
+		if (DELETED(c))
+			continue;
+		printf("%p corner %d,%d layer %d net %d\n", (void *) c, c->x, c->y, c->layer, c->net);
+	}
+	for (l = lines; l; l = l->next) {
+		if (DELETED(l))
+			continue;
+		printf("%p line %p to %p layer %d\n", (void *) l, (void *) (l->s), (void *) (l->e), l->layer);
+	}
+}
+#endif
+
+#if 0
+static void nudge_corner(corner_s * c, int dx, int dy, corner_s * prev_corner)
+{
+	int ox = c->x;
+	int oy = c->y;
+	int l;
+	if (prev_corner && (c->pin || c->pad))
+		return;
+	move_corner(c, ox + dx, oy + dy);
+	for (l = 0; l < c->n_lines; l++) {
+		corner_s *oc = other_corner(c->lines[l], c);
+		if (oc == prev_corner)
+			continue;
+		if (dx && oc->x == ox)
+			nudge_corner(oc, dx, 0, c);
+		if (dy && oc->y == oy)
+			nudge_corner(oc, 0, dy, c);
+	}
+}
+#endif
+
+static line_s *choose_example_line(corner_s * c1, corner_s * c2)
+{
+	int ci, li;
+	corner_s *c[2];
+	c[0] = c1;
+	c[1] = c2;
+	dprintf("choose_example_line\n");
+	for (ci = 0; ci < 2; ci++)
+		for (li = 0; li < c[ci]->n_lines; li++) {
+			dprintf("  try[%d,%d] \033[36m<%#mD-%#mD t%#mS c%#mS f%s>\033[0m\n",
+							ci, li,
+							c[ci]->lines[li]->s->x, c[ci]->lines[li]->s->y,
+							c[ci]->lines[li]->e->x, c[ci]->lines[li]->e->y,
+							c[ci]->lines[li]->line->Thickness,
+							c[ci]->lines[li]->line->Clearance, flags_to_string(c[ci]->lines[li]->line->Flags, PCB_TYPE_LINE));
+			/* Pads are disqualified, as we want to mimic a trace line. */
+			if (c[ci]->lines[li]->line == (LineTypePtr) c[ci]->pad) {
+				dprintf("  bad, pad\n");
+				continue;
+			}
+			/* Lines on layers that don't connect to the other pad are bad too.  */
+			if (!intersecting_layers(c[ci]->lines[li]->layer, c[1 - ci]->layer)) {
+				dprintf("  bad, layers\n");
+				continue;
+			}
+			dprintf("  good\n");
+			return c[ci]->lines[li];
+		}
+	dprintf("choose_example_line: none found!\n");
+	return 0;
+}
+
+static int connect_corners(corner_s * c1, corner_s * c2)
+{
+	int layer;
+	line_s *ex = choose_example_line(c1, c2);
+	LineType *example = ex->line;
+
+	dprintf
+		("connect_corners \033[32m%#mD to %#mD, example line %#mD to %#mD l%d\033[0m\n",
+		 c1->x, c1->y, c2->x, c2->y, ex->s->x, ex->s->y, ex->e->x, ex->e->y, ex->layer);
+
+	layer = ex->layer;
+
+	/* Assume c1 is the moveable one.  */
+	if (!(c1->pin || c1->pad || c1->via) && c1->n_lines == 1) {
+		int nx, ny;
+		/* Extend the line */
+		if (c1->lines[0]->s->x == c1->lines[0]->e->x)
+			nx = c1->x, ny = c2->y;
+		else
+			nx = c2->x, ny = c1->y;
+		if (nx != c2->x || ny != c2->y) {
+			move_corner(c1, nx, ny);
+			new_line(c1, c2, layer, example);
+			return 1;
+		}
+		else {
+			move_corner(c1, nx, ny);
+			return 1;
+		}
+	}
+	else {
+		corner_s *nc = find_corner(c1->x, c2->y, layer);
+		new_line(c1, nc, layer, example);
+		new_line(nc, c2, layer, example);
+		return 0;
+	}
+}
+
+static void pinsnap()
+{
+	corner_s *c;
+	int best_dist[MAX_LAYER + 1];
+	corner_s *best_c[MAX_LAYER + 1];
+	int l, got_one;
+	int left = 0, right = 0, top = 0, bottom = 0;
+	PinType *pin;
+	int again = 1;
+
+	int close = 0;
+	corner_s *c2;
+
+	/* Look for pins that have no connections.  See if there's a corner
+	   close by that should be connected to it.  This usually happens
+	   when the MUCS router needs to route to an off-grid pin.  */
+	while (again) {
+		again = 0;
+		for (c = corners; c; c = c->next) {
+			if (DELETED(c))
+				continue;
+			if (!(c->pin || c->via || c->pad))
+				continue;
+
+			pin = 0;
+
+			dprintf("\ncorner %s\n", corner_name(c));
+			if (c->pin || c->via) {
+				pin = c->pin ? c->pin : c->via;
+				close = pin->Thickness / 2;
+				left = c->x - close;
+				right = c->x + close;
+				bottom = c->y - close;
+				top = c->y + close;
+			}
+			else if (c->pad) {
+				close = c->pad->Thickness / 2 + 1;
+				left = djmin(c->pad->Point1.X, c->pad->Point2.X) - close;
+				right = djmax(c->pad->Point1.X, c->pad->Point2.X) + close;
+				bottom = djmin(c->pad->Point1.Y, c->pad->Point2.Y) - close;
+				top = djmax(c->pad->Point1.Y, c->pad->Point2.Y) + close;
+				if (c->pad->Point1.X == c->pad->Point2.X) {
+					int hy = (c->pad->Point1.Y + c->pad->Point2.Y) / 2;
+					dprintf("pad y %#mS %#mS hy %#mS c %#mS\n", c->pad->Point1.Y, c->pad->Point2.Y, hy, c->y);
+					if (c->y < hy)
+						top = hy;
+					else
+						bottom = hy + 1;
+				}
+				else {
+					int hx = (c->pad->Point1.X + c->pad->Point2.X) / 2;
+					dprintf("pad x %#mS %#mS hx %#mS c %#mS\n", c->pad->Point1.X, c->pad->Point2.X, hx, c->x);
+					if (c->x < hx)
+						right = hx;
+					else
+						left = hx + 1;
+				}
+			}
+
+			dprintf("%s x %#mS-%#mS y %#mS-%#mS\n", corner_name(c), left, right, bottom, top);
+			for (l = 0; l <= max_copper_layer; l++) {
+				best_dist[l] = close * 2;
+				best_c[l] = 0;
+			}
+			got_one = 0;
+			for (c2 = corners; c2; c2 = c2->next) {
+				int lt;
+
+				if (DELETED(c2))
+					continue;
+				lt = corner_radius(c2);
+				if (c2->n_lines && c2 != c && !(c2->pin || c2->pad || c2->via)
+						&& intersecting_layers(c->layer, c2->layer)
+						&& c2->x >= left - lt && c2->x <= right + lt && c2->y >= bottom - lt && c2->y <= top + lt) {
+					int d = dist(c->x, c->y, c2->x, c2->y);
+					if (pin && d > pin->Thickness / 2 + lt)
+						continue;
+					if (c2->n_lines == 1) {
+						got_one++;
+						dprintf("found orphan %s vs %s\n", corner_name(c2), corner_name(c));
+						connect_corners(c, c2);
+						again = 1;
+						continue;
+					}
+					if (best_c[c2->layer] == 0 || c2->n_lines < best_c[c2->layer]->n_lines || (d < best_dist[c2->layer]
+																																										 && c2->n_lines <=
+																																										 best_c[c2->layer]->n_lines)) {
+						best_dist[c2->layer] = d;
+						best_c[c2->layer] = c2;
+						dprintf("layer %d best now %s\n", c2->layer, corner_name(c2));
+					}
+				}
+				if (!got_one && c->n_lines == (c->pad ? 1 : 0)) {
+					for (l = 0; l <= max_copper_layer; l++)
+						if (best_c[l])
+							dprintf("best[%d] = %s\n", l, corner_name(best_c[l]));
+					for (l = 0; l <= max_copper_layer; l++)
+						if (best_c[l]) {
+							dprintf("move %s to %s\n", corner_name(best_c[l]), corner_name(c));
+							connect_corners(best_c[l], c);
+							again = 1;
+							continue;
+						}
+				}
+			}
+		}
+	}
+
+	/* Now look for line ends that don't connect, see if they need to be
+	   extended to intersect another line.  */
+	for (c = corners; c; c = c->next) {
+		line_s *l, *t;
+		int lo;
+
+		if (DELETED(c))
+			continue;
+		if (c->pin || c->via || c->pad)
+			continue;
+		if (c->n_lines != 1)
+			continue;
+
+		l = c->lines[0];
+		lo = line_orient(l, c);
+		dprintf("line end %#mD orient %d\n", c->x, c->y, lo);
+
+		for (t = lines; t; t = t->next) {
+			if (DELETED(t))
+				continue;
+			if (t->layer != c->lines[0]->layer)
+				continue;
+			switch (lo) {							/* remember, orient is for the line relative to the corner */
+			case LEFT:
+				if (t->s->x == t->e->x
+						&& c->x < t->s->x
+						&& t->s->x < c->x + (l->line->Thickness + t->line->Thickness) / 2 && ((t->s->y < c->y && c->y < t->e->y)
+																																									|| (t->e->y < c->y && c->y < t->s->y))) {
+					dprintf("found %#mD - %#mD\n", t->s->x, t->s->y, t->e->x, t->e->y);
+					move_corner(c, t->s->x, c->y);
+				}
+				break;
+			case RIGHT:
+				if (t->s->x == t->e->x
+						&& c->x > t->s->x
+						&& t->s->x > c->x - (l->line->Thickness + t->line->Thickness) / 2 && ((t->s->y < c->y && c->y < t->e->y)
+																																									|| (t->e->y < c->y && c->y < t->s->y))) {
+					dprintf("found %#mD - %#mD\n", t->s->x, t->s->y, t->e->x, t->e->y);
+					move_corner(c, t->s->x, c->y);
+				}
+				break;
+			case UP:
+				if (t->s->y == t->e->y
+						&& c->y < t->s->y
+						&& t->s->y < c->y + (l->line->Thickness + t->line->Thickness) / 2 && ((t->s->x < c->x && c->x < t->e->x)
+																																									|| (t->e->x < c->x && c->x < t->s->x))) {
+					dprintf("found %#mD - %#mD\n", t->s->x, t->s->y, t->e->x, t->e->y);
+					move_corner(c, c->x, t->s->y);
+				}
+				break;
+			case DOWN:
+				if (t->s->y == t->e->y
+						&& c->y > t->s->y
+						&& t->s->y > c->y - (l->line->Thickness + t->line->Thickness) / 2 && ((t->s->x < c->x && c->x < t->e->x)
+																																									|| (t->e->x < c->x && c->x < t->s->x))) {
+					dprintf("found %#mD - %#mD\n", t->s->x, t->s->y, t->e->x, t->e->y);
+					move_corner(c, c->x, t->s->y);
+				}
+				break;
+			}
+		}
+	}
+}
+
+static int pad_orient(PadType * p)
+{
+	if (p->Point1.X == p->Point2.X)
+		return O_VERT;
+	if (p->Point1.Y == p->Point2.Y)
+		return O_HORIZ;
+	return DIAGONAL;
+}
+
+static void padcleaner()
+{
+	line_s *l, *nextl;
+	int close;
+	rect_s r;
+
+	dprintf("\ndj: padcleaner\n");
+	for (l = lines; l; l = nextl) {
+		nextl = l->next;
+
+		if (l->is_pad)
+			continue;
+
+		if (DELETED(l))
+			continue;
+
+		dprintf("dj: line %p\n", (void *) l);
+		check(0, l);
+
+		if (l->s->pad && l->s->pad == l->e->pad)
+			continue;
+
+		ALLPAD_LOOP(PCB->Data);
+		{
+			int layerflag = TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? LT_SOLDER : LT_COMPONENT;
+
+			if (layer_type[l->layer] != layerflag)
+				continue;
+
+			empty_rect(&r);
+			close = pad->Thickness / 2 + 1;
+			add_point_to_rect(&r, pad->Point1.X, pad->Point1.Y, close - SB / 2);
+			add_point_to_rect(&r, pad->Point2.X, pad->Point2.Y, close - SB / 2);
+			if (pin_in_rect(&r, l->s->x, l->s->y, 0)
+					&& pin_in_rect(&r, l->e->x, l->e->y, 0)
+					&& ORIENT(line_orient(l, 0)) == pad_orient(pad)) {
+				dprintf
+					("padcleaner %#mD-%#mD %#mS vs line %#mD-%#mD %#mS\n",
+					 pad->Point1.X, pad->Point1.Y, pad->Point2.X, pad->Point2.Y,
+					 pad->Thickness, l->s->x, l->s->y, l->e->x, l->e->y, l->line->Thickness);
+				remove_line(l);
+				goto next_line;
+			}
+		}
+		ENDALL_LOOP;
+	next_line:;
+	}
+}
+
+static void grok_layer_groups()
+{
+	int i, j, f;
+	LayerGroupType *l = &(PCB->LayerGroups);
+
+	solder_layer = component_layer = -1;
+	for (i = 0; i < max_copper_layer; i++) {
+		layer_type[i] = 0;
+		layer_groupings[i] = 0;
+	}
+	for (i = 0; i < max_group; i++) {
+		f = 0;
+		for (j = 0; j < l->Number[i]; j++) {
+			if (l->Entries[i][j] == solder_silk_layer)
+				f |= LT_SOLDER;
+			if (l->Entries[i][j] == component_silk_layer)
+				f |= LT_COMPONENT;
+		}
+		for (j = 0; j < l->Number[i]; j++) {
+			if (l->Entries[i][j] < max_copper_layer) {
+				layer_type[l->Entries[i][j]] |= f;
+				layer_groupings[l->Entries[i][j]] = i;
+				if (solder_layer == -1 && f == LT_SOLDER)
+					solder_layer = l->Entries[i][j];
+				if (component_layer == -1 && f == LT_COMPONENT)
+					component_layer = l->Entries[i][j];
+			}
+		}
+	}
+}
+
+static const char djopt_syntax[] =
+	"djopt(debumpify|unjaggy|simple|vianudge|viatrim|orthopull)\n" "djopt(auto) - all of the above\n" "djopt(miter)";
+
+static const char djopt_help[] = "Perform various optimizations on the current board.";
+
+/* %start-doc actions djopt
+
+The different types of optimizations change your board in order to
+reduce the total trace length and via count.
+
+ at table @code
+
+ at item debumpify
+Looks for U-shaped traces that can be shortened or eliminated.
+
+ at item unjaggy
+Looks for corners which could be flipped to eliminate one or more
+corners (i.e. jaggy lines become simpler).
+
+ at item simple
+Removing uneeded vias, replacing two or more trace segments in a row
+with a single segment.  This is usually performed automatically after
+other optimizations.
+
+ at item vianudge
+Looks for vias where all traces leave in the same direction.  Tries to
+move via in that direction to eliminate one of the traces (and thus a
+corner).
+
+ at item viatrim
+Looks for traces that go from via to via, where moving that trace to a
+different layer eliminates one or both vias.
+
+ at item orthopull
+Looks for chains of traces all going in one direction, with more
+traces orthogonal on one side than on the other.  Moves the chain in
+that direction, causing a net reduction in trace length, possibly
+eliminating traces and/or corners.
+
+ at item splitlines
+Looks for lines that pass through vias, pins, or pads, and splits them
+into separate lines so they can be managed separately.
+
+ at item auto
+Performs the above options, repeating until no further optimizations
+can be made.
+
+ at item miter
+Replaces 90 degree corners with a pair of 45 degree corners, to reduce
+RF losses and trace length.
+
+ at end table
+
+%end-doc */
+
+static int ActionDJopt(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *arg = argc > 0 ? argv[0] : NULL;
+	int layn, saved = 0;
+	corner_s *c;
+
+#ifdef ENDIF
+	SwitchDrawingWindow(PCB->Zoom, Output.drawing_area->window, conf_core.editor.show_solder_side, pcb_false);
+#endif
+
+	hid_action("Busy");
+
+	lines = 0;
+	corners = 0;
+
+	grok_layer_groups();
+
+	ELEMENT_LOOP(PCB->Data);
+	PIN_LOOP(element);
+	{
+		c = find_corner(pin->X, pin->Y, -1);
+		c->pin = pin;
+	}
+	END_LOOP;
+	PAD_LOOP(element);
+	{
+		int layern = TEST_FLAG(PCB_FLAG_ONSOLDER, pad) ? solder_layer : component_layer;
+		line_s *ls = (line_s *) malloc(sizeof(line_s));
+		ls->next = lines;
+		lines = ls;
+		ls->is_pad = 1;
+		ls->s = find_corner(pad->Point1.X, pad->Point1.Y, layern);
+		ls->s->pad = pad;
+		ls->e = find_corner(pad->Point2.X, pad->Point2.Y, layern);
+		ls->e->pad = pad;
+		ls->layer = layern;
+		ls->line = (LineTypePtr) pad;
+		add_line_to_corner(ls, ls->s);
+		add_line_to_corner(ls, ls->e);
+
+	}
+	END_LOOP;
+	END_LOOP;
+	VIA_LOOP(PCB->Data);
+	/* hace don't mess with vias that have thermals */
+	/* but then again don't bump into them
+	   if (!TEST_FLAG(ALLTHERMFLAGS, via))
+	 */
+	{
+		c = find_corner(via->X, via->Y, -1);
+		c->via = via;
+	}
+	END_LOOP;
+	check(0, 0);
+
+	if (NSTRCMP(arg, "splitlines") == 0) {
+		if (canonicalize_lines())
+			IncrementUndoSerialNumber();
+		return 0;
+	}
+
+	for (layn = 0; layn < max_copper_layer; layn++) {
+		LayerType *layer = LAYER_PTR(layn);
+
+		LINE_LOOP(layer);
+		{
+			line_s *ls;
+
+			if (conf_djopt.plugins.djopt.auto_only && !autorouted(line))
+				continue;
+
+			/* don't mess with thermals */
+			if (TEST_FLAG(PCB_FLAG_USETHERMAL, line))
+				continue;
+
+			if (line->Point1.X == line->Point2.X && line->Point1.Y == line->Point2.Y) {
+				RemoveLine(layer, line);
+				continue;
+			}
+
+			ls = (line_s *) malloc(sizeof(line_s));
+			ls->next = lines;
+			lines = ls;
+			ls->is_pad = 0;
+			ls->s = find_corner(line->Point1.X, line->Point1.Y, layn);
+			ls->e = find_corner(line->Point2.X, line->Point2.Y, layn);
+			ls->line = line;
+			add_line_to_corner(ls, ls->s);
+			add_line_to_corner(ls, ls->e);
+			ls->layer = layn;
+		}
+		END_LOOP;
+	}
+
+	check(0, 0);
+	pinsnap();
+	canonicalize_lines();
+	check(0, 0);
+	classify_nets();
+	/*dump_all(); */
+	check(0, 0);
+
+	if (NSTRCMP(arg, "debumpify") == 0)
+		saved += debumpify();
+	else if (NSTRCMP(arg, "unjaggy") == 0)
+		saved += unjaggy();
+	else if (NSTRCMP(arg, "simple") == 0)
+		saved += simple_optimizations();
+	else if (NSTRCMP(arg, "vianudge") == 0)
+		saved += vianudge();
+	else if (NSTRCMP(arg, "viatrim") == 0)
+		saved += viatrim();
+	else if (NSTRCMP(arg, "orthopull") == 0)
+		saved += orthopull();
+	else if (NSTRCMP(arg, "auto") == 0)
+		saved += automagic();
+	else if (NSTRCMP(arg, "miter") == 0)
+		saved += miter();
+	else {
+		printf("unknown command: %s\n", arg);
+		return 1;
+	}
+
+	padcleaner();
+
+	check(0, 0);
+	if (saved)
+		IncrementUndoSerialNumber();
+	return 0;
+}
+
+HID_Action djopt_action_list[] = {
+	{"djopt", 0, ActionDJopt,
+	 djopt_help, djopt_syntax}
+};
+
+REGISTER_ACTIONS(djopt_action_list, djopt_cookie)
+
+static void hid_djopt_uninit(void)
+{
+	hid_remove_actions_by_cookie(djopt_cookie);
+	conf_unreg_fields("plugins/djopt/");
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_djopt_init(void)
+{
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_djopt, field,isarray,type_name,cpath,cname,desc,flags);
+#include "djopt_conf_fields.h"
+
+	REGISTER_ACTIONS(djopt_action_list, djopt_cookie)
+	return hid_djopt_uninit;
+}
diff --git a/src_plugins/djopt/djopt.h b/src_plugins/djopt/djopt.h
new file mode 100644
index 0000000..d5d09ac
--- /dev/null
+++ b/src_plugins/djopt/djopt.h
@@ -0,0 +1,34 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2003 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+#ifndef	PCB_DJOPT_H
+#define	PCB_DJOPT_H
+
+#include "global.h"
+
+int ActionDJopt(int, char **, Coord, Coord);
+int djopt_set_auto_only(int, char **, Coord, Coord);
+#endif
diff --git a/src_plugins/djopt/djopt_conf.h b/src_plugins/djopt/djopt_conf.h
new file mode 100644
index 0000000..cd12445
--- /dev/null
+++ b/src_plugins/djopt/djopt_conf.h
@@ -0,0 +1,14 @@
+#ifndef PCB_DJOPT_CONF_H
+#define PCB_DJOPT_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct djopt {
+			CFT_BOOLEAN auto_only;         /* Operate on autorouted tracks only */
+		} djopt;
+	} plugins;
+} conf_djopt_t;
+
+#endif
diff --git a/src_plugins/export_bboard/Makefile b/src_plugins/export_bboard/Makefile
new file mode 100644
index 0000000..9fc5059
--- /dev/null
+++ b/src_plugins/export_bboard/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_bboard
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_bboard/Plug.tmpasm b/src_plugins/export_bboard/Plug.tmpasm
new file mode 100644
index 0000000..9728937
--- /dev/null
+++ b/src_plugins/export_bboard/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_bboard}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_bboard/bboard.o @]
+
+switch /local/pcb/export_bboard/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_bboard/README b/src_plugins/export_bboard/README
new file mode 100644
index 0000000..ffedc4d
--- /dev/null
+++ b/src_plugins/export_bboard/README
@@ -0,0 +1,5 @@
+Export breadboard
+
+#state: WIP
+#default: disabled
+#implements: export
diff --git a/src_plugins/export_bboard/bboard.c b/src_plugins/export_bboard/bboard.c
new file mode 100644
index 0000000..896df2b
--- /dev/null
+++ b/src_plugins/export_bboard/bboard.c
@@ -0,0 +1,614 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  Breadboard export HID
+ *  This code is based on the GERBER and OpenSCAD export HID
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <time.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "error.h"
+#include "buffer.h"
+#include "create.h"
+#include "layer.h"
+#include "plugins.h"
+#include "compat_misc.h"
+#include "misc_util.h"
+
+#include "hid.h"
+#include "hid_attrib.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "hid_init.h"
+#include "hid_helper.h"
+
+#include <cairo.h>
+
+static const char *bboard_cookie = "bboard exporter";
+
+static cairo_surface_t *bboard_cairo_sfc;
+static cairo_t *bboard_cairo_ctx;
+#define ASSERT_CAIRO  if ((bboard_cairo_ctx == NULL) || (bboard_cairo_sfc == NULL)) \
+                         return;
+
+#define DPI_SCALE	150
+
+#define BBEXT		".png"
+#define MODELBASE	"models"
+#define BBOARDBASE	"bboard"
+
+/****************************************************************************************************
+* Breadboard export filter parameters and options
+****************************************************************************************************/
+
+static HID bboard_hid;
+
+static struct {
+	int draw;
+	int exp;
+	float z_offset;
+	int solder;
+	int component;
+} group_data[MAX_LAYER];
+
+
+#define HA_bboardfile 		0
+#define HA_bgcolor	 	1
+#define HA_skipsolder	 	2
+#define HA_antialias 		3
+
+static HID_Attribute bboard_options[] = {
+/*
+%start-doc options "Breadboard Export"
+ at ftable @code
+ at item --bbfile <string>
+Name of breadboard image output file.
+ at end ftable
+%end-doc
+*/
+	{
+	 "bbfile", "Braedboard file name", HID_String, 0, 0,
+	 {
+		0, 0, 0}, 0, 0},
+/*
+%start-doc options "Breadboard Export"
+ at ftable @code
+ at item --bgcolor <string>
+Background color. It can be HTML color code (RRGGBB); if left empty or set to 'transparent', transparent background is used.
+ at end ftable
+%end-doc
+*/
+	{
+	 "bgcolor", "Background color (rrggbb)", HID_String, 0, 0,
+	 {
+		0, 0, 0}, 0, 0},
+/*
+%start-doc options "Breadboard Export"
+ at ftable @code
+ at item --skipsolder <bool>
+The solder layer will be ommited (if set to true) from output.
+ at end ftable
+%end-doc
+*/
+	{
+	 "skipsolder", "Ignore solder layer", HID_Boolean, 0, 0,
+	 {
+		1, 0, 0}, 0, 0},
+/*
+%start-doc options "Breadboard Export"
+ at ftable @code
+ at item --antialias <bool>
+Connections are antialiased. Antialiasing applies only to wires, models are not antialiased..
+ at end ftable
+%end-doc
+*/
+	{
+	 "antialias", "Antialiasing", HID_Boolean, 0, 0,
+	 {
+		1, 0, 0}, 0, 0},
+};
+
+#define NUM_OPTIONS (sizeof(bboard_options)/sizeof(bboard_options[0]))
+
+static HID_Attr_Val bboard_values[NUM_OPTIONS];
+
+/****************************************************************************************************/
+static const char *bboard_filename = 0;
+static const char *bboard_bgcolor = 0;
+
+Coord bboard_scale_coord(Coord x)
+{
+	return ((x * DPI_SCALE / 254 / 10000) + 0) / 10;
+}
+
+
+/*******************************************
+* Export filter implementation starts here
+********************************************/
+
+static HID_Attribute *bboard_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+	if (PCB)
+		derive_default_filename(PCB->Filename, &bboard_options[HA_bboardfile], ".png", &last_made_filename);
+
+	bboard_options[HA_bgcolor].default_val.str_value = pcb_strdup("#FFFFFF");
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return bboard_options;
+}
+
+static int bboard_validate_layer(const char *name, int group, int skipsolder)
+{
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+
+	if (name == 0)
+		name = PCB->Data->Layer[idx].Name;
+
+	if (strcmp(name, "invisible") == 0)
+		return 0;
+
+	if (SL_TYPE(idx) == SL_ASSY)
+		return 0;
+
+	if (strcmp(name, "route") == 0)
+		return 0;
+
+	if (strcmp(name, "outline") == 0)
+		return 0;
+
+	if (group_data[group].solder && skipsolder)
+		return 0;
+
+
+	if (group >= 0 && group < max_group) {
+		if (!group_data[group].draw)
+			return 0;
+		group_data[group].exp = 1;
+		return 1;
+	}
+	else
+		return 0;
+}
+
+static void bboard_get_layer_color(LayerType * layer, int *clr_r, int *clr_g, int *clr_b)
+{
+	char *clr;
+	unsigned int r, g, b;
+
+	if ((clr = AttributeGetFromList(&(layer->Attributes), "BBoard::LayerColor")) != NULL) {
+		if (clr[0] == '#') {
+			if (sscanf(&(clr[1]), "%02x%02x%02x", &r, &g, &b) == 3)
+				goto ok;
+		}
+	}
+
+	if (layer->Color && (layer->Color[0] == '#')) {
+		if (sscanf(&(layer->Color[1]), "%02x%02x%02x", &r, &g, &b) == 3)
+			goto ok;
+	}
+
+	/* default color */
+	*clr_r = 0xff;
+	*clr_g = 0xff;
+	*clr_b = 0xff;
+	return;
+
+ok:
+	*clr_r = r;
+	*clr_g = g;
+	*clr_b = b;
+	return;
+}
+
+
+static void bboard_set_color_cairo(int r, int g, int b)
+{
+	ASSERT_CAIRO;
+
+	cairo_set_source_rgb(bboard_cairo_ctx, ((float) r) / 255., ((float) g) / 255., ((float) b) / 255.);
+}
+
+
+static void bboard_draw_line_cairo(Coord x1, Coord y1, Coord x2, Coord y2, Coord thickness)
+{
+	ASSERT_CAIRO;
+
+	cairo_set_line_cap(bboard_cairo_ctx, CAIRO_LINE_CAP_ROUND);
+
+	cairo_move_to(bboard_cairo_ctx, bboard_scale_coord(x1), bboard_scale_coord(y1));
+	cairo_line_to(bboard_cairo_ctx, bboard_scale_coord(x2), bboard_scale_coord(y2));
+
+	cairo_set_line_width(bboard_cairo_ctx, bboard_scale_coord(thickness));
+	cairo_stroke(bboard_cairo_ctx);
+}
+
+#warning TODO: remove x1;y1;x2;y2
+static void
+bboard_draw_arc_cairo(/*Coord x1, Coord y1, Coord x2, Coord y2,*/ Coord x,
+											Coord y, Coord w, Coord h, Angle sa, Angle a, Coord thickness)
+{
+	ASSERT_CAIRO;
+
+	cairo_set_line_cap(bboard_cairo_ctx, CAIRO_LINE_CAP_ROUND);
+
+	cairo_save(bboard_cairo_ctx);
+	cairo_translate(bboard_cairo_ctx, bboard_scale_coord(x), bboard_scale_coord(y));
+	cairo_scale(bboard_cairo_ctx, bboard_scale_coord(w), -bboard_scale_coord(h));
+	if (a < 0) {
+		cairo_arc_negative(bboard_cairo_ctx, 0., 0., 1., (sa + 180.) * M_PI / 180., (a + sa + 180.) * M_PI / 180.);
+	}
+	else {
+		cairo_arc(bboard_cairo_ctx, 0., 0., 1., (sa + 180.) * M_PI / 180., (a + sa + 180.) * M_PI / 180.);
+	}
+	cairo_restore(bboard_cairo_ctx);
+
+	cairo_set_line_width(bboard_cairo_ctx, bboard_scale_coord(thickness));
+	cairo_stroke(bboard_cairo_ctx);
+}
+
+static pcb_bool bboard_init_board_cairo(Coord x1, Coord y1, const char *color, int antialias)
+{
+	unsigned int r, g, b;
+	float tr = 1.;								/* background transparency */
+
+	if (color) {
+		if (strlen(color) == 0 || !strcmp(color, "transparent")) {
+			tr = 0.;
+			r = g = b = 0xff;
+		}
+		else {
+			if ((color[0] != '#')
+					|| sscanf(&(color[1]), "%02x%02x%02x", &r, &g, &b) != 3) {
+				Message(PCB_MSG_ERROR, "BBExport: Invalid background color \"%s\"", color);
+				r = g = b = 0xff;
+			}
+
+		}
+	}
+
+	bboard_cairo_sfc = NULL;
+	bboard_cairo_ctx = NULL;
+	bboard_cairo_sfc = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bboard_scale_coord(x1), bboard_scale_coord(y1));
+	if (bboard_cairo_sfc) {
+		bboard_cairo_ctx = cairo_create(bboard_cairo_sfc);
+		if (!bboard_cairo_ctx) {
+			cairo_surface_destroy(bboard_cairo_sfc);
+			bboard_cairo_sfc = NULL;
+		}
+
+	}
+
+	if ((bboard_cairo_ctx != NULL) && (bboard_cairo_sfc != NULL)) {
+		cairo_set_antialias(bboard_cairo_ctx, (antialias) ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
+		cairo_rectangle(bboard_cairo_ctx, 0, 0, bboard_scale_coord(x1), bboard_scale_coord(y1));
+		cairo_set_source_rgba(bboard_cairo_ctx, ((float) r) / 255., ((float) g) / 255., ((float) b) / 255., tr);
+		cairo_fill(bboard_cairo_ctx);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void bboard_finish_board_cairo(void)
+{
+	ASSERT_CAIRO;
+
+	cairo_surface_write_to_png(bboard_cairo_sfc, bboard_filename);
+
+	cairo_destroy(bboard_cairo_ctx);
+	cairo_surface_destroy(bboard_cairo_sfc);
+}
+
+static char *bboard_get_model_filename(char *basename, char *value, pcb_bool nested)
+{
+	char *s;
+
+/*
+	s = Concat(pcblibdir, PCB_DIR_SEPARATOR_S, MODELBASE, PCB_DIR_SEPARATOR_S,
+					 BBOARDBASE, PCB_DIR_SEPARATOR_S, basename, (value
+																											 && nested) ?
+					 PCB_DIR_SEPARATOR_S : "", (value && nested) ? basename : "", (value) ? "-" : "", (value) ? value : "", BBEXT, NULL);
+*/
+	s = pcb_strdup("TODO_fn1");
+	if (s != NULL) {
+		if (!FileExists(s)) {
+			free(s);
+			s = NULL;
+		}
+	}
+	return s;
+}
+
+
+static int bboard_parse_offset(char *s, Coord * ox, Coord * oy)
+{
+	Coord xx = 0, yy = 0;
+	int n = 0, ln = 0;
+	char val[32];
+
+	while (sscanf(s, "%30s%n", val, &ln) >= 1) {
+		switch (n) {
+		case 0:
+			xx = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 1:
+			yy = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		}
+		s = s + ln;
+		n++;
+	}
+	if (n == 2) {
+		*ox = xx;
+		*oy = yy;
+		return pcb_true;
+	}
+	else {
+		return pcb_false;
+	}
+
+}
+
+
+static void bboard_export_element_cairo(ElementType * element, pcb_bool onsolder)
+{
+	cairo_surface_t *sfc;
+	Coord ex, ey;
+	Coord ox = 0, oy = 0;
+	int w, h;
+	Angle tmp_angle = 0.0;
+	char *model_angle, *s = 0, *s1, *s2, *fname = NULL;
+	pcb_bool offset_in_model = pcb_false;
+
+	ASSERT_CAIRO;
+
+	s1 = AttributeGetFromList(&(element->Attributes), "BBoard::Model");
+	if (s1) {
+		s = pcb_strdup(s1);
+		if (!s)
+			return;
+
+		if ((s2 = strchr(s, ' ')) != NULL) {
+			*s2 = 0;
+			offset_in_model = bboard_parse_offset(s2 + 1, &ox, &oy);
+		}
+		if (!EMPTY_STRING_P(VALUE_NAME(element))) {
+			fname = bboard_get_model_filename(s, VALUE_NAME(element), pcb_true);
+			if (!fname)
+				fname = bboard_get_model_filename(s, VALUE_NAME(element), pcb_false);
+		}
+		if (!fname)
+			fname = bboard_get_model_filename(s, NULL, pcb_false);
+
+		if (s)
+			free(s);
+	}
+	if (!fname) {
+		/* invalidate offset from BBoard::Model, if such model does not exist */
+		offset_in_model = pcb_false;
+
+		s = AttributeGetFromList(&(element->Attributes), "Footprint::File");
+		if (s) {
+			if (!EMPTY_STRING_P(VALUE_NAME(element))) {
+				fname = bboard_get_model_filename(s, VALUE_NAME(element), pcb_true);
+				if (!fname)
+					fname = bboard_get_model_filename(s, VALUE_NAME(element), pcb_false);
+			}
+			if (!fname)
+				fname = bboard_get_model_filename(s, NULL, pcb_false);
+		}
+	}
+	if (!fname) {
+		s = DESCRIPTION_NAME(element);
+		if (!EMPTY_STRING_P(DESCRIPTION_NAME(element))) {
+			if (!EMPTY_STRING_P(VALUE_NAME(element))) {
+				fname = bboard_get_model_filename(DESCRIPTION_NAME(element), VALUE_NAME(element), pcb_true);
+				if (!fname)
+					fname = bboard_get_model_filename(DESCRIPTION_NAME(element), VALUE_NAME(element), pcb_false);
+
+			}
+			if (!fname)
+				fname = bboard_get_model_filename(DESCRIPTION_NAME(element), NULL, pcb_false);
+		}
+	}
+
+	if (!fname)
+		return;
+
+	sfc = cairo_image_surface_create_from_png(fname);
+
+	free(fname);
+
+	if (sfc) {
+		w = cairo_image_surface_get_width(sfc);
+		h = cairo_image_surface_get_height(sfc);
+
+		/* read offest from attribute */
+		if (!offset_in_model) {
+			s = AttributeGetFromList(&(element->Attributes), "BBoard::Offset");
+
+			/* Parse values with units... */
+			if (s) {
+				bboard_parse_offset(s, &ox, &oy);
+			}
+		}
+		ex = bboard_scale_coord(element->MarkX);
+		ey = bboard_scale_coord(element->MarkY);
+
+		cairo_save(bboard_cairo_ctx);
+
+		if ((model_angle = AttributeGetFromList(&(element->Attributes), "Footprint::RotationTracking")) != NULL) {
+			sscanf(model_angle, "%lf", &tmp_angle);
+		}
+
+
+		cairo_translate(bboard_cairo_ctx, ex, ey);
+		if (TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) {
+			cairo_scale(bboard_cairo_ctx, 1, -1);
+		}
+		cairo_rotate(bboard_cairo_ctx, -tmp_angle * M_PI / 180.);
+		cairo_set_source_surface(bboard_cairo_ctx, sfc, bboard_scale_coord(ox) - w / 2, bboard_scale_coord(oy) - h / 2);
+		cairo_paint(bboard_cairo_ctx);
+
+		cairo_restore(bboard_cairo_ctx);
+
+		cairo_surface_destroy(sfc);
+	}
+}
+
+
+static void bboard_do_export(HID_Attr_Val * options)
+{
+	int i;
+	int clr_r, clr_g, clr_b;
+	LayerType *layer;
+
+
+	if (!options) {
+		bboard_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			bboard_values[i] = bboard_options[i].default_val;
+		options = bboard_values;
+	}
+	bboard_filename = options[HA_bboardfile].str_value;
+	if (!bboard_filename)
+		bboard_filename = "unknown.png";
+
+	bboard_bgcolor = options[HA_bgcolor].str_value;
+	if (!bboard_bgcolor)
+		bboard_bgcolor = "FFFFFF";
+
+	memset(group_data, 0, sizeof(group_data));
+#ifdef SOLDER_LAYER
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + SOLDER_LAYER)].solder = 1;
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + COMPONENT_LAYER)].component = 1;
+#else
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + BOTTOM_SIDE)].solder = 1;
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + TOP_SIDE)].component = 1;
+#endif
+
+
+	for (i = 0; i < max_copper_layer; i++) {
+		layer = PCB->Data->Layer + i;
+		if (linelist_length(&layer->Line) > 0)
+			group_data[GetLayerGroupNumberByNumber(i)].draw = 1;
+	}
+
+	bboard_init_board_cairo(PCB->MaxWidth, PCB->MaxHeight, bboard_bgcolor, options[HA_antialias].int_value);
+
+	/* write out components on solder side */
+	ELEMENT_LOOP(PCB->Data);
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) {
+		bboard_export_element_cairo(element, 1);
+	}
+	END_LOOP;
+
+	/* write out components on component side */
+	ELEMENT_LOOP(PCB->Data);
+	if (!TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) {
+		bboard_export_element_cairo(element, 0);
+	}
+	END_LOOP;
+
+	/* draw all wires from all valid layers */
+	for (i = max_copper_layer - 1; i >= 0; i--) {
+		if (bboard_validate_layer(PCB->Data->Layer[i].Name, GetLayerGroupNumberByNumber(i), options[HA_skipsolder].int_value)) {
+			bboard_get_layer_color(&(PCB->Data->Layer[i]), &clr_r, &clr_g, &clr_b);
+			bboard_set_color_cairo(clr_r, clr_g, clr_b);
+			LINE_LOOP(&(PCB->Data->Layer[i]));
+			{
+				bboard_draw_line_cairo(line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, line->Thickness);
+			}
+			END_LOOP;
+			ARC_LOOP(&(PCB->Data->Layer[i]));
+			{
+#warning TODO: remove x1;y1;x2;y2
+				bboard_draw_arc_cairo(/*arc->Point1.X, arc->Point1.Y,
+															arc->Point2.X, arc->Point2.Y,*/
+															arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta, arc->Thickness);
+			}
+			END_LOOP;
+		}
+	}
+
+	bboard_finish_board_cairo();
+
+}
+
+static void bboard_parse_arguments(int *argc, char ***argv)
+{
+	hid_parse_command_line(argc, argv);
+}
+
+
+static void bboard_calibrate(double xval, double yval)
+{
+	fprintf(stderr, "HID error: pcb called unimplemented breaboard function bboard_calibrate.\n");
+	abort();
+}
+
+static void bboard_set_crosshair(int x, int y, int action)
+{
+}
+
+static HID bboard_hid;
+
+
+pcb_uninit_t hid_export_bboard_init()
+{
+	memset(&bboard_hid, 0, sizeof(bboard_hid));
+
+	common_nogui_init(&bboard_hid);
+
+	bboard_hid.struct_size = sizeof(bboard_hid);
+	bboard_hid.name = "bboard";
+	bboard_hid.description = "Breadboard export";
+	bboard_hid.exporter = 1;
+
+	bboard_hid.get_export_options = bboard_get_export_options;
+	bboard_hid.do_export = bboard_do_export;
+	bboard_hid.parse_arguments = bboard_parse_arguments;
+	bboard_hid.calibrate = bboard_calibrate;
+	bboard_hid.set_crosshair = bboard_set_crosshair;
+	hid_register_hid(&bboard_hid);
+
+	hid_register_attributes(bboard_options, sizeof(bboard_options) / sizeof(bboard_options[0]), bboard_cookie, 0);
+	return NULL;
+}
+
diff --git a/src_plugins/export_bom/Makefile b/src_plugins/export_bom/Makefile
new file mode 100644
index 0000000..cafd8e5
--- /dev/null
+++ b/src_plugins/export_bom/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_bom
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_bom/Plug.tmpasm b/src_plugins/export_bom/Plug.tmpasm
new file mode 100644
index 0000000..1d1b730
--- /dev/null
+++ b/src_plugins/export_bom/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_bom}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_bom/bom.o @]
+
+switch /local/pcb/export_bom/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_bom/README b/src_plugins/export_bom/README
new file mode 100644
index 0000000..2ba4a2d
--- /dev/null
+++ b/src_plugins/export_bom/README
@@ -0,0 +1,5 @@
+Export bom (Bill of Materials)
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_bom/bom.c b/src_plugins/export_bom/bom.c
new file mode 100644
index 0000000..313c9f7
--- /dev/null
+++ b/src_plugins/export_bom/bom.c
@@ -0,0 +1,325 @@
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_init.h"
+
+const char *bom_cookie = "bom HID";
+
+static HID_Attribute bom_options[] = {
+/* %start-doc options "8 BOM Creation"
+ at ftable @code
+ at item --bomfile <string>
+Name of the BOM output file.
+ at end ftable
+%end-doc
+*/
+	{"bomfile", "Name of the BOM output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_bomfile 0
+/* %start-doc options "8 BOM Creation"
+ at ftable @code
+ at item --xyfile <string>
+Name of the XY output file.
+ at end ftable
+%end-doc
+*/
+};
+
+#define NUM_OPTIONS (sizeof(bom_options)/sizeof(bom_options[0]))
+
+static HID_Attr_Val bom_values[NUM_OPTIONS];
+
+static const char *bom_filename;
+
+typedef struct _StringList {
+	char *str;
+	struct _StringList *next;
+} StringList;
+
+typedef struct _BomList {
+	char *descr;
+	char *value;
+	int num;
+	StringList *refdes;
+	struct _BomList *next;
+} BomList;
+
+static HID_Attribute *bom_get_export_options(int *n)
+{
+	static char *last_bom_filename = NULL;
+
+	if (PCB)
+		derive_default_filename(PCB->Filename, &bom_options[HA_bomfile], ".bom", &last_bom_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return bom_options;
+}
+
+/* ---------------------------------------------------------------------------
+ * prints a centroid file in a format which includes data needed by a
+ * pick and place machine.  Further formatting for a particular factory setup
+ * can easily be generated with awk or perl.  In addition, a bill of materials
+ * file is generated which can be used for checking stock and purchasing needed
+ * materials.
+ * returns != zero on error
+ */
+char *CleanBOMString(const char *in)
+{
+	char *out;
+	int i;
+
+	if ((out = (char *) malloc((strlen(in) + 1) * sizeof(char))) == NULL) {
+		fprintf(stderr, "Error:  CleanBOMString() malloc() failed\n");
+		exit(1);
+	}
+
+	/*
+	 * copy over in to out with some character conversions.
+	 * Go all the way to then end to get the terminating \0
+	 */
+	for (i = 0; i <= strlen(in); i++) {
+		switch (in[i]) {
+		case '"':
+			out[i] = '\'';
+			break;
+		default:
+			out[i] = in[i];
+		}
+	}
+
+	return out;
+}
+
+static StringList *string_insert(char *str, StringList * list)
+{
+	StringList *newlist, *cur;
+
+	if ((newlist = (StringList *) malloc(sizeof(StringList))) == NULL) {
+		fprintf(stderr, "malloc() failed in string_insert()\n");
+		exit(1);
+	}
+
+	newlist->next = NULL;
+	newlist->str = pcb_strdup(str);
+
+	if (list == NULL)
+		return (newlist);
+
+	cur = list;
+	while (cur->next != NULL)
+		cur = cur->next;
+
+	cur->next = newlist;
+
+	return (list);
+}
+
+static BomList *bom_insert(char *refdes, char *descr, char *value, BomList * bom)
+{
+	BomList *newlist, *cur, *prev = NULL;
+
+	if (bom == NULL) {
+		/* this is the first element so automatically create an entry */
+		if ((newlist = (BomList *) malloc(sizeof(BomList))) == NULL) {
+			fprintf(stderr, "malloc() failed in bom_insert()\n");
+			exit(1);
+		}
+
+		newlist->next = NULL;
+		newlist->descr = pcb_strdup(descr);
+		newlist->value = pcb_strdup(value);
+		newlist->num = 1;
+		newlist->refdes = string_insert(refdes, NULL);
+		return (newlist);
+	}
+
+	/* search and see if we already have used one of these
+	   components */
+	cur = bom;
+	while (cur != NULL) {
+		if ((NSTRCMP(descr, cur->descr) == 0) && (NSTRCMP(value, cur->value) == 0)) {
+			cur->num++;
+			cur->refdes = string_insert(refdes, cur->refdes);
+			break;
+		}
+		prev = cur;
+		cur = cur->next;
+	}
+
+	if (cur == NULL) {
+		if ((newlist = (BomList *) malloc(sizeof(BomList))) == NULL) {
+			fprintf(stderr, "malloc() failed in bom_insert()\n");
+			exit(1);
+		}
+
+		prev->next = newlist;
+
+		newlist->next = NULL;
+		newlist->descr = pcb_strdup(descr);
+		newlist->value = pcb_strdup(value);
+		newlist->num = 1;
+		newlist->refdes = string_insert(refdes, NULL);
+	}
+
+	return (bom);
+
+}
+
+/*
+ * If fp is not NULL then print out the bill of materials contained in
+ * bom.  Either way, free all memory which has been allocated for bom.
+ */
+static void print_and_free(FILE * fp, BomList * bom)
+{
+	BomList *lastb;
+	StringList *lasts;
+	char *descr, *value;
+
+	while (bom != NULL) {
+		if (fp) {
+			descr = CleanBOMString(bom->descr);
+			value = CleanBOMString(bom->value);
+			fprintf(fp, "%d,\"%s\",\"%s\",", bom->num, descr, value);
+			free(descr);
+			free(value);
+		}
+
+		while (bom->refdes != NULL) {
+			if (fp) {
+				fprintf(fp, "%s ", bom->refdes->str);
+			}
+			free(bom->refdes->str);
+			lasts = bom->refdes;
+			bom->refdes = bom->refdes->next;
+			free(lasts);
+		}
+		if (fp) {
+			fprintf(fp, "\n");
+		}
+		lastb = bom;
+		bom = bom->next;
+		free(lastb);
+	}
+}
+
+static int PrintBOM(void)
+{
+	char utcTime[64];
+	time_t currenttime;
+	FILE *fp;
+	BomList *bom = NULL;
+
+	/* Create a portable timestamp. */
+	currenttime = time(NULL);
+	{
+		/* avoid gcc complaints */
+		const char *fmt = "%c UTC";
+		strftime(utcTime, sizeof(utcTime), fmt, gmtime(&currenttime));
+	}
+
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/* insert this component into the bill of materials list */
+		bom = bom_insert((char *) UNKNOWN(NAMEONPCB_NAME(element)),
+										 (char *) UNKNOWN(DESCRIPTION_NAME(element)), (char *) UNKNOWN(VALUE_NAME(element)), bom);
+	}
+	END_LOOP;
+
+	fp = fopen(bom_filename, "w");
+	if (!fp) {
+		gui->log("Cannot open file %s for writing\n", bom_filename);
+		print_and_free(NULL, bom);
+		return 1;
+	}
+
+	fprintf(fp, "# $Id");
+	fprintf(fp, "$\n");
+	fprintf(fp, "# PcbBOM Version 1.0\n");
+	fprintf(fp, "# Date: %s\n", utcTime);
+	fprintf(fp, "# Author: %s\n", pcb_author());
+	fprintf(fp, "# Title: %s - PCB BOM\n", UNKNOWN(PCB->Name));
+	fprintf(fp, "# Quantity, Description, Value, RefDes\n");
+	fprintf(fp, "# --------------------------------------------\n");
+
+	print_and_free(fp, bom);
+
+	fclose(fp);
+
+	return (0);
+}
+
+static void bom_do_export(HID_Attr_Val * options)
+{
+	int i;
+
+	if (!options) {
+		bom_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			bom_values[i] = bom_options[i].default_val;
+		options = bom_values;
+	}
+
+	bom_filename = options[HA_bomfile].str_value;
+	if (!bom_filename)
+		bom_filename = "pcb-out.bom";
+
+	PrintBOM();
+}
+
+static int bom_usage(const char *topic)
+{
+	fprintf(stderr, "\nbom exporter command line arguments:\n\n");
+	hid_usage(bom_options, sizeof(bom_options) / sizeof(bom_options[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x bom foo.pcb [bom_options]\n\n");
+	return 0;
+}
+
+
+static void bom_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(bom_options, sizeof(bom_options) / sizeof(bom_options[0]), bom_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+HID bom_hid;
+
+pcb_uninit_t hid_export_bom_init()
+{
+	memset(&bom_hid, 0, sizeof(HID));
+
+	common_nogui_init(&bom_hid);
+
+	bom_hid.struct_size = sizeof(HID);
+	bom_hid.name = "bom";
+	bom_hid.description = "Exports a Bill of Materials";
+	bom_hid.exporter = 1;
+
+	bom_hid.get_export_options = bom_get_export_options;
+	bom_hid.do_export = bom_do_export;
+	bom_hid.parse_arguments = bom_parse_arguments;
+
+	bom_hid.usage = bom_usage;
+
+	hid_register_hid(&bom_hid);
+	return NULL;
+}
diff --git a/src_plugins/export_dsn/Makefile b/src_plugins/export_dsn/Makefile
new file mode 100644
index 0000000..484a0ad
--- /dev/null
+++ b/src_plugins/export_dsn/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_dsn
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_dsn/Plug.tmpasm b/src_plugins/export_dsn/Plug.tmpasm
new file mode 100644
index 0000000..b487f60
--- /dev/null
+++ b/src_plugins/export_dsn/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_dsn}
+put /local/pcb/mod/OBJS_C99 [@ $(PLUGDIR)/export_dsn/dsn.o @]
+
+switch /local/pcb/export_dsn/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_dsn/README b/src_plugins/export_dsn/README
new file mode 100644
index 0000000..cf2339d
--- /dev/null
+++ b/src_plugins/export_dsn/README
@@ -0,0 +1,5 @@
+Export specctra .dsn files
+
+#state: Work-in-progress
+#default: disable
+#implements: export
diff --git a/src_plugins/export_dsn/dsn.c b/src_plugins/export_dsn/dsn.c
new file mode 100644
index 0000000..c118a5b
--- /dev/null
+++ b/src_plugins/export_dsn/dsn.c
@@ -0,0 +1,589 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  Specctra .dsn export HID
+ *  Copyright (C) 2008, 2011 Josh Jordan, Dan McMahill, and Jared Casper
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+This program exports specctra .dsn files from geda .pcb files.
+By Josh Jordan and Dan McMahill, modified from bom.c
+  -- Updated to use Coord and other fixes by Jared Casper 16 Sep 2011
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "rats.h"
+#include "buffer.h"
+#include "change.h"
+#include "draw.h"
+#include "set.h"
+#include "undo.h"
+#include "pcb-printf.h"
+#include "create.h"
+#include "polygon.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+#include "hid.h"
+#include "hid_draw_helpers.h"
+#include "hid_nogui.h"
+#include "hid_actions.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "plugins.h"
+
+static const char *dsn_cookie = "dsn exporter";
+
+static Coord trackwidth = 8;  /* user options defined in export dialog */
+static Coord clearance = 8;
+static Coord viawidth = 45;
+static Coord viadrill = 25;
+
+static HID dsn_hid;
+
+static HID_Attribute dsn_options[] = {
+	{"dsnfile", "SPECCTRA output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_dsnfile 0
+	{"trackwidth", "track width in mils",
+	 HID_Coord, PCB_MIL_TO_COORD(0), PCB_MIL_TO_COORD(100), {0, 0, 0, PCB_MIL_TO_COORD(8)}, 0, 0},
+#define HA_trackwidth 1
+	{"clearance", "clearance in mils",
+	 HID_Coord, PCB_MIL_TO_COORD(0), PCB_MIL_TO_COORD(100), {0, 0, 0, PCB_MIL_TO_COORD(8)}, 0, 0},
+#define HA_clearance 2
+	{"viawidth", "via width in mils",
+	 HID_Coord, PCB_MIL_TO_COORD(0), PCB_MIL_TO_COORD(100), {0, 0, 0, PCB_MIL_TO_COORD(27)}, 0,
+	 0},
+#define HA_viawidth 3
+	{"viadrill", "via drill diameter in mils",
+	 HID_Coord, PCB_MIL_TO_COORD(0), PCB_MIL_TO_COORD(100), {0, 0, 0, PCB_MIL_TO_COORD(15)}, 0,
+	 0},
+#define HA_viadrill 4
+};
+
+#define NUM_OPTIONS (sizeof(dsn_options)/sizeof(dsn_options[0]))
+REGISTER_ATTRIBUTES(dsn_options, dsn_cookie)
+
+static HID_Attr_Val dsn_values[NUM_OPTIONS];
+
+static const char *dsn_filename;
+
+static HID_Attribute *dsn_get_export_options(int *n)
+{
+	static char *last_dsn_filename = 0;
+	if (PCB) {
+		derive_default_filename(PCB->Filename, &dsn_options[HA_dsnfile], ".dsn", &last_dsn_filename);
+	}
+	if (n)
+		*n = NUM_OPTIONS;
+	return dsn_options;
+}
+
+
+/* this function is mostly ripped from bom.c */
+static PointType get_centroid(ElementType * element)
+{
+	PointType centroid;
+	double sumx = 0.0, sumy = 0.0;
+	int pin_cnt = 0;
+
+	PIN_LOOP(element);
+	{
+		sumx += (double) pin->X;
+		sumy += (double) pin->Y;
+		pin_cnt++;
+	}
+	END_LOOP;
+
+	PAD_LOOP(element);
+	{
+		sumx += (pad->Point1.X + pad->Point2.X) / 2.0;
+		sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0;
+		pin_cnt++;
+	}
+	END_LOOP;
+
+	if (pin_cnt > 0) {
+		centroid.X = sumx / (double) pin_cnt;
+		centroid.Y = PCB->MaxHeight - (sumy / (double) pin_cnt);
+	}
+	else {
+		centroid.X = 0;
+		centroid.Y = 0;
+	}
+	return centroid;
+}
+
+static GList *layerlist = NULL;  /* contain routing layers */
+
+static void print_structure(FILE * fp)
+{
+	int group, top_group, bot_group, top_layer, bot_layer;
+
+	pcb_layer_group_list(PCB_LYT_TOP | PCB_LYT_SILK, &top_group, 1);
+	pcb_layer_group_list(PCB_LYT_BOTTOM | PCB_LYT_SILK, &bot_group, 1);
+
+	top_layer = PCB->LayerGroups.Entries[top_group][0];
+	bot_layer = PCB->LayerGroups.Entries[bot_group][0];
+
+
+	g_list_free(layerlist);				/* might be around from the last export */
+
+	if (PCB->Data->Layer[top_layer].On) {
+		layerlist = g_list_append(layerlist, &PCB->Data->Layer[top_layer]);
+	}
+	else {
+		gui->log("WARNING! DSN export does not include the top layer. "
+						 "Router will consider an inner layer to be the \"top\" layer.\n");
+	}
+
+	for (group = 0; group < max_group; group++) {
+		LayerType *first_layer;
+		if (group == top_group || group == bot_group)
+			continue;
+
+		if (PCB->LayerGroups.Number[group] < 1)
+			continue;
+
+		first_layer = &PCB->Data->Layer[PCB->LayerGroups.Entries[group][0]];
+		if (!first_layer->On)
+			continue;
+
+		layerlist = g_list_append(layerlist, first_layer);
+
+		if (group < top_group) {
+			gui->log("WARNING! DSN export moved layer group with the \"%s\" layer "
+							 "after the top layer group.  DSN files must have the top " "layer first.\n", first_layer->Name);
+		}
+
+		if (group > bot_group) {
+			gui->log("WARNING! DSN export moved layer group with the \"%s\" layer "
+							 "before the bottom layer group.  DSN files must have the " "bottom layer last.\n", first_layer->Name);
+		}
+
+		GROUP_LOOP(PCB->Data, group);
+		{
+			if (entry > 0) {
+				gui->log("WARNING! DSN export squashed layer \"%s\" into layer "
+								 "\"%s\", DSN files do not have layer groups.", layer->Name, first_layer->Name);
+			}
+		}
+		END_LOOP;
+	}
+
+	if (PCB->Data->Layer[bot_layer].On) {
+		layerlist = g_list_append(layerlist, &PCB->Data->Layer[bot_layer]);
+	}
+	else {
+		gui->log("WARNING! DSN export does not include the bottom layer. "
+						 "Router will consider an inner layer to be the \"bottom\" layer.\n");
+	}
+
+	fprintf(fp, "  (structure\n");
+
+	for (GList * iter = layerlist; iter; iter = g_list_next(iter)) {
+		LayerType *layer = iter->data;
+		char *layeropts = pcb_strdup("(type signal)");
+		/* see if layer has same name as a net and make it a power layer */
+		/* loop thru all nets */
+		for (int ni = 0; ni < PCB->NetlistLib[NETLIST_EDITED].MenuN; ni++) {
+			char *nname;
+			nname = PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Name + 2;
+			if (!strcmp(layer->Name, nname)) {
+				g_free(layeropts);
+				layeropts = pcb_strdup_printf("(type power) (use_net \"%s\")", layer->Name);
+			}
+		}
+		fprintf(fp, "    (layer \"%s\"\n", layer->Name);
+		fprintf(fp, "      %s\n", layeropts);
+		fprintf(fp, "    )\n");
+		g_free(layeropts);
+	}
+
+	/* PCB outline */
+	pcb_fprintf(fp, "    (boundary\n");
+	pcb_fprintf(fp, "      (rect pcb 0.0 0.0 %.6mm %.6mm)\n", PCB->MaxWidth, PCB->MaxHeight);
+	pcb_fprintf(fp, "    )\n");
+	pcb_fprintf(fp, "    (via via_%ld_%ld)\n", viawidth, viadrill);
+
+	/* DRC rules */
+	pcb_fprintf(fp, "    (rule\n");
+	pcb_fprintf(fp, "      (width %mm)\n", trackwidth);
+	pcb_fprintf(fp, "      (clear %mm)\n", clearance);
+	pcb_fprintf(fp, "      (clear %mm (type wire_area))\n", clearance);
+	pcb_fprintf(fp, "      (clear %mm (type via_smd via_pin))\n", clearance);
+	pcb_fprintf(fp, "      (clear %mm (type smd_smd))\n", clearance);
+	pcb_fprintf(fp, "      (clear %mm (type default_smd))\n", clearance);
+	pcb_fprintf(fp, "    )\n  )\n");
+}
+
+static void print_placement(FILE * fp)
+{
+	fprintf(fp, "  (placement\n");
+	ELEMENT_LOOP(PCB->Data);
+	{
+		char *ename;
+		PointType ecentroid = get_centroid(element);
+		char *side = TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? "back" : "front";
+		ename = NAMEONPCB_NAME(element);
+		if (ename != NULL)
+			ename = pcb_strdup(ename);
+		else
+			ename = pcb_strdup("null");
+		pcb_fprintf(fp, "    (component %d\n", element->ID);
+		pcb_fprintf(fp, "      (place \"%s\" %.6mm %.6mm %s 0 (PN 0))\n", ename, ecentroid.X, ecentroid.Y, side);
+		pcb_fprintf(fp, "    )\n");
+		g_free(ename);
+	}
+	END_LOOP;
+
+	VIA_LOOP(PCB->Data);
+	{ /* add mounting holes */
+		pcb_fprintf(fp, "    (component %d\n", via->ID);
+		pcb_fprintf(fp, "      (place %d %.6mm %.6mm %s 0 (PN 0))\n", via->ID, via->X, (PCB->MaxHeight - via->Y), "front");
+		pcb_fprintf(fp, "    )\n");
+	}
+	END_LOOP;
+	fprintf(fp, "  )\n");
+}
+
+static void print_library(FILE * fp)
+{
+	GList *pads = NULL, *iter; /* contain unique pad names */
+	gchar *padstack;
+	fprintf(fp, "  (library\n");
+	ELEMENT_LOOP(PCB->Data);
+	{
+		int partside = TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? g_list_length(layerlist) - 1 : 0;
+		int partsidesign = TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? -1 : 1;
+		PointType centroid = get_centroid(element);
+		fprintf(fp, "    (image %ld\n", element->ID); /* map every element by ID */
+		/* loop thru pins and pads to add to image */
+		PIN_LOOP(element);
+		{
+			Coord ty;
+			Coord pinthickness;
+			Coord lx, ly;  /* hold local pin coordinates */
+			ty = PCB->MaxHeight - pin->Y;
+			pinthickness = pin->Thickness;
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin))
+				padstack = pcb_strdup_printf("Th_square_%mI", pinthickness);
+			else
+				padstack = pcb_strdup_printf("Th_round_%mI", pinthickness);
+			lx = (pin->X - centroid.X) * partsidesign;
+			ly = (centroid.Y - ty) * -1;
+
+			if (!pin->Number) { /* if pin is null just make it a keepout */
+				for (GList * iter = layerlist; iter; iter = g_list_next(iter)) {
+					LayerType *lay = iter->data;
+					pcb_fprintf(fp, "      (keepout \"\" (circle \"%s\" %.6mm %.6mm %.6mm))\n", lay->Name, pinthickness, lx, ly);
+				}
+			}
+			else {
+				pcb_fprintf(fp, "      (pin %s \"%s\" %.6mm %.6mm)\n", padstack, pin->Number, lx, ly);
+			}
+
+			if (!g_list_find_custom(pads, padstack, (GCompareFunc) strcmp))
+				pads = g_list_append(pads, padstack);
+			else
+				g_free(padstack);
+		}
+		END_LOOP;
+
+		PAD_LOOP(element);
+		{
+			Coord xlen, ylen, xc, yc, p1y, p2y;
+			Coord lx, ly;  /* store local coordinates for pins */
+			p1y = PCB->MaxHeight - pad->Point1.Y;
+			p2y = PCB->MaxHeight - pad->Point2.Y;
+			/* pad dimensions are unusual-
+			   the width is thickness and length is point difference plus thickness */
+			xlen = ABS(pad->Point1.X - pad->Point2.X);
+			if (xlen == 0) {
+				xlen = pad->Thickness;
+				ylen = ABS(p1y - p2y) + pad->Thickness;
+			}
+			else {
+				ylen = pad->Thickness;
+				xlen += pad->Thickness;
+			}
+			xc = (pad->Point1.X + pad->Point2.X) / 2;
+			yc = (p1y + p2y) / 2;
+			lx = (xc - centroid.X) * partsidesign;
+			ly = (centroid.Y - yc) * -1;
+			padstack = pcb_strdup_printf("Smd_rect_%mIx%mI", xlen, ylen);
+
+			if (!pad->Number) {				/* if pad is null just make it a keepout */
+				LayerType *lay;
+				lay = g_list_nth_data(layerlist, partside);
+				pcb_fprintf(fp, "      (keepout \"\" (rect \"%s\" %.6mm %.6mm %.6mm %.6mm))\n",
+										lay->Name, lx - xlen / 2, ly - ylen / 2, lx + xlen / 2, ly + ylen / 2);
+			}
+			else {
+				pcb_fprintf(fp, "      (pin %s \"%s\" %.6mm %.6mm)\n", padstack, pad->Number, lx, ly);
+			}
+			if (!g_list_find_custom(pads, padstack, (GCompareFunc) strcmp))
+				pads = g_list_append(pads, padstack);
+			else
+				g_free(padstack);
+		}
+		END_LOOP;
+		fprintf(fp, "    )\n");
+	}
+	END_LOOP;
+
+	VIA_LOOP(PCB->Data);
+	{ /* add mounting holes and vias */
+		fprintf(fp, "    (image %ld\n", via->ID); /* map every via by ID */
+		/* for mounting holes, clearance is added to thickness for higher total clearance */
+		padstack = pcb_strdup_printf("Th_round_%mI", via->Thickness + via->Clearance);
+		fprintf(fp, "      (pin %s 1 0 0)\n", padstack);	/* only 1 pin, 0,0 puts it right on component placement spot */
+		fprintf(fp, "    )\n");
+		if (!g_list_find_custom(pads, padstack, (GCompareFunc) strcmp))
+			pads = g_list_append(pads, padstack);
+		else
+			g_free(padstack);
+	}
+	END_LOOP;
+
+	/* loop thru padstacks and define them all */
+	for (iter = pads; iter; iter = g_list_next(iter)) {
+		Coord dim1, dim2;
+		padstack = iter->data;
+		fprintf(fp, "    (padstack %s\n", padstack);
+
+		/* print info about pad here */
+		if (sscanf(padstack, "Smd_rect_%ldx%ld", &dim1, &dim2) == 2) {	/* then pad is smd */
+			pcb_fprintf(fp,
+									"      (shape (rect \"%s\" %.6mm %.6mm %.6mm %.6mm))\n",
+									((LayerType *) (g_list_first(layerlist)->data))->Name, dim1 / -2, dim2 / -2, dim1 / 2, dim2 / 2);
+		}
+		else if (sscanf(padstack, "Th_square_%ld", &dim1) == 1) {
+			pcb_fprintf(fp, "      (shape (rect signal %.6mm %.6mm %.6mm %.6mm))\n", dim1 / -2, dim1 / -2, dim1 / 2, dim1 / 2);
+		}
+		else {
+			sscanf(padstack, "Th_round_%ld", &dim1);
+			pcb_fprintf(fp, "      (shape (circle signal %.6mm))\n", dim1);
+		}
+		fprintf(fp, "      (attach off)\n");
+		fprintf(fp, "    )\n");
+	}
+
+	/* add padstack for via */
+	pcb_fprintf(fp, "    (padstack via_%ld_%ld\n", viawidth, viadrill);
+	pcb_fprintf(fp, "      (shape (circle signal %.6mm))\n", viawidth);
+	pcb_fprintf(fp, "      (attach off)\n    )\n");
+	pcb_fprintf(fp, "  )\n");
+	g_list_foreach(pads, (GFunc) g_free, NULL);
+	g_list_free(pads);
+}
+
+static void print_quoted_pin(FILE * fp, const char *s)
+{
+	char *hyphen_pos = strchr(s, '-');
+	if (!hyphen_pos) {
+		fprintf(fp, " %s", s);
+	}
+	else {
+		char refdes_name[1024];
+		int copy_len = hyphen_pos - s;
+		if (copy_len >= sizeof(refdes_name))
+			copy_len = sizeof(refdes_name) - 1;
+		strncpy(refdes_name, s, copy_len);
+		refdes_name[copy_len] = 0;
+		fprintf(fp, " \"%s\"-\"%s\"", refdes_name, hyphen_pos + 1);
+	}
+}
+
+static void print_network(FILE * fp)
+{
+	int ni, nei;
+	fprintf(fp, "  (network\n");
+	for (ni = 0; ni < PCB->NetlistLib[NETLIST_EDITED].MenuN; ni++) {
+		fprintf(fp, "    (net \"%s\"\n", PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Name + 2);
+		fprintf(fp, "      (pins");
+		for (nei = 0; nei < PCB->NetlistLib[NETLIST_EDITED].Menu[ni].EntryN; nei++)
+			print_quoted_pin(fp, PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Entry[nei].ListEntry);
+		fprintf(fp, ")\n");
+		fprintf(fp, "    )\n");
+	}
+
+	fprintf(fp, "    (class geda_default");
+	for (ni = 0; ni < PCB->NetlistLib[NETLIST_EDITED].MenuN; ni++) {
+		fprintf(fp, " \"%s\"", PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Name + 2);
+	}
+	pcb_fprintf(fp, "\n");
+	pcb_fprintf(fp, "      (circuit\n");
+	pcb_fprintf(fp, "        (use_via via_%ld_%ld)\n", viawidth, viadrill);
+	pcb_fprintf(fp, "      )\n");
+	pcb_fprintf(fp, "      (rule (width %.6mm))\n    )\n  )\n", trackwidth);
+}
+
+static void print_wires(FILE * fp)
+{
+	GList *iter;
+	LayerType *lay;
+	fprintf(fp, "    (wiring\n");
+
+	for (iter = layerlist; iter; iter = g_list_next(iter)) {
+		lay = iter->data;
+		LINE_LOOP(lay);
+		{
+			pcb_fprintf(fp,
+									"        (wire (path %s %.6mm %.6mm %.6mm %.6mm %.6mm)\n",
+									lay->Name, line->Thickness, line->Point1.X,
+									(PCB->MaxHeight - line->Point1.Y), line->Point2.X, (PCB->MaxHeight - line->Point2.Y));
+			fprintf(fp, "            (type protect))\n");
+		}
+		END_LOOP;
+	}
+	fprintf(fp, "\n    )\n)\n"); /* close all braces */
+}
+
+static int PrintSPECCTRA(void)
+{
+	FILE *fp;
+	/* Print out the dsn .dsn file. */
+	fp = fopen(dsn_filename, "w");
+	if (!fp) {
+		gui->log("Cannot open file %s for writing\n", dsn_filename);
+		return 1;
+	}
+
+	/* pcb [required] */
+	fprintf(fp, "(pcb %s\n", ((PCB->Name) && *(PCB->Name) ? (PCB->Name) : "notnamed"));
+
+	/* parser descriptor [optional] */
+	fprintf(fp, "  (parser\n");
+	fprintf(fp, "    (string_quote \")\n");
+	fprintf(fp, "    (space_in_quoted_tokens on)\n");
+	fprintf(fp, "    (host_cad \"gEDA pcb-rnd\")\n");
+	fprintf(fp, "    (host_version \"%s\")\n", VERSION);
+	fprintf(fp, "  )\n");
+
+	/* capacitance resolution descriptor [optional] */
+
+	/* conductance resolution descriptor [optional] */
+
+	/* current resolution descriptor [optional] */
+
+	/* inductance resolution descriptor [optional] */
+
+	/* resistance resolution descriptor [optional] */
+
+	/* resolution descriptor [optional] */
+	fprintf(fp, "  (resolution mm 1000000)\n");
+
+	/* time resolution descriptor [optional] */
+
+	/* voltage resolution descriptor [optional] */
+
+	/* unit descriptor [optional] */
+
+	/* structure descriptor [required] */
+	print_structure(fp);
+
+	/* placement descriptor [optional] */
+	print_placement(fp);
+
+	/* library descriptor [required] */
+	print_library(fp);
+
+	/* floor plan descriptor [optional] */
+
+	/* part library descriptor [optional] */
+
+	/* network descriptor [required] */
+	print_network(fp);
+
+	/* wiring descriptor [optional] */
+	print_wires(fp);
+
+	/* color descriptor [optional] */
+
+	fclose(fp);
+
+	return (0);
+}
+
+
+static void dsn_do_export(HID_Attr_Val * options)
+{
+	int i;
+	if (!options) {
+		dsn_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			dsn_values[i] = dsn_options[i].default_val;
+		options = dsn_values;
+	}
+	dsn_filename = options[HA_dsnfile].str_value;
+	if (!dsn_filename)
+		dsn_filename = "pcb-out.dsn";
+
+	trackwidth = options[HA_trackwidth].coord_value;
+	clearance = options[HA_clearance].coord_value;
+	viawidth = options[HA_viawidth].coord_value;
+	viadrill = options[HA_viadrill].coord_value;
+	PrintSPECCTRA();
+}
+
+static void dsn_parse_arguments(int *argc, char ***argv)
+{
+	hid_parse_command_line(argc, argv);
+}
+
+static void hid_dsn_uninit()
+{
+
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_export_dsn_init()
+{
+	memset(&dsn_hid, 0, sizeof(HID));
+	common_nogui_init(&dsn_hid);
+
+	dsn_hid.struct_size = sizeof(HID);
+	dsn_hid.name = "dsn";
+	dsn_hid.description = "Exports DSN format";
+	dsn_hid.exporter = 1;
+	dsn_hid.get_export_options = dsn_get_export_options;
+	dsn_hid.do_export = dsn_do_export;
+	dsn_hid.parse_arguments = dsn_parse_arguments;
+	hid_register_hid(&dsn_hid);
+
+	hid_register_attributes(dsn_options, sizeof(dsn_options) / sizeof(dsn_options[0]), dsn_cookie, 0);
+	return hid_dsn_uninit;
+}
+
diff --git a/src_plugins/export_dxf/Makefile b/src_plugins/export_dxf/Makefile
new file mode 100644
index 0000000..c6da93f
--- /dev/null
+++ b/src_plugins/export_dxf/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_dxf
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_dxf/Plug.tmpasm b/src_plugins/export_dxf/Plug.tmpasm
new file mode 100644
index 0000000..f29a3ce
--- /dev/null
+++ b/src_plugins/export_dxf/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_dxf}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_dxf/dxf.o @]
+
+switch /local/pcb/export_dxf/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_dxf/README b/src_plugins/export_dxf/README
new file mode 100644
index 0000000..9a1e20c
--- /dev/null
+++ b/src_plugins/export_dxf/README
@@ -0,0 +1,5 @@
+Export dxf
+
+#state: WIP
+#default: disabled
+#implements: export
diff --git a/src_plugins/export_dxf/README.orig b/src_plugins/export_dxf/README.orig
new file mode 100644
index 0000000..7453d11
--- /dev/null
+++ b/src_plugins/export_dxf/README.orig
@@ -0,0 +1,47 @@
+The pcb DXF HID is a DXF exporter for pcb.
+
+The pcb DXF HID exports the loaded pcb layout to a series of DXF files,
+typically one file for every layer, a DXF file for inserting elements by
+means of "XREFS" (in AutoCAD jargon also known as eXternal REFerenced
+drawings), and DXF files for plated and non-plated holes.
+
+These external referenced drawings can be 3D models of elements (ACIS),
+including DDE/OLE attributes of any other application that is supported
+by both AutoCAD (or any alternative software that supports these 
+features) and the (Microsoft) Operating System of your choice.
+
+The parameters for the DXF exporter are:
+
+--dxffile
+
+This is the basename of the generated files.
+Layer-, top and bottom mask-, top and bottom paste, and drill-filenames
+are based upon this string.
+
+--metric
+Tick for mm, default is mil.
+
+--layer-color-BYBLOCK
+Tick for layer color is BYBLOCK, default layer color is BYLAYER.
+
+--xrefs
+Tick for generating an eXternal REFerence file, default is none.
+
+--xreffile
+This string should contain the pathname of the location where your XREF
+drawing files exist.
+
+--verbose
+Tick if you want to see a full report on stderr of what entities are
+written to the files, default is silent.
+
+--export-all-layers
+Tick if you want to export all layers, default is to not export empty
+layers.
+
+-------------------------------------------------------------------------
+                            COPYRIGHT
+
+The pcb DXF HID is covered by the GNU General Public License.
+See the individual files for the exact copyright notices.
+
diff --git a/src_plugins/export_dxf/dxf.c b/src_plugins/export_dxf/dxf.c
new file mode 100644
index 0000000..b700c4b
--- /dev/null
+++ b/src_plugins/export_dxf/dxf.c
@@ -0,0 +1,5978 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/*!
+ * \file dxf.c
+ *
+ * \author Copyright (C) 2007 .. 2011 by Bert Timmerman <bert.timmerman at xs4all.nl>
+ * for DXF relevant code.\n
+ *
+ * \brief This file contains the DXF HID (Human Interface Device) exporter to
+ * generate a DXF file containing Xref's of components and a series of DXF
+ * files, one for every pcb layer.
+ *
+ * <hr>
+ * <b>Drawing eXchange Format (DXF)</b>\n
+ * DXF is a defacto industry standard for the exchange of drawing files
+ * between various Computer Aided Drafting programs.\n
+ * DXF is designed by Autodesk(TM).\n
+ * For more details see http://www.autodesk.com .
+ * <hr>
+ * <h1><b>Warning.</b></h1>\n
+ * Some notes about Coordinate systems:\n
+ * <ul>
+ * <li>PCB follows a "left handed" Cartesian Coordinate System.\n
+ * Positive X is right, positive Y is down.\n
+ * Angles are increasing counter clockwise (CCW) in degrees, with 0 degrees being left
+ * (negative X-axis) and 90 degrees being down (positive Y-axis).\n
+ * <li>AutoCAD DXF follows a "right handed" Cartesian Coordinate system.\n
+ * Positive X is right, positive Y is up.\n
+ * Angles are counter clockwise (CCW) in degrees, with 0 being right
+ * (positive X) and 90 being up (positive Y).\n
+ * <li>Autocad coordinates are either in mil (default) or in mm (see checkbox
+ * in the DXF HID dialog).\n
+ * <li>Lookup Wikipedia with keywords "Cartesian Coordinates" if you want more
+ * information on this subject.\n
+ * </ul>
+ * Filled polygons are drawn with a hatch entity.\n
+ * Elliptic arcs are drawn with an ellipse entity.\n
+ * Successful importing the generated dxf files with these entities
+ * requires AutoCAD version R14 or higher, or any other mechanical CAD
+ * program with the same level of compatibility with regard to the DXF
+ * format.\n
+ * <hr>
+ * <p>
+ * Please send remarks, bugs, improvements, relevant opinions to
+ * bert.timmerman at xs4all.nl or to the geda-dev mailing list. \n
+ * Donations to speed up the development of the gEDA suite best go to
+ * www.gedaconsulting.com. \n
+ * <li>Have fun ;-)
+ * </ul>
+ * <hr>
+ * <p>
+ * <h1><b>The DXF HID function treeview.</b></h1>\n
+ * The following function hierarchy, with the status of the functions in
+ * between [], exists:
+ * <ul>
+ * <li>dxf_get_export_options [stable]
+ * <li>dxf_do_export [stable]
+ *   <ul>
+ *   <li>dxf_get_export_options [stable]
+ *   <li>dxf_export_xref_file [exp]
+ *     <ul>
+ *     <li>dxf_write_comment [stable]
+ *     <li>dxf_write_header [stable]
+ *       <ul>
+ *       <li>dxf_write_header_metric_new [stable]
+ *       <li>dxf_write_header_imperial_new [todo]
+ *       </ul>
+ *     <li>dxf_write_block_record [todo]
+ *     <li>dxf_write_block [stable]
+ *     <li>dxf_write_endsection [stable]
+ *     <li>dxf_write_section [stable]
+ *     <li>dxf_insert [stable]
+ *     <li>dxf_write_eof [stable]
+ *     </ul>
+ *   <li>dxf_maybe_close_file [exp]
+ *     <ul>
+ *     <li>dxf_write_tail [todo]
+ *     <li>dxf_write_tail_metric_new [todo]
+ *     <li>dxf_write_tail_imperial_new [todo]
+ *     <li>dxf_write_eof [stable]
+ *     </ul>
+ *   </ul>
+ * <li>dxf_parse_arguments [stable]
+ * <li>dxf_set_layer [exp]
+ *     <ul>
+ *     <li>dxf_maybe_close_file [stable]
+ *     <li>dxf_write_header [stable]
+ *       <ul>
+ *       <li>dxf_write_header_metric_new [stable]
+ *       <li>dxf_write_header_imperial_new [todo]
+ *       </ul>
+ *     <li>dxf_write_block_record [todo]
+ *     <li>dxf_write_block [stable]
+ *     <li>dxf_write_endsection [stable]
+ *     <li>dxf_write_section [stable]
+ *     </ul>
+ * <li>dxf_make_gc [stable]
+ * <li>dxf_destroy_gc [stable]
+ * <li>dxf_use_mask [stable]
+ * <li>dxf_set_color [stable]
+ * <li>dxf_set_line_cap [stable]
+ * <li>dxf_set_line_width [stable]
+ * <li>dxf_draw_line [exp]
+ *   <ul>
+ *   <li>dxf_write_polyline [exp]
+ *   <li>dxf_write_vertex [stable]
+ *   <li>dxf_write_endseq [stable]
+ *   </ul>
+ * <li>dxf_draw_arc [exp]
+ *   <ul>
+ *   <li>dxf_write_ellipse [exp]
+ *   </ul>
+ * <li>dxf_draw_rect [exp]
+ *   <ul>
+ *   <li>dxf_write_polyline [exp]
+ *   <li>dxf_write_vertex [stable]
+ *   <li>dxf_write_endseq [stable]
+ *   </ul>
+ * <li>dxf_fill_circle [exp]
+ *   <ul>
+ *   <li>dxf_write_circle [exp]
+ *   </ul>
+ * <li>dxf_fill_polygon [exp]
+ *   <ul>
+ *   <li>dxf_write_hatch [stable]
+ *   <li>dxf_write_hatch_boundary_path_polyline [stable]
+ *   <li>dxf_write_hatch_boundary_path_polyline_vertex [stable]
+ *   </ul>
+ * <li>dxf_fill_rect [stable]
+ *   <ul>
+ *   <li>dxf_write_solid [stable]
+ *   </ul>
+ * <li>dxf_calibrate [stable]
+ * <li>dxf_set_crosshair [stable]
+ * </ul>
+ * <br>
+ * Notes:\n
+ * [todo] denotes a function not yet implemented.\n
+ * [exp] denotes a function not yet fully implemented.\n
+ * [stable] denotes a function fully implemented.\n
+ * <hr>
+ * \n\n
+ * <b>Include files</b>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "macro.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "error.h"
+#include "draw.h"
+#include "hid_draw_helpers.h"
+#include "pcb-printf.h"
+#include "compat_misc.h"
+#include "layer.h"
+#include "hid_attrib.h"
+#include "hid_flags.h"
+#include "hid_helper.h"
+#include "hid.h"
+#include "draw.h"
+#include "hid_init.h"
+#include "plugins.h"
+
+/*!
+ * \brief List with string data.
+ */
+typedef struct _StringList {
+	char *str;
+	/*!< String value. */
+	struct _StringList *next;
+	/*!< Pointer to next item. */
+} StringList;
+
+
+/*!
+ * \brief List with pcb element header data.
+ */
+typedef struct _DxfList {
+	char *descr;
+	/*!< description of pcb element. */
+	char *value;
+	/*!< value of pcb element. */
+	int num;
+	/*!< amount of elements per unique pcb element. */
+	StringList *refdes;
+	/*!< list of reference designators (refdes's) of pcb elements. */
+	struct _DxfList *next;
+	/*!< pointer to next (unique) element header data. */
+} DxfList;
+
+
+#define NOSHAPE 0
+#define SHP_ROUND 1									/* shaped like a circle */
+#define SHP_OCTAGON 2								/* shape like an octagon */
+#define SHP_SQUARE 3								/* shaped like a square */
+#define SHP_ROUNDCLEAR 4						/* round clearance in negatives */
+#define SHP_SQUARECLEAR 5						/* square clearance in negatives */
+#define SHP_THERMAL 6								/* negative thermal relief */
+
+
+/*
+ * Function prototypes.
+ */
+static void dxf_beep(void);
+static void dxf_calibrate(double xval, double yval);
+static char *dxf_clean_string(char *in);
+static void dxf_destroy_gc(hidGC gc);
+static void dxf_do_export(HID_Attr_Val * options);
+static void dxf_draw_arc(hidGC gc, int cx, int cy, int width, int height, int start_angle, int delta_angle);
+static void dxf_draw_line(hidGC gc, int x1, int y1, int x2, int y2);
+static void dxf_draw_rect(hidGC gc, int x1, int y1, int x2, int y2);
+static int dxf_drill_sort(const void *va, const void *vb);
+static int dxf_export_xref_file(void);
+static void dxf_fill_circle(hidGC gc, int cx, int cy, int radius);
+static void dxf_fill_polygon(hidGC gc, int n_coords, int *x, int *y);
+static void dxf_fill_rect(hidGC gc, int x1, int y1, int x2, int y2);
+static HID_Attribute *dxf_get_export_options(int *n);
+static int dxf_group_for_layer(int l);
+static DxfList *dxf_insert(char *refdes, char *descr, char *value, DxfList * dxf);
+static int dxf_layer_sort(const void *va, const void *vb);
+static hidGC dxf_make_gc(void);
+static void dxf_maybe_close_file();
+static void dxf_parse_arguments(int *argc, char ***argv);
+static void dxf_progress(int dxf_so_far, int dxf_total, const char *dxf_message);
+static void dxf_set_color(hidGC gc, const char *name);
+static void dxf_set_crosshair(int x, int y);
+static int dxf_set_layer(const char *name, int group);
+static void dxf_set_line_cap(hidGC gc, EndCapStyle style);
+static void dxf_set_line_width(hidGC gc, int width);
+static void dxf_show_item(void *item);
+static StringList *dxf_string_insert(char *str, StringList * list);
+static void dxf_use_gc(hidGC gc, int radius);
+static void dxf_use_mask(int use_it);
+static void dxf_write_block(FILE * fp, int id_code, char *xref_name,
+														char *block_name, char *linetype, char *layer, double x0,
+														double y0, double z0, double thickness, int color, int paperspace, int block_type);
+static void dxf_write_circle(FILE * fp, int id_code, char *linetype,
+														 char *layer, double x0, double y0, double z0, double extr_x0,
+														 double extr_y0, double extr_z0, double thickness, double radius, int color, int paperspace);
+static void dxf_write_comment(FILE * fp, char *comment_string);
+static void dxf_write_ellipse(FILE * fp, int id_code, char *linetype,
+															char *layer, double x0, double y0, double z0, double x1,
+															double y1, double z1, double extr_x0, double extr_y0,
+															double extr_z0, double thickness, double ratio,
+															double start_angle, double end_angle, int color, int paperspace);
+static void dxf_write_endsection(FILE * fp);
+static void dxf_write_eof(FILE * fp);
+static void dxf_write_hatch(FILE * fp, char *pattern_name, int id_code,
+														char *linetype, char *layer, double x0, double y0, double z0,
+														double extr_x0, double extr_y0, double extr_z0, double thickness,
+														double pattern_scale, double pixel_size, double pattern_angle,
+														int color, int paperspace, int solid_fill, int associative,
+														int style, int pattern_style, int pattern_double,
+														int pattern_def_lines, int boundary_paths, int seed_points, double *seed_x0, double *seed_y0);
+static void dxf_write_hatch_boundary_path_polyline(FILE * fp,
+																									 int type_flag, int polyline_has_bulge, int polyline_is_closed,
+																									 int polyline_vertices);
+static void dxf_write_hatch_boundary_path_polyline_vertex(FILE * fp, double x0, double y0, double bulge);
+static void dxf_write_header();
+static void dxf_write_header_imperial_new();
+static void dxf_write_header_metric_new();
+static void dxf_write_insert(FILE * fp, int id_code, char *block_name,
+														 char *linetype, char *layer, double x0, double y0, double z0,
+														 double thickness, double rel_x_scale, double rel_y_scale,
+														 double rel_z_scale, double column_spacing, double row_spacing,
+														 double rot_angle, int color, int attribute_follows, int paperspace, int columns, int rows);
+static void dxf_write_polyline(FILE * fp, int id_code, char *linetype,
+															 char *layer, double x0, double y0, double z0,
+															 double extr_x0, double extr_y0, double extr_z0,
+															 double thickness, double start_width, double end_width,
+															 int color, int vertices_follow, int paperspace, int flag,
+															 int polygon_mesh_M_vertex_count, int polygon_mesh_N_vertex_count,
+															 int smooth_M_surface_density, int smooth_N_surface_density, int surface_type);
+static void dxf_write_section(FILE * fp, char *section_name);
+static void dxf_write_solid(FILE * fp, int id_code, char *linetype,
+														char *layer, double x0, double y0, double z0, double x1,
+														double y1, double z1, double x2, double y2, double z2,
+														double x3, double y3, double z3, double thickness, int color, int paperspace);
+static void dxf_write_table_block_record(FILE * fp);
+static void dxf_write_vertex(FILE * fp, int id_code, char *linetype,
+														 char *layer, double x0, double y0, double z0, double thickness,
+														 double start_width, double end_width, double bulge,
+														 double curve_fit_tangent_direction, int color, int paperspace, int flag);
+static double dxf_xy_to_angle(double x, double y);
+
+/*!
+ * \brief Debugging on/off switch.
+ */
+#define DEBUG 0
+
+/*!
+ * \brief Error handling of not yet implemented functions.
+ */
+#define CRASH fprintf(stderr, "DXF error: pcb called an unimplemented DXF-HID function %s.\n", __FUNCTION__); abort()
+
+/*!
+ * pcb dxf HID version string
+ */
+#define PCB_DXF_HID_VERSION "PCB DXF HID - Version 0.0.1 (2011-10-23)"
+
+/*!
+ * \brief Dxf X-coordinate (in mil).
+ */
+#define DXF_X(pcb,x) (x)
+
+/*!
+ * \brief Dxf Y-coordinate (in mil).
+ */
+#define DXF_Y(pcb,y) ((pcb)->MaxHeight-(y))
+
+/*!
+ * \brief Dxf X offset (in mil).
+ */
+#define DXF_XOffset(pcb,x) (x)
+
+/*!
+ * \brief Dxf Y offset (in mil).
+ */
+#define DXF_YOffset(pcb,y) (-(y))
+
+/*!
+ * \brief Round of value to the nearest multiple of 100.
+ */
+#define DXF_ROUND(x) ((int)(((x)+50)/100)*100)
+
+/*!
+ * \brief DXF color definition, entities with this color follow the color
+ * definition of the block in which it lives.
+ */
+#define DXF_COLOR_BYBLOCK 0
+
+/*!
+ * \brief DXF color definition, pen number "1" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_RED 1
+
+/*!
+ * \brief DXF color definition, pen number "2" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_YELLOW 2
+
+/*!
+ * \brief DXF color definition, pen number "3" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_GREEN 3
+
+/*!
+ * \brief DXF color definition, pen number "4" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_CYAN 4
+
+/*!
+ * \brief DXF color definition, pen number "5" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_BLUE 5
+
+/*!
+ * \brief DXF color definition, pen number "6" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_MAGENTA 6
+
+/*!
+ * \brief DXF color definition, pen number "7" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_WHITE 7
+
+/*!
+ * \brief DXF color definition, pen number "8" in the virtual pen-plotter.
+ */
+#define DXF_COLOR_GREY 8
+
+/*!
+ * \brief DXF color definition, color of the entity follows the color
+ * definition of the layer on which it lives.
+ */
+#define DXF_COLOR_BYLAYER 256
+
+/*!
+ * \brief This is where our hardware is going to live, default value, can be
+ * ommitted in dxf output.
+ */
+#define DXF_MODELSPACE 0
+
+/*!
+ * \brief This is where your annotation (papersheet templates, fab notes and
+ * such) should live, has to be included in DXF output for any entity to live
+ * on paperspace.
+ */
+#define DXF_PAPERSPACE 1
+
+/*!
+ * \brief There is <b>always</b> a layer "0" defined, it's reasonably safe to
+ * assume that this is a valid layername.
+ */
+#define DXF_DEFAULT_LAYER "0"
+
+/*!
+ * \brief There is <b>always</b> a linetype "BYLAYER" defined, it's reasonably
+ * safe to assume that this is a valid linetype.
+ */
+#define DXF_DEFAULT_LINETYPE "BYLAYER"
+
+/*!
+ * \brief There is <b>always</b> a textstyle "STANDARD" defined, it's
+ * reasonably safe to assume that this is a valid text style.
+ */
+#define DXF_DEFAULT_TEXTSTYLE "STANDARD"
+
+/*!
+ * \brief Directory where 3D models of parts live.
+ *
+ * For now the dxf_xref_pathname is set to "parts", in AutoCAD one can
+ * configure default search directories for xrefs.
+ */
+#define DXF_DEFAULT_XREF_PATH_NAME "parts"
+
+/*!
+ * \brief Directory separator character (back slash).
+ *
+ * We target an Autodesk universe with all of it's quirks.
+ *
+ * \todo This has to be solved in a more elegant manner if we want to
+ * use DXF files for *nix based CAD software.
+ */
+#define DXF_DIR_SEPARATOR "\\"
+
+/*!
+ * \brief Default hatch pattern for pcb polygons.
+ *
+ * \todo For now the hatch pattern name is set to "SOLID".\n
+ * If pcb is ever to have thieving implemented, a hatch pattern name field
+ * has to be added to the graphic context.
+ */
+#define DXF_DEFAULT_HATCH_PATTERN_NAME "SOLID"
+
+/*!
+ * \brief File pointer for DXF layer files.
+ */
+static FILE *fp;
+
+/*!
+ * \brief File name of layer DXF files.
+ */
+static char *dxf_filename;
+
+/*!
+ * \brief File name suffix for layer files.
+ */
+static char *dxf_filesuffix;
+
+/*!
+ * \brief .
+ */
+static int dxf_finding_apertures = 0;
+
+/*!
+ * \brief Layer name.
+ */
+static char *dxf_layername = 0;
+
+/*!
+ * \brief Line count ??.
+ */
+static int lncount = 0;
+
+/*!
+ * \brief DXF file with xrefs needed.
+ */
+static int dxf_xrefs;
+
+/*!
+ * \brief File name of Xref (blocks) DXF file.
+ */
+static char *dxf_xref_filename;
+
+/*!
+ * \brief DXF file with output in mm (not mils).
+ */
+static int dxf_metric;
+
+/*!
+ * \brief DXF file with layer color BYBLOCK (or by layer number).
+ */
+static int dxf_color_is_byblock;
+
+/*!
+ * \brief DXF file with verbose output (to contain DXF comments).
+ */
+static int dxf_verbose;
+
+/*!
+ * \brief export all PCB layers to DXF files.
+ */
+static int dxf_export_all_layers;
+
+/*!
+ * \brief Every DXF entity has a unique identifier (per DXF file).
+ */
+static int dxf_id_code = 0;
+
+/*!
+ * \brief Record with all values of the DXF HID.
+ */
+static HID dxf_hid;
+
+/*!
+ * \brief Drill (hole) properties.
+ */
+typedef struct {
+	int diam;
+	/*!< drill diameter. */
+	int x;
+	/*!< X-coordinate. */
+	int y;
+	/*!< Y-coordinate. */
+} DxfPendingDrills;
+
+/*!
+ * \brief Layer is a mask.
+ */
+static int is_mask;
+
+/*!
+ * \brief Current mask.
+ */
+static int current_mask;
+
+/*!
+ * \brief Entity is a drill (hole).
+ */
+static int is_drill;
+
+static int was_drill;
+
+static int n_layerapps = 0;
+
+static int c_layerapps = 0;
+
+/*!
+ * \brief Pending drill (holes).
+ */
+DxfPendingDrills *dxf_pending_drills = 0;
+
+/*!
+ * \brief Number of pending drill (holes).
+ */
+int dxf_n_pending_drills = 0;
+
+/*!
+ * \brief Maximum number of pending drills (holes).
+ */
+int dxf_max_pending_drills = 0;
+
+/*!
+ * \brief Definition of graphic context for the dxf HID.
+ *
+ * This graphics context is an opaque pointer defined by the HID.\n
+ * GCs are HID-specific; attempts to use one HID's GC for a different
+ * HID  will result in a fatal error.\n
+ *
+ * \todo In pcb the cap <b>always</b> extends beyond the coordinates
+ * given, by half the width of the line.\n
+ * With DXF polylines the cap is by default 0, and thus doesn't extend
+ * beyond the coordinates.\n
+ * A round cap could be implemented by drawing a donut (a series of
+ * polyline vertices) with an inner radius of zero at the start and
+ * endpoint of a line, this doesn't solve the problem for other aperture
+ * shapes, and is thus not yet implemented.
+ */
+typedef struct hid_gc_struct {
+	EndCapStyle cap;
+	/*!< end cap style. */
+	int width;
+	/*!< width. */
+	int color;
+	/*!< color. */
+	int erase;
+	/*!< erase. */
+	int drill;
+	/*!< drill. */
+} hid_gc_struct;
+
+/*!
+ * \brief Definition of options the user can select in the DXF exporter
+ * dialog.
+ */
+static HID_Attribute dxf_options[] = {
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --dxffile <string>
+DXF output file prefix. Can include a path.
+ at end ftable
+%end-doc
+*/
+	{"dxffile", "DXF layer filename base",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_dxffile 0
+
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --metric <boolean>
+Export DXF files in mm. Default is mil
+ at end ftable
+%end-doc
+*/
+	{"metric", "export DXF files in mm",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_metric 1
+
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --layer-color-BYBLOCK <boolean>
+Export entities in color BYBLOCK. Default is BYLAYER.
+ at end ftable
+%end-doc
+*/
+	{"layer-color-BYBLOCK", "export entities in color BYBLOCK",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_color_byblock 2
+
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --xrefs <boolean>
+Export a DXF file with XREFS. Default is no XREFS.
+ at end ftable
+%end-doc
+*/
+	{"xrefs", "export a DXF file with xrefs",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_xrefs 3
+
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --xreffile <string>
+DXF Xrefs filename. Can include a path.
+ at end ftable
+%end-doc
+*/
+	{"xreffile", "DXF Xrefs filename",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_xreffile 4
+
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --verbose <boolean>
+Verbose output to stderr (comments).
+ at end ftable
+%end-doc
+*/
+	{"be verbose", "verbose output to stderr (comments)",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_verbose 5
+
+/*
+%start-doc options "DXF Export"
+ at ftable @code
+ at item --export-all-layers <boolean>
+Export all layers.
+ at end ftable
+%end-doc
+*/
+	{"export all layers", "export all layers",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_export_all_layers 6
+};
+
+#define NUM_OPTIONS (sizeof(dxf_options)/sizeof(dxf_options[0]))
+
+/*!
+ * \brief Used for HID attributes (exporting and printing, mostly).
+ *
+ * HA_boolean uses int_value, HA_enum sets int_value to the index and
+ * str_value to the enumeration string.\n
+ * HID_Label just shows the default str_value.\n
+ * HID_Mixed is a real_value followed by an enum, like 0.5in or 100mm.
+ */
+static HID_Attr_Val dxf_values[NUM_OPTIONS];
+
+static int pagecount = 0;
+
+static int linewidth = -1;
+
+static int lastgroup = -1;
+
+static int lastcap = -1;
+
+static int lastcolor = -1;
+
+static int print_layer[MAX_LAYER];
+
+/*!
+ * \brief The last X coordinate.
+ */
+static int dxf_lastX;
+
+/*!
+ * \brief The last Y coordinate.
+ */
+static int dxf_lastY;
+
+/*!
+ * \brief Find a group for a given layer ??.
+ */
+static int dxf_group_for_layer(int l) {
+	if ((l < max_copper_layer + 2) && (l >= 0)) {
+		return GetLayerGroupNumberByNumber(l);
+	}
+	/* else something unique */
+	return max_group + 3 + l;
+}
+
+/*!
+ * \brief Sort layers ??.
+ */
+static int dxf_layer_sort(const void *va, const void *vb) {
+	int a;
+	int b;
+	int d;
+
+	a = *(int *) va;
+	b = *(int *) vb;
+	d = dxf_group_for_layer(b) - dxf_group_for_layer(a);
+	if (d)
+		return d;
+	return b - a;
+}
+
+/*!
+ * \brief Convert pcb x,y coordinates to an angle relative to (0.0, 0.0).
+ */
+static double dxf_xy_to_angle(double x, double y) {
+	double theta;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_xy_to_angle () function.\n", __FILE__, __LINE__);
+#endif
+	if ((x > 0.0) && (y >= 0.0))
+		theta = 180.0;
+	else if ((x <= 0.0) && (y > 0.0))
+		theta = 90.0;
+	else if ((x < 0.0) && (y <= 0.0))
+		theta = 0.0;
+	else if ((x >= 0.0) && (y < 0.0))
+		theta = 270.0;
+	else {
+		theta = 0.0;
+		Message(PCB_MSG_WARNING, "DXF: in dxf_xy_to_angle ():\n"
+						"     unable to figure out angle of element\n"
+						"     because the pin is at the centroid of the part.\n"
+						"     This is a BUG!!!\n" "     Setting to %g degrees\n", theta);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_xy_to_angle () function.\n", __FILE__, __LINE__);
+#endif
+	return (theta);
+}
+
+
+/*!
+ * \brief Clean up a string.
+ *
+ * Copy over input string to output string with some character conversions.\n
+ * Go all the way to end of string to get the termination character.
+ */
+static char *dxf_clean_string(char *in) {
+	char *out;
+	int i;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_clean_string () function.\n", __FILE__, __LINE__);
+#endif
+	if ((out = malloc((strlen(in) + 1) * sizeof(char))) == NULL) {
+		Message(PCB_MSG_ERROR, "DXF: in dxf_clean_string (): malloc () failed.\n");
+		exit(1);
+	}
+	for (i = 0; i <= strlen(in); i++) {
+		switch (in[i]) {
+		case '"':
+			out[i] = '\'';
+			break;
+		case ' ':
+			out[i] = '_';
+			break;
+		default:
+			out[i] = in[i];
+			break;
+		}
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_clean_string () function.\n", __FILE__, __LINE__);
+#endif
+	return (out);
+}
+
+
+/*!
+ * \brief Insert the string to the list of strings.
+ */
+static StringList *dxf_string_insert(char *str, StringList * list) {
+	StringList *new;
+	StringList *cur;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_string_insert () function.\n", __FILE__, __LINE__);
+#endif
+	if ((new = (StringList *) malloc(sizeof(StringList))) == NULL) {
+		Message(PCB_MSG_ERROR, "DXF: in dxf_string_insert (): malloc () failed.\n");
+		exit(1);
+	}
+	new->next = NULL;
+	new->str = pcb_strdup(str);
+	if (list == NULL) {
+		return (new);
+	}
+	cur = list;
+	while (cur->next != NULL)
+		cur = cur->next;
+	cur->next = new;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_string_insert () function.\n", __FILE__, __LINE__);
+#endif
+	return (list);
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a block entity.
+ *
+ * The <c>BLOCKS</c> section of the DXF file contains all the block
+ * definitions.\n
+ * It contains the entities that make up the blocks used in the drawing,
+ * including anonymous blocks generated by the HATCH command and by
+ * associative dimensioning.\n
+ * The format of the entities in this section is identical to those in the
+ * <c>ENTITIES</c> section.\n
+ * All entities in the <c>BLOCKS</c> section appear between block and endblk
+ * entities.\n
+ * Block and endblk entities appear only in the <c>BLOCKS</c> section.\n
+ * Block definitions are never nested (that is, no block or endblk entity ever
+ * appears within another block-endblk pair), although a block definition can
+ * contain an insert entity.\n
+ * \n
+ * External references are written in the DXF file as block definitions,
+ * except that they also include a string (group code 1) that specifies the
+ * path and file name of the external reference.\n
+ * \n
+ * The block table handle, along with any xdata and persistent reactors,
+ * appears in each block definition immediately following the <c>BLOCK</c>
+ * record, which contains all of the specific information that a block table
+ * record stores.\n
+ * \n
+ * The UCS in effect when a block definition is created becomes the WCS for
+ * all entities in the block definition.\n
+ * The new origin for these entities is shifted to match the base point
+ * defined for the block definition.\n
+ * All entity data is translated to fit this new WCS.\n
+ * \n
+ * <c>*MODEL_SPACE</c> and <c>*PAPER_SPACE</c> Block Definition.\n
+ * Now, there are always two extra, empty definitions in the BLOCKS section,
+ * titled <c>*MODEL_SPACE</c> and <c>*PAPER_SPACE</c>.\n
+ * These definitions manifest the new representation of model space and paper
+ * space as block definitions internally.\n
+ * The entities contained in these definitions still appear in the
+ * <c>ENTITIES</c> section for compatibility.\n
+ * \n
+ * Model Space and Paper Space Entity Segregation.\n
+ * The interleaving between model space and paper space will no longer occurs,
+ * because of internal organization.\n
+ * Instead, all paper space entities are output, followed by model space
+ * entities.\n
+ * The flag distinguishing them is the group code 67.\n
+ *
+ * \todo Add group code 102 stuff.
+ */
+static void dxf_write_block(FILE * fp,
+														/*!< file pointer to output file (or device). */
+														int id_code,
+														/*!< group code = 5. */
+														char *xref_name,
+														/*!< group code = 1. */
+														char *block_name,
+														/*!< group code = 2 and 3. */
+														char *linetype,
+														/*!< group code = 6\n
+														 * optional, if omitted defaults to BYLAYER. */
+														char *layer,
+														/*!< group code = 8. */
+														double x0,
+														/*!< group code = 10\n
+														 * base point. */
+														double y0,
+														/*!< group code = 20\n
+														 * base point. */
+														double z0,
+														/*!< group code = 30\n
+														 * base point. */
+														double thickness,
+														/*!< group code = 39\n
+														 * optional, if omitted defaults to 0.0. */
+														int color,
+														/*!< group code = 62\n
+														 * optional, if omitted defaults to BYLAYER. */
+														int paperspace,
+														/*!< group code = 67\n
+														 * optional, if omitted defaults to 0 (modelspace). */
+														int block_type
+														/*!< group code = 70\n
+														 * bit codes:\n
+														 * 1 = this is an anonymous Block generated by hatching,
+														 * associative dimensioning, other internal operations, or an
+														 * application\n
+														 * 2 = this Block has Attributes\n
+														 * 4 = this Block is an external reference (Xref)\n
+														 * 8 = not used\n
+														 * 16 = this Block is externally dependent\n
+														 * 32 = this is a resolved external reference, or dependent
+														 * of an external reference\n
+														 * 64 = this definition is referenced. */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_block () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("BLOCK");
+	if (strcmp(block_name, "") == 0) {
+		if (dxf_verbose) {
+			fprintf(stderr, "DXF Warning: empty block name string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+			fprintf(stderr, "\t%s entity is discarded from output.\n", dxf_entity_name);
+		}
+		return;
+	}
+	if (strcmp(xref_name, "") == 0) {
+		if (dxf_verbose) {
+			fprintf(stderr, "DXF Warning: empty xref name string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+			fprintf(stderr, "\t%s entity is discarded from output.\n", dxf_entity_name);
+		}
+		return;
+	}
+	if (strcmp(layer, "") == 0) {
+		if (dxf_verbose) {
+			fprintf(stderr, "DXF Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+			fprintf(stderr, "\t%s entity is relocated to layer 0.\n", dxf_entity_name);
+		}
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	/* group code 102 stuff goes here  */
+	fprintf(fp, "100\nAcDbEntity\n");
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, "100\nAcDbBlockBegin\n");
+	fprintf(fp, "  2\n%s\n", block_name);
+	fprintf(fp, " 70\n%d\n", block_type);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	fprintf(fp, "  3\n%s\n", block_name);
+	if ((block_type && 4) || (block_type && 32)) {
+		fprintf(fp, "  1\n%s%s%s.dwg\n", xref_name, DXF_DIR_SEPARATOR, block_name);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+	/* now write an end block marker */
+	fprintf(fp, "  0\nENDBLK\n");
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	/* group code 102 stuff goes here */
+	fprintf(fp, "100\nAcDbBlockEnd\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_block () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a <c>BLOCK_RECORD</c> table.
+ *
+ * The DXF <c>BLOCK_RECORD</c> table is part of the <c>TABLES</c> section of
+ * the header that comes after the <c>DIMSTYLE</c> table and before the
+ * <c>ENTITIES</c> section.\n
+ * The <c>BLOCK_RECORD</c> table contains record definitions of all
+ * <c>BLOCK</c> entities, either regular <c>BLOCK</c> entities or external
+ * referenced <c>BLOCK</c> entities, the so calld XREFS.\n
+ * \todo Code it !! Make it happen !!
+ */
+static void dxf_write_table_block_record(FILE * fp
+																				 /*!< file pointer to output file (or device). */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_table_block_record () function.\n", __FILE__, __LINE__);
+#endif
+	/*! \todo Add code. */
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_table_block_record () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a <c>CIRCLE</c> entity.
+ *
+ * \todo The <c>CIRCLE</c> entity has to be replaced by a <c>POLYLINE</c>
+ * entity  with the correct line width (trace width).
+ * \todo The <c>CIRCLE</c> entity is to be used for (unplated) drill holes only.
+ * In the mechanical CAD program these circles (drill holes) can be extruded
+ * to the board thickness and subtracted from the extruded pcb outline.
+ */
+static void dxf_write_circle(FILE * fp,
+														 /*!< file pointer to output file (or device). */
+														 int id_code,
+														 /*!< group code = 5. */
+														 char *linetype,
+														 /*!< group code = 6\n
+														  * optional, defaults to BYLAYER. */
+														 char *layer,
+														 /*!< group code = 8. */
+														 double x0,
+														 /*!< group code = 10\n
+														  * base point. */
+														 double y0,
+														 /*!< group code = 20\n
+														  * base point. */
+														 double z0,
+														 /*!< group code = 30\n
+														  * base point. */
+														 double extr_x0,
+														 /*!< group code = 210\n
+														  * extrusion direction\n
+														  * optional, if ommited defaults to 0.0. */
+														 double extr_y0,
+														 /*!< group code = 220\n
+														  * extrusion direction\n
+														  * optional, if ommited defaults to 0.0. */
+														 double extr_z0,
+														 /*!< group code = 230\n
+														  * extrusion direction\n
+														  * optional, if ommited defaults to 1.0. */
+														 double thickness,
+														 /*!< group code = 39\n
+														  * optional, defaults to 0.0. */
+														 double radius,
+														 /*!< group code = 40. */
+														 int color,
+														 /*!< group code = 62\n
+														  * optional, defaults to BYLAYER. */
+														 int paperspace
+														 /*!< group code = 67\n
+														  * optional, defaults to 0 (modelspace). */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_circle () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("CIRCLE");
+	if (radius == 0.0) {
+		if (dxf_verbose) {
+			fprintf(stderr, "Error: radius value equals 0.0 for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+			return;
+		}
+	}
+	if (strcmp(layer, "") == 0) {
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+		if (dxf_verbose) {
+			fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+			fprintf(stderr, "    %s entity is relocated to layer 0", dxf_entity_name);
+		}
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	fprintf(fp, "100\nAcDbCircle\n");
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	fprintf(fp, " 210\n%f\n", extr_x0);
+	fprintf(fp, " 220\n%f\n", extr_y0);
+	fprintf(fp, " 230\n%f\n", extr_z0);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	fprintf(fp, " 40\n%f\n", radius);
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_circle () function.\n", __FILE__, __LINE__);
+#endif
+	return;
+}
+
+
+/*!
+ * \brief Write DXF output for a comment string with line termination.
+ *
+ * The group code "999" indicates that the following line is a comment
+ * string.\n
+ * The AutoCAD command "DXFOUT" does not currently include such groups in a
+ * DXF output file.\n
+ * The AutoCAD command "DXFIN" honors them and ignores the comments.\n
+ * Thus, you can use the 999 group to include comments in a DXF file you've
+ * created.
+ */
+static void dxf_write_comment(FILE * fp,
+															/*!< file pointer to output file (or device). */
+															char *comment_string
+															/*!< comment string. */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_comment () function.\n", __FILE__, __LINE__);
+#endif
+	if (strcmp(comment_string, "") == 0) {
+		/* no use in writing an empty comment string to file */
+		return;
+	}
+	fprintf(fp, "999\n%s\n", comment_string);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_comment () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for an ellipse entity.
+ *
+ * This entity requires AutoCAD version R14 or higher.\n
+ * The ellipse entity is currently used to draw arcs.\n
+ * Since the pcb file format allows for a height and a width value, it is thus
+ * possible to draw an elliptical arc.
+ *
+ * \todo The ellipse entity has to be replaced by a polyline with the correct
+ * line width (trace width).
+ * When we figured out how to draw a polyline (with vertices) for an arc
+ * (part of a circle) or an elliptical arc (part of an ellipse).
+ * This function will then become obsolete.
+ */
+static void dxf_write_ellipse(FILE * fp,
+															/*!< file pointer to output file (or device). */
+															int id_code,
+															/*!< group code = 5. */
+															char *linetype,
+															/*!< group code = 6\n
+															 * optional, defaults to BYLAYER. */
+															char *layer,
+															/*!< group code = 8. */
+															double x0,
+															/*!< group code = 10\n
+															 * base point. */
+															double y0,
+															/*!< group code = 20\n
+															 * base point. */
+															double z0,
+															/*!< group code = 30\n
+															 * base point. */
+															double x1,
+															/*!< group code = 11\n
+															 * endpoint of major axis, relative to the center (in WCS). */
+															double y1,
+															/*!< group code = 21\n
+															 * endpoint of major axis, relative to the center (in WCS). */
+															double z1,
+															/*!< group code = 31\n
+															 * endpoint of major axis, relative to the center (in WCS). */
+															double extr_x0,
+															/*!< group code = 210\n
+															 * extrusion direction\n
+															 * optional, if ommited defaults to 0.0. */
+															double extr_y0,
+															/*!< group code = 220\n
+															 * extrusion direction\n
+															 * optional, if ommited defaults to 0.0. */
+															double extr_z0,
+															/*!< group code = 230\n
+															 * extrusion direction\n
+															 * optional, if ommited defaults to 1.0. */
+															double thickness,
+															/*!< group code = 39\n
+															 * optional, defaults to 0.0. */
+															double ratio,
+															/*!< group code = 40\n
+															 * ratio of minor axis to major axis. */
+															double start_angle,
+															/*!< group code = 41\n
+															 * start parameter (this value is 0.0 for a full ellipse). */
+															double end_angle,
+															/*!< group code = 42\n
+															 * end parameter (this value is 2*pi for a full ellipse). */
+															int color,
+															/*!< group code = 62\n
+															 * optional, defaults to BYLAYER. */
+															int paperspace
+															/*!< group code = 67\n
+															 * optional, defaults to 0 (modelspace). */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_ellipse () function.\n", __FILE__, __LINE__);
+#endif
+	dxf_entity_name = pcb_strdup("ELLIPSE");
+	if (ratio == 0.0) {
+		if (dxf_verbose) {
+			fprintf(stderr, "Error: ratio value equals 0.0 for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		}
+		return;
+	}
+	if (strcmp(layer, "") == 0) {
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+		if (dxf_verbose) {
+			fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+			fprintf(stderr, "    %s entity is relocated to layer 0", dxf_entity_name);
+		}
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	fprintf(fp, "100\nAcDbEllipse\n");
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	fprintf(fp, " 11\n%f\n", x1);
+	fprintf(fp, " 21\n%f\n", y1);
+	fprintf(fp, " 31\n%f\n", z1);
+	fprintf(fp, " 210\n%f\n", extr_x0);
+	fprintf(fp, " 220\n%f\n", extr_y0);
+	fprintf(fp, " 230\n%f\n", extr_z0);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	fprintf(fp, " 40\n%f\n", ratio);
+	fprintf(fp, " 41\n%f\n", start_angle);
+	fprintf(fp, " 42\n%f\n", end_angle);
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_ellipse () function.\n", __FILE__, __LINE__);
+#endif
+	return;
+}
+
+
+/*!
+ * \brief Write DXF output to a file for an end of section marker.
+ */
+static void dxf_write_endsection(FILE * fp
+																 /*!< file pointer to output file (or device) */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_endsection () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, "  0\nENDSEC\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_endsection () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for an end of sequence marker.
+ *
+ * No fields.\n
+ * This marks the end of:\n
+ * <ul>
+ * <li>vertices (one or more "Vertex" entity) of a Polyline
+ * <li>attribute entities ("Attrib" entity) to an "Insert" entity that has
+ * Attributes (indicated by 66 group present and nonzero in Insert
+ * entity).\n
+ * </ul>
+ */
+static void dxf_write_endseq(FILE * fp
+														 /*!< file pointer to output file (or device).  */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_endseq () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, "  0\nENDSEQ\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_endseq () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write dxf output to a file for an EOF marker.
+ */
+static void dxf_write_eof(FILE * fp
+													/*!< file pointer to output file (or device).  */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_eof () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, "  0\nEOF\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_eof () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a hatch entity.
+ *
+ * This entity requires AutoCAD version R14 or higher.
+ *
+ * \todo writing a hatch with a number of hatch_seed_points > 0 is creating a
+ * segmentation fault in the dxf hid (and thus pcb).
+ */
+static void dxf_write_hatch(FILE * fp,
+														/*!< file pointer to output file (or device). */
+														char *pattern_name,
+														/*!< group code = 2. */
+														int id_code,
+														/*!< group code = 5. */
+														char *linetype,
+														/*!< group code = 6\n
+														 * optional, defaults to BYLAYER. */
+														char *layer,
+														/*!< group code = 8. */
+														double x0,
+														/*!< group code = 10\n
+														 * base point. */
+														double y0,
+														/*!< group code = 20\n
+														 * base point.  */
+														double z0,
+														/*!< group code = 30\n
+														 * base point. */
+														double extr_x0,
+														/*!< group code = 210\n
+														 * extrusion direction\n
+														 * optional, if ommited defaults to 0.0. */
+														double extr_y0,
+														/*!< group code = 220\n
+														 * extrusion direction\n
+														 * optional, if ommited defaults to 0.0.  */
+														double extr_z0,
+														/*!< group code = 230\n
+														 * extrusion direction\n
+														 * optional, if ommited defaults to 1.0.  */
+														double thickness,
+														/*!< group code = 39\n
+														 * optional, defaults to 0.0.  */
+														double pattern_scale,
+														/*!< group code 41\n
+														 * pattern fill only. */
+														double pixel_size,
+														/*!< group code 47. */
+														double pattern_angle,
+														/*!< group code 52\n
+														 * pattern fill only. */
+														int color,
+														/*!< group code = 62\n
+														 * optional, defaults to \c BYLAYER. */
+														int paperspace,
+														/*!< group code = 67\n
+														 * optional, defaults to 0 (modelspace). */
+														int solid_fill,
+														/*!< group code = 70\n
+														 * 0 = pattern fill\n
+														 * 1 = solid fill. */
+														int associative,
+														/*!< group code = 71\n
+														 * 0 = non-associative\n
+														 * 1 = associative. */
+														int style,
+														/*!< group code = 75\n
+														 * 0 = hatch "odd parity" area (Normal style)\n
+														 * 1 = hatch outermost area only (Outer style)\n
+														 * 2 = hatch through entire area (Ignore style). */
+														int pattern_style,
+														/*!< group code = 76\n
+														 * 0 = user defined\n
+														 * 1 = predefined\n
+														 * 2 = custom. */
+														int pattern_double,
+														/*!< group code = 77\n
+														 * pattern fill only\n
+														 * 0 = not double\n
+														 * 1 = double. */
+														int pattern_def_lines,
+														/*!< group code = 78\n
+														 * number of pattern definition lines. */
+														int boundary_paths,
+														/*!< group code = 91\n
+														 * number of boundary paths (loops). */
+														int seed_points,
+														/*!< group code = 98\n
+														 * number of seed points.  */
+														double *seed_x0,
+														/*!< group code = 10\n
+														 * seed point X-value. */
+														double *seed_y0
+														/*!< group code = 20\n
+														 * seed point Y-value.  */
+	) {
+	char *dxf_entity_name;
+	int i;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_hatch () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("HATCH");
+	if (strcmp(layer, "") == 0) {
+		fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		fprintf(stderr, "    %s entity is relocated to layer 0", dxf_entity_name);
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	fprintf(fp, "100\nAcDbHatch\n");
+	fprintf(fp, "  2\n%s\n", pattern_name);
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	fprintf(fp, "210\n%f\n", extr_x0);
+	fprintf(fp, "220\n%f\n", extr_y0);
+	fprintf(fp, "230\n%f\n", extr_z0);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	if (!solid_fill) {
+		fprintf(fp, " 42\n%f\n", pattern_scale);
+	}
+	fprintf(fp, " 47\n%f\n", pixel_size);
+	if (!solid_fill) {
+		fprintf(fp, " 52\n%f\n", pattern_angle);
+	}
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+	fprintf(fp, " 70\n%d\n", solid_fill);
+	fprintf(fp, " 71\n%d\n", associative);
+	fprintf(fp, " 75\n%d\n", style);
+	if (!solid_fill) {
+		fprintf(fp, " 77\n%d\n", pattern_double);
+	}
+	fprintf(fp, " 78\n%d\n", pattern_def_lines);
+	fprintf(fp, " 98\n%d\n", seed_points);
+	for (i = 0; i < seed_points; i++) {
+		fprintf(fp, " 10\n%f\n", seed_x0[i]);
+		fprintf(fp, " 20\n%f\n", seed_y0[i]);
+	}
+	fprintf(fp, " 91\n%d\n", boundary_paths);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_hatch () function.\n", __FILE__, __LINE__);
+#endif
+	return;
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a hatch boundary path polyline.
+ *
+ * This entity requires AutoCAD version R14 or higher.
+ */
+static void dxf_write_hatch_boundary_path_polyline(FILE * fp,
+																									 /*!< file pointer to output file (or device). */
+																									 int type_flag,
+																									 /*!< group code 92.  */
+																									 int polyline_has_bulge,
+																									 /*!< group code = 72\n
+																									  * polyline boundary data group only. */
+																									 int polyline_is_closed,
+																									 /*!< group code = 73\n
+																									  * polyline boundary data group only. */
+																									 int polyline_vertices
+																									 /*!< group code 93\n
+																									  * number of polyline vertices to follow. */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_hatch_boundary_path_polyline () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, " 92\n%d\n", type_flag);
+	fprintf(fp, " 72\n%d\n", polyline_has_bulge);
+	fprintf(fp, " 73\n%d\n", polyline_is_closed);
+	fprintf(fp, " 93\n%d\n", polyline_vertices);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_hatch_boundary_path_polyline () function.\n", __FILE__, __LINE__);
+#endif
+	return;
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a hatch boundary polyline entity.
+ *
+ * This entity requires AutoCAD version R14 or higher.
+ */
+static void dxf_write_hatch_boundary_path_polyline_vertex(FILE * fp,
+																													/*!< file pointer to output file (or device). */
+																													double x0,
+																													/*!< group code = 10\n
+																													 * X-value of vertex point. */
+																													double y0,
+																													/*!< group code = 20\n
+																													 * Y-value of vertex point. */
+																													double bulge
+																													/*!< group code 42\n
+																													 * bulge of polyline vertex\n
+																													 * optional, defaults to 0.0. */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_hatch_boundary_polyline_vertex () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	if (bulge != 0.0) {
+		fprintf(fp, " 42\n%f\n", bulge);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_hatch_boundary_polyline_vertex () function.\n", __FILE__, __LINE__);
+#endif
+	return;
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a imperial DXF header.
+ *
+ * Fall back for if no default imperial header template file exists in the
+ * pcb/src/hid/dxf/template directory.\n
+ * Write down a DXF header from scratch based on imperial values.\n
+ * Included sections and tables are:\n
+ * <ul>
+* <li>HEADER section
+ * <li>CLASSES section
+ * <li>TABLES section
+ *   <ul>
+ *   <li>VPORT table
+ *   <li>LTYPE table
+ *   <li>LAYER table
+ *   <li>STYLE table
+ *   <li>VIEW table
+ *   <li>UCS table
+ *   <li>APPID table
+ *   <li>DIMSTYLE table
+ *   </ul>
+ * </ul>
+ */
+static void dxf_write_header_imperial_new()
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_header_imperial_new () function.\n", __FILE__, __LINE__);
+#endif
+	/* write an imperial HEADER section */
+	fprintf(fp, "  0\nSECTION\n");
+	fprintf(fp, "  2\nHEADER\n");	/* Section name. */
+	fprintf(fp, "  9\n$ACADVER\n  1\nAC1014\n");	/* AutoCAD drawing database version number. */
+	fprintf(fp, "  9\n$ACADMAINTVER\n 70\n     0\n");	/* Maintenance version number. */
+	fprintf(fp, "  9\n$DWGCODEPAGE\n  3\nANSI_1252\n");	/* Drawing code page. */
+	fprintf(fp, "  9\n$INSBASE\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Insertion base set by BASE command (in WCS). */
+	fprintf(fp, "  9\n$EXTMIN\n0 10\n-0.012816\n 20\n-0.009063\n 30\n-0.001526\n");	/* X, Y, and Z drawing extents lower-left corner (in WCS). */
+	fprintf(fp, "  9\n$EXTMAX\n 10\n88.01056\n 20\n35.022217\n 30\n0.0\n");	/* X, Y, and Z drawing extents upper-right corner (in WCS). */
+	fprintf(fp, "  9\n$LIMMIN\n 10\n0.0\n 20\n0.0\n");	/* XY drawing limits lower-left corner (in WCS). */
+	fprintf(fp, "  9\n$LIMMAX\n 10\n420.0\n 20\n297.0\n");	/* XY drawing limits upper-right corner (in WCS). */
+	fprintf(fp, "  9\n$ORTHOMODE\n 70\n     0\n");	/* Ortho mode on if nonzero. */
+	fprintf(fp, "  9\n$REGENMODE\n 70\n     1\n");	/* REGENAUTO mode on if nonzero. */
+	fprintf(fp, "  9\n$FILLMODE\n 70\n     1\n");	/* Fill mode on if nonzero. */
+	fprintf(fp, "  9\n$QTEXTMODE\n 70\n     0\n");	/* Quick text mode on if nonzero. */
+	fprintf(fp, "  9\n$MIRRTEXT\n 70\n     1\n");	/* Mirror text if nonzero. */
+	fprintf(fp, "  9\n$DRAGMODE\n 70\n     2\n");	/* 0 = off, 1 = on, 2 = auto. */
+	fprintf(fp, "  9\n$LTSCALE\n 40\n1.0\n");	/* Global linetype scale. */
+	fprintf(fp, "  9\n$OSMODE\n 70\n   125\n");	/* Running object snap modes. */
+	fprintf(fp, "  9\n$ATTMODE\n 70\n     1\n");	/* Attribute visibility: 0 = none, 1 = normal, 2 = all. */
+	fprintf(fp, "  9\n$TEXTSIZE\n 40\n0.1\n");	/* Default text height = 100 mil. */
+	fprintf(fp, "  9\n$TRACEWID\n 40\n1.0\n");	/* Default trace width. */
+	fprintf(fp, "  9\n$TEXTSTYLE\n  7\nSTANDARD\n");	/* Current text style name. */
+	fprintf(fp, "  9\n$CLAYER\n  8\n0\n");	/* Current layer name. */
+	fprintf(fp, "  9\n$CELTYPE\n  6\nBYLAYER\n");	/* Entity linetype name, or BYBLOCK or BYLAYER. */
+	fprintf(fp, "  9\n$CECOLOR\n 62\n   256\n");	/* Current entity color number: 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$CELTSCALE\n 40\n1.0\n");	/* Current entity linetype scale. */
+	fprintf(fp, "  9\n$DELOBJ\n 70\n     1\n");	/* Controls object deletion: 0=deleted, 1=retained. */
+	fprintf(fp, "  9\n$DISPSILH\n 70\n     0\n");	/* Controls the display of silhouette curves of body objects in wire-frame mode: 0=Off, 1=On. */
+	fprintf(fp, "  9\n$DIMSCALE\n 40\n1.0\n");	/* Overall dimensioning scale factor. */
+	fprintf(fp, "  9\n$DIMASZ\n 40\n0.1\n");	/* Dimensioning arrow size = 100 mil. */
+	fprintf(fp, "  9\n$DIMEXO\n 40\n0.01\n");	/* Extension line offset = 10 mil. */
+	fprintf(fp, "  9\n$DIMDLI\n 40\n0.1\n");	/* Dimension line increment = 100 mil. */
+	fprintf(fp, "  9\n$DIMRND\n 40\n0.0\n");	/* Rounding value for dimension distances. */
+	fprintf(fp, "  9\n$DIMDLE\n 40\n0.0\n");	/* Dimension line extension */
+	fprintf(fp, "  9\n$DIMEXE\n 40\n0.05\n");	/* Extension line extension = 50 mil. */
+	fprintf(fp, "  9\n$DIMTP\n 40\n0.0\n");	/* Plus tolerance. */
+	fprintf(fp, "  9\n$DIMTM\n 40\n0.0\n");	/* Minus tolerance. */
+	fprintf(fp, "  9\n$DIMTXT\n 40\n0.1\n");	/* Dimensioning text height = 100 mil. */
+	fprintf(fp, "  9\n$DIMCEN\n 40\n0.01\n");	/* Size of center mark/lines = 10 mil. */
+	fprintf(fp, "  9\n$DIMTSZ\n 40\n0.01\n");	/* Dimensioning tick size = 10 mil; 0 = no ticks. */
+	fprintf(fp, "  9\n$DIMTOL\n 70\n     0\n");	/* Dimension tolerances generated if nonzero. */
+	fprintf(fp, "  9\n$DIMLIM\n 70\n     0\n");	/* Dimension limits generated if nonzero. */
+	fprintf(fp, "  9\n$DIMTIH\n 70\n     0\n");	/* Text inside horizontal if nonzero. */
+	fprintf(fp, "  9\n$DIMTOH\n 70\n     0\n");	/* Text outside horizontal if nonzero. */
+	fprintf(fp, "  9\n$DIMSE1\n 70\n     0\n");	/* First extension line suppressed if nonzero. */
+	fprintf(fp, "  9\n$DIMSE2\n 70\n     0\n");	/* Second extension line suppressed if nonzero. */
+	fprintf(fp, "  9\n$DIMTAD\n 70\n     1\n");	/* Text above dimension line if nonzero. */
+	fprintf(fp, "  9\n$DIMZIN\n 70\n     8\n");	/* Controls suppression of zeros for primary unit values. */
+	fprintf(fp, "  9\n$DIMBLK\n  1\n\n");	/* Arrow block name. */
+	fprintf(fp, "  9\n$DIMASO\n 70\n     1\n");	/* 1 = create associative dimensioning, 0 = draw individual entities. */
+	fprintf(fp, "  9\n$DIMSHO\n 70\n     1\n");	/* 1 = Recompute dimensions while dragging, 0 = drag original image. */
+	fprintf(fp, "  9\n$DIMPOST\n  1\n\n");	/* General dimensioning suffix. */
+	fprintf(fp, "  9\n$DIMAPOST\n  1\n\n");	/* Alternate dimensioning suffix. */
+	fprintf(fp, "  9\n$DIMALT\n 70\n     0\n");	/* Alternate unit dimensioning performed if nonzero. */
+	fprintf(fp, "  9\n$DIMALTD\n 70\n     4\n");	/* Alternate unit decimal places. */
+	fprintf(fp, "  9\n$DIMALTF\n 40\n0.0254\n");	/* Alternate unit scale factor = inch --> mm. */
+	fprintf(fp, "  9\n$DIMLFAC\n 40\n1.0\n");	/* Linear measurements scale factor. */
+	fprintf(fp, "  9\n$DIMTOFL\n 70\n     1\n");	/* If text outside extensions, force line extensions between extensions if nonzero. */
+	fprintf(fp, "  9\n$DIMTVP\n 40\n0.0\n");	/* Text vertical position. */
+	fprintf(fp, "  9\n$DIMTIX\n 70\n     0\n");	/* Force text inside extensions if nonzero. */
+	fprintf(fp, "  9\n$DIMSOXD\n 70\n     0\n");	/* Suppress outside-extensions dimension lines if nonzero. */
+	fprintf(fp, "  9\n$DIMSAH\n 70\n     0\n");	/* Use separate arrow blocks if nonzero. */
+	fprintf(fp, "  9\n$DIMBLK1\n  1\n\n");	/* First arrow block name. */
+	fprintf(fp, "  9\n$DIMBLK2\n  1\n\n");	/* Second arrow block name. */
+	fprintf(fp, "  9\n$DIMSTYLE\n  2\nSTANDARD\n");	/* Dimension style name. */
+	fprintf(fp, "  9\n$DIMCLRD\n 70\n     0\n");	/*Dimension line color: range is 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$DIMCLRE\n 70\n     0\n");	/* Dimension extension line color:  range is 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$DIMCLRT\n 70\n     0\n");	/* Dimension text color:  range is 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$DIMTFAC\n 40\n1.0\n");	/* Dimension tolerance display scale factor. */
+	fprintf(fp, "  9\n$DIMGAP\n 40\n25.0\n");	/* Dimension line gap = 25 mil. */
+	fprintf(fp, "  9\n$DIMJUST\n 70\n     0\n");	/* Horizontal dimension text position: 0=above dimension line and center-justified between extension lines, 1=above dimension line and next to first extension line, 2=above dimension line and next to second extension line, 3=above and center-justified to first extension line, 4=above and center-justified to second extension line. */
+	fprintf(fp, "  9\n$DIMSD1\n 70\n     0\n");	/* Suppression of first extension line: 0=not suppressed, 1=suppressed. */
+	fprintf(fp, "  9\n$DIMSD2\n 70\n     0\n");	/* Suppression of second extension line: 0=not suppressed, 1=suppressed. */
+	fprintf(fp, "  9\n$DIMTOLJ\n 70\n     1\n");	/* Vertical justification for tolerance values: 0=Top, 1=Middle, 2=Bottom. */
+	fprintf(fp, "  9\n$DIMTZIN\n 70\n     0\n");	/* Controls suppression of zeros for tolerance values: 0 = Suppresses zero feet and precisely zero inches; 1 = Includes zero feet and precisely zero inches; 2 = Includes zero feet and suppresses zero inches; 3 = Includes zero inches and suppresses zero feet. */
+	fprintf(fp, "  9\n$DIMALTZ\n 70\n     0\n");	/* Controls suppression of zeros for alternate unit dimension values: 0 = Suppresses zero feet and precisely zero inches; 1 = Includes zero feet and precisely zero inches; 2 = Includes zero feet and suppresses zero inches; 3 = Includes zero inches and suppresses zero feet. */
+	fprintf(fp, "  9\n$DIMALTTZ\n 70\n     0\n");	/* Controls suppression of zeros for alternate tolerance values: 0 = Suppresses zero feet and precisely zero inches; 1 = Includes zero feet and precisely zero inches; 2 = Includes zero feet and suppresses zero inches; 3 = Includes zero inches and suppresses zero feet. */
+	fprintf(fp, "  9\n$DIMFIT\n 70\n     3\n");	/* Placement of text and arrowheads; Possible values: 0 through 3. */
+	fprintf(fp, "  9\n$DIMUPT\n 70\n     0\n");	/* Cursor functionality for user positioned text:  0=controls only the dimension line location, 1=controls the text position as well as the dimension line location. */
+	fprintf(fp, "  9\n$DIMUNIT\n 70\n     2\n");	/* Units format for all dimension style family members except angular: 1 = Scientific; 2 = Decimal; 3 = Engineering; 4 = Architectural (stacked); 5 = Fractional (stacked); 6 = Architectural; 7 = Fractional. */
+	fprintf(fp, "  9\n$DIMDEC\n 70\n     4\n");	/* Number of decimal places for the tolerance values of a primary units dimension. */
+	fprintf(fp, "  9\n$DIMTDEC\n 70\n     4\n");	/* Number of decimal places to display the tolerance values. */
+	fprintf(fp, "  9\n$DIMALTU\n 70\n     2\n");	/*Units format for alternate units of all dimension style family members except angular: 1 = Scientific; 2 = Decimal; 3 = Engineering; 4 = Architectural (stacked); 5 = Fractional (stacked); 6 = Architectural; 7 = Fractional. */
+	fprintf(fp, "  9\n$DIMALTTD\n 70\n     2\n");	/*Number of decimal places for tolerance values of an alternate units dimension. */
+	fprintf(fp, "  9\n$DIMTXSTY\n  7\nSTANDARD\n");	/* Dimension text style. */
+	fprintf(fp, "  9\n$DIMAUNIT\n 70\n     0\n");	/* Angle format for angular dimensions: 0=Decimal degrees, 1=Degrees/minutes/seconds, 2=Gradians, 3=Radians, 4=Surveyor's units. */
+	fprintf(fp, "  9\n$LUNITS\n 70\n     2\n");	/* Units format for coordinates and distances. */
+	fprintf(fp, "  9\n$LUPREC\n 70\n     4\n");	/* Units precision for coordinates and distances */
+	fprintf(fp, "  9\n$SKETCHINC\n 40\n1.0\n");	/* Sketch record increment. */
+	fprintf(fp, "  9\n$FILLETRAD\n 40\n1.0\n");	/* Fillet radius. */
+	fprintf(fp, "  9\n$AUNITS\n 70\n     0\n");	/* Units format for angles. */
+	fprintf(fp, "  9\n$AUPREC\n 70\n     0\n");	/* Units precision for angles. */
+	fprintf(fp, "  9\n$MENU\n  1\n.\n");	/* Name of menu file. */
+	fprintf(fp, "  9\n$ELEVATION\n 40\n0.0\n");	/* Current elevation set by ELEV command. */
+	fprintf(fp, "  9\n$PELEVATION\n 40\n0.0\n");	/* Current paper space elevation. */
+	fprintf(fp, "  9\n$THICKNESS\n 40\n0.0\n");	/* Current thickness set by ELEV command. */
+	fprintf(fp, "  9\n$LIMCHECK\n 70\n     0\n");	/* Nonzero if limits checking is on. */
+	fprintf(fp, "  9\n$BLIPMODE\n 70\n     0\n");	/* Blip mode on if nonzero. */
+	fprintf(fp, "  9\n$CHAMFERA\n 40\n10.0\n");	/* First chamfer distance. */
+	fprintf(fp, "  9\n$CHAMFERB\n 40\n10.0\n");	/* Second chamfer distance. */
+	fprintf(fp, "  9\n$CHAMFERC\n 40\n0.0\n");	/* Chamfer length. */
+	fprintf(fp, "  9\n$CHAMFERD\n 40\n0.0\n");	/* Chamfer angle. */
+	fprintf(fp, "  9\n$SKPOLY\n 70\n     0\n");	/* 0 = sketch lines, 1 = sketch polylines. */
+	fprintf(fp, "  9\n$TDCREATE\n 40\n0.0\n");	/* Date/time of drawing creation. */
+	fprintf(fp, "  9\n$TDUPDATE\n 40\n0.0\n");	/* Date/time of last drawing update. */
+	fprintf(fp, "  9\n$TDINDWG\n 40\n0.0\n");	/* Cumulative editing time for this drawing. */
+	fprintf(fp, "  9\n$TDUSRTIMER\n 40\n0.0\n");	/* User elapsed timer. */
+	fprintf(fp, "  9\n$USRTIMER\n 70\n     1\n");	/* 0 = timer off, 1 = timer on. */
+	fprintf(fp, "  9\n$ANGBASE\n 50\n0.0\n");	/* Angle 0 direction . */
+	fprintf(fp, "  9\n$ANGDIR\n 70\n     0\n");	/* 1 = clockwise angles, 0 = counterclockwise. */
+	fprintf(fp, "  9\n$PDMODE\n 70\n    98\n");	/* Point display mode. */
+	fprintf(fp, "  9\n$PDSIZE\n 40\n0.0\n");	/* Point display size. */
+	fprintf(fp, "  9\n$PLINEWID\n 40\n0.0\n");	/* Default polyline width. */
+	fprintf(fp, "  9\n$COORDS\n 70\n     2\n");	/* Coordinate display: 0 = static, 1 = continuous update, 2 = "d<a" format. */
+	fprintf(fp, "  9\n$SPLFRAME\n 70\n     0\n");	/* Spline control polygon display: 1 = on, 0 = off. */
+	fprintf(fp, "  9\n$SPLINETYPE\n 70\n     6\n");	/* Spline curve type for PEDIT Spline. */
+	fprintf(fp, "  9\n$SPLINESEGS\n 70\n     8\n");	/* Number of line segments per spline patch. */
+	fprintf(fp, "  9\n$ATTDIA\n 70\n     0\n");	/* Attribute entry dialogs: 1 = on, 0 = off. */
+	fprintf(fp, "  9\n$ATTREQ\n 70\n     1\n");	/* Attribute prompting during INSERT: 1 = on, 0 = off. */
+	fprintf(fp, "  9\n$HANDLING\n 70\n     1\n");	/* Next available handle. */
+	fprintf(fp, "  9\n$HANDSEED\n  5\n262\n");	/* Next available handle. */
+	fprintf(fp, "  9\n$SURFTAB1\n 70\n     6\n");	/* Number of mesh tabulations in first direction. */
+	fprintf(fp, "  9\n$SURFTAB2\n 70\n     6\n");	/* Number of mesh tabulations in second direction. */
+	fprintf(fp, "  9\n$SURFTYPE\n 70\n     6\n");	/* Surface type for PEDIT Smooth. */
+	fprintf(fp, "  9\n$SURFU\n 70\n     6\n");	/* Surface density (for PEDIT Smooth) in M direction. */
+	fprintf(fp, "  9\n$SURFV\n 70\n     6\n");	/* Surface density (for PEDIT Smooth) in N direction. */
+	fprintf(fp, "  9\n$UCSNAME\n  2\n\n");	/* Name of current UCS. */
+	fprintf(fp, "  9\n$UCSORG\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Origin of current UCS (in WCS). */
+	fprintf(fp, "  9\n$UCSXDIR\n 10\n1.0\n 20\n0.0\n 30\n0.0\n");	/* Direction of current UCS's X axis (in WCS). */
+	fprintf(fp, "  9\n$UCSYDIR\n 10\n0.0\n 20\n1.0\n 30\n0.0\n");	/* Direction of current UCS's Y axis (in WCS). */
+	fprintf(fp, "  9\n$PUCSNAME\n  2\n\n");	/* Current paper space UCS name. */
+	fprintf(fp, "  9\n$PUCSORG\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Current paper space UCS origin. */
+	fprintf(fp, "  9\n$PUCSXDIR\n 10\n1.0\n 20\n0.0\n 30\n0.0\n");	/* Current paper space UCS X axis. */
+	fprintf(fp, "  9\n$PUCSYDIR\n 10\n0.0\n 20\n1.0\n 30\n0.0\n");	/* Current paper space UCS Y axis. */
+	fprintf(fp, "  9\n$USERI1\n 70\n     0\n");	/* Five integer variables intended for use by third-party developers. */
+	fprintf(fp, "  9\n$USERI2\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERI3\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERI4\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERI5\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERR1\n 40\n0.0\n");	/* Five real variables intended for use by third-party developers. */
+	fprintf(fp, "  9\n$USERR2\n 40\n0.0\n");
+	fprintf(fp, "  9\n$USERR3\n 40\n0.0\n");
+	fprintf(fp, "  9\n$USERR4\n 40\n0.0\n");
+	fprintf(fp, "  9\n$USERR5\n 40\n0.0\n");
+	fprintf(fp, "  9\n$WORLDVIEW\n 70\n     1\n");	/*1 = set UCS to WCS during DVIEW/VPOINT, 0 = don't change UCS. */
+	fprintf(fp, "  9\n$SHADEDGE\n 70\n     3\n");	/* 0 = faces shaded, edges not highlighted; 1 = faces shaded, edges highlighted in black; 2 = faces not filled, edges in entity color; 3 = faces in entity color, edges in black. */
+	fprintf(fp, "  9\n$SHADEDIF\n 70\n    70\n");	/* Percent ambient/diffuse light, range 1-100, default 70. */
+	fprintf(fp, "  9\n$TILEMODE\n 70\n     1\n");	/* 1 for previous release compatibility mode, 0 otherwise. */
+	fprintf(fp, "  9\n$MAXACTVP\n 70\n    48\n");	/* Sets maximum number of viewports to be regenerated. */
+	fprintf(fp, "  9\n$PINSBASE\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Paper space insertion base point. */
+	fprintf(fp, "  9\n$PLIMCHECK\n 70\n     0\n");	/* Limits checking in paper space when nonzero. */
+	fprintf(fp, "  9\n$PEXTMIN\n 10\n1.000000E+20\n 20\n1.000000E+20\n 30\n1.000000E+20\n");	/* Minimum X, Y, and Z extents for paper space. */
+	fprintf(fp, "  9\n$PEXTMAX\n 10\n-1.000000E+20\n 20\n-1.000000E+20\n 30\n-1.000000E+20\n");	/* Maximum X, Y, and Z extents for paper space. */
+	fprintf(fp, "  9\n$PLIMMIN\n 10\n0.0\n 20\n0.0\n");	/* Minimum X and Y limits in paper space. */
+	fprintf(fp, "  9\n$PLIMMAX\n 10\n11000.0\n 20\n8500.0\n");	/* Maximum X and Y limits in paper space. */
+	fprintf(fp, "  9\n$UNITMODE\n 70\n     0\n");	/* Low bit set = display fractions, feet-and-inches, and surveyor's angles in input format. */
+	fprintf(fp, "  9\n$VISRETAIN\n 70\n     1\n");	/* 0 = don't retain xref-dependent visibility settings, 1 = retain; xref-dependent visibility settings. */
+	fprintf(fp, "  9\n$PLINEGEN\n 70\n     0\n");	/* Governs the generation of linetype patterns around the vertices of a 2D polyline: 1 = linetype is generated in a continuous pattern around vertices of the polyline; 0 = each segment of the polyline starts and ends with a dash. */
+	fprintf(fp, "  9\n$PSLTSCALE\n 70\n     1\n");	/* Controls paper space linetype scaling: 1 = no special linetype scaling; 0 = viewport scaling governs linetype scaling. */
+	fprintf(fp, "  9\n$TREEDEPTH\n 70\n  3020\n");	/* Specifies the maximum depth of the spatial index. */
+	fprintf(fp, "  9\n$PICKSTYLE\n 70\n     1\n");	/* Controls group selection and associative hatch selection: 0= No group selection or associative hatch selection; 1= Group selection; 2 = Associative hatch selection; 3 = Group selection and associative hatch selection. */
+	fprintf(fp, "  9\n$CMLSTYLE\n  2\nSTANDARD\n");	/* Current multiline style name. */
+	fprintf(fp, "  9\n$CMLJUST\n 70\n     0\n");	/* Current multiline justification: 0 = Top; 1 = Middle; 2 = Bottom. */
+	fprintf(fp, "  9\n$CMLSCALE\n 40\n1.0\n");	/* Current multiline scale. */
+	fprintf(fp, "  9\n$PROXYGRAPHICS\n 70\n     1\n");	/* Controls the saving of proxy object images. */
+	fprintf(fp, "  9\n$MEASUREMENT\n 70\n     0\n");	/* Sets drawing units. 0 = English; 1 = Metric. */
+	fprintf(fp, "  0\nENDSEC\n");
+	/* write a CLASSES section */
+	fprintf(fp, "  0\nSECTION\n");
+	fprintf(fp, "  2\nCLASSES\n");	/* Section name. */
+	fprintf(fp, "  0\nENDSEC\n");
+	/* write a TABLES section */
+	fprintf(fp, "  0\nSECTION\n");
+	fprintf(fp, "  2\nTABLES\n");	/* Section name. */
+	/* write a VPORT (viewport) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nVPORT\n");	/* Table name. */
+	fprintf(fp, "  5\n23A\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     2\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nVPORT\n");
+	fprintf(fp, "  5\n261\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbViewportTableRecord\n");
+	fprintf(fp, "  2\n*ACTIVE\n");	/* Viewport name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 10\n0.0\n 20\n0.0\n");	/* Lower-left corner of viewport. */
+	fprintf(fp, " 11\n1.0\n 21\n1.0\n");	/* Upper-right corner of viewport. */
+	fprintf(fp, " 12\n43.998872\n 22\n17.506577\n");	/* View center point (in DCS). */
+	fprintf(fp, " 13\n0.0\n 23\n0.0\n");	/* Snap base point. */
+	fprintf(fp, " 14\n1.0\n 24\n1.0\n");	/* Snap spacing X and Y. */
+	fprintf(fp, " 15\n10.0\n 25\n10.0\n");	/* Grid spacing X and Y. */
+	fprintf(fp, " 16\n0.0\n 26\n0.0\n 36\n1.0\n");	/* View direction from target point (in WCS). */
+	fprintf(fp, " 17\n0.0\n 27\n0.0\n 37\n0.0\n");	/* View target point (in WCS). */
+	fprintf(fp, " 40\n47.164502\n");	/* View height. */
+	fprintf(fp, " 41\n1.882514\n");	/* Viewport aspect ratio. */
+	fprintf(fp, " 42\n50.0\n");		/* Lens length. */
+	fprintf(fp, " 43\n0.0\n");		/* Front clipping plane (offset from target point). */
+	fprintf(fp, " 44\n0.0\n");		/* Back clipping plane (offset from target point). */
+	fprintf(fp, " 50\n0.0\n");		/* Snap rotation angle. */
+	fprintf(fp, " 51\n0.0\n");		/* View twist angle. */
+	fprintf(fp, " 71\n     0\n");	/* View mode (see VIEWMODE system variable). */
+	fprintf(fp, " 72\n   100\n");	/* Circle zoom percent. */
+	fprintf(fp, " 73\n     1\n");	/* Fast zoom setting. */
+	fprintf(fp, " 74\n     3\n");	/* UCSICON setting. */
+	fprintf(fp, " 75\n     0\n");	/* Snap on/off. */
+	fprintf(fp, " 76\n     0\n");	/* Grid on/off. */
+	fprintf(fp, " 77\n     0\n");	/* Snap style. */
+	fprintf(fp, " 78\n     0\n");	/* Snap isopair. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write LTYPE (linetype) table entries */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nLTYPE\n");	/* Table name. */
+	fprintf(fp, "  5\n237\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     1\n");	/* Standard flag values. */
+	/* write a record entry for a BYBLOCK linetype */
+	fprintf(fp, "  0\nLTYPE\n");
+	fprintf(fp, "  5\n244\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLinetypeTableRecord\n");
+	fprintf(fp, "  2\nBYBLOCK\n");	/* Linetype name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\n\n");				/* Descriptive text for linetype. */
+	fprintf(fp, " 72\n    65\n");	/* Alignment code; value is always 65, the ASCII code for A. */
+	fprintf(fp, " 73\n     0\n");	/* The number of linetype elements. */
+	fprintf(fp, " 40\n0.0\n");		/* Total pattern length. */
+	/* write a record entry for a BYLAYER linetype */
+	fprintf(fp, "  0\nLTYPE\n");
+	fprintf(fp, "  5\n245\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLinetypeTableRecord\n");
+	fprintf(fp, "  2\nBYLAYER\n");	/* Linetype name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\n\n");				/* Descriptive text for linetype. */
+	fprintf(fp, " 72\n    65\n");	/* Alignment code; value is always 65, the ASCII code for A. */
+	fprintf(fp, " 73\n     0\n");	/* The number of linetype elements. */
+	fprintf(fp, " 40\n0.0\n");		/* Total pattern length. */
+	/* write a record entry for a CONTINUOUS linetype */
+	fprintf(fp, "  0\nLTYPE\n");
+	fprintf(fp, "  5\n246\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLinetypeTableRecord\n");
+	fprintf(fp, "  2\nCONTINUOUS\n");	/* Linetype name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\nSolid line\n");	/* Descriptive text for linetype. */
+	fprintf(fp, " 72\n    65\n");	/* Alignment code; value is always 65, the ASCII code for A. */
+	fprintf(fp, " 73\n     0\n");	/* The number of linetype elements. */
+	fprintf(fp, " 40\n0.0\n");		/* Total pattern length. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write LAYER table entries */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nLAYER\n");	/* Table name. */
+	fprintf(fp, "  5\n234\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     2\n");	/* Standard flag values. */
+	/* write a record entry for layer "0" */
+	fprintf(fp, "  0\nLAYER\n");
+	fprintf(fp, "  5\n240\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLayerTableRecord\n");
+	fprintf(fp, "  2\n0\n");			/* Layer name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 62\n     7\n");	/* Color number (if negative, layer is Off). */
+	fprintf(fp, "  6\nCONTINUOUS\n");	/* Linetype name. */
+	/* * write a record entry for a layer "ASHADE" */
+	fprintf(fp, "  0\nLAYER\n");
+	fprintf(fp, "  5\n251\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLayerTableRecord\n");
+	fprintf(fp, "  2\nASHADE\n");	/* Layer name. */
+	fprintf(fp, " 70\n     4\n");	/* Standard flag values. */
+	fprintf(fp, " 62\n     7\n");	/* Color number (if negative, layer is Off). */
+	fprintf(fp, "  6\nCONTINUOUS\n");	/* Linetype name. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write STYLE table entries */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nSTYLE\n");	/* Table name. */
+	fprintf(fp, "  5\n235\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     2\n");	/* Standard flag values. */
+	/* write a record entry for a style "STANDARD" */
+	fprintf(fp, "  0\nSTYLE\n");
+	fprintf(fp, "  5\n241\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbTextStyleTableRecord\n");
+	fprintf(fp, "  2\nSTANDARD\n");	/* Style name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 40\n0.0\n");		/* Fixed text height; 0 if not fixed/ */
+	fprintf(fp, " 41\n1.0\n");		/* Width factor. */
+	fprintf(fp, " 50\n0.0\n");		/* Oblique angle. */
+	fprintf(fp, " 71\n     0\n");	/* Text generation flags; 2 = Text is backward (mirrored in X); 4 = Text is upside down (mirrored in Y). */
+	fprintf(fp, " 42\n2.5\n");		/* Last height used. */
+	fprintf(fp, "  3\ntxt\n");		/* Primary font file name. */
+	fprintf(fp, "  4\n\n");				/* Bigfont file name; blank if none. */
+	/* write a record entry for a style "ASHADE" */
+	fprintf(fp, "  0\nSTYLE\n");
+	fprintf(fp, "  5\n252\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbTextStyleTableRecord\n");
+	fprintf(fp, "  2\nASHADE\n");	/* Style name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 40\n0.2\n");		/* Fixed text height; 0 if not fixed/ */
+	fprintf(fp, " 41\n1.0\n");		/* Width factor. */
+	fprintf(fp, " 50\n0.0\n");		/* Oblique angle. */
+	fprintf(fp, " 71\n     0\n");	/* Text generation flags; 2 = Text is backward (mirrored in X); 4 = Text is upside down (mirrored in Y). */
+	fprintf(fp, " 42\n2.5\n");		/* Last height used. */
+	fprintf(fp, "  3\nsimplex.shx\n");	/* Primary font file name. */
+	fprintf(fp, "  4\n\n");				/* Bigfont file name; blank if none. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a VIEW table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nVIEW\n");		/* Table name. */
+	fprintf(fp, "  5\n238\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a UCS (User Coordinate System) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nUCS\n");		/* Table name. */
+	fprintf(fp, "  5\n239\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a APPID (APPlication ID) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nAPPID\n");	/* Table name. */
+	fprintf(fp, "  5\n23B\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     6\n");	/* Standard flag values. */
+	/* write a record entry for a appid "ACAD" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n242\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nACAD\n");		/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_RENDER" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n253\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_RENDER\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_ENTITY_MATERIAL" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n254\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_ENTITY_MATERIAL\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_FINISH" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n255\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_FINISH\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_MATERIAL" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n256\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_MATERIAL\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_GLOBAL" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n257\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_GLOBAL\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a DIMSTYLE (DIMensioning STYLE) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nDIMSTYLE\n");	/* Table name. */
+	fprintf(fp, "  5\n23C\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     1\n");	/* Standard flag values. */
+	/* write a record entry for a dimstyle "STANDARD" */
+	fprintf(fp, "  0\nDIMSTYLE\n");
+	fprintf(fp, "105\n258\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbDimStyleTableRecord\n");
+	fprintf(fp, "  2\nSTANDARD\n");	/* Dimension style name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\n\n");				/* DIMPOST */
+	fprintf(fp, "  4\n\n");				/* DIMAPOST */
+	fprintf(fp, "  5\n\n");				/* DIMBLK */
+	fprintf(fp, "  6\n\n");				/* DIMBLK1 */
+	fprintf(fp, "  7\n\n");				/* DIMBLK2 */
+	fprintf(fp, " 40\n1.0\n");		/* DIMSCALE */
+	fprintf(fp, " 41\n0.18\n");		/* DIMASZ */
+	fprintf(fp, " 42\n0.0625\n");	/* DIMEXO */
+	fprintf(fp, " 43\n0.38\n");		/* DIMDLI */
+	fprintf(fp, " 44\n0.18\n");		/* DIMEXE */
+	fprintf(fp, " 45\n0.0\n");		/* DIMRND */
+	fprintf(fp, " 46\n0.0\n");		/* DIMDLE */
+	fprintf(fp, " 47\n0.0\n");		/* DIMTP */
+	fprintf(fp, " 48\n0.0\n");		/* DIMTM */
+	fprintf(fp, "140\n0.18\n");		/* DIMTXT */
+	fprintf(fp, "141\n0.09\n");		/* DIMCEN */
+	fprintf(fp, "142\n0.0\n");		/* DIMTSZ */
+	fprintf(fp, "143\n25.4\n");		/* DIMALTF */
+	fprintf(fp, "144\n1.0\n");		/* DIMLFAC */
+	fprintf(fp, "145\n0.0\n");		/* DIMTVP */
+	fprintf(fp, "146\n1.0\n");		/* DIMTFAC */
+	fprintf(fp, "147\n0.09\n");		/* DIMGAP */
+	fprintf(fp, " 71\n     0\n");	/* DIMTOL */
+	fprintf(fp, " 72\n     0\n");	/* DIMLIM */
+	fprintf(fp, " 73\n     1\n");	/* DIMTIH */
+	fprintf(fp, " 74\n     1\n");	/* DIMTOH */
+	fprintf(fp, " 75\n     0\n");	/* DIMSE1 */
+	fprintf(fp, " 76\n     0\n");	/* DIMSE2 */
+	fprintf(fp, " 77\n     0\n");	/* DIMTAD */
+	fprintf(fp, " 78\n     0\n");	/* DIMZIN */
+	fprintf(fp, "170\n     0\n");	/* DIMALT */
+	fprintf(fp, "171\n     2\n");	/* DIMALTD */
+	fprintf(fp, "172\n     0\n");	/* DIMTOFL */
+	fprintf(fp, "173\n     0\n");	/* DIMSAH */
+	fprintf(fp, "174\n     0\n");	/* DIMTIX */
+	fprintf(fp, "175\n     0\n");	/* DIMSOXD */
+	fprintf(fp, "176\n     0\n");	/* DIMDLRD */
+	fprintf(fp, "177\n     0\n");	/* DIMCLRE */
+	fprintf(fp, "178\n     0\n");	/* DIMCLRT */
+	fprintf(fp, "270\n     2\n");	/* DIMUNIT */
+	fprintf(fp, "271\n     4\n");	/* DIMDEC */
+	fprintf(fp, "272\n     4\n");	/* DIMTDEC */
+	fprintf(fp, "273\n     2\n");	/* DIMALTU */
+	fprintf(fp, "274\n     2\n");	/* DIMALTTD */
+	fprintf(fp, "340\n241\n");		/* Handle of referenced STYLE object (used instead of storing DIMTXSTY value). */
+	fprintf(fp, "275\n     0\n");	/* DIMAUNIT */
+	fprintf(fp, "280\n     0\n");	/* DIMJUST */
+	fprintf(fp, "281\n     0\n");	/* DIMSD1 */
+	fprintf(fp, "282\n     0\n");	/* DIMSD2 */
+	fprintf(fp, "283\n     1\n");	/* DIMTOLJ */
+	fprintf(fp, "284\n     0\n");	/* DIMTZIN */
+	fprintf(fp, "285\n     0\n");	/* DIMALTZ */
+	fprintf(fp, "286\n     0\n");	/* DIMALTTZ */
+	fprintf(fp, "287\n     3\n");	/* DIMFIT */
+	fprintf(fp, "288\n     0\n");	/* DIMUPT */
+	fprintf(fp, "  0\nENDTAB\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_header_imperial_new () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a metric DXF header.
+ *
+ * Fall back for if no default metric header template file exists in the
+ * pcb/src/hid/dxf/template directory.\n
+ * Write down a DXF header from scratch based on metric values.\n
+ * Included sections and tables are:\n
+ * <ul>
+* <li>HEADER section
+ * <li>CLASSES section
+ * <li>TABLES section
+ *   <ul>
+ *   <li>VPORT table
+ *   <li>LTYPE table
+ *   <li>LAYER table
+ *   <li>STYLE table
+ *   <li>VIEW table
+ *   <li>UCS table
+ *   <li>APPID table
+ *   <li>DIMSTYLE table
+ *   </ul>
+ * </ul>
+ */
+static void dxf_write_header_metric_new()
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_header_metric_new () function.\n", __FILE__, __LINE__);
+#endif
+	/* write a metric HEADER section */
+	fprintf(fp, "  0\nSECTION\n");
+	fprintf(fp, "  2\nHEADER\n");	/* Section name. */
+	fprintf(fp, "  9\n$ACADVER\n  1\nAC1014\n");	/* AutoCAD drawing database version number. */
+	fprintf(fp, "  9\n$ACADMAINTVER\n 70\n     0\n");	/* Maintenance version number. */
+	fprintf(fp, "  9\n$DWGCODEPAGE\n  3\nANSI_1252\n");	/* Drawing code page. */
+	fprintf(fp, "  9\n$INSBASE\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Insertion base set by BASE command (in WCS). */
+	fprintf(fp, "  9\n$EXTMIN\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* X, Y, and Z drawing extents lower-left corner (in WCS). */
+	fprintf(fp, "  9\n$EXTMAX\n 10\n88.01056\n 20\n35.022217\n 30\n0.0\n");	/* X, Y, and Z drawing extents upper-right corner (in WCS). */
+	fprintf(fp, "  9\n$LIMMIN\n 10\n0.0\n 20\n0.0\n");	/* XY drawing limits lower-left corner (in WCS). */
+	fprintf(fp, "  9\n$LIMMAX\n 10\n420.0\n 20\n297.0\n");	/* XY drawing limits upper-right corner (in WCS). */
+	fprintf(fp, "  9\n$ORTHOMODE\n 70\n     0\n");	/* Ortho mode on if nonzero. */
+	fprintf(fp, "  9\n$REGENMODE\n 70\n     1\n");	/* REGENAUTO mode on if nonzero. */
+	fprintf(fp, "  9\n$FILLMODE\n 70\n     1\n");	/* Fill mode on if nonzero. */
+	fprintf(fp, "  9\n$QTEXTMODE\n 70\n     0\n");	/* Quick text mode on if nonzero. */
+	fprintf(fp, "  9\n$MIRRTEXT\n 70\n     1\n");	/* Mirror text if nonzero. */
+	fprintf(fp, "  9\n$DRAGMODE\n 70\n     2\n");	/* 0 = off, 1 = on, 2 = auto. */
+	fprintf(fp, "  9\n$LTSCALE\n 40\n1.0\n");	/* Global linetype scale. */
+	fprintf(fp, "  9\n$OSMODE\n 70\n   125\n");	/* Running object snap modes. */
+	fprintf(fp, "  9\n$ATTMODE\n 70\n     1\n");	/* Attribute visibility: 0 = none, 1 = normal, 2 = all. */
+	fprintf(fp, "  9\n$TEXTSIZE\n 40\n2.5\n");	/* Default text height = 2.5 mm. */
+	fprintf(fp, "  9\n$TRACEWID\n 40\n1.0\n");	/* Default trace width. */
+	fprintf(fp, "  9\n$TEXTSTYLE\n  7\nSTANDARD\n");	/* Current text style name. */
+	fprintf(fp, "  9\n$CLAYER\n  8\n0\n");	/* Current layer name. */
+	fprintf(fp, "  9\n$CELTYPE\n  6\nBYLAYER\n");	/* Entity linetype name, or BYBLOCK or BYLAYER. */
+	fprintf(fp, "  9\n$CECOLOR\n 62\n   256\n");	/* Current entity color number: 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$CELTSCALE\n 40\n1.0\n");	/* Current entity linetype scale. */
+	fprintf(fp, "  9\n$DELOBJ\n 70\n     1\n");	/* Controls object deletion: 0 = deleted, 1 = retained. */
+	fprintf(fp, "  9\n$DISPSILH\n 70\n     0\n");	/* Controls the display of silhouette curves of body objects in wire-frame mode: 0 = Off, 1 = On. */
+	fprintf(fp, "  9\n$DIMSCALE\n 40\n1.0\n");	/* Overall dimensioning scale factor. */
+	fprintf(fp, "  9\n$DIMASZ\n 40\n2.5\n");	/* Dimensioning arrow size = 2.5 mm. */
+	fprintf(fp, "  9\n$DIMEXO\n 40\n0.625\n");	/* Extension line offset = 0.625 mm. */
+	fprintf(fp, "  9\n$DIMDLI\n 40\n3.75\n");	/* Dimension line increment = 3.75 mm. */
+	fprintf(fp, "  9\n$DIMRND\n 40\n0.0\n");	/* Rounding value for dimension distances. */
+	fprintf(fp, "  9\n$DIMDLE\n 40\n0.0\n");	/* Dimension line extension */
+	fprintf(fp, "  9\n$DIMEXE\n 40\n1.25\n");	/* Extension line extension = 1.25 mm. */
+	fprintf(fp, "  9\n$DIMTP\n 40\n0.0\n");	/* Plus tolerance. */
+	fprintf(fp, "  9\n$DIMTM\n 40\n0.0\n");	/* Minus tolerance. */
+	fprintf(fp, "  9\n$DIMTXT\n 40\n2.5\n");	/* Dimensioning text height = 2.5 mm. */
+	fprintf(fp, "  9\n$DIMCEN\n 40\n2.5\n");	/* Size of center mark/lines = 2.5 mm. */
+	fprintf(fp, "  9\n$DIMTSZ\n 40\n0.0\n");	/* Dimensioning tick size; 0 = no ticks. */
+	fprintf(fp, "  9\n$DIMTOL\n 70\n     0\n");	/* Dimension tolerances generated if nonzero. */
+	fprintf(fp, "  9\n$DIMLIM\n 70\n     0\n");	/* Dimension limits generated if nonzero. */
+	fprintf(fp, "  9\n$DIMTIH\n 70\n     0\n");	/* Text inside horizontal if nonzero. */
+	fprintf(fp, "  9\n$DIMTOH\n 70\n     0\n");	/* Text outside horizontal if nonzero. */
+	fprintf(fp, "  9\n$DIMSE1\n 70\n     0\n");	/* First extension line suppressed if nonzero. */
+	fprintf(fp, "  9\n$DIMSE2\n 70\n     0\n");	/* Second extension line suppressed if nonzero. */
+	fprintf(fp, "  9\n$DIMTAD\n 70\n     1\n");	/* Text above dimension line if nonzero. */
+	fprintf(fp, "  9\n$DIMZIN\n 70\n     8\n");	/* Controls suppression of zeros for primary unit values. */
+	fprintf(fp, "  9\n$DIMBLK\n  1\n\n");	/* Arrow block name. */
+	fprintf(fp, "  9\n$DIMASO\n 70\n     1\n");	/* 1 = create associative dimensioning, 0 = draw individual entities. */
+	fprintf(fp, "  9\n$DIMSHO\n 70\n     1\n");	/* 1 = Recompute dimensions while dragging, 0 = drag original image. */
+	fprintf(fp, "  9\n$DIMPOST\n  1\n\n");	/* General dimensioning suffix. */
+	fprintf(fp, "  9\n$DIMAPOST\n  1\n\n");	/* Alternate dimensioning suffix. */
+	fprintf(fp, "  9\n$DIMALT\n 70\n     0\n");	/* Alternate unit dimensioning performed if nonzero. */
+	fprintf(fp, "  9\n$DIMALTD\n 70\n     4\n") /* Alternate unit decimal places. */ ;
+	fprintf(fp, "  9\n$DIMALTF\n 40\n0.0394\n") /* Alternate unit scale factor (mm --> mil). */ ;
+	fprintf(fp, "  9\n$DIMLFAC\n 40\n1.0\n");	/* Linear measurements scale factor. */
+	fprintf(fp, "  9\n$DIMTOFL\n 70\n     1\n");	/* If text outside extensions, force line extensions between extensions if nonzero. */
+	fprintf(fp, "  9\n$DIMTVP\n 40\n0.0\n");	/* Text vertical position. */
+	fprintf(fp, "  9\n$DIMTIX\n 70\n     0\n");	/* Force text inside extensions if nonzero. */
+	fprintf(fp, "  9\n$DIMSOXD\n 70\n     0\n");	/* Suppress outside-extensions dimension lines if nonzero. */
+	fprintf(fp, "  9\n$DIMSAH\n 70\n     0\n");	/* Use separate arrow blocks if nonzero. */
+	fprintf(fp, "  9\n$DIMBLK1\n  1\n\n");	/* First arrow block name. */
+	fprintf(fp, "  9\n$DIMBLK2\n  1\n\n");	/* Second arrow block name. */
+	fprintf(fp, "  9\n$DIMSTYLE\n  2\nSTANDARD\n");	/* Dimension style name. */
+	fprintf(fp, "  9\n$DIMCLRD\n 70\n     0\n");	/* Dimension line color: range is 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$DIMCLRE\n 70\n     0\n");	/* Dimension extension line color:  range is 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$DIMCLRT\n 70\n     0\n");	/* Dimension text color:  range is 0 = BYBLOCK, 256 = BYLAYER. */
+	fprintf(fp, "  9\n$DIMTFAC\n 40\n1.0\n");	/* Dimension tolerance display scale factor. */
+	fprintf(fp, "  9\n$DIMGAP\n 40\n0.625\n") /* Dimension line gap. */ ;
+	fprintf(fp, "  9\n$DIMJUST\n 70\n     0\n");	/* Horizontal dimension text position: 0 = above dimension line and center-justified between extension lines, 1 = above dimension line and next to first extension line, 2=above dimension line and next to second extension line, 3 = above and center-justified to first extension line, 4 = above and center-justified to second extension line. */
+	fprintf(fp, "  9\n$DIMSD1\n 70\n     0\n");	/* Suppression of first extension line: 0 = not suppressed, 1 = suppressed. */
+	fprintf(fp, "  9\n$DIMSD2\n 70\n     0\n");	/* Suppression of second extension line: 0 = not suppressed, 1 = suppressed. */
+	fprintf(fp, "  9\n$DIMTOLJ\n 70\n     1\n");	/* Vertical justification for tolerance values: 0 = Top, 1 = Middle, 2 = Bottom. */
+	fprintf(fp, "  9\n$DIMTZIN\n 70\n     0\n");	/* Controls suppression of zeros for tolerance values: 0 = Suppresses zero feet and precisely zero inches; 1 = Includes zero feet and precisely zero inches; 2 = Includes zero feet and suppresses zero inches; 3 = Includes zero inches and suppresses zero feet. */
+	fprintf(fp, "  9\n$DIMALTZ\n 70\n     0\n");	/* Controls suppression of zeros for alternate unit dimension values: 0 = Suppresses zero feet and precisely zero inches; 1 = Includes zero feet and precisely zero inches; 2 = Includes zero feet and suppresses zero inches; 3 = Includes zero inches and suppresses zero feet. */
+	fprintf(fp,
+					"  9\n$DIMALTTZ\n 70\n     0\n")
+		/* Controls suppression of zeros for alternate tolerance values: 0 = Suppresses zero feet and precisely zero inches; 1 = Includes zero feet and precisely zero inches; 2 = Includes zero feet and suppresses zero inches; 3 = Includes zero inches and suppresses zero feet. */
+		;
+	fprintf(fp, "  9\n$DIMFIT\n 70\n     3\n");	/* Placement of text and arrowheads; Possible values: 0 through 3. */
+	fprintf(fp, "  9\n$DIMUPT\n 70\n     0\n");	/* Cursor functionality for user positioned text:  0=controls only the dimension line location, 1=controls the text position as well as the dimension line location. */
+	fprintf(fp, "  9\n$DIMUNIT\n 70\n     2\n");	/* Units format for all dimension style family members except angular: 1 = Scientific; 2 = Decimal; 3 = Engineering; 4 = Architectural (stacked); 5 = Fractional (stacked); 6 = Architectural; 7 = Fractional. */
+	fprintf(fp, "  9\n$DIMDEC\n 70\n     4\n");	/* Number of decimal places for the tolerance values of a primary units dimension. */
+	fprintf(fp, "  9\n$DIMTDEC\n 70\n     4\n");	/* Number of decimal places to display the tolerance values. */
+	fprintf(fp, "  9\n$DIMALTU\n 70\n     2\n");	/*Units format for alternate units of all dimension style family members except angular: 1 = Scientific; 2 = Decimal; 3 = Engineering; 4 = Architectural (stacked); 5 = Fractional (stacked); 6 = Architectural; 7 = Fractional. */
+	fprintf(fp, "  9\n$DIMALTTD\n 70\n     2\n");	/*Number of decimal places for tolerance values of an alternate units dimension. */
+	fprintf(fp, "  9\n$DIMTXSTY\n  7\nSTANDARD\n");	/* Dimension text style. */
+	fprintf(fp, "  9\n$DIMAUNIT\n 70\n     0\n");	/* Angle format for angular dimensions: 0 = Decimal degrees, 1 = Degrees/minutes/seconds, 2 = Gradians, 3=Radians, 4 = Surveyor's units. */
+	fprintf(fp, "  9\n$LUNITS\n 70\n     2\n");	/* Units format for coordinates and distances. */
+	fprintf(fp, "  9\n$LUPREC\n 70\n     4\n");	/* Units precision for coordinates and distances. */
+	fprintf(fp, "  9\n$SKETCHINC\n 40\n1.0\n");	/* Sketch record increment. */
+	fprintf(fp, "  9\n$FILLETRAD\n 40\n1.0\n");	/* Fillet radius. */
+	fprintf(fp, "  9\n$AUNITS\n 70\n     0\n");	/* Units format for angles. */
+	fprintf(fp, "  9\n$AUPREC\n 70\n     0\n");	/* Units precision for angles. */
+	fprintf(fp, "  9\n$MENU\n  1\n.\n");	/* Name of menu file. */
+	fprintf(fp, "  9\n$ELEVATION\n 40\n0.0\n");	/* Current elevation set by ELEV command. */
+	fprintf(fp, "  9\n$PELEVATION\n 40\n0.0\n") /* Current paper space elevation. */ ;
+	fprintf(fp, "  9\n$THICKNESS\n 40\n0.0\n");	/* Current thickness set by ELEV command. */
+	fprintf(fp, "  9\n$LIMCHECK\n 70\n     0\n");	/* Nonzero if limits checking is on. */
+	fprintf(fp, "  9\n$BLIPMODE\n 70\n     0\n");	/* Blip mode on if nonzero. */
+	fprintf(fp, "  9\n$CHAMFERA\n 40\n10.0\n");	/* First chamfer distance. */
+	fprintf(fp, "  9\n$CHAMFERB\n 40\n10.0\n");	/* Second chamfer distance. */
+	fprintf(fp, "  9\n$CHAMFERC\n 40\n0.0\n");	/* Chamfer length. */
+	fprintf(fp, "  9\n$CHAMFERD\n 40\n0.0\n");	/* Chamfer angle. */
+	fprintf(fp, "  9\n$SKPOLY\n 70\n     0\n");	/* 0 = sketch lines, 1 = sketch polylines. */
+	fprintf(fp, "  9\n$TDCREATE\n 40\n0.0\n");	/* Date/time of drawing creation. */
+	fprintf(fp, "  9\n$TDUPDATE\n 40\n0.0\n");	/* Date/time of last drawing update. */
+	fprintf(fp, "  9\n$TDINDWG\n 40\n0.0\n");	/* Cumulative editing time for this drawing. */
+	fprintf(fp, "  9\n$TDUSRTIMER\n 40\n0.0\n");	/* User elapsed timer. */
+	fprintf(fp, "  9\n$USRTIMER\n 70\n     1\n");	/* 0 = timer off, 1 = timer on. */
+	fprintf(fp, "  9\n$ANGBASE\n 50\n0.0\n");	/* Angle 0 direction . */
+	fprintf(fp, "  9\n$ANGDIR\n 70\n     0\n");	/* 1 = clockwise angles, 0 = counterclockwise. */
+	fprintf(fp, "  9\n$PDMODE\n 70\n    98\n");	/* Point display mode. */
+	fprintf(fp, "  9\n$PDSIZE\n 40\n0.0\n");	/* Point display size. */
+	fprintf(fp, "  9\n$PLINEWID\n 40\n0.0\n");	/* Default polyline width. */
+	fprintf(fp, "  9\n$COORDS\n 70\n     2\n");	/* Coordinate display: 0 = static, 1 = continuous update, 2 = "d<a" format. */
+	fprintf(fp, "  9\n$SPLFRAME\n 70\n     0\n");	/* Spline control polygon display: 1 = on, 0 = off. */
+	fprintf(fp, "  9\n$SPLINETYPE\n 70\n     6\n");	/* Spline curve type for PEDIT Spline. */
+	fprintf(fp, "  9\n$SPLINESEGS\n 70\n     8\n");	/* Number of line segments per spline patch. */
+	fprintf(fp, "  9\n$ATTDIA\n 70\n     0\n");	/* Attribute entry dialogs: 1 = on, 0 = off. */
+	fprintf(fp, "  9\n$ATTREQ\n 70\n     1\n");	/* Attribute prompting during INSERT: 1 = on, 0 = off. */
+	fprintf(fp, "  9\n$HANDLING\n 70\n     1\n");	/* Next available handle. */
+	fprintf(fp, "  9\n$HANDSEED\n  5\n262\n");	/* Next available handle. */
+	fprintf(fp, "  9\n$SURFTAB1\n 70\n     6\n");	/* Number of mesh tabulations in first direction. */
+	fprintf(fp, "  9\n$SURFTAB2\n 70\n     6\n");	/* Number of mesh tabulations in second direction. */
+	fprintf(fp, "  9\n$SURFTYPE\n 70\n     6\n");	/* Surface type for PEDIT Smooth. */
+	fprintf(fp, "  9\n$SURFU\n 70\n     6\n");	/* Surface density (for PEDIT Smooth) in M direction. */
+	fprintf(fp, "  9\n$SURFV\n 70\n     6\n");	/* Surface density (for PEDIT Smooth) in N direction. */
+	fprintf(fp, "  9\n$UCSNAME\n  2\n\n");	/* Name of current UCS. */
+	fprintf(fp, "  9\n$UCSORG\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Origin of current UCS (in WCS). */
+	fprintf(fp, "  9\n$UCSXDIR\n 10\n1.0\n 20\n0.0\n 30\n0.0\n");	/* Direction of current UCS's X axis (in WCS). */
+	fprintf(fp, "  9\n$UCSYDIR\n 10\n0.0\n 20\n1.0\n 30\n0.0\n");	/* Direction of current UCS's Y axis (in WCS). */
+	fprintf(fp, "  9\n$PUCSNAME\n  2\n\n");	/* Current paper space UCS name. */
+	fprintf(fp, "  9\n$PUCSORG\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Current paper space UCS origin. */
+	fprintf(fp, "  9\n$PUCSXDIR\n 10\n1.0\n 20\n0.0\n 30\n0.0\n");	/* Current paper space UCS X axis. */
+	fprintf(fp, "  9\n$PUCSYDIR\n 10\n0.0\n 20\n1.0\n 30\n0.0\n");	/* Current paper space UCS Y axis. */
+	fprintf(fp, "  9\n$USERI1\n 70\n     0\n");	/* Five integer variables intended for use by third-party developers. */
+	fprintf(fp, "  9\n$USERI2\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERI3\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERI4\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERI5\n 70\n     0\n");
+	fprintf(fp, "  9\n$USERR1\n 40\n0.0\n");	/* Five real variables intended for use by third-party developers. */
+	fprintf(fp, "  9\n$USERR2\n 40\n0.0\n");
+	fprintf(fp, "  9\n$USERR3\n 40\n0.0\n");
+	fprintf(fp, "  9\n$USERR4\n 40\n0.0\n");
+	fprintf(fp, "  9\n$USERR5\n 40\n0.0\n");
+	fprintf(fp, "  9\n$WORLDVIEW\n 70\n     1\n");	/*1 = set UCS to WCS during DVIEW/VPOINT, 0 = don't change UCS. */
+	fprintf(fp, "  9\n$SHADEDGE\n 70\n     3\n");	/* 0 = faces shaded, edges not highlighted; 1 = faces shaded, edges highlighted in black; 2 = faces not filled, edges in entity color; 3 = faces in entity color, edges in black. */
+	fprintf(fp, "  9\n$SHADEDIF\n 70\n    70\n");	/* Percent ambient/diffuse light, range 1-100, default 70. */
+	fprintf(fp, "  9\n$TILEMODE\n 70\n     1\n");	/* 1 for previous release compatibility mode, 0 otherwise. */
+	fprintf(fp, "  9\n$MAXACTVP\n 70\n    48\n");	/* Sets maximum number of viewports to be regenerated. */
+	fprintf(fp, "  9\n$PINSBASE\n 10\n0.0\n 20\n0.0\n 30\n0.0\n");	/* Paper space insertion base point. */
+	fprintf(fp, "  9\n$PLIMCHECK\n 70\n     0\n");	/* Limits checking in paper space when nonzero. */
+	fprintf(fp, "  9\n$PEXTMIN\n 10\n1.000000E+20\n 20\n1.000000E+20\n 30\n1.000000E+20\n");	/* Minimum X, Y, and Z extents for paper space. */
+	fprintf(fp, "  9\n$PEXTMAX\n 10\n-1.000000E+20\n 20\n-1.000000E+20\n 30\n-1.000000E+20\n");	/* Maximum X, Y, and Z extents for paper space. */
+	fprintf(fp, "  9\n$PLIMMIN\n 10\n0.0\n 20\n0.0\n");	/* Minimum X and Y limits in paper space. */
+	fprintf(fp, "  9\n$PLIMMAX\n 10\n420.0\n 20\n297.0\n");	/* Maximum X and Y limits in paper space. */
+	fprintf(fp, "  9\n$UNITMODE\n 70\n     0\n");	/* Low bit set = display fractions, feet-and-inches, and surveyor's angles in input format. */
+	fprintf(fp, "  9\n$VISRETAIN\n 70\n     1\n");	/* 0 = don't retain xref-dependent visibility settings, 1 = retain; xref-dependent visibility settings. */
+	fprintf(fp, "  9\n$PLINEGEN\n 70\n     0\n");	/* Governs the generation of linetype patterns around the vertices of a 2D polyline: 1 = linetype is generated in a continuous pattern around vertices of the polyline; 0 = each segment of the polyline starts and ends with a dash. */
+	fprintf(fp, "  9\n$PSLTSCALE\n 70\n     1\n");	/* Controls paper space linetype scaling: 1 = no special linetype scaling; 0 = viewport scaling governs linetype scaling. */
+	fprintf(fp, "  9\n$TREEDEPTH\n 70\n  3020\n");	/* Specifies the maximum depth of the spatial index. */
+	fprintf(fp, "  9\n$PICKSTYLE\n 70\n     1\n");	/* Controls group selection and associative hatch selection: 0= No group selection or associative hatch selection; 1= Group selection; 2 = Associative hatch selection; 3 = Group selection and associative hatch selection. */
+	fprintf(fp, "  9\n$CMLSTYLE\n  2\nSTANDARD\n");	/* Current multiline style name. */
+	fprintf(fp, "  9\n$CMLJUST\n 70\n     0\n");	/* Current multiline justification: 0 = Top; 1 = Middle; 2 = Bottom. */
+	fprintf(fp, "  9\n$CMLSCALE\n 40\n1.0\n");	/* Current multiline scale. */
+	fprintf(fp, "  9\n$PROXYGRAPHICS\n 70\n     1\n");	/* Controls the saving of proxy object images. */
+	fprintf(fp, "  9\n$MEASUREMENT\n 70\n     0\n");	/* Sets drawing units. 0 = Imperial; 1 = Metric. */
+	fprintf(fp, "  0\nENDSEC\n");
+	/* write a CLASSES section */
+	fprintf(fp, "  0\nSECTION\n");
+	fprintf(fp, "  2\nCLASSES\n");	/* Section name. */
+	fprintf(fp, "  0\nENDSEC\n");
+	/* write a TABLES section */
+	fprintf(fp, "  0\nSECTION\n");
+	fprintf(fp, "  2\nTABLES\n");	/* Section name. */
+	/* write a VPORT (viewport) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nVPORT\n");	/* Table name. */
+	fprintf(fp, "  5\n23A\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     2\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nVPORT\n");
+	fprintf(fp, "  5\n261\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbViewportTableRecord\n");
+	fprintf(fp, "  2\n*ACTIVE\n");	/* Viewport name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 10\n0.0\n 20\n0.0\n");	/* Lower-left corner of viewport. */
+	fprintf(fp, " 11\n1.0\n 21\n1.0\n");	/* Upper-right corner of viewport. */
+	fprintf(fp, " 12\n43.998872\n 22\n17.506577\n");	/* View center point (in DCS). */
+	fprintf(fp, " 13\n0.0\n 23\n0.0\n");	/* Snap base point. */
+	fprintf(fp, " 14\n1.0\n 24\n1.0\n");	/* Snap spacing X and Y. */
+	fprintf(fp, " 15\n10.0\n 25\n10.0\n");	/* Grid spacing X and Y. */
+	fprintf(fp, " 16\n0.0\n 26\n0.0\n 36\n1.0\n");	/* View direction from target point (in WCS). */
+	fprintf(fp, " 17\n0.0\n 27\n0.0\n 37\n0.0\n");	/* View target point (in WCS). */
+	fprintf(fp, " 40\n47.164502\n");	/* View height. */
+	fprintf(fp, " 41\n1.882514\n");	/* Viewport aspect ratio. */
+	fprintf(fp, " 42\n50.0\n");		/* Lens length. */
+	fprintf(fp, " 43\n0.0\n");		/* Front clipping plane (offset from target point). */
+	fprintf(fp, " 44\n0.0\n");		/* Back clipping plane (offset from target point). */
+	fprintf(fp, " 50\n0.0\n");		/* Snap rotation angle. */
+	fprintf(fp, " 51\n0.0\n");		/* View twist angle. */
+	fprintf(fp, " 71\n     0\n");	/* View mode (see VIEWMODE system variable). */
+	fprintf(fp, " 72\n   100\n");	/* Circle zoom percent. */
+	fprintf(fp, " 73\n     1\n");	/* Fast zoom setting. */
+	fprintf(fp, " 74\n     3\n");	/* UCSICON setting. */
+	fprintf(fp, " 75\n     0\n");	/* Snap on/off. */
+	fprintf(fp, " 76\n     0\n");	/* Grid on/off. */
+	fprintf(fp, " 77\n     0\n");	/* Snap style. */
+	fprintf(fp, " 78\n     0\n");	/* Snap isopair. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write LTYPE (linetype) table entries */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nLTYPE\n");	/* Table name. */
+	fprintf(fp, "  5\n237\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     1\n");	/* Standard flag values. */
+	/* write a record entry for a BYBLOCK linetype */
+	fprintf(fp, "  0\nLTYPE\n");
+	fprintf(fp, "  5\n244\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLinetypeTableRecord\n");
+	fprintf(fp, "  2\nBYBLOCK\n");	/* Linetype name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\n\n");				/* Descriptive text for linetype. */
+	fprintf(fp, " 72\n    65\n");	/* Alignment code; value is always 65, the ASCII code for A. */
+	fprintf(fp, " 73\n     0\n");	/* The number of linetype elements. */
+	fprintf(fp, " 40\n0.0\n");		/* Total pattern length. */
+	/* write a record entry for a BYLAYER linetype */
+	fprintf(fp, "  0\nLTYPE\n");
+	fprintf(fp, "  5\n245\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLinetypeTableRecord\n");
+	fprintf(fp, "  2\nBYLAYER\n");	/* Linetype name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\n\n");				/* Descriptive text for linetype. */
+	fprintf(fp, " 72\n    65\n");	/* Alignment code; value is always 65, the ASCII code for A. */
+	fprintf(fp, " 73\n     0\n");	/* The number of linetype elements. */
+	fprintf(fp, " 40\n0.0\n");		/* Total pattern length. */
+	/* write a record entry for a CONTINUOUS linetype */
+	fprintf(fp, "  0\nLTYPE\n");
+	fprintf(fp, "  5\n246\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLinetypeTableRecord\n");
+	fprintf(fp, "  2\nCONTINUOUS\n");	/* Linetype name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\nSolid line\n");	/* Descriptive text for linetype. */
+	fprintf(fp, " 72\n    65\n");	/* Alignment code; value is always 65, the ASCII code for A. */
+	fprintf(fp, " 73\n     0\n");	/* The number of linetype elements. */
+	fprintf(fp, " 40\n0.0\n");		/* Total pattern length. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write LAYER table entries */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nLAYER\n");	/* Table name. */
+	fprintf(fp, "  5\n234\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     2\n");	/* Standard flag values. */
+	/* write a record entry for layer "0" */
+	fprintf(fp, "  0\nLAYER\n");
+	fprintf(fp, "  5\n240\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLayerTableRecord\n");
+	fprintf(fp, "  2\n0\n");			/* Layer name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 62\n     7\n");	/* Color number (if negative, layer is Off). */
+	fprintf(fp, "  6\nCONTINUOUS\n");	/* Linetype name. */
+	/* * write a record entry for a layer "ASHADE" */
+	fprintf(fp, "  0\nLAYER\n");
+	fprintf(fp, "  5\n251\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbLayerTableRecord\n");
+	fprintf(fp, "  2\nASHADE\n");	/* Layer name. */
+	fprintf(fp, " 70\n     4\n");	/* Standard flag values. */
+	fprintf(fp, " 62\n     7\n");	/* Color number (if negative, layer is Off). */
+	fprintf(fp, "  6\nCONTINUOUS\n");	/* Linetype name. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write STYLE table entries */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nSTYLE\n");	/* Table name. */
+	fprintf(fp, "  5\n235\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     2\n");	/* Standard flag values. */
+	/* write a record entry for a style "STANDARD" */
+	fprintf(fp, "  0\nSTYLE\n");
+	fprintf(fp, "  5\n241\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbTextStyleTableRecord\n");
+	fprintf(fp, "  2\nSTANDARD\n");	/* Style name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 40\n0.0\n");		/* Fixed text height; 0 if not fixed/ */
+	fprintf(fp, " 41\n1.0\n");		/* Width factor. */
+	fprintf(fp, " 50\n0.0\n");		/* Oblique angle. */
+	fprintf(fp, " 71\n     0\n");	/* Text generation flags; 2 = Text is backward (mirrored in X); 4 = Text is upside down (mirrored in Y). */
+	fprintf(fp, " 42\n2.5\n");		/* Last height used. */
+	fprintf(fp, "  3\ntxt\n");		/* Primary font file name. */
+	fprintf(fp, "  4\n\n");				/* Bigfont file name; blank if none. */
+	/* write a record entry for a style "ASHADE" */
+	fprintf(fp, "  0\nSTYLE\n");
+	fprintf(fp, "  5\n252\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbTextStyleTableRecord\n");
+	fprintf(fp, "  2\nASHADE\n");	/* Style name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, " 40\n0.2\n");		/* Fixed text height; 0 if not fixed/ */
+	fprintf(fp, " 41\n1.0\n");		/* Width factor. */
+	fprintf(fp, " 50\n0.0\n");		/* Oblique angle. */
+	fprintf(fp, " 71\n     0\n");	/* Text generation flags; 2 = Text is backward (mirrored in X); 4 = Text is upside down (mirrored in Y). */
+	fprintf(fp, " 42\n2.5\n");		/* Last height used. */
+	fprintf(fp, "  3\nsimplex.shx\n");	/* Primary font file name. */
+	fprintf(fp, "  4\n\n");				/* Bigfont file name; blank if none. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a VIEW table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nVIEW\n");		/* Table name. */
+	fprintf(fp, "  5\n238\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a UCS (User Coordinate System) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nUCS\n");		/* Table name. */
+	fprintf(fp, "  5\n239\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a APPID (APPlication ID) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nAPPID\n");	/* Table name. */
+	fprintf(fp, "  5\n23B\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     6\n");	/* Standard flag values. */
+	/* write a record entry for a appid "ACAD" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n242\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nACAD\n");		/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_RENDER" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n253\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_RENDER\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_ENTITY_MATERIAL" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n254\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_ENTITY_MATERIAL\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_FINISH" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n255\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_FINISH\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_MATERIAL" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n256\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_MATERIAL\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	/* write a record entry for a appid "AVE_GLOBAL" */
+	fprintf(fp, "  0\nAPPID\n");
+	fprintf(fp, "  5\n257\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbRegAppTableRecord\n");
+	fprintf(fp, "  2\nAVE_GLOBAL\n");	/* User-supplied (or application-supplied) application name (for extended data). These table entries maintain a set of names for all registered applications. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  0\nENDTAB\n");
+	/* write a DIMSTYLE (DIMensioning STYLE) table entry */
+	fprintf(fp, "  0\nTABLE\n");
+	fprintf(fp, "  2\nDIMSTYLE\n");	/* Table name. */
+	fprintf(fp, "  5\n23C\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTable\n");
+	fprintf(fp, " 70\n     1\n");	/* Standard flag values. */
+	/* write a record entry for a dimstyle "STANDARD" */
+	fprintf(fp, "  0\nDIMSTYLE\n");
+	fprintf(fp, "105\n258\n");		/* Handle. */
+	fprintf(fp, "100\nAcDbSymbolTableRecord\n");
+	fprintf(fp, "100\nAcDbDimStyleTableRecord\n");
+	fprintf(fp, "  2\nSTANDARD\n");	/* Dimension style name. */
+	fprintf(fp, " 70\n     0\n");	/* Standard flag values. */
+	fprintf(fp, "  3\n\n");				/* DIMPOST */
+	fprintf(fp, "  4\n\n");				/* DIMAPOST */
+	fprintf(fp, "  5\n\n");				/* DIMBLK */
+	fprintf(fp, "  6\n\n");				/* DIMBLK1 */
+	fprintf(fp, "  7\n\n");				/* DIMBLK2 */
+	fprintf(fp, " 40\n1.0\n");		/* DIMSCALE */
+	fprintf(fp, " 41\n0.18\n");		/* DIMASZ */
+	fprintf(fp, " 42\n0.0625\n");	/* DIMEXO */
+	fprintf(fp, " 43\n0.38\n");		/* DIMDLI */
+	fprintf(fp, " 44\n0.18\n");		/* DIMEXE */
+	fprintf(fp, " 45\n0.0\n");		/* DIMRND */
+	fprintf(fp, " 46\n0.0\n");		/* DIMDLE */
+	fprintf(fp, " 47\n0.0\n");		/* DIMTP */
+	fprintf(fp, " 48\n0.0\n");		/* DIMTM */
+	fprintf(fp, "140\n0.18\n");		/* DIMTXT */
+	fprintf(fp, "141\n0.09\n");		/* DIMCEN */
+	fprintf(fp, "142\n0.0\n");		/* DIMTSZ */
+	fprintf(fp, "143\n25.4\n");		/* DIMALTF */
+	fprintf(fp, "144\n1.0\n");		/* DIMLFAC */
+	fprintf(fp, "145\n0.0\n");		/* DIMTVP */
+	fprintf(fp, "146\n1.0\n");		/* DIMTFAC */
+	fprintf(fp, "147\n0.09\n");		/* DIMGAP */
+	fprintf(fp, " 71\n     0\n");	/* DIMTOL */
+	fprintf(fp, " 72\n     0\n");	/* DIMLIM */
+	fprintf(fp, " 73\n     1\n");	/* DIMTIH */
+	fprintf(fp, " 74\n     1\n");	/* DIMTOH */
+	fprintf(fp, " 75\n     0\n");	/* DIMSE1 */
+	fprintf(fp, " 76\n     0\n");	/* DIMSE2 */
+	fprintf(fp, " 77\n     0\n");	/* DIMTAD */
+	fprintf(fp, " 78\n     0\n");	/* DIMZIN */
+	fprintf(fp, "170\n     0\n");	/* DIMALT */
+	fprintf(fp, "171\n     2\n");	/* DIMALTD */
+	fprintf(fp, "172\n     0\n");	/* DIMTOFL */
+	fprintf(fp, "173\n     0\n");	/* DIMSAH */
+	fprintf(fp, "174\n     0\n");	/* DIMTIX */
+	fprintf(fp, "175\n     0\n");	/* DIMSOXD */
+	fprintf(fp, "176\n     0\n");	/* DIMDLRD */
+	fprintf(fp, "177\n     0\n");	/* DIMCLRE */
+	fprintf(fp, "178\n     0\n");	/* DIMCLRT */
+	fprintf(fp, "270\n     2\n");	/* DIMUNIT */
+	fprintf(fp, "271\n     4\n");	/* DIMDEC */
+	fprintf(fp, "272\n     4\n");	/* DIMTDEC */
+	fprintf(fp, "273\n     2\n");	/* DIMALTU */
+	fprintf(fp, "274\n     2\n");	/* DIMALTTD */
+	fprintf(fp, "340\n241\n");		/* Handle of referenced STYLE object (used instead of storing DIMTXSTY value). */
+	fprintf(fp, "275\n     0\n");	/* DIMAUNIT */
+	fprintf(fp, "280\n     0\n");	/* DIMJUST */
+	fprintf(fp, "281\n     0\n");	/* DIMSD1 */
+	fprintf(fp, "282\n     0\n");	/* DIMSD2 */
+	fprintf(fp, "283\n     1\n");	/* DIMTOLJ */
+	fprintf(fp, "284\n     0\n");	/* DIMTZIN */
+	fprintf(fp, "285\n     0\n");	/* DIMALTZ */
+	fprintf(fp, "286\n     0\n");	/* DIMALTTZ */
+	fprintf(fp, "287\n     3\n");	/* DIMFIT */
+	fprintf(fp, "288\n     0\n");	/* DIMUPT */
+	fprintf(fp, "  0\nENDTAB\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_header_metric_new () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a DXF header derived from a default
+ * header template file if available. If no default header template file is
+ * available, generate one depending on the dxf_metric variable.
+ *
+ * Write down a DXF header based on values derived from a default header
+ * template file (in pcb/src/hid/dxf/template directory).\n
+ * Included sections and tables are:\n
+ * <ul>
+ * <li>HEADER section
+ * <li>CLASSES section
+ * <li>TABLES section
+ *   <ul>
+ *   <li>VPORT table
+ *   <li>LTYPE table
+ *   <li>LAYER table
+ *   <li>STYLE table
+ *   <li>VIEW table
+ *   <li>UCS table
+ *   <li>APPID table
+ *   <li>DIMSTYLE table
+ *   </ul>
+ * </ul>
+ * Continue from here with writing a BLOCK_RECORD table and close the section
+ * with an ENDSEC marker.
+ */
+static void dxf_write_header()
+{
+	FILE *f_temp;
+	char *temp = NULL;
+	static char *dxf_header_filename;
+
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_header () function.\n", __FILE__, __LINE__);
+#endif
+	if (dxf_metric) {
+		dxf_header_filename = pcb_strdup("hid/dxf/template/metric_header.dxf");
+	}
+	else {
+		dxf_header_filename = pcb_strdup("hid/dxf/template/imperial_header.dxf");
+	}
+	/* check if template metric header file exists and open file
+	 * read-only  */
+	f_temp = fopen(dxf_header_filename, "r");
+	if (f_temp) {
+		/* do until EOF of the template file:
+		 * copy line by line from template file (f_temp) to
+		 * destination file (fp) */
+		while (!feof(f_temp)) {
+			fscanf(f_temp, "%s", temp);
+			fprintf(fp, "%s", temp);
+		}
+		/* when we're done close the template file */
+		fclose(f_temp);
+	}
+	else {
+		gui->log("Error in dxf_write_header_from_template (): cannot open file %s for reading.\n", dxf_header_filename);
+		if (dxf_metric) {
+			dxf_write_header_metric_new();
+		}
+		else {
+			dxf_write_header_imperial_new();
+		}
+	}
+	/* write a block record table */
+	dxf_write_table_block_record(fp);
+	/* write ENDSEC marker to close the header */
+	dxf_write_endsection(fp);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_header () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for an imperial DXF footer.
+ *
+ * Included sections are:\n
+ * <ul>
+ * <li>OBJECTS section
+ * <li>THUMBNAILIMAGE section
+ * </ul>
+ */
+static void dxf_write_footer_imperial_new()
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_footer_imperial_new () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, "0\nSECTION\n");
+	fprintf(fp, "  2\nOBJECTS\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\nC\n");
+	fprintf(fp, "330\n0\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nACAD_COLOR\n");
+	fprintf(fp, "350\n75\n");
+	fprintf(fp, "  3\nACAD_GROUP\n");
+	fprintf(fp, "350\nD\n");
+	fprintf(fp, "  3\nACAD_LAYOUT\n");
+	fprintf(fp, "350\n76\n");
+	fprintf(fp, "  3\nACAD_MATERIAL\n");
+	fprintf(fp, "350\n74\n");
+	fprintf(fp, "  3\nACAD_MLINESTYLE\n");
+	fprintf(fp, "350\n1B\n");
+	fprintf(fp, "3\nACAD_PLOTSETTINGS\n");
+	fprintf(fp, "350\n77\n");
+	fprintf(fp, "  3\nACAD_PLOTSTYLENAME\n");
+	fprintf(fp, "350\n72\n");
+	fprintf(fp, "  3\nAVE_ACITABLE\n");
+	fprintf(fp, "350\n51\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n75\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\nD\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n76\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nLayout1\n");
+	fprintf(fp, "350\n79\n");
+	fprintf(fp, "  3\nModel\n");
+	fprintf(fp, "350\n78\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n74\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n1B\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nSTANDARD\n");
+	fprintf(fp, "350\n1C\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n77\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nACDBDICTIONARYWDFLT\n");
+	fprintf(fp, "  5\n72\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nNormal\n");
+	fprintf(fp, "350\n73\n");
+	fprintf(fp, "100\nAcDbDictionaryWithDefault\n");
+	fprintf(fp, "340\n73\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n51\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nLAYOUT\n");
+	fprintf(fp, "  5\n79\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "100\nAcDbPlotSettings\n");
+	fprintf(fp, "  1\n\n");
+	fprintf(fp, "  2\n\\\\NO_SERVER\\NO_PRINTER\n");
+	fprintf(fp, "  4\n\n");
+	fprintf(fp, "  6\n\n");
+	fprintf(fp, " 40\n0.0\n");
+	fprintf(fp, " 41\n0.0\n");
+	fprintf(fp, " 42\n0.0\n");
+	fprintf(fp, " 43\n0.0\n");
+	fprintf(fp, " 44\n0.0\n");
+	fprintf(fp, " 45\n0.0\n");
+	fprintf(fp, " 46\n0.0\n");
+	fprintf(fp, " 47\n0.0\n");
+	fprintf(fp, " 48\n0.0\n");
+	fprintf(fp, " 49\n0.0\n");
+	fprintf(fp, "140\n0.0\n");
+	fprintf(fp, "141\n0.0\n");
+	fprintf(fp, "142\n1.0\n");
+	fprintf(fp, "143\n1.0\n");
+	fprintf(fp, " 70\n   688\n");
+	fprintf(fp, " 72\n     0\n");
+	fprintf(fp, " 73\n     0\n");
+	fprintf(fp, " 74\n     5\n");
+	fprintf(fp, "  7\n\n");
+	fprintf(fp, " 75\n    16\n");
+	fprintf(fp, "147\n1.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, " 77\n     2\n");
+	fprintf(fp, " 78\n   300\n");
+	fprintf(fp, "148\n0.0\n");
+	fprintf(fp, "149\n0.0\n");
+	fprintf(fp, "100\nAcDbLayout\n");
+	fprintf(fp, "  1\nLayout1\n");
+	fprintf(fp, " 70\n     1\n");
+	fprintf(fp, " 71\n     1\n");
+	fprintf(fp, " 10\n0.0\n");
+	fprintf(fp, " 20\n0.0\n");
+	fprintf(fp, " 11\n17.0\n");
+	fprintf(fp, " 21\n11.0\n");
+	fprintf(fp, " 12\n0.0\n");
+	fprintf(fp, " 22\n0.0\n");
+	fprintf(fp, " 32\n0.0\n");
+	fprintf(fp, " 14\n1.000000000000000E+20\n");
+	fprintf(fp, " 24\n1.000000000000000E+20\n");
+	fprintf(fp, " 34\n1.000000000000000E+20\n");
+	fprintf(fp, " 15\n-1.000000000000000E+20\n");
+	fprintf(fp, " 25\n-1.000000000000000E+20\n");
+	fprintf(fp, " 35\n-1.000000000000000E+20\n");
+	fprintf(fp, "146\n0.0\n");
+	fprintf(fp, " 13\n0.0\n");
+	fprintf(fp, " 23\n0.0\n");
+	fprintf(fp, " 33\n0.0\n");
+	fprintf(fp, " 16\n1.0\n");
+	fprintf(fp, " 26\n0.0\n");
+	fprintf(fp, " 36\n0.0\n");
+	fprintf(fp, " 17\n0.0\n");
+	fprintf(fp, " 27\n1.0\n");
+	fprintf(fp, " 37\n0.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, "330\n15\n");
+	fprintf(fp, "  0\nLAYOUT\n");
+	fprintf(fp, "  5\n78\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "100\nAcDbPlotSettings\n");
+	fprintf(fp, "  1\n\n");
+	fprintf(fp, "  2\n\\\\NO_SERVER\\NO_PRINTER\n");
+	fprintf(fp, "  4\n\n");
+	fprintf(fp, "  6\n\n");
+	fprintf(fp, " 40\n0.0\n");
+	fprintf(fp, " 41\n0.0\n");
+	fprintf(fp, " 42\n0.0\n");
+	fprintf(fp, " 43\n0.0\n");
+	fprintf(fp, " 44\n0.0\n");
+	fprintf(fp, " 45\n0.0\n");
+	fprintf(fp, " 46\n0.0\n");
+	fprintf(fp, " 47\n0.0\n");
+	fprintf(fp, " 48\n0.0\n");
+	fprintf(fp, " 49\n0.0\n");
+	fprintf(fp, "140\n0.0\n");
+	fprintf(fp, "141\n0.0\n");
+	fprintf(fp, "142\n1.0\n");
+	fprintf(fp, "143\n1.0\n");
+	fprintf(fp, " 70\n  1712\n");
+	fprintf(fp, " 72\n     0\n");
+	fprintf(fp, " 73\n     0\n");
+	fprintf(fp, " 74\n     0\n");
+	fprintf(fp, "  7\n\n");
+	fprintf(fp, " 75\n     0\n");
+	fprintf(fp, "147\n1.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, " 77\n     2\n");
+	fprintf(fp, " 78\n   300\n");
+	fprintf(fp, "148\n0.0\n");
+	fprintf(fp, "149\n0.0\n");
+	fprintf(fp, "100\nAcDbLayout\n");
+	fprintf(fp, "  1\nModel\n");
+	fprintf(fp, " 70\n     1\n");
+	fprintf(fp, " 71\n     0\n");
+	fprintf(fp, " 10\n0.0\n");
+	fprintf(fp, " 20\n0.0\n");
+	fprintf(fp, " 11\n17.0\n");
+	fprintf(fp, " 21\n11.0\n");
+	fprintf(fp, " 12\n0.0\n");
+	fprintf(fp, " 22\n0.0\n");
+	fprintf(fp, " 32\n0.0\n");
+	fprintf(fp, " 14\n0.0\n");
+	fprintf(fp, " 24\n0.0\n");
+	fprintf(fp, " 34\n0.0\n");
+	fprintf(fp, " 15\n0.0\n");
+	fprintf(fp, " 25\n0.0\n");
+	fprintf(fp, " 35\n0.0\n");
+	fprintf(fp, "146\n0.0\n");
+	fprintf(fp, " 13\n0.0\n");
+	fprintf(fp, " 23\n0.0\n");
+	fprintf(fp, " 33\n0.0\n");
+	fprintf(fp, " 16\n1.0\n");
+	fprintf(fp, " 26\n0.0\n");
+	fprintf(fp, " 36\n0.0\n");
+	fprintf(fp, " 17\n0.0\n");
+	fprintf(fp, " 27\n1.0\n");
+	fprintf(fp, " 37\n0.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, "330\n18\n");
+	fprintf(fp, "  0\nMLINESTYLE\n");
+	fprintf(fp, "  5\n1C\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n1B\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n1B\n");
+	fprintf(fp, "100\nAcDbMlineStyle\n");
+	fprintf(fp, "  2\nSTANDARD\n");
+	fprintf(fp, " 70\n     0\n");
+	fprintf(fp, "  3\n\n");
+	fprintf(fp, " 62\n   256\n");
+	fprintf(fp, " 51\n90.0\n");
+	fprintf(fp, " 52\n90.0\n");
+	fprintf(fp, " 71\n     2\n");
+	fprintf(fp, " 49\n0.5\n");
+	fprintf(fp, " 62\n   256\n");
+	fprintf(fp, "  6\nBYLAYER\n");
+	fprintf(fp, " 49\n-0.5\n");
+	fprintf(fp, " 62\n   256\n");
+	fprintf(fp, "  6\nBYLAYER\n");
+	fprintf(fp, "  0\nACDBPLACEHOLDER\n");
+	fprintf(fp, "  5\n73\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n72\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n72\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_footer_imperial_new () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a metric DXF footer.
+ *
+ * Included sections are:\n
+ * <ul>
+ * <li>OBJECTS section
+ * <li>THUMBNAILIMAGE section
+ * </ul>
+ */
+static void dxf_write_footer_metric_new()
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_footer_metric_new () function.\n", __FILE__, __LINE__);
+#endif
+	fprintf(fp, "0\nSECTION\n");
+	fprintf(fp, "  2\nOBJECTS\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\nC\n");
+	fprintf(fp, "330\n0\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nACAD_COLOR\n");
+	fprintf(fp, "350\n75\n");
+	fprintf(fp, "  3\nACAD_GROUP\n");
+	fprintf(fp, "350\nD\n");
+	fprintf(fp, "  3\nACAD_LAYOUT\n");
+	fprintf(fp, "350\n76\n");
+	fprintf(fp, "  3\nACAD_MATERIAL\n");
+	fprintf(fp, "350\n74\n");
+	fprintf(fp, "  3\nACAD_MLINESTYLE\n");
+	fprintf(fp, "350\n1B\n");
+	fprintf(fp, "3\nACAD_PLOTSETTINGS\n");
+	fprintf(fp, "350\n77\n");
+	fprintf(fp, "  3\nACAD_PLOTSTYLENAME\n");
+	fprintf(fp, "350\n72\n");
+	fprintf(fp, "  3\nAVE_ACITABLE\n");
+	fprintf(fp, "350\n51\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n75\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\nD\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n76\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nLayout1\n");
+	fprintf(fp, "350\n79\n");
+	fprintf(fp, "  3\nModel\n");
+	fprintf(fp, "350\n78\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n74\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n1B\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nSTANDARD\n");
+	fprintf(fp, "350\n1C\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n77\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nACDBDICTIONARYWDFLT\n");
+	fprintf(fp, "  5\n72\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  3\nNormal\n");
+	fprintf(fp, "350\n73\n");
+	fprintf(fp, "100\nAcDbDictionaryWithDefault\n");
+	fprintf(fp, "340\n73\n");
+	fprintf(fp, "  0\nDICTIONARY\n");
+	fprintf(fp, "  5\n51\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\nC\n");
+	fprintf(fp, "100\nAcDbDictionary\n");
+	fprintf(fp, "281\n     1\n");
+	fprintf(fp, "  0\nLAYOUT\n");
+	fprintf(fp, "  5\n79\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "100\nAcDbPlotSettings\n");
+	fprintf(fp, "  1\n\n");
+	fprintf(fp, "  2\n\\\\NO_SERVER\\NO_PRINTER\n");
+	fprintf(fp, "  4\n\n");
+	fprintf(fp, "  6\n\n");
+	fprintf(fp, " 40\n0.0\n");
+	fprintf(fp, " 41\n0.0\n");
+	fprintf(fp, " 42\n0.0\n");
+	fprintf(fp, " 43\n0.0\n");
+	fprintf(fp, " 44\n0.0\n");
+	fprintf(fp, " 45\n0.0\n");
+	fprintf(fp, " 46\n0.0\n");
+	fprintf(fp, " 47\n0.0\n");
+	fprintf(fp, " 48\n0.0\n");
+	fprintf(fp, " 49\n0.0\n");
+	fprintf(fp, "140\n0.0\n");
+	fprintf(fp, "141\n0.0\n");
+	fprintf(fp, "142\n1.0\n");
+	fprintf(fp, "143\n1.0\n");
+	fprintf(fp, " 70\n   688\n");
+	fprintf(fp, " 72\n     0\n");
+	fprintf(fp, " 73\n     0\n");
+	fprintf(fp, " 74\n     5\n");
+	fprintf(fp, "  7\n\n");
+	fprintf(fp, " 75\n    16\n");
+	fprintf(fp, "147\n1.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, " 77\n     2\n");
+	fprintf(fp, " 78\n   300\n");
+	fprintf(fp, "148\n0.0\n");
+	fprintf(fp, "149\n0.0\n");
+	fprintf(fp, "100\nAcDbLayout\n");
+	fprintf(fp, "  1\nLayout1\n");
+	fprintf(fp, " 70\n     1\n");
+	fprintf(fp, " 71\n     1\n");
+	fprintf(fp, " 10\n0.0\n");
+	fprintf(fp, " 20\n0.0\n");
+	fprintf(fp, " 11\n420.0\n");
+	fprintf(fp, " 21\n297.0\n");
+	fprintf(fp, " 12\n0.0\n");
+	fprintf(fp, " 22\n0.0\n");
+	fprintf(fp, " 32\n0.0\n");
+	fprintf(fp, " 14\n1.000000000000000E+20\n");
+	fprintf(fp, " 24\n1.000000000000000E+20\n");
+	fprintf(fp, " 34\n1.000000000000000E+20\n");
+	fprintf(fp, " 15\n-1.000000000000000E+20\n");
+	fprintf(fp, " 25\n-1.000000000000000E+20\n");
+	fprintf(fp, " 35\n-1.000000000000000E+20\n");
+	fprintf(fp, "146\n0.0\n");
+	fprintf(fp, " 13\n0.0\n");
+	fprintf(fp, " 23\n0.0\n");
+	fprintf(fp, " 33\n0.0\n");
+	fprintf(fp, " 16\n1.0\n");
+	fprintf(fp, " 26\n0.0\n");
+	fprintf(fp, " 36\n0.0\n");
+	fprintf(fp, " 17\n0.0\n");
+	fprintf(fp, " 27\n1.0\n");
+	fprintf(fp, " 37\n0.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, "330\n15\n");
+	fprintf(fp, "  0\nLAYOUT\n");
+	fprintf(fp, "  5\n78\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n76\n");
+	fprintf(fp, "100\nAcDbPlotSettings\n");
+	fprintf(fp, "  1\n\n");
+	fprintf(fp, "  2\n\\\\NO_SERVER\\NO_PRINTER\n");
+	fprintf(fp, "  4\n\n");
+	fprintf(fp, "  6\n\n");
+	fprintf(fp, " 40\n0.0\n");
+	fprintf(fp, " 41\n0.0\n");
+	fprintf(fp, " 42\n0.0\n");
+	fprintf(fp, " 43\n0.0\n");
+	fprintf(fp, " 44\n0.0\n");
+	fprintf(fp, " 45\n0.0\n");
+	fprintf(fp, " 46\n0.0\n");
+	fprintf(fp, " 47\n0.0\n");
+	fprintf(fp, " 48\n0.0\n");
+	fprintf(fp, " 49\n0.0\n");
+	fprintf(fp, "140\n0.0\n");
+	fprintf(fp, "141\n0.0\n");
+	fprintf(fp, "142\n1.0\n");
+	fprintf(fp, "143\n1.0\n");
+	fprintf(fp, " 70\n  1712\n");
+	fprintf(fp, " 72\n     0\n");
+	fprintf(fp, " 73\n     0\n");
+	fprintf(fp, " 74\n     0\n");
+	fprintf(fp, "  7\n\n");
+	fprintf(fp, " 75\n     0\n");
+	fprintf(fp, "147\n1.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, " 77\n     2\n");
+	fprintf(fp, " 78\n   300\n");
+	fprintf(fp, "148\n0.0\n");
+	fprintf(fp, "149\n0.0\n");
+	fprintf(fp, "100\nAcDbLayout\n");
+	fprintf(fp, "  1\nModel\n");
+	fprintf(fp, " 70\n     1\n");
+	fprintf(fp, " 71\n     0\n");
+	fprintf(fp, " 10\n0.0\n");
+	fprintf(fp, " 20\n0.0\n");
+	fprintf(fp, " 11\n12.0\n");
+	fprintf(fp, " 21\n9.0\n");
+	fprintf(fp, " 12\n0.0\n");
+	fprintf(fp, " 22\n0.0\n");
+	fprintf(fp, " 32\n0.0\n");
+	fprintf(fp, " 14\n0.0\n");
+	fprintf(fp, " 24\n0.0\n");
+	fprintf(fp, " 34\n0.0\n");
+	fprintf(fp, " 15\n0.0\n");
+	fprintf(fp, " 25\n0.0\n");
+	fprintf(fp, " 35\n0.0\n");
+	fprintf(fp, "146\n0.0\n");
+	fprintf(fp, " 13\n0.0\n");
+	fprintf(fp, " 23\n0.0\n");
+	fprintf(fp, " 33\n0.0\n");
+	fprintf(fp, " 16\n1.0\n");
+	fprintf(fp, " 26\n0.0\n");
+	fprintf(fp, " 36\n0.0\n");
+	fprintf(fp, " 17\n0.0\n");
+	fprintf(fp, " 27\n1.0\n");
+	fprintf(fp, " 37\n0.0\n");
+	fprintf(fp, " 76\n     0\n");
+	fprintf(fp, "330\n18\n");
+	fprintf(fp, "  0\nMLINESTYLE\n");
+	fprintf(fp, "  5\n1C\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n1B\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n1B\n");
+	fprintf(fp, "100\nAcDbMlineStyle\n");
+	fprintf(fp, "  2\nSTANDARD\n");
+	fprintf(fp, " 70\n     0\n");
+	fprintf(fp, "  3\n\n");
+	fprintf(fp, " 62\n   256\n");
+	fprintf(fp, " 51\n90.0\n");
+	fprintf(fp, " 52\n90.0\n");
+	fprintf(fp, " 71\n     2\n");
+	fprintf(fp, " 49\n0.5\n");
+	fprintf(fp, " 62\n   256\n");
+	fprintf(fp, "  6\nBYLAYER\n");
+	fprintf(fp, " 49\n-0.5\n");
+	fprintf(fp, " 62\n   256\n");
+	fprintf(fp, "  6\nBYLAYER\n");
+	fprintf(fp, "  0\nACDBPLACEHOLDER\n");
+	fprintf(fp, "  5\n73\n");
+	fprintf(fp, "102\n{ACAD_REACTORS\n");
+	fprintf(fp, "330\n72\n");
+	fprintf(fp, "102\n}\n");
+	fprintf(fp, "330\n72\n");
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_footer_metric_new () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a DXF footer.
+ *
+ * Depending on the metric/imperial units setting a footer will be
+ * appended to a DXF file.\n
+ * If a custom metric/imperial DXF footer file is available this will be
+ * used, if not aavailable a fall back functions will be called for
+ * appending a default metric/imperial footer.
+ */
+static void dxf_write_footer()
+{
+	FILE *f_temp;
+	char *temp = NULL;
+	static char *dxf_footer_filename;
+
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_footer () function.\n", __FILE__, __LINE__);
+#endif
+	if (dxf_metric) {
+		dxf_footer_filename = pcb_strdup("hid/dxf/template/metric_footer.dxf");
+	}
+	else {
+		dxf_footer_filename = pcb_strdup("hid/dxf/template/imperial_footer.dxf");
+	}
+	/* check if template metric footer file exists and open file
+	 * read-only  */
+	f_temp = fopen(dxf_footer_filename, "r");
+	if (f_temp) {
+		/* do until EOF of the template file:
+		 * copy line by line from template file (f_temp) to
+		 * destination file (fp) */
+		while (!feof(f_temp)) {
+			fscanf(f_temp, "%s", temp);
+			fprintf(fp, "%s", temp);
+		}
+		/* when we're done close the template file */
+		fclose(f_temp);
+	}
+	else {
+		gui->log("Error in dxf_write_footer (): cannot open file %s for reading.\n", dxf_footer_filename);
+		if (dxf_metric) {
+			dxf_write_footer_metric_new();
+		}
+		else {
+			dxf_write_footer_imperial_new();
+		}
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_footer () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for an insert entity.
+ */
+static void dxf_write_insert(FILE * fp,
+														 /*!< file pointer to output file (or device). */
+														 int id_code,
+														 /*!< group code = 5. */
+														 char *block_name,
+														 /*!< group code = 2. */
+														 char *linetype,
+														 /*!< group code = 6\n
+														  * optional, if omitted defaults to \c BYLAYER. */
+														 char *layer,
+														 /*!< group code = 8. */
+														 double x0,
+														 /*!< group code = 10\n
+														  * base point. */
+														 double y0,
+														 /*!< group code = 20\n
+														  * base point. */
+														 double z0,
+														 /*!< group code = 30\n
+														  * base point. */
+														 double thickness,
+														 /*!< group code = 39\n
+														  * optional, if omitted defaults to 0.0. */
+														 double rel_x_scale,
+														 /*!< group code = 41\n
+														  * optional, if omitted defaults to 1.0. */
+														 double rel_y_scale,
+														 /*!< group code = 42\n
+														  * optional, if omitted defaults to 1.0. */
+														 double rel_z_scale,
+														 /*!< group code = 43\n
+														  * optional, if omitted defaults to 1.0. */
+														 double column_spacing,
+														 /*!< group code = 44\n
+														  * optional, if omitted defaults to 0.0. */
+														 double row_spacing,
+														 /*!< group code = 45\n
+														  * optional, if omitted defaults to 0.0. */
+														 double rot_angle,
+														 /*!< group code = 50\n
+														  * optional, if omitted defaults to 0.0. */
+														 int color,
+														 /*!< group code = 62\n
+														  * optional, if omitted defaults to \c BYLAYER. */
+														 int attribute_follows,
+														 /*!< group code = 66\n
+														  * optional, if omitted defaults to 0. */
+														 int paperspace,
+														 /*!< group code = 67\n
+														  * optional, if omitted defaults to 0 (modelspace). */
+														 int columns,
+														 /*!< group code = 70\n
+														  * optional, if omitted defaults to 1. */
+														 int rows
+														 /*!< group code = 71\n
+														  * optional, if omitted defaults to 1. */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_insert () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with ID code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("INSERT");
+	if (strcmp(block_name, "") == 0) {
+		fprintf(stderr, "Warning: empty block name string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		fprintf(stderr, "   %s entity is discarded from output.\n", dxf_entity_name);
+		return;
+	}
+	if (strcmp(layer, "") == 0) {
+		fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		fprintf(stderr, "    %s entity is relocated to layer 0.\n", dxf_entity_name);
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+	}
+	if (rel_x_scale == 0.0) {
+		fprintf(stderr, "Warning: relative X-scale factor has a value of 0.0 for the %s entity with id-code: %x\n", dxf_entity_name,
+						id_code);
+		fprintf(stderr, "    default relative X-scale of 1.0 applied to %s entity.\n", dxf_entity_name);
+		rel_x_scale = 1.0;
+	}
+	if (rel_y_scale == 0.0) {
+		fprintf(stderr, "Warning: relative Y-scale factor has a value of 0.0 for the %s entity with id-code: %x\n", dxf_entity_name,
+						id_code);
+		fprintf(stderr, "    default relative Y-scale of 1.0 applied to %s entity.\n", dxf_entity_name);
+		rel_y_scale = 1.0;
+	}
+	if (rel_z_scale == 0.0) {
+		fprintf(stderr, "Warning: relative Z-scale factor has a value of 0.0 for the %s entity with id-code: %x\n", dxf_entity_name,
+						id_code);
+		fprintf(stderr, "    default relative Z-scale of 1.0 applied to %s entity.\n", dxf_entity_name);
+		rel_z_scale = 1.0;
+	}
+	if ((columns > 1) && (column_spacing == 0.0)) {
+		fprintf(stderr,
+						"Warning: number of columns is greater than 1 and the column spacing has a value of 0.0 for the %s entity with id-code: %x\n",
+						dxf_entity_name, id_code);
+		fprintf(stderr, "    default number of columns value of 1 applied to %s entity.\n", dxf_entity_name);
+		columns = 1;
+	}
+	if ((rows > 1) && (row_spacing == 0.0)) {
+		fprintf(stderr,
+						"Warning: number of rows is greater than 1 and the row spacing has a value of 0.0 for the %s entity with id-code: %x\n",
+						dxf_entity_name, id_code);
+		fprintf(stderr, "    default number of rows value of 1 applied to %s entity.\n", dxf_entity_name);
+		rows = 1;
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	fprintf(fp, "  2\n%s\n", block_name);
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	if (rel_x_scale != 1.0) {
+		fprintf(fp, " 41\n%f\n", rel_x_scale);
+	}
+	if (rel_y_scale != 1.0) {
+		fprintf(fp, " 42\n%f\n", rel_y_scale);
+	}
+	if (rel_z_scale != 1.0) {
+		fprintf(fp, " 43\n%f\n", rel_z_scale);
+	}
+	if ((columns > 1) && (column_spacing > 0.0)) {
+		fprintf(fp, " 44\n%f\n", column_spacing);
+	}
+	if ((rows > 1) && (row_spacing > 0.0)) {
+		fprintf(fp, " 45\n%f\n", row_spacing);
+	}
+	if (rot_angle != 0.0) {
+		fprintf(fp, " 50\n%f\n", rot_angle);
+	}
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	if (attribute_follows != 0) {
+		fprintf(fp, " 66\n%d\n", attribute_follows);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+	if (columns > 1) {
+		fprintf(fp, " 70\n%d\n", columns);
+	}
+	if (rows > 1) {
+		fprintf(fp, " 71\n%d\n", rows);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_insert () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a polyline entity.
+ *
+ * Following the Polyline header is a sequence of <c>Vertex</c> entities that
+ * specify the vertex coordinates and faces that compose the mesh.\n
+ * Vertices such as these are described in the following subsection on
+ * Vertex.\n
+ * \n
+ * Applications might want to represent polygons with an arbitrarily large
+ * number of sides in polyface meshes.\n
+ * However, the AutoCAD entity structure imposes a limit on the number of
+ * vertices that a given face entity can specify.\n
+ * You can represent more complex polygons by decomposing them into triangular
+ * wedges.\n
+ */
+static void dxf_write_polyline(FILE * fp,
+															 /*!< file pointer to output device  */
+															 int id_code,
+															 /*!< group code = 5  */
+															 char *linetype,
+															 /*!< group code = 6 \n optional, if omitted defaults to BYLAYER  */
+															 char *layer,
+															 /*!< group code = 8  */
+															 double x0,
+															 /*!< group code = 10 \n if omitted defaults to 0.0  */
+															 double y0,
+															 /*!< group code = 20 \n if omitted defaults to 0.0  */
+															 double z0,
+															 /*!< group code = 30 \n default elevation for vertices  */
+															 double extr_x0,
+															 /*!< group code = 210 \n extrusion direction \n optional, if ommited defaults to 0.0  */
+															 double extr_y0,
+															 /*!< group code = 220 \n extrusion direction \n optional, if ommited defaults to 0.0  */
+															 double extr_z0,
+															 /*!< group code = 230 \n extrusion direction \n optional, if ommited defaults to 1.0  */
+															 double thickness,
+															 /*!< group code = 39 \n optional, if omitted defaults to 0.0  */
+															 double start_width,
+															 /*!< group code = 40 \n optional, if omitted defaults to 0.0  */
+															 double end_width,
+															 /*!< group code = 41 \n optional, if omitted defaults to 0.0  */
+															 int color,
+															 /*!< group code = 62 \n optional, if omitted defaults to BYLAYER  */
+															 int vertices_follow,
+															 /*!< group code = 66 \n mandatory, always 1 (one or more vertices make up a polyline)  */
+															 int paperspace,
+															 /*!< group code = 67 \n optional, if omitted defaults to 0 (modelspace)  */
+															 int flag,
+															 /*!< group code = 70 \n optional, if omitted defaults to 0  */
+															 /*!< 1 = This is a closed Polyline (or a polygon mesh closed in the M direction) \n  */
+															 /*!< 2 = Curve-fit vertices have been added \n  */
+															 /*!< 4 = Spline-fit vertices have been added \n  */
+															 /*!< 8 = This is a 3D Polyline \n  */
+															 /*!< 16 = This is a 3D polygon mesh. \n  */
+															 /*!< 32 = The polygon mesh is closed in the N direction \n  */
+															 /*!< 64 = This Polyline is a polyface mesh \n  */
+															 /*!< 128 = The linetype pattern is generated continuously around the vertices of this Polyline  */
+															 int polygon_mesh_M_vertex_count,
+															 /*!< group code = 71 \n optional, if omitted defaults to 0  */
+															 int polygon_mesh_N_vertex_count,
+															 /*!< group code = 72 \n optional, if omitted defaults to 0  */
+															 int smooth_M_surface_density,
+															 /*!< group code = 73 \n optional, if omitted defaults to 0  */
+															 int smooth_N_surface_density,
+															 /*!< group code = 74 \n optional, if omitted defaults to 0  */
+															 int surface_type
+															 /*!< group code = 75 \n optional, if omitted defaults to 0 \n integer coded, not bit-coded: \n  */
+															 /*!< 0 = no smooth surface fitted \n  */
+															 /*!< 5 = quadratic B-spline surface \n  */
+															 /*!< 6 = cubic B-spline surface \n  */
+															 /*!< 8 = Bezier surface \n  */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_polyline () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("POLYLINE");
+	if (x0 != 0.0) {
+		fprintf(stderr, "Warning: start point has an invalid X-value for the %s entity with id-code: %x\n", dxf_entity_name,
+						id_code);
+		fprintf(stderr, "   %s entity is discarded from output.\n", dxf_entity_name);
+		return;
+	}
+	if (y0 != 0.0) {
+		fprintf(stderr, "Warning: start point has an invalid Y-value for the %s entity with id-code: %x\n", dxf_entity_name,
+						id_code);
+		fprintf(stderr, "   %s entity is discarded from output.\n", dxf_entity_name);
+		return;
+	}
+	if (vertices_follow != 1) {
+		fprintf(stderr, "Warning: vertices follow flag has an invalid value for the %s entity with id-code: %x\n", dxf_entity_name,
+						id_code);
+		fprintf(stderr, "   %s entity is discarded from output.\n", dxf_entity_name);
+		return;
+	}
+	if (strcmp(layer, "") == 0) {
+		fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		fprintf(stderr, "   %s entity is relocated to layer 0\n", dxf_entity_name);
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	fprintf(fp, "100\nAcDb3dPolyline\n");
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	fprintf(fp, "210\n%f\n", extr_x0);
+	fprintf(fp, "220\n%f\n", extr_y0);
+	fprintf(fp, "230\n%f\n", extr_z0);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	if (start_width != 0.0) {
+		fprintf(fp, " 40\n%f\n", start_width);
+	}
+	if (end_width != 0.0) {
+		fprintf(fp, " 41\n%f\n", end_width);
+	}
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	fprintf(fp, " 66\n%d\n", vertices_follow);
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+	fprintf(fp, " 70\n%d\n", flag);
+	fprintf(fp, " 71\n%d\n", polygon_mesh_M_vertex_count);
+	fprintf(fp, " 72\n%d\n", polygon_mesh_N_vertex_count);
+	fprintf(fp, " 73\n%d\n", smooth_M_surface_density);
+	fprintf(fp, " 74\n%d\n", smooth_N_surface_density);
+	fprintf(fp, " 75\n%d\n", surface_type);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_polyline () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a section marker.
+ */
+static void dxf_write_section(FILE * fp,
+															/*!< file pointer to output device  */
+															char *section_name
+															/*!< section name  */
+	) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_section () function.\n", __FILE__, __LINE__);
+#endif
+	/* no use in writing an empty string to file */
+	if (strcmp(section_name, "") == 0) {
+		return;
+	}
+	fprintf(fp, "  0\nSECTION\n  2\n%s\n", section_name);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_section () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a solid entity.
+ */
+static void dxf_write_solid(FILE * fp,
+														/*!< file pointer to output device  */
+														int id_code,
+														/*!< group code = 5  */
+														char *linetype,
+														/*!< group code = 6 \n optional, defaults to BYLAYER  */
+														char *layer,
+														/*!< group code = 8  */
+														double x0,
+														/*!< group code = 10 \n base point X-value, bottom left  */
+														double y0,
+														/*!< group code = 20 \n base point Y-value, bottom left  */
+														double z0,
+														/*!< group code = 30 \n base point Z-value, bottom left  */
+														double x1,
+														/*!< group code = 11 \n alignment point X-vaule, bottom right  */
+														double y1,
+														/*!< group code = 21 \n alignment point Y-vaule, bottom right  */
+														double z1,
+														/*!< group code = 31 \n alignment point Z-vaule, bottom right  */
+														double x2,
+														/*!< group code = 12 \n alignment point X-value, top left  */
+														double y2,
+														/*!< group code = 22 \n alignment point Y-value, top left  */
+														double z2,
+														/*!< group code = 32 \n alignment point Z-value, top left  */
+														double x3,
+														/*!< group code = 13 \n alignment point X-value, top right  */
+														double y3,
+														/*!< group code = 23 \n alignment point Y-value, top right  */
+														double z3,
+														/*!< group code = 33 \n alignment point Z-value, top right  */
+														double thickness,
+														/*!< group code = 39 \n optional, defaults to 0.0  */
+														int color,
+														/*!< group code = 62 \n optional, defaults to BYLAYER  */
+														int paperspace
+														/*!< group code = 67 \n optional, defaults to 0 (modelspace)  */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_solid () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("SOLID");
+	if (strcmp(layer, "") == 0) {
+		fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		fprintf(stderr, "    %s entity is relocated to layer 0", dxf_entity_name);
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+	}
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	fprintf(fp, " 11\n%f\n", x1);
+	fprintf(fp, " 21\n%f\n", y1);
+	fprintf(fp, " 31\n%f\n", z1);
+	fprintf(fp, " 12\n%f\n", x2);
+	fprintf(fp, " 22\n%f\n", y2);
+	fprintf(fp, " 32\n%f\n", z2);
+	fprintf(fp, " 13\n%f\n", x3);
+	fprintf(fp, " 23\n%f\n", y3);
+	fprintf(fp, " 33\n%f\n", z3);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_solid () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Write DXF output to a file for a polyline vertex entity.
+ */
+static void dxf_write_vertex(FILE * fp,
+														 /*!< file pointer to output device */
+														 int id_code,
+														 /*!< group code = 5 */
+														 char *linetype,
+														 /*!< group code = 6 \n
+														  * optional, if omitted defaults to BYLAYER */
+														 char *layer,
+														 /*!< group code = 8 */
+														 double x0,
+														 /*!< group code = 10 \n */
+														 double y0,
+														 /*!< group code = 20 \n */
+														 double z0,
+														 /*!< group code = 30 \n */
+														 double thickness,
+														 /*!< group code = 39 \n
+														  * optional, if omitted defaults to 0.0 */
+														 double start_width,
+														 /*!< group code = 40 \n
+														  * optional, if omitted defaults to 0.0 */
+														 double end_width,
+														 /*!< group code = 41 \n
+														  * optional, if omitted defaults to 0.0 */
+														 double bulge,
+														 /*!< group code = 42 \n
+														  * optional, if omitted defaults to 0.0 \n
+														  * The bulge is the tangent of 1/4 of the included angle
+														  * for an arc segment. \n
+														  * Made negative if the arc goes clockwise from the start
+														  * point to the endpoint. \n
+														  * A bulge of 0 indicates a straight segment, and a bulge
+														  * of 1 is a semicircle. \n */
+														 double curve_fit_tangent_direction,
+														 /*!< group code = 50 \n optional, a curve-fit tangent direction of 0.0 may be omitted from the DXF output, but is significant if the flag bit is set. \n */
+														 int color,
+														 /*!< group code = 62 \n optional, if omitted defaults to BYLAYER */
+														 int paperspace,
+														 /*!< group code = 67 \n optional, if omitted defaults to 0 (modelspace) */
+														 int flag
+														 /*!< group code = 70 \n optional, if omitted defaults to 0
+														  *  bit coded: \n
+														  * 1 = extra vertex created by curve-fitting \n
+														  * 2 = curve-fit tangent defined for this vertex. \n
+														  * 4 = unused (never set in DXF files) \n
+														  * 8 = spline vertex created by spline-fitting \n
+														  * 16 = spline frame control point \n
+														  * 32 = 3D Polyline vertex \n
+														  * 64 = 3D polygon mesh vertex \n
+														  * 128 = polyface mesh vertex */
+	) {
+	char *dxf_entity_name;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_write_vertex () function.\n", __FILE__, __LINE__);
+	fprintf(stderr, "[DXF entity with code %x]\n", id_code);
+#endif
+	dxf_entity_name = pcb_strdup("VERTEX");
+	if (strcmp(layer, "") == 0) {
+		fprintf(stderr, "Warning: empty layer string for the %s entity with id-code: %x\n", dxf_entity_name, id_code);
+		fprintf(stderr, "    %s entity is relocated to layer 0", dxf_entity_name);
+		layer = pcb_strdup(DXF_DEFAULT_LAYER);
+	}
+
+	fprintf(fp, "  0\n%s\n", dxf_entity_name);
+	if (id_code != -1) {
+		fprintf(fp, "  5\n%x\n", id_code);
+	}
+	if (strcmp(linetype, DXF_DEFAULT_LINETYPE) != 0) {
+		fprintf(fp, "  6\n%s\n", linetype);
+	}
+	fprintf(fp, "  8\n%s\n", layer);
+	fprintf(fp, " 10\n%f\n", x0);
+	fprintf(fp, " 20\n%f\n", y0);
+	fprintf(fp, " 30\n%f\n", z0);
+	if (thickness != 0.0) {
+		fprintf(fp, " 39\n%f\n", thickness);
+	}
+	if (start_width != 0.0) {
+		fprintf(fp, " 40\n%f\n", start_width);
+	}
+	if (end_width != 0.0) {
+		fprintf(fp, " 41\n%f\n", end_width);
+	}
+	if (bulge != 0.0) {
+		fprintf(fp, " 42\n%f\n", bulge);
+	}
+	if (curve_fit_tangent_direction != 0.0) {
+		fprintf(fp, " 50\n%f\n", curve_fit_tangent_direction);
+	}
+	if (color != DXF_COLOR_BYLAYER) {
+		fprintf(fp, " 62\n%d\n", color);
+	}
+	if (paperspace == DXF_PAPERSPACE) {
+		fprintf(fp, " 67\n%d\n", DXF_PAPERSPACE);
+	}
+	fprintf(fp, " 70\n%d\n", flag);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_write_vertex () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Get export options such as filename and filename base.
+ *
+ * Returns a set of resources describing options the export or print HID
+ * supports.\n
+ * In GUI mode, the print/export dialogs use this to set up the selectable
+ * options.\n
+ * In command line mode, these are used to interpret command line options.\n
+ * If n_ret is non-NULL, the number of attributes is stored there.
+ */
+static HID_Attribute *dxf_get_export_options(int *n)
+{
+	static char *last_dxf_filename;
+	static char *last_dxf_xref_filename;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_get_export_options () function.\n", __FILE__, __LINE__);
+#endif
+	last_dxf_filename = 0;
+	last_dxf_xref_filename = 0;
+	if (PCB) {
+		derive_default_filename(PCB->Filename, &dxf_options[HA_dxffile], "", &last_dxf_filename);
+		derive_default_filename(PCB->Filename, &dxf_options[HA_xreffile], "", &last_dxf_xref_filename);
+	}
+	if (n) {
+		*n = NUM_OPTIONS;
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_get_export_options () function.\n", __FILE__, __LINE__);
+#endif
+	return dxf_options;
+}
+
+
+/*!
+ * \brief Insert an element in the list of elements.
+ */
+static DxfList *dxf_insert(char *refdes,
+													 /*!< reference designator. */
+													 char *descr,
+													 /*!< description or footprint. */
+													 char *value,
+													 /*!< element value. */
+													 DxfList * dxf
+													 /*!< next item in list. */
+	) {
+	DxfList *new;
+	DxfList *cur;
+	DxfList *prev;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_insert () function.\n", __FILE__, __LINE__);
+#endif
+	prev = NULL;
+	if (dxf == NULL) {
+		/*
+		 * this is the first element so automatically create an entry.
+		 */
+		if ((new = (DxfList *) malloc(sizeof(DxfList))) == NULL) {
+			fprintf(stderr, "Error in dxf.c|dxf_insert (): malloc() failed.\n");
+			exit(1);
+		}
+		new->next = NULL;
+		new->descr = pcb_strdup(descr);
+		new->value = pcb_strdup(value);
+		new->num = 1;
+		new->refdes = dxf_string_insert(refdes, NULL);
+		return (new);
+	}
+	/*
+	 * search and see if we already have used one of these components.
+	 */
+	cur = dxf;
+	while (cur != NULL) {
+		if ((NSTRCMP(descr, cur->descr) == 0) && (NSTRCMP(value, cur->value) == 0)) {
+			cur->num++;
+			cur->refdes = dxf_string_insert(refdes, cur->refdes);
+			break;
+		}
+		prev = cur;
+		cur = cur->next;
+	}
+	if (cur == NULL) {
+		if ((new = (DxfList *) malloc(sizeof(DxfList))) == NULL) {
+			fprintf(stderr, "Error in dxf.c|dxf_insert (): malloc() failed.\n");
+			exit(1);
+		}
+		prev->next = new;
+		new->next = NULL;
+		new->descr = pcb_strdup(descr);
+		new->value = pcb_strdup(value);
+		new->num = 1;
+		new->refdes = dxf_string_insert(refdes, NULL);
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_insert () function.\n", __FILE__, __LINE__);
+#endif
+	return (dxf);
+}
+
+
+/*!
+ * \brief Print Xrefs to DXF file.
+ *
+ * Generate a file in the AutoCAD R14 DXF format for insertion of 3D models as
+ * external references (Xref's).\n
+ * An external reference is a reference to an external drawing block which is
+ * loaded at runtime (of the mechanical CAD software; for example AutoCAD)
+ * during the loading of the toplevel drawing model (the dxf file) in a
+ * mechanical CAD program or during separate insertions after the initial
+ * loading whilst in drawing mode.\n
+ * Note that for most mechanical CAD software the inserted block cannot be a
+ * DXF file.\n
+ * In most cases a DXF file representing a (3D) model must first be converted
+ * to a ".dwg" file, or any other file format native to the mechanical CAD
+ * software used.\n
+ * The filename of the 3D model inserted in the dxf file is:\n
+ * "parts\" + "Description or footprint name" + ".dwg" (file extension).\n
+ * It is assumed that the 3D models (.dwg) of the parts reside in a
+ * "parts" directory.\n
+ * All spaces in the "Description or footprint name" are palced with an
+ * underscore "_" resulting in the following string:
+ * "Description_or_footprint_name".\n
+ * Any element without a valid description or footprint name is inserted in the
+ * dxf file with a 3D model with a text "(unknown)" and this has to be
+ * manually inserted in the toplevel model after the initial loading of the dxf
+ * file.\n
+ * This is to prevent unnoticed ommissions of parts in the toplevel 3D model.
+ */
+static int dxf_export_xref_file(void)
+{
+	char *name;
+	char utcTime[64];
+	double x;
+	double y;
+	double theta;
+	double sumx;
+	double sumy;
+	double pin1x;
+	double pin1y;
+	double pin1angle;
+	double pin2x;
+	double pin2y;
+	double pin2angle;
+	int found_pin1;
+	int found_pin2;
+	int pin_cnt;
+	time_t currenttime;
+	DxfList *dxf;
+	DxfList *lastb;
+	char *dxf_block_name;
+	char *dxf_xref_name;
+	double dxf_x0;
+	double dxf_y0;
+	double dxf_rot_angle;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_export_xref_file () function.\n", __FILE__, __LINE__);
+#endif
+	name = NULL;
+	theta = 0.0;
+	pin1x = 0.0;
+	pin1y = 0.0;
+	pin1angle = 0.0;
+	pin2x = 0.0;
+	pin2y = 0.0;
+	dxf = NULL;
+	dxf_block_name = NULL;
+	dxf_xref_name = NULL;
+	dxf_x0 = 0.0;
+	dxf_y0 = 0.0;
+	dxf_rot_angle = 0.0;
+
+	fp = fopen(dxf_xref_filename, "w");
+	if (!fp) {
+		gui->log("Error in dxf.c|dxf_export_xref_file (): cannot open file %s for writing.\n", dxf_xref_filename);
+		return 1;
+	}
+	/*
+	 * create a portable timestamp.
+	 */
+	currenttime = time(NULL);
+	strftime(utcTime, sizeof(utcTime), "%c UTC", gmtime(&currenttime));
+	if (dxf_verbose) {
+		/* report at the beginning of each file */
+		fprintf(stderr, "DXF: Board Name: %s, %s \n", UNKNOWN(PCB->Name), UNKNOWN(name));
+		fprintf(stderr, "DXF: Created by: %s.\n", PCB_DXF_HID_VERSION);
+		fprintf(stderr, "DXF: Creation date: %s \n", utcTime);
+		fprintf(stderr, "DXF: File Format according to: AutoCAD R14.\n");
+		if (dxf_metric) {
+			fprintf(stderr, "DXF using Metric coordinates [mm].\n");
+			pcb_fprintf(stderr, "PCB Dimensions: %.0mm x %.0mm.\n", PCB->MaxWidth, PCB->MaxHeight);
+		}
+		else {
+			fprintf(stderr, "DXF using Imperial coordinates [mil].\n");
+			pcb_fprintf(stderr, "PCB Dimensions: %.0mil x %.0mil.\n", PCB->MaxWidth, PCB->MaxHeight);
+		}
+		fprintf(stderr, "PCB Coordinate Origin: lower left.\n");
+		fprintf(stderr, "DXF: Now processing Xrefs file.\n");
+	}
+	/* write version info as a dxf comment */
+	dxf_write_comment(fp, PCB_DXF_HID_VERSION);
+	/* write dxf header information */
+	dxf_write_header(fp);
+	dxf_write_section(fp, "BLOCKS");
+	/*
+	 * lookup all elements on pcb and insert element in the list of elements.
+	 */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/*
+		 * insert the elements into the dxf list.
+		 */
+		dxf = dxf_insert(UNKNOWN(NAMEONPCB_NAME(element)), UNKNOWN(DESCRIPTION_NAME(element)), UNKNOWN(VALUE_NAME(element)), dxf);
+	}
+	END_LOOP;											/* End of ELEMENT_LOOP  */
+	/*
+	 * now write a single block definition for every unique element to
+	 * the BLOCKS section of the DXF file.
+	 * since these are all supposed to be Xref blocks they are not to
+	 * contain entities, just the path and filename (including extension).
+	 * write a section BLOCKS marker to the DXF file.
+	 */
+	while (dxf != NULL) {
+		dxf_block_name = pcb_strdup(dxf_clean_string(dxf->descr));
+		dxf_xref_name = DXF_DEFAULT_XREF_PATH_NAME;
+		dxf_write_block(fp, dxf_id_code, dxf_xref_name, dxf_block_name, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										DXF_DEFAULT_LAYER,	/* layer, */
+										0.0,				/* dxf_x0, */
+										0.0,				/* dxf_y0, */
+										0.0,				/* dxf_z0, */
+										0.0,				/* dxf_thickness, */
+										DXF_COLOR_BYLAYER,	/* dxf_color, */
+										0,					/* dxf_paperspace, */
+										36					/* dxf_block_type */
+			);
+		dxf_id_code++;
+		lastb = dxf;
+		dxf = dxf->next;
+		free(lastb);
+	}
+	/* write an ENDSEC marker to the DXF file */
+	dxf_write_endsection(fp);
+	/*
+	 * write a section ENTITIES marker to the DXF file.
+	 */
+	dxf_write_section(fp, "ENTITIES");
+	/*
+	 * for each element we calculate the centroid of the footprint.
+	 * in addition, we need to extract some notion of rotation.
+	 */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/*
+		 * initialize our pin count and our totals for finding the
+		 * centroid.
+		 */
+		pin_cnt = 0;
+		sumx = 0.0;
+		sumy = 0.0;
+		found_pin1 = 0;
+		found_pin2 = 0;
+		/*
+		 * iterate over the pins and pads keeping a running count of
+		 * many pins/pads total and the sum of x and y coordinates.
+		 * While we're at it, store the location of pin/pad #1 and #2
+		 * if we can find them.
+		 */
+		PIN_LOOP(element);
+		{
+			sumx += (double) pin->X;
+			sumy += (double) pin->Y;
+			pin_cnt++;
+			if (NSTRCMP(pin->Number, "1") == 0) {
+				pin1x = (double) pin->X;
+				pin1y = (double) pin->Y;
+				pin1angle = 0.0;
+				found_pin1 = 1;
+			}
+			else if (NSTRCMP(pin->Number, "2") == 0) {
+				pin2x = (double) pin->X;
+				pin2y = (double) pin->Y;
+				pin2angle = 0.0;
+				found_pin2 = 1;
+			}
+		}
+		END_LOOP;										/* End of PIN_LOOP  */
+		PAD_LOOP(element);
+		{
+			sumx += (pad->Point1.X + pad->Point2.X) / 2.0;
+			sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0;
+			pin_cnt++;
+			if (NSTRCMP(pad->Number, "1") == 0) {
+				pin1x = (double) (pad->Point1.X + pad->Point2.X) / 2.0;
+				pin1y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0;
+				/*
+				 * NOTE: we swap the Y points, because in PCB
+				 * the Y-axis is inverted, and increasing Y
+				 * moves down.
+				 * we want to deal with a right-handed
+				 * Cartesian Coordinate System where
+				 * increasing Y moves up.
+				 */
+				pin1angle = (180.0 / M_PI) * atan2(pad->Point1.Y - pad->Point2.Y, pad->Point2.X - pad->Point1.X);
+				found_pin1 = 1;
+			}
+			else if (NSTRCMP(pad->Number, "2") == 0) {
+				pin2x = (double) (pad->Point1.X + pad->Point2.X) / 2.0;
+				pin2y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0;
+				pin2angle = (180.0 / M_PI) * atan2(pad->Point1.Y - pad->Point2.Y, pad->Point2.X - pad->Point1.X);
+				found_pin2 = 1;
+			}
+		}
+		END_LOOP;										/* End of PAD_LOOP  */
+		if (pin_cnt > 0) {
+			x = sumx / (double) pin_cnt;
+			y = sumy / (double) pin_cnt;
+			if (found_pin1) {
+				/*
+				 * recenter pin #1 onto the axis which cross
+				 * at the part centroid.
+				 */
+				pin1x -= x;
+				pin1y -= y;
+				pin1y = -1.0 * pin1y;
+				/* if only 1 pin, use pin 1's angle */
+				if (pin_cnt == 1)
+					theta = pin1angle;
+				else {
+					/*
+					 * if pin #1 is at (0,0) use pin #2 for
+					 * rotation
+					 */
+					if ((pin1x == 0.0) && (pin1y == 0.0)) {
+						if (found_pin2)
+							theta = dxf_xy_to_angle(pin2x, pin2y);
+						else {
+							Message(PCB_MSG_WARNING, "dxf.c|dxf_export_xref_file ():\n"
+											"     unable to figure out angle of element\n"
+											"     %s because pin #1 is at the centroid of the part\n"
+											"     and I could not find pin #2's location.\n"
+											"     Setting to %g degrees.\n", UNKNOWN(NAMEONPCB_NAME(element)), theta);
+						}
+					}
+					else
+						theta = dxf_xy_to_angle(pin1x, pin1y);
+				}
+			}
+			/* we did not find pin #1 */
+			else {
+				theta = 0.0;
+				Message(PCB_MSG_WARNING, "dxf.c|dxf_export_xref_file ():\n"
+								"     unable to figure out angle because I could\n"
+								"     not find pin #1 of element %s.\n"
+								"     Setting to %g degrees.\n", UNKNOWN(NAMEONPCB_NAME(element)), theta);
+			}
+			dxf_block_name = pcb_strdup(dxf_clean_string(UNKNOWN(DESCRIPTION_NAME(element))));
+			if (dxf_metric) {
+				/* convert mils to mm */
+				dxf_x0 = PCB_COORD_TO_MM(x);
+				/* convert mils to mm and a right handed
+				 * Cartesian Coordinate System */
+				dxf_y0 = PCB_COORD_TO_MM(PCB->MaxHeight - y);
+			}
+			else {
+				/*
+				 * no need to convert, some things remain the
+				 * same.
+				 */
+				dxf_x0 = PCB_COORD_TO_MIL(x);
+				/*
+				 * only convert to a right handed Cartesian
+				 * Coordinate System.
+				 */
+				dxf_y0 = PCB_COORD_TO_MIL(PCB->MaxHeight - y);
+			}
+#if 0
+			/*
+			 * convert the rotation angle as well:
+			 * theta -> CW, DXF -> CCW.
+			 */
+			/*!
+			 * \todo for now we only support Cardinal angles
+			 * [North, East, South, West]
+			 */
+			if (theta == 0.0)
+				dxf_rot_angle = 90.0;
+			else if (theta == 90.0)
+				dxf_rot_angle = 0.0;
+			else if (theta == 180.0)
+				dxf_rot_angle = 270.0;
+			else if (theta == 270.0)
+				dxf_rot_angle = 180.0;
+			else {
+				dxf_rot_angle = 0.0;
+				Message(PCB_MSG_WARNING, "dxf.c|dxf_export_xref_file ():\n"
+								"     unable to figure out angle of dxf block\n"
+								"     %s because pcb angle theta is not Cardinal [0.0, 90.0, 180.0, 270.0].\n"
+								"     Setting dxf_rot_angle to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), dxf_rot_angle);
+			}
+#endif
+			dxf_write_insert(fp, dxf_id_code, dxf_block_name, DXF_DEFAULT_LINETYPE,	/* dxf_linetype, */
+											 DXF_DEFAULT_LAYER,	/* dxf_layer, */
+											 dxf_x0, dxf_y0, 0.0,	/* dxf_z0, */
+											 0.0,			/* dxf_thickness, */
+											 1.0,			/* dxf_rel_x_scale, */
+											 1.0,			/* dxf_rel_y_scale, */
+											 1.0,			/* dxf_rel_z_scale, */
+											 0.0,			/* dxf_column_spacing, */
+											 0.0,			/* dxf_row_spacing, */
+											 dxf_rot_angle, DXF_COLOR_BYLAYER,	/* dxf_color, */
+											 0,				/* dxf_attribute_follows, */
+											 0,				/* dxf_paperspace, */
+											 1,				/* dxf_columns, */
+											 1				/* dxf_rows */
+				);
+		}
+		dxf_id_code++;
+	}
+	END_LOOP;											/* End of ELEMENT_LOOP  */
+	/*
+	 * write an ENDSEC marker to the DXF file.
+	 */
+	dxf_write_endsection(fp);
+	/*
+	 * write an EOF marker and close the DXF file.
+	 */
+	dxf_write_eof(fp);
+	fclose(fp);
+	fp = NULL;
+	dxf_id_code = 0;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_export_xref_file () function.\n", __FILE__, __LINE__);
+#endif
+	return (0);
+}
+
+
+/*!
+ * \brief Close DXF layer file.
+ */
+static void dxf_maybe_close_file()
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_maybe_close_file () function.\n", __FILE__, __LINE__);
+#endif
+	if (fp) {
+		/* write an EOF marker and close the DXF file */
+		dxf_write_eof(fp);
+		fclose(fp);
+	}
+	dxf_id_code = 0;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_maybe_close_file () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Export (or print) the current PCB.
+ *
+ * The options given represent the choices made from the options returned
+ * from dxf_get_export_options.\n
+ * Call with options == NULL to start the primary GUI (create a main window,
+ * print, export, etc).\n
+ * \n
+ * First get the export options.\n
+ * Do export all the DXF files required.\n
+ * <ul>
+ * <li>Export the DXF file with Xref blocks to a seperate dxf file if required.\n
+ * <li>Export a DXF file for every PCB layer.\n
+ * </ul>
+ */
+static void dxf_do_export(HID_Attr_Val * options)
+{
+	const char *dxf_fnbase;
+	int i;
+	static int saved_layer_stack[MAX_LAYER];
+	BoxType region;
+	int save_ons[MAX_LAYER + 2];
+	int tmp[128], len;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_do_export () function.\n", __FILE__, __LINE__);
+#endif
+	dxf_fnbase = NULL;
+
+	if (!options) {
+		dxf_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++) {
+			dxf_values[i] = dxf_options[i].default_val;
+		}
+		options = dxf_values;
+	}
+	/*
+	 * verbose output (dxf files to contain comments).
+	 */
+	dxf_verbose = options[HA_verbose].int_value;
+	/*
+	 * if all layers needs to be exported.
+	 */
+	dxf_export_all_layers = options[HA_export_all_layers].int_value;
+	/*
+	 * output to be in in mils or mm.
+	 */
+	dxf_metric = options[HA_metric].int_value;
+	/*
+	 * entity color to be BYBLOCK (or by layer number).
+	 */
+	dxf_color_is_byblock = options[HA_color_byblock].int_value;
+	/*
+	 * if xrefs DXF file needs to be exported.
+	 */
+	dxf_xrefs = options[HA_xrefs].int_value;
+	if (dxf_xrefs) {
+		const char *xreff;
+		/*
+		 * determine a file name for the xref file.
+		 */
+		xreff = options[HA_xreffile].str_value;
+		if (xreff == NULL)
+			xreff = "pcb-out_xrefs";
+		i = strlen(xreff);
+		dxf_xref_filename = (char *) realloc(dxf_xref_filename, i + 40);
+		memcpy(dxf_xref_filename, xreff, i+1);
+		strcat(dxf_xref_filename, "_xrefs.dxf");
+		dxf_filesuffix = dxf_xref_filename + strlen(dxf_xref_filename);
+		dxf_export_xref_file();
+	}
+	/*
+	 * determine a file name base for the DXF layer files.
+	 */
+	dxf_fnbase = options[HA_dxffile].str_value;
+	if (!dxf_fnbase) {
+		dxf_fnbase = "pcb_layers";
+	}
+	i = strlen(dxf_fnbase);
+	dxf_filename = (char *) realloc(dxf_filename, i + 40);
+	strcpy(dxf_filename, dxf_fnbase);
+	strcat(dxf_filename, "_");
+	dxf_filesuffix = dxf_filename + strlen(dxf_filename);
+	memset(print_layer, 0, sizeof(print_layer));
+	/*
+	 * use this to temporarily enable all layers.
+	 */
+	hid_save_and_show_layer_ons(save_ons);
+
+	len = pcb_layer_list(PCB_LYT_SILK | PCB_LYT_COPPER, tmp, sizeof(tmp));
+	for(i = 0; i < len; i++)
+		if (!IsLayerNumEmpty(tmp[i]))
+			print_layer[tmp[i]] = 1;
+
+	memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack));
+	qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), dxf_layer_sort);
+	linewidth = -1;
+	lastcap = -1;
+	lastgroup = -1;
+	lastcolor = -1;
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+	pagecount = 1;
+/*  dxf_init_apertures ();*/
+	lastgroup = -1;
+	c_layerapps = 0;
+	dxf_finding_apertures = 1;
+	hid_expose_callback(&dxf_hid, &region, 0);
+	c_layerapps = 0;
+	dxf_finding_apertures = 0;
+	hid_expose_callback(&dxf_hid, &region, 0);
+	memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack));
+	dxf_maybe_close_file();
+	hid_restore_layer_ons(save_ons);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_do_export () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Parse the command line.
+ *
+ * Parse HID register attributes and HID command line arguments.\n
+ * Call this early for whatever HID will be the primary HID, as it will set
+ * all the registered attributes.\n
+ * The HID should remove all arguments, leaving any possible file names
+ * behind.
+ */
+static void dxf_parse_arguments(int *argc, char ***argv)
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_parse_arguments () function.\n", __FILE__, __LINE__);
+#endif
+	hid_parse_command_line(argc, argv);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_parse_arguments () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Sort drills (holes).
+ */
+static int dxf_drill_sort(const void *va, const void *vb) {
+	DxfPendingDrills *a;
+	DxfPendingDrills *b;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_drill_sort () function.\n", __FILE__, __LINE__);
+#endif
+	a = (DxfPendingDrills *) va;
+	b = (DxfPendingDrills *) vb;
+	if (a->diam != b->diam)
+		return a->diam - b->diam;
+	if (a->x != b->x)
+		return a->x - a->x;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_drill_sort () function.\n", __FILE__, __LINE__);
+#endif
+	return b->y - b->y;
+}
+
+
+/*!
+ * \brief Set the layer with <c>name</c> for DXF export.
+ *
+ * During redraw or print/export cycles, this is called once per layer
+ * (or layer group, for copper layers).\n
+ * If it returns false (zero), the HID does not want that layer, and none of
+ * the drawing functions should be called.\n
+ * If it returns true (nonzero), the items in that layer [group] should be
+ * drawn using the various drawing functions.\n
+ * In addition to the MAX_LAYERS copper layer groups, you may select layers
+ * indicated by the macros SL_* defined, or any others with an index of -1.\n
+ * For copper layer groups, you may pass NULL for name to have a name fetched
+ * from the PCB struct.\n
+ * \n
+ * All copper containing layers are set for DXF export.\n
+ * All assembly layers are set for DXF export.\n
+ * Exceptions are: \n
+ * <ul>
+ * <li>Layers with the name "invisible" are not set for DXF export.
+ * <li>Layers with the name "keepout" are not set for DXF export.
+ * <li>Layers without exportable items are not set for DXF export.
+ * </ul>
+ */
+static int dxf_set_layer(const char *name, int group) {
+	char *cp;
+	int idx;
+	const char *fmt;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_set_layer () function.\n", __FILE__, __LINE__);
+#endif
+	idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+
+	if (name == 0) {
+		/* if none given, get the layer name from pcb */
+		name = PCB->Data->Layer[idx].Name;
+	}
+	if (dxf_verbose) {
+		fprintf(stderr, "DXF: now processing Layer %s group %d\n", name, group);
+	}
+	if (dxf_export_all_layers) {
+		/* do nothing here to export all layers */
+	}
+	else {
+		if (idx >= 0 && idx < max_copper_layer && !print_layer[idx]) {
+			/* do not export empty layers */
+			if (dxf_verbose) {
+				fprintf(stderr, "DXF: Warning, Layer %s contains no exportable items and is not set.\n", name);
+				fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_layer () function.\n", __FILE__, __LINE__);
+			}
+			return 0;
+		}
+		if (strcmp(name, "invisible") == 0) {
+			/* do not export the layer with the name "invisible" */
+			if (dxf_verbose) {
+				fprintf(stderr, "DXF: Warning, Layer %s not set.\n", name);
+				fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_layer () function.\n", __FILE__, __LINE__);
+			}
+			return 0;
+		}
+		if (strcmp(name, "keepout") == 0) {
+			/* do not export the layer with the name "keepout" */
+			if (dxf_verbose) {
+				fprintf(stderr, "DXF: Warning, Layer %s not set.\n", name);
+				fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_layer () function.\n", __FILE__, __LINE__);
+			}
+			return 0;
+		}
+		if (SL_TYPE(idx) == SL_ASSY) {
+			/* do not export the layers with the type SL_ASSY */
+			if (dxf_verbose) {
+				fprintf(stderr, "DXF: Warning, Layer %s with type SL_ASSY not set.\n", name);
+				fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_layer () function.\n", __FILE__, __LINE__);
+			}
+			return 0;
+		}
+	}
+	if (is_drill && dxf_n_pending_drills) {
+		int i;
+		/* dump pending drills in sequence */
+		qsort(dxf_pending_drills, dxf_n_pending_drills, sizeof(DxfPendingDrills), dxf_drill_sort);
+		for (i = 0; i < dxf_n_pending_drills; i++) {
+			if (i == 0 || dxf_pending_drills[i].diam != dxf_pending_drills[i - 1].diam) {
+				if (dxf_verbose) {
+					/*!
+					 * \todo this output should go to file in
+					 * whatever form instead of being put on stderr.
+					 */
+/*          fprintf (stderr,
+            "DXF: T%02d\015\012", ap);*/
+				}
+			}
+			if (dxf_verbose) {
+				/*!
+				 * \todo this output should go to file in
+				 * whatever form instead of being put on stderr.
+				 */
+				fprintf(stderr, "DXF: X:%06d Y:%06ld\n", DXF_X(PCB, dxf_pending_drills[i].x), DXF_Y(PCB, dxf_pending_drills[i].y));
+			}
+		}
+		free(dxf_pending_drills);
+		dxf_n_pending_drills = dxf_max_pending_drills = 0;
+		dxf_pending_drills = 0;
+	}
+	is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	is_mask = (SL_TYPE(idx) == SL_MASK);
+	current_mask = 0;
+	if (group < 0 || group != lastgroup) {
+		time_t currenttime;
+		char utcTime[64];
+		char *sext = "_layer.dxf";
+		lastgroup = group;
+		dxf_lastX = -1;
+		dxf_lastY = -1;
+		lastcolor = 0;
+		linewidth = -1;
+		lastcap = -1;
+/*    dxf_set_app_layer (c_layerapps);*/
+		c_layerapps++;
+		if (dxf_finding_apertures) {
+			return 1;
+		}
+/*    if (!curapp->nextAperture)
+      {
+        return 0;
+      }*/
+		dxf_maybe_close_file();
+		pagecount++;
+		switch (idx) {
+		case SL(PDRILL, 0):
+			sext = ".dxf";
+			break;
+		case SL(UDRILL, 0):
+			sext = ".dxf";
+			break;
+		}
+		strcpy(dxf_filesuffix, layer_type_to_file_name(idx, FNS_first));
+		strcat(dxf_filesuffix, sext);
+		fp = fopen(dxf_filename, "w");
+		if (fp == NULL) {
+			Message(PCB_MSG_ERROR, "DXF: could not open %s for writing.\n", dxf_filename);
+			return 1;
+		}
+		/* write version info as a dxf comment */
+		dxf_write_comment(fp, PCB_DXF_HID_VERSION);
+		/* write dxf header information */
+		dxf_write_header();
+		/* write a section ENTITIES marker to the DXF file */
+		dxf_write_section(fp, "ENTITIES");
+		was_drill = is_drill;
+		/*!
+		 * \todo this output should go to file in
+		 * whatever form instead of being put on stderr.
+		 */
+		if (dxf_verbose) {
+			fprintf(stderr, "DXF: Start of page %d for group %d idx %d\n", pagecount, group, idx);
+		}
+		if (group < 0 || group != lastgroup) {
+			/* create a portable timestamp */
+			currenttime = time(NULL);
+			/* avoid gcc complaints */
+			fmt = pcb_strdup("%c UTC");
+			strftime(utcTime, sizeof utcTime, fmt, gmtime(&currenttime));
+		}
+		if (dxf_verbose) {
+			/* report at the beginning of each file */
+			fprintf(stderr, "DXF: Board Name: %s, %s \n", UNKNOWN(PCB->Name), UNKNOWN(name));
+			fprintf(stderr, "DXF: Created by: %s.\n", PCB_DXF_HID_VERSION);
+			fprintf(stderr, "DXF: Creation date: %s \n", utcTime);
+			fprintf(stderr, "DXF: File Format according to: AutoCAD R14.\n");
+			if (dxf_metric) {
+				fprintf(stderr, "DXF using Metric coordinates [mm].\n");
+				pcb_fprintf(stderr, "PCB Dimensions: %.0mm x %.0mm.\n", PCB->MaxWidth, PCB->MaxHeight);
+			}
+			else {
+				fprintf(stderr, "DXF using Imperial coordinates [mil].\n");
+				pcb_fprintf(stderr, "PCB Dimensions: %.0ml x %.0ml.\n", PCB->MaxWidth, PCB->MaxHeight);
+			}
+			fprintf(stderr, "PCB Coordinate Origin: lower left.\n");
+			fprintf(stderr, "DXF: Now processing Layer %s group %d drill %d mask %d\n", name, group, is_drill, is_mask);
+		}
+		/* build a legal identifier */
+		if (dxf_layername) {
+			free(dxf_layername);
+		}
+		dxf_layername = pcb_strdup(dxf_filesuffix);
+		dxf_layername[strlen(dxf_layername) - strlen(sext)] = 0;
+		/* remove all non-alpha-nummerical characters and change all to upper characters */
+		for (cp = dxf_layername; *cp; cp++) {
+			if (isalnum((int) *cp)) {
+				*cp = toupper(*cp);
+			}
+			else {
+				*cp = '_';
+			}
+		}
+		lncount = 1;
+		if (dxf_verbose) {
+			fprintf(stderr, "DXF: Setting Layer %s.\n", dxf_layername);
+/*      fprintf (stderr, "DXF: Aperture Data %s.\n", curapp->appList.Data);*/
+		}
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_layer () function.\n", __FILE__, __LINE__);
+#endif
+	return 1;
+}
+
+
+/*!
+ * \brief Constructor for the graphic context.
+ */
+static hidGC dxf_make_gc(void)
+{
+	hidGC rv;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_make_gc () function.\n", __FILE__, __LINE__);
+#endif
+	rv = (hidGC) calloc(1, sizeof(hid_gc_struct));
+	rv->cap = Trace_Cap;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_make_gc () function.\n", __FILE__, __LINE__);
+#endif
+	return rv;
+}
+
+
+/*!
+ * \brief Destructor for the graphic context.
+ */
+static void dxf_destroy_gc(hidGC gc)
+{
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_destroy_gc () function.\n", __FILE__, __LINE__);
+#endif
+	free(gc);
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_destroy_gc () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+/*!
+ * Special note about the "erase" color: To use this color, you must use this
+ * function to tell the HID when you're using it.\n
+ * At the beginning of a layer redraw cycle (i.e. after set_layer), call
+ * use_mask() to redirect output to a buffer.\n
+ * Draw to the buffer (using regular HID calls) using regular and "erase"
+ * colors.\n
+ * Then call use_mask(HID_MASK_OFF) to flush the buffer to the HID.\n
+ * If you use the "erase" color when use_mask is disabled, it simply draws in
+ * the background color.\n
+ * Values:\n
+ * <ul>
+ *   <li> <c>HID_MASK_OFF == 0 </c> Flush the buffer and return to non-mask operation.
+ *   <li> <c>HID_MASK_BEFORE == 1 </c> Polygons being drawn before clears.
+ *   <li> <c>HID_MASK_CLEAR == 2 </c> Clearances being drawn.
+ *   <li> <c>HID_MASK_AFTER == 3 </c> Polygons being drawn after clears.
+ * </ul>
+ */
+static void dxf_use_mask(int use_it) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_use_mask () function.\n", __FILE__, __LINE__);
+#endif
+	current_mask = use_it;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_use_mask () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Set a color.
+ *
+ * Set the color of the entity.
+ * Names can be like "red" or "#rrggbb" or special names like "erase".
+ * <b>Always</b> use the "erase" color for removing ink (like polygon reliefs
+ * or thermals), as you cannot rely on knowing the background color or special
+ * needs of the HID.\n
+ * Always use the "drill" color to draw holes.\n
+ * You may assume this is cheap enough to call inside the redraw callback,
+ * but not cheap enough to call for each item drawn.
+ */
+static void dxf_set_color(hidGC gc,	/*!< graphic context  */
+													const char *name) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_set_color () function.\n", __FILE__, __LINE__);
+#endif
+	if (strcmp(name, "erase") == 0) {
+		gc->color = 1;
+		gc->erase = 1;
+		gc->drill = 0;
+	}
+	else if (strcmp(name, "drill") == 0) {
+		gc->color = 1;
+		gc->erase = 0;
+		gc->drill = 1;
+	}
+	else {
+		gc->color = 0;
+		gc->erase = 0;
+		gc->drill = 0;
+	}
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_color () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Set the line style.
+ *
+ * Set the line cap style in the graphic context.\n
+ * While calling this is cheap, calling it with different values each time
+ * may be expensive, so grouping items by line style is helpful.
+*/
+static void dxf_set_line_cap(hidGC gc, EndCapStyle style) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_set_line_cap () function.\n", __FILE__, __LINE__);
+#endif
+	gc->cap = style;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_line_cap () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Set the line width.
+ *
+ * Set the line width in the graphic context.\n
+ * While calling this is cheap, calling it with different values each time
+ * may be expensive, so grouping items by line width is helpful.
+ */
+static void dxf_set_line_width(hidGC gc, int width) {
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_set_line_width () function.\n", __FILE__, __LINE__);
+#endif
+	gc->width = width;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_set_line_width () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+static void dxf_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+
+/*!
+ * \brief Use the graphic context.
+ */
+static void dxf_use_gc(hidGC gc, int radius) {
+	int c;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_use_gc () function.\n", __FILE__, __LINE__);
+#endif
+	if (radius) {
+		radius *= 2;
+		if (radius != linewidth || lastcap != Round_Cap) {
+/*      c = dxf_find_aperture_code (radius, ROUND);*/
+			if (c <= 0) {
+				fprintf(stderr, "DXF: Error, aperture for radius %d type ROUND is %d\n", radius, c);
+			}
+			if (fp && !is_drill) {
+				fprintf(stderr, "DXF: is not a drill %d.\n", c);
+			}
+			linewidth = radius;
+			lastcap = Round_Cap;
+		}
+	}
+	else if (linewidth != gc->width || lastcap != gc->cap) {
+		linewidth = gc->width;
+		lastcap = gc->cap;
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			c = SHP_ROUND;
+			break;
+		default:
+		case Square_Cap:
+			c = SHP_SQUARE;
+			break;
+		}
+		if (fp) {
+/*      fprintf (stderr, "DXF: aperture %d.\n ", ap); */
+		}
+	}
+#if 0
+	if (lastcolor != gc->color) {
+		c = gc->color;
+		if (is_drill)
+			return;
+		if (is_mask)
+			c = (gc->erase ? 0 : 1);
+		lastcolor = gc->color;
+		if (fp) {
+			if (c) {
+				/*!
+				 * \todo this output should go to file in
+				 * whatever form instead of being put on stderr.
+				 */
+				fprintf(stderr, "%%LN%s_C%d*%%\015\012", layername, lncount++);
+				fprintf(stderr, "%%LPC*%%\015\012");
+			}
+			else {
+				fprintf(stderr, "%%LN%s_D%d*%%\015\012", layername, lncount++);
+				fprintf(stderr, "%%LPD*%%\015\012");
+			}
+		}
+	}
+#endif
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leavinging dxf_use_gc () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Draw a rectangle.
+ *
+ * The usual drawing functions.\n
+ * "draw" means to use segments of the given width, whereas "fill" means to
+ * fill to a zero-width outline.\n
+ * We draw the rectangle counter clockwise (CCW) with 5 vertices
+ * (XY-coordinates).\n
+ * It is assumed that the first XY-coordinate pair (x1, y1) contains the
+ * bottom left corner values and that the second XY-coordinate pair (x2, y2)
+ * contains the top right corner values. \n
+ * The rectangle is not filled, use dxf_fill_rect () for a filled rectangle.
+ */
+static void dxf_draw_rect(hidGC gc,
+													/*!< graphic context  */
+													int x1,
+													/*!< X-value bottom left ?? point  */
+													int y1,
+													/*!< Y-value bottom left ?? point  */
+													int x2,
+													/*!< X-value top right ?? point  */
+													int y2
+													/*!< Y-value top right ?? point  */
+	) {
+	double dxf_start_width;
+	double dxf_end_width;
+	int dxf_color;
+	double dxf_x0;
+	double dxf_y0;
+	double dxf_x1;
+	double dxf_y1;
+	double dxf_x2;
+	double dxf_y2;
+	double dxf_x3;
+	double dxf_y3;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_draw_rect () function.\n", __FILE__, __LINE__);
+#endif
+	/*
+	 * return if no valid file pointer exists.
+	 */
+	if (!fp) {
+		fprintf(stderr, "Warning: no valid file pointer exists.\n");
+		return;
+	}
+	if ((x1 == x2) && (y1 == y2)) {
+		fprintf(stderr, "Warning: start point and end point are identical for the entity with id-code: %x\n", dxf_id_code);
+		fprintf(stderr, "   entity is discarded from output.\n");
+		return;
+	}
+	dxf_start_width = (double) gc->width;
+	dxf_end_width = (double) gc->width;
+	if (dxf_color_is_byblock) {
+		dxf_color = DXF_COLOR_BYBLOCK;
+	}
+	else
+		dxf_color = gc->color;
+	dxf_x0 = DXF_X(PCB, x1);
+	dxf_y0 = DXF_Y(PCB, y1);
+	dxf_x1 = DXF_X(PCB, x2);
+	dxf_y1 = DXF_Y(PCB, y1);
+	dxf_x2 = DXF_X(PCB, x1);
+	dxf_y2 = DXF_Y(PCB, y2);
+	dxf_x3 = DXF_X(PCB, x2);
+	dxf_y3 = DXF_Y(PCB, y2);
+	/*
+	 * write polyline sequence.
+	 */
+	dxf_write_polyline(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 0.0,				/* x0, *//* the polyline entity <b>always</b> remains on 0.0, 0.0, 0.0  */
+										 0.0,				/* y0, */
+										 0.0,				/* z0, */
+										 0.0,				/* extr_x0, *//* the polyline extrusion vector <b>always</b> is 0.0, 0.0, 1.0  */
+										 0.0,				/* extr_y0, */
+										 1.0,				/* extr_z0, */
+										 0.0,				/* thickness, *//* copper weight ??  */
+										 dxf_start_width, dxf_end_width, dxf_color,	/* color, */
+										 1,					/* vertices_follow, */
+										 0,					/* modelspace, */
+										 0,					/* flag, */
+										 0,					/* polygon_mesh_M_vertex_count, */
+										 0,					/* polygon_mesh_N_vertex_count, */
+										 0,					/* smooth_M_surface_density, */
+										 0,					/* smooth_N_surface_density, */
+										 0					/* surface_type */
+		);
+	dxf_id_code++;
+	/*
+	 * write first XY-coordinate (base point, bottom left corner).
+	 */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x0, dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/*
+	 * write second XY-coordinate (bottom right corner).
+	 */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x1, dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/*
+	 * write third XY-coordinate (top right left corner).
+	 */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x1, dxf_y1, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/*
+	 * write fourth XY-coordinate (top left corner).
+	 */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x0, dxf_y1, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/*
+	 * write fifth XY-coordinate (again the bottom left corner, to close
+	 * the rectangle).
+	 */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x0, dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/*
+	 * end of polyline sequence.
+	 */
+	dxf_write_endseq(fp);
+	dxf_lastX = dxf_x1;
+	dxf_lastY = dxf_y1;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_draw_rect () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Draw a line.
+ *
+ * The usual drawing functions.\n
+ * "draw" means to use segments of the given width, whereas "fill" means to
+ * fill to a zero-width outline.\n
+ * Translate the pcb X,Y-coordinates of lines and trace segments to dxf
+ * X,Y,Z-coordinates.\n
+ * Add layer, linetype, color and width values.\n
+ * Write a series of polylines and vertices by calling low level functions.\n
+ * If the endcap style is SHP_ROUND add a donut at the begin and end coordinates
+ * of the line segment.\n
+ * If the endcap style is SHP_SQUARE elongate the line segment with half its
+ * width.\n
+ * Remarks:\n
+ * <ul>
+ * <li>We do not draw lines of 1 mil wide or smaller.\n
+ * <li>We do not draw lines with identical start and end XY-coordinates (zero
+ * length).\n
+ * <li>We draw every trace segment as a single AutoCAD entity (polyline).\n
+ * </ul>
+ *
+ * \todo In case of a series of trace segments, we have to continue with a
+ * vertex from the last XY-coordinates.\n
+ * While the conditions for starting or continuing are simple to determine:\n
+ * <c>if ((dxf_x1, dxf_y1) == dxf_lastX, dxf_lastY)) ... </c>\n
+ * The caveat is how to determine when to close the polyline sequence
+ * (with an ENDSEQ marker) after the last vertex (endpoint of the last trace
+ * segment).\n
+ * One approach could be to close the series when the start coordinates of the
+ * new (to be drawn) trace segment do not coincide with the endpoint of the
+ * previously used trace segment (dxf_last[X. Y] values).\n
+ * This however would not be a solution for a branching trace segment or the
+ * last trace segment to be drawn on that particular layer.\n
+ * For the last trace segment to be drawn on a particular layer, we would have
+ * to check if the layer didn't change since the last trace segment was
+ * drawn.\n
+ */
+static void dxf_draw_line(hidGC gc,
+													/*!< graphic context  */
+													int x1,
+													/*!< X-value start point  */
+													int y1,
+													/*!< Y-value start point  */
+													int x2,
+													/*!< X-value end point  */
+													int y2
+													/*!< Y-value end point  */
+	) {
+	pcb_bool m;
+	double dxf_x0;								/* start point */
+	double dxf_y0;								/* start point */
+	double dxf_x1;								/* end point */
+	double dxf_y1;								/* end point */
+	double dxf_start_width;				/* trace width */
+	double dxf_end_width;					/* trace width */
+	int dxf_color;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_draw_line () function.\n", __FILE__, __LINE__);
+#endif
+	m = pcb_false;
+	if (!fp) {
+		/* return if no valid file pointer exists */
+		fprintf(stderr, "Warning: no valid file pointer exists.\n");
+		return;
+	}
+	if (gc->width == 1) {
+		/* we do not draw 1 mil width traces */
+		fprintf(stderr, "Warning: lines with a width == 1 mil will not be drawn.\n");
+		fprintf(stderr, "   entity is discarded from output.\n");
+		return;
+	}
+	if ((x1 == x2) && (y1 == y2)) {
+		/* we do not draw zero length traces */
+		fprintf(stderr, "Warning: start point and end point are identical for the entity with id-code: %x.\n", dxf_id_code);
+		fprintf(stderr, "   entity is discarded from output.\n");
+		return;
+	}
+	dxf_use_gc(gc, 0);
+	/* determine the polyline widths */
+	if (dxf_metric) {
+		dxf_start_width = (double) PCB_COORD_TO_MM(gc->width);
+		dxf_end_width = (double) PCB_COORD_TO_MM(gc->width);
+	}
+	else {
+		dxf_start_width = (double) PCB_COORD_TO_MIL(gc->width);
+		dxf_end_width = (double) PCB_COORD_TO_MIL(gc->width);
+	}
+	/* determine polyline color */
+	if (dxf_color_is_byblock) {
+		dxf_color = DXF_COLOR_BYBLOCK;
+	}
+	else
+		dxf_color = gc->color;
+	/*
+	 * determine start and end point X,Y-values w.r.t. metric or imperial.
+	 */
+	if (dxf_metric) {
+		dxf_x0 = PCB_COORD_TO_MM(DXF_X(PCB, x1));
+		dxf_y0 = PCB_COORD_TO_MM(DXF_Y(PCB, y1));
+		dxf_x1 = PCB_COORD_TO_MM(DXF_X(PCB, x2));
+		dxf_y1 = PCB_COORD_TO_MM(DXF_Y(PCB, y2));
+	}
+	else {
+		dxf_x0 = PCB_COORD_TO_MIL(DXF_X(PCB, x1));
+		dxf_y0 = PCB_COORD_TO_MIL(DXF_Y(PCB, y1));
+		dxf_x1 = PCB_COORD_TO_MIL(DXF_X(PCB, x2));
+		dxf_y1 = PCB_COORD_TO_MIL(DXF_Y(PCB, y2));
+	}
+	/*!
+	 * \todo Someday we have to do something here with multiple trace
+	 * segments here, the problem for now is how to determine when the
+	 * last trace segment was passed.
+	 */
+	if ((dxf_x0 == dxf_lastX) && (dxf_y0 == dxf_lastY)) {
+		m = pcb_true;
+	}
+	/*
+	 * This is just a dirty hack for AutoCAD doesn't have endcap styles.
+	 * Donuts can not be implementend in the trace polyline since donuts
+	 * are a closed polyline themselves.
+	 */
+	if (gc->cap == SHP_ROUND) {
+		/* place a donut at the start of the trace segment */
+		dxf_write_polyline(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+											 DXF_DEFAULT_LAYER,	/* layer, */
+											 0.0,			/* x0, */
+											 0.0,			/* y0, */
+											 0.0,			/* z0, */
+											 0.0,			/* extr_x0, */
+											 0.0,			/* extr_y0, */
+											 1.0,			/* extr_z0, */
+											 0.0,			/* thickness, copper weight ??  */
+											 0.5 * dxf_start_width, 0.5 * dxf_end_width, dxf_color,	/* color, */
+											 1,				/* vertices_follow, */
+											 0,				/* modelspace, */
+											 1,				/* flag, */
+											 0,				/* polygon_mesh_M_vertex_count, */
+											 0,				/* polygon_mesh_N_vertex_count, */
+											 0,				/* smooth_M_surface_density, */
+											 0,				/* smooth_N_surface_density, */
+											 0				/* surface_type */
+			);
+		dxf_id_code++;
+		/*
+		 * write first XY-coordinate (at the start of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_x0 - (0.25 * dxf_start_width), dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write second XY-coordinate (at the start of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_x0 + (0.25 * dxf_start_width), dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/* write the end of polyline sequence marker */
+		dxf_write_endseq(fp);
+		/* place a donut at the end of the trace segment */
+		dxf_write_polyline(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+											 DXF_DEFAULT_LAYER,	/* layer, */
+											 0.0,			/* x0, */
+											 0.0,			/* y0, */
+											 0.0,			/* z0, */
+											 0.0,			/* extr_x0, */
+											 0.0,			/* extr_y0, */
+											 1.0,			/* extr_z0, */
+											 0.0,			/* thickness, *//* copper weight ?? */
+											 0.5 * dxf_start_width, 0.5 * dxf_end_width, dxf_color,	/* color, */
+											 1,				/* vertices_follow, */
+											 0,				/* modelspace, */
+											 1,				/* flag, */
+											 0,				/* polygon_mesh_M_vertex_count, */
+											 0,				/* polygon_mesh_N_vertex_count, */
+											 0,				/* smooth_M_surface_density, */
+											 0,				/* smooth_N_surface_density, */
+											 0				/* surface_type */
+			);
+		dxf_id_code++;
+		/*
+		 * write first XY-coordinate (at the end of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_x1 - (0.25 * dxf_start_width), dxf_y1, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+										 0.0,				/* thickness, *//* copper weight ??  */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write second XY-coordinate (at the end of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_x1 + (0.25 * dxf_start_width), dxf_y1, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+										 0.0,				/* thickness, *//* copper weight ??  */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write the end of polyline sequence marker.
+		 */
+		dxf_write_endseq(fp);
+	}
+	/* if the end cap style is an OCTAGON: ?? */
+	if (gc->cap == SHP_OCTAGON) {
+		/*!
+		 * \todo This end cap style has yet to be implemented at the
+		 * start and end point of a trace.
+		 * Note: done for SHP_ROUND and SQUARE.
+		 */
+	}
+	/*
+	 * if the end cap style is SQUARE: recompute the start and end
+	 * coordinates, that is, elongate the trace with half of the width.
+	 */
+	if (gc->cap == SHP_SQUARE) {
+		double length;							/* trace length */
+		double dxf_x0_1;						/* extended start point */
+		double dxf_y0_1;						/* extended start point */
+		double dxf_x1_1;						/* extended end point */
+		double dxf_y1_1;						/* extended end point */
+		length = sqrt((dxf_y1 - dxf_y0) * (dxf_y1 - dxf_y0) + (dxf_x1 - dxf_x0) * (dxf_x1 - dxf_x0));
+		dxf_x0_1 = dxf_x0 - ((dxf_x1 - dxf_x0) / length) * 0.5 * dxf_start_width;
+		dxf_y0_1 = dxf_y0 - ((dxf_y1 - dxf_y0) / length) * 0.5 * dxf_start_width;
+		dxf_x1_1 = dxf_x1 + ((dxf_x1 - dxf_x0) / length) * 0.5 * dxf_end_width;
+		dxf_y1_1 = dxf_y1 + ((dxf_y1 - dxf_y0) / length) * 0.5 * dxf_end_width;
+		dxf_x0 = dxf_x0_1;
+		dxf_y0 = dxf_y0_1;
+		dxf_x1 = dxf_x1_1;
+		dxf_y1 = dxf_y1_1;
+	}
+	/* write polyline sequence for the trace */
+	dxf_write_polyline(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 0.0,				/* x0, */
+										 0.0,				/* y0, */
+										 0.0,				/* z0, */
+										 0.0,				/* extr_x0, */
+										 0.0,				/* extr_y0, */
+										 1.0,				/* extr_z0, */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 dxf_start_width, dxf_end_width, dxf_color,	/* color, */
+										 1,					/* vertices_follow, */
+										 0,					/* modelspace, */
+										 0,					/* flag, */
+										 0,					/* polygon_mesh_M_vertex_count, */
+										 0,					/* polygon_mesh_N_vertex_count, */
+										 0,					/* smooth_M_surface_density, */
+										 0,					/* smooth_N_surface_density, */
+										 0					/* surface_type */
+		);
+	dxf_id_code++;
+	/* write first XY-coordinate (start of trace segment) */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x0, dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/* write second XY-coordinate (end of trace segment) */
+	dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x1, dxf_y1, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ??  */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_start_width, dxf_end_width, 0.0,	/* bulge, */
+									 0.0,					/* curve_fit_tangent_direction, */
+									 dxf_color, 0,	/* modelspace, */
+									 0						/* flag */
+		);
+	dxf_id_code++;
+	/* write the end of polyline sequence marker */
+	dxf_write_endseq(fp);
+	dxf_lastX = dxf_x1;
+	dxf_lastY = dxf_y1;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_draw_line () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Draw an (elliptic ?) arc.
+ *
+ * The usual drawing functions.\n
+ * "draw" means to use segments of the given width, whereas "fill" means to
+ * fill to a zero-width outline.\n
+ * For now we draw an (elliptic) arc with the assumption that the width is
+ * along the X-axis and the height is along the Y-axis.\n
+ * Thus the major axis of the ellipse has a size of 2 * the maximum of the
+ * greatest value of [width, height], and the minor axis has a size of 2 * the
+ * minimum value of [width, height].\n
+ * An elliptic arc with a line width of 0 is implemented for now.
+ *
+ * \todo The elliptic arc entity has to be replaced by a polyline with the
+ * correct line width (trace width).
+ * \todo In the case of a series of trace segments, continue with a vertex
+ * from the last XY-coordinates.\n
+ * While the conditions for starting or continuing are simple to determine:\n
+ * <c>if ((x1, y1) == dxf_lastX, dxf_lastY)) ... </c> \n
+ * The caveat is how to determine when to close the polyline sequence (with
+ * an ENDSEQ marker) after the last vertex (endpoint of the last trace
+ * segment).\n
+ * \todo The end cap style has to be implemented at the start and end point of
+ * a trace.
+ */
+static void dxf_draw_arc(hidGC gc,
+												 /*!< graphic context  */
+												 int cx,
+												 /*!< X-value center point  */
+												 int cy,
+												 /*!< X-value center point  */
+												 int width,
+												 /*!< length of major axis  */
+												 int height,
+												 /*!< length of minor axis  */
+												 int start_angle,
+												 /*!< start angle of elliptic arc  */
+												 int delta_angle
+												 /*!< relative angle to end angle  */
+	) {
+	float arcStartX;
+	float arcStopX;
+	float arcStartY;
+	float arcStopY;
+	double dxf_x0;								/* center point */
+	double dxf_y0;								/* center point */
+	double dxf_x1;								/* end point major axis */
+	double dxf_y1;								/* end point major axis */
+	double dxf_arcstart_x;
+	double dxf_arcstart_y;
+	double dxf_arcstop_x;
+	double dxf_arcstop_y;
+	double dxf_start_width;				/* trace width */
+	double dxf_end_width;					/* trace width */
+	double dxf_width;							/* arc width */
+	double dxf_height;						/* arc height */
+	double dxf_ratio;
+	double dxf_start_angle;
+	double dxf_end_angle;
+	int dxf_color;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_draw_arc () function.\n", __FILE__, __LINE__);
+#endif
+#if 0
+	pcb_bool m = pcb_false;
+#endif
+	if (!fp) {
+		/* return if no valid file pointer exists */
+		fprintf(stderr, "Warning: no valid file pointer exists.\n");
+		return;
+	}
+	if (gc->width == 0) {
+		/* we do not draw 0 mil wide traces */
+		fprintf(stderr, "Warning: arcs with a width == 0 mil will not be drawn.\n");
+		fprintf(stderr, "         entity is discarded from output.\n");
+		return;
+	}
+	dxf_use_gc(gc, 0);
+	arcStartX = cx - width * cos(TO_RADIANS(start_angle));
+	arcStartY = cy + height * sin(TO_RADIANS(start_angle));
+	arcStopX = cx - width * cos(TO_RADIANS(start_angle + delta_angle));
+	arcStopY = cy + height * sin(TO_RADIANS(start_angle + delta_angle));
+	if (dxf_metric) {
+		/* use metric (mm) */
+		dxf_x0 = PCB_COORD_TO_MM(DXF_X(PCB, cx));
+		dxf_y0 = PCB_COORD_TO_MM(DXF_Y(PCB, cy));
+		dxf_start_width = PCB_COORD_TO_MM(DXF_X(PCB, width));
+		dxf_end_width = PCB_COORD_TO_MM(DXF_X(PCB, width));
+		dxf_height = PCB_COORD_TO_MM(DXF_X(PCB, height));
+		dxf_width = PCB_COORD_TO_MM(DXF_X(PCB, width));
+		if (dxf_width > dxf_height) {
+			/*
+			 * the major axis of the ellipse coincides with the
+			 * X-axis.
+			 */
+			dxf_x1 = PCB_COORD_TO_MM(DXF_X(PCB, (cx + width)));
+			dxf_y1 = PCB_COORD_TO_MM(DXF_Y(PCB, cy));
+			/*
+			 * the dxf_ratio is the minor axis length over major
+			 * axis length, and is always <= 1.0
+			 */
+			dxf_ratio = dxf_height / dxf_width;
+		}
+		else {
+			/*
+			 * the major axis of the ellipse coincides with the
+			 * Y-axis.
+			 */
+			dxf_x1 = PCB_COORD_TO_MM(DXF_X(PCB, cx));
+			dxf_y1 = PCB_COORD_TO_MM(DXF_Y(PCB, (cy + height)));
+			/*
+			 * the dxf_ratio is the minor axis length over major
+			 * axis length, and is always <= 1.0
+			 */
+			dxf_ratio = dxf_width / dxf_height;
+		}
+	}
+	else {
+		/* use imperial (mil) */
+		dxf_x0 = PCB_COORD_TO_MIL(DXF_X(PCB, cx));
+		dxf_y0 = PCB_COORD_TO_MIL(DXF_Y(PCB, cy));
+		dxf_start_width = PCB_COORD_TO_MIL(DXF_X(PCB, width));
+		dxf_end_width = PCB_COORD_TO_MIL(DXF_X(PCB, width));
+		dxf_height = PCB_COORD_TO_MIL(DXF_Y(PCB, height));
+		dxf_width = PCB_COORD_TO_MIL(DXF_Y(PCB, width));
+		if (dxf_width > dxf_height) {
+			/*
+			 * the major axis of the ellipse coincides with the
+			 * X-axis.
+			 */
+			dxf_x1 = DXF_X(PCB, (cx + width));
+			dxf_y1 = DXF_Y(PCB, cy);
+			/*
+			 * the dxf_ratio is the minor axis length over major
+			 * axis length, and is always <= 1.0
+			 */
+			dxf_ratio = dxf_height / dxf_width;
+		}
+		else {
+			/*
+			 * the major axis of the ellipse coincides with the
+			 * Y-axis.
+			 */
+			dxf_x1 = DXF_X(PCB, cx);
+			dxf_y1 = DXF_Y(PCB, (cy + height));
+			/*
+			 * the dxf_ratio is the minor axis length over major
+			 * axis length, and is always <= 1.0
+			 */
+			dxf_ratio = dxf_width / dxf_height;
+		}
+	}
+	/*
+	 * we have to add 180 degrees for start_angle and end_angle because
+	 * in the pcb universe 0 degrees (the negative X-axis) is to the left,
+	 * and in the dxf universe 0 degrees is to the right.
+	 */
+	dxf_start_angle = TO_RADIANS(start_angle + 180);
+	dxf_end_angle = TO_RADIANS(start_angle + delta_angle + 180);
+	if (dxf_start_angle >= (2 * M_PI)) {
+		dxf_start_angle = dxf_start_angle - (2 * M_PI);
+	}
+	if (dxf_end_angle >= (2 * M_PI)) {
+		dxf_end_angle = dxf_end_angle - (2 * M_PI);
+	}
+	if (dxf_color_is_byblock) {
+		dxf_color = DXF_COLOR_BYBLOCK;
+	}
+	else
+		dxf_color = gc->color;
+	dxf_arcstart_x = DXF_X(PCB, arcStartX);
+	dxf_arcstart_y = DXF_Y(PCB, arcStartY);
+	dxf_arcstop_x = DXF_X(PCB, arcStopX);
+	dxf_arcstop_y = DXF_Y(PCB, arcStopY);
+	/*
+	 * This is just a dirty hack for AutoCAD doesn't have endcap styles.
+	 * Donuts can not be implemented in the trace polyline since donuts
+	 * are a closed polyline themselves.
+	 */
+	if (gc->cap == SHP_ROUND) {
+		/* place a donut at the start of the trace segment */
+		dxf_write_polyline(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+											 DXF_DEFAULT_LAYER,	/* layer, */
+											 0.0,			/* x0, */
+											 0.0,			/* y0, */
+											 0.0,			/* z0, */
+											 0.0,			/* extr_x0, */
+											 0.0,			/* extr_y0, */
+											 1.0,			/* extr_z0, */
+											 0.0,			/* thickness, *//* copper weight ?? */
+											 0.5 * dxf_start_width, 0.5 * dxf_end_width, dxf_color,	/* color, */
+											 1,				/* vertices_follow, */
+											 0,				/* modelspace, */
+											 1,				/* flag, */
+											 0,				/* polygon_mesh_M_vertex_count, */
+											 0,				/* polygon_mesh_N_vertex_count, */
+											 0,				/* smooth_M_surface_density, */
+											 0,				/* smooth_N_surface_density, */
+											 0				/* surface_type */
+			);
+		dxf_id_code++;
+		/*
+		 * write first XY-coordinate (at the start of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_arcstart_x - (0.25 * dxf_start_width), dxf_arcstart_y, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write second XY-coordinate (at the start of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_arcstart_x + (0.25 * dxf_start_width), dxf_arcstart_y, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write the end of polyline sequence marker.
+		 */
+		dxf_write_endseq(fp);
+		/*
+		 * place a donut at the end of the trace segment.
+		 */
+		dxf_write_polyline(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+											 DXF_DEFAULT_LAYER,	/* layer, */
+											 0.0,			/* x0, */
+											 0.0,			/* y0, */
+											 0.0,			/* z0, */
+											 0.0,			/* extr_x0, */
+											 0.0,			/* extr_y0, */
+											 1.0,			/* extr_z0, */
+											 0.0,			/* thickness, *//* copper weight ?? */
+											 0.5 * dxf_start_width, 0.5 * dxf_end_width, dxf_color,	/* color, */
+											 1,				/* vertices_follow, */
+											 0,				/* modelspace, */
+											 1,				/* flag, */
+											 0,				/* polygon_mesh_M_vertex_count, */
+											 0,				/* polygon_mesh_N_vertex_count, */
+											 0,				/* smooth_M_surface_density, */
+											 0,				/* smooth_N_surface_density, */
+											 0				/* surface_type */
+			);
+		dxf_id_code++;
+		/*
+		 * write first XY-coordinate (at the end of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_arcstop_x - (0.25 * dxf_start_width), dxf_arcstop_y, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write second XY-coordinate (at the end of trace segment).
+		 */
+		dxf_write_vertex(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										 DXF_DEFAULT_LAYER,	/* layer, */
+										 dxf_arcstop_x + (0.25 * dxf_start_width), dxf_arcstop_y, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+										 0.0,				/* thickness, *//* copper weight ?? */
+										 0.5 * dxf_start_width, 0.5 * dxf_end_width, 1.0,	/* bulge, */
+										 0.0,				/* curve_fit_tangent_direction, */
+										 dxf_color, 0,	/* modelspace, */
+										 0					/* flag */
+			);
+		dxf_id_code++;
+		/*
+		 * write the end of polyline sequence marker.
+		 */
+		dxf_write_endseq(fp);
+	}
+	/*
+	 * write an ellipse for the trace.
+	 */
+	dxf_write_ellipse(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+										DXF_DEFAULT_LAYER,	/* layer, */
+										dxf_x0, dxf_y0, 0.0,	/* z0, */
+										dxf_x1, dxf_y1, 0.0,	/* z1, *//* stacked, curved or flexable pcb's ?? */
+										0.0,				/* dxf_extr_x0, */
+										0.0,				/* dxf_extr_y0, */
+										1.0,				/* dxf_extr_z0, */
+										0.0,				/* thickness, *//* copper weight ?? */
+										dxf_ratio, dxf_start_angle, dxf_end_angle, dxf_color, 0	/* modelspace */
+		);
+	dxf_id_code++;
+	dxf_lastX = arcStopX;
+	dxf_lastY = arcStopY;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_draw_arc () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Draw a filled circle.
+ *
+ * The usual drawing functions.\n
+ * "draw" means to use segments of the given width, whereas "fill" means to
+ * fill to a zero-width outline.\n
+ * \todo Implement a donut (polyline) instead of a circle.
+ */
+static void dxf_fill_circle(hidGC gc,
+														/*!< graphic context. */
+														int cx,
+														/*!< X-value center point. */
+														int cy,
+														/*!< Y-value center point. */
+														int radius
+														/*!< radius of circle. */
+	) {
+	double dxf_x0;
+	double dxf_y0;
+	double dxf_radius;
+	int dxf_color;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_fill_circle () function.\n", __FILE__, __LINE__);
+#endif
+	/*
+	 * return if no valid file pointer exists.
+	 */
+	if (!fp) {
+		fprintf(stderr, "Warning: no valid file pointer exists.\n");
+		return;
+	}
+	/*
+	 * drill sizes increase per 2 mil ?
+	 */
+	if (is_drill) {
+		radius = DXF_ROUND(radius * 2) / 2;
+	}
+	dxf_use_gc(gc, radius);
+	if (is_drill) {
+		if (dxf_n_pending_drills >= dxf_max_pending_drills) {
+			dxf_max_pending_drills += 100;
+			/*
+			 * re-allocate for another 100 pending drills.
+			 */
+			dxf_pending_drills = (DxfPendingDrills *) realloc(dxf_pending_drills, dxf_max_pending_drills * sizeof(DxfPendingDrills));
+		}
+		dxf_pending_drills[dxf_n_pending_drills].x = cx;
+		dxf_pending_drills[dxf_n_pending_drills].y = cy;
+		dxf_pending_drills[dxf_n_pending_drills].diam = radius * 2;
+		dxf_n_pending_drills++;
+		return;
+	}
+	else if (gc->drill)
+		return;
+	if (dxf_metric) {							/* use metric mm */
+		dxf_x0 = PCB_COORD_TO_MM(DXF_X(PCB, cx));
+		dxf_y0 = PCB_COORD_TO_MM(DXF_Y(PCB, cy));
+		dxf_radius = PCB_COORD_TO_MM(DXF_X(PCB, radius));
+	}
+	else {												/* use imperial mil */
+
+		dxf_x0 = PCB_COORD_TO_MIL(DXF_X(PCB, cx));
+		dxf_y0 = PCB_COORD_TO_MIL(DXF_Y(PCB, cy));
+		dxf_radius = PCB_COORD_TO_MIL(DXF_X(PCB, radius));
+	}
+	if (dxf_color_is_byblock) {
+		dxf_color = DXF_COLOR_BYBLOCK;
+	}
+	else
+		dxf_color = gc->color;
+	dxf_write_circle(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									 DXF_DEFAULT_LAYER,	/* layer, */
+									 dxf_x0, dxf_y0, 0.0,	/* z0, *//* curved or flexable pcb's ??  */
+									 0.0,					/* dxf_extr_x0, */
+									 0.0,					/* dxf_extr_y0, */
+									 1.0,					/* dxf_extr_z0, */
+									 0.0,					/* thickness, *//* copper weight ??  */
+									 dxf_radius, dxf_color, 0	/* modelspace */
+		);
+	dxf_id_code++;
+	dxf_lastX = dxf_x0;
+	dxf_lastY = dxf_y0;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_fill_circle () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Draw a filled polygon.
+ *
+ * The usual drawing functions.\n
+ * "draw" means to use segments of the given width, whereas "fill" means to
+ * fill to a zero-width outline.\n
+ * A polygon is drawn with a solid fill pattern.
+ *
+ * The filled polygon is by drawn by a (closed) polyline sequence with
+ * (n_coords + 1) vertices and add a SOLID hatch pattern to this polyline.\n
+ * This solution would allow for thieving if it were ever implemented in pcb
+ * (select a hatch pattern, create a boundary path, apply a scale and all the
+ * other stuff that is needed).
+ */
+static void dxf_fill_polygon(hidGC gc,
+														 /*!< graphic context. */
+														 int n_coords,
+														 /*!< number of XY-coordinates. */
+														 int *x,
+														 /*!< pointer to array of X-values of coordinates. */
+														 int *y
+														 /*!< pointer to array of Y-values of coordinates. */
+	) {
+	pcb_bool m;
+	int i;
+	double dxf_x0;
+	double dxf_y0;
+	int dxf_color;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_fill_polygon () function.\n", __FILE__, __LINE__);
+#endif
+	dxf_x0 = 0.0;
+	dxf_y0 = 0.0;
+	dxf_color = DXF_COLOR_BYLAYER;
+
+	m = pcb_false;
+	if (is_mask && current_mask == HID_MASK_BEFORE) {
+		return;
+	}
+	dxf_use_gc(gc, 10 * 100);
+	/*
+	 * return if no valid file pointer exists.
+	 */
+	if (!fp) {
+		fprintf(stderr, "Warning: no valid file pointer exists.\n");
+		return;
+	}
+	if (dxf_color_is_byblock) {
+		dxf_color = DXF_COLOR_BYBLOCK;
+	}
+	else
+		dxf_color = gc->color;
+	/*
+	 * write hatch sequence.
+	 */
+	dxf_write_hatch(fp, DXF_DEFAULT_HATCH_PATTERN_NAME,	/* pattern_name, */
+									dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									DXF_DEFAULT_LAYER,	/* layer, */
+									dxf_x0, dxf_y0, 0.0,	/* z0, *//* stacked, curved or flexable pcb's ?? */
+									0.0,					/* extr_x0, */
+									0.0,					/* extr_y0, */
+									1.0,					/* extr_z0, */
+									0.0,					/* thickness, *//* copper weight ?? */
+									1.0,					/* pattern_scale, */
+									0.0,					/* pixel_size, */
+									45.0,					/* pattern_angle, */
+									dxf_color, 0,	/* modelspace, */
+									1,						/* solid_fill, */
+									1,						/* associative, */
+									0,						/* style, */
+									1,						/* pattern_style, */
+									0,						/* pattern_double, */
+									0,						/* pattern_def_lines, */
+									1,						/* boundary_paths, */
+									0,						/* seed_points, */
+									0,						/* seed_x0, */
+									0							/* seed_y0, */
+		);
+	/*
+	 * draw hatch boundary path polyline.
+	 */
+	dxf_write_hatch_boundary_path_polyline(fp, 2,	/* path_type_flag, *//* 2 = polyline  */
+																				 0,	/* polyline_has_bulge, *//* 0 = polygons have sharp angles  */
+																				 1,	/* polyline_is_closed, *//* 1 = closed  */
+																				 n_coords + 1	/* polyline_vertices, *//* number of polyline vertices to follow  */
+		);
+	/*
+	 * draw hatch boundary polyline vertices, write (n_coords)
+	 * XY-coordinates.
+	 */
+	for (i = 0; i < n_coords; i++) {
+		dxf_x0 = DXF_X(PCB, x[i]);
+		dxf_y0 = DXF_Y(PCB, y[i]);
+		dxf_write_hatch_boundary_path_polyline_vertex(fp, dxf_x0, dxf_y0, 0.0	/* dxf_z0 */
+			);
+		/*
+		 * close polyline with first coordinate X-Y pair.
+		 */
+		dxf_x0 = DXF_X(PCB, x[0]);
+		dxf_y0 = DXF_Y(PCB, y[0]);
+		dxf_write_hatch_boundary_path_polyline_vertex(fp, dxf_x0, dxf_y0, 0.0	/* dxf_z0 */
+			);
+	}
+	dxf_id_code++;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_fill_polygon () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief Draw a filled rectangle.
+ *
+ * The usual drawing functions.\n
+ * "draw" means to use segments of the given width, whereas "fill" means to
+ * fill to a zero-width outline.\n
+ */
+static void dxf_fill_rect(hidGC gc,
+													/*!< graphic context. */
+													int x1,
+													/*!< X-value bottom left ?? point. */
+													int y1,
+													/*!< Y-value bottom left ?? point. */
+													int x2,
+													/*!< X-value top right ?? point. */
+													int y2
+													/*!< Y-value top right ?? point. */
+	) {
+	int dxf_color;
+	double dxf_x0;
+	double dxf_y0;
+	double dxf_x1;
+	double dxf_y1;
+	double dxf_x2;
+	double dxf_y2;
+	double dxf_x3;
+	double dxf_y3;
+
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Entering dxf_fill_rect () function.\n", __FILE__, __LINE__);
+#endif
+	/*
+	 * return if no valid file pointer exists.
+	 */
+	if (!fp) {
+		fprintf(stderr, "Warning: no valid file pointer exists.\n");
+		return;
+	}
+	if ((x1 == x2) && (y1 == y2)) {
+		fprintf(stderr, "Warning: start point and end point are identical for the entity with id-code: %x\n", dxf_id_code);
+		fprintf(stderr, "   entity is discarded from output.\n");
+		return;
+	}
+	if (dxf_color_is_byblock) {
+		dxf_color = DXF_COLOR_BYBLOCK;
+	}
+	else
+		dxf_color = gc->color;
+	dxf_x0 = DXF_X(PCB, x1);
+	dxf_y0 = DXF_Y(PCB, y1);
+	dxf_x1 = DXF_X(PCB, x2);
+	dxf_y1 = DXF_Y(PCB, y1);
+	dxf_x2 = DXF_X(PCB, x1);
+	dxf_y2 = DXF_Y(PCB, y2);
+	dxf_x3 = DXF_X(PCB, x2);
+	dxf_y3 = DXF_Y(PCB, y2);
+	dxf_write_solid(fp, dxf_id_code, DXF_DEFAULT_LINETYPE,	/* linetype, */
+									DXF_DEFAULT_LAYER,	/* layer, */
+									dxf_x0,				/* base point, bottom left  */
+									dxf_y0, 0.0,	/* z0, */
+									dxf_x1,				/* alignment point, bottom right  */
+									dxf_y1, 0.0,	/* z1, */
+									dxf_x2,				/* alignment point, top left  */
+									dxf_y2, 0.0,	/* z2, */
+									dxf_x3,				/* alignment point, top right  */
+									dxf_y3, 0.0,	/* z3, */
+									0.0,					/* thickness, */
+									dxf_color, 0	/* modelspace */
+		);
+	dxf_id_code++;
+#if DEBUG
+	fprintf(stderr, "[File: %s: line: %d] Leaving dxf_fill_rect () function.\n", __FILE__, __LINE__);
+#endif
+}
+
+
+/*!
+ * \brief This is for the printer.
+ *
+ * If you call this for the GUI, xval and yval are ignored, and a dialog pops
+ * up to lead you through the calibration procedure.\n
+ * For the printer, if xval and yval are zero, a calibration page is printed
+ * with instructions for calibrating your printer.\n
+ * After calibrating, nonzero xval and yval are passed according to the
+ * instructions.\n
+ * Metric is nonzero if the user prefers metric units, else inches are used.\n
+ * Calibrate a DXF file ?.\n
+ * Since we do not calibrate a DXF file, we ignore this one.
+ */
+static void dxf_calibrate(double xval,
+													/*!< X-value. */
+													double yval
+													/*!< Y-value. */
+	) {
+	/* Intentionally: do nothing here */
+}
+
+
+/*!
+ * \brief Sets the crosshair.
+ *
+ * Which may differ from the pointer depending on grid and pad snap.\n
+ * Note that the HID is responsible for hiding, showing, redrawing, etc.\n
+ * The core just tells it what coordinates it's actually using.\n
+ * Note that this routine may need to know what "pcb units" are so it can
+ * display them in mm or mils accordingly.\n
+ * Set a crosshair in a DXF file ?.\n
+ * Since it is useless to set a crosshair in a DXF file, we ignore this one.
+ */
+static void dxf_set_crosshair(int x,
+															/*!< X-value of coordinate. */
+															int y
+															/*!< Y-value of coordinate. */
+	) {
+	/* Intentionally: do nothing here */
+}
+
+
+/*!
+ * \brief Show item ?.
+ */
+static void dxf_show_item(void *item)
+{
+}
+
+
+/*!
+ * \brief Send beep signal to stdout ?.
+ */
+static void dxf_beep(void)
+{
+	putchar(7);
+	fflush(stdout);
+}
+
+
+/*!
+ * \brief Show progress ?.
+ */
+static void dxf_progress(int dxf_so_far, int dxf_total, const char *dxf_message)
+{
+}
+
+const char *dxf_cookie = "dxf exporter";
+
+
+/*!
+ * \brief Call this as soon as possible from main().
+ *
+ * Initialise and register the DXF HID.
+ * No other HID calls are valid until this is called.
+ */
+pcb_uninit_t hid_export_dxf_init()
+{
+	memset(&dxf_hid, 0, sizeof(HID));
+
+	common_nogui_init(&dxf_hid);
+	common_draw_helpers_init(&dxf_hid);
+	dxf_hid.struct_size = sizeof(HID);
+	dxf_hid.name = "dxf";
+	dxf_hid.description = "DXF export";
+	dxf_hid.exporter = 1;
+	dxf_hid.poly_before = 1;
+
+	dxf_hid.get_export_options = dxf_get_export_options;
+	dxf_hid.do_export = dxf_do_export;
+	dxf_hid.parse_arguments = dxf_parse_arguments;
+	dxf_hid.set_layer = dxf_set_layer;
+	dxf_hid.calibrate = dxf_calibrate;
+	dxf_hid.set_crosshair = dxf_set_crosshair;
+	dxf_hid.show_item = dxf_show_item;
+	dxf_hid.beep = dxf_beep;
+	dxf_hid.progress = dxf_progress;
+
+	dxf_hid.make_gc = dxf_make_gc;
+	dxf_hid.destroy_gc = dxf_destroy_gc;
+	dxf_hid.use_mask = dxf_use_mask;
+	dxf_hid.set_color = dxf_set_color;
+	dxf_hid.set_line_cap = dxf_set_line_cap;
+	dxf_hid.set_line_width = dxf_set_line_width;
+	dxf_hid.set_draw_xor = dxf_set_draw_xor;
+	dxf_hid.draw_line = dxf_draw_line;
+	dxf_hid.draw_arc = dxf_draw_arc;
+	dxf_hid.draw_rect = dxf_draw_rect;
+	dxf_hid.fill_circle = dxf_fill_circle;
+	dxf_hid.fill_polygon = dxf_fill_polygon;
+	dxf_hid.fill_rect = dxf_fill_rect;
+
+	hid_register_hid(&dxf_hid);
+
+	hid_register_attributes(dxf_options, sizeof(dxf_options) / sizeof(dxf_options[0]), dxf_cookie, 0);
+
+/*	return hid_dxf_uninit();*/
+	return NULL;
+}
+
+
+/* EOF */
diff --git a/src_plugins/export_gcode/Makefile b/src_plugins/export_gcode/Makefile
new file mode 100644
index 0000000..2c43ef0
--- /dev/null
+++ b/src_plugins/export_gcode/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_gcode
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_gcode/Plug.tmpasm b/src_plugins/export_gcode/Plug.tmpasm
new file mode 100644
index 0000000..9d9443a
--- /dev/null
+++ b/src_plugins/export_gcode/Plug.tmpasm
@@ -0,0 +1,17 @@
+put /local/pcb/mod {export_gcode}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_gcode/gcode.o $(PLUGDIR)/export_gcode/decompose.o $(PLUGDIR)/export_gcode/trace.o $(PLUGDIR)/export_gcode/curve.o @]
+
+switch /local/pcb/export_gcode/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/LDFLAGS         libs/gui/gd/ldflags
+		put /local/pcb/mod/CFLAGS          libs/gui/gd/cflags
+		end
+end
+
+
+switch /local/pcb/export_gcode/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_gcode/README b/src_plugins/export_gcode/README
new file mode 100644
index 0000000..9ceedd2
--- /dev/null
+++ b/src_plugins/export_gcode/README
@@ -0,0 +1,5 @@
+Export to gcode
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_gcode/auxiliary.h b/src_plugins/export_gcode/auxiliary.h
new file mode 100644
index 0000000..a0b6e47
--- /dev/null
+++ b/src_plugins/export_gcode/auxiliary.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* This header file collects some general-purpose macros (and static
+   inline functions) that are used in various places. */
+
+#ifndef AUXILIARY_H
+#define AUXILIARY_H
+
+#include "config.h"
+
+/* ---------------------------------------------------------------------- */
+/* point arithmetic */
+
+#include "potracelib.h"
+
+struct point_s {
+	long x;
+	long y;
+};
+typedef struct point_s point_t;
+
+typedef potrace_dpoint_t dpoint_t;
+
+/* convert point_t to dpoint_t */
+static inline PCB_FUNC_UNUSED dpoint_t dpoint(point_t p)
+{
+	dpoint_t res;
+	res.x = p.x;
+	res.y = p.y;
+	return res;
+}
+
+/* range over the straight line segment [a,b] when lambda ranges over [0,1] */
+static inline PCB_FUNC_UNUSED dpoint_t interval(double lambda, dpoint_t a, dpoint_t b)
+{
+	dpoint_t res;
+
+	res.x = a.x + lambda * (b.x - a.x);
+	res.y = a.y + lambda * (b.y - a.y);
+	return res;
+}
+
+/* ---------------------------------------------------------------------- */
+/* some useful macros. Note: the "mod" macro works correctly for
+   negative a. Also note that the test for a>=n, while redundant,
+   speeds up the mod function by 70% in the average case (significant
+   since the program spends about 16% of its time here - or 40%
+   without the test). The "floordiv" macro returns the largest integer
+   <= a/n, and again this works correctly for negative a, as long as
+   a,n are integers and n>0. */
+
+/* integer arithmetic */
+
+static inline PCB_FUNC_UNUSED int mod(int a, int n)
+{
+	return a >= n ? a % n : a >= 0 ? a : n - 1 - (-1 - a) % n;
+}
+
+static inline PCB_FUNC_UNUSED int floordiv(int a, int n)
+{
+	return a >= 0 ? a / n : -1 - (-1 - a) / n;
+}
+
+/* Note: the following work for integers and other numeric types. */
+#undef sign
+#undef abs
+#undef min
+#undef max
+#undef sq
+#undef cu
+#define sign(x) ((x)>0 ? 1 : (x)<0 ? -1 : 0)
+#define abs(a) ((a)>0 ? (a) : -(a))
+#define min(a,b) ((a)<(b) ? (a) : (b))
+#define max(a,b) ((a)>(b) ? (a) : (b))
+#define sq(a) ((a)*(a))
+#define cu(a) ((a)*(a)*(a))
+
+#endif /* AUXILIARY_H */
diff --git a/src_plugins/export_gcode/bitmap.h b/src_plugins/export_gcode/bitmap.h
new file mode 100644
index 0000000..0978a70
--- /dev/null
+++ b/src_plugins/export_gcode/bitmap.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/* The bitmap type is defined in potracelib.h */
+#include "potracelib.h"
+
+/* The present file defines some convenient macros and static inline
+   functions for accessing bitmaps. Since they only produce inline
+   code, they can be conveniently shared by the library and frontends,
+   if desired */
+
+/* ---------------------------------------------------------------------- */
+/* some measurements */
+
+#define BM_WORDSIZE ((int)sizeof(potrace_word))
+#define BM_WORDBITS (8*BM_WORDSIZE)
+#define BM_HIBIT (((potrace_word)1)<<(BM_WORDBITS-1))
+#define BM_ALLBITS (~(potrace_word)0)
+
+/* macros for accessing pixel at index (x,y). U* macros omit the
+   bounds check. */
+
+#define bm_scanline(bm, y) ((bm)->map + (y)*(bm)->dy)
+#define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS])
+#define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1)))
+#define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a))
+#define bm_safe(bm, x, y) (bm_range(x, (bm)->w) && bm_range(y, (bm)->h))
+#define BM_UGET(bm, x, y) ((*bm_index(bm, x, y) & bm_mask(x)) != 0)
+#define BM_USET(bm, x, y) (*bm_index(bm, x, y) |= bm_mask(x))
+#define BM_UCLR(bm, x, y) (*bm_index(bm, x, y) &= ~bm_mask(x))
+#define BM_UINV(bm, x, y) (*bm_index(bm, x, y) ^= bm_mask(x))
+#define BM_UPUT(bm, x, y, b) ((b) ? BM_USET(bm, x, y) : BM_UCLR(bm, x, y))
+#define BM_GET(bm, x, y) (bm_safe(bm, x, y) ? BM_UGET(bm, x, y) : 0)
+#define BM_SET(bm, x, y) (bm_safe(bm, x, y) ? BM_USET(bm, x, y) : 0)
+#define BM_CLR(bm, x, y) (bm_safe(bm, x, y) ? BM_UCLR(bm, x, y) : 0)
+#define BM_INV(bm, x, y) (bm_safe(bm, x, y) ? BM_UINV(bm, x, y) : 0)
+#define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0)
+
+/* free the given bitmap. Leaves errno untouched. */
+static inline PCB_FUNC_UNUSED void bm_free(potrace_bitmap_t * bm)
+{
+	if (bm) {
+		free(bm->map);
+	}
+	free(bm);
+}
+
+/* return new un-initialized bitmap. NULL with errno on error */
+static inline PCB_FUNC_UNUSED potrace_bitmap_t *bm_new(int w, int h)
+{
+	potrace_bitmap_t *bm;
+	int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS;
+
+	bm = (potrace_bitmap_t *) malloc(sizeof(potrace_bitmap_t));
+	if (!bm) {
+		return NULL;
+	}
+	bm->w = w;
+	bm->h = h;
+	bm->dy = dy;
+	bm->map = (potrace_word *) malloc(dy * h * BM_WORDSIZE);
+	if (!bm->map) {
+		free(bm);
+		return NULL;
+	}
+	return bm;
+}
+
+/* clear the given bitmap. Set all bits to c. */
+static inline PCB_FUNC_UNUSED void bm_clear(potrace_bitmap_t * bm, int c)
+{
+	memset(bm->map, c ? -1 : 0, bm->dy * bm->h * BM_WORDSIZE);
+}
+
+/* duplicate the given bitmap. Return NULL on error with errno set. */
+static inline PCB_FUNC_UNUSED potrace_bitmap_t *bm_dup(const potrace_bitmap_t * bm)
+{
+	potrace_bitmap_t *bm1 = bm_new(bm->w, bm->h);
+	if (!bm1) {
+		return NULL;
+	}
+	memcpy(bm1->map, bm->map, bm->dy * bm->h * BM_WORDSIZE);
+	return bm1;
+}
+
+/* invert the given bitmap. */
+static inline PCB_FUNC_UNUSED void bm_invert(potrace_bitmap_t * bm)
+{
+	int i;
+	for (i = 0; i < bm->dy * bm->h; i++) {
+		bm->map[i] ^= BM_ALLBITS;
+	}
+}
+
+#endif /* BITMAP_H */
diff --git a/src_plugins/export_gcode/curve.c b/src_plugins/export_gcode/curve.c
new file mode 100644
index 0000000..b89c790
--- /dev/null
+++ b/src_plugins/export_gcode/curve.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: curve.c 147 2007-04-09 00:44:09Z selinger $ */
+/* private part of the path and curve data structures */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "potracelib.h"
+#include "lists.h"
+#include "curve.h"
+
+#define SAFE_MALLOC(var, n, typ) \
+  if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error
+
+/* ---------------------------------------------------------------------- */
+/* allocate and free path objects */
+
+path_t *path_new(void)
+{
+	path_t *p = NULL;
+	privpath_t *priv = NULL;
+
+	SAFE_MALLOC(p, 1, path_t);
+	memset(p, 0, sizeof(path_t));
+	SAFE_MALLOC(priv, 1, privpath_t);
+	memset(priv, 0, sizeof(privpath_t));
+	p->priv = priv;
+	return p;
+
+malloc_error:
+	free(p);
+	free(priv);
+	return NULL;
+}
+
+/* free the members of the given curve structure. Leave errno unchanged. */
+static void privcurve_free_members(privcurve_t * curve)
+{
+	free(curve->tag);
+	free(curve->c);
+	free(curve->vertex);
+	free(curve->alpha);
+	free(curve->alpha0);
+	free(curve->beta);
+}
+
+/* free a path. Leave errno untouched. */
+void path_free(path_t * p)
+{
+	if (p) {
+		if (p->priv) {
+			free(p->priv->pt);
+			free(p->priv->lon);
+			free(p->priv->sums);
+			free(p->priv->po);
+			privcurve_free_members(&p->priv->curve);
+			privcurve_free_members(&p->priv->ocurve);
+		}
+		free(p->priv);
+		/* do not free p->fcurve ! */
+	}
+	free(p);
+}
+
+/* free a pathlist, leaving errno untouched. */
+void pathlist_free(path_t * plist)
+{
+	path_t *p;
+
+	list_forall_unlink(p, plist) {
+		path_free(p);
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+/* initialize and finalize curve structures */
+
+typedef dpoint_t dpoint3_t[3];
+
+/* initialize the members of the given curve structure to size m.
+   Return 0 on success, 1 on error with errno set. */
+int privcurve_init(privcurve_t * curve, int n)
+{
+	memset(curve, 0, sizeof(privcurve_t));
+	curve->n = n;
+	SAFE_MALLOC(curve->tag, n, int);
+	SAFE_MALLOC(curve->c, n, dpoint3_t);
+	SAFE_MALLOC(curve->vertex, n, dpoint_t);
+	SAFE_MALLOC(curve->alpha, n, double);
+	SAFE_MALLOC(curve->alpha0, n, double);
+	SAFE_MALLOC(curve->beta, n, double);
+	return 0;
+
+malloc_error:
+	free(curve->tag);
+	free(curve->c);
+	free(curve->vertex);
+	free(curve->alpha);
+	free(curve->alpha0);
+	free(curve->beta);
+	return 1;
+}
+
+/* copy private to public curve structure */
+void privcurve_to_curve(privcurve_t * pc, potrace_curve_t * c)
+{
+	c->n = pc->n;
+	c->tag = pc->tag;
+	c->c = pc->c;
+}
diff --git a/src_plugins/export_gcode/curve.h b/src_plugins/export_gcode/curve.h
new file mode 100644
index 0000000..162dffe
--- /dev/null
+++ b/src_plugins/export_gcode/curve.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef CURVE_H
+#define CURVE_H
+
+#include "auxiliary.h"
+
+/* vertex is c[1] for tag=POTRACE_CORNER, and the intersection of
+   .c[-1][2]..c[0] and c[1]..c[2] for tag=POTRACE_CURVETO. alpha is only
+   defined for tag=POTRACE_CURVETO and is the alpha parameter of the curve:
+   .c[-1][2]..c[0] = alpha*(.c[-1][2]..vertex), and
+   c[2]..c[1] = alpha*(c[2]..vertex).
+   Beta is so that (.beta[i])[.vertex[i],.vertex[i+1]] = .c[i][2].
+*/
+
+struct privcurve_s {
+	int n;												/* number of segments */
+	int *tag;											/* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */
+	  dpoint_t(*c)[3];						/* c[n][i]: control points. 
+																   c[n][0] is unused for tag[n]=POTRACE_CORNER */
+	/* the remainder of this structure is special to privcurve, and is
+	   used in EPS debug output and special EPS "short coding". These
+	   fields are valid only if "alphacurve" is set. */
+	int alphacurve;								/* have the following fields been initialized? */
+	dpoint_t *vertex;							/* for POTRACE_CORNER, this equals c[1] */
+	double *alpha;								/* only for POTRACE_CURVETO */
+	double *alpha0;								/* "uncropped" alpha parameter - for debug output only */
+	double *beta;
+};
+typedef struct privcurve_s privcurve_t;
+
+struct sums_s {
+	double x;
+	double y;
+	double x2;
+	double xy;
+	double y2;
+};
+typedef struct sums_s sums_t;
+
+/* the path structure is filled in with information about a given path
+   as it is accumulated and passed through the different stages of the
+   Potrace algorithm. Backends only need to read the fcurve and fm
+   fields of this data structure, but debugging backends may read
+   other fields. */
+struct potrace_privpath_s {
+	int len;
+	point_t *pt;									/* pt[len]: path as extracted from bitmap */
+	int *lon;											/* lon[len]: (i,lon[i]) = longest straight line from i */
+
+	int x0, y0;										/* origin for sums */
+	sums_t *sums;									/* sums[len+1]: cache for fast summing */
+
+	int m;												/* length of optimal polygon */
+	int *po;											/* po[m]: optimal polygon */
+
+	privcurve_t curve;						/* curve[m]: array of curve elements */
+	privcurve_t ocurve;						/* ocurve[om]: array of curve elements */
+	privcurve_t *fcurve;					/* final curve: this points to either curve or
+																   ocurve. Do not free this separately. */
+};
+typedef struct potrace_privpath_s potrace_privpath_t;
+
+/* shorter names */
+typedef potrace_privpath_t privpath_t;
+typedef potrace_path_t path_t;
+
+path_t *path_new(void);
+void path_free(path_t * p);
+void pathlist_free(path_t * plist);
+int privcurve_init(privcurve_t * curve, int n);
+void privcurve_to_curve(privcurve_t * pc, potrace_curve_t * c);
+
+#endif /* CURVE_H */
diff --git a/src_plugins/export_gcode/decompose.c b/src_plugins/export_gcode/decompose.c
new file mode 100644
index 0000000..f63d5b3
--- /dev/null
+++ b/src_plugins/export_gcode/decompose.c
@@ -0,0 +1,525 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: decompose.c 146 2007-04-09 00:43:46Z selinger $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "potracelib.h"
+#include "curve.h"
+#include "lists.h"
+#include "auxiliary.h"
+#include "bitmap.h"
+#include "decompose.h"
+/*#include "progress.h"*/
+
+/* ---------------------------------------------------------------------- */
+/* auxiliary bitmap manipulations */
+
+/* set the excess padding to 0 */
+static void bm_clearexcess(potrace_bitmap_t * bm)
+{
+	potrace_word mask;
+	int y;
+
+	if (bm->w % BM_WORDBITS != 0) {
+		mask = BM_ALLBITS << (BM_WORDBITS - (bm->w % BM_WORDBITS));
+		for (y = 0; y < bm->h; y++) {
+			*bm_index(bm, bm->w, y) &= mask;
+		}
+	}
+}
+
+struct bbox_s {
+	int x0, x1, y0, y1;						/* bounding box */
+};
+typedef struct bbox_s bbox_t;
+
+/* clear the bm, assuming the bounding box is set correctly (faster
+   than clearing the whole bitmap) */
+static void clear_bm_with_bbox(potrace_bitmap_t * bm, bbox_t * bbox)
+{
+	int imin = (bbox->x0 / BM_WORDBITS);
+	int imax = ((bbox->x1 + BM_WORDBITS - 1) / BM_WORDBITS);
+	int i, y;
+
+	for (y = bbox->y0; y < bbox->y1; y++) {
+		for (i = imin; i < imax; i++) {
+			bm_scanline(bm, y)[i] = 0;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+/* auxiliary functions */
+
+/* deterministically and efficiently hash (x,y) into a pseudo-random bit */
+static inline int detrand(int x, int y)
+{
+	unsigned int z;
+	static const unsigned char t[256] = {
+		/* non-linear sequence: constant term of inverse in GF(8), 
+		   mod x^8+x^4+x^3+x+1 */
+		0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1,
+		0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0,
+		0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+		1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+		0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0,
+		0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
+		0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0,
+		0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
+		1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0,
+		0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1,
+		1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+	};
+
+	/* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible
+	   5-bit sequence */
+	z = ((0x04b3e375 * x) ^ y) * 0x05a8ef93;
+	z = t[z & 0xff] ^ t[(z >> 8) & 0xff] ^ t[(z >> 16) & 0xff] ^ t[(z >> 24) & 0xff];
+	return z & 1;
+}
+
+/* return the "majority" value of bitmap bm at intersection (x,y). We
+   assume that the bitmap is balanced at "radius" 1.  */
+static int majority(potrace_bitmap_t * bm, int x, int y)
+{
+	int i, a, ct;
+
+	for (i = 2; i < 5; i++) {			/* check at "radius" i */
+		ct = 0;
+		for (a = -i + 1; a <= i - 1; a++) {
+			ct += BM_GET(bm, x + a, y + i - 1) ? 1 : -1;
+			ct += BM_GET(bm, x + i - 1, y + a - 1) ? 1 : -1;
+			ct += BM_GET(bm, x + a - 1, y - i) ? 1 : -1;
+			ct += BM_GET(bm, x - i, y + a) ? 1 : -1;
+		}
+		if (ct > 0) {
+			return 1;
+		}
+		else if (ct < 0) {
+			return 0;
+		}
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* decompose image into paths */
+
+/* efficiently invert bits [x,infty) and [xa,infty) in line y. Here xa
+   must be a multiple of BM_WORDBITS. */
+static void xor_to_ref(potrace_bitmap_t * bm, int x, int y, int xa)
+{
+	int xhi = x & -BM_WORDBITS;
+	int xlo = x & (BM_WORDBITS - 1);	/* = x % BM_WORDBITS */
+	int i;
+
+	if (xhi < xa) {
+		for (i = xhi; i < xa; i += BM_WORDBITS) {
+			*bm_index(bm, i, y) ^= BM_ALLBITS;
+		}
+	}
+	else {
+		for (i = xa; i < xhi; i += BM_WORDBITS) {
+			*bm_index(bm, i, y) ^= BM_ALLBITS;
+		}
+	}
+	/* note: the following "if" is needed because x86 treats a<<b as
+	   a<<(b&31). I spent hours looking for this bug. */
+	if (xlo) {
+		*bm_index(bm, xhi, y) ^= (BM_ALLBITS << (BM_WORDBITS - xlo));
+	}
+}
+
+/* a path is represented as an array of points, which are thought to
+   lie on the corners of pixels (not on their centers). The path point
+   (x,y) is the lower left corner of the pixel (x,y). Paths are
+   represented by the len/pt components of a path_t object (which
+   also stores other information about the path) */
+
+/* xor the given pixmap with the interior of the given path. Note: the
+   path must be within the dimensions of the pixmap. */
+static void xor_path(potrace_bitmap_t * bm, path_t * p)
+{
+	int xa, x, y, k, y1;
+
+	if (p->priv->len <= 0) {			/* a path of length 0 is silly, but legal */
+		return;
+	}
+
+	y1 = p->priv->pt[p->priv->len - 1].y;
+
+	xa = p->priv->pt[0].x & -BM_WORDBITS;
+	for (k = 0; k < p->priv->len; k++) {
+		x = p->priv->pt[k].x;
+		y = p->priv->pt[k].y;
+
+		if (y != y1) {
+			/* efficiently invert the rectangle [x,xa] x [y,y1] */
+			xor_to_ref(bm, x, min(y, y1), xa);
+			y1 = y;
+		}
+	}
+}
+
+/* Find the bounding box of a given path. Path is assumed to be of
+   non-zero length. */
+static void setbbox_path(bbox_t * bbox, path_t * p)
+{
+	int x, y;
+	int k;
+
+	bbox->y0 = INT_MAX;
+	bbox->y1 = 0;
+	bbox->x0 = INT_MAX;
+	bbox->x1 = 0;
+
+	for (k = 0; k < p->priv->len; k++) {
+		x = p->priv->pt[k].x;
+		y = p->priv->pt[k].y;
+
+		if (x < bbox->x0) {
+			bbox->x0 = x;
+		}
+		if (x > bbox->x1) {
+			bbox->x1 = x;
+		}
+		if (y < bbox->y0) {
+			bbox->y0 = y;
+		}
+		if (y > bbox->y1) {
+			bbox->y1 = y;
+		}
+	}
+}
+
+/* compute a path in the given pixmap, separating black from white.
+   Start path at the point (x0,x1), which must be an upper left corner
+   of the path. Also compute the area enclosed by the path. Return a
+   new path_t object, or NULL on error (note that a legitimate path
+   cannot have length 0). Sign is required for correct interpretation
+   of turnpolicies. */
+static path_t *findpath(potrace_bitmap_t * bm, int x0, int y0, int sign, int turnpolicy)
+{
+	int x, y, dirx, diry, len, size, area;
+	int c, d, tmp;
+	point_t *pt, *pt1;
+	path_t *p = NULL;
+
+	x = x0;
+	y = y0;
+	dirx = 0;
+	diry = -1;
+
+	len = size = 0;
+	pt = NULL;
+	area = 0;
+
+	while (1) {
+		/* add point to path */
+		if (len >= size) {
+			size += 100;
+			size = (int) (1.3 * size);
+			pt1 = (point_t *) realloc(pt, size * sizeof(point_t));
+			if (!pt1) {
+				goto error;
+			}
+			pt = pt1;
+		}
+		pt[len].x = x;
+		pt[len].y = y;
+		len++;
+
+		/* move to next point */
+		x += dirx;
+		y += diry;
+		area += x * diry;
+
+		/* path complete? */
+		if (x == x0 && y == y0) {
+			break;
+		}
+
+		/* determine next direction */
+		c = BM_GET(bm, x + (dirx + diry - 1) / 2, y + (diry - dirx - 1) / 2);
+		d = BM_GET(bm, x + (dirx - diry - 1) / 2, y + (diry + dirx - 1) / 2);
+
+		if (c && !d) {							/* ambiguous turn */
+			if (turnpolicy == POTRACE_TURNPOLICY_RIGHT || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+')
+					|| (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-')
+					|| (turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand(x, y))
+					|| (turnpolicy == POTRACE_TURNPOLICY_MAJORITY && majority(bm, x, y))
+					|| (turnpolicy == POTRACE_TURNPOLICY_MINORITY && !majority(bm, x, y))) {
+				tmp = dirx;							/* right turn */
+				dirx = diry;
+				diry = -tmp;
+			}
+			else {
+				tmp = dirx;							/* left turn */
+				dirx = -diry;
+				diry = tmp;
+			}
+		}
+		else if (c) {								/* right turn */
+			tmp = dirx;
+			dirx = diry;
+			diry = -tmp;
+		}
+		else if (!d) {							/* left turn */
+			tmp = dirx;
+			dirx = -diry;
+			diry = tmp;
+		}
+	}															/* while this path */
+
+	/* allocate new path object */
+	p = path_new();
+	if (!p) {
+		goto error;
+	}
+
+	p->priv->pt = pt;
+	p->priv->len = len;
+	p->area = area;
+	p->sign = sign;
+
+	return p;
+
+error:
+	free(pt);
+	return NULL;
+}
+
+/* Give a tree structure to the given path list, based on "insideness"
+   testing. I.e., path A is considered "below" path B if it is inside
+   path B. The input pathlist is assumed to be ordered so that "outer"
+   paths occur before "inner" paths. The tree structure is stored in
+   the "childlist" and "sibling" components of the path_t
+   structure. The linked list structure is also changed so that
+   negative path components are listed immediately after their
+   positive parent.  Note: some backends may ignore the tree
+   structure, others may use it e.g. to group path components. We
+   assume that in the input, point 0 of each path is an "upper left"
+   corner of the path, as returned by bm_to_pathlist. This makes it
+   easy to find an "interior" point. The bm argument should be a
+   bitmap of the correct size (large enough to hold all the paths),
+   and will be used as scratch space. Return 0 on success or -1 on
+   error with errno set. */
+
+static void pathlist_to_tree(path_t * plist, potrace_bitmap_t * bm)
+{
+	path_t *p, *p1;
+	path_t *heap, *heap1;
+	path_t *cur;
+	path_t *head;
+	path_t **hook, **hook_in, **hook_out;	/* for fast appending to linked list */
+	bbox_t bbox;
+
+	bm_clear(bm, 0);
+
+	/* save original "next" pointers */
+	list_forall(p, plist) {
+		p->sibling = p->next;
+		p->childlist = NULL;
+	}
+
+	heap = plist;
+
+	/* the heap holds a list of lists of paths. Use "childlist" field
+	   for outer list, "next" field for inner list. Each of the sublists
+	   is to be turned into a tree. This code is messy, but it is
+	   actually fast. Each path is rendered exactly once. We use the
+	   heap to get a tail recursive algorithm: the heap holds a list of
+	   pathlists which still need to be transformed. */
+
+	while (heap) {
+		/* unlink first sublist */
+		cur = heap;
+		heap = heap->childlist;
+		cur->childlist = NULL;
+
+		/* unlink first path */
+		head = cur;
+		cur = cur->next;
+		head->next = NULL;
+
+		/* render path */
+		xor_path(bm, head);
+		setbbox_path(&bbox, head);
+
+		/* now do insideness test for each element of cur; append it to
+		   head->childlist if it's inside head, else append it to
+		   head->next. */
+		hook_in = &head->childlist;
+		hook_out = &head->next;
+		list_forall_unlink(p, cur) {
+			if (p->priv->pt[0].y <= bbox.y0) {
+				list_insert_beforehook(p, hook_out);
+				/* append the remainder of the list to hook_out */
+				*hook_out = cur;
+				break;
+			}
+			if (BM_GET(bm, p->priv->pt[0].x, p->priv->pt[0].y - 1)) {
+				list_insert_beforehook(p, hook_in);
+			}
+			else {
+				list_insert_beforehook(p, hook_out);
+			}
+		}
+
+		/* clear bm */
+		clear_bm_with_bbox(bm, &bbox);
+
+		/* now schedule head->childlist and head->next for further
+		   processing */
+		if (head->next) {
+			head->next->childlist = heap;
+			heap = head->next;
+		}
+		if (head->childlist) {
+			head->childlist->childlist = heap;
+			heap = head->childlist;
+		}
+	}
+
+	/* copy sibling structure from "next" to "sibling" component */
+	p = plist;
+	while (p) {
+		p1 = p->sibling;
+		p->sibling = p->next;
+		p = p1;
+	}
+
+	/* reconstruct a new linked list ("next") structure from tree
+	   ("childlist", "sibling") structure. This code is slightly messy,
+	   because we use a heap to make it tail recursive: the heap
+	   contains a list of childlists which still need to be
+	   processed. */
+	heap = plist;
+	if (heap) {
+		heap->next = NULL;					/* heap is a linked list of childlists */
+	}
+	plist = NULL;
+	hook = &plist;
+	while (heap) {
+		heap1 = heap->next;
+		for (p = heap; p; p = p->sibling) {
+			/* p is a positive path */
+			/* append to linked list */
+			list_insert_beforehook(p, hook);
+
+			/* go through its children */
+			for (p1 = p->childlist; p1; p1 = p1->sibling) {
+				/* append to linked list */
+				list_insert_beforehook(p1, hook);
+				/* append its childlist to heap, if non-empty */
+				if (p1->childlist) {
+					list_append(path_t, heap1, p1->childlist);
+				}
+			}
+		}
+		heap = heap1;
+	}
+
+	return;
+}
+
+/* find the next set pixel in a row <= y. Pixels are searched first
+   left-to-right, then top-down. In other words, (x,y)<(x',y') if y>y'
+   or y=y' and x<x'. If found, return 0 and store pixel in
+   (*xp,*yp). Else return 1. Note that this function assumes that
+   excess bytes have been cleared with bm_clearexcess. */
+static int findnext(potrace_bitmap_t * bm, int *xp, int *yp)
+{
+	int x;
+	int y;
+
+	for (y = *yp; y >= 0; y--) {
+		for (x = 0; x < bm->w; x += BM_WORDBITS) {
+			if (*bm_index(bm, x, y)) {
+				while (!BM_GET(bm, x, y)) {
+					x++;
+				}
+				/* found */
+				*xp = x;
+				*yp = y;
+				return 0;
+			}
+		}
+	}
+	/* not found */
+	return 1;
+}
+
+/* Decompose the given bitmap into paths. Returns a linked list of
+   path_t objects with the fields len, pt, area, sign filled
+   in. Returns 0 on success with plistp set, or -1 on error with errno
+   set. */
+
+int bm_to_pathlist(const potrace_bitmap_t * bm, path_t ** plistp, const potrace_param_t * param)
+{
+	int x;
+	int y;
+	path_t *p;
+	path_t *plist = NULL;					/* linked list of path objects */
+	path_t **hook = &plist;				/* used to speed up appending to linked list */
+	potrace_bitmap_t *bm1 = NULL;
+	int sign;
+
+	bm1 = bm_dup(bm);
+	if (!bm1) {
+		goto error;
+	}
+
+	/* be sure the byte padding on the right is set to 0, as the fast
+	   pixel search below relies on it */
+	bm_clearexcess(bm1);
+
+	/* iterate through components */
+	y = bm1->h - 1;
+	while (findnext(bm1, &x, &y) == 0) {
+		/* calculate the sign by looking at the original */
+		sign = BM_GET(bm, x, y) ? '+' : '-';
+
+		/* calculate the path */
+		p = findpath(bm1, x, y + 1, sign, param->turnpolicy);
+		if (p == NULL) {
+			goto error;
+		}
+
+		/* update buffered image */
+		xor_path(bm1, p);
+
+		/* if it's a turd, eliminate it, else append it to the list */
+		if (p->area <= param->turdsize) {
+			path_free(p);
+		}
+		else {
+			list_insert_beforehook(p, hook);
+		}
+
+		if (bm1->h > 0) {						/* to be sure */
+			/*progress_update(1-y/(double)bm1->h, progress); */
+		}
+	}
+
+	pathlist_to_tree(plist, bm1);
+	bm_free(bm1);
+	*plistp = plist;
+
+/*  progress_update(1.0, progress);*/
+
+	return 0;
+
+error:
+	bm_free(bm1);
+	list_forall_unlink(p, plist) {
+		path_free(p);
+	}
+	return -1;
+}
diff --git a/src_plugins/export_gcode/decompose.h b/src_plugins/export_gcode/decompose.h
new file mode 100644
index 0000000..eb6ff17
--- /dev/null
+++ b/src_plugins/export_gcode/decompose.h
@@ -0,0 +1,15 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: decompose.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef DECOMPOSE_H
+#define DECOMPOSE_H
+
+#include "potracelib.h"
+/*#include "progress.h"*/
+
+int bm_to_pathlist(const potrace_bitmap_t * bm, path_t ** plistp, const potrace_param_t * param);
+
+#endif /* DECOMPOSE_H */
diff --git a/src_plugins/export_gcode/gcode.c b/src_plugins/export_gcode/gcode.c
new file mode 100644
index 0000000..95ca52c
--- /dev/null
+++ b/src_plugins/export_gcode/gcode.c
@@ -0,0 +1,918 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  GCODE export HID
+ *  Copyright (C) 2010 Alberto Maccioni
+ *  this code is based on the NELMA export HID, the PNG export HID,
+ *  and potrace, a tracing program by Peter Selinger
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This HID exports a PCB layout into: 
+ * one layer mask file (PNG format) per copper layer,
+ * one G-CODE CNC drill file.
+ * one G-CODE CNC file per copper layer.
+ * The latter is used by a CNC milling machine to mill the pcb.  
+ */
+
+#include "config.h"
+#include "conf_core.h"
+#include "plugins.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <time.h>
+
+#include "global.h"
+#include "error.h"							/* Message(PCB_MSG_DEFAULT, ) */
+#include "data.h"
+#include "misc.h"
+#include "rats.h"
+#include "hid_helper.h"
+#include "layer.h"
+#include "compat_misc.h"
+
+#include "hid.h"
+#include <gd.h>
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "gcode.h"
+#include "bitmap.h"
+#include "curve.h"
+#include "potracelib.h"
+#include "trace.h"
+#include "decompose.h"
+#include "pcb-printf.h"
+
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_flags.h"
+#include "hid_color.h"
+
+
+const char *gcode_cookie = "gcode HID";
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", func); abort()
+struct color_struct {
+	/* the descriptor used by the gd library */
+	int c;
+
+	/* so I can figure out what rgb value c refers to */
+	unsigned int r, g, b;
+};
+
+struct hid_gc_struct {
+	HID *me_pointer;
+	EndCapStyle cap;
+	int width;
+	unsigned char r, g, b;
+	int erase;
+	int faded;
+	struct color_struct *color;
+	gdImagePtr brush;
+};
+
+static struct color_struct *black = NULL, *white = NULL;
+static int linewidth = -1;
+static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1);
+static int lastcolor = -1;
+
+/* gd image and file for PNG export */
+static gdImagePtr gcode_im = NULL;
+static FILE *gcode_f = NULL, *gcode_f2 = NULL;
+
+static int is_mask;
+static int is_drill;
+static int is_solder;
+
+/*
+ * Which groups of layers to export into PNG layer masks. 1 means export, 0
+ * means do not export.
+ */
+static int gcode_export_group[MAX_LAYER];
+
+/* Group that is currently exported. */
+static int gcode_cur_group;
+
+/* Filename prefix that will be used when saving files. */
+static const char *gcode_basename = NULL;
+
+/* Horizontal DPI (grid points per inch) */
+static int gcode_dpi = -1;
+
+static double gcode_cutdepth = 0;	/* milling depth (inch) */
+static double gcode_drilldepth = 0;	/* drilling depth (inch) */
+static double gcode_safeZ = 100;	/* safe Z (inch) */
+static double gcode_toolradius = 0;	/* tool radius(inch) */
+static int save_drill = 0;
+static int n_drill = 0;
+static int nmax_drill = 0;
+struct drill_struct {
+	double x;
+	double y;
+};
+
+static struct drill_struct *drill = 0;
+
+static const char *units[] = {
+	"mm",
+	"mil",
+	"um",
+	"inch",
+	NULL
+};
+
+HID_Attribute gcode_attribute_list[] = {
+	/* other HIDs expect this to be first.  */
+	{"basename", "File name prefix",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_basename 0
+
+	{"dpi", "Resolution of intermediate image (pixels/inch)",
+	 HID_Integer, 0, 2000, {600, 0, 0}, 0, 0},
+#define HA_dpi 1
+
+	{"mill-depth", "Milling depth",
+	 HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0},
+#define HA_cutdepth 2
+
+	{"safe-Z", "Safe Z for traverse move",
+	 HID_Real, -1000, 10000, {0, 0, 2}, 0, 0},
+#define HA_safeZ 3
+
+	{"tool-radius", "Milling tool radius compensation",
+	 HID_Real, 0, 10000, {0, 0, 0.1}, 0, 0},
+#define HA_toolradius 4
+
+	{"drill-depth", "Drilling depth",
+	 HID_Real, -10000, 10000, {0, 0, -2}, 0, 0},
+#define HA_drilldepth 5
+
+	{"measurement-unit", "Measurement unit",
+	 HID_Unit, 0, 0, {-1, 0, 0}, units, 0},
+#define HA_unit 6
+
+};
+
+#define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0]))
+
+REGISTER_ATTRIBUTES(gcode_attribute_list, gcode_cookie)
+		 static HID_Attr_Val gcode_values[NUM_OPTIONS];
+
+/* *** Utility funcions **************************************************** */
+
+/* convert from default PCB units to gcode units */
+		 static int pcb_to_gcode(int pcb)
+{
+	return pcb_round(PCB_COORD_TO_INCH(pcb) * gcode_dpi);
+}
+
+static char *gcode_get_png_name(const char *basename, const char *suffix)
+{
+	return pcb_strdup_printf("%s.%s.png", basename, suffix);
+}
+
+/* Sorts drills in order of distance from the origin */
+struct drill_struct *sort_drill(struct drill_struct *drill, int n_drill)
+{
+	int i, j, imin;
+	double dmin, d;
+	struct drill_struct p = { 0, 0 };
+	struct drill_struct *temp = (struct drill_struct *) malloc(n_drill * sizeof(struct drill_struct));
+	for (j = 0; j < n_drill; j++) {
+		dmin = 1e20;
+		imin = 0;
+		for (i = 0; i < n_drill - j; i++) {
+			d = (drill[i].x - p.x) * (drill[i].x - p.x) + (drill[i].y - p.y) * (drill[i].y - p.y);
+			if (d < dmin) {
+				imin = i;
+				dmin = d;
+			}
+		}
+		/* printf("j=%d imin=%d dmin=%f p=(%f,%f)\n",j,imin,dmin,p.x,p.y); */
+		temp[j] = drill[imin];
+		drill[imin] = drill[n_drill - j - 1];
+		p = temp[j];
+	}
+	free(drill);
+	return temp;
+}
+
+/* *** Main export callback ************************************************ */
+
+static void gcode_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(gcode_attribute_list, sizeof(gcode_attribute_list) / sizeof(gcode_attribute_list[0]), gcode_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static HID_Attribute *gcode_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+	static int last_unit_value = -1;
+
+	if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value) {
+		if (conf_core.editor.grid_unit)
+			gcode_attribute_list[HA_unit].default_val.int_value = conf_core.editor.grid_unit->index;
+		else
+			gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct("mil")->index;
+		last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value;
+	}
+
+	if (PCB) {
+		derive_default_filename(PCB->Filename, &gcode_attribute_list[HA_basename], ".gcode", &last_made_filename);
+	}
+	if (n) {
+		*n = NUM_OPTIONS;
+	}
+	return gcode_attribute_list;
+}
+
+/* Populates gcode_export_group array */
+void gcode_choose_groups()
+{
+	int n, m;
+	LayerType *layer;
+
+	/* Set entire array to 0 (don't export any layer groups by default */
+	memset(gcode_export_group, 0, sizeof(gcode_export_group));
+
+	for (n = 0; n < max_copper_layer; n++) {
+		layer = &PCB->Data->Layer[n];
+
+		if (!LAYER_IS_EMPTY(layer)) {
+			/* layer isn't empty */
+
+			/*
+			 * is this check necessary? It seems that special
+			 * layers have negative indexes?
+			 */
+
+			if (SL_TYPE(n) == 0) {
+				/* layer is a copper layer */
+				m = GetLayerGroupNumberByNumber(n);
+
+				/* the export layer */
+				gcode_export_group[m] = 1;
+			}
+		}
+	}
+}
+
+static void gcode_alloc_colors()
+{
+	/*
+	 * Allocate white and black -- the first color allocated becomes the
+	 * background color
+	 */
+
+	white = (struct color_struct *) malloc(sizeof(*white));
+	white->r = white->g = white->b = 255;
+	white->c = gdImageColorAllocate(gcode_im, white->r, white->g, white->b);
+
+	black = (struct color_struct *) malloc(sizeof(*black));
+	black->r = black->g = black->b = 0;
+	black->c = gdImageColorAllocate(gcode_im, black->r, black->g, black->b);
+}
+
+static void gcode_start_png(const char *basename, const char *suffix)
+{
+	int h, w;
+	char *buf;
+
+	buf = gcode_get_png_name(basename, suffix);
+
+	h = pcb_to_gcode(PCB->MaxHeight);
+	w = pcb_to_gcode(PCB->MaxWidth);
+
+	/* Nelma only works with true color images */
+	gcode_im = gdImageCreate(w, h);
+	gcode_f = fopen(buf, "wb");
+
+	gcode_alloc_colors();
+
+	free(buf);
+}
+
+static void gcode_finish_png()
+{
+#ifdef HAVE_GDIMAGEPNG
+	gdImagePng(gcode_im, gcode_f);
+#else
+	Message(PCB_MSG_DEFAULT, "GCODE: PNG not supported by gd. Can't write layer mask.\n");
+#endif
+	gdImageDestroy(gcode_im);
+	fclose(gcode_f);
+
+	free(white);
+	free(black);
+
+	gcode_im = NULL;
+	gcode_f = NULL;
+}
+
+void gcode_start_png_export()
+{
+	BoxType region;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	linewidth = -1;
+	lastbrush = (gdImagePtr) ((void *) -1);
+	lastcolor = -1;
+
+	hid_expose_callback(&gcode_hid, &region, 0);
+}
+
+static void gcode_do_export(HID_Attr_Val * options)
+{
+	int save_ons[MAX_LAYER + 2];
+	int i, idx;
+	time_t t;
+	const Unit *unit;
+	double scale = 0, d = 0;
+	int r, c, v, p, metric;
+	char *filename;
+	path_t *plist = NULL;
+	potrace_bitmap_t *bm = NULL;
+	potrace_param_t param_default = {
+		2,													/* turnsize */
+		POTRACE_TURNPOLICY_MINORITY,	/* turnpolicy */
+		1.0,												/* alphamax */
+		1,													/* opticurve */
+		0.2,												/* opttolerance */
+		{
+		 NULL,											/* callback function */
+		 NULL,											/* callback data */
+		 0.0, 1.0,									/* progress range  */
+		 0.0,												/* granularity  */
+		 },
+	};
+
+	if (!options) {
+		gcode_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++) {
+			gcode_values[i] = gcode_attribute_list[i].default_val;
+		}
+		options = gcode_values;
+	}
+	gcode_basename = options[HA_basename].str_value;
+	if (!gcode_basename) {
+		gcode_basename = "pcb-out";
+	}
+	gcode_dpi = options[HA_dpi].int_value;
+	if (gcode_dpi < 0) {
+		fprintf(stderr, "ERROR:  dpi may not be < 0\n");
+		return;
+	}
+	unit = &(get_unit_list()[options[HA_unit].int_value]);
+	metric = (unit->family == METRIC);
+	scale = metric ? 1.0 / coord_to_unit(unit, PCB_MM_TO_COORD(1.0))
+		: 1.0 / coord_to_unit(unit, PCB_INCH_TO_COORD(1.0));
+
+	gcode_cutdepth = options[HA_cutdepth].real_value * scale;
+	gcode_drilldepth = options[HA_drilldepth].real_value * scale;
+	gcode_safeZ = options[HA_safeZ].real_value * scale;
+	gcode_toolradius = metric ? PCB_MM_TO_COORD(options[HA_toolradius].real_value * scale)
+		: PCB_INCH_TO_COORD(options[HA_toolradius].real_value * scale);
+	gcode_choose_groups();
+
+	for (i = 0; i < MAX_LAYER; i++) {
+		if (gcode_export_group[i]) {
+
+			gcode_cur_group = i;
+
+			/* magic */
+			idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i;
+			printf("idx=%d %s\n", idx, layer_type_to_file_name(idx, FNS_fixed));
+			is_solder = (GetLayerGroupNumberByNumber(idx) == GetLayerGroupNumberByNumber(solder_silk_layer)) ? 1 : 0;
+			save_drill = is_solder;		/* save drills for one layer only */
+			gcode_start_png(gcode_basename, layer_type_to_file_name(idx, FNS_fixed));
+			hid_save_and_show_layer_ons(save_ons);
+			gcode_start_png_export();
+			hid_restore_layer_ons(save_ons);
+
+/* ***************** gcode conversion *************************** */
+/* potrace uses a different kind of bitmap; for simplicity gcode_im is copied to this format */
+			bm = bm_new(gdImageSX(gcode_im), gdImageSY(gcode_im));
+			filename = (char *) malloc(MAXPATHLEN);
+			plist = NULL;
+			if (is_solder) {					/* only for back layer */
+				gdImagePtr temp_im = gdImageCreate(gdImageSX(gcode_im), gdImageSY(gcode_im));
+				gdImageCopy(temp_im, gcode_im, 0, 0, 0, 0, gdImageSX(gcode_im), gdImageSY(gcode_im));
+				for (r = 0; r < gdImageSX(gcode_im); r++) {
+					for (c = 0; c < gdImageSY(gcode_im); c++) {
+						gdImageSetPixel(gcode_im, r, c, gdImageGetPixel(temp_im, gdImageSX(gcode_im) - 1 - r, c));
+					}
+				}
+				gdImageDestroy(temp_im);
+			}
+			sprintf(filename, "%s.%s.cnc", gcode_basename, layer_type_to_file_name(idx, FNS_fixed));
+			for (r = 0; r < gdImageSX(gcode_im); r++) {
+				for (c = 0; c < gdImageSY(gcode_im); c++) {
+					v = gdImageGetPixel(gcode_im, r, gdImageSY(gcode_im) - 1 - c);
+					p = (gcode_im->red[v] || gcode_im->green[v]
+							 || gcode_im->blue[v]) ? 0 : 0xFFFFFF;
+					BM_PUT(bm, r, c, p);
+				}
+			}
+			gcode_f2 = fopen(filename, "wb");
+			if (!gcode_f2) {
+				perror(filename);
+				return;
+			}
+			fprintf(gcode_f2, "(Created by G-code exporter)\n");
+			t = time(NULL);
+			sprintf(filename, "%s", ctime(&t));
+			filename[strlen(filename) - 1] = 0;
+			fprintf(gcode_f2, "( %s )\n", filename);
+			fprintf(gcode_f2, "(%d dpi)\n", gcode_dpi);
+			fprintf(gcode_f2, "(Unit: %s)\n", metric ? "mm" : "inch");
+			if (metric)
+				pcb_fprintf(gcode_f2, "(Board size: %.2mmx%.2mm mm)", PCB->MaxWidth, PCB->MaxHeight);
+			else
+				pcb_fprintf(gcode_f2, "(Board size: %.2mix%.2mi inches)", PCB->MaxWidth, PCB->MaxHeight);
+			fprintf(gcode_f2, "#100=%f  (safe Z)\n", gcode_safeZ);
+			fprintf(gcode_f2, "#101=%f  (cutting depth)\n", gcode_cutdepth);
+			fprintf(gcode_f2, "(---------------------------------)\n");
+			fprintf(gcode_f2, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%d\n", metric ? 21 : 20, metric ? 25 : 1);
+			fprintf(gcode_f2, "G0 Z#100\n");
+			/* extract contour points from image */
+			r = bm_to_pathlist(bm, &plist, &param_default);
+			if (r) {
+				fprintf(stderr, "ERROR: pathlist function failed\n");
+				return;
+			}
+			/* generate best polygon and write vertices in g-code format */
+			d = process_path(plist, &param_default, bm, gcode_f2, metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi);
+			if (d < 0) {
+				fprintf(stderr, "ERROR: path process function failed\n");
+				return;
+			}
+			if (metric)
+				fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", d, d * 1 / 25.4);
+			else
+				fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", 25.4 * d, d);
+			fprintf(gcode_f2, "M5 M9 M2\n");
+			pathlist_free(plist);
+			bm_free(bm);
+			fclose(gcode_f2);
+			if (save_drill) {
+				d = 0;
+				drill = sort_drill(drill, n_drill);
+				sprintf(filename, "%s.drill.cnc", gcode_basename);
+				gcode_f2 = fopen(filename, "wb");
+				if (!gcode_f2) {
+					perror(filename);
+					return;
+				}
+				fprintf(gcode_f2, "(Created by G-code exporter)\n");
+				fprintf(gcode_f2, "(drill file: %d drills)\n", n_drill);
+				sprintf(filename, "%s", ctime(&t));
+				filename[strlen(filename) - 1] = 0;
+				fprintf(gcode_f2, "( %s )\n", filename);
+				fprintf(gcode_f2, "(Unit: %s)\n", metric ? "mm" : "inch");
+				if (metric)
+					pcb_fprintf(gcode_f2, "(Board size: %.2mmx%.2mm mm)", PCB->MaxWidth, PCB->MaxHeight);
+				else
+					pcb_fprintf(gcode_f2, "(Board size: %.2mix%.2mi inches)", PCB->MaxWidth, PCB->MaxHeight);
+				fprintf(gcode_f2, "#100=%f  (safe Z)\n", gcode_safeZ);
+				fprintf(gcode_f2, "#101=%f  (drill depth)\n", gcode_drilldepth);
+				fprintf(gcode_f2, "(---------------------------------)\n");
+				fprintf(gcode_f2, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%d\n", metric ? 21 : 20, metric ? 25 : 1);
+/*                              fprintf(gcode_f2,"G0 Z#100\n"); */
+				for (r = 0; r < n_drill; r++) {
+/*                                      if(metric) fprintf(gcode_f2,"G0 X%f Y%f\n",drill[r].x*25.4,drill[r].y*25.4); */
+/*                                      else fprintf(gcode_f2,"G0 X%f Y%f\n",drill[r].x,drill[r].y); */
+					if (metric)
+						fprintf(gcode_f2, "G81 X%f Y%f Z#101 R#100\n", drill[r].x * 25.4, drill[r].y * 25.4);
+					else
+						fprintf(gcode_f2, "G81 X%f Y%f Z#101 R#100\n", drill[r].x, drill[r].y);
+/*                                      fprintf(gcode_f2,"G1 Z#101\n"); */
+/*                                      fprintf(gcode_f2,"G0 Z#100\n"); */
+					if (r > 0)
+						d +=
+							sqrt((drill[r].x - drill[r - 1].x) * (drill[r].x -
+																										drill[r - 1].x) +
+									 (drill[r].y - drill[r - 1].y) * (drill[r].y - drill[r - 1].y));
+				}
+				fprintf(gcode_f2, "M5 M9 M2\n");
+				fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", 25.4 * d, d);
+				fclose(gcode_f2);
+				free(drill);
+				drill = NULL;
+				n_drill = nmax_drill = 0;
+			}
+			free(filename);
+
+/* ******************* end gcode conversion **************************** */
+			gcode_finish_png();
+		}
+	}
+}
+
+/* *** PNG export (slightly modified code from PNG export HID) ************* */
+
+static int gcode_set_layer(const char *name, int group, int empty)
+{
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+
+	if (name == 0) {
+		name = PCB->Data->Layer[idx].Name;
+	}
+	if (strcmp(name, "invisible") == 0) {
+		return 0;
+	}
+	is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	is_mask = (SL_TYPE(idx) == SL_MASK);
+
+	if (is_mask) {
+		/* Don't print masks */
+		return 0;
+	}
+	if (is_drill) {
+		/*
+		 * Print 'holes', so that we can fill gaps in the copper
+		 * layer
+		 */
+		return 1;
+	}
+	if (group == gcode_cur_group) {
+		return 1;
+	}
+	return 0;
+}
+
+static hidGC gcode_make_gc(void)
+{
+	hidGC rv = (hidGC) malloc(sizeof(struct hid_gc_struct));
+	rv->me_pointer = &gcode_hid;
+	rv->cap = Trace_Cap;
+	rv->width = 1;
+	rv->color = (struct color_struct *) malloc(sizeof(*rv->color));
+	rv->color->r = rv->color->g = rv->color->b = 0;
+	rv->color->c = 0;
+	return rv;
+}
+
+static void gcode_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void gcode_use_mask(int use_it)
+{
+	/* does nothing */
+}
+
+static void gcode_set_color(hidGC gc, const char *name)
+{
+	if (gcode_im == NULL) {
+		return;
+	}
+	if (name == NULL) {
+		name = "#ff0000";
+	}
+	if (!strcmp(name, "drill")) {
+		gc->color = black;
+		gc->erase = 0;
+		return;
+	}
+	if (!strcmp(name, "erase")) {
+		/* FIXME -- should be background, not white */
+		gc->color = white;
+		gc->erase = 1;
+		return;
+	}
+	gc->color = black;
+	gc->erase = 0;
+	return;
+}
+
+static void gcode_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void gcode_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void gcode_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+static void gcode_set_draw_faded(hidGC gc, int faded)
+{
+	gc->faded = faded;
+}
+
+static void use_gc(hidGC gc)
+{
+	int need_brush = 0;
+
+	if (gc->me_pointer != &gcode_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to gcode HID\n");
+		abort();
+	}
+	if (linewidth != gc->width) {
+		/* Make sure the scaling doesn't erase lines completely */
+		/*
+		   if (SCALE (gc->width) == 0 && gc->width > 0)
+		   gdImageSetThickness (im, 1);
+		   else
+		 */
+		gdImageSetThickness(gcode_im, pcb_to_gcode(gc->width + 2 * gcode_toolradius));
+		linewidth = gc->width;
+		need_brush = 1;
+	}
+	if (lastbrush != gc->brush || need_brush) {
+		static void *bcache = 0;
+		hidval bval;
+		char name[256];
+		char type;
+		int r;
+
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			type = 'C';
+			r = pcb_to_gcode(gc->width / 2 + gcode_toolradius);
+			break;
+		default:
+		case Square_Cap:
+			r = pcb_to_gcode(gc->width + gcode_toolradius * 2);
+			type = 'S';
+			break;
+		}
+		sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r);
+
+		if (hid_cache_color(0, name, &bval, &bcache)) {
+			gc->brush = (gdImagePtr) bval.ptr;
+		}
+		else {
+			int bg, fg;
+			if (type == 'C')
+				gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1);
+			else
+				gc->brush = gdImageCreate(r + 1, r + 1);
+			bg = gdImageColorAllocate(gc->brush, 255, 255, 255);
+			fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b);
+			gdImageColorTransparent(gc->brush, bg);
+
+			/*
+			 * if we shrunk to a radius/box width of zero, then just use
+			 * a single pixel to draw with.
+			 */
+			if (r == 0)
+				gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg);
+			else {
+				if (type == 'C')
+					gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg);
+				else
+					gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg);
+			}
+			bval.ptr = gc->brush;
+			hid_cache_color(1, name, &bval, &bcache);
+		}
+
+		gdImageSetBrush(gcode_im, gc->brush);
+		lastbrush = gc->brush;
+
+	}
+#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
+	if (lastcolor != CBLEND(gc)) {
+		if (is_drill || is_mask) {
+#ifdef FIXME
+			fprintf(f, "%d gray\n", gc->erase ? 0 : 1);
+#endif
+			lastcolor = 0;
+		}
+		else {
+			double r, g, b;
+			r = gc->r;
+			g = gc->g;
+			b = gc->b;
+			if (gc->faded) {
+				r = 0.8 * 255 + 0.2 * r;
+				g = 0.8 * 255 + 0.2 * g;
+				b = 0.8 * 255 + 0.2 * b;
+			}
+#ifdef FIXME
+			if (gc->r == gc->g && gc->g == gc->b)
+				fprintf(f, "%g gray\n", r / 255.0);
+			else
+				fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
+#endif
+			lastcolor = CBLEND(gc);
+		}
+	}
+}
+
+static void gcode_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	gdImageRectangle(gcode_im,
+									 pcb_to_gcode(x1 - gcode_toolradius),
+									 pcb_to_gcode(y1 - gcode_toolradius),
+									 pcb_to_gcode(x2 + gcode_toolradius), pcb_to_gcode(y2 + gcode_toolradius), gc->color->c);
+/*      printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
+}
+
+static void gcode_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	gdImageSetThickness(gcode_im, 0);
+	linewidth = 0;
+	gdImageFilledRectangle(gcode_im,
+												 pcb_to_gcode(x1 - gcode_toolradius),
+												 pcb_to_gcode(y1 - gcode_toolradius),
+												 pcb_to_gcode(x2 + gcode_toolradius), pcb_to_gcode(y2 + gcode_toolradius), gc->color->c);
+/*      printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
+}
+
+static void gcode_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	if (x1 == x2 && y1 == y2) {
+		Coord w = gc->width / 2;
+		gcode_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
+		return;
+	}
+	use_gc(gc);
+
+	gdImageSetThickness(gcode_im, 0);
+	linewidth = 0;
+	gdImageLine(gcode_im, pcb_to_gcode(x1), pcb_to_gcode(y1), pcb_to_gcode(x2), pcb_to_gcode(y2), gdBrushed);
+}
+
+static void gcode_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	Angle sa, ea;
+
+	/*
+	 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
+	 * in pcb, 0 degrees is to the left and +90 degrees is down
+	 */
+	start_angle = 180 - start_angle;
+	delta_angle = -delta_angle;
+	if (delta_angle > 0) {
+		sa = start_angle;
+		ea = start_angle + delta_angle;
+	}
+	else {
+		sa = start_angle + delta_angle;
+		ea = start_angle;
+	}
+
+	/*
+	 * make sure we start between 0 and 360 otherwise gd does strange
+	 * things
+	 */
+	sa = NormalizeAngle(sa);
+	ea = NormalizeAngle(ea);
+
+#if 0
+	printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea);
+	printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
+				 (void *)im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c);
+#endif
+	use_gc(gc);
+	gdImageSetThickness(gcode_im, 0);
+	linewidth = 0;
+	gdImageArc(gcode_im, pcb_to_gcode(cx), pcb_to_gcode(cy),
+						 pcb_to_gcode(2 * width + gcode_toolradius * 2),
+						 pcb_to_gcode(2 * height + gcode_toolradius * 2), sa, ea, gdBrushed);
+}
+
+static void gcode_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	use_gc(gc);
+
+	gdImageSetThickness(gcode_im, 0);
+	linewidth = 0;
+	gdImageFilledEllipse(gcode_im, pcb_to_gcode(cx), pcb_to_gcode(cy),
+											 pcb_to_gcode(2 * radius + gcode_toolradius * 2),
+											 pcb_to_gcode(2 * radius + gcode_toolradius * 2), gc->color->c);
+	if (save_drill && is_drill) {
+		if (n_drill == nmax_drill) {
+			drill = (struct drill_struct *) realloc(drill, (nmax_drill + 100) * sizeof(struct drill_struct));
+			nmax_drill += 100;
+		}
+		drill[n_drill].x = PCB_COORD_TO_INCH(PCB->MaxWidth - cx);	/* convert to inch, flip: will drill from bottom side */
+		drill[n_drill].y = PCB_COORD_TO_INCH(PCB->MaxHeight - cy);	/* PCB reverses y axis */
+		n_drill++;
+/*              printf("Circle %d %d\n",cx,cy); */
+	}
+}
+
+static void gcode_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	gdPoint *points;
+
+	points = (gdPoint *) malloc(n_coords * sizeof(gdPoint));
+	if (points == NULL) {
+		fprintf(stderr, "ERROR:  gcode_fill_polygon():  malloc failed\n");
+		exit(1);
+	}
+	use_gc(gc);
+	for (i = 0; i < n_coords; i++) {
+		points[i].x = pcb_to_gcode(x[i]);
+		points[i].y = pcb_to_gcode(y[i]);
+	}
+	gdImageSetThickness(gcode_im, 0);
+	linewidth = 0;
+	gdImageFilledPolygon(gcode_im, points, n_coords, gc->color->c);
+	free(points);
+/*      printf("FillPoly\n"); */
+}
+
+static void gcode_calibrate(double xval, double yval)
+{
+	CRASH("gcode_calibrate");
+}
+
+static void gcode_set_crosshair(int x, int y, int a)
+{
+}
+
+static int gcode_usage(const char *topic)
+{
+	fprintf(stderr, "\ngcode exporter command line arguments:\n\n");
+	hid_usage(gcode_attribute_list, sizeof(gcode_attribute_list) / sizeof(gcode_attribute_list[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x gcode foo.pcb [gcode options]\n\n");
+	return 0;
+}
+
+/* *** Miscellaneous ******************************************************* */
+
+#include "dolists.h"
+
+HID gcode_hid;
+
+pcb_uninit_t hid_export_gcode_init()
+{
+	memset(&gcode_hid, 0, sizeof(HID));
+
+	common_nogui_init(&gcode_hid);
+	common_draw_helpers_init(&gcode_hid);
+
+	gcode_hid.struct_size = sizeof(HID);
+	gcode_hid.name = "gcode";
+	gcode_hid.description = "G-CODE export";
+	gcode_hid.exporter = 1;
+	gcode_hid.poly_before = 1;
+
+	gcode_hid.get_export_options = gcode_get_export_options;
+	gcode_hid.do_export = gcode_do_export;
+	gcode_hid.parse_arguments = gcode_parse_arguments;
+	gcode_hid.set_layer = gcode_set_layer;
+	gcode_hid.make_gc = gcode_make_gc;
+	gcode_hid.destroy_gc = gcode_destroy_gc;
+	gcode_hid.use_mask = gcode_use_mask;
+	gcode_hid.set_color = gcode_set_color;
+	gcode_hid.set_line_cap = gcode_set_line_cap;
+	gcode_hid.set_line_width = gcode_set_line_width;
+	gcode_hid.set_draw_xor = gcode_set_draw_xor;
+	gcode_hid.set_draw_faded = gcode_set_draw_faded;
+	gcode_hid.draw_line = gcode_draw_line;
+	gcode_hid.draw_arc = gcode_draw_arc;
+	gcode_hid.draw_rect = gcode_draw_rect;
+	gcode_hid.fill_circle = gcode_fill_circle;
+	gcode_hid.fill_polygon = gcode_fill_polygon;
+	gcode_hid.fill_rect = gcode_fill_rect;
+	gcode_hid.calibrate = gcode_calibrate;
+	gcode_hid.set_crosshair = gcode_set_crosshair;
+
+	gcode_hid.usage = gcode_usage;
+
+	hid_register_hid(&gcode_hid);
+
+	return NULL;
+}
diff --git a/src_plugins/export_gcode/gcode.h b/src_plugins/export_gcode/gcode.h
new file mode 100644
index 0000000..0e95475
--- /dev/null
+++ b/src_plugins/export_gcode/gcode.h
@@ -0,0 +1,3 @@
+/* $Id: nelma.h,v 1.2 2007/04/20 11:31:15 danmc Exp $ */
+extern const char *gcode_cookie;
+extern HID gcode_hid;
diff --git a/src_plugins/export_gcode/lists.h b/src_plugins/export_gcode/lists.h
new file mode 100644
index 0000000..3513184
--- /dev/null
+++ b/src_plugins/export_gcode/lists.h
@@ -0,0 +1,285 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: lists.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef PCB_HID_GCODE_LISTS_H
+#define PCB_HID_GCODE_LISTS_H
+
+/* here we define some general list macros. Because they are macros,
+   they should work on any datatype with a "->next" component. Some of
+   them use a "hook". If elt and list are of type t* then hook is of
+   type t**. A hook stands for an insertion point in the list, i.e.,
+   either before the first element, or between two elements, or after
+   the last element. If an operation "sets the hook" for an element,
+   then the hook is set to just before the element. One can insert
+   something at a hook. One can also unlink at a hook: this means,
+   unlink the element just after the hook. By "to unlink", we mean the
+   element is removed from the list, but not deleted. Thus, it and its
+   components still need to be freed. */
+
+/* Note: these macros are somewhat experimental. Only the ones that
+   are actually *used* have been tested. So be careful to test any
+   that you use. Looking at the output of the preprocessor, "gcc -E"
+   (possibly piped though "indent"), might help too. Also: these
+   macros define some internal (local) variables that start with
+   "_". */
+
+/* we enclose macro definitions whose body consists of more than one
+   statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'.  The
+   reason is that we want to be able to use the macro in a context
+   such as "if (...) macro(...); else ...". If we didn't use this obscure
+   trick, we'd have to omit the ";" in such cases. */
+
+#define MACRO_BEGIN do {
+#define MACRO_END   } while (0)
+
+/* ---------------------------------------------------------------------- */
+/* macros for singly-linked lists */
+
+/* traverse list. At the end, elt is set to NULL. */
+#define list_forall(elt, list)   for (elt=list; elt!=NULL; elt=elt->next)
+
+/* set elt to the first element of list satisfying boolean condition
+   c, or NULL if not found */
+#define list_find(elt, list, c) \
+  MACRO_BEGIN list_forall(elt, list) if (c) break; MACRO_END
+
+/* like forall, except also set hook for elt. */
+#define list_forall2(elt, list, hook) \
+  for (elt=list, hook=&list; elt!=NULL; hook=&elt->next, elt=elt->next)
+
+/* same as list_find, except also set hook for elt. */
+#define list_find2(elt, list, c, hook) \
+  MACRO_BEGIN list_forall2(elt, list, hook) if (c) break; MACRO_END
+
+/* same, except only use hook. */
+#define _list_forall_hook(list, hook) \
+  for (hook=&list; *hook!=NULL; hook=&(*hook)->next)
+
+/* same, except only use hook. Note: c may only refer to *hook, not elt. */
+#define _list_find_hook(list, c, hook) \
+  MACRO_BEGIN _list_forall_hook(list, hook) if (c) break; MACRO_END
+
+/* insert element after hook */
+#define list_insert_athook(elt, hook) \
+  MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END
+
+/* insert element before hook */
+#define list_insert_beforehook(elt, hook) \
+  MACRO_BEGIN elt->next = *hook; *hook = elt; hook=&elt->next; MACRO_END
+
+/* unlink element after hook, let elt be unlinked element, or NULL.
+   hook remains. */
+#define list_unlink_athook(list, elt, hook) \
+  MACRO_BEGIN \
+  elt = hook ? *hook : NULL; if (elt) { *hook = elt->next; elt->next = NULL; }\
+  MACRO_END
+
+/* unlink the specific element, if it is in the list. Otherwise, set
+   elt to NULL */
+#define list_unlink(listtype, list, elt)      \
+  MACRO_BEGIN  	       	       	       	      \
+  listtype **_hook;			      \
+  _list_find_hook(list, *_hook==elt, _hook);  \
+  list_unlink_athook(list, elt, _hook);	      \
+  MACRO_END
+
+/* prepend elt to list */
+#define list_prepend(list, elt) \
+  MACRO_BEGIN elt->next = list; list = elt; MACRO_END
+
+/* append elt to list. */
+#define list_append(listtype, list, elt)     \
+  MACRO_BEGIN                                \
+  listtype **_hook;                          \
+  _list_forall_hook(list, _hook) {}          \
+  list_insert_athook(elt, _hook);            \
+  MACRO_END
+
+/* unlink the first element that satisfies the condition. */
+#define list_unlink_cond(listtype, list, elt, c)     \
+  MACRO_BEGIN                                        \
+  listtype **_hook;			  	     \
+  list_find2(elt, list, c, _hook);                   \
+  list_unlink_athook(list, elt, _hook);              \
+  MACRO_END
+
+/* let elt be the nth element of the list, starting to count from 0.
+   Return NULL if out of bounds.   */
+#define list_nth(elt, list, n)                                \
+  MACRO_BEGIN                                                 \
+  int _x;  /* only evaluate n once */                         \
+  for (_x=(n), elt=list; _x && elt; _x--, elt=elt->next) {}   \
+  MACRO_END
+
+/* let elt be the nth element of the list, starting to count from 0.
+   Return NULL if out of bounds.   */
+#define list_nth_hook(elt, list, n, hook)                     \
+  MACRO_BEGIN                                                 \
+  int _x;  /* only evaluate n once */                         \
+  for (_x=(n), elt=list, hook=&list; _x && elt; _x--, hook=&elt->next, elt=elt->next) {}   \
+  MACRO_END
+
+/* set n to the length of the list */
+#define list_length(listtype, list, n)                   \
+  MACRO_BEGIN          	       	       	       	       	 \
+  listtype *_elt;   			 		 \
+  n=0;					 		 \
+  list_forall(_elt, list) 		 		 \
+    n++;				 		 \
+  MACRO_END
+
+/* set n to the index of the first element satisfying cond, or -1 if
+   none found. Also set elt to the element, or NULL if none found. */
+#define list_index(list, n, elt, c)                      \
+  MACRO_BEGIN				 		 \
+  n=0;					 		 \
+  list_forall(elt, list) {		 		 \
+    if (c) break;			 		 \
+    n++;				 		 \
+  }					 		 \
+  if (!elt)				 		 \
+    n=-1;				 		 \
+  MACRO_END
+
+/* set n to the number of elements in the list that satisfy condition c */
+#define list_count(list, n, elt, c)                      \
+  MACRO_BEGIN				 		 \
+  n=0;					 		 \
+  list_forall(elt, list) {		 		 \
+    if (c) n++;				 		 \
+  }                                                      \
+  MACRO_END
+
+/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
+#define list_forall_unlink(elt, list) \
+  for (elt=list; elt ? (list=elt->next, elt->next=NULL), 1 : 0; elt=list)
+
+/* reverse a list (efficient) */
+#define list_reverse(listtype, list)            \
+  MACRO_BEGIN				 	\
+  listtype *_list1=NULL, *elt;			\
+  list_forall_unlink(elt, list) 		\
+    list_prepend(_list1, elt);			\
+  list = _list1;				\
+  MACRO_END
+
+/* insert the element ELT just before the first element TMP of the
+   list for which COND holds. Here COND must be a condition of ELT and
+   TMP.  Typical usage is to insert an element into an ordered list:
+   for instance, list_insert_ordered(listtype, list, elt, tmp,
+   elt->size <= tmp->size).  Note: if we give a "less than or equal"
+   condition, the new element will be inserted just before a sequence
+   of equal elements. If we give a "less than" condition, the new
+   element will be inserted just after a list of equal elements.
+   Note: it is much more efficient to construct a list with
+   list_prepend and then order it with list_merge_sort, than to
+   construct it with list_insert_ordered. */
+#define list_insert_ordered(listtype, list, elt, tmp, cond) \
+  MACRO_BEGIN                                               \
+  listtype **_hook;                                         \
+  _list_find_hook(list, (tmp=*_hook, (cond)), _hook);       \
+  list_insert_athook(elt, _hook);                           \
+  MACRO_END
+
+/* sort the given list, according to the comparison condition.
+   Typical usage is list_sort(listtype, list, a, b, a->size <
+   b->size).  Note: if we give "less than or equal" condition, each
+   segment of equal elements will be reversed in order. If we give a
+   "less than" condition, each segment of equal elements will retain
+   the original order. The latter is slower but sometimes
+   prettier. Average running time: n*n/2. */
+#define list_sort(listtype, list, a, b, cond)            \
+  MACRO_BEGIN                                            \
+  listtype *_newlist=NULL;                               \
+  list_forall_unlink(a, list)                            \
+    list_insert_ordered(listtype, _newlist, a, b, cond); \
+  list = _newlist;                                       \
+  MACRO_END
+
+/* a much faster sort algorithm (merge sort, n log n worst case). It
+   is required that the list type has an additional, unused next1
+   component. Note there is no curious reversal of order of equal
+   elements as for list_sort. */
+
+#define list_mergesort(listtype, list, a, b, cond)              \
+  MACRO_BEGIN						        \
+  listtype *_elt, **_hook1;				    	\
+							    	\
+  for (_elt=list; _elt; _elt=_elt->next1) {			\
+    _elt->next1 = _elt->next;				    	\
+    _elt->next = NULL;					    	\
+  }							    	\
+  do {			                               	    	\
+    _hook1 = &(list);				    	    	\
+    while ((a = *_hook1) != NULL && (b = a->next1) != NULL ) {  \
+      _elt = b->next1;					    	\
+      _list_merge_cond(listtype, a, b, cond, *_hook1);      	\
+      _hook1 = &((*_hook1)->next1);			    	\
+      *_hook1 = _elt;				            	\
+    }							    	\
+  } while (_hook1 != &(list));                                 	\
+  MACRO_END
+
+/* merge two sorted lists. Store result at &result */
+#define _list_merge_cond(listtype, a, b, cond, result)   \
+  MACRO_BEGIN                                            \
+  listtype **_hook;					 \
+  _hook = &(result);					 \
+  while (1) {                                            \
+     if (a==NULL) {					 \
+       *_hook = b;					 \
+       break;						 \
+     } else if (b==NULL) {				 \
+       *_hook = a;					 \
+       break;						 \
+     } else if (cond) {					 \
+       *_hook = a;					 \
+       _hook = &(a->next);				 \
+       a = a->next;					 \
+     } else {						 \
+       *_hook = b;					 \
+       _hook = &(b->next);				 \
+       b = b->next;					 \
+     }							 \
+  }							 \
+  MACRO_END
+
+/* ---------------------------------------------------------------------- */
+/* macros for doubly-linked lists */
+
+#define dlist_append(head, end, elt)                    \
+  MACRO_BEGIN  	       	       	       	       	       	 \
+  elt->prev = end;					 \
+  elt->next = NULL;					 \
+  if (end) {						 \
+    end->next = elt;					 \
+  } else {  						 \
+    head = elt;						 \
+  }	    						 \
+  end = elt;						 \
+  MACRO_END
+
+/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
+#define dlist_forall_unlink(elt, head, end) \
+  for (elt=head; elt ? (head=elt->next, elt->next=NULL, elt->prev=NULL), 1 : (end=NULL, 0); elt=head)
+
+/* unlink the first element of the list */
+#define dlist_unlink_first(head, end, elt)               \
+  MACRO_BEGIN				       	       	 \
+  elt = head;						 \
+  if (head) {						 \
+    head = head->next;					 \
+    if (head) {						 \
+      head->prev = NULL;				 \
+    } else {						 \
+      end = NULL;					 \
+    }    						 \
+    elt->prev = NULL;					 \
+    elt->next = NULL;					 \
+  }							 \
+  MACRO_END
+
+#endif /* PCB_HID_GCODE_LISTS_H */
diff --git a/src_plugins/export_gcode/potracelib.h b/src_plugins/export_gcode/potracelib.h
new file mode 100644
index 0000000..21232c2
--- /dev/null
+++ b/src_plugins/export_gcode/potracelib.h
@@ -0,0 +1,130 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef POTRACELIB_H
+#define POTRACELIB_H
+
+/* this file defines the API for the core Potrace library. For a more
+   detailed description of the API, see doc/potracelib.txt */
+
+/* ---------------------------------------------------------------------- */
+/* tracing parameters */
+
+/* turn policies */
+#define POTRACE_TURNPOLICY_BLACK 0
+#define POTRACE_TURNPOLICY_WHITE 1
+#define POTRACE_TURNPOLICY_LEFT 2
+#define POTRACE_TURNPOLICY_RIGHT 3
+#define POTRACE_TURNPOLICY_MINORITY 4
+#define POTRACE_TURNPOLICY_MAJORITY 5
+#define POTRACE_TURNPOLICY_RANDOM 6
+
+/* structure to hold progress bar callback data */
+struct potrace_progress_s {
+	void (*callback) (double progress, void *privdata);	/* callback fn */
+	void *data;										/* callback function's private data */
+	double min, max;							/* desired range of progress, e.g. 0.0 to 1.0 */
+	double epsilon;								/* granularity: can skip smaller increments */
+};
+typedef struct potrace_progress_s potrace_progress_t;
+
+/* structure to hold tracing parameters */
+struct potrace_param_s {
+	int turdsize;									/* area of largest path to be ignored */
+	int turnpolicy;								/* resolves ambiguous turns in path decomposition */
+	double alphamax;							/* corner threshold */
+	int opticurve;								/* use curve optimization? */
+	double opttolerance;					/* curve optimization tolerance */
+	potrace_progress_t progress;	/* progress callback function */
+};
+typedef struct potrace_param_s potrace_param_t;
+
+/* ---------------------------------------------------------------------- */
+/* bitmaps */
+
+/* native word size */
+typedef unsigned long potrace_word;
+
+/* Internal bitmap format. The n-th scanline starts at scanline(n) =
+   (map + n*dy). Raster data is stored as a sequence of potrace_words
+   (NOT bytes). The leftmost bit of scanline n is the most significant
+   bit of scanline(n)[0]. */
+struct potrace_bitmap_s {
+	int w, h;											/* width and height, in pixels */
+	int dy;												/* words per scanline (not bytes) */
+	potrace_word *map;						/* raw data, dy*h words */
+};
+typedef struct potrace_bitmap_s potrace_bitmap_t;
+
+/* ---------------------------------------------------------------------- */
+/* curves */
+
+/* point */
+struct potrace_dpoint_s {
+	double x, y;
+};
+typedef struct potrace_dpoint_s potrace_dpoint_t;
+
+/* segment tags */
+#define POTRACE_CURVETO 1
+#define POTRACE_CORNER 2
+
+/* closed curve segment */
+struct potrace_curve_s {
+	int n;												/* number of segments */
+	int *tag;											/* tag[n]: POTRACE_CURVETO or POTRACE_CORNER */
+	  potrace_dpoint_t(*c)[3];		/* c[n][3]: control points. 
+																   c[n][0] is unused for tag[n]=POTRACE_CORNER */
+};
+typedef struct potrace_curve_s potrace_curve_t;
+
+/* Linked list of signed curve segments. Also carries a tree structure. */
+struct potrace_path_s {
+	int area;											/* area of the bitmap path */
+	int sign;											/* '+' or '-', depending on orientation */
+	potrace_curve_t curve;				/* this path's vector data */
+
+	struct potrace_path_s *next;	/* linked list structure */
+
+	struct potrace_path_s *childlist;	/* tree structure */
+	struct potrace_path_s *sibling;	/* tree structure */
+
+	struct potrace_privpath_s *priv;	/* private state */
+};
+typedef struct potrace_path_s potrace_path_t;
+
+/* ---------------------------------------------------------------------- */
+/* Potrace state */
+
+#define POTRACE_STATUS_OK         0
+#define POTRACE_STATUS_INCOMPLETE 1
+
+struct potrace_state_s {
+	int status;
+	potrace_path_t *plist;				/* vector data */
+
+	struct potrace_privstate_s *priv;	/* private state */
+};
+typedef struct potrace_state_s potrace_state_t;
+
+/* ---------------------------------------------------------------------- */
+/* API functions */
+
+/* get default parameters */
+potrace_param_t *potrace_param_default(void);
+
+/* free parameter set */
+void potrace_param_free(potrace_param_t * p);
+
+/* trace a bitmap*/
+potrace_state_t *potrace_trace(const potrace_param_t * param, const potrace_bitmap_t * bm);
+
+/* free a Potrace state */
+void potrace_state_free(potrace_state_t * st);
+
+/* return a static plain text version string identifying this version
+   of potracelib */
+char *potrace_version(void);
+
+#endif /* POTRACELIB_H */
diff --git a/src_plugins/export_gcode/trace.c b/src_plugins/export_gcode/trace.c
new file mode 100644
index 0000000..c067920
--- /dev/null
+++ b/src_plugins/export_gcode/trace.c
@@ -0,0 +1,1292 @@
+/* This file was slightly modified by Alberto Maccioni to be used with PCB G-CODE exporter*/
+
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: trace.c 147 2007-04-09 00:44:09Z selinger $ */
+/* transform jaggy paths into smooth curves */
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+#include "potracelib.h"
+#include "curve.h"
+#include "lists.h"
+#include "auxiliary.h"
+#include "trace.h"
+/*#include "progress.h"*/
+
+#define INFTY 10000000					/* it suffices that this is longer than any
+																   path; it need not be really infinite */
+#define COS179 -0.999847695156	/* the cosine of 179 degrees */
+
+/* ---------------------------------------------------------------------- */
+#define SAFE_MALLOC(var, n, typ) \
+  if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error
+
+/* ---------------------------------------------------------------------- */
+/* auxiliary functions */
+
+/* return a direction that is 90 degrees counterclockwise from p2-p0,
+   but then restricted to one of the major wind directions (n, nw, w, etc) */
+static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2)
+{
+	point_t r;
+
+	r.y = sign(p2.x - p0.x);
+	r.x = -sign(p2.y - p0.y);
+
+	return r;
+}
+
+/* return (p1-p0)x(p2-p0), the area of the parallelogram */
+static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2)
+{
+	double x1, y1, x2, y2;
+
+	x1 = p1.x - p0.x;
+	y1 = p1.y - p0.y;
+	x2 = p2.x - p0.x;
+	y2 = p2.y - p0.y;
+
+	return x1 * y2 - x2 * y1;
+}
+
+/* ddenom/dpara have the property that the square of radius 1 centered
+   at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */
+static inline double ddenom(dpoint_t p0, dpoint_t p2)
+{
+	point_t r = dorth_infty(p0, p2);
+
+	return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y);
+}
+
+/* return 1 if a <= b < c < a, in a cyclic sense (mod n) */
+static inline int cyclic(int a, int b, int c)
+{
+	if (a <= c) {
+		return (a <= b && b < c);
+	}
+	else {
+		return (a <= b || b < c);
+	}
+}
+
+/* determine the center and slope of the line i..j. Assume i<j. Needs
+   "sum" components of p to be set. */
+static void pointslope(privpath_t * pp, int i, int j, dpoint_t * ctr, dpoint_t * dir)
+{
+	/* assume i<j */
+
+	int n = pp->len;
+	sums_t *sums = pp->sums;
+
+	double x, y, x2, xy, y2;
+	double k;
+	double a, b, c, lambda2, l;
+	int r = 0;										/* rotations from i to j */
+
+	while (j >= n) {
+		j -= n;
+		r += 1;
+	}
+	while (i >= n) {
+		i -= n;
+		r -= 1;
+	}
+	while (j < 0) {
+		j += n;
+		r -= 1;
+	}
+	while (i < 0) {
+		i += n;
+		r += 1;
+	}
+
+	x = sums[j + 1].x - sums[i].x + r * sums[n].x;
+	y = sums[j + 1].y - sums[i].y + r * sums[n].y;
+	x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2;
+	xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy;
+	y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2;
+	k = j + 1 - i + r * n;
+
+	ctr->x = x / k;
+	ctr->y = y / k;
+
+	a = (x2 - (double) x * x / k) / k;
+	b = (xy - (double) x * y / k) / k;
+	c = (y2 - (double) y * y / k) / k;
+
+	lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2;	/* larger e.value */
+
+	/* now find e.vector for lambda2 */
+	a -= lambda2;
+	c -= lambda2;
+
+	if (fabs(a) >= fabs(c)) {
+		l = sqrt(a * a + b * b);
+		if (l != 0) {
+			dir->x = -b / l;
+			dir->y = a / l;
+		}
+	}
+	else {
+		l = sqrt(c * c + b * b);
+		if (l != 0) {
+			dir->x = -c / l;
+			dir->y = b / l;
+		}
+	}
+	if (l == 0) {
+		dir->x = dir->y = 0;				/* sometimes this can happen when k=4:
+																   the two eigenvalues coincide */
+	}
+}
+
+/* the type of (affine) quadratic forms, represented as symmetric 3x3
+   matrices.  The value of the quadratic form at a vector (x,y) is v^t
+   Q v, where v = (x,y,1)^t. */
+typedef double quadform_t[3][3];
+
+/* Apply quadratic form Q to vector w = (w.x,w.y) */
+static inline double quadform(quadform_t Q, dpoint_t w)
+{
+	double v[3];
+	int i, j;
+	double sum;
+
+	v[0] = w.x;
+	v[1] = w.y;
+	v[2] = 1;
+	sum = 0.0;
+
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 3; j++) {
+			sum += v[i] * Q[i][j] * v[j];
+		}
+	}
+	return sum;
+}
+
+/* calculate p1 x p2 */
+static inline int xprod(point_t p1, point_t p2)
+{
+	return p1.x * p2.y - p1.y * p2.x;
+}
+
+/* calculate (p1-p0)x(p3-p2) */
+static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3)
+{
+	double x1, y1, x2, y2;
+
+	x1 = p1.x - p0.x;
+	y1 = p1.y - p0.y;
+	x2 = p3.x - p2.x;
+	y2 = p3.y - p2.y;
+
+	return x1 * y2 - x2 * y1;
+}
+
+/* calculate (p1-p0)*(p2-p0) */
+static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2)
+{
+	double x1, y1, x2, y2;
+
+	x1 = p1.x - p0.x;
+	y1 = p1.y - p0.y;
+	x2 = p2.x - p0.x;
+	y2 = p2.y - p0.y;
+
+	return x1 * x2 + y1 * y2;
+}
+
+/* calculate (p1-p0)*(p3-p2) */
+static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3)
+{
+	double x1, y1, x2, y2;
+
+	x1 = p1.x - p0.x;
+	y1 = p1.y - p0.y;
+	x2 = p3.x - p2.x;
+	y2 = p3.y - p2.y;
+
+	return x1 * x2 + y1 * y2;
+}
+
+/* calculate distance between two points */
+static inline double ddist(dpoint_t p, dpoint_t q)
+{
+	return sqrt(sq(p.x - q.x) + sq(p.y - q.y));
+}
+
+/* calculate point of a bezier curve */
+static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3)
+{
+	double s = 1 - t;
+	dpoint_t res;
+
+	/* Note: a good optimizing compiler (such as gcc-3) reduces the
+	   following to 16 multiplications, using common subexpression
+	   elimination. */
+
+	res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x;
+	res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y;
+
+	return res;
+}
+
+/* calculate the point t in [0..1] on the (convex) bezier curve
+   (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no
+   solution in [0..1]. */
+static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1)
+{
+	double A, B, C;								/* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */
+	double a, b, c;								/* a t^2 + b t + c = 0 */
+	double d, s, r1, r2;
+
+	A = cprod(p0, p1, q0, q1);
+	B = cprod(p1, p2, q0, q1);
+	C = cprod(p2, p3, q0, q1);
+
+	a = A - 2 * B + C;
+	b = -2 * A + 2 * B;
+	c = A;
+
+	d = b * b - 4 * a * c;
+
+	if (a == 0 || d < 0) {
+		return -1.0;
+	}
+
+	s = sqrt(d);
+
+	r1 = (-b + s) / (2 * a);
+	r2 = (-b - s) / (2 * a);
+
+	if (r1 >= 0 && r1 <= 1) {
+		return r1;
+	}
+	else if (r2 >= 0 && r2 <= 1) {
+		return r2;
+	}
+	else {
+		return -1.0;
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+/* Preparation: fill in the sum* fields of a path (used for later
+   rapid summing). Return 0 on success, 1 with errno set on
+   failure. */
+static int calc_sums(privpath_t * pp)
+{
+	int i, x, y;
+	int n = pp->len;
+
+	SAFE_MALLOC(pp->sums, pp->len + 1, sums_t);
+
+	/* origin */
+	pp->x0 = pp->pt[0].x;
+	pp->y0 = pp->pt[0].y;
+
+	/* preparatory computation for later fast summing */
+	pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0;
+	for (i = 0; i < n; i++) {
+		x = pp->pt[i].x - pp->x0;
+		y = pp->pt[i].y - pp->y0;
+		pp->sums[i + 1].x = pp->sums[i].x + x;
+		pp->sums[i + 1].y = pp->sums[i].y + y;
+		pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x;
+		pp->sums[i + 1].xy = pp->sums[i].xy + x * y;
+		pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y;
+	}
+	return 0;
+
+malloc_error:
+	return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the
+   "lon" component of a path object (based on pt/len).	For each i,
+   lon[i] is the furthest index such that a straight line can be drawn
+   from i to lon[i]. Return 1 on error with errno set, else 0. */
+
+/* this algorithm depends on the fact that the existence of straight
+   subpaths is a triplewise property. I.e., there exists a straight
+   line through squares i0,...,in iff there exists a straight line
+   through i,j,k, for all i0<=i<j<k<=in. (Proof?) */
+
+/* this implementation of calc_lon is O(n^2). It replaces an older
+   O(n^3) version. A "constraint" means that future points must
+   satisfy xprod(constraint[0], cur) >= 0 and xprod(constraint[1],
+   cur) <= 0. */
+
+/* Remark for Potrace 1.1: the current implementation of calc_lon is
+   more complex than the implementation found in Potrace 1.0, but it
+   is considerably faster. The introduction of the "nc" data structure
+   means that we only have to test the constraints for "corner"
+   points. On a typical input file, this speeds up the calc_lon
+   function by a factor of 31.2, thereby decreasing its time share
+   within the overall Potrace algorithm from 72.6% to 7.82%, and
+   speeding up the overall algorithm by a factor of 3.36. On another
+   input file, calc_lon was sped up by a factor of 6.7, decreasing its
+   time share from 51.4% to 13.61%, and speeding up the overall
+   algorithm by a factor of 1.78. In any case, the savings are
+   substantial. */
+
+/* returns 0 on success, 1 on error with errno set */
+static int calc_lon(privpath_t * pp)
+{
+	point_t *pt = pp->pt;
+	int n = pp->len;
+	int i, j, k, k1;
+	int ct[4], dir;
+	point_t constraint[2];
+	point_t cur;
+	point_t off;
+	int *pivk = NULL;							/* pivk[n] */
+	int *nc = NULL;								/* nc[n]: next corner */
+	point_t dk;										/* direction of k-k1 */
+	int a, b, c, d;
+
+	SAFE_MALLOC(pivk, n, int);
+	SAFE_MALLOC(nc, n, int);
+
+	/* initialize the nc data structure. Point from each point to the
+	   furthest future point to which it is connected by a vertical or
+	   horizontal segment. We take advantage of the fact that there is
+	   always a direction change at 0 (due to the path decomposition
+	   algorithm). But even if this were not so, there is no harm, as
+	   in practice, correctness does not depend on the word "furthest"
+	   above.  */
+	k = 0;
+	for (i = n - 1; i >= 0; i--) {
+		if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) {
+			k = i + 1;								/* necessarily i<n-1 in this case */
+		}
+		nc[i] = k;
+	}
+
+	SAFE_MALLOC(pp->lon, n, int);
+
+	/* determine pivot points: for each i, let pivk[i] be the furthest k
+	   such that all j with i<j<k lie on a line connecting i,k. */
+
+	for (i = n - 1; i >= 0; i--) {
+		ct[0] = ct[1] = ct[2] = ct[3] = 0;
+
+		/* keep track of "directions" that have occurred */
+		dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2;
+		ct[dir]++;
+
+		constraint[0].x = 0;
+		constraint[0].y = 0;
+		constraint[1].x = 0;
+		constraint[1].y = 0;
+
+		/* find the next k such that no straight line from i to k */
+		k = nc[i];
+		k1 = i;
+		while (1) {
+
+			dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2;
+			ct[dir]++;
+
+			/* if all four "directions" have occurred, cut this path */
+			if (ct[0] && ct[1] && ct[2] && ct[3]) {
+				pivk[i] = k1;
+				goto foundk;
+			}
+
+			cur.x = pt[k].x - pt[i].x;
+			cur.y = pt[k].y - pt[i].y;
+
+			/* see if current constraint is violated */
+			if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) {
+				goto constraint_viol;
+			}
+
+			/* else, update constraint */
+			if (abs(cur.x) <= 1 && abs(cur.y) <= 1) {
+				/* no constraint */
+			}
+			else {
+				off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1);
+				off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1);
+				if (xprod(constraint[0], off) >= 0) {
+					constraint[0] = off;
+				}
+				off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1);
+				off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1);
+				if (xprod(constraint[1], off) <= 0) {
+					constraint[1] = off;
+				}
+			}
+			k1 = k;
+			k = nc[k1];
+			if (!cyclic(k, i, k1)) {
+				break;
+			}
+		}
+	constraint_viol:
+		/* k1 was the last "corner" satisfying the current constraint, and
+		   k is the first one violating it. We now need to find the last
+		   point along k1..k which satisfied the constraint. */
+		dk.x = sign(pt[k].x - pt[k1].x);
+		dk.y = sign(pt[k].y - pt[k1].y);
+		cur.x = pt[k1].x - pt[i].x;
+		cur.y = pt[k1].y - pt[i].y;
+		/* find largest integer j such that xprod(constraint[0], cur+j*dk)
+		   >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity
+		   of xprod. */
+		a = xprod(constraint[0], cur);
+		b = xprod(constraint[0], dk);
+		c = xprod(constraint[1], cur);
+		d = xprod(constraint[1], dk);
+		/* find largest integer j such that a+j*b>=0 and c+j*d<=0. This
+		   can be solved with integer arithmetic. */
+		j = INFTY;
+		if (b < 0) {
+			j = floordiv(a, -b);
+		}
+		if (d > 0) {
+			j = min(j, floordiv(-c, d));
+		}
+		pivk[i] = mod(k1 + j, n);
+	foundk:
+		;
+	}															/* for i */
+
+	/* clean up: for each i, let lon[i] be the largest k such that for
+	   all i' with i<=i'<k, i'<k<=pivk[i']. */
+
+	j = pivk[n - 1];
+	pp->lon[n - 1] = j;
+	for (i = n - 2; i >= 0; i--) {
+		if (cyclic(i + 1, pivk[i], j)) {
+			j = pivk[i];
+		}
+		pp->lon[i] = j;
+	}
+
+	for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) {
+		pp->lon[i] = j;
+	}
+
+	free(pivk);
+	free(nc);
+	return 0;
+
+malloc_error:
+	free(pivk);
+	free(nc);
+	return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */
+
+/* Auxiliary function: calculate the penalty of an edge from i to j in
+   the given path. This needs the "lon" and "sum*" data. */
+
+static double penalty3(privpath_t * pp, int i, int j)
+{
+	int n = pp->len;
+	point_t *pt = pp->pt;
+	sums_t *sums = pp->sums;
+
+	/* assume 0<=i<j<=n  */
+	double x, y, x2, xy, y2;
+	double k;
+	double a, b, c, s;
+	double px, py, ex, ey;
+
+	int r = 0;										/* rotations from i to j */
+
+	if (j >= n) {
+		j -= n;
+		r += 1;
+	}
+
+	x = sums[j + 1].x - sums[i].x + r * sums[n].x;
+	y = sums[j + 1].y - sums[i].y + r * sums[n].y;
+	x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2;
+	xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy;
+	y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2;
+	k = j + 1 - i + r * n;
+
+	px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x;
+	py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y;
+	ey = (pt[j].x - pt[i].x);
+	ex = -(pt[j].y - pt[i].y);
+
+	a = ((x2 - 2 * x * px) / k + px * px);
+	b = ((xy - x * py - y * px) / k + px * py);
+	c = ((y2 - 2 * y * py) / k + py * py);
+
+	s = ex * ex * a + 2 * ex * ey * b + ey * ey * c;
+
+	return sqrt(s);
+}
+
+/* find the optimal polygon. Fill in the m and po components. Return 1
+   on failure with errno set, else 0. Non-cyclic version: assumes i=0
+   is in the polygon. Fixme: ### implement cyclic version. */
+static int bestpolygon(privpath_t * pp)
+{
+	int i, j, m, k;
+	int n = pp->len;
+	double *pen = NULL;						/* pen[n+1]: penalty vector */
+	int *prev = NULL;							/* prev[n+1]: best path pointer vector */
+	int *clip0 = NULL;						/* clip0[n]: longest segment pointer, non-cyclic */
+	int *clip1 = NULL;						/* clip1[n+1]: backwards segment pointer, non-cyclic */
+	int *seg0 = NULL;							/* seg0[m+1]: forward segment bounds, m<=n */
+	int *seg1 = NULL;							/* seg1[m+1]: backward segment bounds, m<=n */
+	double thispen;
+	double best;
+	int c;
+
+	SAFE_MALLOC(pen, n + 1, double);
+	SAFE_MALLOC(prev, n + 1, int);
+	SAFE_MALLOC(clip0, n, int);
+	SAFE_MALLOC(clip1, n + 1, int);
+	SAFE_MALLOC(seg0, n + 1, int);
+	SAFE_MALLOC(seg1, n + 1, int);
+
+	/* calculate clipped paths */
+	for (i = 0; i < n; i++) {
+		c = mod(pp->lon[mod(i - 1, n)] - 1, n);
+		if (c == i) {
+			c = mod(i + 1, n);
+		}
+		if (c < i) {
+			clip0[i] = n;
+		}
+		else {
+			clip0[i] = c;
+		}
+	}
+
+	/* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff
+	   clip1[j] <= i, for i,j=0..n. */
+	j = 1;
+	for (i = 0; i < n; i++) {
+		while (j <= clip0[i]) {
+			clip1[j] = i;
+			j++;
+		}
+	}
+
+	/* calculate seg0[j] = longest path from 0 with j segments */
+	i = 0;
+	for (j = 0; i < n; j++) {
+		seg0[j] = i;
+		i = clip0[i];
+	}
+	seg0[j] = n;
+	m = j;
+
+	/* calculate seg1[j] = longest path to n with m-j segments */
+	i = n;
+	for (j = m; j > 0; j--) {
+		seg1[j] = i;
+		i = clip1[i];
+	}
+	seg1[0] = 0;
+
+	/* now find the shortest path with m segments, based on penalty3 */
+	/* note: the outer 2 loops jointly have at most n interations, thus
+	   the worst-case behavior here is quadratic. In practice, it is
+	   close to linear since the inner loop tends to be short. */
+	pen[0] = 0;
+	for (j = 1; j <= m; j++) {
+		for (i = seg1[j]; i <= seg0[j]; i++) {
+			best = -1;
+			for (k = seg0[j - 1]; k >= clip1[i]; k--) {
+				thispen = penalty3(pp, k, i) + pen[k];
+				if (best < 0 || thispen < best) {
+					prev[i] = k;
+					best = thispen;
+				}
+			}
+			pen[i] = best;
+		}
+	}
+
+	pp->m = m;
+	SAFE_MALLOC(pp->po, m, int);
+
+	/* read off shortest path */
+	for (i = n, j = m - 1; i > 0; j--) {
+		i = prev[i];
+		pp->po[j] = i;
+	}
+
+	free(pen);
+	free(prev);
+	free(clip0);
+	free(clip1);
+	free(seg0);
+	free(seg1);
+	return 0;
+
+malloc_error:
+	free(pen);
+	free(prev);
+	free(clip0);
+	free(clip1);
+	free(seg0);
+	free(seg1);
+	return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Stage 3: vertex adjustment (Sec. 2.3.1). */
+
+/* Adjust vertices of optimal polygon: calculate the intersection of
+   the two "optimal" line segments, then move it into the unit square
+   if it lies outside. Return 1 with errno set on error; 0 on
+   success. */
+
+static int adjust_vertices(privpath_t * pp)
+{
+	int m = pp->m;
+	int *po = pp->po;
+	int n = pp->len;
+	point_t *pt = pp->pt;
+	int x0 = pp->x0;
+	int y0 = pp->y0;
+
+	dpoint_t *ctr = NULL;					/* ctr[m] */
+	dpoint_t *dir = NULL;					/* dir[m] */
+	quadform_t *q = NULL;					/* q[m] */
+	double v[3];
+	double d;
+	int i, j, k, l;
+	dpoint_t s;
+	int r;
+
+	SAFE_MALLOC(ctr, m, dpoint_t);
+	SAFE_MALLOC(dir, m, dpoint_t);
+	SAFE_MALLOC(q, m, quadform_t);
+
+	r = privcurve_init(&pp->curve, m);
+	if (r) {
+		goto malloc_error;
+	}
+
+	/* calculate "optimal" point-slope representation for each line
+	   segment */
+	for (i = 0; i < m; i++) {
+		j = po[mod(i + 1, m)];
+		j = mod(j - po[i], n) + po[i];
+		pointslope(pp, po[i], j, &ctr[i], &dir[i]);
+	}
+
+	/* represent each line segment as a singular quadratic form; the
+	   distance of a point (x,y) from the line segment will be
+	   (x,y,1)Q(x,y,1)^t, where Q=q[i]. */
+	for (i = 0; i < m; i++) {
+		d = sq(dir[i].x) + sq(dir[i].y);
+		if (d == 0.0) {
+			for (j = 0; j < 3; j++) {
+				for (k = 0; k < 3; k++) {
+					q[i][j][k] = 0;
+				}
+			}
+		}
+		else {
+			v[0] = dir[i].y;
+			v[1] = -dir[i].x;
+			v[2] = -v[1] * ctr[i].y - v[0] * ctr[i].x;
+			for (l = 0; l < 3; l++) {
+				for (k = 0; k < 3; k++) {
+					q[i][l][k] = v[l] * v[k] / d;
+				}
+			}
+		}
+	}
+
+	/* now calculate the "intersections" of consecutive segments.
+	   Instead of using the actual intersection, we find the point
+	   within a given unit square which minimizes the square distance to
+	   the two lines. */
+	for (i = 0; i < m; i++) {
+		quadform_t Q;
+		dpoint_t w;
+		double dx, dy;
+		double det;
+		double min, cand;						/* minimum and candidate for minimum of quad. form */
+		double xmin, ymin;					/* coordinates of minimum */
+		int z;
+
+		/* let s be the vertex, in coordinates relative to x0/y0 */
+		s.x = pt[po[i]].x - x0;
+		s.y = pt[po[i]].y - y0;
+
+		/* intersect segments i-1 and i */
+
+		j = mod(i - 1, m);
+
+		/* add quadratic forms */
+		for (l = 0; l < 3; l++) {
+			for (k = 0; k < 3; k++) {
+				Q[l][k] = q[j][l][k] + q[i][l][k];
+			}
+		}
+
+		while (1) {
+			/* minimize the quadratic form Q on the unit square */
+			/* find intersection */
+
+#ifdef HAVE_GCC_LOOP_BUG
+			/* work around gcc bug #12243 */
+			free(NULL);
+#endif
+
+			det = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0];
+			if (det != 0.0) {
+				w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det;
+				w.y = (Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det;
+				break;
+			}
+
+			/* matrix is singular - lines are parallel. Add another,
+			   orthogonal axis, through the center of the unit square */
+			if (Q[0][0] > Q[1][1]) {
+				v[0] = -Q[0][1];
+				v[1] = Q[0][0];
+			}
+			else if (Q[1][1]) {
+				v[0] = -Q[1][1];
+				v[1] = Q[1][0];
+			}
+			else {
+				v[0] = 1;
+				v[1] = 0;
+			}
+			d = sq(v[0]) + sq(v[1]);
+			v[2] = -v[1] * s.y - v[0] * s.x;
+			for (l = 0; l < 3; l++) {
+				for (k = 0; k < 3; k++) {
+					Q[l][k] += v[l] * v[k] / d;
+				}
+			}
+		}
+		dx = fabs(w.x - s.x);
+		dy = fabs(w.y - s.y);
+		if (dx <= .5 && dy <= .5) {
+			pp->curve.vertex[i].x = w.x + x0;
+			pp->curve.vertex[i].y = w.y + y0;
+			continue;
+		}
+
+		/* the minimum was not in the unit square; now minimize quadratic
+		   on boundary of square */
+		min = quadform(Q, s);
+		xmin = s.x;
+		ymin = s.y;
+
+		if (Q[0][0] == 0.0) {
+			goto fixx;
+		}
+		for (z = 0; z < 2; z++) {		/* value of the y-coordinate */
+			w.y = s.y - 0.5 + z;
+			w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0];
+			dx = fabs(w.x - s.x);
+			cand = quadform(Q, w);
+			if (dx <= .5 && cand < min) {
+				min = cand;
+				xmin = w.x;
+				ymin = w.y;
+			}
+		}
+	fixx:
+		if (Q[1][1] == 0.0) {
+			goto corners;
+		}
+		for (z = 0; z < 2; z++) {		/* value of the x-coordinate */
+			w.x = s.x - 0.5 + z;
+			w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1];
+			dy = fabs(w.y - s.y);
+			cand = quadform(Q, w);
+			if (dy <= .5 && cand < min) {
+				min = cand;
+				xmin = w.x;
+				ymin = w.y;
+			}
+		}
+	corners:
+		/* check four corners */
+		for (l = 0; l < 2; l++) {
+			for (k = 0; k < 2; k++) {
+				w.x = s.x - 0.5 + l;
+				w.y = s.y - 0.5 + k;
+				cand = quadform(Q, w);
+				if (cand < min) {
+					min = cand;
+					xmin = w.x;
+					ymin = w.y;
+				}
+			}
+		}
+
+		pp->curve.vertex[i].x = xmin + x0;
+		pp->curve.vertex[i].y = ymin + y0;
+		continue;
+	}
+
+	free(ctr);
+	free(dir);
+	free(q);
+	return 0;
+
+malloc_error:
+	free(ctr);
+	free(dir);
+	free(q);
+	return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */
+
+/* Always succeeds and returns 0 */
+static int
+ATTRIBUTE_UNUSED smooth(privcurve_t * curve, int sign, double alphamax)
+{
+	int m = curve->n;
+
+	int i, j, k;
+	double dd, denom, alpha;
+	dpoint_t p2, p3, p4;
+
+	if (sign == '-') {
+		/* reverse orientation of negative paths */
+		for (i = 0, j = m - 1; i < j; i++, j--) {
+			dpoint_t tmp;
+			tmp = curve->vertex[i];
+			curve->vertex[i] = curve->vertex[j];
+			curve->vertex[j] = tmp;
+		}
+	}
+
+	/* examine each vertex and find its best fit */
+	for (i = 0; i < m; i++) {
+		j = mod(i + 1, m);
+		k = mod(i + 2, m);
+		p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]);
+
+		denom = ddenom(curve->vertex[i], curve->vertex[k]);
+		if (denom != 0.0) {
+			dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom;
+			dd = fabs(dd);
+			alpha = dd > 1 ? (1 - 1.0 / dd) : 0;
+			alpha = alpha / 0.75;
+		}
+		else {
+			alpha = 4 / 3.0;
+		}
+		curve->alpha0[j] = alpha;		/* remember "original" value of alpha */
+
+		if (alpha > alphamax) {			/* pointed corner */
+			curve->tag[j] = POTRACE_CORNER;
+			curve->c[j][1] = curve->vertex[j];
+			curve->c[j][2] = p4;
+		}
+		else {
+			if (alpha < 0.55) {
+				alpha = 0.55;
+			}
+			else if (alpha > 1) {
+				alpha = 1;
+			}
+			p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]);
+			p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]);
+			curve->tag[j] = POTRACE_CURVETO;
+			curve->c[j][0] = p2;
+			curve->c[j][1] = p3;
+			curve->c[j][2] = p4;
+		}
+		curve->alpha[j] = alpha;		/* store the "cropped" value of alpha */
+		curve->beta[j] = 0.5;
+	}
+	curve->alphacurve = 1;
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Stage 5: Curve optimization (Sec. 2.4) */
+
+/* a private type for the result of opti_penalty */
+struct opti_s {
+	double pen;										/* penalty */
+	dpoint_t c[2];								/* curve parameters */
+	double t, s;									/* curve parameters */
+	double alpha;									/* curve parameter */
+};
+typedef struct opti_s opti_t;
+
+/* calculate best fit from i+.5 to j+.5.  Assume i<j (cyclically).
+   Return 0 and set badness and parameters (alpha, beta), if
+   possible. Return 1 if impossible. */
+static int opti_penalty(privpath_t * pp, int i, int j, opti_t * res, double opttolerance, int *convc, double *areac)
+{
+	int m = pp->curve.n;
+	int k, k1, k2, conv, i1;
+	double area, alpha, d, d1, d2;
+	dpoint_t p0, p1, p2, p3, pt;
+	double A, R, A1, A2, A3, A4;
+	double s, t;
+
+	/* check convexity, corner-freeness, and maximum bend < 179 degrees */
+
+	if (i == j) {									/* sanity - a full loop can never be an opticurve */
+		return 1;
+	}
+
+	k = i;
+	i1 = mod(i + 1, m);
+	k1 = mod(k + 1, m);
+	conv = convc[k1];
+	if (conv == 0) {
+		return 1;
+	}
+	d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]);
+	for (k = k1; k != j; k = k1) {
+		k1 = mod(k + 1, m);
+		k2 = mod(k + 2, m);
+		if (convc[k1] != conv) {
+			return 1;
+		}
+		if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) {
+			return 1;
+		}
+		if (iprod1
+				(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1],
+				 pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) {
+			return 1;
+		}
+	}
+
+	/* the curve we're working in: */
+	p0 = pp->curve.c[mod(i, m)][2];
+	p1 = pp->curve.vertex[mod(i + 1, m)];
+	p2 = pp->curve.vertex[mod(j, m)];
+	p3 = pp->curve.c[mod(j, m)][2];
+
+	/* determine its area */
+	area = areac[j] - areac[i];
+	area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2;
+	if (i >= j) {
+		area += areac[m];
+	}
+
+	/* find intersection o of p0p1 and p2p3. Let t,s such that o =
+	   interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the
+	   triangle (p0,o,p3). */
+
+	A1 = dpara(p0, p1, p2);
+	A2 = dpara(p0, p1, p3);
+	A3 = dpara(p0, p2, p3);
+	/* A4 = dpara(p1, p2, p3); */
+	A4 = A1 + A3 - A2;
+
+	if (A2 == A1) {								/* this should never happen */
+		return 1;
+	}
+
+	t = A3 / (A3 - A4);
+	s = A2 / (A2 - A1);
+	A = A2 * t / 2.0;
+
+	if (A == 0.0) {								/* this should never happen */
+		return 1;
+	}
+
+	R = area / A;									/* relative area */
+	alpha = 2 - sqrt(4 - R / 0.3);	/* overall alpha for p0-o-p3 curve */
+
+	res->c[0] = interval(t * alpha, p0, p1);
+	res->c[1] = interval(s * alpha, p3, p2);
+	res->alpha = alpha;
+	res->t = t;
+	res->s = s;
+
+	p1 = res->c[0];
+	p2 = res->c[1];								/* the proposed curve is now (p0,p1,p2,p3) */
+
+	res->pen = 0;
+
+	/* calculate penalty */
+	/* check tangency with edges */
+	for (k = mod(i + 1, m); k != j; k = k1) {
+		k1 = mod(k + 1, m);
+		t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]);
+		if (t < -.5) {
+			return 1;
+		}
+		pt = bezier(t, p0, p1, p2, p3);
+		d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]);
+		if (d == 0.0) {							/* this should never happen */
+			return 1;
+		}
+		d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d;
+		if (fabs(d1) > opttolerance) {
+			return 1;
+		}
+		if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) {
+			return 1;
+		}
+		res->pen += sq(d1);
+	}
+
+	/* check corners */
+	for (k = i; k != j; k = k1) {
+		k1 = mod(k + 1, m);
+		t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]);
+		if (t < -.5) {
+			return 1;
+		}
+		pt = bezier(t, p0, p1, p2, p3);
+		d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]);
+		if (d == 0.0) {							/* this should never happen */
+			return 1;
+		}
+		d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d;
+		d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d;
+		d2 *= 0.75 * pp->curve.alpha[k1];
+		if (d2 < 0) {
+			d1 = -d1;
+			d2 = -d2;
+		}
+		if (d1 < d2 - opttolerance) {
+			return 1;
+		}
+		if (d1 < d2) {
+			res->pen += sq(d1 - d2);
+		}
+	}
+
+	return 0;
+}
+
+/* optimize the path p, replacing sequences of Bezier segments by a
+   single segment when possible. Return 0 on success, 1 with errno set
+   on failure. */
+static int
+ATTRIBUTE_UNUSED opticurve(privpath_t * pp, double opttolerance)
+{
+	int m = pp->curve.n;
+	int *pt = NULL;								/* pt[m+1] */
+	double *pen = NULL;						/* pen[m+1] */
+	int *len = NULL;							/* len[m+1] */
+	opti_t *opt = NULL;						/* opt[m+1] */
+	int om;
+	int i, j, r;
+	opti_t o;
+	dpoint_t p0;
+	int i1;
+	double area;
+	double alpha;
+	double *s = NULL;
+	double *t = NULL;
+
+	int *convc = NULL;						/* conv[m]: pre-computed convexities */
+	double *areac = NULL;					/* cumarea[m+1]: cache for fast area computation */
+
+	SAFE_MALLOC(pt, m + 1, int);
+	SAFE_MALLOC(pen, m + 1, double);
+	SAFE_MALLOC(len, m + 1, int);
+	SAFE_MALLOC(opt, m + 1, opti_t);
+	SAFE_MALLOC(convc, m, int);
+	SAFE_MALLOC(areac, m + 1, double);
+
+	/* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */
+	for (i = 0; i < m; i++) {
+		if (pp->curve.tag[i] == POTRACE_CURVETO) {
+			convc[i] = sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)]));
+		}
+		else {
+			convc[i] = 0;
+		}
+	}
+
+	/* pre-calculate areas */
+	area = 0.0;
+	areac[0] = 0.0;
+	p0 = pp->curve.vertex[0];
+	for (i = 0; i < m; i++) {
+		i1 = mod(i + 1, m);
+		if (pp->curve.tag[i1] == POTRACE_CURVETO) {
+			alpha = pp->curve.alpha[i1];
+			area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2;
+			area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2;
+		}
+		areac[i + 1] = area;
+	}
+
+	pt[0] = -1;
+	pen[0] = 0;
+	len[0] = 0;
+
+	/* Fixme: we always start from a fixed point -- should find the best
+	   curve cyclically ### */
+
+	for (j = 1; j <= m; j++) {
+		/* calculate best path from 0 to j */
+		pt[j] = j - 1;
+		pen[j] = pen[j - 1];
+		len[j] = len[j - 1] + 1;
+
+		for (i = j - 2; i >= 0; i--) {
+			r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac);
+			if (r) {
+				break;
+			}
+			if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) {
+				pt[j] = i;
+				pen[j] = pen[i] + o.pen;
+				len[j] = len[i] + 1;
+				opt[j] = o;
+			}
+		}
+	}
+	om = len[m];
+	r = privcurve_init(&pp->ocurve, om);
+	if (r) {
+		goto malloc_error;
+	}
+	SAFE_MALLOC(s, om, double);
+	SAFE_MALLOC(t, om, double);
+
+	j = m;
+	for (i = om - 1; i >= 0; i--) {
+		if (pt[j] == j - 1) {
+			pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)];
+			pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0];
+			pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1];
+			pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2];
+			pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)];
+			pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)];
+			pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)];
+			pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)];
+			s[i] = t[i] = 1.0;
+		}
+		else {
+			pp->ocurve.tag[i] = POTRACE_CURVETO;
+			pp->ocurve.c[i][0] = opt[j].c[0];
+			pp->ocurve.c[i][1] = opt[j].c[1];
+			pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2];
+			pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]);
+			pp->ocurve.alpha[i] = opt[j].alpha;
+			pp->ocurve.alpha0[i] = opt[j].alpha;
+			s[i] = opt[j].s;
+			t[i] = opt[j].t;
+		}
+		j = pt[j];
+	}
+
+	/* calculate beta parameters */
+	for (i = 0; i < om; i++) {
+		i1 = mod(i + 1, om);
+		pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]);
+	}
+	pp->ocurve.alphacurve = 1;
+
+	free(pt);
+	free(pen);
+	free(len);
+	free(opt);
+	free(s);
+	free(t);
+	free(convc);
+	free(areac);
+	return 0;
+
+malloc_error:
+	free(pt);
+	free(pen);
+	free(len);
+	free(opt);
+	free(s);
+	free(t);
+	free(convc);
+	free(areac);
+	return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+double plotpolygon(privpath_t * pp, FILE * f, double scale)
+{
+	int i;
+	int m = pp->m;
+	int *po = pp->po;
+	point_t *pt = pp->pt;
+	/* double scale=1.0/dpi; */
+	double dm = 0;
+
+	if (!m)
+		return 0;
+
+	po = pp->po;
+	pt = pp->pt;
+
+	fprintf(f, "G0 X%f Y%f    (start point)\n", pt[po[0]].x * scale, pt[po[0]].y * scale);
+	fprintf(f, "G1 Z#101\n");
+	for (i = 1; i < m; i++) {
+		fprintf(f, "G1 X%f Y%f\n", pt[po[i]].x * scale, pt[po[i]].y * scale);
+		dm +=
+			sqrt((pt[po[i]].x - pt[po[i - 1]].x) * scale * (pt[po[i]].x -
+																											pt[po[i - 1]].x) *
+					 scale + (pt[po[i]].y - pt[po[i - 1]].y) * scale * (pt[po[i]].y - pt[po[i - 1]].y) * scale);
+	}
+	fprintf(f, "G1 X%f Y%f\n", pt[po[0]].x * scale, pt[po[0]].y * scale);
+	fprintf(f, "G0 Z#100\n");
+	dm +=
+		sqrt((pt[po[m - 1]].x - pt[po[0]].x) * scale * (pt[po[m - 1]].x -
+																										pt[po[0]].x) * scale +
+				 (pt[po[m - 1]].y - pt[po[0]].y) * scale * (pt[po[m - 1]].y - pt[po[0]].y) * scale);
+	fprintf(f, "(polygon end, distance %.2f)\n", dm);
+	return dm;
+}
+
+#define TRY(x) if (x) goto try_error
+
+/* return distance on success, -1 on error with errno set. */
+double process_path(path_t * plist, const potrace_param_t * param, const potrace_bitmap_t * bm, FILE * f, double scale)
+{
+	path_t *p;
+	double dm = 0;
+	int n = 0;
+	/* call downstream function with each path */
+	list_forall(p, plist) {
+		TRY(calc_sums(p->priv));
+		TRY(calc_lon(p->priv));
+		TRY(bestpolygon(p->priv));
+		TRY(adjust_vertices(p->priv));
+		fprintf(f, "(polygon %d)\n", ++n);
+		dm += plotpolygon(p->priv, f, scale);
+/*  No need to extract curves
+	TRY(smooth(&p->priv->curve, p->sign, param->alphamax));
+    if (param->opticurve) {
+      TRY(opticurve(p->priv, param->opttolerance));
+      p->priv->fcurve = &p->priv->ocurve;
+    } else {
+      p->priv->fcurve = &p->priv->curve;
+    }
+    privcurve_to_curve(p->priv->fcurve, &p->curve);*/
+	}
+/*      fprintf(f,"(end, total distance %.2fmm = %.2fin)\n",25.4*dm,dm); */
+	return dm;
+
+try_error:
+	return -1;
+}
diff --git a/src_plugins/export_gcode/trace.h b/src_plugins/export_gcode/trace.h
new file mode 100644
index 0000000..e3591e4
--- /dev/null
+++ b/src_plugins/export_gcode/trace.h
@@ -0,0 +1,14 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+   This file is part of Potrace. It is free software and it is covered
+   by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: trace.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include "potracelib.h"
+
+double process_path(path_t * plist, const potrace_param_t * param, const potrace_bitmap_t * bm, FILE * f, double scale);
+
+#endif /* TRACE_H */
diff --git a/src_plugins/export_gerber/Makefile b/src_plugins/export_gerber/Makefile
new file mode 100644
index 0000000..14bcdf0
--- /dev/null
+++ b/src_plugins/export_gerber/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_gerber
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_gerber/Plug.tmpasm b/src_plugins/export_gerber/Plug.tmpasm
new file mode 100644
index 0000000..9d97c15
--- /dev/null
+++ b/src_plugins/export_gerber/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_gerber}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_gerber/gerber.o @]
+
+switch /local/pcb/export_gerber/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_gerber/README b/src_plugins/export_gerber/README
new file mode 100644
index 0000000..65fe3a7
--- /dev/null
+++ b/src_plugins/export_gerber/README
@@ -0,0 +1,5 @@
+Export to gerber
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_gerber/gerber.c b/src_plugins/export_gerber/gerber.c
new file mode 100644
index 0000000..e594320
--- /dev/null
+++ b/src_plugins/export_gerber/gerber.c
@@ -0,0 +1,1201 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+
+#include <time.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "error.h"
+#include "draw.h"
+#include "layer.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_helper.h"
+#include "compat_misc.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_flags.h"
+#include "conf_core.h"
+
+const char *gerber_cookie = "gerber HID";
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented Gerber function %s.\n", func); abort()
+
+/*----------------------------------------------------------------------------*/
+/* Function prototypes                                                        */
+/*----------------------------------------------------------------------------*/
+
+static HID_Attribute *gerber_get_export_options(int *n);
+static void gerber_do_export(HID_Attr_Val * options);
+static void gerber_parse_arguments(int *argc, char ***argv);
+static int gerber_set_layer(const char *name, int group, int empty);
+static hidGC gerber_make_gc(void);
+static void gerber_destroy_gc(hidGC gc);
+static void gerber_use_mask(int use_it);
+static void gerber_set_color(hidGC gc, const char *name);
+static void gerber_set_line_cap(hidGC gc, EndCapStyle style);
+static void gerber_set_line_width(hidGC gc, Coord width);
+static void gerber_set_draw_xor(hidGC gc, int _xor);
+static void gerber_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void gerber_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle);
+static void gerber_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void gerber_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+static void gerber_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void gerber_calibrate(double xval, double yval);
+static void gerber_set_crosshair(int x, int y, int action);
+static void gerber_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y);
+
+/*----------------------------------------------------------------------------*/
+/* Utility routines                                                           */
+/*----------------------------------------------------------------------------*/
+
+/* These are for films */
+#define gerberX(pcb, x) ((Coord) (x))
+#define gerberY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)))
+#define gerberXOffset(pcb, x) ((Coord) (x))
+#define gerberYOffset(pcb, y) ((Coord) (-(y)))
+
+/* These are for drills (printed as mils but are really 1/10th mil) */
+#define gerberDrX(pcb, x) ((Coord) (x) * 10)
+#define gerberDrY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)) * 10)
+
+/*----------------------------------------------------------------------------*/
+/* Private data structures                                                    */
+/*----------------------------------------------------------------------------*/
+
+static int verbose;
+static int all_layers;
+static int is_mask, was_drill;
+static int is_drill;
+static int current_mask;
+static int flash_drills;
+static int copy_outline_mode;
+static int name_style;
+static LayerTypePtr outline_layer;
+
+enum ApertureShape {
+	ROUND,												/* Shaped like a circle */
+	OCTAGON,											/* octagonal shape */
+	SQUARE,												/* Shaped like a square */
+	ROUNDCLEAR,										/* clearance in negatives */
+	SQUARECLEAR,
+	THERMAL												/* negative thermal relief */
+};
+typedef enum ApertureShape ApertureShape;
+
+/* This is added to the global aperture array indexes to get gerber
+   dcode and macro numbers.  */
+#define DCODE_BASE 11
+
+typedef struct aperture {
+	int dCode;										/* The RS-274X D code */
+	Coord width;									/* Size in pcb units */
+	ApertureShape shape;					/* ROUND/SQUARE etc */
+	struct aperture *next;
+} Aperture;
+
+typedef struct {
+	Aperture *data;
+	int count;
+} ApertureList;
+
+static ApertureList *layer_aptr_list;
+static ApertureList *curr_aptr_list;
+static int layer_list_max;
+static int layer_list_idx;
+
+typedef struct {
+	Coord diam;
+	Coord x;
+	Coord y;
+} PendingDrills;
+PendingDrills *pending_drills = NULL;
+int n_pending_drills = 0, max_pending_drills = 0;
+
+/*----------------------------------------------------------------------------*/
+/* Defined Constants                                                          */
+/*----------------------------------------------------------------------------*/
+#define AUTO_OUTLINE_WIDTH PCB_MIL_TO_COORD(8)	/* Auto-geneated outline width of 8 mils */
+
+/*----------------------------------------------------------------------------*/
+/* Aperture Routines                                                          */
+/*----------------------------------------------------------------------------*/
+
+/* Initialize aperture list */
+static void initApertureList(ApertureList * list)
+{
+	list->data = NULL;
+	list->count = 0;
+}
+
+static void deinitApertureList(ApertureList * list)
+{
+	Aperture *search = list->data;
+	Aperture *next;
+	while (search) {
+		next = search->next;
+		free(search);
+		search = next;
+	}
+	initApertureList(list);
+}
+
+static void resetApertures()
+{
+	int i;
+	for (i = 0; i < layer_list_max; ++i)
+		deinitApertureList(&layer_aptr_list[i]);
+	free(layer_aptr_list);
+	layer_aptr_list = NULL;
+	curr_aptr_list = NULL;
+	layer_list_max = 0;
+	layer_list_idx = 0;
+}
+
+/* Create and add a new aperture to the list */
+static Aperture *addAperture(ApertureList * list, Coord width, ApertureShape shape)
+{
+	static int aperture_count;
+
+	Aperture *app = (Aperture *) malloc(sizeof *app);
+	if (app == NULL)
+		return NULL;
+
+	app->width = width;
+	app->shape = shape;
+	app->dCode = DCODE_BASE + aperture_count++;
+	app->next = list->data;
+
+	list->data = app;
+	++list->count;
+
+	return app;
+}
+
+/* Fetch an aperture from the list with the specified
+ *  width/shape, creating a new one if none exists */
+static Aperture *findAperture(ApertureList * list, Coord width, ApertureShape shape)
+{
+	Aperture *search;
+
+	/* we never draw zero-width lines */
+	if (width == 0)
+		return NULL;
+
+	/* Search for an appropriate aperture. */
+	for (search = list->data; search; search = search->next)
+		if (search->width == width && search->shape == shape)
+			return search;
+
+	/* Failing that, create a new one */
+	return addAperture(list, width, shape);
+}
+
+/* Output aperture data to the file */
+static void fprintAperture(FILE * f, Aperture * aptr)
+{
+	switch (aptr->shape) {
+	case ROUND:
+		pcb_fprintf(f, "%%ADD%dC,%.4mi*%%\r\n", aptr->dCode, aptr->width);
+		break;
+	case SQUARE:
+		pcb_fprintf(f, "%%ADD%dR,%.4miX%.4mi*%%\r\n", aptr->dCode, aptr->width, aptr->width);
+		break;
+	case OCTAGON:
+		pcb_fprintf(f, "%%AMOCT%d*5,0,8,0,0,%.4mi,22.5*%%\r\n"
+								"%%ADD%dOCT%d*%%\r\n", aptr->dCode, (Coord) ((double) aptr->width / PCB_COS_22_5_DEGREE), aptr->dCode, aptr->dCode);
+		break;
+#if 0
+	case THERMAL:
+		fprintf(f, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\r\n"
+						"%%ADD%dTHERM%d*%%\r\n", dCode, gap / 100000.0, width / 100000.0, finger / 100000.0, dCode, dCode);
+		break;
+	case ROUNDCLEAR:
+		fprintf(f, "%%ADD%dC,%.4fX%.4f*%%\r\n", dCode, gap / 100000.0, width / 100000.0);
+		break;
+	case SQUARECLEAR:
+		fprintf(f, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\r\n",
+						dCode, gap / 100000.0, gap / 100000.0, width / 100000.0, width / 100000.0);
+		break;
+#else
+	default:
+		break;
+#endif
+	}
+}
+
+/* Set the aperture list for the current layer,
+ * expanding the list buffer if needed  */
+static ApertureList *setLayerApertureList(int layer_idx)
+{
+	if (layer_idx >= layer_list_max) {
+		int i = layer_list_max;
+		layer_list_max = 2 * (layer_idx + 1);
+		layer_aptr_list = (ApertureList *)
+			realloc(layer_aptr_list, layer_list_max * sizeof(*layer_aptr_list));
+		for (; i < layer_list_max; ++i)
+			initApertureList(&layer_aptr_list[i]);
+	}
+	curr_aptr_list = &layer_aptr_list[layer_idx];
+	return curr_aptr_list;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static HID gerber_hid;
+
+typedef struct hid_gc_struct {
+	EndCapStyle cap;
+	int width;
+	int color;
+	int erase;
+	int drill;
+} hid_gc_struct;
+
+static FILE *f = NULL;
+static char *filename = NULL;
+static char *filesuff = NULL;
+static char *layername = NULL;
+static int lncount = 0;
+
+static int finding_apertures = 0;
+static int pagecount = 0;
+static int linewidth = -1;
+static int lastgroup = -1;
+static int lastcap = -1;
+static int lastcolor = -1;
+static int print_group[MAX_LAYER];
+static int print_layer[MAX_LAYER];
+static int lastX, lastY;				/* the last X and Y coordinate */
+
+static const char *copy_outline_names[] = {
+#define COPY_OUTLINE_NONE 0
+	"none",
+#define COPY_OUTLINE_MASK 1
+	"mask",
+#define COPY_OUTLINE_SILK 2
+	"silk",
+#define COPY_OUTLINE_ALL 3
+	"all",
+	NULL
+};
+
+static const char *name_style_names[] = {
+#define NAME_STYLE_FIXED 0
+	"fixed",
+#define NAME_STYLE_SINGLE 1
+	"single",
+#define NAME_STYLE_FIRST 2
+	"first",
+#define NAME_STYLE_EAGLE 3
+	"eagle",
+	NULL
+};
+
+static HID_Attribute gerber_options[] = {
+
+/* %start-doc options "90 Gerber Export"
+ at ftable @code
+ at item --gerberfile <string>
+Gerber output file prefix. Can include a path.
+ at end ftable
+%end-doc
+*/
+	{"gerberfile", "Gerber output file base",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_gerberfile 0
+
+/* %start-doc options "90 Gerber Export"
+ at ftable @code
+ at item --all-layers
+Output contains all layers, even empty ones.
+ at end ftable
+%end-doc
+*/
+	{"all-layers", "Output all layers, even empty ones",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_all_layers 1
+
+/* %start-doc options "90 Gerber Export"
+ at ftable @code
+ at item --verbose
+Print file names and aperture counts on stdout.
+ at end ftable
+%end-doc
+*/
+	{"verbose", "Print file names and aperture counts on stdout",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_verbose 2
+	{"copy-outline", "Copy outline onto other layers",
+	 HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0},
+#define HA_copy_outline 3
+	{"name-style", "Naming style for individual gerber files",
+	 HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0},
+#define HA_name_style 4
+};
+
+#define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0]))
+
+static HID_Attr_Val gerber_values[NUM_OPTIONS];
+
+static HID_Attribute *gerber_get_export_options(int *n)
+{
+	static char *last_made_filename = NULL;
+	if (PCB)
+		derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return gerber_options;
+}
+
+static int group_for_layer(int l)
+{
+	if (l < max_copper_layer + 2 && l >= 0)
+		return GetLayerGroupNumberByNumber(l);
+	/* else something unique */
+	return max_group + 3 + l;
+}
+
+static int layer_sort(const void *va, const void *vb)
+{
+	int a = *(int *) va;
+	int b = *(int *) vb;
+	int d = group_for_layer(b) - group_for_layer(a);
+	if (d)
+		return d;
+	return b - a;
+}
+
+static void maybe_close_f(FILE * f)
+{
+	if (f) {
+		if (was_drill)
+			fprintf(f, "M30\r\n");
+		else
+			fprintf(f, "M02*\r\n");
+		fclose(f);
+	}
+}
+
+static BoxType region;
+
+/* Very similar to layer_type_to_file_name() but appends only a
+   three-character suffix compatible with Eagle's defaults.  */
+static void assign_eagle_file_suffix(char *dest, int idx)
+{
+	int group;
+	int nlayers;
+	const char *suff = "out";
+
+	switch (idx) {
+	case SL(SILK, TOP):
+		suff = "plc";
+		break;
+	case SL(SILK, BOTTOM):
+		suff = "pls";
+		break;
+	case SL(MASK, TOP):
+		suff = "stc";
+		break;
+	case SL(MASK, BOTTOM):
+		suff = "sts";
+		break;
+	case SL(PDRILL, 0):
+		suff = "drd";
+		break;
+	case SL(UDRILL, 0):
+		suff = "dru";
+		break;
+	case SL(PASTE, TOP):
+		suff = "crc";
+		break;
+	case SL(PASTE, BOTTOM):
+		suff = "crs";
+		break;
+	case SL(INVISIBLE, 0):
+		suff = "inv";
+		break;
+	case SL(FAB, 0):
+		suff = "fab";
+		break;
+	case SL(ASSY, TOP):
+		suff = "ast";
+		break;
+	case SL(ASSY, BOTTOM):
+		suff = "asb";
+		break;
+
+	default:
+		group = GetLayerGroupNumberByNumber(idx);
+		nlayers = PCB->LayerGroups.Number[group];
+		if (group == GetLayerGroupNumberByNumber(component_silk_layer)) {
+			suff = "cmp";
+		}
+		else if (group == GetLayerGroupNumberByNumber(solder_silk_layer)) {
+			suff = "sol";
+		}
+		else if (nlayers == 1
+						 && (strcmp(PCB->Data->Layer[idx].Name, "route") == 0 || strcmp(PCB->Data->Layer[idx].Name, "outline") == 0)) {
+			suff = "oln";
+		}
+		else {
+			static char buf[20];
+			sprintf(buf, "ly%d", group);
+			suff = buf;
+		}
+		break;
+	}
+
+	strcpy(dest, suff);
+}
+
+static void assign_file_suffix(char *dest, int idx)
+{
+	int fns_style;
+	const char *sext = ".gbr";
+
+	switch (name_style) {
+	default:
+	case NAME_STYLE_FIXED:
+		fns_style = FNS_fixed;
+		break;
+	case NAME_STYLE_SINGLE:
+		fns_style = FNS_single;
+		break;
+	case NAME_STYLE_FIRST:
+		fns_style = FNS_first;
+		break;
+	case NAME_STYLE_EAGLE:
+		assign_eagle_file_suffix(dest, idx);
+		return;
+	}
+
+	switch (idx) {
+	case SL(PDRILL, 0):
+		sext = ".cnc";
+		break;
+	case SL(UDRILL, 0):
+		sext = ".cnc";
+		break;
+	}
+
+	strcpy(dest, layer_type_to_file_name(idx, fns_style));
+	strcat(dest, sext);
+}
+
+static void gerber_do_export(HID_Attr_Val * options)
+{
+	const char *fnbase;
+	int i;
+	static int saved_layer_stack[MAX_LAYER];
+	int save_ons[MAX_LAYER + 2];
+
+	conf_force_set_bool(conf_core.editor.thin_draw, 0);
+	conf_force_set_bool(conf_core.editor.thin_draw_poly, 0);
+	conf_force_set_bool(conf_core.editor.check_planes, 0);
+
+	if (!options) {
+		gerber_get_export_options(NULL);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			gerber_values[i] = gerber_options[i].default_val;
+		options = gerber_values;
+	}
+
+	fnbase = options[HA_gerberfile].str_value;
+	if (!fnbase)
+		fnbase = "pcb-out";
+
+	verbose = options[HA_verbose].int_value;
+	all_layers = options[HA_all_layers].int_value;
+
+	copy_outline_mode = options[HA_copy_outline].int_value;
+	name_style = options[HA_name_style].int_value;
+
+	outline_layer = NULL;
+
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *layer = PCB->Data->Layer + i;
+		if (strcmp(layer->Name, "outline") == 0 || strcmp(layer->Name, "route") == 0) {
+			outline_layer = layer;
+		}
+	}
+
+	i = strlen(fnbase);
+	filename = (char *) realloc(filename, i + 40);
+	strcpy(filename, fnbase);
+	strcat(filename, ".");
+	filesuff = filename + strlen(filename);
+
+	if (all_layers) {
+		memset(print_group, 1, sizeof(print_group));
+		memset(print_layer, 1, sizeof(print_layer));
+	}
+	else {
+		memset(print_group, 0, sizeof(print_group));
+		memset(print_layer, 0, sizeof(print_layer));
+	}
+
+	hid_save_and_show_layer_ons(save_ons);
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *layer = PCB->Data->Layer + i;
+		if (!LAYER_IS_EMPTY(layer))
+			print_group[GetLayerGroupNumberByNumber(i)] = 1;
+	}
+	print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1;
+	print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1;
+	for (i = 0; i < max_copper_layer; i++)
+		if (print_group[GetLayerGroupNumberByNumber(i)])
+			print_layer[i] = 1;
+
+	memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack));
+	qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort);
+	linewidth = -1;
+	lastcap = -1;
+	lastgroup = -1;
+	lastcolor = -1;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	pagecount = 1;
+	resetApertures();
+
+	lastgroup = -1;
+	layer_list_idx = 0;
+	finding_apertures = 1;
+	hid_expose_callback(&gerber_hid, &region, 0);
+
+	layer_list_idx = 0;
+	finding_apertures = 0;
+	hid_expose_callback(&gerber_hid, &region, 0);
+
+	memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack));
+
+	maybe_close_f(f);
+	f = NULL;
+	hid_restore_layer_ons(save_ons);
+	conf_update(NULL); /* resotre forced sets */
+}
+
+static void gerber_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(gerber_options, NUM_OPTIONS, gerber_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static int drill_sort(const void *va, const void *vb)
+{
+	PendingDrills *a = (PendingDrills *) va;
+	PendingDrills *b = (PendingDrills *) vb;
+	if (a->diam != b->diam)
+		return a->diam - b->diam;
+	if (a->x != b->x)
+		return a->x - a->x;
+	return b->y - b->y;
+}
+
+static int gerber_set_layer(const char *name, int group, int empty)
+{
+	int want_outline;
+	char *cp;
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+
+	if (name == NULL)
+		name = PCB->Data->Layer[idx].Name;
+
+	if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
+		return 0;
+
+	if (strcmp(name, "invisible") == 0)
+		return 0;
+	if (SL_TYPE(idx) == SL_ASSY)
+		return 0;
+
+	flash_drills = 0;
+	if (strcmp(name, "outline") == 0 || strcmp(name, "route") == 0)
+		flash_drills = 1;
+
+	if (is_drill && n_pending_drills) {
+		int i;
+		/* dump pending drills in sequence */
+		qsort(pending_drills, n_pending_drills, sizeof(pending_drills[0]), drill_sort);
+		for (i = 0; i < n_pending_drills; i++) {
+			if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam) {
+				Aperture *ap = findAperture(curr_aptr_list, pending_drills[i].diam, ROUND);
+				fprintf(f, "T%02d\r\n", ap->dCode);
+			}
+			pcb_fprintf(f, "X%06.0mlY%06.0ml\r\n", gerberDrX(PCB, pending_drills[i].x), gerberDrY(PCB, pending_drills[i].y));
+		}
+		free(pending_drills);
+		n_pending_drills = max_pending_drills = 0;
+		pending_drills = NULL;
+	}
+
+	is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	is_mask = (SL_TYPE(idx) == SL_MASK);
+	current_mask = 0;
+#if 0
+	printf("Layer %s group %d drill %d mask %d\n", name, group, is_drill, is_mask);
+#endif
+
+	if (group < 0 || group != lastgroup) {
+		time_t currenttime;
+		char utcTime[64];
+		ApertureList *aptr_list;
+		Aperture *search;
+
+		lastgroup = group;
+		lastX = -1;
+		lastY = -1;
+		lastcolor = 0;
+		linewidth = -1;
+		lastcap = -1;
+
+		aptr_list = setLayerApertureList(layer_list_idx++);
+
+		if (finding_apertures)
+			goto emit_outline;
+
+		if (aptr_list->count == 0 && !all_layers)
+			return 0;
+
+		maybe_close_f(f);
+		f = NULL;
+
+		pagecount++;
+		assign_file_suffix(filesuff, idx);
+		f = fopen(filename, "wb");	/* Binary needed to force CR-LF */
+		if (f == NULL) {
+			Message(PCB_MSG_DEFAULT, "Error:  Could not open %s for writing.\n", filename);
+			return 1;
+		}
+
+		was_drill = is_drill;
+
+		if (verbose) {
+			int c = aptr_list->count;
+			printf("Gerber: %d aperture%s in %s\n", c, c == 1 ? "" : "s", filename);
+		}
+
+		if (is_drill) {
+			/* We omit the ,TZ here because we are not omitting trailing zeros.  Our format is
+			   always six-digit 0.1 mil resolution (i.e. 001100 = 0.11") */
+			fprintf(f, "M48\r\n" "INCH\r\n");
+			for (search = aptr_list->data; search; search = search->next)
+				pcb_fprintf(f, "T%02dC%.3mi\r\n", search->dCode, search->width);
+			fprintf(f, "%%\r\n");
+			/* FIXME */
+			return 1;
+		}
+
+		fprintf(f, "G04 start of page %d for group %d idx %d *\r\n", pagecount, group, idx);
+
+		/* Create a portable timestamp. */
+		currenttime = time(NULL);
+		{
+			/* avoid gcc complaints */
+			const char *fmt = "%c UTC";
+			strftime(utcTime, sizeof utcTime, fmt, gmtime(&currenttime));
+		}
+		/* Print a cute file header at the beginning of each file. */
+		fprintf(f, "G04 Title: %s, %s *\r\n", UNKNOWN(PCB->Name), UNKNOWN(name));
+		fprintf(f, "G04 Creator: pcb-rnd " VERSION " *\r\n");
+		fprintf(f, "G04 CreationDate: %s *\r\n", utcTime);
+
+		/* ID the user. */
+		fprintf(f, "G04 For: %s *\r\n", get_user_name());
+
+		fprintf(f, "G04 Format: Gerber/RS-274X *\r\n");
+		pcb_fprintf(f, "G04 PCB-Dimensions: %.0mc %.0mc *\r\n", PCB->MaxWidth, PCB->MaxHeight);
+		fprintf(f, "G04 PCB-Coordinate-Origin: lower left *\r\n");
+
+		/* Signal data in inches. */
+		fprintf(f, "%%MOIN*%%\r\n");
+
+		/* Signal Leading zero suppression, Absolute Data, 2.5 format */
+		fprintf(f, "%%FSLAX25Y25*%%\r\n");
+
+		/* build a legal identifier. */
+		if (layername)
+			free(layername);
+		layername = pcb_strdup(filesuff);
+		if (strrchr(layername, '.'))
+			*strrchr(layername, '.') = 0;
+
+		for (cp = layername; *cp; cp++) {
+			if (isalnum((int) *cp))
+				*cp = toupper((int) *cp);
+			else
+				*cp = '_';
+		}
+		fprintf(f, "%%LN%s*%%\r\n", layername);
+		lncount = 1;
+
+		for (search = aptr_list->data; search; search = search->next)
+			fprintAperture(f, search);
+		if (aptr_list->count == 0)
+			/* We need to put *something* in the file to make it be parsed
+			   as RS-274X instead of RS-274D. */
+			fprintf(f, "%%ADD11C,0.0100*%%\r\n");
+	}
+
+emit_outline:
+	/* If we're printing a copper layer other than the outline layer,
+	   and we want to "print outlines", and we have an outline layer,
+	   print the outline layer on this layer also.  */
+	want_outline = 0;
+	if (copy_outline_mode == COPY_OUTLINE_MASK && SL_TYPE(idx) == SL_MASK)
+		want_outline = 1;
+	if (copy_outline_mode == COPY_OUTLINE_SILK && SL_TYPE(idx) == SL_SILK)
+		want_outline = 1;
+	if (copy_outline_mode == COPY_OUTLINE_ALL
+			&& (SL_TYPE(idx) == SL_SILK
+					|| SL_TYPE(idx) == SL_MASK || SL_TYPE(idx) == SL_FAB || SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == 0))
+		want_outline = 1;
+
+	if (want_outline && strcmp(name, "outline")
+			&& strcmp(name, "route")) {
+		if (outline_layer && outline_layer != PCB->Data->Layer + idx)
+			DrawLayer(outline_layer, &region);
+		else if (!outline_layer) {
+			hidGC gc = gui->make_gc();
+			printf("name %s idx %d\n", name, idx);
+			if (SL_TYPE(idx) == SL_SILK)
+				gui->set_line_width(gc, PCB->minSlk);
+			else if (group >= 0)
+				gui->set_line_width(gc, PCB->minWid);
+			else
+				gui->set_line_width(gc, AUTO_OUTLINE_WIDTH);
+			gui->draw_line(gc, 0, 0, PCB->MaxWidth, 0);
+			gui->draw_line(gc, 0, 0, 0, PCB->MaxHeight);
+			gui->draw_line(gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight);
+			gui->draw_line(gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
+			gui->destroy_gc(gc);
+		}
+	}
+
+	return 1;
+}
+
+static hidGC gerber_make_gc(void)
+{
+	hidGC rv = (hidGC) calloc(1, sizeof(*rv));
+	rv->cap = Trace_Cap;
+	return rv;
+}
+
+static void gerber_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void gerber_use_mask(int use_it)
+{
+	current_mask = use_it;
+}
+
+static void gerber_set_color(hidGC gc, const char *name)
+{
+	if (strcmp(name, "erase") == 0) {
+		gc->color = 1;
+		gc->erase = 1;
+		gc->drill = 0;
+	}
+	else if (strcmp(name, "drill") == 0) {
+		gc->color = 1;
+		gc->erase = 0;
+		gc->drill = 1;
+	}
+	else {
+		gc->color = 0;
+		gc->erase = 0;
+		gc->drill = 0;
+	}
+}
+
+static void gerber_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void gerber_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void gerber_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+static void use_gc(hidGC gc, int radius)
+{
+	if (radius) {
+		radius *= 2;
+		if (radius != linewidth || lastcap != Round_Cap) {
+			Aperture *aptr = findAperture(curr_aptr_list, radius, ROUND);
+			if (aptr == NULL)
+				pcb_fprintf(stderr, "error: aperture for radius %$mS type ROUND is null\n", radius);
+			else if (f && !is_drill)
+				fprintf(f, "G54D%d*", aptr->dCode);
+			linewidth = radius;
+			lastcap = Round_Cap;
+		}
+	}
+	else if (linewidth != gc->width || lastcap != gc->cap) {
+		Aperture *aptr;
+		ApertureShape shape;
+
+		linewidth = gc->width;
+		lastcap = gc->cap;
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			shape = ROUND;
+			break;
+		default:
+		case Square_Cap:
+			shape = SQUARE;
+			break;
+		}
+		aptr = findAperture(curr_aptr_list, linewidth, shape);
+		if (aptr == NULL)
+			pcb_fprintf(stderr, "error: aperture for width %$mS type %s is null\n", linewidth, shape == ROUND ? "ROUND" : "SQUARE");
+		if (f)
+			fprintf(f, "G54D%d*", aptr->dCode);
+	}
+#if 0
+	if (lastcolor != gc->color) {
+		c = gc->color;
+		if (is_drill)
+			return;
+		if (is_mask)
+			c = (gc->erase ? 0 : 1);
+		lastcolor = gc->color;
+		if (f) {
+			if (c) {
+				fprintf(f, "%%LN%s_C%d*%%\r\n", layername, lncount++);
+				fprintf(f, "%%LPC*%%\r\n");
+			}
+			else {
+				fprintf(f, "%%LN%s_D%d*%%\r\n", layername, lncount++);
+				fprintf(f, "%%LPD*%%\r\n");
+			}
+		}
+	}
+#endif
+}
+
+static void gerber_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	gerber_draw_line(gc, x1, y1, x1, y2);
+	gerber_draw_line(gc, x1, y1, x2, y1);
+	gerber_draw_line(gc, x1, y2, x2, y2);
+	gerber_draw_line(gc, x2, y1, x2, y2);
+}
+
+static void gerber_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	pcb_bool m = pcb_false;
+
+	if (x1 != x2 && y1 != y2 && gc->cap == Square_Cap) {
+		Coord x[5], y[5];
+		double tx, ty, theta;
+
+		theta = atan2(y2 - y1, x2 - x1);
+
+		/* T is a vector half a thickness long, in the direction of
+		   one of the corners.  */
+		tx = gc->width / 2.0 * cos(theta + M_PI / 4) * sqrt(2.0);
+		ty = gc->width / 2.0 * sin(theta + M_PI / 4) * sqrt(2.0);
+
+		x[0] = x1 - tx;
+		y[0] = y1 - ty;
+		x[1] = x2 + ty;
+		y[1] = y2 - tx;
+		x[2] = x2 + tx;
+		y[2] = y2 + ty;
+		x[3] = x1 - ty;
+		y[3] = y1 + tx;
+
+		x[4] = x[0];
+		y[4] = y[0];
+		gerber_fill_polygon(gc, 5, x, y);
+		return;
+	}
+
+	use_gc(gc, 0);
+	if (!f)
+		return;
+
+	if (x1 != lastX) {
+		m = pcb_true;
+		lastX = x1;
+		pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX));
+	}
+	if (y1 != lastY) {
+		m = pcb_true;
+		lastY = y1;
+		pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY));
+	}
+	if ((x1 == x2) && (y1 == y2))
+		fprintf(f, "D03*\r\n");
+	else {
+		if (m)
+			fprintf(f, "D02*");
+		if (x2 != lastX) {
+			lastX = x2;
+			pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX));
+		}
+		if (y2 != lastY) {
+			lastY = y2;
+			pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY));
+
+		}
+		fprintf(f, "D01*\r\n");
+	}
+
+}
+
+static void gerber_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	pcb_bool m = pcb_false;
+	double arcStartX, arcStopX, arcStartY, arcStopY;
+
+	/* we never draw zero-width lines */
+	if (gc->width == 0)
+		return;
+
+	use_gc(gc, 0);
+	if (!f)
+		return;
+
+	arcStartX = cx - width * cos(TO_RADIANS(start_angle));
+	arcStartY = cy + height * sin(TO_RADIANS(start_angle));
+
+	/* I checked three different gerber viewers, and they all disagreed
+	   on how ellipses should be drawn.  The spec just calls G74/G75
+	   "circular interpolation" so there's a chance it just doesn't
+	   support ellipses at all.  Thus, we draw them out with line
+	   segments.  Note that most arcs in pcb are circles anyway.  */
+	if (width != height) {
+		double step, angle;
+		Coord max = width > height ? width : height;
+		Coord minr = max - gc->width / 10;
+		int nsteps;
+		Coord x0, y0, x1, y1;
+
+		if (minr >= max)
+			minr = max - 1;
+		step = acos((double) minr / (double) max) * 180.0 / M_PI;
+		if (step > 5)
+			step = 5;
+		nsteps = fabs(delta_angle) / step + 1;
+		step = (double) delta_angle / nsteps;
+
+		x0 = arcStartX;
+		y0 = arcStartY;
+		angle = start_angle;
+		while (nsteps > 0) {
+			nsteps--;
+			x1 = cx - width * cos(TO_RADIANS(angle + step));
+			y1 = cy + height * sin(TO_RADIANS(angle + step));
+			gerber_draw_line(gc, x0, y0, x1, y1);
+			x0 = x1;
+			y0 = y1;
+			angle += step;
+		}
+		return;
+	}
+
+	arcStopX = cx - width * cos(TO_RADIANS(start_angle + delta_angle));
+	arcStopY = cy + height * sin(TO_RADIANS(start_angle + delta_angle));
+	if (arcStartX != lastX) {
+		m = pcb_true;
+		lastX = arcStartX;
+		pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX));
+	}
+	if (arcStartY != lastY) {
+		m = pcb_true;
+		lastY = arcStartY;
+		pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY));
+	}
+	if (m)
+		fprintf(f, "D02*");
+	pcb_fprintf(f,
+							"G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n",
+							(delta_angle < 0) ? 2 : 3,
+							gerberX(PCB, arcStopX), gerberY(PCB, arcStopY),
+							gerberXOffset(PCB, cx - arcStartX), gerberYOffset(PCB, cy - arcStartY));
+	lastX = arcStopX;
+	lastY = arcStopY;
+}
+
+static void gerber_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	if (radius <= 0)
+		return;
+	if (is_drill)
+		radius = 50 * pcb_round(radius / 50.0);
+	use_gc(gc, radius);
+	if (!f)
+		return;
+	if (is_drill) {
+		if (n_pending_drills >= max_pending_drills) {
+			max_pending_drills += 100;
+			pending_drills = (PendingDrills *) realloc(pending_drills, max_pending_drills * sizeof(pending_drills[0]));
+		}
+		pending_drills[n_pending_drills].x = cx;
+		pending_drills[n_pending_drills].y = cy;
+		pending_drills[n_pending_drills].diam = radius * 2;
+		n_pending_drills++;
+		return;
+	}
+	else if (gc->drill && !flash_drills)
+		return;
+	if (cx != lastX) {
+		lastX = cx;
+		pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX));
+	}
+	if (cy != lastY) {
+		lastY = cy;
+		pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY));
+	}
+	fprintf(f, "D03*\r\n");
+}
+
+static void gerber_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	pcb_bool m = pcb_false;
+	int i;
+	int firstTime = 1;
+	Coord startX = 0, startY = 0;
+
+	if (is_mask && current_mask == HID_MASK_BEFORE)
+		return;
+
+	use_gc(gc, 10 * 100);
+	if (!f)
+		return;
+	fprintf(f, "G36*\r\n");
+	for (i = 0; i < n_coords; i++) {
+		if (x[i] != lastX) {
+			m = pcb_true;
+			lastX = x[i];
+			pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX));
+		}
+		if (y[i] != lastY) {
+			m = pcb_true;
+			lastY = y[i];
+			pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY));
+		}
+		if (firstTime) {
+			firstTime = 0;
+			startX = x[i];
+			startY = y[i];
+			if (m)
+				fprintf(f, "D02*");
+		}
+		else if (m)
+			fprintf(f, "D01*\r\n");
+		m = pcb_false;
+	}
+	if (startX != lastX) {
+		m = pcb_true;
+		lastX = startX;
+		pcb_fprintf(f, "X%.0mc", gerberX(PCB, startX));
+	}
+	if (startY != lastY) {
+		m = pcb_true;
+		lastY = startY;
+		pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY));
+	}
+	if (m)
+		fprintf(f, "D01*\r\n");
+	fprintf(f, "G37*\r\n");
+}
+
+static void gerber_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	Coord x[5];
+	Coord y[5];
+	x[0] = x[4] = x1;
+	y[0] = y[4] = y1;
+	x[1] = x1;
+	y[1] = y2;
+	x[2] = x2;
+	y[2] = y2;
+	x[3] = x2;
+	y[3] = y1;
+	gerber_fill_polygon(gc, 5, x, y);
+}
+
+static void gerber_calibrate(double xval, double yval)
+{
+	CRASH("gerber_calibrate");
+}
+
+static int gerber_usage(const char *topic)
+{
+	fprintf(stderr, "\ngerber exporter command line arguments:\n\n");
+	hid_usage(gerber_options, sizeof(gerber_options) / sizeof(gerber_options[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x gerber foo.pcb [gerber options]\n\n");
+	return 0;
+}
+
+static void gerber_set_crosshair(int x, int y, int action)
+{
+}
+
+pcb_uninit_t hid_export_gerber_init()
+{
+	memset(&gerber_hid, 0, sizeof(gerber_hid));
+
+	common_nogui_init(&gerber_hid);
+	common_draw_helpers_init(&gerber_hid);
+
+	gerber_hid.struct_size = sizeof(gerber_hid);
+	gerber_hid.name = "gerber";
+	gerber_hid.description = "RS-274X (Gerber) export";
+	gerber_hid.exporter = 1;
+
+	gerber_hid.get_export_options = gerber_get_export_options;
+	gerber_hid.do_export = gerber_do_export;
+	gerber_hid.parse_arguments = gerber_parse_arguments;
+	gerber_hid.set_layer = gerber_set_layer;
+	gerber_hid.make_gc = gerber_make_gc;
+	gerber_hid.destroy_gc = gerber_destroy_gc;
+	gerber_hid.use_mask = gerber_use_mask;
+	gerber_hid.set_color = gerber_set_color;
+	gerber_hid.set_line_cap = gerber_set_line_cap;
+	gerber_hid.set_line_width = gerber_set_line_width;
+	gerber_hid.set_draw_xor = gerber_set_draw_xor;
+	gerber_hid.draw_line = gerber_draw_line;
+	gerber_hid.draw_arc = gerber_draw_arc;
+	gerber_hid.draw_rect = gerber_draw_rect;
+	gerber_hid.fill_circle = gerber_fill_circle;
+	gerber_hid.fill_polygon = gerber_fill_polygon;
+	gerber_hid.fill_rect = gerber_fill_rect;
+	gerber_hid.calibrate = gerber_calibrate;
+	gerber_hid.set_crosshair = gerber_set_crosshair;
+	gerber_hid.usage = gerber_usage;
+
+	hid_register_hid(&gerber_hid);
+	return NULL;
+}
diff --git a/src_plugins/export_ipcd356/Makefile b/src_plugins/export_ipcd356/Makefile
new file mode 100644
index 0000000..f816b9f
--- /dev/null
+++ b/src_plugins/export_ipcd356/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_ipcd356
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_ipcd356/Plug.tmpasm b/src_plugins/export_ipcd356/Plug.tmpasm
new file mode 100644
index 0000000..0bd26fd
--- /dev/null
+++ b/src_plugins/export_ipcd356/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_ipcd356}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_ipcd356/ipcd356.o @]
+
+switch /local/pcb/export_ipcd356/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_ipcd356/README b/src_plugins/export_ipcd356/README
new file mode 100644
index 0000000..006b720
--- /dev/null
+++ b/src_plugins/export_ipcd356/README
@@ -0,0 +1,5 @@
+IPC-D-356 Netlist export.
+
+#state: Work-in-progress
+#default: disable
+#implements: export
diff --git a/src_plugins/export_ipcd356/ipcd356.c b/src_plugins/export_ipcd356/ipcd356.c
new file mode 100644
index 0000000..de3042b
--- /dev/null
+++ b/src_plugins/export_ipcd356/ipcd356.c
@@ -0,0 +1,627 @@
+/*!
+ *
+ * \brief IPC-D-356 Netlist export.
+ *
+ * \author Copyright (C) 2012 Jerome Marchand (Jerome.Marchand at gmail.com)
+ *
+ * <hr>
+ *
+ * <h1><b>Copyright.</b></h1>\n
+ *
+ * PCB, interactive printed circuit board design
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Contact addresses for paper mail and Email:
+ *
+ * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *
+ * Thomas.Nau at rz.uni-ulm.de
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "data.h"
+#include "config.h"
+#include "global.h"
+#include "rats.h"
+#include "error.h"
+#include "find.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "netlist.h"
+#include "conf_core.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_helper.h"
+#include "hid_attrib.h"
+#include "hid_init.h"
+#include "plugins.h"
+
+static const char *ipcd356_cookie = "ipcd356 exporter";
+
+static HID_Attribute IPCD356_options[] = {
+/* %start-doc options "8 IPC-D-356 Netlist Export"
+ at ftable @code
+ at item --netlist-file <string>
+Name of the IPC-D-356 Netlist output file.
+ at end ftable
+%end-doc
+*/
+	{
+	 "netlistfile",
+	 "Name of the IPC-D-356 Netlist output file",
+	 HID_String,
+	 0, 0, {0, 0, 0}, 0, 0},
+#define HA_IPCD356_filename 0
+};
+
+#define NUM_OPTIONS (sizeof(IPCD356_options)/sizeof(IPCD356_options[0]))
+
+static HID_Attr_Val IPCD356_values[NUM_OPTIONS];
+
+const char *IPCD356_filename;
+
+typedef struct {
+	char NName[11];
+	char NetName[256];
+} IPCD356_Alias;
+
+typedef struct {
+	int AliasN;										/*!< Number of entries. */
+	IPCD356_Alias *Alias;
+} IPCD356_AliasList;
+
+void IPCD356_WriteNet(FILE *, char *);
+void IPCD356_WriteHeader(FILE *);
+void IPCD356_End(FILE *);
+int IPCD356_Netlist(void);
+int IPCD356_WriteAliases(FILE *, IPCD356_AliasList *);
+void ResetVisitPinsViasAndPads(void);
+void CheckNetLength(char *, IPCD356_AliasList *);
+IPCD356_AliasList *CreateAliasList(void);
+IPCD356_AliasList *AddAliasToList(IPCD356_AliasList *);
+int IPCD356_SanityCheck(void);
+
+static HID_Attribute *IPCD356_get_export_options(int *n)
+{
+	static char *last_IPCD356_filename = 0;
+
+	if (PCB) {
+		derive_default_filename(PCB->Filename, &IPCD356_options[HA_IPCD356_filename], ".net", &last_IPCD356_filename);
+	}
+
+	if (n)
+		*n = NUM_OPTIONS;
+
+	return IPCD356_options;
+}
+
+/*!
+ * \brief Writes the IPC-D-356 Header to the file provided.
+ *
+ * The JOB name is the PCB Name (if set), otherwise the filename
+ * (including the path) is used.
+ *
+ * The units used for the netlist depends on what is set (mils or mm).
+ */
+void IPCD356_WriteHeader(FILE * fd)
+{
+	time_t currenttime;
+	char utcTime[64];
+	const char *fmt = "%c UTC";
+
+	currenttime = time(NULL);
+	strftime(utcTime, sizeof utcTime, fmt, gmtime(&currenttime));
+
+	fprintf(fd, "C  IPC-D-356 Netlist generated by gEDA pcb-rnd " VERSION "\nC  \n");
+	fprintf(fd, "C  File created on %s\nC  \n", utcTime);
+	if (PCB->Name == NULL) {
+		fprintf(fd, "P  JOB   %s\n", PCB->Filename);	/* Use the file name if the PCB name in not set. */
+	}
+	else {
+		fprintf(fd, "P  JOB   %s\n", PCB->Name);
+	}
+	fprintf(fd, "P  CODE  00\n");
+	if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {	/* Use whatever unit is currently in use (mil or mm). */
+		fprintf(fd, "P  UNITS CUST 0\n");
+	}
+	else {
+		fprintf(fd, "P  UNITS CUST 1\n");
+	}
+	fprintf(fd, "P  DIM   N\n");
+	fprintf(fd, "P  VER   IPC-D-356\n");
+	fprintf(fd, "P  IMAGE PRIMARY\nC  \n");
+}
+
+
+/*!
+ * \brief Writes a net to the file provided.
+ *
+ * The net name is passed through the "net" and should be 14 characters
+ * max.\n
+ * The function scans through pads, pins and vias  and looks for the
+ * \c PCB_FLAG_FOUND.\n
+ * Once the object has been added to the net list the \c PCB_FLAG_VISIT is
+ * set on that object.
+ *
+ * \todo 1) The bottom layer is always written as layer #2 (A02).\n
+ *          It could output the actual layer number (example: A06 on a
+ *          6 layer board).\n
+ *          But I could not find an easy way to do this...
+ *
+ * \todo 2) Objects with mutiple connections could have the "M"
+ *          (column 32) field written to indicate a Mid Net Point.
+ */
+void IPCD356_WriteNet(FILE * fd, char *net)
+{
+	int padx, pady, tmp;
+
+	ELEMENT_LOOP(PCB->Data);
+	PAD_LOOP(element);
+	if (TEST_FLAG(PCB_FLAG_FOUND, pad)) {
+		fprintf(fd, "327%-17.14s", net);	/* Net Name. */
+		fprintf(fd, "%-6.6s", element->Name[1].TextString);	/* Refdes. */
+		fprintf(fd, "-%-4.4s", pad->Number);	/* pin number. */
+		fprintf(fd, " ");						/*! \todo Midpoint indicator (M). */
+		fprintf(fd, "      ");			/* Drilled hole Id (blank for pads). */
+		if (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) == pcb_true) {
+			fprintf(fd, "A02");				/*! \todo Put actual layer # for bottom side. */
+		}
+		else {
+			fprintf(fd, "A01");				/* Top side. */
+		}
+		padx = (pad->Point1.X + pad->Point2.X) / 2;	/* X location in PCB units. */
+		pady = (PCB->MaxHeight - ((pad->Point1.Y + pad->Point2.Y) / 2));	/* Y location in PCB units. */
+
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			padx = padx / 2540;				/* X location in 0.0001". */
+			pady = pady / 2540;				/* Y location in 0.0001". */
+		}
+		else {
+			padx = padx / 1000;				/* X location in 0.001 mm. */
+			pady = pady / 1000;				/* Y location in 0.001 mm. */
+		}
+		fprintf(fd, "X%+6.6d", padx);	/* X Pad center. */
+		fprintf(fd, "Y%+6.6d", pady);	/* Y pad center. */
+
+		padx = (pad->Thickness + (pad->Point2.X - pad->Point1.X));	/* Pad dimension X in PCB units. */
+		pady = (pad->Thickness + (pad->Point2.Y - pad->Point1.Y));	/* Pad dimension Y in PCB units. */
+
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			padx = padx / 2540;				/* X location in 0.0001". */
+			pady = pady / 2540;				/* Y location in 0.0001". */
+		}
+		else {
+			padx = padx / 1000;				/* X location in 0.001mm */
+			pady = pady / 1000;				/* Y location in 0.001mm */
+		}
+
+		fprintf(fd, "X%4.4d", padx);
+		fprintf(fd, "Y%4.4d", pady);
+		fprintf(fd, "R000");				/* Rotation (0 degrees). */
+		fprintf(fd, " ");						/* Column 72 should be left blank. */
+		if (pad->Mask > 0) {
+			if (TEST_FLAG(PCB_FLAG_ONSOLDER, pad) == pcb_true) {
+				fprintf(fd, "S2");			/* Soldermask on bottom side. */
+			}
+			else {
+				fprintf(fd, "S1");			/* SolderMask on top side. */
+			}
+		}
+		else {
+			fprintf(fd, "S3");				/* No soldermask. */
+		}
+		fprintf(fd, "      ");			/* Padding. */
+		fprintf(fd, "\n");
+		SET_FLAG(PCB_FLAG_VISIT, pad);
+	}
+
+	END_LOOP;											/* Pad. */
+	PIN_LOOP(element);
+	if (TEST_FLAG(PCB_FLAG_FOUND, pin)) {
+		if (TEST_FLAG(PCB_FLAG_HOLE, pin)) {	/* Non plated? */
+			fprintf(fd, "367%-17.14s", net);	/* Net Name. */
+		}
+		else {
+			fprintf(fd, "317%-17.14s", net);	/* Net Name. */
+		}
+		fprintf(fd, "%-6.6s", element->Name[1].TextString);	/* Refdes. */
+		fprintf(fd, "-%-4.4s", pin->Number);	/* Pin number. */
+		fprintf(fd, " ");						/*! \todo Midpoint indicator (M). */
+		tmp = pin->DrillingHole;
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			tmp = tmp / 2540;					/* 0.0001". */
+		}
+		else {
+			tmp = tmp / 1000;					/* 0.001 mm. */
+		}
+
+		if (TEST_FLAG(PCB_FLAG_HOLE, pin)) {
+			fprintf(fd, "D%-4.4dU", tmp);	/* Unplated Drilled hole Id. */
+		}
+		else {
+			fprintf(fd, "D%-4.4dP", tmp);	/* Plated drill hole. */
+		}
+		fprintf(fd, "A00");					/* Accessible from both sides. */
+		padx = pin->X;							/* X location in PCB units. */
+		pady = (PCB->MaxHeight - pin->Y);	/* Y location in PCB units. */
+
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			padx = padx / 2540;				/* X location in 0.0001". */
+			pady = pady / 2540;				/* Y location in 0.0001". */
+		}
+		else {
+			padx = padx / 1000;				/* X location in 0.001 mm. */
+			pady = pady / 1000;				/* Y location in 0.001 mm. */
+		}
+
+		fprintf(fd, "X%+6.6d", padx);	/* X Pad center. */
+		fprintf(fd, "Y%+6.6d", pady);	/* Y pad center. */
+
+		padx = pin->Thickness;
+
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			padx = padx / 2540;				/* X location in 0.0001". */
+		}
+		else {
+			padx = padx / 1000;				/* X location in 0.001 mm. */
+		}
+
+		fprintf(fd, "X%4.4d", padx);	/* Pad dimension X. */
+		if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+			fprintf(fd, "Y%4.4d", padx);	/* Pad dimension Y. */
+		}
+		else {
+			fprintf(fd, "Y0000");			/*  Y is 0 for round pins. */
+		}
+		fprintf(fd, "R000");				/* Rotation (0 degrees). */
+		fprintf(fd, " ");						/* Column 72 should be left blank. */
+		if (pin->Mask > 0) {
+			fprintf(fd, "S0");				/* No Soldermask. */
+		}
+		else {
+			fprintf(fd, "S3");				/* Soldermask on both sides. */
+		}
+		fprintf(fd, "      ");			/* Padding. */
+
+		fprintf(fd, "\n");
+
+		SET_FLAG(PCB_FLAG_VISIT, pin);
+
+	}
+
+	END_LOOP;											/* Pin. */
+	END_LOOP;											/* Element */
+
+	VIA_LOOP(PCB->Data);
+	if (TEST_FLAG(PCB_FLAG_FOUND, via)) {
+		if (TEST_FLAG(PCB_FLAG_HOLE, via)) {	/* Non plated ? */
+			fprintf(fd, "367%-17.14s", net);	/* Net Name. */
+		}
+		else {
+			fprintf(fd, "317%-17.14s", net);	/* Net Name. */
+		}
+		fprintf(fd, "VIA   ");			/* Refdes. */
+		fprintf(fd, "-    ");				/* Pin number. */
+		fprintf(fd, " ");						/*! \todo Midpoint indicator (M). */
+		tmp = via->DrillingHole;
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			tmp = tmp / 2540;					/* 0.0001". */
+		}
+		else {
+			tmp = tmp / 1000;					/* 0.001 mm. */
+		}
+
+		if (TEST_FLAG(PCB_FLAG_HOLE, via)) {
+			fprintf(fd, "D%-4.4dU", tmp);	/* Unplated Drilled hole Id. */
+		}
+		else {
+			fprintf(fd, "D%-4.4dP", tmp);	/* Plated drill hole. */
+		}
+		fprintf(fd, "A00");					/* Accessible from both sides. */
+		padx = via->X;							/* X location in PCB units. */
+		pady = (PCB->MaxHeight - via->Y);	/* Y location in PCB units. */
+
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			padx = padx / 2540;				/* X location in 0.0001". */
+			pady = pady / 2540;				/* Y location in 0.0001". */
+		}
+		else {
+			padx = padx / 1000;				/* X location in 0.001 mm. */
+			pady = pady / 1000;				/* Y location in 0.001 mm. */
+		}
+
+		fprintf(fd, "X%+6.6d", padx);	/* X Pad center. */
+		fprintf(fd, "Y%+6.6d", pady);	/* Y pad center. */
+
+		padx = via->Thickness;
+
+		if (strcmp(conf_core.editor.grid_unit->suffix, "mil") == 0) {
+			padx = padx / 2540;				/* X location in 0.0001". */
+		}
+		else {
+			padx = padx / 1000;				/* X location in 0.001 mm. */
+		}
+
+		fprintf(fd, "X%4.4d", padx);	/* Pad dimension X. */
+		fprintf(fd, "Y0000");				/* Y is 0 for round pins (vias always round?). */
+		fprintf(fd, "R000");				/* Rotation (0 degrees). */
+		fprintf(fd, " ");						/* Column 72 should be left blank. */
+		if (via->Mask > 0) {
+			fprintf(fd, "S0");				/* No Soldermask. */
+		}
+		else {
+			fprintf(fd, "S3");				/* Soldermask on both sides. */
+		}
+		fprintf(fd, "      ");			/* Padding. */
+		fprintf(fd, "\n");
+		SET_FLAG(PCB_FLAG_VISIT, via);
+	}
+
+	END_LOOP;											/* Via. */
+}
+
+
+/*!
+ * \brief The main IPC-D-356 function.
+ *
+ * Gets the filename for the netlist from the dialog.
+ */
+int IPCD356_Netlist(void)
+{
+	FILE *fp;
+	char nodename[256];
+	char net[256];
+	LibraryMenuType *netname;
+	IPCD356_AliasList *aliaslist;
+
+	if (IPCD356_SanityCheck()) {	/* Check for invalid names + numbers. */
+		Message(PCB_MSG_ERROR, "IPCD356: aborting on the sanity check.\n");
+		return (1);
+	}
+
+	sprintf(net, "%s.ipc", PCB->Name);
+	if (IPCD356_filename == NULL)
+		return 1;
+
+	fp = fopen(IPCD356_filename, "w+");
+	if (fp == NULL) {
+		Message(PCB_MSG_ERROR, "error opening %s\n", IPCD356_filename);
+		return 1;
+	}
+/*   free (IPCD356_filename); */
+
+
+	IPCD356_WriteHeader(fp);
+
+	aliaslist = CreateAliasList();
+	if (aliaslist == NULL) {
+		Message(PCB_MSG_ERROR, "Error Aloccating memory for IPC-D-356 AliasList\n");
+		return 1;
+	}
+
+	if (IPCD356_WriteAliases(fp, aliaslist)) {
+		Message(PCB_MSG_ERROR, "Error Writing IPC-D-356 AliasList\n");
+		return 1;
+	}
+
+
+	ELEMENT_LOOP(PCB->Data);
+	PIN_LOOP(element);
+	if (!TEST_FLAG(PCB_FLAG_VISIT, pin)) {
+		ClearFlagOnLinesAndPolygons(pcb_true, PCB_FLAG_FOUND);
+		ClearFlagOnPinsViasAndPads(pcb_true, PCB_FLAG_FOUND);
+		LookupConnectionByPin(PCB_TYPE_PIN, pin);
+		sprintf(nodename, "%s-%s", element->Name[1].TextString, pin->Number);
+		netname = pcb_netnode_to_netname(nodename);
+/*      Message(PCB_MSG_INFO, "Netname: %s\n", netname->Name +2); */
+		if (netname) {
+			strcpy(net, &netname->Name[2]);
+			CheckNetLength(net, aliaslist);
+		}
+		else {
+			strcpy(net, "N/C");
+		}
+		IPCD356_WriteNet(fp, net);
+	}
+	END_LOOP;											/* Pin. */
+	PAD_LOOP(element);
+	if (!TEST_FLAG(PCB_FLAG_VISIT, pad)) {
+		ClearFlagOnLinesAndPolygons(pcb_true, PCB_FLAG_FOUND);
+		ClearFlagOnPinsViasAndPads(pcb_true, PCB_FLAG_FOUND);
+		LookupConnectionByPin(PCB_TYPE_PAD, pad);
+		sprintf(nodename, "%s-%s", element->Name[1].TextString, pad->Number);
+		netname = pcb_netnode_to_netname(nodename);
+/*      Message(PCB_MSG_INFO, "Netname: %s\n", netname->Name +2); */
+		if (netname) {
+			strcpy(net, &netname->Name[2]);
+			CheckNetLength(net, aliaslist);
+		}
+		else {
+			strcpy(net, "N/C");
+		}
+		IPCD356_WriteNet(fp, net);
+	}
+	END_LOOP;											/* Pad. */
+
+	END_LOOP;											/* Element. */
+
+	VIA_LOOP(PCB->Data);
+	if (!TEST_FLAG(PCB_FLAG_VISIT, via)) {
+		ClearFlagOnLinesAndPolygons(pcb_true, PCB_FLAG_FOUND);
+		ClearFlagOnPinsViasAndPads(pcb_true, PCB_FLAG_FOUND);
+		LookupConnectionByPin(PCB_TYPE_PIN, via);
+		strcpy(net, "N/C");
+		IPCD356_WriteNet(fp, net);
+	}
+	END_LOOP;											/* Via. */
+
+	IPCD356_End(fp);
+	fclose(fp);
+	free(aliaslist);
+	ResetVisitPinsViasAndPads();
+	ClearFlagOnLinesAndPolygons(pcb_true, PCB_FLAG_FOUND);
+	ClearFlagOnPinsViasAndPads(pcb_true, PCB_FLAG_FOUND);
+	return 0;
+}
+
+void IPCD356_End(FILE * fd)
+{
+	fprintf(fd, "999\n");
+}
+
+void ResetVisitPinsViasAndPads()
+{
+	VIA_LOOP(PCB->Data);
+	CLEAR_FLAG(PCB_FLAG_VISIT, via);
+	END_LOOP;											/* Via. */
+	ELEMENT_LOOP(PCB->Data);
+	PIN_LOOP(element);
+	CLEAR_FLAG(PCB_FLAG_VISIT, pin);
+	END_LOOP;											/* Pin. */
+	PAD_LOOP(element);
+	CLEAR_FLAG(PCB_FLAG_VISIT, pad);
+	END_LOOP;											/* Pad. */
+	END_LOOP;											/* Element. */
+}
+
+int IPCD356_WriteAliases(FILE * fd, IPCD356_AliasList * aliaslist)
+{
+	int index;
+	int i;
+
+	index = 1;
+
+	for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
+		if (strlen(PCB->NetlistLib[NETLIST_EDITED].Menu[i].Name + 2) > 14) {
+			if (index == 1) {
+				fprintf(fd, "C  Netname Aliases Section\n");
+			}
+			aliaslist = AddAliasToList(aliaslist);
+			if (aliaslist == NULL) {
+				return 1;
+			}
+			sprintf(aliaslist->Alias[index].NName, "NNAME%-5.5d", index);
+			strcpy(aliaslist->Alias[index].NetName, PCB->NetlistLib[NETLIST_EDITED].Menu[i].Name + 2);
+
+			fprintf(fd, "P  %s  %-58.58s\n", aliaslist->Alias[index].NName, aliaslist->Alias[index].NetName);
+			index++;
+		}
+	}
+	if (index > 1) {
+		fprintf(fd, "C  End Netname Aliases Section\nC  \n");
+	}
+	return 0;
+}
+
+IPCD356_AliasList *CreateAliasList()
+{
+	IPCD356_AliasList *aliaslist;
+
+	aliaslist = malloc(sizeof(IPCD356_AliasList));	/* Create an alias list. */
+	aliaslist->AliasN = 0;				/* Initialize Number of Alias. */
+	return aliaslist;
+}
+
+IPCD356_AliasList *AddAliasToList(IPCD356_AliasList * aliaslist)
+{
+	aliaslist->AliasN++;
+	aliaslist->Alias = realloc(aliaslist->Alias, sizeof(IPCD356_Alias) * (aliaslist->AliasN + 1));
+	if (aliaslist->Alias == NULL) {
+		return NULL;
+	}
+	return aliaslist;
+}
+
+void CheckNetLength(char *net, IPCD356_AliasList * aliaslist)
+{
+	int i;
+
+	if (strlen(net) > 14) {
+		for (i = 1; i <= aliaslist->AliasN; i++) {
+			if (strcmp(net, aliaslist->Alias[i].NetName) == 0) {
+				strcpy(net, aliaslist->Alias[i].NName);
+			}
+		}
+	}
+}
+
+int IPCD356_SanityCheck()
+{
+	ELEMENT_LOOP(PCB->Data);
+	if (element->Name[1].TextString == '\0') {
+		Message(PCB_MSG_ERROR, "Error: Found unnamed element. All elements need to be named to create an IPC-D-356 netlist.\n");
+		return (1);
+	}
+	END_LOOP;											/* Element. */
+	return (0);
+}
+
+static void IPCD356_do_export(HID_Attr_Val * options)
+{
+	int i;
+
+	if (!options) {
+		IPCD356_get_export_options(0);
+
+		for (i = 0; i < NUM_OPTIONS; i++)
+			IPCD356_values[i] = IPCD356_options[i].default_val;
+
+		options = IPCD356_values;
+	}
+
+	IPCD356_filename = options[HA_IPCD356_filename].str_value;
+	if (!IPCD356_filename)
+		IPCD356_filename = "pcb-out.net";
+
+	IPCD356_Netlist();
+}
+
+static void IPCD356_parse_arguments(int *argc, char ***argv)
+{
+	hid_parse_command_line(argc, argv);
+}
+
+HID IPCD356_hid;
+
+pcb_uninit_t *hid_export_ipcd356_init()
+{
+	memset(&IPCD356_hid, 0, sizeof(HID));
+
+	common_nogui_init(&IPCD356_hid);
+
+	IPCD356_hid.struct_size = sizeof(HID);
+	IPCD356_hid.name = "IPC-D-356";
+	IPCD356_hid.description = "Exports a IPC-D-356 Netlist";
+	IPCD356_hid.exporter = 1;
+
+	IPCD356_hid.get_export_options = IPCD356_get_export_options;
+	IPCD356_hid.do_export = IPCD356_do_export;
+	IPCD356_hid.parse_arguments = IPCD356_parse_arguments;
+
+	hid_register_hid(&IPCD356_hid);
+
+	hid_register_attributes(IPCD356_options, sizeof(IPCD356_options) / sizeof(IPCD356_options[0]), ipcd356_cookie, 0);
+	return NULL;
+}
+
diff --git a/src_plugins/export_lpr/Makefile b/src_plugins/export_lpr/Makefile
new file mode 100644
index 0000000..bbf5342
--- /dev/null
+++ b/src_plugins/export_lpr/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_lpr
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_lpr/Plug.tmpasm b/src_plugins/export_lpr/Plug.tmpasm
new file mode 100644
index 0000000..9898072
--- /dev/null
+++ b/src_plugins/export_lpr/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_lpr}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_lpr/lpr.o @]
+
+switch /local/pcb/export_lpr/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_lpr/README b/src_plugins/export_lpr/README
new file mode 100644
index 0000000..d1efc1d
--- /dev/null
+++ b/src_plugins/export_lpr/README
@@ -0,0 +1,5 @@
+Export to lpr (using export_ps to generate postscript)
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_lpr/lpr.c b/src_plugins/export_lpr/lpr.c
new file mode 100644
index 0000000..7383232
--- /dev/null
+++ b/src_plugins/export_lpr/lpr.c
@@ -0,0 +1,151 @@
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+#include "hid.h"
+#include "../export_ps/ps.h"
+#include "hid_nogui.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_actions.h"
+
+const char *lpr_cookie = "lpr HID";
+
+static HID_Attribute base_lpr_options[] = {
+
+/* %start-doc options "98 lpr Printing Options"
+ at ftable @code
+ at item --lprcommand <string>
+Command to use for printing. Defaults to @code{lpr}. This can be used to produce
+PDF output with a virtual PDF printer. Example: @*
+ at code{--lprcommand "lp -d CUPS-PDF-Printer"}.
+ at end ftable
+ at noindent In addition, all @ref{Postscript Export} options are valid.
+%end-doc
+*/
+	{"lprcommand", "Command to use for printing",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_lprcommand 0
+};
+
+#define NUM_OPTIONS (sizeof(lpr_options)/sizeof(lpr_options[0]))
+
+static HID_Attribute *lpr_options = 0;
+static int num_lpr_options = 0;
+static HID_Attr_Val *lpr_values;
+
+static HID_Attribute *lpr_get_export_options(int *n)
+{
+	/*
+	 * We initialize the default value in this manner because the GUI
+	 * HID's may want to free() this string value and replace it with a
+	 * new one based on how a user fills out a print dialog.
+	 */
+	if (base_lpr_options[HA_lprcommand].default_val.str_value == NULL) {
+		base_lpr_options[HA_lprcommand].default_val.str_value = pcb_strdup("lpr");
+	}
+
+	if (lpr_options == 0) {
+		HID_Attribute *ps_opts = ps_hid.get_export_options(&num_lpr_options);
+		lpr_options = (HID_Attribute *) calloc(num_lpr_options, sizeof(HID_Attribute));
+		memcpy(lpr_options, ps_opts, num_lpr_options * sizeof(HID_Attribute));
+		memcpy(lpr_options, base_lpr_options, sizeof(base_lpr_options));
+		lpr_values = (HID_Attr_Val *) calloc(num_lpr_options, sizeof(HID_Attr_Val));
+	}
+	if (n)
+		*n = num_lpr_options;
+	return lpr_options;
+}
+
+static void lpr_do_export(HID_Attr_Val * options)
+{
+	FILE *f;
+	int i;
+	const char *filename;
+
+	if (!options) {
+		lpr_get_export_options(0);
+		for (i = 0; i < num_lpr_options; i++)
+			lpr_values[i] = lpr_options[i].default_val;
+		options = lpr_values;
+	}
+
+	filename = options[HA_lprcommand].str_value;
+
+	printf("LPR: open %s\n", filename);
+	f = popen(filename, "w");
+	if (!f) {
+		perror(filename);
+		return;
+	}
+
+	ps_hid_export_to_file(f, options);
+
+	fclose(f);
+}
+
+static void lpr_parse_arguments(int *argc, char ***argv)
+{
+	lpr_get_export_options(0);
+	hid_register_attributes(lpr_options, num_lpr_options, lpr_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static void lpr_calibrate(double xval, double yval)
+{
+	ps_calibrate_1(xval, yval, 1);
+}
+
+static HID lpr_hid;
+
+static int lpr_usage(const char *topic)
+{
+	fprintf(stderr, "\nlpr exporter command line arguments:\n\n");
+	hid_usage(base_lpr_options, sizeof(base_lpr_options) / sizeof(base_lpr_options[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x lpr foo.pcb [lpr options]\n\n");
+	return 0;
+}
+
+static void plugin_lpr_uninit(void)
+{
+	hid_remove_actions_by_cookie(lpr_cookie);
+}
+
+pcb_uninit_t hid_export_lpr_init()
+{
+	memset(&lpr_hid, 0, sizeof(HID));
+
+	common_nogui_init(&lpr_hid);
+	ps_ps_init(&lpr_hid);
+
+	lpr_hid.struct_size = sizeof(HID);
+	lpr_hid.name = "lpr";
+	lpr_hid.description = "Postscript print";
+	lpr_hid.printer = 1;
+	lpr_hid.poly_before = 1;
+
+	lpr_hid.get_export_options = lpr_get_export_options;
+	lpr_hid.do_export = lpr_do_export;
+	lpr_hid.parse_arguments = lpr_parse_arguments;
+	lpr_hid.calibrate = lpr_calibrate;
+
+	lpr_hid.usage = lpr_usage;
+
+	hid_register_hid(&lpr_hid);
+
+	return plugin_lpr_uninit;
+}
diff --git a/src_plugins/export_nelma/Makefile b/src_plugins/export_nelma/Makefile
new file mode 100644
index 0000000..3b6a322
--- /dev/null
+++ b/src_plugins/export_nelma/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_nelma
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_nelma/Plug.tmpasm b/src_plugins/export_nelma/Plug.tmpasm
new file mode 100644
index 0000000..bba3f02
--- /dev/null
+++ b/src_plugins/export_nelma/Plug.tmpasm
@@ -0,0 +1,16 @@
+put /local/pcb/mod {export_nelma}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_nelma/nelma.o @]
+
+switch /local/pcb/export_nelma/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/LDFLAGS         libs/gui/gd/ldflags
+		put /local/pcb/mod/CFLAGS          libs/gui/gd/cflags
+		end
+end
+
+switch /local/pcb/export_nelma/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_nelma/README b/src_plugins/export_nelma/README
new file mode 100644
index 0000000..45276bb
--- /dev/null
+++ b/src_plugins/export_nelma/README
@@ -0,0 +1,5 @@
+Export to nelma (Numerical capacitance calculator)
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_nelma/nelma.c b/src_plugins/export_nelma/nelma.c
new file mode 100644
index 0000000..b51e8ea
--- /dev/null
+++ b/src_plugins/export_nelma/nelma.c
@@ -0,0 +1,1026 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  NELMA (Numerical capacitance calculator) export HID
+ *  Copyright (C) 2006 Tomaz Solc (tomaz.solc at tablix.org)
+ *
+ *  PNG export code is based on the PNG export HID
+ *  Copyright (C) 2006 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This HID exports a PCB layout into: o One layer mask file (PNG format) per
+ * copper layer. o Nelma configuration file that contains netlist and pin
+ * information.
+ */
+
+/*
+ * FIXME:
+ *
+ * If you have a section of a net that does not contain any pins then that
+ * section will be missing from the Nelma's copper geometry.
+ *
+ * For example:
+ *
+ * this section will be ignored by Nelma |       |
+ *
+ * ||             ||=======||            ||     component layer ||
+ * ||       ||            || ||=============||       ||============||
+ * solder layer
+ *
+ * pin1           via      via           pin2
+ *
+ * Single layer layouts are always exported correctly.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <time.h>
+
+#include "global.h"
+#include "error.h"							/* Message(PCB_MSG_DEFAULT, ) */
+#include "data.h"
+#include "layer.h"
+#include "misc.h"
+#include "rats.h"
+#include "plugins.h"
+#include "hid_helper.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+
+#include <gd.h>
+
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_flags.h"
+#include "hid_color.h"
+
+const char *nelma_cookie = "nelma HID";
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", func); abort()
+
+/* Needed for PNG export */
+
+struct color_struct {
+	/* the descriptor used by the gd library */
+	int c;
+
+	/* so I can figure out what rgb value c refers to */
+	unsigned int r, g, b;
+};
+
+struct hid_gc_struct {
+	HID *me_pointer;
+	EndCapStyle cap;
+	Coord width;
+	unsigned char r, g, b;
+	int erase;
+	int faded;
+	struct color_struct *color;
+	gdImagePtr brush;
+};
+
+static HID nelma_hid;
+
+static struct color_struct *black = NULL, *white = NULL;
+static Coord linewidth = -1;
+static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1);
+static int lastcolor = -1;
+
+/* gd image and file for PNG export */
+static gdImagePtr nelma_im = NULL;
+static FILE *nelma_f = NULL;
+
+static int is_mask;
+static int is_drill;
+
+/*
+ * Which groups of layers to export into PNG layer masks. 1 means export, 0
+ * means do not export.
+ */
+static int nelma_export_group[MAX_LAYER];
+
+/* Group that is currently exported. */
+static int nelma_cur_group;
+
+/* Filename prefix that will be used when saving files. */
+static const char *nelma_basename = NULL;
+
+/* Horizontal DPI (grid points per inch) */
+static int nelma_dpi = -1;
+
+/* Height of the copper layers in micrometers. */
+
+/*
+ * The height of the copper layer is currently taken as the vertical grid
+ * step, since this is the smallest vertical feature in the layout.
+ */
+static int nelma_copperh = -1;
+/* Height of the substrate layers in micrometers. */
+static int nelma_substrateh = -1;
+/* Relative permittivity of the substrate. */
+static double nelma_substratee = -1;
+
+/* Permittivity of empty space (As/Vm) */
+static const double nelma_air_epsilon = 8.85e-12;
+
+HID_Attribute nelma_attribute_list[] = {
+	/* other HIDs expect this to be first.  */
+
+/* %start-doc options "nelma Options"
+ at ftable @code
+ at item -- basename <string>
+File name prefix.
+ at end ftable
+%end-doc
+*/
+	{"basename", "File name prefix",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_basename 0
+
+/* %start-doc options "nelma Options"
+ at ftable @code
+ at item --dpi <num>
+Horizontal scale factor (grid points/inch).
+ at end ftable
+%end-doc
+*/
+	{"dpi", "Horizontal scale factor (grid points/inch)",
+	 HID_Integer, 0, 1000, {100, 0, 0}, 0, 0},
+#define HA_dpi 1
+
+/* %start-doc options "nelma Options"
+ at ftable @code
+ at item --copper-height <num>
+Copper layer height (um).
+ at end ftable
+%end-doc
+*/
+	{"copper-height", "Copper layer height (um)",
+	 HID_Integer, 0, 200, {100, 0, 0}, 0, 0},
+#define HA_copperh 2
+
+/* %start-doc options "nelma Options"
+ at ftable @code
+ at item --substrate-height <num>
+Substrate layer height (um).
+ at end ftable
+%end-doc
+*/
+	{"substrate-height", "Substrate layer height (um)",
+	 HID_Integer, 0, 10000, {2000, 0, 0}, 0, 0},
+#define HA_substrateh 3
+
+/* %start-doc options "nelma Options"
+ at ftable @code
+ at item --substrate-epsilon <num>
+Substrate relative epsilon.
+ at end ftable
+%end-doc
+*/
+	{"substrate-epsilon", "Substrate relative epsilon",
+	 HID_Real, 0, 100, {0, 0, 4.0}, 0, 0},
+#define HA_substratee 4
+};
+
+#define NUM_OPTIONS (sizeof(nelma_attribute_list)/sizeof(nelma_attribute_list[0]))
+
+REGISTER_ATTRIBUTES(nelma_attribute_list, nelma_cookie)
+		 static HID_Attr_Val nelma_values[NUM_OPTIONS];
+
+/* *** Utility funcions **************************************************** */
+
+/* convert from default PCB units to nelma units */
+		 static int pcb_to_nelma(Coord pcb)
+{
+	return PCB_COORD_TO_INCH(pcb) * nelma_dpi;
+}
+
+static char *nelma_get_png_name(const char *basename, const char *suffix)
+{
+	char *buf;
+	int len;
+
+	len = strlen(basename) + strlen(suffix) + 6;
+	buf = (char *) malloc(sizeof(*buf) * len);
+
+	sprintf(buf, "%s.%s.png", basename, suffix);
+
+	return buf;
+}
+
+/* *** Exporting netlist data and geometry to the nelma config file ******** */
+
+static void nelma_write_space(FILE * out)
+{
+	double xh, zh;
+
+	int z;
+	int i, idx;
+	const char *ext;
+
+	xh = 2.54e-2 / ((double) nelma_dpi);
+	zh = nelma_copperh * 1e-6;
+
+	fprintf(out, "\n/* **** Space **** */\n\n");
+
+	fprintf(out, "space pcb {\n");
+	fprintf(out, "\tstep = { %e, %e, %e }\n", xh, xh, zh);
+	fprintf(out, "\tlayers = {\n");
+
+	fprintf(out, "\t\t\"air-top\",\n");
+	fprintf(out, "\t\t\"air-bottom\"");
+
+	z = 10;
+	for (i = 0; i < MAX_LAYER; i++)
+		if (nelma_export_group[i]) {
+			idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i;
+			ext = layer_type_to_file_name(idx, FNS_fixed);
+
+			if (z != 10) {
+				fprintf(out, ",\n");
+				fprintf(out, "\t\t\"substrate-%d\"", z);
+				z++;
+			}
+			fprintf(out, ",\n");
+			fprintf(out, "\t\t\"%s\"", ext);
+			z++;
+		}
+	fprintf(out, "\n\t}\n");
+	fprintf(out, "}\n");
+}
+
+
+static void nelma_write_material(FILE * out, const char *name, const char *type, double e)
+{
+	fprintf(out, "material %s {\n", name);
+	fprintf(out, "\ttype = \"%s\"\n", type);
+	fprintf(out, "\tpermittivity = %e\n", e);
+	fprintf(out, "\tconductivity = 0.0\n");
+	fprintf(out, "\tpermeability = 0.0\n");
+	fprintf(out, "}\n");
+}
+
+static void nelma_write_materials(FILE * out)
+{
+	fprintf(out, "\n/* **** Materials **** */\n\n");
+
+	nelma_write_material(out, "copper", "metal", nelma_air_epsilon);
+	nelma_write_material(out, "air", "dielectric", nelma_air_epsilon);
+	nelma_write_material(out, "composite", "dielectric", nelma_air_epsilon * nelma_substratee);
+}
+
+static void nelma_write_nets(FILE * out)
+{
+	LibraryType netlist;
+	LibraryMenuTypePtr net;
+	LibraryEntryTypePtr pin;
+
+	int n, m, i, idx;
+
+	const char *ext;
+
+	netlist = PCB->NetlistLib[NETLIST_EDITED];
+
+	fprintf(out, "\n/* **** Nets **** */\n\n");
+
+	for (n = 0; n < netlist.MenuN; n++) {
+		net = &netlist.Menu[n];
+
+		/* Weird, but correct */
+		fprintf(out, "net %s {\n", &net->Name[2]);
+
+		fprintf(out, "\tobjects = {\n");
+
+		for (m = 0; m < net->EntryN; m++) {
+			pin = &net->Entry[m];
+
+			/* pcb_pin_name_to_xy(pin, &x, &y); */
+
+			for (i = 0; i < MAX_LAYER; i++)
+				if (nelma_export_group[i]) {
+					idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i;
+					ext = layer_type_to_file_name(idx, FNS_fixed);
+
+					if (m != 0 || i != 0)
+						fprintf(out, ",\n");
+					fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, ext);
+				}
+		}
+
+		fprintf(out, "\n");
+		fprintf(out, "\t}\n");
+		fprintf(out, "}\n");
+	}
+}
+
+static void nelma_write_layer(FILE * out, int z, int h, const char *name, int full, const char *mat)
+{
+	LibraryType netlist;
+	LibraryMenuTypePtr net;
+	LibraryEntryTypePtr pin;
+
+	int n, m;
+
+	fprintf(out, "layer %s {\n", name);
+	fprintf(out, "\theight = %d\n", h);
+	fprintf(out, "\tz-order = %d\n", z);
+	fprintf(out, "\tmaterial = \"%s\"\n", mat);
+
+	if (full) {
+		fprintf(out, "\tobjects = {\n");
+		netlist = PCB->NetlistLib[NETLIST_EDITED];
+
+		for (n = 0; n < netlist.MenuN; n++) {
+			net = &netlist.Menu[n];
+
+			for (m = 0; m < net->EntryN; m++) {
+				pin = &net->Entry[m];
+
+				if (m != 0 || n != 0)
+					fprintf(out, ",\n");
+				fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, name);
+			}
+
+		}
+		fprintf(out, "\n\t}\n");
+	}
+	fprintf(out, "}\n");
+}
+
+static void nelma_write_layers(FILE * out)
+{
+	int i, idx;
+	int z;
+
+	const char *ext;
+	char buf[100];
+
+	int subh;
+
+	subh = nelma_substrateh / nelma_copperh;
+
+	fprintf(out, "\n/* **** Layers **** */\n\n");
+
+	/* Air layers on top and bottom of the stack */
+	/* Their height is double substrate height. */
+	nelma_write_layer(out, 1, 2 * subh, "air-top", 0, "air");
+	nelma_write_layer(out, 1000, 2 * subh, "air-bottom", 0, "air");
+
+	z = 10;
+	for (i = 0; i < MAX_LAYER; i++)
+		if (nelma_export_group[i]) {
+			idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i;
+			ext = layer_type_to_file_name(idx, FNS_fixed);
+
+			if (z != 10) {
+				sprintf(buf, "substrate-%d", z);
+				nelma_write_layer(out, z, subh, buf, 0, "composite");
+				z++;
+			}
+			/*
+			 * FIXME: for layers that are not on top or bottom,
+			 * the material should be "composite"
+			 */
+			nelma_write_layer(out, z, 1, ext, 1, "air");
+
+			z++;
+		}
+}
+
+static void nelma_write_object(FILE * out, LibraryEntryTypePtr pin)
+{
+	int i, idx;
+	Coord px = 0, py = 0;
+	int x, y;
+
+	char *f;
+	const char *ext;
+
+	pcb_pin_name_to_xy(pin, &px, &py);
+
+	x = pcb_to_nelma(px);
+	y = pcb_to_nelma(py);
+
+	for (i = 0; i < MAX_LAYER; i++)
+		if (nelma_export_group[i]) {
+			idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i;
+			ext = layer_type_to_file_name(idx, FNS_fixed);
+
+			fprintf(out, "object %s-%s {\n", pin->ListEntry, ext);
+			fprintf(out, "\tposition = { 0, 0 }\n");
+			fprintf(out, "\tmaterial = \"copper\"\n");
+			fprintf(out, "\ttype = \"image\"\n");
+			fprintf(out, "\trole = \"net\"\n");
+
+			f = nelma_get_png_name(nelma_basename, ext);
+
+			fprintf(out, "\tfile = \"%s\"\n", f);
+
+			free(f);
+
+			fprintf(out, "\tfile-pos = { %d, %d }\n", x, y);
+			fprintf(out, "}\n");
+		}
+}
+
+static void nelma_write_objects(FILE * out)
+{
+	LibraryType netlist;
+	LibraryMenuTypePtr net;
+	LibraryEntryTypePtr pin;
+
+	int n, m;
+
+	netlist = PCB->NetlistLib[NETLIST_EDITED];
+
+	fprintf(out, "\n/* **** Objects **** */\n\n");
+
+	for (n = 0; n < netlist.MenuN; n++) {
+		net = &netlist.Menu[n];
+
+		for (m = 0; m < net->EntryN; m++) {
+			pin = &net->Entry[m];
+
+			nelma_write_object(out, pin);
+		}
+	}
+}
+
+/* *** Main export callback ************************************************ */
+
+static void nelma_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(nelma_attribute_list, sizeof(nelma_attribute_list) / sizeof(nelma_attribute_list[0]), nelma_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static HID_Attribute *nelma_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+
+	if (PCB) {
+		derive_default_filename(PCB->Filename, &nelma_attribute_list[HA_basename], ".nelma", &last_made_filename);
+	}
+	if (n) {
+		*n = NUM_OPTIONS;
+	}
+	return nelma_attribute_list;
+}
+
+/* Populates nelma_export_group array */
+void nelma_choose_groups()
+{
+	int n, m;
+	LayerType *layer;
+
+	/* Set entire array to 0 (don't export any layer groups by default */
+	memset(nelma_export_group, 0, sizeof(nelma_export_group));
+
+	for (n = 0; n < max_copper_layer; n++) {
+		layer = &PCB->Data->Layer[n];
+
+		if (!LAYER_IS_EMPTY(layer)) {
+			/* layer isn't empty */
+
+			/*
+			 * is this check necessary? It seems that special
+			 * layers have negative indexes?
+			 */
+
+			if (SL_TYPE(n) == 0) {
+				/* layer is a copper layer */
+				m = GetLayerGroupNumberByNumber(n);
+
+				/* the export layer */
+				nelma_export_group[m] = 1;
+			}
+		}
+	}
+}
+
+static void nelma_alloc_colors()
+{
+	/*
+	 * Allocate white and black -- the first color allocated becomes the
+	 * background color
+	 */
+
+	white = (struct color_struct *) malloc(sizeof(*white));
+	white->r = white->g = white->b = 255;
+	white->c = gdImageColorAllocate(nelma_im, white->r, white->g, white->b);
+
+	black = (struct color_struct *) malloc(sizeof(*black));
+	black->r = black->g = black->b = 0;
+	black->c = gdImageColorAllocate(nelma_im, black->r, black->g, black->b);
+}
+
+static void nelma_start_png(const char *basename, const char *suffix)
+{
+	int h, w;
+	char *buf;
+
+	buf = nelma_get_png_name(basename, suffix);
+
+	h = pcb_to_nelma(PCB->MaxHeight);
+	w = pcb_to_nelma(PCB->MaxWidth);
+
+	/* nelma_im = gdImageCreate (w, h); */
+
+	/* Nelma only works with true color images */
+	nelma_im = gdImageCreate(w, h);
+	nelma_f = fopen(buf, "wb");
+
+	nelma_alloc_colors();
+
+	free(buf);
+}
+
+static void nelma_finish_png()
+{
+#ifdef HAVE_GDIMAGEPNG
+	gdImagePng(nelma_im, nelma_f);
+#else
+	Message(PCB_MSG_DEFAULT, "NELMA: PNG not supported by gd. Can't write layer mask.\n");
+#endif
+	gdImageDestroy(nelma_im);
+	fclose(nelma_f);
+
+	free(white);
+	free(black);
+
+	nelma_im = NULL;
+	nelma_f = NULL;
+}
+
+void nelma_start_png_export()
+{
+	BoxType region;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	linewidth = -1;
+	lastbrush = (gdImagePtr) ((void *) -1);
+	lastcolor = -1;
+
+	hid_expose_callback(&nelma_hid, &region, 0);
+}
+
+static void nelma_do_export(HID_Attr_Val * options)
+{
+	int save_ons[MAX_LAYER + 2];
+	int i, idx;
+	FILE *nelma_config;
+	char *buf;
+	int len;
+
+	time_t t;
+
+	if (!options) {
+		nelma_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++) {
+			nelma_values[i] = nelma_attribute_list[i].default_val;
+		}
+		options = nelma_values;
+	}
+	nelma_basename = options[HA_basename].str_value;
+	if (!nelma_basename) {
+		nelma_basename = "pcb-out";
+	}
+	nelma_dpi = options[HA_dpi].int_value;
+	if (nelma_dpi < 0) {
+		fprintf(stderr, "ERROR:  dpi may not be < 0\n");
+		return;
+	}
+	nelma_copperh = options[HA_copperh].int_value;
+	nelma_substrateh = options[HA_substrateh].int_value;
+	nelma_substratee = options[HA_substratee].real_value;
+
+	nelma_choose_groups();
+
+	for (i = 0; i < MAX_LAYER; i++) {
+		if (nelma_export_group[i]) {
+
+			nelma_cur_group = i;
+
+			/* magic */
+			idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i;
+
+			nelma_start_png(nelma_basename, layer_type_to_file_name(idx, FNS_fixed));
+
+			hid_save_and_show_layer_ons(save_ons);
+			nelma_start_png_export();
+			hid_restore_layer_ons(save_ons);
+
+			nelma_finish_png();
+		}
+	}
+
+	len = strlen(nelma_basename) + 4;
+	buf = (char *) malloc(sizeof(*buf) * len);
+
+	sprintf(buf, "%s.em", nelma_basename);
+	nelma_config = fopen(buf, "w");
+
+	free(buf);
+
+	fprintf(nelma_config, "/* Made with PCB Nelma export HID */");
+	t = time(NULL);
+	fprintf(nelma_config, "/* %s */", ctime(&t));
+
+	nelma_write_nets(nelma_config);
+	nelma_write_objects(nelma_config);
+	nelma_write_layers(nelma_config);
+	nelma_write_materials(nelma_config);
+	nelma_write_space(nelma_config);
+
+	fclose(nelma_config);
+}
+
+/* *** PNG export (slightly modified code from PNG export HID) ************* */
+
+static int nelma_set_layer(const char *name, int group, int empty)
+{
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+
+	if (name == 0) {
+		name = PCB->Data->Layer[idx].Name;
+	}
+	if (strcmp(name, "invisible") == 0) {
+		return 0;
+	}
+	is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	is_mask = (SL_TYPE(idx) == SL_MASK);
+
+	if (is_mask) {
+		/* Don't print masks */
+		return 0;
+	}
+	if (is_drill) {
+		/*
+		 * Print 'holes', so that we can fill gaps in the copper
+		 * layer
+		 */
+		return 1;
+	}
+	if (group == nelma_cur_group) {
+		return 1;
+	}
+	return 0;
+}
+
+static hidGC nelma_make_gc(void)
+{
+	hidGC rv = (hidGC) malloc(sizeof(struct hid_gc_struct));
+	rv->me_pointer = &nelma_hid;
+	rv->cap = Trace_Cap;
+	rv->width = 1;
+	rv->color = (struct color_struct *) malloc(sizeof(*rv->color));
+	rv->color->r = rv->color->g = rv->color->b = 0;
+	rv->color->c = 0;
+	return rv;
+}
+
+static void nelma_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void nelma_use_mask(int use_it)
+{
+	/* does nothing */
+}
+
+static void nelma_set_color(hidGC gc, const char *name)
+{
+	if (nelma_im == NULL) {
+		return;
+	}
+	if (name == NULL) {
+		name = "#ff0000";
+	}
+	if (!strcmp(name, "drill")) {
+		gc->color = black;
+		gc->erase = 0;
+		return;
+	}
+	if (!strcmp(name, "erase")) {
+		/* FIXME -- should be background, not white */
+		gc->color = white;
+		gc->erase = 1;
+		return;
+	}
+	gc->color = black;
+	gc->erase = 0;
+	return;
+}
+
+static void nelma_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void nelma_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void nelma_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+static void nelma_set_draw_faded(hidGC gc, int faded)
+{
+	gc->faded = faded;
+}
+
+static void use_gc(hidGC gc)
+{
+	int need_brush = 0;
+
+	if (gc->me_pointer != &nelma_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to nelma HID\n");
+		abort();
+	}
+	if (linewidth != gc->width) {
+		/* Make sure the scaling doesn't erase lines completely */
+		/*
+		   if (SCALE (gc->width) == 0 && gc->width > 0)
+		   gdImageSetThickness (im, 1);
+		   else
+		 */
+		gdImageSetThickness(nelma_im, pcb_to_nelma(gc->width));
+		linewidth = gc->width;
+		need_brush = 1;
+	}
+	if (lastbrush != gc->brush || need_brush) {
+		static void *bcache = 0;
+		hidval bval;
+		char name[256];
+		char type;
+		int r;
+
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			type = 'C';
+			r = pcb_to_nelma(gc->width / 2);
+			break;
+		default:
+		case Square_Cap:
+			r = pcb_to_nelma(gc->width);
+			type = 'S';
+			break;
+		}
+		sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r);
+
+		if (hid_cache_color(0, name, &bval, &bcache)) {
+			gc->brush = (gdImagePtr) bval.ptr;
+		}
+		else {
+			int bg, fg;
+			if (type == 'C')
+				gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1);
+			else
+				gc->brush = gdImageCreate(r + 1, r + 1);
+			bg = gdImageColorAllocate(gc->brush, 255, 255, 255);
+			fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b);
+			gdImageColorTransparent(gc->brush, bg);
+
+			/*
+			 * if we shrunk to a radius/box width of zero, then just use
+			 * a single pixel to draw with.
+			 */
+			if (r == 0)
+				gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg);
+			else {
+				if (type == 'C')
+					gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg);
+				else
+					gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg);
+			}
+			bval.ptr = gc->brush;
+			hid_cache_color(1, name, &bval, &bcache);
+		}
+
+		gdImageSetBrush(nelma_im, gc->brush);
+		lastbrush = gc->brush;
+
+	}
+#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
+	if (lastcolor != CBLEND(gc)) {
+		if (is_drill || is_mask) {
+#ifdef FIXME
+			fprintf(f, "%d gray\n", gc->erase ? 0 : 1);
+#endif
+			lastcolor = 0;
+		}
+		else {
+			double r, g, b;
+			r = gc->r;
+			g = gc->g;
+			b = gc->b;
+			if (gc->faded) {
+				r = 0.8 * 255 + 0.2 * r;
+				g = 0.8 * 255 + 0.2 * g;
+				b = 0.8 * 255 + 0.2 * b;
+			}
+#ifdef FIXME
+			if (gc->r == gc->g && gc->g == gc->b)
+				fprintf(f, "%g gray\n", r / 255.0);
+			else
+				fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
+#endif
+			lastcolor = CBLEND(gc);
+		}
+	}
+}
+
+static void nelma_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	gdImageRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c);
+}
+
+static void nelma_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	gdImageSetThickness(nelma_im, 0);
+	linewidth = 0;
+	gdImageFilledRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c);
+}
+
+static void nelma_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	if (x1 == x2 && y1 == y2) {
+		Coord w = gc->width / 2;
+		nelma_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
+		return;
+	}
+	use_gc(gc);
+
+	gdImageSetThickness(nelma_im, 0);
+	linewidth = 0;
+	gdImageLine(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gdBrushed);
+}
+
+static void nelma_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	Angle sa, ea;
+
+	/*
+	 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
+	 * in pcb, 0 degrees is to the left and +90 degrees is down
+	 */
+	start_angle = 180 - start_angle;
+	delta_angle = -delta_angle;
+	if (delta_angle > 0) {
+		sa = start_angle;
+		ea = start_angle + delta_angle;
+	}
+	else {
+		sa = start_angle + delta_angle;
+		ea = start_angle;
+	}
+
+	/*
+	 * make sure we start between 0 and 360 otherwise gd does strange
+	 * things
+	 */
+	sa = NormalizeAngle(sa);
+	ea = NormalizeAngle(ea);
+
+#if 0
+	printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea);
+	printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
+				 (void *)im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c);
+#endif
+	use_gc(gc);
+	gdImageSetThickness(nelma_im, 0);
+	linewidth = 0;
+	gdImageArc(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy),
+						 pcb_to_nelma(2 * width), pcb_to_nelma(2 * height), sa, ea, gdBrushed);
+}
+
+static void nelma_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	use_gc(gc);
+
+	gdImageSetThickness(nelma_im, 0);
+	linewidth = 0;
+	gdImageFilledEllipse(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy),
+											 pcb_to_nelma(2 * radius), pcb_to_nelma(2 * radius), gc->color->c);
+
+}
+
+static void nelma_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	gdPoint *points;
+
+	points = (gdPoint *) malloc(n_coords * sizeof(gdPoint));
+	if (points == NULL) {
+		fprintf(stderr, "ERROR:  nelma_fill_polygon():  malloc failed\n");
+		exit(1);
+	}
+	use_gc(gc);
+	for (i = 0; i < n_coords; i++) {
+		points[i].x = pcb_to_nelma(x[i]);
+		points[i].y = pcb_to_nelma(y[i]);
+	}
+	gdImageSetThickness(nelma_im, 0);
+	linewidth = 0;
+	gdImageFilledPolygon(nelma_im, points, n_coords, gc->color->c);
+	free(points);
+}
+
+static void nelma_calibrate(double xval, double yval)
+{
+	CRASH("nelma_calibrate");
+}
+
+static void nelma_set_crosshair(int x, int y, int a)
+{
+}
+
+static int nelma_usage(const char *topic)
+{
+	fprintf(stderr, "\nnelma exporter command line arguments:\n\n");
+	hid_usage(nelma_attribute_list, sizeof(nelma_attribute_list) / sizeof(nelma_attribute_list[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x nelma foo.pcb [nelma options]\n\n");
+	return 0;
+}
+
+/* *** Miscellaneous ******************************************************* */
+
+#include "dolists.h"
+
+pcb_uninit_t hid_export_nelma_init()
+{
+	memset(&nelma_hid, 0, sizeof(HID));
+
+	common_nogui_init(&nelma_hid);
+	common_draw_helpers_init(&nelma_hid);
+
+	nelma_hid.struct_size = sizeof(HID);
+	nelma_hid.name = "nelma";
+	nelma_hid.description = "Numerical analysis package export";
+	nelma_hid.exporter = 1;
+	nelma_hid.poly_before = 1;
+
+	nelma_hid.get_export_options = nelma_get_export_options;
+	nelma_hid.do_export = nelma_do_export;
+	nelma_hid.parse_arguments = nelma_parse_arguments;
+	nelma_hid.set_layer = nelma_set_layer;
+	nelma_hid.make_gc = nelma_make_gc;
+	nelma_hid.destroy_gc = nelma_destroy_gc;
+	nelma_hid.use_mask = nelma_use_mask;
+	nelma_hid.set_color = nelma_set_color;
+	nelma_hid.set_line_cap = nelma_set_line_cap;
+	nelma_hid.set_line_width = nelma_set_line_width;
+	nelma_hid.set_draw_xor = nelma_set_draw_xor;
+	nelma_hid.set_draw_faded = nelma_set_draw_faded;
+	nelma_hid.draw_line = nelma_draw_line;
+	nelma_hid.draw_arc = nelma_draw_arc;
+	nelma_hid.draw_rect = nelma_draw_rect;
+	nelma_hid.fill_circle = nelma_fill_circle;
+	nelma_hid.fill_polygon = nelma_fill_polygon;
+	nelma_hid.fill_rect = nelma_fill_rect;
+	nelma_hid.calibrate = nelma_calibrate;
+	nelma_hid.set_crosshair = nelma_set_crosshair;
+
+	nelma_hid.usage = nelma_usage;
+
+	hid_register_hid(&nelma_hid);
+	return NULL;
+}
diff --git a/src_plugins/export_openscad/Makefile b/src_plugins/export_openscad/Makefile
new file mode 100644
index 0000000..c77f26b
--- /dev/null
+++ b/src_plugins/export_openscad/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_openscad
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_openscad/Plug.tmpasm b/src_plugins/export_openscad/Plug.tmpasm
new file mode 100644
index 0000000..0ed107c
--- /dev/null
+++ b/src_plugins/export_openscad/Plug.tmpasm
@@ -0,0 +1,12 @@
+put /local/pcb/mod {export_openscad}
+put /local/pcb/mod/OBJS [@
+	$(PLUGDIR)/export_openscad/scad.o
+	$(PLUGDIR)/export_openscad/scadproto.o
+	$(PLUGDIR)/export_openscad/scadcomp.o
+@]
+
+switch /local/pcb/export_openscad/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_openscad/README b/src_plugins/export_openscad/README
new file mode 100644
index 0000000..c83ad0f
--- /dev/null
+++ b/src_plugins/export_openscad/README
@@ -0,0 +1,5 @@
+Export openscad
+
+#state: WIP
+#default: disabled
+#implements: export
diff --git a/src_plugins/export_openscad/scad.c b/src_plugins/export_openscad/scad.c
new file mode 100644
index 0000000..d29d06c
--- /dev/null
+++ b/src_plugins/export_openscad/scad.c
@@ -0,0 +1,1177 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  OpenSCAD export HID
+ *  This code is based on the GERBER and VRML export HID
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <time.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "error.h"
+#include "buffer.h"
+#include "create.h"
+#include "conf_core.h"
+#include "layer.h"
+#include "plugins.h"
+
+#include "hid.h"
+#include "hid_draw_helpers.h"
+#include "hid_nogui.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+
+#include "scad.h"
+
+/****************************************************************************************************
+* VRML export filter parameters and options
+****************************************************************************************************/
+
+static const char *export_modes[] = {
+	"None",
+	"Boxes",
+	"Simple",
+	"Realistic",
+	0
+};
+
+static const char *board_outlines[] = {
+	"None",
+	"Outline",
+	"Board",
+	0
+};
+
+static const char *copper_colors[] = {
+	"Copper",
+	"Gold",
+	"HAL",
+	0
+};
+
+static const char *mask_colors[] = {
+	"None",
+	"Green",
+	"Blue",
+	"Red",
+	0
+};
+
+static const char *board_cuts[] = {
+	"All",
+	"Top",
+	"Top_only",
+	"Bottom",
+	"Bottom_only"
+};
+
+static char *mask_color_table[] = {
+	"", SCAD_MASK_COLOR_G, SCAD_MASK_COLOR_B, SCAD_MASK_COLOR_R
+};
+
+static char *finish_color_table[] = {
+	SCAD_COPPER_COLOR, SCAD_COPPER_COLOR_GOLD, SCAD_COPPER_COLOR_TIN
+};
+
+
+
+
+static HID scad_hid;
+
+static int silk_layer, drill_layer, outline_layer, mask_layer;
+static int layer_open, fresh_layer;
+static char layer_id[64];
+
+static struct {
+	int draw;
+	int exp;
+	float z_offset;
+	int solder;
+	int component;
+} group_data[MAX_LAYER];
+
+
+#define HA_scadfile 		0
+#define HA_outline_type 	1
+#define HA_exp_component 	2
+#define HA_exp_copper 		3
+#define HA_exp_silk 		4
+#define HA_exp_inner_layers 	5
+#define HA_mask_color 		6
+#define HA_copper_color		7
+#define HA_minimal_drill        9
+#define HA_board_cut            10
+
+static HID_Attribute scad_options[] = {
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --scad-file <string>
+Name of the OpenSCAD model file.
+ at end ftable
+%end-doc
+*/
+	{
+	 "scad_file", "SCAD file name", HID_String, 0, 0,
+	 {
+		0, 0, 0, 0}, 0, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --board_outline <string>
+How the board outline is created. @samp{None} - no board outline (inner layers copper is visible), @samp{Outline} - board outline is constructed from @samp{outline} layer,
+ at samp{Board} - board outline ha rectangular shape, dimensions are taken from board properties. Default: @samp{Board}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "board_outline", "Board outline",
+	 HID_Enum,
+	 0, 0,
+	 {
+		2, 0, 0, 0}, board_outlines, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --components <string>
+Populates board with components.  Values: @samp{None}, @samp{Boxes}, @samp{Simple}, @samp{Realistic}. Default: @samp{Realistic}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "components", "Export components",
+	 HID_Enum,
+	 0, 0,
+	 {
+		3, 0, 0, 0}, export_modes, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --copper <boolean>
+Exports copper tracks, pads, pins and vias. Default: @samp{false}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "copper", "Export tracks and copper planes", HID_Boolean, 0, 0,
+	 {
+		0, 0, 0, 0}, 0, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --silk_layers <boolean>
+Exports silk screen layers. Default: @samp{false}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "silk_layers", "Export silk layers", HID_Boolean, 0, 0,
+	 {
+		0, 0, 0, 0}, 0, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --inner_layers <boolean>
+Exports inner layers (normally invisible, hidden inside board). Default: @samp{false}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "innner_layers", "Export inner layers", HID_Boolean, 0, 0,
+	 {
+		0, 0, 0, 0}, 0, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --solder_mask <string>
+Type of solder mask. Available options: @samp{None}, @samp{Green}, @samp{Red} and @samp{Blue}. Default: @samp{None}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "solder_mask", "Solder mask color", HID_Enum, 0, 0,
+	 {
+		0, 0, 0, 0}, mask_colors, 0},
+	{
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --copper_finish <string>
+Type of copper finish. Values: @samp{Copper}, @samp{Gold} and @samp{HAL}. Default: @samp{Copper}.
+ at end ftable
+%end-doc
+*/
+	 "copper_finish", "Surface finish of copper",
+	 HID_Enum,
+	 0, 0,
+	 {
+		0, 0, 0, 0}, copper_colors, 0},
+
+	{
+	 " ", " ", HID_Label, 0, 0,
+	 {
+		0, 0, 0, 0}, 0, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --min_drill <measure>
+Minimal drill to be exported. Default: @samp{1mm}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "min_drill", "Minimal drill to export", HID_Coord, PCB_MM_TO_COORD(0),
+	 PCB_MM_TO_COORD(10),
+	 {
+		0, 0, 0, PCB_MM_TO_COORD(1)}, 0, 0},
+/*
+%start-doc options "Advanced OpenSCAD Export"
+ at ftable @code
+ at item --board cut <string>
+Type of board cut. Values: @samp{All}, @samp{Top}, @samp{Top only}, @samp{Bottom}, @samp{Bottom only}. Default: @samp{None}.
+ at end ftable
+%end-doc
+*/
+	{
+	 "board_cut", "Section of board to be exported (3D print support)",
+	 HID_Enum,
+	 0, 0,
+	 {
+		0, 0, 0, 0}, board_cuts, 0},
+};
+
+#define NUM_OPTIONS (sizeof(scad_options)/sizeof(scad_options[0]))
+
+static HID_Attr_Val scad_values[NUM_OPTIONS];
+
+/****************************************************************************************************/
+
+FILE *scad_output;
+
+static const char *scad_filename = 0;
+
+static int opt_exp_silk;
+static int opt_exp_component;
+static int opt_exp_inner_layers;
+static int opt_exp_copper;
+static int opt_outline_type;
+static int opt_copper_color;
+static int opt_mask_color;
+static Coord opt_minimal_drill;
+static int opt_board_cut;
+
+static int lastseq = 0;
+static int current_mask;
+
+static float scaled_layer_thickness;
+
+static int n_alloc_outline_segments, n_outline_segments;
+static t_outline_segment *outline_segments;
+
+static void scad_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y);
+static void scad_emit_polygon(hidGC gc, int n_coords, Coord * x, Coord * y, float thickness);
+
+
+/* scaling function - all output is in milimeters */
+
+float scad_scale_coord(float x)
+{
+	return x * METRIC_SCALE;
+}
+
+
+
+static void scad_close_layer()
+{
+
+	if (!outline_layer) {
+		if (drill_layer) {
+			fprintf(scad_output, "];\n\n");
+		}
+		else {
+			fprintf(scad_output, "}\n}\n\n");
+		}
+
+		fprintf(scad_output, "\n// END_OF_LAYER %s\n\n", layer_id);
+	}
+	layer_open = 0;
+}
+
+
+/*******************************************
+* Export filter implementation starts here
+********************************************/
+
+static HID_Attribute *scad_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+	if (PCB)
+		derive_default_filename(PCB->Filename, &scad_options[HA_scadfile], ".scad", &last_made_filename);
+
+/*    scad_options[HA_minimal_drill].coord_value = scad_options[HA_minimal_drill].*/
+	if (n)
+		*n = NUM_OPTIONS;
+	return scad_options;
+}
+
+/*******************************************
+* main export function
+* - collect information about used layers and their grouping
+* - calculates positions olaers, dependiing on options and number of layer groups
+* - calls the export, exports the board layout
+* - generates the board outline, based on (depends on user selection):
+*   - physical dimensions set in preferences
+*   - lines drawn on "Outline" layer
+*   - polygons drawn on "Shape" layer
+* - populates the board with components
+********************************************/
+
+static void init_outline()
+{
+	outline_segments = 0;
+	n_alloc_outline_segments = 0;
+	n_outline_segments = 0;
+}
+
+static void add_outline_segment(Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	if (!n_alloc_outline_segments) {
+		outline_segments = (t_outline_segment *) malloc(sizeof(t_outline_segment) * 50);
+		n_alloc_outline_segments = 50;
+		if (!outline_segments) {
+			Message(PCB_MSG_ERROR, "openscad: cannot allocate memory for board outline. Board outline cannot be created.\n");
+			return;
+		}
+	}
+	else {
+		if (n_alloc_outline_segments == n_outline_segments) {
+			t_outline_segment *os = (t_outline_segment *) realloc(outline_segments,
+																														sizeof(t_outline_segment) * (n_alloc_outline_segments + 50));
+
+			if (os) {
+				outline_segments = os;
+				n_alloc_outline_segments = n_alloc_outline_segments + 50;
+			}
+			else {
+				Message(PCB_MSG_ERROR, "openscad: cannot allocate more memory for board outline. Board outline will be incomplete.\n");
+				return;
+			}
+		}
+	}
+
+	outline_segments[n_outline_segments].processed = 0;
+	outline_segments[n_outline_segments].x1 = x1;
+	outline_segments[n_outline_segments].y1 = y1;
+	outline_segments[n_outline_segments].x2 = x2;
+	outline_segments[n_outline_segments].y2 = y2;
+	n_outline_segments++;
+}
+
+
+typedef struct {
+	Coord x;
+	Coord y;
+	int marker;
+} t_OutlinePoint;
+
+static int is_same_point(Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	return (abs(x1 - x2) < SCAD_MIN_OUTLINE_DIST)
+		&& (abs(y1 - y2) < SCAD_MIN_OUTLINE_DIST);
+}
+
+
+void scad_process_outline()
+{
+	int i, j, n;
+	t_OutlinePoint *op;
+
+	if (outline_segments && n_outline_segments) {
+
+		op = malloc(n_outline_segments * 2 * sizeof(t_OutlinePoint));
+
+		if (op != NULL) {
+
+			n = 0;
+			for (i = 0; i < n_outline_segments; i++) {
+
+				if (!outline_segments[i].processed) {
+					outline_segments[i].processed = 1;
+					op[n].x = outline_segments[i].x1;
+					op[n].y = outline_segments[i].y1;
+					op[n].marker = 1;
+					op[n + 1].x = outline_segments[i].x2;
+					op[n + 1].y = outline_segments[i].y2;
+					op[n + 1].marker = 0;
+					n += 2;;
+					do {
+						for (j = i + 1; j < n_outline_segments; j++) {
+							if (!outline_segments[j].processed) {
+								if (is_same_point(op[n - 1].x, op[n - 1].y, outline_segments[j].x1, outline_segments[j].y1)) {
+									op[n - 1].x = (op[n - 1].x + outline_segments[j].x1) / 2;
+									op[n - 1].y = (op[n - 1].y + outline_segments[j].y1) / 2;
+									op[n].x = outline_segments[j].x2;
+									op[n].y = outline_segments[j].y2;
+									n++;
+									outline_segments[j].processed = 1;
+									break;
+								}
+								else if (is_same_point(op[n - 1].x, op[n - 1].y, outline_segments[j].x2, outline_segments[j].y2)) {
+									op[n - 1].x = (op[n - 1].x + outline_segments[j].x2) / 2;
+									op[n - 1].y = (op[n - 1].y + outline_segments[j].y2) / 2;
+									op[n].x = outline_segments[j].x1;
+									op[n].y = outline_segments[j].y1;
+									n++;
+									outline_segments[j].processed = 1;
+									break;
+								}
+							}
+						}
+					}
+					while (j < n_outline_segments);
+				}
+			}
+
+			fprintf(scad_output, "module board_outline () {\n\tpolygon([");
+
+			/* Outline points */
+			for (i = 0; i < n; i++) {
+				fprintf(scad_output, "\t\t[%f, %f]%s\n",
+								scad_scale_coord((float) op[i].x), -scad_scale_coord((float) op[i].y), (i < (n - 1)) ? ", " : "");
+			}
+
+			/* Outline paths */
+			fprintf(scad_output, "\t],[\n\t\t");
+
+			fprintf(scad_output, "\t\t[");
+			for (i = 0; i < n; i++) {
+/*            if (!(i % 10) && i)
+                fprintf (scad_output, "\n\t\t");*/
+				if (i > 0 && op[i].marker)
+					fprintf(scad_output, "],\n\t\t[");
+/*            fprintf (scad_output, "%d%s", i, (i < (n - 1)) ? ", " : "");*/
+				fprintf(scad_output, "%d%s", i, ((i < (n - 1)) && op[i + 1].marker == 0) ? ", " : "");
+			}
+			fprintf(scad_output, "\t]]);\n");
+			fprintf(scad_output, "}\n\n");
+		}
+		else {
+			Message(PCB_MSG_ERROR, "openscad: cannot allocate more memory for board outline. Board outline will be incomplete.\n");
+		}
+		if (op)
+			free(op);
+		free(outline_segments);
+	}
+}
+
+static void scad_do_export(HID_Attr_Val * options)
+{
+	int i;
+	int inner_layers;
+	float layer_spacing, layer_offset, cut_offset = 0.;
+	BoxType region;
+	LayerType *layer;
+
+	conf_force_set_bool(conf_core.editor.thin_draw, 0);
+	conf_force_set_bool(conf_core.editor.thin_draw_poly, 0);
+
+	if (!options) {
+		scad_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			scad_values[i] = scad_options[i].default_val;
+		options = scad_values;
+	}
+	opt_mask_color = options[HA_mask_color].int_value;
+	opt_exp_silk = options[HA_exp_silk].int_value;
+	opt_exp_component = options[HA_exp_component].int_value;
+	opt_exp_inner_layers = options[HA_exp_inner_layers].int_value;
+	opt_exp_copper = options[HA_exp_copper].int_value;
+	opt_outline_type = options[HA_outline_type].int_value;
+	opt_copper_color = options[HA_copper_color].int_value;
+	opt_minimal_drill = options[HA_minimal_drill].coord_value;
+	opt_board_cut = options[HA_board_cut].int_value;
+
+	scad_filename = options[HA_scadfile].str_value;
+	if (!scad_filename)
+		scad_filename = "unknown.scad";
+
+	scad_output = fopen(scad_filename, "w");
+	if (scad_output == NULL) {
+		Message(PCB_MSG_ERROR, "openscad: could not open %s for writing.\n", scad_filename);
+		goto quit;
+	}
+
+	scad_write_prologue(PCB->Filename);
+
+	memset(group_data, 0, sizeof(group_data));
+#ifdef SOLDER_LAYER
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + SOLDER_LAYER)].solder = 1;
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + COMPONENT_LAYER)].component = 1;
+#else
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + BOTTOM_SIDE)].solder = 1;
+	group_data[GetLayerGroupNumberByNumber(max_copper_layer + TOP_SIDE)].component = 1;
+#endif
+	for (i = 0; i < max_copper_layer; i++) {
+		layer = PCB->Data->Layer + i;
+		if (!IsLayerEmpty(layer))
+			group_data[GetLayerGroupNumberByNumber(i)].draw = 1;
+	}
+
+	inner_layers = 0;
+	for (i = 0; i < max_group; i++) {
+		if (group_data[i].draw && !(group_data[i].component || group_data[i].solder)) {
+			inner_layers++;
+		}
+	}
+
+	layer_spacing = BOARD_THICKNESS / ((float) inner_layers + 1);
+	layer_offset = BOARD_THICKNESS / 2. - layer_spacing;
+	for (i = 0; i < max_group; i++) {
+		if (group_data[i].component) {
+			group_data[i].z_offset = (BOARD_THICKNESS / 2.) + (OUTER_COPPER_THICKNESS / 2.);
+		}
+		else if (group_data[i].solder) {
+			group_data[i].z_offset = -(BOARD_THICKNESS / 2.) - (OUTER_COPPER_THICKNESS / 2.);
+		}
+		else if (group_data[i].draw) {
+			group_data[i].z_offset = layer_offset;
+			layer_offset -= layer_spacing;
+		}
+	}
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	layer_open = 0;
+
+	hid_expose_callback(&scad_hid, &region, 0);
+
+/* And now .... Board outlines */
+
+	if (opt_outline_type == SCAD_OUTLINE_SIZE) {
+		fprintf(scad_output, "module board_outline () {\n\tpolygon(");
+		fprintf(scad_output, "[[0,0],[0,%f],[%f,%f],[%f,0]],\n",
+						-scad_scale_coord((float) PCB->MaxHeight),
+						scad_scale_coord((float) PCB->MaxWidth),
+						-scad_scale_coord((float) PCB->MaxHeight), scad_scale_coord((float) PCB->MaxWidth));
+		fprintf(scad_output, "[[0,1,2,3]]);\n");
+		fprintf(scad_output, "}\n\n");
+
+	}
+	else if (opt_outline_type == SCAD_OUTLINE_OUTLINE /* and collected lines */ ) {
+		scad_process_outline();
+	}
+
+	if (layer_open) {
+		scad_close_layer();
+	}
+
+	scad_generate_holes();
+	if (opt_exp_copper)
+		scad_generate_plating();
+
+	if (opt_outline_type != SCAD_OUTLINE_NONE)
+		scad_generate_board();
+
+	if (opt_mask_color != SCAD_MASK_NONE && opt_outline_type != SCAD_OUTLINE_NONE)
+		scad_generate_mask();
+
+
+	if (EXPORT_COMPONENTS) {
+		fprintf(scad_output, "/***************************************************/\n");
+		fprintf(scad_output, "/*                                                 */\n");
+		fprintf(scad_output, "/* Components                                      */\n");
+		fprintf(scad_output, "/*                                                 */\n");
+		fprintf(scad_output, "/***************************************************/\n");
+		scad_process_components(opt_exp_component);
+	}
+
+
+	fprintf(scad_output, "/***************************************************/\n");
+	fprintf(scad_output, "/*                                                 */\n");
+	fprintf(scad_output, "/* Final board assembly                            */\n");
+	fprintf(scad_output, "/* Here is the complete board built from           */\n");
+	fprintf(scad_output, "/* pre-generated modules                           */\n");
+	fprintf(scad_output, "/*                                                 */\n");
+	fprintf(scad_output, "/***************************************************/\n");
+
+	if (opt_board_cut != SCAD_CUT_COMPLETE) {
+		fprintf(scad_output, "intersection () {\n\tunion () {\n");
+	}
+
+	if (opt_exp_copper) {
+		for (i = 0; i < max_group; i++) {
+			if (group_data[i].exp) {
+/*        printf("%d\n",i); */
+
+				if (group_data[i].component || group_data[i].solder || opt_exp_inner_layers) {
+					fprintf(scad_output, "\t\tcolor (%s)\n", finish_color_table[opt_copper_color]);
+					fprintf(scad_output, "\t\t\tdifference() {\n");
+					fprintf(scad_output, "\t\t\t\tlayer_%02d_body(%f);\n", i, group_data[i].z_offset);
+					fprintf(scad_output, "\t\t\t\tall_holes();\n");
+					fprintf(scad_output, "\t\t\t}\n\n");
+				}
+
+			}
+		}
+	}
+
+	if (opt_exp_silk) {
+		fprintf(scad_output, "\t\tcolor (%s)\n", SCAD_SILK_COLOR);
+		fprintf(scad_output, "\t\t\tlayer_topsilk_body(%f);\n\n",
+						(opt_mask_color != SCAD_MASK_NONE
+						 && opt_outline_type != SCAD_OUTLINE_NONE) ? SILK_LAYER_OFFSET2 : SILK_LAYER_OFFSET);
+		fprintf(scad_output, "\t\tcolor (%s)\n", SCAD_SILK_COLOR);
+		fprintf(scad_output, "\t\t\tlayer_bottomsilk_body(%f);\n\n",
+						(opt_mask_color != SCAD_MASK_NONE
+						 && opt_outline_type != SCAD_OUTLINE_NONE) ? -SILK_LAYER_OFFSET2 : -SILK_LAYER_OFFSET);
+	}
+
+	if (opt_exp_copper) {
+		fprintf(scad_output, "\t\tcolor (%s)\n", finish_color_table[opt_copper_color]);
+		fprintf(scad_output, "\t\t\tall_plating();\n\n");
+	}
+
+	if (opt_outline_type != SCAD_OUTLINE_NONE) {
+		fprintf(scad_output, "\t\tcolor (%s)\n", SCAD_BOARD_COLOR);
+		fprintf(scad_output, "\t\t\tdifference() {\n");
+		fprintf(scad_output, "\t\t\t\tboard_body();\n");
+		fprintf(scad_output, "\t\t\t\tall_holes();\n");
+		fprintf(scad_output, "\t\t\t}\n\n");
+	}
+	if (opt_mask_color != SCAD_MASK_NONE && opt_outline_type != SCAD_OUTLINE_NONE) {
+		fprintf(scad_output, "\t\tcolor (%s) translate ([0,0,%f])\n",
+						mask_color_table[opt_mask_color], (BOARD_THICKNESS + MASK_THICKNESS) / 2.);
+		fprintf(scad_output, "\t\t\tdifference() {\n");
+		fprintf(scad_output, "\t\t\t\tmask_surface();\n");
+		fprintf(scad_output, "\t\t\t\tlayer_topmask_body();\n");
+		fprintf(scad_output, "\t\t\t}\n\n");
+		fprintf(scad_output, "\t\tcolor (%s) translate ([0,0,%f])\n",
+						mask_color_table[opt_mask_color], -(BOARD_THICKNESS + MASK_THICKNESS) / 2.);
+		fprintf(scad_output, "\t\t\tdifference() {\n");
+		fprintf(scad_output, "\t\t\t\tmask_surface();\n");
+		fprintf(scad_output, "\t\t\t\tlayer_bottommask_body();\n");
+		fprintf(scad_output, "\t\t\t}\n\n");
+	}
+
+	if (EXPORT_COMPONENTS)
+		fprintf(scad_output, "\t\tall_components();\n");
+
+	if (opt_board_cut != SCAD_CUT_COMPLETE) {
+		switch (opt_board_cut) {
+		case SCAD_CUT_TOP:
+			cut_offset = 0.;
+			break;
+		case SCAD_CUT_TOP_ONLY:
+			cut_offset = -BOARD_THICKNESS / 2.;
+			break;
+		case SCAD_CUT_BOTTOM:
+			cut_offset = -100.;
+			break;
+		case SCAD_CUT_BOTTOM_ONLY:
+			cut_offset = -100. + BOARD_THICKNESS / 2.;
+			break;
+		}
+		fprintf(scad_output, "\t}\n");
+		fprintf(scad_output, "\t\t translate ([%f,%f,%f]) ", -25.,
+						-scad_scale_coord((float) PCB->MaxHeight) / 2. - 25., cut_offset);
+		fprintf(scad_output, "\t\t cube ([%f,%f,100]);\n",
+						scad_scale_coord((float) PCB->MaxWidth) + 50., scad_scale_coord((float) PCB->MaxHeight));
+		fprintf(scad_output, "}\n");
+	}
+
+	fprintf(scad_output, "// END_OF_BOARD\n");
+	fclose(scad_output);
+	quit:;
+	conf_update(NULL); /* restore forced sets */
+}
+
+static void scad_parse_arguments(int *argc, char ***argv)
+{
+	hid_parse_command_line(argc, argv);
+}
+
+static int scad_set_layer(const char *name, int group, int empty)
+{
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+	int layer_ok;
+
+	if (layer_open) {
+		scad_close_layer();
+	}
+
+
+
+	if (name == 0)
+		name = PCB->Data->Layer[idx].Name;
+
+/*  printf("%s\n",name); */
+
+	silk_layer = 0;
+	drill_layer = 0;
+	mask_layer = 0;
+	outline_layer = 0;
+	fresh_layer = 1;
+
+	if (strcmp(name, "invisible") == 0)
+		return 0;
+
+	if (SL_TYPE(idx) == SL_ASSY)
+		return 0;
+
+	if (strcmp(name, "route") == 0)
+		return 0;
+
+	if (group >= 0 && group < max_group) {
+		layer_ok = (opt_exp_inner_layers || group_data[group].component || group_data[group].solder) && opt_exp_copper;
+	}
+	else {
+		layer_ok = 1;
+	}
+
+	if (strcmp(name, "outline") == 0) {
+		if (opt_outline_type == SCAD_OUTLINE_OUTLINE) {
+			outline_layer = 1;
+			layer_ok = 1;
+			init_outline();
+			n_alloc_outline_segments = 0;
+			n_outline_segments = 0;
+		}
+		else
+			return 0;
+	}
+
+	if (!layer_ok)
+		return 0;
+
+	if (group >= 0 && group < max_group) {
+		if (!group_data[group].draw)
+			return 0;
+		scaled_layer_thickness = (group_data[group].solder
+															|| group_data[group].component) ? OUTER_COPPER_THICKNESS : INNER_COPPER_THICKNESS;
+		sprintf(layer_id, "layer_%02d", group);
+		if (!outline_layer) {
+			group_data[group].exp = 1;
+		}
+	}
+	else {
+		if (SL_TYPE(group) == SL_PDRILL) {
+			drill_layer = SL_TYPE(group);
+			strcpy(layer_id, "layer_pdrill");
+		}
+		else if (SL_TYPE(group) == SL_UDRILL) {
+			drill_layer = SL_TYPE(group);
+			strcpy(layer_id, "layer_udrill");
+		}
+		else if (SL_TYPE(group) == SL_SILK) {
+			if (!opt_exp_silk)
+				return 0;
+			scaled_layer_thickness = SILK_LAYER_THICKNESS;
+			silk_layer = SL_TYPE(group);
+			if (SL_SIDE(group) == SL_TOP_SIDE) {
+				strcpy(layer_id, "layer_topsilk");
+			}
+			else {
+				strcpy(layer_id, "layer_bottomsilk");
+			}
+		}
+		else if (SL_TYPE(group) == SL_MASK) {
+			if (opt_mask_color == SCAD_MASK_NONE || opt_outline_type == SCAD_OUTLINE_NONE)
+				return 0;
+			scaled_layer_thickness = MASK_THICKNESS * 2.;
+			silk_layer = SL_TYPE(group);
+			if (SL_SIDE(group) == SL_TOP_SIDE) {
+				strcpy(layer_id, "layer_topmask");
+			}
+			else {
+				strcpy(layer_id, "layer_bottommask");
+			}
+		}
+		else {
+			return 0;
+		}
+	}
+
+	layer_open = 1;
+
+	if (!outline_layer) {
+		fprintf(scad_output, "// START_OF_LAYER: %s\n", name);
+		if (drill_layer) {
+			fprintf(scad_output, "%s_list=[\n", layer_id);
+		}
+		else {
+			fprintf(scad_output, "module %s_body (offset) {\ntranslate ([0, 0, offset]) union () {\n", layer_id);
+		}
+	}
+	return 1;
+}
+
+static hidGC scad_make_gc(void)
+{
+	hidGC rv = (hidGC) calloc(1, sizeof(hid_gc_struct));
+	rv->cap = Trace_Cap;
+	rv->seq = lastseq++;
+	return rv;
+}
+
+static void scad_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void scad_use_mask(int use_it)
+{
+	current_mask = use_it;
+}
+
+static void scad_set_color(hidGC gc, const char *name)
+{
+	if (strcmp(name, "erase") == 0) {
+		gc->erase = 1;
+		gc->drill = 0;
+	}
+	else if (strcmp(name, "drill") == 0) {
+		gc->erase = 0;
+		gc->drill = 1;
+	}
+	else {
+		if (name[0] == '#') {
+			unsigned int r, g, b;
+			if (sscanf(name + 1, "%02x%02x%02x", &r, &g, &b) != 3)
+				Message(PCB_MSG_ERROR, "Invalid color format: %s\n", name);
+			gc->r = r;
+			gc->g = g;
+			gc->b = b;
+		}
+		else {
+			gc->r = 1;
+			gc->g = 1;
+			gc->b = 1;
+		}
+		gc->erase = 0;
+		gc->drill = 0;
+	}
+}
+
+static void scad_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void scad_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void scad_set_draw_xor(hidGC gc, int xor)
+{
+}
+
+
+static void scad_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	Coord x[5];
+	Coord y[5];
+	x[0] = x[4] = x1;
+	y[0] = y[4] = y1;
+	x[1] = x1;
+	y[1] = y2;
+	x[2] = x2;
+	y[2] = y2;
+	x[3] = x2;
+	y[3] = y1;
+	scad_fill_polygon(gc, 5, x, y);
+}
+
+/* Helper function - draws the line or collects the line segments
+*  - on outline layer the line segments are collected and later used to draw board outline
+*  - otherwise it is drawn as rotated box with circle at the end(s), depending on mode:
+*    -- on single line segment or last polyline segment caps are drawn on both ends
+*    -- on polyline segment cap is drawn only on beginning
+*/
+
+static void scad_emit_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2, int mode)
+{
+	int zero_length;
+	float angle = 0., length = 0.;
+	float cx, cy;
+	int bd = 0, c1 = 0, c2 = 0;
+
+	if (outline_layer) {
+		add_outline_segment(x1, y1, x2, y2);
+		return;
+	}
+
+
+
+	zero_length = (x1 == x2 && y1 == y2 && gc->cap != Square_Cap) ? 1 : 0;
+
+	cx = ((float) (x1 + x2)) / 2.;
+	cy = ((float) (y1 + y2)) / 2.;
+
+
+	if (!zero_length) {
+		angle = -(atan2(-(float) (y2 - y1), -(float) (x2 - x1))) / M_PI * 180.;
+		length = sqrt((float) (x2 - x1) * (float) (x2 - x1) + (float) (y2 - y1) * (float) (y2 - y1));
+		if (gc->cap == Square_Cap) {
+			length += (float) gc->width;
+		}
+	}
+
+
+	if (!zero_length) {
+		bd = 1;
+	}
+	if (gc->cap == Trace_Cap || gc->cap == Round_Cap || mode == SCAD_EL_LASTPOLY || mode == SCAD_EL_POLY) {
+		c2 = 1;
+		if (!zero_length && (mode != SCAD_EL_POLY)) {
+			c1 = 1;
+		}
+	}
+
+	if (c1 || c2) {
+		fprintf(scad_output, "\tline_segment_r(%f,%f,%f,%f,%f,%f,%d,%d,%d);\n",
+						scad_scale_coord(length),
+						scad_scale_coord((float) gc->width), scaled_layer_thickness,
+						scad_scale_coord(cx), scad_scale_coord(-cy), angle, bd, c1, c2);
+	}
+	else {
+		if (bd)
+			fprintf(scad_output, "\tline_segment(%f,%f,%f,%f,%f,%f);\n",
+							scad_scale_coord(length),
+							scad_scale_coord((float) gc->width), scaled_layer_thickness, scad_scale_coord(cx), scad_scale_coord(-cy), angle);
+	}
+
+}
+
+static void scad_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	if (drill_layer)
+		return;
+
+	scad_emit_line(gc, x1, y1, x2, y2, SCAD_EL_STANDARD);
+}
+
+
+static void scad_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	int i, n_steps, x, y, ox = 0, oy = 0, sa;
+	float angle;
+
+	if (drill_layer)
+		return;
+
+	n_steps = ((delta_angle < 0) ? (-delta_angle) : delta_angle + 4) / 5;
+	sa = start_angle + 180;
+	if (sa > 360)
+		sa -= 360;
+
+	for (i = 0; i <= n_steps; i++) {
+
+		angle = ((float) (sa) + ((float) delta_angle * (float) i / (float) n_steps)) * M_PI / 180.;
+
+		if (i) {
+			x = (int) ((float) width * cos(angle)) + cx;
+			y = -(int) ((float) height * sin(angle)) + cy;
+			scad_emit_line(gc, ox, oy, x, y, (i == (n_steps)) ? SCAD_EL_LASTPOLY : SCAD_EL_POLY);
+
+			ox = x;
+			oy = y;
+		}
+		else {
+			ox = (int) ((float) width * cos(angle)) + cx;
+			oy = -(int) ((float) height * sin(angle)) + cy;
+		}
+	}
+}
+
+
+/* Emit the cylinder - its appearance depends on layer it is located on:
+*    - as plated or unplated drills it creates vector of holes
+*    - otherwise it is drawn as simple
+*/
+static void scad_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+/*  int i; */
+	if (outline_layer)
+		return;
+
+	if (drill_layer && !gc->drill) {
+		return;
+	}
+	if (!drill_layer && gc->drill) {
+		return;
+	}
+
+	if (radius <= 0)
+		return;
+
+	if (drill_layer && ((2 * radius) < opt_minimal_drill))
+		return;
+
+	if (drill_layer) {
+		fprintf(scad_output, "\t[%f, [%f, %f]],\n", scad_scale_coord((float) radius), scad_scale_coord(cx), -scad_scale_coord(cy));
+	}
+	else {
+		fprintf(scad_output,
+						"\ttranslate ([%f, %f, 0]) cylinder (r=%f, h=%f, center=true, $fn=30);\n",
+						scad_scale_coord((float) cx), -scad_scale_coord((float) cy),
+						scad_scale_coord((float) radius), scaled_layer_thickness);
+	}
+}
+
+/*
+* Helper function - creates extruded polygon
+*/
+
+static void scad_emit_polygon(hidGC gc, int n_coords, Coord * x, Coord * y, float thickness)
+{
+	int i, n;
+/*  int cw, cx; */
+
+
+	fprintf(scad_output, "\ttranslate ([0, 0, %f]) linear_extrude(height=%f) polygon ([", -thickness / 2., thickness);
+
+
+/* Normalize polygon - remove last point, if it is equal to first point */
+
+	n = n_coords;
+	if (x[n_coords - 1] == x[0] && y[n_coords - 1] == y[0])
+		n--;
+
+/* Polygon points*/
+
+	for (i = 0; i < n; i++) {
+		fprintf(scad_output, "\t\t[%f, %f]%s\n",
+						scad_scale_coord((float) x[i]), -scad_scale_coord((float) y[i]), (i < (n - 1)) ? ", " : "");
+	}
+
+	fprintf(scad_output, "\t],[[\n\t\t");
+
+	for (i = 0; i < n; i++) {
+		if (!(i % 10) && i)
+			fprintf(scad_output, "\n\t\t");
+		fprintf(scad_output, "%d%s", i, (i < (n - 1)) ? ", " : "");
+	}
+	fprintf(scad_output, "]]);\n");
+
+}
+
+static void scad_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	if (outline_layer)
+		return;
+
+	scad_emit_polygon(gc, n_coords, x, y, scaled_layer_thickness);
+}
+
+static void scad_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	Coord x[5];
+	Coord y[5];
+
+	if (scad_output)
+		fprintf(scad_output, "// Fill rect\n");
+
+	x[0] = x[4] = x1;
+	y[0] = y[4] = y1;
+	x[1] = x1;
+	y[1] = y2;
+	x[2] = x2;
+	y[2] = y2;
+	x[3] = x2;
+	y[3] = y1;
+	scad_fill_polygon(gc, 5, x, y);
+}
+
+static void scad_calibrate(double xval, double yval)
+{
+ fprintf(stderr, "HID error: pcb called unimplemented openscad function scad_calibrate.\n");
+ abort();
+}
+
+static void scad_set_crosshair(int x, int y, int action)
+{
+	if (scad_output)
+		fprintf(scad_output, "// Set CrossHair\n");
+}
+
+static const char *openscad_cookie = "openscad exporter";
+
+static HID scad_hid;
+
+pcb_uninit_t hid_export_openscad_init()
+{
+	memset(&scad_hid, 0, sizeof(scad_hid));
+
+	common_nogui_init(&scad_hid);
+	common_draw_helpers_init(&scad_hid);
+
+	scad_hid.struct_size = sizeof(scad_hid);
+	scad_hid.name = "openscad";
+	scad_hid.description = "OpenSCAD script export";
+	scad_hid.exporter = 1;
+
+	scad_hid.get_export_options = scad_get_export_options;
+	scad_hid.do_export = scad_do_export;
+	scad_hid.parse_arguments = scad_parse_arguments;
+	scad_hid.set_layer = scad_set_layer;
+	scad_hid.calibrate = scad_calibrate;
+	scad_hid.set_crosshair = scad_set_crosshair;
+
+	scad_hid.make_gc = scad_make_gc;
+	scad_hid.destroy_gc = scad_destroy_gc;
+	scad_hid.use_mask = scad_use_mask;
+	scad_hid.set_color = scad_set_color;
+	scad_hid.set_line_cap = scad_set_line_cap;
+	scad_hid.set_line_width = scad_set_line_width;
+	scad_hid.set_draw_xor = scad_set_draw_xor;
+
+	scad_hid.draw_line = scad_draw_line;
+	scad_hid.draw_arc = scad_draw_arc;
+	scad_hid.draw_rect = scad_draw_rect;
+	scad_hid.fill_circle = scad_fill_circle;
+	scad_hid.fill_polygon = scad_fill_polygon;
+	scad_hid.fill_rect = scad_fill_rect;
+
+	hid_register_hid(&scad_hid);
+
+	hid_register_attributes(scad_options, sizeof(scad_options) / sizeof(scad_options[0]), openscad_cookie, 0);
+	return NULL;
+}
diff --git a/src_plugins/export_openscad/scad.h b/src_plugins/export_openscad/scad.h
new file mode 100644
index 0000000..4dafd60
--- /dev/null
+++ b/src_plugins/export_openscad/scad.h
@@ -0,0 +1,137 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  OpenSCAD export HID
+ *  This code is based on the GERBER and VRML export HID
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __SCAD_H
+#define __SCAD_H
+
+#define SCAD_EXT ".scad"
+#define SCAD_STL_EXT ".stl"
+
+/*
+#define SCAD_MAP_EXT ".3dm"
+#define SCAD_FPMAP_EXT ".fp.3dm"
+*/
+
+#define MODELBASE "models"
+#define SCADBASE "scad"
+#define SCADSCRIPTS "scripts"
+#define SCADSIMPLEMODELS "simple"
+
+
+/* dimensions (nanometer version) and colors*/
+#define METRIC_SCALE	0.000001
+
+#define SCAD_MIN_OUTLINE_DIST		100000
+
+#define BOARD_THICKNESS			1.6
+#define OUTER_COPPER_THICKNESS		0.035
+#define INNER_COPPER_THICKNESS		(OUTER_COPPER_THICKNESS / 2.)
+#define MASK_THICKNESS			(OUTER_COPPER_THICKNESS + 0.02 )
+#define SILK_LAYER_THICKNESS		( OUTER_COPPER_THICKNESS + 0.0025 )
+#define SILK_LAYER_OFFSET		( ( BOARD_THICKNESS + SILK_LAYER_THICKNESS ) / 2. )
+#define SILK_LAYER_OFFSET2		( ( BOARD_THICKNESS + SILK_LAYER_THICKNESS ) / 2. + MASK_THICKNESS)
+#define HOLE_THICKNESS			( BOARD_THICKNESS + 2. * OUTER_COPPER_THICKNESS + 0.1)
+#define PLATING_THICKNESS		( BOARD_THICKNESS + 2. * OUTER_COPPER_THICKNESS)
+#define HOLE_PLATING			0.0175
+
+#define SCAD_BOARD_COLOR		"[0.44, 0.44, 0]"
+#define SCAD_SILK_COLOR			"[1, 1, 1]"
+#define SCAD_COPPER_COLOR		"[1, 0.4, 0.2]"
+#define SCAD_COPPER_COLOR_TIN		"[0.76, 0.76, 0.76]"
+#define SCAD_COPPER_COLOR_GOLD		"[1, 0.85, 0.24]"
+#define SCAD_MASK_COLOR_G		"[0, 0.4, 0.2, 0.65]"
+#define SCAD_MASK_COLOR_R		"[0.8, 0.1, 0.1, 0.65]"
+#define SCAD_MASK_COLOR_B		"[0.1, 0.1, 0.8, 0.65]"
+
+
+#define SCAD_COMPONENT_NONE		0
+#define SCAD_COMPONENT_BOXES		1
+#define SCAD_COMPONENT_SIMPLE		2
+#define SCAD_COMPONENT_REALISTIC	3
+
+#define EXPORT_COMPONENTS ((opt_exp_component == SCAD_COMPONENT_BOXES) || (opt_exp_component == SCAD_COMPONENT_SIMPLE) || (opt_exp_component == SCAD_COMPONENT_REALISTIC))
+
+#define SCAD_OUTLINE_NONE	0
+#define SCAD_OUTLINE_OUTLINE	1
+#define SCAD_OUTLINE_SIZE	2
+
+#define SCAD_COPPER_COPPER	0
+#define SCAD_COPPER_GOLD	1
+#define SCAD_COPPER_TIN		2
+
+#define SCAD_MASK_NONE		0
+#define SCAD_MASK_GREEN		1
+#define SCAD_MASK_BLUE		2
+#define SCAD_MASK_RED		3
+
+#define SCAD_CUT_COMPLETE	0
+#define SCAD_CUT_TOP		1
+#define SCAD_CUT_TOP_ONLY	2
+#define SCAD_CUT_BOTTOM		3
+#define SCAD_CUT_BOTTOM_ONLY	4
+
+#define MAX_LAYER_COLORS (MAX_LAYER *2)
+
+/* polygon attributes */
+
+#define POLY_CW		1
+#define POLY_CCW	2
+
+#define POLY_CONVEX	1
+#define POLY_CONCAVE	2
+
+#define SCAD_EL_STANDARD	0
+#define SCAD_EL_POLY		1
+#define SCAD_EL_LASTPOLY	2
+
+typedef struct color_table_struct {
+	int r, g, b;
+} color_table_struct;
+
+typedef struct hid_gc_struct {
+	EndCapStyle cap;
+	int width;
+	int erase;
+	int drill;
+	int r, g, b;
+	int seq;
+} hid_gc_struct;
+
+typedef struct {
+	int processed;
+	Coord x1, y1, x2, y2;
+} t_outline_segment;
+
+extern FILE *scad_output;
+
+extern void scad_write_prologue();
+extern void scad_generate_holes();
+extern void scad_generate_plating();
+extern void scad_generate_board();
+extern void scad_generate_mask();
+extern float scad_scale_coord(float x);
+
+void scad_process_components(int mode);
+
+#endif
diff --git a/src_plugins/export_openscad/scadcomp.c b/src_plugins/export_openscad/scadcomp.c
new file mode 100644
index 0000000..6d2eca5
--- /dev/null
+++ b/src_plugins/export_openscad/scadcomp.c
@@ -0,0 +1,558 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  OpenSCAD export HID
+ *  This code is based on the GERBER and VRML export HID
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <time.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "error.h"
+#include "buffer.h"
+#include "mirror.h"
+#include "create.h"
+#include "misc_util.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "hid_init.h"
+
+#include "scad.h"
+
+/* model types for scad_export_model */
+#define SCAD_STANDARD	0					/* primary model */
+#define SCAD_OVERLAY	1
+
+
+static char *include_files_list;
+static int include_files_size, include_files_bufsize, include_file_maxlength;
+
+static void scad_init_include_files(void)
+{
+	include_files_list = NULL;
+	include_files_size = 0;
+	include_files_bufsize = 0;
+	include_file_maxlength = 0;
+}
+
+static void scad_free_include_files(void)
+{
+	if (include_files_list)
+		free(include_files_list);
+}
+
+static void scad_add_include_file(char *include_file)
+{
+
+	int ln = strlen(include_file);
+	int new_length = ln + include_files_size + 1;
+	int ptr;
+	char *bf;
+
+/* quietly assumimng, that include file name is shorter than 2048 characters */
+	if (!include_files_list || (include_files_list != NULL && new_length > include_files_bufsize)) {
+		bf = malloc(2048 + include_files_bufsize);
+		if (bf) {
+			include_files_list = bf;
+			include_files_size = 0;
+			include_files_bufsize = 2048;
+		}
+		else {
+			Message(PCB_MSG_ERROR, "openscad: cannot allocate memory for component included files.\n");
+		}
+	}
+
+/* Check, if the file is already in list */
+	ptr = 0;
+	while (ptr < include_files_size) {
+		if (strcmp(include_files_list + ptr, include_file) == 0) {
+			return;
+		}
+		ptr += strlen(include_files_list + ptr) + 1;
+	}
+	strcpy(include_files_list + ptr, include_file);
+	include_files_size = new_length;
+	if (ln > include_file_maxlength) {
+		include_file_maxlength = ln;
+	}
+}
+
+static void scad_export_include_files(void)
+{
+	int ptr;
+	char *fullname;
+	char line[2048];
+	int l;
+	FILE *f;
+#warning TODO: no libdir
+	char *pcblibdir = "TODO_libdir63";
+
+	if (!include_files_list)
+		return;
+
+	l = strlen(pcblibdir) + 1 + strlen(MODELBASE) + 1 + strlen(SCADBASE) + 1 + include_file_maxlength + 1;
+	if ((fullname = (char *) malloc(l * sizeof(char))) == NULL) {
+		Message(PCB_MSG_ERROR, "openscad: cannot allocate memory for component included files.\n");
+		return;
+	}
+	sprintf(fullname, "%s%s%s%s%s%s", pcblibdir, PCB_DIR_SEPARATOR_S,
+					MODELBASE, PCB_DIR_SEPARATOR_S, SCADBASE, PCB_DIR_SEPARATOR_S);
+
+	l = strlen(fullname);					/* index to be used to append include file names */
+
+	fprintf(scad_output, "/***************************************************/\n");
+	fprintf(scad_output, "/*                                                 */\n");
+	fprintf(scad_output, "/* Embedded include files                          */\n");
+	fprintf(scad_output, "/*                                                 */\n");
+	fprintf(scad_output, "/***************************************************/\n");
+
+	ptr = 0;
+	while (ptr < include_files_size) {
+		strcpy(fullname + l, include_files_list + ptr);
+		/* printf ("[%s] @ %s\n", include_files_list + ptr, fullname); */
+		f = fopen(fullname, "r");
+		if (f) {
+			while (fgets(line, sizeof(line), f)) {
+				fputs(line, scad_output);
+			}
+			fclose(f);
+		}
+		else {
+			fprintf(scad_output, "include <%s>\n", include_files_list + ptr);
+		}
+
+		ptr += strlen(include_files_list + ptr) + 1;
+	}
+	fprintf(scad_output, "\n");
+
+}
+
+
+/***********************************************************************
+*
+* Export of the components
+*
+***********************************************************************/
+static void scad_imported_model_name(char *model, char *name, int size, pcb_bool simple)
+{
+
+	sprintf(name, "%s%s%s%s", model, (simple) ? "-" : "", (simple) ? SCADSIMPLEMODELS : "", SCAD_STL_EXT);
+}
+
+static void scad_close_model(FILE * f)
+{
+	if (f) {
+		fclose(f);
+	}
+}
+
+static FILE *scad_open_model(char *model, char *first_line, int size, pcb_bool simple)
+{
+	int l;
+	FILE *f = NULL;
+	char *cmd;
+#warning TODO: no libdir
+	char *pcblibdir = "TODO_libdir63";
+
+	l =
+		strlen(pcblibdir) + 1 + strlen(MODELBASE) + 1 + strlen(SCADBASE) + 1 +
+		strlen(SCADSIMPLEMODELS) + 1 + strlen(model) + 1 + strlen(SCAD_EXT);
+	if ((cmd = (char *) malloc(l * sizeof(char))) != NULL) {
+		sprintf(cmd, "%s%s%s%s%s%s%s%s%s%s", pcblibdir, PCB_DIR_SEPARATOR_S,
+						MODELBASE, PCB_DIR_SEPARATOR_S, SCADBASE, PCB_DIR_SEPARATOR_S,
+						model, (simple) ? "-" : "", (simple) ? SCADSIMPLEMODELS : "", SCAD_EXT);
+
+		f = fopen(cmd, "r");
+#if 0
+		if (!f) {
+			sprintf(cmd, "%s%s%s%s%s%s%s%s%s%s", pcblibdir,
+							PCB_DIR_SEPARATOR_S, MODELBASE, PCB_DIR_SEPARATOR_S,
+							SCADBASE, PCB_DIR_SEPARATOR_S,
+							(simple) ? SCADSIMPLEMODELS : "", (simple) ? PCB_DIR_SEPARATOR_S : "", model, SCAD_EXT);
+			f = fopen(cmd, "r");
+		}
+#endif
+	}
+
+	if (cmd)
+		free(cmd);
+
+	if (f && fgets(first_line, size, f))
+		return f;
+
+	if (f)
+		scad_close_model(f);
+
+	return NULL;
+
+}
+
+static void scad_process_line(char *line)
+{
+	char *s0, *s1, *s2;
+
+	if ((s0 = strstr(line, "include")) != NULL && (s1 = strchr(s0, '<')) != NULL && (s2 = strchr(s1, '>')) != NULL) {
+		*s2 = 0;
+		scad_add_include_file(s1 + 1);
+	}
+	else {
+		fputs(line, scad_output);
+	}
+}
+
+
+static int scad_parse_coord_triplet(char *s, Coord * ox, Coord * oy, Coord * oz)
+{
+	Coord xx = 0, yy = 0, zz = 0;
+	int n = 0, ln = 0;
+	char val[32];
+
+	while (sscanf(s, "%30s%n", val, &ln) >= 1) {
+		switch (n) {
+		case 0:
+			xx = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 1:
+			yy = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 2:
+			zz = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		}
+		s = s + ln;
+		n++;
+	}
+	if (n == 3) {
+		*ox = xx;
+		*oy = yy;
+		*oz = zz;
+		return pcb_true;
+	}
+	else {
+		return pcb_false;
+	}
+}
+
+static int scad_parse_float_triplet(char *s, float *ox, float *oy, float *oz)
+{
+	float xx = 0, yy = 0, zz = 0;
+
+	if (sscanf(s, "%f %f %f", &xx, &yy, &zz) == 3) {
+		*ox = xx;
+		*oy = yy;
+		*oz = zz;
+		return pcb_true;
+	}
+	else {
+		return pcb_false;
+	}
+}
+
+/************************************************************
+* Export of the single model
+* - adjusts the model position, rotation, scale
+* - processes the model line-by-line and perfoprms variable expansion
+************************************************************/
+static void scad_export_model(int model_type, ElementType * element, pcb_bool imported, FILE * f, char *line, int size)
+{
+	char *model_rotation, *model_translate, *model_scale, *model_angle;
+	Angle tmp_angle = (Angle) 0;
+	Coord tx, ty, tz;
+	float fx, fy, fz;
+
+	int x = element->MarkX, y = element->MarkY;
+
+	model_rotation =
+		AttributeGetFromList(&(element->Attributes),
+												 (model_type == SCAD_OVERLAY) ? "OpenSCAD::Overlay:rotate" : "OpenSCAD::Model:rotate");
+	model_scale =
+		AttributeGetFromList(&(element->Attributes),
+												 (model_type == SCAD_OVERLAY) ? "OpenSCAD::Overlay:scale" : "OpenSCAD::Model:scale");
+	model_translate =
+		AttributeGetFromList(&(element->Attributes),
+												 (model_type == SCAD_OVERLAY) ? "OpenSCAD::Overlay:translate" : "OpenSCAD::Model:translate");
+
+	if ((model_angle = AttributeGetFromList(&(element->Attributes), "Footprint::RotationTracking")) != NULL) {
+		sscanf(model_angle, "%lf", &tmp_angle);
+	}
+
+	if (model_translate && scad_parse_coord_triplet(model_translate, &tx, &ty, &tz))
+		fprintf(scad_output, "translate ([%f, %f, %f]) ", scad_scale_coord(tx), scad_scale_coord(ty), scad_scale_coord(tz));
+
+	fprintf(scad_output, "translate ([%f, %f, %f]) ",
+					scad_scale_coord((float) x), -scad_scale_coord((float) y),
+					((TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) ? -1. : 1.) * (BOARD_THICKNESS / 2. + OUTER_COPPER_THICKNESS));
+
+	/* rotate order: angle onsolder user-defined */
+	if (tmp_angle != 0.)
+		fprintf(scad_output, "rotate ([0, 0, %f]) ", (TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) ? -tmp_angle : tmp_angle);
+
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, (element)))
+		fprintf(scad_output, "rotate([180.,0,0]) ");
+
+	if (model_rotation && scad_parse_float_triplet(model_rotation, &fx, &fy, &fz))
+		fprintf(scad_output, "rotate ([%f, %f, %f]) ", fx, fy, fz);
+
+	if (model_scale && scad_parse_float_triplet(model_scale, &fx, &fy, &fz))
+		fprintf(scad_output, "scale ([%f, %f, %f]) ", fx, fy, fz);
+
+	if (imported) {
+		fprintf(scad_output, "{ import(\"%s\"); }\n", line);
+
+	}
+	else {
+		fprintf(scad_output, "{\n");
+
+		/* Flush first line of text, already read in buffer */
+		scad_process_line(line);
+
+		while (fgets(line, size, f)) {
+			scad_process_line(line);
+		}
+		fprintf(scad_output, "}\n");
+	}
+}
+
+
+
+extern void FreeRotateBuffer(BufferType * Buffer, Angle angle);
+
+static int scad_calculate_bbox(ElementType * element, Angle angle, float *w, float *h, float *ox, float *oy)
+{
+	return 0;
+
+/*
+  TODO: automatic calculation of bounding box
+
+  BufferType element_buffer;
+
+  element_buffer.Data = CreateNewBuffer ();
+
+  -- Copy
+  AddElementToBuffer (ElementType *Element)
+  if (ON_SIDE(Element,(Settings.ShowBottomSide)?BOTTOM_SIDE:TOP_SIDE))
+     MirrorElementCoordinates (element_buffer.Data, element, 0);
+
+  ClearBuffer (&element_buffer);
+*/
+}
+
+/************************************************************
+* Export of the single element
+* - identifies the model for the component - primary and overlay
+* - exports both models
+************************************************************/
+static void scad_export_bbox(ElementType * element)
+{
+	char *model_angle, *bbox;
+	Angle tmp_angle = (Angle) 0;
+	float w = 0., h = 0., t = 0., ox = 0., oy = 0.;
+	int x = element->MarkX, y = element->MarkY;
+	int n, ln;
+	char val[32], *s;
+
+	if ((bbox = AttributeGetFromList(&(element->Attributes), "Footprint::BoundingBox")) == NULL)
+		return;
+
+
+	if ((model_angle = AttributeGetFromList(&(element->Attributes), "Footprint::RotationTracking")) != NULL) {
+		sscanf(model_angle, "%lf", &tmp_angle);
+	}
+
+	/* Parse values with units... */
+	s = bbox;
+	n = 0;
+	while (sscanf(s, "%30s%n", val, &ln) >= 1) {
+		switch (n) {
+		case 0:
+			w = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 1:
+			h = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 2:
+			t = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 3:
+			ox = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		case 4:
+			oy = GetValueEx(val, NULL, NULL, NULL, "mm", NULL);
+			break;
+		}
+		s = s + ln;
+		n++;
+	}
+
+	if (n == 3) {
+		ox = 0.;
+		oy = 0.;
+	}
+	else if (n == 1) {
+		/* Try automatically calculate the bounding box */
+		t = w;
+		if (!scad_calculate_bbox(element, tmp_angle, &w, &h, &ox, &oy))
+			return;
+	}
+	else if (n != 5)
+		return;
+
+	fprintf(scad_output, "translate ([%f, %f, %f]) ",
+					scad_scale_coord((float) x), -scad_scale_coord((float) y),
+					((TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) ? -1. : 1.) * (BOARD_THICKNESS / 2. + OUTER_COPPER_THICKNESS));
+
+	if (tmp_angle != 0.)
+		fprintf(scad_output, "rotate ([0, 0, %f]) ", (TEST_FLAG(PCB_FLAG_ONSOLDER, (element))) ? -tmp_angle : tmp_angle);
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, (element)))
+		fprintf(scad_output, "rotate([180.,0,0]) ");
+
+	fprintf(scad_output, "{\n");
+
+	fprintf(scad_output,
+					"translate ([%f, %f, %f]) color ([0.2, 0.2, 0.2]) cube ([%f,%f,%f],true);\n",
+					scad_scale_coord((float) ox), scad_scale_coord((float) oy),
+					scad_scale_coord((float) t / 2.), scad_scale_coord((float) w),
+					scad_scale_coord((float) h), scad_scale_coord((float) t));
+
+	fprintf(scad_output, "}\n");
+
+}
+
+static void scad_writeout_element(ElementType * element, char *name, int model_type, pcb_bool imported, pcb_bool simple)
+{
+	FILE *f = NULL;
+	char line[2048];
+
+	if (imported) {
+		scad_imported_model_name(name, line, sizeof(line), simple);
+		scad_export_model(model_type, element, imported, f, line, sizeof(line));
+	}
+	else {
+		/* if model is defined, try to open it */
+		f = scad_open_model(name, line, sizeof(line), simple);
+
+		if (f) {
+			scad_export_model(model_type, element, imported, f, line, sizeof(line));
+			scad_close_model(f);
+		}
+	}
+}
+
+/************************************************************
+* Export of the single element
+* - identifies the model for the component - primary and overlay
+* - exports both models
+************************************************************/
+static void scad_export_element(ElementType * element, pcb_bool simple)
+{
+	char *model_name, *s;
+	pcb_bool imported_model;
+
+	s = AttributeGetFromList(&(element->Attributes), "OpenSCAD::Model:type");
+	imported_model = s && (strcmp(s, "STL") == 0);
+
+	/* get model name from attibute */
+	model_name = AttributeGetFromList(&(element->Attributes), "OpenSCAD::Model");
+
+	if (model_name) {
+		scad_writeout_element(element, model_name, SCAD_STANDARD, imported_model, simple);
+	}
+	else {
+		/* no model variable found, try model, based on footprint name attribute */
+		model_name = AttributeGetFromList(&(element->Attributes), "Footprint::File");
+		if (model_name) {
+			scad_writeout_element(element, model_name, SCAD_STANDARD, imported_model, simple);
+		}
+		else {
+			/* still no model found, try model, based on description */
+			model_name = DESCRIPTION_NAME(element);
+			if (model_name) {
+				scad_writeout_element(element, model_name, SCAD_STANDARD, imported_model, simple);
+			}
+		}
+	}
+
+	s = AttributeGetFromList(&(element->Attributes), "OpenSCAD::Overlay:type");
+	imported_model = s && (strcmp(s, "STL") == 0);
+
+	/* get overlay name from attibute */
+	model_name = AttributeGetFromList(&(element->Attributes), "OpenSCAD::Overlay");
+
+	if (model_name) {
+		scad_writeout_element(element, model_name, SCAD_OVERLAY, imported_model, simple);
+	}
+
+	return;
+}
+
+/************************************************************
+* Main function for components export
+* - initialize footprint and element database
+* - loops through all components on the board and for each component:
+*   - loads the footprint into temporary buffer and calculates anfgle
+*   - export the element
+************************************************************/
+void scad_process_components(int mode)
+{
+
+	scad_init_include_files();
+
+
+	fprintf(scad_output, "module all_components() {\n");
+
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if ((mode == SCAD_COMPONENT_SIMPLE) || (mode == SCAD_COMPONENT_REALISTIC)) {
+			scad_export_element(element, (mode == SCAD_COMPONENT_SIMPLE) ? 1 : 0);
+		}
+		else {
+			scad_export_bbox(element);
+		}
+	}
+
+	END_LOOP;
+
+	fprintf(scad_output, "}\n\n");
+
+	scad_export_include_files();
+
+	scad_free_include_files();
+
+}
diff --git a/src_plugins/export_openscad/scadproto.c b/src_plugins/export_openscad/scadproto.c
new file mode 100644
index 0000000..033d1e8
--- /dev/null
+++ b/src_plugins/export_openscad/scadproto.c
@@ -0,0 +1,118 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  OpenSCAD export HID
+ *  This code is based on the GERBER and VRML export HID
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <time.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "error.h"
+#include "buffer.h"
+#include "create.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "hid_init.h"
+
+#include "scad.h"
+
+
+void scad_write_prologue()
+{
+	fputs("//SCAD\n\n", scad_output);
+
+	fputs
+		("module line_segment_r(length, width, thickness, x, y, a, bd, c1, c2) {\n"
+		 "\ttranslate([x,y,0]) rotate ([0,0,a]) union() {\n"
+		 "\t\tif (bd) {cube ([length, width,thickness],true);}\n"
+		 "\t\tif (c2) {translate([length/2.,0,0]) cylinder(h=thickness, r=width/2,center=true,$fn=30);}\n"
+		 "\t\tif (c1) { translate([-length/2.,0,0]) cylinder(h=thickness, r=width/2,center=true,$fn=30);}\n"
+		 "\t}\n" "}\n\n", scad_output);
+
+	fputs("module line_segment(length, width, thickness, x, y, a) {\n"
+				"\ttranslate([x,y,0]) rotate ([0,0,a]) {\n"
+				"\t\tcube ([length, width,thickness],true);\n" "\t}\n" "}\n\n", scad_output);
+}
+
+void scad_generate_holes()
+{
+	fprintf(scad_output, "module all_holes() {\n\tplating=%f;\n", HOLE_PLATING);
+	fprintf(scad_output, "\tunion () {\n");
+	fprintf(scad_output, "\t\tfor (i = layer_pdrill_list) {\n");
+	fprintf(scad_output,
+					"\t\t\ttranslate([i[1][0],i[1][1],0]) cylinder(r=i[0]+2*plating, h=%f, center=true, $fn=30);\n", HOLE_THICKNESS);
+	fprintf(scad_output, "\t\t}\n");
+	fprintf(scad_output, "\t\tfor (i = layer_udrill_list) {\n");
+	fprintf(scad_output, "\t\t\ttranslate([i[1][0],i[1][1],0]) cylinder(r=i[0], h=%f, center=true, $fn=30);\n", HOLE_THICKNESS);
+	fprintf(scad_output, "\t\t}\n");
+	fprintf(scad_output, "\t}\n");
+	fprintf(scad_output, "}\n\n");
+}
+
+void scad_generate_plating()
+{
+	fprintf(scad_output, "module all_plating() {\n");
+	fprintf(scad_output, "\tplating=%f;\n", HOLE_PLATING + 0.02);
+	fprintf(scad_output, "\tunion () {\n");
+	fprintf(scad_output, "\t\tfor (i = layer_pdrill_list) {\n");
+	fprintf(scad_output, "\t\t\ttranslate([i[1][0],i[1][1],0]) difference () {\n");
+	fprintf(scad_output, "\t\t\t\tcylinder(r=i[0]+2*plating, h=%f, center=true, $fn=30);\n", PLATING_THICKNESS + 0.01);
+	fprintf(scad_output, "\t\t\t\tcylinder(r=i[0]-0.01, h=%f, center=true, $fn=30);\n", PLATING_THICKNESS + 0.2);
+	fprintf(scad_output, "\t\t\t}\n");
+	fprintf(scad_output, "\t\t}\n");
+	fprintf(scad_output, "\t}\n");
+	fprintf(scad_output, "}\n\n");
+}
+
+void scad_generate_board()
+{
+	fprintf(scad_output, "module board_body() {\n");
+	fprintf(scad_output,
+					"\ttranslate ([0, 0, %f]) linear_extrude(height=%f) board_outline();", -BOARD_THICKNESS / 2., BOARD_THICKNESS);
+	fprintf(scad_output, "}\n\n");
+}
+
+void scad_generate_mask()
+{
+	fprintf(scad_output, "module mask_surface() {\n");
+	fprintf(scad_output,
+					"\ttranslate ([0, 0, %f]) linear_extrude(height=%f) board_outline();", -MASK_THICKNESS / 2., MASK_THICKNESS);
+	fprintf(scad_output, "}\n\n");
+}
diff --git a/src_plugins/export_png/Makefile b/src_plugins/export_png/Makefile
new file mode 100644
index 0000000..8b4cfea
--- /dev/null
+++ b/src_plugins/export_png/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_png
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_png/Plug.tmpasm b/src_plugins/export_png/Plug.tmpasm
new file mode 100644
index 0000000..babad9f
--- /dev/null
+++ b/src_plugins/export_png/Plug.tmpasm
@@ -0,0 +1,16 @@
+put /local/pcb/mod {export_png}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_png/png.o @]
+
+switch /local/pcb/export_png/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/LDFLAGS         libs/gui/gd/ldflags
+		put /local/pcb/mod/CFLAGS          libs/gui/gd/cflags
+		end
+end
+
+switch /local/pcb/export_png/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_png/README b/src_plugins/export_png/README
new file mode 100644
index 0000000..58b458a
--- /dev/null
+++ b/src_plugins/export_png/README
@@ -0,0 +1,5 @@
+Export to png, gif and jpeg
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_png/png.c b/src_plugins/export_png/png.c
new file mode 100644
index 0000000..ac5c805
--- /dev/null
+++ b/src_plugins/export_png/png.c
@@ -0,0 +1,1533 @@
+ /*
+  *                            COPYRIGHT
+  *
+  *  PCB, interactive printed circuit board design
+  *  Copyright (C) 2006 Dan McMahill
+  *
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+  *  the Free Software Foundation; either version 2 of the License, or
+  *  (at your option) any later version.
+  *
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+  *
+  *  You should have received a copy of the GNU General Public License
+  *  along with this program; if not, write to the Free Software
+  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  *
+  */
+
+/*
+ *  Heavily based on the ps HID written by DJ Delorie
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "layer.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "plugins.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "png.h"
+
+/* the gd library which makes this all so easy */
+#include <gd.h>
+
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_color.h"
+#include "hid_helper.h"
+#include "hid_flags.h"
+
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", func); abort()
+
+static HID png_hid;
+
+const char *png_cookie = "png HID";
+
+static void *color_cache = NULL;
+static void *brush_cache = NULL;
+
+static double bloat = 0;
+static double scale = 1;
+static Coord x_shift = 0;
+static Coord y_shift = 0;
+static int show_solder_side;
+#define SCALE(w)   ((int)((w)/scale + 0.5))
+#define SCALE_X(x) ((int)(((x) - x_shift)/scale))
+#define SCALE_Y(y) ((int)(((show_solder_side ? (PCB->MaxHeight-(y)) : (y)) - y_shift)/scale))
+#define SWAP_IF_SOLDER(a,b) do { int c; if (show_solder_side) { c=a; a=b; b=c; }} while (0)
+
+/* Used to detect non-trivial outlines */
+#define NOT_EDGE_X(x) ((x) != 0 && (x) != PCB->MaxWidth)
+#define NOT_EDGE_Y(y) ((y) != 0 && (y) != PCB->MaxHeight)
+#define NOT_EDGE(x,y) (NOT_EDGE_X(x) || NOT_EDGE_Y(y))
+
+static void png_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+
+/* The result of a failed gdImageColorAllocate() call */
+#define BADC -1
+
+typedef struct color_struct {
+	/* the descriptor used by the gd library */
+	int c;
+
+	/* so I can figure out what rgb value c refers to */
+	unsigned int r, g, b, a;
+
+} color_struct;
+
+typedef struct hid_gc_struct {
+	HID *me_pointer;
+	EndCapStyle cap;
+	int width;
+	unsigned char r, g, b;
+	color_struct *color;
+	gdImagePtr brush;
+	int is_erase;
+} hid_gc_struct;
+
+static color_struct *black = NULL, *white = NULL;
+static gdImagePtr im = NULL, master_im, mask_im = NULL;
+static FILE *f = 0;
+static int linewidth = -1;
+static int lastgroup = -1;
+static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1);
+static int lastcap = -1;
+static int print_group[MAX_LAYER];
+static int print_layer[MAX_LAYER];
+
+/* For photo-mode we need the following layers as monochrome masks:
+
+   top soldermask
+   top silk
+   copper layers
+   drill
+*/
+
+#define PHOTO_FLIP_X	1
+#define PHOTO_FLIP_Y	2
+
+static int photo_mode, photo_flip;
+static gdImagePtr photo_copper[MAX_LAYER + 2];
+static gdImagePtr photo_silk, photo_mask, photo_drill, *photo_im;
+static gdImagePtr photo_outline;
+static int photo_groups[MAX_LAYER + 2], photo_ngroups;
+static int photo_has_inners;
+
+static int doing_outline, have_outline;
+
+#define FMT_gif "GIF"
+#define FMT_jpg "JPEG"
+#define FMT_png "PNG"
+
+/* If this table has no elements in it, then we have no reason to
+   register this HID and will refrain from doing so at the end of this
+   file.  */
+
+#undef HAVE_SOME_FORMAT
+
+static const char *filetypes[] = {
+#ifdef HAVE_GDIMAGEPNG
+	FMT_png,
+#define HAVE_SOME_FORMAT 1
+#endif
+
+#ifdef HAVE_GDIMAGEGIF
+	FMT_gif,
+#define HAVE_SOME_FORMAT 1
+#endif
+
+#ifdef HAVE_GDIMAGEJPEG
+	FMT_jpg,
+#define HAVE_SOME_FORMAT 1
+#endif
+
+	NULL
+};
+
+HID_Attribute png_attribute_list[] = {
+	/* other HIDs expect this to be first.  */
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --outfile <string>
+Name of the file to be exported to. Can contain a path.
+ at end ftable
+%end-doc
+*/
+	{"outfile", "Graphics output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_pngfile 0
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --dpi
+Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout.
+ at end ftable
+%end-doc
+*/
+	{"dpi", "Scale factor (pixels/inch). 0 to scale to specified size",
+	 HID_Integer, 0, 10000, {100, 0, 0}, 0, 0},
+#define HA_dpi 1
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --x-max
+Width of the png image in pixels. No constraint, when set to 0.
+ at end ftable
+%end-doc
+*/
+	{"x-max", "Maximum width (pixels).  0 to not constrain",
+	 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
+#define HA_xmax 2
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --y-max
+Height of the png output in pixels. No constraint, when set to 0.
+ at end ftable
+%end-doc
+*/
+	{"y-max", "Maximum height (pixels).  0 to not constrain",
+	 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
+#define HA_ymax 3
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --xy-max
+Maximum width and height of the PNG output in pixels. No constraint, when set to 0.
+ at end ftable
+%end-doc
+*/
+	{"xy-max", "Maximum width and height (pixels).  0 to not constrain",
+	 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
+#define HA_xymax 4
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --as-shown
+Export layers as shown on screen.
+ at end ftable
+%end-doc
+*/
+	{"as-shown", "Export layers as shown on screen",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_as_shown 5
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --monochrome
+Convert output to monochrome.
+ at end ftable
+%end-doc
+*/
+	{"monochrome", "Convert to monochrome",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_mono 6
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --only-vivible
+Limit the bounds of the exported PNG image to the visible items.
+ at end ftable
+%end-doc
+*/
+	{"only-visible", "Limit the bounds of the PNG image to the visible items",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_only_visible 7
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --use-alpha
+Make the background and any holes transparent.
+ at end ftable
+%end-doc
+*/
+	{"use-alpha", "Make the background and any holes transparent",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_use_alpha 8
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --format <string>
+File format to be exported. Parameter @code{<string>} can be @samp{PNG},
+ at samp{GIF}, or @samp{JPEG}.
+ at end ftable
+%end-doc
+*/
+	{"format", "Export file format",
+	 HID_Enum, 0, 0, {0, 0, 0}, filetypes, 0},
+#define HA_filetype 9
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --png-bloat <num><dim>
+Amount of extra thickness to add to traces, pads, or pin edges. The parameter
+ at samp{<num><dim>} is a number, appended by a dimension @samp{mm}, @samp{mil}, or
+ at samp{pix}. If no dimension is given, the default dimension is 1/100 mil.
+ at end ftable
+%end-doc
+*/
+	{"png-bloat", "Amount (in/mm/mil/pix) to add to trace/pad/pin edges (1 = 1/100 mil)",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_bloat 10
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at cindex photo-mode
+ at item --photo-mode
+Export a photo realistic image of the layout.
+ at end ftable
+%end-doc
+*/
+	{"photo-mode", "Photo-realistic export mode",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_photo_mode 11
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --photo-flip-x
+In photo-realistic mode, export the reverse side of the layout. Left-right flip.
+ at end ftable
+%end-doc
+*/
+	{"photo-flip-x", "Show reverse side of the board, left-right flip",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_photo_flip_x 12
+
+/* %start-doc options "93 PNG Options"
+ at ftable @code
+ at item --photo-flip-y
+In photo-realistic mode, export the reverse side of the layout. Up-down flip.
+ at end ftable
+%end-doc
+*/
+	{"photo-flip-y", "Show reverse side of the board, up-down flip",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_photo_flip_y 13
+
+	{"ben-mode", ATTR_UNDOCUMENTED,
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_ben_mode 11
+
+	{"ben-flip-x", ATTR_UNDOCUMENTED,
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_ben_flip_x 12
+
+	{"ben-flip-y", ATTR_UNDOCUMENTED,
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_ben_flip_y 13
+};
+
+#define NUM_OPTIONS (sizeof(png_attribute_list)/sizeof(png_attribute_list[0]))
+
+REGISTER_ATTRIBUTES(png_attribute_list, png_cookie)
+
+		 static HID_Attr_Val png_values[NUM_OPTIONS];
+
+		 static const char *get_file_suffix(void)
+{
+	const char *result = NULL;
+	const char *fmt;
+
+	fmt = filetypes[png_attribute_list[HA_filetype].default_val.int_value];
+
+	if (fmt == NULL);							/* Do nothing */
+	else if (strcmp(fmt, FMT_gif) == 0)
+		result = ".gif";
+	else if (strcmp(fmt, FMT_jpg) == 0)
+		result = ".jpg";
+	else if (strcmp(fmt, FMT_png) == 0)
+		result = ".png";
+
+	if (result == NULL) {
+		fprintf(stderr, "Error:  Invalid graphic file format\n");
+		result = ".???";
+	}
+	return result;
+}
+
+static HID_Attribute *png_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+	const char *suffix = get_file_suffix();
+
+	if (PCB)
+		derive_default_filename(PCB->Filename, &png_attribute_list[HA_pngfile], suffix, &last_made_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return png_attribute_list;
+}
+
+static int comp_layer, solder_layer;
+
+static int group_for_layer(int l)
+{
+	if (l < max_copper_layer + 2 && l >= 0)
+		return GetLayerGroupNumberByNumber(l);
+	/* else something unique */
+	return max_group + 3 + l;
+}
+
+static int layer_sort(const void *va, const void *vb)
+{
+	int a = *(int *) va;
+	int b = *(int *) vb;
+	int al = group_for_layer(a);
+	int bl = group_for_layer(b);
+	int d = bl - al;
+
+	if (a >= 0 && a <= max_copper_layer + 1) {
+		int aside = (al == solder_layer ? 0 : al == comp_layer ? 2 : 1);
+		int bside = (bl == solder_layer ? 0 : bl == comp_layer ? 2 : 1);
+		if (bside != aside)
+			return bside - aside;
+	}
+	if (d)
+		return d;
+	return b - a;
+}
+
+static const char *filename;
+static BoxType *bounds;
+static int in_mono, as_shown;
+
+static void parse_bloat(const char *str)
+{
+	int n;
+	UnitList extra_units = {
+		{"pix", 0, 0},
+		{"px", 0, 0},
+		{"", 0, 0}
+	};
+	for(n = 0; n < (sizeof(extra_units)/sizeof(extra_units[0]))-1; n++)
+		extra_units[n].scale = scale;
+	if (str == NULL)
+		return;
+	bloat = GetValueEx(str, NULL, NULL, extra_units, "", NULL);
+}
+
+void png_hid_export_to_file(FILE * the_file, HID_Attr_Val * options)
+{
+	int i;
+	static int saved_layer_stack[MAX_LAYER];
+	BoxType region;
+
+	f = the_file;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	if (options[HA_only_visible].int_value)
+		bounds = GetDataBoundingBox(PCB->Data);
+	else
+		bounds = ®ion;
+
+	memset(print_group, 0, sizeof(print_group));
+	memset(print_layer, 0, sizeof(print_layer));
+
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *layer = PCB->Data->Layer + i;
+		if (!LAYER_IS_EMPTY(layer))
+			print_group[GetLayerGroupNumberByNumber(i)] = 1;
+	}
+	print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1;
+	print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1;
+	for (i = 0; i < max_copper_layer; i++)
+		if (print_group[GetLayerGroupNumberByNumber(i)])
+			print_layer[i] = 1;
+
+	memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack));
+
+	as_shown = options[HA_as_shown].int_value;
+	if (!options[HA_as_shown].int_value) {
+		conf_force_set_bool(conf_core.editor.thin_draw, 0);
+		conf_force_set_bool(conf_core.editor.thin_draw_poly, 0);
+/*		conf_force_set_bool(conf_core.editor.check_planes, 0);*/
+		conf_force_set_bool(conf_core.editor.show_solder_side, 0);
+		conf_force_set_bool(conf_core.editor.show_mask, 0);
+
+		comp_layer = GetLayerGroupNumberByNumber(component_silk_layer);
+		solder_layer = GetLayerGroupNumberByNumber(solder_silk_layer);
+		qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort);
+
+		if (photo_mode) {
+			int i, n = 0;
+			conf_force_set_bool(conf_core.editor.show_mask, 1);
+			photo_has_inners = 0;
+			if (comp_layer < solder_layer)
+				for (i = comp_layer; i <= solder_layer; i++) {
+					photo_groups[n++] = i;
+					if (i != comp_layer && i != solder_layer && !IsLayerGroupEmpty(i))
+						photo_has_inners = 1;
+				}
+			else
+				for (i = comp_layer; i >= solder_layer; i--) {
+					photo_groups[n++] = i;
+					if (i != comp_layer && i != solder_layer && !IsLayerGroupEmpty(i))
+						photo_has_inners = 1;
+				}
+			if (!photo_has_inners) {
+				photo_groups[1] = photo_groups[n - 1];
+				n = 2;
+			}
+			photo_ngroups = n;
+
+			if (photo_flip) {
+				for (i = 0, n = photo_ngroups - 1; i < n; i++, n--) {
+					int tmp = photo_groups[i];
+					photo_groups[i] = photo_groups[n];
+					photo_groups[n] = tmp;
+				}
+			}
+		}
+	}
+	linewidth = -1;
+	lastbrush = (gdImagePtr) ((void *) -1);
+	lastcap = -1;
+	lastgroup = -1;
+	show_solder_side = conf_core.editor.show_solder_side;
+
+	in_mono = options[HA_mono].int_value;
+
+	if (!photo_mode && conf_core.editor.show_solder_side) {
+		int i, j;
+		for (i = 0, j = max_copper_layer - 1; i < j; i++, j--) {
+			int k = LayerStack[i];
+			LayerStack[i] = LayerStack[j];
+			LayerStack[j] = k;
+		}
+	}
+
+	hid_expose_callback(&png_hid, bounds, 0);
+
+	memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack));
+	conf_update(NULL); /* restore forced sets */
+}
+
+static void blend(color_struct * dest, float a_amount, color_struct * a, color_struct * b)
+{
+	dest->r = a->r * a_amount + b->r * (1 - a_amount);
+	dest->g = a->g * a_amount + b->g * (1 - a_amount);
+	dest->b = a->b * a_amount + b->b * (1 - a_amount);
+}
+
+static void rgb(color_struct * dest, int r, int g, int b)
+{
+	dest->r = r;
+	dest->g = g;
+	dest->b = b;
+}
+
+static int smshadows[3][3] = {
+	{1, 20, 1},
+	{10, 0, -10},
+	{-1, -20, -1},
+};
+
+static int shadows[5][5] = {
+	{1, 1, 1, 1, -1},
+	{1, 1, 1, -1, -1},
+	{1, 1, 0, -1, -1},
+	{1, -1, -1, -1, -1},
+	{-1, -1, -1, -1, -1},
+};
+
+/* black and white are 0 and 1 */
+#define TOP_SHADOW 2
+#define BOTTOM_SHADOW 3
+
+static void ts_bs(gdImagePtr im)
+{
+	int x, y, sx, sy, si;
+	for (x = 0; x < gdImageSX(im); x++)
+		for (y = 0; y < gdImageSY(im); y++) {
+			si = 0;
+			for (sx = -2; sx < 3; sx++)
+				for (sy = -2; sy < 3; sy++)
+					if (!gdImageGetPixel(im, x + sx, y + sy))
+						si += shadows[sx + 2][sy + 2];
+			if (gdImageGetPixel(im, x, y)) {
+				if (si > 1)
+					gdImageSetPixel(im, x, y, TOP_SHADOW);
+				else if (si < -1)
+					gdImageSetPixel(im, x, y, BOTTOM_SHADOW);
+			}
+		}
+}
+
+static void ts_bs_sm(gdImagePtr im)
+{
+	int x, y, sx, sy, si;
+	for (x = 0; x < gdImageSX(im); x++)
+		for (y = 0; y < gdImageSY(im); y++) {
+			si = 0;
+			for (sx = -1; sx < 2; sx++)
+				for (sy = -1; sy < 2; sy++)
+					if (!gdImageGetPixel(im, x + sx, y + sy))
+						si += smshadows[sx + 1][sy + 1];
+			if (gdImageGetPixel(im, x, y)) {
+				if (si > 1)
+					gdImageSetPixel(im, x, y, TOP_SHADOW);
+				else if (si < -1)
+					gdImageSetPixel(im, x, y, BOTTOM_SHADOW);
+			}
+		}
+}
+
+static void png_do_export(HID_Attr_Val * options)
+{
+	int save_ons[MAX_LAYER + 2];
+	int i;
+	BoxType *bbox;
+	int w, h;
+	int xmax, ymax, dpi;
+	const char *fmt;
+	pcb_bool format_error = pcb_false;
+
+	if (color_cache) {
+		free(color_cache);
+		color_cache = NULL;
+	}
+
+	if (brush_cache) {
+		free(brush_cache);
+		brush_cache = NULL;
+	}
+
+	if (!options) {
+		png_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			png_values[i] = png_attribute_list[i].default_val;
+		options = png_values;
+	}
+
+	if (options[HA_photo_mode].int_value || options[HA_ben_mode].int_value) {
+		photo_mode = 1;
+		options[HA_mono].int_value = 1;
+		options[HA_as_shown].int_value = 0;
+		memset(photo_copper, 0, sizeof(photo_copper));
+		photo_silk = photo_mask = photo_drill = 0;
+		photo_outline = 0;
+		if (options[HA_photo_flip_x].int_value || options[HA_ben_flip_x].int_value)
+			photo_flip = PHOTO_FLIP_X;
+		else if (options[HA_photo_flip_y].int_value || options[HA_ben_flip_y].int_value)
+			photo_flip = PHOTO_FLIP_Y;
+		else
+			photo_flip = 0;
+	}
+	else
+		photo_mode = 0;
+
+	filename = options[HA_pngfile].str_value;
+	if (!filename)
+		filename = "pcb-out.png";
+
+	/* figure out width and height of the board */
+	if (options[HA_only_visible].int_value) {
+		bbox = GetDataBoundingBox(PCB->Data);
+		x_shift = bbox->X1;
+		y_shift = bbox->Y1;
+		h = bbox->Y2 - bbox->Y1;
+		w = bbox->X2 - bbox->X1;
+	}
+	else {
+		x_shift = 0;
+		y_shift = 0;
+		h = PCB->MaxHeight;
+		w = PCB->MaxWidth;
+	}
+
+	/*
+	 * figure out the scale factor we need to make the image
+	 * fit in our specified PNG file size
+	 */
+	xmax = ymax = dpi = 0;
+	if (options[HA_dpi].int_value != 0) {
+		dpi = options[HA_dpi].int_value;
+		if (dpi < 0) {
+			fprintf(stderr, "ERROR:  dpi may not be < 0\n");
+			return;
+		}
+	}
+
+	if (options[HA_xmax].int_value > 0) {
+		xmax = options[HA_xmax].int_value;
+		dpi = 0;
+	}
+
+	if (options[HA_ymax].int_value > 0) {
+		ymax = options[HA_ymax].int_value;
+		dpi = 0;
+	}
+
+	if (options[HA_xymax].int_value > 0) {
+		dpi = 0;
+		if (options[HA_xymax].int_value < xmax || xmax == 0)
+			xmax = options[HA_xymax].int_value;
+		if (options[HA_xymax].int_value < ymax || ymax == 0)
+			ymax = options[HA_xymax].int_value;
+	}
+
+	if (xmax < 0 || ymax < 0) {
+		fprintf(stderr, "ERROR:  xmax and ymax may not be < 0\n");
+		return;
+	}
+
+	if (dpi > 0) {
+		/*
+		 * a scale of 1  means 1 pixel is 1 inch
+		 * a scale of 10 means 1 pixel is 10 inches
+		 */
+		scale = PCB_INCH_TO_COORD(1) / dpi;
+		w = w / scale;
+		h = h / scale;
+	}
+	else if (xmax == 0 && ymax == 0) {
+		fprintf(stderr, "ERROR:  You may not set both xmax, ymax," "and xy-max to zero\n");
+		return;
+	}
+	else {
+		if (ymax == 0 || ((xmax > 0)
+											&& ((w / xmax) > (h / ymax)))) {
+			h = (h * xmax) / w;
+			scale = w / xmax;
+			w = xmax;
+		}
+		else {
+			w = (w * ymax) / h;
+			scale = h / ymax;
+			h = ymax;
+		}
+	}
+
+	im = gdImageCreate(w, h);
+	if (im == NULL) {
+		Message(PCB_MSG_DEFAULT, "png_do_export():  gdImageCreate(%d, %d) returned NULL.  Aborting export.\n", w, h);
+		return;
+	}
+
+	master_im = im;
+
+	parse_bloat(options[HA_bloat].str_value);
+
+	/* 
+	 * Allocate white and black -- the first color allocated
+	 * becomes the background color
+	 */
+
+	white = (color_struct *) malloc(sizeof(color_struct));
+	white->r = white->g = white->b = 255;
+	if (options[HA_use_alpha].int_value)
+		white->a = 127;
+	else
+		white->a = 0;
+	white->c = gdImageColorAllocateAlpha(im, white->r, white->g, white->b, white->a);
+	if (white->c == BADC) {
+		Message(PCB_MSG_DEFAULT, "png_do_export():  gdImageColorAllocateAlpha() returned NULL.  Aborting export.\n");
+		return;
+	}
+
+	gdImageFilledRectangle(im, 0, 0, gdImageSX(im), gdImageSY(im), white->c);
+
+	black = (color_struct *) malloc(sizeof(color_struct));
+	black->r = black->g = black->b = black->a = 0;
+	black->c = gdImageColorAllocate(im, black->r, black->g, black->b);
+	if (black->c == BADC) {
+		Message(PCB_MSG_DEFAULT, "png_do_export():  gdImageColorAllocateAlpha() returned NULL.  Aborting export.\n");
+		return;
+	}
+
+	f = fopen(filename, "wb");
+	if (!f) {
+		perror(filename);
+		return;
+	}
+
+	if (!options[HA_as_shown].int_value)
+		hid_save_and_show_layer_ons(save_ons);
+
+	png_hid_export_to_file(f, options);
+
+	if (!options[HA_as_shown].int_value)
+		hid_restore_layer_ons(save_ons);
+
+	if (photo_mode) {
+		int x, y;
+		color_struct white, black, fr4;
+
+		rgb(&white, 255, 255, 255);
+		rgb(&black, 0, 0, 0);
+		rgb(&fr4, 70, 70, 70);
+
+		im = master_im;
+
+		ts_bs(photo_copper[photo_groups[0]]);
+		ts_bs(photo_silk);
+		ts_bs_sm(photo_mask);
+
+		if (photo_outline && have_outline) {
+			int black = gdImageColorResolve(photo_outline, 0x00, 0x00, 0x00);
+
+			/* go all the way around the image, trying to fill the outline */
+			for (x = 0; x < gdImageSX(im); x++) {
+				gdImageFillToBorder(photo_outline, x, 0, black, black);
+				gdImageFillToBorder(photo_outline, x, gdImageSY(im) - 1, black, black);
+			}
+			for (y = 1; y < gdImageSY(im) - 1; y++) {
+				gdImageFillToBorder(photo_outline, 0, y, black, black);
+				gdImageFillToBorder(photo_outline, gdImageSX(im) - 1, y, black, black);
+
+			}
+		}
+
+
+		for (x = 0; x < gdImageSX(im); x++) {
+			for (y = 0; y < gdImageSY(im); y++) {
+				color_struct p, cop;
+				int cc, mask, silk;
+				int transparent;
+
+				if (photo_outline && have_outline) {
+					transparent = gdImageGetPixel(photo_outline, x, y);
+				}
+				else {
+					transparent = 0;
+				}
+
+				mask = photo_mask ? gdImageGetPixel(photo_mask, x, y) : 0;
+				silk = photo_silk ? gdImageGetPixel(photo_silk, x, y) : 0;
+
+				if (photo_copper[photo_groups[1]]
+						&& gdImageGetPixel(photo_copper[photo_groups[1]], x, y))
+					rgb(&cop, 40, 40, 40);
+				else
+					rgb(&cop, 100, 100, 110);
+
+				if (photo_ngroups == 2)
+					blend(&cop, 0.3, &cop, &fr4);
+
+				cc = gdImageGetPixel(photo_copper[photo_groups[0]], x, y);
+				if (cc) {
+					int r;
+
+					if (mask)
+						rgb(&cop, 220, 145, 230);
+					else {
+						rgb(&cop, 140, 150, 160);
+
+						r = (pcb_rand() % 5 - 2) * 2;
+						cop.r += r;
+						cop.g += r;
+						cop.b += r;
+
+					}
+
+					if (cc == TOP_SHADOW) {
+						cop.r = 255 - (255 - cop.r) * 0.7;
+						cop.g = 255 - (255 - cop.g) * 0.7;
+						cop.b = 255 - (255 - cop.b) * 0.7;
+					}
+					if (cc == BOTTOM_SHADOW) {
+						cop.r *= 0.7;
+						cop.g *= 0.7;
+						cop.b *= 0.7;
+					}
+				}
+
+				if (photo_drill && !gdImageGetPixel(photo_drill, x, y)) {
+					rgb(&p, 0, 0, 0);
+					transparent = 1;
+				}
+				else if (silk) {
+					if (silk == TOP_SHADOW)
+						rgb(&p, 255, 255, 255);
+					else if (silk == BOTTOM_SHADOW)
+						rgb(&p, 192, 192, 192);
+					else
+						rgb(&p, 224, 224, 224);
+				}
+				else if (mask) {
+					p = cop;
+					p.r /= 2;
+					p.b /= 2;
+					if (mask == TOP_SHADOW)
+						blend(&p, 0.7, &p, &white);
+					if (mask == BOTTOM_SHADOW)
+						blend(&p, 0.7, &p, &black);
+				}
+				else
+					p = cop;
+
+				if (options[HA_use_alpha].int_value) {
+
+					cc = (transparent) ? gdImageColorResolveAlpha(im, 0, 0, 0, 127) : gdImageColorResolveAlpha(im, p.r, p.g, p.b, 0);
+
+				}
+				else {
+					cc = (transparent) ? gdImageColorResolve(im, 0, 0, 0) : gdImageColorResolve(im, p.r, p.g, p.b);
+				}
+
+				if (photo_flip == PHOTO_FLIP_X)
+					gdImageSetPixel(im, gdImageSX(im) - x - 1, y, cc);
+				else if (photo_flip == PHOTO_FLIP_Y)
+					gdImageSetPixel(im, x, gdImageSY(im) - y - 1, cc);
+				else
+					gdImageSetPixel(im, x, y, cc);
+			}
+		}
+	}
+
+	/* actually write out the image */
+	fmt = filetypes[options[HA_filetype].int_value];
+
+	if (fmt == NULL)
+		format_error = pcb_true;
+	else if (strcmp(fmt, FMT_gif) == 0)
+#ifdef HAVE_GDIMAGEGIF
+		gdImageGif(im, f);
+#else
+		format_error = pcb_true;
+#endif
+	else if (strcmp(fmt, FMT_jpg) == 0)
+#ifdef HAVE_GDIMAGEJPEG
+		gdImageJpeg(im, f, -1);
+#else
+		format_error = pcb_true;
+#endif
+	else if (strcmp(fmt, FMT_png) == 0)
+#ifdef HAVE_GDIMAGEPNG
+		gdImagePng(im, f);
+#else
+		format_error = pcb_true;
+#endif
+	else
+		format_error = pcb_true;
+
+	if (format_error)
+		fprintf(stderr, "Error:  Invalid graphic file format." "  This is a bug.  Please report it.\n");
+
+	fclose(f);
+
+	gdImageDestroy(im);
+}
+
+static void png_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(png_attribute_list, sizeof(png_attribute_list) / sizeof(png_attribute_list[0]), png_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+
+static int is_mask;
+static int is_drill;
+
+static int png_set_layer(const char *name, int group, int empty)
+{
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+	if (name == 0)
+		name = PCB->Data->Layer[idx].Name;
+
+	doing_outline = 0;
+
+	if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
+		return 0;
+	if (SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == SL_FAB)
+		return 0;
+
+	if (strcmp(name, "invisible") == 0)
+		return 0;
+
+	is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	is_mask = (SL_TYPE(idx) == SL_MASK);
+
+	if (SL_TYPE(idx) == SL_PASTE)
+		return 0;
+
+	if (photo_mode) {
+		switch (idx) {
+		case SL(SILK, TOP):
+			if (photo_flip)
+				return 0;
+			photo_im = &photo_silk;
+			break;
+		case SL(SILK, BOTTOM):
+			if (!photo_flip)
+				return 0;
+			photo_im = &photo_silk;
+			break;
+
+		case SL(MASK, TOP):
+			if (photo_flip)
+				return 0;
+			photo_im = &photo_mask;
+			break;
+		case SL(MASK, BOTTOM):
+			if (!photo_flip)
+				return 0;
+			photo_im = &photo_mask;
+			break;
+
+		case SL(PDRILL, 0):
+		case SL(UDRILL, 0):
+			photo_im = &photo_drill;
+			break;
+
+		default:
+			if (idx < 0)
+				return 0;
+
+			if (strcmp(name, "outline") == 0) {
+				doing_outline = 1;
+				have_outline = 0;
+				photo_im = &photo_outline;
+			}
+			else
+				photo_im = photo_copper + group;
+
+			break;
+		}
+
+		if (!*photo_im) {
+			static color_struct *black = NULL, *white = NULL;
+			*photo_im = gdImageCreate(gdImageSX(im), gdImageSY(im));
+			if (photo_im == NULL) {
+				Message(PCB_MSG_DEFAULT, "png_set_layer():  gdImageCreate(%d, %d) returned NULL.  Aborting export.\n", gdImageSX(im), gdImageSY(im));
+				return 0;
+			}
+
+
+			white = (color_struct *) malloc(sizeof(color_struct));
+			white->r = white->g = white->b = 255;
+			white->a = 0;
+			white->c = gdImageColorAllocate(*photo_im, white->r, white->g, white->b);
+			if (white->c == BADC) {
+				Message(PCB_MSG_DEFAULT, "png_set_layer():  gdImageColorAllocate() returned NULL.  Aborting export.\n");
+				return 0;
+			}
+
+			black = (color_struct *) malloc(sizeof(color_struct));
+			black->r = black->g = black->b = black->a = 0;
+			black->c = gdImageColorAllocate(*photo_im, black->r, black->g, black->b);
+			if (black->c == BADC) {
+				Message(PCB_MSG_DEFAULT, "png_set_layer(): gdImageColorAllocate() returned NULL.  Aborting export.\n");
+				return 0;
+			}
+
+			if (idx == SL(PDRILL, 0)
+					|| idx == SL(UDRILL, 0))
+				gdImageFilledRectangle(*photo_im, 0, 0, gdImageSX(im), gdImageSY(im), black->c);
+		}
+		im = *photo_im;
+		return 1;
+	}
+
+	if (as_shown) {
+		switch (idx) {
+		case SL(SILK, TOP):
+		case SL(SILK, BOTTOM):
+			if (SL_MYSIDE(idx))
+				return PCB->ElementOn;
+			return 0;
+
+		case SL(MASK, TOP):
+		case SL(MASK, BOTTOM):
+			return conf_core.editor.show_mask && SL_MYSIDE(idx);
+		}
+	}
+	else {
+		if (is_mask)
+			return 0;
+
+		switch (idx) {
+		case SL(SILK, TOP):
+			return 1;
+		case SL(SILK, BOTTOM):
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+static hidGC png_make_gc(void)
+{
+	hidGC rv = (hidGC) malloc(sizeof(hid_gc_struct));
+	rv->me_pointer = &png_hid;
+	rv->cap = Trace_Cap;
+	rv->width = 1;
+	rv->color = (color_struct *) malloc(sizeof(color_struct));
+	rv->color->r = rv->color->g = rv->color->b = rv->color->a = 0;
+	rv->color->c = 0;
+	rv->is_erase = 0;
+	return rv;
+}
+
+static void png_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void png_use_mask(int use_it)
+{
+	if (photo_mode)
+		return;
+
+	if (use_it == HID_MASK_CLEAR) {
+		return;
+	}
+	if (use_it) {
+		if (mask_im == NULL) {
+			mask_im = gdImageCreate(gdImageSX(im), gdImageSY(im));
+			if (!mask_im) {
+				Message(PCB_MSG_DEFAULT, "png_use_mask():  gdImageCreate(%d, %d) returned NULL.  Corrupt export!\n", gdImageSY(im), gdImageSY(im));
+				return;
+			}
+			gdImagePaletteCopy(mask_im, im);
+		}
+		im = mask_im;
+		gdImageFilledRectangle(mask_im, 0, 0, gdImageSX(mask_im), gdImageSY(mask_im), white->c);
+	}
+	else {
+		int x, y, c;
+
+		im = master_im;
+
+		for (x = 0; x < gdImageSX(im); x++)
+			for (y = 0; y < gdImageSY(im); y++) {
+				c = gdImageGetPixel(mask_im, x, y);
+				if (c)
+					gdImageSetPixel(im, x, y, c);
+			}
+	}
+}
+
+static void png_set_color(hidGC gc, const char *name)
+{
+	hidval cval;
+
+	if (im == NULL)
+		return;
+
+	if (name == NULL)
+		name = "#ff0000";
+
+	if (strcmp(name, "erase") == 0 || strcmp(name, "drill") == 0) {
+		gc->color = white;
+		gc->is_erase = 1;
+		return;
+	}
+	gc->is_erase = 0;
+
+	if (in_mono || (strcmp(name, "#000000") == 0)) {
+		gc->color = black;
+		return;
+	}
+
+	if (hid_cache_color(0, name, &cval, &color_cache)) {
+		gc->color = (color_struct *) cval.ptr;
+	}
+	else if (name[0] == '#') {
+		gc->color = (color_struct *) malloc(sizeof(color_struct));
+		sscanf(name + 1, "%2x%2x%2x", &(gc->color->r), &(gc->color->g), &(gc->color->b));
+		gc->color->c = gdImageColorAllocate(master_im, gc->color->r, gc->color->g, gc->color->b);
+		if (gc->color->c == BADC) {
+			Message(PCB_MSG_DEFAULT, "png_set_color():  gdImageColorAllocate() returned NULL.  Aborting export.\n");
+			return;
+		}
+		cval.ptr = gc->color;
+		hid_cache_color(1, name, &cval, &color_cache);
+	}
+	else {
+		printf("WE SHOULD NOT BE HERE!!!\n");
+		gc->color = black;
+	}
+
+}
+
+static void png_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void png_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void png_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+static void use_gc(hidGC gc)
+{
+	int need_brush = 0;
+
+	if (gc->me_pointer != &png_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to png HID\n");
+		abort();
+	}
+
+	if (linewidth != gc->width) {
+		/* Make sure the scaling doesn't erase lines completely */
+		if (SCALE(gc->width) == 0 && gc->width > 0)
+			gdImageSetThickness(im, 1);
+		else
+			gdImageSetThickness(im, SCALE(gc->width + 2 * bloat));
+		linewidth = gc->width;
+		need_brush = 1;
+	}
+
+	if (lastbrush != gc->brush || need_brush) {
+		hidval bval;
+		char name[256];
+		char type;
+		int r;
+
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			type = 'C';
+			break;
+		default:
+		case Square_Cap:
+			type = 'S';
+			break;
+		}
+		if (gc->width)
+			r = SCALE(gc->width + 2 * bloat);
+		else
+			r = 1;
+
+		/* do not allow a brush size that is zero width.  In this case limit to a single pixel. */
+		if (r == 0) {
+			r = 1;
+		}
+
+		sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r);
+
+		if (hid_cache_color(0, name, &bval, &brush_cache)) {
+			gc->brush = (gdImagePtr) bval.ptr;
+		}
+		else {
+			int bg, fg;
+			gc->brush = gdImageCreate(r, r);
+			if (gc->brush == NULL) {
+				Message(PCB_MSG_DEFAULT, "use_gc():  gdImageCreate(%d, %d) returned NULL.  Aborting export.\n", r, r);
+				return;
+			}
+
+			bg = gdImageColorAllocate(gc->brush, 255, 255, 255);
+			if (bg == BADC) {
+				Message(PCB_MSG_DEFAULT, "use_gc():  gdImageColorAllocate() returned NULL.  Aborting export.\n");
+				return;
+			}
+			fg = gdImageColorAllocateAlpha(gc->brush, gc->color->r, gc->color->g, gc->color->b, 0);
+			if (fg == BADC) {
+				Message(PCB_MSG_DEFAULT, "use_gc():  gdImageColorAllocate() returned NULL.  Aborting export.\n");
+				return;
+			}
+			gdImageColorTransparent(gc->brush, bg);
+
+			/*
+			 * if we shrunk to a radius/box width of zero, then just use
+			 * a single pixel to draw with.
+			 */
+			if (r <= 1)
+				gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg);
+			else {
+				if (type == 'C') {
+					gdImageFilledEllipse(gc->brush, r / 2, r / 2, r, r, fg);
+					/* Make sure the ellipse is the right exact size.  */
+					gdImageSetPixel(gc->brush, 0, r / 2, fg);
+					gdImageSetPixel(gc->brush, r - 1, r / 2, fg);
+					gdImageSetPixel(gc->brush, r / 2, 0, fg);
+					gdImageSetPixel(gc->brush, r / 2, r - 1, fg);
+				}
+				else
+					gdImageFilledRectangle(gc->brush, 0, 0, r - 1, r - 1, fg);
+			}
+			bval.ptr = gc->brush;
+			hid_cache_color(1, name, &bval, &brush_cache);
+		}
+
+		gdImageSetBrush(im, gc->brush);
+		lastbrush = gc->brush;
+
+	}
+}
+
+static void png_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	gdImageRectangle(im, SCALE_X(x1), SCALE_Y(y1), SCALE_X(x2), SCALE_Y(y2), gc->color->c);
+}
+
+static void png_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	gdImageSetThickness(im, 0);
+	linewidth = 0;
+
+	if (x1 > x2) {
+		Coord t = x1;
+		x2 = x2;
+		x2 = t;
+	}
+	if (y1 > y2) {
+		Coord t = y1;
+		y2 = y2;
+		y2 = t;
+	}
+	y1 -= bloat;
+	y2 += bloat;
+	SWAP_IF_SOLDER(y1, y2);
+
+	gdImageFilledRectangle(im, SCALE_X(x1 - bloat), SCALE_Y(y1), SCALE_X(x2 + bloat) - 1, SCALE_Y(y2) - 1, gc->color->c);
+	have_outline |= doing_outline;
+}
+
+static void png_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	if (x1 == x2 && y1 == y2) {
+		Coord w = gc->width / 2;
+		if (gc->cap != Square_Cap)
+			png_fill_circle(gc, x1, y1, w);
+		else
+			png_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
+		return;
+	}
+	use_gc(gc);
+
+	if (NOT_EDGE(x1, y1) || NOT_EDGE(x2, y2))
+		have_outline |= doing_outline;
+	if (doing_outline) {
+		/* Special case - lines drawn along the bottom or right edges
+		   are brought in by a pixel to make sure we have contiguous
+		   outlines.  */
+		if (x1 == PCB->MaxWidth && x2 == PCB->MaxWidth) {
+			x1 -= scale / 2;
+			x2 -= scale / 2;
+		}
+		if (y1 == PCB->MaxHeight && y2 == PCB->MaxHeight) {
+			y1 -= scale / 2;
+			y2 -= scale / 2;
+		}
+	}
+
+	gdImageSetThickness(im, 0);
+	linewidth = 0;
+	if (gc->cap != Square_Cap || x1 == x2 || y1 == y2) {
+		gdImageLine(im, SCALE_X(x1), SCALE_Y(y1), SCALE_X(x2), SCALE_Y(y2), gdBrushed);
+	}
+	else {
+		/*
+		 * if we are drawing a line with a square end cap and it is
+		 * not purely horizontal or vertical, then we need to draw
+		 * it as a filled polygon.
+		 */
+		int fg = gdImageColorResolve(im, gc->color->r, gc->color->g,
+																 gc->color->b), w = gc->width, dx = x2 - x1, dy = y2 - y1, dwx, dwy;
+		gdPoint p[4];
+		double l = sqrt(dx * dx + dy * dy) * 2;
+
+		w += 2 * bloat;
+		dwx = -w / l * dy;
+		dwy = w / l * dx;
+		p[0].x = SCALE_X(x1 + dwx - dwy);
+		p[0].y = SCALE_Y(y1 + dwy + dwx);
+		p[1].x = SCALE_X(x1 - dwx - dwy);
+		p[1].y = SCALE_Y(y1 - dwy + dwx);
+		p[2].x = SCALE_X(x2 - dwx + dwy);
+		p[2].y = SCALE_Y(y2 - dwy - dwx);
+		p[3].x = SCALE_X(x2 + dwx + dwy);
+		p[3].y = SCALE_Y(y2 + dwy - dwx);
+		gdImageFilledPolygon(im, p, 4, fg);
+	}
+}
+
+static void png_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	Angle sa, ea;
+
+	/*
+	 * zero angle arcs need special handling as gd will output either
+	 * nothing at all or a full circle when passed delta angle of 0 or 360.
+	 */
+	if (delta_angle == 0) {
+		Coord x = (width * cos(start_angle * M_PI / 180));
+		Coord y = (width * sin(start_angle * M_PI / 180));
+		x = cx - x;
+		y = cy + y;
+		png_fill_circle(gc, x, y, gc->width / 2);
+		return;
+	}
+
+	/* 
+	 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
+	 * in pcb, 0 degrees is to the left and +90 degrees is down
+	 */
+	start_angle = 180 - start_angle;
+	delta_angle = -delta_angle;
+	if (show_solder_side) {
+		start_angle = -start_angle;
+		delta_angle = -delta_angle;
+	}
+	if (delta_angle > 0) {
+		sa = start_angle;
+		ea = start_angle + delta_angle;
+	}
+	else {
+		sa = start_angle + delta_angle;
+		ea = start_angle;
+	}
+
+	/* 
+	 * make sure we start between 0 and 360 otherwise gd does
+	 * strange things
+	 */
+	sa = NormalizeAngle(sa);
+	ea = NormalizeAngle(ea);
+
+	have_outline |= doing_outline;
+
+#if 0
+	printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea);
+	printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
+				 (void *)im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c);
+#endif
+	use_gc(gc);
+	gdImageSetThickness(im, 0);
+	linewidth = 0;
+	gdImageArc(im, SCALE_X(cx), SCALE_Y(cy), SCALE(2 * width), SCALE(2 * height), sa, ea, gdBrushed);
+}
+
+static void png_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	Coord my_bloat;
+
+	use_gc(gc);
+
+	if (gc->is_erase)
+		my_bloat = -2 * bloat;
+	else
+		my_bloat = 2 * bloat;
+
+
+	have_outline |= doing_outline;
+
+	gdImageSetThickness(im, 0);
+	linewidth = 0;
+	gdImageFilledEllipse(im, SCALE_X(cx), SCALE_Y(cy), SCALE(2 * radius + my_bloat), SCALE(2 * radius + my_bloat), gc->color->c);
+
+}
+
+static void png_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	gdPoint *points;
+
+	points = (gdPoint *) malloc(n_coords * sizeof(gdPoint));
+	if (points == NULL) {
+		fprintf(stderr, "ERROR:  png_fill_polygon():  malloc failed\n");
+		exit(1);
+	}
+
+	use_gc(gc);
+	for (i = 0; i < n_coords; i++) {
+		if (NOT_EDGE(x[i], y[i]))
+			have_outline |= doing_outline;
+		points[i].x = SCALE_X(x[i]);
+		points[i].y = SCALE_Y(y[i]);
+	}
+	gdImageSetThickness(im, 0);
+	linewidth = 0;
+	gdImageFilledPolygon(im, points, n_coords, gc->color->c);
+	free(points);
+}
+
+static void png_calibrate(double xval, double yval)
+{
+	CRASH("png_calibrate");
+}
+
+static void png_set_crosshair(int x, int y, int a)
+{
+}
+
+static int png_usage(const char *topic)
+{
+	fprintf(stderr, "\npng exporter command line arguments:\n\n");
+	hid_usage(png_attribute_list, sizeof(png_attribute_list) / sizeof(png_attribute_list[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x png foo.pcb [png options]\n\n");
+	return 0;
+}
+
+#include "dolists.h"
+
+pcb_uninit_t hid_export_png_init()
+{
+	memset(&png_hid, 0, sizeof(HID));
+
+	common_nogui_init(&png_hid);
+	common_draw_helpers_init(&png_hid);
+
+	png_hid.struct_size = sizeof(HID);
+	png_hid.name = "png";
+	png_hid.description = "GIF/JPEG/PNG export";
+	png_hid.exporter = 1;
+	png_hid.poly_before = 1;
+
+	png_hid.get_export_options = png_get_export_options;
+	png_hid.do_export = png_do_export;
+	png_hid.parse_arguments = png_parse_arguments;
+	png_hid.set_layer = png_set_layer;
+	png_hid.make_gc = png_make_gc;
+	png_hid.destroy_gc = png_destroy_gc;
+	png_hid.use_mask = png_use_mask;
+	png_hid.set_color = png_set_color;
+	png_hid.set_line_cap = png_set_line_cap;
+	png_hid.set_line_width = png_set_line_width;
+	png_hid.set_draw_xor = png_set_draw_xor;
+	png_hid.draw_line = png_draw_line;
+	png_hid.draw_arc = png_draw_arc;
+	png_hid.draw_rect = png_draw_rect;
+	png_hid.fill_circle = png_fill_circle;
+	png_hid.fill_polygon = png_fill_polygon;
+	png_hid.fill_rect = png_fill_rect;
+	png_hid.calibrate = png_calibrate;
+	png_hid.set_crosshair = png_set_crosshair;
+
+	png_hid.usage = png_usage;
+
+#ifdef HAVE_SOME_FORMAT
+	hid_register_hid(&png_hid);
+
+#endif
+	return NULL;
+}
diff --git a/src_plugins/export_png/png.h b/src_plugins/export_png/png.h
new file mode 100644
index 0000000..4192fd1
--- /dev/null
+++ b/src_plugins/export_png/png.h
@@ -0,0 +1,2 @@
+extern const char *png_cookie;
+extern void png_hid_export_to_file(FILE *, HID_Attr_Val *);
diff --git a/src_plugins/export_ps/Makefile b/src_plugins/export_ps/Makefile
new file mode 100644
index 0000000..c1dd95c
--- /dev/null
+++ b/src_plugins/export_ps/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_ps
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_ps/Plug.tmpasm b/src_plugins/export_ps/Plug.tmpasm
new file mode 100644
index 0000000..0f5959f
--- /dev/null
+++ b/src_plugins/export_ps/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_ps}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_ps/ps.o  $(PLUGDIR)/export_ps/eps.o  @]
+
+switch /local/pcb/export_ps/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_ps/README b/src_plugins/export_ps/README
new file mode 100644
index 0000000..4c90c38
--- /dev/null
+++ b/src_plugins/export_ps/README
@@ -0,0 +1,5 @@
+Export postscript or embedded postscript.
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_ps/eps.c b/src_plugins/export_ps/eps.c
new file mode 100644
index 0000000..917ceeb
--- /dev/null
+++ b/src_plugins/export_ps/eps.c
@@ -0,0 +1,638 @@
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "global.h"
+#include "data.h"
+#include "layer.h"
+#include "misc.h"
+#include "pcb-printf.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "ps.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_flags.h"
+#include "hid_color.h"
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented EPS function %s.\n", func); abort()
+
+/*----------------------------------------------------------------------------*/
+/* Function prototypes                                                        */
+/*----------------------------------------------------------------------------*/
+static HID_Attribute *eps_get_export_options(int *n);
+static void eps_do_export(HID_Attr_Val * options);
+static void eps_parse_arguments(int *argc, char ***argv);
+static int eps_set_layer(const char *name, int group, int empty);
+static hidGC eps_make_gc(void);
+static void eps_destroy_gc(hidGC gc);
+static void eps_use_mask(int use_it);
+static void eps_set_color(hidGC gc, const char *name);
+static void eps_set_line_cap(hidGC gc, EndCapStyle style);
+static void eps_set_line_width(hidGC gc, Coord width);
+static void eps_set_draw_xor(hidGC gc, int _xor);
+static void eps_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void eps_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void eps_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle);
+static void eps_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void eps_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+static void eps_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y);
+static void eps_calibrate(double xval, double yval);
+static void eps_set_crosshair(int x, int y, int action);
+/*----------------------------------------------------------------------------*/
+
+typedef struct hid_gc_struct {
+	EndCapStyle cap;
+	Coord width;
+	int color;
+	int erase;
+} hid_gc_struct;
+
+static HID eps_hid;
+
+static FILE *f = 0;
+static Coord linewidth = -1;
+static int lastcap = -1;
+static int lastcolor = -1;
+static int print_group[MAX_LAYER];
+static int print_layer[MAX_LAYER];
+static int fast_erase = -1;
+
+static HID_Attribute eps_attribute_list[] = {
+	/* other HIDs expect this to be first.  */
+
+/* %start-doc options "92 Encapsulated Postscript Export"
+ at ftable @code
+ at item --eps-file <string>
+Name of the encapsulated postscript output file. Can contain a path.
+ at end ftable
+%end-doc
+*/
+	{"eps-file", "Encapsulated Postscript output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_psfile 0
+
+/* %start-doc options "92 Encapsulated Postscript Export"
+ at ftable @code
+ at item --eps-scale <num>
+Scale EPS output by the parameter @samp{num}.
+ at end ftable
+%end-doc
+*/
+	{"eps-scale", "EPS scale",
+	 HID_Real, 0, 100, {0, 0, 1.0}, 0, 0},
+#define HA_scale 1
+
+/* %start-doc options "92 Encapsulated Postscript Export"
+ at ftable @code
+ at cindex as-shown (EPS)
+ at item --as-shown
+Export layers as shown on screen.
+ at end ftable
+%end-doc
+*/
+	{"as-shown", "Export layers as shown on screen",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_as_shown 2
+
+/* %start-doc options "92 Encapsulated Postscript Export"
+ at ftable @code
+ at item --monochrome
+Convert output to monochrome.
+ at end ftable
+%end-doc
+*/
+	{"monochrome", "Convert to monochrome",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_mono 3
+
+/* %start-doc options "92 Encapsulated Postscript Export"
+ at ftable @code
+ at cindex only-visible
+ at item --only-visible
+Limit the bounds of the EPS file to the visible items.
+ at end ftable
+%end-doc
+*/
+	{"only-visible", "Limit the bounds of the EPS file to the visible items",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_only_visible 4
+};
+
+#define NUM_OPTIONS (sizeof(eps_attribute_list)/sizeof(eps_attribute_list[0]))
+
+REGISTER_ATTRIBUTES(eps_attribute_list, ps_cookie)
+
+		 static HID_Attr_Val eps_values[NUM_OPTIONS];
+
+		 static HID_Attribute *eps_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+
+	if (PCB)
+		derive_default_filename(PCB->Filename, &eps_attribute_list[HA_psfile], ".eps", &last_made_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return eps_attribute_list;
+}
+
+static int comp_layer, solder_layer;
+
+static int group_for_layer(int l)
+{
+	if (l < max_copper_layer + 2 && l >= 0)
+		return GetLayerGroupNumberByNumber(l);
+	/* else something unique */
+	return max_group + 3 + l;
+}
+
+static int layer_sort(const void *va, const void *vb)
+{
+	int a = *(int *) va;
+	int b = *(int *) vb;
+	int al = group_for_layer(a);
+	int bl = group_for_layer(b);
+	int d = bl - al;
+
+	if (a >= 0 && a <= max_copper_layer + 1) {
+		int aside = (al == solder_layer ? 0 : al == comp_layer ? 2 : 1);
+		int bside = (bl == solder_layer ? 0 : bl == comp_layer ? 2 : 1);
+		if (bside != aside)
+			return bside - aside;
+	}
+	if (d)
+		return d;
+	return b - a;
+}
+
+static const char *filename;
+static BoxType *bounds;
+static int in_mono, as_shown;
+
+void eps_hid_export_to_file(FILE * the_file, HID_Attr_Val * options)
+{
+	int i;
+	static int saved_layer_stack[MAX_LAYER];
+	BoxType region;
+
+	conf_force_set_bool(conf_core.editor.thin_draw, 0);
+	conf_force_set_bool(conf_core.editor.thin_draw_poly, 0);
+	conf_force_set_bool(conf_core.editor.check_planes, 0);
+
+	f = the_file;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	if (options[HA_only_visible].int_value)
+		bounds = GetDataBoundingBox(PCB->Data);
+	else
+		bounds = ®ion;
+
+	memset(print_group, 0, sizeof(print_group));
+	memset(print_layer, 0, sizeof(print_layer));
+
+	/* Figure out which layers actually have stuff on them.  */
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *layer = PCB->Data->Layer + i;
+		if (layer->On)
+			if (!LAYER_IS_EMPTY(layer))
+				print_group[GetLayerGroupNumberByNumber(i)] = 1;
+	}
+
+	/* Now, if only one layer has real stuff on it, we can use the fast
+	   erase logic.  Otherwise, we have to use the expensive multi-mask
+	   erase.  */
+	fast_erase = 0;
+	for (i = 0; i < max_group; i++)
+		if (print_group[i])
+			fast_erase++;
+
+	/* If NO layers had anything on them, at least print the component
+	   layer to get the pins.  */
+	if (fast_erase == 0) {
+		print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1;
+		fast_erase = 1;
+	}
+
+	/* "fast_erase" is 1 if we can just paint white to erase.  */
+	fast_erase = fast_erase == 1 ? 1 : 0;
+
+	/* Now, for each group we're printing, mark its layers for
+	   printing.  */
+	for (i = 0; i < max_copper_layer; i++)
+		if (print_group[GetLayerGroupNumberByNumber(i)])
+			print_layer[i] = 1;
+
+	if (fast_erase) {
+		eps_hid.poly_before = 1;
+		eps_hid.poly_after = 0;
+	}
+	else {
+		eps_hid.poly_before = 0;
+		eps_hid.poly_after = 1;
+	}
+
+	memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack));
+	as_shown = options[HA_as_shown].int_value;
+	if (!options[HA_as_shown].int_value) {
+		comp_layer = GetLayerGroupNumberByNumber(component_silk_layer);
+		solder_layer = GetLayerGroupNumberByNumber(solder_silk_layer);
+		qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort);
+	}
+	fprintf(f, "%%!PS-Adobe-3.0 EPSF-3.0\n");
+	linewidth = -1;
+	lastcap = -1;
+	lastcolor = -1;
+
+	in_mono = options[HA_mono].int_value;
+
+#define pcb2em(x) 1 + PCB_COORD_TO_INCH (x) * 72.0 * options[HA_scale].real_value
+	fprintf(f, "%%%%BoundingBox: 0 0 %f %f\n", pcb2em(bounds->X2 - bounds->X1), pcb2em(bounds->Y2 - bounds->Y1));
+#undef pcb2em
+	fprintf(f, "%%%%Pages: 1\n");
+	fprintf(f, "save countdictstack mark newpath /showpage {} def /setpagedevice {pop} def\n");
+	fprintf(f, "%%%%EndProlog\n");
+	fprintf(f, "%%%%Page: 1 1\n");
+	fprintf(f, "%%%%BeginDocument: %s\n\n", filename);
+
+	fprintf(f, "72 72 scale\n");
+	fprintf(f, "1 dup neg scale\n");
+	fprintf(f, "%g dup scale\n", options[HA_scale].real_value);
+	pcb_fprintf(f, "%mi %mi translate\n", -bounds->X1, -bounds->Y2);
+	if (options[HA_as_shown].int_value && conf_core.editor.show_solder_side)
+		pcb_fprintf(f, "-1 1 scale %mi 0 translate\n", bounds->X1 - bounds->X2);
+	linewidth = -1;
+	lastcap = -1;
+	lastcolor = -1;
+#define Q (Coord) PCB_MIL_TO_COORD(10)
+	pcb_fprintf(f,
+							"/nclip { %mi %mi moveto %mi %mi lineto %mi %mi lineto %mi %mi lineto %mi %mi lineto eoclip newpath } def\n",
+							bounds->X1 - Q, bounds->Y1 - Q, bounds->X1 - Q, bounds->Y2 + Q,
+							bounds->X2 + Q, bounds->Y2 + Q, bounds->X2 + Q, bounds->Y1 - Q, bounds->X1 - Q, bounds->Y1 - Q);
+#undef Q
+	fprintf(f, "/t { moveto lineto stroke } bind def\n");
+	fprintf(f, "/tc { moveto lineto strokepath nclip } bind def\n");
+	fprintf(f, "/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n");
+	fprintf(f, "     x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n");
+	fprintf(f, "/c { 0 360 arc fill } bind def\n");
+	fprintf(f, "/cc { 0 360 arc nclip } bind def\n");
+	fprintf(f, "/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
+
+	hid_expose_callback(&eps_hid, bounds, 0);
+
+	fprintf(f, "showpage\n");
+
+	fprintf(f, "%%%%EndDocument\n");
+	fprintf(f, "%%%%Trailer\n");
+	fprintf(f, "cleartomark countdictstack exch sub { end } repeat restore\n");
+	fprintf(f, "%%%%EOF\n");
+
+	memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack));
+	conf_update(NULL); /* restore forced sets */
+}
+
+static void eps_do_export(HID_Attr_Val * options)
+{
+	int i;
+	int save_ons[MAX_LAYER + 2];
+
+	if (!options) {
+		eps_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			eps_values[i] = eps_attribute_list[i].default_val;
+		options = eps_values;
+	}
+
+	filename = options[HA_psfile].str_value;
+	if (!filename)
+		filename = "pcb-out.eps";
+
+	f = fopen(filename, "w");
+	if (!f) {
+		perror(filename);
+		return;
+	}
+
+	if (!options[HA_as_shown].int_value)
+		hid_save_and_show_layer_ons(save_ons);
+	eps_hid_export_to_file(f, options);
+	if (!options[HA_as_shown].int_value)
+		hid_restore_layer_ons(save_ons);
+
+	fclose(f);
+}
+
+static void eps_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(eps_attribute_list, sizeof(eps_attribute_list) / sizeof(eps_attribute_list[0]), ps_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static int is_mask;
+static int is_paste;
+static int is_drill;
+
+static int eps_set_layer(const char *name, int group, int empty)
+{
+	int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group;
+	if (name == 0)
+		name = PCB->Data->Layer[idx].Name;
+
+	if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
+		return 0;
+	if (SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == SL_FAB)
+		return 0;
+
+	if (strcmp(name, "invisible") == 0)
+		return 0;
+
+	is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	is_mask = (SL_TYPE(idx) == SL_MASK);
+	is_paste = (SL_TYPE(idx) == SL_PASTE);
+
+	if (is_mask || is_paste)
+		return 0;
+#if 0
+	printf("Layer %s group %d drill %d mask %d\n", name, group, is_drill, is_mask);
+#endif
+	fprintf(f, "%% Layer %s group %d drill %d mask %d\n", name, group, is_drill, is_mask);
+
+	if (as_shown) {
+		switch (idx) {
+		case SL(SILK, TOP):
+		case SL(SILK, BOTTOM):
+			if (SL_MYSIDE(idx))
+				return PCB->ElementOn;
+			else
+				return 0;
+		}
+	}
+	else {
+		switch (idx) {
+		case SL(SILK, TOP):
+			return 1;
+		case SL(SILK, BOTTOM):
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static hidGC eps_make_gc(void)
+{
+	hidGC rv = (hidGC) malloc(sizeof(hid_gc_struct));
+	rv->cap = Trace_Cap;
+	rv->width = 0;
+	rv->color = 0;
+	return rv;
+}
+
+static void eps_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void eps_use_mask(int use_it)
+{
+	static int mask_pending = 0;
+	switch (use_it) {
+	case HID_MASK_CLEAR:
+		if (!mask_pending) {
+			mask_pending = 1;
+			fprintf(f, "gsave\n");
+		}
+		break;
+	case HID_MASK_AFTER:
+		break;
+	case HID_MASK_OFF:
+		if (mask_pending) {
+			mask_pending = 0;
+			fprintf(f, "grestore\n");
+			lastcolor = -1;
+		}
+		break;
+	}
+}
+
+static void eps_set_color(hidGC gc, const char *name)
+{
+	static void *cache = 0;
+	hidval cval;
+
+	if (strcmp(name, "erase") == 0) {
+		gc->color = 0xffffff;
+		gc->erase = fast_erase ? 0 : 1;
+		return;
+	}
+	if (strcmp(name, "drill") == 0) {
+		gc->color = 0xffffff;
+		gc->erase = 0;
+		return;
+	}
+	gc->erase = 0;
+	if (hid_cache_color(0, name, &cval, &cache)) {
+		gc->color = cval.lval;
+	}
+	else if (in_mono) {
+		gc->color = 0;
+	}
+	else if (name[0] == '#') {
+		unsigned int r, g, b;
+		sscanf(name + 1, "%2x%2x%2x", &r, &g, &b);
+		gc->color = (r << 16) + (g << 8) + b;
+	}
+	else
+		gc->color = 0;
+}
+
+static void eps_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void eps_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void eps_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+static void use_gc(hidGC gc)
+{
+	if (linewidth != gc->width) {
+		pcb_fprintf(f, "%mi setlinewidth\n", gc->width);
+		linewidth = gc->width;
+	}
+	if (lastcap != gc->cap) {
+		int c;
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			c = 1;
+			break;
+		default:
+		case Square_Cap:
+			c = 2;
+			break;
+		}
+		fprintf(f, "%d setlinecap\n", c);
+		lastcap = gc->cap;
+	}
+	if (lastcolor != gc->color) {
+		int c = gc->color;
+#define CV(x,b) (((x>>b)&0xff)/255.0)
+		fprintf(f, "%g %g %g setrgbcolor\n", CV(c, 16), CV(c, 8), CV(c, 0));
+		lastcolor = gc->color;
+	}
+}
+
+static void eps_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void eps_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+
+static void eps_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	pcb_fprintf(f, "%mi %mi %mi %mi r\n", x1, y1, x2, y2);
+}
+
+static void eps_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	Coord w = gc->width / 2;
+	if (x1 == x2 && y1 == y2) {
+		if (gc->cap == Square_Cap)
+			eps_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
+		else
+			eps_fill_circle(gc, x1, y1, w);
+		return;
+	}
+	use_gc(gc);
+	if (gc->erase && gc->cap != Square_Cap) {
+		double ang = atan2(y2 - y1, x2 - x1);
+		double dx = w * sin(ang);
+		double dy = -w * cos(ang);
+		double deg = ang * 180.0 / M_PI;
+		Coord vx1 = x1 + dx;
+		Coord vy1 = y1 + dy;
+
+		pcb_fprintf(f, "%mi %mi moveto ", vx1, vy1);
+		pcb_fprintf(f, "%mi %mi %mi %g %g arc\n", x2, y2, w, deg - 90, deg + 90);
+		pcb_fprintf(f, "%mi %mi %mi %g %g arc\n", x1, y1, w, deg + 90, deg + 270);
+		fprintf(f, "nclip\n");
+
+		return;
+	}
+	pcb_fprintf(f, "%mi %mi %mi %mi %s\n", x1, y1, x2, y2, gc->erase ? "tc" : "t");
+}
+
+static void eps_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	Angle sa, ea;
+	if (delta_angle > 0) {
+		sa = start_angle;
+		ea = start_angle + delta_angle;
+	}
+	else {
+		sa = start_angle + delta_angle;
+		ea = start_angle;
+	}
+#if 0
+	printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea);
+#endif
+	use_gc(gc);
+	pcb_fprintf(f, "%ma %ma %mi %mi %mi %mi %g a\n", sa, ea, -width, height, cx, cy, (double) linewidth / width);
+}
+
+static void eps_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	use_gc(gc);
+	pcb_fprintf(f, "%mi %mi %mi %s\n", cx, cy, radius, gc->erase ? "cc" : "c");
+}
+
+static void eps_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	const char *op = "moveto";
+	use_gc(gc);
+	for (i = 0; i < n_coords; i++) {
+		pcb_fprintf(f, "%mi %mi %s\n", x[i], y[i], op);
+		op = "lineto";
+	}
+	fprintf(f, "fill\n");
+}
+
+static void eps_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	pcb_fprintf(f, "%mi %mi %mi %mi r\n", x1, y1, x2, y2);
+}
+
+static void eps_calibrate(double xval, double yval)
+{
+	CRASH("eps_calibrate");
+}
+
+static void eps_set_crosshair(int x, int y, int action)
+{
+}
+
+static int eps_usage(const char *topic)
+{
+	fprintf(stderr, "\neps exporter command line arguments:\n\n");
+	hid_usage(eps_attribute_list, sizeof(eps_attribute_list) / sizeof(eps_attribute_list[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x eps foo.pcb [eps options]\n\n");
+	return 0;
+}
+
+void hid_eps_init()
+{
+	memset(&eps_hid, 0, sizeof(HID));
+
+	common_nogui_init(&eps_hid);
+	common_draw_helpers_init(&eps_hid);
+
+	eps_hid.struct_size = sizeof(HID);
+	eps_hid.name = "eps";
+	eps_hid.description = "Encapsulated Postscript";
+	eps_hid.exporter = 1;
+	eps_hid.poly_after = 1;
+
+	eps_hid.get_export_options = eps_get_export_options;
+	eps_hid.do_export = eps_do_export;
+	eps_hid.parse_arguments = eps_parse_arguments;
+	eps_hid.set_layer = eps_set_layer;
+	eps_hid.make_gc = eps_make_gc;
+	eps_hid.destroy_gc = eps_destroy_gc;
+	eps_hid.use_mask = eps_use_mask;
+	eps_hid.set_color = eps_set_color;
+	eps_hid.set_line_cap = eps_set_line_cap;
+	eps_hid.set_line_width = eps_set_line_width;
+	eps_hid.set_draw_xor = eps_set_draw_xor;
+	eps_hid.draw_line = eps_draw_line;
+	eps_hid.draw_arc = eps_draw_arc;
+	eps_hid.draw_rect = eps_draw_rect;
+	eps_hid.fill_circle = eps_fill_circle;
+	eps_hid.fill_polygon = eps_fill_polygon;
+	eps_hid.fill_rect = eps_fill_rect;
+	eps_hid.calibrate = eps_calibrate;
+	eps_hid.set_crosshair = eps_set_crosshair;
+
+	eps_hid.usage = eps_usage;
+
+	hid_register_hid(&eps_hid);
+}
diff --git a/src_plugins/export_ps/ps.c b/src_plugins/export_ps/ps.c
new file mode 100644
index 0000000..4522010
--- /dev/null
+++ b/src_plugins/export_ps/ps.c
@@ -0,0 +1,1631 @@
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>							/* not used */
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>							/* not used */
+#include <time.h>
+
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "layer.h"
+#include "error.h"
+#include "draw.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_helper.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "ps.h"
+#include "draw_fab.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_flags.h"
+#include "hid_actions.h"
+#include "conf_core.h"
+#include "compat_misc.h"
+
+const char *ps_cookie = "ps HID";
+
+static int ps_set_layer(const char *name, int group, int empty);
+static void use_gc(hidGC gc);
+
+typedef struct hid_gc_struct {
+	HID *me_pointer;
+	EndCapStyle cap;
+	Coord width;
+	unsigned char r, g, b;
+	int erase;
+	int faded;
+} hid_gc_struct;
+
+static const char *medias[] = {
+	"A0", "A1", "A2", "A3", "A4", "A5",
+	"A6", "A7", "A8", "A9", "A10",
+	"B0", "B1", "B2", "B3", "B4", "B5",
+	"B6", "B7", "B8", "B9", "B10",
+	"Letter", "11x17", "Ledger",
+	"Legal", "Executive",
+	"A-Size", "B-size",
+	"C-Size", "D-size", "E-size",
+	"US-Business_Card", "Intl-Business_Card",
+	0
+};
+
+typedef struct {
+	const char *name;
+	Coord Width, Height;
+	Coord MarginX, MarginY;
+} MediaType, *MediaTypePtr;
+
+/*
+ * Metric ISO sizes in mm.  See http://en.wikipedia.org/wiki/ISO_paper_sizes
+ *
+ * A0  841 x 1189
+ * A1  594 x 841
+ * A2  420 x 594
+ * A3  297 x 420
+ * A4  210 x 297
+ * A5  148 x 210
+ * A6  105 x 148
+ * A7   74 x 105
+ * A8   52 x  74
+ * A9   37 x  52
+ * A10  26 x  37
+ *
+ * B0  1000 x 1414
+ * B1   707 x 1000
+ * B2   500 x  707
+ * B3   353 x  500
+ * B4   250 x  353
+ * B5   176 x  250
+ * B6   125 x  176
+ * B7    88 x  125
+ * B8    62 x   88
+ * B9    44 x   62
+ * B10   31 x   44
+ *
+ * awk '{printf("  {\"%s\", %d, %d, MARGINX, MARGINY},\n", $2, $3*100000/25.4, $5*100000/25.4)}'
+ *
+ * See http://en.wikipedia.org/wiki/Paper_size#Loose_sizes for some of the other sizes.  The
+ * {A,B,C,D,E}-Size here are the ANSI sizes and not the architectural sizes.
+ */
+
+#define MARGINX PCB_MIL_TO_COORD(500)
+#define MARGINY PCB_MIL_TO_COORD(500)
+
+static MediaType media_data[] = {
+	{"A0", PCB_MM_TO_COORD(841), PCB_MM_TO_COORD(1189), MARGINX, MARGINY},
+	{"A1", PCB_MM_TO_COORD(594), PCB_MM_TO_COORD(841), MARGINX, MARGINY},
+	{"A2", PCB_MM_TO_COORD(420), PCB_MM_TO_COORD(594), MARGINX, MARGINY},
+	{"A3", PCB_MM_TO_COORD(297), PCB_MM_TO_COORD(420), MARGINX, MARGINY},
+	{"A4", PCB_MM_TO_COORD(210), PCB_MM_TO_COORD(297), MARGINX, MARGINY},
+	{"A5", PCB_MM_TO_COORD(148), PCB_MM_TO_COORD(210), MARGINX, MARGINY},
+	{"A6", PCB_MM_TO_COORD(105), PCB_MM_TO_COORD(148), MARGINX, MARGINY},
+	{"A7", PCB_MM_TO_COORD(74), PCB_MM_TO_COORD(105), MARGINX, MARGINY},
+	{"A8", PCB_MM_TO_COORD(52), PCB_MM_TO_COORD(74), MARGINX, MARGINY},
+	{"A9", PCB_MM_TO_COORD(37), PCB_MM_TO_COORD(52), MARGINX, MARGINY},
+	{"A10", PCB_MM_TO_COORD(26), PCB_MM_TO_COORD(37), MARGINX, MARGINY},
+	{"B0", PCB_MM_TO_COORD(1000), PCB_MM_TO_COORD(1414), MARGINX, MARGINY},
+	{"B1", PCB_MM_TO_COORD(707), PCB_MM_TO_COORD(1000), MARGINX, MARGINY},
+	{"B2", PCB_MM_TO_COORD(500), PCB_MM_TO_COORD(707), MARGINX, MARGINY},
+	{"B3", PCB_MM_TO_COORD(353), PCB_MM_TO_COORD(500), MARGINX, MARGINY},
+	{"B4", PCB_MM_TO_COORD(250), PCB_MM_TO_COORD(353), MARGINX, MARGINY},
+	{"B5", PCB_MM_TO_COORD(176), PCB_MM_TO_COORD(250), MARGINX, MARGINY},
+	{"B6", PCB_MM_TO_COORD(125), PCB_MM_TO_COORD(176), MARGINX, MARGINY},
+	{"B7", PCB_MM_TO_COORD(88), PCB_MM_TO_COORD(125), MARGINX, MARGINY},
+	{"B8", PCB_MM_TO_COORD(62), PCB_MM_TO_COORD(88), MARGINX, MARGINY},
+	{"B9", PCB_MM_TO_COORD(44), PCB_MM_TO_COORD(62), MARGINX, MARGINY},
+	{"B10", PCB_MM_TO_COORD(31), PCB_MM_TO_COORD(44), MARGINX, MARGINY},
+	{"Letter", PCB_INCH_TO_COORD(8.5), PCB_INCH_TO_COORD(11), MARGINX, MARGINY},
+	{"11x17", PCB_INCH_TO_COORD(11), PCB_INCH_TO_COORD(17), MARGINX, MARGINY},
+	{"Ledger", PCB_INCH_TO_COORD(17), PCB_INCH_TO_COORD(11), MARGINX, MARGINY},
+	{"Legal", PCB_INCH_TO_COORD(8.5), PCB_INCH_TO_COORD(14), MARGINX, MARGINY},
+	{"Executive", PCB_INCH_TO_COORD(7.5), PCB_INCH_TO_COORD(10), MARGINX, MARGINY},
+	{"A-size", PCB_INCH_TO_COORD(8.5), PCB_INCH_TO_COORD(11), MARGINX, MARGINY},
+	{"B-size", PCB_INCH_TO_COORD(11), PCB_INCH_TO_COORD(17), MARGINX, MARGINY},
+	{"C-size", PCB_INCH_TO_COORD(17), PCB_INCH_TO_COORD(22), MARGINX, MARGINY},
+	{"D-size", PCB_INCH_TO_COORD(22), PCB_INCH_TO_COORD(34), MARGINX, MARGINY},
+	{"E-size", PCB_INCH_TO_COORD(34), PCB_INCH_TO_COORD(44), MARGINX, MARGINY},
+	{"US-Business_Card", PCB_INCH_TO_COORD(3.5), PCB_INCH_TO_COORD(2.0), 0, 0},
+	{"Intl-Business_Card", PCB_INCH_TO_COORD(3.375), PCB_INCH_TO_COORD(2.125), 0, 0}
+};
+
+#undef MARGINX
+#undef MARGINY
+
+HID_Attribute ps_attribute_list[] = {
+	/* other HIDs expect this to be first.  */
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --psfile <string>
+Name of the postscript output file. Can contain a path.
+ at end ftable
+%end-doc
+*/
+	{"psfile", "Postscript output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_psfile 0
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex drill-helper
+ at item --drill-helper
+Print a centering target in large drill holes.
+ at end ftable
+%end-doc
+*/
+	{"drill-helper", "Print a centering target in large drill holes",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_drillhelper 1
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex align-marks
+ at item --align-marks
+Print alignment marks on each sheet. This is meant to ease alignment during exposure.
+ at end ftable
+%end-doc
+*/
+	{"align-marks", "Print alignment marks on each sheet",
+	 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
+#define HA_alignmarks 2
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --outline
+Print the contents of the outline layer on each sheet.
+ at end ftable
+%end-doc
+*/
+	{"outline", "Print outline on each sheet",
+	 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
+#define HA_outline 3
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --mirror
+Print mirror image.
+ at end ftable
+%end-doc
+*/
+	{"mirror", "Print mirror image of every page",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_mirror 4
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --fill-page
+Scale output to make the board fit the page.
+ at end ftable
+%end-doc
+*/
+	{"fill-page", "Scale board to fill page",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_fillpage 5
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --auto-mirror
+Print mirror image of appropriate layers.
+ at end ftable
+%end-doc
+*/
+	{"auto-mirror", "Print mirror image of appropriate layers",
+	 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
+#define HA_automirror 6
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --ps-color
+Postscript output in color.
+ at end ftable
+%end-doc
+*/
+	{"ps-color", "Prints in color",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_color 7
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex ps-bloat
+ at item --ps-bloat <num>
+Amount to add to trace/pad/pin edges.
+ at end ftable
+%end-doc
+*/
+	{"ps-bloat", "Amount to add to trace/pad/pin edges",
+	 HID_Coord, -PCB_MIL_TO_COORD(100), PCB_MIL_TO_COORD(100), {0, 0, 0}, 0, 0},
+#define HA_psbloat 8
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex ps-invert
+ at item --ps-invert
+Draw objects as white-on-black.
+ at end ftable
+%end-doc
+*/
+	{"ps-invert", "Draw objects as white-on-black",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_psinvert 9
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --media <media-name>
+Size of the media, the postscript is fitted to. The parameter
+ at code{<media-name>} can be any of the standard names for paper size: @samp{A0}
+to @samp{A10}, @samp{B0} to @samp{B10}, @samp{Letter}, @samp{11x17},
+ at samp{Ledger}, @samp{Legal}, @samp{Executive}, @samp{A-Size}, @samp{B-size},
+ at samp{C-Size}, @samp{D-size}, @samp{E-size}, @samp{US-Business_Card},
+ at samp{Intl-Business_Card}.
+ at end ftable
+%end-doc
+*/
+	{"media", "media type",
+	 HID_Enum, 0, 0, {22, 0, 0}, medias, 0},
+#define HA_media 10
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex psfade
+ at item --psfade <num>
+Fade amount for assembly drawings (0.0=missing, 1.0=solid).
+ at end ftable
+%end-doc
+*/
+	{"psfade", "Fade amount for assembly drawings (0.0=missing, 1.0=solid)",
+	 HID_Real, 0, 1, {0, 0, 0.40}, 0, 0},
+#define HA_psfade 11
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --scale <num>
+Scale value to compensate for printer sizing errors (1.0 = full scale).
+ at end ftable
+%end-doc
+*/
+	{"scale", "Scale value to compensate for printer sizing errors (1.0 = full scale)",
+	 HID_Real, 0.01, 4, {0, 0, 1.00}, 0, 0},
+#define HA_scale 12
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex multi-file
+ at item --multi-file
+Produce multiple files, one per page, instead of a single multi page file.
+ at end ftable
+%end-doc
+*/
+	{"multi-file", "Produce multiple files, one per page, instead of a single file",
+	 HID_Boolean, 0, 0, {0, 0, 0.40}, 0, 0},
+#define HA_multifile 13
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --xcalib <num>
+Paper width. Used for x-Axis calibration.
+ at end ftable
+%end-doc
+*/
+	{"xcalib", "Paper width. Used for x-Axis calibration",
+	 HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
+#define HA_xcalib 14
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --ycalib <num>
+Paper height. Used for y-Axis calibration.
+ at end ftable
+%end-doc
+*/
+	{"ycalib", "Paper height. Used for y-Axis calibration",
+	 HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
+#define HA_ycalib 15
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --drill-copper
+Draw drill holes in pins / vias, instead of leaving solid copper.
+ at end ftable
+%end-doc
+*/
+	{"drill-copper", "Draw drill holes in pins / vias, instead of leaving solid copper",
+	 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
+#define HA_drillcopper 16
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at cindex show-legend
+ at item --show-legend
+Print file name and scale on printout.
+ at end ftable
+%end-doc
+*/
+	{"show-legend", "Print file name and scale on printout",
+	 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
+#define HA_legend 17
+
+/* %start-doc options "91 Postscript Export"
+ at ftable @code
+ at item --polygrid <num>
+If non-zero grid polygons instead of filling them with gridlines spaced as specified.
+ at end ftable
+%end-doc
+*/
+	{"polygrid", "When non-zero: grid polygons instead of filling  with gridlines spaced as specified",
+	 HID_Real, 0, 10, {0, 0, 0.0}, 0, 0},
+#define HA_polygrid 18
+
+};
+
+#define NUM_OPTIONS (sizeof(ps_attribute_list)/sizeof(ps_attribute_list[0]))
+
+REGISTER_ATTRIBUTES(ps_attribute_list, ps_cookie)
+
+/* All file-scope data is in global struct */
+static struct {
+	double calibration_x, calibration_y;
+
+	FILE *f;
+	int pagecount;
+	Coord linewidth;
+	pcb_bool print_group[MAX_LAYER];
+	pcb_bool print_layer[MAX_LAYER];
+	double fade_ratio;
+	pcb_bool multi_file;
+	Coord media_width, media_height, ps_width, ps_height;
+
+	const char *filename;
+	pcb_bool drill_helper;
+	pcb_bool align_marks;
+	pcb_bool outline;
+	pcb_bool mirror;
+	pcb_bool fillpage;
+	pcb_bool automirror;
+	pcb_bool incolor;
+	pcb_bool doing_toc;
+	Coord bloat;
+	pcb_bool invert;
+	int media_idx;
+	pcb_bool drillcopper;
+	pcb_bool legend;
+
+	LayerTypePtr outline_layer;
+
+	double scale_factor;
+
+	BoxType region;
+
+	HID_Attr_Val ps_values[NUM_OPTIONS];
+
+	pcb_bool is_mask;
+	pcb_bool is_drill;
+	pcb_bool is_assy;
+	pcb_bool is_copper;
+	pcb_bool is_paste;
+
+	double polygrid;
+} global;
+
+static HID_Attribute *ps_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+	if (PCB)
+		derive_default_filename(PCB->Filename, &ps_attribute_list[HA_psfile], ".ps", &last_made_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return ps_attribute_list;
+}
+
+static int group_for_layer(int l)
+{
+	if (l < max_copper_layer + 2 && l >= 0)
+		return GetLayerGroupNumberByNumber(l);
+	/* else something unique */
+	return max_group + 3 + l;
+}
+
+static int layer_sort(const void *va, const void *vb)
+{
+	int a = *(int *) va;
+	int b = *(int *) vb;
+	int d = group_for_layer(b) - group_for_layer(a);
+	if (d)
+		return d;
+	return b - a;
+}
+
+void ps_start_file(FILE * f)
+{
+	time_t currenttime = time(NULL);
+
+	fprintf(f, "%%!PS-Adobe-3.0\n");
+
+	/* Document Structuring Conventions (DCS): */
+
+	/* Start General Header Comments: */
+
+	/*
+	 * %%Title DCS provides text title for the document that is useful
+	 * for printing banner pages.
+	 */
+	fprintf(f, "%%%%Title: %s\n", PCB->Filename);
+
+	/*
+	 * %%CreationDate DCS indicates the date and time the document was
+	 * created. Neither the date nor time need be in any standard
+	 * format. This comment is meant to be used purely for informational
+	 * purposes, such as printing on banner pages.
+	 */
+	fprintf(f, "%%%%CreationDate: %s", asctime(localtime(&currenttime)));
+
+	/*
+	 * %%Creator DCS indicates the document creator, usually the name of
+	 * the document composition software.
+	 */
+	fprintf(f, "%%%%Creator: PCB release: pcb-rnd " VERSION "\n");
+
+	/*
+	 * %%Version DCS comment can be used to note the version and
+	 * revision number of a document or resource. A document manager may
+	 * wish to provide version control services, or allow substitution
+	 * of compatible versions/revisions of a resource or document.
+	 *
+	 * The format should be in the form of 'procname':
+	 *  <procname>::= < name> < version> < revision>
+	 *  < name> ::= < text>
+	 *  < version> ::= < real>
+	 *  < revision> ::= < uint>
+	 *
+	 * If a version numbering scheme is not used, these fields should
+	 * still be filled with a dummy value of 0.
+	 *
+	 * There is currently no code in PCB to manage this revision number.
+	 *
+	 */
+	fprintf(f, "%%%%Version: (PCB pcb-rnd " VERSION ") 0.0 0\n");
+
+
+	/*
+	 * %%PageOrder DCS is intended to help document managers determine
+	 * the order of pages in the document file, which in turn enables a
+	 * document manager optionally to reorder the pages.  'Ascend'-The
+	 * pages are in ascending order for example, 1-2-3-4-5-6.
+	 */
+	fprintf(f, "%%%%PageOrder: Ascend\n");
+
+	/*
+	 * %%Pages: < numpages> | (atend) < numpages> ::= < uint> (Total
+	 * %%number of pages)
+	 *
+	 * %%Pages DCS defines the number of virtual pages that a document
+	 * will image.  (atend) defers the count until the end of the file,
+	 * which is useful for dynamically generated contents.
+	 */
+	fprintf(f, "%%%%Pages: (atend)\n");
+
+	/*
+	 * %%DocumentMedia: <name> <width> <height> <weight> <color> <type>
+	 *
+	 * Substitute 0 or "" for N/A.  Width and height are in points
+	 * (1/72").
+	 *
+	 * Media sizes are in PCB units
+	 */
+	pcb_fprintf(f, "%%%%DocumentMedia: %s %mi %mi 0 \"\" \"\"\n",
+							media_data[global.media_idx].name,
+							72 * media_data[global.media_idx].Width, 72 * media_data[global.media_idx].Height);
+	pcb_fprintf(f, "%%%%DocumentPaperSizes: %s\n", media_data[global.media_idx].name);
+
+	/* End General Header Comments. */
+
+	/* General Body Comments go here. Currently there are none. */
+
+	/*
+	 * %%EndComments DCS indicates an explicit end to the header
+	 * comments of the document.  All global DCS's must preceded
+	 * this.  A blank line gives an implicit end to the comments.
+	 */
+	fprintf(f, "%%%%EndComments\n\n");
+}
+
+static void ps_end_file(FILE * f)
+{
+	/*
+	 * %%Trailer DCS must only occur once at the end of the document
+	 * script.  Any post-processing or cleanup should be contained in
+	 * the trailer of the document, which is anything that follows the
+	 * %%Trailer comment. Any of the document level structure comments
+	 * that were deferred by using the (atend) convention must be
+	 * mentioned in the trailer of the document after the %%Trailer
+	 * comment.
+	 */
+	fprintf(f, "%%%%Trailer\n");
+
+	/*
+	 * %%Pages was deferred until the end of the document via the
+	 * (atend) mentioned, in the General Header section.
+	 */
+	fprintf(f, "%%%%Pages: %d\n", global.pagecount);
+
+	/*
+	 * %%EOF DCS signifies the end of the document. When the document
+	 * manager sees this comment, it issues an end-of-file signal to the
+	 * PostScript interpreter.  This is done so system-dependent file
+	 * endings, such as Control-D and end-of-file packets, do not
+	 * confuse the PostScript interpreter.
+	 */
+	fprintf(f, "%%%%EOF\n");
+}
+
+static FILE *psopen(const char *base, const char *which)
+{
+	FILE *ps_open_file;
+	char *buf, *suff, *buf2;
+
+	if (!global.multi_file)
+		return fopen(base, "w");
+
+	buf = (char *) malloc(strlen(base) + strlen(which) + 5);
+
+	suff = (char *) strrchr(base, '.');
+	if (suff) {
+		strcpy(buf, base);
+		buf2 = strrchr(buf, '.');
+		sprintf(buf2, ".%s.%s", which, suff + 1);
+	}
+	else {
+		sprintf(buf, "%s.%s.ps", base, which);
+	}
+	printf("PS: open %s\n", buf);
+	ps_open_file = fopen(buf, "w");
+	free(buf);
+	return ps_open_file;
+}
+
+/* This is used by other HIDs that use a postscript format, like lpr
+   or eps.  */
+void ps_hid_export_to_file(FILE * the_file, HID_Attr_Val * options)
+{
+	int i;
+	static int saved_layer_stack[MAX_LAYER];
+
+	conf_force_set_bool(conf_core.editor.thin_draw, 0);
+	conf_force_set_bool(conf_core.editor.thin_draw_poly, 0);
+	conf_force_set_bool(conf_core.editor.check_planes, 0);
+
+	global.f = the_file;
+	global.drill_helper = options[HA_drillhelper].int_value;
+	global.align_marks = options[HA_alignmarks].int_value;
+	global.outline = options[HA_outline].int_value;
+	global.mirror = options[HA_mirror].int_value;
+	global.fillpage = options[HA_fillpage].int_value;
+	global.automirror = options[HA_automirror].int_value;
+	global.incolor = options[HA_color].int_value;
+	global.bloat = options[HA_psbloat].int_value;
+	global.invert = options[HA_psinvert].int_value;
+	global.fade_ratio = PCB_CLAMP(options[HA_psfade].real_value, 0, 1);
+	global.media_idx = options[HA_media].int_value;
+	global.media_width = media_data[global.media_idx].Width;
+	global.media_height = media_data[global.media_idx].Height;
+	global.ps_width = global.media_width - 2.0 * media_data[global.media_idx].MarginX;
+	global.ps_height = global.media_height - 2.0 * media_data[global.media_idx].MarginY;
+	global.scale_factor = options[HA_scale].real_value;
+	global.calibration_x = options[HA_xcalib].real_value;
+	global.calibration_y = options[HA_ycalib].real_value;
+	global.drillcopper = options[HA_drillcopper].int_value;
+	global.legend = options[HA_legend].int_value;
+	global.polygrid = options[HA_polygrid].real_value;
+
+	if (the_file)
+		ps_start_file(the_file);
+
+	if (global.fillpage) {
+		double zx, zy;
+		if (PCB->MaxWidth > PCB->MaxHeight) {
+			zx = global.ps_height / PCB->MaxWidth;
+			zy = global.ps_width / PCB->MaxHeight;
+		}
+		else {
+			zx = global.ps_height / PCB->MaxHeight;
+			zy = global.ps_width / PCB->MaxWidth;
+		}
+		global.scale_factor *= MIN(zx, zy);
+	}
+
+	memset(global.print_group, 0, sizeof(global.print_group));
+	memset(global.print_layer, 0, sizeof(global.print_layer));
+
+	global.outline_layer = NULL;
+
+	for (i = 0; i < max_copper_layer; i++) {
+		LayerType *layer = PCB->Data->Layer + i;
+		if (!LAYER_IS_EMPTY(layer))
+			global.print_group[GetLayerGroupNumberByNumber(i)] = 1;
+
+		if (strcmp(layer->Name, "outline") == 0 || strcmp(layer->Name, "route") == 0) {
+			global.outline_layer = layer;
+		}
+	}
+	global.print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1;
+	global.print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1;
+	for (i = 0; i < max_copper_layer; i++)
+		if (global.print_group[GetLayerGroupNumberByNumber(i)])
+			global.print_layer[i] = 1;
+
+	memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack));
+	qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort);
+
+	global.linewidth = -1;
+	/* reset static vars */
+	ps_set_layer(NULL, 0, -1);
+	use_gc(NULL);
+
+	global.region.X1 = 0;
+	global.region.Y1 = 0;
+	global.region.X2 = PCB->MaxWidth;
+	global.region.Y2 = PCB->MaxHeight;
+
+	if (!global.multi_file) {
+		/* %%Page DSC requires both a label and an ordinal */
+		fprintf(the_file, "%%%%Page: TableOfContents 1\n");
+		fprintf(the_file, "/Times-Roman findfont 24 scalefont setfont\n");
+		fprintf(the_file, "/rightshow { /s exch def s stringwidth pop -1 mul 0 rmoveto s show } def\n");
+		fprintf(the_file, "/y 72 9 mul def /toc { 100 y moveto show /y y 24 sub def } bind def\n");
+		fprintf(the_file, "/tocp { /y y 12 sub def 90 y moveto rightshow } bind def\n");
+
+		global.doing_toc = 1;
+		global.pagecount = 1;				/* 'pagecount' is modified by hid_expose_callback() call */
+		hid_expose_callback(&ps_hid, &global.region, 0);
+	}
+
+	global.pagecount = 1;					/* Reset 'pagecount' if single file */
+	global.doing_toc = 0;
+	ps_set_layer(NULL, 0, -1);		/* reset static vars */
+	hid_expose_callback(&ps_hid, &global.region, 0);
+
+	if (the_file)
+		fprintf(the_file, "showpage\n");
+
+	memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack));
+	conf_update(NULL); /* restore forced sets */
+}
+
+static void ps_do_export(HID_Attr_Val * options)
+{
+	FILE *fh;
+	int save_ons[MAX_LAYER + 2];
+	int i;
+
+	if (!options) {
+		ps_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			global.ps_values[i] = ps_attribute_list[i].default_val;
+		options = global.ps_values;
+	}
+
+	global.filename = options[HA_psfile].str_value;
+	if (!global.filename)
+		global.filename = "pcb-out.ps";
+
+	global.multi_file = options[HA_multifile].int_value;
+
+	if (global.multi_file)
+		fh = 0;
+	else {
+		fh = psopen(global.filename, "toc");
+		if (!fh) {
+			perror(global.filename);
+			return;
+		}
+	}
+
+	hid_save_and_show_layer_ons(save_ons);
+	ps_hid_export_to_file(fh, options);
+	hid_restore_layer_ons(save_ons);
+
+	global.multi_file = 0;
+	if (fh) {
+		ps_end_file(fh);
+		fclose(fh);
+	}
+}
+
+static void ps_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(ps_attribute_list, NUM_OPTIONS, ps_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static void corner(FILE * fh, Coord x, Coord y, Coord dx, Coord dy)
+{
+	Coord len = PCB_MIL_TO_COORD(2000);
+	Coord len2 = PCB_MIL_TO_COORD(200);
+	Coord thick = 0;
+	/*
+	 * Originally 'thick' used thicker lines.  Currently is uses
+	 * Postscript's "device thin" line - i.e. zero width means one
+	 * device pixel.  The code remains in case you want to make them
+	 * thicker - it needs to offset everything so that the *edge* of the
+	 * thick line lines up with the edge of the board, not the *center*
+	 * of the thick line.
+	 */
+
+	pcb_fprintf(fh, "gsave %mi setlinewidth %mi %mi translate %mi %mi scale\n", thick * 2, x, y, dx, dy);
+	pcb_fprintf(fh, "%mi %mi moveto %mi %mi %mi 0 90 arc %mi %mi lineto\n", len, thick, thick, thick, len2 + thick, thick, len);
+	if (dx < 0 && dy < 0)
+		pcb_fprintf(fh, "%mi %mi moveto 0 %mi rlineto\n", len2 * 2 + thick, thick, -len2);
+	fprintf(fh, "stroke grestore\n");
+}
+
+static int ps_set_layer(const char *name, int group, int empty)
+{
+	static int lastgroup = -1;
+	time_t currenttime;
+	int idx = (group >= 0 && group < max_group)
+		? PCB->LayerGroups.Entries[group][0]
+		: group;
+	if (name == 0)
+		name = PCB->Data->Layer[idx].Name;
+
+	if (empty == -1)
+		lastgroup = -1;
+	if (empty)
+		return 0;
+
+	if (idx >= 0 && idx < max_copper_layer && !global.print_layer[idx])
+		return 0;
+
+	if (strcmp(name, "invisible") == 0)
+		return 0;
+
+	global.is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
+	global.is_mask = (SL_TYPE(idx) == SL_MASK);
+	global.is_assy = (SL_TYPE(idx) == SL_ASSY);
+	global.is_copper = (SL_TYPE(idx) == 0);
+	global.is_paste = (SL_TYPE(idx) == SL_PASTE);
+#if 0
+	printf("Layer %s group %d drill %d mask %d\n", name, group, global.is_drill, global.is_mask);
+#endif
+
+	if (global.doing_toc) {
+		if (group < 0 || group != lastgroup) {
+			if (global.pagecount == 1) {
+				currenttime = time(NULL);
+				fprintf(global.f, "30 30 moveto (%s) show\n", PCB->Filename);
+
+				fprintf(global.f, "(%d.) tocp\n", global.pagecount);
+				fprintf(global.f, "(Table of Contents \\(This Page\\)) toc\n");
+
+				fprintf(global.f, "(Created on %s) toc\n", asctime(localtime(&currenttime)));
+				fprintf(global.f, "( ) tocp\n");
+			}
+
+			global.pagecount++;
+			lastgroup = group;
+			fprintf(global.f, "(%d.) tocp\n", global.pagecount);
+		}
+		fprintf(global.f, "(%s) toc\n", name);
+		return 0;
+	}
+
+	if (group < 0 || group != lastgroup) {
+		double boffset;
+		int mirror_this = 0;
+		lastgroup = group;
+
+		if (global.pagecount != 0) {
+			pcb_fprintf(global.f, "showpage\n");
+		}
+		global.pagecount++;
+		if (global.multi_file) {
+			if (global.f) {
+				ps_end_file(global.f);
+				fclose(global.f);
+			}
+			global.f = psopen(global.filename, layer_type_to_file_name(idx, FNS_fixed));
+			if (!global.f) {
+				perror(global.filename);
+				return 0;
+			}
+
+			ps_start_file(global.f);
+		}
+
+		/*
+		 * %%Page DSC comment marks the beginning of the PostScript
+		 * language instructions that describe a particular
+		 * page. %%Page: requires two arguments: a page label and a
+		 * sequential page number. The label may be anything, but the
+		 * ordinal page number must reflect the position of that page in
+		 * the body of the PostScript file and must start with 1, not 0.
+		 */
+		fprintf(global.f, "%%%%Page: %s %d\n", layer_type_to_file_name(idx, FNS_fixed), global.pagecount);
+
+		if (global.mirror)
+			mirror_this = !mirror_this;
+		if (global.automirror && ((idx >= 0 && group == GetLayerGroupNumberByNumber(solder_silk_layer))
+															|| (idx < 0 && SL_SIDE(idx) == SL_BOTTOM_SIDE)))
+			mirror_this = !mirror_this;
+
+		fprintf(global.f, "/Helvetica findfont 10 scalefont setfont\n");
+		if (global.legend) {
+			fprintf(global.f, "30 30 moveto (%s) show\n", PCB->Filename);
+			if (PCB->Name)
+				fprintf(global.f, "30 41 moveto (%s, %s) show\n", PCB->Name, layer_type_to_file_name(idx, FNS_fixed));
+			else
+				fprintf(global.f, "30 41 moveto (%s) show\n", layer_type_to_file_name(idx, FNS_fixed));
+			if (mirror_this)
+				fprintf(global.f, "( \\(mirrored\\)) show\n");
+
+			if (global.fillpage)
+				fprintf(global.f, "(, not to scale) show\n");
+			else
+				fprintf(global.f, "(, scale = 1:%.3f) show\n", global.scale_factor);
+		}
+		fprintf(global.f, "newpath\n");
+
+		pcb_fprintf(global.f, "72 72 scale %mi %mi translate\n", global.media_width / 2, global.media_height / 2);
+
+		boffset = global.media_height / 2;
+		if (PCB->MaxWidth > PCB->MaxHeight) {
+			fprintf(global.f, "90 rotate\n");
+			boffset = global.media_width / 2;
+			fprintf(global.f, "%g %g scale %% calibration\n", global.calibration_y, global.calibration_x);
+		}
+		else
+			fprintf(global.f, "%g %g scale %% calibration\n", global.calibration_x, global.calibration_y);
+
+		if (mirror_this)
+			fprintf(global.f, "1 -1 scale\n");
+
+		fprintf(global.f, "%g dup neg scale\n", (SL_TYPE(idx) == SL_FAB) ? 1.0 : global.scale_factor);
+		pcb_fprintf(global.f, "%mi %mi translate\n", -PCB->MaxWidth / 2, -PCB->MaxHeight / 2);
+
+		/* Keep the drill list from falling off the left edge of the paper,
+		 * even if it means some of the board falls off the right edge.
+		 * If users don't want to make smaller boards, or use fewer drill
+		 * sizes, they can always ignore this sheet. */
+		if (SL_TYPE(idx) == SL_FAB) {
+			Coord natural = boffset - PCB_MIL_TO_COORD(500) - PCB->MaxHeight / 2;
+			Coord needed = DrawFab_overhang();
+			pcb_fprintf(global.f, "%% PrintFab overhang natural %mi, needed %mi\n", natural, needed);
+			if (needed > natural)
+				pcb_fprintf(global.f, "0 %mi translate\n", needed - natural);
+		}
+
+		if (global.invert) {
+			fprintf(global.f, "/gray { 1 exch sub setgray } bind def\n");
+			fprintf(global.f, "/rgb { 1 1 3 { pop 1 exch sub 3 1 roll } for setrgbcolor } bind def\n");
+		}
+		else {
+			fprintf(global.f, "/gray { setgray } bind def\n");
+			fprintf(global.f, "/rgb { setrgbcolor } bind def\n");
+		}
+
+		if ((global.outline && !global.outline_layer) ||global.invert) {
+			pcb_fprintf(global.f,
+									"0 setgray 0 setlinewidth 0 0 moveto 0 "
+									"%mi lineto %mi %mi lineto %mi 0 lineto closepath %s\n",
+									PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight, PCB->MaxWidth, global.invert ? "fill" : "stroke");
+		}
+
+		if (global.align_marks) {
+			corner(global.f, 0, 0, -1, -1);
+			corner(global.f, PCB->MaxWidth, 0, 1, -1);
+			corner(global.f, PCB->MaxWidth, PCB->MaxHeight, 1, 1);
+			corner(global.f, 0, PCB->MaxHeight, -1, 1);
+		}
+
+		global.linewidth = -1;
+		use_gc(NULL);								/* reset static vars */
+
+		fprintf(global.f,
+						"/ts 1 def\n"
+						"/ty ts neg def /tx 0 def /Helvetica findfont ts scalefont setfont\n"
+						"/t { moveto lineto stroke } bind def\n"
+						"/dr { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
+						"      x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath stroke } bind def\n");
+		fprintf(global.f,"/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
+						"     x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n"
+						"/c { 0 360 arc fill } bind def\n"
+						"/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
+		if (global.drill_helper)
+			pcb_fprintf(global.f,
+									"/dh { gsave %mi setlinewidth 0 gray %mi 0 360 arc stroke grestore} bind def\n",
+									(Coord) MIN_PINORVIAHOLE, (Coord) (MIN_PINORVIAHOLE * 3 / 2));
+	}
+#if 0
+	/* Try to outsmart ps2pdf's heuristics for page rotation, by putting
+	 * text on all pages -- even if that text is blank */
+	if (SL_TYPE(idx) != SL_FAB)
+		fprintf(global.f, "gsave tx ty translate 1 -1 scale 0 0 moveto (Layer %s) show grestore newpath /ty ty ts sub def\n", name);
+	else
+		fprintf(global.f, "gsave tx ty translate 1 -1 scale 0 0 moveto ( ) show grestore newpath /ty ty ts sub def\n");
+#endif
+
+	/* If we're printing a layer other than the outline layer, and
+	   we want to "print outlines", and we have an outline layer,
+	   print the outline layer on this layer also.  */
+	if (global.outline &&
+			global.outline_layer != NULL &&
+			global.outline_layer != PCB->Data->Layer + idx && strcmp(name, "outline") != 0 && strcmp(name, "route") != 0) {
+		DrawLayer(global.outline_layer, &global.region);
+	}
+
+	return 1;
+}
+
+static hidGC ps_make_gc(void)
+{
+	hidGC rv = (hidGC) calloc(1, sizeof(hid_gc_struct));
+	rv->me_pointer = &ps_hid;
+	rv->cap = Trace_Cap;
+	return rv;
+}
+
+static void ps_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void ps_use_mask(int use_it)
+{
+	/* does nothing */
+}
+
+static void ps_set_color(hidGC gc, const char *name)
+{
+	if (strcmp(name, "erase") == 0 || strcmp(name, "drill") == 0) {
+		gc->r = gc->g = gc->b = 255;
+		gc->erase = 1;
+	}
+	else if (global.incolor) {
+		unsigned int r, g, b;
+		sscanf(name + 1, "%02x%02x%02x", &r, &g, &b);
+		gc->r = r;
+		gc->g = g;
+		gc->b = b;
+		gc->erase = 0;
+	}
+	else {
+		gc->r = gc->g = gc->b = 0;
+		gc->erase = 0;
+	}
+}
+
+static void ps_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void ps_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void ps_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+static void ps_set_draw_faded(hidGC gc, int faded)
+{
+	gc->faded = faded;
+}
+
+static void use_gc(hidGC gc)
+{
+	static int lastcap = -1;
+	static int lastcolor = -1;
+
+	if (gc == NULL) {
+		lastcap = lastcolor = -1;
+		return;
+	}
+	if (gc->me_pointer != &ps_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to ps HID\n");
+		abort();
+	}
+	if (global.linewidth != gc->width) {
+		pcb_fprintf(global.f, "%mi setlinewidth\n", gc->width + (gc->erase ? -2 : 2) * global.bloat);
+		global.linewidth = gc->width;
+	}
+	if (lastcap != gc->cap) {
+		int c;
+		switch (gc->cap) {
+		case Round_Cap:
+		case Trace_Cap:
+			c = 1;
+			break;
+		default:
+		case Square_Cap:
+			c = 2;
+			break;
+		}
+		fprintf(global.f, "%d setlinecap %d setlinejoin\n", c, c);
+		lastcap = gc->cap;
+	}
+#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
+	if (lastcolor != CBLEND(gc)) {
+		if (global.is_drill || global.is_mask) {
+			fprintf(global.f, "%d gray\n", gc->erase ? 0 : 1);
+			lastcolor = 0;
+		}
+		else {
+			double r, g, b;
+			r = gc->r;
+			g = gc->g;
+			b = gc->b;
+			if (gc->faded) {
+				r = (1 - global.fade_ratio) *255 + global.fade_ratio * r;
+				g = (1 - global.fade_ratio) *255 + global.fade_ratio * g;
+				b = (1 - global.fade_ratio) *255 + global.fade_ratio * b;
+			}
+			if (gc->r == gc->g && gc->g == gc->b)
+				fprintf(global.f, "%g gray\n", r / 255.0);
+			else
+				fprintf(global.f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
+			lastcolor = CBLEND(gc);
+		}
+	}
+}
+
+static void ps_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	pcb_fprintf(global.f, "%mi %mi %mi %mi dr\n", x1, y1, x2, y2);
+}
+
+static void ps_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+static void ps_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+
+static void ps_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+#if 0
+	/* If you're etching your own paste mask, this will reduce the
+	   amount of brass you need to etch by drawing outlines for large
+	   pads.  See also ps_fill_rect.  */
+	if (is_paste && gc->width > 2500 && gc->cap == Square_Cap && (x1 == x2 || y1 == y2)) {
+		Coord t, w;
+		if (x1 > x2) {
+			t = x1;
+			x1 = x2;
+			x2 = t;
+		}
+		if (y1 > y2) {
+			t = y1;
+			y1 = y2;
+			y2 = t;
+		}
+		w = gc->width / 2;
+		ps_fill_rect(gc, x1 - w, y1 - w, x2 + w, y2 + w);
+		return;
+	}
+#endif
+	if (x1 == x2 && y1 == y2) {
+		Coord w = gc->width / 2;
+		if (gc->cap == Square_Cap)
+			ps_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
+		else
+			ps_fill_circle(gc, x1, y1, w);
+		return;
+	}
+	use_gc(gc);
+	pcb_fprintf(global.f, "%mi %mi %mi %mi t\n", x1, y1, x2, y2);
+}
+
+static void ps_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	Angle sa, ea;
+	if (delta_angle > 0) {
+		sa = start_angle;
+		ea = start_angle + delta_angle;
+	}
+	else {
+		sa = start_angle + delta_angle;
+		ea = start_angle;
+	}
+#if 0
+	printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea);
+#endif
+	use_gc(gc);
+	pcb_fprintf(global.f, "%ma %ma %mi %mi %mi %mi %g a\n",
+							sa, ea, -width, height, cx, cy, (double) (global.linewidth + 2 * global.bloat) /(double) width);
+}
+
+static void ps_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	use_gc(gc);
+	if (!gc->erase || !global.is_copper || global.drillcopper) {
+		if (gc->erase && global.is_copper && global.drill_helper && radius >= PCB->minDrill / 4)
+			radius = PCB->minDrill / 4;
+		pcb_fprintf(global.f, "%mi %mi %mi c\n", cx, cy, radius + (gc->erase ? -1 : 1) * global.bloat);
+	}
+}
+
+static void ps_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	const char *op = "moveto";
+	use_gc(gc);
+	for (i = 0; i < n_coords; i++) {
+		pcb_fprintf(global.f, "%mi %mi %s\n", x[i], y[i], op);
+		op = "lineto";
+	}
+	fprintf(global.f, "fill\n");
+}
+
+typedef struct {
+	Coord x1, y1, x2, y2;
+} lseg_t;
+
+typedef struct {
+	Coord x, y;
+} lpoint_t;
+
+#define minmax(val, min, max) \
+do { \
+	if (val < min) min = val; \
+	if (val > max) max = val; \
+} while(0)
+
+#define lsegs_append(x1_, y1_, x2_, y2_) \
+do { \
+	if (y1_ < y2_) { \
+		lsegs[lsegs_used].x1 = x1_; \
+		lsegs[lsegs_used].y1 = y1_; \
+		lsegs[lsegs_used].x2 = x2_; \
+		lsegs[lsegs_used].y2 = y2_; \
+	} \
+	else { \
+		lsegs[lsegs_used].x2 = x1_; \
+		lsegs[lsegs_used].y2 = y1_; \
+		lsegs[lsegs_used].x1 = x2_; \
+		lsegs[lsegs_used].y1 = y2_; \
+	} \
+	lsegs_used++; \
+	minmax(y1_, lsegs_ymin, lsegs_ymax); \
+	minmax(y2_, lsegs_ymin, lsegs_ymax); \
+	minmax(x1_, lsegs_xmin, lsegs_xmax); \
+	minmax(x2_, lsegs_xmin, lsegs_xmax); \
+} while(0)
+
+#define lseg_line(x1_, y1_, x2_, y2_) \
+	do { \
+			fprintf(global.f, "newpath\n"); \
+			pcb_fprintf(global.f, "%mi %mi moveto\n", x1_, y1_); \
+			pcb_fprintf(global.f, "%mi %mi lineto\n", x2_, y2_); \
+			fprintf (global.f, "stroke\n"); \
+	} while(0)
+
+int coord_comp(const void *c1_, const void *c2_)
+{
+	const Coord *c1 = c1_, *c2 = c2_;
+	return *c1 < *c2;
+}
+
+static void ps_fill_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box)
+{
+	/* Ignore clip_box, just draw everything */
+
+	VNODE *v;
+	PLINE *pl;
+	const char *op;
+	int len;
+	double POLYGRID = ps_attribute_list[HA_polygrid].default_val.real_value;
+
+	use_gc(gc);
+
+	pl = poly->Clipped->contours;
+	len = 0;
+	if (POLYGRID > 0.1)
+		POLYGRID *= 1000000.0;
+
+	do {
+		v = pl->head.next;
+		if (POLYGRID > 0.1)
+			fprintf(global.f, "closepath\n");
+		op = "moveto";
+		do {
+			pcb_fprintf(global.f, "%mi %mi %s\n", v->point[0], v->point[1], op);
+			op = "lineto";
+			len++;
+		}
+		while ((v = v->next) != pl->head.next);
+		len++;
+	}
+	while ((pl = pl->next) != NULL);
+
+	if (POLYGRID > 0.1) {
+		Coord y, x, lx, ly, fx, fy, lsegs_xmin, lsegs_xmax, lsegs_ymin, lsegs_ymax;
+		lseg_t *lsegs = malloc(sizeof(lseg_t) * len);
+		Coord *lpoints = malloc(sizeof(Coord) * len);
+		int lsegs_used = 0;
+
+		lsegs_xmin = -1000000000;
+		lsegs_ymin = -1000000000;
+		lsegs_xmax = +1000000000;
+		lsegs_ymax = +1000000000;
+
+		/* save all line segs in an array */
+		pl = poly->Clipped->contours;
+		do {
+			v = pl->head.next;
+			fx = v->point[0];
+			fy = v->point[1];
+			goto start1;
+			do {
+				lsegs_append(lx, ly, v->point[0], v->point[1]);
+			start1:;
+				lx = v->point[0];
+				ly = v->point[1];
+			} while ((v = v->next) != pl->head.next);
+			lsegs_append(lx, ly, fx, fy);
+		} while ((pl = pl->next) != NULL);
+
+
+
+
+		fprintf(global.f, "%% POLYGRID2\n");
+		fprintf(global.f, "gsave\n");
+		fprintf(global.f, "0.0015 setlinewidth\n");
+		fprintf(global.f, "closepath\n");
+		fprintf(global.f, "stroke\n");
+
+		for (y = lsegs_ymin; y < lsegs_ymax; y += POLYGRID) {
+			int pts, n;
+/*		pcb_fprintf(global.f, "%% gridline at y %mi\n", y);*/
+		retry1:;
+			if (y > lsegs_ymax)
+				break;
+			pts = 0;
+			for (n = 0; n < lsegs_used; n++) {
+				if ((lsegs[n].y1 <= y) && (lsegs[n].y2 >= y)) {
+					if ((lsegs[n].y2 == lsegs[n].y1) || (lsegs[n].y1 == y) || (lsegs[n].y2 == y)) {
+						y += POLYGRID / 100.0;
+						goto retry1;
+					}
+					x = lsegs[n].x1 + (lsegs[n].x2 - lsegs[n].x1) * (y - lsegs[n].y1) / (lsegs[n].y2 - lsegs[n].y1);
+					lpoints[pts] = x;
+					pts++;
+				}
+			}
+			if ((pts % 2) != 0) {
+				y += POLYGRID / 100.0;
+				goto retry1;
+			}
+			if (pts > 1) {
+				qsort(lpoints, pts, sizeof(Coord), coord_comp);
+				for (n = 0; n < pts; n += 2)
+					lseg_line(lpoints[n], y, lpoints[n + 1], y);
+			}
+		}
+
+		for (x = lsegs_xmin; x < lsegs_xmax; x += POLYGRID) {
+			int pts, n;
+/*		pcb_fprintf(global.f, "%% gridline at y %mi\n", y); */
+		retry2:;
+			if (x > lsegs_xmax)
+				break;
+			pts = 0;
+			for (n = 0; n < lsegs_used; n++) {
+				if (((lsegs[n].x1 <= x) && (lsegs[n].x2 >= x)) || ((lsegs[n].x1 >= x) && (lsegs[n].x2 <= x))) {
+					if ((lsegs[n].x2 == lsegs[n].x1) || (lsegs[n].x1 == x) || (lsegs[n].x2 == x)) {
+						x += POLYGRID / 100.0;
+						goto retry2;
+					}
+					y = lsegs[n].y1 + (lsegs[n].y2 - lsegs[n].y1) * (x - lsegs[n].x1) / (lsegs[n].x2 - lsegs[n].x1);
+					lpoints[pts] = y;
+					pts++;
+				}
+			}
+			if ((pts % 2) != 0) {
+				x += POLYGRID / 100.0;
+				goto retry2;
+			}
+			if ((pts > 1)) {
+				qsort(lpoints, pts, sizeof(Coord), coord_comp);
+				for (n = 0; n < pts; n += 2)
+					lseg_line(x, lpoints[n], x, lpoints[n + 1]);
+			}
+		}
+
+
+		fprintf(global.f, "grestore\nnewpath\n");
+		free(lsegs);
+		free(lpoints);
+	}
+	else
+		fprintf(global.f, "fill\n");
+}
+
+static void ps_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	use_gc(gc);
+	if (x1 > x2) {
+		Coord t = x1;
+		x1 = x2;
+		x2 = t;
+	}
+	if (y1 > y2) {
+		Coord t = y1;
+		y1 = y2;
+		y2 = t;
+	}
+#if 0
+	/* See comment in ps_draw_line.  */
+	if (is_paste && (x2 - x1) > 2500 && (y2 - y1) > 2500) {
+		linewidth = 1000;
+		lastcap = Round_Cap;
+		fprintf(f, "1000 setlinewidth 1 setlinecap 1 setlinejoin\n");
+		fprintf(f, "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
+						x1 + 500 - bloat, y1 + 500 - bloat,
+						x1 + 500 - bloat, y2 - 500 + bloat, x2 - 500 + bloat, y2 - 500 + bloat, x2 - 500 + bloat, y1 + 500 - bloat);
+		return;
+	}
+#endif
+	pcb_fprintf(global.f, "%mi %mi %mi %mi r\n", x1 - global.bloat, y1 - global.bloat, x2 + global.bloat, y2 + global.bloat);
+}
+
+HID_Attribute ps_calib_attribute_list[] = {
+	{"lprcommand", "Command to print",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+};
+
+static const char *const calib_lines[] = {
+	"%!PS-Adobe-3.0\n",
+	"%%Title: Calibration Page\n",
+	"%%PageOrder: Ascend\n",
+	"%%Pages: 1\n",
+	"%%EndComments\n",
+	"\n",
+	"%%Page: Calibrate 1\n",
+	"72 72 scale\n",
+	"\n",
+	"0 setlinewidth\n",
+	"0.375 0.375 moveto\n",
+	"8.125 0.375 lineto\n",
+	"8.125 10.625 lineto\n",
+	"0.375 10.625 lineto\n",
+	"closepath stroke\n",
+	"\n",
+	"0.5 0.5 translate\n",
+	"0.001 setlinewidth\n",
+	"\n",
+	"/Times-Roman findfont 0.2 scalefont setfont\n",
+	"\n",
+	"/sign {\n",
+	"    0 lt { -1 } { 1 } ifelse\n",
+	"} def\n",
+	"\n",
+	"/cbar {\n",
+	"    /units exch def\n",
+	"    /x exch def\n",
+	"    /y exch def  \n",
+	"\n",
+	"    /x x sign 0.5 mul def\n",
+	"\n",
+	"    0 setlinewidth\n",
+	"    newpath x y 0.25 0 180 arc gsave 0.85 setgray fill grestore closepath stroke\n",
+	"    newpath x 0 0.25 180 360 arc gsave 0.85 setgray fill grestore closepath stroke\n",
+	"    0.001 setlinewidth\n",
+	"\n",
+	"    x 0 moveto\n",
+	"    x y lineto\n",
+	"%    -0.07 -0.2 rlineto 0.14 0 rmoveto -0.07 0.2 rlineto\n",
+	"    x y lineto\n",
+	"    -0.1 0 rlineto 0.2 0 rlineto\n",
+	"    stroke\n",
+	"    x 0 moveto\n",
+	"%    -0.07 0.2 rlineto 0.14 0 rmoveto -0.07 -0.2 rlineto\n",
+	"    x 0 moveto\n",
+	"    -0.1 0 rlineto 0.2 0 rlineto\n",
+	"     stroke\n",
+	"\n",
+	"    x 0.1 add\n",
+	"    y 0.2 sub moveto\n",
+	"    units show\n",
+	"} bind def\n",
+	"\n",
+	"/y 9 def\n",
+	"/t {\n",
+	"    /str exch def\n",
+	"    1.5 y moveto str show\n",
+	"    /y y 0.25 sub def\n",
+	"} bind def\n",
+	"\n",
+	"(Please measure ONE of the horizontal lines, in the units indicated for)t\n",
+	"(that line, and enter that value as X.  Similarly, measure ONE of the)t\n",
+	"(vertical lines and enter that value as Y.  Measurements should be)t\n",
+	"(between the flat faces of the semicircles.)t\n",
+	"()t\n",
+	"(The large box is 10.25 by 7.75 inches)t\n",
+	"\n",
+	"/in { } bind def\n",
+	"/cm { 2.54 div } bind def\n",
+	"/mm { 25.4 div } bind def\n",
+	"\n",
+	0
+};
+
+static int guess(double val, double close_to, double *calib)
+{
+	if (val >= close_to * 0.9 && val <= close_to * 1.1) {
+		*calib = close_to / val;
+		return 0;
+	}
+	return 1;
+}
+
+void ps_calibrate_1(double xval, double yval, int use_command)
+{
+	HID_Attr_Val vals[3];
+	FILE *ps_cal_file;
+	int used_popen = 0, c;
+
+	if (xval > 0 && yval > 0) {
+		if (guess(xval, 4, &global.calibration_x))
+			if (guess(xval, 15, &global.calibration_x))
+				if (guess(xval, 7.5, &global.calibration_x)) {
+					if (xval < 2)
+						ps_attribute_list[HA_xcalib].default_val.real_value = global.calibration_x = xval;
+					else
+						Message(PCB_MSG_DEFAULT, "X value of %g is too far off.\n" "Expecting it near: 1.0, 4.0, 15.0, 7.5\n", xval);
+				}
+		if (guess(yval, 4, &global.calibration_y))
+			if (guess(yval, 20, &global.calibration_y))
+				if (guess(yval, 10, &global.calibration_y)) {
+					if (yval < 2)
+						ps_attribute_list[HA_ycalib].default_val.real_value = global.calibration_y = yval;
+					else
+						Message(PCB_MSG_DEFAULT, "Y value of %g is too far off.\n" "Expecting it near: 1.0, 4.0, 20.0, 10.0\n", yval);
+				}
+		return;
+	}
+
+	if (ps_calib_attribute_list[0].default_val.str_value == NULL) {
+		ps_calib_attribute_list[0].default_val.str_value = pcb_strdup("lpr");
+	}
+
+	if (gui->
+			attribute_dialog(ps_calib_attribute_list, 1, vals, _("Print Calibration Page"),
+											 _("Generates a printer calibration page")))
+		return;
+
+	if (use_command || strchr(vals[0].str_value, '|')) {
+		const char *cmd = vals[0].str_value;
+		while (*cmd == ' ' || *cmd == '|')
+			cmd++;
+		ps_cal_file = popen(cmd, "w");
+		used_popen = 1;
+	}
+	else
+		ps_cal_file = fopen(vals[0].str_value, "w");
+
+	for (c = 0; calib_lines[c]; c++)
+		fputs(calib_lines[c], ps_cal_file);
+
+	fprintf(ps_cal_file, "4 in 0.5 (Y in) cbar\n");
+	fprintf(ps_cal_file, "20 cm 1.5 (Y cm) cbar\n");
+	fprintf(ps_cal_file, "10 in 2.5 (Y in) cbar\n");
+	fprintf(ps_cal_file, "-90 rotate\n");
+	fprintf(ps_cal_file, "4 in -0.5 (X in) cbar\n");
+	fprintf(ps_cal_file, "15 cm -1.5 (X cm) cbar\n");
+	fprintf(ps_cal_file, "7.5 in -2.5 (X in) cbar\n");
+
+	fprintf(ps_cal_file, "showpage\n");
+
+	fprintf(ps_cal_file, "%%%%EOF\n");
+
+	if (used_popen)
+		pclose(ps_cal_file);
+	else
+		fclose(ps_cal_file);
+}
+
+static void ps_calibrate(double xval, double yval)
+{
+	ps_calibrate_1(xval, yval, 0);
+}
+
+static void ps_set_crosshair(int x, int y, int action)
+{
+}
+
+static int ActionPSCalib(int argc, const char **argv, Coord x, Coord y)
+{
+	ps_calibrate(0.0, 0.0);
+	return 0;
+}
+
+HID_Action hidps_action_list[] = {
+	{"pscalib", 0, ActionPSCalib}
+};
+
+REGISTER_ACTIONS(hidps_action_list, ps_cookie)
+
+
+#include "dolists.h"
+
+HID ps_hid;
+static int ps_inited = 0;
+void ps_ps_init(HID * hid)
+{
+	if (ps_inited)
+		return;
+
+	hid->get_export_options = ps_get_export_options;
+	hid->do_export = ps_do_export;
+	hid->parse_arguments = ps_parse_arguments;
+	hid->set_layer = ps_set_layer;
+	hid->make_gc = ps_make_gc;
+	hid->destroy_gc = ps_destroy_gc;
+	hid->use_mask = ps_use_mask;
+	hid->set_color = ps_set_color;
+	hid->set_line_cap = ps_set_line_cap;
+	hid->set_line_width = ps_set_line_width;
+	hid->set_draw_xor = ps_set_draw_xor;
+	hid->set_draw_faded = ps_set_draw_faded;
+	hid->draw_line = ps_draw_line;
+	hid->draw_arc = ps_draw_arc;
+	hid->draw_rect = ps_draw_rect;
+	hid->fill_circle = ps_fill_circle;
+	hid->fill_polygon = ps_fill_polygon;
+	hid->fill_pcb_polygon = ps_fill_pcb_polygon;
+	hid->fill_rect = ps_fill_rect;
+	hid->calibrate = ps_calibrate;
+	hid->set_crosshair = ps_set_crosshair;
+
+	REGISTER_ACTIONS(hidps_action_list, ps_cookie)
+
+	ps_inited = 1;
+}
+
+static int ps_usage(const char *topic)
+{
+	fprintf(stderr, "\nps exporter command line arguments:\n\n");
+	hid_usage(ps_attribute_list, sizeof(ps_attribute_list) / sizeof(ps_attribute_list[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x ps foo.pcb [ps options]\n\n");
+	return 0;
+}
+
+static void plugin_ps_uninit(void)
+{
+	hid_remove_actions_by_cookie(ps_cookie);
+	ps_inited = 0;
+}
+
+
+pcb_uninit_t hid_export_ps_init()
+{
+	memset(&ps_hid, 0, sizeof(HID));
+
+	common_nogui_init(&ps_hid);
+	common_draw_helpers_init(&ps_hid);
+	ps_ps_init(&ps_hid);
+
+	ps_hid.struct_size = sizeof(HID);
+	ps_hid.name = "ps";
+	ps_hid.description = "Postscript export";
+	ps_hid.exporter = 1;
+	ps_hid.poly_before = 1;
+
+	ps_hid.usage = ps_usage;
+
+	hid_register_hid(&ps_hid);
+
+	hid_eps_init();
+	return plugin_ps_uninit;
+}
diff --git a/src_plugins/export_ps/ps.h b/src_plugins/export_ps/ps.h
new file mode 100644
index 0000000..424021a
--- /dev/null
+++ b/src_plugins/export_ps/ps.h
@@ -0,0 +1,7 @@
+extern const char *ps_cookie;
+extern HID ps_hid;
+extern void ps_hid_export_to_file(FILE *, HID_Attr_Val *);
+extern void ps_start_file(FILE *);
+extern void ps_calibrate_1(double, double, int);
+extern void hid_eps_init();
+void ps_ps_init(HID * hid);
diff --git a/src_plugins/export_svg/Makefile b/src_plugins/export_svg/Makefile
new file mode 100644
index 0000000..90a3a2d
--- /dev/null
+++ b/src_plugins/export_svg/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_svg
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_svg/Plug.tmpasm b/src_plugins/export_svg/Plug.tmpasm
new file mode 100644
index 0000000..5972c1f
--- /dev/null
+++ b/src_plugins/export_svg/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_svg}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_svg/svg.o @]
+
+switch /local/pcb/export_svg/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_svg/README b/src_plugins/export_svg/README
new file mode 100644
index 0000000..69d53e7
--- /dev/null
+++ b/src_plugins/export_svg/README
@@ -0,0 +1,5 @@
+Scalable Vector Graphics (SVG) exporter
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_svg/svg.c b/src_plugins/export_svg/svg.c
new file mode 100644
index 0000000..56ce219
--- /dev/null
+++ b/src_plugins/export_svg/svg.c
@@ -0,0 +1,735 @@
+ /*
+  *                            COPYRIGHT
+  *
+  *  PCB, interactive printed circuit board design
+  *  Copyright (C) 2006 Dan McMahill
+  *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+  *
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+  *  the Free Software Foundation; either version 2 of the License, or
+  *  (at your option) any later version.
+  *
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+  *
+  *  You should have received a copy of the GNU General Public License
+  *  along with this program; if not, write to the Free Software
+  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  *
+  */
+
+/*
+ *  Based on the png exporter by Dan McMahill
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <genvector/gds_char.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "layer.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "plugins.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_color.h"
+#include "hid_helper.h"
+#include "hid_flags.h"
+
+
+static HID svg_hid;
+
+const char *svg_cookie = "svg HID";
+
+
+typedef struct hid_gc_struct {
+	HID *me_pointer;
+	EndCapStyle cap;
+	int width;
+	char *color;
+	int erase, drill;
+} hid_gc_struct;
+
+static const char *CAPS(EndCapStyle cap)
+{
+	switch (cap) {
+		case Trace_Cap:
+		case Round_Cap:
+			return "round";
+		case Square_Cap:
+			return "square";
+		case Beveled_Cap:
+			return "butt";
+	}
+	return "";
+}
+
+static FILE *f = NULL;
+static int group_open = 0;
+static int opacity = 100, drawing_mask, drawing_hole, photo_mode, flip;
+
+gds_t sbright, sdark, snormal;
+
+/* Photo mode colors and hacks */
+const char *board_color = "#464646";
+const char *mask_color = "#00ff00";
+float mask_opacity_factor = 0.5;
+
+enum {
+	PHOTO_MASK,
+	PHOTO_SILK,
+	PHOTO_COPPER,
+	PHOTO_INNER
+} photo_color;
+
+struct {
+	const char *bright;
+	const char *normal;
+	const char *dark;
+	Coord offs;
+} photo_palette[] = {
+	/* MASK */   { "#00ff00", "#00ff00", "#00ff00", PCB_MM_TO_COORD(0) },
+	/* SILK */   { "#ffffff", "#eeeeee", "#aaaaaa", PCB_MM_TO_COORD(0) },
+	/* COPPER */ { "#bbbbbb", "#707090", "#555555", PCB_MM_TO_COORD(0.05) },
+	/* INNER */  { "#222222", "#111111", "#000000", PCB_MM_TO_COORD(0.05) }
+};
+
+HID_Attribute svg_attribute_list[] = {
+	/* other HIDs expect this to be first.  */
+
+/* %start-doc options "93 SVG Options"
+ at ftable @code
+ at item --outfile <string>
+Name of the file to be exported to. Can contain a path.
+ at end ftable
+%end-doc
+*/
+	{"outfile", "Graphics output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_svgfile 0
+
+/* %start-doc options "93 SVG Options"
+ at ftable @code
+ at cindex photo-mode
+ at item --photo-mode
+Export a photo realistic image of the layout.
+ at end ftable
+%end-doc
+*/
+	{"photo-mode", "Photo-realistic export mode",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_photo_mode 1
+
+/* %start-doc options "93 SVG Options"
+ at ftable @code
+ at cindex opacity
+ at item --opacity
+Layer opacity
+ at end ftable
+%end-doc
+*/
+	{"opacity", "Layer opacity",
+	 HID_Integer, 0, 100, {100, 0, 0}, 0, 0},
+#define HA_opacity 2
+
+/* %start-doc options "93 SVG Options"
+ at ftable @code
+ at cindex flip
+ at item --flip
+Flip board, look at it from the bottom side
+ at end ftable
+%end-doc
+*/
+	{"flip", "Flip board",
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}
+#define HA_flip 3
+};
+
+#define NUM_OPTIONS (sizeof(svg_attribute_list)/sizeof(svg_attribute_list[0]))
+
+#define TRX(x)
+#define TRY(y) \
+do { \
+	if (flip) \
+		y = PCB->MaxHeight - y; \
+} while(0)
+
+
+REGISTER_ATTRIBUTES(svg_attribute_list, svg_cookie)
+
+static HID_Attr_Val svg_values[NUM_OPTIONS];
+
+static HID_Attribute *svg_get_export_options(int *n)
+{
+	static char *last_made_filename = 0;
+	const char *suffix = ".svg";
+
+	if (PCB)
+		derive_default_filename(PCB->Filename, &svg_attribute_list[HA_svgfile], suffix, &last_made_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return svg_attribute_list;
+}
+
+void svg_hid_export_to_file(FILE * the_file, HID_Attr_Val * options)
+{
+	static int saved_layer_stack[MAX_LAYER];
+	BoxType region;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	f = the_file;
+
+	memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack));
+
+	{
+		conf_force_set_bool(conf_core.editor.thin_draw, 0);
+		conf_force_set_bool(conf_core.editor.thin_draw_poly, 0);
+/*		conf_force_set_bool(conf_core.editor.check_planes, 0);*/
+		conf_force_set_bool(conf_core.editor.show_solder_side, 0);
+		conf_force_set_bool(conf_core.editor.show_mask, 0);
+
+		if (options[HA_photo_mode].int_value) {
+			photo_mode = 1;
+			conf_force_set_bool(conf_core.editor.show_mask, 1);
+		}
+		else
+			photo_mode = 0;
+
+		if (options[HA_flip].int_value) {
+			flip = 1;
+			conf_force_set_bool(conf_core.editor.show_solder_side, 1);
+		}
+		else
+			flip = 0;
+	}
+
+	if (photo_mode) {
+		pcb_fprintf(f, "<rect x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+			0, 0, PCB->MaxWidth, PCB->MaxHeight, board_color);
+	}
+
+	opacity = options[HA_opacity].int_value;
+
+	gds_init(&sbright);
+	gds_init(&sdark);
+	gds_init(&snormal);
+	hid_expose_callback(&svg_hid, &region, 0);
+
+	conf_update(NULL); /* restore forced sets */
+}
+
+static void group_close()
+{
+	if (group_open == 1) {
+		if (gds_len(&sdark) > 0) {
+			fprintf(f, "<!--dark-->\n");
+			fprintf(f, "%s", sdark.array);
+			gds_truncate(&sdark, 0);
+		}
+
+		if (gds_len(&sbright) > 0) {
+			fprintf(f, "<!--bright-->\n");
+			fprintf(f, "%s", sbright.array);
+			gds_truncate(&sbright, 0);
+		}
+
+		if (gds_len(&snormal) > 0) {
+			fprintf(f, "<!--normal-->\n");
+			fprintf(f, "%s", snormal.array);
+			gds_truncate(&snormal, 0);
+		}
+
+	}
+	fprintf(f, "</g>\n");
+}
+
+static void svg_do_export(HID_Attr_Val * options)
+{
+	const char *filename;
+	int save_ons[MAX_LAYER + 2];
+	int i;
+	Coord w, h, x1, y1, x2, y2;
+
+	if (!options) {
+		svg_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			svg_values[i] = svg_attribute_list[i].default_val;
+		options = svg_values;
+	}
+
+	filename = options[HA_svgfile].str_value;
+	if (!filename)
+		filename = "pcb.svg";
+
+	f = fopen(filename, "wb");
+	if (!f) {
+		perror(filename);
+		return;
+	}
+
+	fprintf(f, "<?xml version=\"1.0\"?>\n");
+	w = PCB->MaxWidth;
+	h = PCB->MaxHeight;
+	while((w < PCB_MM_TO_COORD(1024)) && (h < PCB_MM_TO_COORD(1024))) {
+		w *= 2;
+		h *= 2;
+	}
+
+	x1 = PCB_MM_TO_COORD(2);
+	y1 = PCB_MM_TO_COORD(2);
+	x2 = PCB->MaxWidth;
+	y2 = PCB->MaxHeight;
+	x2 += PCB_MM_TO_COORD(5);
+	y2 += PCB_MM_TO_COORD(5);
+	pcb_fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.0\" width=\"%mm\" height=\"%mm\" viewBox=\"-%mm -%mm %mm %mm\">\n", w, h, x1, y1, x2, y2);
+
+	hid_save_and_show_layer_ons(save_ons);
+
+	svg_hid_export_to_file(f, options);
+
+	hid_restore_layer_ons(save_ons);
+
+	while(group_open) {
+		group_close();
+		group_open--;
+	}
+
+	fprintf(f, "</svg>\n");
+	fclose(f);
+	f = NULL;
+}
+
+static void svg_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(svg_attribute_list, sizeof(svg_attribute_list) / sizeof(svg_attribute_list[0]), svg_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+static int svg_set_layer(const char *name, int group, int empty)
+{
+	int opa, our_mask, our_silk;
+	unsigned int our_side;
+
+	if (flip) {
+		our_mask = SL(MASK, BOTTOM);
+		our_silk = SL(SILK, BOTTOM);
+		our_side = PCB_LYT_BOTTOM;
+	}
+	else {
+		our_mask = SL(MASK, TOP);
+		our_silk = SL(SILK, TOP);
+		our_side = PCB_LYT_TOP;
+	}
+
+	/* don't draw the mask if we are not in the photo mode */
+	if (!photo_mode && ((group == SL(MASK, TOP)) || (group == SL(MASK, BOTTOM))))
+		return 0;
+
+	if ((group < 0) && (group != our_silk) && (group != our_mask) && (group != SL(UDRILL, 0)) && (group != SL(PDRILL, 0)))
+		return 0;
+	while(group_open) {
+		group_close();
+		group_open--;
+	}
+	if (name == NULL)
+		name = "copper";
+	fprintf(f, "<g id=\"layer_%d_%s\"", group, name);
+	drawing_mask = (group == our_mask);
+	opa = opacity;
+	if (drawing_mask)
+		opa *= mask_opacity_factor;
+	if (opa != 100)
+		fprintf(f, " opacity=\"%.2f\"", ((float)opa) / 100.0);
+	fprintf(f, ">\n");
+	group_open = 1;
+
+	if (photo_mode) {
+		if (group == our_silk)
+			photo_color = PHOTO_SILK;
+		if (group == our_mask)
+			photo_color = PHOTO_MASK;
+		else if (group >= 0) {
+			int ly = PCB->LayerGroups.Entries[group][0];
+			unsigned int fl;
+			fl = pcb_layer_flags(ly) & PCB_LYT_ANYWHERE;
+			if (fl == our_side)
+				photo_color = PHOTO_COPPER;
+			else
+				photo_color = PHOTO_INNER;
+		}
+	}
+
+	drawing_hole = (group == SL(UDRILL, 0)) || (group == SL(PDRILL, 0));
+
+	return 1;
+}
+
+
+static hidGC svg_make_gc(void)
+{
+	hidGC rv = (hidGC) malloc(sizeof(hid_gc_struct));
+	rv->me_pointer = &svg_hid;
+	rv->cap = Trace_Cap;
+	rv->width = 1;
+	rv->color = NULL;
+	return rv;
+}
+
+static void svg_destroy_gc(hidGC gc)
+{
+	free(gc);
+}
+
+static void svg_use_mask(int use_it)
+{
+	if (use_it == HID_MASK_CLEAR) {
+		return;
+	}
+	if (use_it) {
+	}
+}
+
+static void svg_set_color(hidGC gc, const char *name)
+{
+	gc->drill = gc->erase = 0;
+	if (name == NULL)
+		name = "#ff0000";
+	if (strcmp(name, "drill") == 0) {
+		name = "#ffffff";
+		gc->drill = 1;
+	}
+	else if (strcmp(name, "erase") == 0) {
+		name = "#ffffff";
+		gc->erase = 1;
+	}
+	else if (drawing_mask)
+		name = mask_color;
+	if ((gc->color != NULL) && (strcmp(gc->color, name) == 0))
+		return;
+	free(gc->color);
+	gc->color = pcb_strdup(name);
+}
+
+static void svg_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void svg_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void indent(gds_t *s)
+{
+	static char ind[] = "                                                                              ";
+	if (group_open < sizeof(ind)-1) {
+		ind[group_open] = '\0';
+		if (s == NULL)
+			pcb_fprintf(f, ind);
+		else
+			pcb_append_printf(s, ind);
+		ind[group_open] = ' ';
+		return;
+	}
+
+	if (s == NULL)
+		pcb_fprintf(f, ind);
+	else
+		pcb_append_printf(s, ind);
+}
+
+static void svg_set_draw_xor(hidGC gc, int xor_)
+{
+	;
+}
+
+#define fix_rect_coords() \
+	if (x1 > x2) {\
+		Coord t = x1; \
+		x1 = x2; \
+		x2 = t; \
+	} \
+	if (y1 > y2) { \
+		Coord t = y1; \
+		y1 = y2; \
+		y2 = t; \
+	}
+
+static void draw_rect(hidGC gc, Coord x1, Coord y1, Coord w, Coord h, Coord stroke)
+{
+	indent(&snormal);
+	pcb_append_printf(&snormal, "<rect x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\" fill=\"none\"/>\n",
+		x1, y1, w, h, stroke, gc->color, CAPS(gc->cap));
+}
+
+static void svg_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	fix_rect_coords();
+	draw_rect(gc, x1, y1, x2-x1, y2-y1, gc->width);
+}
+
+static void draw_fill_rect(hidGC gc, Coord x1, Coord y1, Coord w, Coord h)
+{
+	if ((photo_mode) && (!gc->erase)) {
+		Coord photo_offs = photo_palette[photo_color].offs;
+		if (photo_offs != 0) {
+			indent(&sdark);
+			pcb_append_printf(&sdark, "<rect x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+				x1+photo_offs, y1+photo_offs, w, h, photo_palette[photo_color].dark);
+			indent(&sbright);
+			pcb_append_printf(&sbright, "<rect x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+				x1-photo_offs, y1-photo_offs, w, h, photo_palette[photo_color].bright);
+		}
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<rect x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+			x1, y1, w, h, photo_palette[photo_color].normal);
+	}
+	else {
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<rect x=\"%mm\" y=\"%mm\" width=\"%mm\" height=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+			x1, y1, w, h, gc->color);
+	}
+}
+
+static void svg_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	TRX(x1); TRY(y1); TRX(x2); TRY(y2);
+	fix_rect_coords();
+	draw_fill_rect(gc, x1, y1, x2-x1, y2-y1);
+}
+
+static void draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	if ((photo_mode) && (!gc->erase)) {
+		Coord photo_offs = photo_palette[photo_color].offs;
+		if (photo_offs != 0) {
+			indent(&sbright);
+			pcb_append_printf(&sbright, "<line x1=\"%mm\" y1=\"%mm\" x2=\"%mm\" y2=\"%mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\"/>\n",
+				x1-photo_offs, y1-photo_offs, x2-photo_offs, y2-photo_offs, gc->width, photo_palette[photo_color].bright, CAPS(gc->cap));
+			indent(&sdark);
+			pcb_append_printf(&sdark, "<line x1=\"%mm\" y1=\"%mm\" x2=\"%mm\" y2=\"%mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\"/>\n",
+				x1+photo_offs, y1+photo_offs, x2+photo_offs, y2+photo_offs, gc->width, photo_palette[photo_color].dark, CAPS(gc->cap));
+		}
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<line x1=\"%mm\" y1=\"%mm\" x2=\"%mm\" y2=\"%mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\"/>\n",
+			x1, y1, x2, y2, gc->width, photo_palette[photo_color].normal, CAPS(gc->cap));
+	}
+	else {
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<line x1=\"%mm\" y1=\"%mm\" x2=\"%mm\" y2=\"%mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\"/>\n",
+			x1, y1, x2, y2, gc->width, gc->color, CAPS(gc->cap));
+	}
+}
+
+static void svg_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	TRX(x1); TRY(y1); TRX(x2); TRY(y2);
+	draw_line(gc, x1, y1, x2, y2);
+}
+
+static void draw_arc(hidGC gc, Coord x1, Coord y1, Coord r, Coord x2, Coord y2, Coord stroke)
+{
+	if ((photo_mode) && (!gc->erase)) {
+		Coord photo_offs = photo_palette[photo_color].offs;
+		if (photo_offs != 0) {
+			indent(&sbright);
+			pcb_append_printf(&sbright, "<path d=\"M %mm %mm A %mm %mm 0 0 0 %mm %mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\" fill=\"none\"/>\n",
+				x1-photo_offs, y1-photo_offs, r, r, x2-photo_offs, y2-photo_offs, gc->width, photo_palette[photo_color].bright, CAPS(gc->cap));
+			indent(&sdark);
+			pcb_append_printf(&sdark, "<path d=\"M %mm %mm A %mm %mm 0 0 0 %mm %mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\" fill=\"none\"/>\n",
+				x1+photo_offs, y1+photo_offs, r, r, x2+photo_offs, y2+photo_offs, gc->width, photo_palette[photo_color].dark, CAPS(gc->cap));
+		}
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<path d=\"M %mm %mm A %mm %mm 0 0 0 %mm %mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\" fill=\"none\"/>\n",
+			x1, y1, r, r, x2, y2, gc->width, photo_palette[photo_color].normal, CAPS(gc->cap));
+	}
+	else {
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<path d=\"M %mm %mm A %mm %mm 0 0 0 %mm %mm\" stroke-width=\"%mm\" stroke=\"%s\" stroke-linecap=\"%s\" fill=\"none\"/>\n",
+			x1, y1, r, r, x2, y2, gc->width, gc->color, CAPS(gc->cap));
+	}
+}
+
+static void svg_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	Coord x1, y1, x2, y2;
+	Angle sa, ea;
+
+	TRX(cx); TRY(cy);
+
+	/* calculate start and end angles considering flip */
+	start_angle = 180 - start_angle;
+	delta_angle = -delta_angle;
+	if (flip) {
+		start_angle = -start_angle;
+		delta_angle = -delta_angle;
+	}
+	if (delta_angle > 0) {
+		sa = start_angle;
+		ea = start_angle + delta_angle;
+	}
+	else {
+		sa = start_angle + delta_angle;
+		ea = start_angle;
+	}
+
+	/* calculate the endpoints */
+	x2 = cx + (width * cos(sa * M_PI / 180));
+	y2 = cy + (width * sin(sa * M_PI / 180));
+	x1 = cx + (width * cos(ea * M_PI / 180));
+	y1 = cy + (width * sin(ea * M_PI / 180));
+
+	draw_arc(gc, x1, y1, width, x2, y2, gc->width);
+}
+
+static void draw_fill_circle(hidGC gc, Coord cx, Coord cy, Coord r, Coord stroke)
+{
+	if ((photo_mode) && (!gc->erase)) {
+		if (!drawing_hole) {
+			Coord photo_offs = photo_palette[photo_color].offs;
+			if ((!gc->drill) && (photo_offs != 0)) {
+				indent(&sbright);
+				pcb_append_printf(&sbright, "<circle cx=\"%mm\" cy=\"%mm\" r=\"%mm\" stroke-width=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+					cx-photo_offs, cy-photo_offs, r, stroke, photo_palette[photo_color].bright);
+
+				indent(&sdark);
+				pcb_append_printf(&sdark, "<circle cx=\"%mm\" cy=\"%mm\" r=\"%mm\" stroke-width=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+					cx+photo_offs, cy+photo_offs, r, stroke, photo_palette[photo_color].dark);
+			}
+			indent(&snormal);
+			pcb_append_printf(&snormal, "<circle cx=\"%mm\" cy=\"%mm\" r=\"%mm\" stroke-width=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+				cx, cy, r, stroke, photo_palette[photo_color].normal);
+		}
+		else {
+			indent(&snormal);
+			pcb_append_printf(&snormal, "<circle cx=\"%mm\" cy=\"%mm\" r=\"%mm\" stroke-width=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+				cx, cy, r, stroke, "#000000");
+		}
+	}
+	else{
+		indent(&snormal);
+		pcb_append_printf(&snormal, "<circle cx=\"%mm\" cy=\"%mm\" r=\"%mm\" stroke-width=\"%mm\" fill=\"%s\" stroke=\"none\"/>\n",
+			cx, cy, r, stroke, gc->color);
+	}
+}
+
+static void svg_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	TRX(cx); TRY(cy);
+	draw_fill_circle(gc, cx, cy, radius, gc->width);
+}
+
+static void draw_poly(gds_t *s, hidGC gc, int n_coords, Coord * x, Coord * y, Coord offs, const char *clr)
+{
+	int i;
+	float poly_bloat = 0.075;
+
+	indent(s);
+	gds_append_str(s, "<polygon points=\"");
+	for (i = 0; i < n_coords; i++) {
+		Coord px = x[i], py = y[i];
+		TRX(px); TRY(py);
+		pcb_append_printf(s, "%mm,%mm ", px+offs, py+offs);
+	}
+	pcb_append_printf(s, "\" stroke-width=\"%.3f\" stroke=\"%s\" fill=\"%s\"/>\n", poly_bloat, clr, clr);
+}
+
+static void svg_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	if ((photo_mode) && (!gc->erase)) {
+		Coord photo_offs = photo_palette[photo_color].offs;
+		if (photo_offs != 0) {
+			draw_poly(&sbright, gc, n_coords, x, y, -photo_offs, photo_palette[photo_color].bright);
+			draw_poly(&sdark, gc, n_coords, x, y, +photo_offs, photo_palette[photo_color].dark);
+		}
+		draw_poly(&snormal, gc, n_coords, x, y, 0, photo_palette[photo_color].normal);
+	}
+	else
+		draw_poly(&snormal, gc, n_coords, x, y, 0, gc->color);
+}
+
+static void svg_calibrate(double xval, double yval)
+{
+	Message(PCB_MSG_ERROR, "svg_calibrate() not implemented");
+	return;
+}
+
+static void svg_set_crosshair(int x, int y, int a)
+{
+}
+
+static int svg_usage(const char *topic)
+{
+	fprintf(stderr, "\nsvg exporter command line arguments:\n\n");
+	hid_usage(svg_attribute_list, sizeof(svg_attribute_list) / sizeof(svg_attribute_list[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x svg foo.pcb [svg options]\n\n");
+	return 0;
+}
+
+#include "dolists.h"
+
+pcb_uninit_t hid_export_svg_init()
+{
+	memset(&svg_hid, 0, sizeof(HID));
+
+	common_nogui_init(&svg_hid);
+	common_draw_helpers_init(&svg_hid);
+
+	svg_hid.struct_size = sizeof(HID);
+	svg_hid.name = "svg";
+	svg_hid.description = "Scalable Vector Graphics export";
+	svg_hid.exporter = 1;
+	svg_hid.poly_before = 1;
+	svg_hid.holes_after = 1;
+
+	svg_hid.get_export_options = svg_get_export_options;
+	svg_hid.do_export = svg_do_export;
+	svg_hid.parse_arguments = svg_parse_arguments;
+	svg_hid.set_layer = svg_set_layer;
+	svg_hid.make_gc = svg_make_gc;
+	svg_hid.destroy_gc = svg_destroy_gc;
+	svg_hid.use_mask = svg_use_mask;
+	svg_hid.set_color = svg_set_color;
+	svg_hid.set_line_cap = svg_set_line_cap;
+	svg_hid.set_line_width = svg_set_line_width;
+	svg_hid.set_draw_xor = svg_set_draw_xor;
+	svg_hid.draw_line = svg_draw_line;
+	svg_hid.draw_arc = svg_draw_arc;
+	svg_hid.draw_rect = svg_draw_rect;
+	svg_hid.fill_circle = svg_fill_circle;
+	svg_hid.fill_polygon = svg_fill_polygon;
+	svg_hid.fill_rect = svg_fill_rect;
+	svg_hid.calibrate = svg_calibrate;
+	svg_hid.set_crosshair = svg_set_crosshair;
+
+	svg_hid.usage = svg_usage;
+
+	hid_register_hid(&svg_hid);
+
+	return NULL;
+}
diff --git a/src_plugins/export_test/Makefile b/src_plugins/export_test/Makefile
new file mode 100644
index 0000000..2213a7f
--- /dev/null
+++ b/src_plugins/export_test/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_export_test
+	
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_test/Plug.tmpasm b/src_plugins/export_test/Plug.tmpasm
new file mode 100644
index 0000000..a5edcdc
--- /dev/null
+++ b/src_plugins/export_test/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {export_test}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_test/export_test.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/export_test/export_test_conf.h}
+
+switch /local/pcb/export_test/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_test/README b/src_plugins/export_test/README
new file mode 100644
index 0000000..496f431
--- /dev/null
+++ b/src_plugins/export_test/README
@@ -0,0 +1,6 @@
+A thin layer of code to dump exporter calls for testing the HID exporter API.
+
+#state: disabled
+#lstate: work in progress
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_test/export_test.c b/src_plugins/export_test/export_test.c
new file mode 100644
index 0000000..c4d65cd
--- /dev/null
+++ b/src_plugins/export_test/export_test.c
@@ -0,0 +1,352 @@
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_init.h"
+
+const char *export_test_cookie = "export_test HID";
+
+static HID_Attribute export_test_options[] = {
+/* %start-doc options "8 export_test Creation"
+ at ftable @code
+ at item --export_testfile <string>
+Name of the export_test output file. Use stdout if not specified.
+ at end ftable
+%end-doc
+*/
+	{"export_testfile", "Name of the export_test output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_export_testfile 0
+};
+
+#define NUM_OPTIONS (sizeof(export_test_options)/sizeof(export_test_options[0]))
+
+static HID_Attr_Val export_test_values[NUM_OPTIONS];
+
+static const char *export_test_filename;
+
+static HID_Attribute *export_test_get_export_options(int *n)
+{
+	static char *last_export_test_filename = 0;
+
+	if (PCB) {
+		derive_default_filename(PCB->Filename, &export_test_options[HA_export_testfile], ".export_test", &last_export_test_filename);
+	}
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return export_test_options;
+}
+
+
+/* 
+ * If fp is not NULL then print out the bill of materials contained in
+ * export_test.  Either way, free all memory which has been allocated for export_test.
+ */
+static void print_and_free(FILE * fp, /*export_testList*/ void * export_test)
+{
+#if 0
+	export_testList *lastb;
+	StringList *lasts;
+	char *descr, *value;
+
+	while (export_test != NULL) {
+		if (fp) {
+			descr = Cleanexport_testString(export_test->descr);
+			value = Cleanexport_testString(export_test->value);
+			fprintf(fp, "%d,\"%s\",\"%s\",", export_test->num, descr, value);
+			free(descr);
+			free(value);
+		}
+
+		while (export_test->refdes != NULL) {
+			if (fp) {
+				fprintf(fp, "%s ", export_test->refdes->str);
+			}
+			free(export_test->refdes->str);
+			lasts = export_test->refdes;
+			export_test->refdes = export_test->refdes->next;
+			free(lasts);
+		}
+		if (fp) {
+			fprintf(fp, "\n");
+		}
+		lastb = export_test;
+		export_test = export_test->next;
+		free(lastb);
+	}
+#endif
+}
+
+static int Printexport_test(void)
+{
+#if 0
+	char utcTime[64];
+	Coord x, y;
+	double theta = 0.0;
+	double sumx, sumy;
+	double pin1x = 0.0, pin1y = 0.0, pin1angle = 0.0;
+	double pin2x = 0.0, pin2y = 0.0;
+	int found_pin1;
+	int found_pin2;
+	int pin_cnt;
+	time_t currenttime;
+	FILE *fp;
+	export_testList *export_test = NULL;
+	char *name, *descr, *value;
+
+
+	fp = fopen(xy_filename, "w");
+	if (!fp) {
+		gui->log("Cannot open file %s for writing\n", xy_filename);
+		return 1;
+	}
+
+	/* Create a portable timestamp. */
+	currenttime = time(NULL);
+	{
+		/* avoid gcc complaints */
+		const char *fmt = "%c UTC";
+		strftime(utcTime, sizeof(utcTime), fmt, gmtime(&currenttime));
+	}
+	fprintf(fp, "# $Id");
+	fprintf(fp, "$\n");
+	fprintf(fp, "# PcbXY Version 1.0\n");
+	fprintf(fp, "# Date: %s\n", utcTime);
+	fprintf(fp, "# Author: %s\n", pcb_author());
+	fprintf(fp, "# Title: %s - PCB X-Y\n", UNKNOWN(PCB->Name));
+	fprintf(fp, "# RefDes, Description, Value, X, Y, rotation, top/bottom\n");
+	fprintf(fp, "# X,Y in %s.  rotation in degrees.\n", xy_unit->in_suffix);
+	fprintf(fp, "# --------------------------------------------\n");
+
+	/*
+	 * For each element we calculate the centroid of the footprint.
+	 * In addition, we need to extract some notion of rotation.  
+	 * While here generate the export_test list
+	 */
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+
+		/* initialize our pin count and our totals for finding the
+		   centriod */
+		pin_cnt = 0;
+		sumx = 0.0;
+		sumy = 0.0;
+		found_pin1 = 0;
+		found_pin2 = 0;
+
+		/* insert this component into the bill of materials list */
+		export_test = export_test_insert((char *) UNKNOWN(NAMEONPCB_NAME(element)),
+										 (char *) UNKNOWN(DESCRIPTION_NAME(element)), (char *) UNKNOWN(VALUE_NAME(element)), export_test);
+
+
+		/*
+		 * iterate over the pins and pads keeping a running count of how
+		 * many pins/pads total and the sum of x and y coordinates
+		 * 
+		 * While we're at it, store the location of pin/pad #1 and #2 if
+		 * we can find them
+		 */
+
+		PIN_LOOP(element);
+		{
+			sumx += (double) pin->X;
+			sumy += (double) pin->Y;
+			pin_cnt++;
+
+			if (NSTRCMP(pin->Number, "1") == 0) {
+				pin1x = (double) pin->X;
+				pin1y = (double) pin->Y;
+				pin1angle = 0.0;				/* pins have no notion of angle */
+				found_pin1 = 1;
+			}
+			else if (NSTRCMP(pin->Number, "2") == 0) {
+				pin2x = (double) pin->X;
+				pin2y = (double) pin->Y;
+				found_pin2 = 1;
+			}
+		}
+		END_LOOP;
+
+		PAD_LOOP(element);
+		{
+			sumx += (pad->Point1.X + pad->Point2.X) / 2.0;
+			sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0;
+			pin_cnt++;
+
+			if (NSTRCMP(pad->Number, "1") == 0) {
+				pin1x = (double) (pad->Point1.X + pad->Point2.X) / 2.0;
+				pin1y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0;
+				/*
+				 * NOTE:  We swap the Y points because in PCB, the Y-axis
+				 * is inverted.  Increasing Y moves down.  We want to deal
+				 * in the usual increasing Y moves up coordinates though.
+				 */
+				pin1angle = (180.0 / M_PI) * atan2(pad->Point1.Y - pad->Point2.Y, pad->Point2.X - pad->Point1.X);
+				found_pin1 = 1;
+			}
+			else if (NSTRCMP(pad->Number, "2") == 0) {
+				pin2x = (double) (pad->Point1.X + pad->Point2.X) / 2.0;
+				pin2y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0;
+				found_pin2 = 1;
+			}
+
+		}
+		END_LOOP;
+
+		if (pin_cnt > 0) {
+			x = sumx / (double) pin_cnt;
+			y = sumy / (double) pin_cnt;
+
+			if (found_pin1) {
+				/* recenter pin #1 onto the axis which cross at the part
+				   centroid */
+				pin1x -= x;
+				pin1y -= y;
+				pin1y = -1.0 * pin1y;
+
+				/* if only 1 pin, use pin 1's angle */
+				if (pin_cnt == 1)
+					theta = pin1angle;
+				else {
+					/* if pin #1 is at (0,0) use pin #2 for rotation */
+					if ((pin1x == 0.0) && (pin1y == 0.0)) {
+						if (found_pin2)
+							theta = xyToAngle(pin2x, pin2y);
+						else {
+							Message
+								("Printexport_test(): unable to figure out angle of element\n"
+								 "     %s because pin #1 is at the centroid of the part.\n"
+								 "     and I could not find pin #2's location\n"
+								 "     Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta);
+						}
+					}
+					else
+						theta = xyToAngle(pin1x, pin1y);
+				}
+			}
+			/* we did not find pin #1 */
+			else {
+				theta = 0.0;
+				Message
+					("Printexport_test(): unable to figure out angle because I could\n"
+					 "     not find pin #1 of element %s\n" "     Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta);
+			}
+
+			name = Cleanexport_testString((char *) UNKNOWN(NAMEONPCB_NAME(element)));
+			descr = Cleanexport_testString((char *) UNKNOWN(DESCRIPTION_NAME(element)));
+			value = Cleanexport_testString((char *) UNKNOWN(VALUE_NAME(element)));
+
+			y = PCB->MaxHeight - y;
+			pcb_fprintf(fp, "%m+%s,\"%s\",\"%s\",%mS,%.2mS,%g,%s\n",
+									xy_unit->allow, name, descr, value, x, y, theta, FRONT(element) == 1 ? "top" : "bottom");
+			free(name);
+			free(descr);
+			free(value);
+		}
+	}
+	END_LOOP;
+
+	fclose(fp);
+
+	/* Now print out a Bill of Materials file */
+
+	fp = fopen(export_test_filename, "w");
+	if (!fp) {
+		gui->log("Cannot open file %s for writing\n", export_test_filename);
+		print_and_free(NULL, export_test);
+		return 1;
+	}
+
+	fprintf(fp, "# $Id");
+	fprintf(fp, "$\n");
+	fprintf(fp, "# Pcbexport_test Version 1.0\n");
+	fprintf(fp, "# Date: %s\n", utcTime);
+	fprintf(fp, "# Author: %s\n", pcb_author());
+	fprintf(fp, "# Title: %s - PCB export_test\n", UNKNOWN(PCB->Name));
+	fprintf(fp, "# Quantity, Description, Value, RefDes\n");
+	fprintf(fp, "# --------------------------------------------\n");
+
+	print_and_free(fp, export_test);
+
+	fclose(fp);
+
+#endif
+	return (0);
+}
+
+static void export_test_do_export(HID_Attr_Val * options)
+{
+	int i;
+
+	if (!options) {
+		export_test_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			export_test_values[i] = export_test_options[i].default_val;
+		options = export_test_values;
+	}
+
+	export_test_filename = options[HA_export_testfile].str_value;
+	if (!export_test_filename)
+		export_test_filename = "pcb-out.export_test";
+	else {
+#warning TODO: set some FILE *fp to stdout
+	}
+
+	Printexport_test();
+}
+
+static int export_test_usage(const char *topic)
+{
+	fprintf(stderr, "\nexport_test exporter command line arguments:\n\n");
+	hid_usage(export_test_options, sizeof(export_test_options) / sizeof(export_test_options[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x export_test foo.pcb [export_test_options]\n\n");
+	return 0;
+}
+
+
+static void export_test_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(export_test_options, sizeof(export_test_options) / sizeof(export_test_options[0]), export_test_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+HID export_test_hid;
+
+pcb_uninit_t hid_export_test_init()
+{
+	memset(&export_test_hid, 0, sizeof(HID));
+
+	common_nogui_init(&export_test_hid);
+
+	export_test_hid.struct_size = sizeof(HID);
+	export_test_hid.name = "export_test";
+	export_test_hid.description = "Exports a dump of HID calls";
+	export_test_hid.exporter = 1;
+
+	export_test_hid.get_export_options = export_test_get_export_options;
+	export_test_hid.do_export = export_test_do_export;
+	export_test_hid.parse_arguments = export_test_parse_arguments;
+
+	export_test_hid.usage = export_test_usage;
+
+	hid_register_hid(&export_test_hid);
+	return NULL;
+}
diff --git a/src_plugins/export_xy/Makefile b/src_plugins/export_xy/Makefile
new file mode 100644
index 0000000..52b1a8f
--- /dev/null
+++ b/src_plugins/export_xy/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_export_xy
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/export_xy/Plug.tmpasm b/src_plugins/export_xy/Plug.tmpasm
new file mode 100644
index 0000000..fe88013
--- /dev/null
+++ b/src_plugins/export_xy/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {export_xy}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_xy/xy.o @]
+
+switch /local/pcb/export_xy/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/export_xy/README b/src_plugins/export_xy/README
new file mode 100644
index 0000000..def73d0
--- /dev/null
+++ b/src_plugins/export_xy/README
@@ -0,0 +1,5 @@
+Export XY centroid element data for pick & place.
+
+#state: works
+#default: buildin
+#implements: export
diff --git a/src_plugins/export_xy/xy.c b/src_plugins/export_xy/xy.c
new file mode 100644
index 0000000..4ec6ec3
--- /dev/null
+++ b/src_plugins/export_xy/xy.c
@@ -0,0 +1,384 @@
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_init.h"
+
+extern char *CleanBOMString(const char *in);
+
+
+const char *xy_cookie = "bom HID";
+
+static HID_Attribute xy_options[] = {
+/* %start-doc options "8 XY Creation"
+ at ftable @code
+ at item --xyfile <string>
+Name of the XY output file.
+ at end ftable
+%end-doc
+*/
+	{"xyfile", "Name of the XY output file",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_xyfile 0
+
+/* %start-doc options "8 BOM Creation"
+ at ftable @code
+ at item --xy-unit <unit>
+Unit of XY dimensions. Defaults to mil.
+ at end ftable
+%end-doc
+*/
+	{"xy-unit", "XY units",
+	 HID_Unit, 0, 0, {-1, 0, 0}, NULL, 0},
+#define HA_unit 1
+	{"xy-in-mm", ATTR_UNDOCUMENTED,
+	 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
+#define HA_xymm 2
+};
+
+#define NUM_OPTIONS (sizeof(xy_options)/sizeof(xy_options[0]))
+
+static HID_Attr_Val xy_values[NUM_OPTIONS];
+
+static const char *xy_filename;
+static const Unit *xy_unit;
+
+static HID_Attribute *xy_get_export_options(int *n)
+{
+	static char *last_xy_filename = 0;
+	static int last_unit_value = -1;
+
+	if (xy_options[HA_unit].default_val.int_value == last_unit_value) {
+		if (conf_core.editor.grid_unit)
+			xy_options[HA_unit].default_val.int_value = conf_core.editor.grid_unit->index;
+		else
+			xy_options[HA_unit].default_val.int_value = get_unit_struct("mil")->index;
+		last_unit_value = xy_options[HA_unit].default_val.int_value;
+	}
+	if (PCB)
+		derive_default_filename(PCB->Filename, &xy_options[HA_xyfile], ".xy", &last_xy_filename);
+
+	if (n)
+		*n = NUM_OPTIONS;
+	return xy_options;
+}
+
+/* ---------------------------------------------------------------------------
+ * prints a centroid file in a format which includes data needed by a
+ * pick and place machine.  Further formatting for a particular factory setup
+ * can easily be generated with awk or perl.
+ * returns != zero on error
+ */
+static double xyToAngle(double x, double y, pcb_bool morethan2pins)
+{
+	double d = atan2(-y, x) * 180.0 / M_PI;
+
+	/* IPC 7351 defines different rules for 2 pin elements */
+	if (morethan2pins) {
+		/* Multi pin case:
+		 * Output 0 degrees if pin1 in is top left or top, i.e. between angles of
+		 * 80 to 170 degrees.
+		 * Pin #1 can be at dead top (e.g. certain PLCCs) or anywhere in the top
+		 * left.
+		 */
+		if (d < -100)
+			return 90;								/* -180 to -100 */
+		else if (d < -10)
+			return 180;								/* -100 to -10 */
+		else if (d < 80)
+			return 270;								/* -10 to 80 */
+		else if (d < 170)
+			return 0;									/* 80 to 170 */
+		else
+			return 90;								/* 170 to 180 */
+	}
+	else {
+		/* 2 pin element:
+		 * Output 0 degrees if pin #1 is in top left or left, i.e. in sector
+		 * between angles of 95 and 185 degrees.
+		 */
+		if (d < -175)
+			return 0;									/* -180 to -175 */
+		else if (d < -85)
+			return 90;								/* -175 to -85 */
+		else if (d < 5)
+			return 180;								/* -85 to 5 */
+		else if (d < 95)
+			return 270;								/* 5 to 95 */
+		else
+			return 0;									/* 95 to 180 */
+	}
+}
+
+/*
+ * In order of preference.
+ * Includes numbered and BGA pins.
+ * Possibly BGA pins can be missing, so we add a few to try.
+ */
+#define MAXREFPINS 32 /* max length of following list */
+static char *reference_pin_names[] = {"1", "2", "A1", "A2", "B1", "B2", 0};
+
+static int PrintXY(void)
+{
+	char utcTime[64];
+	Coord x, y;
+	double theta = 0.0;
+	double sumx, sumy;
+	double pin1x = 0.0, pin1y = 0.0;
+	int pin_cnt;
+	time_t currenttime;
+	FILE *fp;
+	char *name, *descr, *value, *fixed_rotation;
+	int pinfound[MAXREFPINS];
+	double pinx[MAXREFPINS];
+	double piny[MAXREFPINS];
+	double pinangle[MAXREFPINS];
+	double padcentrex, padcentrey;
+	double centroidx, centroidy;
+	int found_any_not_at_centroid, found_any, rpindex;
+
+
+	fp = fopen(xy_filename, "w");
+	if (!fp) {
+		gui->log("Cannot open file %s for writing\n", xy_filename);
+		return 1;
+	}
+
+	/* Create a portable timestamp. */
+	currenttime = time(NULL);
+	{
+		/* avoid gcc complaints */
+		const char *fmt = "%c UTC";
+		strftime(utcTime, sizeof(utcTime), fmt, gmtime(&currenttime));
+	}
+	fprintf(fp, "# $Id");
+	fprintf(fp, "$\n");
+	fprintf(fp, "# PcbXY Version 1.0\n");
+	fprintf(fp, "# Date: %s\n", utcTime);
+	fprintf(fp, "# Author: %s\n", pcb_author());
+	fprintf(fp, "# Title: %s - PCB X-Y\n", UNKNOWN(PCB->Name));
+	fprintf(fp, "# RefDes, Description, Value, X, Y, rotation, top/bottom\n");
+	fprintf(fp, "# X,Y in %s.  rotation in degrees.\n", xy_unit->in_suffix);
+	fprintf(fp, "# --------------------------------------------\n");
+
+	/*
+	 * For each element we calculate the centroid of the footprint.
+	 */
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		/* initialize our pin count and our totals for finding the
+		   centriod */
+		pin_cnt = 0;
+		sumx = 0.0;
+		sumy = 0.0;
+
+		/*
+		 * iterate over the pins and pads keeping a running count of how
+		 * many pins/pads total and the sum of x and y coordinates
+		 *
+		 * While we're at it, store the location of pin/pad #1 and #2 if
+		 * we can find them
+		 */
+
+		PIN_LOOP(element);
+		{
+			sumx += (double) pin->X;
+			sumy += (double) pin->Y;
+			pin_cnt++;
+
+			for (rpindex = 0; reference_pin_names[rpindex]; rpindex++) {
+				if (NSTRCMP(pin->Number, reference_pin_names[rpindex]) == 0) {
+					pinx[rpindex] = (double) pin->X;
+					piny[rpindex] = (double) pin->Y;
+					pinangle[rpindex] = 0.0;	/* pins have no notion of angle */
+					pinfound[rpindex] = 1;
+				}
+			}
+		}
+		END_LOOP;
+
+		PAD_LOOP(element);
+		{
+			sumx += (pad->Point1.X + pad->Point2.X) / 2.0;
+			sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0;
+			pin_cnt++;
+
+			for (rpindex = 0; reference_pin_names[rpindex]; rpindex++) {
+				if (NSTRCMP(pad->Number, reference_pin_names[rpindex]) == 0) {
+					padcentrex = (double) (pad->Point1.X + pad->Point2.X) / 2.0;
+					padcentrey = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0;
+					pinx[rpindex] = padcentrex;
+					piny[rpindex] = padcentrey;
+					/*
+					 * NOTE: We swap the Y points because in PCB, the Y-axis
+					 * is inverted.  Increasing Y moves down.  We want to deal
+					 * in the usual increasing Y moves up coordinates though.
+					 */
+					pinangle[rpindex] = (180.0 / M_PI) * atan2(pad->Point1.Y - pad->Point2.Y, pad->Point2.X - pad->Point1.X);
+					pinfound[rpindex] = 1;
+				}
+			}
+		}
+		END_LOOP;
+
+		if (pin_cnt > 0) {
+			centroidx = sumx / (double) pin_cnt;
+			centroidy = sumy / (double) pin_cnt;
+
+			if (NSTRCMP(AttributeGetFromList(&element->Attributes, "xy-centre"), "origin") == 0) {
+				x = element->MarkX;
+				y = element->MarkY;
+			}
+			else {
+				x = centroidx;
+				y = centroidy;
+			}
+
+			fixed_rotation = AttributeGetFromList(&element->Attributes, "xy-fixed-rotation");
+			if (fixed_rotation) {
+				/* The user specified a fixed rotation */
+				theta = atof(fixed_rotation);
+				found_any_not_at_centroid = 1;
+				found_any = 1;
+			}
+			else {
+				/* Find first reference pin not at the  centroid  */
+				found_any_not_at_centroid = 0;
+				found_any = 0;
+				theta = 0.0;
+				for (rpindex = 0; reference_pin_names[rpindex] && !found_any_not_at_centroid; rpindex++) {
+					if (pinfound[rpindex]) {
+						found_any = 1;
+
+						/* Recenter pin "#1" onto the axis which cross at the part
+						   centroid */
+						pin1x = pinx[rpindex] - x;
+						pin1y = piny[rpindex] - y;
+
+						/* flip x, to reverse rotation for elements on back */
+						if (FRONT(element) != 1)
+							pin1x = -pin1x;
+
+						/* if only 1 pin, use pin 1's angle */
+						if (pin_cnt == 1) {
+							theta = pinangle[rpindex];
+							found_any_not_at_centroid = 1;
+						}
+						else if ((pin1x != 0.0) || (pin1y != 0.0)) {
+							theta = xyToAngle(pin1x, pin1y, pin_cnt > 2);
+							found_any_not_at_centroid = 1;
+						}
+					}
+				}
+
+				if (!found_any) {
+					Message
+						(PCB_MSG_WARNING, "PrintBOM(): unable to figure out angle because I could\n"
+						 "     not find a suitable reference pin of element %s\n"
+						 "     Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta);
+				}
+				else if (!found_any_not_at_centroid) {
+					Message
+						(PCB_MSG_WARNING, "PrintBOM(): unable to figure out angle of element\n"
+						 "     %s because the reference pin(s) are at the centroid of the part.\n"
+						 "     Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta);
+				}
+			}
+		}
+
+
+		name = CleanBOMString((char *) UNKNOWN(NAMEONPCB_NAME(element)));
+		descr = CleanBOMString((char *) UNKNOWN(DESCRIPTION_NAME(element)));
+		value = CleanBOMString((char *) UNKNOWN(VALUE_NAME(element)));
+
+		y = PCB->MaxHeight - y;
+		pcb_fprintf(fp, "%m+%s,\"%s\",\"%s\",%mS,%.2mS,%g,%s\n",
+								xy_unit->allow, name, descr, value, x, y, theta, FRONT(element) == 1 ? "top" : "bottom");
+		free(name);
+		free(descr);
+		free(value);
+	}
+	END_LOOP;
+
+	fclose(fp);
+
+	return (0);
+}
+
+static void xy_do_export(HID_Attr_Val * options)
+{
+	int i;
+
+	if (!options) {
+		xy_get_export_options(0);
+		for (i = 0; i < NUM_OPTIONS; i++)
+			xy_values[i] = xy_options[i].default_val;
+		options = xy_values;
+	}
+
+	xy_filename = options[HA_xyfile].str_value;
+	if (!xy_filename)
+		xy_filename = "pcb-out.xy";
+
+	if (options[HA_unit].int_value == -1)
+		xy_unit = options[HA_xymm].int_value ? get_unit_struct("mm")
+			: get_unit_struct("mil");
+	else
+		xy_unit = &get_unit_list()[options[HA_unit].int_value];
+	PrintXY();
+}
+
+static int xy_usage(const char *topic)
+{
+	fprintf(stderr, "\nXY exporter command line arguments:\n\n");
+	hid_usage(xy_options, sizeof(xy_options) / sizeof(xy_options[0]));
+	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x xy foo.pcb [xy_options]\n\n");
+	return 0;
+}
+
+static void xy_parse_arguments(int *argc, char ***argv)
+{
+	hid_register_attributes(xy_options, sizeof(xy_options) / sizeof(xy_options[0]), xy_cookie, 0);
+	hid_parse_command_line(argc, argv);
+}
+
+HID xy_hid;
+
+pcb_uninit_t hid_export_xy_init()
+{
+	memset(&xy_hid, 0, sizeof(HID));
+
+	common_nogui_init(&xy_hid);
+
+	xy_hid.struct_size = sizeof(HID);
+	xy_hid.name = "XY";
+	xy_hid.description = "Exports a XY (centroid)";
+	xy_hid.exporter = 1;
+
+	xy_hid.get_export_options = xy_get_export_options;
+	xy_hid.do_export = xy_do_export;
+	xy_hid.parse_arguments = xy_parse_arguments;
+
+	xy_hid.usage = xy_usage;
+
+	hid_register_hid(&xy_hid);
+	return NULL;
+}
diff --git a/src_plugins/fontmode/Makefile b/src_plugins/fontmode/Makefile
new file mode 100644
index 0000000..0692e77
--- /dev/null
+++ b/src_plugins/fontmode/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_fontmode
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/fontmode/Plug.tmpasm b/src_plugins/fontmode/Plug.tmpasm
new file mode 100644
index 0000000..cf72a8c
--- /dev/null
+++ b/src_plugins/fontmode/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {fontmode}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/fontmode/fontmode.o @]
+
+switch /local/pcb/fontmode/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/fontmode/README b/src_plugins/fontmode/README
new file mode 100644
index 0000000..95f313d
--- /dev/null
+++ b/src_plugins/fontmode/README
@@ -0,0 +1,5 @@
+Font editing actions.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/fontmode/fontmode.c b/src_plugins/fontmode/fontmode.c
new file mode 100644
index 0000000..45801f5
--- /dev/null
+++ b/src_plugins/fontmode/fontmode.c
@@ -0,0 +1,245 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2006 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+
+#include <math.h>
+#include <memory.h>
+#include <limits.h>
+
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "layer.h"
+#include "misc.h"
+#include "move.h"
+#include "remove.h"
+#include "rtree.h"
+#include "strflags.h"
+#include "undo.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+
+/* FIXME - we currently hardcode the grid and PCB size.  What we
+   should do in the future is scan the font for its extents, and size
+   the grid appropriately.  Also, when we convert back to a font, we
+   should search the grid for the gridlines and use them to figure out
+   where the symbols are. */
+
+#define CELL_SIZE   ((Coord)(PCB_MIL_TO_COORD (100)))
+
+#define XYtoSym(x,y) (((x) / CELL_SIZE - 1)  +  (16 * ((y) / CELL_SIZE - 1)))
+
+static const char fontedit_syntax[] = "FontEdit()";
+
+static const char fontedit_help[] = "Convert the current font to a PCB for editing.";
+
+/* %start-doc actions FontEdit
+
+%end-doc */
+
+static int FontEdit(int argc, const char **argv, Coord Ux, Coord Uy)
+{
+	FontType *font;
+	SymbolType *symbol;
+	LayerTypePtr lfont, lorig, lwidth, lgrid;
+	int s, l;
+
+	if (hid_actionl("New", "Font", 0))
+		return 1;
+
+#warning TODO do we need to change design.bloat here?
+	conf_set(CFR_DESIGN, "editor/grid_unit", -1, "mil", POL_OVERWRITE);
+	conf_set_design("design/bloat", "%s", "1"); PCB->Bloat = 1;
+	conf_set_design("design/shrink", "%s", "1"); PCB->Shrink = 1;
+	conf_set_design("design/min_wid", "%s", "1"); PCB->minWid = 1;
+	conf_set_design("design/min_slk", "%s", "1"); PCB->minSlk = 1;
+
+	MoveLayerToGroup(max_copper_layer + COMPONENT_LAYER, 0);
+	MoveLayerToGroup(max_copper_layer + SOLDER_LAYER, 1);
+
+	while (PCB->Data->LayerN > 4)
+		MoveLayer(4, -1);
+	for (l = 0; l < 4; l++) {
+		MoveLayerToGroup(l, l);
+	}
+	PCB->MaxWidth = CELL_SIZE * 18;
+	PCB->MaxHeight = CELL_SIZE * ((MAX_FONTPOSITION + 15) / 16 + 2);
+	PCB->Grid = PCB_MIL_TO_COORD(5);
+	PCB->Data->Layer[0].Name = pcb_strdup("Font");
+	PCB->Data->Layer[1].Name = pcb_strdup("OrigFont");
+	PCB->Data->Layer[2].Name = pcb_strdup("Width");
+	PCB->Data->Layer[3].Name = pcb_strdup("Grid");
+	hid_action("PCBChanged");
+	hid_action("LayersChanged");
+
+	lfont = PCB->Data->Layer + 0;
+	lorig = PCB->Data->Layer + 1;
+	lwidth = PCB->Data->Layer + 2;
+	lgrid = PCB->Data->Layer + 3;
+
+	font = &PCB->Font;
+	for (s = 0; s <= MAX_FONTPOSITION; s++) {
+		Coord ox = (s % 16 + 1) * CELL_SIZE;
+		Coord oy = (s / 16 + 1) * CELL_SIZE;
+		Coord w, miny, maxy, maxx = 0;
+
+		symbol = &font->Symbol[s];
+
+		miny = PCB_MIL_TO_COORD(5);
+		maxy = font->MaxHeight;
+
+		for (l = 0; l < symbol->LineN; l++) {
+			CreateDrawnLineOnLayer(lfont,
+														 symbol->Line[l].Point1.X + ox,
+														 symbol->Line[l].Point1.Y + oy,
+														 symbol->Line[l].Point2.X + ox,
+														 symbol->Line[l].Point2.Y + oy, symbol->Line[l].Thickness, symbol->Line[l].Thickness, NoFlags());
+			CreateDrawnLineOnLayer(lorig, symbol->Line[l].Point1.X + ox,
+														 symbol->Line[l].Point1.Y + oy,
+														 symbol->Line[l].Point2.X + ox,
+														 symbol->Line[l].Point2.Y + oy, symbol->Line[l].Thickness, symbol->Line[l].Thickness, NoFlags());
+			if (maxx < symbol->Line[l].Point1.X)
+				maxx = symbol->Line[l].Point1.X;
+			if (maxx < symbol->Line[l].Point2.X)
+				maxx = symbol->Line[l].Point2.X;
+		}
+		w = maxx + symbol->Delta + ox;
+		CreateDrawnLineOnLayer(lwidth, w, miny + oy, w, maxy + oy, PCB_MIL_TO_COORD(1), PCB_MIL_TO_COORD(1), NoFlags());
+	}
+
+	for (l = 0; l < 16; l++) {
+		int x = (l + 1) * CELL_SIZE;
+		CreateDrawnLineOnLayer(lgrid, x, 0, x, PCB->MaxHeight, PCB_MIL_TO_COORD(1), PCB_MIL_TO_COORD(1), NoFlags());
+	}
+	for (l = 0; l <= MAX_FONTPOSITION / 16 + 1; l++) {
+		int y = (l + 1) * CELL_SIZE;
+		CreateDrawnLineOnLayer(lgrid, 0, y, PCB->MaxWidth, y, PCB_MIL_TO_COORD(1), PCB_MIL_TO_COORD(1), NoFlags());
+	}
+	return 0;
+}
+
+static const char fontsave_syntax[] = "FontSave()";
+
+static const char fontsave_help[] = "Convert the current PCB back to a font.";
+
+/* %start-doc actions FontSave
+
+%end-doc */
+
+static int FontSave(int argc, const char **argv, Coord Ux, Coord Uy)
+{
+	FontTypePtr font;
+	SymbolTypePtr symbol;
+	int i;
+	LineType *l;
+	gdl_iterator_t it;
+	LayerTypePtr lfont, lwidth;
+
+	font = &PCB->Font;
+	lfont = PCB->Data->Layer + 0;
+	lwidth = PCB->Data->Layer + 2;
+
+	for (i = 0; i <= MAX_FONTPOSITION; i++) {
+		font->Symbol[i].LineN = 0;
+		font->Symbol[i].Valid = 0;
+		font->Symbol[i].Width = 0;
+	}
+
+	linelist_foreach(&lfont->Line, &it, l) {
+		int x1 = l->Point1.X;
+		int y1 = l->Point1.Y;
+		int x2 = l->Point2.X;
+		int y2 = l->Point2.Y;
+		int ox, oy, s;
+
+		s = XYtoSym(x1, y1);
+		ox = (s % 16 + 1) * CELL_SIZE;
+		oy = (s / 16 + 1) * CELL_SIZE;
+		symbol = &PCB->Font.Symbol[s];
+
+		x1 -= ox;
+		y1 -= oy;
+		x2 -= ox;
+		y2 -= oy;
+
+		if (symbol->Width < x1)
+			symbol->Width = x1;
+		if (symbol->Width < x2)
+			symbol->Width = x2;
+		symbol->Valid = 1;
+
+		CreateNewLineInSymbol(symbol, x1, y1, x2, y2, l->Thickness);
+	}
+
+	linelist_foreach(&lwidth->Line, &it, l) {
+		Coord x1 = l->Point1.X;
+		Coord y1 = l->Point1.Y;
+		Coord ox, s;
+
+		s = XYtoSym(x1, y1);
+		ox = (s % 16 + 1) * CELL_SIZE;
+		symbol = &PCB->Font.Symbol[s];
+
+		x1 -= ox;
+
+		symbol->Delta = x1 - symbol->Width;
+	}
+
+	SetFontInfo(font);
+
+	return 0;
+}
+
+HID_Action fontmode_action_list[] = {
+	{"FontEdit", 0, FontEdit,
+	 fontedit_help, fontedit_syntax}
+	,
+	{"FontSave", 0, FontSave,
+	 fontsave_help, fontsave_syntax}
+};
+
+static const char *fontmode_cookie = "fontmode plugin";
+
+REGISTER_ACTIONS(fontmode_action_list, fontmode_cookie)
+
+static void hid_fontmode_uninit(void)
+{
+	hid_remove_actions_by_cookie(fontmode_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_fontmode_init(void)
+{
+	REGISTER_ACTIONS(fontmode_action_list, fontmode_cookie)
+	return hid_fontmode_uninit;
+}
diff --git a/src_plugins/fp_fs/Makefile b/src_plugins/fp_fs/Makefile
new file mode 100644
index 0000000..eed8033
--- /dev/null
+++ b/src_plugins/fp_fs/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_fp_fs
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/fp_fs/Plug.tmpasm b/src_plugins/fp_fs/Plug.tmpasm
new file mode 100644
index 0000000..9338a4d
--- /dev/null
+++ b/src_plugins/fp_fs/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {fp_fs}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/fp_fs/fp_fs.o @]
+
+switch /local/pcb/fp_fs/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/fp_fs/README b/src_plugins/fp_fs/README
new file mode 100644
index 0000000..45a7393
--- /dev/null
+++ b/src_plugins/fp_fs/README
@@ -0,0 +1,7 @@
+Footprint: file system based implementation. Used to be called Newlib: load
+footprints from directories. Run external processes for the parametric
+footprints.
+
+#state: works
+#default: buildin
+#implements: fp
diff --git a/src_plugins/fp_fs/fp_fs.c b/src_plugins/fp_fs/fp_fs.c
new file mode 100644
index 0000000..52f7dd1
--- /dev/null
+++ b/src_plugins/fp_fs/fp_fs.c
@@ -0,0 +1,513 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mymem.h"
+#include "data.h"
+#include "paths.h"
+#include "plugins.h"
+#include "plug_footprint.h"
+#include "compat_fs.h"
+#include "compat_misc.h"
+#include "error.h"
+#include "misc.h"
+#include "conf.h"
+#include "conf_core.h"
+
+/* opendir, readdir */
+#include "compat_inc.h"
+
+static fp_type_t pcb_fp_file_type(const char *fn, void ***tags);
+
+/* ---------------------------------------------------------------------------
+ * Parse the directory tree where newlib footprints are found
+ */
+typedef struct list_dir_s list_dir_t;
+
+struct list_dir_s {
+	char *parent;
+	char *subdir;
+	list_dir_t *next;
+};
+
+typedef struct {
+	library_t *menu;
+	list_dir_t *subdirs;
+	int children;
+} list_st_t;
+
+static int list_cb(void *cookie, const char *subdir, const char *name, fp_type_t type, void *tags[])
+{
+	list_st_t *l = (list_st_t *) cookie;
+	library_t *e;
+
+	if (type == PCB_FP_DIR) {
+		list_dir_t *d;
+		/* can not recurse directly from here because that would ruin the menu
+		   pointer: GetLibraryMenuMemory (&Library) calls realloc()!
+		   Build a list of directories to be visited later, instead. */
+		d = malloc(sizeof(list_dir_t));
+		d->subdir = pcb_strdup(name);
+		d->parent = pcb_strdup(subdir);
+		d->next = l->subdirs;
+		l->subdirs = d;
+		return 0;
+	}
+
+	l->children++;
+	e = fp_append_entry(l->menu, name, type, tags);
+
+/* Avoid using Concat() - would be a new dependency for gsch2pcb-rnd */
+	{
+		int sl = strlen(subdir);
+		int nl = strlen(name);
+		char *end;
+
+		end = e->data.fp.loc_info = malloc(sl+nl+3);
+		memcpy(end, subdir, sl); end += sl;
+		*end = '/'; end++;
+		memcpy(end, name, nl+1); end += nl;
+	}
+
+	return 0;
+}
+
+static int fp_fs_list(library_t *pl, const char *subdir, int recurse,
+								int (*cb) (void *cookie, const char *subdir, const char *name, fp_type_t type, void *tags[]), void *cookie,
+								int subdir_may_not_exist, int need_tags)
+{
+	char olddir[MAXPATHLEN + 1];	/* The directory we start out in (cwd) */
+	char new_subdir[MAXPATHLEN + 1];
+	char fn[MAXPATHLEN + 1], *fn_end;
+	DIR *subdirobj;								/* Interable object holding all subdir entries */
+	struct dirent *subdirentry;		/* Individual subdir entry */
+	struct stat buffer;						/* Buffer used in stat */
+	size_t l;
+	int n_footprints = 0;					/* Running count of footprints found in this subdir */
+
+	/* Cache old dir, then cd into subdir because stat is given relative file names. */
+	memset(olddir, 0, sizeof olddir);
+	if (GetWorkingDirectory(olddir) == NULL) {
+		Message(PCB_MSG_DEFAULT, _("fp_fs_list(): Could not determine initial working directory\n"));
+		return 0;
+	}
+
+	if (strcmp(subdir, ".svn") == 0)
+		return 0;
+
+	if (chdir(subdir)) {
+		if (!subdir_may_not_exist)
+			ChdirErrorMessage(subdir);
+		return 0;
+	}
+
+
+	/* Determine subdir's abs path */
+	if (GetWorkingDirectory(new_subdir) == NULL) {
+		Message(PCB_MSG_DEFAULT, _("fp_fs_list(): Could not determine new working directory\n"));
+		if (chdir(olddir))
+			ChdirErrorMessage(olddir);
+		return 0;
+	}
+
+	l = strlen(new_subdir);
+	memcpy(fn, new_subdir, l);
+	fn[l] = PCB_DIR_SEPARATOR_C;
+	fn_end = fn + l + 1;
+
+	/* First try opening the directory specified by path */
+	if ((subdirobj = opendir(new_subdir)) == NULL) {
+		OpendirErrorMessage(new_subdir);
+		if (chdir(olddir))
+			ChdirErrorMessage(olddir);
+		return 0;
+	}
+
+	/* Now loop over files in this directory looking for files.
+	 * We ignore certain files which are not footprints.
+	 */
+	while ((subdirentry = readdir(subdirobj)) != NULL) {
+#ifdef DEBUG
+/*    printf("...  Examining file %s ... \n", subdirentry->d_name); */
+#endif
+
+		/* Ignore non-footprint files found in this directory
+		 * We're skipping .png and .html because those
+		 * may exist in a library tree to provide an html browsable
+		 * index of the library.
+		 */
+		l = strlen(subdirentry->d_name);
+		if (!stat(subdirentry->d_name, &buffer)
+				&& subdirentry->d_name[0] != '.'
+				&& NSTRCMP(subdirentry->d_name, "CVS") != 0
+				&& NSTRCMP(subdirentry->d_name, "Makefile") != 0
+				&& NSTRCMP(subdirentry->d_name, "Makefile.am") != 0
+				&& NSTRCMP(subdirentry->d_name, "Makefile.in") != 0 && (l < 4 || NSTRCMP(subdirentry->d_name + (l - 4), ".png") != 0)
+				&& (l < 5 || NSTRCMP(subdirentry->d_name + (l - 5), ".html") != 0)
+				&& (l < 4 || NSTRCMP(subdirentry->d_name + (l - 4), ".pcb") != 0)) {
+
+#ifdef DEBUG
+/*	printf("...  Found a footprint %s ... \n", subdirentry->d_name); */
+#endif
+			strcpy(fn_end, subdirentry->d_name);
+			if ((S_ISREG(buffer.st_mode)) || (WRAP_S_ISLNK(buffer.st_mode))) {
+				fp_type_t ty;
+				void **tags = NULL;
+				ty = pcb_fp_file_type(subdirentry->d_name, (need_tags ? &tags : NULL));
+				if ((ty == PCB_FP_FILE) || (ty == PCB_FP_PARAMETRIC)) {
+					n_footprints++;
+					if (cb(cookie, new_subdir, subdirentry->d_name, ty, tags))
+						break;
+					continue;
+				}
+				else
+					if (tags != NULL)
+						free(tags);
+			}
+
+			if ((S_ISDIR(buffer.st_mode)) || (WRAP_S_ISLNK(buffer.st_mode))) {
+				cb(cookie, new_subdir, subdirentry->d_name, PCB_FP_DIR, NULL);
+				if (recurse) {
+					n_footprints += fp_fs_list(pl, fn, recurse, cb, cookie, 0, need_tags);
+				}
+				continue;
+			}
+
+		}
+	}
+	/* Done.  Clean up, cd back into old dir, and return */
+	closedir(subdirobj);
+	if (chdir(olddir))
+		ChdirErrorMessage(olddir);
+	return n_footprints;
+}
+
+static int fp_fs_load_dir_(library_t *pl, const char *subdir, const char *toppath, int is_root)
+{
+	list_st_t l;
+	list_dir_t *d, *nextd;
+	char working_[MAXPATHLEN + 1];
+	const char *visible_subdir;
+	char *working;								/* String holding abs path to working dir */
+
+	sprintf(working_, "%s%c%s", toppath, PCB_DIR_SEPARATOR_C, subdir);
+	resolve_path(working_, &working, 0);
+
+	if (strcmp(subdir, ".") == 0)
+		visible_subdir = "fs";
+	else
+		visible_subdir = subdir;
+
+	l.menu = fp_lib_search(pl, visible_subdir);
+	if (l.menu == NULL)
+		l.menu = fp_mkdir_len(pl, visible_subdir, -1);
+	l.subdirs = NULL;
+	l.children = 0;
+
+	fp_fs_list(l.menu, working, 0, list_cb, &l, is_root, 1);
+
+	/* now recurse to each subdirectory mapped in the previous call;
+	   by now we don't care if menu is ruined by the realloc() in GetLibraryMenuMemory() */
+	for (d = l.subdirs; d != NULL; d = nextd) {
+		l.children += fp_fs_load_dir_(l.menu, d->subdir, d->parent, 0);
+		nextd = d->next;
+		free(d->subdir);
+		free(d->parent);
+		free(d);
+	}
+	if ((l.children == 0) && (l.menu->data.dir.children.used == 0))
+		fp_rmdir(l.menu);
+	free(working);
+	return l.children;
+}
+
+
+static int fp_fs_load_dir(plug_fp_t *ctx, const char *path)
+{
+	return fp_fs_load_dir_(&library, ".", path, 1);
+}
+
+typedef struct {
+	const char *target;
+	int target_len;
+	int parametric;
+	char *path;
+	char *real_name;
+} fp_search_t;
+
+static int fp_search_cb(void *cookie, const char *subdir, const char *name, fp_type_t type, void *tags[])
+{
+	fp_search_t *ctx = (fp_search_t *) cookie;
+	if ((strncmp(ctx->target, name, ctx->target_len) == 0) && ((! !ctx->parametric) == (type == PCB_FP_PARAMETRIC))) {
+		const char *suffix = name + ctx->target_len;
+		/* ugly heuristics: footprint names may end in .fp or .ele */
+		if ((*suffix == '\0') || (strcasecmp(suffix, ".fp") == 0) || (strcasecmp(suffix, ".ele") == 0)) {
+			ctx->path = pcb_strdup(subdir);
+			ctx->real_name = pcb_strdup(name);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* walk the search_path for finding the first footprint for basename (shall not contain "(") */
+static char *fp_fs_search(const char *search_path, const char *basename, int parametric)
+{
+	const char *p, *end;
+	char path[MAXPATHLEN + 1];
+	fp_search_t ctx;
+
+	if ((*basename == '/') || (*basename == PCB_DIR_SEPARATOR_C))
+		return pcb_strdup(basename);
+
+	ctx.target = basename;
+	ctx.target_len = strlen(ctx.target);
+	ctx.parametric = parametric;
+	ctx.path = NULL;
+
+/*	fprintf("Looking for %s\n", ctx.target);*/
+
+	for (p = search_path; *p != '\0'; p = end + 1) {
+		char *fpath;
+		end = strchr(p, ':');
+		if (end == NULL)
+			end = p + strlen(p);
+		memcpy(path, p, end - p);
+		path[end - p] = '\0';
+
+		resolve_path(path, &fpath, 0);
+/*		fprintf(stderr, " in '%s'\n", fpath);*/
+
+		fp_fs_list(&library, fpath, 1, fp_search_cb, &ctx, 1, 0);
+		if (ctx.path != NULL) {
+			sprintf(path, "%s%c%s", ctx.path, PCB_DIR_SEPARATOR_C, ctx.real_name);
+			free(ctx.path);
+			free(ctx.real_name);
+/*			fprintf("  found '%s'\n", path);*/
+			free(fpath);
+			return pcb_strdup(path);
+		}
+		free(fpath);
+		if (end == NULL)
+			break;
+	}
+	return NULL;
+}
+
+/* Decide about the type of a footprint file:
+   - it is a file element if the first non-comment is "Element(" or "Element["
+   - else it is a parametric element (footprint generator) if it contains
+     "@@" "purpose"
+   - else it's not an element.
+   - if a line of a file element starts with ## and doesn't contain @, it's a tag
+   - if tags is not NULL, it's a pointer to a void *tags[] - an array of tag IDs
+*/
+static fp_type_t pcb_fp_file_type(const char *fn, void ***tags)
+{
+	int c, comment_len;
+	int first_element = 1;
+	FILE *f;
+	enum {
+		ST_WS,
+		ST_COMMENT,
+		ST_ELEMENT,
+		ST_TAG
+	} state = ST_WS;
+	char *tag = NULL;
+	int talloced = 0, tused = 0;
+	int Talloced = 0, Tused = 0;
+	fp_type_t ret = PCB_FP_INVALID;
+
+	if (tags != NULL)
+		*tags = NULL;
+
+	f = fopen(fn, "r");
+	if (f == NULL)
+		return PCB_FP_INVALID;
+
+	while ((c = fgetc(f)) != EOF) {
+		switch (state) {
+		case ST_ELEMENT:
+			if (isspace(c))
+				break;
+			if ((c == '(') || (c == '[')) {
+				ret = PCB_FP_FILE;
+				goto out;
+			}
+		case ST_WS:
+			if (isspace(c))
+				break;
+			if (c == '#') {
+				comment_len = 0;
+				state = ST_COMMENT;
+				break;
+			}
+			else if ((first_element) && (c == 'E')) {
+				char s[8];
+				/* Element */
+				fgets(s, 7, f);
+				s[6] = '\0';
+				if (strcmp(s, "lement") == 0) {
+					state = ST_ELEMENT;
+					break;
+				}
+			}
+			first_element = 0;
+			/* fall-thru for detecting @ */
+		case ST_COMMENT:
+			comment_len++;
+			if ((c == '#') && (comment_len == 1)) {
+				state = ST_TAG;
+				break;
+			}
+			if ((c == '\r') || (c == '\n'))
+				state = ST_WS;
+			if (c == '@') {
+				char s[10];
+			maybe_purpose:;
+				/* "@@" "purpose" */
+				fgets(s, 9, f);
+				s[8] = '\0';
+				if (strcmp(s, "@purpose") == 0) {
+					ret = PCB_FP_PARAMETRIC;
+					goto out;
+				}
+			}
+			break;
+		case ST_TAG:
+			if ((c == '\r') || (c == '\n')) {	/* end of a tag */
+				if (tags != NULL) {
+					tag[tused] = '\0';
+					if (Tused >= Talloced) {
+						Talloced += 8;
+						*tags = realloc(*tags, (Talloced + 1) * sizeof(void *));
+					}
+					(*tags)[Tused] = (void *) fp_tag(tag, 1);
+					Tused++;
+					(*tags)[Tused] = NULL;
+				}
+
+				tused = 0;
+				state = ST_WS;
+				break;
+			}
+			if (c == '@')
+				goto maybe_purpose;
+			if (tused >= talloced) {
+				talloced += 64;
+				tag = realloc(tag, talloced + 1);	/* always make room for an extra \0 */
+			}
+			tag[tused] = c;
+			tused++;
+		}
+	}
+
+out:;
+	if (tag != NULL)
+		free(tag);
+	fclose(f);
+	return ret;
+}
+
+#define F_IS_PARAMETRIC 0
+static FILE *fp_fs_fopen(plug_fp_t *ctx, const char *path, const char *name, fp_fopen_ctx_t *fctx)
+{
+	char *basename, *params, *fullname;
+	FILE *f = NULL;
+	const char *libshell = conf_core.rc.library_shell;
+
+	fctx->field[F_IS_PARAMETRIC].i = fp_dupname(name, &basename, &params);
+	if (basename == NULL)
+		return NULL;
+
+	fctx->backend = ctx;
+
+	fullname = fp_fs_search(path, basename, fctx->field[F_IS_PARAMETRIC].i);
+/*	fprintf(stderr, "basename=%s fullname=%s\n", basename, fullname);*/
+/*	printf("pcb_fp_fopen: %d '%s' '%s' fullname='%s'\n", fctx->field[F_IS_PARAMETRIC].i, basename, params, fullname);*/
+
+
+	if (fullname != NULL) {
+/*fprintf(stderr, "fullname=%s param=%d\n",  fullname, fctx->field[F_IS_PARAMETRIC].i);*/
+		if (fctx->field[F_IS_PARAMETRIC].i) {
+			char *cmd;
+			const char *sep = " ";
+			if (libshell == NULL) {
+				libshell = "";
+				sep = "";
+			}
+			cmd = malloc(strlen(libshell) + strlen(fullname) + strlen(params) + 16);
+			sprintf(cmd, "%s%s%s %s", libshell, sep, fullname, params);
+/*fprintf(stderr, " cmd=%s\n",  cmd);*/
+			f = popen(cmd, "r");
+			free(cmd);
+		}
+		else
+			f = fopen(fullname, "r");
+		free(fullname);
+	}
+
+	free(basename);
+	return f;
+}
+
+static void fp_fs_fclose(plug_fp_t *ctx, FILE * f, fp_fopen_ctx_t *fctx)
+{
+	if (fctx->field[F_IS_PARAMETRIC].i)
+		pclose(f);
+	else
+		fclose(f);
+}
+
+
+static plug_fp_t fp_fs;
+
+void hid_fp_fs_uninit(void)
+{
+	HOOK_UNREGISTER(plug_fp_t, plug_fp_chain, &fp_fs);
+}
+
+pcb_uninit_t hid_fp_fs_init(void)
+{
+	fp_fs.plugin_data = NULL;
+	fp_fs.load_dir = fp_fs_load_dir;
+	fp_fs.fopen = fp_fs_fopen;
+	fp_fs.fclose = fp_fs_fclose;
+	HOOK_REGISTER(plug_fp_t, plug_fp_chain, &fp_fs);
+	return hid_fp_fs_uninit;
+}
diff --git a/src_plugins/fp_wget/Makefile b/src_plugins/fp_wget/Makefile
new file mode 100644
index 0000000..3c17ed4
--- /dev/null
+++ b/src_plugins/fp_wget/Makefile
@@ -0,0 +1,12 @@
+all: tester
+	cd ../../src && make mod_fp_wget
+
+CFLAGS = -Wall -g -I../../src -I../.. -I../../src_3rd -I../../src_3rd/liblihata
+
+test: tester
+
+tester: tester.o gedasymbols.o wget_common.o ../../src_3rd/genvector/gds_char.o
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/fp_wget/Plug.tmpasm b/src_plugins/fp_wget/Plug.tmpasm
new file mode 100644
index 0000000..b7a2294
--- /dev/null
+++ b/src_plugins/fp_wget/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {fp_wget}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/fp_wget/fp_wget.o $(PLUGDIR)/fp_wget/wget_common.o $(PLUGDIR)/fp_wget/gedasymbols.o @]
+
+switch /local/pcb/fp_wget/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/fp_wget/README b/src_plugins/fp_wget/README
new file mode 100644
index 0000000..d11c0b7
--- /dev/null
+++ b/src_plugins/fp_wget/README
@@ -0,0 +1,6 @@
+Footprint: get static (file) footprints from the web, e.g. from
+http://gedasymbols.org
+
+#state: works
+#default: buildin
+#implements: fp
diff --git a/src_plugins/fp_wget/fp_wget.c b/src_plugins/fp_wget/fp_wget.c
new file mode 100644
index 0000000..f509fe8
--- /dev/null
+++ b/src_plugins/fp_wget/fp_wget.c
@@ -0,0 +1,14 @@
+#include "global.h"
+#include "gedasymbols.h"
+#include "plugins.h"
+
+void hid_fp_wget_uninit(void)
+{
+	fp_gedasymbols_uninit();
+}
+
+pcb_uninit_t hid_fp_wget_init(void)
+{
+	fp_gedasymbols_init();
+	return hid_fp_wget_uninit;
+}
diff --git a/src_plugins/fp_wget/gedasymbols.c b/src_plugins/fp_wget/gedasymbols.c
new file mode 100644
index 0000000..6612d5b
--- /dev/null
+++ b/src_plugins/fp_wget/gedasymbols.c
@@ -0,0 +1,197 @@
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <genvector/gds_char.h>
+#include <genht/htsp.h>
+#include <genht/hash.h>
+#include "global.h"
+#include "wget_common.h"
+#include "gedasymbols.h"
+#include "plugins.h"
+#include "plug_footprint.h"
+#include "compat_misc.h"
+
+#define REQUIRE_PATH_PREFIX "wget at gedasymbols"
+
+#define CGI_URL "http://www.gedasymbols.org/scripts/global_list.cgi"
+#define FP_URL "http://www.gedasymbols.org/"
+#define FP_DL "?dl"
+static const char *url_idx_md5 = CGI_URL "?md5";
+static const char *url_idx_list = CGI_URL;
+static const char *gedasym_cache = "fp_wget_cache";
+static const char *last_sum_fn = "fp_wget_cache/gedasymbols.last";
+
+static char *load_md5_sum(FILE *f)
+{
+	char *s, sum[64];
+
+	if (f == NULL)
+		return NULL;
+
+	*sum = '\0';
+	fgets(sum, sizeof(sum), f);
+	sum[sizeof(sum)-1] = '\0';
+
+	for(s = sum;; s++) {
+		if ((*s == '\0') || (isspace(*s))) {
+			if ((s - sum) == 32) {
+				*s = '\0';
+				return pcb_strdup(sum);
+			}
+			else
+				return NULL;
+		}
+		if (isdigit(*s))
+			continue;
+		if ((*s >= 'a') && (*s <= 'f'))
+			continue;
+		if ((*s >= 'A') && (*s <= 'F'))
+			continue;
+		return NULL;
+	}
+}
+
+static int md5_cmp_free(const char *last_fn, char *md5_last, char *md5_new)
+{
+	int changed = 0;
+
+	if ((md5_last == NULL) || (strcmp(md5_last, md5_new) != 0)) {
+		FILE *f;
+		f = fopen(last_fn, "w");
+		fputs(md5_new, f);
+		fclose(f);
+		changed = 1;
+	}
+	if (md5_last != NULL)
+		free(md5_last);
+	free(md5_new);
+	return changed;
+}
+
+int fp_gedasymbols_load_dir(plug_fp_t *ctx, const char *path)
+{
+	FILE *f;
+	int fctx;
+	char *md5_last, *md5_new;
+	char line[1024];
+	fp_get_mode mode;
+	gds_t vpath;
+	int vpath_base_len;
+
+	if (strncmp(path, REQUIRE_PATH_PREFIX, strlen(REQUIRE_PATH_PREFIX)) != 0)
+		return -1;
+
+	if (fp_wget_open(url_idx_md5, gedasym_cache, &f, &fctx, 0) != 0)
+		return -1;
+	md5_new = load_md5_sum(f);
+	fp_wget_close(&f, &fctx);
+
+	if (md5_new == NULL)
+		return -1;
+
+	f = fopen(last_sum_fn, "r");
+	md5_last = load_md5_sum(f);
+	if (f != NULL)
+		fclose(f);
+
+	printf("old='%s' new='%s'\n", md5_last, md5_new);
+
+	if (!md5_cmp_free(last_sum_fn, md5_last, md5_new)) {
+		printf("no chg.\n");
+		mode = FP_WGET_OFFLINE; /* use the cache */
+	}
+	else
+		mode = 0;
+
+	if (fp_wget_open(url_idx_list, gedasym_cache, &f, &fctx, mode) != 0) {
+		printf("failed to download the new list\n");
+		remove(last_sum_fn); /* make sure it is downloaded next time */
+		return -1;
+	}
+
+	gds_init(&vpath);
+	gds_append_str(&vpath, REQUIRE_PATH_PREFIX);
+	gds_append(&vpath, '/');
+	vpath_base_len = vpath.used;
+
+	while(fgets(line, sizeof(line), f) != NULL) {
+		char *end, *fn;
+		library_t *l;
+
+		if (*line == '#')
+			continue;
+		end = strchr(line, '|');
+		if (end == NULL)
+			continue;
+		*end = '\0';
+
+		/* split path and fn; path stays in vpath.array, fn is a ptr to the file name */
+		gds_truncate(&vpath, vpath_base_len);
+		gds_append_str(&vpath, line);
+		end = vpath.array + vpath.used - 1;
+		while((end > vpath.array) && (*end != '/')) { end--; vpath.used--; }
+		*end = '\0';
+		vpath.used--;
+		end++;
+		fn = end;
+
+		/* add to the database */
+		l = fp_mkdir_p(vpath.array);
+		l = fp_append_entry(l, fn, PCB_FP_FILE, NULL);
+		fn[-1] = '/';
+		l->data.fp.loc_info = pcb_strdup(vpath.array);
+	}
+	fp_wget_close(&f, &fctx);
+
+	printf("update!\n");
+	return 0;
+}
+
+#define FIELD_WGET_CTX 0
+
+FILE *fp_gedasymbols_fopen(plug_fp_t *ctx, const char *path, const char *name, fp_fopen_ctx_t *fctx)
+{
+	gds_t s;
+	FILE *f;
+
+	if (strncmp(name, REQUIRE_PATH_PREFIX, strlen(REQUIRE_PATH_PREFIX)) == 0)
+		name+=strlen(REQUIRE_PATH_PREFIX);
+	else
+		return NULL;
+
+	if (*name == '/')
+		name++;
+
+	gds_init(&s);
+	gds_append_str(&s, FP_URL);
+	gds_append_str(&s, name);
+	gds_append_str(&s, FP_DL);
+
+	fp_wget_open(s.array, gedasym_cache, &f, &(fctx->field[FIELD_WGET_CTX].i), FP_WGET_UPDATE);
+
+	gds_uninit(&s);
+	return f;
+}
+
+void fp_gedasymbols_fclose(plug_fp_t *ctx, FILE * f, fp_fopen_ctx_t *fctx)
+{
+	fp_wget_close(&f, &(fctx->field[FIELD_WGET_CTX].i));
+}
+
+
+static plug_fp_t fp_gedasymbols;
+
+void fp_gedasymbols_uninit(void)
+{
+	HOOK_UNREGISTER(plug_fp_t, plug_fp_chain, &fp_gedasymbols);
+}
+
+void fp_gedasymbols_init(void)
+{
+	fp_gedasymbols.plugin_data = NULL;
+	fp_gedasymbols.load_dir = fp_gedasymbols_load_dir;
+	fp_gedasymbols.fopen = fp_gedasymbols_fopen;
+	fp_gedasymbols.fclose = fp_gedasymbols_fclose;
+
+	HOOK_REGISTER(plug_fp_t, plug_fp_chain, &fp_gedasymbols);
+}
diff --git a/src_plugins/fp_wget/gedasymbols.h b/src_plugins/fp_wget/gedasymbols.h
new file mode 100644
index 0000000..95d1f78
--- /dev/null
+++ b/src_plugins/fp_wget/gedasymbols.h
@@ -0,0 +1,7 @@
+#include "plug_footprint.h"
+int fp_gedasymbols_load_dir(plug_fp_t *ctx, const char *path);
+FILE *fp_gedasymbols_fopen(plug_fp_t *ctx, const char *path, const char *name, fp_fopen_ctx_t *fctx);
+void fp_gedasymbols_fclose(plug_fp_t *ctx, FILE * f, fp_fopen_ctx_t *fctx);
+void fp_gedasymbols_init(void);
+void fp_gedasymbols_uninit(void);
+
diff --git a/src_plugins/fp_wget/tester.c b/src_plugins/fp_wget/tester.c
new file mode 100644
index 0000000..6384ff9
--- /dev/null
+++ b/src_plugins/fp_wget/tester.c
@@ -0,0 +1,39 @@
+#include <stdlib.h>
+#include <string.h>
+#include "global.h"
+#include "gedasymbols.h"
+
+#undef strdup
+char *pcb_strdup(const char *s) { return strdup(s); }
+
+plug_fp_t *plug_fp_chain = NULL;
+
+library_t ltmp;
+library_t *fp_mkdir_p(const char *path)
+{
+	printf("lib mkdir: '%s'\n", path);
+	return (library_t *)<mp;
+}
+
+library_t *fp_append_entry(library_t *parent, const char *name, fp_type_t type, void *tags[])
+{
+	printf("lib entry: '%s'\n", name);
+	return (library_t *)<mp;
+}
+
+int main()
+{
+	fp_fopen_ctx_t fctx;
+	FILE *f;
+	char line[1024];
+
+/*	fp_gedasymbols_load_dir(NULL, "gedasymbols://"); */
+	f = fp_gedasymbols_fopen(NULL, NULL, "wget at gedasymbols/user/sean_depagnier/footprints/HDMI_CONN.fp", &fctx);
+
+	while(fgets(line, sizeof(line), f) != NULL)
+		printf("|%s", line);
+
+	fp_gedasymbols_fclose(NULL, f, &fctx);
+
+}
+
diff --git a/src_plugins/fp_wget/wget_common.c b/src_plugins/fp_wget/wget_common.c
new file mode 100644
index 0000000..e7892ae
--- /dev/null
+++ b/src_plugins/fp_wget/wget_common.c
@@ -0,0 +1,107 @@
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+
+#include <stdlib.h>
+#include <string.h>
+#include "wget_common.h"
+
+enum {
+	FCTX_INVALID = 0,
+	FCTX_POPEN,
+	FCTX_FOPEN,
+	FCTX_NOP
+};
+
+const char *wget_cmd = "wget -U 'pcb-rnd-fp_wget'";
+int fp_wget_offline = 0;
+
+static int mkdirp(const char *dir)
+{
+/* TODO */
+	char buff[8192];
+	sprintf(buff, "mkdir -p '%s'", dir);
+	return system(buff);
+}
+
+int fp_wget_open(const char *url, const char *cache_path, FILE **f, int *fctx, fp_get_mode mode)
+{
+	char *cmd;
+	const char *upds;
+	int wl = strlen(wget_cmd), ul = strlen(url), cl = strlen(cache_path);
+	cmd = malloc(wl+ul*2+cl+32);
+
+	*fctx = FCTX_INVALID;
+
+	if (mode & FP_WGET_UPDATE)
+		upds = "-c";
+	else
+		upds = "";
+
+	if (cache_path == NULL) {
+		sprintf(cmd, "%s -O - %s '%s'", wget_cmd, upds, url);
+		if (f == NULL)
+			goto error;
+		if (!fp_wget_offline)
+			*f = popen(cmd, "r");
+		if (*f == NULL)
+			goto error;
+		*fctx = FCTX_POPEN;
+	}
+	else {
+		char *cdir;
+		cdir = strstr(url, "://");
+		if (cdir == NULL)
+			goto error;
+		cdir += 3;
+
+		{
+			char *end;
+			sprintf(cmd, "%s/%s", cache_path, cdir);
+			end = strrchr(cmd, '/');
+			if (end != NULL) {
+				*end = '\0';
+				if (mkdirp(cmd) != 0)
+					goto error;
+				*end = '/';
+			}
+		}
+
+		if ((!fp_wget_offline) && !(mode & FP_WGET_OFFLINE)) {
+			sprintf(cmd, "%s -O '%s/%s' %s '%s'", wget_cmd, cache_path, cdir, upds, url);
+			system(cmd);
+		}
+		if (f != NULL) {
+			sprintf(cmd, "%s/%s", cache_path, cdir);
+			*f = fopen(cmd, "r");
+			if (*f == NULL)
+				goto error;
+			*fctx = FCTX_FOPEN;
+		}
+		else
+			*fctx = FCTX_NOP;
+	}
+	free(cmd);
+	return 0;
+
+	error:;
+	free(cmd);
+	return -1;
+}
+
+int fp_wget_close(FILE **f, int *fctx)
+{
+	if (*fctx == FCTX_NOP)
+		return 0;
+
+	if (*f == NULL)
+		return -1;
+
+	switch(*fctx) {
+		case FCTX_POPEN:  pclose(*f); *f = NULL; return 0;
+		case FCTX_FOPEN: fclose(*f); *f = NULL; return 0;
+	}
+
+	return -1;
+}
diff --git a/src_plugins/fp_wget/wget_common.h b/src_plugins/fp_wget/wget_common.h
new file mode 100644
index 0000000..c131524
--- /dev/null
+++ b/src_plugins/fp_wget/wget_common.h
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+typedef enum {
+	FP_WGET_UPDATE = 1,  /* wget -c: update existing file, don't replace (a.k.a. cache) */
+	FP_WGET_OFFLINE = 2  /* do not wget, open cached file or fail */
+} fp_get_mode;
+
+int fp_wget_open(const char *url, const char *cache_path, FILE **f, int *fctx, fp_get_mode mode);
+int fp_wget_close(FILE **f, int *fctx);
diff --git a/src_plugins/gl/README b/src_plugins/gl/README
new file mode 100644
index 0000000..eaa783c
--- /dev/null
+++ b/src_plugins/gl/README
@@ -0,0 +1,6 @@
+Common gl functions for hids.
+
+#state: disabled
+#lstate: pcb-rnd has no support for opengl.
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/gl/hidgl.c b/src_plugins/gl/hidgl.c
new file mode 100644
index 0000000..f50b4d2
--- /dev/null
+++ b/src_plugins/gl/hidgl.c
@@ -0,0 +1,723 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2009-2011 PCB Contributers (See ChangeLog for details)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+/* The Linux OpenGL ABI 1.0 spec requires that we define
+ * GL_GLEXT_PROTOTYPES before including gl.h or glx.h for extensions
+ * in order to get prototypes:
+ *   http://www.opengl.org/registry/ABI/
+ */
+#define GL_GLEXT_PROTOTYPES 1
+#ifdef HAVE_OPENGL_GL_H
+#include <OpenGL/gl.h>
+#else
+#include <GL/gl.h>
+#endif
+
+#ifdef HAVE_OPENGL_GLU_H
+#include <OpenGL/glu.h>
+#else
+#include <GL/glu.h>
+#endif
+
+#include "action_helper.h"
+#include "crosshair.h"
+#include "data.h"
+#include "error.h"
+#include "global.h"
+#include "mymem.h"
+#include "clip.h"
+
+#include "hid.h"
+#include "hidgl.h"
+#include "rtree.h"
+
+
+
+triangle_buffer buffer;
+float global_depth = 0;
+
+void hidgl_init_triangle_array(triangle_buffer * buffer)
+{
+	buffer->triangle_count = 0;
+	buffer->coord_comp_count = 0;
+}
+
+void hidgl_flush_triangles(triangle_buffer * buffer)
+{
+	if (buffer->triangle_count == 0)
+		return;
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glVertexPointer(3, GL_FLOAT, 0, buffer->triangle_array);
+	glDrawArrays(GL_TRIANGLES, 0, buffer->triangle_count * 3);
+	glDisableClientState(GL_VERTEX_ARRAY);
+
+	buffer->triangle_count = 0;
+	buffer->coord_comp_count = 0;
+}
+
+void hidgl_ensure_triangle_space(triangle_buffer * buffer, int count)
+{
+	if (count > TRIANGLE_ARRAY_SIZE) {
+		fprintf(stderr, "Not enough space in vertex buffer\n");
+		fprintf(stderr, "Requested %i triangles, %i available\n", count, TRIANGLE_ARRAY_SIZE);
+		exit(1);
+	}
+	if (count > TRIANGLE_ARRAY_SIZE - buffer->triangle_count)
+		hidgl_flush_triangles(buffer);
+}
+
+void hidgl_set_depth(float depth)
+{
+	global_depth = depth;
+}
+
+void hidgl_draw_grid(BoxType * drawn_area)
+{
+	static GLfloat *points = 0;
+	static int npoints = 0;
+	Coord x1, y1, x2, y2, n, i;
+	double x, y;
+
+	if (!Settings.DrawGrid)
+		return;
+
+	x1 = GridFit(MAX(0, drawn_area->X1), PCB->Grid, PCB->GridOffsetX);
+	y1 = GridFit(MAX(0, drawn_area->Y1), PCB->Grid, PCB->GridOffsetY);
+	x2 = GridFit(MIN(PCB->MaxWidth, drawn_area->X2), PCB->Grid, PCB->GridOffsetX);
+	y2 = GridFit(MIN(PCB->MaxHeight, drawn_area->Y2), PCB->Grid, PCB->GridOffsetY);
+
+	if (x1 > x2) {
+		Coord tmp = x1;
+		x1 = x2;
+		x2 = tmp;
+	}
+
+	if (y1 > y2) {
+		Coord tmp = y1;
+		y1 = y2;
+		y2 = tmp;
+	}
+
+	n = (int) ((x2 - x1) / PCB->Grid + 0.5) + 1;
+	if (n > npoints) {
+		npoints = n + 10;
+		points = realloc(points, npoints * 3 * sizeof(GLfloat));
+	}
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glVertexPointer(3, GL_FLOAT, 0, points);
+
+	n = 0;
+	for (x = x1; x <= x2; x += PCB->Grid) {
+		points[3 * n + 0] = x;
+		points[3 * n + 2] = global_depth;
+		n++;
+	}
+	for (y = y1; y <= y2; y += PCB->Grid) {
+		for (i = 0; i < n; i++)
+			points[3 * i + 1] = y;
+		glDrawArrays(GL_POINTS, 0, n);
+	}
+
+	glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+#define MAX_PIXELS_ARC_TO_CHORD 0.5
+#define MIN_SLICES 6
+int calc_slices(float pix_radius, float sweep_angle)
+{
+	float slices;
+
+	if (pix_radius <= MAX_PIXELS_ARC_TO_CHORD)
+		return MIN_SLICES;
+
+	slices = sweep_angle / acosf(1 - MAX_PIXELS_ARC_TO_CHORD / pix_radius) / 2.;
+	return (int) ceilf(slices);
+}
+
+#define MIN_TRIANGLES_PER_CAP 3
+#define MAX_TRIANGLES_PER_CAP 90
+static void draw_cap(Coord width, Coord x, Coord y, Angle angle, double scale)
+{
+	float last_capx, last_capy;
+	float capx, capy;
+	float radius = width / 2.;
+	int slices = calc_slices(radius / scale, M_PI);
+	int i;
+
+	if (slices < MIN_TRIANGLES_PER_CAP)
+		slices = MIN_TRIANGLES_PER_CAP;
+
+	if (slices > MAX_TRIANGLES_PER_CAP)
+		slices = MAX_TRIANGLES_PER_CAP;
+
+	hidgl_ensure_triangle_space(&buffer, slices);
+
+	last_capx = radius * cosf(angle * M_PI / 180.) + x;
+	last_capy = -radius * sinf(angle * M_PI / 180.) + y;
+	for (i = 0; i < slices; i++) {
+		capx = radius * cosf(angle * M_PI / 180. + ((float) (i + 1)) * M_PI / (float) slices) + x;
+		capy = -radius * sinf(angle * M_PI / 180. + ((float) (i + 1)) * M_PI / (float) slices) + y;
+		hidgl_add_triangle(&buffer, last_capx, last_capy, capx, capy, x, y);
+		last_capx = capx;
+		last_capy = capy;
+	}
+}
+
+void hidgl_draw_line(int cap, Coord width, Coord x1, Coord y1, Coord x2, Coord y2, double scale)
+{
+	double angle;
+	float deltax, deltay, length;
+	float wdx, wdy;
+	int circular_caps = 0;
+	int hairline = 0;
+
+	if (width == 0.0)
+		hairline = 1;
+
+	if (width < scale)
+		width = scale;
+
+	deltax = x2 - x1;
+	deltay = y2 - y1;
+
+	length = sqrt(deltax * deltax + deltay * deltay);
+
+	if (length == 0) {
+		/* Assume the orientation of the line is horizontal */
+		angle = 0;
+		wdx = -width / 2.;
+		wdy = 0;
+		length = 1.;
+		deltax = 1.;
+		deltay = 0.;
+	}
+	else {
+		wdy = deltax * width / 2. / length;
+		wdx = -deltay * width / 2. / length;
+
+		if (deltay == 0.)
+			angle = (deltax < 0) ? 270. : 90.;
+		else
+			angle = 180. / M_PI * atanl(deltax / deltay);
+
+		if (deltay < 0)
+			angle += 180.;
+	}
+
+	switch (cap) {
+	case Trace_Cap:
+	case Round_Cap:
+		circular_caps = 1;
+		break;
+
+	case Square_Cap:
+	case Beveled_Cap:
+		x1 -= deltax * width / 2. / length;
+		y1 -= deltay * width / 2. / length;
+		x2 += deltax * width / 2. / length;
+		y2 += deltay * width / 2. / length;
+		break;
+	}
+
+	hidgl_ensure_triangle_space(&buffer, 2);
+	hidgl_add_triangle(&buffer, x1 - wdx, y1 - wdy, x2 - wdx, y2 - wdy, x2 + wdx, y2 + wdy);
+	hidgl_add_triangle(&buffer, x1 - wdx, y1 - wdy, x2 + wdx, y2 + wdy, x1 + wdx, y1 + wdy);
+
+	/* Don't bother capping hairlines */
+	if (circular_caps && !hairline) {
+		draw_cap(width, x1, y1, angle, scale);
+		draw_cap(width, x2, y2, angle + 180., scale);
+	}
+}
+
+#define MIN_SLICES_PER_ARC 6
+#define MAX_SLICES_PER_ARC 360
+void hidgl_draw_arc(Coord width, Coord x, Coord y, Coord rx, Coord ry, Angle start_angle, Angle delta_angle, double scale)
+{
+	float last_inner_x, last_inner_y;
+	float last_outer_x, last_outer_y;
+	float inner_x, inner_y;
+	float outer_x, outer_y;
+	float inner_r;
+	float outer_r;
+	float cos_ang, sin_ang;
+	float start_angle_rad;
+	float delta_angle_rad;
+	float angle_incr_rad;
+	int slices;
+	int i;
+	int hairline = 0;
+
+	if (width == 0.0)
+		hairline = 1;
+
+	if (width < scale)
+		width = scale;
+
+	inner_r = rx - width / 2.;
+	outer_r = rx + width / 2.;
+
+	if (delta_angle < 0) {
+		start_angle += delta_angle;
+		delta_angle = -delta_angle;
+	}
+
+	start_angle_rad = start_angle * M_PI / 180.;
+	delta_angle_rad = delta_angle * M_PI / 180.;
+
+	slices = calc_slices((rx + width / 2.) / scale, delta_angle_rad);
+
+	if (slices < MIN_SLICES_PER_ARC)
+		slices = MIN_SLICES_PER_ARC;
+
+	if (slices > MAX_SLICES_PER_ARC)
+		slices = MAX_SLICES_PER_ARC;
+
+	hidgl_ensure_triangle_space(&buffer, 2 * slices);
+
+	angle_incr_rad = delta_angle_rad / (float) slices;
+
+	cos_ang = cosf(start_angle_rad);
+	sin_ang = sinf(start_angle_rad);
+	last_inner_x = -inner_r * cos_ang + x;
+	last_inner_y = inner_r * sin_ang + y;
+	last_outer_x = -outer_r * cos_ang + x;
+	last_outer_y = outer_r * sin_ang + y;
+	for (i = 1; i <= slices; i++) {
+		cos_ang = cosf(start_angle_rad + ((float) (i)) * angle_incr_rad);
+		sin_ang = sinf(start_angle_rad + ((float) (i)) * angle_incr_rad);
+		inner_x = -inner_r * cos_ang + x;
+		inner_y = inner_r * sin_ang + y;
+		outer_x = -outer_r * cos_ang + x;
+		outer_y = outer_r * sin_ang + y;
+		hidgl_add_triangle(&buffer, last_inner_x, last_inner_y, last_outer_x, last_outer_y, outer_x, outer_y);
+		hidgl_add_triangle(&buffer, last_inner_x, last_inner_y, inner_x, inner_y, outer_x, outer_y);
+		last_inner_x = inner_x;
+		last_inner_y = inner_y;
+		last_outer_x = outer_x;
+		last_outer_y = outer_y;
+	}
+
+	/* Don't bother capping hairlines */
+	if (hairline)
+		return;
+
+	draw_cap(width, x + rx * -cosf(start_angle_rad), y + rx * sinf(start_angle_rad), start_angle, scale);
+	draw_cap(width, x + rx * -cosf(start_angle_rad + delta_angle_rad),
+					 y + rx * sinf(start_angle_rad + delta_angle_rad), start_angle + delta_angle + 180., scale);
+}
+
+void hidgl_draw_rect(Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	glBegin(GL_LINE_LOOP);
+	glVertex3f(x1, y1, global_depth);
+	glVertex3f(x1, y2, global_depth);
+	glVertex3f(x2, y2, global_depth);
+	glVertex3f(x2, y1, global_depth);
+	glEnd();
+}
+
+
+void hidgl_fill_circle(Coord vx, Coord vy, Coord vr, double scale)
+{
+#define MIN_TRIANGLES_PER_CIRCLE 6
+#define MAX_TRIANGLES_PER_CIRCLE 360
+	float last_x, last_y;
+	float radius = vr;
+	int slices;
+	int i;
+
+	slices = calc_slices(vr / scale, 2 * M_PI);
+
+	if (slices < MIN_TRIANGLES_PER_CIRCLE)
+		slices = MIN_TRIANGLES_PER_CIRCLE;
+
+	if (slices > MAX_TRIANGLES_PER_CIRCLE)
+		slices = MAX_TRIANGLES_PER_CIRCLE;
+
+	hidgl_ensure_triangle_space(&buffer, slices);
+
+	last_x = vx + vr;
+	last_y = vy;
+
+	for (i = 0; i < slices; i++) {
+		float x, y;
+		x = radius * cosf(((float) (i + 1)) * 2. * M_PI / (float) slices) + vx;
+		y = radius * sinf(((float) (i + 1)) * 2. * M_PI / (float) slices) + vy;
+		hidgl_add_triangle(&buffer, vx, vy, last_x, last_y, x, y);
+		last_x = x;
+		last_y = y;
+	}
+}
+
+#define MAX_COMBINED_MALLOCS 2500
+static void *combined_to_free[MAX_COMBINED_MALLOCS];
+static int combined_num_to_free = 0;
+
+static GLenum tessVertexType;
+static int stashed_vertices;
+static int triangle_comp_idx;
+
+
+static void myError(GLenum errno)
+{
+	printf("gluTess error: %s\n", gluErrorString(errno));
+}
+
+static void myFreeCombined()
+{
+	while (combined_num_to_free)
+		free(combined_to_free[--combined_num_to_free]);
+}
+
+static void myCombine(GLdouble coords[3], void *vertex_data[4], GLfloat weight[4], void **dataOut)
+{
+#define MAX_COMBINED_VERTICES 2500
+	static GLdouble combined_vertices[3 * MAX_COMBINED_VERTICES];
+	static int num_combined_vertices = 0;
+
+	GLdouble *new_vertex;
+
+	if (num_combined_vertices < MAX_COMBINED_VERTICES) {
+		new_vertex = &combined_vertices[3 * num_combined_vertices];
+		num_combined_vertices++;
+	}
+	else {
+		new_vertex = malloc(3 * sizeof(GLdouble));
+
+		if (combined_num_to_free < MAX_COMBINED_MALLOCS)
+			combined_to_free[combined_num_to_free++] = new_vertex;
+		else
+			printf("myCombine leaking %lu bytes of memory\n", 3 * sizeof(GLdouble));
+	}
+
+	new_vertex[0] = coords[0];
+	new_vertex[1] = coords[1];
+	new_vertex[2] = coords[2];
+
+	*dataOut = new_vertex;
+}
+
+static void myBegin(GLenum type)
+{
+	tessVertexType = type;
+	stashed_vertices = 0;
+	triangle_comp_idx = 0;
+}
+
+static double global_scale;
+
+static void myVertex(GLdouble * vertex_data)
+{
+	static GLfloat triangle_vertices[2 * 3];
+
+	if (tessVertexType == GL_TRIANGLE_STRIP || tessVertexType == GL_TRIANGLE_FAN) {
+		if (stashed_vertices < 2) {
+			triangle_vertices[triangle_comp_idx++] = vertex_data[0];
+			triangle_vertices[triangle_comp_idx++] = vertex_data[1];
+			stashed_vertices++;
+		}
+		else {
+			hidgl_ensure_triangle_space(&buffer, 1);
+			hidgl_add_triangle(&buffer,
+												 triangle_vertices[0], triangle_vertices[1],
+												 triangle_vertices[2], triangle_vertices[3], vertex_data[0], vertex_data[1]);
+
+			if (tessVertexType == GL_TRIANGLE_STRIP) {
+				/* STRIP saves the last two vertices for re-use in the next triangle */
+				triangle_vertices[0] = triangle_vertices[2];
+				triangle_vertices[1] = triangle_vertices[3];
+			}
+			/* Both FAN and STRIP save the last vertex for re-use in the next triangle */
+			triangle_vertices[2] = vertex_data[0];
+			triangle_vertices[3] = vertex_data[1];
+		}
+	}
+	else if (tessVertexType == GL_TRIANGLES) {
+		triangle_vertices[triangle_comp_idx++] = vertex_data[0];
+		triangle_vertices[triangle_comp_idx++] = vertex_data[1];
+		stashed_vertices++;
+		if (stashed_vertices == 3) {
+			hidgl_ensure_triangle_space(&buffer, 1);
+			hidgl_add_triangle(&buffer,
+												 triangle_vertices[0], triangle_vertices[1],
+												 triangle_vertices[2], triangle_vertices[3], triangle_vertices[4], triangle_vertices[5]);
+			triangle_comp_idx = 0;
+			stashed_vertices = 0;
+		}
+	}
+	else
+		printf("Vertex received with unknown type\n");
+}
+
+void hidgl_fill_polygon(int n_coords, Coord * x, Coord * y)
+{
+	int i;
+	GLUtesselator *tobj;
+	GLdouble *vertices;
+
+	assert(n_coords > 0);
+
+	vertices = malloc(sizeof(GLdouble) * n_coords * 3);
+
+	tobj = gluNewTess();
+	gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr) myBegin);
+	gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr) myVertex);
+	gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr) myCombine);
+	gluTessCallback(tobj, GLU_TESS_ERROR, (_GLUfuncptr) myError);
+
+	gluTessBeginPolygon(tobj, NULL);
+	gluTessBeginContour(tobj);
+
+	for (i = 0; i < n_coords; i++) {
+		vertices[0 + i * 3] = x[i];
+		vertices[1 + i * 3] = y[i];
+		vertices[2 + i * 3] = 0.;
+		gluTessVertex(tobj, &vertices[i * 3], &vertices[i * 3]);
+	}
+
+	gluTessEndContour(tobj);
+	gluTessEndPolygon(tobj);
+	gluDeleteTess(tobj);
+
+	myFreeCombined();
+	free(vertices);
+}
+
+void tesselate_contour(GLUtesselator * tobj, PLINE * contour, GLdouble * vertices, double scale)
+{
+	VNODE *vn = &contour->head;
+	int offset = 0;
+
+	/* If the contour is round, and hidgl_fill_circle would use
+	 * less slices than we have vertices to draw it, then call
+	 * hidgl_fill_circle to draw this contour.
+	 */
+	if (contour->is_round) {
+		double slices = calc_slices(contour->radius / scale, 2 * M_PI);
+		if (slices < contour->Count) {
+			hidgl_fill_circle(contour->cx, contour->cy, contour->radius, scale);
+			return;
+		}
+	}
+
+	gluTessBeginPolygon(tobj, NULL);
+	gluTessBeginContour(tobj);
+	do {
+		vertices[0 + offset] = vn->point[0];
+		vertices[1 + offset] = vn->point[1];
+		vertices[2 + offset] = 0.;
+		gluTessVertex(tobj, &vertices[offset], &vertices[offset]);
+		offset += 3;
+	} while ((vn = vn->next) != &contour->head);
+	gluTessEndContour(tobj);
+	gluTessEndPolygon(tobj);
+}
+
+struct do_hole_info {
+	GLUtesselator *tobj;
+	GLdouble *vertices;
+	double scale;
+};
+
+static r_dir_t do_hole(const BoxType * b, void *cl)
+{
+	struct do_hole_info *info = cl;
+	PLINE *curc = (PLINE *) b;
+
+	/* Ignore the outer contour - we draw it first explicitly */
+	if (curc->Flags.orient == PLF_DIR) {
+		return R_DIR_NOT_FOUND;
+	}
+
+	tesselate_contour(info->tobj, curc, info->vertices, info->scale);
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static GLint stencil_bits;
+static int dirty_bits = 0;
+static int assigned_bits = 0;
+
+/* FIXME: JUST DRAWS THE FIRST PIECE.. TODO: SUPPORT FOR FULLPOLY POLYGONS */
+void hidgl_fill_pcb_polygon(PolygonType * poly, const BoxType * clip_box, double scale)
+{
+	int vertex_count = 0;
+	PLINE *contour;
+	struct do_hole_info info;
+	int stencil_bit;
+
+	info.scale = scale;
+	global_scale = scale;
+
+	if (poly->Clipped == NULL) {
+		fprintf(stderr, "hidgl_fill_pcb_polygon: poly->Clipped == NULL\n");
+		return;
+	}
+
+	stencil_bit = hidgl_assign_clear_stencil_bit();
+	if (!stencil_bit) {
+		printf("hidgl_fill_pcb_polygon: No free stencil bits, aborting polygon\n");
+		return;
+	}
+
+	/* Flush out any existing geoemtry to be rendered */
+	hidgl_flush_triangles(&buffer);
+
+	/* Walk the polygon structure, counting vertices */
+	/* This gives an upper bound on the amount of storage required */
+	for (contour = poly->Clipped->contours; contour != NULL; contour = contour->next)
+		vertex_count = MAX(vertex_count, contour->Count);
+
+	info.vertices = malloc(sizeof(GLdouble) * vertex_count * 3);
+	info.tobj = gluNewTess();
+	gluTessCallback(info.tobj, GLU_TESS_BEGIN, (_GLUfuncptr) myBegin);
+	gluTessCallback(info.tobj, GLU_TESS_VERTEX, (_GLUfuncptr) myVertex);
+	gluTessCallback(info.tobj, GLU_TESS_COMBINE, (_GLUfuncptr) myCombine);
+	gluTessCallback(info.tobj, GLU_TESS_ERROR, (_GLUfuncptr) myError);
+
+	glPushAttrib(GL_STENCIL_BUFFER_BIT);	/* Save the write mask etc.. for final restore */
+	glEnable(GL_STENCIL_TEST);
+	glPushAttrib(GL_STENCIL_BUFFER_BIT |	/* Resave the stencil write-mask etc.., and */
+							 GL_COLOR_BUFFER_BIT);	/* the colour buffer write mask etc.. for part way restore */
+	glStencilMask(stencil_bit);		/* Only write to our stencil bit */
+	glStencilFunc(GL_ALWAYS, stencil_bit, stencil_bit);	/* Always pass stencil test, ref value is our bit */
+	glColorMask(0, 0, 0, 0);			/* Disable writting in color buffer */
+
+	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);	/* Stencil pass => replace stencil value */
+
+	/* Drawing operations now set our reference bit in the stencil buffer */
+
+	r_search(poly->Clipped->contour_tree, clip_box, NULL, do_hole, &info, NULL);
+	hidgl_flush_triangles(&buffer);
+
+	glPopAttrib();								/* Restore the colour and stencil buffer write-mask etc.. */
+
+	glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);	/* This allows us to toggle the bit on any subcompositing bitplane */
+	/* If the stencil test has passed, we know that bit is 0, so we're */
+	/* effectively just setting it to 1. */
+
+	glStencilFunc(GL_GEQUAL, 0, assigned_bits);	/* Pass stencil test if all assigned bits clear, */
+	/* reference is all assigned bits so we set */
+	/* any bits permitted by the stencil writemask */
+
+	/* Drawing operations as masked to areas where the stencil buffer is '0' */
+
+	/* Draw the polygon outer */
+	tesselate_contour(info.tobj, poly->Clipped->contours, info.vertices, scale);
+	hidgl_flush_triangles(&buffer);
+
+	/* Unassign our stencil buffer bit */
+	hidgl_return_stencil_bit(stencil_bit);
+
+	glPopAttrib();								/* Restore the stencil buffer write-mask etc.. */
+
+	gluDeleteTess(info.tobj);
+	myFreeCombined();
+	free(info.vertices);
+}
+
+void hidgl_fill_rect(Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	hidgl_ensure_triangle_space(&buffer, 2);
+	hidgl_add_triangle(&buffer, x1, y1, x1, y2, x2, y2);
+	hidgl_add_triangle(&buffer, x2, y1, x2, y2, x1, y1);
+}
+
+void hidgl_init(void)
+{
+	glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+
+	if (stencil_bits == 0) {
+		printf("No stencil bits available.\n" "Cannot mask polygon holes or subcomposite layers\n");
+		/* TODO: Flag this to the HID so it can revert to the dicer? */
+	}
+	else if (stencil_bits == 1) {
+		printf("Only one stencil bitplane avilable\n" "Cannot use stencil buffer to sub-composite layers.\n");
+		/* Do we need to disable that somewhere? */
+	}
+}
+
+int hidgl_stencil_bits(void)
+{
+	return stencil_bits;
+}
+
+static void hidgl_clean_unassigned_stencil(void)
+{
+	glPushAttrib(GL_STENCIL_BUFFER_BIT);
+	glStencilMask(~assigned_bits);
+	glClearStencil(0);
+	glClear(GL_STENCIL_BUFFER_BIT);
+	glPopAttrib();
+}
+
+int hidgl_assign_clear_stencil_bit(void)
+{
+	int stencil_bitmask = (1 << stencil_bits) - 1;
+	int test;
+	int first_dirty = 0;
+
+	if (assigned_bits == stencil_bitmask) {
+		printf("No more stencil bits available, total of %i already assigned\n", stencil_bits);
+		return 0;
+	}
+
+	/* Look for a bitplane we don't have to clear */
+	for (test = 1; test & stencil_bitmask; test <<= 1) {
+		if (!(test & dirty_bits)) {
+			assigned_bits |= test;
+			dirty_bits |= test;
+			return test;
+		}
+		else if (!first_dirty && !(test & assigned_bits)) {
+			first_dirty = test;
+		}
+	}
+
+	/* Didn't find any non dirty planes. Clear those dirty ones which aren't in use */
+	hidgl_clean_unassigned_stencil();
+	assigned_bits |= first_dirty;
+	dirty_bits = assigned_bits;
+
+	return first_dirty;
+}
+
+void hidgl_return_stencil_bit(int bit)
+{
+	assigned_bits &= ~bit;
+}
+
+void hidgl_reset_stencil_usage(void)
+{
+	assigned_bits = 0;
+	dirty_bits = 0;
+}
diff --git a/src_plugins/gl/hidgl.h b/src_plugins/gl/hidgl.h
new file mode 100644
index 0000000..49325ca
--- /dev/null
+++ b/src_plugins/gl/hidgl.h
@@ -0,0 +1,79 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2009-2011 PCB Contributors (See ChangeLog for details).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_HID_COMMON_HIDGL_H
+#define PCB_HID_COMMON_HIDGL_H
+
+#define TRIANGLE_ARRAY_SIZE 5461
+typedef struct {
+	GLfloat triangle_array[3 * 3 * TRIANGLE_ARRAY_SIZE];
+	unsigned int triangle_count;
+	unsigned int coord_comp_count;
+} triangle_buffer;
+
+extern triangle_buffer buffer;
+extern float global_depth;
+
+void hidgl_init_triangle_array(triangle_buffer * buffer);
+void hidgl_flush_triangles(triangle_buffer * buffer);
+void hidgl_ensure_triangle_space(triangle_buffer * buffer, int count);
+
+static inline void
+hidgl_add_triangle_3D(triangle_buffer * buffer,
+											GLfloat x1, GLfloat y1, GLfloat z1,
+											GLfloat x2, GLfloat y2, GLfloat z2, GLfloat x3, GLfloat y3, GLfloat z3)
+{
+	buffer->triangle_array[buffer->coord_comp_count++] = x1;
+	buffer->triangle_array[buffer->coord_comp_count++] = y1;
+	buffer->triangle_array[buffer->coord_comp_count++] = z1;
+	buffer->triangle_array[buffer->coord_comp_count++] = x2;
+	buffer->triangle_array[buffer->coord_comp_count++] = y2;
+	buffer->triangle_array[buffer->coord_comp_count++] = z2;
+	buffer->triangle_array[buffer->coord_comp_count++] = x3;
+	buffer->triangle_array[buffer->coord_comp_count++] = y3;
+	buffer->triangle_array[buffer->coord_comp_count++] = z3;
+	buffer->triangle_count++;
+}
+
+static inline void
+hidgl_add_triangle(triangle_buffer * buffer, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3)
+{
+	hidgl_add_triangle_3D(buffer, x1, y1, global_depth, x2, y2, global_depth, x3, y3, global_depth);
+}
+
+void hidgl_draw_grid(BoxType * drawn_area);
+void hidgl_set_depth(float depth);
+void hidgl_draw_line(int cap, Coord width, Coord x1, Coord y1, Coord x2, Coord y2, double scale);
+void hidgl_draw_arc(Coord width, Coord vx, Coord vy, Coord vrx, Coord vry, Angle start_angle, Angle delta_angle, double scale);
+void hidgl_draw_rect(Coord x1, Coord y1, Coord x2, Coord y2);
+void hidgl_fill_circle(Coord vx, Coord vy, Coord vr, double scale);
+void hidgl_fill_polygon(int n_coords, Coord * x, Coord * y);
+void hidgl_fill_pcb_polygon(PolygonType * poly, const BoxType * clip_box, double scale);
+void hidgl_fill_rect(Coord x1, Coord y1, Coord x2, Coord y2);
+
+void hidgl_init(void);
+int hidgl_stencil_bits(void);
+int hidgl_assign_clear_stencil_bit(void);
+void hidgl_return_stencil_bit(int bit);
+void hidgl_reset_stencil_usage(void);
+
+#endif /* PCB_HID_COMMON_HIDGL_H  */
diff --git a/src_plugins/gl/todo.tmpasm b/src_plugins/gl/todo.tmpasm
new file mode 100644
index 0000000..b0cb9a5
--- /dev/null
+++ b/src_plugins/gl/todo.tmpasm
@@ -0,0 +1,6 @@
+#LIBGTK_GL_SRCS= \
+#	hid/gtk/gtkhid-gl.c
+
+#GL_SRCS= \
+#	hid/common/hidgl.c \
+#	hid/common/hidgl.h
diff --git a/src_plugins/gpmi/Makefile b/src_plugins/gpmi/Makefile
new file mode 100644
index 0000000..b2da266
--- /dev/null
+++ b/src_plugins/gpmi/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_pcb_gpmi
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/gpmi/Plug.tmpasm b/src_plugins/gpmi/Plug.tmpasm
new file mode 100644
index 0000000..847c9d9
--- /dev/null
+++ b/src_plugins/gpmi/Plug.tmpasm
@@ -0,0 +1,102 @@
+append /local/pcb/pcb_gpmi/enable {}
+append /local/pcb/pcb_gpmi/buildin {}
+
+switch /local/pcb/gpmi/controls
+case {disable}
+	put /local/pcb/TOPVARS     {}
+	put /local/pcb/CLEANRULES  {}
+	end
+default
+
+append /local/pcb/RULES [@
+### gpmi_plugin
+clean_gpmi: FORCE
+	cd $(PCB_GPMI) && make clean
+
+$(PLUGIDIR)/pcb-rnd-gpmi: $(PCB_GPMI)/gpmi_plugin/gpmi_plugin.o FORCE
+	$(MKDIR) $(PLUGIDIR)
+	$(MKDIR) $(PLUGIDIR)/pcb-rnd-gpmi
+	$(CP) $(PLUGDIR)/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/*.so $(PLUGIDIR)/pcb-rnd-gpmi
+
+# HACK: the gpmi plugin, for historical reasons, have an own build system,
+# need to go there to properly build it with prefixes.
+$(PCB_GPMI)/gpmi_plugin/gpmi_plugin.o:
+	cd $(PCB_GPMI)/gpmi_plugin && make
+@]
+
+
+append /local/pcb/all   [@ $(PLUGIDIR)/pcb-rnd-gpmi @]
+
+append /local/pcb/CLEANRULES  {clean_gpmi}
+
+append /local/pcb/TOPVARS [@
+PCB_GPMI=$(PLUGDIR)/gpmi/pcb-gpmi
+@]
+
+append /local/pcb/rules/install     {	cd $(PCB_GPMI) && make install} {
+}
+append /local/pcb/rules/linstall    {	cd $(PCB_GPMI) && make linstall} {
+}
+append /local/pcb/rules/uninstall   {	cd $(PCB_GPMI) && make uninstall} {
+}
+end
+end
+
+
+switch /local/pcb/gpmi/controls
+	case {buildin}
+		append /local/pcb/RULES [@
+
+mod_pcb_gpmi: all
+
+@]
+
+		append /local/pcb/LIBS        {$(PCB_GPMI)/gpmi_plugin/gpmi_buildin.a}
+		append /local/pcb/EXEDEPS     {$(PCB_GPMI)/gpmi_plugin/gpmi_buildin.a}
+		append /local/pcb/LDFLAGS      /target/libs/script/gpmi/ldflags
+
+		append /local/pcb/buildin_init_extern    [@extern pcb_uninit_t hid_gpmi_init();@] {\n}
+
+
+		append /local/pcb/buildin_init_code    [@
+	uninit_func = hid_gpmi_init();
+	plugin_register("gpmi", "<gpmi>", NULL, 0, uninit_func);
+@]  {\n}
+
+
+#		append /local/pcb/CFLAGS   /target/libs/script/gpmi/cflags
+#		append /local/pcb/LDFLAGS  /target/libs/script/gpmi/ldflags
+#		append /local/pcb/LIBS     /target/libs/script/gpmi/libs
+
+		append /local/pcb/RULES [@
+
+$(PCB_GPMI)/gpmi_plugin/gpmi_buildin.a: FORCE
+	cd $(PCB_GPMI)/gpmi_plugin && make all_buildin
+
+@]
+
+		end
+	case {plugin}
+		append /local/pcb/all   [@ $(PLUGIDIR)/gpmi_plugin.so @]
+		append /local/pcb/RULES [@
+
+mod_pcb_gpmi: $(PLUGIDIR)/gpmi_plugin.so
+
+$(PLUGIDIR)/gpmi_plugin.so: $(PCB_GPMI)/gpmi_plugin/gpmi_plugin.so
+	$(MKDIR) $(PLUGIDIR)
+	$(CP) $(PCB_GPMI)/gpmi_plugin/gpmi_plugin.so $(PLUGIDIR)/gpmi_plugin.so
+
+@]
+
+		append /local/pcb/CLEANFILES [@ $(PLUGIDIR)/gpmi_plugin.so @]
+
+		append /local/pcb/RULES [@
+
+$(PCB_GPMI)/gpmi_plugin/gpmi_plugin.so: FORCE
+	cd $(PCB_GPMI)/gpmi_plugin && make all_plugin
+
+@]
+		end
+	case {disable}
+		end
+end
diff --git a/src_plugins/gpmi/README b/src_plugins/gpmi/README
new file mode 100644
index 0000000..f4ed7af
--- /dev/null
+++ b/src_plugins/gpmi/README
@@ -0,0 +1,8 @@
+Scriptable plugin system with about 10 scripting languages supported and
+dynamic load/unload of scripts that can manipulate the GUI, the board,
+can implement exporters, etc.
+
+#state: works
+#default: buildin
+#ldefault: if gpmi is installed
+#implements: (feature)
diff --git a/src_plugins/gpmi/pcb-gpmi/Makefile b/src_plugins/gpmi/pcb-gpmi/Makefile
new file mode 100644
index 0000000..7dfe4e4
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/Makefile
@@ -0,0 +1,28 @@
+PCB_GPMI_ROOT=.
+
+all:
+	cd gpmi_plugin; make all
+
+include ../../../Makefile.conf
+PLUGIN_DIR=$(LIBDIR)/plugins
+
+clean:
+	cd gpmi_plugin; make clean
+
+test: all
+	cd host_lib; ./test.sh
+
+
+# TODO: temporary code until gpmi-config is fixed to generate install rules
+install:
+	-mkdir -p $(PLUGIN_DIR)/pcb-rnd-gpmi/
+	if test -f gpmi_plugin/gpmi_plugin.so; then cp gpmi_plugin/gpmi_plugin.so $(PLUGIN_DIR)/gpmi_plugin.so; fi
+	cp `ls gpmi_plugin/gpmi_pkg/*.h gpmi_plugin/gpmi_pkg/*.so` $(PLUGIN_DIR)/pcb-rnd-gpmi/
+
+linstall:
+	-mkdir -p $(PLUGIN_DIR)/pcb-rnd-gpmi/
+	-if test -f gpmi_plugin/gpmi_plugin.so; then ln -sf "`pwd`/gpmi_plugin/gpmi_plugin.so" "$(PLUGIN_DIR)/gpmi_plugin.so"; fi
+	-for n in `ls gpmi_plugin/gpmi_pkg/*.h gpmi_plugin/gpmi_pkg/*.so`; do ln -sf "`pwd`/$$n"  "$(PLUGIN_DIR)/pcb-rnd-gpmi/`basename $$n`"; done
+
+uninstall:
+	rm -rf $(PLUGIN_DIR)/pcb-rnd-gpmi
diff --git a/src_plugins/gpmi/pcb-gpmi/Makefile.config.in b/src_plugins/gpmi/pcb-gpmi/Makefile.config.in
new file mode 100644
index 0000000..3d3504b
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/Makefile.config.in
@@ -0,0 +1,16 @@
+print [@
+### Generated by scconfig, do not edit ###
+
+###TODO1###
+PCB_SRC=$(PCB_GPMI_ROOT)/../../..
+
+PCB_CFLAGS= \
+ @/local/global_cflags@ \
+ @/target/libs/sul/glib/cflags@ \
+ @/target/libs/script/gpmi/cflags@ \
+ -I$(PCB_SRC)/src_3rd \
+ -I$(PCB_SRC)/src_3rd/liblihata
+
+PCB_LDFLAGS= @/target/libs/script/gpmi/ldflags@
+
+@]
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/Makefile b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/Makefile
new file mode 100644
index 0000000..de0d2d5
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/Makefile
@@ -0,0 +1,37 @@
+PCB_GPMI_ROOT=..
+include Makefile.gpmi
+include $(PCB_GPMI_ROOT)/Makefile.config
+
+CFLAGS += -Wall -g -I$(PCB_SRC) $(PCB_CFLAGS)
+
+COMMON_OBJS = scripts.o manage_scripts.o
+BUILDIN_OBJS = gpmi_buildin.o $(COMMON_OBJS)
+PLUGIN_OBJS = gpmi_plugin.o $(COMMON_OBJS)
+
+all: gpmi_plugin.so gpmi_buildin.a
+	cd gpmi_pkg; make
+
+all_plugin: gpmi_plugin.so
+	cd gpmi_pkg; make
+
+all_buildin: gpmi_buildin.a
+	cd gpmi_pkg; make
+
+gpmi_buildin.a: $(BUILDIN_OBJS)
+	ar rvu gpmi_buildin.a $(BUILDIN_OBJS)
+
+gpmi_plugin.so: $(PLUGIN_OBJS)
+	$(CC) $(LDFLAGS) -shared -rdynamic  -o gpmi_plugin.so $(PLUGIN_OBJS) $(PCB_LDFLAGS)
+
+gpmi_plugin.o: gpmi_plugin.c scripts.h
+
+gpmi_buildin.o: gpmi_plugin.c scripts.h
+	$(CC) -DPLUGIN_INIT_NAME=hid_gpmi_init $(CFLAGS) -c gpmi_plugin.c -o gpmi_buildin.o
+
+scripts.o: scripts.c
+
+manage_scripts.o: manage_scripts.c
+
+clean:
+	rm gpmi_plugin.o gpmi_plugin.so gpmi_buildin.a gpmi_buildin.o $(COMMON_OBJS) 2>/dev/null ; true
+	cd gpmi_pkg; make clean
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/Makefile.am b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/Makefile.am
new file mode 100644
index 0000000..fa07517
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/Makefile.am
@@ -0,0 +1,15 @@
+# This file is package-specific. Do include it in the distribution.
+#
+# GPMI fingerprint: Makefile.am.ext-pkg
+
+# End INST_PATH with /
+INST_PATH = pcb-gpmi/
+FILE_NAME = actions
+
+SOURCE  = actions.c
+HEADER  = actions.h
+
+PKG_LDFLAGS = 
+
+include ../../../Makefile.config
+INCLUDES = -I $(PCB_SRC)
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/Makefile.dep b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/Makefile.dep
new file mode 100644
index 0000000..d2f87c1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/Makefile.dep
@@ -0,0 +1 @@
+#Please run make depend!
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.c
new file mode 100644
index 0000000..a267c87
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.c
@@ -0,0 +1,102 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gpmi.h>
+#include "src/global.h"
+#include "src/hid.h"
+#include "src/error.h"
+#include "actions.h"
+#include "src/hid_actions.h"
+#include "src/compat_misc.h"
+#include "../../gpmi_plugin.h"
+
+typedef struct acontext_s  acontext_t;
+
+struct acontext_s {
+	HID_Action action;
+	char *name;
+	gpmi_module *module;
+	acontext_t *next;
+};
+
+static int action_argc = 0;
+static const char **action_argv = NULL;
+
+const char *action_arg(int argn)
+{
+	if ((argn < 0) || (argn >= action_argc))
+		return NULL;
+	return action_argv[argn];
+}
+
+
+static int action_cb(int argc, const char **argv, Coord x, Coord y)
+{
+	acontext_t *ctx = (acontext_t *)current_action;
+	int action_argc_old;
+	const char **action_argv_old;
+
+	/* save argc/argv for action_arg() */
+	action_argc_old = action_argc;
+	action_argv_old = action_argv;
+	action_argc = argc;
+	action_argv = argv;
+
+	/* call event */
+	gpmi_event(ctx->module, ACTE_action, ctx->name, argc, x, y);
+
+	/* restore argc/argv of action_arg() */
+	action_argc = action_argc_old;
+	action_argv = action_argv_old;
+
+	return 0;
+}
+
+static void cleanup_action(gpmi_module *mod, gpmi_cleanup *cl)
+{
+	acontext_t *ctx = cl->argv[0].p;
+	hid_remove_action(&ctx->action);
+	free((char *)ctx->action.name);
+	if (ctx->action.need_coord_msg != NULL)
+		free((char *)ctx->action.need_coord_msg);
+	free((char *)ctx->action.description);
+	free((char *)ctx->action.syntax);
+	free(ctx);
+}
+
+int action_register(const char *name, const char *need_xy, const char *description, const char *syntax)
+{
+	acontext_t *ctx;
+	
+
+	if ((need_xy != NULL) && (*need_xy == '\0'))
+		need_xy = NULL;
+
+
+	ctx = malloc(sizeof(acontext_t));
+	ctx->action.name           = pcb_strdup(name);
+	ctx->action.need_coord_msg = pcb_strdup_null(need_xy);
+	ctx->action.description    = pcb_strdup(description);
+	ctx->action.syntax         = pcb_strdup(syntax);
+	ctx->action.trigger_cb     = action_cb;
+	ctx->name                  = pcb_strdup(name);
+	ctx->module                = gpmi_get_current_module();
+	ctx->next                  = NULL;
+
+	hid_register_action(&ctx->action, gpmi_cookie, 0);
+
+	gpmi_mod_cleanup_insert(ctx->module, cleanup_action, "p", ctx);
+
+	printf("registered.\n");
+	return 0;
+}
+
+int action(const char *cmdline)
+{
+	return hid_parse_command(cmdline);
+}
+
+void create_menu(const char *path, const char *action, const char *mnemonic, const char *hotkey, const char *tooltip)
+{
+	gui->create_menu(path, action, mnemonic, hotkey, tooltip, "TODO#2");
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.h
new file mode 100644
index 0000000..1d3e7b1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.h
@@ -0,0 +1,56 @@
+#include <gpmi.h>
+#include "src/pcb-printf.h"
+
+#undef snprintf
+#define snprintf pcb_snprintf
+
+/* Generated when an action registered by the script is executed.
+   Arguments:
+    name: name of the action (as registed using function action_register())
+    argc: number of arguments. Arguments can be accessed using function action_arg
+    x, y: optional coords, if need_xy was not empty at action_register */
+gpmi_define_event(ACTE_action)(const char *name, int argc, int x, int y);
+
+/* Generated right after gui initialization, before the gui main loop.
+   Arguments:
+    argc: number of arguments the gui was initialized with.
+    argv[]: arguments the gui was initialized with - unaccessible for the scripts. */
+gpmi_define_event(ACTE_gui_init)(int argc, char **argv);
+
+
+/* Generated right before unloading a script to give the script a chance
+   to clean up.
+   Arguments:
+    conffile: the name of the config file that originally triggered laoding the script, or empty if the script was loaded from the gui. */
+gpmi_define_event(ACTE_unload)(const char *conffile);
+
+/* Register an action in PCB - when the action is executed, event
+   ACTE_action is generated with the action name.
+   Multiple actions can be registered. Any action registered by the script
+   will trigger an ACTE_event sent to the script.
+   Arguments:
+    name: name of the action
+    need_xy: the question the user is asked when he needs to choose a coordinate; if empty, no coordinate is asked
+    description: description of the action (for the help)
+    syntax: syntax of the action (for the help)
+   Returns 0 on success.
+ */
+int action_register(const char *name, const char *need_xy, const char *description, const char *syntax);
+
+/* extract the (argn)th event argument for the current action (makes sense only in an ACTE_action event handler */
+const char *action_arg(int argn);
+
+/* call an existing action using PCB syntax (e.g. foo(1, 2, 3))
+   Returns non-zero on error; generally returns value of the action
+   (which is also non-zero on error). */
+int action(const char *cmdline);
+
+/* Create a new menu or submenu at path. Missing parents are created
+   automatically with empty action, mnemonic, hotkey and tooltip.
+   Arguments:
+    path: the full path of the new menu
+    action: this action is executed when the user clicks on the menu
+    mnemonic: which letter to underline in the menu text (will be the fast-jump-there key once the menu is open)
+    hotkey: when this key is pressed in the main gui, the action is also triggered; the format is modifiers<Key>letter, where modifiers is Alt, Shift or Ctrl. This is the same syntax that is used in the .res files.
+    tooltip: short help text */
+void create_menu(const char *path, const char *action, const char *mnemonic, const char *hotkey, const char *tooltip);
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/gpmi.conf b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/gpmi.conf
new file mode 100644
index 0000000..b8f19ff
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/gpmi.conf
@@ -0,0 +1,18 @@
+@/local/headers
+	actions.h
+
+@/local/srcs
+	actions.c
+
+@/local/file_name
+	actions
+
+@/local/inst_path
+	actions
+
+@/local/CFLAGS
+	-I.. -I$(PCB_SRC) $(PCB_CFLAGS)  -D###/target/sys/class###
+
+@@/local/hook/postall
+	PCB_GPMI_ROOT=../../..
+	include $(PCB_GPMI_ROOT)/Makefile.config
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/gpmi/package.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/gpmi/package.c
new file mode 100644
index 0000000..c079706
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/gpmi/package.c
@@ -0,0 +1,19 @@
+#include <gpmi.h>
+
+int PKG_FUNC(init)()
+{
+	return 0;
+}
+
+void PKG_FUNC(uninit)() {
+}
+
+void PKG_FUNC(register)() {
+}
+
+int PKG_FUNC(checkver)(int requested) {
+	if (requested <= 1)
+		return 0;
+
+	return -1;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/Makefile.am b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/Makefile.am
new file mode 100644
index 0000000..0edeea1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/Makefile.am
@@ -0,0 +1,17 @@
+# This file is package-specific. Do include it in the distribution.
+#
+# GPMI fingerprint: Makefile.am.ext-pkg
+
+# End INST_PATH with /
+INST_PATH = pcb-gpmi/
+FILE_NAME = coordgeo
+
+SOURCE  = coordgeo.c
+HEADER  = coordgeo.h
+
+PKG_LDFLAGS = -lm
+
+include ../../../Makefile.config
+INCLUDES = -I $(PCB_SRC)
+
+test: test.o coordgeo.o
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/Makefile.dep b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/Makefile.dep
new file mode 100644
index 0000000..d2f87c1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/Makefile.dep
@@ -0,0 +1 @@
+#Please run make depend!
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.c
new file mode 100644
index 0000000..5a16681
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.c
@@ -0,0 +1,580 @@
+#include <stdlib.h>
+#include <math.h>
+#include "coordgeo.h"
+#include "src/compat_misc.h"
+
+cg_obj_t *cg_objs = NULL;
+int cg_objs_used = 0, cg_objs_alloced = 0;
+
+#define DEGMPY 10000.0
+#define PI 3.141592654
+
+#define OBJ(i) cg_objs[i]
+#define OBJ_VALID(i) ((i >= 0) && (i < cg_objs_used))
+
+/* todo */
+#define invalid -0xFFFF
+
+static int oalloc()
+{
+	int id;
+	if (cg_objs_used >= cg_objs_alloced) {
+		cg_objs_alloced = cg_objs_used + 128;
+		cg_objs = realloc(cg_objs, sizeof(cg_obj_t) * cg_objs_alloced);
+	}
+	id = cg_objs_used;
+	cg_objs_used++;
+	return id;
+}
+
+static void ofree(int id)
+{
+	/* todo */
+}
+
+int cg_new_object(cg_obj_type_t type, int x1, int y1, int x2, int y2, int r)
+{
+	int id;
+
+	id = oalloc();
+	OBJ(id).type = type;
+	OBJ(id).x1 = x1;
+	OBJ(id).y1 = y1;
+	OBJ(id).x2 = x2;
+	OBJ(id).y2 = y2;
+	OBJ(id).r = r;
+	return id;
+}
+
+static inline int cg_clone_object_(cg_obj_t *o)
+{
+	int id;
+
+	id = oalloc();
+	OBJ(id) = *o;
+	return id;
+}
+
+int cg_clone_object(int objID)
+{
+	return cg_clone_object_(&OBJ(objID));
+}
+
+/* destroys an object by ID */
+void cg_destroy_object(int ID)
+{
+	ofree(ID);
+}
+
+/* -- field accessors -- */
+cg_obj_type_t cg_get_type(int objID)
+{
+	return OBJ(objID).type;
+}
+
+int cg_get_coord(int objID, cg_coord_t coordname)
+{
+	switch(coordname) {
+		case CG_X1: return OBJ(objID).x1;
+		case CG_Y1: return OBJ(objID).y1;
+		case CG_X2: return OBJ(objID).x2;
+		case CG_Y2: return OBJ(objID).y2;
+		case CG_R:  return OBJ(objID).r;
+	}
+	return invalid;
+}
+
+void cg_set_coord(int objID, cg_coord_t coordname, int newvalue)
+{
+	switch(coordname) {
+		case CG_X1: OBJ(objID).x1 = newvalue;
+		case CG_Y1: OBJ(objID).y1 = newvalue;
+		case CG_X2: OBJ(objID).x2 = newvalue;
+		case CG_Y2: OBJ(objID).y2 = newvalue;
+		case CG_R:  OBJ(objID).r  = newvalue;
+	}
+}
+
+
+/* -- object operations -- */
+double cg_dist_(int x1, int y1, int x2, int y2)
+{
+	int dx, dy;
+
+	dx = x2 - x1;
+	dy = y2 - y1;
+	return sqrt(dx * dx + dy * dy);
+}
+
+int cg_solve_quad(double a, double b, double c, double *x1, double *x2)
+{
+	double d;
+
+	d = b * b - 4 * a * c;
+	if (d < 0)
+		return 0;
+	if (d != 0)
+		d = sqrt(d);
+
+	if (x1 != NULL)
+		*x1 = (-b + d) / (2 * a);
+
+	if (x2 != NULL)
+		*x2 = (-b - d) / (2 * a);
+
+	if (d == 0)
+		return 1;
+	return 2;
+}
+
+
+/* return ID to a new object which is parallel obj in distance dist
+   - for a line dist > 0 means a new line right to the original
+     (looking from x1;y1 towards x2;y2)
+   - for arc the same rule applies, looking from angle x2 towards y2
+   - for circle dist > 0 means smaller concentric circle
+   - for point and vector always returns invalid
+*/
+int cg_para_obj(int objID, int dist)
+{
+	cg_obj_t v1, v2;
+	int rid;
+
+	switch(OBJ(objID).type) {
+		case CG_POINT:
+		case CG_VECTOR:
+			return -1;
+		case CG_ARC:
+			/* TODO */
+			abort();
+			break;
+		case CG_CIRCLE:
+			rid = cg_clone_object(objID);
+			OBJ(rid).r -= dist;
+			return rid;
+		case CG_LINE:
+			cg_perp_line_(&OBJ(objID), &v1, OBJ(objID).x1, OBJ(objID).y1, dist);
+			cg_perp_line_(&OBJ(objID), &v2, OBJ(objID).x2, OBJ(objID).y2, dist);
+			rid = oalloc();
+			OBJ(rid).type = CG_LINE;
+			OBJ(rid).x1 = v1.x2;
+			OBJ(rid).y1 = v1.y2;
+			OBJ(rid).x2 = v2.x2;
+			OBJ(rid).y2 = v2.y2;
+			OBJ(rid).r  = 0;
+			return rid;
+	}
+	return -1;
+}
+
+void cg_perp_line_(const cg_obj_t *i, cg_obj_t *o, int xp, int yp, int len)
+{
+	double dx, dy, dl;
+
+	dx = -(i->y2 - i->y1);
+	dy = +(i->x2 - i->x1);
+	dl = sqrt(dx * dx + dy * dy);
+	dx = dx / dl;
+	dy = dy / dl;
+
+	o->type = CG_LINE;
+	o->x1 = xp;
+	o->y1 = yp;
+	o->x2 = pcb_round((double)xp + dx * (double)len);
+	o->y2 = pcb_round((double)yp + dy * (double)len);
+	o->r  = 0;
+}
+
+
+/* return ID to a new line which is perpendicular to line1 and starts at
+   point xp;yp and is len long */
+int cg_perp_line(int line1ID, int xp, int yp, int len)
+{
+	int id;
+
+	id = oalloc();
+	cg_perp_line_(&OBJ(line1ID), &OBJ(id), xp, yp, len);
+
+	return id;
+}
+
+int cg_ison_line_(cg_obj_t *l, int x, int y, int extend)
+{
+	int vx, vy;
+
+	vx = l->x2 - l->x1;
+	vy = l->y2 - l->y1;
+
+	/* is the point on the extended line? */
+	if ((vy * x - vx * y) != (vy * l->x1 - vy * l->y1))
+		return 0;
+
+	if ((!extend) && ((x < l->x1) || (x > l->x2)))
+		return 0;
+	return 1;
+}
+
+int cg_ison_arc_(cg_obj_t *a, int x, int y, int extend)
+{
+	double deg;
+
+	/* is the point on the circle? */
+	if (cg_dist_(a->x1, a->y1, x, y) != a->r)
+		return 0;
+
+	/* for circles (or an extended arc, which is just a circle) this means the point must be on */
+	if (extend || (a->type == CG_CIRCLE))
+		return 1;
+
+	/* check if the point is between start and end angles */
+	deg = atan2(y - a->y1, x - a->x1) * DEGMPY;
+	if ((deg >= a->x2) && (deg >= a->y2))
+		return 1;
+
+	return 0;
+}
+
+int cg_ison_point_(cg_obj_t *p, int x, int y)
+{
+	return (p->x1 == x) && (p->y1 == y);
+}
+
+int cg_ison_(cg_obj_t *o, int x, int y, int extend)
+{
+	switch(o->type) {
+		case CG_POINT:  return cg_ison_point_(o, x, y);
+		case CG_VECTOR: return 1;
+		case CG_LINE:   return cg_ison_line_(o, x, y, extend);
+		case CG_ARC:    return cg_ison_arc_(o, x, y, extend);
+		case CG_CIRCLE: return cg_ison_circle_(o, x, y);
+	}
+
+	/* can't get here */
+	abort();
+	return 0;
+}
+
+/* intersection of 2 lines (always returns 1) */
+int cg_intersect_ll(cg_obj_t *l1, cg_obj_t *l2, cg_obj_t *o)
+{
+	double ua, ub, xi, yi, X1, Y1, X2, Y2, X3, Y3, X4, Y4, tmp;
+
+	/* maths from http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ */
+
+	X1 = l1->x1;
+	X2 = l1->x2;
+	X3 = l2->x1;
+	X4 = l2->x2;
+	Y1 = l1->y1;
+	Y2 = l1->y2;
+	Y3 = l2->y1;
+	Y4 = l2->y2;
+
+	tmp = ((Y4-Y3)*(X2-X1) - (X4-X3)*(Y2-Y1));
+	ua=((X4-X3)*(Y1-Y3) - (Y4-Y3)*(X1-X3)) / tmp;
+	ub=((X2-X1)*(Y1-Y3) - (Y2-Y1)*(X1-X3)) / tmp;
+	xi = X1 + ua * (X2 - X1);
+	yi = Y1 + ua * (Y2 - Y1);
+
+	o->type = CG_LINE;
+	o->x1 = o->x2 = pcb_round(xi);
+	o->y1 = o->y2 = pcb_round(yi);
+	o->r = 0;
+	return 1;
+}
+
+/* intersections of a line and a circle (returns number of possible intersections) */
+int cg_intersect_lc(cg_obj_t *l, cg_obj_t *c, cg_obj_t *o)
+{
+	int x1, y1, vx, vy, a, vx2, ints;
+	double A, B, C, X1, X2, Y1, Y2;
+
+	/* first we transform the line to the coordinate system with origo in the
+	   center of the circle - this will simplify the equations */
+	x1 = l->x1 - c->x1;
+	y1 = l->y1 - c->y1;
+	vx = l->x2 - l->x1;
+	vy = l->y2 - l->y1;
+
+	/* some cache */
+	a = vy * x1 - vx * y1;
+	vx2 = vx * vx;
+
+	/* this quadratic equation results in the two intersections, X1 and X2 */
+	A = 1+vy*vy/vx2;
+	B = - 2*a*vy / (vx2);
+	C = a*a / vx2 - c->r * c->r;
+
+	ints = cg_solve_quad(A, B, C, &X1, &X2);
+	if (ints == 0)
+		return 0;
+
+	/* knowing X1 and X2 we can easily get Y1 and Y2 */
+	Y1 = (a - vy * X1) / (-vx);
+	Y2 = (a - vy * X2) / (-vx);
+
+	/* transoform back to the original coordinate system */
+	X1 += c->x1;
+	X2 += c->x1;
+	Y1 += c->y1;
+	Y2 += c->y1;
+
+	/* set up return line and return number of intersections */
+	o->type = CG_LINE;
+	o->x1 = X1;
+	o->y1 = Y1;
+	o->x2 = X2;
+	o->y2 = Y2;
+	o->r  = 0;
+	return ints;
+}
+
+/* intersections of 2 circles (returns number of possible intersections) */
+int cg_intersect_cc(cg_obj_t *c1, cg_obj_t *c2, cg_obj_t *o)
+{
+	double d, a, h;
+	int P2x, P2y;
+
+	d = cg_dist_(c1->x1, c1->y1, c2->x1, c2->y1);
+
+	if (d > c1->r + c2->r)
+		return 0; /* separate */
+	if (d < abs(c1->r - c2->r))
+		return 0; /* contained */
+	if ((d == 0) && (c1->r == c2->r))
+		return -1; /* they are the same circle */
+
+	/* some temps */
+	a = (double)(c1->r * c1->r - c2->r * c2->r + d * d) / (2.0 * d);
+	h = sqrt(c1->r * c1->r - a * a);
+	P2x = c1->x1 + a * (c2->x1 - c1->x1) / d;
+	P2y = c1->y1 + a * (c2->y1 - c1->y1) / d;
+
+	/* final coordinates */
+	o->type = CG_LINE;
+	o->x1 = P2x + h * (c2->y1 - c1->y1) / d;
+	o->y1 = P2y - h * (c2->x1 - c1->x1) / d;
+	o->x2 = P2x - h * (c2->y1 - c1->y1) / d;
+	o->y2 = P2y + h * (c2->x1 - c1->x1) / d;
+
+	if (d == c1->r + c2->r)
+		return 1;
+	return 2;
+}
+
+
+int cg_intersect_(cg_obj_t *i1, cg_obj_t *i2, cg_obj_t *o)
+{
+	switch(i1->type) {
+		case CG_VECTOR:
+			/* invalid */
+			return -1;
+		case CG_POINT:
+			/* TODO: ison */
+			break;
+		case CG_LINE:
+			switch(i2->type) {
+				case CG_VECTOR:
+					/* invalid */
+					return -1;
+				case CG_POINT:
+					/* TODO: ison */
+					break;
+				case CG_LINE:
+					return cg_intersect_ll(i1, i2, o);
+				case CG_CIRCLE:
+				case CG_ARC:
+					return cg_intersect_lc(i1, i2, o);
+			}
+			return -1;
+		case CG_CIRCLE:
+		case CG_ARC:
+			switch(i2->type) {
+				case CG_VECTOR:
+					/* invalid */
+					return -1;
+				case CG_POINT:
+					/* TODO: ison */
+					break;
+				case CG_LINE:
+					return cg_intersect_lc(i2, i1, o);
+				case CG_CIRCLE:
+				case CG_ARC:
+					return cg_intersect_cc(i1, i2, o);
+			}
+			return -1;
+	}
+	return -1;
+}
+
+int cg_intersect(int obj1ID, int obj2ID, int extend)
+{
+	cg_obj_t res;
+	int ints;
+
+	ints = cg_intersect_(&OBJ(obj1ID), &OBJ(obj2ID), &res);
+
+	/* if we needed to extend, we shouldn't care if the intersections
+	   are on the objects. */
+	if (extend)
+		return cg_clone_object_(&res);
+
+
+	while(1) {
+		if (ints == 0)
+			return -1;
+
+		if ((!cg_ison_(&OBJ(obj1ID), res.x1, res.y1, 0)) || (!cg_ison_(&OBJ(obj2ID), res.x1, res.y1, 0))) {
+			/* x1;y1 is not on the objects. make x2;y2 the new x1;y1 and test again */
+			res.x1 = res.x2;
+			res.y1 = res.y2;
+			ints--;
+		}
+		else
+			break;
+	}
+
+	/* x1;y1 is on the objects; check x2;y2 if we still have 2 intersections */
+	if ((ints == 2) && ((!cg_ison_(&OBJ(obj1ID), res.x2, res.y2, 0)) || (!cg_ison_(&OBJ(obj2ID), res.x2, res.y2, 0)))) {
+		/* x2;y2 not on the objects, kill it */
+		res.x2 = res.x1;
+		res.y2 = res.y1;
+		ints = 1;
+	}
+
+	return cg_clone_object_(&res);
+}
+
+/* Truncate an object at x;y. Point x;y must be on the object. Optionally
+   invert which part is kept. Default:
+    - line: keep the part that originates from x1;y1
+    - arc: keep the segment that is closer to the starting angle (x2)
+    - circle: always fails
+    - point: always fails
+    - vector: always fails
+   Returns 0 on success.
+*/
+int cg_truncate_(cg_obj_t *o, int x, int y, int invert_kept)
+{
+	double a;
+
+	/* point is not on the object */
+	if (!cg_ison_(o, x, y, 0))
+		return 1;
+
+	switch(o->type) {
+		case CG_VECTOR:
+		case CG_POINT:
+		case CG_CIRCLE:
+			return 2;
+		case CG_LINE:
+			if (!invert_kept) {
+				o->x2 = x;
+				o->y2 = y;
+			}
+			else {
+				o->x1 = x;
+				o->y1 = y;
+			}
+			break;
+		case CG_ARC:
+			a = atan2(y - o->y1, x - o->x1) * DEGMPY;
+			if (!invert_kept)
+				o->y2 = a;
+			else
+				o->x2 = a;
+			break;
+	}
+	return 0;
+}
+
+int cg_truncate(int objID, int x, int y, int invert_kept)
+{
+	return cg_truncate_(&OBJ(objID), x, y, invert_kept);
+}
+
+/* cut target object with cutting edge object; optionally invert which
+   part is kept of target. Default:
+    - when line cuts, looking from x1;y1 towards x2;y2, right is kept
+    - when circle or arc cuts, we are "walking" clock-wise, right is kept
+      (in short, inner part is kept)
+   Returns 0 on success.
+*/
+int cg_cut_(cg_obj_t *t, cg_obj_t *c, int invert_kept)
+{
+	cg_obj_t intersects;
+	int num_intersects, ni;
+
+	num_intersects = cg_intersect_(t, c, &intersects);
+	if (num_intersects < 1)
+		return 1;
+
+	for(; num_intersects > 0; num_intersects--)
+	{
+		switch(c->type)  {
+			case CG_POINT:
+			case CG_VECTOR:
+				return 1;
+			case CG_LINE:
+				/* TODO: leftof */
+				return 1;
+			case CG_CIRCLE:
+			case CG_ARC:
+				/* TODO: leftof */
+				break;
+		}
+		/* shift intersection */
+		intersects.x1 = intersects.x2;
+		intersects.y1 = intersects.y2;
+	}
+	return 0;
+}
+
+int cg_cut(int targetID, int cutting_edgeID, int invert_kept);
+
+cg_obj_t *cg_get_object(int ID)
+{
+	if (OBJ_VALID(ID))
+		return &OBJ(ID);
+	else
+		return NULL;
+}
+
+int cg_simplify_object_(cg_obj_t *o)
+{
+	int ret = 0; /* no direct returns because CG_ARC can have more than one optimization */
+	switch(o->type) {
+		case CG_LINE:
+			if ((o->x1 == o->x2) && (o->y1 == o->y2)) {
+				o->type = CG_POINT;
+				ret++;
+				break;
+			}
+			break;
+		case CG_ARC:
+			if ((fabs(o->x2 - o->y2) * DEGMPY) >= 2*PI) {
+				o->type = CG_CIRCLE;
+				ret++;
+			}
+			/* intended fall trough for r==0 check */
+		case CG_CIRCLE:
+			if (o->r == 0) {
+				o->type = CG_POINT;
+				ret++;
+				break;
+			}
+			break;
+		default:
+			/* no simplification possible */
+			;
+	}
+	return ret;
+}
+
+
+int cg_simplify_object(int ID)
+{
+	if (OBJ_VALID(ID))
+		return cg_simplify_object_(&OBJ(ID));
+	return -1;
+}
\ No newline at end of file
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.h
new file mode 100644
index 0000000..97230b1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/coordgeo.h
@@ -0,0 +1,127 @@
+#include <gpmi.h>
+#include "src/pcb-printf.h"
+
+#undef snprintf
+#define snprintf pcb_snprintf
+
+typedef enum cg_obj_type_e {
+	CG_POINT,   /* x1;y1 */
+	CG_LINE,    /* from x1;y1 to x2;y2 */
+	CG_VECTOR,  /* x1;y1 (not associated with a point) */
+	CG_CIRCLE,  /* center x1;y1, radius r */
+	CG_ARC      /* center x1;y1, radius r, from x2 rad to y2 rad */
+} cg_obj_type_t;
+
+typedef enum cg_coord_e {
+	CG_X1,
+	CG_Y1,
+	CG_X2,
+	CG_Y2,
+	CG_R
+} cg_coord_t;
+
+
+/* -- object administration -- */
+/* returns ID to the new object */
+int cg_new_object(cg_obj_type_t type, int x1, int y1, int x2, int y2, int r);
+
+/* cloned an existing object and returns ID to the new object */
+int cg_clone_object(int objID);
+
+/* destroys an object by ID */
+void cg_destroy_object(int ID);
+
+/* -- field accessors -- */
+cg_obj_type_t cg_get_type(int objID);
+int cg_get_coord(int objID, cg_coord_t coordname);
+void cg_set_coord(int objID, cg_coord_t coordname, int newvalue);
+
+
+/* -- object operations -- */
+/* return ID to a new object which is parallel obj in distance dist
+   - for a line dist > 0 means a new line right to the original
+     (looking from x1;y1 towards x2;y2)
+   - for arc the same rule applies, looking from angle x2 towards y2
+   - for circle dist > 0 means smaller concentric circle
+   - for point and vector always returns invalid
+*/
+int cg_para_obj(int objID, int dist);
+
+/* return ID to a new line which is perpendicular to line1 and starts at
+   point xp;yp and is len long */
+int cg_perp_line(int line1ID, int xp, int yp, int len);
+
+/* returns ID to a line which marks the intersection(s) of two objects or
+   returns -1 if there's no intersection. If there's only one intersection,
+   the start and end point of the line is the same point. With the current
+   primitives, there can not be more than 2 intersections between 2 objects,
+   except when they have infinite amount of intersections:
+    - overlapping lines
+    - overlapping arcs
+    - two circles with the same radius and center
+   If extend is non-zero, the objects are extended during the calculation.
+   For lines this means infinite length, for arcs this means handling them as
+   circles.
+*/
+int cg_intersect(int obj1ID, int obj2ID, int extend);
+
+/* Truncate an object at x;y. Point x;y must be on the object. Optionally
+   invert which part is kept. Default:
+    - line: keep the part that originates from x1;y1
+    - arc: keep the segment that is closer to the starting angle (x2)
+    - circle: always fails
+    - point: always fails
+    - vector: always fails
+   Returns 0 on success.
+*/
+int cg_truncate(int objID, int x, int y, int invert_kept);
+
+/* cut target object with cutting edge object; optionally invert which
+   part is kept of target. Defaults:
+    - when line cuts, looking from x1;y1 towards x2;y2, right is kept
+    - when circle or arc cuts, we are "walking" clock-wise, right is kept
+      (in short, inner part is kept)
+    - vector and point fails
+   Returns 0 on success.
+*/
+int cg_cut(int targetID, int cutting_edgeID, int invert_kept);
+
+/* Convert object to simpler form, if possible:
+    - if x1;y1 and x2;y2 are equal in a line, it is converted to a point
+    - if radius of a circle or arc is zero, it is converted to a point
+    - if an arc is a full circle, convert it to a circle
+   Returns number of simplifications (0, 1 or 2) issued or -1 for
+   invalid object ID.
+*/
+int cg_simplify_object(int ID);
+
+
+/* these are for C API, scripts have no direct access */
+typedef struct cg_obj_s {
+	cg_obj_type_t type;
+	int x1, y1, x2, y2;
+	int r;
+} cg_obj_t;
+
+extern cg_obj_t *cg_objs;
+extern int cg_objs_used, cg_objs_alloced;
+
+nowrap cg_obj_t *cg_get_object(int ID);
+
+/* create a line in o that is perpendicular to i and starts at point xp and yp */
+nowrap void cg_perp_line_(const cg_obj_t *i, cg_obj_t *o, int xp, int yp, int len);
+
+/* distance */
+nowrap double cg_dist_(int x1, int y1, int x2, int y2);
+
+/* is point on the object? Extend means the object is extended (lines become
+   infinite long, arcs become circles). Vector always returns 1. */
+nowrap int cg_ison_(cg_obj_t *o, int x, int y, int extend);
+nowrap int cg_ison_line_(cg_obj_t *l, int x, int y, int extend);
+nowrap int cg_ison_arc_(cg_obj_t *a, int x, int y, int extend);
+nowrap int cg_ison_point_(cg_obj_t *p, int x, int y);
+#define cg_ison_circle_(c, x, y) cg_ison_arc_(c, x, y, 1)
+
+/* these calls are the low level versions of the ones defined for scripts above */
+nowrap int cg_simplify_object_(cg_obj_t *o);
+
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/gpmi.conf b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/gpmi.conf
new file mode 100644
index 0000000..63afc9c
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/gpmi.conf
@@ -0,0 +1,18 @@
+@/local/headers
+	coordgeo.h
+
+@/local/srcs
+	coordgeo.c
+
+@/local/file_name
+	coordgeo
+
+@/local/inst_path
+	coordgeo
+
+@/local/CFLAGS
+	-I$(PCB_SRC) $(PCB_CFLAGS)  -D###/target/sys/class###
+
+@@/local/hook/postall
+	PCB_GPMI_ROOT=../../..
+	include $(PCB_GPMI_ROOT)/Makefile.config
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/gpmi/package.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/gpmi/package.c
new file mode 100644
index 0000000..c079706
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/gpmi/package.c
@@ -0,0 +1,19 @@
+#include <gpmi.h>
+
+int PKG_FUNC(init)()
+{
+	return 0;
+}
+
+void PKG_FUNC(uninit)() {
+}
+
+void PKG_FUNC(register)() {
+}
+
+int PKG_FUNC(checkver)(int requested) {
+	if (requested <= 1)
+		return 0;
+
+	return -1;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/test.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/test.c
new file mode 100644
index 0000000..e2bfc01
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/coordgeo/test.c
@@ -0,0 +1,199 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "coordgeo.h"
+
+#define POINT_MARK_SIZE 2
+#define PS_SCALE 3
+#define PS_MARGIN 8
+
+#define SCALE(v) ((v+PS_MARGIN) * PS_SCALE)
+
+void line(FILE *ps, int x1, int y1, int x2, int y2)
+{
+	fprintf(ps, "%d %d moveto\n", SCALE(x1), SCALE(y1));
+	fprintf(ps, "%d %d lineto\n", SCALE(x2), SCALE(y2));
+}
+
+void color(FILE *ps, float r, float g, float b)
+{
+	static int colorspace=0;
+
+	if (colorspace == 0) {
+		fprintf(ps, "/DeviceRGB setcolorspace\n");
+		colorspace = 1;
+	}
+	fprintf(ps, "%f %f %f setcolor\n", r, g, b);
+}
+
+
+FILE *ps_start(const char *fn)
+{
+	FILE *f;
+
+	f = fopen(fn, "w");
+	if (f == NULL)
+		return NULL;
+
+	fprintf(f, "%%!\n");
+
+	return f;
+}
+
+int ps_draw(FILE *ps, int ID)
+{
+	cg_obj_t *o;
+	o = cg_get_object(ID);
+	assert(o != NULL);
+	fprintf(ps, "newpath\n");
+
+	switch(o->type) {
+		case CG_POINT:
+			fprintf(ps, "0.1 setlinewidth\n");
+			line(ps, o->x1-POINT_MARK_SIZE, o->y1, o->x1+POINT_MARK_SIZE, o->y1);
+			line(ps, o->x1, o->y1-POINT_MARK_SIZE, o->x1, o->y1+POINT_MARK_SIZE);
+			break;
+		case CG_LINE:
+			fprintf(ps, "0.1 setlinewidth\n");
+			line(ps, o->x1, o->y1, o->x2, o->y2);
+			break;
+		case CG_VECTOR:
+			fprintf(ps, "0.1 setlinewidth\n");
+			line(ps, o->x1, o->y1, o->x2, o->y2);
+			break;
+		case CG_CIRCLE:
+			fprintf(ps, "0.1 setlinewidth\n");
+			fprintf(ps, "%d %d %d %d %d arc\n", SCALE(o->x1), SCALE(o->y1), o->r*PS_SCALE, 0, 360);
+			break;
+		case CG_ARC:
+			fprintf(ps, "0.1 setlinewidth\n");
+			fprintf(ps, "%d %d %d %d %d arc\n", SCALE(o->x1), SCALE(o->y1), o->r*PS_SCALE, o->x2, o->y2);
+			break;
+	}
+
+	fprintf(ps, "stroke\n");	
+	return ID;
+}
+
+void ps_end(FILE *ps)
+{
+	fprintf(ps, "showpage\n");
+	fclose(ps);
+}
+
+void test_primitives()
+{
+	FILE *ps;
+
+	ps = ps_start("primitives.ps");
+	cg_destroy_object(ps_draw(ps, cg_new_object(CG_LINE, 0, 0, 10, 10, 0)));
+	cg_destroy_object(ps_draw(ps, cg_new_object(CG_VECTOR, 20, 0, 30, 10, 0)));
+	cg_destroy_object(ps_draw(ps, cg_new_object(CG_POINT, 40, 5, 0, 0, 0)));
+	cg_destroy_object(ps_draw(ps, cg_new_object(CG_CIRCLE, 70, 10, 0, 0, 5)));
+	cg_destroy_object(ps_draw(ps, cg_new_object(CG_ARC, 90, 10, 0, 45, 5)));
+	ps_end(ps);
+}
+
+void test_parallels()
+{
+	FILE *ps;
+	int obj;
+
+	ps = ps_start("parallels.ps");
+
+	obj = ps_draw(ps, cg_new_object(CG_LINE, 0, 0, 10, 10, 0));
+	cg_destroy_object(ps_draw(ps, cg_para_obj(obj, 4)));
+	cg_destroy_object(ps_draw(ps, cg_para_obj(obj, -4)));
+	cg_destroy_object(obj);
+
+	obj = ps_draw(ps, cg_new_object(CG_CIRCLE, 70, 10, 0, 0, 5));
+	cg_destroy_object(ps_draw(ps, cg_para_obj(obj, 4)));
+	cg_destroy_object(ps_draw(ps, cg_para_obj(obj, -4)));
+	cg_destroy_object(obj);
+
+/*	obj = ps_draw(ps, cg_new_object(CG_ARC, 90, 10, 0, 45, 5));
+	cg_destroy_object(ps_draw(ps, cg_para_obj(obj, 4)));
+	cg_destroy_object(ps_draw(ps, cg_para_obj(obj, -4)));
+	cg_destroy_object(obj);*/
+
+
+	ps_end(ps);
+
+}
+
+void test_perps()
+{
+	FILE *ps;
+	int obj;
+
+	ps = ps_start("perpendicular.ps");
+
+	obj = ps_draw(ps, cg_new_object(CG_LINE, 0, 0, 10, 10, 0));
+	cg_destroy_object(ps_draw(ps, cg_perp_line(obj, 0,0,4)));
+	cg_destroy_object(ps_draw(ps, cg_perp_line(obj, 10,10,-4)));
+	cg_destroy_object(obj);
+
+	ps_end(ps);
+
+}
+
+void test_intersect()
+{
+	FILE *ps;
+	int o1, o2, i;
+	cg_obj_t *oi;
+
+	ps = ps_start("intersect.ps");
+
+	/* two lines, intersecting */
+	color(ps, 0, 0, 0);
+	o1 = ps_draw(ps, cg_new_object(CG_LINE, 0, 0, 10, 10, 0));
+	o2 = ps_draw(ps, cg_new_object(CG_LINE, 5, 0, 6, 13, 0));
+	color(ps, 1, 0, 0);
+	cg_simplify_object(i = cg_intersect(o1, o2, 1));
+	cg_destroy_object(ps_draw(ps, i));
+	cg_destroy_object(o1);
+	cg_destroy_object(o2);
+
+	/* two lines, no visible intersection */
+	color(ps, 0, 0, 0);
+	o1 = ps_draw(ps, cg_new_object(CG_LINE, 20, 0, 30, 10, 0));
+	o2 = ps_draw(ps, cg_new_object(CG_LINE, 25, 0, 32, 13, 0));
+	color(ps, 1, 0, 0);
+	cg_simplify_object(i = cg_intersect(o1, o2, 1));
+	cg_destroy_object(ps_draw(ps, i));
+	cg_destroy_object(o1);
+	cg_destroy_object(o2);
+
+	/* circle vs. line */
+	color(ps, 0, 0, 0);
+	o1 = ps_draw(ps, cg_new_object(CG_LINE,   40, 0, 55, 15, 0));
+	o2 = ps_draw(ps, cg_new_object(CG_CIRCLE, 45, 8, 0, 0, 6));
+	color(ps, 1, 0, 0);
+	cg_simplify_object(i = cg_intersect(o1, o2, 1));
+	cg_destroy_object(ps_draw(ps, i));
+	cg_destroy_object(o1);
+	cg_destroy_object(o2);
+
+	/* circle vs. circle */
+	color(ps, 0, 0, 0);
+	o1 = ps_draw(ps, cg_new_object(CG_CIRCLE, 69, 6, 0, 0, 4));
+	o2 = ps_draw(ps, cg_new_object(CG_CIRCLE, 65, 8, 0, 0, 6));
+	color(ps, 1, 0, 0);
+	cg_simplify_object(i = cg_intersect(o1, o2, 1));
+	cg_destroy_object(ps_draw(ps, i));
+	cg_destroy_object(o1);
+	cg_destroy_object(o2);
+
+
+	ps_end(ps);
+}
+
+
+int main()
+{
+	test_primitives();
+	test_parallels();
+	test_perps();
+	test_intersect();
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/Makefile.am b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/Makefile.am
new file mode 100644
index 0000000..d60137e
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/Makefile.am
@@ -0,0 +1,16 @@
+# This file is package-specific. Do include it in the distribution.
+#
+# GPMI fingerprint: Makefile.am.ext-pkg
+
+# End INST_PATH with /
+INST_PATH = pcb-gpmi/
+FILE_NAME = dialogs
+
+SOURCE  = dialogs.c
+HEADER  = dialogs.h
+
+PKG_LDFLAGS = 
+
+PCB_GPMI_ROOT=../../..
+include $(PCB_GPMI_ROOT)/Makefile.config
+INCLUDES = -I.. -I../.. -I $(PCB_SRC)
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/Makefile.dep b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/Makefile.dep
new file mode 100644
index 0000000..d2f87c1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/Makefile.dep
@@ -0,0 +1 @@
+#Please run make depend!
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/dialogs.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/dialogs.c
new file mode 100644
index 0000000..0fa73f0
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/dialogs.c
@@ -0,0 +1,91 @@
+#include "dialogs.h"
+#include "src/error.h"
+#include "src/hid_attrib.h"
+
+extern HID *gui;
+
+void dialog_log(const char *msg)
+{
+	if (gui == NULL)
+		fprintf(stderr, "couldn't find gui for log: \"%s\"\n", msg);
+	else
+		Message(PCB_MSG_DEFAULT, "%s", msg);
+}
+
+#define empty(s) (((s) == NULL) || ((*s) == '\0'))
+int dialog_confirm(const char *msg, const char *ok, const char *cancel)
+{
+	if (gui == NULL) {
+		fprintf(stderr, "couldn't find gui for dialog_confirm: \"%s\"\n", msg);
+		return -1;
+	}
+
+	if (empty(ok))
+		ok = NULL;
+	if (empty(cancel))
+		cancel = NULL;
+
+	return gui->confirm_dialog(msg, cancel, ok, NULL);
+}
+#undef empty
+
+void dialog_report(const char *title, const char *msg)
+{
+	if (gui == NULL)
+		fprintf(stderr, "couldn't find gui for dialog_report: \"%s\" \"%s\"\n", title, msg);
+	else
+		gui->report_dialog(title, msg);
+}
+
+dynamic char *dialog_prompt(const char *msg, const char *default_)
+{
+	if (gui == NULL) {
+		fprintf(stderr, "couldn't find gui for dialog_prompt: \"%s\" \"%s\"\n", msg, default_);
+		return NULL;
+	}
+	else
+		return gui->prompt_for(msg, default_);
+}
+
+dynamic char *dialog_fileselect(const char *title, const char *descr, char *default_file, char *default_ext, const char *history_tag, multiple dialog_fileselect_t flags)
+{
+	if (gui == NULL) {
+		fprintf(stderr, "couldn't find gui for dialog_fileselect\n");
+		return NULL;
+	}
+	else
+		return gui->fileselect(title, descr, default_file, default_ext, history_tag, flags);
+}
+
+void dialog_beep(void)
+{
+	if (gui == NULL)
+		fprintf(stderr, "couldn't find gui for dialog_beep\n");
+	else
+		gui->beep();
+}
+
+int dialog_progress(int so_far, int total, const char *message)
+{
+	if (gui == NULL) {
+		fprintf(stderr, "couldn't find gui for dialog_process: %d/%d \"%s\"\n", so_far, total, message);
+		return -1;
+	}
+	return gui->progress(so_far, total, message);
+}
+
+int dialog_attribute(hid_t *hid, const char *title, const char *descr)
+{
+	if (gui == NULL) {
+		fprintf(stderr, "couldn't find gui for dialog_attribute: \"%s\" \"%s\"\n", title, descr);
+		return -1;
+	}
+
+	if (hid->result != NULL) {
+		/* TODO: free string fields to avoid memleaks */
+	}
+	else
+		hid->result = calloc(sizeof(HID_Attribute), hid->attr_num);
+
+	return gui->attribute_dialog(hid->attr, hid->attr_num, hid->result, title, descr);
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/dialogs.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/dialogs.h
new file mode 100644
index 0000000..26201d5
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/dialogs.h
@@ -0,0 +1,78 @@
+#include <gpmi.h>
+#include "src/global.h"
+#define FROM_PKG
+#include "hid/hid.h"
+#include "src/pcb-printf.h"
+
+#undef snprintf
+#define snprintf pcb_snprintf
+
+/* Filter on what files a file select dialog should list */
+typedef enum dialog_fileselect_e {
+	FS_NONE      = 0, /* none of the below */
+	FS_READ      = 1, /* when the selected file will be read, not written (HID_FILESELECT_READ) */
+	FS_NOT_EXIST = 2, /* the function calling hid->fileselect will deal with the case when the selected file already exists.  If not given, then the gui will prompt with an "overwrite?" prompt.  Only used when writing. (HID_FILESELECT_MAY_NOT_EXIST)  */
+	FS_TEMPLATE  = 4  /* the call is supposed to return a file template (for gerber output for example) instead of an actual file.  Only used when writing. (HID_FILESELECT_IS_TEMPLATE) */
+} dialog_fileselect_t;
+gpmi_keyword *kw_dialog_fileselect_e; /* of dialog_fileselect_t */
+
+/* Append a msg to the log (log window and/or stderr). */
+void dialog_log(const char *msg);
+
+/* Ask the user for confirmation (usually using a popup). Returns 0 for
+   cancel and 1 for ok.
+   Arguments:
+     msg: message to the user
+     ok: label of the OK button
+     cancel: label of the cancel button
+  Arguments "ok" and "cancel" may be empty (or NULL) in which
+  case the GUI will use the default (perhaps localized) labels for
+  those buttons. */
+int dialog_confirm(const char *msg, const char *ok, const char *cancel);
+
+
+/* Pop up a report dialog.
+   Arguments:
+     title: title of the window
+     msg: message */
+void dialog_report(const char *title, const char *msg);
+
+/* Ask the user to input a string (usually in a popup).
+   Arguments:
+     msg: message or question text
+     default_: default answer (this may be filled in on start)
+   Returns the answer. */
+dynamic char *dialog_prompt(const char *msg, const char *default_);
+
+/* Pops up a file selection dialog.
+   Arguments:
+     title: window title
+     descr: description
+     default_file_
+     default_ext: default file name extension
+     history_tag
+     flags: one or more flags (see below)
+   Returns the selected file or NULL (empty). */
+dynamic char *dialog_fileselect(const char *title, const char *descr, char *default_file_, char *default_ext, const char *history_tag, multiple dialog_fileselect_t flags);
+
+/* Audible beep */
+void dialog_beep(void);
+
+/* Request the GUI hid to draw a progress bar.
+   Arguments:
+     int so_far: achieved state
+     int total: maximum state
+     const char *message: informs the users what they are waiting for
+   If so_far is bigger than total, the progress bar is closed.
+   Returns nonzero if the user wishes to cancel the operation.
+*/
+int dialog_progress(int so_far, int total, const char *message);
+
+
+/* Pop up an attribute dialog; content (widgets) of the dialog box are coming
+   from hid (see the hid package).
+   Arguments:
+     hid: widgets
+     title: title of the window
+     descr: descripting printed in the dialog */
+int dialog_attribute(hid_t *hid, const char *title, const char *descr);
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/gpmi.conf b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/gpmi.conf
new file mode 100644
index 0000000..442a4c1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/dialogs/gpmi.conf
@@ -0,0 +1,18 @@
+@/local/headers
+	dialogs.h
+
+@/local/srcs
+	dialogs.c
+
+@/local/file_name
+	dialogs
+
+@/local/inst_path
+	dialogs
+
+@/local/CFLAGS
+	-I.. -I$(PCB_SRC) $(PCB_CFLAGS)  -D###/target/sys/class###
+
+@@/local/hook/postall
+	PCB_GPMI_ROOT=../../..
+	include $(PCB_GPMI_ROOT)/Makefile.config
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.am b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.am
new file mode 100644
index 0000000..663adfa
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.am
@@ -0,0 +1,13 @@
+# GPMI fingerprint: Makefile.am.ext-pkg
+
+# End INST_PATH with /
+INST_PATH = pcb-gpmi/
+FILE_NAME = hid
+
+SOURCE  = hid.c
+SOURCE += hid_callbacks.c
+
+HEADER  = hid.h
+HEADER += hid_events.h
+include ../../../Makefile.config
+INCLUDES = -I $(PCB_SRC)
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.dep b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.dep
new file mode 100644
index 0000000..d2f87c1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/Makefile.dep
@@ -0,0 +1 @@
+#Please run make depend!
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi.conf b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi.conf
new file mode 100644
index 0000000..d3656a4
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi.conf
@@ -0,0 +1,20 @@
+@/local/headers
+	hid.h
+	hid_events.h
+
+@/local/srcs
+	hid.c
+	hid_callbacks.c
+
+@/local/file_name
+	hid
+
+@/local/inst_path
+	hid
+
+@/local/CFLAGS
+	-I.. -I$(PCB_SRC) $(PCB_CFLAGS)  -D###/target/sys/class###
+
+@@/local/hook/postall
+	PCB_GPMI_ROOT=../../..
+	include $(PCB_GPMI_ROOT)/Makefile.config
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi/package.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi/package.c
new file mode 100644
index 0000000..c079706
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/gpmi/package.c
@@ -0,0 +1,19 @@
+#include <gpmi.h>
+
+int PKG_FUNC(init)()
+{
+	return 0;
+}
+
+void PKG_FUNC(uninit)() {
+}
+
+void PKG_FUNC(register)() {
+}
+
+int PKG_FUNC(checkver)(int requested) {
+	if (requested <= 1)
+		return 0;
+
+	return -1;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.c
new file mode 100644
index 0000000..0f2304c
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.c
@@ -0,0 +1,308 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gpmi.h>
+#include "src/global.h"
+#include "src/error.h"
+#include "src/hid.h"
+#include "src/hid_nogui.h"
+#include "src/pcb-printf.h"
+#include "src/hid_attrib.h"
+#include "src/hid_init.h"
+#include "src/compat_misc.h"
+
+#define FROM_PKG
+#include "hid.h"
+#include "hid_callbacks.h"
+
+
+void hid_gpmi_data_set(hid_t *h, void *data)
+{
+	h->hid->user_context = data;
+}
+
+hid_t *hid_gpmi_data_get(HID *h)
+{
+	if (h == NULL)
+		return NULL;
+	return (hid_t *)h->user_context;
+}
+
+hid_t *hid_create(char *hid_name, char *description)
+{
+	hid_t *h;
+
+	h      = calloc(sizeof(hid_t), 1);
+	h->hid = calloc(sizeof(HID), 1);
+
+	common_nogui_init (h->hid);
+
+	h->module = gpmi_get_current_module();
+
+	h->hid->name        = pcb_strdup(hid_name);
+	h->hid->description = pcb_strdup(description);
+	h->hid->exporter    = 1;
+	h->hid->gui         = 0;
+	h->hid->struct_size = sizeof(HID);
+
+	h->hid->get_export_options = gpmi_hid_get_export_options;
+	h->hid->make_gc            = gpmi_hid_make_gc;
+	h->hid->destroy_gc         = gpmi_hid_destroy_gc;
+	h->hid->do_export          = gpmi_hid_do_export;
+	h->hid->parse_arguments    = gpmi_hid_parse_arguments;
+	h->hid->set_crosshair      = gpmi_hid_set_crosshair;
+	h->hid->set_layer          = gpmi_hid_set_layer;
+	h->hid->set_color          = gpmi_hid_set_color;
+	h->hid->set_line_cap       = gpmi_hid_set_line_cap;
+	h->hid->set_line_width     = gpmi_hid_set_line_width;
+	h->hid->set_draw_xor       = gpmi_hid_set_draw_xor;
+	h->hid->set_draw_faded     = gpmi_hid_set_draw_faded;
+	h->hid->draw_line          = gpmi_hid_draw_line;
+	h->hid->draw_arc           = gpmi_hid_draw_arc;
+	h->hid->draw_rect          = gpmi_hid_draw_rect;
+	h->hid->fill_circle        = gpmi_hid_fill_circle;
+	h->hid->fill_polygon       = gpmi_hid_fill_polygon;
+	h->hid->fill_pcb_polygon   = gpmi_hid_fill_pcb_polygon;
+	h->hid->fill_rect          = gpmi_hid_fill_rect;
+	h->hid->fill_pcb_pv        = gpmi_hid_fill_pcb_pv;
+	h->hid->fill_pcb_pad       = gpmi_hid_fill_pcb_pad;
+	h->hid->use_mask           = gpmi_hid_use_mask;
+
+	h->attr_num = 0;
+	h->attr     = NULL;
+	h->new_gc   = NULL;
+
+	hid_gpmi_data_set(h, h);
+	return h;
+}
+
+dynamic char *hid_get_attribute(hid_t *hid, int attr_id)
+{
+	const char *res;
+	char buff[128];
+	HID_Attr_Val *v;
+
+	if ((hid == NULL) || (attr_id < 0) || (attr_id >= hid->attr_num) || (hid->result == NULL))
+		return 0;
+
+	res = NULL;
+
+	v = &(hid->result[attr_id]);
+	switch(hid->type[attr_id]) {
+		case HIDA_Boolean:
+			if (v->int_value)
+				res = "true";
+			else
+				res = "false";
+			break;
+		case HIDA_Integer:
+			pcb_snprintf(buff, sizeof(buff), "%d", v->int_value);
+			res = buff;
+			break;
+		case HIDA_Real:
+			pcb_snprintf(buff, sizeof(buff), "%f", v->real_value);
+			res = buff;
+			break;
+		case HIDA_String:
+		case HIDA_Label:
+		case HIDA_Path:
+			res = v->str_value;
+			break;
+		case HIDA_Enum:
+			res = hid->attr[attr_id].enumerations[v->int_value];
+/*			printf("res='%s' %d\n", res, v->int_value);*/
+			break;
+		case HIDA_Coord:
+				pcb_sprintf(buff, "%mI", v->coord_value);
+				res = buff;
+				break;
+		case HIDA_Unit:
+			{
+				const Unit *u;
+				double fact;
+				u = get_unit_by_idx(v->int_value);
+				if (u == NULL)
+					fact = 0;
+				else
+					fact = unit_to_factor(u);
+				pcb_snprintf(buff, sizeof(buff), "%f", fact);
+				res = buff;
+/*				fprintf(stderr, "unit idx: %d %p res='%s'\n", v->int_value, (void *)u, res);*/
+			}
+			break;
+		case HIDA_Mixed:
+		default:
+			fprintf(stderr, "error: hid_string2val: can't handle type %d\n", hid->type[attr_id]);
+
+	}
+	if (res == NULL)
+		return NULL;
+	return pcb_strdup(res);
+}
+
+
+HID_Attr_Val hid_string2val(const hid_attr_type_t type, const char *str)
+{
+	HID_Attr_Val v;
+	memset(&v, 0, sizeof(v));
+	switch(type) {
+		case HIDA_Boolean:
+			if ((strcasecmp(str, "true") == 0) || (strcasecmp(str, "yes") == 0) || (strcasecmp(str, "1") == 0))
+				v.int_value = 1;
+			else
+				v.int_value = 0;
+			break;
+		case HIDA_Integer:
+			v.int_value = atoi(str);
+			break;
+		case HIDA_Coord:
+			{
+				char *end;
+				double val;
+				val = strtod(str, &end);
+				while(isspace(*end)) end++;
+				if (*end != '\0') {
+					const Unit *u;
+					u = get_unit_struct(end);
+					if (u == NULL) {
+						Message(PCB_MSG_DEFAULT, "Invalid unit for HIDA_Coord in the script: '%s'\n", end);
+						v.coord_value = 0;
+					}
+					else
+						v.coord_value = unit_to_coord(u, val);
+				}
+				else 
+					v.coord_value = val;
+			}
+			break;
+		case HIDA_Unit:
+			{
+				const Unit *u;
+				u = get_unit_struct(str);
+				if (u != NULL)
+					v.real_value = unit_to_factor(u);
+				else
+					v.real_value = 0;
+			}
+			break;
+		case HIDA_Real:
+			v.real_value = atof(str);
+			break;
+		case HIDA_String:
+		case HIDA_Label:
+		case HIDA_Enum:
+		case HIDA_Path:
+			v.str_value = pcb_strdup(str);
+			break;
+		case HIDA_Mixed:
+		default:
+			fprintf(stderr, "error: hid_string2val: can't handle type %d\n", type);
+	}
+	return v;
+}
+
+char **hid_string2enum(const char *str, HID_Attr_Val *def)
+{
+	char **e;
+	const char *s, *last;
+	int n, len;
+
+	for(n=0, s=str; *s != '\0'; s++)
+		if (*s == '|')
+			n++;
+	e = malloc(sizeof(char *) * (n+2));
+
+	def->int_value = 0;
+	def->str_value = NULL;
+	def->real_value = 0.0;
+
+	for(n = 0, last=s=str;; s++) {
+
+		if ((*s == '|') || (*s == '\0')) {
+			if (*last == '*') {
+				def->int_value = n;
+				last++;
+			}
+			len = s - last;
+			e[n] = malloc(len+1);
+			if (len != 0)
+				strncpy(e[n], last, len);
+			e[n][len] = '\0';
+			last = s+1;
+			n++;
+		}
+		if (*s == '\0')
+			break;
+	}
+	e[n] = NULL;
+	return e;
+}
+
+int hid_add_attribute(hid_t *hid, char *attr_name, char *help, hid_attr_type_t type, int min, int max, char *default_val)
+{
+	int current = hid->attr_num;
+
+	/* TODO: should realloc more space here */
+	hid->attr_num++;
+	hid->attr = realloc(hid->attr, sizeof(HID_Attribute) * hid->attr_num);
+	hid->type = realloc(hid->type, sizeof(hid_attr_type_t) * hid->attr_num);
+
+	hid->attr[current].name         = pcb_strdup(attr_name);
+	hid->attr[current].help_text    = pcb_strdup(help);
+	hid->attr[current].type         = type;
+	hid->attr[current].min_val      = min;
+	hid->attr[current].max_val      = max;
+	if (type == HIDA_Unit) {
+		const Unit *u, *all;
+		all = get_unit_list();
+		u = get_unit_struct(default_val);
+		if (u != NULL)
+			hid->attr[current].default_val.int_value = u-all;
+		else
+			hid->attr[current].default_val.int_value = -1;
+	}
+	else if (type == HIDA_Enum) {
+		hid->attr[current].enumerations = (const char **)hid_string2enum(default_val, &(hid->attr[current].default_val));
+	}
+	else {
+		hid->attr[current].default_val  = hid_string2val(type, default_val);
+		hid->attr[current].enumerations = NULL;
+	}
+	hid->attr[current].hash         = 0;
+
+	hid->type[current] = type;
+
+	return current;
+}
+
+static void cleanup_hid_reg(gpmi_module *mod, gpmi_cleanup *cl)
+{
+	hid_t *hid = cl->argv[0].p;
+	int n;
+
+	hid_remove_hid(hid->hid);
+
+	for(n = 0; n < hid->attr_num; n++) {
+		free((char *)hid->attr[n].name);
+		free((char *)hid->attr[n].help_text);
+	}
+
+	if (hid->attr != NULL)
+		free(hid->attr);
+	if (hid->type != NULL)
+		free(hid->type);
+
+	free((char *)hid->hid->name);
+	free((char *)hid->hid->description);
+	free(hid->hid);
+	free(hid);
+}
+
+int hid_register(hid_t *hid)
+{
+	hid_register_hid(hid->hid);
+
+	gpmi_mod_cleanup_insert(NULL, cleanup_hid_reg, "p", hid);
+
+	return 0;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.h
new file mode 100644
index 0000000..6cde596
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid.h
@@ -0,0 +1,90 @@
+#include <gpmi.h>
+#include "src/pcb-printf.h"
+
+#undef snprintf
+#define snprintf pcb_snprintf
+
+/* Type of an HID attribute (usually a widget on an attribute dialog box) */
+typedef enum hid_attr_type_e {
+	HIDA_Label,    /* non-editable label displayed on the GUI */
+	HIDA_Integer,  /* a sugned integer value */
+	HIDA_Real,     /* a floating point value */
+	HIDA_String,   /* one line textual input */
+	HIDA_Boolean,  /* true/false boolean value */
+	HIDA_Enum,     /* select an item of a predefined list */
+	HIDA_Mixed,    /* TODO */
+	HIDA_Path,     /* path to a file or directory */
+	HIDA_Unit,     /* select a dimension unit */
+	HIDA_Coord     /* enter a coordinate */
+} hid_attr_type_t;
+
+gpmi_keyword *kw_hid_attr_type_e; /* of hid_attr_type_t */
+
+/* TODO: these should not be here; GPMI needs to switch over to c99tree! */
+#ifndef FROM_PKG
+typedef void HID;
+typedef void HID_Attribute;
+typedef void* hidGC;
+typedef char* HID_Attr_Val;
+
+/* Line or arc ending style */
+typedef enum EndCapStyle_e {
+ Trace_Cap,    /* filled circle (trace drawing) */
+ Square_Cap,   /* rectangular lines (square pad) */
+ Round_Cap,    /* round pins or round-ended pads, thermals */
+ Beveled_Cap   /* octagon pins or bevel-cornered pads */
+} EndCapStyle;
+
+typedef void *PolygonType;
+typedef void *BoxType;
+#endif
+
+typedef struct hid_s {
+	gpmi_module *module;
+	int attr_num;
+	HID_Attribute *attr;
+	hid_attr_type_t *type;
+	HID *hid;
+	HID_Attr_Val *result;
+	hidGC new_gc;
+} hid_t;
+
+/* Creates a new hid context. Name and description matters only if the hid is
+registered as an exporter later. */
+hid_t *hid_create(char *hid_name, char *description);
+
+/* Append an attribute in a hid previously created using hid_create().
+   Arguments:
+     hid: hid_t previously created using hid_create()
+     attr_name: name of the attribute
+     help: help text for the attribute
+     type: type of the attribute (input widget type)
+     min: minimum value of the attribute, if type is integer or real)
+     max: maximum value of the attribute, if type is integer or real)
+     default_val: default value of the attribute
+  Returns an unique ID of the attribute the caller should store for
+  later reference. For example this ID is used when retrieving the
+  value of the attribute after the user finished entering data in
+  the dialog. */
+int hid_add_attribute(hid_t *hid, char *attr_name, char *help, hid_attr_type_t type, int min, int max, char *default_val);
+
+/* Query an attribute from the hid after dialog_attributes() returned.
+   Arguments:
+     hid: hid_t previously created using hid_create()
+     attr_id: the unique ID of the attribute (returned by hid_add_attribute())
+   Returns the value (converted to string) set by the user. */
+dynamic char *hid_get_attribute(hid_t *hid, int attr_id);
+
+/* Register the hid; call it after a hid is created and its attributes
+   are all set up */
+int hid_register(hid_t *hid);
+
+/* For internal use */
+void hid_gpmi_data_set(hid_t *h, void *data);
+
+/* For internal use */
+hid_t *hid_gpmi_data_get(HID *h);
+
+/* For internal use */
+nowrap HID_Attr_Val hid_string2val(const hid_attr_type_t type, const char *str);
+
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_callbacks.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_callbacks.c
new file mode 100644
index 0000000..6c821de
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_callbacks.c
@@ -0,0 +1,180 @@
+#include <stdlib.h>
+#include "src/global.h"
+#include "src/hid.h"
+#include "src/data.h"
+#define FROM_PKG
+#include "hid.h"
+#include "hid_events.h"
+#include "hid_callbacks.h"
+#include "src/hid_flags.h"
+#include "src/hid_init.h"
+
+
+/* TODO */
+#define MAX_LAYER 16
+
+HID_Attribute *gpmi_hid_get_export_options(int *num)
+{
+	hid_t *h;
+
+	h = hid_gpmi_data_get(exporter);
+
+	if (h == NULL)
+		return NULL;
+
+	gpmi_event(h->module, HIDE_get_export_options, h);
+
+	if (num != NULL)
+		*num = h->attr_num;
+	return h->attr;
+}
+
+static char *gcs = "abcdefghijklmnopqrstuvxyz";
+hidGC gpmi_hid_make_gc(void)
+{
+	hidGC ret;
+	hid_t *h = hid_gpmi_data_get(exporter);
+
+	/* TODO: fix gc handling... */
+	h->new_gc = (void *)(gcs++);
+	gpmi_event(h->module, HIDE_make_gc, h, h->new_gc);
+	ret = h->new_gc;
+	h->new_gc = NULL;
+	return ret;
+}
+
+void gpmi_hid_destroy_gc(hidGC gc)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_destroy_gc, h, gc);
+}
+
+void gpmi_hid_do_export(HID_Attr_Val * options)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+  int save_ons[MAX_LAYER + 2];
+  BoxType region;
+
+	h->result = options;
+	gpmi_event(h->module, HIDE_do_export_start, h);
+
+	hid_save_and_show_layer_ons(save_ons);
+
+  region.X1 = 0;
+  region.Y1 = 0;
+  region.X2 = PCB->MaxWidth;
+  region.Y2 = PCB->MaxHeight;
+
+	hid_expose_callback(h->hid, &region, 0);
+	hid_restore_layer_ons(save_ons);
+	gpmi_event(h->module, HIDE_do_export_finish, h);
+	h->result = NULL;
+}
+
+void gpmi_hid_parse_arguments(int *pcbargc, char ***pcbargv)
+{
+	/* Do nothing for now */
+	hid_parse_command_line(pcbargc, pcbargv);
+}
+
+void gpmi_hid_set_crosshair(int x, int y, int cursor_action)
+{
+	/* Do nothing */
+}
+
+int gpmi_hid_set_layer(const char *name, int group, int empty)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_set_layer, h, name, group, empty);
+	return 1;
+}
+
+void gpmi_hid_set_color(hidGC gc, const char *name)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_set_color, h, gc, name);
+}
+
+void gpmi_hid_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_set_line_cap, h, gc, style);
+}
+
+void gpmi_hid_set_line_width(hidGC gc, Coord width)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_set_line_width, h, gc, width);
+}
+
+void gpmi_hid_set_draw_xor(hidGC gc, int xor)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_set_draw_xor, h, gc, xor);
+}
+
+void gpmi_hid_set_draw_faded(hidGC gc, int faded)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_set_draw_faded, h, gc, faded);
+}
+
+void gpmi_hid_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_draw_line, h, gc, x1, y1, x2, y2);
+}
+
+void gpmi_hid_draw_arc(hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_draw_arc, h, gc, cx, cy, xradius, yradius, start_angle, delta_angle);
+}
+
+void gpmi_hid_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_draw_rect, h, gc, x1, y1, x2, y2);
+}
+
+void gpmi_hid_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_fill_circle, h, gc, cx, cy, radius);
+}
+
+void gpmi_hid_fill_polygon(hidGC gc, int n_coords, Coord *x, Coord *y)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	/* TODO: need accessor for these */
+	gpmi_event(h->module, HIDE_fill_polygon, h, gc, x, y);
+}
+
+void gpmi_hid_fill_pcb_polygon(hidGC gc, PolygonType *poly, const BoxType *clip_box)
+{
+	/* TODO */
+}
+
+void gpmi_hid_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_fill_rect, h, gc, x1, y1, x2, y2);
+}
+
+void gpmi_hid_use_mask(int use_it)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_use_mask, h, use_it);
+}
+
+void gpmi_hid_fill_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType *pad, pcb_bool drawHole, pcb_bool mask)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_fill_pcb_pv, h, fg_gc, bg_gc, pad, drawHole, mask);
+}
+
+void gpmi_hid_fill_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask)
+{
+	hid_t *h = hid_gpmi_data_get(exporter);
+	gpmi_event(h->module, HIDE_fill_pcb_pad, h, gc, pad, clear, mask);
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_callbacks.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_callbacks.h
new file mode 100644
index 0000000..4f2d7fe
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_callbacks.h
@@ -0,0 +1,22 @@
+HID_Attribute *gpmi_hid_get_export_options(int *num);
+hidGC gpmi_hid_make_gc(void);
+void gpmi_hid_destroy_gc(hidGC gc);
+void gpmi_hid_do_export(HID_Attr_Val * options);
+void gpmi_hid_parse_arguments(int *pcbargc, char ***pcbargv);
+void gpmi_hid_set_crosshair(int x, int y, int cursor_action);
+int gpmi_hid_set_layer(const char *name, int group, int _empty);
+void gpmi_hid_set_color(hidGC gc, const char *name);
+void gpmi_hid_set_line_cap(hidGC gc, EndCapStyle style);
+void gpmi_hid_set_line_width(hidGC gc, Coord width);
+void gpmi_hid_set_draw_xor(hidGC gc, int xor);
+void gpmi_hid_set_draw_faded(hidGC gc, int faded);
+void gpmi_hid_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+void gpmi_hid_draw_arc(hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle);
+void gpmi_hid_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+void gpmi_hid_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+void gpmi_hid_fill_polygon(hidGC gc, int n_coords, Coord *x, Coord *y);
+void gpmi_hid_fill_pcb_polygon(hidGC gc, PolygonType *poly, const BoxType *clip_box);
+void gpmi_hid_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+void gpmi_hid_fill_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType *pad, pcb_bool drawHole, pcb_bool mask);
+void gpmi_hid_fill_pcb_pad(hidGC gc, PadType * pad, pcb_bool clear, pcb_bool mask);
+void gpmi_hid_use_mask(int use_it);
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_events.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_events.h
new file mode 100644
index 0000000..c33bedd
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/hid/hid_events.h
@@ -0,0 +1,63 @@
+/*********** Exporter events ************/
+
+/* Called before get_exporter_options returns the option list to the GUI hid */
+gpmi_define_event(HIDE_get_export_options)(void *hid);
+
+/* Called before export redraw starts */
+gpmi_define_event(HIDE_do_export_start)(void *hid);
+
+/* Called after export redraw finihsed */
+gpmi_define_event(HIDE_do_export_finish)(void *hid);
+
+/* DRAWING */
+
+/* PCB callback events for drawing: change layer */
+gpmi_define_event(HIDE_set_layer)(void *hid, const char *name, int group, int empty);
+
+/* PCB callback events for drawing: change drawing color */
+gpmi_define_event(HIDE_set_color)(void *hid, void *gc, const char *name);
+
+/* PCB callback events for drawing: change drawing line cap style*/
+gpmi_define_event(HIDE_set_line_cap)(void *hid, void *gc, EndCapStyle style);
+
+/* PCB callback events for drawing: change drawing line width */
+gpmi_define_event(HIDE_set_line_width)(void *hid, void *gc, int width);
+
+/* PCB callback events for drawing: toggle xor drawing method */
+gpmi_define_event(HIDE_set_draw_xor)(void *hid, void *gc, int xor);
+
+/* PCB callback events for drawing: toggle faded drawing method */
+gpmi_define_event(HIDE_set_draw_faded)(void *hid, void *gc, int faded);
+
+/* PCB callback events for drawing: draw a line */
+gpmi_define_event(HIDE_draw_line)(void *hid, void *gc, int x1, int y1, int x2, int y2);
+
+/* PCB callback events for drawing: draw an arc from center cx;cy */
+gpmi_define_event(HIDE_draw_arc)(void *hid, void *gc, int cx, int cy, int xradius, int yradius, double start_angle, double delta_angle);
+
+/* PCB callback events for drawing: draw a rectangle */
+gpmi_define_event(HIDE_draw_rect)(void *hid, void *gc, int x1, int y1, int x2, int y2);
+
+/* PCB callback events for drawing: draw a filled circle */
+gpmi_define_event(HIDE_fill_circle)(void *hid, void *gc, int cx, int cy, int radius);
+
+/* PCB callback events for drawing: draw a filled ploygon */
+gpmi_define_event(HIDE_fill_polygon)(void *hid, void *gc, int n_coords, int *x, int *y);
+
+/* PCB callback events for drawing: draw a filled rectangle */
+gpmi_define_event(HIDE_fill_rect)(void *hid, void *gc, int x1, int y1, int x2, int y2);
+
+/* PCB callback events for drawing: TODO */
+gpmi_define_event(HIDE_use_mask)(void *hid, int use_it);
+
+/* PCB callback events for drawing: create a new graphical context */
+gpmi_define_event(HIDE_make_gc)(void *hid, void *gc);
+
+/* PCB callback events for drawing: destroy a graphical context */
+gpmi_define_event(HIDE_destroy_gc)(void *hid, void *gc);
+
+/* PCB callback events for drawing: TODO */
+gpmi_define_event(HIDE_fill_pcb_pv)(void *hid, void *fg_gc, void *bg_gc, void *pad, int drawHole, int mask);
+
+/* PCB callback events for drawing: TODO */
+gpmi_define_event(HIDE_fill_pcb_pad)(void *hid, void *pad, int clear, int mask);
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/Makefile.am b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/Makefile.am
new file mode 100644
index 0000000..ccc0b50
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/Makefile.am
@@ -0,0 +1,20 @@
+# This file is package-specific. Do include it in the distribution.
+#
+# GPMI fingerprint: Makefile.am.ext-pkg
+
+# End INST_PATH with /
+INST_PATH = pcb-gpmi/
+FILE_NAME = layout
+
+SOURCE  = search.c
+SOURCE += object.c
+SOURCE += create.c
+SOURCE += layers.c
+SOURCE += page.c
+
+HEADER  = layout.h
+
+PKG_LDFLAGS = 
+
+include ../../../Makefile.config
+INCLUDES = -I $(PCB_SRC)
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/Makefile.dep b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/Makefile.dep
new file mode 100644
index 0000000..d2f87c1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/Makefile.dep
@@ -0,0 +1 @@
+#Please run make depend!
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/coord.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/coord.c
new file mode 100644
index 0000000..b8f1237
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/coord.c
@@ -0,0 +1,21 @@
+#include "layout.h"
+#include "src/pcb-printf.h"
+#include "src/conf_core.h"
+
+double mil2pcb_multiplier()
+{
+	return 10000.0*2.54;
+}
+
+double mm2pcb_multiplier()
+{
+	return 1000000.0;
+}
+
+const char *current_grid_unit()
+{
+	const Unit *u = conf_core.editor.grid_unit;
+	if (u == NULL)
+		return "";
+	return u->suffix;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/create.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/create.c
new file mode 100644
index 0000000..b86d168
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/create.c
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+#include <assert.h>
+#include "layout.h"
+#include "src/undo.h"
+#include "src/conf_core.h"
+#include "src/layer.h"
+
+
+typedef struct flag_tr_s {
+	int flag; /* flag or thermal */
+	int gpmi, pcb;
+} flag_tr_t;
+
+static flag_tr_t flags[] = {
+#warning TODO: get these from conf
+#if 0
+	{1, FL_SHOWNUMBER,   SHOWNUMBERFLAG},
+	{1, FL_LOCALREF,     LOCALREFFLAG},
+	{1, FL_CHECKPLANS,   CHECKPLANESFLAG},
+	{1, FL_SHOWDRC,      SHOWPCB_FLAG_DRC},
+	{1, FL_RUBBERBAND,   RUBBERBANDFLAG},
+	{1, FL_DESCRIPTION,  DESCRIPTIONFLAG},
+	{1, FL_NAMEONPCB,    NAMEONPCBFLAG},
+	{1, FL_AUTODRC,      AUTOPCB_FLAG_DRC},
+	{1, FL_ALLDIRECTION, ALLDIRECTIONFLAG},
+	{1, FL_SWAPSTARTDIR, SWAPSTARTDIRFLAG},
+	{1, FL_UNIQUENAME,   UNIQUENAMEFLAG},
+	{1, FL_CLEARNEW,     CLEARNEWFLAG},
+	{1, FL_SNAPPIN,      SNAPPCB_FLAG_PIN},
+	{1, FL_SHOWMASK,     SHOWMASKFLAG},
+	{1, FL_THINDRAW,     THINDRAWFLAG},
+	{1, FL_ORTHOMOVE,    ORTHOMOVEFLAG},
+	{1, FL_LIVEROUTE,    LIVEROUTEFLAG},
+	{1, FL_THINDRAWPOLY, THINDRAWPOLYFLAG},
+	{1, FL_LOCKNAMES,    LOCKNAMESFLAG},
+	{1, FL_ONLYNAMES,    ONLYNAMESFLAG},
+	{1, FL_NEWFULLPOLY,  NEWPCB_FLAG_FULLPOLY},
+	{1, FL_HIDENAMES,    HIDENAMESFLAG},
+#endif
+
+	{0, FL_THERMALSTYLE1, 1},
+	{0, FL_THERMALSTYLE2, 2},
+	{0, FL_THERMALSTYLE3, 3},
+	{0, FL_THERMALSTYLE4, 4},
+	{0, FL_THERMALSTYLE5, 5},
+	{0, 0, 0}
+};
+
+static FlagType get_flags(int in)
+{
+	flag_tr_t *f;
+	static FlagType out;
+
+	out.f = 0;
+	memset(out.t, 0, sizeof(out.t));
+	for(f = flags; f->gpmi != 0; f++) {
+		if (in & f->gpmi) {
+			if (f->flag)
+				out.f |= f->pcb;
+			else
+				memset(out.t, f->pcb, sizeof(out.t));
+		}
+	}
+	return out;
+}
+
+static void *layout_create_line_(int x1, int y1, int x2, int y2, int thickness, int clearance, multiple layout_flag_t flags)
+{
+	void *line;
+
+	line = CreateNewLineOnLayer (CURRENT, x1, y1, x2, y2, thickness, clearance, get_flags(flags));
+	if (line != NULL) {
+		AddObjectToCreateUndoList (PCB_TYPE_LINE, CURRENT, line, line);
+		return line;
+	}
+	return NULL;
+}
+
+int layout_create_line(int x1, int y1, int x2, int y2, int thickness, int clearance, multiple layout_flag_t flags)
+{
+	return layout_create_line_(x1, y1, x2, y2, thickness, clearance, flags) != NULL;
+}
+
+static void *layout_create_via_(int x, int y, int thickness, int clearance, int mask, int hole, const char *name, multiple layout_flag_t flags)
+{
+	void *pin;
+
+	pin = CreateNewVia (PCB->Data, x, y, thickness, clearance, mask, hole, name, get_flags(flags));
+
+	if (pin != NULL) {
+		AddObjectToCreateUndoList (PCB_TYPE_VIA, pin, pin, pin);
+		return pin;
+	}
+	return NULL;
+}
+
+int layout_create_via(int x, int y, int thickness, int clearance, int mask, int hole, const char *name, multiple layout_flag_t flags)
+{
+	return layout_create_via_(x, y, thickness, clearance, mask, hole, name, flags) != NULL;
+}
+
+static void *layout_create_arc_(int x, int y, int width, int height, int sa, int dir, int thickness, int clearance, multiple layout_flag_t flags)
+{
+	void *arc;
+	arc = CreateNewArcOnLayer (CURRENT, x, y, width, height, sa, dir, thickness, clearance, get_flags(flags));
+	if (arc != NULL) {
+		AddObjectToCreateUndoList (PCB_TYPE_ARC, CURRENT, arc, arc);
+		return 0;
+	}
+	return NULL;
+}
+
+int layout_create_arc(int x, int y, int width, int height, int sa, int dir, int thickness, int clearance, multiple layout_flag_t flags)
+{
+	return layout_create_arc_(x, y, width, height, sa, dir, thickness, clearance, flags) != NULL;
+}
+
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/debug_draw.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/debug_draw.c
new file mode 100644
index 0000000..fa1c599
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/debug_draw.c
@@ -0,0 +1,49 @@
+#include <stdlib.h>
+#include <assert.h>
+#include "layout.h"
+#include "src/hid.h"
+#include "src/error.h"
+
+static HID *ddh = NULL;
+
+#define need_ddh if (ddh == NULL) return
+
+
+int debug_draw_request(void)
+{
+	ddh = gui->request_debug_draw();
+	if (ddh == NULL)
+		return 0;
+	return 1;
+}
+
+void debug_draw_flush(void)
+{
+	need_ddh;
+	gui->flush_debug_draw();
+}
+
+void debug_draw_finish(dctx_t *ctx)
+{
+	need_ddh;
+	ddh->destroy_gc(ctx->gc);
+	gui->finish_debug_draw();
+	free(ctx);
+	ddh = NULL;
+}
+
+dctx_t *debug_draw_dctx(void)
+{
+	dctx_t *ctx;
+	hidGC gc;
+	need_ddh(NULL);
+	gc = ddh->make_gc();
+	if (gc == NULL) {
+		Message(PCB_MSG_DEFAULT, "debug_draw_dctx(): failed to make a new gc on ddh %p\n", (void *)ddh);
+		return NULL;
+	}
+
+	ctx = malloc(sizeof(dctx_t));
+	ctx->hid = ddh;
+	ctx->gc = gc;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/draw.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/draw.c
new file mode 100644
index 0000000..20a5c6f
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/draw.c
@@ -0,0 +1,45 @@
+#include <stdlib.h>
+#include <assert.h>
+#include "layout.h"
+#include "src/hid.h"
+#include "src/error.h"
+
+#define setup(func) \
+	hidGC gc = ctx->gc; \
+	HID  *hid = ctx->hid; \
+	if ((hid == NULL) && (gc == NULL)) Message(PCB_MSG_DEFAULT, "%s failed because of invalid hid or gc\n", func); \
+	if ((hid == NULL) && (gc == NULL)) return
+
+
+void draw_set_color(dctx_t *ctx, const char *name)
+{
+	setup("draw_set_color");
+	hid->set_color(gc, name);
+}
+
+/*void set_line_cap(dctx_t *ctx, EndCapStyle style_);*/
+void draw_set_line_width(dctx_t *ctx, int width)
+{
+	setup("draw_set_line_width");
+	hid->set_line_width(gc, width);
+}
+
+void draw_set_draw_xor(dctx_t *ctx, int xor)
+{
+	setup("draw_set_draw_xor");
+	hid->set_draw_xor(gc, xor);
+}
+
+
+void draw_set_draw_faded(dctx_t *ctx, int faded)
+{
+	setup("draw_set_draw_faded");
+	hid->set_draw_faded(gc, faded);
+}
+
+void draw_line(dctx_t *ctx, int x1, int y1, int x2, int y2)
+{
+	setup("draw_line");
+	hid->draw_line(gc, x1, y1, x2, y2);
+}
+
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/gpmi.conf b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/gpmi.conf
new file mode 100644
index 0000000..7db65be
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/gpmi.conf
@@ -0,0 +1,18 @@
+@/local/headers
+	layout.h
+
+@/local/srcs
+	create.c layers.c object.c page.c search.c coord.c debug_draw.c draw.c
+
+@/local/file_name
+	layout
+
+@/local/inst_path
+	layout
+
+@/local/CFLAGS
+	-I.. -I$(PCB_SRC) $(PCB_CFLAGS)  -D###/target/sys/class###
+
+@@/local/hook/postall
+	PCB_GPMI_ROOT=../../..
+	include $(PCB_GPMI_ROOT)/Makefile.config
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layers.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layers.c
new file mode 100644
index 0000000..f6e1f93
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layers.c
@@ -0,0 +1,77 @@
+#include <stdlib.h>
+#include <assert.h>
+#include "layout.h"
+#include "src/misc.h"
+#include "src/draw.h"
+#include "src/conf_core.h"
+#include "src/layer.h"
+
+#define layer_check(layer) \
+	if ((layer < 0) || (layer >= MAX_LAYER+2)) \
+		return
+
+
+void layout_switch_to_layer(int layer)
+{
+	layer_check(layer);
+	ChangeGroupVisibility(layer, pcb_true, pcb_true);
+	Redraw();
+}
+
+int layout_get_current_layer()
+{
+	return GetLayerNumber(PCB->Data, CURRENT);
+}
+
+int layout_resolve_layer(const char *name)
+{
+	int n;
+	if (name == NULL)
+		return -2;
+	for(n = 0; n < MAX_LAYER + 2; n++)
+		if ((PCB->Data->Layer[n].Name != NULL) && (strcmp(PCB->Data->Layer[n].Name, name) == 0))
+			return n;
+	return -1;
+}
+
+int layout_get_max_possible_layer()
+{
+	return MAX_LAYER+2;
+}
+
+int layout_get_max_copper_layer()
+{
+	return max_copper_layer;
+}
+
+int layout_get_max_layer()
+{
+	return max_copper_layer+2;
+}
+
+
+const char *layout_layer_name(int layer)
+{
+	layer_check(layer)("");
+	return PCB->Data->Layer[layer].Name;
+}
+
+const char *layout_layer_color(int layer)
+{
+	layer_check(layer)("");
+	return PCB->Data->Layer[layer].Color;
+}
+
+int layout_layer_field(int layer, layer_field_t fld)
+{
+	layer_check(layer)(-1);
+	switch(fld) {
+		case LFLD_NUM_LINES: return linelist_length(&(PCB->Data->Layer[layer].Line));
+		case LFLD_NUM_TEXTS: return textlist_length(&(PCB->Data->Layer[layer].Text));
+		case LFLD_NUM_POLYS: return polylist_length(&(PCB->Data->Layer[layer].Polygon));
+		case LFLD_NUM_ARCS:  return arclist_length(&(PCB->Data->Layer[layer].Arc));
+		case LFLD_VISIBLE:   return PCB->Data->Layer[layer].On;
+		case LFLD_NODRC:     return PCB->Data->Layer[layer].no_drc;
+	}
+	return -1;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layout.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layout.h
new file mode 100644
index 0000000..2ff4e3e
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/layout.h
@@ -0,0 +1,267 @@
+#include <gpmi.h>
+#include "src/global.h"
+#include "src/rtree.h"
+#include "src/create.h"
+#include "src/data.h"
+#include "src/pcb-printf.h"
+
+#undef snprintf
+#define snprintf pcb_snprintf
+
+
+/* Object type search mask bits */
+typedef enum layout_object_mask_e {
+	OM_LINE     = 1,       /* lines (traces, silk lines, not font) */
+	OM_TEXT     = 2,       /* text written using the font */
+	OM_POLYGON  = 4,       /* polygons, including rectangles */
+	OM_ARC      = 8,       /* arcs, circles */
+	OM_VIA      = 16,      /* vias and holes which are not part of a footprint */
+	OM_PIN      = 32,      /* pins/pads of a footprint */
+
+	OM_ANY      = 0xffff   /* shorthand for "find anything" */
+} layout_object_mask_t;
+gpmi_keyword *kw_layout_object_mask_e; /* of layout_object_mask_t */
+
+/* Which coordinate of the object is referenced */
+typedef enum layout_object_coord_e {
+/* X/Y coords */
+	OC_BX1 = 1, /* bounding box X1 */
+	OC_BX2 = 2, /* bounding box X2 */
+	OC_BY1 = 3, /* bounding box Y1 */
+	OC_BY2 = 4, /* bounding box Y2 */
+	OC_P1X = 5, /* point 1 X */
+	OC_P2X = 6, /* point 2 X */
+	OC_P1Y = 7, /* point 1 Y */
+	OC_P2Y = 8, /* point 2 Y */
+	OC_OBJ = 9, /* the whole object */
+
+/* point aliases for X/Y coords */
+	OC_P1  = 5, /* point 1 is P1X*/
+	OC_P2  = 6  /* point 2 is P2X */
+
+} layout_object_coord_t;
+gpmi_keyword *kw_layout_object_coord_e; /* of layout_object_coord_t */
+
+
+typedef enum layout_flag_e {
+	FL_NONE          = 0,
+	FL_SHOWNUMBER    = 0x00000001,
+	FL_LOCALREF      = 0x00000002,
+	FL_CHECKPLANS    = 0x00000004,
+	FL_SHOWDRC       = 0x00000008,
+	FL_RUBBERBAND    = 0x00000010,
+	FL_DESCRIPTION   = 0x00000020,
+	FL_NAMEONPCB     = 0x00000040,
+	FL_AUTODRC       = 0x00000080,
+	FL_ALLDIRECTION  = 0x00000100,
+	FL_SWAPSTARTDIR  = 0x00000200,
+	FL_UNIQUENAME    = 0x00000400,
+	FL_CLEARNEW      = 0x00000800,
+	FL_SNAPPIN       = 0x00001000,
+	FL_SHOWMASK      = 0x00002000,
+	FL_THINDRAW      = 0x00004000,
+	FL_ORTHOMOVE     = 0x00008000,
+	FL_LIVEROUTE     = 0x00010000,
+	FL_THINDRAWPOLY  = 0x00020000,
+	FL_LOCKNAMES     = 0x00040000,
+	FL_ONLYNAMES     = 0x00080000,
+	FL_NEWFULLPOLY   = 0x00100000,
+	FL_HIDENAMES     = 0x00200000,
+
+	FL_THERMALSTYLE1 = 0x08000000,
+	FL_THERMALSTYLE2 = 0x10000000,
+	FL_THERMALSTYLE3 = 0x20000000,
+	FL_THERMALSTYLE4 = 0x40000000,
+	FL_THERMALSTYLE5 = 0x80000000
+} layout_flag_t;
+gpmi_keyword *kw_layout_flag_e; /* of layout_flag_t */
+
+
+
+typedef struct layout_object_s {
+	layout_object_mask_t type;
+	union {
+		LineType    *l;
+		TextType    *t;
+		PolygonType *p;
+		ArcType     *a;
+		PinType     *v;
+		PinType     *pin;
+	} obj;
+	int layer;
+} layout_object_t;
+
+
+typedef struct layout_search_s {
+	layout_object_mask_t searching; /* during the search, this field is used to communicate with the callback */
+	int used, alloced;
+	layout_object_t *objects;
+	int layer;
+} layout_search_t;
+
+/* -- search -- (search.c) */
+/* creates a new search and adds all objects that matches obj_types mask within the given rectangle on the current layer
+   Arguments:
+     search_ID: unique name of the search (overwrites existing search on the same name)
+     obj_types: on or more object types
+     x1, y1, x2, y2: box the search is done within (PCB coords)
+   Returns the number of object on the search list. */
+int layout_search_box(const char *search_ID, layout_object_mask_t obj_types, int x1, int y1, int x2, int y2);
+
+/* creates a new search and adds all selected objects
+   Arguments:
+     search_ID: unique name of the search (overwrites existing search on the same name)
+     obj_types: on or more object types
+   Returns the number of object on the search list. */
+int layout_search_selected(const char *search_ID, multiple layout_object_mask_t obj_types);
+
+/* creates a new search and adds all found objects (the green highlight)
+   Arguments:
+     search_ID: unique name of the search (overwrites existing search on the same name)
+     obj_types: on or more object types
+   Returns the number of object on the search list. */
+int layout_search_found(const char *search_ID, multiple layout_object_mask_t obj_types);
+
+/* Returns the nth object from a search list (or NULL pointer if n is beyond the list) */
+layout_object_t *layout_search_get(const char *search_ID, int n);
+
+/* Frees all memory related to a search. Returns 0 on success.
+   Argument:
+     search_ID: unique name of the search (requires an existing search) */
+int layout_search_free(const char *search_ID);
+
+/* -- object accessors -- (object.c) */
+/* Return the requested coord of an object; except for the bounding box
+    coordinates, the meaning of coordinates are object-specific.
+    Point 1 and point 2 are usually endpoints of the object (line, arc),
+    "the whole object" coordinate is a central point. */
+int layout_obj_coord(layout_object_t *obj, layout_object_coord_t coord);
+
+/* Return the type of an object (always a single bit) */
+layout_object_mask_t layout_obj_type(layout_object_t *obj);
+
+/* Change location of an object or parts of the object (like move endpoint of a line);
+   Arguments:
+     obj: the object
+     coord: which coordinate to drag (e.g. move only the endpoint)
+     dx, dy: relative x and y coordinates the selected coordinate is displaced by
+   Returns 0 on success */
+int layout_obj_move(layout_object_t *obj, layout_object_coord_t coord, int dx, int dy);
+
+/* change angles of an arc; start and delate are relative if relative is non-zero; returns 0 on success */
+int layout_arc_angles(layout_object_t *obj, int relative, int start, int delta);
+
+/* -- create new objects -- (create.c) */
+/* create a line */
+int layout_create_line(int x1, int y1, int x2, int y2, int thickness, int clearance, multiple layout_flag_t flags);
+
+/* same as layout_create_line(), but appends the result to a list and
+   returns the index of the new object on the list (can be used as n for
+   layour_search_get)
+int layout_lcreate_line(const char *search_ID, int x1, int y1, int x2, int y2, int thickness, int clearance, multiple layout_flag_t flags);*/
+
+/* create a named via */
+int layout_create_via(int x, int y, int thickness, int clearance, int mask, int hole, const char *name, multiple layout_flag_t flags);
+
+/* create a new arc; sa is start angle, dir is delta angle */
+int layout_create_arc(int x, int y, int width, int height, int sa, int dir, int thickness, int clearance, multiple layout_flag_t flags);
+
+/* -- layer manipulation -- (layers.c) */
+/* Field name of the layer structure */
+typedef enum layer_field_e {
+	LFLD_NUM_LINES,   /* number of lines on the layer */
+	LFLD_NUM_TEXTS,   /* number of texts on the layer */
+	LFLD_NUM_POLYS,   /* number of polygons on the layer */
+	LFLD_NUM_ARCS,    /* number of arcs on the layer */
+	LFLD_VISIBLE,     /* non-zero if the layer is visible */
+	LFLD_NODRC        /* non-zero if the layer doesn't use DRC */
+} layer_field_t;
+
+/* switch to layer (further layer-specific actions will take place there) */
+void layout_switch_to_layer(int layer);
+
+/* returns the number of the current layer */
+int layout_get_current_layer();
+
+/* resolve layer number by name (case sensitive); returns negative number if not found */
+int layout_resolve_layer(const char *name);
+
+/* return the theoretical number of layers supported by PCB */
+int layout_get_max_possible_layer();
+
+/* return the actual number of copper layers on the current design */
+int layout_get_max_copper_layer();
+
+/* return the actual number of layers on the current design */
+int layout_get_max_layer();
+
+/* return the name of a layer */
+const char *layout_layer_name(int layer);
+
+/* return the color of a layer */
+const char *layout_layer_color(int layer);
+
+/* return an integer field of a layer */
+int layout_layer_field(int layer, layer_field_t fld);
+
+
+/* -- page manipulation -- (page.c) */
+/* query or set width and height of the drawing */
+int layout_get_page_width();
+int layout_get_page_height();
+void layout_set_page_size(int width, int height);
+
+/* -- coordinate system -- (coord.c) */
+double mil2pcb_multiplier();
+double mm2pcb_multiplier();
+const char *current_grid_unit();
+
+typedef struct dctx_s {
+	void *hid;
+	void *gc;
+} dctx_t;
+
+/* -- debug draw GC -- */
+/* Initialize debug drawing; returns 1 if worked, 0 if denied */
+int debug_draw_request(void);
+
+/* Flush the drawing */
+void debug_draw_flush(void);
+
+/* Finish (close) drawing */
+void debug_draw_finish(dctx_t *ctx);
+
+/* Get the draw context of debug draw */
+dctx_t *debug_draw_dctx(void);
+
+/* -- draw on a GC -- */
+
+/* Debug draw style: set drawing color */
+void draw_set_color(dctx_t *ctx, const char *name);
+/*void set_line_cap(dctx_t *ctx, EndCapStyle style_);*/
+
+/* Debug draw style: set line width */
+void draw_set_line_width(dctx_t *ctx, int width);
+
+/* Debug draw style: set whether drawing should happen in xor */
+void draw_set_draw_xor(dctx_t *ctx, int xor);
+
+/* Debug draw style: set whether drawing should happen in faded mode  */
+void draw_set_draw_faded(dctx_t *ctx, int faded);
+
+/* Debug draw: draw a line using the current style settings */
+void draw_line(dctx_t *ctx, int x1_, int y1_, int x2_, int y2_);
+
+/*
+void draw_arc(dctx_t *ctx, int cx_, int cy_, int xradius_, int yradius_, double start_angle_, double delta_angle_);
+void draw_rect(dctx_t *ctx, int x1_, int y1_, int x2_, int y2_);
+void fill_circle(dctx_t *ctx, int cx_, int cy_, int radius_);
+void fill_polygon(dctx_t *ctx, int n_ints_, int *x_, int *y_);
+void fill_pcb_polygon(dctx_t *ctx, PolygonType *poly, const BoxType *clip_box);
+void thindraw_pcb_polygon(dctx_t *ctx, PolygonType *poly, const BoxType *clip_box);
+void fill_pcb_pad(dctx_t *ctx, PadType *pad, pcb_bool clip, pcb_bool mask);
+void thindraw_pcb_pad(dctx_t *ctx, PadType *pad, pcb_bool clip, pcb_bool mask);
+void fill_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType *pv, pcb_bool drawHole, pcb_bool mask);
+void thindraw_pcb_pv(hidGC fg_gc, hidGC bg_gc, PinType *pv, pcb_bool drawHole, pcb_bool mask);
+void fill_rect(dctx_t *ctx, int x1_, int y1_, int x2_, int y2_);
+*/
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/object.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/object.c
new file mode 100644
index 0000000..8a8f3d1
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/object.c
@@ -0,0 +1,145 @@
+#include <stdlib.h>
+#include <assert.h>
+#include "layout.h"
+#include "src/change.h"
+#include "src/misc.h"
+#include "src/move.h"
+#include "src/conf_core.h"
+#include "src/layer.h"
+
+int layout_obj_coord(layout_object_t *obj, layout_object_coord_t coord)
+{
+	AnyObjectType *o;
+
+	if (obj == NULL)
+		return -1;
+
+	o = (AnyObjectType *)obj->obj.l;
+
+	/* bounding box is the same for any type */
+	switch (coord) {
+		case OC_BX1: return o->BoundingBox.X1;
+		case OC_BX2: return o->BoundingBox.X2;
+		case OC_BY1: return o->BoundingBox.Y1;
+		case OC_BY2: return o->BoundingBox.Y2;
+		case OC_OBJ: return -1;
+		default: /* avoids warnings for unhandled requests we handle later, per object type */
+			;
+	}
+
+	switch(obj->type) {
+		case OM_LINE:
+			switch (coord) {
+				case OC_P1X: return obj->obj.l->Point1.X;
+				case OC_P2X: return obj->obj.l->Point2.X;
+				case OC_P1Y: return obj->obj.l->Point1.Y;
+				case OC_P2Y: return obj->obj.l->Point2.Y;
+				default: /* avoids warnings for unhandled requests we handled above */
+				;
+			}
+			break;
+		case OM_TEXT:
+			switch (coord) {
+				case OC_P1X:
+				case OC_P2X: return obj->obj.t->X;
+				case OC_P1Y:
+				case OC_P2Y: return obj->obj.t->Y;
+				default: /* avoids warnings for unhandled requests we handled above */
+				;
+			}
+			break;
+		case OM_VIA:
+			switch (coord) {
+				case OC_P1X:
+				case OC_P2X: return obj->obj.v->X;
+				case OC_P1Y:
+				case OC_P2Y: return obj->obj.v->Y;
+				default: /* avoids warnings for unhandled requests we handled above */
+				;
+			}
+			break;
+		case OM_PIN:
+			switch (coord) {
+				case OC_P1X:
+				case OC_P2X: return obj->obj.pin->X;
+				case OC_P1Y:
+				case OC_P2Y: return obj->obj.pin->Y;
+				default: /* avoids warnings for unhandled requests we handled above */
+				;
+			}
+			break;
+	}
+
+	return -1;
+}
+
+layout_object_mask_t layout_obj_type(layout_object_t *obj)
+{
+	if (obj == NULL)
+		return 0;
+	return obj->type;
+}
+
+int layout_obj_move(layout_object_t *obj, layout_object_coord_t coord, int dx, int dy)
+{
+	void *what = NULL;;
+
+	if (obj == NULL)
+		return -1;
+
+	switch(obj->type) {
+		case OM_LINE:
+			switch(coord) {
+				case OC_OBJ:
+					MoveObject (PCB_TYPE_LINE_POINT, CURRENT, obj->obj.l, &(obj->obj.l->Point2), dx, dy);
+					/* intended falltrough */
+				case OC_P1X:
+				case OC_P1Y: what = &(obj->obj.l->Point1); break;
+				case OC_P2X:
+				case OC_P2Y: what = &(obj->obj.l->Point2); break;
+				default: /* we do not handle anything else for now */
+					;
+			}
+			MoveObject (PCB_TYPE_LINE_POINT, CURRENT, obj->obj.l, what, dx, dy);
+			return 0;
+		case OM_TEXT:
+			MoveObject (PCB_TYPE_TEXT, CURRENT, obj->obj.t, obj->obj.t, dx, dy);
+			return 0;
+		case OM_VIA:
+			MoveObject (PCB_TYPE_VIA, obj->obj.v, obj->obj.v, obj->obj.v, dx, dy);
+			return 0;
+		case OM_PIN:
+			MoveObject (PCB_TYPE_PIN, obj->obj.pin, obj->obj.pin, obj->obj.pin, dx, dy);
+			return 0;
+		case OM_ARC:
+			switch(coord) {
+				case OC_OBJ:
+					MoveObject (PCB_TYPE_ARC, CURRENT, obj->obj.a, obj->obj.a, dx, dy);
+					return 0;
+				default: /* we do not handle anything else for now */
+					;
+			}
+			/*TODO: move endpoints! */
+			break;
+		case OM_POLYGON:
+			if (obj->layer != -1) {
+				MoveObject (PCB_TYPE_POLYGON, PCB->Data->Layer + obj->layer, obj->obj.p, obj->obj.p, dx, dy);
+				return 0;
+			}
+	}
+	return -1;
+}
+
+int layout_arc_angles(layout_object_t *obj, int relative, int start, int delta)
+{
+	if (obj == NULL)
+		return -1;
+	if (obj->type != OM_ARC)
+		return 1;
+	if (relative) {
+		start += obj->obj.a->StartAngle;
+		delta += obj->obj.a->Delta;
+	}
+	ChangeArcAngles (CURRENT, obj->obj.a, start, delta);
+	return 0;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/page.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/page.c
new file mode 100644
index 0000000..4565b8c
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/page.c
@@ -0,0 +1,18 @@
+#include "layout.h"
+#include "src/change.h"
+
+int layout_get_page_width()
+{
+	return PCB->MaxWidth;
+}
+
+int layout_get_page_height()
+{
+	return PCB->MaxHeight;
+}
+
+void layout_set_page_size(int width, int height)
+{
+	ChangePCBSize (MIN(MAX_COORD, MAX(width, MIN_SIZE)), MIN(MAX_COORD, MAX(height, MIN_SIZE)));
+}
+
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/search.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/search.c
new file mode 100644
index 0000000..c0bbb77
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/layout/search.c
@@ -0,0 +1,186 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "src/conf_core.h"
+#include "src/layer.h"
+#include "layout.h"
+#include "config.h"
+
+static inline void search_append(layout_search_t *s, void *obj)
+{
+	layout_object_t *o;
+	if (s->used >= s->alloced) {
+		s->alloced += 256;
+		s->objects = realloc(s->objects, s->alloced * sizeof(layout_object_t));
+	}
+	o = s->objects + s->used;
+	s->used++;
+	o->type = s->searching;
+	o->layer = s->layer;
+	switch(s->searching) {
+		case OM_LINE:     o->obj.l   = obj; break;
+		case OM_TEXT:     o->obj.t   = obj; break;
+		case OM_POLYGON:  o->obj.p   = obj; break;
+		case OM_ARC:      o->obj.a   = obj; break;
+		case OM_VIA:      o->obj.v   = obj; break;
+		case OM_PIN:      o->obj.pin = obj; break;
+		default:
+			assert(!"Unimplemented object type");
+	}
+}
+
+static r_dir_t search_callback (const BoxType * b, void *cl)
+{
+	search_append(cl, (void *)b);
+  return R_DIR_FOUND_CONTINUE;
+}
+
+hash_t *layout_searches = NULL;
+static layout_search_t *new_search(const char *search_ID)
+{
+	layout_search_t *s;
+	layout_search_free(search_ID);
+
+	if (layout_searches == NULL) {
+		layout_searches = hash_create(64, gpmi_hash_ptr);
+		assert(layout_searches != NULL);
+	}
+
+	s = calloc(sizeof(layout_search_t), 1);
+
+	hash_store(layout_searches, search_ID, s);
+	return s;
+}
+
+int layout_search_box(const char *search_ID, layout_object_mask_t obj_types, int x1, int y1, int x2, int y2)
+{
+  BoxType spot;
+	layout_search_t *s = new_search(search_ID);
+
+	spot.X1 = x1;
+	spot.Y1 = y1;
+	spot.X2 = x2;
+	spot.Y2 = y2;
+
+	s->layer = -1;
+	if (obj_types & OM_LINE) {
+		s->searching = OM_LINE;
+		r_search (CURRENT->line_tree, &spot, NULL, search_callback, s, NULL);
+	}
+
+	if (obj_types & OM_TEXT) {
+		s->searching = OM_TEXT;
+		r_search (CURRENT->text_tree, &spot, NULL, search_callback, s, NULL);
+	}
+
+	if (obj_types & OM_ARC) {
+		s->searching = OM_ARC;
+		r_search (CURRENT->arc_tree, &spot, NULL, search_callback, s, NULL);
+	}
+
+	if (obj_types & OM_VIA) {
+		s->searching = OM_VIA;
+		r_search (PCB->Data->via_tree, &spot, NULL, search_callback, s, NULL);
+	}
+
+	if (obj_types & OM_PIN) {
+		s->searching = OM_PIN;
+		r_search (PCB->Data->pin_tree, &spot, NULL, search_callback, s, NULL);
+	}
+
+	if (obj_types & OM_POLYGON) {
+		s->searching = OM_POLYGON;
+		for (s->layer = 0; s->layer < MAX_LAYER + 2; s->layer++)
+			r_search (PCB->Data->Layer[s->layer].polygon_tree, &spot, NULL, search_callback, s, NULL);
+		s->layer = -1;
+	}
+
+	return s->used;
+}
+
+/* PCB_FLAG_SELECTED */
+
+typedef struct {
+	int flag;
+	layout_search_t *search;
+} select_t;
+
+static void select_cb(void *obj_, void *ud)
+{
+	select_t *ctx = ud;
+	AnyObjectTypePtr obj = obj_;
+	if (TEST_FLAG(ctx->flag, obj))
+		search_append(ctx->search, obj);
+}
+
+#define select2(s, om, flag, lst) \
+	do { \
+		gdl_iterator_t it; \
+		void *item; \
+		select_t ctx; \
+		ctx.flag = flag; \
+		ctx.search = s; \
+		s->searching = om; \
+		linelist_foreach(lst, &it, item) select_cb(item, &ctx); \
+		s->searching = 0; \
+	} while(0)
+
+static int layout_search_flag(const char *search_ID, multiple layout_object_mask_t obj_types, int flag)
+{
+	pcb_cardinal_t l, n;
+	layout_search_t *s = new_search(search_ID);
+	LayerType *layer = PCB->Data->Layer;
+
+	for (l =0; l < MAX_LAYER + 2; l++, layer++) {
+		s->layer = l;
+		select2(s, OM_ARC,     flag, &layer->Arc);
+		select2(s, OM_LINE,    flag, &layer->Line);
+		select2(s, OM_TEXT,    flag, &layer->Text);
+		select2(s, OM_POLYGON, flag, &layer->Polygon);
+	}
+	select2(s, OM_VIA,  flag, &PCB->Data->Via);
+/*	select2(s, OM_PIN,  flag, &PCB->Data->Pin); /* TODO */
+
+	return s->used;
+}
+#undef select
+
+int layout_search_selected(const char *search_ID, multiple layout_object_mask_t obj_types)
+{
+	return layout_search_flag(search_ID, obj_types, PCB_FLAG_SELECTED);
+}
+
+int layout_search_found(const char *search_ID, multiple layout_object_mask_t obj_types)
+{
+	return layout_search_flag(search_ID, obj_types, PCB_FLAG_FOUND);
+}
+
+layout_object_t *layout_search_get(const char *search_ID, int n)
+{
+	const layout_search_t *s;
+
+	s = hash_find(layout_searches, search_ID);
+
+/*	printf("s=%p\n", (void *)s);*/
+	if ((s == NULL) || (n < 0) || (n >= s->used))
+		return NULL;
+	return s->objects+n;
+}
+
+int layout_search_free(const char *search_ID)
+{
+	layout_search_t *s;
+
+	if (layout_searches == NULL)
+		return 1;
+
+	s = (layout_search_t *)hash_find(layout_searches, search_ID);
+	if (s != NULL) {
+		hash_del_key(layout_searches, search_ID);
+		free(s->objects);
+		free(s);
+		return 0;
+	}
+	return 2;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c
new file mode 100644
index 0000000..d3e4e21
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c
@@ -0,0 +1,234 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpmi.h>
+#include "src/misc.h"
+#include "src/misc_util.h"
+#include "src/conf_core.h"
+#include "src/event.h"
+#include "src/paths.h"
+#include "src/error.h"
+#include "src/plugins.h"
+#include "src/hid_actions.h"
+#include "scripts.h"
+#include "manage_scripts.h"
+
+const char *gpmi_cookie = "GPMI plugin cookie";
+
+/* This function is used to print a detailed GPMI error message */
+void gpmi_hid_print_error(gpmi_err_stack_t *entry, char *string)
+{
+	Message(PCB_MSG_DEFAULT, "[GPMI] %s\n", string);
+}
+
+
+int gpmi_hid_gui_inited = 0;
+static void ev_gui_init(void *user_data, int argc, event_arg_t *argv[])
+{
+	int ev;
+	char *ev_args;
+	hid_gpmi_script_info_t *i;
+	const char *menu = "/main_menu/Plugins/GPMI scripting/Scripts";
+
+	gui->create_menu(menu, "gpmi_scripts()", "S", "Alt<Key>g", "Manage GPMI scripts", gpmi_cookie);
+
+	ev = gpmi_event_find("ACTE_gui_init", &ev_args);
+	if (ev >= 0) {
+		for(i = hid_gpmi_script_info; i != NULL; i = i->next)
+			if (i->module != NULL)
+				gpmi_event(i->module, ev, argc, argv);
+	}
+	gpmi_hid_gui_inited = 1;
+}
+
+static void cmd_reload(const char *name)
+{
+	hid_gpmi_script_info_t *i;
+	if (name != NULL) {
+		i = hid_gpmi_lookup(name);
+		if (i != NULL)
+			hid_gpmi_reload_module(i);
+		else
+			Message(PCB_MSG_DEFAULT, "Script %s not found\n", name);
+	}
+	else {
+		for(i = hid_gpmi_script_info; i != NULL; i = i->next)
+			hid_gpmi_reload_module(i);
+	}
+}
+
+static int action_gpmi_scripts(int argc, const char **argv, Coord x, Coord y)
+{
+	if (argc == 0) {
+		gpmi_hid_manage_scripts();
+		return 0;
+	}
+	if (strcasecmp(argv[0], "reload") == 0) {
+		if (argc > 1)
+			cmd_reload(argv[1]);
+		else
+			cmd_reload(NULL);
+	}
+	else if (strcasecmp(argv[0], "load") == 0) {
+		if (argc == 3) {
+			if (hid_gpmi_load_module(NULL, argv[1], argv[2], NULL) == NULL)
+				Message(PCB_MSG_DEFAULT, "Failed to load %s %s\n", argv[1], argv[2]);
+		}
+		else
+			Message(PCB_MSG_DEFAULT, "Invalid number of arguments for load\n");
+	}
+	else if (strcasecmp(argv[0], "unload") == 0) {
+		if (argc == 2) {
+			hid_gpmi_script_info_t *i = hid_gpmi_lookup(argv[1]);
+			if (i != NULL) {
+				if (gpmi_hid_script_unload(i) != 0) {
+					Message(PCB_MSG_DEFAULT, "Failed to unload %s\n", argv[1]);
+					return 1;
+				}
+			}
+			else {
+				Message(PCB_MSG_DEFAULT, "Failed to unload %s: not loaded\n", argv[1]);
+				return 1;
+			}
+		}
+		else {
+			Message(PCB_MSG_DEFAULT, "Invalid number of arguments for unload\n");
+			return 1;
+		}
+	}
+	else {
+		Message(PCB_MSG_DEFAULT, "Invalid arguments in gpmi_scripts()\n");
+		return 1;
+	}
+	return 0;
+}
+
+static int action_gpmi_rehash(int argc, const char **argv, Coord x, Coord y)
+{
+	cmd_reload(NULL);
+	return 0;
+}
+
+static void register_actions()
+{
+	HID_Action act;
+
+	act.name           = "gpmi_scripts";
+	act.need_coord_msg = NULL;
+	act.description    = "Manage gpmi scripts";
+	act.syntax         = "TODO";
+	act.trigger_cb     = action_gpmi_scripts;
+	hid_register_action(&act, gpmi_cookie, 0);
+
+	act.name           = "rehash";
+	act.need_coord_msg = NULL;
+	act.description    = "Reload all gpmi scripts";
+	act.syntax         = "TODO";
+	act.trigger_cb     = action_gpmi_rehash;
+	hid_register_action(&act, gpmi_cookie, 0);
+}
+
+#ifndef PLUGIN_INIT_NAME
+#define PLUGIN_INIT_NAME pcb_plugin_init
+#endif
+
+pcb_uninit_t PLUGIN_INIT_NAME ();
+static gpmi_package *pkg_scripts = NULL;
+
+static void load_base_and_cfg(void)
+{
+	char *dir, *libdirg, *libdirh, *wdir, *wdirh, *hdirh;
+	const char *home;
+	void **gpmi_asm_scriptname;
+
+	libdirg = resolve_path_inplace(Concat(PCBLIBDIR, PCB_DIR_SEPARATOR_S "plugins", NULL), 0);
+	libdirh = resolve_path_inplace(Concat(PCBLIBDIR, PCB_DIR_SEPARATOR_S "plugins" PCB_DIR_SEPARATOR_S, HOST, NULL), 0);
+	wdirh = resolve_path_inplace(Concat ("plugins" PCB_DIR_SEPARATOR_S, HOST, NULL), 0);
+	wdir = Concat("plugins", NULL);
+
+	home = getenv ("PCB_RND_GPMI_HOME");
+	if (home == NULL)
+		home = conf_core.rc.path.home;
+
+	hdirh = resolve_path_inplace(Concat(home, PCB_DIR_SEPARATOR_S ".pcb" PCB_DIR_SEPARATOR_S "plugins" PCB_DIR_SEPARATOR_S, HOST, NULL), 0);
+
+	fprintf(stderr, "gpmi dirs: lg=%s lh=%s wh=%s w=%s hh=%s\n", libdirg, libdirh, wdirh, wdir, hdirh);
+
+	/* first add package search path to all host-specific plugin dirs
+	   This is needed because a script installed in ~/.pcb/plugins/ *.conf
+	   (added automatically from, the gui)
+	   could depend on a package being anywhere else
+	*/
+	gpmi_path_insert(GPMI_PATH_PACKAGES, libdirh);
+	gpmi_path_insert(GPMI_PATH_PACKAGES, libdirg);
+	gpmi_path_insert(GPMI_PATH_PACKAGES, wdirh);
+	gpmi_path_insert(GPMI_PATH_PACKAGES, hdirh);
+
+	/* the final fallback - append this as loading anything from here is arch-unsafe */
+	gpmi_path_append(GPMI_PATH_PACKAGES, wdir);
+
+
+	gpmi_err_stack_enable();
+	if (gpmi_pkg_load("gpmi_scripts", 0, NULL, NULL, &pkg_scripts))
+	{
+		gpmi_err_stack_process_str(gpmi_hid_print_error);
+		abort();
+	}
+	gpmi_err_stack_destroy(NULL);
+
+
+	gpmi_asm_scriptname = gpmi_pkg_resolve(pkg_scripts, "gpmi_scripts_asm_scriptname");
+	assert(gpmi_asm_scriptname != NULL);
+	*gpmi_asm_scriptname = gpmi_hid_asm_scriptname;
+
+	register_actions();
+	event_bind(EVENT_GUI_INIT, ev_gui_init, NULL, gpmi_cookie);
+
+	hid_gpmi_load_dir(libdirh, 0);
+	hid_gpmi_load_dir(libdirg, 0);
+
+	dir = Concat(PCBLIBDIR, PCB_DIR_SEPARATOR_S "plugins", NULL);
+	hid_gpmi_load_dir(dir, 1);
+	free(dir);
+
+	if (home != NULL) {
+		hid_gpmi_load_dir (hdirh, 0);
+
+		dir = resolve_path_inplace(Concat(home, PCB_DIR_SEPARATOR_S ".pcb" PCB_DIR_SEPARATOR_S "plugins", NULL), 0);
+		hid_gpmi_load_dir(dir, 1);
+		free(dir);
+	}
+
+	hid_gpmi_load_dir(wdirh, 0);
+	hid_gpmi_load_dir(wdir, 0);
+
+	free(wdir);
+	free(wdirh);
+	free(libdirg);
+	free(libdirh);
+	free(hdirh);
+}
+
+static void plugin_gpmi_uninit(void)
+{
+	event_unbind_allcookie(gpmi_cookie);
+	hid_remove_actions_by_cookie(gpmi_cookie);
+	hid_gpmi_script_info_uninit();
+	gpmi_pkg_unload(pkg_scripts);
+	gpmi_uninit();
+}
+
+pcb_uninit_t hid_gpmi_init(void)
+{
+	printf("pcb-gpmi hid is loaded.\n");
+	gpmi_init();
+	load_base_and_cfg();
+	return plugin_gpmi_uninit;
+}
+
+/* Workaround: can't call it gpmi.so so basename is gpmi_plugin thus init name must be that too for the loader */
+pcb_uninit_t hid_gpmi_plugin_init(void)
+{
+	return hid_gpmi_init();
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h
new file mode 100644
index 0000000..d02ba22
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h
@@ -0,0 +1,4 @@
+extern const char *gpmi_cookie;
+extern int gpmi_hid_gui_inited; /* whether the gui is already initialzied */
+void gpmi_hid_print_error(gpmi_err_stack_t *entry, char *string);
+
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.c
new file mode 100644
index 0000000..1dfa132
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.c
@@ -0,0 +1,244 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpmi.h>
+#include "src/misc.h"
+#include "src/misc_util.h"
+#include "src/event.h"
+#include "gpmi_plugin.h"
+#include "scripts.h"
+#include "src/hid_attrib.h"
+
+
+extern HID *gui;
+
+#define attr_make_label(attr, name_, help_) \
+do { \
+	memset((attr), 0, sizeof(HID_Attribute)); \
+	(attr)->name         = name_; \
+	(attr)->help_text    = help_; \
+	(attr)->type         = HID_Label; \
+} while(0)
+
+#define attr_make_label_str(attr, name1, name2, help_) \
+do { \
+	char *__names__; \
+	__names__ = Concat(name1, name2, NULL); \
+	attr_make_label(attr, __names__, help_); \
+} while(0)
+
+
+#define attr_make_enum(attr, name_, help_, enum_vals, default_item) \
+do { \
+	memset((attr), 0, sizeof(HID_Attribute)); \
+	(attr)->name         = name_; \
+	(attr)->help_text    = help_; \
+	(attr)->type         = HID_Enum; \
+	(attr)->enumerations = enum_vals; \
+	(attr)->default_val.int_value = default_item; \
+} while(0)
+
+static hid_gpmi_script_info_t *choose_script(const char **operations, int *operation)
+{
+	HID_Attribute attr[3];
+	HID_Attr_Val result[3];
+	char **scrl, **s;
+	hid_gpmi_script_info_t *i;
+	int n, res;
+
+	n = gpmi_hid_scripts_count();
+
+	scrl = malloc(sizeof(char *) * (n+1));
+	for(i = hid_gpmi_script_info, n = 0; i != NULL; i = i->next, n++) {
+		char *basename;
+
+		basename = strrchr(i->name, PCB_DIR_SEPARATOR_C);
+		if (basename == NULL)
+			basename = i->name;
+		else
+			basename++;
+
+		scrl[n] = Concat(basename, "\t", i->module_name, NULL);
+	}
+	scrl[n] = NULL;
+
+	attr_make_enum(&attr[0],  "script", "Select an item from the list of scripts loaded", (const char **)scrl, -1);
+
+	if (operations != NULL)
+		attr_make_enum(&attr[1],  "operation", "Choose what to do with the script", operations, *operation);
+
+	res = gui->attribute_dialog(attr, 1 + (operations != NULL), result, "GPMI manage scripts - select script", "Select one of the scripts already loaded");
+
+	/* free scrl slots before return */
+	for(s = scrl; *s != NULL; s++)
+		free(*s);
+
+	if (res) {
+		if (operation != NULL)
+			*operation = -1;
+		return NULL;
+	}
+
+	if ((operations != NULL) && (operation != NULL))
+		*operation = result[1].int_value;
+
+/*	printf("res=%d\n", result[0].int_value);*/
+
+	if (result[0].int_value != -1) {
+		for(i = hid_gpmi_script_info, n = result[0].int_value; i != NULL && n != 0; i = i->next, n--);
+/*		printf("name=%s\n", i->name);*/
+		return i;
+	}
+	return NULL;
+}
+
+static hid_gpmi_script_info_t *load_script(void)
+{
+	char *fn, *ext;
+	hid_gpmi_script_info_t *info;
+	int default_mod = -1;
+	HID_Attribute attr[3];
+	HID_Attr_Val result[3];
+	char *exts[] = {
+		".tcl",    "tcl",
+		".lua",    "lua",
+		".awk",    "mawk",
+		".mawk",   "mawk",
+		".py",     "python",
+		".scm",    "scheme",
+		".rb",     "mruby",
+		".ruby",   "mruby",
+		".st",     "stutter",
+		".pas",    "ghli",
+		".pl",     "perl",
+		".php",    "php",
+		".sh",     "cli",
+		".bash",   "cli",
+		NULL,      NULL
+	};
+	const char *modules[] = { "tcl", "lua", "mawk", "python","scheme", "mruby",
+	                          "stutter", "ghli", "perl", "php", "cli", NULL };
+
+
+	fn = gui->fileselect("Load script", "Load a GPMI script", NULL, NULL, "gpmi_load_script", HID_FILESELECT_READ);
+
+	if (fn == NULL)
+		return NULL;
+
+	ext = strrchr(fn, '.');
+	if (ext != NULL) {
+		char **s;
+		const char **i;
+		/* find the extension in the extension->module pairs */
+		for(s = exts; s[0] != NULL; s+=2)
+			if (strcmp(ext, s[0]) == 0)
+				break;
+
+		/* if found, look up the "default enum value" for that module */
+		if (s[1] != NULL) {
+			int n;
+			for(i = modules, n = 0; *i != NULL; i++,n++) {
+				if (strcmp(*i, s[1]) == 0) {
+					default_mod = n;
+					break;
+				}
+			}
+		}
+	}
+
+	attr_make_enum(&attr[0],  "module", "Select a GPMI module to interpret the script", modules, default_mod);
+
+	if (gui->attribute_dialog(attr, 1, result, "GPMI manage scripts - select module", "Select one of GPMI modules to interpret the script"))
+		return NULL;
+
+	if (result[0].int_value < 0)
+		return NULL;
+
+	info = hid_gpmi_load_module(NULL, modules[result[0].int_value], fn, NULL);
+	if (info == NULL)
+		gui->report_dialog("GPMI script load", "Error loading the script.\nPlease consult the message log for details.");
+	return info;
+}
+
+static void script_details(hid_gpmi_script_info_t *i)
+{
+	HID_Attribute attr[4];
+	HID_Attr_Val result[4];
+	char *cf;
+
+	cf = i->conffile_name == NULL ? "<none>" : i->conffile_name;
+
+
+	attr_make_label_str(&attr[0], "File name:   ", i->name, "File name of the script (if not absolute, it's relative to the config file)");
+	attr_make_label_str(&attr[1], "GPMI module: ", i->module_name, "Name of the GPMI module that is interpreting the script");
+	attr_make_label_str(&attr[2], "Config file: ", cf, "Name of config file that requested the script to be loaded ");
+	gui->attribute_dialog(attr, 3, result, "GPMI manage scripts - script details", "Displaying detailed info on a script already loaded");
+	free((char *)attr[0].name);
+	free((char *)attr[1].name);
+	free((char *)attr[2].name);
+}
+
+void gpmi_hid_manage_scripts(void)
+{
+	hid_gpmi_script_info_t *i;
+	static const char *err_no_script = "Error: you didn't select a script";
+#define CONSULT "Please consult the message log for details."
+
+	const char *operations[] = {"show details...", "reload", "unload", "unload and remove from the config file", "load a new script...", "load a new script and add it in the config...", NULL};
+	int op = 0;
+	i = choose_script(operations, &op);
+	switch(op) {
+		case 0:
+			if (i != NULL)
+				script_details(i);
+			else
+				gui->report_dialog("GPMI script details", err_no_script);
+			break;
+		case 1:
+			if (i != NULL) {
+				i = hid_gpmi_reload_module(i);
+				if (i == NULL)
+					gui->report_dialog("GPMI script reload", "Error reloading the script.\nThe script is now unloaded.\n" CONSULT "\n(e.g. there may be syntax errors in the script source).");
+			}
+			else
+				gui->report_dialog("GPMI script reload", err_no_script);
+			break;
+		case 2:
+			if (i != NULL) {
+				if (gpmi_hid_script_unload(i) != 0)
+					gui->report_dialog("GPMI script unload", "Error unloading the script.\n" CONSULT "\n");
+			}
+			else
+				gui->report_dialog("GPMI script unload", err_no_script);
+			break;
+		case 3:
+			if (i != NULL) {
+				int r1, r2;
+				r1 = gpmi_hid_script_remove(i);
+				r2 = gpmi_hid_script_unload(i);
+				if (r1 || r2) {
+					char *msg;
+					msg = Concat("Error:", 
+					             (r1 ? "couldnt't remove the script from the config file;" : ""), 
+					             (r2 ? "couldnt't unload the script;" : ""),
+					             "\n" CONSULT "\n", NULL);
+					gui->report_dialog("GPMI script unload and remove", msg);
+					free(msg);
+				}
+			else
+				gui->report_dialog("GPMI script unload and remove", err_no_script);
+			}
+			break;
+		case 4:
+			load_script();
+			break;
+		case 5:
+			i = load_script();
+			if (i != NULL) {
+				if (gpmi_hid_script_addcfg(i) != 0)
+					gui->report_dialog("GPMI script add to config", "Error adding the script in user configuration.\n" CONSULT "\n");
+			}
+			break;
+	}
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.h
new file mode 100644
index 0000000..c600c25
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/manage_scripts.h
@@ -0,0 +1 @@
+void gpmi_hid_manage_scripts(void);
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.c b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.c
new file mode 100644
index 0000000..92ee726
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.c
@@ -0,0 +1,386 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <gpmi.h>
+#include "src/misc.h"
+#include "src/misc_util.h"
+#include "src/event.h"
+#include "src/error.h"
+#include "gpmi_plugin.h"
+#include "scripts.h"
+#include "src/conf_core.h"
+#include "src/compat_fs.h"
+#include "src/compat_misc.h"
+#include "src/pcb-printf.h"
+
+#define CONFNAME "pcb-rnd-gpmi.conf"
+
+hid_gpmi_script_info_t *hid_gpmi_script_info = NULL;
+
+int gpmi_hid_scripts_count()
+{
+	int n;
+	hid_gpmi_script_info_t *i;
+
+	for(i = hid_gpmi_script_info, n = 0; i != NULL; i = i->next, n++) ;
+
+	return n;
+}
+
+static void hid_gpmi_script_info_free(hid_gpmi_script_info_t *i)
+{
+	int ev;
+	char *ev_args;
+	ev = gpmi_event_find("ACTE_unload", &ev_args);
+	if (ev >= 0)
+		gpmi_event(i->module, ev, i->conffile_name);
+
+	gpmi_mod_unload(i->module);
+	free(i->name);
+	free(i->module_name);
+	free(i->conffile_name);
+}
+
+static hid_gpmi_script_info_t *hid_gpmi_script_info_add(hid_gpmi_script_info_t *i, gpmi_module *module, const char *name_, const char *module_name_, const char *conffile_name_)
+{
+	char *name, *module_name, *conffile_name;
+	/* make these copies before the free()'s because of reload calling us with
+	   the same pointers... */
+	name = pcb_strdup(name_);
+	module_name = pcb_strdup(module_name_);
+	if (conffile_name_ != NULL)
+		conffile_name = pcb_strdup(conffile_name_);
+	else
+		conffile_name = NULL;
+
+	if (i == NULL) {
+		i = malloc(sizeof(hid_gpmi_script_info_t));
+		i->next = hid_gpmi_script_info;
+		hid_gpmi_script_info = i;
+	}
+	else
+		hid_gpmi_script_info_free(i);
+
+	i->module = module;
+	i->name = name;
+	i->module_name = module_name;
+	i->conffile_name = conffile_name;
+	return i;
+}
+
+hid_gpmi_script_info_t *hid_gpmi_lookup(const char *name)
+{
+	hid_gpmi_script_info_t *i;
+	if (name == NULL)
+		return NULL;
+	for(i = hid_gpmi_script_info; i != NULL; i = i->next)
+		if (strcmp(name, i->name) == 0)
+			return i;
+	return NULL;
+}
+
+/* Unload a script and remove it from the list */
+static void hid_gpmi_script_info_del(hid_gpmi_script_info_t *inf)
+{
+	hid_gpmi_script_info_t *i, *prev;
+	prev = NULL;
+	for(i = hid_gpmi_script_info; i != NULL; prev = i, i = i->next) {
+		if (i == inf) {
+			/* unlink */
+			if (prev == NULL)
+				hid_gpmi_script_info = inf->next;
+			else
+				prev->next = inf->next;
+			hid_gpmi_script_info_free(inf);
+			free(inf);
+			return;
+		}
+	}
+}
+
+/* Unload all scripts and remove them from the list */
+void hid_gpmi_script_info_uninit(void)
+{
+	hid_gpmi_script_info_t *i, *next;
+
+	for(i = hid_gpmi_script_info; i != NULL; i = next) {
+		next = i->next;
+		hid_gpmi_script_info_free(i);
+		free(i);
+	}
+	hid_gpmi_script_info = NULL;
+}
+
+
+static const char *conf_dir = NULL;
+
+hid_gpmi_script_info_t *hid_gpmi_load_module(hid_gpmi_script_info_t *i, const char *module_name, const char *params, const char *config_file_name)
+{
+	gpmi_module *module;
+
+	Message(PCB_MSG_DEFAULT, "Loading GPMI module %s with params %s...\n", module_name, params);
+	module = gpmi_mod_load(module_name, params);
+	if (module == NULL) {
+		Message(PCB_MSG_DEFAULT, " Failed loading the script. Details:\n");
+		gpmi_err_stack_process_str(gpmi_hid_print_error);
+	}
+	gpmi_err_stack_destroy(NULL);
+
+	if (module != NULL) {
+		hid_gpmi_script_info_t *ri;
+		int ev;
+
+		ri = hid_gpmi_script_info_add(i, module, params, module_name, config_file_name);
+		if ((ri != NULL) && (gpmi_hid_gui_inited)) {
+			char *ev_args;
+			/* If a script is loaded with a GUI already inited, send the event right after the load */
+			ev = gpmi_event_find("ACTE_gui_init", &ev_args);
+			gpmi_event(ri->module, ev, 0, NULL);
+		}
+		return ri;
+	}
+
+	return NULL;
+}
+
+hid_gpmi_script_info_t *hid_gpmi_reload_module(hid_gpmi_script_info_t *i)
+{
+	hid_gpmi_script_info_t *r;
+	const char *old_cd;
+
+	old_cd = conf_dir;
+
+	if (i->conffile_name != NULL) {
+		char *end;
+		conf_dir = pcb_strdup(i->conffile_name);
+		end = strrchr(conf_dir, PCB_DIR_SEPARATOR_C);
+		if (end == NULL) {
+			free((char *)conf_dir);
+			conf_dir = NULL;
+		}
+		else
+			*end = '\0';
+	}
+	else
+		conf_dir = NULL;
+
+	r = hid_gpmi_load_module(i, i->module_name, i->name, i->conffile_name);
+
+	if (conf_dir != NULL)
+		free((char *)conf_dir);
+	conf_dir = old_cd;
+
+	return r;
+}
+
+/* Read and parse gpmi config file fin;
+   if fout is NULL, take cfn as config name and load all modules, return number of modules loaded;
+   else write all lines to fout, but comment out the one whose second token matches cfn, return number of commented lines
+   
+*/
+
+static int cfgfile(FILE *fin, FILE *fout, char *cfn)
+{
+char line[1024], *module, *params, *s;
+	int found = 0;
+
+	while(!(feof(fin))) {
+		*line = '\0';
+		fgets(line, sizeof(line), fin);
+		switch(*line) {
+			case '\0':
+			case '\n':
+			case '\r':
+			case '#':
+				/* Empty line or comment */
+				if (fout != NULL)
+					fprintf(fout, "%s", line);
+				break;
+			default:
+				module = pcb_strdup(line);
+				params = module + strcspn(module, "\t ");
+				while((*params == ' ') || (*params == '\t')) {
+					*(params) = '\0';
+					params++;
+				}
+				s = strchr(params, '\n');
+				*s = '\0';
+				if (fout == NULL) {
+					fprintf(stderr, " ...loading %s %s\n", module, params);
+					hid_gpmi_load_module(NULL, module, params, cfn);
+					found++;
+				}
+				else {
+					if (strcmp(params, cfn) == 0) {
+						fprintf(fout, "# removed from pcb-rnd GUI: ");
+						found++;
+					}
+					if (fout != NULL)
+						fprintf(fout, "%s", line);
+				}
+				free(module);
+		}
+	}
+
+	return found;
+}
+
+void hid_gpmi_load_dir(const char *dir, int add_pkg_path)
+{
+	FILE *f;
+	char *cfn;
+
+	conf_dir = dir;
+	cfn = Concat(dir, PCB_DIR_SEPARATOR_S, CONFNAME,  NULL);
+#ifdef CONFIG_DEBUG
+	fprintf(stderr, "pcb-gpmi: opening config: %s\n", cfn);
+#endif
+	f = fopen(cfn, "r");
+	if (f == NULL) {
+		free(cfn);
+#ifdef CONFIG_DEBUG
+		fprintf(stderr, " ...failed\n");
+#endif
+		return;
+	}
+
+	if (add_pkg_path)
+		gpmi_path_insert(GPMI_PATH_PACKAGES, dir);
+
+	cfgfile(f, NULL, cfn);
+
+	fclose(f);
+	free(cfn);
+	conf_dir = NULL;
+}
+
+/* Dummy script name generator allows loading from any path */
+char *gpmi_hid_asm_scriptname(const void *info, const char *file_name)
+{
+	char buffer[1024];
+	const char *cd;
+
+	switch(*file_name) {
+		case '~':
+			file_name += 2;
+			if (conf_core.rc.path.home != NULL) {
+				pcb_snprintf(buffer, sizeof(buffer), "%s%c%s", conf_core.rc.path.home, PCB_DIR_SEPARATOR_C, file_name);
+				fprintf(stderr, "asm_scriptname FN=%s\n", buffer);
+				return pcb_strdup(buffer);
+			}
+			else {
+				fprintf(stderr, "pcb-gpmi error: can't access $HOME for substituting ~\n");
+#ifdef CONFIG_DEBUG
+				printf("FN=%s\n", file_name);
+#endif
+				return pcb_strdup(file_name);
+			}
+		case PCB_DIR_SEPARATOR_C: /* full path */
+			return pcb_strdup(file_name);
+		default: /* relative path - must be relative to the current conf_dir */
+			if ((file_name[0] == '.') && (file_name[1] == PCB_DIR_SEPARATOR_C))
+				file_name += 2;
+			if (conf_dir == NULL)
+				cd = ".";
+			else
+				cd = conf_dir;
+			pcb_snprintf(buffer, sizeof(buffer), "%s%c%s", cd, PCB_DIR_SEPARATOR_C, file_name);
+#ifdef CONFIG_DEBUG
+			printf("FN=%s\n", buffer);
+#endif
+			return pcb_strdup(buffer);
+	}
+	return NULL;
+}
+
+int gpmi_hid_script_unload(hid_gpmi_script_info_t *i)
+{
+	hid_gpmi_script_info_del(i);
+	return 0;
+}
+
+int gpmi_hid_script_remove(hid_gpmi_script_info_t *i)
+{
+	FILE *fin, *fout;
+	char *tmpfn;
+	int res;
+
+	if (i->conffile_name == NULL) {
+		Message(PCB_MSG_DEFAULT, "gpmi_hid_script_remove(): can't remove script from configs, the script is not loaded from a config.\n");
+		return -1;
+	}
+
+	fin = fopen(i->conffile_name, "r");
+	if (fin == NULL) {
+		Message(PCB_MSG_DEFAULT, "gpmi_hid_script_remove(): can't remove script from configs, can't open %s for read.\n", i->conffile_name);
+		return -1;
+	}
+	tmpfn = Concat(i->conffile_name, ".tmp", NULL);
+	fout = fopen(tmpfn, "w");
+	if (fout == NULL) {
+		Message(PCB_MSG_DEFAULT, "gpmi_hid_script_remove(): can't remove script from configs, can't create %s.\n", tmpfn);
+		fclose(fin);
+		free(tmpfn);
+		return -1;
+	}
+
+	res = cfgfile(fin, fout, i->name);
+
+	fclose(fin);
+	fclose(fout);
+
+	if (res < 1) {
+		Message(PCB_MSG_DEFAULT, "gpmi_hid_script_remove(): can't remove script from configs, can't find the correspondign config line in %s\n", i->conffile_name);
+		free(tmpfn);
+		return -1;
+	}
+
+	if (rename(tmpfn, i->conffile_name) != 0) {
+		Message(PCB_MSG_DEFAULT, "gpmi_hid_script_remove(): can't remove script from configs, can't move %s to %s.\n", tmpfn, i->conffile_name);
+		free(tmpfn);
+		return -1;
+	}
+
+	free(tmpfn);
+	return 0;
+}
+
+int gpmi_hid_script_addcfg(hid_gpmi_script_info_t *i)
+{
+	char *fn;
+	const char *home;
+	FILE *f;
+
+	home = getenv ("PCB_RND_GPMI_HOME");
+	if (home == NULL)
+		home = conf_core.rc.path.home;
+
+	if (conf_core.rc.path.home != NULL) {
+		fn = Concat(home, PCB_DIR_SEPARATOR_S ".pcb", NULL);
+		pcb_mkdir(fn, 0755);
+		free(fn);
+
+		fn = Concat(home, PCB_DIR_SEPARATOR_S ".pcb" PCB_DIR_SEPARATOR_S "plugins", NULL);
+		pcb_mkdir(fn, 0755);
+		free(fn);
+		
+		fn = Concat(home, PCB_DIR_SEPARATOR_S ".pcb" PCB_DIR_SEPARATOR_S "plugins" PCB_DIR_SEPARATOR_S, CONFNAME, NULL);
+	}
+	else
+		fn = Concat("plugins" PCB_DIR_SEPARATOR_S, CONFNAME, NULL);
+
+		f = fopen(fn, "a");
+	if (f == NULL) {
+		Message(PCB_MSG_DEFAULT, "gpmi_hid_script_addcfg: can't open %s for write\n", fn);
+		return -1;
+	}
+
+	fprintf(f, "\n%s\t%s\n", i->module_name, i->name);
+	fclose(f);
+
+	free(fn);
+	return 0;
+}
diff --git a/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.h b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.h
new file mode 100644
index 0000000..28b5d93
--- /dev/null
+++ b/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/scripts.h
@@ -0,0 +1,42 @@
+typedef struct hid_gpmi_script_info_s hid_gpmi_script_info_t;
+
+struct hid_gpmi_script_info_s {
+	char *name;
+	char *module_name;
+	char *conffile_name;
+	gpmi_module *module;
+	
+	hid_gpmi_script_info_t *next;
+};
+
+extern hid_gpmi_script_info_t *hid_gpmi_script_info;
+
+/* Load a GPMI module; if i is NULL, allocate a new slot on the list, else unload
+   i first and place the new module in place of i. */
+hid_gpmi_script_info_t *hid_gpmi_load_module(hid_gpmi_script_info_t *i, const char *module_name, const char *params, const char *config_file_name);
+
+/* Reload a script - useful if the source of the script has changed
+   Reloads a module already loaded; return NULL on error. */
+hid_gpmi_script_info_t *hid_gpmi_reload_module(hid_gpmi_script_info_t *i);
+
+/* look up a module by name - slow linear search */
+hid_gpmi_script_info_t *hid_gpmi_lookup(const char *name);
+
+void hid_gpmi_load_dir(const char *dir, int add_pkg_path);
+char *gpmi_hid_asm_scriptname(const void *info, const char *file_name);
+
+/* Return the number of scripts (gpmi modules) loaded */
+int gpmi_hid_scripts_count();
+
+/* Unload a script (also removes i from the list) - temporary effect,
+   the script will be loaded again on the next startup */
+int gpmi_hid_script_unload(hid_gpmi_script_info_t *i);
+
+/* Remove a script from the config file (but do not unload it now) */
+int gpmi_hid_script_remove(hid_gpmi_script_info_t *i);
+
+/* Edit a config file so that the script is in it */
+int gpmi_hid_script_addcfg(hid_gpmi_script_info_t *i);
+
+/* Uninit the script_info database, removing all scripts loaded */
+void hid_gpmi_script_info_uninit(void);
diff --git a/src_plugins/hid_batch/Makefile b/src_plugins/hid_batch/Makefile
new file mode 100644
index 0000000..3477d42
--- /dev/null
+++ b/src_plugins/hid_batch/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_hid_batch
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/hid_batch/Plug.tmpasm b/src_plugins/hid_batch/Plug.tmpasm
new file mode 100644
index 0000000..b2fc1fa
--- /dev/null
+++ b/src_plugins/hid_batch/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {hid_batch}
+append /local/pcb/mod/OBJS [@ $(PLUGDIR)/hid_batch/batch.o @]
+
+switch /local/pcb/hid_batch/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/hid_batch/README b/src_plugins/hid_batch/README
new file mode 100644
index 0000000..23642b8
--- /dev/null
+++ b/src_plugins/hid_batch/README
@@ -0,0 +1,5 @@
+HID without GUI; read actions from stdin.
+
+#state: works
+#default: buildin
+#implements: hid
diff --git a/src_plugins/hid_batch/batch.c b/src_plugins/hid_batch/batch.c
new file mode 100644
index 0000000..089ccb6
--- /dev/null
+++ b/src_plugins/hid_batch/batch.c
@@ -0,0 +1,411 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+#include "hid.h"
+#include "data.h"
+#include "misc.h"
+#include "layer.h"
+#include "hid.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+#include "hid_draw_helpers.h"
+#include "hid_nogui.h"
+#include "hid_actions.h"
+#include "hid_init.h"
+
+static const char *batch_cookie = "batch HID";
+
+static void batch_begin(void);
+static void batch_end(void);
+
+/* This is a text-line "batch" HID, which exists for scripting and
+   non-GUI needs.  */
+
+typedef struct hid_gc_struct {
+	int nothing_interesting_here;
+} hid_gc_struct;
+
+static HID_Attribute *batch_get_export_options(int *n_ret)
+{
+	return 0;
+}
+
+/* ----------------------------------------------------------------------------- */
+
+static char *prompt = NULL;
+
+static void hid_batch_uninit(void)
+{
+	hid_remove_actions_by_cookie(batch_cookie);
+	if (prompt != NULL)
+		free(prompt);
+}
+
+static int nop(int argc, const char **argv, Coord x, Coord y)
+{
+	return 0;
+}
+
+static int PCBChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	if (prompt != NULL)
+		free(prompt);
+	if (PCB && PCB->Filename) {
+		prompt = strrchr(PCB->Filename, '/');
+		if (prompt)
+			prompt++;
+		else
+			prompt = PCB->Filename;
+		if (prompt != NULL)
+			prompt = pcb_strdup(prompt);
+	}
+	else
+		prompt = pcb_strdup("no-board");
+	return 0;
+}
+
+static int help(int argc, const char **argv, Coord x, Coord y)
+{
+	print_actions();
+	return 0;
+}
+
+static int info(int argc, const char **argv, Coord x, Coord y)
+{
+	int i, j;
+	int cg, sg;
+	if (!PCB || !PCB->Data || !PCB->Filename) {
+		printf("No PCB loaded.\n");
+		return 0;
+	}
+	printf("Filename: %s\n", PCB->Filename);
+	pcb_printf("Size: %ml x %ml mils, %mm x %mm mm\n", PCB->MaxWidth, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
+	cg = GetLayerGroupNumberByNumber(component_silk_layer);
+	sg = GetLayerGroupNumberByNumber(solder_silk_layer);
+	for (i = 0; i < MAX_LAYER; i++) {
+
+		int lg = GetLayerGroupNumberByNumber(i);
+		for (j = 0; j < MAX_LAYER; j++)
+			putchar(j == lg ? '#' : '-');
+		printf(" %c %s\n", lg == cg ? 'c' : lg == sg ? 's' : '-', PCB->Data->Layer[i].Name);
+	}
+	return 0;
+}
+
+HID_Action batch_action_list[] = {
+	{"PCBChanged", 0, PCBChanged}
+	,
+	{"RouteStylesChanged", 0, nop}
+	,
+	{"NetlistChanged", 0, nop}
+	,
+	{"LayersChanged", 0, nop}
+	,
+	{"LibraryChanged", 0, nop}
+	,
+	{"Busy", 0, nop}
+	,
+	{"Help", 0, help}
+	,
+	{"Info", 0, info}
+	,
+	{"PointCursor", 0, info}
+};
+
+REGISTER_ACTIONS(batch_action_list, batch_cookie)
+
+extern int isatty();
+
+/* ----------------------------------------------------------------------------- */
+static int batch_stay;
+static void batch_do_export(HID_Attr_Val * options)
+{
+	int interactive;
+	char line[1000];
+
+	batch_begin();
+
+	if (isatty(0))
+		interactive = 1;
+	else
+		interactive = 0;
+
+	if (interactive) {
+		printf("Entering %s version %s batch mode.\n", PACKAGE, VERSION);
+		printf("See http://repo.hu/projects/pcb-rnd for project information\n");
+	}
+
+	batch_stay = 1;
+	while (batch_stay) {
+		if (interactive) {
+			printf("%s> ", prompt);
+			fflush(stdout);
+		}
+		if (fgets(line, sizeof(line) - 1, stdin) == NULL) {
+			hid_batch_uninit();
+			return;
+		}
+		hid_parse_command(line);
+	}
+	batch_end();
+}
+
+static void batch_do_exit(HID *hid)
+{
+	batch_stay = 0;
+}
+
+static void batch_parse_arguments(int *argc, char ***argv)
+{
+	hid_parse_command_line(argc, argv);
+}
+
+static void batch_invalidate_lr(int l, int r, int t, int b)
+{
+}
+
+static void batch_invalidate_all(void)
+{
+}
+
+static int batch_set_layer(const char *name, int idx, int empty)
+{
+	return 0;
+}
+
+static hidGC batch_make_gc(void)
+{
+	return 0;
+}
+
+static void batch_destroy_gc(hidGC gc)
+{
+}
+
+static void batch_use_mask(int use_it)
+{
+}
+
+static void batch_set_color(hidGC gc, const char *name)
+{
+}
+
+static void batch_set_line_cap(hidGC gc, EndCapStyle style)
+{
+}
+
+static void batch_set_line_width(hidGC gc, Coord width)
+{
+}
+
+static void batch_set_draw_xor(hidGC gc, int xor_set)
+{
+}
+
+static void batch_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+}
+
+static void batch_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle end_angle)
+{
+}
+
+static void batch_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+}
+
+static void batch_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+}
+
+static void batch_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+}
+
+static void batch_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+}
+
+static void batch_calibrate(double xval, double yval)
+{
+}
+
+static int batch_shift_is_pressed(void)
+{
+	return 0;
+}
+
+static int batch_control_is_pressed(void)
+{
+	return 0;
+}
+
+static int batch_mod1_is_pressed(void)
+{
+	return 0;
+}
+
+static void batch_get_coords(const char *msg, Coord * x, Coord * y)
+{
+}
+
+static void batch_set_crosshair(int x, int y, int action)
+{
+}
+
+static hidval batch_add_timer(void (*func) (hidval user_data), unsigned long milliseconds, hidval user_data)
+{
+	hidval rv;
+	rv.lval = 0;
+	return rv;
+}
+
+static void batch_stop_timer(hidval timer)
+{
+}
+
+hidval
+batch_watch_file(int fd, unsigned int condition, void (*func) (hidval watch, int fd, unsigned int condition, hidval user_data),
+								 hidval user_data)
+{
+	hidval ret;
+	ret.ptr = NULL;
+	return ret;
+}
+
+void batch_unwatch_file(hidval data)
+{
+}
+
+static hidval batch_add_block_hook(void (*func) (hidval data), hidval user_data)
+{
+	hidval ret;
+	ret.ptr = NULL;
+	return ret;
+}
+
+static void batch_stop_block_hook(hidval mlpoll)
+{
+}
+
+static int
+batch_attribute_dialog(HID_Attribute * attrs_, int n_attrs_, HID_Attr_Val * results_, const char *title_, const char *descr_)
+{
+	return 0;
+}
+
+static void batch_show_item(void *item)
+{
+}
+
+static void batch_create_menu(const char *menu, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie)
+{
+}
+
+static int batch_propedit_start(void *pe, int num_props, const char *(*query)(void *pe, const char *cmd, const char *key, const char *val, int idx))
+{
+	printf("propedit start %d\n", num_props);
+ 	return 0;
+}
+
+static void batch_propedit_end(void *pe)
+{
+	printf("propedit end\n");
+}
+
+static void *batch_propedit_add_prop(void *pe, const char *propname, int is_mutable, int num_vals)
+{
+	printf("  %s (%d) %s\n", propname, num_vals, is_mutable ? "mutable" : "immutable");
+	return NULL;
+}
+
+static void batch_propedit_add_value(void *pe, const char *propname, void *propctx, const char *value, int repeat_cnt)
+{
+	printf("    %s (%d)\n", value, repeat_cnt);
+}
+
+static void batch_propedit_add_stat(void *pe, const char *propname, void *propctx, const char *most_common, const char *min, const char *max, const char *avg)
+{
+	printf("    [%s|%s|%s|%s]\n", most_common, min, max, avg);
+}
+
+
+#include "dolists.h"
+
+static HID batch_hid;
+
+pcb_uninit_t hid_hid_batch_init()
+{
+	memset(&batch_hid, 0, sizeof(HID));
+
+	common_nogui_init(&batch_hid);
+	common_draw_helpers_init(&batch_hid);
+
+	batch_hid.struct_size = sizeof(HID);
+	batch_hid.name = "batch";
+	batch_hid.description = "Batch-mode GUI for non-interactive use.";
+	batch_hid.gui = 1;
+
+	batch_hid.get_export_options = batch_get_export_options;
+	batch_hid.do_export = batch_do_export;
+	batch_hid.do_exit = batch_do_exit;
+	batch_hid.parse_arguments = batch_parse_arguments;
+	batch_hid.invalidate_lr = batch_invalidate_lr;
+	batch_hid.invalidate_all = batch_invalidate_all;
+	batch_hid.set_layer = batch_set_layer;
+	batch_hid.make_gc = batch_make_gc;
+	batch_hid.destroy_gc = batch_destroy_gc;
+	batch_hid.use_mask = batch_use_mask;
+	batch_hid.set_color = batch_set_color;
+	batch_hid.set_line_cap = batch_set_line_cap;
+	batch_hid.set_line_width = batch_set_line_width;
+	batch_hid.set_draw_xor = batch_set_draw_xor;
+	batch_hid.draw_line = batch_draw_line;
+	batch_hid.draw_arc = batch_draw_arc;
+	batch_hid.draw_rect = batch_draw_rect;
+	batch_hid.fill_circle = batch_fill_circle;
+	batch_hid.fill_polygon = batch_fill_polygon;
+	batch_hid.fill_rect = batch_fill_rect;
+	batch_hid.calibrate = batch_calibrate;
+	batch_hid.shift_is_pressed = batch_shift_is_pressed;
+	batch_hid.control_is_pressed = batch_control_is_pressed;
+	batch_hid.mod1_is_pressed = batch_mod1_is_pressed;
+	batch_hid.get_coords = batch_get_coords;
+	batch_hid.set_crosshair = batch_set_crosshair;
+	batch_hid.add_timer = batch_add_timer;
+	batch_hid.stop_timer = batch_stop_timer;
+	batch_hid.watch_file = batch_watch_file;
+	batch_hid.unwatch_file = batch_unwatch_file;
+	batch_hid.add_block_hook = batch_add_block_hook;
+	batch_hid.stop_block_hook = batch_stop_block_hook;
+	batch_hid.attribute_dialog = batch_attribute_dialog;
+	batch_hid.show_item = batch_show_item;
+	batch_hid.create_menu = batch_create_menu;
+
+	batch_hid.propedit_start = batch_propedit_start;
+	batch_hid.propedit_end = batch_propedit_end;
+	batch_hid.propedit_add_prop = batch_propedit_add_prop;
+	batch_hid.propedit_add_value = batch_propedit_add_value;
+	batch_hid.propedit_add_stat = batch_propedit_add_stat;
+
+
+	hid_register_hid(&batch_hid);
+	return NULL;
+}
+
+static void batch_begin(void)
+{
+	REGISTER_ACTIONS(batch_action_list, batch_cookie)
+}
+
+static void batch_end(void)
+{
+	hid_remove_actions_by_cookie(batch_cookie);
+}
diff --git a/src_plugins/hid_gtk/Makefile b/src_plugins/hid_gtk/Makefile
new file mode 100644
index 0000000..84cf1c9
--- /dev/null
+++ b/src_plugins/hid_gtk/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_hid_gtk
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/hid_gtk/Plug.tmpasm b/src_plugins/hid_gtk/Plug.tmpasm
new file mode 100644
index 0000000..29766ee
--- /dev/null
+++ b/src_plugins/hid_gtk/Plug.tmpasm
@@ -0,0 +1,52 @@
+put /local/pcb/mod {hid_gtk}
+put /local/pcb/mod/OBJS_C99 [@
+	$(PLUGDIR)/hid_gtk/ghid-cell-renderer-visibility.o
+	$(PLUGDIR)/hid_gtk/ghid-coord-entry.o
+	$(PLUGDIR)/hid_gtk/ghid-layer-selector.o
+	$(PLUGDIR)/hid_gtk/ghid-main-menu.o
+	$(PLUGDIR)/hid_gtk/ghid-route-style-selector.o
+	$(PLUGDIR)/hid_gtk/ghid-propedit.o
+	$(PLUGDIR)/hid_gtk/ghid-search.o
+	$(PLUGDIR)/hid_gtk/gtkhid-main.o
+	$(PLUGDIR)/hid_gtk/gui-command-window.o
+	$(PLUGDIR)/hid_gtk/gui-config.o
+	$(PLUGDIR)/hid_gtk/gui-dialog-print.o
+	$(PLUGDIR)/hid_gtk/gui-dialog.o
+	$(PLUGDIR)/hid_gtk/gui-drc-window.o
+	$(PLUGDIR)/hid_gtk/gui-keyref-window.o
+	$(PLUGDIR)/hid_gtk/gui-library-window.o
+	$(PLUGDIR)/hid_gtk/gui-log-window.o
+	$(PLUGDIR)/hid_gtk/gui-misc.o
+	$(PLUGDIR)/hid_gtk/gui-netlist-window.o
+	$(PLUGDIR)/hid_gtk/gui-output-events.o
+	$(PLUGDIR)/hid_gtk/gui-pinout-preview.o
+	$(PLUGDIR)/hid_gtk/gui-pinout-window.o
+	$(PLUGDIR)/hid_gtk/gui-top-window.o
+	$(PLUGDIR)/hid_gtk/gui-utils.o
+	$(PLUGDIR)/hid_gtk/gtkhid-gdk.o
+	$(PLUGDIR)/hid_gtk/menu_lht.o
+	$(PLUGDIR)/hid_gtk/gtk_conf_list.o
+	$(PLUGDIR)/hid_gtk/gschem_accel_label.o
+	$(PLUGDIR)/hid_gtk/win_place.o
+@]
+put /local/pcb/mod/CONF {$(PLUGDIR)/hid_gtk/hid_gtk_conf.h}
+
+switch /local/pcb/hid_gtk/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/CFLAGS   /target/libs/gui/gtk2/cflags
+		put /local/pcb/mod/LDFLAGS  /target/libs/gui/gtk2/ldflags
+		append /local/pcb/RULES [@
+### lesstif menu embed
+$(PLUGDIR)/hid_gtk/menu_lht.c: pcb-menu-gtk.lht
+	$(CQUOTE) -n hid_gtk_menu_default <pcb-menu-gtk.lht >$(PLUGDIR)/hid_gtk/menu_lht.c
+@]
+
+	end
+end
+
+switch /local/pcb/hid_gtk/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/hid_gtk/README b/src_plugins/hid_gtk/README
new file mode 100644
index 0000000..c306c50
--- /dev/null
+++ b/src_plugins/hid_gtk/README
@@ -0,0 +1,5 @@
+GUI: the GTK HID.
+
+#state: works
+#default: buildin
+#implements: hid
diff --git a/src_plugins/hid_gtk/ghid-cell-renderer-visibility.c b/src_plugins/hid_gtk/ghid-cell-renderer-visibility.c
new file mode 100644
index 0000000..3a069cc
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-cell-renderer-visibility.c
@@ -0,0 +1,255 @@
+/*! \file <gtk-pcb-cell-render-visibility.c>
+ *  \brief Implementation of GtkCellRenderer for layer visibility toggler
+ *  \par More Information
+ *  For details on the functions implemented here, see the Gtk
+ *  documentation for the GtkCellRenderer object, which defines
+ *  the interface we are implementing.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gtkhid.h"
+#include "gui.h"
+
+#include "ghid-cell-renderer-visibility.h"
+
+enum {
+	TOGGLED,
+	LAST_SIGNAL
+};
+static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_ACTIVE = 1,
+	PROP_COLOR
+};
+
+struct _GHidCellRendererVisibility {
+	GtkCellRenderer parent;
+
+	gboolean active;
+	gchar *color;
+};
+
+struct _GHidCellRendererVisibilityClass {
+	GtkCellRendererClass parent_class;
+
+	void (*toggled) (GHidCellRendererVisibility * cell, const gchar * path);
+};
+
+/* RENDERER FUNCTIONS */
+/*! \brief Calculates the window area the renderer will use */
+static void
+ghid_cell_renderer_visibility_get_size(GtkCellRenderer * cell,
+																			 GtkWidget * widget,
+																			 GdkRectangle * cell_area, gint * x_offset, gint * y_offset, gint * width, gint * height)
+{
+	GtkStyle *style = gtk_widget_get_style(widget);
+	gint w, h;
+	gint xpad, ypad;
+	gfloat xalign, yalign;
+
+	gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
+	gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
+
+	w = VISIBILITY_TOGGLE_SIZE + 2 * (xpad + style->xthickness);
+	h = VISIBILITY_TOGGLE_SIZE + 2 * (ypad + style->ythickness);
+
+	if (width)
+		*width = w;
+	if (height)
+		*height = h;
+
+	if (cell_area) {
+		if (x_offset) {
+			if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
+				xalign = 1. - xalign;
+			*x_offset = MAX(0, xalign * (cell_area->width - w));
+		}
+		if (y_offset)
+			*y_offset = MAX(0, yalign * (cell_area->height - h));
+	}
+}
+
+/*! \brief Actually renders the swatch */
+static void
+ghid_cell_renderer_visibility_render(GtkCellRenderer * cell,
+																		 GdkWindow * window,
+																		 GtkWidget * widget,
+																		 GdkRectangle * background_area,
+																		 GdkRectangle * cell_area, GdkRectangle * expose_area, GtkCellRendererState flags)
+{
+	GHidCellRendererVisibility *pcb_cell;
+	GdkRectangle toggle_rect;
+	GdkRectangle draw_rect;
+	gint xpad, ypad;
+
+	pcb_cell = GHID_CELL_RENDERER_VISIBILITY(cell);
+	ghid_cell_renderer_visibility_get_size(cell, widget, cell_area,
+																				 &toggle_rect.x, &toggle_rect.y, &toggle_rect.width, &toggle_rect.height);
+	gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
+
+	toggle_rect.x += cell_area->x + xpad;
+	toggle_rect.y += cell_area->y + ypad;
+	toggle_rect.width -= xpad * 2;
+	toggle_rect.height -= ypad * 2;
+
+	if (toggle_rect.width <= 0 || toggle_rect.height <= 0)
+		return;
+
+	if (gdk_rectangle_intersect(expose_area, cell_area, &draw_rect)) {
+		GdkColor color;
+		cairo_t *cr = gdk_cairo_create(window);
+		if (expose_area) {
+			gdk_cairo_rectangle(cr, expose_area);
+			cairo_clip(cr);
+		}
+		cairo_set_line_width(cr, 1);
+
+		cairo_rectangle(cr, toggle_rect.x + 0.5, toggle_rect.y + 0.5, toggle_rect.width - 1, toggle_rect.height - 1);
+		cairo_set_source_rgb(cr, 1, 1, 1);
+		cairo_fill_preserve(cr);
+		cairo_set_source_rgb(cr, 0, 0, 0);
+		cairo_stroke(cr);
+
+		gdk_color_parse(pcb_cell->color, &color);
+		if (flags & GTK_CELL_RENDERER_PRELIT) {
+			color.red = (4 * color.red + 65535) / 5;
+			color.green = (4 * color.green + 65535) / 5;
+			color.blue = (4 * color.blue + 65535) / 5;
+		}
+		gdk_cairo_set_source_color(cr, &color);
+		if (pcb_cell->active)
+			cairo_rectangle(cr, toggle_rect.x + 0.5, toggle_rect.y + 0.5, toggle_rect.width - 1, toggle_rect.height - 1);
+		else {
+			cairo_move_to(cr, toggle_rect.x + 1, toggle_rect.y + 1);
+			cairo_rel_line_to(cr, toggle_rect.width / 2, 0);
+			cairo_rel_line_to(cr, -toggle_rect.width / 2, toggle_rect.width / 2);
+			cairo_close_path(cr);
+		}
+		cairo_fill(cr);
+
+		cairo_destroy(cr);
+	}
+}
+
+/*! \brief Toggless the swatch */
+static gint
+ghid_cell_renderer_visibility_activate(GtkCellRenderer * cell,
+																			 GdkEvent * event,
+																			 GtkWidget * widget,
+																			 const gchar * path,
+																			 GdkRectangle * background_area, GdkRectangle * cell_area, GtkCellRendererState flags)
+{
+	g_signal_emit(cell, toggle_cell_signals[TOGGLED], 0, path);
+	return TRUE;
+}
+
+/* Setter/Getter */
+static void ghid_cell_renderer_visibility_get_property(GObject * object, guint param_id, GValue * value, GParamSpec * pspec)
+{
+	GHidCellRendererVisibility *pcb_cell = GHID_CELL_RENDERER_VISIBILITY(object);
+
+	switch (param_id) {
+	case PROP_ACTIVE:
+		g_value_set_boolean(value, pcb_cell->active);
+		break;
+	case PROP_COLOR:
+		g_value_set_string(value, pcb_cell->color);
+		break;
+	}
+}
+
+static void
+ghid_cell_renderer_visibility_set_property(GObject * object, guint param_id, const GValue * value, GParamSpec * pspec)
+{
+	GHidCellRendererVisibility *pcb_cell = GHID_CELL_RENDERER_VISIBILITY(object);
+
+	switch (param_id) {
+	case PROP_ACTIVE:
+		pcb_cell->active = g_value_get_boolean(value);
+		break;
+	case PROP_COLOR:
+		g_free(pcb_cell->color);
+		pcb_cell->color = g_value_dup_string(value);
+		break;
+	}
+}
+
+
+/* CONSTRUCTOR */
+static void ghid_cell_renderer_visibility_init(GHidCellRendererVisibility * ls)
+{
+	g_object_set(ls, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+}
+
+static void ghid_cell_renderer_visibility_class_init(GHidCellRendererVisibilityClass * klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS(klass);
+	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(klass);
+
+	object_class->get_property = ghid_cell_renderer_visibility_get_property;
+	object_class->set_property = ghid_cell_renderer_visibility_set_property;
+
+	cell_class->get_size = ghid_cell_renderer_visibility_get_size;
+	cell_class->render = ghid_cell_renderer_visibility_render;
+	cell_class->activate = ghid_cell_renderer_visibility_activate;
+
+	g_object_class_install_property(object_class, PROP_ACTIVE,
+																	g_param_spec_boolean("active",
+																											 _("Visibility state"),
+																											 _("Visibility of the layer"), FALSE, G_PARAM_READWRITE));
+	g_object_class_install_property(object_class, PROP_COLOR,
+																	g_param_spec_string("color", _("Layer color"), _("Layer color"), FALSE, G_PARAM_READWRITE));
+
+
+ /**
+  * GHidCellRendererVisibility::toggled:
+  * @cell_renderer: the object which received the signal
+  * @path: string representation of #GtkTreePath describing the 
+  *        event location
+  *
+  * The ::toggled signal is emitted when the cell is toggled. 
+  **/
+	toggle_cell_signals[TOGGLED] =
+		g_signal_new(_("toggled"),
+								 G_OBJECT_CLASS_TYPE(object_class),
+								 G_SIGNAL_RUN_LAST,
+								 G_STRUCT_OFFSET(GHidCellRendererVisibilityClass, toggled),
+								 NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+/* PUBLIC FUNCTIONS */
+GType ghid_cell_renderer_visibility_get_type(void)
+{
+	static GType ls_type = 0;
+
+	if (!ls_type) {
+		const GTypeInfo ls_info = {
+			sizeof(GHidCellRendererVisibilityClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_cell_renderer_visibility_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GHidCellRendererVisibility),
+			0,												/* n_preallocs */
+			(GInstanceInitFunc) ghid_cell_renderer_visibility_init,
+		};
+
+		ls_type = g_type_register_static(GTK_TYPE_CELL_RENDERER, "GHidCellRendererVisibility", &ls_info, 0);
+	}
+
+	return ls_type;
+}
+
+GtkCellRenderer *ghid_cell_renderer_visibility_new(void)
+{
+	GHidCellRendererVisibility *rv = g_object_new(GHID_CELL_RENDERER_VISIBILITY_TYPE, NULL);
+
+	rv->active = FALSE;
+
+	return GTK_CELL_RENDERER(rv);
+}
diff --git a/src_plugins/hid_gtk/ghid-cell-renderer-visibility.h b/src_plugins/hid_gtk/ghid-cell-renderer-visibility.h
new file mode 100644
index 0000000..c87308b
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-cell-renderer-visibility.h
@@ -0,0 +1,21 @@
+#ifndef GHID_CELL_RENDERER_VISIBILITY_H__
+#define GHID_CELL_RENDERER_VISIBILITY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS										/* keep c++ happy */
+#define VISIBILITY_TOGGLE_SIZE	16
+#define GHID_CELL_RENDERER_VISIBILITY_TYPE            (ghid_cell_renderer_visibility_get_type ())
+#define GHID_CELL_RENDERER_VISIBILITY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_CELL_RENDERER_VISIBILITY_TYPE, GHidCellRendererVisibility))
+#define GHID_CELL_RENDERER_VISIBILITY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GHID_CELL_RENDERER_VISIBILITY_TYPE, GHidCellRendererVisibilityClass))
+#define IS_GHID_CELL_RENDERER_VISIBILITY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_CELL_RENDERER_VISIBILITY_TYPE))
+#define IS_GHID_CELL_RENDERER_VISIBILITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHID_CELL_RENDERER_VISIBILITY_TYPE))
+typedef struct _GHidCellRendererVisibility GHidCellRendererVisibility;
+typedef struct _GHidCellRendererVisibilityClass GHidCellRendererVisibilityClass;
+
+GType ghid_cell_renderer_visibility_get_type(void);
+GtkCellRenderer *ghid_cell_renderer_visibility_new(void);
+
+G_END_DECLS											/* keep c++ happy */
+#endif
diff --git a/src_plugins/hid_gtk/ghid-coord-entry.c b/src_plugins/hid_gtk/ghid-coord-entry.c
new file mode 100644
index 0000000..3ba0679
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-coord-entry.c
@@ -0,0 +1,289 @@
+/*! \file <gtk-pcb-coord-entry.c>
+ *  \brief Implementation of GHidCoordEntry widget
+ *  \par Description
+ *  This widget is a modified spinbox for the user to enter
+ *  pcb coords. It is assigned a default unit (for display),
+ *  but this can be changed by the user by typing a new one
+ *  or right-clicking on the box.
+ *
+ *  Internally, it keeps track of its value in pcb coords.
+ *  From the user's perspective, it uses natural human units.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gtkhid.h"
+#include "gui.h"
+#include "pcb-printf.h"
+
+#include "ghid-coord-entry.h"
+
+enum {
+	UNIT_CHANGE_SIGNAL,
+	LAST_SIGNAL
+};
+
+static guint ghid_coord_entry_signals[LAST_SIGNAL] = { 0 };
+
+struct _GHidCoordEntry {
+	GtkSpinButton parent;
+
+	Coord min_value;
+	Coord max_value;
+	Coord value;
+
+	enum ce_step_size step_size;
+	const Unit *unit;
+};
+
+struct _GHidCoordEntryClass {
+	GtkSpinButtonClass parent_class;
+
+	void (*change_unit) (GHidCoordEntry *, const Unit *);
+};
+
+/* SIGNAL HANDLERS */
+/*! \brief Callback for "Change Unit" menu click */
+static void menu_item_activate_cb(GtkMenuItem * item, GHidCoordEntry * ce)
+{
+	const char *text = gtk_menu_item_get_label(item);
+	const Unit *unit = get_unit_struct(text);
+
+	g_signal_emit(ce, ghid_coord_entry_signals[UNIT_CHANGE_SIGNAL], 0, unit);
+}
+
+/*! \brief Callback for context menu creation */
+static void ghid_coord_entry_popup_cb(GHidCoordEntry * ce, GtkMenu * menu, gpointer data)
+{
+	int i, n;
+	const Unit *unit_list;
+	GtkWidget *menu_item, *submenu;
+
+	/* Build submenu */
+	n = get_n_units();
+	unit_list = get_unit_list();
+
+	submenu = gtk_menu_new();
+	for (i = 0; i < n; ++i) {
+		menu_item = gtk_menu_item_new_with_label(unit_list[i].suffix);
+		g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(menu_item_activate_cb), ce);
+		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menu_item);
+		gtk_widget_show(menu_item);
+	}
+
+	/* Add submenu to menu */
+	menu_item = gtk_separator_menu_item_new();
+	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menu_item);
+	gtk_widget_show(menu_item);
+
+	menu_item = gtk_menu_item_new_with_label(_("Change Units"));
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
+	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menu_item);
+	gtk_widget_show(menu_item);
+}
+
+/*! \brief Callback for user output */
+static gboolean ghid_coord_entry_output_cb(GHidCoordEntry * ce, gpointer data)
+{
+	GtkAdjustment *adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(ce));
+	double value = gtk_adjustment_get_value(adj);
+	char *text;
+
+	text = pcb_strdup_printf("%.*f %s", ce->unit->default_prec, value, ce->unit->suffix);
+	gtk_entry_set_text(GTK_ENTRY(ce), text);
+	free(text);
+
+	return TRUE;
+}
+
+/*! \brief Callback for user input */
+static gboolean ghid_coord_text_changed_cb(GHidCoordEntry * entry, gpointer data)
+{
+	const char *text;
+	char *suffix;
+	const Unit *new_unit;
+	double value;
+
+	/* Check if units have changed */
+	text = gtk_entry_get_text(GTK_ENTRY(entry));
+	value = strtod(text, &suffix);
+	new_unit = get_unit_struct(suffix);
+	if (new_unit && new_unit != entry->unit) {
+		entry->value = unit_to_coord(new_unit, value);
+		g_signal_emit(entry, ghid_coord_entry_signals[UNIT_CHANGE_SIGNAL], 0, new_unit);
+	}
+
+	return FALSE;
+}
+
+/*! \brief Callback for change in value (input or ^v clicks) */
+static gboolean ghid_coord_value_changed_cb(GHidCoordEntry * ce, gpointer data)
+{
+	GtkAdjustment *adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(ce));
+
+	/* Re-calculate internal value */
+	double value = gtk_adjustment_get_value(adj);
+	ce->value = unit_to_coord(ce->unit, value);
+	/* Handle potential unit changes */
+	ghid_coord_text_changed_cb(ce, data);
+
+	return FALSE;
+}
+
+/*! \brief Change the unit used by a coord entry
+ *
+ *  \param [in] ce         The entry to be acted on
+ *  \parin [in] new_unit   The new unit to be used
+ */
+static void ghid_coord_entry_change_unit(GHidCoordEntry * ce, const Unit * new_unit)
+{
+	double climb_rate = 0.0;
+	GtkAdjustment *adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(ce));
+
+	ce->unit = new_unit;
+	/* Re-calculate min/max values for spinbox */
+	gtk_adjustment_configure(adj, coord_to_unit(new_unit, ce->value),
+													 coord_to_unit(new_unit, ce->min_value),
+													 coord_to_unit(new_unit, ce->max_value), ce->unit->step_small, ce->unit->step_medium, 0.0);
+
+	switch (ce->step_size) {
+	case CE_TINY:
+		climb_rate = new_unit->step_tiny;
+		break;
+	case CE_SMALL:
+		climb_rate = new_unit->step_small;
+		break;
+	case CE_MEDIUM:
+		climb_rate = new_unit->step_medium;
+		break;
+	case CE_LARGE:
+		climb_rate = new_unit->step_large;
+		break;
+	}
+	gtk_spin_button_configure(GTK_SPIN_BUTTON(ce), adj, climb_rate, new_unit->default_prec + strlen(new_unit->suffix));
+}
+
+/* CONSTRUCTOR */
+static void ghid_coord_entry_init(GHidCoordEntry * ce)
+{
+	/* Hookup signal handlers */
+	g_signal_connect(G_OBJECT(ce), "focus_out_event", G_CALLBACK(ghid_coord_text_changed_cb), NULL);
+	g_signal_connect(G_OBJECT(ce), "value_changed", G_CALLBACK(ghid_coord_value_changed_cb), NULL);
+	g_signal_connect(G_OBJECT(ce), "populate_popup", G_CALLBACK(ghid_coord_entry_popup_cb), NULL);
+	g_signal_connect(G_OBJECT(ce), "output", G_CALLBACK(ghid_coord_entry_output_cb), NULL);
+}
+
+static void ghid_coord_entry_class_init(GHidCoordEntryClass * klass)
+{
+	klass->change_unit = ghid_coord_entry_change_unit;
+
+	/* GtkAutoComplete *ce : the object acted on */
+	/* const Unit *new_unit: the new unit that was set */
+	ghid_coord_entry_signals[UNIT_CHANGE_SIGNAL] =
+		g_signal_new("change-unit",
+								 G_TYPE_FROM_CLASS(klass),
+								 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+								 G_STRUCT_OFFSET(GHidCoordEntryClass, change_unit),
+								 NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+}
+
+/* PUBLIC FUNCTIONS */
+GType ghid_coord_entry_get_type(void)
+{
+	static GType ce_type = 0;
+
+	if (!ce_type) {
+		const GTypeInfo ce_info = {
+			sizeof(GHidCoordEntryClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_coord_entry_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GHidCoordEntry),
+			0,												/* n_preallocs */
+			(GInstanceInitFunc) ghid_coord_entry_init,
+		};
+
+		ce_type = g_type_register_static(GTK_TYPE_SPIN_BUTTON, "GHidCoordEntry", &ce_info, 0);
+	}
+
+	return ce_type;
+}
+
+/*! \brief Create a new GHidCoordEntry
+ *
+ *  \param [in] min_val    The minimum allowed value, in pcb coords
+ *  \param [in] max_val    The maximum allowed value, in pcb coords
+ *  \param [in] value      The default value, in pcb coords
+ *  \param [in] unit       The default unit
+ *  \param [in] step_size  How large the default increments should be
+ *
+ *  \return a freshly-allocated GHidCoordEntry
+ */
+GtkWidget *ghid_coord_entry_new(Coord min_val, Coord max_val, Coord value, const Unit * unit, enum ce_step_size step_size)
+{
+	/* Setup spinbox min/max values */
+	double small_step, big_step;
+	GtkAdjustment *adj;
+	GHidCoordEntry *ce = g_object_new(GHID_COORD_ENTRY_TYPE, NULL);
+
+	ce->unit = unit;
+	ce->min_value = min_val;
+	ce->max_value = max_val;
+	ce->value = value;
+
+	ce->step_size = step_size;
+	switch (step_size) {
+	case CE_TINY:
+		small_step = unit->step_tiny;
+		big_step = unit->step_small;
+		break;
+	case CE_SMALL:
+		small_step = unit->step_small;
+		big_step = unit->step_medium;
+		break;
+	case CE_MEDIUM:
+		small_step = unit->step_medium;
+		big_step = unit->step_large;
+		break;
+	case CE_LARGE:
+		small_step = unit->step_large;
+		big_step = unit->step_huge;
+		break;
+	default:
+		small_step = big_step = 0;
+		break;
+	}
+
+	adj = GTK_ADJUSTMENT(gtk_adjustment_new(coord_to_unit(unit, value),
+																					coord_to_unit(unit, min_val),
+																					coord_to_unit(unit, max_val), small_step, big_step, 0.0));
+	gtk_spin_button_configure(GTK_SPIN_BUTTON(ce), adj, small_step, unit->default_prec + strlen(unit->suffix));
+	gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(ce), FALSE);
+
+	return GTK_WIDGET(ce);
+}
+
+/*! \brief Gets a GHidCoordEntry's value, in pcb coords */
+Coord ghid_coord_entry_get_value(GHidCoordEntry * ce)
+{
+	return ce->value;
+}
+
+/*! \brief Gets a GHidCoordEntry's value as text */
+int ghid_coord_entry_get_value_str(GHidCoordEntry * ce, char *out, int out_len)
+{
+	GtkAdjustment *adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(ce));
+	double value = gtk_adjustment_get_value(adj);
+	return pcb_snprintf(out, out_len, "%.*f %s", ce->unit->default_prec, value, ce->unit->suffix);
+}
+
+/*! \brief Sets a GHidCoordEntry's value, in pcb coords */
+void ghid_coord_entry_set_value(GHidCoordEntry * ce, Coord val)
+{
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(ce), coord_to_unit(ce->unit, val));
+}
diff --git a/src_plugins/hid_gtk/ghid-coord-entry.h b/src_plugins/hid_gtk/ghid-coord-entry.h
new file mode 100644
index 0000000..69b763f
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-coord-entry.h
@@ -0,0 +1,34 @@
+/* This is the modified GtkSpinbox used for entering Coords.
+ * Hopefully it can be used as a template whenever we migrate the
+ * rest of the Gtk HID to use GObjects and GtkWidget subclassing.
+ */
+#ifndef GHID_COORD_ENTRY_H__
+#define GHID_COORD_ENTRY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "unit.h"
+
+G_BEGIN_DECLS										/* keep c++ happy */
+#define GHID_COORD_ENTRY_TYPE            (ghid_coord_entry_get_type ())
+#define GHID_COORD_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_COORD_ENTRY_TYPE, GHidCoordEntry))
+#define GHID_COORD_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GHID_COORD_ENTRY_TYPE, GHidCoordEntryClass))
+#define IS_GHID_COORD_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_COORD_ENTRY_TYPE))
+#define IS_GHID_COORD_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHID_COORD_ENTRY_TYPE))
+typedef struct _GHidCoordEntry GHidCoordEntry;
+typedef struct _GHidCoordEntryClass GHidCoordEntryClass;
+
+/* Step sizes */
+enum ce_step_size { CE_TINY, CE_SMALL, CE_MEDIUM, CE_LARGE };
+
+GType ghid_coord_entry_get_type(void);
+GtkWidget *ghid_coord_entry_new(Coord min_val, Coord max_val, Coord value, const Unit * unit, enum ce_step_size step_size);
+void ghid_coord_entry_add_entry(GHidCoordEntry * ce, const gchar * name, const gchar * desc);
+gchar *ghid_coord_entry_get_last_command(GHidCoordEntry * ce);
+
+int ghid_coord_entry_get_value_str(GHidCoordEntry * ce, char *out, int out_len);
+Coord ghid_coord_entry_get_value(GHidCoordEntry * ce);
+void ghid_coord_entry_set_value(GHidCoordEntry * ce, Coord val);
+
+G_END_DECLS											/* keep c++ happy */
+#endif
diff --git a/src_plugins/hid_gtk/ghid-layer-selector.c b/src_plugins/hid_gtk/ghid-layer-selector.c
new file mode 100644
index 0000000..fce20c4
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-layer-selector.c
@@ -0,0 +1,806 @@
+/*! \file <gtk-pcb-layer-selector.c>
+ *  \brief Implementation of GHidLayerSelector widget
+ *  \par Description
+ *  This widget is the layer selector on the left side of the Gtk
+ *  GUI. It also builds the relevant sections of the menu for layer
+ *  selection and visibility toggling, and keeps these in sync.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gtkhid.h"
+#include "gui.h"
+#include "pcb-printf.h"
+
+#include "ghid-layer-selector.h"
+#include "ghid-cell-renderer-visibility.h"
+
+#define INITIAL_ACTION_MAX	40
+
+/* Forward dec'ls */
+struct _layer;
+static void ghid_layer_selector_finalize(GObject * object);
+static void menu_pick_cb(GtkRadioAction * action, struct _layer *ldata);
+
+/*! \brief Signals exposed by the widget */
+enum {
+	SELECT_LAYER_SIGNAL,
+	TOGGLE_LAYER_SIGNAL,
+	LAST_SIGNAL
+};
+
+/*! \brief Columns used for internal data store */
+enum {
+	STRUCT_COL,
+	USER_ID_COL,
+	VISIBLE_COL,
+	COLOR_COL,
+	TEXT_COL,
+	FONT_COL,
+	ACTIVATABLE_COL,
+	SEPARATOR_COL,
+	N_COLS
+};
+
+static GtkTreeView *ghid_layer_selector_parent_class;
+static guint ghid_layer_selector_signals[LAST_SIGNAL] = { 0 };
+
+struct _GHidLayerSelector {
+	GtkTreeView parent;
+
+	GtkListStore *list_store;
+	GtkTreeSelection *selection;
+	GtkTreeViewColumn *visibility_column;
+
+	GtkActionGroup *action_group;
+	GtkAccelGroup *accel_group;
+
+	GSList *radio_group;
+	int n_actions;
+
+	gboolean accel_available[20];
+
+	gboolean last_activatable;
+
+	gulong selection_changed_sig_id;
+};
+
+struct _GHidLayerSelectorClass {
+	GtkTreeViewClass parent_class;
+
+	void (*select_layer) (GHidLayerSelector *, gint);
+	void (*toggle_layer) (GHidLayerSelector *, gint);
+};
+
+struct _layer {
+	gint accel_index;							/* Index into ls->accel_available */
+	GtkWidget *pick_item;
+	GtkWidget *view_item;
+	GtkToggleAction *view_action;
+	GtkRadioAction *pick_action;
+	GtkTreeRowReference *rref;
+};
+
+/*! \brief Deletes the action and accelerator from a layer */
+static void free_ldata(GHidLayerSelector * ls, struct _layer *ldata)
+{
+	if (ldata->pick_action) {
+		gtk_action_disconnect_accelerator(GTK_ACTION(ldata->pick_action));
+		gtk_action_group_remove_action(ls->action_group, GTK_ACTION(ldata->pick_action));
+/* TODO: make this work without wrecking the radio action group
+ *           g_object_unref (G_OBJECT (ldata->pick_action)); 
+ *                   */
+	}
+	if (ldata->view_action) {
+		gtk_action_disconnect_accelerator(GTK_ACTION(ldata->view_action));
+		gtk_action_group_remove_action(ls->action_group, GTK_ACTION(ldata->view_action));
+		g_object_unref(G_OBJECT(ldata->view_action));
+	}
+	gtk_tree_row_reference_free(ldata->rref);
+	if (ldata->accel_index >= 0)
+		ls->accel_available[ldata->accel_index] = TRUE;
+	g_free(ldata);
+
+}
+
+/*! \brief internal set-visibility function -- emits no signals */
+static void set_visibility(GHidLayerSelector * ls, GtkTreeIter * iter, struct _layer *ldata, gboolean state)
+{
+	gtk_list_store_set(ls->list_store, iter, VISIBLE_COL, state, -1);
+
+	if ((ldata) && (ldata->view_item)) {
+		gtk_action_block_activate(GTK_ACTION(ldata->view_action));
+		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ldata->view_item), state);
+		gtk_action_unblock_activate(GTK_ACTION(ldata->view_action));
+	}
+}
+
+/*! \brief Flip the visibility state of a given layer 
+ *  \par Function Description
+ *  Changes the internal toggle state and menu checkbox state
+ *  of the layer pointed to by iter. Emits a toggle-layer signal.
+ *
+ *  \param [in] ls    The selector to be acted on
+ *  \param [in] iter  A GtkTreeIter pointed at the relevant layer
+ *  \param [in] emit  Whether or not to emit a signal
+ */
+static void toggle_visibility(GHidLayerSelector * ls, GtkTreeIter * iter, gboolean emit)
+{
+	gint user_id;
+	struct _layer *ldata;
+	gboolean toggle;
+	gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), iter, USER_ID_COL, &user_id, VISIBLE_COL, &toggle, STRUCT_COL, &ldata, -1);
+	set_visibility(ls, iter, ldata, !toggle);
+	if (emit)
+		g_signal_emit(ls, ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL], 0, user_id);
+}
+
+/*! \brief Decide if a GtkListStore entry is a layer or separator */
+static gboolean tree_view_separator_func(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
+{
+	gboolean ret_val;
+	gtk_tree_model_get(model, iter, SEPARATOR_COL, &ret_val, -1);
+	return ret_val;
+}
+
+/*! \brief Decide if a GtkListStore entry may be selected */
+static gboolean
+tree_selection_func(GtkTreeSelection * selection, GtkTreeModel * model, GtkTreePath * path, gboolean selected, gpointer data)
+{
+	GtkTreeIter iter;
+
+	if (gtk_tree_model_get_iter(model, &iter, path)) {
+		gboolean activatable;
+		gtk_tree_model_get(model, &iter, ACTIVATABLE_COL, &activatable, -1);
+		return activatable;
+	}
+
+	return FALSE;
+}
+
+/* SIGNAL HANDLERS */
+/*! \brief Callback for mouse-click: toggle visibility */
+static gboolean button_press_cb(GHidLayerSelector * ls, GdkEventButton * event)
+{
+	/* Handle visibility independently to prevent changing the active
+	 *  layer, which will happen if we let this event propagate.  */
+	GtkTreeViewColumn *column;
+	GtkTreePath *path;
+
+	/* Ignore the synthetic presses caused by double and tripple clicks, and
+	 * also ignore all but left-clicks
+	 */
+	if (event->type != GDK_BUTTON_PRESS || event->button != 1)
+		return TRUE;
+
+	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(ls), event->x, event->y, &path, &column, NULL, NULL)) {
+		GtkTreeIter iter;
+		gboolean activatable, separator;
+		gtk_tree_model_get_iter(GTK_TREE_MODEL(ls->list_store), &iter, path);
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, ACTIVATABLE_COL, &activatable, SEPARATOR_COL, &separator, -1);
+		/* Toggle visibility for non-activatable layers no matter
+		 *  where you click. */
+		if (!separator && (column == ls->visibility_column || !activatable)) {
+			toggle_visibility(ls, &iter, TRUE);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/*! \brief Callback for layer selection change: sync menu, emit signal */
+static void selection_changed_cb(GtkTreeSelection * selection, GHidLayerSelector * ls)
+{
+	GtkTreeIter iter;
+	if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+		gint user_id;
+		struct _layer *ldata;
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, STRUCT_COL, &ldata, USER_ID_COL, &user_id, -1);
+
+		if (ldata && ldata->pick_action) {
+			gtk_action_block_activate(GTK_ACTION(ldata->pick_action));
+			gtk_radio_action_set_current_value(ldata->pick_action, user_id);
+			gtk_action_unblock_activate(GTK_ACTION(ldata->pick_action));
+		}
+		g_signal_emit(ls, ghid_layer_selector_signals[SELECT_LAYER_SIGNAL], 0, user_id);
+	}
+}
+
+/*! \brief Callback for menu actions: sync layer selection list, emit signal */
+static void menu_view_cb(GtkToggleAction * action, struct _layer *ldata)
+{
+	GHidLayerSelector *ls;
+	GtkTreeModel *model = gtk_tree_row_reference_get_model(ldata->rref);
+	GtkTreePath *path = gtk_tree_row_reference_get_path(ldata->rref);
+	gboolean state = gtk_toggle_action_get_active(action);
+	GtkTreeIter iter;
+	gint user_id;
+
+	gtk_tree_model_get_iter(model, &iter, path);
+	gtk_list_store_set(GTK_LIST_STORE(model), &iter, VISIBLE_COL, state, -1);
+	gtk_tree_model_get(model, &iter, USER_ID_COL, &user_id, -1);
+
+	ls = g_object_get_data(G_OBJECT(model), "layer-selector");
+	g_signal_emit(ls, ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL], 0, user_id);
+}
+
+/*! \brief Callback for menu actions: sync layer selection list, emit signal */
+static void menu_pick_cb(GtkRadioAction * action, struct _layer *ldata)
+{
+	/* We only care about the activation signal (as opposed to deactivation).
+	 * A row we are /deactivating/ might not even exist anymore! */
+	if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))) {
+		GHidLayerSelector *ls;
+		GtkTreeModel *model = gtk_tree_row_reference_get_model(ldata->rref);
+		GtkTreePath *path = gtk_tree_row_reference_get_path(ldata->rref);
+		GtkTreeIter iter;
+		gint user_id;
+
+		gtk_tree_model_get_iter(model, &iter, path);
+		gtk_tree_model_get(model, &iter, USER_ID_COL, &user_id, -1);
+
+		ls = g_object_get_data(G_OBJECT(model), "layer-selector");
+		g_signal_handler_block(ls->selection, ls->selection_changed_sig_id);
+		gtk_tree_selection_select_path(ls->selection, path);
+		g_signal_handler_unblock(ls->selection, ls->selection_changed_sig_id);
+		g_signal_emit(ls, ghid_layer_selector_signals[SELECT_LAYER_SIGNAL], 0, user_id);
+	}
+}
+
+/* CONSTRUCTOR */
+static void ghid_layer_selector_init(GHidLayerSelector * ls)
+{
+	/* Hookup signal handlers */
+}
+
+static void ghid_layer_selector_class_init(GHidLayerSelectorClass * klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+
+	ghid_layer_selector_signals[SELECT_LAYER_SIGNAL] =
+		g_signal_new("select-layer",
+								 G_TYPE_FROM_CLASS(klass),
+								 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+								 G_STRUCT_OFFSET(GHidLayerSelectorClass, select_layer),
+								 NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+	ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL] =
+		g_signal_new("toggle-layer",
+								 G_TYPE_FROM_CLASS(klass),
+								 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+								 G_STRUCT_OFFSET(GHidLayerSelectorClass, toggle_layer),
+								 NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+	object_class->finalize = ghid_layer_selector_finalize;
+}
+
+/*! \brief Clean up object before garbage collection
+ */
+static void ghid_layer_selector_finalize(GObject * object)
+{
+	GtkTreeIter iter;
+	GHidLayerSelector *ls = (GHidLayerSelector *) object;
+
+	g_object_unref(ls->accel_group);
+	g_object_unref(ls->action_group);
+
+	gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+	do {
+		struct _layer *ldata;
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, STRUCT_COL, &ldata, -1);
+		free_ldata(ls, ldata);
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+
+	G_OBJECT_CLASS(ghid_layer_selector_parent_class)->finalize(object);
+}
+
+/* PUBLIC FUNCTIONS */
+GType ghid_layer_selector_get_type(void)
+{
+	static GType ls_type = 0;
+
+	if (!ls_type) {
+		const GTypeInfo ls_info = {
+			sizeof(GHidLayerSelectorClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_layer_selector_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GHidLayerSelector),
+			0,												/* n_preallocs */
+			(GInstanceInitFunc) ghid_layer_selector_init,
+		};
+
+		ls_type = g_type_register_static(GTK_TYPE_TREE_VIEW, "GHidLayerSelector", &ls_info, 0);
+	}
+
+	return ls_type;
+}
+
+/*! \brief Create a new GHidLayerSelector
+ *
+ *  \return a freshly-allocated GHidLayerSelector.
+ */
+GtkWidget *ghid_layer_selector_new(void)
+{
+	int i;
+	GtkCellRenderer *renderer1 = ghid_cell_renderer_visibility_new();
+	GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new();
+	GtkTreeViewColumn *opacity_col = gtk_tree_view_column_new_with_attributes("", renderer1,
+																																						"active", VISIBLE_COL,
+																																						"color", COLOR_COL, NULL);
+	GtkTreeViewColumn *name_col = gtk_tree_view_column_new_with_attributes("", renderer2,
+																																				 "text", TEXT_COL,
+																																				 "font", FONT_COL,
+																																				 NULL);
+
+	GHidLayerSelector *ls = g_object_new(GHID_LAYER_SELECTOR_TYPE, NULL);
+
+	/* action index, active, color, text, font, is_separator */
+	ls->list_store = gtk_list_store_new(N_COLS, G_TYPE_POINTER, G_TYPE_INT,
+																			G_TYPE_BOOLEAN, G_TYPE_STRING,
+																			G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+	gtk_tree_view_insert_column(GTK_TREE_VIEW(ls), opacity_col, -1);
+	gtk_tree_view_insert_column(GTK_TREE_VIEW(ls), name_col, -1);
+	gtk_tree_view_set_model(GTK_TREE_VIEW(ls), GTK_TREE_MODEL(ls->list_store));
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ls), FALSE);
+
+	ls->last_activatable = TRUE;
+	ls->visibility_column = opacity_col;
+	ls->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ls));
+	ls->accel_group = gtk_accel_group_new();
+	ls->action_group = gtk_action_group_new("LayerSelector");
+	ls->n_actions = 0;
+	for (i = 0; i < 20; ++i)
+		ls->accel_available[i] = TRUE;
+
+	gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(ls), tree_view_separator_func, NULL, NULL);
+	gtk_tree_selection_set_select_function(ls->selection, tree_selection_func, NULL, NULL);
+	gtk_tree_selection_set_mode(ls->selection, GTK_SELECTION_BROWSE);
+
+	g_object_set_data(G_OBJECT(ls->list_store), "layer-selector", ls);
+	g_signal_connect(ls, "button_press_event", G_CALLBACK(button_press_cb), NULL);
+	ls->selection_changed_sig_id = g_signal_connect(ls->selection, "changed", G_CALLBACK(selection_changed_cb), ls);
+
+	g_object_ref(ls->accel_group);
+
+	return GTK_WIDGET(ls);
+}
+
+/*! \brief Add a layer to a GHidLayerSelector.
+ *  \par Function Description
+ *  This function adds an entry to a GHidLayerSelector, which will
+ *  appear in the layer-selection list as well as visibility and selection
+ *  menus (assuming this is a selectable layer). For the first 20 layers,
+ *  keyboard accelerators will be added for selection/visibility toggling.
+ *
+ *  If the user_id passed already exists in the layer selector, that layer
+ *  will have its data overwritten with the new stuff.
+ *
+ *  \param [in] ls            The selector to be acted on
+ *  \param [in] user_id       An ID used to identify the layer; will be passed to selection/visibility callbacks
+ *  \param [in] name          The name of the layer; will be used on selector and menus
+ *  \param [in] color_string  The color of the layer on selector
+ *  \param [in] visibile      Whether the layer is visible
+ *  \param [in] activatable   Whether the layer appears in menus and can be selected
+ */
+void
+ghid_layer_selector_add_layer(GHidLayerSelector * ls,
+															gint user_id,
+															const gchar * name, const gchar * color_string, gboolean visible, gboolean activatable)
+{
+	struct _layer *new_layer = NULL;
+	gchar *pname, *vname;
+	gboolean new_iter = TRUE;
+	gboolean last_activatable = TRUE;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	int i;
+
+	/* Look for existing layer with this ID */
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter))
+		do {
+			gboolean is_sep, active;
+			gint read_id;
+			gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store),
+												 &iter, USER_ID_COL, &read_id, SEPARATOR_COL, &is_sep, ACTIVATABLE_COL, &active, -1);
+			if (!is_sep) {
+				last_activatable = active;
+				if (read_id == user_id) {
+					new_iter = FALSE;
+					break;
+				}
+			}
+		}
+		while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+
+	/* Handle separator addition */
+	if (new_iter) {
+		if (activatable != last_activatable) {
+			/* Add separator between activatable/non-activatable boundaries */
+			gtk_list_store_append(ls->list_store, &iter);
+			gtk_list_store_set(ls->list_store, &iter, STRUCT_COL, NULL, SEPARATOR_COL, TRUE, -1);
+		}
+		/* Create new layer */
+		new_layer = malloc(sizeof(*new_layer));
+		gtk_list_store_append(ls->list_store, &iter);
+		gtk_list_store_set(ls->list_store, &iter,
+											 STRUCT_COL, new_layer,
+											 USER_ID_COL, user_id,
+											 VISIBLE_COL, visible,
+											 COLOR_COL, color_string,
+											 TEXT_COL, name,
+											 FONT_COL, activatable ? NULL : "Italic", ACTIVATABLE_COL, activatable, SEPARATOR_COL, FALSE, -1);
+	}
+	else {
+		/* If the row exists, we clear out its ldata to create
+		 * a new action, accelerator and menu item. */
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, STRUCT_COL, &new_layer, -1);
+		free_ldata(ls, new_layer);
+		new_layer = malloc(sizeof(*new_layer));
+
+		gtk_list_store_set(ls->list_store, &iter,
+											 STRUCT_COL, new_layer,
+											 VISIBLE_COL, visible,
+											 COLOR_COL, color_string,
+											 TEXT_COL, name, FONT_COL, activatable ? NULL : "Italic", ACTIVATABLE_COL, activatable, -1);
+	}
+
+	/* -- Setup new actions -- */
+	vname = g_strdup_printf("LayerView%d", ls->n_actions);
+	pname = g_strdup_printf("LayerPick%d", ls->n_actions);
+
+	/* Create row reference for actions */
+	path = gtk_tree_model_get_path(GTK_TREE_MODEL(ls->list_store), &iter);
+	new_layer->rref = gtk_tree_row_reference_new(GTK_TREE_MODEL(ls->list_store), path);
+	gtk_tree_path_free(path);
+
+	/* Create selection action */
+	if (activatable) {
+		new_layer->pick_action = gtk_radio_action_new(pname, name, NULL, NULL, user_id);
+		gtk_radio_action_set_group(new_layer->pick_action, ls->radio_group);
+		ls->radio_group = gtk_radio_action_get_group(new_layer->pick_action);
+	}
+	else
+		new_layer->pick_action = NULL;
+
+	/* Create visibility action */
+	new_layer->view_action = gtk_toggle_action_new(vname, name, NULL, NULL);
+	gtk_toggle_action_set_active(new_layer->view_action, visible);
+
+	/* Determine keyboard accelerators */
+	for (i = 0; i < 20; ++i)
+		if (ls->accel_available[i])
+			break;
+	if (i < 20) {
+		/* Map 1-0 to actions 1-10 (with '0' meaning 10) */
+		gchar *accel1 = g_strdup_printf("%s%d",
+																		i < 10 ? "" : "<Alt>",
+																		(i + 1) % 10);
+		gchar *accel2 = g_strdup_printf("<Ctrl>%s%d",
+																		i < 10 ? "" : "<Alt>",
+																		(i + 1) % 10);
+
+		if (activatable) {
+			GtkAction *action = GTK_ACTION(new_layer->pick_action);
+			gtk_action_set_accel_group(action, ls->accel_group);
+			gtk_action_group_add_action_with_accel(ls->action_group, action, accel1);
+			gtk_action_connect_accelerator(action);
+			g_signal_connect(G_OBJECT(action), "activate", G_CALLBACK(menu_pick_cb), new_layer);
+		}
+		gtk_action_set_accel_group(GTK_ACTION(new_layer->view_action), ls->accel_group);
+		gtk_action_group_add_action_with_accel(ls->action_group, GTK_ACTION(new_layer->view_action), accel2);
+		gtk_action_connect_accelerator(GTK_ACTION(new_layer->view_action));
+		g_signal_connect(G_OBJECT(new_layer->view_action), "activate", G_CALLBACK(menu_view_cb), new_layer);
+
+		ls->accel_available[i] = FALSE;
+		new_layer->accel_index = i;
+		g_free(accel2);
+		g_free(accel1);
+	}
+	else {
+		new_layer->accel_index = -1;
+	}
+	/* finalize new layer struct */
+	new_layer->pick_item = new_layer->view_item = NULL;
+
+	/* cleanup */
+	g_free(vname);
+	g_free(pname);
+
+	ls->n_actions++;
+}
+
+/*! \brief Install the "Current Layer" menu items for a layer selector
+ *  \par Function Description
+ *  Takes a menu shell and installs menu items for layer selection in
+ *  the shell, at the given position.
+ *
+ *  \param [in] ls      The selector to be acted on
+ *  \param [in] shell   The menu to install the items in
+ *  \param [in] pos     The position in the menu to install items
+ *
+ *  \return the number of items installed
+ */
+gint ghid_layer_selector_install_pick_items(GHidLayerSelector * ls, GtkMenuShell * shell, gint pos)
+{
+	GtkTreeIter iter;
+	int n = 0;
+
+	gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+	do {
+		struct _layer *ldata;
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, STRUCT_COL, &ldata, -1);
+		if (ldata && ldata->pick_action) {
+			GtkAction *action = GTK_ACTION(ldata->pick_action);
+			ldata->pick_item = gtk_action_create_menu_item(action);
+			gtk_menu_shell_insert(shell, ldata->pick_item, pos + n);
+			++n;
+		}
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+
+	return n;
+}
+
+/*! \brief Install the "Shown Layers" menu items for a layer selector
+ *  \par Function Description
+ *  Takes a menu shell and installs menu items for layer selection in
+ *  the shell, at the given position.
+ *
+ *  \param [in] ls      The selector to be acted on
+ *  \param [in] shell   The menu to install the items in
+ *  \param [in] pos     The position in the menu to install items
+ *
+ *  \return the number of items installed
+ */
+gint ghid_layer_selector_install_view_items(GHidLayerSelector * ls, GtkMenuShell * shell, gint pos)
+{
+	GtkTreeIter iter;
+	int n = 0;
+
+	gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+	do {
+		struct _layer *ldata;
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, STRUCT_COL, &ldata, -1);
+		if (ldata && ldata->view_action) {
+			GtkAction *action = GTK_ACTION(ldata->view_action);
+			ldata->view_item = gtk_action_create_menu_item(action);
+			gtk_menu_shell_insert(shell, ldata->view_item, pos + n);
+			++n;
+		}
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+
+	return n;
+}
+
+/*! \brief Returns the GtkAccelGroup of a layer selector
+ *  \par Function Description
+ *
+ *  \param [in] ls            The selector to be acted on
+ *
+ *  \return the accel group of the selector
+ */
+GtkAccelGroup *ghid_layer_selector_get_accel_group(GHidLayerSelector * ls)
+{
+	return ls->accel_group;
+}
+
+/*! \brief used internally */
+static gboolean toggle_foreach_func(GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data)
+{
+	gint id;
+	GHidLayerSelector *ls = g_object_get_data(G_OBJECT(model),
+																						"layer-selector");
+
+	gtk_tree_model_get(model, iter, USER_ID_COL, &id, -1);
+	if (id == *(gint *) data) {
+		toggle_visibility(ls, iter, TRUE);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/*! \brief Toggle a layer's visibility
+ *  \par Function Description
+ *  Toggle the layer indicated by user_id, emitting a layer-toggle signal.
+ *
+ *  \param [in] ls       The selector to be acted on
+ *  \param [in] user_id  The ID of the layer to be affected
+ */
+void ghid_layer_selector_toggle_layer(GHidLayerSelector * ls, gint user_id)
+{
+	gtk_tree_model_foreach(GTK_TREE_MODEL(ls->list_store), toggle_foreach_func, &user_id);
+}
+
+/*! \brief used internally */
+static gboolean select_foreach_func(GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data)
+{
+	gint id;
+	GHidLayerSelector *ls = g_object_get_data(G_OBJECT(model),
+																						"layer-selector");
+
+	gtk_tree_model_get(model, iter, USER_ID_COL, &id, -1);
+	if (id == *(gint *) data) {
+		gtk_tree_selection_select_path(ls->selection, path);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/*! \brief Select a layer
+ *  \par Function Description
+ *  Select the layer indicated by user_id, emitting a layer-select signal.
+ *
+ *  \param [in] ls       The selector to be acted on
+ *  \param [in] user_id  The ID of the layer to be affected
+ */
+void ghid_layer_selector_select_layer(GHidLayerSelector * ls, gint user_id)
+{
+	gtk_tree_model_foreach(GTK_TREE_MODEL(ls->list_store), select_foreach_func, &user_id);
+}
+
+/*! \brief Selects the next visible layer
+ *  \par Function Description
+ *  Used to ensure hidden layers are not active; if the active layer is
+ *  visible, this function is a noop. Otherwise, it will look for the
+ *  next layer that IS visible, and select that. Failing that, it will
+ *  return FALSE.
+ *
+ *  \param [in] ls       The selector to be acted on
+ *
+ *  \return TRUE on success, FALSE if all selectable layers are hidden
+ */
+gboolean ghid_layer_selector_select_next_visible(GHidLayerSelector * ls)
+{
+	GtkTreeIter iter;
+	if (gtk_tree_selection_get_selected(ls->selection, NULL, &iter)) {
+		/* Scan forward, looking for selectable iter */
+		do {
+			gboolean visible, activatable;
+			gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, VISIBLE_COL, &visible, ACTIVATABLE_COL, &activatable, -1);
+			if (visible && activatable) {
+				gtk_tree_selection_select_iter(ls->selection, &iter);
+				return TRUE;
+			}
+		}
+		while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+		/* Move iter to start, and repeat. */
+		gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+		do {
+			gboolean visible, activatable;
+			gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, VISIBLE_COL, &visible, ACTIVATABLE_COL, &activatable, -1);
+			if (visible && activatable) {
+				gtk_tree_selection_select_iter(ls->selection, &iter);
+				return TRUE;
+			}
+		}
+		while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+		/* Failing this, just emit a selected signal on the original layer. */
+		selection_changed_cb(ls->selection, ls);
+	}
+	/* If we get here, nothing is selectable, so fail. */
+	return FALSE;
+}
+
+/*! \brief Makes the selected layer visible
+ *  \par Function Description
+ *  Used to ensure hidden layers are not active; un-hides the currently
+ *  selected layer.
+ *
+ *  \param [in] ls       The selector to be acted on
+ */
+void ghid_layer_selector_make_selected_visible(GHidLayerSelector * ls)
+{
+	GtkTreeIter iter;
+	if (gtk_tree_selection_get_selected(ls->selection, NULL, &iter)) {
+		gboolean visible;
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, VISIBLE_COL, &visible, -1);
+		if (!visible)
+			toggle_visibility(ls, &iter, FALSE);
+	}
+}
+
+/*! \brief Sets the colors of all layers in a layer-selector
+ *  \par Function Description
+ *  Updates the colors of a layer selector via a callback mechanism:
+ *  the user_id of each layer is passed to the callback function,
+ *  which returns a color string to update the layer's color, or NULL
+ *  to leave it alone.
+ *
+ *  \param [in] ls       The selector to be acted on
+ *  \param [in] callback Takes the user_id of the layer and returns a color string
+ */
+void ghid_layer_selector_update_colors(GHidLayerSelector * ls, const gchar * (*callback) (int user_id))
+{
+	GtkTreeIter iter;
+	gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+	do {
+		gint user_id;
+		const gchar *new_color;
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store), &iter, USER_ID_COL, &user_id, -1);
+		new_color = callback(user_id);
+		if (new_color != NULL)
+			gtk_list_store_set(ls->list_store, &iter, COLOR_COL, new_color, -1);
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+}
+
+/*! \brief Deletes layers from a layer selector
+ *  \par Function Description
+ *  Deletes layers according to a callback function: a return value of TRUE
+ *  means delete, FALSE means leave it alone. Do not try to delete all layers
+ *  using this function; with nothing left to select, pcb will likely go into
+ *  an infinite recursion between hid_action() and g_signal().
+ *
+ *  Separators will be deleted if the layer AFTER them is deleted.
+ *
+ *  \param [in] ls       The selector to be acted on
+ *  \param [in] callback Takes the user_id of the layer and returns a boolean
+ */
+void ghid_layer_selector_delete_layers(GHidLayerSelector * ls, gboolean(*callback) (int user_id))
+{
+	GtkTreeIter iter, last_iter;
+
+	gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+	while (iter_valid) {
+		struct _layer *ldata;
+		gboolean sep, was_sep = FALSE;
+		gint user_id;
+
+		/* Find next iter to delete */
+		while (iter_valid) {
+			gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store),
+												 &iter, USER_ID_COL, &user_id, STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
+			if (!sep && callback(user_id))
+				break;
+
+			/* save iter in case it's a bad separator */
+			was_sep = sep;
+			last_iter = iter;
+			/* iterate */
+			iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter);
+		}
+
+		if (iter_valid) {
+			/* remove preceeding separator */
+			if (was_sep)
+				gtk_list_store_remove(ls->list_store, &last_iter);
+
+					/*** remove row ***/
+			iter_valid = gtk_list_store_remove(ls->list_store, &iter);
+			free_ldata(ls, ldata);
+		}
+		last_iter = iter;
+	}
+}
+
+/*! \brief Sets the visibility toggle-state of all layers
+ *  \par Function Description
+ *  Shows layers according to a callback function: a return value of TRUE
+ *  means show, FALSE means hide.
+ *
+ *  \param [in] ls       The selector to be acted on
+ *  \param [in] callback Takes the user_id of the layer and returns a boolean
+ */
+void ghid_layer_selector_show_layers(GHidLayerSelector * ls, gboolean(*callback) (int user_id))
+{
+	GtkTreeIter iter;
+	gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls->list_store), &iter);
+	do {
+		struct _layer *ldata;
+		gboolean sep;
+		gint user_id;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(ls->list_store),
+											 &iter, USER_ID_COL, &user_id, STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
+		if (!sep)
+			set_visibility(ls, &iter, ldata, callback(user_id));
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ls->list_store), &iter));
+}
diff --git a/src_plugins/hid_gtk/ghid-layer-selector.h b/src_plugins/hid_gtk/ghid-layer-selector.h
new file mode 100644
index 0000000..56a00a3
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-layer-selector.h
@@ -0,0 +1,38 @@
+#ifndef GHID_LAYER_SELECTOR_H__
+#define GHID_LAYER_SELECTOR_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS										/* keep c++ happy */
+#define GHID_LAYER_SELECTOR_TYPE            (ghid_layer_selector_get_type ())
+#define GHID_LAYER_SELECTOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_LAYER_SELECTOR_TYPE, GHidLayerSelector))
+#define GHID_LAYER_SELECTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GHID_LAYER_SELECTOR_TYPE, GHidLayerSelectorClass))
+#define IS_GHID_LAYER_SELECTOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_LAYER_SELECTOR_TYPE))
+#define IS_GHID_LAYER_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHID_LAYER_SELECTOR_TYPE))
+typedef struct _GHidLayerSelector GHidLayerSelector;
+typedef struct _GHidLayerSelectorClass GHidLayerSelectorClass;
+
+GType ghid_layer_selector_get_type(void);
+GtkWidget *ghid_layer_selector_new(void);
+
+void ghid_layer_selector_add_layer(GHidLayerSelector * ls,
+																	 gint user_id,
+																	 const gchar * name, const gchar * color_string, gboolean visible, gboolean activatable);
+
+gint ghid_layer_selector_install_pick_items(GHidLayerSelector * ls, GtkMenuShell * shell, gint pos);
+gint ghid_layer_selector_install_view_items(GHidLayerSelector * ls, GtkMenuShell * shell, gint pos);
+
+GtkAccelGroup *ghid_layer_selector_get_accel_group(GHidLayerSelector * ls);
+
+void ghid_layer_selector_toggle_layer(GHidLayerSelector * ls, gint user_id);
+void ghid_layer_selector_select_layer(GHidLayerSelector * ls, gint user_id);
+gboolean ghid_layer_selector_select_next_visible(GHidLayerSelector * ls);
+void ghid_layer_selector_make_selected_visible(GHidLayerSelector * ls);
+void ghid_layer_selector_update_colors(GHidLayerSelector * ls, const gchar * (*callback) (int user_id));
+void ghid_layer_selector_delete_layers(GHidLayerSelector * ls, gboolean(*callback) (int user_id));
+void ghid_layer_selector_show_layers(GHidLayerSelector * ls, gboolean(*callback) (int user_id));
+
+G_END_DECLS											/* keep c++ happy */
+#endif
diff --git a/src_plugins/hid_gtk/ghid-main-menu.c b/src_plugins/hid_gtk/ghid-main-menu.c
new file mode 100644
index 0000000..eadc587
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-main-menu.c
@@ -0,0 +1,521 @@
+/*! \file <ghid-main-menu.c>
+ *  \brief Implementation of GHidMainMenu widget
+ *  \par Description
+ *  This widget is the main pcb menu.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <liblihata/tree.h>
+
+#include "gtkhid.h"
+#include "gui.h"
+#include "pcb-printf.h"
+#include "misc_util.h"
+#include "error.h"
+#include "conf.h"
+#include "ghid-main-menu.h"
+#include "ghid-layer-selector.h"
+#include "ghid-route-style-selector.h"
+#include "gschem_accel_label.h"
+
+static int action_counter;
+
+typedef struct {
+	GtkWidget *widget;  /* for most uses */
+	GtkWidget *destroy; /* destroy this */
+} menu_handle_t;
+
+static menu_handle_t *handle_alloc(GtkWidget *widget, GtkWidget *destroy)
+{
+	menu_handle_t *m = malloc(sizeof(menu_handle_t));
+	m->widget = widget;
+	m->destroy = destroy;
+	return m;
+}
+
+struct _GHidMainMenu {
+	GtkMenuBar parent;
+
+	GtkActionGroup *action_group;
+	GtkAccelGroup *accel_group;
+
+	gint layer_view_pos;
+	gint layer_pick_pos;
+	gint route_style_pos;
+
+	GtkMenuShell *layer_view_shell;
+	GtkMenuShell *layer_pick_shell;
+	GtkMenuShell *route_style_shell;
+
+	GList *actions;
+
+	gint n_layer_views;
+	gint n_layer_picks;
+	gint n_route_styles;
+
+	GCallback action_cb;
+};
+
+struct _GHidMainMenuClass {
+	GtkMenuBarClass parent_class;
+};
+
+/* TODO: write finalize function */
+
+/* SIGNAL HANDLERS */
+
+/* LHT HANDLER */
+
+void ghid_main_menu_real_add_node(GHidMainMenu * menu, GtkMenuShell * shell, lht_node_t *base);
+
+extern conf_hid_id_t ghid_menuconf_id;
+static GtkAction *ghid_add_menu(GHidMainMenu * menu, GtkMenuShell * shell, lht_node_t * sub_res)
+{
+	const char *tmp_val;
+	gchar mnemonic = 0;
+	GtkAction *action = NULL;
+	char *accel = NULL;
+	char *menu_label;
+	lht_node_t *n_action = hid_cfg_menu_field(sub_res, MF_ACTION, NULL);
+	lht_node_t *n_keydesc = hid_cfg_menu_field(sub_res, MF_ACCELERATOR, NULL);
+
+	/* Resolve accelerator and save it */
+	if (n_keydesc != NULL) {
+		if (n_action != NULL) {
+			hid_cfg_keys_add_by_desc(&ghid_keymap, n_keydesc, n_action, NULL, 0);
+			accel = hid_cfg_keys_gen_accel(&ghid_keymap, n_keydesc, 1, NULL);
+		}
+		else
+			hid_cfg_error(sub_res, "No action specified for key accel\n");
+	}
+
+	/* Resolve the mnemonic */
+	tmp_val = hid_cfg_menu_field_str(sub_res, MF_MNEMONIC);
+	if (tmp_val)
+		mnemonic = tmp_val[0];
+
+	/* Resolve menu name */
+	tmp_val = sub_res->name;
+
+	/* Hack '_' in based on mnemonic value */
+	if (!mnemonic)
+		menu_label = g_strdup(tmp_val);
+	else {
+		char *post_ = strchr(tmp_val, mnemonic);
+		if (post_ == NULL)
+			menu_label = g_strdup(tmp_val);
+		else {
+			GString *tmp = g_string_new("");
+			g_string_append_len(tmp, tmp_val, post_ - tmp_val);
+			g_string_append_c(tmp, '_');
+			g_string_append(tmp, post_);
+			menu_label = g_string_free(tmp, FALSE);
+		}
+	}
+
+	if (hid_cfg_has_submenus(sub_res)) {
+		/* SUBMENU */
+		GtkWidget *submenu = gtk_menu_new();
+		GtkWidget *item = gtk_menu_item_new_with_mnemonic(menu_label);
+		GtkWidget *tearoff = gtk_tearoff_menu_item_new();
+		lht_node_t *n;
+
+		sub_res->user_data = handle_alloc(submenu, item);
+
+		gtk_menu_shell_append(shell, item);
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+
+		/* add tearoff to menu */
+		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), tearoff);
+
+		/* recurse on the newly-added submenu; hid_cfg_has_submenus() makes sure
+		   the node format is correct; iterate over the list of submenus and create
+		   them recursively. */
+		n = hid_cfg_menu_field(sub_res, MF_SUBMENU, NULL);
+		for(n = n->data.list.first; n != NULL; n = n->next)
+			ghid_main_menu_real_add_node(menu, GTK_MENU_SHELL(submenu), n);
+	}
+	else {
+		/* NON-SUBMENU: MENU ITEM */
+		const char *checked = hid_cfg_menu_field_str(sub_res, MF_CHECKED);
+		const char *update_on = hid_cfg_menu_field_str(sub_res, MF_UPDATE_ON);
+		const char *label = hid_cfg_menu_field_str(sub_res, MF_SENSITIVE);
+		const char *tip = hid_cfg_menu_field_str(sub_res, MF_TIP);
+		if (checked) {
+			/* TOGGLE ITEM */
+			conf_native_t *nat = NULL;
+			gchar *name = g_strdup_printf("MainMenuAction%d", action_counter++);
+			action = GTK_ACTION(gtk_toggle_action_new(name, menu_label, tip, NULL));
+			/* checked=foo       is a binary flag (checkbox)
+			 * checked=foo=bar   is a flag compared to a value (radio) */
+			gtk_toggle_action_set_draw_as_radio(GTK_TOGGLE_ACTION(action), ! !strchr(checked, '='));
+
+			if (update_on != NULL)
+				nat = conf_get_field(update_on);
+			else
+				nat = conf_get_field(checked);
+
+			if (nat != NULL) {
+				static conf_hid_callbacks_t cbs;
+				static int cbs_inited = 0;
+				if (!cbs_inited) {
+					memset(&cbs, 0, sizeof(conf_hid_callbacks_t));
+					cbs.val_change_post = ghid_confchg_checkbox;
+					cbs_inited = 1;
+				}
+/*				pcb_trace("conf_hid_set for %s -> %s\n", checked, nat->hash_path);*/
+				conf_hid_set_cb(nat, ghid_menuconf_id, &cbs);
+			}
+			else {
+				if ((update_on == NULL) || (*update_on != '\0')) /* warn if update_on is not explicitly empty */
+					Message(PCB_MSG_WARNING, "Checkbox menu item not %s updated on any conf change - try to use the update_on field\n", checked);
+			}
+		}
+		else if (label && strcmp(label, "false") == 0) {
+			/* INSENSITIVE ITEM */
+			GtkWidget *item = gtk_menu_item_new_with_label(menu_label);
+			gtk_widget_set_sensitive(item, FALSE);
+			gtk_menu_shell_append(shell, item);
+			sub_res->user_data = handle_alloc(item, item);
+		}
+		else {
+			/* NORMAL ITEM */
+			GtkWidget *item = gtk_menu_item_new_gschem(menu_label, accel);
+			accel = NULL;
+			gtk_menu_shell_append(shell, item);
+			sub_res->user_data = handle_alloc(item, item);
+			g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu->action_cb), (gpointer)n_action);
+			if ((tip != NULL) || (n_keydesc != NULL)) {
+				char *acc = NULL, *s;
+				if (n_keydesc != NULL)
+					acc = hid_cfg_keys_gen_accel(&ghid_keymap, n_keydesc, -1, "\nhotkey: ");
+				s = Concat((tip == NULL ? "" : tip), "\nhotkey: ", (acc == NULL ? "" : acc), NULL);
+				gtk_widget_set_tooltip_text(item, s);
+				free(s);
+			}
+		}
+
+	}
+
+	/* By now this runs only for toggle items. */
+	if (action) {
+		GtkWidget *item;
+		gtk_action_set_accel_group(action, menu->accel_group);
+/*		gtk_action_group_add_action_with_accel(menu->action_group, action, accel);*/
+		gtk_action_connect_accelerator(action);
+		g_signal_connect(G_OBJECT(action), "activate", menu->action_cb, (gpointer) n_action);
+		g_object_set_data(G_OBJECT(action), "resource", (gpointer) sub_res);
+		item = gtk_action_create_menu_item(action);
+		gtk_menu_shell_append(shell, item);
+		menu->actions = g_list_append(menu->actions, action);
+		sub_res->user_data = handle_alloc(item, item);
+	}
+
+	/* unused accel key - generated, but never stored, time to free it */
+	if (accel != NULL)
+		free(accel);
+
+	return action;
+}
+
+/*! \brief Translate a resource tree into a menu structure
+ *
+ *  \param [in] menu    The GHidMainMenu widget to be acted on
+ *  \param [in] shall   The base menu shell (a menu bar or popup menu)
+ *  \param [in] base    The base of the menu item subtree
+ * */
+void ghid_main_menu_real_add_node(GHidMainMenu * menu, GtkMenuShell * shell, lht_node_t *base)
+{
+	switch(base->type) {
+		case LHT_HASH: /* leaf submenu */
+			{
+				GtkAction *action = NULL;
+				action = ghid_add_menu(menu, shell, base);
+				if (action) {
+					const char *val;
+
+					val = hid_cfg_menu_field_str(base, MF_CHECKED);
+					if (val != NULL)
+						g_object_set_data(G_OBJECT(action), "checked-flag", (gpointer *)val);
+
+					val = hid_cfg_menu_field_str(base, MF_ACTIVE);
+					if (val != NULL)
+						g_object_set_data(G_OBJECT(action), "active-flag", (gpointer *)val);
+				}
+			}
+			break;
+		case LHT_TEXT: /* separator */
+			{
+				GList *children;
+				int pos;
+
+				children = gtk_container_get_children(GTK_CONTAINER(shell));
+				pos = g_list_length(children);
+				g_list_free(children);
+
+				if ((strcmp(base->data.text.value, "sep") == 0) || (strcmp(base->data.text.value, "-") == 0)) {
+					GtkWidget *item = gtk_separator_menu_item_new();
+					gtk_menu_shell_append(shell, item);
+					base->user_data = handle_alloc(item, item);
+				}
+				else if (strcmp(base->data.text.value, "@layerview") == 0) {
+					menu->layer_view_shell = shell;
+					menu->layer_view_pos = pos;
+				}
+				else if (strcmp(base->data.text.value, "@layerpick") == 0) {
+					menu->layer_pick_shell = shell;
+					menu->layer_pick_pos = pos;
+				}
+				else if (strcmp(base->data.text.value, "@routestyles") == 0) {
+					menu->route_style_shell = shell;
+					menu->route_style_pos = pos;
+				}
+				else
+					hid_cfg_error(base, "Unexpected text node; the only text accepted here is sep, -, @layerview, @layerpick and @routestyles");
+			}
+			break;
+		default:
+			hid_cfg_error(base, "Unexpected node type; should be hash (submenu) or text (separator or @special)");
+	}
+}
+
+/* CONSTRUCTOR */
+static void ghid_main_menu_init(GHidMainMenu * mm)
+{
+	/* Hookup signal handlers */
+}
+
+static void ghid_main_menu_class_init(GHidMainMenuClass * klass)
+{
+}
+
+/* PUBLIC FUNCTIONS */
+GType ghid_main_menu_get_type(void)
+{
+	static GType mm_type = 0;
+
+	if (!mm_type) {
+		const GTypeInfo mm_info = {
+			sizeof(GHidMainMenuClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_main_menu_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GHidMainMenu),
+			0,												/* n_preallocs */
+			(GInstanceInitFunc) ghid_main_menu_init,
+		};
+
+		mm_type = g_type_register_static(GTK_TYPE_MENU_BAR, "GHidMainMenu", &mm_info, 0);
+	}
+
+	return mm_type;
+}
+
+/*! \brief Create a new GHidMainMenu
+ *
+ *  \return a freshly-allocated GHidMainMenu
+ */
+GtkWidget *ghid_main_menu_new(GCallback action_cb)
+{
+	GHidMainMenu *mm = g_object_new(GHID_MAIN_MENU_TYPE, NULL);
+
+	mm->accel_group = gtk_accel_group_new();
+	mm->action_group = gtk_action_group_new("MainMenu");
+
+	mm->layer_view_pos = 0;
+	mm->layer_pick_pos = 0;
+	mm->route_style_pos = 0;
+	mm->n_layer_views = 0;
+	mm->n_layer_picks = 0;
+	mm->n_route_styles = 0;
+	mm->layer_view_shell = NULL;
+	mm->layer_pick_shell = NULL;
+	mm->route_style_shell = NULL;
+
+	mm->action_cb = action_cb;
+	mm->actions = NULL;
+
+	return GTK_WIDGET(mm);
+}
+
+/*! \brief Turn a lht node into the main menu */
+void ghid_main_menu_add_node(GHidMainMenu * menu, const lht_node_t *base)
+{
+	lht_node_t *n;
+	if (base->type != LHT_LIST) {
+		hid_cfg_error(base, "Menu description shall be a list (li)\n");
+		abort();
+	}
+	for(n = base->data.list.first; n != NULL; n = n->next) {
+		ghid_main_menu_real_add_node(menu, GTK_MENU_SHELL(menu), n);
+	}
+}
+
+/*! \brief Turn a lihata node into a popup menu */
+void ghid_main_menu_add_popup_node(GHidMainMenu * menu, lht_node_t *base)
+{
+	lht_node_t *submenu, *i;
+	GtkWidget *new_menu;
+
+	submenu = hid_cfg_menu_field_path(base, "submenu");
+	if (submenu == NULL) {
+		hid_cfg_error(base, "can not create popup without submenu list");
+		return;
+	}
+
+	new_menu = gtk_menu_new();
+	g_object_ref_sink(new_menu);
+	base->user_data = handle_alloc(new_menu, new_menu);
+
+	for(i = submenu->data.list.first; i != NULL; i = i->next)
+		ghid_main_menu_real_add_node(menu, GTK_MENU_SHELL(new_menu), i);
+
+	gtk_widget_show_all(new_menu);
+}
+
+/*! \brief Updates the toggle/active state of all items
+ *  \par Function Description
+ *  Loops through all actions, passing the action, its toggle
+ *  flag (maybe NULL), and its active flag (maybe NULL), to a
+ *  callback function. It is the responsibility of the function
+ *  to actually change the state of the action.
+ *
+ *  \param [in] menu    The menu to be acted on.
+ *  \param [in] cb      The callback that toggles the actions
+ */
+void
+ghid_main_menu_update_toggle_state(GHidMainMenu * menu,
+																	 void (*cb) (GtkAction *, const char *toggle_flag, const char *active_flag))
+{
+	GList *list;
+	for (list = menu->actions; list; list = list->next) {
+		lht_node_t *res = g_object_get_data(G_OBJECT(list->data), "resource");
+		lht_node_t *act = hid_cfg_menu_field(res, MF_ACTION, NULL);
+		const char *tf = g_object_get_data(G_OBJECT(list->data),
+																			 "checked-flag");
+		const char *af = g_object_get_data(G_OBJECT(list->data),
+																			 "active-flag");
+		g_signal_handlers_block_by_func(G_OBJECT(list->data), menu->action_cb, act);
+		cb(GTK_ACTION(list->data), tf, af);
+		g_signal_handlers_unblock_by_func(G_OBJECT(list->data), menu->action_cb, act);
+	}
+}
+
+/*! \brief Installs or updates layer selector items */
+void ghid_main_menu_install_layer_selector(GHidMainMenu * mm, GHidLayerSelector * ls)
+{
+	GList *children, *iter;
+
+	/* @layerview */
+	if (mm->layer_view_shell) {
+		/* Remove old children */
+		children = gtk_container_get_children(GTK_CONTAINER(mm->layer_view_shell));
+		for (iter = g_list_nth(children, mm->layer_view_pos);
+				 iter != NULL && mm->n_layer_views > 0; iter = g_list_next(iter), mm->n_layer_views--)
+			gtk_container_remove(GTK_CONTAINER(mm->layer_view_shell), iter->data);
+		g_list_free(children);
+
+		/* Install new ones */
+		mm->n_layer_views = ghid_layer_selector_install_view_items(ls, mm->layer_view_shell, mm->layer_view_pos);
+	}
+
+	/* @layerpick */
+	if (mm->layer_pick_shell) {
+		/* Remove old children */
+		children = gtk_container_get_children(GTK_CONTAINER(mm->layer_pick_shell));
+		for (iter = g_list_nth(children, mm->layer_pick_pos);
+				 iter != NULL && mm->n_layer_picks > 0; iter = g_list_next(iter), mm->n_layer_picks--)
+			gtk_container_remove(GTK_CONTAINER(mm->layer_pick_shell), iter->data);
+		g_list_free(children);
+
+		/* Install new ones */
+		mm->n_layer_picks = ghid_layer_selector_install_pick_items(ls, mm->layer_pick_shell, mm->layer_pick_pos);
+	}
+}
+
+/*! \brief Installs or updates route style selector items */
+void ghid_main_menu_install_route_style_selector(GHidMainMenu * mm, GHidRouteStyleSelector * rss)
+{
+	GList *children, *iter;
+	/* @routestyles */
+	if (mm->route_style_shell) {
+		/* Remove old children */
+		children = gtk_container_get_children(GTK_CONTAINER(mm->route_style_shell));
+		for (iter = g_list_nth(children, mm->route_style_pos);
+				 iter != NULL && mm->n_route_styles > 0; iter = g_list_next(iter), mm->n_route_styles--)
+			gtk_container_remove(GTK_CONTAINER(mm->route_style_shell), iter->data);
+		g_list_free(children);
+		/* Install new ones */
+		mm->n_route_styles = ghid_route_style_selector_install_items(rss, mm->route_style_shell, mm->route_style_pos);
+	}
+}
+
+/*! \brief Returns the menu bar's accelerator group */
+GtkAccelGroup *ghid_main_menu_get_accel_group(GHidMainMenu * menu)
+{
+	if (menu == NULL) {
+		Message(PCB_MSG_DEFAULT, "ghid: can't initialize the menu - is your menu .lht valid?\n");
+		exit(1);
+	}
+	return menu->accel_group;
+}
+
+/* Create a new popup window */
+static GtkWidget *new_popup(lht_node_t *menu_item)
+{
+	GtkWidget *new_menu = gtk_menu_new();
+/*	GHidMainMenu *menu  = GHID_MAIN_MENU(ghidgui->menu_bar);*/
+
+	g_object_ref_sink(new_menu);
+	menu_item->user_data = handle_alloc(new_menu, new_menu);
+
+	return new_menu;
+}
+
+/* Menu widget create callback: create a main menu, popup or submenu as descending the path */
+static int ghid_create_menu_widget(void *ctx, const char *path, const char *name, int is_main, lht_node_t *parent, lht_node_t *menu_item)
+{
+	int is_popup = (strncmp(path, "/popups", 7) == 0);
+	menu_handle_t *ph = parent->user_data;
+	GtkWidget *w = (is_main) ? (is_popup ? new_popup(menu_item) : ghidgui->menu_bar) : ph->widget;
+
+	ghid_main_menu_real_add_node(GHID_MAIN_MENU(ghidgui->menu_bar), GTK_MENU_SHELL(w), menu_item);
+
+/* make sure new menu items appear on screen */
+	gtk_widget_show_all(w);
+	return 0;
+}
+
+static int ghid_remove_menu_widget(void *ctx, lht_node_t *nd)
+{
+	menu_handle_t *h = nd->user_data;
+	if (h != NULL) {
+/*		printf("GUI remove '%s' %p %p\n", nd->name, h->widget, h->destroy);*/
+		gtk_widget_destroy(h->destroy);
+		free(h);
+		nd->user_data = NULL;
+	}
+#if 0
+	else {
+		/* @layer pick and friends */
+		printf("GUI remove NULL '%s'\n", nd->name);
+	}
+#endif
+	return 0;
+}
+
+/* Create a new menu by path */
+void ghid_create_menu(const char *menu_path, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie)
+{
+	hid_cfg_create_menu(ghid_cfg, menu_path, action, mnemonic, accel, tip, cookie, ghid_create_menu_widget, NULL);
+}
+
+int ghid_remove_menu(const char *menu_path)
+{
+	return hid_cfg_remove_menu(ghid_cfg, menu_path, ghid_remove_menu_widget, NULL);
+}
+
diff --git a/src_plugins/hid_gtk/ghid-main-menu.h b/src_plugins/hid_gtk/ghid-main-menu.h
new file mode 100644
index 0000000..24d75e9
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-main-menu.h
@@ -0,0 +1,40 @@
+#ifndef GHID_MAIN_MENU_H__
+#define GHID_MAIN_MENU_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "ghid-layer-selector.h"
+#include "ghid-route-style-selector.h"
+#include "hid_cfg.h"
+#include "hid_cfg_input.h"
+
+G_BEGIN_DECLS										/* keep c++ happy */
+#define GHID_MAIN_MENU_TYPE            (ghid_main_menu_get_type ())
+#define GHID_MAIN_MENU(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_MAIN_MENU_TYPE, GHidMainMenu))
+#define GHID_MAIN_MENU_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GHID_MAIN_MENU_TYPE, GHidMainMenuClass))
+#define IS_GHID_MAIN_MENU(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_MAIN_MENU_TYPE))
+#define IS_GHID_MAIN_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHID_MAIN_MENU_TYPE))
+typedef struct _GHidMainMenu GHidMainMenu;
+typedef struct _GHidMainMenuClass GHidMainMenuClass;
+
+GType ghid_main_menu_get_type(void);
+GtkWidget *ghid_main_menu_new(GCallback action_cb);
+void ghid_main_menu_add_node(GHidMainMenu * menu, const lht_node_t *base);
+GtkAccelGroup *ghid_main_menu_get_accel_group(GHidMainMenu * menu);
+void ghid_main_menu_update_toggle_state(GHidMainMenu * menu,
+																				void (*cb) (GtkAction *, const char *toggle_flag, const char *active_flag));
+
+void ghid_main_menu_add_popup_node(GHidMainMenu * menu, lht_node_t *base);
+
+void ghid_main_menu_install_layer_selector(GHidMainMenu * mm, GHidLayerSelector * ls);
+void ghid_main_menu_install_route_style_selector(GHidMainMenu * mm, GHidRouteStyleSelector * rss);
+
+void ghid_create_menu(const char *menu_path, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie);
+int ghid_remove_menu(const char *menu_path);
+
+extern hid_cfg_t *ghid_cfg;
+
+G_END_DECLS											/* keep c++ happy */
+#endif
diff --git a/src_plugins/hid_gtk/ghid-propedit.c b/src_plugins/hid_gtk/ghid-propedit.c
new file mode 100644
index 0000000..85b5ebc
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-propedit.c
@@ -0,0 +1,559 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* object property edit dialog */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "gui.h"
+#include "create.h"
+#include "compat_misc.h"
+#include "polygon.h"
+
+static char *str_sub(const char *val, char sepi, char sepo)
+{
+	char *tmp, *sep;
+	tmp = pcb_strdup(val);
+	sep = strchr(tmp, sepi);
+	if (sep != NULL)
+		*sep = sepo;
+	return tmp;
+}
+
+static void val_combo_changed_cb(GtkComboBox * combo, ghid_propedit_dialog_t *dlg)
+{
+	char *cval, *tmp;
+	GtkTreeIter iter;
+	if (gtk_combo_box_get_active_iter(combo, &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(dlg->vals), &iter, 0, &cval, -1);
+		dlg->stock_val = 1;
+		tmp = str_sub(cval, '=', '\0');
+		gtk_entry_set_text(GTK_ENTRY(dlg->entry_val), tmp);
+		free(tmp);
+	}
+}
+
+static void val_entry_changed_cb(GtkWidget *entry, ghid_propedit_dialog_t *dlg)
+{
+	if (dlg->stock_val == 0)
+		gtk_combo_box_set_active(GTK_COMBO_BOX(dlg->val_box), -1);
+	else
+		dlg->stock_val = 0;
+}
+
+static void val_combo_reset(ghid_propedit_dialog_t *dlg)
+{
+	gtk_list_store_clear(dlg->vals);
+}
+
+static void val_combo_add(ghid_propedit_dialog_t *dlg, const char *val)
+{
+	gtk_list_store_insert_with_values(dlg->vals, NULL, -1, 0, val, -1);
+}
+
+#define NO0(s) pcb_strdup((s) == NULL ? "" : (s))
+void ghid_propedit_prop_add(ghid_propedit_dialog_t *dlg, const char *name, const char *common, const char *min, const char *max, const char *avg)
+{
+	gtk_list_store_insert_with_values(dlg->props, &dlg->last_add_iter, -1,  0,pcb_strdup(name),  1,NO0(common),  2,NO0(min),  3,NO0(max),  4,NO0(avg),  -1);
+	dlg->last_add_iter_valid = 1;
+}
+
+static void hdr_add(ghid_propedit_dialog_t *dlg, const char *name, int col)
+{
+	GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dlg->tree), -1, name, renderer, "text", col, NULL);
+}
+
+
+static void list_cursor_changed_cb(GtkWidget *tree, ghid_propedit_dialog_t *dlg)
+{
+	GtkTreeSelection *tsel;
+	GtkTreeModel *tm;
+	GtkTreeIter iter;
+	const char *prop, *comm, *val;
+	char *tmp;
+
+	tsel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dlg->tree));
+	if (tsel == NULL)
+		return;
+
+	gtk_tree_selection_get_selected(tsel, &tm, &iter);
+	if (iter.stamp == 0)
+		return;
+
+	gtk_tree_model_get(tm, &iter, 0, &prop, 1, &comm, -1);
+
+	printf("prop: %s!\n", prop);
+
+	val_combo_reset(dlg);
+
+	val = ghidgui->propedit_query(ghidgui->propedit_pe, "v1st", prop, NULL, 0);
+	while(val != NULL) {
+		tmp = str_sub(val, '\n', '=');
+		val_combo_add(dlg, tmp);
+		free(tmp);
+		val = ghidgui->propedit_query(ghidgui->propedit_pe, "vnxt", prop, NULL, 0);
+	}
+
+	tmp = str_sub(comm, '\n', '\0');
+	gtk_entry_set_text(GTK_ENTRY(dlg->entry_val), tmp);
+	free(tmp);
+}
+
+static void do_remove_cb(GtkWidget *tree, ghid_propedit_dialog_t *dlg)
+{
+	GtkTreeSelection *tsel;
+	GtkTreeModel *tm;
+	GtkTreeIter iter;
+	char *prop;
+
+	tsel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dlg->tree));
+	if (tsel == NULL)
+		return;
+
+	gtk_tree_selection_get_selected(tsel, &tm, &iter);
+	if (iter.stamp == 0)
+		return;
+
+	gtk_tree_model_get(tm, &iter, 0, &prop, -1);
+
+	if (ghidgui->propedit_query(ghidgui->propedit_pe, "vdel", prop, NULL, 0) != NULL)
+		gtk_list_store_remove(GTK_LIST_STORE(tm), &iter);
+
+	free(prop);
+}
+
+static int keyval_input(char **key, char **val)
+{
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	GtkWidget *vbox, *label, *kentry, *ventry;
+	gboolean response;
+	GHidPort *out = &ghid_port;
+
+	dialog = gtk_dialog_new_with_buttons("New attribute",
+																			 GTK_WINDOW(out->top_window),
+																			 GTK_DIALOG_MODAL,
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+	vbox = gtk_vbox_new(FALSE, 4);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+	label = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), "Attribute key:");
+
+	kentry = gtk_entry_new();
+	gtk_entry_set_activates_default(GTK_ENTRY(kentry), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), kentry, TRUE, TRUE, 0);
+
+	label = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), "Attribute value:");
+
+	ventry = gtk_entry_new();
+	gtk_entry_set_activates_default(GTK_ENTRY(ventry), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), ventry, TRUE, TRUE, 0);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	gtk_container_add(GTK_CONTAINER(content_area), vbox);
+	gtk_widget_show_all(dialog);
+
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+	if (response == GTK_RESPONSE_OK) {
+		*key = gtk_editable_get_chars(GTK_EDITABLE(kentry), 0, -1);
+		*val = gtk_editable_get_chars(GTK_EDITABLE(ventry), 0, -1);
+		gtk_widget_destroy(dialog);
+		return 1;
+	}
+
+	gtk_widget_destroy(dialog);
+	return 0;
+}
+
+
+static void do_addattr_cb(GtkWidget *tree, ghid_propedit_dialog_t *dlg)
+{
+	char *name, *value;
+
+	if (keyval_input(&name, &value)) {
+		char *path;
+		if ((name[0] == 'a') && (name[1] == '/')) {
+			path = name;
+			name = NULL;
+		}
+		else {
+			int len = strlen(name);
+			path = malloc(len+3);
+			path[0] = 'a';
+			path[1] = '/';
+			strcpy(path+2, name);
+		}
+		ghidgui->propedit_query(ghidgui->propedit_pe, "vset", path, value, 0);
+		free(path);
+		free(name);
+	}
+}
+
+static void do_apply_cb(GtkWidget *tree, ghid_propedit_dialog_t *dlg)
+{
+	GtkTreeSelection *tsel;
+	GtkTreeModel *tm;
+	GtkTreeIter iter;
+	char *prop, *val;
+	const char *typ;
+
+	tsel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dlg->tree));
+	if (tsel == NULL)
+		return;
+
+	gtk_tree_selection_get_selected(tsel, &tm, &iter);
+	if (iter.stamp == 0)
+		return;
+
+	gtk_tree_model_get(tm, &iter, 0, &prop, -1);
+
+	val = pcb_strdup(gtk_entry_get_text(GTK_ENTRY(dlg->entry_val)));
+
+	typ = ghidgui->propedit_query(ghidgui->propedit_pe, "type", prop, NULL, 0);
+	if (typ != NULL) {
+		if (*typ == 'c') { /* if type of the field if coords, we may need to fix missing units */
+			char *end;
+			strtod(val, &end);
+			while(isspace(*end)) end++;
+			if (*end == '\0') { /* no unit - automatically append current default */
+				int len = strlen(val);
+				char *new_val;
+				new_val = malloc(len+32);
+				strcpy(new_val, val);
+				sprintf(new_val+len, " %s", conf_core.editor.grid_unit->suffix);
+				free(val);
+				val = new_val;
+			}
+		}
+
+		if (ghidgui->propedit_query(ghidgui->propedit_pe, "vset", prop, val, 0) != NULL) {
+			/* could change values update the table - the new row is already added, remove the old */
+			gtk_list_store_remove(GTK_LIST_STORE(tm), &iter);
+			if (dlg->last_add_iter_valid) {
+				gtk_tree_selection_select_iter(tsel, &dlg->last_add_iter);
+				dlg->last_add_iter_valid = 0;
+			}
+			/* get the combo box updated */
+			list_cursor_changed_cb(dlg->tree, dlg);
+			if ((*val == '+') || (*val == '-'))
+				gtk_entry_set_text(GTK_ENTRY(dlg->entry_val), val); /* keep relative values intact for a reapply */
+		}
+		else
+			Message(PCB_MSG_WARNING, "Failed to change any object - %s is possibly invalid value for %s\n", val, prop);
+	}
+	else
+		Message(PCB_MSG_ERROR, "Internal error: no type for proeprty %s\n", prop);
+
+	free(val);
+	g_free(prop);
+}
+
+static GdkPixmap *pm;
+static gboolean preview_expose_event(GtkWidget *w, GdkEventExpose *event)
+{
+	gdk_draw_drawable(w->window, w->style->fg_gc[gtk_widget_get_state(w)],
+		pm,
+		event->area.x, event->area.y,
+		event->area.x, event->area.y,
+		event->area.width, event->area.height);
+	return FALSE;
+}
+
+static PCBType preview_pcb;
+
+static GtkWidget *preview_init(ghid_propedit_dialog_t *dlg)
+{
+	GtkWidget *area = gtk_drawing_area_new();
+	PCBType *old_pcb;
+	int n, zoom1, fx, fy;
+	Coord cx, cy;
+
+/*
+	void *v;
+	v = CreateNewPolygonFromRectangle(PCB->Data->Layer+1,
+		PCB_MIL_TO_COORD(0), PCB_MIL_TO_COORD(0),
+		PCB_MIL_TO_COORD(1500), PCB_MIL_TO_COORD(1500),
+		MakeFlags(PCB_FLAG_CLEARPOLY | PCB_FLAG_FULLPOLY));
+	printf("poly2=%p -----------\n", (void *)v);
+	DrawPolygon(PCB->Data->Layer+1, v);
+	Draw();
+	gtk_drawing_area_size(GTK_DRAWING_AREA(area), 300, 400);
+	return;
+*/
+
+	memset(&preview_pcb, 0, sizeof(preview_pcb));
+	preview_pcb.Data = CreateNewBuffer();
+	preview_pcb.MaxWidth = preview_pcb.MaxHeight = PCB_MIL_TO_COORD(2000);
+	pcb_colors_from_settings(&preview_pcb);
+	CreateDefaultFont(&preview_pcb);
+	preview_pcb.ViaOn = 1;
+
+	for(n = 0; n < max_copper_layer+2; n++) {
+		preview_pcb.Data->Layer[n].On = 1;
+		preview_pcb.Data->Layer[n].Color = pcb_strdup(PCB->Data->Layer[n].Color);
+		preview_pcb.Data->Layer[n].Name = pcb_strdup("preview dummy");
+	}
+
+	memcpy(&preview_pcb.LayerGroups, &PCB->LayerGroups, sizeof(PCB->LayerGroups));
+	preview_pcb.Data->LayerN = max_copper_layer;
+	preview_pcb.Data->pcb = &preview_pcb;
+
+#warning TODO: preview_pcb is never freed
+
+	CreateNewVia(preview_pcb.Data,
+							PCB_MIL_TO_COORD(1000), PCB_MIL_TO_COORD(1000),
+							PCB_MIL_TO_COORD(50), PCB_MIL_TO_COORD(10), 0, PCB_MIL_TO_COORD(20), "", NoFlags());
+
+	CreateNewLineOnLayer(preview_pcb.Data->Layer+0,
+		PCB_MIL_TO_COORD(1000), PCB_MIL_TO_COORD(1000),
+		PCB_MIL_TO_COORD(1000), PCB_MIL_TO_COORD(1300),
+		PCB_MIL_TO_COORD(20), PCB_MIL_TO_COORD(20), MakeFlags(PCB_FLAG_CLEARLINE));
+
+	CreateNewArcOnLayer(preview_pcb.Data->Layer+0,
+		PCB_MIL_TO_COORD(1000), PCB_MIL_TO_COORD(1000),
+		PCB_MIL_TO_COORD(100), PCB_MIL_TO_COORD(100),
+		0.0, 90.0,
+		PCB_MIL_TO_COORD(20), PCB_MIL_TO_COORD(20), MakeFlags(PCB_FLAG_CLEARLINE));
+
+		CreateNewText(preview_pcb.Data->Layer+0, &PCB->Font,
+							PCB_MIL_TO_COORD(850), PCB_MIL_TO_COORD(1150), 0, 100, "Text", MakeFlags(PCB_FLAG_CLEARLINE));
+
+	{
+		PolygonType *v = CreateNewPolygonFromRectangle(preview_pcb.Data->Layer,
+			PCB_MIL_TO_COORD(10), PCB_MIL_TO_COORD(10),
+			PCB_MIL_TO_COORD(1200), PCB_MIL_TO_COORD(1200),
+			MakeFlags(PCB_FLAG_CLEARPOLY));
+		InitClip(preview_pcb.Data, preview_pcb.Data->Layer, v);
+	}
+
+	old_pcb = PCB;
+	PCB = &preview_pcb;
+
+	zoom1 = 1;
+	cx = PCB_MIL_TO_COORD(1000+300/2*zoom1);
+	cy = PCB_MIL_TO_COORD(1000+400/2*zoom1);
+	fx = conf_core.editor.view.flip_x;
+	fy = conf_core.editor.view.flip_y;
+	conf_set(CFR_DESIGN, "editor/view/flip_x", -1, "0", POL_OVERWRITE);
+	conf_set(CFR_DESIGN, "editor/view/flip_y", -1, "0", POL_OVERWRITE);
+	pm = ghid_render_pixmap(cx, cy,
+	40000 * zoom1, 300, 400, gdk_drawable_get_depth(GDK_DRAWABLE(gport->top_window->window)));
+	conf_setf(CFR_DESIGN, "editor/view/flip_x", -1, "%d", fx, POL_OVERWRITE);
+	conf_setf(CFR_DESIGN, "editor/view/flip_y", -1, "%d", fy, POL_OVERWRITE);
+
+/*
+	{
+		GdkGC *gc = gdk_gc_new(GDK_DRAWABLE(gport->top_window->window));
+		GdkColor clr = {0, 0, 0, 0};
+		int x, y;
+		double zm = 40000 * zoom1;
+
+		gdk_gc_set_rgb_fg_color(gc, &clr);
+
+		x = (PCB_MIL_TO_COORD(1000) - cx) / zm + 0.5 + 200;
+		y = (PCB_MIL_TO_COORD(1000) - cy) / zm + 0.5 + 150;
+		gdk_draw_line(pm, gc, x, y, 0, 0);
+		gdk_draw_line(pm, gc, x+1, y+1, 1, 1);
+		gdk_draw_line(pm, gc, x-1, y-1, -1, -1);
+	}
+*/
+
+
+	PCB = old_pcb;
+
+
+	g_signal_connect(G_OBJECT(area), "expose-event", G_CALLBACK(preview_expose_event), pm);
+	return area;
+}
+
+/*static void sort_by_name(GtkTreeModel *liststore)
+{
+	GtkTreeSortable *sortable = GTK_TREE_SORTABLE(liststore);
+	gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING);
+}*/
+
+static gint sort_name_cmp(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
+{
+	gint sortcol = GPOINTER_TO_INT(userdata);
+	gint ret = 0;
+  gchar *name1, *name2;
+
+	if (sortcol != 0)
+		return 0;
+
+	gtk_tree_model_get(model, a, 0, &name1, -1);
+	gtk_tree_model_get(model, b, 0, &name2, -1);
+
+	if ((name1 == NULL) && (name2 == NULL))
+		return 0;
+
+	if (name1 == NULL)
+		ret = -1;
+	else if (name2 == NULL)
+		ret = 1;
+	else if ((*name1 == 'a') && (*name2 == 'p')) /* force attributes to the bottom */
+		return 1;
+	else if ((*name1 == 'p') && (*name2 == 'a'))
+		return -1;
+	else
+		ret = strcmp(name1, name2);
+
+
+	g_free(name1);
+	g_free(name2);
+
+	return ret;
+}
+
+static void make_sortable(GtkTreeModel *liststore)
+{
+	GtkTreeSortable *sortable;
+
+	sortable = GTK_TREE_SORTABLE(liststore);
+	gtk_tree_sortable_set_sort_func(sortable, 0, sort_name_cmp, GINT_TO_POINTER(0), NULL);
+
+	gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING);
+}
+
+GtkWidget *ghid_propedit_dialog_create(ghid_propedit_dialog_t *dlg)
+{
+	GtkWidget *window, *vbox_tree, *vbox_edit, *hbox_win, *label, *hbx, *dummy, *box_val_edit, *preview;
+	GtkCellRenderer *renderer ;
+	GtkWidget *content_area, *top_window = gport->top_window;
+
+	dlg->last_add_iter_valid = 0;
+
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	window = gtk_dialog_new_with_buttons(_("Edit Properties"),
+																			 GTK_WINDOW(top_window),
+																			 GTK_DIALOG_DESTROY_WITH_PARENT,
+																			 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(window));
+
+	hbox_win = gtk_hbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(content_area), hbox_win);
+
+
+	vbox_tree = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox_win), vbox_tree, TRUE, TRUE, 4);
+	vbox_edit = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox_win), vbox_edit, TRUE, TRUE, 4);
+
+/***** LEFT *****/
+
+	label = gtk_label_new("Properties");
+	gtk_box_pack_start(GTK_BOX(vbox_tree), label, FALSE, FALSE, 4);
+
+	dlg->tree = gtk_tree_view_new();
+	gtk_box_pack_start(GTK_BOX(vbox_tree), dlg->tree, FALSE, TRUE, 4);
+
+	GType ty[5];
+
+	int n;
+	for(n = 0; n < 5; n++)
+		ty[n] = G_TYPE_STRING;
+	dlg->props = gtk_list_store_newv(5, ty);
+	make_sortable((GtkTreeModel *)dlg->props);
+	gtk_tree_view_set_model(GTK_TREE_VIEW(dlg->tree), GTK_TREE_MODEL(dlg->props));
+
+	hdr_add(dlg, "property", 0);
+	hdr_add(dlg, "common", 1);
+	hdr_add(dlg, "min", 2);
+	hdr_add(dlg, "max", 3);
+	hdr_add(dlg, "avg", 4);
+
+	/* dummy box to eat up vertical space */
+	hbx = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox_tree), hbx, TRUE, TRUE, 4);
+
+	/* list manipulation */
+	hbx = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox_tree), hbx, FALSE, TRUE, 4);
+
+	dlg->remove = gtk_button_new_with_label("Remove attribute");
+	gtk_box_pack_start(GTK_BOX(hbx), dlg->remove, FALSE, TRUE, 4);
+	g_signal_connect(G_OBJECT(dlg->remove), "clicked", G_CALLBACK(do_remove_cb), dlg);
+
+	dlg->addattr = gtk_button_new_with_label("Add attribute");
+	gtk_box_pack_start(GTK_BOX(hbx), dlg->addattr, FALSE, TRUE, 4);
+	g_signal_connect(G_OBJECT(dlg->addattr), "clicked", G_CALLBACK(do_addattr_cb), dlg);
+
+
+/***** RIGHT *****/
+/* preview */
+	preview = preview_init(dlg);
+	gtk_box_pack_start(GTK_BOX(vbox_edit), preview, TRUE, TRUE, 4);
+
+	label = gtk_label_new("Change property of all objects");
+	gtk_box_pack_start(GTK_BOX(vbox_edit), label, FALSE, TRUE, 4);
+
+	g_signal_connect(G_OBJECT(dlg->tree), "cursor-changed", G_CALLBACK(list_cursor_changed_cb), dlg);
+
+	/* value edit */
+	renderer = gtk_cell_renderer_text_new();
+
+	dlg->stock_val = 0;
+	dlg->vals = gtk_list_store_new(1, G_TYPE_STRING);
+	box_val_edit = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox_edit), box_val_edit, FALSE, TRUE, 4);
+
+	/* combo */
+	dlg->val_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dlg->vals));
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dlg->val_box), renderer, TRUE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dlg->val_box), renderer, "text", 0, NULL);
+	gtk_box_pack_start(GTK_BOX(box_val_edit), dlg->val_box, FALSE, TRUE, 0);
+
+	g_signal_connect(G_OBJECT(dlg->val_box), "changed", G_CALLBACK(val_combo_changed_cb), dlg);
+
+	/* entry */
+	dlg->entry_val = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(box_val_edit), dlg->entry_val, TRUE, TRUE, 0);
+
+	g_signal_connect(G_OBJECT(dlg->entry_val), "changed", G_CALLBACK(val_entry_changed_cb), dlg);
+
+	/* Apply button */
+	hbx = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox_edit), hbx, FALSE, TRUE, 4);
+
+	dummy = gtk_vbox_new(FALSE, 0); /* dummy box to eat up free space on the left */
+	gtk_box_pack_start(GTK_BOX(hbx), dummy, TRUE, TRUE, 4);
+
+	dlg->apply = gtk_button_new_with_label("Apply");
+	gtk_box_pack_start(GTK_BOX(hbx), dlg->apply, FALSE, TRUE, 4);
+	g_signal_connect(G_OBJECT(dlg->apply), "clicked", G_CALLBACK(do_apply_cb), dlg);
+
+
+	gtk_widget_show_all(window);
+
+	return window;
+}
diff --git a/src_plugins/hid_gtk/ghid-propedit.h b/src_plugins/hid_gtk/ghid-propedit.h
new file mode 100644
index 0000000..1128bbd
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-propedit.h
@@ -0,0 +1,44 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* object property edit dialog */
+
+typedef struct {
+	/* property list */
+	GtkWidget *tree;        /* property list widget */
+	GtkListStore *props;    /* list model of properties */
+
+	/* value entry */
+	GtkWidget *entry_val;   /* text entry */
+	GtkWidget *val_box;     /* combo box */
+	GtkListStore *vals;     /* model of the combo box */
+	int stock_val;          /* 1 if the value in the entry box is being edited from the combo */
+	GtkTreeIter last_add_iter; /* the iterator of the last added row (sometimes it needs to be selected) */
+	int last_add_iter_valid;
+
+	/* buttons */
+	GtkWidget *apply, *remove, *addattr;
+} ghid_propedit_dialog_t;
+
+GtkWidget *ghid_propedit_dialog_create(ghid_propedit_dialog_t *dlg);
+
+void ghid_propedit_prop_add(ghid_propedit_dialog_t *dlg, const char *name, const char *common, const char *min, const char *max, const char *avg);
diff --git a/src_plugins/hid_gtk/ghid-route-style-selector.c b/src_plugins/hid_gtk/ghid-route-style-selector.c
new file mode 100644
index 0000000..d01b7f0
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-route-style-selector.c
@@ -0,0 +1,659 @@
+/*! \file <gtk-pcb-route-style-selector.c>
+ *  \brief Implementation of GHidRouteStyleSelector widget
+ *  \par Description
+ *  Please write description here.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "global.h"
+#include "conf_core.h"
+#include "gtkhid.h"
+#include "gui.h"
+#include "pcb-printf.h"
+#include "route_style.h"
+#include "set.h"
+
+#include "ghid-route-style-selector.h"
+
+/* Forward dec'ls */
+struct _route_style;
+struct _route_style *ghid_route_style_selector_real_add_route_style(GHidRouteStyleSelector *, RouteStyleType *, int);
+static void ghid_route_style_selector_finalize(GObject * object);
+static void add_new_iter(GHidRouteStyleSelector * rss);
+
+/*! \brief Global action creation counter */
+static gint action_count;
+
+/*! \brief Signals exposed by the widget */
+enum {
+	SELECT_STYLE_SIGNAL,
+	STYLE_EDITED_SIGNAL,
+	LAST_SIGNAL
+};
+
+/*! \brief Columns used for internal data store */
+enum {
+	TEXT_COL,
+	DATA_COL,
+	N_COLS
+};
+
+static GtkVBox *ghid_route_style_selector_parent_class;
+static guint ghid_route_style_selector_signals[LAST_SIGNAL] = { 0 };
+
+struct _GHidRouteStyleSelector {
+	GtkVBox parent;
+
+	GSList *button_radio_group;
+	GSList *action_radio_group;
+	GtkWidget *edit_button;
+
+	GtkActionGroup *action_group;
+	GtkAccelGroup *accel_group;
+
+	int hidden_button; /* whether the hidden button is created */
+	int selected; /* index of the currently selected route style */
+
+	GtkListStore *model;
+	struct _route_style *active_style;
+
+	GtkTreeIter new_iter;     /* iter for the <new> item */
+};
+
+struct _GHidRouteStyleSelectorClass {
+	GtkVBoxClass parent_class;
+
+	void (*select_style) (GHidRouteStyleSelector *, RouteStyleType *);
+	void (*style_edited) (GHidRouteStyleSelector *, gboolean);
+};
+
+struct _route_style {
+	GtkRadioAction *action;
+	GtkWidget *button;
+	GtkWidget *menu_item;
+	GtkTreeRowReference *rref;
+	RouteStyleType *rst;
+	gulong sig_id;
+	int hidden;
+};
+
+/* SIGNAL HANDLERS */
+/*! \brief Callback for user selection of a route style */
+static void radio_select_cb(GtkToggleAction * action, GHidRouteStyleSelector * rss)
+{
+	rss->active_style = g_object_get_data(G_OBJECT(action), "route-style");
+	if (gtk_toggle_action_get_active(action))
+		g_signal_emit(rss, ghid_route_style_selector_signals[SELECT_STYLE_SIGNAL], 0, rss->active_style->rst);
+}
+
+/* EDIT DIALOG */
+struct _dialog {
+	GHidRouteStyleSelector *rss;
+	GtkWidget *name_entry;
+	GtkWidget *line_entry;
+	GtkWidget *via_hole_entry;
+	GtkWidget *via_size_entry;
+	GtkWidget *clearance_entry;
+
+	GtkWidget *select_box;
+
+	int inhibit_style_change; /* when 1, do not do anything when style changes */
+};
+
+/*! \brief Callback for dialog box's combobox being changed
+ *  \par Function Description
+ *  When a different layer is selected, this function loads
+ *  that layer's data into the dialog. Alternately, if the
+ *  "New layer" option is selected, this loads a new name
+ *  but no other data.
+ *
+ *  \param [in] combo   The combobox
+ *  \param [in] dialog  The rest of the widgets to be updated
+ */
+static void dialog_style_changed_cb(GtkComboBox * combo, struct _dialog *dialog)
+{
+	struct _route_style *style;
+	GtkTreeIter iter;
+
+	if (dialog->inhibit_style_change)
+		return;
+
+	gtk_combo_box_get_active_iter(combo, &iter);
+	gtk_tree_model_get(GTK_TREE_MODEL(dialog->rss->model), &iter, DATA_COL, &style, -1);
+
+	if (style == NULL) {
+		gtk_entry_set_text(GTK_ENTRY(dialog->name_entry), _("New Style"));
+		dialog->rss->selected = -1;
+		return;
+	}
+
+	gtk_entry_set_text(GTK_ENTRY(dialog->name_entry), style->rst->name);
+	ghid_coord_entry_set_value(GHID_COORD_ENTRY(dialog->line_entry), style->rst->Thick);
+	ghid_coord_entry_set_value(GHID_COORD_ENTRY(dialog->via_hole_entry), style->rst->Hole);
+	ghid_coord_entry_set_value(GHID_COORD_ENTRY(dialog->via_size_entry), style->rst->Diameter);
+	ghid_coord_entry_set_value(GHID_COORD_ENTRY(dialog->clearance_entry), style->rst->Clearance);
+
+	if (style->hidden)
+		dialog->rss->selected = -1;
+	else
+		dialog->rss->selected = style->rst - PCB->RouteStyle.array;
+}
+
+/*  Callback for Delete route style button */
+static void delete_button_cb(GtkButton *button, struct _dialog *dialog)
+{
+	if (dialog->rss->selected < 0)
+		return;
+
+	dialog->inhibit_style_change = 1;
+	ghid_route_style_selector_empty(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+	vtroutestyle_remove(&PCB->RouteStyle, dialog->rss->selected, 1);
+	dialog->rss->active_style = NULL;
+	make_route_style_buttons(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+	pcb_trace("Style: %d deleted\n", dialog->rss->selected);
+	SetChangedFlag(pcb_true);
+	ghid_window_set_name_label(PCB->Name);
+	add_new_iter(dialog->rss);
+	dialog->inhibit_style_change = 0;
+	ghid_route_style_selector_select_style(dialog->rss, &pcb_custom_route_style);
+	gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->select_box), 0);
+}
+
+/* \brief Helper for edit_button_cb */
+static void _table_attach(GtkWidget * table, gint row, const gchar * label, GtkWidget ** entry, Coord min, Coord max)
+{
+	GtkWidget *label_w = gtk_label_new(label);
+	gtk_misc_set_alignment(GTK_MISC(label_w), 1.0, 0.5);
+
+	*entry = ghid_coord_entry_new(min, max, 0, conf_core.editor.grid_unit, CE_SMALL);
+	gtk_table_attach(GTK_TABLE(table), label_w, 0, 1, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+	gtk_table_attach(GTK_TABLE(table), *entry, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+}
+
+static void add_new_iter(GHidRouteStyleSelector * rss)
+{
+	/* Add "new style" option to list */
+	gtk_list_store_append(rss->model, &rss->new_iter);
+	gtk_list_store_set(rss->model, &rss->new_iter, TEXT_COL, _("<New>"), DATA_COL, NULL, -1);
+}
+
+/* \brief Builds and runs the "edit route styles" dialog */
+void ghid_route_style_selector_edit_dialog(GHidRouteStyleSelector * rss)
+{
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	struct _dialog dialog_data;
+	GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+	GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(rss));
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	GtkWidget *vbox, *hbox, *sub_vbox, *table;
+	GtkWidget *label, *select_box, *check_box;
+	GtkWidget *button;
+	const char *new_name;
+
+	memset(&dialog_data, 0, sizeof(dialog_data)); /* make sure all flags are cleared */
+
+	/* Build dialog */
+	dialog = gtk_dialog_new_with_buttons(_("Edit Route Styles"),
+																			 GTK_WINDOW(window),
+																			 GTK_DIALOG_DESTROY_WITH_PARENT,
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_NONE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	label = gtk_label_new(_("Edit Style:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+
+	select_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(rss->model));
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(select_box), renderer, TRUE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(select_box), renderer, "text", TEXT_COL, NULL);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+	hbox = gtk_hbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(content_area), hbox, TRUE, TRUE, 4);
+	vbox = gtk_vbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 4);
+
+	hbox = gtk_hbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), select_box, TRUE, TRUE, 0);
+
+	sub_vbox = ghid_category_vbox(vbox, _("Route Style Data"), 4, 2, TRUE, TRUE);
+	table = gtk_table_new(5, 2, FALSE);
+	gtk_box_pack_start(GTK_BOX(sub_vbox), table, TRUE, TRUE, 4);
+	label = gtk_label_new(_("Name:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+	dialog_data.name_entry = gtk_entry_new();
+	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+	gtk_table_attach(GTK_TABLE(table), dialog_data.name_entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2);
+
+	_table_attach(table, 1, _("Line width:"), &dialog_data.line_entry, MIN_LINESIZE, MAX_LINESIZE);
+	_table_attach(table, 2, _("Via hole size:"),
+								&dialog_data.via_hole_entry, MIN_PINORVIAHOLE, MAX_PINORVIASIZE - MIN_PINORVIACOPPER);
+	_table_attach(table, 3, _("Via ring size:"),
+								&dialog_data.via_size_entry, MIN_PINORVIAHOLE + MIN_PINORVIACOPPER, MAX_PINORVIASIZE);
+	_table_attach(table, 4, _("Clearance:"), &dialog_data.clearance_entry, MIN_LINESIZE, MAX_LINESIZE);
+
+	/* create delete button */
+	button = gtk_button_new_with_label (_("Delete Style"));
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_button_cb), &dialog_data);
+	gtk_box_pack_start(GTK_BOX(vbox), button , TRUE, TRUE, 0);
+
+	sub_vbox = ghid_category_vbox(vbox, _("Set as Default"), 4, 2, TRUE, TRUE);
+	check_box = gtk_check_button_new_with_label(_("Save route style settings as default"));
+	gtk_box_pack_start(GTK_BOX(sub_vbox), check_box, TRUE, TRUE, 0);
+
+
+	add_new_iter(rss);
+
+	/* Display dialog */
+	dialog_data.rss = rss;
+	dialog_data.select_box = select_box;
+	if (rss->active_style != NULL) {
+		path = gtk_tree_row_reference_get_path(rss->active_style->rref);
+		gtk_tree_model_get_iter(GTK_TREE_MODEL(rss->model), &iter, path);
+		g_signal_connect(G_OBJECT(select_box), "changed", G_CALLBACK(dialog_style_changed_cb), &dialog_data);
+		gtk_combo_box_set_active_iter(GTK_COMBO_BOX(select_box), &iter);
+	}
+	gtk_widget_show_all(dialog);
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+		int changed = 0;
+		RouteStyleType *rst;
+		struct _route_style *style;
+		gboolean save;
+		if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(select_box), &iter))
+			goto cancel;
+		gtk_tree_model_get(GTK_TREE_MODEL(rss->model), &iter, DATA_COL, &style, -1);
+		if (style == NULL) {
+			int n = vtroutestyle_len(&PCB->RouteStyle);
+			rst = vtroutestyle_get(&PCB->RouteStyle, n, 1);
+		}
+		else {
+			rst = style->rst;
+			*rst->name = '\0';
+		}
+
+		new_name = gtk_entry_get_text(GTK_ENTRY(dialog_data.name_entry));
+
+		while(isspace(*new_name)) new_name++;
+		if (strcmp(rst->name, new_name) != 0) {
+			strncpy(rst->name, new_name, sizeof(rst->name)-1);
+			rst->name[sizeof(rst->name)-1] = '0';
+			changed = 1;
+		}
+
+/* Modify the route style only if there's significant difference (beyond rouding errors) */
+#define rst_modify(changed, dst, src) \
+	do { \
+		Coord __tmp__ = src; \
+		if (abs(dst - __tmp__) > 10) { \
+			changed = 1; \
+			dst = __tmp__; \
+		} \
+	} while(0)
+		rst_modify(changed, rst->Thick, ghid_coord_entry_get_value(GHID_COORD_ENTRY(dialog_data.line_entry)));
+		rst_modify(changed, rst->Hole, ghid_coord_entry_get_value(GHID_COORD_ENTRY(dialog_data.via_hole_entry)));
+		rst_modify(changed, rst->Diameter, ghid_coord_entry_get_value(GHID_COORD_ENTRY(dialog_data.via_size_entry)));
+		rst_modify(changed, rst->Clearance, ghid_coord_entry_get_value(GHID_COORD_ENTRY(dialog_data.clearance_entry)));
+#undef rst_modify
+		save = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_box));
+		if (style == NULL)
+			style = ghid_route_style_selector_real_add_route_style(rss, rst, 0);
+		else {
+			gtk_action_set_label(GTK_ACTION(style->action), rst->name);
+			gtk_list_store_set(rss->model, &iter, TEXT_COL, rst->name, -1);
+		}
+
+		/* Cleanup */
+		gtk_widget_destroy(dialog);
+		gtk_list_store_remove(rss->model, &rss->new_iter);
+		/* Emit change signals */
+		ghid_route_style_selector_select_style(rss, rst);
+		g_signal_emit(rss, ghid_route_style_selector_signals[STYLE_EDITED_SIGNAL], 0, save);
+
+		if (changed) {
+			SetChangedFlag(pcb_true);
+			ghid_window_set_name_label(PCB->Name);
+		}
+	}
+	else {
+		cancel:;
+		gtk_widget_destroy(dialog);
+		gtk_list_store_remove(rss->model, &rss->new_iter);
+	}
+}
+
+/* end EDIT DIALOG */
+
+static void edit_button_cb(GtkButton * btn, GHidRouteStyleSelector * rss)
+{
+	ghid_route_style_selector_edit_dialog(rss);
+}
+
+/* CONSTRUCTOR */
+static void ghid_route_style_selector_init(GHidRouteStyleSelector * rss)
+{
+}
+
+static void ghid_route_style_selector_class_init(GHidRouteStyleSelectorClass * klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+
+	ghid_route_style_selector_parent_class = g_type_class_peek_parent(klass);
+
+	ghid_route_style_selector_signals[SELECT_STYLE_SIGNAL] =
+		g_signal_new("select-style",
+								 G_TYPE_FROM_CLASS(klass),
+								 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+								 G_STRUCT_OFFSET(GHidRouteStyleSelectorClass, select_style),
+								 NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+	ghid_route_style_selector_signals[STYLE_EDITED_SIGNAL] =
+		g_signal_new("style-edited",
+								 G_TYPE_FROM_CLASS(klass),
+								 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+								 G_STRUCT_OFFSET(GHidRouteStyleSelectorClass, style_edited),
+								 NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+	object_class->finalize = ghid_route_style_selector_finalize;
+}
+
+/*! \brief Clean up object before garbage collection
+ */
+static void ghid_route_style_selector_finalize(GObject * object)
+{
+	GHidRouteStyleSelector *rss = (GHidRouteStyleSelector *) object;
+
+	ghid_route_style_selector_empty(rss);
+
+	g_object_unref(rss->accel_group);
+	g_object_unref(rss->action_group);
+
+	G_OBJECT_CLASS(ghid_route_style_selector_parent_class)->finalize(object);
+}
+
+/* PUBLIC FUNCTIONS */
+GType ghid_route_style_selector_get_type(void)
+{
+	static GType rss_type = 0;
+
+	if (!rss_type) {
+		const GTypeInfo rss_info = {
+			sizeof(GHidRouteStyleSelectorClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_route_style_selector_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GHidRouteStyleSelector),
+			0,												/* n_preallocs */
+			(GInstanceInitFunc) ghid_route_style_selector_init,
+		};
+
+		rss_type = g_type_register_static(GTK_TYPE_VBOX, "GHidRouteStyleSelector", &rss_info, 0);
+	}
+
+	return rss_type;
+}
+
+/*! \brief Create a new GHidRouteStyleSelector
+ *
+ *  \return a freshly-allocated GHidRouteStyleSelector.
+ */
+GtkWidget *ghid_route_style_selector_new()
+{
+	GHidRouteStyleSelector *rss = g_object_new(GHID_ROUTE_STYLE_SELECTOR_TYPE, NULL);
+
+	rss->active_style = NULL;
+	rss->action_radio_group = NULL;
+	rss->button_radio_group = NULL;
+	rss->model = gtk_list_store_new(N_COLS, G_TYPE_STRING, G_TYPE_POINTER);
+
+	rss->accel_group = gtk_accel_group_new();
+	rss->action_group = gtk_action_group_new("RouteStyleSelector");
+
+	/* Create edit button */
+	rss->edit_button = gtk_button_new_with_label(_("Route Styles"));
+	g_signal_connect(G_OBJECT(rss->edit_button), "clicked", G_CALLBACK(edit_button_cb), rss);
+	gtk_box_pack_start(GTK_BOX(rss), rss->edit_button, FALSE, FALSE, 0);
+
+	return GTK_WIDGET(rss);
+}
+
+/*! \brief Create a new GHidRouteStyleSelector 
+ *
+ *  \param [in] rss     The selector to be acted on
+ *  \param [in] data    PCB's route style object that will be edited
+ */
+struct _route_style *ghid_route_style_selector_real_add_route_style(GHidRouteStyleSelector * rss,
+																																		RouteStyleType * data, int hide)
+{
+	GtkTreeIter iter;
+	GtkTreePath *path;
+	gchar *action_name = g_strdup_printf("RouteStyle%d", action_count);
+	struct _route_style *new_style = g_malloc(sizeof(*new_style));
+
+	/* Key the route style data with the RouteStyleType it controls */
+	new_style->hidden = hide;
+	new_style->rst = data;
+	new_style->action = gtk_radio_action_new(action_name, data->name, NULL, NULL, action_count);
+	gtk_radio_action_set_group(new_style->action, rss->action_radio_group);
+	rss->action_radio_group = gtk_radio_action_get_group(new_style->action);
+	new_style->button = gtk_radio_button_new(rss->button_radio_group);
+	rss->button_radio_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(new_style->button));
+	gtk_activatable_set_related_action(GTK_ACTIVATABLE(new_style->button), GTK_ACTION(new_style->action));
+
+	gtk_list_store_append(rss->model, &iter);
+	gtk_list_store_set(rss->model, &iter, TEXT_COL, data->name, DATA_COL, new_style, -1);
+	path = gtk_tree_model_get_path(GTK_TREE_MODEL(rss->model), &iter);
+	new_style->rref = gtk_tree_row_reference_new(GTK_TREE_MODEL(rss->model), path);
+	gtk_tree_path_free(path);
+
+	/* Setup accelerator */
+	if (action_count < 12) {
+		gchar *accel = g_strdup_printf("<Ctrl>F%d", action_count + 1);
+		gtk_action_set_accel_group(GTK_ACTION(new_style->action), rss->accel_group);
+		gtk_action_group_add_action_with_accel(rss->action_group, GTK_ACTION(new_style->action), accel);
+		g_free(accel);
+	}
+
+	/* Hookup and install radio button */
+	g_object_set_data(G_OBJECT(new_style->action), "route-style", new_style);
+	new_style->sig_id = g_signal_connect(G_OBJECT(new_style->action), "activate", G_CALLBACK(radio_select_cb), rss);
+	gtk_box_pack_start(GTK_BOX(rss), new_style->button, FALSE, FALSE, 0);
+
+	g_free(action_name);
+	++action_count;
+
+	if (hide)
+		gtk_widget_hide(new_style->button);
+
+	return new_style;
+}
+
+/*! \brief Adds a route style to a GHidRouteStyleSelector
+ *  \par Function Description
+ *  Adds a new route style to be managed by this selector. Note
+ *  that the route style object passed to this function will be
+ *  updated directly.
+ *
+ *  \param [in] rss     The selector to be acted on
+ *  \param [in] data    PCB's route style object describing the style
+ */
+void ghid_route_style_selector_add_route_style(GHidRouteStyleSelector * rss, RouteStyleType * data)
+{
+	if (!rss->hidden_button) {
+		memset(&pcb_custom_route_style, 0, sizeof(pcb_custom_route_style));
+		strcpy(pcb_custom_route_style.name, "<custom>");
+		ghid_route_style_selector_real_add_route_style(rss, &pcb_custom_route_style, 1);
+		rss->hidden_button = 1;
+	}
+	if (data != NULL)
+		ghid_route_style_selector_real_add_route_style(rss, data, 0);
+}
+
+
+/*! \brief Install the "Route Style" menu items
+ *  \par Function Description
+ *  Takes a menu shell and installs menu items for route style selection in
+ *  the shell, at the given position. Note that we aren't really guaranteed
+ *  the ordering of these items, since our internal data structure is a hash
+ *  table. This shouldn't be a problem.
+ *
+ *  \param [in] rss     The selector to be acted on
+ *  \param [in] shell   The menu to install the items in
+ *  \param [in] pos     The position in the menu to install items
+ *
+ *  \return the number of items installed
+ */
+gint ghid_route_style_selector_install_items(GHidRouteStyleSelector * rss, GtkMenuShell * shell, gint pos)
+{
+	gint n = 0;
+	GtkTreeIter iter;
+
+	if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rss->model), &iter))
+		return 0;
+	do {
+		GtkAction *action;
+		struct _route_style *style;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(rss->model), &iter, DATA_COL, &style, -1);
+		if (style->hidden)
+			continue;
+		action = GTK_ACTION(style->action);
+		style->menu_item = gtk_action_create_menu_item(action);
+		gtk_menu_shell_insert(shell, style->menu_item, pos + n);
+		++n;
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(rss->model), &iter));
+
+	return n;
+}
+
+/*! \brief Selects a route style and emits a select-style signal
+ *
+ *  \param [in] rss  The selector to be acted on
+ *  \param [in] rst  The style to select
+ *
+ *  \return TRUE if a style was selected, FALSE otherwise
+ */
+gboolean ghid_route_style_selector_select_style(GHidRouteStyleSelector * rss, RouteStyleType * rst)
+{
+	GtkTreeIter iter;
+	gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rss->model), &iter);
+
+	do {
+		struct _route_style *style;
+		gtk_tree_model_get(GTK_TREE_MODEL(rss->model), &iter, DATA_COL, &style, -1);
+		if ((style != NULL) && (style->rst == rst)) {
+			g_signal_handler_block(G_OBJECT(style->action), style->sig_id);
+			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(style->action), TRUE);
+			g_signal_handler_unblock(G_OBJECT(style->action), style->sig_id);
+			rss->active_style = style;
+			g_signal_emit(rss, ghid_route_style_selector_signals[SELECT_STYLE_SIGNAL], 0, rss->active_style->rst);
+			return TRUE;
+		}
+	}
+	while (gtk_tree_model_iter_next(GTK_TREE_MODEL(rss->model), &iter));
+
+	return FALSE;
+}
+
+/*! \brief Returns the GtkAccelGroup of a route style selector
+ *  \par Function Description
+ *
+ *  \param [in] rss            The selector to be acted on
+ *
+ *  \return the accel group of the selector
+ */
+GtkAccelGroup *ghid_route_style_selector_get_accel_group(GHidRouteStyleSelector * rss)
+{
+	return rss->accel_group;
+}
+
+/*! \brief Sets a GHidRouteStyleSelector selection to given values
+ *  \par Function Description
+ *  Given the line thickness, via size and clearance values of a route
+ *  style, this function selects a route style with the given values.
+ *  If there is no such style registered with the selector, nothing
+ *  will happen. This function does not emit any signals.
+ *
+ *  \param [in] rss       The selector to be acted on
+ *  \param [in] Thick     Coord to match selection to
+ *  \param [in] Hole      Coord to match selection to
+ *  \param [in] Diameter  Coord to match selection to
+ *  \param [in] Clearance  Coord to match selection to
+ */
+void ghid_route_style_selector_sync(GHidRouteStyleSelector * rss, Coord Thick, Coord Hole, Coord Diameter, Coord Clearance)
+{
+	GtkTreeIter iter;
+	int target, n;
+
+	/* Always update the label - even if there's no style, the current settings need to show */
+	ghid_set_status_line_label();
+
+	if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rss->model), &iter))
+		return;
+
+	target = pcb_route_style_lookup(&PCB->RouteStyle, Thick, Diameter, Hole, Clearance, NULL);
+	if (target == -1) {
+		struct _route_style *style;
+
+		/* None of the styles matched: select the hidden custom button */
+		if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rss->model), &iter))
+			return;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(rss->model), &iter, DATA_COL, &style, -1);
+		g_signal_handler_block(G_OBJECT(style->action), style->sig_id);
+		gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(style->action), TRUE);
+		g_signal_handler_unblock(G_OBJECT(style->action), style->sig_id);
+		rss->active_style = style;
+
+		return;
+	}
+
+	/* need to select the style with index stored in target */
+	n = -1;
+	do {
+		struct _route_style *style;
+		gtk_tree_model_get(GTK_TREE_MODEL(rss->model), &iter, DATA_COL, &style, -1);
+		if (n == target) {
+			g_signal_handler_block(G_OBJECT(style->action), style->sig_id);
+			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(style->action), TRUE);
+			g_signal_handler_unblock(G_OBJECT(style->action), style->sig_id);
+			rss->active_style = style;
+			break;
+		}
+		n++;
+	} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(rss->model), &iter));
+}
+
+/*! \brief Removes all styles from a route style selector */
+void ghid_route_style_selector_empty(GHidRouteStyleSelector * rss)
+{
+	GtkTreeIter iter;
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rss->model), &iter)) {
+		do {
+			struct _route_style *rsdata;
+			gtk_tree_model_get(GTK_TREE_MODEL(rss->model), &iter, DATA_COL, &rsdata, -1);
+			if (rsdata != NULL) {
+				if (rsdata->action) {
+					gtk_action_disconnect_accelerator(GTK_ACTION(rsdata->action));
+					gtk_action_group_remove_action(rss->action_group, GTK_ACTION(rsdata->action));
+					g_object_unref(G_OBJECT(rsdata->action));
+				}
+				if (rsdata->button)
+					gtk_widget_destroy(GTK_WIDGET(rsdata->button));;
+				gtk_tree_row_reference_free(rsdata->rref);
+				free(rsdata);
+			}
+		} while (gtk_list_store_remove(rss->model, &iter));
+	}
+	rss->action_radio_group = NULL;
+	rss->button_radio_group = NULL;
+	rss->hidden_button = 0;
+}
diff --git a/src_plugins/hid_gtk/ghid-route-style-selector.h b/src_plugins/hid_gtk/ghid-route-style-selector.h
new file mode 100644
index 0000000..33e21bc
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-route-style-selector.h
@@ -0,0 +1,34 @@
+#ifndef GHID_ROUTE_STYLE_SELECTOR_H__
+#define GHID_ROUTE_STYLE_SELECTOR_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "global.h"
+
+G_BEGIN_DECLS										/* keep c++ happy */
+#define GHID_ROUTE_STYLE_SELECTOR_TYPE            (ghid_route_style_selector_get_type ())
+#define GHID_ROUTE_STYLE_SELECTOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_ROUTE_STYLE_SELECTOR_TYPE, GHidRouteStyleSelector))
+#define GHID_ROUTE_STYLE_SELECTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GHID_ROUTE_STYLE_SELECTOR_TYPE, GHidRouteStyleSelectorClass))
+#define IS_GHID_ROUTE_STYLE_SELECTOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_ROUTE_STYLE_SELECTOR_TYPE))
+#define IS_GHID_ROUTE_STYLE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHID_ROUTE_STYLE_SELECTOR_TYPE))
+typedef struct _GHidRouteStyleSelector GHidRouteStyleSelector;
+typedef struct _GHidRouteStyleSelectorClass GHidRouteStyleSelectorClass;
+
+GType ghid_route_style_selector_get_type(void);
+GtkWidget *ghid_route_style_selector_new(void);
+
+gint ghid_route_style_selector_install_items(GHidRouteStyleSelector * rss, GtkMenuShell * shell, gint pos);
+
+void ghid_route_style_selector_add_route_style(GHidRouteStyleSelector * rss, RouteStyleType * data);
+gboolean ghid_route_style_selector_select_style(GHidRouteStyleSelector * rss, RouteStyleType * rst);
+void ghid_route_style_selector_edit_dialog(GHidRouteStyleSelector * rss);
+
+GtkAccelGroup *ghid_route_style_selector_get_accel_group(GHidRouteStyleSelector * rss);
+
+void ghid_route_style_selector_sync(GHidRouteStyleSelector * rss, Coord Thick, Coord Hole, Coord Diameter, Coord Clearance);
+void ghid_route_style_selector_empty(GHidRouteStyleSelector * rss);
+
+G_END_DECLS											/* keep c++ happy */
+#endif
diff --git a/src_plugins/hid_gtk/ghid-search-tab.h b/src_plugins/hid_gtk/ghid-search-tab.h
new file mode 100644
index 0000000..900213f
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-search-tab.h
@@ -0,0 +1,171 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* advanced search dialog - expressioin wizard tables; intended to be
+   included from ghid-search.c only */
+
+static struct {
+	GtkWidget *entry_left;
+	GtkWidget *tr_left, *tr_op;
+	GtkWidget *right_str, *right_coord, *tr_right, *right_int;
+	GtkTreeStore *md_left;
+	GtkAdjustment *right_adj;
+} expr_wizard_dlg;
+
+static GType model_op[2] = { G_TYPE_STRING, G_TYPE_POINTER };
+
+typedef struct expr_wizard_op_s expr_wizard_op_t;
+struct expr_wizard_op_s {
+	const char **ops;
+	GtkListStore *model;
+};
+
+typedef enum {
+	RIGHT_STR,
+	RIGHT_INT,
+	RIGHT_COORD,
+	RIGHT_CONST
+} right_type;
+
+typedef struct expr_wizard_s expr_wizard_t;
+struct expr_wizard_s {
+	const char *left_var;
+	const char *left_desc;
+	const expr_wizard_op_t *ops;
+	right_type rtype;
+	const expr_wizard_op_t *right_const;
+};
+
+enum {
+	OPS_ANY,
+	OPS_EQ,
+	OPS_STR
+};
+
+static const char *ops_any[] = {"==", "!=", ">=", "<=", ">", "<", NULL};
+static const char *ops_eq[]  = {"==", "!=", NULL};
+static const char *ops_str[] = {"==", "!=", "~", NULL};
+
+static expr_wizard_op_t op_tab[] = {
+	{ops_any, NULL},
+	{ops_eq, NULL},
+	{ops_str, NULL},
+	{NULL, NULL}
+};
+
+static const char *right_const_objtype[] = {
+	"POINT", "LINE", "TEXT", "POLYGON", "ARC", "RAT", "PAD", "PIN", "VIA",
+	"ELEMENT", "NET", "LAYER", "ELINE", "EARC", "ETEXT",
+	NULL
+};
+static const char *right_const_yesno[] = {"YES", "NO", NULL};
+static const char *right_const_layerpos[] = {"TOP", "BOTTOM", "INTERNAL", NULL};
+static const char *right_const_layertype[] = {"COPPER", "SILK", "MASK", "PASTE", "OUTLINE" , NULL};
+
+enum {
+	RC_OBJTYPE,
+	RC_YESNO,
+	RC_LAYERPOS,
+	RC_LAYERTYPE
+};
+
+static expr_wizard_op_t right_const_tab[] = {
+	{right_const_objtype, NULL},
+	{right_const_yesno, NULL},
+	{right_const_layerpos, NULL},
+	{right_const_layertype, NULL},
+	{NULL, NULL}
+};
+
+static const expr_wizard_t expr_tab[] = {
+	{"@.id",              "object ID",        &op_tab[OPS_ANY], RIGHT_INT, NULL},
+	{"@.type",            "object type",      &op_tab[OPS_EQ],  RIGHT_CONST, &right_const_tab[RC_OBJTYPE]},
+
+	{NULL,                "bounding box",     NULL,             0, NULL},
+	{"@.bbox.x1",         "X1",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.bbox.y1",         "Y1",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.bbox.x2",         "X2",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.bbox.y2",         "Y2",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.bbox.w",          "width",            &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.bbox.h",          "height",           &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+
+
+	{NULL,                "trace object's",   NULL,             0, NULL},
+	{"@.thickness",       "thickness",        &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.clearance",       "clearance",        &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.length",          "length",           &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+
+	{NULL,                "line",             NULL,             0, NULL},
+	{"@.x1",              "X1",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.y1",              "Y1",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.x2",              "X2",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.y2",              "Y2",               &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+
+	{NULL,                "arc",              NULL,             0, NULL},
+	{"@.x",               "center X",         &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.y",               "center Y",         &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.angle.start",     "start angle",      &op_tab[OPS_ANY], RIGHT_INT, NULL},
+	{"@.angle.delta",     "delta angle",      &op_tab[OPS_ANY], RIGHT_INT, NULL},
+
+	{NULL,                "text",             NULL,             0, NULL},
+	{"@.x",               "X",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.y",               "Y",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.scale",           "scale",            &op_tab[OPS_ANY], RIGHT_INT, NULL},
+	{"@.string",          "string",           &op_tab[OPS_ANY], RIGHT_STR, NULL},
+	{"@.rotation",        "rotation",         &op_tab[OPS_ANY], RIGHT_INT, NULL},
+
+	{NULL,                "polygon",          NULL,             0, NULL},
+	{"@.points",          "points",           &op_tab[OPS_ANY], RIGHT_INT, NULL},
+
+	{NULL,                "pin or via",       NULL,             0, NULL},
+	{"@.x",               "X",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.y",               "Y",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.hole",            "drilling hole dia",&op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.mask",            "mask",             &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.name",            "name",             &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.number",          "number",           &op_tab[OPS_STR], RIGHT_STR, NULL},
+
+	{NULL,                "element",          NULL,             0, NULL},
+	{"@.x",               "X",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.y",               "Y",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.name",            "name",             &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.refdes",          "refdes",           &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.description",     "description",      &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.value",           "value",            &op_tab[OPS_STR], RIGHT_STR, NULL},
+
+	{NULL,                "host layer's",     NULL,             0, NULL},
+	{"@.layer.name",      "name",             &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.layer.visible",   "visible",          &op_tab[OPS_EQ],  RIGHT_CONST, &right_const_tab[RC_YESNO]},
+	{"@.layer.position",  "stack position",   &op_tab[OPS_EQ],  RIGHT_CONST, &right_const_tab[RC_LAYERPOS]},
+	{"@.layer.type",      "type",             &op_tab[OPS_EQ],  RIGHT_CONST, &right_const_tab[RC_LAYERTYPE]},
+
+	{NULL,                "host element's",   NULL,             0, NULL},
+	{"@.element.x",       "X",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.element.y",       "Y",                &op_tab[OPS_ANY], RIGHT_COORD, NULL},
+	{"@.element.refdes",  "refdes",           &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.element.name",    "name",             &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.element.description","description",   &op_tab[OPS_STR], RIGHT_STR, NULL},
+	{"@.element.value",   "value",            &op_tab[OPS_STR], RIGHT_STR, NULL},
+
+	{NULL, NULL, NULL, 0, NULL}
+};
+
diff --git a/src_plugins/hid_gtk/ghid-search.c b/src_plugins/hid_gtk/ghid-search.c
new file mode 100644
index 0000000..cca5a7b
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-search.c
@@ -0,0 +1,856 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* advanced search dialog */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <genlist/gendlist.h>
+#include "gui.h"
+#include "create.h"
+#include "compat_misc.h"
+#include "ghid-search.h"
+#include "win_place.h"
+#include "hid_actions.h"
+#include "misc_util.h"
+
+typedef struct expr1_s expr1_t;
+
+struct expr1_s {
+	GtkWidget *and;        /* only if not the first row */
+
+	GtkWidget *hbox;       /* only if first in row*/
+	GtkWidget *remove_row; /* only if first in row*/
+	GtkWidget *append_col; /* only if first in row*/
+	GtkWidget *spc;        /* only if first in row*/
+
+	GtkWidget *remove, *remove_button;
+	GtkWidget *content;    /* the actual expression */
+	GtkWidget *or;         /* only if not the first in the row */
+
+	gdl_elem_t next_or;   /* --> */
+	gdl_elem_t next_and;  /* v */
+	gdl_list_t ors;       /* only in the first; the row-first is not on this list, so it collects the subseqent siblings only */
+
+	expr1_t *row;         /* only if not the first */
+
+	gulong sig_remove_row;
+	gulong sig_append_col;
+
+	char *code;          /* ... to use in the query script */
+};
+
+typedef struct {
+	GtkWidget *window;
+	GtkWidget *expr;          /* manual expression entry */
+	GtkWidget *action;        /* what-to-do combo box */
+	GtkWidget *wizard_enable; /* checkbox */
+	GtkWidget *wizard_vbox;
+	GtkWidget *new_row;
+
+	gdl_list_t wizard;       /* of expr1_t */
+} ghid_search_dialog_t;
+
+static ghid_search_dialog_t sdlg;
+
+static void new_col_cb(GtkWidget *button, void *data);
+static void remove_row_cb(GtkWidget *button, void *data);
+static void remove_expr_cb(GtkWidget *button, void *data);
+static void edit_expr_cb(GtkWidget *button, void *data);
+static void expr_wizard_dialog(expr1_t *e);
+
+static void build_expr1(expr1_t *e, GtkWidget *parent_box)
+{
+	e->content = gtk_button_new_with_label("<expr>");
+	gtk_button_set_image(GTK_BUTTON(e->content), gtk_image_new_from_icon_name("gtk-new", GTK_ICON_SIZE_MENU));
+	gtk_box_pack_start(GTK_BOX(parent_box), e->content, FALSE, FALSE, 0);
+	gtk_widget_set_tooltip_text(e->content, "Edit search expression");
+	g_signal_connect(e->content, "clicked", G_CALLBACK(edit_expr_cb), e);
+
+	e->remove = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(parent_box), e->remove, FALSE, FALSE, 0);
+
+	e->remove_button = gtk_button_new_with_label("");
+	gtk_button_set_image(GTK_BUTTON(e->remove_button), gtk_image_new_from_icon_name("gtk-delete", GTK_ICON_SIZE_MENU));
+	gtk_box_pack_start(GTK_BOX(e->remove), e->remove_button, FALSE, FALSE, 0);
+	gtk_widget_set_tooltip_text(e->remove_button, "Remove this expression");
+	g_signal_connect(e->remove_button, "clicked", G_CALLBACK(remove_expr_cb), e);
+}
+
+/* e is not part of any list by the time of the call */
+static void destroy_expr1(expr1_t *e)
+{
+#	define destroy(w) if (w != NULL) gtk_widget_destroy(w)
+	destroy(e->and);
+	destroy(e->spc);
+	destroy(e->remove_row);
+	destroy(e->append_col);
+	destroy(e->remove);
+	destroy(e->content);
+	destroy(e->or);
+	destroy(e->hbox);
+	free(e);
+#	undef destroy
+}
+
+static expr1_t *append_row()
+{
+	expr1_t *e = calloc(sizeof(expr1_t), 1);
+
+	if (gdl_first(&sdlg.wizard) != NULL) {
+		e->and = gtk_label_new("AND");
+		gtk_misc_set_alignment(GTK_MISC(e->and), -1, 0.);
+		gtk_box_pack_start(GTK_BOX(sdlg.wizard_vbox), e->and, FALSE, FALSE, 0);
+	}
+
+	e->hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(sdlg.wizard_vbox), e->hbox, FALSE, FALSE, 0);
+
+	e->remove_row = gtk_button_new_with_label("");
+	gtk_button_set_image(GTK_BUTTON(e->remove_row), gtk_image_new_from_icon_name("gtk-delete", GTK_ICON_SIZE_SMALL_TOOLBAR));
+	gtk_box_pack_start(GTK_BOX(e->hbox), e->remove_row, FALSE, FALSE, 0);
+	e->sig_remove_row = g_signal_connect(e->remove_row, "clicked", G_CALLBACK(remove_row_cb), e);
+	gtk_widget_set_tooltip_text(e->remove_row, "Remove this row of expressions");
+
+
+	e->append_col = gtk_button_new_with_label("");
+	gtk_button_set_image(GTK_BUTTON(e->append_col), gtk_image_new_from_icon_name("gtk-add", GTK_ICON_SIZE_SMALL_TOOLBAR));
+	gtk_box_pack_start(GTK_BOX(e->hbox), e->append_col, FALSE, FALSE, 0);
+	e->sig_append_col = g_signal_connect(e->append_col, "clicked", G_CALLBACK(new_col_cb), e);
+	gtk_widget_set_tooltip_text(e->append_col, "Append an expression to this row with OR");
+
+	e->spc = gtk_vbox_new(FALSE, 10);
+	gtk_box_pack_start(GTK_BOX(e->hbox), e->spc, FALSE, FALSE, 10);
+
+	build_expr1(e, e->hbox);
+
+	gdl_append(&sdlg.wizard, e, next_and);
+	return e;
+}
+
+static expr1_t *append_col(expr1_t *row)
+{
+	expr1_t *e = calloc(sizeof(expr1_t), 1);
+
+	e->or = gtk_label_new(" OR ");
+	gtk_misc_set_alignment(GTK_MISC(e->or), -1, 1);
+	gtk_box_pack_start(GTK_BOX(row->hbox), e->or, FALSE, FALSE, 0);
+
+	build_expr1(e, row->hbox);
+
+	gdl_append(&row->ors, e, next_or);
+	e->row = row;
+	return e;
+}
+
+static void remove_row(expr1_t *row)
+{
+	expr1_t *o;
+	gdl_remove(&sdlg.wizard, row, next_and);
+	for(o = gdl_first(&row->ors); o != NULL; o = gdl_next(&row->ors, o))
+		destroy_expr1(o);
+	destroy_expr1(row);
+
+	/* the new first widget must not have an AND "preface" */
+	o = gdl_first(&sdlg.wizard);
+	if ((o != NULL) && (o->and != NULL)) {
+		gtk_widget_destroy(o->and);
+		o->and = NULL;
+	}
+}
+
+static void remove_expr(expr1_t *e)
+{
+	if (e->row == NULL) {
+		/* first item in a row */
+		expr1_t *o, *o2 = gdl_first(&e->ors);
+		if (o2 != NULL) {
+			/* there are subsequent items in the row - have to make the first of them the new head */
+			gdl_remove(&e->ors, o2, next_or);
+			gdl_insert_before(&sdlg.wizard, e, o2, next_and);
+			gdl_remove(&sdlg.wizard, e, next_and);
+#			define inherit(dst, src, fld)  dst->fld = src->fld; src->fld = NULL;
+			inherit(o2, e, and);
+			inherit(o2, e, hbox);
+			inherit(o2, e, remove_row);
+			inherit(o2, e, append_col);
+			inherit(o2, e, spc);
+#			undef inherit
+			/* move the list, reparenting it */
+			memcpy(&o2->ors, &e->ors, sizeof(e->ors));
+			for(o = gdl_first(&o2->ors); o != NULL; o = gdl_next(&o2->ors, o))
+				o->next_or.parent = &o2->ors;
+
+			o2->row = NULL;
+			memset(&e->ors, 0, sizeof(e->ors));
+			if (o2->or != NULL) {
+				gtk_widget_destroy(o2->or);
+				o2->or = NULL;
+			}
+
+			/* reconnect row signals to the new head */
+			g_signal_handler_disconnect(o2->remove_row, e->sig_remove_row);
+			g_signal_handler_disconnect(o2->append_col, e->sig_append_col);
+
+			o2->sig_remove_row = g_signal_connect(o2->remove_row, "clicked", G_CALLBACK(remove_row_cb), o2);
+			o2->sig_append_col = g_signal_connect(o2->append_col, "clicked", G_CALLBACK(new_col_cb), o2);
+		}
+		else {
+			/* only item of the row */
+			remove_row(e);
+			return;
+		}
+	}
+	else
+		gdl_remove(&e->row->ors, e, next_or);
+	destroy_expr1(e);
+}
+
+static int num_ors(expr1_t *head)
+{
+	int count = 0;
+	expr1_t *o;
+	if (head->code != NULL)
+		count++;
+	for(o = gdl_first(&head->ors); o != NULL; o = gdl_next(&head->ors, o))
+		if (o->code != NULL)
+			count++;
+	return count;
+}
+
+static void rebuild(void)
+{
+	int and_first = 1;
+	gds_t s;
+	expr1_t *a, *o;
+	gds_init(&s);
+
+	for(a = gdl_first(&sdlg.wizard); a != NULL; a = gdl_next(&sdlg.wizard, a)) {
+		int or_first = 1;
+		int ors = num_ors(a);
+		if (ors == 0)
+			continue;
+
+		/* add the && if needed */
+		if (!and_first)
+			gds_append_str(&s, " && ");
+		and_first = 0;
+
+		if (ors > 1)
+			gds_append_str(&s, "(");
+
+		if (a->code != NULL) {
+			gds_append_str(&s, a->code);
+			or_first = 0;
+		}
+
+		for(o = gdl_first(&a->ors); o != NULL; o = gdl_next(&a->ors, o)) {
+			if (o->code != NULL) {
+				if (!or_first)
+					gds_append_str(&s, " || ");
+				or_first = 0;
+			}
+			gds_append_str(&s, o->code);
+		}
+
+		if (ors > 1)
+			gds_append_str(&s, ")");
+	}
+	gtk_entry_set_text(GTK_ENTRY(sdlg.expr), s.array);
+	gds_uninit(&s);
+}
+
+/* button callbacks */
+static void new_row_cb(GtkWidget *button, void *data)
+{
+	append_row();
+	gtk_widget_show_all(sdlg.window);
+}
+
+static void remove_row_cb(GtkWidget *button, void *data)
+{
+	expr1_t *row = (expr1_t *)data;
+	remove_row(row);
+	rebuild();
+	gtk_widget_show_all(sdlg.window);
+}
+
+static void remove_expr_cb(GtkWidget *button, void *data)
+{
+	remove_expr((expr1_t *)data);
+	rebuild();
+	gtk_widget_show_all(sdlg.window);
+}
+
+static void edit_expr_cb(GtkWidget *button, void *data)
+{
+	expr_wizard_dialog((expr1_t *)data);
+}
+
+static void new_col_cb(GtkWidget *button, void *data)
+{
+	expr1_t *row = (expr1_t *)data;
+	append_col(row);
+	gtk_widget_show_all(sdlg.window);
+}
+
+static void wizard_toggle_cb(GtkCheckButton *button, void *data)
+{
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) {
+		gtk_widget_show(sdlg.wizard_vbox);
+		gtk_widget_set_sensitive(sdlg.new_row, 1);
+		gtk_widget_set_sensitive(sdlg.expr, 0);
+
+	}
+	else {
+		gtk_widget_hide(sdlg.wizard_vbox);
+		gtk_widget_set_sensitive(sdlg.new_row, 0);
+		gtk_widget_set_sensitive(sdlg.expr, 1);
+	}
+}
+
+
+/* Run the expression wizard dialog box */
+#include "ghid-search-tab.h"
+
+static void expr_wizard_init_model()
+{
+	const expr_wizard_t *w;
+	expr_wizard_op_t *o;
+	const char **s;
+
+	if (expr_wizard_dlg.md_left != NULL)
+		return;
+
+	/* render operator models */
+	for(o = op_tab; o->ops != NULL; o++) {
+		o->model = gtk_list_store_newv(1, model_op);
+		for(s = o->ops; *s != NULL; s++)
+			gtk_list_store_insert_with_values(o->model, NULL, -1,  0, *s,  -1);
+	}
+
+	/* render right constant models */
+	for(o = right_const_tab; o->ops != NULL; o++) {
+		o->model = gtk_list_store_newv(1, model_op);
+		for(s = o->ops; *s != NULL; s++)
+			gtk_list_store_insert_with_values(o->model, NULL, -1,  0, *s,  -1);
+	}
+
+	{ /* create the left tree model */
+		GtkTreeIter *parent = NULL, iter, iparent;
+		expr_wizard_dlg.md_left = gtk_tree_store_newv(2, model_op);
+		for(w = expr_tab; w->left_desc != NULL; w++) {
+			if (w->left_var == NULL)
+				parent = NULL;
+			gtk_tree_store_append(expr_wizard_dlg.md_left, &iter, parent);
+			gtk_tree_store_set(expr_wizard_dlg.md_left, &iter, 0,w->left_desc, 1,w, -1);
+			if (w->left_var == NULL) {
+				/* new section */
+				iparent = iter;
+				parent = &iparent;
+			}
+		}
+	}
+}
+
+static void right_hide(void)
+{
+	gtk_widget_hide(expr_wizard_dlg.right_str);
+	gtk_widget_hide(expr_wizard_dlg.right_int);
+	gtk_widget_hide(expr_wizard_dlg.right_coord);
+	gtk_widget_hide(expr_wizard_dlg.tr_right);
+}
+
+static const expr_wizard_t *left_get_wiz(void)
+{
+	const expr_wizard_t *w;
+	GtkTreeSelection *tsel;
+	GtkTreeModel *tm;
+	GtkTreeIter iter;
+
+	tsel = gtk_tree_view_get_selection(GTK_TREE_VIEW(expr_wizard_dlg.tr_left));
+	if (tsel == NULL)
+		return NULL;
+
+	gtk_tree_selection_get_selected(tsel, &tm, &iter);
+	if (iter.stamp == 0)
+		return NULL;
+
+	gtk_tree_model_get(tm, &iter, 1, &w, -1);
+	return w;
+}
+
+/* Returns the string current value of a single-column-string-tree */
+static const char *wiz_get_tree_str(GtkWidget *tr)
+{
+	const char *s;
+	GtkTreeSelection *tsel;
+	GtkTreeModel *tm;
+	GtkTreeIter iter;
+
+	tsel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tr));
+	if (tsel == NULL)
+		return NULL;
+
+	gtk_tree_selection_get_selected(tsel, &tm, &iter);
+	if (iter.stamp == 0)
+		return NULL;
+
+	gtk_tree_model_get(tm, &iter, 0, &s, -1);
+	return s;
+}
+
+static void left_chg_cb(GtkTreeView *t, gpointer *data)
+{
+	const expr_wizard_t *w = left_get_wiz();
+
+	right_hide();
+
+	if ((w == NULL) || (w->left_var == NULL))
+		return;
+
+	gtk_tree_view_set_model(GTK_TREE_VIEW(expr_wizard_dlg.tr_op), GTK_TREE_MODEL(w->ops->model));
+
+	switch(w->rtype) {
+		case RIGHT_INT: gtk_widget_show(expr_wizard_dlg.right_int); break;
+		case RIGHT_STR: gtk_widget_show(expr_wizard_dlg.right_str); break;
+		case RIGHT_COORD: gtk_widget_show(expr_wizard_dlg.right_coord); break;
+		case RIGHT_CONST:
+			gtk_tree_view_set_model(GTK_TREE_VIEW(expr_wizard_dlg.tr_right), GTK_TREE_MODEL(w->right_const->model));
+			gtk_widget_show(expr_wizard_dlg.tr_right);
+			break;
+	}
+}
+
+static char *expr_wizard_result(int desc)
+{
+	gds_t s;
+	char tmp[128];
+	const char *cs;
+	const expr_wizard_t *parent, *w = left_get_wiz();
+
+	if ((w == NULL) || (w->left_var == NULL))
+		return NULL;
+
+
+	gds_init(&s);
+	if (desc) {
+		/* search the parent */
+		for(parent = w; parent >= expr_tab; parent--)
+			if (parent->left_var == NULL)
+				break;
+
+		if (parent->left_desc != NULL)
+			pcb_append_printf(&s, "%s\t%s", parent->left_desc, w->left_desc);
+		else
+			pcb_append_printf(&s, "%s", w->left_desc);
+	}
+	else
+		pcb_append_printf(&s, "(%s", w->left_var);
+
+	cs = wiz_get_tree_str(expr_wizard_dlg.tr_op);
+	if (cs == NULL)
+		goto err;
+
+	if (desc)
+		pcb_append_printf(&s, "\n%s\n", cs);
+	else
+		pcb_append_printf(&s, " %s ", cs);
+
+	switch(w->rtype) {
+		case RIGHT_INT: pcb_append_printf(&s, "%.0f", gtk_adjustment_get_value(expr_wizard_dlg.right_adj)); break;
+		case RIGHT_STR:
+			if (!desc)
+				gds_append_str(&s, "\"");
+			pcb_append_printf(&s, "%s", gtk_entry_get_text(GTK_ENTRY(expr_wizard_dlg.right_str)));
+			if (!desc)
+				gds_append_str(&s, "\"");
+			break;
+		case RIGHT_COORD:
+			ghid_coord_entry_get_value_str(GHID_COORD_ENTRY(expr_wizard_dlg.right_coord), tmp, sizeof(tmp));
+			pcb_append_printf(&s, "%s", tmp);
+			break;
+		case RIGHT_CONST:
+			cs = wiz_get_tree_str(expr_wizard_dlg.tr_right);
+			if (cs == NULL)
+				goto err;
+			pcb_append_printf(&s, "%s", cs);
+			break;
+	}
+
+	if (!desc)
+		gds_append_str(&s, ")");
+
+	return s.array;
+
+	err:;
+	free(s.array);
+	return NULL;
+}
+
+/* look up a tab entry with a slow linear search */
+static const expr_wizard_t *find_tab_entry(const expr_wizard_t *start_at, const char *desc, int need_hdr, int *idx)
+{
+	const expr_wizard_t *w;
+	int i = 0, initial = 1;
+
+	for(w = start_at; w->left_desc != NULL; w++) {
+		if (need_hdr) {
+			if (w->left_var != NULL) {
+				if (initial)
+					i++;
+				continue;
+			}
+			else
+				initial = 0;
+		}
+		else {
+			if (w->left_var == NULL)
+				return NULL;
+		}
+		if (strcmp(w->left_desc, desc) == 0) {
+			*idx = i;
+			return w;
+		}
+		i++;
+	}
+
+	return NULL;
+}
+
+/* Set enum-like tree cursor from a string; returns 0 if not found, 1 if found. */
+static int set_tree_from_enum(GtkWidget *tree, const char **vals, const char *target)
+{
+	int n, found;
+	const char **s;
+	for(n = 0, found = 0, s = vals; *s != NULL; s++, n++) {
+		if (strcmp(*s, target) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (found) {
+		GtkTreePath *path = gtk_tree_path_new_from_indices(n, -1);
+		gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree), path, NULL, FALSE);
+		gtk_tree_path_free(path);
+	}
+	return found;
+}
+
+/* Set the value of expr wizard widgets to match the button text from the previous set */
+void expr_wizard_import(const char *desc_)
+{
+	char *desc, *left, *left_parent, *left_desc, *op, *right, *sep;
+	const expr_wizard_t *w;
+	int l1idx = -1, l2idx = -1;
+
+	if (desc_ == NULL)
+		return;
+
+	/* split the string into left, op and right */
+	desc = pcb_strdup(desc_);
+	left = desc;
+
+	op = strchr(left, '\n');
+	if (op == NULL) goto fail;
+	*op = '\0';
+	op++;
+
+	right = strchr(op, '\n');
+	if (right == NULL) goto fail;
+	*right = '\0';
+	right++;
+
+	/* split left */
+	sep = strchr(left, '\t');
+	if (sep != NULL) {
+		*sep = '\0';
+		sep++;
+		left_parent = left;
+		left_desc = sep;
+	}
+	else {
+		left_parent = NULL;
+		left_desc = left;
+	}
+
+/*	printf("lp='%s' ld='%s' op='%s' r='%s'\n", left_parent, left_desc, op, right);*/
+	/* Find the tab entry */
+	
+	if (left_parent != NULL) {
+		w = find_tab_entry(expr_tab, left_parent, 1, &l1idx);
+		if (w == NULL) goto fail;
+		w++;
+	}
+	else
+		w = expr_tab;
+	w = find_tab_entry(w, left_desc, 0, &l2idx);
+	if (w == NULL) goto fail;
+
+	/* set left tree widget cursor */
+	{
+		GtkTreePath *path;
+		if (left_parent != NULL)
+			path = gtk_tree_path_new_from_indices(l1idx, l2idx, -1);
+		else
+			path = gtk_tree_path_new_from_indices(l2idx, -1);
+
+		gtk_tree_view_expand_to_path(GTK_TREE_VIEW(expr_wizard_dlg.tr_left), path);
+		gtk_tree_view_set_cursor(GTK_TREE_VIEW(expr_wizard_dlg.tr_left), path, NULL, FALSE);
+		gtk_tree_path_free(path);
+	}
+
+	/* set op cursor */
+	set_tree_from_enum(expr_wizard_dlg.tr_op, w->ops->ops, op);
+
+	/* set value field */
+	{
+		switch(w->rtype) {
+			case RIGHT_STR:
+				gtk_entry_set_text(GTK_ENTRY(expr_wizard_dlg.right_str), right);
+				break;
+			case RIGHT_INT:
+			{
+				char *end;
+				double d = strtod(right, &end);
+				if (*end == '\0')
+					gtk_spin_button_set_value(GTK_SPIN_BUTTON(expr_wizard_dlg.right_int), d);
+				break;
+			}
+			case RIGHT_COORD:
+			{
+				pcb_bool succ;
+				double d = GetValueEx(right, NULL, NULL, NULL, "mm", &succ);
+				if (succ)
+					ghid_coord_entry_set_value(GHID_COORD_ENTRY(expr_wizard_dlg.right_coord), d);
+				break;
+			}
+			case RIGHT_CONST:
+				set_tree_from_enum(expr_wizard_dlg.tr_right, w->right_const->ops, right);
+				break;
+		}
+	}
+
+	fail:;
+	free(desc);
+}
+
+static void expr_wizard_dialog(expr1_t *e)
+{
+	GtkWidget *dialog, *vbox, *hbox;
+	GtkCellRenderer *renderer;
+	gboolean response;
+
+	expr_wizard_init_model();
+
+	/* Create the dialog */
+	dialog = gtk_dialog_new_with_buttons("Expression wizard",
+	                                     GTK_WINDOW(gport->top_window),
+	                                     GTK_DIALOG_MODAL,
+	                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+	hbox = gtk_hbox_new(FALSE, 4);
+	gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+
+	/* left */
+	vbox = gtk_vbox_new(FALSE, 4);
+	expr_wizard_dlg.tr_left = gtk_tree_view_new();
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(expr_wizard_dlg.tr_left), -1, "variable", renderer, "text",0,  NULL);
+	gtk_tree_view_set_model(GTK_TREE_VIEW(expr_wizard_dlg.tr_left), GTK_TREE_MODEL(expr_wizard_dlg.md_left));
+	g_signal_connect(G_OBJECT(expr_wizard_dlg.tr_left), "cursor-changed", G_CALLBACK(left_chg_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.tr_left, FALSE, TRUE, 4);
+
+
+	expr_wizard_dlg.entry_left = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.entry_left, FALSE, TRUE, 4);
+
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 4);
+
+	/* operator */
+	vbox = gtk_vbox_new(FALSE, 4);
+
+	expr_wizard_dlg.tr_op = gtk_tree_view_new();
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(expr_wizard_dlg.tr_op), -1, "op", renderer, "text", 0, NULL);
+	gtk_tree_view_set_model(GTK_TREE_VIEW(expr_wizard_dlg.tr_op), GTK_TREE_MODEL(op_tab[OPS_ANY].model));
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.tr_op, FALSE, FALSE, 4);
+
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4);
+
+	/* right */
+	vbox = gtk_vbox_new(FALSE, 4);
+
+	expr_wizard_dlg.tr_right = gtk_tree_view_new();
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(expr_wizard_dlg.tr_right), -1, "constant", renderer, "text", 0, NULL);
+/*	gtk_tree_view_set_model(GTK_TREE_VIEW(expr_wizard_dlg.tr_right), GTK_TREE_MODEL(expr_wizard_dlg.md_objtype));*/
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.tr_right, FALSE, TRUE, 4);
+
+	expr_wizard_dlg.right_str = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.right_str, FALSE, TRUE, 4);
+
+	expr_wizard_dlg.right_adj = GTK_ADJUSTMENT(gtk_adjustment_new(10,
+	                                                              0, /* min */
+	                                                              8,
+	                                                              1, 1, /* steps */
+	                                                              0.0));
+/*	g_signal_connect(G_OBJECT(expr_wizard_dlg.right_adj), "value-changed", G_CALLBACK(config_auto_idx_changed_cb), NULL); */
+	expr_wizard_dlg.right_int = gtk_spin_button_new(expr_wizard_dlg.right_adj, 1, 8);
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.right_int, FALSE, TRUE, 4);
+
+	expr_wizard_dlg.right_coord = ghid_coord_entry_new(0, PCB_MM_TO_COORD(2100), 0, conf_core.editor.grid_unit, CE_MEDIUM);
+	gtk_box_pack_start(GTK_BOX(vbox), expr_wizard_dlg.right_coord, FALSE, TRUE, 4);
+
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 4);
+
+	/* pack content */
+	gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), hbox);
+	gtk_widget_show_all(dialog);
+	right_hide();
+
+	/* Run the dialog */
+	expr_wizard_import(gtk_button_get_label(GTK_BUTTON(e->content)));
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+	if (response == GTK_RESPONSE_OK) {
+		char *res = expr_wizard_result(1);
+		if (res != NULL) {
+			gtk_button_set_label(GTK_BUTTON(e->content), res);
+			free(res);
+		}
+		if (e->code != NULL)
+			free(e->code);
+		e->code = expr_wizard_result(0);
+		gtk_button_set_image(GTK_BUTTON(e->content), gtk_image_new_from_icon_name("gtk-refresh", GTK_ICON_SIZE_MENU));
+		rebuild();
+	}
+	gtk_widget_destroy(dialog);
+}
+
+
+/******** Advanced search window creation and administration ********/
+static void dialog_cb(GtkDialog *dlg, gint response_id, gpointer *data)
+{
+	const char *act, *script;
+
+	switch(response_id) {
+		case GTK_RESPONSE_APPLY:
+			script = gtk_entry_get_text(GTK_ENTRY(sdlg.expr));
+			act = gtk_combo_box_get_active_text(GTK_COMBO_BOX(sdlg.action));
+			hid_actionl("query", act, script, NULL);
+			break;
+		case GTK_RESPONSE_CLOSE:
+			gtk_widget_destroy(GTK_WIDGET(dlg));
+			break;
+	}
+}
+
+static void ghid_search_window_create()
+{
+	GtkWidget *vbox_win, *lab, *vbox;
+	GtkWidget *content_area, *top_window = gport->top_window;
+	const char *actions[] = { "select", "unselect", NULL };
+	const char **s;
+	int ver;
+
+	ver = hid_actionl("query", "version", NULL);
+	if (ver < 0100) {
+		sdlg.window = NULL;
+		Message(PCB_MSG_ERROR, "The query plugin is not avaialble, can not do advanced search.\n");
+		return;
+	}
+
+	/* make sure the list is empty */
+	memset(&sdlg.wizard, 0, sizeof(sdlg.wizard));
+
+	sdlg.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	sdlg.window = gtk_dialog_new_with_buttons(_("Advanced search"),
+																			 GTK_WINDOW(top_window),
+																			 GTK_DIALOG_DESTROY_WITH_PARENT,
+																			 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, NULL);
+
+	g_signal_connect(sdlg.window, "response", G_CALLBACK(dialog_cb), NULL);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(sdlg.window));
+
+	vbox_win = gtk_vbox_new(FALSE, 4);
+	gtk_container_add(GTK_CONTAINER(content_area), vbox_win);
+
+	lab = gtk_label_new("Query expression:");
+	gtk_box_pack_start(GTK_BOX(vbox_win), lab, FALSE, FALSE, 0);
+	gtk_misc_set_alignment(GTK_MISC(lab), -1, 0.);
+
+/* expr entry */
+	sdlg.expr = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(vbox_win), sdlg.expr, FALSE, FALSE, 0);
+
+	{
+		GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
+
+		sdlg.action = gtk_combo_box_new_text();
+		gtk_widget_set_tooltip_text(sdlg.action, "Do this with any object matching the query expression");
+		for(s = actions; *s != NULL; s++)
+			gtk_combo_box_append_text(GTK_COMBO_BOX(sdlg.action), *s);
+		gtk_box_pack_start(GTK_BOX(hbox), sdlg.action, FALSE, FALSE, 0);
+		gtk_combo_box_set_active(GTK_COMBO_BOX(sdlg.action), 0);
+
+		gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("matching items"), FALSE, FALSE, 0);
+
+		gtk_box_pack_start(GTK_BOX(vbox_win), hbox, FALSE, FALSE, 0);
+	}
+
+	sdlg.wizard_enable = gtk_check_button_new_with_label("Enable wizard");
+	g_signal_connect(sdlg.wizard_enable, "toggled", G_CALLBACK(wizard_toggle_cb), NULL);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sdlg.wizard_enable), 1);
+	gtk_box_pack_start(GTK_BOX(vbox_win), sdlg.wizard_enable, FALSE, FALSE, 0);
+
+/* */
+	vbox = ghid_framed_vbox(vbox_win, "wizard", 1, TRUE, 4, 10);
+
+	sdlg.wizard_vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(vbox), sdlg.wizard_vbox, TRUE, TRUE, 4);
+
+	sdlg.new_row = gtk_button_new_with_label("Add new row");
+	g_signal_connect(sdlg.new_row, "clicked", G_CALLBACK(new_row_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(vbox), sdlg.new_row, FALSE, FALSE, 0);
+	gtk_button_set_image(GTK_BUTTON(sdlg.new_row), gtk_image_new_from_icon_name("gtk-new", GTK_ICON_SIZE_MENU));
+	gtk_widget_set_tooltip_text(sdlg.new_row, "Append a row of expressions to the query with AND");
+
+
+/* Add one row of wizard to save a click in the most common case */
+	append_row();
+
+	gtk_widget_realize(sdlg.window);
+}
+
+void ghid_search_window_show(gboolean raise)
+{
+	ghid_search_window_create();
+	if (sdlg.window == NULL)
+		return;
+	gtk_widget_show_all(sdlg.window);
+	wplc_place(WPLC_SEARCH, sdlg.window);
+	if (raise)
+		gtk_window_present(GTK_WINDOW(sdlg.window));
+}
diff --git a/src_plugins/hid_gtk/ghid-search.h b/src_plugins/hid_gtk/ghid-search.h
new file mode 100644
index 0000000..cf90ffa
--- /dev/null
+++ b/src_plugins/hid_gtk/ghid-search.h
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* advanced search dialog */
+void ghid_search_window_show(gboolean raise);
+
+
diff --git a/src_plugins/hid_gtk/gschem_accel_label.c b/src_plugins/hid_gtk/gschem_accel_label.c
new file mode 100644
index 0000000..aa31b43
--- /dev/null
+++ b/src_plugins/hid_gtk/gschem_accel_label.c
@@ -0,0 +1,341 @@
+/* gEDA - GPL Electronic Design Automation
+ * gschem - gEDA Schematic Capture
+ * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
+ *
+ * Code based on GTK 2.14.5 gtk/gtkaccellabel.c (LGPL)
+ *
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GschemAccelLabel: GtkLabel with accelerator monitoring facilities.
+ * Copyright (C) 1998 Tim Janik
+ *
+ * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ *  Adapted for gEDA by Peter Clifton <pcjc2 at cam.ac.uk>
+ *
+ *  THIS FILE IS LGPL LICENSED, pcb-rnd AS A WHOLE IS GPL LICENSED
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+/* This file has been copied from gschem sources. It seems gtk's bondage
+and discipline menu item simply can not handle arbitrary accel label text
+and the default accel label widget works only if gtk takes over handling
+accel key input. In pbc-rnd there's a central input key handling and support
+for multi-key sequences. gschem had the same problem because of multi-key,
+and came up with this solution to rewrite the accel label object.
+
+Modifications:
+	- indentation
+	- #includes
+	- helper call that creates a menu item with label and accel label
+*/
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "gschem_accel_label.h"
+
+#define P_(x) (x)
+
+enum {
+	PROP_0,
+	PROP_ACCEL_CLOSURE,
+	PROP_ACCEL_WIDGET,
+	PROP_ACCEL_STRING,
+};
+
+G_DEFINE_TYPE(GschemAccelLabel, gschem_accel_label, GTK_TYPE_ACCEL_LABEL)
+
+
+gboolean gschem_accel_label_refetch(GschemAccelLabel * accel_label)
+{
+	gboolean enable_accels;
+
+	g_return_val_if_fail(GSCHEM_IS_ACCEL_LABEL(accel_label), FALSE);
+
+	g_object_get(gtk_widget_get_settings(GTK_WIDGET(accel_label)), "gtk-enable-accels", &enable_accels, NULL);
+
+	if (!enable_accels || accel_label->accel_string == NULL) {
+		if (accel_label->accel_string != NULL)
+			g_free(accel_label->accel_string);
+
+		accel_label->accel_string = g_strdup("");
+	}
+
+	gtk_widget_queue_resize(GTK_WIDGET(accel_label));
+
+	return FALSE;
+}
+
+
+static const gchar *gschem_accel_label_get_string(GschemAccelLabel * accel_label)
+{
+	if (!accel_label->accel_string)
+		gschem_accel_label_refetch(accel_label);
+
+	return accel_label->accel_string;
+}
+
+
+static void gschem_accel_label_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+	GschemAccelLabel *accel_label;
+
+	accel_label = GSCHEM_ACCEL_LABEL(object);
+
+	switch (prop_id) {
+		/* Dummy properties from GtkAccelLabel */
+	case PROP_ACCEL_CLOSURE:
+	case PROP_ACCEL_WIDGET:
+		break;
+
+	case PROP_ACCEL_STRING:
+		gschem_accel_label_set_accel_string(accel_label, g_value_get_string(value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void gschem_accel_label_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
+{
+	GschemAccelLabel *accel_label;
+
+	accel_label = GSCHEM_ACCEL_LABEL(object);
+
+	switch (prop_id) {
+		/* Dummy property from GtkAccelLabel */
+	case PROP_ACCEL_CLOSURE:
+		g_value_set_boxed(value, NULL);
+		break;
+
+		/* Dummy property from GtkAccelLabel */
+	case PROP_ACCEL_WIDGET:
+		g_value_set_object(value, NULL);
+		break;
+
+	case PROP_ACCEL_STRING:
+		g_value_set_string(value, accel_label->accel_string);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void gschem_accel_label_init(GschemAccelLabel * accel_label)
+{
+	accel_label->accel_padding = 3;
+	accel_label->accel_string = NULL;
+}
+
+static void gschem_accel_label_finalize(GObject * object)
+{
+	GschemAccelLabel *accel_label = GSCHEM_ACCEL_LABEL(object);
+
+	g_free(accel_label->accel_string);
+
+	G_OBJECT_CLASS(gschem_accel_label_parent_class)->finalize(object);
+}
+
+guint gschem_accel_label_get_accel_width(GschemAccelLabel * accel_label)
+{
+	g_return_val_if_fail(GSCHEM_IS_ACCEL_LABEL(accel_label), 0);
+
+	return (accel_label->accel_string_width + (accel_label->accel_string_width ? accel_label->accel_padding : 0));
+}
+
+static void gschem_accel_label_size_request(GtkWidget * widget, GtkRequisition * requisition)
+{
+	GschemAccelLabel *accel_label = GSCHEM_ACCEL_LABEL(widget);
+	GtkAccelLabel *gtk_accel_label = GTK_ACCEL_LABEL(widget);
+	PangoLayout *layout;
+	gint width;
+
+	GTK_WIDGET_CLASS(gschem_accel_label_parent_class)->size_request(widget, requisition);
+
+	layout = gtk_widget_create_pango_layout(widget, gschem_accel_label_get_string(accel_label));
+	pango_layout_get_pixel_size(layout, &width, NULL);
+	accel_label->accel_string_width = width;
+	gtk_accel_label->accel_string_width = width;	/* HACK: This field is private to GtkAccelLabel */
+	g_object_unref(layout);
+}
+
+static gint get_first_baseline(PangoLayout * layout)
+{
+	PangoLayoutIter *iter;
+	gint result;
+
+	iter = pango_layout_get_iter(layout);
+	result = pango_layout_iter_get_baseline(iter);
+	pango_layout_iter_free(iter);
+
+	return PANGO_PIXELS(result);
+}
+
+static gboolean gschem_accel_label_expose_event(GtkWidget * widget, GdkEventExpose * event)
+{
+	GschemAccelLabel *accel_label = GSCHEM_ACCEL_LABEL(widget);
+	GtkMisc *misc = GTK_MISC(accel_label);
+	GtkTextDirection direction;
+
+	direction = gtk_widget_get_direction(widget);
+
+	if (gtk_widget_is_drawable(GTK_WIDGET(accel_label))) {
+		guint ac_width;
+
+		ac_width = gschem_accel_label_get_accel_width(accel_label);
+
+		if (widget->allocation.width >= widget->requisition.width + ac_width) {
+			PangoLayout *label_layout;
+			PangoLayout *accel_layout;
+			GtkLabel *label = GTK_LABEL(widget);
+
+			gint x;
+			gint y;
+
+			label_layout = gtk_label_get_layout(GTK_LABEL(accel_label));
+
+			if (direction == GTK_TEXT_DIR_RTL)
+				widget->allocation.x += ac_width;
+			widget->allocation.width -= ac_width;
+			if (gtk_label_get_ellipsize(label))
+				pango_layout_set_width(label_layout, pango_layout_get_width(label_layout)
+															 - ac_width * PANGO_SCALE);
+
+			if (GTK_WIDGET_CLASS(gschem_accel_label_parent_class)->expose_event)
+				GTK_WIDGET_CLASS(gschem_accel_label_parent_class)->expose_event(widget, event);
+			if (direction == GTK_TEXT_DIR_RTL)
+				widget->allocation.x -= ac_width;
+			widget->allocation.width += ac_width;
+			if (gtk_label_get_ellipsize(label))
+				pango_layout_set_width(label_layout, pango_layout_get_width(label_layout)
+															 + ac_width * PANGO_SCALE);
+
+			if (direction == GTK_TEXT_DIR_RTL)
+				x = widget->allocation.x + misc->xpad;
+			else
+				x = widget->allocation.x + widget->allocation.width - misc->xpad - ac_width;
+
+			gtk_label_get_layout_offsets(GTK_LABEL(accel_label), NULL, &y);
+
+			accel_layout = gtk_widget_create_pango_layout(widget, gschem_accel_label_get_string(accel_label));
+
+			y += get_first_baseline(label_layout) - get_first_baseline(accel_layout);
+
+			gtk_paint_layout(widget->style,
+											 widget->window,
+											 gtk_widget_get_state(widget), FALSE, &event->area, widget, "accellabel", x, y, accel_layout);
+
+			g_object_unref(accel_layout);
+		}
+		else {
+			if (GTK_WIDGET_CLASS(gschem_accel_label_parent_class)->expose_event)
+				GTK_WIDGET_CLASS(gschem_accel_label_parent_class)->expose_event(widget, event);
+		}
+	}
+
+	return FALSE;
+}
+
+/* Underscores in key names are better displayed as spaces
+ * E.g., Page_Up should be "Page Up"
+ */
+static void substitute_underscores(char *str)
+{
+	char *p;
+
+	for (p = str; *p; p++)
+		if (*p == '_')
+			*p = ' ';
+}
+
+
+/**
+ * gschem_accel_label_set_accel_string:
+ * \param accel_label a #GschemAccelLabel
+ * \param accel_string the accelerator string.
+ *
+ * Sets the accelerator string for this accelerator label.
+ **/
+void gschem_accel_label_set_accel_string(GschemAccelLabel * accel_label, const gchar * accel_string)
+{
+	g_return_if_fail(GSCHEM_IS_ACCEL_LABEL(accel_label));
+
+	if (accel_label->accel_string)
+		g_free(accel_label->accel_string);
+
+	if (accel_string) {
+		accel_label->accel_string = g_strdup(accel_string);
+		substitute_underscores(accel_label->accel_string);
+	}
+	else {
+		accel_label->accel_string = NULL;
+	}
+
+	g_object_notify(G_OBJECT(accel_label), "accel-string");
+}
+
+static void gschem_accel_label_class_init(GschemAccelLabelClass * class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
+
+	gobject_class->finalize = gschem_accel_label_finalize;
+	gobject_class->set_property = gschem_accel_label_set_property;
+	gobject_class->get_property = gschem_accel_label_get_property;
+
+	widget_class->size_request = gschem_accel_label_size_request;
+	widget_class->expose_event = gschem_accel_label_expose_event;
+
+	g_object_class_install_property(gobject_class,
+																	PROP_ACCEL_CLOSURE,
+																	g_param_spec_boxed("accel-closure",
+																										 P_("Accelerator Closure"),
+																										 P_("The closure to be monitored for accelerator changes"),
+																										 G_TYPE_CLOSURE, G_PARAM_READWRITE));
+	g_object_class_install_property(gobject_class,
+																	PROP_ACCEL_WIDGET,
+																	g_param_spec_object("accel-widget",
+																											P_("Accelerator Widget"),
+																											P_("The widget to be monitored for accelerator changes"),
+																											GTK_TYPE_WIDGET, G_PARAM_READWRITE));
+	g_object_class_install_property(gobject_class,
+																	PROP_ACCEL_STRING,
+																	g_param_spec_string("accel-string",
+																											P_("Accelerator String"),
+																											P_("The accelerator string to be displayed"), NULL, G_PARAM_READWRITE));
+}
+
+GtkWidget *gtk_menu_item_new_gschem(const char *label, const char *accel_label)
+{
+	GtkWidget *w = gtk_menu_item_new();
+	GschemAccelLabel *al = g_object_new (GSCHEM_TYPE_ACCEL_LABEL,
+		"use-underline", TRUE,
+		"xalign", 0.0,
+		"visible", TRUE,
+		"label", label,
+		"accel-string", accel_label,
+		NULL);
+	gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(al));
+	return w;
+}
diff --git a/src_plugins/hid_gtk/gschem_accel_label.h b/src_plugins/hid_gtk/gschem_accel_label.h
new file mode 100644
index 0000000..0d20a66
--- /dev/null
+++ b/src_plugins/hid_gtk/gschem_accel_label.h
@@ -0,0 +1,77 @@
+/* gEDA - GPL Electronic Design Automation
+ * gschem - gEDA Schematic Capture
+ * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
+ *
+ * Code based on GTK 2.14.5 gtk/gtkaccellabel.h (LGPL)
+ *
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GschemAccelLabel: GtkLabel with accelerator monitoring facilities.
+ * Copyright (C) 1998 Tim Janik
+ *
+ * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ *  Adapted for gEDA by Peter Clifton <pcjc2 at cam.ac.uk>
+ *
+ *  THIS FILE IS LGPL LICENSED, gEDA AS A WHOLE IS GPL LICENSED
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GSCHEM_ACCEL_LABEL_H__
+#define __GSCHEM_ACCEL_LABEL_H__
+
+G_BEGIN_DECLS
+#define GSCHEM_TYPE_ACCEL_LABEL            (gschem_accel_label_get_type ())
+#define GSCHEM_ACCEL_LABEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSCHEM_TYPE_ACCEL_LABEL, GschemAccelLabel))
+#define GSCHEM_ACCEL_LABEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSCHEM_TYPE_ACCEL_LABEL, GschemAccelLabelClass))
+#define GSCHEM_IS_ACCEL_LABEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSCHEM_TYPE_ACCEL_LABEL))
+#define GSCHEM_IS_ACCEL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSCHEM_TYPE_ACCEL_LABEL))
+#define GSCHEM_ACCEL_LABEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSCHEM_TYPE_ACCEL_LABEL, GschemAccelLabelClass))
+typedef struct _GschemAccelLabel GschemAccelLabel;
+typedef struct _GschemAccelLabelClass GschemAccelLabelClass;
+
+struct _GschemAccelLabel {
+	GtkAccelLabel label;
+
+	guint accel_padding;
+	gchar *accel_string;
+	guint16 accel_string_width;
+};
+
+struct _GschemAccelLabelClass {
+	GtkAccelLabelClass parent_class;
+};
+
+
+GType gschem_accel_label_get_type(void) G_GNUC_CONST;
+GtkWidget *gschem_accel_label_new(const gchar * string);
+guint gschem_accel_label_get_accel_width(GschemAccelLabel * accel_label);
+void gschem_accel_label_set_accel_string(GschemAccelLabel * accel_label, const gchar * accel_string);
+gboolean gschem_accel_label_refetch(GschemAccelLabel * accel_label);
+
+/* Helper: create a new menu item with label and accel label set up */
+GtkWidget *gtk_menu_item_new_gschem(const char *label, const char *accel_label);
+
+/* private */
+gchar *_gschem_accel_label_class_get_accelerator_label(GschemAccelLabelClass * klass, guint accelerator_key, GdkModifierType accelerator_mods);
+
+G_END_DECLS
+#endif /* __GSCHEM_ACCEL_LABEL_H__ */
diff --git a/src_plugins/hid_gtk/gtk_conf_list.c b/src_plugins/hid_gtk/gtk_conf_list.c
new file mode 100644
index 0000000..613679d
--- /dev/null
+++ b/src_plugins/hid_gtk/gtk_conf_list.c
@@ -0,0 +1,323 @@
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "gtk_conf_list.h"
+#include "compat_misc.h"
+
+static void fill_misc_cols(gtk_conf_list_t *cl, int row_idx, GtkTreeIter *iter, lht_node_t *nd)
+{
+	int col;
+	if (cl->get_misc_col_data != NULL) {
+		for(col = 0; col < cl->num_cols; col++) {
+			if ((col == cl->col_data) || (col == cl->col_src))
+				continue;
+			char *s = cl->get_misc_col_data(row_idx, col, nd);
+			if (s != NULL) {
+				gtk_list_store_set(cl->l, iter, col, s, -1);
+				free(s);
+			}
+		}
+	}
+}
+
+/* Rebuild the list from the GUI. There would be a cheaper alternative for most
+   operations, but our lists are short (less than 10 items usually) and
+   edited rarely - optimize for code size instead of speed. This also solves
+   a higher level problem: sometimes the input list is a composite from multiple
+   sources whereas the output list must be all in CFR_DESIGN. */
+static void rebuild(gtk_conf_list_t *cl)
+{
+	GtkTreeModel *tm = gtk_tree_view_get_model(GTK_TREE_VIEW(cl->t));
+	GtkTreeIter it;
+	gboolean valid;
+	int n;
+
+	if (cl->inhibit_rebuild)
+		return;
+
+	if (cl->pre_rebuild != NULL)
+		cl->pre_rebuild(cl);
+
+/*	printf("rebuild\n");*/
+
+	for(valid = gtk_tree_model_get_iter_first(tm, &it), n = 0; valid; valid = gtk_tree_model_iter_next(tm, &it), n++) {
+		gchar *s;
+		lht_node_t *nd;
+
+		gtk_tree_model_get(tm, &it, cl->col_data, &s, -1);
+/*		printf(" -> %s\n", s);*/
+		if (cl->col_src > 0)
+			gtk_list_store_set(cl->l, &it, cl->col_src, "<not saved yet>", -1);
+
+		nd = lht_dom_node_alloc(LHT_TEXT, "");
+		nd->data.text.value = pcb_strdup(s == NULL ? "" : s);
+		nd->doc = cl->lst->doc;
+		lht_dom_list_append(cl->lst, nd);
+
+		fill_misc_cols(cl, n, &it, nd);
+		g_free(s);
+	}
+
+	if (cl->post_rebuild != NULL)
+		cl->post_rebuild(cl);
+}
+
+static int get_sel(gtk_conf_list_t *cl, GtkTreeIter *iter)
+{
+	GtkTreeSelection *tsel;
+	GtkTreeModel *tm;
+	GtkTreePath *path;
+	int *i;
+
+	tsel = gtk_tree_view_get_selection(GTK_TREE_VIEW(cl->t));
+	if (tsel == NULL)
+		return -1;
+
+	gtk_tree_selection_get_selected(tsel, &tm, iter);
+	if (iter->stamp == 0)
+		return -1;
+	path = gtk_tree_model_get_path(tm, iter);
+	if (path != NULL) {
+		i = gtk_tree_path_get_indices(path);
+		if (i != NULL)
+			return i[0];
+	}
+	return -1;
+}
+
+static void row_insert_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter, gtk_conf_list_t *cl)
+{
+	rebuild(cl);
+}
+
+static void row_delete_cb(GtkTreeModel *m, GtkTreePath *p, gtk_conf_list_t *cl)
+{
+	rebuild(cl);
+}
+
+static void button_ins_cb(GtkButton * button, gtk_conf_list_t *cl)
+{
+	GtkTreeIter *sibl, sibl_, iter;
+	lht_node_t *nd;
+	int idx = get_sel(cl, &sibl_);
+	if (idx < 0) {
+		idx = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(cl->t)), NULL);
+		sibl = NULL;
+	}
+	else
+		sibl = &sibl_;
+
+	gtk_list_store_insert_before(cl->l, &iter, sibl);
+
+	rebuild(cl);
+#warning TODO: insert new item at idx
+	nd = NULL;
+
+	fill_misc_cols(cl, idx, &iter, nd);
+	printf("ins %d!\n", idx);
+}
+
+static void button_del_cb(GtkButton * button, gtk_conf_list_t *cl)
+{
+	GtkTreeIter iter;
+	int max, idx = get_sel(cl, &iter);
+
+	if (idx < 0)
+		return;
+
+	printf("del %d!\n", idx);
+	gtk_list_store_remove(cl->l, &iter);
+	rebuild(cl);
+#warning TODO: remove list item idx
+	
+	/* set cursor to where the user may expect it */
+	max = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(cl->t)), NULL) - 1;
+	if (idx > max)
+		idx = max;
+	if (idx >= 0) {
+		GtkTreePath *path;
+		path = gtk_tree_path_new_from_indices(idx, -1);
+		gtk_tree_view_set_cursor(GTK_TREE_VIEW(cl->t), path, NULL, 0);
+	}
+}
+
+static void button_sel_cb(GtkButton * button, gtk_conf_list_t *cl)
+{
+	GtkWidget *fcd;
+	GtkTreeIter iter;
+	int idx = get_sel(cl, &iter);
+	if (idx < 0)
+		return;
+	fcd = gtk_file_chooser_dialog_new(cl->file_chooser_title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+	gtk_file_chooser_set_action(GTK_FILE_CHOOSER(fcd), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+
+	if (gtk_dialog_run(GTK_DIALOG(fcd)) == GTK_RESPONSE_ACCEPT) {
+		char *fno = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
+		const char *fn = fno;
+		
+		if (cl->file_chooser_postproc != NULL)
+			fn = cl->file_chooser_postproc(fno);
+
+		printf("sel %d '%s'\n", idx, fn);
+		gtk_list_store_set(cl->l, &iter, cl->col_data, fn, -1);
+#warning TODO: replace list item idx
+		rebuild(cl);
+		g_free(fno);
+	}
+	gtk_widget_destroy(fcd);
+}
+
+
+static void cell_edited_cb(GtkCellRendererText *cell, gchar *path, gchar *new_text, gtk_conf_list_t *cl)
+{
+	GtkTreeIter iter;
+	int idx = get_sel(cl, &iter);
+
+	cl->editing = 0;
+
+	printf("edit %d to %s!\n", idx, new_text);
+	gtk_list_store_set(cl->l, &iter, cl->col_data, new_text, -1);
+#warning TODO: replace list item idx
+	rebuild(cl);
+}
+
+static void cell_edit_started_cb(GtkCellRendererText *cell, GtkCellEditable *e,gchar *path, gtk_conf_list_t *cl)
+{
+	cl->editing = 1;
+}
+
+static void cell_edit_canceled_cb(GtkCellRendererText *cell, gtk_conf_list_t *cl)
+{
+	cl->editing = 0;
+}
+
+/* bind a few intuitive keys so that the list can be used without mouse */
+gboolean key_release_cb(GtkWidget *widget, GdkEventKey *event, gtk_conf_list_t *cl)
+{
+	unsigned short int kv = event->keyval;
+	if (cl->editing)
+		return FALSE;
+/*	printf("REL! %d %d\n", cl->editing, kv);*/
+	switch(kv) {
+#ifdef GDK_KEY_KP_Insert
+		case GDK_KEY_KP_Insert:
+#endif
+#ifdef GDK_KEY_Insert
+		case GDK_KEY_Insert:
+#endif
+		case 'i':
+			button_ins_cb(NULL, cl);
+			break;
+#ifdef GDK_KEY_KP_Delete
+		case GDK_KEY_KP_Delete:
+#endif
+#ifdef GDK_KEY_Delete
+		case GDK_KEY_Delete:
+#endif
+		case 'd':
+			button_del_cb(NULL, cl);
+			break;
+		case 'c':
+		case 's':
+			if (cl->file_chooser_title != NULL)
+				button_sel_cb(NULL, cl);
+			break;
+	}
+	return 0;
+}
+
+int gtk_conf_list_set_list(gtk_conf_list_t *cl, lht_node_t *lst)
+{
+	GtkTreeIter iter;
+	lht_node_t *nd;
+
+	if ((lst == NULL) || (lst->type != LHT_LIST))
+		return -1;
+
+	cl->lst = lst;
+	cl->inhibit_rebuild = 1;
+
+	gtk_list_store_clear(cl->l);
+
+	/* fill in the list with initial data */
+	for(nd = cl->lst->data.list.first; nd != NULL; nd = nd->next) {
+		if (nd->type != LHT_TEXT)
+			continue;
+
+		gtk_list_store_append(cl->l, &iter);
+		gtk_list_store_set(cl->l, &iter, cl->col_data, nd->data.text.value, -1);
+		if (nd->file_name != NULL)
+			gtk_list_store_set(cl->l, &iter, cl->col_src, nd->file_name, -1);
+		fill_misc_cols(cl, cl->num_cols, &iter, nd);
+	}
+
+	cl->inhibit_rebuild = 0;
+
+	return 0;
+}
+
+GtkWidget *gtk_conf_list_widget(gtk_conf_list_t *cl)
+{
+	GtkWidget *vbox, *hbox, *bins, *bdel, *bsel;
+	int n;
+	GType *ty;
+
+	cl->editing = 0;
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	cl->t = gtk_tree_view_new();
+
+	/* create the list model */
+	ty = malloc(sizeof(GType) * cl->num_cols);
+	for(n = 0; n < cl->num_cols; n++)
+		ty[n] = G_TYPE_STRING;
+	cl->l = gtk_list_store_newv(cl->num_cols, ty);
+	free(ty);
+
+	if (cl->lst != NULL) {
+		lht_node_t *lst = cl->lst;
+		cl->lst = NULL;
+		gtk_conf_list_set_list(cl, lst);
+	}
+
+	/* add all columns */
+	for(n = 0; n < cl->num_cols; n++) {
+		GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(cl->t), -1, cl->col_names[n], renderer, "text", n, NULL);
+		if (n == cl->col_data) {
+			g_object_set(renderer, "editable", TRUE, NULL);
+			g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_cb), cl);
+			g_signal_connect(renderer, "editing-started", G_CALLBACK(cell_edit_started_cb), cl);
+			g_signal_connect(renderer, "editing-canceled", G_CALLBACK(cell_edit_canceled_cb), cl);
+		}
+	}
+
+	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(cl->t), cl->reorder);
+
+	g_signal_connect(G_OBJECT(cl->l), "row_deleted", G_CALLBACK(row_delete_cb), cl);
+	g_signal_connect(G_OBJECT(cl->l), "row_inserted", G_CALLBACK(row_insert_cb), cl);
+	g_signal_connect(G_OBJECT(cl->t), "key-release-event", G_CALLBACK(key_release_cb), cl);
+
+	gtk_tree_view_set_model(GTK_TREE_VIEW(cl->t), GTK_TREE_MODEL(cl->l));
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	bins = gtk_button_new_with_label("insert new");
+	bdel = gtk_button_new_with_label("remove");
+
+	gtk_box_pack_start(GTK_BOX(hbox), bins, FALSE, FALSE, 2);
+	gtk_box_pack_start(GTK_BOX(hbox), bdel, FALSE, FALSE, 2);
+
+	g_signal_connect(G_OBJECT(bins), "clicked", G_CALLBACK(button_ins_cb), cl);
+	g_signal_connect(G_OBJECT(bdel), "clicked", G_CALLBACK(button_del_cb), cl);
+
+	if (cl->file_chooser_title != NULL) {
+		bsel = gtk_button_new_with_label("change path");
+		gtk_box_pack_start(GTK_BOX(hbox), bsel, FALSE, FALSE, 2);
+		g_signal_connect(G_OBJECT(bsel), "clicked", G_CALLBACK(button_sel_cb), cl);
+	}
+
+	gtk_box_pack_start(GTK_BOX(vbox), cl->t, TRUE, TRUE, 2);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+	return vbox;
+}
diff --git a/src_plugins/hid_gtk/gtk_conf_list.h b/src_plugins/hid_gtk/gtk_conf_list.h
new file mode 100644
index 0000000..2c45a56
--- /dev/null
+++ b/src_plugins/hid_gtk/gtk_conf_list.h
@@ -0,0 +1,35 @@
+#include <gtk/gtk.h>
+#include "conf.h"
+
+typedef struct gtk_conf_list_s  gtk_conf_list_t;
+
+struct gtk_conf_list_s {
+	int num_cols;               /* number of visible columns */
+	const char **col_names;     /* header for each visible column */
+	int col_data, col_src;      /* column index where list data and list source (lihata path) should be automatically added (-1 means off) */
+	int reorder;                /* 0 to disable reordering */
+
+	lht_node_t *lst;            /* the list (of text nodes!) to build the initial GUI list from */
+/*	lht_node_t *lst_out;*/        /* the list to overwrite */
+/*	char *lst_update_path;*/      /* path to conf_update after an overwrite */
+	void (*pre_rebuild)(gtk_conf_list_t *cl); /* called before rebuilding lst */
+	void (*post_rebuild)(gtk_conf_list_t *cl); /* called after rebuilding lst */
+
+	/* callback to fill in non-data, non-src cells */
+	char *(*get_misc_col_data)(int row, int col, lht_node_t *nd); /* free()'d after the call; NULL=empty */
+
+	/* if not NULL, allow the file chooser to be invoked on the data cells */
+	const char *file_chooser_title; 
+
+	/* if the file chooser ran, filter the name through this function before inserting in the table */
+	const char *(*file_chooser_postproc)(char *path);
+
+	/* -- internal -- */
+	GtkListStore *l;
+	GtkWidget *t;
+	int editing;
+	int inhibit_rebuild;
+};
+
+GtkWidget *gtk_conf_list_widget(gtk_conf_list_t *cl);
+int gtk_conf_list_set_list(gtk_conf_list_t *cl, lht_node_t *lst);
diff --git a/src_plugins/hid_gtk/gtk_debug.h b/src_plugins/hid_gtk/gtk_debug.h
new file mode 100644
index 0000000..85c85a4
--- /dev/null
+++ b/src_plugins/hid_gtk/gtk_debug.h
@@ -0,0 +1,26 @@
+/* #include this file to get widget print functions */
+
+#include <stdio.h>
+#include <glib.h>
+
+static void print_widget(GtkWidget * w)
+{
+	fprintf(stderr, " %p %d;%d %d;%d %d;%d\n", (void *)w, w->allocation.x, w->allocation.y, w->allocation.width, w->allocation.height,
+					w->requisition.width, w->requisition.height);
+	fprintf(stderr, "  flags=%x typ=%d realized=%d vis=%d\n", GTK_WIDGET_FLAGS(w), GTK_WIDGET_TYPE(w), GTK_WIDGET_REALIZED(w),
+					GTK_WIDGET_VISIBLE(w));
+}
+
+
+static void print_menu_shell_cb(void *obj, void *ud)
+{
+	GtkWidget *w = obj;
+	print_widget(w);
+}
+
+static void print_menu_shell(GtkMenuShell * sh)
+{
+	GList *children;
+	children = gtk_container_get_children(GTK_CONTAINER(sh));
+	g_list_foreach(children, print_menu_shell_cb, NULL);
+}
diff --git a/src_plugins/hid_gtk/gtkhid-gdk.c b/src_plugins/hid_gtk/gtkhid-gdk.c
new file mode 100644
index 0000000..d9a2987
--- /dev/null
+++ b/src_plugins/hid_gtk/gtkhid-gdk.c
@@ -0,0 +1,1378 @@
+#include "global.h"
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+
+#include "crosshair.h"
+#include "clip.h"
+#include "layer.h"
+#include "gui.h"
+#include "hid_draw_helpers.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_color.h"
+
+extern HID ghid_hid;
+
+/* Sets priv->u_gc to the "right" GC to use (wrt mask or window)
+*/
+#define USE_GC(gc) if (!use_gc(gc)) return
+
+static int cur_mask = -1;
+static int mask_seq = 0;
+
+typedef struct render_priv {
+	GdkGC *bg_gc;
+	GdkGC *offlimits_gc;
+	GdkGC *mask_gc;
+	GdkGC *u_gc;
+	GdkGC *grid_gc;
+	pcb_bool clip;
+	GdkRectangle clip_rect;
+	int attached_invalidate_depth;
+	int mark_invalidate_depth;
+
+	/* Feature for leading the user to a particular location */
+	guint lead_user_timeout;
+	GTimer *lead_user_timer;
+	pcb_bool lead_user;
+	Coord lead_user_radius;
+	Coord lead_user_x;
+	Coord lead_user_y;
+
+} render_priv;
+
+
+typedef struct hid_gc_struct {
+	HID *me_pointer;
+	GdkGC *gc;
+
+	gchar *colorname;
+	Coord width;
+	gint cap, join;
+	gchar xor_mask;
+	gint mask_seq;
+} hid_gc_struct;
+
+
+static void draw_lead_user(render_priv * priv);
+
+
+int ghid_set_layer(const char *name, int group, int empty)
+{
+	int idx = group;
+	if (idx >= 0 && idx < max_group) {
+		int n = PCB->LayerGroups.Number[group];
+		for (idx = 0; idx < n - 1; idx++) {
+			int ni = PCB->LayerGroups.Entries[group][idx];
+			if (ni >= 0 && ni < max_copper_layer + 2 && PCB->Data->Layer[ni].On)
+				break;
+		}
+		idx = PCB->LayerGroups.Entries[group][idx];
+	}
+
+	if (idx >= 0 && idx < max_copper_layer + 2)
+		return /*pinout ? 1 : */ PCB->Data->Layer[idx].On;
+	if (idx < 0) {
+		switch (SL_TYPE(idx)) {
+		case SL_INVISIBLE:
+			return /* pinout ? 0 : */ PCB->InvisibleObjectsOn;
+		case SL_MASK:
+			if (SL_MYSIDE(idx) /*&& !pinout */ )
+				return conf_core.editor.show_mask;
+			return 0;
+		case SL_SILK:
+			if (SL_MYSIDE(idx) /*|| pinout */ )
+				return PCB->ElementOn;
+			return 0;
+		case SL_ASSY:
+			return 0;
+		case SL_PDRILL:
+		case SL_UDRILL:
+			return 1;
+		case SL_RATS:
+			return PCB->RatOn;
+		}
+	}
+	return 0;
+}
+
+void ghid_destroy_gc(hidGC gc)
+{
+	if (gc->gc)
+		g_object_unref(gc->gc);
+	if (gc->colorname != NULL)
+		g_free(gc->colorname);
+	g_free(gc);
+}
+
+hidGC ghid_make_gc(void)
+{
+	hidGC rv;
+
+	rv = g_new0(hid_gc_struct, 1);
+	rv->me_pointer = &ghid_hid;
+	rv->colorname = g_strdup(conf_core.appearance.color.background);
+	return rv;
+}
+
+static void set_clip(render_priv * priv, GdkGC * gc)
+{
+	if (gc == NULL)
+		return;
+
+	if (priv->clip)
+		gdk_gc_set_clip_rectangle(gc, &priv->clip_rect);
+	else
+		gdk_gc_set_clip_mask(gc, NULL);
+}
+
+static inline void ghid_draw_grid_global(void)
+{
+	render_priv *priv = gport->render_priv;
+	Coord x, y, x1, y1, x2, y2, grd;
+	int n, i;
+	static GdkPoint *points = NULL;
+	static int npoints = 0;
+
+	x1 = GridFit(MAX(0, SIDE_X(gport->view.x0)), PCB->Grid, PCB->GridOffsetX);
+	y1 = GridFit(MAX(0, SIDE_Y(gport->view.y0)), PCB->Grid, PCB->GridOffsetY);
+	x2 = GridFit(MIN(PCB->MaxWidth,  SIDE_X(gport->view.x0 + gport->view.width - 1)), PCB->Grid, PCB->GridOffsetX);
+	y2 = GridFit(MIN(PCB->MaxHeight, SIDE_Y(gport->view.y0 + gport->view.height - 1)), PCB->Grid, PCB->GridOffsetY);
+
+	grd = PCB->Grid;
+	
+	if (Vz(grd) < conf_hid_gtk.plugins.hid_gtk.global_grid.min_dist_px) {
+		if (!conf_hid_gtk.plugins.hid_gtk.global_grid.sparse)
+			return;
+		grd *= (conf_hid_gtk.plugins.hid_gtk.global_grid.min_dist_px / Vz(grd));
+	}
+
+	if (x1 > x2) {
+		Coord tmp = x1;
+		x1 = x2;
+		x2 = tmp;
+	}
+	if (y1 > y2) {
+		Coord tmp = y1;
+		y1 = y2;
+		y2 = tmp;
+	}
+	if (Vx(x1) < 0)
+		x1 += grd;
+	if (Vy(y1) < 0)
+		y1 += grd;
+	if (Vx(x2) >= gport->width)
+		x2 -= grd;
+	if (Vy(y2) >= gport->height)
+		y2 -= grd;
+
+
+	n = (x2 - x1) / grd + 1;
+	if (n > npoints) {
+		npoints = n + 10;
+		points = (GdkPoint *) realloc(points, npoints * sizeof(GdkPoint));
+	}
+	n = 0;
+	for (x = x1; x <= x2; x += grd) {
+		points[n].x = Vx(x);
+		n++;
+	}
+	if (n == 0)
+		return;
+	for (y = y1; y <= y2; y += grd) {
+		for (i = 0; i < n; i++)
+			points[i].y = Vy(y);
+		gdk_draw_points(gport->drawable, priv->grid_gc, points, n);
+	}
+}
+
+static void ghid_draw_grid_local_(Coord cx, Coord cy, int radius)
+{
+	render_priv *priv = gport->render_priv;
+	static GdkPoint *points_base = NULL;
+	static GdkPoint *points_abs = NULL;
+	static int apoints = 0, npoints = 0, old_radius = 0;
+	static Coord last_grid = 0;
+	int recalc = 0, n, r2;
+	Coord x, y;
+
+	/* PI is approximated with 3.25 here - allows a minimal overallocation, speeds up calculations */
+	r2 = radius * radius;
+	n = r2 * 3 + r2 / 4 + 1;
+	if (n > apoints) {
+		apoints = n;
+		points_base = (GdkPoint *) realloc(points_base, apoints * sizeof(GdkPoint));
+		points_abs  = (GdkPoint *) realloc(points_abs,  apoints * sizeof(GdkPoint));
+	}
+
+	if (radius != old_radius) {
+		old_radius = radius;
+		recalc = 1;
+	}
+
+	if (last_grid != PCB->Grid) {
+		last_grid = PCB->Grid;
+		recalc = 1;
+	}
+
+	/* reclaculate the 'filled circle' mask (base relative coords) if grid or radius changed */
+	if (recalc) {
+
+		npoints = 0;
+		for(y = -radius; y <= radius; y++) {
+			int y2 = y*y;
+			for(x = -radius; x <= radius; x++) {
+				if (x*x + y2 < r2) {
+					points_base[npoints].x = x*PCB->Grid;
+					points_base[npoints].y = y*PCB->Grid;
+					npoints++;
+				}
+			}
+		}
+	}
+
+	/* calculate absolute positions */
+	for(n = 0; n < npoints; n++) {
+		points_abs[n].x = Vx(points_base[n].x + cx);
+		points_abs[n].y = Vy(points_base[n].y + cy);
+	}
+
+	gdk_draw_points(gport->drawable, priv->grid_gc, points_abs, npoints);
+}
+
+
+static int grid_local_have_old = 0, grid_local_old_r = 0;
+static Coord grid_local_old_x, grid_local_old_y;
+
+void ghid_draw_grid_local(Coord cx, Coord cy)
+{
+	if (grid_local_have_old) {
+		ghid_draw_grid_local_(grid_local_old_x, grid_local_old_y, grid_local_old_r);
+		grid_local_have_old = 0;
+	}
+
+	if (!conf_hid_gtk.plugins.hid_gtk.local_grid.enable)
+		return;
+
+	if ((Vz(PCB->Grid) < MIN_GRID_DISTANCE) || (!conf_core.editor.draw_grid))
+		return;
+
+	/* cx and cy are the actual cursor snapped to wherever - round them to the nearest real grid point */
+	cx = (cx / PCB->Grid) * PCB->Grid + PCB->GridOffsetX;
+	cy = (cy / PCB->Grid) * PCB->Grid + PCB->GridOffsetY;
+
+	grid_local_have_old = 1;
+	ghid_draw_grid_local_(cx, cy, conf_hid_gtk.plugins.hid_gtk.local_grid.radius);
+	grid_local_old_x = cx;
+	grid_local_old_y = cy;
+	grid_local_old_r = conf_hid_gtk.plugins.hid_gtk.local_grid.radius;
+}
+
+static void ghid_draw_grid(void)
+{
+	render_priv *priv = gport->render_priv;
+
+	grid_local_have_old = 0;
+
+	if (!conf_core.editor.draw_grid)
+		return;
+	if (!priv->grid_gc) {
+		if (gdk_color_parse(conf_core.appearance.color.grid, &gport->grid_color)) {
+			gport->grid_color.red ^= gport->bg_color.red;
+			gport->grid_color.green ^= gport->bg_color.green;
+			gport->grid_color.blue ^= gport->bg_color.blue;
+			gdk_color_alloc(gport->colormap, &gport->grid_color);
+		}
+		priv->grid_gc = gdk_gc_new(gport->drawable);
+		gdk_gc_set_function(priv->grid_gc, GDK_XOR);
+		gdk_gc_set_foreground(priv->grid_gc, &gport->grid_color);
+		gdk_gc_set_clip_origin(priv->grid_gc, 0, 0);
+		set_clip(priv, priv->grid_gc);
+	}
+
+	if (conf_hid_gtk.plugins.hid_gtk.local_grid.enable) {
+		ghid_draw_grid_local(grid_local_old_x, grid_local_old_y);
+		return;
+	}
+
+
+	ghid_draw_grid_global();
+}
+
+/* ------------------------------------------------------------ */
+static void ghid_draw_bg_image(void)
+{
+	static GdkPixbuf *pixbuf;
+	GdkInterpType interp_type;
+	gint src_x, src_y, dst_x, dst_y, w, h, w_src, h_src;
+	static gint w_scaled, h_scaled;
+	render_priv *priv = gport->render_priv;
+
+	if (!ghidgui->bg_pixbuf)
+		return;
+
+	src_x = gport->view.x0;
+	src_y = gport->view.y0;
+	dst_x = 0;
+	dst_y = 0;
+
+	if (src_x < 0) {
+		dst_x = -src_x;
+		src_x = 0;
+	}
+	if (src_y < 0) {
+		dst_y = -src_y;
+		src_y = 0;
+	}
+
+	w = PCB->MaxWidth / gport->view.coord_per_px;
+	h = PCB->MaxHeight / gport->view.coord_per_px;
+	src_x = src_x / gport->view.coord_per_px;
+	src_y = src_y / gport->view.coord_per_px;
+	dst_x = dst_x / gport->view.coord_per_px;
+	dst_y = dst_y / gport->view.coord_per_px;
+
+	if (w_scaled != w || h_scaled != h) {
+		if (pixbuf)
+			g_object_unref(G_OBJECT(pixbuf));
+
+		w_src = gdk_pixbuf_get_width(ghidgui->bg_pixbuf);
+		h_src = gdk_pixbuf_get_height(ghidgui->bg_pixbuf);
+		if (w > w_src && h > h_src)
+			interp_type = GDK_INTERP_NEAREST;
+		else
+			interp_type = GDK_INTERP_BILINEAR;
+
+		pixbuf = gdk_pixbuf_scale_simple(ghidgui->bg_pixbuf, w, h, interp_type);
+		w_scaled = w;
+		h_scaled = h;
+	}
+
+	if (pixbuf)
+		gdk_pixbuf_render_to_drawable(pixbuf, gport->drawable, priv->bg_gc, src_x, src_y, dst_x, dst_y, w - src_x, h - src_y, GDK_RGB_DITHER_NORMAL, 0, 0);
+}
+
+#define WHICH_GC(gc) (cur_mask == HID_MASK_CLEAR ? priv->mask_gc : (gc)->gc)
+
+void ghid_use_mask(int use_it)
+{
+	static int mask_seq_id = 0;
+	GdkColor color;
+	render_priv *priv = gport->render_priv;
+
+	if (!gport->pixmap)
+		return;
+	if (use_it == cur_mask)
+		return;
+	switch (use_it) {
+	case HID_MASK_OFF:
+		gport->drawable = gport->pixmap;
+		mask_seq = 0;
+		break;
+
+	case HID_MASK_BEFORE:
+		/* The HID asks not to receive this mask type, so warn if we get it */
+		g_return_if_reached();
+
+	case HID_MASK_CLEAR:
+		if (!gport->mask)
+			gport->mask = gdk_pixmap_new(0, gport->width, gport->height, 1);
+		gport->drawable = gport->mask;
+		mask_seq = 0;
+		if (!priv->mask_gc) {
+			priv->mask_gc = gdk_gc_new(gport->drawable);
+			gdk_gc_set_clip_origin(priv->mask_gc, 0, 0);
+			set_clip(priv, priv->mask_gc);
+		}
+		color.pixel = 1;
+		gdk_gc_set_foreground(priv->mask_gc, &color);
+		gdk_draw_rectangle(gport->drawable, priv->mask_gc, TRUE, 0, 0, gport->width, gport->height);
+		color.pixel = 0;
+		gdk_gc_set_foreground(priv->mask_gc, &color);
+		break;
+
+	case HID_MASK_AFTER:
+		mask_seq_id++;
+		if (!mask_seq_id)
+			mask_seq_id = 1;
+		mask_seq = mask_seq_id;
+
+		gport->drawable = gport->pixmap;
+		break;
+
+	}
+	cur_mask = use_it;
+}
+
+
+typedef struct {
+	int color_set;
+	GdkColor color;
+	int xor_set;
+	GdkColor xor_color;
+} ColorCache;
+
+
+	/* Config helper functions for when the user changes color preferences.
+	   |  set_special colors used in the gtkhid.
+	 */
+static void set_special_grid_color(void)
+{
+	render_priv *priv = gport->render_priv;
+	int red, green, blue;
+
+	if (!gport->colormap)
+		return;
+
+	red = (gport->grid_color.red ^ gport->bg_color.red) & 0xFF;
+	green = (gport->grid_color.green ^ gport->bg_color.green) & 0xFF;
+	blue = (gport->grid_color.blue ^ gport->bg_color.blue) & 0xFF;
+	conf_setf(CFR_DESIGN, "appearance/color/grid", -1, "#%02x%02x%02x", red, green, blue);
+	ghid_map_color_string(conf_core.appearance.color.grid, &gport->grid_color);
+
+	config_color_button_update(conf_get_field("appearance/color/grid"), -1);
+
+	if (priv->grid_gc)
+		gdk_gc_set_foreground(priv->grid_gc, &gport->grid_color);
+}
+
+void ghid_set_special_colors(conf_native_t *cfg)
+{
+	render_priv *priv = gport->render_priv;
+	if (((CFT_COLOR *)cfg->val.color == &conf_core.appearance.color.background) && priv->bg_gc) {
+		ghid_map_color_string(cfg->val.color[0], &gport->bg_color);
+		gdk_gc_set_foreground(priv->bg_gc, &gport->bg_color);
+		set_special_grid_color();
+	}
+	else if (((CFT_COLOR *)cfg->val.color == &conf_core.appearance.color.off_limit) && priv->offlimits_gc) {
+		ghid_map_color_string(cfg->val.color[0], &gport->offlimits_color);
+		gdk_gc_set_foreground(priv->offlimits_gc, &gport->offlimits_color);
+	}
+	else if (((CFT_COLOR *)cfg->val.color == &conf_core.appearance.color.grid) && priv->grid_gc) {
+		ghid_map_color_string(cfg->val.color[0], &gport->grid_color);
+		set_special_grid_color();
+	}
+}
+
+void ghid_set_color(hidGC gc, const char *name)
+{
+	static void *cache = 0;
+	hidval cval;
+
+	if (name == NULL) {
+		fprintf(stderr, "ghid_set_color():  name = NULL, setting to magenta\n");
+		name = "magenta";
+	}
+
+	if (name != gc->colorname) {
+		if (gc->colorname != NULL)
+			g_free(gc->colorname);
+		gc->colorname = g_strdup(name);
+	}
+
+	if (!gc->gc)
+		return;
+	if (gport->colormap == 0)
+		gport->colormap = gtk_widget_get_colormap(gport->top_window);
+
+	if (strcmp(name, "erase") == 0) {
+		gdk_gc_set_foreground(gc->gc, &gport->bg_color);
+	}
+	else if (strcmp(name, "drill") == 0) {
+		gdk_gc_set_foreground(gc->gc, &gport->offlimits_color);
+	}
+	else {
+		ColorCache *cc;
+		if (hid_cache_color(0, name, &cval, &cache))
+			cc = (ColorCache *) cval.ptr;
+		else {
+			cc = (ColorCache *) malloc(sizeof(ColorCache));
+			memset(cc, 0, sizeof(*cc));
+			cval.ptr = cc;
+			hid_cache_color(1, name, &cval, &cache);
+		}
+
+		if (!cc->color_set) {
+			if (gdk_color_parse(name, &cc->color))
+				gdk_color_alloc(gport->colormap, &cc->color);
+			else
+				gdk_color_white(gport->colormap, &cc->color);
+			cc->color_set = 1;
+		}
+		if (gc->xor_mask) {
+			if (!cc->xor_set) {
+				cc->xor_color.red = cc->color.red ^ gport->bg_color.red;
+				cc->xor_color.green = cc->color.green ^ gport->bg_color.green;
+				cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue;
+				gdk_color_alloc(gport->colormap, &cc->xor_color);
+				cc->xor_set = 1;
+			}
+			gdk_gc_set_foreground(gc->gc, &cc->xor_color);
+		}
+		else {
+			gdk_gc_set_foreground(gc->gc, &cc->color);
+		}
+	}
+}
+
+void ghid_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	render_priv *priv = gport->render_priv;
+
+	switch (style) {
+	case Trace_Cap:
+	case Round_Cap:
+		gc->cap = GDK_CAP_ROUND;
+		gc->join = GDK_JOIN_ROUND;
+		break;
+	case Square_Cap:
+	case Beveled_Cap:
+		gc->cap = GDK_CAP_PROJECTING;
+		gc->join = GDK_JOIN_MITER;
+		break;
+	}
+	if (gc->gc)
+		gdk_gc_set_line_attributes(WHICH_GC(gc), Vz(gc->width), GDK_LINE_SOLID, (GdkCapStyle) gc->cap, (GdkJoinStyle) gc->join);
+}
+
+void ghid_set_line_width(hidGC gc, Coord width)
+{
+	render_priv *priv = gport->render_priv;
+
+	gc->width = width;
+	if (gc->gc)
+		gdk_gc_set_line_attributes(WHICH_GC(gc), Vz(gc->width), GDK_LINE_SOLID, (GdkCapStyle) gc->cap, (GdkJoinStyle) gc->join);
+}
+
+void ghid_set_draw_xor(hidGC gc, int xor_mask)
+{
+	gc->xor_mask = xor_mask;
+	if (!gc->gc)
+		return;
+	gdk_gc_set_function(gc->gc, xor_mask ? GDK_XOR : GDK_COPY);
+	ghid_set_color(gc, gc->colorname);
+}
+
+static int use_gc(hidGC gc)
+{
+	render_priv *priv = gport->render_priv;
+	GdkWindow *window = gtk_widget_get_window(gport->top_window);
+
+	if (gc->me_pointer != &ghid_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to GTK HID\n");
+		abort();
+	}
+
+	if (!gport->pixmap)
+		return 0;
+	if (!gc->gc) {
+		gc->gc = gdk_gc_new(window);
+		ghid_set_color(gc, gc->colorname);
+		ghid_set_line_width(gc, gc->width);
+		ghid_set_line_cap(gc, (EndCapStyle) gc->cap);
+		ghid_set_draw_xor(gc, gc->xor_mask);
+		gdk_gc_set_clip_origin(gc->gc, 0, 0);
+	}
+	if (gc->mask_seq != mask_seq) {
+		if (mask_seq)
+			gdk_gc_set_clip_mask(gc->gc, gport->mask);
+		else
+			set_clip(priv, gc->gc);
+		gc->mask_seq = mask_seq;
+	}
+	priv->u_gc = WHICH_GC(gc);
+	return 1;
+}
+
+void ghid_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	double dx1, dy1, dx2, dy2;
+	render_priv *priv = gport->render_priv;
+
+	dx1 = Vx((double) x1);
+	dy1 = Vy((double) y1);
+	dx2 = Vx((double) x2);
+	dy2 = Vy((double) y2);
+
+	if (!ClipLine(0, 0, gport->width, gport->height, &dx1, &dy1, &dx2, &dy2, gc->width / gport->view.coord_per_px))
+		return;
+
+	USE_GC(gc);
+	gdk_draw_line(gport->drawable, priv->u_gc, dx1, dy1, dx2, dy2);
+}
+
+void ghid_draw_arc(hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
+{
+	gint vrx, vry;
+	gint w, h, radius;
+	render_priv *priv = gport->render_priv;
+
+	w = gport->width * gport->view.coord_per_px;
+	h = gport->height * gport->view.coord_per_px;
+	radius = (xradius > yradius) ? xradius : yradius;
+	if (SIDE_X(cx) < gport->view.x0 - radius
+			|| SIDE_X(cx) > gport->view.x0 + w + radius
+			|| SIDE_Y(cy) < gport->view.y0 - radius || SIDE_Y(cy) > gport->view.y0 + h + radius)
+		return;
+
+	USE_GC(gc);
+	vrx = Vz(xradius);
+	vry = Vz(yradius);
+
+	if (conf_core.editor.view.flip_x) {
+		start_angle = 180 - start_angle;
+		delta_angle = -delta_angle;
+	}
+	if (conf_core.editor.view.flip_y) {
+		start_angle = -start_angle;
+		delta_angle = -delta_angle;
+	}
+	/* make sure we fall in the -180 to +180 range */
+	start_angle = NormalizeAngle(start_angle);
+	if (start_angle >= 180)
+		start_angle -= 360;
+
+	gdk_draw_arc(gport->drawable, priv->u_gc, 0,
+							 Vx(cx) - vrx, Vy(cy) - vry, vrx * 2, vry * 2, (start_angle + 180) * 64, delta_angle * 64);
+}
+
+void ghid_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	gint w, h, lw;
+	render_priv *priv = gport->render_priv;
+
+	lw = gc->width;
+	w = gport->width * gport->view.coord_per_px;
+	h = gport->height * gport->view.coord_per_px;
+
+	if ((SIDE_X(x1) < gport->view.x0 - lw && SIDE_X(x2) < gport->view.x0 - lw)
+			|| (SIDE_X(x1) > gport->view.x0 + w + lw && SIDE_X(x2) > gport->view.x0 + w + lw)
+			|| (SIDE_Y(y1) < gport->view.y0 - lw && SIDE_Y(y2) < gport->view.y0 - lw)
+			|| (SIDE_Y(y1) > gport->view.y0 + h + lw && SIDE_Y(y2) > gport->view.y0 + h + lw))
+		return;
+
+	x1 = Vx(x1);
+	y1 = Vy(y1);
+	x2 = Vx(x2);
+	y2 = Vy(y2);
+
+	if (x1 > x2) {
+		gint xt = x1;
+		x1 = x2;
+		x2 = xt;
+	}
+	if (y1 > y2) {
+		gint yt = y1;
+		y1 = y2;
+		y2 = yt;
+	}
+
+	USE_GC(gc);
+	gdk_draw_rectangle(gport->drawable, priv->u_gc, FALSE, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+}
+
+
+void ghid_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	gint w, h, vr;
+	render_priv *priv = gport->render_priv;
+
+	w = gport->width * gport->view.coord_per_px;
+	h = gport->height * gport->view.coord_per_px;
+	if (SIDE_X(cx) < gport->view.x0 - radius
+			|| SIDE_X(cx) > gport->view.x0 + w + radius
+			|| SIDE_Y(cy) < gport->view.y0 - radius || SIDE_Y(cy) > gport->view.y0 + h + radius)
+		return;
+
+	USE_GC(gc);
+	vr = Vz(radius);
+	gdk_draw_arc(gport->drawable, priv->u_gc, TRUE, Vx(cx) - vr, Vy(cy) - vr, vr * 2, vr * 2, 0, 360 * 64);
+}
+
+void ghid_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	static GdkPoint *points = 0;
+	static int npoints = 0;
+	int i;
+	render_priv *priv = gport->render_priv;
+	USE_GC(gc);
+
+	if (npoints < n_coords) {
+		npoints = n_coords + 1;
+		points = (GdkPoint *) realloc(points, npoints * sizeof(GdkPoint));
+	}
+	for (i = 0; i < n_coords; i++) {
+		points[i].x = Vx(x[i]);
+		points[i].y = Vy(y[i]);
+	}
+	gdk_draw_polygon(gport->drawable, priv->u_gc, 1, points, n_coords);
+}
+
+void ghid_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	gint w, h, lw, xx, yy;
+	render_priv *priv = gport->render_priv;
+
+	lw = gc->width;
+	w = gport->width * gport->view.coord_per_px;
+	h = gport->height * gport->view.coord_per_px;
+
+	if ((SIDE_X(x1) < gport->view.x0 - lw && SIDE_X(x2) < gport->view.x0 - lw)
+			|| (SIDE_X(x1) > gport->view.x0 + w + lw && SIDE_X(x2) > gport->view.x0 + w + lw)
+			|| (SIDE_Y(y1) < gport->view.y0 - lw && SIDE_Y(y2) < gport->view.y0 - lw)
+			|| (SIDE_Y(y1) > gport->view.y0 + h + lw && SIDE_Y(y2) > gport->view.y0 + h + lw))
+		return;
+
+	x1 = Vx(x1);
+	y1 = Vy(y1);
+	x2 = Vx(x2);
+	y2 = Vy(y2);
+	if (x2 < x1) {
+		xx = x1;
+		x1 = x2;
+		x2 = xx;
+	}
+	if (y2 < y1) {
+		yy = y1;
+		y1 = y2;
+		y2 = yy;
+	}
+	USE_GC(gc);
+	gdk_draw_rectangle(gport->drawable, priv->u_gc, TRUE, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+}
+
+static void redraw_region(GdkRectangle * rect)
+{
+	int eleft, eright, etop, ebottom;
+	BoxType region;
+	render_priv *priv = gport->render_priv;
+
+	if (!gport->pixmap)
+		return;
+
+	if (rect != NULL) {
+		priv->clip_rect = *rect;
+		priv->clip = pcb_true;
+	}
+	else {
+		priv->clip_rect.x = 0;
+		priv->clip_rect.y = 0;
+		priv->clip_rect.width = gport->width;
+		priv->clip_rect.height = gport->height;
+		priv->clip = pcb_false;
+	}
+
+	set_clip(priv, priv->bg_gc);
+	set_clip(priv, priv->offlimits_gc);
+	set_clip(priv, priv->mask_gc);
+	set_clip(priv, priv->grid_gc);
+
+	region.X1 = MIN(Px(priv->clip_rect.x), Px(priv->clip_rect.x + priv->clip_rect.width + 1));
+	region.Y1 = MIN(Py(priv->clip_rect.y), Py(priv->clip_rect.y + priv->clip_rect.height + 1));
+	region.X2 = MAX(Px(priv->clip_rect.x), Px(priv->clip_rect.x + priv->clip_rect.width + 1));
+	region.Y2 = MAX(Py(priv->clip_rect.y), Py(priv->clip_rect.y + priv->clip_rect.height + 1));
+
+	region.X1 = MAX(0, MIN(PCB->MaxWidth, region.X1));
+	region.X2 = MAX(0, MIN(PCB->MaxWidth, region.X2));
+	region.Y1 = MAX(0, MIN(PCB->MaxHeight, region.Y1));
+	region.Y2 = MAX(0, MIN(PCB->MaxHeight, region.Y2));
+
+	eleft = Vx(0);
+	eright = Vx(PCB->MaxWidth);
+	etop = Vy(0);
+	ebottom = Vy(PCB->MaxHeight);
+	if (eleft > eright) {
+		int tmp = eleft;
+		eleft = eright;
+		eright = tmp;
+	}
+	if (etop > ebottom) {
+		int tmp = etop;
+		etop = ebottom;
+		ebottom = tmp;
+	}
+
+	if (eleft > 0)
+		gdk_draw_rectangle(gport->drawable, priv->offlimits_gc, 1, 0, 0, eleft, gport->height);
+	else
+		eleft = 0;
+	if (eright < gport->width)
+		gdk_draw_rectangle(gport->drawable, priv->offlimits_gc, 1, eright, 0, gport->width - eright, gport->height);
+	else
+		eright = gport->width;
+	if (etop > 0)
+		gdk_draw_rectangle(gport->drawable, priv->offlimits_gc, 1, eleft, 0, eright - eleft + 1, etop);
+	else
+		etop = 0;
+	if (ebottom < gport->height)
+		gdk_draw_rectangle(gport->drawable, priv->offlimits_gc, 1, eleft, ebottom, eright - eleft + 1, gport->height - ebottom);
+	else
+		ebottom = gport->height;
+
+	gdk_draw_rectangle(gport->drawable, priv->bg_gc, 1, eleft, etop, eright - eleft + 1, ebottom - etop + 1);
+
+	ghid_draw_bg_image();
+
+	hid_expose_callback(&ghid_hid, &region, 0);
+	ghid_draw_grid();
+
+	/* In some cases we are called with the crosshair still off */
+	if (priv->attached_invalidate_depth == 0)
+		DrawAttached();
+
+	/* In some cases we are called with the mark still off */
+	if (priv->mark_invalidate_depth == 0)
+		DrawMark();
+
+	draw_lead_user(priv);
+
+	priv->clip = pcb_false;
+
+	/* Rest the clip for bg_gc, as it is used outside this function */
+	gdk_gc_set_clip_mask(priv->bg_gc, NULL);
+}
+
+void ghid_invalidate_lr(int left, int right, int top, int bottom)
+{
+	int dleft, dright, dtop, dbottom;
+	int minx, maxx, miny, maxy;
+	GdkRectangle rect;
+
+	dleft = Vx(left);
+	dright = Vx(right);
+	dtop = Vy(top);
+	dbottom = Vy(bottom);
+
+	minx = MIN(dleft, dright);
+	maxx = MAX(dleft, dright);
+	miny = MIN(dtop, dbottom);
+	maxy = MAX(dtop, dbottom);
+
+	rect.x = minx;
+	rect.y = miny;
+	rect.width = maxx - minx;
+	rect.height = maxy - miny;
+
+	redraw_region(&rect);
+	ghid_screen_update();
+}
+
+
+void ghid_invalidate_all()
+{
+	redraw_region(NULL);
+	ghid_screen_update();
+}
+
+void ghid_notify_crosshair_change(pcb_bool changes_complete)
+{
+	render_priv *priv = gport->render_priv;
+
+	/* We sometimes get called before the GUI is up */
+	if (gport->drawing_area == NULL)
+		return;
+
+	if (changes_complete)
+		priv->attached_invalidate_depth--;
+
+	if (priv->attached_invalidate_depth < 0) {
+		priv->attached_invalidate_depth = 0;
+		/* A mismatch of changes_complete == pcb_false and == pcb_true notifications
+		 * is not expected to occur, but we will try to handle it gracefully.
+		 * As we know the crosshair will have been shown already, we must
+		 * repaint the entire view to be sure not to leave an artaefact.
+		 */
+		ghid_invalidate_all();
+		return;
+	}
+
+	if (priv->attached_invalidate_depth == 0)
+		DrawAttached();
+
+	if (!changes_complete) {
+		priv->attached_invalidate_depth++;
+	}
+	else if (gport->drawing_area != NULL) {
+		/* Queue a GTK expose when changes are complete */
+		ghid_draw_area_update(gport, NULL);
+	}
+}
+
+void ghid_notify_mark_change(pcb_bool changes_complete)
+{
+	render_priv *priv = gport->render_priv;
+
+	/* We sometimes get called before the GUI is up */
+	if (gport->drawing_area == NULL)
+		return;
+
+	if (changes_complete)
+		priv->mark_invalidate_depth--;
+
+	if (priv->mark_invalidate_depth < 0) {
+		priv->mark_invalidate_depth = 0;
+		/* A mismatch of changes_complete == pcb_false and == pcb_true notifications
+		 * is not expected to occur, but we will try to handle it gracefully.
+		 * As we know the mark will have been shown already, we must
+		 * repaint the entire view to be sure not to leave an artaefact.
+		 */
+		ghid_invalidate_all();
+		return;
+	}
+
+	if (priv->mark_invalidate_depth == 0)
+		DrawMark();
+
+	if (!changes_complete) {
+		priv->mark_invalidate_depth++;
+	}
+	else if (gport->drawing_area != NULL) {
+		/* Queue a GTK expose when changes are complete */
+		ghid_draw_area_update(gport, NULL);
+	}
+}
+
+static void draw_right_cross(GdkGC * xor_gc, gint x, gint y)
+{
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+
+	gdk_draw_line(window, xor_gc, x, 0, x, gport->height);
+	gdk_draw_line(window, xor_gc, 0, y, gport->width, y);
+}
+
+static void draw_slanted_cross(GdkGC * xor_gc, gint x, gint y)
+{
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+	gint x0, y0, x1, y1;
+
+	x0 = x + (gport->height - y);
+	x0 = MAX(0, MIN(x0, gport->width));
+	x1 = x - y;
+	x1 = MAX(0, MIN(x1, gport->width));
+	y0 = y + (gport->width - x);
+	y0 = MAX(0, MIN(y0, gport->height));
+	y1 = y - x;
+	y1 = MAX(0, MIN(y1, gport->height));
+	gdk_draw_line(window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x - (gport->height - y);
+	x0 = MAX(0, MIN(x0, gport->width));
+	x1 = x + y;
+	x1 = MAX(0, MIN(x1, gport->width));
+	y0 = y + x;
+	y0 = MAX(0, MIN(y0, gport->height));
+	y1 = y - (gport->width - x);
+	y1 = MAX(0, MIN(y1, gport->height));
+	gdk_draw_line(window, xor_gc, x0, y0, x1, y1);
+}
+
+static void draw_dozen_cross(GdkGC * xor_gc, gint x, gint y)
+{
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+	gint x0, y0, x1, y1;
+	gdouble tan60 = sqrt(3);
+
+	x0 = x + (gport->height - y) / tan60;
+	x0 = MAX(0, MIN(x0, gport->width));
+	x1 = x - y / tan60;
+	x1 = MAX(0, MIN(x1, gport->width));
+	y0 = y + (gport->width - x) * tan60;
+	y0 = MAX(0, MIN(y0, gport->height));
+	y1 = y - x * tan60;
+	y1 = MAX(0, MIN(y1, gport->height));
+	gdk_draw_line(window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x + (gport->height - y) * tan60;
+	x0 = MAX(0, MIN(x0, gport->width));
+	x1 = x - y * tan60;
+	x1 = MAX(0, MIN(x1, gport->width));
+	y0 = y + (gport->width - x) / tan60;
+	y0 = MAX(0, MIN(y0, gport->height));
+	y1 = y - x / tan60;
+	y1 = MAX(0, MIN(y1, gport->height));
+	gdk_draw_line(window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x - (gport->height - y) / tan60;
+	x0 = MAX(0, MIN(x0, gport->width));
+	x1 = x + y / tan60;
+	x1 = MAX(0, MIN(x1, gport->width));
+	y0 = y + x * tan60;
+	y0 = MAX(0, MIN(y0, gport->height));
+	y1 = y - (gport->width - x) * tan60;
+	y1 = MAX(0, MIN(y1, gport->height));
+	gdk_draw_line(window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x - (gport->height - y) * tan60;
+	x0 = MAX(0, MIN(x0, gport->width));
+	x1 = x + y * tan60;
+	x1 = MAX(0, MIN(x1, gport->width));
+	y0 = y + x / tan60;
+	y0 = MAX(0, MIN(y0, gport->height));
+	y1 = y - (gport->width - x) / tan60;
+	y1 = MAX(0, MIN(y1, gport->height));
+	gdk_draw_line(window, xor_gc, x0, y0, x1, y1);
+}
+
+static void draw_crosshair(GdkGC * xor_gc, gint x, gint y)
+{
+	static enum crosshair_shape prev = Basic_Crosshair_Shape;
+
+	draw_right_cross(xor_gc, x, y);
+	if (prev == Union_Jack_Crosshair_Shape)
+		draw_slanted_cross(xor_gc, x, y);
+	if (prev == Dozen_Crosshair_Shape)
+		draw_dozen_cross(xor_gc, x, y);
+	prev = Crosshair.shape;
+}
+
+static void show_crosshair(gboolean paint_new_location)
+{
+	render_priv *priv = gport->render_priv;
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+	GtkStyle *style = gtk_widget_get_style(gport->drawing_area);
+	gint x, y;
+	static gint x_prev = -1, y_prev = -1;
+	static GdkGC *xor_gc;
+	static GdkColor cross_color;
+
+	if (gport->crosshair_x < 0 || ghidgui->creating || !gport->has_entered)
+		return;
+
+	if (!xor_gc) {
+		xor_gc = gdk_gc_new(window);
+		gdk_gc_copy(xor_gc, style->white_gc);
+		gdk_gc_set_function(xor_gc, GDK_XOR);
+		gdk_gc_set_clip_origin(xor_gc, 0, 0);
+		set_clip(priv, xor_gc);
+		/* FIXME: when CrossColor changed from config */
+		ghid_map_color_string(conf_core.appearance.color.cross, &cross_color);
+	}
+	x = DRAW_X(gport->crosshair_x);
+	y = DRAW_Y(gport->crosshair_y);
+
+	gdk_gc_set_foreground(xor_gc, &cross_color);
+
+	if (x_prev >= 0 && !paint_new_location)
+		draw_crosshair(xor_gc, x_prev, y_prev);
+
+	if (x >= 0 && paint_new_location) {
+		draw_crosshair(xor_gc, x, y);
+		x_prev = x;
+		y_prev = y;
+	}
+	else
+		x_prev = y_prev = -1;
+}
+
+void ghid_init_renderer(int *argc, char ***argv, GHidPort * port)
+{
+	/* Init any GC's required */
+	port->render_priv = g_new0(render_priv, 1);
+}
+
+void ghid_shutdown_renderer(GHidPort * port)
+{
+	ghid_cancel_lead_user();
+	g_free(port->render_priv);
+	port->render_priv = NULL;
+}
+
+void ghid_init_drawing_widget(GtkWidget * widget, GHidPort * port)
+{
+}
+
+void ghid_drawing_area_configure_hook(GHidPort * port)
+{
+	static int done_once = 0;
+	render_priv *priv = port->render_priv;
+
+	if (!done_once) {
+		priv->bg_gc = gdk_gc_new(port->drawable);
+		gdk_gc_set_foreground(priv->bg_gc, &port->bg_color);
+		gdk_gc_set_clip_origin(priv->bg_gc, 0, 0);
+
+		priv->offlimits_gc = gdk_gc_new(port->drawable);
+		gdk_gc_set_foreground(priv->offlimits_gc, &port->offlimits_color);
+		gdk_gc_set_clip_origin(priv->offlimits_gc, 0, 0);
+		done_once = 1;
+	}
+
+	if (port->mask) {
+		gdk_pixmap_unref(port->mask);
+		port->mask = gdk_pixmap_new(0, port->width, port->height, 1);
+	}
+}
+
+void ghid_screen_update(void)
+{
+	render_priv *priv = gport->render_priv;
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+
+	if (gport->pixmap == NULL)
+		return;
+
+	gdk_draw_drawable(window, priv->bg_gc, gport->pixmap, 0, 0, 0, 0, gport->width, gport->height);
+	show_crosshair(TRUE);
+}
+
+gboolean ghid_drawing_area_expose_cb(GtkWidget * widget, GdkEventExpose * ev, GHidPort * port)
+{
+	render_priv *priv = port->render_priv;
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+
+	gdk_draw_drawable(window, priv->bg_gc, port->pixmap,
+										ev->area.x, ev->area.y, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+	show_crosshair(TRUE);
+	return FALSE;
+}
+
+void ghid_port_drawing_realize_cb(GtkWidget * widget, gpointer data)
+{
+}
+
+gboolean ghid_pinout_preview_expose(GtkWidget * widget, GdkEventExpose * ev)
+{
+	GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW(widget);
+	GdkWindow *window = gtk_widget_get_window(widget);
+	GdkDrawable *save_drawable;
+	GtkAllocation allocation;
+	view_data save_view;
+	int save_width, save_height;
+	double xz, yz;
+	render_priv *priv = gport->render_priv;
+
+	/* Setup drawable and zoom factor for drawing routines
+	 */
+	save_drawable = gport->drawable;
+	save_view = gport->view;
+	save_width = gport->width;
+	save_height = gport->height;
+
+	gtk_widget_get_allocation(widget, &allocation);
+	xz = (double) pinout->x_max / allocation.width;
+	yz = (double) pinout->y_max / allocation.height;
+	if (xz > yz)
+		gport->view.coord_per_px = xz;
+	else
+		gport->view.coord_per_px = yz;
+
+	gport->drawable = window;
+	gport->width = allocation.width;
+	gport->height = allocation.height;
+	gport->view.width = allocation.width * gport->view.coord_per_px;
+	gport->view.height = allocation.height * gport->view.coord_per_px;
+	gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
+	gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
+
+	/* clear background */
+	gdk_draw_rectangle(window, priv->bg_gc, TRUE, 0, 0, allocation.width, allocation.height);
+
+	/* call the drawing routine */
+	hid_expose_callback(&ghid_hid, NULL, &pinout->element);
+
+	gport->drawable = save_drawable;
+	gport->view = save_view;
+	gport->width = save_width;
+	gport->height = save_height;
+
+	return FALSE;
+}
+
+GdkPixmap *ghid_render_pixmap(int cx, int cy, double zoom, int width, int height, int depth)
+{
+	GdkPixmap *pixmap;
+	GdkDrawable *save_drawable;
+	view_data save_view;
+	int save_width, save_height;
+	BoxType region;
+	render_priv *priv = gport->render_priv;
+
+	save_drawable = gport->drawable;
+	save_view = gport->view;
+	save_width = gport->width;
+	save_height = gport->height;
+
+	pixmap = gdk_pixmap_new(NULL, width, height, depth);
+
+	/* Setup drawable and zoom factor for drawing routines
+	 */
+
+	gport->drawable = pixmap;
+	gport->view.coord_per_px = zoom;
+	gport->width = width;
+	gport->height = height;
+	gport->view.width = width * gport->view.coord_per_px;
+	gport->view.height = height * gport->view.coord_per_px;
+	gport->view.x0 = conf_core.editor.view.flip_x ? PCB->MaxWidth - cx : cx;
+	gport->view.x0 -= gport->view.height / 2;
+	gport->view.y0 = conf_core.editor.view.flip_y ? PCB->MaxHeight - cy : cy;
+	gport->view.y0 -= gport->view.width / 2;
+
+	/* clear background */
+	gdk_draw_rectangle(pixmap, priv->bg_gc, TRUE, 0, 0, width, height);
+
+	/* call the drawing routine */
+	region.X1 = MIN(Px(0), Px(gport->width + 1));
+	region.Y1 = MIN(Py(0), Py(gport->height + 1));
+	region.X2 = MAX(Px(0), Px(gport->width + 1));
+	region.Y2 = MAX(Py(0), Py(gport->height + 1));
+
+	region.X1 = MAX(0, MIN(PCB->MaxWidth, region.X1));
+	region.X2 = MAX(0, MIN(PCB->MaxWidth, region.X2));
+	region.Y1 = MAX(0, MIN(PCB->MaxHeight, region.Y1));
+	region.Y2 = MAX(0, MIN(PCB->MaxHeight, region.Y2));
+
+	hid_expose_callback(&ghid_hid, &region, NULL);
+
+	gport->drawable = save_drawable;
+	gport->view = save_view;
+	gport->width = save_width;
+	gport->height = save_height;
+
+	return pixmap;
+}
+
+HID *ghid_request_debug_draw(void)
+{
+	/* No special setup requirements, drawing goes into
+	 * the backing pixmap. */
+	return &ghid_hid;
+}
+
+void ghid_flush_debug_draw(void)
+{
+	ghid_screen_update();
+	gdk_flush();
+}
+
+void ghid_finish_debug_draw(void)
+{
+	ghid_flush_debug_draw();
+	/* No special tear down requirements
+	 */
+}
+
+pcb_bool ghid_event_to_pcb_coords(int event_x, int event_y, Coord * pcb_x, Coord * pcb_y)
+{
+	*pcb_x = EVENT_TO_PCB_X(event_x);
+	*pcb_y = EVENT_TO_PCB_Y(event_y);
+
+	return pcb_true;
+}
+
+pcb_bool ghid_pcb_to_event_coords(Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
+{
+	*event_x = DRAW_X(pcb_x);
+	*event_y = DRAW_Y(pcb_y);
+
+	return pcb_true;
+}
+
+
+#define LEAD_USER_WIDTH           0.2	/* millimeters */
+#define LEAD_USER_PERIOD          (1000 / 5)	/* 5fps (in ms) */
+#define LEAD_USER_VELOCITY        3.	/* millimeters per second */
+#define LEAD_USER_ARC_COUNT       3
+#define LEAD_USER_ARC_SEPARATION  3.	/* millimeters */
+#define LEAD_USER_INITIAL_RADIUS  10.	/* millimetres */
+#define LEAD_USER_COLOR_R         1.
+#define LEAD_USER_COLOR_G         1.
+#define LEAD_USER_COLOR_B         0.
+
+static void draw_lead_user(render_priv * priv)
+{
+	GdkWindow *window = gtk_widget_get_window(gport->drawing_area);
+	GtkStyle *style = gtk_widget_get_style(gport->drawing_area);
+	int i;
+	Coord radius = priv->lead_user_radius;
+	Coord width = PCB_MM_TO_COORD(LEAD_USER_WIDTH);
+	Coord separation = PCB_MM_TO_COORD(LEAD_USER_ARC_SEPARATION);
+	static GdkGC *lead_gc = NULL;
+	GdkColor lead_color;
+
+	if (!priv->lead_user)
+		return;
+
+	if (lead_gc == NULL) {
+		lead_gc = gdk_gc_new(window);
+		gdk_gc_copy(lead_gc, style->white_gc);
+		gdk_gc_set_function(lead_gc, GDK_XOR);
+		gdk_gc_set_clip_origin(lead_gc, 0, 0);
+		lead_color.pixel = 0;
+		lead_color.red = (int) (65535. * LEAD_USER_COLOR_R);
+		lead_color.green = (int) (65535. * LEAD_USER_COLOR_G);
+		lead_color.blue = (int) (65535. * LEAD_USER_COLOR_B);
+		gdk_color_alloc(gport->colormap, &lead_color);
+		gdk_gc_set_foreground(lead_gc, &lead_color);
+	}
+
+	set_clip(priv, lead_gc);
+	gdk_gc_set_line_attributes(lead_gc, Vz(width), GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
+
+	/* arcs at the approrpriate radii */
+
+	for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation) {
+		if (radius < width)
+			radius += PCB_MM_TO_COORD(LEAD_USER_INITIAL_RADIUS);
+
+		/* Draw an arc at radius */
+		gdk_draw_arc(gport->drawable, lead_gc, FALSE,
+								 Vx(priv->lead_user_x - radius), Vy(priv->lead_user_y - radius), Vz(2. * radius), Vz(2. * radius), 0, 360 * 64);
+	}
+}
+
+gboolean lead_user_cb(gpointer data)
+{
+	render_priv *priv = data;
+	Coord step;
+	double elapsed_time;
+
+	/* Queue a redraw */
+	ghid_invalidate_all();
+
+	/* Update radius */
+	elapsed_time = g_timer_elapsed(priv->lead_user_timer, NULL);
+	g_timer_start(priv->lead_user_timer);
+
+	step = PCB_MM_TO_COORD(LEAD_USER_VELOCITY * elapsed_time);
+	if (priv->lead_user_radius > step)
+		priv->lead_user_radius -= step;
+	else
+		priv->lead_user_radius = PCB_MM_TO_COORD(LEAD_USER_INITIAL_RADIUS);
+
+	return TRUE;
+}
+
+void ghid_lead_user_to_location(Coord x, Coord y)
+{
+	render_priv *priv = gport->render_priv;
+
+	ghid_cancel_lead_user();
+
+	priv->lead_user = pcb_true;
+	priv->lead_user_x = x;
+	priv->lead_user_y = y;
+	priv->lead_user_radius = PCB_MM_TO_COORD(LEAD_USER_INITIAL_RADIUS);
+	priv->lead_user_timeout = g_timeout_add(LEAD_USER_PERIOD, lead_user_cb, priv);
+	priv->lead_user_timer = g_timer_new();
+}
+
+void ghid_cancel_lead_user(void)
+{
+	render_priv *priv = gport->render_priv;
+
+	if (priv->lead_user_timeout)
+		g_source_remove(priv->lead_user_timeout);
+
+	if (priv->lead_user_timer)
+		g_timer_destroy(priv->lead_user_timer);
+
+	if (priv->lead_user)
+		ghid_invalidate_all();
+
+	priv->lead_user_timeout = 0;
+	priv->lead_user_timer = NULL;
+	priv->lead_user = pcb_false;
+}
diff --git a/src_plugins/hid_gtk/gtkhid-gl-config.h b/src_plugins/hid_gtk/gtkhid-gl-config.h
new file mode 100644
index 0000000..4b52cf9
--- /dev/null
+++ b/src_plugins/hid_gtk/gtkhid-gl-config.h
@@ -0,0 +1,17 @@
+#warning scconfig TODO: these need to be detectet by scconfig
+/**** GL ****/
+
+/* Define to 1 if GL support is to be compiled in */
+/* #undef ENABLE_GL */
+
+/* Define to 1 if you have the <GL/glu.h> header file. */
+/* #undef HAVE_GL_GLU_H */
+
+/* Define to 1 if you have the <GL/gl.h> header file. */
+/* #undef HAVE_GL_GL_H */
+
+/* Define to 1 if you have the <OpenGL/glu.h> header file. */
+/* #undef HAVE_OPENGL_GLU_H */
+
+/* Define to 1 if you have the <OpenGL/gl.h> header file. */
+/* #undef HAVE_OPENGL_GL_H */
diff --git a/src_plugins/hid_gtk/gtkhid-gl.c b/src_plugins/hid_gtk/gtkhid-gl.c
new file mode 100644
index 0000000..e8a12d9
--- /dev/null
+++ b/src_plugins/hid_gtk/gtkhid-gl.c
@@ -0,0 +1,1250 @@
+#include "config.h"
+
+#include <stdio.h>
+
+#include "gtkhid-gl-config.h"
+#include "crosshair.h"
+#include "clip.h"
+#include "gui.h"
+#include "gui-pinout-preview.h"
+
+/* The Linux OpenGL ABI 1.0 spec requires that we define
+ * GL_GLEXT_PROTOTYPES before including gl.h or glx.h for extensions
+ * in order to get prototypes:
+ *   http://www.opengl.org/registry/ABI/
+ */
+
+#define GL_GLEXT_PROTOTYPES 1
+#ifdef HAVE_OPENGL_GL_H
+#include <OpenGL/gl.h>
+#else
+#include <GL/gl.h>
+#endif
+
+#include <gtk/gtkgl.h>
+#include "hid/common/hidgl.h"
+#include "hid_draw_helpers.h"
+
+extern HID ghid_hid;
+
+static hidGC current_gc = NULL;
+
+/* Sets gport->u_gc to the "right" GC to use (wrt mask or window)
+*/
+#define USE_GC(gc) if (!use_gc(gc)) return
+
+static int cur_mask = -1;
+
+typedef struct render_priv {
+	GdkGLConfig *glconfig;
+	bool trans_lines;
+	bool in_context;
+	int subcomposite_stencil_bit;
+	char *current_colorname;
+	double current_alpha_mult;
+
+	/* Feature for leading the user to a particular location */
+	guint lead_user_timeout;
+	GTimer *lead_user_timer;
+	bool lead_user;
+	Coord lead_user_radius;
+	Coord lead_user_x;
+	Coord lead_user_y;
+
+} render_priv;
+
+
+typedef struct hid_gc_struct {
+	HID *me_pointer;
+
+	const char *colorname;
+	double alpha_mult;
+	Coord width;
+	gint cap, join;
+	gchar xor;
+} hid_gc_struct;
+
+
+static void draw_lead_user(render_priv * priv);
+
+
+static void start_subcomposite(void)
+{
+	render_priv *priv = gport->render_priv;
+	int stencil_bit;
+
+	/* Flush out any existing geoemtry to be rendered */
+	hidgl_flush_triangles(&buffer);
+
+	glEnable(GL_STENCIL_TEST);		/* Enable Stencil test */
+	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);	/* Stencil pass => replace stencil value (with 1) */
+
+	stencil_bit = hidgl_assign_clear_stencil_bit();	/* Get a new (clean) bitplane to stencil with */
+	glStencilMask(stencil_bit);		/* Only write to our subcompositing stencil bitplane */
+	glStencilFunc(GL_GREATER, stencil_bit, stencil_bit);	/* Pass stencil test if our assigned bit is clear */
+
+	priv->subcomposite_stencil_bit = stencil_bit;
+}
+
+static void end_subcomposite(void)
+{
+	render_priv *priv = gport->render_priv;
+
+	/* Flush out any existing geoemtry to be rendered */
+	hidgl_flush_triangles(&buffer);
+
+	hidgl_return_stencil_bit(priv->subcomposite_stencil_bit);	/* Relinquish any bitplane we previously used */
+
+	glStencilMask(0);
+	glStencilFunc(GL_ALWAYS, 0, 0);	/* Always pass stencil test */
+	glDisable(GL_STENCIL_TEST);		/* Disable Stencil test */
+
+	priv->subcomposite_stencil_bit = 0;
+}
+
+
+int ghid_set_layer(const char *name, int group, int empty)
+{
+	render_priv *priv = gport->render_priv;
+	int idx = group;
+	if (idx >= 0 && idx < max_group) {
+		int n = PCB->LayerGroups.Number[group];
+		for (idx = 0; idx < n - 1; idx++) {
+			int ni = PCB->LayerGroups.Entries[group][idx];
+			if (ni >= 0 && ni < max_copper_layer + 2 && PCB->Data->Layer[ni].On)
+				break;
+		}
+		idx = PCB->LayerGroups.Entries[group][idx];
+	}
+
+	end_subcomposite();
+	start_subcomposite();
+
+	if (idx >= 0 && idx < max_copper_layer + 2) {
+		priv->trans_lines = pcb_true;
+		return PCB->Data->Layer[idx].On;
+	}
+	if (idx < 0) {
+		switch (SL_TYPE(idx)) {
+		case SL_INVISIBLE:
+			return PCB->InvisibleObjectsOn;
+		case SL_MASK:
+			if (SL_MYSIDE(idx))
+				return TEST_FLAG(SHOWMASKFLAG, PCB);
+			return 0;
+		case SL_SILK:
+			priv->trans_lines = pcb_true;
+			if (SL_MYSIDE(idx))
+				return PCB->ElementOn;
+			return 0;
+		case SL_ASSY:
+			return 0;
+		case SL_PDRILL:
+		case SL_UDRILL:
+			return 1;
+		case SL_RATS:
+			if (PCB->RatOn)
+				priv->trans_lines = pcb_true;
+			return PCB->RatOn;
+		}
+	}
+	return 0;
+}
+
+static void ghid_end_layer(void)
+{
+	end_subcomposite();
+}
+
+void ghid_destroy_gc(hidGC gc)
+{
+	g_free(gc);
+}
+
+hidGC ghid_make_gc(void)
+{
+	hidGC rv;
+
+	rv = g_new0(hid_gc_struct, 1);
+	rv->me_pointer = &ghid_hid;
+	rv->colorname = conf_core.appearance.color.background;
+	rv->alpha_mult = 1.0;
+	return rv;
+}
+
+static void ghid_draw_grid(BoxTypePtr drawn_area)
+{
+	if (Vz(PCB->Grid) < MIN_GRID_DISTANCE)
+		return;
+
+	if (gdk_color_parse(conf_core.appearance.color.grid, &gport->grid_color)) {
+		gport->grid_color.red ^= gport->bg_color.red;
+		gport->grid_color.green ^= gport->bg_color.green;
+		gport->grid_color.blue ^= gport->bg_color.blue;
+	}
+
+	glEnable(GL_COLOR_LOGIC_OP);
+	glLogicOp(GL_XOR);
+
+	glColor3f(gport->grid_color.red / 65535., gport->grid_color.green / 65535., gport->grid_color.blue / 65535.);
+
+#error this does not draw the local grid and ignores other new grid options
+	hidgl_draw_grid(drawn_area);
+
+	glDisable(GL_COLOR_LOGIC_OP);
+}
+
+static void ghid_draw_bg_image(void)
+{
+	static GLuint texture_handle = 0;
+
+	if (!ghidgui->bg_pixbuf)
+		return;
+
+	if (texture_handle == 0) {
+		int width = gdk_pixbuf_get_width(ghidgui->bg_pixbuf);
+		int height = gdk_pixbuf_get_height(ghidgui->bg_pixbuf);
+		int rowstride = gdk_pixbuf_get_rowstride(ghidgui->bg_pixbuf);
+		int bits_per_sample = gdk_pixbuf_get_bits_per_sample(ghidgui->bg_pixbuf);
+		int n_channels = gdk_pixbuf_get_n_channels(ghidgui->bg_pixbuf);
+		unsigned char *pixels = gdk_pixbuf_get_pixels(ghidgui->bg_pixbuf);
+
+		g_warn_if_fail(bits_per_sample == 8);
+		g_warn_if_fail(rowstride == width * n_channels);
+
+		glGenTextures(1, &texture_handle);
+		glBindTexture(GL_TEXTURE_2D, texture_handle);
+
+		/* XXX: We should proabbly determine what the maxmimum texture supported is,
+		 *      and if our image is larger, shrink it down using GDK pixbuf routines
+		 *      rather than having it fail below.
+		 */
+
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, (n_channels == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, pixels);
+	}
+
+	glBindTexture(GL_TEXTURE_2D, texture_handle);
+
+	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+	glEnable(GL_TEXTURE_2D);
+
+	/* Render a quad with the background as a texture */
+
+	glBegin(GL_QUADS);
+	glTexCoord2d(0., 0.);
+	glVertex3i(0, 0, 0);
+	glTexCoord2d(1., 0.);
+	glVertex3i(PCB->MaxWidth, 0, 0);
+	glTexCoord2d(1., 1.);
+	glVertex3i(PCB->MaxWidth, PCB->MaxHeight, 0);
+	glTexCoord2d(0., 1.);
+	glVertex3i(0, PCB->MaxHeight, 0);
+	glEnd();
+
+	glDisable(GL_TEXTURE_2D);
+}
+
+void ghid_use_mask(int use_it)
+{
+	static int stencil_bit = 0;
+
+	if (use_it == cur_mask)
+		return;
+
+	/* Flush out any existing geoemtry to be rendered */
+	hidgl_flush_triangles(&buffer);
+
+	switch (use_it) {
+	case HID_MASK_BEFORE:
+		/* The HID asks not to receive this mask type, so warn if we get it */
+		g_return_if_reached();
+
+	case HID_MASK_CLEAR:
+		/* Write '1' to the stencil buffer where the solder-mask should not be drawn. */
+		glColorMask(0, 0, 0, 0);		/* Disable writting in color buffer */
+		glEnable(GL_STENCIL_TEST);	/* Enable Stencil test */
+		stencil_bit = hidgl_assign_clear_stencil_bit();	/* Get a new (clean) bitplane to stencil with */
+		glStencilFunc(GL_ALWAYS, stencil_bit, stencil_bit);	/* Always pass stencil test, write stencil_bit */
+		glStencilMask(stencil_bit);	/* Only write to our subcompositing stencil bitplane */
+		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);	/* Stencil pass => replace stencil value (with 1) */
+		break;
+
+	case HID_MASK_AFTER:
+		/* Drawing operations as masked to areas where the stencil buffer is '0' */
+		glColorMask(1, 1, 1, 1);		/* Enable drawing of r, g, b & a */
+		glStencilFunc(GL_GEQUAL, 0, stencil_bit);	/* Draw only where our bit of the stencil buffer is clear */
+		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);	/* Stencil buffer read only */
+		break;
+
+	case HID_MASK_OFF:
+		/* Disable stenciling */
+		hidgl_return_stencil_bit(stencil_bit);	/* Relinquish any bitplane we previously used */
+		glDisable(GL_STENCIL_TEST);	/* Disable Stencil test */
+		break;
+	}
+	cur_mask = use_it;
+}
+
+
+	/* Config helper functions for when the user changes color preferences.
+	   |  set_special colors used in the gtkhid.
+	 */
+static void set_special_grid_color(void)
+{
+	if (!gport->colormap)
+		return;
+	gport->grid_color.red ^= gport->bg_color.red;
+	gport->grid_color.green ^= gport->bg_color.green;
+	gport->grid_color.blue ^= gport->bg_color.blue;
+}
+
+void ghid_set_special_colors(HID_Attribute * ha)
+{
+	if (!ha->name || !ha->value)
+		return;
+	if (!strcmp(ha->name, "background-color")) {
+		ghid_map_color_string(*(char **) ha->value, &gport->bg_color);
+		set_special_grid_color();
+	}
+	else if (!strcmp(ha->name, "off-limit-color")) {
+		ghid_map_color_string(*(char **) ha->value, &gport->offlimits_color);
+	}
+	else if (!strcmp(ha->name, "grid-color")) {
+		ghid_map_color_string(*(char **) ha->value, &gport->grid_color);
+		set_special_grid_color();
+	}
+}
+
+typedef struct {
+	int color_set;
+	GdkColor color;
+	int xor_set;
+	GdkColor xor_color;
+	double red;
+	double green;
+	double blue;
+} ColorCache;
+
+static void set_gl_color_for_gc(hidGC gc)
+{
+	render_priv *priv = gport->render_priv;
+	static void *cache = NULL;
+	hidval cval;
+	ColorCache *cc;
+	double r, g, b, a;
+
+	if (priv->current_colorname != NULL &&
+			strcmp(priv->current_colorname, gc->colorname) == 0 && priv->current_alpha_mult == gc->alpha_mult)
+		return;
+
+	free(priv->current_colorname);
+	priv->current_colorname = strdup(gc->colorname);
+	priv->current_alpha_mult = gc->alpha_mult;
+
+	if (gport->colormap == NULL)
+		gport->colormap = gtk_widget_get_colormap(gport->top_window);
+	if (strcmp(gc->colorname, "erase") == 0) {
+		r = gport->bg_color.red / 65535.;
+		g = gport->bg_color.green / 65535.;
+		b = gport->bg_color.blue / 65535.;
+		a = 1.0;
+	}
+	else if (strcmp(gc->colorname, "drill") == 0) {
+		r = gport->offlimits_color.red / 65535.;
+		g = gport->offlimits_color.green / 65535.;
+		b = gport->offlimits_color.blue / 65535.;
+		a = 0.85;
+	}
+	else {
+		if (hid_cache_color(0, gc->colorname, &cval, &cache))
+			cc = (ColorCache *) cval.ptr;
+		else {
+			cc = (ColorCache *) malloc(sizeof(ColorCache));
+			memset(cc, 0, sizeof(*cc));
+			cval.ptr = cc;
+			hid_cache_color(1, gc->colorname, &cval, &cache);
+		}
+
+		if (!cc->color_set) {
+			if (gdk_color_parse(gc->colorname, &cc->color))
+				gdk_color_alloc(gport->colormap, &cc->color);
+			else
+				gdk_color_white(gport->colormap, &cc->color);
+			cc->red = cc->color.red / 65535.;
+			cc->green = cc->color.green / 65535.;
+			cc->blue = cc->color.blue / 65535.;
+			cc->color_set = 1;
+		}
+		if (gc->xor) {
+			if (!cc->xor_set) {
+				cc->xor_color.red = cc->color.red ^ gport->bg_color.red;
+				cc->xor_color.green = cc->color.green ^ gport->bg_color.green;
+				cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue;
+				gdk_color_alloc(gport->colormap, &cc->xor_color);
+				cc->red = cc->color.red / 65535.;
+				cc->green = cc->color.green / 65535.;
+				cc->blue = cc->color.blue / 65535.;
+				cc->xor_set = 1;
+			}
+		}
+		r = cc->red;
+		g = cc->green;
+		b = cc->blue;
+		a = 0.7;
+	}
+	if (1) {
+		double maxi, mult;
+		a *= gc->alpha_mult;
+		if (!priv->trans_lines)
+			a = 1.0;
+		maxi = r;
+		if (g > maxi)
+			maxi = g;
+		if (b > maxi)
+			maxi = b;
+		mult = MIN(1 / a, 1 / maxi);
+#if 1
+		r = r * mult;
+		g = g * mult;
+		b = b * mult;
+#endif
+	}
+
+	if (!priv->in_context)
+		return;
+
+	hidgl_flush_triangles(&buffer);
+	glColor4d(r, g, b, a);
+}
+
+void ghid_set_color(hidGC gc, const char *name)
+{
+	gc->colorname = name;
+	set_gl_color_for_gc(gc);
+}
+
+void ghid_set_alpha_mult(hidGC gc, double alpha_mult)
+{
+	gc->alpha_mult = alpha_mult;
+	set_gl_color_for_gc(gc);
+}
+
+void ghid_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+void ghid_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+
+void ghid_set_draw_xor(hidGC gc, int xor)
+{
+	/* NOT IMPLEMENTED */
+
+	/* Only presently called when setting up a crosshair GC.
+	 * We manage our own drawing model for that anyway. */
+}
+
+void ghid_set_draw_faded(hidGC gc, int faded)
+{
+	printf("ghid_set_draw_faded(%p,%d) -- not implemented\n", (void *)gc, faded);
+}
+
+void ghid_set_line_cap_angle(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	printf("ghid_set_line_cap_angle() -- not implemented\n");
+}
+
+static void ghid_invalidate_current_gc(void)
+{
+	current_gc = NULL;
+}
+
+static int use_gc(hidGC gc)
+{
+	if (gc->me_pointer != &ghid_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to GTK HID\n");
+		abort();
+	}
+
+	if (current_gc == gc)
+		return 1;
+
+	current_gc = gc;
+
+	set_gl_color_for_gc(gc);
+	return 1;
+}
+
+void ghid_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	USE_GC(gc);
+
+	hidgl_draw_line(gc->cap, gc->width, x1, y1, x2, y2, gport->view.coord_per_px);
+}
+
+void ghid_draw_arc(hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
+{
+	USE_GC(gc);
+
+	hidgl_draw_arc(gc->width, cx, cy, xradius, yradius, start_angle, delta_angle, gport->view.coord_per_px);
+}
+
+void ghid_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	USE_GC(gc);
+
+	hidgl_draw_rect(x1, y1, x2, y2);
+}
+
+
+void ghid_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	USE_GC(gc);
+
+	hidgl_fill_circle(cx, cy, radius, gport->view.coord_per_px);
+}
+
+
+void ghid_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	USE_GC(gc);
+
+	hidgl_fill_polygon(n_coords, x, y);
+}
+
+void ghid_fill_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box)
+{
+	USE_GC(gc);
+
+	hidgl_fill_pcb_polygon(poly, clip_box, gport->view.coord_per_px);
+}
+
+void ghid_thindraw_pcb_polygon(hidGC gc, PolygonType * poly, const BoxType * clip_box)
+{
+	common_thindraw_pcb_polygon(gc, poly, clip_box);
+	ghid_set_alpha_mult(gc, 0.25);
+	ghid_fill_pcb_polygon(gc, poly, clip_box);
+	ghid_set_alpha_mult(gc, 1.0);
+}
+
+void ghid_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	USE_GC(gc);
+
+	hidgl_fill_rect(x1, y1, x2, y2);
+}
+
+void ghid_invalidate_lr(int left, int right, int top, int bottom)
+{
+	ghid_invalidate_all();
+}
+
+void ghid_invalidate_all()
+{
+	ghid_draw_area_update(gport, NULL);
+}
+
+void ghid_notify_crosshair_change(bool changes_complete)
+{
+	/* We sometimes get called before the GUI is up */
+	if (gport->drawing_area == NULL)
+		return;
+
+	/* FIXME: We could just invalidate the bounds of the crosshair attached objects? */
+	ghid_invalidate_all();
+}
+
+void ghid_notify_mark_change(bool changes_complete)
+{
+	/* We sometimes get called before the GUI is up */
+	if (gport->drawing_area == NULL)
+		return;
+
+	/* FIXME: We could just invalidate the bounds of the mark? */
+	ghid_invalidate_all();
+}
+
+static void draw_right_cross(gint x, gint y, gint z)
+{
+	glVertex3i(x, 0, z);
+	glVertex3i(x, PCB->MaxHeight, z);
+	glVertex3i(0, y, z);
+	glVertex3i(PCB->MaxWidth, y, z);
+}
+
+static void draw_slanted_cross(gint x, gint y, gint z)
+{
+	gint x0, y0, x1, y1;
+
+	x0 = x + (PCB->MaxHeight - y);
+	x0 = MAX(0, MIN(x0, PCB->MaxWidth));
+	x1 = x - y;
+	x1 = MAX(0, MIN(x1, PCB->MaxWidth));
+	y0 = y + (PCB->MaxWidth - x);
+	y0 = MAX(0, MIN(y0, PCB->MaxHeight));
+	y1 = y - x;
+	y1 = MAX(0, MIN(y1, PCB->MaxHeight));
+	glVertex3i(x0, y0, z);
+	glVertex3i(x1, y1, z);
+
+	x0 = x - (PCB->MaxHeight - y);
+	x0 = MAX(0, MIN(x0, PCB->MaxWidth));
+	x1 = x + y;
+	x1 = MAX(0, MIN(x1, PCB->MaxWidth));
+	y0 = y + x;
+	y0 = MAX(0, MIN(y0, PCB->MaxHeight));
+	y1 = y - (PCB->MaxWidth - x);
+	y1 = MAX(0, MIN(y1, PCB->MaxHeight));
+	glVertex3i(x0, y0, z);
+	glVertex3i(x1, y1, z);
+}
+
+static void draw_dozen_cross(gint x, gint y, gint z)
+{
+	gint x0, y0, x1, y1;
+	gdouble tan60 = sqrt(3);
+
+	x0 = x + (PCB->MaxHeight - y) / tan60;
+	x0 = MAX(0, MIN(x0, PCB->MaxWidth));
+	x1 = x - y / tan60;
+	x1 = MAX(0, MIN(x1, PCB->MaxWidth));
+	y0 = y + (PCB->MaxWidth - x) * tan60;
+	y0 = MAX(0, MIN(y0, PCB->MaxHeight));
+	y1 = y - x * tan60;
+	y1 = MAX(0, MIN(y1, PCB->MaxHeight));
+	glVertex3i(x0, y0, z);
+	glVertex3i(x1, y1, z);
+
+	x0 = x + (PCB->MaxHeight - y) * tan60;
+	x0 = MAX(0, MIN(x0, PCB->MaxWidth));
+	x1 = x - y * tan60;
+	x1 = MAX(0, MIN(x1, PCB->MaxWidth));
+	y0 = y + (PCB->MaxWidth - x) / tan60;
+	y0 = MAX(0, MIN(y0, PCB->MaxHeight));
+	y1 = y - x / tan60;
+	y1 = MAX(0, MIN(y1, PCB->MaxHeight));
+	glVertex3i(x0, y0, z);
+	glVertex3i(x1, y1, z);
+
+	x0 = x - (PCB->MaxHeight - y) / tan60;
+	x0 = MAX(0, MIN(x0, PCB->MaxWidth));
+	x1 = x + y / tan60;
+	x1 = MAX(0, MIN(x1, PCB->MaxWidth));
+	y0 = y + x * tan60;
+	y0 = MAX(0, MIN(y0, PCB->MaxHeight));
+	y1 = y - (PCB->MaxWidth - x) * tan60;
+	y1 = MAX(0, MIN(y1, PCB->MaxHeight));
+	glVertex3i(x0, y0, z);
+	glVertex3i(x1, y1, z);
+
+	x0 = x - (PCB->MaxHeight - y) * tan60;
+	x0 = MAX(0, MIN(x0, PCB->MaxWidth));
+	x1 = x + y * tan60;
+	x1 = MAX(0, MIN(x1, PCB->MaxWidth));
+	y0 = y + x / tan60;
+	y0 = MAX(0, MIN(y0, PCB->MaxHeight));
+	y1 = y - (PCB->MaxWidth - x) / tan60;
+	y1 = MAX(0, MIN(y1, PCB->MaxHeight));
+	glVertex3i(x0, y0, z);
+	glVertex3i(x1, y1, z);
+}
+
+static void draw_crosshair(gint x, gint y, gint z)
+{
+	static enum crosshair_shape prev = Basic_Crosshair_Shape;
+
+	draw_right_cross(x, y, z);
+	if (prev == Union_Jack_Crosshair_Shape)
+		draw_slanted_cross(x, y, z);
+	if (prev == Dozen_Crosshair_Shape)
+		draw_dozen_cross(x, y, z);
+	prev = Crosshair.shape;
+}
+
+void ghid_show_crosshair(gboolean paint_new_location)
+{
+	gint x, y, z;
+	static int done_once = 0;
+	static GdkColor cross_color;
+
+	if (!paint_new_location)
+		return;
+
+	if (!done_once) {
+		done_once = 1;
+		/* FIXME: when CrossColor changed from config */
+		ghid_map_color_string(conf_core.appearance.color.cross, &cross_color);
+	}
+	x = gport->crosshair_x;
+	y = gport->crosshair_y;
+	z = 0;
+
+	glEnable(GL_COLOR_LOGIC_OP);
+	glLogicOp(GL_XOR);
+
+	glColor3f(cross_color.red / 65535., cross_color.green / 65535., cross_color.blue / 65535.);
+
+	if (x >= 0 && paint_new_location) {
+		glBegin(GL_LINES);
+		draw_crosshair(x, y, z);
+		glEnd();
+	}
+
+	glDisable(GL_COLOR_LOGIC_OP);
+}
+
+void ghid_init_renderer(int *argc, char ***argv, GHidPort * port)
+{
+	render_priv *priv;
+
+	port->render_priv = priv = g_new0(render_priv, 1);
+
+	gtk_gl_init(argc, argv);
+
+	/* setup GL-context */
+	priv->glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGBA | GDK_GL_MODE_STENCIL | GDK_GL_MODE_DOUBLE);
+	if (!priv->glconfig) {
+		printf("Could not setup GL-context!\n");
+		return;											/* Should we abort? */
+	}
+
+	/* Setup HID function pointers specific to the GL renderer */
+	ghid_hid.end_layer = ghid_end_layer;
+	ghid_hid.fill_pcb_polygon = ghid_fill_pcb_polygon;
+	ghid_hid.thindraw_pcb_polygon = ghid_thindraw_pcb_polygon;
+}
+
+void ghid_shutdown_renderer(GHidPort * port)
+{
+	ghid_cancel_lead_user();
+	g_free(port->render_priv);
+	port->render_priv = NULL;
+}
+
+void ghid_init_drawing_widget(GtkWidget * widget, GHidPort * port)
+{
+	render_priv *priv = port->render_priv;
+
+	gtk_widget_set_gl_capability(widget, priv->glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
+}
+
+void ghid_drawing_area_configure_hook(GHidPort * port)
+{
+}
+
+gboolean ghid_start_drawing(GHidPort * port)
+{
+	GtkWidget *widget = port->drawing_area;
+	GdkGLContext *pGlContext = gtk_widget_get_gl_context(widget);
+	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(widget);
+
+	/* make GL-context "current" */
+	if (!gdk_gl_drawable_gl_begin(pGlDrawable, pGlContext))
+		return FALSE;
+
+	port->render_priv->in_context = pcb_true;
+
+	return TRUE;
+}
+
+void ghid_end_drawing(GHidPort * port)
+{
+	GtkWidget *widget = port->drawing_area;
+	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(widget);
+
+	if (gdk_gl_drawable_is_double_buffered(pGlDrawable))
+		gdk_gl_drawable_swap_buffers(pGlDrawable);
+	else
+		glFlush();
+
+	port->render_priv->in_context = pcb_false;
+
+	/* end drawing to current GL-context */
+	gdk_gl_drawable_gl_end(pGlDrawable);
+}
+
+void ghid_screen_update(void)
+{
+}
+
+#define Z_NEAR 3.0
+gboolean ghid_drawing_area_expose_cb(GtkWidget * widget, GdkEventExpose * ev, GHidPort * port)
+{
+	render_priv *priv = port->render_priv;
+	GtkAllocation allocation;
+	BoxType region;
+
+	gtk_widget_get_allocation(widget, &allocation);
+
+	ghid_start_drawing(port);
+
+	hidgl_init();
+
+	/* If we don't have any stencil bits available,
+	   we can't use the hidgl polygon drawing routine */
+	/* TODO: We could use the GLU tessellator though */
+	if (hidgl_stencil_bits() == 0)
+		ghid_hid.fill_pcb_polygon = common_fill_pcb_polygon;
+
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	glViewport(0, 0, allocation.width, allocation.height);
+
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(ev->area.x, allocation.height - ev->area.height - ev->area.y, ev->area.width, ev->area.height);
+
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, allocation.width, allocation.height, 0, 0, 100);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	glTranslatef(0.0f, 0.0f, -Z_NEAR);
+
+	glScalef((conf_core.editor.view.flip_x ? -1. : 1.) / port->view.coord_per_px,
+					 (conf_core.editor.view.flip_y ? -1. : 1.) / port->view.coord_per_px,
+					 ((conf_core.editor.view.flip_x == conf_core.editor.view.flip_y) ? 1. : -1.) / port->view.coord_per_px);
+	glTranslatef(conf_core.editor.view.flip_x ? port->view.x0 - PCB->MaxWidth :
+							 -port->view.x0, conf_core.editor.view.flip_y ? port->view.y0 - PCB->MaxHeight : -port->view.y0, 0);
+
+	glEnable(GL_STENCIL_TEST);
+	glClearColor(port->offlimits_color.red / 65535.,
+							 port->offlimits_color.green / 65535., port->offlimits_color.blue / 65535., 1.);
+	glStencilMask(~0);
+	glClearStencil(0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+	hidgl_reset_stencil_usage();
+
+	/* Disable the stencil test until we need it - otherwise it gets dirty */
+	glDisable(GL_STENCIL_TEST);
+	glStencilMask(0);
+	glStencilFunc(GL_ALWAYS, 0, 0);
+
+	region.X1 = MIN(Px(ev->area.x), Px(ev->area.x + ev->area.width + 1));
+	region.X2 = MAX(Px(ev->area.x), Px(ev->area.x + ev->area.width + 1));
+	region.Y1 = MIN(Py(ev->area.y), Py(ev->area.y + ev->area.height + 1));
+	region.Y2 = MAX(Py(ev->area.y), Py(ev->area.y + ev->area.height + 1));
+
+	region.X1 = MAX(0, MIN(PCB->MaxWidth, region.X1));
+	region.X2 = MAX(0, MIN(PCB->MaxWidth, region.X2));
+	region.Y1 = MAX(0, MIN(PCB->MaxHeight, region.Y1));
+	region.Y2 = MAX(0, MIN(PCB->MaxHeight, region.Y2));
+
+	glColor3f(port->bg_color.red / 65535., port->bg_color.green / 65535., port->bg_color.blue / 65535.);
+
+	glBegin(GL_QUADS);
+	glVertex3i(0, 0, 0);
+	glVertex3i(PCB->MaxWidth, 0, 0);
+	glVertex3i(PCB->MaxWidth, PCB->MaxHeight, 0);
+	glVertex3i(0, PCB->MaxHeight, 0);
+	glEnd();
+
+	ghid_draw_bg_image();
+
+	hidgl_init_triangle_array(&buffer);
+	ghid_invalidate_current_gc();
+	hid_expose_callback(&ghid_hid, &region, 0);
+	hidgl_flush_triangles(&buffer);
+
+	ghid_draw_grid(&region);
+
+	ghid_invalidate_current_gc();
+
+	DrawAttached();
+	DrawMark();
+	hidgl_flush_triangles(&buffer);
+
+	ghid_show_crosshair(TRUE);
+
+	hidgl_flush_triangles(&buffer);
+
+	draw_lead_user(priv);
+
+	ghid_end_drawing(port);
+
+	return FALSE;
+}
+
+/* This realize callback is used to work around a crash bug in some mesa
+ * versions (observed on a machine running the intel i965 driver. It isn't
+ * obvious why it helps, but somehow fiddling with the GL context here solves
+ * the issue. The problem appears to have been fixed in recent mesa versions.
+ */
+void ghid_port_drawing_realize_cb(GtkWidget * widget, gpointer data)
+{
+	GdkGLContext *glcontext = gtk_widget_get_gl_context(widget);
+	GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget);
+
+	if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
+		return;
+
+	gdk_gl_drawable_gl_end(gldrawable);
+	return;
+}
+
+gboolean ghid_pinout_preview_expose(GtkWidget * widget, GdkEventExpose * ev)
+{
+	GdkGLContext *pGlContext = gtk_widget_get_gl_context(widget);
+	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(widget);
+	GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW(widget);
+	GtkAllocation allocation;
+	view_data save_view;
+	int save_width, save_height;
+	double xz, yz;
+
+	save_view = gport->view;
+	save_width = gport->width;
+	save_height = gport->height;
+
+	/* Setup zoom factor for drawing routines */
+
+	gtk_widget_get_allocation(widget, &allocation);
+	xz = (double) pinout->x_max / allocation.width;
+	yz = (double) pinout->y_max / allocation.height;
+	if (xz > yz)
+		gport->view.coord_per_px = xz;
+	else
+		gport->view.coord_per_px = yz;
+
+	gport->width = allocation.width;
+	gport->height = allocation.height;
+	gport->view.width = allocation.width * gport->view.coord_per_px;
+	gport->view.height = allocation.height * gport->view.coord_per_px;
+	gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
+	gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
+
+	/* make GL-context "current" */
+	if (!gdk_gl_drawable_gl_begin(pGlDrawable, pGlContext)) {
+		return FALSE;
+	}
+	gport->render_priv->in_context = pcb_true;
+
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	glViewport(0, 0, allocation.width, allocation.height);
+
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(ev->area.x, allocation.height - ev->area.height - ev->area.y, ev->area.width, ev->area.height);
+
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, allocation.width, allocation.height, 0, 0, 100);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	glTranslatef(0.0f, 0.0f, -Z_NEAR);
+
+	glClearColor(gport->bg_color.red / 65535., gport->bg_color.green / 65535., gport->bg_color.blue / 65535., 1.);
+	glStencilMask(~0);
+	glClearStencil(0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+	hidgl_reset_stencil_usage();
+
+	/* call the drawing routine */
+	hidgl_init_triangle_array(&buffer);
+	ghid_invalidate_current_gc();
+	glPushMatrix();
+	glScalef((conf_core.editor.view.flip_x ? -1. : 1.) / gport->view.coord_per_px,
+					 (conf_core.editor.view.flip_y ? -1. : 1.) / gport->view.coord_per_px, 1);
+	glTranslatef(conf_core.editor.view.flip_x ? gport->view.x0 - PCB->MaxWidth :
+							 -gport->view.x0, conf_core.editor.view.flip_y ? gport->view.y0 - PCB->MaxHeight : -gport->view.y0, 0);
+	hid_expose_callback(&ghid_hid, NULL, &pinout->element);
+	hidgl_flush_triangles(&buffer);
+	glPopMatrix();
+
+	if (gdk_gl_drawable_is_double_buffered(pGlDrawable))
+		gdk_gl_drawable_swap_buffers(pGlDrawable);
+	else
+		glFlush();
+
+	/* end drawing to current GL-context */
+	gport->render_priv->in_context = pcb_false;
+	gdk_gl_drawable_gl_end(pGlDrawable);
+
+	gport->view = save_view;
+	gport->width = save_width;
+	gport->height = save_height;
+
+	return FALSE;
+}
+
+
+GdkPixmap *ghid_render_pixmap(int cx, int cy, double zoom, int width, int height, int depth)
+{
+	GdkGLConfig *glconfig;
+	GdkPixmap *pixmap;
+	GdkGLPixmap *glpixmap;
+	GdkGLContext *glcontext;
+	GdkGLDrawable *gldrawable;
+	view_data save_view;
+	int save_width, save_height;
+	BoxType region;
+
+	save_view = gport->view;
+	save_width = gport->width;
+	save_height = gport->height;
+
+	/* Setup rendering context for drawing routines
+	 */
+
+	glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_STENCIL | GDK_GL_MODE_SINGLE);
+
+	pixmap = gdk_pixmap_new(NULL, width, height, depth);
+	glpixmap = gdk_pixmap_set_gl_capability(pixmap, glconfig, NULL);
+	gldrawable = GDK_GL_DRAWABLE(glpixmap);
+	glcontext = gdk_gl_context_new(gldrawable, NULL, FALSE, GDK_GL_RGBA_TYPE);
+
+	/* Setup zoom factor for drawing routines */
+
+	gport->view.coord_per_px = zoom;
+	gport->width = width;
+	gport->height = height;
+	gport->view.width = width * gport->view.coord_per_px;
+	gport->view.height = height * gport->view.coord_per_px;
+	gport->view.x0 = conf_core.editor.view.flip_x ? PCB->MaxWidth - cx : cx;
+	gport->view.x0 -= gport->view.height / 2;
+	gport->view.y0 = conf_core.editor.view.flip_y ? PCB->MaxHeight - cy : cy;
+	gport->view.y0 -= gport->view.width / 2;
+
+	/* make GL-context "current" */
+	if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) {
+		return NULL;
+	}
+	gport->render_priv->in_context = pcb_true;
+
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	glViewport(0, 0, width, height);
+
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(0, 0, width, height);
+
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, width, height, 0, 0, 100);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	glTranslatef(0.0f, 0.0f, -Z_NEAR);
+
+	glClearColor(gport->bg_color.red / 65535., gport->bg_color.green / 65535., gport->bg_color.blue / 65535., 1.);
+	glStencilMask(~0);
+	glClearStencil(0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+	hidgl_reset_stencil_usage();
+
+	/* call the drawing routine */
+	hidgl_init_triangle_array(&buffer);
+	ghid_invalidate_current_gc();
+	glPushMatrix();
+	glScalef((conf_core.editor.view.flip_x ? -1. : 1.) / gport->view.coord_per_px,
+					 (conf_core.editor.view.flip_y ? -1. : 1.) / gport->view.coord_per_px, 1);
+	glTranslatef(gport->view.flip_x ? gport->view.x0 - PCB->MaxWidth :
+							 -gport->view.x0, gport->view.flip_y ? gport->view.y0 - PCB->MaxHeight : -gport->view.y0, 0);
+
+	region.X1 = MIN(Px(0), Px(gport->width + 1));
+	region.Y1 = MIN(Py(0), Py(gport->height + 1));
+	region.X2 = MAX(Px(0), Px(gport->width + 1));
+	region.Y2 = MAX(Py(0), Py(gport->height + 1));
+
+	region.X1 = MAX(0, MIN(PCB->MaxWidth, region.X1));
+	region.X2 = MAX(0, MIN(PCB->MaxWidth, region.X2));
+	region.Y1 = MAX(0, MIN(PCB->MaxHeight, region.Y1));
+	region.Y2 = MAX(0, MIN(PCB->MaxHeight, region.Y2));
+
+	hid_expose_callback(&ghid_hid, &region, NULL);
+	hidgl_flush_triangles(&buffer);
+	glPopMatrix();
+
+	glFlush();
+
+	/* end drawing to current GL-context */
+	gport->render_priv->in_context = pcb_false;
+	gdk_gl_drawable_gl_end(gldrawable);
+
+	gdk_pixmap_unset_gl_capability(pixmap);
+
+	g_object_unref(glconfig);
+	g_object_unref(glcontext);
+
+	gport->view = save_view;
+	gport->width = save_width;
+	gport->height = save_height;
+
+	return pixmap;
+}
+
+HID *ghid_request_debug_draw(void)
+{
+	GHidPort *port = gport;
+	GtkWidget *widget = port->drawing_area;
+	GtkAllocation allocation;
+
+	gtk_widget_get_allocation(widget, &allocation);
+
+	ghid_start_drawing(port);
+
+	glViewport(0, 0, allocation.width, allocation.height);
+
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, allocation.width, allocation.height, 0, 0, 100);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	glTranslatef(0.0f, 0.0f, -Z_NEAR);
+
+	hidgl_init_triangle_array(&buffer);
+	ghid_invalidate_current_gc();
+
+	/* Setup stenciling */
+	glDisable(GL_STENCIL_TEST);
+
+	glPushMatrix();
+	glScalef((conf_core.editor.view.flip_x ? -1. : 1.) / port->view.coord_per_px,
+					 (conf_core.editor.view.flip_y ? -1. : 1.) / port->view.coord_per_px, (port->view.flip_x == port->view.flip_y) ? 1. : -1.);
+	glTranslatef(conf_core.editor.view.flip_x ? port->view.x0 - PCB->MaxWidth :
+							 -port->view.x0, conf_core.editor.view.flip_y ? port->view.y0 - PCB->MaxHeight : -port->view.y0, 0);
+
+	return &ghid_hid;
+}
+
+void ghid_flush_debug_draw(void)
+{
+	GtkWidget *widget = gport->drawing_area;
+	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(widget);
+
+	hidgl_flush_triangles(&buffer);
+
+	if (gdk_gl_drawable_is_double_buffered(pGlDrawable))
+		gdk_gl_drawable_swap_buffers(pGlDrawable);
+	else
+		glFlush();
+}
+
+void ghid_finish_debug_draw(void)
+{
+	hidgl_flush_triangles(&buffer);
+	glPopMatrix();
+
+	ghid_end_drawing(gport);
+}
+
+bool ghid_event_to_pcb_coords(int event_x, int event_y, Coord * pcb_x, Coord * pcb_y)
+{
+	*pcb_x = EVENT_TO_PCB_X(event_x);
+	*pcb_y = EVENT_TO_PCB_Y(event_y);
+
+	return pcb_true;
+}
+
+bool ghid_pcb_to_event_coords(Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
+{
+	*event_x = DRAW_X(pcb_x);
+	*event_y = DRAW_Y(pcb_y);
+
+	return pcb_true;
+}
+
+
+#define LEAD_USER_WIDTH           0.2	/* millimeters */
+#define LEAD_USER_PERIOD          (1000 / 20)	/* 20fps (in ms) */
+#define LEAD_USER_VELOCITY        3.	/* millimeters per second */
+#define LEAD_USER_ARC_COUNT       3
+#define LEAD_USER_ARC_SEPARATION  3.	/* millimeters */
+#define LEAD_USER_INITIAL_RADIUS  10.	/* millimetres */
+#define LEAD_USER_COLOR_R         1.
+#define LEAD_USER_COLOR_G         1.
+#define LEAD_USER_COLOR_B         0.
+
+static void draw_lead_user(render_priv * priv)
+{
+	int i;
+	double radius = priv->lead_user_radius;
+	double width = PCB_MM_TO_COORD(LEAD_USER_WIDTH);
+	double separation = PCB_MM_TO_COORD(LEAD_USER_ARC_SEPARATION);
+
+	if (!priv->lead_user)
+		return;
+
+	glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT);
+	glEnable(GL_COLOR_LOGIC_OP);
+	glLogicOp(GL_XOR);
+	glColor3f(LEAD_USER_COLOR_R, LEAD_USER_COLOR_G, LEAD_USER_COLOR_B);
+
+
+	/* arcs at the approrpriate radii */
+
+	for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation) {
+		if (radius < width)
+			radius += PCB_MM_TO_COORD(LEAD_USER_INITIAL_RADIUS);
+
+		/* Draw an arc at radius */
+		hidgl_draw_arc(width, priv->lead_user_x, priv->lead_user_y, radius, radius, 0, 360, gport->view.coord_per_px);
+	}
+
+	hidgl_flush_triangles(&buffer);
+	glPopAttrib();
+}
+
+gboolean lead_user_cb(gpointer data)
+{
+	render_priv *priv = data;
+	Coord step;
+	double elapsed_time;
+
+	/* Queue a redraw */
+	ghid_invalidate_all();
+
+	/* Update radius */
+	elapsed_time = g_timer_elapsed(priv->lead_user_timer, NULL);
+	g_timer_start(priv->lead_user_timer);
+
+	step = PCB_MM_TO_COORD(LEAD_USER_VELOCITY * elapsed_time);
+	if (priv->lead_user_radius > step)
+		priv->lead_user_radius -= step;
+	else
+		priv->lead_user_radius = PCB_MM_TO_COORD(LEAD_USER_INITIAL_RADIUS);
+
+	return TRUE;
+}
+
+void ghid_lead_user_to_location(Coord x, Coord y)
+{
+	render_priv *priv = gport->render_priv;
+
+	ghid_cancel_lead_user();
+
+	priv->lead_user = pcb_true;
+	priv->lead_user_x = x;
+	priv->lead_user_y = y;
+	priv->lead_user_radius = PCB_MM_TO_COORD(LEAD_USER_INITIAL_RADIUS);
+	priv->lead_user_timeout = g_timeout_add(LEAD_USER_PERIOD, lead_user_cb, priv);
+	priv->lead_user_timer = g_timer_new();
+}
+
+void ghid_cancel_lead_user(void)
+{
+	render_priv *priv = gport->render_priv;
+
+	if (priv->lead_user_timeout)
+		g_source_remove(priv->lead_user_timeout);
+
+	if (priv->lead_user_timer)
+		g_timer_destroy(priv->lead_user_timer);
+
+	if (priv->lead_user)
+		ghid_invalidate_all();
+
+	priv->lead_user_timeout = 0;
+	priv->lead_user_timer = NULL;
+	priv->lead_user = pcb_false;
+}
diff --git a/src_plugins/hid_gtk/gtkhid-main.c b/src_plugins/hid_gtk/gtkhid-main.c
new file mode 100644
index 0000000..a716627
--- /dev/null
+++ b/src_plugins/hid_gtk/gtkhid-main.c
@@ -0,0 +1,2155 @@
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include "action_helper.h"
+#include "crosshair.h"
+#include "error.h"
+#include "gui.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_attrib.h"
+#include "hid_init.h"
+#include "hid_flags.h"
+#include "hid_actions.h"
+#include "plug_footprint.h"
+#include "plug_io.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "layer.h"
+#include "ghid-search.h"
+
+#include "gtkhid.h"
+
+conf_hid_id_t ghid_conf_id = -1;
+conf_hid_id_t ghid_menuconf_id = -1;
+GdkModifierType ghid_glob_mask;
+
+static void pan_common(GHidPort * port)
+{
+	int event_x, event_y;
+
+	/* We need to fix up the PCB coordinates corresponding to the last
+	 * event so convert it back to event coordinates temporarily. */
+	ghid_pcb_to_event_coords(gport->pcb_x, gport->pcb_y, &event_x, &event_y);
+
+	/* Don't pan so far the board is completely off the screen */
+	port->view.x0 = MAX(-port->view.width, port->view.x0);
+	port->view.y0 = MAX(-port->view.height, port->view.y0);
+	port->view.x0 = MIN(port->view.x0, PCB->MaxWidth);
+	port->view.y0 = MIN(port->view.y0, PCB->MaxHeight);
+
+	/* Fix up noted event coordinates to match where we clamped. Alternatively
+	 * we could call ghid_note_event_location (NULL); to get a new pointer
+	 * location, but this costs us an xserver round-trip (on X11 platforms)
+	 */
+	ghid_event_to_pcb_coords(event_x, event_y, &gport->pcb_x, &gport->pcb_y);
+
+	ghidgui->adjustment_changed_holdoff = TRUE;
+	gtk_range_set_value(GTK_RANGE(ghidgui->h_range), gport->view.x0);
+	gtk_range_set_value(GTK_RANGE(ghidgui->v_range), gport->view.y0);
+	ghidgui->adjustment_changed_holdoff = FALSE;
+
+	ghid_port_ranges_changed();
+}
+
+static void ghid_pan_view_abs(Coord pcb_x, Coord pcb_y, int widget_x, int widget_y)
+{
+	gport->view.x0 = SIDE_X(pcb_x) - widget_x * gport->view.coord_per_px;
+	gport->view.y0 = SIDE_Y(pcb_y) - widget_y * gport->view.coord_per_px;
+
+	pan_common(gport);
+}
+
+void ghid_pan_view_rel(Coord dx, Coord dy)
+{
+	gport->view.x0 += dx;
+	gport->view.y0 += dy;
+
+	pan_common(gport);
+}
+
+
+/* gport->view.coord_per_px:
+ * zoom value is PCB units per screen pixel.  Larger numbers mean zooming
+ * out - the largest value means you are looking at the whole board.
+ *
+ * gport->view_width and gport->view_height are in PCB coordinates
+ */
+
+#define ALLOW_ZOOM_OUT_BY 10		/* Arbitrary, and same as the lesstif HID MAX_ZOOM_SCALE */
+static void ghid_zoom_view_abs(Coord center_x, Coord center_y, double new_zoom)
+{
+	double min_zoom, max_zoom;
+	double xtmp, ytmp;
+	Coord cmaxx, cmaxy;
+
+	/* Limit the "minimum" zoom constant (maximum zoom), at 1 pixel per PCB
+	 * unit, and set the "maximum" zoom constant (minimum zoom), such that
+	 * the entire board just fits inside the viewport
+	 */
+	min_zoom = 200;
+	max_zoom = MAX(PCB->MaxWidth / gport->width, PCB->MaxHeight / gport->height) * ALLOW_ZOOM_OUT_BY;
+	new_zoom = MIN(MAX(min_zoom, new_zoom), max_zoom);
+
+	if ((new_zoom > max_zoom) || (new_zoom < min_zoom))
+		return;
+
+	if (gport->view.coord_per_px == new_zoom)
+		return;
+
+	/* Do not allow zoom level that'd overflow the coord type */
+	cmaxx = gport->width  * (new_zoom / 2.0);
+	cmaxy = gport->height * (new_zoom / 2.0);
+	if ((cmaxx >= COORD_MAX/2) || (cmaxy >= COORD_MAX/2)) {
+		return;
+	}
+
+	xtmp = (SIDE_X(center_x) - gport->view.x0) / (double) gport->view.width;
+	ytmp = (SIDE_Y(center_y) - gport->view.y0) / (double) gport->view.height;
+
+	gport->view.coord_per_px = new_zoom;
+	pixel_slop = new_zoom;
+	ghid_port_ranges_scale();
+
+	gport->view.x0 = SIDE_X(center_x) - xtmp * gport->view.width;
+	gport->view.y0 = SIDE_Y(center_y) - ytmp * gport->view.height;
+
+	pan_common(gport);
+
+	ghid_set_status_line_label();
+}
+
+static void ghid_zoom_view_rel(Coord center_x, Coord center_y, double factor)
+{
+	ghid_zoom_view_abs(center_x, center_y, gport->view.coord_per_px * factor);
+}
+
+static void ghid_zoom_view_fit(void)
+{
+	ghid_pan_view_abs(SIDE_X(0), SIDE_Y(0), 0, 0);
+	ghid_zoom_view_abs(SIDE_X(0), SIDE_Y(0), MAX(PCB->MaxWidth / gport->width, PCB->MaxHeight / gport->height));
+}
+
+static void ghid_flip_view(Coord center_x, Coord center_y, pcb_bool flip_x, pcb_bool flip_y)
+{
+	int widget_x, widget_y;
+
+	/* Work out where on the screen the flip point is */
+	ghid_pcb_to_event_coords(center_x, center_y, &widget_x, &widget_y);
+
+	conf_set_design("editor/view/flip_x", "%d", conf_core.editor.view.flip_x != flip_x);
+	conf_set_design("editor/view/flip_y", "%d", conf_core.editor.view.flip_y != flip_y);
+
+	/* Pan the board so the center location remains in the same place */
+	ghid_pan_view_abs(center_x, center_y, widget_x, widget_y);
+
+	ghid_invalidate_all();
+}
+
+/* ------------------------------------------------------------ */
+
+static const char zoom_syntax[] = "Zoom()\n" "Zoom(factor)";
+
+
+static const char zoom_help[] = N_("Various zoom factor changes.");
+
+/* %start-doc actions Zoom
+Changes the zoom (magnification) of the view of the board.  If no
+arguments are passed, the view is scaled such that the board just fits
+inside the visible window (i.e. ``view all'').  Otherwise,
+ at var{factor} specifies a change in zoom factor.  It may be prefixed by
+ at code{+}, @code{-}, or @code{=} to change how the zoom factor is
+modified.  The @var{factor} is a floating point number, such as
+ at code{1.5} or @code{0.75}.
+
+ at table @code
+
+ at item + at var{factor}
+Values greater than 1.0 cause the board to be drawn smaller; more of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn bigger; less of the board will be visible.
+
+ at item - at var{factor}
+Values greater than 1.0 cause the board to be drawn bigger; less of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn smaller; more of the board will be visible.
+
+ at item =@var{factor}
+
+The @var{factor} is an absolute zoom factor; the unit for this value
+is "PCB units per screen pixel".  Since PCB units are 0.01 mil, a
+ at var{factor} of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI,
+about the actual resolution of most screens - resulting in an "actual
+size" board.  Similarly, a @var{factor} of 100 gives you a 10x actual
+size.
+
+ at end table
+
+Note that zoom factors of zero are silently ignored.
+
+
+
+%end-doc */
+
+static int Zoom(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *vp;
+	double v;
+
+	if (argc > 1)
+		AFAIL(zoom);
+
+	if (argc < 1) {
+		ghid_zoom_view_fit();
+		return 0;
+	}
+
+	vp = argv[0];
+	if (*vp == '+' || *vp == '-' || *vp == '=')
+		vp++;
+	v = g_ascii_strtod(vp, 0);
+	if (v <= 0)
+		return 1;
+	switch (argv[0][0]) {
+	case '-':
+		ghid_zoom_view_rel(x, y, 1 / v);
+		break;
+	default:
+	case '+':
+		ghid_zoom_view_rel(x, y, v);
+		break;
+	case '=':
+		ghid_zoom_view_abs(x, y, v);
+		break;
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+void ghid_calibrate(double xval, double yval)
+{
+	printf(_("ghid_calibrate() -- not implemented\n"));
+}
+
+static int ghid_gui_is_up = 0;
+
+void ghid_notify_gui_is_up()
+{
+	ghid_gui_is_up = 1;
+}
+
+int ghid_shift_is_pressed()
+{
+	GdkModifierType mask;
+	GHidPort *out = &ghid_port;
+
+	if (!ghid_gui_is_up)
+		return 0;
+
+	gdk_window_get_pointer(gtk_widget_get_window(out->drawing_area), NULL, NULL, &mask);
+	return (mask & GDK_SHIFT_MASK) ? TRUE : FALSE;
+}
+
+int ghid_control_is_pressed()
+{
+	GdkModifierType mask;
+	GHidPort *out = &ghid_port;
+
+	if (!ghid_gui_is_up)
+		return 0;
+
+	gdk_window_get_pointer(gtk_widget_get_window(out->drawing_area), NULL, NULL, &mask);
+
+#ifdef PCB_WORKAROUND_GTK_CTRL
+	/* On some systems the above query fails and we need to return the last known state instead */
+	return ghid_glob_mask & GDK_CONTROL_MASK;
+#else
+	return (mask & GDK_CONTROL_MASK) ? TRUE : FALSE;
+#endif
+}
+
+int ghid_mod1_is_pressed()
+{
+	GdkModifierType mask;
+	GHidPort *out = &ghid_port;
+
+	if (!ghid_gui_is_up)
+		return 0;
+
+	gdk_window_get_pointer(gtk_widget_get_window(out->drawing_area), NULL, NULL, &mask);
+#ifdef __APPLE__
+	return (mask & (1 << 13)) ? TRUE : FALSE;	/* The option key is not MOD1, although it should be... */
+#else
+	return (mask & GDK_MOD1_MASK) ? TRUE : FALSE;
+#endif
+}
+
+void ghid_set_crosshair(int x, int y, int action)
+{
+	GdkDisplay *display;
+	GdkScreen *screen;
+	int offset_x, offset_y;
+	int widget_x, widget_y;
+	int pointer_x, pointer_y;
+	Coord pcb_x, pcb_y;
+
+	ghid_draw_grid_local(x, y);
+
+	if (gport->crosshair_x != x || gport->crosshair_y != y) {
+		ghid_set_cursor_position_labels();
+		gport->crosshair_x = x;
+		gport->crosshair_y = y;
+
+		/* FIXME - does this trigger the idle_proc stuff?  It is in the
+		 * lesstif HID.  Maybe something is needed here?
+		 *
+		 * need_idle_proc ();
+		 */
+	}
+
+	if (action != HID_SC_PAN_VIEWPORT && action != HID_SC_WARP_POINTER)
+		return;
+
+	/* Find out where the drawing area is on the screen. gdk_display_get_pointer
+	 * and gdk_display_warp_pointer work relative to the whole display, whilst
+	 * our coordinates are relative to the drawing area origin.
+	 */
+	gdk_window_get_origin(gtk_widget_get_window(gport->drawing_area), &offset_x, &offset_y);
+	display = gdk_display_get_default();
+
+	switch (action) {
+	case HID_SC_PAN_VIEWPORT:
+		/* Pan the board in the viewport so that the crosshair (who's location
+		 * relative on the board was set above) lands where the pointer is.
+		 * We pass the request to pan a particular point on the board to a
+		 * given widget coordinate of the viewport into the rendering code
+		 */
+
+		/* Find out where the pointer is relative to the display */
+		gdk_display_get_pointer(display, NULL, &pointer_x, &pointer_y, NULL);
+
+		widget_x = pointer_x - offset_x;
+		widget_y = pointer_y - offset_y;
+
+		ghid_event_to_pcb_coords(widget_x, widget_y, &pcb_x, &pcb_y);
+		ghid_pan_view_abs(pcb_x, pcb_y, widget_x, widget_y);
+
+		/* Just in case we couldn't pan the board the whole way,
+		 * we warp the pointer to where the crosshair DID land.
+		 */
+		/* Fall through */
+
+	case HID_SC_WARP_POINTER:
+		screen = gdk_display_get_default_screen(display);
+
+		ghid_pcb_to_event_coords(x, y, &widget_x, &widget_y);
+
+		pointer_x = offset_x + widget_x;
+		pointer_y = offset_y + widget_y;
+
+		gdk_display_warp_pointer(display, screen, pointer_x, pointer_y);
+
+		break;
+	}
+}
+
+typedef struct {
+	void (*func) (hidval);
+	guint id;
+	hidval user_data;
+} GuiTimer;
+
+	/* We need a wrapper around the hid timer because a gtk timer needs
+	   |  to return FALSE else the timer will be restarted.
+	 */
+static gboolean ghid_timer(GuiTimer * timer)
+{
+	(*timer->func) (timer->user_data);
+	ghid_mode_cursor(conf_core.editor.mode);
+	return FALSE;									/* Turns timer off */
+}
+
+hidval ghid_add_timer(void (*func) (hidval user_data), unsigned long milliseconds, hidval user_data)
+{
+	GuiTimer *timer = g_new0(GuiTimer, 1);
+	hidval ret;
+
+	timer->func = func;
+	timer->user_data = user_data;
+	timer->id = g_timeout_add(milliseconds, (GSourceFunc) ghid_timer, timer);
+	ret.ptr = (void *) timer;
+	return ret;
+}
+
+void ghid_stop_timer(hidval timer)
+{
+	void *ptr = timer.ptr;
+
+	g_source_remove(((GuiTimer *) ptr)->id);
+	g_free(ptr);
+}
+
+typedef struct {
+	void (*func) (hidval, int, unsigned int, hidval);
+	hidval user_data;
+	int fd;
+	GIOChannel *channel;
+	gint id;
+} GuiWatch;
+
+	/* We need a wrapper around the hid file watch to pass the correct flags
+	 */
+static gboolean ghid_watch(GIOChannel * source, GIOCondition condition, gpointer data)
+{
+	unsigned int pcb_condition = 0;
+	hidval x;
+	GuiWatch *watch = (GuiWatch *) data;
+
+	if (condition & G_IO_IN)
+		pcb_condition |= PCB_WATCH_READABLE;
+	if (condition & G_IO_OUT)
+		pcb_condition |= PCB_WATCH_WRITABLE;
+	if (condition & G_IO_ERR)
+		pcb_condition |= PCB_WATCH_ERROR;
+	if (condition & G_IO_HUP)
+		pcb_condition |= PCB_WATCH_HANGUP;
+
+	x.ptr = (void *) watch;
+	watch->func(x, watch->fd, pcb_condition, watch->user_data);
+	ghid_mode_cursor(conf_core.editor.mode);
+
+	return TRUE;									/* Leave watch on */
+}
+
+hidval
+ghid_watch_file(int fd, unsigned int condition, void (*func) (hidval watch, int fd, unsigned int condition, hidval user_data),
+								hidval user_data)
+{
+	GuiWatch *watch = g_new0(GuiWatch, 1);
+	hidval ret;
+	unsigned int glib_condition = 0;
+
+	if (condition & PCB_WATCH_READABLE)
+		glib_condition |= G_IO_IN;
+	if (condition & PCB_WATCH_WRITABLE)
+		glib_condition |= G_IO_OUT;
+	if (condition & PCB_WATCH_ERROR)
+		glib_condition |= G_IO_ERR;
+	if (condition & PCB_WATCH_HANGUP)
+		glib_condition |= G_IO_HUP;
+
+	watch->func = func;
+	watch->user_data = user_data;
+	watch->fd = fd;
+	watch->channel = g_io_channel_unix_new(fd);
+	watch->id = g_io_add_watch(watch->channel, (GIOCondition) glib_condition, ghid_watch, watch);
+
+	ret.ptr = (void *) watch;
+	return ret;
+}
+
+void ghid_unwatch_file(hidval data)
+{
+	GuiWatch *watch = (GuiWatch *) data.ptr;
+
+	g_io_channel_shutdown(watch->channel, TRUE, NULL);
+	g_io_channel_unref(watch->channel);
+	g_free(watch);
+}
+
+typedef struct {
+	GSource source;
+	void (*func) (hidval user_data);
+	hidval user_data;
+} BlockHookSource;
+
+static gboolean ghid_block_hook_prepare(GSource * source, gint * timeout);
+static gboolean ghid_block_hook_check(GSource * source);
+static gboolean ghid_block_hook_dispatch(GSource * source, GSourceFunc callback, gpointer user_data);
+
+static GSourceFuncs ghid_block_hook_funcs = {
+	ghid_block_hook_prepare,
+	ghid_block_hook_check,
+	ghid_block_hook_dispatch,
+	NULL													/* No destroy notification */
+};
+
+static gboolean ghid_block_hook_prepare(GSource * source, gint * timeout)
+{
+	hidval data = ((BlockHookSource *) source)->user_data;
+	((BlockHookSource *) source)->func(data);
+	return FALSE;
+}
+
+static gboolean ghid_block_hook_check(GSource * source)
+{
+	return FALSE;
+}
+
+static gboolean ghid_block_hook_dispatch(GSource * source, GSourceFunc callback, gpointer user_data)
+{
+	return FALSE;
+}
+
+static hidval ghid_add_block_hook(void (*func) (hidval data), hidval user_data)
+{
+	hidval ret;
+	BlockHookSource *source;
+
+	source = (BlockHookSource *) g_source_new(&ghid_block_hook_funcs, sizeof(BlockHookSource));
+
+	source->func = func;
+	source->user_data = user_data;
+
+	g_source_attach((GSource *) source, NULL);
+
+	ret.ptr = (void *) source;
+	return ret;
+}
+
+static void ghid_stop_block_hook(hidval mlpoll)
+{
+	GSource *source = (GSource *) mlpoll.ptr;
+	g_source_destroy(source);
+}
+
+int ghid_confirm_dialog(const char *msg, ...)
+{
+	int rv = 0;
+	va_list ap;
+	const char *cancelmsg = NULL, *okmsg = NULL;
+	static gint x = -1, y = -1;
+	GtkWidget *dialog;
+	GHidPort *out = &ghid_port;
+
+	va_start(ap, msg);
+	cancelmsg = va_arg(ap, char *);
+	okmsg = va_arg(ap, char *);
+	va_end(ap);
+
+	if (!cancelmsg) {
+		cancelmsg = _("_Cancel");
+		okmsg = _("_OK");
+	}
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(out->top_window),
+																	(GtkDialogFlags) (GTK_DIALOG_MODAL |
+																										GTK_DIALOG_DESTROY_WITH_PARENT),
+																	GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", msg);
+	gtk_dialog_add_button(GTK_DIALOG(dialog), cancelmsg, GTK_RESPONSE_CANCEL);
+	if (okmsg) {
+		gtk_dialog_add_button(GTK_DIALOG(dialog), okmsg, GTK_RESPONSE_OK);
+	}
+
+	if (x != -1) {
+		gtk_window_move(GTK_WINDOW(dialog), x, y);
+	}
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+		rv = 1;
+
+	gtk_window_get_position(GTK_WINDOW(dialog), &x, &y);
+
+	gtk_widget_destroy(dialog);
+	return rv;
+}
+
+int ghid_close_confirm_dialog()
+{
+	switch (ghid_dialog_close_confirm()) {
+	case GUI_DIALOG_CLOSE_CONFIRM_SAVE:
+		{
+			if (hid_actionl("Save", NULL)) {	/* Save failed */
+				return 0;								/* Cancel */
+			}
+			else {
+				return 1;								/* Close */
+			}
+		}
+	case GUI_DIALOG_CLOSE_CONFIRM_NOSAVE:
+		{
+			return 1;									/* Close */
+		}
+	case GUI_DIALOG_CLOSE_CONFIRM_CANCEL:
+	default:
+		{
+			return 0;									/* Cancel */
+		}
+	}
+}
+
+void ghid_report_dialog(const char *title, const char *msg)
+{
+	ghid_dialog_report(title, msg);
+}
+
+char *ghid_prompt_for(const char *msg, const char *default_string)
+{
+	char *rv;
+
+	rv = ghid_dialog_input(msg, default_string);
+	return rv;
+}
+
+/* FIXME -- implement a proper file select dialog */
+#ifdef FIXME
+char *ghid_fileselect(const char *title, const char *descr,
+											const char *default_file, const char *default_ext, const char *history_tag, int flags)
+{
+	char *rv;
+
+	rv = ghid_dialog_input(title, default_file);
+	return rv;
+}
+#endif
+
+void ghid_show_item(void *item)
+{
+	ghid_pinout_window_show(&ghid_port, (ElementTypePtr) item);
+}
+
+void ghid_beep()
+{
+	gdk_beep();
+}
+
+struct progress_dialog {
+	GtkWidget *dialog;
+	GtkWidget *message;
+	GtkWidget *progress;
+	gint response_id;
+	GMainLoop *loop;
+	gboolean destroyed;
+	gboolean started;
+	GTimer *timer;
+
+	gulong response_handler;
+	gulong destroy_handler;
+	gulong delete_handler;
+};
+
+static void run_response_handler(GtkDialog * dialog, gint response_id, gpointer data)
+{
+	struct progress_dialog *pd = data;
+
+	pd->response_id = response_id;
+}
+
+static gint run_delete_handler(GtkDialog * dialog, GdkEventAny * event, gpointer data)
+{
+	struct progress_dialog *pd = data;
+
+	pd->response_id = GTK_RESPONSE_DELETE_EVENT;
+
+	return TRUE;									/* Do not destroy */
+}
+
+static void run_destroy_handler(GtkDialog * dialog, gpointer data)
+{
+	struct progress_dialog *pd = data;
+
+	pd->destroyed = TRUE;
+}
+
+static struct progress_dialog *make_progress_dialog(void)
+{
+	struct progress_dialog *pd;
+	GtkWidget *content_area;
+	GtkWidget *alignment;
+	GtkWidget *vbox;
+
+	pd = g_new0(struct progress_dialog, 1);
+	pd->response_id = GTK_RESPONSE_NONE;
+
+	pd->dialog = gtk_dialog_new_with_buttons(_("Progress"), GTK_WINDOW(gport->top_window),
+																					 /* Modal so nothing else can get events whilst
+																					    the main mainloop isn't running */
+																					 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+																					 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+
+	gtk_window_set_deletable(GTK_WINDOW(pd->dialog), FALSE);
+	gtk_window_set_skip_pager_hint(GTK_WINDOW(pd->dialog), TRUE);
+	gtk_window_set_skip_taskbar_hint(GTK_WINDOW(pd->dialog), TRUE);
+	gtk_widget_set_size_request(pd->dialog, 300, -1);
+
+	pd->message = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(pd->message), 0., 0.);
+
+	pd->progress = gtk_progress_bar_new();
+	gtk_widget_set_size_request(pd->progress, -1, 26);
+
+	vbox = gtk_vbox_new(pcb_false, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), pd->message, pcb_true, pcb_true, 8);
+	gtk_box_pack_start(GTK_BOX(vbox), pd->progress, pcb_false, pcb_true, 8);
+
+	alignment = gtk_alignment_new(0., 0., 1., 1.);
+	gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 8, 8, 8, 8);
+	gtk_container_add(GTK_CONTAINER(alignment), vbox);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(pd->dialog));
+	gtk_box_pack_start(GTK_BOX(content_area), alignment, pcb_true, pcb_true, 0);
+
+	gtk_widget_show_all(alignment);
+
+	g_object_ref(pd->dialog);
+	gtk_window_present(GTK_WINDOW(pd->dialog));
+
+	pd->response_handler = g_signal_connect(pd->dialog, "response", G_CALLBACK(run_response_handler), pd);
+	pd->delete_handler = g_signal_connect(pd->dialog, "delete-event", G_CALLBACK(run_delete_handler), pd);
+	pd->destroy_handler = g_signal_connect(pd->dialog, "destroy", G_CALLBACK(run_destroy_handler), pd);
+
+	pd->loop = g_main_loop_new(NULL, FALSE);
+	pd->timer = g_timer_new();
+
+	return pd;
+}
+
+static void destroy_progress_dialog(struct progress_dialog *pd)
+{
+	if (pd == NULL)
+		return;
+
+	if (!pd->destroyed) {
+		g_signal_handler_disconnect(pd->dialog, pd->response_handler);
+		g_signal_handler_disconnect(pd->dialog, pd->delete_handler);
+		g_signal_handler_disconnect(pd->dialog, pd->destroy_handler);
+	}
+
+	g_timer_destroy(pd->timer);
+	g_object_unref(pd->dialog);
+	g_main_loop_unref(pd->loop);
+
+	gtk_widget_destroy(pd->dialog);
+
+	pd->loop = NULL;
+	g_free(pd);
+}
+
+static void handle_progress_dialog_events(struct progress_dialog *pd)
+{
+	GMainContext *context = g_main_loop_get_context(pd->loop);
+
+	/* Process events */
+	while (g_main_context_pending(context)) {
+		g_main_context_iteration(context, FALSE);
+	}
+}
+
+#define MIN_TIME_SEPARATION (50./1000.)	/* 50ms */
+static int ghid_progress(int so_far, int total, const char *message)
+{
+	static struct progress_dialog *pd = NULL;
+
+	/* If we are finished, destroy any dialog */
+	if (so_far == 0 && total == 0 && message == NULL) {
+		destroy_progress_dialog(pd);
+		pd = NULL;
+		return 0;
+	}
+
+	if (pd == NULL)
+		pd = make_progress_dialog();
+
+	/* We don't want to keep the underlying process too busy whilst we
+	 * process events. If we get called quickly after the last progress
+	 * update, wait a little bit before we respond - perhaps the next
+	 * time progress is reported.
+
+	 * The exception here is that we always want to process the first
+	 * batch of events after having shown the dialog for the first time
+	 */
+	if (pd->started && g_timer_elapsed(pd->timer, NULL) < MIN_TIME_SEPARATION)
+		return 0;
+
+	gtk_label_set_text(GTK_LABEL(pd->message), message);
+	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pd->progress), (double) so_far / (double) total);
+
+	handle_progress_dialog_events(pd);
+	g_timer_start(pd->timer);
+
+	pd->started = TRUE;
+
+	return (pd->response_id == GTK_RESPONSE_CANCEL || pd->response_id == GTK_RESPONSE_DELETE_EVENT) ? 1 : 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int ghid_propedit_start(void *pe, int num_props, const char *(*query)(void *pe, const char *cmd, const char *key, const char *val, int idx))
+{
+
+	ghidgui->propedit_widget = ghid_propedit_dialog_create(&ghidgui->propedit_dlg);
+	ghidgui->propedit_query = query;
+	ghidgui->propedit_pe = pe;
+	return 0;
+}
+
+static void ghid_propedit_end(void *pe)
+{
+	if (gtk_dialog_run(GTK_DIALOG(ghidgui->propedit_widget)) == GTK_RESPONSE_OK) {
+	}
+	gtk_widget_destroy(ghidgui->propedit_widget);
+}
+
+static void ghid_propedit_add_stat(void *pe, const char *propname, void *propctx, const char *most_common, const char *min, const char *max, const char *avg)
+{
+	ghid_propedit_prop_add(&ghidgui->propedit_dlg, propname, most_common, min, max, avg);
+}
+
+/* ---------------------------------------------------------------------- */
+
+
+typedef struct {
+	GtkWidget *del;
+	GtkWidget *w_name;
+	GtkWidget *w_value;
+} AttrRow;
+
+static AttrRow *attr_row = 0;
+static int attr_num_rows = 0;
+static int attr_max_rows = 0;
+static AttributeListType *attributes_list;
+static GtkWidget *attributes_dialog, *attr_table;
+
+static void attributes_delete_callback(GtkWidget * w, void *v);
+
+#define GA_RESPONSE_REVERT	1
+#define GA_RESPONSE_NEW		2
+
+static void ghid_attr_set_table_size()
+{
+	gtk_table_resize(GTK_TABLE(attr_table), attr_num_rows > 0 ? attr_num_rows : 1, 3);
+}
+
+static void ghid_attributes_need_rows(int new_max)
+{
+	if (attr_max_rows < new_max) {
+		if (attr_row)
+			attr_row = (AttrRow *) realloc(attr_row, new_max * sizeof(AttrRow));
+		else
+			attr_row = (AttrRow *) malloc(new_max * sizeof(AttrRow));
+	}
+	while (attr_max_rows < new_max) {
+		/* add [attr_max_rows] */
+		attr_row[attr_max_rows].del = gtk_button_new_with_label("del");
+		gtk_table_attach(GTK_TABLE(attr_table), attr_row[attr_max_rows].del,
+										 0, 1, attr_max_rows, attr_max_rows + 1, (GtkAttachOptions) (GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
+		g_signal_connect(G_OBJECT(attr_row[attr_max_rows].del), "clicked",
+										 G_CALLBACK(attributes_delete_callback), GINT_TO_POINTER(attr_max_rows));
+
+		attr_row[attr_max_rows].w_name = gtk_entry_new();
+		gtk_table_attach(GTK_TABLE(attr_table), attr_row[attr_max_rows].w_name,
+										 1, 2, attr_max_rows, attr_max_rows + 1, (GtkAttachOptions) (GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
+
+		attr_row[attr_max_rows].w_value = gtk_entry_new();
+		gtk_table_attach(GTK_TABLE(attr_table), attr_row[attr_max_rows].w_value,
+										 2, 3, attr_max_rows, attr_max_rows + 1, (GtkAttachOptions) (GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
+
+		attr_max_rows++;
+	}
+
+	/* Manage any previously unused rows we now need to show.  */
+	while (attr_num_rows < new_max) {
+		/* manage attr_num_rows */
+		gtk_widget_show(attr_row[attr_num_rows].del);
+		gtk_widget_show(attr_row[attr_num_rows].w_name);
+		gtk_widget_show(attr_row[attr_num_rows].w_value);
+		attr_num_rows++;
+	}
+}
+
+static void ghid_attributes_revert()
+{
+	int i;
+
+	ghid_attributes_need_rows(attributes_list->Number);
+
+	/* Unmanage any previously used rows we don't need.  */
+	while (attr_num_rows > attributes_list->Number) {
+		attr_num_rows--;
+		gtk_widget_hide(attr_row[attr_num_rows].del);
+		gtk_widget_hide(attr_row[attr_num_rows].w_name);
+		gtk_widget_hide(attr_row[attr_num_rows].w_value);
+	}
+
+	/* Fill in values */
+	for (i = 0; i < attributes_list->Number; i++) {
+		/* create row [i] */
+		gtk_entry_set_text(GTK_ENTRY(attr_row[i].w_name), attributes_list->List[i].name);
+		gtk_entry_set_text(GTK_ENTRY(attr_row[i].w_value), attributes_list->List[i].value);
+#if 0
+#endif
+	}
+	ghid_attr_set_table_size();
+}
+
+static void attributes_delete_callback(GtkWidget * w, void *v)
+{
+	int i, n;
+
+	n = GPOINTER_TO_INT(v);
+
+	for (i = n; i < attr_num_rows - 1; i++) {
+		gtk_entry_set_text(GTK_ENTRY(attr_row[i].w_name), gtk_entry_get_text(GTK_ENTRY(attr_row[i + 1].w_name)));
+		gtk_entry_set_text(GTK_ENTRY(attr_row[i].w_value), gtk_entry_get_text(GTK_ENTRY(attr_row[i + 1].w_value)));
+	}
+	attr_num_rows--;
+
+	gtk_widget_hide(attr_row[attr_num_rows].del);
+	gtk_widget_hide(attr_row[attr_num_rows].w_name);
+	gtk_widget_hide(attr_row[attr_num_rows].w_value);
+
+	ghid_attr_set_table_size();
+}
+
+static void ghid_attributes(const char *owner, AttributeListType * attrs)
+{
+	GtkWidget *content_area;
+	int response;
+
+	attributes_list = attrs;
+
+	attr_max_rows = 0;
+	attr_num_rows = 0;
+
+	attributes_dialog = gtk_dialog_new_with_buttons(owner,
+																									GTK_WINDOW(ghid_port.top_window),
+																									GTK_DIALOG_MODAL,
+																									GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+																									"Revert", GA_RESPONSE_REVERT,
+																									"New", GA_RESPONSE_NEW, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	attr_table = gtk_table_new(attrs->Number, 3, 0);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(attributes_dialog));
+	gtk_box_pack_start(GTK_BOX(content_area), attr_table, FALSE, FALSE, 0);
+
+	gtk_widget_show(attr_table);
+
+	ghid_attributes_revert();
+
+	while (1) {
+		response = gtk_dialog_run(GTK_DIALOG(attributes_dialog));
+
+		if (response == GTK_RESPONSE_CANCEL)
+			break;
+
+		if (response == GTK_RESPONSE_OK) {
+			int i;
+			/* Copy the values back */
+			for (i = 0; i < attributes_list->Number; i++) {
+				if (attributes_list->List[i].name)
+					free(attributes_list->List[i].name);
+				if (attributes_list->List[i].value)
+					free(attributes_list->List[i].value);
+			}
+			if (attributes_list->Max < attr_num_rows) {
+				int sz = attr_num_rows * sizeof(AttributeType);
+				if (attributes_list->List == NULL)
+					attributes_list->List = (AttributeType *) malloc(sz);
+				else
+					attributes_list->List = (AttributeType *) realloc(attributes_list->List, sz);
+				attributes_list->Max = attr_num_rows;
+			}
+			for (i = 0; i < attr_num_rows; i++) {
+				attributes_list->List[i].name = pcb_strdup(gtk_entry_get_text(GTK_ENTRY(attr_row[i].w_name)));
+				attributes_list->List[i].value = pcb_strdup(gtk_entry_get_text(GTK_ENTRY(attr_row[i].w_value)));
+				attributes_list->Number = attr_num_rows;
+			}
+
+			break;
+		}
+
+		if (response == GA_RESPONSE_REVERT) {
+			/* Revert */
+			ghid_attributes_revert();
+		}
+
+		if (response == GA_RESPONSE_NEW) {
+			ghid_attributes_need_rows(attr_num_rows + 1);	/* also bumps attr_num_rows */
+
+			gtk_entry_set_text(GTK_ENTRY(attr_row[attr_num_rows - 1].w_name), "");
+			gtk_entry_set_text(GTK_ENTRY(attr_row[attr_num_rows - 1].w_value), "");
+
+			ghid_attr_set_table_size();
+		}
+	}
+
+	gtk_widget_destroy(attributes_dialog);
+	free(attr_row);
+	attr_row = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+HID_DRC_GUI ghid_drc_gui = {
+	1,														/* log_drc_overview */
+	0,														/* log_drc_details */
+	ghid_drc_window_reset_message,
+	ghid_drc_window_append_violation,
+	ghid_drc_window_throw_dialog,
+};
+
+extern HID_Attribute *ghid_get_export_options(int *);
+
+
+/* ------------------------------------------------------------
+ *
+ * Actions specific to the GTK HID follow from here
+ *
+ */
+
+
+/* ------------------------------------------------------------ */
+static const char about_syntax[] = "About()";
+
+static const char about_help[] = N_("Tell the user about this version of PCB.");
+
+/* %start-doc actions About
+
+This just pops up a dialog telling the user which version of
+ at code{pcb} they're running.
+
+%end-doc */
+
+
+static int About(int argc, const char **argv, Coord x, Coord y)
+{
+	ghid_dialog_about();
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char getxy_syntax[] = "GetXY()";
+
+static const char getxy_help[] = N_("Get a coordinate.");
+
+/* %start-doc actions GetXY
+
+Prompts the user for a coordinate, if one is not already selected.
+
+%end-doc */
+
+static int GetXY(int argc, const char **argv, Coord x, Coord y)
+{
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int PointCursor(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!ghidgui)
+		return 0;
+
+	if (argc > 0)
+		ghid_point_cursor();
+	else
+		ghid_mode_cursor(conf_core.editor.mode);
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int RouteStylesChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!ghidgui || !ghidgui->route_style_selector)
+		return 0;
+
+	ghid_route_style_selector_sync
+		(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector),
+		 conf_core.design.line_thickness, conf_core.design.via_drilling_hole, conf_core.design.via_thickness, conf_core.design.clearance);
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int PCBChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!ghidgui)
+		return 0;
+
+	if (PCB != NULL)
+		ghid_window_set_name_label(PCB->Name);
+
+	if (!gport->pixmap)
+		return 0;
+
+	if (ghidgui->route_style_selector) {
+		ghid_route_style_selector_empty(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+		make_route_style_buttons(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+	}
+	RouteStylesChanged(0, NULL, 0, 0);
+
+	ghid_port_ranges_scale();
+	ghid_zoom_view_fit();
+	ghid_sync_with_new_layout();
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int LayerGroupsChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	printf(_("LayerGroupsChanged -- not implemented\n"));
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int LibraryChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	/* No need to show the library window every time it changes...
+	 *  ghid_library_window_show (&ghid_port, FALSE);
+	 */
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int Command(int argc, const char **argv, Coord x, Coord y)
+{
+	ghid_handle_user_command(TRUE);
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static char *dup_cwd()
+{
+#if defined (PATH_MAX)
+	char tmp[PATH_MAX+1];
+#else
+	char tmp[8193];
+#endif
+	return pcb_strdup(getcwd(tmp, sizeof(tmp)));
+}
+
+static int Load(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function;
+	char *name = NULL;
+
+	static gchar *current_element_dir = NULL;
+	static gchar *current_layout_dir = NULL;
+	static gchar *current_netlist_dir = NULL;
+
+	if (!current_element_dir)
+		current_element_dir = dup_cwd();
+	if (!current_layout_dir)
+		current_layout_dir = dup_cwd();
+	if (!current_netlist_dir)
+		current_netlist_dir = dup_cwd();
+
+	/* we've been given the file name */
+	if (argc > 1)
+		return hid_actionv("LoadFrom", argc, argv);
+
+	function = argc ? argv[0] : "Layout";
+
+	if (strcasecmp(function, "Netlist") == 0) {
+		name = ghid_dialog_file_select_open(_("Load netlist file"), &current_netlist_dir, conf_core.rc.file_path);
+	}
+	else if (strcasecmp(function, "ElementToBuffer") == 0) {
+		gchar *path = (gchar *)fp_default_search_path();
+		name = ghid_dialog_file_select_open(_("Load element to buffer"), &current_element_dir, path);
+	}
+	else if (strcasecmp(function, "LayoutToBuffer") == 0) {
+		name = ghid_dialog_file_select_open(_("Load layout file to buffer"), &current_layout_dir, conf_core.rc.file_path);
+	}
+	else if (strcasecmp(function, "Layout") == 0) {
+		name = ghid_dialog_file_select_open(_("Load layout file"), &current_layout_dir, conf_core.rc.file_path);
+	}
+
+	if (name) {
+		if (conf_core.rc.verbose)
+			fprintf(stderr, "Load:  Calling LoadFrom(%s, %s)\n", function, name);
+		hid_actionl("LoadFrom", function, name, NULL);
+		g_free(name);
+	}
+
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+static const char save_syntax[] =
+	"Save()\n" "Save(Layout|LayoutAs)\n" "Save(AllConnections|AllUnusedPins|ElementConnections)\n" "Save(PasteBuffer)";
+
+static const char save_help[] = N_("Save layout and/or element data to a user-selected file.");
+
+/* %start-doc actions Save
+
+This action is a GUI front-end to the core's @code{SaveTo} action
+(@pxref{SaveTo Action}).  If you happen to pass a filename, like
+ at code{SaveTo}, then @code{SaveTo} is called directly.  Else, the
+user is prompted for a filename to save, and then @code{SaveTo} is
+called with that filename.
+
+%end-doc */
+
+static int Save(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function;
+	char *name;
+	const char *prompt;
+	pcb_io_formats_t avail;
+	const char **formats_param = NULL;
+	int fmt, *fmt_param = NULL;
+
+	static gchar *current_dir = NULL;
+
+	if (!current_dir)
+		current_dir = dup_cwd();
+
+	if (argc > 1)
+		return hid_actionv("SaveTo", argc, argv);
+
+	function = argc ? argv[0] : "Layout";
+
+	if (strcasecmp(function, "Layout") == 0)
+		if (PCB->Filename)
+			return hid_actionl("SaveTo", "Layout", PCB->Filename, NULL);
+
+	if (strcasecmp(function, "PasteBuffer") == 0) {
+		prompt = _("Save element as");
+		if (pcb_io_list(&avail, PCB_IOT_BUFFER, 1, 1) > 0) {
+			formats_param = (const char **)avail.digest;
+			fmt_param = &fmt;
+			fmt = 0;
+		}
+		else {
+			Message(PCB_MSG_DEFAULT, "Error: no IO plugin avaialble for saving a buffer.");
+			return -1;
+		}
+	}
+	else {
+		prompt = _("Save layout as");
+		if (pcb_io_list(&avail, PCB_IOT_PCB, 1, 1) > 0) {
+			formats_param = (const char **)avail.digest;
+			fmt_param = &fmt;
+			fmt = 0;
+		}
+		else {
+			Message(PCB_MSG_DEFAULT, "Error: no IO plugin avaialble for saving a buffer.");
+			return -1;
+		}
+	}
+
+	name = ghid_dialog_file_select_save(prompt, &current_dir, PCB->Filename, conf_core.rc.file_path, formats_param, fmt_param);
+
+	if (name) {
+		if (conf_core.rc.verbose)
+			fprintf(stderr, "Save:  Calling SaveTo(%s, %s)\n", function, name);
+
+		if (strcasecmp(function, "PasteBuffer") == 0) {
+			hid_actionl("PasteBuffer", "Save", name, avail.plug[fmt]->description, "1", NULL);
+			pcb_io_list_free(&avail);
+		}
+		else {
+			const char *sfmt = NULL;
+			/*
+			 * if we got this far and the function is Layout, then
+			 * we really needed it to be a LayoutAs.  Otherwise
+			 * ActionSaveTo() will ignore the new file name we
+			 * just obtained.
+			 */
+			if (fmt_param != NULL)
+				sfmt = avail.plug[fmt]->description;
+			if (strcasecmp(function, "Layout") == 0)
+				hid_actionl("SaveTo", "LayoutAs", name, sfmt, NULL);
+			else
+				hid_actionl("SaveTo", function, name, sfmt, NULL);
+		}
+		g_free(name);
+	}
+	else {
+		return 1;
+	}
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+static const char swapsides_syntax[] = "SwapSides(|v|h|r)";
+
+static const char swapsides_help[] = N_("Swaps the side of the board you're looking at.");
+
+/* %start-doc actions SwapSides
+
+This action changes the way you view the board.
+
+ at table @code
+
+ at item v
+Flips the board over vertically (up/down).
+
+ at item h
+Flips the board over horizontally (left/right), like flipping pages in
+a book.
+
+ at item r
+Rotates the board 180 degrees without changing sides.
+
+ at end table
+
+If no argument is given, the board isn't moved but the opposite side
+is shown.
+
+Normally, this action changes which pads and silk layer are drawn as
+pcb_true silk, and which are drawn as the "invisible" layer.  It also
+determines which solder mask you see.
+
+As a special case, if the layer group for the side you're looking at
+is visible and currently active, and the layer group for the opposite
+is not visible (i.e. disabled), then this action will also swap which
+layer group is visible and active, effectively swapping the ``working
+side'' of the board.
+
+%end-doc */
+
+
+static int SwapSides(int argc, const char **argv, Coord x, Coord y)
+{
+	int active_group = GetLayerGroupNumberByNumber(LayerStack[0]);
+	int comp_group = GetLayerGroupNumberByNumber(component_silk_layer);
+	int solder_group = GetLayerGroupNumberByNumber(solder_silk_layer);
+	pcb_bool comp_on = LAYER_PTR(PCB->LayerGroups.Entries[comp_group][0])->On;
+	pcb_bool solder_on = LAYER_PTR(PCB->LayerGroups.Entries[solder_group][0])->On;
+
+	if (argc > 0) {
+		switch (argv[0][0]) {
+		case 'h':
+		case 'H':
+			ghid_flip_view(gport->pcb_x, gport->pcb_y, pcb_true, pcb_false);
+			break;
+		case 'v':
+		case 'V':
+			ghid_flip_view(gport->pcb_x, gport->pcb_y, pcb_false, pcb_true);
+			break;
+		case 'r':
+		case 'R':
+			ghid_flip_view(gport->pcb_x, gport->pcb_y, pcb_true, pcb_true);
+			conf_toggle_editor(show_solder_side); /* Swapped back below */
+			break;
+		default:
+			return 1;
+		}
+	}
+
+	conf_toggle_editor(show_solder_side);
+
+	if ((active_group == comp_group && comp_on && !solder_on) || (active_group == solder_group && solder_on && !comp_on)) {
+		pcb_bool new_solder_vis = conf_core.editor.show_solder_side;
+
+		ChangeGroupVisibility(PCB->LayerGroups.Entries[comp_group][0], !new_solder_vis, !new_solder_vis);
+		ChangeGroupVisibility(PCB->LayerGroups.Entries[solder_group][0], new_solder_vis, new_solder_vis);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char print_syntax[] = "Print()";
+
+static const char print_help[] = N_("Print the layout.");
+
+/* %start-doc actions Print
+
+This will find the default printing HID, prompt the user for its
+options, and print the layout.
+
+%end-doc */
+
+static int Print(int argc, const char **argv, Coord x, Coord y)
+{
+	HID **hids;
+	int i;
+	HID *printer = NULL;
+
+	hids = hid_enumerate();
+	for (i = 0; hids[i]; i++) {
+		if (hids[i]->printer)
+			printer = hids[i];
+	}
+
+	if (printer == NULL) {
+		gui->log(_("Can't find a suitable printer HID"));
+		return -1;
+	}
+
+	/* check if layout is empty */
+	if (!IsDataEmpty(PCB->Data)) {
+		ghid_dialog_print(printer);
+	}
+	else
+		gui->log(_("Can't print empty layout"));
+
+	return 0;
+}
+
+
+/* ------------------------------------------------------------ */
+
+static HID_Attribute printer_calibrate_attrs[] = {
+	{N_("Enter Values here:"), "",
+	 HID_Label, 0, 0, {0, 0, 0}, 0, 0},
+	{N_("x-calibration"), N_("X scale for calibrating your printer"),
+	 HID_Real, 0.5, 25, {0, 0, 1.00}, 0, 0},
+	{N_("y-calibration"), N_("Y scale for calibrating your printer"),
+	 HID_Real, 0.5, 25, {0, 0, 1.00}, 0, 0}
+};
+
+static HID_Attr_Val printer_calibrate_values[3];
+
+static const char printcalibrate_syntax[] = "PrintCalibrate()";
+
+static const char printcalibrate_help[] = N_("Calibrate the printer.");
+
+/* %start-doc actions PrintCalibrate
+
+This will print a calibration page, which you would measure and type
+the measurements in, so that future printouts will be more precise.
+
+%end-doc */
+
+static int PrintCalibrate(int argc, const char **argv, Coord x, Coord y)
+{
+	HID *printer = hid_find_printer();
+	printer->calibrate(0.0, 0.0);
+
+	if (gui->attribute_dialog(printer_calibrate_attrs, 3,
+														printer_calibrate_values,
+														_("Printer Calibration Values"), _("Enter calibration values for your printer")))
+		return 1;
+	printer->calibrate(printer_calibrate_values[1].real_value, printer_calibrate_values[2].real_value);
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static int Export(int argc, const char **argv, Coord x, Coord y)
+{
+
+	/* check if layout is empty */
+	if (!IsDataEmpty(PCB->Data)) {
+		ghid_dialog_export();
+	}
+	else
+		gui->log(_("Can't export empty layout"));
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static int Benchmark(int argc, const char **argv, Coord x, Coord y)
+{
+	int i = 0;
+	time_t start, end;
+	GdkDisplay *display;
+
+	display = gdk_drawable_get_display(gport->drawable);
+
+	gdk_display_sync(display);
+	time(&start);
+	do {
+		ghid_invalidate_all();
+		gdk_window_process_updates(gtk_widget_get_window(gport->drawing_area), FALSE);
+		time(&end);
+		i++;
+	}
+	while (end - start < 10);
+
+	printf(_("%g redraws per second\n"), i / 10.0);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char center_syntax[] = "Center()\n";
+
+static const char center_help[] = N_("Moves the pointer to the center of the window.");
+
+/* %start-doc actions Center
+
+Move the pointer to the center of the window, but only if it's
+currently within the window already.
+
+%end-doc */
+
+static int Center(int argc, const char **argv, Coord pcb_x, Coord pcb_y)
+{
+	GdkDisplay *display;
+	GdkScreen *screen;
+	int offset_x, offset_y;
+	int widget_x, widget_y;
+	int pointer_x, pointer_y;
+
+	if (argc != 0)
+		AFAIL(center);
+
+	/* Aim to put the given x, y PCB coordinates in the center of the widget */
+	widget_x = gport->width / 2;
+	widget_y = gport->height / 2;
+
+	ghid_pan_view_abs(pcb_x, pcb_y, widget_x, widget_y);
+
+	/* Now move the mouse pointer to the place where the board location
+	 * actually ended up.
+	 *
+	 * XXX: Should only do this if we confirm we are inside our window?
+	 */
+
+	ghid_pcb_to_event_coords(pcb_x, pcb_y, &widget_x, &widget_y);
+	gdk_window_get_origin(gtk_widget_get_window(gport->drawing_area), &offset_x, &offset_y);
+
+	pointer_x = offset_x + widget_x;
+	pointer_y = offset_y + widget_y;
+
+	display = gdk_display_get_default();
+	screen = gdk_display_get_default_screen(display);
+	gdk_display_warp_pointer(display, screen, pointer_x, pointer_y);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char cursor_syntax[] = "Cursor(Type,DeltaUp,DeltaRight,Units)";
+
+static const char cursor_help[] = N_("Move the cursor.");
+
+/* %start-doc actions Cursor
+
+This action moves the mouse cursor.  Unlike other actions which take
+coordinates, this action's coordinates are always relative to the
+user's view of the board.  Thus, a positive @var{DeltaUp} may move the
+cursor towards the board origin if the board is inverted.
+
+Type is one of @samp{Pan} or @samp{Warp}.  @samp{Pan} causes the
+viewport to move such that the crosshair is under the mouse cursor.
+ at samp{Warp} causes the mouse cursor to move to be above the crosshair.
+
+ at var{Units} can be one of the following:
+
+ at table @samp
+
+ at item mil
+ at itemx mm
+The cursor is moved by that amount, in board units.
+
+ at item grid
+The cursor is moved by that many grid points.
+
+ at item view
+The values are percentages of the viewport's view.  Thus, a pan of
+ at samp{100} would scroll the viewport by exactly the width of the
+current view.
+
+ at item board
+The values are percentages of the board size.  Thus, a move of
+ at samp{50,50} moves you halfway across the board.
+
+ at end table
+
+%end-doc */
+
+static int CursorAction(int argc, const char **argv, Coord x, Coord y)
+{
+	UnitList extra_units_x = {
+		{"grid", PCB->Grid, 0},
+		{"view", gport->view.width, UNIT_PERCENT},
+		{"board", PCB->MaxWidth, UNIT_PERCENT},
+		{"", 0, 0}
+	};
+	UnitList extra_units_y = {
+		{"grid", PCB->Grid, 0},
+		{"view", gport->view.height, UNIT_PERCENT},
+		{"board", PCB->MaxHeight, UNIT_PERCENT},
+		{"", 0, 0}
+	};
+	int pan_warp = HID_SC_DO_NOTHING;
+	double dx, dy;
+
+	if (argc != 4)
+		AFAIL(cursor);
+
+	if (strcasecmp(argv[0], "pan") == 0)
+		pan_warp = HID_SC_PAN_VIEWPORT;
+	else if (strcasecmp(argv[0], "warp") == 0)
+		pan_warp = HID_SC_WARP_POINTER;
+	else
+		AFAIL(cursor);
+
+	dx = GetValueEx(argv[1], argv[3], NULL, extra_units_x, "", NULL);
+	if (conf_core.editor.view.flip_x)
+		dx = -dx;
+	dy = GetValueEx(argv[2], argv[3], NULL, extra_units_y, "", NULL);
+	if (!conf_core.editor.view.flip_y)
+		dy = -dy;
+
+	EventMoveCrosshair(Crosshair.X + dx, Crosshair.Y + dy);
+	gui->set_crosshair(Crosshair.X, Crosshair.Y, pan_warp);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char dowindows_syntax[] = "DoWindows(1|2|3|4|5|6|7,[false])\n" "DoWindows(Layout|Library|Log|Netlist|Preferences|DRC,[false])";
+
+static const char dowindows_help[] = N_("Open various GUI windows. With false, do not raise the window (no focus stealing).");
+
+/* %start-doc actions DoWindows
+
+ at table @code
+
+ at item 1
+ at itemx Layout
+Open the layout window.  Since the layout window is always shown
+anyway, this has no effect.
+
+ at item 2
+ at itemx Library
+Open the library window.
+
+ at item 3
+ at itemx Log
+Open the log window.
+
+ at item 4
+ at itemx Netlist
+Open the netlist window.
+
+ at item 5
+ at itemx Preferences
+Open the preferences window.
+
+ at item 6
+ at itemx DRC
+Open the DRC violations window.
+
+ at item 7
+ at itemx Search
+Open the advanced search window.
+
+ at end table
+
+%end-doc */
+
+static int DoWindows(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *a = argc >= 1 ? argv[0] : "";
+	gboolean raise = TRUE;
+
+	if (argc >= 2) {
+		char c = tolower((argv[1])[0]);
+		if ((c == 'n') || (c == 'f') || (c == '0'))
+			raise = FALSE;
+	}
+
+	if (strcmp(a, "1") == 0 || strcasecmp(a, "Layout") == 0) {
+	}
+	else if (strcmp(a, "2") == 0 || strcasecmp(a, "Library") == 0) {
+		ghid_library_window_show(gport, raise);
+	}
+	else if (strcmp(a, "3") == 0 || strcasecmp(a, "Log") == 0) {
+		ghid_log_window_show(raise);
+	}
+	else if (strcmp(a, "4") == 0 || strcasecmp(a, "Netlist") == 0) {
+		ghid_netlist_window_show(gport, raise);
+	}
+	else if (strcmp(a, "5") == 0 || strcasecmp(a, "Preferences") == 0) {
+		ghid_config_window_show();
+	}
+	else if (strcmp(a, "6") == 0 || strcasecmp(a, "DRC") == 0) {
+		ghid_drc_window_show(raise);
+	}
+	else if (strcmp(a, "7") == 0 || strcasecmp(a, "search") == 0) {
+		ghid_search_window_show(raise);
+	}
+	else {
+		AFAIL(dowindows);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char setunits_syntax[] = "SetUnits(mm|mil)";
+
+static const char setunits_help[] = N_("Set the default measurement units.");
+
+/* %start-doc actions SetUnits
+
+ at table @code
+
+ at item mil
+Sets the display units to mils (1/1000 inch).
+
+ at item mm
+Sets the display units to millimeters.
+
+ at end table
+
+%end-doc */
+
+static int SetUnits(int argc, const char **argv, Coord x, Coord y)
+{
+	const Unit *new_unit;
+	if (argc == 0)
+		return 0;
+
+	new_unit = get_unit_struct(argv[0]);
+	if (new_unit != NULL && new_unit->allow != NO_PRINT) {
+		conf_set(CFR_DESIGN, "editor/grid_unit", -1, argv[0], POL_OVERWRITE);
+		AttributePut(PCB, "PCB::grid::unit", argv[0]);
+	}
+
+	ghid_config_handle_units_changed();
+
+	ghid_set_status_line_label();
+
+	/* FIXME ?
+	 * lesstif_sizes_reset ();
+	 * lesstif_styles_update_values ();
+	 */
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char scroll_syntax[] = "Scroll(up|down|left|right, [div])";
+
+static const char scroll_help[] = N_("Scroll the viewport.");
+
+/* % start-doc actions Scroll
+
+ at item up|down|left|right
+Specifies the direction to scroll
+
+ at item div
+Optional.  Specifies how much to scroll by.  The viewport is scrolled
+by 1/div of what is visible, so div = 1 scrolls a whole page. If not
+default is given, div=40.
+
+%end-doc */
+
+static int ScrollAction(int argc, const char **argv, Coord x, Coord y)
+{
+	gdouble dx = 0.0, dy = 0.0;
+	int div = 40;
+
+	if (!ghidgui)
+		return 0;
+
+	if (argc != 1 && argc != 2)
+		AFAIL(scroll);
+
+	if (argc == 2)
+		div = atoi(argv[1]);
+
+	if (strcasecmp(argv[0], "up") == 0)
+		dy = -gport->view.height / div;
+	else if (strcasecmp(argv[0], "down") == 0)
+		dy = gport->view.height / div;
+	else if (strcasecmp(argv[0], "right") == 0)
+		dx = gport->view.width / div;
+	else if (strcasecmp(argv[0], "left") == 0)
+		dx = -gport->view.width / div;
+	else
+		AFAIL(scroll);
+
+	ghid_pan_view_rel(dx, dy);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char pan_syntax[] = "Pan([thumb], Mode)";
+
+static const char pan_help[] =
+N_("Start or stop panning (Mode = 1 to start, 0 to stop)\n" "Optional thumb argument is ignored for now in gtk hid.\n");
+
+/* %start-doc actions Pan
+
+Start or stop panning.  To start call with Mode = 1, to stop call with
+Mode = 0.
+
+%end-doc */
+
+static int PanAction(int argc, const char **argv, Coord x, Coord y)
+{
+	int mode;
+
+	if (!ghidgui)
+		return 0;
+
+	if (argc != 1 && argc != 2)
+		AFAIL(pan);
+
+	if (argc == 1)
+		mode = atoi(argv[0]);
+	else {
+		mode = atoi(argv[1]);
+		Message(PCB_MSG_DEFAULT, _("The gtk gui currently ignores the optional first argument "
+							"to the Pan action.\nFeel free to provide patches.\n"));
+	}
+
+	gport->panning = mode;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char popup_syntax[] = "Popup(MenuName, [Button])";
+
+static const char popup_help[] =
+N_("Bring up the popup menu specified by @code{MenuName}.\n"
+	 "If called by a mouse event then the mouse button number\n" "must be specified as the optional second argument.");
+
+/* %start-doc actions Popup
+
+This just pops up the specified menu.  The menu must have been defined
+in the popups subtree in the menu lht file.
+
+%end-doc */
+static int Popup(int argc, const char **argv, Coord x, Coord y)
+{
+	GtkMenu *menu = NULL;
+	char name[256];
+
+	if (argc != 1 && argc != 2)
+		AFAIL(popup);
+
+	if (strlen(argv[0]) < sizeof(name)-32) {
+		lht_node_t *menu_node;
+		sprintf(name, "/popups/%s", argv[0]);
+		menu_node = hid_cfg_get_menu(ghid_cfg, name);
+		if (menu_node != NULL)
+			menu = menu_node->user_data;
+	}
+
+	if (!GTK_IS_MENU(menu)) {
+		Message(PCB_MSG_DEFAULT, _("The specified popup menu \"%s\" has not been defined.\n"), argv[0]);
+		return 1;
+	}
+	else {
+		ghidgui->in_popup = TRUE;
+		gtk_widget_grab_focus(ghid_port.drawing_area);
+		gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
+	}
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char importgui_syntax[] = "ImportGUI()";
+
+static const char importgui_help[] = N_("Asks user which schematics to import into PCB.\n");
+
+/* %start-doc actions ImportGUI
+
+Asks user which schematics to import into PCB.
+
+%end-doc */
+
+
+static int ImportGUI(int argc, const char **argv, Coord x, Coord y)
+{
+	char *name = NULL;
+	static gchar *current_layout_dir = NULL;
+	static int I_am_recursing = 0;
+	int rv;
+
+	if (!current_layout_dir)
+		current_layout_dir = dup_cwd();
+
+	if (I_am_recursing)
+		return 1;
+
+
+	name = ghid_dialog_file_select_open(_("Load schematics"), &current_layout_dir, conf_core.rc.file_path);
+
+#ifdef DEBUG
+	printf("File selected = %s\n", name);
+#endif
+
+	AttributePut(PCB, "import::src0", name);
+	free(name);
+
+	I_am_recursing = 1;
+	rv = hid_action("Import");
+	I_am_recursing = 0;
+
+	return rv;
+}
+
+/* ------------------------------------------------------------ */
+static const char savewingeo_syntax[] = "SaveWindowGeometry()";
+
+static const char savewingeo_help[] = N_("Saves window geometry in the config.\n");
+
+static int SaveWinGeo(int argc, const char **argv, Coord x, Coord y)
+{
+	ghid_wgeo_save(1, 0);
+	return 0;
+}
+
+
+
+/* ------------------------------------------------------------ */
+static int Busy(int argc, const char **argv, Coord x, Coord y)
+{
+	ghid_watch_cursor();
+	return 0;
+}
+
+HID_Action ghid_main_action_list[] = {
+	{"About", 0, About, about_help, about_syntax}
+	,
+	{"Benchmark", 0, Benchmark}
+	,
+	{"Busy", 0, Busy}
+	,
+	{"Center", N_("Click on a location to center"), Center, center_help, center_syntax}
+	,
+	{"Command", 0, Command}
+	,
+	{"Cursor", 0, CursorAction, cursor_help, cursor_syntax}
+	,
+	{"DoWindows", 0, DoWindows, dowindows_help, dowindows_syntax}
+	,
+	{"Export", 0, Export}
+	,
+	{"GetXY", "", GetXY, getxy_help, getxy_syntax}
+	,
+	{"ImportGUI", 0, ImportGUI, importgui_help, importgui_syntax}
+	,
+	{"LayerGroupsChanged", 0, LayerGroupsChanged}
+	,
+	{"LibraryChanged", 0, LibraryChanged}
+	,
+	{"Load", 0, Load}
+	,
+	{"Pan", 0, PanAction, pan_help, pan_syntax}
+	,
+	{"PCBChanged", 0, PCBChanged}
+	,
+	{"PointCursor", 0, PointCursor}
+	,
+	{"Popup", 0, Popup, popup_help, popup_syntax}
+	,
+	{"Print", 0, Print,
+	 print_help, print_syntax}
+	,
+	{"PrintCalibrate", 0, PrintCalibrate,
+	 printcalibrate_help, printcalibrate_syntax}
+	,
+	{"RouteStylesChanged", 0, RouteStylesChanged}
+	,
+	{"Save", 0, Save, save_help, save_syntax}
+	,
+	{"SaveWindowGeometry", 0, SaveWinGeo, savewingeo_help, savewingeo_syntax}
+	,
+	{"Scroll", N_("Click on a place to scroll"), ScrollAction, scroll_help, scroll_syntax}
+	,
+	{"SetUnits", 0, SetUnits, setunits_help, setunits_syntax}
+	,
+	{"SwapSides", 0, SwapSides, swapsides_help, swapsides_syntax}
+	,
+	{"Zoom", N_("Click on zoom focus"), Zoom, zoom_help, zoom_syntax}
+};
+
+REGISTER_ACTIONS(ghid_main_action_list, ghid_cookie)
+
+#include "dolists.h"
+/*
+ * We will need these for finding the windows installation
+ * directory.  Without that we can't find our fonts and
+ * footprint libraries.
+ */
+#ifdef WIN32
+#include <windows.h>
+#include <winreg.h>
+#endif
+
+HID ghid_hid;
+
+static void init_conf_watch(conf_hid_callbacks_t *cbs, const char *path, void (*func)(conf_native_t *))
+{
+	conf_native_t *n = conf_get_field(path);
+	if (n != NULL) {
+		memset(cbs, 0, sizeof(conf_hid_callbacks_t));
+		cbs->val_change_post = func;
+		conf_hid_set_cb(n, ghid_conf_id, cbs);
+	}
+}
+
+static void ghid_conf_regs()
+{
+	static conf_hid_callbacks_t cbs_refraction, cbs_direction, cbs_fullscreen;
+
+	init_conf_watch(&cbs_direction,   "editor/all_direction_lines",  ghid_confchg_all_direction_lines);
+	init_conf_watch(&cbs_refraction,  "editor/line_refraction",      ghid_confchg_line_refraction);
+	init_conf_watch(&cbs_fullscreen,  "editor/fullscreen",           ghid_confchg_fullscreen);
+}
+
+void hid_hid_gtk_uninit()
+{
+	event_unbind_allcookie(ghid_cookie);
+	conf_hid_unreg(ghid_cookie);
+	conf_hid_unreg(ghid_menu_cookie);
+}
+
+pcb_uninit_t hid_hid_gtk_init()
+{
+#ifdef WIN32
+	char *tmps;
+	char *share_dir;
+	char *loader_cache;
+	FILE *loader_file;
+#endif
+
+#ifdef WIN32
+	tmps = g_win32_get_package_installation_directory(PACKAGE "-" VERSION, NULL);
+#define REST_OF_PATH G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S PACKAGE
+#define REST_OF_CACHE G_DIR_SEPARATOR_S "loaders.cache"
+	share_dir = (char *) malloc(strlen(tmps) + strlen(REST_OF_PATH) + 1);
+	sprintf(share_dir, "%s%s", tmps, REST_OF_PATH);
+
+	/* Point to our gdk-pixbuf loader cache.  */
+	loader_cache = (char *) malloc(strlen("bindir_todo12") + strlen(REST_OF_CACHE) + 1);
+	sprintf(loader_cache, "%s%s", "bindir_todo12", REST_OF_CACHE);
+	loader_file = fopen(loader_cache, "r");
+	if (loader_file) {
+		fclose(loader_file);
+		g_setenv("GDK_PIXBUF_MODULE_FILE", loader_cache, TRUE);
+	}
+
+	free(tmps);
+#undef REST_OF_PATH
+	printf("\"Share\" installation path is \"%s\"\n", "share_dir_todo12");
+#endif
+
+	memset(&ghid_hid, 0, sizeof(HID));
+
+	common_nogui_init(&ghid_hid);
+	common_draw_helpers_init(&ghid_hid);
+
+	ghid_hid.struct_size = sizeof(HID);
+	ghid_hid.name = "gtk";
+	ghid_hid.description = "Gtk - The Gimp Toolkit";
+	ghid_hid.gui = 1;
+	ghid_hid.poly_after = 1;
+
+	ghid_hid.do_export = ghid_do_export;
+	ghid_hid.do_exit = ghid_do_exit;
+	ghid_hid.parse_arguments = ghid_parse_arguments;
+	ghid_hid.invalidate_lr = ghid_invalidate_lr;
+	ghid_hid.invalidate_all = ghid_invalidate_all;
+	ghid_hid.notify_crosshair_change = ghid_notify_crosshair_change;
+	ghid_hid.notify_mark_change = ghid_notify_mark_change;
+	ghid_hid.set_layer = ghid_set_layer;
+	ghid_hid.make_gc = ghid_make_gc;
+	ghid_hid.destroy_gc = ghid_destroy_gc;
+	ghid_hid.use_mask = ghid_use_mask;
+	ghid_hid.set_color = ghid_set_color;
+	ghid_hid.set_line_cap = ghid_set_line_cap;
+	ghid_hid.set_line_width = ghid_set_line_width;
+	ghid_hid.set_draw_xor = ghid_set_draw_xor;
+	ghid_hid.draw_line = ghid_draw_line;
+	ghid_hid.draw_arc = ghid_draw_arc;
+	ghid_hid.draw_rect = ghid_draw_rect;
+	ghid_hid.fill_circle = ghid_fill_circle;
+	ghid_hid.fill_polygon = ghid_fill_polygon;
+	ghid_hid.fill_rect = ghid_fill_rect;
+
+	ghid_hid.calibrate = ghid_calibrate;
+	ghid_hid.shift_is_pressed = ghid_shift_is_pressed;
+	ghid_hid.control_is_pressed = ghid_control_is_pressed;
+	ghid_hid.mod1_is_pressed = ghid_mod1_is_pressed, ghid_hid.get_coords = ghid_get_coords;
+	ghid_hid.set_crosshair = ghid_set_crosshair;
+	ghid_hid.add_timer = ghid_add_timer;
+	ghid_hid.stop_timer = ghid_stop_timer;
+	ghid_hid.watch_file = ghid_watch_file;
+	ghid_hid.unwatch_file = ghid_unwatch_file;
+	ghid_hid.add_block_hook = ghid_add_block_hook;
+	ghid_hid.stop_block_hook = ghid_stop_block_hook;
+
+	ghid_hid.log = ghid_log;
+	ghid_hid.logv = ghid_logv;
+	ghid_hid.confirm_dialog = ghid_confirm_dialog;
+	ghid_hid.close_confirm_dialog = ghid_close_confirm_dialog;
+	ghid_hid.report_dialog = ghid_report_dialog;
+	ghid_hid.prompt_for = ghid_prompt_for;
+	ghid_hid.fileselect = ghid_fileselect;
+	ghid_hid.attribute_dialog = ghid_attribute_dialog;
+	ghid_hid.show_item = ghid_show_item;
+	ghid_hid.beep = ghid_beep;
+	ghid_hid.progress = ghid_progress;
+	ghid_hid.drc_gui = &ghid_drc_gui, ghid_hid.edit_attributes = ghid_attributes;
+
+	ghid_hid.request_debug_draw = ghid_request_debug_draw;
+	ghid_hid.flush_debug_draw = ghid_flush_debug_draw;
+	ghid_hid.finish_debug_draw = ghid_finish_debug_draw;
+
+	ghid_hid.notify_save_pcb = ghid_notify_save_pcb;
+	ghid_hid.notify_filename_changed = ghid_notify_filename_changed;
+
+	ghid_hid.propedit_start = ghid_propedit_start;
+	ghid_hid.propedit_end = ghid_propedit_end;
+	ghid_hid.propedit_add_stat = ghid_propedit_add_stat;
+/*	ghid_hid.propedit_add_prop = ghid_propedit_add_prop;*/
+/*	ghid_hid.propedit_add_value = ghid_propedit_add_value;*/
+
+
+	ghid_conf_id = conf_hid_reg(ghid_cookie, NULL);
+	ghid_menuconf_id = conf_hid_reg(ghid_menu_cookie, NULL);
+	ghid_conf_regs();
+
+	ghid_hid.create_menu = ghid_create_menu;
+	ghid_hid.remove_menu = ghid_remove_menu;
+
+	ghid_hid.usage = ghid_usage;
+
+	hid_register_hid(&ghid_hid);
+
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_hid_gtk, field,isarray,type_name,cpath,cname,desc,flags);
+#include "hid_gtk_conf_fields.h"
+
+	event_bind(EVENT_SAVE_PRE, ghid_conf_save_pre_wgeo, NULL, ghid_cookie);
+	event_bind(EVENT_LOAD_POST, ghid_conf_load_post_wgeo, NULL, ghid_cookie);
+
+	return hid_hid_gtk_uninit;
+}
+
+int gtkhid_active = 0;
+
+void gtkhid_begin(void)
+{
+	REGISTER_ACTIONS(ghid_main_action_list, ghid_cookie)
+	REGISTER_ACTIONS(ghid_netlist_action_list, ghid_cookie)
+	REGISTER_ACTIONS(ghid_log_action_list, ghid_cookie)
+	REGISTER_ACTIONS(gtk_topwindow_action_list, ghid_cookie)
+	REGISTER_ACTIONS(ghid_menu_action_list, ghid_cookie)
+	gtkhid_active = 1;
+}
+
+void gtkhid_end(void)
+{
+	hid_remove_actions_by_cookie(ghid_cookie);
+	hid_remove_attributes_by_cookie(ghid_cookie);
+	gtkhid_active = 0;
+}
diff --git a/src_plugins/hid_gtk/gtkhid.h b/src_plugins/hid_gtk/gtkhid.h
new file mode 100644
index 0000000..8fa9ed6
--- /dev/null
+++ b/src_plugins/hid_gtk/gtkhid.h
@@ -0,0 +1,14 @@
+#ifndef PCB_HID_GTK_GTKHID_H
+#define PCB_HID_GTK_GTKHID_H
+
+#include "conf_hid.h"
+
+void ghid_notify_gui_is_up(void);
+
+extern int gtkhid_active;
+void gtkhid_begin(void);
+void gtkhid_end(void);
+
+extern conf_hid_id_t ghid_conf_id;
+
+#endif /* PCB_HID_GTK_GTKHID_H */
diff --git a/src_plugins/hid_gtk/gui-command-window.c b/src_plugins/hid_gtk/gui-command-window.c
new file mode 100644
index 0000000..94adf28
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-command-window.c
@@ -0,0 +1,431 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "gui.h"
+#include <gdk/gdkkeysyms.h>
+
+#include "crosshair.h"
+#include "hid_actions.h"
+#include "hid_gtk_conf.h"
+
+static GtkWidget *command_window;
+static GtkWidget *combo_vbox;
+static GList *history_list;
+static gchar *command_entered;
+GMainLoop *ghid_entry_loop;
+
+
+/* gui-command-window.c provides two interfaces for getting user input
+|  for executing a command.
+|
+|  As the Xt PCB was ported to Gtk, the traditional user entry in the
+|  status line window presented some focus problems which require that
+|  there can be no menu key shortcuts that might be a key the user would
+|  type in.  It also requires a coordinating flag so the drawing area
+|  won't grab focus while the command entry is up.
+|
+|  I thought the interface should be cleaner, so so I made an alternate
+|  command window interface which works better I think as a gui interface.
+|  The user must focus onto the command window, but since it's a separate
+|  window, there's no confusion.  It has the restriction that objects to
+|  be operated on must be selected, but that actually seems a better user
+|  interface than one where typing into one location requires the user to
+|  be careful about which object might be under the cursor somewhere else.
+|
+|  In any event, both interfaces are here to work with.
+*/
+
+
+	/* When using a command window for command entry, provide a quick and
+	   |  abbreviated reference to available commands.
+	   |  This is currently just a start and can be expanded if it proves useful.
+	 */
+static const gchar *command_ref_text[] = {
+	N_("Common commands easily accessible via the gui may not be included here.\n"),
+	"\n",
+	N_("In user commands below, 'size' values may be absolute or relative\n"
+		 "if preceded by a '+' or '-'.  Where 'units' are indicated, use \n"
+		 "'mil' or 'mm' otherwise PCB internal units will be used.\n"),
+	"\n",
+	"<b>changesize(target, size, units)\n",
+	"\ttarget = {selectedlines | selectedpins | selectedvias | selectedpads \n"
+		"\t\t\t| selectedtexts | selectednames | selectedelements | selected}\n",
+	"\n",
+	"<b>changedrillsize(target, size, units)\n",
+	"\ttarget = {selectedpins | selectedvias | selectedobjects | selected}\n",
+	"\n",
+	"<b>changeclearsize(target, size, units)\n",
+	"\ttarget = {selectedpins | selectedpads | selectedvias | selectedlines\n"
+		"\t\t\t| selectedarcs | selectedobjects | selected}\n",
+	N_("\tChanges the clearance of objects.\n"),
+	"\n",
+	"<b>setvalue(target, size, units)\n",
+	"\ttarget = {grid | zoom | line | textscale | viadrillinghole\n" "\t\t\t| viadrillinghole | via}\n",
+	N_("\tChanges values.  Omit 'units' for 'grid' and 'zoom'.\n"),
+	"\n",
+	"<b>changejoin(target)\n",
+	"\ttarget = {object | selectedlines | selectedarcs | selected}\n",
+	N_("\tChanges the join (clearance through polygons) of objects.\n"),
+	"\n",
+	"<b>changesquare(target)\n",
+	"<b>setsquare(target)\n",
+	"<b>clearsquare(target)\n",
+	"\ttarget = {object | selectedelements | selectedpins | selected}\n",
+	N_("\tToggles, sets, or clears the square flag of objects.\n"),
+	"\n",
+	"<b>changeoctagon(target)\n",
+	"<b>setoctagon(target)\n",
+	"<b>clearoctagon(target)\n",
+	"\ttarget = {object | selectedelements | selectedpins selectedvias | selected}\n",
+	N_("\tToggles, sets, or clears the octagon flag of objects.\n"),
+	"\n",
+	"<b>changehole(target)\n",
+	"\ttarget = {object | selectedvias | selected}\n",
+	N_("\tChanges the hole flag of objects.\n"),
+	"\n",
+	"<b>flip(target)\n",
+	"\ttarget = {object | selectedelements | selected}\n",
+	N_("\tFlip elements to the opposite side of the board.\n"),
+	"\n",
+	"<b>togglethermal(target)\n",
+	"<b>setthermal(target)\n",
+	"<b>clearthermal(target)\n",
+	"\ttarget = {object | selectedpins | selectedvias | selected}\n",
+	N_("\tToggle, set or clear a thermal (on the current layer) to pins or vias.\n"),
+	"\n",
+	"<b>loadvendor(target)\n",
+	"\ttarget = [filename]\n",
+	N_("\tLoad a vendor file.  If 'filename' omitted, pop up file select dialog.\n"),
+};
+
+
+	/* Put an allocated string on the history list and combo text list
+	   |  if it is not a duplicate.  The history_list is just a shadow of the
+	   |  combo list, but I think is needed because I don't see an api for reading
+	   |  the combo strings.  The combo box strings take "const gchar *", so the
+	   |  same allocated string can go in both the history list and the combo list.
+	   |  If removed from both lists, a string can be freed.
+	 */
+static void command_history_add(gchar * cmd)
+{
+	GList *list;
+	gchar *s;
+	gint i;
+
+	if (!cmd || !*cmd)
+		return;
+
+	/* Check for a duplicate command.  If found, move it to the
+	   |  top of the list and similarly modify the combo box strings.
+	 */
+	for (i = 0, list = history_list; list; list = list->next, ++i) {
+		s = (gchar *) list->data;
+		if (!strcmp(cmd, s)) {
+			history_list = g_list_remove(history_list, s);
+			history_list = g_list_prepend(history_list, s);
+			gtk_combo_box_remove_text(GTK_COMBO_BOX(ghidgui->command_combo_box), i);
+			gtk_combo_box_prepend_text(GTK_COMBO_BOX(ghidgui->command_combo_box), s);
+			return;
+		}
+	}
+
+	/* Not a duplicate, so put first in history list and combo box text list.
+	 */
+	s = g_strdup(cmd);
+	history_list = g_list_prepend(history_list, s);
+	gtk_combo_box_prepend_text(GTK_COMBO_BOX(ghidgui->command_combo_box), s);
+
+	/* And keep the lists trimmed!
+	 */
+	if (g_list_length(history_list) > conf_hid_gtk.plugins.hid_gtk.history_size) {
+		s = (gchar *) g_list_nth_data(history_list, conf_hid_gtk.plugins.hid_gtk.history_size);
+		history_list = g_list_remove(history_list, s);
+		gtk_combo_box_remove_text(GTK_COMBO_BOX(ghidgui->command_combo_box), conf_hid_gtk.plugins.hid_gtk.history_size);
+		g_free(s);
+	}
+}
+
+
+	/* Called when user hits "Enter" key in command entry.  The action to take
+	   |  depends on where the combo box is.  If it's in the command window, we can
+	   |  immediately execute the command and carry on.  If it's in the status
+	   |  line hbox, then we need stop the command entry g_main_loop from running
+	   |  and save the allocated string so it can be returned from
+	   |  ghid_command_entry_get()
+	 */
+static void command_entry_activate_cb(GtkWidget * widget, gpointer data)
+{
+	gchar *command;
+
+	command = g_strdup(ghid_entry_get_text(GTK_WIDGET(ghidgui->command_entry)));
+	gtk_entry_set_text(ghidgui->command_entry, "");
+
+	if (*command)
+		command_history_add(command);
+
+	if (conf_hid_gtk.plugins.hid_gtk.use_command_window) {
+		hid_parse_command(command);
+		g_free(command);
+	}
+	else {
+		if (ghid_entry_loop && g_main_loop_is_running(ghid_entry_loop))	/* should always be */
+			g_main_loop_quit(ghid_entry_loop);
+		command_entered = command;	/* Caller will free it */
+	}
+}
+
+	/* Create the command_combo_box.  Called once, either by
+	   |  ghid_command_window_show() or ghid_command_entry_get().  Then as long as
+	   |  conf_hid_gtk.plugins.hid_gtk.use_command_window is TRUE, the command_combo_box will live
+	   |  in a command window vbox or float if the command window is not up.
+	   |  But if conf_hid_gtk.plugins.hid_gtk.use_command_window is FALSE, the command_combo_box
+	   |  will live in the status_line_hbox either shown or hidden.
+	   |  Since it's never destroyed, the combo history strings never need
+	   |  rebuilding and history is maintained if the combo box location is moved.
+	 */
+static void command_combo_box_entry_create(void)
+{
+	ghidgui->command_combo_box = gtk_combo_box_entry_new_text();
+	ghidgui->command_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(ghidgui->command_combo_box)));
+
+	gtk_entry_set_width_chars(ghidgui->command_entry, 40);
+	gtk_entry_set_activates_default(ghidgui->command_entry, TRUE);
+
+	g_signal_connect(G_OBJECT(ghidgui->command_entry), "activate", G_CALLBACK(command_entry_activate_cb), NULL);
+
+	g_object_ref(G_OBJECT(ghidgui->command_combo_box));	/* so can move it */
+}
+
+static void command_window_close_cb(void)
+{
+	if (command_window) {
+		gtk_container_remove(GTK_CONTAINER(combo_vbox),	/* Float it */
+												 ghidgui->command_combo_box);
+		gtk_widget_destroy(command_window);
+	}
+	combo_vbox = NULL;
+	command_window = NULL;
+}
+
+static void command_destroy_cb(GtkWidget * widget, gpointer data)
+{
+	command_window = NULL;
+}
+
+	/* If conf_hid_gtk.plugins.hid_gtk.use_command_window toggles, the config code calls
+	   |  this to ensure the command_combo_box is set up for living in the
+	   |  right place.
+	 */
+void ghid_command_use_command_window_sync(void)
+{
+	/* The combo box will be NULL and not living anywhere until the
+	   |  first command entry.
+	 */
+	if (!ghidgui->command_combo_box)
+		return;
+
+	if (conf_hid_gtk.plugins.hid_gtk.use_command_window)
+		gtk_container_remove(GTK_CONTAINER(ghidgui->status_line_hbox), ghidgui->command_combo_box);
+	else {
+		/* Destroy the window (if it's up) which floats the command_combo_box
+		   |  so we can pack it back into the status line hbox.  If the window
+		   |  wasn't up, the command_combo_box was already floating.
+		 */
+		command_window_close_cb();
+		gtk_widget_hide(ghidgui->command_combo_box);
+		gtk_box_pack_start(GTK_BOX(ghidgui->status_line_hbox), ghidgui->command_combo_box, FALSE, FALSE, 0);
+	}
+}
+
+	/* If conf_hid_gtk.plugins.hid_gtk.use_command_window is TRUE this will get called from
+	   |  ActionCommand() to show the command window.
+	 */
+void ghid_command_window_show(gboolean raise)
+{
+	GtkWidget *vbox, *vbox1, *hbox, *button, *expander, *text;
+	gint i;
+
+	if (command_window) {
+		if (raise)
+			gtk_window_present(GTK_WINDOW(command_window));
+		return;
+	}
+	command_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	g_signal_connect(G_OBJECT(command_window), "destroy", G_CALLBACK(command_destroy_cb), NULL);
+	gtk_window_set_title(GTK_WINDOW(command_window), _("pcb-rnd Command Entry"));
+	gtk_window_set_wmclass(GTK_WINDOW(command_window), "PCB_Command", "PCB");
+	gtk_window_set_resizable(GTK_WINDOW(command_window), FALSE);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_container_add(GTK_CONTAINER(command_window), vbox);
+
+	if (!ghidgui->command_combo_box)
+		command_combo_box_entry_create();
+
+	gtk_box_pack_start(GTK_BOX(vbox), ghidgui->command_combo_box, FALSE, FALSE, 0);
+	combo_vbox = vbox;
+
+	/* Make the command reference scrolled text view.  Use high level
+	   |  utility functions in gui-utils.c
+	 */
+	expander = gtk_expander_new(_("Command Reference"));
+	gtk_box_pack_start(GTK_BOX(vbox), expander, TRUE, TRUE, 2);
+	vbox1 = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(expander), vbox1);
+	gtk_widget_set_size_request(vbox1, -1, 350);
+
+	text = ghid_scrolled_text_view(vbox1, NULL, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	for (i = 0; i < sizeof(command_ref_text) / sizeof(gchar *); ++i)
+		ghid_text_view_append(text, _(command_ref_text[i]));
+
+	/* The command window close button.
+	 */
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(command_window_close_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	gtk_widget_show_all(command_window);
+}
+
+
+static gboolean command_escape_cb(GtkWidget * widget, GdkEventKey * kev, gpointer data)
+{
+	gint ksym = kev->keyval;
+
+	if (ksym != GDK_Escape)
+		return FALSE;
+
+	if (ghid_entry_loop && g_main_loop_is_running(ghid_entry_loop))	/* should always be */
+		g_main_loop_quit(ghid_entry_loop);
+	command_entered = NULL;				/* We are aborting */
+
+	return TRUE;
+}
+
+
+	/* This is the command entry function called from ActionCommand() when
+	   |  conf_hid_gtk.plugins.hid_gtk.use_command_window is FALSE.  The command_combo_box is already
+	   |  packed into the status line label hbox in this case.
+	 */
+gchar *ghid_command_entry_get(const gchar * prompt, const gchar * command)
+{
+	gchar *s;
+	gint escape_sig_id;
+	GHidPort *out = &ghid_port;
+
+	/* If this is the first user command entry, we have to create the
+	   |  command_combo_box and pack it into the status_line_hbox.
+	 */
+	if (!ghidgui->command_combo_box) {
+		command_combo_box_entry_create();
+		gtk_box_pack_start(GTK_BOX(ghidgui->status_line_hbox), ghidgui->command_combo_box, FALSE, FALSE, 0);
+	}
+
+	/* Make the prompt bold and set the label before showing the combo to
+	   |  avoid window resizing wider.
+	 */
+	s = g_strdup_printf("<b>%s</b>", prompt ? prompt : "");
+	ghid_status_line_set_text(s);
+	g_free(s);
+
+	/* Flag so output drawing area won't try to get focus away from us and
+	   |  so resetting the status line label can be blocked when resize
+	   |  callbacks are invokded from the resize caused by showing the combo box.
+	 */
+	ghidgui->command_entry_status_line_active = TRUE;
+
+	gtk_entry_set_text(ghidgui->command_entry, command ? command : "");
+	gtk_widget_show_all(ghidgui->command_combo_box);
+
+	/* Remove the top window accel group so keys intended for the entry
+	   |  don't get intercepted by the menu system.  Set the interface
+	   |  insensitive so all the user can do is enter a command, grab focus
+	   |  and connect a handler to look for the escape key.
+	 */
+	ghid_remove_accel_groups(GTK_WINDOW(gport->top_window), ghidgui);
+	ghid_interface_input_signals_disconnect();
+	ghid_interface_set_sensitive(FALSE);
+	gtk_widget_grab_focus(GTK_WIDGET(ghidgui->command_entry));
+	escape_sig_id = g_signal_connect(G_OBJECT(ghidgui->command_entry), "key_press_event", G_CALLBACK(command_escape_cb), NULL);
+
+	ghid_entry_loop = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(ghid_entry_loop);
+
+	g_main_loop_unref(ghid_entry_loop);
+	ghid_entry_loop = NULL;
+
+	ghidgui->command_entry_status_line_active = FALSE;
+
+	/* Restore the damage we did before entering the loop.
+	 */
+	g_signal_handler_disconnect(ghidgui->command_entry, escape_sig_id);
+	ghid_interface_input_signals_connect();
+	ghid_interface_set_sensitive(TRUE);
+	ghid_install_accel_groups(GTK_WINDOW(gport->top_window), ghidgui);
+
+	/* Restore the status line label and give focus back to the drawing area
+	 */
+	gtk_widget_hide(ghidgui->command_combo_box);
+	gtk_widget_grab_focus(out->drawing_area);
+
+	return command_entered;
+}
+
+
+void ghid_handle_user_command(gboolean raise)
+{
+	char *command;
+	static char *previous = NULL;
+
+	if (conf_hid_gtk.plugins.hid_gtk.use_command_window)
+		ghid_command_window_show(raise);
+	else {
+		command = ghid_command_entry_get(_("Enter command:"), (conf_core.editor.save_last_command && previous) ? previous : (gchar *) "");
+		if (command != NULL) {
+			/* copy new comand line to save buffer */
+			g_free(previous);
+			previous = g_strdup(command);
+			hid_parse_command(command);
+			g_free(command);
+		}
+		else if (previous) {
+			command = g_strdup(previous);
+			hid_parse_command(command);
+			g_free(command);
+		}
+	}
+	ghid_window_set_name_label(PCB->Name);
+	ghid_set_status_line_label();
+}
diff --git a/src_plugins/hid_gtk/gui-config.c b/src_plugins/hid_gtk/gui-config.c
new file mode 100644
index 0000000..4134e60
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-config.c
@@ -0,0 +1,2836 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port.
+*/
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdlib.h>
+#include <genht/hash.h>
+
+#include "gui.h"
+#include "win_place.h"
+#include "hid.h"
+#include "gtkhid.h"
+
+#include "global.h"
+#include "action_helper.h"
+#include "change.h"
+#include "plug_io.h"
+#include "error.h"
+#include "draw.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "set.h"
+#include "hid_attrib.h"
+#include "conf.h"
+#include "misc_util.h"
+#include "hid_gtk_conf.h"
+#include "gtk_conf_list.h"
+#include "paths.h"
+#include "plug_footprint.h"
+#include "compat_misc.h"
+#include "fptr_cast.h"
+#include <liblihata/tree.h>
+
+#if 0
+#include <locale.h>
+#endif
+
+extern int MoveLayerAction(int argc, char **argv, int x, int y);
+conf_hid_gtk_t conf_hid_gtk;
+window_geometry_t hid_gtk_wgeo, hid_gtk_wgeo_old;
+
+#define hid_gtk_wgeo_save_(field, dest_role) \
+	conf_setf(dest_role, "plugins/hid_gtk/window_geometry/" #field, -1, "%d", hid_gtk_wgeo.field); \
+
+#define hid_gtk_wgeo_update_(field, dest_role) \
+	if (hid_gtk_wgeo.field != hid_gtk_wgeo_old.field) { \
+		hid_gtk_wgeo_old.field = hid_gtk_wgeo.field; \
+		hid_gtk_wgeo_save_(field, dest_role); \
+	}
+
+void hid_gtk_wgeo_update(void)
+{
+	if (conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_design)
+		GHID_WGEO_ALL(hid_gtk_wgeo_update_, CFR_DESIGN);
+	if (conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_project)
+		GHID_WGEO_ALL(hid_gtk_wgeo_update_, CFR_PROJECT);
+	if (conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_user)
+		GHID_WGEO_ALL(hid_gtk_wgeo_update_, CFR_USER);
+}
+
+
+void ghid_wgeo_save(int save_to_file, int skip_user)
+{
+	if (conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_design) {
+		GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_DESIGN);
+		if (save_to_file)
+			conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), CFR_DESIGN, NULL);
+	}
+
+	if (conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_project) {
+		GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_PROJECT);
+		if (save_to_file)
+			conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), CFR_PROJECT, NULL);
+	}
+
+	if (!skip_user) {
+		if (conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_user) {
+			GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_USER);
+			if (save_to_file)
+				conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), CFR_USER, NULL);
+		}
+	}
+}
+
+static void wgeo_save_direct(GtkButton *widget, const char *ctx)
+{
+	if (ctx == NULL) {
+		GtkWidget *fcd = gtk_file_chooser_dialog_new("Save window geometry to...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+		if (gtk_dialog_run(GTK_DIALOG(fcd)) == GTK_RESPONSE_ACCEPT) {
+			char *fn = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
+			conf_reset(CFR_file, "<wgeo_save_direct>");
+			GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_file);
+			conf_export_to_file(fn, CFR_file, "/");
+			gtk_widget_destroy(fcd);
+			g_free(fn);
+			conf_reset(CFR_file, "<internal>");
+		}
+	}
+	else if (*ctx == '*') {
+		switch(ctx[1]) {
+			case 'd':
+				GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_DESIGN);
+				conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), CFR_DESIGN, NULL);
+				break;
+			case 'p':
+				GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_PROJECT);
+				conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), CFR_PROJECT, NULL);
+				break;
+			case 'u':
+				GHID_WGEO_ALL(hid_gtk_wgeo_save_, CFR_USER);
+				conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), CFR_USER, NULL);
+				break;
+		}
+	}
+}
+
+#undef hid_gtk_wgeo_update_
+#undef hid_gtk_wgeo_save_
+
+/* event handler that runs before the current pcb is saved - save win geo
+   in the corresponding lihata trees if the checkbox is selected. */
+void ghid_conf_save_pre_wgeo(void *user_data, int argc, event_arg_t * argv[])
+{
+	ghid_wgeo_save(1, 1);
+}
+
+
+static int just_loaded(const char *path)
+{
+	conf_native_t *n = conf_get_field(path);
+	conf_role_t r;
+	if ((n == NULL) || (n->used == 0))
+		return 0;
+	r = conf_lookup_role(n->prop[0].src);
+	return (r == CFR_DESIGN) || (r == CFR_PROJECT);
+}
+
+#define path_prefix "plugins/hid_gtk/window_geometry/"
+#define did_just_load_geo(win, id) \
+	if (just_loaded(path_prefix #win "_width") || just_loaded(path_prefix #win "_height") \
+	 || just_loaded(path_prefix #win "_x") || just_loaded(path_prefix #win "_y")) { \
+		hid_gtk_wgeo.win ## _width  = conf_hid_gtk.plugins.hid_gtk.window_geometry.win ## _width; \
+		hid_gtk_wgeo.win ## _height = conf_hid_gtk.plugins.hid_gtk.window_geometry.win ## _height; \
+		hid_gtk_wgeo.win ## _x      = conf_hid_gtk.plugins.hid_gtk.window_geometry.win ## _x; \
+		hid_gtk_wgeo.win ## _y      = conf_hid_gtk.plugins.hid_gtk.window_geometry.win ## _y; \
+		wplc_place(id, NULL); \
+	}
+
+void ghid_conf_load_post_wgeo(void *user_data, int argc, event_arg_t * argv[])
+{
+	did_just_load_geo(top, WPLC_TOP);
+	did_just_load_geo(log, WPLC_LOG);
+	did_just_load_geo(drc, WPLC_DRC);
+	did_just_load_geo(library, WPLC_LIBRARY);
+	did_just_load_geo(netlist, WPLC_NETLIST);
+	did_just_load_geo(keyref, WPLC_KEYREF);
+	did_just_load_geo(pinout, WPLC_PINOUT);
+	did_just_load_geo(pinout, WPLC_SEARCH);
+}
+
+#undef path_prefix
+
+
+#undef just_loaded_geo
+
+enum ConfigType {
+	CONFIG_Boolean,
+	CONFIG_Integer,
+	CONFIG_Coord,
+	CONFIG_Real,
+	CONFIG_String,
+	CONFIG_Unused
+};
+
+typedef struct {
+	gchar *name;
+	enum ConfigType type;
+	void *value;
+} ConfigAttribute;
+
+extern void ghid_set_special_colors(conf_native_t *cfg);
+
+void ghid_config_init(void)
+{
+	hid_gtk_wgeo_old = hid_gtk_wgeo = conf_hid_gtk.plugins.hid_gtk.window_geometry;
+#warning CONF TODO: inject the internal part here?
+}
+
+typedef struct {
+	conf_role_t dst_role;
+	conf_role_t src_role;
+} save_ctx_t;
+
+static void ghid_config_window_close(void);
+
+static GtkTreeView *gui_config_treeview;
+static GtkNotebook *config_notebook;
+
+typedef struct tvmap_s tvmap_t;
+
+struct tvmap_s {
+	GtkTreePath *path;
+	tvmap_t *next;
+};
+
+static void tvmap(GtkTreeView *tree, GtkTreePath *path, gpointer user_data)
+{
+	tvmap_t **first = user_data, *m = malloc(sizeof(tvmap_t));
+	m->path = gtk_tree_path_copy(path);
+	m->next = *first;
+	*first = m;
+/*	printf("exp1 %s\n", gtk_tree_path_to_string(m->path));*/
+}
+
+/* Replace a list of paths in dst with src; each path must be either:
+   - a path to a config filed that presents in the hash (not a ha:subtree)
+   - a subtree or multiple subtrees matching a prefix (e.g. "*appearance/color")
+*/
+void config_any_replace(save_ctx_t *ctx, const char **paths)
+{
+	const char **p;
+	int need_update = 0;
+
+	for(p = paths; *p != NULL; p++) {
+		/* wildcards - match subtree */
+		if (**p == '*') {
+			const char *wildp = (*p)+1;
+			int pl = strlen(wildp);
+			htsp_entry_t *e;
+			conf_fields_foreach(e) {
+				if (strncmp(e->key, wildp, pl) == 0) {
+					if (conf_replace_subtree(ctx->dst_role, e->key, ctx->src_role, e->key) != 0)
+						Message(PCB_MSG_DEFAULT, "Error: failed to save config item %s\n", *p);
+					if (ctx->dst_role < CFR_max_real) {
+						conf_update(e->key);
+						need_update++;
+					}
+				}
+			}
+		}
+		else {
+			/* plain node */
+			if (conf_replace_subtree(ctx->dst_role, *p, ctx->src_role, *p) != 0)
+					Message(PCB_MSG_DEFAULT, "Error: failed to save config item %s\n", *p);
+			if (ctx->dst_role < CFR_max_real) {
+				conf_update(*p);
+				need_update++;
+			}
+		}
+	}
+
+	/* present a file choose dialog box on save to custom file */
+	if (ctx->dst_role == CFR_file) {
+		GtkWidget *fcd = gtk_file_chooser_dialog_new("Save config settings to...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+		if (gtk_dialog_run(GTK_DIALOG(fcd)) == GTK_RESPONSE_ACCEPT) {
+			char *fn = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
+			conf_export_to_file(fn, CFR_file, "/");
+			conf_reset(CFR_file, "<internal>");
+			g_free(fn);
+		}
+		gtk_widget_destroy(fcd);
+	}
+
+	if (need_update) {
+		/* do all the gui updates */
+		ghid_set_status_line_label();
+		ghid_pack_mode_buttons();
+
+	}
+
+	if ((ctx->dst_role == CFR_USER) || (ctx->dst_role == CFR_PROJECT))
+		conf_save_file(NULL, (PCB == NULL ? NULL : PCB->Filename), ctx->dst_role, NULL);
+
+	if (need_update) { /* need to reopen the preferences dialog to show the new settings */
+		tvmap_t *first = NULL, *next;
+		int page;
+
+		/* save expansions and notebook states */
+		gtk_tree_view_map_expanded_rows(gui_config_treeview, tvmap, &first);
+		page = gtk_notebook_get_current_page(config_notebook);
+
+		ghid_config_window_close();
+		ghid_config_window_show();
+
+		/* restore expansions and notebook states */
+		for(; first != NULL; first = next) {
+/*			printf("exp2 %s\n", gtk_tree_path_to_string(first->path));*/
+			next = first->next;
+			gtk_tree_view_expand_to_path(gui_config_treeview, first->path);
+			gtk_tree_path_free(first->path);
+			free(first);
+		}
+		gtk_notebook_set_current_page(config_notebook, page);
+	}
+}
+
+/* =================== OK, now the gui stuff ======================
+*/
+static GtkWidget *config_window;
+
+static void config_user_role_section(GtkWidget * vbox, void (*save_cb)(GtkButton *widget, save_ctx_t *sctx))
+{
+	GtkWidget *config_color_warn_label, *button, *hbox, *vbox2;
+	static save_ctx_t ctx_all2project = { CFR_PROJECT, CFR_binary };
+	static save_ctx_t ctx_all2user    = { CFR_USER, CFR_binary };
+	static save_ctx_t ctx_all2file    = { CFR_file, CFR_binary };
+	static save_ctx_t ctx_int2design  = { CFR_DESIGN, CFR_INTERNAL };
+
+	hbox = gtk_hbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 4);
+
+	config_color_warn_label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(config_color_warn_label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(config_color_warn_label),
+											 _("<small>The above are <i>design-level</i>\nconfiguration, <u>saved</u> with the\npcb file. Use these buttons\nto save all the above settings:</small>"));
+	gtk_box_pack_start(GTK_BOX(hbox), config_color_warn_label, FALSE, FALSE, 4);
+
+	vbox2 = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 0);
+
+	button = gtk_button_new_with_label("Save in project config");
+	gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(save_cb), &ctx_all2project);
+
+	button = gtk_button_new_with_label("Save in user config");
+	gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(save_cb), &ctx_all2user);
+
+	vbox2 = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 0);
+
+	button = gtk_button_new_with_label("Save to file");
+	gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(save_cb), &ctx_all2file);
+
+	button = gtk_button_new_with_label("Restore factory defaults");
+	gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(save_cb), &ctx_int2design);
+}
+
+	/* -------------- The General config page ----------------
+	 */
+
+static void config_command_window_toggle_cb(GtkToggleButton * button, gpointer data)
+{
+/*	gboolean active =*/ gtk_toggle_button_get_active(button);
+	static gboolean holdoff;
+
+	if (holdoff)
+		return;
+
+	/* Can't toggle into command window mode if the status line command
+	   |  entry is active.
+	 */
+	if (ghidgui->command_entry_status_line_active) {
+		holdoff = TRUE;
+		gtk_toggle_button_set_active(button, FALSE);
+		holdoff = FALSE;
+		return;
+	}
+	conf_set(CFR_DESIGN, "plugins/hid_gtk/use_command_window", -1, "1", POL_OVERWRITE);
+	ghid_command_use_command_window_sync();
+}
+
+static void config_compact_horizontal_toggle_cb(GtkToggleButton * button, gpointer data)
+{
+	gboolean active = gtk_toggle_button_get_active(button);
+
+	conf_setf(CFR_DESIGN, "plugins/hid_gtk/compact_horizontal", -1, "%d", active);
+	ghid_set_status_line_label();
+}
+
+static void config_compact_vertical_toggle_cb(GtkToggleButton * button, gpointer data)
+{
+	gboolean active = gtk_toggle_button_get_active(button);
+
+	conf_setf(CFR_DESIGN, "plugins/hid_gtk/compact_vertical", -1, "%d", active);
+	ghid_pack_mode_buttons();
+}
+
+static GtkWidget *pref_auto_place_lab;
+static void pref_auto_place_update(void)
+{
+	const char *warn;
+	if (conf_core.editor.auto_place)
+		warn = "Restoring window geometry is <b>enabled</b>:\npcb-rnd will attempt to move and resize windows.\nIt can be disabled in General/";
+	else
+		warn = "Restoring window geometry is <b>disabled</b>:\npcb-rnd will <b>not</b> attempt to move and resize windows.\nConsider changing it in General/";
+	gtk_label_set_markup(GTK_LABEL(pref_auto_place_lab), _(warn));
+}
+
+static void config_auto_place_toggle_cb(GtkToggleButton * button, gpointer data)
+{
+	gboolean active = gtk_toggle_button_get_active(button);
+
+	conf_setf(CFR_DESIGN, "editor/auto_place", -1, "%d", active);
+	pref_auto_place_update();
+}
+
+static void config_general_toggle_cb(GtkToggleButton * button, void *setting)
+{
+	*(gint *) setting = gtk_toggle_button_get_active(button);
+}
+
+static void config_backup_spin_button_cb(GtkSpinButton * spin_button, gpointer data)
+{
+	int i;
+	char s[32];
+	i = gtk_spin_button_get_value_as_int(spin_button);
+	sprintf(s, "%d", i);
+	conf_set(CFR_DESIGN, "rc/backup_interval", -1, s, POL_OVERWRITE);
+	EnableAutosave();
+}
+
+static void config_history_spin_button_cb(GtkSpinButton * spin_button, gpointer data)
+{
+	conf_setf(CFR_DESIGN, "plugins/hid_gtk/history_size", -1, "%d", gtk_spin_button_get_value_as_int(spin_button));
+}
+
+void config_general_save(GtkButton *widget, save_ctx_t *ctx)
+{
+	const char *paths[] = {
+		"plugins/hid_gtk/use_command_window",
+		"plugins/hid_gtk/compact_horizontal",
+		"plugins/hid_gtk/compact_vertical",
+		"plugins/hid_gtk/history_size",
+		"rc/backup_interval",
+		"editor/auto_place",
+		"editor/save_in_tmp",
+		NULL
+	};
+
+	config_any_replace(ctx, paths);
+}
+
+static void config_general_tab_create(GtkWidget * tab_vbox)
+{
+	GtkWidget *vbox, *content_vbox;
+
+	content_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), content_vbox, TRUE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(content_vbox), 6);
+
+	vbox = ghid_category_vbox(content_vbox, _("Enables"), 4, 2, TRUE, TRUE);
+
+	ghid_check_button_connected(vbox, NULL, conf_hid_gtk.plugins.hid_gtk.use_command_window,
+															TRUE, FALSE, FALSE, 2,
+															config_command_window_toggle_cb, NULL, _("Use separate window for command entry"));
+
+	ghid_check_button_connected(vbox, NULL, conf_hid_gtk.plugins.hid_gtk.compact_horizontal,
+															TRUE, FALSE, FALSE, 2,
+															config_compact_horizontal_toggle_cb, NULL,
+															_("Alternate window layout to allow smaller horizontal size"));
+
+	ghid_check_button_connected(vbox, NULL, conf_hid_gtk.plugins.hid_gtk.compact_vertical,
+															TRUE, FALSE, FALSE, 2,
+															config_compact_vertical_toggle_cb, NULL,
+															_("Alternate window layout to allow smaller vertical size"));
+
+	ghid_check_button_connected(vbox, NULL, conf_core.editor.auto_place,
+															TRUE, FALSE, FALSE, 2,
+															config_auto_place_toggle_cb, NULL,
+															_("Restore window geometry (when saved geometry is available)"));
+
+	vbox = ghid_category_vbox(content_vbox, _("Backups"), 4, 2, TRUE, TRUE);
+	ghid_check_button_connected(vbox, NULL, conf_core.editor.save_in_tmp,
+															TRUE, FALSE, FALSE, 2,
+															config_general_toggle_cb, (void *)&conf_core.editor.save_in_tmp,
+															_("If layout is modified at exit, save into PCB.%i.save"));
+	ghid_spin_button(vbox, NULL, conf_core.rc.backup_interval, 0.0, 60 * 60, 60.0,
+									 600.0, 0, 0, config_backup_spin_button_cb, NULL, FALSE,
+									 _("Seconds between auto backups\n" "(set to zero to disable auto backups)"));
+
+	vbox = ghid_category_vbox(content_vbox, _("Misc"), 4, 2, TRUE, TRUE);
+	ghid_spin_button(vbox, NULL, conf_hid_gtk.plugins.hid_gtk.history_size,
+									 5.0, 25.0, 1.0, 1.0, 0, 0,
+									 config_history_spin_button_cb, NULL, FALSE, _("Number of commands to remember in the history list"));
+
+
+	vbox = gtk_vbox_new(TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), vbox, TRUE, TRUE, 0);
+	config_user_role_section(tab_vbox, config_general_save);
+}
+
+	/* -------------- The Sizes config page ----------------
+	 */
+static GtkWidget *config_sizes_vbox, *config_sizes_tab_vbox, *config_text_spin_button;
+
+static Coord new_board_width, new_board_height;
+
+static void config_sizes_apply(void)
+{
+	conf_setf(CFR_DESIGN, "design/max_width", -1, "%$mS", new_board_width);
+	conf_setf(CFR_DESIGN, "design/max_height", -1, "%$mS", new_board_height);
+
+	conf_set_design("design/bloat", "%$mS", PCB->Bloat);
+	conf_set_design("design/shrink", "%$mS", PCB->Shrink);
+	conf_set_design("design/min_wid", "%$mS", PCB->minWid);
+	conf_set_design("design/min_slk", "%$mS", PCB->minSlk);
+	conf_set_design("design/poly_isle_area", "%f", PCB->IsleArea);
+	conf_set_design("design/min_drill", "%$mS", PCB->minDrill);
+	conf_set_design("design/min_ring", "%$mS", PCB->minRing);
+
+	if (PCB->MaxWidth != conf_core.design.max_width || PCB->MaxHeight != conf_core.design.max_height)
+		ChangePCBSize(conf_core.design.max_width, conf_core.design.max_height);
+}
+
+static void text_spin_button_cb(GtkSpinButton * spin, void *dst)
+{
+	conf_setf(CFR_DESIGN, "design/text_scale", -1, "%d", gtk_spin_button_get_value_as_int(spin));
+	ghid_set_status_line_label();
+}
+
+static void coord_entry_cb(GHidCoordEntry * ce, void *dst)
+{
+	*(Coord *) dst = ghid_coord_entry_get_value(ce);
+}
+
+void config_sizes_save(GtkButton *widget, save_ctx_t *ctx)
+{
+	const char *paths[] = {
+		"design/max_width",
+		"design/max_height",
+		"design/bloat",
+		"design/shrink",
+		"design/min_wid",
+		"design/min_slk",
+		"design/poly_isle_area",
+		"design/min_drill",
+		"design/min_ring",
+		"design/text_scale",
+		NULL
+	};
+
+	config_sizes_apply();
+	config_any_replace(ctx, paths);
+}
+
+
+static void config_sizes_tab_create(GtkWidget * tab_vbox)
+{
+	GtkWidget *table, *vbox, *hbox, *content_vbox;
+
+	content_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), content_vbox, TRUE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(content_vbox), 6);
+
+	/* Need a vbox we can destroy if user changes grid units.
+	 */
+	if (!config_sizes_vbox) {
+		vbox = gtk_vbox_new(FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(content_vbox), vbox, FALSE, FALSE, 0);
+		gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+		config_sizes_vbox = vbox;
+		config_sizes_tab_vbox = content_vbox;
+	}
+
+	/* ---- Board Size ---- */
+	vbox = ghid_category_vbox(config_sizes_vbox, _("Board Size"), 4, 2, TRUE, TRUE);
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	table = gtk_table_new(2, 2, FALSE);
+	gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+
+	new_board_width = PCB->MaxWidth;
+	new_board_height = PCB->MaxHeight;
+	ghid_table_coord_entry(table, 0, 0, NULL,
+												 PCB->MaxWidth, MIN_SIZE, MAX_COORD, CE_LARGE, 0, coord_entry_cb, &new_board_width, FALSE, _("Width"));
+
+	ghid_table_coord_entry(table, 1, 0, NULL,
+												 PCB->MaxHeight, MIN_SIZE, MAX_COORD,
+												 CE_LARGE, 0, coord_entry_cb, &new_board_height, FALSE, _("Height"));
+
+	/* ---- Text Scale ---- */
+	vbox = ghid_category_vbox(config_sizes_vbox, _("Text Scale"), 4, 2, TRUE, TRUE);
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	table = gtk_table_new(4, 2, FALSE);
+	gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+	ghid_table_spin_button(table, 0, 0, &config_text_spin_button,
+												 conf_core.design.text_scale,
+												 MIN_TEXTSCALE, MAX_TEXTSCALE, 10.0, 10.0, 0, 0, text_spin_button_cb, NULL, FALSE, "%");
+
+
+	/* ---- DRC Sizes ---- */
+	vbox = ghid_category_vbox(config_sizes_vbox, _("Design Rule Checking"), 4, 2, TRUE, TRUE);
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	table = gtk_table_new(4, 2, FALSE);
+	gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+
+	ghid_table_coord_entry(table, 0, 0, NULL,
+												 PCB->Bloat, MIN_DRC_VALUE, MAX_DRC_VALUE,
+												 CE_SMALL, 0, coord_entry_cb, &PCB->Bloat, FALSE, _("Minimum copper spacing"));
+
+	ghid_table_coord_entry(table, 1, 0, NULL,
+												 PCB->minWid, MIN_DRC_VALUE, MAX_DRC_VALUE,
+												 CE_SMALL, 0, coord_entry_cb, &PCB->minWid, FALSE, _("Minimum copper width"));
+
+	ghid_table_coord_entry(table, 2, 0, NULL,
+												 PCB->Shrink, MIN_DRC_VALUE, MAX_DRC_VALUE,
+												 CE_SMALL, 0, coord_entry_cb, &PCB->Shrink, FALSE, _("Minimum touching copper overlap"));
+
+	ghid_table_coord_entry(table, 3, 0, NULL,
+												 PCB->minSlk, MIN_DRC_VALUE, MAX_DRC_VALUE,
+												 CE_SMALL, 0, coord_entry_cb, &PCB->minSlk, FALSE, _("Minimum silk width"));
+
+	ghid_table_coord_entry(table, 4, 0, NULL,
+												 PCB->minDrill, MIN_DRC_VALUE, MAX_DRC_VALUE,
+												 CE_SMALL, 0, coord_entry_cb, &PCB->minDrill, FALSE, _("Minimum drill diameter"));
+
+	ghid_table_coord_entry(table, 5, 0, NULL,
+												 PCB->minRing, MIN_DRC_VALUE, MAX_DRC_VALUE,
+												 CE_SMALL, 0, coord_entry_cb, &PCB->minRing, FALSE, _("Minimum annular ring"));
+
+	vbox = gtk_vbox_new(TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), vbox, TRUE, TRUE, 0);
+	config_user_role_section(tab_vbox, config_sizes_save);
+
+	gtk_widget_show_all(config_sizes_vbox);
+}
+
+
+	/* -------------- The window config page ----------------
+	 */
+static GtkWidget *config_window_vbox;
+
+static void config_window_toggle_cb(GtkToggleButton * button, gpointer data)
+{
+	gboolean active = gtk_toggle_button_get_active(button);
+	const char *path = NULL, *ctx = data;
+
+	if (ctx == NULL)
+		return;
+
+	if (*ctx == '*') {
+		switch(ctx[1]) {
+			case 'd': path = "plugins/hid_gtk/auto_save_window_geometry/to_design"; break;
+			case 'p': path = "plugins/hid_gtk/auto_save_window_geometry/to_project"; break;
+			case 'u': path = "plugins/hid_gtk/auto_save_window_geometry/to_user"; break;
+		}
+		if (path != NULL)
+			conf_set(CFR_USER, path, -1, (active ? "1" : "0"), POL_OVERWRITE);
+	}
+}
+
+
+static void config_window_row(GtkWidget *parent, const char *desc, int load, const char *wgeo_save_str, CFT_BOOLEAN chk)
+{
+	GtkWidget *hbox, *lab, *button;
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+	lab = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), lab, TRUE, FALSE, 0);
+
+	lab = gtk_label_new(desc);
+	gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 4);
+
+	button = gtk_button_new_with_label(_("now"));
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(wgeo_save_direct), (void *)wgeo_save_str);
+
+	if (load) {
+		button = gtk_button_new_with_label(_("Load from file"));
+		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	}
+	else {
+		GtkWidget *btn;
+		ghid_check_button_connected(hbox, &btn, chk,
+															TRUE, FALSE, FALSE, 2,
+															config_window_toggle_cb, (void *)wgeo_save_str, _("every time pcb-rnd exits"));
+		if (wgeo_save_str == NULL)
+			gtk_widget_set_sensitive(btn, FALSE);
+	}
+}
+
+static void config_window_tab_create(GtkWidget *tab_vbox)
+{
+	GtkWidget *lab;
+
+	config_window_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), config_window_vbox, FALSE, FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(config_window_vbox), 6);
+
+	lab = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(lab), TRUE);
+	gtk_label_set_markup(GTK_LABEL(lab),
+											 _("<b>Save window geometry...</b>"));
+	gtk_box_pack_start(GTK_BOX(config_window_vbox), lab, FALSE, FALSE, 4);
+	gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.5);
+
+	config_window_row(config_window_vbox, "... in the design (.pcb) file", 0, "*d", conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_design);
+	config_window_row(config_window_vbox, "... in the project file", 0, "*p", conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_project);
+	config_window_row(config_window_vbox, "... in the central user configuration", 0, "*u", conf_hid_gtk.plugins.hid_gtk.auto_save_window_geometry.to_user);
+	config_window_row(config_window_vbox, "... in a custom file", 0, NULL, 0);
+
+
+
+
+	lab = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(lab), TRUE);
+	gtk_label_set_markup(GTK_LABEL(lab),
+											 _("<small>Note: the above checkbox values are saved in the user config</small>"));
+	gtk_box_pack_start(GTK_BOX(config_window_vbox), lab, FALSE, FALSE, 4);
+	gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.5);
+
+	lab = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(lab), TRUE);
+	pref_auto_place_lab = lab;
+	pref_auto_place_update();
+	gtk_box_pack_start(GTK_BOX(config_window_vbox), lab, FALSE, FALSE, 4);
+	gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.5);
+
+	gtk_widget_show_all(config_window_vbox);
+}
+
+
+	/* -------------- The Increments config page ----------------
+	 */
+	/* Increment/decrement values are kept in mil and mm units and not in
+	   |  PCB units.
+	 */
+GtkWidget *config_increments_tbl[4][4]; /* [col][row] */
+
+static GtkWidget *config_increments_vbox = NULL, *config_increments_tab_vbox = NULL;
+
+static void increment_tbl_update_cell(GtkLabel *lab, Coord val, const char *fmt)
+{
+	char s[128];
+	pcb_snprintf(s, sizeof(s), fmt, val);
+	gtk_label_set_text(lab, s);
+}
+
+static void increment_tbl_update_row(int row, Coord edit_in_mm, Coord edit_in_mil)
+{
+	increment_tbl_update_cell(GTK_LABEL(config_increments_tbl[0][row]), edit_in_mm, "%$mm");
+	increment_tbl_update_cell(GTK_LABEL(config_increments_tbl[1][row]), edit_in_mil, "%$mm");
+	increment_tbl_update_cell(GTK_LABEL(config_increments_tbl[2][row]), edit_in_mm, "%$ml");
+	increment_tbl_update_cell(GTK_LABEL(config_increments_tbl[3][row]), edit_in_mil, "%$ml");
+}
+
+static void increment_tbl_update()
+{
+	increment_tbl_update_row(0, conf_core.editor.increments_mm.grid, conf_core.editor.increments_mil.grid);
+	increment_tbl_update_row(1, conf_core.editor.increments_mm.size, conf_core.editor.increments_mil.size);
+	increment_tbl_update_row(2, conf_core.editor.increments_mm.line, conf_core.editor.increments_mil.line);
+	increment_tbl_update_row(3, conf_core.editor.increments_mm.clear, conf_core.editor.increments_mil.clear);
+}
+
+static void increment_spin_button_cb(GHidCoordEntry * ce, void *dst)
+{
+	const char *path = dst;
+	conf_setf(CFR_DESIGN, path, -1, "%mr", (Coord)ghid_coord_entry_get_value(ce));
+	increment_tbl_update();
+}
+
+static void config_increments_sect_create(GtkWidget * vbox)
+{
+	GtkWidget * hbox, *label;
+	const int width = 128;
+	char pathmm[256], *pemm;
+	char pathmil[256], *pemil;
+	const Unit *umm = get_unit_struct("mm");
+	const Unit *umil = get_unit_struct("mil");
+	const char *base_pathmm = "editor/increments_mm";
+	const char *base_pathmil = "editor/increments_mil";
+	int lmm = strlen(base_pathmm);
+	int lmil = strlen(base_pathmil);
+
+	memcpy(pathmm, base_pathmm, lmm);
+	pemm = pathmm+lmm;
+	*pemm = '/';
+	pemm++;
+
+	memcpy(pathmil, base_pathmil, lmil);
+	pemil = pathmil+lmil;
+	*pemil = '/';
+	pemil++;
+
+#warning leak: pcb_strdup(path) never free()d
+	/* ---- Grid Increment/Decrement ---- */
+	strcpy(pemm, "grid");
+	strcpy(pemil, "grid");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mm.grid,
+									 conf_core.editor.increments_mm.grid_min,
+									 conf_core.editor.increments_mm.grid_max,
+									 CE_SMALL, umm, width, increment_spin_button_cb, pcb_strdup(pathmm), _("Grid:"), NULL);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mil.grid,
+									 conf_core.editor.increments_mil.grid_min,
+									 conf_core.editor.increments_mil.grid_max,
+									 CE_SMALL, umil, width, increment_spin_button_cb, pcb_strdup(pathmil), NULL, NULL);
+
+	label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), _("<small>For 'g' and '<shift>g' grid change actions</small>"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2);
+
+	/* ---- Size Increment/Decrement ---- */
+	strcpy(pemm, "size");
+	strcpy(pemil, "size");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mm.size,
+									 conf_core.editor.increments_mm.size_min,
+									 conf_core.editor.increments_mm.size_max,
+									 CE_SMALL, umm, width, increment_spin_button_cb,
+									 pcb_strdup(pathmm), _("Size:"), NULL);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mil.size,
+									 conf_core.editor.increments_mil.size_min,
+									 conf_core.editor.increments_mil.size_max,
+									 CE_SMALL, umil, width, increment_spin_button_cb,
+									 pcb_strdup(pathmil), NULL, NULL);
+
+	label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), _("For <small>'s' and '<shift>s' on lines, pads, text; '<ctrl>s' and '<shift><ctrl>s' on holes.</small>"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2);
+
+	/* ---- Line Increment/Decrement ---- */
+	strcpy(pemm, "line");
+	strcpy(pemil, "line");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mm.line,
+									 conf_core.editor.increments_mm.line_min,
+									 conf_core.editor.increments_mm.line_max,
+									 CE_SMALL, umm, width, increment_spin_button_cb,
+									 pcb_strdup(pathmm), _("Line:"), NULL);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mil.line,
+									 conf_core.editor.increments_mil.line_min,
+									 conf_core.editor.increments_mil.line_max,
+									 CE_SMALL, umil, width, increment_spin_button_cb,
+									 pcb_strdup(pathmil), NULL, NULL);
+
+	label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), _("<small>For 'l' and '<shift>l' routing line width change actions</small>"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2);
+
+	/* ---- Clear Increment/Decrement ---- */
+	strcpy(pemm, "clear");
+	strcpy(pemil, "clear");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mm.clear,
+									 conf_core.editor.increments_mm.clear_min,
+									 conf_core.editor.increments_mm.clear_max,
+									 CE_SMALL, umm, width, increment_spin_button_cb,
+									 pcb_strdup(pathmm), _("Clear:"), NULL);
+
+	ghid_coord_entry(hbox, NULL,
+									 conf_core.editor.increments_mil.clear,
+									 conf_core.editor.increments_mil.clear_min,
+									 conf_core.editor.increments_mil.clear_max,
+									 CE_SMALL, umil, width, increment_spin_button_cb,
+									 pcb_strdup(pathmil), NULL, NULL);
+
+	label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label),_("<small>For 'k' and '<shift>k' line clearance inside polygon size change actions</small>"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2);
+
+
+	gtk_widget_show_all(config_increments_vbox);
+}
+
+static GtkWidget *config_increments_table_attach(GtkWidget *table, int x, int y, int colspan, const char *text)
+{
+	GtkWidget *box, *label;
+
+	box = gtk_vbox_new(FALSE, 0);
+	label = gtk_label_new(text);
+	gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
+	gtk_table_attach(GTK_TABLE(table), box,  x,x+colspan,y,y+1,   0,0,10,2);
+	return label;
+}
+
+void config_increments_save(GtkButton *widget, save_ctx_t *ctx)
+{
+	const char *paths[] = {
+		"editor/increments_mm",
+		"editor/increments_mil",
+		NULL
+	};
+
+	config_any_replace(ctx, paths);
+}
+
+static void config_increments_tab_create(GtkWidget * tab_vbox)
+{
+	GtkWidget *vbox, *catvbox, *content_vbox;
+
+	content_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), content_vbox, TRUE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(content_vbox), 6);
+
+	if (config_increments_vbox != NULL) {
+		gtk_widget_destroy(GTK_WIDGET(config_increments_vbox));
+		config_increments_vbox = NULL;
+	}
+
+	/* the actual content */
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(content_vbox), vbox, FALSE, FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	config_increments_vbox = vbox;
+	config_increments_tab_vbox = content_vbox;
+
+
+	catvbox = ghid_category_vbox (config_increments_vbox, "Increment Settings", 0, 0, TRUE, TRUE);
+	config_increments_sect_create(catvbox);
+
+	catvbox = ghid_category_vbox (config_increments_vbox, _("Comparison table"), 0, 0, TRUE, TRUE);
+
+	/* increment summary table */
+	{
+		GtkWidget *table;
+		int y, x;
+
+		table = gtk_table_new(7, 3, 0);
+		gtk_box_pack_start(GTK_BOX(catvbox), table, FALSE, FALSE, 0);
+
+		config_increments_table_attach(table, 1, 0, 2, "converter to mm");
+		config_increments_table_attach(table, 1, 1, 1, "metric editing");
+		config_increments_table_attach(table, 2, 1, 1, "imperial editing");
+
+		config_increments_table_attach(table, 3, 0, 2, "converter to mil");
+		config_increments_table_attach(table, 3, 1, 1, "metric editing");
+		config_increments_table_attach(table, 4, 1, 1, "imperial editing");
+
+		config_increments_table_attach(table, 0, 2, 1, "grid");
+		config_increments_table_attach(table, 0, 3, 1, "size");
+		config_increments_table_attach(table, 0, 4, 1, "line");
+		config_increments_table_attach(table, 0, 5, 1, "clear");
+
+		for(y = 0; y < 4; y++)
+			for(x = 0; x < 4; x++)
+				config_increments_tbl[x][y] = config_increments_table_attach(table, x+1, y+2, 1, "n/a");
+		increment_tbl_update();
+	}
+
+	vbox = gtk_vbox_new(TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), vbox, TRUE, TRUE, 0);
+	config_user_role_section(tab_vbox, config_increments_save);
+}
+
+	/* -------------- The Library config page ----------------
+	 */
+static gtk_conf_list_t library_cl;
+
+static void config_library_apply(void)
+{
+	fp_rehash();
+}
+
+static char *get_misc_col_data(int row, int col, lht_node_t *nd)
+{
+	if ((nd != NULL) && (col == 1)) {
+		char *out;
+		resolve_path(nd->data.text.value, &out, 0);
+		return out;
+	}
+	return NULL;
+}
+
+#warning TODO: leak: this is never free()d
+lht_doc_t *config_library_lst_doc;
+lht_node_t *config_library_lst;
+
+static void lht_clean_list(lht_node_t *lst)
+{
+	lht_node_t *n;
+	while(lst->data.list.first != NULL) {
+		n = lst->data.list.first;
+		if (n->doc == NULL) {
+			if (lst->data.list.last == n)
+				lst->data.list.last = NULL;
+			lst->data.list.first = n->next;
+		}
+		else
+			lht_tree_unlink(n);
+		lht_dom_node_free(n);
+	}
+	lst->data.list.last = NULL;
+}
+
+/* Create a lihata list of the current library paths - to be tuned into CFR design upon the first modification */
+static lht_node_t *config_library_list()
+{
+	conf_listitem_t *i;
+	int idx;
+	const char *s;
+
+	if (config_library_lst_doc == NULL) {
+		/* create a new list */
+		config_library_lst_doc = lht_dom_init();
+		config_library_lst = lht_dom_node_alloc(LHT_LIST, "library_search_paths");
+		config_library_lst_doc->root = config_library_lst;
+		config_library_lst->doc = config_library_lst_doc;
+	}
+	else {
+		/* clean the old list */
+		lht_clean_list(config_library_lst);
+	}
+
+	conf_loop_list_str(&conf_core.rc.library_search_paths, i, s, idx) {
+		lht_node_t *txt;
+		const char *sfn;
+		if (i->prop.src->file_name != NULL) {
+			lht_dom_loc_newfile(config_library_lst_doc, i->prop.src->file_name);
+			lht_dom_loc_active(config_library_lst_doc, &sfn, NULL, NULL);
+		}
+		else
+			sfn = NULL;
+
+		txt = lht_dom_node_alloc(LHT_TEXT, "");
+		txt->data.text.value = pcb_strdup(i->payload);
+		txt->file_name = sfn;
+		txt->doc = config_library_lst_doc;
+		printf("append: '%s' '%s'\n", txt->data.text.value, sfn);
+		lht_dom_list_append(config_library_lst, txt);
+	}
+	return config_library_lst;
+}
+
+static void pre_rebuild(gtk_conf_list_t *cl)
+{
+	lht_node_t *m;
+	lht_clean_list(config_library_lst);
+
+	m = conf_lht_get_first(CFR_DESIGN);
+
+	cl->lst = lht_tree_path_(m->doc, m, "rc/library_search_paths", 1, 0, NULL);
+	if (cl->lst == NULL) {
+		conf_set(CFR_DESIGN, "rc/library_search_paths", 0, "", POL_OVERWRITE);
+	}
+	cl->lst = lht_tree_path_(m->doc, m, "rc/library_search_paths", 1, 0, NULL);
+	assert(cl->lst != NULL);
+
+	lht_clean_list(cl->lst);
+}
+
+static void post_rebuild(gtk_conf_list_t *cl)
+{
+	conf_update("rc/library_search_paths");
+}
+
+
+static GtkWidget *config_library_append_paths(int post_sep)
+{
+	GtkWidget *hbox, *vbox_key, *vbox_val, *vbox_sep, *label;
+	htsp_entry_t *e;
+
+	hbox = gtk_hbox_new(FALSE, 0);
+
+	vbox_key = gtk_vbox_new(FALSE, 0);
+	vbox_sep = gtk_vbox_new(FALSE, 0);
+	vbox_val = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox_key, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox_sep, FALSE, FALSE, 16);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox_val, FALSE, FALSE, 0);
+
+	conf_fields_foreach(e) {
+		if (strncmp(e->key, "rc/path/", 8) == 0) {
+			conf_native_t *nat = e->value;
+			char tmp[256];
+
+			sprintf(tmp, "  $(rc.path.%s)", e->key+8);
+			label = gtk_label_new(tmp);
+			gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+			gtk_box_pack_start(GTK_BOX(vbox_key), label, FALSE, FALSE, 0);
+
+			label = gtk_label_new(nat->val.string[0]);
+			gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+			gtk_box_pack_start(GTK_BOX(vbox_val), label, FALSE, FALSE, 0);
+		}
+	}
+	if (post_sep) {
+		label = gtk_label_new("\n\n");
+		gtk_box_pack_start(GTK_BOX(vbox_val), label, FALSE, FALSE, 0);
+	}
+
+	return hbox;
+}
+
+void config_library_save(GtkButton *widget, save_ctx_t *ctx)
+{
+	const char *paths[] = {
+		"rc/library_search_paths",
+		NULL
+	};
+
+	config_any_replace(ctx, paths);
+}
+
+static void config_library_tab_create(GtkWidget * tab_vbox)
+{
+	GtkWidget *vbox, *label, *entry, *content_vbox, *paths_box;
+	const char *cnames[] = {"configured path", "actual path on the filesystem", "config source"};
+
+	library_cl.num_cols = 3;
+	library_cl.col_names = cnames;
+	library_cl.col_data = 0;
+	library_cl.col_src = 2;
+	library_cl.reorder = 1;
+	library_cl.get_misc_col_data = NULL;
+	library_cl.file_chooser_title = "Select footprint library directory";
+	library_cl.file_chooser_postproc = NULL;
+	library_cl.get_misc_col_data = get_misc_col_data;
+	library_cl.lst = config_library_list();
+	library_cl.pre_rebuild = pre_rebuild;
+	library_cl.post_rebuild = post_rebuild;
+
+	content_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), content_vbox, TRUE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(content_vbox), 6);
+
+	gtk_container_set_border_width(GTK_CONTAINER(content_vbox), 6);
+	vbox = ghid_category_vbox(content_vbox, _("Element Directories"), 4, 2, TRUE, FALSE);
+
+	label = gtk_label_new(_("Ordered list of footprint library search directories; use drag&drop to reorder.\nThe following $(variables) can be used in the path:\n\n"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	paths_box = config_library_append_paths(1);
+	gtk_box_pack_start(GTK_BOX(vbox), paths_box, FALSE, FALSE, 0);
+
+	entry = gtk_conf_list_widget(&library_cl);
+	gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 4);
+
+	config_user_role_section(tab_vbox, config_library_save);
+}
+
+
+	/* -------------- The Layers Group config page ----------------
+	 */
+static GtkWidget *config_groups_table, *config_groups_vbox, *config_groups_window;
+
+static GtkWidget *layer_entry[MAX_LAYER];
+static GtkWidget *group_button[MAX_LAYER + 2][MAX_LAYER];
+
+static gint config_layer_group[MAX_LAYER + 2];
+
+static LayerGroupType layer_groups,	/* Working copy */
+ *lg_monitor;										/* Keep track if our working copy */
+										/* needs to be changed (new layout) */
+
+static gboolean groups_modified, groups_holdoff, layers_applying;
+
+static const gchar *layer_info_text[] = {
+	N_("<h>Layer Names\n"),
+	N_("You may enter layer names for the layers drawn on the screen.\n"
+		 "The special 'component side' and 'solder side' are layers which\n"
+		 "will be printed out, so they must have in their group at least one\n"
+		 "of the other layers that are drawn on the screen.\n"),
+	"\n",
+	N_("<h>Layer Groups\n"),
+	N_("Each layer on the screen may be in its own group which allows the\n"
+		 "maximum number of board layers.  However, for boards with fewer\n"
+		 "layers, you may group layers together which will then print as a\n"
+		 "single layer on a printout.  This allows a visual color distinction\n"
+		 "to be displayed on the screen for signal groups which will print as\n" "a single layer\n"),
+	"\n",
+	N_("For example, for a 4 layer board a useful layer group arrangement\n"
+		 "can be to have 3 screen displayed layers grouped into the same group\n"
+		 "as the 'component side' and 'solder side' printout layers.  Then\n"
+		 "groups such as signals, ground, and supply traces can be color\n"
+		 "coded on the screen while printing as a single layer.  For this\n"
+		 "you would select buttons and enter names on the Setup page to\n" "structure four layer groups similar to this:\n"),
+	"\n",
+	N_("<b>Group 1:"),
+	"\n\t",
+	N_("solder"),
+	"\n\t",
+	N_("GND-solder"),
+	"\n\t",
+	N_("Vcc-solder"),
+	"\n\t",
+	N_("solder side"),
+	"\n",
+	N_("<b>Group 2:"),
+	"\n\t",
+	N_("component"),
+	"\n\t",
+	N_("GND-component"),
+	"\n\t",
+	N_("Vcc-component"),
+	"\n\t",
+	N_("component side"),
+	"\n",
+	N_("<b>Group 3:"),
+	"\n\t",
+	N_("signal1"),
+	"\n",
+	N_("<b>Group 4:"),
+	"\n\t",
+	N_("signal2"),
+	"\n"
+};
+
+static void config_layer_groups_radio_button_cb(GtkToggleButton * button, gpointer data)
+{
+	gint layer = GPOINTER_TO_INT(data) >> 8;
+	gint group = GPOINTER_TO_INT(data) & 0xff;
+
+	if (!gtk_toggle_button_get_active(button) || groups_holdoff)
+		return;
+	config_layer_group[layer] = group;
+	groups_modified = TRUE;
+}
+
+	/* Construct a layer group string.  Follow logic in WritePCBDataHeader(),
+	   |  but use g_string functions.
+	 */
+static gchar *make_layer_group_string(LayerGroupType * lg)
+{
+	GString *string;
+	gint group, entry, layer;
+
+	string = g_string_new("");
+
+	for (group = 0; group < max_group; group++) {
+		if (lg->Number[group] == 0)
+			continue;
+		for (entry = 0; entry < lg->Number[group]; entry++) {
+			layer = lg->Entries[group][entry];
+			if (layer == component_silk_layer)
+				string = g_string_append(string, "c");
+			else if (layer == solder_silk_layer)
+				string = g_string_append(string, "s");
+			else
+				g_string_append_printf(string, "%d", layer + 1);
+
+			if (entry != lg->Number[group] - 1)
+				string = g_string_append(string, ",");
+		}
+		if (group != max_group - 1)
+			string = g_string_append(string, ":");
+	}
+	return g_string_free(string, FALSE);	/* Don't free string->str */
+}
+
+static void config_layers_apply(void)
+{
+	LayerType *layer;
+	const gchar *s;
+	gint group, i;
+	gint componentgroup = 0, soldergroup = 0;
+	gboolean layers_modified = FALSE;
+
+	/* Get each layer name entry and dup if modified into the PCB layer names
+	   |  and, if to use as default, the Settings layer names.
+	 */
+	for (i = 0; i < max_copper_layer; ++i) {
+		layer = &PCB->Data->Layer[i];
+		s = ghid_entry_get_text(layer_entry[i]);
+		if (dup_string((char**)&layer->Name, s))
+			layers_modified = TRUE;
+	}
+	/* Layer names can be changed from the menus and that can update the
+	   |  config.  So holdoff the loop.
+	 */
+	layers_applying = TRUE;
+	if (layers_modified)
+		ghid_layer_buttons_update();
+	layers_applying = FALSE;
+
+	if (groups_modified) {				/* If any group radio buttons were toggled. */
+		/* clear all entries and read layer by layer
+		 */
+		for (group = 0; group < max_group; group++)
+			layer_groups.Number[group] = 0;
+
+		for (i = 0; i < max_copper_layer + 2; i++) {
+			group = config_layer_group[i] - 1;
+			layer_groups.Entries[group][layer_groups.Number[group]++] = i;
+
+			if (i == component_silk_layer)
+				componentgroup = group;
+			else if (i == solder_silk_layer)
+				soldergroup = group;
+		}
+
+		/* do some cross-checking
+		   |  solder-side and component-side must be in different groups
+		   |  solder-side and component-side must not be the only one in the group
+		 */
+		if (layer_groups.Number[soldergroup] <= 1 || layer_groups.Number[componentgroup] <= 1) {
+			Message(PCB_MSG_DEFAULT, _("Both 'solder side' or 'component side' layers must have at least\n" "\tone other layer in their group.\n"));
+			return;
+		}
+		else if (soldergroup == componentgroup) {
+			Message(PCB_MSG_DEFAULT, _("The 'solder side' and 'component side' layers are not allowed\n" "\tto be in the same layer group #\n"));
+			return;
+		}
+		PCB->LayerGroups = layer_groups;
+		ghid_invalidate_all();
+		groups_modified = FALSE;
+	}
+}
+
+static void config_layer_group_button_state_update(void)
+{
+	gint g, i;
+
+	/* Set button active corresponding to layer group state.
+	 */
+	groups_holdoff = TRUE;
+	for (g = 0; g < max_group; g++)
+		for (i = 0; i < layer_groups.Number[g]; i++) {
+/*			printf("layer %d in group %d\n", layer_groups.Entries[g][i], g +1); */
+			config_layer_group[layer_groups.Entries[g][i]] = g + 1;
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(group_button[layer_groups.Entries[g][i]][g]), TRUE);
+		}
+	groups_holdoff = FALSE;
+}
+
+static void layer_name_entry_cb(GtkWidget * entry, gpointer data)
+{
+	gint i = GPOINTER_TO_INT(data);
+	LayerType *layer;
+	const gchar *name;
+
+	layer = &PCB->Data->Layer[i];
+	name = ghid_entry_get_text(entry);
+	if (dup_string((char**)&layer->Name, name))
+		ghid_layer_buttons_update();
+}
+
+void ghid_config_groups_changed(void)
+{
+	GtkWidget *vbox, *table, *button, *label, *scrolled_window;
+	GSList *group;
+	gchar buf[32];
+	const char *name;
+	gint layer, i;
+
+	if (!config_groups_vbox)
+		return;
+	vbox = config_groups_vbox;
+
+	if (config_groups_table)
+		gtk_widget_destroy(config_groups_table);
+	if (config_groups_window)
+		gtk_widget_destroy(config_groups_window);
+
+	config_groups_window = scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+	gtk_widget_set_size_request(scrolled_window, (max_group + 1)*34, 300);
+	gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 3);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+	gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
+	gtk_widget_show(scrolled_window);
+
+
+	table = gtk_table_new(max_copper_layer + 3, max_group + 1, FALSE);
+	config_groups_table = table;
+	gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), table);
+	gtk_widget_show(table);
+
+	layer_groups = PCB->LayerGroups;	/* working copy */
+	lg_monitor = &PCB->LayerGroups;	/* So can know if PCB changes on us */
+
+	label = gtk_label_new(_("Group #"));
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
+	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+
+	for (i = 1; i < max_group + 1; ++i) {
+		pcb_snprintf(buf, sizeof(buf), "% 3d", i);
+		label = gtk_label_new(buf);
+		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+		gtk_table_attach_defaults(GTK_TABLE(table), label, i, i + 1, 0, 1);
+	}
+
+	/* Create a row of radio toggle buttons for layer.  So each layer
+	   |  can have an active radio button set for the group it needs to be in.
+	 */
+	for (layer = 0; layer < max_copper_layer + 2; ++layer) {
+		if (layer == component_silk_layer)
+			name = _("component side");
+		else if (layer == solder_silk_layer)
+			name = _("solder side");
+		else
+			name = (gchar *) UNKNOWN(PCB->Data->Layer[layer].Name);
+
+		if (layer >= max_copper_layer) {
+			label = gtk_label_new(name);
+			gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+			gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, layer + 1, layer + 2);
+		}
+		else {
+			layer_entry[layer] = gtk_entry_new();
+			gtk_entry_set_text(GTK_ENTRY(layer_entry[layer]), name);
+			gtk_table_attach_defaults(GTK_TABLE(table), layer_entry[layer], 0, 1, layer + 1, layer + 2);
+			g_signal_connect(G_OBJECT(layer_entry[layer]), "activate", G_CALLBACK(layer_name_entry_cb), GINT_TO_POINTER(layer));
+		}
+
+		group = NULL;
+		for (i = 0; i < max_group; ++i) {
+			button = gtk_radio_button_new(group);
+
+			group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+			gtk_table_attach_defaults(GTK_TABLE(table), button, i + 1, i + 2, layer + 1, layer + 2);
+			g_signal_connect(G_OBJECT(button), "toggled",
+											 G_CALLBACK(config_layer_groups_radio_button_cb), GINT_TO_POINTER((layer << 8) | (i + 1)));
+			group_button[layer][i] = button;
+		}
+	}
+	gtk_widget_show_all(config_groups_vbox);
+	config_layer_group_button_state_update();
+}
+
+
+static void edit_layer_button_cb(GtkWidget * widget, gchar * data)
+{
+	gchar **argv;
+
+	if (PCB->RatDraw || PCB->SilkActive)
+		return;
+
+	argv = g_strsplit(data, ",", -1);
+	MoveLayerAction(2, argv, 0, 0);
+	g_strfreev(argv);
+}
+
+void config_layers_save(GtkButton *widget, save_ctx_t *ctx)
+{
+	gchar *s;
+	int n;
+	const char *paths[] = {
+		"design/groups",
+		"design/default_layer_name",
+		NULL
+	};
+
+
+	config_layers_apply();
+	s = make_layer_group_string(&PCB->LayerGroups);
+
+	/* change default layer names to the current ones in dest */
+	for (n = 0; n < max_copper_layer; n++) {
+		LayerType *layer;
+		char lnp[128];
+		lht_node_t *nd;
+		sprintf(lnp, "design/default_layer_name[%d]", n);
+		nd = conf_lht_get_at(ctx->dst_role, lnp, 1);
+		layer = &PCB->Data->Layer[n];
+		if (strcmp(layer->Name, nd->data.text.value) != 0) {
+			free(nd->data.text.value);
+			nd->data.text.value = pcb_strdup(layer->Name);
+			conf_makedirty(ctx->dst_role);
+		}
+	}
+	conf_update("design/default_layer_name");
+	conf_set(CFR_DESIGN, "design/groups", -1, s, POL_OVERWRITE);
+	g_free(s);
+	config_any_replace(ctx, paths);
+}
+
+static void config_layers_tab_create(GtkWidget * tab_vbox)
+{
+	GtkWidget *tabs, *vbox, *vbox1, *button, *text, *content_vbox;
+	GtkWidget *hbox, *arrow;
+	gint i;
+
+	content_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), content_vbox, TRUE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(content_vbox), 6);
+
+	tabs = gtk_notebook_new();
+	gtk_box_pack_start(GTK_BOX(content_vbox), tabs, TRUE, TRUE, 0);
+
+/* -- Change tab */
+	vbox = ghid_notebook_page(tabs, _("Change"), 0, 6);
+	vbox1 = ghid_category_vbox(vbox, _("Operations on currently selected layer:"), 4, 2, TRUE, TRUE);
+
+	button = gtk_button_new();
+	arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_ETCHED_IN);
+	gtk_container_add(GTK_CONTAINER(button), arrow);
+	g_signal_connect(G_OBJECT(button), (gchar *) "clicked", G_CALLBACK(edit_layer_button_cb), (gchar *) "c,up");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+	button = gtk_button_new();
+	arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_ETCHED_IN);
+	gtk_container_add(GTK_CONTAINER(button), arrow);
+	g_signal_connect(G_OBJECT(button), (gchar *) "clicked", G_CALLBACK(edit_layer_button_cb), (gchar *) "c,down");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+	g_signal_connect(G_OBJECT(button), (gchar *) "clicked", G_CALLBACK(edit_layer_button_cb), (gchar *) "c,-1");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+	vbox1 = ghid_category_vbox(vbox, _("Add new layer above currently selected layer:"), 4, 2, TRUE, TRUE);
+	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	g_signal_connect(G_OBJECT(button), (gchar *) "clicked", G_CALLBACK(edit_layer_button_cb), (gchar *) "-1,c");
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+/* -- Groups tab */
+	vbox = ghid_notebook_page(tabs, _("Groups"), 0, 6);
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
+
+	config_groups_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), config_groups_vbox, TRUE, TRUE, 0);
+	ghid_config_groups_changed();
+
+	/* A dummy vbox on the right to take up excess horizontal space so the layer list is not scretched horizontally */
+	vbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
+
+/* -- Info tab */
+	vbox = ghid_notebook_page(tabs, _("Info"), 0, 6);
+
+	text = ghid_scrolled_text_view(vbox, NULL, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	for (i = 0; i < sizeof(layer_info_text) / sizeof(gchar *); ++i)
+		ghid_text_view_append(text, _(layer_info_text[i]));
+
+/* -- common */
+	vbox = gtk_vbox_new(TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), vbox, TRUE, TRUE, 0);
+	config_user_role_section(tab_vbox, config_layers_save);
+}
+
+void ghid_config_layer_name_update(gchar * name, gint layer)
+{
+	if (!config_window || layers_applying || !name)
+		return;
+	gtk_entry_set_text(GTK_ENTRY(layer_entry[layer]), name);
+
+	/* If we get a config layer name change because a new PCB is loaded
+	   |  or new layout started, need to change our working layer group copy.
+	 */
+	if (lg_monitor != &PCB->LayerGroups) {
+		layer_groups = PCB->LayerGroups;
+		lg_monitor = &PCB->LayerGroups;
+		config_layer_group_button_state_update();
+		groups_modified = FALSE;
+	}
+}
+
+	/* -------------- The Colors config page ----------------
+	 */
+static GtkWidget *config_colors_vbox,
+	*config_colors_tab_vbox;
+
+static void config_colors_tab_create(GtkWidget * tab_vbox);
+
+typedef struct {
+	conf_native_t *cfg;
+	int idx;
+	GdkColor *color;
+	GtkWidget *button;
+} cfg_color_idx_t;
+
+static void config_color_set_cb(GtkWidget * button, cfg_color_idx_t *ci)
+{
+	GdkColor new_color;
+	const char *str;
+
+	gtk_color_button_get_color(GTK_COLOR_BUTTON(button), &new_color);
+	str = ghid_get_color_name(&new_color);
+
+	if (conf_set(CFR_DESIGN, ci->cfg->hash_path, ci->idx, str, POL_OVERWRITE) == 0) {
+		ghid_set_special_colors(ci->cfg);
+		ghid_layer_buttons_color_update();
+		ghid_invalidate_all();
+	}
+}
+
+static void config_color_button_create(GtkWidget * box, conf_native_t *cfg, int idx)
+{
+	GtkWidget *hbox, *label;
+	gchar *title;
+	cfg_color_idx_t *ci = conf_hid_get_data(cfg, ghid_conf_id);
+
+	hbox = gtk_hbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
+
+	if (ci == NULL) {
+#warning LEAK: this is never free()d
+		ci = malloc(sizeof(cfg_color_idx_t));
+		ci->cfg = cfg;
+		ci->idx = idx;
+		ci->color = calloc(sizeof(GdkColor), cfg->array_size);
+		conf_hid_set_data(cfg, ghid_conf_id, ci);
+	}
+
+	ghid_map_color_string(cfg->val.color[idx], &(ci->color[idx]));
+
+	title = g_strdup_printf(_("pcb-rnd %s Color"), cfg->description);
+	ci->button = gtk_color_button_new_with_color(&(ci->color[idx]));
+	gtk_color_button_set_title(GTK_COLOR_BUTTON(ci->button), title);
+	g_free(title);
+
+	gtk_box_pack_start(GTK_BOX(hbox), ci->button, FALSE, FALSE, 0);
+	label = gtk_label_new(cfg->description);
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(ci->button), "color-set", G_CALLBACK(config_color_set_cb), ci);
+}
+
+void config_color_button_update(conf_native_t *cfg, int idx)
+{
+	if (idx < 0) {
+		for(idx = 0; idx < cfg->array_size; idx++)
+			config_color_button_update(cfg, idx);
+	}
+	else {
+		cfg_color_idx_t *ci = conf_hid_get_data(cfg, ghid_conf_id);
+
+		ghid_map_color_string(cfg->val.color[idx], &(ci->color[idx]));
+		gtk_color_button_set_color(GTK_COLOR_BUTTON(ci->button), &(ci->color[idx]));
+	}
+}
+
+void config_colors_tab_create_scalar(GtkWidget *parent_vbox, const char *path_prefix, int selected)
+{
+	htsp_entry_t *e;
+	int pl = strlen(path_prefix);
+
+	conf_fields_foreach(e) {
+		conf_native_t *cfg = e->value;
+		if ((strncmp(e->key, path_prefix, pl) == 0) && (cfg->type == CFN_COLOR) && (cfg->array_size == 1)) {
+			int is_selected = (strstr(e->key, "_selected") != NULL);
+			if (is_selected == selected)
+				config_color_button_create(parent_vbox, cfg, 0);
+		}
+	}
+}
+
+void config_colors_tab_create_array(GtkWidget *parent_vbox, const char *path)
+{
+	conf_native_t *cfg = conf_get_field(path);
+	int n;
+	if (cfg->type != CFN_COLOR)
+		return;
+
+	for(n = 0; n < cfg->used; n++)
+		config_color_button_create(parent_vbox, cfg, n);
+}
+
+void config_colors_save(GtkButton *widget, save_ctx_t *ctx)
+{
+	const char *paths[] = {
+		"*appearance/color/",
+		NULL
+	};
+
+	config_any_replace(ctx, paths);
+}
+
+static void config_colors_tab_create(GtkWidget * tab_vbox)
+{
+	GtkWidget *scrolled_vbox, *vbox, *expander;
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(tab_vbox), vbox, TRUE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+
+	config_colors_vbox = vbox;		/* can be destroyed if color file loaded */
+	config_colors_tab_vbox = tab_vbox;
+
+	scrolled_vbox = ghid_scrolled_vbox(config_colors_vbox, NULL, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+	/* ---- Main colors ---- */
+	expander = gtk_expander_new(_("Main colors"));
+	gtk_box_pack_start(GTK_BOX(scrolled_vbox), expander, FALSE, FALSE, 2);
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(expander), vbox);
+	vbox = ghid_category_vbox(vbox, NULL, 0, 2, TRUE, FALSE);
+
+	config_colors_tab_create_scalar(vbox, "appearance/color", 0);
+
+	/* ---- Layer colors ---- */
+	expander = gtk_expander_new(_("Layer colors"));
+	gtk_box_pack_start(GTK_BOX(scrolled_vbox), expander, FALSE, FALSE, 2);
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(expander), vbox);
+	vbox = ghid_category_vbox(vbox, NULL, 0, 2, TRUE, FALSE);
+
+	config_colors_tab_create_array(vbox, "appearance/color/layer");
+
+	/* ---- Selected colors ---- */
+	expander = gtk_expander_new(_("Selected colors"));
+	gtk_box_pack_start(GTK_BOX(scrolled_vbox), expander, FALSE, FALSE, 2);
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(expander), vbox);
+	vbox = ghid_category_vbox(vbox, NULL, 0, 2, TRUE, FALSE);
+
+	config_colors_tab_create_scalar(vbox, "appearance/color", 1);
+
+	config_colors_tab_create_array(vbox, "appearance/color/layer_selected");
+	config_user_role_section(config_colors_vbox, config_colors_save);
+
+	gtk_widget_show_all(config_colors_vbox);
+}
+
+
+	/* --------------- The main config page -----------------
+	 */
+enum {
+	CONFIG_NAME_COLUMN,
+	CONFIG_PAGE_COLUMN,
+	CONFIG_PAGE_UPDATE_CB,
+	CONFIG_PAGE_DATA,
+	N_CONFIG_COLUMNS
+};
+
+
+static GtkWidget *config_page_create(GtkTreeStore * tree, GtkTreeIter * iter, GtkNotebook * notebook)
+{
+	GtkWidget *vbox;
+	gint page;
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_notebook_append_page(notebook, vbox, NULL);
+	page = gtk_notebook_get_n_pages(notebook) - 1;
+	gtk_tree_store_set(tree, iter, CONFIG_PAGE_COLUMN, page, CONFIG_PAGE_UPDATE_CB, NULL, CONFIG_PAGE_DATA, NULL, -1);
+	return vbox;
+}
+
+void ghid_config_handle_units_changed(void)
+{
+	char *text = pcb_strdup_printf("<b>%s</b>",
+																		conf_core.editor.grid_unit->in_suffix);
+	ghid_set_cursor_position_labels();
+	gtk_label_set_markup(GTK_LABEL(ghidgui->grid_units_label), text);
+	free(text);
+
+	if (config_sizes_vbox) {
+		gtk_widget_destroy(config_sizes_vbox);
+		config_sizes_vbox = NULL;
+		config_sizes_tab_create(config_sizes_tab_vbox);
+	}
+}
+
+void ghid_config_text_scale_update(void)
+{
+	if (config_window)
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_text_spin_button), (gdouble) conf_core.design.text_scale);
+}
+
+static void config_close_cb(gpointer data)
+{
+	/* Config pages may need to check for modified entries, use as default
+	   |  options, etc when the config window is closed.
+	 */
+	config_sizes_apply();
+	config_layers_apply();
+	config_library_apply();
+
+	config_sizes_vbox = NULL;
+	config_increments_vbox = NULL;
+
+	config_groups_vbox = config_groups_table = NULL;
+	config_groups_window = NULL;
+
+	gtk_widget_destroy(config_window);
+	config_window = NULL;
+}
+
+static void config_destroy_cb(gpointer data)
+{
+	config_sizes_vbox = NULL;
+	config_increments_vbox = NULL;
+	config_groups_vbox = config_groups_table = NULL;
+	config_groups_window = NULL;
+	gtk_widget_destroy(config_window);
+	config_window = NULL;
+}
+
+static void config_selection_changed_cb(GtkTreeSelection * selection, gpointer data)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	gint page;
+	void *cb, *page_data;
+
+	if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+		return;
+	gtk_tree_model_get(model, &iter, CONFIG_PAGE_COLUMN, &page, CONFIG_PAGE_UPDATE_CB, &cb, CONFIG_PAGE_DATA, &page_data, -1);
+	if (cb != NULL) {
+		void (*fnc)(void *data) = pcb_cast_d2f(cb);
+		fnc(page_data);
+	}
+	gtk_notebook_set_current_page(config_notebook, page);
+}
+
+/* Create a root (e.g. Config PoV) top level in the preference tree; iter is output and acts as a parent for further nodes */
+static void config_tree_sect(GtkTreeStore *model, GtkTreeIter *parent, GtkTreeIter *iter, const char *name, const char *desc)
+{
+	GtkWidget *vbox, *label;
+	gtk_tree_store_append(model, iter, parent);
+	gtk_tree_store_set(model, iter, CONFIG_NAME_COLUMN, name, -1);
+	vbox = config_page_create(model, iter, config_notebook);
+	label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), desc);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+}
+
+/* Create a leaf node with a custom tab */
+static GtkWidget *config_tree_leaf_(GtkTreeStore *model, GtkTreeIter *parent, const char *name, void (*tab_create)(GtkWidget *tab_vbox), GtkTreeIter *iter)
+{
+	GtkWidget *vbox = NULL;
+
+	gtk_tree_store_append(model, iter, parent);
+	gtk_tree_store_set(model, iter, CONFIG_NAME_COLUMN, name, -1);
+	if (tab_create != NULL) {
+		vbox = config_page_create(model, iter, config_notebook);
+		tab_create(vbox);
+	}
+	return vbox;
+}
+
+static GtkWidget *config_tree_leaf(GtkTreeStore *model, GtkTreeIter *parent, const char *name, void (*tab_create)(GtkWidget *tab_vbox))
+{
+	GtkTreeIter iter;
+	return config_tree_leaf_(model, parent, name, tab_create, &iter);
+}
+
+/***** auto *****/
+static struct {
+	GtkWidget *name, *desc, *src;
+
+	GtkWidget *edit_idx_box;
+	GtkWidget *edit_idx;
+
+	GtkWidget *edit_idx_new;
+
+	GtkWidget *edit_string;
+	GtkWidget *edit_coord;
+	GtkWidget *edit_int;
+	GtkWidget *edit_real;
+	GtkWidget *edit_boolean;
+	GtkWidget *edit_color;
+	GtkWidget *edit_unit;
+	GtkWidget *edit_list;
+
+	GtkWidget *result;
+	GtkWidget *finalize; /* finalzie hbox */
+
+	GtkWidget *btn_apply;
+	GtkWidget *btn_reset;
+	GtkWidget *btn_remove;
+	GtkWidget *txt_apply;
+
+	GtkWidget *btn_create;
+
+	GtkAdjustment *edit_idx_adj;
+	GtkAdjustment *edit_int_adj;
+	GtkAdjustment *edit_real_adj;
+	GdkColor color;
+	gtk_conf_list_t cl;
+
+	GtkListStore *src_l, *res_l;
+	GtkWidget *src_t, *res_t;
+	conf_native_t *nat;
+} auto_tab_widgets;
+
+static void config_auto_src_changed_cb(GtkTreeView *tree, void *data);
+static void config_auto_idx_changed_cb(GtkTreeView *tree, void *data);
+static void config_auto_idx_create_cb(GtkButton *btn, void *data);
+static void config_auto_idx_remove_cb(GtkButton *btn, void *data);
+static void config_auto_apply_cb(GtkButton *btn, void *data);
+static void config_auto_reset_cb(GtkButton *btn, void *data);
+static void config_auto_remove_cb(GtkButton *btn, void *data);
+static void config_auto_create_cb(GtkButton *btn, void *data);
+static void config_page_update_auto(void *data);
+
+/* Evaluates to 1 if the user canedit the config for this role */
+#define EDITABLE_ROLE(role) ((role == CFR_USER)  || (role == CFR_DESIGN) || (role == CFR_CLI) || ((role == CFR_PROJECT) && (PCB != NULL) && (PCB->Filename != NULL)))
+
+static void config_auto_tab_create(GtkWidget * tab_vbox, const char *basename)
+{
+	GtkWidget *vbox, *src, *src_left, *src_right, *w;
+
+	gtk_container_set_border_width(GTK_CONTAINER(tab_vbox), 6);
+	vbox = ghid_category_vbox(tab_vbox, "Configuration node", 4, 2, TRUE, TRUE);
+
+	/* header */
+	auto_tab_widgets.name = gtk_label_new("setting name");
+	gtk_label_set_use_markup(GTK_LABEL(auto_tab_widgets.name), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), auto_tab_widgets.name, FALSE, FALSE, 0);
+	gtk_misc_set_alignment(GTK_MISC(auto_tab_widgets.name), 0., 0.);
+
+	auto_tab_widgets.desc = gtk_label_new("setting desc");
+	gtk_box_pack_start(GTK_BOX(vbox), auto_tab_widgets.desc, FALSE, FALSE, 0);
+	gtk_misc_set_alignment(GTK_MISC(auto_tab_widgets.desc), 0., 0.);
+
+
+	/* upper hbox */
+	gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 4);
+	src = gtk_hbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(vbox), src, FALSE, FALSE, 4);
+
+	src_left = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(src), src_left, FALSE, FALSE, 4);
+	src_right = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(src), src_right, FALSE, FALSE, 4);
+
+	/* upper-left: sources */
+	{
+		static const char *col_names[] = {"role", "prio", "policy", "value"};
+		static GType ty[] = {G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING};
+		const char **s;
+		int n, num_cols = sizeof(col_names)/sizeof(col_names[0]);
+		auto_tab_widgets.src_t = gtk_tree_view_new();
+		auto_tab_widgets.src_l = gtk_list_store_newv(num_cols, ty);
+		for(n = 0, s = col_names; n < num_cols; n++,s++) {
+			GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+			gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(auto_tab_widgets.src_t), -1, *s, renderer, "text", n, NULL);
+		}
+		gtk_tree_view_set_model(GTK_TREE_VIEW(auto_tab_widgets.src_t), GTK_TREE_MODEL(auto_tab_widgets.src_l));
+		gtk_box_pack_start(GTK_BOX(src_left), auto_tab_widgets.src_t, FALSE, FALSE, 4);
+		g_signal_connect(G_OBJECT(auto_tab_widgets.src_t), "cursor-changed", G_CALLBACK(config_auto_src_changed_cb), NULL);
+	}
+
+
+	/* upper-right: edit data */
+	gtk_box_pack_start(GTK_BOX(src_right), gtk_label_new("Edit value of the selected source"), FALSE, FALSE, 0);
+
+	auto_tab_widgets.src = gtk_label_new("source");
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.src, FALSE, FALSE, 0);
+
+	{ /* array index bar */
+		auto_tab_widgets.edit_idx_box = gtk_hbox_new(FALSE, 4);
+		gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_idx_box, FALSE, FALSE, 4);
+
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.edit_idx_box), gtk_label_new("Array index:"), FALSE, FALSE, 4);
+
+		auto_tab_widgets.edit_idx_adj = GTK_ADJUSTMENT(gtk_adjustment_new(10,
+		                                                                  0, /* min */
+		                                                                  4,
+		                                                                  1, 1, /* steps */
+		                                                                  0.0));
+		g_signal_connect(G_OBJECT(auto_tab_widgets.edit_idx_adj), "value-changed", G_CALLBACK(config_auto_idx_changed_cb), NULL);
+		auto_tab_widgets.edit_idx = gtk_spin_button_new(auto_tab_widgets.edit_idx_adj, 1, 4);
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.edit_idx_box), auto_tab_widgets.edit_idx, FALSE, FALSE, 4);
+
+
+		w = gtk_button_new_with_label("Append item");
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.edit_idx_box), w, FALSE, FALSE, 0);
+		g_signal_connect(GTK_OBJECT(w), "clicked", G_CALLBACK(config_auto_idx_create_cb), NULL);
+
+		w = gtk_button_new_with_label("Remove item");
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.edit_idx_box), w, FALSE, FALSE, 0);
+		g_signal_connect(GTK_OBJECT(w), "clicked", G_CALLBACK(config_auto_idx_remove_cb), NULL);
+
+	}
+
+
+	auto_tab_widgets.edit_string = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_string, FALSE, FALSE, 4);
+
+	auto_tab_widgets.edit_coord = ghid_coord_entry_new(10, PCB_MM_TO_COORD(1000), 0, conf_core.editor.grid_unit, CE_TINY);
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_coord, FALSE, FALSE, 4);
+
+	auto_tab_widgets.edit_int_adj = GTK_ADJUSTMENT(gtk_adjustment_new(10,
+	                                                                  0, /* min */
+	                                                                  20000,
+	                                                                  1, 10, /* steps */
+	                                                                  0.0));
+	auto_tab_widgets.edit_int = gtk_spin_button_new(auto_tab_widgets.edit_int_adj, 1, 4);
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_int, FALSE, FALSE, 4);
+
+	auto_tab_widgets.edit_real_adj = GTK_ADJUSTMENT(gtk_adjustment_new(10,
+	                                                                   0, /* min */
+	                                                                   20000,
+	                                                                   1, 10, /* steps */
+	                                                                   0.0));
+	auto_tab_widgets.edit_real = gtk_spin_button_new(auto_tab_widgets.edit_real_adj, 1, 4);
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_real, FALSE, FALSE, 4);
+
+	ghid_check_button_connected(src_right, &auto_tab_widgets.edit_boolean, 0,
+	                            TRUE, FALSE, FALSE, 2,
+	                            NULL, NULL, NULL);
+
+	auto_tab_widgets.edit_color = gtk_color_button_new();
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_color, FALSE, FALSE, 4);
+
+	auto_tab_widgets.edit_unit = gtk_combo_box_new_text();
+	gtk_combo_box_append_text(GTK_COMBO_BOX(auto_tab_widgets.edit_unit), "mm");
+	gtk_combo_box_append_text(GTK_COMBO_BOX(auto_tab_widgets.edit_unit), "mil");
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_unit, FALSE, FALSE, 4);
+
+	{ /* list */
+		static const char *col_names[] = {"list items"};
+		auto_tab_widgets.cl.num_cols = 1;
+		auto_tab_widgets.cl.col_names = col_names;
+		auto_tab_widgets.cl.col_data = 0;
+		auto_tab_widgets.cl.col_src = -1;
+		auto_tab_widgets.cl.reorder = 1;
+		auto_tab_widgets.cl.inhibit_rebuild = 1;
+		auto_tab_widgets.cl.lst = NULL;
+		auto_tab_widgets.cl.pre_rebuild = auto_tab_widgets.cl.post_rebuild = NULL;
+		auto_tab_widgets.cl.get_misc_col_data = NULL;
+		auto_tab_widgets.cl.file_chooser_title = NULL;
+		auto_tab_widgets.edit_list = gtk_conf_list_widget(&auto_tab_widgets.cl);
+		gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.edit_list, FALSE, FALSE, 4);
+	}
+
+	/* Apply/cancel buttons */
+	{
+		auto_tab_widgets.finalize = gtk_hbox_new(FALSE, 0);
+
+		auto_tab_widgets.btn_apply = w = gtk_button_new_with_label("Apply");
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.finalize), w, FALSE, FALSE, 0);
+		g_signal_connect(GTK_OBJECT(w), "clicked", G_CALLBACK(config_auto_apply_cb), NULL);
+
+		auto_tab_widgets.btn_reset = w = gtk_button_new_with_label("Reset");
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.finalize), w, FALSE, FALSE, 0);
+		g_signal_connect(GTK_OBJECT(w), "clicked", G_CALLBACK(config_auto_reset_cb), NULL);
+
+		auto_tab_widgets.btn_remove = w = gtk_button_new_with_label("Remove");
+		gtk_box_pack_start(GTK_BOX(auto_tab_widgets.finalize), w, FALSE, FALSE, 0);
+		g_signal_connect(GTK_OBJECT(w), "clicked", G_CALLBACK(config_auto_remove_cb), NULL);
+
+		gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.finalize, FALSE, FALSE, 0);
+	}
+	
+	auto_tab_widgets.txt_apply = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(src_right), auto_tab_widgets.txt_apply, FALSE, FALSE, 0);
+	gtk_misc_set_alignment(GTK_MISC(auto_tab_widgets.txt_apply), 0., 0.);
+
+	auto_tab_widgets.btn_create = w = gtk_button_new_with_label("Create item in selected source");
+	gtk_box_pack_start(GTK_BOX(src_right), w, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(w), "clicked", G_CALLBACK(config_auto_create_cb), NULL);
+
+
+	/* lower hbox for displaying the rendered value */
+	gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 4);
+	src = gtk_hbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(vbox), src, FALSE, FALSE, 4);
+
+	w = gtk_label_new("Resulting native configuration value, after merging all sources above:");
+	gtk_box_pack_start(GTK_BOX(vbox), w, FALSE, FALSE, 0);
+	gtk_misc_set_alignment(GTK_MISC(w), 0., 0.);
+
+	{
+		static const char *col_names[] = {"index", "role & prio", "value"};
+		static GType ty[] = {G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING};
+		const char **s;
+		int n, num_cols = sizeof(col_names)/sizeof(col_names[0]);
+		auto_tab_widgets.res_t = gtk_tree_view_new();
+		auto_tab_widgets.res_l = gtk_list_store_newv(num_cols, ty);
+		for(n = 0, s = col_names; n < num_cols; n++,s++) {
+			GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+			gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(auto_tab_widgets.res_t), -1, *s, renderer, "text", n, NULL);
+		}
+		gtk_tree_view_set_model(GTK_TREE_VIEW(auto_tab_widgets.res_t), GTK_TREE_MODEL(auto_tab_widgets.res_l));
+		gtk_box_pack_start(GTK_BOX(vbox), auto_tab_widgets.res_t, FALSE, FALSE, 4);
+	}
+}
+
+/* hide all source edit widgets */
+static void config_auto_src_hide(void)
+{
+	gtk_widget_hide(auto_tab_widgets.edit_string);
+	gtk_widget_hide(auto_tab_widgets.edit_coord);
+	gtk_widget_hide(auto_tab_widgets.edit_int);
+	gtk_widget_hide(auto_tab_widgets.edit_real);
+	gtk_widget_hide(auto_tab_widgets.edit_boolean);
+	gtk_widget_hide(auto_tab_widgets.edit_color);
+	gtk_widget_hide(auto_tab_widgets.edit_unit);
+	gtk_widget_hide(auto_tab_widgets.edit_list);
+	gtk_widget_hide(auto_tab_widgets.src);
+	gtk_widget_hide(auto_tab_widgets.finalize);
+
+}
+
+/* Return the nth child of a list - only if it's really a list; if idx < 0,
+   use the index adjustment value */
+static lht_node_t *config_auto_get_nth(const lht_node_t *list, int idx)
+{
+	const lht_node_t *nd;
+
+	if (list->type != LHT_LIST)
+		return NULL;
+
+	if (idx < 0)
+		idx = gtk_adjustment_get_value(auto_tab_widgets.edit_idx_adj);
+
+	for(nd = list->data.list.first; (idx > 0) && (nd != NULL); nd = nd->next, idx--) ;
+
+	return (lht_node_t *)nd;
+}
+
+/* set up all source edit widgets for a lihata source node */
+static void config_auto_src_show(lht_node_t *nd)
+{
+	conf_native_t *nat = auto_tab_widgets.nat;
+	char *tmp;
+	int l;
+	confitem_t citem;
+
+	if (nd != NULL) {
+		tmp = pcb_strdup_printf("%s:%d.%d", nd->file_name, nd->line, nd->col);
+		gtk_label_set(GTK_LABEL(auto_tab_widgets.src), tmp);
+		free(tmp);
+		gtk_widget_show(auto_tab_widgets.src);
+	}
+	else
+		gtk_widget_hide(auto_tab_widgets.src);
+
+	/* don't allow arrays of lists atm */
+	if (nat->type == CFN_LIST) {
+		if (nd->type != LHT_LIST)
+			return;
+	}
+	if (nat->array_size > 1) {
+		nd = config_auto_get_nth(nd, -1);
+		if (nd == NULL)
+			return;
+		if (nd->type != LHT_TEXT)
+			return;
+	}
+	else {
+		if (nd->type != LHT_TEXT)
+			return;
+	}
+
+	memset(&citem, 0, sizeof(citem));
+
+	switch(nat->type) {
+		case CFN_STRING:
+			gtk_entry_set_text(GTK_ENTRY(auto_tab_widgets.edit_string), nd->data.text.value);
+			gtk_widget_show(auto_tab_widgets.edit_string);
+			break;
+		case CFN_COORD:
+			{
+				Coord coord = 0;
+				citem.coord = &coord;
+				conf_parse_text(&citem, 0, nat->type, nd->data.text.value, nd);
+				ghid_coord_entry_set_value(GHID_COORD_ENTRY(auto_tab_widgets.edit_coord), coord);
+				gtk_widget_show(auto_tab_widgets.edit_coord);
+			}
+			break;
+		case CFN_INTEGER:
+			{
+				long i = 0;
+				citem.integer = &i;
+				conf_parse_text(&citem, 0, nat->type, nd->data.text.value, nd);
+				gtk_adjustment_set_value(GTK_ADJUSTMENT(auto_tab_widgets.edit_int_adj), i);
+				gtk_widget_show(auto_tab_widgets.edit_int);
+			}
+			break;
+		case CFN_REAL:
+			{
+				double d = 0;
+				citem.real = &d;
+				conf_parse_text(&citem, 0, nat->type, nd->data.text.value, nd);
+				gtk_adjustment_set_value(GTK_ADJUSTMENT(auto_tab_widgets.edit_real_adj), d);
+				gtk_widget_show(auto_tab_widgets.edit_real);
+			}
+			break;
+		case CFN_BOOLEAN:
+			{
+				int b = 0;
+				citem.boolean = &b;
+				conf_parse_text(&citem, 0, nat->type, nd->data.text.value, nd);
+				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(auto_tab_widgets.edit_boolean), b);
+				gtk_widget_show(auto_tab_widgets.edit_boolean);
+			}
+			break;
+		case CFN_COLOR:
+			ghid_map_color_string(nd->data.text.value, &auto_tab_widgets.color);
+			gtk_color_button_set_color(GTK_COLOR_BUTTON(auto_tab_widgets.edit_color), &auto_tab_widgets.color);
+			gtk_widget_show(auto_tab_widgets.edit_color);
+			break;
+		case CFN_UNIT:
+			{
+				const Unit *u = NULL;
+				citem.unit = &u;
+				conf_parse_text(&citem, 0, nat->type, nd->data.text.value, nd);
+				if (citem.unit[0] == NULL)
+					l = -1;
+				else if (strcmp(citem.unit[0]->suffix, "mm") == 0)
+					l = 0;
+				else if (strcmp(citem.unit[0]->suffix, "mil") == 0)
+					l = 1;
+				else
+					l = -1;
+				gtk_combo_box_set_active(GTK_COMBO_BOX(auto_tab_widgets.edit_unit), l);
+				gtk_widget_show(auto_tab_widgets.edit_unit);
+			}
+			break;
+		case CFN_LIST:
+			gtk_conf_list_set_list(&auto_tab_widgets.cl, nd);
+			gtk_widget_show(auto_tab_widgets.edit_list);
+			break;
+	}
+	gtk_widget_show(auto_tab_widgets.finalize);
+}
+
+static void config_auto_res_show_add(const char *s_idx, const confprop_t *prop, const char *val)
+{
+	GtkTreeIter iter;
+	char s_pol_prio[256];
+
+	if (prop->src != NULL)
+		sprintf(s_pol_prio, "%s-%d", conf_role_name(conf_lookup_role(prop->src)), prop->prio);
+	else
+		sprintf(s_pol_prio, "%d", prop->prio);
+
+	gtk_list_store_append(auto_tab_widgets.res_l, &iter);
+	gtk_list_store_set(auto_tab_widgets.res_l, &iter,
+		0, s_idx,
+		1, s_pol_prio,
+		2, val,
+		-1);
+}
+
+/* Fill in the result table on the bottom using the native value */
+static void config_auto_res_show(void)
+{
+	char s_idx[16];
+	conf_native_t *nat = auto_tab_widgets.nat;
+	gds_t buff;
+
+	gtk_list_store_clear(auto_tab_widgets.res_l);
+
+	/* This code addmitedly does not handle array of lists - for now; once the
+	   list-loop below is moved to config_auto_res_show_add, it will work */
+	if(nat->type == CFN_LIST) {
+		conf_listitem_t *n;
+		for(n = conflist_first(nat->val.list); n != NULL; n = conflist_next(n)) {
+			gds_init(&buff);
+			conf_print_native_field((conf_pfn)pcb_append_printf, &buff, 0, &n->val, n->type, &n->prop, 0);
+			config_auto_res_show_add("0", &n->prop, buff.array);
+			gds_uninit(&buff);
+		}
+	}
+	else {
+		pcb_cardinal_t n;
+		for(n = 0; n < nat->used; n++) {
+			if (nat->array_size >= 2)
+				sprintf(s_idx, "%d", n);
+			else
+				*s_idx = '\0';
+			gds_init(&buff);
+			conf_print_native_field((conf_pfn)pcb_append_printf, &buff, 0, &nat->val, nat->type, nat->prop, n);
+			config_auto_res_show_add(s_idx, &nat->prop[n], buff.array);
+			gds_uninit(&buff);
+		}
+	}
+}
+
+/* return the role we are currently editing */
+static conf_role_t config_auto_get_edited_role(void)
+{
+	GtkTreePath *p;
+	gint *i;
+	int role = CFR_invalid;
+
+	gtk_tree_view_get_cursor(GTK_TREE_VIEW(auto_tab_widgets.src_t), &p, NULL);
+	i = gtk_tree_path_get_indices(p);
+	if (i != NULL)
+		role = i[0];
+	gtk_tree_path_free(p);
+	return role;
+}
+
+static conf_auto_set_edited_role(conf_role_t r)
+{
+	GtkTreePath *p = gtk_tree_path_new_from_indices(r, -1);
+	gtk_tree_view_set_cursor(GTK_TREE_VIEW(auto_tab_widgets.src_t), p, NULL, 0);
+	gtk_tree_path_free(p);
+}
+
+
+/* Update the conf item edit section; called when a source is clicked */
+static void config_auto_src_changed_cb(GtkTreeView *tree, void *data)
+{
+	int role = config_auto_get_edited_role(), idx, len = 0;
+	lht_node_t *nd;
+	int allow_idx = 0;
+
+	gtk_widget_hide(auto_tab_widgets.edit_idx_box);
+
+	if (role != CFR_invalid) {
+		nd = conf_lht_get_at(role, auto_tab_widgets.nat->hash_path, 0);
+		if (nd != NULL) {
+			config_auto_src_show(nd);
+			gtk_widget_hide(auto_tab_widgets.btn_create);
+			allow_idx = 0;
+			if (nd->type == LHT_LIST) {
+				lht_node_t *n;
+				for(n = nd->data.list.first; n != NULL; n = n->next)
+					len++;
+			}
+		}
+		else {
+			config_auto_src_hide();
+			if (EDITABLE_ROLE(role))
+				gtk_widget_show(auto_tab_widgets.btn_create);
+			else
+				gtk_widget_hide(auto_tab_widgets.btn_create);
+		}
+	}
+
+	if (EDITABLE_ROLE(role)) {
+		gtk_widget_set_sensitive(auto_tab_widgets.btn_apply, 1);
+		gtk_widget_set_sensitive(auto_tab_widgets.btn_reset, 1);
+		gtk_label_set_text(GTK_LABEL(auto_tab_widgets.txt_apply), "");
+		if (conf_lht_get_at(role, auto_tab_widgets.nat->hash_path, 0) != NULL)
+			allow_idx = 1;
+	}
+	else {
+		gtk_widget_set_sensitive(auto_tab_widgets.btn_apply, 0);
+		gtk_widget_set_sensitive(auto_tab_widgets.btn_reset, 0);
+		gtk_label_set_text(GTK_LABEL(auto_tab_widgets.txt_apply), "(Can't write config source)");
+	}
+
+	/* adjust array index widget state and max according to our actual array size */
+	idx = gtk_adjustment_get_value(auto_tab_widgets.edit_idx_adj);
+	if (idx >= len)
+		gtk_adjustment_set_value(auto_tab_widgets.edit_idx_adj, len-1);
+
+	gtk_adjustment_set_upper(auto_tab_widgets.edit_idx_adj, len-1);
+
+	if ((allow_idx) && (auto_tab_widgets.nat->array_size > 1))
+		gtk_widget_show(auto_tab_widgets.edit_idx_box);
+}
+
+static void config_auto_idx_changed_cb(GtkTreeView *tree, void *data)
+{
+	int role = config_auto_get_edited_role();
+	if (role != CFR_invalid) {
+		lht_node_t *nd = conf_lht_get_at(role, auto_tab_widgets.nat->hash_path, 0);
+		if (nd != NULL)
+			config_auto_src_show(nd);
+	}
+}
+
+static void config_auto_idx_deladd_cb(int del)
+{
+	int role = config_auto_get_edited_role();
+	if (role != CFR_invalid) {
+		if (del) {
+			int idx = gtk_adjustment_get_value(auto_tab_widgets.edit_idx_adj);
+			conf_set(role, auto_tab_widgets.nat->hash_path, idx, NULL, POL_OVERWRITE);
+		}
+		else
+			conf_set(role, auto_tab_widgets.nat->hash_path, -1, "", POL_APPEND);
+		config_auto_src_changed_cb(GTK_TREE_VIEW(auto_tab_widgets.src_t), NULL);
+		config_auto_res_show();
+	}
+}
+
+static void config_auto_idx_create_cb(GtkButton *btn, void *data)
+{
+	config_auto_idx_deladd_cb(0);
+}
+
+static void config_auto_idx_remove_cb(GtkButton *btn, void *data)
+{
+	config_auto_idx_deladd_cb(1);
+}
+
+static void config_auto_save(conf_role_t role)
+{
+	const char *pcbfn = (PCB == NULL ? NULL : PCB->Filename);
+	/* Can't save the CLI */
+	if (role == CFR_CLI)
+		return;
+
+	if (role == CFR_PROJECT) {
+		const char *try;
+		const char *fn = conf_get_project_conf_name(NULL, pcbfn, &try);
+		if (fn == NULL) {
+			FILE *f;
+			f = fopen(try, "w");
+			if (f == NULL) {
+				Message(PCB_MSG_ERROR, "can not create config to project file: %s\n", try);
+				return;
+			}
+			fclose(f);
+		}
+	}
+
+	conf_save_file(NULL, pcbfn, role, NULL);
+}
+
+static void config_auto_apply_cb(GtkButton *btn, void *data)
+{
+	conf_native_t *nat = auto_tab_widgets.nat;
+	conf_role_t role = config_auto_get_edited_role();
+	char buff[128];
+	const char *new_val = NULL;
+	int arr_idx = -1;
+	int update_clr = 0;
+
+	switch(nat->type) {
+		case CFN_STRING:
+			new_val = gtk_entry_get_text(GTK_ENTRY(auto_tab_widgets.edit_string));
+			break;
+		case CFN_COORD:
+			ghid_coord_entry_get_value_str(auto_tab_widgets.edit_coord, buff, sizeof(buff));
+			new_val = buff;
+			break;
+		case CFN_INTEGER:
+			sprintf(buff, "%.0f", gtk_adjustment_get_value(GTK_ADJUSTMENT(auto_tab_widgets.edit_int_adj)));
+			new_val = buff;
+			break;
+		case CFN_REAL:
+			sprintf(buff, "%.16f", gtk_adjustment_get_value(GTK_ADJUSTMENT(auto_tab_widgets.edit_int_adj)));
+			new_val = buff;
+			break;
+		case CFN_BOOLEAN:
+			if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_tab_widgets.edit_boolean)))
+				new_val = "1";
+			else
+				new_val = "0";
+			break;
+		case CFN_COLOR:
+			{
+				GdkColor clr;
+				gtk_color_button_get_color(GTK_COLOR_BUTTON(auto_tab_widgets.edit_color), &clr);
+				sprintf(buff, "#%02x%02x%02x", (clr.red >> 8) & 0xFF, (clr.green >> 8) & 0xFF, (clr.blue >> 8) & 0xFF);
+				new_val = buff;
+				update_clr = 1;
+			}
+			break;
+		case CFN_UNIT:
+			{
+				char *s = gtk_combo_box_get_active_text(GTK_COMBO_BOX(auto_tab_widgets.edit_unit));
+				strcpy(buff, s);
+				new_val = buff;
+				g_free(s);
+			}
+			break;
+		case CFN_LIST:
+			{
+				GtkTreeModel *tm = gtk_tree_view_get_model(GTK_TREE_VIEW(auto_tab_widgets.cl.t));
+				GtkTreeIter it;
+				gboolean valid;
+				int n;
+
+				for(valid = gtk_tree_model_get_iter_first(tm, &it), n = 0; valid; valid = gtk_tree_model_iter_next(tm, &it), n++) {
+					gchar *s;
+					gtk_tree_model_get(tm, &it, auto_tab_widgets.cl.col_data, &s, -1);
+					conf_set_dry(role, nat->hash_path, -1, pcb_strdup(s), (n == 0) ? POL_OVERWRITE : POL_APPEND);
+					g_free(s);
+				}
+				conf_update(nat->hash_path);
+				config_auto_save(role);
+			}
+			new_val = NULL; /* do not run conf_set, but run the rest of the updates */
+			break;
+	}
+
+	if (nat->array_size > 1)
+		arr_idx = gtk_adjustment_get_value(auto_tab_widgets.edit_idx_adj);
+
+	if (new_val != NULL) {
+		conf_set(role, nat->hash_path, arr_idx, new_val, POL_OVERWRITE);
+		config_auto_save(role);
+	}
+
+	config_page_update_auto(nat);
+	if (update_clr) {
+#warning TODO: conf hooks should solve these
+		ghid_set_special_colors(nat);
+		ghid_layer_buttons_color_update();
+		ghid_invalidate_all();
+	}
+}
+
+static void config_auto_reset_cb(GtkButton *btn, void *data)
+{
+	config_auto_src_changed_cb(GTK_TREE_VIEW(auto_tab_widgets.src_t), NULL);
+}
+
+static void config_auto_remove_cb(GtkButton *btn, void *data)
+{
+	conf_native_t *nat = auto_tab_widgets.nat;
+	conf_role_t role = config_auto_get_edited_role();
+
+	if (nat->array_size > 1) {
+		Message(PCB_MSG_ERROR, "Can't create remove array %s\n", nat->hash_path);
+		return;
+	}
+
+	conf_del(role, nat->hash_path, -1);
+
+	config_page_update_auto(nat);
+	config_auto_save(role);
+	conf_auto_set_edited_role(role);
+}
+
+static void config_auto_create_cb(GtkButton *btn, void *data)
+{
+	gds_t s;
+	char *val;
+	conf_native_t *nat = auto_tab_widgets.nat;
+	conf_role_t role = config_auto_get_edited_role();
+
+	/* create the config node in the source lht */
+	gds_init(&s);
+	conf_print_native_field((conf_pfn)pcb_append_printf, &s, 0, &nat->val, nat->type, &nat->prop[0], 0);
+	val = s.array;
+
+	/* strip {} from arrays */
+	if (nat->array_size > 1) {
+		int end;
+		while(*val == '{') val++;
+		end = gds_len(&s) - (val - s.array);
+		if (end > 0)
+			s.array[end] = '\0';
+	}
+
+	conf_set(role, nat->hash_path, -1, val, POL_OVERWRITE);
+	gds_uninit(&s);
+
+	config_page_update_auto(nat);
+	config_auto_save(role);
+	conf_auto_set_edited_role(role);
+}
+
+
+/* Update the config tab for a given entry - called when a new config item is clicked/selected from the tree */
+static void config_page_update_auto(void *data)
+{
+	char *tmp, *so;
+	const char *si;
+	int l, n;
+	conf_native_t *nat = data;
+	lht_node_t *nd;
+
+	/* set name */
+	gtk_label_set(GTK_LABEL(auto_tab_widgets.name), nat->hash_path);
+
+	auto_tab_widgets.nat = data;
+
+	/* split long description strings at whitepsace to make the preferences window narrower */
+	tmp = malloc(strlen(nat->description) * 2);
+	for(si = nat->description, so = tmp, l = 0; *si != '\0'; si++,so++,l++) {
+		if (isspace(*si) && (l > 64))
+			*so = '\n';
+		else
+			*so = *si;
+
+		if (*so == '\n')
+			l = 0;
+	}
+	*so = '\0';
+	gtk_label_set(GTK_LABEL(auto_tab_widgets.desc), tmp);
+	free(tmp);
+
+	config_auto_src_hide();
+
+	/* build the source table */
+	gtk_list_store_clear(auto_tab_widgets.src_l);
+	for(n = 0; n < CFR_max_real; n++) {
+		GtkTreeIter iter;
+		long prio = conf_default_prio[n];
+		conf_policy_t pol = POL_OVERWRITE;
+
+		nd = conf_lht_get_at(n, nat->hash_path, 0);
+		if (nd != NULL)
+			conf_get_policy_prio(nd, &pol, &prio);
+
+		gtk_list_store_append(auto_tab_widgets.src_l, &iter);
+		if (nd != NULL) {
+			char sprio[32];
+			const char *val;
+
+			switch(nd->type) {
+				case LHT_TEXT: val = nd->data.text.value; break;
+				case LHT_LIST: val = "<list>"; break;
+				case LHT_HASH: val = "<hash>"; break;
+				case LHT_TABLE: val = "<table>"; break;
+				case LHT_SYMLINK: val = "<symlink>"; break;
+				case LHT_INVALID_TYPE: val = "<invalid>"; break;
+			}
+			sprintf(sprio, "%ld", prio);
+			gtk_list_store_set(auto_tab_widgets.src_l, &iter,
+				0, conf_role_name(n),
+				1, sprio,
+				2, conf_policy_name(pol),
+				3, val,
+				-1);
+		}
+		else {
+			gtk_list_store_set(auto_tab_widgets.src_l, &iter,
+				0, conf_role_name(n),
+				-1);
+		}
+	}
+	gtk_widget_hide(auto_tab_widgets.edit_idx_box);
+	gtk_label_set_text(GTK_LABEL(auto_tab_widgets.txt_apply), "");
+	gtk_widget_hide(auto_tab_widgets.btn_create);
+	config_auto_res_show();
+}
+
+static GtkTreeIter *config_tree_auto_mkdirp(GtkTreeStore *model, GtkTreeIter *main_parent, htsp_t *dirs, char *path)
+{
+	char *basename;
+	GtkTreeIter *parent, *cwd;
+
+	cwd = htsp_get(dirs, path);
+	if (cwd != NULL)
+		return cwd;
+
+	cwd = malloc(sizeof(GtkTreeIter));
+	htsp_set(dirs, path, cwd);
+
+	basename = strrchr(path, '/');
+	if (basename == NULL) {
+		parent = main_parent;
+		basename = path;
+	}
+	else {
+		*basename = '\0';
+		basename++;
+		parent = config_tree_auto_mkdirp(model, main_parent, dirs, path);
+	}
+
+	config_tree_sect(model, parent, cwd, basename, "");
+	return cwd;
+}
+
+static int config_tree_auto_cmp(const void *v1, const void *v2)
+{
+	const htsp_entry_t **e1 = (const htsp_entry_t **)v1, **e2 = (const htsp_entry_t **)v2;
+	return strcmp((*e1)->key, (*e2)->key);
+}
+
+
+/* Automatically create a subtree using the central config field hash */
+static void config_tree_auto(GtkTreeStore *model, GtkTreeIter *main_parent)
+{
+	htsp_t *dirs;
+	htsp_entry_t *e;
+	char path[1024];
+	GtkTreeIter *parent;
+	htsp_entry_t **sorted;
+	int num_paths, n;
+	gint auto_page;
+
+	{
+		GtkWidget *vbox;
+		vbox = gtk_vbox_new(FALSE, 0);
+		gtk_notebook_append_page(config_notebook, vbox, NULL);
+		auto_page = gtk_notebook_get_n_pages(config_notebook) - 1;
+		config_auto_tab_create(vbox, "auto");
+	}
+
+	/* remember the parent for each dir */
+	dirs = htsp_alloc(strhash, strkeyeq);
+
+	/* alpha sort keys for the more consistend UI */
+	for (e = htsp_first(conf_fields), num_paths = 0; e; e = htsp_next(conf_fields, e))
+		num_paths++;
+	sorted = malloc(sizeof(htsp_entry_t *) * num_paths);
+	for (e = htsp_first(conf_fields), n = 0; e; e = htsp_next(conf_fields, e), n++)
+		sorted[n] = e;
+	qsort(sorted, num_paths, sizeof(htsp_entry_t *), config_tree_auto_cmp);
+
+	for (n = 0; n < num_paths; n++) {
+		GtkTreeIter iter;
+		char *basename;
+		e = sorted[n];
+		if (strlen(e->key) > sizeof(path)-1) {
+			Message(PCB_MSG_DEFAULT, "Warning: can't create config item for %s: path too long\n", e->key);
+			continue;
+		}
+		strcpy(path, e->key);
+		basename = strrchr(path, '/');
+		if ((basename == NULL) || (basename == path)) {
+			Message(PCB_MSG_DEFAULT, "Warning: can't create config item for %s: invalid path\n", e->key);
+			continue;
+		}
+		*basename = '\0';
+		basename++;
+		parent = config_tree_auto_mkdirp(model, main_parent, dirs, path);
+		config_tree_leaf_(model, parent, basename, NULL, &iter);
+		gtk_tree_store_set(model, &iter, CONFIG_PAGE_COLUMN, auto_page, CONFIG_PAGE_UPDATE_CB, config_page_update_auto, CONFIG_PAGE_DATA, e->value, -1);
+	}
+	htsp_free(dirs);
+	free(sorted);
+}
+
+void ghid_config_window_show(void)
+{
+	GtkWidget *widget, *main_vbox, *config_hbox, *hbox;
+	GtkWidget *scrolled;
+	GtkWidget *button;
+	GtkTreeStore *model;
+	GtkTreeView *treeview;
+	GtkTreeIter user_pov, config_pov;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *select;
+
+	if (config_window) {
+		gtk_window_present(GTK_WINDOW(config_window));
+		return;
+	}
+
+	config_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	g_signal_connect(G_OBJECT(config_window), "delete_event", G_CALLBACK(config_destroy_cb), NULL);
+
+	gtk_window_set_title(GTK_WINDOW(config_window), _("pcb-rnd Preferences"));
+	gtk_window_set_wmclass(GTK_WINDOW(config_window), "Pcb_Conf", "PCB");
+	gtk_container_set_border_width(GTK_CONTAINER(config_window), 2);
+
+	config_hbox = gtk_hbox_new(FALSE, 4);
+	gtk_container_add(GTK_CONTAINER(config_window), config_hbox);
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+	gtk_widget_set_size_request(scrolled, 150, 0);
+	gtk_box_pack_start(GTK_BOX(config_hbox), scrolled, TRUE, TRUE, 0);
+
+	main_vbox = gtk_vbox_new(FALSE, 4);
+	gtk_box_pack_start(GTK_BOX(config_hbox), main_vbox, TRUE, TRUE, 0);
+
+	widget = gtk_notebook_new();
+	gtk_box_pack_start(GTK_BOX(main_vbox), widget, TRUE, TRUE, 0);
+	config_notebook = GTK_NOTEBOOK(widget);
+	gtk_notebook_set_show_tabs(config_notebook, FALSE);
+
+	/* build the tree */
+	model = gtk_tree_store_new(N_CONFIG_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_POINTER);
+
+	config_tree_sect(model, NULL, &user_pov,   _("User PoV"),   _("\n<b>User PoV</b>\nA subset of configuration settings regroupped,\npresented in the User's Point of View."));
+	config_tree_sect(model, NULL, &config_pov, _("Config PoV"), _("\n<b>Config PoV</b>\nAccess all configuration fields presented in\na tree that matches the configuration\nfile (lht) structure."));
+
+	config_tree_leaf(model, &user_pov, _("General"), config_general_tab_create);
+	config_tree_leaf(model, &user_pov, _("Window"), config_window_tab_create);
+	config_tree_leaf(model, &user_pov, _("Sizes"), config_sizes_tab_create);
+	config_tree_leaf(model, &user_pov, _("Increments"), config_increments_tab_create);
+	config_tree_leaf(model, &user_pov, _("Library"), config_library_tab_create);
+	config_tree_leaf(model, &user_pov, _("Layers"), config_layers_tab_create);
+	config_tree_leaf(model, &user_pov, _("Colors"), config_colors_tab_create);
+
+	config_tree_auto(model, &config_pov);
+
+	/* Create the tree view */
+	gui_config_treeview = treeview = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)));
+	g_object_unref(G_OBJECT(model));	/* Don't need the model anymore */
+
+	renderer = gtk_cell_renderer_text_new();
+	column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", CONFIG_NAME_COLUMN, NULL);
+	gtk_tree_view_append_column(treeview, column);
+	gtk_container_add(GTK_CONTAINER(scrolled), GTK_WIDGET(treeview));
+
+
+	select = gtk_tree_view_get_selection(treeview);
+	gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
+	g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(config_selection_changed_cb), NULL);
+
+
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_set_spacing(GTK_BOX(hbox), 5);
+	gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_OK);
+	gtk_widget_set_can_default(button, TRUE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(config_close_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+	gtk_widget_grab_default(button);
+
+	gtk_widget_show_all(config_window);
+}
+
+static void ghid_config_window_close(void)
+{
+	gtk_widget_destroy(config_window);
+
+	config_sizes_vbox = NULL;
+	config_increments_vbox = NULL;
+	config_groups_vbox = config_groups_table = NULL;
+	config_groups_window = NULL;
+	config_window = NULL;
+	gui_config_treeview = NULL;
+}
diff --git a/src_plugins/hid_gtk/gui-dialog-print.c b/src_plugins/hid_gtk/gui-dialog-print.c
new file mode 100644
index 0000000..71323d6
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-dialog-print.c
@@ -0,0 +1,408 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port. */
+
+#include "config.h"
+#include "conf_core.h"
+#include <stdlib.h>
+
+#include "gui.h"
+#include "pcb-printf.h"
+#include "hid_attrib.h"
+#include "hid_init.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+
+static GtkWidget *export_dialog = NULL;
+
+static void set_flag_cb(GtkToggleButton * button, void *flag)
+{
+	*(gboolean *) flag = gtk_toggle_button_get_active(button);
+}
+
+
+static void intspinner_changed_cb(GtkSpinButton * spin_button, gpointer data)
+{
+	int *ival = (int *) data;
+
+	*ival = gtk_spin_button_get_value(GTK_SPIN_BUTTON((GtkWidget *) spin_button));
+}
+
+static void coordentry_changed_cb(GtkEntry * entry, Coord * res)
+{
+	const gchar *s = gtk_entry_get_text(entry);
+	*res = GetValue(s, NULL, NULL, NULL);
+}
+
+static void dblspinner_changed_cb(GtkSpinButton * spin_button, gpointer data)
+{
+	double *dval = (double *) data;
+
+	*dval = gtk_spin_button_get_value(GTK_SPIN_BUTTON((GtkWidget *) spin_button));
+}
+
+
+static void entry_changed_cb(GtkEntry * entry, char **str)
+{
+	const gchar *s;
+
+	s = gtk_entry_get_text(entry);
+
+	if (*str)
+		free(*str);
+	*str = pcb_strdup(s);
+}
+
+static void enum_changed_cb(GtkWidget * combo_box, int *val)
+{
+	gint active;
+
+	active = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_box));
+	*val = active;
+}
+
+
+int ghid_attribute_dialog(HID_Attribute * attrs, int n_attrs, HID_Attr_Val * results, const char *title, const char *descr)
+{
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	GtkWidget *main_vbox, *vbox, *vbox1, *hbox, *entry;
+	GtkWidget *combo;
+	GtkWidget *widget;
+	GHidPort *out = &ghid_port;
+	int i, j, n;
+	int rc = 0;
+
+	dialog = gtk_dialog_new_with_buttons(_(title),
+																			 GTK_WINDOW(out->top_window),
+																			 (GtkDialogFlags) (GTK_DIALOG_MODAL
+																												 | GTK_DIALOG_DESTROY_WITH_PARENT),
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_NONE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+	gtk_window_set_wmclass(GTK_WINDOW(dialog), "PCB_attribute_editor", "PCB");
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+	main_vbox = gtk_vbox_new(FALSE, 6);
+	gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 6);
+	gtk_container_add(GTK_CONTAINER(content_area), main_vbox);
+
+	vbox = ghid_category_vbox(main_vbox, descr != NULL ? descr : "", 4, 2, TRUE, TRUE);
+
+	/*
+	 * Iterate over all the export options and build up a dialog box
+	 * that lets us control all of the options.  By doing things this
+	 * way, any changes to the exporter HID's automatically are
+	 * reflected in this dialog box.
+	 */
+	for (j = 0; j < n_attrs; j++) {
+		const Unit *unit_list;
+		if (attrs[j].help_text == ATTR_UNDOCUMENTED)
+			continue;
+		switch (attrs[j].type) {
+		case HID_Label:
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
+			gtk_widget_set_tooltip_text(widget, attrs[j].help_text);
+			break;
+
+		case HID_Integer:
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			/*
+			 * FIXME
+			 * need to pick the "digits" argument based on min/max
+			 * values
+			 */
+			ghid_spin_button(hbox, &widget, attrs[j].default_val.int_value,
+											 attrs[j].min_val, attrs[j].max_val, 1.0, 1.0, 0, 0,
+											 intspinner_changed_cb, &(attrs[j].default_val.int_value), FALSE, NULL);
+			gtk_widget_set_tooltip_text(widget, attrs[j].help_text);
+
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
+			break;
+
+		case HID_Coord:
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			entry = ghid_coord_entry_new(attrs[j].min_val, attrs[j].max_val,
+																	 attrs[j].default_val.coord_value, conf_core.editor.grid_unit, CE_SMALL);
+			gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
+			if (attrs[j].default_val.str_value != NULL)
+				gtk_entry_set_text(GTK_ENTRY(entry), attrs[j].default_val.str_value);
+			gtk_widget_set_tooltip_text(entry, attrs[j].help_text);
+			g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(coordentry_changed_cb), &(attrs[j].default_val.coord_value));
+
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
+			break;
+
+		case HID_Real:
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			/*
+			 * FIXME
+			 * need to pick the "digits" and step size argument more
+			 * intelligently
+			 */
+			ghid_spin_button(hbox, &widget, attrs[j].default_val.real_value,
+											 attrs[j].min_val, attrs[j].max_val, 0.01, 0.01, 3,
+											 0, dblspinner_changed_cb, &(attrs[j].default_val.real_value), FALSE, NULL);
+
+			gtk_widget_set_tooltip_text(widget, attrs[j].help_text);
+
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
+			break;
+
+		case HID_String:
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			entry = gtk_entry_new();
+			gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
+			if (attrs[j].default_val.str_value != NULL)
+				gtk_entry_set_text(GTK_ENTRY(entry), attrs[j].default_val.str_value);
+			gtk_widget_set_tooltip_text(entry, attrs[j].help_text);
+			g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_changed_cb), &(attrs[j].default_val.str_value));
+
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
+			break;
+
+		case HID_Boolean:
+			/* put this in a check button */
+			ghid_check_button_connected(vbox, &widget,
+																	attrs[j].default_val.int_value,
+																	TRUE, FALSE, FALSE, 0, set_flag_cb, &(attrs[j].default_val.int_value), attrs[j].name);
+			gtk_widget_set_tooltip_text(widget, attrs[j].help_text);
+			break;
+
+		case HID_Enum:
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+		do_enum:
+			combo = gtk_combo_box_new_text();
+			gtk_widget_set_tooltip_text(combo, attrs[j].help_text);
+			gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
+			g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(enum_changed_cb), &(attrs[j].default_val.int_value));
+
+
+			/*
+			 * Iterate through each value and add them to the
+			 * combo box
+			 */
+			i = 0;
+			while (attrs[j].enumerations[i]) {
+				gtk_combo_box_append_text(GTK_COMBO_BOX(combo), attrs[j].enumerations[i]);
+				i++;
+			}
+			gtk_combo_box_set_active(GTK_COMBO_BOX(combo), attrs[j].default_val.int_value);
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
+			break;
+
+		case HID_Mixed:
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			/*
+			 * FIXME
+			 * need to pick the "digits" and step size argument more
+			 * intelligently
+			 */
+			ghid_spin_button(hbox, &widget, attrs[j].default_val.real_value,
+											 attrs[j].min_val, attrs[j].max_val, 0.01, 0.01, 3,
+											 0, dblspinner_changed_cb, &(attrs[j].default_val.real_value), FALSE, NULL);
+			gtk_widget_set_tooltip_text(widget, attrs[j].help_text);
+
+			goto do_enum;
+			break;
+
+		case HID_Path:
+			vbox1 = ghid_category_vbox(vbox, attrs[j].name, 4, 2, TRUE, TRUE);
+			entry = gtk_entry_new();
+			gtk_box_pack_start(GTK_BOX(vbox1), entry, FALSE, FALSE, 0);
+			gtk_entry_set_text(GTK_ENTRY(entry), attrs[j].default_val.str_value);
+			g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_changed_cb), &(attrs[j].default_val.str_value));
+
+			gtk_widget_set_tooltip_text(entry, attrs[j].help_text);
+			break;
+
+		case HID_Unit:
+			unit_list = get_unit_list();
+			n = get_n_units();
+
+			hbox = gtk_hbox_new(FALSE, 4);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			combo = gtk_combo_box_new_text();
+			gtk_widget_set_tooltip_text(combo, attrs[j].help_text);
+			gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
+			g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(enum_changed_cb), &(attrs[j].default_val.int_value));
+
+			/*
+			 * Iterate through each value and add them to the
+			 * combo box
+			 */
+			for (i = 0; i < n; ++i)
+				gtk_combo_box_append_text(GTK_COMBO_BOX(combo), unit_list[i].in_suffix);
+			gtk_combo_box_set_active(GTK_COMBO_BOX(combo), attrs[j].default_val.int_value);
+			widget = gtk_label_new(attrs[j].name);
+			gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
+			break;
+		default:
+			printf("ghid_attribute_dialog: unknown type of HID attribute\n");
+			break;
+		}
+	}
+
+
+	gtk_widget_show_all(dialog);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+		/* copy over the results */
+		for (i = 0; i < n_attrs; i++) {
+			results[i] = attrs[i].default_val;
+			if (results[i].str_value)
+				results[i].str_value = pcb_strdup(results[i].str_value);
+		}
+		rc = 0;
+	}
+	else
+		rc = 1;
+
+	gtk_widget_destroy(dialog);
+
+	return rc;
+}
+
+
+
+static void exporter_clicked_cb(GtkButton * button, HID * exporter)
+{
+	ghid_dialog_print(exporter);
+}
+
+void ghid_dialog_print(HID * hid)
+{
+	HID_Attribute *attr;
+	int n = 0;
+	int i;
+	HID_Attr_Val *results = NULL;
+
+	/* signal the initial export select dialog that it should close */
+	if (export_dialog)
+		gtk_dialog_response(GTK_DIALOG(export_dialog), GTK_RESPONSE_CANCEL);
+
+	exporter = hid;
+
+	attr = exporter->get_export_options(&n);
+	if (n > 0) {
+		results = (HID_Attr_Val *) malloc(n * sizeof(HID_Attr_Val));
+		if (results == NULL) {
+			fprintf(stderr, "ghid_dialog_print() -- malloc failed\n");
+			exit(1);
+		}
+
+		/* non-zero means cancel was picked */
+		if (ghid_attribute_dialog(attr, n, results, _("PCB Print Layout"), exporter->description))
+			return;
+
+	}
+
+	exporter->do_export(results);
+
+	for (i = 0; i < n; i++) {
+		if (results[i].str_value)
+			free((void *) results[i].str_value);
+	}
+
+	if (results)
+		free(results);
+
+	exporter = NULL;
+}
+
+void ghid_dialog_export(void)
+{
+	GtkWidget *content_area;
+	GtkWidget *vbox, *button;
+	GHidPort *out = &ghid_port;
+	int i;
+	HID **hids;
+	gboolean no_exporter = TRUE;
+
+	export_dialog = gtk_dialog_new_with_buttons(_("PCB Export Layout"),
+																							GTK_WINDOW(out->top_window),
+																							(GtkDialogFlags) (GTK_DIALOG_MODAL
+																																|
+																																GTK_DIALOG_DESTROY_WITH_PARENT),
+																							GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+	gtk_window_set_wmclass(GTK_WINDOW(export_dialog), "PCB_Export", "PCB");
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(export_dialog));
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_container_add(GTK_CONTAINER(content_area), vbox);
+
+	/*
+	 * Iterate over all the export HID's and build up a dialog box that
+	 * lets us choose which one we want to use.
+	 * This way, any additions to the exporter HID's automatically are
+	 * reflected in this dialog box.
+	 */
+
+	hids = hid_enumerate();
+	for (i = 0; hids[i]; i++) {
+		if (hids[i]->exporter) {
+			no_exporter = FALSE;
+			button = gtk_button_new_with_label(hids[i]->name);
+			gtk_widget_set_tooltip_text(button, hids[i]->description);
+			gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+			g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(exporter_clicked_cb), hids[i]);
+		}
+	}
+
+	if (no_exporter) {
+		GtkWidget *label;
+		label = gtk_label_new("No exporter found. Check your plugins!");
+			gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	}
+
+	gtk_widget_show_all(export_dialog);
+	gtk_dialog_run(GTK_DIALOG(export_dialog));
+
+	if (export_dialog != NULL)
+		gtk_widget_destroy(export_dialog);
+	export_dialog = NULL;
+}
diff --git a/src_plugins/hid_gtk/gui-dialog.c b/src_plugins/hid_gtk/gui-dialog.c
new file mode 100644
index 0000000..e4b1a90
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-dialog.c
@@ -0,0 +1,553 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port.
+*/
+
+#include "config.h"
+#include "compat_misc.h"
+
+#include "data.h"
+#include "gui.h"
+
+/* ---------------------------------------------- */
+gchar *ghid_dialog_input(const char *prompt, const char *initial)
+{
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	GtkWidget *vbox, *label, *entry;
+	gchar *string;
+	gboolean response;
+	GHidPort *out = &ghid_port;
+
+	dialog = gtk_dialog_new_with_buttons("PCB User Input",
+																			 GTK_WINDOW(out->top_window),
+																			 GTK_DIALOG_MODAL,
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+	vbox = gtk_vbox_new(FALSE, 4);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+	label = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), prompt ? prompt : "Enter something");
+
+	entry = gtk_entry_new();
+	if (initial)
+		gtk_entry_set_text(GTK_ENTRY(entry), initial);
+
+	gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	gtk_container_add(GTK_CONTAINER(content_area), vbox);
+	gtk_widget_show_all(dialog);
+
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+	if (response != GTK_RESPONSE_OK)
+		string = g_strdup(initial ? initial : "");
+	else
+		string = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
+
+	gtk_widget_destroy(dialog);
+	return string;
+}
+
+/* ---------------------------------------------- */
+void ghid_dialog_about(void)
+{
+	GtkWidget *dialog;
+	GHidPort *out = &ghid_port;
+	dialog = gtk_message_dialog_new(GTK_WINDOW(out->top_window),
+																	(GtkDialogFlags) (GTK_DIALOG_MODAL
+																										| GTK_DIALOG_DESTROY_WITH_PARENT),
+																	GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", GetInfoString());
+
+	gtk_dialog_run(GTK_DIALOG(dialog));
+	gtk_widget_destroy(dialog);
+}
+
+/* ---------------------------------------------- */
+gint ghid_dialog_confirm_all(gchar * all_message)
+{
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	GtkWidget *label, *vbox;
+	gint response;
+	GHidPort *out = &ghid_port;
+
+	dialog = gtk_dialog_new_with_buttons("Confirm",
+																			 GTK_WINDOW(out->top_window),
+																			 (GtkDialogFlags) (GTK_DIALOG_MODAL |
+																												 GTK_DIALOG_DESTROY_WITH_PARENT),
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+																			 GTK_STOCK_OK, GTK_RESPONSE_OK, "Sequence OK", GUI_DIALOG_RESPONSE_ALL, NULL);
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	vbox = ghid_framed_vbox(content_area, NULL, 6, FALSE, 4, 6);
+
+	label = gtk_label_new(all_message);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 3);
+	gtk_widget_show_all(dialog);
+
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+	gtk_widget_destroy(dialog);
+
+	return response;
+}
+
+/* ---------------------------------------------- */
+void ghid_dialog_message(gchar * message)
+{
+	GtkWidget *dialog;
+	GHidPort *out = &ghid_port;
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(out->top_window),
+																	(GtkDialogFlags) (GTK_DIALOG_MODAL |
+																										GTK_DIALOG_DESTROY_WITH_PARENT),
+																	GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "%s", message);
+
+	gtk_dialog_run(GTK_DIALOG(dialog));
+	gtk_widget_destroy(dialog);
+}
+
+/* ---------------------------------------------- */
+gboolean ghid_dialog_confirm(const gchar * message, const gchar * cancelmsg, const gchar * okmsg)
+{
+	static gint x = -1, y = -1;
+	GtkWidget *dialog;
+	gboolean confirm = FALSE;
+	GHidPort *out = &ghid_port;
+
+	if (cancelmsg == NULL) {
+		cancelmsg = _("_Cancel");
+	}
+	if (okmsg == NULL) {
+		okmsg = _("_OK");
+	}
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(out->top_window),
+																	(GtkDialogFlags) (GTK_DIALOG_MODAL |
+																										GTK_DIALOG_DESTROY_WITH_PARENT),
+																	GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", message);
+	gtk_dialog_add_buttons(GTK_DIALOG(dialog), cancelmsg, GTK_RESPONSE_CANCEL, okmsg, GTK_RESPONSE_OK, NULL);
+
+	if (x != -1) {
+		gtk_window_move(GTK_WINDOW(dialog), x, y);
+	}
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+		confirm = TRUE;
+
+	gtk_window_get_position(GTK_WINDOW(dialog), &x, &y);
+
+	gtk_widget_destroy(dialog);
+	return confirm;
+}
+
+/* ---------------------------------------------- */
+gint ghid_dialog_close_confirm()
+{
+	GtkWidget *dialog;
+	gint rv;
+	GHidPort *out = &ghid_port;
+	gchar *tmp;
+	gchar *str;
+	const char *msg;
+
+	if (PCB->Filename == NULL) {
+		tmp = g_strdup_printf(_("Save the changes to layout before closing?"));
+	}
+	else {
+		tmp = g_strdup_printf(_("Save the changes to layout \"%s\" before closing?"), PCB->Filename);
+	}
+	str = g_strconcat("<big><b>", tmp, "</b></big>", NULL);
+	g_free(tmp);
+	msg = _("If you don't save, all your changes will be permanently lost.");
+	str = g_strconcat(str, "\n\n", msg, NULL);
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(out->top_window),
+																	(GtkDialogFlags) (GTK_DIALOG_MODAL |
+																										GTK_DIALOG_DESTROY_WITH_PARENT),
+																	GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, NULL);
+	gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), str);
+	gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+												 _("Close _without saving"), GTK_RESPONSE_NO,
+												 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL);
+
+	/* Set the alternative button order (ok, cancel, help) for other systems */
+	gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog), GTK_RESPONSE_YES, GTK_RESPONSE_NO, GTK_RESPONSE_CANCEL, -1);
+
+	switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
+	case GTK_RESPONSE_NO:
+		{
+			rv = GUI_DIALOG_CLOSE_CONFIRM_NOSAVE;
+			break;
+		}
+	case GTK_RESPONSE_YES:
+		{
+			rv = GUI_DIALOG_CLOSE_CONFIRM_SAVE;
+			break;
+		}
+	case GTK_RESPONSE_CANCEL:
+	default:
+		{
+			rv = GUI_DIALOG_CLOSE_CONFIRM_CANCEL;
+			break;
+		}
+	}
+	gtk_widget_destroy(dialog);
+	return rv;
+}
+
+/* ---------------------------------------------- */
+/* Caller must g_free() the returned filename.*/
+gchar *ghid_dialog_file_select_open(const gchar * title, gchar ** path, const gchar * shortcuts)
+{
+	GtkWidget *dialog;
+	gchar *result = NULL, *folder, *seed;
+	GHidPort *out = &ghid_port;
+	GtkFileFilter *no_filter;
+
+	dialog = gtk_file_chooser_dialog_new(title,
+																			 GTK_WINDOW(out->top_window),
+																			 GTK_FILE_CHOOSER_ACTION_OPEN,
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+	/* add a default filter for not filtering files */
+	no_filter = gtk_file_filter_new();
+	gtk_file_filter_set_name(no_filter, "all");
+	gtk_file_filter_add_pattern(no_filter, "*.*");
+	gtk_file_filter_add_pattern(no_filter, "*");
+	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), no_filter);
+
+	/* in case we have a dialog for loading a footprint file */
+	if (strcmp(title, _("Load element to buffer")) == 0) {
+		/* add a filter for footprint files */
+		GtkFileFilter *fp_filter;
+		fp_filter = gtk_file_filter_new();
+		gtk_file_filter_set_name(fp_filter, "fp");
+		gtk_file_filter_add_mime_type(fp_filter, "application/x-pcb-footprint");
+		gtk_file_filter_add_pattern(fp_filter, "*.fp");
+		gtk_file_filter_add_pattern(fp_filter, "*.FP");
+		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), fp_filter);
+	}
+
+	/* in case we have a dialog for loading a layout file */
+	if ((strcmp(title, _("Load layout file")) == 0)
+			|| (strcmp(title, _("Load layout file to buffer")) == 0)) {
+		/* add a filter for layout files */
+		GtkFileFilter *pcb_filter;
+		pcb_filter = gtk_file_filter_new();
+		gtk_file_filter_set_name(pcb_filter, "pcb");
+		gtk_file_filter_add_mime_type(pcb_filter, "application/x-pcb-layout");
+		gtk_file_filter_add_pattern(pcb_filter, "*.pcb");
+		gtk_file_filter_add_pattern(pcb_filter, "*.PCB");
+		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), pcb_filter);
+	}
+
+	/* in case we have a dialog for loading a netlist file */
+	if (strcmp(title, _("Load netlist file")) == 0) {
+		/* add a filter for netlist files */
+		GtkFileFilter *net_filter;
+		net_filter = gtk_file_filter_new();
+		gtk_file_filter_set_name(net_filter, "netlist");
+		gtk_file_filter_add_mime_type(net_filter, "application/x-pcb-netlist");
+		gtk_file_filter_add_pattern(net_filter, "*.net");
+		gtk_file_filter_add_pattern(net_filter, "*.NET");
+		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), net_filter);
+	}
+
+	if (path && *path)
+		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), *path);
+
+	if (shortcuts && *shortcuts) {
+		folder = g_strdup(shortcuts);
+		seed = folder;
+		while ((folder = strtok(seed, ":")) != NULL) {
+			gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), folder, NULL);
+			seed = NULL;
+		}
+		g_free(folder);
+	}
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+		result = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+		folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
+		if (folder && path) {
+			dup_string(path, folder);
+			g_free(folder);
+		}
+	}
+	gtk_widget_destroy(dialog);
+
+
+	return result;
+}
+
+/* ---------------------------------------------- */
+/* Caller must g_free() the returned filename. */
+gchar *ghid_dialog_file_select_save(const gchar * title, gchar ** path, const gchar * file, const gchar * shortcuts, const char **formats, int *format)
+{
+	GtkWidget *dialog, *fmt, *tmp, *fmt_combo;
+	gchar *result = NULL, *folder, *seed;
+	GHidPort *out = &ghid_port;
+
+	dialog = gtk_file_chooser_dialog_new(title,
+																			 GTK_WINDOW(out->top_window),
+																			 GTK_FILE_CHOOSER_ACTION_SAVE,
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+	/* Create and add the file format widget */
+	if (format != NULL) {
+		const char **s;
+		fmt = gtk_hbox_new(FALSE, 0);
+
+		tmp = gtk_vbox_new(FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(fmt), tmp, TRUE, TRUE, 0);
+
+		tmp = gtk_label_new("File format: ");
+		gtk_box_pack_start(GTK_BOX(fmt), tmp, FALSE, FALSE, 0);
+
+		fmt_combo = gtk_combo_box_new_text();
+		gtk_box_pack_start(GTK_BOX(fmt), fmt_combo, FALSE, FALSE, 0);
+
+		for(s = formats; *s != NULL; s++)
+			gtk_combo_box_append_text(GTK_COMBO_BOX(fmt_combo), *s);
+
+		gtk_combo_box_set_active(GTK_COMBO_BOX(fmt_combo), *format);
+
+		gtk_widget_show_all(fmt);
+		gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), fmt);
+	}
+
+	if (path && *path && **path)
+		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), *path);
+
+	if (file && *file) {
+		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), g_path_get_basename(file));
+		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), g_path_get_dirname(file));
+	}
+
+	if (shortcuts && *shortcuts) {
+		folder = g_strdup(shortcuts);
+		seed = folder;
+		while ((folder = strtok(seed, ":")) != NULL) {
+			gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), folder, NULL);
+			seed = NULL;
+		}
+		g_free(folder);
+	}
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+		result = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+		folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
+		if (folder && path) {
+			dup_string(path, folder);
+			g_free(folder);
+		}
+	}
+
+	if (format != NULL)
+		*format = gtk_combo_box_get_active(GTK_COMBO_BOX(fmt_combo));
+
+	gtk_widget_destroy(dialog);
+
+	return result;
+}
+
+
+/* ---------------------------------------------- */
+/* how many files and directories to keep for the shortcuts */
+#define NHIST 8
+typedef struct ghid_file_history_struct {
+	/*
+	 * an identifier as to which recent files pool this is.  For example
+	 * "boards", "eco", "netlists", etc.
+	 */
+	char *id;
+
+	/*
+	 * the array of files or directories
+	 */
+	char *history[NHIST];
+} ghid_file_history;
+
+static int n_recent_dirs = 0;
+static ghid_file_history *recent_dirs = NULL;
+
+/* ---------------------------------------------- */
+/* Caller must g_free() the returned filename. */
+gchar *ghid_fileselect(const char *title, const char *descr,
+											 const char *default_file, const char *default_ext, const char *history_tag, int flags)
+{
+	GtkWidget *dialog;
+	gchar *result = NULL;
+	GHidPort *out = &ghid_port;
+	gchar *path = NULL, *base = NULL;
+	int history_pool = -1;
+	int i;
+
+	if (history_tag && *history_tag) {
+		/*
+		 * I used a simple linear search here because the number of
+		 * entries in the array is likely to be quite small (5, maybe 10 at
+		 * the absolute most) and this function is used when pulling up
+		 * a file dialog box instead of something called over and over
+		 * again as part of moving elements or autorouting.  So, keep it
+		 * simple....
+		 */
+		history_pool = 0;
+		while (history_pool < n_recent_dirs && strcmp(recent_dirs[history_pool].id, history_tag) != 0) {
+			history_pool++;
+		}
+
+		/*
+		 * If we counted all the way to n_recent_dirs, that means we
+		 * didn't find our entry
+		 */
+		if (history_pool >= n_recent_dirs) {
+			n_recent_dirs++;
+
+			recent_dirs = (ghid_file_history *) realloc(recent_dirs, n_recent_dirs * sizeof(ghid_file_history));
+
+			if (recent_dirs == NULL) {
+				fprintf(stderr, "ghid_fileselect():  realloc failed\n");
+				exit(1);
+			}
+
+			recent_dirs[history_pool].id = pcb_strdup(history_tag);
+
+			/* Initialize the entries in our history list to all be NULL */
+			for (i = 0; i < NHIST; i++) {
+				recent_dirs[history_pool].history[i] = NULL;
+			}
+		}
+	}
+
+	if (default_file && *default_file) {
+		path = g_path_get_dirname(default_file);
+		base = g_path_get_basename(default_file);
+	}
+
+	dialog = gtk_file_chooser_dialog_new(title,
+																			 GTK_WINDOW(out->top_window),
+																			 (flags & HID_FILESELECT_READ) ?
+																			 GTK_FILE_CHOOSER_ACTION_OPEN :
+																			 GTK_FILE_CHOOSER_ACTION_SAVE,
+																			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+	if (path && *path) {
+		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
+		g_free(path);
+	}
+
+	if (base && *base) {
+		/* default file is only supposed to be for writing, not reading */
+		if (!(flags & HID_FILESELECT_READ)) {
+			gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), base);
+		}
+		g_free(base);
+	}
+
+	for (i = 0; i < NHIST && recent_dirs[history_pool].history[i] != NULL; i++) {
+		gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), recent_dirs[history_pool].history[i], NULL);
+	}
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+		result = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+		if (result != NULL)
+			path = g_path_get_dirname(result);
+		else
+			path = NULL;
+
+		/* update the history list */
+		if (path != NULL) {
+			char *tmps, *tmps2;
+			int k = 0;
+
+			/*
+			 * Put this at the top of the list and bump everything else
+			 * down but skip any old entry of this directory
+			 *
+			 */
+			while (k < NHIST &&
+						 recent_dirs[history_pool].history[k] != NULL && strcmp(recent_dirs[history_pool].history[k], path) == 0) {
+				k++;
+			}
+			tmps = recent_dirs[history_pool].history[k];
+			recent_dirs[history_pool].history[0] = path;
+			for (i = 1; i < NHIST; i++) {
+				/* store our current entry, but skip duplicates */
+				while (i + k < NHIST &&
+							 recent_dirs[history_pool].history[i + k] != NULL &&
+							 strcmp(recent_dirs[history_pool].history[i + k], path) == 0) {
+					k++;
+				}
+
+				if (i + k < NHIST)
+					tmps2 = recent_dirs[history_pool].history[i + k];
+				else
+					tmps2 = NULL;
+
+				/* move down the one we stored last time */
+				recent_dirs[history_pool].history[i] = tmps;
+
+				/* and remember the displace entry */
+				tmps = tmps2;
+			}
+
+			/*
+			 * the last one has fallen off the end of the history list
+			 * so we need to free() it.
+			 */
+			if (tmps) {
+				free(tmps);
+			}
+		}
+
+#ifdef DEBUG
+		printf("\n\n-----\n\n");
+		for (i = 0; i < NHIST; i++) {
+			printf("After update recent_dirs[%d].history[%d] = \"%s\"\n",
+						 history_pool, i, recent_dirs[history_pool].history[i] != NULL ? recent_dirs[history_pool].history[i] : "NULL");
+		}
+#endif
+
+	}
+	gtk_widget_destroy(dialog);
+
+
+	return result;
+}
diff --git a/src_plugins/hid_gtk/gui-drc-window.c b/src_plugins/hid_gtk/gui-drc-window.c
new file mode 100644
index 0000000..ba23d6e
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-drc-window.c
@@ -0,0 +1,806 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "error.h"
+#include "search.h"
+#include "draw.h"
+#include "layer.h"
+#include "pcb-printf.h"
+#include "undo.h"
+#include "set.h"
+#include "gui.h"
+#include "win_place.h"
+#include "gui-drc-window.h"
+#include "hid_actions.h"
+
+#define VIOLATION_PIXMAP_PIXEL_SIZE   100
+#define VIOLATION_PIXMAP_PIXEL_BORDER 5
+#define VIOLATION_PIXMAP_PCB_SIZE     PCB_MIL_TO_COORD (100)
+
+static GtkWidget *drc_window, *drc_list;
+static GtkListStore *drc_list_model = NULL;
+static int num_violations = 0;
+
+/* Remember user window resizes. */
+static gint drc_window_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, gpointer data)
+{
+	wplc_config_event(widget, &hid_gtk_wgeo.drc_x, &hid_gtk_wgeo.drc_y, &hid_gtk_wgeo.drc_width, &hid_gtk_wgeo.drc_height);
+	return FALSE;
+}
+
+static void drc_close_cb(gpointer data)
+{
+	gtk_widget_destroy(drc_window);
+	drc_window = NULL;
+}
+
+static void drc_refresh_cb(gpointer data)
+{
+	hid_actionl("DRC", NULL);
+}
+
+static void drc_destroy_cb(GtkWidget * widget, gpointer data)
+{
+	drc_window = NULL;
+}
+
+enum {
+	DRC_VIOLATION_NUM_COL = 0,
+	DRC_VIOLATION_OBJ_COL,
+	NUM_DRC_COLUMNS
+};
+
+
+static void unset_found_flags(int AndDraw)
+{
+	int flag = PCB_FLAG_FOUND;
+	int change = 0;
+
+	VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, via)) {
+			AddObjectToFlagUndoList(PCB_TYPE_VIA, via, via, via);
+			CLEAR_FLAG(flag, via);
+			DrawVia(via);
+			change = pcb_true;
+		}
+	}
+	END_LOOP;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(flag, pin)) {
+				AddObjectToFlagUndoList(PCB_TYPE_PIN, element, pin, pin);
+				CLEAR_FLAG(flag, pin);
+				DrawPin(pin);
+				change = pcb_true;
+			}
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (TEST_FLAG(flag, pad)) {
+				AddObjectToFlagUndoList(PCB_TYPE_PAD, element, pad, pad);
+				CLEAR_FLAG(flag, pad);
+				DrawPad(pad);
+				change = pcb_true;
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+	RAT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, line)) {
+			AddObjectToFlagUndoList(PCB_TYPE_RATLINE, line, line, line);
+			CLEAR_FLAG(flag, line);
+			DrawRat(line);
+			change = pcb_true;
+		}
+	}
+	END_LOOP;
+	COPPERLINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, line)) {
+			AddObjectToFlagUndoList(PCB_TYPE_LINE, layer, line, line);
+			CLEAR_FLAG(flag, line);
+			DrawLine(layer, line);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	COPPERARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, arc)) {
+			AddObjectToFlagUndoList(PCB_TYPE_ARC, layer, arc, arc);
+			CLEAR_FLAG(flag, arc);
+			DrawArc(layer, arc);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	COPPERPOLYGON_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(flag, polygon)) {
+			AddObjectToFlagUndoList(PCB_TYPE_POLYGON, layer, polygon, polygon);
+			CLEAR_FLAG(flag, polygon);
+			DrawPolygon(layer, polygon);
+			change = pcb_true;
+		}
+	}
+	ENDALL_LOOP;
+	if (change) {
+		SetChangedFlag(pcb_true);
+		if (AndDraw) {
+			IncrementUndoSerialNumber();
+			Draw();
+		}
+	}
+}
+
+static void selection_changed_cb(GtkTreeSelection * selection, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GhidDrcViolation *violation;
+	int i;
+
+	if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
+		unset_found_flags(pcb_true);
+		return;
+	}
+
+	/* Check the selected node has children, if so; return. */
+	if (gtk_tree_model_iter_has_child(model, &iter))
+		return;
+
+	gtk_tree_model_get(model, &iter, DRC_VIOLATION_OBJ_COL, &violation, -1);
+
+	unset_found_flags(pcb_false);
+
+	if (violation == NULL)
+		return;
+
+	/* Flag the objects listed against this DRC violation */
+	for (i = 0; i < violation->object_count; i++) {
+		int object_id = violation->object_id_list[i];
+		int object_type = violation->object_type_list[i];
+		int found_type;
+		void *ptr1, *ptr2, *ptr3;
+
+		found_type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, object_id, object_type);
+		if (found_type == PCB_TYPE_NONE) {
+			Message(PCB_MSG_DEFAULT, _("Object ID %i identified during DRC was not found. Stale DRC window?\n"), object_id);
+			continue;
+		}
+		AddObjectToFlagUndoList(object_type, ptr1, ptr2, ptr3);
+		SET_FLAG(PCB_FLAG_FOUND, (AnyObjectType *) ptr2);
+		switch (violation->object_type_list[i]) {
+		case PCB_TYPE_LINE:
+		case PCB_TYPE_ARC:
+		case PCB_TYPE_POLYGON:
+			ChangeGroupVisibility(GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1), pcb_true, pcb_true);
+		}
+		DrawObject(object_type, ptr1, ptr2);
+	}
+	SetChangedFlag(pcb_true);
+	IncrementUndoSerialNumber();
+	Draw();
+}
+
+static void row_activated_cb(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer user_data)
+{
+	GtkTreeModel *model = gtk_tree_view_get_model(view);
+	GtkTreeIter iter;
+	GhidDrcViolation *violation;
+
+	gtk_tree_model_get_iter(model, &iter, path);
+
+	gtk_tree_model_get(model, &iter, DRC_VIOLATION_OBJ_COL, &violation, -1);
+
+	if (violation == NULL)
+		return;
+
+	CenterDisplay(violation->x_coord, violation->y_coord);
+}
+
+
+enum {
+	PROP_TITLE = 1,
+	PROP_EXPLANATION,
+	PROP_X_COORD,
+	PROP_Y_COORD,
+	PROP_ANGLE,
+	PROP_HAVE_MEASURED,
+	PROP_MEASURED_VALUE,
+	PROP_REQUIRED_VALUE,
+	PROP_OBJECT_LIST,
+	PROP_PIXMAP
+};
+
+
+static GObjectClass *ghid_drc_violation_parent_class = NULL;
+
+
+/*! \brief GObject finalise handler
+ *
+ *  \par Function Description
+ *  Just before the GhidDrcViolation GObject is finalized, free our
+ *  allocated data, and then chain up to the parent's finalize handler.
+ *
+ *  \param [in] widget  The GObject being finalized.
+ */
+static void ghid_drc_violation_finalize(GObject * object)
+{
+	GhidDrcViolation *violation = GHID_DRC_VIOLATION(object);
+
+	g_free(violation->title);
+	g_free(violation->explanation);
+	g_free(violation->object_id_list);
+	g_free(violation->object_type_list);
+	if (violation->pixmap != NULL)
+		g_object_unref(violation->pixmap);
+
+	G_OBJECT_CLASS(ghid_drc_violation_parent_class)->finalize(object);
+}
+
+typedef struct object_list {
+	int count;
+	long int *id_list;
+	int *type_list;
+} object_list;
+
+/*! \brief GObject property setter function
+ *
+ *  \par Function Description
+ *  Setter function for GhidDrcViolation's GObject properties,
+ *  "settings-name" and "toplevel"
+ *
+ *  \param [in]  object       The GObject whose properties we are setting
+ *  \param [in]  property_id  The numeric id. under which the property was
+ *                            registered with g_object_class_install_property()
+ *  \param [in]  value        The GValue the property is being set from
+ *  \param [in]  pspec        A GParamSpec describing the property being set
+ */
+static void ghid_drc_violation_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
+{
+	GhidDrcViolation *violation = GHID_DRC_VIOLATION(object);
+	object_list *obj_list;
+
+	switch (property_id) {
+	case PROP_TITLE:
+		g_free(violation->title);
+		violation->title = g_value_dup_string(value);
+		break;
+	case PROP_EXPLANATION:
+		g_free(violation->explanation);
+		violation->explanation = g_value_dup_string(value);
+		break;
+	case PROP_X_COORD:
+		violation->x_coord = g_value_get_int(value);
+		break;
+	case PROP_Y_COORD:
+		violation->y_coord = g_value_get_int(value);
+		break;
+	case PROP_ANGLE:
+		violation->angle = g_value_get_double(value);
+		break;
+	case PROP_HAVE_MEASURED:
+		violation->have_measured = g_value_get_boolean(value);
+		break;
+	case PROP_MEASURED_VALUE:
+		violation->measured_value = g_value_get_int(value);
+		break;
+	case PROP_REQUIRED_VALUE:
+		violation->required_value = g_value_get_int(value);
+		break;
+	case PROP_OBJECT_LIST:
+		/* Copy the passed data to make new lists */
+		g_free(violation->object_id_list);
+		g_free(violation->object_type_list);
+		obj_list = (object_list *) g_value_get_pointer(value);
+		violation->object_count = obj_list->count;
+		violation->object_id_list = g_new(long int, obj_list->count);
+		violation->object_type_list = g_new(int, obj_list->count);
+		memcpy(violation->object_id_list, obj_list->id_list, sizeof(long int) * obj_list->count);
+		memcpy(violation->object_type_list, obj_list->type_list, sizeof(int) * obj_list->count);
+		break;
+	case PROP_PIXMAP:
+		if (violation->pixmap)
+			g_object_unref(violation->pixmap);	/* Frees our old reference */
+		violation->pixmap = (GdkDrawable *) g_value_dup_object(value);	/* Takes a new reference */
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+		return;
+	}
+}
+
+
+/*! \brief GObject property getter function
+ *
+ *  \par Function Description
+ *  Getter function for GhidDrcViolation's GObject properties,
+ *  "settings-name" and "toplevel".
+ *
+ *  \param [in]  object       The GObject whose properties we are getting
+ *  \param [in]  property_id  The numeric id. under which the property was
+ *                            registered with g_object_class_install_property()
+ *  \param [out] value        The GValue in which to return the value of the property
+ *  \param [in]  pspec        A GParamSpec describing the property being got
+ */
+static void ghid_drc_violation_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
+{
+	switch (property_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+	}
+
+}
+
+
+/*! \brief GType class initialiser for GhidDrcViolation
+ *
+ *  \par Function Description
+ *  GType class initialiser for GhidDrcViolation. We override our parent
+ *  virtual class methods as needed and register our GObject properties.
+ *
+ *  \param [in]  klass       The GhidDrcViolationClass we are initialising
+ */
+static void ghid_drc_violation_class_init(GhidViolationRendererClass * klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+	gobject_class->finalize = ghid_drc_violation_finalize;
+	gobject_class->set_property = ghid_drc_violation_set_property;
+	gobject_class->get_property = ghid_drc_violation_get_property;
+
+	ghid_drc_violation_parent_class = (GObjectClass *) g_type_class_peek_parent(klass);
+
+	g_object_class_install_property(gobject_class, PROP_TITLE, g_param_spec_string("title", "", "", "", G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_EXPLANATION,
+																	g_param_spec_string("explanation", "", "", "", G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_X_COORD,
+																	g_param_spec_int("x-coord", "", "", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_Y_COORD,
+																	g_param_spec_int("y-coord", "", "", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_ANGLE,
+																	g_param_spec_double("angle", "", "", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_HAVE_MEASURED,
+																	g_param_spec_boolean("have-measured", "", "", 0, G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_MEASURED_VALUE,
+																	g_param_spec_int("measured-value", "", "", G_MININT, G_MAXINT, 0., G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_REQUIRED_VALUE,
+																	g_param_spec_int("required-value", "", "", G_MININT, G_MAXINT, 0., G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_OBJECT_LIST,
+																	g_param_spec_pointer("object-list", "", "", G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_PIXMAP,
+																	g_param_spec_object("pixmap", "", "", GDK_TYPE_DRAWABLE, G_PARAM_WRITABLE));
+}
+
+
+
+/*! \brief Function to retrieve GhidViolationRenderer's GType identifier.
+ *
+ *  \par Function Description
+ *  Function to retrieve GhidViolationRenderer's GType identifier.
+ *  Upon first call, this registers the GhidViolationRenderer in the GType system.
+ *  Subsequently it returns the saved value from its first execution.
+ *
+ *  \return the GType identifier associated with GhidViolationRenderer.
+ */
+GType ghid_drc_violation_get_type()
+{
+	static GType ghid_drc_violation_type = 0;
+
+	if (!ghid_drc_violation_type) {
+		static const GTypeInfo ghid_drc_violation_info = {
+			sizeof(GhidDrcViolationClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_drc_violation_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GhidDrcViolation),
+			0,												/* n_preallocs */
+			NULL,											/* instance_init */
+		};
+
+		ghid_drc_violation_type =
+			g_type_register_static(G_TYPE_OBJECT, "GhidDrcViolation", &ghid_drc_violation_info, (GTypeFlags) 0);
+	}
+
+	return ghid_drc_violation_type;
+}
+
+
+GhidDrcViolation *ghid_drc_violation_new(DrcViolationType * violation, GdkDrawable * pixmap)
+{
+	object_list obj_list;
+
+	obj_list.count = violation->object_count;
+	obj_list.id_list = violation->object_id_list;
+	obj_list.type_list = violation->object_type_list;
+
+	return (GhidDrcViolation *) g_object_new(GHID_TYPE_DRC_VIOLATION,
+																					 "title", violation->title,
+																					 "explanation", violation->explanation,
+																					 "x-coord", violation->x,
+																					 "y-coord", violation->y,
+																					 "angle", violation->angle,
+																					 "have-measured", violation->have_measured,
+																					 "measured-value", violation->measured_value,
+																					 "required-value", violation->required_value,
+																					 "object-list", &obj_list, "pixmap", pixmap, NULL);
+}
+
+enum {
+	PROP_VIOLATION = 1,
+};
+
+
+static GObjectClass *ghid_violation_renderer_parent_class = NULL;
+
+
+/*! \brief GObject finalise handler
+ *
+ *  \par Function Description
+ *  Just before the GhidViolationRenderer GObject is finalized, free our
+ *  allocated data, and then chain up to the parent's finalize handler.
+ *
+ *  \param [in] widget  The GObject being finalized.
+ */
+static void ghid_violation_renderer_finalize(GObject * object)
+{
+	GhidViolationRenderer *renderer = GHID_VIOLATION_RENDERER(object);
+
+	if (renderer->violation != NULL)
+		g_object_unref(renderer->violation);
+
+	G_OBJECT_CLASS(ghid_violation_renderer_parent_class)->finalize(object);
+}
+
+
+/*! \brief GObject property setter function
+ *
+ *  \par Function Description
+ *  Setter function for GhidViolationRenderer's GObject properties,
+ *  "settings-name" and "toplevel".
+ *
+ *  \param [in]  object       The GObject whose properties we are setting
+ *  \param [in]  property_id  The numeric id. under which the property was
+ *                            registered with g_object_class_install_property()
+ *  \param [in]  value        The GValue the property is being set from
+ *  \param [in]  pspec        A GParamSpec describing the property being set
+ */
+static void ghid_violation_renderer_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
+{
+	GhidViolationRenderer *renderer = GHID_VIOLATION_RENDERER(object);
+	char *markup;
+
+	switch (property_id) {
+	case PROP_VIOLATION:
+		if (renderer->violation != NULL)
+			g_object_unref(renderer->violation);
+		renderer->violation = (GhidDrcViolation *) g_value_dup_object(value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+		return;
+	}
+
+	if (renderer->violation == NULL)
+		return;
+
+	if (renderer->violation->have_measured) {
+		markup = pcb_strdup_printf("%m+<b>%s (%$mS)</b>\n"
+																 "<span size='1024'> </span>\n"
+																 "<small>"
+																 "<i>%s</i>\n"
+																 "<span size='5120'> </span>\n"
+																 "Required: %$mS"
+																 "</small>",
+																 conf_core.editor.grid_unit->allow,
+																 renderer->violation->title,
+																 renderer->violation->measured_value,
+																 renderer->violation->explanation, renderer->violation->required_value);
+	}
+	else {
+		markup = pcb_strdup_printf("%m+<b>%s</b>\n"
+																 "<span size='1024'> </span>\n"
+																 "<small>"
+																 "<i>%s</i>\n"
+																 "<span size='5120'> </span>\n"
+																 "Required: %$mS"
+																 "</small>",
+																 conf_core.editor.grid_unit->allow,
+																 renderer->violation->title,
+																 renderer->violation->explanation, renderer->violation->required_value);
+	}
+
+	g_object_set(object, "markup", markup, NULL);
+	free(markup);
+}
+
+
+/*! \brief GObject property getter function
+ *
+ *  \par Function Description
+ *  Getter function for GhidViolationRenderer's GObject properties.
+ *
+ *  \param [in]  object       The GObject whose properties we are getting
+ *  \param [in]  property_id  The numeric id. under which the property was
+ *                            registered with g_object_class_install_property()
+ *  \param [out] value        The GValue in which to return the value of the property
+ *  \param [in]  pspec        A GParamSpec describing the property being got
+ */
+static void ghid_violation_renderer_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
+{
+	switch (property_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+	}
+
+}
+
+static void
+ghid_violation_renderer_get_size(GtkCellRenderer * cell,
+																 GtkWidget * widget,
+																 GdkRectangle * cell_area, gint * x_offset, gint * y_offset, gint * width, gint * height)
+{
+	GTK_CELL_RENDERER_CLASS(ghid_violation_renderer_parent_class)->get_size(cell,
+																																					widget, cell_area, x_offset, y_offset, width, height);
+	if (width != NULL)
+		*width += VIOLATION_PIXMAP_PIXEL_SIZE;
+	if (height != NULL)
+		*height = MAX(*height, VIOLATION_PIXMAP_PIXEL_SIZE);
+}
+
+
+static void
+ghid_violation_renderer_render(GtkCellRenderer * cell,
+															 GdkDrawable * window,
+															 GtkWidget * widget,
+															 GdkRectangle * background_area,
+															 GdkRectangle * cell_area, GdkRectangle * expose_area, GtkCellRendererState flags)
+{
+	GdkDrawable *mydrawable;
+	GtkStyle *style = gtk_widget_get_style(widget);
+	GhidViolationRenderer *renderer = GHID_VIOLATION_RENDERER(cell);
+	GhidDrcViolation *violation = renderer->violation;
+	int pixmap_size = VIOLATION_PIXMAP_PIXEL_SIZE - 2 * VIOLATION_PIXMAP_PIXEL_BORDER;
+
+	cell_area->width -= VIOLATION_PIXMAP_PIXEL_SIZE;
+	GTK_CELL_RENDERER_CLASS(ghid_violation_renderer_parent_class)->render(cell,
+																																				window,
+																																				widget, background_area, cell_area, expose_area, flags);
+
+	if (violation == NULL)
+		return;
+
+	if (violation->pixmap == NULL) {
+		GdkPixmap *pixmap = ghid_render_pixmap(violation->x_coord,
+																					 violation->y_coord,
+																					 VIOLATION_PIXMAP_PCB_SIZE / pixmap_size,
+																					 pixmap_size, pixmap_size,
+																					 gdk_drawable_get_depth(window));
+		g_object_set(violation, "pixmap", pixmap, NULL);
+		g_object_unref(pixmap);
+	}
+
+	if (violation->pixmap == NULL)
+		return;
+
+	mydrawable = GDK_DRAWABLE(violation->pixmap);
+
+	gdk_draw_drawable(window, style->fg_gc[gtk_widget_get_state(widget)],
+										mydrawable, 0, 0,
+										cell_area->x + cell_area->width + VIOLATION_PIXMAP_PIXEL_BORDER,
+										cell_area->y + VIOLATION_PIXMAP_PIXEL_BORDER, -1, -1);
+}
+
+
+/*! \brief GType class initialiser for GhidViolationRenderer
+ *
+ *  \par Function Description
+ *  GType class initialiser for GhidViolationRenderer. We override our parent
+ *  virtual class methods as needed and register our GObject properties.
+ *
+ *  \param [in]  klass       The GhidViolationRendererClass we are initialising
+ */
+static void ghid_violation_renderer_class_init(GhidViolationRendererClass * klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+	GtkCellRendererClass *cellrenderer_class = GTK_CELL_RENDERER_CLASS(klass);
+
+	gobject_class->finalize = ghid_violation_renderer_finalize;
+	gobject_class->set_property = ghid_violation_renderer_set_property;
+	gobject_class->get_property = ghid_violation_renderer_get_property;
+
+	cellrenderer_class->get_size = ghid_violation_renderer_get_size;
+	cellrenderer_class->render = ghid_violation_renderer_render;
+
+	ghid_violation_renderer_parent_class = (GObjectClass *) g_type_class_peek_parent(klass);
+
+	g_object_class_install_property(gobject_class, PROP_VIOLATION,
+																	g_param_spec_object("violation", "", "", GHID_TYPE_DRC_VIOLATION, G_PARAM_WRITABLE));
+}
+
+
+/*! \brief Function to retrieve GhidViolationRenderer's GType identifier.
+ *
+ *  \par Function Description
+ *  Function to retrieve GhidViolationRenderer's GType identifier.
+ *  Upon first call, this registers the GhidViolationRenderer in the GType system.
+ *  Subsequently it returns the saved value from its first execution.
+ *
+ *  \return the GType identifier associated with GhidViolationRenderer.
+ */
+GType ghid_violation_renderer_get_type()
+{
+	static GType ghid_violation_renderer_type = 0;
+
+	if (!ghid_violation_renderer_type) {
+		static const GTypeInfo ghid_violation_renderer_info = {
+			sizeof(GhidViolationRendererClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_violation_renderer_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GhidViolationRenderer),
+			0,												/* n_preallocs */
+			NULL,											/* instance_init */
+		};
+
+		ghid_violation_renderer_type =
+			g_type_register_static(GTK_TYPE_CELL_RENDERER_TEXT, "GhidViolationRenderer",
+														 &ghid_violation_renderer_info, (GTypeFlags) 0);
+	}
+
+	return ghid_violation_renderer_type;
+}
+
+
+/*! \brief Convenience function to create a new violation renderer
+ *
+ *  \par Function Description
+ *  Convenience function which creates a GhidViolationRenderer.
+ *
+ *  \return  The GhidViolationRenderer created.
+ */
+GtkCellRenderer *ghid_violation_renderer_new(void)
+{
+	GhidViolationRenderer *renderer;
+
+	renderer = (GhidViolationRenderer *) g_object_new(GHID_TYPE_VIOLATION_RENDERER, "ypad", 6, NULL);
+
+	return GTK_CELL_RENDERER(renderer);
+}
+
+
+void ghid_drc_window_show(gboolean raise)
+{
+	GtkWidget *vbox, *hbox, *button, *scrolled_window;
+	GtkCellRenderer *violation_renderer;
+
+	if (drc_window) {
+		if (raise)
+			gtk_window_present(GTK_WINDOW(drc_window));
+		return;
+	}
+
+	drc_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	g_signal_connect(G_OBJECT(drc_window), "destroy", G_CALLBACK(drc_destroy_cb), NULL);
+	g_signal_connect(G_OBJECT(drc_window), "configure_event", G_CALLBACK(drc_window_configure_event_cb), NULL);
+	gtk_window_set_title(GTK_WINDOW(drc_window), _("pcb-rnd DRC"));
+	gtk_window_set_wmclass(GTK_WINDOW(drc_window), "PCB_DRC", "PCB");
+	gtk_window_set_default_size(GTK_WINDOW(drc_window), hid_gtk_wgeo.drc_width, hid_gtk_wgeo.drc_height);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(drc_window), vbox);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_box_set_spacing(GTK_BOX(vbox), 6);
+
+	drc_list_model = gtk_list_store_new(NUM_DRC_COLUMNS, G_TYPE_INT,	/* DRC_VIOLATION_NUM_COL */
+																			G_TYPE_OBJECT);	/* DRC_VIOLATION_OBJ_COL */
+
+	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+	gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE /* EXPAND */ , TRUE /* FILL */ , 0 /* PADDING */ );
+
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+	drc_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(drc_list_model));
+	gtk_container_add(GTK_CONTAINER(scrolled_window), drc_list);
+
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(drc_list), TRUE);
+	g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(drc_list)), "changed", G_CALLBACK(selection_changed_cb), NULL);
+	g_signal_connect(drc_list, "row-activated", G_CALLBACK(row_activated_cb), NULL);
+
+	violation_renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(drc_list), -1,	/* APPEND */
+																							_("No."),	/* TITLE */
+																							violation_renderer, "text", DRC_VIOLATION_NUM_COL, NULL);
+
+	violation_renderer = ghid_violation_renderer_new();
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(drc_list), -1,	/* APPEND */
+																							_("Violation details"),	/* TITLE */
+																							violation_renderer, "violation", DRC_VIOLATION_OBJ_COL, NULL);
+
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	gtk_box_set_spacing(GTK_BOX(hbox), 6);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(drc_refresh_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(drc_close_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	wplc_place(WPLC_DRC, drc_window);
+
+	gtk_widget_realize(drc_window);
+	gtk_widget_show_all(drc_window);
+}
+
+void ghid_drc_window_append_violation(DrcViolationType * violation)
+{
+	GhidDrcViolation *violation_obj;
+	GtkTreeIter iter;
+
+	/* Ensure the required structures are setup */
+	ghid_drc_window_show(FALSE);
+
+	num_violations++;
+
+	violation_obj = ghid_drc_violation_new(violation, /* pixmap */ NULL);
+
+	gtk_list_store_append(drc_list_model, &iter);
+	gtk_list_store_set(drc_list_model, &iter, DRC_VIOLATION_NUM_COL, num_violations, DRC_VIOLATION_OBJ_COL, violation_obj, -1);
+
+	g_object_unref(violation_obj);	/* The list store takes its own reference */
+}
+
+void ghid_drc_window_reset_message(void)
+{
+	if (drc_list_model != NULL)
+		gtk_list_store_clear(drc_list_model);
+	num_violations = 0;
+}
+
+int ghid_drc_window_throw_dialog(void)
+{
+	ghid_drc_window_show(TRUE);
+	return 1;
+}
diff --git a/src_plugins/hid_gtk/gui-drc-window.h b/src_plugins/hid_gtk/gui-drc-window.h
new file mode 100644
index 0000000..5b581b8
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-drc-window.h
@@ -0,0 +1,93 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#ifndef PCB_HID_GTK_GUI_DRC_WINDOW_H
+#define PCB_HID_GTK_GUI_DRC_WINDOW_H
+
+
+#define GHID_TYPE_DRC_VIOLATION           (ghid_drc_violation_get_type())
+#define GHID_DRC_VIOLATION(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_TYPE_DRC_VIOLATION, GhidDrcViolation))
+#define GHID_DRC_VIOLATION_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass),  GHID_TYPE_DRC_VIOLATION, GhidDrcViolationClass))
+#define GHID_IS_DRC_VIOLATION(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_TYPE_DRC_VIOLATION))
+#define GHID_DRC_VIOLATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),  GHID_TYPE_DRC_VIOLATION, GhidDrcViolationClass))
+
+typedef struct _GhidDrcViolationClass GhidDrcViolationClass;
+typedef struct _GhidDrcViolation GhidDrcViolation;
+
+
+struct _GhidDrcViolationClass {
+	GObjectClass parent_class;
+};
+
+struct _GhidDrcViolation {
+	GObject parent_instance;
+
+	char *title;
+	char *explanation;
+	Coord x_coord;
+	Coord y_coord;
+	Angle angle;
+	pcb_bool have_measured;
+	Coord measured_value;
+	Coord required_value;
+	int object_count;
+	long int *object_id_list;
+	int *object_type_list;
+	GdkDrawable *pixmap;
+};
+
+
+GType ghid_drc_violation_get_type(void);
+
+GhidDrcViolation *ghid_drc_violation_new(DrcViolationType * violation, GdkDrawable * pixmap);
+
+
+#define GHID_TYPE_VIOLATION_RENDERER           (ghid_violation_renderer_get_type())
+#define GHID_VIOLATION_RENDERER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_TYPE_VIOLATION_RENDERER, GhidViolationRenderer))
+#define GHID_VIOLATION_RENDERER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass),  GHID_TYPE_VIOLATION_RENDERER, GhidViolationRendererClass))
+#define GHID_IS_VIOLATION_RENDERER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_TYPE_VIOLATION_RENDERER))
+#define GHID_VIOLATION_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),  GHID_TYPE_VIOLATION_RENDERER, GhidViolationRendererClass))
+
+typedef struct _GhidViolationRendererClass GhidViolationRendererClass;
+typedef struct _GhidViolationRenderer GhidViolationRenderer;
+
+
+struct _GhidViolationRendererClass {
+	GtkCellRendererTextClass parent_class;
+};
+
+struct _GhidViolationRenderer {
+	GtkCellRendererText parent_instance;
+
+	GhidDrcViolation *violation;
+};
+
+
+GType ghid_violation_renderer_get_type(void);
+
+GtkCellRenderer *ghid_violation_renderer_new(void);
+
+#endif /* PCB_HID_GTK_GUI_DRC_WINDOW_H */
diff --git a/src_plugins/hid_gtk/gui-icons-misc.data b/src_plugins/hid_gtk/gui-icons-misc.data
new file mode 100644
index 0000000..e1f879a
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-icons-misc.data
@@ -0,0 +1,111 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ *  RCS: $Id$
+ */
+
+
+/* XPM */
+static const char *icon_bits[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 6 1",
+"  c black",
+". c #53E336",
+"X c #E34736",
+"o c #BED7E3",
+"O c #E7E3E7",
+"+ c None",
+/* pixels */
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"+                              +",
+"+ o   oo   o   oo   o   oo   o +",
+"+ o + oo + o + oo + o + oo + o +",
+"+ o   oo   o   oo   o   oo   o +",
+"+ oooooooooooooooooooooooooooo +",
+"+   oooooooooooooooooooooooooo +",
+"+   oooooooooooooooooooooooooo +",
+"+   oooooooooooooooooooooooooo +",
+"+   oooooooooooooooooooooooooo +",
+"+ oooooooooooooooooooooooooooo +",
+"+ o   oo   o   oo   o   oo   o +",
+"+ o + oo + o + oo + o + oo + o +",
+"+ o   oo . o   oo   o   oo X o +",
+"+        .                 X   +",
+"+++++++++.+++++++++++++++++X++++",
+"+++++++++.+++++++++++++++++X++++",
+"+++++++++.+++++++++++++++++X++++",
+"+++++++++.+++++++++++++++++X++++",
+"++++++++.++++++++++++++++++X++++",
+"+++++++.++++ +++ ++++++++++X++++",
+"+++++++.++++ ++  +++++++++XX++++",
+"++++++ . +++ + o ++++   +XX+++++",
+"++++++ .      oo      XXXX++++++",
+"++++++   +++ + o ++++   ++++++++",
+"++++++++++++ ++  +++++++++++++++",
+"++++++++++++ +++ +++++++++++++++",
+"++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++"
+};
+
+#define rotateIcon_width 16
+#define rotateIcon_height 16
+static unsigned char rotateIcon_bits[] = {
+   0xf0, 0x03, 0xf8, 0x87, 0x0c, 0xcc, 0x06, 0xf8, 0x03, 0xb0, 0x01, 0x98,
+   0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x19, 0x80, 0x0d, 0xc0,
+   0x1f, 0x60, 0x3b, 0x30, 0xe1, 0x3f, 0xc0, 0x0f};
+
+#define rotateMask_width 16
+#define rotateMask_height 16
+static unsigned char rotateMask_bits[] = {
+   0xf0, 0x03, 0xf8, 0x87, 0x0c, 0xcc, 0x06, 0xf8, 0x03, 0xf0, 0x01, 0xf8,
+   0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x80, 0x0f, 0xc0,
+   0x1f, 0x60, 0x3b, 0x30, 0xe1, 0x3f, 0xc0, 0x0f};
+
+#define handIcon_width 16
+#define handIcon_height 16
+static unsigned char handIcon_bits[] = {
+   0x80, 0x00, 0x48, 0x09, 0x54, 0x15, 0x54, 0x15, 0x54, 0x15, 0x64, 0xcb,
+   0x68, 0xab, 0x2b, 0x5b, 0x05, 0x58, 0x05, 0x20, 0x01, 0x20, 0x02, 0x20,
+   0x02, 0x30, 0x04, 0x10, 0x08, 0x08, 0xf0, 0x0f};
+#define handMask_width 16
+#define handMask_height 16
+static unsigned char handMask_bits[] = {
+   0x80, 0x00, 0xc8, 0x09, 0xdc, 0x1d, 0xdc, 0x1d, 0xdc, 0x1d, 0xfc, 0xcf,
+   0x38, 0xee, 0x1f, 0x78, 0x0f, 0x78, 0x07, 0x30, 0x07, 0x30, 0x0e, 0x38,
+   0x1e, 0x3c, 0x3c, 0x1e, 0xf8, 0x0f, 0xf0, 0x0f};
+#define lockIcon_width 16
+#define lockIcon_height 16
+static unsigned char lockIcon_bits[] = {
+   0x00, 0x00, 0xe0, 0x07, 0x30, 0x0c, 0x10, 0x08, 0x18, 0x18, 0x08, 0x10,
+   0x08, 0x00, 0xfc, 0x3f, 0x04, 0x20, 0xfc, 0x3f, 0x04, 0x20, 0xfc, 0x3f,
+   0x04, 0x20, 0xfc, 0x3f, 0x04, 0x20, 0xfc, 0x3f};
+#define lockMask_width 16
+#define lockMask_height 16
+static unsigned char lockMask_bits[] = {
+   0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0x38, 0x1c, 0x1c, 0x3c, 0x1c, 0x38,
+   0x1c, 0x30, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f,
+   0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f};
diff --git a/src_plugins/hid_gtk/gui-icons-mode-buttons.data b/src_plugins/hid_gtk/gui-icons-mode-buttons.data
new file mode 100644
index 0000000..7cf22b7
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-icons-mode-buttons.data
@@ -0,0 +1,510 @@
+/* $Id$ */
+
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* XPM */
+static const char *arc[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #4E85b7",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooo.ooooooooooooooo",
+"ooooo.ooooooooooooooo",
+"ooooo.ooooooooooooooo",
+"ooooo.ooooooooooooooo",
+"oooooo.oooooooooooooo",
+"oooooo.oooooooooooooo",
+"ooooooo.ooooooooooooo",
+"ooooooo..oooooooooooo",
+"oooooooo..ooooooooooo",
+"oooooooooo..ooooooooo",
+"oooooooooooo....ooooo",
+"ooooooooooooooooooooo",
+"ooo oooo    oooo   oo",
+"oo o ooo ooo oo ooo o",
+"oo o ooo ooo oo ooo o",
+"o ooo oo    ooo ooooo",
+"o     oo  ooooo ooooo",
+"o ooo oo o oooo ooooo",
+"o ooo oo oo ooo ooo o",
+"o ooo oo ooo ooo   oo",
+"ooooooooooooooooooooo"
+};
+
+
+/* XPM */
+static const char *buf[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #D0C7AD",
+"X c gray100",
+"o c None",
+/* pixels */
+"oooooooo  oo  ooooooo",
+"oooooo.. o  o ..ooooo",
+"oooooooo oooo ooooooo",
+"oooooo.. oooo ..ooooo",
+"oooooooo oooo ooooooo",
+"oooooo.. oooo ..ooooo",
+"oooooooo oooo ooooooo",
+"oooooo.. oooo ..ooooo",
+"oooooooo oooo ooooooo",
+"oooooo.. oooo ..ooooo",
+"oooooooo      ooooooo",
+"ooooooooooooooooooooo",
+"oo     oo ooo o     o",
+"ooo ooo o ooo o ooooo",
+"ooo ooo o ooo o ooooo",
+"ooo ooo o ooo o    oo",
+"ooo    oo ooo o ooooo",
+"ooo ooo o ooo o ooooo",
+"ooo ooo o ooo o ooooo",
+"oo     ooo   oo ooooo",
+"ooooooooooooooooooooo"
+};
+
+
+/* XPM */
+static const char *del[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 3 1",
+"  c black",
+". c gray100",
+"X c None",
+/* pixels */
+"XXX XXXXXXXXXXXX XXXX",
+"XXXX XXX    XXX XXXXX",
+"XXXXX X      X XXXXXX",
+"XXXXXX XX  XX XXXXXXX",
+"XXXXX  XX  XX  XXXXXX",
+"XXXXX          XXXXXX",
+"XXXXXX        XXXXXXX",
+"XXXXXXX  XX  XXXXXXXX",
+"XXXXXXX  XX XXXXXXXXX",
+"XXXXXX X XX  XXXXXXXX",
+"XXXX  XX    X  XXXXXX",
+"XX  XXXXX  XXXX  XXXX",
+"XXXXXXXXXXXXXXXXXXXXX",
+"XX     XX     X XXXXX",
+"XXX XXX X XXXXX XXXXX",
+"XXX XXX X XXXXX XXXXX",
+"XXX XXX X XXXXX XXXXX",
+"XXX XXX X    XX XXXXX",
+"XXX XXX X XXXXX XXXXX",
+"XXX XXX X XXXXX XXXXX",
+"XX     XX     X     X"
+};
+
+
+/* XPM */
+static const char *ins[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #7A8584",
+"X c gray100",
+"o c None",
+/* pixels */
+"oooooo...oooooooooooo",
+"ooooo.ooo.ooooooooooo",
+"ooooo.o.o.ooooooooooo",
+"oooooo....ooooooooooo",
+"ooooooooooooooooooooo",
+"oooo  ooooo  oooooooo",
+"ooooooooooooooooooooo",
+"oo...ooooooooooo...oo",
+"o.oo..ooooooooo.ooo.o",
+"o.o o...........o o.o",
+"o.ooo.ooooooooo.ooo.o",
+"oo...ooooooooooo...oo",
+"ooooooooooooooooooooo",
+"ooo   o ooo oo    ooo",
+"oooo oo ooo o ooooooo",
+"oooo oo  oo o ooooooo",
+"oooo oo o o oo   oooo",
+"oooo oo oo  ooooo ooo",
+"oooo oo ooo ooooo ooo",
+"oooo oo ooo ooooo ooo",
+"ooo   o ooo o    oooo"
+};
+
+
+/* XPM */
+static const char *line[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 5 1",
+"  c black",
+". c #7A8584",
+"X c #4E85b7",
+"o c gray100",
+"O c None",
+/* pixels */
+"OOOOOOOOOOOOOOOOOOOOO",
+"OO...OOOOOOOOOOOOOOOO",
+"O.OOO.OOOOOOOOOOOOOOO",
+"O.OXXXOOOOOOOOOOOOOOO",
+"O.OOO.XXXOOOOOOOOOOOO",
+"OO...OOOOXXXOOOO...OO",
+"OOOOOOOOOOOOXXX.OOO.O",
+"OOOOOOOOOOOOOOOXXXO.O",
+"OOOOOOOOOOOOOOO.OOO.O",
+"OOOOOOOOOOOOOOOO...OO",
+"OOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOO",
+" OOOO   O OOOO O    O",
+" OOOOO OO  OOO O OOOO",
+" OOOOO OO O OO O OOOO",
+" OOOOO OO O OO O OOOO",
+" OOOOO OO OO O O   OO",
+" OOOOO OO OO O O OOOO",
+" OOOOO OO OOO  O OOOO",
+"    O   O OOOO O    O",
+"OOOOOOOOOOOOOOOOOOOOO"
+};
+
+
+/* XPM */
+static const char *lock[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 3 1",
+"  c black",
+". c gray100",
+"X c None",
+/* pixels */
+"XXXXXXXX     XXXXXXXX",
+"XXXXXXX  XXX  XXXXXXX",
+"XXXXXXX XXXXX XXXXXXX",
+"XXXXXX  XXXXX  XXXXXX",
+"XXXXXX XXXXXXX XXXXXX",
+"XXXXXX XXXXXXX XXXXXX",
+"XXXX             XXXX",
+"XXXX XXXXXXXXXXX XXXX",
+"XXXX             XXXX",
+"XXXX XXXXXXXXXXX XXXX",
+"XXXX             XXXX",
+"XXXX XXXXXXXXXXX XXXX",
+"XXXX             XXXX",
+"XXXXXXXXXXXXXXXXXXXXX",
+"XX XXXX  XXX  X XX XX",
+"XX XXX XX X XXX XX XX",
+"XX XXX XX X XXX X XXX",
+"XX XXX XX X XXX  XXXX",
+"XX XXX XX X XXX X XXX",
+"XX XXX XX X XXX XX XX",
+"XX   XX  XXX  X XX XX"
+};
+
+#if 0
+/* XPM */
+static const char *pan[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 3 1",
+"  c black",
+". c #E7E3E7",
+"X c None",
+/* pixels */
+"XXXXXXXXXX XXXXXXXXXX",
+"XXXXXX XX X XX XXXXXX",
+"XXXXX X X X X X XX XX",
+"XXXXX X X X X X X X X",
+"XXXXX X X X X X X X X",
+"X  XX X  XX X X  X  X",
+"X X X XX XXX XX XX XX",
+"X XX  XXXXXXXXX X XXX",
+"XX XX XXXXXXXXXXX XXX",
+"XXX XXXXXXXXXXXX XXXX",
+"XXXX XXXXXXXXXXX XXXX",
+"XXXXX XXXXXXXXXX XXXX",
+"XXXXXX          XXXXX",
+"XXXXXXXXXXXXXXXXXXXXX",
+"XXXX   XXX  XX XXX XX",
+"XXXX XX X XX X  XX XX",
+"XXXX XX X XX X   X XX",
+"XXXX   XX    X X X XX",
+"XXXX XXXX XX X X   XX",
+"XXXX XXXX XX X XX  XX",
+"XXXX XXXX XX X XXX XX"
+};
+
+#endif
+/* XPM */
+static const char *poly[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #6EA5D7",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooooooooo.ooooooooo",
+"oooooooooo..ooooooooo",
+"ooooooooo...ooooooooo",
+"oooooooo....ooooooooo",
+"ooooooo.....ooooooooo",
+"oooooo.......oooooooo",
+"ooooo.........ooooooo",
+"oooo...........oooooo",
+"oooo............ooooo",
+"oooo.............oooo",
+"oooo..............ooo",
+"ooooooooooooooooooooo",
+"o    ooo   oo oo ooo ",
+"o ooo o ooo o oo ooo ",
+"o ooo o ooo o ooo o o",
+"o    oo ooo o oooo oo",
+"o ooooo ooo o oooo oo",
+"o ooooo ooo o oooo oo",
+"o ooooo ooo o oooo oo",
+"o oooooo   oo    o oo",
+"ooooooooooooooooooooo"
+};
+
+/* XPM */
+static const char * polyhole[] = {
+"21 21 3 1",
+" 	c None",
+".	c #6EA5D7",
+"+	c #000000",
+"        ..           ",
+"       ...           ",
+"      .....          ",
+"     .......         ",
+"    .........        ",
+"  ....+++++...       ",
+"  ....+   +....      ",
+"  ...+    +.....     ",
+"  ...++++++......    ",
+"  ................   ",
+"  .................  ",
+"                     ",
+"  +  +  ++  +   +++  ",
+"  +  + +  + +   +    ",
+"  +  + +  + +   +    ",
+"  ++++ +  + +   +++  ",
+"  +  + +  + +   +    ",
+"  +  + +  + +   +    ",
+"  +  + +  + +   +    ",
+"  +  +  ++  +++ +++  ",
+"                     "
+};
+
+/* XPM */
+static const char *rect[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #6EA5D7",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooooooooooooooooooo",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"oo..................o",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"o   oo    oo  oo     ",
+"o oo o oooo oo ooo oo",
+"o oo o oooo oooooo oo",
+"o   oo oooo oooooo oo",
+"o o oo   oo oooooo oo",
+"o oo o oooo oooooo oo",
+"o oo o oooo oo ooo oo",
+"o oo o    oo  oooo oo",
+"ooooooooooooooooooooo"
+};
+
+
+/* XPM */
+static const char *rot[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #4E85b7",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooooooooo.ooooooooo",
+"oooooooooo..ooooooooo",
+"ooooooooo....oooooooo",
+"oooooooooo..o.ooooooo",
+"ooooooooooo.oo.oooooo",
+"oooooooooooooo.oooooo",
+"oooooooooooooo.oooooo",
+"oooooooooooooo.oooooo",
+"oooooooooooooo.oooooo",
+"ooooooooooooo.ooooooo",
+"oooooooooooo.oooooooo",
+"oooooooooo..ooooooooo",
+"ooooooooooooooooooooo",
+"ooo    ooo   oo     o",
+"ooo ooo o ooo ooo ooo",
+"ooo ooo o ooo ooo ooo",
+"ooo    oo ooo ooo ooo",
+"ooo   ooo ooo ooo ooo",
+"ooo o  oo ooo ooo ooo",
+"ooo oo  o ooo ooo ooo",
+"ooo ooo oo   oooo ooo"
+};
+
+
+/* XPM */
+static const char *sel[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #6EA5D7",
+"X c gray100",
+"o c None",
+/* pixels */
+"oo .. ooooooooooooooo",
+"oo .... ooooooooooooo",
+"ooo ...... oooooooooo",
+"ooo ........ oooooooo",
+"ooo ....... ooooooooo",
+"oooo ..... oooooooooo",
+"oooo ...... ooooooooo",
+"ooooo .. ... oooooooo",
+"ooooo . o ... ooooooo",
+"oooooooooo ... oooooo",
+"ooooooooooo .. oooooo",
+"oooooooooooo  ooooooo",
+"ooooooooooooooooooooo",
+"ooo   oo     o oooooo",
+"oo ooo o ooooo oooooo",
+"ooo oooo ooooo oooooo",
+"oooo ooo    oo oooooo",
+"ooooo oo ooooo oooooo",
+"oooooo o ooooo oooooo",
+"oo ooo o ooooo oooooo",
+"ooo   oo     o     oo"
+};
+
+
+/* XPM */
+static const char *text[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #4E85b7",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"oo.ooo.ooo.ooo.ooo.oo",
+"o.o.o.o.o.o.o.o.o.o.o",
+"oo.ooo.ooo.ooo.ooo.oo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"     o   o ooo o     ",
+"oo ooo ooo ooo ooo oo",
+"oo ooo oooo o oooo oo",
+"oo ooo ooooo ooooo oo",
+"oo ooo  ooo o oooo oo",
+"oo ooo ooo ooo ooo oo",
+"oo ooo ooo ooo ooo oo",
+"oo ooo   o ooo ooo oo",
+"ooooooooooooooooooooo"
+};
+
+
+/* XPM */
+static const char *thrm[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #69E1B0",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooooooooooooooooooo",
+"oooo ooooooooo oooooo",
+"ooooo ooooooo ooooooo",
+"oooooo o...o oooooooo",
+"ooooooo ooo ooooooooo",
+"oooooo.ooooo.oooooooo",
+"oooooo.ooooo.oooooooo",
+"oooooo.ooooo.oooooooo",
+"ooooooo ooo ooooooooo",
+"oooooo o...o oooooooo",
+"ooooo ooooooo ooooooo",
+"oooo ooooooooo oooooo",
+"ooooooooooooooooooooo",
+"     o oo o   oo ooo ",
+"oo ooo oo o oo o  o  ",
+"oo ooo oo o oo o o o ",
+"oo ooo oo o   oo o o ",
+"oo ooo    o o oo ooo ",
+"oo ooo oo o oo o ooo ",
+"oo ooo oo o oo o ooo ",
+"oo ooo oo o oo o ooo "
+};
+
+
+/* XPM */
+static const char *via[] = {
+/* columns rows colors chars-per-pixel */
+"21 21 4 1",
+"  c black",
+". c #7A8584",
+"X c gray100",
+"o c None",
+/* pixels */
+"ooooooooooooooooooooo",
+"ooooooooo...ooooooooo",
+"oooooooo.....oooooooo",
+"ooooooo..ooo..ooooooo",
+"oooooo..ooooo..oooooo",
+"oooooo..ooooo..oooooo",
+"oooooo..ooooo..oooooo",
+"ooooooo..ooo..ooooooo",
+"oooooooo.....oooooooo",
+"ooooooooo...ooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooooooooooooooooooooo",
+"ooo ooo o   ooo ooooo",
+"ooo ooo oo ooo o oooo",
+"ooo ooo oo oo ooo ooo",
+"oooo o ooo oo ooo ooo",
+"oooo o ooo oo     ooo",
+"oooo o ooo oo ooo ooo",
+"ooooo ooo   o ooo ooo",
+"ooooooooooooooooooooo"
+};
diff --git a/src_plugins/hid_gtk/gui-keyref-window.c b/src_plugins/hid_gtk/gui-keyref-window.c
new file mode 100644
index 0000000..6fd1d6a
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-keyref-window.c
@@ -0,0 +1,352 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port */
+
+#include "config.h"
+
+#include "gui.h"
+#include "win_place.h"
+
+static GtkWidget *keyref_window;
+
+static const gchar *key_ref_text[] = {
+	"<h>",
+	N_("Keyboard\n"),
+	N_("Keyboard shortcuts and actions available in PCB.\n"),
+	N_("In below key actions, <s> is <shift>, <c> is <ctrl>\n"),
+	N_("and <a> is <alt> or <mod>\n"),
+	"\n",
+
+	"<b>\ta\t",
+	N_("Set layer and size from line or arc\n"),
+	"\n",
+
+	"<b><s><a>a ",
+	N_("Unselect all objects\n"),
+	"\n",
+
+	"<b>\tb\t",
+	N_("Flip element to opposite side of the board\n"),
+	"<b><c>\tb\t",
+	N_("Flip selected objects to opposite side of the board\n"),
+	"\n",
+
+	"<b><c>\tc\t",
+	N_("Copy selected to buffer and unselect\n"),
+	"\n",
+
+	"<b>\td\t",
+	N_("Display pin/pad names (numbers with View->Enable pinout shows number)\n"),
+	"<b><s>\td\t",
+	N_("Open pinout window for element under cursor\n"),
+	"\n",
+
+	"<b>\te\t",
+	N_("Delete all rats\n"),
+	"<b><s>\te\t",
+	N_("Delete selected rats\n"),
+	"\n",
+
+	"<b>\tf\t",
+	N_("Highlight connections to object\n"),
+	"<b><s>\tf\t",
+	N_("Reset highlighted connections\n"),
+	"<b><c>\tf\t",
+	N_("Cumulative highlight connections to object\n"),
+	"\n",
+
+	"<b>\tg\t",
+	N_("Increment grid by configured grid increment\n"),
+
+	"<b><s>\tg\t",
+	N_("Decrement grid by configured grid increment\n"),
+	"\n",
+
+	"<b>\th\t",
+	N_("Toggle visibility of element name under cursor\n"),
+
+	"<b><s>\th\t",
+	N_("Toggle visibility of selected element names\n"),
+
+	"<b><c>\th\t",
+	N_("Toggle the hole flag of object under cursor\n"),
+	"\n",
+
+	"<b>\tj\t",
+	N_("Toggle line/arc should clear polygons flag of object under cursor\n"),
+
+	"<b><s>\tj\t",
+	N_("Toggle line/arc should clear polygons flag of selected\n"),
+	"\n",
+
+	"<b>\tk\t",
+	N_("Increase clearance of object by configured clearance\n"),
+
+	"<b><s>\tk\t",
+	N_("Decrease clearance of object by configured clearance\n"),
+
+	"<b><c>\tk\t",
+	N_("Increase clearance of selected objects by configured clearance\n"),
+
+	"<b><s><c>k ",
+	N_("Decrease clearance of selected objects by configured clearance\n"),
+	"\n",
+
+	"<b>\tl\t",
+	N_("Increment current route style line size by configured line increment\n"),
+
+	"<b><s>\tl\t",
+	N_("Decrement current route style line size by configured line increment\n"),
+	"\n",
+
+	"<b>\tm\t",
+	N_("Move object to current layer\n"),
+
+	"<b><s>\tm\t",
+	N_("Move selected objects to current layer\n"),
+
+	"<b><c>\tm\t",
+	N_("Mark at cursor location for showing relative offsets\n"),
+	"\n",
+
+	"<b><s>\tn\t",
+	N_("Select the shortest unselected rat on the board\n"),
+	"\n",
+
+	"<b>\to\t",
+	N_("Optimize and draw all rats\n"),
+
+	"<b><s>\to\t",
+	N_("Optimize and draw selected rats\n"),
+	"\n",
+
+	"<b><c>\to\t",
+	N_("Change octagon flag of object\n"),
+	"\n",
+
+	"<b>\tp\t",
+	N_("Backup polygon drawing to previous point\n"),
+
+	"<b><s>\tp\t",
+	N_("Close polygon\n"),
+	"\n",
+
+	"<b>\tq\t",
+	N_("Toggle the square flag of an object\n"),
+	"\n",
+
+	"<b><s>\tr\t",
+	N_("Redo last undone operation\n"),
+	"\n",
+
+	"<b>\ts\t",
+	N_("Increment size of an object by configured size increment\n"),
+	"<b><s>\ts\t",
+	N_("Decrement size of an object by configured size increment\n"),
+	"<b><a>\ts\t",
+	N_("Increment drill size of a pin or via\n"),
+	"<b><s><a>s ",
+	N_("Decrement drill size of a pin or via\n"),
+	"\n",
+
+	"<b>\tt\t",
+	N_("Adjust text scale so new text increases by the configured size increment\n"),
+	"<b><s>\tt\t",
+	N_("Adjust text scale so new text decreases by the configured size increment\n"),
+	"\n",
+
+	"<b>\tu\t",
+	N_("Undo last operation\n"),
+	"\n",
+
+	"<b>\tv\t",
+	N_("Zoom to board extents\n"),
+	"<b><c>\tv\t",
+	N_("Increment current route style via size\n"),
+	"<b><s><c>v ",
+	N_("Decrement current route style via size\n"),
+	"<b><a>\tv\t",
+	N_("Increment current route style via hole size\n"),
+	"<b><s><a>v ",
+	N_("Decrement current route style via hole size\n"),
+	"\n",
+
+	"<b><c>\tx\t",
+	N_("Copy selection to buffer and enter pastebuffer mode\n"),
+	"<b><s><c>x ",
+	N_("Cut selection to buffer and enter pastebuffer mode\n"),
+	"\n",
+
+	"<b>\tz\t",
+	N_("Zoom in\n"),
+	"<b><z>\tz\t",
+	N_("Zoom out\n"),
+	"\n",
+
+	"<b>\t|\t",
+	N_("Toggle thin draw mode\n"),
+	"\n",
+
+	"<b>\t/\t",
+	N_("Cycle multiline mode (Using <s> overrides)\n"),
+	"\n",
+
+	"<b>\t.\t",
+	N_("Toggle all direction lines mode\n"),
+	"\n",
+
+	"<b>\tEsc\t",
+	N_("If drawing an object, return to a neutral state.\n"),
+	"\n",
+
+	"<b>\tTab\t",
+	N_("Switch view to other side\n"),
+	"\n",
+
+	"<b>  Space\t",
+	N_("Switch to select mode\n"),
+	"\n",
+
+	"<b>\t:\t",
+	N_("Enter user command or pop up command window\n"),
+	"\n",
+
+	"<b>\tDEL\t",
+	N_("Delete object\n"),
+	"\n",
+
+	"<b>\t1-9\t",
+	N_("Select drawing layers\n"),
+	"\n",
+
+	"<b><c>\t1-5\t",
+	N_("Select current buffer\n"),
+	"\n",
+	"\n",
+	"<h>",
+	N_("Mouse\n"),
+	N_("Modifier key use can be combined with mouse button presses\n" "to modify mouse button actions.\n"),
+	"\n",
+	N_("<b>Left button\n"),
+	N_("\tPerform or initiate action determined by current mode.\n"),
+	"\n",
+	"\t",
+	N_("<b><shift>"),
+	N_(" - change rotation direction for rotation tool actions.\n"),
+	"\n",
+	N_("\tAfter a draw operation has been left mouse button initiated,\n" "\tmodifier key effects:\n"),
+	"\t",
+	N_("<b><shift>"),
+	N_(" - change line 45 degree direction and arc angle direction,\n"),
+	"\n",
+	N_("<b>Middle button\n"),
+	N_("\tIf a line, arc, rectangle, or polygon draw operation has been\n"
+		 "\tinitiated, a click restarts the draw operation at the cursor position.\n"),
+	"\n",
+	N_("\tIf such a draw has not been initiated, a click selects objects and\n" "\ta press and drag moves objects.\n"),
+	"\n",
+	N_("<b>Right button\n"),
+	N_("\tPress and drag to pan.\n" "\tWhile drawing or moving, a click without a drag toggles auto pan mode.\n"),
+	"\n\t",
+	N_("<b><shift>"),
+	N_(" - Popup a menu.\n"),
+	"\n",
+	N_("<b>Scroll wheel\n"),
+	N_("\tZoom in/out.\n"),
+	"\n\t",
+	N_("<b><shift>"),
+	N_(" - pan vertically.\n"),
+	"\t",
+	N_("<b><ctrl>"),
+	N_(" - pan horizontally.\n"),
+	"\n",
+	N_("<b>Usage:\n"),
+	N_("\tMouse actions can typically be combined.  For example: while moving\n"
+		 "\tan object (with left or middle press and drag), the right button may\n"
+		 "\tbe simultaneously clicked to toggle auto pan or pressed and dragged\n"
+		 "\tto manually pan.  Mouse moving or drawing may also be combined with\n" "\tkey actions.\n"),
+};
+
+
+
+
+	/* Remember user window resizes.
+	 */
+static gint keyref_window_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, gpointer data)
+{
+	wplc_config_event(widget, &hid_gtk_wgeo.keyref_x, &hid_gtk_wgeo.keyref_y, &hid_gtk_wgeo.keyref_width, &hid_gtk_wgeo.keyref_height);
+	return FALSE;
+}
+
+static void keyref_close_cb(gpointer data)
+{
+	gtk_widget_destroy(keyref_window);
+	keyref_window = NULL;
+}
+
+static void keyref_destroy_cb(GtkWidget * widget, gpointer data)
+{
+	keyref_window = NULL;
+}
+
+void ghid_keyref_window_show(gboolean raise)
+{
+	GtkWidget *vbox, *hbox, *button, *text;
+	gint i;
+
+	if (keyref_window) {
+		if (raise)
+			gtk_window_present(GTK_WINDOW(keyref_window));
+		return;
+	}
+	keyref_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	g_signal_connect(G_OBJECT(keyref_window), "destroy", G_CALLBACK(keyref_destroy_cb), NULL);
+	g_signal_connect(G_OBJECT(keyref_window), "configure_event", G_CALLBACK(keyref_window_configure_event_cb), NULL);
+	gtk_window_set_title(GTK_WINDOW(keyref_window), _("pcb-rnd Key Reference"));
+	gtk_window_set_wmclass(GTK_WINDOW(keyref_window), "PCB_Keyref", "PCB");
+	gtk_window_set_default_size(GTK_WINDOW(keyref_window), hid_gtk_wgeo.keyref_width, hid_gtk_wgeo.keyref_height);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_container_add(GTK_CONTAINER(keyref_window), vbox);
+
+	text = ghid_scrolled_text_view(vbox, NULL, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	for (i = 0; i < sizeof(key_ref_text) / sizeof(gchar *); ++i)
+		ghid_text_view_append(text, _(key_ref_text[i]));
+
+	/* The keyref window close button.
+	 */
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(keyref_close_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	gtk_widget_show_all(keyref_window);
+
+}
diff --git a/src_plugins/hid_gtk/gui-library-window.c b/src_plugins/hid_gtk/gui-library-window.c
new file mode 100644
index 0000000..9a07fa5
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-library-window.c
@@ -0,0 +1,868 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file originally from the PCB Gtk port by Bill Wilson. It has
+ * since been combined with modified code from the gEDA project:
+ *
+ * gschem/src/ghid_library_window.c, checked out by Peter Clifton
+ * from gEDA/gaf commit 72581a91da08c9d69593c24756144fc18940992e
+ * on 3rd Jan, 2008.
+ *
+ * gEDA - GPL Electronic Design Automation
+ * gschem - gEDA Schematic Capture
+ * Copyright (C) 1998-2007 Ales Hvezda
+ * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "gui.h"
+#include "win_place.h"
+#include "global.h"
+#include "buffer.h"
+#include "data.h"
+#include "set.h"
+#include "plug_footprint.h"
+
+#include <gdk/gdkkeysyms.h>
+
+static GtkWidget *library_window;
+
+#include "gui-pinout-preview.h"
+#include "gui-library-window.h"
+
+/*! \def LIBRARY_FILTER_INTERVAL
+ *  \brief The time interval between request and actual filtering
+ *
+ *  This constant is the time-lag between user modifications in the
+ *  filter entry and the actual evaluation of the filter which
+ *  ultimately update the display. It helps reduce the frequency of
+ *  evaluation of the filter as user types.
+ *
+ *  Unit is milliseconds.
+ */
+#define LIBRARY_FILTER_INTERVAL 200
+
+
+static gint library_window_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, gpointer data)
+{
+	wplc_config_event(widget, &hid_gtk_wgeo.library_x, &hid_gtk_wgeo.library_y, &hid_gtk_wgeo.library_width, &hid_gtk_wgeo.library_height);
+	return FALSE;
+}
+
+
+enum {
+	MENU_NAME_COLUMN,							/* Text to display in the tree     */
+	MENU_ENTRY_COLUMN,						/* Pointer to the library_t */
+	N_MENU_COLUMNS
+};
+
+
+/*! \brief Process the response returned by the library dialog.
+ *  \par Function Description
+ *  This function handles the response <B>arg1</B> of the library
+ *  dialog <B>dialog</B>.
+ *
+ *  Parameter <B>user_data</B> is a pointer on the relevant toplevel
+ *  structure.
+ *
+ *  \param [in] dialog    The library dialog.
+ *  \param [in] arg1      The response ID.
+ *  \param [in] user_data
+ */
+static void library_window_callback_response(GtkDialog * dialog, gint arg1, gpointer user_data)
+{
+	switch (arg1) {
+	case GTK_RESPONSE_CLOSE:
+	case GTK_RESPONSE_DELETE_EVENT:
+		gtk_widget_destroy(GTK_WIDGET(library_window));
+		library_window = NULL;
+		break;
+
+	default:
+		/* Do nothing, in case there's another handler function which
+		   can handle the response ID received. */
+		break;
+	}
+}
+
+
+/*! \brief Creates a library dialog.
+ *  \par Function Description
+ *  This function create the library dialog if it is not already created.
+ *  It does not show the dialog, use ghid_library_window_show for that.
+ *
+ */
+void ghid_library_window_create(GHidPort * out)
+{
+	GtkWidget *current_tab, *entry_filter;
+	GtkNotebook *notebook;
+
+	if (library_window)
+		return;
+
+	library_window = (GtkWidget *) g_object_new(GHID_TYPE_LIBRARY_WINDOW, NULL);
+
+	g_signal_connect(library_window, "response", G_CALLBACK(library_window_callback_response), NULL);
+	g_signal_connect(G_OBJECT(library_window), "configure_event", G_CALLBACK(library_window_configure_event_cb), NULL);
+	gtk_window_set_default_size(GTK_WINDOW(library_window), hid_gtk_wgeo.library_width, hid_gtk_wgeo.library_height);
+
+	gtk_window_set_title(GTK_WINDOW(library_window), _("pcb-rnd Library"));
+	gtk_window_set_wmclass(GTK_WINDOW(library_window), "PCB_Library", "PCB");
+
+	wplc_place(WPLC_LIBRARY, library_window);
+
+	gtk_widget_realize(library_window);
+
+	gtk_editable_select_region(GTK_EDITABLE(GHID_LIBRARY_WINDOW(library_window)->entry_filter), 0, -1);
+
+	/* Set the focus to the filter entry only if it is in the current
+	   displayed tab */
+	notebook = GTK_NOTEBOOK(GHID_LIBRARY_WINDOW(library_window)->viewtabs);
+	current_tab = gtk_notebook_get_nth_page(notebook, gtk_notebook_get_current_page(notebook));
+	entry_filter = GTK_WIDGET(GHID_LIBRARY_WINDOW(library_window)->entry_filter);
+	if (gtk_widget_is_ancestor(entry_filter, current_tab)) {
+		gtk_widget_grab_focus(entry_filter);
+	}
+}
+
+/*! \brief Show the library dialog.
+ *  \par Function Description
+ *  This function show the library dialog, creating it if it is not
+ *  already created, and presents it to the user (brings it to the
+ *  front with focus).
+ */
+void ghid_library_window_show(GHidPort * out, gboolean raise)
+{
+	ghid_library_window_create(out);
+	gtk_widget_show_all(library_window);
+	if (raise)
+		gtk_window_present(GTK_WINDOW(library_window));
+}
+
+static GObjectClass *library_window_parent_class = NULL;
+
+
+/*! \brief Determines visibility of items of the library treeview.
+ *  \par Function Description
+ *  This is the function used to filter entries of the footprint
+ *  selection tree.
+ *
+ *  \param [in] model The current selection in the treeview.
+ *  \param [in] iter  An iterator on a footprint or folder in the tree.
+ *  \param [in] data  The library dialog.
+ *  \returns TRUE if item should be visible, FALSE otherwise.
+ */
+static gboolean lib_model_filter_visible_func(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
+{
+	GhidLibraryWindow *library_window = (GhidLibraryWindow *) data;
+	const gchar *compname;
+	gchar *compname_upper, *text_upper, *pattern;
+	const gchar *text_;
+	char text[1024];
+	gboolean ret;
+	char *p, *tags;
+	int len, is_para = 0;
+
+	g_assert(GHID_IS_LIBRARY_WINDOW(data));
+
+	text_ = gtk_entry_get_text(library_window->entry_filter);
+	if (g_ascii_strcasecmp(text_, "") == 0) {
+		return TRUE;
+	}
+
+/* TODO: do these only once.... */
+	p = strchr(text_, '(');
+	if (p != NULL) {
+		len = p - text_;
+		is_para = 1;
+	}
+	else
+		len = sizeof(text) - 1;
+
+	strncpy(text, text_, len);
+	text[len] = '\0';
+
+	/* apply tags on non-parametrics */
+	if (!is_para) {
+		tags = strchr(text, ' ');
+		if (tags != NULL) {
+			*tags = '\0';
+			tags++;
+			while (isspace(*tags))
+				tags++;
+			if (*tags == '\0')
+				tags = NULL;
+		}
+	}
+	else
+		tags = NULL;
+
+	/* If this is a source, only display it if it has children that
+	 * match */
+	if (gtk_tree_model_iter_has_child(model, iter)) {
+		GtkTreeIter iter2;
+
+		gtk_tree_model_iter_children(model, &iter2, iter);
+		ret = FALSE;
+		do {
+			if (lib_model_filter_visible_func(model, &iter2, data)) {
+				ret = TRUE;
+				break;
+			}
+		}
+		while (gtk_tree_model_iter_next(model, &iter2));
+	}
+	else {
+		gtk_tree_model_get(model, iter, MENU_NAME_COLUMN, &compname, -1);
+		/* Do a case insensitive comparison, converting the strings
+		   to uppercase */
+		compname_upper = g_ascii_strup(compname, -1);
+		text_upper = g_ascii_strup(text, -1);
+		if (is_para)
+			pattern = g_strconcat("*", text_upper, "(*", NULL);
+		else
+			pattern = g_strconcat("*", text_upper, "*", NULL);
+
+		ret = g_pattern_match_simple(pattern, compname_upper);
+
+		if ((tags != NULL) && ret) {
+			library_t *entry = NULL;
+			gtk_tree_model_get(model, iter, MENU_ENTRY_COLUMN, &entry, -1);
+			if ((entry != NULL) && (entry->type == LIB_FOOTPRINT) && (entry->data.fp.tags != NULL)) {
+				char *next, *tag;
+				int found;
+				void **t;
+				const void *need;
+
+				for (tag = tags; tag != NULL; tag = next) {
+					next = strpbrk(tag, " \t\r\n");
+					if (next != NULL) {
+						*next = '\0';
+						next++;
+						while (isspace(*next))
+							next++;
+					}
+					need = fp_tag(tag, 0);
+					fprintf(stderr, "TAG: '%s' %p\n", tag, (void *)need);
+					if (need == NULL) {
+						ret = FALSE;
+						break;
+					}
+					found = 0;
+					for (t = entry->data.fp.tags; *t != NULL; t++) {
+						if (*t == need) {
+							found = 1;
+							break;
+						}
+					}
+					if (!found) {
+						ret = FALSE;
+						break;
+					}
+				}
+			}
+			else
+				ret = FALSE;
+		}
+
+		g_free(compname_upper);
+		g_free(text_upper);
+		g_free(pattern);
+	}
+
+	return ret;
+}
+
+
+/*! \brief Handles activation (e.g. double-clicking) of a component row
+ *  \par Function Description
+ *  Component row activated handler:
+ *  As a convenince to the user, expand / contract any node with children.
+ *
+ *  \param [in] tree_view The component treeview.
+ *  \param [in] path      The GtkTreePath to the activated row.
+ *  \param [in] column    The GtkTreeViewColumn in which the activation occurre
+ *  \param [in] user_data The component selection dialog.
+ */
+static void tree_row_activated(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	model = gtk_tree_view_get_model(tree_view);
+	gtk_tree_model_get_iter(model, &iter, path);
+
+	if (!gtk_tree_model_iter_has_child(model, &iter))
+		return;
+
+	if (gtk_tree_view_row_expanded(tree_view, path))
+		gtk_tree_view_collapse_row(tree_view, path);
+	else
+		gtk_tree_view_expand_row(tree_view, path, FALSE);
+}
+
+/*! \brief Handles CTRL-C keypress in the TreeView
+ *  \par Function Description
+ *  Keypress activation handler:
+ *  If CTRL-C is pressed, copy footprint name into the clipboard.
+ *
+ *  \param [in] tree_view The component treeview.
+ *  \param [in] event     The GdkEventKey with keypress info.
+ *  \param [in] user_data Not used.
+ *  \return TRUE if CTRL-C event was handled, FALSE otherwise.
+ */
+static gboolean tree_row_key_pressed(GtkTreeView * tree_view, GdkEventKey * event, gpointer user_data)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GtkClipboard *clipboard;
+	const gchar *compname;
+	guint default_mod_mask = gtk_accelerator_get_default_mod_mask();
+
+	/* Handle both lower- and uppercase `c' */
+	if (((event->state & default_mod_mask) != GDK_CONTROL_MASK)
+			|| ((event->keyval != GDK_c) && (event->keyval != GDK_C)))
+		return FALSE;
+
+	selection = gtk_tree_view_get_selection(tree_view);
+	g_return_val_if_fail(selection != NULL, TRUE);
+
+	if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+		return TRUE;
+
+	gtk_tree_model_get(model, &iter, MENU_NAME_COLUMN, &compname, -1);
+
+	clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+	g_return_val_if_fail(clipboard != NULL, TRUE);
+
+	gtk_clipboard_set_text(clipboard, compname, -1);
+
+	return TRUE;
+}
+
+static void library_window_preview_refresh(GhidLibraryWindow * library_window, const char *name, library_t * entry)
+{
+	GString *pt;
+	char *fullp;
+
+	/* -1 flags this is an element file part and the file path is in
+	   |  entry->AllocateMemory.
+	 */
+	if (name == NULL) {
+		if ((entry == NULL) || (entry->type != LIB_FOOTPRINT))
+			return;
+		fullp = entry->data.fp.loc_info;
+	}
+		SetMode(PCB_MODE_ARROW);
+	if (LoadElementToBuffer(PASTEBUFFER, name == NULL ? fullp : name))
+		SetMode(PCB_MODE_PASTE_BUFFER);
+
+	/* update the preview with new symbol data */
+	if ((PASTEBUFFER->Data != NULL) && (elementlist_length(&PASTEBUFFER->Data->Element) != 0))
+		g_object_set(library_window->preview, "element-data", elementlist_first(&PASTEBUFFER->Data->Element), NULL);
+	else {
+		g_object_set(library_window->preview, "element-data", NULL, NULL);
+	}
+
+	/* update the text */
+	pt = g_string_new("Tags:");
+	if ((entry != NULL) && (entry->type == LIB_FOOTPRINT) && (entry->data.fp.tags != NULL)) {
+		void **t;
+
+		for (t = entry->data.fp.tags; *t != NULL; t++) {
+			const char *name = fp_tagname(*t);
+			if (name != NULL) {
+				g_string_append(pt, "\n  ");
+				g_string_append(pt, name);
+			}
+		}
+		g_string_append(pt, "\nLocation:\n ");
+		g_string_append(pt, entry->data.fp.loc_info);
+		g_string_append(pt, "\n");
+	}
+	gtk_label_set_text(GTK_LABEL(library_window->preview_text), g_string_free(pt, FALSE));
+}
+
+/*! \brief Handles changes in the treeview selection.
+ *  \par Function Description
+ *  This is the callback function that is called every time the user
+ *  select a row the library treeview of the dialog.
+ *
+ *  If the selection is not a selection of a footprint, it does
+ *  nothing. Otherwise it updates the preview and Element data.
+ *
+ *  \param [in] selection The current selection in the treeview.
+ *  \param [in] user_data The library dialog.
+ */
+static void library_window_callback_tree_selection_changed(GtkTreeSelection * selection, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GhidLibraryWindow *library_window = (GhidLibraryWindow *) user_data;
+	library_t *entry = NULL;
+
+	if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+		return;
+
+	gtk_tree_model_get(model, &iter, MENU_ENTRY_COLUMN, &entry, -1);
+
+	if (entry == NULL)
+		return;
+
+	library_window_preview_refresh(library_window, NULL, entry);
+}
+
+/*! \brief Requests re-evaluation of the filter.
+ *  \par Function Description
+ *  This is the timeout function for the filtering of footprint
+ *  in the tree of the dialog.
+ *
+ *  The timeout this callback is attached to is removed after the
+ *  function.
+ *
+ *  \param [in] data The library dialog.
+ *  \returns FALSE to remove the timeout.
+ */
+static gboolean library_window_filter_timeout(gpointer data)
+{
+	GhidLibraryWindow *library_window = GHID_LIBRARY_WINDOW(data);
+	GtkTreeModel *model;
+
+	/* resets the source id in library_window */
+	library_window->filter_timeout = 0;
+
+	model = gtk_tree_view_get_model(library_window->libtreeview);
+
+	if (model != NULL) {
+		const gchar *text = gtk_entry_get_text(library_window->entry_filter);
+		gtk_tree_model_filter_refilter((GtkTreeModelFilter *) model);
+		if (strcmp(text, "") != 0) {
+			/* filter text not-empty */
+			gtk_tree_view_expand_all(library_window->libtreeview);
+
+			/* parametric footprints need to be refreshed on edit */
+			if (strchr(text, ')') != NULL)
+				library_window_preview_refresh(library_window, text, NULL);
+
+		}
+		else {
+			/* filter text is empty, collapse expanded tree */
+			gtk_tree_view_collapse_all(library_window->libtreeview);
+		}
+
+
+
+	}
+
+
+	/* return FALSE to remove the source */
+	return FALSE;
+}
+
+/*! \brief Callback function for the changed signal of the filter entry.
+ *  \par Function Description
+ *  This function monitors changes in the entry filter of the dialog.
+ *
+ *  It specifically manages the sensitivity of the clear button of the
+ *  entry depending on its contents. It also requests an update of the
+ *  footprint list by re-evaluating filter at every changes.
+ *
+ *  \param [in] editable  The filter text entry.
+ *  \param [in] user_data The library dialog.
+ */
+static void library_window_callback_filter_entry_changed(GtkEditable * editable, gpointer user_data)
+{
+	GhidLibraryWindow *library_window = GHID_LIBRARY_WINDOW(user_data);
+	GtkWidget *button;
+	gboolean sensitive;
+
+	/* turns button off if filter entry is empty */
+	/* turns it on otherwise */
+	button = GTK_WIDGET(library_window->button_clear);
+	sensitive = (g_ascii_strcasecmp(gtk_entry_get_text(library_window->entry_filter), "") != 0);
+	gtk_widget_set_sensitive(button, sensitive);
+
+	/* Cancel any pending update of the footprint list filter */
+	if (library_window->filter_timeout != 0)
+		g_source_remove(library_window->filter_timeout);
+
+	/* Schedule an update of the footprint list filter in
+	 * LIBRARY_FILTER_INTERVAL milliseconds */
+	library_window->filter_timeout = g_timeout_add(LIBRARY_FILTER_INTERVAL, library_window_filter_timeout, library_window);
+
+}
+
+/*! \brief Handles a click on the clear button.
+ *  \par Function Description
+ *  This is the callback function called every time the user press the
+ *  clear button associated with the filter.
+ *
+ *  It resets the filter entry, indirectly causing re-evaluation
+ *  of the filter on the list of symbols to update the display.
+ *
+ *  \param [in] editable  The filter text entry.
+ *  \param [in] user_data The library dialog.
+ */
+static void library_window_callback_filter_button_clicked(GtkButton * button, gpointer user_data)
+{
+	GhidLibraryWindow *library_window = GHID_LIBRARY_WINDOW(user_data);
+
+	/* clears text in text entry for filter */
+	gtk_entry_set_text(library_window->entry_filter, "");
+
+}
+
+/* \brief Create the tree model for the "Library" view.
+ * \par Function Description
+ * Creates a tree where the branches are the available library
+ * sources and the leaves are the footprints.
+ */
+static GtkTreeModel *create_lib_tree_model_recurse(GtkTreeStore *tree, GhidLibraryWindow *library_window, library_t *parent, GtkTreeIter *iter_parent)
+{
+	GtkTreeIter p_iter;
+	library_t *menu;
+	int n;
+
+	for(menu = parent->data.dir.children.array, n = 0; n < parent->data.dir.children.used; n++, menu++)
+	{
+		gtk_tree_store_append(tree, &p_iter, iter_parent);
+		gtk_tree_store_set(tree, &p_iter, MENU_NAME_COLUMN, menu->name, MENU_ENTRY_COLUMN, menu, -1);
+		if (menu->type == LIB_DIR)
+			create_lib_tree_model_recurse(tree, library_window, menu, &p_iter);
+	}
+
+	return (GtkTreeModel *) tree;
+}
+
+static GtkTreeModel *create_lib_tree_model(GhidLibraryWindow *library_window)
+{
+	GtkTreeStore *tree = gtk_tree_store_new(N_MENU_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
+	return create_lib_tree_model_recurse(tree, library_window, &library, NULL);
+}
+
+#if 0
+/* \brief On-demand refresh of the footprint library.
+ * \par Function Description
+ * Requests a rescan of the footprint library in order to pick up any
+ * new signals, and then updates the library window.
+ */
+static void library_window_callback_refresh_library(GtkButton * button, gpointer user_data)
+{
+	GhidLibraryWindow *library_window = GHID_LIBRARY_WINDOW(user_data);
+	GtkTreeModel *model;
+
+	/* Rescan the libraries for symbols */
+	/*  TODO: How do we do this in PCB?  */
+
+	/* Refresh the "Library" view */
+	model = (GtkTreeModel *)
+		g_object_new(GTK_TYPE_TREE_MODEL_FILTER, "child-model", create_lib_tree_model(library_window), "virtual-root", NULL, NULL);
+
+	gtk_tree_model_filter_set_visible_func((GtkTreeModelFilter *) model, lib_model_filter_visible_func, library_window, NULL);
+
+	gtk_tree_view_set_model(library_window->libtreeview, model);
+}
+#endif
+
+
+/*! \brief Creates the treeview for the "Library" view */
+static GtkWidget *create_lib_treeview(GhidLibraryWindow * library_window)
+{
+	GtkWidget *libtreeview, *vbox, *scrolled_win, *label, *hbox, *entry, *button;
+	GtkTreeModel *child_model, *model;
+	GtkTreeSelection *selection;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+
+	/* -- library selection view -- */
+
+	/* vertical box for footprint selection and search entry */
+	vbox = GTK_WIDGET(g_object_new(GTK_TYPE_VBOX,
+																 /* GtkContainer */
+																 "border-width", 5,
+																 /* GtkBox */
+																 "homogeneous", FALSE, "spacing", 5, NULL));
+
+	child_model = create_lib_tree_model(library_window);
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(child_model), MENU_NAME_COLUMN, GTK_SORT_ASCENDING);
+	model = (GtkTreeModel *) g_object_new(GTK_TYPE_TREE_MODEL_FILTER, "child-model", child_model, "virtual-root", NULL, NULL);
+
+	scrolled_win = GTK_WIDGET(g_object_new(GTK_TYPE_SCROLLED_WINDOW,
+																				 /* GtkScrolledWindow */
+																				 "hscrollbar-policy",
+																				 GTK_POLICY_AUTOMATIC,
+																				 "vscrollbar-policy", GTK_POLICY_ALWAYS, "shadow-type", GTK_SHADOW_ETCHED_IN, NULL));
+	/* create the treeview */
+	libtreeview = GTK_WIDGET(g_object_new(GTK_TYPE_TREE_VIEW,
+																				/* GtkTreeView */
+																				"model", model, "rules-hint", TRUE, "headers-visible", FALSE, NULL));
+
+	g_signal_connect(libtreeview, "row-activated", G_CALLBACK(tree_row_activated), NULL);
+
+	g_signal_connect(libtreeview, "key-press-event", G_CALLBACK(tree_row_key_pressed), NULL);
+
+	/* connect callback to selection */
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(libtreeview));
+	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+	g_signal_connect(selection, "changed", G_CALLBACK(library_window_callback_tree_selection_changed), library_window);
+
+	/* insert a column to treeview for library/symbol name */
+	renderer = GTK_CELL_RENDERER(g_object_new(GTK_TYPE_CELL_RENDERER_TEXT,
+																						/* GtkCellRendererText */
+																						"editable", FALSE, NULL));
+	column = GTK_TREE_VIEW_COLUMN(g_object_new(GTK_TYPE_TREE_VIEW_COLUMN,
+																						 /* GtkTreeViewColumn */
+																						 "title", _("Components"), NULL));
+	gtk_tree_view_column_pack_start(column, renderer, TRUE);
+	gtk_tree_view_column_set_attributes(column, renderer, "text", MENU_NAME_COLUMN, NULL);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(libtreeview), column);
+
+	/* add the treeview to the scrolled window */
+	gtk_container_add(GTK_CONTAINER(scrolled_win), libtreeview);
+	/* set directory/footprint treeview of library_window */
+	library_window->libtreeview = GTK_TREE_VIEW(libtreeview);
+
+	/* add the scrolled window for directories to the vertical box */
+	gtk_box_pack_start(GTK_BOX(vbox), scrolled_win, TRUE, TRUE, 0);
+
+
+	/* -- filter area -- */
+	hbox = GTK_WIDGET(g_object_new(GTK_TYPE_HBOX,
+																 /* GtkBox */
+																 "homogeneous", FALSE, "spacing", 3, NULL));
+
+	/* create the entry label */
+	label = GTK_WIDGET(g_object_new(GTK_TYPE_LABEL,
+																	/* GtkMisc */
+																	"xalign", 0.0,
+																	/* GtkLabel */
+																	"label", _("Filter:"), NULL));
+	/* add the search label to the filter area */
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+	/* create the text entry for filter in footprints */
+	entry = GTK_WIDGET(g_object_new(GTK_TYPE_ENTRY,
+																	/* GtkEntry */
+																	"text", "", NULL));
+	g_signal_connect(entry, "changed", G_CALLBACK(library_window_callback_filter_entry_changed), library_window);
+
+	/* now that that we have an entry, set the filter func of model */
+	gtk_tree_model_filter_set_visible_func((GtkTreeModelFilter *) model, lib_model_filter_visible_func, library_window, NULL);
+
+	/* add the filter entry to the filter area */
+	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+	/* set filter entry of library_window */
+	library_window->entry_filter = GTK_ENTRY(entry);
+	/* and init the event source for footprint filter */
+	library_window->filter_timeout = 0;
+
+	/* create the erase button for filter entry */
+	button = GTK_WIDGET(g_object_new(GTK_TYPE_BUTTON,
+																	 /* GtkWidget */
+																	 "sensitive", FALSE,
+																	 /* GtkButton */
+																	 "relief", GTK_RELIEF_NONE, NULL));
+
+	gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_SMALL_TOOLBAR));
+	g_signal_connect(button, "clicked", G_CALLBACK(library_window_callback_filter_button_clicked), library_window);
+	/* add the clear button to the filter area */
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	/* set clear button of library_window */
+	library_window->button_clear = GTK_BUTTON(button);
+
+#if 0
+	/* create the refresh button */
+	button = GTK_WIDGET(g_object_new(GTK_TYPE_BUTTON,
+																	 /* GtkWidget */
+																	 "sensitive", TRUE,
+																	 /* GtkButton */
+																	 "relief", GTK_RELIEF_NONE, NULL));
+	gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_SMALL_TOOLBAR));
+	/* add the refresh button to the filter area */
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	g_signal_connect(button, "clicked", G_CALLBACK(library_window_callback_refresh_library), library_window);
+#endif
+
+	/* add the filter area to the vertical box */
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	library_window->libtreeview = GTK_TREE_VIEW(libtreeview);
+
+	return vbox;
+}
+
+
+static GObject *library_window_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_params)
+{
+	GObject *object;
+	GhidLibraryWindow *library_window;
+	GtkWidget *content_area;
+	GtkWidget *hpaned, *notebook;
+	GtkWidget *libview;
+	GtkWidget *preview, *preview_text;
+	GtkWidget *alignment, *frame;
+
+	/* chain up to constructor of parent class */
+	object = G_OBJECT_CLASS(library_window_parent_class)->constructor(type, n_construct_properties, construct_params);
+	library_window = GHID_LIBRARY_WINDOW(object);
+
+	/* dialog initialization */
+	g_object_set(object,
+							 /* GtkWindow */
+							 "type", GTK_WINDOW_TOPLEVEL,
+							 "title", _("Select Footprint..."),
+							 "default-height", 300, "default-width", 400, "modal", FALSE, "window-position", GTK_WIN_POS_NONE,
+							 /* GtkDialog */
+							 "has-separator", TRUE, NULL);
+	g_object_set(gtk_dialog_get_content_area(GTK_DIALOG(library_window)), "homogeneous", FALSE, NULL);
+
+	/* horizontal pane containing selection and preview */
+	hpaned = GTK_WIDGET(g_object_new(GTK_TYPE_HPANED,
+																	 /* GtkContainer */
+																	 "border-width", 5, NULL));
+	library_window->hpaned = hpaned;
+
+	/* notebook for library views */
+	notebook = GTK_WIDGET(g_object_new(GTK_TYPE_NOTEBOOK, "show-tabs", FALSE, NULL));
+	library_window->viewtabs = GTK_NOTEBOOK(notebook);
+
+	libview = create_lib_treeview(library_window);
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), libview, gtk_label_new(_("Libraries")));
+
+	/* include the vertical box in horizontal box */
+	gtk_paned_pack1(GTK_PANED(hpaned), notebook, TRUE, FALSE);
+
+
+	/* -- preview area -- */
+	frame = GTK_WIDGET(g_object_new(GTK_TYPE_FRAME,
+																	/* GtkFrame */
+																	"label", _("Preview"), NULL));
+	alignment = GTK_WIDGET(g_object_new(GTK_TYPE_ALIGNMENT,
+																			/* GtkAlignment */
+																			"left-padding", 5,
+																			"right-padding", 5,
+																			"top-padding", 5,
+																			"bottom-padding", 5, "xscale", 1.0, "yscale", 1.0, "xalign", 0.5, "yalign", 0.5, NULL));
+	preview = (GtkWidget *) g_object_new(GHID_TYPE_PINOUT_PREVIEW,
+																			 /* GhidPinoutPreview */
+																			 "element-data", NULL,
+																			 /* GtkWidget */
+																			 "width-request", 150, "height-request", 150, NULL);
+
+	preview_text = gtk_label_new("");
+
+	{
+		GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(vbox), preview, TRUE, TRUE, 0);
+		gtk_box_pack_end(GTK_BOX(vbox), preview_text, FALSE, FALSE, 0);
+
+		gtk_container_add(GTK_CONTAINER(alignment), vbox);
+		gtk_container_add(GTK_CONTAINER(frame), alignment);
+	}
+
+	/* set preview of library_window */
+	library_window->preview = preview;
+	library_window->preview_text = preview_text;
+
+	gtk_paned_pack2(GTK_PANED(hpaned), frame, FALSE, FALSE);
+
+	/* add the hpaned to the dialog content area */
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(library_window));
+	gtk_box_pack_start(GTK_BOX(content_area), hpaned, TRUE, TRUE, 0);
+	gtk_widget_show_all(hpaned);
+
+
+	/* now add buttons in the action area */
+	gtk_dialog_add_buttons(GTK_DIALOG(library_window),
+												 /*  - close button */
+												 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+
+	return object;
+}
+
+static void library_window_finalize(GObject * object)
+{
+	GhidLibraryWindow *library_window = GHID_LIBRARY_WINDOW(object);
+
+	if (library_window->filter_timeout != 0) {
+		g_source_remove(library_window->filter_timeout);
+		library_window->filter_timeout = 0;
+	}
+
+	G_OBJECT_CLASS(library_window_parent_class)->finalize(object);
+}
+
+
+static void library_window_class_init(GhidLibraryWindowClass * klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+	gobject_class->constructor = library_window_constructor;
+	gobject_class->finalize = library_window_finalize;
+
+	library_window_parent_class = (GObjectClass *) g_type_class_peek_parent(klass);
+}
+
+
+GType ghid_library_window_get_type()
+{
+	static GType library_window_type = 0;
+
+	if (!library_window_type) {
+		static const GTypeInfo library_window_info = {
+			sizeof(GhidLibraryWindowClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) library_window_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GhidLibraryWindow),
+			0,												/* n_preallocs */
+			NULL											/* instance_init */
+		};
+
+		library_window_type = g_type_register_static(GTK_TYPE_DIALOG, "GhidLibraryWindow", &library_window_info, (GTypeFlags) 0);
+	}
+
+	return library_window_type;
+}
diff --git a/src_plugins/hid_gtk/gui-library-window.h b/src_plugins/hid_gtk/gui-library-window.h
new file mode 100644
index 0000000..8f821e5
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-library-window.h
@@ -0,0 +1,59 @@
+/* gEDA - GPL Electronic Design Automation
+ * gschem - gEDA Schematic Capture
+ * Copyright (C) 1998-2004 Ales V. Hvezda
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef PCB_HID_GTK_GUI_LIBRARY_WINDOW_H
+#define PCB_HID_GTK_GUI_LIBRARY_WINDOW_H
+
+
+/*
+ * GhidLibraryWindow
+ */
+
+#define GHID_TYPE_LIBRARY_WINDOW           (ghid_library_window_get_type())
+#define GHID_LIBRARY_WINDOW(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_TYPE_LIBRARY_WINDOW, GhidLibraryWindow))
+#define GHID_LIBRARY_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass),  GHID_TYPE_LIBRARY_WINDOW, GhidLibraryWindowClass))
+#define GHID_IS_LIBRARY_WINDOW(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_TYPE_LIBRARY_WINDOW))
+#define GHID_GET_LIBRARY_WINDOW_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),  GHID_TYPE_LIBRARY, GhidLibraryWindowClass))
+
+typedef struct _GhidLibraryWindowClass GhidLibraryWindowClass;
+typedef struct _GhidLibraryWindow GhidLibraryWindow;
+
+
+struct _GhidLibraryWindowClass {
+	GtkDialogClass parent_class;
+};
+
+struct _GhidLibraryWindow {
+	GtkDialog parent_instance;
+
+	GtkWidget *hpaned;
+	GtkTreeView *libtreeview;
+	GtkNotebook *viewtabs;
+	GtkWidget *preview;
+	GtkWidget *preview_text;
+	GtkEntry *entry_filter;
+	GtkButton *button_clear;
+	guint filter_timeout;
+};
+
+
+GType ghid_library_window_get_type(void);
+
+#endif /* PCB_HID_GTK_GUI_LIBRARY_WINDOW_H */
diff --git a/src_plugins/hid_gtk/gui-log-window.c b/src_plugins/hid_gtk/gui-log-window.c
new file mode 100644
index 0000000..b2508ba
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-log-window.c
@@ -0,0 +1,201 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port
+*/
+
+#include "config.h"
+#include "conf_core.h"
+#include "conf_hid.h"
+
+#include "gui.h"
+#include "win_place.h"
+#include "pcb-printf.h"
+#include "hid_actions.h"
+
+static GtkWidget *log_window, *log_text;
+static gboolean log_show_on_append = FALSE;
+
+/* Remember user window resizes. */
+static gint log_window_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, gpointer data)
+{
+	wplc_config_event(widget, &hid_gtk_wgeo.log_x, &hid_gtk_wgeo.log_y, &hid_gtk_wgeo.log_width, &hid_gtk_wgeo.log_height);
+	return FALSE;
+}
+
+static void log_close_cb(gpointer data)
+{
+	gtk_widget_destroy(log_window);
+	log_window = NULL;
+}
+
+static void log_destroy_cb(GtkWidget * widget, gpointer data)
+{
+	log_window = NULL;
+}
+
+void ghid_log_window_create()
+{
+	GtkWidget *vbox, *hbox, *button;
+	extern int gtkhid_active;
+
+	if ((log_window) || (ghidgui == NULL) || (!gtkhid_active))
+		return;
+
+	log_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	g_signal_connect(G_OBJECT(log_window), "destroy", G_CALLBACK(log_destroy_cb), NULL);
+	g_signal_connect(G_OBJECT(log_window), "configure_event", G_CALLBACK(log_window_configure_event_cb), NULL);
+	gtk_window_set_title(GTK_WINDOW(log_window), _("pcb-rnd Log"));
+	gtk_window_set_wmclass(GTK_WINDOW(log_window), "PCB_Log", "PCB");
+	gtk_window_set_default_size(GTK_WINDOW(log_window), hid_gtk_wgeo.log_width, hid_gtk_wgeo.log_height);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_container_add(GTK_CONTAINER(log_window), vbox);
+
+	log_text = ghid_scrolled_text_view(vbox, NULL, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(log_close_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	wplc_place(WPLC_LOG, log_window);
+
+	gtk_widget_realize(log_window);
+}
+
+void ghid_log_window_show(gboolean raise)
+{
+	ghid_log_window_create();
+	gtk_widget_show_all(log_window);
+	if (raise)
+		gtk_window_present(GTK_WINDOW(log_window));
+}
+
+typedef struct log_pending_s log_pending_t;
+struct log_pending_s {
+	log_pending_t *next;
+	enum pcb_message_level level;
+	char msg[1];
+};
+
+log_pending_t *log_pending_first = NULL, *log_pending_last = NULL;
+
+static void ghid_log_append_string_(enum pcb_message_level level, gchar *msg)
+{
+	const char *tag;
+	int popup;
+
+	conf_loglevel_props(level, &tag, &popup);
+	if (tag != NULL)
+		ghid_text_view_append(log_text, (gchar *)tag);
+
+	ghid_text_view_append(log_text, msg);
+
+	if (popup)
+		hid_actionl("DoWindows", "Log", "false", NULL);
+}
+
+static void ghid_log_append_string(enum pcb_message_level level, gchar *s)
+{
+	extern int gtkhid_active;
+	log_pending_t *m, *next;
+
+	if (!gtkhid_active) {
+		/* GUI not initialized yet - save these messages for later
+		   NOTE: no need to free this at quit: the GUI will be inited and then it'll be freed */
+		m = malloc(sizeof(log_pending_t) + strlen(s));
+		strcpy(m->msg, s);
+		m->level = level;
+		m->next = NULL;
+		if (log_pending_last != NULL)
+			log_pending_last->next = m;
+		log_pending_last = m;
+		if (log_pending_first == NULL)
+			log_pending_first = m;
+		return;
+	}
+
+	if (!log_show_on_append) {
+		ghid_log_window_create();
+		/* display and free pending messages */
+		for(m = log_pending_first; m != NULL; m = next) {
+			next = m->next;
+			ghid_log_append_string_(m->level, m->msg);
+			free(m);
+		}
+		log_pending_last = log_pending_first = NULL;
+	}
+	else
+		ghid_log_window_show(FALSE);
+
+	ghid_log_append_string_(level, s);
+}
+
+void ghid_log(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	ghid_logv(PCB_MSG_INFO, fmt, ap);
+	va_end(ap);
+}
+
+void ghid_logv(enum pcb_message_level level, const char *fmt, va_list args)
+{
+	char *msg = pcb_strdup_vprintf(fmt, args);
+	ghid_log_append_string(level, msg);
+	free(msg);
+}
+
+static const char logshowonappend_syntax[] = "LogShowOnAppend(true|false)";
+
+static const char logshowonappend_help[] = "If true, the log window will be shown whenever something is appended \
+to it.  If false, the log will still be updated, but the window won't \
+be shown.";
+
+static gint GhidLogShowOnAppend(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *a = argc == 1 ? argv[0] : "";
+
+	if (strncasecmp(a, "t", 1) == 0) {
+		log_show_on_append = TRUE;
+	}
+	else if (strncasecmp(a, "f", 1) == 0) {
+		log_show_on_append = FALSE;
+	}
+	return 0;
+}
+
+HID_Action ghid_log_action_list[] = {
+	{"LogShowOnAppend", 0, GhidLogShowOnAppend,
+	 logshowonappend_help, logshowonappend_syntax}
+	,
+};
+
+REGISTER_ACTIONS(ghid_log_action_list, ghid_cookie)
diff --git a/src_plugins/hid_gtk/gui-misc.c b/src_plugins/hid_gtk/gui-misc.c
new file mode 100644
index 0000000..f8a69d0
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-misc.c
@@ -0,0 +1,420 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* This file was originally written by Bill Wilson for the PCB Gtk port */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+#include "crosshair.h"
+#include "data.h"
+#include "misc.h"
+#include "action_helper.h"
+#include "set.h"
+#include "pcb-printf.h"
+#include "misc_util.h"
+
+#include "gui.h"
+#include <gdk/gdkkeysyms.h>
+
+const char *ghid_cookie = "gtk hid";
+const char *ghid_menu_cookie = "gtk hid menu";
+
+#define CUSTOM_CURSOR_CLOCKWISE		(GDK_LAST_CURSOR + 10)
+#define CUSTOM_CURSOR_DRAG			(GDK_LAST_CURSOR + 11)
+#define CUSTOM_CURSOR_LOCK			(GDK_LAST_CURSOR + 12)
+
+#define ICON_X_HOT 8
+#define ICON_Y_HOT 8
+
+
+GdkPixmap *XC_clock_source, *XC_clock_mask, *XC_hand_source, *XC_hand_mask, *XC_lock_source, *XC_lock_mask;
+
+
+static GdkCursorType oldCursor;
+
+void ghid_status_line_set_text(const gchar * text)
+{
+	if (ghidgui->command_entry_status_line_active)
+		return;
+
+	ghid_label_set_markup(ghidgui->status_line_label, text);
+}
+
+void ghid_cursor_position_label_set_text(gchar * text)
+{
+	ghid_label_set_markup(ghidgui->cursor_position_absolute_label, text);
+}
+
+void ghid_cursor_position_relative_label_set_text(gchar * text)
+{
+	ghid_label_set_markup(ghidgui->cursor_position_relative_label, text);
+}
+
+static GdkCursorType gport_set_cursor(GdkCursorType shape)
+{
+	GdkWindow *window;
+	GdkCursorType old_shape = gport->X_cursor_shape;
+	GdkColor fg = { 0, 65535, 65535, 65535 };	/* white */
+	GdkColor bg = { 0, 0, 0, 0 };	/* black */
+
+	if (gport->drawing_area == NULL)
+		return GDK_X_CURSOR;
+
+	window = gtk_widget_get_window(gport->drawing_area);
+
+	if (gport->X_cursor_shape == shape)
+		return shape;
+
+	/* check if window exists to prevent from fatal errors */
+	if (window == NULL)
+		return GDK_X_CURSOR;
+
+	gport->X_cursor_shape = shape;
+	if (shape > GDK_LAST_CURSOR) {
+		if (shape == CUSTOM_CURSOR_CLOCKWISE)
+			gport->X_cursor = gdk_cursor_new_from_pixmap(XC_clock_source, XC_clock_mask, &fg, &bg, ICON_X_HOT, ICON_Y_HOT);
+		else if (shape == CUSTOM_CURSOR_DRAG)
+			gport->X_cursor = gdk_cursor_new_from_pixmap(XC_hand_source, XC_hand_mask, &fg, &bg, ICON_X_HOT, ICON_Y_HOT);
+		else if (shape == CUSTOM_CURSOR_LOCK)
+			gport->X_cursor = gdk_cursor_new_from_pixmap(XC_lock_source, XC_lock_mask, &fg, &bg, ICON_X_HOT, ICON_Y_HOT);
+	}
+	else
+		gport->X_cursor = gdk_cursor_new(shape);
+
+	gdk_window_set_cursor(window, gport->X_cursor);
+	gdk_cursor_unref(gport->X_cursor);
+
+	return old_shape;
+}
+
+void ghid_point_cursor(void)
+{
+	oldCursor = gport_set_cursor(GDK_DRAPED_BOX);
+}
+
+void ghid_hand_cursor(void)
+{
+	oldCursor = gport_set_cursor(GDK_HAND2);
+}
+
+void ghid_watch_cursor(void)
+{
+	GdkCursorType tmp;
+
+	tmp = gport_set_cursor(GDK_WATCH);
+	if (tmp != GDK_WATCH)
+		oldCursor = tmp;
+}
+
+void ghid_mode_cursor(int Mode)
+{
+	switch (Mode) {
+	case PCB_MODE_NO:
+		gport_set_cursor((GdkCursorType) CUSTOM_CURSOR_DRAG);
+		break;
+
+	case PCB_MODE_VIA:
+		gport_set_cursor(GDK_ARROW);
+		break;
+
+	case PCB_MODE_LINE:
+		gport_set_cursor(GDK_PENCIL);
+		break;
+
+	case PCB_MODE_ARC:
+		gport_set_cursor(GDK_QUESTION_ARROW);
+		break;
+
+	case PCB_MODE_ARROW:
+		gport_set_cursor(GDK_LEFT_PTR);
+		break;
+
+	case PCB_MODE_POLYGON:
+	case PCB_MODE_POLYGON_HOLE:
+		gport_set_cursor(GDK_SB_UP_ARROW);
+		break;
+
+	case PCB_MODE_PASTE_BUFFER:
+		gport_set_cursor(GDK_HAND1);
+		break;
+
+	case PCB_MODE_TEXT:
+		gport_set_cursor(GDK_XTERM);
+		break;
+
+	case PCB_MODE_RECTANGLE:
+		gport_set_cursor(GDK_UL_ANGLE);
+		break;
+
+	case PCB_MODE_THERMAL:
+		gport_set_cursor(GDK_IRON_CROSS);
+		break;
+
+	case PCB_MODE_REMOVE:
+		gport_set_cursor(GDK_PIRATE);
+		break;
+
+	case PCB_MODE_ROTATE:
+		if (ghid_shift_is_pressed())
+			gport_set_cursor((GdkCursorType) CUSTOM_CURSOR_CLOCKWISE);
+		else
+			gport_set_cursor(GDK_EXCHANGE);
+		break;
+
+	case PCB_MODE_COPY:
+	case PCB_MODE_MOVE:
+		gport_set_cursor(GDK_CROSSHAIR);
+		break;
+
+	case PCB_MODE_INSERT_POINT:
+		gport_set_cursor(GDK_DOTBOX);
+		break;
+
+	case PCB_MODE_LOCK:
+		gport_set_cursor((GdkCursorType) CUSTOM_CURSOR_LOCK);
+	}
+}
+
+void ghid_corner_cursor(void)
+{
+	GdkCursorType shape;
+
+	if (Crosshair.Y <= Crosshair.AttachedBox.Point1.Y)
+		shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ? GDK_UR_ANGLE : GDK_UL_ANGLE;
+	else
+		shape = (Crosshair.X >= Crosshair.AttachedBox.Point1.X) ? GDK_LR_ANGLE : GDK_LL_ANGLE;
+	if (gport->X_cursor_shape != shape)
+		gport_set_cursor(shape);
+}
+
+void ghid_restore_cursor(void)
+{
+	gport_set_cursor(oldCursor);
+}
+
+
+
+	/* =============================================================== */
+static gboolean got_location;
+
+	/* If user hits a key instead of the mouse button, we'll abort unless
+	   |  it's the enter key (which accepts the current crosshair location).
+	 */
+static gboolean loop_key_press_cb(GtkWidget * drawing_area, GdkEventKey * kev, GMainLoop ** loop)
+{
+	gint ksym = kev->keyval;
+
+	if (ghid_is_modifier_key_sym(ksym))
+		return TRUE;
+
+	switch (ksym) {
+	case GDK_Return:							/* Accept cursor location */
+		if (g_main_loop_is_running(*loop))
+			g_main_loop_quit(*loop);
+		break;
+
+	default:											/* Abort */
+		got_location = FALSE;
+		if (g_main_loop_is_running(*loop))
+			g_main_loop_quit(*loop);
+		break;
+	}
+	return TRUE;
+}
+
+	/* User hit a mouse button in the Output drawing area, so quit the loop
+	   |  and the cursor values when the button was pressed will be used.
+	 */
+static gboolean loop_button_press_cb(GtkWidget * drawing_area, GdkEventButton * ev, GMainLoop ** loop)
+{
+	if (g_main_loop_is_running(*loop))
+		g_main_loop_quit(*loop);
+	ghid_note_event_location(ev);
+	return TRUE;
+}
+
+int ghid_wheel_zoom = 0;
+	/* Run a glib GMainLoop which intercepts key and mouse button events from
+	   |  the top level loop.  When a mouse or key is hit in the Output drawing
+	   |  area, quit the loop so the top level loop can continue and use the
+	   |  the mouse pointer coordinates at the time of the mouse button event.
+	 */
+static gboolean run_get_location_loop(const gchar * message)
+{
+	static int getting_loc = 0;
+	GMainLoop *loop;
+	gulong button_handler, key_handler;
+	gint oldObjState, oldLineState, oldBoxState;
+
+	/* Do not enter the loop recursively (ask for coord only once); also don't
+	   ask for coord if the scrollwheel triggered the event, it may cause strange
+	   GUI lockups when done outside of the drawing area */
+	if ((getting_loc) || (ghid_wheel_zoom))
+		return pcb_false;
+
+	getting_loc = 1;
+	ghid_status_line_set_text(message);
+
+	oldObjState = Crosshair.AttachedObject.State;
+	oldLineState = Crosshair.AttachedLine.State;
+	oldBoxState = Crosshair.AttachedBox.State;
+	notify_crosshair_change(pcb_false);
+	Crosshair.AttachedObject.State = STATE_FIRST;
+	Crosshair.AttachedLine.State = STATE_FIRST;
+	Crosshair.AttachedBox.State = STATE_FIRST;
+	ghid_hand_cursor();
+	notify_crosshair_change(pcb_true);
+
+	/* Stop the top level GMainLoop from getting user input from keyboard
+	   |  and mouse so we can install our own handlers here.  Also set the
+	   |  control interface insensitive so all the user can do is hit a key
+	   |  or mouse button in the Output drawing area.
+	 */
+	ghid_interface_input_signals_disconnect();
+	ghid_interface_set_sensitive(FALSE);
+
+	got_location = TRUE;					/* Will be unset by hitting most keys */
+	button_handler =
+		g_signal_connect(G_OBJECT(gport->drawing_area), "button_press_event", G_CALLBACK(loop_button_press_cb), &loop);
+	key_handler = g_signal_connect(G_OBJECT(gport->top_window), "key_press_event", G_CALLBACK(loop_key_press_cb), &loop);
+
+	loop = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(loop);
+
+	g_main_loop_unref(loop);
+
+	g_signal_handler_disconnect(gport->drawing_area, button_handler);
+	g_signal_handler_disconnect(gport->top_window, key_handler);
+
+	ghid_interface_input_signals_connect();	/* return to normal */
+	ghid_interface_set_sensitive(TRUE);
+
+	notify_crosshair_change(pcb_false);
+	Crosshair.AttachedObject.State = oldObjState;
+	Crosshair.AttachedLine.State = oldLineState;
+	Crosshair.AttachedBox.State = oldBoxState;
+	notify_crosshair_change(pcb_true);
+	ghid_restore_cursor();
+
+	ghid_set_status_line_label();
+
+	getting_loc = 0;
+	return got_location;
+}
+
+
+
+/* ---------------------------------------------------------------------------*/
+void ghid_get_user_xy(const char *msg)
+{
+	run_get_location_loop(msg);
+}
+
+	/* XXX The abort dialog isn't implemented yet in the Gtk port
+	 */
+void ghid_create_abort_dialog(char *msg)
+{
+}
+
+gboolean ghid_check_abort(void)
+{
+	return FALSE;									/* Abort isn't implemented, so never abort */
+}
+
+void ghid_end_abort(void)
+{
+}
+
+void ghid_get_pointer(int *x, int *y)
+{
+	gint xp, yp;
+
+	gdk_window_get_pointer(gtk_widget_get_window(gport->drawing_area), &xp, &yp, NULL);
+	if (x)
+		*x = xp;
+	if (y)
+		*y = yp;
+}
+
+/* ---------------------------------------------------------------------------
+ * output of status line
+ */
+void ghid_set_status_line_label(void)
+{
+	const gchar *flag = conf_core.editor.all_direction_lines
+	    ? "*" : (conf_core.editor.line_refraction == 0 ? "X" : (conf_core.editor.line_refraction == 1 ? "_/" : "\\_"));
+	    char *text = pcb_strdup_printf(_("%m+<b>view</b>=%s  "
+	        "<b>grid</b>=%$mS  "
+	        "%s%s  "
+	        "<b>line</b>=%mS  "
+	        "<b>via</b>=%mS (%mS)  %s"
+	        "<b>clearance</b>=%mS  "
+	        "<b>text</b>=%i%%  "
+	        "<b>buffer</b>=#%i"),
+	conf_core.editor.grid_unit->allow,
+	conf_core.editor.show_solder_side ? _("solder") : _("component"),
+	PCB->Grid,
+	flag, conf_core.editor.rubber_band_mode ? ",R  " : "  ",
+	conf_core.design.line_thickness,
+	conf_core.design.via_thickness,
+	conf_core.design.via_drilling_hole,
+	conf_hid_gtk.plugins.hid_gtk.compact_horizontal ? "\n" : "",
+	conf_core.design.clearance,
+	conf_core.design.text_scale, conf_core.editor.buffer_number + 1);
+
+	ghid_status_line_set_text(text);
+	free(text);
+}
+
+/* ---------------------------------------------------------------------------
+ * output of cursor position
+ */
+void ghid_set_cursor_position_labels(void)
+{
+	char *text, sep = ' ';
+	if (conf_hid_gtk.plugins.hid_gtk.compact_vertical)
+		sep = '\n';
+
+	if (Marked.status) {
+		Coord dx = Crosshair.X - Marked.X;
+		Coord dy = Crosshair.Y - Marked.Y;
+		Coord r = Distance(Crosshair.X, Crosshair.Y, Marked.X, Marked.Y);
+		double a = atan2(dy, dx) * PCB_RAD_TO_DEG;
+
+
+		text = pcb_strdup_printf(_("%m+r %-mS;%cphi %-.1f;%c%-mS %-mS"), conf_core.editor.grid_unit->allow, r, sep, a, sep, dx, dy);
+		ghid_cursor_position_relative_label_set_text(text);
+		free(text);
+	}
+	else {
+		char text[64];
+		sprintf(text, _("r __.__;%cphi __._;%c__.__ __.__"), sep, sep);
+		ghid_cursor_position_relative_label_set_text(text);
+	}
+
+
+	text = pcb_strdup_printf("%m+%-mS%c%-mS", conf_core.editor.grid_unit->allow, Crosshair.X, sep, Crosshair.Y);
+	ghid_cursor_position_label_set_text(text);
+	free(text);
+}
diff --git a/src_plugins/hid_gtk/gui-netlist-window.c b/src_plugins/hid_gtk/gui-netlist-window.c
new file mode 100644
index 0000000..6007673
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-netlist-window.c
@@ -0,0 +1,968 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/*
+ * This file written by Bill Wilson for the PCB Gtk port
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "global.h"
+
+#include "win_place.h"
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "misc.h"
+#include "mymem.h"
+#include "rats.h"
+#include "remove.h"
+#include "search.h"
+#include "select.h"
+#include "set.h"
+#include "undo.h"
+#include "hid_actions.h"
+
+#include "gui.h"
+
+
+#define NET_HIERARCHY_SEPARATOR "/"
+
+static GtkWidget *netlist_window;
+static GtkWidget *disable_all_button;
+
+static GtkTreeModel *node_model;
+static GtkTreeView *node_treeview;
+static GtkTreeSelection *node_selection;
+
+static gboolean selection_holdoff;
+
+static LibraryMenuType *selected_net;
+static LibraryMenuType *node_selected_net;
+
+
+/* The Netlist window displays all the layout nets in a left treeview
+   |  When one of the nets is selected, all of its nodes (or connections)
+   |  will be displayed in a right treeview.  If a "Select on layout" button
+   |  is pressed, the net that is selected in the left treeview will be
+   |  drawn selected on the layout.
+   |
+   |  Gtk separates the data model from the view in its treeview widgets so
+   |  here we maintain two data models.  The net data model has pointers to
+   |  all the nets in the layout and the node data model keeps pointers to all
+   |  the nodes for the currently selected net.  By updating the data models
+   |  the net and node gtk treeviews handle displaying the results.
+   |
+   |  The netlist window code has a public interface providing hooks so PCB
+   |  code can control the net and node treeviews:
+   |
+   |	ghid_get_net_from_node_name gchar *node_name, gboolean enabled_only)
+   |		Given a node name (eg C101-1), walk through the nets in the net
+   |		data model and search each net for the given node_name.  If found
+   |		and enabled_only is true, make the net treeview scroll to and
+   |		highlight (select) the found net.  Return the found net.
+   |
+   |	ghid_netlist_highlight_node()
+   |		Given some PCB internal pointers (not really a good gui api here)
+   |		look up a node name determined by the pointers and highlight the node
+   |		in the node treeview.  By using ghid_get_net_from_node_name() to
+   |		look up the node, the net the node belongs to will also be
+   |		highlighted in the net treeview.
+   |
+   |	ghid_netlist_window_update(gboolean init_nodes)
+   |		PCB calls this to tell the gui netlist code the layout net has
+   |		changed and the gui data structures (net and optionally node data
+   |		models) should be rebuilt.
+*/
+
+
+/* -------- The netlist nodes (LibraryEntryType) data model ----------
+   |  Each time a net is selected in the left treeview, this node model
+   |  is recreated containing all the nodes (pins/pads) that are connected
+   |  to the net.  Loading the new model will update the right treeview with
+   |  all the new node names (C100-1, R100-1, etc).
+   |
+   |  The terminology is a bit confusing because the PCB netlist data
+   |  structures are generic structures used for library elements, netlist
+   |  data, and possibly other things also.  The mapping is that
+   |  the layout netlist data structure is a LibraryType which
+   |  contains an allocated array of LibraryMenuType structs.  Each of these
+   |  structs represents a net in the netlist and contains an array
+   |  of LibraryEntryType structs which represent the nodes connecting to
+   |  the net.  So we have:
+   |
+   |                      Nets              Nodes
+   |       LibraryType    LibraryMenuType   LibraryEntryType
+   | -------------------------------------------------------
+   |  PCB->NetlistLib------Menu[0]-----------Entry[0]
+   |                     |                   Entry[1]
+   |                     |                     ...
+   |                     |
+   |                     --Menu[1]-----------Entry[0]
+   |                     |                   Entry[1]
+   |                     |                     ...
+   |                     |
+   |                     -- ...
+   |
+   | Where for example Menu[] names would be nets GND, Vcc, etc and Entry[]
+   | names would be nodes C101-1, R101-2, etc
+*/
+
+LibraryEntryType *node_get_node_from_name(gchar * node_name, LibraryMenuType ** node_net);
+
+enum {
+	NODE_NAME_COLUMN,							/* Name to show in the treeview         */
+	NODE_LIBRARY_COLUMN,					/* Pointer to this node (LibraryEntryType)      */
+	N_NODE_COLUMNS
+};
+
+/* Given a net in the netlist (a LibraryMenuType) put all the Entry[]
+   |  names (the nodes) into a newly created node tree model.
+*/
+static GtkTreeModel *node_model_create(LibraryMenuType * menu)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+
+	store = gtk_list_store_new(N_NODE_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
+
+	if (menu == NULL)
+		return GTK_TREE_MODEL(store);
+
+	ENTRY_LOOP(menu);
+	{
+		if (!entry->ListEntry)
+			continue;
+		gtk_list_store_append(store, &iter);
+		gtk_list_store_set(store, &iter, NODE_NAME_COLUMN, entry->ListEntry, NODE_LIBRARY_COLUMN, entry, -1);
+	}
+	END_LOOP;
+
+	return GTK_TREE_MODEL(store);
+}
+
+/* When there's a new node to display in the node treeview, call this.
+   |  Create a new model containing the nodes of the given net, insert
+   |  the model into the treeview and unref the old model.
+*/
+static void node_model_update(LibraryMenuType * menu)
+{
+	GtkTreeModel *model;
+
+	if (menu == NULL) {
+		Message(PCB_MSG_DEFAULT, "Error: can't update netlist window: there is no netlist loaded.\n");
+		return;
+	}
+
+	model = node_model;
+	node_model = node_model_create(menu);
+	gtk_tree_view_set_model(node_treeview, node_model);
+
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(node_model), NODE_NAME_COLUMN, GTK_SORT_ASCENDING);
+
+	/* We could be using gtk_list_store_clear() on the same model, but it's
+	   |  just as easy that we've created a new one and here unref the old one.
+	 */
+	if (model)
+		g_object_unref(G_OBJECT(model));
+}
+
+static void toggle_pin_selected(LibraryEntryType * entry)
+{
+	ConnectionType conn;
+
+	if (!SeekPad(entry, &conn, pcb_false))
+		return;
+
+	AddObjectToFlagUndoList(conn.type, conn.ptr1, conn.ptr2, conn.ptr2);
+	TOGGLE_FLAG(PCB_FLAG_SELECTED, (AnyObjectType *) conn.ptr2);
+	DrawObject(conn.type, conn.ptr1, conn.ptr2);
+}
+
+
+/* Callback when the user clicks on a PCB node in the right node treeview.
+ */
+static void node_selection_changed_cb(GtkTreeSelection * selection, gpointer data)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	LibraryMenuType *node_net;
+	LibraryEntryType *node;
+	ConnectionType conn;
+	Coord x, y;
+	static gchar *node_name;
+
+	if (selection_holdoff)				/* PCB is highlighting, user is not selecting */
+		return;
+
+	/* Toggle off the previous selection.  Look up node_name to make sure
+	   |  it still exists.  This toggling can get out of sync if a node is
+	   |  toggled selected, then the net that includes the node is selected
+	   |  then unselected.
+	 */
+	if ((node = node_get_node_from_name(node_name, &node_net)) != NULL) {
+		/* If net node belongs to has been highlighted/unhighighed, toggling
+		   |  if off here will get our on/off toggling out of sync.
+		 */
+		if (node_net == node_selected_net) {
+			toggle_pin_selected(node);
+			ghid_cancel_lead_user();
+		}
+		g_free(node_name);
+		node_name = NULL;
+	}
+
+	/* Get the selected treeview row.
+	 */
+	if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
+		if (node)
+			ghid_invalidate_all();
+		return;
+	}
+
+	/* From the treeview row, extract the node pointer stored there and
+	   |  we've got a pointer to the LibraryEntryType (node) the row
+	   |  represents.
+	 */
+	gtk_tree_model_get(model, &iter, NODE_LIBRARY_COLUMN, &node, -1);
+
+	dup_string(&node_name, node->ListEntry);
+	node_selected_net = selected_net;
+
+	/* Now just toggle a select of the node on the layout
+	 */
+	toggle_pin_selected(node);
+	IncrementUndoSerialNumber();
+
+	/* And lead the user to the location */
+	if (SeekPad(node, &conn, pcb_false))
+		switch (conn.type) {
+		case PCB_TYPE_PIN:
+			{
+				PinTypePtr pin = (PinTypePtr) conn.ptr2;
+				x = pin->X;
+				y = pin->Y;
+				gui->set_crosshair(x, y, 0);
+				ghid_lead_user_to_location(x, y);
+				break;
+			}
+		case PCB_TYPE_PAD:
+			{
+				PadTypePtr pad = (PadTypePtr) conn.ptr2;
+				x = pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2;
+				y = pad->Point1.Y + (pad->Point2.Y - pad->Point2.Y) / 2;
+				gui->set_crosshair(x, y, 0);
+				ghid_lead_user_to_location(x, y);
+				break;
+			}
+		}
+}
+
+
+/* -------- The net (LibraryMenuType) data model ----------
+ */
+/* TODO: the enable and disable all nets.  Can't seem to get how that's
+   |  supposed to work, but it'll take updating the NET_ENABLED_COLUMN in
+   |  the net_model.  Probably it should be made into a gpointer and make
+   |  a text renderer for it and just write a '*' or a ' ' similar to the
+   |  the Xt PCB scheme.  Or better, since it's an "all nets" function, just
+   |  have a "Disable all nets" toggle button and don't mess with the
+   |  model/treeview at all.
+*/
+enum {
+	NET_ENABLED_COLUMN,						/* If enabled will be ' ', if disable '*'       */
+	NET_NAME_COLUMN,							/* Name to show in the treeview */
+	NET_LIBRARY_COLUMN,						/* Pointer to this net (LibraryMenuType)        */
+	N_NET_COLUMNS
+};
+
+static GtkTreeModel *net_model = NULL;
+static GtkTreeView *net_treeview;
+
+static gboolean loading_new_netlist;
+
+static GtkTreeModel *net_model_create(void)
+{
+	GtkTreeModel *model;
+	GtkTreeStore *store;
+	GtkTreeIter new_iter;
+	GtkTreeIter parent_iter;
+	GtkTreeIter *parent_ptr;
+	GtkTreePath *path;
+	GtkTreeRowReference *row_ref;
+	GHashTable *prefix_hash;
+	char *display_name;
+	char *hash_string;
+	char **join_array;
+	char **path_segments;
+	int path_depth;
+	int try_depth;
+
+	store = gtk_tree_store_new(N_NET_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+
+	model = GTK_TREE_MODEL(store);
+
+	/* Hash table stores GtkTreeRowReference for given path prefixes */
+	prefix_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)
+																			gtk_tree_row_reference_free);
+
+	MENU_LOOP(&PCB->NetlistLib[NETLIST_EDITED]);
+	{
+		if (!menu->Name)
+			continue;
+
+		if (loading_new_netlist)
+			menu->flag = TRUE;
+
+		parent_ptr = NULL;
+
+		path_segments = g_strsplit(menu->Name, NET_HIERARCHY_SEPARATOR, 0);
+		path_depth = g_strv_length(path_segments);
+
+		for (try_depth = path_depth - 1; try_depth > 0; try_depth--) {
+			join_array = g_new0(char *, try_depth + 1);
+			memcpy(join_array, path_segments, sizeof(char *) * try_depth);
+
+			/* See if this net's parent node is in the hash table */
+			hash_string = g_strjoinv(NET_HIERARCHY_SEPARATOR, join_array);
+			g_free(join_array);
+
+			row_ref = (GtkTreeRowReference *) g_hash_table_lookup(prefix_hash, hash_string);
+			g_free(hash_string);
+
+			/* If we didn't find the path at this level, keep looping */
+			if (row_ref == NULL)
+				continue;
+
+			path = gtk_tree_row_reference_get_path(row_ref);
+			gtk_tree_model_get_iter(model, &parent_iter, path);
+			parent_ptr = &parent_iter;
+			break;
+		}
+
+		/* NB: parent_ptr may still be NULL if we reached the toplevel */
+
+		/* Now walk up the desired path, adding the nodes */
+
+		for (; try_depth < path_depth - 1; try_depth++) {
+			display_name = g_strconcat(path_segments[try_depth], NET_HIERARCHY_SEPARATOR, NULL);
+			gtk_tree_store_append(store, &new_iter, parent_ptr);
+			gtk_tree_store_set(store, &new_iter, NET_ENABLED_COLUMN, "", NET_NAME_COLUMN, display_name, NET_LIBRARY_COLUMN, NULL, -1);
+			g_free(display_name);
+
+			path = gtk_tree_model_get_path(model, &new_iter);
+			row_ref = gtk_tree_row_reference_new(model, path);
+			parent_iter = new_iter;
+			parent_ptr = &parent_iter;
+
+			join_array = g_new0(char *, try_depth + 2);
+			memcpy(join_array, path_segments, sizeof(char *) * (try_depth + 1));
+
+			hash_string = g_strjoinv(NET_HIERARCHY_SEPARATOR, join_array);
+			g_free(join_array);
+
+			/* Insert those node in the hash table */
+			g_hash_table_insert(prefix_hash, hash_string, row_ref);
+			/* Don't free hash_string, it is now oened by the hash table */
+		}
+
+		gtk_tree_store_append(store, &new_iter, parent_ptr);
+		gtk_tree_store_set(store, &new_iter,
+											 NET_ENABLED_COLUMN, menu->flag ? "" : "*",
+											 NET_NAME_COLUMN, path_segments[path_depth - 1], NET_LIBRARY_COLUMN, menu, -1);
+		g_strfreev(path_segments);
+	}
+	END_LOOP;
+
+	g_hash_table_destroy(prefix_hash);
+
+	return model;
+}
+
+
+/* Called when the user double clicks on a net in the left treeview.
+ */
+static void net_selection_double_click_cb(GtkTreeView * treeview, GtkTreePath * path, GtkTreeViewColumn * col, gpointer data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gchar *str;
+	LibraryMenuType *menu;
+
+	model = gtk_tree_view_get_model(treeview);
+	if (gtk_tree_model_get_iter(model, &iter, path)) {
+
+		/* Expand / contract nodes with children */
+		if (gtk_tree_model_iter_has_child(model, &iter)) {
+			if (gtk_tree_view_row_expanded(treeview, path))
+				gtk_tree_view_collapse_row(treeview, path);
+			else
+				gtk_tree_view_expand_row(treeview, path, FALSE);
+			return;
+		}
+
+		/* Get the current enabled string and toggle it between "" and "*"
+		 */
+		gtk_tree_model_get(model, &iter, NET_ENABLED_COLUMN, &str, -1);
+		gtk_tree_store_set(GTK_TREE_STORE(model), &iter, NET_ENABLED_COLUMN, !strcmp(str, "*") ? "" : "*", -1);
+		/* set/clear the flag which says the net is enabled or disabled */
+		gtk_tree_model_get(model, &iter, NET_LIBRARY_COLUMN, &menu, -1);
+		menu->flag = strcmp(str, "*") == 0 ? 1 : 0;
+		g_free(str);
+	}
+}
+
+/* Called when the user clicks on a net in the left treeview.
+ */
+static void net_selection_changed_cb(GtkTreeSelection * selection, gpointer data)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	LibraryMenuType *net;
+
+	if (selection_holdoff)				/* PCB is highlighting, user is not selecting */
+		return;
+
+	if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
+		selected_net = NULL;
+
+		return;
+	}
+
+	/* Get a pointer, net, to the LibraryMenuType of the newly selected
+	   |  netlist row, and create a new node model from the net entries
+	   |  and insert that model into the node view.  Delete old entry model.
+	 */
+	gtk_tree_model_get(model, &iter, NET_LIBRARY_COLUMN, &net, -1);
+	node_model_update(net);
+
+	selected_net = net;
+}
+
+static void netlist_disable_all_cb(GtkToggleButton * button, gpointer data)
+{
+	GtkTreeIter iter;
+	gboolean active = gtk_toggle_button_get_active(button);
+	LibraryMenuType *menu;
+
+	/* Get each net iter and change the NET_ENABLED_COLUMN to a "*" or ""
+	   |  to flag it as disabled or enabled based on toggle button state.
+	 */
+	if (gtk_tree_model_get_iter_first(net_model, &iter))
+		do {
+			gtk_tree_store_set(GTK_TREE_STORE(net_model), &iter, NET_ENABLED_COLUMN, active ? "*" : "", -1);
+			/* set/clear the flag which says the net is enabled or disabled */
+			gtk_tree_model_get(net_model, &iter, NET_LIBRARY_COLUMN, &menu, -1);
+			menu->flag = active ? 0 : 1;
+		}
+		while (gtk_tree_model_iter_next(net_model, &iter));
+}
+
+/* Select on the layout the current net treeview selection
+ */
+static void netlist_select_cb(GtkWidget * widget, gpointer data)
+{
+	LibraryEntryType *entry;
+	ConnectionType conn;
+	gint i;
+	gboolean select_flag = GPOINTER_TO_INT(data);
+
+	if (!selected_net)
+		return;
+	if (selected_net == node_selected_net)
+		node_selected_net = NULL;
+
+	InitConnectionLookup();
+	ResetConnections(pcb_true);
+
+	for (i = selected_net->EntryN, entry = selected_net->Entry; i; i--, entry++)
+		if (SeekPad(entry, &conn, pcb_false))
+			RatFindHook(conn.type, conn.ptr1, conn.ptr2, conn.ptr2, pcb_true, pcb_true);
+
+	SelectConnection(select_flag);
+	ResetConnections(pcb_false);
+	FreeConnectionLookupMemory();
+	IncrementUndoSerialNumber();
+	Draw();
+}
+
+static void netlist_find_cb(GtkWidget * widget, gpointer data)
+{
+	char *name = NULL;
+
+	if (!selected_net)
+		return;
+
+	name = selected_net->Name + 2;
+	hid_actionl("connection", "reset", NULL);
+	hid_actionl("netlist", "find", name, NULL);
+}
+
+static void netlist_rip_up_cb(GtkWidget * widget, gpointer data)
+{
+
+	if (!selected_net)
+		return;
+	netlist_find_cb(widget, data);
+
+	VISIBLELINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, line) && !TEST_FLAG(PCB_FLAG_LOCK, line))
+			RemoveObject(PCB_TYPE_LINE, layer, line, line);
+	}
+	ENDALL_LOOP;
+
+	VISIBLEARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, arc) && !TEST_FLAG(PCB_FLAG_LOCK, arc))
+			RemoveObject(PCB_TYPE_ARC, layer, arc, arc);
+	}
+	ENDALL_LOOP;
+
+	if (PCB->ViaOn)
+		VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, via) && !TEST_FLAG(PCB_FLAG_LOCK, via))
+			RemoveObject(PCB_TYPE_VIA, via, via, via);
+	}
+	END_LOOP;
+
+}
+
+ /**/ typedef struct {
+	LibraryEntryType *ret_val;
+	LibraryMenuType *node_net;
+	const gchar *node_name;
+	pcb_bool found;
+} node_get_node_from_name_state;
+
+static gboolean node_get_node_from_name_helper(GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data)
+{
+	LibraryMenuType *net;
+	LibraryEntryType *node;
+	node_get_node_from_name_state *state = data;
+
+	gtk_tree_model_get(net_model, iter, NET_LIBRARY_COLUMN, &net, -1);
+	/* Ignore non-nets (category headers) */
+	if (net == NULL)
+		return FALSE;
+
+	/* Look for the node name in this net. */
+	for (node = net->Entry; node - net->Entry < net->EntryN; node++)
+		if (node->ListEntry && !strcmp(state->node_name, node->ListEntry)) {
+			state->node_net = net;
+			state->ret_val = node;
+			/* stop iterating */
+			state->found = TRUE;
+			return TRUE;
+		}
+	return FALSE;
+}
+
+LibraryEntryType *node_get_node_from_name(gchar * node_name, LibraryMenuType ** node_net)
+{
+	node_get_node_from_name_state state;
+
+	if (!node_name)
+		return NULL;
+
+	/* Have to force the netlist window created because we need the treeview
+	   |  models constructed to do the search.
+	 */
+	ghid_netlist_window_create(gport);
+
+	/* Now walk through node entries of each net in the net model looking for
+	   |  the node_name.
+	 */
+	state.found = 0;
+	state.node_name = node_name;
+	gtk_tree_model_foreach(net_model, node_get_node_from_name_helper, &state);
+	if (state.found) {
+		if (node_net)
+			*node_net = state.node_net;
+		return state.ret_val;
+	}
+	return NULL;
+}
+
+ /**/
+/* ---------- Manage the GUI treeview of the data models -----------
+ */
+static gint netlist_window_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, gpointer data)
+{
+	wplc_config_event(widget, &hid_gtk_wgeo.netlist_x, &hid_gtk_wgeo.netlist_y, &hid_gtk_wgeo.netlist_width, &hid_gtk_wgeo.netlist_height);
+	return FALSE;
+}
+
+static void netlist_close_cb(GtkWidget * widget, gpointer data)
+{
+	gtk_widget_destroy(netlist_window);
+	selected_net = NULL;
+	netlist_window = NULL;
+
+	/* For now, we are the only consumer of this API, so we can just do this */
+	ghid_cancel_lead_user();
+}
+
+
+static void netlist_destroy_cb(GtkWidget * widget, GHidPort * out)
+{
+	selected_net = NULL;
+	netlist_window = NULL;
+}
+
+void ghid_netlist_window_create(GHidPort * out)
+{
+	GtkWidget *vbox, *hbox, *button, *label, *sep;
+	GtkTreeView *treeview;
+	GtkTreeModel *model;
+	GtkTreeSelection *selection;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+
+	/* No point in putting up the window if no netlist is loaded.
+	 */
+	if (!PCB->NetlistLib[NETLIST_EDITED].MenuN)
+		return;
+
+	if (netlist_window)
+		return;
+
+	netlist_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	g_signal_connect(G_OBJECT(netlist_window), "destroy", G_CALLBACK(netlist_destroy_cb), out);
+	gtk_window_set_title(GTK_WINDOW(netlist_window), _("pcb-rnd Netlist"));
+	gtk_window_set_wmclass(GTK_WINDOW(netlist_window), "PCB_Netlist", "PCB");
+	g_signal_connect(G_OBJECT(netlist_window), "configure_event", G_CALLBACK(netlist_window_configure_event_cb), NULL);
+	gtk_window_set_default_size(GTK_WINDOW(netlist_window), -1, hid_gtk_wgeo.netlist_height);
+
+	gtk_container_set_border_width(GTK_CONTAINER(netlist_window), 2);
+
+	vbox = gtk_vbox_new(FALSE, 4);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_container_add(GTK_CONTAINER(netlist_window), vbox);
+	hbox = gtk_hbox_new(FALSE, 8);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4);
+
+
+	model = net_model_create();
+	treeview = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
+	net_model = model;
+	net_treeview = treeview;
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(net_model), NET_NAME_COLUMN, GTK_SORT_ASCENDING);
+
+	gtk_tree_view_set_rules_hint(treeview, FALSE);
+	g_object_set(treeview, "enable-tree-lines", TRUE, NULL);
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(treeview, -1, _(" "), renderer, "text", NET_ENABLED_COLUMN, NULL);
+
+	renderer = gtk_cell_renderer_text_new();
+	column = gtk_tree_view_column_new_with_attributes(_("Net Name"), renderer, "text", NET_NAME_COLUMN, NULL);
+	gtk_tree_view_insert_column(treeview, column, -1);
+	gtk_tree_view_set_expander_column(treeview, column);
+
+	/* TODO: dont expand all, but record expanded states when window is
+	   |  destroyed and restore state here.
+	 */
+	gtk_tree_view_expand_all(treeview);
+
+	selection = ghid_scrolled_selection(treeview, hbox,
+																			GTK_SELECTION_SINGLE,
+																			GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, net_selection_changed_cb, NULL);
+
+	/* Connect to the double click event.
+	 */
+	g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(net_selection_double_click_cb), NULL);
+
+
+
+	/* Create the elements treeview and wait for a callback to populate it.
+	 */
+	treeview = GTK_TREE_VIEW(gtk_tree_view_new());
+	node_treeview = treeview;
+
+	gtk_tree_view_set_rules_hint(treeview, FALSE);
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(treeview, -1, _("Nodes"), renderer, "text", NODE_NAME_COLUMN, NULL);
+
+	selection = ghid_scrolled_selection(treeview, hbox,
+																			GTK_SELECTION_SINGLE,
+																			GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, node_selection_changed_cb, NULL);
+	node_selection = selection;
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	label = gtk_label_new(_("Operations on selected 'Net Name':"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 4);
+
+	button = gtk_button_new_with_label(_("Select"));
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(netlist_select_cb), GINT_TO_POINTER(1));
+
+	button = gtk_button_new_with_label(_("Unselect"));
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(netlist_select_cb), GINT_TO_POINTER(0));
+
+	button = gtk_button_new_with_label(_("Find"));
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(netlist_find_cb), GINT_TO_POINTER(0));
+
+	button = gtk_button_new_with_label(_("Rip Up"));
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(netlist_rip_up_cb), GINT_TO_POINTER(0));
+
+	ghid_check_button_connected(vbox, &disable_all_button, FALSE, TRUE, FALSE,
+															FALSE, 0, netlist_disable_all_cb, NULL, _("Disable all nets for adding rats"));
+
+	sep = gtk_hseparator_new();
+	gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 3);
+
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 4);
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(netlist_close_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	wplc_place(WPLC_NETLIST, netlist_window);
+
+	gtk_widget_realize(netlist_window);
+}
+
+void ghid_netlist_window_show(GHidPort * out, gboolean raise)
+{
+	ghid_netlist_window_create(out);
+	gtk_widget_show_all(netlist_window);
+	ghid_netlist_window_update(TRUE);
+	if (raise)
+		gtk_window_present(GTK_WINDOW(netlist_window));
+}
+
+struct ggnfnn_task {
+	gboolean enabled_only;
+	const gchar *node_name;
+	LibraryMenuType *found_net;
+	GtkTreeIter iter;
+};
+
+static gboolean hunt_named_node(GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data)
+{
+	struct ggnfnn_task *task = (struct ggnfnn_task *) data;
+	LibraryMenuType *net;
+	LibraryEntryType *node;
+	gchar *str;
+	gint j;
+	gboolean is_disabled;
+
+	/* We only want to inspect leaf nodes in the tree */
+	if (gtk_tree_model_iter_has_child(model, iter))
+		return FALSE;
+
+	gtk_tree_model_get(model, iter, NET_LIBRARY_COLUMN, &net, -1);
+	gtk_tree_model_get(model, iter, NET_ENABLED_COLUMN, &str, -1);
+	is_disabled = !strcmp(str, "*");
+	g_free(str);
+
+	/* Don't check net nodes of disabled nets. */
+	if (task->enabled_only && is_disabled)
+		return FALSE;
+
+	/* Look for the node name in this net. */
+	for (j = net->EntryN, node = net->Entry; j; j--, node++)
+		if (node->ListEntry && !strcmp(task->node_name, node->ListEntry)) {
+			task->found_net = net;
+			task->iter = *iter;
+			return TRUE;
+		}
+
+	return FALSE;
+}
+
+LibraryMenuType *ghid_get_net_from_node_name(const gchar * node_name, gboolean enabled_only)
+{
+	GtkTreePath *path;
+	struct ggnfnn_task task;
+
+	if (!node_name)
+		return NULL;
+
+	/* Have to force the netlist window created because we need the treeview
+	   |  models constructed so we can find the LibraryMenuType pointer the
+	   |  caller wants.
+	 */
+	ghid_netlist_window_create(gport);
+
+	/* If no netlist is loaded the window doesn't appear. */
+	if (netlist_window == NULL)
+		return NULL;
+
+	task.enabled_only = enabled_only;
+	task.node_name = node_name;
+	task.found_net = NULL;
+
+	/* Now walk through node entries of each net in the net model looking for
+	   |  the node_name.
+	 */
+	gtk_tree_model_foreach(net_model, hunt_named_node, &task);
+
+	/* We are asked to highlight the found net if enabled_only is TRUE.
+	   |  Set holdoff TRUE since this is just a highlight and user is not
+	   |  expecting normal select action to happen?  Or should the node
+	   |  treeview also get updated?  Original PCB code just tries to highlight.
+	 */
+	if (task.found_net && enabled_only) {
+		selection_holdoff = TRUE;
+		path = gtk_tree_model_get_path(net_model, &task.iter);
+		gtk_tree_view_scroll_to_cell(net_treeview, path, NULL, TRUE, 0.5, 0.5);
+		gtk_tree_selection_select_path(gtk_tree_view_get_selection(net_treeview), path);
+		selection_holdoff = FALSE;
+	}
+	return task.found_net;
+}
+
+/* PCB LookupConnection code in find.c calls this if it wants a node
+   |  and its net highlighted.
+*/
+void ghid_netlist_highlight_node(const gchar * node_name)
+{
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	LibraryMenuType *net;
+	gchar *name;
+
+	if (!node_name)
+		return;
+
+	if ((net = ghid_get_net_from_node_name(node_name, TRUE)) == NULL)
+		return;
+
+	/* We've found the net containing the node, so update the node treeview
+	   |  to contain the nodes from the net.  Then we have to find the node
+	   |  in the new node model so we can highlight it.
+	 */
+	node_model_update(net);
+
+	if (gtk_tree_model_get_iter_first(node_model, &iter))
+		do {
+			gtk_tree_model_get(node_model, &iter, NODE_NAME_COLUMN, &name, -1);
+
+			if (!strcmp(node_name, name)) {	/* found it, so highlight it */
+				selection_holdoff = TRUE;
+				selected_net = net;
+				path = gtk_tree_model_get_path(node_model, &iter);
+				gtk_tree_view_scroll_to_cell(node_treeview, path, NULL, TRUE, 0.5, 0.5);
+				gtk_tree_selection_select_path(gtk_tree_view_get_selection(node_treeview), path);
+				selection_holdoff = FALSE;
+			}
+			g_free(name);
+		}
+		while (gtk_tree_model_iter_next(node_model, &iter));
+}
+
+/* If code in PCB should change the netlist, call this to update
+   |  what's in the netlist window.
+*/
+void ghid_netlist_window_update(gboolean init_nodes)
+{
+	GtkTreeModel *model;
+
+	/* Make sure there is something to update */
+	ghid_netlist_window_create(gport);
+
+	model = net_model;
+	net_model = net_model_create();
+	gtk_tree_view_set_model(net_treeview, net_model);
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(net_model), NET_NAME_COLUMN, GTK_SORT_ASCENDING);
+	if (model) {
+		gtk_tree_store_clear(GTK_TREE_STORE(model));
+		g_object_unref(model);
+	}
+
+	selected_net = NULL;
+
+	/* XXX Check if the select callback does this for us */
+	if (init_nodes)
+		node_model_update((&PCB->NetlistLib[NETLIST_EDITED])->Menu);
+}
+
+static gint GhidNetlistChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	loading_new_netlist = TRUE;
+	ghid_netlist_window_update(TRUE);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(disable_all_button), FALSE);
+	loading_new_netlist = FALSE;
+	return 0;
+}
+
+static const char netlistshow_syntax[] = "NetlistShow(pinname|netname)";
+
+static const char netlistshow_help[] = "Selects the given pinname or netname in the netlist window. Does not \
+show the window if it isn't already shown.";
+
+static gint GhidNetlistShow(int argc, const char **argv, Coord x, Coord y)
+{
+	ghid_netlist_window_create(gport);
+	if (argc > 0)
+		ghid_netlist_highlight_node(argv[0]);
+	return 0;
+}
+
+static const char netlistpresent_syntax[] = "NetlistPresent()";
+
+static const char netlistpresent_help[] = "Presents the netlist window.";
+
+static gint GhidNetlistPresent(int argc, const char **argv, Coord x, Coord y)
+{
+	ghid_netlist_window_show(gport, TRUE);
+	return 0;
+}
+
+HID_Action ghid_netlist_action_list[] = {
+	{"NetlistChanged", 0, GhidNetlistChanged,
+	 netlistchanged_help, netlistchanged_syntax}
+	,
+	{"NetlistShow", 0, GhidNetlistShow,
+	 netlistshow_help, netlistshow_syntax}
+	,
+	{"NetlistPresent", 0, GhidNetlistPresent,
+	 netlistpresent_help, netlistpresent_syntax}
+	,
+};
+
+REGISTER_ACTIONS(ghid_netlist_action_list, ghid_cookie)
diff --git a/src_plugins/hid_gtk/gui-output-events.c b/src_plugins/hid_gtk/gui-output-events.c
new file mode 100644
index 0000000..26bc9cd
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-output-events.c
@@ -0,0 +1,534 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "gui.h"
+#include "gtkhid.h"
+#include "hid_cfg.h"
+
+#include <gdk/gdkkeysyms.h>
+
+#include "action_helper.h"
+#include "crosshair.h"
+#include "draw.h"
+#include "error.h"
+#include "layer.h"
+#include "misc.h"
+#include "set.h"
+#include "find.h"
+#include "search.h"
+#include "rats.h"
+
+
+#define TOOLTIP_UPDATE_DELAY 200
+
+void ghid_port_ranges_changed(void)
+{
+	GtkAdjustment *h_adj, *v_adj;
+
+	h_adj = gtk_range_get_adjustment(GTK_RANGE(ghidgui->h_range));
+	v_adj = gtk_range_get_adjustment(GTK_RANGE(ghidgui->v_range));
+	gport->view.x0 = gtk_adjustment_get_value(h_adj);
+	gport->view.y0 = gtk_adjustment_get_value(v_adj);
+
+	ghid_invalidate_all();
+}
+
+/* Do scrollbar scaling based on current port drawing area size and
+   |  overall PCB board size.
+ */
+void ghid_port_ranges_scale(void)
+{
+	GtkAdjustment *adj;
+	gdouble page_size;
+
+	/* Update the scrollbars with PCB units.  So Scale the current
+	   |  drawing area size in pixels to PCB units and that will be
+	   |  the page size for the Gtk adjustment.
+	 */
+	gport->view.width = gport->width * gport->view.coord_per_px;
+	gport->view.height = gport->height * gport->view.coord_per_px;
+
+	adj = gtk_range_get_adjustment(GTK_RANGE(ghidgui->h_range));
+	page_size = MIN(gport->view.width, PCB->MaxWidth);
+	gtk_adjustment_configure(adj, gtk_adjustment_get_value(adj),	/* value          */
+													 -gport->view.width,	/* lower          */
+													 PCB->MaxWidth + page_size,	/* upper          */
+													 page_size / 100.0,	/* step_increment */
+													 page_size / 10.0,	/* page_increment */
+													 page_size);	/* page_size      */
+
+	adj = gtk_range_get_adjustment(GTK_RANGE(ghidgui->v_range));
+	page_size = MIN(gport->view.height, PCB->MaxHeight);
+	gtk_adjustment_configure(adj, gtk_adjustment_get_value(adj),	/* value          */
+													 -gport->view.height,	/* lower          */
+													 PCB->MaxHeight + page_size,	/* upper          */
+													 page_size / 100.0,	/* step_increment */
+													 page_size / 10.0,	/* page_increment */
+													 page_size);	/* page_size      */
+}
+
+
+/* ----------------------------------------------------------------------
+ * handles all events from PCB drawing area
+ */
+
+void ghid_get_coords(const char *msg, Coord * x, Coord * y)
+{
+	if (!ghid_port.has_entered && msg)
+		ghid_get_user_xy(msg);
+	if (ghid_port.has_entered) {
+		*x = gport->pcb_x;
+		*y = gport->pcb_y;
+	}
+}
+
+void ghid_note_event_location(GdkEventButton * ev)
+{
+	gint event_x, event_y;
+
+	if (!ev) {
+		gdk_window_get_pointer(gtk_widget_get_window(ghid_port.drawing_area), &event_x, &event_y, NULL);
+	}
+	else {
+		event_x = ev->x;
+		event_y = ev->y;
+	}
+
+	ghid_event_to_pcb_coords(event_x, event_y, &gport->pcb_x, &gport->pcb_y);
+
+	EventMoveCrosshair(gport->pcb_x, gport->pcb_y);
+	ghid_set_cursor_position_labels();
+}
+
+static gboolean ghid_idle_cb(gpointer data)
+{
+	if (conf_core.editor.mode == PCB_MODE_NO)
+		SetMode(PCB_MODE_ARROW);
+	ghid_mode_cursor(conf_core.editor.mode);
+	if (ghidgui->settings_mode != conf_core.editor.mode) {
+		ghid_mode_buttons_update();
+	}
+	ghidgui->settings_mode = conf_core.editor.mode;
+	return FALSE;
+}
+
+gboolean ghid_port_key_release_cb(GtkWidget * drawing_area, GdkEventKey * kev, gpointer data)
+{
+	gint ksym = kev->keyval;
+
+	if (ghid_is_modifier_key_sym(ksym))
+		ghid_note_event_location(NULL);
+
+	AdjustAttachedObjects();
+	ghid_invalidate_all();
+	g_idle_add(ghid_idle_cb, NULL);
+	return FALSE;
+}
+
+/* Handle user keys in the output drawing area. */
+gboolean ghid_port_key_press_cb(GtkWidget * drawing_area, GdkEventKey * kev, gpointer data)
+{
+	if (ghid_is_modifier_key_sym(kev->keyval))
+		return FALSE;
+
+	if (kev->keyval <= 0xffff) {
+		GdkModifierType state = (GdkModifierType) (kev->state);
+		int slen, mods = 0;
+		static hid_cfg_keyseq_t *seq[32];
+		static int seq_len = 0;
+		unsigned short int kv = kev->keyval;
+
+		ghid_note_event_location(NULL);
+
+		extern GdkModifierType ghid_glob_mask;
+		ghid_glob_mask = state;
+
+		if (state & GDK_MOD1_MASK)    mods |= M_Alt;
+		if (state & GDK_CONTROL_MASK) mods |= M_Ctrl;
+		if (state & GDK_SHIFT_MASK) {
+/* TODO#3: this works only on US keyboard */
+			static const char *ignore_shift = "~!@#$%^&*()_+{}|:\"<>?";
+			if ((kv < 32) || (kv > 126) || (strchr(ignore_shift, kv) == NULL)) {
+				mods |= M_Shift;
+				if ((kv >= 'A') && (kv <= 'Z'))
+					kv = tolower(kv);
+			}
+		}
+
+		if (kv == GDK_KEY_ISO_Left_Tab) kv = GDK_KEY_Tab;
+		slen = hid_cfg_keys_input(&ghid_keymap, mods, kv, seq, &seq_len);
+		if (slen > 0) {
+			ghid_port.has_entered  = 1;
+			hid_cfg_keys_action(seq, slen);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static hid_cfg_mod_t ghid_mouse_button(int ev_button)
+{
+	/* GDK numbers buttons from 1..5, there seem to be no symbolic names */
+	return (MB_LEFT << (ev_button-1));
+}
+
+gboolean ghid_port_button_press_cb(GtkWidget * drawing_area, GdkEventButton * ev, gpointer data)
+{
+	ModifierKeysState mk;
+	GdkModifierType state;
+
+GHidPort *out = &ghid_port;
+GdkModifierType mask;
+
+	/* Reject double and triple click events */
+	if (ev->type != GDK_BUTTON_PRESS)
+		return TRUE;
+
+	ghid_note_event_location(ev);
+	state = (GdkModifierType) (ev->state);
+	mk = ghid_modifier_keys_state(&state);
+
+	extern GdkModifierType ghid_glob_mask;
+	ghid_glob_mask = state;
+
+	gdk_window_get_pointer(gtk_widget_get_window(out->drawing_area), NULL, NULL, &mask);
+
+	hid_cfg_mouse_action(&ghid_mouse, ghid_mouse_button(ev->button) | mk);
+
+	ghid_invalidate_all();
+	ghid_window_set_name_label(PCB->Name);
+	ghid_set_status_line_label();
+	if (!gport->panning)
+		g_idle_add(ghid_idle_cb, NULL);
+	return TRUE;
+}
+
+
+gboolean ghid_port_button_release_cb(GtkWidget * drawing_area, GdkEventButton * ev, gpointer data)
+{
+	ModifierKeysState mk;
+	GdkModifierType state;
+
+	ghid_note_event_location(ev);
+	state = (GdkModifierType) (ev->state);
+	mk = ghid_modifier_keys_state(&state);
+
+	hid_cfg_mouse_action(&ghid_mouse, ghid_mouse_button(ev->button) | mk | M_Release);
+
+	AdjustAttachedObjects();
+	ghid_invalidate_all();
+
+	ghid_window_set_name_label(PCB->Name);
+	ghid_set_status_line_label();
+	g_idle_add(ghid_idle_cb, NULL);
+	return TRUE;
+}
+
+
+gboolean ghid_port_drawing_area_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, GHidPort * out)
+{
+	static gboolean first_time_done;
+
+	gport->width = ev->width;
+	gport->height = ev->height;
+
+	if (gport->pixmap)
+		gdk_pixmap_unref(gport->pixmap);
+
+	gport->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget), gport->width, gport->height, -1);
+	gport->drawable = gport->pixmap;
+
+	if (!first_time_done) {
+		gport->colormap = gtk_widget_get_colormap(gport->top_window);
+		if (gdk_color_parse(conf_core.appearance.color.background, &gport->bg_color))
+			gdk_color_alloc(gport->colormap, &gport->bg_color);
+		else
+			gdk_color_white(gport->colormap, &gport->bg_color);
+
+		if (gdk_color_parse(conf_core.appearance.color.off_limit, &gport->offlimits_color))
+			gdk_color_alloc(gport->colormap, &gport->offlimits_color);
+		else
+			gdk_color_white(gport->colormap, &gport->offlimits_color);
+		first_time_done = TRUE;
+		ghid_drawing_area_configure_hook(out);
+		PCBChanged(0, NULL, 0, 0);
+	}
+	else {
+		ghid_drawing_area_configure_hook(out);
+	}
+
+	ghid_port_ranges_scale();
+	ghid_invalidate_all();
+	return 0;
+}
+
+
+static char *describe_location(Coord X, Coord Y)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	int Range = 0;
+	const char *elename = "";
+	char *pinname;
+	char *netname = NULL;
+	char *description;
+
+	/* check if there are any pins or pads at that position */
+
+	type = SearchObjectByLocation(PCB_TYPE_PIN | PCB_TYPE_PAD, &ptr1, &ptr2, &ptr3, X, Y, Range);
+	if (type == PCB_TYPE_NONE)
+		return NULL;
+
+	/* don't mess with silk objects! */
+	if (type & SILK_TYPE && GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1) >= max_copper_layer)
+		return NULL;
+
+	if (type == PCB_TYPE_PIN || type == PCB_TYPE_PAD)
+		elename = (char *) UNKNOWN(NAMEONPCB_NAME((ElementTypePtr) ptr1));
+
+	pinname = ConnectionName(type, ptr1, ptr2);
+
+	if (pinname == NULL)
+		return NULL;
+
+	/* Find netlist entry */
+	MENU_LOOP(&PCB->NetlistLib[NETLIST_EDITED]);
+	{
+		if (!menu->Name)
+			continue;
+
+		ENTRY_LOOP(menu);
+		{
+			if (!entry->ListEntry)
+				continue;
+
+			if (strcmp(entry->ListEntry, pinname) == 0) {
+				netname = g_strdup(menu->Name);
+				/* For some reason, the netname has spaces in front of it, strip them */
+				g_strstrip(netname);
+				break;
+			}
+		}
+		END_LOOP;
+
+		if (netname != NULL)
+			break;
+	}
+	END_LOOP;
+
+	description = g_strdup_printf("Element name: %s\n"
+																"Pinname : %s\n"
+																"Netname : %s",
+																elename, (pinname != NULL) ? pinname : "--", (netname != NULL) ? netname : "--");
+
+	g_free(netname);
+
+	return description;
+}
+
+static int tooltip_update_timeout_id = 0;
+static gboolean check_object_tooltips(GHidPort * out)
+{
+	char *description;
+
+	/* Make sure the timer is not removed - we are called by the timer and it is
+	   automatically removed because we are returning false */
+	tooltip_update_timeout_id = 0;
+
+	/* check if there are any pins or pads at that position */
+	description = describe_location(out->crosshair_x, out->crosshair_y);
+
+	if (description == NULL)
+		return FALSE;
+
+	gtk_widget_set_tooltip_text(out->drawing_area, description);
+	g_free(description);
+
+	return FALSE;
+}
+
+static void cancel_tooltip_update()
+{
+	if (tooltip_update_timeout_id)
+		g_source_remove(tooltip_update_timeout_id);
+	tooltip_update_timeout_id = 0;
+}
+
+/* FIXME: If the GHidPort is ever destroyed, we must call
+ * cancel_tooltip_update (), otherwise the timeout might
+ * fire after the data it utilises has been free'd.
+ */
+static void queue_tooltip_update(GHidPort * out)
+{
+	/* Zap the old tool-tip text and force it to be removed from the screen */
+	gtk_widget_set_tooltip_text(out->drawing_area, NULL);
+	gtk_widget_trigger_tooltip_query(out->drawing_area);
+
+	cancel_tooltip_update();
+
+	tooltip_update_timeout_id = g_timeout_add(TOOLTIP_UPDATE_DELAY, (GSourceFunc) check_object_tooltips, out);
+}
+
+gint ghid_port_window_motion_cb(GtkWidget * widget, GdkEventMotion * ev, GHidPort * out)
+{
+	gdouble dx, dy;
+	static gint x_prev = -1, y_prev = -1;
+
+	gdk_event_request_motions(ev);
+
+	if (out->panning) {
+		dx = gport->view.coord_per_px * (x_prev - ev->x);
+		dy = gport->view.coord_per_px * (y_prev - ev->y);
+		if (x_prev > 0)
+			ghid_pan_view_rel(dx, dy);
+		x_prev = ev->x;
+		y_prev = ev->y;
+		return FALSE;
+	}
+	x_prev = y_prev = -1;
+	ghid_note_event_location((GdkEventButton *) ev);
+
+	queue_tooltip_update(out);
+
+	return FALSE;
+}
+
+gint ghid_port_window_enter_cb(GtkWidget * widget, GdkEventCrossing * ev, GHidPort * out)
+{
+	/* printf("enter: mode: %d detail: %d\n", ev->mode, ev->detail); */
+
+	/* See comment in ghid_port_window_leave_cb() */
+
+	if (ev->mode != GDK_CROSSING_NORMAL && ev->detail != GDK_NOTIFY_NONLINEAR) {
+		return FALSE;
+	}
+
+	if (!ghidgui->command_entry_status_line_active) {
+		out->has_entered = TRUE;
+		/* Make sure drawing area has keyboard focus when we are in it.
+		 */
+		gtk_widget_grab_focus(out->drawing_area);
+	}
+	ghidgui->in_popup = FALSE;
+
+	/* Following expression is true if a you open a menu from the menu bar,
+	 * move the mouse to the viewport and click on it. This closes the menu
+	 * and moves the pointer to the viewport without the pointer going over
+	 * the edge of the viewport */
+	if (ev->mode == GDK_CROSSING_UNGRAB && ev->detail == GDK_NOTIFY_NONLINEAR) {
+		ghid_screen_update();
+	}
+	return FALSE;
+}
+
+gint ghid_port_window_leave_cb(GtkWidget * widget, GdkEventCrossing * ev, GHidPort * out)
+{
+	/* printf("leave mode: %d detail: %d\n", ev->mode, ev->detail); */
+
+	/* Window leave events can also be triggered because of focus grabs. Some
+	 * X applications occasionally grab the focus and so trigger this function.
+	 * At least GNOME's window manager is known to do this on every mouse click.
+	 *
+	 * See http://bugzilla.gnome.org/show_bug.cgi?id=102209
+	 */
+
+	if (ev->mode != GDK_CROSSING_NORMAL) {
+		return FALSE;
+	}
+
+	out->has_entered = FALSE;
+
+	ghid_screen_update();
+
+	return FALSE;
+}
+
+
+	/* Mouse scroll wheel events
+	 */
+gint ghid_port_window_mouse_scroll_cb(GtkWidget * widget, GdkEventScroll * ev, GHidPort * out)
+{
+	ModifierKeysState mk;
+	GdkModifierType state;
+	int button;
+
+	state = (GdkModifierType) (ev->state);
+	mk = ghid_modifier_keys_state(&state);
+
+	/* X11 gtk hard codes buttons 4, 5, 6, 7 as below in
+	 * gtk+/gdk/x11/gdkevents-x11.c:1121, but quartz and windows have
+	 * special mouse scroll events, so this may conflict with a mouse
+	 * who has buttons 4 - 7 that aren't the scroll wheel?
+	 */
+	switch (ev->direction) {
+		case GDK_SCROLL_UP:    button = MB_SCROLL_UP; break;
+		case GDK_SCROLL_DOWN:  button = MB_SCROLL_DOWN; break;
+		case GDK_SCROLL_LEFT:  button = MB_SCROLL_LEFT; break;
+		case GDK_SCROLL_RIGHT: button = MB_SCROLL_RIGHT; break;
+		default: return FALSE;
+	}
+
+	ghid_wheel_zoom = 1;
+	hid_cfg_mouse_action(&ghid_mouse, button | mk);
+	ghid_wheel_zoom = 0;
+
+	return TRUE;
+}
+
+void ghid_confchg_line_refraction(conf_native_t *cfg)
+{
+	/* test if PCB struct doesn't exist at startup */
+	if (!PCB)
+		return;
+	ghid_set_status_line_label();
+}
+
+void ghid_confchg_all_direction_lines(conf_native_t *cfg)
+{
+	/* test if PCB struct doesn't exist at startup */
+	if (!PCB)
+		return;
+	ghid_set_status_line_label();
+}
+
+void ghid_confchg_fullscreen(conf_native_t *cfg)
+{
+	if (gtkhid_active)
+		ghid_fullscreen_apply();
+}
+
+
+void ghid_confchg_checkbox(conf_native_t *cfg)
+{
+	if (gtkhid_active)
+		ghid_update_toggle_flags();
+}
diff --git a/src_plugins/hid_gtk/gui-pinout-preview.c b/src_plugins/hid_gtk/gui-pinout-preview.c
new file mode 100644
index 0000000..1142a84
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-pinout-preview.c
@@ -0,0 +1,297 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file copied and modified by Peter Clifton, starting from
+ * gui-pinout-window.c, written by Bill Wilson for the PCB Gtk port */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+
+#include "gui.h"
+
+#include "copy.h"
+#include "data.h"
+#include "draw.h"
+#include "mymem.h"
+#include "move.h"
+#include "rotate.h"
+#include "gui-pinout-preview.h"
+
+/* Just define a sensible scale, lets say (for example), 100 pixel per 150 mil */
+#define SENSIBLE_VIEW_SCALE  (100. / PCB_MIL_TO_COORD (150.))
+static void pinout_set_view(GhidPinoutPreview * pinout)
+{
+	float scale = SENSIBLE_VIEW_SCALE;
+
+	pinout->x_max = pinout->element.BoundingBox.X2 + conf_core.appearance.pinout.offset_x;
+	pinout->y_max = pinout->element.BoundingBox.Y2 + conf_core.appearance.pinout.offset_y;
+	pinout->w_pixels = scale * (pinout->element.BoundingBox.X2 - pinout->element.BoundingBox.X1);
+	pinout->h_pixels = scale * (pinout->element.BoundingBox.Y2 - pinout->element.BoundingBox.Y1);
+}
+
+
+static void pinout_set_data(GhidPinoutPreview * pinout, ElementType * element)
+{
+	if (element == NULL) {
+		FreeElementMemory(&pinout->element);
+		pinout->w_pixels = 0;
+		pinout->h_pixels = 0;
+		return;
+	}
+
+	/* 
+	 * copy element data 
+	 * enable output of pin and padnames
+	 * move element to a 5% offset from zero position
+	 * set all package lines/arcs to zero width
+	 */
+	CopyElementLowLevel(NULL, &pinout->element, element, FALSE, 0, 0);
+	PIN_LOOP(&pinout->element);
+	{
+		SET_FLAG(PCB_FLAG_DISPLAYNAME, pin);
+	}
+	END_LOOP;
+
+	PAD_LOOP(&pinout->element);
+	{
+		SET_FLAG(PCB_FLAG_DISPLAYNAME, pad);
+	}
+	END_LOOP;
+
+
+	MoveElementLowLevel(NULL, &pinout->element,
+											conf_core.appearance.pinout.offset_x -
+											pinout->element.BoundingBox.X1, conf_core.appearance.pinout.offset_y - pinout->element.BoundingBox.Y1);
+
+	pinout_set_view(pinout);
+
+	ELEMENTLINE_LOOP(&pinout->element);
+	{
+		line->Thickness = 0;
+	}
+	END_LOOP;
+
+	ARC_LOOP(&pinout->element);
+	{
+		/* 
+		 * for whatever reason setting a thickness of 0 causes the arcs to
+		 * not display so pick 1 which does display but is still quite
+		 * thin.
+		 */
+		arc->Thickness = 1;
+	}
+	END_LOOP;
+}
+
+
+enum {
+	PROP_ELEMENT_DATA = 1,
+};
+
+
+static GObjectClass *ghid_pinout_preview_parent_class = NULL;
+
+
+/*! \brief GObject constructed
+ *
+ *  \par Function Description
+ *  Initialise the pinout preview object once it is constructed.
+ *  Chain up in case the parent class wants to do anything too.
+ *
+ *  \param [in] object  The pinout preview object
+ */
+static void ghid_pinout_preview_constructed(GObject * object)
+{
+	/* chain up to the parent class */
+	if (G_OBJECT_CLASS(ghid_pinout_preview_parent_class)->constructed != NULL)
+		G_OBJECT_CLASS(ghid_pinout_preview_parent_class)->constructed(object);
+
+	ghid_init_drawing_widget(GTK_WIDGET(object), gport);
+}
+
+
+
+/*! \brief GObject finalise handler
+ *
+ *  \par Function Description
+ *  Just before the GhidPinoutPreview GObject is finalized, free our
+ *  allocated data, and then chain up to the parent's finalize handler.
+ *
+ *  \param [in] widget  The GObject being finalized.
+ */
+static void ghid_pinout_preview_finalize(GObject * object)
+{
+	GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW(object);
+
+	/* Passing NULL for element data will free the old memory */
+	pinout_set_data(pinout, NULL);
+
+	G_OBJECT_CLASS(ghid_pinout_preview_parent_class)->finalize(object);
+}
+
+
+/*! \brief GObject property setter function
+ *
+ *  \par Function Description
+ *  Setter function for GhidPinoutPreview's GObject properties,
+ *  "settings-name" and "toplevel".
+ *
+ *  \param [in]  object       The GObject whose properties we are setting
+ *  \param [in]  property_id  The numeric id. under which the property was
+ *                            registered with g_object_class_install_property()
+ *  \param [in]  value        The GValue the property is being set from
+ *  \param [in]  pspec        A GParamSpec describing the property being set
+ */
+static void ghid_pinout_preview_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
+{
+	GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW(object);
+	GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(pinout));
+
+	switch (property_id) {
+	case PROP_ELEMENT_DATA:
+		pinout_set_data(pinout, (ElementType *) g_value_get_pointer(value));
+		if (window != NULL)
+			gdk_window_invalidate_rect(window, NULL, FALSE);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+	}
+
+}
+
+
+/*! \brief GObject property getter function
+ *
+ *  \par Function Description
+ *  Getter function for GhidPinoutPreview's GObject properties,
+ *  "settings-name" and "toplevel".
+ *
+ *  \param [in]  object       The GObject whose properties we are getting
+ *  \param [in]  property_id  The numeric id. under which the property was
+ *                            registered with g_object_class_install_property()
+ *  \param [out] value        The GValue in which to return the value of the property
+ *  \param [in]  pspec        A GParamSpec describing the property being got
+ */
+static void ghid_pinout_preview_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
+{
+	switch (property_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+	}
+
+}
+
+
+/*! \brief GType class initialiser for GhidPinoutPreview
+ *
+ *  \par Function Description
+ *  GType class initialiser for GhidPinoutPreview. We override our parent
+ *  virtual class methods as needed and register our GObject properties.
+ *
+ *  \param [in]  klass       The GhidPinoutPreviewClass we are initialising
+ */
+static void ghid_pinout_preview_class_init(GhidPinoutPreviewClass * klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS(klass);
+
+	gobject_class->finalize = ghid_pinout_preview_finalize;
+	gobject_class->set_property = ghid_pinout_preview_set_property;
+	gobject_class->get_property = ghid_pinout_preview_get_property;
+	gobject_class->constructed = ghid_pinout_preview_constructed;
+
+	gtk_widget_class->expose_event = ghid_pinout_preview_expose;
+
+	ghid_pinout_preview_parent_class = (GObjectClass *) g_type_class_peek_parent(klass);
+
+	g_object_class_install_property(gobject_class, PROP_ELEMENT_DATA,
+																	g_param_spec_pointer("element-data", "", "", G_PARAM_WRITABLE));
+}
+
+
+/*! \brief Function to retrieve GhidPinoutPreview's GType identifier.
+ *
+ *  \par Function Description
+ *  Function to retrieve GhidPinoutPreview's GType identifier.
+ *  Upon first call, this registers the GhidPinoutPreview in the GType system.
+ *  Subsequently it returns the saved value from its first execution.
+ *
+ *  \return the GType identifier associated with GhidPinoutPreview.
+ */
+GType ghid_pinout_preview_get_type()
+{
+	static GType ghid_pinout_preview_type = 0;
+
+	if (!ghid_pinout_preview_type) {
+		static const GTypeInfo ghid_pinout_preview_info = {
+			sizeof(GhidPinoutPreviewClass),
+			NULL,											/* base_init */
+			NULL,											/* base_finalize */
+			(GClassInitFunc) ghid_pinout_preview_class_init,
+			NULL,											/* class_finalize */
+			NULL,											/* class_data */
+			sizeof(GhidPinoutPreview),
+			0,												/* n_preallocs */
+			NULL,											/* instance_init */
+		};
+
+		ghid_pinout_preview_type =
+			g_type_register_static(GTK_TYPE_DRAWING_AREA, "GhidPinoutPreview", &ghid_pinout_preview_info, (GTypeFlags) 0);
+	}
+
+	return ghid_pinout_preview_type;
+}
+
+
+/*! \brief Convenience function to create a new pinout preview
+ *
+ *  \par Function Description
+ *  Convenience function which creates a GhidPinoutPreview.
+ *
+ *  \return  The GhidPinoutPreview created.
+ */
+GtkWidget *ghid_pinout_preview_new(ElementType * element)
+{
+	GhidPinoutPreview *pinout_preview;
+
+	pinout_preview = (GhidPinoutPreview *) g_object_new(GHID_TYPE_PINOUT_PREVIEW, "element-data", element, NULL);
+
+	return GTK_WIDGET(pinout_preview);
+}
+
+
+/*! \brief Query the natural size of a pinout preview
+ *
+ *  \par Function Description
+ *  Convenience function to query the natural size of a pinout preview
+ */
+void ghid_pinout_preview_get_natural_size(GhidPinoutPreview * pinout, int *width, int *height)
+{
+	*width = pinout->w_pixels;
+	*height = pinout->h_pixels;
+}
diff --git a/src_plugins/hid_gtk/gui-pinout-preview.h b/src_plugins/hid_gtk/gui-pinout-preview.h
new file mode 100644
index 0000000..9d18d35
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-pinout-preview.h
@@ -0,0 +1,61 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Peter Clifton */
+
+#ifndef PCB_HID_GTK_GUI_PINOUT_PREVIEW_H
+#define PCB_HID_GTK_GUI_PINOUT_PREVIEW_H
+
+
+#define GHID_TYPE_PINOUT_PREVIEW           (ghid_pinout_preview_get_type())
+#define GHID_PINOUT_PREVIEW(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_TYPE_PINOUT_PREVIEW, GhidPinoutPreview))
+#define GHID_PINOUT_PREVIEW_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass),  GHID_TYPE_PINOUT_PREVIEW, GhidPinoutPreviewClass))
+#define GHID_IS_PINOUT_PREVIEW(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_TYPE_PINOUT_PREVIEW))
+#define GHID_PINOUT_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),  GHID_TYPE_PINOUT_PREVIEW, GhidPinoutPreviewClass))
+
+typedef struct _GhidPinoutPreviewClass GhidPinoutPreviewClass;
+typedef struct _GhidPinoutPreview GhidPinoutPreview;
+
+
+struct _GhidPinoutPreviewClass {
+	GtkDrawingAreaClass parent_class;
+};
+
+struct _GhidPinoutPreview {
+	GtkDrawingArea parent_instance;
+
+	ElementType element;					/* element data to display */
+	gint x_max, y_max;
+	gint w_pixels, h_pixels;			/* natural size of element preview */
+};
+
+
+GType ghid_pinout_preview_get_type(void);
+
+GtkWidget *ghid_pinout_preview_new(ElementType * element);
+void ghid_pinout_preview_get_natural_size(GhidPinoutPreview * pinout, int *width, int *height);
+
+#endif /* PCB_HID_GTK_GUI_PINOUT_PREVIEW_H */
diff --git a/src_plugins/hid_gtk/gui-pinout-window.c b/src_plugins/hid_gtk/gui-pinout-window.c
new file mode 100644
index 0000000..9d13511
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-pinout-window.c
@@ -0,0 +1,90 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* This file written by Bill Wilson for the PCB Gtk port */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+
+#include "gui.h"
+#include "win_place.h"
+
+#include "copy.h"
+#include "data.h"
+#include "draw.h"
+#include "mymem.h"
+#include "move.h"
+#include "rotate.h"
+
+#include "gui-pinout-preview.h"
+
+static void pinout_close_cb(GtkWidget * widget, GtkWidget * top_window)
+{
+	gtk_widget_destroy(top_window);
+}
+
+
+void ghid_pinout_window_show(GHidPort * out, ElementType * element)
+{
+	GtkWidget *button, *vbox, *hbox, *preview, *top_window;
+	gchar *title;
+	int width, height;
+
+	if (!element)
+		return;
+	title = g_strdup_printf("%s [%s,%s]",
+													UNKNOWN(DESCRIPTION_NAME(element)), UNKNOWN(NAMEONPCB_NAME(element)), UNKNOWN(VALUE_NAME(element)));
+
+	top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(top_window), title);
+	g_free(title);
+	gtk_window_set_wmclass(GTK_WINDOW(top_window), "PCB_Pinout", "PCB");
+	gtk_container_set_border_width(GTK_CONTAINER(top_window), 4);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(top_window), vbox);
+
+
+	preview = ghid_pinout_preview_new(element);
+	gtk_box_pack_start(GTK_BOX(vbox), preview, TRUE, TRUE, 0);
+
+	ghid_pinout_preview_get_natural_size(GHID_PINOUT_PREVIEW(preview), &width, &height);
+
+	gtk_window_set_default_size(GTK_WINDOW(top_window), width + 50, height + 50);
+
+	hbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pinout_close_cb), top_window);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+	gtk_widget_realize(top_window);
+	wplc_place(WPLC_PINOUT, top_window);
+	gtk_widget_show_all(top_window);
+}
diff --git a/src_plugins/hid_gtk/gui-top-window.c b/src_plugins/hid_gtk/gui-top-window.c
new file mode 100644
index 0000000..6827ff0
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-top-window.c
@@ -0,0 +1,1766 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* #define DEBUG_MENUS */
+
+#ifdef DAN_FIXME
+TODO:
+
+-what about stuff like this:
+
+	/* Set to ! because ActionDisplay toggles it */
+conf_core.editor.draw_grid = !gtk_toggle_action_get_active(action);
+hid_actionl("Display", "Grid", "", NULL);
+ghid_set_status_line_label();
+
+
+I NEED TO DO THE STATUS LINE THING.for example shift - alt - v to change the
+	via size.NOte the status line label does not get updated properly until a zoom in / out.
+#endif
+/* This file was originally written by Bill Wilson for the PCB Gtk
+ * port.  It was later heavily modified by Dan McMahill to provide
+ * user customized menus.
+*/
+/* gui-top-window.c
+|  This handles creation of the top level window and all its widgets.
+|  events for the Output.drawing_area widget are handled in a separate
+|  file gui-output-events.c
+|
+|  Some caveats with menu shorcut keys:  Some keys are trapped out by Gtk
+|  and can't be used as shortcuts (eg. '|', TAB, etc).  For these cases
+|  we have our own shortcut table and capture the keys and send the events
+|  there in ghid_port_key_press_cb().
+*/
+#include "config.h"
+#include "conf_core.h"
+
+#include <locale.h>
+#include "ghid-layer-selector.h"
+#include "ghid-route-style-selector.h"
+#include "gtkhid.h"
+#include "gui.h"
+#include "hid.h"
+#include "hid_cfg.h"
+#include "hid_cfg_action.h"
+#include "action_helper.h"
+#include "buffer.h"
+#include "change.h"
+#include "copy.h"
+#include "create.h"
+#include "crosshair.h"
+#include "draw.h"
+#include "error.h"
+#include "plug_io.h"
+#include "find.h"
+#include "insert.h"
+#include "line.h"
+#include "mymem.h"
+#include "misc.h"
+#include "layer.h"
+#include "move.h"
+#include "pcb-printf.h"
+#include "polygon.h"
+#include "rats.h"
+#include "remove.h"
+#include "rotate.h"
+#include "rubberband.h"
+#include "search.h"
+#include "select.h"
+#include "set.h"
+#include "undo.h"
+#include "event.h"
+#include "free_atexit.h"
+#include "paths.h"
+#include "gui-icons-mode-buttons.data"
+#include "gui-icons-misc.data"
+#include "gui.h"
+#include "win_place.h"
+#include "hid_attrib.h"
+#include "hid_actions.h"
+#include "hid_flags.h"
+#include "route_style.h"
+
+static pcb_bool ignore_layer_update;
+
+static GtkWidget *ghid_load_menus(void);
+
+GhidGui _ghidgui, *ghidgui = &_ghidgui;
+
+GHidPort ghid_port, *gport;
+
+hid_cfg_t *ghid_cfg = NULL;
+hid_cfg_mouse_t ghid_mouse;
+hid_cfg_keys_t ghid_keymap;
+
+/*! \brief callback for ghid_main_menu_update_toggle_state () */
+void menu_toggle_update_cb(GtkAction * act, const char *tflag, const char *aflag)
+{
+	if (tflag != NULL) {
+		int v = hid_get_flag(tflag);
+		if (v < 0) {
+			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), 0);
+			gtk_action_set_sensitive(act, 0);
+		}
+		else
+			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ! !v);
+	}
+	if (aflag != NULL) {
+		int v = hid_get_flag(aflag);
+		gtk_action_set_sensitive(act, ! !v);
+	}
+}
+
+/*! \brief sync the menu checkboxes with actual pcb state */
+void ghid_update_toggle_flags()
+{
+	ghid_main_menu_update_toggle_state(GHID_MAIN_MENU(ghidgui->menu_bar), menu_toggle_update_cb);
+}
+
+static void h_adjustment_changed_cb(GtkAdjustment * adj, GhidGui * g)
+{
+	if (g->adjustment_changed_holdoff)
+		return;
+
+	ghid_port_ranges_changed();
+}
+
+static void v_adjustment_changed_cb(GtkAdjustment * adj, GhidGui * g)
+{
+	if (g->adjustment_changed_holdoff)
+		return;
+
+	ghid_port_ranges_changed();
+}
+
+	/* Save size of top window changes so PCB can restart at its size at exit.
+	 */
+static gint top_window_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, GHidPort * port)
+{
+	wplc_config_event(widget, &hid_gtk_wgeo.top_x, &hid_gtk_wgeo.top_y, &hid_gtk_wgeo.top_width, &hid_gtk_wgeo.top_height);
+
+	return FALSE;
+}
+
+static void info_bar_response_cb(GtkInfoBar * info_bar, gint response_id, GhidGui * _gui)
+{
+	gtk_widget_destroy(_gui->info_bar);
+	_gui->info_bar = NULL;
+
+	if (response_id == GTK_RESPONSE_ACCEPT)
+		RevertPCB();
+}
+
+static void close_file_modified_externally_prompt(void)
+{
+	if (ghidgui->info_bar != NULL)
+		gtk_widget_destroy(ghidgui->info_bar);
+	ghidgui->info_bar = NULL;
+}
+
+static void show_file_modified_externally_prompt(void)
+{
+	GtkWidget *button;
+	GtkWidget *button_image;
+	GtkWidget *icon;
+	GtkWidget *label;
+	GtkWidget *content_area;
+	char *file_path_utf8;
+	const char *secondary_text;
+	char *markup;
+
+	close_file_modified_externally_prompt();
+
+	ghidgui->info_bar = gtk_info_bar_new();
+
+	button = gtk_info_bar_add_button(GTK_INFO_BAR(ghidgui->info_bar), _("Reload"), GTK_RESPONSE_ACCEPT);
+	button_image = gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON);
+	gtk_button_set_image(GTK_BUTTON(button), button_image);
+
+	gtk_info_bar_add_button(GTK_INFO_BAR(ghidgui->info_bar), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_info_bar_set_message_type(GTK_INFO_BAR(ghidgui->info_bar), GTK_MESSAGE_WARNING);
+	gtk_box_pack_start(GTK_BOX(ghidgui->vbox_middle), ghidgui->info_bar, FALSE, FALSE, 0);
+	gtk_box_reorder_child(GTK_BOX(ghidgui->vbox_middle), ghidgui->info_bar, 0);
+
+
+	g_signal_connect(ghidgui->info_bar, "response", G_CALLBACK(info_bar_response_cb), ghidgui);
+
+	file_path_utf8 = g_filename_to_utf8(PCB->Filename, -1, NULL, NULL, NULL);
+
+	secondary_text = PCB->Changed ? "Do you want to drop your changes and reload the file?" : "Do you want to reload the file?";
+
+	markup = g_markup_printf_escaped(_("<b>The file %s has changed on disk</b>\n\n%s"), file_path_utf8, secondary_text);
+	g_free(file_path_utf8);
+
+	content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(ghidgui->info_bar));
+
+	icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
+	gtk_box_pack_start(GTK_BOX(content_area), icon, FALSE, FALSE, 0);
+
+	label = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(content_area), label, TRUE, TRUE, 6);
+
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+
+	gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
+
+	gtk_widget_show_all(ghidgui->info_bar);
+}
+
+static pcb_bool check_externally_modified(void)
+{
+	GFile *file;
+	GFileInfo *info;
+	GTimeVal timeval;
+
+	/* Treat zero time as a flag to indicate we've not got an mtime yet */
+	if (PCB->Filename == NULL || (ghidgui->our_mtime.tv_sec == 0 && ghidgui->our_mtime.tv_usec == 0))
+		return pcb_false;
+
+	file = g_file_new_for_path(PCB->Filename);
+	info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+	g_object_unref(file);
+
+	if (info == NULL || !g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
+		return pcb_false;
+
+	g_file_info_get_modification_time(info, &timeval);	/*&ghidgui->last_seen_mtime); */
+	g_object_unref(info);
+
+	/* Ignore when the file on disk is the same age as when we last looked */
+	if (timeval.tv_sec == ghidgui->last_seen_mtime.tv_sec && timeval.tv_usec == ghidgui->last_seen_mtime.tv_usec)
+		return pcb_false;
+
+	ghidgui->last_seen_mtime = timeval;
+
+	return (ghidgui->last_seen_mtime.tv_sec > ghidgui->our_mtime.tv_sec) ||
+		(ghidgui->last_seen_mtime.tv_sec == ghidgui->our_mtime.tv_sec &&
+		 ghidgui->last_seen_mtime.tv_usec > ghidgui->our_mtime.tv_usec);
+}
+
+static gboolean top_window_enter_cb(GtkWidget * widget, GdkEvent * event, GHidPort * port)
+{
+	if (check_externally_modified())
+		show_file_modified_externally_prompt();
+
+	return FALSE;
+}
+
+/*! \brief Menu action callback function
+ *  \par Function Description
+ *  This is the main menu callback function.  The callback receives
+ *  the original lihata action node pointer HID actions to be
+ *  executed.
+ *
+ *  \param [in]   The action that was activated
+ *  \param [in]   The related menu lht action node
+ */
+
+static void ghid_menu_cb(GtkAction * action, const lht_node_t * node)
+{
+	if (action == NULL || node == NULL)
+		return;
+
+	hid_cfg_action(node);
+
+	/* Sync gui widgets with pcb state */
+	ghid_mode_buttons_update();
+
+	/* Sync gui status display with pcb state */
+	AdjustAttachedObjects();
+	ghid_invalidate_all();
+	ghid_window_set_name_label(PCB->Name);
+	ghid_set_status_line_label();
+}
+
+static void update_board_mtime_from_disk(void)
+{
+	GFile *file;
+	GFileInfo *info;
+
+	ghidgui->our_mtime.tv_sec = 0;
+	ghidgui->our_mtime.tv_usec = 0;
+	ghidgui->last_seen_mtime = ghidgui->our_mtime;
+
+	if (PCB->Filename == NULL)
+		return;
+
+	file = g_file_new_for_path(PCB->Filename);
+	info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+	g_object_unref(file);
+
+	if (info == NULL || !g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
+		return;
+
+	g_file_info_get_modification_time(info, &ghidgui->our_mtime);
+	g_object_unref(info);
+
+	ghidgui->last_seen_mtime = ghidgui->our_mtime;
+}
+
+	/* Sync toggle states that were saved with the layout and notify the
+	   |  config code to update Settings values it manages.
+	 */
+void ghid_sync_with_new_layout(void)
+{
+	if (vtroutestyle_len(&PCB->RouteStyle) > 0) {
+		pcb_use_route_style(&PCB->RouteStyle.array[0]);
+		ghid_route_style_selector_select_style(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector), &PCB->RouteStyle.array[0]);
+	}
+
+	ghid_config_handle_units_changed();
+
+	ghid_window_set_name_label(PCB->Name);
+	ghid_set_status_line_label();
+	close_file_modified_externally_prompt();
+	update_board_mtime_from_disk();
+}
+
+void ghid_notify_save_pcb(const char *filename, pcb_bool done)
+{
+	/* Do nothing if it is not the active PCB file that is being saved.
+	 */
+	if (PCB->Filename == NULL || strcmp(filename, PCB->Filename) != 0)
+		return;
+
+	if (done)
+		update_board_mtime_from_disk();
+}
+
+void ghid_notify_filename_changed(void)
+{
+	/* Pick up the mtime of the new PCB file */
+	update_board_mtime_from_disk();
+	ghid_window_set_name_label(PCB->Name);
+}
+
+/* ---------------------------------------------------------------------------
+ *
+ * layer_process()
+ *
+ * Takes the index into the layers and produces the text string for
+ * the layer and if the layer is currently visible or not.  This is
+ * used by a couple of functions.
+ *
+ */
+static void layer_process(const gchar ** color_string, const char **text, int *set, int i)
+{
+	int tmp;
+	const char *tmps;
+	const gchar *tmpc;
+
+	/* cheap hack to let users pass in NULL for either text or set if
+	 * they don't care about the result
+	 */
+
+	if (color_string == NULL)
+		color_string = &tmpc;
+
+	if (text == NULL)
+		text = &tmps;
+
+	if (set == NULL)
+		set = &tmp;
+
+	switch (i) {
+	case LAYER_BUTTON_SILK:
+		*color_string = conf_core.appearance.color.element;
+		*text = _("silk");
+		*set = PCB->ElementOn;
+		break;
+	case LAYER_BUTTON_RATS:
+		*color_string = conf_core.appearance.color.rat;
+		*text = _("rat lines");
+		*set = PCB->RatOn;
+		break;
+	case LAYER_BUTTON_PINS:
+		*color_string = conf_core.appearance.color.pin;
+		*text = _("pins/pads");
+		*set = PCB->PinOn;
+		break;
+	case LAYER_BUTTON_VIAS:
+		*color_string = conf_core.appearance.color.via;
+		*text = _("vias");
+		*set = PCB->ViaOn;
+		break;
+	case LAYER_BUTTON_FARSIDE:
+		*color_string = conf_core.appearance.color.invisible_objects;
+		*text = _("far side");
+		*set = PCB->InvisibleObjectsOn;
+		break;
+	case LAYER_BUTTON_MASK:
+		*color_string = conf_core.appearance.color.mask;
+		*text = _("solder mask");
+		*set = conf_core.editor.show_mask;
+		break;
+	default:											/* layers */
+		*color_string = conf_core.appearance.color.layer[i];
+		*text = (char *) UNKNOWN(PCB->Data->Layer[i].Name);
+		*set = PCB->Data->Layer[i].On;
+		break;
+	}
+}
+
+/*! \brief Callback for GHidLayerSelector layer selection */
+static void layer_selector_select_callback(GHidLayerSelector * ls, int layer, gpointer d)
+{
+	ignore_layer_update = pcb_true;
+	/* Select Layer */
+	PCB->SilkActive = (layer == LAYER_BUTTON_SILK);
+	PCB->RatDraw = (layer == LAYER_BUTTON_RATS);
+	if (layer == LAYER_BUTTON_SILK) {
+		PCB->ElementOn = pcb_true;
+		hid_action("LayersChanged");
+	}
+	else if (layer == LAYER_BUTTON_RATS) {
+		PCB->RatOn = pcb_true;
+		hid_action("LayersChanged");
+	}
+	else if (layer < max_copper_layer)
+		ChangeGroupVisibility(layer, TRUE, pcb_true);
+
+	ignore_layer_update = pcb_false;
+
+	ghid_invalidate_all();
+}
+
+/*! \brief Callback for GHidLayerSelector layer toggling */
+static void layer_selector_toggle_callback(GHidLayerSelector * ls, int layer, gpointer d)
+{
+	gboolean redraw = FALSE;
+	gboolean active;
+	layer_process(NULL, NULL, &active, layer);
+
+	active = !active;
+	ignore_layer_update = pcb_true;
+	switch (layer) {
+	case LAYER_BUTTON_SILK:
+		PCB->ElementOn = active;
+		PCB->Data->SILKLAYER.On = PCB->ElementOn;
+		PCB->Data->BACKSILKLAYER.On = PCB->ElementOn;
+		redraw = 1;
+		break;
+	case LAYER_BUTTON_RATS:
+		PCB->RatOn = active;
+		redraw = 1;
+		break;
+	case LAYER_BUTTON_PINS:
+		PCB->PinOn = active;
+		redraw |= (elementlist_length(&PCB->Data->Element) != 0);
+		break;
+	case LAYER_BUTTON_VIAS:
+		PCB->ViaOn = active;
+		redraw |= (pinlist_length(&PCB->Data->Via) != 0);
+		break;
+	case LAYER_BUTTON_FARSIDE:
+		PCB->InvisibleObjectsOn = active;
+		PCB->Data->BACKSILKLAYER.On = (active && PCB->ElementOn);
+		redraw = TRUE;
+		break;
+	case LAYER_BUTTON_MASK:
+		if (active)
+			conf_set_editor(show_mask, 1);
+		else
+			conf_set_editor(show_mask, 0);
+		redraw = TRUE;
+		break;
+	default:
+		/* Flip the visibility */
+		ChangeGroupVisibility(layer, active, pcb_false);
+		redraw = TRUE;
+		break;
+	}
+
+	/* Select the next visible layer. (If there is none, this will
+	 * select the currently-selected layer, triggering the selection
+	 * callback, which will turn the visibility on.) This way we
+	 * will never have an invisible layer selected.
+	 */
+	if (!active)
+		ghid_layer_selector_select_next_visible(ls);
+
+	ignore_layer_update = pcb_false;
+
+	if (redraw)
+		ghid_invalidate_all();
+}
+
+/*! \brief Install menu bar and accelerator groups */
+void ghid_install_accel_groups(GtkWindow * window, GhidGui * gui)
+{
+	gtk_window_add_accel_group(window, ghid_main_menu_get_accel_group(GHID_MAIN_MENU(gui->menu_bar)));
+	gtk_window_add_accel_group(window, ghid_layer_selector_get_accel_group(GHID_LAYER_SELECTOR(gui->layer_selector)));
+	gtk_window_add_accel_group
+		(window, ghid_route_style_selector_get_accel_group(GHID_ROUTE_STYLE_SELECTOR(gui->route_style_selector)));
+}
+
+/*! \brief Remove menu bar and accelerator groups */
+void ghid_remove_accel_groups(GtkWindow * window, GhidGui * gui)
+{
+	gtk_window_remove_accel_group(window, ghid_main_menu_get_accel_group(GHID_MAIN_MENU(gui->menu_bar)));
+	gtk_window_remove_accel_group(window, ghid_layer_selector_get_accel_group(GHID_LAYER_SELECTOR(gui->layer_selector)));
+	gtk_window_remove_accel_group
+		(window, ghid_route_style_selector_get_accel_group(GHID_ROUTE_STYLE_SELECTOR(gui->route_style_selector)));
+}
+
+/* Refreshes the window title bar and sets the PCB name to the
+ * window title bar or to a seperate label
+ */
+void ghid_window_set_name_label(gchar * name)
+{
+	gchar *str;
+	gchar *filename;
+
+	/* FIXME -- should this happen?  It does... */
+	/* This happens if we're calling an exporter from the command line */
+	if (ghidgui == NULL)
+		return;
+
+	dup_string(&(ghidgui->name_label_string), name);
+	if (!ghidgui->name_label_string || !*ghidgui->name_label_string)
+		ghidgui->name_label_string = g_strdup(_("Unnamed"));
+
+	if (!PCB->Filename || !*PCB->Filename)
+		filename = g_strdup(_("Unsaved.pcb"));
+	else
+		filename = g_strdup(PCB->Filename);
+
+	str = g_strdup_printf("%s%s (%s) - pcb-rnd", PCB->Changed ? "*" : "", ghidgui->name_label_string, filename);
+	gtk_window_set_title(GTK_WINDOW(gport->top_window), str);
+	g_free(str);
+	g_free(filename);
+}
+
+static void grid_units_button_cb(GtkWidget * widget, gpointer data)
+{
+	/* Button only toggles between mm and mil */
+	if (conf_core.editor.grid_unit == get_unit_struct("mm"))
+		hid_actionl("SetUnits", "mil", NULL);
+	else
+		hid_actionl("SetUnits", "mm", NULL);
+}
+
+/*
+ * The two following callbacks are used to keep the absolute
+ * and relative cursor labels from growing and shrinking as you
+ * move the cursor around.
+ */
+static void absolute_label_size_req_cb(GtkWidget * widget, GtkRequisition * req, gpointer data)
+{
+
+	static gint w = 0;
+	if (req->width > w)
+		w = req->width;
+	else
+		req->width = w;
+}
+
+static void relative_label_size_req_cb(GtkWidget * widget, GtkRequisition * req, gpointer data)
+{
+
+	static gint w = 0;
+	if (req->width > w)
+		w = req->width;
+	else
+		req->width = w;
+}
+
+static void make_cursor_position_labels(GtkWidget * hbox, GHidPort * port)
+{
+	GtkWidget *frame, *label;
+
+	/* The grid units button next to the cursor position labels.
+	 */
+	ghidgui->grid_units_button = gtk_button_new();
+	label = gtk_label_new("");
+	gtk_label_set_markup(GTK_LABEL(label), conf_core.editor.grid_unit->in_suffix);
+	ghidgui->grid_units_label = label;
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_container_add(GTK_CONTAINER(ghidgui->grid_units_button), label);
+	gtk_box_pack_end(GTK_BOX(hbox), ghidgui->grid_units_button, FALSE, TRUE, 0);
+	g_signal_connect(ghidgui->grid_units_button, "clicked", G_CALLBACK(grid_units_button_cb), NULL);
+
+	/* The absolute cursor position label
+	 */
+	frame = gtk_frame_new(NULL);
+	gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
+	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
+
+	label = gtk_label_new("");
+	gtk_container_add(GTK_CONTAINER(frame), label);
+	ghidgui->cursor_position_absolute_label = label;
+	g_signal_connect(G_OBJECT(label), "size-request", G_CALLBACK(absolute_label_size_req_cb), NULL);
+
+
+	/* The relative cursor position label
+	 */
+	frame = gtk_frame_new(NULL);
+	gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
+	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
+	label = gtk_label_new(" __.__  __.__ ");
+	gtk_container_add(GTK_CONTAINER(frame), label);
+	ghidgui->cursor_position_relative_label = label;
+	g_signal_connect(G_OBJECT(label), "size-request", G_CALLBACK(relative_label_size_req_cb), NULL);
+
+}
+
+/* \brief Add "virtual layers" to a layer selector */
+static void make_virtual_layer_buttons(GtkWidget * layer_selector)
+{
+	GHidLayerSelector *layersel = GHID_LAYER_SELECTOR(layer_selector);
+	const gchar *text;
+	const gchar *color_string;
+	gboolean active;
+
+	layer_process(&color_string, &text, &active, LAYER_BUTTON_SILK);
+	ghid_layer_selector_add_layer(layersel, LAYER_BUTTON_SILK, text, color_string, active, TRUE);
+	layer_process(&color_string, &text, &active, LAYER_BUTTON_RATS);
+	ghid_layer_selector_add_layer(layersel, LAYER_BUTTON_RATS, text, color_string, active, TRUE);
+	layer_process(&color_string, &text, &active, LAYER_BUTTON_PINS);
+	ghid_layer_selector_add_layer(layersel, LAYER_BUTTON_PINS, text, color_string, active, FALSE);
+	layer_process(&color_string, &text, &active, LAYER_BUTTON_VIAS);
+	ghid_layer_selector_add_layer(layersel, LAYER_BUTTON_VIAS, text, color_string, active, FALSE);
+	layer_process(&color_string, &text, &active, LAYER_BUTTON_FARSIDE);
+	ghid_layer_selector_add_layer(layersel, LAYER_BUTTON_FARSIDE, text, color_string, active, FALSE);
+	layer_process(&color_string, &text, &active, LAYER_BUTTON_MASK);
+	ghid_layer_selector_add_layer(layersel, LAYER_BUTTON_MASK, text, color_string, active, FALSE);
+}
+
+/*! \brief callback for ghid_layer_selector_update_colors */
+const gchar *get_layer_color(gint layer)
+{
+	const gchar *rv;
+	layer_process(&rv, NULL, NULL, layer);
+	return rv;
+}
+
+/*! \brief Update a layer selector's color scheme */
+void ghid_layer_buttons_color_update(void)
+{
+	ghid_layer_selector_update_colors(GHID_LAYER_SELECTOR(ghidgui->layer_selector), get_layer_color);
+	pcb_colors_from_settings(PCB);
+}
+
+/*! \brief Populate a layer selector with all layers Gtk is aware of */
+static void make_layer_buttons(GtkWidget * layersel)
+{
+	gint i;
+	const gchar *text;
+	const gchar *color_string;
+	gboolean active = TRUE;
+
+	for (i = 0; i < max_copper_layer; ++i) {
+		layer_process(&color_string, &text, &active, i);
+		ghid_layer_selector_add_layer(GHID_LAYER_SELECTOR(layersel), i, text, color_string, active, TRUE);
+	}
+}
+
+
+/*! \brief callback for ghid_layer_selector_delete_layers */
+gboolean get_layer_delete(gint layer)
+{
+	return layer >= max_copper_layer;
+}
+
+/*! \brief Synchronize layer selector widget with current PCB state
+ *  \par Function Description
+ *  Called when user toggles layer visibility or changes drawing layer,
+ *  or when layer visibility is changed programatically.
+ */
+void ghid_layer_buttons_update(void)
+{
+	gint layer;
+
+	if (ignore_layer_update)
+		return;
+
+	ghid_layer_selector_delete_layers(GHID_LAYER_SELECTOR(ghidgui->layer_selector), get_layer_delete);
+	make_layer_buttons(ghidgui->layer_selector);
+	make_virtual_layer_buttons(ghidgui->layer_selector);
+	ghid_main_menu_install_layer_selector(GHID_MAIN_MENU(ghidgui->menu_bar), GHID_LAYER_SELECTOR(ghidgui->layer_selector));
+
+	/* Sync selected layer with PCB's state */
+	if (PCB->RatDraw)
+		layer = LAYER_BUTTON_RATS;
+	else if (PCB->SilkActive)
+		layer = LAYER_BUTTON_SILK;
+	else
+		layer = LayerStack[0];
+
+	ghid_layer_selector_select_layer(GHID_LAYER_SELECTOR(ghidgui->layer_selector), layer);
+}
+
+/*! \brief Called when user clicks OK on route style dialog */
+static void route_styles_edited_cb(GHidRouteStyleSelector * rss, gboolean save, gpointer data)
+{
+	conf_setf(CFR_DESIGN, "design/routes", -1, "%s", make_route_string(&PCB->RouteStyle));
+	if (save)
+		conf_setf(CFR_USER, "design/routes", -1, "%s", make_route_string(&PCB->RouteStyle));
+	ghid_main_menu_install_route_style_selector
+		(GHID_MAIN_MENU(ghidgui->menu_bar), GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+}
+
+/*! \brief Called when a route style is selected */
+static void route_style_changed_cb(GHidRouteStyleSelector * rss, RouteStyleType * rst, gpointer data)
+{
+	pcb_use_route_style(rst);
+	ghid_set_status_line_label();
+}
+
+/*! \brief Configure the route style selector */
+void make_route_style_buttons(GHidRouteStyleSelector * rss)
+{
+	int i;
+
+	/* Make sure the <custom> item is added */
+	ghid_route_style_selector_add_route_style(rss, NULL);
+
+	for (i = 0; i < vtroutestyle_len(&PCB->RouteStyle); ++i)
+		ghid_route_style_selector_add_route_style(rss, &PCB->RouteStyle.array[i]);
+	g_signal_connect(G_OBJECT(rss), "select_style", G_CALLBACK(route_style_changed_cb), NULL);
+	g_signal_connect(G_OBJECT(rss), "style_edited", G_CALLBACK(route_styles_edited_cb), NULL);
+}
+
+/*
+ *  ---------------------------------------------------------------
+ * Mode buttons
+ */
+typedef struct {
+	GtkWidget *button;
+	GtkWidget *toolbar_button;
+	guint button_cb_id;
+	guint toolbar_button_cb_id;
+	const gchar *name;
+	gint mode;
+	const gchar **xpm;
+} ModeButton;
+
+
+static ModeButton mode_buttons[] = {
+	{NULL, NULL, 0, 0, "via", PCB_MODE_VIA, via},
+	{NULL, NULL, 0, 0, "line", PCB_MODE_LINE, line},
+	{NULL, NULL, 0, 0, "arc", PCB_MODE_ARC, arc},
+	{NULL, NULL, 0, 0, "text", PCB_MODE_TEXT, text},
+	{NULL, NULL, 0, 0, "rectangle", PCB_MODE_RECTANGLE, rect},
+	{NULL, NULL, 0, 0, "polygon", PCB_MODE_POLYGON, poly},
+	{NULL, NULL, 0, 0, "polygonhole", PCB_MODE_POLYGON_HOLE, polyhole},
+	{NULL, NULL, 0, 0, "buffer", PCB_MODE_PASTE_BUFFER, buf},
+	{NULL, NULL, 0, 0, "remove", PCB_MODE_REMOVE, del},
+	{NULL, NULL, 0, 0, "rotate", PCB_MODE_ROTATE, rot},
+	{NULL, NULL, 0, 0, "insertPoint", PCB_MODE_INSERT_POINT, ins},
+	{NULL, NULL, 0, 0, "thermal", PCB_MODE_THERMAL, thrm},
+	{NULL, NULL, 0, 0, "select", PCB_MODE_ARROW, sel},
+	{NULL, NULL, 0, 0, "lock", PCB_MODE_LOCK, lock}
+};
+
+static gint n_mode_buttons = G_N_ELEMENTS(mode_buttons);
+
+static void do_set_mode(int mode)
+{
+	SetMode(mode);
+	ghid_mode_cursor(mode);
+	ghidgui->settings_mode = mode;
+}
+
+static void mode_toolbar_button_toggled_cb(GtkToggleButton * button, ModeButton * mb)
+{
+	gboolean active = gtk_toggle_button_get_active(button);
+
+	if (mb->button != NULL) {
+		g_signal_handler_block(mb->button, mb->button_cb_id);
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->button), active);
+		g_signal_handler_unblock(mb->button, mb->button_cb_id);
+	}
+
+	if (active)
+		do_set_mode(mb->mode);
+}
+
+static void mode_button_toggled_cb(GtkWidget * widget, ModeButton * mb)
+{
+	gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+	if (mb->toolbar_button != NULL) {
+		g_signal_handler_block(mb->toolbar_button, mb->toolbar_button_cb_id);
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->toolbar_button), active);
+		g_signal_handler_unblock(mb->toolbar_button, mb->toolbar_button_cb_id);
+	}
+
+	if (active)
+		do_set_mode(mb->mode);
+}
+
+void ghid_mode_buttons_update(void)
+{
+	ModeButton *mb;
+	gint i;
+
+	for (i = 0; i < n_mode_buttons; ++i) {
+		mb = &mode_buttons[i];
+		if (conf_core.editor.mode == mb->mode) {
+			g_signal_handler_block(mb->button, mb->button_cb_id);
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->button), TRUE);
+			g_signal_handler_unblock(mb->button, mb->button_cb_id);
+
+			g_signal_handler_block(mb->toolbar_button, mb->toolbar_button_cb_id);
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->toolbar_button), TRUE);
+			g_signal_handler_unblock(mb->toolbar_button, mb->toolbar_button_cb_id);
+			break;
+		}
+	}
+}
+
+void ghid_pack_mode_buttons(void)
+{
+	if (conf_hid_gtk.plugins.hid_gtk.compact_vertical) {
+		gtk_widget_hide(ghidgui->mode_buttons_frame);
+		gtk_widget_show_all(ghidgui->mode_toolbar);
+	}
+	else {
+		gtk_widget_hide(ghidgui->mode_toolbar);
+		gtk_widget_show_all(ghidgui->mode_buttons_frame);
+	}
+}
+
+static void make_mode_buttons_and_toolbar(GtkWidget ** mode_frame, GtkWidget ** mode_toolbar, GtkWidget ** mode_toolbar_vbox)
+{
+	GtkToolItem *tool_item;
+	GtkWidget *vbox, *hbox = NULL;
+	GtkWidget *pad_hbox, *pad_vbox;
+	GtkWidget *image;
+	GdkPixbuf *pixbuf;
+	GSList *group = NULL;
+	GSList *toolbar_group = NULL;
+	ModeButton *mb;
+	int n_mb, i, tb_width = 0;
+
+	*mode_toolbar = gtk_toolbar_new();
+
+	*mode_frame = gtk_frame_new(NULL);
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(*mode_frame), vbox);
+
+	for (i = 0; i < n_mode_buttons; ++i) {
+		mb = &mode_buttons[i];
+
+		/* Create tool button for mode frame */
+		mb->button = gtk_radio_button_new(group);
+		group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(mb->button));
+		gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(mb->button), FALSE);
+
+		/* Create tool button for toolbar */
+		mb->toolbar_button = gtk_radio_button_new(toolbar_group);
+		toolbar_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(mb->toolbar_button));
+		gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(mb->toolbar_button), FALSE);
+
+		/* Pack mode-frame button into the frame */
+		n_mb = conf_hid_gtk.plugins.hid_gtk.n_mode_button_columns;
+		if ((n_mb < 1) || (n_mb > 10))
+			n_mb = 3;
+		if ((i % n_mb) == 0) {
+			hbox = gtk_hbox_new(FALSE, 0);
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+		}
+		gtk_box_pack_start(GTK_BOX(hbox), mb->button, FALSE, FALSE, 0);
+
+		/* Create a container for the toolbar button and add that */
+		tool_item = gtk_tool_item_new();
+		gtk_container_add(GTK_CONTAINER(tool_item), mb->toolbar_button);
+		gtk_toolbar_insert(GTK_TOOLBAR(*mode_toolbar), tool_item, -1);
+
+		/* Load the image for the button, create GtkImage widgets for both
+		 * the grid button and the toolbar button, then pack into the buttons
+		 */
+		pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) mb->xpm);
+		image = gtk_image_new_from_pixbuf(pixbuf);
+		gtk_container_add(GTK_CONTAINER(mb->button), image);
+		image = gtk_image_new_from_pixbuf(pixbuf);
+		gtk_container_add(GTK_CONTAINER(mb->toolbar_button), image);
+		g_object_unref(pixbuf);
+		tb_width += image->requisition.width;
+
+		if (strcmp(mb->name, "select") == 0) {
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->button), TRUE);
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->toolbar_button), TRUE);
+		}
+
+		mb->button_cb_id = g_signal_connect(mb->button, "toggled", G_CALLBACK(mode_button_toggled_cb), mb);
+		mb->toolbar_button_cb_id = g_signal_connect(mb->toolbar_button, "toggled", G_CALLBACK(mode_toolbar_button_toggled_cb), mb);
+	}
+
+	*mode_toolbar_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(*mode_toolbar_vbox), *mode_toolbar, FALSE, FALSE, 0);
+
+	/* Pack an empty, wide hbox right below the toolbar and make it as wide
+	   as the calculated width of the toolbar with some tuning. Toolbar icons
+	   disappear if the container hbox is not wide enough. Without this hack
+	   the width would be determined by the menu bar, and that could be short
+	   if the user changes the menu layout. */
+	pad_hbox = gtk_hbox_new(FALSE, 0);
+	pad_vbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(pad_hbox), pad_vbox, FALSE, FALSE, tb_width * 3 / 4);
+	gtk_box_pack_start(GTK_BOX(*mode_toolbar_vbox), pad_hbox, FALSE, FALSE, 0);
+}
+
+
+/*
+ * ---------------------------------------------------------------
+ * Top window
+ * ---------------------------------------------------------------
+ */
+
+static gint delete_chart_cb(GtkWidget * widget, GdkEvent * event, GHidPort * port)
+{
+	if (ghid_entry_loop != NULL)
+		g_main_loop_quit(ghid_entry_loop);
+
+	hid_action("Quit");
+
+	/*
+	 * Return TRUE to keep our app running.  A FALSE here would let the
+	 * delete signal continue on and kill our program.
+	 */
+	return TRUE;
+}
+
+static void destroy_chart_cb(GtkWidget * widget, GHidPort * port)
+{
+	ghid_shutdown_renderer(port);
+	gtk_main_quit();
+}
+
+static void get_widget_styles(GtkStyle ** menu_bar_style, GtkStyle ** tool_button_style, GtkStyle ** tool_button_label_style)
+{
+	GtkWidget *tool_button;
+	GtkWidget *tool_button_label;
+	GtkToolItem *tool_item;
+
+	/* Build a tool item to extract the theme's styling for a toolbar button with text */
+	tool_item = gtk_tool_item_new();
+	gtk_toolbar_insert(GTK_TOOLBAR(ghidgui->mode_toolbar), tool_item, 0);
+	tool_button = gtk_button_new();
+	gtk_container_add(GTK_CONTAINER(tool_item), tool_button);
+	tool_button_label = gtk_label_new("");
+	gtk_container_add(GTK_CONTAINER(tool_button), tool_button_label);
+
+	/* Grab the various styles we need */
+	gtk_widget_ensure_style(ghidgui->menu_bar);
+	*menu_bar_style = gtk_widget_get_style(ghidgui->menu_bar);
+
+	gtk_widget_ensure_style(tool_button);
+	*tool_button_style = gtk_widget_get_style(tool_button);
+
+	gtk_widget_ensure_style(tool_button_label);
+	*tool_button_label_style = gtk_widget_get_style(tool_button_label);
+
+	gtk_widget_destroy(GTK_WIDGET(tool_item));
+}
+
+static void do_fix_topbar_theming(void)
+{
+	GtkWidget *rel_pos_frame;
+	GtkWidget *abs_pos_frame;
+	GtkStyle *menu_bar_style;
+	GtkStyle *tool_button_style;
+	GtkStyle *tool_button_label_style;
+
+	get_widget_styles(&menu_bar_style, &tool_button_style, &tool_button_label_style);
+
+	/* Style the top bar background as if it were all a menu bar */
+	gtk_widget_set_style(ghidgui->top_bar_background, menu_bar_style);
+
+	/* Style the cursor position labels using the menu bar style as well.
+	 * If this turns out to cause problems with certain gtk themes, we may
+	 * need to grab the GtkStyle associated with an actual menu item to
+	 * get a text color to render with.
+	 */
+	gtk_widget_set_style(ghidgui->cursor_position_relative_label, menu_bar_style);
+	gtk_widget_set_style(ghidgui->cursor_position_absolute_label, menu_bar_style);
+
+	/* Style the units button as if it were a toolbar button - hopefully
+	 * this isn't too ugly sitting on a background themed as a menu bar.
+	 * It is unlikely any theme defines colours for a GtkButton sitting on
+	 * a menu bar.
+	 */
+	rel_pos_frame = gtk_widget_get_parent(ghidgui->cursor_position_relative_label);
+	abs_pos_frame = gtk_widget_get_parent(ghidgui->cursor_position_absolute_label);
+	gtk_widget_set_style(rel_pos_frame, menu_bar_style);
+	gtk_widget_set_style(abs_pos_frame, menu_bar_style);
+	gtk_widget_set_style(ghidgui->grid_units_button, tool_button_style);
+	gtk_widget_set_style(ghidgui->grid_units_label, tool_button_label_style);
+}
+
+/* Attempt to produce a conststent style for our extra menu-bar items by
+ * copying aspects from the menu bar style set by the user's GTK theme.
+ * Setup signal handlers to update our efforts if the user changes their
+ * theme whilst we are running.
+ */
+static void fix_topbar_theming(void)
+{
+	GtkSettings *settings;
+
+	do_fix_topbar_theming();
+
+	settings = gtk_widget_get_settings(ghidgui->top_bar_background);
+	g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(do_fix_topbar_theming), NULL);
+	g_signal_connect(settings, "notify::gtk-font-name", G_CALLBACK(do_fix_topbar_theming), NULL);
+}
+
+static void fullscreen_cb(GtkButton *btn, void *data)
+{
+	conf_setf(CFR_DESIGN, "editor/fullscreen", -1, "%d", !conf_core.editor.fullscreen, POL_OVERWRITE);
+}
+
+/*
+ * Create the top_window contents.  The config settings should be loaded
+ * before this is called.
+ */
+static void ghid_build_pcb_top_window(void)
+{
+	GtkWidget *window;
+	GtkWidget *vbox_main, *hbox_middle, *hbox;
+	GtkWidget *vbox, *frame, *hbox_scroll, *fullscreen_btn;
+	GtkWidget *label;
+	GHidPort *port = &ghid_port;
+	GtkWidget *scrolled;
+
+	window = gport->top_window;
+
+	vbox_main = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(window), vbox_main);
+
+	/* -- Top control bar */
+	ghidgui->top_bar_background = gtk_event_box_new();
+	gtk_box_pack_start(GTK_BOX(vbox_main), ghidgui->top_bar_background, FALSE, FALSE, 0);
+
+	ghidgui->top_hbox = gtk_hbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(ghidgui->top_bar_background), ghidgui->top_hbox);
+
+	/*
+	 * menu_hbox will be made insensitive when the gui needs
+	 * a modal button GetLocation button press.
+	 */
+	ghidgui->menu_hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(ghidgui->top_hbox), ghidgui->menu_hbox, FALSE, FALSE, 0);
+
+	ghidgui->menubar_toolbar_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(ghidgui->menu_hbox), ghidgui->menubar_toolbar_vbox, FALSE, FALSE, 0);
+
+	/* Build layer menus */
+	ghidgui->layer_selector = ghid_layer_selector_new();
+	make_layer_buttons(ghidgui->layer_selector);
+	make_virtual_layer_buttons(ghidgui->layer_selector);
+	g_signal_connect(G_OBJECT(ghidgui->layer_selector), "select_layer", G_CALLBACK(layer_selector_select_callback), NULL);
+	g_signal_connect(G_OBJECT(ghidgui->layer_selector), "toggle_layer", G_CALLBACK(layer_selector_toggle_callback), NULL);
+	/* Build main menu */
+	ghidgui->menu_bar = ghid_load_menus();
+	gtk_box_pack_start(GTK_BOX(ghidgui->menubar_toolbar_vbox), ghidgui->menu_bar, FALSE, FALSE, 0);
+
+	make_mode_buttons_and_toolbar(&ghidgui->mode_buttons_frame, &ghidgui->mode_toolbar, &ghidgui->mode_toolbar_vbox);
+	gtk_box_pack_start(GTK_BOX(ghidgui->menubar_toolbar_vbox), ghidgui->mode_toolbar_vbox, FALSE, FALSE, 0);
+
+	ghidgui->position_hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_end(GTK_BOX(ghidgui->top_hbox), ghidgui->position_hbox, FALSE, FALSE, 0);
+
+	make_cursor_position_labels(ghidgui->position_hbox, port);
+
+	hbox_middle = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox_main), hbox_middle, TRUE, TRUE, 0);
+
+	fix_topbar_theming();					/* Must be called after toolbar is created */
+
+	/* -- Left control bar */
+	/*
+	 * This box will be made insensitive when the gui needs
+	 * a modal button GetLocation button press.
+	 */
+	ghidgui->left_toolbar = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox_middle), ghidgui->left_toolbar, FALSE, FALSE, 0);
+
+	vbox = ghid_scrolled_vbox(ghidgui->left_toolbar, &scrolled, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_box_pack_start(GTK_BOX(vbox), ghidgui->layer_selector, FALSE, FALSE, 0);
+
+	/* ghidgui->mode_buttons_frame was created above in the call to
+	 * make_mode_buttons_and_toolbar (...);
+	 */
+	gtk_box_pack_start(GTK_BOX(ghidgui->left_toolbar), ghidgui->mode_buttons_frame, FALSE, FALSE, 0);
+
+	frame = gtk_frame_new(NULL);
+	gtk_box_pack_end(GTK_BOX(ghidgui->left_toolbar), frame, FALSE, FALSE, 0);
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(frame), vbox);
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 1);
+	ghidgui->route_style_selector = ghid_route_style_selector_new();
+	make_route_style_buttons(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+	gtk_box_pack_start(GTK_BOX(hbox), ghidgui->route_style_selector, FALSE, FALSE, 0);
+
+	ghidgui->vbox_middle = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox_middle), ghidgui->vbox_middle, TRUE, TRUE, 0);
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(ghidgui->vbox_middle), hbox, TRUE, TRUE, 0);
+
+	/* -- The PCB layout output drawing area */
+
+	gport->drawing_area = gtk_drawing_area_new();
+	ghid_init_drawing_widget(gport->drawing_area, gport);
+
+	gtk_widget_add_events(gport->drawing_area, GDK_EXPOSURE_MASK
+												| GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
+												| GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK
+												| GDK_KEY_RELEASE_MASK | GDK_KEY_PRESS_MASK
+												| GDK_FOCUS_CHANGE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
+
+	/*
+	 * This is required to get the drawing_area key-press-event.  Also the
+	 * enter and button press callbacks grab focus to be sure we have it
+	 * when in the drawing_area.
+	 */
+	gtk_widget_set_can_focus(gport->drawing_area, TRUE);
+
+	gtk_box_pack_start(GTK_BOX(hbox), gport->drawing_area, TRUE, TRUE, 0);
+
+	ghidgui->v_adjustment = gtk_adjustment_new(0.0, 0.0, 100.0, 10.0, 10.0, 10.0);
+	ghidgui->v_range = gtk_vscrollbar_new(GTK_ADJUSTMENT(ghidgui->v_adjustment));
+
+	gtk_box_pack_start(GTK_BOX(hbox), ghidgui->v_range, FALSE, FALSE, 0);
+
+
+
+	g_signal_connect(G_OBJECT(ghidgui->v_adjustment), "value_changed", G_CALLBACK(v_adjustment_changed_cb), ghidgui);
+
+	ghidgui->h_adjustment = gtk_adjustment_new(0.0, 0.0, 100.0, 10.0, 10.0, 10.0);
+
+	hbox_scroll = gtk_hbox_new(FALSE, 0);
+	ghidgui->h_range = gtk_hscrollbar_new(GTK_ADJUSTMENT(ghidgui->h_adjustment));
+	fullscreen_btn = gtk_button_new_with_label("FS");
+	g_signal_connect(GTK_OBJECT(fullscreen_btn), "clicked", G_CALLBACK(fullscreen_cb), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox_scroll), ghidgui->h_range, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox_scroll), fullscreen_btn, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(ghidgui->vbox_middle), hbox_scroll, FALSE, FALSE, 0);
+
+
+	g_signal_connect(G_OBJECT(ghidgui->h_adjustment), "value_changed", G_CALLBACK(h_adjustment_changed_cb), ghidgui);
+
+	/* -- The bottom status line label */
+	ghidgui->status_line_hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(ghidgui->vbox_middle), ghidgui->status_line_hbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new("");
+	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+	gtk_box_pack_start(GTK_BOX(ghidgui->status_line_hbox), label, FALSE, FALSE, 0);
+	ghidgui->status_line_label = label;
+
+	/* Depending on user setting, the command_combo_box may get packed into
+	   |  the status_line_hbox, but it will happen on demand the first time
+	   |  the user does a command entry.
+	 */
+
+	g_signal_connect(G_OBJECT(gport->drawing_area), "realize", G_CALLBACK(ghid_port_drawing_realize_cb), port);
+	g_signal_connect(G_OBJECT(gport->drawing_area), "expose_event", G_CALLBACK(ghid_drawing_area_expose_cb), port);
+	g_signal_connect(G_OBJECT(gport->top_window), "configure_event", G_CALLBACK(top_window_configure_event_cb), port);
+	g_signal_connect(gport->top_window, "enter-notify-event", G_CALLBACK(top_window_enter_cb), port);
+	g_signal_connect(G_OBJECT(gport->drawing_area), "configure_event",
+									 G_CALLBACK(ghid_port_drawing_area_configure_event_cb), port);
+
+
+	/* Mouse and key events will need to be intercepted when PCB needs a
+	   |  location from the user.
+	 */
+
+	ghid_interface_input_signals_connect();
+
+	g_signal_connect(G_OBJECT(gport->drawing_area), "scroll_event", G_CALLBACK(ghid_port_window_mouse_scroll_cb), port);
+	g_signal_connect(G_OBJECT(gport->drawing_area), "enter_notify_event", G_CALLBACK(ghid_port_window_enter_cb), port);
+	g_signal_connect(G_OBJECT(gport->drawing_area), "leave_notify_event", G_CALLBACK(ghid_port_window_leave_cb), port);
+	g_signal_connect(G_OBJECT(gport->drawing_area), "motion_notify_event", G_CALLBACK(ghid_port_window_motion_cb), port);
+
+
+
+	g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_chart_cb), port);
+	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_chart_cb), port);
+
+	ghidgui->creating = FALSE;
+
+	gtk_widget_show_all(gport->top_window);
+	ghid_pack_mode_buttons();
+	gdk_window_set_back_pixmap(gtk_widget_get_window(gport->drawing_area), NULL, FALSE);
+
+	ghid_fullscreen_apply();
+}
+
+
+	/* Connect and disconnect just the signals a g_main_loop() will need.
+	   |  Cursor and motion events still need to be handled by the top level
+	   |  loop, so don't connect/reconnect these.
+	   |  A g_main_loop will be running when PCB wants the user to select a
+	   |  location or if command entry is needed in the status line hbox.
+	   |  During these times normal button/key presses are intercepted, either
+	   |  by new signal handlers or the command_combo_box entry.
+	 */
+static gulong button_press_handler, button_release_handler, key_press_handler, key_release_handler;
+
+void ghid_interface_input_signals_connect(void)
+{
+	button_press_handler =
+		g_signal_connect(G_OBJECT(gport->drawing_area), "button_press_event", G_CALLBACK(ghid_port_button_press_cb), NULL);
+
+	button_release_handler =
+		g_signal_connect(G_OBJECT(gport->drawing_area), "button_release_event", G_CALLBACK(ghid_port_button_release_cb), NULL);
+
+	key_press_handler =
+		g_signal_connect(G_OBJECT(gport->drawing_area), "key_press_event", G_CALLBACK(ghid_port_key_press_cb), NULL);
+
+	key_release_handler =
+		g_signal_connect(G_OBJECT(gport->drawing_area), "key_release_event", G_CALLBACK(ghid_port_key_release_cb), NULL);
+}
+
+void ghid_interface_input_signals_disconnect(void)
+{
+	if (button_press_handler)
+		g_signal_handler_disconnect(gport->drawing_area, button_press_handler);
+
+	if (button_release_handler)
+		g_signal_handler_disconnect(gport->drawing_area, button_release_handler);
+
+	if (key_press_handler)
+		g_signal_handler_disconnect(gport->drawing_area, key_press_handler);
+
+	if (key_release_handler)
+		g_signal_handler_disconnect(gport->drawing_area, key_release_handler);
+
+	button_press_handler = button_release_handler = 0;
+	key_press_handler = key_release_handler = 0;
+
+}
+
+
+	/* We'll set the interface insensitive when a g_main_loop is running so the
+	   |  Gtk menus and buttons don't respond and interfere with the special entry
+	   |  the user needs to be doing.
+	 */
+void ghid_interface_set_sensitive(gboolean sensitive)
+{
+	gtk_widget_set_sensitive(ghidgui->left_toolbar, sensitive);
+	gtk_widget_set_sensitive(ghidgui->menu_hbox, sensitive);
+}
+
+
+/* ----------------------------------------------------------------------
+ * initializes icon pixmap and also cursor bit maps
+ */
+static void ghid_init_icons(GHidPort * port)
+{
+	GdkWindow *window = gtk_widget_get_window(gport->top_window);
+
+	XC_clock_source = gdk_bitmap_create_from_data(window, (char *) rotateIcon_bits, rotateIcon_width, rotateIcon_height);
+	XC_clock_mask = gdk_bitmap_create_from_data(window, (char *) rotateMask_bits, rotateMask_width, rotateMask_height);
+
+	XC_hand_source = gdk_bitmap_create_from_data(window, (char *) handIcon_bits, handIcon_width, handIcon_height);
+	XC_hand_mask = gdk_bitmap_create_from_data(window, (char *) handMask_bits, handMask_width, handMask_height);
+
+	XC_lock_source = gdk_bitmap_create_from_data(window, (char *) lockIcon_bits, lockIcon_width, lockIcon_height);
+	XC_lock_mask = gdk_bitmap_create_from_data(window, (char *) lockMask_bits, lockMask_width, lockMask_height);
+}
+
+void ghid_create_pcb_widgets(void)
+{
+	GHidPort *port = &ghid_port;
+	GError *err = NULL;
+
+	if (conf_hid_gtk.plugins.hid_gtk.bg_image)
+		ghidgui->bg_pixbuf = gdk_pixbuf_new_from_file(conf_hid_gtk.plugins.hid_gtk.bg_image, &err);
+	if (err) {
+		g_error("%s", err->message);
+		g_error_free(err);
+	}
+	ghid_build_pcb_top_window();
+	ghid_install_accel_groups(GTK_WINDOW(port->top_window), ghidgui);
+	ghid_update_toggle_flags();
+
+	ghid_init_icons(port);
+	SetMode(PCB_MODE_ARROW);
+	ghid_mode_buttons_update();
+}
+
+static gboolean ghid_listener_cb(GIOChannel * source, GIOCondition condition, gpointer data)
+{
+	GIOStatus status;
+	gchar *str;
+	gsize len;
+	gsize term;
+	GError *err = NULL;
+
+
+	if (condition & G_IO_HUP) {
+		gui->log("Read end of pipe died!\n");
+		return FALSE;
+	}
+
+	if (condition == G_IO_IN) {
+		status = g_io_channel_read_line(source, &str, &len, &term, &err);
+		switch (status) {
+		case G_IO_STATUS_NORMAL:
+			hid_parse_actions(str);
+			g_free(str);
+			break;
+
+		case G_IO_STATUS_ERROR:
+			gui->log("ERROR status from g_io_channel_read_line\n");
+			return FALSE;
+			break;
+
+		case G_IO_STATUS_EOF:
+			gui->log("Input pipe returned EOF.  The --listen option is \n" "probably not running anymore in this session.\n");
+			return FALSE;
+			break;
+
+		case G_IO_STATUS_AGAIN:
+			gui->log("AGAIN status from g_io_channel_read_line\n");
+			return FALSE;
+			break;
+
+		default:
+			fprintf(stderr, "ERROR:  unhandled case in ghid_listener_cb\n");
+			return FALSE;
+			break;
+		}
+
+	}
+	else
+		fprintf(stderr, "Unknown condition in ghid_listener_cb\n");
+
+	return TRUE;
+}
+
+static void ghid_create_listener(void)
+{
+	GIOChannel *channel;
+	int fd = fileno(stdin);
+
+	channel = g_io_channel_unix_new(fd);
+	g_io_add_watch(channel, G_IO_IN, ghid_listener_cb, NULL);
+}
+
+
+/* ------------------------------------------------------------ */
+int ghid_usage(const char *topic)
+{
+	fprintf(stderr, "\nGTK GUI command line arguments:\n\n");
+	conf_usage("plugins/hid_gtk", hid_usage_option);
+	fprintf(stderr, "\nInvocation: pcb-rnd --gui gtk [options]\n");
+	return 0;
+}
+
+	/* Create top level window for routines that will need top_window
+	   |  before ghid_create_pcb_widgets() is called.
+	 */
+void ghid_parse_arguments(int *argc, char ***argv)
+{
+	GtkWidget *window;
+	GdkPixbuf *icon;
+
+	ghid_config_init();
+
+	/* on windows we need to figure out the installation directory */
+#ifdef WIN32
+	char *tmps;
+	char *libdir;
+	tmps = g_win32_get_package_installation_directory(PACKAGE "-" VERSION, NULL);
+#define REST_OF_PATH G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S PACKAGE  G_DIR_SEPARATOR_S "pcblib"
+	libdir = (char *) malloc(strlen(tmps) + strlen(REST_OF_PATH) + 1);
+	sprintf(libdir, "%s%s", tmps, REST_OF_PATH);
+	free(tmps);
+
+#undef REST_OF_PATH
+
+#endif
+
+#if defined (DEBUG)
+	{
+		int i;
+		for (i = 0; i < *argc; i++)
+			printf("ghid_parse_arguments():  *argv[%d] = \"%s\"\n", i, (*argv)[i]);
+	}
+#endif
+
+	/* Threads aren't used in PCB, but this call would go here.
+	 */
+	/* g_thread_init (NULL); */
+
+#if defined (ENABLE_NLS)
+	/* Do our own setlocale() stufff since we want to override LC_NUMERIC
+	 */
+	gtk_set_locale();
+	setlocale(LC_NUMERIC, "C");		/* use decimal point instead of comma */
+#endif
+
+	conf_parse_arguments("plugins/hid_gtk/", argc, argv);
+
+	/*
+	 * Prevent gtk_init() and gtk_init_check() from automatically
+	 * calling setlocale (LC_ALL, "") which would undo LC_NUMERIC if ENABLE_NLS
+	 * We also don't want locale set if no ENABLE_NLS to keep "C" LC_NUMERIC.
+	 */
+	gtk_disable_setlocale();
+	gtk_init(argc, argv);
+
+
+	gport = &ghid_port;
+	gport->view.coord_per_px = 300.0;
+	pixel_slop = 300;
+
+	ghid_init_renderer(argc, argv, gport);
+
+#ifdef ENABLE_NLS
+#ifdef LOCALEDIR
+	bindtextdomain(PACKAGE, LOCALEDIR);
+#endif
+	textdomain(PACKAGE);
+	bind_textdomain_codeset(PACKAGE, "UTF-8");
+#endif /* ENABLE_NLS */
+
+	icon = gdk_pixbuf_new_from_xpm_data((const gchar **) icon_bits);
+	gtk_window_set_default_icon(icon);
+
+	window = gport->top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(window), "pcb-rnd");
+
+	wplc_place(WPLC_TOP, window);
+
+	gtk_widget_show_all(gport->top_window);
+	ghidgui->creating = TRUE;
+}
+
+static unsigned short int ghid_translate_key(const char *desc, int len)
+{
+	guint key;
+
+	if (strcasecmp(desc, "enter") == 0) desc = "Return";
+
+	key = gdk_keyval_from_name(desc);
+	if (key > 0xffff) {
+		Message(PCB_MSG_DEFAULT, "Ignoring invalid/exotic key sym: '%s'\n", desc);
+		return 0;
+	}
+	return key;
+}
+
+int ghid_key_name(unsigned short int key_char, char *out, int out_len)
+{
+	char *name = gdk_keyval_name(key_char);
+	if (name == NULL)
+		return -1;
+	strncpy(out, name, out_len);
+	out[out_len-1] = '\0';
+	return 0;
+}
+
+void ghid_do_export(HID_Attr_Val * options)
+{
+	gtkhid_begin();
+
+	hid_cfg_keys_init(&ghid_keymap);
+	ghid_keymap.translate_key = ghid_translate_key;
+	ghid_keymap.key_name = ghid_key_name;
+	ghid_keymap.auto_chr = 1;
+	ghid_keymap.auto_tr = hid_cfg_key_default_trans;
+
+	ghid_create_pcb_widgets();
+
+	/* These are needed to make sure the @layerpick and @layerview menus
+	 * are properly initialized and synchronized with the current PCB.
+	 */
+	ghid_layer_buttons_update();
+	ghid_main_menu_install_route_style_selector
+		(GHID_MAIN_MENU(ghidgui->menu_bar), GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+
+	if (conf_hid_gtk.plugins.hid_gtk.listen)
+		ghid_create_listener();
+
+	ghid_notify_gui_is_up();
+
+	event(EVENT_GUI_INIT, NULL);
+
+	gtk_main();
+	hid_cfg_keys_uninit(&ghid_keymap);
+	gtkhid_end();
+}
+
+void ghid_do_exit(HID *hid)
+{
+	gtk_main_quit();
+}
+
+void ghid_fullscreen_apply(void)
+{
+	if (conf_core.editor.fullscreen) {
+		gtk_widget_hide(ghidgui->left_toolbar);
+		gtk_widget_hide(ghidgui->top_hbox);
+		gtk_widget_hide(ghidgui->status_line_hbox);
+	}
+	else {
+		gtk_widget_show(ghidgui->left_toolbar);
+		gtk_widget_show(ghidgui->top_hbox);
+		gtk_widget_show(ghidgui->status_line_hbox);
+	}
+}
+
+/*! \brief callback for */
+static gboolean get_layer_visible_cb(int id)
+{
+	int visible;
+	layer_process(NULL, NULL, &visible, id);
+	return visible;
+}
+
+gint LayersChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!ghidgui || !ghidgui->menu_bar)
+		return 0;
+
+	ghid_config_groups_changed();
+	ghid_layer_buttons_update();
+	ghid_layer_selector_show_layers(GHID_LAYER_SELECTOR(ghidgui->layer_selector), get_layer_visible_cb);
+
+	/* FIXME - if a layer is moved it should retain its color.  But layers
+	   |  currently can't do that because color info is not saved in the
+	   |  pcb file.  So this makes a moved layer change its color to reflect
+	   |  the way it will be when the pcb is reloaded.
+	 */
+	pcb_colors_from_settings(PCB);
+	return 0;
+}
+
+static const char toggleview_syntax[] =
+	"ToggleView(1..MAXLAYER)\n" "ToggleView(layername)\n" "ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)";
+
+static const char toggleview_help[] = "Toggle the visibility of the specified layer or layer group.";
+
+/* %start-doc actions ToggleView
+
+If you pass an integer, that layer is specified by index (the first
+layer is @code{1}, etc).  If you pass a layer name, that layer is
+specified by name.  When a layer is specified, the visibility of the
+layer group containing that layer is toggled.
+
+If you pass a special layer name, the visibility of those components
+(silk, rats, etc) is toggled.  Note that if you have a layer named
+the same as a special layer, the layer is chosen over the special layer.
+
+%end-doc */
+
+static int ToggleView(int argc, const char **argv, Coord x, Coord y)
+{
+	int i, l;
+
+#ifdef DEBUG_MENUS
+	puts("Starting ToggleView().");
+#endif
+
+	if (argc == 0) {
+		AFAIL(toggleview);
+	}
+	if (isdigit((int) argv[0][0])) {
+		l = atoi(argv[0]) - 1;
+	}
+	else if (strcmp(argv[0], "Silk") == 0)
+		l = LAYER_BUTTON_SILK;
+	else if (strcmp(argv[0], "Rats") == 0)
+		l = LAYER_BUTTON_RATS;
+	else if (strcmp(argv[0], "Pins") == 0)
+		l = LAYER_BUTTON_PINS;
+	else if (strcmp(argv[0], "Vias") == 0)
+		l = LAYER_BUTTON_VIAS;
+	else if (strcmp(argv[0], "Mask") == 0)
+		l = LAYER_BUTTON_MASK;
+	else if (strcmp(argv[0], "BackSide") == 0)
+		l = LAYER_BUTTON_FARSIDE;
+	else {
+		l = -1;
+		for (i = 0; i < max_copper_layer + 2; i++)
+			if (strcmp(argv[0], PCB->Data->Layer[i].Name) == 0) {
+				l = i;
+				break;
+			}
+		if (l == -1) {
+			AFAIL(toggleview);
+		}
+
+	}
+
+	/* Now that we've figured out which toggle button ought to control
+	 * this layer, simply hit the button and let the pre-existing code deal
+	 */
+	ghid_layer_selector_toggle_layer(GHID_LAYER_SELECTOR(ghidgui->layer_selector), l);
+	return 0;
+}
+
+static const char selectlayer_syntax[] = "SelectLayer(1..MAXLAYER|Silk|Rats)";
+
+static const char selectlayer_help[] = "Select which layer is the current layer.";
+
+/* %start-doc actions SelectLayer
+
+The specified layer becomes the currently active layer.  It is made
+visible if it is not already visible
+
+%end-doc */
+
+static int SelectLayer(int argc, const char **argv, Coord x, Coord y)
+{
+	int newl;
+	if (argc == 0)
+		AFAIL(selectlayer);
+
+	if (strcasecmp(argv[0], "silk") == 0)
+		newl = LAYER_BUTTON_SILK;
+	else if (strcasecmp(argv[0], "rats") == 0)
+		newl = LAYER_BUTTON_RATS;
+	else
+		newl = atoi(argv[0]) - 1;
+
+#ifdef DEBUG_MENUS
+	printf("SelectLayer():  newl = %d\n", newl);
+#endif
+
+	/* Now that we've figured out which radio button ought to select
+	 * this layer, simply hit the button and let the pre-existing code deal
+	 */
+	ghid_layer_selector_select_layer(GHID_LAYER_SELECTOR(ghidgui->layer_selector), newl);
+
+	return 0;
+}
+
+
+HID_Action gtk_topwindow_action_list[] = {
+	{"LayersChanged", 0, LayersChanged,
+	 layerschanged_help, layerschanged_syntax}
+	,
+	{"SelectLayer", 0, SelectLayer,
+	 selectlayer_help, selectlayer_syntax}
+	,
+	{"ToggleView", 0, ToggleView,
+	 toggleview_help, toggleview_syntax}
+};
+
+REGISTER_ACTIONS(gtk_topwindow_action_list, ghid_cookie)
+
+static GtkWidget *ghid_load_menus(void)
+{
+	const lht_node_t *mr;
+	GtkWidget *menu_bar = NULL;
+	extern const char *hid_gtk_menu_default;
+
+	ghid_cfg = hid_cfg_load("gtk", 0, hid_gtk_menu_default);
+	if (ghid_cfg == NULL) {
+		Message(PCB_MSG_DEFAULT, "FATAL: can't load the gtk menu res either from file or from hardwired default.");
+		abort();
+	}
+
+	mr = hid_cfg_get_menu(ghid_cfg, "/main_menu");
+	if (mr != NULL) {
+		menu_bar = ghid_main_menu_new(G_CALLBACK(ghid_menu_cb));
+		ghid_main_menu_add_node(GHID_MAIN_MENU(menu_bar), mr);
+	}
+
+	mr = hid_cfg_get_menu(ghid_cfg, "/popups");
+	if (mr != NULL) {
+		if (mr->type == LHT_LIST) {
+			lht_node_t *n;
+			for(n = mr->data.list.first; n != NULL; n = n->next)
+				ghid_main_menu_add_popup_node(GHID_MAIN_MENU(menu_bar), n);
+		}
+		else
+			hid_cfg_error(mr, "/popups should be a list");
+	}
+
+#ifdef DEBUG_MENUS
+	puts("Finished loading menus.");
+#endif
+
+	mr = hid_cfg_get_menu(ghid_cfg, "/mouse");
+	if (hid_cfg_mouse_init(ghid_cfg, &ghid_mouse) != 0)
+		Message(PCB_MSG_DEFAULT, "Error: failed to load mouse actions from the hid config lihata - mouse input will not work.");
+
+	return menu_bar;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char adjuststyle_syntax[] = "AdjustStyle()\n";
+
+static const char adjuststyle_help[] = "Open the window which allows editing of the route styles.";
+
+/* %start-doc actions AdjustStyle
+
+Opens the window which allows editing of the route styles.
+
+%end-doc */
+
+static int AdjustStyle(int argc, const char **argv, Coord x, Coord y)
+{
+	if (argc > 1)
+		AFAIL(adjuststyle);
+
+	ghid_route_style_selector_edit_dialog(GHID_ROUTE_STYLE_SELECTOR(ghidgui->route_style_selector));
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char editlayergroups_syntax[] = "EditLayerGroups()\n";
+
+static const char editlayergroups_help[] = "Open the preferences window which allows editing of the layer groups.";
+
+/* %start-doc actions EditLayerGroups
+
+Opens the preferences window which is where the layer groups
+are edited.  This action is primarily provides to provide menu
+lht compatibility with the lesstif HID.
+
+%end-doc */
+
+static int EditLayerGroups(int argc, const char **argv, Coord x, Coord y)
+{
+
+	if (argc != 0)
+		AFAIL(editlayergroups);
+
+	hid_actionl("DoWindows", "Preferences", NULL);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+HID_Action ghid_menu_action_list[] = {
+	{"AdjustStyle", 0, AdjustStyle, adjuststyle_help, adjuststyle_syntax}
+	,
+	{"EditLayerGroups", 0, EditLayerGroups, editlayergroups_help, editlayergroups_syntax}
+};
+
+REGISTER_ACTIONS(ghid_menu_action_list, ghid_cookie)
diff --git a/src_plugins/hid_gtk/gui-utils.c b/src_plugins/hid_gtk/gui-utils.c
new file mode 100644
index 0000000..47ce04f
--- /dev/null
+++ b/src_plugins/hid_gtk/gui-utils.c
@@ -0,0 +1,745 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This module, gui-utils.c, was written by Bill Wilson and the functions
+ * here are Copyright (C) 2004 by Bill Wilson.  These functions are utility
+ * functions which are taken from my other GPL'd projects gkrellm and
+ * gstocks and are copied here for the Gtk PCB port.
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "gui.h"
+#include <gdk/gdkkeysyms.h>
+
+/* Not a gui function, but no better place to put it...
+ */
+gboolean dup_string(gchar ** dst, const gchar * src)
+{
+	if ((dst == NULL) || ((*dst == NULL) && (src == NULL)))
+		return FALSE;
+	if (*dst) {
+		if (src && !strcmp(*dst, src))
+			return FALSE;
+		g_free(*dst);
+	}
+	*dst = g_strdup(src);
+	return TRUE;
+}
+
+void free_glist_and_data(GList ** list_head)
+{
+	GList *list;
+
+	if (*list_head == NULL)
+		return;
+	for (list = *list_head; list; list = list->next)
+		if (list->data)
+			g_free(list->data);
+	g_list_free(*list_head);
+	*list_head = NULL;
+}
+
+
+gboolean ghid_is_modifier_key_sym(gint ksym)
+{
+	if (ksym == GDK_Shift_R || ksym == GDK_Shift_L || ksym == GDK_Control_R || ksym == GDK_Control_L)
+		return TRUE;
+	return FALSE;
+}
+
+
+ModifierKeysState ghid_modifier_keys_state(GdkModifierType * state)
+{
+	GdkModifierType mask;
+	ModifierKeysState mk;
+	gboolean shift, control, mod1;
+	GHidPort *out = &ghid_port;
+
+	if (!state)
+		gdk_window_get_pointer(gtk_widget_get_window(out->drawing_area), NULL, NULL, &mask);
+	else
+		mask = *state;
+
+	shift = (mask & GDK_SHIFT_MASK);
+	control = (mask & GDK_CONTROL_MASK);
+	mod1 = (mask & GDK_MOD1_MASK);
+
+	if (shift && !control && !mod1)
+		mk = SHIFT_PRESSED;
+	else if (!shift && control && !mod1)
+		mk = CONTROL_PRESSED;
+	else if (!shift && !control && mod1)
+		mk = MOD1_PRESSED;
+	else if (shift && control && !mod1)
+		mk = SHIFT_CONTROL_PRESSED;
+	else if (shift && !control && mod1)
+		mk = SHIFT_MOD1_PRESSED;
+	else if (!shift && control && mod1)
+		mk = CONTROL_MOD1_PRESSED;
+	else if (shift && control && mod1)
+		mk = SHIFT_CONTROL_MOD1_PRESSED;
+	else
+		mk = NONE_PRESSED;
+
+	return mk;
+}
+
+ButtonState ghid_button_state(GdkModifierType * state)
+{
+	GdkModifierType mask;
+	ButtonState bs;
+	gboolean button1, button2, button3;
+	GHidPort *out = &ghid_port;
+
+	if (!state) {
+		gdk_window_get_pointer(gtk_widget_get_window(out->drawing_area), NULL, NULL, &mask);
+	}
+	else
+		mask = *state;
+
+	extern GdkModifierType ghid_glob_mask;
+	ghid_glob_mask = mask;
+
+	button1 = (mask & GDK_BUTTON1_MASK);
+	button2 = (mask & GDK_BUTTON2_MASK);
+	button3 = (mask & GDK_BUTTON3_MASK);
+
+	if (button1)
+		bs = BUTTON1_PRESSED;
+	else if (button2)
+		bs = BUTTON2_PRESSED;
+	else if (button3)
+		bs = BUTTON3_PRESSED;
+	else
+		bs = NO_BUTTON_PRESSED;
+
+	return bs;
+}
+
+void ghid_draw_area_update(GHidPort * port, GdkRectangle * rect)
+{
+	gdk_window_invalidate_rect(gtk_widget_get_window(port->drawing_area), rect, FALSE);
+}
+
+
+const gchar *ghid_get_color_name(GdkColor * color)
+{
+	static char tmp[16];
+
+	if (!color)
+		return "#000000";
+
+	sprintf(tmp, "#%2.2x%2.2x%2.2x", (color->red >> 8) & 0xff, (color->green >> 8) & 0xff, (color->blue >> 8) & 0xff);
+	return tmp;
+}
+
+void ghid_map_color_string(const char *color_string, GdkColor * color)
+{
+	static GdkColormap *colormap = NULL;
+	GHidPort *out = &ghid_port;
+
+	if (!color || !out->top_window)
+		return;
+	if (colormap == NULL)
+		colormap = gtk_widget_get_colormap(out->top_window);
+	if (color->red || color->green || color->blue)
+		gdk_colormap_free_colors(colormap, color, 1);
+	gdk_color_parse(color_string, color);
+	gdk_color_alloc(colormap, color);
+}
+
+
+const gchar *ghid_entry_get_text(GtkWidget * entry)
+{
+	const gchar *s = "";
+
+	if (entry)
+		s = gtk_entry_get_text(GTK_ENTRY(entry));
+	while (*s == ' ' || *s == '\t')
+		++s;
+	return s;
+}
+
+
+
+void
+ghid_check_button_connected(GtkWidget * box,
+														GtkWidget ** button,
+														gboolean active,
+														gboolean pack_start,
+														gboolean expand,
+														gboolean fill,
+														gint pad, void (*cb_func) (GtkToggleButton *, gpointer), gpointer data, const gchar * string)
+{
+	GtkWidget *b;
+
+	if (string != NULL)
+		b = gtk_check_button_new_with_label(string);
+	else
+		b = gtk_check_button_new();
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b), active);
+	if (box && pack_start)
+		gtk_box_pack_start(GTK_BOX(box), b, expand, fill, pad);
+	else if (box && !pack_start)
+		gtk_box_pack_end(GTK_BOX(box), b, expand, fill, pad);
+
+	if (cb_func)
+		g_signal_connect(b, "clicked", G_CALLBACK(cb_func), data);
+	if (button)
+		*button = b;
+}
+
+void
+ghid_coord_entry(GtkWidget * box, GtkWidget ** coord_entry, Coord value,
+								 Coord low, Coord high, enum ce_step_size step_size, const Unit *u,
+								 gint width, void (*cb_func) (GHidCoordEntry *, gpointer), gpointer data, const gchar * string_pre, const gchar * string_post)
+{
+	GtkWidget *hbox = NULL, *label, *entry_widget;
+	GHidCoordEntry *entry;
+
+	if (u == NULL)
+		u = conf_core.editor.grid_unit;
+
+	if ((string_pre || string_post) && box) {
+		hbox = gtk_hbox_new(FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 2);
+		box = hbox;
+	}
+
+	entry_widget = ghid_coord_entry_new(low, high, value, u, step_size);
+	if (coord_entry)
+		*coord_entry = entry_widget;
+	if (width > 0)
+		gtk_widget_set_size_request(entry_widget, width, -1);
+	entry = GHID_COORD_ENTRY(entry_widget);
+	if (data == NULL)
+		data = (gpointer) entry;
+	if (cb_func)
+		g_signal_connect(G_OBJECT(entry_widget), "value_changed", G_CALLBACK(cb_func), data);
+	if (box) {
+		if (string_pre) {
+			label = gtk_label_new(string_pre);
+			gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 2);
+		}
+		gtk_box_pack_start(GTK_BOX(box), entry_widget, FALSE, FALSE, 2);
+		if (string_post) {
+			label = gtk_label_new(string_post);
+			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+			gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 2);
+		}
+	}
+}
+
+void
+ghid_spin_button(GtkWidget * box, GtkWidget ** spin_button, gfloat value,
+								 gfloat low, gfloat high, gfloat step0, gfloat step1,
+								 gint digits, gint width,
+								 void (*cb_func) (GtkSpinButton *, gpointer), gpointer data, gboolean right_align, const gchar * string)
+{
+	GtkWidget *hbox = NULL, *label, *spin_but;
+	GtkSpinButton *spin;
+	GtkAdjustment *adj;
+
+	if (string && box) {
+		hbox = gtk_hbox_new(FALSE, 0);
+		gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 2);
+		box = hbox;
+	}
+	adj = (GtkAdjustment *) gtk_adjustment_new(value, low, high, step0, step1, 0.0);
+	spin_but = gtk_spin_button_new(adj, 0.5, digits);
+	if (spin_button)
+		*spin_button = spin_but;
+	if (width > 0)
+		gtk_widget_set_size_request(spin_but, width, -1);
+	spin = GTK_SPIN_BUTTON(spin_but);
+	gtk_spin_button_set_numeric(spin, TRUE);
+	if (data == NULL)
+		data = (gpointer) spin;
+	if (cb_func)
+		g_signal_connect(G_OBJECT(spin_but), "value_changed", G_CALLBACK(cb_func), data);
+	if (box) {
+		if (right_align && string) {
+			label = gtk_label_new(string);
+			gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+			gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 2);
+		}
+		gtk_box_pack_start(GTK_BOX(box), spin_but, FALSE, FALSE, 2);
+		if (!right_align && string) {
+			label = gtk_label_new(string);
+			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+			gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 2);
+		}
+	}
+}
+
+void
+ghid_table_coord_entry(GtkWidget * table, gint row, gint column,
+											 GtkWidget ** coord_entry, Coord value,
+											 Coord low, Coord high, enum ce_step_size step_size,
+											 gint width, void (*cb_func) (GHidCoordEntry *, gpointer),
+											 gpointer data, gboolean right_align, const gchar * string)
+{
+	GtkWidget *label, *entry_widget;
+	GHidCoordEntry *entry;
+
+	if (!table)
+		return;
+
+	entry_widget = ghid_coord_entry_new(low, high, value, conf_core.editor.grid_unit, step_size);
+	if (coord_entry)
+		*coord_entry = entry_widget;
+	if (width > 0)
+		gtk_widget_set_size_request(entry_widget, width, -1);
+	entry = GHID_COORD_ENTRY(entry_widget);
+	if (data == NULL)
+		data = (gpointer) entry;
+	if (cb_func)
+		g_signal_connect(G_OBJECT(entry), "value_changed", G_CALLBACK(cb_func), data);
+
+	if (right_align) {
+		gtk_table_attach_defaults(GTK_TABLE(table), entry_widget, column + 1, column + 2, row, row + 1);
+		if (string) {
+			label = gtk_label_new(string);
+			gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+			gtk_table_attach_defaults(GTK_TABLE(table), label, column, column + 1, row, row + 1);
+		}
+	}
+	else {
+		gtk_table_attach_defaults(GTK_TABLE(table), entry_widget, column, column + 1, row, row + 1);
+		if (string) {
+			label = gtk_label_new(string);
+			gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+			gtk_table_attach_defaults(GTK_TABLE(table), label, column + 1, column + 2, row, row + 1);
+		}
+	}
+}
+
+
+void
+ghid_table_spin_button(GtkWidget * table, gint row, gint column,
+											 GtkWidget ** spin_button, gfloat value,
+											 gfloat low, gfloat high, gfloat step0, gfloat step1,
+											 gint digits, gint width,
+											 void (*cb_func) (GtkSpinButton *, gpointer), gpointer data, gboolean right_align, const gchar * string)
+{
+	GtkWidget *label, *spin_but;
+	GtkSpinButton *spin;
+	GtkAdjustment *adj;
+
+	if (!table)
+		return;
+
+	adj = (GtkAdjustment *) gtk_adjustment_new(value, low, high, step0, step1, 0.0);
+	spin_but = gtk_spin_button_new(adj, 0.5, digits);
+
+	if (spin_button)
+		*spin_button = spin_but;
+	if (width > 0)
+		gtk_widget_set_size_request(spin_but, width, -1);
+	spin = GTK_SPIN_BUTTON(spin_but);
+	gtk_spin_button_set_numeric(spin, TRUE);
+	if (data == NULL)
+		data = (gpointer) spin;
+	if (cb_func)
+		g_signal_connect(G_OBJECT(spin_but), "value_changed", G_CALLBACK(cb_func), data);
+
+	if (right_align) {
+		gtk_table_attach_defaults(GTK_TABLE(table), spin_but, column + 1, column + 2, row, row + 1);
+		if (string) {
+			label = gtk_label_new(string);
+			gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+			gtk_table_attach_defaults(GTK_TABLE(table), label, column, column + 1, row, row + 1);
+		}
+	}
+	else {
+		gtk_table_attach_defaults(GTK_TABLE(table), spin_but, column, column + 1, row, row + 1);
+		if (string) {
+			label = gtk_label_new(string);
+			gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+			gtk_table_attach_defaults(GTK_TABLE(table), label, column + 1, column + 2, row, row + 1);
+		}
+	}
+}
+
+void
+ghid_range_control(GtkWidget * box, GtkWidget ** scale_res,
+									 gboolean horizontal, GtkPositionType pos,
+									 gboolean set_draw_value, gint digits, gboolean pack_start,
+									 gboolean expand, gboolean fill, guint pad, gfloat value,
+									 gfloat low, gfloat high, gfloat step0, gfloat step1, void (*cb_func) (), gpointer data)
+{
+	GtkWidget *scale;
+	GtkAdjustment *adj;
+
+	adj = (GtkAdjustment *) gtk_adjustment_new(value, low, high, step0, step1, 0.0);
+
+	if (horizontal)
+		scale = gtk_hscale_new(GTK_ADJUSTMENT(adj));
+	else
+		scale = gtk_vscale_new(GTK_ADJUSTMENT(adj));
+	gtk_scale_set_value_pos(GTK_SCALE(scale), pos);
+	gtk_scale_set_draw_value(GTK_SCALE(scale), set_draw_value);
+	gtk_scale_set_digits(GTK_SCALE(scale), digits);
+
+	/* Increments don't make sense, use -1,1 because that does closest to
+	   |  what I want: scroll down decrements slider value.
+	 */
+	gtk_range_set_increments(GTK_RANGE(scale), -1, 1);
+
+	if (pack_start)
+		gtk_box_pack_start(GTK_BOX(box), scale, expand, fill, pad);
+	else
+		gtk_box_pack_end(GTK_BOX(box), scale, expand, fill, pad);
+
+	if (data == NULL)
+		data = (gpointer) adj;
+	if (cb_func)
+		g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(cb_func), data);
+	if (scale_res)
+		*scale_res = scale;
+}
+
+GtkWidget *ghid_scrolled_vbox(GtkWidget * box, GtkWidget ** scr, GtkPolicyType h_policy, GtkPolicyType v_policy)
+{
+	GtkWidget *scrolled, *vbox;
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), h_policy, v_policy);
+	gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0);
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), vbox);
+	if (scr)
+		*scr = scrolled;
+	return vbox;
+}
+
+/* frame_border_width - border around outside of frame.
+   |  vbox_pad - pad between widgets to be packed in returned vbox.
+   |  vbox_border_width - border between returned vbox and frame.
+*/
+GtkWidget *ghid_framed_vbox(GtkWidget * box, gchar * label, gint frame_border_width,
+														gboolean frame_expand, gint vbox_pad, gint vbox_border_width)
+{
+	GtkWidget *frame;
+	GtkWidget *vbox;
+
+	frame = gtk_frame_new(label);
+	gtk_container_set_border_width(GTK_CONTAINER(frame), frame_border_width);
+	gtk_box_pack_start(GTK_BOX(box), frame, frame_expand, frame_expand, 0);
+	vbox = gtk_vbox_new(FALSE, vbox_pad);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), vbox_border_width);
+	gtk_container_add(GTK_CONTAINER(frame), vbox);
+	return vbox;
+}
+
+GtkWidget *ghid_framed_vbox_end(GtkWidget * box, gchar * label, gint frame_border_width,
+																gboolean frame_expand, gint vbox_pad, gint vbox_border_width)
+{
+	GtkWidget *frame;
+	GtkWidget *vbox;
+
+	frame = gtk_frame_new(label);
+	gtk_container_set_border_width(GTK_CONTAINER(frame), frame_border_width);
+	gtk_box_pack_end(GTK_BOX(box), frame, frame_expand, frame_expand, 0);
+	vbox = gtk_vbox_new(FALSE, vbox_pad);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), vbox_border_width);
+	gtk_container_add(GTK_CONTAINER(frame), vbox);
+	return vbox;
+}
+
+GtkWidget *ghid_category_vbox(GtkWidget * box, const gchar * category_header,
+															gint header_pad, gint box_pad, gboolean pack_start, gboolean bottom_pad)
+{
+	GtkWidget *vbox, *vbox1, *hbox, *label;
+	gchar *s;
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	if (pack_start)
+		gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);
+	else
+		gtk_box_pack_end(GTK_BOX(box), vbox, FALSE, FALSE, 0);
+
+	if (category_header) {
+		label = gtk_label_new(NULL);
+		s = g_strconcat("<span weight=\"bold\">", category_header, "</span>", NULL);
+		gtk_label_set_markup(GTK_LABEL(label), s);
+		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, header_pad);
+		g_free(s);
+	}
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	label = gtk_label_new("     ");
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	vbox1 = gtk_vbox_new(FALSE, box_pad);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0);
+
+	if (bottom_pad) {
+		label = gtk_label_new("");
+		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	}
+	return vbox1;
+}
+
+GtkTreeSelection *ghid_scrolled_selection(GtkTreeView * treeview, GtkWidget * box,
+																					GtkSelectionMode s_mode,
+																					GtkPolicyType h_policy, GtkPolicyType v_policy,
+																					void (*func_cb) (GtkTreeSelection *, gpointer), gpointer data)
+{
+	GtkTreeSelection *selection;
+	GtkWidget *scrolled;
+
+	if (!box || !treeview)
+		return NULL;
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0);
+	gtk_container_add(GTK_CONTAINER(scrolled), GTK_WIDGET(treeview));
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), h_policy, v_policy);
+	selection = gtk_tree_view_get_selection(treeview);
+	gtk_tree_selection_set_mode(selection, s_mode);
+	if (func_cb)
+		g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(func_cb), data);
+	return selection;
+}
+
+GtkWidget *ghid_notebook_page(GtkWidget * tabs, const char *name, gint pad, gint border)
+{
+	GtkWidget *label;
+	GtkWidget *vbox;
+
+	vbox = gtk_vbox_new(FALSE, pad);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), border);
+
+	label = gtk_label_new(name);
+	gtk_notebook_append_page(GTK_NOTEBOOK(tabs), vbox, label);
+
+	return vbox;
+}
+
+GtkWidget *ghid_framed_notebook_page(GtkWidget * tabs, const char *name, gint border,
+																		 gint frame_border, gint vbox_pad, gint vbox_border)
+{
+	GtkWidget *vbox;
+
+	vbox = ghid_notebook_page(tabs, name, 0, border);
+	vbox = ghid_framed_vbox(vbox, NULL, frame_border, TRUE, vbox_pad, vbox_border);
+	return vbox;
+}
+
+void ghid_dialog_report(const gchar * title, const gchar * message)
+{
+	GtkWidget *top_win;
+	GtkWidget *dialog;
+	GtkWidget *content_area;
+	GtkWidget *scrolled;
+	GtkWidget *vbox, *vbox1;
+	GtkWidget *label;
+	const gchar *s;
+	gint nlines;
+	GHidPort *out = &ghid_port;
+
+	if (!message)
+		return;
+	top_win = out->top_window;
+	dialog = gtk_dialog_new_with_buttons(title ? title : "PCB",
+																			 GTK_WINDOW(top_win),
+																			 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_NONE, NULL);
+	g_signal_connect_swapped(GTK_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), GTK_OBJECT(dialog));
+	gtk_window_set_wmclass(GTK_WINDOW(dialog), "PCB_Dialog", "PCB");
+
+	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+	gtk_box_pack_start(GTK_BOX(content_area), vbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new(message);
+	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+
+	for (nlines = 0, s = message; *s; ++s)
+		if (*s == '\n')
+			++nlines;
+	if (nlines > 20) {
+		vbox1 = ghid_scrolled_vbox(vbox, &scrolled, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+		gtk_widget_set_size_request(scrolled, -1, 300);
+		gtk_box_pack_start(GTK_BOX(vbox1), label, FALSE, FALSE, 0);
+	}
+	else
+		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	gtk_widget_show_all(dialog);
+}
+
+
+void ghid_label_set_markup(GtkWidget * label, const gchar * text)
+{
+	if (label)
+		gtk_label_set_markup(GTK_LABEL(label), text ? text : "");
+}
+
+
+static void text_view_append(GtkWidget * view, const gchar * s)
+{
+	GtkTextIter iter;
+	GtkTextBuffer *buffer;
+	GtkTextMark *mark;
+
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
+	gtk_text_buffer_get_end_iter(buffer, &iter);
+	/* gtk_text_iter_forward_to_end(&iter); */
+
+	if (strncmp(s, "<b>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "bold", NULL);
+	else if (strncmp(s, "<i>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "italic", NULL);
+	else if (strncmp(s, "<h>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "heading", NULL);
+	else if (strncmp(s, "<c>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "center", NULL);
+	else if (strncmp(s, "<R>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "red", NULL);
+	else if (strncmp(s, "<G>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "green", NULL);
+	else if (strncmp(s, "<B>", 3) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 3, -1, "blue", NULL);
+	else if (strncmp(s, "<ul>", 4) == 0)
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s + 4, -1, "underline", NULL);
+	else
+		gtk_text_buffer_insert(buffer, &iter, s, -1);
+
+	mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
+	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(view), mark, 0, TRUE, 0.0, 1.0);
+	gtk_text_buffer_delete_mark(buffer, mark);
+}
+
+void ghid_text_view_append(GtkWidget * view, const gchar * string)
+{
+	static gchar *tag;
+	const gchar *s;
+
+	s = string;
+	if (*s == '<' && ((*(s + 2) == '>' && !*(s + 3)) || (*(s + 3) == '>' && !*(s + 4)))) {
+		tag = g_strdup(s);
+		return;
+	}
+
+	if (tag) {
+		char *concatenation;
+		concatenation = g_strconcat(tag, string, NULL);
+		text_view_append(view, concatenation);
+		g_free(concatenation);
+		g_free(tag);
+		tag = NULL;
+	}
+	else
+		text_view_append(view, string);
+}
+
+void ghid_text_view_append_strings(GtkWidget * view, const gchar ** string, gint n_strings)
+{
+	gchar *tag = NULL;
+	const gchar *s;
+	gint i;
+
+	for (i = 0; i < n_strings; ++i) {
+		s = string[i];
+		if (*s == '<' && ((*(s + 2) == '>' && !*(s + 3))
+											|| (*(s + 3) == '>' && !*(s + 4)))) {
+			tag = g_strdup(s);
+			continue;
+		}
+#if defined(ENABLE_NLS)
+		s = gettext(string[i]);
+#else
+		s = string[i];
+#endif
+		if (tag) {
+			char *concatenation;
+			concatenation = g_strconcat(tag, s, NULL);
+			text_view_append(view, concatenation);
+			g_free(concatenation);
+			g_free(tag);
+			tag = NULL;
+		}
+		else
+			text_view_append(view, s);
+	}
+}
+
+
+GtkWidget *ghid_scrolled_text_view(GtkWidget * box, GtkWidget ** scr, GtkPolicyType h_policy, GtkPolicyType v_policy)
+{
+	GtkWidget *scrolled, *view;
+	GtkTextBuffer *buffer;
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), h_policy, v_policy);
+	gtk_box_pack_start(GTK_BOX(box), scrolled, TRUE, TRUE, 0);
+
+	view = gtk_text_view_new();
+	gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
+	gtk_text_buffer_create_tag(buffer, "heading", "weight", PANGO_WEIGHT_BOLD, "size", 14 * PANGO_SCALE, NULL);
+	gtk_text_buffer_create_tag(buffer, "italic", "style", PANGO_STYLE_ITALIC, NULL);
+	gtk_text_buffer_create_tag(buffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
+	gtk_text_buffer_create_tag(buffer, "center", "justification", GTK_JUSTIFY_CENTER, NULL);
+	gtk_text_buffer_create_tag(buffer, "underline", "underline", PANGO_UNDERLINE_SINGLE, NULL);
+	gtk_text_buffer_create_tag(buffer, "red", "foreground", "#aa0000", NULL);
+	gtk_text_buffer_create_tag(buffer, "green", "foreground", "#00aa00", NULL);
+	gtk_text_buffer_create_tag(buffer, "blue", "foreground", "#0000aa", NULL);
+
+	gtk_container_add(GTK_CONTAINER(scrolled), view);
+	if (scr)
+		*scr = scrolled;
+	return view;
+}
+
+
+/* If src is not utf8, *dst is converted to utf8.
+ */
+gboolean utf8_dup_string(gchar ** dst_utf8, const gchar * src)
+{
+	if (!dst_utf8 || (!*dst_utf8 && !src))
+		return FALSE;
+	if (*dst_utf8) {
+		if (src && !strcmp(*dst_utf8, src))
+			return FALSE;
+		g_free(*dst_utf8);
+	}
+	if (src) {
+		if (g_utf8_validate(src, -1, NULL))
+			*dst_utf8 = g_strdup(src);
+		else {
+			*dst_utf8 = g_locale_to_utf8(src, -1, NULL, NULL, NULL);
+			if (!*dst_utf8)
+				*dst_utf8 = g_strdup(src);
+		}
+	}
+	else
+		*dst_utf8 = NULL;
+
+	return TRUE;
+}
diff --git a/src_plugins/hid_gtk/gui.h b/src_plugins/hid_gtk/gui.h
new file mode 100644
index 0000000..b309eb8
--- /dev/null
+++ b/src_plugins/hid_gtk/gui.h
@@ -0,0 +1,527 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* FIXME - rename this file to ghid.h */
+
+#ifndef PCB_HID_GTK_GHID_H
+#define PCB_HID_GTK_GHID_H
+
+#include "global.h"
+#include "hid.h"
+#include "hid_cfg.h"
+#include "hid_cfg_input.h"
+
+#include "data.h"
+#include "misc.h"
+
+#include <gtk/gtk.h>
+#include "ghid-coord-entry.h"
+#include "ghid-main-menu.h"
+#include "gui-pinout-preview.h"
+#include "ghid-propedit.h"
+#include "conf_core.h"
+#include "event.h"
+#include "compat_misc.h"
+
+#include "hid_gtk_conf.h"
+
+	/* Silk and rats lines are the two additional selectable to draw on.
+	   |  gui code in gui-top-window.c and group code in misc.c must agree
+	   |  on what layer is what!
+	 */
+#define	LAYER_BUTTON_SILK			MAX_LAYER
+#define	LAYER_BUTTON_RATS			(MAX_LAYER + 1)
+#define	N_SELECTABLE_LAYER_BUTTONS	(LAYER_BUTTON_RATS + 1)
+
+#define LAYER_BUTTON_PINS			(MAX_LAYER + 2)
+#define LAYER_BUTTON_VIAS			(MAX_LAYER + 3)
+#define LAYER_BUTTON_FARSIDE		(MAX_LAYER + 4)
+#define LAYER_BUTTON_MASK			(MAX_LAYER + 5)
+#define N_LAYER_BUTTONS				(MAX_LAYER + 6)
+
+	/* Go from from the grid units in use (millimeters or mils) to PCB units
+	   |  and back again.
+	   |  PCB keeps values internally higher precision, but gui
+	   |  widgets (spin buttons, labels, etc) need mils or millimeters.
+	 */
+#define	FROM_PCB_UNITS(v)	coord_to_unit (conf_core.editor.grid_unit, v)
+#define	TO_PCB_UNITS(v)		unit_to_coord (conf_core.editor.grid_unit, v)
+
+#define SIDE_X(x)         ((conf_core.editor.view.flip_x ? PCB->MaxWidth - (x) : (x)))
+#define SIDE_Y(y)         ((conf_core.editor.view.flip_y ? PCB->MaxHeight - (y) : (y)))
+
+#define	DRAW_X(x)         (gint)((SIDE_X(x) - gport->view.x0) / gport->view.coord_per_px)
+#define	DRAW_Y(y)         (gint)((SIDE_Y(y) - gport->view.y0) / gport->view.coord_per_px)
+
+#define	EVENT_TO_PCB_X(x) SIDE_X((gint)((x) * gport->view.coord_per_px + gport->view.x0))
+#define	EVENT_TO_PCB_Y(y) SIDE_Y((gint)((y) * gport->view.coord_per_px + gport->view.y0))
+
+/*
+ * Used to intercept "special" hotkeys that gtk doesn't usually pass
+ * on to the menu hotkeys.  We catch them and put them back where we
+ * want them.
+ */
+
+/* The modifier keys */
+
+/* The actual keys */
+#define GHID_KEY_TAB      0x81
+#define GHID_KEY_UP       0x82
+#define GHID_KEY_DOWN     0x83
+#define GHID_KEY_LEFT     0x84
+#define GHID_KEY_RIGHT    0x85
+
+typedef struct {
+	GtkActionGroup *main_actions, *change_selected_actions, *displayed_name_actions;
+
+	  GtkWidget
+		* status_line_label,
+		*cursor_position_relative_label, *cursor_position_absolute_label, *grid_units_label, *status_line_hbox, *command_combo_box;
+	GtkEntry *command_entry;
+
+	GtkWidget *top_hbox, *top_bar_background, *menu_hbox, *position_hbox, *menubar_toolbar_vbox, *mode_buttons_frame;
+	GtkWidget *left_toolbar;
+	GtkWidget *grid_units_button;
+	GtkWidget *menu_bar, *layer_selector, *route_style_selector;
+	GtkWidget *mode_toolbar;
+	GtkWidget *mode_toolbar_vbox;
+	GtkWidget *vbox_middle;
+
+	GtkWidget *info_bar;
+	GTimeVal our_mtime;
+	GTimeVal last_seen_mtime;
+
+	GtkWidget *h_range, *v_range;
+	GtkObject *h_adjustment, *v_adjustment;
+
+	GdkPixbuf *bg_pixbuf;
+
+	gchar *name_label_string;
+
+	gboolean adjustment_changed_holdoff, command_entry_status_line_active, in_popup;
+
+	gboolean small_label_markup, creating;
+
+	gint settings_mode;
+
+	ghid_propedit_dialog_t propedit_dlg;
+	GtkWidget *propedit_widget;
+	const char *(*propedit_query)(void *pe, const char *cmd, const char *key, const char *val, int idx);
+	void *propedit_pe;
+} GhidGui;
+
+extern GhidGui _ghidgui, *ghidgui;
+
+typedef struct {
+	double coord_per_px;					/* Zoom level described as PCB units per screen pixel */
+
+	Coord x0;
+	Coord y0;
+	Coord width;
+	Coord height;
+
+} view_data;
+
+	/* The output viewport
+	 */
+typedef struct {
+	GtkWidget *top_window,				/* toplevel widget              */
+	 *drawing_area;								/* and its drawing area */
+	GdkPixmap *pixmap, *mask;
+	GdkDrawable *drawable;				/* Current drawable for drawing routines */
+	gint width, height;
+
+	struct render_priv *render_priv;
+
+	GdkColor bg_color, offlimits_color, grid_color;
+
+	GdkColormap *colormap;
+
+	GdkCursor *X_cursor;					/* used X cursor */
+	GdkCursorType X_cursor_shape;	/* and its shape */
+
+	gboolean has_entered;
+	gboolean panning;
+
+	view_data view;
+	Coord pcb_x, pcb_y;						/* PCB coordinates of the mouse pointer */
+	Coord crosshair_x, crosshair_y;	/* PCB coordinates of the crosshair     */
+} GHidPort;
+
+extern GHidPort ghid_port, *gport;
+
+typedef enum {
+	NONE_PRESSED = 0,
+	SHIFT_PRESSED = M_Shift,
+	CONTROL_PRESSED = M_Ctrl,
+	MOD1_PRESSED = M_Mod1,
+	SHIFT_CONTROL_PRESSED = M_Shift | M_Ctrl,
+	SHIFT_MOD1_PRESSED = M_Shift | M_Mod1,
+	CONTROL_MOD1_PRESSED = M_Ctrl | M_Mod1,
+	SHIFT_CONTROL_MOD1_PRESSED = M_Shift | M_Ctrl | M_Mod1
+} ModifierKeysState;
+
+typedef enum {
+	NO_BUTTON_PRESSED,
+	BUTTON1_PRESSED,
+	BUTTON2_PRESSED,
+	BUTTON3_PRESSED
+} ButtonState;
+
+/* Function prototypes
+*/
+void ghid_parse_arguments(gint * argc, gchar *** argv);
+void ghid_do_export(HID_Attr_Val * options);
+void ghid_do_exit(HID *hid);
+
+void ghid_create_pcb_widgets(void);
+void ghid_window_set_name_label(gchar * name);
+void ghid_interface_set_sensitive(gboolean sensitive);
+void ghid_interface_input_signals_connect(void);
+void ghid_interface_input_signals_disconnect(void);
+
+void ghid_pcb_saved_toggle_states_set(void);
+void ghid_sync_with_new_layout(void);
+
+void ghid_change_selected_update_menu_actions(void);
+
+void ghid_config_window_show();
+void ghid_config_handle_units_changed(void);
+void ghid_config_start_backup_timer(void);
+void ghid_config_text_scale_update(void);
+void ghid_config_layer_name_update(gchar * name, gint layer);
+void ghid_config_groups_changed(void);
+
+void ghid_config_init(void);
+void ghid_wgeo_save(int save_to_file, int skip_user);
+void ghid_conf_save_pre_wgeo(void *user_data, int argc, event_arg_t * argv[]);
+void ghid_conf_load_post_wgeo(void *user_data, int argc, event_arg_t * argv[]);
+
+void ghid_mode_buttons_update(void);
+void ghid_pack_mode_buttons(void);
+void ghid_layer_buttons_update(void);
+void ghid_layer_buttons_color_update(void);
+
+
+/* gui-misc.c function prototypes
+*/
+void ghid_status_line_set_text(const gchar * text);
+void ghid_cursor_position_label_set_text(gchar * text);
+void ghid_cursor_position_relative_label_set_text(gchar * text);
+
+void ghid_hand_cursor(void);
+void ghid_point_cursor(void);
+void ghid_watch_cursor(void);
+void ghid_mode_cursor(gint mode);
+void ghid_corner_cursor(void);
+void ghid_restore_cursor(void);
+void ghid_get_user_xy(const gchar * msg);
+void ghid_create_abort_dialog(gchar *);
+gboolean ghid_check_abort(void);
+void ghid_end_abort(void);
+void ghid_get_pointer(gint *, gint *);
+
+
+/* gui-output-events.c function prototypes.
+*/
+void ghid_port_ranges_changed(void);
+void ghid_port_ranges_scale(void);
+
+void ghid_note_event_location(GdkEventButton * ev);
+gboolean ghid_port_key_press_cb(GtkWidget * drawing_area, GdkEventKey * kev, gpointer data);
+gboolean ghid_port_key_release_cb(GtkWidget * drawing_area, GdkEventKey * kev, gpointer data);
+gboolean ghid_port_button_press_cb(GtkWidget * drawing_area, GdkEventButton * ev, gpointer data);
+gboolean ghid_port_button_release_cb(GtkWidget * drawing_area, GdkEventButton * ev, gpointer data);
+
+
+gint ghid_port_window_enter_cb(GtkWidget * widget, GdkEventCrossing * ev, GHidPort * out);
+gint ghid_port_window_leave_cb(GtkWidget * widget, GdkEventCrossing * ev, GHidPort * out);
+gint ghid_port_window_motion_cb(GtkWidget * widget, GdkEventMotion * ev, GHidPort * out);
+gint ghid_port_window_mouse_scroll_cb(GtkWidget * widget, GdkEventScroll * ev, GHidPort * out);
+
+
+gint ghid_port_drawing_area_configure_event_cb(GtkWidget * widget, GdkEventConfigure * ev, GHidPort * out);
+
+
+/* gui-dialog.c function prototypes.
+*/
+#define		GUI_DIALOG_RESPONSE_ALL	1
+
+gchar *ghid_dialog_file_select_open(const gchar * title, gchar ** path, const gchar * shortcuts);
+gchar *ghid_dialog_file_select_save(const gchar * title, gchar ** path, const gchar * file, const gchar * shortcuts, const char **formats, int *format);
+void ghid_dialog_message(gchar * message);
+gboolean ghid_dialog_confirm(const gchar * message, const gchar * cancelmsg, const gchar * okmsg);
+int ghid_dialog_close_confirm(void);
+#define GUI_DIALOG_CLOSE_CONFIRM_CANCEL 0
+#define GUI_DIALOG_CLOSE_CONFIRM_NOSAVE 1
+#define GUI_DIALOG_CLOSE_CONFIRM_SAVE   2
+gint ghid_dialog_confirm_all(gchar * message);
+gchar *ghid_dialog_input(const char *prompt, const char *initial);
+void ghid_dialog_about(void);
+
+char *ghid_fileselect(const char *, const char *, const char *, const char *, const char *, int);
+
+
+/* gui-dialog-print.c */
+void ghid_dialog_export(void);
+void ghid_dialog_print(HID *);
+
+int ghid_attribute_dialog(HID_Attribute *, int, HID_Attr_Val *, const char *, const char *);
+
+/* gui-drc-window.c */
+void ghid_drc_window_show(gboolean raise);
+void ghid_drc_window_reset_message(void);
+void ghid_drc_window_append_violation(DrcViolationType * violation);
+void ghid_drc_window_append_messagev(const char *fmt, va_list va);
+int ghid_drc_window_throw_dialog(void);
+
+/* In gui-top-window.c  */
+void ghid_update_toggle_flags(void);
+void ghid_notify_save_pcb(const char *file, pcb_bool done);
+void ghid_notify_filename_changed(void);
+void ghid_install_accel_groups(GtkWindow * window, GhidGui * gui);
+void ghid_remove_accel_groups(GtkWindow * window, GhidGui * gui);
+void make_route_style_buttons(GHidRouteStyleSelector * rss);
+
+/* gui-utils.c
+*/
+gboolean dup_string(gchar ** dst, const gchar * src);
+gboolean utf8_dup_string(gchar ** dst_utf8, const gchar * src);
+void free_glist_and_data(GList ** list_head);
+
+ModifierKeysState ghid_modifier_keys_state(GdkModifierType * state);
+ButtonState ghid_button_state(GdkModifierType * state);
+gboolean ghid_is_modifier_key_sym(gint ksym);
+gboolean ghid_control_is_pressed(void);
+gboolean ghid_mod1_is_pressed(void);
+gboolean ghid_shift_is_pressed(void);
+
+void ghid_draw_area_update(GHidPort * out, GdkRectangle * rect);
+const gchar *ghid_get_color_name(GdkColor * color);
+void ghid_map_color_string(const gchar * color_string, GdkColor * color);
+const gchar *ghid_entry_get_text(GtkWidget * entry);
+void ghid_check_button_connected(GtkWidget * box, GtkWidget ** button,
+																 gboolean active, gboolean pack_start,
+																 gboolean expand, gboolean fill, gint pad,
+																 void (*cb_func) (GtkToggleButton *, gpointer), gpointer data, const gchar * string);
+void ghid_coord_entry(GtkWidget * box, GtkWidget ** coord_entry, Coord value,
+											Coord low, Coord high, enum ce_step_size step_size, const Unit *u,
+											gint width, void (*cb_func) (GHidCoordEntry *, gpointer),
+											gpointer data, const gchar * string_pre, const gchar * string_post);
+void ghid_spin_button(GtkWidget * box, GtkWidget ** spin_button,
+											gfloat value, gfloat low, gfloat high, gfloat step0,
+											gfloat step1, gint digits, gint width,
+											void (*cb_func) (GtkSpinButton *, gpointer), gpointer data, gboolean right_align, const gchar * string);
+void ghid_table_coord_entry(GtkWidget * table, gint row, gint column,
+														GtkWidget ** coord_entry, Coord value,
+														Coord low, Coord high, enum ce_step_size, gint width,
+														void (*cb_func) (GHidCoordEntry *, gpointer), gpointer data, gboolean right_align, const gchar * string);
+void ghid_table_spin_button(GtkWidget * box, gint row, gint column,
+														GtkWidget ** spin_button, gfloat value,
+														gfloat low, gfloat high, gfloat step0,
+														gfloat step1, gint digits, gint width,
+														void (*cb_func) (GtkSpinButton *, gpointer), gpointer data, gboolean right_align, const gchar * string);
+
+void ghid_range_control(GtkWidget * box, GtkWidget ** scale_res,
+												gboolean horizontal, GtkPositionType pos,
+												gboolean set_draw_value, gint digits,
+												gboolean pack_start, gboolean expand, gboolean fill,
+												guint pad, gfloat value, gfloat low, gfloat high,
+												gfloat step0, gfloat step1, void (*cb_func) (), gpointer data);
+GtkWidget *ghid_scrolled_vbox(GtkWidget * box, GtkWidget ** scr, GtkPolicyType h_policy, GtkPolicyType v_policy);
+GtkWidget *ghid_framed_vbox(GtkWidget * box, gchar * label,
+														gint frame_border_width, gboolean frame_expand, gint vbox_pad, gint vbox_border_width);
+GtkWidget *ghid_framed_vbox_end(GtkWidget * box, gchar * label,
+																gint frame_border_width, gboolean frame_expand, gint vbox_pad, gint vbox_border_width);
+GtkWidget *ghid_category_vbox(GtkWidget * box, const gchar * category_header,
+															gint header_pad, gint box_pad, gboolean pack_start, gboolean bottom_pad);
+GtkWidget *ghid_notebook_page(GtkWidget * tabs, const char *name, gint pad, gint border);
+GtkWidget *ghid_framed_notebook_page(GtkWidget * tabs, const char *name,
+																		 gint border, gint frame_border, gint vbox_pad, gint vbox_border);
+GtkWidget *ghid_scrolled_text_view(GtkWidget * box, GtkWidget ** scr, GtkPolicyType h_policy, GtkPolicyType v_policy);
+void ghid_text_view_append(GtkWidget * view, const gchar * string);
+void ghid_text_view_append_strings(GtkWidget * view, const gchar ** string, gint n_strings);
+GtkTreeSelection *ghid_scrolled_selection(GtkTreeView * treeview,
+																					GtkWidget * box,
+																					GtkSelectionMode s_mode,
+																					GtkPolicyType h_policy,
+																					GtkPolicyType v_policy,
+																					void (*func_cb) (GtkTreeSelection *, gpointer), gpointer data);
+
+void ghid_dialog_report(const gchar * title, const gchar * message);
+void ghid_label_set_markup(GtkWidget * label, const gchar * text);
+
+void ghid_set_cursor_position_labels(void);
+void ghid_set_status_line_label(void);
+
+
+/* gui-netlist-window.c */
+void ghid_netlist_window_create(GHidPort * out);
+void ghid_netlist_window_show(GHidPort * out, gboolean raise);
+void ghid_netlist_window_update(gboolean init_nodes);
+
+LibraryMenuType *ghid_get_net_from_node_name(const gchar * name, gboolean);
+void ghid_netlist_highlight_node(const gchar * name);
+
+
+/* gui-command-window.c */
+void ghid_handle_user_command(gboolean raise);
+void ghid_command_window_show(gboolean raise);
+gchar *ghid_command_entry_get(const gchar * prompt, const gchar * command);
+void ghid_command_use_command_window_sync(void);
+
+/* gui-keyref-window.c */
+void ghid_keyref_window_show(gboolean raise);
+
+/* gui-library-window.c */
+void ghid_library_window_create(GHidPort * out);
+void ghid_library_window_show(GHidPort * out, gboolean raise);
+
+
+/* gui-log-window.c */
+void ghid_log_window_create();
+void ghid_log_window_show(gboolean raise);
+void ghid_log(const char *fmt, ...);
+void ghid_logv(enum pcb_message_level level, const char *fmt, va_list args);
+
+/* gui-pinout-window.c */
+void ghid_pinout_window_show(GHidPort * out, ElementTypePtr Element);
+
+/* gtkhid-gdk.c AND gtkhid-gl.c */
+int ghid_set_layer(const char *name, int group, int empty);
+hidGC ghid_make_gc(void);
+void ghid_destroy_gc(hidGC);
+void ghid_use_mask(int use_it);
+void ghid_set_color(hidGC gc, const char *name);
+void ghid_set_line_cap(hidGC gc, EndCapStyle style);
+void ghid_set_line_width(hidGC gc, Coord width);
+void ghid_set_draw_xor(hidGC gc, int _xor);
+void ghid_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+void ghid_draw_arc(hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle);
+void ghid_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+void ghid_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius);
+void ghid_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y);
+void ghid_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
+void ghid_invalidate_lr(int left, int right, int top, int bottom);
+void ghid_invalidate_all();
+void ghid_notify_crosshair_change(pcb_bool changes_complete);
+void ghid_notify_mark_change(pcb_bool changes_complete);
+void ghid_init_renderer(int *, char ***, GHidPort *);
+void ghid_shutdown_renderer(GHidPort *);
+void ghid_init_drawing_widget(GtkWidget * widget, GHidPort *);
+void ghid_drawing_area_configure_hook(GHidPort * port);
+void ghid_screen_update(void);
+gboolean ghid_drawing_area_expose_cb(GtkWidget *, GdkEventExpose *, GHidPort *);
+void ghid_port_drawing_realize_cb(GtkWidget *, gpointer);
+gboolean ghid_pinout_preview_expose(GtkWidget * widget, GdkEventExpose * ev);
+GdkPixmap *ghid_render_pixmap(int cx, int cy, double zoom, int width, int height, int depth);
+HID *ghid_request_debug_draw(void);
+void ghid_flush_debug_draw(void);
+void ghid_finish_debug_draw(void);
+pcb_bool ghid_event_to_pcb_coords(int event_x, int event_y, Coord * pcb_x, Coord * pcb_y);
+pcb_bool ghid_pcb_to_event_coords(Coord pcb_x, Coord pcb_y, int *event_x, int *event_y);
+
+void ghid_lead_user_to_location(Coord x, Coord y);
+void ghid_cancel_lead_user(void);
+
+/* gtkhid-main.c */
+void ghid_pan_view_rel(Coord dx, Coord dy);
+void ghid_get_coords(const char *msg, Coord * x, Coord * y);
+gint PCBChanged(int argc, const char **argv, Coord x, Coord y);
+
+
+
+
+extern GdkPixmap *XC_hand_source, *XC_hand_mask;
+extern GdkPixmap *XC_lock_source, *XC_lock_mask;
+extern GdkPixmap *XC_clock_source, *XC_clock_mask;
+
+
+/* Coordinate conversions */
+/* Px converts view->pcb, Vx converts pcb->view */
+static inline int Vx(Coord x)
+{
+	double rv;
+	if (conf_core.editor.view.flip_x)
+		rv = (PCB->MaxWidth - x - gport->view.x0) / gport->view.coord_per_px + 0.5;
+	else
+		rv = (x - gport->view.x0) / gport->view.coord_per_px + 0.5;
+	return pcb_round(rv);
+}
+
+static inline int Vy(Coord y)
+{
+	double rv;
+	if (conf_core.editor.view.flip_y)
+		rv = (PCB->MaxHeight - y - gport->view.y0) / gport->view.coord_per_px + 0.5;
+	else
+		rv = (y - gport->view.y0) / gport->view.coord_per_px + 0.5;
+	return pcb_round(rv);
+}
+
+static inline int Vz(Coord z)
+{
+	return pcb_round((double)z / gport->view.coord_per_px + 0.5);
+}
+
+static inline Coord Px(int x)
+{
+	Coord rv = x * gport->view.coord_per_px + gport->view.x0;
+	if (conf_core.editor.view.flip_x)
+		rv = PCB->MaxWidth - (x * gport->view.coord_per_px + gport->view.x0);
+	return rv;
+}
+
+static inline Coord Py(int y)
+{
+	Coord rv = y * gport->view.coord_per_px + gport->view.y0;
+	if (conf_core.editor.view.flip_y)
+		rv = PCB->MaxHeight - (y * gport->view.coord_per_px + gport->view.y0);
+	return rv;
+}
+
+static inline Coord Pz(int z)
+{
+	return (z * gport->view.coord_per_px);
+}
+
+extern const char *ghid_cookie;
+extern const char *ghid_menu_cookie;
+extern hid_cfg_mouse_t ghid_mouse;
+extern hid_cfg_keys_t ghid_keymap;
+extern int ghid_wheel_zoom;
+
+int ghid_usage(const char *topic);
+void hid_gtk_wgeo_update(void);
+void config_color_button_update(conf_native_t *cfg, int idx);
+
+void ghid_confchg_line_refraction(conf_native_t *cfg);
+void ghid_confchg_all_direction_lines(conf_native_t *cfg);
+void ghid_confchg_fullscreen(conf_native_t *cfg);
+void ghid_confchg_checkbox(conf_native_t *cfg);
+void ghid_draw_grid_local(Coord cx, Coord cy);
+
+void ghid_fullscreen_apply(void);
+
+GMainLoop *ghid_entry_loop;
+
+#endif /* PCB_HID_GTK_GHID_GUI_H */
diff --git a/src_plugins/hid_gtk/hid_gtk_conf.h b/src_plugins/hid_gtk/hid_gtk_conf.h
new file mode 100644
index 0000000..b0b1568
--- /dev/null
+++ b/src_plugins/hid_gtk/hid_gtk_conf.h
@@ -0,0 +1,96 @@
+#ifndef PCB_HID_GTK_CONF_H
+#define PCB_HID_GTK_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct hid_gtk {
+			CFT_BOOLEAN listen;                          /* @usage Listen for actions on stdin. */
+			CFT_STRING  bg_image;                        /* @usage File name of an image to put into the background of the GUI canvas. The image must be a color PPM image, in binary (not ASCII) format. It can be any size, and will be automatically scaled to fit the canvas. */
+
+			CFT_BOOLEAN compact_horizontal;
+			CFT_BOOLEAN compact_vertical;
+			CFT_BOOLEAN use_command_window;
+			CFT_INTEGER history_size;
+			CFT_INTEGER n_mode_button_columns;
+
+			const struct local_grid {
+				CFT_BOOLEAN enable;                       /* enable local grid to draw grid points only in a small radius around the crosshair - speeds up software rendering on large screens */
+				CFT_INTEGER radius;                       /* radius, in number of grid points, around the local grid */
+			} local_grid;
+
+			const struct global_grid {
+				CFT_INTEGER min_dist_px;                  /* never try to draw a grid so dense that the disatance between grid points is smaller than this */
+				CFT_BOOLEAN sparse;                       /* enable drawing sparse grid: when zoomed out byeond min_dist_px draw every 2nd, 4th, 8th, etc. grid point; if disabled the grid is turned off when it'd get too dense */
+			} global_grid;
+
+			const struct auto_save_window_geometry {
+				CFT_BOOLEAN to_design;
+				CFT_BOOLEAN to_project;
+				CFT_BOOLEAN to_user;
+			} auto_save_window_geometry;
+
+			const struct window_geometry {
+				CFT_INTEGER top_x;
+				CFT_INTEGER top_y;
+				CFT_INTEGER top_width;
+				CFT_INTEGER top_height;
+
+				CFT_INTEGER log_x;
+				CFT_INTEGER log_y;
+				CFT_INTEGER log_width;
+				CFT_INTEGER log_height;
+
+				CFT_INTEGER drc_x;
+				CFT_INTEGER drc_y;
+				CFT_INTEGER drc_width;
+				CFT_INTEGER drc_height;
+
+				CFT_INTEGER library_x;
+				CFT_INTEGER library_y;
+				CFT_INTEGER library_width;
+				CFT_INTEGER library_height;
+
+				CFT_INTEGER keyref_x;
+				CFT_INTEGER keyref_y;
+				CFT_INTEGER keyref_width;
+				CFT_INTEGER keyref_height;
+
+				CFT_INTEGER netlist_x;
+				CFT_INTEGER netlist_y;
+				CFT_INTEGER netlist_height;
+				CFT_INTEGER netlist_width;
+
+				CFT_INTEGER pinout_x;
+				CFT_INTEGER pinout_y;
+				CFT_INTEGER pinout_height;
+				CFT_INTEGER pinout_width;
+			} window_geometry;
+		} hid_gtk;
+	} plugins;
+} conf_hid_gtk_t;
+
+#define GHID_WGEO1(win, op, arg) \
+	op(win ## _x, arg); \
+	op(win ## _y, arg); \
+	op(win ## _width, arg); \
+	op(win ## _height, arg);
+
+/* Call macro op(field_name, arg) for each window geometry field */
+#define GHID_WGEO_ALL(op, arg) \
+do { \
+	GHID_WGEO1(top, op, arg); \
+	GHID_WGEO1(log, op, arg); \
+	GHID_WGEO1(drc, op, arg); \
+	GHID_WGEO1(library, op, arg); \
+	GHID_WGEO1(keyref, op, arg); \
+	GHID_WGEO1(netlist, op, arg); \
+	GHID_WGEO1(pinout, op, arg); \
+} while(0)
+
+typedef struct window_geometry window_geometry_t;
+extern window_geometry_t hid_gtk_wgeo;
+extern conf_hid_gtk_t conf_hid_gtk;
+
+#endif
diff --git a/src_plugins/hid_gtk/pcb.rc b/src_plugins/hid_gtk/pcb.rc
new file mode 100644
index 0000000..4f54486
--- /dev/null
+++ b/src_plugins/hid_gtk/pcb.rc
@@ -0,0 +1 @@
+1 ICON "pcb_icon.ico"
diff --git a/src_plugins/hid_gtk/win_place.c b/src_plugins/hid_gtk/win_place.c
new file mode 100644
index 0000000..8404e0c
--- /dev/null
+++ b/src_plugins/hid_gtk/win_place.c
@@ -0,0 +1,108 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "win_place.h"
+
+#define CONF_PREFIX "plugins/hid_gtk/window_geometry/"
+static const char *conf_prefix[WPLC_max] = { /* order DOES matter */
+	"top_",
+	"log_",
+	"drc_",
+	"library_",
+	"netlist_",
+	"keyref_",
+	"pinout_",
+	"search_"
+};
+
+static GtkWidget *wplc_windows[WPLC_max];
+
+/* true if the given configuration item has exactly one integer value */
+#define HAVE(native) ((native != NULL) && ((native)->used == 1) && ((native)->type == CFN_INTEGER))
+
+void wplc_place(wplc_win_t id, GtkWidget *new_win)
+{
+	char path[128], *pe;
+	conf_native_t *nx, *ny, *nw, *nh;
+	GtkWidget *win;
+
+	if (!conf_core.editor.auto_place)
+		return; /* feature disabled */
+
+	if ((id < 0) || (id >= WPLC_max))
+		return; /* invalid window */
+
+	/* build base path for the specific window */
+	pe = path;
+	strcpy(pe, CONF_PREFIX);      pe += strlen(CONF_PREFIX);
+	strcpy(pe, conf_prefix[id]);  pe += strlen(conf_prefix[id]);
+
+	/* query each parameter */
+	strcpy(pe, "height"); nh = conf_get_field(path);
+	strcpy(pe, "width");  nw = conf_get_field(path);
+	strcpy(pe, "x");      nx = conf_get_field(path);
+	strcpy(pe, "y");      ny = conf_get_field(path);
+
+	if (new_win != NULL) {
+		wplc_windows[id] = new_win;
+		win = new_win;
+		/* for new windows set hint */
+		if (HAVE(nw) && HAVE(nh))
+			gtk_window_set_default_size(GTK_WINDOW(win), nw->val.integer[0], nh->val.integer[0]);
+		if (HAVE(nx) && HAVE(ny))
+			gtk_window_move(GTK_WINDOW(win), nx->val.integer[0], ny->val.integer[0]);
+		else
+			gtk_window_move(GTK_WINDOW(win), 10, 10); /* original behaviour */
+	}
+	else {
+		win = wplc_windows[id];
+		if (HAVE(nw) && HAVE(nh))
+			gtk_window_resize(GTK_WINDOW(win), nw->val.integer[0], nh->val.integer[0]);
+		if (HAVE(nx) && HAVE(ny))
+			gtk_window_move(GTK_WINDOW(win), nx->val.integer[0], ny->val.integer[0]);
+	}
+}
+#undef HAVE
+
+void wplc_config_event(GtkWidget *win, long *cx, long *cy, long *cw, long *ch)
+{
+	GtkAllocation allocation;
+	gboolean new_w, new_h, new_x, new_y;
+
+	gtk_widget_get_allocation(win, &allocation);
+
+	/* For whatever reason, get_allocation doesn't set these. Gtk. */
+	gtk_window_get_position(GTK_WINDOW(win), &allocation.x, &allocation.y);
+
+	new_w = (*cw != allocation.width);
+	new_h = (*ch != allocation.height);
+	new_x = (*cx != allocation.x);
+	new_y = (*cy != allocation.y);
+
+	*cx = allocation.x;
+	*cy = allocation.y;
+	*cw = allocation.width;
+	*ch = allocation.height;
+
+	if (new_w || new_h || new_x || new_y)
+		hid_gtk_wgeo_update();
+}
diff --git a/src_plugins/hid_gtk/win_place.h b/src_plugins/hid_gtk/win_place.h
new file mode 100644
index 0000000..a2f3a7b
--- /dev/null
+++ b/src_plugins/hid_gtk/win_place.h
@@ -0,0 +1,22 @@
+#ifndef GHID_WIN_PLACE
+#define GHID_WIN_PLACE
+#include "gui.h"
+
+typedef enum {
+	WPLC_TOP,
+	WPLC_LOG,
+	WPLC_DRC,
+	WPLC_LIBRARY,
+	WPLC_NETLIST,
+	WPLC_KEYREF,
+	WPLC_PINOUT,
+	WPLC_SEARCH,
+	WPLC_max
+} wplc_win_t;
+
+/* Place the window if it's enabled and there are coords in the config. */
+void wplc_place(wplc_win_t id, GtkWidget *win);
+
+/* query window current window sizes and update wgeo cache */
+void wplc_config_event(GtkWidget *win, long *cx, long *cy, long *cw, long *ch);
+#endif
diff --git a/src_plugins/hid_lesstif/Makefile b/src_plugins/hid_lesstif/Makefile
new file mode 100644
index 0000000..ba83ecf
--- /dev/null
+++ b/src_plugins/hid_lesstif/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_hid_lesstif
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/hid_lesstif/Plug.tmpasm b/src_plugins/hid_lesstif/Plug.tmpasm
new file mode 100644
index 0000000..51bf6b8
--- /dev/null
+++ b/src_plugins/hid_lesstif/Plug.tmpasm
@@ -0,0 +1,41 @@
+put /local/pcb/mod {hid_lesstif}
+put /local/pcb/mod/OBJS [@
+	$(PLUGDIR)/hid_lesstif/dialogs.o
+	$(PLUGDIR)/hid_lesstif/library.o
+	$(PLUGDIR)/hid_lesstif/main.o
+	$(PLUGDIR)/hid_lesstif/menu.o
+	$(PLUGDIR)/hid_lesstif/menu_lht.o
+	$(PLUGDIR)/hid_lesstif/netlist.o
+	$(PLUGDIR)/hid_lesstif/styles.o
+	$(PLUGDIR)/hid_lesstif/stdarg.o
+@]
+
+switch /local/pcb/hid_lesstif/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/CFLAGS [@
+			@?/target/libs/gui/lesstif2/cflags@
+			@?/target/libs/gui/xinerama/cflags@
+			@?/target/libs/gui/xrender/cflags@
+		@]
+
+		put /local/pcb/mod/LDFLAGS [@
+			@?/target/libs/gui/lesstif2/ldflags@
+			@?/target/libs/gui/xinerama/ldflags@
+			@?/target/libs/gui/xrender/ldflags@
+		@]
+
+		append /local/pcb/RULES [@
+### lesstif menu embed
+$(PLUGDIR)/hid_lesstif/menu_lht.c: pcb-menu-lesstif.lht
+	$(CQUOTE) -n lesstif_menu_default <pcb-menu-lesstif.lht >$(PLUGDIR)/hid_lesstif/menu_lht.c
+@]
+
+		end
+end
+
+switch /local/pcb/hid_lesstif/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/hid_lesstif/README b/src_plugins/hid_lesstif/README
new file mode 100644
index 0000000..80e9486
--- /dev/null
+++ b/src_plugins/hid_lesstif/README
@@ -0,0 +1,5 @@
+GUI: the lesstif HID.
+
+#state: works
+#default: buildin
+#implements: hid
diff --git a/src_plugins/hid_lesstif/dialogs.c b/src_plugins/hid_lesstif/dialogs.c
new file mode 100644
index 0000000..a196442
--- /dev/null
+++ b/src_plugins/hid_lesstif/dialogs.c
@@ -0,0 +1,1948 @@
+#include "xincludes.h"
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "compat_misc.h"
+#include "global.h"
+#include "data.h"
+#include "crosshair.h"
+#include "layer.h"
+#include "misc.h"
+#include "pcb-printf.h"
+
+#include "hid.h"
+#include "lesstif.h"
+#include "hid_attrib.h"
+#include "hid_actions.h"
+#include "hid_init.h"
+#include "stdarg.h"
+#include "misc_util.h"
+
+static int ok;
+
+#define COMPONENT_SIDE_NAME "(top)"
+#define SOLDER_SIDE_NAME "(bottom)"
+
+/* ------------------------------------------------------------ */
+
+static void dialog_callback(Widget w, void *v, void *cbs)
+{
+	ok = (int) (size_t) v;
+}
+
+static int wait_for_dialog(Widget w)
+{
+	ok = -1;
+	XtManageChild(w);
+	while (ok == -1 && XtIsManaged(w)) {
+		XEvent e;
+		XtAppNextEvent(app_context, &e);
+		XtDispatchEvent(&e);
+	}
+	XtUnmanageChild(w);
+	return ok;
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget fsb = 0;
+static XmString xms_pcb, xms_net, xms_vend, xms_all, xms_load, xms_loadv, xms_save, xms_fp;
+
+static void setup_fsb_dialog()
+{
+	if (fsb)
+		return;
+
+	xms_pcb = XmStringCreatePCB("*.pcb");
+	xms_fp = XmStringCreatePCB("*.fp");
+	xms_net = XmStringCreatePCB("*.net");
+	xms_vend = XmStringCreatePCB("*.vend");
+	xms_all = XmStringCreatePCB("*");
+	xms_load = XmStringCreatePCB("Load From");
+	xms_loadv = XmStringCreatePCB("Load Vendor");
+	xms_save = XmStringCreatePCB("Save As");
+
+	stdarg_n = 0;
+	fsb = XmCreateFileSelectionDialog(mainwind, XmStrCast("file"), stdarg_args, stdarg_n);
+
+	XtAddCallback(fsb, XmNokCallback, (XtCallbackProc) dialog_callback, (XtPointer) 1);
+	XtAddCallback(fsb, XmNcancelCallback, (XtCallbackProc) dialog_callback, (XtPointer) 0);
+}
+
+static const char load_syntax[] = "Load()\n" "Load(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert)";
+
+static const char load_help[] = "Load layout data from a user-selected file.";
+
+/* %start-doc actions Load
+
+This action is a GUI front-end to the core's @code{LoadFrom} action
+(@pxref{LoadFrom Action}).  If you happen to pass a filename, like
+ at code{LoadFrom}, then @code{LoadFrom} is called directly.  Else, the
+user is prompted for a filename to load, and then @code{LoadFrom} is
+called with that filename.
+
+%end-doc */
+
+static int Load(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function;
+	char *name;
+	XmString xmname, pattern;
+
+	if (argc > 1)
+		return hid_actionv("LoadFrom", argc, argv);
+
+	function = argc ? argv[0] : "Layout";
+
+	setup_fsb_dialog();
+
+	if (strcasecmp(function, "Netlist") == 0)
+		pattern = xms_net;
+	else if (strcasecmp(function, "ElementToBuffer") == 0)
+		pattern = xms_fp;
+	else
+		pattern = xms_pcb;
+
+	stdarg_n = 0;
+	stdarg(XmNtitle, "Load From");
+	XtSetValues(XtParent(fsb), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNpattern, pattern);
+	stdarg(XmNmustMatch, True);
+	stdarg(XmNselectionLabelString, xms_load);
+	XtSetValues(fsb, stdarg_args, stdarg_n);
+
+	if (!wait_for_dialog(fsb))
+		return 1;
+
+	stdarg_n = 0;
+	stdarg(XmNdirSpec, &xmname);
+	XtGetValues(fsb, stdarg_args, stdarg_n);
+
+	XmStringGetLtoR(xmname, XmFONTLIST_DEFAULT_TAG, &name);
+
+	hid_actionl("LoadFrom", function, name, NULL);
+
+	XtFree(name);
+
+	return 0;
+}
+
+static const char loadvendor_syntax[] = "LoadVendor()";
+
+static const char loadvendor_help[] = "Loads a user-selected vendor resource file.";
+
+/* %start-doc actions LoadVendor
+
+The user is prompted for a file to load, and then
+ at code{LoadVendorFrom} is called (@pxref{LoadVendorFrom Action}) to
+load that vendor file.
+
+%end-doc */
+
+static int LoadVendor(int argc, const char **argv, Coord x, Coord y)
+{
+	char *name;
+	XmString xmname, pattern;
+
+	if (argc > 0)
+		return hid_actionv("LoadVendorFrom", argc, argv);
+
+	setup_fsb_dialog();
+
+	pattern = xms_vend;
+
+	stdarg_n = 0;
+	stdarg(XmNtitle, "Load Vendor");
+	XtSetValues(XtParent(fsb), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNpattern, pattern);
+	stdarg(XmNmustMatch, True);
+	stdarg(XmNselectionLabelString, xms_loadv);
+	XtSetValues(fsb, stdarg_args, stdarg_n);
+
+	if (!wait_for_dialog(fsb))
+		return 1;
+
+	stdarg_n = 0;
+	stdarg(XmNdirSpec, &xmname);
+	XtGetValues(fsb, stdarg_args, stdarg_n);
+
+	XmStringGetLtoR(xmname, XmFONTLIST_DEFAULT_TAG, &name);
+
+	hid_actionl("LoadVendorFrom", name, NULL);
+
+	XtFree(name);
+
+	return 0;
+}
+
+static const char save_syntax[] =
+	"Save()\n" "Save(Layout|LayoutAs)\n" "Save(AllConnections|AllUnusedPins|ElementConnections)\n" "Save(PasteBuffer)";
+
+static const char save_help[] = "Save layout data to a user-selected file.";
+
+/* %start-doc actions Save
+
+This action is a GUI front-end to the core's @code{SaveTo} action
+(@pxref{SaveTo Action}).  If you happen to pass a filename, like
+ at code{SaveTo}, then @code{SaveTo} is called directly.  Else, the
+user is prompted for a filename to save, and then @code{SaveTo} is
+called with that filename.
+
+%end-doc */
+
+static int Save(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function;
+	char *name;
+	XmString xmname, pattern;
+
+	if (argc > 1)
+		hid_actionv("SaveTo", argc, argv);
+
+	function = argc ? argv[0] : "Layout";
+
+	if (strcasecmp(function, "Layout") == 0)
+		if (PCB->Filename)
+			return hid_actionl("SaveTo", "Layout", PCB->Filename, NULL);
+
+	setup_fsb_dialog();
+
+	pattern = xms_pcb;
+
+	XtManageChild(fsb);
+
+	stdarg_n = 0;
+	stdarg(XmNtitle, "Save As");
+	XtSetValues(XtParent(fsb), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNpattern, pattern);
+	stdarg(XmNmustMatch, False);
+	stdarg(XmNselectionLabelString, xms_save);
+	XtSetValues(fsb, stdarg_args, stdarg_n);
+
+	if (!wait_for_dialog(fsb))
+		return 1;
+
+	stdarg_n = 0;
+	stdarg(XmNdirSpec, &xmname);
+	XtGetValues(fsb, stdarg_args, stdarg_n);
+
+	XmStringGetLtoR(xmname, XmFONTLIST_DEFAULT_TAG, &name);
+
+	if (strcasecmp(function, "PasteBuffer") == 0)
+		hid_actionl("PasteBuffer", "Save", name, NULL);
+	else {
+		/*
+		 * if we got this far and the function is Layout, then
+		 * we really needed it to be a LayoutAs.  Otherwise
+		 * ActionSaveTo() will ignore the new file name we
+		 * just obtained.
+		 */
+		if (strcasecmp(function, "Layout") == 0)
+			hid_actionl("SaveTo", "LayoutAs", name, NULL);
+		else
+			hid_actionl("SaveTo", function, name, NULL);
+	}
+	XtFree(name);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget log_form, log_text;
+static int log_size = 0;
+static int pending_newline = 0;
+
+static void log_clear(Widget w, void *up, void *cbp)
+{
+	XmTextSetString(log_text, XmStrCast(""));
+	log_size = 0;
+	pending_newline = 0;
+}
+
+static void log_dismiss(Widget w, void *up, void *cbp)
+{
+	XtUnmanageChild(log_form);
+}
+
+void lesstif_logv(enum pcb_message_level level, const char *fmt, va_list ap)
+{
+	/* TODO(hzeller): do something useful with level (color etc.) */
+	char *buf, *scan;
+	if (!mainwind) {
+		vprintf(fmt, ap);
+		return;
+	}
+	if (!log_form) {
+		Widget clear_button, dismiss_button;
+
+		stdarg_n = 0;
+		stdarg(XmNautoUnmanage, False);
+		stdarg(XmNwidth, 600);
+		stdarg(XmNheight, 200);
+		stdarg(XmNtitle, "pcb-rnd Log");
+		log_form = XmCreateFormDialog(mainwind, XmStrCast("log"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		clear_button = XmCreatePushButton(log_form, XmStrCast("clear"), stdarg_args, stdarg_n);
+		XtManageChild(clear_button);
+		XtAddCallback(clear_button, XmNactivateCallback, (XtCallbackProc) log_clear, 0);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_WIDGET);
+		stdarg(XmNrightWidget, clear_button);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		dismiss_button = XmCreatePushButton(log_form, XmStrCast("dismiss"), stdarg_args, stdarg_n);
+		XtManageChild(dismiss_button);
+		XtAddCallback(dismiss_button, XmNactivateCallback, (XtCallbackProc) log_dismiss, 0);
+
+		stdarg_n = 0;
+		stdarg(XmNeditable, False);
+		stdarg(XmNeditMode, XmMULTI_LINE_EDIT);
+		stdarg(XmNcursorPositionVisible, True);
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNbottomWidget, clear_button);
+		log_text = XmCreateScrolledText(log_form, XmStrCast("text"), stdarg_args, stdarg_n);
+		XtManageChild(log_text);
+
+		XtManageChild(log_form);
+	}
+	if (pending_newline) {
+		XmTextInsert(log_text, log_size++, XmStrCast("\n"));
+		pending_newline = 0;
+	}
+	buf = pcb_strdup_vprintf(fmt, ap);
+	scan = &buf[strlen(buf) - 1];
+	while (scan >= buf && *scan == '\n') {
+		pending_newline++;
+		*scan-- = 0;
+	}
+	XmTextInsert(log_text, log_size, buf);
+	log_size += strlen(buf);
+
+	scan = strrchr(buf, '\n');
+	if (scan)
+		scan++;
+	else
+		scan = buf;
+	XmTextSetCursorPosition(log_text, log_size - strlen(scan));
+	free(buf);
+}
+
+void lesstif_log(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	lesstif_logv(PCB_MSG_INFO, fmt, ap);
+	va_end(ap);
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget confirm_dialog = 0;
+static Widget confirm_cancel, confirm_ok, confirm_label;
+
+int lesstif_confirm_dialog(const char *msg, ...)
+{
+	const char *cancelmsg, *okmsg;
+	va_list ap;
+	XmString xs;
+
+	if (mainwind == 0)
+		return 1;
+
+	if (confirm_dialog == 0) {
+		stdarg_n = 0;
+		stdarg(XmNdefaultButtonType, XmDIALOG_OK_BUTTON);
+		stdarg(XmNtitle, "Confirm");
+		confirm_dialog = XmCreateQuestionDialog(mainwind, XmStrCast("confirm"), stdarg_args, stdarg_n);
+		XtAddCallback(confirm_dialog, XmNcancelCallback, (XtCallbackProc) dialog_callback, (XtPointer) 0);
+		XtAddCallback(confirm_dialog, XmNokCallback, (XtCallbackProc) dialog_callback, (XtPointer) 1);
+
+		confirm_cancel = XmMessageBoxGetChild(confirm_dialog, XmDIALOG_CANCEL_BUTTON);
+		confirm_ok = XmMessageBoxGetChild(confirm_dialog, XmDIALOG_OK_BUTTON);
+		confirm_label = XmMessageBoxGetChild(confirm_dialog, XmDIALOG_MESSAGE_LABEL);
+		XtUnmanageChild(XmMessageBoxGetChild(confirm_dialog, XmDIALOG_HELP_BUTTON));
+	}
+
+	va_start(ap, msg);
+	cancelmsg = va_arg(ap, const char *);
+	okmsg = va_arg(ap, const char *);
+	va_end(ap);
+
+	if (!cancelmsg) {
+		cancelmsg = "Cancel";
+		okmsg = "Ok";
+	}
+
+	stdarg_n = 0;
+	xs = XmStringCreatePCB(cancelmsg);
+
+	if (okmsg) {
+		stdarg(XmNcancelLabelString, xs);
+		xs = XmStringCreatePCB(okmsg);
+		XtManageChild(confirm_cancel);
+	}
+	else
+		XtUnmanageChild(confirm_cancel);
+
+	stdarg(XmNokLabelString, xs);
+
+	xs = XmStringCreatePCB(msg);
+	stdarg(XmNmessageString, xs);
+	XtSetValues(confirm_dialog, stdarg_args, stdarg_n);
+
+	wait_for_dialog(confirm_dialog);
+
+	stdarg_n = 0;
+	stdarg(XmNdefaultPosition, False);
+	XtSetValues(confirm_dialog, stdarg_args, stdarg_n);
+
+	return ok;
+}
+
+static int ConfirmAction(int argc, const char **argv, Coord x, Coord y)
+{
+	int rv = lesstif_confirm_dialog(argc > 0 ? argv[0] : 0,
+																	argc > 1 ? argv[1] : 0,
+																	argc > 2 ? argv[2] : 0,
+																	0);
+	return rv;
+}
+
+/* ------------------------------------------------------------ */
+
+int lesstif_close_confirm_dialog()
+{
+	return lesstif_confirm_dialog("OK to lose data ?", NULL);
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget report = 0, report_form;
+
+void lesstif_report_dialog(const char *title, const char *msg)
+{
+	if (!report) {
+		if (mainwind == 0)
+			return;
+
+		stdarg_n = 0;
+		stdarg(XmNautoUnmanage, False);
+		stdarg(XmNwidth, 600);
+		stdarg(XmNheight, 200);
+		stdarg(XmNtitle, title);
+		report_form = XmCreateFormDialog(mainwind, XmStrCast("report"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNeditable, False);
+		stdarg(XmNeditMode, XmMULTI_LINE_EDIT);
+		stdarg(XmNcursorPositionVisible, False);
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		report = XmCreateScrolledText(report_form, XmStrCast("text"), stdarg_args, stdarg_n);
+		XtManageChild(report);
+	}
+	stdarg_n = 0;
+	stdarg(XmNtitle, title);
+	XtSetValues(report_form, stdarg_args, stdarg_n);
+	XmTextSetString(report, (char *) msg);
+
+	XtManageChild(report_form);
+}
+
+/* ------------------------------------------------------------ */
+/* FIXME -- make this a proper file select dialog box */
+char *lesstif_fileselect(const char *title, const char *descr,
+												 const char *default_file, const char *default_ext, const char *history_tag, int flags)
+{
+
+	return lesstif_prompt_for(title, default_file);
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget prompt_dialog = 0;
+static Widget prompt_label, prompt_text;
+
+char *lesstif_prompt_for(const char *msg, const char *default_string)
+{
+	char *rv;
+	XmString xs;
+	if (prompt_dialog == 0) {
+		stdarg_n = 0;
+		stdarg(XmNautoUnmanage, False);
+		stdarg(XmNtitle, "pcb-rnd Prompt");
+		prompt_dialog = XmCreateFormDialog(mainwind, XmStrCast("prompt"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNalignment, XmALIGNMENT_BEGINNING);
+		prompt_label = XmCreateLabel(prompt_dialog, XmStrCast("label"), stdarg_args, stdarg_n);
+		XtManageChild(prompt_label);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_WIDGET);
+		stdarg(XmNtopWidget, prompt_label);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNeditable, True);
+		prompt_text = XmCreateText(prompt_dialog, XmStrCast("text"), stdarg_args, stdarg_n);
+		XtManageChild(prompt_text);
+		XtAddCallback(prompt_text, XmNactivateCallback, (XtCallbackProc) dialog_callback, (XtPointer) 1);
+	}
+	if (!default_string)
+		default_string = "";
+	if (!msg)
+		msg = "Enter text:";
+	stdarg_n = 0;
+	xs = XmStringCreatePCB(msg);
+	stdarg(XmNlabelString, xs);
+	XtSetValues(prompt_label, stdarg_args, stdarg_n);
+	XmTextSetString(prompt_text, (char *) default_string);
+	XmTextSetCursorPosition(prompt_text, strlen(default_string));
+	wait_for_dialog(prompt_dialog);
+	rv = XmTextGetString(prompt_text);
+	return rv;
+}
+
+static const char promptfor_syntax[] = "PromptFor([message[,default]])";
+
+static const char promptfor_help[] = "Prompt for a response.";
+
+/* %start-doc actions PromptFor
+
+This is mostly for testing the lesstif HID interface.  The parameters
+are passed to the @code{prompt_for()} HID function, causing the user
+to be prompted for a response.  The respose is simply printed to the
+user's stdout.
+
+%end-doc */
+
+static int PromptFor(int argc, const char **argv, Coord x, Coord y)
+{
+	char *rv = lesstif_prompt_for(argc > 0 ? argv[0] : 0,
+																argc > 1 ? argv[1] : 0);
+	printf("rv = `%s'\n", rv);
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget create_form_ok_dialog(const char *name, int ok)
+{
+	Widget dialog, topform;
+	stdarg_n = 0;
+	dialog = XmCreateQuestionDialog(mainwind, XmStrCast(name), stdarg_args, stdarg_n);
+
+	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_SYMBOL_LABEL));
+	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_MESSAGE_LABEL));
+	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
+	XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc) dialog_callback, (XtPointer) 0);
+	if (ok)
+		XtAddCallback(dialog, XmNokCallback, (XtCallbackProc) dialog_callback, (XtPointer) 1);
+	else
+		XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_OK_BUTTON));
+
+	stdarg_n = 0;
+	topform = XmCreateForm(dialog, XmStrCast("attributes"), stdarg_args, stdarg_n);
+	XtManageChild(topform);
+	return topform;
+}
+
+int lesstif_attribute_dialog(HID_Attribute * attrs, int n_attrs, HID_Attr_Val * results, const char *title, const char *descr)
+{
+	Widget dialog, topform, lform, form;
+	Widget *wl;
+	int i, rv;
+	static XmString empty = 0;
+	int actual_nattrs = 0;
+	int attrcount = 0;
+
+	if (!empty)
+		empty = XmStringCreatePCB(" ");
+
+	for (i = 0; i < n_attrs; i++) {
+		if (attrs[i].help_text != ATTR_UNDOCUMENTED)
+			actual_nattrs++;
+		results[i] = attrs[i].default_val;
+		if (results[i].str_value)
+			results[i].str_value = pcb_strdup(results[i].str_value);
+	}
+
+	wl = (Widget *) malloc(n_attrs * sizeof(Widget));
+
+	topform = create_form_ok_dialog(title, 1);
+	dialog = XtParent(topform);
+
+	stdarg_n = 0;
+	stdarg(XmNfractionBase, n_attrs);
+	XtSetValues(topform, stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNfractionBase, actual_nattrs);
+	lform = XmCreateForm(topform, XmStrCast("attributes"), stdarg_args, stdarg_n);
+	XtManageChild(lform);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+	stdarg(XmNleftWidget, lform);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNfractionBase, actual_nattrs);
+	form = XmCreateForm(topform, XmStrCast("attributes"), stdarg_args, stdarg_n);
+	XtManageChild(form);
+
+	attrcount = -1;
+	for (i = 0; i < n_attrs; i++) {
+		Widget w;
+
+		if (attrs[i].help_text == ATTR_UNDOCUMENTED)
+			continue;
+		attrcount++;
+
+		stdarg_n = 0;
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNtopAttachment, XmATTACH_POSITION);
+		stdarg(XmNtopPosition, attrcount);
+		stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+		stdarg(XmNbottomPosition, attrcount + 1);
+		stdarg(XmNalignment, XmALIGNMENT_END);
+		w = XmCreateLabel(lform, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+		XtManageChild(w);
+	}
+
+	attrcount = -1;
+	for (i = 0; i < n_attrs; i++) {
+		static char buf[30];
+		stdarg_n = 0;
+
+		if (attrs[i].help_text == ATTR_UNDOCUMENTED)
+			continue;
+		attrcount++;
+
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNtopAttachment, XmATTACH_POSITION);
+		stdarg(XmNtopPosition, attrcount);
+		stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+		stdarg(XmNbottomPosition, attrcount + 1);
+		stdarg(XmNalignment, XmALIGNMENT_END);
+
+		switch (attrs[i].type) {
+		case HID_Label:
+			stdarg(XmNlabelString, empty);
+			wl[i] = XmCreateLabel(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+			break;
+		case HID_Boolean:
+			stdarg(XmNlabelString, empty);
+			stdarg(XmNset, results[i].int_value);
+			wl[i] = XmCreateToggleButton(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+			break;
+		case HID_String:
+			stdarg(XmNcolumns, 40);
+			stdarg(XmNresizeWidth, True);
+			stdarg(XmNvalue, results[i].str_value);
+			wl[i] = XmCreateTextField(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+			break;
+		case HID_Integer:
+			stdarg(XmNcolumns, 13);
+			stdarg(XmNresizeWidth, True);
+			sprintf(buf, "%d", results[i].int_value);
+			stdarg(XmNvalue, buf);
+			wl[i] = XmCreateTextField(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+			break;
+		case HID_Coord:
+			stdarg(XmNcolumns, 13);
+			stdarg(XmNresizeWidth, True);
+			pcb_snprintf(buf, sizeof(buf), "%$mS", results[i].coord_value);
+			stdarg(XmNvalue, buf);
+			wl[i] = XmCreateTextField(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+			break;
+		case HID_Real:
+			stdarg(XmNcolumns, 16);
+			stdarg(XmNresizeWidth, True);
+			pcb_snprintf(buf, sizeof(buf), "%g", results[i].real_value);
+			stdarg(XmNvalue, buf);
+			wl[i] = XmCreateTextField(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+			break;
+		case HID_Enum:
+			{
+				static XmString empty = 0;
+				Widget submenu, default_button = 0;
+				int sn = stdarg_n;
+
+				if (empty == 0)
+					empty = XmStringCreatePCB("");
+
+				submenu = XmCreatePulldownMenu(form, XmStrCast(attrs[i].name), stdarg_args + sn, stdarg_n - sn);
+
+				stdarg_n = sn;
+				stdarg(XmNlabelString, empty);
+				stdarg(XmNsubMenuId, submenu);
+				wl[i] = XmCreateOptionMenu(form, XmStrCast(attrs[i].name), stdarg_args, stdarg_n);
+
+				for (sn = 0; attrs[i].enumerations[sn]; sn++) {
+					Widget btn;
+					XmString label;
+					stdarg_n = 0;
+					label = XmStringCreatePCB(attrs[i].enumerations[sn]);
+					stdarg(XmNuserData, &attrs[i].enumerations[sn]);
+					stdarg(XmNlabelString, label);
+					btn = XmCreatePushButton(submenu, XmStrCast("menubutton"), stdarg_args, stdarg_n);
+					XtManageChild(btn);
+					XmStringFree(label);
+					if (sn == attrs[i].default_val.int_value)
+						default_button = btn;
+				}
+				if (default_button) {
+					stdarg_n = 0;
+					stdarg(XmNmenuHistory, default_button);
+					XtSetValues(wl[i], stdarg_args, stdarg_n);
+				}
+			}
+			break;
+		default:
+			wl[i] = XmCreateLabel(form, XmStrCast("UNIMPLEMENTED"), stdarg_args, stdarg_n);
+			break;
+		}
+
+		XtManageChild(wl[i]);
+	}
+
+	rv = wait_for_dialog(dialog);
+
+	for (i = 0; i < n_attrs; i++) {
+		char *cp;
+
+		if (attrs[i].help_text == ATTR_UNDOCUMENTED)
+			continue;
+
+		switch (attrs[i].type) {
+		case HID_Boolean:
+			results[i].int_value = XmToggleButtonGetState(wl[i]);
+			break;
+		case HID_String:
+			results[i].str_value = XmTextGetString(wl[i]);
+			break;
+		case HID_Integer:
+			cp = XmTextGetString(wl[i]);
+			sscanf(cp, "%d", &results[i].int_value);
+			break;
+		case HID_Coord:
+			cp = XmTextGetString(wl[i]);
+			results[i].coord_value = GetValue(cp, NULL, NULL, NULL);
+			break;
+		case HID_Real:
+			cp = XmTextGetString(wl[i]);
+			sscanf(cp, "%lg", &results[i].real_value);
+			break;
+		case HID_Enum:
+			{
+				const char **uptr;
+				Widget btn;
+
+				stdarg_n = 0;
+				stdarg(XmNmenuHistory, &btn);
+				XtGetValues(wl[i], stdarg_args, stdarg_n);
+				stdarg_n = 0;
+				stdarg(XmNuserData, &uptr);
+				XtGetValues(btn, stdarg_args, stdarg_n);
+				results[i].int_value = uptr - attrs[i].enumerations;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	free(wl);
+	XtDestroyWidget(dialog);
+
+	return rv ? 0 : 1;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char dowindows_syntax[] = "DoWindows(1|2|3|4)\n" "DoWindows(Layout|Library|Log|Netlist)";
+
+static const char dowindows_help[] = "Open various GUI windows.";
+
+/* %start-doc actions DoWindows
+
+ at table @code
+
+ at item 1
+ at itemx Layout
+Open the layout window.  Since the layout window is always shown
+anyway, this has no effect.
+
+ at item 2
+ at itemx Library
+Open the library window.
+
+ at item 3
+ at itemx Log
+Open the log window.
+
+ at item 4
+ at itemx Netlist
+Open the netlist window.
+
+ at end table
+
+%end-doc */
+
+static int DoWindows(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *a = argc == 1 ? argv[0] : "";
+	if (strcmp(a, "1") == 0 || strcasecmp(a, "Layout") == 0) {
+	}
+	else if (strcmp(a, "2") == 0 || strcasecmp(a, "Library") == 0) {
+		lesstif_show_library();
+	}
+	else if (strcmp(a, "3") == 0 || strcasecmp(a, "Log") == 0) {
+		if (log_form == 0)
+			lesstif_log("");
+		XtManageChild(log_form);
+	}
+	else if (strcmp(a, "4") == 0 || strcasecmp(a, "Netlist") == 0) {
+		lesstif_show_netlist();
+	}
+	else {
+		lesstif_log("Usage: DoWindows(1|2|3|4|Layout|Library|Log|Netlist)");
+		return 1;
+	}
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+static const char about_syntax[] = "About()";
+
+static const char about_help[] = "Tell the user about this version of PCB.";
+
+/* %start-doc actions About
+
+This just pops up a dialog telling the user which version of
+ at code{pcb} they're running.
+
+%end-doc */
+
+
+static int About(int argc, const char **argv, Coord x, Coord y)
+{
+	static Widget about = 0;
+	if (!about) {
+		XmString xs;
+		stdarg_n = 0;
+		xs = XmStringCreatePCB(GetInfoString());
+		stdarg(XmNmessageString, xs);
+		stdarg(XmNtitle, "About pcb-rnd");
+		about = XmCreateInformationDialog(mainwind, XmStrCast("about"), stdarg_args, stdarg_n);
+		XtUnmanageChild(XmMessageBoxGetChild(about, XmDIALOG_CANCEL_BUTTON));
+		XtUnmanageChild(XmMessageBoxGetChild(about, XmDIALOG_HELP_BUTTON));
+	}
+	wait_for_dialog(about);
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char print_syntax[] = "Print()";
+
+static const char print_help[] = "Print the layout.";
+
+/* %start-doc actions Print
+
+This will find the default printing HID, prompt the user for its
+options, and print the layout.
+
+%end-doc */
+
+static int Print(int argc, const char **argv, Coord x, Coord y)
+{
+	HID_Attribute *opts;
+	HID *printer;
+	HID_Attr_Val *vals;
+	int n;
+
+	printer = hid_find_printer();
+	if (!printer) {
+		lesstif_confirm_dialog("No printer?", "Oh well", 0);
+		return 1;
+	}
+	opts = printer->get_export_options(&n);
+	vals = (HID_Attr_Val *) calloc(n, sizeof(HID_Attr_Val));
+	if (lesstif_attribute_dialog(opts, n, vals, "Print", "")) {
+		free(vals);
+		return 1;
+	}
+	printer->do_export(vals);
+	free(vals);
+	return 0;
+}
+
+static HID_Attribute printer_calibrate_attrs[] = {
+	{"Enter Values here:", "",
+	 HID_Label, 0, 0, {0, 0, 0}, 0, 0},
+	{"x-calibration", "X scale for calibrating your printer",
+	 HID_Real, 0.5, 25, {0, 0, 1.00}, 0, 0},
+	{"y-calibration", "Y scale for calibrating your printer",
+	 HID_Real, 0.5, 25, {0, 0, 1.00}, 0, 0}
+};
+
+static HID_Attr_Val printer_calibrate_values[3];
+
+static const char printcalibrate_syntax[] = "PrintCalibrate()";
+
+static const char printcalibrate_help[] = "Calibrate the printer.";
+
+/* %start-doc actions PrintCalibrate
+
+This will print a calibration page, which you would measure and type
+the measurements in, so that future printouts will be more precise.
+
+%end-doc */
+
+static int PrintCalibrate(int argc, const char **argv, Coord x, Coord y)
+{
+	HID *printer = hid_find_printer();
+	printer->calibrate(0.0, 0.0);
+	if (gui->attribute_dialog(printer_calibrate_attrs, 3,
+														printer_calibrate_values,
+														"Printer Calibration Values", "Enter calibration values for your printer"))
+		return 1;
+	printer->calibrate(printer_calibrate_values[1].real_value, printer_calibrate_values[2].real_value);
+	return 0;
+}
+
+static const char export_syntax[] = "Export()";
+
+static const char export_help[] = "Export the layout.";
+
+/* %start-doc actions Export
+
+Prompts the user for an exporter to use.  Then, prompts the user for
+that exporter's options, and exports the layout.
+
+%end-doc */
+
+static int Export(int argc, const char **argv, Coord x, Coord y)
+{
+	static Widget selector = 0;
+	HID_Attribute *opts;
+	HID *printer, **hids;
+	HID_Attr_Val *vals;
+	int n, i, count;
+	Widget prev = 0;
+	Widget w;
+
+	hids = hid_enumerate();
+
+	if (!selector) {
+		stdarg_n = 0;
+		stdarg(XmNtitle, "Export HIDs");
+		selector = create_form_ok_dialog("export", 0);
+		count = 0;
+		for (i = 0; hids[i]; i++) {
+			if (hids[i]->exporter) {
+				stdarg_n = 0;
+				if (prev) {
+					stdarg(XmNtopAttachment, XmATTACH_WIDGET);
+					stdarg(XmNtopWidget, prev);
+				}
+				else {
+					stdarg(XmNtopAttachment, XmATTACH_FORM);
+				}
+				stdarg(XmNrightAttachment, XmATTACH_FORM);
+				stdarg(XmNleftAttachment, XmATTACH_FORM);
+				w = XmCreatePushButton(selector, (char *) hids[i]->name, stdarg_args, stdarg_n);
+				XtManageChild(w);
+				XtAddCallback(w, XmNactivateCallback, (XtCallbackProc) dialog_callback, (XtPointer) ((size_t) i + 1));
+				prev = w;
+				count++;
+			}
+		}
+		if (count == 0) {
+			Widget label;
+			stdarg_n = 0;
+			stdarg(XmNlabelString, XmStringCreatePCB("No exporter found. Check your plugins!"));
+			stdarg(XmNtopAttachment, XmATTACH_FORM);
+			stdarg(XmNrightAttachment, XmATTACH_FORM);
+			stdarg(XmNleftAttachment, XmATTACH_FORM);
+			label = XmCreateLabel(selector, XmStrCast("label"), stdarg_args, stdarg_n);
+			XtManageChild(label);
+		}
+		selector = XtParent(selector);
+	}
+
+
+	i = wait_for_dialog(selector);
+
+	if (i <= 0)
+		return 1;
+	printer = hids[i - 1];
+
+	exporter = printer;
+
+	opts = printer->get_export_options(&n);
+	vals = (HID_Attr_Val *) calloc(n, sizeof(HID_Attr_Val));
+	if (lesstif_attribute_dialog(opts, n, vals, "Export", NULL)) {
+		free(vals);
+		return 1;
+	}
+	printer->do_export(vals);
+	free(vals);
+	exporter = NULL;
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget sizes_dialog = 0;
+static Widget sz_pcb_w, sz_pcb_h, sz_bloat, sz_shrink, sz_drc_wid, sz_drc_slk, sz_drc_drill, sz_drc_ring;
+static Widget sz_text;
+static Widget sz_set, sz_reset, sz_units;
+
+static int sz_str2val(Widget w, pcb_bool pcbu)
+{
+	char *buf = XmTextGetString(w);
+	if (!pcbu)
+		return strtol(buf, NULL, 0);
+	return GetValueEx(buf, NULL, NULL, NULL, conf_core.editor.grid_unit->suffix, NULL);
+}
+
+static void sz_val2str(Widget w, Coord u, int pcbu)
+{
+	static char buf[40];
+	if (pcbu)
+		pcb_sprintf(buf, "%m+%.2mS", conf_core.editor.grid_unit->allow, u);
+	else
+		pcb_snprintf(buf, sizeof(buf), "%#mS %%", u);
+	XmTextSetString(w, buf);
+}
+
+static void sizes_set()
+{
+	PCB->MaxWidth = sz_str2val(sz_pcb_w, 1);
+	PCB->MaxHeight = sz_str2val(sz_pcb_h, 1);
+	PCB->Bloat = sz_str2val(sz_bloat, 1);
+	PCB->Shrink = sz_str2val(sz_shrink, 1);
+	PCB->minWid = sz_str2val(sz_drc_wid, 1);
+	PCB->minSlk = sz_str2val(sz_drc_slk, 1);
+	PCB->minDrill = sz_str2val(sz_drc_drill, 1);
+	PCB->minRing = sz_str2val(sz_drc_ring, 1);
+#warning think these over - are these only for new designs amd we keep real values in PCB-> ?
+	conf_set_design("design/text_scale", "%s", sz_text);
+	conf_set_design("design/bloat", "%s", sz_bloat);
+	conf_set_design("design/shrink", "%s", sz_shrink);
+	conf_set_design("design/min_wid", "%s", sz_drc_wid);
+	conf_set_design("design/min_slk", "%s", sz_drc_slk);
+	conf_set_design("design/min_drill", "%s", sz_drc_drill);
+	conf_set_design("design/min_ring", "%s", sz_drc_ring);
+
+	SetCrosshairRange(0, 0, PCB->MaxWidth, PCB->MaxHeight);
+	lesstif_pan_fixup();
+}
+
+void lesstif_sizes_reset()
+{
+	char *ls;
+	if (!sizes_dialog)
+		return;
+	sz_val2str(sz_pcb_w, PCB->MaxWidth, 1);
+	sz_val2str(sz_pcb_h, PCB->MaxHeight, 1);
+	sz_val2str(sz_bloat, PCB->Bloat, 1);
+	sz_val2str(sz_shrink, PCB->Shrink, 1);
+	sz_val2str(sz_drc_wid, PCB->minWid, 1);
+	sz_val2str(sz_drc_slk, PCB->minSlk, 1);
+	sz_val2str(sz_drc_drill, PCB->minDrill, 1);
+	sz_val2str(sz_drc_ring, PCB->minRing, 1);
+	sz_val2str(sz_text, conf_core.design.text_scale, 0);
+
+	ls = pcb_strdup_printf(_("Units are %s."), conf_core.editor.grid_unit->in_suffix);
+	stdarg_n = 0;
+	stdarg(XmNlabelString, XmStringCreatePCB(ls));
+	XtSetValues(sz_units, stdarg_args, stdarg_n);
+	free(ls);
+}
+
+static Widget size_field(Widget parent, const char *label, int posn)
+{
+	Widget w, l;
+	stdarg_n = 0;
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNtopAttachment, XmATTACH_POSITION);
+	stdarg(XmNtopPosition, posn);
+	stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+	stdarg(XmNbottomPosition, posn + 1);
+	stdarg(XmNcolumns, 10);
+	w = XmCreateTextField(parent, XmStrCast("field"), stdarg_args, stdarg_n);
+	XtManageChild(w);
+
+	stdarg_n = 0;
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_WIDGET);
+	stdarg(XmNrightWidget, w);
+	stdarg(XmNtopAttachment, XmATTACH_POSITION);
+	stdarg(XmNtopPosition, posn);
+	stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+	stdarg(XmNbottomPosition, posn + 1);
+	stdarg(XmNlabelString, XmStringCreatePCB(label));
+	stdarg(XmNalignment, XmALIGNMENT_END);
+	l = XmCreateLabel(parent, XmStrCast("label"), stdarg_args, stdarg_n);
+	XtManageChild(l);
+
+	return w;
+}
+
+static const char adjustsizes_syntax[] = "AdjustSizes()";
+
+static const char adjustsizes_help[] = "Let the user change the board size, DRC parameters, etc";
+
+/* %start-doc actions AdjustSizes
+
+Displays a dialog box that lets the user change the board
+size, DRC parameters, and text scale.
+
+The units are determined by the default display units.
+
+%end-doc */
+
+static int AdjustSizes(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!sizes_dialog) {
+		Widget inf, sep;
+
+		stdarg_n = 0;
+		stdarg(XmNmarginWidth, 3);
+		stdarg(XmNmarginHeight, 3);
+		stdarg(XmNhorizontalSpacing, 3);
+		stdarg(XmNverticalSpacing, 3);
+		stdarg(XmNautoUnmanage, False);
+		stdarg(XmNtitle, "Board Sizes");
+		sizes_dialog = XmCreateFormDialog(mainwind, XmStrCast("sizes"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		sz_reset = XmCreatePushButton(sizes_dialog, XmStrCast("Reset"), stdarg_args, stdarg_n);
+		XtManageChild(sz_reset);
+		XtAddCallback(sz_reset, XmNactivateCallback, (XtCallbackProc) lesstif_sizes_reset, 0);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_WIDGET);
+		stdarg(XmNrightWidget, sz_reset);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		sz_set = XmCreatePushButton(sizes_dialog, XmStrCast("Set"), stdarg_args, stdarg_n);
+		XtManageChild(sz_set);
+		XtAddCallback(sz_set, XmNactivateCallback, (XtCallbackProc) sizes_set, 0);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNbottomWidget, sz_reset);
+		sep = XmCreateSeparator(sizes_dialog, XmStrCast("sep"), stdarg_args, stdarg_n);
+		XtManageChild(sep);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNbottomWidget, sep);
+		sz_units = XmCreateLabel(sizes_dialog, XmStrCast("units"), stdarg_args, stdarg_n);
+		XtManageChild(sz_units);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNbottomWidget, sz_units);
+		stdarg(XmNfractionBase, 9);
+		inf = XmCreateForm(sizes_dialog, XmStrCast("sizes"), stdarg_args, stdarg_n);
+		XtManageChild(inf);
+
+		sz_pcb_w = size_field(inf, "PCB Width", 0);
+		sz_pcb_h = size_field(inf, "PCB Height", 1);
+		sz_bloat = size_field(inf, "Bloat", 2);
+		sz_shrink = size_field(inf, "Shrink", 3);
+		sz_drc_wid = size_field(inf, "DRC Min Wid", 4);
+		sz_drc_slk = size_field(inf, "DRC Min Silk", 5);
+		sz_drc_drill = size_field(inf, "DRC Min Drill", 6);
+		sz_drc_ring = size_field(inf, "DRC Min Annular Ring", 7);
+		sz_text = size_field(inf, "Text Scale", 8);
+	}
+	lesstif_sizes_reset();
+	XtManageChild(sizes_dialog);
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static Widget layer_groups_form = 0;
+static Widget lg_buttonform = 0;
+
+static int lg_setcol[MAX_LAYER + 2];
+static int lg_width, lg_height;
+static int lg_r[MAX_LAYER + 3];
+static int lg_c[MAX_LAYER + 1];
+static int lg_label_width, lg_fa, lg_fd;
+static GC lg_gc = 0;
+
+#if 0
+static Widget lglabels[MAX_LAYER + 2];
+static Widget lgbuttons[MAX_LAYER + 2][MAX_LAYER];
+#endif
+
+typedef struct {
+	XFontStruct *font;
+	Pixel fg, bg, sel;
+} LgResource;
+
+static LgResource lgr;
+
+static XtResource lg_resources[] = {
+	{(char*)"font", (char*)"Font", XtRFontStruct, sizeof(XFontStruct *), XtOffset(LgResource *, font), XtRString, (void *) "fixed"},
+	{(char*)"foreground", (char*)"Foreground", XtRPixel, sizeof(Pixel), XtOffset(LgResource *, fg), XtRString, (void *) "black"},
+	{(char*)"selectColor", (char*)"Foreground", XtRPixel, sizeof(Pixel), XtOffset(LgResource *, sel), XtRString, (void *) "blue"},
+	{(char*)"background", (char*)"Background", XtRPixel, sizeof(Pixel), XtOffset(LgResource *, bg), XtRString, (void *) "white"}
+};
+
+#if 0
+static void lgbutton_cb(Widget w, int ij, void *cbs)
+{
+	int layer, group, k;
+
+	layer = ij / max_group;
+	group = ij % max_group;
+	group = MoveLayerToGroup(layer, group);
+	for (k = 0; k < max_group; k++) {
+		if (k == group)
+			XmToggleButtonSetState(lgbuttons[layer][k], 1, 0);
+		else
+			XmToggleButtonSetState(lgbuttons[layer][k], 0, 0);
+	}
+}
+#endif
+
+static void lgbutton_expose(Widget w, XtPointer u, XmDrawingAreaCallbackStruct * cbs)
+{
+	int i;
+	Window win = XtWindow(w);
+
+	if (cbs && cbs->event->xexpose.count)
+		return;
+	if (lg_gc == 0 && !cbs)
+		return;
+	if (lg_gc == 0 && cbs) {
+		lg_gc = XCreateGC(display, win, 0, 0);
+		XSetFont(display, lg_gc, lgr.font->fid);
+	}
+
+	XSetForeground(display, lg_gc, lgr.bg);
+	XFillRectangle(display, win, lg_gc, 0, 0, lg_width, lg_height);
+	XSetForeground(display, lg_gc, lgr.fg);
+	for (i = 0; i < max_group; i++)
+		XDrawLine(display, win, lg_gc, lg_c[i], 0, lg_c[i], lg_height);
+	for (i = 1; i < max_copper_layer + 2; i++)
+		XDrawLine(display, win, lg_gc, lg_label_width, lg_r[i], lg_width, lg_r[i]);
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		int dir;
+		XCharStruct size;
+		int swidth;
+		const char *name;
+
+		if (i == solder_silk_layer)
+			name = SOLDER_SIDE_NAME;
+		else if (i == component_silk_layer)
+			name = COMPONENT_SIDE_NAME;
+		else
+			name = PCB->Data->Layer[i].Name;
+		XTextExtents(lgr.font, name, strlen(name), &dir, &lg_fa, &lg_fd, &size);
+		swidth = size.rbearing - size.lbearing;
+		XDrawString(display, win, lg_gc,
+								(lg_label_width - swidth) / 2 - size.lbearing,
+								(lg_r[i] + lg_r[i + 1] + lg_fd + lg_fa) / 2 - 1, name, strlen(name));
+	}
+	XSetForeground(display, lg_gc, lgr.sel);
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		int c = lg_setcol[i];
+		int x1 = lg_c[c] + 2;
+		int x2 = lg_c[c + 1] - 2;
+		int y1 = lg_r[i] + 2;
+		int y2 = lg_r[i + 1] - 2;
+		XFillRectangle(display, win, lg_gc, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+	}
+}
+
+static void lgbutton_input(Widget w, XtPointer u, XmDrawingAreaCallbackStruct * cbs)
+{
+	int layer, group;
+	if (cbs->event->type != ButtonPress)
+		return;
+	layer = cbs->event->xbutton.y * (max_copper_layer + 2) / lg_height;
+	group = (cbs->event->xbutton.x - lg_label_width) * max_group / (lg_width - lg_label_width);
+	group = MoveLayerToGroup(layer, group);
+	lg_setcol[layer] = group;
+	lgbutton_expose(w, 0, 0);
+	gui->invalidate_all();
+}
+
+static void lgbutton_resize(Widget w, XtPointer u, XmDrawingAreaCallbackStruct * cbs)
+{
+	int i;
+	Dimension width, height;
+	stdarg_n = 0;
+	stdarg(XmNwidth, &width);
+	stdarg(XmNheight, &height);
+	XtGetValues(w, stdarg_args, stdarg_n);
+	lg_width = width;
+	lg_height = height;
+
+	for (i = 0; i <= max_group; i++)
+		lg_c[i] = lg_label_width + (lg_width - lg_label_width) * i / max_group;
+	for (i = 0; i <= max_copper_layer + 2; i++)
+		lg_r[i] = lg_height * i / (max_copper_layer + 2);
+	lgbutton_expose(w, 0, 0);
+}
+
+void lesstif_update_layer_groups()
+{
+	int sets[MAX_LAYER + 2][MAX_LAYER];
+	int i, j;
+	LayerGroupType *l = &(PCB->LayerGroups);
+
+	if (!layer_groups_form)
+		return;
+
+	memset(sets, 0, sizeof(sets));
+
+	for (i = 0; i < max_group; i++)
+		for (j = 0; j < l->Number[i]; j++) {
+			sets[l->Entries[i][j]][i] = 1;
+			lg_setcol[l->Entries[i][j]] = i;
+		}
+
+	lg_label_width = 0;
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		int dir;
+		XCharStruct size;
+		int swidth;
+		const char *name;
+
+		if (i == solder_silk_layer)
+			name = SOLDER_SIDE_NAME;
+		else if (i == component_silk_layer)
+			name = COMPONENT_SIDE_NAME;
+		else
+			name = PCB->Data->Layer[i].Name;
+		XTextExtents(lgr.font, name, strlen(name), &dir, &lg_fa, &lg_fd, &size);
+		swidth = size.rbearing - size.lbearing;
+		if (lg_label_width < swidth)
+			lg_label_width = swidth;
+	}
+	lg_label_width += 4;
+
+	stdarg_n = 0;
+	stdarg(XmNwidth, lg_label_width + (lg_fa + lg_fd) * max_group);
+	stdarg(XmNheight, (lg_fa + lg_fd) * (max_copper_layer + 2));
+	XtSetValues(lg_buttonform, stdarg_args, stdarg_n);
+	lgbutton_expose(lg_buttonform, 0, 0);
+
+#if 0
+	for (i = 0; i < max_copper_layer + 2; i++) {
+		char *name = "unknown";
+		stdarg_n = 0;
+		if (i < max_copper_layer)
+			name = PCB->Data->Layer[i].Name;
+		else if (i == solder_silk_layer)
+			name = SOLDER_SIDE_NAME;
+		else if (i == component_silk_layer)
+			name = COMPONENT_SIDE_NAME;
+		stdarg(XmNlabelString, XmStringCreatePCB(name));
+		XtSetValues(lglabels[i], stdarg_args, stdarg_n);
+		for (j = 0; j < max_group; j++) {
+			if (sets[i][j] != XmToggleButtonGetState(lgbuttons[i][j])) {
+				XmToggleButtonSetState(lgbuttons[i][j], sets[i][j], 0);
+			}
+		}
+	}
+	XtUnmanageChild(lg_buttonform);
+	for (i = 0; i < MAX_LAYER + 2; i++)
+		for (j = 0; j < MAX_LAYER; j++) {
+			if (i < max_copper_layer + 2 && j < max_group) {
+				XtManageChild(lgbuttons[i][j]);
+				stdarg_n = 0;
+				stdarg(XmNleftPosition, j * (max_copper_layer + 2));
+				stdarg(XmNrightPosition, (j + 1) * (max_copper_layer + 2));
+				stdarg(XmNtopPosition, i * max_group);
+				stdarg(XmNbottomPosition, (i + 1) * max_group);
+				XtSetValues(lgbuttons[i][j], stdarg_args, stdarg_n);
+			}
+			else
+				XtUnmanageChild(lgbuttons[i][j]);
+		}
+	stdarg_n = 0;
+	stdarg(XmNfractionBase, max_copper_layer + 2);
+	XtSetValues(layer_groups_form, stdarg_args, stdarg_n);
+	stdarg_n = 0;
+	stdarg(XmNfractionBase, max_group * (max_copper_layer + 2));
+	XtSetValues(lg_buttonform, stdarg_args, stdarg_n);
+	XtManageChild(lg_buttonform);
+#endif
+}
+
+static const char editlayergroups_syntax[] = "EditLayerGroups()";
+
+static const char editlayergroups_help[] = "Let the user change the layer groupings";
+
+/* %start-doc actions EditLayerGroups
+
+Displays a dialog that lets the user view and change the layer
+groupings.  Each layer (row) can be a member of any one layer group
+(column).  Note the special layers @code{solder} and @code{component}
+allow you to specify which groups represent the top and bottom of the
+board.
+
+See @ref{ChangeName Action}.
+
+%end-doc */
+
+static int EditLayerGroups(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!layer_groups_form) {
+
+		stdarg_n = 0;
+		stdarg(XmNfractionBase, max_copper_layer + 2);
+		stdarg(XmNtitle, "Layer Groups");
+		layer_groups_form = XmCreateFormDialog(mainwind, XmStrCast("layers"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		lg_buttonform = XmCreateDrawingArea(layer_groups_form, XmStrCast("layers"), stdarg_args, stdarg_n);
+		XtManageChild(lg_buttonform);
+
+		XtAddCallback(lg_buttonform, XmNexposeCallback, (XtCallbackProc) lgbutton_expose, 0);
+		XtAddCallback(lg_buttonform, XmNinputCallback, (XtCallbackProc) lgbutton_input, 0);
+		XtAddCallback(lg_buttonform, XmNresizeCallback, (XtCallbackProc) lgbutton_resize, 0);
+
+		XtGetSubresources(layer_groups_form, &lgr, "layergroups", "LayerGroups", lg_resources, XtNumber(lg_resources), 0, 0);
+#if 0
+		stdarg(XmNfractionBase, max_group * (MAX_LAYER + 2));
+		lg_buttonform = XmCreateForm(layer_groups_form, "lgbutton", stdarg_args, stdarg_n);
+
+		for (i = 0; i < MAX_LAYER + 2; i++) {
+			stdarg_n = 0;
+			stdarg(XmNleftAttachment, XmATTACH_FORM);
+			stdarg(XmNtopAttachment, XmATTACH_POSITION);
+			stdarg(XmNtopPosition, i);
+			stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+			stdarg(XmNbottomPosition, i + 1);
+			stdarg(XmNrightAttachment, XmATTACH_WIDGET);
+			stdarg(XmNrightWidget, lg_buttonform);
+			lglabels[i] = XmCreateLabel(layer_groups_form, "layer", stdarg_args, stdarg_n);
+			XtManageChild(lglabels[i]);
+
+			for (j = 0; j < MAX_LAYER; j++) {
+				stdarg_n = 0;
+				stdarg(XmNleftAttachment, XmATTACH_POSITION);
+				stdarg(XmNleftPosition, j * (MAX_LAYER + 2));
+				stdarg(XmNrightAttachment, XmATTACH_POSITION);
+				stdarg(XmNrightPosition, (j + 1) * (MAX_LAYER + 2));
+				stdarg(XmNtopAttachment, XmATTACH_POSITION);
+				stdarg(XmNtopPosition, i * MAX_LAYER);
+				stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+				stdarg(XmNbottomPosition, (i + 1) * MAX_LAYER);
+				stdarg(XmNlabelString, XmStringCreatePCB(" "));
+				stdarg(XmNspacing, 0);
+				stdarg(XmNvisibleWhenOff, True);
+				stdarg(XmNfillOnSelect, True);
+				stdarg(XmNshadowThickness, 0);
+				stdarg(XmNmarginWidth, 0);
+				stdarg(XmNmarginHeight, 0);
+				stdarg(XmNhighlightThickness, 0);
+				lgbuttons[i][j] = XmCreateToggleButton(lg_buttonform, "label", stdarg_args, stdarg_n);
+				XtManageChild(lgbuttons[i][j]);
+
+				XtAddCallback(lgbuttons[i][j], XmNvalueChangedCallback, (XtCallbackProc) lgbutton_cb, (XtPointer) (i * max_group + j));
+			}
+		}
+#endif
+	}
+	lesstif_update_layer_groups();
+	XtManageChild(layer_groups_form);
+	return 1;
+}
+
+/* ------------------------------------------------------------ */
+
+typedef struct {
+	Widget del;
+	Widget w_name;
+	Widget w_value;
+} AttrRow;
+
+static AttrRow *attr_row = 0;
+static int attr_num_rows = 0;
+static int attr_max_rows = 0;
+static Widget attr_dialog = NULL, f_top;
+static AttributeListType *attributes_list;
+
+static void attributes_delete_callback(Widget w, void *v, void *cbs);
+
+static void fiddle_with_bb_layout()
+{
+	int i;
+	int max_height = 0;
+	int max_del_width = 0;
+	int max_name_width = 0;
+	int max_value_width = 0;
+	short ncolumns = 20;
+	short vcolumns = 20;
+
+	for (i = 0; i < attr_num_rows; i++) {
+		String v;
+
+		stdarg_n = 0;
+		stdarg(XmNvalue, &v);
+		XtGetValues(attr_row[i].w_name, stdarg_args, stdarg_n);
+		if (ncolumns < strlen(v))
+			ncolumns = strlen(v);
+
+		stdarg_n = 0;
+		stdarg(XmNvalue, &v);
+		XtGetValues(attr_row[i].w_value, stdarg_args, stdarg_n);
+		if (vcolumns < strlen(v))
+			vcolumns = strlen(v);
+	}
+
+	for (i = 0; i < attr_num_rows; i++) {
+		stdarg_n = 0;
+		stdarg(XmNcolumns, ncolumns);
+		XtSetValues(attr_row[i].w_name, stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNcolumns, vcolumns);
+		XtSetValues(attr_row[i].w_value, stdarg_args, stdarg_n);
+	}
+
+	for (i = 0; i < attr_num_rows; i++) {
+		Dimension w, h;
+		stdarg_n = 0;
+		stdarg(XmNwidth, &w);
+		stdarg(XmNheight, &h);
+
+		XtGetValues(attr_row[i].del, stdarg_args, stdarg_n);
+		if (max_height < h)
+			max_height = h;
+		if (max_del_width < w)
+			max_del_width = w;
+
+		XtGetValues(attr_row[i].w_name, stdarg_args, stdarg_n);
+		if (max_height < h)
+			max_height = h;
+		if (max_name_width < w)
+			max_name_width = w;
+
+		XtGetValues(attr_row[i].w_value, stdarg_args, stdarg_n);
+		if (max_height < h)
+			max_height = h;
+		if (max_value_width < w)
+			max_value_width = w;
+	}
+
+	for (i = 0; i < attr_num_rows; i++) {
+		stdarg_n = 0;
+		stdarg(XmNx, 0);
+		stdarg(XmNy, i * max_height);
+		stdarg(XmNwidth, max_del_width);
+		stdarg(XmNheight, max_height);
+		XtSetValues(attr_row[i].del, stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNx, max_del_width);
+		stdarg(XmNy, i * max_height);
+		stdarg(XmNwidth, max_name_width);
+		stdarg(XmNheight, max_height);
+		XtSetValues(attr_row[i].w_name, stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNx, max_del_width + max_name_width);
+		stdarg(XmNy, i * max_height);
+		stdarg(XmNwidth, max_value_width);
+		stdarg(XmNheight, max_height);
+		XtSetValues(attr_row[i].w_value, stdarg_args, stdarg_n);
+	}
+
+	stdarg_n = 0;
+	stdarg(XmNwidth, max_del_width + max_name_width + max_value_width + 1);
+	stdarg(XmNheight, max_height * attr_num_rows + 1);
+	XtSetValues(f_top, stdarg_args, stdarg_n);
+}
+
+static void lesstif_attributes_need_rows(int new_max)
+{
+	if (attr_max_rows < new_max) {
+		if (attr_row)
+			attr_row = (AttrRow *) realloc(attr_row, new_max * sizeof(AttrRow));
+		else
+			attr_row = (AttrRow *) malloc(new_max * sizeof(AttrRow));
+	}
+
+	while (attr_max_rows < new_max) {
+		stdarg_n = 0;
+		attr_row[attr_max_rows].del = XmCreatePushButton(f_top, XmStrCast("del"), stdarg_args, stdarg_n);
+		XtManageChild(attr_row[attr_max_rows].del);
+		XtAddCallback(attr_row[attr_max_rows].del, XmNactivateCallback,
+									(XtCallbackProc) attributes_delete_callback, (XtPointer) (size_t) attr_max_rows);
+
+		stdarg_n = 0;
+		stdarg(XmNresizeWidth, True);
+		attr_row[attr_max_rows].w_name = XmCreateTextField(f_top, XmStrCast("name"), stdarg_args, stdarg_n);
+		XtManageChild(attr_row[attr_max_rows].w_name);
+		XtAddCallback(attr_row[attr_max_rows].w_name, XmNvalueChangedCallback, (XtCallbackProc) fiddle_with_bb_layout, NULL);
+
+		stdarg_n = 0;
+		stdarg(XmNresizeWidth, True);
+		attr_row[attr_max_rows].w_value = XmCreateTextField(f_top, XmStrCast("value"), stdarg_args, stdarg_n);
+		XtManageChild(attr_row[attr_max_rows].w_value);
+		XtAddCallback(attr_row[attr_max_rows].w_value, XmNvalueChangedCallback, (XtCallbackProc) fiddle_with_bb_layout, NULL);
+
+		attr_max_rows++;
+	}
+
+	/* Manage any previously unused rows we now need to show.  */
+	while (attr_num_rows < new_max) {
+		XtManageChild(attr_row[attr_num_rows].del);
+		XtManageChild(attr_row[attr_num_rows].w_name);
+		XtManageChild(attr_row[attr_num_rows].w_value);
+		attr_num_rows++;
+	}
+}
+
+static void lesstif_attributes_revert()
+{
+	int i;
+
+	lesstif_attributes_need_rows(attributes_list->Number);
+
+	/* Unmanage any previously used rows we don't need.  */
+	while (attr_num_rows > attributes_list->Number) {
+		attr_num_rows--;
+		XtUnmanageChild(attr_row[attr_num_rows].del);
+		XtUnmanageChild(attr_row[attr_num_rows].w_name);
+		XtUnmanageChild(attr_row[attr_num_rows].w_value);
+	}
+
+	/* Fill in values */
+	for (i = 0; i < attributes_list->Number; i++) {
+		XmTextFieldSetString(attr_row[i].w_name, attributes_list->List[i].name);
+		XmTextFieldSetString(attr_row[i].w_value, attributes_list->List[i].value);
+	}
+
+	fiddle_with_bb_layout();
+}
+
+static void attributes_new_callback(Widget w, void *v, void *cbs)
+{
+	lesstif_attributes_need_rows(attr_num_rows + 1);	/* also bumps attr_num_rows */
+	XmTextFieldSetString(attr_row[attr_num_rows - 1].w_name, XmStrCast(""));
+	XmTextFieldSetString(attr_row[attr_num_rows - 1].w_value, XmStrCast(""));
+
+	fiddle_with_bb_layout();
+}
+
+static void attributes_delete_callback(Widget w, void *v, void *cbs)
+{
+	int i, n;
+	Widget wn, wv;
+
+	n = (int) (size_t) v;
+
+	wn = attr_row[n].w_name;
+	wv = attr_row[n].w_value;
+
+	for (i = n; i < attr_num_rows - 1; i++) {
+		attr_row[i].w_name = attr_row[i + 1].w_name;
+		attr_row[i].w_value = attr_row[i + 1].w_value;
+	}
+	attr_row[attr_num_rows - 1].w_name = wn;
+	attr_row[attr_num_rows - 1].w_value = wv;
+	attr_num_rows--;
+
+	XtUnmanageChild(wn);
+	XtUnmanageChild(wv);
+
+	fiddle_with_bb_layout();
+}
+
+static void attributes_revert_callback(Widget w, void *v, void *cbs)
+{
+	lesstif_attributes_revert();
+}
+
+void lesstif_attributes_dialog(const char *owner, AttributeListType * attrs_list)
+{
+	Widget bform, sw, b_ok, b_cancel, b_revert, b_new;
+	Widget sep;
+
+	if (attr_dialog == NULL) {
+		stdarg_n = 0;
+		stdarg(XmNautoUnmanage, False);
+		stdarg(XmNtitle, owner);
+		stdarg(XmNwidth, 400);
+		stdarg(XmNheight, 300);
+		attr_dialog = XmCreateFormDialog(mainwind, XmStrCast("attributes"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		stdarg(XmNorientation, XmHORIZONTAL);
+		stdarg(XmNentryAlignment, XmALIGNMENT_CENTER);
+		stdarg(XmNpacking, XmPACK_COLUMN);
+		bform = XmCreateRowColumn(attr_dialog, XmStrCast("attributes"), stdarg_args, stdarg_n);
+		XtManageChild(bform);
+
+		stdarg_n = 0;
+		b_ok = XmCreatePushButton(bform, XmStrCast("OK"), stdarg_args, stdarg_n);
+		XtManageChild(b_ok);
+		XtAddCallback(b_ok, XmNactivateCallback, (XtCallbackProc) dialog_callback, (XtPointer) 0);
+
+		stdarg_n = 0;
+		b_new = XmCreatePushButton(bform, XmStrCast("New"), stdarg_args, stdarg_n);
+		XtManageChild(b_new);
+		XtAddCallback(b_new, XmNactivateCallback, (XtCallbackProc) attributes_new_callback, NULL);
+
+		stdarg_n = 0;
+		b_revert = XmCreatePushButton(bform, XmStrCast("Revert"), stdarg_args, stdarg_n);
+		XtManageChild(b_revert);
+		XtAddCallback(b_revert, XmNactivateCallback, (XtCallbackProc) attributes_revert_callback, NULL);
+
+		stdarg_n = 0;
+		b_cancel = XmCreatePushButton(bform, XmStrCast("Cancel"), stdarg_args, stdarg_n);
+		XtManageChild(b_cancel);
+		XtAddCallback(b_cancel, XmNactivateCallback, (XtCallbackProc) dialog_callback, (XtPointer) 1);
+
+		stdarg_n = 0;
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNbottomWidget, bform);
+		sep = XmCreateSeparator(attr_dialog, XmStrCast("attributes"), stdarg_args, stdarg_n);
+		XtManageChild(sep);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+		stdarg(XmNbottomWidget, sep);
+		stdarg(XmNscrollingPolicy, XmAUTOMATIC);
+		sw = XmCreateScrolledWindow(attr_dialog, XmStrCast("attributes"), stdarg_args, stdarg_n);
+		XtManageChild(sw);
+
+		stdarg_n = 0;
+		stdarg(XmNmarginHeight, 0);
+		stdarg(XmNmarginWidth, 0);
+		f_top = XmCreateBulletinBoard(sw, XmStrCast("f_top"), stdarg_args, stdarg_n);
+		XtManageChild(f_top);
+	}
+	else {
+		stdarg_n = 0;
+		stdarg(XmNtitle, owner);
+		XtSetValues(XtParent(attr_dialog), stdarg_args, stdarg_n);
+	}
+
+	attributes_list = attrs_list;
+	lesstif_attributes_revert();
+
+	fiddle_with_bb_layout();
+
+	if (wait_for_dialog(attr_dialog) == 0) {
+		int i;
+		/* Copy the values back */
+		for (i = 0; i < attributes_list->Number; i++) {
+			if (attributes_list->List[i].name)
+				free(attributes_list->List[i].name);
+			if (attributes_list->List[i].value)
+				free(attributes_list->List[i].value);
+		}
+		if (attributes_list->Max < attr_num_rows) {
+			int sz = attr_num_rows * sizeof(AttributeType);
+			if (attributes_list->List == NULL)
+				attributes_list->List = (AttributeType *) malloc(sz);
+			else
+				attributes_list->List = (AttributeType *) realloc(attributes_list->List, sz);
+			attributes_list->Max = attr_num_rows;
+		}
+		for (i = 0; i < attr_num_rows; i++) {
+			attributes_list->List[i].name = pcb_strdup(XmTextFieldGetString(attr_row[i].w_name));
+			attributes_list->List[i].value = pcb_strdup(XmTextFieldGetString(attr_row[i].w_value));
+			attributes_list->Number = attr_num_rows;
+		}
+	}
+
+	return;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char importgui_syntax[] = "ImportGUI()";
+
+static const char importgui_help[] = "Lets the user choose the schematics to import from";
+
+/* %start-doc actions ImportGUI
+
+Displays a dialog that lets the user select the schematic(s) to import
+from, then saves that information in the layout's attributes for
+future imports.
+
+%end-doc */
+
+static int ImportGUI(int argc, const char **argv, Coord x, Coord y)
+{
+	static int I_am_recursing = 0;
+	static XmString xms_sch = 0, xms_import = 0;
+	int rv;
+	XmString xmname;
+	char *name, *bname;
+	char *original_dir, *target_dir, *last_slash;
+
+	if (I_am_recursing)
+		return 1;
+
+	if (xms_sch == 0)
+		xms_sch = XmStringCreatePCB("*.sch");
+	if (xms_import == 0)
+		xms_import = XmStringCreatePCB("Import from");
+
+	setup_fsb_dialog();
+
+	stdarg_n = 0;
+	stdarg(XmNtitle, "Import From");
+	XtSetValues(XtParent(fsb), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNpattern, xms_sch);
+	stdarg(XmNmustMatch, True);
+	stdarg(XmNselectionLabelString, xms_import);
+	XtSetValues(fsb, stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNdirectory, &xmname);
+	XtGetValues(fsb, stdarg_args, stdarg_n);
+	XmStringGetLtoR(xmname, XmFONTLIST_DEFAULT_TAG, &original_dir);
+
+	if (!wait_for_dialog(fsb))
+		return 1;
+
+	stdarg_n = 0;
+	stdarg(XmNdirectory, &xmname);
+	XtGetValues(fsb, stdarg_args, stdarg_n);
+	XmStringGetLtoR(xmname, XmFONTLIST_DEFAULT_TAG, &target_dir);
+
+	stdarg_n = 0;
+	stdarg(XmNdirSpec, &xmname);
+	XtGetValues(fsb, stdarg_args, stdarg_n);
+
+	XmStringGetLtoR(xmname, XmFONTLIST_DEFAULT_TAG, &name);
+
+	/* If the user didn't change directories, use just the base name.
+	   This is the common case and means we don't have to get clever
+	   about converting absolute paths into relative paths.  */
+	bname = name;
+	if (strcmp(original_dir, target_dir) == 0) {
+		last_slash = strrchr(name, '/');
+		if (last_slash)
+			bname = last_slash + 1;
+	}
+
+	AttributePut(PCB, "import::src0", bname);
+
+	XtFree(name);
+
+
+	I_am_recursing = 1;
+	rv = hid_action("Import");
+	I_am_recursing = 0;
+
+	return rv;
+}
+
+/* ------------------------------------------------------------ */
+
+HID_Action lesstif_dialog_action_list[] = {
+	{"Load", 0, Load,
+	 load_help, load_syntax}
+	,
+	{"LoadVendor", 0, LoadVendor,
+	 loadvendor_help, loadvendor_syntax}
+	,
+	{"Save", 0, Save,
+	 save_help, save_syntax}
+	,
+	{"DoWindows", 0, DoWindows,
+	 dowindows_help, dowindows_syntax}
+	,
+	{"PromptFor", 0, PromptFor,
+	 promptfor_help, promptfor_syntax}
+	,
+	{"Confirm", 0, ConfirmAction}
+	,
+	{"About", 0, About,
+	 about_help, about_syntax}
+	,
+	{"Print", 0, Print,
+	 print_help, print_syntax}
+	,
+	{"PrintCalibrate", 0, PrintCalibrate,
+	 printcalibrate_help, printcalibrate_syntax}
+	,
+	{"Export", 0, Export,
+	 export_help, export_syntax}
+	,
+	{"AdjustSizes", 0, AdjustSizes,
+	 adjustsizes_help, adjustsizes_syntax}
+	,
+	{"EditLayerGroups", 0, EditLayerGroups,
+	 editlayergroups_help, editlayergroups_syntax}
+	,
+	{"ImportGUI", 0, ImportGUI,
+	 importgui_help, importgui_syntax}
+	,
+};
+
+REGISTER_ACTIONS(lesstif_dialog_action_list, lesstif_cookie)
diff --git a/src_plugins/hid_lesstif/lesstif.h b/src_plugins/hid_lesstif/lesstif.h
new file mode 100644
index 0000000..f2ba2e8
--- /dev/null
+++ b/src_plugins/hid_lesstif/lesstif.h
@@ -0,0 +1,84 @@
+#include "hid_cfg_input.h"
+
+#define app_context lesstif_app_context
+#define appwidget lesstif_appwidget
+#define display lesstif_display
+#define screen_s lesstif_screen_s
+#define screen lesstif_screen
+#define mainwind lesstif_mainwind
+#define work_area lesstif_work_area
+#define messages lesstif_messages
+#define command lesstif_command
+#define hscroll lesstif_hscroll
+#define vscroll lesstif_vscroll
+#define m_click lesstif_message_click
+
+extern XtAppContext app_context;
+extern Widget appwidget;
+extern Display *display;
+extern Screen *screen_s;
+extern int screen;
+
+extern hid_cfg_mouse_t lesstif_mouse;
+extern hid_cfg_keys_t lesstif_keymap;
+
+extern Widget mainwind, work_area, command, hscroll, vscroll;
+extern Widget m_click;
+
+extern Widget lesstif_menu(Widget, const char *, Arg *, int);
+extern int lesstif_key_event(XKeyEvent *);
+extern int lesstif_button_event(Widget w, XEvent * e);
+
+/* Returns TRUE if the point mapped to the PCB region, FALSE (=0) if
+   we're off-board.  Note that *pcbxy is always written to, even if
+   out of range.  */
+extern int lesstif_winxy_to_pcbxy(int winx, int winy, int *pcbx, int *pcby);
+
+/* Returns TRUE if the point is in the window, FALSE (=0) otherwise. */
+extern int lesstif_pcbxy_to_winxy(int pcbx, int pcby, int *winx, int *winy);
+
+extern void lesstif_need_idle_proc(void);
+extern void lesstif_show_crosshair(int);
+extern void lesstif_invalidate_all(void);
+extern void lesstif_coords_to_pcb(int, int, Coord *, Coord *);
+extern void lesstif_get_xy(const char *msg);
+extern void lesstif_update_widget_flags(void);
+extern int lesstif_call_action(const char *, int, char **);
+extern void lesstif_sizes_reset(void);
+extern void lesstif_pan_fixup(void);
+extern void lesstif_show_library(void);
+extern void lesstif_show_netlist(void);
+extern Pixel lesstif_parse_color(const char *value);
+extern void lesstif_insert_style_buttons(Widget menu);
+extern void lesstif_styles_update_values();
+extern void lesstif_update_layer_groups();
+extern void lesstif_update_status_line();
+extern char *lesstif_prompt_for(const char *, const char *);
+extern char *lesstif_fileselect(const char *, const char *, const char *, const char *, const char *, int);
+extern void lesstif_log(const char *fmt, ...);
+extern void lesstif_attributes_dialog(const char *, AttributeListType *);
+
+#ifndef XtRPCBCoord
+#define XtRPCBCoord	"PCBCoord"
+#endif
+
+#define need_idle_proc lesstif_need_idle_proc
+#define show_crosshair lesstif_show_crosshair
+
+/*
+ * Motif comes from a time when even constant strings where
+ * passed as char*. These days, this requires to do ugly
+ * type-casting. To better identify all the places where this
+ * is necessary, we make this cast even more ugly but unique
+ * enough that it is simple to grep.
+ */
+#define XmStrCast(s) ((char*)(s))
+
+static XmString XmStringCreatePCB(const char *x)
+{
+	if (x && x[0])
+		x = gettext(x);
+	return XmStringCreateLtoR(XmStrCast(x), XmFONTLIST_DEFAULT_TAG);
+}
+
+extern const char *lesstif_cookie;
diff --git a/src_plugins/hid_lesstif/library.c b/src_plugins/hid_lesstif/library.c
new file mode 100644
index 0000000..976eef4
--- /dev/null
+++ b/src_plugins/hid_lesstif/library.c
@@ -0,0 +1,195 @@
+#include "xincludes.h"
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat_misc.h"
+#include "global.h"
+#include "data.h"
+#include "misc.h"
+#include "set.h"
+#include "buffer.h"
+#include "vtptr.h"
+#include "plug_footprint.h"
+
+#include "hid.h"
+#include "lesstif.h"
+#include "stdarg.h"
+
+static Widget library_dialog = 0;
+static Widget library_list, libnode_list;
+
+static XmString *library_strings = 0;
+static XmString *libnode_strings = 0;
+static int last_pick = -1;
+
+vtptr_t picks;      /* of library_t * */
+vtptr_t pick_names; /* of char * */
+
+static void pick_net(int pick)
+{
+	library_t *menu = (library_t *)picks.array[pick];
+	int i, found;
+
+	if (pick == last_pick)
+		return;
+	last_pick = pick;
+
+	if (libnode_strings)
+		free(libnode_strings);
+
+	libnode_strings = (XmString *) malloc(menu->data.dir.children.used * sizeof(XmString));
+	for (found = 0, i = 0; i < menu->data.dir.children.used; i++) {
+		if (menu->data.dir.children.array[i].type == LIB_FOOTPRINT) {
+			libnode_strings[i] = XmStringCreatePCB(menu->data.dir.children.array[i].name);
+			found++;
+		}
+	}
+
+	stdarg_n = 0;
+	stdarg(XmNitems, libnode_strings);
+	stdarg(XmNitemCount, found);
+	XtSetValues(libnode_list, stdarg_args, stdarg_n);
+}
+
+static void library_browse(Widget w, void *v, XmListCallbackStruct * cbs)
+{
+	pick_net(cbs->item_position - 1);
+}
+
+static void libnode_select(Widget w, void *v, XmListCallbackStruct * cbs)
+{
+	library_t *e = picks.array[last_pick];
+	e = &e->data.dir.children.array[cbs->item_position - 1];
+	if (LoadElementToBuffer(PASTEBUFFER, e->data.fp.loc_info))
+		SetMode(PCB_MODE_PASTE_BUFFER);
+}
+
+static int build_library_dialog()
+{
+	if (!mainwind)
+		return 1;
+	if (library_dialog)
+		return 0;
+
+	stdarg_n = 0;
+	stdarg(XmNresizePolicy, XmRESIZE_GROW);
+	stdarg(XmNtitle, "Element Library");
+	library_dialog = XmCreateFormDialog(mainwind, XmStrCast("library"), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNvisibleItemCount, 10);
+	library_list = XmCreateScrolledList(library_dialog, XmStrCast("nets"), stdarg_args, stdarg_n);
+	XtManageChild(library_list);
+	XtAddCallback(library_list, XmNbrowseSelectionCallback, (XtCallbackProc) library_browse, 0);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+	stdarg(XmNleftWidget, library_list);
+	libnode_list = XmCreateScrolledList(library_dialog, XmStrCast("nodes"), stdarg_args, stdarg_n);
+	XtManageChild(libnode_list);
+	XtAddCallback(libnode_list, XmNbrowseSelectionCallback, (XtCallbackProc) libnode_select, 0);
+
+	return 0;
+}
+
+static void lib_dfs(library_t *parent, int level)
+{
+	library_t *l;
+	char *s;
+	int n, len;
+
+	if (parent->type != LIB_DIR)
+		return;
+
+	if (parent->name != NULL) {
+		vtptr_append(&picks, parent);
+		len = strlen(parent->name);
+		s = malloc(len+level+1);
+		for(n = 0; n < level-1; n++) s[n] = ' ';
+		strcpy(s+level-1, parent->name);
+		vtptr_append(&pick_names, s);
+	}
+
+	for(l = parent->data.dir.children.array, n = 0; n < parent->data.dir.children.used; n++,l++)
+		lib_dfs(l, level+1);
+}
+
+static int LibraryChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	int i;
+	if (library.data.dir.children.used == 0)
+		return 0;
+	if (build_library_dialog())
+		return 0;
+	last_pick = -1;
+
+	for (i = 0; i < pick_names.used; i++)
+		free(pick_names.array[i]);
+
+	vtptr_truncate(&picks, 0);
+	vtptr_truncate(&pick_names, 0);
+
+	lib_dfs(&library, 0);
+
+
+	if (library_strings)
+		free(library_strings);
+	library_strings = (XmString *) malloc(picks.used * sizeof(XmString));
+	for (i = 0; i < picks.used; i++)
+		library_strings[i] = XmStringCreatePCB(pick_names.array[i]);
+
+	stdarg_n = 0;
+	stdarg(XmNitems, library_strings);
+	stdarg(XmNitemCount, picks.used);
+	XtSetValues(library_list, stdarg_args, stdarg_n);
+
+	pick_net(0);
+	return 0;
+}
+
+static const char libraryshow_syntax[] = "LibraryShow()";
+
+static const char libraryshow_help[] = "Displays the library window.";
+
+/* %start-doc actions LibraryShow
+
+%end-doc */
+
+static int LibraryShow(int argc, const char **argv, Coord x, Coord y)
+{
+	if (build_library_dialog())
+		return 0;
+	return 0;
+}
+
+void lesstif_show_library()
+{
+	if (mainwind) {
+		if (!library_dialog)
+			LibraryChanged(0, 0, 0, 0);
+		XtManageChild(library_dialog);
+	}
+}
+
+HID_Action lesstif_library_action_list[] = {
+	{"LibraryChanged", 0, LibraryChanged,
+	 librarychanged_help, librarychanged_syntax}
+	,
+	{"LibraryShow", 0, LibraryShow,
+	 libraryshow_help, libraryshow_syntax}
+	,
+};
+
+REGISTER_ACTIONS(lesstif_library_action_list, lesstif_cookie)
diff --git a/src_plugins/hid_lesstif/main.c b/src_plugins/hid_lesstif/main.c
new file mode 100644
index 0000000..1b6cb75
--- /dev/null
+++ b/src_plugins/hid_lesstif/main.c
@@ -0,0 +1,3854 @@
+/* 15 Oct 2008 Ineiev: add different crosshair shapes */
+
+#include "xincludes.h"
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <time.h>
+#include <string.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "global.h"
+#include "data.h"
+#include "action_helper.h"
+#include "crosshair.h"
+#include "layer.h"
+#include "mymem.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "clip.h"
+#include "event.h"
+#include "error.h"
+#include "plugins.h"
+
+#include "hid.h"
+#include "hid_nogui.h"
+#include "hid_draw_helpers.h"
+#include "hid_cfg.h"
+#include "lesstif.h"
+#include "hid_cfg_input.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "hid_init.h"
+#include "hid_color.h"
+#include "hid_extents.h"
+#include "hid_flags.h"
+#include "hid_actions.h"
+#include "stdarg.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+
+#include <sys/poll.h>
+
+const char *lesstif_cookie = "lesstif HID";
+
+hid_cfg_mouse_t lesstif_mouse;
+hid_cfg_keys_t lesstif_keymap;
+
+
+#ifndef XtRDouble
+#define XtRDouble "Double"
+#endif
+
+/* How big the viewport can be relative to the pcb size.  */
+#define MAX_ZOOM_SCALE	10
+#define UUNIT	conf_core.editor.grid_unit->allow
+
+typedef struct hid_gc_struct {
+	HID *me_pointer;
+	Pixel color;
+	char *colorname;
+	int width;
+	EndCapStyle cap;
+	char xor_set;
+	char erase;
+} hid_gc_struct;
+
+static HID lesstif_hid;
+
+#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented GUI function %s\n", func), abort()
+
+XtAppContext app_context;
+Widget appwidget;
+Display *display;
+static Window window = 0;
+static Cursor my_cursor = 0;
+static int old_cursor_mode = -1;
+static int over_point = 0;
+
+/* The first is the "current" pixmap.  The main_ is the real one we
+   usually use, the mask_ are the ones for doing polygon masks.  The
+   pixmap is the saved pixels, the bitmap is for the "erase" color.
+   We set pixmap to point to main_pixmap or mask_pixmap as needed.  */
+static Pixmap pixmap = 0;
+static Pixmap main_pixmap = 0;
+static Pixmap mask_pixmap = 0;
+static Pixmap mask_bitmap = 0;
+static int use_mask = 0;
+
+static int use_xrender = 0;
+#ifdef HAVE_XRENDER
+static Picture main_picture;
+static Picture mask_picture;
+static Pixmap pale_pixmap;
+static Picture pale_picture;
+#endif /* HAVE_XRENDER */
+
+static int pixmap_w = 0, pixmap_h = 0;
+Screen *screen_s;
+int screen;
+Colormap lesstif_colormap;
+static GC my_gc = 0, bg_gc, clip_gc = 0, bset_gc = 0, bclear_gc = 0, mask_gc = 0;
+static Pixel bgcolor, offlimit_color, grid_color;
+static int bgred, bggreen, bgblue;
+
+static GC arc1_gc, arc2_gc;
+
+/* These are for the pinout windows. */
+typedef struct PinoutData {
+	struct PinoutData *prev, *next;
+	Widget form;
+	Window window;
+	Coord left, right, top, bottom;	/* PCB extents of item */
+	Coord x, y;										/* PCB coordinates of upper right corner of window */
+	double zoom;									/* PCB units per screen pixel */
+	int v_width, v_height;				/* pixels */
+	void *item;
+} PinoutData;
+
+/* Linked list of all pinout windows.  */
+static PinoutData *pinouts = 0;
+/* If set, we are currently updating this pinout window.  */
+static PinoutData *pinout = 0;
+
+static int crosshair_x = 0, crosshair_y = 0;
+static int in_move_event = 0, crosshair_in_window = 1;
+
+Widget mainwind;
+Widget work_area, messages, command, hscroll, vscroll;
+static Widget m_mark, m_crosshair, m_grid, m_zoom, m_mode, m_status;
+static Widget m_rats;
+Widget lesstif_m_layer;
+Widget m_click;
+
+/* This is the size, in pixels, of the viewport.  */
+static int view_width, view_height;
+/* This is the PCB location represented by the upper left corner of
+   the viewport.  Note that PCB coordinates put 0,0 in the upper left,
+   much like X does.  */
+static int view_left_x = 0, view_top_y = 0;
+/* Denotes PCB units per screen pixel.  Larger numbers mean zooming
+   out - the largest value means you are looking at the whole
+   board.  */
+static double view_zoom = PCB_MIL_TO_COORD(10), prev_view_zoom = PCB_MIL_TO_COORD(10);
+static pcb_bool autofade = 0;
+static pcb_bool crosshair_on = pcb_true;
+
+static void lesstif_begin(void);
+static void lesstif_end(void);
+
+static void ShowCrosshair(pcb_bool show)
+{
+	if (crosshair_on == show)
+		return;
+
+	notify_crosshair_change(pcb_false);
+	if (Marked.status)
+		notify_mark_change(pcb_false);
+
+	crosshair_on = show;
+
+	notify_crosshair_change(pcb_true);
+	if (Marked.status)
+		notify_mark_change(pcb_true);
+}
+
+/* This is the size of the current PCB work area.  */
+/* Use PCB->MaxWidth, PCB->MaxHeight.  */
+/* static int pcb_width, pcb_height; */
+		 static int use_private_colormap = 0;
+		 static int stdin_listen = 0;
+		 static char *background_image_file = 0;
+
+		 HID_Attribute lesstif_attribute_list[] = {
+			 {"install", "Install private colormap",
+				HID_Boolean, 0, 0, {0, 0, 0}, 0, &use_private_colormap},
+#define HA_colormap 0
+
+/* %start-doc options "22 lesstif GUI Options"
+ at ftable @code
+ at item --listen
+Listen for actions on stdin.
+ at end ftable
+%end-doc
+*/
+			 {"listen", "Listen on standard input for actions",
+				HID_Boolean, 0, 0, {0, 0, 0}, 0, &stdin_listen},
+#define HA_listen 1
+
+/* %start-doc options "22 lesstif GUI Options"
+ at ftable @code
+ at item --bg-image <string>
+File name of an image to put into the background of the GUI canvas. The image must
+be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
+automatically scaled to fit the canvas.
+ at end ftable
+%end-doc
+*/
+			 {"bg-image", "Background Image",
+				HID_String, 0, 0, {0, 0, 0}, 0, &background_image_file},
+#define HA_bg_image 2
+
+/* %start-doc options "22 lesstif GUI Options"
+ at ftable @code
+ at item --pcb-menu <string>
+Location of the @file{pcb-menu.res} file which defines the menu for the lesstif GUI.
+ at end ftable
+%end-doc
+*/
+#warning TODO#1: this should be generic and not depend on the HID
+/*
+			 {"pcb-menu", "Location of pcb-menu.res file",
+				HID_String, 0, 0, {0, PCBSHAREDIR "/pcb-menu.res", 0}, 0, &lesstif_pcbmenu_path}
+#define HA_pcbmenu 3
+*/
+		 };
+
+REGISTER_ATTRIBUTES(lesstif_attribute_list, lesstif_cookie)
+
+		 static void lesstif_use_mask(int use_it);
+		 static void zoom_max();
+		 static void zoom_to(double factor, int x, int y);
+		 static void zoom_by(double factor, int x, int y);
+		 static void zoom_toggle(int x, int y);
+		 static void pinout_callback(Widget, PinoutData *, XmDrawingAreaCallbackStruct *);
+		 static void pinout_unmap(Widget, PinoutData *, void *);
+		 static void Pan(int mode, int x, int y);
+
+/* Px converts view->pcb, Vx converts pcb->view */
+
+		 static inline int
+		   Vx(Coord x)
+{
+	int rv = (x - view_left_x) / view_zoom + 0.5;
+	if (conf_core.editor.view.flip_x)
+		rv = view_width - rv;
+	return rv;
+}
+
+static inline int Vy(Coord y)
+{
+	int rv = (y - view_top_y) / view_zoom + 0.5;
+	if (conf_core.editor.view.flip_y)
+		rv = view_height - rv;
+	return rv;
+}
+
+static inline int Vz(Coord z)
+{
+	return z / view_zoom + 0.5;
+}
+
+static inline Coord Px(int x)
+{
+	if (conf_core.editor.view.flip_x)
+		x = view_width - x;
+	return x * view_zoom + view_left_x;
+}
+
+static inline Coord Py(int y)
+{
+	if (conf_core.editor.view.flip_y)
+		y = view_height - y;
+	return y * view_zoom + view_top_y;
+}
+
+static inline Coord Pz(int z)
+{
+	return z * view_zoom;
+}
+
+void lesstif_coords_to_pcb(int vx, int vy, Coord * px, Coord * py)
+{
+	*px = Px(vx);
+	*py = Py(vy);
+}
+
+Pixel lesstif_parse_color(const char *value)
+{
+	XColor color;
+	if (XParseColor(display, lesstif_colormap, value, &color))
+		if (XAllocColor(display, lesstif_colormap, &color))
+			return color.pixel;
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static const char *cur_clip()
+{
+	if (conf_core.editor.orthogonal_moves)
+		return "+";
+	if (conf_core.editor.all_direction_lines)
+		return "*";
+	if (conf_core.editor.line_refraction == 0)
+		return "X";
+	if (conf_core.editor.line_refraction == 1)
+		return "_/";
+	return "\\_";
+}
+
+/* Called from the core when it's busy doing something and we need to
+   indicate that to the user.  */
+static int Busy(int argc, const char **argv, Coord x, Coord y)
+{
+	static Cursor busy_cursor = 0;
+	if (busy_cursor == 0)
+		busy_cursor = XCreateFontCursor(display, XC_watch);
+	XDefineCursor(display, window, busy_cursor);
+	XFlush(display);
+	old_cursor_mode = -1;
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Local actions.  */
+
+static int PointCursor(int argc, const char **argv, Coord x, Coord y)
+{
+	if (argc > 0)
+		over_point = 1;
+	else
+		over_point = 0;
+	old_cursor_mode = -1;
+	return 0;
+}
+
+static int PCBChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	if (work_area == 0)
+		return 0;
+	/*pcb_printf("PCB Changed! %$mD\n", PCB->MaxWidth, PCB->MaxHeight); */
+	stdarg_n = 0;
+	stdarg(XmNminimum, 0);
+	stdarg(XmNvalue, 0);
+	stdarg(XmNsliderSize, PCB->MaxWidth ? PCB->MaxWidth : 1);
+	stdarg(XmNmaximum, PCB->MaxWidth ? PCB->MaxWidth : 1);
+	XtSetValues(hscroll, stdarg_args, stdarg_n);
+	stdarg_n = 0;
+	stdarg(XmNminimum, 0);
+	stdarg(XmNvalue, 0);
+	stdarg(XmNsliderSize, PCB->MaxHeight ? PCB->MaxHeight : 1);
+	stdarg(XmNmaximum, PCB->MaxHeight ? PCB->MaxHeight : 1);
+	XtSetValues(vscroll, stdarg_args, stdarg_n);
+	zoom_max();
+
+	hid_action("NetlistChanged");
+	hid_action("LayersChanged");
+	hid_action("RouteStylesChanged");
+	lesstif_sizes_reset();
+	lesstif_update_layer_groups();
+	while (pinouts)
+		pinout_unmap(0, pinouts, 0);
+	if (PCB->Filename) {
+		char *cp = strrchr(PCB->Filename, '/');
+		stdarg_n = 0;
+		stdarg(XmNtitle, cp ? cp + 1 : PCB->Filename);
+		XtSetValues(appwidget, stdarg_args, stdarg_n);
+	}
+	return 0;
+}
+
+
+static const char setunits_syntax[] = "SetUnits(mm|mil)";
+
+static const char setunits_help[] = "Set the default measurement units.";
+
+/* %start-doc actions SetUnits
+
+ at table @code
+
+ at item mil
+Sets the display units to mils (1/1000 inch).
+
+ at item mm
+Sets the display units to millimeters.
+
+ at end table
+
+%end-doc */
+
+static int SetUnits(int argc, const char **argv, Coord x, Coord y)
+{
+	const Unit *new_unit;
+	if (argc == 0)
+		return 0;
+	new_unit = get_unit_struct(argv[0]);
+	if (new_unit != NULL && new_unit->allow != NO_PRINT) {
+		conf_set(CFR_DESIGN, "editor/grid_unit", -1, argv[0], POL_OVERWRITE);
+#warning TODO: figure what to do with increments
+#if 0
+		Settings.increments = get_increments_struct(Settings.grid_unit->suffix);
+#endif
+		AttributePut(PCB, "PCB::grid::unit", argv[0]);
+	}
+	lesstif_sizes_reset();
+	lesstif_styles_update_values();
+	return 0;
+}
+
+static const char zoom_syntax[] = "Zoom()\n" "Zoom(factor)";
+
+static const char zoom_help[] = "Various zoom factor changes.";
+
+/* %start-doc actions Zoom
+
+Changes the zoom (magnification) of the view of the board.  If no
+arguments are passed, the view is scaled such that the board just fits
+inside the visible window (i.e. ``view all'').  Otherwise,
+ at var{factor} specifies a change in zoom factor.  It may be prefixed by
+ at code{+}, @code{-}, or @code{=} to change how the zoom factor is
+modified.  The @var{factor} is a floating point number, such as
+ at code{1.5} or @code{0.75}.
+
+ at table @code
+
+ at item + at var{factor}
+Values greater than 1.0 cause the board to be drawn smaller; more of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn bigger; less of the board will be visible.
+
+ at item - at var{factor}
+Values greater than 1.0 cause the board to be drawn bigger; less of
+the board will be visible.  Values between 0.0 and 1.0 cause the board
+to be drawn smaller; more of the board will be visible.
+
+ at item =@var{factor}
+
+The @var{factor} is an absolute zoom factor; the unit for this value
+is "PCB units per screen pixel".  Since PCB units are 0.01 mil, a
+ at var{factor} of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI,
+about the actual resolution of most screens - resulting in an "actual
+size" board.  Similarly, a @var{factor} of 100 gives you a 10x actual
+size.
+
+ at end table
+
+Note that zoom factors of zero are silently ignored.
+
+%end-doc */
+
+static int ZoomAction(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *vp;
+	double v;
+	if (x == 0 && y == 0) {
+		x = view_width / 2;
+		y = view_height / 2;
+	}
+	else {
+		x = Vx(x);
+		y = Vy(y);
+	}
+	if (argc < 1) {
+		zoom_max();
+		return 0;
+	}
+	vp = argv[0];
+	if (strcasecmp(vp, "toggle") == 0) {
+		zoom_toggle(x, y);
+		return 0;
+	}
+	if (*vp == '+' || *vp == '-' || *vp == '=')
+		vp++;
+	setlocale(LC_ALL, "C");
+	v = strtod(vp, NULL);
+	setlocale(LC_ALL, "");
+	if (v <= 0)
+		return 1;
+	switch (argv[0][0]) {
+	case '-':
+		zoom_by(1 / v, x, y);
+		break;
+	default:
+	case '+':
+		zoom_by(v, x, y);
+		break;
+	case '=':
+		zoom_to(v, x, y);
+		break;
+	}
+	return 0;
+}
+
+static int pan_thumb_mode;
+
+static int PanAction(int argc, const char **argv, Coord x, Coord y)
+{
+	int mode;
+
+	if (argc == 2) {
+		pan_thumb_mode = (strcasecmp(argv[0], "thumb") == 0) ? 1 : 0;
+		mode = atoi(argv[1]);
+	}
+	else {
+		pan_thumb_mode = 0;
+		mode = atoi(argv[0]);
+	}
+	Pan(mode, Vx(x), Vy(y));
+
+	return 0;
+}
+
+static const char swapsides_syntax[] = "SwapSides(|v|h|r)";
+
+static const char swapsides_help[] = "Swaps the side of the board you're looking at.";
+
+/* %start-doc actions SwapSides
+
+This action changes the way you view the board.
+
+ at table @code
+
+ at item v
+Flips the board over vertically (up/down).
+
+ at item h
+Flips the board over horizontally (left/right), like flipping pages in
+a book.
+
+ at item r
+Rotates the board 180 degrees without changing sides.
+
+ at end table
+
+If no argument is given, the board isn't moved but the opposite side
+is shown.
+
+Normally, this action changes which pads and silk layer are drawn as
+pcb_true silk, and which are drawn as the "invisible" layer.  It also
+determines which solder mask you see.
+
+As a special case, if the layer group for the side you're looking at
+is visible and currently active, and the layer group for the opposite
+is not visible (i.e. disabled), then this action will also swap which
+layer group is visible and active, effectively swapping the ``working
+side'' of the board.
+
+%end-doc */
+
+static int group_showing(int g, int *c)
+{
+	int i, l;
+	*c = PCB->LayerGroups.Entries[g][0];
+	for (i = 0; i < PCB->LayerGroups.Number[g]; i++) {
+		l = PCB->LayerGroups.Entries[g][i];
+		if (l >= 0 && l < max_copper_layer) {
+			*c = l;
+			if (PCB->Data->Layer[l].On)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static int SwapSides(int argc, const char **argv, Coord x, Coord y)
+{
+	int old_shown_side = conf_core.editor.show_solder_side;
+	int comp_group = GetLayerGroupNumberByNumber(component_silk_layer);
+	int solder_group = GetLayerGroupNumberByNumber(solder_silk_layer);
+	int active_group = GetLayerGroupNumberByNumber(LayerStack[0]);
+	int comp_layer;
+	int solder_layer;
+	int comp_showing = group_showing(comp_group, &comp_layer);
+	int solder_showing = group_showing(solder_group, &solder_layer);
+
+	if (argc > 0) {
+		switch (argv[0][0]) {
+		case 'h':
+		case 'H':
+			conf_toggle_editor_("view/flip_x", view.flip_x);
+			break;
+		case 'v':
+		case 'V':
+			conf_toggle_editor_("view/flip_y", view.flip_y);
+			break;
+		case 'r':
+		case 'R':
+			conf_toggle_editor_("view/flip_x", view.flip_x);
+			conf_toggle_editor_("view/flip_y", view.flip_y);
+			break;
+		default:
+			return 1;
+		}
+		/* SwapSides will swap this */
+		conf_set_editor(show_solder_side, (conf_core.editor.view.flip_x == conf_core.editor.view.flip_y));
+	}
+
+	stdarg_n = 0;
+	if (conf_core.editor.view.flip_x)
+		stdarg(XmNprocessingDirection, XmMAX_ON_LEFT);
+	else
+		stdarg(XmNprocessingDirection, XmMAX_ON_RIGHT);
+	XtSetValues(hscroll, stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	if (conf_core.editor.view.flip_y)
+		stdarg(XmNprocessingDirection, XmMAX_ON_TOP);
+	else
+		stdarg(XmNprocessingDirection, XmMAX_ON_BOTTOM);
+	XtSetValues(vscroll, stdarg_args, stdarg_n);
+
+	conf_toggle_editor(show_solder_side);
+
+	/* The idea is that if we're looking at the front side and the front
+	   layer is active (or visa versa), switching sides should switch
+	   layers too.  We used to only do this if the other layer wasn't
+	   shown, but we now do it always.  Change it back if users get
+	   confused.  */
+	if (conf_core.editor.show_solder_side != old_shown_side) {
+		if (conf_core.editor.show_solder_side) {
+			if (active_group == comp_group) {
+				if (comp_showing && !solder_showing)
+					ChangeGroupVisibility(comp_layer, 0, 0);
+				ChangeGroupVisibility(solder_layer, 1, 1);
+			}
+		}
+		else {
+			if (active_group == solder_group) {
+				if (solder_showing && !comp_showing)
+					ChangeGroupVisibility(solder_layer, 0, 0);
+				ChangeGroupVisibility(comp_layer, 1, 1);
+			}
+		}
+	}
+	lesstif_invalidate_all();
+	return 0;
+}
+
+static Widget m_cmd = 0, m_cmd_label;
+
+static void command_callback(Widget w, XtPointer uptr, XmTextVerifyCallbackStruct * cbs)
+{
+	char *s;
+	switch (cbs->reason) {
+	case XmCR_ACTIVATE:
+		s = XmTextGetString(w);
+		lesstif_show_crosshair(0);
+		hid_parse_command(s);
+		XtFree(s);
+		XmTextSetString(w, XmStrCast(""));
+	case XmCR_LOSING_FOCUS:
+		XtUnmanageChild(m_cmd);
+		XtUnmanageChild(m_cmd_label);
+		break;
+	}
+}
+
+static void command_event_handler(Widget w, XtPointer p, XEvent * e, Boolean * cont)
+{
+	char buf[10];
+	KeySym sym;
+
+	switch (e->type) {
+	case KeyPress:
+		XLookupString((XKeyEvent *) e, buf, sizeof(buf), &sym, NULL);
+		switch (sym) {
+		case XK_Escape:
+			XtUnmanageChild(m_cmd);
+			XtUnmanageChild(m_cmd_label);
+			XmTextSetString(w, XmStrCast(""));
+			*cont = False;
+			break;
+		}
+		break;
+	}
+}
+
+static const char command_syntax[] = "Command()";
+
+static const char command_help[] = "Displays the command line input window.";
+
+/* %start-doc actions Command
+
+The command window allows the user to manually enter actions to be
+executed.  Action syntax can be done one of two ways:
+
+ at table @code
+
+ at item
+Follow the action name by an open parenthesis, arguments separated by
+commas, end with a close parenthesis.  Example: @code{Abc(1,2,3)}
+
+ at item
+Separate the action name and arguments by spaces.  Example: @code{Abc
+1 2 3}.
+
+ at end table
+
+The first option allows you to have arguments with spaces in them,
+but the second is more ``natural'' to type for most people.
+
+Note that action names are not case sensitive, but arguments normally
+are.  However, most actions will check for ``keywords'' in a case
+insensitive way.
+
+There are three ways to finish with the command window.  If you press
+the @code{Enter} key, the command is invoked, the window goes away,
+and the next time you bring up the command window it's empty.  If you
+press the @code{Esc} key, the window goes away without invoking
+anything, and the next time you bring up the command window it's
+empty.  If you change focus away from the command window (i.e. click
+on some other window), the command window goes away but the next time
+you bring it up it resumes entering the command you were entering
+before.
+
+%end-doc */
+
+static int Command(int argc, const char **argv, Coord x, Coord y)
+{
+	XtManageChild(m_cmd_label);
+	XtManageChild(m_cmd);
+	XmProcessTraversal(m_cmd, XmTRAVERSE_CURRENT);
+	return 0;
+}
+
+static const char benchmark_syntax[] = "Benchmark()";
+
+static const char benchmark_help[] = "Benchmark the GUI speed.";
+
+/* %start-doc actions Benchmark
+
+This action is used to speed-test the Lesstif graphics subsystem.  It
+redraws the current screen as many times as possible in ten seconds.
+It reports the amount of time needed to draw the screen once.
+
+%end-doc */
+
+static int Benchmark(int argc, const char **argv, Coord x, Coord y)
+{
+	int i = 0;
+	time_t start, end;
+	BoxType region;
+	Drawable save_main;
+
+	save_main = main_pixmap;
+	main_pixmap = window;
+
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	pixmap = window;
+	XSync(display, 0);
+	time(&start);
+	do {
+		XFillRectangle(display, pixmap, bg_gc, 0, 0, view_width, view_height);
+		hid_expose_callback(&lesstif_hid, &region, 0);
+		XSync(display, 0);
+		time(&end);
+		i++;
+	}
+	while (end - start < 10);
+
+	printf("%g redraws per second\n", i / 10.0);
+
+	main_pixmap = save_main;
+	return 0;
+}
+
+static int Center(int argc, const char **argv, Coord x, Coord y)
+{
+	x = GridFit(x, PCB->Grid, PCB->GridOffsetX);
+	y = GridFit(y, PCB->Grid, PCB->GridOffsetY);
+	view_left_x = x - (view_width * view_zoom) / 2;
+	view_top_y = y - (view_height * view_zoom) / 2;
+	lesstif_pan_fixup();
+	/* Move the pointer to the center of the window, but only if it's
+	   currently within the window already.  Watch out for edges,
+	   though.  */
+	XWarpPointer(display, window, window, 0, 0, view_width, view_height, Vx(x), Vy(y));
+	return 0;
+}
+
+static const char cursor_syntax[] = "Cursor(Type,DeltaUp,DeltaRight,Units)";
+
+static const char cursor_help[] = "Move the cursor.";
+
+/* %start-doc actions Cursor
+
+This action moves the mouse cursor.  Unlike other actions which take
+coordinates, this action's coordinates are always relative to the
+user's view of the board.  Thus, a positive @var{DeltaUp} may move the
+cursor towards the board origin if the board is inverted.
+
+Type is one of @samp{Pan} or @samp{Warp}.  @samp{Pan} causes the
+viewport to move such that the crosshair is under the mouse cursor.
+ at samp{Warp} causes the mouse cursor to move to be above the crosshair.
+
+ at var{Units} can be one of the following:
+
+ at table @samp
+
+ at item mil
+ at itemx mm
+The cursor is moved by that amount, in board units.
+
+ at item grid
+The cursor is moved by that many grid points.
+
+ at item view
+The values are percentages of the viewport's view.  Thus, a pan of
+ at samp{100} would scroll the viewport by exactly the width of the
+current view.
+
+ at item board
+The values are percentages of the board size.  Thus, a move of
+ at samp{50,50} moves you halfway across the board.
+
+ at end table
+
+%end-doc */
+
+static int CursorAction(int argc, const char **argv, Coord x, Coord y)
+{
+	UnitList extra_units_x = {
+		{"grid", 0, 0},
+		{"view", 0, UNIT_PERCENT},
+		{"board", 0, UNIT_PERCENT},
+		{"", 0, 0}
+	};
+	UnitList extra_units_y = {
+		{"grid", 0, 0},
+		{"view", 0, UNIT_PERCENT},
+		{"board", 0, UNIT_PERCENT},
+		{"", 0, 0}
+	};
+	int pan_warp = HID_SC_DO_NOTHING;
+	double dx, dy;
+
+	extra_units_x[0].scale = PCB->Grid;
+	extra_units_x[1].scale = Pz(view_width);
+	extra_units_x[2].scale = PCB->MaxWidth;
+
+	extra_units_y[0].scale = PCB->Grid;
+	extra_units_y[1].scale = Pz(view_height);
+	extra_units_y[2].scale = PCB->MaxHeight;
+
+	if (argc != 4)
+		AFAIL(cursor);
+
+	if (strcasecmp(argv[0], "pan") == 0)
+		pan_warp = HID_SC_PAN_VIEWPORT;
+	else if (strcasecmp(argv[0], "warp") == 0)
+		pan_warp = HID_SC_WARP_POINTER;
+	else
+		AFAIL(cursor);
+
+	dx = GetValueEx(argv[1], argv[3], NULL, extra_units_x, "mil", NULL);
+	if (conf_core.editor.view.flip_x)
+		dx = -dx;
+	dy = GetValueEx(argv[2], argv[3], NULL, extra_units_y, "mil", NULL);
+	if (!conf_core.editor.view.flip_y)
+		dy = -dy;
+
+	EventMoveCrosshair(Crosshair.X + dx, Crosshair.Y + dy);
+	gui->set_crosshair(Crosshair.X, Crosshair.Y, pan_warp);
+
+	return 0;
+}
+
+HID_Action lesstif_main_action_list[] = {
+	{"PCBChanged", 0, PCBChanged,
+	 pcbchanged_help, pcbchanged_syntax}
+	,
+	{"SetUnits", 0, SetUnits,
+	 setunits_help, setunits_syntax}
+	,
+	{"Zoom", "Click on a place to zoom in", ZoomAction,
+	 zoom_help, zoom_syntax}
+	,
+	{"Pan", "Click on a place to pan", PanAction,
+	 zoom_help, zoom_syntax}
+	,
+	{"SwapSides", 0, SwapSides,
+	 swapsides_help, swapsides_syntax}
+	,
+	{"Command", 0, Command,
+	 command_help, command_syntax}
+	,
+	{"Benchmark", 0, Benchmark,
+	 benchmark_help, benchmark_syntax}
+	,
+	{"PointCursor", 0, PointCursor}
+	,
+	{"Center", "Click on a location to center", Center}
+	,
+	{"Busy", 0, Busy}
+	,
+	{"Cursor", 0, CursorAction,
+	 cursor_help, cursor_syntax}
+	,
+};
+
+REGISTER_ACTIONS(lesstif_main_action_list, lesstif_cookie)
+
+
+/* ----------------------------------------------------------------------
+ * redraws the background image
+ */
+		 static int bg_w, bg_h, bgi_w, bgi_h;
+		 static Pixel **bg = 0;
+		 static XImage *bgi = 0;
+		 static enum {
+			 PT_unknown,
+			 PT_RGB565,
+			 PT_RGB888
+		 } pixel_type = PT_unknown;
+
+		 static void
+		   LoadBackgroundFile(FILE * f, char *filename)
+{
+	XVisualInfo vinfot, *vinfo;
+	Visual *vis;
+	int c, r, b;
+	int i, nret;
+	int p[3], rows, cols, maxval;
+
+	if (fgetc(f) != 'P') {
+		printf("bgimage: %s signature not P6\n", filename);
+		return;
+	}
+	if (fgetc(f) != '6') {
+		printf("bgimage: %s signature not P6\n", filename);
+		return;
+	}
+	for (i = 0; i < 3; i++) {
+		do {
+			b = fgetc(f);
+			if (feof(f))
+				return;
+			if (b == '#')
+				while (!feof(f) && b != '\n')
+					b = fgetc(f);
+		} while (!isdigit(b));
+		p[i] = b - '0';
+		while (isdigit(b = fgetc(f)))
+			p[i] = p[i] * 10 + b - '0';
+	}
+	bg_w = cols = p[0];
+	bg_h = rows = p[1];
+	maxval = p[2];
+
+	setbuf(stdout, 0);
+	bg = (Pixel **) malloc(rows * sizeof(Pixel *));
+	if (!bg) {
+		printf("Out of memory loading %s\n", filename);
+		return;
+	}
+	for (i = 0; i < rows; i++) {
+		bg[i] = (Pixel *) malloc(cols * sizeof(Pixel));
+		if (!bg[i]) {
+			printf("Out of memory loading %s\n", filename);
+			while (--i >= 0)
+				free(bg[i]);
+			free(bg);
+			bg = 0;
+			return;
+		}
+	}
+
+	vis = DefaultVisual(display, DefaultScreen(display));
+
+	vinfot.visualid = XVisualIDFromVisual(vis);
+	vinfo = XGetVisualInfo(display, VisualIDMask, &vinfot, &nret);
+
+#if 0
+	/* If you want to support more visuals below, you'll probably need
+	   this. */
+	printf("vinfo: rm %04x gm %04x bm %04x depth %d class %d\n",
+				 vinfo->red_mask, vinfo->green_mask, vinfo->blue_mask, vinfo->depth, vinfo->class);
+#endif
+
+#if !defined(__cplusplus)
+#define c_class class
+#endif
+
+	if (vinfo->c_class == TrueColor
+			&& vinfo->depth == 16 && vinfo->red_mask == 0xf800 && vinfo->green_mask == 0x07e0 && vinfo->blue_mask == 0x001f)
+		pixel_type = PT_RGB565;
+
+	if (vinfo->c_class == TrueColor
+			&& vinfo->depth == 24 && vinfo->red_mask == 0xff0000 && vinfo->green_mask == 0x00ff00 && vinfo->blue_mask == 0x0000ff)
+		pixel_type = PT_RGB888;
+
+	for (r = 0; r < rows; r++) {
+		for (c = 0; c < cols; c++) {
+			XColor pix;
+			unsigned int pr = (unsigned) fgetc(f);
+			unsigned int pg = (unsigned) fgetc(f);
+			unsigned int pb = (unsigned) fgetc(f);
+
+			switch (pixel_type) {
+			case PT_unknown:
+				pix.red = pr * 65535 / maxval;
+				pix.green = pg * 65535 / maxval;
+				pix.blue = pb * 65535 / maxval;
+				pix.flags = DoRed | DoGreen | DoBlue;
+				XAllocColor(display, lesstif_colormap, &pix);
+				bg[r][c] = pix.pixel;
+				break;
+			case PT_RGB565:
+				bg[r][c] = (pr >> 3) << 11 | (pg >> 2) << 5 | (pb >> 3);
+				break;
+			case PT_RGB888:
+				bg[r][c] = (pr << 16) | (pg << 8) | (pb);
+				break;
+			}
+		}
+	}
+}
+
+void LoadBackgroundImage(char *filename)
+{
+	FILE *f = fopen(filename, "rb");
+	if (!f) {
+		if (NSTRCMP(filename, "pcb-background.ppm"))
+			perror(filename);
+		return;
+	}
+	LoadBackgroundFile(f, filename);
+	fclose(f);
+}
+
+static void DrawBackgroundImage()
+{
+	int x, y, w, h;
+	double xscale, yscale;
+	int pcbwidth = PCB->MaxWidth / view_zoom;
+	int pcbheight = PCB->MaxHeight / view_zoom;
+
+	if (!window || !bg)
+		return;
+
+	if (!bgi || view_width != bgi_w || view_height != bgi_h) {
+		if (bgi)
+			XDestroyImage(bgi);
+		/* Cheat - get the image, which sets up the format too.  */
+		bgi = XGetImage(XtDisplay(work_area), window, 0, 0, view_width, view_height, -1, ZPixmap);
+		bgi_w = view_width;
+		bgi_h = view_height;
+	}
+
+	w = MIN(view_width, pcbwidth);
+	h = MIN(view_height, pcbheight);
+
+	xscale = (double) bg_w / PCB->MaxWidth;
+	yscale = (double) bg_h / PCB->MaxHeight;
+
+	for (y = 0; y < h; y++) {
+		int pr = Py(y);
+		int ir = pr * yscale;
+		for (x = 0; x < w; x++) {
+			int pc = Px(x);
+			int ic = pc * xscale;
+			XPutPixel(bgi, x, y, bg[ir][ic]);
+		}
+	}
+	XPutImage(display, main_pixmap, bg_gc, bgi, 0, 0, 0, 0, w, h);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static HID_Attribute *lesstif_get_export_options(int *n)
+{
+	*n = sizeof(lesstif_attribute_list) / sizeof(HID_Attribute);
+	return lesstif_attribute_list;
+}
+
+static void set_scroll(Widget s, int pos, int view, int pcb)
+{
+	int sz = view * view_zoom;
+	if (sz > pcb)
+		sz = pcb;
+	stdarg_n = 0;
+	stdarg(XmNvalue, pos);
+	stdarg(XmNsliderSize, sz);
+	stdarg(XmNincrement, view_zoom);
+	stdarg(XmNpageIncrement, sz);
+	stdarg(XmNmaximum, pcb);
+	XtSetValues(s, stdarg_args, stdarg_n);
+}
+
+void lesstif_pan_fixup()
+{
+#if 0
+	if (view_left_x > PCB->MaxWidth - (view_width * view_zoom))
+		view_left_x = PCB->MaxWidth - (view_width * view_zoom);
+	if (view_top_y > PCB->MaxHeight - (view_height * view_zoom))
+		view_top_y = PCB->MaxHeight - (view_height * view_zoom);
+	if (view_left_x < 0)
+		view_left_x = 0;
+	if (view_top_y < 0)
+		view_top_y = 0;
+	if (view_width * view_zoom > PCB->MaxWidth && view_height * view_zoom > PCB->MaxHeight) {
+		zoom_by(1, 0, 0);
+		return;
+	}
+#endif
+
+	set_scroll(hscroll, view_left_x, view_width, PCB->MaxWidth);
+	set_scroll(vscroll, view_top_y, view_height, PCB->MaxHeight);
+
+	lesstif_invalidate_all();
+}
+
+static void zoom_max()
+{
+	double new_zoom = PCB->MaxWidth / view_width;
+	if (new_zoom < PCB->MaxHeight / view_height)
+		new_zoom = PCB->MaxHeight / view_height;
+
+	view_left_x = -(view_width * new_zoom - PCB->MaxWidth) / 2;
+	view_top_y = -(view_height * new_zoom - PCB->MaxHeight) / 2;
+	view_zoom = new_zoom;
+	pixel_slop = view_zoom;
+	lesstif_pan_fixup();
+}
+
+static void zoom_to(double new_zoom, int x, int y)
+{
+	double max_zoom, xfrac, yfrac;
+	int cx, cy;
+
+	if (PCB == NULL)
+		return;
+
+	xfrac = (double) x / (double) view_width;
+	yfrac = (double) y / (double) view_height;
+
+	if (conf_core.editor.view.flip_x)
+		xfrac = 1 - xfrac;
+	if (conf_core.editor.view.flip_y)
+		yfrac = 1 - yfrac;
+
+	max_zoom = PCB->MaxWidth / view_width;
+	if (max_zoom < PCB->MaxHeight / view_height)
+		max_zoom = PCB->MaxHeight / view_height;
+
+	max_zoom *= MAX_ZOOM_SCALE;
+
+	if (new_zoom < 1)
+		new_zoom = 1;
+	if (new_zoom > max_zoom)
+		new_zoom = max_zoom;
+
+	cx = view_left_x + view_width * xfrac * view_zoom;
+	cy = view_top_y + view_height * yfrac * view_zoom;
+
+	if (view_zoom != new_zoom) {
+		view_zoom = new_zoom;
+		pixel_slop = view_zoom;
+
+		view_left_x = cx - view_width * xfrac * view_zoom;
+		view_top_y = cy - view_height * yfrac * view_zoom;
+	}
+	lesstif_pan_fixup();
+}
+
+static void zoom_toggle(int x, int y)
+{
+	double tmp;
+
+	tmp = prev_view_zoom;
+	prev_view_zoom = view_zoom;
+	zoom_to(tmp, x, y);
+}
+
+void zoom_by(double factor, int x, int y)
+{
+	zoom_to(view_zoom * factor, x, y);
+}
+
+static int panning = 0;
+static int shift_pressed;
+static int ctrl_pressed;
+static int alt_pressed;
+
+/* X and Y are in screen coordinates.  */
+static void Pan(int mode, int x, int y)
+{
+	static int ox, oy;
+	static int opx, opy;
+
+	panning = mode;
+	/* This is for ctrl-pan, where the viewport's position is directly
+	   proportional to the cursor position in the window (like the Xaw
+	   thumb panner) */
+	if (pan_thumb_mode) {
+		opx = x * PCB->MaxWidth / view_width;
+		opy = y * PCB->MaxHeight / view_height;
+		if (conf_core.editor.view.flip_x)
+			opx = PCB->MaxWidth - opx;
+		if (conf_core.editor.view.flip_y)
+			opy = PCB->MaxHeight - opy;
+		view_left_x = opx - view_width / 2 * view_zoom;
+		view_top_y = opy - view_height / 2 * view_zoom;
+		lesstif_pan_fixup();
+	}
+	/* This is the start of a regular pan.  On the first click, we
+	   remember the coordinates where we "grabbed" the screen.  */
+	else if (mode == 1) {
+		ox = x;
+		oy = y;
+		opx = view_left_x;
+		opy = view_top_y;
+	}
+	/* continued drag, we calculate how far we've moved the cursor and
+	   set the position accordingly.  */
+	else {
+		if (conf_core.editor.view.flip_x)
+			view_left_x = opx + (x - ox) * view_zoom;
+		else
+			view_left_x = opx - (x - ox) * view_zoom;
+		if (conf_core.editor.view.flip_y)
+			view_top_y = opy + (y - oy) * view_zoom;
+		else
+			view_top_y = opy - (y - oy) * view_zoom;
+		lesstif_pan_fixup();
+	}
+}
+
+static void mod_changed(XKeyEvent * e, int set)
+{
+	switch (XKeycodeToKeysym(display, e->keycode, 0)) {
+	case XK_Shift_L:
+	case XK_Shift_R:
+		shift_pressed = set;
+		break;
+	case XK_Control_L:
+	case XK_Control_R:
+		ctrl_pressed = set;
+		break;
+#ifdef __APPLE__
+	case XK_Mode_switch:
+#else
+	case XK_Alt_L:
+	case XK_Alt_R:
+#endif
+		alt_pressed = set;
+		break;
+	default:
+		/* to include the Apple keyboard left and right command keys use XK_Meta_L and XK_Meta_R respectivly. */
+		return;
+	}
+	in_move_event = 1;
+	notify_crosshair_change(pcb_false);
+	if (panning)
+		Pan(2, e->x, e->y);
+	EventMoveCrosshair(Px(e->x), Py(e->y));
+	AdjustAttachedObjects();
+	notify_crosshair_change(pcb_true);
+	in_move_event = 0;
+}
+
+static hid_cfg_mod_t lesstif_mb2cfg(int but)
+{
+	switch(but) {
+		case 1: return MB_LEFT;
+		case 2: return MB_MIDDLE;
+		case 3: return MB_RIGHT;
+		case 4: return MB_SCROLL_UP;
+		case 5: return MB_SCROLL_DOWN;
+	}
+	return 0;
+}
+
+static void work_area_input(Widget w, XtPointer v, XEvent * e, Boolean * ctd)
+{
+	static int pressed_button = 0;
+
+	show_crosshair(0);
+	switch (e->type) {
+	case KeyPress:
+		mod_changed(&(e->xkey), 1);
+		if (lesstif_key_event(&(e->xkey)))
+			return;
+		break;
+
+	case KeyRelease:
+		mod_changed(&(e->xkey), 0);
+		break;
+
+	case ButtonPress:
+		{
+			int mods;
+			if (pressed_button)
+				return;
+			/*printf("click %d\n", e->xbutton.button); */
+			if (lesstif_button_event(w, e))
+				return;
+
+			notify_crosshair_change(pcb_false);
+			pressed_button = e->xbutton.button;
+			mods = ((e->xbutton.state & ShiftMask) ? M_Shift : 0)
+				+ ((e->xbutton.state & ControlMask) ? M_Ctrl : 0)
+#ifdef __APPLE__
+				+ ((e->xbutton.state & (1 << 13)) ? M_Alt : 0);
+#else
+				+ ((e->xbutton.state & Mod1Mask) ? M_Alt : 0);
+#endif
+			hid_cfg_mouse_action(&lesstif_mouse, lesstif_mb2cfg(e->xbutton.button) | mods);
+
+			notify_crosshair_change(pcb_true);
+			break;
+		}
+
+	case ButtonRelease:
+		{
+			int mods;
+			if (e->xbutton.button != pressed_button)
+				return;
+			lesstif_button_event(w, e);
+			notify_crosshair_change(pcb_false);
+			pressed_button = 0;
+			mods = ((e->xbutton.state & ShiftMask) ? M_Shift : 0)
+				+ ((e->xbutton.state & ControlMask) ? M_Ctrl : 0)
+#ifdef __APPLE__
+				+ ((e->xbutton.state & (1 << 13)) ? M_Alt : 0)
+#else
+				+ ((e->xbutton.state & Mod1Mask) ? M_Alt : 0)
+#endif
+				+ M_Release;
+			hid_cfg_mouse_action(&lesstif_mouse, lesstif_mb2cfg(e->xbutton.button) | mods);
+			notify_crosshair_change(pcb_true);
+			break;
+		}
+
+	case MotionNotify:
+		{
+			Window root, child;
+			unsigned int keys_buttons;
+			int root_x, root_y, pos_x, pos_y;
+			while (XCheckMaskEvent(display, PointerMotionMask, e));
+			XQueryPointer(display, e->xmotion.window, &root, &child, &root_x, &root_y, &pos_x, &pos_y, &keys_buttons);
+			shift_pressed = (keys_buttons & ShiftMask);
+			ctrl_pressed = (keys_buttons & ControlMask);
+#ifdef __APPLE__
+			alt_pressed = (keys_buttons & (1 << 13));
+#else
+			alt_pressed = (keys_buttons & Mod1Mask);
+#endif
+			/*pcb_printf("m %#mS %#mS\n", Px(e->xmotion.x), Py(e->xmotion.y)); */
+			crosshair_in_window = 1;
+			in_move_event = 1;
+			if (panning)
+				Pan(2, pos_x, pos_y);
+			EventMoveCrosshair(Px(pos_x), Py(pos_y));
+			in_move_event = 0;
+		}
+		break;
+
+	case LeaveNotify:
+		crosshair_in_window = 0;
+		ShowCrosshair(pcb_false);
+		need_idle_proc();
+		break;
+
+	case EnterNotify:
+		crosshair_in_window = 1;
+		in_move_event = 1;
+		EventMoveCrosshair(Px(e->xcrossing.x), Py(e->xcrossing.y));
+		ShowCrosshair(pcb_true);
+		in_move_event = 0;
+		need_idle_proc();
+		break;
+
+	default:
+		printf("work_area: unknown event %d\n", e->type);
+		break;
+	}
+}
+
+static void draw_right_cross(GC xor_gc, int x, int y, int view_width, int view_height)
+{
+	XDrawLine(display, window, xor_gc, 0, y, view_width, y);
+	XDrawLine(display, window, xor_gc, x, 0, x, view_height);
+}
+
+static void draw_slanted_cross(GC xor_gc, int x, int y, int view_width, int view_height)
+{
+	int x0, y0, x1, y1;
+
+	x0 = x + (view_height - y);
+	x0 = MAX(0, MIN(x0, view_width));
+	x1 = x - y;
+	x1 = MAX(0, MIN(x1, view_width));
+	y0 = y + (view_width - x);
+	y0 = MAX(0, MIN(y0, view_height));
+	y1 = y - x;
+	y1 = MAX(0, MIN(y1, view_height));
+	XDrawLine(display, window, xor_gc, x0, y0, x1, y1);
+	x0 = x - (view_height - y);
+	x0 = MAX(0, MIN(x0, view_width));
+	x1 = x + y;
+	x1 = MAX(0, MIN(x1, view_width));
+	y0 = y + x;
+	y0 = MAX(0, MIN(y0, view_height));
+	y1 = y - (view_width - x);
+	y1 = MAX(0, MIN(y1, view_height));
+	XDrawLine(display, window, xor_gc, x0, y0, x1, y1);
+}
+
+static void draw_dozen_cross(GC xor_gc, int x, int y, int view_width, int view_height)
+{
+	int x0, y0, x1, y1;
+	double tan60 = sqrt(3);
+
+	x0 = x + (view_height - y) / tan60;
+	x0 = MAX(0, MIN(x0, view_width));
+	x1 = x - y / tan60;
+	x1 = MAX(0, MIN(x1, view_width));
+	y0 = y + (view_width - x) * tan60;
+	y0 = MAX(0, MIN(y0, view_height));
+	y1 = y - x * tan60;
+	y1 = MAX(0, MIN(y1, view_height));
+	XDrawLine(display, window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x + (view_height - y) * tan60;
+	x0 = MAX(0, MIN(x0, view_width));
+	x1 = x - y * tan60;
+	x1 = MAX(0, MIN(x1, view_width));
+	y0 = y + (view_width - x) / tan60;
+	y0 = MAX(0, MIN(y0, view_height));
+	y1 = y - x / tan60;
+	y1 = MAX(0, MIN(y1, view_height));
+	XDrawLine(display, window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x - (view_height - y) / tan60;
+	x0 = MAX(0, MIN(x0, view_width));
+	x1 = x + y / tan60;
+	x1 = MAX(0, MIN(x1, view_width));
+	y0 = y + x * tan60;
+	y0 = MAX(0, MIN(y0, view_height));
+	y1 = y - (view_width - x) * tan60;
+	y1 = MAX(0, MIN(y1, view_height));
+	XDrawLine(display, window, xor_gc, x0, y0, x1, y1);
+
+	x0 = x - (view_height - y) * tan60;
+	x0 = MAX(0, MIN(x0, view_width));
+	x1 = x + y * tan60;
+	x1 = MAX(0, MIN(x1, view_width));
+	y0 = y + x / tan60;
+	y0 = MAX(0, MIN(y0, view_height));
+	y1 = y - (view_width - x) / tan60;
+	y1 = MAX(0, MIN(y1, view_height));
+	XDrawLine(display, window, xor_gc, x0, y0, x1, y1);
+}
+
+static void draw_crosshair(GC xor_gc, int x, int y, int view_width, int view_height)
+{
+	draw_right_cross(xor_gc, x, y, view_width, view_height);
+	if (Crosshair.shape == Union_Jack_Crosshair_Shape)
+		draw_slanted_cross(xor_gc, x, y, view_width, view_height);
+	if (Crosshair.shape == Dozen_Crosshair_Shape)
+		draw_dozen_cross(xor_gc, x, y, view_width, view_height);
+}
+
+void lesstif_show_crosshair(int show)
+{
+	static int showing = 0;
+	static int sx, sy;
+	static GC xor_gc = 0;
+	Pixel crosshair_color;
+
+	if (!crosshair_in_window || !window)
+		return;
+	if (xor_gc == 0) {
+		crosshair_color = lesstif_parse_color(conf_core.appearance.color.crosshair) ^ bgcolor;
+		xor_gc = XCreateGC(display, window, 0, 0);
+		XSetFunction(display, xor_gc, GXxor);
+		XSetForeground(display, xor_gc, crosshair_color);
+	}
+	if (show == showing)
+		return;
+	if (show) {
+		sx = Vx(crosshair_x);
+		sy = Vy(crosshair_y);
+	}
+	else
+		need_idle_proc();
+	draw_crosshair(xor_gc, sx, sy, view_width, view_height);
+	showing = show;
+}
+
+static void work_area_expose(Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs)
+{
+	XExposeEvent *e;
+
+	show_crosshair(0);
+	e = &(cbs->event->xexpose);
+	XSetFunction(display, my_gc, GXcopy);
+	XCopyArea(display, main_pixmap, window, my_gc, e->x, e->y, e->width, e->height, e->x, e->y);
+	show_crosshair(1);
+}
+
+static void scroll_callback(Widget scroll, int *view_dim, XmScrollBarCallbackStruct * cbs)
+{
+	*view_dim = cbs->value;
+	lesstif_invalidate_all();
+}
+
+static void work_area_make_pixmaps(Dimension width, Dimension height)
+{
+	if (main_pixmap)
+		XFreePixmap(display, main_pixmap);
+	main_pixmap = XCreatePixmap(display, window, width, height, XDefaultDepth(display, screen));
+
+	if (mask_pixmap)
+		XFreePixmap(display, mask_pixmap);
+	mask_pixmap = XCreatePixmap(display, window, width, height, XDefaultDepth(display, screen));
+#ifdef HAVE_XRENDER
+	if (main_picture) {
+		XRenderFreePicture(display, main_picture);
+		main_picture = 0;
+	}
+	if (mask_picture) {
+		XRenderFreePicture(display, mask_picture);
+		mask_picture = 0;
+	}
+	if (use_xrender) {
+		main_picture = XRenderCreatePicture(display, main_pixmap,
+																				XRenderFindVisualFormat(display, DefaultVisual(display, screen)), 0, 0);
+		mask_picture = XRenderCreatePicture(display, mask_pixmap,
+																				XRenderFindVisualFormat(display, DefaultVisual(display, screen)), 0, 0);
+		if (!main_picture || !mask_picture)
+			use_xrender = 0;
+	}
+#endif /* HAVE_XRENDER */
+
+	if (mask_bitmap)
+		XFreePixmap(display, mask_bitmap);
+	mask_bitmap = XCreatePixmap(display, window, width, height, 1);
+
+	pixmap = use_mask ? main_pixmap : mask_pixmap;
+	pixmap_w = width;
+	pixmap_h = height;
+}
+
+static void work_area_resize(Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs)
+{
+	XColor color;
+	Dimension width, height;
+
+	show_crosshair(0);
+
+	stdarg_n = 0;
+	stdarg(XtNwidth, &width);
+	stdarg(XtNheight, &height);
+	stdarg(XmNbackground, &bgcolor);
+	XtGetValues(work_area, stdarg_args, stdarg_n);
+	view_width = width;
+	view_height = height;
+
+	color.pixel = bgcolor;
+	XQueryColor(display, lesstif_colormap, &color);
+	bgred = color.red;
+	bggreen = color.green;
+	bgblue = color.blue;
+
+	if (!window)
+		return;
+
+	work_area_make_pixmaps(view_width, view_height);
+
+	zoom_by(1, 0, 0);
+}
+
+static void work_area_first_expose(Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs)
+{
+	int c;
+	Dimension width, height;
+	static char dashes[] = { 4, 4 };
+
+	window = XtWindow(work_area);
+	my_gc = XCreateGC(display, window, 0, 0);
+
+	arc1_gc = XCreateGC(display, window, 0, 0);
+	c = lesstif_parse_color("#804000");
+	XSetForeground(display, arc1_gc, c);
+	arc2_gc = XCreateGC(display, window, 0, 0);
+	c = lesstif_parse_color("#004080");
+	XSetForeground(display, arc2_gc, c);
+	XSetLineAttributes(display, arc1_gc, 1, LineOnOffDash, 0, 0);
+	XSetLineAttributes(display, arc2_gc, 1, LineOnOffDash, 0, 0);
+	XSetDashes(display, arc1_gc, 0, dashes, 2);
+	XSetDashes(display, arc2_gc, 0, dashes, 2);
+
+	stdarg_n = 0;
+	stdarg(XtNwidth, &width);
+	stdarg(XtNheight, &height);
+	stdarg(XmNbackground, &bgcolor);
+	XtGetValues(work_area, stdarg_args, stdarg_n);
+	view_width = width;
+	view_height = height;
+
+	offlimit_color = lesstif_parse_color(conf_core.appearance.color.off_limit);
+	grid_color = lesstif_parse_color(conf_core.appearance.color.grid);
+
+	bg_gc = XCreateGC(display, window, 0, 0);
+	XSetForeground(display, bg_gc, bgcolor);
+
+	work_area_make_pixmaps(width, height);
+
+#ifdef HAVE_XRENDER
+	if (use_xrender) {
+		XRenderPictureAttributes pa;
+		XRenderColor a = { 0, 0, 0, 0x8000 };
+
+		pale_pixmap = XCreatePixmap(display, window, 1, 1, 8);
+		pa.repeat = True;
+		pale_picture = XRenderCreatePicture(display, pale_pixmap,
+																				XRenderFindStandardFormat(display, PictStandardA8), CPRepeat, &pa);
+		if (pale_picture)
+			XRenderFillRectangle(display, PictOpSrc, pale_picture, &a, 0, 0, 1, 1);
+		else
+			use_xrender = 0;
+	}
+#endif /* HAVE_XRENDER */
+
+	clip_gc = XCreateGC(display, window, 0, 0);
+	bset_gc = XCreateGC(display, mask_bitmap, 0, 0);
+	XSetForeground(display, bset_gc, 1);
+	bclear_gc = XCreateGC(display, mask_bitmap, 0, 0);
+	XSetForeground(display, bclear_gc, 0);
+
+	XtRemoveCallback(work_area, XmNexposeCallback, (XtCallbackProc) work_area_first_expose, 0);
+	XtAddCallback(work_area, XmNexposeCallback, (XtCallbackProc) work_area_expose, 0);
+	lesstif_invalidate_all();
+}
+
+static Widget make_message(const char *name, Widget left, int resizeable)
+{
+	Widget w, f;
+	stdarg_n = 0;
+	if (left) {
+		stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+		stdarg(XmNleftWidget, XtParent(left));
+	}
+	else {
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+	}
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNshadowType, XmSHADOW_IN);
+	stdarg(XmNshadowThickness, 1);
+	stdarg(XmNalignment, XmALIGNMENT_CENTER);
+	stdarg(XmNmarginWidth, 4);
+	stdarg(XmNmarginHeight, 1);
+	if (!resizeable)
+		stdarg(XmNresizePolicy, XmRESIZE_GROW);
+	f = XmCreateForm(messages, XmStrCast(name), stdarg_args, stdarg_n);
+	XtManageChild(f);
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	w = XmCreateLabel(f, XmStrCast(name), stdarg_args, stdarg_n);
+	XtManageChild(w);
+	return w;
+}
+
+static unsigned short int lesstif_translate_key(const char *desc, int len)
+{
+	KeySym key;
+
+	if (strcasecmp(desc, "enter") == 0) desc = "Return";
+
+	key = XStringToKeysym(desc);
+	if (key == NoSymbol && len > 1) {
+		Message(PCB_MSG_DEFAULT, "no symbol for %s\n", desc);
+		return 0;
+	}
+	return key;
+}
+
+int lesstif_key_name(unsigned short int key_char, char *out, int out_len)
+{
+	char *name = XKeysymToString(key_char);
+	if (name == NULL)
+		return -1;
+	strncpy(out, name, out_len);
+	out[out_len-1] = '\0';
+	return 0;
+}
+
+
+extern Widget lesstif_menubar;
+static int lesstif_hid_inited = 0;
+
+static void lesstif_do_export(HID_Attr_Val * options)
+{
+	Dimension width, height;
+	Widget menu;
+	Widget work_area_frame;
+
+	lesstif_begin();
+
+	hid_cfg_keys_init(&lesstif_keymap);
+	lesstif_keymap.translate_key = lesstif_translate_key;
+	lesstif_keymap.key_name = lesstif_key_name;
+	lesstif_keymap.auto_chr = 1;
+	lesstif_keymap.auto_tr = hid_cfg_key_default_trans;
+
+	stdarg_n = 0;
+	stdarg(XtNwidth, &width);
+	stdarg(XtNheight, &height);
+	XtGetValues(appwidget, stdarg_args, stdarg_n);
+
+	if (width < 1)
+		width = 640;
+	if (width > XDisplayWidth(display, screen))
+		width = XDisplayWidth(display, screen);
+	if (height < 1)
+		height = 480;
+	if (height > XDisplayHeight(display, screen))
+		height = XDisplayHeight(display, screen);
+
+	stdarg_n = 0;
+	stdarg(XmNwidth, width);
+	stdarg(XmNheight, height);
+	XtSetValues(appwidget, stdarg_args, stdarg_n);
+
+	stdarg(XmNspacing, 0);
+	mainwind = XmCreateMainWindow(appwidget, XmStrCast("mainWind"), stdarg_args, stdarg_n);
+	XtManageChild(mainwind);
+
+	stdarg_n = 0;
+	stdarg(XmNmarginWidth, 0);
+	stdarg(XmNmarginHeight, 0);
+	menu = lesstif_menu(mainwind, "menubar", stdarg_args, stdarg_n);
+	XtManageChild(menu);
+
+	stdarg_n = 0;
+	stdarg(XmNshadowType, XmSHADOW_IN);
+	work_area_frame = XmCreateFrame(mainwind, XmStrCast("work_area_frame"), stdarg_args, stdarg_n);
+	XtManageChild(work_area_frame);
+
+	stdarg_n = 0;
+	stdarg_do_color(conf_core.appearance.color.background, XmNbackground);
+	work_area = XmCreateDrawingArea(work_area_frame, XmStrCast("work_area"), stdarg_args, stdarg_n);
+	XtManageChild(work_area);
+	XtAddCallback(work_area, XmNexposeCallback, (XtCallbackProc) work_area_first_expose, 0);
+	XtAddCallback(work_area, XmNresizeCallback, (XtCallbackProc) work_area_resize, 0);
+	/* A regular callback won't work here, because lesstif swallows any
+	   Ctrl<Button>1 event.  */
+	XtAddEventHandler(work_area,
+										ButtonPressMask | ButtonReleaseMask
+										| PointerMotionMask | PointerMotionHintMask
+										| KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask, 0, work_area_input, 0);
+
+	stdarg_n = 0;
+	stdarg(XmNorientation, XmVERTICAL);
+	stdarg(XmNprocessingDirection, XmMAX_ON_BOTTOM);
+	stdarg(XmNmaximum, PCB->MaxHeight ? PCB->MaxHeight : 1);
+	vscroll = XmCreateScrollBar(mainwind, XmStrCast("vscroll"), stdarg_args, stdarg_n);
+	XtAddCallback(vscroll, XmNvalueChangedCallback, (XtCallbackProc) scroll_callback, (XtPointer) & view_top_y);
+	XtAddCallback(vscroll, XmNdragCallback, (XtCallbackProc) scroll_callback, (XtPointer) & view_top_y);
+	XtManageChild(vscroll);
+
+	stdarg_n = 0;
+	stdarg(XmNorientation, XmHORIZONTAL);
+	stdarg(XmNmaximum, PCB->MaxWidth ? PCB->MaxWidth : 1);
+	hscroll = XmCreateScrollBar(mainwind, XmStrCast("hscroll"), stdarg_args, stdarg_n);
+	XtAddCallback(hscroll, XmNvalueChangedCallback, (XtCallbackProc) scroll_callback, (XtPointer) & view_left_x);
+	XtAddCallback(hscroll, XmNdragCallback, (XtCallbackProc) scroll_callback, (XtPointer) & view_left_x);
+	XtManageChild(hscroll);
+
+	stdarg_n = 0;
+	stdarg(XmNresize, True);
+	stdarg(XmNresizePolicy, XmRESIZE_ANY);
+	messages = XmCreateForm(mainwind, XmStrCast("messages"), stdarg_args, stdarg_n);
+	XtManageChild(messages);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNalignment, XmALIGNMENT_CENTER);
+	stdarg(XmNshadowThickness, 2);
+	m_click = XmCreateLabel(messages, XmStrCast("click"), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNlabelString, XmStringCreatePCB("Command: "));
+	m_cmd_label = XmCreateLabel(messages, XmStrCast("command"), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+	stdarg(XmNleftWidget, m_cmd_label);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNshadowThickness, 1);
+	stdarg(XmNhighlightThickness, 0);
+	stdarg(XmNmarginWidth, 2);
+	stdarg(XmNmarginHeight, 2);
+	m_cmd = XmCreateTextField(messages, XmStrCast("command"), stdarg_args, stdarg_n);
+	XtAddCallback(m_cmd, XmNactivateCallback, (XtCallbackProc) command_callback, 0);
+	XtAddCallback(m_cmd, XmNlosingFocusCallback, (XtCallbackProc) command_callback, 0);
+	XtAddEventHandler(m_cmd, KeyPressMask, 0, command_event_handler, 0);
+
+	m_mark = make_message("m_mark", 0, 0);
+	m_crosshair = make_message("m_crosshair", m_mark, 0);
+	m_grid = make_message("m_grid", m_crosshair, 1);
+	m_zoom = make_message("m_zoom", m_grid, 1);
+	lesstif_m_layer = make_message("m_layer", m_zoom, 0);
+	m_mode = make_message("m_mode", lesstif_m_layer, 1);
+	m_rats = make_message("m_rats", m_mode, 1);
+	m_status = make_message("m_status", m_mode, 1);
+
+	XtUnmanageChild(XtParent(m_mark));
+	XtUnmanageChild(XtParent(m_rats));
+
+	stdarg_n = 0;
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	XtSetValues(XtParent(m_status), stdarg_args, stdarg_n);
+
+	/* We'll use this later.  */
+	stdarg_n = 0;
+	stdarg(XmNleftWidget, XtParent(m_mark));
+	XtSetValues(XtParent(m_crosshair), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	stdarg(XmNmessageWindow, messages);
+	XtSetValues(mainwind, stdarg_args, stdarg_n);
+
+	if (background_image_file)
+		LoadBackgroundImage(background_image_file);
+
+	XtRealizeWidget(appwidget);
+
+	while (!window) {
+		XEvent e;
+		XtAppNextEvent(app_context, &e);
+		XtDispatchEvent(&e);
+	}
+
+	PCBChanged(0, 0, 0, 0);
+
+	lesstif_menubar = menu;
+	event(EVENT_GUI_INIT, NULL);
+
+	lesstif_hid_inited = 1;
+
+	XtAppMainLoop(app_context);
+
+	hid_cfg_keys_uninit(&lesstif_keymap);
+	lesstif_end();
+}
+
+static void lesstif_do_exit(HID *hid)
+{
+	XtAppSetExitFlag(app_context);
+}
+
+void lesstif_uninit_menu(void);
+
+static void lesstif_uninit(HID *hid)
+{
+	if (lesstif_hid_inited) {
+		lesstif_uninit_menu();
+		lesstif_hid_inited = 0;
+	}
+}
+
+typedef union {
+	int i;
+	double f;
+	char *s;
+	Coord c;
+} val_union;
+
+static Boolean
+pcb_cvt_string_to_double(Display * d, XrmValue * args, Cardinal * num_args, XrmValue * from, XrmValue * to, XtPointer * data)
+{
+	static double rv;
+	rv = strtod((char *) from->addr, 0);
+	if (to->addr)
+		*(double *) to->addr = rv;
+	else
+		to->addr = (XPointer) & rv;
+	to->size = sizeof(rv);
+	return True;
+}
+
+static Boolean
+pcb_cvt_string_to_coord(Display * d, XrmValue * args, Cardinal * num_args, XrmValue * from, XrmValue * to, XtPointer * data)
+{
+	static Coord rv;
+	rv = GetValue((char *) from->addr, NULL, NULL, NULL);
+	if (to->addr)
+		*(Coord *) to->addr = rv;
+	else
+		to->addr = (XPointer) & rv;
+	to->size = sizeof(rv);
+	return TRUE;
+}
+
+static void mainwind_delete_cb()
+{
+	hid_action("Quit");
+}
+
+static void lesstif_listener_cb(XtPointer client_data, int *fid, XtInputId * id)
+{
+	char buf[BUFSIZ];
+	int nbytes;
+
+	if ((nbytes = read(*fid, buf, BUFSIZ)) == -1)
+		perror("lesstif_listener_cb");
+
+	if (nbytes) {
+		buf[nbytes] = '\0';
+		hid_parse_actions(buf);
+	}
+}
+
+static void lesstif_parse_arguments(int *argc, char ***argv)
+{
+	Atom close_atom;
+	HID_AttrNode *ha;
+	int acount = 0, amax;
+	int rcount = 0, rmax;
+	int i;
+	XrmOptionDescRec *new_options;
+	XtResource *new_resources;
+	val_union *new_values;
+	int render_event, render_error;
+
+	XtSetTypeConverter(XtRString, XtRDouble, pcb_cvt_string_to_double, NULL, 0, XtCacheAll, NULL);
+	XtSetTypeConverter(XtRString, XtRPCBCoord, pcb_cvt_string_to_coord, NULL, 0, XtCacheAll, NULL);
+
+
+	for (ha = hid_attr_nodes; ha; ha = ha->next)
+		for (i = 0; i < ha->n; i++) {
+			HID_Attribute *a = ha->attributes + i;
+			switch (a->type) {
+			case HID_Integer:
+			case HID_Coord:
+			case HID_Real:
+			case HID_String:
+			case HID_Path:
+			case HID_Boolean:
+				acount++;
+				rcount++;
+				break;
+			default:
+				break;
+			}
+		}
+
+#if 0
+	amax = acount + XtNumber(lesstif_options);
+#else
+	amax = acount;
+#endif
+
+	new_options = (XrmOptionDescRec *) malloc((amax + 1) * sizeof(XrmOptionDescRec));
+
+#if 0
+	memcpy(new_options + acount, lesstif_options, sizeof(lesstif_options));
+#endif
+	acount = 0;
+
+#if 0
+	rmax = rcount + XtNumber(lesstif_resources);
+#else
+	rmax = rcount;
+#endif
+
+	new_resources = (XtResource *) malloc((rmax + 1) * sizeof(XtResource));
+	new_values = (val_union *) malloc((rmax + 1) * sizeof(val_union));
+#if 0
+	memcpy(new_resources + acount, lesstif_resources, sizeof(lesstif_resources));
+#endif
+	rcount = 0;
+
+	for (ha = hid_attr_nodes; ha; ha = ha->next)
+		for (i = 0; i < ha->n; i++) {
+			HID_Attribute *a = ha->attributes + i;
+			XrmOptionDescRec *o = new_options + acount;
+			char *tmpopt, *tmpres;
+			XtResource *r = new_resources + rcount;
+
+			tmpopt = (char *) malloc(strlen(a->name) + 3);
+			tmpopt[0] = tmpopt[1] = '-';
+			strcpy(tmpopt + 2, a->name);
+			o->option = tmpopt;
+
+			tmpres = (char *) malloc(strlen(a->name) + 2);
+			tmpres[0] = '*';
+			strcpy(tmpres + 1, a->name);
+			o->specifier = tmpres;
+
+			switch (a->type) {
+			case HID_Integer:
+			case HID_Coord:
+			case HID_Real:
+			case HID_String:
+			case HID_Path:
+				o->argKind = XrmoptionSepArg;
+				o->value = NULL;
+				acount++;
+				break;
+			case HID_Boolean:
+				o->argKind = XrmoptionNoArg;
+				o->value = XmStrCast("True");
+				acount++;
+				break;
+			default:
+				break;
+			}
+
+			r->resource_name = XmStrCast(a->name);
+			r->resource_class = XmStrCast(a->name);
+			r->resource_offset = sizeof(val_union) * rcount;
+
+			switch (a->type) {
+			case HID_Integer:
+				r->resource_type = XtRInt;
+				r->default_type = XtRInt;
+				r->resource_size = sizeof(int);
+				r->default_addr = &(a->default_val.int_value);
+				rcount++;
+				break;
+			case HID_Coord:
+				r->resource_type = XmStrCast(XtRPCBCoord);
+				r->default_type = XmStrCast(XtRPCBCoord);
+				r->resource_size = sizeof(Coord);
+				r->default_addr = &(a->default_val.coord_value);
+				rcount++;
+				break;
+			case HID_Real:
+				r->resource_type = XmStrCast(XtRDouble);
+				r->default_type = XmStrCast(XtRDouble);
+				r->resource_size = sizeof(double);
+				r->default_addr = &(a->default_val.real_value);
+				rcount++;
+				break;
+			case HID_String:
+			case HID_Path:
+				r->resource_type = XtRString;
+				r->default_type = XtRString;
+				r->resource_size = sizeof(char *);
+				r->default_addr = (char *) a->default_val.str_value;
+				rcount++;
+				break;
+			case HID_Boolean:
+				r->resource_type = XtRBoolean;
+				r->default_type = XtRInt;
+				r->resource_size = sizeof(int);
+				r->default_addr = &(a->default_val.int_value);
+				rcount++;
+				break;
+			default:
+				break;
+			}
+		}
+#if 0
+	for (i = 0; i < XtNumber(lesstif_resources); i++) {
+		XtResource *r = new_resources + rcount;
+		r->resource_offset = sizeof(val_union) * rcount;
+		rcount++;
+	}
+#endif
+
+	stdarg_n = 0;
+	stdarg(XmNdeleteResponse, XmDO_NOTHING);
+
+	appwidget = XtAppInitialize(&app_context, "Pcb", new_options, amax, argc, *argv, 0, stdarg_args, stdarg_n);
+
+	display = XtDisplay(appwidget);
+	screen_s = XtScreen(appwidget);
+	screen = XScreenNumberOfScreen(screen_s);
+	lesstif_colormap = XDefaultColormap(display, screen);
+
+	close_atom = XmInternAtom(display, XmStrCast("WM_DELETE_WINDOW"), 0);
+	XmAddWMProtocolCallback(appwidget, close_atom, (XtCallbackProc) mainwind_delete_cb, 0);
+
+	/*  XSynchronize(display, True); */
+
+	XtGetApplicationResources(appwidget, new_values, new_resources, rmax, 0, 0);
+
+#ifdef HAVE_XRENDER
+	use_xrender = XRenderQueryExtension(display, &render_event, &render_error) &&
+		XRenderFindVisualFormat(display, DefaultVisual(display, screen));
+#ifdef HAVE_XINERAMA
+	/* Xinerama and XRender don't get along well */
+	if (XineramaQueryExtension(display, &render_event, &render_error)
+			&& XineramaIsActive(display))
+		use_xrender = 0;
+#endif /* HAVE_XINERAMA */
+#endif /* HAVE_XRENDER */
+
+	rcount = 0;
+	for (ha = hid_attr_nodes; ha; ha = ha->next)
+		for (i = 0; i < ha->n; i++) {
+			HID_Attribute *a = ha->attributes + i;
+			val_union *v = new_values + rcount;
+			switch (a->type) {
+			case HID_Integer:
+				if (a->value)
+					*(int *) a->value = v->i;
+				else
+					a->default_val.int_value = v->i;
+				rcount++;
+				break;
+			case HID_Coord:
+				if (a->value)
+					*(Coord *) a->value = v->c;
+				else
+					a->default_val.coord_value = v->c;
+				rcount++;
+				break;
+			case HID_Boolean:
+				if (a->value)
+					*(char *) a->value = v->i;
+				else
+					a->default_val.int_value = v->i;
+				rcount++;
+				break;
+			case HID_Real:
+				if (a->value)
+					*(double *) a->value = v->f;
+				else
+					a->default_val.real_value = v->f;
+				rcount++;
+				break;
+			case HID_String:
+			case HID_Path:
+				if (a->value)
+					*(char **) a->value = v->s;
+				else
+					a->default_val.str_value = v->s;
+				rcount++;
+				break;
+			default:
+				break;
+			}
+		}
+
+	/* redefine lesstif_colormap, if requested via "-install" */
+	if (use_private_colormap) {
+		lesstif_colormap = XCopyColormapAndFree(display, lesstif_colormap);
+		XtVaSetValues(appwidget, XtNcolormap, lesstif_colormap, NULL);
+	}
+
+	/* listen on standard input for actions */
+	if (stdin_listen) {
+		XtAppAddInput(app_context, fileno(stdin), (XtPointer) XtInputReadMask, lesstif_listener_cb, NULL);
+	}
+}
+
+static void draw_grid()
+{
+	static XPoint *points = 0;
+	static int npoints = 0;
+	Coord x1, y1, x2, y2, prevx;
+	Coord x, y;
+	int n;
+	static GC grid_gc = 0;
+
+	if (!conf_core.editor.draw_grid)
+		return;
+	if (Vz(PCB->Grid) < MIN_GRID_DISTANCE)
+		return;
+	if (!grid_gc) {
+		grid_gc = XCreateGC(display, window, 0, 0);
+		XSetFunction(display, grid_gc, GXxor);
+		XSetForeground(display, grid_gc, grid_color);
+	}
+	if (conf_core.editor.view.flip_x) {
+		x2 = GridFit(Px(0), PCB->Grid, PCB->GridOffsetX);
+		x1 = GridFit(Px(view_width), PCB->Grid, PCB->GridOffsetX);
+		if (Vx(x2) < 0)
+			x2 -= PCB->Grid;
+		if (Vx(x1) >= view_width)
+			x1 += PCB->Grid;
+	}
+	else {
+		x1 = GridFit(Px(0), PCB->Grid, PCB->GridOffsetX);
+		x2 = GridFit(Px(view_width), PCB->Grid, PCB->GridOffsetX);
+		if (Vx(x1) < 0)
+			x1 += PCB->Grid;
+		if (Vx(x2) >= view_width)
+			x2 -= PCB->Grid;
+	}
+	if (conf_core.editor.view.flip_y) {
+		y2 = GridFit(Py(0), PCB->Grid, PCB->GridOffsetY);
+		y1 = GridFit(Py(view_height), PCB->Grid, PCB->GridOffsetY);
+		if (Vy(y2) < 0)
+			y2 -= PCB->Grid;
+		if (Vy(y1) >= view_height)
+			y1 += PCB->Grid;
+	}
+	else {
+		y1 = GridFit(Py(0), PCB->Grid, PCB->GridOffsetY);
+		y2 = GridFit(Py(view_height), PCB->Grid, PCB->GridOffsetY);
+		if (Vy(y1) < 0)
+			y1 += PCB->Grid;
+		if (Vy(y2) >= view_height)
+			y2 -= PCB->Grid;
+	}
+	n = (x2 - x1) / PCB->Grid + 1;
+	if (n > npoints) {
+		npoints = n + 10;
+		points = (XPoint *) realloc(points, npoints * sizeof(XPoint));
+	}
+	n = 0;
+	prevx = 0;
+	for (x = x1; x <= x2; x += PCB->Grid) {
+		int temp = Vx(x);
+		points[n].x = temp;
+		if (n) {
+			points[n].x -= prevx;
+			points[n].y = 0;
+		}
+		prevx = temp;
+		n++;
+	}
+	for (y = y1; y <= y2; y += PCB->Grid) {
+		int vy = Vy(y);
+		points[0].y = vy;
+		XDrawPoints(display, pixmap, grid_gc, points, n, CoordModePrevious);
+	}
+}
+
+static void mark_delta_to_widget(Coord dx, Coord dy, Widget w)
+{
+	char *buf;
+	double g = coord_to_unit(conf_core.editor.grid_unit, PCB->Grid);
+	int prec;
+	XmString ms;
+
+	/* Integer-sized grid? */
+	if (((int) (g * 10000 + 0.5) % 10000) == 0)
+		prec = 0;
+	else
+		prec = conf_core.editor.grid_unit->default_prec;
+
+	if (dx == 0 && dy == 0)
+		buf = pcb_strdup_printf("%m+%+.*mS, %+.*mS", UUNIT, prec, dx, prec, dy);
+	else {
+		Angle angle = atan2(dy, -dx) * 180 / M_PI;
+		Coord dist = Distance(0, 0, dx, dy);
+
+		buf = pcb_strdup_printf("%m+%+.*mS, %+.*mS (%.*mS, %.2f\260)", UUNIT, prec, dx, prec, dy, prec, dist, angle);
+	}
+
+	ms = XmStringCreatePCB(buf);
+	stdarg_n = 0;
+	stdarg(XmNlabelString, ms);
+	XtSetValues(w, stdarg_args, stdarg_n);
+	free(buf);
+}
+
+static int cursor_pos_to_widget(Coord x, Coord y, Widget w, int prev_state)
+{
+	int this_state = prev_state;
+	char *buf = NULL;
+	const char *msg = "";
+	double g = coord_to_unit(conf_core.editor.grid_unit, PCB->Grid);
+	XmString ms;
+	int prec;
+
+	/* Determine necessary precision (and state) based
+	 * on the user's grid setting */
+	if (((int) (g * 10000 + 0.5) % 10000) == 0) {
+		prec = 0;
+		this_state = conf_core.editor.grid_unit->allow;
+	}
+	else {
+		prec = conf_core.editor.grid_unit->default_prec;
+		this_state = -conf_core.editor.grid_unit->allow;
+	}
+
+	if (x >= 0) {
+		buf = pcb_strdup_printf("%m+%.*mS, %.*mS", UUNIT, prec, x, prec, y);
+		msg = buf;
+	}
+
+	ms = XmStringCreatePCB(msg);
+	stdarg_n = 0;
+	stdarg(XmNlabelString, ms);
+	XtSetValues(w, stdarg_args, stdarg_n);
+	free(buf);
+	return this_state;
+}
+
+void lesstif_update_status_line()
+{
+	const char *msg = "";
+	char *buf = NULL;
+	const char *s45 = cur_clip();
+	XmString xs;
+
+	switch (conf_core.editor.mode) {
+	case PCB_MODE_VIA:
+		buf = pcb_strdup_printf("%m+%.2mS/%.2mS \370=%.2mS", UUNIT, conf_core.design.via_thickness, conf_core.design.clearance, conf_core.design.via_drilling_hole);
+		break;
+	case PCB_MODE_LINE:
+	case PCB_MODE_ARC:
+		buf = pcb_strdup_printf("%m+%.2mS/%.2mS %s", UUNIT, conf_core.design.line_thickness, conf_core.design.clearance, s45);
+		break;
+	case PCB_MODE_RECTANGLE:
+	case PCB_MODE_POLYGON:
+		buf = pcb_strdup_printf("%m+%.2mS %s", UUNIT, conf_core.design.clearance, s45);
+		break;
+	case PCB_MODE_TEXT:
+		buf = pcb_strdup_printf("%d %%", conf_core.design.text_scale);
+		break;
+	case PCB_MODE_MOVE:
+	case PCB_MODE_COPY:
+	case PCB_MODE_INSERT_POINT:
+	case PCB_MODE_RUBBERBAND_MOVE:
+		if (s45 != NULL)
+			buf = pcb_strdup(s45);
+		break;
+	case PCB_MODE_NO:
+	case PCB_MODE_PASTE_BUFFER:
+	case PCB_MODE_ROTATE:
+	case PCB_MODE_REMOVE:
+	case PCB_MODE_THERMAL:
+	case PCB_MODE_ARROW:
+	case PCB_MODE_LOCK:
+	default:
+		break;
+	}
+
+	if (buf != NULL) {
+		msg = buf;
+	}
+	xs = XmStringCreatePCB(msg);
+	stdarg_n = 0;
+	stdarg(XmNlabelString, xs);
+	XtSetValues(m_status, stdarg_args, stdarg_n);
+	free(buf);
+}
+
+static int idle_proc_set = 0;
+static int need_redraw = 0;
+
+static Boolean idle_proc(XtPointer dummy)
+{
+	if (need_redraw) {
+		int mx, my;
+		BoxType region;
+		lesstif_use_mask(0);
+		pixmap = main_pixmap;
+		mx = view_width;
+		my = view_height;
+		region.X1 = Px(0);
+		region.Y1 = Py(0);
+		region.X2 = Px(view_width);
+		region.Y2 = Py(view_height);
+		if (conf_core.editor.view.flip_x) {
+			Coord tmp = region.X1;
+			region.X1 = region.X2;
+			region.X2 = tmp;
+		}
+		if (conf_core.editor.view.flip_y) {
+			Coord tmp = region.Y1;
+			region.Y1 = region.Y2;
+			region.Y2 = tmp;
+		}
+		XSetForeground(display, bg_gc, bgcolor);
+		XFillRectangle(display, main_pixmap, bg_gc, 0, 0, mx, my);
+
+		if (region.X1 < 0 || region.Y1 < 0 || region.X2 > PCB->MaxWidth || region.Y2 > PCB->MaxHeight) {
+			int leftmost, rightmost, topmost, bottommost;
+
+			leftmost = Vx(0);
+			rightmost = Vx(PCB->MaxWidth);
+			topmost = Vy(0);
+			bottommost = Vy(PCB->MaxHeight);
+			if (leftmost > rightmost) {
+				int t = leftmost;
+				leftmost = rightmost;
+				rightmost = t;
+			}
+			if (topmost > bottommost) {
+				int t = topmost;
+				topmost = bottommost;
+				bottommost = t;
+			}
+			if (leftmost < 0)
+				leftmost = 0;
+			if (topmost < 0)
+				topmost = 0;
+			if (rightmost > view_width)
+				rightmost = view_width;
+			if (bottommost > view_height)
+				bottommost = view_height;
+
+			XSetForeground(display, bg_gc, offlimit_color);
+
+			/* L T R
+			   L x R
+			   L B R */
+
+			if (leftmost > 0) {
+				XFillRectangle(display, main_pixmap, bg_gc, 0, 0, leftmost, view_height);
+			}
+			if (rightmost < view_width) {
+				XFillRectangle(display, main_pixmap, bg_gc, rightmost + 1, 0, view_width - rightmost + 1, view_height);
+			}
+			if (topmost > 0) {
+				XFillRectangle(display, main_pixmap, bg_gc, leftmost, 0, rightmost - leftmost + 1, topmost);
+			}
+			if (bottommost < view_height) {
+				XFillRectangle(display, main_pixmap, bg_gc, leftmost, bottommost + 1,
+											 rightmost - leftmost + 1, view_height - bottommost + 1);
+			}
+		}
+		DrawBackgroundImage();
+		hid_expose_callback(&lesstif_hid, &region, 0);
+		draw_grid();
+		lesstif_use_mask(0);
+		show_crosshair(0);					/* To keep the drawn / not drawn info correct */
+		XSetFunction(display, my_gc, GXcopy);
+		XCopyArea(display, main_pixmap, window, my_gc, 0, 0, view_width, view_height, 0, 0);
+		pixmap = window;
+		if (crosshair_on) {
+			DrawAttached();
+			DrawMark();
+		}
+		need_redraw = 0;
+	}
+
+	{
+		static int c_x = -2, c_y = -2;
+		static MarkType saved_mark;
+		static const Unit *old_grid_unit = NULL;
+		if (crosshair_x != c_x || crosshair_y != c_y
+				|| conf_core.editor.grid_unit != old_grid_unit || memcmp(&saved_mark, &Marked, sizeof(MarkType))) {
+			static int last_state = 0;
+			static int this_state = 0;
+
+			c_x = crosshair_x;
+			c_y = crosshair_y;
+
+			this_state = cursor_pos_to_widget(crosshair_x, crosshair_y, m_crosshair, this_state);
+			if (Marked.status)
+				mark_delta_to_widget(crosshair_x - Marked.X, crosshair_y - Marked.Y, m_mark);
+
+			if (Marked.status != saved_mark.status) {
+				if (Marked.status) {
+					XtManageChild(XtParent(m_mark));
+					XtManageChild(m_mark);
+					stdarg_n = 0;
+					stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+					stdarg(XmNleftWidget, XtParent(m_mark));
+					XtSetValues(XtParent(m_crosshair), stdarg_args, stdarg_n);
+				}
+				else {
+					stdarg_n = 0;
+					stdarg(XmNleftAttachment, XmATTACH_FORM);
+					XtSetValues(XtParent(m_crosshair), stdarg_args, stdarg_n);
+					XtUnmanageChild(XtParent(m_mark));
+				}
+				last_state = this_state + 100;
+			}
+			memcpy(&saved_mark, &Marked, sizeof(MarkType));
+
+			if (old_grid_unit != conf_core.editor.grid_unit) {
+				old_grid_unit = conf_core.editor.grid_unit;
+				/* Force a resize on units change.  */
+				last_state++;
+			}
+
+			/* This is obtuse.  We want to enable XmRESIZE_ANY long enough
+			   to shrink to fit the new format (if any), then switch it
+			   back to XmRESIZE_GROW to prevent it from shrinking due to
+			   changes in the number of actual digits printed.  Thus, when
+			   you switch from a small grid and %.2f formats to a large
+			   grid and %d formats, you aren't punished with a wide and
+			   mostly white-space label widget.  "this_state" indicates
+			   which of the above formats we're using.  "last_state" is
+			   either zero (when resizing) or the same as "this_state"
+			   (when grow-only), or a non-zero but not "this_state" which
+			   means we need to start a resize cycle.  */
+			if (this_state != last_state && last_state) {
+				stdarg_n = 0;
+				stdarg(XmNresizePolicy, XmRESIZE_ANY);
+				XtSetValues(XtParent(m_mark), stdarg_args, stdarg_n);
+				XtSetValues(XtParent(m_crosshair), stdarg_args, stdarg_n);
+				last_state = 0;
+			}
+			else if (this_state != last_state) {
+				stdarg_n = 0;
+				stdarg(XmNresizePolicy, XmRESIZE_GROW);
+				XtSetValues(XtParent(m_mark), stdarg_args, stdarg_n);
+				XtSetValues(XtParent(m_crosshair), stdarg_args, stdarg_n);
+				last_state = this_state;
+			}
+		}
+	}
+
+	{
+		static Coord old_grid = -1;
+		static Coord old_gx, old_gy;
+		static const Unit *old_unit;
+		XmString ms;
+		if (PCB->Grid != old_grid || PCB->GridOffsetX != old_gx || PCB->GridOffsetY != old_gy || conf_core.editor.grid_unit != old_unit) {
+			static char buf[100];
+			old_grid = PCB->Grid;
+			old_unit = conf_core.editor.grid_unit;
+			old_gx = PCB->GridOffsetX;
+			old_gy = PCB->GridOffsetY;
+			if (old_grid == 1) {
+				strcpy(buf, "No Grid");
+			}
+			else {
+				if (old_gx || old_gy)
+					pcb_snprintf(buf, sizeof(buf), "%m+%$mS @%mS,%mS", UUNIT, old_grid, old_gx, old_gy);
+				else
+					pcb_snprintf(buf, sizeof(buf), "%m+%$mS", UUNIT, old_grid);
+			}
+			ms = XmStringCreatePCB(buf);
+			stdarg_n = 0;
+			stdarg(XmNlabelString, ms);
+			XtSetValues(m_grid, stdarg_args, stdarg_n);
+		}
+	}
+
+	{
+		static double old_zoom = -1;
+		static const Unit *old_grid_unit = NULL;
+		if (view_zoom != old_zoom || conf_core.editor.grid_unit != old_grid_unit) {
+			char *buf = pcb_strdup_printf("%m+%$mS/pix",
+																			 conf_core.editor.grid_unit->allow, (Coord) view_zoom);
+			XmString ms;
+
+			old_zoom = view_zoom;
+			old_grid_unit = conf_core.editor.grid_unit;
+
+			ms = XmStringCreatePCB(buf);
+			stdarg_n = 0;
+			stdarg(XmNlabelString, ms);
+			XtSetValues(m_zoom, stdarg_args, stdarg_n);
+			free(buf);
+		}
+	}
+
+	{
+		if (old_cursor_mode != conf_core.editor.mode) {
+			const char *s = "None";
+			XmString ms;
+			int cursor = -1;
+			static int free_cursor = 0;
+
+			old_cursor_mode = conf_core.editor.mode;
+			switch (conf_core.editor.mode) {
+			case PCB_MODE_NO:
+				s = "None";
+				cursor = XC_X_cursor;
+				break;
+			case PCB_MODE_VIA:
+				s = "Via";
+				cursor = -1;
+				break;
+			case PCB_MODE_LINE:
+				s = "Line";
+				cursor = XC_pencil;
+				break;
+			case PCB_MODE_RECTANGLE:
+				s = "Rectangle";
+				cursor = XC_ul_angle;
+				break;
+			case PCB_MODE_POLYGON:
+				s = "Polygon";
+				cursor = XC_sb_up_arrow;
+				break;
+			case PCB_MODE_POLYGON_HOLE:
+				s = "Polygon Hole";
+				cursor = XC_sb_up_arrow;
+				break;
+			case PCB_MODE_PASTE_BUFFER:
+				s = "Paste";
+				cursor = XC_hand1;
+				break;
+			case PCB_MODE_TEXT:
+				s = "Text";
+				cursor = XC_xterm;
+				break;
+			case PCB_MODE_ROTATE:
+				s = "Rotate";
+				cursor = XC_exchange;
+				break;
+			case PCB_MODE_REMOVE:
+				s = "Remove";
+				cursor = XC_pirate;
+				break;
+			case PCB_MODE_MOVE:
+				s = "Move";
+				cursor = XC_crosshair;
+				break;
+			case PCB_MODE_COPY:
+				s = "Copy";
+				cursor = XC_crosshair;
+				break;
+			case PCB_MODE_INSERT_POINT:
+				s = "Insert";
+				cursor = XC_dotbox;
+				break;
+			case PCB_MODE_RUBBERBAND_MOVE:
+				s = "RBMove";
+				cursor = XC_top_left_corner;
+				break;
+			case PCB_MODE_THERMAL:
+				s = "Thermal";
+				cursor = XC_iron_cross;
+				break;
+			case PCB_MODE_ARC:
+				s = "Arc";
+				cursor = XC_question_arrow;
+				break;
+			case PCB_MODE_ARROW:
+				s = "Arrow";
+				if (over_point)
+					cursor = XC_draped_box;
+				else
+					cursor = XC_left_ptr;
+				break;
+			case PCB_MODE_LOCK:
+				s = "Lock";
+				cursor = XC_hand2;
+				break;
+			}
+			ms = XmStringCreatePCB(s);
+			stdarg_n = 0;
+			stdarg(XmNlabelString, ms);
+			XtSetValues(m_mode, stdarg_args, stdarg_n);
+
+			if (free_cursor) {
+				XFreeCursor(display, my_cursor);
+				free_cursor = 0;
+			}
+			if (cursor == -1) {
+				static Pixmap nocur_source = 0;
+				static Pixmap nocur_mask = 0;
+				static Cursor nocursor = 0;
+				if (nocur_source == 0) {
+					XColor fg, bg;
+					nocur_source = XCreateBitmapFromData(display, window, "\0", 1, 1);
+					nocur_mask = XCreateBitmapFromData(display, window, "\0", 1, 1);
+
+					fg.red = fg.green = fg.blue = 65535;
+					bg.red = bg.green = bg.blue = 0;
+					fg.flags = bg.flags = DoRed | DoGreen | DoBlue;
+					nocursor = XCreatePixmapCursor(display, nocur_source, nocur_mask, &fg, &bg, 0, 0);
+				}
+				my_cursor = nocursor;
+			}
+			else {
+				my_cursor = XCreateFontCursor(display, cursor);
+				free_cursor = 1;
+			}
+			XDefineCursor(display, window, my_cursor);
+			lesstif_update_status_line();
+		}
+	}
+	{
+		static const char *old_clip = NULL;
+		static int old_tscale = -1;
+		const char *new_clip = cur_clip();
+
+		if (new_clip != old_clip || conf_core.design.text_scale != old_tscale) {
+			lesstif_update_status_line();
+			old_clip = new_clip;
+			old_tscale = conf_core.design.text_scale;
+		}
+	}
+
+	{
+		static int old_nrats = -1;
+		static char buf[20];
+
+		if (old_nrats != ratlist_length(&PCB->Data->Rat)) {
+			old_nrats = ratlist_length(&PCB->Data->Rat);
+			sprintf(buf, "%ld rat%s", (long)ratlist_length(&PCB->Data->Rat), ratlist_length(&PCB->Data->Rat) == 1 ? "" : "s");
+			if (ratlist_length(&PCB->Data->Rat)) {
+				XtManageChild(XtParent(m_rats));
+				XtManageChild(m_rats);
+				stdarg_n = 0;
+				stdarg(XmNleftWidget, m_rats);
+				XtSetValues(XtParent(m_status), stdarg_args, stdarg_n);
+			}
+
+			stdarg_n = 0;
+			stdarg(XmNlabelString, XmStringCreatePCB(buf));
+			XtSetValues(m_rats, stdarg_args, stdarg_n);
+
+			if (!ratlist_length(&PCB->Data->Rat)) {
+				stdarg_n = 0;
+				stdarg(XmNleftWidget, m_mode);
+				XtSetValues(XtParent(m_status), stdarg_args, stdarg_n);
+				XtUnmanageChild(XtParent(m_rats));
+			}
+		}
+	}
+
+	lesstif_update_widget_flags();
+
+	show_crosshair(1);
+	idle_proc_set = 0;
+	return True;
+}
+
+void lesstif_need_idle_proc()
+{
+	if (idle_proc_set || window == 0)
+		return;
+	XtAppAddWorkProc(app_context, idle_proc, 0);
+	idle_proc_set = 1;
+}
+
+static void lesstif_invalidate_lr(int l, int r, int t, int b)
+{
+	if (!window)
+		return;
+
+	need_redraw = 1;
+	need_idle_proc();
+}
+
+void lesstif_invalidate_all(void)
+{
+	lesstif_invalidate_lr(0, PCB->MaxWidth, 0, PCB->MaxHeight);
+}
+
+static void lesstif_notify_crosshair_change(pcb_bool changes_complete)
+{
+	static int invalidate_depth = 0;
+	Pixmap save_pixmap;
+
+	if (!my_gc)
+		return;
+
+	if (changes_complete)
+		invalidate_depth--;
+
+	if (invalidate_depth < 0) {
+		invalidate_depth = 0;
+		/* A mismatch of changes_complete == pcb_false and == pcb_true notifications
+		 * is not expected to occur, but we will try to handle it gracefully.
+		 * As we know the crosshair will have been shown already, we must
+		 * repaint the entire view to be sure not to leave an artaefact.
+		 */
+		need_idle_proc();
+		return;
+	}
+
+	if (invalidate_depth == 0 && crosshair_on) {
+		save_pixmap = pixmap;
+		pixmap = window;
+		DrawAttached();
+		pixmap = save_pixmap;
+	}
+
+	if (!changes_complete)
+		invalidate_depth++;
+}
+
+static void lesstif_notify_mark_change(pcb_bool changes_complete)
+{
+	static int invalidate_depth = 0;
+	Pixmap save_pixmap;
+
+	if (changes_complete)
+		invalidate_depth--;
+
+	if (invalidate_depth < 0) {
+		invalidate_depth = 0;
+		/* A mismatch of changes_complete == pcb_false and == pcb_true notifications
+		 * is not expected to occur, but we will try to handle it gracefully.
+		 * As we know the mark will have been shown already, we must
+		 * repaint the entire view to be sure not to leave an artaefact.
+		 */
+		need_idle_proc();
+		return;
+	}
+
+	if (invalidate_depth == 0 && crosshair_on) {
+		save_pixmap = pixmap;
+		pixmap = window;
+		DrawMark();
+		pixmap = save_pixmap;
+	}
+
+	if (!changes_complete)
+		invalidate_depth++;
+}
+
+static int lesstif_set_layer(const char *name, int group, int empty)
+{
+	int idx = group;
+	if (idx >= 0 && idx < max_group) {
+		int n = PCB->LayerGroups.Number[group];
+		for (idx = 0; idx < n - 1; idx++) {
+			int ni = PCB->LayerGroups.Entries[group][idx];
+			if (ni >= 0 && ni < max_copper_layer + 2 && PCB->Data->Layer[ni].On)
+				break;
+		}
+		idx = PCB->LayerGroups.Entries[group][idx];
+#if 0
+		if (idx == LayerStack[0]
+				|| GetLayerGroupNumberByNumber(idx) == GetLayerGroupNumberByNumber(LayerStack[0]))
+			autofade = 0;
+		else
+			autofade = 1;
+#endif
+	}
+#if 0
+	else
+		autofade = 0;
+#endif
+	if (idx >= 0 && idx < max_copper_layer + 2)
+		return pinout ? 1 : PCB->Data->Layer[idx].On;
+	if (idx < 0) {
+		switch (SL_TYPE(idx)) {
+		case SL_INVISIBLE:
+			return pinout ? 0 : PCB->InvisibleObjectsOn;
+		case SL_MASK:
+			if (SL_MYSIDE(idx) && !pinout)
+				return conf_core.editor.show_mask;
+			return 0;
+		case SL_SILK:
+			if (SL_MYSIDE(idx) || pinout)
+				return PCB->ElementOn;
+			return 0;
+		case SL_ASSY:
+			return 0;
+		case SL_UDRILL:
+		case SL_PDRILL:
+			return 1;
+		case SL_RATS:
+			return PCB->RatOn;
+		}
+	}
+	return 0;
+}
+
+static hidGC lesstif_make_gc(void)
+{
+	hidGC rv = (hid_gc_struct *) malloc(sizeof(hid_gc_struct));
+	memset(rv, 0, sizeof(hid_gc_struct));
+	rv->me_pointer = &lesstif_hid;
+	rv->colorname = NULL;
+	return rv;
+}
+
+static void lesstif_destroy_gc(hidGC gc)
+{
+	if (gc->colorname != NULL)
+		free(gc->colorname);
+	free(gc);
+}
+
+static void lesstif_use_mask(int use_it)
+{
+	if ((conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly) && !use_xrender)
+		use_it = 0;
+	if ((use_it == 0) == (use_mask == 0))
+		return;
+	use_mask = use_it;
+	if (pinout)
+		return;
+	if (!window)
+		return;
+	/*  printf("use_mask(%d)\n", use_it); */
+	if (!mask_pixmap) {
+		mask_pixmap = XCreatePixmap(display, window, pixmap_w, pixmap_h, XDefaultDepth(display, screen));
+		mask_bitmap = XCreatePixmap(display, window, pixmap_w, pixmap_h, 1);
+	}
+	if (use_it) {
+		pixmap = mask_pixmap;
+		XSetForeground(display, my_gc, 0);
+		XSetFunction(display, my_gc, GXcopy);
+		XFillRectangle(display, mask_pixmap, my_gc, 0, 0, view_width, view_height);
+		XFillRectangle(display, mask_bitmap, bclear_gc, 0, 0, view_width, view_height);
+	}
+	else {
+		pixmap = main_pixmap;
+#ifdef HAVE_XRENDER
+		if (use_xrender) {
+			XRenderPictureAttributes pa;
+
+			pa.clip_mask = mask_bitmap;
+			XRenderChangePicture(display, main_picture, CPClipMask, &pa);
+			XRenderComposite(display, PictOpOver, mask_picture, pale_picture,
+											 main_picture, 0, 0, 0, 0, 0, 0, view_width, view_height);
+		}
+		else
+#endif /* HAVE_XRENDER */
+		{
+			XSetClipMask(display, clip_gc, mask_bitmap);
+			XCopyArea(display, mask_pixmap, main_pixmap, clip_gc, 0, 0, view_width, view_height, 0, 0);
+		}
+	}
+}
+
+static void lesstif_set_color(hidGC gc, const char *name)
+{
+	static void *cache = 0;
+	hidval cval;
+	static XColor color, exact_color;
+
+	if (!display)
+		return;
+	if (!name)
+		name = "red";
+
+	if (name != gc->colorname) {
+		if (gc->colorname != NULL)
+			free(gc->colorname);
+		gc->colorname = pcb_strdup(name);
+	}
+
+	if (strcmp(name, "erase") == 0) {
+		gc->color = bgcolor;
+		gc->erase = 1;
+	}
+	else if (strcmp(name, "drill") == 0) {
+		gc->color = offlimit_color;
+		gc->erase = 0;
+	}
+	else if (hid_cache_color(0, name, &cval, &cache)) {
+		gc->color = cval.lval;
+		gc->erase = 0;
+	}
+	else {
+		if (!XAllocNamedColor(display, lesstif_colormap, name, &color, &exact_color))
+			color.pixel = WhitePixel(display, screen);
+#if 0
+		printf("lesstif_set_color `%s' %08x rgb/%d/%d/%d\n", name, color.pixel, color.red, color.green, color.blue);
+#endif
+		cval.lval = gc->color = color.pixel;
+		hid_cache_color(1, name, &cval, &cache);
+		gc->erase = 0;
+	}
+	if (autofade) {
+		static int lastcolor = -1, lastfade = -1;
+		if (gc->color == lastcolor)
+			gc->color = lastfade;
+		else {
+			lastcolor = gc->color;
+			color.pixel = gc->color;
+
+			XQueryColor(display, lesstif_colormap, &color);
+			color.red = (bgred + color.red) / 2;
+			color.green = (bggreen + color.green) / 2;
+			color.blue = (bgblue + color.blue) / 2;
+			XAllocColor(display, lesstif_colormap, &color);
+			lastfade = gc->color = color.pixel;
+		}
+	}
+}
+
+static void set_gc(hidGC gc)
+{
+	int cap, join, width;
+	if (gc->me_pointer != &lesstif_hid) {
+		fprintf(stderr, "Fatal: GC from another HID passed to lesstif HID\n");
+		abort();
+	}
+#if 0
+	pcb_printf("set_gc c%s %08lx w%#mS c%d x%d e%d\n", gc->colorname, gc->color, gc->width, gc->cap, gc->xor_set, gc->erase);
+#endif
+	switch (gc->cap) {
+	case Square_Cap:
+		cap = CapProjecting;
+		join = JoinMiter;
+		break;
+	case Trace_Cap:
+	case Round_Cap:
+		cap = CapRound;
+		join = JoinRound;
+		break;
+	case Beveled_Cap:
+		cap = CapProjecting;
+		join = JoinBevel;
+		break;
+	default:
+		cap = CapProjecting;
+		join = JoinBevel;
+		break;
+	}
+	if (gc->xor_set) {
+		XSetFunction(display, my_gc, GXxor);
+		XSetForeground(display, my_gc, gc->color ^ bgcolor);
+	}
+	else if (gc->erase) {
+		XSetFunction(display, my_gc, GXcopy);
+		XSetForeground(display, my_gc, offlimit_color);
+	}
+	else {
+		XSetFunction(display, my_gc, GXcopy);
+		XSetForeground(display, my_gc, gc->color);
+	}
+	width = Vz(gc->width);
+	if (width < 0)
+		width = 0;
+	XSetLineAttributes(display, my_gc, width, LineSolid, cap, join);
+	if (use_mask) {
+		if (gc->erase)
+			mask_gc = bclear_gc;
+		else
+			mask_gc = bset_gc;
+		XSetLineAttributes(display, mask_gc, Vz(gc->width), LineSolid, cap, join);
+	}
+}
+
+static void lesstif_set_line_cap(hidGC gc, EndCapStyle style)
+{
+	gc->cap = style;
+}
+
+static void lesstif_set_line_width(hidGC gc, Coord width)
+{
+	gc->width = width;
+}
+
+static void lesstif_set_draw_xor(hidGC gc, int xor_set)
+{
+	gc->xor_set = xor_set;
+}
+
+#define ISORT(a,b) if (a>b) { a^=b; b^=a; a^=b; }
+
+static void lesstif_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	double dx1, dy1, dx2, dy2;
+	int vw = Vz(gc->width);
+	if ((pinout || conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly) && gc->erase)
+		return;
+#if 0
+	pcb_printf("draw_line %#mD-%#mD @%#mS", x1, y1, x2, y2, gc->width);
+#endif
+	dx1 = Vx(x1);
+	dy1 = Vy(y1);
+	dx2 = Vx(x2);
+	dy2 = Vy(y2);
+#if 0
+	pcb_printf(" = %#mD-%#mD %s\n", x1, y1, x2, y2, gc->colorname);
+#endif
+
+#if 1
+	if (!ClipLine(0, 0, view_width, view_height, &dx1, &dy1, &dx2, &dy2, vw))
+		return;
+#endif
+
+	x1 = dx1;
+	y1 = dy1;
+	x2 = dx2;
+	y2 = dy2;
+
+	set_gc(gc);
+	if (gc->cap == Square_Cap && x1 == x2 && y1 == y2) {
+		XFillRectangle(display, pixmap, my_gc, x1 - vw / 2, y1 - vw / 2, vw, vw);
+		if (use_mask)
+			XFillRectangle(display, mask_bitmap, mask_gc, x1 - vw / 2, y1 - vw / 2, vw, vw);
+	}
+	else {
+		XDrawLine(display, pixmap, my_gc, x1, y1, x2, y2);
+		if (use_mask)
+			XDrawLine(display, mask_bitmap, mask_gc, x1, y1, x2, y2);
+	}
+}
+
+static void lesstif_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle)
+{
+	if ((pinout || conf_core.editor.thin_draw) && gc->erase)
+		return;
+#if 0
+	pcb_printf("draw_arc %#mD %#mSx%#mS s %d d %d", cx, cy, width, height, start_angle, delta_angle);
+#endif
+	width = Vz(width);
+	height = Vz(height);
+	cx = Vx(cx) - width;
+	cy = Vy(cy) - height;
+	if (conf_core.editor.view.flip_x) {
+		start_angle = 180 - start_angle;
+		delta_angle = -delta_angle;
+	}
+	if (conf_core.editor.view.flip_y) {
+		start_angle = -start_angle;
+		delta_angle = -delta_angle;
+	}
+	start_angle = NormalizeAngle(start_angle);
+	if (start_angle >= 180)
+		start_angle -= 360;
+#if 0
+	pcb_printf(" = %#mD %#mSx%#mS %d %s\n", cx, cy, width, height, gc->width, gc->colorname);
+#endif
+	set_gc(gc);
+	XDrawArc(display, pixmap, my_gc, cx, cy, width * 2, height * 2, (start_angle + 180) * 64, delta_angle * 64);
+	if (use_mask && !conf_core.editor.thin_draw)
+		XDrawArc(display, mask_bitmap, mask_gc, cx, cy, width * 2, height * 2, (start_angle + 180) * 64, delta_angle * 64);
+#warning TODO: make this #if a flag and add it in the gtk hid as well
+#if 0
+	/* Enable this if you want to see the center and radii of drawn
+	   arcs, for debugging.  */
+	if (conf_core.editor.thin_draw && (delta_angle != 360)) {
+		cx += width;
+		cy += height;
+		XDrawLine(display, pixmap, arc1_gc, cx, cy,
+							cx - width * cos(start_angle * M_PI / 180), cy + width * sin(start_angle * M_PI / 180));
+		XDrawLine(display, pixmap, arc2_gc, cx, cy,
+							cx - width * cos((start_angle + delta_angle) * M_PI / 180),
+							cy + width * sin((start_angle + delta_angle) * M_PI / 180));
+	}
+#endif
+}
+
+static void lesstif_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	int vw = Vz(gc->width);
+	if ((pinout || conf_core.editor.thin_draw) && gc->erase)
+		return;
+	x1 = Vx(x1);
+	y1 = Vy(y1);
+	x2 = Vx(x2);
+	y2 = Vy(y2);
+	if (x1 < -vw && x2 < -vw)
+		return;
+	if (y1 < -vw && y2 < -vw)
+		return;
+	if (x1 > view_width + vw && x2 > view_width + vw)
+		return;
+	if (y1 > view_height + vw && y2 > view_height + vw)
+		return;
+	if (x1 > x2) {
+		int xt = x1;
+		x1 = x2;
+		x2 = xt;
+	}
+	if (y1 > y2) {
+		int yt = y1;
+		y1 = y2;
+		y2 = yt;
+	}
+	set_gc(gc);
+	XDrawRectangle(display, pixmap, my_gc, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+	if (use_mask)
+		XDrawRectangle(display, mask_bitmap, mask_gc, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+}
+
+static void lesstif_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
+{
+	if (pinout && use_mask && gc->erase)
+		return;
+	if ((conf_core.editor.thin_draw || conf_core.editor.thin_draw_poly) && gc->erase)
+		return;
+#if 0
+	pcb_printf("fill_circle %#mD %#mS", cx, cy, radius);
+#endif
+	radius = Vz(radius);
+	cx = Vx(cx) - radius;
+	cy = Vy(cy) - radius;
+	if (cx < -2 * radius || cx > view_width)
+		return;
+	if (cy < -2 * radius || cy > view_height)
+		return;
+#if 0
+	pcb_printf(" = %#mD %#mS %lx %s\n", cx, cy, radius, gc->color, gc->colorname);
+#endif
+	set_gc(gc);
+	XFillArc(display, pixmap, my_gc, cx, cy, radius * 2, radius * 2, 0, 360 * 64);
+	if (use_mask)
+		XFillArc(display, mask_bitmap, mask_gc, cx, cy, radius * 2, radius * 2, 0, 360 * 64);
+}
+
+static void lesstif_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y)
+{
+	static XPoint *p = 0;
+	static int maxp = 0;
+	int i;
+
+	if (maxp < n_coords) {
+		maxp = n_coords + 10;
+		if (p)
+			p = (XPoint *) realloc(p, maxp * sizeof(XPoint));
+		else
+			p = (XPoint *) malloc(maxp * sizeof(XPoint));
+	}
+
+	for (i = 0; i < n_coords; i++) {
+		p[i].x = Vx(x[i]);
+		p[i].y = Vy(y[i]);
+	}
+#if 0
+	printf("fill_polygon %d pts\n", n_coords);
+#endif
+	set_gc(gc);
+	XFillPolygon(display, pixmap, my_gc, p, n_coords, Complex, CoordModeOrigin);
+	if (use_mask)
+		XFillPolygon(display, mask_bitmap, mask_gc, p, n_coords, Complex, CoordModeOrigin);
+}
+
+static void lesstif_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
+{
+	int vw = Vz(gc->width);
+	if ((pinout || conf_core.editor.thin_draw) && gc->erase)
+		return;
+	x1 = Vx(x1);
+	y1 = Vy(y1);
+	x2 = Vx(x2);
+	y2 = Vy(y2);
+	if (x1 < -vw && x2 < -vw)
+		return;
+	if (y1 < -vw && y2 < -vw)
+		return;
+	if (x1 > view_width + vw && x2 > view_width + vw)
+		return;
+	if (y1 > view_height + vw && y2 > view_height + vw)
+		return;
+	if (x1 > x2) {
+		int xt = x1;
+		x1 = x2;
+		x2 = xt;
+	}
+	if (y1 > y2) {
+		int yt = y1;
+		y1 = y2;
+		y2 = yt;
+	}
+	set_gc(gc);
+	XFillRectangle(display, pixmap, my_gc, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+	if (use_mask)
+		XFillRectangle(display, mask_bitmap, mask_gc, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+}
+
+static void lesstif_calibrate(double xval, double yval)
+{
+	CRASH("lesstif_calibrate");
+}
+
+static int lesstif_shift_is_pressed(void)
+{
+	return shift_pressed;
+}
+
+static int lesstif_control_is_pressed(void)
+{
+	return ctrl_pressed;
+}
+
+static int lesstif_mod1_is_pressed(void)
+{
+	return alt_pressed;
+}
+
+extern void lesstif_get_coords(const char *msg, Coord * x, Coord * y);
+
+static void lesstif_set_crosshair(int x, int y, int action)
+{
+	if (crosshair_x != x || crosshair_y != y) {
+		lesstif_show_crosshair(0);
+		crosshair_x = x;
+		crosshair_y = y;
+		need_idle_proc();
+
+		if (mainwind
+				&& !in_move_event
+				&& (x < view_left_x
+						|| x > view_left_x + view_width * view_zoom || y < view_top_y || y > view_top_y + view_height * view_zoom)) {
+			view_left_x = x - (view_width * view_zoom) / 2;
+			view_top_y = y - (view_height * view_zoom) / 2;
+			lesstif_pan_fixup();
+		}
+
+	}
+
+	if (action == HID_SC_PAN_VIEWPORT) {
+		Window root, child;
+		unsigned int keys_buttons;
+		int pos_x, pos_y, root_x, root_y;
+		XQueryPointer(display, window, &root, &child, &root_x, &root_y, &pos_x, &pos_y, &keys_buttons);
+		if (conf_core.editor.view.flip_x)
+			view_left_x = x - (view_width - pos_x) * view_zoom;
+		else
+			view_left_x = x - pos_x * view_zoom;
+		if (conf_core.editor.view.flip_y)
+			view_top_y = y - (view_height - pos_y) * view_zoom;
+		else
+			view_top_y = y - pos_y * view_zoom;
+		lesstif_pan_fixup();
+		action = HID_SC_WARP_POINTER;
+	}
+	if (action == HID_SC_WARP_POINTER) {
+		in_move_event++;
+		XWarpPointer(display, None, window, 0, 0, 0, 0, Vx(x), Vy(y));
+		in_move_event--;
+	}
+}
+
+typedef struct {
+	void (*func) (hidval);
+	hidval user_data;
+	XtIntervalId id;
+} TimerStruct;
+
+static void lesstif_timer_cb(XtPointer * p, XtIntervalId * id)
+{
+	TimerStruct *ts = (TimerStruct *) p;
+	ts->func(ts->user_data);
+	free(ts);
+}
+
+static hidval lesstif_add_timer(void (*func) (hidval user_data), unsigned long milliseconds, hidval user_data)
+{
+	TimerStruct *t;
+	hidval rv;
+	t = (TimerStruct *) malloc(sizeof(TimerStruct));
+	rv.ptr = t;
+	t->func = func;
+	t->user_data = user_data;
+	t->id = XtAppAddTimeOut(app_context, milliseconds, (XtTimerCallbackProc) lesstif_timer_cb, t);
+	return rv;
+}
+
+static void lesstif_stop_timer(hidval hv)
+{
+	TimerStruct *ts = (TimerStruct *) hv.ptr;
+	XtRemoveTimeOut(ts->id);
+	free(ts);
+}
+
+
+typedef struct {
+	void (*func) (hidval, int, unsigned int, hidval);
+	hidval user_data;
+	int fd;
+	XtInputId id;
+} WatchStruct;
+
+	/* We need a wrapper around the hid file watch because to pass the correct flags
+	 */
+static void lesstif_watch_cb(XtPointer client_data, int *fid, XtInputId * id)
+{
+	unsigned int pcb_condition = 0;
+	struct pollfd fds;
+	short condition;
+	hidval x;
+	WatchStruct *watch = (WatchStruct *) client_data;
+
+	fds.fd = watch->fd;
+	fds.events = POLLIN | POLLOUT;
+	poll(&fds, 1, 0);
+	condition = fds.revents;
+
+	/* Should we only include those we were asked to watch? */
+	if (condition & POLLIN)
+		pcb_condition |= PCB_WATCH_READABLE;
+	if (condition & POLLOUT)
+		pcb_condition |= PCB_WATCH_WRITABLE;
+	if (condition & POLLERR)
+		pcb_condition |= PCB_WATCH_ERROR;
+	if (condition & POLLHUP)
+		pcb_condition |= PCB_WATCH_HANGUP;
+
+	x.ptr = (void *) watch;
+	watch->func(x, watch->fd, pcb_condition, watch->user_data);
+
+	return;
+}
+
+hidval
+lesstif_watch_file(int fd, unsigned int condition,
+									 void (*func) (hidval watch, int fd, unsigned int condition, hidval user_data), hidval user_data)
+{
+	WatchStruct *watch = (WatchStruct *) malloc(sizeof(WatchStruct));
+	hidval ret;
+	unsigned int xt_condition = 0;
+
+	if (condition & PCB_WATCH_READABLE)
+		xt_condition |= XtInputReadMask;
+	if (condition & PCB_WATCH_WRITABLE)
+		xt_condition |= XtInputWriteMask;
+	if (condition & PCB_WATCH_ERROR)
+		xt_condition |= XtInputExceptMask;
+	if (condition & PCB_WATCH_HANGUP)
+		xt_condition |= XtInputExceptMask;
+
+	watch->func = func;
+	watch->user_data = user_data;
+	watch->fd = fd;
+	watch->id = XtAppAddInput(app_context, fd, (XtPointer) (size_t) xt_condition, lesstif_watch_cb, watch);
+
+	ret.ptr = (void *) watch;
+	return ret;
+}
+
+void lesstif_unwatch_file(hidval data)
+{
+	WatchStruct *watch = (WatchStruct *) data.ptr;
+	XtRemoveInput(watch->id);
+	free(watch);
+}
+
+typedef struct {
+	XtBlockHookId id;
+	void (*func) (hidval user_data);
+	hidval user_data;
+} BlockHookStruct;
+
+static void lesstif_block_hook_cb(XtPointer user_data);
+
+static void lesstif_block_hook_cb(XtPointer user_data)
+{
+	BlockHookStruct *block_hook = (BlockHookStruct *) user_data;
+	block_hook->func(block_hook->user_data);
+}
+
+static hidval lesstif_add_block_hook(void (*func) (hidval data), hidval user_data)
+{
+	hidval ret;
+	BlockHookStruct *block_hook = (BlockHookStruct *) malloc(sizeof(BlockHookStruct));
+
+	block_hook->func = func;
+	block_hook->user_data = user_data;
+
+	block_hook->id = XtAppAddBlockHook(app_context, lesstif_block_hook_cb, (XtPointer) block_hook);
+
+	ret.ptr = (void *) block_hook;
+	return ret;
+}
+
+static void lesstif_stop_block_hook(hidval mlpoll)
+{
+	BlockHookStruct *block_hook = (BlockHookStruct *) mlpoll.ptr;
+	XtRemoveBlockHook(block_hook->id);
+	free(block_hook);
+}
+
+
+extern void lesstif_logv(enum pcb_message_level level, const char *fmt, va_list ap);
+
+extern int lesstif_confirm_dialog(const char *msg, ...);
+
+extern int lesstif_close_confirm_dialog();
+
+extern void lesstif_report_dialog(const char *title, const char *msg);
+
+extern int
+lesstif_attribute_dialog(HID_Attribute * attrs, int n_attrs, HID_Attr_Val * results, const char *title, const char *descr);
+
+static void pinout_callback(Widget da, PinoutData * pd, XmDrawingAreaCallbackStruct * cbs)
+{
+	BoxType region;
+	int save_vx, save_vy, save_vw, save_vh;
+	int save_fx, save_fy;
+	double save_vz;
+	Pixmap save_px;
+	int reason = cbs ? cbs->reason : 0;
+
+	if (pd->window == 0 && reason == XmCR_RESIZE)
+		return;
+	if (pd->window == 0 || reason == XmCR_RESIZE) {
+		Dimension w, h;
+		double z;
+
+		stdarg_n = 0;
+		stdarg(XmNwidth, &w);
+		stdarg(XmNheight, &h);
+		XtGetValues(da, stdarg_args, stdarg_n);
+
+		pd->window = XtWindow(da);
+		pd->v_width = w;
+		pd->v_height = h;
+		pd->zoom = (pd->right - pd->left + 1) / (double) w;
+		z = (pd->bottom - pd->top + 1) / (double) h;
+		if (pd->zoom < z)
+			pd->zoom = z;
+
+		pd->x = (pd->left + pd->right) / 2 - pd->v_width * pd->zoom / 2;
+		pd->y = (pd->top + pd->bottom) / 2 - pd->v_height * pd->zoom / 2;
+	}
+
+	save_vx = view_left_x;
+	save_vy = view_top_y;
+	save_vz = view_zoom;
+	save_vw = view_width;
+	save_vh = view_height;
+	save_fx = conf_core.editor.view.flip_x;
+	save_fy = conf_core.editor.view.flip_y;
+	save_px = pixmap;
+	pinout = pd;
+	pixmap = pd->window;
+	view_left_x = pd->x;
+	view_top_y = pd->y;
+	view_zoom = pd->zoom;
+	view_width = pd->v_width;
+	view_height = pd->v_height;
+	use_mask = 0;
+	conf_force_set_bool(conf_core.editor.view.flip_x, 0);
+	conf_force_set_bool(conf_core.editor.view.flip_y, 0);
+	region.X1 = 0;
+	region.Y1 = 0;
+	region.X2 = PCB->MaxWidth;
+	region.Y2 = PCB->MaxHeight;
+
+	XFillRectangle(display, pixmap, bg_gc, 0, 0, pd->v_width, pd->v_height);
+	hid_expose_callback(&lesstif_hid, &region, pd->item);
+
+	pinout = 0;
+	view_left_x = save_vx;
+	view_top_y = save_vy;
+	view_zoom = save_vz;
+	view_width = save_vw;
+	view_height = save_vh;
+	pixmap = save_px;
+	conf_force_set_bool(conf_core.editor.view.flip_x, save_fx);
+	conf_force_set_bool(conf_core.editor.view.flip_y, save_fy);
+}
+
+static void pinout_unmap(Widget w, PinoutData * pd, void *v)
+{
+	if (pd->prev)
+		pd->prev->next = pd->next;
+	else
+		pinouts = pd->next;
+	if (pd->next)
+		pd->next->prev = pd->prev;
+	XtDestroyWidget(XtParent(pd->form));
+	free(pd);
+}
+
+static void lesstif_show_item(void *item)
+{
+	double scale;
+	Widget da;
+	BoxType *extents;
+	PinoutData *pd;
+
+	for (pd = pinouts; pd; pd = pd->next)
+		if (pd->item == item)
+			return;
+	if (!mainwind)
+		return;
+
+	pd = (PinoutData *) calloc(1, sizeof(PinoutData));
+
+	pd->item = item;
+
+	extents = hid_get_extents(item);
+	pd->left = extents->X1;
+	pd->right = extents->X2;
+	pd->top = extents->Y1;
+	pd->bottom = extents->Y2;
+
+	if (pd->left > pd->right) {
+		free(pd);
+		return;
+	}
+	pd->prev = 0;
+	pd->next = pinouts;
+	if (pd->next)
+		pd->next->prev = pd;
+	pinouts = pd;
+	pd->zoom = 0;
+
+	stdarg_n = 0;
+	pd->form = XmCreateFormDialog(mainwind, XmStrCast("pinout"), stdarg_args, stdarg_n);
+	pd->window = 0;
+	XtAddCallback(pd->form, XmNunmapCallback, (XtCallbackProc) pinout_unmap, (XtPointer) pd);
+
+	scale = sqrt(200.0 * 200.0 / ((pd->right - pd->left + 1.0) * (pd->bottom - pd->top + 1.0)));
+
+	stdarg_n = 0;
+	stdarg(XmNwidth, (int) (scale * (pd->right - pd->left + 1)));
+	stdarg(XmNheight, (int) (scale * (pd->bottom - pd->top + 1)));
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_FORM);
+	da = XmCreateDrawingArea(pd->form, XmStrCast("pinout"), stdarg_args, stdarg_n);
+	XtManageChild(da);
+
+	XtAddCallback(da, XmNexposeCallback, (XtCallbackProc) pinout_callback, (XtPointer) pd);
+	XtAddCallback(da, XmNresizeCallback, (XtCallbackProc) pinout_callback, (XtPointer) pd);
+
+	XtManageChild(pd->form);
+	pinout = 0;
+}
+
+static void lesstif_beep(void)
+{
+	putchar(7);
+	fflush(stdout);
+}
+
+
+static pcb_bool progress_cancelled = pcb_false;
+
+static void progress_cancel_callback(Widget w, void *v, void *cbs)
+{
+	progress_cancelled = pcb_true;
+}
+
+static Widget progress_dialog = 0;
+static Widget progress_cancel, progress_label;
+static Widget progress_scale;
+
+static void lesstif_progress_dialog(int so_far, int total, const char *msg)
+{
+	XmString xs;
+
+	if (mainwind == 0)
+		return;
+
+	if (progress_dialog == 0) {
+		Atom close_atom;
+
+		stdarg_n = 0;
+		stdarg(XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON);
+		stdarg(XmNtitle, "Progress");
+		stdarg(XmNdialogStyle, XmDIALOG_APPLICATION_MODAL);
+		stdarg(XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
+		progress_dialog = XmCreateInformationDialog(mainwind, XmStrCast("progress"), stdarg_args, stdarg_n);
+		XtAddCallback(progress_dialog, XmNcancelCallback, (XtCallbackProc) progress_cancel_callback, NULL);
+
+		progress_cancel = XmMessageBoxGetChild(progress_dialog, XmDIALOG_CANCEL_BUTTON);
+		progress_label = XmMessageBoxGetChild(progress_dialog, XmDIALOG_MESSAGE_LABEL);
+
+		XtUnmanageChild(XmMessageBoxGetChild(progress_dialog, XmDIALOG_OK_BUTTON));
+		XtUnmanageChild(XmMessageBoxGetChild(progress_dialog, XmDIALOG_HELP_BUTTON));
+
+		stdarg(XmNdefaultPosition, False);
+		XtSetValues(progress_dialog, stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNminimum, 0);
+		stdarg(XmNvalue, 0);
+		stdarg(XmNmaximum, total > 0 ? total : 1);
+		stdarg(XmNorientation, XmHORIZONTAL);
+		stdarg(XmNshowArrows, pcb_false);
+		progress_scale = XmCreateScrollBar(progress_dialog, XmStrCast("scale"), stdarg_args, stdarg_n);
+		XtManageChild(progress_scale);
+
+		close_atom = XmInternAtom(display, XmStrCast("WM_DELETE_WINDOW"), 0);
+		XmAddWMProtocolCallback(XtParent(progress_dialog), close_atom, (XtCallbackProc) progress_cancel_callback, 0);
+	}
+
+	stdarg_n = 0;
+	stdarg(XmNvalue, 0);
+	stdarg(XmNsliderSize, (so_far <= total) ? (so_far < 0) ? 0 : so_far : total);
+	stdarg(XmNmaximum, total > 0 ? total : 1);
+	XtSetValues(progress_scale, stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	xs = XmStringCreatePCB(msg);
+	stdarg(XmNmessageString, xs);
+	XtSetValues(progress_dialog, stdarg_args, stdarg_n);
+
+	return;
+}
+
+#define MIN_TIME_SEPARATION 0.1	/* seconds */
+
+static int lesstif_progress(int so_far, int total, const char *message)
+{
+	static pcb_bool started = pcb_false;
+	XEvent e;
+	struct timeval time;
+	double time_delta, time_now;
+	static double time_then = 0.0;
+	int retval = 0;
+
+	if (so_far == 0 && total == 0 && message == NULL) {
+		XtUnmanageChild(progress_dialog);
+		started = pcb_false;
+		progress_cancelled = pcb_false;
+		return retval;
+	}
+
+	gettimeofday(&time, NULL);
+	time_now = time.tv_sec + time.tv_usec / 1000000.0;
+
+	time_delta = time_now - time_then;
+
+	if (started && time_delta < MIN_TIME_SEPARATION)
+		return retval;
+
+	/* Create or update the progress dialog */
+	lesstif_progress_dialog(so_far, total, message);
+
+	if (!started) {
+		XtManageChild(progress_dialog);
+		started = pcb_true;
+	}
+
+	/* Dispatch pending events */
+	while (XtAppPending(app_context)) {
+		XtAppNextEvent(app_context, &e);
+		XtDispatchEvent(&e);
+	}
+	idle_proc(NULL);
+
+	/* If rendering takes a while, make sure the core has enough time to
+	   do work.  */
+	gettimeofday(&time, NULL);
+	time_then = time.tv_sec + time.tv_usec / 1000000.0;
+
+	return progress_cancelled;
+}
+
+static HID *lesstif_request_debug_draw(void)
+{
+	/* Send drawing to the backing pixmap */
+	pixmap = main_pixmap;
+	return &lesstif_hid;
+}
+
+static void lesstif_flush_debug_draw(void)
+{
+	/* Copy the backing pixmap to the display and redraw any attached objects */
+	XSetFunction(display, my_gc, GXcopy);
+	XCopyArea(display, main_pixmap, window, my_gc, 0, 0, view_width, view_height, 0, 0);
+	pixmap = window;
+	if (crosshair_on) {
+		DrawAttached();
+		DrawMark();
+	}
+	pixmap = main_pixmap;
+}
+
+static void lesstif_finish_debug_draw(void)
+{
+	lesstif_flush_debug_draw();
+	/* No special tear down requirements
+	 */
+}
+
+static int lesstif_usage(const char *topic)
+{
+	fprintf(stderr, "\nLesstif GUI command line arguments:\n\n");
+	hid_usage(lesstif_attribute_list, sizeof(lesstif_attribute_list) / sizeof(lesstif_attribute_list[0]));
+	fprintf(stderr, "\nInvocation: pcb-rnd --gui lesstif [options]\n");
+	return 0;
+}
+
+#include "dolists.h"
+
+void lesstif_create_menu(const char *menu, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie);
+
+pcb_uninit_t hid_hid_lesstif_init()
+{
+	memset(&lesstif_hid, 0, sizeof(HID));
+
+	common_nogui_init(&lesstif_hid);
+	common_draw_helpers_init(&lesstif_hid);
+
+	lesstif_hid.struct_size = sizeof(HID);
+	lesstif_hid.name = "lesstif";
+	lesstif_hid.description = "LessTif - a Motif clone for X/Unix";
+	lesstif_hid.gui = 1;
+	lesstif_hid.poly_before = 1;
+
+	lesstif_hid.get_export_options = lesstif_get_export_options;
+	lesstif_hid.do_export = lesstif_do_export;
+	lesstif_hid.do_exit = lesstif_do_exit;
+	lesstif_hid.uninit = lesstif_uninit;
+	lesstif_hid.parse_arguments = lesstif_parse_arguments;
+	lesstif_hid.invalidate_lr = lesstif_invalidate_lr;
+	lesstif_hid.invalidate_all = lesstif_invalidate_all;
+	lesstif_hid.notify_crosshair_change = lesstif_notify_crosshair_change;
+	lesstif_hid.notify_mark_change = lesstif_notify_mark_change;
+	lesstif_hid.set_layer = lesstif_set_layer;
+	lesstif_hid.make_gc = lesstif_make_gc;
+	lesstif_hid.destroy_gc = lesstif_destroy_gc;
+	lesstif_hid.use_mask = lesstif_use_mask;
+	lesstif_hid.set_color = lesstif_set_color;
+	lesstif_hid.set_line_cap = lesstif_set_line_cap;
+	lesstif_hid.set_line_width = lesstif_set_line_width;
+	lesstif_hid.set_draw_xor = lesstif_set_draw_xor;
+	lesstif_hid.draw_line = lesstif_draw_line;
+	lesstif_hid.draw_arc = lesstif_draw_arc;
+	lesstif_hid.draw_rect = lesstif_draw_rect;
+	lesstif_hid.fill_circle = lesstif_fill_circle;
+	lesstif_hid.fill_polygon = lesstif_fill_polygon;
+	lesstif_hid.fill_rect = lesstif_fill_rect;
+
+	lesstif_hid.calibrate = lesstif_calibrate;
+	lesstif_hid.shift_is_pressed = lesstif_shift_is_pressed;
+	lesstif_hid.control_is_pressed = lesstif_control_is_pressed;
+	lesstif_hid.mod1_is_pressed = lesstif_mod1_is_pressed;
+	lesstif_hid.get_coords = lesstif_get_coords;
+	lesstif_hid.set_crosshair = lesstif_set_crosshair;
+	lesstif_hid.add_timer = lesstif_add_timer;
+	lesstif_hid.stop_timer = lesstif_stop_timer;
+	lesstif_hid.watch_file = lesstif_watch_file;
+	lesstif_hid.unwatch_file = lesstif_unwatch_file;
+	lesstif_hid.add_block_hook = lesstif_add_block_hook;
+	lesstif_hid.stop_block_hook = lesstif_stop_block_hook;
+
+	lesstif_hid.log = lesstif_log;
+	lesstif_hid.logv = lesstif_logv;
+	lesstif_hid.confirm_dialog = lesstif_confirm_dialog;
+	lesstif_hid.close_confirm_dialog = lesstif_close_confirm_dialog;
+	lesstif_hid.report_dialog = lesstif_report_dialog;
+	lesstif_hid.prompt_for = lesstif_prompt_for;
+	lesstif_hid.fileselect = lesstif_fileselect;
+	lesstif_hid.attribute_dialog = lesstif_attribute_dialog;
+	lesstif_hid.show_item = lesstif_show_item;
+	lesstif_hid.beep = lesstif_beep;
+	lesstif_hid.progress = lesstif_progress;
+	lesstif_hid.edit_attributes = lesstif_attributes_dialog;
+
+	lesstif_hid.request_debug_draw = lesstif_request_debug_draw;
+	lesstif_hid.flush_debug_draw = lesstif_flush_debug_draw;
+	lesstif_hid.finish_debug_draw = lesstif_finish_debug_draw;
+
+	lesstif_hid.create_menu = lesstif_create_menu;
+	lesstif_hid.usage = lesstif_usage;
+
+	hid_register_hid(&lesstif_hid);
+
+	return NULL;
+}
+
+static void lesstif_begin(void)
+{
+	REGISTER_ACTIONS(lesstif_library_action_list, lesstif_cookie)
+	REGISTER_ATTRIBUTES(lesstif_attribute_list, lesstif_cookie)
+	REGISTER_ACTIONS(lesstif_main_action_list, lesstif_cookie)
+	REGISTER_ACTIONS(lesstif_dialog_action_list, lesstif_cookie)
+	REGISTER_ACTIONS(lesstif_netlist_action_list, lesstif_cookie)
+	REGISTER_ACTIONS(lesstif_menu_action_list, lesstif_cookie)
+	REGISTER_ACTIONS(lesstif_styles_action_list, lesstif_cookie)
+}
+
+static void lesstif_end(void)
+{
+	hid_remove_actions_by_cookie(lesstif_cookie);
+	hid_remove_attributes_by_cookie(lesstif_cookie);
+}
diff --git a/src_plugins/hid_lesstif/menu.c b/src_plugins/hid_lesstif/menu.c
new file mode 100644
index 0000000..1428785
--- /dev/null
+++ b/src_plugins/hid_lesstif/menu.c
@@ -0,0 +1,979 @@
+#include "xincludes.h"
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "pcb-printf.h"
+#include "layer.h"
+
+#include "hid.h"
+#include "hid_cfg.h"
+#include "hid_cfg_action.h"
+#include "hid_cfg_input.h"
+#include "lesstif.h"
+#include "mymem.h"
+#include "paths.h"
+#include "hid_actions.h"
+#include "hid_flags.h"
+#include "stdarg.h"
+#include "compat_misc.h"
+#include <genht/hash.h>
+
+Widget lesstif_menubar;
+hid_cfg_t *lesstif_cfg;
+
+#ifndef R_OK
+/* Common value for systems that don't define it.  */
+#define R_OK 4
+#endif
+
+static Colormap cmap;
+
+static void note_accelerator(const lht_node_t *node);
+static void note_widget_flag(Widget w, char *type, const char *name);
+
+static const char getxy_syntax[] = "GetXY()";
+
+static const char getxy_help[] = "Get a coordinate.";
+
+/* %start-doc actions GetXY
+
+Prompts the user for a coordinate, if one is not already selected.
+
+%end-doc */
+
+static int GetXY(int argc, const char **argv, Coord x, Coord y)
+{
+	return 0;
+}
+
+/*-----------------------------------------------------------------------------*/
+
+#define LB_SILK	(MAX_LAYER+0)
+#define LB_RATS	(MAX_LAYER+1)
+#define LB_NUMPICK (LB_RATS+1)
+/* more */
+#define LB_PINS	(MAX_LAYER+2)
+#define LB_VIAS	(MAX_LAYER+3)
+#define LB_BACK	(MAX_LAYER+4)
+#define LB_MASK	(MAX_LAYER+5)
+#define LB_NUM  (MAX_LAYER+6)
+
+typedef struct {
+	Widget w[LB_NUM];
+	int is_pick;
+} LayerButtons;
+
+static LayerButtons *layer_button_list = 0;
+static int num_layer_buttons = 0;
+static int fg_colors[LB_NUM];
+static int bg_color;
+
+extern Widget lesstif_m_layer;
+
+static int LayersChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	int l, i, set;
+	const char *name;
+	int current_layer;
+
+	if (!layer_button_list)
+		return 0;
+	if (PCB && PCB->Data) {
+		DataType *d = PCB->Data;
+		for (i = 0; i < MAX_LAYER; i++)
+			fg_colors[i] = lesstif_parse_color(d->Layer[i].Color);
+		fg_colors[LB_SILK] = lesstif_parse_color(PCB->ElementColor);
+		fg_colors[LB_RATS] = lesstif_parse_color(PCB->RatColor);
+		fg_colors[LB_PINS] = lesstif_parse_color(PCB->PinColor);
+		fg_colors[LB_VIAS] = lesstif_parse_color(PCB->ViaColor);
+		fg_colors[LB_BACK] = lesstif_parse_color(PCB->InvisibleObjectsColor);
+		fg_colors[LB_MASK] = lesstif_parse_color(PCB->MaskColor);
+		bg_color = lesstif_parse_color(conf_core.appearance.color.background);
+	}
+	else {
+		for (i = 0; i < MAX_LAYER; i++)
+			fg_colors[i] = lesstif_parse_color(conf_core.appearance.color.layer[i]);
+		fg_colors[LB_SILK] = lesstif_parse_color(conf_core.appearance.color.element);
+		fg_colors[LB_RATS] = lesstif_parse_color(conf_core.appearance.color.rat);
+		fg_colors[LB_PINS] = lesstif_parse_color(conf_core.appearance.color.pin);
+		fg_colors[LB_VIAS] = lesstif_parse_color(conf_core.appearance.color.via);
+		fg_colors[LB_BACK] = lesstif_parse_color(conf_core.appearance.color.invisible_objects);
+		fg_colors[LB_MASK] = lesstif_parse_color(conf_core.appearance.color.mask);
+		bg_color = lesstif_parse_color(conf_core.appearance.color.background);
+	}
+
+	if (PCB->RatDraw)
+		current_layer = LB_RATS;
+	else if (PCB->SilkActive)
+		current_layer = LB_SILK;
+	else
+		current_layer = LayerStack[0];
+
+	for (l = 0; l < num_layer_buttons; l++) {
+		LayerButtons *lb = layer_button_list + l;
+		for (i = 0; i < (lb->is_pick ? LB_NUMPICK : LB_NUM); i++) {
+			switch (i) {
+			case LB_SILK:
+				set = PCB->ElementOn;
+				break;
+			case LB_RATS:
+				set = PCB->RatOn;
+				break;
+			case LB_PINS:
+				set = PCB->PinOn;
+				break;
+			case LB_VIAS:
+				set = PCB->ViaOn;
+				break;
+			case LB_BACK:
+				set = PCB->InvisibleObjectsOn;
+				break;
+			case LB_MASK:
+				set = conf_core.editor.show_mask;
+				break;
+			default:									/* layers */
+				set = PCB->Data->Layer[i].On;
+				break;
+			}
+
+			stdarg_n = 0;
+			if (i < MAX_LAYER && PCB->Data->Layer[i].Name) {
+				XmString s = XmStringCreatePCB(PCB->Data->Layer[i].Name);
+				stdarg(XmNlabelString, s);
+			}
+			if (!lb->is_pick) {
+				if (set) {
+					stdarg(XmNforeground, bg_color);
+					stdarg(XmNbackground, fg_colors[i]);
+				}
+				else {
+					stdarg(XmNforeground, fg_colors[i]);
+					stdarg(XmNbackground, bg_color);
+				}
+				stdarg(XmNset, set);
+			}
+			else {
+				stdarg(XmNforeground, bg_color);
+				stdarg(XmNbackground, fg_colors[i]);
+				stdarg(XmNset, current_layer == i ? True : False);
+			}
+			XtSetValues(lb->w[i], stdarg_args, stdarg_n);
+
+			if (i >= max_copper_layer && i < MAX_LAYER)
+				XtUnmanageChild(lb->w[i]);
+			else
+				XtManageChild(lb->w[i]);
+		}
+	}
+	if (lesstif_m_layer) {
+		switch (current_layer) {
+		case LB_RATS:
+			name = "Rats";
+			break;
+		case LB_SILK:
+			name = "Silk";
+			break;
+		default:
+			name = PCB->Data->Layer[current_layer].Name;
+			break;
+		}
+		stdarg_n = 0;
+		stdarg(XmNbackground, fg_colors[current_layer]);
+		stdarg(XmNforeground, bg_color);
+		stdarg(XmNlabelString, XmStringCreatePCB(name));
+		XtSetValues(lesstif_m_layer, stdarg_args, stdarg_n);
+	}
+
+	lesstif_update_layer_groups();
+
+	return 0;
+}
+
+static void show_one_layer_button(int layer, int set)
+{
+	int l;
+
+	stdarg_n = 0;
+	if (set) {
+		stdarg(XmNforeground, bg_color);
+		stdarg(XmNbackground, fg_colors[layer]);
+	}
+	else {
+		stdarg(XmNforeground, fg_colors[layer]);
+		stdarg(XmNbackground, bg_color);
+	}
+	stdarg(XmNset, set);
+
+	for (l = 0; l < num_layer_buttons; l++) {
+		LayerButtons *lb = layer_button_list + l;
+		if (!lb->is_pick)
+			XtSetValues(lb->w[layer], stdarg_args, stdarg_n);
+	}
+}
+
+static void layer_button_callback(Widget w, int layer, XmPushButtonCallbackStruct * pbcs)
+{
+	int l, set;
+	switch (layer) {
+	case LB_SILK:
+		set = PCB->ElementOn = !PCB->ElementOn;
+		PCB->Data->SILKLAYER.On = set;
+		PCB->Data->BACKSILKLAYER.On = set;
+		break;
+	case LB_RATS:
+		set = PCB->RatOn = !PCB->RatOn;
+		break;
+	case LB_PINS:
+		set = PCB->PinOn = !PCB->PinOn;
+		break;
+	case LB_VIAS:
+		set = PCB->ViaOn = !PCB->ViaOn;
+		break;
+	case LB_BACK:
+		set = PCB->InvisibleObjectsOn = !PCB->InvisibleObjectsOn;
+		break;
+	case LB_MASK:
+		conf_toggle_editor(show_mask);
+		set = conf_core.editor.show_mask;
+		break;
+	default:											/* layers */
+		set = PCB->Data->Layer[layer].On = !PCB->Data->Layer[layer].On;
+		break;
+	}
+
+	show_one_layer_button(layer, set);
+	if (layer < max_copper_layer) {
+		int i;
+		int group = GetLayerGroupNumberByNumber(layer);
+		for (i = 0; i < PCB->LayerGroups.Number[group]; i++) {
+			l = PCB->LayerGroups.Entries[group][i];
+			if (l != layer && l < max_copper_layer) {
+				show_one_layer_button(l, set);
+				PCB->Data->Layer[l].On = set;
+			}
+		}
+	}
+	lesstif_invalidate_all();
+}
+
+static void layerpick_button_callback(Widget w, int layer, XmPushButtonCallbackStruct * pbcs)
+{
+	int l, i;
+	const char *name;
+	PCB->RatDraw = (layer == LB_RATS);
+	PCB->SilkActive = (layer == LB_SILK);
+	if (layer < max_copper_layer)
+		ChangeGroupVisibility(layer, 1, 1);
+	for (l = 0; l < num_layer_buttons; l++) {
+		LayerButtons *lb = layer_button_list + l;
+		if (!lb->is_pick)
+			continue;
+		for (i = 0; i < LB_NUMPICK; i++)
+			XmToggleButtonSetState(lb->w[i], layer == i, False);
+	}
+	switch (layer) {
+	case LB_RATS:
+		name = "Rats";
+		break;
+	case LB_SILK:
+		name = "Silk";
+		break;
+	default:
+		name = PCB->Data->Layer[layer].Name;
+		break;
+	}
+	stdarg_n = 0;
+	stdarg(XmNbackground, fg_colors[layer]);
+	stdarg(XmNforeground, bg_color);
+	stdarg(XmNlabelString, XmStringCreatePCB(name));
+	XtSetValues(lesstif_m_layer, stdarg_args, stdarg_n);
+	lesstif_invalidate_all();
+}
+
+static const char selectlayer_syntax[] = "SelectLayer(1..MAXLAYER|Silk|Rats)";
+
+static const char selectlayer_help[] = "Select which layer is the current layer.";
+
+/* %start-doc actions SelectLayer
+
+The specified layer becomes the currently active layer.  It is made
+visible if it is not already visible
+
+%end-doc */
+
+static int SelectLayer(int argc, const char **argv, Coord x, Coord y)
+{
+	int newl;
+	if (argc == 0)
+		return 1;
+	if (strcasecmp(argv[0], "silk") == 0)
+		newl = LB_SILK;
+	else if (strcasecmp(argv[0], "rats") == 0)
+		newl = LB_RATS;
+	else
+		newl = atoi(argv[0]) - 1;
+	layerpick_button_callback(0, newl, 0);
+	return 0;
+}
+
+static const char toggleview_syntax[] =
+	"ToggleView(1..MAXLAYER)\n" "ToggleView(layername)\n" "ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)";
+
+static const char toggleview_help[] = "Toggle the visibility of the specified layer or layer group.";
+
+/* %start-doc actions ToggleView
+
+If you pass an integer, that layer is specified by index (the first
+layer is @code{1}, etc).  If you pass a layer name, that layer is
+specified by name.  When a layer is specified, the visibility of the
+layer group containing that layer is toggled.
+
+If you pass a special layer name, the visibility of those components
+(silk, rats, etc) is toggled.  Note that if you have a layer named
+the same as a special layer, the layer is chosen over the special layer.
+
+%end-doc */
+
+static int ToggleView(int argc, const char **argv, Coord x, Coord y)
+{
+	int i, l;
+
+	if (argc == 0)
+		return 1;
+	if (isdigit((int) argv[0][0])) {
+		l = atoi(argv[0]) - 1;
+		layer_button_callback(0, l, 0);
+	}
+	else if (strcmp(argv[0], "Silk") == 0)
+		layer_button_callback(0, LB_SILK, 0);
+	else if (strcmp(argv[0], "Rats") == 0)
+		layer_button_callback(0, LB_RATS, 0);
+	else if (strcmp(argv[0], "Pins") == 0)
+		layer_button_callback(0, LB_PINS, 0);
+	else if (strcmp(argv[0], "Vias") == 0)
+		layer_button_callback(0, LB_VIAS, 0);
+	else if (strcmp(argv[0], "Mask") == 0)
+		layer_button_callback(0, LB_MASK, 0);
+	else if (strcmp(argv[0], "BackSide") == 0)
+		layer_button_callback(0, LB_BACK, 0);
+	else {
+		l = -1;
+		for (i = 0; i < max_copper_layer + 2; i++)
+			if (strcmp(argv[0], PCB->Data->Layer[i].Name) == 0) {
+				l = i;
+				break;
+			}
+		if (l == -1)
+			return 1;
+		layer_button_callback(0, l, 0);
+	}
+	return 0;
+}
+
+static void insert_layerview_buttons(Widget menu)
+{
+	int i, s;
+	LayerButtons *lb;
+
+	num_layer_buttons++;
+	s = num_layer_buttons * sizeof(LayerButtons);
+	if (layer_button_list)
+		layer_button_list = (LayerButtons *) realloc(layer_button_list, s);
+	else
+		layer_button_list = (LayerButtons *) malloc(s);
+	lb = layer_button_list + num_layer_buttons - 1;
+
+	for (i = 0; i < LB_NUM; i++) {
+		static char namestr[] = "Label ";
+		const char *name = namestr;
+		/*int accel_idx = i;*/
+		Widget btn;
+		namestr[5] = 'A' + i;
+		switch (i) {
+		case LB_SILK:
+			name = "Silk";
+			/*accel_idx = max_copper_layer;*/
+			break;
+		case LB_RATS:
+			name = "Rat Lines";
+			/*accel_idx = max_copper_layer + 1;*/
+			break;
+		case LB_PINS:
+			name = "Pins/Pads";
+			break;
+		case LB_VIAS:
+			name = "Vias";
+			break;
+		case LB_BACK:
+			name = "Far Side";
+			break;
+		case LB_MASK:
+			name = "Solder Mask";
+			break;
+		}
+		stdarg_n = 0;
+		btn = XmCreateToggleButton(menu, XmStrCast(name), stdarg_args, stdarg_n);
+		XtManageChild(btn);
+		XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) layer_button_callback, (XtPointer) (size_t) i);
+		lb->w[i] = btn;
+
+		if (i == LB_MASK)
+			note_widget_flag(btn, XmNset, "showmask");
+	}
+	lb->is_pick = 0;
+	LayersChanged(0, 0, 0, 0);
+}
+
+static void insert_layerpick_buttons(Widget menu)
+{
+	int i, s;
+	LayerButtons *lb;
+
+	num_layer_buttons++;
+	s = num_layer_buttons * sizeof(LayerButtons);
+	if (layer_button_list)
+		layer_button_list = (LayerButtons *) realloc(layer_button_list, s);
+	else
+		layer_button_list = (LayerButtons *) malloc(s);
+	lb = layer_button_list + num_layer_buttons - 1;
+
+	for (i = 0; i < LB_NUMPICK; i++) {
+		static char namestr[] = "Label ";
+		const char *name = namestr;
+		/*int accel_idx = i;*/
+		char av[30];
+		Widget btn;
+		namestr[5] = 'A' + i;
+		switch (i) {
+		case LB_SILK:
+			name = "Silk";
+			/*accel_idx = max_copper_layer;*/
+			strcpy(av, "SelectLayer(Silk)");
+			break;
+		case LB_RATS:
+			name = "Rat Lines";
+			/*accel_idx = max_copper_layer + 1;*/
+			strcpy(av, "SelectLayer(Rats)");
+			break;
+		default:
+			sprintf(av, "SelectLayer(%d)", i + 1);
+			break;
+		}
+		stdarg_n = 0;
+		stdarg(XmNindicatorType, XmONE_OF_MANY);
+		btn = XmCreateToggleButton(menu, XmStrCast(name), stdarg_args, stdarg_n);
+		XtManageChild(btn);
+		XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) layerpick_button_callback, (XtPointer) (size_t) i);
+		lb->w[i] = btn;
+	}
+	lb->is_pick = 1;
+	LayersChanged(0, 0, 0, 0);
+}
+
+/*-----------------------------------------------------------------------------*/
+
+typedef struct {
+	Widget w;
+	const char *flagname;
+	int oldval;
+	char *xres;
+} WidgetFlagType;
+
+static WidgetFlagType *wflags = 0;
+static int n_wflags = 0;
+static int max_wflags = 0;
+
+static void note_widget_flag(Widget w, char *type, const char *name)
+{
+	if (n_wflags >= max_wflags) {
+		max_wflags += 20;
+		wflags = (WidgetFlagType *) realloc(wflags, max_wflags * sizeof(WidgetFlagType));
+	}
+	wflags[n_wflags].w = w;
+	wflags[n_wflags].flagname = name;
+	wflags[n_wflags].oldval = -1;
+	wflags[n_wflags].xres = type;
+	n_wflags++;
+}
+
+void lesstif_update_widget_flags()
+{
+	int i;
+	for (i = 0; i < n_wflags; i++) {
+		int v = hid_get_flag(wflags[i].flagname);
+		Arg args[2];
+		if (v < 0) {
+			XtSetArg(args[0], wflags[i].xres, 0);
+			XtSetArg(args[1], XtNsensitive, 0);
+			XtSetValues(wflags[i].w, args, 2);
+		}
+		else {
+			XtSetArg(args[0], wflags[i].xres, v ? 1 : 0);
+			XtSetValues(wflags[i].w, args, 1);
+		}
+		wflags[i].oldval = v;
+	}
+}
+
+/*-----------------------------------------------------------------------------*/
+
+HID_Action lesstif_menu_action_list[] = {
+	{"GetXY", "", GetXY,
+	 getxy_help, getxy_syntax},
+	{"LayersChanged", 0, LayersChanged,
+	 layerschanged_help, layerschanged_syntax},
+	{"ToggleView", 0, ToggleView,
+	 toggleview_help, toggleview_syntax},
+	{"SelectLayer", 0, SelectLayer,
+	 selectlayer_help, selectlayer_syntax}
+};
+
+REGISTER_ACTIONS(lesstif_menu_action_list, lesstif_cookie)
+#if 0
+		 static void
+		   stdarg_do_color(char *value, char *which)
+{
+	XColor color;
+	if (XParseColor(display, cmap, value, &color))
+		if (XAllocColor(display, cmap, &color)) {
+			stdarg(which, color.pixel);
+		}
+}
+#endif
+
+static int need_xy = 0, have_xy = 0, action_x, action_y;
+
+#if 0
+typedef struct ToggleItem {
+	struct ToggleItem *next;
+	Widget w;
+	char *group, *item;
+	XtCallbackProc callback;
+	lht_node_t *node;
+} ToggleItem;
+static ToggleItem *toggle_items = 0;
+
+static void radio_callback(Widget toggle, ToggleItem * me, XmToggleButtonCallbackStruct * cbs)
+{
+	if (!cbs->set)								/* uh uh, can't turn it off */
+		XmToggleButtonSetState(toggle, 1, 0);
+	else {
+		ToggleItem *ti;
+		for (ti = toggle_items; ti; ti = ti->next)
+			if (strcmp(me->group, ti->group) == 0) {
+				if (me->item == ti->item || strcmp(me->item, ti->item) == 0)
+					XmToggleButtonSetState(ti->w, 1, 0);
+				else
+					XmToggleButtonSetState(ti->w, 0, 0);
+			}
+		me->callback(toggle, me->node, cbs);
+	}
+}
+#endif
+
+int lesstif_button_event(Widget w, XEvent * e)
+{
+	have_xy = 1;
+	action_x = e->xbutton.x;
+	action_y = e->xbutton.y;
+	if (!need_xy)
+		return 0;
+	if (w != work_area)
+		return 1;
+	return 0;
+}
+
+void lesstif_get_xy(const char *message)
+{
+	XmString ls = XmStringCreatePCB(message);
+
+	XtManageChild(m_click);
+	stdarg_n = 0;
+	stdarg(XmNlabelString, ls);
+	XtSetValues(m_click, stdarg_args, stdarg_n);
+	/*printf("need xy: msg `%s'\n", msg); */
+	need_xy = 1;
+	XBell(display, 100);
+	while (!have_xy) {
+		XEvent e;
+		XtAppNextEvent(app_context, &e);
+		XtDispatchEvent(&e);
+	}
+	need_xy = 0;
+	have_xy = 1;
+	XtUnmanageChild(m_click);
+}
+
+void lesstif_get_coords(const char *msg, Coord * px, Coord * py)
+{
+	if (!have_xy && msg)
+		lesstif_get_xy(msg);
+	if (have_xy)
+		lesstif_coords_to_pcb(action_x, action_y, px, py);
+}
+
+static void callback(Widget w, lht_node_t * node, XmPushButtonCallbackStruct * pbcs)
+{
+	have_xy = 0;
+	lesstif_show_crosshair(0);
+	if (pbcs->event && pbcs->event->type == KeyPress) {
+		Dimension wx, wy;
+		Widget aw = XtWindowToWidget(display, pbcs->event->xkey.window);
+		action_x = pbcs->event->xkey.x;
+		action_y = pbcs->event->xkey.y;
+		if (aw) {
+			Widget p = work_area;
+			while (p && p != aw) {
+				stdarg_n = 0;
+				stdarg(XmNx, &wx);
+				stdarg(XmNy, &wy);
+				XtGetValues(p, stdarg_args, stdarg_n);
+				action_x -= wx;
+				action_y -= wy;
+				p = XtParent(p);
+			}
+			if (p == aw)
+				have_xy = 1;
+		}
+		/*pcb_printf("have xy from %s: %$mD\n", XtName(aw), action_x, action_y); */
+	}
+
+	lesstif_need_idle_proc();
+	hid_cfg_action(node);
+}
+
+static void note_accelerator(const lht_node_t *node)
+{
+	lht_node_t *anode, *knode;
+	assert(node != NULL);
+	anode = hid_cfg_menu_field(node, MF_ACTION, NULL);
+	knode = hid_cfg_menu_field(node, MF_ACCELERATOR, NULL);
+	if ((anode != NULL) && (knode != NULL))
+		hid_cfg_keys_add_by_desc(&lesstif_keymap, knode, anode, NULL, 0);
+	else
+		hid_cfg_error(node, "No action specified for key accel\n");
+}
+
+int lesstif_key_event(XKeyEvent * e)
+{
+	char buf[10];
+	KeySym sym;
+	int slen;
+	int mods = 0;
+	static hid_cfg_keyseq_t *seq[32];
+	static int seq_len = 0;
+
+	if (e->state & ShiftMask)
+		mods |= M_Shift;
+	if (e->state & ControlMask)
+		mods |= M_Ctrl;
+	if (e->state & Mod1Mask)
+		mods |= M_Alt;
+
+	e->state &= ~(ControlMask | Mod1Mask);
+
+	if (e->state & ShiftMask)
+		e->state &= ~ShiftMask;
+	slen = XLookupString(e, buf, sizeof(buf), &sym, NULL);
+
+	/* Ignore these.  */
+	switch (sym) {
+	case XK_Shift_L:
+	case XK_Shift_R:
+	case XK_Control_L:
+	case XK_Control_R:
+	case XK_Caps_Lock:
+	case XK_Shift_Lock:
+	case XK_Meta_L:
+	case XK_Meta_R:
+	case XK_Alt_L:
+	case XK_Alt_R:
+	case XK_Super_L:
+	case XK_Super_R:
+	case XK_Hyper_L:
+	case XK_Hyper_R:
+	case XK_ISO_Level3_Shift:
+		return 1;
+	}
+
+/* TODO#3: this works only on US keyboard */
+	if (mods & M_Shift) {
+		static const char *lower = "`1234567890-=[]\\;',./";
+		static const char *upper = "~!@#$%^&*()_+{}|:\"<>?";
+		char *l;
+		if ((sym >= 'A') && (sym <= 'Z'))
+			sym = tolower(sym);
+		else if ((l = strchr(lower, sym)) != NULL) {
+			sym = upper[l - lower];
+			mods &= ~M_Shift;
+		}
+	}
+
+/*	printf("KEY lookup: mod=%x sym=%x/%d\n", mods, sym, slen); */
+
+	slen = hid_cfg_keys_input(&lesstif_keymap, mods, sym, seq, &seq_len);
+	if (slen <= 0)
+		return 1;
+
+	if (e->window == XtWindow(work_area)) {
+		have_xy = 1;
+		action_x = e->x;
+		action_y = e->y;
+	}
+	else
+		have_xy = 0;
+
+	/* Parsing actions may not return until more user interaction
+	   happens.  */
+	hid_cfg_keys_action(seq, slen);
+
+	return 1;
+}
+
+static void add_node_to_menu(Widget menu, lht_node_t *node, XtCallbackProc callback, int level);
+
+static void add_res2menu_main(Widget menu, lht_node_t *node, XtCallbackProc callback)
+{
+	Widget sub, btn = NULL;
+	stdarg_n = 0;
+	stdarg(XmNtearOffModel, XmTEAR_OFF_ENABLED);
+	sub = XmCreatePulldownMenu(menu, node->name, stdarg_args, stdarg_n);
+	XtSetValues(sub, stdarg_args, stdarg_n);
+	stdarg_n = 0;
+	stdarg(XmNsubMenuId, sub);
+	btn = XmCreateCascadeButton(menu, node->name, stdarg_args, stdarg_n);
+	XtManageChild(btn);
+
+	node->user_data = sub;
+
+	if (hid_cfg_has_submenus(node)) {
+		lht_node_t *i;
+		i = hid_cfg_menu_field(node, MF_SUBMENU, NULL);
+		for(i = i->data.list.first; i != NULL; i = i->next)
+			add_node_to_menu(sub, i, callback, 1);
+	}
+}
+
+static void add_res2menu_named(Widget menu, lht_node_t *node, XtCallbackProc callback, int level)
+{
+	const char *v;
+	Widget sub, btn = NULL;
+	lht_node_t *act, *kacc;
+
+	stdarg_n = 0;
+	v = hid_cfg_menu_field_str(node, MF_FOREGROUND);
+	if (v != NULL)
+		stdarg_do_color(v, XmNforeground);
+
+	v = hid_cfg_menu_field_str(node, MF_BACKGROUND);
+	if (v != NULL)
+		stdarg_do_color(v, XmNbackground);
+
+	v = hid_cfg_menu_field_str(node, MF_FONT);
+	if (v != NULL) {
+		XFontStruct *fs = XLoadQueryFont(display, v);
+		if (fs) {
+			XmFontList fl = XmFontListCreate(fs, XmSTRING_DEFAULT_CHARSET);
+			stdarg(XmNfontList, fl);
+		}
+	}
+
+	v = hid_cfg_menu_field_str(node, MF_MNEMONIC);
+	if (v != NULL)
+		stdarg(XmNmnemonic, v);
+
+	kacc = hid_cfg_menu_field(node, MF_ACCELERATOR, NULL);
+	if (kacc != NULL) {
+		char *acc_str = hid_cfg_keys_gen_accel(&lesstif_keymap, kacc, 1, NULL);
+
+		if (acc_str != NULL) {
+			XmString as = XmStringCreatePCB(acc_str);
+			stdarg(XmNacceleratorText, as);
+		}
+
+#warning TODO: remove this call
+		note_accelerator(node);
+	}
+
+	v = node->name;
+	stdarg(XmNlabelString, XmStringCreatePCB(pcb_strdup(v)));
+
+	if (hid_cfg_has_submenus(node)) {
+		int nn = stdarg_n;
+		lht_node_t *i;
+		const char *field_name;
+		lht_node_t *submenu_node = hid_cfg_menu_field(node, MF_SUBMENU, &field_name);
+
+		stdarg(XmNtearOffModel, XmTEAR_OFF_ENABLED);
+		sub = XmCreatePulldownMenu(menu, pcb_strdup(v), stdarg_args + nn, stdarg_n - nn);
+		node->user_data = sub;
+		stdarg_n = nn;
+		stdarg(XmNsubMenuId, sub);
+		btn = XmCreateCascadeButton(menu, XmStrCast("menubutton"), stdarg_args, stdarg_n);
+		XtManageChild(btn);
+
+		/* assume submenu is a list, hid_cfg_has_submenus() already checked that */
+		for(i = submenu_node->data.list.first; i != NULL; i = i->next)
+			add_node_to_menu(sub, i, callback, level+1);
+	}
+	else {
+		/* doesn't have submenu */
+		const char *checked = hid_cfg_menu_field_str(node, MF_CHECKED);
+		const char *label = hid_cfg_menu_field_str(node, MF_SENSITIVE);
+#if 0
+/* Do not support radio for now: the gtk HID doesn't have it either */
+		Resource *radio = resource_subres(node->v[i].subres, "radio");
+		if (radio) {
+			ToggleItem *ti = (ToggleItem *) malloc(sizeof(ToggleItem));
+			ti->next = toggle_items;
+			ti->group = radio->v[0].value;
+			ti->item = radio->v[1].value;
+			ti->callback = callback;
+			ti->node = node->v[i].subres;
+			toggle_items = ti;
+
+			if (resource_value(node->v[i].subres, "set")) {
+				stdarg(XmNset, True);
+			}
+			stdarg(XmNindicatorType, XmONE_OF_MANY);
+			btn = XmCreateToggleButton(menu, "menubutton", args, n);
+			ti->w = btn;
+			XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) radio_callback, (XtPointer) ti);
+		}
+		else
+#endif
+		act = hid_cfg_menu_field(node, MF_ACTION, NULL);
+		if (checked) {
+			if (strchr(checked, '='))
+				stdarg(XmNindicatorType, XmONE_OF_MANY);
+			else
+				stdarg(XmNindicatorType, XmN_OF_MANY);
+			btn = XmCreateToggleButton(menu, XmStrCast("menubutton"), stdarg_args, stdarg_n);
+			if (act != NULL)
+				XtAddCallback(btn, XmNvalueChangedCallback, callback, (XtPointer) act);
+		}
+		else if (label && strcmp(label, "false") == 0) {
+			stdarg(XmNalignment, XmALIGNMENT_BEGINNING);
+			btn = XmCreateLabel(menu, XmStrCast("menulabel"), stdarg_args, stdarg_n);
+		}
+		else {
+			btn = XmCreatePushButton(menu, XmStrCast("menubutton"), stdarg_args, stdarg_n);
+			XtAddCallback(btn, XmNactivateCallback, callback, (XtPointer) act);
+		}
+
+		v = hid_cfg_menu_field_str(node, MF_CHECKED);
+		if (v != NULL)
+			note_widget_flag(btn, XmNset, v);
+
+		v = hid_cfg_menu_field_str(node, MF_ACTIVE);
+		if (v != NULL)
+			note_widget_flag(btn, XmNsensitive, v);
+
+		XtManageChild(btn);
+	}
+}
+
+static void add_res2menu_text_special(Widget menu, lht_node_t *node, XtCallbackProc callback)
+{
+#warning TODO: make this a flag hash, also in the gtk hid
+	Widget btn = NULL;
+	stdarg_n = 0;
+	if (*node->data.text.value == '@') {
+		if (strcmp(node->data.text.value, "@layerview") == 0)
+			insert_layerview_buttons(menu);
+		if (strcmp(node->data.text.value, "@layerpick") == 0)
+			insert_layerpick_buttons(menu);
+		if (strcmp(node->data.text.value, "@routestyles") == 0)
+			lesstif_insert_style_buttons(menu);
+	}
+	else if ((strcmp(node->data.text.value, "-") == 0) || (strcmp(node->data.text.value, "-"))) {
+		btn = XmCreateSeparator(menu, XmStrCast("sep"), stdarg_args, stdarg_n);
+		XtManageChild(btn);
+	}
+}
+
+static void add_node_to_menu(Widget in_menu, lht_node_t *node, XtCallbackProc callback, int level)
+{
+	if (level == 0) {
+		add_res2menu_main(in_menu, node, callback);
+		return;
+	}
+
+	switch(node->type) {
+		case LHT_HASH: add_res2menu_named(in_menu, node, callback, level); break;
+		case LHT_TEXT: add_res2menu_text_special(in_menu, node, callback); break;
+		default: /* ignore them */;
+	}
+}
+
+extern char *lesstif_pcbmenu_path;
+extern const char *lesstif_menu_default;
+
+
+Widget lesstif_menu(Widget parent, const char *name, Arg * margs, int mn)
+{
+	Widget mb = XmCreateMenuBar(parent, XmStrCast(name), margs, mn);
+	int screen;
+	lht_node_t *mr;
+
+	display = XtDisplay(mb);
+	screen = DefaultScreen(display);
+	cmap = DefaultColormap(display, screen);
+
+	lesstif_cfg = hid_cfg_load("lesstif", 0, lesstif_menu_default);
+	if (lesstif_cfg == NULL) {
+		Message(PCB_MSG_DEFAULT, "FATAL: can't load the lesstif menu res either from file or from hardwired default.");
+		abort();
+	}
+
+	mr = hid_cfg_get_menu(lesstif_cfg, "/main_menu");
+	if (mr != NULL) {
+		if (mr->type == LHT_LIST) {
+			lht_node_t *n;
+			for(n = mr->data.list.first; n != NULL; n = n->next)
+				add_node_to_menu(mb, n, (XtCallbackProc) callback, 0);
+		}
+		else
+			hid_cfg_error(mr, "/main_menu should be a list");
+	}
+
+
+	hid_cfg_mouse_init(lesstif_cfg, &lesstif_mouse);
+
+	return mb;
+}
+
+static int lesstif_create_menu_widget(void *ctx, const char *path, const char *name, int is_main, lht_node_t *parent, lht_node_t *menu_item)
+{
+	Widget w = (is_main) ? lesstif_menubar : parent->user_data;
+
+	if (strncmp(path, "/popups", 7) == 0)
+		return -1; /* there's no popup support in lesstif */
+
+	add_node_to_menu(w, menu_item, (XtCallbackProc) callback, is_main ? 0 : 2);
+
+	return 0;
+}
+
+
+void lesstif_create_menu(const char *menu_path, const char *action, const char *mnemonic, const char *accel, const char *tip, const char *cookie)
+{
+	hid_cfg_create_menu(lesstif_cfg, menu_path, action, mnemonic, accel, tip, cookie, lesstif_create_menu_widget, NULL);
+}
+
+
+void lesstif_uninit_menu(void)
+{
+	XtDestroyWidget(lesstif_menubar);
+}
diff --git a/src_plugins/hid_lesstif/netlist.c b/src_plugins/hid_lesstif/netlist.c
new file mode 100644
index 0000000..daa9dc9
--- /dev/null
+++ b/src_plugins/hid_lesstif/netlist.c
@@ -0,0 +1,441 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xincludes.h"
+
+#include "compat_misc.h"
+#include "global.h"
+#include "data.h"
+
+#include "find.h"
+#include "rats.h"
+#include "select.h"
+#include "undo.h"
+#include "remove.h"
+#include "crosshair.h"
+#include "draw.h"
+
+#include "hid.h"
+#include "hid_actions.h"
+#include "lesstif.h"
+#include "stdarg.h"
+
+static Widget netlist_dialog = 0;
+static Widget netlist_list, netnode_list;
+
+static XmString *netlist_strings = 0;
+static XmString *netnode_strings = 0;
+static int n_netnode_strings;
+static int last_pick = -1;
+
+static int LesstifNetlistChanged(int argc, const char **argv, Coord x, Coord y);
+
+static void pick_net(int pick)
+{
+	LibraryMenuType *menu = PCB->NetlistLib[NETLIST_EDITED].Menu + pick;
+	int i;
+
+	if (pick == last_pick)
+		return;
+	last_pick = pick;
+
+	if (netnode_strings)
+		free(netnode_strings);			/* XXX leaked all XmStrings??? */
+	n_netnode_strings = menu->EntryN;
+	netnode_strings = (XmString *) malloc(menu->EntryN * sizeof(XmString));
+	for (i = 0; i < menu->EntryN; i++)
+		netnode_strings[i] = XmStringCreatePCB(menu->Entry[i].ListEntry);
+	stdarg_n = 0;
+	stdarg(XmNitems, netnode_strings);
+	stdarg(XmNitemCount, menu->EntryN);
+	XtSetValues(netnode_list, stdarg_args, stdarg_n);
+}
+
+static void netlist_select(Widget w, void *v, XmListCallbackStruct * cbs)
+{
+	XmString str;
+	int pos = cbs->item_position;
+	LibraryMenuTypePtr net = &(PCB->NetlistLib[NETLIST_EDITED].Menu[pos - 1]);
+	char *name = net->Name;
+	if (name[0] == ' ') {
+		name[0] = '*';
+		net->flag = 0;
+	}
+	else {
+		name[0] = ' ';
+		net->flag = 1;
+	}
+
+	str = XmStringCreatePCB(name);
+	XmListReplaceItemsPos(netlist_list, &str, 1, pos);
+	XmStringFree(str);
+	XmListSelectPos(netlist_list, pos, False);
+}
+
+static void netlist_extend(Widget w, void *v, XmListCallbackStruct * cbs)
+{
+	if (cbs->selected_item_count == 1)
+		pick_net(cbs->item_position - 1);
+}
+
+typedef void (*Std_Nbcb_Func) (LibraryMenuTypePtr, int);
+
+static void nbcb_rat_on(LibraryMenuTypePtr net, int pos)
+{
+	XmString str;
+	char *name = net->Name;
+	name[0] = ' ';
+	net->flag = 1;
+	str = XmStringCreatePCB(name);
+	XmListReplaceItemsPos(netlist_list, &str, 1, pos);
+	XmStringFree(str);
+}
+
+static void nbcb_rat_off(LibraryMenuTypePtr net, int pos)
+{
+	XmString str;
+	char *name = net->Name;
+	name[0] = '*';
+	net->flag = 0;
+	str = XmStringCreatePCB(name);
+	XmListReplaceItemsPos(netlist_list, &str, 1, pos);
+	XmStringFree(str);
+}
+
+
+/* Select on the layout the current net treeview selection
+ */
+static void nbcb_select_common(LibraryMenuTypePtr net, int pos, int select_flag)
+{
+	LibraryEntryType *entry;
+	ConnectionType conn;
+	int i;
+
+	InitConnectionLookup();
+	ResetConnections(pcb_true);
+
+	for (i = net->EntryN, entry = net->Entry; i; i--, entry++)
+		if (SeekPad(entry, &conn, pcb_false))
+			RatFindHook(conn.type, conn.ptr1, conn.ptr2, conn.ptr2, pcb_true, pcb_true);
+
+	SelectConnection(select_flag);
+	ResetConnections(pcb_false);
+	FreeConnectionLookupMemory();
+	IncrementUndoSerialNumber();
+	Draw();
+}
+
+static void nbcb_select(LibraryMenuTypePtr net, int pos)
+{
+	nbcb_select_common(net, pos, 1);
+}
+
+static void nbcb_deselect(LibraryMenuTypePtr net, int pos)
+{
+	nbcb_select_common(net, pos, 0);
+}
+
+static void nbcb_find(LibraryMenuTypePtr net, int pos)
+{
+	char *name = net->Name + 2;
+	hid_actionl("netlist", "find", name, NULL);
+}
+
+static void nbcb_std_callback(Widget w, Std_Nbcb_Func v, XmPushButtonCallbackStruct * cbs)
+{
+	int *posl, posc, i;
+	XmString **items, **selected;
+	if (XmListGetSelectedPos(netlist_list, &posl, &posc) == False)
+		return;
+	if (v == nbcb_find)
+		hid_actionl("connection", "reset", NULL);
+	for (i = 0; i < posc; i++) {
+		LibraryMenuTypePtr net = &(PCB->NetlistLib[NETLIST_EDITED].Menu[posl[i] - 1]);
+		v(net, posl[i]);
+	}
+	stdarg_n = 0;
+	stdarg(XmNitems, &items);
+	XtGetValues(netlist_list, stdarg_args, stdarg_n);
+	selected = (XmString **) malloc(posc * sizeof(XmString *));
+	for (i = 0; i < posc; i++)
+		selected[i] = items[posl[i] - 1];
+
+	stdarg_n = 0;
+	stdarg(XmNselectedItems, selected);
+	XtSetValues(netlist_list, stdarg_args, stdarg_n);
+}
+
+static void nbcb_ripup(Widget w, Std_Nbcb_Func v, XmPushButtonCallbackStruct * cbs)
+{
+	nbcb_std_callback(w, nbcb_find, cbs);
+
+	VISIBLELINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, line) && !TEST_FLAG(PCB_FLAG_LOCK, line))
+			RemoveObject(PCB_TYPE_LINE, layer, line, line);
+	}
+	ENDALL_LOOP;
+
+	VISIBLEARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, arc) && !TEST_FLAG(PCB_FLAG_LOCK, arc))
+			RemoveObject(PCB_TYPE_ARC, layer, arc, arc);
+	}
+	ENDALL_LOOP;
+
+	if (PCB->ViaOn)
+		VIA_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, via) && !TEST_FLAG(PCB_FLAG_LOCK, via))
+			RemoveObject(PCB_TYPE_VIA, via, via, via);
+	}
+	END_LOOP;
+}
+
+static void netnode_browse(Widget w, XtPointer v, XmListCallbackStruct * cbs)
+{
+	LibraryMenuType *menu = PCB->NetlistLib[NETLIST_EDITED].Menu + last_pick;
+	const char *name = menu->Entry[cbs->item_position - 1].ListEntry;
+	char *ename, *pname;
+
+	ename = pcb_strdup(name);
+	pname = strchr(ename, '-');
+	if (!pname) {
+		free(ename);
+		return;
+	}
+	*pname++ = 0;
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		char *es = element->Name[NAMEONPCB_INDEX].TextString;
+		if (es && strcmp(es, ename) == 0) {
+			PIN_LOOP(element);
+			{
+				if (strcmp(pin->Number, pname) == 0) {
+					MoveCrosshairAbsolute(pin->X, pin->Y);
+					free(ename);
+					return;
+				}
+			}
+			END_LOOP;
+			PAD_LOOP(element);
+			{
+				if (strcmp(pad->Number, pname) == 0) {
+					int x = (pad->Point1.X + pad->Point2.X) / 2;
+					int y = (pad->Point1.Y + pad->Point2.Y) / 2;
+					gui->set_crosshair(x, y, HID_SC_PAN_VIEWPORT);
+					free(ename);
+					return;
+				}
+			}
+			END_LOOP;
+		}
+	}
+	END_LOOP;
+	free(ename);
+}
+
+#define NLB_FORM ((Widget)(~0))
+static Widget
+netlist_button(Widget parent, const char *name, const char *string,
+							 Widget top, Widget bottom, Widget left, Widget right, XtCallbackProc callback, void *user_data)
+{
+	Widget rv;
+	XmString str;
+
+#define NLB_W(w) if (w == NLB_FORM) { stdarg(XmN ## w ## Attachment, XmATTACH_FORM); } \
+  else if (w) { stdarg(XmN ## w ## Attachment, XmATTACH_WIDGET); \
+    stdarg (XmN ## w ## Widget, w); }
+
+	NLB_W(top);
+	NLB_W(bottom);
+	NLB_W(left);
+	NLB_W(right);
+	str = XmStringCreatePCB(string);
+	stdarg(XmNlabelString, str);
+	rv = XmCreatePushButton(parent, XmStrCast(name), stdarg_args, stdarg_n);
+	XtManageChild(rv);
+	if (callback)
+		XtAddCallback(rv, XmNactivateCallback, callback, (XtPointer) user_data);
+	XmStringFree(str);
+	return rv;
+}
+
+static int build_netlist_dialog()
+{
+	Widget b_sel, b_unsel, b_find, /*b_ripup,*/ b_rat_on, /*b_rat_off,*/ l_ops;
+	XmString ops_str;
+
+	if (!mainwind)
+		return 1;
+	if (netlist_dialog)
+		return 0;
+
+	stdarg_n = 0;
+	stdarg(XmNresizePolicy, XmRESIZE_GROW);
+	stdarg(XmNtitle, "Netlists");
+	stdarg(XmNautoUnmanage, False);
+	netlist_dialog = XmCreateFormDialog(mainwind, XmStrCast("netlist"), stdarg_args, stdarg_n);
+
+	stdarg_n = 0;
+	b_rat_on = netlist_button(netlist_dialog, "rat_on", "Enable for rats",
+														0, NLB_FORM, NLB_FORM, 0, (XtCallbackProc) nbcb_std_callback, (void *) nbcb_rat_on);
+
+	stdarg_n = 0;
+	/*b_rat_off =*/ netlist_button(netlist_dialog, "rat_off", "Disable for rats",
+														 0, NLB_FORM, b_rat_on, 0, (XtCallbackProc) nbcb_std_callback, (void *) nbcb_rat_off);
+
+	stdarg_n = 0;
+	b_sel = netlist_button(netlist_dialog, "select", "Select",
+												 0, b_rat_on, NLB_FORM, 0, (XtCallbackProc) nbcb_std_callback, (void *) nbcb_select);
+
+	stdarg_n = 0;
+	b_unsel = netlist_button(netlist_dialog, "deselect", "Deselect",
+													 0, b_rat_on, b_sel, 0, (XtCallbackProc) nbcb_std_callback, (void *) nbcb_deselect);
+
+	stdarg_n = 0;
+	b_find = netlist_button(netlist_dialog, "find", "Find",
+													0, b_rat_on, b_unsel, 0, (XtCallbackProc) nbcb_std_callback, (void *) nbcb_find);
+
+
+	stdarg_n = 0;
+	/*b_ripup =*/ netlist_button(netlist_dialog, "ripup", "Rip Up", 0, b_rat_on, b_find, 0, (XtCallbackProc) nbcb_ripup, 0);
+
+	stdarg_n = 0;
+	stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+	stdarg(XmNbottomWidget, b_sel);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	ops_str = XmStringCreatePCB("Operations on selected net names:");
+	stdarg(XmNlabelString, ops_str);
+	l_ops = XmCreateLabel(netlist_dialog, XmStrCast("ops"), stdarg_args, stdarg_n);
+	XtManageChild(l_ops);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+	stdarg(XmNbottomWidget, l_ops);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_POSITION);
+	stdarg(XmNrightPosition, 50);
+	stdarg(XmNvisibleItemCount, 10);
+	stdarg(XmNselectionPolicy, XmEXTENDED_SELECT);
+	netlist_list = XmCreateScrolledList(netlist_dialog, XmStrCast("nets"), stdarg_args, stdarg_n);
+	XtManageChild(netlist_list);
+	XtAddCallback(netlist_list, XmNdefaultActionCallback, (XtCallbackProc) netlist_select, 0);
+	XtAddCallback(netlist_list, XmNextendedSelectionCallback, (XtCallbackProc) netlist_extend, 0);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_FORM);
+	stdarg(XmNbottomAttachment, XmATTACH_WIDGET);
+	stdarg(XmNbottomWidget, l_ops);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_POSITION);
+	stdarg(XmNleftPosition, 50);
+	netnode_list = XmCreateScrolledList(netlist_dialog, XmStrCast("nodes"), stdarg_args, stdarg_n);
+	XtManageChild(netnode_list);
+	XtAddCallback(netnode_list, XmNbrowseSelectionCallback, (XtCallbackProc) netnode_browse, 0);
+
+	return 0;
+}
+
+static int LesstifNetlistChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	int i;
+	if (!PCB->NetlistLib[NETLIST_EDITED].MenuN)
+		return 0;
+	if (build_netlist_dialog())
+		return 0;
+	last_pick = -1;
+	if (netlist_strings)
+		free(netlist_strings);
+	netlist_strings = (XmString *) malloc(PCB->NetlistLib[NETLIST_EDITED].MenuN * sizeof(XmString));
+	for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++)
+		netlist_strings[i] = XmStringCreatePCB(PCB->NetlistLib[NETLIST_EDITED].Menu[i].Name);
+	stdarg_n = 0;
+	stdarg(XmNitems, netlist_strings);
+	stdarg(XmNitemCount, PCB->NetlistLib[NETLIST_EDITED].MenuN);
+	XtSetValues(netlist_list, stdarg_args, stdarg_n);
+	pick_net(0);
+	return 0;
+}
+
+static const char netlistshow_syntax[] = "NetlistShow(pinname|netname)";
+
+static const char netlistshow_help[] = "Selects the given pinname or netname in the netlist window.";
+
+/* %start-doc actions NetlistShow
+
+%end-doc */
+
+static int LesstifNetlistShow(int argc, const char **argv, Coord x, Coord y)
+{
+	if (build_netlist_dialog())
+		return 0;
+
+	if (argc == 1) {
+		LibraryMenuTypePtr net;
+
+		net = pcb_netnode_to_netname(argv[0]);
+		if (net) {
+			XmString item;
+			int vis = 0;
+
+			/* Select net first, 'True' causes pick_net() to be invoked */
+			item = XmStringCreatePCB(net->Name);
+			XmListSelectItem(netlist_list, item, True);
+			XmListSetItem(netlist_list, item);
+			XmStringFree(item);
+
+			/* Now the netnode_list has the right contents */
+			item = XmStringCreatePCB(argv[0]);
+			XmListSelectItem(netnode_list, item, False);
+
+			/*
+			 * Only force the item to the top if there are enough to scroll.
+			 * A bug (?) in lesstif will cause the window to get ever wider
+			 * if an XmList that doesn't require a scrollbar is forced to
+			 * have one (when the top item is not the first item).
+			 */
+			stdarg_n = 0;
+			stdarg(XmNvisibleItemCount, &vis);
+			XtGetValues(netnode_list, stdarg_args, stdarg_n);
+			if (n_netnode_strings > vis) {
+				XmListSetItem(netnode_list, item);
+			}
+			XmStringFree(item);
+		}
+		else {
+			/* Try the argument as a netname */
+			net = pcb_netname_to_netname(argv[0]);
+			if (net) {
+				XmString item;
+
+				item = XmStringCreatePCB(net->Name);
+				XmListSetItem(netlist_list, item);
+				XmListSelectItem(netlist_list, item, True);
+				XmStringFree(item);
+			}
+		}
+	}
+	return 0;
+}
+
+void lesstif_show_netlist()
+{
+	build_netlist_dialog();
+	XtManageChild(netlist_dialog);
+}
+
+HID_Action lesstif_netlist_action_list[] = {
+	{"NetlistChanged", 0, LesstifNetlistChanged,
+	 netlistchanged_help, netlistchanged_syntax},
+	{"NetlistShow", 0, LesstifNetlistShow,
+	 netlistshow_help, netlistshow_syntax}
+};
+
+REGISTER_ACTIONS(lesstif_netlist_action_list, lesstif_cookie)
diff --git a/src_plugins/hid_lesstif/stdarg.c b/src_plugins/hid_lesstif/stdarg.c
new file mode 100644
index 0000000..9b18242
--- /dev/null
+++ b/src_plugins/hid_lesstif/stdarg.c
@@ -0,0 +1,15 @@
+#include "stdarg.h"
+Arg stdarg_args[30];
+int stdarg_n;
+
+extern Colormap lesstif_colormap;
+extern Display *lesstif_display;
+
+void stdarg_do_color(const char *value, char *which)
+{
+	XColor color;
+	if (XParseColor(lesstif_display, lesstif_colormap, value, &color))
+		if (XAllocColor(lesstif_display, lesstif_colormap, &color)) {
+			stdarg(which, color.pixel);
+		}
+}
diff --git a/src_plugins/hid_lesstif/stdarg.h b/src_plugins/hid_lesstif/stdarg.h
new file mode 100644
index 0000000..70a2444
--- /dev/null
+++ b/src_plugins/hid_lesstif/stdarg.h
@@ -0,0 +1,10 @@
+#include "xincludes.h"
+
+extern Colormap lesstif_colormap;
+
+extern Arg stdarg_args[];
+extern int stdarg_n;
+#define stdarg(t,v) XtSetArg(stdarg_args[stdarg_n], t, v), stdarg_n++
+
+void stdarg_do_color(const char *value, char *which);
+
diff --git a/src_plugins/hid_lesstif/styles.c b/src_plugins/hid_lesstif/styles.c
new file mode 100644
index 0000000..e46a030
--- /dev/null
+++ b/src_plugins/hid_lesstif/styles.c
@@ -0,0 +1,440 @@
+#include "xincludes.h"
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat_misc.h"
+#include "global.h"
+#include "data.h"
+#include "set.h"
+#include "misc.h"
+#include "mymem.h"
+#include "pcb-printf.h"
+
+#include "hid.h"
+#include "lesstif.h"
+#include "hid_flags.h"
+#include "stdarg.h"
+#include "misc_util.h"
+
+/* There are three places where styles are kept:
+
+   First, the "active" style is in conf_core.design.line_thickness et al.
+
+   Second, there are NUM_STYLES styles in PCB->RouteStyle[].
+
+   (not anymore: Third, there are NUM_STYLES styles in conf_core.design.RouteStyle[])
+
+   Selecting a style copies its values to the active style.  We also
+   need a way to modify the active style, copy the active style to
+   PCB->RouteStyle[], and copy PCB->RouteStyle[] to
+   Settings.RouteStyle[].  Since Lesstif reads the default style from
+   .Xdefaults, we can ignore Settings.RouteStyle[] in general, as it's
+   only used to initialize new PCB files.
+
+   So, we need to do PCB->RouteStyle <-> active style.
+*/
+
+typedef enum {
+	SSthick, SSdiam, SShole, SSkeep,
+	SSNUM
+} StyleValues;
+
+static Widget style_dialog = 0;
+static Widget style_values[SSNUM];
+
+/* dynamic arrays for styles */
+static int alloced_styles = 0;
+static Widget *style_pb;
+static Widget *units_pb;
+static int *name_hashes;
+typedef struct {
+	Widget *w;
+} StyleButtons;
+
+static StyleButtons *style_button_list = NULL;
+static int num_style_buttons = 0; /* number of style_button_list instances (depends on how many times it's placed in the menu) */
+/**/
+
+static Widget value_form, value_labels, value_texts, units_form;
+static int local_update = 0;
+XmString xms_mm, xms_mil;
+
+static const Unit *unit = 0;
+static XmString ustr;
+
+static int hash(char *cp)
+{
+	int h = 0;
+	while (*cp) {
+		h = h * 13 + *(unsigned char *) cp;
+		h ^= (h >> 16);
+		cp++;
+	}
+	return h;
+}
+
+static const char *value_names[] = {
+	"Thickness", "Diameter", "Hole", "Clearance"
+};
+
+static int RouteStylesChanged(int argc, const char **argv, Coord x, Coord y);
+
+static void update_one_value(int i, Coord v)
+{
+	char buf[100];
+
+	pcb_snprintf(buf, sizeof(buf), "%m+%.2mS", unit->allow, v);
+	XmTextSetString(style_values[i], buf);
+	stdarg_n = 0;
+	stdarg(XmNlabelString, ustr);
+	XtSetValues(units_pb[i], stdarg_args, stdarg_n);
+}
+
+static void update_values()
+{
+	local_update = 1;
+	update_one_value(SSthick, conf_core.design.line_thickness);
+	update_one_value(SSdiam, conf_core.design.via_thickness);
+	update_one_value(SShole, conf_core.design.via_drilling_hole);
+	update_one_value(SSkeep, conf_core.design.clearance);
+	local_update = 0;
+	lesstif_update_status_line();
+}
+
+void lesstif_styles_update_values()
+{
+	if (!style_dialog) {
+		lesstif_update_status_line();
+		return;
+	}
+	unit = conf_core.editor.grid_unit;
+	ustr = XmStringCreateLocalized((char *) conf_core.editor.grid_unit->suffix);
+	update_values();
+}
+
+static void update_style_buttons()
+{
+	int i = hid_get_flag("GetStyle()");
+	int j, n;
+
+	for (n = 0; n < num_style_buttons; n++) {
+		for (j = 0; j < vtroutestyle_len(&PCB->RouteStyle); j++)
+			if (j != i)
+				XmToggleButtonSetState(style_button_list[n].w[j], 0, 0);
+			else
+				XmToggleButtonSetState(style_button_list[n].w[j], 1, 0);
+	}
+	if (style_dialog) {
+		for (j = 0; j < vtroutestyle_len(&PCB->RouteStyle); j++)
+			if (j != i)
+				XmToggleButtonSetState(style_pb[j], 0, 0);
+			else
+				XmToggleButtonSetState(style_pb[j], 1, 0);
+	}
+}
+
+static void style_value_cb(Widget w, int i, void *cbs)
+{
+	char *s;
+
+	if (local_update)
+		return;
+	s = XmTextGetString(w);
+	GetValueEx(s, NULL, NULL, NULL, unit->suffix, NULL);
+	switch (i) {
+	case SSthick:
+		conf_setf(CFR_DESIGN, "design/line_thickness", -1, "%s %s", s, unit->suffix);
+		break;
+	case SSdiam:
+		conf_setf(CFR_DESIGN, "design/via_thickness", -1, "%s %s", s, unit->suffix);
+		break;
+	case SShole:
+		conf_setf(CFR_DESIGN, "design/via_drilling_hole", -1, "%s %s", s, unit->suffix);
+		break;
+	case SSkeep:
+		conf_setf(CFR_DESIGN, "design/clearance", -1, "%s %s", s, unit->suffix);
+		break;
+	}
+	update_style_buttons();
+}
+
+static void units_cb()
+{
+	if (unit == get_unit_struct("mm"))
+		unit = get_unit_struct("mil");
+	else
+		unit = get_unit_struct("mm");
+	ustr = XmStringCreateLocalized((char *) unit->suffix);
+	update_values();
+}
+
+static Widget style_value(int i)
+{
+	Widget w, l;
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_POSITION);
+	stdarg(XmNtopPosition, i);
+	stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+	stdarg(XmNbottomPosition, i + 1);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNalignment, XmALIGNMENT_END);
+	l = XmCreateLabel(value_labels, XmStrCast(value_names[i]), stdarg_args, stdarg_n);
+	XtManageChild(l);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_POSITION);
+	stdarg(XmNtopPosition, i);
+	stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+	stdarg(XmNbottomPosition, i + 1);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNcolumns, 8);
+	w = XmCreateTextField(value_texts, XmStrCast(value_names[i]), stdarg_args, stdarg_n);
+	XtAddCallback(w, XmNvalueChangedCallback, (XtCallbackProc) style_value_cb, (XtPointer) (size_t) i);
+	XtManageChild(w);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_POSITION);
+	stdarg(XmNtopPosition, i);
+	stdarg(XmNbottomAttachment, XmATTACH_POSITION);
+	stdarg(XmNbottomPosition, i + 1);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNlabelString, ustr);
+	units_pb[i] = XmCreatePushButton(units_form, XmStrCast(value_names[i]), stdarg_args, stdarg_n);
+	XtAddCallback(units_pb[i], XmNactivateCallback, (XtCallbackProc) units_cb, (XtPointer) (size_t) i);
+	XtManageChild(units_pb[i]);
+
+	return w;
+}
+
+static void style_name_cb(Widget w, int i, XmToggleButtonCallbackStruct * cbs)
+{
+	char *newname = lesstif_prompt_for("New name", PCB->RouteStyle.array[i].name);
+	strncpy(PCB->RouteStyle.array[i].name, newname, sizeof(PCB->RouteStyle.array[i].name)-1);
+	PCB->RouteStyle.array[i].name[sizeof(PCB->RouteStyle.array[i].name)-1] = '\0';
+	free(newname);
+
+	RouteStylesChanged(0, 0, 0, 0);
+}
+
+static void style_set_cb(Widget w, int i, XmToggleButtonCallbackStruct * cbs)
+{
+	PCB->RouteStyle.array[i].Thick = conf_core.design.line_thickness;
+	PCB->RouteStyle.array[i].Diameter = conf_core.design.via_thickness;
+	PCB->RouteStyle.array[i].Hole = conf_core.design.via_drilling_hole;
+	PCB->RouteStyle.array[i].Clearance = conf_core.design.clearance;
+	update_style_buttons();
+}
+
+static void style_selected(Widget w, int i, XmToggleButtonCallbackStruct * cbs)
+{
+	RouteStyleType *style;
+	int j, n;
+	if (cbs && cbs->set == 0) {
+		XmToggleButtonSetState(w, 1, 0);
+		return;
+	}
+	style = PCB->RouteStyle.array + i;
+	SetLineSize(style->Thick);
+	SetViaSize(style->Diameter, pcb_true);
+	SetViaDrillingHole(style->Hole, pcb_true);
+	SetClearanceWidth(style->Clearance);
+	if (style_dialog) {
+		for (j = 0; j < vtroutestyle_len(&PCB->RouteStyle); j++)
+			if (j != i)
+				XmToggleButtonSetState(style_pb[j], 0, 0);
+			else
+				XmToggleButtonSetState(style_pb[j], 1, 0);
+		update_values();
+	}
+	else
+		lesstif_update_status_line();
+	for (n = 0; n < num_style_buttons; n++) {
+		for (j = 0; j < vtroutestyle_len(&PCB->RouteStyle); j++)
+			if (j != i)
+				XmToggleButtonSetState(style_button_list[n].w[j], 0, 0);
+			else
+				XmToggleButtonSetState(style_button_list[n].w[j], 1, 0);
+	}
+}
+
+static Widget style_button(int i)
+{
+	Widget pb, set;
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_WIDGET);
+	stdarg(XmNtopWidget, i ? style_pb[i - 1] : value_form);
+	stdarg(XmNleftAttachment, XmATTACH_FORM);
+	stdarg(XmNlabelString, XmStringCreatePCB("Name"));
+	set = XmCreatePushButton(style_dialog, XmStrCast("style"), stdarg_args, stdarg_n);
+	XtManageChild(set);
+	XtAddCallback(set, XmNactivateCallback, (XtCallbackProc) style_name_cb, (XtPointer) (size_t) i);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_WIDGET);
+	stdarg(XmNtopWidget, i ? style_pb[i - 1] : value_form);
+	stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+	stdarg(XmNleftWidget, set);
+	stdarg(XmNlabelString, XmStringCreatePCB("Set"));
+	set = XmCreatePushButton(style_dialog, XmStrCast("style"), stdarg_args, stdarg_n);
+	XtManageChild(set);
+	XtAddCallback(set, XmNactivateCallback, (XtCallbackProc) style_set_cb, (XtPointer) (size_t) i);
+
+	stdarg_n = 0;
+	stdarg(XmNtopAttachment, XmATTACH_WIDGET);
+	stdarg(XmNtopWidget, i ? style_pb[i - 1] : value_form);
+	stdarg(XmNrightAttachment, XmATTACH_FORM);
+	stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+	stdarg(XmNleftWidget, set);
+	stdarg(XmNlabelString, XmStringCreatePCB(PCB->RouteStyle.array[i].name));
+	stdarg(XmNindicatorType, XmONE_OF_MANY);
+	stdarg(XmNalignment, XmALIGNMENT_BEGINNING);
+	pb = XmCreateToggleButton(style_dialog, XmStrCast("style"), stdarg_args, stdarg_n);
+	XtManageChild(pb);
+	XtAddCallback(pb, XmNvalueChangedCallback, (XtCallbackProc) style_selected, (XtPointer) (size_t) i);
+	return pb;
+}
+
+static const char adjuststyle_syntax[] = "AdjustStyle()";
+
+static const char adjuststyle_help[] = "Displays the route style adjustment window.";
+
+/* %start-doc actions AdjustStyle
+
+%end-doc */
+
+static int AdjustStyle(int argc, const char **argv, Coord x, Coord y)
+{
+	if ((!mainwind) || (PCB->RouteStyle.array == NULL))
+		return 1;
+	if (style_dialog == 0) {
+		int i;
+
+		unit = conf_core.editor.grid_unit;
+		ustr = XmStringCreateLocalized((char *) unit->suffix);
+
+		stdarg_n = 0;
+		stdarg(XmNautoUnmanage, False);
+		stdarg(XmNtitle, "Route Styles");
+		style_dialog = XmCreateFormDialog(mainwind, XmStrCast("style"), stdarg_args, stdarg_n);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		value_form = XmCreateForm(style_dialog, XmStrCast("values"), stdarg_args, stdarg_n);
+		XtManageChild(value_form);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		stdarg(XmNfractionBase, 4);
+		stdarg(XmNresizePolicy, XmRESIZE_GROW);
+		units_form = XmCreateForm(value_form, XmStrCast("units"), stdarg_args, stdarg_n);
+		XtManageChild(units_form);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		stdarg(XmNleftAttachment, XmATTACH_FORM);
+		stdarg(XmNfractionBase, 4);
+		value_labels = XmCreateForm(value_form, XmStrCast("values"), stdarg_args, stdarg_n);
+		XtManageChild(value_labels);
+
+		stdarg_n = 0;
+		stdarg(XmNtopAttachment, XmATTACH_FORM);
+		stdarg(XmNbottomAttachment, XmATTACH_FORM);
+		stdarg(XmNrightAttachment, XmATTACH_WIDGET);
+		stdarg(XmNrightWidget, units_form);
+		stdarg(XmNleftAttachment, XmATTACH_WIDGET);
+		stdarg(XmNleftWidget, value_labels);
+		stdarg(XmNfractionBase, 4);
+		value_texts = XmCreateForm(value_form, XmStrCast("values"), stdarg_args, stdarg_n);
+		XtManageChild(value_texts);
+
+		for (i = 0; i < SSNUM; i++) {
+			style_values[i] = style_value(i);
+			name_hashes[i] = hash(PCB->RouteStyle.array[i].name);
+		}
+		for (i = 0; i < vtroutestyle_len(&PCB->RouteStyle); i++)
+			style_pb[i] = style_button(i);
+		update_values();
+		update_style_buttons();
+	}
+	XtManageChild(style_dialog);
+	return 0;
+}
+
+static int RouteStylesChanged(int argc, const char **argv, Coord x, Coord y)
+{
+	int i, j, h;
+	if (!PCB || vtroutestyle_len(&PCB->RouteStyle) == 0)
+		return 0;
+	update_style_buttons();
+	if (!style_dialog)
+		return 0;
+	for (j = 0; j < vtroutestyle_len(&PCB->RouteStyle); j++) {
+		h = hash(PCB->RouteStyle.array[j].name);
+		if (name_hashes[j] == h)
+			continue;
+		name_hashes[j] = h;
+		stdarg_n = 0;
+		stdarg(XmNlabelString, XmStringCreatePCB(PCB->RouteStyle.array[j].name));
+		if (style_dialog)
+			XtSetValues(style_pb[j], stdarg_args, stdarg_n);
+		for (i = 0; i < num_style_buttons; i++)
+			XtSetValues(style_button_list[i].w[j], stdarg_args, stdarg_n);
+	}
+	update_values();
+	return 0;
+}
+
+void lesstif_insert_style_buttons(Widget menu)
+{
+	StyleButtons *sb;
+	int s, i;
+
+	num_style_buttons++;
+	s = num_style_buttons * sizeof(StyleButtons);
+	style_button_list = (StyleButtons *) realloc(style_button_list, s);
+	sb = style_button_list + num_style_buttons - 1;
+	sb->w = NULL;
+
+	alloced_styles = vtroutestyle_len(&PCB->RouteStyle);
+	style_pb = realloc(style_pb, sizeof(style_pb[0]) * alloced_styles);
+	units_pb = realloc(units_pb, sizeof(units_pb[0]) * alloced_styles);
+	name_hashes = realloc(name_hashes, sizeof(name_hashes[0]) * alloced_styles);
+	sb->w = realloc(sb->w, sizeof(sb->w[0]) * alloced_styles);
+
+	for (i = 0; i < vtroutestyle_len(&PCB->RouteStyle); i++) {
+		Widget btn;
+		stdarg_n = 0;
+		stdarg(XmNindicatorType, XmONE_OF_MANY);
+		stdarg(XmNlabelString, XmStringCreatePCB(PCB->RouteStyle.array[i].name));
+		btn = XmCreateToggleButton(menu, XmStrCast("style"), stdarg_args, stdarg_n);
+		XtManageChild(btn);
+		XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) style_selected, (XtPointer) (size_t) i);
+		sb->w[i] = btn;
+	}
+	update_style_buttons();
+}
+
+HID_Action lesstif_styles_action_list[] = {
+	{"AdjustStyle", 0, AdjustStyle,
+	 adjuststyle_help, adjuststyle_syntax}
+	,
+	{"RouteStylesChanged", 0, RouteStylesChanged,
+	 routestyleschanged_help, routestyleschanged_syntax}
+};
+
+REGISTER_ACTIONS(lesstif_styles_action_list, lesstif_cookie)
diff --git a/src_plugins/hid_lesstif/xincludes.h b/src_plugins/hid_lesstif/xincludes.h
new file mode 100644
index 0000000..dbed5f4
--- /dev/null
+++ b/src_plugins/hid_lesstif/xincludes.h
@@ -0,0 +1,46 @@
+/*
+ * Some of the X headers are not very friendly in terms of namespace.
+ * For example, X.h typedef's Mask but we use Mask in the core of pcb
+ * and this causes problem.  To avoid this, pull in the X headers in
+ * this file where we can add workarounds as needed.
+ */
+
+#define Mask X_Mask
+
+#include <X11/Intrinsic.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+
+#include <Xm/CascadeB.h>
+#include <Xm/DrawingA.h>
+#include <Xm/FileSB.h>
+#include <Xm/Form.h>
+#include <Xm/Frame.h>
+#include <Xm/Label.h>
+#include <Xm/List.h>
+#include <Xm/MainW.h>
+#include <Xm/MenuShell.h>
+#include <Xm/MessageB.h>
+#include <Xm/Protocols.h>
+#include <Xm/PushB.h>
+#include <Xm/RowColumn.h>
+#include <Xm/Scale.h>
+#include <Xm/ScrollBar.h>
+#include <Xm/ScrolledW.h>
+#include <Xm/Separator.h>
+#include <Xm/Text.h>
+#include <Xm/TextF.h>
+#include <Xm/ToggleB.h>
+#include <Xm/Xm.h>
+
+#ifdef HAVE_XRENDER
+#include <X11/extensions/Xrender.h>
+#endif /* HAVE_XRENDER */
+
+#ifdef HAVE_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif /* HAVE_XINERAMA */
+
+#undef Mask
diff --git a/src_plugins/import_dsn/Makefile b/src_plugins/import_dsn/Makefile
new file mode 100644
index 0000000..f061554
--- /dev/null
+++ b/src_plugins/import_dsn/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_import_dsn
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/import_dsn/Plug.tmpasm b/src_plugins/import_dsn/Plug.tmpasm
new file mode 100644
index 0000000..4e531da
--- /dev/null
+++ b/src_plugins/import_dsn/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {import_dsn}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/import_dsn/dsn.o @]
+
+switch /local/pcb/import_dsn/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/import_dsn/README b/src_plugins/import_dsn/README
new file mode 100644
index 0000000..1825fb1
--- /dev/null
+++ b/src_plugins/import_dsn/README
@@ -0,0 +1,5 @@
+Import specctra .dsn files
+
+#state: Work-in-progress
+#default: disable
+#implements: import
diff --git a/src_plugins/import_dsn/dsn.c b/src_plugins/import_dsn/dsn.c
new file mode 100644
index 0000000..c2b5eb0
--- /dev/null
+++ b/src_plugins/import_dsn/dsn.c
@@ -0,0 +1,177 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *
+ *  Specctra .dsn import HID
+ *  Copyright (C) 2008, 2011 Josh Jordan, Dan McMahill, and Jared Casper
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+This program imports specctra .dsn files to geda .pcb files.
+By Josh Jordan and Dan McMahill, modified from bom.c
+  -- Updated to use Coord and other fixes by Jared Casper 16 Sep 2011
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "misc.h"
+#include "rats.h"
+#include "buffer.h"
+#include "change.h"
+#include "draw.h"
+#include "set.h"
+#include "undo.h"
+#include "pcb-printf.h"
+#include "create.h"
+#include "polygon.h"
+#include "compat_misc.h"
+
+#include "hid.h"
+#include "hid_draw_helpers.h"
+#include "hid_nogui.h"
+#include "hid_actions.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+#include "hid_helper.h"
+#include "plugins.h"
+
+static const char *dsn_cookie = "dsn importer";
+
+/* dsn name */
+#define FREE(x) if((x) != NULL) { free (x); (x) = NULL; }
+
+static const char load_dsn_syntax[] = "LoadDsnFrom(filename)";
+
+static const char load_dsn_help[] = "Loads the specified dsn resource file.";
+
+/* %start-doc actions LoaddsnFrom
+
+ at cindex Specctra routed import
+ at findex LoadDsnFrom()
+
+ at table @var
+ at item filename
+Name of the dsn resource file.  If not specified, the user will
+be prompted to enter one.
+ at end table
+
+%end-doc */
+
+int ActionLoadDsnFrom(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *fname = NULL;
+	static char *default_file = NULL;
+	char str[200];
+	FILE *fp;
+	int ret;
+	Coord dim1, dim2, x0 = 0, y0 = 0, x1, y1;
+	Coord linethick = 0, lineclear, viadiam, viadrill;
+	char lname[200];
+	LayerType *rlayer = NULL;
+	LineType *line = NULL;
+
+	fname = argc ? argv[0] : 0;
+
+	if (!fname || !*fname) {
+		fname = gui->fileselect(_("Load dsn routing session Resource File..."),
+														_("Picks a dsn session resource file to load.\n"
+															"This file could be generated by freeroute.net\n"),
+														default_file, ".ses", "ses", HID_FILESELECT_READ);
+		if (fname == NULL)
+			AFAIL(load_dsn);
+		if (default_file != NULL) {
+			free(default_file);
+			default_file = NULL;
+		}
+
+		if (fname && *fname)
+			default_file = pcb_strdup(fname);
+	}
+
+	lineclear = PCB->RouteStyle.array[0].Clearance * 2;
+	fp = fopen(fname, "r");
+	if (!fp) {
+		Message(PCB_MSG_ERROR, "Can't load dsn file %s for read\n", fname);
+		return 1;
+	}
+	while (fgets(str, sizeof(str), fp) != NULL) {
+		/* strip trailing '\n' if it exists */
+		int len = strlen(str) - 1;
+		if (str[len] == '\n')
+			str[len] = 0;
+		ret = sscanf(str, "          (path %s %ld", lname, &dim1);
+		if (ret == 2) {
+			rlayer = 0;
+			LAYER_LOOP(PCB->Data, max_group) {
+				if (!strcmp(layer->Name, lname))
+					rlayer = layer;
+			}
+			END_LOOP;
+			linethick = dim1;
+			x0 = 0;
+			y0 = 0;
+		}
+		ret = sscanf(str, "            %ld %ld", &dim1, &dim2);
+		if (ret == 2) {
+			x1 = dim1;
+			y1 = dim2;
+			if (x0 != 0 || y0 != 0) {
+				line = CreateDrawnLineOnLayer(rlayer, x0, PCB->MaxHeight - y0,
+																			x1, PCB->MaxHeight - y1, linethick, lineclear, MakeFlags(PCB_FLAG_AUTO | PCB_FLAG_CLEARLINE));
+				ClearFromPolygon(PCB->Data, PCB_TYPE_LINE, rlayer, line);
+			}
+			x0 = x1;
+			y0 = y1;
+		}
+		ret = sscanf(str, "        (via via_%ld_%ld %ld %ld", &viadiam, &viadrill, &dim1, &dim2);
+		if (ret == 4) {
+			CreateNewVia(PCB->Data, dim1, PCB->MaxHeight - dim2, viadiam, lineclear, 0, viadrill, 0, MakeFlags(PCB_FLAG_AUTO));
+		}
+	}
+	fclose(fp);
+	return 0;
+}
+
+HID_Action dsn_action_list[] = {
+	{"LoadDsnFrom", 0, ActionLoadDsnFrom, load_dsn_help, load_dsn_syntax}
+};
+
+REGISTER_ACTIONS(dsn_action_list, dsn_cookie)
+
+static void hid_dsn_uninit()
+{
+
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_import_dsn_init()
+{
+	REGISTER_ACTIONS(dsn_action_list, dsn_cookie)
+	return hid_dsn_uninit;
+}
+
diff --git a/src_plugins/import_edif/Makefile b/src_plugins/import_edif/Makefile
new file mode 100644
index 0000000..0fb3a2e
--- /dev/null
+++ b/src_plugins/import_edif/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_import_edif
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/import_edif/Plug.tmpasm b/src_plugins/import_edif/Plug.tmpasm
new file mode 100644
index 0000000..c46b7fa
--- /dev/null
+++ b/src_plugins/import_edif/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {import_edif}
+append /local/pcb/mod/OBJS [@ $(PLUGDIR)/import_edif/edif.o $(PLUGDIR)/import_edif/import_edif.o @]
+append /local/pcb/mod/YACC {$(PLUGDIR)/import_edif/edif}
+
+switch /local/pcb/import_edif/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/import_edif/README b/src_plugins/import_edif/README
new file mode 100644
index 0000000..80d8ff9
--- /dev/null
+++ b/src_plugins/import_edif/README
@@ -0,0 +1,5 @@
+Import plugin for netlists in the EDIF format.
+
+#state: works
+#default: buildin
+#implements: import
diff --git a/src_plugins/import_edif/edif.c b/src_plugins/import_edif/edif.c
new file mode 100644
index 0000000..ad59be7
--- /dev/null
+++ b/src_plugins/import_edif/edif.c
@@ -0,0 +1,6243 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "3.0.2"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names.  */
+#define yyparse         edifparse
+#define yylex           ediflex
+#define yyerror         ediferror
+#define yydebug         edifdebug
+#define yynerrs         edifnerrs
+
+#define yylval          ediflval
+#define yychar          edifchar
+
+/* Copy the first part of user declarations.  */
+#line 1 "../../src_plugins/import_edif/edif.y" /* yacc.c:339  */
+
+/*
+ * PCB Edif parser based heavily on:
+ *
+ *	Header: edif.y,v 1.18 87/12/07 19:59:49 roger Locked
+ */
+/************************************************************************
+ *									*
+ *				edif.y					*
+ *									*
+ *			EDIF 2.0.0 parser, Level 0			*
+ *									*
+ *	You are free to copy, distribute, use it, abuse it, make it	*
+ *	write bad tracks all over the disk ... or anything else.	*
+ *									*
+ *	Your friendly neighborhood Rogue Monster - roger at mips.com	*
+ *									*
+ ************************************************************************/
+#include <stdio.h>
+
+/* for malloc, free, atoi */
+#include <stdlib.h>
+
+/* for strcpy */
+#include <string.h>
+
+#include <ctype.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+/* from mymem.h, not include because of the malloc junk */
+LibraryMenuTypePtr GetLibraryMenuMemory(LibraryTypePtr, int *idx);
+LibraryEntryTypePtr GetLibraryEntryMemory(LibraryMenuTypePtr);
+
+
+/*
+ *	Local definitions.
+ */
+#define	IDENT_LENGTH		255
+#define	Malloc(s)		malloc(s)
+#define	Free(p)			free(p)
+#define	Getc(s)			getc(s)
+#define	Ungetc(c)		ungetc(c,Input)
+
+ typedef struct _str_pair
+ {
+     char* str1;
+     char* str2;
+     struct _str_pair* next;
+ } str_pair;
+
+ typedef struct _pair_list
+ {
+     char* name;
+     str_pair* list;
+ } pair_list;
+
+ str_pair* new_str_pair(char* s1, char* s2)
+ {
+   str_pair* ps = (str_pair *)malloc(sizeof(str_pair));
+     ps->str1 = s1;
+     ps->str2 = s2;
+     ps->next = NULL;
+     return ps;
+ }
+
+ pair_list* new_pair_list(str_pair* ps)
+ {
+   pair_list* pl = (pair_list *)malloc(sizeof(pair_list));
+     pl->list = ps;
+     pl->name = NULL;
+     return pl;
+ }
+
+ void str_pair_free(str_pair* ps)
+ {
+     str_pair* node;
+     while ( ps )
+     {
+	 free(ps->str1);
+	 free(ps->str2);
+	 node = ps;
+	 ps = ps->next;
+	 free(node);
+     }
+ }
+
+ void pair_list_free(pair_list* pl)
+ {
+     str_pair_free(pl->list);
+     free(pl->name);
+     free(pl);
+ }
+
+ void define_pcb_net(str_pair* name, pair_list* nodes)
+ {
+     int tl;
+     str_pair* done_node;
+     str_pair* node;
+     char* buf;
+     char* p;
+     LibraryEntryTypePtr entry;
+     LibraryMenuTypePtr menu = GetLibraryMenuMemory(&PCB->NetlistLib[NETLIST_INPUT], NULL);
+
+     if ( !name->str1 )
+     {
+	 /* no net name given, stop now */
+	 /* if renamed str2 also exists and must be freed */
+	 if ( name->str2 )  free(name->str2);
+	 free(name);
+	 pair_list_free(nodes);
+	 return;
+     }
+     menu->Name = pcb_strdup (name->str1);
+     free(name->str1);
+     /* if renamed str2 also exists and must be freed */
+     if ( name->str2 )  free(name->str2);
+     free(name);
+     buf = (char *)malloc(256);
+     if ( !buf )
+     {
+	 /* no memory */
+	 pair_list_free(nodes);
+	 return;
+     }
+
+     node = nodes->list;
+     free(nodes->name);
+     free(nodes);
+     while ( node )
+     {
+	 /* check for node with no instance */
+	 if ( !node->str1 )
+	 {
+	     /* toss it and move on */
+	     free(node->str2);
+	     done_node = node;
+	     node = node->next;
+	     free(done_node);
+	     continue;
+	 }
+	 tl = strlen(node->str1) + strlen(node->str2);
+	 if ( tl + 3 > 256 )
+	 {
+	     free(buf);
+	     buf = (char *)malloc(tl+3);
+	     if ( !buf )
+	     {
+		 /* no memory */
+		 str_pair_free(node);
+		 return;
+	     }
+	 }
+	 strcpy(buf,node->str1);
+	 /* make all upper case, because of PCB funky behaviour */
+	 p=buf;
+	 while ( *p )
+	 {
+	     *p = toupper( (int) *p);
+	     p++;
+	 }
+	 /* add dash separating designator from node */
+	 *(buf+strlen(node->str1)) = '-';
+	 /* check for the edif number prefix */
+	 if ( node->str2[0] == '&' )
+	 {
+	     /* skip number prefix */
+	     strcpy(buf+strlen(node->str1)+1,node->str2 +1);
+	 }
+	 else
+	 {
+	     strcpy(buf+strlen(node->str1)+1,node->str2);
+	 }
+	 /* free the strings */
+	 free(node->str1);
+	 free(node->str2);
+	 entry = GetLibraryEntryMemory (menu);
+	 entry->ListEntry = pcb_strdup(buf);
+	 done_node = node;
+	 node = node->next;
+	 free(done_node);
+     }
+ }
+
+
+/* forward function declarations */
+ static int yylex(void);
+ static void yyerror(const char *);
+ static void PopC(void);
+
+#line 269 "edif.tab.c" /* yacc.c:339  */
+
+# ifndef YY_NULLPTR
+#  if defined __cplusplus && 201103L <= __cplusplus
+#   define YY_NULLPTR nullptr
+#  else
+#   define YY_NULLPTR 0
+#  endif
+# endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+   by #include "edif.tab.h".  */
+#ifndef YY_EDIF_EDIF_TAB_H_INCLUDED
+# define YY_EDIF_EDIF_TAB_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int edifdebug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    EDIF_TOK_IDENT = 258,
+    EDIF_TOK_INT = 259,
+    EDIF_TOK_KEYWORD = 260,
+    EDIF_TOK_STR = 261,
+    EDIF_TOK_ANGLE = 262,
+    EDIF_TOK_BEHAVIOR = 263,
+    EDIF_TOK_CALCULATED = 264,
+    EDIF_TOK_CAPACITANCE = 265,
+    EDIF_TOK_CENTERCENTER = 266,
+    EDIF_TOK_CENTERLEFT = 267,
+    EDIF_TOK_CENTERRIGHT = 268,
+    EDIF_TOK_CHARGE = 269,
+    EDIF_TOK_CONDUCTANCE = 270,
+    EDIF_TOK_CURRENT = 271,
+    EDIF_TOK_DISTANCE = 272,
+    EDIF_TOK_DOCUMENT = 273,
+    EDIF_TOK_ENERGY = 274,
+    EDIF_TOK_EXTEND = 275,
+    EDIF_TOK_FLUX = 276,
+    EDIF_TOK_FREQUENCY = 277,
+    EDIF_TOK_GENERIC = 278,
+    EDIF_TOK_GRAPHIC = 279,
+    EDIF_TOK_INDUCTANCE = 280,
+    EDIF_TOK_INOUT = 281,
+    EDIF_TOK_INPUT = 282,
+    EDIF_TOK_LOGICMODEL = 283,
+    EDIF_TOK_LOWERCENTER = 284,
+    EDIF_TOK_LOWERLEFT = 285,
+    EDIF_TOK_LOWERRIGHT = 286,
+    EDIF_TOK_MASKLAYOUT = 287,
+    EDIF_TOK_MASS = 288,
+    EDIF_TOK_MEASURED = 289,
+    EDIF_TOK_MX = 290,
+    EDIF_TOK_MXR90 = 291,
+    EDIF_TOK_MY = 292,
+    EDIF_TOK_MYR90 = 293,
+    EDIF_TOK_NETLIST = 294,
+    EDIF_TOK_OUTPUT = 295,
+    EDIF_TOK_PCBLAYOUT = 296,
+    EDIF_TOK_POWER = 297,
+    EDIF_TOK_R0 = 298,
+    EDIF_TOK_R180 = 299,
+    EDIF_TOK_R270 = 300,
+    EDIF_TOK_R90 = 301,
+    EDIF_TOK_REQUIRED = 302,
+    EDIF_TOK_RESISTANCE = 303,
+    EDIF_TOK_RIPPER = 304,
+    EDIF_TOK_ROUND = 305,
+    EDIF_TOK_SCHEMATIC = 306,
+    EDIF_TOK_STRANGER = 307,
+    EDIF_TOK_SYMBOLIC = 308,
+    EDIF_TOK_TEMPERATURE = 309,
+    EDIF_TOK_TIE = 310,
+    EDIF_TOK_TIME = 311,
+    EDIF_TOK_TRUNCATE = 312,
+    EDIF_TOK_UPPERCENTER = 313,
+    EDIF_TOK_UPPERLEFT = 314,
+    EDIF_TOK_UPPERRIGHT = 315,
+    EDIF_TOK_VOLTAGE = 316,
+    EDIF_TOK_ACLOAD = 317,
+    EDIF_TOK_AFTER = 318,
+    EDIF_TOK_ANNOTATE = 319,
+    EDIF_TOK_APPLY = 320,
+    EDIF_TOK_ARC = 321,
+    EDIF_TOK_ARRAY = 322,
+    EDIF_TOK_ARRAYMACRO = 323,
+    EDIF_TOK_ARRAYRELATEDINFO = 324,
+    EDIF_TOK_ARRAYSITE = 325,
+    EDIF_TOK_ATLEAST = 326,
+    EDIF_TOK_ATMOST = 327,
+    EDIF_TOK_AUTHOR = 328,
+    EDIF_TOK_BASEARRAY = 329,
+    EDIF_TOK_BECOMES = 330,
+    EDIF_TOK_BETWEEN = 331,
+    EDIF_TOK_BOOLEAN = 332,
+    EDIF_TOK_BOOLEANDISPLAY = 333,
+    EDIF_TOK_BOOLEANMAP = 334,
+    EDIF_TOK_BORDERPATTERN = 335,
+    EDIF_TOK_BORDERWIDTH = 336,
+    EDIF_TOK_BOUNDINGBOX = 337,
+    EDIF_TOK_CELL = 338,
+    EDIF_TOK_CELLREF = 339,
+    EDIF_TOK_CELLTYPE = 340,
+    EDIF_TOK_CHANGE = 341,
+    EDIF_TOK_CIRCLE = 342,
+    EDIF_TOK_COLOR = 343,
+    EDIF_TOK_COMMENT = 344,
+    EDIF_TOK_COMMENTGRAPHICS = 345,
+    EDIF_TOK_COMPOUND = 346,
+    EDIF_TOK_CONNECTLOCATION = 347,
+    EDIF_TOK_CONTENTS = 348,
+    EDIF_TOK_CORNERTYPE = 349,
+    EDIF_TOK_CRITICALITY = 350,
+    EDIF_TOK_CURRENTMAP = 351,
+    EDIF_TOK_CURVE = 352,
+    EDIF_TOK_CYCLE = 353,
+    EDIF_TOK_DATAORIGIN = 354,
+    EDIF_TOK_DCFANINLOAD = 355,
+    EDIF_TOK_DCFANOUTLOAD = 356,
+    EDIF_TOK_DCMAXFANIN = 357,
+    EDIF_TOK_DCMAXFANOUT = 358,
+    EDIF_TOK_DELAY = 359,
+    EDIF_TOK_DELTA = 360,
+    EDIF_TOK_DERIVATION = 361,
+    EDIF_TOK_DESIGN = 362,
+    EDIF_TOK_DESIGNATOR = 363,
+    EDIF_TOK_DIFFERENCE = 364,
+    EDIF_TOK_DIRECTION = 365,
+    EDIF_TOK_DISPLAY = 366,
+    EDIF_TOK_DOMINATES = 367,
+    EDIF_TOK_DOT = 368,
+    EDIF_TOK_DURATION = 369,
+    EDIF_TOK_E = 370,
+    EDIF_TOK_EDIF = 371,
+    EDIF_TOK_EDIFLEVEL = 372,
+    EDIF_TOK_EDIFVERSION = 373,
+    EDIF_TOK_ENCLOSUREDISTANCE = 374,
+    EDIF_TOK_ENDTYPE = 375,
+    EDIF_TOK_ENTRY = 376,
+    EDIF_TOK_EVENT = 377,
+    EDIF_TOK_EXACTLY = 378,
+    EDIF_TOK_EXTERNAL = 379,
+    EDIF_TOK_FABRICATE = 380,
+    EDIF_TOK_FALSE = 381,
+    EDIF_TOK_FIGURE = 382,
+    EDIF_TOK_FIGUREAREA = 383,
+    EDIF_TOK_FIGUREGROUP = 384,
+    EDIF_TOK_FIGUREGROUPOBJECT = 385,
+    EDIF_TOK_FIGUREGROUPOVERRIDE = 386,
+    EDIF_TOK_FIGUREGROUPREF = 387,
+    EDIF_TOK_FIGUREPERIMETER = 388,
+    EDIF_TOK_FIGUREWIDTH = 389,
+    EDIF_TOK_FILLPATTERN = 390,
+    EDIF_TOK_FOLLOW = 391,
+    EDIF_TOK_FORBIDDENEVENT = 392,
+    EDIF_TOK_GLOBALPORTREF = 393,
+    EDIF_TOK_GREATERTHAN = 394,
+    EDIF_TOK_GRIDMAP = 395,
+    EDIF_TOK_IGNORE = 396,
+    EDIF_TOK_INCLUDEFIGUREGROUP = 397,
+    EDIF_TOK_INITIAL = 398,
+    EDIF_TOK_INSTANCE = 399,
+    EDIF_TOK_INSTANCEBACKANNOTATE = 400,
+    EDIF_TOK_INSTANCEGROUP = 401,
+    EDIF_TOK_INSTANCEMAP = 402,
+    EDIF_TOK_INSTANCEREF = 403,
+    EDIF_TOK_INTEGER = 404,
+    EDIF_TOK_INTEGERDISPLAY = 405,
+    EDIF_TOK_INTERFACE = 406,
+    EDIF_TOK_INTERFIGUREGROUPSPACING = 407,
+    EDIF_TOK_INTERSECTION = 408,
+    EDIF_TOK_INTRAFIGUREGROUPSPACING = 409,
+    EDIF_TOK_INVERSE = 410,
+    EDIF_TOK_ISOLATED = 411,
+    EDIF_TOK_JOINED = 412,
+    EDIF_TOK_JUSTIFY = 413,
+    EDIF_TOK_KEYWORDDISPLAY = 414,
+    EDIF_TOK_KEYWORDLEVEL = 415,
+    EDIF_TOK_KEYWORDMAP = 416,
+    EDIF_TOK_LESSTHAN = 417,
+    EDIF_TOK_LIBRARY = 418,
+    EDIF_TOK_LIBRARYREF = 419,
+    EDIF_TOK_LISTOFNETS = 420,
+    EDIF_TOK_LISTOFPORTS = 421,
+    EDIF_TOK_LOADDELAY = 422,
+    EDIF_TOK_LOGICASSIGN = 423,
+    EDIF_TOK_LOGICINPUT = 424,
+    EDIF_TOK_LOGICLIST = 425,
+    EDIF_TOK_LOGICMAPINPUT = 426,
+    EDIF_TOK_LOGICMAPOUTPUT = 427,
+    EDIF_TOK_LOGICONEOF = 428,
+    EDIF_TOK_LOGICOUTPUT = 429,
+    EDIF_TOK_LOGICPORT = 430,
+    EDIF_TOK_LOGICREF = 431,
+    EDIF_TOK_LOGICVALUE = 432,
+    EDIF_TOK_LOGICWAVEFORM = 433,
+    EDIF_TOK_MAINTAIN = 434,
+    EDIF_TOK_MATCH = 435,
+    EDIF_TOK_MEMBER = 436,
+    EDIF_TOK_MINOMAX = 437,
+    EDIF_TOK_MINOMAXDISPLAY = 438,
+    EDIF_TOK_MNM = 439,
+    EDIF_TOK_MULTIPLEVALUESET = 440,
+    EDIF_TOK_MUSTJOIN = 441,
+    EDIF_TOK_NAME = 442,
+    EDIF_TOK_NET = 443,
+    EDIF_TOK_NETBACKANNOTATE = 444,
+    EDIF_TOK_NETBUNDLE = 445,
+    EDIF_TOK_NETDELAY = 446,
+    EDIF_TOK_NETGROUP = 447,
+    EDIF_TOK_NETMAP = 448,
+    EDIF_TOK_NETREF = 449,
+    EDIF_TOK_NOCHANGE = 450,
+    EDIF_TOK_NONPERMUTABLE = 451,
+    EDIF_TOK_NOTALLOWED = 452,
+    EDIF_TOK_NOTCHSPACING = 453,
+    EDIF_TOK_NUMBER = 454,
+    EDIF_TOK_NUMBERDEFINITION = 455,
+    EDIF_TOK_NUMBERDISPLAY = 456,
+    EDIF_TOK_OFFPAGECONNECTOR = 457,
+    EDIF_TOK_OFFSETEVENT = 458,
+    EDIF_TOK_OPENSHAPE = 459,
+    EDIF_TOK_ORIENTATION = 460,
+    EDIF_TOK_ORIGIN = 461,
+    EDIF_TOK_OVERHANGDISTANCE = 462,
+    EDIF_TOK_OVERLAPDISTANCE = 463,
+    EDIF_TOK_OVERSIZE = 464,
+    EDIF_TOK_OWNER = 465,
+    EDIF_TOK_PAGE = 466,
+    EDIF_TOK_PAGESIZE = 467,
+    EDIF_TOK_PARAMETER = 468,
+    EDIF_TOK_PARAMETERASSIGN = 469,
+    EDIF_TOK_PARAMETERDISPLAY = 470,
+    EDIF_TOK_PATH = 471,
+    EDIF_TOK_PATHDELAY = 472,
+    EDIF_TOK_PATHWIDTH = 473,
+    EDIF_TOK_PERMUTABLE = 474,
+    EDIF_TOK_PHYSICALDESIGNRULE = 475,
+    EDIF_TOK_PLUG = 476,
+    EDIF_TOK_POINT = 477,
+    EDIF_TOK_POINTDISPLAY = 478,
+    EDIF_TOK_POINTLIST = 479,
+    EDIF_TOK_POLYGON = 480,
+    EDIF_TOK_PORT = 481,
+    EDIF_TOK_PORTBACKANNOTATE = 482,
+    EDIF_TOK_PORTBUNDLE = 483,
+    EDIF_TOK_PORTDELAY = 484,
+    EDIF_TOK_PORTGROUP = 485,
+    EDIF_TOK_PORTIMPLEMENTATION = 486,
+    EDIF_TOK_PORTINSTANCE = 487,
+    EDIF_TOK_PORTLIST = 488,
+    EDIF_TOK_PORTLISTALIAS = 489,
+    EDIF_TOK_PORTMAP = 490,
+    EDIF_TOK_PORTREF = 491,
+    EDIF_TOK_PROGRAM = 492,
+    EDIF_TOK_PROPERTY = 493,
+    EDIF_TOK_PROPERTYDISPLAY = 494,
+    EDIF_TOK_PROTECTIONFRAME = 495,
+    EDIF_TOK_PT = 496,
+    EDIF_TOK_RANGEVECTOR = 497,
+    EDIF_TOK_RECTANGLE = 498,
+    EDIF_TOK_RECTANGLESIZE = 499,
+    EDIF_TOK_RENAME = 500,
+    EDIF_TOK_RESOLVES = 501,
+    EDIF_TOK_SCALE = 502,
+    EDIF_TOK_SCALEX = 503,
+    EDIF_TOK_SCALEY = 504,
+    EDIF_TOK_SECTION = 505,
+    EDIF_TOK_SHAPE = 506,
+    EDIF_TOK_SIMULATE = 507,
+    EDIF_TOK_SIMULATIONINFO = 508,
+    EDIF_TOK_SINGLEVALUESET = 509,
+    EDIF_TOK_SITE = 510,
+    EDIF_TOK_SOCKET = 511,
+    EDIF_TOK_SOCKETSET = 512,
+    EDIF_TOK_STATUS = 513,
+    EDIF_TOK_STEADY = 514,
+    EDIF_TOK_STRING = 515,
+    EDIF_TOK_STRINGDISPLAY = 516,
+    EDIF_TOK_STRONG = 517,
+    EDIF_TOK_SYMBOL = 518,
+    EDIF_TOK_SYMMETRY = 519,
+    EDIF_TOK_TABLE = 520,
+    EDIF_TOK_TABLEDEFAULT = 521,
+    EDIF_TOK_TECHNOLOGY = 522,
+    EDIF_TOK_TEXTHEIGHT = 523,
+    EDIF_TOK_TIMEINTERVAL = 524,
+    EDIF_TOK_TIMESTAMP = 525,
+    EDIF_TOK_TIMING = 526,
+    EDIF_TOK_TRANSFORM = 527,
+    EDIF_TOK_TRANSITION = 528,
+    EDIF_TOK_TRIGGER = 529,
+    EDIF_TOK_TRUE = 530,
+    EDIF_TOK_UNCONSTRAINED = 531,
+    EDIF_TOK_UNDEFINED = 532,
+    EDIF_TOK_UNION = 533,
+    EDIF_TOK_UNIT = 534,
+    EDIF_TOK_UNUSED = 535,
+    EDIF_TOK_USERDATA = 536,
+    EDIF_TOK_VERSION = 537,
+    EDIF_TOK_VIEW = 538,
+    EDIF_TOK_VIEWLIST = 539,
+    EDIF_TOK_VIEWMAP = 540,
+    EDIF_TOK_VIEWREF = 541,
+    EDIF_TOK_VIEWTYPE = 542,
+    EDIF_TOK_VISIBLE = 543,
+    EDIF_TOK_VOLTAGEMAP = 544,
+    EDIF_TOK_WAVEVALUE = 545,
+    EDIF_TOK_WEAK = 546,
+    EDIF_TOK_WEAKJOINED = 547,
+    EDIF_TOK_WHEN = 548,
+    EDIF_TOK_WRITTEN = 549
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 198 "../../src_plugins/import_edif/edif.y" /* yacc.c:355  */
+
+    char* s;
+    pair_list* pl;
+    str_pair* ps;
+
+#line 610 "edif.tab.c" /* yacc.c:355  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE ediflval;
+
+int edifparse (void);
+
+#endif /* !YY_EDIF_EDIF_TAB_H_INCLUDED  */
+
+/* Copy the second part of user declarations.  */
+
+#line 625 "edif.tab.c" /* yacc.c:358  */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__                                               \
+      && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)))  \
+     || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+#  define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+#  define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE   YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+#  define _Noreturn __declspec (noreturn)
+# else
+#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+    _Pragma ("GCC diagnostic push") \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYSIZE_T yynewbytes;                                            \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYSIZE_T yyi;                         \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  11
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   2619
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  296
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  472
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  1129
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  1626
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+   by yylex, with out-of-bounds checking.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   549
+
+#define YYTRANSLATE(YYX)                                                \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, without out-of-bounds checking.  */
+static const yytype_uint16 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,   295,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    69,    70,    71,    72,    73,    74,
+      75,    76,    77,    78,    79,    80,    81,    82,    83,    84,
+      85,    86,    87,    88,    89,    90,    91,    92,    93,    94,
+      95,    96,    97,    98,    99,   100,   101,   102,   103,   104,
+     105,   106,   107,   108,   109,   110,   111,   112,   113,   114,
+     115,   116,   117,   118,   119,   120,   121,   122,   123,   124,
+     125,   126,   127,   128,   129,   130,   131,   132,   133,   134,
+     135,   136,   137,   138,   139,   140,   141,   142,   143,   144,
+     145,   146,   147,   148,   149,   150,   151,   152,   153,   154,
+     155,   156,   157,   158,   159,   160,   161,   162,   163,   164,
+     165,   166,   167,   168,   169,   170,   171,   172,   173,   174,
+     175,   176,   177,   178,   179,   180,   181,   182,   183,   184,
+     185,   186,   187,   188,   189,   190,   191,   192,   193,   194,
+     195,   196,   197,   198,   199,   200,   201,   202,   203,   204,
+     205,   206,   207,   208,   209,   210,   211,   212,   213,   214,
+     215,   216,   217,   218,   219,   220,   221,   222,   223,   224,
+     225,   226,   227,   228,   229,   230,   231,   232,   233,   234,
+     235,   236,   237,   238,   239,   240,   241,   242,   243,   244,
+     245,   246,   247,   248,   249,   250,   251,   252,   253,   254,
+     255,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294
+};
+
+#if YYDEBUG
+  /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   510,   510,   513,   516,   517,   518,   519,   520,   521,
+     522,   525,   528,   531,   535,   538,   539,   542,   545,   546,
+     547,   548,   549,   550,   553,   556,   557,   560,   563,   564,
+     565,   566,   567,   570,   573,   576,   577,   580,   583,   586,
+     587,   588,   589,   590,   593,   596,   599,   602,   605,   608,
+     611,   612,   613,   616,   619,   620,   623,   624,   627,   630,
+     631,   632,   633,   636,   639,   640,   643,   646,   647,   650,
+     653,   656,   659,   662,   663,   664,   665,   666,   667,   668,
+     671,   674,   677,   678,   681,   684,   687,   688,   689,   692,
+     695,   696,   697,   700,   701,   702,   705,   708,   709,   712,
+     715,   718,   719,   722,   725,   726,   727,   728,   729,   730,
+     731,   732,   735,   738,   741,   742,   743,   744,   745,   746,
+     747,   748,   749,   750,   751,   752,   753,   754,   755,   756,
+     757,   758,   761,   764,   765,   768,   771,   772,   773,   776,
+     779,   780,   783,   786,   789,   790,   791,   794,   797,   798,
+     801,   804,   805,   808,   811,   812,   815,   818,   819,   822,
+     825,   826,   829,   832,   833,   836,   839,   840,   843,   846,
+     847,   850,   853,   854,   855,   858,   861,   862,   863,   864,
+     865,   868,   871,   872,   875,   878,   881,   882,   883,   884,
+     885,   886,   887,   888,   889,   890,   891,   892,   893,   894,
+     895,   898,   901,   902,   903,   904,   907,   910,   911,   912,
+     915,   918,   919,   922,   923,   926,   927,   930,   931,   934,
+     937,   938,   941,   944,   945,   948,   951,   955,   956,   957,
+     958,   961,   964,   965,   966,   969,   973,   974,   975,   978,
+     979,   980,   981,   984,   985,   986,   989,   992,   993,   994,
+     995,   996,   997,   998,  1001,  1004,  1007,  1008,  1009,  1010,
+    1011,  1014,  1017,  1020,  1023,  1024,  1025,  1026,  1027,  1028,
+    1029,  1030,  1031,  1032,  1033,  1034,  1035,  1036,  1039,  1042,
+    1045,  1048,  1049,  1050,  1053,  1056,  1057,  1058,  1059,  1060,
+    1061,  1062,  1063,  1064,  1065,  1066,  1067,  1068,  1071,  1074,
+    1075,  1078,  1081,  1082,  1083,  1084,  1085,  1086,  1087,  1088,
+    1089,  1090,  1091,  1094,  1097,  1098,  1099,  1100,  1103,  1104,
+    1105,  1106,  1107,  1110,  1113,  1114,  1115,  1116,  1119,  1122,
+    1123,  1124,  1125,  1128,  1131,  1134,  1135,  1138,  1139,  1140,
+    1141,  1144,  1147,  1148,  1151,  1154,  1155,  1156,  1157,  1158,
+    1161,  1164,  1167,  1170,  1173,  1176,  1177,  1180,  1183,  1186,
+    1187,  1188,  1189,  1190,  1191,  1192,  1193,  1194,  1195,  1198,
+    1201,  1202,  1203,  1206,  1209,  1210,  1211,  1212,  1213,  1216,
+    1219,  1220,  1223,  1226,  1227,  1228,  1229,  1230,  1233,  1234,
+    1237,  1238,  1241,  1244,  1245,  1248,  1251,  1252,  1253,  1254,
+    1257,  1260,  1261,  1262,  1263,  1264,  1265,  1266,  1267,  1268,
+    1269,  1270,  1271,  1272,  1273,  1274,  1275,  1276,  1279,  1283,
+    1284,  1285,  1286,  1289,  1292,  1293,  1294,  1295,  1298,  1301,
+    1302,  1303,  1304,  1307,  1310,  1311,  1314,  1317,  1320,  1321,
+    1322,  1323,  1326,  1329,  1330,  1331,  1332,  1333,  1334,  1335,
+    1336,  1337,  1340,  1343,  1344,  1347,  1350,  1353,  1354,  1357,
+    1360,  1363,  1366,  1369,  1372,  1375,  1376,  1377,  1378,  1379,
+    1382,  1385,  1388,  1389,  1392,  1395,  1396,  1397,  1400,  1403,
+    1404,  1407,  1410,  1411,  1414,  1415,  1416,  1419,  1420,  1421,
+    1424,  1427,  1428,  1429,  1430,  1433,  1436,  1437,  1438,  1439,
+    1442,  1445,  1446,  1449,  1452,  1453,  1456,  1459,  1462,  1465,
+    1466,  1467,  1470,  1473,  1474,  1475,  1476,  1479,  1482,  1483,
+    1484,  1485,  1488,  1491,  1492,  1495,  1498,  1499,  1500,  1501,
+    1502,  1503,  1504,  1505,  1506,  1507,  1508,  1509,  1510,  1511,
+    1512,  1515,  1518,  1519,  1520,  1521,  1522,  1525,  1528,  1529,
+    1532,  1533,  1534,  1537,  1540,  1541,  1542,  1545,  1546,  1547,
+    1550,  1553,  1554,  1557,  1560,  1561,  1562,  1563,  1566,  1569,
+    1570,  1573,  1574,  1577,  1580,  1581,  1582,  1585,  1588,  1589,
+    1592,  1595,  1596,  1597,  1598,  1599,  1602,  1605,  1606,  1609,
+    1610,  1611,  1614,  1615,  1618,  1621,  1622,  1623,  1624,  1625,
+    1626,  1627,  1628,  1629,  1630,  1633,  1636,  1637,  1638,  1639,
+    1640,  1643,  1646,  1647,  1648,  1649,  1650,  1651,  1654,  1657,
+    1658,  1659,  1662,  1665,  1666,  1667,  1670,  1673,  1674,  1675,
+    1676,  1677,  1680,  1681,  1685,  1686,  1689,  1692,  1693,  1694,
+    1695,  1698,  1701,  1704,  1705,  1706,  1709,  1712,  1713,  1714,
+    1717,  1720,  1721,  1722,  1723,  1726,  1729,  1730,  1731,  1732,
+    1735,  1738,  1739,  1742,  1745,  1746,  1747,  1748,  1751,  1754,
+    1755,  1756,  1757,  1758,  1761,  1764,  1767,  1768,  1771,  1774,
+    1775,  1776,  1777,  1778,  1779,  1780,  1781,  1784,  1787,  1791,
+    1792,  1793,  1794,  1797,  1801,  1802,  1803,  1804,  1807,  1810,
+    1811,  1814,  1817,  1820,  1821,  1822,  1823,  1824,  1825,  1826,
+    1827,  1828,  1829,  1832,  1835,  1838,  1839,  1842,  1845,  1846,
+    1849,  1852,  1855,  1856,  1859,  1862,  1863,  1866,  1869,  1872,
+    1873,  1874,  1875,  1878,  1881,  1882,  1885,  1888,  1889,  1890,
+    1891,  1894,  1897,  1898,  1901,  1904,  1905,  1908,  1911,  1914,
+    1915,  1918,  1921,  1922,  1923,  1924,  1925,  1926,  1927,  1928,
+    1929,  1930,  1931,  1932,  1933,  1936,  1939,  1940,  1941,  1942,
+    1943,  1944,  1945,  1946,  1947,  1948,  1951,  1954,  1955,  1956,
+    1957,  1960,  1963,  1964,  1965,  1966,  1969,  1972,  1973,  1974,
+    1977,  1980,  1981,  1982,  1983,  1984,  1985,  1986,  1987,  1988,
+    1989,  1990,  1993,  1996,  1997,  1998,  1999,  2000,  2001,  2002,
+    2003,  2004,  2005,  2006,  2007,  2008,  2011,  2014,  2015,  2016,
+    2019,  2022,  2025,  2026,  2027,  2028,  2029,  2032,  2033,  2036,
+    2037,  2040,  2055,  2056,  2057,  2058,  2061,  2064,  2065,  2068,
+    2071,  2072,  2075,  2078,  2079,  2080,  2081,  2082,  2085,  2088,
+    2091,  2094,  2095,  2096,  2097,  2098,  2099,  2100,  2101,  2102,
+    2103,  2104,  2105,  2108,  2109,  2110,  2111,  2112,  2113,  2116,
+    2119,  2120,  2121,  2124,  2127,  2128,  2131,  2134,  2135,  2136,
+    2137,  2140,  2144,  2145,  2148,  2149,  2152,  2155,  2156,  2159,
+    2162,  2165,  2166,  2169,  2172,  2175,  2178,  2179,  2180,  2181,
+    2184,  2187,  2188,  2191,  2194,  2197,  2198,  2199,  2200,  2201,
+    2202,  2205,  2208,  2209,  2210,  2211,  2214,  2217,  2218,  2221,
+    2224,  2225,  2228,  2231,  2232,  2235,  2238,  2239,  2242,  2245,
+    2246,  2247,  2248,  2251,  2254,  2255,  2256,  2259,  2260,  2261,
+    2264,  2267,  2270,  2271,  2272,  2273,  2276,  2277,  2280,  2283,
+    2286,  2287,  2288,  2289,  2290,  2291,  2292,  2293,  2294,  2295,
+    2296,  2297,  2298,  2299,  2302,  2305,  2306,  2309,  2312,  2313,
+    2314,  2317,  2320,  2321,  2322,  2323,  2326,  2327,  2328,  2331,
+    2334,  2335,  2336,  2337,  2338,  2339,  2340,  2343,  2346,  2349,
+    2350,  2353,  2354,  2355,  2358,  2362,  2365,  2366,  2367,  2368,
+    2369,  2372,  2376,  2377,  2380,  2381,  2384,  2385,  2388,  2389,
+    2392,  2393,  2396,  2399,  2400,  2401,  2404,  2407,  2408,  2409,
+    2410,  2413,  2416,  2417,  2418,  2419,  2420,  2421,  2424,  2427,
+    2430,  2433,  2434,  2435,  2436,  2439,  2442,  2443,  2444,  2445,
+    2446,  2447,  2448,  2449,  2450,  2451,  2452,  2453,  2454,  2455,
+    2456,  2457,  2460,  2463,  2466,  2467,  2468,  2469,  2470,  2473,
+    2474,  2477,  2478,  2481,  2484,  2487,  2488,  2489,  2490,  2491,
+    2492,  2495,  2498,  2499,  2500,  2503,  2506,  2507,  2508,  2509,
+    2510,  2511,  2512,  2513,  2514,  2517,  2520,  2523,  2526,  2527,
+    2530,  2533,  2534,  2535,  2536,  2537,  2538,  2539,  2540,  2541,
+    2542,  2545,  2548,  2551,  2554,  2557,  2560,  2561,  2562,  2563,
+    2566,  2569,  2570,  2571,  2572,  2573,  2574,  2575,  2578,  2581,
+    2582,  2583,  2584,  2585,  2586,  2587,  2590,  2593,  2596,  2599
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "EDIF_TOK_IDENT", "EDIF_TOK_INT",
+  "EDIF_TOK_KEYWORD", "EDIF_TOK_STR", "EDIF_TOK_ANGLE",
+  "EDIF_TOK_BEHAVIOR", "EDIF_TOK_CALCULATED", "EDIF_TOK_CAPACITANCE",
+  "EDIF_TOK_CENTERCENTER", "EDIF_TOK_CENTERLEFT", "EDIF_TOK_CENTERRIGHT",
+  "EDIF_TOK_CHARGE", "EDIF_TOK_CONDUCTANCE", "EDIF_TOK_CURRENT",
+  "EDIF_TOK_DISTANCE", "EDIF_TOK_DOCUMENT", "EDIF_TOK_ENERGY",
+  "EDIF_TOK_EXTEND", "EDIF_TOK_FLUX", "EDIF_TOK_FREQUENCY",
+  "EDIF_TOK_GENERIC", "EDIF_TOK_GRAPHIC", "EDIF_TOK_INDUCTANCE",
+  "EDIF_TOK_INOUT", "EDIF_TOK_INPUT", "EDIF_TOK_LOGICMODEL",
+  "EDIF_TOK_LOWERCENTER", "EDIF_TOK_LOWERLEFT", "EDIF_TOK_LOWERRIGHT",
+  "EDIF_TOK_MASKLAYOUT", "EDIF_TOK_MASS", "EDIF_TOK_MEASURED",
+  "EDIF_TOK_MX", "EDIF_TOK_MXR90", "EDIF_TOK_MY", "EDIF_TOK_MYR90",
+  "EDIF_TOK_NETLIST", "EDIF_TOK_OUTPUT", "EDIF_TOK_PCBLAYOUT",
+  "EDIF_TOK_POWER", "EDIF_TOK_R0", "EDIF_TOK_R180", "EDIF_TOK_R270",
+  "EDIF_TOK_R90", "EDIF_TOK_REQUIRED", "EDIF_TOK_RESISTANCE",
+  "EDIF_TOK_RIPPER", "EDIF_TOK_ROUND", "EDIF_TOK_SCHEMATIC",
+  "EDIF_TOK_STRANGER", "EDIF_TOK_SYMBOLIC", "EDIF_TOK_TEMPERATURE",
+  "EDIF_TOK_TIE", "EDIF_TOK_TIME", "EDIF_TOK_TRUNCATE",
+  "EDIF_TOK_UPPERCENTER", "EDIF_TOK_UPPERLEFT", "EDIF_TOK_UPPERRIGHT",
+  "EDIF_TOK_VOLTAGE", "EDIF_TOK_ACLOAD", "EDIF_TOK_AFTER",
+  "EDIF_TOK_ANNOTATE", "EDIF_TOK_APPLY", "EDIF_TOK_ARC", "EDIF_TOK_ARRAY",
+  "EDIF_TOK_ARRAYMACRO", "EDIF_TOK_ARRAYRELATEDINFO", "EDIF_TOK_ARRAYSITE",
+  "EDIF_TOK_ATLEAST", "EDIF_TOK_ATMOST", "EDIF_TOK_AUTHOR",
+  "EDIF_TOK_BASEARRAY", "EDIF_TOK_BECOMES", "EDIF_TOK_BETWEEN",
+  "EDIF_TOK_BOOLEAN", "EDIF_TOK_BOOLEANDISPLAY", "EDIF_TOK_BOOLEANMAP",
+  "EDIF_TOK_BORDERPATTERN", "EDIF_TOK_BORDERWIDTH", "EDIF_TOK_BOUNDINGBOX",
+  "EDIF_TOK_CELL", "EDIF_TOK_CELLREF", "EDIF_TOK_CELLTYPE",
+  "EDIF_TOK_CHANGE", "EDIF_TOK_CIRCLE", "EDIF_TOK_COLOR",
+  "EDIF_TOK_COMMENT", "EDIF_TOK_COMMENTGRAPHICS", "EDIF_TOK_COMPOUND",
+  "EDIF_TOK_CONNECTLOCATION", "EDIF_TOK_CONTENTS", "EDIF_TOK_CORNERTYPE",
+  "EDIF_TOK_CRITICALITY", "EDIF_TOK_CURRENTMAP", "EDIF_TOK_CURVE",
+  "EDIF_TOK_CYCLE", "EDIF_TOK_DATAORIGIN", "EDIF_TOK_DCFANINLOAD",
+  "EDIF_TOK_DCFANOUTLOAD", "EDIF_TOK_DCMAXFANIN", "EDIF_TOK_DCMAXFANOUT",
+  "EDIF_TOK_DELAY", "EDIF_TOK_DELTA", "EDIF_TOK_DERIVATION",
+  "EDIF_TOK_DESIGN", "EDIF_TOK_DESIGNATOR", "EDIF_TOK_DIFFERENCE",
+  "EDIF_TOK_DIRECTION", "EDIF_TOK_DISPLAY", "EDIF_TOK_DOMINATES",
+  "EDIF_TOK_DOT", "EDIF_TOK_DURATION", "EDIF_TOK_E", "EDIF_TOK_EDIF",
+  "EDIF_TOK_EDIFLEVEL", "EDIF_TOK_EDIFVERSION",
+  "EDIF_TOK_ENCLOSUREDISTANCE", "EDIF_TOK_ENDTYPE", "EDIF_TOK_ENTRY",
+  "EDIF_TOK_EVENT", "EDIF_TOK_EXACTLY", "EDIF_TOK_EXTERNAL",
+  "EDIF_TOK_FABRICATE", "EDIF_TOK_FALSE", "EDIF_TOK_FIGURE",
+  "EDIF_TOK_FIGUREAREA", "EDIF_TOK_FIGUREGROUP",
+  "EDIF_TOK_FIGUREGROUPOBJECT", "EDIF_TOK_FIGUREGROUPOVERRIDE",
+  "EDIF_TOK_FIGUREGROUPREF", "EDIF_TOK_FIGUREPERIMETER",
+  "EDIF_TOK_FIGUREWIDTH", "EDIF_TOK_FILLPATTERN", "EDIF_TOK_FOLLOW",
+  "EDIF_TOK_FORBIDDENEVENT", "EDIF_TOK_GLOBALPORTREF",
+  "EDIF_TOK_GREATERTHAN", "EDIF_TOK_GRIDMAP", "EDIF_TOK_IGNORE",
+  "EDIF_TOK_INCLUDEFIGUREGROUP", "EDIF_TOK_INITIAL", "EDIF_TOK_INSTANCE",
+  "EDIF_TOK_INSTANCEBACKANNOTATE", "EDIF_TOK_INSTANCEGROUP",
+  "EDIF_TOK_INSTANCEMAP", "EDIF_TOK_INSTANCEREF", "EDIF_TOK_INTEGER",
+  "EDIF_TOK_INTEGERDISPLAY", "EDIF_TOK_INTERFACE",
+  "EDIF_TOK_INTERFIGUREGROUPSPACING", "EDIF_TOK_INTERSECTION",
+  "EDIF_TOK_INTRAFIGUREGROUPSPACING", "EDIF_TOK_INVERSE",
+  "EDIF_TOK_ISOLATED", "EDIF_TOK_JOINED", "EDIF_TOK_JUSTIFY",
+  "EDIF_TOK_KEYWORDDISPLAY", "EDIF_TOK_KEYWORDLEVEL",
+  "EDIF_TOK_KEYWORDMAP", "EDIF_TOK_LESSTHAN", "EDIF_TOK_LIBRARY",
+  "EDIF_TOK_LIBRARYREF", "EDIF_TOK_LISTOFNETS", "EDIF_TOK_LISTOFPORTS",
+  "EDIF_TOK_LOADDELAY", "EDIF_TOK_LOGICASSIGN", "EDIF_TOK_LOGICINPUT",
+  "EDIF_TOK_LOGICLIST", "EDIF_TOK_LOGICMAPINPUT",
+  "EDIF_TOK_LOGICMAPOUTPUT", "EDIF_TOK_LOGICONEOF", "EDIF_TOK_LOGICOUTPUT",
+  "EDIF_TOK_LOGICPORT", "EDIF_TOK_LOGICREF", "EDIF_TOK_LOGICVALUE",
+  "EDIF_TOK_LOGICWAVEFORM", "EDIF_TOK_MAINTAIN", "EDIF_TOK_MATCH",
+  "EDIF_TOK_MEMBER", "EDIF_TOK_MINOMAX", "EDIF_TOK_MINOMAXDISPLAY",
+  "EDIF_TOK_MNM", "EDIF_TOK_MULTIPLEVALUESET", "EDIF_TOK_MUSTJOIN",
+  "EDIF_TOK_NAME", "EDIF_TOK_NET", "EDIF_TOK_NETBACKANNOTATE",
+  "EDIF_TOK_NETBUNDLE", "EDIF_TOK_NETDELAY", "EDIF_TOK_NETGROUP",
+  "EDIF_TOK_NETMAP", "EDIF_TOK_NETREF", "EDIF_TOK_NOCHANGE",
+  "EDIF_TOK_NONPERMUTABLE", "EDIF_TOK_NOTALLOWED", "EDIF_TOK_NOTCHSPACING",
+  "EDIF_TOK_NUMBER", "EDIF_TOK_NUMBERDEFINITION", "EDIF_TOK_NUMBERDISPLAY",
+  "EDIF_TOK_OFFPAGECONNECTOR", "EDIF_TOK_OFFSETEVENT",
+  "EDIF_TOK_OPENSHAPE", "EDIF_TOK_ORIENTATION", "EDIF_TOK_ORIGIN",
+  "EDIF_TOK_OVERHANGDISTANCE", "EDIF_TOK_OVERLAPDISTANCE",
+  "EDIF_TOK_OVERSIZE", "EDIF_TOK_OWNER", "EDIF_TOK_PAGE",
+  "EDIF_TOK_PAGESIZE", "EDIF_TOK_PARAMETER", "EDIF_TOK_PARAMETERASSIGN",
+  "EDIF_TOK_PARAMETERDISPLAY", "EDIF_TOK_PATH", "EDIF_TOK_PATHDELAY",
+  "EDIF_TOK_PATHWIDTH", "EDIF_TOK_PERMUTABLE",
+  "EDIF_TOK_PHYSICALDESIGNRULE", "EDIF_TOK_PLUG", "EDIF_TOK_POINT",
+  "EDIF_TOK_POINTDISPLAY", "EDIF_TOK_POINTLIST", "EDIF_TOK_POLYGON",
+  "EDIF_TOK_PORT", "EDIF_TOK_PORTBACKANNOTATE", "EDIF_TOK_PORTBUNDLE",
+  "EDIF_TOK_PORTDELAY", "EDIF_TOK_PORTGROUP",
+  "EDIF_TOK_PORTIMPLEMENTATION", "EDIF_TOK_PORTINSTANCE",
+  "EDIF_TOK_PORTLIST", "EDIF_TOK_PORTLISTALIAS", "EDIF_TOK_PORTMAP",
+  "EDIF_TOK_PORTREF", "EDIF_TOK_PROGRAM", "EDIF_TOK_PROPERTY",
+  "EDIF_TOK_PROPERTYDISPLAY", "EDIF_TOK_PROTECTIONFRAME", "EDIF_TOK_PT",
+  "EDIF_TOK_RANGEVECTOR", "EDIF_TOK_RECTANGLE", "EDIF_TOK_RECTANGLESIZE",
+  "EDIF_TOK_RENAME", "EDIF_TOK_RESOLVES", "EDIF_TOK_SCALE",
+  "EDIF_TOK_SCALEX", "EDIF_TOK_SCALEY", "EDIF_TOK_SECTION",
+  "EDIF_TOK_SHAPE", "EDIF_TOK_SIMULATE", "EDIF_TOK_SIMULATIONINFO",
+  "EDIF_TOK_SINGLEVALUESET", "EDIF_TOK_SITE", "EDIF_TOK_SOCKET",
+  "EDIF_TOK_SOCKETSET", "EDIF_TOK_STATUS", "EDIF_TOK_STEADY",
+  "EDIF_TOK_STRING", "EDIF_TOK_STRINGDISPLAY", "EDIF_TOK_STRONG",
+  "EDIF_TOK_SYMBOL", "EDIF_TOK_SYMMETRY", "EDIF_TOK_TABLE",
+  "EDIF_TOK_TABLEDEFAULT", "EDIF_TOK_TECHNOLOGY", "EDIF_TOK_TEXTHEIGHT",
+  "EDIF_TOK_TIMEINTERVAL", "EDIF_TOK_TIMESTAMP", "EDIF_TOK_TIMING",
+  "EDIF_TOK_TRANSFORM", "EDIF_TOK_TRANSITION", "EDIF_TOK_TRIGGER",
+  "EDIF_TOK_TRUE", "EDIF_TOK_UNCONSTRAINED", "EDIF_TOK_UNDEFINED",
+  "EDIF_TOK_UNION", "EDIF_TOK_UNIT", "EDIF_TOK_UNUSED",
+  "EDIF_TOK_USERDATA", "EDIF_TOK_VERSION", "EDIF_TOK_VIEW",
+  "EDIF_TOK_VIEWLIST", "EDIF_TOK_VIEWMAP", "EDIF_TOK_VIEWREF",
+  "EDIF_TOK_VIEWTYPE", "EDIF_TOK_VISIBLE", "EDIF_TOK_VOLTAGEMAP",
+  "EDIF_TOK_WAVEVALUE", "EDIF_TOK_WEAK", "EDIF_TOK_WEAKJOINED",
+  "EDIF_TOK_WHEN", "EDIF_TOK_WRITTEN", "')'", "$accept", "PopC", "Edif",
+  "_Edif", "EdifFileName", "EdifLevel", "EdifVersion", "AcLoad", "_AcLoad",
+  "After", "_After", "Annotate", "_Annotate", "Apply", "_Apply", "Arc",
+  "Array", "_Array", "ArrayMacro", "ArrayRelInfo", "_ArrayRelInfo",
+  "ArraySite", "AtLeast", "AtMost", "Author", "BaseArray", "Becomes",
+  "_Becomes", "Between", "__Between", "_Between", "Boolean", "_Boolean",
+  "BooleanDisp", "_BooleanDisp", "BooleanMap", "BooleanValue", "BorderPat",
+  "BorderWidth", "BoundBox", "Cell", "_Cell", "CellNameDef", "CellRef",
+  "_CellRef", "CellNameRef", "CellType", "_CellType", "Change", "__Change",
+  "_Change", "Circle", "_Circle", "Color", "Comment", "_Comment",
+  "CommGraph", "_CommGraph", "Compound", "Contents", "_Contents",
+  "ConnectLoc", "_ConnectLoc", "CornerType", "_CornerType", "Criticality",
+  "_Criticality", "CurrentMap", "Curve", "_Curve", "Cycle", "_Cycle",
+  "DataOrigin", "_DataOrigin", "DcFanInLoad", "_DcFanInLoad",
+  "DcFanOutLoad", "_DcFanOutLoad", "DcMaxFanIn", "_DcMaxFanIn",
+  "DcMaxFanOut", "_DcMaxFanOut", "Delay", "_Delay", "Delta", "_Delta",
+  "Derivation", "_Derivation", "Design", "_Design", "Designator",
+  "_Designator", "DesignNameDef", "DesignRule", "_DesignRule",
+  "Difference", "_Difference", "Direction", "_Direction", "Display",
+  "_Display", "_DisplayJust", "_DisplayOrien", "_DisplayOrg", "Dominates",
+  "_Dominates", "Dot", "_Dot", "Duration", "EncloseDist", "_EncloseDist",
+  "EndType", "_EndType", "Entry", "___Entry", "__Entry", "_Entry", "Event",
+  "_Event", "Exactly", "External", "_External", "Fabricate", "False",
+  "FigGrp", "_FigGrp", "FigGrpNameDef", "FigGrpNameRef", "FigGrpObj",
+  "_FigGrpObj", "FigGrpOver", "_FigGrpOver", "FigGrpRef", "_FigGrpRef",
+  "Figure", "_Figure", "FigureArea", "_FigureArea", "FigureOp",
+  "FigurePerim", "_FigurePerim", "FigureWidth", "_FigureWidth",
+  "FillPattern", "Follow", "__Follow", "_Follow", "Forbidden",
+  "_Forbidden", "Form", "_Form", "GlobPortRef", "GreaterThan", "GridMap",
+  "Ignore", "IncFigGrp", "_IncFigGrp", "Initial", "Instance", "_Instance",
+  "InstanceRef", "_InstanceRef", "InstBackAn", "_InstBackAn", "InstGroup",
+  "_InstGroup", "InstMap", "_InstMap", "InstNameDef", "InstNameRef",
+  "IntDisplay", "_IntDisplay", "Integer", "_Integer", "Interface",
+  "_Interface", "InterFigGrp", "_InterFigGrp", "Intersection",
+  "_Intersection", "IntraFigGrp", "_IntraFigGrp", "Inverse", "_Inverse",
+  "Isolated", "Joined", "_Joined", "Justify", "_Justify", "KeywordDisp",
+  "_KeywordDisp", "KeywordLevel", "KeywordMap", "_KeywordMap",
+  "KeywordName", "LayerNameDef", "LessThan", "LibNameDef", "LibNameRef",
+  "Library", "_Library", "LibraryRef", "ListOfNets", "_ListOfNets",
+  "ListOfPorts", "_ListOfPorts", "LoadDelay", "_LoadDelay", "LogicAssn",
+  "___LogicAssn", "__LogicAssn", "_LogicAssn", "LogicIn", "_LogicIn",
+  "LogicList", "_LogicList", "LogicMapIn", "_LogicMapIn", "LogicMapOut",
+  "_LogicMapOut", "LogicNameDef", "LogicNameRef", "LogicOneOf",
+  "_LogicOneOf", "LogicOut", "_LogicOut", "LogicPort", "_LogicPort",
+  "LogicRef", "_LogicRef", "LogicValue", "_LogicValue", "LogicWave",
+  "_LogicWave", "Maintain", "__Maintain", "_Maintain", "Match", "__Match",
+  "_Match", "Member", "_Member", "MiNoMa", "_MiNoMa", "MiNoMaDisp",
+  "_MiNoMaDisp", "MiNoMaValue", "Mnm", "_Mnm", "MultValSet", "_MultValSet",
+  "MustJoin", "_MustJoin", "Name", "_Name", "NameDef", "NameRef", "Net",
+  "_Net", "NetBackAn", "_NetBackAn", "NetBundle", "_NetBundle", "NetDelay",
+  "_NetDelay", "NetGroup", "_NetGroup", "NetMap", "_NetMap", "NetNameDef",
+  "NetNameRef", "NetRef", "_NetRef", "NoChange", "NonPermut", "_NonPermut",
+  "NotAllowed", "_NotAllowed", "NotchSpace", "_NotchSpace", "Number",
+  "_Number", "NumbDisplay", "_NumbDisplay", "NumberDefn", "_NumberDefn",
+  "OffPageConn", "_OffPageConn", "OffsetEvent", "OpenShape", "_OpenShape",
+  "Orientation", "_Orientation", "Origin", "OverhngDist", "_OverhngDist",
+  "OverlapDist", "_OverlapDist", "Oversize", "_Oversize", "Owner", "Page",
+  "_Page", "PageSize", "ParamDisp", "_ParamDisp", "Parameter",
+  "_Parameter", "ParamAssign", "Path", "_Path", "PathDelay", "_PathDelay",
+  "PathWidth", "Permutable", "_Permutable", "Plug", "_Plug", "Point",
+  "_Point", "PointDisp", "_PointDisp", "PointList", "_PointList",
+  "PointValue", "Polygon", "_Polygon", "Port", "_Port", "PortBackAn",
+  "_PortBackAn", "PortBundle", "_PortBundle", "PortDelay", "_PortDelay",
+  "PortGroup", "_PortGroup", "PortImpl", "_PortImpl", "PortInst",
+  "_PortInst", "PortList", "_PortList", "PortListAls", "PortMap",
+  "_PortMap", "PortNameDef", "PortNameRef", "PortRef", "_PortRef",
+  "Program", "_Program", "PropDisplay", "_PropDisplay", "Property",
+  "_Property", "PropNameDef", "PropNameRef", "ProtectFrame",
+  "_ProtectFrame", "Range", "RangeVector", "_RangeVector", "Rectangle",
+  "_Rectangle", "RectSize", "_RectSize", "Rename", "__Rename", "_Rename",
+  "Resolves", "_Resolves", "RuleNameDef", "Scale", "ScaledInt", "ScaleX",
+  "ScaleY", "Section", "_Section", "Shape", "_Shape", "SimNameDef",
+  "Simulate", "_Simulate", "SimulInfo", "_SimulInfo", "SingleValSet",
+  "_SingleValSet", "Site", "_Site", "Socket", "_Socket", "SocketSet",
+  "_SocketSet", "Status", "_Status", "Steady", "__Steady", "_Steady",
+  "StrDisplay", "String", "_String", "_StrDisplay", "Strong", "Symbol",
+  "_Symbol", "Symmetry", "_Symmetry", "Table", "_Table", "TableDeflt",
+  "__TableDeflt", "_TableDeflt", "Technology", "_Technology", "TextHeight",
+  "TimeIntval", "__TimeIntval", "_TimeIntval", "TimeStamp", "Timing",
+  "_Timing", "Transform", "_TransX", "_TransY", "_TransDelta",
+  "_TransOrien", "_TransOrg", "Transition", "_Transition", "Trigger",
+  "_Trigger", "True", "TypedValue", "Unconstrained", "Undefined", "Union",
+  "_Union", "Unit", "_Unit", "Unused", "UserData", "_UserData",
+  "ValueNameDef", "ValueNameRef", "Version", "View", "_View", "ViewList",
+  "_ViewList", "ViewMap", "_ViewMap", "ViewNameDef", "ViewNameRef",
+  "ViewRef", "_ViewRef", "ViewType", "_ViewType", "Visible", "VoltageMap",
+  "WaveValue", "Weak", "WeakJoined", "_WeakJoined", "When", "_When",
+  "Written", "_Written", "Ident", "Str", "Int", "Keyword", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+   (internal) symbol number NUM (which must be that of a token).  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
+     305,   306,   307,   308,   309,   310,   311,   312,   313,   314,
+     315,   316,   317,   318,   319,   320,   321,   322,   323,   324,
+     325,   326,   327,   328,   329,   330,   331,   332,   333,   334,
+     335,   336,   337,   338,   339,   340,   341,   342,   343,   344,
+     345,   346,   347,   348,   349,   350,   351,   352,   353,   354,
+     355,   356,   357,   358,   359,   360,   361,   362,   363,   364,
+     365,   366,   367,   368,   369,   370,   371,   372,   373,   374,
+     375,   376,   377,   378,   379,   380,   381,   382,   383,   384,
+     385,   386,   387,   388,   389,   390,   391,   392,   393,   394,
+     395,   396,   397,   398,   399,   400,   401,   402,   403,   404,
+     405,   406,   407,   408,   409,   410,   411,   412,   413,   414,
+     415,   416,   417,   418,   419,   420,   421,   422,   423,   424,
+     425,   426,   427,   428,   429,   430,   431,   432,   433,   434,
+     435,   436,   437,   438,   439,   440,   441,   442,   443,   444,
+     445,   446,   447,   448,   449,   450,   451,   452,   453,   454,
+     455,   456,   457,   458,   459,   460,   461,   462,   463,   464,
+     465,   466,   467,   468,   469,   470,   471,   472,   473,   474,
+     475,   476,   477,   478,   479,   480,   481,   482,   483,   484,
+     485,   486,   487,   488,   489,   490,   491,   492,   493,   494,
+     495,   496,   497,   498,   499,   500,   501,   502,   503,   504,
+     505,   506,   507,   508,   509,   510,   511,   512,   513,   514,
+     515,   516,   517,   518,   519,   520,   521,   522,   523,   524,
+     525,   526,   527,   528,   529,   530,   531,   532,   533,   534,
+     535,   536,   537,   538,   539,   540,   541,   542,   543,   544,
+     545,   546,   547,   548,   549,    41
+};
+# endif
+
+#define YYPACT_NINF -1333
+
+#define yypact_value_is_default(Yystate) \
+  (!!((Yystate) == (-1333)))
+
+#define YYTABLE_NINF -1
+
+#define yytable_value_is_error(Yytable_value) \
+  0
+
+  /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+     STATE-NUM.  */
+static const yytype_int16 yypact[] =
+{
+      41,   152,   149, -1333,   176,    81,   305, -1333, -1333, -1333,
+   -1333, -1333,    33, -1333, -1333,    59, -1333,   148,   317,   101,
+   -1333, -1333, -1333, -1333,   437,   150, -1333, -1333, -1333,   148,
+     148,   304,    81,   312, -1333, -1333, -1333, -1333, -1333,    33,
+   -1333, -1333,   148,   150,   316, -1333, -1333,  1508,  1780,   272,
+   -1333, -1333, -1333,   150, -1333,   148, -1333,   122,   715,   148,
+     148,   111, -1333,   997,  1020,   148,   148,   152,   148,   176,
+     300, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333,   150,  1588,   280, -1333, -1333,   150, -1333,
+   -1333,   152,   152,   152, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333,   148,   150,   148,   111, -1333,    36, -1333, -1333,
+   -1333,   150, -1333, -1333, -1333,   150,   148,   150, -1333,  1023,
+     150,    66, -1333,   150,   150,   150, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333,   150,   260,   150,
+   -1333, -1333,   424, -1333,   317, -1333,   317,   273,   439, -1333,
+     148,   111, -1333, -1333, -1333, -1333,   439, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   310,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333,   148,   150, -1333,    81, -1333,   441,
+     254,   254,   266, -1333, -1333, -1333, -1333,   150,   150,   150,
+     150,   416,    49,   115,    91,   801,    77,   437,  2006, -1333,
+   -1333, -1333, -1333, -1333,    71,   148, -1333,   376, -1333, -1333,
+   -1333, -1333, -1333, -1333,   343,   445, -1333,   445, -1333,   148,
+   -1333,   158, -1333, -1333, -1333, -1333,   300, -1333, -1333, -1333,
+   -1333,   148, -1333, -1333, -1333, -1333,   276,   144, -1333, -1333,
+   -1333, -1333, -1333, -1333,   111, -1333, -1333, -1333, -1333,   260,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   150, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,   150, -1333, -1333, -1333, -1333,
+   -1333,   150,    81,   150, -1333, -1333, -1333,   546,   152, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   148,
+     437,   437,   437, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+      33, -1333,    33, -1333,    33, -1333,   150,   150,   144, -1333,
+   -1333, -1333,    33, -1333,    33, -1333, -1333, -1333, -1333,   150,
+   -1333, -1333,   132,   152,   152, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333,   468, -1333,   148,   150,   306,   306,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   144, -1333,
+   -1333, -1333, -1333, -1333,   111,   111, -1333, -1333, -1333, -1333,
+      81, -1333,  1442, -1333, -1333,  1794,   519,   832,   940, -1333,
+     148, -1333,   437,   150, -1333,   150, -1333,   150,   111,   111,
+     150,  1196, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333,   152,   152,   152,   152,
+     152,   152,   152,   152,   152,   152,   152, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333,   152, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+     150,   152, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+     148,   150, -1333, -1333, -1333,   150,   321, -1333,  1196,    81,
+    1196,  1196,   148,  1196, -1333, -1333, -1333,   150, -1333, -1333,
+   -1333, -1333, -1333,   461,   461,   461,   461,   461,   461,   461,
+     461,   461,   461,   461, -1333,  1198, -1333, -1333, -1333,   322,
+     867,   148, -1333, -1333,   150,  1263, -1333, -1333,   376, -1333,
+   -1333,  1263, -1333, -1333,   150,  1196, -1333, -1333,  1263, -1333,
+     342,   461,  1545,  1545,  1545,   461,  1545, -1333,   130,  1545,
+     461,   461,   -29,   300,    81,   276, -1333,   150, -1333, -1333,
+   -1333,    81,   276,    81, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,  1879,
+     444,   466, -1333,   425, -1333,   382, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333,   150, -1333, -1333, -1333,
+   -1333,   150, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   540,
+   -1333, -1333, -1333, -1333,   150, -1333, -1333,  1545,   111,   111,
+      42,   111,   111,   111,  1276, -1333, -1333, -1333, -1333,   130,
+   -1333, -1333, -1333, -1333,   130, -1333, -1333,   130, -1333, -1333,
+    1545,   130, -1333, -1333, -1333, -1333, -1333,   130, -1333, -1333,
+    1545,  1545, -1333, -1333, -1333, -1333,   130,   150,   150, -1333,
+     150,    63, -1333,    63,    63,    63,   150,   150,   150, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   150,
+   -1333, -1333,   186,   529, -1333,   653,   833,   529,   605, -1333,
+     171,   529,   581, -1333,   783, -1333, -1333,   150, -1333,   130,
+   -1333, -1333,   150,   150, -1333,    53, -1333,   150,   150,   150,
+   -1333,   150, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333,   130, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+     130, -1333, -1333,   130, -1333, -1333,   -75,   361, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333,   990, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333,    81,   -34, -1333, -1333,    59,   548,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333,   442,   460,   548, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   539,
+     523,    93,    93,    93,    93,   548, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   150,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   543,
+   -1333, -1333,   108, -1333,   108,   108, -1333,   152, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333,  1584,   148,    81,
+   -1333,   150, -1333,   150, -1333, -1333,   894, -1333,   708,   252,
+   -1333, -1333,   150, -1333,   150, -1333, -1333,   561,    46, -1333,
+   -1333,   150, -1333,   150, -1333, -1333,   150, -1333, -1333,   150,
+   -1333, -1333,   150, -1333, -1333,   150, -1333, -1333,    26,    86,
+   -1333,   431,   421,   150, -1333,   130, -1333, -1333,   531,   281,
+     152, -1333, -1333,  1023,   -73, -1333, -1333,   631, -1333,   512,
+     762, -1333, -1333,   481,   365,   588,   454, -1333,    76,   104,
+     108,   108,   108,   108,   108,   108,   104,   437,   418, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333,    51, -1333, -1333,
+     424, -1333, -1333, -1333, -1333, -1333,   150,   436,   561, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   523, -1333,
+     -31, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+     523, -1333, -1333,   -31, -1333, -1333, -1333, -1333,   150,   453,
+     150, -1333, -1333, -1333, -1333,   529, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333,   148,   321, -1333, -1333,
+   -1333, -1333, -1333,   711,   150, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+     726,   176,   529,    81, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333,   620,   108,   152, -1333,
+   -1333, -1333, -1333, -1333, -1333,    59,   454, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,   260,   150,   691, -1333, -1333,
+     751,   -85, -1333, -1333, -1333,  -115, -1333,   726, -1333, -1333,
+   -1333,   568,   562,   635, -1333, -1333,   449,  1253, -1333, -1333,
+      97, -1333, -1333, -1333,   448, -1333, -1333, -1333,   150, -1333,
+     384,   -24, -1333, -1333,   -24,   150, -1333, -1333,   950,   950,
+   -1333, -1333, -1333,   523, -1333, -1333, -1333, -1333, -1333,  -122,
+   -1333, -1333,   150, -1333, -1333,   150,    86,   148,   150, -1333,
+     -77, -1333, -1333, -1333,   150, -1333,   -90, -1333, -1333, -1333,
+   -1333,    33, -1333, -1333, -1333, -1333,    33, -1333, -1333,    33,
+   -1333,   148,   409, -1333,   530,   111,   150, -1333, -1333,   150,
+     260, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+     260,   260,   675,   553,   553,   675, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333,    99, -1333, -1333,
+   -1333,   812, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   679,
+   -1333, -1333,   946, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   651, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333,    34,   276,   104,   104, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,  1014,   671, -1333, -1333,   634,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333,   150, -1333, -1333,
+   -1333, -1333, -1333, -1333,   950,   150,   453, -1333, -1333,  -119,
+   -1333, -1333, -1333, -1333, -1333,   150, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333,   684,   255,   255, -1333, -1333, -1333, -1333, -1333,   150,
+     624, -1333, -1333, -1333,  -117,   260,  -117, -1333, -1333, -1333,
+    -117, -1333,  -117, -1333, -1333,  -117, -1333,  -117,   269, -1333,
+   -1333, -1333,   428,   529,   104,   560, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,   -98, -1333, -1333, -1333, -1333,
+   -1333, -1333,   150, -1333,   255,   150,   255, -1333, -1333, -1333,
+   -1333,   739, -1333,   -64, -1333, -1333,    26, -1333, -1333,   -31,
+   -1333, -1333, -1333, -1333, -1333,   111,   111, -1333, -1333, -1333,
+     150,   114,    64, -1333,   150, -1333,  -127, -1333, -1333, -1333,
+   -1333,   150, -1333,   -52, -1333, -1333, -1333,   -52, -1333, -1333,
+   -1333, -1333, -1333,   150, -1333, -1333, -1333, -1333, -1333,    28,
+   -1333, -1333,   -46, -1333, -1333, -1333, -1333, -1333, -1333,   383,
+     890, -1333, -1333, -1333, -1333, -1333, -1333,  1023,   657, -1333,
+   -1333,   148, -1333,   564, -1333, -1333, -1333,    11, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,   684, -1333, -1333, -1333, -1333,
+   -1333, -1333,    81,    26, -1333, -1333, -1333, -1333, -1333,   150,
+   -1333, -1333, -1333,   150,   150, -1333,   150, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,   534, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333,   113, -1333,  -117,   260, -1333, -1333, -1333,
+   -1333, -1333,   255, -1333,   890, -1333, -1333,   150, -1333, -1333,
+   -1333, -1333,    26,   150, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333, -1333,   148,   148, -1333,   725,
+   -1333,   150, -1333, -1333,   -31,   376, -1333, -1333,   150, -1333,
+   -1333, -1333, -1333,   577, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333,   260,   950, -1333, -1333, -1333,    26, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333,   150, -1333,   150,   148, -1333, -1333,
+     272, -1333, -1333, -1333, -1333, -1333,   150, -1333,   150, -1333,
+     260, -1333, -1333, -1333,   150, -1333,   150, -1333, -1333, -1333,
+     150,   -46, -1333,   280, -1333, -1333,   150, -1333, -1333, -1333,
+   -1333, -1333, -1333,   150, -1333, -1333
+};
+
+  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+     Performed when YYTABLE does not specify something else to do.  Zero
+     means the default is an error.  */
+static const yytype_uint16 yydefact[] =
+{
+       0,     0,     0,  1126,     0,     0,     0,   590,    11,   591,
+     589,     1,     0,   587,   883,     0,   882,     0,     0,     0,
+       2,   586,   588,  1127,     0,     0,   885,   884,  1128,     0,
+       0,     0,     0,   213,   211,   212,   593,   279,   592,     0,
+     946,   881,     0,     0,     0,     4,   285,     0,     0,   215,
+     214,   940,   947,     0,    12,     0,   457,     0,     0,     0,
+       0,     0,   101,     0,     0,     0,     0,     0,     0,     0,
+       0,   284,   292,   289,   290,   295,   286,   287,   291,   288,
+     296,   293,   297,   294,   443,   444,   445,   446,   447,   448,
+     449,   450,   451,     0,     0,   217,   216,    13,     0,   456,
+     458,     0,     0,     0,   929,     3,     9,     8,     6,     7,
+       5,    10,     0,     0,     0,     0,   891,     0,   136,   137,
+     138,     0,   232,   233,   234,     0,     0,     0,   848,     0,
+       0,     0,  1054,     0,     0,     0,    68,    67,   442,   683,
+     686,   684,   685,   679,   681,   682,   680,     0,     0,     0,
+     218,   455,     0,   184,     0,   462,     0,     0,     0,    70,
+       0,     0,   100,   102,   135,   231,     0,   727,    59,   396,
+     564,   656,   737,   942,  1022,  1023,  1024,  1025,  1026,     0,
+    1027,   843,   987,  1129,  1053,  1058,  1057,  1056,  1055,   345,
+     262,  1021,  1101,   678,     0,     0,   210,     0,   176,     0,
+       0,     0,     0,   928,   931,   932,   930,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   842,
+     847,   844,   846,   845,     0,     0,   687,    82,    84,   175,
+     178,   179,   177,   180,     0,     0,   256,     0,   465,     0,
+    1119,     0,    69,   892,    99,   333,     0,    58,    62,    61,
+      60,     0,   395,   398,   399,   397,     0,     0,   563,   567,
+     566,   565,   571,   572,     0,   655,   659,   658,   657,     0,
+     736,   740,   739,   738,   941,   944,   945,   943,     0,  1051,
+    1037,  1048,  1049,  1038,  1036,  1046,  1050,  1044,  1045,  1043,
+    1047,  1039,  1040,  1041,  1042,     0,   344,   349,   348,   347,
+     346,     0,     0,     0,    83,   664,   980,     0,     0,   255,
+     258,   259,   257,   260,   464,   467,   468,   466,   469,     0,
+       0,     0,     0,  1118,  1120,  1124,  1122,  1121,  1123,  1125,
+       0,    64,     0,   393,     0,   569,     0,     0,     0,   574,
+     576,   575,     0,   661,     0,   742,   701,  1035,   747,     0,
+     463,    81,     0,     0,     0,   186,   912,   979,   985,   984,
+     982,   981,   983,   986,     0,    80,     0,     0,   151,   837,
+      63,    65,   392,   394,   568,   570,  1028,  1029,     0,   660,
+     662,   741,   743,   470,     0,     0,   663,   667,   666,   665,
+       0,   460,     0,   264,   278,     0,     0,     0,     0,    73,
+       0,    47,     0,     0,   152,     0,   838,     0,     0,     0,
+       0,     0,   263,   271,   268,   269,   274,   265,   266,   270,
+     277,   267,   275,   272,   276,   273,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   185,   199,   193,
+     198,   188,   190,   187,   194,   195,   197,   196,   192,   191,
+     189,   200,     0,   911,   914,   913,   915,    88,    87,    86,
+       0,     0,  1076,    72,    77,    79,    74,    78,    76,    75,
+       0,     0,   150,   836,   573,     0,     0,   261,     0,     0,
+       0,     0,     0,     0,   320,   355,   356,     0,   318,   321,
+     322,   319,   889,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   526,     0,   506,    85,  1085,     0,
+       0,     0,  1063,   352,     0,     0,   202,   203,   299,   424,
+     425,     0,   434,   435,     0,     0,  1031,  1032,     0,   354,
+       0,     0,     0,     0,     0,     0,     0,   647,     0,     0,
+       0,     0,     0,     0,     0,     0,   220,     0,   501,   504,
+     887,     0,     0,     0,   525,   529,   539,   530,   528,   533,
+     536,   535,   534,   538,   537,   532,   540,   527,   531,     0,
+       0,     0,   383,     0,   627,     0,   822,  1075,  1083,  1080,
+    1079,  1082,  1081,  1078,  1077,  1084,     0,   890,   201,   204,
+     205,     0,   300,   423,   426,   427,   433,   699,   700,     0,
+    1030,  1033,  1034,   281,     0,   282,   283,     0,     0,     0,
+       0,     0,     0,     0,   917,   866,   865,   868,   867,     0,
+     864,   863,   314,   315,     0,   324,   325,     0,   329,   330,
+       0,     0,   429,   430,   646,   648,   649,     0,   651,   652,
+       0,     0,   578,   870,   878,   877,     0,     0,     0,   507,
+       0,     0,   436,     0,     0,     0,     0,     0,     0,  1096,
+    1098,  1099,  1097,  1091,  1093,  1092,  1094,  1100,  1095,     0,
+     401,  1065,     0,     0,   374,     0,     0,     0,     0,   606,
+       0,     0,     0,   766,     0,   994,   298,     0,   280,     0,
+     227,   228,     0,     0,    54,     0,    55,     0,     0,     0,
+     918,     0,   313,   316,   317,   323,   326,   327,   328,   331,
+     332,     0,   419,   420,   428,   431,   432,   650,   653,   654,
+       0,   689,   690,     0,   694,   695,     0,     0,   876,   879,
+     880,    66,   112,   142,   219,   221,   500,   502,   503,   505,
+     886,   888,   948,  1102,  1104,  1090,     0,   114,  1064,  1068,
+    1067,  1069,  1066,  1070,     0,   370,   391,   390,     0,     0,
+     373,   378,   375,   377,   376,   380,   382,   386,   384,   385,
+     387,   635,   634,   637,     0,     0,   605,   610,   608,   607,
+     609,   623,   626,   630,   629,   628,   631,   830,   829,   832,
+       0,     0,     0,     0,     0,     0,   765,   773,   775,   769,
+     770,   771,   772,   767,   768,   774,   787,   821,   825,   824,
+     823,   826,   698,   226,   229,   230,    45,    46,    56,     0,
+      57,   254,   351,   461,   916,   418,   421,   422,   688,   691,
+     692,   693,   696,   697,   577,   579,   869,   871,   872,     0,
+     438,   581,     0,   729,     0,     0,   851,     0,   950,  1106,
+     400,   406,   416,   414,   408,   409,   407,   411,   402,   403,
+     415,   405,   413,   404,   412,   417,   410,     0,     0,     0,
+     371,     0,   372,     0,   183,   182,     0,   996,     0,     0,
+     639,   638,     0,   640,     0,   141,   140,     0,     0,   834,
+     833,     0,   835,     0,    16,    15,     0,   155,   154,     0,
+     158,   157,     0,   161,   160,     0,   164,   163,     0,     0,
+      53,     0,     0,     0,    41,     0,    40,    39,     0,     0,
+       0,  1060,  1059,     0,     0,   828,   827,     0,   752,     0,
+       0,   903,   905,     0,     0,     0,     0,   104,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   113,
+     129,   130,   122,   117,   127,   115,   128,   119,   120,   116,
+     121,   123,   118,   125,   124,   131,   126,     0,   561,  1086,
+    1088,   369,   181,   172,   173,   174,     0,     0,     0,   995,
+     999,   998,   997,  1000,   379,   381,   636,   139,     0,   619,
+       0,   622,   624,   625,   831,    14,   153,   156,   159,   162,
+       0,   782,   783,     0,   786,   788,   789,   734,     0,   923,
+       0,    48,    38,    42,    43,     0,   817,   437,   441,   440,
+     439,   580,   585,   583,   582,   584,     0,   718,   643,   728,
+     732,   731,   730,     0,     0,   751,   761,   763,   757,   758,
+     759,   760,   756,   753,   755,   762,   754,   764,   475,   777,
+       0,     0,     0,     0,   850,   856,   861,   855,   853,   854,
+     858,   859,   852,   857,   860,   862,     0,     0,     0,   904,
+     908,   909,   906,   910,   907,     0,     0,   949,   955,   957,
+     962,   954,   952,   953,   959,   956,   960,   951,   958,   961,
+     963,  1105,  1109,  1108,  1107,     0,     0,     0,   302,   303,
+       0,     0,   335,   336,   389,     0,   388,     0,   518,   633,
+     632,     0,     0,     0,   669,   703,     0,     0,   792,   791,
+       0,   896,  1017,  1111,     0,   560,   562,  1089,     0,   171,
+       0,     0,   342,   725,     0,     0,   167,   166,     0,     0,
+     618,   621,   620,     0,   480,   479,   781,   785,   784,     0,
+      37,   965,     0,   924,    44,     0,     0,    35,     0,   719,
+       0,   207,   208,   209,     0,  1052,     0,   776,   779,   778,
+     780,     0,   453,   459,  1062,  1061,     0,   715,   849,     0,
+     840,     0,     0,    28,     0,     0,     0,    26,    25,     0,
+       0,    71,   103,   105,   108,   110,   106,   107,   109,   111,
+       0,     0,     0,     0,     0,     0,   301,   304,   311,   305,
+     306,   307,   308,   309,   310,   312,   968,     0,   337,   338,
+    1072,     0,   360,   359,   517,   520,   519,   521,   595,     0,
+     472,   612,     0,   668,   672,   671,   670,   673,   702,   710,
+     711,   707,   704,   705,   706,   709,   708,   712,   133,   790,
+     801,   796,   793,   794,   795,   798,   797,   799,   800,   895,
+     899,   897,   898,     0,     0,     0,     0,  1110,  1112,  1116,
+    1113,  1115,  1114,  1117,  1087,     0,     0,   989,   990,     0,
+     341,   343,   724,   726,   165,   496,   509,     0,    51,    50,
+      52,  1014,  1013,  1015,     0,     0,     0,   733,   735,     0,
+     922,   350,   816,   819,   818,     0,    36,   717,   642,   645,
+     644,   206,   474,   476,   477,   452,   454,   714,   716,   839,
+     841,   148,     0,     0,    27,    31,    29,    30,    32,     0,
+       0,    24,   713,   874,     0,     0,     0,   223,   144,   676,
+       0,   745,     0,   722,   749,     0,   901,     0,     0,   334,
+     339,   340,     0,     0,     0,  1002,   358,   367,   365,   362,
+     363,   366,   364,   361,   368,   594,   603,   601,   596,   598,
+     600,   599,   597,   602,   604,     0,   611,   616,   614,   613,
+     615,   617,     0,   134,     0,     0,     0,  1016,  1018,  1020,
+    1019,     0,    18,     0,   482,   483,   550,   548,   549,     0,
+     251,   250,   249,   248,   247,     0,     0,   993,   991,   992,
+       0,     0,     0,    49,     0,   478,     0,   926,   964,   966,
+      34,     0,   149,     0,   491,   493,   492,     0,   513,   515,
+     514,   820,   542,     0,   873,   875,    97,   222,   224,     0,
+     675,   677,     0,   721,   723,   748,   750,   900,   902,     0,
+       0,   967,   969,   970,  1071,  1074,  1073,     0,     0,   804,
+     803,     0,  1003,  1004,   471,   473,   132,    93,    92,    90,
+      91,   357,   936,   934,   935,     0,    17,    22,    19,    21,
+      20,    23,     0,   487,   485,   484,   486,   551,   552,     0,
+     246,   253,   252,     0,     0,   988,     0,   495,   499,   497,
+     498,   508,   511,   510,  1012,     0,   925,   927,   147,   490,
+     494,   512,   516,     0,  1103,     0,     0,   143,   145,   146,
+     744,   746,     0,   237,     0,   236,   238,     0,   972,   974,
+     973,   975,   976,     0,   802,   812,   814,   808,   809,   810,
+     811,   807,   806,   813,   805,   815,     0,     0,  1005,  1006,
+      94,     0,    95,   937,     0,   523,   488,   489,     0,   547,
+     674,   225,   353,   920,   541,   546,   544,   543,   545,    96,
+      98,     0,     0,   556,   554,   555,   243,   239,   241,   240,
+     242,   641,   977,   978,     0,   720,     0,     0,   169,  1007,
+    1008,    89,   933,   939,   938,   524,     0,   481,     0,   921,
+       0,   558,   557,   559,     0,   244,     0,   245,   971,   893,
+       0,     0,  1009,  1010,   522,   919,     0,   553,   235,   894,
+     168,   170,  1011,     0,    33,  1001
+};
+
+  /* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+   -1333,   -25, -1333, -1333, -1333,     8, -1333,  -906, -1333, -1333,
+   -1333,  -265, -1333, -1333, -1333, -1333,   173, -1333, -1333, -1333,
+   -1333, -1333,   223,   139, -1333, -1333,  -996, -1333, -1333, -1333,
+   -1333,   645, -1333, -1333, -1333, -1333,  -168,   470,   473,  -285,
+     599, -1333, -1333,   -91, -1333, -1333, -1333, -1333,  -557, -1333,
+   -1333, -1333, -1333,   504,  1011, -1333,  -878, -1333, -1333, -1333,
+   -1333, -1333, -1333,  -310, -1333,  -316, -1333, -1333,  -291, -1333,
+   -1333, -1333, -1333, -1333,  -902, -1333,  -901, -1333,  -896, -1333,
+    -893, -1333,  -891, -1333, -1333, -1333,  -630, -1333, -1333, -1333,
+    -666, -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,   -26,
+   -1333, -1333, -1333, -1333, -1333, -1333, -1333, -1333,  -890, -1333,
+   -1333,   527, -1333, -1333, -1333, -1333, -1333,  -284, -1333, -1333,
+   -1333, -1333, -1333, -1333,   528, -1333,   -11,     3,  1663, -1333,
+      -7, -1333,  1085, -1333,  -880, -1333, -1333, -1333,  1301, -1333,
+   -1333, -1333, -1333,   544, -1043, -1333, -1333, -1333, -1333,   716,
+   -1333, -1333,   329, -1333,  -570, -1333, -1333, -1333,  -872, -1333,
+     308, -1333, -1333, -1333, -1333, -1333, -1333, -1333,     4, -1333,
+     180, -1333,   745, -1333, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333, -1333,  -834, -1333, -1333, -1333,  -871,
+   -1333, -1333, -1333, -1333, -1333, -1333,   263,   858, -1333, -1333,
+   -1333,  -517, -1333, -1333, -1333, -1333, -1202,  -181,  -428, -1333,
+   -1333, -1333, -1333, -1333, -1109, -1333, -1333, -1333, -1333, -1333,
+     -99,  -514, -1097, -1333, -1333, -1333, -1333, -1333, -1332, -1333,
+   -1333, -1333, -1227, -1333,  -420, -1333, -1333, -1333, -1333, -1333,
+    -665, -1333,   769, -1333,  -193, -1333,  -209, -1333,    90, -1333,
+   -1333, -1333, -1333,   355, -1333,   903,   128, -1025, -1333, -1333,
+   -1333,  -141, -1333,  -242, -1333,  -287, -1333, -1333, -1333,    55,
+     123,  -674, -1333,  -502, -1333, -1333, -1333, -1333, -1333, -1333,
+     813, -1333,   472, -1333, -1333, -1333, -1333, -1333,  -251, -1333,
+   -1333,  -560, -1333,  -582, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333,   -83,   105, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333, -1333,   656,  -864, -1333, -1333, -1333,   830, -1333,
+   -1333, -1333,  -155, -1333,  -210, -1333, -1333,  -113, -1333, -1333,
+   -1333,  -105, -1333,  -889, -1333,  -213, -1333,  -803, -1333, -1333,
+   -1333,  -874, -1333, -1333, -1333, -1333,  -679,  -882,   502, -1333,
+   -1333, -1333,  -699, -1333,   767, -1333, -1333, -1333, -1333, -1333,
+     845,   339, -1333,  -942, -1333, -1333, -1333, -1333, -1333, -1333,
+   -1333, -1333,   902, -1333,   141, -1333, -1333,   -44, -1333, -1333,
+   -1333, -1333,   213, -1333, -1333, -1333,   411, -1333, -1333, -1333,
+   -1333, -1333, -1333, -1333,  -189, -1333,  -363, -1333, -1333,  -214,
+     873, -1333, -1333, -1333, -1333, -1333,  -195, -1333, -1300, -1333,
+   -1333, -1333, -1333,   904, -1333,   712, -1333, -1333, -1333, -1333,
+    -706, -1333, -1248, -1333, -1333, -1333, -1333, -1333,  -980,  -186,
+   -1333, -1333, -1333,  -912, -1333, -1333, -1333, -1333,  -467, -1333,
+   -1089,  1032, -1333, -1333,  -241,   742, -1333, -1333,  -235, -1333,
+   -1333, -1333, -1333, -1333,  -770, -1333, -1333, -1333,   727, -1333,
+   -1333, -1333,   203, -1333, -1333, -1333, -1333, -1333,  1551,    12,
+     347, -1333
+};
+
+  /* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,    21,     2,    58,     6,    31,    18,   797,   893,  1268,
+    1391,  1078,  1186,  1070,  1182,  1518,   925,  1305,   914,   851,
+     915,   916,   615,   616,   324,   917,  1141,  1287,   617,   695,
+     819,   174,   211,   249,   330,   555,   135,    72,    73,   950,
+     310,   398,   364,   198,   303,   227,   399,   460,  1388,  1467,
+    1551,  1207,  1515,    74,    75,   117,   952,  1097,   557,   750,
+     867,  1252,  1382,    76,   121,   778,   884,   558,  1339,  1439,
+    1183,  1421,   326,   403,   799,   896,   800,   899,   801,   902,
+     802,   905,   989,  1135,  1589,  1611,   877,   976,   107,   199,
+     762,   873,   152,   359,   395,   484,   515,  1043,  1164,    22,
+      33,    49,    95,   149,   559,   651,  1209,  1336,  1407,   439,
+     689,    77,   125,  1452,  1524,  1576,  1606,  1277,  1399,   618,
+     108,   235,   360,   136,   361,   392,   393,    34,   531,   604,
+      35,    47,   485,   591,   953,  1100,   441,   619,   486,   442,
+     624,   443,   627,    78,   954,  1101,  1217,   981,  1131,   185,
+     224,  1018,   620,   388,  1498,   420,   487,  1389,   955,  1221,
+     674,   871,   579,   675,   769,   879,   580,   676,  1105,   755,
+     253,   332,   175,   212,   671,   746,   444,   711,   488,   521,
+     445,   631,   489,   524,   560,   854,   918,    50,    93,  1060,
+    1171,    56,    45,    57,  1172,   390,   621,   154,   349,   109,
+     237,   304,  1231,  1375,  1049,  1166,  1002,  1143,  1271,  1393,
+    1483,  1558,  1326,  1423,  1291,  1411,   561,   653,   562,   654,
+     504,  1292,  1293,  1412,  1327,  1427,   956,  1107,  1484,  1596,
+     455,   505,  1433,  1513,  1272,  1396,  1489,  1525,  1572,  1604,
+     787,   967,   176,   213,  1144,   334,  1145,   262,   338,   644,
+     726,   855,   919,    36,    12,   492,   649,   957,  1229,   581,
+     678,   958,  1232,   779,   990,   784,   888,   582,   680,  1111,
+     773,   679,   882,  1529,  1030,  1160,   446,   538,   447,   637,
+     177,   214,   267,   342,   306,   352,   959,  1113,  1278,  1210,
+    1340,    96,   147,   150,   448,   720,   449,   723,   490,   599,
+     221,   960,  1116,  1085,  1061,  1176,   856,  1158,  1359,  1211,
+    1342,   982,  1134,    79,   857,   924,  1008,  1149,   178,   215,
+     272,   344,  1343,  1442,   195,  1212,  1345,   858,   927,   583,
+     682,   859,  1050,   804,  1003,   809,   909,   961,  1117,  1360,
+    1458,  1019,  1156,  1072,   584,   684,   928,   789,   683,   891,
+     327,   405,  1063,  1179,    80,   179,   129,  1180,   861,   930,
+     622,   645,   727,  1096,  1334,   450,   646,     9,    15,    25,
+     564,   655,   493,   389,   263,  1462,  1548,   962,  1120,  1214,
+    1347,   932,   862,   933,   362,   396,   623,   701,  1507,  1598,
+    1010,  1152,  1298,  1416,   110,   157,  1390,  1475,  1554,    26,
+     180,   216,    39,   565,   863,   934,  1153,  1299,  1219,  1348,
+    1453,  1532,  1584,   236,   307,    81,  1132,  1279,  1410,   240,
+     764,   878,  1363,  1463,  1549,  1590,  1613,  1623,  1142,  1294,
+    1123,  1263,   137,   181,   340,   341,   491,   528,   223,   295,
+    1046,    82,   131,   923,  1177,   404,   468,   672,  1222,  1352,
+     469,   510,   509,   970,   872,  1128,   570,   669,    83,   567,
+    1074,   568,   866,   935,   966,  1124,   206,   241,    38,    27,
+     116,   189
+};
+
+  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+     positive, shift that token.  If negative, reduce the rule whose
+     number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_uint16 yytable[] =
+{
+      41,   592,   275,   883,   261,   273,   785,  1147,   756,   514,
+     232,  1027,   771,    52,    51,  1351,   803,  1001,    54,   892,
+     260,  1036,    71,  1148,  1236,  1038,  1039,  1005,    97,  1288,
+     648,  1040,    99,   105,  1041,    46,    40,   656,  1044,   658,
+     864,  1290,    23,   250,  1138,  1023,   312,   335,   317,     3,
+    1058,  1419,  1057,    28,  1082,    28,  1081,  1102,  1059,   345,
+    1031,  1093,  1083,  1084,  1118,    23,     3,     3,   138,     3,
+      28,   183,    23,   151,     3,    28,   183,    23,   331,     3,
+     853,  1270,   417,    23,     3,  1022,  1138,  1133,   159,     3,
+     942,  1243,   162,  1486,  1516,    28,   164,    28,  1275,   881,
+     165,  1092,   167,    23,     3,   182,   184,     3,   190,   191,
+     192,     3,  1482,   608,   673,    28,     3,     3,  1528,    28,
+    1384,    67,   193,  1028,   196,   609,  1432,  1062,  1505,   163,
+     988,  1087,   203,  1155,  1189,  1296,   844,   735,   845,   737,
+     739,   741,   843,   187,    19,   887,   843,    37,    28,    11,
+    1531,   681,    28,  1355,   219,     3,   642,     1,  1213,   681,
+      37,   964,   200,   681,   201,   908,   929,   643,    20,  1220,
+     226,   869,   681,    20,   229,   920,    20,  1385,    20,     3,
+    1216,   612,   242,   243,   244,   245,   247,   252,   258,   265,
+     270,   274,  1577,  1000,  1488,   194,  1510,    20,   169,   251,
+    1512,  1216,   115,   988,  1371,    20,   114,    32,   114,   466,
+     309,    62,   314,   643,   993,   613,   323,  1196,    20,    62,
+      20,    62,    20,   771,  1580,  1197,   114,   754,   277,   278,
+     114,   320,    32,     4,  1285,  1088,   299,  1253,  1241,  1251,
+     677,   940,  1139,    20,  1242,  1254,  1255,    62,  1260,    20,
+       4,     4,   869,   346,  1496,  1496,   161,   321,     3,   114,
+      62,  1042,  1108,     4,    20,  1114,  1000,   754,     4,   194,
+     347,    20,   384,     4,  1303,    62,   348,  1228,   351,   747,
+      28,  1557,   357,  1285,  1139,   754,  1286,  1286,     4,   687,
+     171,     4,   264,  1386,   264,     4,  1309,   170,   256,   257,
+       4,     4,   209,  1502,   371,   370,   373,   372,   375,   374,
+    1329,   376,   377,  1246,  1500,  1599,   380,   379,   382,   381,
+      24,     5,   681,    20,   383,   228,  1350,   386,    20,    20,
+    1583,    20,   367,   368,   369,  1223,   650,   173,    24,     4,
+     681,    20,   401,   657,    20,     3,    20,   947,  1478,  1369,
+    1465,  1367,  1379,     5,  1378,   268,     7,  1370,    20,    20,
+      14,    20,    62,   781,    29,   677,   296,   412,  1383,  1544,
+     437,   453,    20,   463,  1607,   647,    42,    43,   472,   385,
+     473,    20,   474,  1394,  1397,   477,    20,  1174,  1184,    53,
+    1449,   114,    20,   410,    20,   322,    67,     5,   339,    62,
+     673,  1403,    98,  1491,  1566,   343,   112,   113,    20,    20,
+      20,    69,   126,   127,   471,   130,  1568,    20,  1256,  1492,
+     336,   337,     7,    17,    67,    20,   133,    20,   378,  1075,
+     350,  1422,   608,   609,    30,   507,   754,   610,   840,    69,
+    1425,  1429,     4,    23,   104,    20,   512,   936,  1424,  1428,
+     513,   478,    69,    20,    62,   937,     7,     7,     7,   158,
+     257,   160,   529,  1601,    28,    44,    20,    69,   407,  1384,
+      48,  1550,  1459,   166,   479,  1603,    55,    94,   188,   339,
+     554,    20,   518,   752,   611,   577,   148,  1552,  1016,   587,
+     588,   681,   938,   168,   246,   480,   593,   481,    62,   596,
+     612,   194,  1469,   600,  1473,  1487,  1275,   208,   197,   940,
+    1468,  1264,  1472,   634,  1016,  1362,   168,   681,    37,   339,
+     217,   234,   652,   613,  1051,   408,   409,    28,   308,     4,
+      62,   936,     3,   603,    62,  1450,   239,    62,    62,   937,
+     302,   225,   133,   305,   874,  1533,  1066,    20,    67,   475,
+     476,   482,  1535,   397,    69,  1358,  1537,  1538,  1593,   255,
+    1159,   685,  1539,  1522,    20,  1540,   686,   202,    20,  1542,
+      62,   300,   301,   849,  1594,   134,    20,  1076,  1322,   688,
+    1052,   895,  1456,  1323,   939,  1553,   319,  1276,   402,   218,
+     673,   530,  1556,   940,   702,   670,   946,   894,   333,   705,
+     218,  1401,   708,    67,  1053,    20,   714,    37,    62,   569,
+     251,   911,   717,   912,   673,   614,  1265,   913,   681,   677,
+     483,   728,   731,   732,  1289,   733,   734,  1266,   736,   738,
+     740,   742,   743,   744,    63,    62,   677,   942,   114,   943,
+    1574,  1582,  1386,   790,   745,  1055,    69,   748,  1573,  1079,
+     760,   766,  1007,   776,   876,   782,    20,   796,    37,   807,
+      20,  1076,   812,     7,   813,   988,   366,   816,   817,  1015,
+      62,   353,   821,   822,   823,   354,   824,  1009,  1048,    67,
+     946,   791,   792,   793,   794,  1605,   825,   673,  1174,   758,
+      69,   134,  1122,   790,    62,   828,   452,  1095,   831,   104,
+     774,   834,   836,   104,    20,  1130,   256,   257,     7,     7,
+     754,    20,  1220,   400,   869,  1067,     4,  1151,  1181,   790,
+      62,   850,    69,    20,    62,   840,    69,  1230,   869,    69,
+      69,   791,   792,   793,   794,  1563,    20,  1161,  1162,   758,
+      20,  1033,    62,    20,    20,   840,    62,   470,  1406,   692,
+     693,  1163,   697,   698,   699,  1075,  1275,   791,   792,   793,
+     794,   758,    69,  1016,  1016,   758,   355,   681,    62,   937,
+     875,  1068,  1338,   936,   774,   681,    20,  1341,   938,  1137,
+      62,     7,     7,     7,     7,     7,     7,     7,     7,     7,
+       7,     7,  1541,  1275,   910,  1136,   775,    62,  1406,   356,
+      69,   757,  1432,   207,    62,   772,   938,     7,  1461,   788,
+     795,   210,  1194,  1547,    20,    62,     7,   511,   938,    67,
+     869,  1016,   101,   940,   681,   869,    20,    69,    62,   525,
+    1588,  1239,  1193,   694,   818,   940,   315,  1276,  1200,   102,
+      62,    20,   949,    67,   936,   977,   971,  1281,   972,  1355,
+    1283,    62,   937,   979,   984,   457,   248,   986,   586,   987,
+     795,  1187,   413,   991,  1201,   414,   994,   942,   995,    67,
+     775,   996,    62,    67,   997,   939,    20,   998,   103,  1127,
+     999,   458,   868,    20,  1004,  1190,   795,   459,  1011,   938,
+    1012,    67,  1523,  1017,  1021,    67,   415,  1499,  1503,  1029,
+      20,    62,  1035,   973,     8,  1054,   940,  1265,  1069,  1077,
+    1091,  1034,    69,  1368,  1346,  1034,    69,    67,  1266,   418,
+     758,  1051,    62,   440,   759,   978,    20,  1098,   974,    67,
+      20,  1099,   898,   901,   904,   907,   419,  1034,    69,   696,
+     297,   975,  1125,  1565,   626,   629,   222,   633,    20,  1115,
+     639,  1129,    20,     3,   885,  1202,    62,   254,   820,  1121,
+      69,   156,  1295,  1479,    67,  1140,   231,  1203,  1555,  1185,
+     128,  1480,    69,   104,    20,  1244,  1204,  1052,  1146,   765,
+    1333,   673,   259,  1150,   768,  1154,    20,  1372,  1400,    69,
+    1335,  1337,  1405,   946,  1095,  1408,    69,   969,  1112,  1567,
+      67,  1053,  1205,    20,   153,   155,   155,    69,   328,  1165,
+      20,   992,   571,   806,   572,   921,   772,   118,   691,   681,
+      69,    20,  1578,   172,   269,  1167,  1353,   266,  1409,    62,
+    1612,  1622,    69,  1245,    20,    62,   937,   788,  1595,  1086,
+     122,   713,   194,    69,  1354,   271,    20,   119,   421,  1344,
+      67,   722,   725,  1313,   120,  1392,   573,    20,  1602,   839,
+     574,  1314,  1402,   870,    69,   835,  1482,   788,   100,   106,
+     123,  1191,  1192,   938,   788,  1206,  1261,   124,    20,    62,
+     963,   880,  1224,   759,  1355,  1527,  1526,  1188,  1233,   276,
+     111,  1238,  1249,    69,   575,  1259,    20,   889,   758,  1267,
+     168,  1417,   576,  1274,   423,   238,  1280,    20,  1414,  1282,
+    1284,   406,  1457,  1104,    69,  1109,  1109,  1455,  1104,   425,
+    1285,   886,  1025,  1286,  1297,  1436,   681,  1300,    20,     0,
+    1301,  1302,  1262,  1307,     0,  1308,     0,     4,   838,  1311,
+       0,  1312,     0,   788,     0,  1316,  1315,   840,    69,     0,
+    1318,  1317,     0,  1320,  1319,  1216,     0,  1324,     0,   422,
+       0,  1331,    20,     0,  1332,   465,     0,     0,   204,     0,
+       0,     0,   169,     0,     0,     0,   841,     0,    67,     0,
+    1175,  1178,     0,     0,    67,     0,   810,   985,     0,   205,
+     220,     0,  1349,     0,     0,     0,  1356,     7,   104,     7,
+       7,     0,     7,   842,  1365,   170,   781,  1376,   677,   843,
+     230,   365,     0,     0,     0,   968,   844,     0,   845,     0,
+       0,    69,   171,   461,     0,   462,     0,    69,    67,  1519,
+     846,   233,  1521,     0,     0,    20,     0,     0,  1387,     0,
+       0,    20,   847,     0,   806,   172,   311,  1016,   316,     0,
+     681,     0,   325,   848,     0,     0,   391,   394,     0,     0,
+       0,   759,  1413,   897,   900,   903,   906,   313,     0,   318,
+    1415,    69,   563,   329,  1418,     7,     0,   543,     0,     0,
+    1420,     0,   849,   173,   788,    20,     0,    62,     0,   544,
+       0,   890,     0,     7,   545,     7,     7,     7,     7,     7,
+       7,     0,     0,     0,  1431,   478,  1571,     0,     0,  1434,
+     546,  1437,     0,     0,  1126,  1440,     0,  1443,   358,     0,
+    1445,     0,  1447,  1451,     0,     0,  1330,  1454,   479,   494,
+     495,   496,   497,   498,   499,   500,   501,   502,   503,   363,
+       0,     0,    62,   937,     0,  1248,     0,   608,   609,   480,
+    1464,   481,   610,     0,   547,   506,     0,  1466,     0,     0,
+    1471,  1600,     0,   387,   508,     0,  1476,     0,     0,   548,
+     549,     0,   478,  1157,  1490,     0,     0,     0,   625,   628,
+     938,   632,     0,     0,   638,  1495,  1497,  1501,     0,  1504,
+    1616,  1506,     0,   788,   788,   479,  1508,   940,  1509,   611,
+       0,  1621,  1511,   416,     0,   482,   438,   454,  1514,   464,
+       0,  1006,  1051,     0,  1517,   612,   480,  1520,   481,     0,
+    1020,  1024,     7,     7,   424,     0,  1032,   451,   456,     0,
+     467,     0,     0,  1534,     0,     0,    67,  1094,   613,   751,
+       0,  1103,   763,     0,   550,   780,     0,     0,  1119,   805,
+     788,   788,   690,     0,     0,     0,     0,     0,     0,   700,
+     551,     0,     0,     0,  1559,     0,     0,     0,  1560,  1561,
+       0,  1562,   482,     0,   483,   712,     0,     0,     0,    69,
+       0,  1175,   788,     0,     0,   721,   724,   552,  1564,   553,
+    1569,    67,  1053,    20,     0,     0,     0,     0,     0,     0,
+       0,     0,  1581,     0,  1306,     0,     0,     0,  1585,     0,
+       0,     0,   788,   860,   788,     0,   556,     0,     0,     0,
+       0,   578,    59,    60,     0,     0,  1591,     0,  1321,  1592,
+      61,    62,     0,  1597,    69,     0,    63,   566,     0,     0,
+       0,   483,   585,     0,     0,     0,  1493,  1494,    20,   635,
+       0,     0,    10,     0,     0,    13,    16,     0,    20,  1608,
+       0,  1609,    64,   516,     0,   519,   522,     0,   526,     0,
+     636,  1614,   837,  1615,     0,     0,     0,    65,     0,  1617,
+       0,  1618,     0,     0,   411,  1619,  1620,     0,    59,    60,
+       0,  1624,     0,     0,     0,     0,    61,    62,  1625,     0,
+     589,     0,    63,  1218,     0,     0,   594,     0,     0,     0,
+     597,     0,     0,   601,     0,   605,   608,   609,    10,     0,
+     132,   610,     0,   139,   140,   141,   142,     0,    64,     0,
+     703,   143,   144,   145,   146,   706,     0,     0,   709,     0,
+       0,     0,   715,    65,     0,     0,     0,     0,   718,     0,
+     788,   704,    10,    10,    10,     0,   707,   729,  1304,   710,
+      66,     0,  1310,   716,     0,     0,   936,     0,   611,   719,
+       0,     0,     0,    62,   937,     0,     0,     0,   730,     0,
+      67,     0,   186,   749,   612,     0,   761,   767,     0,   777,
+       0,   783,     0,   798,  1045,   808,     0,  1064,     0,     0,
+     814,  1089,     0,     0,   753,     0,     0,   613,   770,     0,
+      68,   938,   786,     0,     0,     0,   811,     0,     0,     0,
+     939,   815,   826,    69,     0,     0,    66,     0,   940,     0,
+      70,   829,     0,     0,   832,     0,     0,    20,     0,     0,
+       0,     0,     0,   827,     0,   922,    67,   926,   926,     0,
+     931,     0,   830,     0,     0,   833,     0,   852,     0,   941,
+       0,     0,     0,     0,     0,     0,     0,  1395,  1398,     0,
+       0,     0,   942,     0,   943,   298,    68,  1404,   865,   517,
+       0,   520,   523,     0,   527,     0,   944,     0,     0,    69,
+       0,    84,    85,    86,     0,   945,    70,     0,     0,   614,
+       0,     0,     0,    20,     0,     0,     0,     0,  1546,    87,
+      88,    89,     0,     0,     0,   946,   590,  1169,     0,     0,
+       0,     0,   595,  1026,  1426,  1430,   598,     0,     0,   602,
+       0,   606,     0,     0,   947,     0,   847,     0,    90,    91,
+      92,   394,     0,  1106,   926,  1110,  1110,   926,  1106,     0,
+       0,     0,     0,     0,     0,   759,  1460,     0,     0,    10,
+       0,     0,     0,     0,  1198,    69,     0,     0,     0,     0,
+       0,     0,     0,     0,  1226,     0,     0,   948,   951,    20,
+    1235,     0,     0,    62,  1257,     0,  1470,   659,  1474,   980,
+       0,     0,     0,  1586,  1587,  1485,     0,   660,     0,   965,
+       0,     0,     0,   661,    10,    10,     0,   662,     0,     0,
+     983,   663,     0,   426,     0,     0,     0,     0,   664,     0,
+     665,     0,   427,   354,     0,     0,  1013,   428,   429,     0,
+     666,   667,   668,     0,  1610,     0,     0,     0,  1037,     0,
+       0,  1056,     0,     0,  1071,  1080,   430,  1014,   431,     0,
+       0,     0,  1530,     0,     0,     0,     0,     0,     0,  1047,
+       0,     0,  1065,     0,     0,  1073,  1090,     0,     0,     0,
+     926,   506,     0,     0,     0,     0,     0,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,  1361,     0,
+       0,   432,   433,     0,     0,     0,  1373,     0,     0,  1380,
+       0,   434,   435,    10,     0,     0,     0,     0,     0,     0,
+       0,     0,    10,   279,     0,     0,   280,     0,     0,     0,
+     281,   282,   283,   284,  1575,   285,  1579,   286,   287,     0,
+       0,   288,     0,     0,     0,     0,     0,     0,   436,   289,
+       0,     0,     0,     0,     0,     0,     0,     0,   290,     0,
+       0,     0,     0,     0,   291,     0,     0,     0,     0,     0,
+     292,  1168,   293,     0,     0,     0,     0,   294,     0,     0,
+       0,     0,     0,     0,     0,    69,     0,     0,     0,     0,
+       0,     0,  1170,     0,     0,     0,     0,     0,     0,    20,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,  1435,     0,  1438,     0,     0,     0,  1441,  1195,  1444,
+       0,  1208,  1446,     0,  1448,     0,     0,     0,  1225,     0,
+       0,     0,     0,     0,  1234,     0,     0,  1240,  1250,  1199,
+       0,     0,  1215,     0,     0,  1269,     0,     0,     0,  1227,
+       0,     0,     0,     0,     0,  1237,     0,     0,  1247,  1258,
+       0,     0,     0,     0,     0,     0,  1273,   532,   533,   534,
+     535,   536,   537,   539,   540,   541,   542,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,  1325,   607,     0,     0,     0,   630,     0,
+       0,     0,     0,   640,   641,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,  1328,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,  1543,     0,     0,     0,     0,
+       0,     0,  1357,     0,     0,     0,     0,     0,     0,     0,
+    1366,     0,     0,  1377,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,  1364,     0,     0,     0,     0,     0,     0,
+       0,  1374,     0,     0,  1381,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,  1570,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,    10,     0,    10,    10,     0,    10,     0,
+       0,     0,  1477,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,  1481,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,  1536,
+       0,    10,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    10,
+    1545,    10,    10,    10,    10,    10,    10,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,  1173,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,    10,    10
+};
+
+static const yytype_int16 yycheck[] =
+{
+      25,   518,   216,   773,   213,   215,   680,  1003,   673,   476,
+     199,   923,   677,    39,    39,  1217,   682,   908,    43,   789,
+     213,   927,    47,  1003,  1113,   927,   927,   909,    53,  1138,
+     544,   927,    57,    58,   927,    32,    24,   551,   927,   553,
+     746,  1138,     6,   211,    75,   919,   235,   256,   237,     3,
+     930,  1299,   930,     4,   934,     4,   934,   939,   930,   269,
+     924,   935,   934,   934,   946,     6,     3,     3,    93,     3,
+       4,     5,     6,    98,     3,     4,     5,     6,   246,     3,
+     746,  1124,   392,     6,     3,   919,    75,   978,   113,     3,
+     188,  1116,   117,  1393,    66,     4,   121,     4,   122,   773,
+     125,   935,   127,     6,     3,   130,   131,     3,   133,   134,
+     135,     3,   176,    71,   148,     4,     3,     3,  1450,     4,
+      86,   238,   147,   196,   149,    72,   178,   930,   255,   117,
+     104,   934,   157,  1015,  1076,   257,   226,   651,   228,   653,
+     654,   655,   219,   131,   111,   775,   219,    19,     4,     0,
+    1450,   236,     4,   272,   179,     3,   185,   116,  1100,   236,
+      32,   867,   154,   236,   156,   795,   845,   242,   295,   284,
+     195,   286,   236,   295,   199,    67,   295,   143,   295,     3,
+     265,   139,   207,   208,   209,   210,   211,   212,   213,   214,
+     215,   216,  1524,   167,  1396,   241,  1423,   295,   149,   150,
+    1427,   265,    61,   104,  1229,   295,   115,   131,   115,   398,
+     235,    89,   237,   242,   888,   162,   241,  1097,   295,    89,
+     295,    89,   295,   888,  1524,  1097,   115,   181,   216,   217,
+     115,    73,   131,   187,   170,   934,   224,  1117,  1116,  1117,
+     194,   144,   273,   295,  1116,  1117,  1117,    89,  1120,   295,
+     187,   187,   286,   278,   141,   141,   115,    99,     3,   115,
+      89,   927,   941,   187,   295,   944,   167,   181,   187,   241,
+     295,   295,   140,   187,  1156,    89,   301,  1111,   303,    93,
+       4,  1483,   307,   170,   273,   181,   173,   173,   187,   599,
+     199,   187,   201,   259,   201,   187,  1160,   182,   183,   184,
+     187,   187,   161,  1412,   330,   330,   332,   332,   334,   334,
+    1184,   336,   337,  1116,  1411,  1563,   342,   342,   344,   344,
+     261,   245,   236,   295,   349,   197,  1217,   352,   295,   295,
+    1532,   295,   320,   321,   322,  1105,   545,   260,   261,   187,
+     236,   295,   367,   552,   295,     3,   295,   250,  1391,  1229,
+    1375,  1229,  1232,   245,  1232,   214,     1,  1229,   295,   295,
+       5,   295,    89,   192,    17,   194,   295,   392,  1248,  1458,
+     395,   396,   295,   398,  1576,   543,    29,    30,   403,   247,
+     405,   295,   407,  1265,  1266,   410,   295,  1052,  1067,    42,
+     121,   115,   295,   390,   295,   237,   238,   245,   257,    89,
+     148,  1275,    55,  1399,  1513,   264,    59,    60,   295,   295,
+     295,   281,    65,    66,   402,    68,  1513,   295,  1117,  1399,
+     276,   277,    67,   118,   238,   295,   126,   295,   338,    64,
+     302,  1321,    71,    72,   117,   460,   181,    76,   157,   281,
+    1322,  1323,   187,     6,   258,   295,   471,    82,  1322,  1323,
+     475,   109,   281,   295,    89,    90,   101,   102,   103,   112,
+     184,   114,   487,  1572,     4,   161,   295,   281,   378,    86,
+     158,  1467,  1354,   126,   132,  1572,   160,   205,   131,   338,
+     505,   295,   479,   672,   123,   510,   206,  1467,   233,   514,
+     515,   236,   127,    77,    78,   153,   521,   155,    89,   524,
+     139,   241,  1384,   528,  1386,  1396,   122,   160,    84,   144,
+    1384,    63,  1386,   538,   233,  1221,    77,   236,   390,   378,
+     210,   267,   547,   162,   159,   384,   385,     4,    83,   187,
+      89,    82,     3,   530,    89,   266,   270,    89,    89,    90,
+     164,   194,   126,   200,   758,  1457,    65,   295,   238,   408,
+     409,   209,  1458,    85,   281,  1221,  1458,  1458,  1554,   212,
+    1027,   586,  1458,   180,   295,  1458,   591,   294,   295,  1458,
+      89,   224,   225,   292,  1554,   275,   295,   212,   169,   604,
+     215,   790,  1352,   174,   136,  1475,   239,   203,   282,   279,
+     148,   130,  1483,   144,   619,   151,   231,   790,   251,   624,
+     279,  1275,   627,   238,   239,   295,   631,   479,    89,   287,
+     150,    68,   637,    70,   148,   254,   168,    74,   236,   194,
+     278,   646,   647,   648,  1138,   650,   651,   179,   653,   654,
+     655,   656,   657,   658,    94,    89,   194,   188,   115,   190,
+    1522,  1532,   259,    62,   669,   930,   281,   672,  1522,   934,
+     675,   676,   221,   678,   106,   680,   295,   682,   530,   684,
+     295,   212,   687,   308,   689,   104,   319,   692,   693,   138,
+      89,   125,   697,   698,   699,   129,   701,   256,   166,   238,
+     231,   100,   101,   102,   103,  1576,   711,   148,  1353,   108,
+     281,   275,   274,    62,    89,   720,   177,   243,   723,   258,
+      95,   726,   727,   258,   295,   269,   183,   184,   353,   354,
+     181,   295,   284,   366,   286,   234,   187,   264,    98,    62,
+      89,   746,   281,   295,    89,   157,   281,   165,   286,   281,
+     281,   100,   101,   102,   103,  1505,   295,    26,    27,   108,
+     295,   110,    89,   295,   295,   157,    89,   400,   114,   608,
+     609,    40,   611,   612,   613,    64,   122,   100,   101,   102,
+     103,   108,   281,   233,   233,   108,   220,   236,    89,    90,
+     758,   290,    97,    82,    95,   236,   295,   224,   127,   988,
+      89,   426,   427,   428,   429,   430,   431,   432,   433,   434,
+     435,   436,  1458,   122,   819,   988,   191,    89,   114,   253,
+     281,   673,   178,   158,    89,   677,   127,   452,   248,   681,
+     229,   166,  1097,   249,   295,    89,   461,   470,   127,   238,
+     286,   233,   107,   144,   236,   286,   295,   281,    89,   482,
+     105,  1116,  1097,   610,   695,   144,   237,   203,    87,   124,
+      89,   295,   867,   238,    82,   137,   871,  1131,   873,   272,
+    1134,    89,    90,   878,   879,    23,   211,   882,   511,   884,
+     229,  1075,   392,   888,   113,   392,   891,   188,   893,   238,
+     191,   896,    89,   238,   899,   136,   295,   902,   163,   970,
+     905,    49,   754,   295,   909,  1095,   229,    55,   913,   127,
+     915,   238,  1449,   918,   919,   238,   392,  1411,  1412,   924,
+     295,    89,   927,     9,     1,   930,   144,   168,   933,   934,
+     935,   280,   281,  1229,  1205,   280,   281,   238,   179,   392,
+     108,   159,    89,   395,   271,   217,   295,   938,    34,   238,
+     295,   938,   791,   792,   793,   794,   392,   280,   281,   610,
+     224,    47,   967,  1513,   533,   534,   179,   536,   295,   945,
+     539,   976,   295,     3,   774,   204,    89,   212,   695,   947,
+     281,   103,  1143,  1391,   238,   990,   199,   216,  1482,  1068,
+      67,  1391,   281,   258,   295,  1116,   225,   215,  1003,   146,
+    1190,   148,   213,  1008,   676,  1010,   295,  1229,  1275,   281,
+    1200,  1201,  1276,   231,   243,  1279,   281,   869,   943,  1513,
+     238,   239,   251,   295,   101,   102,   103,   281,   241,  1034,
+     295,   888,   145,   230,   147,   842,   888,    20,   607,   236,
+     281,   295,  1524,   222,   223,  1050,   214,   214,  1279,    89,
+    1590,  1613,   281,  1116,   295,    89,    90,   909,  1555,   934,
+      20,   630,   241,   281,   232,   215,   295,    50,   392,  1204,
+     238,   640,   641,  1166,    57,  1264,   189,   295,  1572,    69,
+     193,  1166,  1275,   755,   281,   726,   176,   939,    57,    58,
+      50,  1096,  1097,   127,   946,  1100,  1120,    57,   295,    89,
+     867,   773,  1107,   271,   272,   195,  1449,  1075,  1113,   216,
+      58,  1116,  1117,   281,   227,  1120,   295,   789,   108,  1124,
+      77,  1296,   235,  1128,   392,   201,  1131,   295,  1294,  1134,
+    1135,   369,  1353,   940,   281,   942,   943,  1352,   945,   392,
+     170,   774,   919,   173,  1149,  1335,   236,  1152,   295,    -1,
+    1155,  1156,  1120,  1158,    -1,  1160,    -1,   187,   727,  1164,
+      -1,  1166,    -1,  1015,    -1,  1171,  1171,   157,   281,    -1,
+    1176,  1176,    -1,  1179,  1179,   265,    -1,  1182,    -1,   392,
+      -1,  1186,   295,    -1,  1189,   398,    -1,    -1,   157,    -1,
+      -1,    -1,   149,    -1,    -1,    -1,   186,    -1,   238,    -1,
+    1052,  1053,    -1,    -1,   238,    -1,   684,   879,    -1,   157,
+     179,    -1,  1217,    -1,    -1,    -1,  1221,   842,   258,   844,
+     845,    -1,   847,   213,  1229,   182,   192,  1232,   194,   219,
+     199,   308,    -1,    -1,    -1,   868,   226,    -1,   228,    -1,
+      -1,   281,   199,   283,    -1,   285,    -1,   281,   238,  1439,
+     240,   199,  1442,    -1,    -1,   295,    -1,    -1,  1263,    -1,
+      -1,   295,   252,    -1,   230,   222,   235,   233,   237,    -1,
+     236,    -1,   241,   263,    -1,    -1,   353,   354,    -1,    -1,
+      -1,   271,  1287,   791,   792,   793,   794,   235,    -1,   237,
+    1295,   281,   505,   241,  1299,   920,    -1,    79,    -1,    -1,
+    1305,    -1,   292,   260,  1156,   295,    -1,    89,    -1,    91,
+      -1,   789,    -1,   938,    96,   940,   941,   942,   943,   944,
+     945,    -1,    -1,    -1,  1329,   109,  1516,    -1,    -1,  1334,
+     112,  1336,    -1,    -1,   967,  1340,    -1,  1342,   307,    -1,
+    1345,    -1,  1347,  1348,    -1,    -1,  1185,  1352,   132,   427,
+     428,   429,   430,   431,   432,   433,   434,   435,   436,   307,
+      -1,    -1,    89,    90,    -1,    92,    -1,    71,    72,   153,
+    1375,   155,    76,    -1,   156,   452,    -1,  1382,    -1,    -1,
+    1385,  1571,    -1,   352,   461,    -1,  1391,    -1,    -1,   171,
+     172,    -1,   109,  1026,  1399,    -1,    -1,    -1,   533,   534,
+     127,   536,    -1,    -1,   539,  1410,  1411,  1412,    -1,  1414,
+    1600,  1416,    -1,  1265,  1266,   132,  1421,   144,  1423,   123,
+      -1,  1611,  1427,   392,    -1,   209,   395,   396,  1433,   398,
+      -1,   909,   159,    -1,  1439,   139,   153,  1442,   155,    -1,
+     918,   919,  1067,  1068,   392,    -1,   924,   395,   396,    -1,
+     398,    -1,    -1,  1458,    -1,    -1,   238,   935,   162,   672,
+      -1,   939,   675,    -1,   246,   678,    -1,    -1,   946,   682,
+    1322,  1323,   607,    -1,    -1,    -1,    -1,    -1,    -1,   614,
+     262,    -1,    -1,    -1,  1489,    -1,    -1,    -1,  1493,  1494,
+      -1,  1496,   209,    -1,   278,   630,    -1,    -1,    -1,   281,
+      -1,  1353,  1354,    -1,    -1,   640,   641,   289,  1513,   291,
+    1515,   238,   239,   295,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,  1527,    -1,  1157,    -1,    -1,    -1,  1533,    -1,
+      -1,    -1,  1384,   746,  1386,    -1,   505,    -1,    -1,    -1,
+      -1,   510,    80,    81,    -1,    -1,  1551,    -1,  1181,  1554,
+      88,    89,    -1,  1558,   281,    -1,    94,   505,    -1,    -1,
+      -1,   278,   510,    -1,    -1,    -1,  1405,  1406,   295,   538,
+      -1,    -1,     1,    -1,    -1,     4,     5,    -1,   295,  1584,
+      -1,  1586,   120,   478,    -1,   480,   481,    -1,   483,    -1,
+     538,  1596,   727,  1598,    -1,    -1,    -1,   135,    -1,  1604,
+      -1,  1606,    -1,    -1,   142,  1610,  1611,    -1,    80,    81,
+      -1,  1616,    -1,    -1,    -1,    -1,    88,    89,  1623,    -1,
+     515,    -1,    94,  1101,    -1,    -1,   521,    -1,    -1,    -1,
+     525,    -1,    -1,   528,    -1,   530,    71,    72,    67,    -1,
+      69,    76,    -1,    35,    36,    37,    38,    -1,   120,    -1,
+     619,    43,    44,    45,    46,   624,    -1,    -1,   627,    -1,
+      -1,    -1,   631,   135,    -1,    -1,    -1,    -1,   637,    -1,
+    1522,   619,   101,   102,   103,    -1,   624,   646,  1156,   627,
+     218,    -1,  1160,   631,    -1,    -1,    82,    -1,   123,   637,
+      -1,    -1,    -1,    89,    90,    -1,    -1,    -1,   646,    -1,
+     238,    -1,   131,   672,   139,    -1,   675,   676,    -1,   678,
+      -1,   680,    -1,   682,   927,   684,    -1,   930,    -1,    -1,
+     689,   934,    -1,    -1,   672,    -1,    -1,   162,   676,    -1,
+     268,   127,   680,    -1,    -1,    -1,   684,    -1,    -1,    -1,
+     136,   689,   711,   281,    -1,    -1,   218,    -1,   144,    -1,
+     288,   720,    -1,    -1,   723,    -1,    -1,   295,    -1,    -1,
+      -1,    -1,    -1,   711,    -1,   842,   238,   844,   845,    -1,
+     847,    -1,   720,    -1,    -1,   723,    -1,   746,    -1,   175,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,  1265,  1266,    -1,
+      -1,    -1,   188,    -1,   190,   224,   268,  1275,   746,   478,
+      -1,   480,   481,    -1,   483,    -1,   202,    -1,    -1,   281,
+      -1,    11,    12,    13,    -1,   211,   288,    -1,    -1,   254,
+      -1,    -1,    -1,   295,    -1,    -1,    -1,    -1,  1461,    29,
+      30,    31,    -1,    -1,    -1,   231,   515,  1050,    -1,    -1,
+      -1,    -1,   521,   920,  1322,  1323,   525,    -1,    -1,   528,
+      -1,   530,    -1,    -1,   250,    -1,   252,    -1,    58,    59,
+      60,   938,    -1,   940,   941,   942,   943,   944,   945,    -1,
+      -1,    -1,    -1,    -1,    -1,   271,  1354,    -1,    -1,   308,
+      -1,    -1,    -1,    -1,  1097,   281,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,  1107,    -1,    -1,   293,   867,   295,
+    1113,    -1,    -1,    89,  1117,    -1,  1384,     8,  1386,   878,
+      -1,    -1,    -1,  1546,  1547,  1393,    -1,    18,    -1,   867,
+      -1,    -1,    -1,    24,   353,   354,    -1,    28,    -1,    -1,
+     878,    32,    -1,   119,    -1,    -1,    -1,    -1,    39,    -1,
+      41,    -1,   128,   129,    -1,    -1,   915,   133,   134,    -1,
+      51,    52,    53,    -1,  1587,    -1,    -1,    -1,   927,    -1,
+      -1,   930,    -1,    -1,   933,   934,   152,   915,   154,    -1,
+      -1,    -1,  1450,    -1,    -1,    -1,    -1,    -1,    -1,   927,
+      -1,    -1,   930,    -1,    -1,   933,   934,    -1,    -1,    -1,
+    1067,  1068,    -1,    -1,    -1,    -1,    -1,   426,   427,   428,
+     429,   430,   431,   432,   433,   434,   435,   436,  1221,    -1,
+      -1,   197,   198,    -1,    -1,    -1,  1229,    -1,    -1,  1232,
+      -1,   207,   208,   452,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,   461,     7,    -1,    -1,    10,    -1,    -1,    -1,
+      14,    15,    16,    17,  1522,    19,  1524,    21,    22,    -1,
+      -1,    25,    -1,    -1,    -1,    -1,    -1,    -1,   244,    33,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    42,    -1,
+      -1,    -1,    -1,    -1,    48,    -1,    -1,    -1,    -1,    -1,
+      54,  1050,    56,    -1,    -1,    -1,    -1,    61,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,   281,    -1,    -1,    -1,    -1,
+      -1,    -1,  1050,    -1,    -1,    -1,    -1,    -1,    -1,   295,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,  1334,    -1,  1336,    -1,    -1,    -1,  1340,  1097,  1342,
+      -1,  1100,  1345,    -1,  1347,    -1,    -1,    -1,  1107,    -1,
+      -1,    -1,    -1,    -1,  1113,    -1,    -1,  1116,  1117,  1097,
+      -1,    -1,  1100,    -1,    -1,  1124,    -1,    -1,    -1,  1107,
+      -1,    -1,    -1,    -1,    -1,  1113,    -1,    -1,  1116,  1117,
+      -1,    -1,    -1,    -1,    -1,    -1,  1124,   494,   495,   496,
+     497,   498,   499,   500,   501,   502,   503,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,  1182,   531,    -1,    -1,    -1,   535,    -1,
+      -1,    -1,    -1,   540,   541,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,  1182,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,  1458,    -1,    -1,    -1,    -1,
+      -1,    -1,  1221,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+    1229,    -1,    -1,  1232,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,  1221,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,  1229,    -1,    -1,  1232,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,  1515,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,   842,    -1,   844,   845,    -1,   847,    -1,
+      -1,    -1,  1391,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,  1391,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,  1458,
+      -1,   920,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   938,
+    1458,   940,   941,   942,   943,   944,   945,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,  1051,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,  1067,  1068
+};
+
+  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+     symbol of state STATE-NUM.  */
+static const yytype_uint16 yystos[] =
+{
+       0,   116,   298,     3,   187,   245,   300,   549,   551,   663,
+     764,     0,   550,   764,   549,   664,   764,   118,   302,   111,
+     295,   297,   395,     6,   261,   665,   695,   765,     4,   766,
+     117,   301,   131,   396,   423,   426,   549,   552,   764,   698,
+     765,   297,   766,   766,   161,   488,   423,   427,   158,   397,
+     483,   297,   395,   766,   297,   160,   487,   489,   299,    80,
+      81,    88,    89,    94,   120,   135,   218,   238,   268,   281,
+     288,   297,   333,   334,   349,   350,   359,   407,   439,   609,
+     650,   711,   737,   754,    11,    12,    13,    29,    30,    31,
+      58,    59,    60,   484,   205,   398,   587,   297,   766,   297,
+     350,   107,   124,   163,   258,   297,   350,   384,   416,   495,
+     690,   737,   766,   766,   115,   670,   766,   351,    20,    50,
+      57,   360,    20,    50,    57,   408,   766,   766,   551,   652,
+     766,   738,   764,   126,   275,   332,   419,   728,   297,    35,
+      36,    37,    38,    43,    44,    45,    46,   588,   206,   399,
+     589,   297,   388,   551,   493,   551,   493,   691,   766,   297,
+     766,   670,   297,   765,   297,   297,   766,   297,    77,   149,
+     182,   199,   222,   260,   327,   468,   538,   576,   614,   651,
+     696,   729,   297,     5,   297,   445,   764,   765,   766,   767,
+     297,   297,   297,   297,   241,   620,   297,    84,   339,   385,
+     301,   301,   294,   297,   350,   737,   762,   327,   766,   670,
+     327,   328,   469,   539,   577,   615,   697,   210,   279,   297,
+     350,   596,   650,   734,   446,   766,   297,   341,   552,   297,
+     350,   650,   690,   737,   267,   417,   709,   496,   709,   270,
+     715,   763,   297,   297,   297,   297,    78,   297,   327,   329,
+     332,   150,   297,   466,   468,   766,   183,   184,   297,   538,
+     540,   542,   543,   670,   201,   297,   576,   578,   670,   223,
+     297,   614,   616,   620,   297,   695,   696,   765,   765,     7,
+      10,    14,    15,    16,    17,    19,    21,    22,    25,    33,
+      42,    48,    54,    56,    61,   735,   295,   445,   764,   765,
+     766,   766,   164,   340,   497,   200,   580,   710,    83,   297,
+     336,   350,   690,   737,   297,   336,   350,   690,   737,   766,
+      73,    99,   237,   297,   320,   350,   368,   646,   650,   737,
+     330,   332,   467,   766,   541,   542,   276,   277,   544,   670,
+     730,   731,   579,   670,   617,   620,   297,   297,   297,   494,
+     552,   297,   581,   125,   129,   220,   253,   297,   350,   389,
+     418,   420,   680,   737,   338,   551,   766,   765,   765,   765,
+     297,   395,   297,   395,   297,   395,   297,   297,   544,   297,
+     395,   297,   395,   297,   140,   247,   297,   350,   449,   669,
+     491,   551,   421,   422,   551,   390,   681,    85,   337,   342,
+     766,   297,   282,   369,   741,   647,   741,   544,   670,   670,
+     423,   142,   297,   333,   334,   349,   350,   359,   407,   439,
+     451,   609,   650,   711,   737,   754,   119,   128,   133,   134,
+     152,   154,   197,   198,   207,   208,   244,   297,   350,   405,
+     420,   432,   435,   437,   472,   476,   572,   574,   590,   592,
+     661,   737,   177,   297,   350,   526,   737,    23,    49,    55,
+     343,   283,   285,   297,   350,   650,   690,   737,   742,   746,
+     766,   765,   297,   297,   297,   670,   670,   297,   109,   132,
+     153,   155,   209,   278,   391,   428,   434,   452,   474,   478,
+     594,   732,   551,   668,   668,   668,   668,   668,   668,   668,
+     668,   668,   668,   668,   516,   527,   551,   297,   551,   748,
+     747,   766,   297,   297,   734,   392,   428,   434,   423,   428,
+     434,   475,   428,   434,   479,   766,   428,   434,   733,   297,
+     130,   424,   424,   424,   424,   424,   424,   424,   573,   424,
+     424,   424,   424,    79,    91,    96,   112,   156,   171,   172,
+     246,   262,   289,   291,   297,   331,   350,   354,   363,   400,
+     480,   512,   514,   650,   666,   699,   737,   755,   757,   287,
+     752,   145,   147,   189,   193,   227,   235,   297,   350,   458,
+     462,   555,   563,   625,   640,   737,   766,   297,   297,   428,
+     434,   429,   497,   297,   428,   434,   297,   428,   434,   595,
+     297,   428,   434,   423,   425,   428,   434,   424,    71,    72,
+      76,   123,   139,   162,   254,   318,   319,   324,   415,   433,
+     448,   492,   656,   682,   436,   656,   682,   438,   656,   682,
+     424,   477,   656,   682,   297,   350,   737,   575,   656,   682,
+     424,   424,   185,   242,   545,   657,   662,   332,   517,   552,
+     542,   401,   297,   513,   515,   667,   517,   542,   517,     8,
+      18,    24,    28,    32,    39,    41,    51,    52,    53,   753,
+     151,   470,   743,   148,   456,   459,   463,   194,   556,   567,
+     564,   236,   626,   644,   641,   297,   297,   359,   297,   406,
+     656,   682,   670,   670,   318,   325,   448,   670,   670,   670,
+     656,   683,   297,   350,   737,   297,   350,   737,   297,   350,
+     737,   473,   656,   682,   297,   350,   737,   297,   350,   737,
+     591,   656,   682,   593,   656,   682,   546,   658,   297,   350,
+     737,   297,   297,   297,   297,   517,   297,   517,   297,   517,
+     297,   517,   297,   297,   297,   297,   471,    93,   297,   350,
+     355,   650,   690,   737,   181,   465,   536,   552,   108,   271,
+     297,   350,   386,   650,   716,   146,   297,   350,   456,   460,
+     737,   536,   552,   566,    95,   191,   297,   350,   361,   559,
+     650,   192,   297,   350,   561,   567,   737,   536,   552,   643,
+      62,   100,   101,   102,   103,   229,   297,   303,   350,   370,
+     372,   374,   376,   386,   629,   650,   230,   297,   350,   631,
+     644,   737,   297,   297,   350,   737,   297,   297,   319,   326,
+     492,   297,   297,   297,   297,   297,   350,   737,   297,   350,
+     737,   297,   350,   737,   297,   657,   297,   656,   682,    69,
+     157,   186,   213,   219,   226,   228,   240,   252,   263,   292,
+     297,   315,   350,   386,   481,   547,   602,   610,   623,   627,
+     650,   654,   678,   700,   716,   737,   758,   356,   552,   286,
+     456,   457,   750,   387,   695,   765,   106,   382,   717,   461,
+     456,   567,   568,   750,   362,   466,   766,   382,   562,   456,
+     644,   645,   750,   304,   540,   542,   371,   578,   670,   373,
+     578,   670,   375,   578,   670,   377,   578,   670,   382,   632,
+     297,    68,    70,    74,   314,   316,   317,   321,   482,   548,
+      67,   312,   551,   739,   611,   312,   551,   624,   642,   642,
+     655,   551,   677,   679,   701,   759,    82,    90,   127,   136,
+     144,   175,   188,   190,   202,   211,   231,   250,   293,   297,
+     335,   350,   352,   430,   440,   454,   522,   553,   557,   582,
+     597,   633,   673,   678,   716,   737,   760,   537,   766,   552,
+     749,   297,   297,     9,    34,    47,   383,   137,   217,   297,
+     350,   443,   607,   737,   297,   456,   297,   297,   104,   378,
+     560,   297,   566,   567,   297,   297,   297,   297,   297,   297,
+     167,   378,   502,   630,   297,   643,   644,   221,   612,   256,
+     686,   297,   297,   350,   737,   138,   233,   297,   447,   637,
+     644,   297,   481,   637,   644,   758,   551,   729,   196,   297,
+     570,   610,   644,   110,   280,   297,   303,   350,   370,   372,
+     374,   376,   386,   393,   629,   650,   736,   737,   166,   500,
+     628,   159,   215,   239,   297,   335,   350,   352,   430,   454,
+     485,   600,   633,   648,   650,   737,    65,   234,   290,   297,
+     309,   350,   639,   737,   756,    64,   212,   297,   307,   335,
+     350,   352,   430,   454,   485,   599,   600,   633,   648,   650,
+     737,   297,   481,   637,   644,   243,   659,   353,   422,   426,
+     431,   441,   643,   644,   312,   464,   551,   523,   642,   312,
+     551,   565,   565,   583,   642,   464,   598,   634,   643,   644,
+     674,   765,   274,   726,   761,   297,   766,   339,   751,   297,
+     269,   444,   712,   378,   608,   379,   540,   542,    75,   273,
+     297,   322,   724,   503,   540,   542,   297,   322,   724,   613,
+     297,   264,   687,   702,   297,   643,   638,   766,   603,   734,
+     571,    26,    27,    40,   394,   297,   501,   297,   350,   650,
+     737,   486,   490,   764,   536,   552,   601,   740,   552,   649,
+     653,    98,   310,   366,   642,   516,   308,   695,   765,   659,
+     620,   297,   297,   307,   335,   350,   430,   454,   650,   737,
+      87,   113,   204,   216,   225,   251,   297,   347,   350,   402,
+     585,   605,   621,   659,   675,   737,   265,   442,   644,   704,
+     284,   455,   744,   750,   297,   350,   650,   737,   481,   554,
+     165,   498,   558,   297,   350,   650,   736,   737,   297,   335,
+     350,   352,   454,   553,   557,   599,   633,   737,    92,   297,
+     350,   352,   357,   430,   454,   485,   648,   650,   737,   297,
+     454,   673,   765,   727,    63,   168,   179,   297,   305,   350,
+     440,   504,   530,   737,   297,   122,   203,   413,   584,   713,
+     297,   413,   297,   413,   297,   170,   173,   323,   510,   517,
+     518,   510,   517,   518,   725,   503,   257,   297,   688,   703,
+     297,   297,   297,   643,   644,   313,   766,   297,   297,   610,
+     644,   297,   297,   623,   627,   297,   395,   297,   395,   297,
+     395,   766,   169,   174,   297,   350,   508,   520,   737,   637,
+     670,   297,   297,   620,   660,   620,   403,   620,    97,   364,
+     586,   224,   606,   618,   618,   622,   364,   676,   705,   297,
+     378,   502,   745,   214,   232,   272,   297,   350,   386,   604,
+     635,   650,   716,   718,   737,   297,   350,   352,   361,   430,
+     454,   553,   559,   650,   737,   499,   297,   350,   352,   430,
+     650,   737,   358,   430,    86,   143,   259,   297,   344,   453,
+     692,   306,   542,   505,   643,   644,   531,   643,   644,   414,
+     561,   567,   631,   637,   644,   413,   114,   404,   413,   584,
+     714,   511,   519,   297,   725,   297,   689,   702,   297,   718,
+     297,   367,   404,   509,   637,   643,   644,   521,   637,   643,
+     644,   297,   178,   528,   297,   650,   620,   297,   650,   365,
+     297,   650,   619,   297,   650,   297,   650,   297,   650,   121,
+     266,   297,   409,   706,   297,   744,   750,   740,   636,   643,
+     644,   248,   671,   719,   297,   553,   297,   345,   637,   643,
+     644,   297,   637,   643,   644,   693,   297,   350,   440,   504,
+     530,   737,   176,   506,   524,   644,   704,   378,   502,   532,
+     297,   322,   724,   670,   670,   297,   141,   297,   450,   517,
+     518,   297,   510,   517,   297,   255,   297,   684,   297,   297,
+     528,   297,   528,   529,   297,   348,    66,   297,   311,   620,
+     297,   620,   180,   344,   410,   533,   692,   195,   524,   569,
+     644,   704,   707,   729,   297,   303,   350,   370,   372,   374,
+     376,   386,   629,   650,   736,   737,   766,   249,   672,   720,
+     322,   346,   724,   404,   694,   517,   378,   502,   507,   297,
+     297,   297,   297,   750,   297,   450,   510,   517,   518,   297,
+     650,   620,   534,   637,   643,   644,   411,   524,   569,   644,
+     704,   297,   378,   502,   708,   297,   766,   766,   105,   380,
+     721,   297,   297,   322,   724,   497,   525,   297,   685,   718,
+     620,   510,   517,   518,   535,   378,   412,   502,   297,   297,
+     766,   381,   587,   722,   297,   297,   620,   297,   297,   297,
+     297,   620,   589,   723,   297,   297
+};
+
+  /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint16 yyr1[] =
+{
+       0,   296,   297,   298,   299,   299,   299,   299,   299,   299,
+     299,   300,   301,   302,   303,   304,   304,   305,   306,   306,
+     306,   306,   306,   306,   307,   308,   308,   309,   310,   310,
+     310,   310,   310,   311,   312,   313,   313,   314,   315,   316,
+     316,   316,   316,   316,   317,   318,   319,   320,   321,   322,
+     323,   323,   323,   324,   325,   325,   326,   326,   327,   328,
+     328,   328,   328,   329,   330,   330,   331,   332,   332,   333,
+     334,   335,   336,   337,   337,   337,   337,   337,   337,   337,
+     338,   339,   340,   340,   341,   342,   343,   343,   343,   344,
+     345,   345,   345,   346,   346,   346,   347,   348,   348,   349,
+     350,   351,   351,   352,   353,   353,   353,   353,   353,   353,
+     353,   353,   354,   355,   356,   356,   356,   356,   356,   356,
+     356,   356,   356,   356,   356,   356,   356,   356,   356,   356,
+     356,   356,   357,   358,   358,   359,   360,   360,   360,   361,
+     362,   362,   363,   364,   365,   365,   365,   366,   367,   367,
+     368,   369,   369,   370,   371,   371,   372,   373,   373,   374,
+     375,   375,   376,   377,   377,   378,   379,   379,   380,   381,
+     381,   382,   383,   383,   383,   384,   385,   385,   385,   385,
+     385,   386,   387,   387,   388,   389,   390,   390,   390,   390,
+     390,   390,   390,   390,   390,   390,   390,   390,   390,   390,
+     390,   391,   392,   392,   392,   392,   393,   394,   394,   394,
+     395,   396,   396,   397,   397,   398,   398,   399,   399,   400,
+     401,   401,   402,   403,   403,   404,   405,   406,   406,   406,
+     406,   407,   408,   408,   408,   409,   410,   410,   410,   411,
+     411,   411,   411,   412,   412,   412,   413,   414,   414,   414,
+     414,   414,   414,   414,   415,   416,   417,   417,   417,   417,
+     417,   418,   419,   420,   421,   421,   421,   421,   421,   421,
+     421,   421,   421,   421,   421,   421,   421,   421,   422,   423,
+     424,   425,   425,   425,   426,   427,   427,   427,   427,   427,
+     427,   427,   427,   427,   427,   427,   427,   427,   428,   429,
+     429,   430,   431,   431,   431,   431,   431,   431,   431,   431,
+     431,   431,   431,   432,   433,   433,   433,   433,   434,   434,
+     434,   434,   434,   435,   436,   436,   436,   436,   437,   438,
+     438,   438,   438,   439,   440,   441,   441,   442,   442,   442,
+     442,   443,   444,   444,   445,   446,   446,   446,   446,   446,
+     447,   448,   449,   450,   451,   452,   452,   453,   454,   455,
+     455,   455,   455,   455,   455,   455,   455,   455,   455,   456,
+     457,   457,   457,   458,   459,   459,   459,   459,   459,   460,
+     461,   461,   462,   463,   463,   463,   463,   463,   464,   464,
+     465,   465,   466,   467,   467,   468,   469,   469,   469,   469,
+     470,   471,   471,   471,   471,   471,   471,   471,   471,   471,
+     471,   471,   471,   471,   471,   471,   471,   471,   472,   473,
+     473,   473,   473,   474,   475,   475,   475,   475,   476,   477,
+     477,   477,   477,   478,   479,   479,   480,   481,   482,   482,
+     482,   482,   483,   484,   484,   484,   484,   484,   484,   484,
+     484,   484,   485,   486,   486,   487,   488,   489,   489,   490,
+     491,   492,   493,   494,   495,   496,   496,   496,   496,   496,
+     497,   498,   499,   499,   500,   501,   501,   501,   502,   503,
+     503,   504,   505,   505,   506,   506,   506,   507,   507,   507,
+     508,   509,   509,   509,   509,   510,   511,   511,   511,   511,
+     512,   513,   513,   514,   515,   515,   516,   517,   518,   519,
+     519,   519,   520,   521,   521,   521,   521,   522,   523,   523,
+     523,   523,   524,   525,   525,   526,   527,   527,   527,   527,
+     527,   527,   527,   527,   527,   527,   527,   527,   527,   527,
+     527,   528,   529,   529,   529,   529,   529,   530,   531,   531,
+     532,   532,   532,   533,   534,   534,   534,   535,   535,   535,
+     536,   537,   537,   538,   539,   539,   539,   539,   540,   541,
+     541,   542,   542,   543,   544,   544,   544,   545,   546,   546,
+     547,   548,   548,   548,   548,   548,   549,   550,   550,   551,
+     551,   551,   552,   552,   553,   554,   554,   554,   554,   554,
+     554,   554,   554,   554,   554,   555,   556,   556,   556,   556,
+     556,   557,   558,   558,   558,   558,   558,   558,   559,   560,
+     560,   560,   561,   562,   562,   562,   563,   564,   564,   564,
+     564,   564,   565,   565,   566,   566,   567,   568,   568,   568,
+     568,   569,   570,   571,   571,   571,   572,   573,   573,   573,
+     574,   575,   575,   575,   575,   576,   577,   577,   577,   577,
+     578,   579,   579,   580,   581,   581,   581,   581,   582,   583,
+     583,   583,   583,   583,   584,   585,   586,   586,   587,   588,
+     588,   588,   588,   588,   588,   588,   588,   589,   590,   591,
+     591,   591,   591,   592,   593,   593,   593,   593,   594,   595,
+     595,   596,   597,   598,   598,   598,   598,   598,   598,   598,
+     598,   598,   598,   599,   600,   601,   601,   602,   603,   603,
+     604,   605,   606,   606,   607,   608,   608,   609,   610,   611,
+     611,   611,   611,   612,   613,   613,   614,   615,   615,   615,
+     615,   616,   617,   617,   618,   619,   619,   620,   621,   622,
+     622,   623,   624,   624,   624,   624,   624,   624,   624,   624,
+     624,   624,   624,   624,   624,   625,   626,   626,   626,   626,
+     626,   626,   626,   626,   626,   626,   627,   628,   628,   628,
+     628,   629,   630,   630,   630,   630,   631,   632,   632,   632,
+     633,   634,   634,   634,   634,   634,   634,   634,   634,   634,
+     634,   634,   635,   636,   636,   636,   636,   636,   636,   636,
+     636,   636,   636,   636,   636,   636,   637,   638,   638,   638,
+     639,   640,   641,   641,   641,   641,   641,   642,   642,   643,
+     643,   644,   645,   645,   645,   645,   646,   647,   647,   648,
+     649,   649,   650,   651,   651,   651,   651,   651,   652,   653,
+     654,   655,   655,   655,   655,   655,   655,   655,   655,   655,
+     655,   655,   655,   656,   656,   656,   656,   656,   656,   657,
+     658,   658,   658,   659,   660,   660,   661,   662,   662,   662,
+     662,   663,   664,   664,   665,   665,   666,   667,   667,   668,
+     669,   670,   670,   671,   672,   673,   674,   674,   674,   674,
+     675,   676,   676,   677,   678,   679,   679,   679,   679,   679,
+     679,   680,   681,   681,   681,   681,   682,   683,   683,   684,
+     685,   685,   686,   687,   687,   688,   689,   689,   690,   691,
+     691,   691,   691,   692,   693,   693,   693,   694,   694,   694,
+     695,   696,   697,   697,   697,   697,   698,   698,   699,   700,
+     701,   701,   701,   701,   701,   701,   701,   701,   701,   701,
+     701,   701,   701,   701,   702,   703,   703,   704,   705,   705,
+     705,   706,   707,   707,   707,   707,   708,   708,   708,   709,
+     710,   710,   710,   710,   710,   710,   710,   711,   712,   713,
+     713,   714,   714,   714,   715,   716,   717,   717,   717,   717,
+     717,   718,   719,   719,   720,   720,   721,   721,   722,   722,
+     723,   723,   724,   725,   725,   725,   726,   727,   727,   727,
+     727,   728,   729,   729,   729,   729,   729,   729,   730,   731,
+     732,   733,   733,   733,   733,   734,   735,   735,   735,   735,
+     735,   735,   735,   735,   735,   735,   735,   735,   735,   735,
+     735,   735,   736,   737,   738,   738,   738,   738,   738,   739,
+     739,   740,   740,   741,   742,   743,   743,   743,   743,   743,
+     743,   744,   745,   745,   745,   746,   747,   747,   747,   747,
+     747,   747,   747,   747,   747,   748,   749,   750,   751,   751,
+     752,   753,   753,   753,   753,   753,   753,   753,   753,   753,
+     753,   754,   755,   756,   757,   758,   759,   759,   759,   759,
+     760,   761,   761,   761,   761,   761,   761,   761,   762,   763,
+     763,   763,   763,   763,   763,   763,   764,   765,   766,   767
+};
+
+  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     1,     7,     0,     2,     2,     2,     2,     2,
+       2,     1,     3,     5,     3,     1,     1,     3,     1,     2,
+       2,     2,     2,     2,     3,     1,     1,     3,     1,     2,
+       2,     2,     2,     5,     5,     0,     1,     3,     3,     1,
+       1,     1,     2,     2,     3,     3,     3,     3,     2,     3,
+       1,     1,     1,     4,     1,     1,     1,     1,     3,     0,
+       2,     2,     2,     3,     1,     2,     3,     1,     1,     5,
+       3,     3,     4,     1,     2,     2,     2,     2,     2,     2,
+       1,     4,     0,     1,     1,     3,     1,     1,     1,     4,
+       1,     1,     1,     0,     1,     1,     5,     0,     2,     5,
+       3,     0,     2,     3,     0,     2,     2,     2,     2,     2,
+       2,     2,     3,     3,     0,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     3,     0,     1,     3,     1,     1,     1,     3,
+       1,     1,     3,     3,     0,     2,     2,     4,     0,     1,
+       4,     0,     1,     3,     1,     1,     3,     1,     1,     3,
+       1,     1,     3,     1,     1,     3,     1,     1,     3,     0,
+       2,     3,     1,     1,     1,     4,     1,     2,     2,     2,
+       2,     3,     1,     1,     1,     3,     0,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     3,     1,     1,     2,     2,     3,     1,     1,     1,
+       6,     1,     1,     0,     1,     0,     1,     0,     1,     3,
+       0,     2,     3,     1,     2,     3,     6,     1,     1,     2,
+       2,     3,     1,     1,     1,     5,     1,     1,     1,     1,
+       1,     1,     1,     0,     1,     1,     3,     1,     1,     1,
+       1,     1,     2,     2,     3,     5,     1,     2,     2,     2,
+       2,     4,     2,     3,     1,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     1,     1,
+       3,     1,     1,     1,     3,     1,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     4,     0,
+       1,     3,     1,     1,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     5,     1,     1,     2,     2,     1,     1,
+       1,     1,     1,     5,     1,     1,     2,     2,     5,     1,
+       1,     2,     2,     5,     4,     1,     1,     1,     1,     2,
+       2,     3,     1,     2,     3,     0,     2,     2,     2,     2,
+       3,     3,     4,     2,     3,     1,     1,     2,     4,     1,
+       1,     2,     2,     2,     2,     2,     2,     2,     2,     4,
+       0,     1,     1,     3,     1,     2,     2,     2,     2,     3,
+       0,     2,     3,     0,     2,     2,     2,     2,     1,     1,
+       1,     1,     3,     1,     2,     3,     0,     2,     2,     2,
+       3,     0,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     6,     1,
+       1,     2,     2,     3,     1,     1,     2,     2,     5,     1,
+       1,     2,     2,     3,     1,     1,     2,     3,     0,     2,
+       2,     2,     3,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     3,     1,     2,     3,     3,     1,     2,     1,
+       1,     3,     1,     1,     5,     1,     2,     2,     2,     2,
+       3,     3,     0,     2,     3,     0,     2,     2,     4,     1,
+       1,     5,     1,     1,     1,     1,     1,     0,     1,     1,
+       3,     1,     1,     1,     2,     3,     0,     2,     2,     2,
+       3,     0,     2,     3,     0,     2,     1,     1,     3,     0,
+       2,     2,     3,     1,     1,     1,     2,     3,     1,     2,
+       2,     2,     4,     0,     1,     3,     1,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     3,     0,     2,     2,     2,     2,     4,     1,     1,
+       0,     1,     1,     4,     1,     1,     1,     1,     1,     1,
+       4,     1,     2,     3,     0,     2,     2,     2,     3,     1,
+       2,     1,     1,     5,     1,     1,     1,     3,     0,     2,
+       3,     0,     2,     2,     2,     2,     3,     1,     2,     1,
+       1,     1,     1,     1,     4,     1,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     3,     1,     2,     2,     2,
+       2,     4,     1,     2,     2,     2,     2,     2,     4,     1,
+       2,     2,     3,     0,     2,     2,     3,     0,     2,     2,
+       2,     2,     1,     1,     1,     1,     4,     0,     1,     1,
+       1,     2,     3,     0,     2,     2,     4,     1,     2,     2,
+       5,     1,     1,     2,     2,     3,     0,     2,     2,     2,
+       3,     1,     2,     3,     0,     2,     2,     2,     3,     1,
+       2,     2,     2,     2,     4,     3,     1,     2,     3,     1,
+       1,     1,     1,     1,     1,     1,     1,     3,     6,     1,
+       1,     2,     2,     6,     1,     1,     2,     2,     5,     1,
+       1,     3,     3,     1,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     3,     3,     1,     2,     5,     0,     1,
+       4,     3,     1,     2,     3,     1,     2,     3,     3,     0,
+       2,     2,     2,     3,     0,     2,     3,     0,     2,     2,
+       2,     3,     1,     2,     3,     0,     2,     4,     3,     1,
+       2,     3,     1,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     3,     1,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     4,     1,     2,     2,
+       2,     4,     1,     1,     2,     2,     3,     0,     2,     2,
+       3,     1,     1,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     3,     1,     1,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     3,     0,     2,     2,
+       4,     3,     0,     2,     2,     2,     2,     1,     1,     1,
+       1,     4,     0,     1,     1,     1,     4,     0,     1,     3,
+       1,     2,     4,     1,     2,     2,     2,     2,     1,     1,
+       3,     0,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     1,     1,     1,     1,     1,     1,     3,
+       0,     2,     2,     4,     1,     2,     5,     1,     1,     2,
+       2,     4,     1,     1,     1,     1,     3,     0,     2,     1,
+       5,     1,     4,     4,     4,     3,     1,     2,     2,     2,
+       3,     1,     2,     1,     3,     1,     2,     2,     2,     2,
+       2,     3,     0,     2,     2,     2,     3,     0,     1,     4,
+       0,     1,     3,     0,     1,     3,     1,     2,     3,     0,
+       2,     2,     2,     4,     1,     1,     1,     1,     2,     2,
+       3,     3,     0,     2,     2,     2,     1,     2,     3,     3,
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     3,     0,     2,     3,     0,     2,
+       2,     4,     1,     1,     1,     1,     0,     1,     1,     3,
+       1,     2,     2,     2,     2,     2,     2,     3,     4,     1,
+       1,     1,     1,     1,     8,     3,     1,     2,     2,     2,
+       2,     7,     0,     1,     0,     1,     0,     1,     0,     1,
+       0,     1,     4,     1,     1,     1,     3,     0,     2,     2,
+       2,     2,     1,     1,     1,     1,     1,     1,     2,     2,
+       3,     1,     1,     2,     2,     3,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     2,     3,     1,     2,     2,     2,     2,     1,
+       1,     1,     1,     3,     5,     1,     2,     2,     2,     2,
+       2,     3,     0,     2,     2,     3,     0,     2,     2,     2,
+       2,     2,     2,     2,     2,     1,     1,     4,     0,     1,
+       3,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     3,     3,     5,     3,     3,     0,     2,     2,     2,
+       3,     1,     2,     2,     2,     2,     2,     2,     3,     1,
+       2,     2,     2,     2,     2,     2,     1,     1,     1,     1
+};
+
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+#define YYEMPTY         (-2)
+#define YYEOF           0
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                  \
+do                                                              \
+  if (yychar == YYEMPTY)                                        \
+    {                                                           \
+      yychar = (Token);                                         \
+      yylval = (Value);                                         \
+      YYPOPSTACK (yylen);                                       \
+      yystate = *yyssp;                                         \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    {                                                           \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                  \
+    }                                                           \
+while (0)
+
+/* Error token number */
+#define YYTERROR        1
+#define YYERRCODE       256
+
+
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Type, Value); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT.  |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+  FILE *yyo = yyoutput;
+  YYUSE (yyo);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+  YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+  YYFPRINTF (yyoutput, "%s %s (",
+             yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule)
+{
+  unsigned long int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       yystos[yyssp[yyi + 1 - yynrhs]],
+                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                                              );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+        switch (*++yyp)
+          {
+          case '\'':
+          case ',':
+            goto do_not_strip_quotes;
+
+          case '\\':
+            if (*++yyp != '\\')
+              goto do_not_strip_quotes;
+            /* Fall through.  */
+          default:
+            if (yyres)
+              yyres[yyn] = *yyp;
+            yyn++;
+            break;
+
+          case '"':
+            if (yyres)
+              yyres[yyn] = '\0';
+            return yyn;
+          }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = YY_NULLPTR;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                {
+                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+                  if (! (yysize <= yysize1
+                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                    return 2;
+                  yysize = yysize1;
+                }
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  {
+    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+      return 2;
+    yysize = yysize1;
+  }
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+{
+  YYUSE (yyvaluep);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YYUSE (yytype);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (void)
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       'yyss': related to states.
+       'yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        YYSTYPE *yyvs1 = yyvs;
+        yytype_int16 *yyss1 = yyss;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * sizeof (*yyssp),
+                    &yyvs1, yysize * sizeof (*yyvsp),
+                    &yystacksize);
+
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yytype_int16 *yyss1 = yyss;
+        union yyalloc *yyptr =
+          (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+        if (! yyptr)
+          goto yyexhaustedlab;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = yylex ();
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 510 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { PopC(); }
+#line 3373 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 11:
+#line 525 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3379 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 12:
+#line 528 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 3385 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 13:
+#line 532 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-3].s)); free((yyvsp[-2].s)); free((yyvsp[-1].s)); }
+#line 3391 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 25:
+#line 556 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3397 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 34:
+#line 573 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[-3].ps)); free((yyvsp[-2].s)); }
+#line 3403 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 36:
+#line 577 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3409 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 47:
+#line 602 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 3415 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 69:
+#line 650 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-3].s)); free((yyvsp[-2].s)); }
+#line 3421 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 70:
+#line 653 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 3427 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 80:
+#line 671 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3433 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 84:
+#line 681 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3439 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 91:
+#line 696 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3445 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 102:
+#line 719 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3451 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 140:
+#line 779 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3457 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 147:
+#line 794 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); }
+#line 3463 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 150:
+#line 801 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); }
+#line 3469 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 182:
+#line 871 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3475 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 184:
+#line 875 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3481 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 240:
+#line 979 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3487 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 247:
+#line 992 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3493 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 278:
+#line 1039 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3499 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 279:
+#line 1042 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3505 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 333:
+#line 1128 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-3].s)); free((yyvsp[-2].s)); }
+#line 3511 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 336:
+#line 1135 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3517 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 337:
+#line 1138 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3523 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 344:
+#line 1151 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); }
+#line 3529 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 346:
+#line 1155 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3535 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 347:
+#line 1156 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3541 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 348:
+#line 1157 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3547 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 369:
+#line 1198 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[-2].s); }
+#line 3553 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 371:
+#line 1202 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3559 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 374:
+#line 1209 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3565 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 381:
+#line 1220 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3571 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 384:
+#line 1227 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3577 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 388:
+#line 1233 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3583 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 390:
+#line 1237 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3589 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 393:
+#line 1244 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3595 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 397:
+#line 1252 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3601 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 408:
+#line 1267 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { pair_list_free((yyvsp[0].pl)); }
+#line 3607 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 437:
+#line 1317 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.pl) = new_pair_list((yyvsp[-1].ps)); }
+#line 3613 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 438:
+#line 1320 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=NULL; }
+#line 3619 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 439:
+#line 1321 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyvsp[0].ps)->next = (yyvsp[-1].ps); (yyval.ps) = (yyvsp[0].ps); }
+#line 3625 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 455:
+#line 1347 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 3631 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 459:
+#line 1357 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3637 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 460:
+#line 1360 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3643 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 462:
+#line 1366 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3649 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 463:
+#line 1369 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3655 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 483:
+#line 1411 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3661 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 484:
+#line 1414 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3667 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 492:
+#line 1428 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3673 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 506:
+#line 1456 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3679 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 507:
+#line 1459 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3685 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 514:
+#line 1474 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3691 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 549:
+#line 1529 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3697 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 555:
+#line 1541 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3703 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 560:
+#line 1550 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); }
+#line 3709 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 561:
+#line 1553 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3715 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 562:
+#line 1554 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3721 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 582:
+#line 1596 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3727 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 585:
+#line 1599 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { pair_list_free((yyvsp[0].pl)); }
+#line 3733 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 586:
+#line 1602 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[-1].s); }
+#line 3739 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 587:
+#line 1605 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3745 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 589:
+#line 1609 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps) = new_str_pair((yyvsp[0].s),NULL); }
+#line 3751 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 590:
+#line 1610 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps) = new_str_pair((yyvsp[0].s),NULL); }
+#line 3757 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 591:
+#line 1611 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=(yyvsp[0].ps); }
+#line 3763 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 592:
+#line 1614 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3769 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 593:
+#line 1615 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3775 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 594:
+#line 1618 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { define_pcb_net((yyvsp[-2].ps), (yyvsp[-1].pl)); }
+#line 3781 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 595:
+#line 1621 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.pl)=(yyvsp[0].pl); }
+#line 3787 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 611:
+#line 1643 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[-2].ps)); }
+#line 3793 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 632:
+#line 1680 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=(yyvsp[0].ps); }
+#line 3799 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 633:
+#line 1681 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=NULL; }
+#line 3805 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 634:
+#line 1685 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3811 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 639:
+#line 1694 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3817 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 644:
+#line 1705 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3823 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 698:
+#line 1807 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-3].s)); }
+#line 3829 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 701:
+#line 1814 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 3835 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 727:
+#line 1866 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 3841 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 730:
+#line 1873 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3847 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 747:
+#line 1908 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); free((yyvsp[-1].s)); }
+#line 3853 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 766:
+#line 1939 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3859 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 789:
+#line 1974 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3865 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 791:
+#line 1980 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3871 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 803:
+#line 1996 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3877 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 818:
+#line 2015 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3883 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 823:
+#line 2026 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3889 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 827:
+#line 2032 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3895 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 829:
+#line 2036 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3901 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 831:
+#line 2041 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    {
+    if ((yyvsp[-1].ps))
+    {
+	(yyval.ps) = new_str_pair((yyvsp[-1].ps)->str1,(yyvsp[-2].s));
+	free((yyvsp[-1].ps));
+    }
+    else
+    {
+	/* handle port with no instance by passing up the chain */
+	(yyval.ps) = new_str_pair(NULL,(yyvsp[-2].s));
+    }
+}
+#line 3918 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 832:
+#line 2055 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=NULL; }
+#line 3924 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 833:
+#line 2056 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=(yyvsp[0].ps); }
+#line 3930 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 834:
+#line 2057 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps) = new_str_pair((yyvsp[0].s),NULL); }
+#line 3936 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 835:
+#line 2058 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps)=NULL; }
+#line 3942 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 848:
+#line 2085 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3948 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 849:
+#line 2088 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3954 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 881:
+#line 2141 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.ps) = new_str_pair((yyvsp[-2].s),(yyvsp[-1].s)); }
+#line 3960 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 882:
+#line 2144 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3966 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 883:
+#line 2145 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3972 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 884:
+#line 2148 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 3978 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 885:
+#line 2149 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=NULL; }
+#line 3984 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 889:
+#line 2159 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 3990 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 891:
+#line 2165 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 3996 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 892:
+#line 2166 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); free((yyvsp[-1].s)); }
+#line 4002 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 893:
+#line 2169 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); free((yyvsp[-1].s)); }
+#line 4008 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 894:
+#line 2172 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-2].s)); free((yyvsp[-1].s)); }
+#line 4014 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 896:
+#line 2178 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4020 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 898:
+#line 2180 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4026 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 903:
+#line 2191 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 4032 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 935:
+#line 2255 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 4038 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 943:
+#line 2271 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4044 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 946:
+#line 2276 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4050 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 973:
+#line 2321 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 4056 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 987:
+#line 2343 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 4062 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 994:
+#line 2359 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-6].s)); free((yyvsp[-5].s)); free((yyvsp[-4].s)); free((yyvsp[-3].s)); free((yyvsp[-2].s)); free((yyvsp[-1].s)); }
+#line 4068 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1054:
+#line 2466 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4074 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1055:
+#line 2467 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4080 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1056:
+#line 2468 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4086 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1057:
+#line 2469 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4092 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1059:
+#line 2473 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 4098 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1061:
+#line 2477 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4104 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1063:
+#line 2481 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[-1].s)); }
+#line 4110 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1085:
+#line 2517 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 4116 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1086:
+#line 2520 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { free((yyvsp[0].s)); }
+#line 4122 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1107:
+#line 2561 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { str_pair_free((yyvsp[0].ps)); }
+#line 4128 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1109:
+#line 2563 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { pair_list_free((yyvsp[0].pl)); }
+#line 4134 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1126:
+#line 2590 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 4140 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1127:
+#line 2593 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 4146 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1128:
+#line 2596 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 4152 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+  case 1129:
+#line 2599 "../../src_plugins/import_edif/edif.y" /* yacc.c:1646  */
+    { (yyval.s)=(yyvsp[0].s); }
+#line 4158 "edif.tab.c" /* yacc.c:1646  */
+    break;
+
+
+#line 4162 "edif.tab.c" /* yacc.c:1646  */
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == YYEOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval);
+          yychar = YYEMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYTERROR;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  return yyresult;
+}
+#line 2602 "../../src_plugins/import_edif/edif.y" /* yacc.c:1906  */
+
+/*
+ *	xmalloc:
+ *
+ *	  Garbage function for 'alloca()'.
+ */
+char *xmalloc(int siz)
+{
+  return ((char *)Malloc(siz));
+}
+/*
+ *	Token & context carriers:
+ *
+ *	  These are the linkage pointers for threading this context garbage
+ *	for converting identifiers into parser tokens.
+ */
+typedef struct TokenCar {
+  struct TokenCar *Next;	/* pointer to next carrier */
+  struct Token *Token;		/* associated token */
+} TokenCar;
+typedef struct UsedCar {
+  struct UsedCar *Next;		/* pointer to next carrier */
+  short Code;			/* used '%token' value */
+} UsedCar;
+typedef struct ContextCar {
+  struct ContextCar *Next;	/* pointer to next carrier */
+  struct Context *Context;	/* associated context */
+  union {
+    int Single;			/* single usage flag (context tree) */
+    struct UsedCar *Used;	/* single used list (context stack) */
+  } u;
+} ContextCar;
+/*
+ *	Token definitions:
+ *
+ *	  This associates the '%token' codings with strings which are to
+ *	be free standing tokens. Doesn't have to be in sorted order but the
+ *	strings must be in lower case.
+ */
+typedef struct Token {
+  const char *Name;			/* token name */
+  int Code;			/* '%token' value */
+  struct Token *Next;		/* hash table linkage */
+} Token;
+static Token TokenDef[] = {
+  {"angle",		EDIF_TOK_ANGLE},
+  {"behavior",		EDIF_TOK_BEHAVIOR},
+  {"calculated",	EDIF_TOK_CALCULATED},
+  {"capacitance",	EDIF_TOK_CAPACITANCE},
+  {"centercenter",	EDIF_TOK_CENTERCENTER},
+  {"centerleft",	EDIF_TOK_CENTERLEFT},
+  {"centerright",	EDIF_TOK_CENTERRIGHT},
+  {"charge",		EDIF_TOK_CHARGE},
+  {"conductance",	EDIF_TOK_CONDUCTANCE},
+  {"current",		EDIF_TOK_CURRENT},
+  {"distance",		EDIF_TOK_DISTANCE},
+  {"document",		EDIF_TOK_DOCUMENT},
+  {"energy",		EDIF_TOK_ENERGY},
+  {"extend",		EDIF_TOK_EXTEND},
+  {"flux",		EDIF_TOK_FLUX},
+  {"frequency",		EDIF_TOK_FREQUENCY},
+  {"generic",		EDIF_TOK_GENERIC},
+  {"graphic",		EDIF_TOK_GRAPHIC},
+  {"inductance",	EDIF_TOK_INDUCTANCE},
+  {"inout",		EDIF_TOK_INOUT},
+  {"input",		EDIF_TOK_INPUT},
+  {"logicmodel",	EDIF_TOK_LOGICMODEL},
+  {"lowercenter",	EDIF_TOK_LOWERCENTER},
+  {"lowerleft",		EDIF_TOK_LOWERLEFT},
+  {"lowerright",	EDIF_TOK_LOWERRIGHT},
+  {"masklayout",	EDIF_TOK_MASKLAYOUT},
+  {"mass",		EDIF_TOK_MASS},
+  {"measured",		EDIF_TOK_MEASURED},
+  {"mx",		EDIF_TOK_MX},
+  {"mxr90",		EDIF_TOK_MXR90},
+  {"my",		EDIF_TOK_MY},
+  {"myr90",		EDIF_TOK_MYR90},
+  {"netlist",		EDIF_TOK_NETLIST},
+  {"output",		EDIF_TOK_OUTPUT},
+  {"pcblayout",		EDIF_TOK_PCBLAYOUT},
+  {"power",		EDIF_TOK_POWER},
+  {"r0",		EDIF_TOK_R0},
+  {"r180",		EDIF_TOK_R180},
+  {"r270",		EDIF_TOK_R270},
+  {"r90",		EDIF_TOK_R90},
+  {"required",		EDIF_TOK_REQUIRED},
+  {"resistance",	EDIF_TOK_RESISTANCE},
+  {"ripper",		EDIF_TOK_RIPPER},
+  {"round",		EDIF_TOK_ROUND},
+  {"schematic",		EDIF_TOK_SCHEMATIC},
+  {"stranger",		EDIF_TOK_STRANGER},
+  {"symbolic",		EDIF_TOK_SYMBOLIC},
+  {"temperature",	EDIF_TOK_TEMPERATURE},
+  {"tie",		EDIF_TOK_TIE},
+  {"time",		EDIF_TOK_TIME},
+  {"truncate",		EDIF_TOK_TRUNCATE},
+  {"uppercenter",	EDIF_TOK_UPPERCENTER},
+  {"upperleft",		EDIF_TOK_UPPERLEFT},
+  {"upperright",	EDIF_TOK_UPPERRIGHT},
+  {"voltage",		EDIF_TOK_VOLTAGE}
+};
+static int TokenDefSize = sizeof(TokenDef) / sizeof(Token);
+/*
+ *	Token enable definitions:
+ *
+ *	  There is one array for each set of tokens enabled by a
+ *	particular context (barf). Another array is used to bind
+ *	these arrays to a context.
+ */
+static short e_CellType[] = {EDIF_TOK_TIE, EDIF_TOK_RIPPER, EDIF_TOK_GENERIC};
+static short e_CornerType[] = {EDIF_TOK_EXTEND, EDIF_TOK_TRUNCATE,
+			       EDIF_TOK_ROUND};
+static short e_Derivation[] = {EDIF_TOK_CALCULATED, EDIF_TOK_MEASURED,
+			       EDIF_TOK_REQUIRED};
+static short e_Direction[] = {EDIF_TOK_INPUT, EDIF_TOK_OUTPUT,
+			      EDIF_TOK_INOUT};
+static short e_EndType[] = {EDIF_TOK_EXTEND, EDIF_TOK_TRUNCATE,
+			    EDIF_TOK_ROUND};
+static short e_Justify[] = {EDIF_TOK_CENTERCENTER, EDIF_TOK_CENTERLEFT,
+			    EDIF_TOK_CENTERRIGHT, EDIF_TOK_LOWERCENTER,
+			    EDIF_TOK_LOWERLEFT, EDIF_TOK_LOWERRIGHT,
+			    EDIF_TOK_UPPERCENTER, EDIF_TOK_UPPERLEFT,
+			    EDIF_TOK_UPPERRIGHT};
+static short e_Orientation[] = {EDIF_TOK_R0, EDIF_TOK_R90, EDIF_TOK_R180,
+				EDIF_TOK_R270, EDIF_TOK_MX, EDIF_TOK_MY,
+				EDIF_TOK_MXR90, EDIF_TOK_MYR90};
+static short e_Unit[] = {EDIF_TOK_DISTANCE, EDIF_TOK_CAPACITANCE,
+			 EDIF_TOK_CURRENT, EDIF_TOK_RESISTANCE,
+			 EDIF_TOK_TEMPERATURE, EDIF_TOK_TIME,
+			 EDIF_TOK_VOLTAGE, EDIF_TOK_MASS, EDIF_TOK_FREQUENCY,
+			 EDIF_TOK_INDUCTANCE, EDIF_TOK_ENERGY,
+			 EDIF_TOK_POWER, EDIF_TOK_CHARGE,
+			 EDIF_TOK_CONDUCTANCE, EDIF_TOK_FLUX, EDIF_TOK_ANGLE};
+static short e_ViewType[] = {EDIF_TOK_MASKLAYOUT, EDIF_TOK_PCBLAYOUT,
+			     EDIF_TOK_NETLIST, EDIF_TOK_SCHEMATIC,
+			     EDIF_TOK_SYMBOLIC, EDIF_TOK_BEHAVIOR,
+			     EDIF_TOK_LOGICMODEL, EDIF_TOK_DOCUMENT,
+			     EDIF_TOK_GRAPHIC, EDIF_TOK_STRANGER};
+/*
+ *	Token tying table:
+ *
+ *	  This binds enabled tokens to a context.
+ */
+typedef struct Tie {
+  short *Enable;		/* pointer to enable array */
+  short Origin;			/* '%token' value of context */
+  short EnableSize;		/* size of enabled array */
+} Tie;
+#define	TE(e,o)			{e,o,sizeof(e)/sizeof(short)}
+static Tie TieDef[] = {
+  TE(e_CellType,	EDIF_TOK_CELLTYPE),
+  TE(e_CornerType,	EDIF_TOK_CORNERTYPE),
+  TE(e_Derivation,	EDIF_TOK_DERIVATION),
+  TE(e_Direction,	EDIF_TOK_DIRECTION),
+  TE(e_EndType,		EDIF_TOK_ENDTYPE),
+  TE(e_Justify,		EDIF_TOK_JUSTIFY),
+  TE(e_Orientation,	EDIF_TOK_ORIENTATION),
+  TE(e_Unit,		EDIF_TOK_UNIT),
+  TE(e_ViewType,	EDIF_TOK_VIEWTYPE)
+};
+static int TieDefSize = sizeof(TieDef) / sizeof(Tie);
+/*
+ *	Context definitions:
+ *
+ *	  This associates keyword strings with '%token' values. It
+ *	also creates a pretty much empty header for later building of
+ *	the context tree. Again they needn't be sorted, but strings
+ *	must be lower case.
+ */
+typedef struct Context {
+  const char *Name;			/* keyword name */
+  short Code;			/* '%token' value */
+  short Flags;			/* special operation flags */
+  struct ContextCar *Context;	/* contexts which can be moved to */
+  struct TokenCar *Token;	/* active tokens */
+  struct Context *Next;		/* hash table linkage */
+} Context;
+static Context ContextDef[] = {
+  {"",				0},		/* start context */
+  {"acload",			EDIF_TOK_ACLOAD},
+  {"after",			EDIF_TOK_AFTER},
+  {"annotate",			EDIF_TOK_ANNOTATE},
+  {"apply",			EDIF_TOK_APPLY},
+  {"arc",			EDIF_TOK_ARC},
+  {"array",			EDIF_TOK_ARRAY},
+  {"arraymacro",		EDIF_TOK_ARRAYMACRO},
+  {"arrayrelatedinfo",		EDIF_TOK_ARRAYRELATEDINFO},
+  {"arraysite",			EDIF_TOK_ARRAYSITE},
+  {"atleast",			EDIF_TOK_ATLEAST},
+  {"atmost",			EDIF_TOK_ATMOST},
+  {"author",			EDIF_TOK_AUTHOR},
+  {"basearray",			EDIF_TOK_BASEARRAY},
+  {"becomes",			EDIF_TOK_BECOMES},
+  {"between",			EDIF_TOK_BETWEEN},
+  {"boolean",			EDIF_TOK_BOOLEAN},
+  {"booleandisplay",		EDIF_TOK_BOOLEANDISPLAY},
+  {"booleanmap",		EDIF_TOK_BOOLEANMAP},
+  {"borderpattern",		EDIF_TOK_BORDERPATTERN},
+  {"borderwidth",		EDIF_TOK_BORDERWIDTH},
+  {"boundingbox",		EDIF_TOK_BOUNDINGBOX},
+  {"cell",			EDIF_TOK_CELL},
+  {"cellref",			EDIF_TOK_CELLREF},
+  {"celltype",			EDIF_TOK_CELLTYPE},
+  {"change",			EDIF_TOK_CHANGE},
+  {"circle",			EDIF_TOK_CIRCLE},
+  {"color",			EDIF_TOK_COLOR},
+  {"comment",			EDIF_TOK_COMMENT},
+  {"commentgraphics",		EDIF_TOK_COMMENTGRAPHICS},
+  {"compound",			EDIF_TOK_COMPOUND},
+  {"connectlocation",		EDIF_TOK_CONNECTLOCATION},
+  {"contents",			EDIF_TOK_CONTENTS},
+  {"cornertype",		EDIF_TOK_CORNERTYPE},
+  {"criticality",		EDIF_TOK_CRITICALITY},
+  {"currentmap",		EDIF_TOK_CURRENTMAP},
+  {"curve",			EDIF_TOK_CURVE},
+  {"cycle",			EDIF_TOK_CYCLE},
+  {"dataorigin",		EDIF_TOK_DATAORIGIN},
+  {"dcfaninload",		EDIF_TOK_DCFANINLOAD},
+  {"dcfanoutload",		EDIF_TOK_DCFANOUTLOAD},
+  {"dcmaxfanin",		EDIF_TOK_DCMAXFANIN},
+  {"dcmaxfanout",		EDIF_TOK_DCMAXFANOUT},
+  {"delay",			EDIF_TOK_DELAY},
+  {"delta",			EDIF_TOK_DELTA},
+  {"derivation",		EDIF_TOK_DERIVATION},
+  {"design",			EDIF_TOK_DESIGN},
+  {"designator",		EDIF_TOK_DESIGNATOR},
+  {"difference",		EDIF_TOK_DIFFERENCE},
+  {"direction",			EDIF_TOK_DIRECTION},
+  {"display",			EDIF_TOK_DISPLAY},
+  {"dominates",			EDIF_TOK_DOMINATES},
+  {"dot",			EDIF_TOK_DOT},
+  {"duration",			EDIF_TOK_DURATION},
+  {"e",				EDIF_TOK_E},
+  {"edif",			EDIF_TOK_EDIF},
+  {"ediflevel",			EDIF_TOK_EDIFLEVEL},
+  {"edifversion",		EDIF_TOK_EDIFVERSION},
+  {"enclosuredistance",		EDIF_TOK_ENCLOSUREDISTANCE},
+  {"endtype",			EDIF_TOK_ENDTYPE},
+  {"entry",			EDIF_TOK_ENTRY},
+  {"exactly",			EDIF_TOK_EXACTLY},
+  {"external",			EDIF_TOK_EXTERNAL},
+  {"fabricate",			EDIF_TOK_FABRICATE},
+  {"false",			EDIF_TOK_FALSE},
+  {"figure",			EDIF_TOK_FIGURE},
+  {"figurearea",		EDIF_TOK_FIGUREAREA},
+  {"figuregroup",		EDIF_TOK_FIGUREGROUP},
+  {"figuregroupobject",		EDIF_TOK_FIGUREGROUPOBJECT},
+  {"figuregroupoverride",	EDIF_TOK_FIGUREGROUPOVERRIDE},
+  {"figuregroupref",		EDIF_TOK_FIGUREGROUPREF},
+  {"figureperimeter",		EDIF_TOK_FIGUREPERIMETER},
+  {"figurewidth",		EDIF_TOK_FIGUREWIDTH},
+  {"fillpattern",		EDIF_TOK_FILLPATTERN},
+  {"follow",			EDIF_TOK_FOLLOW},
+  {"forbiddenevent",		EDIF_TOK_FORBIDDENEVENT},
+  {"globalportref",		EDIF_TOK_GLOBALPORTREF},
+  {"greaterthan",		EDIF_TOK_GREATERTHAN},
+  {"gridmap",			EDIF_TOK_GRIDMAP},
+  {"ignore",			EDIF_TOK_IGNORE},
+  {"includefiguregroup",	EDIF_TOK_INCLUDEFIGUREGROUP},
+  {"initial",			EDIF_TOK_INITIAL},
+  {"instance",			EDIF_TOK_INSTANCE},
+  {"instancebackannotate",	EDIF_TOK_INSTANCEBACKANNOTATE},
+  {"instancegroup",		EDIF_TOK_INSTANCEGROUP},
+  {"instancemap",		EDIF_TOK_INSTANCEMAP},
+  {"instanceref",		EDIF_TOK_INSTANCEREF},
+  {"integer",			EDIF_TOK_INTEGER},
+  {"integerdisplay",		EDIF_TOK_INTEGERDISPLAY},
+  {"interface",			EDIF_TOK_INTERFACE},
+  {"interfiguregroupspacing",	EDIF_TOK_INTERFIGUREGROUPSPACING},
+  {"intersection",		EDIF_TOK_INTERSECTION},
+  {"intrafiguregroupspacing",	EDIF_TOK_INTRAFIGUREGROUPSPACING},
+  {"inverse",			EDIF_TOK_INVERSE},
+  {"isolated",			EDIF_TOK_ISOLATED},
+  {"joined",			EDIF_TOK_JOINED},
+  {"justify",			EDIF_TOK_JUSTIFY},
+  {"keyworddisplay",		EDIF_TOK_KEYWORDDISPLAY},
+  {"keywordlevel",		EDIF_TOK_KEYWORDLEVEL},
+  {"keywordmap",		EDIF_TOK_KEYWORDMAP},
+  {"lessthan",			EDIF_TOK_LESSTHAN},
+  {"library",			EDIF_TOK_LIBRARY},
+  {"libraryref",		EDIF_TOK_LIBRARYREF},
+  {"listofnets",		EDIF_TOK_LISTOFNETS},
+  {"listofports",		EDIF_TOK_LISTOFPORTS},
+  {"loaddelay",			EDIF_TOK_LOADDELAY},
+  {"logicassign",		EDIF_TOK_LOGICASSIGN},
+  {"logicinput",		EDIF_TOK_LOGICINPUT},
+  {"logiclist",			EDIF_TOK_LOGICLIST},
+  {"logicmapinput",		EDIF_TOK_LOGICMAPINPUT},
+  {"logicmapoutput",		EDIF_TOK_LOGICMAPOUTPUT},
+  {"logiconeof",		EDIF_TOK_LOGICONEOF},
+  {"logicoutput",		EDIF_TOK_LOGICOUTPUT},
+  {"logicport",			EDIF_TOK_LOGICPORT},
+  {"logicref",			EDIF_TOK_LOGICREF},
+  {"logicvalue",		EDIF_TOK_LOGICVALUE},
+  {"logicwaveform",		EDIF_TOK_LOGICWAVEFORM},
+  {"maintain",			EDIF_TOK_MAINTAIN},
+  {"match",			EDIF_TOK_MATCH},
+  {"member",			EDIF_TOK_MEMBER},
+  {"minomax",			EDIF_TOK_MINOMAX},
+  {"minomaxdisplay",		EDIF_TOK_MINOMAXDISPLAY},
+  {"mnm",			EDIF_TOK_MNM},
+  {"multiplevalueset",		EDIF_TOK_MULTIPLEVALUESET},
+  {"mustjoin",			EDIF_TOK_MUSTJOIN},
+  {"name",			EDIF_TOK_NAME},
+  {"net",			EDIF_TOK_NET},
+  {"netbackannotate",		EDIF_TOK_NETBACKANNOTATE},
+  {"netbundle",			EDIF_TOK_NETBUNDLE},
+  {"netdelay",			EDIF_TOK_NETDELAY},
+  {"netgroup",			EDIF_TOK_NETGROUP},
+  {"netmap",			EDIF_TOK_NETMAP},
+  {"netref",			EDIF_TOK_NETREF},
+  {"nochange",			EDIF_TOK_NOCHANGE},
+  {"nonpermutable",		EDIF_TOK_NONPERMUTABLE},
+  {"notallowed",		EDIF_TOK_NOTALLOWED},
+  {"notchspacing",		EDIF_TOK_NOTCHSPACING},
+  {"number",			EDIF_TOK_NUMBER},
+  {"numberdefinition",		EDIF_TOK_NUMBERDEFINITION},
+  {"numberdisplay",		EDIF_TOK_NUMBERDISPLAY},
+  {"offpageconnector",		EDIF_TOK_OFFPAGECONNECTOR},
+  {"offsetevent",		EDIF_TOK_OFFSETEVENT},
+  {"openshape",			EDIF_TOK_OPENSHAPE},
+  {"orientation",		EDIF_TOK_ORIENTATION},
+  {"origin",			EDIF_TOK_ORIGIN},
+  {"overhangdistance",		EDIF_TOK_OVERHANGDISTANCE},
+  {"overlapdistance",		EDIF_TOK_OVERLAPDISTANCE},
+  {"oversize",			EDIF_TOK_OVERSIZE},
+  {"owner",			EDIF_TOK_OWNER},
+  {"page",			EDIF_TOK_PAGE},
+  {"pagesize",			EDIF_TOK_PAGESIZE},
+  {"parameter",			EDIF_TOK_PARAMETER},
+  {"parameterassign",		EDIF_TOK_PARAMETERASSIGN},
+  {"parameterdisplay",		EDIF_TOK_PARAMETERDISPLAY},
+  {"path",			EDIF_TOK_PATH},
+  {"pathdelay",			EDIF_TOK_PATHDELAY},
+  {"pathwidth",			EDIF_TOK_PATHWIDTH},
+  {"permutable",		EDIF_TOK_PERMUTABLE},
+  {"physicaldesignrule",	EDIF_TOK_PHYSICALDESIGNRULE},
+  {"plug",			EDIF_TOK_PLUG},
+  {"point",			EDIF_TOK_POINT},
+  {"pointdisplay",		EDIF_TOK_POINTDISPLAY},
+  {"pointlist",			EDIF_TOK_POINTLIST},
+  {"polygon",			EDIF_TOK_POLYGON},
+  {"port",			EDIF_TOK_PORT},
+  {"portbackannotate",		EDIF_TOK_PORTBACKANNOTATE},
+  {"portbundle",		EDIF_TOK_PORTBUNDLE},
+  {"portdelay",			EDIF_TOK_PORTDELAY},
+  {"portgroup",			EDIF_TOK_PORTGROUP},
+  {"portimplementation",	EDIF_TOK_PORTIMPLEMENTATION},
+  {"portinstance",		EDIF_TOK_PORTINSTANCE},
+  {"portlist",			EDIF_TOK_PORTLIST},
+  {"portlistalias",		EDIF_TOK_PORTLISTALIAS},
+  {"portmap",			EDIF_TOK_PORTMAP},
+  {"portref",			EDIF_TOK_PORTREF},
+  {"program",			EDIF_TOK_PROGRAM},
+  {"property",			EDIF_TOK_PROPERTY},
+  {"propertydisplay",		EDIF_TOK_PROPERTYDISPLAY},
+  {"protectionframe",		EDIF_TOK_PROTECTIONFRAME},
+  {"pt",			EDIF_TOK_PT},
+  {"rangevector",		EDIF_TOK_RANGEVECTOR},
+  {"rectangle",			EDIF_TOK_RECTANGLE},
+  {"rectanglesize",		EDIF_TOK_RECTANGLESIZE},
+  {"rename",			EDIF_TOK_RENAME},
+  {"resolves",			EDIF_TOK_RESOLVES},
+  {"scale",			EDIF_TOK_SCALE},
+  {"scalex",			EDIF_TOK_SCALEX},
+  {"scaley",			EDIF_TOK_SCALEY},
+  {"section",			EDIF_TOK_SECTION},
+  {"shape",			EDIF_TOK_SHAPE},
+  {"simulate",			EDIF_TOK_SIMULATE},
+  {"simulationinfo",		EDIF_TOK_SIMULATIONINFO},
+  {"singlevalueset",		EDIF_TOK_SINGLEVALUESET},
+  {"site",			EDIF_TOK_SITE},
+  {"socket",			EDIF_TOK_SOCKET},
+  {"socketset",			EDIF_TOK_SOCKETSET},
+  {"status",			EDIF_TOK_STATUS},
+  {"steady",			EDIF_TOK_STEADY},
+  {"string",			EDIF_TOK_STRING},
+  {"stringdisplay",		EDIF_TOK_STRINGDISPLAY},
+  {"strong",			EDIF_TOK_STRONG},
+  {"symbol",			EDIF_TOK_SYMBOL},
+  {"symmetry",			EDIF_TOK_SYMMETRY},
+  {"table",			EDIF_TOK_TABLE},
+  {"tabledefault",		EDIF_TOK_TABLEDEFAULT},
+  {"technology",		EDIF_TOK_TECHNOLOGY},
+  {"textheight",		EDIF_TOK_TEXTHEIGHT},
+  {"timeinterval",		EDIF_TOK_TIMEINTERVAL},
+  {"timestamp",			EDIF_TOK_TIMESTAMP},
+  {"timing",			EDIF_TOK_TIMING},
+  {"transform",			EDIF_TOK_TRANSFORM},
+  {"transition",		EDIF_TOK_TRANSITION},
+  {"trigger",			EDIF_TOK_TRIGGER},
+  {"true",			EDIF_TOK_TRUE},
+  {"unconstrained",		EDIF_TOK_UNCONSTRAINED},
+  {"undefined",			EDIF_TOK_UNDEFINED},
+  {"union",			EDIF_TOK_UNION},
+  {"unit",			EDIF_TOK_UNIT},
+  {"unused",			EDIF_TOK_UNUSED},
+  {"userdata",			EDIF_TOK_USERDATA},
+  {"version",			EDIF_TOK_VERSION},
+  {"view",			EDIF_TOK_VIEW},
+  {"viewlist",			EDIF_TOK_VIEWLIST},
+  {"viewmap",			EDIF_TOK_VIEWMAP},
+  {"viewref",			EDIF_TOK_VIEWREF},
+  {"viewtype",			EDIF_TOK_VIEWTYPE},
+  {"visible",			EDIF_TOK_VISIBLE},
+  {"voltagemap",		EDIF_TOK_VOLTAGEMAP},
+  {"wavevalue",			EDIF_TOK_WAVEVALUE},
+  {"weak",			EDIF_TOK_WEAK},
+  {"weakjoined",		EDIF_TOK_WEAKJOINED},
+  {"when",			EDIF_TOK_WHEN},
+  {"written",			EDIF_TOK_WRITTEN}
+};
+static int ContextDefSize = sizeof(ContextDef) / sizeof(Context);
+/*
+ *	Context follower tables:
+ *
+ *	  This is pretty ugly, an array is defined for each context
+ *	which has following context levels. Yet another table is used
+ *	to bind these arrays to the originating contexts.
+ *	  Arrays are declared as:
+ *
+ *		static short f_<Context name>[] = { ... };
+ *
+ *	The array entries are the '%token' values for all keywords which
+ *	can be reached from the <Context name> context. Like I said, ugly,
+ *	but it works.
+ *	  A negative entry means that the follow can only occur once within
+ *	the specified context.
+ */
+static short f_NULL[] = {EDIF_TOK_EDIF};
+static short f_Edif[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_EDIFVERSION,
+			 EDIF_TOK_EDIFLEVEL, EDIF_TOK_KEYWORDMAP,
+			 -EDIF_TOK_STATUS, EDIF_TOK_EXTERNAL,
+			 EDIF_TOK_LIBRARY, EDIF_TOK_DESIGN, EDIF_TOK_COMMENT,
+			 EDIF_TOK_USERDATA};
+static short f_AcLoad[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_MINOMAXDISPLAY};
+static short f_After[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_FOLLOW,
+			  EDIF_TOK_MAINTAIN, EDIF_TOK_LOGICASSIGN,
+			  EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Annotate[] = {EDIF_TOK_STRINGDISPLAY};
+static short f_Apply[] = {EDIF_TOK_CYCLE, EDIF_TOK_LOGICINPUT,
+			  EDIF_TOK_LOGICOUTPUT, EDIF_TOK_COMMENT,
+			  EDIF_TOK_USERDATA};
+static short f_Arc[] = {EDIF_TOK_PT};
+static short f_Array[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME};
+static short f_ArrayMacro[] = {EDIF_TOK_PLUG};
+static short f_ArrayRelatedInfo[] = {EDIF_TOK_BASEARRAY, EDIF_TOK_ARRAYSITE,
+				     EDIF_TOK_ARRAYMACRO, EDIF_TOK_COMMENT,
+				     EDIF_TOK_USERDATA};
+static short f_ArraySite[] = {EDIF_TOK_SOCKET};
+static short f_AtLeast[] = {EDIF_TOK_E};
+static short f_AtMost[] = {EDIF_TOK_E};
+static short f_Becomes[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST,
+			    EDIF_TOK_LOGICONEOF};
+/*
+static short f_Between[] = {EDIF_TOK_ATLEAST, EDIF_TOK_GREATERTHAN,
+			    EDIF_TOK_ATMOST, EDIF_TOK_LESSTHAN};
+*/
+static short f_Boolean[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE,
+			    EDIF_TOK_BOOLEANDISPLAY, EDIF_TOK_BOOLEAN};
+static short f_BooleanDisplay[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE,
+				   EDIF_TOK_DISPLAY};
+static short f_BooleanMap[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE};
+static short f_BorderPattern[] = {EDIF_TOK_BOOLEAN};
+static short f_BoundingBox[] = {EDIF_TOK_RECTANGLE};
+static short f_Cell[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_CELLTYPE,
+			 -EDIF_TOK_STATUS, -EDIF_TOK_VIEWMAP, EDIF_TOK_VIEW,
+			 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA,
+			 EDIF_TOK_PROPERTY};
+static short f_CellRef[] = {EDIF_TOK_NAME, EDIF_TOK_LIBRARYREF};
+static short f_Change[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			   EDIF_TOK_BECOMES, EDIF_TOK_TRANSITION};
+static short f_Circle[] = {EDIF_TOK_PT, EDIF_TOK_PROPERTY};
+static short f_Color[] = {EDIF_TOK_E};
+static short f_CommentGraphics[] = {EDIF_TOK_ANNOTATE, EDIF_TOK_FIGURE,
+				    EDIF_TOK_INSTANCE, -EDIF_TOK_BOUNDINGBOX,
+				    EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+				    EDIF_TOK_USERDATA};
+static short f_Compound[] = {EDIF_TOK_NAME};
+static short f_ConnectLocation[] = {EDIF_TOK_FIGURE};
+static short f_Contents[] = {EDIF_TOK_INSTANCE, EDIF_TOK_OFFPAGECONNECTOR,
+			     EDIF_TOK_FIGURE, EDIF_TOK_SECTION, EDIF_TOK_NET,
+			     EDIF_TOK_NETBUNDLE, EDIF_TOK_PAGE,
+			     EDIF_TOK_COMMENTGRAPHICS,
+			     EDIF_TOK_PORTIMPLEMENTATION,
+			     EDIF_TOK_TIMING, EDIF_TOK_SIMULATE,
+			     EDIF_TOK_WHEN, EDIF_TOK_FOLLOW,
+			     EDIF_TOK_LOGICPORT, -EDIF_TOK_BOUNDINGBOX,
+			     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Criticality[] = {EDIF_TOK_INTEGERDISPLAY};
+static short f_CurrentMap[] = {EDIF_TOK_MNM, EDIF_TOK_E};
+static short f_Curve[] = {EDIF_TOK_ARC, EDIF_TOK_PT};
+static short f_Cycle[] = {EDIF_TOK_DURATION};
+static short f_DataOrigin[] = {EDIF_TOK_VERSION};
+static short f_DcFanInLoad[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_DcFanOutLoad[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_DcMaxFanIn[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_DcMaxFanOut[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_Delay[] = {EDIF_TOK_MNM, EDIF_TOK_E};
+static short f_Delta[] = {EDIF_TOK_PT};
+static short f_Design[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_CELLREF,
+			   EDIF_TOK_STATUS, EDIF_TOK_COMMENT,
+			   EDIF_TOK_PROPERTY, EDIF_TOK_USERDATA};
+static short f_Designator[] = {EDIF_TOK_STRINGDISPLAY};
+static short f_Difference[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			       EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			       EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE};
+static short f_Display[] = {EDIF_TOK_NAME, EDIF_TOK_FIGUREGROUPOVERRIDE,
+			    EDIF_TOK_JUSTIFY, EDIF_TOK_ORIENTATION,
+			    EDIF_TOK_ORIGIN};
+static short f_Dominates[] = {EDIF_TOK_NAME};
+static short f_Dot[] = {EDIF_TOK_PT, EDIF_TOK_PROPERTY};
+static short f_Duration[] = {EDIF_TOK_E};
+static short f_EnclosureDistance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				      EDIF_TOK_FIGUREGROUPOBJECT,
+				      EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				      EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				      EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+				      EDIF_TOK_SINGLEVALUESET,
+				      EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Entry[] = {EDIF_TOK_MATCH, EDIF_TOK_CHANGE, EDIF_TOK_STEADY,
+			  EDIF_TOK_LOGICREF, EDIF_TOK_PORTREF,
+			  EDIF_TOK_NOCHANGE, EDIF_TOK_TABLE,
+			  EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_Exactly[] = {EDIF_TOK_E};
+static short f_External[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			     EDIF_TOK_EDIFLEVEL, EDIF_TOK_TECHNOLOGY,
+			     -EDIF_TOK_STATUS, EDIF_TOK_CELL, EDIF_TOK_COMMENT,
+			     EDIF_TOK_USERDATA};
+static short f_Fabricate[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME};
+static short f_Figure[] = {EDIF_TOK_NAME, EDIF_TOK_FIGUREGROUPOVERRIDE,
+			   EDIF_TOK_CIRCLE, EDIF_TOK_DOT, EDIF_TOK_OPENSHAPE,
+			   EDIF_TOK_PATH, EDIF_TOK_POLYGON,
+			   EDIF_TOK_RECTANGLE, EDIF_TOK_SHAPE,
+			   EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_FigureArea[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			       EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+			       EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+			       EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+			       EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+			       EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_FigureGroup[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				-EDIF_TOK_CORNERTYPE, -EDIF_TOK_ENDTYPE,
+				-EDIF_TOK_PATHWIDTH, -EDIF_TOK_BORDERWIDTH,
+				-EDIF_TOK_COLOR, -EDIF_TOK_FILLPATTERN,
+				-EDIF_TOK_BORDERPATTERN, -EDIF_TOK_TEXTHEIGHT,
+				-EDIF_TOK_VISIBLE, EDIF_TOK_INCLUDEFIGUREGROUP,
+				EDIF_TOK_COMMENT, EDIF_TOK_PROPERTY,
+				EDIF_TOK_USERDATA};
+static short f_FigureGroupObject[] = {EDIF_TOK_NAME,
+				      EDIF_TOK_FIGUREGROUPOBJECT,
+				      EDIF_TOK_INTERSECTION, EDIF_TOK_UNION,
+				      EDIF_TOK_DIFFERENCE, EDIF_TOK_INVERSE,
+				      EDIF_TOK_OVERSIZE};
+static short f_FigureGroupOverride[] = {EDIF_TOK_NAME, -EDIF_TOK_CORNERTYPE,
+					-EDIF_TOK_ENDTYPE, -EDIF_TOK_PATHWIDTH,
+					-EDIF_TOK_BORDERWIDTH, -EDIF_TOK_COLOR,
+					-EDIF_TOK_FILLPATTERN,
+					-EDIF_TOK_TEXTHEIGHT,
+					-EDIF_TOK_BORDERPATTERN,
+					EDIF_TOK_VISIBLE, EDIF_TOK_COMMENT,
+					EDIF_TOK_PROPERTY, EDIF_TOK_USERDATA};
+static short f_FigureGroupRef[] = {EDIF_TOK_NAME, EDIF_TOK_LIBRARYREF};
+static short f_FigurePerimeter[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				    EDIF_TOK_FIGUREGROUPOBJECT,
+				    EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				    EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				    EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+				    EDIF_TOK_SINGLEVALUESET, EDIF_TOK_COMMENT,
+				    EDIF_TOK_USERDATA};
+static short f_FigureWidth[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_FillPattern[] = {EDIF_TOK_BOOLEAN};
+static short f_Follow[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_TABLE,
+			   EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_ForbiddenEvent[] = {EDIF_TOK_TIMEINTERVAL, EDIF_TOK_EVENT};
+static short f_GlobalPortRef[] = {EDIF_TOK_NAME};
+static short f_GreaterThan[] = {EDIF_TOK_E};
+static short f_GridMap[] = {EDIF_TOK_E};
+static short f_IncludeFigureGroup[] = {EDIF_TOK_FIGUREGROUPREF,
+				       EDIF_TOK_INTERSECTION, EDIF_TOK_UNION,
+				       EDIF_TOK_DIFFERENCE, EDIF_TOK_INVERSE,
+				       EDIF_TOK_OVERSIZE};
+static short f_Instance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			     EDIF_TOK_VIEWREF, EDIF_TOK_VIEWLIST,
+			     -EDIF_TOK_TRANSFORM, EDIF_TOK_PARAMETERASSIGN,
+			     EDIF_TOK_PORTINSTANCE, EDIF_TOK_TIMING,
+			     -EDIF_TOK_DESIGNATOR, EDIF_TOK_PROPERTY,
+			     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_InstanceBackAnnotate[] = {EDIF_TOK_INSTANCEREF,
+					 -EDIF_TOK_DESIGNATOR, EDIF_TOK_TIMING,
+					 EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT};
+static short f_InstanceGroup[] = {EDIF_TOK_INSTANCEREF};
+static short f_InstanceMap[] = {EDIF_TOK_INSTANCEREF, EDIF_TOK_INSTANCEGROUP,
+				EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_InstanceRef[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				EDIF_TOK_INSTANCEREF, EDIF_TOK_VIEWREF};
+static short f_Integer[] = {EDIF_TOK_INTEGERDISPLAY, EDIF_TOK_INTEGER};
+static short f_IntegerDisplay[] = {EDIF_TOK_DISPLAY};
+static short f_Interface[] = {EDIF_TOK_PORT, EDIF_TOK_PORTBUNDLE,
+			      -EDIF_TOK_SYMBOL, -EDIF_TOK_PROTECTIONFRAME,
+			      -EDIF_TOK_ARRAYRELATEDINFO, EDIF_TOK_PARAMETER,
+			      EDIF_TOK_JOINED, EDIF_TOK_MUSTJOIN,
+			      EDIF_TOK_WEAKJOINED, EDIF_TOK_PERMUTABLE,
+			      EDIF_TOK_TIMING, EDIF_TOK_SIMULATE,
+			      -EDIF_TOK_DESIGNATOR, EDIF_TOK_PROPERTY,
+			      EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_InterFigureGroupSpacing[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+					    EDIF_TOK_FIGUREGROUPOBJECT,
+					    EDIF_TOK_LESSTHAN,
+					    EDIF_TOK_GREATERTHAN,
+					    EDIF_TOK_ATMOST,
+					    EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+					    EDIF_TOK_BETWEEN,
+					    EDIF_TOK_SINGLEVALUESET,
+					    EDIF_TOK_COMMENT,
+					    EDIF_TOK_USERDATA};
+static short f_Intersection[] = {EDIF_TOK_FIGUREGROUPREF,
+				 EDIF_TOK_INTERSECTION, EDIF_TOK_UNION,
+				 EDIF_TOK_DIFFERENCE, EDIF_TOK_INVERSE,
+				 EDIF_TOK_OVERSIZE};
+static short f_IntraFigureGroupSpacing[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+					    EDIF_TOK_FIGUREGROUPOBJECT,
+					    EDIF_TOK_LESSTHAN,
+					    EDIF_TOK_GREATERTHAN,
+					    EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+					    EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+					    EDIF_TOK_SINGLEVALUESET,
+					    EDIF_TOK_COMMENT,
+					    EDIF_TOK_USERDATA};
+static short f_Inverse[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			    EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			    EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE};
+static short f_Joined[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			   EDIF_TOK_GLOBALPORTREF};
+static short f_KeywordDisplay[] = {EDIF_TOK_DISPLAY};
+static short f_KeywordMap[] = {EDIF_TOK_KEYWORDLEVEL, EDIF_TOK_COMMENT};
+static short f_LessThan[] = {EDIF_TOK_E};
+static short f_Library[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_EDIFLEVEL,
+			    EDIF_TOK_TECHNOLOGY, -EDIF_TOK_STATUS,
+			    EDIF_TOK_CELL, EDIF_TOK_COMMENT,
+			    EDIF_TOK_USERDATA};
+static short f_LibraryRef[] = {EDIF_TOK_NAME};
+static short f_ListOfNets[] = {EDIF_TOK_NET};
+static short f_ListOfPorts[] = {EDIF_TOK_PORT, EDIF_TOK_PORTBUNDLE};
+static short f_LoadDelay[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_MINOMAXDISPLAY};
+static short f_LogicAssign[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF,
+				EDIF_TOK_LOGICREF, EDIF_TOK_TABLE,
+				EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_LogicInput[] = {EDIF_TOK_PORTLIST, EDIF_TOK_PORTREF,
+			       EDIF_TOK_NAME, EDIF_TOK_LOGICWAVEFORM};
+static short f_LogicList[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICONEOF,
+			      EDIF_TOK_IGNORE};
+static short f_LogicMapInput[] = {EDIF_TOK_LOGICREF};
+static short f_LogicMapOutput[] = {EDIF_TOK_LOGICREF};
+static short f_LogicOneOf[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST};
+static short f_LogicOutput[] = {EDIF_TOK_PORTLIST, EDIF_TOK_PORTREF,
+				EDIF_TOK_NAME, EDIF_TOK_LOGICWAVEFORM};
+static short f_LogicPort[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			      EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			      EDIF_TOK_USERDATA};
+static short f_LogicRef[] = {EDIF_TOK_NAME, EDIF_TOK_LIBRARYREF};
+static short f_LogicValue[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			       -EDIF_TOK_VOLTAGEMAP, -EDIF_TOK_CURRENTMAP,
+			       -EDIF_TOK_BOOLEANMAP, -EDIF_TOK_COMPOUND,
+			       -EDIF_TOK_WEAK ,-EDIF_TOK_STRONG,
+			       -EDIF_TOK_DOMINATES, -EDIF_TOK_LOGICMAPOUTPUT,
+			       -EDIF_TOK_LOGICMAPINPUT,
+			       -EDIF_TOK_ISOLATED, EDIF_TOK_RESOLVES,
+			       EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			       EDIF_TOK_USERDATA};
+static short f_LogicWaveform[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST,
+				  EDIF_TOK_LOGICONEOF, EDIF_TOK_IGNORE};
+static short f_Maintain[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_DELAY,
+			     EDIF_TOK_LOADDELAY};
+static short f_Match[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			  EDIF_TOK_LOGICLIST, EDIF_TOK_LOGICONEOF};
+static short f_Member[] = {EDIF_TOK_NAME};
+static short f_MiNoMax[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_MINOMAXDISPLAY,
+			    EDIF_TOK_MINOMAX};
+static short f_MiNoMaxDisplay[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_DISPLAY};
+static short f_Mnm[] = {EDIF_TOK_E, EDIF_TOK_UNDEFINED,
+			EDIF_TOK_UNCONSTRAINED};
+static short f_MultipleValueSet[] = {EDIF_TOK_RANGEVECTOR};
+static short f_MustJoin[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			     EDIF_TOK_WEAKJOINED, EDIF_TOK_JOINED};
+static short f_Name[] = {EDIF_TOK_DISPLAY};
+static short f_Net[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, -EDIF_TOK_CRITICALITY,
+			EDIF_TOK_NETDELAY, EDIF_TOK_FIGURE, EDIF_TOK_NET,
+			EDIF_TOK_INSTANCE, EDIF_TOK_COMMENTGRAPHICS,
+			EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			EDIF_TOK_USERDATA, EDIF_TOK_JOINED, EDIF_TOK_ARRAY};
+static short f_NetBackAnnotate[] = {EDIF_TOK_NETREF, EDIF_TOK_NETDELAY,
+				    -EDIF_TOK_CRITICALITY, EDIF_TOK_PROPERTY,
+				    EDIF_TOK_COMMENT};
+static short f_NetBundle[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			      EDIF_TOK_LISTOFNETS, EDIF_TOK_FIGURE,
+			      EDIF_TOK_COMMENTGRAPHICS, EDIF_TOK_PROPERTY,
+			      EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_NetDelay[] = {EDIF_TOK_DERIVATION, EDIF_TOK_DELAY,
+			     EDIF_TOK_TRANSITION, EDIF_TOK_BECOMES};
+static short f_NetGroup[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER, EDIF_TOK_NETREF};
+static short f_NetMap[] = {EDIF_TOK_NETREF, EDIF_TOK_NETGROUP,
+			   EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_NetRef[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER, EDIF_TOK_NETREF,
+			   EDIF_TOK_INSTANCEREF, EDIF_TOK_VIEWREF};
+static short f_NonPermutable[] = {EDIF_TOK_PORTREF, EDIF_TOK_PERMUTABLE};
+static short f_NotAllowed[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			       EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_COMMENT,
+			       EDIF_TOK_USERDATA};
+static short f_NotchSpacing[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				 EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				 EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				 EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				 EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Number[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY, EDIF_TOK_NUMBER};
+static short f_NumberDefinition[] = {EDIF_TOK_SCALE, -EDIF_TOK_GRIDMAP,
+				     EDIF_TOK_COMMENT};
+static short f_NumberDisplay[] = {EDIF_TOK_E, EDIF_TOK_DISPLAY};
+static short f_OffPageConnector[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				     -EDIF_TOK_UNUSED, EDIF_TOK_PROPERTY,
+				     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_OffsetEvent[] = {EDIF_TOK_EVENT, EDIF_TOK_E};
+static short f_OpenShape[] = {EDIF_TOK_CURVE, EDIF_TOK_PROPERTY};
+static short f_Origin[] = {EDIF_TOK_PT};
+static short f_OverhangDistance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				     EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				     EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				     EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				     EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_OverlapDistance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				    EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				    EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				    EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				    EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				    EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Oversize[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			     EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			     EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE,
+			     EDIF_TOK_CORNERTYPE};
+static short f_Page[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			 EDIF_TOK_INSTANCE, EDIF_TOK_NET, EDIF_TOK_NETBUNDLE,
+			 EDIF_TOK_COMMENTGRAPHICS, EDIF_TOK_PORTIMPLEMENTATION,
+			 -EDIF_TOK_PAGESIZE, -EDIF_TOK_BOUNDINGBOX,
+			 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PageSize[] = {EDIF_TOK_RECTANGLE};
+static short f_Parameter[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			      EDIF_TOK_BOOLEAN, EDIF_TOK_INTEGER,
+			      EDIF_TOK_MINOMAX, EDIF_TOK_NUMBER,
+			      EDIF_TOK_POINT, EDIF_TOK_STRING};
+static short f_ParameterAssign[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				    EDIF_TOK_BOOLEAN, EDIF_TOK_INTEGER,
+				    EDIF_TOK_MINOMAX, EDIF_TOK_NUMBER, EDIF_TOK_POINT,
+				    EDIF_TOK_STRING};
+static short f_ParameterDisplay[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				     EDIF_TOK_DISPLAY};
+static short f_Path[] = {EDIF_TOK_POINTLIST, EDIF_TOK_PROPERTY};
+static short f_PathDelay[] = {EDIF_TOK_DELAY, EDIF_TOK_EVENT};
+static short f_Permutable[] = {EDIF_TOK_PORTREF, EDIF_TOK_PERMUTABLE,
+			       EDIF_TOK_NONPERMUTABLE};
+static short f_PhysicalDesignRule[] = {EDIF_TOK_FIGUREWIDTH,
+				       EDIF_TOK_FIGUREAREA,
+				       EDIF_TOK_RECTANGLESIZE,
+				       EDIF_TOK_FIGUREPERIMETER,
+				       EDIF_TOK_OVERLAPDISTANCE,
+				       EDIF_TOK_OVERHANGDISTANCE,
+				       EDIF_TOK_ENCLOSUREDISTANCE,
+				       EDIF_TOK_INTERFIGUREGROUPSPACING,
+				       EDIF_TOK_NOTCHSPACING,
+				       EDIF_TOK_INTRAFIGUREGROUPSPACING,
+				       EDIF_TOK_NOTALLOWED,
+				       EDIF_TOK_FIGUREGROUP, EDIF_TOK_COMMENT,
+				       EDIF_TOK_USERDATA};
+static short f_Plug[] = {EDIF_TOK_SOCKETSET};
+static short f_Point[] = {EDIF_TOK_PT, EDIF_TOK_POINTDISPLAY,
+			  EDIF_TOK_POINT};
+static short f_PointDisplay[] = {EDIF_TOK_PT, EDIF_TOK_DISPLAY};
+static short f_PointList[] = {EDIF_TOK_PT};
+static short f_Polygon[] = {EDIF_TOK_POINTLIST, EDIF_TOK_PROPERTY};
+static short f_Port[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			 -EDIF_TOK_DIRECTION, -EDIF_TOK_UNUSED,
+			 EDIF_TOK_PORTDELAY, -EDIF_TOK_DESIGNATOR,
+			 -EDIF_TOK_DCFANINLOAD, -EDIF_TOK_DCFANOUTLOAD,
+			 -EDIF_TOK_DCMAXFANIN, -EDIF_TOK_DCMAXFANOUT,
+			 -EDIF_TOK_ACLOAD, EDIF_TOK_PROPERTY,
+			 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PortBackAnnotate[] = {EDIF_TOK_PORTREF, -EDIF_TOK_DESIGNATOR,
+				     EDIF_TOK_PORTDELAY, -EDIF_TOK_DCFANINLOAD,
+				     -EDIF_TOK_DCFANOUTLOAD,
+				     -EDIF_TOK_DCMAXFANIN,
+				     -EDIF_TOK_DCMAXFANOUT, -EDIF_TOK_ACLOAD,
+				     EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT};
+static short f_PortBundle[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			       EDIF_TOK_LISTOFPORTS, EDIF_TOK_PROPERTY,
+			       EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PortDelay[] = {EDIF_TOK_DERIVATION, EDIF_TOK_DELAY,
+			      EDIF_TOK_LOADDELAY, EDIF_TOK_TRANSITION,
+			      EDIF_TOK_BECOMES};
+static short f_PortGroup[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+			      EDIF_TOK_PORTREF};
+static short f_PortImplementation[] = {EDIF_TOK_PORTREF, EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				       -EDIF_TOK_CONNECTLOCATION,
+				       EDIF_TOK_FIGURE, EDIF_TOK_INSTANCE,
+				       EDIF_TOK_COMMENTGRAPHICS,
+				       EDIF_TOK_PROPERTYDISPLAY,
+				       EDIF_TOK_KEYWORDDISPLAY,
+				       EDIF_TOK_PROPERTY,
+				       EDIF_TOK_USERDATA, EDIF_TOK_COMMENT};
+static short f_PortInstance[] = {EDIF_TOK_PORTREF, EDIF_TOK_NAME,
+				 EDIF_TOK_MEMBER, -EDIF_TOK_UNUSED,
+				 EDIF_TOK_PORTDELAY, -EDIF_TOK_DESIGNATOR,
+				 -EDIF_TOK_DCFANINLOAD,
+				 -EDIF_TOK_DCFANOUTLOAD, -EDIF_TOK_DCMAXFANIN,
+				 -EDIF_TOK_DCMAXFANOUT, -EDIF_TOK_ACLOAD,
+				 EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+				 EDIF_TOK_USERDATA};
+static short f_PortList[] = {EDIF_TOK_PORTREF, EDIF_TOK_NAME,
+			     EDIF_TOK_MEMBER};
+static short f_PortListAlias[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				  EDIF_TOK_ARRAY, EDIF_TOK_PORTLIST};
+static short f_PortMap[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTGROUP,
+			    EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PortRef[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+			    EDIF_TOK_PORTREF, EDIF_TOK_INSTANCEREF,
+			    EDIF_TOK_VIEWREF};
+static short f_Program[] = {EDIF_TOK_VERSION};
+static short f_Property[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_BOOLEAN,
+			     EDIF_TOK_INTEGER, EDIF_TOK_MINOMAX,
+			     EDIF_TOK_NUMBER, EDIF_TOK_POINT, EDIF_TOK_STRING,
+			     -EDIF_TOK_OWNER, -EDIF_TOK_UNIT,
+			     EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT};
+static short f_PropertyDisplay[] = {EDIF_TOK_NAME, EDIF_TOK_DISPLAY};
+static short f_ProtectionFrame[] = {EDIF_TOK_PORTIMPLEMENTATION,
+				    EDIF_TOK_FIGURE, EDIF_TOK_INSTANCE,
+				    EDIF_TOK_COMMENTGRAPHICS,
+				    -EDIF_TOK_BOUNDINGBOX,
+				    EDIF_TOK_PROPERTYDISPLAY,
+				    EDIF_TOK_KEYWORDDISPLAY,
+				    EDIF_TOK_PARAMETERDISPLAY,
+				    EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+				    EDIF_TOK_USERDATA};
+static short f_RangeVector[] = {EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+				EDIF_TOK_SINGLEVALUESET};
+static short f_Rectangle[] = {EDIF_TOK_PT, EDIF_TOK_PROPERTY};
+static short f_RectangleSize[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				  EDIF_TOK_FIGUREGROUPOBJECT,
+				  EDIF_TOK_RANGEVECTOR,
+				  EDIF_TOK_MULTIPLEVALUESET,EDIF_TOK_COMMENT,
+				  EDIF_TOK_USERDATA};
+static short f_Rename[] = {EDIF_TOK_NAME, EDIF_TOK_STRINGDISPLAY};
+static short f_Resolves[] = {EDIF_TOK_NAME};
+static short f_Scale[] = {EDIF_TOK_E, EDIF_TOK_UNIT};
+static short f_Section[] = {EDIF_TOK_SECTION, EDIF_TOK_INSTANCE};
+static short f_Shape[] = {EDIF_TOK_CURVE, EDIF_TOK_PROPERTY};
+static short f_Simulate[] = {EDIF_TOK_NAME, EDIF_TOK_PORTLISTALIAS,
+			     EDIF_TOK_WAVEVALUE, EDIF_TOK_APPLY,
+			     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_SimulationInfo[] = {EDIF_TOK_LOGICVALUE, EDIF_TOK_COMMENT,
+				   EDIF_TOK_USERDATA};
+static short f_SingleValueSet[] = {EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				   EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				   EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN};
+static short f_Site[] = {EDIF_TOK_VIEWREF, EDIF_TOK_TRANSFORM};
+static short f_Socket[] = {EDIF_TOK_SYMMETRY};
+static short f_SocketSet[] = {EDIF_TOK_SYMMETRY, EDIF_TOK_SITE};
+static short f_Status[] = {EDIF_TOK_WRITTEN, EDIF_TOK_COMMENT,
+			   EDIF_TOK_USERDATA};
+static short f_Steady[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER, EDIF_TOK_PORTREF,
+			   EDIF_TOK_PORTLIST, EDIF_TOK_DURATION,
+			   EDIF_TOK_TRANSITION, EDIF_TOK_BECOMES};
+static short f_String[] = {EDIF_TOK_STRINGDISPLAY, EDIF_TOK_STRING};
+static short f_StringDisplay[] = {EDIF_TOK_DISPLAY};
+static short f_Strong[] = {EDIF_TOK_NAME};
+static short f_Symbol[] = {EDIF_TOK_PORTIMPLEMENTATION, EDIF_TOK_FIGURE,
+			   EDIF_TOK_INSTANCE, EDIF_TOK_COMMENTGRAPHICS,
+			   EDIF_TOK_ANNOTATE, -EDIF_TOK_PAGESIZE,
+			   -EDIF_TOK_BOUNDINGBOX, EDIF_TOK_PROPERTYDISPLAY,
+			   EDIF_TOK_KEYWORDDISPLAY, EDIF_TOK_PARAMETERDISPLAY,
+			   EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			   EDIF_TOK_USERDATA};
+static short f_Symmetry[] = {EDIF_TOK_TRANSFORM};
+static short f_Table[] = {EDIF_TOK_ENTRY, EDIF_TOK_TABLEDEFAULT};
+static short f_TableDefault[] = {EDIF_TOK_LOGICREF, EDIF_TOK_PORTREF,
+				 EDIF_TOK_NOCHANGE, EDIF_TOK_TABLE,
+				 EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_Technology[] = {EDIF_TOK_NUMBERDEFINITION, EDIF_TOK_FIGUREGROUP,
+			       EDIF_TOK_FABRICATE, -EDIF_TOK_SIMULATIONINFO,
+			       EDIF_TOK_COMMENT, EDIF_TOK_USERDATA,
+			       -EDIF_TOK_PHYSICALDESIGNRULE};
+static short f_TimeInterval[] = {EDIF_TOK_EVENT, EDIF_TOK_OFFSETEVENT,
+				 EDIF_TOK_DURATION};
+static short f_Timing[] = {EDIF_TOK_DERIVATION, EDIF_TOK_PATHDELAY,
+			   EDIF_TOK_FORBIDDENEVENT, EDIF_TOK_COMMENT,
+			   EDIF_TOK_USERDATA};
+static short f_Transform[] = {EDIF_TOK_SCALEX, EDIF_TOK_SCALEY, EDIF_TOK_DELTA,
+			      EDIF_TOK_ORIENTATION, EDIF_TOK_ORIGIN};
+static short f_Transition[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST,
+			       EDIF_TOK_LOGICONEOF};
+static short f_Trigger[] = {EDIF_TOK_CHANGE, EDIF_TOK_STEADY,
+			    EDIF_TOK_INITIAL};
+static short f_Union[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			  EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			  EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE};
+static short f_View[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_VIEWTYPE,
+			 EDIF_TOK_INTERFACE, -EDIF_TOK_STATUS,
+			 -EDIF_TOK_CONTENTS, EDIF_TOK_COMMENT,
+			 EDIF_TOK_PROPERTY, EDIF_TOK_USERDATA};
+static short f_ViewList[] = {EDIF_TOK_VIEWREF, EDIF_TOK_VIEWLIST};
+static short f_ViewMap[] = {EDIF_TOK_PORTMAP, EDIF_TOK_PORTBACKANNOTATE,
+			    EDIF_TOK_INSTANCEMAP,
+			    EDIF_TOK_INSTANCEBACKANNOTATE, EDIF_TOK_NETMAP,
+			    EDIF_TOK_NETBACKANNOTATE, EDIF_TOK_COMMENT,
+			    EDIF_TOK_USERDATA};
+static short f_ViewRef[] = {EDIF_TOK_NAME, EDIF_TOK_CELLREF};
+static short f_Visible[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE};
+static short f_VoltageMap[] = {EDIF_TOK_MNM, EDIF_TOK_E};
+static short f_WaveValue[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_E,
+			      EDIF_TOK_LOGICWAVEFORM};
+static short f_Weak[] = {EDIF_TOK_NAME};
+static short f_WeakJoined[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			       EDIF_TOK_JOINED};
+static short f_When[] = {EDIF_TOK_TRIGGER, EDIF_TOK_AFTER,
+			 EDIF_TOK_FOLLOW, EDIF_TOK_MAINTAIN,
+			 EDIF_TOK_LOGICASSIGN, EDIF_TOK_COMMENT,
+			 EDIF_TOK_USERDATA};
+static short f_Written[] = {EDIF_TOK_TIMESTAMP, EDIF_TOK_AUTHOR,
+			    EDIF_TOK_PROGRAM, EDIF_TOK_DATAORIGIN,
+			    EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			    EDIF_TOK_USERDATA};
+/*
+ *	Context binding table:
+ *
+ *	  This binds context follower arrays to their originating context.
+ */
+typedef struct Binder {
+  short *Follower;		/* pointer to follower array */
+  short Origin;			/* '%token' value of origin */
+  short FollowerSize;		/* size of follower array */
+} Binder;
+#define	BE(f,o)			{f,o,sizeof(f)/sizeof(short)}
+static Binder BinderDef[] = {
+  BE(f_NULL,			0),
+  BE(f_Edif,			EDIF_TOK_EDIF),
+  BE(f_AcLoad,			EDIF_TOK_ACLOAD),
+  BE(f_After,			EDIF_TOK_AFTER),
+  BE(f_Annotate,		EDIF_TOK_ANNOTATE),
+  BE(f_Apply,			EDIF_TOK_APPLY),
+  BE(f_Arc,			EDIF_TOK_ARC),
+  BE(f_Array,			EDIF_TOK_ARRAY),
+  BE(f_ArrayMacro,		EDIF_TOK_ARRAYMACRO),
+  BE(f_ArrayRelatedInfo,	EDIF_TOK_ARRAYRELATEDINFO),
+  BE(f_ArraySite,		EDIF_TOK_ARRAYSITE),
+  BE(f_AtLeast,			EDIF_TOK_ATLEAST),
+  BE(f_AtMost,			EDIF_TOK_ATMOST),
+  BE(f_Becomes,			EDIF_TOK_BECOMES),
+  BE(f_Boolean,			EDIF_TOK_BOOLEAN),
+  BE(f_BooleanDisplay,		EDIF_TOK_BOOLEANDISPLAY),
+  BE(f_BooleanMap,		EDIF_TOK_BOOLEANMAP),
+  BE(f_BorderPattern,		EDIF_TOK_BORDERPATTERN),
+  BE(f_BoundingBox,		EDIF_TOK_BOUNDINGBOX),
+  BE(f_Cell,			EDIF_TOK_CELL),
+  BE(f_CellRef,			EDIF_TOK_CELLREF),
+  BE(f_Change,			EDIF_TOK_CHANGE),
+  BE(f_Circle,			EDIF_TOK_CIRCLE),
+  BE(f_Color,			EDIF_TOK_COLOR),
+  BE(f_CommentGraphics,		EDIF_TOK_COMMENTGRAPHICS),
+  BE(f_Compound,		EDIF_TOK_COMPOUND),
+  BE(f_ConnectLocation,		EDIF_TOK_CONNECTLOCATION),
+  BE(f_Contents,		EDIF_TOK_CONTENTS),
+  BE(f_Criticality,		EDIF_TOK_CRITICALITY),
+  BE(f_CurrentMap,		EDIF_TOK_CURRENTMAP),
+  BE(f_Curve,			EDIF_TOK_CURVE),
+  BE(f_Cycle,			EDIF_TOK_CYCLE),
+  BE(f_DataOrigin,		EDIF_TOK_DATAORIGIN),
+  BE(f_DcFanInLoad,		EDIF_TOK_DCFANINLOAD),
+  BE(f_DcFanOutLoad,		EDIF_TOK_DCFANOUTLOAD),
+  BE(f_DcMaxFanIn,		EDIF_TOK_DCMAXFANIN),
+  BE(f_DcMaxFanOut,		EDIF_TOK_DCMAXFANOUT),
+  BE(f_Delay,			EDIF_TOK_DELAY),
+  BE(f_Delta,			EDIF_TOK_DELTA),
+  BE(f_Design,			EDIF_TOK_DESIGN),
+  BE(f_Designator,		EDIF_TOK_DESIGNATOR),
+  BE(f_Difference,		EDIF_TOK_DIFFERENCE),
+  BE(f_Display,			EDIF_TOK_DISPLAY),
+  BE(f_Dominates,		EDIF_TOK_DOMINATES),
+  BE(f_Dot,			EDIF_TOK_DOT),
+  BE(f_Duration,		EDIF_TOK_DURATION),
+  BE(f_EnclosureDistance,	EDIF_TOK_ENCLOSUREDISTANCE),
+  BE(f_Entry,			EDIF_TOK_ENTRY),
+  BE(f_Exactly,			EDIF_TOK_EXACTLY),
+  BE(f_External,		EDIF_TOK_EXTERNAL),
+  BE(f_Fabricate,		EDIF_TOK_FABRICATE),
+  BE(f_Figure,			EDIF_TOK_FIGURE),
+  BE(f_FigureArea,		EDIF_TOK_FIGUREAREA),
+  BE(f_FigureGroup,		EDIF_TOK_FIGUREGROUP),
+  BE(f_FigureGroupObject,	EDIF_TOK_FIGUREGROUPOBJECT),
+  BE(f_FigureGroupOverride,	EDIF_TOK_FIGUREGROUPOVERRIDE),
+  BE(f_FigureGroupRef,		EDIF_TOK_FIGUREGROUPREF),
+  BE(f_FigurePerimeter,		EDIF_TOK_FIGUREPERIMETER),
+  BE(f_FigureWidth,		EDIF_TOK_FIGUREWIDTH),
+  BE(f_FillPattern,		EDIF_TOK_FILLPATTERN),
+  BE(f_Follow,			EDIF_TOK_FOLLOW),
+  BE(f_ForbiddenEvent,		EDIF_TOK_FORBIDDENEVENT),
+  BE(f_GlobalPortRef,		EDIF_TOK_GLOBALPORTREF),
+  BE(f_GreaterThan,		EDIF_TOK_GREATERTHAN),
+  BE(f_GridMap,			EDIF_TOK_GRIDMAP),
+  BE(f_IncludeFigureGroup,	EDIF_TOK_INCLUDEFIGUREGROUP),
+  BE(f_Instance,		EDIF_TOK_INSTANCE),
+  BE(f_InstanceBackAnnotate,	EDIF_TOK_INSTANCEBACKANNOTATE),
+  BE(f_InstanceGroup,		EDIF_TOK_INSTANCEGROUP),
+  BE(f_InstanceMap,		EDIF_TOK_INSTANCEMAP),
+  BE(f_InstanceRef,		EDIF_TOK_INSTANCEREF),
+  BE(f_Integer,			EDIF_TOK_INTEGER),
+  BE(f_IntegerDisplay,		EDIF_TOK_INTEGERDISPLAY),
+  BE(f_InterFigureGroupSpacing,	EDIF_TOK_INTERFIGUREGROUPSPACING),
+  BE(f_Interface,		EDIF_TOK_INTERFACE),
+  BE(f_Intersection,		EDIF_TOK_INTERSECTION),
+  BE(f_IntraFigureGroupSpacing,	EDIF_TOK_INTRAFIGUREGROUPSPACING),
+  BE(f_Inverse,			EDIF_TOK_INVERSE),
+  BE(f_Joined,			EDIF_TOK_JOINED),
+  BE(f_KeywordDisplay,		EDIF_TOK_KEYWORDDISPLAY),
+  BE(f_KeywordMap,		EDIF_TOK_KEYWORDMAP),
+  BE(f_LessThan,		EDIF_TOK_LESSTHAN),
+  BE(f_Library,			EDIF_TOK_LIBRARY),
+  BE(f_LibraryRef,		EDIF_TOK_LIBRARYREF),
+  BE(f_ListOfNets,		EDIF_TOK_LISTOFNETS),
+  BE(f_ListOfPorts,		EDIF_TOK_LISTOFPORTS),
+  BE(f_LoadDelay,		EDIF_TOK_LOADDELAY),
+  BE(f_LogicAssign,		EDIF_TOK_LOGICASSIGN),
+  BE(f_LogicInput,		EDIF_TOK_LOGICINPUT),
+  BE(f_LogicList,		EDIF_TOK_LOGICLIST),
+  BE(f_LogicMapInput,		EDIF_TOK_LOGICMAPINPUT),
+  BE(f_LogicMapOutput,		EDIF_TOK_LOGICMAPOUTPUT),
+  BE(f_LogicOneOf,		EDIF_TOK_LOGICONEOF),
+  BE(f_LogicOutput,		EDIF_TOK_LOGICOUTPUT),
+  BE(f_LogicPort,		EDIF_TOK_LOGICPORT),
+  BE(f_LogicRef,		EDIF_TOK_LOGICREF),
+  BE(f_LogicValue,		EDIF_TOK_LOGICVALUE),
+  BE(f_LogicWaveform,		EDIF_TOK_LOGICWAVEFORM),
+  BE(f_Maintain,		EDIF_TOK_MAINTAIN),
+  BE(f_Match,			EDIF_TOK_MATCH),
+  BE(f_Member,			EDIF_TOK_MEMBER),
+  BE(f_MiNoMax,			EDIF_TOK_MINOMAX),
+  BE(f_MiNoMaxDisplay,		EDIF_TOK_MINOMAXDISPLAY),
+  BE(f_Mnm,			EDIF_TOK_MNM),
+  BE(f_MultipleValueSet,	EDIF_TOK_MULTIPLEVALUESET),
+  BE(f_MustJoin,		EDIF_TOK_MUSTJOIN),
+  BE(f_Name,			EDIF_TOK_NAME),
+  BE(f_Net,			EDIF_TOK_NET),
+  BE(f_NetBackAnnotate,		EDIF_TOK_NETBACKANNOTATE),
+  BE(f_NetBundle,		EDIF_TOK_NETBUNDLE),
+  BE(f_NetDelay,		EDIF_TOK_NETDELAY),
+  BE(f_NetGroup,		EDIF_TOK_NETGROUP),
+  BE(f_NetMap,			EDIF_TOK_NETMAP),
+  BE(f_NetRef,			EDIF_TOK_NETREF),
+  BE(f_NonPermutable,		EDIF_TOK_NONPERMUTABLE),
+  BE(f_NotAllowed,		EDIF_TOK_NOTALLOWED),
+  BE(f_NotchSpacing,		EDIF_TOK_NOTCHSPACING),
+  BE(f_Number,			EDIF_TOK_NUMBER),
+  BE(f_NumberDefinition,	EDIF_TOK_NUMBERDEFINITION),
+  BE(f_NumberDisplay,		EDIF_TOK_NUMBERDISPLAY),
+  BE(f_OffPageConnector,	EDIF_TOK_OFFPAGECONNECTOR),
+  BE(f_OffsetEvent,		EDIF_TOK_OFFSETEVENT),
+  BE(f_OpenShape,		EDIF_TOK_OPENSHAPE),
+  BE(f_Origin,			EDIF_TOK_ORIGIN),
+  BE(f_OverhangDistance,	EDIF_TOK_OVERHANGDISTANCE),
+  BE(f_OverlapDistance,		EDIF_TOK_OVERLAPDISTANCE),
+  BE(f_Oversize,		EDIF_TOK_OVERSIZE),
+  BE(f_Page,			EDIF_TOK_PAGE),
+  BE(f_PageSize,		EDIF_TOK_PAGESIZE),
+  BE(f_Parameter,		EDIF_TOK_PARAMETER),
+  BE(f_ParameterAssign,		EDIF_TOK_PARAMETERASSIGN),
+  BE(f_ParameterDisplay,	EDIF_TOK_PARAMETERDISPLAY),
+  BE(f_Path,			EDIF_TOK_PATH),
+  BE(f_PathDelay,		EDIF_TOK_PATHDELAY),
+  BE(f_Permutable,		EDIF_TOK_PERMUTABLE),
+  BE(f_PhysicalDesignRule,	EDIF_TOK_PHYSICALDESIGNRULE),
+  BE(f_Plug,			EDIF_TOK_PLUG),
+  BE(f_Point,			EDIF_TOK_POINT),
+  BE(f_PointDisplay,		EDIF_TOK_POINTDISPLAY),
+  BE(f_PointList,		EDIF_TOK_POINTLIST),
+  BE(f_Polygon,			EDIF_TOK_POLYGON),
+  BE(f_Port,			EDIF_TOK_PORT),
+  BE(f_PortBackAnnotate,	EDIF_TOK_PORTBACKANNOTATE),
+  BE(f_PortBundle,		EDIF_TOK_PORTBUNDLE),
+  BE(f_PortDelay,		EDIF_TOK_PORTDELAY),
+  BE(f_PortGroup,		EDIF_TOK_PORTGROUP),
+  BE(f_PortImplementation,	EDIF_TOK_PORTIMPLEMENTATION),
+  BE(f_PortInstance,		EDIF_TOK_PORTINSTANCE),
+  BE(f_PortList,		EDIF_TOK_PORTLIST),
+  BE(f_PortListAlias,		EDIF_TOK_PORTLISTALIAS),
+  BE(f_PortMap,			EDIF_TOK_PORTMAP),
+  BE(f_PortRef,			EDIF_TOK_PORTREF),
+  BE(f_Program,			EDIF_TOK_PROGRAM),
+  BE(f_Property,		EDIF_TOK_PROPERTY),
+  BE(f_PropertyDisplay,		EDIF_TOK_PROPERTYDISPLAY),
+  BE(f_ProtectionFrame,		EDIF_TOK_PROTECTIONFRAME),
+  BE(f_RangeVector,		EDIF_TOK_RANGEVECTOR),
+  BE(f_Rectangle,		EDIF_TOK_RECTANGLE),
+  BE(f_RectangleSize,		EDIF_TOK_RECTANGLESIZE),
+  BE(f_Rename,			EDIF_TOK_RENAME),
+  BE(f_Resolves,		EDIF_TOK_RESOLVES),
+  BE(f_Scale,			EDIF_TOK_SCALE),
+  BE(f_Section,			EDIF_TOK_SECTION),
+  BE(f_Shape,			EDIF_TOK_SHAPE),
+  BE(f_Simulate,		EDIF_TOK_SIMULATE),
+  BE(f_SimulationInfo,		EDIF_TOK_SIMULATIONINFO),
+  BE(f_SingleValueSet,		EDIF_TOK_SINGLEVALUESET),
+  BE(f_Site,			EDIF_TOK_SITE),
+  BE(f_Socket,			EDIF_TOK_SOCKET),
+  BE(f_SocketSet,		EDIF_TOK_SOCKETSET),
+  BE(f_Status,			EDIF_TOK_STATUS),
+  BE(f_Steady,			EDIF_TOK_STEADY),
+  BE(f_String,			EDIF_TOK_STRING),
+  BE(f_StringDisplay,		EDIF_TOK_STRINGDISPLAY),
+  BE(f_Strong,			EDIF_TOK_STRONG),
+  BE(f_Symbol,			EDIF_TOK_SYMBOL),
+  BE(f_Symmetry,		EDIF_TOK_SYMMETRY),
+  BE(f_Table,			EDIF_TOK_TABLE),
+  BE(f_TableDefault,		EDIF_TOK_TABLEDEFAULT),
+  BE(f_Technology,		EDIF_TOK_TECHNOLOGY),
+  BE(f_TimeInterval,		EDIF_TOK_TIMEINTERVAL),
+  BE(f_Timing,			EDIF_TOK_TIMING),
+  BE(f_Transform,		EDIF_TOK_TRANSFORM),
+  BE(f_Transition,		EDIF_TOK_TRANSITION),
+  BE(f_Trigger,			EDIF_TOK_TRIGGER),
+  BE(f_Union,			EDIF_TOK_UNION),
+  BE(f_View,			EDIF_TOK_VIEW),
+  BE(f_ViewList,		EDIF_TOK_VIEWLIST),
+  BE(f_ViewMap,			EDIF_TOK_VIEWMAP),
+  BE(f_ViewRef,			EDIF_TOK_VIEWREF),
+  BE(f_Visible,			EDIF_TOK_VISIBLE),
+  BE(f_VoltageMap,		EDIF_TOK_VOLTAGEMAP),
+  BE(f_WaveValue,		EDIF_TOK_WAVEVALUE),
+  BE(f_Weak,			EDIF_TOK_WEAK),
+  BE(f_WeakJoined,		EDIF_TOK_WEAKJOINED),
+  BE(f_When,			EDIF_TOK_WHEN),
+  BE(f_Written,			EDIF_TOK_WRITTEN)
+};
+static int BinderDefSize = sizeof(BinderDef) / sizeof(Binder);
+/*
+ *	Keyword table:
+ *
+ *	  This hash table holds all strings which may have to be matched
+ *	to. WARNING: it is assumed that there is no overlap of the 'token'
+ *	and 'context' strings.
+ */
+typedef struct Keyword {
+  struct Keyword *Next;	 	/* pointer to next entry */
+  const char *String;			/* pointer to associated string */
+} Keyword;
+#define	KEYWORD_HASH	127	/* hash table size */
+static Keyword *KeywordTable[KEYWORD_HASH];
+/*
+ *	Enter keyword:
+ *
+ *	  The passed string is entered into the keyword hash table.
+ */
+static void EnterKeyword(const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register Keyword *key;
+  register unsigned int hsh;
+  register const char *cp;
+  /*
+   *	Create the hash code, and add an entry to the table.
+   */
+  for (hsh = 0, cp = str; *cp; hsh += hsh + *cp++);
+  hsh %= KEYWORD_HASH;
+  key = (Keyword *) Malloc(sizeof(Keyword));
+  key->Next = KeywordTable[hsh];
+  (KeywordTable[hsh] = key)->String = str;
+}
+/*
+ *	Find keyword:
+ *
+ *	  The passed string is located within the keyword table. If an
+ *	entry exists, then the value of the keyword string is returned. This
+ *	is real useful for doing string comparisons by pointer value later.
+ *	If there is no match, a NULL is returned.
+ */
+static const char *FindKeyword(const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register Keyword *wlk,*owk;
+  register unsigned int hsh;
+  register char *cp;
+  char lower[IDENT_LENGTH + 1];
+  /*
+   *	Create a lower case copy of the string.
+   */
+  for (cp = lower; *str;)
+    if (isupper( (int) *str))
+      *cp++ = tolower( (int) *str++);
+    else
+      *cp++ = *str++;
+  *cp = '\0';
+  /*
+   *	Search the hash table for a match.
+   */
+  for (hsh = 0, cp = lower; *cp; hsh += hsh + *cp++);
+  hsh %= KEYWORD_HASH;
+  for (owk = NULL, wlk = KeywordTable[hsh]; wlk; wlk = (owk = wlk)->Next)
+    if (!strcmp(wlk->String,lower)){
+      /*
+       *	Readjust the LRU.
+       */
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = KeywordTable[hsh];
+      	KeywordTable[hsh] = wlk;
+      }
+      return (wlk->String);
+    }
+  return (NULL);
+}
+/*
+ *	Token hash table.
+ */
+#define	TOKEN_HASH	51
+static Token *TokenHash[TOKEN_HASH];
+/*
+ *	Find token:
+ *
+ *	  A pointer to the token of the passed code is returned. If
+ *	no such beastie is present a NULL is returned instead.
+ */
+static Token *FindToken(register int cod)
+{
+  /*
+   *	Locals.
+   */
+  register Token *wlk,*owk;
+  register unsigned int hsh;
+  /*
+   *	Search the hash table for a matching token.
+   */
+  hsh = cod % TOKEN_HASH;
+  for (owk = NULL, wlk = TokenHash[hsh]; wlk; wlk = (owk = wlk)->Next)
+    if (cod == wlk->Code){
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = TokenHash[hsh];
+      	TokenHash[hsh] = wlk;
+      }
+      break;
+    }
+  return (wlk);
+}
+/*
+ *	Context hash table.
+ */
+#define	CONTEXT_HASH	127
+static Context *ContextHash[CONTEXT_HASH];
+/*
+ *	Find context:
+ *
+ *	  A pointer to the context of the passed code is returned. If
+ *	no such beastie is present a NULL is returned instead.
+ */
+static Context *FindContext(register int cod)
+{
+  /*
+   *	Locals.
+   */
+  register Context *wlk,*owk;
+  register unsigned int hsh;
+  /*
+   *	Search the hash table for a matching context.
+   */
+  hsh = cod % CONTEXT_HASH;
+  for (owk = NULL, wlk = ContextHash[hsh]; wlk; wlk = (owk = wlk)->Next)
+    if (cod == wlk->Code){
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = ContextHash[hsh];
+      	ContextHash[hsh] = wlk;
+      }
+      break;
+    }
+  return (wlk);
+}
+/*
+ *	Token stacking variables.
+ */
+#ifdef	DEBUG
+#define	TS_DEPTH	8
+#define	TS_MASK		(TS_DEPTH - 1)
+static unsigned int TSP = 0;		/* token stack pointer */
+static char *TokenStack[TS_DEPTH];	/* token name strings */
+static short TokenType[TS_DEPTH];	/* token types */
+/*
+ *	Stack:
+ *
+ *	  Add a token to the debug stack. The passed string and type are
+ *	what is to be pushed.
+ */
+static void Stack(char * str, int typ)
+{
+  /*
+   *	Free any previous string, then push.
+   */
+  if (TokenStack[TSP & TS_MASK])
+    Free(TokenStack[TSP & TS_MASK]);
+  TokenStack[TSP & TS_MASK] = strcpy((char *)Malloc(strlen(str) + 1),str);
+  TokenType[TSP & TS_MASK] = typ;
+  TSP += 1;
+}
+/*
+ *	Dump stack:
+ *
+ *	  This displays the last set of accumulated tokens.
+ */
+static void DumpStack()
+{
+  /*
+   *	Locals.
+   */
+  register int i;
+  register Context *cxt;
+  register Token *tok;
+  register char *nam;
+  FILE *Error = stderr;
+  /*
+   *	Run through the list displaying the oldest first.
+   */
+  fprintf(Error,"\n\n");
+  for (i = 0; i < TS_DEPTH; i += 1)
+    if (TokenStack[(TSP + i) & TS_MASK]){
+      /*
+       *	Get the type name string.
+       */
+      if ((cxt = FindContext(TokenType[(TSP + i) & TS_MASK])) != NULL)
+        nam = cxt->Name;
+      else if ((tok = FindToken(TokenType[(TSP + i) & TS_MASK])) != NULL)
+        nam = tok->Name;
+      else switch (TokenType[(TSP + i) & TS_MASK]){
+      	case EDIF_TOK_IDENT:	nam = "IDENT";		break;
+      	case EDIF_TOK_INT:	nam = "INT";		break;
+      	case EDIF_TOK_KEYWORD:	nam = "KEYWORD";	break;
+      	case EDIF_TOK_STR:	nam = "STR";		break;
+      	default:	nam = "?";		break;
+      }
+      /*
+       *	Now print the token state.
+       */
+      fprintf(Error,"%2d %-16.16s '%s'\n",TS_DEPTH - i,nam,
+        TokenStack[(TSP + i) & TS_MASK]);
+    }
+  fprintf(Error,"\n");
+}
+#else
+#define	Stack(s,t)
+#endif	/* DEBUG */
+/*
+ *	Parser state variables.
+ */
+static FILE *Input = NULL;		/* input stream */
+static FILE *Error = NULL;		/* error stream */
+static char *InFile;			/* file name on the input stream */
+static long LineNumber;			/* current input line number */
+static ContextCar *CSP = NULL;		/* top of context stack */
+static char yytext[IDENT_LENGTH + 1];	/* token buffer */
+static char CharBuf[IDENT_LENGTH + 1];	/* garbage buffer */
+/*
+ *	yyerror:
+ *
+ *	  Standard error reporter, it prints out the passed string
+ *	preceeded by the current filename and line number.
+ */
+static void yyerror(const char *ers)
+{
+#ifdef	DEBUG
+  DumpStack();
+#endif	/* DEBUG */
+  fprintf(Error,"%s, line %ld: %s\n",InFile,LineNumber,ers);
+}
+/*
+ *	String bucket definitions.
+ */
+#define	BUCKET_SIZE	64
+typedef struct Bucket {
+  struct Bucket *Next;			/* pointer to next bucket */
+  int Index;				/* pointer to next free slot */
+  char Data[BUCKET_SIZE];		/* string data */
+} Bucket;
+static Bucket *CurrentBucket = NULL;	/* string bucket list */
+static int StringSize = 0;		/* current string length */
+/*
+ *	Push string:
+ *
+ *	  This adds the passed charater to the current string bucket.
+ */
+static void PushString(char chr)
+{
+  /*
+   *	Locals.
+   */
+  register Bucket *bck;
+  /*
+   *	Make sure there is room for the push.
+   */
+  if ((bck = CurrentBucket)->Index >= BUCKET_SIZE){
+    bck = (Bucket *) Malloc(sizeof(Bucket));
+    bck->Next = CurrentBucket;
+    (CurrentBucket = bck)->Index = 0;
+  }
+  /*
+   *	Push the character.
+   */
+  bck->Data[bck->Index++] = chr;
+  StringSize += 1;
+}
+/*
+ *	Form string:
+ *
+ *	  This converts the current string bucket into a real live string,
+ *	whose pointer is returned.
+ */
+static char *FormString()
+{
+  /*
+   *	Locals.
+   */
+  register Bucket *bck;
+  register char *cp;
+  /*
+   *	Allocate space for the string, set the pointer at the end.
+   */
+  cp = (char *) Malloc(StringSize + 1);
+
+  cp += StringSize;
+  *cp-- = '\0';
+  /*
+   *	Yank characters out of the bucket.
+   */
+  for (bck = CurrentBucket; bck->Index || bck->Next;){
+    if (!bck->Index){
+      CurrentBucket = bck->Next;
+      Free(bck);
+      bck = CurrentBucket;
+    }
+    *cp-- = bck->Data[--bck->Index];
+  }
+  /* reset buffer size  to zero */
+  StringSize =0;
+  return (cp + 1);
+}
+/*
+ *	Parse EDIF:
+ *
+ *	  This builds the context tree and then calls the real parser.
+ *	It is passed two file streams, the first is where the input comes
+ *	from; the second is where error messages get printed.
+ */
+void ParseEDIF(char* filename,FILE* err)
+{
+  /*
+   *	Locals.
+   */
+  register int i;
+  static int ContextDefined = 1;
+  /*
+   *	Set up the file state to something useful.
+   */
+  InFile = filename;
+  Input = fopen(filename,"r");
+  Error = err;
+  LineNumber = 1;
+  /*
+   *	Define both the enabled token and context strings.
+   */
+  if (ContextDefined){
+    for (i = TokenDefSize; i--; EnterKeyword(TokenDef[i].Name)){
+      register unsigned int hsh;
+      hsh = TokenDef[i].Code % TOKEN_HASH;
+      TokenDef[i].Next = TokenHash[hsh];
+      TokenHash[hsh] = &TokenDef[i];
+    }
+    for (i = ContextDefSize; i--; EnterKeyword(ContextDef[i].Name)){
+      register unsigned int hsh;
+      hsh = ContextDef[i].Code % CONTEXT_HASH;
+      ContextDef[i].Next = ContextHash[hsh];
+      ContextHash[hsh] = &ContextDef[i];
+    }
+    /*
+     *	Build the context tree.
+     */
+    for (i = BinderDefSize; i--;){
+      register Context *cxt;
+      register int j;
+      /*
+       *	Define the current context to have carriers bound to it.
+       */
+      cxt = FindContext(BinderDef[i].Origin);
+      for (j = BinderDef[i].FollowerSize; j--;){
+        register ContextCar *cc;
+        /*
+         *	Add carriers to the current context.
+         */
+        cc = (ContextCar *) Malloc(sizeof(ContextCar));
+        cc->Next = cxt->Context;
+        (cxt->Context = cc)->Context =
+          FindContext(PCB_ABS(BinderDef[i].Follower[j]));
+        cc->u.Single = BinderDef[i].Follower[j] < 0;
+      }
+    }
+    /*
+     *	Build the token tree.
+     */
+    for (i = TieDefSize; i--;){
+      register Context *cxt;
+      register int j;
+      /*
+       *	Define the current context to have carriers bound to it.
+       */
+      cxt = FindContext(TieDef[i].Origin);
+      for (j = TieDef[i].EnableSize; j--;){
+        register TokenCar *tc;
+        /*
+         *	Add carriers to the current context.
+         */
+        tc = (TokenCar *) Malloc(sizeof(TokenCar));
+        tc->Next = cxt->Token;
+        (cxt->Token = tc)->Token = FindToken(TieDef[i].Enable[j]);
+      }
+    }
+    /*
+     *	Put a bogus context on the stack which has 'EDIF' as its
+     *	follower.
+     */
+    CSP = (ContextCar *) Malloc(sizeof(ContextCar));
+    CSP->Next = NULL;
+    CSP->Context = FindContext(0);
+    CSP->u.Used = NULL;
+    ContextDefined = 0;
+  }
+  /*
+   *	Create an initial, empty string bucket.
+   */
+  CurrentBucket = (Bucket *) Malloc(sizeof(Bucket));
+  CurrentBucket->Next = 0;
+  CurrentBucket->Index = 0;
+  /*
+   *	Fill the token stack with NULLs if debugging is enabled.
+   */
+#ifdef	DEBUG
+  for (i = TS_DEPTH; i--; TokenStack[i] = NULL)
+    if (TokenStack[i])
+      Free(TokenStack[i]);
+  TSP = 0;
+#endif	/* DEBUG */
+  /*
+   *	Go parse things!
+   */
+  edifparse();
+}
+/*
+ *	Match token:
+ *
+ *	  The passed string is looked up in the current context's token
+ *	list to see if it is enabled. If so the token value is returned,
+ *	if not then zero.
+ */
+static int MatchToken(register const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register TokenCar *wlk,*owk;
+  /*
+   *	Convert the string to the proper form, then search the token
+   *	carrier list for a match.
+   */
+  str = FindKeyword(str);
+  for (owk = NULL, wlk = CSP->Context->Token; wlk; wlk = (owk = wlk)->Next)
+    if (str == wlk->Token->Name){
+      if (owk){
+        owk->Next = wlk->Next;
+        wlk->Next = CSP->Context->Token;
+        CSP->Context->Token = wlk;
+      }
+      return (wlk->Token->Code);
+    }
+  return (0);
+}
+/*
+ *	Match context:
+ *
+ *	  If the passed keyword string is within the current context, the
+ *	new context is pushed and token value is returned. A zero otherwise.
+ */
+static int MatchContext(register const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register ContextCar *wlk,*owk;
+  /*
+   *	See if the context is present.
+   */
+  str = FindKeyword(str);
+  for (owk = NULL, wlk = CSP->Context->Context; wlk; wlk = (owk = wlk)->Next)
+    if (str == wlk->Context->Name){
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = CSP->Context->Context;
+      	CSP->Context->Context = wlk;
+      }
+      /*
+       *	If a single context, make sure it isn't already used.
+       */
+      if (wlk->u.Single){
+      	register UsedCar *usc;
+      	for (usc = CSP->u.Used; usc; usc = usc->Next)
+      	  if (usc->Code == wlk->Context->Code)
+      	    break;
+      	if (usc){
+      	  sprintf(CharBuf,"'%s' is used more than once within '%s'",
+      	    str,CSP->Context->Name);
+      	  yyerror(CharBuf);
+      	} else {
+      	  usc = (UsedCar *) Malloc(sizeof(UsedCar));
+      	  usc->Next = CSP->u.Used;
+      	  (CSP->u.Used = usc)->Code = wlk->Context->Code;
+      	}
+      }
+      /*
+       *	Push the new context.
+       */
+      owk = (ContextCar *) Malloc(sizeof(ContextCar));
+      owk->Next = CSP;
+      (CSP = owk)->Context = wlk->Context;
+      owk->u.Used = NULL;
+      return (wlk->Context->Code);
+    }
+  return (0);
+}
+/*
+ *	PopC:
+ *
+ *	  This pops the current context.
+ */
+static void PopC()
+{
+  /*
+   *	Locals.
+   */
+  register UsedCar *usc;
+  register ContextCar *csp;
+  /*
+   *	Release single markers and pop context.
+   */
+  while ( (usc = CSP->u.Used) ){
+    CSP->u.Used = usc->Next;
+    Free(usc);
+  }
+  csp = CSP->Next;
+  Free(CSP);
+  CSP = csp;
+}
+/*
+ *	Lexical analyzer states.
+ */
+#define	L_START		0
+#define	L_INT		1
+#define	L_IDENT		2
+#define	L_KEYWORD	3
+#define	L_STRING	4
+#define	L_KEYWORD2	5
+#define	L_ASCIICHAR	6
+#define	L_ASCIICHAR2	7
+/*
+ *	yylex:
+ *
+ *	  This is the lexical analyzer called by the YACC/BISON parser.
+ *	It returns a pretty restricted set of token types and does the
+ *	context movement when acceptable keywords are found. The token
+ *	value returned is a NULL terminated string to allocated storage
+ *	(ie - it should get released some time) with some restrictions.
+ *	  The token value for integers is strips a leading '+' if present.
+ *	String token values have the leading and trailing '"'-s stripped.
+ *	'%' conversion characters in string values are passed converted.
+ *	The '(' and ')' characters do not have a token value.
+ */
+static int yylex()
+{
+  /*
+   *	Locals.
+   */
+  register int c,s,l;
+  /*
+   *	Keep on sucking up characters until we find something which
+   *	explicitly forces us out of this function.
+   */
+  for (s = L_START, l = 0; 1;){
+    yytext[l++] = c = Getc(Input);
+    switch (s){
+      /*
+       *	Starting state, look for something resembling a token.
+       */
+      case L_START:
+        if (isdigit(c) || c == '-')
+          s = L_INT;
+        else if (isalpha(c) || c == '&')
+          s = L_IDENT;
+        else if (isspace(c)){
+          if (c == '\n')
+            LineNumber += 1;
+          l = 0;
+        } else if (c == '('){
+          l = 0;
+          s = L_KEYWORD;
+        } else if (c == '"')
+          s = L_STRING;
+        else if (c == '+'){
+          l = 0;				/* strip '+' */
+          s = L_INT;
+        } else if (c == EOF)
+          return ('\0');
+        else {
+          yytext[1] = '\0';
+          Stack(yytext,c);
+          return (c);
+        }
+        break;
+      /*
+       *	Suck up the integer digits.
+       */
+      case L_INT:
+        if (isdigit(c))
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        yylval.s = strcpy((char *)Malloc(l + 1),yytext);
+        Stack(yytext,EDIF_TOK_INT);
+        return (EDIF_TOK_INT);
+      /*
+       *	Grab an identifier, see if the current context enables
+       *	it with a specific token value.
+       */
+      case L_IDENT:
+        if (isalpha(c) || isdigit(c) || c == '_')
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        if (CSP->Context->Token && (c = MatchToken(yytext))){
+          Stack(yytext,c);
+          return (c);
+        }
+        yylval.s = strcpy((char *)Malloc(l + 1),yytext);
+        Stack(yytext, EDIF_TOK_IDENT);
+        return (EDIF_TOK_IDENT);
+      /*
+       *	Scan until you find the start of an identifier, discard
+       *	any whitespace found. On no identifier, return a '('.
+       */
+      case L_KEYWORD:
+        if (isalpha(c) || c == '&'){
+          s = L_KEYWORD2;
+          break;
+        } else if (isspace(c)){
+          l = 0;
+          break;
+        }
+        Ungetc(c);
+        Stack("(",'(');
+        return ('(');
+      /*
+       *	Suck up the keyword identifier, if it matches the set of
+       *	allowable contexts then return its token value and push
+       *	the context, otherwise just return the identifier string.
+       */
+      case L_KEYWORD2:
+        if (isalpha(c) || isdigit(c) || c == '_')
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        if ( (c = MatchContext(yytext)) ){
+          Stack(yytext,c);
+          return (c);
+        }
+        yylval.s = strcpy((char *)Malloc(l + 1),yytext);
+        Stack(yytext, EDIF_TOK_KEYWORD);
+        return (EDIF_TOK_KEYWORD);
+      /*
+       *	Suck up string characters but once resolved they should
+       *	be deposited in the string bucket because they can be
+       *	arbitrarily long.
+       */
+      case L_STRING:
+        if (c == '\n')
+          LineNumber += 1;
+        else if (c == '\r')
+          ;
+        else if (c == '"' || c == EOF){
+          yylval.s = FormString();
+          Stack(yylval.s, EDIF_TOK_STR);
+          return (EDIF_TOK_STR);
+        } else if (c == '%')
+          s = L_ASCIICHAR;
+        else
+          PushString(c);
+        l = 0;
+        break;
+      /*
+       *	Skip white space and look for integers to be pushed
+       *	as characters.
+       */
+      case L_ASCIICHAR:
+        if (isdigit(c)){
+          s = L_ASCIICHAR2;
+          break;
+        } else if (c == '%' || c == EOF)
+          s = L_STRING;
+        else if (c == '\n')
+          LineNumber += 1;
+        l = 0;
+        break;
+      /*
+       *	Convert the accumulated integer into a char and push.
+       */
+      case L_ASCIICHAR2:
+        if (isdigit(c))
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        PushString(atoi(yytext));
+        s = L_ASCIICHAR;
+        l = 0;
+        break;
+    }
+  }
+}
+
+int ReadEdifNetlist(char *filename)
+{
+	Message(PCB_MSG_INFO, _("Importing edif netlist %s\n"), filename);
+	ParseEDIF(filename, NULL);
+
+	return 0;
+}
diff --git a/src_plugins/import_edif/edif.h b/src_plugins/import_edif/edif.h
new file mode 100644
index 0000000..505014e
--- /dev/null
+++ b/src_plugins/import_edif/edif.h
@@ -0,0 +1,365 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY_EDIF_EDIF_TAB_H_INCLUDED
+# define YY_EDIF_EDIF_TAB_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int edifdebug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    EDIF_TOK_IDENT = 258,
+    EDIF_TOK_INT = 259,
+    EDIF_TOK_KEYWORD = 260,
+    EDIF_TOK_STR = 261,
+    EDIF_TOK_ANGLE = 262,
+    EDIF_TOK_BEHAVIOR = 263,
+    EDIF_TOK_CALCULATED = 264,
+    EDIF_TOK_CAPACITANCE = 265,
+    EDIF_TOK_CENTERCENTER = 266,
+    EDIF_TOK_CENTERLEFT = 267,
+    EDIF_TOK_CENTERRIGHT = 268,
+    EDIF_TOK_CHARGE = 269,
+    EDIF_TOK_CONDUCTANCE = 270,
+    EDIF_TOK_CURRENT = 271,
+    EDIF_TOK_DISTANCE = 272,
+    EDIF_TOK_DOCUMENT = 273,
+    EDIF_TOK_ENERGY = 274,
+    EDIF_TOK_EXTEND = 275,
+    EDIF_TOK_FLUX = 276,
+    EDIF_TOK_FREQUENCY = 277,
+    EDIF_TOK_GENERIC = 278,
+    EDIF_TOK_GRAPHIC = 279,
+    EDIF_TOK_INDUCTANCE = 280,
+    EDIF_TOK_INOUT = 281,
+    EDIF_TOK_INPUT = 282,
+    EDIF_TOK_LOGICMODEL = 283,
+    EDIF_TOK_LOWERCENTER = 284,
+    EDIF_TOK_LOWERLEFT = 285,
+    EDIF_TOK_LOWERRIGHT = 286,
+    EDIF_TOK_MASKLAYOUT = 287,
+    EDIF_TOK_MASS = 288,
+    EDIF_TOK_MEASURED = 289,
+    EDIF_TOK_MX = 290,
+    EDIF_TOK_MXR90 = 291,
+    EDIF_TOK_MY = 292,
+    EDIF_TOK_MYR90 = 293,
+    EDIF_TOK_NETLIST = 294,
+    EDIF_TOK_OUTPUT = 295,
+    EDIF_TOK_PCBLAYOUT = 296,
+    EDIF_TOK_POWER = 297,
+    EDIF_TOK_R0 = 298,
+    EDIF_TOK_R180 = 299,
+    EDIF_TOK_R270 = 300,
+    EDIF_TOK_R90 = 301,
+    EDIF_TOK_REQUIRED = 302,
+    EDIF_TOK_RESISTANCE = 303,
+    EDIF_TOK_RIPPER = 304,
+    EDIF_TOK_ROUND = 305,
+    EDIF_TOK_SCHEMATIC = 306,
+    EDIF_TOK_STRANGER = 307,
+    EDIF_TOK_SYMBOLIC = 308,
+    EDIF_TOK_TEMPERATURE = 309,
+    EDIF_TOK_TIE = 310,
+    EDIF_TOK_TIME = 311,
+    EDIF_TOK_TRUNCATE = 312,
+    EDIF_TOK_UPPERCENTER = 313,
+    EDIF_TOK_UPPERLEFT = 314,
+    EDIF_TOK_UPPERRIGHT = 315,
+    EDIF_TOK_VOLTAGE = 316,
+    EDIF_TOK_ACLOAD = 317,
+    EDIF_TOK_AFTER = 318,
+    EDIF_TOK_ANNOTATE = 319,
+    EDIF_TOK_APPLY = 320,
+    EDIF_TOK_ARC = 321,
+    EDIF_TOK_ARRAY = 322,
+    EDIF_TOK_ARRAYMACRO = 323,
+    EDIF_TOK_ARRAYRELATEDINFO = 324,
+    EDIF_TOK_ARRAYSITE = 325,
+    EDIF_TOK_ATLEAST = 326,
+    EDIF_TOK_ATMOST = 327,
+    EDIF_TOK_AUTHOR = 328,
+    EDIF_TOK_BASEARRAY = 329,
+    EDIF_TOK_BECOMES = 330,
+    EDIF_TOK_BETWEEN = 331,
+    EDIF_TOK_BOOLEAN = 332,
+    EDIF_TOK_BOOLEANDISPLAY = 333,
+    EDIF_TOK_BOOLEANMAP = 334,
+    EDIF_TOK_BORDERPATTERN = 335,
+    EDIF_TOK_BORDERWIDTH = 336,
+    EDIF_TOK_BOUNDINGBOX = 337,
+    EDIF_TOK_CELL = 338,
+    EDIF_TOK_CELLREF = 339,
+    EDIF_TOK_CELLTYPE = 340,
+    EDIF_TOK_CHANGE = 341,
+    EDIF_TOK_CIRCLE = 342,
+    EDIF_TOK_COLOR = 343,
+    EDIF_TOK_COMMENT = 344,
+    EDIF_TOK_COMMENTGRAPHICS = 345,
+    EDIF_TOK_COMPOUND = 346,
+    EDIF_TOK_CONNECTLOCATION = 347,
+    EDIF_TOK_CONTENTS = 348,
+    EDIF_TOK_CORNERTYPE = 349,
+    EDIF_TOK_CRITICALITY = 350,
+    EDIF_TOK_CURRENTMAP = 351,
+    EDIF_TOK_CURVE = 352,
+    EDIF_TOK_CYCLE = 353,
+    EDIF_TOK_DATAORIGIN = 354,
+    EDIF_TOK_DCFANINLOAD = 355,
+    EDIF_TOK_DCFANOUTLOAD = 356,
+    EDIF_TOK_DCMAXFANIN = 357,
+    EDIF_TOK_DCMAXFANOUT = 358,
+    EDIF_TOK_DELAY = 359,
+    EDIF_TOK_DELTA = 360,
+    EDIF_TOK_DERIVATION = 361,
+    EDIF_TOK_DESIGN = 362,
+    EDIF_TOK_DESIGNATOR = 363,
+    EDIF_TOK_DIFFERENCE = 364,
+    EDIF_TOK_DIRECTION = 365,
+    EDIF_TOK_DISPLAY = 366,
+    EDIF_TOK_DOMINATES = 367,
+    EDIF_TOK_DOT = 368,
+    EDIF_TOK_DURATION = 369,
+    EDIF_TOK_E = 370,
+    EDIF_TOK_EDIF = 371,
+    EDIF_TOK_EDIFLEVEL = 372,
+    EDIF_TOK_EDIFVERSION = 373,
+    EDIF_TOK_ENCLOSUREDISTANCE = 374,
+    EDIF_TOK_ENDTYPE = 375,
+    EDIF_TOK_ENTRY = 376,
+    EDIF_TOK_EVENT = 377,
+    EDIF_TOK_EXACTLY = 378,
+    EDIF_TOK_EXTERNAL = 379,
+    EDIF_TOK_FABRICATE = 380,
+    EDIF_TOK_FALSE = 381,
+    EDIF_TOK_FIGURE = 382,
+    EDIF_TOK_FIGUREAREA = 383,
+    EDIF_TOK_FIGUREGROUP = 384,
+    EDIF_TOK_FIGUREGROUPOBJECT = 385,
+    EDIF_TOK_FIGUREGROUPOVERRIDE = 386,
+    EDIF_TOK_FIGUREGROUPREF = 387,
+    EDIF_TOK_FIGUREPERIMETER = 388,
+    EDIF_TOK_FIGUREWIDTH = 389,
+    EDIF_TOK_FILLPATTERN = 390,
+    EDIF_TOK_FOLLOW = 391,
+    EDIF_TOK_FORBIDDENEVENT = 392,
+    EDIF_TOK_GLOBALPORTREF = 393,
+    EDIF_TOK_GREATERTHAN = 394,
+    EDIF_TOK_GRIDMAP = 395,
+    EDIF_TOK_IGNORE = 396,
+    EDIF_TOK_INCLUDEFIGUREGROUP = 397,
+    EDIF_TOK_INITIAL = 398,
+    EDIF_TOK_INSTANCE = 399,
+    EDIF_TOK_INSTANCEBACKANNOTATE = 400,
+    EDIF_TOK_INSTANCEGROUP = 401,
+    EDIF_TOK_INSTANCEMAP = 402,
+    EDIF_TOK_INSTANCEREF = 403,
+    EDIF_TOK_INTEGER = 404,
+    EDIF_TOK_INTEGERDISPLAY = 405,
+    EDIF_TOK_INTERFACE = 406,
+    EDIF_TOK_INTERFIGUREGROUPSPACING = 407,
+    EDIF_TOK_INTERSECTION = 408,
+    EDIF_TOK_INTRAFIGUREGROUPSPACING = 409,
+    EDIF_TOK_INVERSE = 410,
+    EDIF_TOK_ISOLATED = 411,
+    EDIF_TOK_JOINED = 412,
+    EDIF_TOK_JUSTIFY = 413,
+    EDIF_TOK_KEYWORDDISPLAY = 414,
+    EDIF_TOK_KEYWORDLEVEL = 415,
+    EDIF_TOK_KEYWORDMAP = 416,
+    EDIF_TOK_LESSTHAN = 417,
+    EDIF_TOK_LIBRARY = 418,
+    EDIF_TOK_LIBRARYREF = 419,
+    EDIF_TOK_LISTOFNETS = 420,
+    EDIF_TOK_LISTOFPORTS = 421,
+    EDIF_TOK_LOADDELAY = 422,
+    EDIF_TOK_LOGICASSIGN = 423,
+    EDIF_TOK_LOGICINPUT = 424,
+    EDIF_TOK_LOGICLIST = 425,
+    EDIF_TOK_LOGICMAPINPUT = 426,
+    EDIF_TOK_LOGICMAPOUTPUT = 427,
+    EDIF_TOK_LOGICONEOF = 428,
+    EDIF_TOK_LOGICOUTPUT = 429,
+    EDIF_TOK_LOGICPORT = 430,
+    EDIF_TOK_LOGICREF = 431,
+    EDIF_TOK_LOGICVALUE = 432,
+    EDIF_TOK_LOGICWAVEFORM = 433,
+    EDIF_TOK_MAINTAIN = 434,
+    EDIF_TOK_MATCH = 435,
+    EDIF_TOK_MEMBER = 436,
+    EDIF_TOK_MINOMAX = 437,
+    EDIF_TOK_MINOMAXDISPLAY = 438,
+    EDIF_TOK_MNM = 439,
+    EDIF_TOK_MULTIPLEVALUESET = 440,
+    EDIF_TOK_MUSTJOIN = 441,
+    EDIF_TOK_NAME = 442,
+    EDIF_TOK_NET = 443,
+    EDIF_TOK_NETBACKANNOTATE = 444,
+    EDIF_TOK_NETBUNDLE = 445,
+    EDIF_TOK_NETDELAY = 446,
+    EDIF_TOK_NETGROUP = 447,
+    EDIF_TOK_NETMAP = 448,
+    EDIF_TOK_NETREF = 449,
+    EDIF_TOK_NOCHANGE = 450,
+    EDIF_TOK_NONPERMUTABLE = 451,
+    EDIF_TOK_NOTALLOWED = 452,
+    EDIF_TOK_NOTCHSPACING = 453,
+    EDIF_TOK_NUMBER = 454,
+    EDIF_TOK_NUMBERDEFINITION = 455,
+    EDIF_TOK_NUMBERDISPLAY = 456,
+    EDIF_TOK_OFFPAGECONNECTOR = 457,
+    EDIF_TOK_OFFSETEVENT = 458,
+    EDIF_TOK_OPENSHAPE = 459,
+    EDIF_TOK_ORIENTATION = 460,
+    EDIF_TOK_ORIGIN = 461,
+    EDIF_TOK_OVERHANGDISTANCE = 462,
+    EDIF_TOK_OVERLAPDISTANCE = 463,
+    EDIF_TOK_OVERSIZE = 464,
+    EDIF_TOK_OWNER = 465,
+    EDIF_TOK_PAGE = 466,
+    EDIF_TOK_PAGESIZE = 467,
+    EDIF_TOK_PARAMETER = 468,
+    EDIF_TOK_PARAMETERASSIGN = 469,
+    EDIF_TOK_PARAMETERDISPLAY = 470,
+    EDIF_TOK_PATH = 471,
+    EDIF_TOK_PATHDELAY = 472,
+    EDIF_TOK_PATHWIDTH = 473,
+    EDIF_TOK_PERMUTABLE = 474,
+    EDIF_TOK_PHYSICALDESIGNRULE = 475,
+    EDIF_TOK_PLUG = 476,
+    EDIF_TOK_POINT = 477,
+    EDIF_TOK_POINTDISPLAY = 478,
+    EDIF_TOK_POINTLIST = 479,
+    EDIF_TOK_POLYGON = 480,
+    EDIF_TOK_PORT = 481,
+    EDIF_TOK_PORTBACKANNOTATE = 482,
+    EDIF_TOK_PORTBUNDLE = 483,
+    EDIF_TOK_PORTDELAY = 484,
+    EDIF_TOK_PORTGROUP = 485,
+    EDIF_TOK_PORTIMPLEMENTATION = 486,
+    EDIF_TOK_PORTINSTANCE = 487,
+    EDIF_TOK_PORTLIST = 488,
+    EDIF_TOK_PORTLISTALIAS = 489,
+    EDIF_TOK_PORTMAP = 490,
+    EDIF_TOK_PORTREF = 491,
+    EDIF_TOK_PROGRAM = 492,
+    EDIF_TOK_PROPERTY = 493,
+    EDIF_TOK_PROPERTYDISPLAY = 494,
+    EDIF_TOK_PROTECTIONFRAME = 495,
+    EDIF_TOK_PT = 496,
+    EDIF_TOK_RANGEVECTOR = 497,
+    EDIF_TOK_RECTANGLE = 498,
+    EDIF_TOK_RECTANGLESIZE = 499,
+    EDIF_TOK_RENAME = 500,
+    EDIF_TOK_RESOLVES = 501,
+    EDIF_TOK_SCALE = 502,
+    EDIF_TOK_SCALEX = 503,
+    EDIF_TOK_SCALEY = 504,
+    EDIF_TOK_SECTION = 505,
+    EDIF_TOK_SHAPE = 506,
+    EDIF_TOK_SIMULATE = 507,
+    EDIF_TOK_SIMULATIONINFO = 508,
+    EDIF_TOK_SINGLEVALUESET = 509,
+    EDIF_TOK_SITE = 510,
+    EDIF_TOK_SOCKET = 511,
+    EDIF_TOK_SOCKETSET = 512,
+    EDIF_TOK_STATUS = 513,
+    EDIF_TOK_STEADY = 514,
+    EDIF_TOK_STRING = 515,
+    EDIF_TOK_STRINGDISPLAY = 516,
+    EDIF_TOK_STRONG = 517,
+    EDIF_TOK_SYMBOL = 518,
+    EDIF_TOK_SYMMETRY = 519,
+    EDIF_TOK_TABLE = 520,
+    EDIF_TOK_TABLEDEFAULT = 521,
+    EDIF_TOK_TECHNOLOGY = 522,
+    EDIF_TOK_TEXTHEIGHT = 523,
+    EDIF_TOK_TIMEINTERVAL = 524,
+    EDIF_TOK_TIMESTAMP = 525,
+    EDIF_TOK_TIMING = 526,
+    EDIF_TOK_TRANSFORM = 527,
+    EDIF_TOK_TRANSITION = 528,
+    EDIF_TOK_TRIGGER = 529,
+    EDIF_TOK_TRUE = 530,
+    EDIF_TOK_UNCONSTRAINED = 531,
+    EDIF_TOK_UNDEFINED = 532,
+    EDIF_TOK_UNION = 533,
+    EDIF_TOK_UNIT = 534,
+    EDIF_TOK_UNUSED = 535,
+    EDIF_TOK_USERDATA = 536,
+    EDIF_TOK_VERSION = 537,
+    EDIF_TOK_VIEW = 538,
+    EDIF_TOK_VIEWLIST = 539,
+    EDIF_TOK_VIEWMAP = 540,
+    EDIF_TOK_VIEWREF = 541,
+    EDIF_TOK_VIEWTYPE = 542,
+    EDIF_TOK_VISIBLE = 543,
+    EDIF_TOK_VOLTAGEMAP = 544,
+    EDIF_TOK_WAVEVALUE = 545,
+    EDIF_TOK_WEAK = 546,
+    EDIF_TOK_WEAKJOINED = 547,
+    EDIF_TOK_WHEN = 548,
+    EDIF_TOK_WRITTEN = 549
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 198 "../../src_plugins/import_edif/edif.y" /* yacc.c:1909  */
+
+    char* s;
+    pair_list* pl;
+    str_pair* ps;
+
+#line 355 "edif.tab.h" /* yacc.c:1909  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE ediflval;
+
+int edifparse (void);
+
+#endif /* !YY_EDIF_EDIF_TAB_H_INCLUDED  */
diff --git a/src_plugins/import_edif/edif.y b/src_plugins/import_edif/edif.y
new file mode 100644
index 0000000..3dd069d
--- /dev/null
+++ b/src_plugins/import_edif/edif.y
@@ -0,0 +1,4455 @@
+%{
+/*
+ * PCB Edif parser based heavily on:
+ *
+ *	Header: edif.y,v 1.18 87/12/07 19:59:49 roger Locked
+ */
+/************************************************************************
+ *									*
+ *				edif.y					*
+ *									*
+ *			EDIF 2.0.0 parser, Level 0			*
+ *									*
+ *	You are free to copy, distribute, use it, abuse it, make it	*
+ *	write bad tracks all over the disk ... or anything else.	*
+ *									*
+ *	Your friendly neighborhood Rogue Monster - roger at mips.com	*
+ *									*
+ ************************************************************************/
+#include <stdio.h>
+
+/* for malloc, free, atoi */
+#include <stdlib.h>
+
+/* for strcpy */
+#include <string.h>
+
+#include <ctype.h>
+
+#include "global.h"
+#include "data.h"
+#include "error.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+/* from mymem.h, not include because of the malloc junk */
+LibraryMenuTypePtr GetLibraryMenuMemory(LibraryTypePtr, int *idx);
+LibraryEntryTypePtr GetLibraryEntryMemory(LibraryMenuTypePtr);
+
+
+/*
+ *	Local definitions.
+ */
+#define	IDENT_LENGTH		255
+#define	Malloc(s)		malloc(s)
+#define	Free(p)			free(p)
+#define	Getc(s)			getc(s)
+#define	Ungetc(c)		ungetc(c,Input)
+
+ typedef struct _str_pair
+ {
+     char* str1;
+     char* str2;
+     struct _str_pair* next;
+ } str_pair;
+
+ typedef struct _pair_list
+ {
+     char* name;
+     str_pair* list;
+ } pair_list;
+
+ str_pair* new_str_pair(char* s1, char* s2)
+ {
+   str_pair* ps = (str_pair *)malloc(sizeof(str_pair));
+     ps->str1 = s1;
+     ps->str2 = s2;
+     ps->next = NULL;
+     return ps;
+ }
+
+ pair_list* new_pair_list(str_pair* ps)
+ {
+   pair_list* pl = (pair_list *)malloc(sizeof(pair_list));
+     pl->list = ps;
+     pl->name = NULL;
+     return pl;
+ }
+
+ void str_pair_free(str_pair* ps)
+ {
+     str_pair* node;
+     while ( ps )
+     {
+	 free(ps->str1);
+	 free(ps->str2);
+	 node = ps;
+	 ps = ps->next;
+	 free(node);
+     }
+ }
+
+ void pair_list_free(pair_list* pl)
+ {
+     str_pair_free(pl->list);
+     free(pl->name);
+     free(pl);
+ }
+
+ void define_pcb_net(str_pair* name, pair_list* nodes)
+ {
+     int tl;
+     str_pair* done_node;
+     str_pair* node;
+     char* buf;
+     char* p;
+     LibraryEntryTypePtr entry;
+     LibraryMenuTypePtr menu = GetLibraryMenuMemory(&PCB->NetlistLib[NETLIST_INPUT], NULL);
+
+     if ( !name->str1 )
+     {
+	 /* no net name given, stop now */
+	 /* if renamed str2 also exists and must be freed */
+	 if ( name->str2 )  free(name->str2);
+	 free(name);
+	 pair_list_free(nodes);
+	 return;
+     }
+     menu->Name = pcb_strdup (name->str1);
+     free(name->str1);
+     /* if renamed str2 also exists and must be freed */
+     if ( name->str2 )  free(name->str2);
+     free(name);
+     buf = (char *)malloc(256);
+     if ( !buf )
+     {
+	 /* no memory */
+	 pair_list_free(nodes);
+	 return;
+     }
+
+     node = nodes->list;
+     free(nodes->name);
+     free(nodes);
+     while ( node )
+     {
+	 /* check for node with no instance */
+	 if ( !node->str1 )
+	 {
+	     /* toss it and move on */
+	     free(node->str2);
+	     done_node = node;
+	     node = node->next;
+	     free(done_node);
+	     continue;
+	 }
+	 tl = strlen(node->str1) + strlen(node->str2);
+	 if ( tl + 3 > 256 )
+	 {
+	     free(buf);
+	     buf = (char *)malloc(tl+3);
+	     if ( !buf )
+	     {
+		 /* no memory */
+		 str_pair_free(node);
+		 return;
+	     }
+	 }
+	 strcpy(buf,node->str1);
+	 /* make all upper case, because of PCB funky behaviour */
+	 p=buf;
+	 while ( *p )
+	 {
+	     *p = toupper( (int) *p);
+	     p++;
+	 }
+	 /* add dash separating designator from node */
+	 *(buf+strlen(node->str1)) = '-';
+	 /* check for the edif number prefix */
+	 if ( node->str2[0] == '&' )
+	 {
+	     /* skip number prefix */
+	     strcpy(buf+strlen(node->str1)+1,node->str2 +1);
+	 }
+	 else
+	 {
+	     strcpy(buf+strlen(node->str1)+1,node->str2);
+	 }
+	 /* free the strings */
+	 free(node->str1);
+	 free(node->str2);
+	 entry = GetLibraryEntryMemory (menu);
+	 entry->ListEntry = pcb_strdup(buf);
+	 done_node = node;
+	 node = node->next;
+	 free(done_node);
+     }
+ }
+
+
+/* forward function declarations */
+ static int yylex(void);
+ static void yyerror(const char *);
+ static void PopC(void);
+%}
+
+%name-prefix "edif"
+
+%union {
+    char* s;
+    pair_list* pl;
+    str_pair* ps;
+}
+
+%type <s> Int Ident Str Keyword Name _Name
+%type <ps>  PortRef _PortRef NetNameDef NameDef
+%type <ps> Rename _Joined
+%type <s>__Rename _Rename NameRef
+%type <s> InstanceRef InstNameRef Member PortNameRef
+%type <pl> Net _Net Joined
+
+%token	<s>	EDIF_TOK_IDENT
+%token	<s>	EDIF_TOK_INT
+%token	<s>	EDIF_TOK_KEYWORD
+%token	<s>	EDIF_TOK_STR
+
+%token		EDIF_TOK_ANGLE
+%token		EDIF_TOK_BEHAVIOR
+%token		EDIF_TOK_CALCULATED
+%token		EDIF_TOK_CAPACITANCE
+%token		EDIF_TOK_CENTERCENTER
+%token		EDIF_TOK_CENTERLEFT
+%token		EDIF_TOK_CENTERRIGHT
+%token		EDIF_TOK_CHARGE
+%token		EDIF_TOK_CONDUCTANCE
+%token		EDIF_TOK_CURRENT
+%token		EDIF_TOK_DISTANCE
+%token		EDIF_TOK_DOCUMENT
+%token		EDIF_TOK_ENERGY
+%token		EDIF_TOK_EXTEND
+%token		EDIF_TOK_FLUX
+%token		EDIF_TOK_FREQUENCY
+%token		EDIF_TOK_GENERIC
+%token		EDIF_TOK_GRAPHIC
+%token		EDIF_TOK_INDUCTANCE
+%token		EDIF_TOK_INOUT
+%token		EDIF_TOK_INPUT
+%token		EDIF_TOK_LOGICMODEL
+%token		EDIF_TOK_LOWERCENTER
+%token		EDIF_TOK_LOWERLEFT
+%token		EDIF_TOK_LOWERRIGHT
+%token		EDIF_TOK_MASKLAYOUT
+%token		EDIF_TOK_MASS
+%token		EDIF_TOK_MEASURED
+%token		EDIF_TOK_MX
+%token		EDIF_TOK_MXR90
+%token		EDIF_TOK_MY
+%token		EDIF_TOK_MYR90
+%token		EDIF_TOK_NETLIST
+%token		EDIF_TOK_OUTPUT
+%token		EDIF_TOK_PCBLAYOUT
+%token		EDIF_TOK_POWER
+%token		EDIF_TOK_R0
+%token		EDIF_TOK_R180
+%token		EDIF_TOK_R270
+%token		EDIF_TOK_R90
+%token		EDIF_TOK_REQUIRED
+%token		EDIF_TOK_RESISTANCE
+%token		EDIF_TOK_RIPPER
+%token		EDIF_TOK_ROUND
+%token		EDIF_TOK_SCHEMATIC
+%token		EDIF_TOK_STRANGER
+%token		EDIF_TOK_SYMBOLIC
+%token		EDIF_TOK_TEMPERATURE
+%token		EDIF_TOK_TIE
+%token		EDIF_TOK_TIME
+%token		EDIF_TOK_TRUNCATE
+%token		EDIF_TOK_UPPERCENTER
+%token		EDIF_TOK_UPPERLEFT
+%token		EDIF_TOK_UPPERRIGHT
+%token		EDIF_TOK_VOLTAGE
+
+%token		EDIF_TOK_ACLOAD
+%token		EDIF_TOK_AFTER
+%token		EDIF_TOK_ANNOTATE
+%token		EDIF_TOK_APPLY
+%token		EDIF_TOK_ARC
+%token		EDIF_TOK_ARRAY
+%token		EDIF_TOK_ARRAYMACRO
+%token		EDIF_TOK_ARRAYRELATEDINFO
+%token		EDIF_TOK_ARRAYSITE
+%token		EDIF_TOK_ATLEAST
+%token		EDIF_TOK_ATMOST
+%token		EDIF_TOK_AUTHOR
+%token		EDIF_TOK_BASEARRAY
+%token		EDIF_TOK_BECOMES
+%token		EDIF_TOK_BETWEEN
+%token		EDIF_TOK_BOOLEAN
+%token		EDIF_TOK_BOOLEANDISPLAY
+%token		EDIF_TOK_BOOLEANMAP
+%token		EDIF_TOK_BORDERPATTERN
+%token		EDIF_TOK_BORDERWIDTH
+%token		EDIF_TOK_BOUNDINGBOX
+%token		EDIF_TOK_CELL
+%token		EDIF_TOK_CELLREF
+%token		EDIF_TOK_CELLTYPE
+%token		EDIF_TOK_CHANGE
+%token		EDIF_TOK_CIRCLE
+%token		EDIF_TOK_COLOR
+%token		EDIF_TOK_COMMENT
+%token		EDIF_TOK_COMMENTGRAPHICS
+%token		EDIF_TOK_COMPOUND
+%token		EDIF_TOK_CONNECTLOCATION
+%token		EDIF_TOK_CONTENTS
+%token		EDIF_TOK_CORNERTYPE
+%token		EDIF_TOK_CRITICALITY
+%token		EDIF_TOK_CURRENTMAP
+%token		EDIF_TOK_CURVE
+%token		EDIF_TOK_CYCLE
+%token		EDIF_TOK_DATAORIGIN
+%token		EDIF_TOK_DCFANINLOAD
+%token		EDIF_TOK_DCFANOUTLOAD
+%token		EDIF_TOK_DCMAXFANIN
+%token		EDIF_TOK_DCMAXFANOUT
+%token		EDIF_TOK_DELAY
+%token		EDIF_TOK_DELTA
+%token		EDIF_TOK_DERIVATION
+%token		EDIF_TOK_DESIGN
+%token		EDIF_TOK_DESIGNATOR
+%token		EDIF_TOK_DIFFERENCE
+%token		EDIF_TOK_DIRECTION
+%token		EDIF_TOK_DISPLAY
+%token		EDIF_TOK_DOMINATES
+%token		EDIF_TOK_DOT
+%token		EDIF_TOK_DURATION
+%token		EDIF_TOK_E
+%token		EDIF_TOK_EDIF
+%token		EDIF_TOK_EDIFLEVEL
+%token		EDIF_TOK_EDIFVERSION
+%token		EDIF_TOK_ENCLOSUREDISTANCE
+%token		EDIF_TOK_ENDTYPE
+%token		EDIF_TOK_ENTRY
+%token		EDIF_TOK_EVENT
+%token		EDIF_TOK_EXACTLY
+%token		EDIF_TOK_EXTERNAL
+%token		EDIF_TOK_FABRICATE
+%token		EDIF_TOK_FALSE
+%token		EDIF_TOK_FIGURE
+%token		EDIF_TOK_FIGUREAREA
+%token		EDIF_TOK_FIGUREGROUP
+%token		EDIF_TOK_FIGUREGROUPOBJECT
+%token		EDIF_TOK_FIGUREGROUPOVERRIDE
+%token		EDIF_TOK_FIGUREGROUPREF
+%token		EDIF_TOK_FIGUREPERIMETER
+%token		EDIF_TOK_FIGUREWIDTH
+%token		EDIF_TOK_FILLPATTERN
+%token		EDIF_TOK_FOLLOW
+%token		EDIF_TOK_FORBIDDENEVENT
+%token		EDIF_TOK_GLOBALPORTREF
+%token		EDIF_TOK_GREATERTHAN
+%token		EDIF_TOK_GRIDMAP
+%token		EDIF_TOK_IGNORE
+%token		EDIF_TOK_INCLUDEFIGUREGROUP
+%token		EDIF_TOK_INITIAL
+%token		EDIF_TOK_INSTANCE
+%token		EDIF_TOK_INSTANCEBACKANNOTATE
+%token		EDIF_TOK_INSTANCEGROUP
+%token		EDIF_TOK_INSTANCEMAP
+%token		EDIF_TOK_INSTANCEREF
+%token		EDIF_TOK_INTEGER
+%token		EDIF_TOK_INTEGERDISPLAY
+%token		EDIF_TOK_INTERFACE
+%token		EDIF_TOK_INTERFIGUREGROUPSPACING
+%token		EDIF_TOK_INTERSECTION
+%token		EDIF_TOK_INTRAFIGUREGROUPSPACING
+%token		EDIF_TOK_INVERSE
+%token		EDIF_TOK_ISOLATED
+%token		EDIF_TOK_JOINED
+%token		EDIF_TOK_JUSTIFY
+%token		EDIF_TOK_KEYWORDDISPLAY
+%token		EDIF_TOK_KEYWORDLEVEL
+%token		EDIF_TOK_KEYWORDMAP
+%token		EDIF_TOK_LESSTHAN
+%token		EDIF_TOK_LIBRARY
+%token		EDIF_TOK_LIBRARYREF
+%token		EDIF_TOK_LISTOFNETS
+%token		EDIF_TOK_LISTOFPORTS
+%token		EDIF_TOK_LOADDELAY
+%token		EDIF_TOK_LOGICASSIGN
+%token		EDIF_TOK_LOGICINPUT
+%token		EDIF_TOK_LOGICLIST
+%token		EDIF_TOK_LOGICMAPINPUT
+%token		EDIF_TOK_LOGICMAPOUTPUT
+%token		EDIF_TOK_LOGICONEOF
+%token		EDIF_TOK_LOGICOUTPUT
+%token		EDIF_TOK_LOGICPORT
+%token		EDIF_TOK_LOGICREF
+%token		EDIF_TOK_LOGICVALUE
+%token		EDIF_TOK_LOGICWAVEFORM
+%token		EDIF_TOK_MAINTAIN
+%token		EDIF_TOK_MATCH
+%token		EDIF_TOK_MEMBER
+%token		EDIF_TOK_MINOMAX
+%token		EDIF_TOK_MINOMAXDISPLAY
+%token		EDIF_TOK_MNM
+%token		EDIF_TOK_MULTIPLEVALUESET
+%token		EDIF_TOK_MUSTJOIN
+%token		EDIF_TOK_NAME
+%token		EDIF_TOK_NET
+%token		EDIF_TOK_NETBACKANNOTATE
+%token		EDIF_TOK_NETBUNDLE
+%token		EDIF_TOK_NETDELAY
+%token		EDIF_TOK_NETGROUP
+%token		EDIF_TOK_NETMAP
+%token		EDIF_TOK_NETREF
+%token		EDIF_TOK_NOCHANGE
+%token		EDIF_TOK_NONPERMUTABLE
+%token		EDIF_TOK_NOTALLOWED
+%token		EDIF_TOK_NOTCHSPACING
+%token		EDIF_TOK_NUMBER
+%token		EDIF_TOK_NUMBERDEFINITION
+%token		EDIF_TOK_NUMBERDISPLAY
+%token		EDIF_TOK_OFFPAGECONNECTOR
+%token		EDIF_TOK_OFFSETEVENT
+%token		EDIF_TOK_OPENSHAPE
+%token		EDIF_TOK_ORIENTATION
+%token		EDIF_TOK_ORIGIN
+%token		EDIF_TOK_OVERHANGDISTANCE
+%token		EDIF_TOK_OVERLAPDISTANCE
+%token		EDIF_TOK_OVERSIZE
+%token		EDIF_TOK_OWNER
+%token		EDIF_TOK_PAGE
+%token		EDIF_TOK_PAGESIZE
+%token		EDIF_TOK_PARAMETER
+%token		EDIF_TOK_PARAMETERASSIGN
+%token		EDIF_TOK_PARAMETERDISPLAY
+%token		EDIF_TOK_PATH
+%token		EDIF_TOK_PATHDELAY
+%token		EDIF_TOK_PATHWIDTH
+%token		EDIF_TOK_PERMUTABLE
+%token		EDIF_TOK_PHYSICALDESIGNRULE
+%token		EDIF_TOK_PLUG
+%token		EDIF_TOK_POINT
+%token		EDIF_TOK_POINTDISPLAY
+%token		EDIF_TOK_POINTLIST
+%token		EDIF_TOK_POLYGON
+%token		EDIF_TOK_PORT
+%token		EDIF_TOK_PORTBACKANNOTATE
+%token		EDIF_TOK_PORTBUNDLE
+%token		EDIF_TOK_PORTDELAY
+%token		EDIF_TOK_PORTGROUP
+%token		EDIF_TOK_PORTIMPLEMENTATION
+%token		EDIF_TOK_PORTINSTANCE
+%token		EDIF_TOK_PORTLIST
+%token		EDIF_TOK_PORTLISTALIAS
+%token		EDIF_TOK_PORTMAP
+%token		EDIF_TOK_PORTREF
+%token		EDIF_TOK_PROGRAM
+%token		EDIF_TOK_PROPERTY
+%token		EDIF_TOK_PROPERTYDISPLAY
+%token		EDIF_TOK_PROTECTIONFRAME
+%token		EDIF_TOK_PT
+%token		EDIF_TOK_RANGEVECTOR
+%token		EDIF_TOK_RECTANGLE
+%token		EDIF_TOK_RECTANGLESIZE
+%token		EDIF_TOK_RENAME
+%token		EDIF_TOK_RESOLVES
+%token		EDIF_TOK_SCALE
+%token		EDIF_TOK_SCALEX
+%token		EDIF_TOK_SCALEY
+%token		EDIF_TOK_SECTION
+%token		EDIF_TOK_SHAPE
+%token		EDIF_TOK_SIMULATE
+%token		EDIF_TOK_SIMULATIONINFO
+%token		EDIF_TOK_SINGLEVALUESET
+%token		EDIF_TOK_SITE
+%token		EDIF_TOK_SOCKET
+%token		EDIF_TOK_SOCKETSET
+%token		EDIF_TOK_STATUS
+%token		EDIF_TOK_STEADY
+%token		EDIF_TOK_STRING
+%token		EDIF_TOK_STRINGDISPLAY
+%token		EDIF_TOK_STRONG
+%token		EDIF_TOK_SYMBOL
+%token		EDIF_TOK_SYMMETRY
+%token		EDIF_TOK_TABLE
+%token		EDIF_TOK_TABLEDEFAULT
+%token		EDIF_TOK_TECHNOLOGY
+%token		EDIF_TOK_TEXTHEIGHT
+%token		EDIF_TOK_TIMEINTERVAL
+%token		EDIF_TOK_TIMESTAMP
+%token		EDIF_TOK_TIMING
+%token		EDIF_TOK_TRANSFORM
+%token		EDIF_TOK_TRANSITION
+%token		EDIF_TOK_TRIGGER
+%token		EDIF_TOK_TRUE
+%token		EDIF_TOK_UNCONSTRAINED
+%token		EDIF_TOK_UNDEFINED
+%token		EDIF_TOK_UNION
+%token		EDIF_TOK_UNIT
+%token		EDIF_TOK_UNUSED
+%token		EDIF_TOK_USERDATA
+%token		EDIF_TOK_VERSION
+%token		EDIF_TOK_VIEW
+%token		EDIF_TOK_VIEWLIST
+%token		EDIF_TOK_VIEWMAP
+%token		EDIF_TOK_VIEWREF
+%token		EDIF_TOK_VIEWTYPE
+%token		EDIF_TOK_VISIBLE
+%token		EDIF_TOK_VOLTAGEMAP
+%token		EDIF_TOK_WAVEVALUE
+%token		EDIF_TOK_WEAK
+%token		EDIF_TOK_WEAKJOINED
+%token		EDIF_TOK_WHEN
+%token		EDIF_TOK_WRITTEN
+
+%start	Edif
+
+%%
+
+PopC :		')' { PopC(); }
+     ;
+
+Edif :		EDIF_TOK_EDIF EdifFileName EdifVersion EdifLevel KeywordMap _Edif PopC
+     ;
+
+_Edif :
+      |		_Edif Status
+      |		_Edif External
+      |		_Edif Library
+      |		_Edif Design
+      |		_Edif Comment
+      |		_Edif UserData
+      ;
+
+EdifFileName :	NameDef { str_pair_free($1); }
+	     ;
+
+EdifLevel :	EDIF_TOK_EDIFLEVEL Int PopC { free($2); }
+	  ;
+
+EdifVersion :	EDIF_TOK_EDIFVERSION Int Int Int PopC
+{ free($2); free($3); free($4); }
+	    ;
+
+AcLoad :	EDIF_TOK_ACLOAD _AcLoad PopC
+       ;
+
+_AcLoad :	MiNoMaValue
+	|	MiNoMaDisp
+	;
+
+After :		EDIF_TOK_AFTER _After PopC
+      ;
+
+_After :	MiNoMaValue
+       |	_After Follow
+       |	_After Maintain
+       |	_After LogicAssn
+       |	_After Comment
+       |	_After UserData
+       ;
+
+Annotate :	EDIF_TOK_ANNOTATE _Annotate PopC
+	 ;
+
+_Annotate :	Str { free($1); }
+	  |	StrDisplay
+	  ;
+
+Apply :		EDIF_TOK_APPLY _Apply PopC
+      ;
+
+_Apply :	Cycle
+       |	_Apply LogicIn
+       |	_Apply LogicOut
+       |	_Apply Comment
+       |	_Apply UserData
+       ;
+
+Arc :		EDIF_TOK_ARC PointValue PointValue PointValue PopC
+    ;
+
+Array :		EDIF_TOK_ARRAY NameDef Int _Array PopC { str_pair_free($2); free($3); }
+      ;
+
+_Array :
+       |	Int { free($1); }
+       ;
+
+ArrayMacro :	EDIF_TOK_ARRAYMACRO Plug PopC
+	   ;
+
+ArrayRelInfo :	EDIF_TOK_ARRAYRELATEDINFO _ArrayRelInfo PopC
+	     ;
+
+_ArrayRelInfo :	BaseArray
+	      |	ArraySite
+	      |	ArrayMacro
+	      |	_ArrayRelInfo Comment
+	      |	_ArrayRelInfo UserData
+	      ;
+
+ArraySite :	EDIF_TOK_ARRAYSITE Socket PopC
+	  ;
+
+AtLeast :	EDIF_TOK_ATLEAST ScaledInt PopC
+	;
+
+AtMost :	EDIF_TOK_ATMOST ScaledInt PopC
+       ;
+
+Author :	EDIF_TOK_AUTHOR Str PopC { free($2); }
+       ;
+
+BaseArray :	EDIF_TOK_BASEARRAY PopC
+	  ;
+
+Becomes :	EDIF_TOK_BECOMES _Becomes PopC
+	;
+
+_Becomes :	LogicNameRef
+	 |	LogicList
+	 |	LogicOneOf
+	 ;
+
+Between :	EDIF_TOK_BETWEEN __Between _Between PopC
+	;
+
+__Between :	AtLeast
+	  |	GreaterThan
+	  ;
+
+_Between :	AtMost
+	 |	LessThan
+	 ;
+
+Boolean :	EDIF_TOK_BOOLEAN _Boolean PopC
+	;
+
+_Boolean :
+	 |	_Boolean BooleanValue
+	 |	_Boolean BooleanDisp
+	 |	_Boolean Boolean
+	 ;
+
+BooleanDisp :	EDIF_TOK_BOOLEANDISPLAY _BooleanDisp PopC
+	    ;
+
+_BooleanDisp :	BooleanValue
+	     |	_BooleanDisp Display
+	     ;
+
+BooleanMap :	EDIF_TOK_BOOLEANMAP BooleanValue PopC
+	   ;
+
+BooleanValue :	True
+	     |	False
+	     ;
+
+BorderPat :	EDIF_TOK_BORDERPATTERN Int Int Boolean PopC { free($2); free($3); }
+	  ;
+
+BorderWidth :	EDIF_TOK_BORDERWIDTH Int PopC { free($2); }
+	    ;
+
+BoundBox :	EDIF_TOK_BOUNDINGBOX Rectangle PopC
+	 ;
+
+Cell :		EDIF_TOK_CELL CellNameDef _Cell PopC
+     ;
+
+_Cell :		CellType
+      |		_Cell Status
+      |		_Cell ViewMap
+      |		_Cell View
+      |		_Cell Comment
+      |		_Cell UserData
+      |		_Cell Property
+      ;
+
+CellNameDef :	NameDef { str_pair_free($1); }
+	    ;
+
+CellRef :	EDIF_TOK_CELLREF CellNameRef _CellRef PopC
+	;
+
+_CellRef :
+	 |	LibraryRef
+	 ;
+
+CellNameRef :	NameRef { free($1); }
+	    ;
+
+CellType :	EDIF_TOK_CELLTYPE _CellType PopC
+	 ;
+
+_CellType :	EDIF_TOK_TIE
+	  |	EDIF_TOK_RIPPER
+	  |	EDIF_TOK_GENERIC
+	  ;
+
+Change :	EDIF_TOK_CHANGE __Change _Change PopC
+       ;
+
+__Change :	PortNameRef
+	 |	PortRef { str_pair_free($1); }
+	 |	PortList
+	 ;
+
+_Change :
+	|	Becomes
+	|	Transition
+	;
+
+Circle :	EDIF_TOK_CIRCLE PointValue PointValue _Circle PopC
+       ;
+
+_Circle :
+	|	_Circle Property
+	;
+
+Color :		EDIF_TOK_COLOR ScaledInt ScaledInt ScaledInt PopC
+      ;
+
+Comment :	EDIF_TOK_COMMENT _Comment PopC
+	;
+
+_Comment :
+	 |	_Comment Str { free($2); }
+	 ;
+
+CommGraph :	EDIF_TOK_COMMENTGRAPHICS _CommGraph PopC
+          ;
+
+_CommGraph :
+	   |	_CommGraph Annotate
+	   |	_CommGraph Figure
+	   |	_CommGraph Instance
+	   |	_CommGraph BoundBox
+	   |	_CommGraph Property
+	   |	_CommGraph Comment
+	   |	_CommGraph UserData
+	   ;
+
+Compound :	EDIF_TOK_COMPOUND LogicNameRef PopC
+	 ;
+
+Contents :	EDIF_TOK_CONTENTS _Contents PopC
+	 ;
+
+_Contents :
+	  |	_Contents Instance
+	  |	_Contents OffPageConn
+	  |	_Contents Figure
+	  |	_Contents Section
+	  |	_Contents Net
+	  |	_Contents NetBundle
+	  |	_Contents Page
+	  |	_Contents CommGraph
+	  |	_Contents PortImpl
+	  |	_Contents Timing
+	  |	_Contents Simulate
+	  |	_Contents When
+	  |	_Contents Follow
+	  |	_Contents LogicPort
+	  |	_Contents BoundBox
+	  |	_Contents Comment
+	  |	_Contents UserData
+	  ;
+
+ConnectLoc :	EDIF_TOK_CONNECTLOCATION _ConnectLoc PopC
+	   ;
+
+_ConnectLoc :
+	    |	Figure
+	    ;
+
+CornerType :	EDIF_TOK_CORNERTYPE _CornerType PopC
+	   ;
+
+_CornerType :	EDIF_TOK_EXTEND
+	    |	EDIF_TOK_ROUND
+	    |	EDIF_TOK_TRUNCATE
+	    ;
+
+Criticality :	EDIF_TOK_CRITICALITY _Criticality PopC
+	    ;
+
+_Criticality :	Int { free($1); }
+	     |	IntDisplay
+	     ;
+
+CurrentMap :	EDIF_TOK_CURRENTMAP MiNoMaValue PopC
+	   ;
+
+Curve :		EDIF_TOK_CURVE _Curve PopC
+      ;
+
+_Curve :
+       |	_Curve Arc
+       |	_Curve PointValue
+       ;
+
+Cycle :		EDIF_TOK_CYCLE Int _Cycle PopC { free($2); }
+      ;
+
+_Cycle :
+       |	Duration
+       ;
+
+DataOrigin :	EDIF_TOK_DATAORIGIN Str _DataOrigin PopC { free($2); }
+	   ;
+
+_DataOrigin :
+	    |	Version
+	    ;
+
+DcFanInLoad :	EDIF_TOK_DCFANINLOAD _DcFanInLoad PopC
+	    ;
+
+_DcFanInLoad :	ScaledInt
+	     |	NumbDisplay
+	     ;
+
+DcFanOutLoad :	EDIF_TOK_DCFANOUTLOAD _DcFanOutLoad PopC
+	     ;
+
+_DcFanOutLoad :	ScaledInt
+	      |	NumbDisplay
+	      ;
+
+DcMaxFanIn :	EDIF_TOK_DCMAXFANIN _DcMaxFanIn PopC
+	   ;
+
+_DcMaxFanIn :	ScaledInt
+	    |	NumbDisplay
+	    ;
+
+DcMaxFanOut :	EDIF_TOK_DCMAXFANOUT _DcMaxFanOut PopC
+	    ;
+
+_DcMaxFanOut :	ScaledInt
+	     |	NumbDisplay
+	     ;
+
+Delay :		EDIF_TOK_DELAY _Delay PopC
+      ;
+
+_Delay :	MiNoMaValue
+       |	MiNoMaDisp
+       ;
+
+Delta :		EDIF_TOK_DELTA _Delta PopC
+      ;
+
+_Delta :
+       |	_Delta PointValue
+       ;
+
+Derivation :	EDIF_TOK_DERIVATION _Derivation PopC
+	   ;
+
+_Derivation :	EDIF_TOK_CALCULATED
+	    |	EDIF_TOK_MEASURED
+	    |	EDIF_TOK_REQUIRED
+	    ;
+
+Design :	EDIF_TOK_DESIGN DesignNameDef _Design PopC
+       ;
+
+_Design :	CellRef
+	|	_Design Status
+	|	_Design Comment
+	|	_Design Property
+	|	_Design UserData
+	;
+
+Designator :	EDIF_TOK_DESIGNATOR _Designator PopC
+	   ;
+
+_Designator :	Str { free($1); }
+	    |	StrDisplay
+	    ;
+
+DesignNameDef :	NameDef { str_pair_free($1); }
+	      ;
+
+DesignRule :	EDIF_TOK_PHYSICALDESIGNRULE _DesignRule PopC
+	   ;
+
+_DesignRule :
+	    |	_DesignRule FigureWidth
+	    |	_DesignRule FigureArea
+	    |	_DesignRule RectSize
+	    |	_DesignRule FigurePerim
+	    |	_DesignRule OverlapDist
+	    |	_DesignRule OverhngDist
+	    |	_DesignRule EncloseDist
+	    |	_DesignRule InterFigGrp
+	    |	_DesignRule IntraFigGrp
+	    |	_DesignRule NotchSpace
+	    |	_DesignRule NotAllowed
+	    |	_DesignRule FigGrp
+	    |	_DesignRule Comment
+	    |	_DesignRule UserData
+	    ;
+
+Difference :	EDIF_TOK_DIFFERENCE _Difference PopC
+	   ;
+
+_Difference :	FigGrpRef
+	    |	FigureOp
+	    |	_Difference FigGrpRef
+	    |	_Difference FigureOp
+	    ;
+
+Direction :	EDIF_TOK_DIRECTION _Direction PopC
+	  ;
+
+_Direction :	EDIF_TOK_INOUT
+	   |	EDIF_TOK_INPUT
+	   |	EDIF_TOK_OUTPUT
+	   ;
+
+Display :	EDIF_TOK_DISPLAY _Display _DisplayJust _DisplayOrien _DisplayOrg PopC
+	;
+
+_Display :	FigGrpNameRef
+	 |	FigGrpOver
+	 ;
+
+_DisplayJust :
+	     |	Justify
+	     ;
+
+_DisplayOrien :
+	      |	Orientation
+	      ;
+
+_DisplayOrg :
+	    |	Origin
+	    ;
+
+Dominates :	EDIF_TOK_DOMINATES _Dominates PopC
+	  ;
+
+_Dominates :
+	   |	_Dominates LogicNameRef
+	   ;
+
+Dot :		EDIF_TOK_DOT _Dot PopC
+    ;
+
+_Dot :		PointValue
+     |		_Dot Property
+     ;
+
+Duration :	EDIF_TOK_DURATION ScaledInt PopC
+	 ;
+
+EncloseDist :	EDIF_TOK_ENCLOSUREDISTANCE RuleNameDef FigGrpObj FigGrpObj _EncloseDist
+		PopC
+	    ;
+
+_EncloseDist :	Range
+	     |	SingleValSet
+	     |	_EncloseDist Comment
+	     |	_EncloseDist UserData
+	     ;
+
+EndType :	EDIF_TOK_ENDTYPE _EndType PopC
+	;
+
+_EndType :	EDIF_TOK_EXTEND
+	 |	EDIF_TOK_ROUND
+	 |	EDIF_TOK_TRUNCATE
+	 ;
+
+Entry :		EDIF_TOK_ENTRY ___Entry __Entry _Entry
+		PopC
+      ;
+
+___Entry :	Match
+	 |	Change
+	 |	Steady
+	 ;
+
+__Entry :	LogicRef
+	|	PortRef { str_pair_free($1); }
+	|	NoChange
+	|	Table
+	;
+
+_Entry :
+       |	Delay
+       |	LoadDelay
+       ;
+
+Event :		EDIF_TOK_EVENT _Event PopC
+      ;
+
+_Event :	PortRef { str_pair_free($1); }
+       |	PortList
+       |	PortGroup
+       |	NetRef
+       |	NetGroup
+       |	_Event Transition
+       |	_Event Becomes
+       ;
+
+Exactly :	EDIF_TOK_EXACTLY ScaledInt PopC
+	;
+
+External :	EDIF_TOK_EXTERNAL LibNameDef EdifLevel _External PopC
+	 ;
+
+_External :	Technology
+	  |	_External Status
+	  |	_External Cell
+	  |	_External Comment
+	  |	_External UserData
+	  ;
+
+Fabricate :	EDIF_TOK_FABRICATE LayerNameDef FigGrpNameRef PopC
+	  ;
+
+False :		EDIF_TOK_FALSE PopC
+      ;
+
+FigGrp :	EDIF_TOK_FIGUREGROUP _FigGrp PopC
+       ;
+
+_FigGrp :	FigGrpNameDef
+	|	_FigGrp CornerType
+	|	_FigGrp EndType
+	|	_FigGrp PathWidth
+	|	_FigGrp BorderWidth
+	|	_FigGrp Color
+	|	_FigGrp FillPattern
+	|	_FigGrp BorderPat
+	|	_FigGrp TextHeight
+	|	_FigGrp Visible
+	|	_FigGrp Comment
+	|	_FigGrp Property
+	|	_FigGrp UserData
+	|	_FigGrp IncFigGrp
+	;
+
+FigGrpNameDef :	NameDef { str_pair_free($1); }
+	      ;
+
+FigGrpNameRef :	NameRef { free($1); }
+	      ;
+
+FigGrpObj :	EDIF_TOK_FIGUREGROUPOBJECT _FigGrpObj PopC
+	  ;
+
+_FigGrpObj :	FigGrpNameRef
+	   |	FigGrpRef
+	   |	FigureOp
+	   ;
+
+FigGrpOver :	EDIF_TOK_FIGUREGROUPOVERRIDE _FigGrpOver PopC
+	   ;
+
+_FigGrpOver :	FigGrpNameRef
+	    |	_FigGrpOver CornerType
+	    |	_FigGrpOver EndType
+	    |	_FigGrpOver PathWidth
+	    |	_FigGrpOver BorderWidth
+	    |	_FigGrpOver Color
+	    |	_FigGrpOver FillPattern
+	    |	_FigGrpOver BorderPat
+	    |	_FigGrpOver TextHeight
+	    |	_FigGrpOver Visible
+	    |	_FigGrpOver Comment
+	    |	_FigGrpOver Property
+	    |	_FigGrpOver UserData
+	    ;
+
+FigGrpRef :	EDIF_TOK_FIGUREGROUPREF FigGrpNameRef _FigGrpRef PopC
+	  ;
+
+_FigGrpRef :
+	   |	LibraryRef
+	   ;
+
+Figure :	EDIF_TOK_FIGURE _Figure PopC
+       ;
+
+_Figure :	FigGrpNameDef
+	|	FigGrpOver
+	|	_Figure Circle
+	|	_Figure Dot
+	|	_Figure OpenShape
+	|	_Figure Path
+	|	_Figure Polygon
+	|	_Figure Rectangle
+	|	_Figure Shape
+	|	_Figure Comment
+	|	_Figure UserData
+	;
+
+FigureArea :	EDIF_TOK_FIGUREAREA RuleNameDef FigGrpObj _FigureArea PopC
+	   ;
+
+_FigureArea :	Range
+	    |	SingleValSet
+	    |	_FigureArea Comment
+	    |	_FigureArea UserData
+	    ;
+
+FigureOp :	Intersection
+	 |	Union
+	 |	Difference
+	 |	Inverse
+	 |	Oversize
+	 ;
+
+FigurePerim :	EDIF_TOK_FIGUREPERIMETER RuleNameDef FigGrpObj _FigurePerim PopC
+	    ;
+
+_FigurePerim :	Range
+	     |	SingleValSet
+	     |	_FigurePerim Comment
+	     |	_FigurePerim UserData
+	     ;
+
+FigureWidth :	EDIF_TOK_FIGUREWIDTH RuleNameDef FigGrpObj _FigureWidth PopC
+	    ;
+
+_FigureWidth :	Range
+	     |	SingleValSet
+	     |	_FigureWidth Comment
+	     |	_FigureWidth UserData
+	     ;
+
+FillPattern :	EDIF_TOK_FILLPATTERN Int Int Boolean PopC { free($2); free($3); }
+	    ;
+
+Follow :	EDIF_TOK_FOLLOW __Follow _Follow PopC
+       ;
+
+__Follow :	PortNameRef
+	 |	PortRef { str_pair_free($1); }
+	 ;
+
+_Follow :	PortRef { str_pair_free($1); }
+	|	Table
+	|	_Follow Delay
+	|	_Follow LoadDelay
+	;
+
+Forbidden :	EDIF_TOK_FORBIDDENEVENT _Forbidden PopC
+	  ;
+
+_Forbidden :	TimeIntval
+	   |	_Forbidden Event
+	   ;
+
+Form :		Keyword _Form ')' { free($1); }
+     ;
+
+_Form :
+      |		_Form Int { free($2); }
+      |		_Form Str { free($2); }
+      |		_Form Ident { free($2); }
+      |		_Form Form
+      ;
+
+GlobPortRef :	EDIF_TOK_GLOBALPORTREF PortNameRef PopC
+	    ;
+
+GreaterThan :	EDIF_TOK_GREATERTHAN ScaledInt PopC
+	    ;
+
+GridMap :	EDIF_TOK_GRIDMAP ScaledInt ScaledInt PopC
+	;
+
+Ignore :	EDIF_TOK_IGNORE PopC
+       ;
+
+IncFigGrp :	EDIF_TOK_INCLUDEFIGUREGROUP _IncFigGrp PopC
+	  ;
+
+_IncFigGrp :	FigGrpRef
+	   |	FigureOp
+	   ;
+
+Initial :	EDIF_TOK_INITIAL PopC
+	;
+
+Instance :	EDIF_TOK_INSTANCE InstNameDef _Instance PopC
+	 ;
+
+_Instance :	ViewRef
+	  |	ViewList
+	  |	_Instance Transform
+	  |	_Instance ParamAssign
+	  |	_Instance PortInst
+	  |	_Instance Timing
+	  |	_Instance Designator
+	  |	_Instance Property
+	  |	_Instance Comment
+	  |	_Instance UserData
+	  ;
+
+InstanceRef :	EDIF_TOK_INSTANCEREF InstNameRef _InstanceRef PopC { $$=$2; }
+	    ;
+
+_InstanceRef :
+|	InstanceRef { free($1); }
+	     |	ViewRef
+	     ;
+
+InstBackAn :	EDIF_TOK_INSTANCEBACKANNOTATE _InstBackAn PopC
+	   ;
+
+_InstBackAn :	InstanceRef { free($1); }
+	    |	_InstBackAn Designator
+	    |	_InstBackAn Timing
+	    |	_InstBackAn Property
+	    |	_InstBackAn Comment
+	    ;
+
+InstGroup :	EDIF_TOK_INSTANCEGROUP _InstGroup PopC
+	  ;
+
+_InstGroup :
+	   |	_InstGroup InstanceRef { free($2); }
+	   ;
+
+InstMap :	EDIF_TOK_INSTANCEMAP _InstMap PopC
+	;
+
+_InstMap :
+	 |	_InstMap InstanceRef { free($2); }
+	 |	_InstMap InstGroup
+	 |	_InstMap Comment
+	 |	_InstMap UserData
+	 ;
+
+InstNameDef :	NameDef { str_pair_free($1); }
+	    |	Array
+	    ;
+
+InstNameRef :	NameRef { $$=$1; }
+	    |	Member
+	    ;
+
+IntDisplay :	EDIF_TOK_INTEGERDISPLAY _IntDisplay PopC
+	   ;
+
+_IntDisplay :	Int { free($1); }
+	    |	_IntDisplay Display
+	    ;
+
+Integer :	EDIF_TOK_INTEGER _Integer PopC
+	;
+
+_Integer :
+	 |	_Integer Int { free($2); }
+	 |	_Integer IntDisplay
+	 |	_Integer Integer
+	 ;
+
+Interface :	EDIF_TOK_INTERFACE _Interface PopC
+	  ;
+
+_Interface :
+	   |	_Interface Port
+	   |	_Interface PortBundle
+	   |	_Interface Symbol
+	   |	_Interface ProtectFrame
+	   |	_Interface ArrayRelInfo
+	   |	_Interface Parameter
+	   |	_Interface Joined { pair_list_free($2); }
+	   |	_Interface MustJoin
+	   |	_Interface WeakJoined
+	   |	_Interface Permutable
+	   |	_Interface Timing
+	   |	_Interface Simulate
+	   |	_Interface Designator
+	   |	_Interface Property
+	   |	_Interface Comment
+	   |	_Interface UserData
+	   ;
+
+InterFigGrp :	EDIF_TOK_INTERFIGUREGROUPSPACING RuleNameDef FigGrpObj FigGrpObj
+		_InterFigGrp PopC
+	    ;
+
+_InterFigGrp :	Range
+	     |	SingleValSet
+	     |	_InterFigGrp Comment
+	     |	_InterFigGrp UserData
+	     ;
+
+Intersection :	EDIF_TOK_INTERSECTION _Intersection PopC
+	     ;
+
+_Intersection :	FigGrpRef
+	      |	FigureOp
+	      |	_Intersection FigGrpRef
+	      |	_Intersection FigureOp
+	      ;
+
+IntraFigGrp :	EDIF_TOK_INTRAFIGUREGROUPSPACING RuleNameDef FigGrpObj _IntraFigGrp PopC
+	    ;
+
+_IntraFigGrp :	Range
+	     |	SingleValSet
+	     |	_IntraFigGrp Comment
+	     |	_IntraFigGrp UserData
+	     ;
+
+Inverse :	EDIF_TOK_INVERSE _Inverse PopC
+	;
+
+_Inverse :	FigGrpRef
+	 |	FigureOp
+	 ;
+
+Isolated :	EDIF_TOK_ISOLATED PopC
+	 ;
+
+Joined :	EDIF_TOK_JOINED _Joined PopC { $$ = new_pair_list($2); }
+       ;
+
+_Joined : { $$=NULL; }
+|	_Joined PortRef { $2->next = $1; $$ = $2; }
+	|	_Joined PortList
+	|	_Joined GlobPortRef
+	;
+
+Justify :	EDIF_TOK_JUSTIFY _Justify PopC
+	;
+
+_Justify :	EDIF_TOK_CENTERCENTER
+	 |	EDIF_TOK_CENTERLEFT
+	 |	EDIF_TOK_CENTERRIGHT
+	 |	EDIF_TOK_LOWERCENTER
+	 |	EDIF_TOK_LOWERLEFT
+	 |	EDIF_TOK_LOWERRIGHT
+	 |	EDIF_TOK_UPPERCENTER
+	 |	EDIF_TOK_UPPERLEFT
+	 |	EDIF_TOK_UPPERRIGHT
+	 ;
+
+KeywordDisp :	EDIF_TOK_KEYWORDDISPLAY _KeywordDisp PopC
+	    ;
+
+_KeywordDisp :	KeywordName
+	     |	_KeywordDisp Display
+	     ;
+
+KeywordLevel :	EDIF_TOK_KEYWORDLEVEL Int PopC { free($2); }
+	     ;
+
+KeywordMap :	EDIF_TOK_KEYWORDMAP _KeywordMap PopC
+	   ;
+
+_KeywordMap :	KeywordLevel
+	    |	_KeywordMap Comment
+	    ;
+
+KeywordName :	Ident { free($1); }
+	    ;
+
+LayerNameDef :	NameDef { str_pair_free($1); }
+	     ;
+
+LessThan :	EDIF_TOK_LESSTHAN ScaledInt PopC
+	 ;
+
+LibNameDef :	NameDef { str_pair_free($1); }
+	   ;
+
+LibNameRef :	NameRef { free($1); }
+	   ;
+
+Library :	EDIF_TOK_LIBRARY LibNameDef EdifLevel _Library PopC
+	;
+
+_Library :	Technology
+	 |	_Library Status
+	 |	_Library Cell
+	 |	_Library Comment
+	 |	_Library UserData
+	 ;
+
+LibraryRef :	EDIF_TOK_LIBRARYREF LibNameRef PopC
+	   ;
+
+ListOfNets :	EDIF_TOK_LISTOFNETS _ListOfNets PopC
+	   ;
+
+_ListOfNets :
+	    |	_ListOfNets Net
+	    ;
+
+ListOfPorts :	EDIF_TOK_LISTOFPORTS _ListOfPorts PopC
+	    ;
+
+_ListOfPorts :
+	     |	_ListOfPorts Port
+	     |	_ListOfPorts PortBundle
+	     ;
+
+LoadDelay :	EDIF_TOK_LOADDELAY _LoadDelay _LoadDelay PopC
+	  ;
+
+_LoadDelay :	MiNoMaValue
+	   |	MiNoMaDisp
+	   ;
+
+LogicAssn :	EDIF_TOK_LOGICASSIGN ___LogicAssn __LogicAssn _LogicAssn PopC
+	  ;
+
+___LogicAssn :	PortNameRef
+	     |	PortRef { str_pair_free($1); }
+	     ;
+
+__LogicAssn :	PortRef { str_pair_free($1); }
+	    |	LogicRef
+	    |	Table
+	    ;
+
+_LogicAssn :
+	   |	Delay
+	   |	LoadDelay
+	   ;
+
+LogicIn :	EDIF_TOK_LOGICINPUT _LogicIn PopC
+	;
+
+_LogicIn :	PortList
+	 |	PortRef { str_pair_free($1); }
+	 |	PortNameRef
+	 |	_LogicIn LogicWave
+	 ;
+
+LogicList :	EDIF_TOK_LOGICLIST _LogicList PopC
+	  ;
+
+_LogicList :
+	   |	_LogicList LogicNameRef
+	   |	_LogicList LogicOneOf
+	   |	_LogicList Ignore
+	   ;
+
+LogicMapIn :	EDIF_TOK_LOGICMAPINPUT _LogicMapIn PopC
+	   ;
+
+_LogicMapIn :
+	    |	_LogicMapIn LogicNameRef
+	    ;
+
+LogicMapOut :	EDIF_TOK_LOGICMAPOUTPUT _LogicMapOut PopC
+	    ;
+
+_LogicMapOut :
+	     |	_LogicMapOut LogicNameRef
+	     ;
+
+LogicNameDef :	NameDef { str_pair_free($1); }
+	     ;
+
+LogicNameRef :	NameRef { free($1); }
+	     ;
+
+LogicOneOf :	EDIF_TOK_LOGICONEOF _LogicOneOf PopC
+	   ;
+
+_LogicOneOf :
+	    |	_LogicOneOf LogicNameRef
+	    |	_LogicOneOf LogicList
+	    ;
+
+LogicOut :	EDIF_TOK_LOGICOUTPUT _LogicOut PopC
+	 ;
+
+_LogicOut :	PortList
+	  |	PortRef { str_pair_free($1); }
+	  |	PortNameRef
+	  |	_LogicOut LogicWave
+	  ;
+
+LogicPort :	EDIF_TOK_LOGICPORT _LogicPort PopC
+	  ;
+
+_LogicPort :	PortNameDef
+	   |	_LogicPort Property
+	   |	_LogicPort Comment
+	   |	_LogicPort UserData
+	   ;
+
+LogicRef :	EDIF_TOK_LOGICREF LogicNameRef _LogicRef PopC
+	 ;
+
+_LogicRef :
+	  |	LibraryRef
+	  ;
+
+LogicValue :	EDIF_TOK_LOGICVALUE _LogicValue PopC
+	   ;
+
+_LogicValue :	LogicNameDef
+	    |	_LogicValue VoltageMap
+	    |	_LogicValue CurrentMap
+	    |	_LogicValue BooleanMap
+	    |	_LogicValue Compound
+	    |	_LogicValue Weak
+	    |	_LogicValue Strong
+	    |	_LogicValue Dominates
+	    |	_LogicValue LogicMapOut
+	    |	_LogicValue LogicMapIn
+	    |	_LogicValue Isolated
+	    |	_LogicValue Resolves
+	    |	_LogicValue Property
+	    |	_LogicValue Comment
+	    |	_LogicValue UserData
+	    ;
+
+LogicWave :	EDIF_TOK_LOGICWAVEFORM _LogicWave PopC
+	  ;
+
+_LogicWave :
+	   |	_LogicWave LogicNameRef
+	   |	_LogicWave LogicList
+	   |	_LogicWave LogicOneOf
+	   |	_LogicWave Ignore
+	   ;
+
+Maintain :	EDIF_TOK_MAINTAIN __Maintain _Maintain PopC
+	 ;
+
+__Maintain :	PortNameRef
+	   |	PortRef { str_pair_free($1); }
+	   ;
+
+_Maintain :
+	  |	Delay
+	  |	LoadDelay
+	  ;
+
+Match :		EDIF_TOK_MATCH __Match _Match PopC
+      ;
+
+__Match :	PortNameRef
+	|	PortRef { str_pair_free($1); }
+	|	PortList
+	;
+
+_Match :	LogicNameRef
+       |	LogicList
+       |	LogicOneOf
+       ;
+
+Member :	EDIF_TOK_MEMBER NameRef _Member PopC  { free($2); }
+       ;
+
+_Member :	Int { free($1); }
+	|	_Member Int { free($2); }
+	;
+
+MiNoMa :	EDIF_TOK_MINOMAX _MiNoMa PopC
+       ;
+
+_MiNoMa :
+	|	_MiNoMa MiNoMaValue
+	|	_MiNoMa MiNoMaDisp
+	|	_MiNoMa MiNoMa
+	;
+
+MiNoMaDisp :	EDIF_TOK_MINOMAXDISPLAY _MiNoMaDisp PopC
+	   ;
+
+_MiNoMaDisp :	MiNoMaValue
+	    |	_MiNoMaDisp Display
+	    ;
+
+MiNoMaValue :	Mnm
+	    |	ScaledInt
+	    ;
+
+Mnm :		EDIF_TOK_MNM _Mnm _Mnm _Mnm PopC
+    ;
+
+_Mnm :		ScaledInt
+     |		Undefined
+     |		Unconstrained
+     ;
+
+MultValSet :	EDIF_TOK_MULTIPLEVALUESET _MultValSet PopC
+	   ;
+
+_MultValSet :
+	    |	_MultValSet RangeVector
+	    ;
+
+MustJoin :	EDIF_TOK_MUSTJOIN _MustJoin PopC
+	 ;
+
+_MustJoin :
+	  |	_MustJoin PortRef { str_pair_free($2); }
+	  |	_MustJoin PortList
+	  |	_MustJoin WeakJoined
+	  |	_MustJoin Joined { pair_list_free($2); }
+	  ;
+
+Name :		EDIF_TOK_NAME _Name PopC { $$=$2; }
+     ;
+
+_Name :		Ident { $$=$1; }
+      |		_Name Display
+      ;
+
+NameDef :	Ident { $$ = new_str_pair($1,NULL); }
+	|	Name { $$ = new_str_pair($1,NULL); }
+|	Rename { $$=$1; }
+	;
+
+NameRef :	Ident { $$=$1; }
+	|	Name { $$=$1; }
+	;
+
+Net :		EDIF_TOK_NET NetNameDef _Net PopC { define_pcb_net($2, $3); }
+    ;
+
+_Net :		Joined { $$=$1; }
+     |		_Net Criticality
+     |		_Net NetDelay
+     |		_Net Figure
+     |		_Net Net
+     |		_Net Instance
+     |		_Net CommGraph
+     |		_Net Property
+     |		_Net Comment
+     |		_Net UserData
+     ;
+
+NetBackAn :	EDIF_TOK_NETBACKANNOTATE _NetBackAn PopC
+	  ;
+
+_NetBackAn :	NetRef
+	   |	_NetBackAn NetDelay
+	   |	_NetBackAn Criticality
+	   |	_NetBackAn Property
+	   |	_NetBackAn Comment
+	   ;
+
+NetBundle :	EDIF_TOK_NETBUNDLE NetNameDef _NetBundle PopC { str_pair_free($2); }
+	  ;
+
+_NetBundle :	ListOfNets
+	   |	_NetBundle Figure
+	   |	_NetBundle CommGraph
+	   |	_NetBundle Property
+	   |	_NetBundle Comment
+	   |	_NetBundle UserData
+	   ;
+
+NetDelay :	EDIF_TOK_NETDELAY Derivation _NetDelay PopC
+	 ;
+
+_NetDelay :	Delay
+	  |	_NetDelay Transition
+	  |	_NetDelay Becomes
+	  ;
+
+NetGroup :	EDIF_TOK_NETGROUP _NetGroup PopC
+	 ;
+
+_NetGroup :
+	  |	_NetGroup NetNameRef
+	  |	_NetGroup NetRef
+	  ;
+
+NetMap :	EDIF_TOK_NETMAP _NetMap PopC
+       ;
+
+_NetMap :
+	|	_NetMap NetRef
+	|	_NetMap NetGroup
+	|	_NetMap Comment
+	|	_NetMap UserData
+	;
+
+NetNameDef :	NameDef { $$=$1; }
+|	Array { $$=NULL; }
+
+	   ;
+
+NetNameRef :	NameRef { free($1); }
+	   |	Member
+	   ;
+
+NetRef :	EDIF_TOK_NETREF NetNameRef _NetRef PopC
+       ;
+
+_NetRef :
+	|	NetRef
+	|	InstanceRef { free($1); }
+	|	ViewRef
+	;
+
+NoChange :	EDIF_TOK_NOCHANGE PopC
+	 ;
+
+NonPermut :	EDIF_TOK_NONPERMUTABLE _NonPermut PopC
+	  ;
+
+_NonPermut :
+	   |	_NonPermut PortRef { str_pair_free($2); }
+	   |	_NonPermut Permutable
+	   ;
+
+NotAllowed :	EDIF_TOK_NOTALLOWED RuleNameDef _NotAllowed PopC
+	   ;
+
+_NotAllowed :	FigGrpObj
+	    |	_NotAllowed Comment
+	    |	_NotAllowed UserData
+	    ;
+
+NotchSpace :	EDIF_TOK_NOTCHSPACING RuleNameDef FigGrpObj _NotchSpace PopC
+	   ;
+
+_NotchSpace :	Range
+	    |	SingleValSet
+	    |	_NotchSpace Comment
+	    |	_NotchSpace UserData
+	    ;
+
+Number :	EDIF_TOK_NUMBER _Number PopC
+       ;
+
+_Number :
+	|	_Number ScaledInt
+	|	_Number NumbDisplay
+	|	_Number Number
+	;
+
+NumbDisplay :	EDIF_TOK_NUMBERDISPLAY _NumbDisplay PopC
+	    ;
+
+_NumbDisplay :	ScaledInt
+	     |	_NumbDisplay Display
+	     ;
+
+NumberDefn :	EDIF_TOK_NUMBERDEFINITION _NumberDefn PopC
+	   ;
+
+_NumberDefn :
+	    |	_NumberDefn Scale
+	    |	_NumberDefn GridMap
+	    |	_NumberDefn Comment
+	    ;
+
+OffPageConn :	EDIF_TOK_OFFPAGECONNECTOR _OffPageConn PopC
+	    ;
+
+_OffPageConn :	PortNameDef
+	     |	_OffPageConn Unused
+	     |	_OffPageConn Property
+	     |	_OffPageConn Comment
+	     |	_OffPageConn UserData
+	     ;
+
+OffsetEvent :	EDIF_TOK_OFFSETEVENT Event ScaledInt PopC
+	    ;
+
+OpenShape :	EDIF_TOK_OPENSHAPE _OpenShape PopC
+	  ;
+
+_OpenShape :	Curve
+	   |	_OpenShape Property
+	   ;
+
+Orientation :	EDIF_TOK_ORIENTATION _Orientation PopC
+	    ;
+
+_Orientation :	EDIF_TOK_R0
+	     |	EDIF_TOK_R90
+	     |	EDIF_TOK_R180
+	     |	EDIF_TOK_R270
+	     |	EDIF_TOK_MX
+	     |	EDIF_TOK_MY
+	     |	EDIF_TOK_MYR90
+	     |	EDIF_TOK_MXR90
+	     ;
+
+Origin :	EDIF_TOK_ORIGIN PointValue PopC
+       ;
+
+OverhngDist :	EDIF_TOK_OVERHANGDISTANCE RuleNameDef FigGrpObj FigGrpObj _OverhngDist
+		PopC
+	    ;
+
+_OverhngDist :	Range
+	     |	SingleValSet
+	     |	_OverhngDist Comment
+	     |	_OverhngDist UserData
+	     ;
+
+OverlapDist :	EDIF_TOK_OVERLAPDISTANCE RuleNameDef FigGrpObj FigGrpObj _OverlapDist
+		PopC
+	    ;
+
+_OverlapDist :	Range
+	     |	SingleValSet
+	     |	_OverlapDist Comment
+	     |	_OverlapDist UserData
+	     ;
+
+Oversize :	EDIF_TOK_OVERSIZE Int _Oversize CornerType PopC { free($2); }
+	 ;
+
+_Oversize :	FigGrpRef
+	  |	FigureOp
+	  ;
+
+Owner :		EDIF_TOK_OWNER Str PopC { free($2); }
+      ;
+
+Page :		EDIF_TOK_PAGE _Page PopC
+     ;
+
+_Page :		InstNameDef
+      |		_Page Instance
+      |		_Page Net
+      |		_Page NetBundle
+      |		_Page CommGraph
+      |		_Page PortImpl
+      |		_Page PageSize
+      |		_Page BoundBox
+      |		_Page Comment
+      |		_Page UserData
+      ;
+
+PageSize :	EDIF_TOK_PAGESIZE Rectangle PopC
+	 ;
+
+ParamDisp :	EDIF_TOK_PARAMETERDISPLAY _ParamDisp PopC
+	  ;
+
+_ParamDisp :	ValueNameRef
+	   |	_ParamDisp Display
+	   ;
+
+Parameter :	EDIF_TOK_PARAMETER ValueNameDef TypedValue _Parameter PopC
+	  ;
+
+_Parameter :
+	   |	Unit
+	   ;
+
+ParamAssign :	EDIF_TOK_PARAMETERASSIGN ValueNameRef TypedValue PopC
+	    ;
+
+Path :		EDIF_TOK_PATH _Path PopC
+     ;
+
+_Path :		PointList
+      |		_Path Property
+      ;
+
+PathDelay :	EDIF_TOK_PATHDELAY _PathDelay PopC
+	  ;
+
+_PathDelay :	Delay
+	   |	_PathDelay Event
+	   ;
+
+PathWidth :	EDIF_TOK_PATHWIDTH Int PopC { free($2); }
+	  ;
+
+Permutable :	EDIF_TOK_PERMUTABLE _Permutable PopC
+	   ;
+
+_Permutable :
+	    |	_Permutable PortRef { str_pair_free($2); }
+	    |	_Permutable Permutable
+	    |	_Permutable NonPermut
+	    ;
+
+Plug :		EDIF_TOK_PLUG _Plug PopC
+     ;
+
+_Plug :
+      |		_Plug SocketSet
+      ;
+
+Point :		EDIF_TOK_POINT _Point PopC
+      ;
+
+_Point :
+       |	_Point PointValue
+       |	_Point PointDisp
+       |	_Point Point
+       ;
+
+PointDisp :	EDIF_TOK_POINTDISPLAY _PointDisp PopC
+	  ;
+
+_PointDisp :	PointValue
+	   |	_PointDisp Display
+	   ;
+
+PointList :	EDIF_TOK_POINTLIST _PointList PopC
+	  ;
+
+_PointList :
+	   |	_PointList PointValue
+	   ;
+
+PointValue : 	EDIF_TOK_PT Int Int PopC { free($2); free($3); }
+	   ;
+
+Polygon :	EDIF_TOK_POLYGON _Polygon PopC
+	;
+
+_Polygon :	PointList
+	 |	_Polygon Property
+	 ;
+
+Port :		EDIF_TOK_PORT _Port PopC
+     ;
+
+_Port :		PortNameDef
+      |		_Port Direction
+      |		_Port Unused
+      |		_Port PortDelay
+      |		_Port Designator
+      |		_Port DcFanInLoad
+      |		_Port DcFanOutLoad
+      |		_Port DcMaxFanIn
+      |		_Port DcMaxFanOut
+      |		_Port AcLoad
+      |		_Port Property
+      |		_Port Comment
+      |		_Port UserData
+      ;
+
+PortBackAn :	EDIF_TOK_PORTBACKANNOTATE _PortBackAn PopC
+	   ;
+
+_PortBackAn :	PortRef { str_pair_free($1); }
+	    |	_PortBackAn Designator
+	    |	_PortBackAn PortDelay
+	    |	_PortBackAn DcFanInLoad
+	    |	_PortBackAn DcFanOutLoad
+	    |	_PortBackAn DcMaxFanIn
+	    |	_PortBackAn DcMaxFanOut
+	    |	_PortBackAn AcLoad
+	    |	_PortBackAn Property
+	    |	_PortBackAn Comment
+	    ;
+
+PortBundle :	EDIF_TOK_PORTBUNDLE PortNameDef _PortBundle PopC
+	   ;
+
+_PortBundle :	ListOfPorts
+	    |	_PortBundle Property
+	    |	_PortBundle Comment
+	    |	_PortBundle UserData
+	    ;
+
+PortDelay :	EDIF_TOK_PORTDELAY Derivation _PortDelay PopC
+	  ;
+
+_PortDelay :	Delay
+	   |	LoadDelay
+	   |	_PortDelay Transition
+	   |	_PortDelay Becomes
+	   ;
+
+PortGroup :	EDIF_TOK_PORTGROUP _PortGroup PopC
+	  ;
+
+_PortGroup :
+	   |	_PortGroup PortNameRef
+	   |	_PortGroup PortRef { str_pair_free($2); }
+	   ;
+
+PortImpl :	EDIF_TOK_PORTIMPLEMENTATION _PortImpl PopC
+	 ;
+
+_PortImpl :	PortRef { str_pair_free($1); }
+	  |	PortNameRef
+	  |	_PortImpl ConnectLoc
+	  |	_PortImpl Figure
+	  |	_PortImpl Instance
+	  |	_PortImpl CommGraph
+	  |	_PortImpl PropDisplay
+	  |	_PortImpl KeywordDisp
+	  |	_PortImpl Property
+	  |	_PortImpl UserData
+	  |	_PortImpl Comment
+	  ;
+
+PortInst :	EDIF_TOK_PORTINSTANCE _PortInst PopC
+	 ;
+
+_PortInst :	PortRef { str_pair_free($1); }
+	  |	PortNameRef
+	  |	_PortInst Unused
+	  |	_PortInst PortDelay
+	  |	_PortInst Designator
+	  |	_PortInst DcFanInLoad
+	  |	_PortInst DcFanOutLoad
+	  |	_PortInst DcMaxFanIn
+	  |	_PortInst DcMaxFanOut
+	  |	_PortInst AcLoad
+	  |	_PortInst Property
+	  |	_PortInst Comment
+	  |	_PortInst UserData
+	  ;
+
+PortList :	EDIF_TOK_PORTLIST _PortList PopC
+	 ;
+
+_PortList :
+	  |	_PortList PortRef { str_pair_free($2); }
+	  |	_PortList PortNameRef
+	  ;
+
+PortListAls :	EDIF_TOK_PORTLISTALIAS PortNameDef PortList PopC
+	    ;
+
+PortMap :	EDIF_TOK_PORTMAP _PortMap PopC
+	;
+
+_PortMap :
+	 |	_PortMap PortRef { str_pair_free($2); }
+	 |	_PortMap PortGroup
+	 |	_PortMap Comment
+	 |	_PortMap UserData
+	 ;
+
+PortNameDef :	NameDef { str_pair_free($1); }
+	    |	Array
+	    ;
+
+PortNameRef :	NameRef { $$=$1; }
+	    |	Member
+	    ;
+
+PortRef :	EDIF_TOK_PORTREF PortNameRef _PortRef PopC
+{
+    if ($3)
+    {
+	$$ = new_str_pair($3->str1,$2);
+	free($3);
+    }
+    else
+    {
+	/* handle port with no instance by passing up the chain */
+	$$ = new_str_pair(NULL,$2);
+    }
+}
+	;
+
+_PortRef : { $$=NULL; }
+	 |	PortRef { $$=$1; }
+	 |	InstanceRef { $$ = new_str_pair($1,NULL); }
+	 |	ViewRef { $$=NULL; }
+	 ;
+
+Program :	EDIF_TOK_PROGRAM Str _Program PopC
+	;
+
+_Program :
+	 |	Version
+	 ;
+
+PropDisplay :	EDIF_TOK_PROPERTYDISPLAY _PropDisplay PopC
+	    ;
+
+_PropDisplay :	PropNameRef
+	     |	_PropDisplay Display
+	     ;
+
+Property :	EDIF_TOK_PROPERTY PropNameDef _Property PopC
+	 ;
+
+_Property :	TypedValue
+	  |	_Property Owner
+	  |	_Property Unit
+	  |	_Property Property
+	  |	_Property Comment
+	  ;
+
+PropNameDef :	NameDef { str_pair_free($1); }
+	    ;
+
+PropNameRef :	NameRef { free($1); }
+	    ;
+
+ProtectFrame :	EDIF_TOK_PROTECTIONFRAME _ProtectFrame PopC
+	     ;
+
+_ProtectFrame :
+	      |	_ProtectFrame PortImpl
+	      |	_ProtectFrame Figure
+	      |	_ProtectFrame Instance
+	      |	_ProtectFrame CommGraph
+	      |	_ProtectFrame BoundBox
+	      |	_ProtectFrame PropDisplay
+	      |	_ProtectFrame KeywordDisp
+	      |	_ProtectFrame ParamDisp
+	      |	_ProtectFrame Property
+	      |	_ProtectFrame Comment
+	      |	_ProtectFrame UserData
+	      ;
+
+Range :		LessThan
+      |		GreaterThan
+      |		AtMost
+      |		AtLeast
+      |		Exactly
+      |		Between
+      ;
+
+RangeVector :	EDIF_TOK_RANGEVECTOR _RangeVector PopC
+	    ;
+
+_RangeVector :
+	     |	_RangeVector Range
+	     |	_RangeVector SingleValSet
+	     ;
+
+Rectangle :	EDIF_TOK_RECTANGLE PointValue _Rectangle PopC
+	  ;
+
+_Rectangle :	PointValue
+	   |	_Rectangle Property
+	   ;
+
+RectSize :	EDIF_TOK_RECTANGLESIZE RuleNameDef FigGrpObj _RectSize PopC
+	 ;
+
+_RectSize :	RangeVector
+	  |	MultValSet
+	  |	_RectSize Comment
+	  |	_RectSize UserData
+	  ;
+
+Rename :	EDIF_TOK_RENAME __Rename _Rename PopC
+{ $$ = new_str_pair($2,$3); }
+       ;
+
+__Rename :	Ident { $$=$1; }
+	 |	Name { $$=$1; }
+	 ;
+
+_Rename :	Str { $$=$1; }
+	|	StrDisplay { $$=NULL; }
+	;
+
+Resolves :	EDIF_TOK_RESOLVES _Resolves PopC
+	 ;
+
+_Resolves :
+	  |	_Resolves LogicNameRef
+	  ;
+
+RuleNameDef :	NameDef { str_pair_free($1); }
+	    ;
+
+Scale :		EDIF_TOK_SCALE ScaledInt ScaledInt Unit PopC
+      ;
+
+ScaledInt :	Int { free($1); }
+	  |	EDIF_TOK_E Int Int PopC { free($2); free($3); }
+	  ;
+
+ScaleX :	EDIF_TOK_SCALEX Int Int PopC { free($2); free($3); }
+       ;
+
+ScaleY :	EDIF_TOK_SCALEY Int Int PopC { free($2); free($3); }
+       ;
+
+Section :	EDIF_TOK_SECTION _Section PopC
+	;
+
+_Section :	Str { free($1); }
+	 |	_Section Section
+	 |	_Section Str { free($2); }
+	 |	_Section Instance
+	 ;
+
+Shape :		EDIF_TOK_SHAPE _Shape PopC
+      ;
+
+_Shape :	Curve
+       |	_Shape Property
+       ;
+
+SimNameDef :	NameDef { str_pair_free($1); }
+	   ;
+
+Simulate :	EDIF_TOK_SIMULATE _Simulate PopC
+	 ;
+
+_Simulate :	SimNameDef
+	  |	_Simulate PortListAls
+	  |	_Simulate WaveValue
+	  |	_Simulate Apply
+	  |	_Simulate Comment
+	  |	_Simulate UserData
+	  ;
+
+SimulInfo :	EDIF_TOK_SIMULATIONINFO _SimulInfo PopC
+	  ;
+
+_SimulInfo :
+	   |	_SimulInfo LogicValue
+	   |	_SimulInfo Comment
+	   |	_SimulInfo UserData
+	   ;
+
+SingleValSet :	EDIF_TOK_SINGLEVALUESET _SingleValSet PopC
+	     ;
+
+_SingleValSet :
+	      |	Range
+	      ;
+
+Site :		EDIF_TOK_SITE ViewRef _Site PopC
+     ;
+
+_Site :
+      |		Transform
+      ;
+
+Socket :	EDIF_TOK_SOCKET _Socket PopC
+       ;
+
+_Socket :
+	|	Symmetry
+	;
+
+SocketSet :	EDIF_TOK_SOCKETSET _SocketSet PopC
+	  ;
+
+_SocketSet :	Symmetry
+	   |	_SocketSet Site
+	   ;
+
+Status :	EDIF_TOK_STATUS _Status PopC
+       ;
+
+_Status :
+	|	_Status Written
+	|	_Status Comment
+	|	_Status UserData
+	;
+
+Steady :	EDIF_TOK_STEADY __Steady _Steady PopC
+       ;
+
+__Steady :	PortNameRef
+	 |	PortRef { str_pair_free($1); }
+	 |	PortList
+	 ;
+
+_Steady :	Duration
+	|	_Steady Transition
+	|	_Steady Becomes
+	;
+
+StrDisplay :	EDIF_TOK_STRINGDISPLAY _StrDisplay PopC
+	   ;
+
+String :	EDIF_TOK_STRING _String PopC
+       ;
+
+_String :
+	|	_String Str { free($2); }
+	|	_String StrDisplay
+	|	_String String
+	;
+
+_StrDisplay :	Str { free($1); }
+	    |	_StrDisplay Display
+	    ;
+
+Strong :	EDIF_TOK_STRONG LogicNameRef PopC
+       ;
+
+Symbol :	EDIF_TOK_SYMBOL _Symbol PopC
+       ;
+
+_Symbol :
+	|	_Symbol PortImpl
+	|	_Symbol Figure
+	|	_Symbol Instance
+	|	_Symbol CommGraph
+	|	_Symbol Annotate
+	|	_Symbol PageSize
+	|	_Symbol BoundBox
+	|	_Symbol PropDisplay
+	|	_Symbol KeywordDisp
+	|	_Symbol ParamDisp
+	|	_Symbol Property
+	|	_Symbol Comment
+	|	_Symbol UserData
+	;
+
+Symmetry :	EDIF_TOK_SYMMETRY _Symmetry PopC
+	 ;
+
+_Symmetry :
+	  |	_Symmetry Transform
+	  ;
+
+Table :		EDIF_TOK_TABLE _Table PopC
+      ;
+
+_Table :
+       |	_Table Entry
+       |	_Table TableDeflt
+       ;
+
+TableDeflt :	EDIF_TOK_TABLEDEFAULT __TableDeflt _TableDeflt PopC
+	   ;
+
+__TableDeflt :	LogicRef
+	     |	PortRef { str_pair_free($1); }
+	     |	NoChange
+	     |	Table
+	     ;
+
+_TableDeflt :
+	    |	Delay
+	    |	LoadDelay
+	    ;
+
+Technology :	EDIF_TOK_TECHNOLOGY _Technology PopC
+	   ;
+
+_Technology :	NumberDefn
+	    |	_Technology FigGrp
+	    |	_Technology Fabricate
+	    |	_Technology SimulInfo
+	    |	_Technology DesignRule
+	    |	_Technology Comment
+	    |	_Technology UserData
+	    ;
+
+TextHeight :	EDIF_TOK_TEXTHEIGHT Int PopC { free($2); }
+	   ;
+
+TimeIntval :	EDIF_TOK_TIMEINTERVAL __TimeIntval _TimeIntval PopC
+	   ;
+
+__TimeIntval :	Event
+	     |	OffsetEvent
+	     ;
+
+_TimeIntval :	Event
+	    |	OffsetEvent
+	    |	Duration
+	    ;
+
+TimeStamp :	EDIF_TOK_TIMESTAMP Int Int Int Int Int Int PopC
+{ free($2); free($3); free($4); free($5); free($6); free($7); }
+	  ;
+
+Timing :	EDIF_TOK_TIMING _Timing PopC
+       ;
+
+_Timing :	Derivation
+	|	_Timing PathDelay
+	|	_Timing Forbidden
+	|	_Timing Comment
+	|	_Timing UserData
+	;
+
+Transform :	EDIF_TOK_TRANSFORM _TransX _TransY _TransDelta _TransOrien _TransOrg
+		PopC
+	  ;
+
+_TransX :
+	|	ScaleX
+	;
+
+_TransY :
+	|	ScaleY
+	;
+
+_TransDelta :
+	    |	Delta
+	    ;
+
+_TransOrien :
+	    |	Orientation
+	    ;
+
+_TransOrg :
+	  |	Origin
+	  ;
+
+Transition :	EDIF_TOK_TRANSITION _Transition _Transition PopC
+	   ;
+
+_Transition :	LogicNameRef
+	    |	LogicList
+	    |	LogicOneOf
+	    ;
+
+Trigger :	EDIF_TOK_TRIGGER _Trigger PopC
+	;
+
+_Trigger :
+	 |	_Trigger Change
+	 |	_Trigger Steady
+	 |	_Trigger Initial
+	 ;
+
+True :		EDIF_TOK_TRUE PopC
+     ;
+
+TypedValue :	Boolean
+	   |	Integer
+	   |	MiNoMa
+	   |	Number
+	   |	Point
+	   |	String
+	   ;
+
+Unconstrained :	EDIF_TOK_UNCONSTRAINED PopC
+	      ;
+
+Undefined :	EDIF_TOK_UNDEFINED PopC
+	  ;
+
+Union :		EDIF_TOK_UNION _Union PopC
+      ;
+
+_Union :	FigGrpRef
+       |	FigureOp
+       |	_Union FigGrpRef
+       |	_Union FigureOp
+       ;
+
+Unit :		EDIF_TOK_UNIT _Unit PopC
+     ;
+
+_Unit :		EDIF_TOK_DISTANCE
+      |		EDIF_TOK_CAPACITANCE
+      |		EDIF_TOK_CURRENT
+      |		EDIF_TOK_RESISTANCE
+      |		EDIF_TOK_TEMPERATURE
+      |		EDIF_TOK_TIME
+      |		EDIF_TOK_VOLTAGE
+      |		EDIF_TOK_MASS
+      |		EDIF_TOK_FREQUENCY
+      |		EDIF_TOK_INDUCTANCE
+      |		EDIF_TOK_ENERGY
+      |		EDIF_TOK_POWER
+      |		EDIF_TOK_CHARGE
+      |		EDIF_TOK_CONDUCTANCE
+      |		EDIF_TOK_FLUX
+      |		EDIF_TOK_ANGLE
+      ;
+
+Unused :	EDIF_TOK_UNUSED PopC
+       ;
+
+UserData :	EDIF_TOK_USERDATA _UserData PopC
+	 ;
+
+_UserData :	Ident { free($1); }
+	  |	_UserData Int { free($2); }
+	  |	_UserData Str { free($2); }
+	  |	_UserData Ident { free($2); }
+	  |	_UserData Form
+	  ;
+
+ValueNameDef :	NameDef { str_pair_free($1); }
+	     |	Array
+	     ;
+
+ValueNameRef :	NameRef { free($1); }
+	     |	Member
+	     ;
+
+Version :	EDIF_TOK_VERSION Str PopC { free($2); }
+	;
+
+View :		EDIF_TOK_VIEW ViewNameDef ViewType _View PopC
+     ;
+
+_View :		Interface
+      |		_View Status
+      |		_View Contents
+      |		_View Comment
+      |		_View Property
+      |		_View UserData
+      ;
+
+ViewList :	EDIF_TOK_VIEWLIST _ViewList PopC
+	 ;
+
+_ViewList :
+	  |	_ViewList ViewRef
+	  |	_ViewList ViewList
+	  ;
+
+ViewMap :	EDIF_TOK_VIEWMAP _ViewMap PopC
+	;
+
+_ViewMap :
+	 |	_ViewMap PortMap
+	 |	_ViewMap PortBackAn
+	 |	_ViewMap InstMap
+	 |	_ViewMap InstBackAn
+	 |	_ViewMap NetMap
+	 |	_ViewMap NetBackAn
+	 |	_ViewMap Comment
+	 |	_ViewMap UserData
+	 ;
+
+ViewNameDef :	NameDef { str_pair_free($1); }
+	    ;
+
+ViewNameRef :	NameRef { free($1); }
+	    ;
+
+ViewRef :	EDIF_TOK_VIEWREF ViewNameRef _ViewRef PopC
+	;
+
+_ViewRef :
+	 |	CellRef
+	 ;
+
+ViewType :	EDIF_TOK_VIEWTYPE _ViewType PopC
+	 ;
+
+_ViewType :	EDIF_TOK_MASKLAYOUT
+	  |	EDIF_TOK_PCBLAYOUT
+	  |	EDIF_TOK_NETLIST
+	  |	EDIF_TOK_SCHEMATIC
+	  |	EDIF_TOK_SYMBOLIC
+	  |	EDIF_TOK_BEHAVIOR
+	  |	EDIF_TOK_LOGICMODEL
+	  |	EDIF_TOK_DOCUMENT
+	  |	EDIF_TOK_GRAPHIC
+	  |	EDIF_TOK_STRANGER
+	  ;
+
+Visible :	EDIF_TOK_VISIBLE BooleanValue PopC
+	;
+
+VoltageMap :	EDIF_TOK_VOLTAGEMAP MiNoMaValue PopC
+	   ;
+
+WaveValue :	EDIF_TOK_WAVEVALUE LogicNameDef ScaledInt LogicWave PopC
+	  ;
+
+Weak :		EDIF_TOK_WEAK LogicNameRef PopC
+     ;
+
+WeakJoined :	EDIF_TOK_WEAKJOINED _WeakJoined PopC
+	   ;
+
+_WeakJoined :
+	    |	_WeakJoined PortRef { str_pair_free($2); }
+	    |	_WeakJoined PortList
+	    |	_WeakJoined Joined { pair_list_free($2); }
+	    ;
+
+When :		EDIF_TOK_WHEN _When PopC
+     ;
+
+_When :		Trigger
+      |		_When After
+      |		_When Follow
+      |		_When Maintain
+      |		_When LogicAssn
+      |		_When Comment
+      |		_When UserData
+      ;
+
+Written :	EDIF_TOK_WRITTEN _Written PopC
+	;
+
+_Written :	TimeStamp
+	 |	_Written Author
+	 |	_Written Program
+	 |	_Written DataOrigin
+	 |	_Written Property
+	 |	_Written Comment
+	 |	_Written UserData
+	 ;
+
+Ident :		EDIF_TOK_IDENT { $$=$1; }
+      ;
+
+Str :		EDIF_TOK_STR { $$=$1; }
+    ;
+
+Int :		EDIF_TOK_INT { $$=$1; }
+    ;
+
+Keyword :	EDIF_TOK_KEYWORD	{ $$=$1; }
+	;
+
+%%
+/*
+ *	xmalloc:
+ *
+ *	  Garbage function for 'alloca()'.
+ */
+char *xmalloc(int siz)
+{
+  return ((char *)Malloc(siz));
+}
+/*
+ *	Token & context carriers:
+ *
+ *	  These are the linkage pointers for threading this context garbage
+ *	for converting identifiers into parser tokens.
+ */
+typedef struct TokenCar {
+  struct TokenCar *Next;	/* pointer to next carrier */
+  struct Token *Token;		/* associated token */
+} TokenCar;
+typedef struct UsedCar {
+  struct UsedCar *Next;		/* pointer to next carrier */
+  short Code;			/* used '%token' value */
+} UsedCar;
+typedef struct ContextCar {
+  struct ContextCar *Next;	/* pointer to next carrier */
+  struct Context *Context;	/* associated context */
+  union {
+    int Single;			/* single usage flag (context tree) */
+    struct UsedCar *Used;	/* single used list (context stack) */
+  } u;
+} ContextCar;
+/*
+ *	Token definitions:
+ *
+ *	  This associates the '%token' codings with strings which are to
+ *	be free standing tokens. Doesn't have to be in sorted order but the
+ *	strings must be in lower case.
+ */
+typedef struct Token {
+  const char *Name;			/* token name */
+  int Code;			/* '%token' value */
+  struct Token *Next;		/* hash table linkage */
+} Token;
+static Token TokenDef[] = {
+  {"angle",		EDIF_TOK_ANGLE},
+  {"behavior",		EDIF_TOK_BEHAVIOR},
+  {"calculated",	EDIF_TOK_CALCULATED},
+  {"capacitance",	EDIF_TOK_CAPACITANCE},
+  {"centercenter",	EDIF_TOK_CENTERCENTER},
+  {"centerleft",	EDIF_TOK_CENTERLEFT},
+  {"centerright",	EDIF_TOK_CENTERRIGHT},
+  {"charge",		EDIF_TOK_CHARGE},
+  {"conductance",	EDIF_TOK_CONDUCTANCE},
+  {"current",		EDIF_TOK_CURRENT},
+  {"distance",		EDIF_TOK_DISTANCE},
+  {"document",		EDIF_TOK_DOCUMENT},
+  {"energy",		EDIF_TOK_ENERGY},
+  {"extend",		EDIF_TOK_EXTEND},
+  {"flux",		EDIF_TOK_FLUX},
+  {"frequency",		EDIF_TOK_FREQUENCY},
+  {"generic",		EDIF_TOK_GENERIC},
+  {"graphic",		EDIF_TOK_GRAPHIC},
+  {"inductance",	EDIF_TOK_INDUCTANCE},
+  {"inout",		EDIF_TOK_INOUT},
+  {"input",		EDIF_TOK_INPUT},
+  {"logicmodel",	EDIF_TOK_LOGICMODEL},
+  {"lowercenter",	EDIF_TOK_LOWERCENTER},
+  {"lowerleft",		EDIF_TOK_LOWERLEFT},
+  {"lowerright",	EDIF_TOK_LOWERRIGHT},
+  {"masklayout",	EDIF_TOK_MASKLAYOUT},
+  {"mass",		EDIF_TOK_MASS},
+  {"measured",		EDIF_TOK_MEASURED},
+  {"mx",		EDIF_TOK_MX},
+  {"mxr90",		EDIF_TOK_MXR90},
+  {"my",		EDIF_TOK_MY},
+  {"myr90",		EDIF_TOK_MYR90},
+  {"netlist",		EDIF_TOK_NETLIST},
+  {"output",		EDIF_TOK_OUTPUT},
+  {"pcblayout",		EDIF_TOK_PCBLAYOUT},
+  {"power",		EDIF_TOK_POWER},
+  {"r0",		EDIF_TOK_R0},
+  {"r180",		EDIF_TOK_R180},
+  {"r270",		EDIF_TOK_R270},
+  {"r90",		EDIF_TOK_R90},
+  {"required",		EDIF_TOK_REQUIRED},
+  {"resistance",	EDIF_TOK_RESISTANCE},
+  {"ripper",		EDIF_TOK_RIPPER},
+  {"round",		EDIF_TOK_ROUND},
+  {"schematic",		EDIF_TOK_SCHEMATIC},
+  {"stranger",		EDIF_TOK_STRANGER},
+  {"symbolic",		EDIF_TOK_SYMBOLIC},
+  {"temperature",	EDIF_TOK_TEMPERATURE},
+  {"tie",		EDIF_TOK_TIE},
+  {"time",		EDIF_TOK_TIME},
+  {"truncate",		EDIF_TOK_TRUNCATE},
+  {"uppercenter",	EDIF_TOK_UPPERCENTER},
+  {"upperleft",		EDIF_TOK_UPPERLEFT},
+  {"upperright",	EDIF_TOK_UPPERRIGHT},
+  {"voltage",		EDIF_TOK_VOLTAGE}
+};
+static int TokenDefSize = sizeof(TokenDef) / sizeof(Token);
+/*
+ *	Token enable definitions:
+ *
+ *	  There is one array for each set of tokens enabled by a
+ *	particular context (barf). Another array is used to bind
+ *	these arrays to a context.
+ */
+static short e_CellType[] = {EDIF_TOK_TIE, EDIF_TOK_RIPPER, EDIF_TOK_GENERIC};
+static short e_CornerType[] = {EDIF_TOK_EXTEND, EDIF_TOK_TRUNCATE,
+			       EDIF_TOK_ROUND};
+static short e_Derivation[] = {EDIF_TOK_CALCULATED, EDIF_TOK_MEASURED,
+			       EDIF_TOK_REQUIRED};
+static short e_Direction[] = {EDIF_TOK_INPUT, EDIF_TOK_OUTPUT,
+			      EDIF_TOK_INOUT};
+static short e_EndType[] = {EDIF_TOK_EXTEND, EDIF_TOK_TRUNCATE,
+			    EDIF_TOK_ROUND};
+static short e_Justify[] = {EDIF_TOK_CENTERCENTER, EDIF_TOK_CENTERLEFT,
+			    EDIF_TOK_CENTERRIGHT, EDIF_TOK_LOWERCENTER,
+			    EDIF_TOK_LOWERLEFT, EDIF_TOK_LOWERRIGHT,
+			    EDIF_TOK_UPPERCENTER, EDIF_TOK_UPPERLEFT,
+			    EDIF_TOK_UPPERRIGHT};
+static short e_Orientation[] = {EDIF_TOK_R0, EDIF_TOK_R90, EDIF_TOK_R180,
+				EDIF_TOK_R270, EDIF_TOK_MX, EDIF_TOK_MY,
+				EDIF_TOK_MXR90, EDIF_TOK_MYR90};
+static short e_Unit[] = {EDIF_TOK_DISTANCE, EDIF_TOK_CAPACITANCE,
+			 EDIF_TOK_CURRENT, EDIF_TOK_RESISTANCE,
+			 EDIF_TOK_TEMPERATURE, EDIF_TOK_TIME,
+			 EDIF_TOK_VOLTAGE, EDIF_TOK_MASS, EDIF_TOK_FREQUENCY,
+			 EDIF_TOK_INDUCTANCE, EDIF_TOK_ENERGY,
+			 EDIF_TOK_POWER, EDIF_TOK_CHARGE,
+			 EDIF_TOK_CONDUCTANCE, EDIF_TOK_FLUX, EDIF_TOK_ANGLE};
+static short e_ViewType[] = {EDIF_TOK_MASKLAYOUT, EDIF_TOK_PCBLAYOUT,
+			     EDIF_TOK_NETLIST, EDIF_TOK_SCHEMATIC,
+			     EDIF_TOK_SYMBOLIC, EDIF_TOK_BEHAVIOR,
+			     EDIF_TOK_LOGICMODEL, EDIF_TOK_DOCUMENT,
+			     EDIF_TOK_GRAPHIC, EDIF_TOK_STRANGER};
+/*
+ *	Token tying table:
+ *
+ *	  This binds enabled tokens to a context.
+ */
+typedef struct Tie {
+  short *Enable;		/* pointer to enable array */
+  short Origin;			/* '%token' value of context */
+  short EnableSize;		/* size of enabled array */
+} Tie;
+#define	TE(e,o)			{e,o,sizeof(e)/sizeof(short)}
+static Tie TieDef[] = {
+  TE(e_CellType,	EDIF_TOK_CELLTYPE),
+  TE(e_CornerType,	EDIF_TOK_CORNERTYPE),
+  TE(e_Derivation,	EDIF_TOK_DERIVATION),
+  TE(e_Direction,	EDIF_TOK_DIRECTION),
+  TE(e_EndType,		EDIF_TOK_ENDTYPE),
+  TE(e_Justify,		EDIF_TOK_JUSTIFY),
+  TE(e_Orientation,	EDIF_TOK_ORIENTATION),
+  TE(e_Unit,		EDIF_TOK_UNIT),
+  TE(e_ViewType,	EDIF_TOK_VIEWTYPE)
+};
+static int TieDefSize = sizeof(TieDef) / sizeof(Tie);
+/*
+ *	Context definitions:
+ *
+ *	  This associates keyword strings with '%token' values. It
+ *	also creates a pretty much empty header for later building of
+ *	the context tree. Again they needn't be sorted, but strings
+ *	must be lower case.
+ */
+typedef struct Context {
+  const char *Name;			/* keyword name */
+  short Code;			/* '%token' value */
+  short Flags;			/* special operation flags */
+  struct ContextCar *Context;	/* contexts which can be moved to */
+  struct TokenCar *Token;	/* active tokens */
+  struct Context *Next;		/* hash table linkage */
+} Context;
+static Context ContextDef[] = {
+  {"",				0},		/* start context */
+  {"acload",			EDIF_TOK_ACLOAD},
+  {"after",			EDIF_TOK_AFTER},
+  {"annotate",			EDIF_TOK_ANNOTATE},
+  {"apply",			EDIF_TOK_APPLY},
+  {"arc",			EDIF_TOK_ARC},
+  {"array",			EDIF_TOK_ARRAY},
+  {"arraymacro",		EDIF_TOK_ARRAYMACRO},
+  {"arrayrelatedinfo",		EDIF_TOK_ARRAYRELATEDINFO},
+  {"arraysite",			EDIF_TOK_ARRAYSITE},
+  {"atleast",			EDIF_TOK_ATLEAST},
+  {"atmost",			EDIF_TOK_ATMOST},
+  {"author",			EDIF_TOK_AUTHOR},
+  {"basearray",			EDIF_TOK_BASEARRAY},
+  {"becomes",			EDIF_TOK_BECOMES},
+  {"between",			EDIF_TOK_BETWEEN},
+  {"boolean",			EDIF_TOK_BOOLEAN},
+  {"booleandisplay",		EDIF_TOK_BOOLEANDISPLAY},
+  {"booleanmap",		EDIF_TOK_BOOLEANMAP},
+  {"borderpattern",		EDIF_TOK_BORDERPATTERN},
+  {"borderwidth",		EDIF_TOK_BORDERWIDTH},
+  {"boundingbox",		EDIF_TOK_BOUNDINGBOX},
+  {"cell",			EDIF_TOK_CELL},
+  {"cellref",			EDIF_TOK_CELLREF},
+  {"celltype",			EDIF_TOK_CELLTYPE},
+  {"change",			EDIF_TOK_CHANGE},
+  {"circle",			EDIF_TOK_CIRCLE},
+  {"color",			EDIF_TOK_COLOR},
+  {"comment",			EDIF_TOK_COMMENT},
+  {"commentgraphics",		EDIF_TOK_COMMENTGRAPHICS},
+  {"compound",			EDIF_TOK_COMPOUND},
+  {"connectlocation",		EDIF_TOK_CONNECTLOCATION},
+  {"contents",			EDIF_TOK_CONTENTS},
+  {"cornertype",		EDIF_TOK_CORNERTYPE},
+  {"criticality",		EDIF_TOK_CRITICALITY},
+  {"currentmap",		EDIF_TOK_CURRENTMAP},
+  {"curve",			EDIF_TOK_CURVE},
+  {"cycle",			EDIF_TOK_CYCLE},
+  {"dataorigin",		EDIF_TOK_DATAORIGIN},
+  {"dcfaninload",		EDIF_TOK_DCFANINLOAD},
+  {"dcfanoutload",		EDIF_TOK_DCFANOUTLOAD},
+  {"dcmaxfanin",		EDIF_TOK_DCMAXFANIN},
+  {"dcmaxfanout",		EDIF_TOK_DCMAXFANOUT},
+  {"delay",			EDIF_TOK_DELAY},
+  {"delta",			EDIF_TOK_DELTA},
+  {"derivation",		EDIF_TOK_DERIVATION},
+  {"design",			EDIF_TOK_DESIGN},
+  {"designator",		EDIF_TOK_DESIGNATOR},
+  {"difference",		EDIF_TOK_DIFFERENCE},
+  {"direction",			EDIF_TOK_DIRECTION},
+  {"display",			EDIF_TOK_DISPLAY},
+  {"dominates",			EDIF_TOK_DOMINATES},
+  {"dot",			EDIF_TOK_DOT},
+  {"duration",			EDIF_TOK_DURATION},
+  {"e",				EDIF_TOK_E},
+  {"edif",			EDIF_TOK_EDIF},
+  {"ediflevel",			EDIF_TOK_EDIFLEVEL},
+  {"edifversion",		EDIF_TOK_EDIFVERSION},
+  {"enclosuredistance",		EDIF_TOK_ENCLOSUREDISTANCE},
+  {"endtype",			EDIF_TOK_ENDTYPE},
+  {"entry",			EDIF_TOK_ENTRY},
+  {"exactly",			EDIF_TOK_EXACTLY},
+  {"external",			EDIF_TOK_EXTERNAL},
+  {"fabricate",			EDIF_TOK_FABRICATE},
+  {"false",			EDIF_TOK_FALSE},
+  {"figure",			EDIF_TOK_FIGURE},
+  {"figurearea",		EDIF_TOK_FIGUREAREA},
+  {"figuregroup",		EDIF_TOK_FIGUREGROUP},
+  {"figuregroupobject",		EDIF_TOK_FIGUREGROUPOBJECT},
+  {"figuregroupoverride",	EDIF_TOK_FIGUREGROUPOVERRIDE},
+  {"figuregroupref",		EDIF_TOK_FIGUREGROUPREF},
+  {"figureperimeter",		EDIF_TOK_FIGUREPERIMETER},
+  {"figurewidth",		EDIF_TOK_FIGUREWIDTH},
+  {"fillpattern",		EDIF_TOK_FILLPATTERN},
+  {"follow",			EDIF_TOK_FOLLOW},
+  {"forbiddenevent",		EDIF_TOK_FORBIDDENEVENT},
+  {"globalportref",		EDIF_TOK_GLOBALPORTREF},
+  {"greaterthan",		EDIF_TOK_GREATERTHAN},
+  {"gridmap",			EDIF_TOK_GRIDMAP},
+  {"ignore",			EDIF_TOK_IGNORE},
+  {"includefiguregroup",	EDIF_TOK_INCLUDEFIGUREGROUP},
+  {"initial",			EDIF_TOK_INITIAL},
+  {"instance",			EDIF_TOK_INSTANCE},
+  {"instancebackannotate",	EDIF_TOK_INSTANCEBACKANNOTATE},
+  {"instancegroup",		EDIF_TOK_INSTANCEGROUP},
+  {"instancemap",		EDIF_TOK_INSTANCEMAP},
+  {"instanceref",		EDIF_TOK_INSTANCEREF},
+  {"integer",			EDIF_TOK_INTEGER},
+  {"integerdisplay",		EDIF_TOK_INTEGERDISPLAY},
+  {"interface",			EDIF_TOK_INTERFACE},
+  {"interfiguregroupspacing",	EDIF_TOK_INTERFIGUREGROUPSPACING},
+  {"intersection",		EDIF_TOK_INTERSECTION},
+  {"intrafiguregroupspacing",	EDIF_TOK_INTRAFIGUREGROUPSPACING},
+  {"inverse",			EDIF_TOK_INVERSE},
+  {"isolated",			EDIF_TOK_ISOLATED},
+  {"joined",			EDIF_TOK_JOINED},
+  {"justify",			EDIF_TOK_JUSTIFY},
+  {"keyworddisplay",		EDIF_TOK_KEYWORDDISPLAY},
+  {"keywordlevel",		EDIF_TOK_KEYWORDLEVEL},
+  {"keywordmap",		EDIF_TOK_KEYWORDMAP},
+  {"lessthan",			EDIF_TOK_LESSTHAN},
+  {"library",			EDIF_TOK_LIBRARY},
+  {"libraryref",		EDIF_TOK_LIBRARYREF},
+  {"listofnets",		EDIF_TOK_LISTOFNETS},
+  {"listofports",		EDIF_TOK_LISTOFPORTS},
+  {"loaddelay",			EDIF_TOK_LOADDELAY},
+  {"logicassign",		EDIF_TOK_LOGICASSIGN},
+  {"logicinput",		EDIF_TOK_LOGICINPUT},
+  {"logiclist",			EDIF_TOK_LOGICLIST},
+  {"logicmapinput",		EDIF_TOK_LOGICMAPINPUT},
+  {"logicmapoutput",		EDIF_TOK_LOGICMAPOUTPUT},
+  {"logiconeof",		EDIF_TOK_LOGICONEOF},
+  {"logicoutput",		EDIF_TOK_LOGICOUTPUT},
+  {"logicport",			EDIF_TOK_LOGICPORT},
+  {"logicref",			EDIF_TOK_LOGICREF},
+  {"logicvalue",		EDIF_TOK_LOGICVALUE},
+  {"logicwaveform",		EDIF_TOK_LOGICWAVEFORM},
+  {"maintain",			EDIF_TOK_MAINTAIN},
+  {"match",			EDIF_TOK_MATCH},
+  {"member",			EDIF_TOK_MEMBER},
+  {"minomax",			EDIF_TOK_MINOMAX},
+  {"minomaxdisplay",		EDIF_TOK_MINOMAXDISPLAY},
+  {"mnm",			EDIF_TOK_MNM},
+  {"multiplevalueset",		EDIF_TOK_MULTIPLEVALUESET},
+  {"mustjoin",			EDIF_TOK_MUSTJOIN},
+  {"name",			EDIF_TOK_NAME},
+  {"net",			EDIF_TOK_NET},
+  {"netbackannotate",		EDIF_TOK_NETBACKANNOTATE},
+  {"netbundle",			EDIF_TOK_NETBUNDLE},
+  {"netdelay",			EDIF_TOK_NETDELAY},
+  {"netgroup",			EDIF_TOK_NETGROUP},
+  {"netmap",			EDIF_TOK_NETMAP},
+  {"netref",			EDIF_TOK_NETREF},
+  {"nochange",			EDIF_TOK_NOCHANGE},
+  {"nonpermutable",		EDIF_TOK_NONPERMUTABLE},
+  {"notallowed",		EDIF_TOK_NOTALLOWED},
+  {"notchspacing",		EDIF_TOK_NOTCHSPACING},
+  {"number",			EDIF_TOK_NUMBER},
+  {"numberdefinition",		EDIF_TOK_NUMBERDEFINITION},
+  {"numberdisplay",		EDIF_TOK_NUMBERDISPLAY},
+  {"offpageconnector",		EDIF_TOK_OFFPAGECONNECTOR},
+  {"offsetevent",		EDIF_TOK_OFFSETEVENT},
+  {"openshape",			EDIF_TOK_OPENSHAPE},
+  {"orientation",		EDIF_TOK_ORIENTATION},
+  {"origin",			EDIF_TOK_ORIGIN},
+  {"overhangdistance",		EDIF_TOK_OVERHANGDISTANCE},
+  {"overlapdistance",		EDIF_TOK_OVERLAPDISTANCE},
+  {"oversize",			EDIF_TOK_OVERSIZE},
+  {"owner",			EDIF_TOK_OWNER},
+  {"page",			EDIF_TOK_PAGE},
+  {"pagesize",			EDIF_TOK_PAGESIZE},
+  {"parameter",			EDIF_TOK_PARAMETER},
+  {"parameterassign",		EDIF_TOK_PARAMETERASSIGN},
+  {"parameterdisplay",		EDIF_TOK_PARAMETERDISPLAY},
+  {"path",			EDIF_TOK_PATH},
+  {"pathdelay",			EDIF_TOK_PATHDELAY},
+  {"pathwidth",			EDIF_TOK_PATHWIDTH},
+  {"permutable",		EDIF_TOK_PERMUTABLE},
+  {"physicaldesignrule",	EDIF_TOK_PHYSICALDESIGNRULE},
+  {"plug",			EDIF_TOK_PLUG},
+  {"point",			EDIF_TOK_POINT},
+  {"pointdisplay",		EDIF_TOK_POINTDISPLAY},
+  {"pointlist",			EDIF_TOK_POINTLIST},
+  {"polygon",			EDIF_TOK_POLYGON},
+  {"port",			EDIF_TOK_PORT},
+  {"portbackannotate",		EDIF_TOK_PORTBACKANNOTATE},
+  {"portbundle",		EDIF_TOK_PORTBUNDLE},
+  {"portdelay",			EDIF_TOK_PORTDELAY},
+  {"portgroup",			EDIF_TOK_PORTGROUP},
+  {"portimplementation",	EDIF_TOK_PORTIMPLEMENTATION},
+  {"portinstance",		EDIF_TOK_PORTINSTANCE},
+  {"portlist",			EDIF_TOK_PORTLIST},
+  {"portlistalias",		EDIF_TOK_PORTLISTALIAS},
+  {"portmap",			EDIF_TOK_PORTMAP},
+  {"portref",			EDIF_TOK_PORTREF},
+  {"program",			EDIF_TOK_PROGRAM},
+  {"property",			EDIF_TOK_PROPERTY},
+  {"propertydisplay",		EDIF_TOK_PROPERTYDISPLAY},
+  {"protectionframe",		EDIF_TOK_PROTECTIONFRAME},
+  {"pt",			EDIF_TOK_PT},
+  {"rangevector",		EDIF_TOK_RANGEVECTOR},
+  {"rectangle",			EDIF_TOK_RECTANGLE},
+  {"rectanglesize",		EDIF_TOK_RECTANGLESIZE},
+  {"rename",			EDIF_TOK_RENAME},
+  {"resolves",			EDIF_TOK_RESOLVES},
+  {"scale",			EDIF_TOK_SCALE},
+  {"scalex",			EDIF_TOK_SCALEX},
+  {"scaley",			EDIF_TOK_SCALEY},
+  {"section",			EDIF_TOK_SECTION},
+  {"shape",			EDIF_TOK_SHAPE},
+  {"simulate",			EDIF_TOK_SIMULATE},
+  {"simulationinfo",		EDIF_TOK_SIMULATIONINFO},
+  {"singlevalueset",		EDIF_TOK_SINGLEVALUESET},
+  {"site",			EDIF_TOK_SITE},
+  {"socket",			EDIF_TOK_SOCKET},
+  {"socketset",			EDIF_TOK_SOCKETSET},
+  {"status",			EDIF_TOK_STATUS},
+  {"steady",			EDIF_TOK_STEADY},
+  {"string",			EDIF_TOK_STRING},
+  {"stringdisplay",		EDIF_TOK_STRINGDISPLAY},
+  {"strong",			EDIF_TOK_STRONG},
+  {"symbol",			EDIF_TOK_SYMBOL},
+  {"symmetry",			EDIF_TOK_SYMMETRY},
+  {"table",			EDIF_TOK_TABLE},
+  {"tabledefault",		EDIF_TOK_TABLEDEFAULT},
+  {"technology",		EDIF_TOK_TECHNOLOGY},
+  {"textheight",		EDIF_TOK_TEXTHEIGHT},
+  {"timeinterval",		EDIF_TOK_TIMEINTERVAL},
+  {"timestamp",			EDIF_TOK_TIMESTAMP},
+  {"timing",			EDIF_TOK_TIMING},
+  {"transform",			EDIF_TOK_TRANSFORM},
+  {"transition",		EDIF_TOK_TRANSITION},
+  {"trigger",			EDIF_TOK_TRIGGER},
+  {"true",			EDIF_TOK_TRUE},
+  {"unconstrained",		EDIF_TOK_UNCONSTRAINED},
+  {"undefined",			EDIF_TOK_UNDEFINED},
+  {"union",			EDIF_TOK_UNION},
+  {"unit",			EDIF_TOK_UNIT},
+  {"unused",			EDIF_TOK_UNUSED},
+  {"userdata",			EDIF_TOK_USERDATA},
+  {"version",			EDIF_TOK_VERSION},
+  {"view",			EDIF_TOK_VIEW},
+  {"viewlist",			EDIF_TOK_VIEWLIST},
+  {"viewmap",			EDIF_TOK_VIEWMAP},
+  {"viewref",			EDIF_TOK_VIEWREF},
+  {"viewtype",			EDIF_TOK_VIEWTYPE},
+  {"visible",			EDIF_TOK_VISIBLE},
+  {"voltagemap",		EDIF_TOK_VOLTAGEMAP},
+  {"wavevalue",			EDIF_TOK_WAVEVALUE},
+  {"weak",			EDIF_TOK_WEAK},
+  {"weakjoined",		EDIF_TOK_WEAKJOINED},
+  {"when",			EDIF_TOK_WHEN},
+  {"written",			EDIF_TOK_WRITTEN}
+};
+static int ContextDefSize = sizeof(ContextDef) / sizeof(Context);
+/*
+ *	Context follower tables:
+ *
+ *	  This is pretty ugly, an array is defined for each context
+ *	which has following context levels. Yet another table is used
+ *	to bind these arrays to the originating contexts.
+ *	  Arrays are declared as:
+ *
+ *		static short f_<Context name>[] = { ... };
+ *
+ *	The array entries are the '%token' values for all keywords which
+ *	can be reached from the <Context name> context. Like I said, ugly,
+ *	but it works.
+ *	  A negative entry means that the follow can only occur once within
+ *	the specified context.
+ */
+static short f_NULL[] = {EDIF_TOK_EDIF};
+static short f_Edif[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_EDIFVERSION,
+			 EDIF_TOK_EDIFLEVEL, EDIF_TOK_KEYWORDMAP,
+			 -EDIF_TOK_STATUS, EDIF_TOK_EXTERNAL,
+			 EDIF_TOK_LIBRARY, EDIF_TOK_DESIGN, EDIF_TOK_COMMENT,
+			 EDIF_TOK_USERDATA};
+static short f_AcLoad[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_MINOMAXDISPLAY};
+static short f_After[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_FOLLOW,
+			  EDIF_TOK_MAINTAIN, EDIF_TOK_LOGICASSIGN,
+			  EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Annotate[] = {EDIF_TOK_STRINGDISPLAY};
+static short f_Apply[] = {EDIF_TOK_CYCLE, EDIF_TOK_LOGICINPUT,
+			  EDIF_TOK_LOGICOUTPUT, EDIF_TOK_COMMENT,
+			  EDIF_TOK_USERDATA};
+static short f_Arc[] = {EDIF_TOK_PT};
+static short f_Array[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME};
+static short f_ArrayMacro[] = {EDIF_TOK_PLUG};
+static short f_ArrayRelatedInfo[] = {EDIF_TOK_BASEARRAY, EDIF_TOK_ARRAYSITE,
+				     EDIF_TOK_ARRAYMACRO, EDIF_TOK_COMMENT,
+				     EDIF_TOK_USERDATA};
+static short f_ArraySite[] = {EDIF_TOK_SOCKET};
+static short f_AtLeast[] = {EDIF_TOK_E};
+static short f_AtMost[] = {EDIF_TOK_E};
+static short f_Becomes[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST,
+			    EDIF_TOK_LOGICONEOF};
+/*
+static short f_Between[] = {EDIF_TOK_ATLEAST, EDIF_TOK_GREATERTHAN,
+			    EDIF_TOK_ATMOST, EDIF_TOK_LESSTHAN};
+*/
+static short f_Boolean[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE,
+			    EDIF_TOK_BOOLEANDISPLAY, EDIF_TOK_BOOLEAN};
+static short f_BooleanDisplay[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE,
+				   EDIF_TOK_DISPLAY};
+static short f_BooleanMap[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE};
+static short f_BorderPattern[] = {EDIF_TOK_BOOLEAN};
+static short f_BoundingBox[] = {EDIF_TOK_RECTANGLE};
+static short f_Cell[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_CELLTYPE,
+			 -EDIF_TOK_STATUS, -EDIF_TOK_VIEWMAP, EDIF_TOK_VIEW,
+			 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA,
+			 EDIF_TOK_PROPERTY};
+static short f_CellRef[] = {EDIF_TOK_NAME, EDIF_TOK_LIBRARYREF};
+static short f_Change[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			   EDIF_TOK_BECOMES, EDIF_TOK_TRANSITION};
+static short f_Circle[] = {EDIF_TOK_PT, EDIF_TOK_PROPERTY};
+static short f_Color[] = {EDIF_TOK_E};
+static short f_CommentGraphics[] = {EDIF_TOK_ANNOTATE, EDIF_TOK_FIGURE,
+				    EDIF_TOK_INSTANCE, -EDIF_TOK_BOUNDINGBOX,
+				    EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+				    EDIF_TOK_USERDATA};
+static short f_Compound[] = {EDIF_TOK_NAME};
+static short f_ConnectLocation[] = {EDIF_TOK_FIGURE};
+static short f_Contents[] = {EDIF_TOK_INSTANCE, EDIF_TOK_OFFPAGECONNECTOR,
+			     EDIF_TOK_FIGURE, EDIF_TOK_SECTION, EDIF_TOK_NET,
+			     EDIF_TOK_NETBUNDLE, EDIF_TOK_PAGE,
+			     EDIF_TOK_COMMENTGRAPHICS,
+			     EDIF_TOK_PORTIMPLEMENTATION,
+			     EDIF_TOK_TIMING, EDIF_TOK_SIMULATE,
+			     EDIF_TOK_WHEN, EDIF_TOK_FOLLOW,
+			     EDIF_TOK_LOGICPORT, -EDIF_TOK_BOUNDINGBOX,
+			     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Criticality[] = {EDIF_TOK_INTEGERDISPLAY};
+static short f_CurrentMap[] = {EDIF_TOK_MNM, EDIF_TOK_E};
+static short f_Curve[] = {EDIF_TOK_ARC, EDIF_TOK_PT};
+static short f_Cycle[] = {EDIF_TOK_DURATION};
+static short f_DataOrigin[] = {EDIF_TOK_VERSION};
+static short f_DcFanInLoad[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_DcFanOutLoad[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_DcMaxFanIn[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_DcMaxFanOut[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY};
+static short f_Delay[] = {EDIF_TOK_MNM, EDIF_TOK_E};
+static short f_Delta[] = {EDIF_TOK_PT};
+static short f_Design[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_CELLREF,
+			   EDIF_TOK_STATUS, EDIF_TOK_COMMENT,
+			   EDIF_TOK_PROPERTY, EDIF_TOK_USERDATA};
+static short f_Designator[] = {EDIF_TOK_STRINGDISPLAY};
+static short f_Difference[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			       EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			       EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE};
+static short f_Display[] = {EDIF_TOK_NAME, EDIF_TOK_FIGUREGROUPOVERRIDE,
+			    EDIF_TOK_JUSTIFY, EDIF_TOK_ORIENTATION,
+			    EDIF_TOK_ORIGIN};
+static short f_Dominates[] = {EDIF_TOK_NAME};
+static short f_Dot[] = {EDIF_TOK_PT, EDIF_TOK_PROPERTY};
+static short f_Duration[] = {EDIF_TOK_E};
+static short f_EnclosureDistance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				      EDIF_TOK_FIGUREGROUPOBJECT,
+				      EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				      EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				      EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+				      EDIF_TOK_SINGLEVALUESET,
+				      EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Entry[] = {EDIF_TOK_MATCH, EDIF_TOK_CHANGE, EDIF_TOK_STEADY,
+			  EDIF_TOK_LOGICREF, EDIF_TOK_PORTREF,
+			  EDIF_TOK_NOCHANGE, EDIF_TOK_TABLE,
+			  EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_Exactly[] = {EDIF_TOK_E};
+static short f_External[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			     EDIF_TOK_EDIFLEVEL, EDIF_TOK_TECHNOLOGY,
+			     -EDIF_TOK_STATUS, EDIF_TOK_CELL, EDIF_TOK_COMMENT,
+			     EDIF_TOK_USERDATA};
+static short f_Fabricate[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME};
+static short f_Figure[] = {EDIF_TOK_NAME, EDIF_TOK_FIGUREGROUPOVERRIDE,
+			   EDIF_TOK_CIRCLE, EDIF_TOK_DOT, EDIF_TOK_OPENSHAPE,
+			   EDIF_TOK_PATH, EDIF_TOK_POLYGON,
+			   EDIF_TOK_RECTANGLE, EDIF_TOK_SHAPE,
+			   EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_FigureArea[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			       EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+			       EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+			       EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+			       EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+			       EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_FigureGroup[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				-EDIF_TOK_CORNERTYPE, -EDIF_TOK_ENDTYPE,
+				-EDIF_TOK_PATHWIDTH, -EDIF_TOK_BORDERWIDTH,
+				-EDIF_TOK_COLOR, -EDIF_TOK_FILLPATTERN,
+				-EDIF_TOK_BORDERPATTERN, -EDIF_TOK_TEXTHEIGHT,
+				-EDIF_TOK_VISIBLE, EDIF_TOK_INCLUDEFIGUREGROUP,
+				EDIF_TOK_COMMENT, EDIF_TOK_PROPERTY,
+				EDIF_TOK_USERDATA};
+static short f_FigureGroupObject[] = {EDIF_TOK_NAME,
+				      EDIF_TOK_FIGUREGROUPOBJECT,
+				      EDIF_TOK_INTERSECTION, EDIF_TOK_UNION,
+				      EDIF_TOK_DIFFERENCE, EDIF_TOK_INVERSE,
+				      EDIF_TOK_OVERSIZE};
+static short f_FigureGroupOverride[] = {EDIF_TOK_NAME, -EDIF_TOK_CORNERTYPE,
+					-EDIF_TOK_ENDTYPE, -EDIF_TOK_PATHWIDTH,
+					-EDIF_TOK_BORDERWIDTH, -EDIF_TOK_COLOR,
+					-EDIF_TOK_FILLPATTERN,
+					-EDIF_TOK_TEXTHEIGHT,
+					-EDIF_TOK_BORDERPATTERN,
+					EDIF_TOK_VISIBLE, EDIF_TOK_COMMENT,
+					EDIF_TOK_PROPERTY, EDIF_TOK_USERDATA};
+static short f_FigureGroupRef[] = {EDIF_TOK_NAME, EDIF_TOK_LIBRARYREF};
+static short f_FigurePerimeter[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				    EDIF_TOK_FIGUREGROUPOBJECT,
+				    EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				    EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				    EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+				    EDIF_TOK_SINGLEVALUESET, EDIF_TOK_COMMENT,
+				    EDIF_TOK_USERDATA};
+static short f_FigureWidth[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_FillPattern[] = {EDIF_TOK_BOOLEAN};
+static short f_Follow[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_TABLE,
+			   EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_ForbiddenEvent[] = {EDIF_TOK_TIMEINTERVAL, EDIF_TOK_EVENT};
+static short f_GlobalPortRef[] = {EDIF_TOK_NAME};
+static short f_GreaterThan[] = {EDIF_TOK_E};
+static short f_GridMap[] = {EDIF_TOK_E};
+static short f_IncludeFigureGroup[] = {EDIF_TOK_FIGUREGROUPREF,
+				       EDIF_TOK_INTERSECTION, EDIF_TOK_UNION,
+				       EDIF_TOK_DIFFERENCE, EDIF_TOK_INVERSE,
+				       EDIF_TOK_OVERSIZE};
+static short f_Instance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			     EDIF_TOK_VIEWREF, EDIF_TOK_VIEWLIST,
+			     -EDIF_TOK_TRANSFORM, EDIF_TOK_PARAMETERASSIGN,
+			     EDIF_TOK_PORTINSTANCE, EDIF_TOK_TIMING,
+			     -EDIF_TOK_DESIGNATOR, EDIF_TOK_PROPERTY,
+			     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_InstanceBackAnnotate[] = {EDIF_TOK_INSTANCEREF,
+					 -EDIF_TOK_DESIGNATOR, EDIF_TOK_TIMING,
+					 EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT};
+static short f_InstanceGroup[] = {EDIF_TOK_INSTANCEREF};
+static short f_InstanceMap[] = {EDIF_TOK_INSTANCEREF, EDIF_TOK_INSTANCEGROUP,
+				EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_InstanceRef[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				EDIF_TOK_INSTANCEREF, EDIF_TOK_VIEWREF};
+static short f_Integer[] = {EDIF_TOK_INTEGERDISPLAY, EDIF_TOK_INTEGER};
+static short f_IntegerDisplay[] = {EDIF_TOK_DISPLAY};
+static short f_Interface[] = {EDIF_TOK_PORT, EDIF_TOK_PORTBUNDLE,
+			      -EDIF_TOK_SYMBOL, -EDIF_TOK_PROTECTIONFRAME,
+			      -EDIF_TOK_ARRAYRELATEDINFO, EDIF_TOK_PARAMETER,
+			      EDIF_TOK_JOINED, EDIF_TOK_MUSTJOIN,
+			      EDIF_TOK_WEAKJOINED, EDIF_TOK_PERMUTABLE,
+			      EDIF_TOK_TIMING, EDIF_TOK_SIMULATE,
+			      -EDIF_TOK_DESIGNATOR, EDIF_TOK_PROPERTY,
+			      EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_InterFigureGroupSpacing[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+					    EDIF_TOK_FIGUREGROUPOBJECT,
+					    EDIF_TOK_LESSTHAN,
+					    EDIF_TOK_GREATERTHAN,
+					    EDIF_TOK_ATMOST,
+					    EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+					    EDIF_TOK_BETWEEN,
+					    EDIF_TOK_SINGLEVALUESET,
+					    EDIF_TOK_COMMENT,
+					    EDIF_TOK_USERDATA};
+static short f_Intersection[] = {EDIF_TOK_FIGUREGROUPREF,
+				 EDIF_TOK_INTERSECTION, EDIF_TOK_UNION,
+				 EDIF_TOK_DIFFERENCE, EDIF_TOK_INVERSE,
+				 EDIF_TOK_OVERSIZE};
+static short f_IntraFigureGroupSpacing[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+					    EDIF_TOK_FIGUREGROUPOBJECT,
+					    EDIF_TOK_LESSTHAN,
+					    EDIF_TOK_GREATERTHAN,
+					    EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+					    EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+					    EDIF_TOK_SINGLEVALUESET,
+					    EDIF_TOK_COMMENT,
+					    EDIF_TOK_USERDATA};
+static short f_Inverse[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			    EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			    EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE};
+static short f_Joined[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			   EDIF_TOK_GLOBALPORTREF};
+static short f_KeywordDisplay[] = {EDIF_TOK_DISPLAY};
+static short f_KeywordMap[] = {EDIF_TOK_KEYWORDLEVEL, EDIF_TOK_COMMENT};
+static short f_LessThan[] = {EDIF_TOK_E};
+static short f_Library[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_EDIFLEVEL,
+			    EDIF_TOK_TECHNOLOGY, -EDIF_TOK_STATUS,
+			    EDIF_TOK_CELL, EDIF_TOK_COMMENT,
+			    EDIF_TOK_USERDATA};
+static short f_LibraryRef[] = {EDIF_TOK_NAME};
+static short f_ListOfNets[] = {EDIF_TOK_NET};
+static short f_ListOfPorts[] = {EDIF_TOK_PORT, EDIF_TOK_PORTBUNDLE};
+static short f_LoadDelay[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_MINOMAXDISPLAY};
+static short f_LogicAssign[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF,
+				EDIF_TOK_LOGICREF, EDIF_TOK_TABLE,
+				EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_LogicInput[] = {EDIF_TOK_PORTLIST, EDIF_TOK_PORTREF,
+			       EDIF_TOK_NAME, EDIF_TOK_LOGICWAVEFORM};
+static short f_LogicList[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICONEOF,
+			      EDIF_TOK_IGNORE};
+static short f_LogicMapInput[] = {EDIF_TOK_LOGICREF};
+static short f_LogicMapOutput[] = {EDIF_TOK_LOGICREF};
+static short f_LogicOneOf[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST};
+static short f_LogicOutput[] = {EDIF_TOK_PORTLIST, EDIF_TOK_PORTREF,
+				EDIF_TOK_NAME, EDIF_TOK_LOGICWAVEFORM};
+static short f_LogicPort[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			      EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			      EDIF_TOK_USERDATA};
+static short f_LogicRef[] = {EDIF_TOK_NAME, EDIF_TOK_LIBRARYREF};
+static short f_LogicValue[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			       -EDIF_TOK_VOLTAGEMAP, -EDIF_TOK_CURRENTMAP,
+			       -EDIF_TOK_BOOLEANMAP, -EDIF_TOK_COMPOUND,
+			       -EDIF_TOK_WEAK ,-EDIF_TOK_STRONG,
+			       -EDIF_TOK_DOMINATES, -EDIF_TOK_LOGICMAPOUTPUT,
+			       -EDIF_TOK_LOGICMAPINPUT,
+			       -EDIF_TOK_ISOLATED, EDIF_TOK_RESOLVES,
+			       EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			       EDIF_TOK_USERDATA};
+static short f_LogicWaveform[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST,
+				  EDIF_TOK_LOGICONEOF, EDIF_TOK_IGNORE};
+static short f_Maintain[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_DELAY,
+			     EDIF_TOK_LOADDELAY};
+static short f_Match[] = {EDIF_TOK_NAME, EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			  EDIF_TOK_LOGICLIST, EDIF_TOK_LOGICONEOF};
+static short f_Member[] = {EDIF_TOK_NAME};
+static short f_MiNoMax[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_MINOMAXDISPLAY,
+			    EDIF_TOK_MINOMAX};
+static short f_MiNoMaxDisplay[] = {EDIF_TOK_MNM, EDIF_TOK_E, EDIF_TOK_DISPLAY};
+static short f_Mnm[] = {EDIF_TOK_E, EDIF_TOK_UNDEFINED,
+			EDIF_TOK_UNCONSTRAINED};
+static short f_MultipleValueSet[] = {EDIF_TOK_RANGEVECTOR};
+static short f_MustJoin[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			     EDIF_TOK_WEAKJOINED, EDIF_TOK_JOINED};
+static short f_Name[] = {EDIF_TOK_DISPLAY};
+static short f_Net[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, -EDIF_TOK_CRITICALITY,
+			EDIF_TOK_NETDELAY, EDIF_TOK_FIGURE, EDIF_TOK_NET,
+			EDIF_TOK_INSTANCE, EDIF_TOK_COMMENTGRAPHICS,
+			EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			EDIF_TOK_USERDATA, EDIF_TOK_JOINED, EDIF_TOK_ARRAY};
+static short f_NetBackAnnotate[] = {EDIF_TOK_NETREF, EDIF_TOK_NETDELAY,
+				    -EDIF_TOK_CRITICALITY, EDIF_TOK_PROPERTY,
+				    EDIF_TOK_COMMENT};
+static short f_NetBundle[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			      EDIF_TOK_LISTOFNETS, EDIF_TOK_FIGURE,
+			      EDIF_TOK_COMMENTGRAPHICS, EDIF_TOK_PROPERTY,
+			      EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_NetDelay[] = {EDIF_TOK_DERIVATION, EDIF_TOK_DELAY,
+			     EDIF_TOK_TRANSITION, EDIF_TOK_BECOMES};
+static short f_NetGroup[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER, EDIF_TOK_NETREF};
+static short f_NetMap[] = {EDIF_TOK_NETREF, EDIF_TOK_NETGROUP,
+			   EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_NetRef[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER, EDIF_TOK_NETREF,
+			   EDIF_TOK_INSTANCEREF, EDIF_TOK_VIEWREF};
+static short f_NonPermutable[] = {EDIF_TOK_PORTREF, EDIF_TOK_PERMUTABLE};
+static short f_NotAllowed[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+			       EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_COMMENT,
+			       EDIF_TOK_USERDATA};
+static short f_NotchSpacing[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				 EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				 EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				 EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				 EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Number[] = {EDIF_TOK_E, EDIF_TOK_NUMBERDISPLAY, EDIF_TOK_NUMBER};
+static short f_NumberDefinition[] = {EDIF_TOK_SCALE, -EDIF_TOK_GRIDMAP,
+				     EDIF_TOK_COMMENT};
+static short f_NumberDisplay[] = {EDIF_TOK_E, EDIF_TOK_DISPLAY};
+static short f_OffPageConnector[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				     -EDIF_TOK_UNUSED, EDIF_TOK_PROPERTY,
+				     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_OffsetEvent[] = {EDIF_TOK_EVENT, EDIF_TOK_E};
+static short f_OpenShape[] = {EDIF_TOK_CURVE, EDIF_TOK_PROPERTY};
+static short f_Origin[] = {EDIF_TOK_PT};
+static short f_OverhangDistance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				     EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				     EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				     EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				     EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_OverlapDistance[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				    EDIF_TOK_FIGUREGROUPOBJECT, EDIF_TOK_LESSTHAN,
+				    EDIF_TOK_GREATERTHAN, EDIF_TOK_ATMOST,
+				    EDIF_TOK_ATLEAST, EDIF_TOK_EXACTLY,
+				    EDIF_TOK_BETWEEN, EDIF_TOK_SINGLEVALUESET,
+				    EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_Oversize[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			     EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			     EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE,
+			     EDIF_TOK_CORNERTYPE};
+static short f_Page[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			 EDIF_TOK_INSTANCE, EDIF_TOK_NET, EDIF_TOK_NETBUNDLE,
+			 EDIF_TOK_COMMENTGRAPHICS, EDIF_TOK_PORTIMPLEMENTATION,
+			 -EDIF_TOK_PAGESIZE, -EDIF_TOK_BOUNDINGBOX,
+			 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PageSize[] = {EDIF_TOK_RECTANGLE};
+static short f_Parameter[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			      EDIF_TOK_BOOLEAN, EDIF_TOK_INTEGER,
+			      EDIF_TOK_MINOMAX, EDIF_TOK_NUMBER,
+			      EDIF_TOK_POINT, EDIF_TOK_STRING};
+static short f_ParameterAssign[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				    EDIF_TOK_BOOLEAN, EDIF_TOK_INTEGER,
+				    EDIF_TOK_MINOMAX, EDIF_TOK_NUMBER, EDIF_TOK_POINT,
+				    EDIF_TOK_STRING};
+static short f_ParameterDisplay[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				     EDIF_TOK_DISPLAY};
+static short f_Path[] = {EDIF_TOK_POINTLIST, EDIF_TOK_PROPERTY};
+static short f_PathDelay[] = {EDIF_TOK_DELAY, EDIF_TOK_EVENT};
+static short f_Permutable[] = {EDIF_TOK_PORTREF, EDIF_TOK_PERMUTABLE,
+			       EDIF_TOK_NONPERMUTABLE};
+static short f_PhysicalDesignRule[] = {EDIF_TOK_FIGUREWIDTH,
+				       EDIF_TOK_FIGUREAREA,
+				       EDIF_TOK_RECTANGLESIZE,
+				       EDIF_TOK_FIGUREPERIMETER,
+				       EDIF_TOK_OVERLAPDISTANCE,
+				       EDIF_TOK_OVERHANGDISTANCE,
+				       EDIF_TOK_ENCLOSUREDISTANCE,
+				       EDIF_TOK_INTERFIGUREGROUPSPACING,
+				       EDIF_TOK_NOTCHSPACING,
+				       EDIF_TOK_INTRAFIGUREGROUPSPACING,
+				       EDIF_TOK_NOTALLOWED,
+				       EDIF_TOK_FIGUREGROUP, EDIF_TOK_COMMENT,
+				       EDIF_TOK_USERDATA};
+static short f_Plug[] = {EDIF_TOK_SOCKETSET};
+static short f_Point[] = {EDIF_TOK_PT, EDIF_TOK_POINTDISPLAY,
+			  EDIF_TOK_POINT};
+static short f_PointDisplay[] = {EDIF_TOK_PT, EDIF_TOK_DISPLAY};
+static short f_PointList[] = {EDIF_TOK_PT};
+static short f_Polygon[] = {EDIF_TOK_POINTLIST, EDIF_TOK_PROPERTY};
+static short f_Port[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			 -EDIF_TOK_DIRECTION, -EDIF_TOK_UNUSED,
+			 EDIF_TOK_PORTDELAY, -EDIF_TOK_DESIGNATOR,
+			 -EDIF_TOK_DCFANINLOAD, -EDIF_TOK_DCFANOUTLOAD,
+			 -EDIF_TOK_DCMAXFANIN, -EDIF_TOK_DCMAXFANOUT,
+			 -EDIF_TOK_ACLOAD, EDIF_TOK_PROPERTY,
+			 EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PortBackAnnotate[] = {EDIF_TOK_PORTREF, -EDIF_TOK_DESIGNATOR,
+				     EDIF_TOK_PORTDELAY, -EDIF_TOK_DCFANINLOAD,
+				     -EDIF_TOK_DCFANOUTLOAD,
+				     -EDIF_TOK_DCMAXFANIN,
+				     -EDIF_TOK_DCMAXFANOUT, -EDIF_TOK_ACLOAD,
+				     EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT};
+static short f_PortBundle[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_ARRAY,
+			       EDIF_TOK_LISTOFPORTS, EDIF_TOK_PROPERTY,
+			       EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PortDelay[] = {EDIF_TOK_DERIVATION, EDIF_TOK_DELAY,
+			      EDIF_TOK_LOADDELAY, EDIF_TOK_TRANSITION,
+			      EDIF_TOK_BECOMES};
+static short f_PortGroup[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+			      EDIF_TOK_PORTREF};
+static short f_PortImplementation[] = {EDIF_TOK_PORTREF, EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+				       -EDIF_TOK_CONNECTLOCATION,
+				       EDIF_TOK_FIGURE, EDIF_TOK_INSTANCE,
+				       EDIF_TOK_COMMENTGRAPHICS,
+				       EDIF_TOK_PROPERTYDISPLAY,
+				       EDIF_TOK_KEYWORDDISPLAY,
+				       EDIF_TOK_PROPERTY,
+				       EDIF_TOK_USERDATA, EDIF_TOK_COMMENT};
+static short f_PortInstance[] = {EDIF_TOK_PORTREF, EDIF_TOK_NAME,
+				 EDIF_TOK_MEMBER, -EDIF_TOK_UNUSED,
+				 EDIF_TOK_PORTDELAY, -EDIF_TOK_DESIGNATOR,
+				 -EDIF_TOK_DCFANINLOAD,
+				 -EDIF_TOK_DCFANOUTLOAD, -EDIF_TOK_DCMAXFANIN,
+				 -EDIF_TOK_DCMAXFANOUT, -EDIF_TOK_ACLOAD,
+				 EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+				 EDIF_TOK_USERDATA};
+static short f_PortList[] = {EDIF_TOK_PORTREF, EDIF_TOK_NAME,
+			     EDIF_TOK_MEMBER};
+static short f_PortListAlias[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				  EDIF_TOK_ARRAY, EDIF_TOK_PORTLIST};
+static short f_PortMap[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTGROUP,
+			    EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_PortRef[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER,
+			    EDIF_TOK_PORTREF, EDIF_TOK_INSTANCEREF,
+			    EDIF_TOK_VIEWREF};
+static short f_Program[] = {EDIF_TOK_VERSION};
+static short f_Property[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_BOOLEAN,
+			     EDIF_TOK_INTEGER, EDIF_TOK_MINOMAX,
+			     EDIF_TOK_NUMBER, EDIF_TOK_POINT, EDIF_TOK_STRING,
+			     -EDIF_TOK_OWNER, -EDIF_TOK_UNIT,
+			     EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT};
+static short f_PropertyDisplay[] = {EDIF_TOK_NAME, EDIF_TOK_DISPLAY};
+static short f_ProtectionFrame[] = {EDIF_TOK_PORTIMPLEMENTATION,
+				    EDIF_TOK_FIGURE, EDIF_TOK_INSTANCE,
+				    EDIF_TOK_COMMENTGRAPHICS,
+				    -EDIF_TOK_BOUNDINGBOX,
+				    EDIF_TOK_PROPERTYDISPLAY,
+				    EDIF_TOK_KEYWORDDISPLAY,
+				    EDIF_TOK_PARAMETERDISPLAY,
+				    EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+				    EDIF_TOK_USERDATA};
+static short f_RangeVector[] = {EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN,
+				EDIF_TOK_SINGLEVALUESET};
+static short f_Rectangle[] = {EDIF_TOK_PT, EDIF_TOK_PROPERTY};
+static short f_RectangleSize[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME,
+				  EDIF_TOK_FIGUREGROUPOBJECT,
+				  EDIF_TOK_RANGEVECTOR,
+				  EDIF_TOK_MULTIPLEVALUESET,EDIF_TOK_COMMENT,
+				  EDIF_TOK_USERDATA};
+static short f_Rename[] = {EDIF_TOK_NAME, EDIF_TOK_STRINGDISPLAY};
+static short f_Resolves[] = {EDIF_TOK_NAME};
+static short f_Scale[] = {EDIF_TOK_E, EDIF_TOK_UNIT};
+static short f_Section[] = {EDIF_TOK_SECTION, EDIF_TOK_INSTANCE};
+static short f_Shape[] = {EDIF_TOK_CURVE, EDIF_TOK_PROPERTY};
+static short f_Simulate[] = {EDIF_TOK_NAME, EDIF_TOK_PORTLISTALIAS,
+			     EDIF_TOK_WAVEVALUE, EDIF_TOK_APPLY,
+			     EDIF_TOK_COMMENT, EDIF_TOK_USERDATA};
+static short f_SimulationInfo[] = {EDIF_TOK_LOGICVALUE, EDIF_TOK_COMMENT,
+				   EDIF_TOK_USERDATA};
+static short f_SingleValueSet[] = {EDIF_TOK_LESSTHAN, EDIF_TOK_GREATERTHAN,
+				   EDIF_TOK_ATMOST, EDIF_TOK_ATLEAST,
+				   EDIF_TOK_EXACTLY, EDIF_TOK_BETWEEN};
+static short f_Site[] = {EDIF_TOK_VIEWREF, EDIF_TOK_TRANSFORM};
+static short f_Socket[] = {EDIF_TOK_SYMMETRY};
+static short f_SocketSet[] = {EDIF_TOK_SYMMETRY, EDIF_TOK_SITE};
+static short f_Status[] = {EDIF_TOK_WRITTEN, EDIF_TOK_COMMENT,
+			   EDIF_TOK_USERDATA};
+static short f_Steady[] = {EDIF_TOK_NAME, EDIF_TOK_MEMBER, EDIF_TOK_PORTREF,
+			   EDIF_TOK_PORTLIST, EDIF_TOK_DURATION,
+			   EDIF_TOK_TRANSITION, EDIF_TOK_BECOMES};
+static short f_String[] = {EDIF_TOK_STRINGDISPLAY, EDIF_TOK_STRING};
+static short f_StringDisplay[] = {EDIF_TOK_DISPLAY};
+static short f_Strong[] = {EDIF_TOK_NAME};
+static short f_Symbol[] = {EDIF_TOK_PORTIMPLEMENTATION, EDIF_TOK_FIGURE,
+			   EDIF_TOK_INSTANCE, EDIF_TOK_COMMENTGRAPHICS,
+			   EDIF_TOK_ANNOTATE, -EDIF_TOK_PAGESIZE,
+			   -EDIF_TOK_BOUNDINGBOX, EDIF_TOK_PROPERTYDISPLAY,
+			   EDIF_TOK_KEYWORDDISPLAY, EDIF_TOK_PARAMETERDISPLAY,
+			   EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			   EDIF_TOK_USERDATA};
+static short f_Symmetry[] = {EDIF_TOK_TRANSFORM};
+static short f_Table[] = {EDIF_TOK_ENTRY, EDIF_TOK_TABLEDEFAULT};
+static short f_TableDefault[] = {EDIF_TOK_LOGICREF, EDIF_TOK_PORTREF,
+				 EDIF_TOK_NOCHANGE, EDIF_TOK_TABLE,
+				 EDIF_TOK_DELAY, EDIF_TOK_LOADDELAY};
+static short f_Technology[] = {EDIF_TOK_NUMBERDEFINITION, EDIF_TOK_FIGUREGROUP,
+			       EDIF_TOK_FABRICATE, -EDIF_TOK_SIMULATIONINFO,
+			       EDIF_TOK_COMMENT, EDIF_TOK_USERDATA,
+			       -EDIF_TOK_PHYSICALDESIGNRULE};
+static short f_TimeInterval[] = {EDIF_TOK_EVENT, EDIF_TOK_OFFSETEVENT,
+				 EDIF_TOK_DURATION};
+static short f_Timing[] = {EDIF_TOK_DERIVATION, EDIF_TOK_PATHDELAY,
+			   EDIF_TOK_FORBIDDENEVENT, EDIF_TOK_COMMENT,
+			   EDIF_TOK_USERDATA};
+static short f_Transform[] = {EDIF_TOK_SCALEX, EDIF_TOK_SCALEY, EDIF_TOK_DELTA,
+			      EDIF_TOK_ORIENTATION, EDIF_TOK_ORIGIN};
+static short f_Transition[] = {EDIF_TOK_NAME, EDIF_TOK_LOGICLIST,
+			       EDIF_TOK_LOGICONEOF};
+static short f_Trigger[] = {EDIF_TOK_CHANGE, EDIF_TOK_STEADY,
+			    EDIF_TOK_INITIAL};
+static short f_Union[] = {EDIF_TOK_FIGUREGROUPREF, EDIF_TOK_INTERSECTION,
+			  EDIF_TOK_UNION, EDIF_TOK_DIFFERENCE,
+			  EDIF_TOK_INVERSE, EDIF_TOK_OVERSIZE};
+static short f_View[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_VIEWTYPE,
+			 EDIF_TOK_INTERFACE, -EDIF_TOK_STATUS,
+			 -EDIF_TOK_CONTENTS, EDIF_TOK_COMMENT,
+			 EDIF_TOK_PROPERTY, EDIF_TOK_USERDATA};
+static short f_ViewList[] = {EDIF_TOK_VIEWREF, EDIF_TOK_VIEWLIST};
+static short f_ViewMap[] = {EDIF_TOK_PORTMAP, EDIF_TOK_PORTBACKANNOTATE,
+			    EDIF_TOK_INSTANCEMAP,
+			    EDIF_TOK_INSTANCEBACKANNOTATE, EDIF_TOK_NETMAP,
+			    EDIF_TOK_NETBACKANNOTATE, EDIF_TOK_COMMENT,
+			    EDIF_TOK_USERDATA};
+static short f_ViewRef[] = {EDIF_TOK_NAME, EDIF_TOK_CELLREF};
+static short f_Visible[] = {EDIF_TOK_FALSE, EDIF_TOK_TRUE};
+static short f_VoltageMap[] = {EDIF_TOK_MNM, EDIF_TOK_E};
+static short f_WaveValue[] = {EDIF_TOK_NAME, EDIF_TOK_RENAME, EDIF_TOK_E,
+			      EDIF_TOK_LOGICWAVEFORM};
+static short f_Weak[] = {EDIF_TOK_NAME};
+static short f_WeakJoined[] = {EDIF_TOK_PORTREF, EDIF_TOK_PORTLIST,
+			       EDIF_TOK_JOINED};
+static short f_When[] = {EDIF_TOK_TRIGGER, EDIF_TOK_AFTER,
+			 EDIF_TOK_FOLLOW, EDIF_TOK_MAINTAIN,
+			 EDIF_TOK_LOGICASSIGN, EDIF_TOK_COMMENT,
+			 EDIF_TOK_USERDATA};
+static short f_Written[] = {EDIF_TOK_TIMESTAMP, EDIF_TOK_AUTHOR,
+			    EDIF_TOK_PROGRAM, EDIF_TOK_DATAORIGIN,
+			    EDIF_TOK_PROPERTY, EDIF_TOK_COMMENT,
+			    EDIF_TOK_USERDATA};
+/*
+ *	Context binding table:
+ *
+ *	  This binds context follower arrays to their originating context.
+ */
+typedef struct Binder {
+  short *Follower;		/* pointer to follower array */
+  short Origin;			/* '%token' value of origin */
+  short FollowerSize;		/* size of follower array */
+} Binder;
+#define	BE(f,o)			{f,o,sizeof(f)/sizeof(short)}
+static Binder BinderDef[] = {
+  BE(f_NULL,			0),
+  BE(f_Edif,			EDIF_TOK_EDIF),
+  BE(f_AcLoad,			EDIF_TOK_ACLOAD),
+  BE(f_After,			EDIF_TOK_AFTER),
+  BE(f_Annotate,		EDIF_TOK_ANNOTATE),
+  BE(f_Apply,			EDIF_TOK_APPLY),
+  BE(f_Arc,			EDIF_TOK_ARC),
+  BE(f_Array,			EDIF_TOK_ARRAY),
+  BE(f_ArrayMacro,		EDIF_TOK_ARRAYMACRO),
+  BE(f_ArrayRelatedInfo,	EDIF_TOK_ARRAYRELATEDINFO),
+  BE(f_ArraySite,		EDIF_TOK_ARRAYSITE),
+  BE(f_AtLeast,			EDIF_TOK_ATLEAST),
+  BE(f_AtMost,			EDIF_TOK_ATMOST),
+  BE(f_Becomes,			EDIF_TOK_BECOMES),
+  BE(f_Boolean,			EDIF_TOK_BOOLEAN),
+  BE(f_BooleanDisplay,		EDIF_TOK_BOOLEANDISPLAY),
+  BE(f_BooleanMap,		EDIF_TOK_BOOLEANMAP),
+  BE(f_BorderPattern,		EDIF_TOK_BORDERPATTERN),
+  BE(f_BoundingBox,		EDIF_TOK_BOUNDINGBOX),
+  BE(f_Cell,			EDIF_TOK_CELL),
+  BE(f_CellRef,			EDIF_TOK_CELLREF),
+  BE(f_Change,			EDIF_TOK_CHANGE),
+  BE(f_Circle,			EDIF_TOK_CIRCLE),
+  BE(f_Color,			EDIF_TOK_COLOR),
+  BE(f_CommentGraphics,		EDIF_TOK_COMMENTGRAPHICS),
+  BE(f_Compound,		EDIF_TOK_COMPOUND),
+  BE(f_ConnectLocation,		EDIF_TOK_CONNECTLOCATION),
+  BE(f_Contents,		EDIF_TOK_CONTENTS),
+  BE(f_Criticality,		EDIF_TOK_CRITICALITY),
+  BE(f_CurrentMap,		EDIF_TOK_CURRENTMAP),
+  BE(f_Curve,			EDIF_TOK_CURVE),
+  BE(f_Cycle,			EDIF_TOK_CYCLE),
+  BE(f_DataOrigin,		EDIF_TOK_DATAORIGIN),
+  BE(f_DcFanInLoad,		EDIF_TOK_DCFANINLOAD),
+  BE(f_DcFanOutLoad,		EDIF_TOK_DCFANOUTLOAD),
+  BE(f_DcMaxFanIn,		EDIF_TOK_DCMAXFANIN),
+  BE(f_DcMaxFanOut,		EDIF_TOK_DCMAXFANOUT),
+  BE(f_Delay,			EDIF_TOK_DELAY),
+  BE(f_Delta,			EDIF_TOK_DELTA),
+  BE(f_Design,			EDIF_TOK_DESIGN),
+  BE(f_Designator,		EDIF_TOK_DESIGNATOR),
+  BE(f_Difference,		EDIF_TOK_DIFFERENCE),
+  BE(f_Display,			EDIF_TOK_DISPLAY),
+  BE(f_Dominates,		EDIF_TOK_DOMINATES),
+  BE(f_Dot,			EDIF_TOK_DOT),
+  BE(f_Duration,		EDIF_TOK_DURATION),
+  BE(f_EnclosureDistance,	EDIF_TOK_ENCLOSUREDISTANCE),
+  BE(f_Entry,			EDIF_TOK_ENTRY),
+  BE(f_Exactly,			EDIF_TOK_EXACTLY),
+  BE(f_External,		EDIF_TOK_EXTERNAL),
+  BE(f_Fabricate,		EDIF_TOK_FABRICATE),
+  BE(f_Figure,			EDIF_TOK_FIGURE),
+  BE(f_FigureArea,		EDIF_TOK_FIGUREAREA),
+  BE(f_FigureGroup,		EDIF_TOK_FIGUREGROUP),
+  BE(f_FigureGroupObject,	EDIF_TOK_FIGUREGROUPOBJECT),
+  BE(f_FigureGroupOverride,	EDIF_TOK_FIGUREGROUPOVERRIDE),
+  BE(f_FigureGroupRef,		EDIF_TOK_FIGUREGROUPREF),
+  BE(f_FigurePerimeter,		EDIF_TOK_FIGUREPERIMETER),
+  BE(f_FigureWidth,		EDIF_TOK_FIGUREWIDTH),
+  BE(f_FillPattern,		EDIF_TOK_FILLPATTERN),
+  BE(f_Follow,			EDIF_TOK_FOLLOW),
+  BE(f_ForbiddenEvent,		EDIF_TOK_FORBIDDENEVENT),
+  BE(f_GlobalPortRef,		EDIF_TOK_GLOBALPORTREF),
+  BE(f_GreaterThan,		EDIF_TOK_GREATERTHAN),
+  BE(f_GridMap,			EDIF_TOK_GRIDMAP),
+  BE(f_IncludeFigureGroup,	EDIF_TOK_INCLUDEFIGUREGROUP),
+  BE(f_Instance,		EDIF_TOK_INSTANCE),
+  BE(f_InstanceBackAnnotate,	EDIF_TOK_INSTANCEBACKANNOTATE),
+  BE(f_InstanceGroup,		EDIF_TOK_INSTANCEGROUP),
+  BE(f_InstanceMap,		EDIF_TOK_INSTANCEMAP),
+  BE(f_InstanceRef,		EDIF_TOK_INSTANCEREF),
+  BE(f_Integer,			EDIF_TOK_INTEGER),
+  BE(f_IntegerDisplay,		EDIF_TOK_INTEGERDISPLAY),
+  BE(f_InterFigureGroupSpacing,	EDIF_TOK_INTERFIGUREGROUPSPACING),
+  BE(f_Interface,		EDIF_TOK_INTERFACE),
+  BE(f_Intersection,		EDIF_TOK_INTERSECTION),
+  BE(f_IntraFigureGroupSpacing,	EDIF_TOK_INTRAFIGUREGROUPSPACING),
+  BE(f_Inverse,			EDIF_TOK_INVERSE),
+  BE(f_Joined,			EDIF_TOK_JOINED),
+  BE(f_KeywordDisplay,		EDIF_TOK_KEYWORDDISPLAY),
+  BE(f_KeywordMap,		EDIF_TOK_KEYWORDMAP),
+  BE(f_LessThan,		EDIF_TOK_LESSTHAN),
+  BE(f_Library,			EDIF_TOK_LIBRARY),
+  BE(f_LibraryRef,		EDIF_TOK_LIBRARYREF),
+  BE(f_ListOfNets,		EDIF_TOK_LISTOFNETS),
+  BE(f_ListOfPorts,		EDIF_TOK_LISTOFPORTS),
+  BE(f_LoadDelay,		EDIF_TOK_LOADDELAY),
+  BE(f_LogicAssign,		EDIF_TOK_LOGICASSIGN),
+  BE(f_LogicInput,		EDIF_TOK_LOGICINPUT),
+  BE(f_LogicList,		EDIF_TOK_LOGICLIST),
+  BE(f_LogicMapInput,		EDIF_TOK_LOGICMAPINPUT),
+  BE(f_LogicMapOutput,		EDIF_TOK_LOGICMAPOUTPUT),
+  BE(f_LogicOneOf,		EDIF_TOK_LOGICONEOF),
+  BE(f_LogicOutput,		EDIF_TOK_LOGICOUTPUT),
+  BE(f_LogicPort,		EDIF_TOK_LOGICPORT),
+  BE(f_LogicRef,		EDIF_TOK_LOGICREF),
+  BE(f_LogicValue,		EDIF_TOK_LOGICVALUE),
+  BE(f_LogicWaveform,		EDIF_TOK_LOGICWAVEFORM),
+  BE(f_Maintain,		EDIF_TOK_MAINTAIN),
+  BE(f_Match,			EDIF_TOK_MATCH),
+  BE(f_Member,			EDIF_TOK_MEMBER),
+  BE(f_MiNoMax,			EDIF_TOK_MINOMAX),
+  BE(f_MiNoMaxDisplay,		EDIF_TOK_MINOMAXDISPLAY),
+  BE(f_Mnm,			EDIF_TOK_MNM),
+  BE(f_MultipleValueSet,	EDIF_TOK_MULTIPLEVALUESET),
+  BE(f_MustJoin,		EDIF_TOK_MUSTJOIN),
+  BE(f_Name,			EDIF_TOK_NAME),
+  BE(f_Net,			EDIF_TOK_NET),
+  BE(f_NetBackAnnotate,		EDIF_TOK_NETBACKANNOTATE),
+  BE(f_NetBundle,		EDIF_TOK_NETBUNDLE),
+  BE(f_NetDelay,		EDIF_TOK_NETDELAY),
+  BE(f_NetGroup,		EDIF_TOK_NETGROUP),
+  BE(f_NetMap,			EDIF_TOK_NETMAP),
+  BE(f_NetRef,			EDIF_TOK_NETREF),
+  BE(f_NonPermutable,		EDIF_TOK_NONPERMUTABLE),
+  BE(f_NotAllowed,		EDIF_TOK_NOTALLOWED),
+  BE(f_NotchSpacing,		EDIF_TOK_NOTCHSPACING),
+  BE(f_Number,			EDIF_TOK_NUMBER),
+  BE(f_NumberDefinition,	EDIF_TOK_NUMBERDEFINITION),
+  BE(f_NumberDisplay,		EDIF_TOK_NUMBERDISPLAY),
+  BE(f_OffPageConnector,	EDIF_TOK_OFFPAGECONNECTOR),
+  BE(f_OffsetEvent,		EDIF_TOK_OFFSETEVENT),
+  BE(f_OpenShape,		EDIF_TOK_OPENSHAPE),
+  BE(f_Origin,			EDIF_TOK_ORIGIN),
+  BE(f_OverhangDistance,	EDIF_TOK_OVERHANGDISTANCE),
+  BE(f_OverlapDistance,		EDIF_TOK_OVERLAPDISTANCE),
+  BE(f_Oversize,		EDIF_TOK_OVERSIZE),
+  BE(f_Page,			EDIF_TOK_PAGE),
+  BE(f_PageSize,		EDIF_TOK_PAGESIZE),
+  BE(f_Parameter,		EDIF_TOK_PARAMETER),
+  BE(f_ParameterAssign,		EDIF_TOK_PARAMETERASSIGN),
+  BE(f_ParameterDisplay,	EDIF_TOK_PARAMETERDISPLAY),
+  BE(f_Path,			EDIF_TOK_PATH),
+  BE(f_PathDelay,		EDIF_TOK_PATHDELAY),
+  BE(f_Permutable,		EDIF_TOK_PERMUTABLE),
+  BE(f_PhysicalDesignRule,	EDIF_TOK_PHYSICALDESIGNRULE),
+  BE(f_Plug,			EDIF_TOK_PLUG),
+  BE(f_Point,			EDIF_TOK_POINT),
+  BE(f_PointDisplay,		EDIF_TOK_POINTDISPLAY),
+  BE(f_PointList,		EDIF_TOK_POINTLIST),
+  BE(f_Polygon,			EDIF_TOK_POLYGON),
+  BE(f_Port,			EDIF_TOK_PORT),
+  BE(f_PortBackAnnotate,	EDIF_TOK_PORTBACKANNOTATE),
+  BE(f_PortBundle,		EDIF_TOK_PORTBUNDLE),
+  BE(f_PortDelay,		EDIF_TOK_PORTDELAY),
+  BE(f_PortGroup,		EDIF_TOK_PORTGROUP),
+  BE(f_PortImplementation,	EDIF_TOK_PORTIMPLEMENTATION),
+  BE(f_PortInstance,		EDIF_TOK_PORTINSTANCE),
+  BE(f_PortList,		EDIF_TOK_PORTLIST),
+  BE(f_PortListAlias,		EDIF_TOK_PORTLISTALIAS),
+  BE(f_PortMap,			EDIF_TOK_PORTMAP),
+  BE(f_PortRef,			EDIF_TOK_PORTREF),
+  BE(f_Program,			EDIF_TOK_PROGRAM),
+  BE(f_Property,		EDIF_TOK_PROPERTY),
+  BE(f_PropertyDisplay,		EDIF_TOK_PROPERTYDISPLAY),
+  BE(f_ProtectionFrame,		EDIF_TOK_PROTECTIONFRAME),
+  BE(f_RangeVector,		EDIF_TOK_RANGEVECTOR),
+  BE(f_Rectangle,		EDIF_TOK_RECTANGLE),
+  BE(f_RectangleSize,		EDIF_TOK_RECTANGLESIZE),
+  BE(f_Rename,			EDIF_TOK_RENAME),
+  BE(f_Resolves,		EDIF_TOK_RESOLVES),
+  BE(f_Scale,			EDIF_TOK_SCALE),
+  BE(f_Section,			EDIF_TOK_SECTION),
+  BE(f_Shape,			EDIF_TOK_SHAPE),
+  BE(f_Simulate,		EDIF_TOK_SIMULATE),
+  BE(f_SimulationInfo,		EDIF_TOK_SIMULATIONINFO),
+  BE(f_SingleValueSet,		EDIF_TOK_SINGLEVALUESET),
+  BE(f_Site,			EDIF_TOK_SITE),
+  BE(f_Socket,			EDIF_TOK_SOCKET),
+  BE(f_SocketSet,		EDIF_TOK_SOCKETSET),
+  BE(f_Status,			EDIF_TOK_STATUS),
+  BE(f_Steady,			EDIF_TOK_STEADY),
+  BE(f_String,			EDIF_TOK_STRING),
+  BE(f_StringDisplay,		EDIF_TOK_STRINGDISPLAY),
+  BE(f_Strong,			EDIF_TOK_STRONG),
+  BE(f_Symbol,			EDIF_TOK_SYMBOL),
+  BE(f_Symmetry,		EDIF_TOK_SYMMETRY),
+  BE(f_Table,			EDIF_TOK_TABLE),
+  BE(f_TableDefault,		EDIF_TOK_TABLEDEFAULT),
+  BE(f_Technology,		EDIF_TOK_TECHNOLOGY),
+  BE(f_TimeInterval,		EDIF_TOK_TIMEINTERVAL),
+  BE(f_Timing,			EDIF_TOK_TIMING),
+  BE(f_Transform,		EDIF_TOK_TRANSFORM),
+  BE(f_Transition,		EDIF_TOK_TRANSITION),
+  BE(f_Trigger,			EDIF_TOK_TRIGGER),
+  BE(f_Union,			EDIF_TOK_UNION),
+  BE(f_View,			EDIF_TOK_VIEW),
+  BE(f_ViewList,		EDIF_TOK_VIEWLIST),
+  BE(f_ViewMap,			EDIF_TOK_VIEWMAP),
+  BE(f_ViewRef,			EDIF_TOK_VIEWREF),
+  BE(f_Visible,			EDIF_TOK_VISIBLE),
+  BE(f_VoltageMap,		EDIF_TOK_VOLTAGEMAP),
+  BE(f_WaveValue,		EDIF_TOK_WAVEVALUE),
+  BE(f_Weak,			EDIF_TOK_WEAK),
+  BE(f_WeakJoined,		EDIF_TOK_WEAKJOINED),
+  BE(f_When,			EDIF_TOK_WHEN),
+  BE(f_Written,			EDIF_TOK_WRITTEN)
+};
+static int BinderDefSize = sizeof(BinderDef) / sizeof(Binder);
+/*
+ *	Keyword table:
+ *
+ *	  This hash table holds all strings which may have to be matched
+ *	to. WARNING: it is assumed that there is no overlap of the 'token'
+ *	and 'context' strings.
+ */
+typedef struct Keyword {
+  struct Keyword *Next;	 	/* pointer to next entry */
+  const char *String;			/* pointer to associated string */
+} Keyword;
+#define	KEYWORD_HASH	127	/* hash table size */
+static Keyword *KeywordTable[KEYWORD_HASH];
+/*
+ *	Enter keyword:
+ *
+ *	  The passed string is entered into the keyword hash table.
+ */
+static void EnterKeyword(const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register Keyword *key;
+  register unsigned int hsh;
+  register const char *cp;
+  /*
+   *	Create the hash code, and add an entry to the table.
+   */
+  for (hsh = 0, cp = str; *cp; hsh += hsh + *cp++);
+  hsh %= KEYWORD_HASH;
+  key = (Keyword *) Malloc(sizeof(Keyword));
+  key->Next = KeywordTable[hsh];
+  (KeywordTable[hsh] = key)->String = str;
+}
+/*
+ *	Find keyword:
+ *
+ *	  The passed string is located within the keyword table. If an
+ *	entry exists, then the value of the keyword string is returned. This
+ *	is real useful for doing string comparisons by pointer value later.
+ *	If there is no match, a NULL is returned.
+ */
+static const char *FindKeyword(const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register Keyword *wlk,*owk;
+  register unsigned int hsh;
+  register char *cp;
+  char lower[IDENT_LENGTH + 1];
+  /*
+   *	Create a lower case copy of the string.
+   */
+  for (cp = lower; *str;)
+    if (isupper( (int) *str))
+      *cp++ = tolower( (int) *str++);
+    else
+      *cp++ = *str++;
+  *cp = '\0';
+  /*
+   *	Search the hash table for a match.
+   */
+  for (hsh = 0, cp = lower; *cp; hsh += hsh + *cp++);
+  hsh %= KEYWORD_HASH;
+  for (owk = NULL, wlk = KeywordTable[hsh]; wlk; wlk = (owk = wlk)->Next)
+    if (!strcmp(wlk->String,lower)){
+      /*
+       *	Readjust the LRU.
+       */
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = KeywordTable[hsh];
+      	KeywordTable[hsh] = wlk;
+      }
+      return (wlk->String);
+    }
+  return (NULL);
+}
+/*
+ *	Token hash table.
+ */
+#define	TOKEN_HASH	51
+static Token *TokenHash[TOKEN_HASH];
+/*
+ *	Find token:
+ *
+ *	  A pointer to the token of the passed code is returned. If
+ *	no such beastie is present a NULL is returned instead.
+ */
+static Token *FindToken(register int cod)
+{
+  /*
+   *	Locals.
+   */
+  register Token *wlk,*owk;
+  register unsigned int hsh;
+  /*
+   *	Search the hash table for a matching token.
+   */
+  hsh = cod % TOKEN_HASH;
+  for (owk = NULL, wlk = TokenHash[hsh]; wlk; wlk = (owk = wlk)->Next)
+    if (cod == wlk->Code){
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = TokenHash[hsh];
+      	TokenHash[hsh] = wlk;
+      }
+      break;
+    }
+  return (wlk);
+}
+/*
+ *	Context hash table.
+ */
+#define	CONTEXT_HASH	127
+static Context *ContextHash[CONTEXT_HASH];
+/*
+ *	Find context:
+ *
+ *	  A pointer to the context of the passed code is returned. If
+ *	no such beastie is present a NULL is returned instead.
+ */
+static Context *FindContext(register int cod)
+{
+  /*
+   *	Locals.
+   */
+  register Context *wlk,*owk;
+  register unsigned int hsh;
+  /*
+   *	Search the hash table for a matching context.
+   */
+  hsh = cod % CONTEXT_HASH;
+  for (owk = NULL, wlk = ContextHash[hsh]; wlk; wlk = (owk = wlk)->Next)
+    if (cod == wlk->Code){
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = ContextHash[hsh];
+      	ContextHash[hsh] = wlk;
+      }
+      break;
+    }
+  return (wlk);
+}
+/*
+ *	Token stacking variables.
+ */
+#ifdef	DEBUG
+#define	TS_DEPTH	8
+#define	TS_MASK		(TS_DEPTH - 1)
+static unsigned int TSP = 0;		/* token stack pointer */
+static char *TokenStack[TS_DEPTH];	/* token name strings */
+static short TokenType[TS_DEPTH];	/* token types */
+/*
+ *	Stack:
+ *
+ *	  Add a token to the debug stack. The passed string and type are
+ *	what is to be pushed.
+ */
+static void Stack(char * str, int typ)
+{
+  /*
+   *	Free any previous string, then push.
+   */
+  if (TokenStack[TSP & TS_MASK])
+    Free(TokenStack[TSP & TS_MASK]);
+  TokenStack[TSP & TS_MASK] = strcpy((char *)Malloc(strlen(str) + 1),str);
+  TokenType[TSP & TS_MASK] = typ;
+  TSP += 1;
+}
+/*
+ *	Dump stack:
+ *
+ *	  This displays the last set of accumulated tokens.
+ */
+static void DumpStack()
+{
+  /*
+   *	Locals.
+   */
+  register int i;
+  register Context *cxt;
+  register Token *tok;
+  register char *nam;
+  FILE *Error = stderr;
+  /*
+   *	Run through the list displaying the oldest first.
+   */
+  fprintf(Error,"\n\n");
+  for (i = 0; i < TS_DEPTH; i += 1)
+    if (TokenStack[(TSP + i) & TS_MASK]){
+      /*
+       *	Get the type name string.
+       */
+      if ((cxt = FindContext(TokenType[(TSP + i) & TS_MASK])) != NULL)
+        nam = cxt->Name;
+      else if ((tok = FindToken(TokenType[(TSP + i) & TS_MASK])) != NULL)
+        nam = tok->Name;
+      else switch (TokenType[(TSP + i) & TS_MASK]){
+      	case EDIF_TOK_IDENT:	nam = "IDENT";		break;
+      	case EDIF_TOK_INT:	nam = "INT";		break;
+      	case EDIF_TOK_KEYWORD:	nam = "KEYWORD";	break;
+      	case EDIF_TOK_STR:	nam = "STR";		break;
+      	default:	nam = "?";		break;
+      }
+      /*
+       *	Now print the token state.
+       */
+      fprintf(Error,"%2d %-16.16s '%s'\n",TS_DEPTH - i,nam,
+        TokenStack[(TSP + i) & TS_MASK]);
+    }
+  fprintf(Error,"\n");
+}
+#else
+#define	Stack(s,t)
+#endif	/* DEBUG */
+/*
+ *	Parser state variables.
+ */
+static FILE *Input = NULL;		/* input stream */
+static FILE *Error = NULL;		/* error stream */
+static char *InFile;			/* file name on the input stream */
+static long LineNumber;			/* current input line number */
+static ContextCar *CSP = NULL;		/* top of context stack */
+static char yytext[IDENT_LENGTH + 1];	/* token buffer */
+static char CharBuf[IDENT_LENGTH + 1];	/* garbage buffer */
+/*
+ *	yyerror:
+ *
+ *	  Standard error reporter, it prints out the passed string
+ *	preceeded by the current filename and line number.
+ */
+static void yyerror(const char *ers)
+{
+#ifdef	DEBUG
+  DumpStack();
+#endif	/* DEBUG */
+  fprintf(Error,"%s, line %ld: %s\n",InFile,LineNumber,ers);
+}
+/*
+ *	String bucket definitions.
+ */
+#define	BUCKET_SIZE	64
+typedef struct Bucket {
+  struct Bucket *Next;			/* pointer to next bucket */
+  int Index;				/* pointer to next free slot */
+  char Data[BUCKET_SIZE];		/* string data */
+} Bucket;
+static Bucket *CurrentBucket = NULL;	/* string bucket list */
+static int StringSize = 0;		/* current string length */
+/*
+ *	Push string:
+ *
+ *	  This adds the passed charater to the current string bucket.
+ */
+static void PushString(char chr)
+{
+  /*
+   *	Locals.
+   */
+  register Bucket *bck;
+  /*
+   *	Make sure there is room for the push.
+   */
+  if ((bck = CurrentBucket)->Index >= BUCKET_SIZE){
+    bck = (Bucket *) Malloc(sizeof(Bucket));
+    bck->Next = CurrentBucket;
+    (CurrentBucket = bck)->Index = 0;
+  }
+  /*
+   *	Push the character.
+   */
+  bck->Data[bck->Index++] = chr;
+  StringSize += 1;
+}
+/*
+ *	Form string:
+ *
+ *	  This converts the current string bucket into a real live string,
+ *	whose pointer is returned.
+ */
+static char *FormString()
+{
+  /*
+   *	Locals.
+   */
+  register Bucket *bck;
+  register char *cp;
+  /*
+   *	Allocate space for the string, set the pointer at the end.
+   */
+  cp = (char *) Malloc(StringSize + 1);
+
+  cp += StringSize;
+  *cp-- = '\0';
+  /*
+   *	Yank characters out of the bucket.
+   */
+  for (bck = CurrentBucket; bck->Index || bck->Next;){
+    if (!bck->Index){
+      CurrentBucket = bck->Next;
+      Free(bck);
+      bck = CurrentBucket;
+    }
+    *cp-- = bck->Data[--bck->Index];
+  }
+  /* reset buffer size  to zero */
+  StringSize =0;
+  return (cp + 1);
+}
+/*
+ *	Parse EDIF:
+ *
+ *	  This builds the context tree and then calls the real parser.
+ *	It is passed two file streams, the first is where the input comes
+ *	from; the second is where error messages get printed.
+ */
+void ParseEDIF(char* filename,FILE* err)
+{
+  /*
+   *	Locals.
+   */
+  register int i;
+  static int ContextDefined = 1;
+  /*
+   *	Set up the file state to something useful.
+   */
+  InFile = filename;
+  Input = fopen(filename,"r");
+  Error = err;
+  LineNumber = 1;
+  /*
+   *	Define both the enabled token and context strings.
+   */
+  if (ContextDefined){
+    for (i = TokenDefSize; i--; EnterKeyword(TokenDef[i].Name)){
+      register unsigned int hsh;
+      hsh = TokenDef[i].Code % TOKEN_HASH;
+      TokenDef[i].Next = TokenHash[hsh];
+      TokenHash[hsh] = &TokenDef[i];
+    }
+    for (i = ContextDefSize; i--; EnterKeyword(ContextDef[i].Name)){
+      register unsigned int hsh;
+      hsh = ContextDef[i].Code % CONTEXT_HASH;
+      ContextDef[i].Next = ContextHash[hsh];
+      ContextHash[hsh] = &ContextDef[i];
+    }
+    /*
+     *	Build the context tree.
+     */
+    for (i = BinderDefSize; i--;){
+      register Context *cxt;
+      register int j;
+      /*
+       *	Define the current context to have carriers bound to it.
+       */
+      cxt = FindContext(BinderDef[i].Origin);
+      for (j = BinderDef[i].FollowerSize; j--;){
+        register ContextCar *cc;
+        /*
+         *	Add carriers to the current context.
+         */
+        cc = (ContextCar *) Malloc(sizeof(ContextCar));
+        cc->Next = cxt->Context;
+        (cxt->Context = cc)->Context =
+          FindContext(PCB_ABS(BinderDef[i].Follower[j]));
+        cc->u.Single = BinderDef[i].Follower[j] < 0;
+      }
+    }
+    /*
+     *	Build the token tree.
+     */
+    for (i = TieDefSize; i--;){
+      register Context *cxt;
+      register int j;
+      /*
+       *	Define the current context to have carriers bound to it.
+       */
+      cxt = FindContext(TieDef[i].Origin);
+      for (j = TieDef[i].EnableSize; j--;){
+        register TokenCar *tc;
+        /*
+         *	Add carriers to the current context.
+         */
+        tc = (TokenCar *) Malloc(sizeof(TokenCar));
+        tc->Next = cxt->Token;
+        (cxt->Token = tc)->Token = FindToken(TieDef[i].Enable[j]);
+      }
+    }
+    /*
+     *	Put a bogus context on the stack which has 'EDIF' as its
+     *	follower.
+     */
+    CSP = (ContextCar *) Malloc(sizeof(ContextCar));
+    CSP->Next = NULL;
+    CSP->Context = FindContext(0);
+    CSP->u.Used = NULL;
+    ContextDefined = 0;
+  }
+  /*
+   *	Create an initial, empty string bucket.
+   */
+  CurrentBucket = (Bucket *) Malloc(sizeof(Bucket));
+  CurrentBucket->Next = 0;
+  CurrentBucket->Index = 0;
+  /*
+   *	Fill the token stack with NULLs if debugging is enabled.
+   */
+#ifdef	DEBUG
+  for (i = TS_DEPTH; i--; TokenStack[i] = NULL)
+    if (TokenStack[i])
+      Free(TokenStack[i]);
+  TSP = 0;
+#endif	/* DEBUG */
+  /*
+   *	Go parse things!
+   */
+  edifparse();
+}
+/*
+ *	Match token:
+ *
+ *	  The passed string is looked up in the current context's token
+ *	list to see if it is enabled. If so the token value is returned,
+ *	if not then zero.
+ */
+static int MatchToken(register const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register TokenCar *wlk,*owk;
+  /*
+   *	Convert the string to the proper form, then search the token
+   *	carrier list for a match.
+   */
+  str = FindKeyword(str);
+  for (owk = NULL, wlk = CSP->Context->Token; wlk; wlk = (owk = wlk)->Next)
+    if (str == wlk->Token->Name){
+      if (owk){
+        owk->Next = wlk->Next;
+        wlk->Next = CSP->Context->Token;
+        CSP->Context->Token = wlk;
+      }
+      return (wlk->Token->Code);
+    }
+  return (0);
+}
+/*
+ *	Match context:
+ *
+ *	  If the passed keyword string is within the current context, the
+ *	new context is pushed and token value is returned. A zero otherwise.
+ */
+static int MatchContext(register const char * str)
+{
+  /*
+   *	Locals.
+   */
+  register ContextCar *wlk,*owk;
+  /*
+   *	See if the context is present.
+   */
+  str = FindKeyword(str);
+  for (owk = NULL, wlk = CSP->Context->Context; wlk; wlk = (owk = wlk)->Next)
+    if (str == wlk->Context->Name){
+      if (owk){
+      	owk->Next = wlk->Next;
+      	wlk->Next = CSP->Context->Context;
+      	CSP->Context->Context = wlk;
+      }
+      /*
+       *	If a single context, make sure it isn't already used.
+       */
+      if (wlk->u.Single){
+      	register UsedCar *usc;
+      	for (usc = CSP->u.Used; usc; usc = usc->Next)
+      	  if (usc->Code == wlk->Context->Code)
+      	    break;
+      	if (usc){
+      	  sprintf(CharBuf,"'%s' is used more than once within '%s'",
+      	    str,CSP->Context->Name);
+      	  yyerror(CharBuf);
+      	} else {
+      	  usc = (UsedCar *) Malloc(sizeof(UsedCar));
+      	  usc->Next = CSP->u.Used;
+      	  (CSP->u.Used = usc)->Code = wlk->Context->Code;
+      	}
+      }
+      /*
+       *	Push the new context.
+       */
+      owk = (ContextCar *) Malloc(sizeof(ContextCar));
+      owk->Next = CSP;
+      (CSP = owk)->Context = wlk->Context;
+      owk->u.Used = NULL;
+      return (wlk->Context->Code);
+    }
+  return (0);
+}
+/*
+ *	PopC:
+ *
+ *	  This pops the current context.
+ */
+static void PopC()
+{
+  /*
+   *	Locals.
+   */
+  register UsedCar *usc;
+  register ContextCar *csp;
+  /*
+   *	Release single markers and pop context.
+   */
+  while ( (usc = CSP->u.Used) ){
+    CSP->u.Used = usc->Next;
+    Free(usc);
+  }
+  csp = CSP->Next;
+  Free(CSP);
+  CSP = csp;
+}
+/*
+ *	Lexical analyzer states.
+ */
+#define	L_START		0
+#define	L_INT		1
+#define	L_IDENT		2
+#define	L_KEYWORD	3
+#define	L_STRING	4
+#define	L_KEYWORD2	5
+#define	L_ASCIICHAR	6
+#define	L_ASCIICHAR2	7
+/*
+ *	yylex:
+ *
+ *	  This is the lexical analyzer called by the YACC/BISON parser.
+ *	It returns a pretty restricted set of token types and does the
+ *	context movement when acceptable keywords are found. The token
+ *	value returned is a NULL terminated string to allocated storage
+ *	(ie - it should get released some time) with some restrictions.
+ *	  The token value for integers is strips a leading '+' if present.
+ *	String token values have the leading and trailing '"'-s stripped.
+ *	'%' conversion characters in string values are passed converted.
+ *	The '(' and ')' characters do not have a token value.
+ */
+static int yylex()
+{
+  /*
+   *	Locals.
+   */
+  register int c,s,l;
+  /*
+   *	Keep on sucking up characters until we find something which
+   *	explicitly forces us out of this function.
+   */
+  for (s = L_START, l = 0; 1;){
+    yytext[l++] = c = Getc(Input);
+    switch (s){
+      /*
+       *	Starting state, look for something resembling a token.
+       */
+      case L_START:
+        if (isdigit(c) || c == '-')
+          s = L_INT;
+        else if (isalpha(c) || c == '&')
+          s = L_IDENT;
+        else if (isspace(c)){
+          if (c == '\n')
+            LineNumber += 1;
+          l = 0;
+        } else if (c == '('){
+          l = 0;
+          s = L_KEYWORD;
+        } else if (c == '"')
+          s = L_STRING;
+        else if (c == '+'){
+          l = 0;				/* strip '+' */
+          s = L_INT;
+        } else if (c == EOF)
+          return ('\0');
+        else {
+          yytext[1] = '\0';
+          Stack(yytext,c);
+          return (c);
+        }
+        break;
+      /*
+       *	Suck up the integer digits.
+       */
+      case L_INT:
+        if (isdigit(c))
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        yylval.s = strcpy((char *)Malloc(l + 1),yytext);
+        Stack(yytext,EDIF_TOK_INT);
+        return (EDIF_TOK_INT);
+      /*
+       *	Grab an identifier, see if the current context enables
+       *	it with a specific token value.
+       */
+      case L_IDENT:
+        if (isalpha(c) || isdigit(c) || c == '_')
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        if (CSP->Context->Token && (c = MatchToken(yytext))){
+          Stack(yytext,c);
+          return (c);
+        }
+        yylval.s = strcpy((char *)Malloc(l + 1),yytext);
+        Stack(yytext, EDIF_TOK_IDENT);
+        return (EDIF_TOK_IDENT);
+      /*
+       *	Scan until you find the start of an identifier, discard
+       *	any whitespace found. On no identifier, return a '('.
+       */
+      case L_KEYWORD:
+        if (isalpha(c) || c == '&'){
+          s = L_KEYWORD2;
+          break;
+        } else if (isspace(c)){
+          l = 0;
+          break;
+        }
+        Ungetc(c);
+        Stack("(",'(');
+        return ('(');
+      /*
+       *	Suck up the keyword identifier, if it matches the set of
+       *	allowable contexts then return its token value and push
+       *	the context, otherwise just return the identifier string.
+       */
+      case L_KEYWORD2:
+        if (isalpha(c) || isdigit(c) || c == '_')
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        if ( (c = MatchContext(yytext)) ){
+          Stack(yytext,c);
+          return (c);
+        }
+        yylval.s = strcpy((char *)Malloc(l + 1),yytext);
+        Stack(yytext, EDIF_TOK_KEYWORD);
+        return (EDIF_TOK_KEYWORD);
+      /*
+       *	Suck up string characters but once resolved they should
+       *	be deposited in the string bucket because they can be
+       *	arbitrarily long.
+       */
+      case L_STRING:
+        if (c == '\n')
+          LineNumber += 1;
+        else if (c == '\r')
+          ;
+        else if (c == '"' || c == EOF){
+          yylval.s = FormString();
+          Stack(yylval.s, EDIF_TOK_STR);
+          return (EDIF_TOK_STR);
+        } else if (c == '%')
+          s = L_ASCIICHAR;
+        else
+          PushString(c);
+        l = 0;
+        break;
+      /*
+       *	Skip white space and look for integers to be pushed
+       *	as characters.
+       */
+      case L_ASCIICHAR:
+        if (isdigit(c)){
+          s = L_ASCIICHAR2;
+          break;
+        } else if (c == '%' || c == EOF)
+          s = L_STRING;
+        else if (c == '\n')
+          LineNumber += 1;
+        l = 0;
+        break;
+      /*
+       *	Convert the accumulated integer into a char and push.
+       */
+      case L_ASCIICHAR2:
+        if (isdigit(c))
+          break;
+        Ungetc(c);
+        yytext[--l] = '\0';
+        PushString(atoi(yytext));
+        s = L_ASCIICHAR;
+        l = 0;
+        break;
+    }
+  }
+}
+
+int ReadEdifNetlist(char *filename)
+{
+	Message(PCB_MSG_INFO, _("Importing edif netlist %s\n"), filename);
+	ParseEDIF(filename, NULL);
+
+	return 0;
+}
diff --git a/src_plugins/import_edif/edif_parse.h b/src_plugins/import_edif/edif_parse.h
new file mode 100644
index 0000000..61206ea
--- /dev/null
+++ b/src_plugins/import_edif/edif_parse.h
@@ -0,0 +1,29 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  Copyright (C) 2006 Jeffry C Bailey
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef	PCB_EDIF_PARSE_H
+#define	PCB_EDIF_PARSE_H
+
+void ParseEDIF(char *filename, FILE * err);
+
+
+#endif /* PCB_EDIF_PARSE_H */
diff --git a/src_plugins/import_edif/import_edif.c b/src_plugins/import_edif/import_edif.c
new file mode 100644
index 0000000..7997251
--- /dev/null
+++ b/src_plugins/import_edif/import_edif.c
@@ -0,0 +1,93 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This module, io_kicad_legacy, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "plugins.h"
+#include "plug_import.h"
+#include "netlist.h"
+#include "rats_patch.h"
+
+/* for pcb_sort_library only */
+#include "plug_io.h"
+
+
+static plug_import_t import_edif;
+
+int edif_support_prio(plug_import_t *ctx, unsigned int aspects, FILE *fp, const char *filename)
+{
+	char buf[65];
+	int len;
+	char *p;
+
+	if (aspects != IMPORT_ASPECT_NETLIST)
+		return 0; /* only pure netlist import is supported */
+
+	if (fp == NULL)
+		return 0; /* only importing from a file is supported */
+
+	/* If the header contains "edif", it is supported */
+	len = fread(buf, 1, sizeof(buf) - 1, fp);
+	buf[len] = '\0';
+	for(p = buf; *p != '\0'; p++)
+		*p = tolower((int) *p);
+	if (strstr(buf, "edif") != NULL)
+		return 100;
+
+	/* Else don't even attempt to load it */
+	return 0;
+}
+
+
+extern int ReadEdifNetlist(char *filename);
+static int edif_import(plug_import_t *ctx, unsigned int aspects, const char *fn)
+{
+	int ret = ReadEdifNetlist((char *)fn);
+	if (ret == 0) {
+		pcb_sort_netlist();
+		rats_patch_make_edited(PCB);
+	}
+	return ret;
+}
+
+static void hid_import_edif_uninit(void)
+{
+}
+
+pcb_uninit_t hid_import_edif_init(void)
+{
+
+	/* register the IO hook */
+	import_edif.plugin_data = NULL;
+
+	import_edif.fmt_support_prio = edif_support_prio;
+	import_edif.import           = edif_import;
+
+	HOOK_REGISTER(plug_import_t, plug_import_chain, &import_edif);
+
+	return hid_import_edif_uninit;
+}
+
diff --git a/src_plugins/import_netlist/Makefile b/src_plugins/import_netlist/Makefile
new file mode 100644
index 0000000..e5bb6ae
--- /dev/null
+++ b/src_plugins/import_netlist/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_import_netlist
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/import_netlist/Plug.tmpasm b/src_plugins/import_netlist/Plug.tmpasm
new file mode 100644
index 0000000..85e6954
--- /dev/null
+++ b/src_plugins/import_netlist/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {import_netlist}
+append /local/pcb/mod/OBJS [@ $(PLUGDIR)/import_netlist/import_netlist.o @]
+append /local/pcb/mod/YACC {$(PLUGDIR)/import_netlist/netlist}
+
+switch /local/pcb/import_netlist/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/import_netlist/README b/src_plugins/import_netlist/README
new file mode 100644
index 0000000..d3590a5
--- /dev/null
+++ b/src_plugins/import_netlist/README
@@ -0,0 +1,5 @@
+Import plugin for netlists in the classic pcb netlist format.
+
+#state: works
+#default: buildin
+#implements: import
diff --git a/src_plugins/import_netlist/import_netlist.c b/src_plugins/import_netlist/import_netlist.c
new file mode 100644
index 0000000..e567a91
--- /dev/null
+++ b/src_plugins/import_netlist/import_netlist.c
@@ -0,0 +1,188 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
+ *  Copyright (C) 2015,2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+#include "global.h"
+#include "plugins.h"
+#include "plug_io.h"
+#include "plug_import.h"
+#include "conf_core.h"
+#include "error.h"
+#include "misc.h"
+#include "data.h"
+#include "rats_patch.h"
+#include "compat_misc.h"
+
+static plug_import_t import_netlist;
+
+
+#define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
+		|| (x) == '\0')
+
+/* ---------------------------------------------------------------------------
+ * Read in a netlist and store it in the netlist menu 
+ */
+static int ReadNetlist(const char *filename)
+{
+	char *command = NULL;
+	char inputline[MAX_NETLIST_LINE_LENGTH + 1];
+	char temp[MAX_NETLIST_LINE_LENGTH + 1];
+	FILE *fp;
+	LibraryMenuTypePtr menu = NULL;
+	LibraryEntryTypePtr entry;
+	int i, j, lines, kind;
+	pcb_bool continued;
+	int used_popen = 0;
+
+	if (!filename)
+		return (1);									/* nothing to do */
+
+	Message(PCB_MSG_DEFAULT, _("Importing PCB netlist %s\n"), filename);
+
+	if (EMPTY_STRING_P(conf_core.rc.rat_command)) {
+		fp = fopen(filename, "r");
+		if (!fp) {
+			Message(PCB_MSG_DEFAULT, "Cannot open %s for reading", filename);
+			return 1;
+		}
+	}
+	else {
+		used_popen = 1;
+		command = EvaluateFilename(conf_core.rc.rat_command, conf_core.rc.rat_path, filename, NULL);
+
+		/* open pipe to stdout of command */
+		if (*command == '\0' || (fp = popen(command, "r")) == NULL) {
+			PopenErrorMessage(command);
+			free(command);
+			return (1);
+		}
+		free(command);
+	}
+	lines = 0;
+	/* kind = 0  is net name
+	 * kind = 1  is route style name
+	 * kind = 2  is connection
+	 */
+	kind = 0;
+	while (fgets(inputline, MAX_NETLIST_LINE_LENGTH, fp)) {
+		size_t len = strlen(inputline);
+		/* check for maximum length line */
+		if (len) {
+			if (inputline[--len] != '\n')
+				Message(PCB_MSG_DEFAULT, _("Line length (%i) exceeded in netlist file.\n"
+									"additional characters will be ignored.\n"), MAX_NETLIST_LINE_LENGTH);
+			else
+				inputline[len] = '\0';
+		}
+		continued = (inputline[len - 1] == '\\') ? pcb_true : pcb_false;
+		if (continued)
+			inputline[len - 1] = '\0';
+		lines++;
+		i = 0;
+		while (inputline[i] != '\0') {
+			j = 0;
+			/* skip leading blanks */
+			while (inputline[i] != '\0' && BLANK(inputline[i]))
+				i++;
+			if (kind == 0) {
+				/* add two spaces for included/unincluded */
+				temp[j++] = ' ';
+				temp[j++] = ' ';
+			}
+			while (!BLANK(inputline[i]))
+				temp[j++] = inputline[i++];
+			temp[j] = '\0';
+			while (inputline[i] != '\0' && BLANK(inputline[i]))
+				i++;
+			if (kind == 0) {
+				menu = GetLibraryMenuMemory(&PCB->NetlistLib[NETLIST_INPUT], NULL);
+				menu->Name = pcb_strdup(temp);
+				menu->flag = 1;
+				kind++;
+			}
+			else {
+				if (kind == 1 && strchr(temp, '-') == NULL) {
+					kind++;
+					menu->Style = pcb_strdup(temp);
+				}
+				else {
+					entry = GetLibraryEntryMemory(menu);
+					entry->ListEntry = pcb_strdup(temp);
+					entry->ListEntry_dontfree = 0;
+				}
+			}
+		}
+		if (!continued)
+			kind = 0;
+	}
+	if (!lines) {
+		Message(PCB_MSG_DEFAULT, _("Empty netlist file!\n"));
+		pclose(fp);
+		return (1);
+	}
+	if (used_popen)
+		pclose(fp);
+	else
+		fclose(fp);
+	pcb_sort_netlist();
+	rats_patch_make_edited(PCB);
+	return (0);
+}
+
+int netlist_support_prio(plug_import_t *ctx, unsigned int aspects, FILE *fp, const char *filename)
+{
+	if (aspects != IMPORT_ASPECT_NETLIST)
+		return 0; /* only pure netlist import is supported */
+
+	/* we are sort of a low prio fallback without any chance to check the file format in advance */
+	return 10;
+}
+
+
+static int netlist_import(plug_import_t *ctx, unsigned int aspects, const char *fn)
+{
+	return ReadNetlist(fn);
+}
+
+static void hid_import_netlist_uninit(void)
+{
+}
+
+pcb_uninit_t hid_import_netlist_init(void)
+{
+
+	/* register the IO hook */
+	import_netlist.plugin_data = NULL;
+
+	import_netlist.fmt_support_prio = netlist_support_prio;
+	import_netlist.import           = netlist_import;
+
+	HOOK_REGISTER(plug_import_t, plug_import_chain, &import_netlist);
+
+	return hid_import_netlist_uninit;
+}
+
diff --git a/src_plugins/import_sch/Makefile b/src_plugins/import_sch/Makefile
new file mode 100644
index 0000000..dfcba41
--- /dev/null
+++ b/src_plugins/import_sch/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_autoplace
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/import_sch/Plug.tmpasm b/src_plugins/import_sch/Plug.tmpasm
new file mode 100644
index 0000000..f50a905
--- /dev/null
+++ b/src_plugins/import_sch/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {import_sch}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/import_sch/import_sch.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/import_sch/import_sch_conf.h}
+
+switch /local/pcb/import_sch/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/import_sch/README b/src_plugins/import_sch/README
new file mode 100644
index 0000000..a44cf0b
--- /dev/null
+++ b/src_plugins/import_sch/README
@@ -0,0 +1,5 @@
+Imports element and netlist data from the schematics (or some other source).
+
+#state: works
+#default: buildin
+#implements: import
diff --git a/src_plugins/import_sch/import_sch.c b/src_plugins/import_sch/import_sch.c
new file mode 100644
index 0000000..a595bcc
--- /dev/null
+++ b/src_plugins/import_sch/import_sch.c
@@ -0,0 +1,481 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "plugins.h"
+
+#include "misc.h"
+#include "compat_fs.h"
+#include "pcb-printf.h"
+#include "remove.h"
+#include "rats.h"
+#include "hid_actions.h"
+#include "import_sch_conf.h"
+#include "misc_util.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+conf_import_sch_t conf_import_sch;
+
+extern int ActionExecuteFile(int argc, const char **argv, Coord x, Coord y);
+
+/* ---------------------------------------------------------------- */
+static const char import_syntax[] =
+	"Import()\n"
+	"Import([gnetlist|make[,source,source,...]])\n" "Import(setnewpoint[,(mark|center|X,Y)])\n" "Import(setdisperse,D,units)\n";
+
+static const char import_help[] = "Import schematics.";
+
+/* %start-doc actions Import
+
+Imports element and netlist data from the schematics (or some other
+source).  The first parameter, which is optional, is the mode.  If not
+specified, the @code{import::mode} attribute in the PCB is used.
+ at code{gnetlist} means gnetlist is used to obtain the information from
+the schematics.  @code{make} invokes @code{make}, assuming the user
+has a @code{Makefile} in the current directory.  The @code{Makefile}
+will be invoked with the following variables set:
+
+ at table @code
+
+ at item PCB
+The name of the .pcb file
+
+ at item SRCLIST
+A space-separated list of source files
+
+ at item OUT
+The name of the file in which to put the command script, which may
+contain any @pcb{} actions.  By default, this is a temporary file
+selected by @pcb{}, but if you specify an @code{import::outfile}
+attribute, that file name is used instead (and not automatically
+deleted afterwards).
+
+ at end table
+
+The target specified to be built is the first of these that apply:
+
+ at itemize @bullet
+
+ at item
+The target specified by an @code{import::target} attribute.
+
+ at item
+The output file specified by an @code{import::outfile} attribute.
+
+ at item
+If nothing else is specified, the target is @code{pcb_import}.
+
+ at end itemize
+
+If you specify an @code{import::makefile} attribute, then "-f <that
+file>" will be added to the command line.
+
+If you specify the mode, you may also specify the source files
+(schematics).  If you do not specify any, the list of schematics is
+obtained by reading the @code{import::src at var{N}} attributes (like
+ at code{import::src0}, @code{import::src1}, etc).
+
+For compatibility with future extensions to the import file format,
+the generated file @emph{must not} start with the two characters
+ at code{#%}.
+
+If a temporary file is needed the @code{TMPDIR} environment variable
+is used to select its location.
+*/
+
+/*
+Note that the programs @code{gnetlist} and @code{make} must be
+configured.
+
+If @pcb{} cannot determine which schematic(s) to import from, the GUI
+is called to let user choose (see @code{ImportGUI()}).
+
+Note that Import() doesn't delete anything - after an Import, elements
+which shouldn't be on the board are selected and may be removed once
+it's determined that the deletion is appropriate.
+
+If @code{Import()} is called with @code{setnewpoint}, then the location
+of new components can be specified.  This is where parts show up when
+they're added to the board.  The default is the center of the board.
+
+ at table @code
+
+ at item Import(setnewpoint)
+
+Prompts the user to click on the board somewhere, uses that point.  If
+called by a hotkey, uses the current location of the crosshair.
+
+ at item Import(setnewpoint,mark)
+
+Uses the location of the mark.  If no mark is present, the point is
+not changed.
+
+ at item Import(setnewpoint,center)
+
+Resets the point to the center of the board.
+
+ at item Import(setnewpoint,X,Y,units)
+
+Sets the point to the specific coordinates given.  Example:
+ at code{Import(setnewpoint,50,25,mm)}
+
+ at end table
+
+Note that the X and Y locations are stored in attributes named
+ at code{import::newX} and @code{import::newY} so you could change them
+manually if you wished.
+
+Calling @code{Import(setdisperse,D,units)} sets how much the newly
+placed elements are dispersed relative to the set point.  For example,
+ at code{Import(setdisperse,10,mm)} will offset each part randomly up to
+10mm away from the point.  The default dispersion is 1/10th of the
+smallest board dimension.  Dispersion is saved in the
+ at code{import::disperse} attribute.
+
+%end-doc */
+
+static int ActionImport(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *mode;
+	const char **sources = NULL;
+	int nsources = 0;
+
+#ifdef DEBUG
+	printf("ActionImport:  ===========  Entering ActionImport  ============\n");
+#endif
+
+	mode = ACTION_ARG(0);
+
+	if (mode && strcasecmp(mode, "setdisperse") == 0) {
+		const char *ds, *units;
+		char buf[50];
+
+		ds = ACTION_ARG(1);
+		units = ACTION_ARG(2);
+		if (!ds) {
+			const char *as = AttributeGet(PCB, "import::disperse");
+			ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
+		}
+		if (units) {
+			sprintf(buf, "%s%s", ds, units);
+			AttributePut(PCB, "import::disperse", buf);
+		}
+		else
+			AttributePut(PCB, "import::disperse", ds);
+		if (ACTION_ARG(1) == NULL)
+			free((char*)ds);
+		return 0;
+	}
+
+	if (mode && strcasecmp(mode, "setnewpoint") == 0) {
+		const char *xs, *ys, *units;
+		Coord x, y;
+		char buf[50];
+
+		xs = ACTION_ARG(1);
+		ys = ACTION_ARG(2);
+		units = ACTION_ARG(3);
+
+		if (!xs) {
+			gui->get_coords(_("Click on a location"), &x, &y);
+		}
+		else if (strcasecmp(xs, "center") == 0) {
+			AttributeRemove(PCB, "import::newX");
+			AttributeRemove(PCB, "import::newY");
+			return 0;
+		}
+		else if (strcasecmp(xs, "mark") == 0) {
+			if (Marked.status) {
+				x = Marked.X;
+				y = Marked.Y;
+			}
+		}
+		else if (ys) {
+			x = GetValue(xs, units, NULL, NULL);
+			y = GetValue(ys, units, NULL, NULL);
+		}
+		else {
+			Message(PCB_MSG_DEFAULT, _("Bad syntax for Import(setnewpoint)"));
+			return 1;
+		}
+
+		pcb_sprintf(buf, "%$ms", x);
+		AttributePut(PCB, "import::newX", buf);
+		pcb_sprintf(buf, "%$ms", y);
+		AttributePut(PCB, "import::newY", buf);
+		return 0;
+	}
+
+	if (!mode)
+		mode = AttributeGet(PCB, "import::mode");
+	if (!mode)
+		mode = "gnetlist";
+
+	if (argc > 1) {
+		sources = argv + 1;
+		nsources = argc - 1;
+	}
+
+	if (!sources) {
+		char sname[40];
+		char *src;
+
+		nsources = -1;
+		do {
+			nsources++;
+			sprintf(sname, "import::src%d", nsources);
+			src = AttributeGet(PCB, sname);
+		} while (src);
+
+		if (nsources > 0) {
+			sources = (const char **) malloc((nsources + 1) * sizeof(char *));
+			nsources = -1;
+			do {
+				nsources++;
+				sprintf(sname, "import::src%d", nsources);
+				src = AttributeGet(PCB, sname);
+				sources[nsources] = src;
+			} while (src);
+		}
+	}
+
+	if (!sources) {
+		/* Replace .pcb with .sch and hope for the best.  */
+		char *pcbname = PCB->Filename;
+		char *schname;
+		char *dot, *slash, *bslash;
+
+		if (!pcbname)
+			return hid_action("ImportGUI");
+
+		schname = (char *) malloc(strlen(pcbname) + 5);
+		strcpy(schname, pcbname);
+		dot = strchr(schname, '.');
+		slash = strchr(schname, '/');
+		bslash = strchr(schname, '\\');
+		if (dot && slash && dot < slash)
+			dot = NULL;
+		if (dot && bslash && dot < bslash)
+			dot = NULL;
+		if (dot)
+			*dot = 0;
+		strcat(schname, ".sch");
+
+		if (access(schname, F_OK))
+			return hid_action("ImportGUI");
+
+		sources = (const char **) malloc(2 * sizeof(char *));
+		sources[0] = schname;
+		sources[1] = NULL;
+		nsources = 1;
+	}
+
+	if (strcasecmp(mode, "gnetlist") == 0) {
+		char *tmpfile = tempfile_name_new("gnetlist_output");
+		const char **cmd;
+		int i;
+
+		if (tmpfile == NULL) {
+			Message(PCB_MSG_ERROR, _("Could not create temp file"));
+			return 1;
+		}
+
+		if ((conf_import_sch.plugins.import_sch.gnetlist_program == NULL) || (*conf_import_sch.plugins.import_sch.gnetlist_program == '\0')) {
+			Message(PCB_MSG_ERROR, _("No gnetlist program configured, can not import. Please fill in configuration setting plugins/import_sch/gnetlist_program\n"));
+			return 1;
+		}
+
+		cmd = (const char **) malloc((7 + nsources) * sizeof(char *));
+		cmd[0] = conf_import_sch.plugins.import_sch.gnetlist_program;
+		cmd[1] = "-g";
+		cmd[2] = "pcbrndfwd";
+		cmd[3] = "-o";
+		cmd[4] = tmpfile;
+		cmd[5] = "--";
+		for (i = 0; i < nsources; i++)
+			cmd[6 + i] = sources[i];
+		cmd[6 + nsources] = NULL;
+
+#ifdef DEBUG
+		printf("ActionImport:  ===========  About to run gnetlist  ============\n");
+		printf("%s %s %s %s %s %s %s ...\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
+#endif
+
+		if (pcb_spawnvp(cmd)) {
+			unlink(tmpfile);
+			return 1;
+		}
+
+#ifdef DEBUG
+		printf("ActionImport:  ===========  About to run ActionExecuteFile, file = %s  ============\n", tmpfile);
+#endif
+
+		cmd[0] = tmpfile;
+		cmd[1] = NULL;
+		ActionExecuteFile(1, cmd, 0, 0);
+
+		free(cmd);
+		tempfile_unlink(tmpfile);
+	}
+	else if (strcasecmp(mode, "make") == 0) {
+		int must_free_tmpfile = 0;
+		char *tmpfile;
+		const char *cmd[10];
+		int i;
+		char *srclist;
+		int srclen;
+		char *user_outfile = NULL;
+		char *user_makefile = NULL;
+		char *user_target = NULL;
+
+
+		user_outfile = AttributeGet(PCB, "import::outfile");
+		user_makefile = AttributeGet(PCB, "import::makefile");
+		user_target = AttributeGet(PCB, "import::target");
+		if (user_outfile && !user_target)
+			user_target = user_outfile;
+
+		if ((conf_import_sch.plugins.import_sch.make_program == NULL) || (*conf_import_sch.plugins.import_sch.make_program == '\0')) {
+			Message(PCB_MSG_ERROR, _("No make program configured, can not import. Please fill in configuration setting plugins/import_sch/make_program\n"));
+			return 1;
+		}
+
+		if (user_outfile)
+			tmpfile = user_outfile;
+		else {
+			tmpfile = tempfile_name_new("gnetlist_output");
+			if (tmpfile == NULL) {
+				Message(PCB_MSG_ERROR, _("Could not create temp file"));
+				return 1;
+			}
+			must_free_tmpfile = 1;
+		}
+
+		srclen = sizeof("SRCLIST=") + 2;
+		for (i = 0; i < nsources; i++)
+			srclen += strlen(sources[i]) + 2;
+		srclist = (char *) malloc(srclen);
+		strcpy(srclist, "SRCLIST=");
+		for (i = 0; i < nsources; i++) {
+			if (i)
+				strcat(srclist, " ");
+			strcat(srclist, sources[i]);
+		}
+
+		cmd[0] = conf_import_sch.plugins.import_sch.make_program;
+		cmd[1] = "-s";
+		cmd[2] = Concat("PCB=", PCB->Filename, NULL);
+		cmd[3] = srclist;
+		cmd[4] = Concat("OUT=", tmpfile, NULL);
+		i = 5;
+		if (user_makefile) {
+			cmd[i++] = "-f";
+			cmd[i++] = user_makefile;
+		}
+		cmd[i++] = user_target ? user_target : (char *) "pcb_import";
+		cmd[i++] = NULL;
+
+		if (pcb_spawnvp(cmd)) {
+			if (must_free_tmpfile)
+				unlink(tmpfile);
+			free((char*)cmd[2]);
+			free((char*)cmd[3]);
+			free((char*)cmd[4]);
+			return 1;
+		}
+
+		cmd[0] = tmpfile;
+		cmd[1] = NULL;
+		ActionExecuteFile(1, cmd, 0, 0);
+
+		free((char*)cmd[2]);
+		free((char*)cmd[3]);
+		free((char*)cmd[4]);
+		if (must_free_tmpfile)
+			tempfile_unlink(tmpfile);
+	}
+	else {
+		Message(PCB_MSG_DEFAULT, _("Unknown import mode: %s\n"), mode);
+		return 1;
+	}
+
+	DeleteRats(pcb_false);
+	AddAllRats(pcb_false, NULL);
+
+#ifdef DEBUG
+	printf("ActionImport:  ===========  Leaving ActionImport  ============\n");
+#endif
+
+	return 0;
+}
+
+static const char *import_sch_cookie = "import_sch plugin";
+
+HID_Action import_sch_action_list[] = {
+	{"Import", 0, ActionImport,
+	 import_help, import_syntax}
+};
+
+REGISTER_ACTIONS(import_sch_action_list, import_sch_cookie)
+
+static void hid_import_sch_uninit(void)
+{
+	hid_remove_actions_by_cookie(import_sch_cookie);
+	conf_unreg_fields("plugins/import_sch/");
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_import_sch_init(void)
+{
+	char *tmp;
+
+	REGISTER_ACTIONS(import_sch_action_list, import_sch_cookie)
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_import_sch, field,isarray,type_name,cpath,cname,desc,flags);
+#include "import_sch_conf_fields.h"
+
+	/* Compatibility: get some settings from the env */
+	tmp = getenv ("PCB_MAKE_PROGRAM");
+	if (tmp != NULL)
+		conf_set(CFR_ENV, "plugins/import_sch/make_program", -1, tmp, POL_OVERWRITE);
+
+	tmp = getenv ("PCB_GNETLIST");
+	if (tmp != NULL)
+		conf_set(CFR_ENV, "plugins/import_sch/gnetlist_program", -1, tmp, POL_OVERWRITE);
+
+	return hid_import_sch_uninit;
+}
diff --git a/src_plugins/import_sch/import_sch_conf.h b/src_plugins/import_sch/import_sch_conf.h
new file mode 100644
index 0000000..e51abaa
--- /dev/null
+++ b/src_plugins/import_sch/import_sch_conf.h
@@ -0,0 +1,15 @@
+#ifndef PCB_IMPORT_SCH_CONF_H
+#define PCB_IMPORT_SCH_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct import_sch {
+			CFT_STRING gnetlist_program;       /* gnetlist program name */
+			CFT_STRING make_program;           /* make program name */
+		} import_sch;
+	} plugins;
+} conf_import_sch_t;
+
+#endif
diff --git a/src_plugins/io_kicad/Makefile b/src_plugins/io_kicad/Makefile
new file mode 100644
index 0000000..27e6cf9
--- /dev/null
+++ b/src_plugins/io_kicad/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_io_kicad
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/io_kicad/Plug.tmpasm b/src_plugins/io_kicad/Plug.tmpasm
new file mode 100644
index 0000000..41e03e4
--- /dev/null
+++ b/src_plugins/io_kicad/Plug.tmpasm
@@ -0,0 +1,15 @@
+put /local/pcb/mod {io_kicad}
+put /local/pcb/mod/OBJS [@
+ $(PLUGDIR)/io_kicad/io_kicad.o
+ $(PLUGDIR)/io_kicad/write.o
+ $(PLUGDIR)/io_kicad/read.o
+ $(PLUGDIR)/io_kicad/uniq_name.o
+@]
+#put /local/pcb/mod/YACC {$(PLUGDIR)/io_kicad/parse_y}
+#put /local/pcb/mod/LEX  {$(PLUGDIR)/io_kicad/parse_l}
+
+switch /local/pcb/io_kicad/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/io_kicad/README b/src_plugins/io_kicad/README
new file mode 100644
index 0000000..697df2e
--- /dev/null
+++ b/src_plugins/io_kicad/README
@@ -0,0 +1,6 @@
+Load and save the design and elements in Kicad's s-expression format - this
+is the new, currently preferred format in Kicad.
+
+#state: work-in-progress
+#default: disabled
+#implements: io
diff --git a/src_plugins/io_kicad/hacking.txt b/src_plugins/io_kicad/hacking.txt
new file mode 100644
index 0000000..83fd03e
--- /dev/null
+++ b/src_plugins/io_kicad/hacking.txt
@@ -0,0 +1,87 @@
+Parser
+~~~~~~
+io_kicad_read_pcb() is the main entry point for parsing a board. It loads
+the s-expression from the file and using gensexpr it parses it into a
+DOM (Document Object Model). Each node of the DOM is a gsxl_node_t, which
+has an ->str field for the text data of the node, a ->parent field pointing
+to the parent node, a ->children field pointing to the first child node and
+a ->next field pointing to the next sibling node (with the same parent).
+
+An expression like this:
+  (general
+    (links 0)
+    (no_connects 0)
+    (area 1 2 3 4)
+  ...
+  )
+
+becomes a subtree like this (levels of the tree is represented by
+indentation):
+
+ general
+  links
+   0
+  no_connects
+   0
+  area
+   1
+   2
+   3
+   4
+  ...
+
+So that general's ->children is links; links' ->next is no_connects;
+no_connects' ->next is area; area's ->next is NULL. In the kicad
+parser the terminology is that the root of a subtree is the _command_
+and its children are its _arguments_. E.g. when processing the
+area node, the command is "area" it's arguments are "1", "2", "3" and "4";
+when processing the "general" subtree, "area" is the 2nd argument (arguments
+are counted from 0).
+
+Once the tree is built in memory, a recursive tree walk is started by
+calling kicad_parse_pcb() on the root of the tree. This function should
+evaluate the children of the root node and call the corresponding subtree
+processor on each. All parser function should be passed the context:
+read_state_t *st. The context holds all relevant information about the
+session so we can avoid global variables (or static local variables) and
+can keep the code reentrant.
+
+Subtree processors have unified prototype: they get the context and the
+node pointer to the  0th argument of the command:
+
+static int kicad_parse_something(read_state_t *st, gsxl_node_t *args)
+
+If a subtree processor returns non-zero, the parsing shall stop and the
+caller function shall return non-zero immediately. This way we can undo
+the recursion and cancel parsing.
+
+There are two kind of subtree processors: static and dynamic. A static
+subtree processor expects a specific subtree of arguments. This means both
+the number and the exact order(!) of the subtrees are fixed. The processor
+for the "links" node in the above example has a static subtree processor
+because "links" will always have only one argument. Since there is a
+fixed amount of arguments, a static subtree processor uses the args->next
+or args->next->next or args->next->next->next, etc for accessing the 0th,
+1st or 2nd, etc arguments, respectively.
+
+A dynamic subtree processor has an unknown amount of arguments but the
+set of possible arguments are known - just their order and number are
+not. For example a "module" is a dynamic subtree because it may have 0 or
+more fp_line, pad, etc. children. A dynamic subtree processor needs to
+iterate through all its arguments using a loop; for each argument it needs
+to look at the ->str field of the argument to decide how to process
+that subtree (e.g. if it's an "fp_line" or a "pad").
+
+To ease this, function kicad_foreach_dispatch() is provided. It iterates
+over all the ->next fields of its "tree" argument and calls a function
+for them according to a dispatcher table passed. The dispatcher table has
+2 columns, node-name and function-to-call. kicad_parse_pcb() demonstrates
+how to set up a table. If the node "general" in the above example
+is to be processed this way, general's ->children is passed as tree to
+the kicad_foreach_dispatch, with a table that has a function for at least
+links, no_connects and area. Those functions will get links' ->children,
+no_connects' ->children and area's ->children as args respectively.
+
+
+How to batch-test the loader:
+echo 'LoadFrom(Layout, foo.kicad_pcb, kicad)'  | ./pcb-rnd --gui batch
diff --git a/src_plugins/io_kicad/io_kicad.c b/src_plugins/io_kicad/io_kicad.c
new file mode 100644
index 0000000..b1fe09e
--- /dev/null
+++ b/src_plugins/io_kicad/io_kicad.c
@@ -0,0 +1,74 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This module, io_kicad, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "plugins.h"
+#include "plug_io.h"
+#include "write.h"
+#include "read.h"
+
+static plug_io_t io_kicad;
+
+int io_kicad_fmt(plug_io_t *ctx, plug_iot_t typ, int wr, const char *fmt)
+{
+	if (strcmp(ctx->description, fmt) == 0)
+		return 200;
+
+	if ((strcmp(fmt, "kicad") != 0) ||
+		((typ & (~(PCB_IOT_FOOTPRINT | PCB_IOT_BUFFER | PCB_IOT_PCB))) != 0))
+		return 0;
+
+	return 100;
+}
+
+static void hid_io_kicad_uninit(void)
+{
+	/* Runs once when the plugin is unloaded. TODO: free plugin-globals here. */
+}
+
+pcb_uninit_t hid_io_kicad_init(void)
+{
+
+	/* register the IO hook */
+	io_kicad.plugin_data = NULL;
+	io_kicad.fmt_support_prio = io_kicad_fmt;
+	io_kicad.parse_pcb = io_kicad_read_pcb;
+	io_kicad.parse_element = NULL;
+	io_kicad.parse_font = NULL;
+	io_kicad.write_buffer = io_kicad_write_buffer;
+	io_kicad.write_element = io_kicad_write_element;
+	io_kicad.write_pcb = io_kicad_write_pcb;
+	io_kicad.default_fmt = "kicad";
+	io_kicad.description = "Kicad, s-expression";
+	io_kicad.save_preference_prio = 92;
+
+	HOOK_REGISTER(plug_io_t, plug_io_chain, &io_kicad);
+
+	/* TODO: Alloc plugin-globals here. */
+
+	return hid_io_kicad_uninit;
+}
+
diff --git a/src_plugins/io_kicad/read.c b/src_plugins/io_kicad/read.c
new file mode 100644
index 0000000..441a30a
--- /dev/null
+++ b/src_plugins/io_kicad/read.c
@@ -0,0 +1,2081 @@
+/*
+ *														COPYRIGHT
+ *
+ *	pcb-rnd, interactive printed circuit board design
+ *	Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *	Copyright (C) 2016 Erich S. Heinzle
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *	Contact addresses for paper mail and Email:
+ *	Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *	Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+#include <assert.h>
+#include <math.h>
+#include <gensexpr/gsxl.h>
+#include <genht/htsi.h>
+#include "compat_misc.h"
+#include "plug_io.h"
+#include "error.h"
+#include "data.h"
+#include "read.h"
+#include "layer.h"
+#include "const.h"
+#include "netlist.h"
+#include "create.h"
+#include "polygon.h"
+#include "misc.h" /* for flag setting */
+#include "misc_util.h" /* for distance calculations */
+#include "conf_core.h"
+#include "move.h"
+#include "macro.h"
+
+
+
+typedef struct {
+	PCBTypePtr PCB;
+	const char *Filename;
+	conf_role_t settings_dest;
+	gsxl_dom_t dom;
+	htsi_t layer_k2i; /* layer name-to-index hash; name is the kicad name, index is the pcb-rnd layer index */
+} read_state_t;
+
+typedef struct {
+	const char *node_name;
+	int (*parser)(read_state_t *st, gsxl_node_t *subtree);
+} dispatch_t;
+
+/* Search the dispatcher table for subtree->str, execute the parser on match
+   with the children ("parameters") of the subtree */
+static int kicad_dispatch(read_state_t *st, gsxl_node_t *subtree, const dispatch_t *disp_table)
+{
+	const dispatch_t *d;
+
+	/* do not tolerate empty/NIL node */
+	if (subtree->str == NULL)
+		return -1;
+
+	for(d = disp_table; d->node_name != NULL; d++)
+		if (strcmp(d->node_name, subtree->str) == 0)
+			return d->parser(st, subtree->children);
+
+	printf("Unknown node: '%s'\n", subtree->str);
+	/* node name not found in the dispatcher table */
+	return -1;
+}
+
+/* Take each children of tree and execute them using kicad_dispatch
+   Useful for procssing nodes that may host various subtrees of different
+   nodes ina  flexible way. Return non-zero if any subtree processor failed. */
+static int kicad_foreach_dispatch(read_state_t *st, gsxl_node_t *tree, const dispatch_t *disp_table)
+{
+	gsxl_node_t *n;
+
+	for(n = tree; n != NULL; n = n->next)
+		if (kicad_dispatch(st, n, disp_table) != 0)
+			return -1;
+
+	return 0; /* success */
+}
+
+/* No-op: ignore the subtree */
+static int kicad_parse_nop(read_state_t *st, gsxl_node_t *subtree)
+{
+	return 0;
+}
+
+/* kicad_pcb/version */
+static int kicad_parse_version(read_state_t *st, gsxl_node_t *subtree)
+{
+	if (subtree->str != NULL) {
+		int ver = atoi(subtree->str);
+		printf("kicad version: '%s' == %d\n", subtree->str, ver);
+		if (ver == 3) /* accept version 3 */
+			return 0;
+	}
+	return -1;
+}
+
+static int kicad_parse_layer_definitions(read_state_t *st, gsxl_node_t *subtree); /* for layout layer definitions */
+static int kicad_parse_net(read_state_t *st, gsxl_node_t *subtree); /* describes netlists for the layout */
+static int kicad_get_layeridx(read_state_t *st, const char *kicad_name);
+
+#define SEEN_NO_DUP(bucket, bit) \
+do { \
+	int __mask__ = (1<<(bit)); \
+	if ((bucket) & __mask__) \
+		return -1; \
+	bucket |= __mask__; \
+} while(0)
+
+#define SEEN(bucket, bit) \
+do { \
+	int __mask__ = (1<<(bit)); \
+	bucket |= __mask__; \
+} while(0)
+
+#define BV(bit) (1<<(bit))
+
+
+/* kicad_pcb/parse_page */
+static int kicad_parse_page_size(read_state_t *st, gsxl_node_t *subtree)
+{
+
+	if (subtree != NULL && subtree->str != NULL) {
+		printf("page setting being parsed: '%s'\n", subtree->str);		
+			if (strcmp("A4", subtree->str) == 0) {
+				st->PCB->MaxWidth = PCB_MM_TO_COORD(297.0);
+				st->PCB->MaxHeight = PCB_MM_TO_COORD(210.0);
+			} else if (strcmp("A3", subtree->str) == 0) {
+				st->PCB->MaxWidth = PCB_MM_TO_COORD(420.0);
+				st->PCB->MaxHeight = PCB_MM_TO_COORD(297.0);
+			} else if (strcmp("A2", subtree->str) == 0) {
+				st->PCB->MaxWidth = PCB_MM_TO_COORD(594.0);
+				st->PCB->MaxHeight = PCB_MM_TO_COORD(420.0);
+			} else if (strcmp("A1", subtree->str) == 0) {
+				st->PCB->MaxWidth = PCB_MM_TO_COORD(841.0);
+				st->PCB->MaxHeight = PCB_MM_TO_COORD(594.0);
+			} else if (strcmp("A0", subtree->str) == 0) {
+				st->PCB->MaxWidth = PCB_MM_TO_COORD(1189.0);
+				st->PCB->MaxHeight = PCB_MM_TO_COORD(841.0);
+			} else { /* default to A0 */
+				st->PCB->MaxWidth = PCB_MM_TO_COORD(1189.0);
+				st->PCB->MaxHeight = PCB_MM_TO_COORD(841.0);
+			}
+			return 0;
+	}
+	return -1;
+}
+
+
+/* kicad_pcb/gr_text */
+static int kicad_parse_gr_text(read_state_t *st, gsxl_node_t *subtree)
+{
+
+	gsxl_node_t *l, *n, *m;
+	int i;
+	unsigned long tally = 0, required;
+
+	char *end, *text;
+	double val;
+	Coord X, Y;
+	int scaling = 100;
+	int textLength = 0;
+	int mirrored = 0;
+	double glyphWidth = 1.27; /* a reasonable approximation of pcb glyph width, ~=  5000 centimils */
+	unsigned direction = 0; /* default is horizontal */
+	FlagType Flags = MakeFlags(0); /* start with something bland here */
+	int PCBLayer = 0; /* sane default value */
+
+	if (subtree->str != NULL) {
+		printf("gr_text element being parsed: '%s'\n", subtree->str);
+		text = subtree->str;
+		for (i = 0; text[i] != 0; i++) {
+			textLength++;
+		}
+		printf("\tgr_text element length: '%d'\n", textLength);
+		for(n = subtree,i = 0; n != NULL; n = n->next, i++) {
+			if (n->str != NULL && strcmp("at", n->str) == 0) {
+					SEEN_NO_DUP(tally, 0);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\ttext at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 1); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\ttext at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 2);
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y = PCB_MM_TO_COORD(val);
+						}	
+						if (n->children->next->next != NULL && n->children->next->next->str != NULL) {
+							pcb_printf("\ttext rotation: '%s'\n", (n->children->next->next->str));
+							val = strtod(n->children->next->next->str, &end);
+							if (*end != 0) {
+								return -1;
+							} else {
+								direction = 0;  /* default */
+								if (val > 45.0 && val <= 135.0) {
+									direction = 1;
+								} else if (val > 135.0 && val <= 225.0) {
+									direction = 2;
+								} else if (val > 225.0 && val <= 315.0) {
+									direction = 3;
+								}
+								printf("\tkicad angle: %f,   Direction %d\n", val, direction);
+							}
+							SEEN_NO_DUP(tally, 3);
+						} 
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("layer", n->str) == 0) {
+				SEEN_NO_DUP(tally, 4);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\ttext layer: '%s'\n", (n->children->str));
+					PCBLayer = kicad_get_layeridx(st, n->children->str);
+					if (PCBLayer == -1) {
+						return -1;
+					} else if (pcb_layer_flags(PCBLayer) & PCB_LYT_BOTTOM) {
+							Flags = MakeFlags(PCB_FLAG_ONSOLDER);
+					}
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("effects", n->str) == 0) {
+				SEEN_NO_DUP(tally, 5);
+				for(m = n->children; m != NULL; m = m->next) {
+					printf("\tstepping through text effects definitions\n"); 
+					if (m->str != NULL && strcmp("font", m->str) == 0) {
+						SEEN_NO_DUP(tally, 6);
+						for(l = m->children; l != NULL; l = l->next) {
+							if (m->str != NULL && strcmp("size", l->str) == 0) {
+								SEEN_NO_DUP(tally, 7);
+								if (l->children != NULL && l->children->str != NULL) {
+									pcb_printf("\tfont sizeX: '%s'\n", (l->children->str));
+									val = strtod(l->children->str, &end);
+									if (*end != 0) {
+										return -1;
+									} else {
+										scaling = (int) (100*val/1.27); /* standard glyph width ~= 1.27mm */
+									}
+								} else {
+									return -1;
+								}
+								if (l->children->next != NULL && l->children->next->str != NULL) {
+									pcb_printf("\tfont sizeY: '%s'\n", (l->children->next->str));
+								} else {
+									return -1;
+								}
+							} else if (strcmp("thickness", l->str) == 0) {
+								SEEN_NO_DUP(tally, 8);
+								if (l->children != NULL && l->children->str != NULL) {
+									pcb_printf("\tfont thickness: '%s'\n", (l->children->str));
+								} else {
+									return -1;
+								}
+							}
+						}
+					} else if (m->str != NULL && strcmp("justify", m->str) == 0) {
+						SEEN_NO_DUP(tally, 9);
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\ttext justification: '%s'\n", (m->children->str));
+							if (strcmp("mirror", m->children->str) == 0) {
+								mirrored = 1;
+							}
+						} else {
+							return -1;
+						}
+					} else {
+						if (m->str != NULL) {
+							printf("Unknown effects argument %s:", m->str);
+						}
+						return -1;
+					}
+				}
+			} 				
+		}
+	}
+	required = 1; /*BV(2) | BV(3) | BV(4) | BV(7) | BV(8); */
+	if ((tally & required) == required) { /* has location, layer, size and stroke thickness at a minimum */
+		if (&st->PCB->Font == NULL) {
+			CreateDefaultFont(st->PCB);
+		}
+
+		if (mirrored != 0) {
+			if (direction%2 == 0) {
+				direction += 2;
+				direction = direction%4;
+			}
+			if (direction == 0 ) {
+				X -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y += PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 1 ) {
+				Y -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X -= PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 2 ) {
+				X += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y -= PCB_MM_TO_COORD(glyphWidth/2.0);  /* centre it vertically */
+			} else if (direction == 3 ) {
+				Y += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X += PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			}
+		} else { /* not back of board text */
+			if (direction == 0 ) {
+				X -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y -= PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 1 ) {
+				Y += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X -= PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 2 ) {
+				X += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y += PCB_MM_TO_COORD(glyphWidth/2.0);  /* centre it vertically */
+			} else if (direction == 3 ) {
+				Y -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X += PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			}
+		}
+
+		CreateNewText( &st->PCB->Data->Layer[PCBLayer], &st->PCB->Font, X, Y, direction, scaling, text, Flags);
+		return 0; /* create new font */
+	}
+	return -1;
+}
+
+/* kicad_pcb/gr_line */
+static int kicad_parse_gr_line(read_state_t *st, gsxl_node_t *subtree)
+{
+	gsxl_node_t *n;
+	unsigned long tally = 0;
+
+	char *end;
+	double val;
+	Coord X1, Y1, X2, Y2, Thickness, Clearance; /* not sure what to do with mask */
+	FlagType Flags = MakeFlags(0); /* start with something bland here */
+	int PCBLayer = 0; /* sane default value */
+
+	Clearance = Thickness = PCB_MM_TO_COORD(0.250); /* start with sane defaults */
+
+	printf("gr_line parsing about to commence:\n");
+
+	if (subtree->str != NULL) {
+		for(n = subtree; n != NULL; n = n->next) {
+			if (n->str != NULL && strcmp("start", n->str) == 0) {
+					SEEN_NO_DUP(tally, 0);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_line start at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 1); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X1 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tgr_line start at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 2);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y1 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("end", n->str) == 0) {
+					SEEN_NO_DUP(tally, 3);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_line end at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 4);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X2 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tgr_line end at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 5);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y2 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("layer", n->str) == 0) {
+					SEEN_NO_DUP(tally, 6);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_line layer: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 7);
+						PCBLayer = kicad_get_layeridx(st, n->children->str);
+						if (PCBLayer == -1) {
+							return -1;
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("width", n->str) == 0) {
+					SEEN_NO_DUP(tally, 8);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_line width: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 9);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Thickness = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("angle", n->str) == 0) { /* unlikely to be used or seen */
+					SEEN_NO_DUP(tally, 10);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_line angle: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 11);
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("net", n->str) == 0) { /* unlikely to be used or seen */
+					SEEN_NO_DUP(tally, 12);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_line net: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 13);
+					} else {
+						return -1;
+					}
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown gr_line argument %s:", n->str);
+				}
+				return -1;
+			}
+		}
+	}
+	if (tally >= 0) { /* need start, end, layer, thickness at a minimum */
+		CreateNewLineOnLayer( &st->PCB->Data->Layer[PCBLayer], X1, Y1, X2, Y2, Thickness, Clearance, Flags);
+		pcb_printf("\tnew gr_line on layer created\n");
+		return 0;
+	}
+	return -1;
+}
+
+/* kicad_pcb/gr_arc     can also parse gr_cicle*/
+static int kicad_parse_gr_arc(read_state_t *st, gsxl_node_t *subtree)
+{
+	gsxl_node_t *n;
+	unsigned long tally = 0;
+
+	char *end;
+	double val;
+	Coord centreX, centreY, endX, endY, width, height, Thickness, Clearance;
+	Angle startAngle = 0.0;
+	Angle delta = 360.0; /* these defaults allow a gr_circle to be parsed, which does not specify (angle XXX) */
+	FlagType Flags = MakeFlags(0); /* start with something bland here */
+	int PCBLayer = 0; /* sane default value */
+
+	Clearance = Thickness = PCB_MM_TO_COORD(0.250); /* start with sane defaults */
+
+	if (subtree->str != NULL) {
+		printf("gr_arc being parsed:\n");		
+		for(n = subtree; n != NULL; n = n->next) {
+			if (n->str != NULL && strcmp("start", n->str) == 0) {
+					SEEN_NO_DUP(tally, 0);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc centre at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 1); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreX = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tgr_arc centre at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 2);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreY = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("center", n->str) == 0) { /* this lets us parse a circle too */
+					SEEN_NO_DUP(tally, 0);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc centre at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 1); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreX = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tgr_arc centre at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 2);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreY = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("end", n->str) == 0) {
+					SEEN_NO_DUP(tally, 3);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc end at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 4);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							endX = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tgr_arc end at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 5);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							endY = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("layer", n->str) == 0) {
+					SEEN_NO_DUP(tally, 6);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc layer: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 7);
+						PCBLayer = kicad_get_layeridx(st, n->children->str);
+						if (PCBLayer == -1) {
+							return -1;
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("width", n->str) == 0) {
+					SEEN_NO_DUP(tally, 8);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc width: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 9);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Thickness = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("angle", n->str) == 0) {
+					SEEN_NO_DUP(tally, 10);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc angle CW rotation: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 11);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							delta = val;
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("net", n->str) == 0) { /* unlikely to be used or seen */
+					SEEN_NO_DUP(tally, 12);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tgr_arc net: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 13);
+					} else {
+						return -1;
+					}
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown gr_arc argument %s:", n->str);
+				}
+				return -1;
+			}
+		}
+	}
+	if (tally >= 0) { /* need start, end, layer, thickness at a minimum */
+		width = height = Distance(centreX, centreY, endX, endY); /* calculate radius of arc */
+		if (width < 1) { /* degenerate case */
+			startAngle = 0;
+		} else {
+			startAngle = 180*atan2(endY - centreY, endX - centreX)/M_PI; /* avoid using atan2 with zero parameters */
+		}
+		CreateNewArcOnLayer( &st->PCB->Data->Layer[PCBLayer], centreX, centreY, width, height, startAngle, -delta, Thickness, Clearance, Flags);
+		return 0;
+	}
+	return -1;
+}
+
+/* kicad_pcb/via */
+static int kicad_parse_via(read_state_t *st, gsxl_node_t *subtree)
+{
+	gsxl_node_t *m, *n;
+	unsigned long tally = 0;
+
+	char *end, *name; /* not using via name for now */
+	double val;
+	Coord X, Y, Thickness, Clearance, Mask, Drill; /* not sure what to do with mask */
+	FlagType Flags = MakeFlags(0); /* start with something bland here */
+/*	int PCBLayer = 0;   not used for now; no blind or buried vias currently in pcb-rnd */
+
+	Clearance = Mask = PCB_MM_TO_COORD(0.250); /* start with something bland here */
+	Drill = PCB_MM_TO_COORD(0.300); /* start with something sane */
+	name = "";
+
+	if (subtree->str != NULL) {
+		printf("via being parsed:\n");		
+		for(n = subtree; n != NULL; n = n->next) {
+			if (n->str != NULL && strcmp("at", n->str) == 0) {
+					SEEN_NO_DUP(tally, 0);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tvia at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 1); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tvia at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 2);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("size", n->str) == 0) {
+					SEEN_NO_DUP(tally, 3);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tvia size: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 4);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Thickness = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("layers", n->str) == 0) {
+					SEEN_NO_DUP(tally, 5);
+					for(m = n->children; m != NULL; m = m->next) {
+						if (m->str != NULL) {
+							pcb_printf("\tvia layer: '%s'\n", (m->str));
+/*							PCBLayer = kicad_get_layeridx(st, m->str);
+ *							if (PCBLayer == -1) {
+ *								return -1;
+ *							}   via layers not currently used in PCB
+ */   
+						} else {
+							return -1;
+						}
+					}
+			} else if (n->str != NULL && strcmp("net", n->str) == 0) {
+					SEEN_NO_DUP(tally, 6);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tvia segment net: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 7);
+					} else {
+						return -1;
+					}
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown via argument %s:", n->str);
+				}
+				return -1;
+			}
+		}
+	}
+	if (tally >= 0) { /* need start, end, layer, thickness at a minimum */
+		CreateNewVia( st->PCB->Data, X, Y, Thickness, Clearance, Mask, Drill, name, Flags);
+		return 0;
+	}
+	return -1;
+}
+
+/* kicad_pcb/segment */
+static int kicad_parse_segment(read_state_t *st, gsxl_node_t *subtree)
+{
+
+	gsxl_node_t *n;
+	unsigned long tally = 0;
+
+	char *end;
+	double val;
+	Coord X1, Y1, X2, Y2, Thickness, Clearance;
+	FlagType Flags = MakeFlags(PCB_FLAG_CLEARLINE); /* we try clearline flag here */
+	int PCBLayer = 0; /* sane default value */
+
+	Clearance = PCB_MM_TO_COORD(0.250); /* start with something bland here */
+
+	if (subtree->str != NULL) {
+		printf("segment being parsed:\n");		
+		for(n = subtree; n != NULL; n = n->next) {
+			if (n->str != NULL && strcmp("start", n->str) == 0) {
+					SEEN_NO_DUP(tally, 0);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tsegment start at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 1); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X1 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tsegment start at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 2);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y1 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("end", n->str) == 0) {
+					SEEN_NO_DUP(tally, 3);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tsegment end at x: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 4);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X2 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tsegment end at y: '%s'\n", (n->children->next->str));
+						SEEN_NO_DUP(tally, 5);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y2 = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("layer", n->str) == 0) {
+					SEEN_NO_DUP(tally, 6);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tsegment layer: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 7);
+						PCBLayer = kicad_get_layeridx(st, n->children->str);
+						if (PCBLayer == -1) {
+							return -1;
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("width", n->str) == 0) {
+					SEEN_NO_DUP(tally, 8);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tsegment width: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 9);
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Thickness = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("net", n->str) == 0) {
+					SEEN_NO_DUP(tally, 10);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tsegment net: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 11);
+					} else {
+						return -1;
+					}
+			} else if (n->str != NULL && strcmp("tstamp", n->str) == 0) { /* not likely to be used */
+					SEEN_NO_DUP(tally, 12);
+					if (n->children != NULL && n->children->str != NULL) {
+						pcb_printf("\tsegment timestamp: '%s'\n", (n->children->str));
+						SEEN_NO_DUP(tally, 13);
+					} else {
+						return -1;
+					}
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown segment argument %s:", n->str);
+				}
+				return -1;
+			}
+		}
+	}
+	if (tally >= 0) { /* need start, end, layer, thickness at a minimum */
+		CreateNewLineOnLayer( &st->PCB->Data->Layer[PCBLayer], X1, Y1, X2, Y2, Thickness, Clearance, Flags);
+		pcb_printf("\tnew segment on layer created\n");
+		return 0;
+	}
+	return -1;
+}
+
+/* Parse a layer definition and do all the administration needed for the layer */
+static int kicad_create_layer(read_state_t *st, int lnum, const char *lname, const char *ltype)
+{
+	int id = -1;
+	switch(lnum) {
+		case 0:
+			id = SOLDER_LAYER;
+			pcb_layer_rename(id, lname);
+			break;
+		case 15:
+			id = COMPONENT_LAYER;
+			pcb_layer_rename(id, lname);
+			break;
+		default:
+			if (strcmp(lname, "Edge.Cuts") == 0) {
+				/* Edge must be the outline */
+				id = pcb_layer_create(PCB_LYT_OUTLINE, 0, 0, "outline");
+			}
+			else if ((strcmp(ltype, "signal") == 0) || (strncmp(lname, "Dwgs.", 4) == 0) || (strncmp(lname, "Cmts.", 4) == 0) || (strncmp(lname, "Eco", 3) == 0)) {
+				/* Create a new inner layer for signals and for emulating misc layers */
+				id = pcb_layer_create(PCB_LYT_INTERN | PCB_LYT_COPPER, 0, 0, lname);
+			}
+#if 0
+			else if ((lname[1] == '.') && ((lname[0] == 'F') || (lname[0] == 'B'))) {
+				/* F. or B. layers */
+				if (strcmp(lname+2, "SilkS") == 0) return 0; /* silk layers are implicit */
+				if (strcmp(lname+2, "Adhes") == 0) return 0; /* pcb-rnd has no adhesive support */
+				if (strcmp(lname+2, "Paste") == 0) return 0; /* pcb-rnd has no custom paste support */
+				if (strcmp(lname+2, "Mask") == 0)  return 0; /* pcb-rnd has no custom mask support */
+				return -1; /* unknown F. or B. layer -> error */
+			}
+#endif
+			else if (lnum > 15) {
+				/* HACK/WORKAROUND: remember kicad layers for those that are unsupported */
+				htsi_set(&st->layer_k2i, pcb_strdup(lname), -lnum);
+				return 0;
+			}
+			else
+				return -1; /* unknown field */
+	}
+
+/* valid layer, save it in the hash */
+	if (id >= 0) {
+	htsi_set(&st->layer_k2i, pcb_strdup(lname), id);
+	} else {
+		assert(id < -1);
+	}
+	return 0;
+}
+
+/* Register a kicad layer in the layer hash after looking up the pcb-rnd equivalent */
+static unsigned int kicad_reg_layer(read_state_t *st, const char *kicad_name, unsigned int mask)
+{
+	int id;
+	if (pcb_layer_list(mask, &id, 1) != 1)
+		return 1;
+	htsi_set(&st->layer_k2i, pcb_strdup(kicad_name), id);
+	return 0;
+}
+
+/* Returns the pcb-rnd layer index for a kicad_name, or -1 if not found */
+static int kicad_get_layeridx(read_state_t *st, const char *kicad_name)
+{
+	htsi_entry_t *e;
+	e = htsi_getentry(&st->layer_k2i, kicad_name);
+	if (e == NULL)
+		return -1;
+	return e->value;
+}
+
+/* kicad_pcb  parse (layers  )  - either board layer defintions, or module pad/via layer defs */
+static int kicad_parse_layer_definitions(read_state_t *st, gsxl_node_t *subtree)
+{
+	gsxl_node_t *n;
+	int i;
+	unsigned int res;
+
+		if (strcmp(subtree->parent->parent->str, "kicad_pcb") != 0) { /* test if deeper in tree than layer definitions for entire board */  
+			pcb_printf("layer definitions encountered in unexpected place in kicad layout\n");
+			return -1;
+		} else { /* we are just below the top level or root of the tree, so this must be a layer definitions section */
+			pcb_layers_reset();
+			pcb_printf("Board layer descriptions:\n");
+			for(n = subtree,i = 0; n != NULL; n = n->next, i++) {
+				if ((n->str != NULL) && (n->children->str != NULL) && (n->children->next != NULL) && (n->children->next->str != NULL) ) {
+					int lnum = atoi(n->str);
+					const char *lname = n->children->str, *ltype = n->children->next->str;
+					pcb_printf("\tlayer #%d LAYERNUM found:\t%s\n", i, n->str);
+					pcb_printf("\tlayer #%d layer label found:\t%s\n", i, lname);
+					pcb_printf("\tlayer #%d layer description/type found:\t%s\n", i, ltype);
+					if (kicad_create_layer(st, lnum, lname, ltype) < 0) {
+						Message(PCB_MSG_ERROR, "Unrecognized layer: %d, %s, %s\n", lnum, lname, ltype);
+						return -1;
+					}
+				} else {
+					printf("unexpected board layer definition(s) encountered.\n");
+					return -1;
+				}
+			}
+			pcb_layers_finalize();
+
+			/* set up the hash for implicit layers */
+			res = 0;
+			res |= kicad_reg_layer(st, "F.SilkS", PCB_LYT_SILK | PCB_LYT_TOP);
+			res |= kicad_reg_layer(st, "B.SilkS", PCB_LYT_SILK | PCB_LYT_BOTTOM);
+
+			/*
+			We don't have custom mask layers yet
+			res |= kicad_reg_layer(st, "F.Mask",  PCB_LYT_MASK | PCB_LYT_TOP);
+			res |= kicad_reg_layer(st, "B.Mask",  PCB_LYT_MASK | PCB_LYT_BOTTOM);
+			*/
+
+			if (res != 0) {
+				Message(PCB_MSG_ERROR, "Internal error: can't find a silk or mask layer\n");
+				return -1;
+			}
+
+			return 0;
+		}
+}
+
+/* kicad_pcb  parse (net  ) ;   used for net descriptions for the entire layout */
+static int kicad_parse_net(read_state_t *st, gsxl_node_t *subtree)
+{
+		if (subtree != NULL && subtree->str != NULL) {
+			pcb_printf("net number: '%s'\n", subtree->str);
+		} else {
+			pcb_printf("missing net number in net descriptors");
+			return -1;
+		}
+		if (subtree->next != NULL && subtree->next->str != NULL) {
+			pcb_printf("\tcorresponding net label: '%s'\n", (subtree->next->str));
+		} else {
+			pcb_printf("missing net label in net descriptors");
+			return -1;
+		}
+		return 0;
+}
+
+static int kicad_parse_module(read_state_t *st, gsxl_node_t *subtree)
+{
+
+	gsxl_node_t *l, *n, *m, *p;
+	int i;
+	int scaling = 100;
+	int textLength = 0;
+	int mirrored = 0;
+	int moduleDefined = 0;
+	int PCBLayer = 0;
+	int kicadLayer = 15; /* default = top side */
+	int SMD = 0;
+	int square = 0;
+	int throughHole = 0;
+	int foundRefdes = 0;
+	int refdesScaling  = 100;
+	unsigned long tally = 0, featureTally, required;
+	Coord moduleX, moduleY, X, Y, X1, Y1, X2, Y2, centreX, centreY, endX, endY, width, height, Thickness, Clearance, padXsize, padYsize, drill, refdesX, refdesY;
+	Angle startAngle = 0.0;
+	Angle endAngle = 0.0;
+	Angle delta = 360.0; /* these defaults allow a fp_circle to be parsed, which does not specify (angle XXX) */
+	double val;
+	double glyphWidth = 1.27; /* a reasonable approximation of pcb glyph width, ~=  5000 centimils */
+	unsigned direction = 0; /* default is horizontal */
+	char * end, * textLabel, * text;
+	char * moduleName, * moduleRefdes, * moduleValue, * pinName;
+	ElementTypePtr newModule;
+
+	FlagType Flags = MakeFlags(0); /* start with something bland here */
+	FlagType TextFlags = MakeFlags(0); /* start with something bland here */
+	Clearance = PCB_MM_TO_COORD(0.250); /* start with something bland here */
+
+	moduleName = moduleRefdes = moduleValue = NULL;
+
+	if (subtree->str != NULL) {
+		printf("Name of module element being parsed: '%s'\n", subtree->str);
+		moduleName = subtree->str;
+		for(n = subtree->next, i = 0; n != NULL; n = n->next, i++) {
+			if (n->str != NULL && strcmp("layer", n->str) == 0) { /* need this to sort out ONSOLDER flags etc... */
+				SEEN_NO_DUP(tally, 0);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tlayer: '%s'\n", (n->children->str));
+					PCBLayer = kicad_get_layeridx(st, n->children->str);
+					if (PCBLayer == -1) {
+						return -1;
+					} else if (pcb_layer_flags(PCBLayer) & PCB_LYT_BOTTOM) {
+							Flags = MakeFlags(PCB_FLAG_ONSOLDER);
+							TextFlags = MakeFlags(PCB_FLAG_ONSOLDER);
+					}
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("tedit", n->str) == 0) {
+				SEEN_NO_DUP(tally, 1);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\ttedit: '%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("tstamp", n->str) == 0) {
+				SEEN_NO_DUP(tally, 2);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\ttstamp: '%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("at", n->str) == 0) {
+				SEEN_NO_DUP(tally, 3);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tat x: '%s'\n", (n->children->str));
+					SEEN_NO_DUP(tally, 4); /* same as ^= 1 was */
+						val = strtod(n->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							moduleX = PCB_MM_TO_COORD(val);
+						}
+				} else {
+					return -1;
+				}
+				if (n->children->next != NULL && n->children->next->str != NULL) {
+					pcb_printf("\tat y: '%s'\n", (n->children->next->str));
+					SEEN_NO_DUP(tally, 5);	
+						val = strtod(n->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							moduleY = PCB_MM_TO_COORD(val);
+						}
+				} else {
+					return -1;
+				}
+
+			} else if (n->str != NULL && strcmp("fp_text", n->str) == 0) {
+					pcb_printf("fp_text found\n");
+					featureTally = 0;
+
+/* ********************************************************** */
+
+
+	if (n->children != NULL && n->children->str != NULL) {
+		textLabel = n->children->str;
+		printf("fp_text element being parsed for %s - label: '%s'\n", moduleName, textLabel);
+		if (n->children->next != NULL && n->children->next->str != NULL) {
+			text = n->children->next->str;
+			foundRefdes = 0;
+			if (strcmp("reference", textLabel) == 0) {
+				printf("\tfp_text reference found: '%s'\n", textLabel);
+				moduleRefdes = text;
+				foundRefdes = 1;
+				for (i = 0; text[i] != 0; i++) {
+					textLength++;
+				}
+				printf("\tmoduleRefdes now: '%s'\n", moduleRefdes);
+			} else if (strcmp("value", textLabel) == 0) {
+				printf("\tfp_text value found: '%s'\n", textLabel);
+				moduleValue = text;
+				foundRefdes = 0;
+				printf("\tmoduleValue now: '%s'\n", moduleValue);
+			} else {
+				foundRefdes = 0;
+			}
+		} else {
+			text = textLabel; /* just a single string, no reference or value */ 
+		}
+
+		printf("\tfp_text element length: '%d'\n", textLength);
+		for(l = n->children->next->next, i = 0; l != NULL; l = l->next, i++) { /*fixed this */
+			if (l->str != NULL && strcmp("at", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 0);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\ttext at x: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 1);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X = PCB_MM_TO_COORD(val);
+							if (foundRefdes) {
+								refdesX = X;
+								pcb_printf("\tRefdesX = %mm", refdesX);
+
+							}
+						}
+					} else {
+						return -1;
+					}
+					if (l->children->next != NULL && l->children->next->str != NULL) {
+						pcb_printf("\ttext at y: '%s'\n", (l->children->next->str));
+						SEEN_NO_DUP(featureTally, 2);
+						val = strtod(l->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y = PCB_MM_TO_COORD(val);
+							if (foundRefdes) {
+								refdesY = Y;
+								pcb_printf("\tRefdesX = %mm", refdesY);
+							}
+						}	
+						if (l->children->next->next != NULL && l->children->next->next->str != NULL) {
+							pcb_printf("\ttext rotation: '%s'\n", (l->children->next->next->str));
+							val = strtod(l->children->next->next->str, &end);
+							if (*end != 0) {
+								return -1;
+							} else {
+								direction = 0;  /* default */
+								if (val > 45.0 && val <= 135.0) {
+									direction = 1;
+								} else if (val > 135.0 && val <= 225.0) {
+									direction = 2;
+								} else if (val > 225.0 && val <= 315.0) {
+									direction = 3;
+								}
+								printf("\tkicad angle: %f,   Direction %d\n", val, direction);
+							}
+							SEEN_NO_DUP(featureTally, 3);
+						} 
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("layer", l->str) == 0) {
+				SEEN_NO_DUP(featureTally, 4);
+				if (l->children != NULL && l->children->str != NULL) {
+					pcb_printf("\ttext layer: '%s'\n", (l->children->str));
+					PCBLayer = kicad_get_layeridx(st, l->children->str);
+					if (PCBLayer == -1) {
+						return -1;
+					} else if (pcb_layer_flags(PCBLayer) & PCB_LYT_BOTTOM) {
+							Flags = MakeFlags(PCB_FLAG_ONSOLDER);
+					}
+				} else {
+					return -1;
+				}
+			} else if (l->str != NULL && strcmp("effects", l->str) == 0) {
+				SEEN_NO_DUP(featureTally, 5);
+				for(m = l->children; m != NULL; m = m->next) {
+					/*printf("\tstepping through effects def, looking at %s\n", m->str);*/ 
+					if (m->str != NULL && strcmp("font", m->str) == 0) {
+						SEEN_NO_DUP(featureTally, 6);
+						for(p = m->children; p != NULL; p = p->next) {
+							if (m->str != NULL && strcmp("size", p->str) == 0) {
+								SEEN_NO_DUP(featureTally, 7);
+								if (p->children != NULL && p->children->str != NULL) {
+									pcb_printf("\tfont sizeX: '%s'\n", (p->children->str));
+									val = strtod(p->children->str, &end);
+									if (*end != 0) {
+										return -1;
+									} else {
+										scaling = (int) (100*val/1.27); /* standard glyph width ~= 1.27mm */
+										if (foundRefdes) {
+											refdesScaling = scaling;
+											foundRefdes = 0;
+										}
+									}
+								} else {
+									return -1;
+								}
+								if (p->children->next != NULL && p->children->next->str != NULL) {
+									pcb_printf("\tfont sizeY: '%s'\n", (p->children->next->str));
+								} else {
+									return -1;
+								}
+							} else if (strcmp("thickness", p->str) == 0) {
+								SEEN_NO_DUP(featureTally, 8);
+								if (p->children != NULL && p->children->str != NULL) {
+									pcb_printf("\tfont thickness: '%s'\n", (p->children->str));
+								} else {
+									return -1;
+								}
+							}
+						}
+					} else if (m->str != NULL && strcmp("justify", m->str) == 0) {
+						SEEN_NO_DUP(featureTally, 9);
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\ttext justification: '%s'\n", (m->children->str));
+							if (strcmp("mirror", m->children->str) == 0) {
+								mirrored = 1;
+							}
+						} else {
+							return -1;
+						}
+					} else {
+						if (m->str != NULL) {
+							printf("Unknown text effects argument %s:", m->str);
+						}
+						return -1;	
+					}
+				}
+			} 				
+		}
+	}
+	required = 1; /*BV(2) | BV(3) | BV(4) | BV(7) | BV(8); */
+	if ((featureTally & required) == required) { /* has location, layer, size and stroke thickness at a minimum */
+		if (&st->PCB->Font == NULL) {
+			CreateDefaultFont(st->PCB);
+		}
+
+		X = refdesX;
+		Y = refdesY;
+		glyphWidth = 1.27;
+		glyphWidth = glyphWidth * refdesScaling/100.0;
+
+		if (mirrored != 0) {
+			if (direction%2 == 0) {
+				direction += 2;
+				direction = direction%4;
+			}
+			if (direction == 0 ) {
+				X -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y += PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 1 ) {
+				Y -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X -= PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 2 ) {
+				X += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y -= PCB_MM_TO_COORD(glyphWidth/2.0);  /* centre it vertically */
+			} else if (direction == 3 ) {
+				Y += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X += PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			}
+		} else { /* not back of board text */
+			if (direction == 0 ) {
+				X -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y -= PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 1 ) {
+				Y += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X -= PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			} else if (direction == 2 ) {
+				X += PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				Y += PCB_MM_TO_COORD(glyphWidth/2.0);  /* centre it vertically */
+			} else if (direction == 3 ) {
+				Y -= PCB_MM_TO_COORD((glyphWidth * textLength)/2.0);
+				X += PCB_MM_TO_COORD(glyphWidth/2.0); /* centre it vertically */
+			}
+		}
+
+		if (moduleValue != NULL && moduleRefdes != NULL && moduleName != NULL && moduleDefined == 0) {
+			moduleDefined = 1;
+			printf("now have RefDes %s and Value %s, can now define module/element %s\n", moduleRefdes, moduleValue, moduleName);
+			newModule = CreateNewElement(st->PCB->Data, NULL,
+								 &st->PCB->Font, Flags,
+								 moduleName, moduleRefdes, moduleValue,
+								 moduleX, moduleY, direction,
+								 refdesScaling, TextFlags,  pcb_false); /*FlagType TextFlags, pcb_bool uniqueName) */
+			MoveObject(PCB_TYPE_ELEMENT_NAME, newModule,  &newModule->Name[NAME_INDEX()],  &newModule->Name[NAME_INDEX()], X, Y);
+		}
+	}
+
+
+/* ********************************************************** */
+
+			} else if (n->str != NULL && strcmp("descr", n->str) == 0) {
+				SEEN_NO_DUP(tally, 6);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tmodule descr: '%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("tags", n->str) == 0) {
+				SEEN_NO_DUP(tally, 7);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tmodule tags: '%s'\n", (n->children->str)); /* maye be more than one? */
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("path", n->str) == 0) {
+				SEEN_NO_DUP(tally, 8);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tmodule path: '%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("model", n->str) == 0) {
+				SEEN_NO_DUP(tally, 9);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tmodule model provided: '%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			/* pads next  - have thru_hole, circle, rect, roundrect, to think about*/ 
+			} else if (n->str != NULL && strcmp("pad", n->str) == 0) {
+				if (n->children != 0 && n->children->str != NULL) {
+					printf("pad name found: %s\n", n->children->str);
+					pinName = n->children->str;
+					if (n->children->next != NULL && n->children->next->str != NULL) {
+						pcb_printf("\tpad type: '%s'\n", (n->children->next->str));
+						if (strcmp("thru_hole", n->children->next->str) == 0) {
+							SMD = 0;
+							throughHole = 1;
+						} else {
+							SMD = 1;
+							throughHole = 0;
+						}
+						if (n->children->next->next != NULL && n->children->next->next->str != NULL) {
+							pcb_printf("\tpad shape: '%s'\n", (n->children->next->next->str));
+							if (strcmp("circle", n->children->next->next->str) == 0) {
+								square = 0;
+							} else {
+								square = 1; /* this will catch obround, roundrect, trapezoidal as well. Kicad does not do octagonal pads */
+							}
+						} else {
+							return -1;
+						}
+					} else {
+						return -1;
+					}
+				} else {
+					return -1;
+				}
+				if (n->children->next->next->next == NULL || n->children->next->next->next->str == NULL) {
+					return -1;
+				}
+				for (m = n->children->next->next->next; m != NULL; m = m->next) {
+					if (m != NULL) {
+						printf("\tstepping through module pad defs, looking at: %s\n", m->str);
+					} else {
+						printf("error in pad def\n");
+					}
+					if (m->str != NULL && strcmp("at", m->str) == 0) {
+						/*SEEN_NO_DUP(padTally, 1); */
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tpad X position:\t'%s'\n", (m->children->str));
+							val = strtod(m->children->str, &end);
+							if (*end != 0) {
+								return -1;
+							} else {
+								X = PCB_MM_TO_COORD(val);
+							}
+							if (m->children->next != NULL && m->children->next->str != NULL) {
+								pcb_printf("\tpad Y position:\t'%s'\n", (m->children->next->str));
+								val = strtod(m->children->next->str, &end);
+								if (*end != 0) {
+									return -1;
+								} else {
+									Y = PCB_MM_TO_COORD(val);
+								}
+							} else {
+								return -1;
+							}
+						} else {
+							return -1;
+						}
+					} else if (m->str != NULL && strcmp("layers", m->str) == 0) {
+						if (SMD) { /* skip testing for pins */
+							/*SEEN_NO_DUP(padTally, 2);*/
+							kicadLayer = 15;
+							for(l = m->children; l != NULL; l = l->next) {
+								if (l->str != NULL) {
+									PCBLayer = kicad_get_layeridx(st, l->str);
+									if (PCBLayer == -1) {
+										printf("Unknown layer definition: %s", l->str);
+										return -1;
+									} else if (PCBLayer < -1) {
+										printf("\tUnimplemented layer definition: %s", l->str);
+									} else if (pcb_layer_flags(PCBLayer) & PCB_LYT_BOTTOM) {
+										Flags = MakeFlags(PCB_FLAG_ONSOLDER);
+										kicadLayer = 0;
+									}
+									pcb_printf("\tpad layer: '%s',  PCB layer number %d\n", (l->str), kicad_get_layeridx(st, l->str));
+								} else {
+									return -1;
+								}
+							}
+						} else {	
+							printf("\tIgnoring layer definitions for through hole pin\n");
+						}
+					} else if (m->str != NULL && strcmp("drill", m->str) == 0) {
+						/*SEEN_NO_DUP(padTally, 3);*/
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tdrill size: '%s'\n", (m->children->str));
+							val = strtod(m->children->str, &end);
+							if (*end != 0) {
+								return -1;
+							} else {
+								drill = PCB_MM_TO_COORD(val);
+							}
+
+						} else {
+							return -1;
+						}
+					} else if (m->str != NULL && strcmp("net", m->str) == 0) { 
+						/*SEEN_NO_DUP(padTally, 4);*/
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tpad's net number:\t'%s'\n", (m->children->str));
+							if (m->children->next != NULL && m->children->next->str != NULL) {
+								pcb_printf("\tpad's net name:\t'%s'\n", (m->children->next->str));
+							} else {
+								return -1;
+							}
+						} else {
+							return -1;
+						}
+					} else if (m->str != NULL && strcmp("size", m->str) == 0) {
+						/*SEEN_NO_DUP(padTally, 5);*/
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tpad X size:\t'%s'\n", (m->children->str));
+							val = strtod(m->children->str, &end);
+							if (*end != 0) {
+								return -1;
+							} else {
+								padXsize = PCB_MM_TO_COORD(val);
+							}
+							if (m->children->next != NULL && m->children->next->str != NULL) {
+								pcb_printf("\tpad Y size:\t'%s'\n", (m->children->next->str));
+								val = strtod(m->children->next->str, &end);
+								if (*end != 0) {
+									return -1;
+								} else {
+									padYsize = PCB_MM_TO_COORD(val);
+								}
+							} else {
+								return -1;
+							}
+						} else {
+							return -1;
+						}
+					} else {
+						if (m->str != NULL) {
+							printf("Unknown pad argument %s:", m->str);
+						}
+					} 
+					printf("\tFinished stepping through pad args\n");
+				}
+				printf("\tfinished pad parse\n");
+				if (throughHole == 1) {
+					printf("\tcreating new pin %s in element\n", pinName);				
+					CreateNewPin(newModule, X + moduleX, Y + moduleY, padXsize, Clearance,
+								Clearance, drill, pinName, pinName, Flags); /* using clearance value for arg 5 = mask too */
+				} else {
+					printf("\tcreating new pad %s in element\n", pinName);				
+					if (padXsize >= padYsize) { /* square pad or rectangular pad, wider than tall */
+						Y1 = Y2 = Y;
+						X1 = X - (padXsize - padYsize)/2;
+						X2 = X + (padXsize - padYsize)/2;
+						Thickness = padYsize;
+					} else { /* rectangular pad, taller than wide */
+						X1 = X2 = X;
+						Y1 = Y - (padYsize - padXsize)/2;
+						Y2 = Y + (padYsize - padXsize)/2;
+						Thickness = padXsize;
+					}
+					if (square && kicadLayer) {
+						Flags = MakeFlags(PCB_FLAG_SQUARE);
+					} else if (kicadLayer) {
+						Flags = MakeFlags(0);
+					} else if (square && !kicadLayer) {
+						Flags = MakeFlags(PCB_FLAG_SQUARE | PCB_FLAG_ONSOLDER);
+					} else {
+						Flags = MakeFlags(PCB_FLAG_ONSOLDER);
+					}
+					CreateNewPad(newModule, X1 + moduleX, Y1 + moduleY, X2 + moduleX, Y2 + moduleY, Thickness, Clearance, 
+								Clearance, pinName, pinName, Flags); /* using clearance value for arg 7 = mask too */
+				}
+
+			} else if (n->str != NULL && strcmp("fp_line", n->str) == 0) {
+					pcb_printf("fp_line found\n");
+					featureTally = 0;
+
+/* ********************************************************** */
+
+	if (n->children->str != NULL) {
+		for(l = n->children; l != NULL; l = l->next) {
+			printf("\tnow looking at fp_line text: '%s'\n", l->str);
+			if (l->str != NULL && strcmp("start", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 0);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_line start at x: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 1); 
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X1 = PCB_MM_TO_COORD(val) + moduleX;
+						}
+					} else {
+						return -1;
+					}
+					if (l->children->next != NULL && l->children->next->str != NULL) {
+						pcb_printf("\tfp_line start at y: '%s'\n", (l->children->next->str));
+						SEEN_NO_DUP(featureTally, 2);	
+						val = strtod(l->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y1 = PCB_MM_TO_COORD(val) + moduleY;
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("end", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 3);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_line end at x: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 4);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							X2 = PCB_MM_TO_COORD(val) + moduleX;
+						}
+					} else {
+						return -1;
+					}
+					if (l->children->next != NULL && l->children->next->str != NULL) {
+						pcb_printf("\tfp_line end at y: '%s'\n", (l->children->next->str));
+						SEEN_NO_DUP(featureTally, 5);	
+						val = strtod(l->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Y2 = PCB_MM_TO_COORD(val) + moduleY;
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("layer", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 6);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_line layer: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 7);
+						PCBLayer = kicad_get_layeridx(st, l->children->str);
+						if (PCBLayer == -1) {
+							return -1;
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("width", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 8);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_line width: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 9);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Thickness = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("angle", l->str) == 0) { /* unlikely to be used or seen */
+					SEEN_NO_DUP(featureTally, 10);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_line angle: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 11);
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("net", l->str) == 0) { /* unlikely to be used or seen */
+					SEEN_NO_DUP(featureTally, 12);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_line net: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 13);
+					} else {
+						return -1;
+					}
+			} else {
+				if (l->str != NULL) {
+					printf("Unknown fp_line argument %s:", l->str);
+				}
+				return -1;
+			}
+		}
+	}
+	if (featureTally >= 0 && newModule != NULL) { /* need start, end, layer, thickness at a minimum */
+
+		CreateNewLineInElement(newModule, X1, Y1, X2, Y2, Thickness);
+		pcb_printf("\tnew fp_line on layer created\n");
+	}
+
+/* ********************************************************** */
+
+			} else if ((n->str != NULL && strcmp("fp_arc", n->str) == 0) || (n->str != NULL && strcmp("fp_circle", n->str) == 0)) {
+					pcb_printf("fp_arc or fp_circle found\n");
+					featureTally = 0;
+
+/* ********************************************************** */
+
+	if (subtree->str != NULL) {
+		printf("fp_arc being parsed: '%s'\n", subtree->str);		
+		for(l = n->children; l != NULL; l = l->next) {
+			if (l->str != NULL && strcmp("start", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 0);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc centre at x: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 1);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreX = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (l->children->next != NULL && l->children->next->str != NULL) {
+						pcb_printf("\tfp_arc centre at y: '%s'\n", (l->children->next->str));
+						SEEN_NO_DUP(featureTally, 2);	
+						val = strtod(l->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreY = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("center", l->str) == 0) { /* this lets us parse a circle too */
+					SEEN_NO_DUP(featureTally, 0);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc centre at x: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 1);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreX = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (l->children->next != NULL && l->children->next->str != NULL) {
+						pcb_printf("\tfp_arc centre at y: '%s'\n", (l->children->next->str));
+						SEEN_NO_DUP(featureTally, 2);
+						val = strtod(l->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							centreY = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("end", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 3);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc end at x: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 4);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							endX = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+					if (l->children->next != NULL && l->children->next->str != NULL) {
+						pcb_printf("\tfp_arc end at y: '%s'\n", (l->children->next->str));
+						SEEN_NO_DUP(featureTally, 5);
+						val = strtod(l->children->next->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							endY = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("layer", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 6);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc layer: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 7);
+						PCBLayer = kicad_get_layeridx(st, l->children->str);
+						if (PCBLayer == -1) {
+							return -1;
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("width", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 8);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc width: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 9);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							Thickness = PCB_MM_TO_COORD(val);
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("angle", l->str) == 0) {
+					SEEN_NO_DUP(featureTally, 10);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc angle CW rotation: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 11);
+						val = strtod(l->children->str, &end);
+						if (*end != 0) {
+							return -1;
+						} else {
+							delta = val;
+						}
+					} else {
+						return -1;
+					}
+			} else if (l->str != NULL && strcmp("net", l->str) == 0) { /* unlikely to be used or seen */
+					SEEN_NO_DUP(featureTally, 12);
+					if (l->children != NULL && l->children->str != NULL) {
+						pcb_printf("\tfp_arc net: '%s'\n", (l->children->str));
+						SEEN_NO_DUP(featureTally, 13);
+					} else {
+						return -1;
+					}
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown gr_arc argument %s:", l->str);
+				}
+				return -1;
+			}
+		}
+	}
+	if (featureTally >= 0) { /* need start, end, layer, thickness at a minimum */
+		width = height = Distance(centreX, centreY, endX, endY); /* calculate radius of arc */
+		if (width < 1) { /* degenerate case */
+			startAngle = 0;
+		} else {
+			endAngle = 180*atan2(-(endY - centreY), endX - centreX)/M_PI; /* avoid using atan2 with zero parameters */
+			if (endAngle < 0.0) {
+				endAngle += 360.0; /*make it 0...360 */
+			}
+			startAngle = endAngle + delta; /* geda is 180 degrees out of phase with kicad, and opposite direction rotation */
+			if (startAngle > 360.0) {
+				startAngle -= 360.0;
+			}
+			if (startAngle < 0.0) {
+				startAngle += 360.0;
+			}
+
+		}
+
+		if (featureTally >= 0 && newModule != NULL) { /* need start, end, layer, thickness at a minimum */
+			CreateNewArcInElement(newModule, moduleX + centreX, moduleY + centreY, width, height, endAngle, delta, Thickness);
+		}
+	}
+
+/* ********************************************************** */
+
+
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown pad argument : %s\n", n->str);
+				}
+			} 
+		}
+		SetElementBoundingBox(PCB->Data, newModule, &PCB->Font);
+		return 0; 
+
+	/* required = 1; BV(2) | BV(3) | BV(4) | BV(7) | BV(8);
+	if ((tally & required) == required) {  */ /* has location, layer, size and stroke thickness at a minimum */
+	} else {
+		return -1;
+	}
+}
+
+static int kicad_parse_zone(read_state_t *st, gsxl_node_t *subtree)
+{
+
+	gsxl_node_t *n, *m;
+	int i;
+	int polycount = 0;
+	long j  = 0;
+	unsigned long tally = 0;
+/*	unsigned long required;     yet to be implemented */
+
+	PolygonTypePtr polygon = NULL;
+	FlagType flags = MakeFlags(PCB_FLAG_CLEARPOLY);
+	char *end;
+	double val;
+	Coord X, Y;
+	int PCBLayer = 0;
+
+	if (subtree->str != NULL) {
+		printf("Zone element found:\t'%s'\n", subtree->str);
+		for(n = subtree->next,i = 0; n != NULL; n = n->next, i++) {
+			if (n->str != NULL && strcmp("net", n->str) == 0) {
+				SEEN_NO_DUP(tally, 0);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tzone net number:\t'%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("net_name", n->str) == 0) {
+				SEEN_NO_DUP(tally, 1);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tzone net_name:\t'%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("tstamp", n->str) == 0) {
+				SEEN_NO_DUP(tally, 2);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tzone tstamp:\t'%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("hatch", n->str) == 0) {
+				SEEN_NO_DUP(tally, 3);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tzone hatch_edge:\t'%s'\n", (n->children->str));
+					SEEN_NO_DUP(tally, 4); /* same as ^= 1 was */
+				} else {
+					return -1;
+				}
+				if (n->children->next != NULL && n->children->next->str != NULL) {
+					pcb_printf("\tzone hatching size:\t'%s'\n", (n->children->next->str));
+					SEEN_NO_DUP(tally, 5);	
+				} else {
+					return -1;
+			}
+			} else if (n->str != NULL && strcmp("connect_pads", n->str) == 0) {
+				SEEN_NO_DUP(tally, 6);
+				if (n->children != NULL && n->children->str != NULL && (strcmp("clearance", n->children->str) == 0) && (n->children->children->str != NULL)) {
+					pcb_printf("\tzone clearance:\t'%s'\n", (n->children->children->str));  /* this is if yes/no flag for connected pads is absent */
+					SEEN_NO_DUP(tally, 7); /* same as ^= 1 was */
+				} else if (n->children != NULL && n->children->str != NULL && n->children->next->str != NULL) {
+					pcb_printf("\tzone connect_pads:\t'%s'\n", (n->children->str));  /* this is if the optional(!) yes or no flag for connected pads is present */
+					SEEN_NO_DUP(tally, 7); /* same as ^= 1 was */
+					if (n->children->next != NULL && n->children->next->str != NULL && n->children->next->children != NULL && n->children->next->children->str != NULL) {
+						if (strcmp("clearance", n->children->next->str) == 0) {
+							SEEN_NO_DUP(tally, 8);
+							pcb_printf("\tzone connect_pads clearance: '%s'\n", (n->children->next->children->str));
+						} else {
+							printf("Unrecognised zone connect_pads option %s\n", n->children->next->str);
+						}
+					}
+				}
+			} else if (n->str != NULL && strcmp("layer", n->str) == 0) {
+				SEEN_NO_DUP(tally, 9);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tzone layer:\t'%s'\n", (n->children->str));
+					PCBLayer = kicad_get_layeridx(st, n->children->str);
+					if (PCBLayer == -1) {
+						return -1;
+					}
+					polygon = CreateNewPolygon(&st->PCB->Data->Layer[PCBLayer], flags);
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("polygon", n->str) == 0) {
+				printf("Processing polygon [%d] points:\n", polycount);
+				polycount++; /*keep track of number of polygons in zone */
+				if (n->children != NULL && n->children->str != NULL) {
+					if (strcmp("pts", n->children->str) == 0) {
+						for (m = n->children->children, j =0; m != NULL; m = m->next, j++) {
+							if (m->str != NULL && strcmp("xy", m->str) == 0) {
+								if (m->children != NULL && m->children->str != NULL) {
+									pcb_printf("\tvertex X[%d]:\t'%s'\n", j, (m->children->str));
+									val = strtod(m->children->str, &end);
+									if (*end != 0) {
+										return -1;
+									} else {
+										X = PCB_MM_TO_COORD(val);
+									}
+									if (m->children->next != NULL && m->children->next->str != NULL) {
+										pcb_printf("\tvertex Y[%d]:\t'%s'\n", j, (m->children->next->str));
+										val = strtod(m->children->next->str, &end);
+										if (*end != 0) {
+											return -1;
+										} else {
+											Y = PCB_MM_TO_COORD(val);
+										}
+										if (polygon != NULL) {
+											CreateNewPointInPolygon(polygon, X, Y);
+										}
+									} else {
+										return -1;
+									}
+								} else {
+									return -1;
+								}
+							}
+						}
+					} else {
+						printf("pts section not found in polygon.\n");
+						return -1;
+					}
+				} else {
+					printf("Empty polygon!\n");
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("fill", n->str) == 0) {
+				SEEN_NO_DUP(tally, 10);
+				printf("\tReading fill settings:\n");
+				for (m = n->children; m != NULL; m = m->next) {
+					if (m->str != NULL && strcmp("arc_segments", m->str) == 0) {
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tzone arc_segments:\t'%s'\n", (m->children->str));
+						} else {
+							return -1;
+						}
+					} else if (m->str != NULL && strcmp("thermal_gap", m->str) == 0) {
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tzone thermal_gap:\t'%s'\n", (m->children->str));
+						} else {
+							return -1;
+						}
+					} else if (m->str != NULL && strcmp("thermal_bridge_width", m->str) == 0) {
+						if (m->children != NULL && m->children->str != NULL) {
+							pcb_printf("\tzone thermal_bridge_width:\t'%s'\n", (m->children->str));
+						} else {
+							return -1;
+						}
+					} else if (m->str != NULL) {
+						printf("Unknown zone fill argument:\t%s\n", m->str);
+					}
+				}
+			} else if (n->str != NULL && strcmp("min_thickness", n->str) == 0) {
+				SEEN_NO_DUP(tally, 11);
+				if (n->children != NULL && n->children->str != NULL) {
+					pcb_printf("\tzone min_thickness:\t'%s'\n", (n->children->str));
+				} else {
+					return -1;
+				}
+			} else if (n->str != NULL && strcmp("filled_polygon", n->str) == 0) {
+				pcb_printf("\tIgnoring filled_polygon definition.\n");
+			} else {
+				if (n->str != NULL) {
+					printf("Unknown polygon argument:\t%s\n", n->str);
+				}
+			} 
+		}
+	} 
+	/* required = 1; BV(2) | BV(3) | BV(4) | BV(7) | BV(8);
+	if ((tally & required) == required) {  */ /* has location, layer, size and stroke thickness at a minimum */
+		if (polygon != NULL) {
+			pcb_add_polygon_on_layer(&st->PCB->Data->Layer[PCBLayer], polygon);
+			InitClip(st->PCB->Data, &st->PCB->Data->Layer[PCBLayer], polygon);
+		}
+		return 0;
+/*}
+	return -1; */
+}
+
+
+/* Parse a board from &st->dom into st->PCB */
+static int kicad_parse_pcb(read_state_t *st)
+{
+	/* gsxl_node_t *n;  not used */
+	static const dispatch_t disp[] = { /* possible children of root */
+		{"version",    kicad_parse_version},
+		{"host",       kicad_parse_nop},
+		{"general",    kicad_parse_nop},
+		{"page",       kicad_parse_page_size},
+		{"layers",     kicad_parse_layer_definitions}, /* board layer defs */
+		{"setup",      kicad_parse_nop},
+		{"net",        kicad_parse_net}, /* net labels if child of root, otherwise net attribute of element */
+		{"net_class",  kicad_parse_nop},
+		{"module",     kicad_parse_module},  /* for footprints */
+		{"gr_line",     kicad_parse_gr_line},
+		{"gr_arc",     kicad_parse_gr_arc},
+		{"gr_circle",     kicad_parse_gr_arc},
+		{"gr_text",    kicad_parse_gr_text},
+		{"via",     kicad_parse_via},
+		{"segment",     kicad_parse_segment},
+		{"zone",     kicad_parse_zone}, /* polygonal zones*/
+		{NULL, NULL}
+	};
+
+	/* require the root node to be kicad_pcb */
+	if ((st->dom.root->str == NULL) || (strcmp(st->dom.root->str, "kicad_pcb") != 0))
+		return -1; 
+
+	/* Call the corresponding subtree parser for each child node of the root
+	   node; if any of them fail, parse fails */
+	return kicad_foreach_dispatch(st, st->dom.root->children, disp);
+}
+	
+int io_kicad_read_pcb(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest)
+{
+	int c, readres = 0;
+	read_state_t st;
+	gsx_parse_res_t res;
+	FILE *FP;
+
+		FP = fopen(Filename, "r");
+	if (FP == NULL)
+		return -1;
+
+	/* set up the parse context */
+	memset(&st, 0, sizeof(st));
+	st.PCB = Ptr;
+	st.Filename = Filename;
+	st.settings_dest = settings_dest;
+	htsi_init(&st.layer_k2i, strhash, strkeyeq);
+
+	/* load the file into the dom */
+	gsxl_init(&st.dom, gsxl_node_t);
+	do {
+		c = fgetc(FP);
+	} while((res = gsxl_parse_char(&st.dom, c)) == GSX_RES_NEXT);
+	fclose(FP);
+
+	if (res == GSX_RES_EOE) {
+		/* compact and simplify the tree */
+		gsxl_compact_tree(&st.dom);
+
+		/* recursively parse the dom */
+		readres = kicad_parse_pcb(&st);
+	}
+	else
+		readres = -1;
+
+	/* clean up */
+	gsxl_uninit(&st.dom);
+
+#warning TODO: free the layer hash
+
+printf("readres=%d\n", readres);
+	return readres;
+}
diff --git a/src_plugins/io_kicad/read.h b/src_plugins/io_kicad/read.h
new file mode 100644
index 0000000..c875baa
--- /dev/null
+++ b/src_plugins/io_kicad/read.h
@@ -0,0 +1,33 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *  Copyright (C) 2016 Erich S. Heinzle
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include "global.h"
+#include "data.h"
+
+int io_kicad_read_pcb(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest);
diff --git a/src_plugins/io_kicad/uniq_name.c b/src_plugins/io_kicad/uniq_name.c
new file mode 100644
index 0000000..fccd940
--- /dev/null
+++ b/src_plugins/io_kicad/uniq_name.c
@@ -0,0 +1,76 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <genht/hash.h>
+#include "uniq_name.h"
+#include "compat_misc.h"
+
+static const char *unm_default_unnamed = "unnamed";
+static const char *unm_default_suffix_sep = "_dup";
+
+void unm_init(unm_t *state)
+{
+	state->unnamed    = unm_default_unnamed;
+	state->suffix_sep = unm_default_suffix_sep;
+	htsp_init(&state->seen, strhash, strkeyeq);
+	state->ctr = 0;
+}
+
+
+void unm_uninit(unm_t *state)
+{
+	htsp_entry_t *e;
+	for (e = htsp_first(&state->seen); e; e = htsp_next(&state->seen, e)) {
+		free(e->key);
+		htsp_delentry(&state->seen, e);
+	}
+	htsp_uninit(&state->seen);
+}
+
+const char *unm_name(unm_t *state, const char *orig_name, void *user_data)
+{
+	int l1, l2;
+	char *name, *end;
+	const char *head;
+
+	if ((orig_name == NULL) || (*orig_name == '\0')) {
+		if (!htsp_has(&state->seen, (char *)state->unnamed)) {
+			name = pcb_strdup(state->unnamed);
+			htsp_set(&state->seen, name, user_data);
+			return name;
+		}
+		head = state->unnamed;
+		l1 = strlen(state->unnamed);
+		l2 = strlen(state->suffix_sep);
+	}
+	else {
+		if (!htsp_has(&state->seen, (char *)orig_name)) {
+			name = pcb_strdup(orig_name);
+			htsp_set(&state->seen, name, user_data);
+			return name;
+		}
+		else {
+			head = orig_name;
+			l1 = strlen(orig_name);
+			l2 = strlen(state->suffix_sep);
+		}
+	}
+
+	/* have to generate a new name, allocate memory and print the static part
+	   leave end to point where the integer part goes
+	   21 is large enough to store the widest 64 bit integer decimal, and a \0 */
+		end = name = malloc(l1+l2+21);
+		memcpy(end, head, l1); end += l1;
+		memcpy(end, state->suffix_sep, l2); end += l2;
+
+	/* look for the first unused entry with this suffix. Note: the entries
+	   this function creates won't collide as ctr is a state of the group, but
+	   it is possible that a new name collides with a past unsuffixed orig_name;
+	   all in all, this loop should exit in the first iteration */
+	do {
+		sprintf(end, "%lu", state->ctr++);
+	} while(htsp_has(&state->seen, name));
+
+	htsp_set(&state->seen, name, user_data);
+	return name;
+}
diff --git a/src_plugins/io_kicad/uniq_name.h b/src_plugins/io_kicad/uniq_name.h
new file mode 100644
index 0000000..611e56c
--- /dev/null
+++ b/src_plugins/io_kicad/uniq_name.h
@@ -0,0 +1,32 @@
+#ifndef PCB_UNIQ_NAME_H
+#define PCB_UNIQ_NAME_H
+
+#include <genht/htsp.h>
+
+typedef struct unm_s {
+	const char *unnamed;     /* name to use when orig_name is NULL */
+	const char *suffix_sep;  /* separator for the suffix appended to generate unique names */
+	htsp_t seen;             /* hash for storing names already handed out */
+	unsigned long int ctr;   /* duplication counter - this will become the suffix for duplicated items */
+} unm_t;
+
+/* Initialize a new group of unique names */
+void unm_init(unm_t *state);
+
+/* Free all memory claimed by the group */
+void unm_uninit(unm_t *state);
+
+/* Generate and return a unique name:
+    - if orig_name is NULL, generate an unnamed item
+    - if orig_name is not-NULL and is unseen so far, return a copy of orig_name
+    - if orig_name is not-NULL and has been already seen, return a modified version
+
+   Corners:
+    - an empty, non-NULL orig_name handled as if it was NULL
+    - the first "unnamed" entry is returned without suffix
+
+   Strings returned are newly allocated and can be used until unm_uninit()
+   is called on state. */
+const char *unm_name(unm_t *state, const char *orig_name, void *user_data);
+
+#endif
diff --git a/src_plugins/io_kicad/write.c b/src_plugins/io_kicad/write.c
new file mode 100644
index 0000000..5c2d1f7
--- /dev/null
+++ b/src_plugins/io_kicad/write.c
@@ -0,0 +1,1304 @@
+/*
+ *														COPYRIGHT
+ *
+ *	pcb-rnd, interactive printed circuit board design
+ *	Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *	Copyright (C) 2016 Erich S. Heinzle
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *	Contact addresses for paper mail and Email:
+ *	Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *	Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+#include <math.h>
+#include "plug_io.h"
+#include "error.h"
+#include "uniq_name.h"
+#include "data.h"
+#include "write.h"
+#include "layer.h"
+#include "const.h"
+#include "netlist.h"
+#include "misc.h"
+
+#define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
+
+/* layer "0" is first copper layer = "0. Back - Solder"
+ * and layer "15" is "15. Front - Component"
+ * and layer "20" SilkScreen Back
+ * and layer "21" SilkScreen Front
+ */
+
+/* generates a line by line listing of the elements being saved */
+static int io_kicad_write_element_index(FILE * FP, DataTypePtr Data);
+
+/* generates text for the kicad layer provided  */
+static char * kicad_sexpr_layer_to_text(int layer);
+
+/* generates a default via drill size for the layout */
+static int write_kicad_layout_via_drill_size(FILE * FP, pcb_cardinal_t indentation);
+
+/* writes the buffer to file */
+int io_kicad_write_buffer(plug_io_t *ctx, FILE * FP, BufferType *buff)
+{
+	/*fputs("io_kicad_legacy_write_buffer()", FP); */
+
+	fputs("PCBNEW-LibModule-V1	jan 01 jan 2016 00:00:01 CET\n",FP);
+	fputs("Units mm\n",FP);
+	fputs("$INDEX\n",FP);
+	io_kicad_write_element_index(FP, buff->Data);
+	fputs("$EndINDEX\n",FP);
+
+	/* WriteViaData(FP, buff->Data); */
+
+	WriteElementData(FP, buff->Data, "kicadl");
+
+	/*
+		for (i = 0; i < max_copper_layer + 2; i++)
+		WriteLayerData(FP, i, &(buff->Data->Layer[i]));
+	*/
+	return (STATUS_OK);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes PCB to file
+ */
+int io_kicad_write_pcb(plug_io_t *ctx, FILE * FP, const char *old_filename, const char *new_filename, pcb_bool emergency)
+{
+	/* this is the first step in exporting a layout;
+	 * creating a kicad module containing the elements used in the layout
+	 */
+
+	/*fputs("io_kicad_legacy_write_pcb()", FP);*/
+
+	int baseSExprIndent = 2;
+
+	pcb_cardinal_t i;
+	int physicalLayerCount = 0;
+	int kicadLayerCount = 0;
+	int silkLayerCount= 0;
+	int outlineLayerCount = 0;
+	int layer = 0;
+	int currentKicadLayer = 0;
+	int currentGroup = 0;
+	Coord outlineThickness = PCB_MIL_TO_COORD(10); 
+
+	int bottomCount;
+	int *bottomLayers;
+	int innerCount;
+	int *innerLayers;
+	int topCount;
+	int *topLayers;
+	int bottomSilkCount;
+	int *bottomSilk;
+	int topSilkCount;
+	int *topSilk;
+	int outlineCount;
+	int *outlineLayers;
+
+	Coord LayoutXOffset;
+	Coord LayoutYOffset;
+
+	/* Kicad expects a layout "sheet" size to be specified in mils, and A4, A3 etc... */
+	int A4HeightMil = 8267;
+	int A4WidthMil = 11700;
+	int sheetHeight = A4HeightMil;
+	int sheetWidth = A4WidthMil;
+	int paperSize = 4; /* default paper size is A4 */
+
+	fputs("(kicad_pcb (version 3) (host pcbnew \"(2013-02-20 BZR 3963)-testing\")",FP);
+
+	fprintf(FP, "\n%*s(general\n", baseSExprIndent, "");
+	fprintf(FP, "%*s)\n", baseSExprIndent, "");
+
+
+	/* we sort out the needed kicad sheet size here, using A4, A3, A2, A1 or A0 size as needed */
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > A4WidthMil ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > A4HeightMil) {
+		sheetHeight = A4WidthMil;		/* 11.7" */
+		sheetWidth = 2*A4HeightMil; /* 16.5" */
+		paperSize = 3; /* this is A3 size */
+	}
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > sheetWidth ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > sheetHeight) {
+		sheetHeight = 2*A4HeightMil; /* 16.5" */
+		sheetWidth = 2*A4WidthMil;	 /* 23.4" */
+		paperSize = 2; /* this is A2 size */
+	}
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > sheetWidth ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > sheetHeight) {
+		sheetHeight = 2*A4WidthMil; /* 23.4" */
+		sheetWidth = 4*A4HeightMil; /* 33.1" */
+		paperSize = 1; /* this is A1 size */
+	}
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > sheetWidth ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > sheetHeight) {
+		sheetHeight = 4*A4HeightMil; /* 33.1" */
+		sheetWidth = 4*A4WidthMil; /* 46.8"	 */
+		paperSize = 0; /* this is A0 size; where would you get it made ?!?! */
+	}
+	fprintf(FP, "\n%*s(page A%d)\n", baseSExprIndent, "", paperSize);
+
+
+	/* we now sort out the offsets for centring the layout in the chosen sheet size here */
+	if (sheetWidth > PCB_COORD_TO_MIL(PCB->MaxWidth)) {	 /* usually A4, bigger if needed */
+		/* fprintf(FP, "%d ", sheetWidth);  legacy kicad: elements decimils, sheet size mils */
+		LayoutXOffset = PCB_MIL_TO_COORD(sheetWidth)/2 - PCB->MaxWidth/2;
+	} else { /* the layout is bigger than A0; most unlikely, but... */
+		/* pcb_fprintf(FP, "%.0ml ", PCB->MaxWidth); */
+		LayoutXOffset = 0;
+	}
+	if (sheetHeight > PCB_COORD_TO_MIL(PCB->MaxHeight)) {
+		/* fprintf(FP, "%d", sheetHeight); */
+		LayoutYOffset = PCB_MIL_TO_COORD(sheetHeight)/2 - PCB->MaxHeight/2;
+	} else { /* the layout is bigger than A0; most unlikely, but... */
+		/* pcb_fprintf(FP, "%.0ml", PCB->MaxHeight); */
+		LayoutYOffset = 0;
+	}
+
+	/* here we define the copper layers in the exported kicad file */
+	physicalLayerCount = pcb_layer_group_list(PCB_LYT_COPPER, NULL, 0);
+
+	fprintf(FP, "\n%*s(layers\n", baseSExprIndent, "");
+	kicadLayerCount = physicalLayerCount;
+	if (kicadLayerCount%2 == 1) {
+		kicadLayerCount++; /* kicad doesn't like odd numbers of layers, has been deprecated for some time apparently */
+	}
+	
+	layer = 0;
+	if (physicalLayerCount >= 1) {
+		fprintf(FP, "%*s(%d bottom_side.Cu signal)\n", baseSExprIndent + 2, "", layer);
+	}
+	if (physicalLayerCount > 1) { /* seems we need to ignore layers > 16 due to kicad limitation */
+		for (layer = 1; (layer < (kicadLayerCount - 1)) && (layer < 15); layer++ ) {
+			fprintf(FP, "%*s(%d Inner%d.Cu signal)\n", baseSExprIndent + 2, "", layer, layer);
+		}
+		fprintf(FP, "%*s(15 top_side.Cu signal)\n", baseSExprIndent + 2, "");	
+	}
+	fprintf(FP, "%*s(20 B.SilkS user)\n", baseSExprIndent + 2, "");
+	fprintf(FP, "%*s(21 F.SilkS user)\n", baseSExprIndent + 2, "");
+	fprintf(FP, "%*s(28 Edge.Cuts user)\n", baseSExprIndent + 2, "");
+	fprintf(FP, "%*s)\n", baseSExprIndent, "");
+
+	/* setup section */
+	fprintf(FP, "\n%*s(setup\n", baseSExprIndent, "");
+	write_kicad_layout_via_drill_size(FP, baseSExprIndent + 2);
+	fprintf(FP, "%*s)\n", baseSExprIndent, "");
+
+	/* now come the netlist "equipotential" descriptors */
+
+	write_kicad_equipotential_netlists(FP, PCB, baseSExprIndent);
+
+	/* module descriptions come next */
+
+	fputs("\n", FP);
+	write_kicad_layout_elements(FP, PCB, PCB->Data, LayoutXOffset, LayoutYOffset, baseSExprIndent);
+
+	/* we now need to map pcb's layer groups onto the kicad layer numbers */
+	currentKicadLayer = 0;
+	currentGroup = 0;
+
+	/* figure out which pcb layers are bottom copper and make a list */
+	bottomLayers = malloc(sizeof(int) * physicalLayerCount);
+	/*int bottomLayers[physicalLayerCount];*/
+	bottomCount = pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_COPPER, NULL, 0);
+	pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_COPPER, bottomLayers, physicalLayerCount);
+
+	/* figure out which pcb layers are internal copper layers and make a list */
+	innerLayers = malloc(sizeof(int) * physicalLayerCount);
+	/*int innerLayers[physicalLayerCount];*/
+	innerCount = pcb_layer_list(PCB_LYT_INTERN | PCB_LYT_COPPER, NULL, 0);
+	pcb_layer_list(PCB_LYT_INTERN | PCB_LYT_COPPER, innerLayers, physicalLayerCount);
+
+	/* figure out which pcb layers are top copper and make a list */
+	topLayers = malloc(sizeof(int) * physicalLayerCount);
+	/*int topLayers[physicalLayerCount];*/
+	topCount = pcb_layer_list(PCB_LYT_TOP | PCB_LYT_COPPER, NULL, 0);
+	pcb_layer_list(PCB_LYT_TOP | PCB_LYT_COPPER, topLayers, physicalLayerCount);
+
+	silkLayerCount = pcb_layer_group_list(PCB_LYT_SILK, NULL, 0);
+
+	/* figure out which pcb layers are bottom silk and make a list */
+	bottomSilk = malloc(sizeof(int) * silkLayerCount);
+	/*int bottomSilk[silkLayerCount];*/
+	bottomSilkCount = pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_SILK, NULL, 0);
+	pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_SILK, bottomSilk, silkLayerCount);
+
+	/* figure out which pcb layers are top silk and make a list */
+	topSilk = malloc(sizeof(int) * silkLayerCount);
+	/*int topSilk[silkLayerCount];*/
+	topSilkCount = pcb_layer_list(PCB_LYT_TOP | PCB_LYT_SILK, NULL, 0);
+	pcb_layer_list(PCB_LYT_TOP | PCB_LYT_SILK, topSilk, silkLayerCount);
+
+	/* here we count outline layers */
+	outlineLayerCount = pcb_layer_group_list(PCB_LYT_OUTLINE, NULL, 0);
+
+	/* figure out which pcb layers are outlines and make a list */
+	outlineLayers = malloc(sizeof(int) * outlineLayerCount);
+	outlineCount = pcb_layer_list(PCB_LYT_OUTLINE, NULL, 0);
+	pcb_layer_list(PCB_LYT_OUTLINE, outlineLayers, outlineCount);
+
+
+	/* we now proceed to write the bottom silk lines, arcs, text to the kicad legacy file, using layer 20 */
+	currentKicadLayer = 20; /* 20 is the bottom silk layer in kicad */
+	for (i = 0; i < bottomSilkCount; i++) /* write bottom silk lines, if any */
+		{
+			write_kicad_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the bottom copper text to the kicad legacy file, layer by layer */
+	currentKicadLayer = 0; /* 0 is the bottom copper layer in kicad */
+	for (i = 0; i < bottomCount; i++) /* write bottom copper tracks, if any */
+		{
+			write_kicad_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}	/* 0 is the bottom most track in kicad */
+
+	/* we now proceed to write the internal copper text to the kicad file, layer by layer */
+	if (innerCount != 0) {
+		currentGroup = pcb_layer_lookup_group(innerLayers[0]);
+	}
+	for (i = 0, currentKicadLayer = 1; i < innerCount; i++) /* write inner copper text, group by group */
+		{
+			if (currentGroup != pcb_layer_lookup_group(innerLayers[i])) {
+				currentGroup = pcb_layer_lookup_group(innerLayers[i]);
+				currentKicadLayer++;
+				if (currentKicadLayer > 14) {
+					currentKicadLayer = 14; /* kicad 16 layers in total, 0...15 */
+				}
+			}
+			write_kicad_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the top copper text to the kicad legacy file, layer by layer */
+	currentKicadLayer = 15; /* 15 is the top most copper layer in kicad */
+	for (i = 0; i < topCount; i++) /* write top copper tracks, if any */
+		{
+			write_kicad_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the top silk lines, arcs, text to the kicad legacy file, using layer 21 */
+	currentKicadLayer = 21; /* 21 is the top silk layer in kicad */
+	for (i = 0; i < topSilkCount; i++) /* write top silk lines, if any */
+		{
+			write_kicad_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* having done the graphical elements, we move onto tracks and vias */ 
+
+	fputs("\n",FP); /* move onto tracks and vias */
+	write_kicad_layout_vias(FP, PCB->Data, LayoutXOffset, LayoutYOffset, baseSExprIndent);
+
+	/* we now proceed to write the bottom copper tracks to the kicad legacy file, layer by layer */
+	currentKicadLayer = 0; /* 0 is the bottom copper layer in kicad */
+	for (i = 0; i < bottomCount; i++) /* write bottom copper tracks, if any */
+		{
+			write_kicad_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}	/* 0 is the bottom most track in kicad */
+
+	/* we now proceed to write the internal copper tracks to the kicad file, layer by layer */
+	if (innerCount != 0) {
+		currentGroup = pcb_layer_lookup_group(innerLayers[0]);
+	}
+	for (i = 0, currentKicadLayer = 1; i < innerCount; i++) /* write inner copper tracks, group by group */
+		{
+			if (currentGroup != pcb_layer_lookup_group(innerLayers[i])) {
+				currentGroup = pcb_layer_lookup_group(innerLayers[i]);
+				currentKicadLayer++;
+				if (currentKicadLayer > 14) {
+					currentKicadLayer = 14; /* kicad 16 layers in total, 0...15 */
+				}
+			}
+			write_kicad_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the top copper tracks to the kicad legacy file, layer by layer */
+	currentKicadLayer = 15; /* 15 is the top most copper layer in kicad */
+	for (i = 0; i < topCount; i++) /* write top copper tracks, if any */
+		
+		{
+			write_kicad_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			write_kicad_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the outline tracks to the kicad file, layer by layer */
+	currentKicadLayer = 28; /* 28 is the edge cuts layer in kicad */
+	if (outlineCount != 0) {
+		for (i = 0; i < outlineCount; i++) /* write outline tracks, if any */
+			{
+				write_kicad_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[outlineLayers[i]]),
+										LayoutXOffset, LayoutYOffset, baseSExprIndent);
+				write_kicad_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[outlineLayers[i]]),
+										LayoutXOffset, LayoutYOffset, baseSExprIndent);
+			}
+	} else { /* no outline layer per se, export the board margins instead  - obviously some scope to reduce redundant code...*/
+				fprintf(FP, "%*s", baseSExprIndent, "");
+				pcb_fprintf(FP, "(segment (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										kicad_sexpr_layer_to_text(currentKicadLayer), outlineThickness);
+				fprintf(FP, "%*s", baseSExprIndent, "");
+				pcb_fprintf(FP, "(segment (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										kicad_sexpr_layer_to_text(currentKicadLayer), outlineThickness);
+				fprintf(FP, "%*s", baseSExprIndent, "");
+				pcb_fprintf(FP, "(segment (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										kicad_sexpr_layer_to_text(currentKicadLayer), outlineThickness);
+				fprintf(FP, "%*s", baseSExprIndent, "");
+				pcb_fprintf(FP, "(segment (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										kicad_sexpr_layer_to_text(currentKicadLayer), outlineThickness);
+	}
+
+	fputs("\n",FP); /* finished  tracks and vias */
+
+	/*
+	  * now we proceed to write polygons for each layer, and iterate much like we did for tracks
+         */
+
+	/* we now proceed to write the bottom silk polygons  to the kicad legacy file, using layer 20 */
+	currentKicadLayer = 20; /* 20 is the bottom silk layer in kicad */
+	for (i = 0; i < bottomSilkCount; i++) /* write bottom silk polygons, if any */
+		{
+			write_kicad_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the bottom copper polygons to the kicad legacy file, layer by layer */
+	currentKicadLayer = 0; /* 0 is the bottom copper layer in kicad */
+	for (i = 0; i < bottomCount; i++) /* write bottom copper polygons, if any */
+		{
+			write_kicad_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}	/* 0 is the bottom most track in kicad */
+
+	/* we now proceed to write the internal copper polygons to the kicad file, layer by layer */
+	if (innerCount != 0) {
+		currentGroup = pcb_layer_lookup_group(innerLayers[0]);
+	}
+	for (i = 0, currentKicadLayer = 1; i < innerCount; i++) /* write inner copper polygons, group by group */
+		{
+			if (currentGroup != pcb_layer_lookup_group(innerLayers[i])) {
+				currentGroup = pcb_layer_lookup_group(innerLayers[i]);
+				currentKicadLayer++;
+				if (currentKicadLayer > 14) {
+					currentKicadLayer = 14; /* kicad 16 layers in total, 0...15 */
+				}
+			}
+			write_kicad_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the top copper polygons to the kicad legacy file, layer by layer */
+	currentKicadLayer = 15; /* 15 is the top most copper layer in kicad */
+	for (i = 0; i < topCount; i++) /* write top copper polygons, if any */
+		{
+			write_kicad_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	/* we now proceed to write the top silk polygons to the kicad legacy file, using layer 21 */
+	currentKicadLayer = 21; /* 21 is the top silk layer in kicad */
+	for (i = 0; i < topSilkCount; i++) /* write top silk polygons, if any */
+		{
+			write_kicad_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset, baseSExprIndent);
+		}
+
+	fputs(")\n",FP); /* finish off the board */
+
+	/* now free memory from arrays that were used */
+	free(bottomLayers);
+	free(innerLayers);
+	free(topLayers);
+	free(topSilk);
+	free(bottomSilk);
+	free(outlineLayers);
+	return (STATUS_OK);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes (eventually) de-duplicated list of element names in kicad legacy format module $INDEX
+ */
+static int io_kicad_write_element_index(FILE * FP, DataTypePtr Data)
+{
+	gdl_iterator_t eit;
+	ElementType *element;
+	unm_t group1; /* group used to deal with missing names and provide unique ones if needed */
+
+	elementlist_dedup_initializer(ededup);
+
+	/* Now initialize the group with defaults */
+	unm_init(&group1);
+
+	elementlist_foreach(&Data->Element, &eit, element) {
+		elementlist_dedup_skip(ededup, element);
+		/* skip duplicate elements */
+		/* only non empty elements */
+
+		if (!linelist_length(&element->Line)
+				&& !pinlist_length(&element->Pin)
+				&& !arclist_length(&element->Arc)
+				&& !padlist_length(&element->Pad))
+			continue;
+
+		fprintf(FP, "%s\n", unm_name(&group1, element->Name[0].TextString, element));
+
+	}
+	/* Release unique name utility memory */
+	unm_uninit(&group1);
+	/* free the state used for deduplication */
+	elementlist_dedup_free(ededup);
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes kicad format via data
+ For a track segment:
+ Position shape Xstart Ystart Xend Yend width
+ Description layer 0 netcode timestamp status
+ Shape parameter is set to 0 (reserved for futu
+*/
+
+
+int write_kicad_layout_vias(FILE * FP, DataTypePtr Data, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation)
+{
+	gdl_iterator_t it;
+	PinType *via;
+	/* write information about vias */
+	pinlist_foreach(&Data->Via, &it, via) {
+		fprintf(FP, "%*s", indentation,"");
+		pcb_fprintf(FP, "(via (at %.3mm %.3mm) (size %.3mm) (layers %s %s))\n", 
+								via->X + xOffset, via->Y + yOffset, via->Thickness,
+								kicad_sexpr_layer_to_text(0), kicad_sexpr_layer_to_text(15)); /* skip (net 0) for now */
+	}
+	return 0;
+}
+
+static char * kicad_sexpr_layer_to_text(int layer)
+{
+	switch (layer) {
+		case 0:
+			return "bottom_side.Cu";
+		case 1:
+			return "Inner1.Cu";
+		case 2:
+			return "Inner2.Cu";
+		case 3:
+			return "Inner3.Cu";
+		case 4:
+			return "Inner4.Cu";
+		case 5:
+			return "Inner5.Cu";
+		case 6:
+			return "Inner6.Cu";
+		case 7:
+			return "Inner7.Cu";
+		case 8:
+			return "Inner8.Cu";
+		case 9:
+			return "Inner9.Cu";
+		case 10:
+			return "Inner10.Cu";
+		case 11:
+			return "Inner11.Cu";
+		case 12:
+			return "Inner12.Cu";
+		case 13:
+			return "Inner13.Cu";
+		case 14:
+			return "Inner14.Cu";
+		case 15:
+			return "top_side.Cu";
+		case 20:
+			return "B.SilkS";
+		case 21:
+			return "F.SilkS";
+		case 28:
+			return "Edge.Cuts"; /* kicad's outline layer */
+		default:
+			return "";
+	}
+}
+
+static int write_kicad_layout_via_drill_size(FILE * FP, pcb_cardinal_t indentation)
+{
+	fprintf(FP, "%*s", indentation,"");
+	pcb_fprintf(FP, "(via_drill 0.635)\n"); /* mm format, default for now, ~= 0.635mm */
+	return 0;
+}
+
+int write_kicad_layout_tracks(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation)
+{
+	gdl_iterator_t it;
+	LineType *line;
+	pcb_cardinal_t currentLayer = number;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		/*
+			fprintf(FP, "Layer(%i ", (int) Number + 1);
+			PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+			fputs(")\n(\n", FP);
+			WriteAttributeList(FP, &layer->Attributes, "\t");
+		*/
+		int localFlag = 0;
+		linelist_foreach(&layer->Line, &it, line) {
+			if ((currentLayer < 16) || (currentLayer == 28)) { /* a copper line i.e. track, or an outline edge cut */
+				fprintf(FP, "%*s", indentation, "");
+				pcb_fprintf(FP, "(segment (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										line->Point1.X + xOffset, line->Point1.Y + yOffset,
+										line->Point2.X + xOffset, line->Point2.Y + yOffset,
+										kicad_sexpr_layer_to_text(currentLayer), line->Thickness); /* neglect (net ___ ) for now */
+			} else if ((currentLayer == 20) || (currentLayer == 21)  || (currentLayer == 28)) { /* a silk line or outline line */
+				fprintf(FP, "%*s", indentation, "");
+				pcb_fprintf(FP, "(gr_line (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										line->Point1.X + xOffset, line->Point1.Y + yOffset,
+										line->Point2.X + xOffset, line->Point2.Y + yOffset,
+										kicad_sexpr_layer_to_text(currentLayer), line->Thickness);
+			}
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
+
+int write_kicad_layout_arcs(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation)
+{
+	gdl_iterator_t it;
+	ArcType *arc;
+	ArcType localArc; /* for converting ellipses to circular arcs */
+	BoxType *boxResult; /* for figuring out arc ends */
+	pcb_cardinal_t currentLayer = number;
+	Coord radius, xStart, yStart, xEnd, yEnd;
+	int copperStartX; /* used for mapping geda copper arcs onto kicad copper lines */
+	int copperStartY; /* used for mapping geda copper arcs onto kicad copper lines */
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		/*
+			fprintf(FP, "Layer(%i ", (int) Number + 1);
+			PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+			fputs(")\n(\n", FP);
+			WriteAttributeList(FP, &layer->Attributes, "\t");
+		*/
+		int localFlag = 0;
+		int kicadArcShape = 2; /* 3 = circle, and 2 = arc, 1= rectangle used in eeschema only */ 
+		arclist_foreach(&layer->Arc, &it, arc) {
+			localArc = *arc;
+			if (arc->Width > arc->Height) {
+				radius = arc->Height;
+				localArc.Width = radius;
+			} else {
+				radius = arc->Width;
+				localArc.Height = radius;
+			}
+		boxResult = GetArcEnds(&localArc);
+			if (arc->Delta == 360.0 || arc->Delta == -360.0 ) { /* it's a circle */
+				kicadArcShape = 3;
+			} else { /* it's an arc */
+				kicadArcShape = 2;
+			}
+			xStart = localArc.X + xOffset;
+			yStart = localArc.Y + yOffset;
+			xEnd = boxResult->X2 + xOffset; 
+			yEnd = boxResult->Y2 + yOffset; 
+			copperStartX = boxResult->X1 + xOffset;
+			copperStartY = boxResult->Y1 + yOffset; 
+			if ((currentLayer < 16) || (currentLayer == 28)) { /* a copper arc, i.e. track, or edge cut, is unsupported by kicad, and will be exported as a line */
+				fprintf(FP, "%*s", indentation, "");
+				pcb_fprintf(FP, "(segment (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										copperStartX, copperStartY, xEnd, yEnd,
+										kicad_sexpr_layer_to_text(currentLayer), arc->Thickness); /* neglect (net ___ ) for now */
+			} else if ((currentLayer == 20) || (currentLayer == 21)) { /* a silk arc, or outline */
+				fprintf(FP, "%*s", indentation, "");
+				pcb_fprintf(FP, "(gr_arc (start %.3mm %.3mm) (end %.3mm %.3mm) (angle %ma) (layer %s) (width %.3mm))\n",
+										xStart, yStart, xEnd, yEnd, arc->Delta,
+										kicad_sexpr_layer_to_text(currentLayer), arc->Thickness);
+			}
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
+
+int write_kicad_layout_text(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation)
+{
+	FontType *myfont = &PCB->Font;
+	Coord mWidth = myfont->MaxWidth; /* kicad needs the width of the widest letter */
+	Coord defaultStrokeThickness = 100*2540; /* use 100 mil as default 100% stroked font line thickness */
+	int kicadMirrored = 1; /* 1 is not mirrored, 0  is mirrored */ 
+
+	Coord defaultXSize;
+	Coord defaultYSize;
+	Coord strokeThickness;
+	int rotation;	
+	Coord textOffsetX;
+	Coord textOffsetY;
+	Coord halfStringWidth;
+	Coord halfStringHeight;
+	int localFlag;
+
+	gdl_iterator_t it;
+	TextType *text;
+	pcb_cardinal_t currentLayer = number;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		/*
+			fprintf(FP, "Layer(%i ", (int) Number + 1);
+			PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+			fputs(")\n(\n", FP);
+			WriteAttributeList(FP, &layer->Attributes, "\t");
+		*/
+		localFlag = 0;
+		textlist_foreach(&layer->Text, &it, text) {
+			if ((currentLayer < 16) || (currentLayer == 20) || (currentLayer == 21) ) { /* copper or silk layer text */
+				fprintf(FP, "%*s(gr_text \"%s\" ", indentation, "", text->TextString);
+				defaultXSize = 5*PCB_SCALE_TEXT(mWidth, text->Scale)/6; /* IIRC kicad treats this as kerned width of upper case m */
+				defaultYSize = defaultXSize;
+				strokeThickness = PCB_SCALE_TEXT(defaultStrokeThickness, text->Scale /2);
+				rotation = 0;	
+				textOffsetX = 0;
+				textOffsetY = 0;
+				halfStringWidth = (text->BoundingBox.X2 - text->BoundingBox.X1)/2;
+				if (halfStringWidth < 0) {
+					halfStringWidth = -halfStringWidth;
+				}
+				halfStringHeight = (text->BoundingBox.Y2 - text->BoundingBox.Y1)/2;
+				if (halfStringHeight < 0) {
+					halfStringHeight = -halfStringHeight;
+				}
+				if (text->Direction == 3) { /*vertical down*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */ 
+						rotation = 2700;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY -= halfStringHeight;
+						textOffsetX -= 2*halfStringWidth; /* was 1*hsw */
+					} else {    /* front copper or silk */
+						rotation = 2700;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY = halfStringHeight;
+						textOffsetX -= halfStringWidth;
+					}
+				} else if (text->Direction == 2)  { /*upside down*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */ 
+						rotation = 0;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY += halfStringHeight;
+					} else {    /* front copper or silk */
+						rotation = 1800;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY -= halfStringHeight;
+					}
+					textOffsetX = -halfStringWidth;
+				} else if (text->Direction == 1) { /*vertical up*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */ 
+						rotation = 900;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY = halfStringHeight;
+						textOffsetX += halfStringWidth; 
+					} else {    /* front copper or silk */
+						rotation = 900;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY = -halfStringHeight;
+						textOffsetX = 0; /* += halfStringWidth; */
+					}
+				} else if (text->Direction == 0)  { /*normal text*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */
+						rotation = 1800;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY -= halfStringHeight;
+					} else {    /* front copper or silk */
+						rotation = 0;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY += halfStringHeight;
+					}
+					textOffsetX = halfStringWidth;
+				}
+/*				printf("\"%s\" direction field: %d\n", text->TextString, text->Direction);
+				printf("textOffsetX: %d,  textOffsetY: %d\n", textOffsetX, textOffsetY);     TODO need to sort out rotation */
+				pcb_fprintf(FP, "(at %.3mm %.3mm", text->X + xOffset + textOffsetX, text->Y + yOffset + textOffsetY);
+				if (rotation != 0) {
+					fprintf(FP, " %d", rotation/10); /* convert decidegrees to degrees */
+				}
+				pcb_fprintf(FP, ") (layer %s)\n", kicad_sexpr_layer_to_text(currentLayer));
+				fprintf(FP, "%*s", indentation +2,"");
+				pcb_fprintf(FP, "(effects (font (size %.3mm %.3mm) (thickness %.3mm))", defaultXSize, defaultYSize, strokeThickness); /* , rotation */
+				if (kicadMirrored  == 0) {
+					fprintf(FP, " (justify mirror)");
+				}
+				fprintf(FP, ")\n%*s)\n", indentation,"");
+			}
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes element data in kicad legacy format for use in a .mod library
+ */
+int io_kicad_write_element(plug_io_t *ctx, FILE * FP, DataTypePtr Data)
+{
+
+
+	/*
+	write_kicad_legacy_module_header(FP);
+	fputs("io_kicad_legacy_write_element()", FP);
+	return 0;
+	*/
+
+
+	gdl_iterator_t eit;
+	LineType *line;
+	ArcType *arc;
+	ElementType *element;
+	BoxType *boxResult;
+
+	Coord arcStartX, arcStartY, arcEndX, arcEndY; /* for arc exporting */
+
+	unm_t group1; /* group used to deal with missing names and provide unique ones if needed */
+	const char * currentElementName;
+
+	elementlist_dedup_initializer(ededup);
+	/* Now initialize the group with defaults */
+	unm_init(&group1);
+
+	elementlist_foreach(&Data->Element, &eit, element) {
+		gdl_iterator_t it;
+		PinType *pin;
+		PadType *pad;
+
+		elementlist_dedup_skip(ededup, element); /* skip duplicate elements */
+
+		/* TOOD: Footprint name element->Name[0].TextString */
+
+		/* only non empty elements */
+		if (!linelist_length(&element->Line) && !pinlist_length(&element->Pin) && !arclist_length(&element->Arc) && !padlist_length(&element->Pad))
+			continue;
+		/* the coordinates and text-flags are the same for
+		 * both names of an element
+		 */
+		/* the following element summary is not used
+			 in kicad; the module's header contains this
+			 information
+
+			 fprintf(FP, "\nDS %s ", F2S(element, PCB_TYPE_ELEMENT));
+			 PrintQuotedString(FP, (char *) EMPTY(DESCRIPTION_NAME(element)));
+			 fputc(' ', FP);
+			 PrintQuotedString(FP, (char *) EMPTY(NAMEONPCB_NAME(element)));
+			 fputc(' ', FP);
+			 PrintQuotedString(FP, (char *) EMPTY(VALUE_NAME(element)));
+			 pcb_fprintf(FP, " %mm %mm %mm %mm %d %d %s]\n(\n",
+			 element->MarkX, element->MarkY,
+			 DESCRIPTION_TEXT(element).X - element->MarkX,
+			 DESCRIPTION_TEXT(element).Y - element->MarkY,
+			 DESCRIPTION_TEXT(element).Direction,
+			 DESCRIPTION_TEXT(element).Scale, F2S(&(DESCRIPTION_TEXT(element)), PCB_TYPE_ELEMENT_NAME));
+
+		*/
+
+		/*		//WriteAttributeList(FP, &element->Attributes, "\t");
+		 */
+
+		currentElementName = unm_name(&group1, element->Name[0].TextString, element);
+		fprintf(FP, "$MODULE %s\n", currentElementName);
+		fputs("Po 0 0 0 15 51534DFF 00000000 ~~\n",FP);
+		fprintf(FP, "Li %s\n", currentElementName);
+		fprintf(FP, "Cd %s\n", currentElementName);
+		fputs("Sc 0\n",FP);
+		fputs("AR\n",FP);
+		fputs("Op 0 0 0\n",FP);
+		fputs("T0 0 -6.000 1.524 1.524 0 0.305 N V 21 N \"S***\"\n",FP); /*1.524 is basically 600 decimil, 0.305 is ~= 120 decimil */
+
+		linelist_foreach(&element->Line, &it, line) {
+			pcb_fprintf(FP, "DS %.3mm %.3mm %.3mm %.3mm %.3mm ",
+									line->Point1.X - element->MarkX,
+									line->Point1.Y - element->MarkY,
+									line->Point2.X - element->MarkX,
+									line->Point2.Y - element->MarkY,
+									line->Thickness);
+			fputs("21\n",FP); /* an arbitrary Kicad layer, front silk, need to refine this */
+		}
+
+		arclist_foreach(&element->Arc, &it, arc) {
+			boxResult = GetArcEnds(arc);
+			arcStartX = boxResult->X1;
+			arcStartY = boxResult->Y1;
+			arcEndX = boxResult->X2; 
+			arcEndY = boxResult->Y2; 
+			if ((arc->Delta == 360.0) || (arc->Delta == -360.0)) { /* it's a circle */
+				pcb_fprintf(FP, "DC %.3mm %.3mm %.3mm %.3mm %.3mm ",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcStartX - element->MarkX, /* x on circle */
+										arcStartY - element->MarkY, /* y on circle */
+										arc->Thickness); /* stroke thickness */
+			} else {
+				/*
+				   as far as can be determined from the Kicad documentation,
+				   http://en.wikibooks.org/wiki/Kicad/file_formats#Drawings
+
+				   the origin for rotation is the positive x direction, and going CW
+
+				   whereas in gEDA, the gEDA origin for rotation is the negative x axis,
+				   with rotation CCW, so we need to reverse delta angle
+
+				   deltaAngle is CW in Kicad in deci-degrees, and CCW in degrees in gEDA
+				   NB it is in degrees in the newer s-file kicad module/footprint format
+				*/
+				pcb_fprintf(FP, "DA %.3mm %.3mm %.3mm %.3mm %mA %.3mm ",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcEndX - element->MarkX, /* x on arc */
+										arcEndY - element->MarkY, /* y on arc */
+										arc->Delta, /* CW delta angle in decidegrees */
+										arc->Thickness); /* stroke thickness */
+			}
+			fputs("21\n",FP); /* and now append a suitable Kicad layer, front silk = 21 */
+		}
+
+		pinlist_foreach(&element->Pin, &it, pin) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for a pin */
+
+			pcb_fprintf(FP, "Po %.3mm %.3mm\n", /* positions of pad */
+									pin->X - element->MarkX,
+									pin->Y - element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pin->Number));
+
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+				fputs(" R ",FP); /* square */
+			} else {
+				fputs(" C ",FP); /* circular */
+			}
+
+			pcb_fprintf(FP, "%.3mm %.3mm ", pin->Thickness, pin->Thickness); /* height = width */
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr ",FP); /* drill details; size and x,y pos relative to pad location */
+			pcb_fprintf(FP, "%mm 0 0\n", pin->DrillingHole);
+
+			fputs("At STD N 00E0FFFF\n", FP); /* through hole STD pin, all copper layers */
+
+			fputs("Ne 0 \"\"\n",FP); /* library parts have empty net descriptors */
+			/*
+				PrintQuotedString(FP, (char *) EMPTY(pin->Name));
+				fprintf(FP, " %s\n", F2S(pin, PCB_TYPE_PIN));
+			*/
+			fputs("$EndPAD\n",FP);
+		}
+		padlist_foreach(&element->Pad, &it, pad) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for an smd pad */
+
+			pcb_fprintf(FP, "Po %.3mm %.3mm\n", /* positions of pad */
+									(pad->Point1.X + pad->Point2.X)/2- element->MarkX,
+									(pad->Point1.Y + pad->Point2.Y)/2- element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pad->Number));
+			fputs(" R ",FP); /* rectangular, not a pin */
+
+			if ((pad->Point1.X-pad->Point2.X) <= 0
+					&& (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) <= 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			}
+
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr 0 0 0\n",FP); /* drill details; zero size; x,y pos vs pad location */
+
+			fputs("At SMD N 00888000\n", FP); /* SMD pin, need to use right layer mask */
+
+			fputs("Ne 0 \"\"\n",FP); /* library parts have empty net descriptors */
+			fputs("$EndPAD\n",FP);
+		}
+		fprintf(FP, "$EndMODULE %s\n", currentElementName);		
+	}
+	/* Release unique name utility memory */
+	unm_uninit(&group1);
+	/* free the state used for deduplication */
+	elementlist_dedup_free(ededup);
+
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes netlist data in kicad legacy format for use in a layout .brd file
+ */
+
+int write_kicad_equipotential_netlists(FILE * FP, PCBTypePtr Layout, pcb_cardinal_t indentation)
+{
+        int n; /* code mostly lifted from netlist.c */ 
+	int netNumber;
+	LibraryMenuTypePtr menu;
+	LibraryEntryTypePtr netlist;
+	
+	/* first we write a default netlist for the 0 net, which is for unconnected pads in pcbnew */
+	fprintf(FP, "\n%*s(net 0 \"\")\n", indentation, "");
+
+	/* now we step through any available netlists and generate descriptors */
+        for (n = 0, netNumber = 1; n < Layout->NetlistLib[NETLIST_EDITED].MenuN; n++, netNumber ++) {
+                menu = &Layout->NetlistLib[NETLIST_EDITED].Menu[n];
+		netlist = &menu->Entry[0];
+		if (netlist != NULL) {
+			fprintf(FP, "%*s(net %d %s)\n", indentation, "", netNumber, pcb_netlist_name(menu));  /* netlist 0 was used for unconnected pads  */
+                }
+        }
+	return 0;
+}
+
+/* may need to export a netclass or two 
+(net_class Default "Ceci est la Netclass par défaut"
+(clearance 0.254)
+(trace_width 0.254)
+(via_dia 0.889)
+(via_drill 0.635)
+(uvia_dia 0.508)
+(uvia_drill 0.127)
+(add_net "")
+*/
+
+
+/* ---------------------------------------------------------------------------
+ * writes element data in kicad legacy format for use in a layout .brd file
+ */
+int write_kicad_layout_elements(FILE * FP, PCBTypePtr Layout, DataTypePtr Data, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation)
+{
+
+	gdl_iterator_t eit;
+	LineType *line;
+	ArcType *arc;
+	Coord arcStartX, arcStartY, arcEndX, arcEndY; /* for arc rendering */
+	Coord xPos, yPos;
+
+	ElementType *element;
+	unm_t group1; /* group used to deal with missing names and provide unique ones if needed */
+	const char * currentElementName;
+	const char * currentElementRef;
+	const char * currentElementVal;
+
+	LibraryMenuTypePtr current_pin_menu;
+	LibraryMenuTypePtr current_pad_menu;
+
+	int silkLayer = 21;  /* hard coded default, 20 is bottom silk */ 
+	int copperLayer = 15; /* hard coded default, 0 is bottom copper */
+
+	elementlist_dedup_initializer(ededup);
+	/* Now initialize the group with defaults */
+	unm_init(&group1);
+
+	elementlist_foreach(&Data->Element, &eit, element) {
+		gdl_iterator_t it;
+		PinType *pin;
+		PadType *pad;
+
+		/* elementlist_dedup_skip(ededup, element);  */
+		/* let's not skip duplicate elements for layout export*/
+
+		/* TOOD: Footprint name element->Name[0].TextString */
+
+		/* only non empty elements */
+		if (!linelist_length(&element->Line) && !pinlist_length(&element->Pin) && !arclist_length(&element->Arc) && !padlist_length(&element->Pad))
+			continue;
+		/* the coordinates and text-flags are the same for
+		 * both names of an element
+		 */
+
+		xPos = element->MarkX + xOffset;
+		yPos = element->MarkY + yOffset;
+		if (TEST_FLAG(PCB_FLAG_ONSOLDER, element)) {
+			silkLayer = 20;
+			copperLayer = 0;
+		} else {
+			silkLayer = 21;
+			copperLayer = 15;
+		}
+
+		currentElementName = unm_name(&group1, element->Name[0].TextString, element);
+		if (currentElementName == NULL) {
+			currentElementName = "unknown";
+		}
+		currentElementRef = element->Name[NAMEONPCB_INDEX].TextString;
+		if (currentElementRef == NULL) {
+			currentElementRef = "unknown";
+		}
+		currentElementVal = element->Name[VALUE_INDEX].TextString;
+		if (currentElementVal == NULL) {
+			currentElementVal = "unknown";
+		}
+
+		fprintf(FP, "%*s", indentation, "");
+		fprintf(FP,  "(module %s (layer %s) (tedit 4E4C0E65) (tstamp 5127A136)\n",
+								currentElementName, kicad_sexpr_layer_to_text(copperLayer));
+		fprintf(FP, "%*s", indentation + 2, "");
+		pcb_fprintf(FP, "(at %.3mm %.3mm)\n", xPos, yPos);
+
+		fprintf(FP, "%*s", indentation + 2, "");
+		pcb_fprintf(FP, "(fp_text reference %s (at 0 0.127) (layer %s)\n",
+								currentElementRef, kicad_sexpr_layer_to_text(silkLayer));
+		fprintf(FP, "%*s", indentation + 4, "");
+		fprintf(FP, "(effects (font (size 1.397 1.27) (thickness 0.2032)))\n");
+		fprintf(FP, "%*s)\n", indentation + 2, "");
+
+		fprintf(FP, "%*s", indentation + 2, "");
+		pcb_fprintf(FP, "(fp_text value %s (at 0 0.127) (layer %s)\n",
+								currentElementVal, kicad_sexpr_layer_to_text(silkLayer));
+		fprintf(FP, "%*s", indentation + 4, "");
+		fprintf(FP, "(effects (font (size 1.397 1.27) (thickness 0.2032)))\n");
+		fprintf(FP, "%*s)\n", indentation + 2, "");
+
+		linelist_foreach(&element->Line, &it, line) {
+			fprintf(FP, "%*s", indentation + 2, "");
+			pcb_fprintf(FP, "(fp_line (start %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+									line->Point1.X - element->MarkX,
+									line->Point1.Y - element->MarkY,
+									line->Point2.X - element->MarkX,
+									line->Point2.Y - element->MarkY,
+									kicad_sexpr_layer_to_text(silkLayer),
+									line->Thickness);
+		}
+
+		arclist_foreach(&element->Arc, &it, arc) {
+
+			BoxType *boxResult = GetArcEnds(arc);
+			arcStartX = boxResult->X1;
+			arcStartY = boxResult->Y1;
+			arcEndX = boxResult->X2; 
+			arcEndY = boxResult->Y2; 
+
+			if ((arc->Delta == 360.0) || (arc->Delta == -360.0)) { /* it's a circle */
+				fprintf(FP, "%*s", indentation +2, "");
+				pcb_fprintf(FP, "(fp_circle (center %.3mm %.3mm) (end %.3mm %.3mm) (layer %s) (width %.3mm))\n",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcStartX - element->MarkX, /* x on circle */
+										arcStartY - element->MarkY, /* y on circle */
+										kicad_sexpr_layer_to_text(silkLayer), arc->Thickness);  /* stroke thickness */
+			} else {
+				fprintf(FP, "%*s", indentation +2, "");
+				pcb_fprintf(FP, "(fp_arc (start %.3mm %.3mm) (end %.3mm %.3mm) (angle %ma) (layer %s) (width %.3mm))\n",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcEndX - element->MarkX, /* x on arc */
+										arcEndY - element->MarkY, /* y on arc */
+										arc->Delta, /* CW delta angle in decidegrees */
+										kicad_sexpr_layer_to_text(silkLayer), arc->Thickness);  /* stroke thickness */
+			}
+		}
+
+
+		pinlist_foreach(&element->Pin, &it, pin) {
+			fprintf(FP, "%*s", indentation + 2, "");
+			fputs("(pad ", FP);
+			PrintQuotedString(FP, (char *) EMPTY(pin->Number));
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+				fputs(" thru_hole rect ",FP); /* square */
+			} else {
+				fputs(" thru_hole circle ",FP); /* circular */
+			}
+			pcb_fprintf(FP, "(at %.3mm %.3mm)", /* positions of pad */
+									pin->X - element->MarkX,
+									pin->Y - element->MarkY);
+			/* PrintQuotedString(FP, (char *) EMPTY(pin->Number)); */
+			pcb_fprintf(FP, " (size %.3mm %.3mm)", pin->Thickness, pin->Thickness); /* height = width */
+			/* drill details; size */
+			pcb_fprintf(FP, " (drill %.3mm)\n", pin->DrillingHole);
+			fprintf(FP, "%*s", indentation + 4, "");
+			fprintf(FP, "(layers *.Cu *.Mask)\n"); /* define included layers for pin */
+			current_pin_menu = pcb_netlist_find_net4pin(Layout, pin);
+			fprintf(FP, "%*s", indentation + 4, "");
+			if ((current_pin_menu != NULL) && (pcb_netlist_net_idx(Layout, current_pin_menu) != PCB_NETLIST_INVALID_INDEX)) {
+				fprintf(FP, "(net %d \"%s\")\n", (1 + pcb_netlist_net_idx(Layout, current_pin_menu)), pcb_netlist_name(current_pin_menu)); /* library parts have empty net descriptors, in a .brd they don't */
+			} else {
+				fprintf(FP, "(net 0 \"\")\n"); /* unconnected pads have zero for net */
+			}
+			fprintf(FP, "%*s)\n", indentation + 2, "");
+			/*
+				PrintQuotedString(FP, (char *) EMPTY(pin->Name));
+				fprintf(FP, " %s\n", F2S(pin, PCB_TYPE_PIN));
+			*/
+		}
+		padlist_foreach(&element->Pad, &it, pad) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for an smd pad */
+
+			pcb_fprintf(FP, "Po %.3mm %.3mm\n", /* positions of pad */
+									(pad->Point1.X + pad->Point2.X)/2- element->MarkX,
+									(pad->Point1.Y + pad->Point2.Y)/2- element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pad->Number));
+			fputs(" R ",FP); /* rectangular, not a pin */
+
+			if ((pad->Point1.X-pad->Point2.X) <= 0
+					&& (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) <= 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			}
+
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr 0 0 0\n",FP); /* drill details; zero size; x,y pos vs pad location */
+
+			fputs("At SMD N 00888000\n", FP); /* SMD pin, need to use right layer mask */
+
+			current_pad_menu = pcb_netlist_find_net4pad(Layout, pad);
+			if ((current_pad_menu != NULL) && (pcb_netlist_net_idx(Layout, current_pad_menu) != PCB_NETLIST_INVALID_INDEX)) {
+				fprintf(FP, "Ne %d \"%s\"\n", (1 + pcb_netlist_net_idx(Layout, current_pad_menu)), pcb_netlist_name(current_pad_menu)); /* library parts have empty net descriptors, in a .brd they don't */
+			} else {
+				fprintf(FP, "Ne 0 \"\"\n"); /* a net number of 0 indicates an unconnected pad in pcbnew */
+			} 
+
+			fprintf(FP, "%*s)\n", indentation + 2, "");
+
+		}
+
+		fprintf(FP, "%*s)\n\n", indentation, ""); /*  finish off module */
+	}
+	/* Release unique name utility memory */
+	unm_uninit(&group1);
+	/* free the state used for deduplication */
+	elementlist_dedup_free(ededup);
+
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes polygon data in kicad legacy format for use in a layout .brd file
+ */
+
+int write_kicad_layout_polygons(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation)
+{
+	int i, j;
+	gdl_iterator_t it;
+	PolygonType *polygon;
+	pcb_cardinal_t currentLayer = number;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		int localFlag = 0;
+		polylist_foreach(&layer->Polygon, &it, polygon) {
+			if (polygon->HoleIndexN == 0) { /* no holes defined within polygon, which we implement support for first */
+
+				/* preliminaries for zone settings */
+
+				fprintf(FP, "%*s(zone (net 0) (net_name \"\") (layer %s) (tstamp 478E3FC8) (hatch edge 0.508)\n", indentation, "", kicad_sexpr_layer_to_text(currentLayer));
+				fprintf(FP, "%*s(connect_pads no (clearance 0.508))\n", indentation+2, "");
+				fprintf(FP, "%*s(min_thickness 0.4826)\n", indentation+2, "");
+				fprintf(FP, "%*s(fill (arc_segments 32) (thermal_gap 0.508) (thermal_bridge_width 0.508))\n", indentation+2, "");
+				fprintf(FP, "%*s(polygon\n", indentation+2, "");
+				fprintf(FP, "%*s(pts\n", indentation+4, "");
+
+				/* now the zone outline is defined */
+
+				for (i = 0; i < polygon->PointN; i = i + 5) { /* kicad exports five coords per line in s-expr files */
+					fprintf(FP, "%*s", indentation + 6, ""); /* pcb_fprintf does not support %*s   */
+					for (j = 0; (j < polygon->PointN) && (j < 5); j++) { 
+						pcb_fprintf(FP, "(xy %.3mm %.3mm)", polygon->Points[i + j].X + xOffset, polygon->Points[i+ j].Y + yOffset);
+						if ((j < 4) && ((i + j) < (polygon->PointN - 1))) {
+							fputs(" ", FP);
+						}
+					}
+					fputs("\n", FP);
+				}
+				fprintf(FP, "%*s)\n", indentation+4, "");
+				fprintf(FP, "%*s)\n", indentation+2, "");
+				fprintf(FP, "%*s)\n", indentation, ""); /* end zone */
+				/*
+				  *   in here could go additional polygon descriptors for holes removed from  the previously defined outer polygon
+				  */ 
+
+			} 
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
diff --git a/src_plugins/io_kicad/write.h b/src_plugins/io_kicad/write.h
new file mode 100644
index 0000000..49fb025
--- /dev/null
+++ b/src_plugins/io_kicad/write.h
@@ -0,0 +1,48 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *  Copyright (C) 2016 Erich S. Heinzle
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include "global.h"
+#include "data.h"
+
+int io_kicad_write_element(plug_io_t *ctx, FILE * FP, DataTypePtr Data);
+int io_kicad_write_buffer(plug_io_t *ctx, FILE * FP, BufferType *buff);
+int io_kicad_write_pcb(plug_io_t *ctx, FILE * FP, const char *old_filename, const char *new_filename, pcb_bool emergency);
+int write_kicad_module_header(FILE * FP, pcb_cardinal_t indentation);
+int write_kicad_layout_header(FILE * FP, pcb_cardinal_t indentation);
+int write_kicad_layout_vias(FILE * FP, DataTypePtr Data, Coord MaxWidth, 										Coord MaxHeight, pcb_cardinal_t indentation);
+int write_kicad_layout_tracks(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,
+						Coord MaxWidth, Coord MaxHeight, pcb_cardinal_t indentation);
+int write_kicad_layout_arcs(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,
+						Coord xOffset, Coord yOffset, pcb_cardinal_t indentation);
+int write_kicad_layout_text(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,
+						Coord xOffset, Coord yOffset, pcb_cardinal_t indentation);
+int write_kicad_equipotential_netlists(FILE * FP, PCBTypePtr Layout, pcb_cardinal_t indentation);
+int write_kicad_layout_elements(FILE * FP, PCBTypePtr Layout, DataTypePtr Data, Coord xOffset, Coord yOffset, pcb_cardinal_t indentation);
+int write_kicad_layout_polygons(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,								Coord xOffset, Coord yOffset, pcb_cardinal_t indentation);
+
diff --git a/src_plugins/io_kicad_legacy/HACKING b/src_plugins/io_kicad_legacy/HACKING
new file mode 100644
index 0000000..c80f2cf
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/HACKING
@@ -0,0 +1,36 @@
+The plugin is disabled by default for now. This lets you work on it without
+having to worry whether you break other people's compilation. You need to
+enable it in your compilation, tho, when running configure:
+
+./configure --buildin-io_kicad_legacy
+
+Then run make (maybe only in src/)
+
+After making modifications to the plugin code, it's enough to run make in
+the plugin dir - as the module is a buildin, it will recompile pcb-rnd.
+
+Run pcb-rnd from the src/ directory (cd there, it won't find its
+files otherwise).
+
+How to run the plugin (GUI version):
+1. run pcb-rnd (from src/)
+2. load a pcb 
+3. open the command prompt (press ':')
+4. type: SaveTo(PasteBuffer, foobar.mod, kicadl)
+
+Now, the gtk dialog "Buffer: save buffer elements to file" allows kicad legacy format to be selected.
+
+Arc and Circle support has been implemented but not fully tested.
+
+This will save the current PasteBuffer contents to foobar.mod using io_kicad_legacy format.
+
+Once layout export has been implemented, the following will be possible:
+ 
+How to run the plugin (CLI version):
+
+echo "SaveTo(LayoutAs, foobar.brd, kicadl)" | ./pcb-rnd  --gui batch my.pcb
+
+(This loads my.pcb and saves it to foobar.brd and then exits)
+
+SVN: when commiting, please prefix all your commit message lines with string
+[io_kicad_legacy] to make my job easier when generating Changelog.
diff --git a/src_plugins/io_kicad_legacy/Makefile b/src_plugins/io_kicad_legacy/Makefile
new file mode 100644
index 0000000..f44b79c
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_io_kicad_legacy
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/io_kicad_legacy/Plug.tmpasm b/src_plugins/io_kicad_legacy/Plug.tmpasm
new file mode 100644
index 0000000..021372a
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/Plug.tmpasm
@@ -0,0 +1,11 @@
+put /local/pcb/mod {io_kicad_legacy}
+put /local/pcb/mod/OBJS [@
+ $(PLUGDIR)/io_kicad_legacy/io_kicad_legacy.o
+ $(PLUGDIR)/io_kicad_legacy/write.o
+@]
+
+switch /local/pcb/io_kicad_legacy/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/io_kicad_legacy/README b/src_plugins/io_kicad_legacy/README
new file mode 100644
index 0000000..9154d12
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/README
@@ -0,0 +1,5 @@
+Load and save the design and elements in Kicad's legacy format.
+
+#state: work-in-progress
+#default: buildin
+#implements: io
diff --git a/src_plugins/io_kicad_legacy/io_kicad_legacy.c b/src_plugins/io_kicad_legacy/io_kicad_legacy.c
new file mode 100644
index 0000000..407df34
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/io_kicad_legacy.c
@@ -0,0 +1,74 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ * 
+ *  This module, io_kicad_legacy, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "plugins.h"
+#include "plug_io.h"
+#include "write.h"
+
+static plug_io_t io_kicad_legacy;
+
+int io_kicad_legacy_fmt(plug_io_t *ctx, plug_iot_t typ, int wr, const char *fmt)
+{
+	if (strcmp(ctx->description, fmt) == 0)
+		return 200;
+
+	if ((strcmp(fmt, "kicadl") != 0) ||
+		((typ & (~(PCB_IOT_FOOTPRINT | PCB_IOT_BUFFER | PCB_IOT_PCB))) != 0))
+		return 0;
+	if (wr)
+		return 100;
+	return 0; /* no read support yet */
+}
+
+static void hid_io_kicad_legacy_uninit(void)
+{
+	/* Runs once when the plugin is unloaded. TODO: free plugin-globals here. */
+}
+
+pcb_uninit_t hid_io_kicad_legacy_init(void)
+{
+
+	/* register the IO hook */
+	io_kicad_legacy.plugin_data = NULL;
+	io_kicad_legacy.fmt_support_prio = io_kicad_legacy_fmt;
+	io_kicad_legacy.parse_pcb = NULL;
+	io_kicad_legacy.parse_element = NULL;
+	io_kicad_legacy.parse_font = NULL;
+	io_kicad_legacy.write_buffer = io_kicad_legacy_write_buffer;
+	io_kicad_legacy.write_element = io_kicad_legacy_write_element;
+	io_kicad_legacy.write_pcb = io_kicad_legacy_write_pcb;
+	io_kicad_legacy.default_fmt = "kicadl";
+	io_kicad_legacy.description = "Kicad, legacy format";
+	io_kicad_legacy.save_preference_prio = 90;
+
+	HOOK_REGISTER(plug_io_t, plug_io_chain, &io_kicad_legacy);
+
+	/* TODO: Alloc plugin-globals here. */
+
+	return hid_io_kicad_legacy_uninit;
+}
+
diff --git a/src_plugins/io_kicad_legacy/write.c b/src_plugins/io_kicad_legacy/write.c
new file mode 100644
index 0000000..108d333
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/write.c
@@ -0,0 +1,1231 @@
+/*
+ *														COPYRIGHT
+ *
+ *	pcb-rnd, interactive printed circuit board design
+ *	Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *     Copyright (C) 2016 Erich S. Heinzle
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *	Contact addresses for paper mail and Email:
+ *	Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *	Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+#include <math.h>
+#include "plug_io.h"
+#include "error.h"
+#include "../io_kicad/uniq_name.h"
+#include "data.h"
+#include "write.h"
+#include "layer.h"
+#include "const.h"
+#include "netlist.h"
+#include "misc.h"
+
+#define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
+
+/* layer "0" is first copper layer = "0. Back - Solder"
+ * and layer "15" is "15. Front - Component"
+ * and layer "20" SilkScreen Back
+ * and layer "21" SilkScreen Front
+ */
+
+/* generates a line by line listing of the elements being saved */
+static int io_kicad_legacy_write_element_index(FILE * FP, DataTypePtr Data);
+
+/* generates a default via drill size for the layout */
+static int write_kicad_legacy_layout_via_drill_size(FILE * FP);
+
+/* writes the buffer to file */
+int io_kicad_legacy_write_buffer(plug_io_t *ctx, FILE * FP, BufferType *buff)
+{
+	/*fputs("io_kicad_legacy_write_buffer()", FP); */
+
+	fputs("PCBNEW-LibModule-V1	jan 01 jan 2016 00:00:01 CET\n",FP);
+	fputs("Units mm\n",FP);
+	fputs("$INDEX\n",FP);
+	io_kicad_legacy_write_element_index(FP, buff->Data);
+	fputs("$EndINDEX\n",FP);
+
+	/* WriteViaData(FP, buff->Data); */
+
+	WriteElementData(FP, buff->Data, "kicadl");
+
+	/*
+		for (i = 0; i < max_copper_layer + 2; i++)
+		WriteLayerData(FP, i, &(buff->Data->Layer[i]));
+	*/
+	return (STATUS_OK);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes PCB to file
+ */
+int io_kicad_legacy_write_pcb(plug_io_t *ctx, FILE * FP, const char *old_filename, const char *new_filename, pcb_bool emergency)
+{
+	/* this is the first step in exporting a layout;
+	 * creating a kicd module containing the elements used in the layout
+	 */
+
+	/*fputs("io_kicad_legacy_write_pcb()", FP);*/
+
+	pcb_cardinal_t i;
+	int physicalLayerCount = 0;
+	int kicadLayerCount = 0;
+	int silkLayerCount= 0;
+	int outlineLayerCount = 0;
+	int layer = 0;
+	int currentKicadLayer = 0;
+	int currentGroup = 0;
+	Coord outlineThickness = PCB_MIL_TO_COORD(10); 
+
+	int bottomCount;
+	int *bottomLayers;
+	int innerCount;
+	int *innerLayers;
+	int topCount;
+	int *topLayers;
+	int bottomSilkCount;
+	int *bottomSilk;
+	int topSilkCount;
+	int *topSilk;
+	int outlineCount;
+	int *outlineLayers;
+
+	Coord LayoutXOffset;
+	Coord LayoutYOffset;
+
+	/* Kicad expects a layout "sheet" size to be specified in mils, and A4, A3 etc... */
+	int A4HeightMil = 8267;
+	int A4WidthMil = 11700;
+	int sheetHeight = A4HeightMil;
+	int sheetWidth = A4WidthMil;
+	int paperSize = 4; /* default paper size is A4 */
+
+	fputs("PCBNEW-BOARD Version 1 jan 01 jan 2016 00:00:01 CET\n",FP);
+
+	fputs("$GENERAL\n",FP);
+	fputs("Ly 1FFF8001\n",FP); /* obsolete, needed for old pcbnew */
+	/*puts("Units mm\n",FP);*/ /*decimils most universal legacy format */
+	fputs("$EndGENERAL\n",FP);
+
+	fputs("$SHEETDESCR\n",FP);
+
+
+	/* we sort out the needed kicad sheet size here, using A4, A3, A2, A1 or A0 size as needed */
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > A4WidthMil ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > A4HeightMil) {
+		sheetHeight = A4WidthMil;		/* 11.7" */
+		sheetWidth = 2*A4HeightMil; /* 16.5" */
+		paperSize = 3; /* this is A3 size */
+	}
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > sheetWidth ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > sheetHeight) {
+		sheetHeight = 2*A4HeightMil; /* 16.5" */
+		sheetWidth = 2*A4WidthMil;	 /* 23.4" */
+		paperSize = 2; /* this is A2 size */
+	}
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > sheetWidth ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > sheetHeight) {
+		sheetHeight = 2*A4WidthMil; /* 23.4" */
+		sheetWidth = 4*A4HeightMil; /* 33.1" */
+		paperSize = 1; /* this is A1 size */
+	}
+	if (PCB_COORD_TO_MIL(PCB->MaxWidth) > sheetWidth ||
+			PCB_COORD_TO_MIL(PCB->MaxHeight) > sheetHeight) {
+		sheetHeight = 4*A4HeightMil; /* 33.1" */
+		sheetWidth = 4*A4WidthMil; /* 46.8"	 */
+		paperSize = 0; /* this is A0 size; where would you get it made ?!?! */
+	}
+	fprintf(FP, "Sheet A%d ", paperSize);
+	/* we now sort out the offsets for centring the layout in the chosen sheet size here */
+	if (sheetWidth > PCB_COORD_TO_MIL(PCB->MaxWidth)) {	 /* usually A4, bigger if needed */
+		fprintf(FP, "%d ", sheetWidth); /* legacy kicad: elements decimils, sheet size mils*/
+		LayoutXOffset = PCB_MIL_TO_COORD(sheetWidth)/2 - PCB->MaxWidth/2;
+	} else { /* the layout is bigger than A0; most unlikely, but... */
+		pcb_fprintf(FP, "%.0ml ", PCB->MaxWidth);
+		LayoutXOffset = 0;
+	}
+	if (sheetHeight > PCB_COORD_TO_MIL(PCB->MaxHeight)) {
+		fprintf(FP, "%d", sheetHeight);
+		LayoutYOffset = PCB_MIL_TO_COORD(sheetHeight)/2 - PCB->MaxHeight/2;
+	} else { /* the layout is bigger than A0; most unlikely, but... */
+		pcb_fprintf(FP, "%.0ml", PCB->MaxHeight);
+		LayoutYOffset = 0;
+	}
+	fputs("\n", FP);
+	fputs("$EndSHEETDESCR\n",FP);
+
+	fputs("$SETUP\n",FP);
+	fputs("InternalUnit 0.000100 INCH\n",FP); /* decimil is the default v1 kicad legacy unit */
+
+	/* here we define the copper layers in the exported kicad file */
+	physicalLayerCount = pcb_layer_group_list(PCB_LYT_COPPER, NULL, 0);
+
+	fputs("Layers ",FP);
+	kicadLayerCount = physicalLayerCount;
+	if (kicadLayerCount%2 == 1) {
+		kicadLayerCount++; /* kicad doesn't like odd numbers of layers, has been deprecated for some time apparently */
+	}
+	
+	fprintf(FP, "%d\n", kicadLayerCount); 
+	layer = 0;
+	if (physicalLayerCount >= 1) {
+		fprintf(FP, "Layer[%d] COPPER_LAYER_0 signal\n", layer);
+	}
+	if (physicalLayerCount > 1) { /* seems we need to ignore layers > 16 due to kicad limitation */
+		for (layer = 1; (layer < (kicadLayerCount - 1)) && (layer < 15); layer++ ) {
+			fprintf(FP, "Layer[%d] Inner%d.Cu signal\n", layer, layer);
+		} 
+		fputs("Layer[15] COPPER_LAYER_15 signal\n",FP);	
+	}
+
+	write_kicad_legacy_layout_via_drill_size(FP);
+
+	fputs("$EndSETUP\n",FP);
+
+	/* now come the netlist "equipotential" descriptors */
+
+	write_kicad_legacy_equipotential_netlists(FP, PCB);
+
+	/* module descriptions come next */
+
+	write_kicad_legacy_layout_elements(FP, PCB, PCB->Data, LayoutXOffset, LayoutYOffset);
+
+	/* we now need to map pcb's layer groups onto the kicad layer numbers */
+	currentKicadLayer = 0;
+	currentGroup = 0;
+
+	/* figure out which pcb layers are bottom copper and make a list */
+	bottomLayers = malloc(sizeof(int) * physicalLayerCount);
+	/*int bottomLayers[physicalLayerCount];*/
+	bottomCount = pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_COPPER, NULL, 0);
+	pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_COPPER, bottomLayers, physicalLayerCount);
+
+	/* figure out which pcb layers are internal copper layers and make a list */
+	innerLayers = malloc(sizeof(int) * physicalLayerCount);
+	/*int innerLayers[physicalLayerCount];*/
+	innerCount = pcb_layer_list(PCB_LYT_INTERN | PCB_LYT_COPPER, NULL, 0);
+	pcb_layer_list(PCB_LYT_INTERN | PCB_LYT_COPPER, innerLayers, physicalLayerCount);
+
+	/* figure out which pcb layers are top copper and make a list */
+	topLayers = malloc(sizeof(int) * physicalLayerCount);
+	/*int topLayers[physicalLayerCount];*/
+	topCount = pcb_layer_list(PCB_LYT_TOP | PCB_LYT_COPPER, NULL, 0);
+	pcb_layer_list(PCB_LYT_TOP | PCB_LYT_COPPER, topLayers, physicalLayerCount);
+
+	silkLayerCount = pcb_layer_group_list(PCB_LYT_SILK, NULL, 0);
+
+	/* figure out which pcb layers are bottom silk and make a list */
+	bottomSilk = malloc(sizeof(int) * silkLayerCount);
+	/*int bottomSilk[silkLayerCount];*/
+	bottomSilkCount = pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_SILK, NULL, 0);
+	pcb_layer_list(PCB_LYT_BOTTOM | PCB_LYT_SILK, bottomSilk, silkLayerCount);
+
+	/* figure out which pcb layers are top silk and make a list */
+	topSilk = malloc(sizeof(int) * silkLayerCount);
+	/*int topSilk[silkLayerCount];*/
+	topSilkCount = pcb_layer_list(PCB_LYT_TOP | PCB_LYT_SILK, NULL, 0);
+	pcb_layer_list(PCB_LYT_TOP | PCB_LYT_SILK, topSilk, silkLayerCount);
+
+	outlineLayerCount = pcb_layer_group_list(PCB_LYT_OUTLINE, NULL, 0);
+
+	/* figure out which pcb layers are outlines and make a list */
+	outlineLayers = malloc(sizeof(int) * outlineLayerCount);
+	outlineCount = pcb_layer_list(PCB_LYT_OUTLINE, NULL, 0);
+	pcb_layer_list(PCB_LYT_OUTLINE, outlineLayers, outlineCount);
+
+	/* we now proceed to write the outline tracks to the kicad file, layer by layer */
+	currentKicadLayer = 28; /* 28 is the edge cuts layer in kicad */
+	if (outlineCount != 0) {
+		for (i = 0; i < outlineCount; i++) /* write top copper tracks, if any */
+			{
+				write_kicad_legacy_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[outlineLayers[i]]),
+										LayoutXOffset, LayoutYOffset);
+				write_kicad_legacy_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[outlineLayers[i]]),
+										LayoutXOffset, LayoutYOffset);
+			}
+	} else { /* no outline layer per se, export the board margins instead  - obviously some scope to reduce redundant code...*/
+				fputs("$DRAWSEGMENT\n", FP);
+				pcb_fprintf(FP, "Po 0 %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										outlineThickness);
+										pcb_fprintf(FP, "De %d 0 0 0 0\n", currentKicadLayer);
+				fputs("$EndDRAWSEGMENT\n", FP);
+				fputs("$DRAWSEGMENT\n", FP);
+				pcb_fprintf(FP, "Po 0 %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										outlineThickness);
+										pcb_fprintf(FP, "De %d 0 0 0 0\n", currentKicadLayer);
+				fputs("$EndDRAWSEGMENT\n", FP);
+				fputs("$DRAWSEGMENT\n", FP);
+				pcb_fprintf(FP, "Po 0 %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										PCB->MaxWidth/2 + LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										outlineThickness);
+										pcb_fprintf(FP, "De %d 0 0 0 0\n", currentKicadLayer);
+				fputs("$EndDRAWSEGMENT\n", FP);
+				fputs("$DRAWSEGMENT\n", FP);
+				pcb_fprintf(FP, "Po 0 %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 + LayoutYOffset,
+										PCB->MaxWidth/2 - LayoutXOffset, PCB->MaxHeight/2 - LayoutYOffset,
+										outlineThickness);
+										pcb_fprintf(FP, "De %d 0 0 0 0\n", currentKicadLayer);
+				fputs("$EndDRAWSEGMENT\n", FP);
+	}
+
+
+	/* we now proceed to write the bottom silk lines, arcs, text to the kicad legacy file, using layer 20 */
+	currentKicadLayer = 20; /* 20 is the bottom silk layer in kicad */
+	for (i = 0; i < bottomSilkCount; i++) /* write bottom silk lines, if any */
+		{
+			write_kicad_legacy_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the bottom copper text to the kicad legacy file, layer by layer */
+	currentKicadLayer = 0; /* 0 is the bottom copper layer in kicad */
+	for (i = 0; i < bottomCount; i++) /* write bottom copper tracks, if any */
+		{
+			write_kicad_legacy_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}	/* 0 is the bottom most track in kicad */
+
+	/* we now proceed to write the internal copper text to the kicad file, layer by layer */
+	if (innerCount != 0) {
+		currentGroup = pcb_layer_lookup_group(innerLayers[0]);
+	}
+	for (i = 0, currentKicadLayer = 1; i < innerCount; i++) /* write inner copper text, group by group */
+		{
+			if (currentGroup != pcb_layer_lookup_group(innerLayers[i])) {
+				currentGroup = pcb_layer_lookup_group(innerLayers[i]);
+				currentKicadLayer++;
+				if (currentKicadLayer > 14) {
+					currentKicadLayer = 14; /* kicad 16 layers in total, 0...15 */
+				}
+			}
+			write_kicad_legacy_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the top copper text to the kicad legacy file, layer by layer */
+	currentKicadLayer = 15; /* 15 is the top most copper layer in kicad */
+	for (i = 0; i < topCount; i++) /* write top copper tracks, if any */
+		{
+			write_kicad_legacy_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the top silk lines, arcs, text to the kicad legacy file, using layer 21 */
+	currentKicadLayer = 21; /* 21 is the top silk layer in kicad */
+	for (i = 0; i < topSilkCount; i++) /* write top silk lines, if any */
+		{
+			write_kicad_legacy_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_text(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* having done the graphical elements, we move onto tracks and vias */ 
+
+	fputs("$TRACK\n",FP);
+	write_kicad_legacy_layout_vias(FP, PCB->Data, LayoutXOffset, LayoutYOffset);
+
+	/* we now proceed to write the bottom copper tracks to the kicad legacy file, layer by layer */
+	currentKicadLayer = 0; /* 0 is the bottom copper layer in kicad */
+	for (i = 0; i < bottomCount; i++) /* write bottom copper tracks, if any */
+		{
+			write_kicad_legacy_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}	/* 0 is the bottom most track in kicad */
+
+	/* we now proceed to write the internal copper tracks to the kicad file, layer by layer */
+	if (innerCount != 0) {
+		currentGroup = pcb_layer_lookup_group(innerLayers[0]);
+	}
+	for (i = 0, currentKicadLayer = 1; i < innerCount; i++) /* write inner copper tracks, group by group */
+		{
+			if (currentGroup != pcb_layer_lookup_group(innerLayers[i])) {
+				currentGroup = pcb_layer_lookup_group(innerLayers[i]);
+				currentKicadLayer++;
+				if (currentKicadLayer > 14) {
+					currentKicadLayer = 14; /* kicad 16 layers in total, 0...15 */
+				}
+			}
+			write_kicad_legacy_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the top copper tracks to the kicad legacy file, layer by layer */
+	currentKicadLayer = 15; /* 15 is the top most copper layer in kicad */
+	for (i = 0; i < topCount; i++) /* write top copper tracks, if any */
+		{
+			write_kicad_legacy_layout_tracks(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+			write_kicad_legacy_layout_arcs(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+	fputs("$EndTRACK\n",FP);
+
+	/*
+	  * now we proceed to write polygons for each layer, and iterate much like we did for tracks
+         */
+
+	/* we now proceed to write the bottom silk polygons  to the kicad legacy file, using layer 20 */
+	currentKicadLayer = 20; /* 20 is the bottom silk layer in kicad */
+	for (i = 0; i < bottomSilkCount; i++) /* write bottom silk polygons, if any */
+		{
+			write_kicad_legacy_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[bottomSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the bottom copper polygons to the kicad legacy file, layer by layer */
+	currentKicadLayer = 0; /* 0 is the bottom copper layer in kicad */
+	for (i = 0; i < bottomCount; i++) /* write bottom copper polygons, if any */
+		{
+			write_kicad_legacy_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[bottomLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}	/* 0 is the bottom most track in kicad */
+
+	/* we now proceed to write the internal copper polygons to the kicad file, layer by layer */
+	if (innerCount != 0) {
+		currentGroup = pcb_layer_lookup_group(innerLayers[0]);
+	}
+	for (i = 0, currentKicadLayer = 1; i < innerCount; i++) /* write inner copper polygons, group by group */
+		{
+			if (currentGroup != pcb_layer_lookup_group(innerLayers[i])) {
+				currentGroup = pcb_layer_lookup_group(innerLayers[i]);
+				currentKicadLayer++;
+				if (currentKicadLayer > 14) {
+					currentKicadLayer = 14; /* kicad 16 layers in total, 0...15 */
+				}
+			}
+			write_kicad_legacy_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[innerLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the top copper polygons to the kicad legacy file, layer by layer */
+	currentKicadLayer = 15; /* 15 is the top most copper layer in kicad */
+	for (i = 0; i < topCount; i++) /* write top copper polygons, if any */
+		{
+			write_kicad_legacy_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[topLayers[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+	/* we now proceed to write the top silk polygons to the kicad legacy file, using layer 21 */
+	currentKicadLayer = 21; /* 21 is the top silk layer in kicad */
+	for (i = 0; i < topSilkCount; i++) /* write top silk polygons, if any */
+		{
+			write_kicad_legacy_layout_polygons(FP, currentKicadLayer, &(PCB->Data->Layer[topSilk[i]]),
+									LayoutXOffset, LayoutYOffset);
+		}
+
+
+	fputs("$EndBOARD\n",FP);
+
+	/* now free memory from arrays that were used */
+	free(bottomLayers);
+	free(innerLayers);
+	free(topLayers);
+	free(topSilk);
+	free(bottomSilk);
+	free(outlineLayers);
+	return (STATUS_OK);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes (eventually) de-duplicated list of element names in kicad legacy format module $INDEX
+ */
+static int io_kicad_legacy_write_element_index(FILE * FP, DataTypePtr Data)
+{
+	gdl_iterator_t eit;
+	ElementType *element;
+	unm_t group1; /* group used to deal with missing names and provide unique ones if needed */
+
+	elementlist_dedup_initializer(ededup);
+
+	/* Now initialize the group with defaults */
+	unm_init(&group1);
+
+	elementlist_foreach(&Data->Element, &eit, element) {
+		elementlist_dedup_skip(ededup, element);
+		/* skip duplicate elements */
+		/* only non empty elements */
+
+		if (!linelist_length(&element->Line)
+				&& !pinlist_length(&element->Pin)
+				&& !arclist_length(&element->Arc)
+				&& !padlist_length(&element->Pad))
+			continue;
+
+		fprintf(FP, "%s\n", unm_name(&group1, element->Name[0].TextString, element));
+
+	}
+	/* Release unique name utility memory */
+	unm_uninit(&group1);
+	/* free the state used for deduplication */
+	elementlist_dedup_free(ededup);
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes kicad format via data
+ For a track segment:
+ Position shape Xstart Ystart Xend Yend width
+ Description layer 0 netcode timestamp status
+ Shape parameter is set to 0 (reserved for futu
+*/
+
+
+int write_kicad_legacy_layout_vias(FILE * FP, DataTypePtr Data, Coord xOffset, Coord yOffset)
+{
+	gdl_iterator_t it;
+	PinType *via;
+	/* write information about vias */
+	pinlist_foreach(&Data->Via, &it, via) {
+		/*		pcb_fprintf(FP, "Po 3 %.3mm %.3mm %.3mm %.3mm %.3mm\n",
+					via->X, via->Y, via->X, via->Y, via->Thickness);
+					pcb_fprintf(FP, "De F0 1 0 0 0\n"); */
+		pcb_fprintf(FP, "Po 3 %.0mk %.0mk %.0mk %.0mk %.0mk\n", /* testing kicad printf */
+								via->X + xOffset, via->Y + yOffset,
+								via->X + xOffset, via->Y + yOffset, via->Thickness);
+		pcb_fprintf(FP, "De 15 1 0 0 0\n"); /* this is equivalent to 0F, via from 15 -> 0 */
+	}
+	return 0;
+}
+
+static int write_kicad_legacy_layout_via_drill_size(FILE * FP)
+{
+	pcb_fprintf(FP, "ViaDrill 250\n"); /* decimil format, default for now, ~= 0.635mm */
+	return 0;
+}
+
+int write_kicad_legacy_layout_tracks(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset)
+{
+	gdl_iterator_t it;
+	LineType *line;
+	pcb_cardinal_t currentLayer = number;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		/*
+			fprintf(FP, "Layer(%i ", (int) Number + 1);
+			PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+			fputs(")\n(\n", FP);
+			WriteAttributeList(FP, &layer->Attributes, "\t");
+		*/
+		int localFlag = 0;
+		linelist_foreach(&layer->Line, &it, line) {
+			if (currentLayer < 16) { /* a copper line i.e. track */
+				pcb_fprintf(FP, "Po 0 %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										line->Point1.X + xOffset, line->Point1.Y + yOffset,
+										line->Point2.X + xOffset, line->Point2.Y + yOffset,
+										line->Thickness);
+				pcb_fprintf(FP, "De %d 0 0 0 0\n", currentLayer); /* omitting net info */
+			} else if ((currentLayer == 20) || (currentLayer == 21) || (currentLayer == 28) ) { /* a silk line or outline */
+				fputs("$DRAWSEGMENT\n", FP);
+				pcb_fprintf(FP, "Po 0 %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										line->Point1.X + xOffset, line->Point1.Y + yOffset,
+										line->Point2.X + xOffset, line->Point2.Y + yOffset,
+										line->Thickness);
+				pcb_fprintf(FP, "De %d 0 0 0 0\n", currentLayer); /* omitting net info */
+				fputs("$EndDRAWSEGMENT\n", FP);
+			}
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
+
+int write_kicad_legacy_layout_arcs(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset)
+{
+	gdl_iterator_t it;
+	ArcType *arc;
+	ArcType localArc; /* for converting ellipses to circular arcs */
+	BoxType *boxResult; /* for figuring out arc ends */
+	pcb_cardinal_t currentLayer = number;
+	Coord radius, xStart, yStart, xEnd, yEnd;
+	int copperStartX; /* used for mapping geda copper arcs onto kicad copper lines */
+	int copperStartY; /* used for mapping geda copper arcs onto kicad copper lines */
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		/*
+			fprintf(FP, "Layer(%i ", (int) Number + 1);
+			PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+			fputs(")\n(\n", FP);
+			WriteAttributeList(FP, &layer->Attributes, "\t");
+		*/
+		int localFlag = 0;
+		int kicadArcShape = 2; /* 3 = circle, and 2 = arc, 1= rectangle used in eeschema only */ 
+		arclist_foreach(&layer->Arc, &it, arc) {
+			localArc = *arc;
+			if (arc->Width > arc->Height) {
+				radius = arc->Height;
+				localArc.Width = radius;
+			} else {
+				radius = arc->Width;
+				localArc.Height = radius;
+			}
+		boxResult = GetArcEnds(&localArc);
+			if (arc->Delta == 360.0 || arc->Delta == -360.0 ) { /* it's a circle */
+				kicadArcShape = 3;
+			} else { /* it's an arc */
+				kicadArcShape = 2;
+			}
+			xStart = localArc.X + xOffset;
+			yStart = localArc.Y + yOffset;
+			xEnd = boxResult->X2 + xOffset; 
+			yEnd = boxResult->Y2 + yOffset; 
+			copperStartX = boxResult->X1 + xOffset;
+			copperStartY = boxResult->Y1 + yOffset; 
+			if (currentLayer < 16) { /* a copper arc, i.e. track, is unsupported by kicad, and will be exported as a line */
+				kicadArcShape = 0; /* make it a line for copper layers - kicad doesn't do arcs on copper */ 
+				pcb_fprintf(FP, "Po %d %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										kicadArcShape, copperStartX, copperStartY, xEnd, yEnd,
+										arc->Thickness);
+				pcb_fprintf(FP, "De %d 0 0 0 0\n", currentLayer); /* in theory, copper arcs unsupported by kicad, make angle = 0 */
+			} else if ((currentLayer == 20) || (currentLayer == 21) || (currentLayer == 28) ) { /* a silk arc or outline */
+				fputs("$DRAWSEGMENT\n", FP);
+				pcb_fprintf(FP, "Po %d %.0mk %.0mk %.0mk %.0mk %.0mk\n",
+										kicadArcShape, xStart, yStart, xEnd, yEnd,
+										arc->Thickness);
+				pcb_fprintf(FP, "De %d 0 %mA 0 0\n", currentLayer, arc->Delta); /* in theory, decidegrees != 900 unsupported by older kicad*/
+				fputs("$EndDRAWSEGMENT\n", FP);
+			}
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
+
+int write_kicad_legacy_layout_text(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset)
+{
+	FontType *myfont = &PCB->Font;
+	Coord mWidth = myfont->MaxWidth; /* kicad needs the width of the widest letter */
+	Coord defaultStrokeThickness = 100*2540; /* use 100 mil as default 100% stroked font line thickness */
+	int kicadMirrored = 1; /* 1 is not mirrored, 0  is mirrored */ 
+
+	Coord defaultXSize;
+	Coord defaultYSize;
+	Coord strokeThickness;
+	int rotation;	
+	Coord textOffsetX;
+	Coord textOffsetY;
+	Coord halfStringWidth;
+	Coord halfStringHeight;
+	int localFlag;
+
+	gdl_iterator_t it;
+	TextType *text;
+	pcb_cardinal_t currentLayer = number;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		/*
+			fprintf(FP, "Layer(%i ", (int) Number + 1);
+			PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+			fputs(")\n(\n", FP);
+			WriteAttributeList(FP, &layer->Attributes, "\t");
+		*/
+		localFlag = 0;
+		textlist_foreach(&layer->Text, &it, text) {
+			if ((currentLayer < 16) || (currentLayer == 20) || (currentLayer == 21) ) { /* copper or silk layer text */
+				fputs("$TEXTPCB\nTe \"", FP);
+				fputs(text->TextString,FP);
+				fputs("\"\n", FP);
+				defaultXSize = 5*PCB_SCALE_TEXT(mWidth, text->Scale)/6; /* IIRC kicad treats this as kerned width of upper case m */
+				defaultYSize = defaultXSize;
+				strokeThickness = PCB_SCALE_TEXT(defaultStrokeThickness, text->Scale /2);
+				rotation = 0;	
+				textOffsetX = 0;
+				textOffsetY = 0;
+				halfStringWidth = (text->BoundingBox.X2 - text->BoundingBox.X1)/2;
+				if (halfStringWidth < 0) {
+					halfStringWidth = -halfStringWidth;
+				}
+				halfStringHeight = (text->BoundingBox.Y2 - text->BoundingBox.Y1)/2;
+				if (halfStringHeight < 0) {
+					halfStringHeight = -halfStringHeight;
+				}
+				if (text->Direction == 3) { /*vertical down*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */ 
+						rotation = 2700;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY -= halfStringHeight;
+						textOffsetX -= 2*halfStringWidth; /* was 1*hsw */
+					} else {    /* front copper or silk */
+						rotation = 2700;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY = halfStringHeight;
+						textOffsetX -= halfStringWidth;
+					}
+				} else if (text->Direction == 2)  { /*upside down*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */ 
+						rotation = 0;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY += halfStringHeight;
+					} else {    /* front copper or silk */
+						rotation = 1800;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY -= halfStringHeight;
+					}
+					textOffsetX = -halfStringWidth;
+				} else if (text->Direction == 1) { /*vertical up*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */ 
+						rotation = 900;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY = halfStringHeight;
+						textOffsetX += halfStringWidth; 
+					} else {    /* front copper or silk */
+						rotation = 900;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY = -halfStringHeight;
+						textOffsetX = 0; /* += halfStringWidth; */
+					}
+				} else if (text->Direction == 0)  { /*normal text*/
+					if (currentLayer == 0 || currentLayer == 20) {  /* back copper or silk */
+						rotation = 1800;
+						kicadMirrored = 0; /* mirrored */
+						textOffsetY -= halfStringHeight;
+					} else {    /* front copper or silk */
+						rotation = 0;
+						kicadMirrored = 1; /* not mirrored */
+						textOffsetY += halfStringHeight;
+					}
+					textOffsetX = halfStringWidth;
+				}
+/*				printf("\"%s\" direction field: %d\n", text->TextString, text->Direction);
+				printf("textOffsetX: %d,  textOffsetY: %d\n", textOffsetX, textOffsetY); */
+				pcb_fprintf(FP, "Po %.0mk %.0mk %.0mk %.0mk %.0mk %d\n",
+										text->X + xOffset + textOffsetX, text->Y + yOffset + textOffsetY,
+										defaultXSize, defaultYSize, strokeThickness, rotation);
+				pcb_fprintf(FP, "De %d %d B98C Normal\n", currentLayer, kicadMirrored); /* timestamp made up B98C  */
+				fputs("$EndTEXTPCB\n", FP);
+			}
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes element data in kicad legacy format for use in a .mod library
+ */
+int io_kicad_legacy_write_element(plug_io_t *ctx, FILE * FP, DataTypePtr Data)
+{
+
+
+	/*
+	write_kicad_legacy_module_header(FP);
+	fputs("io_kicad_legacy_write_element()", FP);
+	return 0;
+	*/
+
+
+	gdl_iterator_t eit;
+	LineType *line;
+	ArcType *arc;
+	ElementType *element;
+	BoxType *boxResult;
+
+	Coord arcStartX, arcStartY, arcEndX, arcEndY; /* for arc exporting */
+
+	unm_t group1; /* group used to deal with missing names and provide unique ones if needed */
+	const char * currentElementName;
+
+	elementlist_dedup_initializer(ededup);
+	/* Now initialize the group with defaults */
+	unm_init(&group1);
+
+	elementlist_foreach(&Data->Element, &eit, element) {
+		gdl_iterator_t it;
+		PinType *pin;
+		PadType *pad;
+
+		elementlist_dedup_skip(ededup, element); /* skip duplicate elements */
+
+		/* TOOD: Footprint name element->Name[0].TextString */
+
+		/* only non empty elements */
+		if (!linelist_length(&element->Line) && !pinlist_length(&element->Pin) && !arclist_length(&element->Arc) && !padlist_length(&element->Pad))
+			continue;
+		/* the coordinates and text-flags are the same for
+		 * both names of an element
+		 */
+		/* the following element summary is not used
+			 in kicad; the module's header contains this
+			 information
+
+			 fprintf(FP, "\nDS %s ", F2S(element, PCB_TYPE_ELEMENT));
+			 PrintQuotedString(FP, (char *) EMPTY(DESCRIPTION_NAME(element)));
+			 fputc(' ', FP);
+			 PrintQuotedString(FP, (char *) EMPTY(NAMEONPCB_NAME(element)));
+			 fputc(' ', FP);
+			 PrintQuotedString(FP, (char *) EMPTY(VALUE_NAME(element)));
+			 pcb_fprintf(FP, " %mm %mm %mm %mm %d %d %s]\n(\n",
+			 element->MarkX, element->MarkY,
+			 DESCRIPTION_TEXT(element).X - element->MarkX,
+			 DESCRIPTION_TEXT(element).Y - element->MarkY,
+			 DESCRIPTION_TEXT(element).Direction,
+			 DESCRIPTION_TEXT(element).Scale, F2S(&(DESCRIPTION_TEXT(element)), PCB_TYPE_ELEMENT_NAME));
+
+		*/
+
+		/*		//WriteAttributeList(FP, &element->Attributes, "\t");
+		 */
+
+		currentElementName = unm_name(&group1, element->Name[0].TextString, element);
+		fprintf(FP, "$MODULE %s\n", currentElementName);
+		fputs("Po 0 0 0 15 51534DFF 00000000 ~~\n",FP);
+		fprintf(FP, "Li %s\n", currentElementName);
+		fprintf(FP, "Cd %s\n", currentElementName);
+		fputs("Sc 0\n",FP);
+		fputs("AR\n",FP);
+		fputs("Op 0 0 0\n",FP);
+		fputs("T0 0 -6.000 1.524 1.524 0 0.305 N V 21 N \"S***\"\n",FP); /*1.524 is basically 600 decimil, 0.305 is ~= 120 decimil */
+
+		linelist_foreach(&element->Line, &it, line) {
+			pcb_fprintf(FP, "DS %.3mm %.3mm %.3mm %.3mm %.3mm ",
+									line->Point1.X - element->MarkX,
+									line->Point1.Y - element->MarkY,
+									line->Point2.X - element->MarkX,
+									line->Point2.Y - element->MarkY,
+									line->Thickness);
+			fputs("21\n",FP); /* an arbitrary Kicad layer, front silk, need to refine this */
+		}
+
+		arclist_foreach(&element->Arc, &it, arc) {
+			boxResult = GetArcEnds(arc);
+			arcStartX = boxResult->X1;
+			arcStartY = boxResult->Y1;
+			arcEndX = boxResult->X2; 
+			arcEndY = boxResult->Y2; 
+			if ((arc->Delta == 360.0) || (arc->Delta == -360.0)) { /* it's a circle */
+				pcb_fprintf(FP, "DC %.3mm %.3mm %.3mm %.3mm %.3mm ",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcStartX - element->MarkX, /* x on circle */
+										arcStartY - element->MarkY, /* y on circle */
+										arc->Thickness); /* stroke thickness */
+			} else {
+				/*
+				   as far as can be determined from the Kicad documentation,
+				   http://en.wikibooks.org/wiki/Kicad/file_formats#Drawings
+
+				   the origin for rotation is the positive x direction, and going CW
+
+				   whereas in gEDA, the gEDA origin for rotation is the negative x axis,
+				   with rotation CCW, so we need to reverse delta angle
+
+				   deltaAngle is CW in Kicad in deci-degrees, and CCW in degrees in gEDA
+				   NB it is in degrees in the newer s-file kicad module/footprint format
+				*/
+				pcb_fprintf(FP, "DA %.3mm %.3mm %.3mm %.3mm %mA %.3mm ",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcEndX - element->MarkX, /* x on arc */
+										arcEndY - element->MarkY, /* y on arc */
+										arc->Delta, /* CW delta angle in decidegrees */
+										arc->Thickness); /* stroke thickness */
+			}
+			fputs("21\n",FP); /* and now append a suitable Kicad layer, front silk = 21 */
+		}
+
+		pinlist_foreach(&element->Pin, &it, pin) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for a pin */
+
+			pcb_fprintf(FP, "Po %.3mm %.3mm\n", /* positions of pad */
+									pin->X - element->MarkX,
+									pin->Y - element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pin->Number));
+
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+				fputs(" R ",FP); /* square */
+			} else {
+				fputs(" C ",FP); /* circular */
+			}
+
+			pcb_fprintf(FP, "%.3mm %.3mm ", pin->Thickness, pin->Thickness); /* height = width */
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr ",FP); /* drill details; size and x,y pos relative to pad location */
+			pcb_fprintf(FP, "%mm 0 0\n", pin->DrillingHole);
+
+			fputs("At STD N 00E0FFFF\n", FP); /* through hole STD pin, all copper layers */
+
+			fputs("Ne 0 \"\"\n",FP); /* library parts have empty net descriptors */
+			/*
+				PrintQuotedString(FP, (char *) EMPTY(pin->Name));
+				fprintf(FP, " %s\n", F2S(pin, PCB_TYPE_PIN));
+			*/
+			fputs("$EndPAD\n",FP);
+		}
+		padlist_foreach(&element->Pad, &it, pad) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for an smd pad */
+
+			pcb_fprintf(FP, "Po %.3mm %.3mm\n", /* positions of pad */
+									(pad->Point1.X + pad->Point2.X)/2- element->MarkX,
+									(pad->Point1.Y + pad->Point2.Y)/2- element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pad->Number));
+			fputs(" R ",FP); /* rectangular, not a pin */
+
+			if ((pad->Point1.X-pad->Point2.X) <= 0
+					&& (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) <= 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.3mm %.3mm ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			}
+
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr 0 0 0\n",FP); /* drill details; zero size; x,y pos vs pad location */
+
+			fputs("At SMD N 00888000\n", FP); /* SMD pin, need to use right layer mask */
+
+			fputs("Ne 0 \"\"\n",FP); /* library parts have empty net descriptors */
+			fputs("$EndPAD\n",FP);
+		}
+		fprintf(FP, "$EndMODULE %s\n", currentElementName);		
+	}
+	/* Release unique name utility memory */
+	unm_uninit(&group1);
+	/* free the state used for deduplication */
+	elementlist_dedup_free(ededup);
+
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes netlist data in kicad legacy format for use in a layout .brd file
+ */
+
+int write_kicad_legacy_equipotential_netlists(FILE * FP, PCBTypePtr Layout)
+{
+        int n; /* code mostly lifted from netlist.c */ 
+	int netNumber;
+	LibraryMenuTypePtr menu;
+	LibraryEntryTypePtr netlist;
+	
+	/* first we write a default netlist for the 0 net, which is for unconnected pads in pcbnew */
+	fputs("$EQUIPOT\n",FP);
+	fputs("Na 0 \"\"\n", FP);
+	fputs("St ~\n", FP);
+	fputs("$EndEQUIPOT\n",FP);
+
+	/* now we step through any available netlists and generate descriptors */
+        for (n = 0, netNumber = 1; n < Layout->NetlistLib[NETLIST_EDITED].MenuN; n++, netNumber ++) {
+                menu = &Layout->NetlistLib[NETLIST_EDITED].Menu[n];
+		netlist = &menu->Entry[0];
+		if (netlist != NULL) {
+			fputs("$EQUIPOT\n",FP);
+			fprintf(FP, "Na %d \"%s\"\n", netNumber, pcb_netlist_name(menu));  /* netlist 0 was used for unconnected pads  */
+			fputs("St ~\n", FP);
+			fputs("$EndEQUIPOT\n",FP);
+                }
+        }
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes element data in kicad legacy format for use in a layout .brd file
+ */
+int write_kicad_legacy_layout_elements(FILE * FP, PCBTypePtr Layout, DataTypePtr Data, Coord xOffset, Coord yOffset)
+{
+
+	gdl_iterator_t eit;
+	LineType *line;
+	ArcType *arc;
+	Coord arcStartX, arcStartY, arcEndX, arcEndY; /* for arc rendering */
+	Coord xPos, yPos;
+
+	ElementType *element;
+	unm_t group1; /* group used to deal with missing names and provide unique ones if needed */
+	const char * currentElementName;
+	LibraryMenuTypePtr current_pin_menu;
+	LibraryMenuTypePtr current_pad_menu;
+
+	int silkLayer = 21;  /* hard coded default, 20 is bottom silk */ 
+	int copperLayer = 15; /* hard coded default, 0 is bottom copper */
+
+	elementlist_dedup_initializer(ededup);
+	/* Now initialize the group with defaults */
+	unm_init(&group1);
+
+	elementlist_foreach(&Data->Element, &eit, element) {
+		gdl_iterator_t it;
+		PinType *pin;
+		PadType *pad;
+
+		/* elementlist_dedup_skip(ededup, element);  */
+		/* let's not skip duplicate elements for layout export*/
+
+		/* TOOD: Footprint name element->Name[0].TextString */
+
+		/* only non empty elements */
+		if (!linelist_length(&element->Line) && !pinlist_length(&element->Pin) && !arclist_length(&element->Arc) && !padlist_length(&element->Pad))
+			continue;
+		/* the coordinates and text-flags are the same for
+		 * both names of an element
+		 */
+
+		xPos = element->MarkX + xOffset;
+		yPos = element->MarkY + yOffset;
+		if (TEST_FLAG(PCB_FLAG_ONSOLDER, element)) {
+			silkLayer = 20;
+			copperLayer = 0;
+		} else {
+			silkLayer = 21;
+			copperLayer = 15;
+		}
+
+		currentElementName = unm_name(&group1, element->Name[0].TextString, element);
+		fprintf(FP, "$MODULE %s\n", currentElementName);
+		pcb_fprintf(FP, "Po %.0mk %.0mk 0 %d 51534DFF 00000000 ~~\n", xPos, yPos, copperLayer);
+		fprintf(FP, "Li %s\n", currentElementName);
+		fprintf(FP, "Cd %s\n", currentElementName);
+		fputs("Sc 0\n",FP);
+		fputs("AR\n",FP);
+		fputs("Op 0 0 0\n",FP);
+		fprintf(FP, "T0 0 -4000 600 600 0 120 N V %d N \"%s\"\n", silkLayer, element->Name[NAMEONPCB_INDEX].TextString);
+		fprintf(FP, "T1 0 -5000 600 600 0 120 N V %d N \"%s\"\n", silkLayer, element->Name[VALUE_INDEX].TextString);
+		pinlist_foreach(&element->Pin, &it, pin) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for a pin */
+
+			pcb_fprintf(FP, "Po %.0mk %.0mk\n", /* positions of pad */
+									pin->X - element->MarkX,
+									pin->Y - element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pin->Number));
+
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+				fputs(" R ",FP); /* square */
+			} else {
+				fputs(" C ",FP); /* circular */
+			}
+
+			pcb_fprintf(FP, "%.0mk %.0mk ", pin->Thickness, pin->Thickness); /* height = width */
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr ",FP); /* drill details; size and x,y pos relative to pad location */
+			pcb_fprintf(FP, "%.0mk 0 0\n", pin->DrillingHole);
+
+			fputs("At STD N 00E0FFFF\n", FP); /* through hole STD pin, all copper layers */
+
+			current_pin_menu = pcb_netlist_find_net4pin(Layout, pin);
+			if ((current_pin_menu != NULL) && (pcb_netlist_net_idx(Layout, current_pin_menu) != PCB_NETLIST_INVALID_INDEX)) {
+				fprintf(FP, "Ne %d \"%s\"\n", (1 + pcb_netlist_net_idx(Layout, current_pin_menu)), pcb_netlist_name(current_pin_menu)); /* library parts have empty net descriptors, in a .brd they don't */
+			} else {
+				fprintf(FP, "Ne 0 \"\"\n"); /* unconnected pads have zero for net */
+			} 
+			/*
+				PrintQuotedString(FP, (char *) EMPTY(pin->Name));
+				fprintf(FP, " %s\n", F2S(pin, PCB_TYPE_PIN));
+			*/
+			fputs("$EndPAD\n",FP);
+		}
+		padlist_foreach(&element->Pad, &it, pad) {
+			fputs("$PAD\n",FP);	 /* start pad descriptor for an smd pad */
+
+			pcb_fprintf(FP, "Po %.0mk %.0mk\n", /* positions of pad */
+									(pad->Point1.X + pad->Point2.X)/2- element->MarkX,
+									(pad->Point1.Y + pad->Point2.Y)/2- element->MarkY);
+
+			fputs("Sh ",FP); /* pin shape descriptor */
+			PrintQuotedString(FP, (char *) EMPTY(pad->Number));
+			fputs(" R ",FP); /* rectangular, not a pin */
+
+			if ((pad->Point1.X-pad->Point2.X) <= 0
+					&& (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) <= 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point2.X-pad->Point1.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) > 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point1.Y-pad->Point2.Y + pad->Thickness); /* height */
+			} else if ((pad->Point1.X-pad->Point2.X) > 0
+								 && (pad->Point1.Y-pad->Point2.Y) <= 0 ) {
+				pcb_fprintf(FP, "%.0mk %.0mk ",
+										pad->Point1.X-pad->Point2.X + pad->Thickness,	 /* width */
+										pad->Point2.Y-pad->Point1.Y + pad->Thickness); /* height */
+			}
+
+			fputs("0 0 0\n",FP); /* deltaX deltaY Orientation as float in decidegrees */
+
+			fputs("Dr 0 0 0\n",FP); /* drill details; zero size; x,y pos vs pad location */
+
+			fputs("At SMD N 00888000\n", FP); /* SMD pin, need to use right layer mask */
+
+			current_pad_menu = pcb_netlist_find_net4pad(Layout, pad);
+			if ((current_pad_menu != NULL) && (pcb_netlist_net_idx(Layout, current_pad_menu) != PCB_NETLIST_INVALID_INDEX)) {
+				fprintf(FP, "Ne %d \"%s\"\n", (1 + pcb_netlist_net_idx(Layout, current_pad_menu)), pcb_netlist_name(current_pad_menu)); /* library parts have empty net descriptors, in a .brd they don't */
+			} else {
+				fprintf(FP, "Ne 0 \"\"\n"); /* a net number of 0 indicates an unconnected pad in pcbnew */
+			} 
+
+			fputs("$EndPAD\n",FP);
+
+		}
+		linelist_foreach(&element->Line, &it, line) {
+			pcb_fprintf(FP, "DS %.0mk %.0mk %.0mk %.0mk %.0mk ",
+									line->Point1.X - element->MarkX,
+									line->Point1.Y - element->MarkY,
+									line->Point2.X - element->MarkX,
+									line->Point2.Y - element->MarkY,
+									line->Thickness);
+			fprintf(FP, "%d\n", silkLayer); /* an arbitrary Kicad layer, front silk, need to refine this */
+		}
+
+		arclist_foreach(&element->Arc, &it, arc) {
+
+			BoxType *boxResult = GetArcEnds(arc);
+			arcStartX = boxResult->X1;
+			arcStartY = boxResult->Y1;
+			arcEndX = boxResult->X2; 
+			arcEndY = boxResult->Y2; 
+
+			if ((arc->Delta == 360.0) || (arc->Delta == -360.0)) { /* it's a circle */
+				pcb_fprintf(FP, "DC %.0mk %.0mk %.0mk %.0mk %.0mk ",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcStartX - element->MarkX, /* x on circle */
+										arcStartY - element->MarkY, /* y on circle */
+										arc->Thickness); /* stroke thickness */
+			} else {
+				pcb_fprintf(FP, "DA %.0mk %.0mk %.0mk %.0mk %mA %.0mk ",
+										arc->X - element->MarkX, /* x_1 centre */
+										arc->Y - element->MarkY, /* y_2 centre */
+										arcEndX - element->MarkX, /* x on arc */
+										arcEndY - element->MarkY, /* y on arc */
+										arc->Delta, /* CW delta angle in decidegrees */
+										arc->Thickness); /* stroke thickness */
+			}
+			fprintf(FP, "%d\n", silkLayer); /* and now append a suitable Kicad layer, front silk = 21, back silk 20 */
+		}
+
+		fprintf(FP, "$EndMODULE %s\n", currentElementName);
+	}
+	/* Release unique name utility memory */
+	unm_uninit(&group1);
+	/* free the state used for deduplication */
+	elementlist_dedup_free(ededup);
+
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes polygon data in kicad legacy format for use in a layout .brd file
+ */
+
+int write_kicad_legacy_layout_polygons(FILE * FP, pcb_cardinal_t number,
+																		 LayerTypePtr layer, Coord xOffset, Coord yOffset)
+{
+	int i, j;
+	gdl_iterator_t it;
+	PolygonType *polygon;
+	pcb_cardinal_t currentLayer = number;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		int localFlag = 0;
+		polylist_foreach(&layer->Polygon, &it, polygon) {
+			if (polygon->HoleIndexN == 0) { /* no holes defined within polygon, which we implement support for first */
+
+				/* preliminaries for zone settings */
+				fputs("$CZONE_OUTLINE\n", FP);
+				fputs("ZInfo 478E3FC8 0 \"\"\n", FP); /* use default empty netname, net 0, not connected */
+				fprintf(FP,"ZLayer %d\n", currentLayer); 
+				fprintf(FP,"ZAux %d E\n", polygon->PointN); /* corner count, use edge hatching for displaying the zone in pcbnew */
+				fputs("ZClearance 200 X\n", FP); /* set pads/pins to not be connected to pours in the zone by default */
+				fputs("ZMinThickness 190\n", FP); /* minimum copper thickness in zone, default setting */
+				fputs("ZOptions 0 32 F 200 200\n", FP); /* solid fill, 32 segments per arc, antipad thickness, thermal stubs width(s) */
+
+				/* now the zone outline is defined */
+
+				for (i = 0, j=0; i < polygon->PointN; i++) {
+					if (i == (polygon->PointN - 1) ) {
+						j = 1; /* flags that this is the last vertex of the outline */
+					}
+					pcb_fprintf(FP, "ZCorner %.0mk %.0mk %d\n",
+											polygon->Points[i].X + xOffset, polygon->Points[i].Y + yOffset, j);
+				}
+
+				/*
+				  *   in here could go additional plolygon descriptors for holes removed from  the previously defined outer polygon
+				  */ 
+
+				fputs("$endCZONE_OUTLINE\n",  FP);
+
+			} 
+			localFlag |= 1;
+		}
+		return localFlag;
+	} else {
+		return 0;
+	}
+}
diff --git a/src_plugins/io_kicad_legacy/write.h b/src_plugins/io_kicad_legacy/write.h
new file mode 100644
index 0000000..16461c8
--- /dev/null
+++ b/src_plugins/io_kicad_legacy/write.h
@@ -0,0 +1,48 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *  Copyright (C) 2016 Erich S. Heinzle
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include "global.h"
+#include "data.h"
+
+int io_kicad_legacy_write_element(plug_io_t *ctx, FILE * FP, DataTypePtr Data);
+int io_kicad_legacy_write_buffer(plug_io_t *ctx, FILE * FP, BufferType *buff);
+int io_kicad_legacy_write_pcb(plug_io_t *ctx, FILE * FP, const char *old_filename, const char *new_filename, pcb_bool emergency);
+int write_kicad_legacy_module_header(FILE * FP);
+int write_kicad_legacy_layout_header(FILE * FP);
+int write_kicad_legacy_layout_vias(FILE * FP, DataTypePtr Data, Coord MaxWidth, Coord MaxHeight);
+int write_kicad_legacy_layout_tracks(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,
+						Coord MaxWidth, Coord MaxHeight);
+int write_kicad_legacy_layout_arcs(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,
+						Coord xOffset, Coord yOffset);
+int write_kicad_legacy_layout_text(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,
+						Coord xOffset, Coord yOffset);
+int write_kicad_legacy_equipotential_netlists(FILE * FP, PCBTypePtr Layout);
+int write_kicad_legacy_layout_elements(FILE * FP, PCBTypePtr Layout, DataTypePtr Data, Coord xOffset, Coord yOffset);
+int write_kicad_legacy_layout_polygons(FILE * FP, pcb_cardinal_t number, LayerTypePtr layer,								Coord xOffset, Coord yOffset);
+
diff --git a/src_plugins/io_lihata/Makefile b/src_plugins/io_lihata/Makefile
new file mode 100644
index 0000000..c37de74
--- /dev/null
+++ b/src_plugins/io_lihata/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_io_lihata
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/io_lihata/Plug.tmpasm b/src_plugins/io_lihata/Plug.tmpasm
new file mode 100644
index 0000000..c75f9fb
--- /dev/null
+++ b/src_plugins/io_lihata/Plug.tmpasm
@@ -0,0 +1,16 @@
+put /local/pcb/mod {io_lihata}
+put /local/pcb/mod/OBJS [@
+ $(PLUGDIR)/io_lihata/io_lihata.o
+ $(PLUGDIR)/io_lihata/write.o
+ $(PLUGDIR)/io_lihata/write_style.o
+ $(PLUGDIR)/io_lihata/read.o
+ $(PLUGDIR)/io_lihata/common.o
+@]
+put /local/pcb/mod/CONF {$(PLUGDIR)/io_lihata/lht_conf.h}
+
+
+switch /local/pcb/io_lihata/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/io_lihata/README b/src_plugins/io_lihata/README
new file mode 100644
index 0000000..1cc4f8c
--- /dev/null
+++ b/src_plugins/io_lihata/README
@@ -0,0 +1,5 @@
+Load and save the design and elements in the lihata board format.
+
+#state: WIP
+#default: disabled
+#implements: io
diff --git a/src_plugins/io_lihata/TODO.check b/src_plugins/io_lihata/TODO.check
new file mode 100644
index 0000000..04552a1
--- /dev/null
+++ b/src_plugins/io_lihata/TODO.check
@@ -0,0 +1,4 @@
+- poly/pin shapes
+- flags on any object type
+- name on any object type
+- line ->Number?
\ No newline at end of file
diff --git a/src_plugins/io_lihata/common.c b/src_plugins/io_lihata/common.c
new file mode 100644
index 0000000..c8f8fba
--- /dev/null
+++ b/src_plugins/io_lihata/common.c
@@ -0,0 +1,65 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Build an in-memory lihata document that represents the board then save it.
+   A document is built for the merge-save. */
+
+#include <stdio.h>
+#include "global.h"
+#include "data.h"
+#include "macro.h"
+#include "common.h"
+
+static const char *thermal_style[] = {
+	NULL,
+	"diagonal-sharp",
+	"horver-sharp",
+	"solid",
+	"diagonal-round",
+	"horver-round"
+};
+
+int io_lihata_resolve_thermal_style(const char *name)
+{
+	int n;
+	char *end;
+
+	if (name == NULL)
+		return 0;
+
+	for(n = 1; n < ENTRIES(thermal_style); n++)
+		if (strcmp(name, thermal_style[n]) == 0)
+			return n;
+
+	n = strtol(name, &end, 10);
+	if (*end == '\0')
+		return n;
+
+	return 0;
+}
+
+const char *io_lihata_thermal_style(int idx)
+{
+	if ((idx > 0) && (idx < ENTRIES(thermal_style)))
+		return thermal_style[idx];
+	return NULL;
+}
diff --git a/src_plugins/io_lihata/common.h b/src_plugins/io_lihata/common.h
new file mode 100644
index 0000000..845f856
--- /dev/null
+++ b/src_plugins/io_lihata/common.h
@@ -0,0 +1,8 @@
+/* Because all the macros expect it, that's why.  */
+typedef struct {
+	FlagType Flags;
+} io_lihata_flag_holder;
+
+/* Convert between thermal style index and textual representation */
+const char *io_lihata_thermal_style(int idx);
+int io_lihata_resolve_thermal_style(const char *name);
diff --git a/src_plugins/io_lihata/io_lihata.c b/src_plugins/io_lihata/io_lihata.c
new file mode 100644
index 0000000..8b46c61
--- /dev/null
+++ b/src_plugins/io_lihata/io_lihata.c
@@ -0,0 +1,75 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "plugins.h"
+#include "plug_io.h"
+#include "read.h"
+#include "write.h"
+#include "io_lihata.h"
+
+static plug_io_t io_lihata;
+conf_io_lihata_t conf_io_lihata;
+
+int io_lihata_fmt(plug_io_t *ctx, plug_iot_t typ, int wr, const char *fmt)
+{
+	if (strcmp(ctx->description, fmt) == 0)
+		return 200;
+
+	if ((strcmp(fmt, "lihata") != 0) ||
+		((typ & (~(/*PCB_IOT_FOOTPRINT | PCB_IOT_BUFFER |*/ PCB_IOT_PCB))) != 0))
+		return 0;
+	if (wr)
+		return 100;
+	return 100;
+}
+
+static void hid_io_lihata_uninit(void)
+{
+}
+
+pcb_uninit_t hid_io_lihata_init(void)
+{
+
+	/* register the IO hook */
+	io_lihata.plugin_data = NULL;
+	io_lihata.fmt_support_prio = io_lihata_fmt;
+	io_lihata.parse_pcb = io_lihata_parse_pcb;
+	io_lihata.parse_element = NULL;
+	io_lihata.parse_font = NULL;
+	io_lihata.write_buffer = NULL;
+	io_lihata.write_element = NULL;
+	io_lihata.write_pcb = io_lihata_write_pcb;
+	io_lihata.default_fmt = "lihata";
+	io_lihata.description = "lihata board";
+	io_lihata.save_preference_prio = 20;
+
+	HOOK_REGISTER(plug_io_t, plug_io_chain, &io_lihata);
+
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_io_lihata, field,isarray,type_name,cpath,cname,desc,flags);
+#include "lht_conf_fields.h"
+
+	return hid_io_lihata_uninit;
+}
+
diff --git a/src_plugins/io_lihata/io_lihata.h b/src_plugins/io_lihata/io_lihata.h
new file mode 100644
index 0000000..d6e4c0a
--- /dev/null
+++ b/src_plugins/io_lihata/io_lihata.h
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "conf.h"
+#include "lht_conf.h"
+
+extern conf_io_lihata_t conf_io_lihata;
diff --git a/src_plugins/io_lihata/lht_conf.h b/src_plugins/io_lihata/lht_conf.h
new file mode 100644
index 0000000..c130a06
--- /dev/null
+++ b/src_plugins/io_lihata/lht_conf.h
@@ -0,0 +1,14 @@
+#ifndef PCB_IO_LIHATA_CONF_H
+#define PCB_IO_LIHATA_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct io_lihata {
+			CFT_STRING aux_pcb_pattern;  /* file name pattern to use when generating the .pcb backup */
+		} io_lihata;
+	} plugins;
+} conf_io_lihata_t;
+
+#endif
diff --git a/src_plugins/io_lihata/read.c b/src_plugins/io_lihata/read.c
new file mode 100644
index 0000000..cb06ba3
--- /dev/null
+++ b/src_plugins/io_lihata/read.c
@@ -0,0 +1,1009 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Load a lihata document in-memory and walk the tree and build pcb native
+   structs. A full dom load is used instead of the event parser so that
+   symlinks and tree merges can be supported later. */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <liblihata/tree.h>
+#include "global.h"
+#include "data.h"
+#include "plugins.h"
+#include "plug_io.h"
+#include "strflags.h"
+#include "compat_misc.h"
+#include "macro.h"
+#include "error.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "layer.h"
+#include "create.h"
+#include "vtptr.h"
+#include "common.h"
+#include "polygon.h"
+#include "conf_core.h"
+
+#warning TODO: put these in a gloal load-context-struct
+vtptr_t post_ids, post_thermal;
+
+/* Collect objects that has unknown ID on a list. Once all objects with
+   known-IDs are allocated, the unknonw-ID objects are allocated a fresh
+   ID. This makes sure they don't occupy IDs that would be used by known-ID
+   objects during the load. */
+#define post_id_req(obj) vtptr_append(&post_ids, &((obj)->ID))
+
+static int parse_attributes(AttributeListType *list, lht_node_t *nd)
+{
+	lht_node_t *n;
+	lht_dom_iterator_t it;
+
+	if (nd == NULL)
+		return 0;
+
+	if (nd->type != LHT_HASH)
+		return -1;
+
+	for(n = lht_dom_first(&it, nd); n != NULL; n = lht_dom_next(&it)) {
+		if (n->type == LHT_TEXT)
+			AttributePutToList(list, n->name, n->data.text.value, 0);
+	}
+
+	return 0;
+}
+
+/* Load the (duplicated) string value of a text node into res. Return 0 on success */
+static int parse_text(char **res, lht_node_t *nd)
+{
+	if ((nd == NULL) || (nd->type != LHT_TEXT))
+		return -1;
+	*res = pcb_strdup(nd->data.text.value);
+	return 0;
+}
+
+/* Load the Coord value of a text node into res. Return 0 on success */
+static int parse_coord(Coord *res, lht_node_t *nd)
+{
+	double tmp;
+	pcb_bool success;
+
+	if ((nd == NULL) || (nd->type != LHT_TEXT))
+		return -1;
+
+	tmp = GetValueEx(nd->data.text.value, NULL, NULL, NULL, NULL, &success);
+	if (!success) {
+		Message(PCB_MSG_ERROR, "#LHT1 Invalid coord value: '%s'\n", nd->data.text.value);
+		return -1;
+	}
+
+	*res = tmp;
+	return 0;
+}
+
+/* Load the Angle value of a text node into res. Return 0 on success */
+static int parse_angle(Angle *res, lht_node_t *nd)
+{
+	double tmp;
+	pcb_bool success;
+
+	if ((nd == NULL) || (nd->type != LHT_TEXT)) {
+		Message(PCB_MSG_ERROR, "#LHT2 Invalid angle type: '%d'\n", nd->type);
+		return -1;
+	}
+
+	tmp = GetValueEx(nd->data.text.value, NULL, NULL, NULL, NULL, &success);
+	if (!success) {
+		Message(PCB_MSG_ERROR, "#LHT3 Invalid angle value: '%s'\n", nd->data.text.value);
+		return -1;
+	}
+
+	*res = tmp;
+	return 0;
+}
+
+/* Load the Coord value of a text node into res. Return 0 on success */
+static int parse_int(int *res, lht_node_t *nd)
+{
+	long int tmp;
+	int base = 10;
+	char *end;
+
+	if (nd == NULL)
+		return -1;
+
+	if (nd->type != LHT_TEXT) {
+		Message(PCB_MSG_ERROR, "#LHT4 Invalid int type: '%d'\n", nd->type);
+		return -1;
+	}
+
+	if ((nd->data.text.value[0] == '0') && (nd->data.text.value[1] == 'x'))
+		base = 16;
+	tmp = strtol(nd->data.text.value, &end, base);
+	if (*end != '\0') {
+		Message(PCB_MSG_ERROR, "#LHT5 Invalid int value: '%s'\n", nd->data.text.value);
+		return -1;
+	}
+
+	*res = tmp;
+	return 0;
+}
+
+/* Load the duble value of a text node into res. Return 0 on success */
+static int parse_double(double *res, lht_node_t *nd)
+{
+	double tmp;
+	char *end;
+
+	if (nd == NULL)
+		return -1;
+
+	if (nd->type != LHT_TEXT) {
+		Message(PCB_MSG_ERROR, "#LHT8 Invalid double type: '%d'\n", nd->type);
+		return -1;
+	}
+
+	tmp = strtod(nd->data.text.value, &end);
+
+	if (*end != '\0') {
+		Message(PCB_MSG_ERROR, "#LHT9 Invalid double value: '%s'\n", nd->data.text.value);
+		return -1;
+	}
+
+	*res = tmp;
+	return 0;
+}
+
+/* Load the id name of a text node (with a prefixed name) into res.
+   Return 0 on success */
+static int parse_id(long int *res, lht_node_t *nd, int prefix_len)
+{
+	long int tmp;
+	char *end;
+
+	if (nd == NULL)
+		return -1;
+
+	tmp = strtol(nd->name + prefix_len, &end, 10);
+	if (*end != '\0') {
+		Message(PCB_MSG_ERROR, "#LHT6 Invalid id value: '%s' in line %d\n", nd->data.text.value, nd->line);
+		return -1;
+	}
+
+	CreateIDBump(tmp+1);
+
+	*res = tmp;
+	return 0;
+}
+
+/* Load the boolean value of a text node into res.
+   Return 0 on success */
+static int parse_bool(pcb_bool *res, lht_node_t *nd)
+{
+	if (nd == NULL)
+		return -1;
+
+	if ((strcmp(nd->data.text.value, "1") == 0) || (strcasecmp(nd->data.text.value, "on") == 0) ||
+	    (strcasecmp(nd->data.text.value, "true") == 0) || (strcasecmp(nd->data.text.value, "yes") == 0)) {
+		*res = 1;
+		return 0;
+	}
+
+	if ((strcmp(nd->data.text.value, "0") == 0) || (strcasecmp(nd->data.text.value, "off") == 0) ||
+	    (strcasecmp(nd->data.text.value, "false") == 0) || (strcasecmp(nd->data.text.value, "no") == 0)) {
+		*res = 0;
+		return 0;
+	}
+
+	Message(PCB_MSG_ERROR, "#LHT7 Invalid bool value: '%s'\n", nd->data.text.value);
+	return -1;
+}
+
+static int parse_meta(PCBType *pcb, lht_node_t *nd)
+{
+	lht_node_t *grp;
+
+	if (nd->type != LHT_HASH)
+		return -1;
+
+	parse_text(&pcb->Name, lht_dom_hash_get(nd, "meta"));
+
+	parse_text(&pcb->Name, lht_dom_hash_get(nd, "board_name"));
+
+	grp = lht_dom_hash_get(nd, "grid");
+	if ((grp != NULL) && (grp->type == LHT_HASH)) {
+		parse_coord(&pcb->GridOffsetX, lht_dom_hash_get(grp, "offs_x"));
+		parse_coord(&pcb->GridOffsetY, lht_dom_hash_get(grp, "offs_y"));
+		parse_coord(&pcb->Grid, lht_dom_hash_get(grp, "spacing"));
+	}
+
+	grp = lht_dom_hash_get(nd, "size");
+	if ((grp != NULL) && (grp->type == LHT_HASH)) {
+		parse_coord(&pcb->MaxWidth, lht_dom_hash_get(grp, "x"));
+		parse_coord(&pcb->MaxHeight, lht_dom_hash_get(grp, "y"));
+		parse_double(&pcb->IsleArea, lht_dom_hash_get(grp, "isle_area_nm2"));
+		parse_double(&pcb->ThermScale, lht_dom_hash_get(grp, "thermal_scale"));
+	}
+
+	grp = lht_dom_hash_get(nd, "drc");
+	if ((grp != NULL) && (grp->type == LHT_HASH)) {
+		parse_coord(&pcb->Bloat, lht_dom_hash_get(grp, "bloat"));
+		parse_coord(&pcb->Shrink, lht_dom_hash_get(grp, "shrink"));
+		parse_coord(&pcb->minWid, lht_dom_hash_get(grp, "min_width"));
+		parse_coord(&pcb->minSlk, lht_dom_hash_get(grp, "min_silk"));
+		parse_coord(&pcb->minDrill, lht_dom_hash_get(grp, "min_drill"));
+		parse_coord(&pcb->minRing, lht_dom_hash_get(grp, "min_ring"));
+	}
+
+	grp = lht_dom_hash_get(nd, "cursor");
+	if ((grp != NULL) && (grp->type == LHT_HASH)) {
+		parse_coord(&pcb->CursorX, lht_dom_hash_get(grp, "x"));
+		parse_coord(&pcb->CursorY, lht_dom_hash_get(grp, "y"));
+		parse_double(&pcb->Zoom, lht_dom_hash_get(grp, "zoom"));
+	}
+
+	return 0;
+}
+
+/* pt is a list of lihata node pointers to thermal nodes; each has user
+   data set to the flag. Look up layer info and fill in thermal flags. This
+   needs to be done in a separate pass at the end of parsing because
+   vias may precede layers in the lihata input file. */
+static int post_thermal_assign(vtptr_t *pt)
+{
+	int i;
+
+	for(i = 0; i < vtptr_len(pt); i++) {
+		lht_node_t *n;
+		lht_dom_iterator_t it;
+		io_lihata_flag_holder fh;
+		lht_node_t *thr = pt->array[i];
+		FlagType *f = thr->user_data;
+
+		memset(&fh, 0, sizeof(fh));
+		fh.Flags = *f;
+		for(n = lht_dom_first(&it, thr); n != NULL; n = lht_dom_next(&it)) {
+			if (n->type == LHT_TEXT) {
+				int layer = pcb_layer_by_name(n->name);
+				if (layer < 0) {
+					Message(PCB_MSG_ERROR, "#LHT10 Invalid layer name in thermal: '%s'\n", n->name);
+					return -1;
+				}
+				ASSIGN_THERM(layer, io_lihata_resolve_thermal_style(n->data.text.value), &fh);
+			}
+		}
+		*f = fh.Flags;
+	}
+	vtptr_uninit(pt);
+	return 0;
+}
+
+/* NOTE: in case of objects with thermal, f must point to the object's
+   flags because termals will be filled in at the end, in a 2nd pass and
+   we need to store the f pointer. */
+static int parse_flags(FlagType *f, lht_node_t *fn, int object_type)
+{
+	io_lihata_flag_holder fh;
+
+	memset(&fh, 0, sizeof(fh));
+
+	if (fn != NULL) {
+		int n;
+		lht_node_t *thr;
+		for (n = 0; n < pcb_object_flagbits_len; n++) {
+			if (pcb_object_flagbits[n].object_types & object_type) {
+				pcb_bool b;
+				if ((parse_bool(&b, lht_dom_hash_get(fn, pcb_object_flagbits[n].name)) == 0) && b)
+					SET_FLAG(pcb_object_flagbits[n].mask, &fh);
+			}
+		}
+
+		thr = lht_dom_hash_get(fn, "thermal");
+		if (thr != NULL) {
+			thr->user_data = f;
+			vtptr_append(&post_thermal, thr);
+		}
+
+		if (parse_int(&n, lht_dom_hash_get(fn, "shape")) == 0)
+			fh.Flags.q = n;
+
+		if (parse_int(&n, lht_dom_hash_get(fn, "intconn")) == 0)
+			fh.Flags.int_conn_grp = n;
+	}
+
+	*f = fh.Flags;
+	return 0;
+}
+
+
+static int parse_line(LayerType *ly, ElementType *el, lht_node_t *obj, int no_id, Coord dx, Coord dy)
+{
+	LineType *line;
+
+	if (ly != NULL)
+		line = GetLineMemory(ly);
+	else if (el != NULL)
+		line = GetElementLineMemory(el);
+	else
+		return -1;
+
+	if (no_id)
+		line->ID = 0;
+	else
+		parse_id(&line->ID, obj, 5);
+	parse_attributes(&line->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&line->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_LINE);
+
+	parse_coord(&line->Thickness, lht_dom_hash_get(obj, "thickness"));
+	parse_coord(&line->Clearance, lht_dom_hash_get(obj, "clearance"));
+	parse_coord(&line->Point1.X, lht_dom_hash_get(obj, "x1"));
+	parse_coord(&line->Point1.Y, lht_dom_hash_get(obj, "y1"));
+	parse_coord(&line->Point2.X, lht_dom_hash_get(obj, "x2"));
+	parse_coord(&line->Point2.Y, lht_dom_hash_get(obj, "y2"));
+
+	line->Point1.X += dx;
+	line->Point2.X += dx;
+	line->Point1.Y += dy;
+	line->Point2.Y += dy;
+
+	if (!no_id) {
+		post_id_req(&line->Point1);
+		post_id_req(&line->Point2);
+	}
+
+	if (ly != NULL)
+		pcb_add_line_on_layer(ly, line);
+
+	return 0;
+}
+
+static int parse_rat(DataType *dt, lht_node_t *obj)
+{
+	RatType rat, *new_rat;
+
+	parse_id(&rat.ID, obj, 4);
+	parse_attributes(&rat.Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&rat.Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_LINE);
+
+	parse_coord(&rat.Point1.X, lht_dom_hash_get(obj, "x1"));
+	parse_coord(&rat.Point1.Y, lht_dom_hash_get(obj, "y1"));
+	parse_coord(&rat.Point2.X, lht_dom_hash_get(obj, "x2"));
+	parse_coord(&rat.Point2.Y, lht_dom_hash_get(obj, "y2"));
+
+	parse_int(&rat.group1, lht_dom_hash_get(obj, "lgrp1"));
+	parse_int(&rat.group2, lht_dom_hash_get(obj, "lgrp2"));
+
+	post_id_req(&rat.Point1);
+	post_id_req(&rat.Point2);
+
+	new_rat = CreateNewRat(dt, rat.Point1.X, rat.Point1.Y, rat.Point2.X, rat.Point2.Y, rat.group1, rat.group2,
+		conf_core.appearance.rat_thickness, rat.Flags);
+
+	new_rat->ID = rat.ID;
+
+	return 0;
+}
+
+static int parse_arc(LayerType *ly, ElementType *el, lht_node_t *obj, Coord dx, Coord dy)
+{
+	ArcType *arc;
+
+	if (ly != NULL)
+		arc = GetArcMemory(ly);
+	else if (el != NULL)
+		arc = GetElementArcMemory(el);
+	else
+		return -1;
+
+	parse_id(&arc->ID, obj, 4);
+	parse_attributes(&arc->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&arc->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_ARC);
+
+	parse_coord(&arc->Thickness, lht_dom_hash_get(obj, "thickness"));
+	parse_coord(&arc->Clearance, lht_dom_hash_get(obj, "clearance"));
+	parse_coord(&arc->X, lht_dom_hash_get(obj, "x"));
+	parse_coord(&arc->Y, lht_dom_hash_get(obj, "y"));
+	parse_coord(&arc->Width, lht_dom_hash_get(obj, "width"));
+	parse_coord(&arc->Height, lht_dom_hash_get(obj, "height"));
+	parse_angle(&arc->StartAngle, lht_dom_hash_get(obj, "astart"));
+	parse_angle(&arc->Delta, lht_dom_hash_get(obj, "adelta"));
+
+	arc->X += dx;
+	arc->Y += dy;
+
+	if (ly != NULL)
+		pcb_add_arc_on_layer(ly, arc);
+
+	return 0;
+
+}
+
+static int parse_polygon(LayerType *ly, ElementType *el, lht_node_t *obj)
+{
+	PolygonType *poly = GetPolygonMemory(ly);
+	lht_node_t *geo;
+	pcb_cardinal_t n = 0, c;
+
+	parse_id(&poly->ID, obj, 8);
+	parse_attributes(&poly->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&poly->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_POLYGON);
+
+	geo = lht_dom_hash_get(obj, "geometry");
+	if ((geo != NULL) && (geo->type == LHT_LIST)) {
+		lht_node_t *cnt;
+		lht_dom_iterator_t it;
+
+		/* count points and holes */
+		poly->PointN = 0;
+		for(c = 0, cnt = lht_dom_first(&it, geo); cnt != NULL; c++, cnt = lht_dom_next(&it)) {
+			if (cnt->type != LHT_TABLE)
+				continue;
+			poly->PointN += cnt->data.table.rows;
+		}
+		poly->PointMax = poly->PointN;
+		poly->Points = malloc(sizeof(PointType) * poly->PointMax);
+		poly->HoleIndexMax = poly->HoleIndexN = c-1;
+		if (poly->HoleIndexN > 0)
+			poly->HoleIndex = malloc(sizeof(pcb_cardinal_t) * poly->HoleIndexMax);
+		else
+			poly->HoleIndex = NULL;
+
+		/* convert points and build hole index */
+		for(c = 0, cnt = lht_dom_first(&it, geo); cnt != NULL; c++, cnt = lht_dom_next(&it)) {
+			pcb_cardinal_t r;
+			if (cnt->type != LHT_TABLE)
+				continue;
+			if (c > 0)
+				poly->HoleIndex[c-1] = n;
+			for(r = 0; r < cnt->data.table.rows; r++) {
+				parse_coord(&poly->Points[n].X, cnt->data.table.r[r][0]);
+				parse_coord(&poly->Points[n].Y, cnt->data.table.r[r][1]);
+				n++;
+			}
+		}
+	}
+
+	pcb_add_polygon_on_layer(ly, poly);
+
+	return 0;
+}
+
+static int parse_pcb_text(LayerType *ly, ElementType *el, lht_node_t *obj)
+{
+	TextType *text;
+	lht_node_t *role;
+	int tmp;
+
+	role = lht_dom_hash_get(obj, "role");
+
+	if (ly != NULL) {
+		if (role != NULL)
+			return -1;
+		text = GetTextMemory(ly);
+	}
+	else if (el != NULL) {
+		if (role == NULL)
+			return -1;
+		if (strcmp(role->data.text.value, "desc") == 0) text = &DESCRIPTION_TEXT(el);
+		else if (strcmp(role->data.text.value, "name") == 0) text = &NAMEONPCB_TEXT(el);
+		else if (strcmp(role->data.text.value, "value") == 0) text = &VALUE_TEXT(el);
+		else
+			return -1;
+	}
+
+	parse_attributes(&text->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&text->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_TEXT);
+	parse_int(&text->Scale, lht_dom_hash_get(obj, "scale"));
+	parse_int(&tmp, lht_dom_hash_get(obj, "direction"));
+	text->Direction = tmp;
+	parse_coord(&text->X, lht_dom_hash_get(obj, "x"));
+	parse_coord(&text->Y, lht_dom_hash_get(obj, "y"));
+	parse_text(&text->TextString, lht_dom_hash_get(obj, "string"));
+
+#warning TODO: get the font
+	if (ly != NULL)
+		pcb_add_text_on_layer(ly, text, &PCB->Font);
+	if (el != NULL)
+		text->Element = el;
+
+	return 0;
+}
+
+static int parse_data_layer(PCBType *pcb, DataType *dt, lht_node_t *grp, int layer_id)
+{
+	lht_node_t *n, *lst;
+	lht_dom_iterator_t it;
+
+	LayerType *ly = &dt->Layer[dt->LayerN];
+	dt->LayerN++;
+
+	ly->Name = pcb_strdup(grp->name);
+	parse_bool(&ly->On, lht_dom_hash_get(grp, "visible"));
+	if (pcb != NULL) {
+		int grp_id;
+		parse_int(&grp_id, lht_dom_hash_get(grp, "group"));
+		pcb_layer_add_in_group(layer_id, grp_id);
+	}
+
+	lst = lht_dom_hash_get(grp, "objects");
+	if (lst != NULL) {
+		if (lst->type != LHT_LIST)
+			return -1;
+
+		for(n = lht_dom_first(&it, lst); n != NULL; n = lht_dom_next(&it)) {
+			if (strncmp(n->name, "line.", 5) == 0)
+				parse_line(ly, NULL, n, 0, 0, 0);
+			if (strncmp(n->name, "arc.", 4) == 0)
+				parse_arc(ly, NULL, n, 0, 0);
+			if (strncmp(n->name, "polygon.", 8) == 0)
+				parse_polygon(ly, NULL, n);
+			if (strncmp(n->name, "text.", 5) == 0)
+				parse_pcb_text(ly, NULL, n);
+		}
+	}
+
+	return 0;
+}
+
+static int parse_data_layers(PCBType *pcb, DataType *dt, lht_node_t *grp)
+{
+	int id;
+	lht_node_t *n;
+	lht_dom_iterator_t it;
+
+	for(id = 0, n = lht_dom_first(&it, grp); n != NULL; id++, n = lht_dom_next(&it))
+		if (n->type == LHT_HASH)
+			parse_data_layer(pcb, dt, n, id);
+
+	dt->LayerN -= 2; /* for the silk layers... */
+	return 0;
+}
+
+/* If el == NULL and dt != NULL it is a via (for now). */
+static int parse_pin(DataType *dt, ElementType *el, lht_node_t *obj, Coord dx, Coord dy)
+{
+	PinType *via;
+
+	if (dt != NULL)
+		via = GetViaMemory(dt);
+	else if (el != NULL)
+		via = GetPinMemory(el);
+	else
+		return -1;
+
+	parse_id(&via->ID, obj, 4);
+	parse_attributes(&via->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&via->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_VIA);
+
+	parse_coord(&via->Thickness, lht_dom_hash_get(obj, "thickness"));
+	parse_coord(&via->Clearance, lht_dom_hash_get(obj, "clearance"));
+	parse_coord(&via->Mask, lht_dom_hash_get(obj, "mask"));
+	parse_coord(&via->DrillingHole, lht_dom_hash_get(obj, "hole"));
+	parse_coord(&via->X, lht_dom_hash_get(obj, "x"));
+	parse_coord(&via->Y, lht_dom_hash_get(obj, "y"));
+	parse_text(&via->Name, lht_dom_hash_get(obj, "name"));
+	parse_text(&via->Number, lht_dom_hash_get(obj, "number"));
+
+	via->X += dx;
+	via->Y += dy;
+
+	if (dt != NULL)
+		pcb_add_via(dt, via);
+	if (el != NULL)
+		via->Element = el;
+
+	return 0;
+}
+
+static int parse_pad(ElementType *el, lht_node_t *obj, Coord dx, Coord dy)
+{
+	PadType *pad;
+
+	pad = GetPadMemory(el);
+
+	parse_id(&pad->ID, obj, 4);
+	parse_attributes(&pad->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&pad->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_PAD);
+
+	parse_coord(&pad->Thickness, lht_dom_hash_get(obj, "thickness"));
+	parse_coord(&pad->Clearance, lht_dom_hash_get(obj, "clearance"));
+	parse_coord(&pad->Mask, lht_dom_hash_get(obj, "mask"));
+	parse_coord(&pad->Point1.X, lht_dom_hash_get(obj, "x1"));
+	parse_coord(&pad->Point1.Y, lht_dom_hash_get(obj, "y1"));
+	parse_coord(&pad->Point2.X, lht_dom_hash_get(obj, "x2"));
+	parse_coord(&pad->Point2.Y, lht_dom_hash_get(obj, "y2"));
+	parse_text(&pad->Name, lht_dom_hash_get(obj, "name"));
+	parse_text(&pad->Number, lht_dom_hash_get(obj, "number"));
+
+	pad->Point1.X += dx;
+	pad->Point2.X += dx;
+	pad->Point1.Y += dy;
+	pad->Point2.Y += dy;
+
+	post_id_req(&pad->Point1);
+	post_id_req(&pad->Point2);
+	pad->Element = el;
+
+	return 0;
+}
+
+
+static int parse_element(PCBType *pcb, DataType *dt, lht_node_t *obj)
+{
+	ElementType *elem = GetElementMemory(dt);
+	lht_node_t *lst, *n;
+	lht_dom_iterator_t it;
+	int onsld;
+
+	parse_id(&elem->ID, obj, 8);
+	parse_attributes(&elem->Attributes, lht_dom_hash_get(obj, "attributes"));
+	parse_flags(&elem->Flags, lht_dom_hash_get(obj, "flags"), PCB_TYPE_ELEMENT);
+	parse_coord(&elem->MarkX, lht_dom_hash_get(obj, "x"));
+	parse_coord(&elem->MarkY, lht_dom_hash_get(obj, "y"));
+
+	onsld = TEST_FLAG(PCB_FLAG_ONSOLDER, elem);
+
+	lst = lht_dom_hash_get(obj, "objects");
+	if (lst->type == LHT_LIST) {
+		for(n = lht_dom_first(&it, lst); n != NULL; n = lht_dom_next(&it)) {
+			if (strncmp(n->name, "line.", 5) == 0)
+				parse_line(NULL, elem, n, 0, elem->MarkX, elem->MarkY);
+			if (strncmp(n->name, "arc.", 4) == 0)
+				parse_arc(NULL, elem, n, elem->MarkX, elem->MarkY);
+/*		if (strncmp(n->name, "polygon.", 8) == 0)
+			parse_polygon(ly, elem, n);*/
+			if (strncmp(n->name, "text.", 5) == 0)
+				parse_pcb_text(NULL, elem, n);
+			if (strncmp(n->name, "pin.", 4) == 0)
+				parse_pin(NULL, elem, n, elem->MarkX, elem->MarkY);
+			if (strncmp(n->name, "pad.", 4) == 0)
+				parse_pad(elem, n, elem->MarkX, elem->MarkY);
+		}
+	}
+
+	if (onsld) {
+		int n;
+		for(n = 0; n < MAX_ELEMENTNAMES; n++)
+			SET_FLAG(PCB_FLAG_ONSOLDER, &elem->Name[n]);
+	}
+
+	/* Make sure we use some sort of font */
+	if (pcb == NULL)
+		pcb = PCB;
+	SetElementBoundingBox(dt, elem, &pcb->Font);
+	return 0;
+}
+
+static int parse_data_objects(PCBType *pcb_for_font, DataType *dt, lht_node_t *grp)
+{
+	lht_node_t *n;
+	lht_dom_iterator_t it;
+
+	if (grp->type != LHT_LIST)
+		return -1;
+
+	for(n = lht_dom_first(&it, grp); n != NULL; n = lht_dom_next(&it)) {
+		if (strncmp(n->name, "via.", 4) == 0)
+			parse_pin(dt, NULL, n, 0, 0);
+		if (strncmp(n->name, "rat.", 4) == 0)
+			parse_rat(dt, n);
+		else if (strncmp(n->name, "element.", 8) == 0)
+			parse_element(pcb_for_font, dt, n);
+	}
+
+	return 0;
+}
+
+static DataType *parse_data(PCBType *pcb, lht_node_t *nd)
+{
+	DataType *dt;
+	lht_node_t *grp;
+	if (nd->type != LHT_HASH)
+		return NULL;
+
+	dt = calloc(sizeof(DataType), 1);
+
+	grp = lht_dom_hash_get(nd, "layers");
+	if ((grp != NULL) && (grp->type == LHT_LIST))
+		parse_data_layers(pcb, dt, grp);
+
+	grp = lht_dom_hash_get(nd, "objects");
+	if (grp != NULL)
+		parse_data_objects(pcb, dt, grp);
+
+	dt->pcb = pcb;
+
+
+	return dt;
+}
+
+static int parse_symbol(SymbolType *sym, lht_node_t *nd)
+{
+	lht_node_t *grp, *obj;
+	lht_dom_iterator_t it;
+
+	parse_coord(&sym->Width, lht_dom_hash_get(nd, "width"));
+	parse_coord(&sym->Height, lht_dom_hash_get(nd, "height"));
+	parse_coord(&sym->Delta, lht_dom_hash_get(nd, "delta"));
+
+	grp = lht_dom_hash_get(nd, "objects");
+	for(obj = lht_dom_first(&it, grp); obj != NULL; obj = lht_dom_next(&it)) {
+		Coord x1, y1, x2, y2, th;
+		parse_coord(&x1, lht_dom_hash_get(obj, "x1"));
+		parse_coord(&y1, lht_dom_hash_get(obj, "y1"));
+		parse_coord(&x2, lht_dom_hash_get(obj, "x2"));
+		parse_coord(&y2, lht_dom_hash_get(obj, "y2"));
+		parse_coord(&th, lht_dom_hash_get(obj, "thickness"));
+		CreateNewLineInSymbol(sym, x1, y1, x2, y2, th);
+	}
+
+	sym->Valid = 1;
+	return 0;
+}
+
+static int parse_font(FontType *font, lht_node_t *nd)
+{
+	lht_node_t *grp, *sym;
+	lht_dom_iterator_t it;
+	int n;
+
+	if (nd->type != LHT_HASH)
+		return -1;
+
+	/* TODO: support only one, hard-wired font for now */
+	nd = lht_dom_hash_get(nd, "geda_pcb");
+
+	parse_coord(&font->MaxHeight, lht_dom_hash_get(nd, "cell_height"));
+	parse_coord(&font->MaxWidth, lht_dom_hash_get(nd, "cell_width"));
+
+	grp = lht_dom_hash_get(nd, "symbols");
+
+	for(n = 0; n < sizeof(font->Symbol) / sizeof(font->Symbol[0]); n++) {
+		font->Symbol[n].LineN = 0;
+		font->Symbol[n].Valid = 0;
+	}
+
+	for(sym = lht_dom_first(&it, grp); sym != NULL; sym = lht_dom_next(&it)) {
+		int chr;
+		if (sym->type != LHT_HASH)
+			continue;
+		if (*sym->name == '&') {
+			char *end;
+			chr = strtol(sym->name+1, &end, 16);
+			if (*end != '\0') {
+				Message(PCB_MSG_ERROR, "Ignoring invalid symbol name '%s'.\n", sym->name);
+				continue;
+			}
+		}
+		else
+			chr = *sym->name;
+		if ((chr >= 0) && (chr < sizeof(font->Symbol) / sizeof(font->Symbol[0]))) {
+			parse_symbol(font->Symbol+chr, sym);
+		}
+	}
+
+	font->Valid = 1;
+	return 0;
+}
+
+static void post_ids_assign(vtptr_t *ids)
+{
+	int n;
+	for(n = 0; n < vtptr_len(ids); n++) {
+		long int *id = ids->array[n];
+		*id = CreateIDGet();
+	}
+	vtptr_uninit(ids);
+}
+
+static int parse_styles(vtroutestyle_t *styles, lht_node_t *nd)
+{
+	lht_node_t *stn;
+	lht_dom_iterator_t it;
+
+	if (nd->type != LHT_LIST)
+		return -1;
+
+	for(stn = lht_dom_first(&it, nd); stn != NULL; stn = lht_dom_next(&it)) {
+		RouteStyleType *s = vtroutestyle_alloc_append(styles, 1);
+		int name_len = strlen(stn->name);
+
+		/* safe copy the name */
+		if (name_len > sizeof(s->name)-1) {
+			Message(PCB_MSG_WARNING, "Route style name too long: '%s' (should be less than %d characters)\n", stn->name, sizeof(s->name)-1);
+			memcpy(s->name, stn->name, sizeof(s->name)-2);
+			s->name[sizeof(s->name)-1] = '\0';
+		}
+		else
+			memcpy(s->name, stn->name, name_len+1);
+
+		parse_coord(&s->Thick, lht_dom_hash_get(stn, "thickness"));
+		parse_coord(&s->Diameter, lht_dom_hash_get(stn, "diameter"));
+		parse_coord(&s->Hole, lht_dom_hash_get(stn, "hole"));
+		parse_coord(&s->Clearance, lht_dom_hash_get(stn, "clearance"));
+	}
+	return 0;
+}
+
+static int parse_netlist_input(LibraryType *lib, lht_node_t *netlist)
+{
+	lht_node_t *nnet;
+	if (netlist->type != LHT_LIST)
+		return -1;
+
+	for(nnet = netlist->data.list.first; nnet != NULL; nnet = nnet->next) {
+		lht_node_t *nconn, *nstyle, *nt;
+		LibraryMenuType *net;
+		const char *style = NULL;
+
+		if (nnet->type != LHT_HASH)
+			return -1;
+		nconn  = lht_dom_hash_get(nnet, "conn");
+		nstyle = lht_dom_hash_get(nnet, "style");
+
+		if ((nconn != NULL) && (nconn->type != LHT_LIST))
+			return -1;
+
+		if (nstyle != NULL) {
+			if (nstyle->type != LHT_TEXT)
+				return -1;
+			style = nstyle->data.text.value;
+		}
+
+		net = CreateNewNet(lib, nnet->name, style);
+		if (nconn != NULL) {
+			for(nt = nconn->data.list.first; nt != NULL; nt = nt->next) {
+				if ((nt->type != LHT_TEXT) || (*nt->data.text.value == '\0'))
+					return -1;
+				CreateNewConnection(net, nt->data.text.value);
+			}
+		}
+	}
+	return 0;
+}
+
+static int parse_netlist_patch(PCBType *pcb, lht_node_t *patches)
+{
+	lht_node_t *np;
+
+	if (patches->type != LHT_LIST)
+		return -1;
+
+	for(np = patches->data.list.first; np != NULL; np = np->next) {
+		lht_node_t *nnet, *nkey, *nval;
+		if (np->type != LHT_HASH)
+			return -1;
+		nnet = lht_dom_hash_get(np, "net");
+		if ((nnet == NULL) || (nnet->type != LHT_TEXT) || (*nnet->data.text.value == '\0'))
+			return -1;
+
+		if (strcmp(np->name, "del_conn") == 0) {
+			nval = lht_dom_hash_get(np, "term");
+			if ((nval == NULL) || (nval->type != LHT_TEXT) || (*nval->data.text.value == '\0'))
+				return -1;
+			rats_patch_append(pcb, RATP_ADD_CONN, nval->data.text.value, nnet->data.text.value, NULL);
+		}
+		else if (strcmp(np->name, "add_conn") == 0) {
+			nval = lht_dom_hash_get(np, "term");
+			if ((nval == NULL) || (nval->type != LHT_TEXT) || (*nval->data.text.value == '\0'))
+				return -1;
+			rats_patch_append(pcb, RATP_DEL_CONN, nval->data.text.value, nnet->data.text.value, NULL);
+		}
+		else if (strcmp(np->name, "change_attrib") == 0) {
+			nkey = lht_dom_hash_get(np, "key");
+			if ((nkey == NULL) || (nkey->type != LHT_TEXT) || (*nkey->data.text.value == '\0'))
+				return -1;
+			nval = lht_dom_hash_get(np, "term");
+			if ((nval == NULL) || (nval->type != LHT_TEXT))
+				return -1;
+			rats_patch_append(pcb, RATP_CHANGE_ATTRIB, nnet->data.text.value, nkey->data.text.value, nval->data.text.value);
+		}
+	}
+	return 0;
+}
+
+static int parse_netlists(PCBType *pcb, lht_node_t *netlists)
+{
+	lht_node_t *sub;
+
+	if (netlists->type != LHT_HASH)
+		return -1;
+
+	sub = lht_dom_hash_get(netlists, "input");
+	if ((sub != NULL) && (parse_netlist_input(pcb->NetlistLib+NETLIST_INPUT, sub) != 0))
+		return -1;
+
+	sub = lht_dom_hash_get(netlists, "netlist_patch");
+	if ((sub != NULL) && (parse_netlist_patch(pcb, sub) != 0))
+		return -1;
+
+	return 0;
+}
+
+static int parse_board(PCBType *pcb, lht_node_t *nd)
+{
+	lht_node_t *sub;
+
+	if ((nd->type != LHT_HASH) || (strcmp(nd->name, "pcb-rnd-board-v1"))) {
+		Message(PCB_MSG_DEFAULT, "Not a board lihata.\n");
+		return -1;
+	}
+
+	vtptr_init(&post_ids);
+	vtptr_init(&post_thermal);
+
+	memset(&pcb->LayerGroups, 0, sizeof(pcb->LayerGroups));
+
+	if (parse_attributes(&pcb->Attributes, lht_dom_hash_get(nd, "attributes")) != 0)
+		return -1;
+
+	sub = lht_dom_hash_get(nd, "meta");
+	if ((sub != NULL) && (parse_meta(pcb, sub) != 0))
+		return -1;
+
+	sub = lht_dom_hash_get(nd, "data");
+	if ((sub != NULL) && ((pcb->Data = parse_data(pcb, sub)) == NULL))
+		return -1;
+
+	sub = lht_dom_hash_get(nd, "font");
+	if ((sub != NULL) && (parse_font(&pcb->Font, sub) != 0))
+		return -1;
+
+	sub = lht_dom_hash_get(nd, "styles");
+	if ((sub != NULL) && (parse_styles(&pcb->RouteStyle, sub) != 0))
+		return -1;
+
+	sub = lht_dom_hash_get(nd, "netlists");
+	if ((sub != NULL) && (parse_netlists(pcb, sub) != 0))
+		return -1;
+
+	post_ids_assign(&post_ids);
+	if (post_thermal_assign(&post_thermal) != 0)
+		return -1;
+
+	/* Run poly clipping at the end so we have all IDs and we can
+	   announce the clipping (it's slow, we may need a progress bar) */
+	ALLPOLYGON_LOOP(pcb->Data);
+	{
+		InitClip(pcb->Data, layer, polygon);
+	}
+	ENDALL_LOOP;
+
+	return 0;
+}
+
+int io_lihata_parse_pcb(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest)
+{
+	int res;
+	char *errmsg;
+	lht_doc_t *doc = lht_dom_load(Filename, &errmsg);
+
+	if (doc == NULL) {
+/*		Message(PCB_MSG_DEFAULT, "Error loading '%s': %s\n", Filename, errmsg);*/
+		return -1;
+	}
+
+	res = parse_board(Ptr, doc->root);
+	lht_dom_uninit(doc);
+	return res;
+}
diff --git a/src_plugins/io_lihata/read.h b/src_plugins/io_lihata/read.h
new file mode 100644
index 0000000..7bea513
--- /dev/null
+++ b/src_plugins/io_lihata/read.h
@@ -0,0 +1,25 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+int io_lihata_parse_pcb(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest);
+
+
diff --git a/src_plugins/io_lihata/write.c b/src_plugins/io_lihata/write.c
new file mode 100644
index 0000000..12a8175
--- /dev/null
+++ b/src_plugins/io_lihata/write.c
@@ -0,0 +1,802 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Build an in-memory lihata document that represents the board then save it.
+   A document is built for the merge-save. */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <liblihata/tree.h>
+#include "global.h"
+#include "data.h"
+#include "plugins.h"
+#include "plug_io.h"
+#include "strflags.h"
+#include "compat_misc.h"
+#include "rats_patch.h"
+#include "hid_actions.h"
+#include "misc_util.h"
+#include "macro.h"
+#include "layer.h"
+#include "common.h"
+#include "write_style.h"
+#include "io_lihata.h"
+
+/*#define CFMT "%[9]"*/
+#define CFMT "%.08$$mH"
+/*#define CFMT "%$$mn"*/
+
+static int io_lihata_full_tree = 0;
+
+/* An invalid node will kill any existing node on an overwrite-save-merge */
+static lht_node_t *dummy_node(const char *name)
+{
+	lht_node_t *n;
+	n = lht_dom_node_alloc(LHT_TEXT, "attributes");
+	n->type = LHT_INVALID_TYPE;
+	return n;
+}
+
+static lht_node_t *dummy_text_node(const char *name)
+{
+	if (io_lihata_full_tree) {
+		lht_node_t *n = lht_dom_node_alloc(LHT_TEXT, name);
+		n->data.text.value = pcb_strdup("");
+		return n;
+	}
+	return dummy_node(name);
+}
+
+static lht_node_t *build_text(const char *key, const char *value)
+{
+	lht_node_t *field;
+
+	if (value == NULL)
+		return dummy_text_node(key);
+
+	field = lht_dom_node_alloc(LHT_TEXT, key);
+	if (value != NULL)
+		field->data.text.value = pcb_strdup(value);
+	return field;
+}
+
+static lht_node_t *build_textf(const char *key, const char *fmt, ...)
+{
+	lht_node_t *field;
+	va_list ap;
+
+	field = lht_dom_node_alloc(LHT_TEXT, key);
+	va_start(ap, fmt);
+	field->data.text.value = pcb_strdup_vprintf(fmt, ap);
+	va_end(ap);
+	return field;
+}
+
+static lht_node_t *build_board_meta(PCBType *pcb)
+{
+	lht_node_t *meta, *grp;
+
+	meta = lht_dom_node_alloc(LHT_HASH, "meta");
+	lht_dom_hash_put(meta, build_text("board_name", pcb->Name));
+
+	grp = lht_dom_node_alloc(LHT_HASH, "grid");
+	lht_dom_hash_put(meta, grp);
+	lht_dom_hash_put(grp, build_textf("offs_x", CFMT, pcb->GridOffsetX));
+	lht_dom_hash_put(grp, build_textf("offs_y", CFMT, pcb->GridOffsetY));
+	lht_dom_hash_put(grp, build_textf("spacing", CFMT, pcb->Grid));
+
+	grp = lht_dom_node_alloc(LHT_HASH, "size");
+	lht_dom_hash_put(meta, grp);
+	lht_dom_hash_put(grp, build_textf("x", CFMT, pcb->MaxWidth));
+	lht_dom_hash_put(grp, build_textf("y", CFMT, pcb->MaxHeight));
+	lht_dom_hash_put(grp, build_textf("isle_area_nm2", "%f", pcb->IsleArea));
+	lht_dom_hash_put(grp, build_textf("thermal_scale", "%f", pcb->ThermScale));
+
+
+	grp = lht_dom_node_alloc(LHT_HASH, "drc");
+	lht_dom_hash_put(meta, grp);
+	lht_dom_hash_put(grp, build_textf("bloat",     CFMT, pcb->Bloat));
+	lht_dom_hash_put(grp, build_textf("shrink",    CFMT, pcb->Shrink));
+	lht_dom_hash_put(grp, build_textf("min_width", CFMT, pcb->minWid));
+	lht_dom_hash_put(grp, build_textf("min_silk",  CFMT, pcb->minSlk));
+	lht_dom_hash_put(grp, build_textf("min_drill", CFMT, pcb->minDrill));
+	lht_dom_hash_put(grp, build_textf("min_ring",  CFMT, pcb->minRing));
+
+	grp = lht_dom_node_alloc(LHT_HASH, "cursor");
+	lht_dom_hash_put(meta, grp);
+	lht_dom_hash_put(grp, build_textf("x", CFMT, pcb->CursorX));
+	lht_dom_hash_put(grp, build_textf("y", CFMT, pcb->CursorY));
+	lht_dom_hash_put(grp, build_textf("zoom", "%.6f", pcb->Zoom));
+
+	return meta;
+}
+
+static lht_node_t *build_attributes(AttributeListType *lst)
+{
+	int n;
+	lht_node_t *ln;
+
+	if ((lst->Number == 0) && (!io_lihata_full_tree))
+		return dummy_node("attributes");
+
+	ln = lht_dom_node_alloc(LHT_HASH, "attributes");
+
+	for (n = 0; n < lst->Number; n++)
+		lht_dom_hash_put(ln, build_text(lst->List[n].name, lst->List[n].value));
+
+	return ln;
+}
+
+static lht_node_t *build_flags(FlagType *f, int object_type)
+{
+	int n, layer, added = 0, thrm = 0;
+	lht_node_t *hsh, *txt, *lst;
+	io_lihata_flag_holder fh;
+
+
+	fh.Flags = *f;
+
+	hsh = lht_dom_node_alloc(LHT_HASH, "flags");
+
+	/* create normal flag nodes */
+	for (n = 0; n < pcb_object_flagbits_len; n++) {
+		if ((pcb_object_flagbits[n].object_types & object_type) && (TEST_FLAG(pcb_object_flagbits[n].mask, &fh))) {
+			lht_dom_hash_put(hsh, build_text(pcb_object_flagbits[n].name, "1"));
+			CLEAR_FLAG(pcb_object_flagbits[n].mask, &fh);
+			added++;
+		}
+	}
+
+	/* thermal flags per layer */
+	lst = lht_dom_node_alloc(LHT_HASH, "thermal");
+
+	for(layer = 0; layer < max_copper_layer; layer++) {
+		if (TEST_ANY_THERMS(&fh)) {
+			int t = GET_THERM(layer, &fh);
+			if (t != 0) {
+				const char *name;
+				txt = lht_dom_node_alloc(LHT_TEXT, PCB->Data->Layer[layer].Name);
+				name = io_lihata_thermal_style(t);
+				if (name != NULL)
+					txt->data.text.value = pcb_strdup(name);
+				else
+					txt->data.text.value = pcb_strdup_printf("%d", t);
+				lht_dom_hash_put(lst, txt);
+				added++;
+				thrm++;
+			}
+		}
+	}
+
+	if (thrm > 0)
+		lht_dom_hash_put(hsh, lst);
+	else
+		lht_dom_node_free(lst);
+
+	if (f->q > 0) {
+		lht_dom_hash_put(hsh, build_textf("shape", "%d", f->q));
+		added++;
+	}
+
+	if (f->int_conn_grp > 0) {
+		lht_dom_hash_put(hsh, build_textf("intconn", "%d", f->int_conn_grp));
+		added++;
+	}
+
+	if ((added == 0) && (!io_lihata_full_tree)) {
+		lht_dom_node_free(hsh);
+		return dummy_node("flags");
+	}
+
+	return hsh;
+}
+
+static lht_node_t *build_line(LineType *line, int local_id, Coord dx, Coord dy)
+{
+	char buff[128];
+	lht_node_t *obj;
+
+	sprintf(buff, "line.%ld", (local_id >= 0 ? local_id : line->ID));
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&line->Attributes));
+	lht_dom_hash_put(obj, build_flags(&line->Flags, PCB_TYPE_LINE));
+	lht_dom_hash_put(obj, build_textf("thickness", CFMT, line->Thickness));
+	lht_dom_hash_put(obj, build_textf("clearance", CFMT, line->Clearance));
+	lht_dom_hash_put(obj, build_textf("x1", CFMT, line->Point1.X+dx));
+	lht_dom_hash_put(obj, build_textf("y1", CFMT, line->Point1.Y+dy));
+	lht_dom_hash_put(obj, build_textf("x2", CFMT, line->Point2.X+dx));
+	lht_dom_hash_put(obj, build_textf("y2", CFMT, line->Point2.Y+dy));
+
+	return obj;
+}
+
+static lht_node_t *build_rat(RatType *rat)
+{
+	char buff[128];
+	lht_node_t *obj;
+	sprintf(buff, "rat.%ld", rat->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&rat->Attributes));
+	lht_dom_hash_put(obj, build_flags(&rat->Flags, PCB_TYPE_LINE));
+	lht_dom_hash_put(obj, build_textf("x1", CFMT, rat->Point1.X));
+	lht_dom_hash_put(obj, build_textf("y1", CFMT, rat->Point1.Y));
+	lht_dom_hash_put(obj, build_textf("x2", CFMT, rat->Point2.X));
+	lht_dom_hash_put(obj, build_textf("y2", CFMT, rat->Point2.Y));
+	lht_dom_hash_put(obj, build_textf("lgrp1", "%d", rat->group1));
+	lht_dom_hash_put(obj, build_textf("lgrp2", "%d", rat->group2));
+
+	return obj;
+}
+
+static lht_node_t *build_arc(ArcType *arc, Coord dx, Coord dy)
+{
+	char buff[128];
+	lht_node_t *obj;
+
+	sprintf(buff, "arc.%ld", arc->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&arc->Attributes));
+	lht_dom_hash_put(obj, build_flags(&arc->Flags, PCB_TYPE_ARC));
+	lht_dom_hash_put(obj, build_textf("thickness", CFMT, arc->Thickness));
+	lht_dom_hash_put(obj, build_textf("clearance", CFMT, arc->Clearance));
+	lht_dom_hash_put(obj, build_textf("x", CFMT, arc->X+dx));
+	lht_dom_hash_put(obj, build_textf("y", CFMT, arc->Y+dy));
+	lht_dom_hash_put(obj, build_textf("width", CFMT, arc->Width));
+	lht_dom_hash_put(obj, build_textf("height", CFMT, arc->Height));
+	lht_dom_hash_put(obj, build_textf("astart", "%ma", arc->StartAngle));
+	lht_dom_hash_put(obj, build_textf("adelta", "%ma", arc->Delta));
+
+	return obj;
+}
+
+static lht_node_t *build_pin(PinType *pin, int is_via, Coord dx, Coord dy)
+{
+	char buff[128];
+	lht_node_t *obj;
+
+	sprintf(buff, "%s.%ld", is_via ? "via" : "pin", pin->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&pin->Attributes));
+	lht_dom_hash_put(obj, build_flags(&pin->Flags, PCB_TYPE_VIA));
+	lht_dom_hash_put(obj, build_textf("thickness", CFMT, pin->Thickness));
+	lht_dom_hash_put(obj, build_textf("clearance", CFMT, pin->Clearance));
+	lht_dom_hash_put(obj, build_textf("mask", CFMT, pin->Mask));
+	lht_dom_hash_put(obj, build_textf("hole", CFMT, pin->DrillingHole));
+	lht_dom_hash_put(obj, build_textf("x", CFMT, pin->X+dx));
+	lht_dom_hash_put(obj, build_textf("y", CFMT, pin->Y+dy));
+	lht_dom_hash_put(obj, build_text("name", pin->Name));
+	lht_dom_hash_put(obj, build_text("number", pin->Number));
+	return obj;
+}
+
+static lht_node_t *build_pad(PadType *pad, Coord dx, Coord dy)
+{
+	char buff[128];
+	lht_node_t *obj;
+
+	sprintf(buff, "pad.%ld", pad->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&pad->Attributes));
+	lht_dom_hash_put(obj, build_flags(&pad->Flags, PCB_TYPE_PAD));
+	lht_dom_hash_put(obj, build_textf("thickness", CFMT, pad->Thickness));
+	lht_dom_hash_put(obj, build_textf("clearance", CFMT, pad->Clearance));
+	lht_dom_hash_put(obj, build_textf("mask", CFMT, pad->Mask));
+	lht_dom_hash_put(obj, build_textf("x1", CFMT, pad->Point1.X+dx));
+	lht_dom_hash_put(obj, build_textf("y1", CFMT, pad->Point1.Y+dy));
+	lht_dom_hash_put(obj, build_textf("x2", CFMT, pad->Point2.X+dx));
+	lht_dom_hash_put(obj, build_textf("y2", CFMT, pad->Point2.Y+dy));
+	lht_dom_hash_put(obj, build_text("name", pad->Name));
+	lht_dom_hash_put(obj, build_text("number", pad->Number));
+	return obj;
+}
+
+static lht_node_t *build_polygon(PolygonType *poly)
+{
+	char buff[128];
+	lht_node_t *obj, *tbl, *geo;
+	pcb_cardinal_t n, hole = 0;
+
+	sprintf(buff, "polygon.%ld", poly->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&poly->Attributes));
+	lht_dom_hash_put(obj, build_flags(&poly->Flags, PCB_TYPE_POLYGON));
+
+	geo = lht_dom_node_alloc(LHT_LIST, "geometry");
+	lht_dom_hash_put(obj, geo);
+
+	tbl = lht_dom_node_alloc(LHT_TABLE, "contour");
+	tbl->data.table.cols = 2;
+	lht_dom_list_append(geo, tbl);
+
+	for(n = 0; n < poly->PointN; n++) {
+		int row;
+		if ((hole < poly->HoleIndexN) && (n == poly->HoleIndex[hole])) {
+			tbl = lht_dom_node_alloc(LHT_TABLE, "hole");
+			tbl->data.table.cols = 2;
+			lht_dom_list_append(geo, tbl);
+			hole++;
+		}
+
+		row = tbl->data.table.rows;
+		lht_tree_table_ins_row(tbl, row);
+		tbl->data.table.r[row][0] = build_textf(NULL, CFMT, poly->Points[n].X);
+		tbl->data.table.r[row][1] = build_textf(NULL, CFMT, poly->Points[n].Y);
+	}
+
+	return obj;
+}
+
+static lht_node_t *build_pcb_text(const char *role, TextType *text)
+{
+	char buff[128];
+	lht_node_t *obj;
+
+	sprintf(buff, "text.%ld", text->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&text->Attributes));
+	lht_dom_hash_put(obj, build_flags(&text->Flags, PCB_TYPE_TEXT));
+	lht_dom_hash_put(obj, build_text("string", text->TextString));
+	lht_dom_hash_put(obj, build_textf("scale", "%d", text->Scale));
+	lht_dom_hash_put(obj, build_textf("direction", "%d", text->Direction));
+	lht_dom_hash_put(obj, build_textf("x", CFMT, text->X));
+	lht_dom_hash_put(obj, build_textf("y", CFMT, text->Y));
+
+	if (role != NULL)
+		lht_dom_hash_put(obj, build_text("role", role));
+
+	return obj;
+}
+
+static lht_node_t *build_element(ElementType *elem)
+{
+	char buff[128];
+	LineType *li;
+	ArcType *ar;
+	PinType *pi;
+	PadType *pa;
+	lht_node_t *obj, *lst;
+
+	sprintf(buff, "element.%ld", elem->ID);
+	obj = lht_dom_node_alloc(LHT_HASH, buff);
+
+	lht_dom_hash_put(obj, build_attributes(&elem->Attributes));
+	lht_dom_hash_put(obj, build_flags(&elem->Flags, PCB_TYPE_ELEMENT));
+
+
+	/* build drawing primitives */
+	lst = lht_dom_node_alloc(LHT_LIST, "objects");
+	lht_dom_hash_put(obj, lst);
+
+	lht_dom_list_append(lst, build_pcb_text("desc", &elem->Name[DESCRIPTION_INDEX]));
+	lht_dom_list_append(lst, build_pcb_text("name", &elem->Name[NAMEONPCB_INDEX]));
+	lht_dom_list_append(lst, build_pcb_text("value", &elem->Name[VALUE_INDEX]));
+	lht_dom_hash_put(obj, build_textf("x", CFMT, elem->MarkX));
+	lht_dom_hash_put(obj, build_textf("y", CFMT, elem->MarkY));
+
+
+	for(li = linelist_first(&elem->Line); li != NULL; li = linelist_next(li))
+		lht_dom_list_append(lst, build_line(li, -1, -elem->MarkX, -elem->MarkY));
+
+	for(ar = arclist_first(&elem->Arc); ar != NULL; ar = arclist_next(ar))
+		lht_dom_list_append(lst, build_arc(ar, -elem->MarkX, -elem->MarkY));
+
+	for(pi = pinlist_first(&elem->Pin); pi != NULL; pi = pinlist_next(pi))
+		lht_dom_list_append(lst, build_pin(pi, 0, -elem->MarkX, -elem->MarkY));
+
+	for(pa = padlist_first(&elem->Pad); pa != NULL; pa = padlist_next(pa))
+		lht_dom_list_append(lst, build_pad(pa, -elem->MarkX, -elem->MarkY));
+
+	return obj;
+}
+
+
+static lht_node_t *build_data_layer(DataType *data, LayerType *layer, int layer_group)
+{
+	lht_node_t *obj, *grp;
+	LineType *li;
+	ArcType *ar;
+	PolygonType *po;
+	TextType *tx;
+	char tmp[16];
+	int added = 0;
+
+	obj = lht_dom_node_alloc(LHT_HASH, layer->Name);
+
+	lht_dom_hash_put(obj, build_text("visible", layer->On ? "1" : "0"));
+	lht_dom_hash_put(obj, build_attributes(&layer->Attributes));
+	sprintf(tmp, "%d", layer_group);
+	lht_dom_hash_put(obj, build_text("group", tmp));
+
+	grp = lht_dom_node_alloc(LHT_LIST, "objects");
+
+	for(li = linelist_first(&layer->Line); li != NULL; li = linelist_next(li)) {
+		lht_dom_list_append(grp, build_line(li, -1, 0, 0));
+		added++;
+	}
+
+	for(ar = arclist_first(&layer->Arc); ar != NULL; ar = arclist_next(ar)) {
+		lht_dom_list_append(grp, build_arc(ar, 0, 0));
+		added++;
+	}
+
+	for(po = polylist_first(&layer->Polygon); po != NULL; po = polylist_next(po)) {
+		lht_dom_list_append(grp, build_polygon(po));
+		added++;
+	}
+
+	for(tx = textlist_first(&layer->Text); tx != NULL; tx = textlist_next(tx)) {
+		lht_dom_list_append(grp, build_pcb_text(NULL, tx));
+		added++;
+	}
+
+	if ((added == 0) && (!io_lihata_full_tree)) {
+		lht_dom_node_free(grp);
+		grp = dummy_node("objects");
+	}
+
+	lht_dom_hash_put(obj, grp);
+
+	return obj;
+}
+
+static lht_node_t *build_data_layers(DataType *data)
+{
+	int n;
+	lht_node_t *layers;
+
+	layers = lht_dom_node_alloc(LHT_LIST, "layers");
+
+	for(n = 0; n < max_copper_layer + 2; n++)
+		lht_dom_list_append(layers, build_data_layer(data, data->Layer+n, pcb_layer_lookup_group(n)));
+
+	return layers;
+}
+
+
+static lht_node_t *build_data(DataType *data)
+{
+	lht_node_t *grp, *ndt;
+	PinType *pi;
+	ElementType *el;
+	gdl_iterator_t it;
+	RatType *line;
+
+	ndt = lht_dom_node_alloc(LHT_HASH, "data");
+
+	/* build layers */
+	lht_dom_hash_put(ndt, build_data_layers(data));
+
+	/* build a list of all global objects */
+	grp = lht_dom_node_alloc(LHT_LIST, "objects");
+	lht_dom_hash_put(ndt, grp);
+
+	for(pi = pinlist_first(&data->Via); pi != NULL; pi = pinlist_next(pi))
+		lht_dom_list_append(grp, build_pin(pi, 1, 0, 0));
+
+	for(el = elementlist_first(&data->Element); el != NULL; el = elementlist_next(el))
+		lht_dom_list_append(grp, build_element(el));
+
+	ratlist_foreach(&data->Rat, &it, line)
+		lht_dom_list_append(grp, build_rat(line));
+
+	return ndt;
+}
+
+static lht_node_t *build_symbol(SymbolType *sym, const char *name)
+{
+	lht_node_t *lst, *ndt;
+	LineType *li;
+	int n;
+
+	ndt = lht_dom_node_alloc(LHT_HASH, name);
+	lht_dom_hash_put(ndt, build_textf("width", CFMT, sym->Width));
+	lht_dom_hash_put(ndt, build_textf("height", CFMT, sym->Height));
+	lht_dom_hash_put(ndt, build_textf("delta", CFMT, sym->Delta));
+
+	lst = lht_dom_node_alloc(LHT_LIST, "objects");
+	lht_dom_hash_put(ndt, lst);
+	for(n = 0, li = sym->Line; n < sym->LineN; n++, li++)
+		lht_dom_list_append(lst, build_line(li, n, 0, 0));
+
+
+	return ndt;
+}
+
+static lht_node_t *build_font(FontType *font)
+{
+	lht_node_t *syms, *ndt, *frt;
+	int n;
+
+	frt = lht_dom_node_alloc(LHT_HASH, "font");
+
+	/* TODO: support only one, hard-wired font for now but make room for extensions */
+	ndt = lht_dom_node_alloc(LHT_HASH, "geda_pcb");
+	lht_dom_hash_put(frt, ndt);
+
+	lht_dom_hash_put(ndt, build_textf("cell_height", CFMT, font->MaxHeight));
+	lht_dom_hash_put(ndt, build_textf("cell_width", CFMT, font->MaxWidth));
+/*	lht_dom_hash_put(ndt, build_symbol(&font->DefaultSymbol)); */
+
+
+	syms = lht_dom_node_alloc(LHT_HASH, "symbols");
+	lht_dom_hash_put(ndt, syms);
+	for(n = 0; n < MAX_FONTPOSITION + 1; n++) {
+		char sname[32];
+		if (!font->Symbol[n].Valid)
+			continue;
+		if ((n <= 32) || (n > 126) || (n == '&') || (n == '#') || (n == '{') || (n == '}') || (n == '/') || (n == ':') || (n == ';') || (n == '=') || (n == '\\') || (n == ':')) {
+			sprintf(sname, "&%02x", n);
+		}
+		else {
+			sname[0] = n;
+			sname[1] = '\0';
+		}
+		lht_dom_hash_put(syms, build_symbol(&font->Symbol[n], sname));
+	}
+	return frt;
+}
+
+static lht_node_t *build_styles(vtroutestyle_t *styles)
+{
+	lht_node_t *stl, *sn;
+	int n;
+
+	stl = lht_dom_node_alloc(LHT_LIST, "styles");
+	for(n = 0; n < vtroutestyle_len(styles); n++) {
+		RouteStyleType *s = styles->array + n;
+		sn = lht_dom_node_alloc(LHT_HASH, s->name);
+		lht_dom_list_append(stl, sn);
+		lht_dom_hash_put(sn, build_textf("thickness", CFMT, s->Thick));
+		lht_dom_hash_put(sn, build_textf("diameter", CFMT, s->Diameter));
+		lht_dom_hash_put(sn, build_textf("hole", CFMT, s->Hole));
+		lht_dom_hash_put(sn, build_textf("clearance", CFMT, s->Clearance));
+	}
+	return stl;
+}
+
+/* Build a plain old netlist */
+static lht_node_t *build_netlist(LibraryType *netlist, const char *name, int *nonempty)
+{
+	lht_node_t *nl, *pl, *pn, *nnet;
+	pcb_cardinal_t n, p;
+
+	if (netlist->MenuN < 0)
+		return dummy_node(name);
+
+	(*nonempty)++;
+
+	nl = lht_dom_node_alloc(LHT_LIST, name);
+	for (n = 0; n < netlist->MenuN; n++) {
+		LibraryMenuTypePtr menu = &netlist->Menu[n];
+		const char *netname = &menu->Name[2];
+		const char *style = menu->Style;
+
+		/* create the net hash */
+		nnet = lht_dom_node_alloc(LHT_HASH, netname);
+		pl = lht_dom_node_alloc(LHT_LIST, "conn");
+		lht_dom_hash_put(nnet, pl);
+		if ((style != NULL) && (*style == '\0')) style = NULL;
+		lht_dom_hash_put(nnet, build_text("style", style));
+
+		/* grow the connection list */
+		for (p = 0; p < menu->EntryN; p++) {
+			LibraryEntryTypePtr entry = &menu->Entry[p];
+			const char *pin = entry->ListEntry;
+			pn = lht_dom_node_alloc(LHT_TEXT, "");
+			pn->data.text.value = pcb_strdup(pin);
+			lht_dom_list_append(pl, pn);
+		}
+		lht_dom_list_append(nl, nnet);
+	}
+	return nl;
+}
+
+typedef struct {
+	lht_node_t *patch, *info;
+} build_net_patch_t;
+
+static void build_net_patch_cb(void *ctx_, pcb_rats_patch_export_ev_t ev, const char *netn, const char *key, const char *val)
+{
+	build_net_patch_t *ctx = ctx_;
+	lht_node_t *n;
+	switch(ev) {
+		case PCB_RPE_INFO_BEGIN:
+			ctx->info = lht_dom_node_alloc(LHT_LIST, "net_info");
+			lht_dom_list_append(ctx->info, build_text("net", netn));
+			break;
+		case PCB_RPE_INFO_TERMINAL:
+			lht_dom_list_append(ctx->info, build_text("term", val));
+			break;
+		case PCB_RPE_INFO_END:
+			lht_dom_list_append(ctx->patch, ctx->info);
+			ctx->info = NULL;
+			break;
+		case PCB_RPE_CONN_ADD:
+			n = lht_dom_node_alloc(LHT_HASH, "add_conn");
+			goto append_term;
+
+		case PCB_RPE_CONN_DEL:
+			n = lht_dom_node_alloc(LHT_HASH, "del_conn");
+			append_term:;
+			lht_dom_hash_put(n, build_text("net", netn));
+			lht_dom_hash_put(n, build_text("term", val));
+			lht_dom_list_append(ctx->patch, n);
+			break;
+
+		case PCB_RPE_ATTR_CHG:
+			n = lht_dom_node_alloc(LHT_HASH, "change_attrib");
+			lht_dom_hash_put(n, build_text("net", netn));
+			lht_dom_hash_put(n, build_text("key", key));
+			lht_dom_hash_put(n, build_text("val", val));
+			lht_dom_list_append(ctx->patch, n);
+			break;
+	}
+}
+
+/* Build a netlist patch so that we don't need to export a complete new set of "as built" netlist */
+static lht_node_t *build_net_patch(PCBType *pcb, rats_patch_line_t *pat, int *nonempty)
+{
+	lht_node_t *pn;
+	build_net_patch_t ctx;
+
+	pn = lht_dom_node_alloc(LHT_LIST, "netlist_patch");
+
+	ctx.patch = pn;
+	rats_patch_export(pcb, pat, pcb_false, build_net_patch_cb, &ctx);
+
+	if (pn->data.list.first == NULL) {
+		lht_dom_node_free(pn);
+		return dummy_node("netlist_patch");
+	}
+
+	(*nonempty)++;
+	return pn;
+}
+
+
+static lht_node_t *build_netlists(PCBType *pcb, LibraryType *netlists, rats_patch_line_t *pat, int num_netlists)
+{
+	lht_node_t *nls;
+	int n, nonempty = 0;
+
+	if (num_netlists > NUM_NETLISTS)
+		return dummy_node("netlists");
+
+	nls = lht_dom_node_alloc(LHT_HASH, "netlists");
+
+	for(n = 0; n < num_netlists; n++) {
+		lht_node_t *nl;
+		if (n == NETLIST_EDITED)
+			nl = build_net_patch(pcb, pat, &nonempty);
+		else
+			nl = build_netlist(netlists+n, pcb_netlist_names[n], &nonempty);
+		lht_dom_hash_put(nls, nl);
+	}
+
+	if (!nonempty) {
+		lht_dom_node_free(nls);
+		return dummy_node("netlists");
+	}
+
+	return nls;
+}
+
+
+static lht_doc_t *build_board(PCBType *pcb)
+{
+	lht_doc_t *brd = lht_dom_init();
+
+	brd->root = lht_dom_node_alloc(LHT_HASH, "pcb-rnd-board-v1");
+	lht_dom_hash_put(brd->root, build_board_meta(pcb));
+	lht_dom_hash_put(brd->root, build_data(pcb->Data));
+	lht_dom_hash_put(brd->root, build_attributes(&pcb->Attributes));
+	lht_dom_hash_put(brd->root, build_font(&pcb->Font));
+	lht_dom_hash_put(brd->root, build_styles(&pcb->RouteStyle));
+	lht_dom_hash_put(brd->root, build_netlists(pcb, pcb->NetlistLib, pcb->NetlistPatches, NUM_NETLISTS));
+	return brd;
+}
+
+static lhtpers_ev_res_t check_text(void *ev_ctx, lht_perstyle_t *style, lht_node_t *inmem_node, const char *ondisk_value)
+{
+	/* for coords, preserve formatting as long as values match */
+	if (lhtpers_rule_find(io_lihata_out_coords, inmem_node) != NULL) {
+		Coord v1, v2;
+		pcb_bool success1, success2;
+
+/*		fprintf(stderr, "SMART d='%s' m='%s'\n", ondisk_value, inmem_node->data.text.value);*/
+
+		v1 = GetValueEx(ondisk_value, NULL, NULL, NULL, NULL, &success1);
+		v2 = GetValueEx(inmem_node->data.text.value, NULL, NULL, NULL, NULL, &success2);
+/*		pcb_fprintf(stderr, " %d %d | %mm %mm\n", success1, success2, v1, v2);*/
+		if (success1 && success2) {
+			/* smart: if values are the same, keep the on-disk version */
+			if (v1 == v2)
+				return LHTPERS_DISK;
+			else
+				return LHTPERS_MEM;
+		}
+		/* else fall back to the string compare below */
+	}
+
+	/* classic method: string mismatch => overwrite from mem */
+	if (strcmp(inmem_node->data.text.value, ondisk_value) != 0)
+		return LHTPERS_MEM;
+
+	return LHTPERS_DISK;
+}
+
+
+int io_lihata_write_pcb(plug_io_t *ctx, FILE * FP, const char *old_filename, const char *new_filename, pcb_bool emergency)
+{
+	int res;
+	lht_doc_t *brd = build_board(PCB);
+	const char *fnpat = conf_io_lihata.plugins.io_lihata.aux_pcb_pattern;
+
+	if ((fnpat != NULL) && (*fnpat != '\0')) {
+		char *orig_fn;
+		char *pcb_fn = pcb_strdup_subst(fnpat, pcb_build_fn_cb);
+		
+		orig_fn = PCB->Filename;
+		PCB->Filename = NULL;
+		fprintf(stderr, "NOTE: io_lihata_write_pcb will save to '%s' but first saves in '%s': %d\n", new_filename, pcb_fn, hid_actionl("SaveTo", "LayoutAs", pcb_fn, "pcb", NULL));
+		free(pcb_fn);
+		
+		/* restore these because SaveTo() has changed them */
+		free(PCB->Filename);
+		PCB->Filename = orig_fn;
+		PCB->Data->loader = ctx;
+	}
+
+	if ((emergency) || ((old_filename == NULL) && (new_filename == NULL))) {
+		/* emergency or pipe save: use the canonical form */
+		res = lht_dom_export(brd->root, FP, "");
+	}
+	else {
+		FILE *inf = NULL;
+		char *errmsg;
+		lhtpers_ev_t events;
+
+		if (old_filename != NULL)
+			inf = fopen(old_filename, "r");
+
+		memset(&events, 0, sizeof(events));
+		events.text = check_text;
+/*		events.list_empty = check_list_empty;
+		events.list_elem_removed = check_list_elem_removed;*/
+		events.output_rules = io_lihata_out_rules;
+
+		res = lhtpers_fsave_as(&events, brd, inf, FP, old_filename, &errmsg);
+		fflush(FP);
+		if (inf != NULL)
+			fclose(inf);
+	}
+
+	lht_dom_uninit(brd);
+	return res;
+}
diff --git a/src_plugins/io_lihata/write.h b/src_plugins/io_lihata/write.h
new file mode 100644
index 0000000..a1447b3
--- /dev/null
+++ b/src_plugins/io_lihata/write.h
@@ -0,0 +1,23 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+int io_lihata_write_pcb(plug_io_t *ctx, FILE * FP);
diff --git a/src_plugins/io_lihata/write_style.c b/src_plugins/io_lihata/write_style.c
new file mode 100644
index 0000000..e5b04d7
--- /dev/null
+++ b/src_plugins/io_lihata/write_style.c
@@ -0,0 +1,517 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Specify default output formatting style to be more compact than 
+   the canonical lihata export style */
+
+#include "write_style.h"
+
+#define PB_BEGIN     {"*", 2, 2}
+#define PB_BEGINNL   {"\n *", 4, 4}
+#define PB_EMPTY     {"", 1, 1}
+#define PB_SEMICOLON {";", 2, 2}
+#define PB_SPACE     {" ", 2, 2}
+#define PB_LBRACE    {"{", 2, 2}
+#define PB_LBRACENL  {"{\n", 3, 3}
+#define PB_LBRACENLI {"{\n*", 4, 4}
+#define PB_RBRACE    {"}", 2, 2}
+#define PB_RBRACENL  {"}\n", 3, 3}
+#define PB_RBRACENLI {"}\n*", 4, 4}
+#define PB_RBRACESC  {"};", 3, 3}
+#define PB_NEWLINE   {"\n", 2, 2}
+#define PB_DEFAULT   {NULL, 0, 0}
+
+/* Space spearated key=val; pairs */
+static lht_perstyle_t style_inline = {
+	/* buff */        {PB_SPACE, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_SEMICOLON},
+	/* has_eq */      1,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+/* tightly packed key=val; pairs */
+static lht_perstyle_t style_inline_tight = {
+	/* buff */        {PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_SEMICOLON},
+	/* has_eq */      1,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_newline = {
+	/* buff */        {PB_BEGIN, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_NEWLINE},
+	/* has_eq */      1,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_istruct = {
+	/* buff */        {PB_SPACE, PB_SPACE, PB_LBRACE, PB_EMPTY, PB_EMPTY, PB_RBRACESC},
+	/* has_eq */      1,
+	/* val_brace */   1,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_struct = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACENL, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_struct_thermal = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACE, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_structi = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACENLI, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_structs = {
+	/* buff */        {PB_BEGIN, PB_SPACE, PB_LBRACE, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t style_nlstruct = {
+	/* buff */        {PB_BEGINNL, PB_SPACE, PB_LBRACENL, PB_EMPTY, PB_EMPTY, PB_RBRACENL},
+	/* has_eq */      0,
+	/* val_brace */   0,
+	/* etype */       0,
+	/* ename */       1,
+	/* name_braced */ 0
+};
+
+static lht_perstyle_t early_nl = {
+	/* buff */        {PB_NEWLINE, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY, PB_EMPTY},
+};
+
+
+static const char *pat_te_flags[]  = {"te:*", "ha:flags", "*", NULL};
+static const char *pat_te_attr[]   = {"te:*", "ha:attributes", "*", NULL};
+static lhtpers_rule_t r_ilists[]   = {
+	{pat_te_flags,  &style_inline_tight, NULL},
+	{pat_te_attr,   &style_newline, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_thermal[] = {"ha:thermal", "ha:flags", "*", NULL};
+static lhtpers_rule_t r_thermal[]   = {
+	{pat_thermal,  &style_struct_thermal, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_ha_flags[] = {"ha:flags", "*", NULL};
+static lhtpers_rule_t r_flags[]   = {
+	{pat_ha_flags,  &style_istruct, r_thermal},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_x1_line[]    = {"te:x1", "*", NULL};
+static const char *pat_y1_line[]    = {"te:y1", "*", NULL};
+static const char *pat_x2_line[]    = {"te:x2", "*", NULL};
+static const char *pat_y2_line[]    = {"te:y2", "*", NULL};
+static const char *pat_thickness[]  = {"te:thickness", "*", NULL};
+static const char *pat_clearance[]  = {"te:clearance", "*", NULL};
+static const char *pat_flags[]      = {"ha:flags", "*", NULL};
+static const char *pat_attributes[] = {"ha:attributes", "*", NULL};
+static lhtpers_rule_t r_line[] = {
+	{pat_x1_line,    &style_inline, NULL},
+	{pat_y1_line,    &style_inline, NULL},
+	{pat_x2_line,    &style_inline, NULL},
+	{pat_y2_line,    &style_inline, NULL},
+	{pat_thickness,  &style_inline, NULL},
+	{pat_clearance,  &style_inline, NULL},
+	{lhtpers_early_end, &early_nl, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_lgrp1_line[] = {"te:lgrp1", "*", NULL};
+static const char *pat_lgrp2_line[] = {"te:lgrp2", "*", NULL};
+static lhtpers_rule_t r_rat[] = {
+	{pat_x1_line,    &style_inline, NULL},
+	{pat_y1_line,    &style_inline, NULL},
+	{pat_lgrp1_line, &style_inline, NULL},
+	{pat_x2_line,    &style_inline, NULL},
+	{pat_y2_line,    &style_inline, NULL},
+	{pat_lgrp2_line, &style_inline, NULL},
+	{lhtpers_early_end, &early_nl, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_x[]             = {"te:x", "*", NULL};
+static const char *pat_y[]             = {"te:y", "*", NULL};
+static const char *pat_width_arc[]     = {"te:width", "*", NULL};
+static const char *pat_height_arc[]    = {"te:height", "*", NULL};
+static const char *pat_astart_arc[]    = {"te:astart", "*", NULL};
+static const char *pat_adelta_arc[]    = {"te:adelta", "*", NULL};
+static lhtpers_rule_t r_arc[] = {
+	{pat_x,          &style_inline, NULL},
+	{pat_y,          &style_inline, NULL},
+	{pat_width_arc,  &style_inline, NULL},
+	{pat_height_arc, &style_inline, NULL},
+	{pat_astart_arc, &style_inline, NULL},
+	{pat_adelta_arc, &style_inline, NULL},
+	{pat_thickness,  &style_inline, NULL},
+	{pat_clearance,  &style_inline, NULL},
+	{lhtpers_early_end, &early_nl, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_name[] = {"te:name", "*", NULL};
+static const char *pat_numb[] = {"te:number", "*", NULL};
+static const char *pat_hole[] = {"te:hole", "*", NULL};
+static const char *pat_mask[] = {"te:mask", "*", NULL};
+static lhtpers_rule_t r_pinvia[] = {
+	{pat_name,       &style_inline, NULL},
+	{pat_numb,       &style_inline, NULL},
+	{pat_x,          &style_inline, NULL},
+	{pat_y,          &style_inline, NULL},
+	{pat_hole,       &style_inline, NULL},
+	{pat_mask,       &style_inline, NULL},
+	{pat_thickness,  &style_inline, NULL},
+	{pat_clearance,  &style_inline, NULL},
+	{lhtpers_early_end, &early_nl, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static lhtpers_rule_t r_pad[] = {
+	{pat_name,       &style_inline, NULL},
+	{pat_numb,       &style_inline, NULL},
+	{pat_x1_line,    &style_inline, NULL},
+	{pat_y1_line,    &style_inline, NULL},
+	{pat_x2_line,    &style_inline, NULL},
+	{pat_y2_line,    &style_inline, NULL},
+	{pat_mask,       &style_inline, NULL},
+	{pat_thickness,  &style_inline, NULL},
+	{pat_clearance,  &style_inline, NULL},
+	{lhtpers_early_end, &early_nl, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_ta_contour[] = {"ta:contour", "*", NULL};
+static const char *pat_ta_hole[]    = {"ta:hole", "*", NULL};
+static lhtpers_rule_t r_geometry[] = {
+	{pat_ta_contour,       &style_istruct, NULL},
+	{pat_ta_hole,          &style_istruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_geometry[] = {"li:geometry", "*", NULL};
+static lhtpers_rule_t r_polygon[] = {
+	{pat_geometry,   &style_nlstruct, r_geometry},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_string[] = {"te:string", "*", NULL};
+static const char *pat_role[]   = {"te:role", "*", NULL};
+static const char *pat_scale[]  = {"te:scale", "*", NULL};
+static const char *pat_direct[] = {"te:direction", "*", NULL};
+static lhtpers_rule_t r_text[] = {
+	{pat_string,     &style_inline, NULL},
+	{pat_x,          &style_inline, NULL},
+	{pat_y,          &style_inline, NULL},
+	{pat_scale,      &style_inline, NULL},
+	{pat_direct,     &style_inline, NULL},
+	{pat_role,       &style_inline, NULL},
+	{lhtpers_early_end, &early_nl, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+
+static const char *pat_objects[] = {"li:objects", "*", NULL};
+static lhtpers_rule_t r_element[] = {
+	{pat_x,          &style_inline, NULL},
+	{pat_y,          &style_inline, NULL},
+	{pat_flags,      &style_nlstruct, r_thermal},
+	{pat_attributes, &style_nlstruct, NULL},
+	{pat_objects,    &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_visible[] = {"te:visible", "*", NULL};
+static const char *pat_group[]   = {"te:group", "*", NULL};
+static lhtpers_rule_t r_layer[] = {
+	{pat_visible,          &style_newline, NULL},
+	{pat_group,            &style_newline, NULL},
+	{pat_attributes,       &style_nlstruct, NULL},
+	{pat_objects,          &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static lhtpers_rule_t r_data[] = {
+	{pat_objects,          &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_width[]   = {"te:width", "*", NULL};
+static const char *pat_delta[]   = {"te:delta", "*", NULL};
+static lhtpers_rule_t r_symbol[] = {
+	{pat_width,      &style_inline, NULL},
+	{pat_delta,      &style_inline, NULL},
+	{pat_objects,    &style_nlstruct, NULL},
+	{pat_attributes,       &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+
+static const char *pat_cell_width[]   = {"te:cell_width", "*", NULL};
+static const char *pat_cell_height[]  = {"te:cell_height", "*", NULL};
+static const char *pat_ha_symbols[]   = {"ha:symbols", "*", NULL};
+static lhtpers_rule_t r_font1[] = {
+	{pat_cell_width,      &style_inline, NULL},
+	{pat_cell_height,     &style_inline, NULL},
+	{pat_attributes,      &style_nlstruct, NULL},
+	{pat_ha_symbols,      &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_li_styles[]   = {"li:styles", "*", NULL};
+static const char *pat_ha_meta[]     = {"ha:meta", "*", NULL};
+static const char *pat_ha_data[]     = {"ha:data", "*", NULL};
+static const char *pat_ha_font[]     = {"ha:font", "*", NULL};
+static lhtpers_rule_t r_root[] = {
+	{pat_attributes,      &style_nlstruct, NULL},
+	{pat_li_styles,       &style_nlstruct, NULL},
+	{pat_ha_meta,         &style_nlstruct, NULL},
+	{pat_ha_data,         &style_nlstruct, NULL},
+	{pat_ha_font,         &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+static const char *pat_net_input[] = {"ha:input", "*", NULL};
+static const char *pat_net_patch[] = {"ha:netlist_patch", "*", NULL};
+static lhtpers_rule_t r_netlists[] = {
+	{pat_net_input,         &style_nlstruct, NULL},
+	{pat_net_patch,         &style_nlstruct, NULL},
+	{NULL, NULL, NULL}
+};
+
+
+static const char *pat_line[] = {"ha:line.*", "*", NULL};
+static const char *pat_rat[]  = {"ha:rat.*", "*", NULL};
+static const char *pat_arc[]  = {"ha:arc.*", "*", NULL};
+static const char *pat_via[]  = {"ha:via.*", "*", NULL};
+static const char *pat_pin[]  = {"ha:pin.*", "*", NULL};
+static const char *pat_pad[]  = {"ha:pad.*", "*", NULL};
+static const char *pat_poly[] = {"ha:polygon.*", "*", NULL};
+static const char *pat_elem[] = {"ha:element.*", "*", NULL};
+static const char *pat_text[] = {"ha:text.*", "*", NULL};
+static const char *pat_data[] = {"ha:data", "*", NULL};
+static const char *pat_netlists[] = {"ha:netlists", "*", NULL};
+static const char *pat_objs[] = {"li:objects", "*", NULL};
+static const char *pat_font1[] = {"ha:geda_pcb", "ha:font", "*", NULL};
+static const char *pat_layer[] = {"ha:*", "li:layers", "*", NULL};
+static const char *pat_symbol[] = {"ha:*", "ha:symbols", "*", NULL};
+static const char *pat_thermt[] = {"te:*", "ha:thermal", "*", NULL};
+static const char *pat_flag[] = {"te:*", "ha:flags", "*", NULL};
+static const char *pat_cell[] = {"te:*", "ta:*", "*", NULL};
+static const char *pat_netinft[] = {"te:*", "li:net_info", "li:netlist_patch", "*", NULL};
+static const char *pat_nettxt[] = {"te:*", "ha:*", "li:netlist_patch", "*", NULL};
+static const char *pat_del_add[] = {"ha:*", "li:netlist_patch", "*", NULL};
+static const char *pat_netinfo[] = {"li:net_info", "li:netlist_patch", "*", NULL};
+static const char *pat_tconn[] = {"te:*", "li:conn", "ha:*", "li:*", "ha:netlists", "*", NULL};
+static const char *pat_lconn[] = {"li:conn", "ha:*", "li:*", "ha:netlists", "*", NULL};
+static const char *pat_root[] = {"^", NULL};
+
+static lhtpers_rule_t r_istructs[] = {
+	{pat_root,    &style_struct, r_root, NULL},
+
+	{pat_layer,   &style_nlstruct, r_layer, NULL},
+	{pat_symbol,  &style_structi, r_symbol, NULL},
+
+	{pat_line,    &style_structi, r_line, NULL},
+	{pat_rat,     &style_structi, r_rat, NULL},
+	{pat_arc,     &style_structi, r_arc, NULL},
+	{pat_via,     &style_structi, r_pinvia, NULL},
+	{pat_pin,     &style_structi, r_pinvia, NULL},
+	{pat_pad,     &style_structi, r_pad, NULL},
+	{pat_poly,    &style_structs, r_polygon, NULL},
+	{pat_elem,    &style_structi, r_element, NULL},
+	{pat_text,    &style_structi, r_text, NULL},
+	{pat_data,    &style_structi, r_data, NULL},
+	{pat_font1,   &style_structi, r_font1, NULL},
+	{pat_netlists,&style_struct, r_netlists, NULL},
+	{pat_objs,    &style_structi, NULL, NULL},
+	{pat_flag,    &style_newline, NULL, NULL},
+
+	{pat_cell,    &style_inline, NULL, NULL},
+	{pat_thermt,  &style_inline, NULL, NULL},
+	{pat_del_add, &style_struct_thermal, NULL, NULL},
+	{pat_netinfo, &style_struct_thermal, NULL, NULL},
+	{pat_netinft, &style_inline, NULL, NULL},
+	{pat_nettxt,  &style_inline, NULL, NULL},
+	{pat_tconn,   &style_inline, NULL, NULL},
+	{pat_lconn,   &style_struct_thermal, NULL, NULL},
+	{NULL, NULL, NULL}
+};
+
+lhtpers_rule_t *io_lihata_out_rules[] = {
+	r_istructs, r_ilists, r_ilists, r_flags, NULL
+};
+
+
+/*****************************************************************************/
+static const char *cpat_rat_x1[]   = {"te:x1", "ha:rat.*", "*", NULL};
+static const char *cpat_rat_y1[]   = {"te:y1", "ha:rat.*", "*", NULL};
+static const char *cpat_rat_x2[]   = {"te:x2", "ha:rat.*", "*", NULL};
+static const char *cpat_rat_y2[]   = {"te:y2", "ha:rat.*", "*", NULL};
+static const char *cpat_line_x1[]  = {"te:x1", "ha:line.*", "*", NULL};
+static const char *cpat_line_y1[]  = {"te:y1", "ha:line.*", "*", NULL};
+static const char *cpat_line_x2[]  = {"te:x2", "ha:line.*", "*", NULL};
+static const char *cpat_line_y2[]  = {"te:y2", "ha:line.*", "*", NULL};
+static const char *cpat_line_th[]  = {"te:thickness", "ha:line.*", "*", NULL};
+static const char *cpat_line_cl[]  = {"te:clearance", "ha:line.*", "*", NULL};
+static const char *cpat_size_x[]   = {"te:x", "ha:size", "ha:meta", "*", NULL};
+static const char *cpat_size_y[]   = {"te:y", "ha:size", "ha:meta", "*", NULL};
+static const char *cpat_curs_x[]   = {"te:x", "ha:cursor", "ha:meta", "*", NULL};
+static const char *cpat_curs_y[]   = {"te:y", "ha:cursor", "ha:meta", "*", NULL};
+static const char *cpat_curs_z[]   = {"te:zoom", "ha:cursor", "ha:meta", "*", NULL};
+static const char *cpat_drc_min[]  = {"te:min_*", "ha:drc", "ha:meta", "*", NULL};
+static const char *cpat_drc_blt[]  = {"te:bloat", "ha:drc", "ha:meta", "*", NULL};
+static const char *cpat_drc_shr[]  = {"te:shrink", "ha:drc", "ha:meta", "*", NULL};
+static const char *cpat_grido[]    = {"te:offs_*", "ha:grid", "ha:meta", "*", NULL};
+static const char *cpat_grids[]    = {"te:spacing", "ha:grid", "ha:meta", "*", NULL};
+
+static const char *cpat_arc_x[]    = {"te:x", "ha:arc.*", "*", NULL};
+static const char *cpat_arc_y[]    = {"te:y", "ha:arc.*", "*", NULL};
+static const char *cpat_arc_w[]    = {"te:width", "ha:arc.*", "*", NULL};
+static const char *cpat_arc_h[]    = {"te:height", "ha:arc.*", "*", NULL};
+static const char *cpat_arc_th[]   = {"te:thickness", "ha:arc.*", "*", NULL};
+static const char *cpat_arc_cl[]   = {"te:clearance", "ha:arc.*", "*", NULL};
+static const char *cpat_geo_ta[]   = {"te:*", "ta:*", "li:geometry", "*", NULL};
+static const char *cpat_text_x[]   = {"te:x", "ha:text.*", "*", NULL};
+static const char *cpat_text_y[]   = {"te:y", "ha:text.*", "*", NULL};
+static const char *cpat_pin_x[]    = {"te:x", "ha:pin.*", "*", NULL};
+static const char *cpat_pin_y[]    = {"te:y", "ha:pin.*", "*", NULL};
+static const char *cpat_pin_hole[] = {"te:hole", "ha:pin.*", "*", NULL};
+static const char *cpat_pin_mask[] = {"te:mask", "ha:pin.*", "*", NULL};
+static const char *cpat_pad_x1[]   = {"te:x1", "ha:pad.*", "*", NULL};
+static const char *cpat_pad_y1[]   = {"te:y1", "ha:pad.*", "*", NULL};
+static const char *cpat_pad_x2[]   = {"te:x2", "ha:pad.*", "*", NULL};
+static const char *cpat_pad_y2[]   = {"te:y2", "ha:pad.*", "*", NULL};
+static const char *cpat_pad_hole[] = {"te:hole", "ha:pad.*", "*", NULL};
+static const char *cpat_pad_mask[] = {"te:mask", "ha:pad.*", "*", NULL};
+static const char *cpat_elem_x[]   = {"te:x", "ha:element.*", "*", NULL};
+static const char *cpat_elem_y[]   = {"te:y", "ha:element.*", "*", NULL};
+
+static const char *cpat_sym_w[]   = {"te:width", "ha:*", "ha:symbols", "*", NULL};
+static const char *cpat_sym_h[]   = {"te:height", "ha:*", "ha:symbols", "*", NULL};
+static const char *cpat_sym_d[]   = {"te:delta", "ha:*", "ha:symbols", "*", NULL};
+static const char *cpat_font_w[]  = {"te:cell_width", "ha:*", "ha:font", "*", NULL};
+static const char *cpat_font_h[]  = {"te:cell_height", "ha:*", "ha:font", "*", NULL};
+static const char *cpat_styl_th[] = {"te:thickness", "ha:*", "li:styles", "*", NULL};
+static const char *cpat_styl_cl[] = {"te:clearance", "ha:*", "li:styles", "*", NULL};
+static const char *cpat_styl_hl[] = {"te:hole", "ha:*", "li:styles", "*", NULL};
+static const char *cpat_styl_da[] = {"te:diameter", "ha:*", "li:styles", "*", NULL};
+
+
+lhtpers_rule_t io_lihata_out_coords[] = {
+	{cpat_rat_x1,   NULL, NULL, NULL},
+	{cpat_rat_y1,   NULL, NULL, NULL},
+	{cpat_rat_x2,   NULL, NULL, NULL},
+	{cpat_rat_y2,   NULL, NULL, NULL},
+	{cpat_line_x1,  NULL, NULL, NULL},
+	{cpat_line_y1,  NULL, NULL, NULL},
+	{cpat_line_x2,  NULL, NULL, NULL},
+	{cpat_line_y2,  NULL, NULL, NULL},
+	{cpat_line_th,  NULL, NULL, NULL},
+	{cpat_line_cl,  NULL, NULL, NULL},
+	{cpat_size_x,   NULL, NULL, NULL},
+	{cpat_size_y,   NULL, NULL, NULL},
+	{cpat_curs_x,   NULL, NULL, NULL},
+	{cpat_curs_y,   NULL, NULL, NULL},
+	{cpat_curs_z,   NULL, NULL, NULL},
+	{cpat_drc_min,  NULL, NULL, NULL},
+	{cpat_drc_blt,  NULL, NULL, NULL},
+	{cpat_drc_shr,  NULL, NULL, NULL},
+	{cpat_grido,    NULL, NULL, NULL},
+	{cpat_grids,    NULL, NULL, NULL},
+	{cpat_arc_x,    NULL, NULL, NULL},
+	{cpat_arc_y,    NULL, NULL, NULL},
+	{cpat_arc_w,    NULL, NULL, NULL},
+	{cpat_arc_h,    NULL, NULL, NULL},
+	{cpat_arc_th,   NULL, NULL, NULL},
+	{cpat_arc_cl,   NULL, NULL, NULL},
+	{cpat_geo_ta,   NULL, NULL, NULL},
+	{cpat_text_x,   NULL, NULL, NULL},
+	{cpat_text_y,   NULL, NULL, NULL},
+	{cpat_pin_x,    NULL, NULL, NULL},
+	{cpat_pin_y,    NULL, NULL, NULL},
+	{cpat_pin_hole, NULL, NULL, NULL},
+	{cpat_pin_mask, NULL, NULL, NULL},
+	{cpat_pad_x1,   NULL, NULL, NULL},
+	{cpat_pad_y1,   NULL, NULL, NULL},
+	{cpat_pad_x2,   NULL, NULL, NULL},
+	{cpat_pad_y2,   NULL, NULL, NULL},
+	{cpat_pad_hole, NULL, NULL, NULL},
+	{cpat_pad_mask, NULL, NULL, NULL},
+	{cpat_elem_x,   NULL, NULL, NULL},
+	{cpat_elem_y,   NULL, NULL, NULL},
+	{cpat_sym_w,    NULL, NULL, NULL},
+	{cpat_sym_h,    NULL, NULL, NULL},
+	{cpat_sym_d,    NULL, NULL, NULL},
+	{cpat_font_w,   NULL, NULL, NULL},
+	{cpat_font_h,   NULL, NULL, NULL},
+	{cpat_styl_th,  NULL, NULL, NULL},
+	{cpat_styl_cl,  NULL, NULL, NULL},
+	{cpat_styl_hl,  NULL, NULL, NULL},
+	{cpat_styl_da,  NULL, NULL, NULL},
+	{NULL, NULL, NULL, NULL}
+};
+
diff --git a/src_plugins/io_lihata/write_style.h b/src_plugins/io_lihata/write_style.h
new file mode 100644
index 0000000..01dcd8e
--- /dev/null
+++ b/src_plugins/io_lihata/write_style.h
@@ -0,0 +1,29 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Specify default output formatting style to be more compact than 
+   the canonical lihata export style */
+
+#include <liblhtpers/lhtpers.h>
+
+extern lhtpers_rule_t *io_lihata_out_rules[];
+extern lhtpers_rule_t io_lihata_out_coords[];
diff --git a/src_plugins/io_pcb/Makefile b/src_plugins/io_pcb/Makefile
new file mode 100644
index 0000000..1023b1a
--- /dev/null
+++ b/src_plugins/io_pcb/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_io_pcb
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/io_pcb/Plug.tmpasm b/src_plugins/io_pcb/Plug.tmpasm
new file mode 100644
index 0000000..3547b13
--- /dev/null
+++ b/src_plugins/io_pcb/Plug.tmpasm
@@ -0,0 +1,10 @@
+put /local/pcb/mod {io_pcb}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/io_pcb/io_pcb.o $(PLUGDIR)/io_pcb/file.o $(PLUGDIR)/io_pcb/parse_y.o $(PLUGDIR)/io_pcb/parse_l.o $(PLUGDIR)/io_pcb/flags.o $(PLUGDIR)/io_pcb/attribs.o @]
+put /local/pcb/mod/YACC {$(PLUGDIR)/io_pcb/parse_y}
+put /local/pcb/mod/LEX  {$(PLUGDIR)/io_pcb/parse_l}
+
+switch /local/pcb/io_pcb/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/io_pcb/README b/src_plugins/io_pcb/README
new file mode 100644
index 0000000..84153b1
--- /dev/null
+++ b/src_plugins/io_pcb/README
@@ -0,0 +1,5 @@
+Load and save the design and elements in the original pcb text format.
+
+#state: works
+#default: buildin
+#implements: io
diff --git a/src_plugins/io_pcb/attribs.c b/src_plugins/io_pcb/attribs.c
new file mode 100644
index 0000000..6e7fd93
--- /dev/null
+++ b/src_plugins/io_pcb/attribs.c
@@ -0,0 +1,127 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "conf.h"
+#include "misc.h"
+#include "compat_misc.h"
+
+#define LISTSEP " [[pcb-rnd]] "
+
+/* Save and load pcb conf attributes, but do not save ::design:: (because they have flags) */
+static const char *conf_attr_prefix = "PCB::conf::";
+static const char *conf_attr_prefix_inhibit = "design::";
+/* The optimizer will fix this */
+#define conf_attr_prefix_len strlen(conf_attr_prefix)
+#define conf_attr_prefix_inhibit_len strlen(conf_attr_prefix_inhibit)
+
+static int path_ok(const char *path)
+{
+	if (strncmp(path, conf_attr_prefix, conf_attr_prefix_len) != 0)
+		return 0;
+	if (strncmp(path + conf_attr_prefix_len, conf_attr_prefix_inhibit, conf_attr_prefix_inhibit_len) == 0)
+		return 0;
+	return 1;
+}
+
+static void c2a(PCBType *pcb, lht_node_t *tree, const char *path1)
+{
+	lht_dom_iterator_t it;
+	lht_node_t *n;
+	char apath[512], *path, *pe;
+	int path1l = strlen(path1);
+
+	memcpy(apath, conf_attr_prefix, conf_attr_prefix_len);
+	path = apath + conf_attr_prefix_len;
+
+	if (path1l != 0) {
+		memcpy(path, path1, path1l);
+		path[path1l] = '/';
+		pe = path+path1l+1;
+	}
+	else
+		pe = path;
+
+	/* a depth-first-search and save config items from the tree */
+	for(n = lht_dom_first(&it, tree); n != NULL; n = lht_dom_next(&it)) {
+		strcpy(pe, n->name);
+		if (n->type == LHT_HASH)
+			c2a(pcb, n, path);
+		if (strncmp(path, "design/",7) == 0) {
+			continue;
+		}
+		if (n->type == LHT_TEXT) {
+			conf_native_t *nv = conf_get_field(path);
+			if ((nv != NULL) && (!nv->random_flags.io_pcb_no_attrib))
+				AttributePutToList(&pcb->Attributes, apath, n->data.text.value, 1);
+		}
+		else if (n->type == LHT_LIST) {
+			lht_node_t *i;
+			conf_native_t *nv = conf_get_field(path);
+			if ((nv != NULL) && (!nv->random_flags.io_pcb_no_attrib)) {
+				gds_t conc;
+				gds_init(&conc);
+				for(i = n->data.list.first; i != NULL; i = i->next) {
+					if (i != n->data.list.first)
+						gds_append_str(&conc, LISTSEP);
+					gds_append_str(&conc, i->data.text.value);
+				}
+				AttributePutToList(&pcb->Attributes, apath,  conc.array, 1);
+				gds_uninit(&conc);
+			}
+		}
+	}
+}
+
+void io_pcb_attrib_c2a(PCBType *pcb)
+{
+	lht_node_t *nmain = conf_lht_get_first(CFR_DESIGN);
+
+	c2a(pcb, nmain, "");
+}
+
+void io_pcb_attrib_a2c(PCBType *pcb)
+{
+	int n;
+
+	for (n = 0; n < pcb->Attributes.Number; n++) {
+		if (path_ok(pcb->Attributes.List[n].name)) {
+			conf_native_t *nv = conf_get_field(pcb->Attributes.List[n].name + conf_attr_prefix_len);
+			if (nv == NULL)
+				continue;
+			if (nv->type == CFN_LIST) {
+				char *tmp = pcb_strdup(pcb->Attributes.List[n].value);
+				char *next, *curr;
+				for(curr = tmp; curr != NULL; curr = next) {
+					next = strstr(curr, LISTSEP);
+					if (next != NULL) {
+						*next = '\0';
+						next += strlen(LISTSEP);
+					}
+					conf_set(CFR_DESIGN, pcb->Attributes.List[n].name + conf_attr_prefix_len, -1, curr, POL_APPEND);
+				}
+				free(tmp);
+			}
+			else /* assume plain string */
+				conf_set(CFR_DESIGN, pcb->Attributes.List[n].name + conf_attr_prefix_len, -1, pcb->Attributes.List[n].value, POL_OVERWRITE);
+		}
+	}
+}
diff --git a/src_plugins/io_pcb/attribs.h b/src_plugins/io_pcb/attribs.h
new file mode 100644
index 0000000..8529766
--- /dev/null
+++ b/src_plugins/io_pcb/attribs.h
@@ -0,0 +1,2 @@
+void io_pcb_attrib_c2a(PCBType *pcb);
+void io_pcb_attrib_a2c(PCBType *pcb);
diff --git a/src_plugins/io_pcb/file.c b/src_plugins/io_pcb/file.c
new file mode 100644
index 0000000..b23ade1
--- /dev/null
+++ b/src_plugins/io_pcb/file.c
@@ -0,0 +1,581 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
+ *  Copyright (C) 2015 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* file save, load, merge ... routines */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <locale.h>
+#include "global.h"
+
+#include <time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buffer.h"
+#include "change.h"
+#include "create.h"
+#include "crosshair.h"
+#include "data.h"
+#include "error.h"
+#include "file.h"
+#include "plug_io.h"
+#include "hid.h"
+#include "layer.h"
+#include "misc.h"
+#include "move.h"
+#include "mymem.h"
+#include "parse_common.h"
+#include "pcb-printf.h"
+#include "polygon.h"
+#include "rats.h"
+#include "remove.h"
+#include "set.h"
+#include "strflags.h"
+#include "compat_fs.h"
+#include "paths.h"
+#include "rats_patch.h"
+#include "hid_actions.h"
+#include "hid_flags.h"
+#include "flags.h"
+#include "attribs.h"
+#include "route_style.h"
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static void WritePCBInfoHeader(FILE *);
+static void WritePCBDataHeader(FILE *);
+static void WritePCBFontData(FILE *);
+static void WriteViaData(FILE *, DataTypePtr);
+static void WritePCBRatData(FILE *);
+static void WriteLayerData(FILE *, pcb_cardinal_t, LayerTypePtr);
+
+/* ---------------------------------------------------------------------------
+ * Flag helper functions
+ */
+
+#define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
+
+/* --------------------------------------------------------------------------- */
+
+/* The idea here is to avoid gratuitously breaking backwards
+   compatibility due to a new but rarely used feature.  The first such
+   case, for example, was the polygon Hole - if your design included
+   polygon holes, you needed a newer PCB to read it, but if your
+   design didn't include holes, PCB would produce a file that older
+   PCBs could read, if only it had the correct version number in it.
+
+   If, however, you have to add or change a feature that really does
+   require a new PCB version all the time, it's time to remove all the
+   tests below and just always output the new version.
+
+   Note: Best practices here is to add support for a feature *first*
+   (and bump PCB_FILE_VERSION in file.h), and note the version that
+   added that support below, and *later* update the file format to
+   need that version (which may then be older than PCB_FILE_VERSION).
+   Hopefully, that allows for one release between adding support and
+   needing it, which should minimize breakage.  Of course, that's not
+   *always* possible, practical, or desirable.
+
+*/
+
+/* Hole[] in Polygon.  */
+#define PCB_FILE_VERSION_HOLES 20100606
+/* First version ever saved.  */
+#define PCB_FILE_VERSION_BASELINE 20070407
+
+int PCBFileVersionNeeded(void)
+{
+	ALLPOLYGON_LOOP(PCB->Data);
+	{
+		if (polygon->HoleIndexN > 0)
+			return PCB_FILE_VERSION_HOLES;
+	}
+	ENDALL_LOOP;
+
+	return PCB_FILE_VERSION_BASELINE;
+}
+
+/* In future all use of this should be supplanted by
+ * pcb-printf and %mr/%m# spec */
+static const char *c_dtostr(double d)
+{
+	static char buf[100];
+	int i, f;
+	char *bufp = buf;
+
+	if (d < 0) {
+		*bufp++ = '-';
+		d = -d;
+	}
+	d += 0.0000005;								/* rounding */
+	i = floor(d);
+	d -= i;
+	sprintf(bufp, "%d", i);
+	bufp += strlen(bufp);
+	*bufp++ = '.';
+
+	f = floor(d * 1000000.0);
+	sprintf(bufp, "%06d", f);
+	return buf;
+}
+
+
+/* ---------------------------------------------------------------------------
+ * writes out an attribute list
+ */
+static void WriteAttributeList(FILE * FP, AttributeListTypePtr list, const char *prefix)
+{
+	int i;
+
+	for (i = 0; i < list->Number; i++)
+		fprintf(FP, "%sAttribute(\"%s\" \"%s\")\n", prefix, list->List[i].name, list->List[i].value);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes layout header information
+ */
+static void WritePCBInfoHeader(FILE * FP)
+{
+	/* write some useful comments */
+	fprintf(FP, "# release: pcb-rnd " VERSION "\n");
+
+	/* avoid writing things like user name or date, as these cause merge
+	 * conflicts in collaborative environments using version control systems
+	 */
+}
+
+static void conf_update_pcb_flag(FlagType *dest, const char *hash_path, int binflag)
+{
+	conf_native_t *n = conf_get_field(hash_path);
+	struct {
+		FlagType Flags;
+	} *tmp = (void *)dest;
+
+	if ((n == NULL) || (n->type != CFN_BOOLEAN) || (n->used < 0) || (!n->val.boolean[0]))
+		CLEAR_FLAG(binflag, tmp);
+	else
+		SET_FLAG(binflag, tmp);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes data header
+ * the name of the PCB, cursor location, zoom and grid
+ * layergroups and some flags
+ */
+static void WritePCBDataHeader(FILE * FP)
+{
+	int group;
+	FlagType pcb_flags;
+
+	memset(&pcb_flags, 0, sizeof(pcb_flags));
+
+	/*
+	 * ************************** README *******************
+	 * ************************** README *******************
+	 *
+	 * If the file format is modified in any way, update
+	 * PCB_FILE_VERSION in file.h as well as PCBFileVersionNeeded()
+	 * at the top of this file.
+	 *
+	 * ************************** README *******************
+	 * ************************** README *******************
+	 */
+
+	io_pcb_attrib_c2a(PCB);
+
+	/* set binary flags from conf hash; these flags used to be checked
+	   with TEST_FLAG() but got moved to the conf system */
+	conf_update_pcb_flag(&pcb_flags, "plugins/mincut/enable", ENABLEPCB_FLAG_MINCUT);
+	conf_update_pcb_flag(&pcb_flags, "editor/show_number", SHOWNUMBERFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/show_drc", SHOWPCB_FLAG_DRC);
+	conf_update_pcb_flag(&pcb_flags, "editor/rubber_band_mode", RUBBERBANDFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/auto_drc", AUTOPCB_FLAG_DRC);
+	conf_update_pcb_flag(&pcb_flags, "editor/all_direction_lines", ALLDIRECTIONFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/swap_start_direction", SWAPSTARTDIRFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/unique_names", UNIQUENAMEFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/clear_line", CLEARNEWFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/full_poly", NEWPCB_FLAG_FULLPOLY);
+	conf_update_pcb_flag(&pcb_flags, "editor/snap_pin", SNAPPCB_FLAG_PIN);
+	conf_update_pcb_flag(&pcb_flags, "editor/orthogonal_moves", ORTHOMOVEFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/live_routing", LIVEROUTEFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/lock_names", LOCKNAMESFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/only_names", ONLYNAMESFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/hide_names", HIDENAMESFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/thin_draw", THINDRAWFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/thin_draw_poly", THINDRAWPOLYFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/local_ref", LOCALREFFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/check_planes",CHECKPLANESFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/description", DESCRIPTIONFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/name_on_pcb", NAMEONPCBFLAG);
+	conf_update_pcb_flag(&pcb_flags, "editor/show_mask", SHOWMASKFLAG);
+
+	fprintf(FP, "\n# To read pcb files, the pcb version (or the git source date) must be >= the file version\n");
+	fprintf(FP, "FileVersion[%i]\n", PCBFileVersionNeeded());
+
+	fputs("\nPCB[", FP);
+	PrintQuotedString(FP, (char *) EMPTY(PCB->Name));
+	pcb_fprintf(FP, " %[0] %[0]]\n\n", PCB->MaxWidth, PCB->MaxHeight);
+	pcb_fprintf(FP, "Grid[%[0] %[0] %[0] %d]\n", PCB->Grid, PCB->GridOffsetX, PCB->GridOffsetY, conf_core.editor.draw_grid);
+	pcb_fprintf(FP, "Cursor[%[0] %[0] %s]\n", Crosshair.X, Crosshair.Y, c_dtostr(PCB->Zoom));
+	/* PolyArea should be output in square cmils, no suffix */
+	fprintf(FP, "PolyArea[%s]\n", c_dtostr(PCB_COORD_TO_MIL(PCB_COORD_TO_MIL(PCB->IsleArea) * 100) * 100));
+	pcb_fprintf(FP, "Thermal[%s]\n", c_dtostr(PCB->ThermScale));
+	pcb_fprintf(FP, "DRC[%[0] %[0] %[0] %[0] %[0] %[0]]\n", PCB->Bloat, PCB->Shrink,
+							PCB->minWid, PCB->minSlk, PCB->minDrill, PCB->minRing);
+	fprintf(FP, "Flags(%s)\n", pcbflags_to_string(pcb_flags));
+	fprintf(FP, "Groups(\"%s\")\n", LayerGroupsToString(&PCB->LayerGroups));
+	fputs("Styles[\"", FP);
+
+	if (vtroutestyle_len(&PCB->RouteStyle) > 0) {
+		for (group = 0; group < vtroutestyle_len(&PCB->RouteStyle) - 1; group++)
+			pcb_fprintf(FP, "%s,%[0],%[0],%[0],%[0]:", PCB->RouteStyle.array[group].name,
+									PCB->RouteStyle.array[group].Thick,
+									PCB->RouteStyle.array[group].Diameter, PCB->RouteStyle.array[group].Hole, PCB->RouteStyle.array[group].Clearance);
+		pcb_fprintf(FP, "%s,%[0],%[0],%[0],%[0]\"]\n\n", PCB->RouteStyle.array[group].name,
+								PCB->RouteStyle.array[group].Thick,
+								PCB->RouteStyle.array[group].Diameter, PCB->RouteStyle.array[group].Hole, PCB->RouteStyle.array[group].Clearance);
+	}
+	else
+		fprintf(FP, "\"]\n\n");
+}
+
+/* ---------------------------------------------------------------------------
+ * writes font data of non empty symbols
+ */
+static void WritePCBFontData(FILE * FP)
+{
+	pcb_cardinal_t i, j;
+	LineTypePtr line;
+	FontTypePtr font;
+
+	for (font = &PCB->Font, i = 0; i <= MAX_FONTPOSITION; i++) {
+		if (!font->Symbol[i].Valid)
+			continue;
+
+		if (isprint(i))
+			pcb_fprintf(FP, "Symbol['%c' %[0]]\n(\n", i, font->Symbol[i].Delta);
+		else
+			pcb_fprintf(FP, "Symbol[%i %[0]]\n(\n", i, font->Symbol[i].Delta);
+
+		line = font->Symbol[i].Line;
+		for (j = font->Symbol[i].LineN; j; j--, line++)
+			pcb_fprintf(FP, "\tSymbolLine[%[0] %[0] %[0] %[0] %[0]]\n",
+									line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, line->Thickness);
+		fputs(")\n", FP);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes via data
+ */
+static void WriteViaData(FILE * FP, DataTypePtr Data)
+{
+	gdl_iterator_t it;
+	PinType *via;
+
+	/* write information about vias */
+	pinlist_foreach(&Data->Via, &it, via) {
+		pcb_fprintf(FP, "Via[%[0] %[0] %[0] %[0] %[0] %[0] ", via->X, via->Y,
+								via->Thickness, via->Clearance, via->Mask, via->DrillingHole);
+		PrintQuotedString(FP, (char *) EMPTY(via->Name));
+		fprintf(FP, " %s]\n", F2S(via, PCB_TYPE_VIA));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes rat-line data
+ */
+static void WritePCBRatData(FILE * FP)
+{
+	gdl_iterator_t it;
+	RatType *line;
+
+	/* write information about rats */
+	ratlist_foreach(&PCB->Data->Rat, &it, line) {
+		pcb_fprintf(FP, "Rat[%[0] %[0] %d %[0] %[0] %d ",
+								line->Point1.X, line->Point1.Y, line->group1, line->Point2.X, line->Point2.Y, line->group2);
+		fprintf(FP, " %s]\n", F2S(line, PCB_TYPE_RATLINE));
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes netlist data
+ */
+static void WritePCBNetlistData(FILE * FP)
+{
+	/* write out the netlist if it exists */
+	if (PCB->NetlistLib[NETLIST_INPUT].MenuN) {
+		int n, p;
+		fprintf(FP, "NetList()\n(\n");
+
+		for (n = 0; n < PCB->NetlistLib[NETLIST_INPUT].MenuN; n++) {
+			LibraryMenuTypePtr menu = &PCB->NetlistLib[NETLIST_INPUT].Menu[n];
+			fprintf(FP, "\tNet(");
+			PrintQuotedString(FP, &menu->Name[2]);
+			fprintf(FP, " ");
+			PrintQuotedString(FP, (char *) UNKNOWN(menu->Style));
+			fprintf(FP, ")\n\t(\n");
+			for (p = 0; p < menu->EntryN; p++) {
+				LibraryEntryTypePtr entry = &menu->Entry[p];
+				fprintf(FP, "\t\tConnect(");
+				PrintQuotedString(FP, entry->ListEntry);
+				fprintf(FP, ")\n");
+			}
+			fprintf(FP, "\t)\n");
+		}
+		fprintf(FP, ")\n");
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes netlist patch data
+ */
+static void WritePCBNetlistPatchData(FILE * FP)
+{
+	if (PCB->NetlistPatches != NULL) {
+		fprintf(FP, "NetListPatch()\n(\n");
+		rats_patch_fexport(PCB, FP, 1);
+		fprintf(FP, ")\n");
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes element data
+ */
+int io_pcb_WriteElementData(plug_io_t *ctx, FILE * FP, DataTypePtr Data)
+{
+	gdl_iterator_t eit;
+	LineType *line;
+	ArcType *arc;
+	ElementType *element;
+
+	pcb_printf_slot[0] = ((io_pcb_ctx_t *)(ctx->plugin_data))->write_coord_fmt;
+	elementlist_foreach(&Data->Element, &eit, element) {
+		gdl_iterator_t it;
+		PinType *pin;
+		PadType *pad;
+
+		/* only non empty elements */
+		if (!linelist_length(&element->Line) && !pinlist_length(&element->Pin) && !arclist_length(&element->Arc) && !padlist_length(&element->Pad))
+			continue;
+		/* the coordinates and text-flags are the same for
+		 * both names of an element
+		 */
+		fprintf(FP, "\nElement[%s ", F2S(element, PCB_TYPE_ELEMENT));
+		PrintQuotedString(FP, (char *) EMPTY(DESCRIPTION_NAME(element)));
+		fputc(' ', FP);
+		PrintQuotedString(FP, (char *) EMPTY(NAMEONPCB_NAME(element)));
+		fputc(' ', FP);
+		PrintQuotedString(FP, (char *) EMPTY(VALUE_NAME(element)));
+		pcb_fprintf(FP, " %[0] %[0] %[0] %[0] %d %d %s]\n(\n",
+								element->MarkX, element->MarkY,
+								DESCRIPTION_TEXT(element).X - element->MarkX,
+								DESCRIPTION_TEXT(element).Y - element->MarkY,
+								DESCRIPTION_TEXT(element).Direction,
+								DESCRIPTION_TEXT(element).Scale, F2S(&(DESCRIPTION_TEXT(element)), PCB_TYPE_ELEMENT_NAME));
+		WriteAttributeList(FP, &element->Attributes, "\t");
+		pinlist_foreach(&element->Pin, &it, pin) {
+			pcb_fprintf(FP, "\tPin[%[0] %[0] %[0] %[0] %[0] %[0] ",
+									pin->X - element->MarkX,
+									pin->Y - element->MarkY, pin->Thickness, pin->Clearance, pin->Mask, pin->DrillingHole);
+			PrintQuotedString(FP, (char *) EMPTY(pin->Name));
+			fprintf(FP, " ");
+			PrintQuotedString(FP, (char *) EMPTY(pin->Number));
+			fprintf(FP, " %s]\n", F2S(pin, PCB_TYPE_PIN));
+		}
+		pinlist_foreach(&element->Pad, &it, pad) {
+			pcb_fprintf(FP, "\tPad[%[0] %[0] %[0] %[0] %[0] %[0] %[0] ",
+									pad->Point1.X - element->MarkX,
+									pad->Point1.Y - element->MarkY,
+									pad->Point2.X - element->MarkX, pad->Point2.Y - element->MarkY, pad->Thickness, pad->Clearance, pad->Mask);
+			PrintQuotedString(FP, (char *) EMPTY(pad->Name));
+			fprintf(FP, " ");
+			PrintQuotedString(FP, (char *) EMPTY(pad->Number));
+			fprintf(FP, " %s]\n", F2S(pad, PCB_TYPE_PAD));
+		}
+		linelist_foreach(&element->Line, &it, line) {
+			pcb_fprintf(FP, "\tElementLine [%[0] %[0] %[0] %[0] %[0]]\n",
+									line->Point1.X - element->MarkX,
+									line->Point1.Y - element->MarkY,
+									line->Point2.X - element->MarkX, line->Point2.Y - element->MarkY, line->Thickness);
+		}
+		linelist_foreach(&element->Arc, &it, arc) {
+			pcb_fprintf(FP, "\tElementArc [%[0] %[0] %[0] %[0] %ma %ma %[0]]\n",
+									arc->X - element->MarkX,
+									arc->Y - element->MarkY, arc->Width, arc->Height, arc->StartAngle, arc->Delta, arc->Thickness);
+		}
+		fputs("\n\t)\n", FP);
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * writes layer data
+ */
+static void WriteLayerData(FILE * FP, pcb_cardinal_t Number, LayerTypePtr layer)
+{
+	gdl_iterator_t it;
+	LineType *line;
+	ArcType *arc;
+	TextType *text;
+	PolygonType *polygon;
+
+	/* write information about non empty layers */
+	if (!LAYER_IS_EMPTY(layer) || (layer->Name && *layer->Name)) {
+		fprintf(FP, "Layer(%i ", (int) Number + 1);
+		PrintQuotedString(FP, (char *) EMPTY(layer->Name));
+		fputs(")\n(\n", FP);
+		WriteAttributeList(FP, &layer->Attributes, "\t");
+
+		linelist_foreach(&layer->Line, &it, line) {
+			pcb_fprintf(FP, "\tLine[%[0] %[0] %[0] %[0] %[0] %[0] %s]\n",
+									line->Point1.X, line->Point1.Y,
+									line->Point2.X, line->Point2.Y, line->Thickness, line->Clearance, F2S(line, PCB_TYPE_LINE));
+		}
+		arclist_foreach(&layer->Arc, &it, arc) {
+			pcb_fprintf(FP, "\tArc[%[0] %[0] %[0] %[0] %[0] %[0] %ma %ma %s]\n",
+									arc->X, arc->Y, arc->Width,
+									arc->Height, arc->Thickness, arc->Clearance, arc->StartAngle, arc->Delta, F2S(arc, PCB_TYPE_ARC));
+		}
+		textlist_foreach(&layer->Text, &it, text) {
+			pcb_fprintf(FP, "\tText[%[0] %[0] %d %d ", text->X, text->Y, text->Direction, text->Scale);
+			PrintQuotedString(FP, (char *) EMPTY(text->TextString));
+			fprintf(FP, " %s]\n", F2S(text, PCB_TYPE_TEXT));
+		}
+		textlist_foreach(&layer->Polygon, &it, polygon) {
+			int p, i = 0;
+			pcb_cardinal_t hole = 0;
+			fprintf(FP, "\tPolygon(%s)\n\t(", F2S(polygon, PCB_TYPE_POLYGON));
+			for (p = 0; p < polygon->PointN; p++) {
+				PointTypePtr point = &polygon->Points[p];
+
+				if (hole < polygon->HoleIndexN && p == polygon->HoleIndex[hole]) {
+					if (hole > 0)
+						fputs("\n\t\t)", FP);
+					fputs("\n\t\tHole (", FP);
+					hole++;
+					i = 0;
+				}
+
+				if (i++ % 5 == 0) {
+					fputs("\n\t\t", FP);
+					if (hole)
+						fputs("\t", FP);
+				}
+				pcb_fprintf(FP, "[%[0] %[0]] ", point->X, point->Y);
+			}
+			if (hole > 0)
+				fputs("\n\t\t)", FP);
+			fputs("\n\t)\n", FP);
+		}
+		fputs(")\n", FP);
+	}
+}
+
+/* ---------------------------------------------------------------------------
+ * writes the buffer to file
+ */
+int io_pcb_WriteBuffer(plug_io_t *ctx, FILE * FP, BufferType *buff)
+{
+	pcb_cardinal_t i;
+
+	pcb_printf_slot[0] = ((io_pcb_ctx_t *)(ctx->plugin_data))->write_coord_fmt;
+	WriteViaData(FP, buff->Data);
+	io_pcb_WriteElementData(ctx, FP, buff->Data);
+	for (i = 0; i < max_copper_layer + 2; i++)
+		WriteLayerData(FP, i, &(buff->Data->Layer[i]));
+	return (STATUS_OK);
+}
+
+/* ---------------------------------------------------------------------------
+ * writes PCB to file
+ */
+int io_pcb_WritePCB(plug_io_t *ctx, FILE * FP, const char *old_filename, const char *new_filename, pcb_bool emergency)
+{
+	pcb_cardinal_t i;
+
+	AttributePutToList(&PCB->Attributes, "PCB::loader", ctx->description, 1);
+
+	pcb_printf_slot[0] = ((io_pcb_ctx_t *)(ctx->plugin_data))->write_coord_fmt;
+	WritePCBInfoHeader(FP);
+	WritePCBDataHeader(FP);
+	WritePCBFontData(FP);
+	WriteAttributeList(FP, &PCB->Attributes, "");
+	WriteViaData(FP, PCB->Data);
+	io_pcb_WriteElementData(ctx, FP, PCB->Data);
+	WritePCBRatData(FP);
+	for (i = 0; i < max_copper_layer + 2; i++)
+		WriteLayerData(FP, i, &(PCB->Data->Layer[i]));
+	WritePCBNetlistData(FP);
+	WritePCBNetlistPatchData(FP);
+
+	return (STATUS_OK);
+}
+
+/* ---------------------------------------------------------------------------
+ * functions for loading elements-as-pcb
+ */
+
+extern PCBTypePtr yyPCB;
+extern DataTypePtr yyData;
+extern FontTypePtr yyFont;
+
+void PreLoadElementPCB()
+{
+
+	if (!yyPCB)
+		return;
+
+	yyFont = &yyPCB->Font;
+	yyData = yyPCB->Data;
+	yyData->pcb = yyPCB;
+	yyData->LayerN = 0;
+}
+
+void PostLoadElementPCB()
+{
+	PCBTypePtr pcb_save = PCB;
+	ElementTypePtr e;
+
+	if (!yyPCB)
+		return;
+
+	CreateNewPCBPost(yyPCB, 0);
+	ParseGroupString("1,c:2,s", &yyPCB->LayerGroups, yyData->LayerN);
+	e = elementlist_first(&yyPCB->Data->Element);	/* we know there's only one */
+	PCB = yyPCB;
+	MoveElementLowLevel(yyPCB->Data, e, -e->BoundingBox.X1, -e->BoundingBox.Y1);
+	PCB = pcb_save;
+	yyPCB->MaxWidth = e->BoundingBox.X2;
+	yyPCB->MaxHeight = e->BoundingBox.Y2;
+	yyPCB->is_footprint = 1;
+}
diff --git a/src_plugins/io_pcb/file.h b/src_plugins/io_pcb/file.h
new file mode 100644
index 0000000..a465d76
--- /dev/null
+++ b/src_plugins/io_pcb/file.h
@@ -0,0 +1,62 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for file routines
+ */
+
+#ifndef	PCB_FILE_H
+#define	PCB_FILE_H
+
+#include <stdio.h>							/* needed to define 'FILE *' */
+#include "global.h"
+#include "plug_io.h"
+
+typedef struct {
+	const char *write_coord_fmt;
+} io_pcb_ctx_t;
+
+int io_pcb_WriteBuffer(plug_io_t *ctx, FILE *f, BufferType *buff);
+int io_pcb_WriteElementData(plug_io_t *ctx, FILE *f, DataTypePtr);
+int io_pcb_WritePCB(plug_io_t *ctx, FILE *f, const char *old_filename, const char *new_filename, pcb_bool emergency);
+
+void PreLoadElementPCB(void);
+void PostLoadElementPCB(void);
+
+/* 
+ * Whenever the pcb file format is modified, this version number
+ * should be updated to the date when the new code is committed.
+ * It will be written out to the file and also used by pcb to give
+ * guidance to the user as to what the minimum version of pcb required
+ * is.
+ */
+
+/* This is the version needed by the file we're saving.  */
+int PCBFileVersionNeeded(void);
+
+/* This is the version we support.  */
+#define PCB_FILE_VERSION 20110603
+
+#endif
diff --git a/src_plugins/io_pcb/flags.c b/src_plugins/io_pcb/flags.c
new file mode 100644
index 0000000..3b6a8cf
--- /dev/null
+++ b/src_plugins/io_pcb/flags.c
@@ -0,0 +1,68 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2005 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+#include "strflags.h"
+#include "flags.h"
+#include "const.h"
+#include "macro.h"
+
+#define N(x) x, sizeof(x)-1
+static FlagBitsType pcb_flagbits[] = {
+	{SHOWNUMBERFLAG, N("shownumber"), 1},
+	{LOCALREFFLAG, N("localref"), 1},
+	{CHECKPLANESFLAG, N("checkplanes"), 1},
+	{SHOWPCB_FLAG_DRC, N("showdrc"), 1},
+	{RUBBERBANDFLAG, N("rubberband"), 1},
+	{DESCRIPTIONFLAG, N("description"), 1},
+	{NAMEONPCBFLAG, N("nameonpcb"), 1},
+	{AUTOPCB_FLAG_DRC, N("autodrc"), 1},
+	{ALLDIRECTIONFLAG, N("alldirection"), 1},
+	{SWAPSTARTDIRFLAG, N("swapstartdir"), 1},
+	{UNIQUENAMEFLAG, N("uniquename"), 1},
+	{CLEARNEWFLAG, N("clearnew"), 1},
+	{NEWPCB_FLAG_FULLPOLY, N("newfullpoly"), 1},
+	{SNAPPCB_FLAG_PIN, N("snappin"), 1},
+	{SHOWMASKFLAG, N("showmask"), 1},
+	{THINDRAWFLAG, N("thindraw"), 1},
+	{ORTHOMOVEFLAG, N("orthomove"), 1},
+	{LIVEROUTEFLAG, N("liveroute"), 1},
+	{THINDRAWPOLYFLAG, N("thindrawpoly"), 1},
+	{LOCKNAMESFLAG, N("locknames"), 1},
+	{ONLYNAMESFLAG, N("onlynames"), 1},
+	{HIDENAMESFLAG, N("hidenames"), 1},
+	{ENABLEPCB_FLAG_MINCUT, N("enablemincut"), 1},
+};
+#undef N
+
+char *pcbflags_to_string(FlagType flags)
+{
+	return common_flags_to_string(flags, PCB_TYPEMASK_ALL, pcb_flagbits, ENTRIES(pcb_flagbits));
+}
+
+FlagType string_to_pcbflags(const char *flagstring, int (*error) (const char *msg))
+{
+	return common_string_to_flags(flagstring, error, pcb_flagbits, ENTRIES(pcb_flagbits));
+}
diff --git a/src_plugins/io_pcb/flags.h b/src_plugins/io_pcb/flags.h
new file mode 100644
index 0000000..8a7605b
--- /dev/null
+++ b/src_plugins/io_pcb/flags.h
@@ -0,0 +1,117 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+char *pcbflags_to_string(FlagType flags);
+FlagType string_to_pcbflags(const char *flagstring, int (*error) (const char *msg));
+
+/* ---------------------------------------------------------------------------
+ * PCB flags - kept only for file format compatibility reasons; these bits
+   should be a mirror of booleans from the conf.
+ */
+
+/* %start-doc pcbfile ~pcbflags
+ at node PCBFlags
+ at section PCBFlags
+ at table @code
+ at item 0x00001
+Pinout displays pin numbers instead of pin names.
+ at item 0x00002
+Use local reference for moves, by setting the mark at the beginning of
+each move.
+ at item 0x00004
+When set, only polygons and their clearances are drawn, to see if
+polygons have isolated regions.
+ at item 0x00008
+Display DRC region on crosshair.
+ at item 0x00010
+Do all move, mirror, rotate with rubberband connections.
+ at item 0x00020
+Display descriptions of elements, instead of refdes.
+ at item 0x00040
+Display names of elements, instead of refdes.
+ at item 0x00080
+Auto-DRC flag.  When set, PCB doesn't let you place copper that
+violates DRC.
+ at item 0x00100
+Enable 'all-direction' lines.
+ at item 0x00200
+Switch starting angle after each click.
+ at item 0x00400
+Force unique names on board.
+ at item 0x00800
+New lines/arc clear polygons.
+ at item 0x01000
+Crosshair snaps to pins and pads.
+ at item 0x02000
+Show the solder mask layer.
+ at item 0x04000
+Draw with thin lines.
+ at item 0x08000
+Move items orthogonally.
+ at item 0x10000
+Draw autoroute paths real-time.
+ at item 0x20000
+New polygons are full ones.
+ at item 0x40000
+Names are locked, the mouse cannot select them.
+ at item 0x80000
+Everything but names are locked, the mouse cannot select anything else.
+ at item 0x100000
+New polygons are full polygons.
+ at item 0x200000
+When set, element names are not drawn.
++ at item 0x800000
++snap to certain off-grid points.
++ at item 0x1000000
++highlight lines and arcs when the crosshair is on one of their endpoints.
+ at end table
+%end-doc */
+
+#define PCB_FLAGS               0x01ffffff	/* all used flags */
+
+#define SHOWNUMBERFLAG          0x00000001
+#define LOCALREFFLAG            0x00000002
+#define CHECKPLANESFLAG         0x00000004
+#define SHOWPCB_FLAG_DRC             0x00000008
+#define RUBBERBANDFLAG          0x00000010
+#define	DESCRIPTIONFLAG         0x00000020
+#define	NAMEONPCBFLAG           0x00000040
+#define AUTOPCB_FLAG_DRC             0x00000080
+#define	ALLDIRECTIONFLAG        0x00000100
+#define SWAPSTARTDIRFLAG        0x00000200
+#define UNIQUENAMEFLAG          0x00000400
+#define CLEARNEWFLAG            0x00000800
+#define SNAPPCB_FLAG_PIN             0x00001000
+#define SHOWMASKFLAG            0x00002000
+#define THINDRAWFLAG            0x00004000
+#define ORTHOMOVEFLAG           0x00008000
+#define LIVEROUTEFLAG           0x00010000
+#define THINDRAWPOLYFLAG        0x00020000
+#define LOCKNAMESFLAG           0x00040000
+#define ONLYNAMESFLAG           0x00080000
+#define NEWPCB_FLAG_FULLPOLY         0x00100000
+#define HIDENAMESFLAG           0x00200000
+#define ENABLEPCB_FLAG_MINCUT        0x00400000
diff --git a/src_plugins/io_pcb/io_pcb.c b/src_plugins/io_pcb/io_pcb.c
new file mode 100644
index 0000000..5eaba41
--- /dev/null
+++ b/src_plugins/io_pcb/io_pcb.c
@@ -0,0 +1,100 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This module, io_pcb, was written and is Copyright (C) 2016 by Tibor Palinkas
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "global.h"
+#include "plugins.h"
+#include "parse_common.h"
+#include "file.h"
+
+static plug_io_t io_pcb[3];
+static io_pcb_ctx_t ctx[3];
+
+int io_pcb_fmt(plug_io_t *ctx, plug_iot_t typ, int wr, const char *fmt)
+{
+	if (strcmp(ctx->description, fmt) == 0)
+		return 200;
+
+	/* All types are supported. */
+	if (strcmp(fmt, "pcb") != 0)
+		return 0;
+	return 100; /* read-write */
+}
+
+extern void pcb_lex_destroy(void);
+static void hid_io_pcb_uninit(void)
+{
+	int n;
+	pcb_lex_destroy();
+
+	for(n = 0; n < 3; n++)
+		HOOK_UNREGISTER(plug_io_t, plug_io_chain, &(io_pcb[n]));
+}
+
+pcb_uninit_t hid_io_pcb_init(void)
+{
+
+	memset(&io_pcb, 0, sizeof(io_pcb));
+
+
+	/* register the IO hook */
+	ctx[0].write_coord_fmt = pcb_printf_slot[8];
+	io_pcb[0].plugin_data = &ctx[0];
+	io_pcb[0].fmt_support_prio = io_pcb_fmt;
+	io_pcb[0].parse_pcb = io_pcb_ParsePCB;
+	io_pcb[0].parse_element = io_pcb_ParseElement;
+	io_pcb[0].parse_font = io_pcb_ParseFont;
+	io_pcb[0].write_buffer = io_pcb_WriteBuffer;
+	io_pcb[0].write_element = io_pcb_WriteElementData;
+	io_pcb[0].write_pcb = io_pcb_WritePCB;
+	io_pcb[0].default_fmt = "pcb";
+	io_pcb[0].description = "geda/pcb - mainline (centimils)";
+	io_pcb[0].save_preference_prio = 100;
+	HOOK_REGISTER(plug_io_t, plug_io_chain, &(io_pcb[0]));
+
+	ctx[1].write_coord_fmt = pcb_printf_slot[9];
+	io_pcb[1].plugin_data = &ctx[1];
+	io_pcb[1].fmt_support_prio = io_pcb_fmt;
+	io_pcb[1].write_buffer = io_pcb_WriteBuffer;
+	io_pcb[1].write_element = io_pcb_WriteElementData;
+	io_pcb[1].write_pcb = io_pcb_WritePCB;
+	io_pcb[1].default_fmt = "pcb";
+	io_pcb[1].description = "geda/pcb - readable units";
+	io_pcb[1].save_preference_prio = 99;
+	HOOK_REGISTER(plug_io_t, plug_io_chain, &(io_pcb[1]));
+
+	ctx[2].write_coord_fmt = "%$$mn";
+	io_pcb[2].plugin_data = &ctx[2];
+	io_pcb[2].fmt_support_prio = io_pcb_fmt;
+	io_pcb[2].write_buffer = io_pcb_WriteBuffer;
+	io_pcb[2].write_element = io_pcb_WriteElementData;
+	io_pcb[2].write_pcb = io_pcb_WritePCB;
+	io_pcb[2].default_fmt = "pcb";
+	io_pcb[2].description = "geda/pcb - nanometer";
+	io_pcb[2].save_preference_prio = 98;
+	HOOK_REGISTER(plug_io_t, plug_io_chain, &(io_pcb[2]));
+
+	return hid_io_pcb_uninit;
+}
diff --git a/src_plugins/io_pcb/parse_common.h b/src_plugins/io_pcb/parse_common.h
new file mode 100644
index 0000000..6f864e2
--- /dev/null
+++ b/src_plugins/io_pcb/parse_common.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ *  RCS: $Id$
+ */
+
+/* just defines common parser identifiers
+ */
+
+#ifndef	PCB_LEX_H
+#define	PCB_LEX_H
+
+#include "global.h"
+#include "plug_io.h"
+
+int io_pcb_ParsePCB(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest);
+int io_pcb_ParseElement(plug_io_t *ctx, DataTypePtr, const char *);
+int io_pcb_ParseFont(plug_io_t *ctx, FontTypePtr, const char *);
+
+#endif
diff --git a/src_plugins/io_pcb/parse_l.c b/src_plugins/io_pcb/parse_l.c
new file mode 100644
index 0000000..34993df
--- /dev/null
+++ b/src_plugins/io_pcb/parse_l.c
@@ -0,0 +1,2602 @@
+#line 2 "parse_l.c"
+
+#line 4 "parse_l.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer pcb__create_buffer
+#define yy_delete_buffer pcb__delete_buffer
+#define yy_flex_debug pcb__flex_debug
+#define yy_init_buffer pcb__init_buffer
+#define yy_flush_buffer pcb__flush_buffer
+#define yy_load_buffer_state pcb__load_buffer_state
+#define yy_switch_to_buffer pcb__switch_to_buffer
+#define yyin pcb_in
+#define yyleng pcb_leng
+#define yylex pcb_lex
+#define yylineno pcb_lineno
+#define yyout pcb_out
+#define yyrestart pcb_restart
+#define yytext pcb_text
+#define yywrap pcb_wrap
+#define yyalloc pcb_alloc
+#define yyrealloc pcb_realloc
+#define yyfree pcb_free
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE pcb_restart(pcb_in  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int pcb_leng;
+
+extern FILE *pcb_in, *pcb_out;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+     *       access to the local variable yy_act. Since yyless() is a macro, it would break
+     *       existing scanners that call yyless() from OUTSIDE pcb_lex. 
+     *       One obvious solution it to make yy_act a global. I tried that, and saw
+     *       a 5% performance hit in a non-pcb_lineno scanner, because yy_act is
+     *       normally declared as a register variable-- so it is not worth it.
+     */
+    #define  YY_LESS_LINENO(n) \
+            do { \
+                int yyl;\
+                for ( yyl = n; yyl < pcb_leng; ++yyl )\
+                    if ( pcb_text[yyl] == '\n' )\
+                        --pcb_lineno;\
+            }while(0)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up pcb_text. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up pcb_text again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via pcb_restart()), so that the user can continue scanning by
+	 * just pointing pcb_in at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when pcb_text is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int pcb_leng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow pcb_wrap()'s to do buffer switches
+ * instead of setting up a fresh pcb_in.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void pcb_restart (FILE *input_file  );
+void pcb__switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE pcb__create_buffer (FILE *file,int size  );
+void pcb__delete_buffer (YY_BUFFER_STATE b  );
+void pcb__flush_buffer (YY_BUFFER_STATE b  );
+void pcb_push_buffer_state (YY_BUFFER_STATE new_buffer  );
+void pcb_pop_buffer_state (void );
+
+static void pcb_ensure_buffer_stack (void );
+static void pcb__load_buffer_state (void );
+static void pcb__init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER pcb__flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE pcb__scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE pcb__scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE pcb__scan_bytes (yyconst char *bytes,int len  );
+
+void *pcb_alloc (yy_size_t  );
+void *pcb_realloc (void *,yy_size_t  );
+void pcb_free (void *  );
+
+#define yy_new_buffer pcb__create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        pcb_ensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            pcb__create_buffer(pcb_in,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        pcb_ensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            pcb__create_buffer(pcb_in,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *pcb_in = (FILE *) 0, *pcb_out = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int pcb_lineno;
+
+int pcb_lineno = 1;
+
+extern char *pcb_text;
+#define yytext_ptr pcb_text
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up pcb_text.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+	pcb_leng = (size_t) (yy_cp - yy_bp); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+	(yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 55
+#define YY_END_OF_BUFFER 56
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[222] =
+    {   0,
+        0,    0,   56,   54,   51,   52,   53,   54,   50,   54,
+       54,   47,   47,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   39,   54,   54,   51,    0,   49,    0,
+       50,    0,   47,   47,   46,    0,   47,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,   44,   40,    0,   38,
+       36,   37,   45,   46,   48,   27,    0,    0,    0,    7,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,   29,
+
+        2,   11,   10,    0,   14,    0,    0,    0,    0,    0,
+       12,    0,    0,    0,    0,   43,    0,    0,    0,    0,
+        0,    0,    0,    3,    0,   26,    0,   13,   22,    0,
+        0,    0,    0,    0,   16,    0,    0,    0,   42,    0,
+       41,    0,    0,    0,    0,    0,    8,    0,    9,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    4,    0,    0,   23,    0,    0,    0,    0,   24,
+       21,    0,    0,    0,    0,    0,   30,   19,    0,   28,
+        0,   25,    0,    0,    5,    0,    0,    0,    0,    0,
+        0,    0,    0,    6,    0,    0,   32,    0,   33,   35,
+
+        0,    0,    0,    0,   15,    0,    0,   18,    0,    0,
+        0,   20,    0,   17,    1,    0,    0,   31,    0,   34,
+        0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    5,    6,    1,    1,    1,    7,    1,
+        1,    1,    8,    1,    8,    9,    1,   10,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,    1,    1,    1,
+        1,    1,    1,    1,   12,   13,   14,   15,   16,   17,
+       18,   19,    1,    1,    1,   20,   21,   22,    1,   23,
+        1,   24,   25,   26,    1,   27,    1,    1,    1,    1,
+        1,   28,    1,    1,   29,    1,   30,   31,   32,   33,
+
+       34,   35,   36,   37,   38,    1,   39,   40,   41,   42,
+       43,   44,    1,   45,   46,   47,   48,    1,    1,   49,
+       50,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[51] =
+    {   0,
+        1,    1,    2,    3,    1,    1,    1,    1,    1,    4,
+        4,    4,    4,    4,    4,    4,    4,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    4,
+        4,    4,    4,    4,    4,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int16_t yy_base[226] =
+    {   0,
+        0,    0,  256,  257,  253,  257,  257,   46,    0,    0,
+       42,   45,   46,   13,   16,  230,  213,   23,  207,  208,
+       32,  220,  215,   52,   35,   21,   38,  210,  214,   36,
+      212,  203,  203,   38,  202,  201,  239,   73,  257,  236,
+        0,  232,  229,   74,   70,    0,   77,  205,  189,  193,
+      189,  219,  198,  191,  200,   53,  189,  178,  185,  181,
+      178,  211,  190,  180,  181,  173,  187,  168,  176,  167,
+      181,  184,  180,  182,  173,  170,  257,  257,  169,  257,
+      257,  170,  257,   82,    0,  257,  162,  164,  159,  257,
+      163,  169,  166,  168,  152,  165,  164,  163,  157,  175,
+
+      257,  257,  257,  144,  257,  146,  152,  160,  143,  144,
+      257,  159,  145,  146,  156,  257,  144,  145,  148,  138,
+      146,  152,  132,  257,  133,  257,  131,  257,  257,  137,
+       77,  144,  139,  129,  257,  130,  138,  133,  257,  136,
+      257,  136,  134,  120,  122,  129,  257,  116,  257,  115,
+      115,  116,  116,  111,  116,  125,  111,  119,  109,  103,
+      103,  257,  102,  103,  257,  100,  112,  103,  108,  257,
+      123,  102,   99,  111,   97,   91,  257,   85,   91,  113,
+      105,  257,   94,   95,  257,   90,  101,   88,   95,   83,
+       89,   88,   93,  257,   85,   74,  257,   68,  257,  257,
+
+       80,   69,   67,   62,  257,   74,   60,  257,   72,   62,
+       71,  257,   57,  257,  257,   63,   61,  257,   67,  257,
+      257,  113,  117,  121,   63
+    } ;
+
+static yyconst flex_int16_t yy_def[226] =
+    {   0,
+      221,    1,  221,  221,  221,  221,  221,  222,  223,  224,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  222,  221,  222,
+      223,  221,  221,  221,  221,  225,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  225,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+        0,  221,  221,  221,  221
+    } ;
+
+static yyconst flex_int16_t yy_nxt[308] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,    4,   12,
+       13,   14,    4,   15,   16,   17,   18,   19,   20,   21,
+       22,   23,   24,   25,   26,   27,   28,    4,    4,   29,
+        4,   30,   31,    4,    4,    4,    4,   32,   33,    4,
+       34,   35,    4,    4,    4,    4,    4,   36,    4,    4,
+       39,   43,   44,   45,   45,   47,   47,   48,   50,   49,
+       54,   58,   55,   51,   66,   62,   85,   68,   67,   59,
+       69,   70,   74,   40,   71,   79,   75,   39,   80,   84,
+       84,   63,   45,   47,   47,   45,   47,   47,  151,   64,
+       94,   84,   84,   46,   65,   95,  190,  220,  219,  218,
+
+       40,  217,  216,  215,  191,  214,  213,  212,  211,  210,
+      209,  208,  152,   38,  207,  206,   38,   41,  205,   41,
+       41,   42,  204,   42,   42,  203,  202,  201,  200,  199,
+      198,  197,  196,  195,  194,  193,  192,  189,  188,  187,
+      186,  185,  184,  183,  182,  181,  180,  179,  178,  177,
+      176,  175,  174,  173,  172,  171,  170,  169,  168,  167,
+      166,  165,  164,  163,  162,  161,  160,  159,  158,  157,
+      156,  155,  154,  153,  150,  149,  148,  147,  146,  145,
+      144,  143,  142,  141,  140,  139,  138,  137,  136,  135,
+      134,  133,  132,  131,  130,  129,  128,  127,  126,  125,
+
+      124,  123,  122,  121,  120,  119,  118,  117,  116,  115,
+      114,  113,  112,  111,  110,  109,  108,  107,  106,  105,
+      104,  103,  102,  101,  100,   99,   98,   97,   96,   93,
+       92,   91,   90,   89,   88,   87,   86,   45,   83,   38,
+       37,   82,   81,   78,   77,   76,   73,   72,   61,   60,
+       57,   56,   53,   52,   37,  221,    3,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+
+      221,  221,  221,  221,  221,  221,  221
+    } ;
+
+static yyconst flex_int16_t yy_chk[308] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        8,   11,   11,   12,   13,   13,   13,   14,   15,   14,
+       18,   21,   18,   15,   25,   24,  225,   26,   25,   21,
+       26,   27,   30,    8,   27,   34,   30,   38,   34,   45,
+       45,   24,   44,   44,   44,   47,   47,   47,  131,   24,
+       56,   84,   84,   12,   24,   56,  178,  219,  217,  216,
+
+       38,  213,  211,  210,  178,  209,  207,  206,  204,  203,
+      202,  201,  131,  222,  198,  196,  222,  223,  195,  223,
+      223,  224,  193,  224,  224,  192,  191,  190,  189,  188,
+      187,  186,  184,  183,  181,  180,  179,  176,  175,  174,
+      173,  172,  171,  169,  168,  167,  166,  164,  163,  161,
+      160,  159,  158,  157,  156,  155,  154,  153,  152,  151,
+      150,  148,  146,  145,  144,  143,  142,  140,  138,  137,
+      136,  134,  133,  132,  130,  127,  125,  123,  122,  121,
+      120,  119,  118,  117,  115,  114,  113,  112,  110,  109,
+      108,  107,  106,  104,  100,   99,   98,   97,   96,   95,
+
+       94,   93,   92,   91,   89,   88,   87,   82,   79,   76,
+       75,   74,   73,   72,   71,   70,   69,   68,   67,   66,
+       65,   64,   63,   62,   61,   60,   59,   58,   57,   55,
+       54,   53,   52,   51,   50,   49,   48,   43,   42,   40,
+       37,   36,   35,   33,   32,   31,   29,   28,   23,   22,
+       20,   19,   17,   16,    5,    3,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+
+      221,  221,  221,  221,  221,  221,  221
+    } ;
+
+/* Table of booleans, true if rule could match eol. */
+static yyconst flex_int32_t yy_rule_can_match_eol[56] =
+    {   0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,     };
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int pcb__flex_debug;
+int pcb__flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *pcb_text;
+#line 1 "parse_l.l"
+#line 4 "parse_l.l"
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* lexical definitions to parse ASCII input of PCB and Element description
+ */
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <locale.h>
+
+#if defined(_POSIX_SOURCE) || defined(_HPUX_SOURCE)
+#include <unistd.h>
+#endif
+
+#include "global.h"
+#include "flags.h"
+
+#ifdef HAVE_LIBDMALLOC
+# include <dmalloc.h> /* see http://dmalloc.com */
+#endif
+
+
+
+
+#include "global.h"
+#include "crosshair.h"
+#include "data.h"
+#include "error.h"
+#include "file.h"
+#include "mymem.h"
+#include "misc.h"
+#include "strflags.h"
+#include "parse_common.h"
+#include "parse_y.h"
+#include "create.h"
+#include "plug_footprint.h"
+#include "attribs.h"
+#include "compat_misc.h"
+
+#define YY_NO_INPUT
+
+/* ---------------------------------------------------------------------------
+ * some shared parser identifiers
+ */
+#ifdef FLEX_SCANNER
+
+#define yyunput ATTRIBUTE_UNUSED yyunput
+#endif
+
+const char			*yyfilename;	/* in this file */
+PCBTypePtr		yyPCB;			/* used by parser */
+DataTypePtr		yyData;
+ElementTypePtr		yyElement;
+FontTypePtr		yyFont;
+conf_role_t yy_settings_dest;
+FlagType yy_pcb_flags;
+
+static int parse_number (void);
+
+/* ---------------------------------------------------------------------------
+ * an external prototypes
+ */
+int	yyparse(void);
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static int Parse(FILE *Pipe, const char *Executable, const char *Path, const char *Filename, const char *Parse_parameter);
+
+#line 741 "parse_l.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int pcb_lex_destroy (void );
+
+int pcb_get_debug (void );
+
+void pcb_set_debug (int debug_flag  );
+
+YY_EXTRA_TYPE pcb_get_extra (void );
+
+void pcb_set_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *pcb_get_in (void );
+
+void pcb_set_in  (FILE * in_str  );
+
+FILE *pcb_get_out (void );
+
+void pcb_set_out  (FILE * out_str  );
+
+int pcb_get_leng (void );
+
+char *pcb_get_text (void );
+
+int pcb_get_lineno (void );
+
+void pcb_set_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int pcb_wrap (void );
+#else
+extern int pcb_wrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+    
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( pcb_text, pcb_leng, 1, pcb_out )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( pcb_in )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( pcb_in ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, pcb_in))==0 && ferror(pcb_in)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(pcb_in); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int pcb_lex (void);
+
+#define YY_DECL int pcb_lex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after pcb_text and pcb_leng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    
+#line 116 "parse_l.l"
+
+
+#line 931 "parse_l.c"
+
+	if ( !(yy_init) )
+		{
+		(yy_init) = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! pcb_in )
+			pcb_in = stdin;
+
+		if ( ! pcb_out )
+			pcb_out = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			pcb_ensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				pcb__create_buffer(pcb_in,YY_BUF_SIZE );
+		}
+
+		pcb__load_buffer_state( );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of pcb_text. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = (yy_start);
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				(yy_last_accepting_state) = yy_current_state;
+				(yy_last_accepting_cpos) = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 222 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 257 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+		if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+			{
+			int yyl;
+			for ( yyl = 0; yyl < pcb_leng; ++yyl )
+				if ( pcb_text[yyl] == '\n' )
+					   
+    pcb_lineno++;
+;
+			}
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = (yy_hold_char);
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 118 "parse_l.l"
+{ return(T_FILEVERSION); }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 119 "parse_l.l"
+{ return(T_PCB); }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 120 "parse_l.l"
+{ return(T_GRID); }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 121 "parse_l.l"
+{ return(T_CURSOR); }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 122 "parse_l.l"
+{ return(T_THERMAL); }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 123 "parse_l.l"
+{ return(T_AREA); }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 124 "parse_l.l"
+{ return(T_DRC); }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 125 "parse_l.l"
+{ return(T_FLAGS); }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 126 "parse_l.l"
+{ return(T_LAYER); }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 127 "parse_l.l"
+{ return(T_PIN); }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 128 "parse_l.l"
+{ return(T_PAD); }
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 129 "parse_l.l"
+{ return(T_VIA); }
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 130 "parse_l.l"
+{ return(T_LINE); }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 131 "parse_l.l"
+{ return(T_RAT); }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 132 "parse_l.l"
+{ return(T_RECTANGLE); }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 133 "parse_l.l"
+{ return(T_TEXT); }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 134 "parse_l.l"
+{ return(T_ELEMENTLINE); }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 135 "parse_l.l"
+{ return(T_ELEMENTARC); }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 136 "parse_l.l"
+{ return(T_ELEMENT); }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 137 "parse_l.l"
+{ return(T_SYMBOLLINE); }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 138 "parse_l.l"
+{ return(T_SYMBOL); }
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 139 "parse_l.l"
+{ return(T_MARK); }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 140 "parse_l.l"
+{ return(T_GROUPS); }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 141 "parse_l.l"
+{ return(T_STYLES); }
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 142 "parse_l.l"
+{ return(T_POLYGON); }
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 143 "parse_l.l"
+{ return(T_POLYGON_HOLE); }
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 144 "parse_l.l"
+{ return(T_ARC); }
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 145 "parse_l.l"
+{ return(T_NETLIST); }
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 146 "parse_l.l"
+{ return(T_NET); }
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 147 "parse_l.l"
+{ return(T_CONN); }
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 148 "parse_l.l"
+{ return(T_NETLISTPATCH); }
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 149 "parse_l.l"
+{ return(T_ADD_CONN); }
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 150 "parse_l.l"
+{ return(T_DEL_CONN); }
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 151 "parse_l.l"
+{ return(T_CHANGE_ATTRIB); }
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 152 "parse_l.l"
+{ return(T_ATTRIBUTE); }
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 154 "parse_l.l"
+{ return T_NM; }
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 155 "parse_l.l"
+{ return T_UM; }
+	YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 156 "parse_l.l"
+{ return T_MM; }
+	YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 157 "parse_l.l"
+{ return T_M; }
+	YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 158 "parse_l.l"
+{ return T_KM; }
+	YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 159 "parse_l.l"
+{ return T_UMIL; }
+	YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 160 "parse_l.l"
+{ return T_CMIL; }
+	YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 161 "parse_l.l"
+{ return T_MIL; }
+	YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 162 "parse_l.l"
+{ return T_IN; }
+	YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 164 "parse_l.l"
+{
+						pcb_lval.integer = (unsigned) *(pcb_text+1);
+						return(CHAR_CONST);
+					}
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 168 "parse_l.l"
+{	return parse_number(); }
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 169 "parse_l.l"
+{	pcb_lval.integer = pcb_round (strtod (pcb_text, NULL)); return INTEGER; }
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 171 "parse_l.l"
+{	unsigned n;
+				sscanf((char *) pcb_text, "%x", &n);
+				pcb_lval.integer = n;
+				return INTEGER;
+					}
+	YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 176 "parse_l.l"
+{
+						char	*p1, *p2;
+
+							/* return NULL on empty string */
+						if (pcb_leng == 2)
+						{
+							pcb_lval.string = NULL;
+							return(STRING);
+						}
+
+							/* allocate memory and copy string;
+							 * stringlength is counted and copied without
+							 * leading and trailing '"'
+							 */
+						pcb_leng -= 2;
+						pcb_lval.string = (char *)calloc (pcb_leng+1, sizeof (char));
+						p1 = (char *) (pcb_text +1);
+						p2 = pcb_lval.string;
+						while(pcb_leng--)
+						{
+								/* check for special character */
+							if (*p1 == '\\')
+							{
+								pcb_leng--;
+								p1++;
+
+							}
+							*p2++ = *p1++;
+						}
+						*p2 = '\0';
+						return(STRING);
+					}
+	YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 208 "parse_l.l"
+{}
+	YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 209 "parse_l.l"
+{}
+	YY_BREAK
+case 52:
+/* rule 52 can match eol */
+YY_RULE_SETUP
+#line 210 "parse_l.l"
+{
+#ifndef FLEX_SCANNER
+						pcb_lineno++;
+#endif
+					}
+	YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 215 "parse_l.l"
+{}
+	YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 216 "parse_l.l"
+{ return(*pcb_text); }
+	YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 218 "parse_l.l"
+ECHO;
+	YY_BREAK
+#line 1342 "parse_l.c"
+case YY_STATE_EOF(INITIAL):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed pcb_in at a new source and called
+			 * pcb_lex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = pcb_in;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = (yy_c_buf_p);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( pcb_wrap( ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * pcb_text, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of pcb_lex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+    	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = (yytext_ptr);
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					pcb_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), (size_t) num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			pcb_restart(pcb_in  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) pcb_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			(yy_last_accepting_state) = yy_current_state;
+			(yy_last_accepting_cpos) = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 222 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+	register int yy_is_jam;
+    	register char *yy_cp = (yy_c_buf_p);
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		(yy_last_accepting_state) = yy_current_state;
+		(yy_last_accepting_cpos) = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 222 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 221);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+	register char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+	/* undo effects of setting up pcb_text */
+	*yy_cp = (yy_hold_char);
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = (yy_n_chars) + 2;
+		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		register char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+    if ( c == '\n' ){
+        --pcb_lineno;
+    }
+
+	(yytext_ptr) = yy_bp;
+	(yy_hold_char) = *yy_cp;
+	(yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+	int c;
+    
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (yy_c_buf_p) - (yytext_ptr);
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					pcb_restart(pcb_in );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( pcb_wrap( ) )
+						return EOF;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve pcb_text */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+	if ( c == '\n' )
+		   
+    pcb_lineno++;
+;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void pcb_restart  (FILE * input_file )
+{
+    
+	if ( ! YY_CURRENT_BUFFER ){
+        pcb_ensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            pcb__create_buffer(pcb_in,YY_BUF_SIZE );
+	}
+
+	pcb__init_buffer(YY_CURRENT_BUFFER,input_file );
+	pcb__load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void pcb__switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		pcb_pop_buffer_state();
+	 *		pcb_push_buffer_state(new_buffer);
+     */
+	pcb_ensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	pcb__load_buffer_state( );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (pcb_wrap()) processing, but the only time this flag
+	 * is looked at is after pcb_wrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void pcb__load_buffer_state  (void)
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	pcb_in = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE pcb__create_buffer  (FILE * file, int  size )
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) pcb_alloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in pcb__create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) pcb_alloc(b->yy_buf_size + 2  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in pcb__create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	pcb__init_buffer(b,file );
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with pcb__create_buffer()
+ * 
+ */
+    void pcb__delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		pcb_free((void *) b->yy_ch_buf  );
+
+	pcb_free((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a pcb_restart() or at EOF.
+ */
+    static void pcb__init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+	int oerrno = errno;
+    
+	pcb__flush_buffer(b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then pcb__init_buffer was _probably_
+     * called from pcb_restart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void pcb__flush_buffer (YY_BUFFER_STATE  b )
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		pcb__load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void pcb_push_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+    	if (new_buffer == NULL)
+		return;
+
+	pcb_ensure_buffer_stack();
+
+	/* This block is copied from pcb__switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from pcb__switch_to_buffer. */
+	pcb__load_buffer_state( );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void pcb_pop_buffer_state (void)
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	pcb__delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		pcb__load_buffer_state( );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void pcb_ensure_buffer_stack (void)
+{
+	int num_to_alloc;
+    
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		(yy_buffer_stack) = (struct yy_buffer_state**)pcb_alloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in pcb_ensure_buffer_stack()" );
+								  
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)pcb_realloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in pcb_ensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE pcb__scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) pcb_alloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in pcb__scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	pcb__switch_to_buffer(b  );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to pcb_lex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       pcb__scan_bytes() instead.
+ */
+YY_BUFFER_STATE pcb__scan_string (yyconst char * yystr )
+{
+    
+	return pcb__scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to pcb_lex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE pcb__scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) pcb_alloc(n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in pcb__scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = pcb__scan_buffer(buf,n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in pcb__scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up pcb_text. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		pcb_text[pcb_leng] = (yy_hold_char); \
+		(yy_c_buf_p) = pcb_text + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		pcb_leng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int pcb_get_lineno  (void)
+{
+        
+    return pcb_lineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *pcb_get_in  (void)
+{
+        return pcb_in;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *pcb_get_out  (void)
+{
+        return pcb_out;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int pcb_get_leng  (void)
+{
+        return pcb_leng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *pcb_get_text  (void)
+{
+        return pcb_text;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void pcb_set_lineno (int  line_number )
+{
+    
+    pcb_lineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see pcb__switch_to_buffer
+ */
+void pcb_set_in (FILE *  in_str )
+{
+        pcb_in = in_str ;
+}
+
+void pcb_set_out (FILE *  out_str )
+{
+        pcb_out = out_str ;
+}
+
+int pcb_get_debug  (void)
+{
+        return pcb__flex_debug;
+}
+
+void pcb_set_debug (int  bdebug )
+{
+        pcb__flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from pcb_lex_destroy(), so don't allocate here.
+     */
+
+    /* We do not touch pcb_lineno unless the option is enabled. */
+    pcb_lineno =  1;
+    
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    pcb_in = stdin;
+    pcb_out = stdout;
+#else
+    pcb_in = (FILE *) 0;
+    pcb_out = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * pcb_lex_init()
+     */
+    return 0;
+}
+
+/* pcb_lex_destroy is for both reentrant and non-reentrant scanners. */
+int pcb_lex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		pcb__delete_buffer(YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		pcb_pop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	pcb_free((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * pcb_lex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *pcb_alloc (yy_size_t  size )
+{
+	return (void *) malloc( size );
+}
+
+void *pcb_realloc  (void * ptr, yy_size_t  size )
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void pcb_free (void * ptr )
+{
+	free( (char *) ptr );	/* see pcb_realloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 218 "parse_l.l"
+
+
+
+/* ---------------------------------------------------------------------------
+ * sets up the preprocessor command
+ */
+static int Parse(FILE *Pipe, const char *Executable, const char *Path, const char *Filename, const char *Parse_parameter)
+{
+	static	char	*command = NULL;
+	int		returncode;
+	int		used_popen = 0;
+	char *tmps;
+	size_t l;
+#ifdef FLEX_SCANNER
+	static	pcb_bool	firsttime = pcb_true;
+#endif
+
+	if (Pipe == NULL) {
+	if (EMPTY_STRING_P (Executable))
+	  {
+	    l = 2;
+	    if ( Path != NULL )
+              l += strlen (Path);
+
+            l += strlen (Filename);
+
+	    if ( (tmps = (char *) malloc ( l * sizeof (char))) == NULL)
+              {
+                fprintf (stderr, "Parse():  malloc failed\n");
+                exit (1);
+              }
+
+	    if ( Path != NULL && *Path != '\0')
+              sprintf (tmps, "%s%s%s", Path, PCB_DIR_SEPARATOR_S, Filename);
+            else
+              sprintf (tmps, "%s", Filename);
+
+	    pcb_in = fopen (tmps, "r");
+	    if (!pcb_in)
+	      {
+		return(1);
+	      }
+            free (tmps);
+	  }
+	else
+	  {
+	    used_popen = 1;
+
+	    command = EvaluateFilename(Executable, Path, Filename, Parse_parameter);
+
+	    /* open pipe to stdout of command */
+	    if (*command == '\0' || (pcb_in = popen(command, "r")) == NULL)
+	      {
+		PopenErrorMessage(command);
+		free(command);
+		return(1);
+	      }
+			free(command);
+	  }
+	}
+	else {
+		pcb_in = Pipe;
+	}
+
+#ifdef FLEX_SCANNER
+		/* reset parser if not called the first time */
+	if (!firsttime)
+		pcb_restart(pcb_in);
+	firsttime = pcb_false;
+#endif
+
+		/* init linenumber and filename for yyerror() */
+	pcb_lineno = 1;
+	yyfilename = Filename;
+
+		/* We need to save the data temporarily because lex-yacc are able
+		 * to break the application if the input file has an illegal format.
+		 * It's not necessary if the system supports the call of functions
+		 * on termination.
+		 */
+
+	CreateBeLenient (pcb_true);
+
+#if !defined(HAS_ATEXIT)
+	if (PCB && PCB->Data)
+	  SaveTMPData();
+	returncode = pcb_parse();
+	RemoveTMPData();
+#else
+	returncode = pcb_parse();
+#endif
+	/* clean up parse buffer */
+	pcb__delete_buffer(YY_CURRENT_BUFFER);
+
+	CreateBeLenient (pcb_false);
+
+	if (Pipe != NULL)
+		return returncode;
+
+	if (used_popen)
+	  return(pclose(pcb_in) ? 1 : returncode);
+	return(fclose(pcb_in) ? 1 : returncode);
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes LEX and calls parser for a single element file
+ */
+int io_pcb_ParseElement(plug_io_t *ctx, DataTypePtr Ptr, const char *name)
+{
+	FILE *f;
+	int ret;
+	fp_fopen_ctx_t st;
+
+	yy_settings_dest = CFR_invalid;
+	yyPCB = NULL;
+	yyData = Ptr;
+	yyFont = &PCB->Font;
+	yyElement = NULL;
+
+	f = fp_fopen(fp_default_search_path(), name, &st);
+
+	if (f == NULL)
+		return -1;
+
+	ret = Parse(f, NULL,NULL,NULL,NULL);
+
+	fp_fclose(f, &st);
+
+	return(ret);
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes LEX and calls parser for a complete board
+ */
+#define	TEST_FLAG_LOCAL(F,FLG)		(((FLG) & (F)) ? 1 : 0)
+#define CONF_BOOL_FLAG(F,FLG)	(TEST_FLAG_LOCAL(F,FLG.f) ? "true" : "false")
+
+/* Hack: set a no-save-attribute flag while loading some fields from the file;
+   because we have all these flags set we won't need to list the paths that
+   have hardwired flags again in a "don't save these in attributes" list. */
+#define CONF_NO_ATTRIB(path) \
+do { \
+	conf_native_t *n = conf_get_field(path); \
+	if (n != NULL) \
+		n->random_flags.io_pcb_no_attrib = 1; \
+} while(0) \
+
+#define CONF_SET(target, path, arr_idx, new_val, pol) \
+do { \
+	CONF_NO_ATTRIB(path); \
+	conf_set(target, path, arr_idx, new_val, pol); \
+} while(0) \
+
+int io_pcb_ParsePCB(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest)
+{
+	int retval;
+	yyPCB = Ptr;
+	yyData = NULL;
+	yyFont = NULL;
+	yyElement = NULL;
+	yy_settings_dest = settings_dest;
+	if (settings_dest != CFR_invalid)
+		conf_reset(settings_dest, Filename);
+	setlocale(LC_ALL, "C"); /* make sure numerics are read predictably */
+	retval = Parse(NULL, conf_core.rc.file_command, conf_core.rc.file_path, Filename, NULL);
+	setlocale(LC_ALL, "");
+	if ((settings_dest != CFR_invalid) && (retval == 0)) {
+		/* overwrite settings from the flags, mark them not-to-save */
+		CONF_SET(settings_dest, "plugins/mincut/enable", -1, CONF_BOOL_FLAG(ENABLEPCB_FLAG_MINCUT, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/show_number", -1, CONF_BOOL_FLAG(SHOWNUMBERFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/show_drc", -1, CONF_BOOL_FLAG(SHOWPCB_FLAG_DRC, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/rubber_band_mode", -1, CONF_BOOL_FLAG(RUBBERBANDFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/auto_drc", -1, CONF_BOOL_FLAG(AUTOPCB_FLAG_DRC, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/all_direction_lines", -1, CONF_BOOL_FLAG(ALLDIRECTIONFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/swap_start_direction", -1, CONF_BOOL_FLAG(SWAPSTARTDIRFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/unique_names", -1, CONF_BOOL_FLAG(UNIQUENAMEFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/clear_line", -1, CONF_BOOL_FLAG(CLEARNEWFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/full_poly", -1, CONF_BOOL_FLAG(NEWPCB_FLAG_FULLPOLY, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/snap_pin", -1, CONF_BOOL_FLAG(SNAPPCB_FLAG_PIN, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/orthogonal_moves", -1, CONF_BOOL_FLAG(ORTHOMOVEFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/live_routing", -1, CONF_BOOL_FLAG(LIVEROUTEFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/lock_names", -1, CONF_BOOL_FLAG(LOCKNAMESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/only_names", -1, CONF_BOOL_FLAG(ONLYNAMESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/hide_names", -1, CONF_BOOL_FLAG(HIDENAMESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/thin_draw", -1, CONF_BOOL_FLAG(THINDRAWFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/thin_draw_poly", -1, CONF_BOOL_FLAG(THINDRAWPOLYFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/local_ref", -1, CONF_BOOL_FLAG(LOCALREFFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/check_planes", -1, CONF_BOOL_FLAG(CHECKPLANESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/description", -1, CONF_BOOL_FLAG(DESCRIPTIONFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/name_on_pcb", -1, CONF_BOOL_FLAG(NAMEONPCBFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/show_mask", -1, CONF_BOOL_FLAG(SHOWMASKFLAG, yy_pcb_flags), POL_OVERWRITE);
+
+		/* don't save this because it is saved manually as PCB::grid::unit */
+		CONF_NO_ATTRIB("editor/grid_unit");
+
+		/* don't save these to reduce noise - they are reset by the GUI anyway */
+		CONF_NO_ATTRIB("editor/mode");
+
+		/* it's saved in [styles] */
+		CONF_NO_ATTRIB("design/routes");
+
+		/* load config nodes not disabled above, from optional attributes */
+		io_pcb_attrib_a2c(Ptr);
+
+		conf_update(NULL);
+	}
+	if (retval == 0) {
+		/* restore loader so the next save will use the same units */
+		const char *loader = AttributeGetFromList(&PCB->Attributes, "PCB::loader");
+		if (loader != NULL) {
+			pcb_find_io_t f;
+			int len;
+
+			len = pcb_find_io(&f, 1, PCB_IOT_PCB, 1, loader);
+/*			printf("PCB::loader: %s -> %d\n", loader, len);*/
+			if (len > 0) {
+/*				printf("   ::loader: '%s'\n", f.plug->description);*/
+				PCB->Data->loader = f.plug;
+			}
+		}
+	}
+	return retval;
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes LEX and calls parser for a font
+ */
+int io_pcb_ParseFont(plug_io_t *ctx, FontTypePtr Ptr, const char *Filename)
+{
+	int r = 0;
+	yyPCB = NULL;
+	yyFont = Ptr;
+	yyElement = NULL;
+
+	yy_settings_dest = CFR_invalid;
+	r = Parse(NULL, conf_core.rc.font_command, NULL, Filename, NULL);
+	if (r == 0) {
+#ifdef DEBUG
+		Message (PCB_MSG_DEBUG, "Found %s in %s\n", Filename, conf_core.rc.font_command);
+#endif
+	}
+	return r;
+}
+
+static int
+parse_number ()
+{
+  pcb_lval.number = strtod ((char *) pcb_text, NULL);
+  return FLOATING;
+}
+
diff --git a/src_plugins/io_pcb/parse_l.h b/src_plugins/io_pcb/parse_l.h
new file mode 100644
index 0000000..61b5edc
--- /dev/null
+++ b/src_plugins/io_pcb/parse_l.h
@@ -0,0 +1,332 @@
+#ifndef pcb_HEADER_H
+#define pcb_HEADER_H 1
+#define pcb_IN_HEADER 1
+
+#line 6 "parse_l.h"
+
+#line 8 "parse_l.h"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int pcb_leng;
+
+extern FILE *pcb_in, *pcb_out;
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void pcb_restart (FILE *input_file  );
+void pcb__switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE pcb__create_buffer (FILE *file,int size  );
+void pcb__delete_buffer (YY_BUFFER_STATE b  );
+void pcb__flush_buffer (YY_BUFFER_STATE b  );
+void pcb_push_buffer_state (YY_BUFFER_STATE new_buffer  );
+void pcb_pop_buffer_state (void );
+
+YY_BUFFER_STATE pcb__scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE pcb__scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE pcb__scan_bytes (yyconst char *bytes,int len  );
+
+void *pcb_alloc (yy_size_t  );
+void *pcb_realloc (void *,yy_size_t  );
+void pcb_free (void *  );
+
+/* Begin user sect3 */
+
+extern int pcb_lineno;
+
+extern char *pcb_text;
+#define yytext_ptr pcb_text
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int pcb_lex_destroy (void );
+
+int pcb_get_debug (void );
+
+void pcb_set_debug (int debug_flag  );
+
+YY_EXTRA_TYPE pcb_get_extra (void );
+
+void pcb_set_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *pcb_get_in (void );
+
+void pcb_set_in  (FILE * in_str  );
+
+FILE *pcb_get_out (void );
+
+void pcb_set_out  (FILE * out_str  );
+
+int pcb_get_leng (void );
+
+char *pcb_get_text (void );
+
+int pcb_get_lineno (void );
+
+void pcb_set_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int pcb_wrap (void );
+#else
+extern int pcb_wrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int pcb_lex (void);
+
+#define YY_DECL int pcb_lex (void)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 218 "parse_l.l"
+
+
+#line 331 "parse_l.h"
+#undef pcb_IN_HEADER
+#endif /* pcb_HEADER_H */
diff --git a/src_plugins/io_pcb/parse_l.l b/src_plugins/io_pcb/parse_l.l
new file mode 100644
index 0000000..4623223
--- /dev/null
+++ b/src_plugins/io_pcb/parse_l.l
@@ -0,0 +1,466 @@
+
+
+%{
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,2006 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* lexical definitions to parse ASCII input of PCB and Element description
+ */
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <locale.h>
+
+#if defined(_POSIX_SOURCE) || defined(_HPUX_SOURCE)
+#include <unistd.h>
+#endif
+
+#include "global.h"
+#include "flags.h"
+
+#ifdef HAVE_LIBDMALLOC
+# include <dmalloc.h> /* see http://dmalloc.com */
+#endif
+
+
+
+
+#include "global.h"
+#include "crosshair.h"
+#include "data.h"
+#include "error.h"
+#include "file.h"
+#include "mymem.h"
+#include "misc.h"
+#include "strflags.h"
+#include "parse_common.h"
+#include "parse_y.h"
+#include "create.h"
+#include "plug_footprint.h"
+#include "attribs.h"
+#include "compat_misc.h"
+
+#define YY_NO_INPUT
+
+/* ---------------------------------------------------------------------------
+ * some shared parser identifiers
+ */
+#ifdef FLEX_SCANNER
+
+#define yyunput ATTRIBUTE_UNUSED yyunput
+#endif
+
+const char			*yyfilename;	/* in this file */
+PCBTypePtr		yyPCB;			/* used by parser */
+DataTypePtr		yyData;
+ElementTypePtr		yyElement;
+FontTypePtr		yyFont;
+conf_role_t yy_settings_dest;
+FlagType yy_pcb_flags;
+
+static int parse_number (void);
+
+/* ---------------------------------------------------------------------------
+ * an external prototypes
+ */
+int	yyparse(void);
+
+/* ---------------------------------------------------------------------------
+ * some local prototypes
+ */
+static int Parse(FILE *Pipe, const char *Executable, const char *Path, const char *Filename, const char *Parse_parameter);
+
+%}
+
+%option prefix="pcb_"
+
+
+HEX				0x[0-9a-fA-F]+
+INTEGER                 [+-]?([1-9][0-9]*|0)
+FLOATING                {INTEGER}"."[0-9]*
+STRINGCHAR		([^"\n\r\\]|\\.)
+
+%option yylineno
+
+%%
+
+FileVersion	{ return(T_FILEVERSION); }
+PCB			{ return(T_PCB); }
+Grid		{ return(T_GRID); }
+Cursor		{ return(T_CURSOR); }
+Thermal		{ return(T_THERMAL); }
+PolyArea	{ return(T_AREA); }
+DRC		{ return(T_DRC); }
+Flags		{ return(T_FLAGS); }
+Layer		{ return(T_LAYER); }
+Pin			{ return(T_PIN); }
+Pad			{ return(T_PAD); }
+Via			{ return(T_VIA); }
+Line		{ return(T_LINE); }
+Rat		{ return(T_RAT); }
+Rectangle	{ return(T_RECTANGLE); }
+Text		{ return(T_TEXT); }
+ElementLine	{ return(T_ELEMENTLINE); }
+ElementArc	{ return(T_ELEMENTARC); }
+Element		{ return(T_ELEMENT); }
+SymbolLine	{ return(T_SYMBOLLINE); }
+Symbol		{ return(T_SYMBOL); }
+Mark		{ return(T_MARK); }
+Groups		{ return(T_GROUPS); }
+Styles		{ return(T_STYLES); }
+Polygon		{ return(T_POLYGON); }
+Hole		{ return(T_POLYGON_HOLE); }
+Arc		{ return(T_ARC); }
+NetList		{ return(T_NETLIST); }
+Net		{ return(T_NET); }
+Connect		{ return(T_CONN); }
+NetListPatch		{ return(T_NETLISTPATCH); }
+add_conn		{ return(T_ADD_CONN); }
+del_conn		{ return(T_DEL_CONN); }
+change_attrib		{ return(T_CHANGE_ATTRIB); }
+Attribute	{ return(T_ATTRIBUTE); }
+
+nm	{ return T_NM; }
+um	{ return T_UM; }
+mm	{ return T_MM; }
+m	{ return T_M; }
+km	{ return T_KM; }
+umil	{ return T_UMIL; }
+cmil	{ return T_CMIL; }
+mil	{ return T_MIL; }
+in	{ return T_IN; }
+
+\'.\'				{
+						pcb_lval.integer = (unsigned) *(yytext+1);
+						return(CHAR_CONST);
+					}
+{FLOATING}		{	return parse_number(); }
+{INTEGER}		{	pcb_lval.integer = pcb_round (strtod (yytext, NULL)); return INTEGER; }
+
+{HEX}			{	unsigned n;
+				sscanf((char *) yytext, "%x", &n);
+				pcb_lval.integer = n;
+				return INTEGER;
+					}
+\"{STRINGCHAR}*\"	{
+						char	*p1, *p2;
+
+							/* return NULL on empty string */
+						if (yyleng == 2)
+						{
+							pcb_lval.string = NULL;
+							return(STRING);
+						}
+
+							/* allocate memory and copy string;
+							 * stringlength is counted and copied without
+							 * leading and trailing '"'
+							 */
+						yyleng -= 2;
+						pcb_lval.string = (char *)calloc (yyleng+1, sizeof (char));
+						p1 = (char *) (yytext +1);
+						p2 = pcb_lval.string;
+						while(yyleng--)
+						{
+								/* check for special character */
+							if (*p1 == '\\')
+							{
+								yyleng--;
+								p1++;
+
+							}
+							*p2++ = *p1++;
+						}
+						*p2 = '\0';
+						return(STRING);
+					}
+#.*					{}
+[ \t]+				{}
+[\n]				{
+#ifndef FLEX_SCANNER
+						yylineno++;
+#endif
+					}
+[\r]				{}
+.					{ return(*yytext); }
+
+%%
+
+/* ---------------------------------------------------------------------------
+ * sets up the preprocessor command
+ */
+static int Parse(FILE *Pipe, const char *Executable, const char *Path, const char *Filename, const char *Parse_parameter)
+{
+	static	char	*command = NULL;
+	int		returncode;
+	int		used_popen = 0;
+	char *tmps;
+	size_t l;
+#ifdef FLEX_SCANNER
+	static	pcb_bool	firsttime = pcb_true;
+#endif
+
+	if (Pipe == NULL) {
+	if (EMPTY_STRING_P (Executable))
+	  {
+	    l = 2;
+	    if ( Path != NULL )
+              l += strlen (Path);
+
+            l += strlen (Filename);
+
+	    if ( (tmps = (char *) malloc ( l * sizeof (char))) == NULL)
+              {
+                fprintf (stderr, "Parse():  malloc failed\n");
+                exit (1);
+              }
+
+	    if ( Path != NULL && *Path != '\0')
+              sprintf (tmps, "%s%s%s", Path, PCB_DIR_SEPARATOR_S, Filename);
+            else
+              sprintf (tmps, "%s", Filename);
+
+	    yyin = fopen (tmps, "r");
+	    if (!yyin)
+	      {
+		return(1);
+	      }
+            free (tmps);
+	  }
+	else
+	  {
+	    used_popen = 1;
+
+	    command = EvaluateFilename(Executable, Path, Filename, Parse_parameter);
+
+	    /* open pipe to stdout of command */
+	    if (*command == '\0' || (yyin = popen(command, "r")) == NULL)
+	      {
+		PopenErrorMessage(command);
+		free(command);
+		return(1);
+	      }
+			free(command);
+	  }
+	}
+	else {
+		yyin = Pipe;
+	}
+
+#ifdef FLEX_SCANNER
+		/* reset parser if not called the first time */
+	if (!firsttime)
+		yyrestart(yyin);
+	firsttime = pcb_false;
+#endif
+
+		/* init linenumber and filename for yyerror() */
+	yylineno = 1;
+	yyfilename = Filename;
+
+		/* We need to save the data temporarily because lex-yacc are able
+		 * to break the application if the input file has an illegal format.
+		 * It's not necessary if the system supports the call of functions
+		 * on termination.
+		 */
+
+	CreateBeLenient (pcb_true);
+
+#if !defined(HAS_ATEXIT)
+	if (PCB && PCB->Data)
+	  SaveTMPData();
+	returncode = pcb_parse();
+	RemoveTMPData();
+#else
+	returncode = pcb_parse();
+#endif
+	/* clean up parse buffer */
+	yy_delete_buffer(YY_CURRENT_BUFFER);
+
+	CreateBeLenient (pcb_false);
+
+	if (Pipe != NULL)
+		return returncode;
+
+	if (used_popen)
+	  return(pclose(yyin) ? 1 : returncode);
+	return(fclose(yyin) ? 1 : returncode);
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes LEX and calls parser for a single element file
+ */
+int io_pcb_ParseElement(plug_io_t *ctx, DataTypePtr Ptr, const char *name)
+{
+	FILE *f;
+	int ret;
+	fp_fopen_ctx_t st;
+
+	yy_settings_dest = CFR_invalid;
+	yyPCB = NULL;
+	yyData = Ptr;
+	yyFont = &PCB->Font;
+	yyElement = NULL;
+
+	f = fp_fopen(fp_default_search_path(), name, &st);
+
+	if (f == NULL)
+		return -1;
+
+	ret = Parse(f, NULL,NULL,NULL,NULL);
+
+	fp_fclose(f, &st);
+
+	return(ret);
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes LEX and calls parser for a complete board
+ */
+#define	TEST_FLAG_LOCAL(F,FLG)		(((FLG) & (F)) ? 1 : 0)
+#define CONF_BOOL_FLAG(F,FLG)	(TEST_FLAG_LOCAL(F,FLG.f) ? "true" : "false")
+
+/* Hack: set a no-save-attribute flag while loading some fields from the file;
+   because we have all these flags set we won't need to list the paths that
+   have hardwired flags again in a "don't save these in attributes" list. */
+#define CONF_NO_ATTRIB(path) \
+do { \
+	conf_native_t *n = conf_get_field(path); \
+	if (n != NULL) \
+		n->random_flags.io_pcb_no_attrib = 1; \
+} while(0) \
+
+#define CONF_SET(target, path, arr_idx, new_val, pol) \
+do { \
+	CONF_NO_ATTRIB(path); \
+	conf_set(target, path, arr_idx, new_val, pol); \
+} while(0) \
+
+int io_pcb_ParsePCB(plug_io_t *ctx, PCBTypePtr Ptr, const char *Filename, conf_role_t settings_dest)
+{
+	int retval;
+	yyPCB = Ptr;
+	yyData = NULL;
+	yyFont = NULL;
+	yyElement = NULL;
+	yy_settings_dest = settings_dest;
+	if (settings_dest != CFR_invalid)
+		conf_reset(settings_dest, Filename);
+	setlocale(LC_ALL, "C"); /* make sure numerics are read predictably */
+	retval = Parse(NULL, conf_core.rc.file_command, conf_core.rc.file_path, Filename, NULL);
+	setlocale(LC_ALL, "");
+	if ((settings_dest != CFR_invalid) && (retval == 0)) {
+		/* overwrite settings from the flags, mark them not-to-save */
+		CONF_SET(settings_dest, "plugins/mincut/enable", -1, CONF_BOOL_FLAG(ENABLEPCB_FLAG_MINCUT, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/show_number", -1, CONF_BOOL_FLAG(SHOWNUMBERFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/show_drc", -1, CONF_BOOL_FLAG(SHOWPCB_FLAG_DRC, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/rubber_band_mode", -1, CONF_BOOL_FLAG(RUBBERBANDFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/auto_drc", -1, CONF_BOOL_FLAG(AUTOPCB_FLAG_DRC, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/all_direction_lines", -1, CONF_BOOL_FLAG(ALLDIRECTIONFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/swap_start_direction", -1, CONF_BOOL_FLAG(SWAPSTARTDIRFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/unique_names", -1, CONF_BOOL_FLAG(UNIQUENAMEFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/clear_line", -1, CONF_BOOL_FLAG(CLEARNEWFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/full_poly", -1, CONF_BOOL_FLAG(NEWPCB_FLAG_FULLPOLY, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/snap_pin", -1, CONF_BOOL_FLAG(SNAPPCB_FLAG_PIN, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/orthogonal_moves", -1, CONF_BOOL_FLAG(ORTHOMOVEFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/live_routing", -1, CONF_BOOL_FLAG(LIVEROUTEFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/lock_names", -1, CONF_BOOL_FLAG(LOCKNAMESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/only_names", -1, CONF_BOOL_FLAG(ONLYNAMESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/hide_names", -1, CONF_BOOL_FLAG(HIDENAMESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/thin_draw", -1, CONF_BOOL_FLAG(THINDRAWFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/thin_draw_poly", -1, CONF_BOOL_FLAG(THINDRAWPOLYFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/local_ref", -1, CONF_BOOL_FLAG(LOCALREFFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/check_planes", -1, CONF_BOOL_FLAG(CHECKPLANESFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/description", -1, CONF_BOOL_FLAG(DESCRIPTIONFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/name_on_pcb", -1, CONF_BOOL_FLAG(NAMEONPCBFLAG, yy_pcb_flags), POL_OVERWRITE);
+		CONF_SET(settings_dest, "editor/show_mask", -1, CONF_BOOL_FLAG(SHOWMASKFLAG, yy_pcb_flags), POL_OVERWRITE);
+
+		/* don't save this because it is saved manually as PCB::grid::unit */
+		CONF_NO_ATTRIB("editor/grid_unit");
+
+		/* don't save these to reduce noise - they are reset by the GUI anyway */
+		CONF_NO_ATTRIB("editor/mode");
+
+		/* it's saved in [styles] */
+		CONF_NO_ATTRIB("design/routes");
+
+		/* load config nodes not disabled above, from optional attributes */
+		io_pcb_attrib_a2c(Ptr);
+
+		conf_update(NULL);
+	}
+	if (retval == 0) {
+		/* restore loader so the next save will use the same units */
+		const char *loader = AttributeGetFromList(&PCB->Attributes, "PCB::loader");
+		if (loader != NULL) {
+			pcb_find_io_t f;
+			int len;
+
+			len = pcb_find_io(&f, 1, PCB_IOT_PCB, 1, loader);
+/*			printf("PCB::loader: %s -> %d\n", loader, len);*/
+			if (len > 0) {
+/*				printf("   ::loader: '%s'\n", f.plug->description);*/
+				PCB->Data->loader = f.plug;
+			}
+		}
+	}
+	return retval;
+}
+
+/* ---------------------------------------------------------------------------
+ * initializes LEX and calls parser for a font
+ */
+int io_pcb_ParseFont(plug_io_t *ctx, FontTypePtr Ptr, const char *Filename)
+{
+	int r = 0;
+	yyPCB = NULL;
+	yyFont = Ptr;
+	yyElement = NULL;
+
+	yy_settings_dest = CFR_invalid;
+	r = Parse(NULL, conf_core.rc.font_command, NULL, Filename, NULL);
+	if (r == 0) {
+#ifdef DEBUG
+		Message (PCB_MSG_DEBUG, "Found %s in %s\n", Filename, conf_core.rc.font_command);
+#endif
+	}
+	return r;
+}
+
+static int
+parse_number ()
+{
+  pcb_lval.number = strtod ((char *) yytext, NULL);
+  return FLOATING;
+}
diff --git a/src_plugins/io_pcb/parse_y.c b/src_plugins/io_pcb/parse_y.c
new file mode 100644
index 0000000..c9ffa07
--- /dev/null
+++ b/src_plugins/io_pcb/parse_y.c
@@ -0,0 +1,3258 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "3.0.2"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names.  */
+#define yyparse         pcb_parse
+#define yylex           pcb_lex
+#define yyerror         pcb_error
+#define yydebug         pcb_debug
+#define yynerrs         pcb_nerrs
+
+#define yylval          pcb_lval
+#define yychar          pcb_char
+
+/* Copy the first part of user declarations.  */
+#line 11 "parse_y.y" /* yacc.c:339  */
+
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* grammar to parse ASCII input of PCB description
+ */
+
+#include "config.h"
+#include "conf_core.h"
+#include "global.h"
+#include "layer.h"
+#include "create.h"
+#include "data.h"
+#include "error.h"
+#include "file.h"
+#include "mymem.h"
+#include "misc.h"
+#include "parse_l.h"
+#include "polygon.h"
+#include "remove.h"
+#include "rtree.h"
+#include "strflags.h"
+#include "thermal.h"
+#include "rats_patch.h"
+#include "flags.h"
+#include "route_style.h"
+#include "compat_misc.h"
+
+#ifdef HAVE_LIBDMALLOC
+# include <dmalloc.h> /* see http://dmalloc.com */
+#endif
+
+
+
+static	LayerTypePtr	Layer;
+static	PolygonTypePtr	Polygon;
+static	SymbolTypePtr	Symbol;
+static	int		pin_num;
+static	LibraryMenuTypePtr	Menu;
+static	pcb_bool			LayerFlag[MAX_LAYER + 2];
+
+extern	char			*yytext;		/* defined by LEX */
+extern	PCBTypePtr		yyPCB;
+extern	DataTypePtr		yyData;
+extern	ElementTypePtr	yyElement;
+extern	FontTypePtr		yyFont;
+extern	int				pcb_lineno;		/* linenumber */
+extern	char			*yyfilename;	/* in this file */
+extern	conf_role_t yy_settings_dest;
+extern FlagType yy_pcb_flags;
+
+static char *layer_group_string;
+
+static AttributeListTypePtr attr_list;
+
+int yyerror(const char *s);
+int yylex();
+static int check_file_version (int);
+
+static void do_measure (PLMeasure *m, Coord i, double d, int u);
+#define M(r,f,d) do_measure (&(r), f, d, 1)
+
+/* Macros for interpreting what "measure" means - integer value only,
+   old units (mil), or new units (cmil).  */
+#define IV(m) integer_value (m)
+#define OU(m) old_units (m)
+#define NU(m) new_units (m)
+
+static int integer_value (PLMeasure m);
+static Coord old_units (PLMeasure m);
+static Coord new_units (PLMeasure m);
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+
+#include "parse_y.h"
+
+
+#line 176 "parse_y.c" /* yacc.c:339  */
+
+# ifndef YY_NULLPTR
+#  if defined __cplusplus && 201103L <= __cplusplus
+#   define YY_NULLPTR nullptr
+#  else
+#   define YY_NULLPTR 0
+#  endif
+# endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+   by #include "parse_y.h".  */
+#ifndef YY_PCB_PARSE_Y_H_INCLUDED
+# define YY_PCB_PARSE_Y_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int pcb_debug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    FLOATING = 258,
+    INTEGER = 259,
+    CHAR_CONST = 260,
+    STRING = 261,
+    T_FILEVERSION = 262,
+    T_PCB = 263,
+    T_LAYER = 264,
+    T_VIA = 265,
+    T_RAT = 266,
+    T_LINE = 267,
+    T_ARC = 268,
+    T_RECTANGLE = 269,
+    T_TEXT = 270,
+    T_ELEMENTLINE = 271,
+    T_ELEMENT = 272,
+    T_PIN = 273,
+    T_PAD = 274,
+    T_GRID = 275,
+    T_FLAGS = 276,
+    T_SYMBOL = 277,
+    T_SYMBOLLINE = 278,
+    T_CURSOR = 279,
+    T_ELEMENTARC = 280,
+    T_MARK = 281,
+    T_GROUPS = 282,
+    T_STYLES = 283,
+    T_POLYGON = 284,
+    T_POLYGON_HOLE = 285,
+    T_NETLIST = 286,
+    T_NET = 287,
+    T_CONN = 288,
+    T_NETLISTPATCH = 289,
+    T_ADD_CONN = 290,
+    T_DEL_CONN = 291,
+    T_CHANGE_ATTRIB = 292,
+    T_AREA = 293,
+    T_THERMAL = 294,
+    T_DRC = 295,
+    T_ATTRIBUTE = 296,
+    T_UMIL = 297,
+    T_CMIL = 298,
+    T_MIL = 299,
+    T_IN = 300,
+    T_NM = 301,
+    T_UM = 302,
+    T_MM = 303,
+    T_M = 304,
+    T_KM = 305
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 118 "parse_y.y" /* yacc.c:355  */
+
+	int		integer;
+	double		number;
+	char		*string;
+	FlagType	flagtype;
+	PLMeasure	measure;
+
+#line 275 "parse_y.c" /* yacc.c:355  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE pcb_lval;
+
+int pcb_parse (void);
+
+#endif /* !YY_PCB_PARSE_Y_H_INCLUDED  */
+
+/* Copy the second part of user declarations.  */
+
+#line 290 "parse_y.c" /* yacc.c:358  */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__                                               \
+      && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)))  \
+     || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+#  define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+#  define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE   YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+#  define _Noreturn __declspec (noreturn)
+# else
+#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+    _Pragma ("GCC diagnostic push") \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYSIZE_T yynewbytes;                                            \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYSIZE_T yyi;                         \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  10
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   608
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  55
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  112
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  214
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  643
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+   by yylex, with out-of-bounds checking.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   305
+
+#define YYTRANSLATE(YYX)                                                \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, without out-of-bounds checking.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      53,    54,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,    51,     2,    52,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50
+};
+
+#if YYDEBUG
+  /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   145,   145,   146,   147,   148,   172,   172,   229,   229,
+     240,   240,   259,   260,   265,   265,   305,   307,   337,   343,
+     349,   378,   379,   380,   383,   391,   406,   440,   446,   452,
+     468,   470,   495,   497,   528,   530,   531,   532,   536,   546,
+     557,   584,   588,   593,   621,   625,   669,   678,   687,   691,
+     692,   696,   697,   701,   702,   702,   703,   704,   706,   706,
+     713,   717,   718,   719,   720,   721,   757,   767,   778,   788,
+     798,   834,   839,   871,   870,   898,   899,   903,   904,   908,
+     909,   910,   911,   912,   913,   915,   920,   921,   922,   923,
+     923,   924,   954,   963,   972,  1020,  1029,  1038,  1075,  1085,
+    1103,  1153,  1152,  1191,  1193,  1198,  1197,  1204,  1206,  1211,
+    1215,  1275,  1276,  1277,  1278,  1279,  1287,  1286,  1305,  1304,
+    1323,  1322,  1343,  1341,  1365,  1363,  1444,  1445,  1449,  1450,
+    1451,  1452,  1453,  1455,  1460,  1465,  1470,  1475,  1480,  1485,
+    1485,  1489,  1490,  1494,  1495,  1496,  1497,  1499,  1505,  1512,
+    1517,  1522,  1522,  1563,  1575,  1587,  1598,  1614,  1668,  1682,
+    1695,  1706,  1717,  1718,  1722,  1723,  1745,  1747,  1763,  1782,
+    1783,  1786,  1788,  1789,  1810,  1817,  1833,  1834,  1838,  1843,
+    1844,  1848,  1849,  1873,  1872,  1882,  1883,  1887,  1888,  1907,
+    1924,  1925,  1929,  1934,  1935,  1939,  1940,  1955,  1956,  1957,
+    1984,  1992,  1993,  1997,  1998,  2003,  2004,  2005,  2006,  2007,
+    2008,  2009,  2010,  2011,  2012
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "FLOATING", "INTEGER", "CHAR_CONST",
+  "STRING", "T_FILEVERSION", "T_PCB", "T_LAYER", "T_VIA", "T_RAT",
+  "T_LINE", "T_ARC", "T_RECTANGLE", "T_TEXT", "T_ELEMENTLINE", "T_ELEMENT",
+  "T_PIN", "T_PAD", "T_GRID", "T_FLAGS", "T_SYMBOL", "T_SYMBOLLINE",
+  "T_CURSOR", "T_ELEMENTARC", "T_MARK", "T_GROUPS", "T_STYLES",
+  "T_POLYGON", "T_POLYGON_HOLE", "T_NETLIST", "T_NET", "T_CONN",
+  "T_NETLISTPATCH", "T_ADD_CONN", "T_DEL_CONN", "T_CHANGE_ATTRIB",
+  "T_AREA", "T_THERMAL", "T_DRC", "T_ATTRIBUTE", "T_UMIL", "T_CMIL",
+  "T_MIL", "T_IN", "T_NM", "T_UM", "T_MM", "T_M", "T_KM", "'['", "']'",
+  "'('", "')'", "$accept", "parse", "parsepcb", "$@1", "$@2", "parsedata",
+  "$@3", "pcbfont", "parsefont", "$@4", "pcbfileversion", "pcbname",
+  "pcbgrid", "pcbgridold", "pcbgridnew", "pcbhigrid", "pcbcursor",
+  "polyarea", "pcbthermal", "pcbdrc", "pcbdrc1", "pcbdrc2", "pcbdrc3",
+  "pcbflags", "pcbgroups", "pcbstyles", "pcbdata", "pcbdefinitions",
+  "pcbdefinition", "$@5", "$@6", "via", "via_hi_format", "via_2.0_format",
+  "via_1.7_format", "via_newformat", "via_oldformat", "rats", "layer",
+  "$@7", "layerdata", "layerdefinitions", "layerdefinition", "$@8",
+  "line_hi_format", "line_1.7_format", "line_oldformat", "arc_hi_format",
+  "arc_1.7_format", "arc_oldformat", "text_oldformat", "text_newformat",
+  "text_hi_format", "polygon_format", "$@9", "polygonholes", "polygonhole",
+  "$@10", "polygonpoints", "polygonpoint", "element", "element_oldformat",
+  "$@11", "element_1.3.4_format", "$@12", "element_newformat", "$@13",
+  "element_1.7_format", "$@14", "element_hi_format", "$@15",
+  "elementdefinitions", "elementdefinition", "$@16", "relementdefs",
+  "relementdef", "$@17", "pin_hi_format", "pin_1.7_format",
+  "pin_1.6.3_format", "pin_newformat", "pin_oldformat", "pad_hi_format",
+  "pad_1.7_format", "pad_newformat", "pad", "flags", "symbols", "symbol",
+  "symbolhead", "symbolid", "symboldata", "symboldefinition",
+  "hiressymbol", "pcbnetlist", "pcbnetdef", "nets", "netdefs", "net",
+  "$@18", "connections", "conndefs", "conn", "pcbnetlistpatch",
+  "pcbnetpatchdef", "netpatches", "netpatchdefs", "netpatch", "attribute",
+  "opt_string", "number", "measure", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+   (internal) symbol number NUM (which must be that of a token).  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
+     305,    91,    93,    40,    41
+};
+# endif
+
+#define YYPACT_NINF -449
+
+#define yypact_value_is_default(Yystate) \
+  (!!((Yystate) == (-449)))
+
+#define YYTABLE_NINF -90
+
+#define yytable_value_is_error(Yytable_value) \
+  0
+
+  /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+     STATE-NUM.  */
+static const yytype_int16 yypact[] =
+{
+     151,  -449,    39,  -449,     4,    30,  -449,    23,  -449,    56,
+    -449,    32,    97,   -31,  -449,  -449,  -449,  -449,  -449,  -449,
+    -449,    73,    -5,    26,  -449,   169,  -449,    88,    30,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,    48,    56,  -449,
+    -449,   129,    55,   116,    49,   113,   140,    63,    63,    63,
+      63,  -449,    95,  -449,  -449,    84,    84,  -449,   -16,   105,
+     143,   157,    92,   147,  -449,  -449,  -449,  -449,  -449,   158,
+     161,   168,   176,  -449,  -449,   305,    63,    63,    63,    63,
+     187,  -449,  -449,    63,    63,   134,  -449,  -449,  -449,  -449,
+      63,     6,    63,    63,   160,   156,   191,   192,    63,   210,
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,    63,
+      63,   224,   229,   233,   196,   197,    63,    63,    63,  -449,
+      63,    63,    63,    63,    63,   218,   257,   272,   185,    63,
+    -449,   236,    63,   221,    63,    63,   245,   255,   270,    63,
+      63,   274,   287,    63,    63,    63,    63,    63,   283,   302,
+      63,    63,    63,   353,   312,    63,   362,   239,    63,    63,
+    -449,  -449,  -449,    63,    63,  -449,  -449,   378,     0,    63,
+      63,   334,    63,   341,   372,  -449,  -449,  -449,    63,    63,
+      63,   342,  -449,    63,   343,   394,   251,   397,   398,    63,
+      63,   351,   350,  -449,   354,   355,  -449,   356,    63,   357,
+     380,    63,    63,    63,   358,   260,   399,  -449,   359,   408,
+     409,    49,   410,    63,    63,  -449,  -449,  -449,  -449,  -449,
+      63,   228,   363,   389,    63,    63,   414,  -449,   205,   226,
+     366,   253,   367,   368,   260,  -449,    88,  -449,  -449,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,    49,  -449,   369,
+     417,   373,   370,   376,   375,    63,   379,   381,   424,   256,
+     412,    63,    90,   384,    33,    63,    63,    63,    63,    63,
+      63,    63,    49,  -449,  -449,  -449,   385,  -449,   386,  -449,
+    -449,  -449,  -449,    11,  -449,  -449,   387,   433,   436,   195,
+    -449,    63,   390,    63,   393,   276,   403,   404,   279,   280,
+     102,  -449,    88,  -449,  -449,  -449,  -449,  -449,    63,    63,
+      63,    63,    63,    63,    63,   405,  -449,  -449,  -449,    13,
+    -449,   391,   406,   416,    49,   411,   454,  -449,    63,    63,
+      63,    63,    63,    63,    63,    63,  -449,  -449,  -449,    63,
+      63,    63,    63,    63,    63,    63,   415,  -449,    63,  -449,
+    -449,   418,   427,  -449,   413,  -449,   419,    33,    63,    63,
+      63,    63,    63,    63,    63,    63,    63,    63,    63,    63,
+      63,    63,   264,  -449,   420,   421,   423,  -449,  -449,   425,
+      33,   426,   121,    63,    63,    63,    63,    63,    63,   422,
+     437,    63,    63,    63,    63,   458,   457,   465,   464,   320,
+    -449,   428,   438,  -449,   183,  -449,  -449,    63,    63,   299,
+      63,    63,    63,  -449,  -449,    63,    63,    63,    63,   439,
+      49,   440,   473,    63,    63,  -449,   320,   448,   442,   165,
+    -449,   165,    63,    63,   486,   490,    63,    63,    63,    49,
+       2,    63,    63,  -449,   445,  -449,   444,    63,    63,   -17,
+    -449,   446,   447,   448,  -449,   -10,   321,   326,   327,   330,
+     222,  -449,    88,  -449,  -449,  -449,  -449,   234,   450,   449,
+     459,   352,   494,    63,    63,   460,   461,  -449,    63,   110,
+    -449,  -449,   462,   463,   466,  -449,  -449,   510,  -449,  -449,
+     467,   468,   469,   478,   -10,  -449,    63,    63,    63,    63,
+      63,    63,    63,    63,  -449,  -449,  -449,  -449,  -449,  -449,
+    -449,   479,   514,   383,    63,    63,  -449,  -449,    49,   480,
+     519,  -449,  -449,  -449,   529,   530,   531,   538,  -449,  -449,
+      63,    63,    63,    63,    63,    63,    63,    63,  -449,   493,
+     495,   544,   502,   503,   506,  -449,   505,   320,   507,   554,
+     556,   557,    63,    63,    63,    63,    63,    63,    63,    63,
+    -449,  -449,   515,  -449,  -449,  -449,  -449,   516,   518,   520,
+     521,   562,    63,    63,    63,    63,    63,    63,    63,    63,
+    -449,  -449,  -449,  -449,  -449,   522,    63,    63,    63,    63,
+      63,    63,    63,    63,   539,  -449,   525,   524,    63,    63,
+      63,    63,    63,    63,   526,   527,   539,  -449,  -449,  -449,
+     567,   574,    63,    63,    63,    63,   576,  -449,  -449,   577,
+     578,   579,   580,   535,   534,   536,    49,   585,   586,   587,
+    -449,  -449,  -449,   542,   537,    49,   591,  -449,  -449,   545,
+     546,  -449,  -449
+};
+
+  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+     Performed when YYTABLE does not specify something else to do.  Zero
+     means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       0,     5,     0,     2,    16,     0,     3,     0,     4,     0,
+       1,     0,     0,     0,     9,   111,   112,   113,   114,   115,
+      60,     0,     0,     0,    11,     0,    51,     0,     0,    53,
+      61,    62,    63,    64,    65,    56,    57,     0,    15,   164,
+     171,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    52,     0,    55,    59,     0,     0,   165,     0,     0,
+       0,     0,     0,    29,    21,    22,    23,   162,   163,     0,
+       0,     0,     0,   203,   204,   205,     0,     0,     0,     0,
+       0,   169,   170,     0,     0,     0,   166,   172,   173,    17,
+       0,     0,     0,     0,     0,    30,     0,     0,     0,   202,
+     206,   207,   208,   209,   210,   211,   212,   213,   214,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    18,
+       0,     0,     0,     0,     0,     0,    32,     0,     0,     0,
+     201,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    34,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     200,   167,   168,     0,     0,    20,    19,     0,     0,     0,
+       0,     0,     0,     0,    43,    35,    36,    37,     0,     0,
+       0,     0,    73,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,    24,     0,     0,    31,     0,     0,     0,
+      45,     0,     0,     0,     0,    76,     0,    70,     0,     0,
+       0,     0,     0,     0,     0,    26,    25,    28,    27,    33,
+       0,     0,     0,    48,     0,     0,     0,   116,     0,     0,
+       0,     0,     0,     0,    75,    77,     0,    79,    80,    81,
+      82,    83,    84,    88,    87,    86,    91,     0,    69,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      13,     0,     0,     0,   139,     0,     0,     0,     0,     0,
+       0,     0,     0,    74,    78,    90,     0,    68,     0,    71,
+      72,   175,   174,     0,    41,    42,     0,     0,     0,     0,
+      12,     0,   204,     0,     0,     0,     0,     0,     0,     0,
+     139,   126,     0,   128,   129,   130,   131,   132,     0,     0,
+       0,     0,     0,     0,     0,     0,    66,    67,    38,     0,
+      44,     0,     0,   177,     0,     0,     0,   118,     0,     0,
+       0,     0,     0,     0,     0,     0,   117,   127,   140,     0,
+       0,     0,     0,     0,     0,     0,     0,    39,     0,    47,
+      46,     0,   191,   176,     0,   120,     0,   139,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,   101,     0,     0,     0,     7,   190,     0,
+     139,     0,   139,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   107,
+      40,     0,     0,   124,   139,   122,   119,     0,     0,     0,
+       0,     0,     0,   137,   138,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,   103,   107,   180,     0,   151,
+     121,   151,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,    85,     0,    98,     0,     0,     0,     0,
+     108,     0,     0,   179,   181,   194,     0,     0,     0,     0,
+     151,   141,     0,   144,   143,   146,   145,   151,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,    94,     0,     0,
+     100,    99,     0,     0,     0,   102,   104,     0,   178,   182,
+       0,     0,     0,     0,   193,   195,     0,     0,     0,     0,
+       0,     0,     0,     0,   125,   142,   152,   123,   133,   134,
+     157,     0,     0,     0,     0,     0,    92,    93,     0,   204,
+       0,   110,   109,   105,     0,     0,     0,     0,   192,   196,
+       0,     0,     0,     0,     0,     0,     0,     0,   156,     0,
+       0,     0,     0,     0,     0,    97,     0,   107,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     155,   161,     0,   135,   136,    95,    96,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     160,   106,   183,   197,   198,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   186,   199,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   185,   187,   147,   148,
+       0,     0,     0,     0,     0,     0,     0,   184,   188,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     149,   150,   189,     0,     0,     0,     0,   153,   154,     0,
+       0,   158,   159
+};
+
+  /* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,   336,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,   309,  -449,   581,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,
+    -449,  -449,   365,  -449,  -449,  -449,  -449,  -449,  -449,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -423,  -449,
+     573,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,  -449,
+    -449,  -339,  -292,  -449,   171,  -448,  -449,  -449,  -449,  -449,
+    -449,  -449,  -449,  -449,  -449,  -449,  -190,  -449,   565,  -449,
+     548,  -449,  -449,  -449,  -449,  -449,  -449,  -449,   152,  -449,
+    -449,  -449,     1,  -449,  -449,  -449,  -449,   114,  -231,  -449,
+     -47,   -48
+};
+
+  /* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,     2,     3,     4,     5,     6,     7,   289,     8,     9,
+      12,    43,    63,    64,    65,    66,    95,   126,   149,   174,
+     175,   176,   177,   200,   223,   260,    24,    25,    26,    27,
+      28,    29,    30,    31,    32,    33,    34,    35,    36,   205,
+     233,   234,   235,   236,   237,   238,   239,   240,   241,   242,
+     243,   244,   245,   246,   399,   449,   486,   547,   425,   426,
+      14,    15,   264,    16,   357,    17,   380,    18,   431,    19,
+     429,   300,   301,   302,   460,   461,   462,   463,   464,   303,
+     304,   305,   465,   466,   306,   307,    69,    38,    39,    40,
+      83,    58,    87,    88,   352,   353,   452,   453,   454,   594,
+     605,   606,   607,   377,   378,   493,   494,   495,    53,   131,
+      75,    76
+};
+
+  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+     positive, shift that token.  If negative, reduce the rule whose
+     number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_int16 yytable[] =
+{
+      77,    78,    79,   450,   192,   275,   476,    85,   337,    73,
+      74,    11,   505,   484,    73,    74,    73,    74,   382,   505,
+      44,   251,    45,   -50,    20,   490,   491,   492,   109,   110,
+     111,   112,    21,    22,    23,   114,   115,   485,    86,    10,
+     -58,   404,   118,   120,   121,   122,    47,    13,    48,   295,
+     129,   296,   297,    67,   193,    68,   477,   276,   298,   299,
+     119,   132,   133,   318,   -54,   347,    73,    74,   139,   140,
+     141,   338,   142,   143,   144,   145,   146,    49,    37,    50,
+     152,   153,   315,    41,   155,   157,   158,   159,    81,    82,
+     337,   163,   164,    73,   292,   167,   168,   169,   170,    55,
+     171,    56,   178,   179,   180,    42,    60,   183,    61,   186,
+     187,   188,   337,    73,   519,   189,   190,    70,   295,    71,
+     296,   297,   194,   195,   567,   197,    46,   298,   299,    52,
+     201,   202,   203,    59,   354,   206,    62,   295,   210,   296,
+     297,   213,   214,    92,    72,    93,   298,   299,    80,    90,
+     220,   -10,     1,   224,   225,   226,   336,    89,    -6,    -6,
+     -10,   -10,   -10,    91,    96,   253,   254,    97,    -8,   -49,
+      20,    94,   255,   -14,    98,   406,   261,   262,    21,    22,
+      23,   456,    99,   457,   458,   116,   -58,   117,    73,    74,
+     459,   151,   -10,   113,   125,   -50,    20,   127,   128,   295,
+     -49,   296,   297,   -49,    21,    22,    23,   283,   298,   299,
+     -54,   123,   -58,   124,   291,   293,   130,   308,   309,   310,
+     311,   312,   313,   314,    73,    74,   -50,   156,   134,   -50,
+     444,   506,   256,   135,   257,   319,   -54,   430,   456,   136,
+     457,   458,    73,    74,   324,   185,   326,   459,   137,   475,
+     456,   138,   457,   458,    73,    74,   265,   209,   266,   459,
+     339,   340,   341,   342,   343,   344,   345,    73,    74,   147,
+     397,   348,   228,   229,   230,   231,   504,   267,   150,   268,
+     358,   359,   360,   361,   362,   363,   364,   365,   507,   232,
+     154,   366,   367,   368,   369,   370,   148,   371,   372,   160,
+     374,   -89,    73,    74,   270,   434,   271,   287,   161,   288,
+     383,   384,   385,   386,   387,   388,   389,   390,   391,   392,
+     393,   394,   395,   162,   396,   398,   165,   328,   544,   329,
+     332,   334,   333,   335,   172,   407,   408,   409,   410,   411,
+     412,   166,   173,   415,   416,   417,   418,   100,   101,   102,
+     103,   104,   105,   106,   107,   108,   511,   181,   512,   432,
+     433,   435,   436,   437,   438,   182,   184,   439,   440,   441,
+     442,   423,   496,   424,   497,   447,   448,   498,   500,   499,
+     501,   502,   191,   503,   468,   469,   196,   540,   472,   541,
+     473,   474,   198,   199,   478,   479,   204,   207,   208,   482,
+     483,   211,   212,   215,   216,   247,   217,   222,   219,   218,
+     221,   227,   249,   248,   252,   250,   258,   259,   263,   269,
+     272,   278,   273,   277,   280,   279,   514,   515,   281,   282,
+     286,   518,   520,   284,   -14,   285,   633,   316,   294,   321,
+     317,   320,   322,   349,   325,   639,   327,   351,   530,   531,
+     532,   533,   534,   535,   536,   537,   330,   331,   356,   346,
+     350,   376,   419,   420,   355,   379,   542,   543,   373,   421,
+     422,   375,   400,   381,   413,   401,   402,   446,   403,   405,
+     451,   427,   552,   553,   554,   555,   556,   557,   558,   559,
+     470,   414,   428,   443,   445,   455,   471,   480,   481,   487,
+     513,   488,   508,   509,   572,   573,   574,   575,   576,   577,
+     578,   579,   516,   510,   521,   517,   524,   522,   539,   523,
+     525,   526,   527,   546,   586,   587,   588,   589,   590,   591,
+     592,   593,   528,   538,   545,   548,   549,   550,   596,   597,
+     598,   599,   600,   601,   551,   602,   603,   560,   562,   561,
+     610,   611,   612,   613,   563,   614,   615,   564,   565,   566,
+     569,   568,   570,   571,   621,   622,   623,   624,   585,   580,
+     581,   582,   604,   619,   583,   584,   595,   608,   609,   616,
+     620,   617,   625,   626,   627,   628,   629,   630,   631,   634,
+     632,   638,   635,   636,   637,   640,   290,   641,   323,   274,
+     642,    54,   467,    57,    84,   489,    51,   618,   529
+};
+
+static const yytype_uint16 yycheck[] =
+{
+      48,    49,    50,   426,     4,   236,     4,    23,   300,     3,
+       4,     7,   460,    30,     3,     4,     3,     4,   357,   467,
+      51,   211,    53,     0,     1,    35,    36,    37,    76,    77,
+      78,    79,     9,    10,    11,    83,    84,    54,    54,     0,
+      17,   380,    90,    91,    92,    93,    51,    17,    53,    16,
+      98,    18,    19,     4,    54,     6,    54,   247,    25,    26,
+      54,   109,   110,    52,    41,    52,     3,     4,   116,   117,
+     118,   302,   120,   121,   122,   123,   124,    51,    22,    53,
+     128,   129,   272,    51,   132,   133,   134,   135,     4,     5,
+     382,   139,   140,     3,     4,   143,   144,   145,   146,    51,
+     147,    53,   150,   151,   152,     8,    51,   155,    53,   157,
+     158,   159,   404,     3,     4,   163,   164,     4,    16,     6,
+      18,    19,   169,   170,   547,   172,    53,    25,    26,    41,
+     178,   179,   180,     4,   324,   183,    20,    16,   186,    18,
+      19,   189,   190,    51,     4,    53,    25,    26,    53,     6,
+     198,     0,     1,   201,   202,   203,    54,    52,     7,     8,
+       9,    10,    11,     6,     6,   213,   214,     6,    17,     0,
+       1,    24,   220,    22,     6,    54,   224,   225,     9,    10,
+      11,    16,     6,    18,    19,    51,    17,    53,     3,     4,
+      25,     6,    41,     6,    38,     0,     1,     6,     6,    16,
+      31,    18,    19,    34,     9,    10,    11,   255,    25,    26,
+      41,    51,    17,    53,   261,   262,     6,   265,   266,   267,
+     268,   269,   270,   271,     3,     4,    31,     6,     4,    34,
+     420,   462,     4,     4,     6,   283,    41,    54,    16,     6,
+      18,    19,     3,     4,   291,     6,   293,    25,    52,   439,
+      16,    54,    18,    19,     3,     4,    51,     6,    53,    25,
+     308,   309,   310,   311,   312,   313,   314,     3,     4,    51,
+       6,   319,    12,    13,    14,    15,    54,    51,     6,    53,
+     328,   329,   330,   331,   332,   333,   334,   335,    54,    29,
+      54,   339,   340,   341,   342,   343,    39,   344,   345,    54,
+     348,    41,     3,     4,    51,     6,    53,    51,    53,    53,
+     358,   359,   360,   361,   362,   363,   364,   365,   366,   367,
+     368,   369,   370,    53,   371,   372,    52,    51,   518,    53,
+      51,    51,    53,    53,    51,   383,   384,   385,   386,   387,
+     388,    54,    40,   391,   392,   393,   394,    42,    43,    44,
+      45,    46,    47,    48,    49,    50,     4,     4,     6,   407,
+     408,   409,   410,   411,   412,    53,     4,   415,   416,   417,
+     418,    51,    51,    53,    53,   423,   424,    51,    51,    53,
+      53,    51,     4,    53,   432,   433,    52,     4,   436,     6,
+     437,   438,    51,    21,   441,   442,    54,    54,     4,   447,
+     448,     4,     4,    52,    54,     6,    52,    27,    52,    54,
+      53,    53,     4,    54,     4,     6,    53,    28,     4,    53,
+      53,     4,    54,    54,    54,    52,   473,   474,    52,    54,
+       6,   478,   479,    54,    22,    54,   626,    52,    54,     6,
+      54,    54,     6,    52,    54,   635,    53,    31,   496,   497,
+     498,   499,   500,   501,   502,   503,    53,    53,     4,    54,
+      54,    34,     4,     6,    53,    52,   514,   515,    53,     4,
+       6,    53,    52,    54,    52,    54,    53,     4,    53,    53,
+      32,    53,   530,   531,   532,   533,   534,   535,   536,   537,
+       4,    54,    54,    54,    54,    53,     6,    52,    54,    53,
+       6,    54,    52,    54,   552,   553,   554,   555,   556,   557,
+     558,   559,    52,    54,    52,    54,     6,    54,     4,    53,
+      53,    53,    53,     4,   572,   573,   574,   575,   576,   577,
+     578,   579,    54,    54,    54,     6,     6,     6,   586,   587,
+     588,   589,   590,   591,     6,   592,   593,    54,     4,    54,
+     598,   599,   600,   601,    52,   602,   603,    54,    52,    54,
+       6,    54,     6,     6,   612,   613,   614,   615,     6,    54,
+      54,    53,    33,     6,    54,    54,    54,    52,    54,    53,
+       6,    54,     6,     6,     6,     6,     6,    52,    54,     4,
+      54,    54,     6,     6,    52,     4,   260,    52,   289,   234,
+      54,    28,   431,    38,    56,   453,    25,   606,   494
+};
+
+  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+     symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     1,    56,    57,    58,    59,    60,    61,    63,    64,
+       0,     7,    65,    17,   115,   116,   118,   120,   122,   124,
+       1,     9,    10,    11,    81,    82,    83,    84,    85,    86,
+      87,    88,    89,    90,    91,    92,    93,    22,   142,   143,
+     144,    51,     8,    66,    51,    53,    53,    51,    53,    51,
+      53,    83,    41,   163,   115,    51,    53,   143,   146,     4,
+      51,    53,    20,    67,    68,    69,    70,     4,     6,   141,
+       4,     6,     4,     3,     4,   165,   166,   166,   166,   166,
+      53,     4,     5,   145,   145,    23,    54,   147,   148,    52,
+       6,     6,    51,    53,    24,    71,     6,     6,     6,     6,
+      42,    43,    44,    45,    46,    47,    48,    49,    50,   166,
+     166,   166,   166,     6,   166,   166,    51,    53,   166,    54,
+     166,   166,   166,    51,    53,    38,    72,     6,     6,   166,
+       6,   164,   166,   166,     4,     4,     6,    52,    54,   166,
+     166,   166,   166,   166,   166,   166,   166,    51,    39,    73,
+       6,     6,   166,   166,    54,   166,     6,   166,   166,   166,
+      54,    53,    53,   166,   166,    52,    54,   166,   166,   166,
+     166,   165,    51,    40,    74,    75,    76,    77,   166,   166,
+     166,     4,    53,   166,     4,     6,   166,   166,   166,   166,
+     166,     4,     4,    54,   165,   165,    52,   165,    51,    21,
+      78,   166,   166,   166,    54,    94,   166,    54,     4,     6,
+     166,     4,     4,   166,   166,    52,    54,    52,    54,    52,
+     166,    53,    27,    79,   166,   166,   166,    53,    12,    13,
+      14,    15,    29,    95,    96,    97,    98,    99,   100,   101,
+     102,   103,   104,   105,   106,   107,   108,     6,    54,     4,
+       6,   141,     4,   166,   166,   166,     4,     6,    53,    28,
+      80,   166,   166,     4,   117,    51,    53,    51,    53,    53,
+      51,    53,    53,    54,    97,   163,   141,    54,     4,    52,
+      54,    52,    54,   166,    54,    54,     6,    51,    53,    62,
+      63,   165,     4,   165,    54,    16,    18,    19,    25,    26,
+     126,   127,   128,   134,   135,   136,   139,   140,   166,   166,
+     166,   166,   166,   166,   166,   141,    52,    54,    52,   166,
+      54,     6,     6,    81,   165,    54,   165,    53,    51,    53,
+      53,    53,    51,    53,    51,    53,    54,   127,   163,   166,
+     166,   166,   166,   166,   166,   166,    54,    52,   166,    52,
+      54,    31,   149,   150,   141,    53,     4,   119,   166,   166,
+     166,   166,   166,   166,   166,   166,   166,   166,   166,   166,
+     166,   165,   165,    53,   166,    53,    34,   158,   159,    52,
+     121,    54,   126,   166,   166,   166,   166,   166,   166,   166,
+     166,   166,   166,   166,   166,   166,   165,     6,   165,   109,
+      52,    54,    53,    53,   126,    53,    54,   166,   166,   166,
+     166,   166,   166,    52,    54,   166,   166,   166,   166,     4,
+       6,     4,     6,    51,    53,   113,   114,    53,    54,   125,
+      54,   123,   166,   166,     6,   166,   166,   166,   166,   166,
+     166,   166,   166,    54,   141,    54,     4,   166,   166,   110,
+     113,    32,   151,   152,   153,    53,    16,    18,    19,    25,
+     129,   130,   131,   132,   133,   137,   138,   129,   166,   166,
+       4,     6,   166,   165,   165,   141,     4,    54,   165,   165,
+      52,    54,   166,   166,    30,    54,   111,    53,    54,   153,
+      35,    36,    37,   160,   161,   162,    51,    53,    51,    53,
+      51,    53,    51,    53,    54,   130,   163,    54,    52,    54,
+      54,     4,     6,     6,   165,   165,    52,    54,   165,     4,
+     165,    52,    54,    53,     6,    53,    53,    53,    54,   162,
+     166,   166,   166,   166,   166,   166,   166,   166,    54,     4,
+       4,     6,   166,   166,   141,    54,     4,   112,     6,     6,
+       6,     6,   166,   166,   166,   166,   166,   166,   166,   166,
+      54,    54,     4,    52,    54,    52,    54,   113,    54,     6,
+       6,     6,   166,   166,   166,   166,   166,   166,   166,   166,
+      54,    54,    53,    54,    54,     6,   166,   166,   166,   166,
+     166,   166,   166,   166,   154,    54,   166,   166,   166,   166,
+     166,   166,   165,   165,    33,   155,   156,   157,    52,    54,
+     166,   166,   166,   166,   165,   165,    53,    54,   157,     6,
+       6,   166,   166,   166,   166,     6,     6,     6,     6,     6,
+      52,    54,    54,   141,     4,     6,     6,    52,    54,   141,
+       4,    52,    54
+};
+
+  /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    55,    56,    56,    56,    56,    58,    57,    59,    57,
+      61,    60,    62,    62,    64,    63,    65,    65,    66,    66,
+      66,    67,    67,    67,    68,    69,    70,    71,    71,    71,
+      72,    72,    73,    73,    74,    74,    74,    74,    75,    76,
+      77,    78,    78,    78,    79,    79,    80,    80,    80,    81,
+      81,    82,    82,    83,    84,    83,    83,    83,    85,    83,
+      83,    86,    86,    86,    86,    86,    87,    88,    89,    90,
+      91,    92,    92,    94,    93,    95,    95,    96,    96,    97,
+      97,    97,    97,    97,    97,    97,    97,    97,    97,    98,
+      97,    97,    99,   100,   101,   102,   103,   104,   105,   106,
+     107,   109,   108,   110,   110,   112,   111,   113,   113,   114,
+     114,   115,   115,   115,   115,   115,   117,   116,   119,   118,
+     121,   120,   123,   122,   125,   124,   126,   126,   127,   127,
+     127,   127,   127,   127,   127,   127,   127,   127,   127,   128,
+     127,   129,   129,   130,   130,   130,   130,   130,   130,   130,
+     130,   131,   130,   132,   133,   134,   135,   136,   137,   138,
+     139,   140,   141,   141,   142,   142,   143,   144,   144,   145,
+     145,   146,   146,   146,   147,   148,   149,   149,   150,   151,
+     151,   152,   152,   154,   153,   155,   155,   156,   156,   157,
+     158,   158,   159,   160,   160,   161,   161,   162,   162,   162,
+     163,   164,   164,   165,   165,   166,   166,   166,   166,   166,
+     166,   166,   166,   166,   166
+};
+
+  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     1,     1,     1,     1,     0,    15,     0,     2,
+       0,     2,     1,     0,     0,     2,     0,     4,     4,     6,
+       6,     1,     1,     1,     6,     7,     7,     6,     6,     0,
+       0,     4,     0,     4,     0,     1,     1,     1,     6,     7,
+       9,     4,     4,     0,     4,     0,     4,     4,     0,     1,
+       0,     1,     2,     1,     0,     2,     1,     1,     0,     2,
+       1,     1,     1,     1,     1,     1,    11,    11,    10,     9,
+       8,    10,    10,     0,    10,     1,     0,     1,     2,     1,
+       1,     1,     1,     1,     1,     8,     1,     1,     1,     0,
+       2,     1,    10,    10,     9,    12,    12,    11,     8,     9,
+       9,     0,     9,     0,     2,     0,     5,     0,     2,     4,
+       4,     1,     1,     1,     1,     1,     0,    12,     0,    15,
+       0,    16,     0,    18,     0,    18,     1,     2,     1,     1,
+       1,     1,     1,     8,     8,    10,    10,     5,     5,     0,
+       2,     1,     2,     1,     1,     1,     1,     8,     8,    10,
+      10,     0,     2,    12,    12,    10,     9,     8,    13,    13,
+      11,    10,     1,     1,     1,     2,     3,     6,     6,     1,
+       1,     0,     2,     2,     8,     8,     1,     0,     6,     1,
+       0,     1,     2,     0,     9,     1,     0,     1,     2,     4,
+       1,     0,     6,     1,     0,     1,     2,     5,     5,     6,
+       5,     1,     0,     1,     1,     1,     2,     2,     2,     2,
+       2,     2,     2,     2,     2
+};
+
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+#define YYEMPTY         (-2)
+#define YYEOF           0
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                  \
+do                                                              \
+  if (yychar == YYEMPTY)                                        \
+    {                                                           \
+      yychar = (Token);                                         \
+      yylval = (Value);                                         \
+      YYPOPSTACK (yylen);                                       \
+      yystate = *yyssp;                                         \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    {                                                           \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                  \
+    }                                                           \
+while (0)
+
+/* Error token number */
+#define YYTERROR        1
+#define YYERRCODE       256
+
+
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Type, Value); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT.  |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+  FILE *yyo = yyoutput;
+  YYUSE (yyo);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+  YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+  YYFPRINTF (yyoutput, "%s %s (",
+             yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule)
+{
+  unsigned long int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       yystos[yyssp[yyi + 1 - yynrhs]],
+                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                                              );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+        switch (*++yyp)
+          {
+          case '\'':
+          case ',':
+            goto do_not_strip_quotes;
+
+          case '\\':
+            if (*++yyp != '\\')
+              goto do_not_strip_quotes;
+            /* Fall through.  */
+          default:
+            if (yyres)
+              yyres[yyn] = *yyp;
+            yyn++;
+            break;
+
+          case '"':
+            if (yyres)
+              yyres[yyn] = '\0';
+            return yyn;
+          }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = YY_NULLPTR;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                {
+                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+                  if (! (yysize <= yysize1
+                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                    return 2;
+                  yysize = yysize1;
+                }
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  {
+    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+      return 2;
+    yysize = yysize1;
+  }
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+{
+  YYUSE (yyvaluep);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YYUSE (yytype);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (void)
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       'yyss': related to states.
+       'yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        YYSTYPE *yyvs1 = yyvs;
+        yytype_int16 *yyss1 = yyss;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * sizeof (*yyssp),
+                    &yyvs1, yysize * sizeof (*yyvsp),
+                    &yystacksize);
+
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yytype_int16 *yyss1 = yyss;
+        union yyalloc *yyptr =
+          (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+        if (! yyptr)
+          goto yyexhaustedlab;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = yylex ();
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 5:
+#line 148 "parse_y.y" /* yacc.c:1646  */
+    { YYABORT; }
+#line 1792 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 6:
+#line 172 "parse_y.y" /* yacc.c:1646  */
+    {
+					/* reset flags for 'used layers';
+					 * init font and data pointers
+					 */
+				int	i;
+
+				if (!yyPCB)
+				{
+					Message(PCB_MSG_ERROR, "illegal fileformat\n");
+					YYABORT;
+				}
+				for (i = 0; i < MAX_LAYER + 2; i++)
+					LayerFlag[i] = pcb_false;
+				yyFont = &yyPCB->Font;
+				yyData = yyPCB->Data;
+				yyData->pcb = yyPCB;
+				yyData->LayerN = 0;
+				yyPCB->NetlistPatches = yyPCB->NetlistPatchLast = NULL;
+				layer_group_string = NULL;
+			}
+#line 1817 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 7:
+#line 206 "parse_y.y" /* yacc.c:1646  */
+    {
+			  PCBTypePtr pcb_save = PCB;
+			  if ((yy_settings_dest != CFR_invalid) && (layer_group_string != NULL))
+					conf_set(yy_settings_dest, "design/groups", -1, layer_group_string, POL_OVERWRITE);
+			  CreateNewPCBPost (yyPCB, 0);
+			  if (ParseGroupString(layer_group_string, &yyPCB->LayerGroups, yyData->LayerN))
+			    {
+			      Message(PCB_MSG_ERROR, "illegal layer-group string\n");
+			      YYABORT;
+			    }
+			/* initialize the polygon clipping now since
+			 * we didn't know the layer grouping before.
+			 */
+			free(layer_group_string);
+			PCB = yyPCB;
+			ALLPOLYGON_LOOP (yyData);
+			{
+			  InitClip (yyData, layer, polygon);
+			}
+			ENDALL_LOOP;
+			PCB = pcb_save;
+			}
+#line 1844 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 8:
+#line 229 "parse_y.y" /* yacc.c:1646  */
+    { PreLoadElementPCB ();
+		    layer_group_string = NULL; }
+#line 1851 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 9:
+#line 232 "parse_y.y" /* yacc.c:1646  */
+    { LayerFlag[0] = pcb_true;
+		    LayerFlag[1] = pcb_true;
+		    yyData->LayerN = 2;
+		    PostLoadElementPCB ();
+		  }
+#line 1861 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 10:
+#line 240 "parse_y.y" /* yacc.c:1646  */
+    {
+					/* reset flags for 'used layers';
+					 * init font and data pointers
+					 */
+				int	i;
+
+				if (!yyData || !yyFont)
+				{
+					Message(PCB_MSG_ERROR, "illegal fileformat\n");
+					YYABORT;
+				}
+				for (i = 0; i < MAX_LAYER + 2; i++)
+					LayerFlag[i] = pcb_false;
+				yyData->LayerN = 0;
+			}
+#line 1881 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 14:
+#line 265 "parse_y.y" /* yacc.c:1646  */
+    {
+					/* mark all symbols invalid */
+				int	i;
+
+				if (!yyFont)
+				{
+					Message(PCB_MSG_ERROR, "illegal fileformat\n");
+					YYABORT;
+				}
+				yyFont->Valid = pcb_false;
+				for (i = 0; i <= MAX_FONTPOSITION; i++)
+					free (yyFont->Symbol[i].Line);
+				memset(yyFont->Symbol, 0, sizeof(yyFont->Symbol));
+			}
+#line 1900 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 15:
+#line 280 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyFont->Valid = pcb_true;
+		  		SetFontInfo(yyFont);
+			}
+#line 1909 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 17:
+#line 308 "parse_y.y" /* yacc.c:1646  */
+    {
+  if (check_file_version ((yyvsp[-1].integer)) != 0)
+    {
+      YYABORT;
+    }
+}
+#line 1920 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 18:
+#line 338 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Name = (yyvsp[-1].string);
+				yyPCB->MaxWidth = MAX_COORD;
+				yyPCB->MaxHeight = MAX_COORD;
+			}
+#line 1930 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 19:
+#line 344 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Name = (yyvsp[-3].string);
+				yyPCB->MaxWidth = OU ((yyvsp[-2].measure));
+				yyPCB->MaxHeight = OU ((yyvsp[-1].measure));
+			}
+#line 1940 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 20:
+#line 350 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Name = (yyvsp[-3].string);
+				yyPCB->MaxWidth = NU ((yyvsp[-2].measure));
+				yyPCB->MaxHeight = NU ((yyvsp[-1].measure));
+			}
+#line 1950 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 24:
+#line 384 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Grid = OU ((yyvsp[-3].measure));
+				yyPCB->GridOffsetX = OU ((yyvsp[-2].measure));
+				yyPCB->GridOffsetY = OU ((yyvsp[-1].measure));
+			}
+#line 1960 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 25:
+#line 392 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Grid = OU ((yyvsp[-4].measure));
+				yyPCB->GridOffsetX = OU ((yyvsp[-3].measure));
+				yyPCB->GridOffsetY = OU ((yyvsp[-2].measure));
+				if (yy_settings_dest != CFR_invalid) {
+					if ((yyvsp[-1].integer))
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "true", POL_OVERWRITE);
+					else
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "false", POL_OVERWRITE);
+				}
+			}
+#line 1976 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 26:
+#line 407 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Grid = NU ((yyvsp[-4].measure));
+				yyPCB->GridOffsetX = NU ((yyvsp[-3].measure));
+				yyPCB->GridOffsetY = NU ((yyvsp[-2].measure));
+				if (yy_settings_dest != CFR_invalid) {
+					if ((yyvsp[-1].integer))
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "true", POL_OVERWRITE);
+					else
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "false", POL_OVERWRITE);
+				}
+			}
+#line 1992 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 27:
+#line 441 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->CursorX = OU ((yyvsp[-3].measure));
+				yyPCB->CursorY = OU ((yyvsp[-2].measure));
+				yyPCB->Zoom = (yyvsp[-1].number)*2;
+			}
+#line 2002 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 28:
+#line 447 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->CursorX = NU ((yyvsp[-3].measure));
+				yyPCB->CursorY = NU ((yyvsp[-2].measure));
+				yyPCB->Zoom = (yyvsp[-1].number);
+			}
+#line 2012 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 31:
+#line 471 "parse_y.y" /* yacc.c:1646  */
+    {
+				/* Read in cmil^2 for now; in future this should be a noop. */
+				yyPCB->IsleArea = PCB_MIL_TO_COORD (PCB_MIL_TO_COORD ((yyvsp[-1].number)) / 100.0) / 100.0;
+			}
+#line 2021 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 33:
+#line 498 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->ThermScale = (yyvsp[-1].number);
+			}
+#line 2029 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 38:
+#line 537 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Bloat = NU ((yyvsp[-3].measure));
+				yyPCB->Shrink = NU ((yyvsp[-2].measure));
+				yyPCB->minWid = NU ((yyvsp[-1].measure));
+				yyPCB->minRing = NU ((yyvsp[-1].measure));
+			}
+#line 2040 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 39:
+#line 547 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Bloat = NU ((yyvsp[-4].measure));
+				yyPCB->Shrink = NU ((yyvsp[-3].measure));
+				yyPCB->minWid = NU ((yyvsp[-2].measure));
+				yyPCB->minSlk = NU ((yyvsp[-1].measure));
+				yyPCB->minRing = NU ((yyvsp[-2].measure));
+			}
+#line 2052 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 40:
+#line 558 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyPCB->Bloat = NU ((yyvsp[-6].measure));
+				yyPCB->Shrink = NU ((yyvsp[-5].measure));
+				yyPCB->minWid = NU ((yyvsp[-4].measure));
+				yyPCB->minSlk = NU ((yyvsp[-3].measure));
+				yyPCB->minDrill = NU ((yyvsp[-2].measure));
+				yyPCB->minRing = NU ((yyvsp[-1].measure));
+			}
+#line 2065 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 41:
+#line 585 "parse_y.y" /* yacc.c:1646  */
+    {
+				yy_pcb_flags = MakeFlags ((yyvsp[-1].integer) & PCB_FLAGS);
+			}
+#line 2073 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 42:
+#line 589 "parse_y.y" /* yacc.c:1646  */
+    {
+			  yy_pcb_flags = string_to_pcbflags ((yyvsp[-1].string), yyerror);
+			  free((yyvsp[-1].string));
+			}
+#line 2082 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 44:
+#line 622 "parse_y.y" /* yacc.c:1646  */
+    {
+			  layer_group_string = (yyvsp[-1].string);
+			}
+#line 2090 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 46:
+#line 670 "parse_y.y" /* yacc.c:1646  */
+    {
+				if (ParseRouteString((yyvsp[-1].string), &yyPCB->RouteStyle, "mil"))
+				{
+					Message(PCB_MSG_ERROR, "illegal route-style string\n");
+					YYABORT;
+				}
+				free((yyvsp[-1].string));
+			}
+#line 2103 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 47:
+#line 679 "parse_y.y" /* yacc.c:1646  */
+    {
+				if (ParseRouteString(((yyvsp[-1].string) == NULL ? "" : (yyvsp[-1].string)), &yyPCB->RouteStyle, "cmil"))
+				{
+					Message(PCB_MSG_ERROR, "illegal route-style string\n");
+					YYABORT;
+				}
+				free((yyvsp[-1].string));
+			}
+#line 2116 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 54:
+#line 702 "parse_y.y" /* yacc.c:1646  */
+    { attr_list = & yyPCB->Attributes; }
+#line 2122 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 58:
+#line 706 "parse_y.y" /* yacc.c:1646  */
+    {
+					/* clear pointer to force memory allocation by
+					 * the appropriate subroutine
+					 */
+				yyElement = NULL;
+			}
+#line 2133 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 60:
+#line 713 "parse_y.y" /* yacc.c:1646  */
+    { YYABORT; }
+#line 2139 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 66:
+#line 758 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewVia(yyData, NU ((yyvsp[-8].measure)), NU ((yyvsp[-7].measure)), NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)),
+				                     NU ((yyvsp[-3].measure)), (yyvsp[-2].string), (yyvsp[-1].flagtype));
+				free ((yyvsp[-2].string));
+			}
+#line 2149 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 67:
+#line 768 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewVia(yyData, OU ((yyvsp[-8].measure)), OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), (yyvsp[-2].string),
+					OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2159 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 68:
+#line 779 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewVia(yyData, OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)),
+					     OU ((yyvsp[-5].measure)) + OU((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2169 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 69:
+#line 789 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewVia(yyData, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), 2*GROUNDPLANEFRAME,
+					OU((yyvsp[-4].measure)) + 2*MASKFRAME,  OU ((yyvsp[-3].measure)), (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2179 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 70:
+#line 799 "parse_y.y" /* yacc.c:1646  */
+    {
+				Coord	hole = (OU((yyvsp[-3].measure)) * DEFAULT_DRILLINGHOLE);
+
+					/* make sure that there's enough copper left */
+				if (OU((yyvsp[-3].measure)) - hole < MIN_PINORVIACOPPER &&
+					OU((yyvsp[-3].measure)) > MIN_PINORVIACOPPER)
+					hole = OU((yyvsp[-3].measure)) - MIN_PINORVIACOPPER;
+
+				CreateNewVia(yyData, OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), 2*GROUNDPLANEFRAME,
+					OU((yyvsp[-3].measure)) + 2*MASKFRAME, hole, (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2196 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 71:
+#line 835 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewRat(yyData, NU ((yyvsp[-7].measure)), NU ((yyvsp[-6].measure)), NU ((yyvsp[-4].measure)), NU ((yyvsp[-3].measure)), (yyvsp[-5].integer), (yyvsp[-2].integer),
+					conf_core.appearance.rat_thickness, (yyvsp[-1].flagtype));
+			}
+#line 2205 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 72:
+#line 840 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewRat(yyData, OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), (yyvsp[-5].integer), (yyvsp[-2].integer),
+					conf_core.appearance.rat_thickness, OldFlags((yyvsp[-1].integer)));
+			}
+#line 2214 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 73:
+#line 871 "parse_y.y" /* yacc.c:1646  */
+    {
+				if ((yyvsp[-4].integer) <= 0 || (yyvsp[-4].integer) > MAX_LAYER + 2)
+				{
+					yyerror("Layernumber out of range");
+					YYABORT;
+				}
+				if (LayerFlag[(yyvsp[-4].integer)-1])
+				{
+					yyerror("Layernumber used twice");
+					YYABORT;
+				}
+				Layer = &yyData->Layer[(yyvsp[-4].integer)-1];
+
+					/* memory for name is already allocated */
+				if (Layer->Name != NULL)
+					free((char*)Layer->Name);
+				Layer->Name = (yyvsp[-3].string);   /* shouldn't this be strdup()'ed ? */
+				LayerFlag[(yyvsp[-4].integer)-1] = pcb_true;
+				if (yyData->LayerN + 2 < (yyvsp[-4].integer))
+				  yyData->LayerN = (yyvsp[-4].integer) - 2;
+				if ((yyvsp[-2].string) != NULL)
+					free((yyvsp[-2].string));
+			}
+#line 2242 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 85:
+#line 916 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPolygonFromRectangle(Layer,
+					OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-5].measure)) + OU ((yyvsp[-3].measure)), OU ((yyvsp[-4].measure)) + OU ((yyvsp[-2].measure)), OldFlags((yyvsp[-1].integer)));
+			}
+#line 2251 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 89:
+#line 923 "parse_y.y" /* yacc.c:1646  */
+    { attr_list = & Layer->Attributes; }
+#line 2257 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 92:
+#line 955 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineOnLayer(Layer, NU ((yyvsp[-7].measure)), NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)),
+				                            NU ((yyvsp[-3].measure)), NU ((yyvsp[-2].measure)), (yyvsp[-1].flagtype));
+			}
+#line 2266 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 93:
+#line 964 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineOnLayer(Layer, OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)),
+						     OU ((yyvsp[-3].measure)), OU ((yyvsp[-2].measure)), OldFlags((yyvsp[-1].integer)));
+			}
+#line 2275 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 94:
+#line 973 "parse_y.y" /* yacc.c:1646  */
+    {
+				/* eliminate old-style rat-lines */
+			if ((IV ((yyvsp[-1].measure)) & PCB_FLAG_RAT) == 0)
+				CreateNewLineOnLayer(Layer, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), OU ((yyvsp[-2].measure)),
+					200*GROUNDPLANEFRAME, OldFlags(IV ((yyvsp[-1].measure))));
+			}
+#line 2286 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 95:
+#line 1021 "parse_y.y" /* yacc.c:1646  */
+    {
+			  CreateNewArcOnLayer(Layer, NU ((yyvsp[-9].measure)), NU ((yyvsp[-8].measure)), NU ((yyvsp[-7].measure)), NU ((yyvsp[-6].measure)), (yyvsp[-3].number), (yyvsp[-2].number),
+			                             NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)), (yyvsp[-1].flagtype));
+			}
+#line 2295 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 96:
+#line 1030 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewArcOnLayer(Layer, OU ((yyvsp[-9].measure)), OU ((yyvsp[-8].measure)), OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), (yyvsp[-3].number), (yyvsp[-2].number),
+						    OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OldFlags((yyvsp[-1].integer)));
+			}
+#line 2304 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 97:
+#line 1039 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewArcOnLayer(Layer, OU ((yyvsp[-8].measure)), OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-6].measure)), IV ((yyvsp[-3].measure)), (yyvsp[-2].number),
+					OU ((yyvsp[-4].measure)), 200*GROUNDPLANEFRAME, OldFlags((yyvsp[-1].integer)));
+			}
+#line 2313 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 98:
+#line 1076 "parse_y.y" /* yacc.c:1646  */
+    {
+					/* use a default scale of 100% */
+				CreateNewText(Layer,yyFont,OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), (yyvsp[-3].number), 100, (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2323 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 99:
+#line 1086 "parse_y.y" /* yacc.c:1646  */
+    {
+				if ((yyvsp[-1].integer) & PCB_FLAG_ONSILK)
+				{
+					LayerTypePtr lay = &yyData->Layer[yyData->LayerN +
+						(((yyvsp[-1].integer) & PCB_FLAG_ONSOLDER) ? SOLDER_LAYER : COMPONENT_LAYER)];
+
+					CreateNewText(lay ,yyFont, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), (yyvsp[-4].number), (yyvsp[-3].number), (yyvsp[-2].string),
+						      OldFlags((yyvsp[-1].integer)));
+				}
+				else
+					CreateNewText(Layer, yyFont, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), (yyvsp[-4].number), (yyvsp[-3].number), (yyvsp[-2].string),
+						      OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2342 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 100:
+#line 1104 "parse_y.y" /* yacc.c:1646  */
+    {
+				/* FIXME: shouldn't know about .f */
+				/* I don't think this matters because anything with hi_format
+				 * will have the silk on its own layer in the file rather
+				 * than using the PCB_FLAG_ONSILK and having it in a copper layer.
+				 * Thus there is no need for anything besides the 'else'
+				 * part of this code.
+				 */
+				if ((yyvsp[-1].flagtype).f & PCB_FLAG_ONSILK)
+				{
+					LayerTypePtr lay = &yyData->Layer[yyData->LayerN +
+						(((yyvsp[-1].flagtype).f & PCB_FLAG_ONSOLDER) ? SOLDER_LAYER : COMPONENT_LAYER)];
+
+					CreateNewText(lay, yyFont, NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), (yyvsp[-4].number), (yyvsp[-3].number), (yyvsp[-2].string), (yyvsp[-1].flagtype));
+				}
+				else
+					CreateNewText(Layer, yyFont, NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), (yyvsp[-4].number), (yyvsp[-3].number), (yyvsp[-2].string), (yyvsp[-1].flagtype));
+				free ((yyvsp[-2].string));
+			}
+#line 2366 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 101:
+#line 1153 "parse_y.y" /* yacc.c:1646  */
+    {
+				Polygon = CreateNewPolygon(Layer, (yyvsp[-2].flagtype));
+			}
+#line 2374 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 102:
+#line 1158 "parse_y.y" /* yacc.c:1646  */
+    {
+				pcb_cardinal_t contour, contour_start, contour_end;
+				pcb_bool bad_contour_found = pcb_false;
+				/* ignore junk */
+				for (contour = 0; contour <= Polygon->HoleIndexN; contour++)
+				  {
+				    contour_start = (contour == 0) ?
+						      0 : Polygon->HoleIndex[contour - 1];
+				    contour_end = (contour == Polygon->HoleIndexN) ?
+						 Polygon->PointN :
+						 Polygon->HoleIndex[contour];
+				    if (contour_end - contour_start < 3)
+				      bad_contour_found = pcb_true;
+				  }
+
+				if (bad_contour_found)
+				  {
+				    Message(PCB_MSG_WARNING, "WARNING parsing file '%s'\n"
+					    "    line:        %i\n"
+					    "    description: 'ignored polygon (< 3 points in a contour)'\n",
+					    yyfilename, pcb_lineno);
+				    DestroyObject(yyData, PCB_TYPE_POLYGON, Layer, Polygon, Polygon);
+				  }
+				else
+				  {
+				    SetPolygonBoundingBox (Polygon);
+				    if (!Layer->polygon_tree)
+				      Layer->polygon_tree = r_create_tree (NULL, 0, 0);
+				    r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
+				  }
+			}
+#line 2410 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 105:
+#line 1198 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewHoleInPolygon (Polygon);
+			}
+#line 2418 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 109:
+#line 1212 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPointInPolygon(Polygon, OU ((yyvsp[-2].measure)), OU ((yyvsp[-1].measure)));
+			}
+#line 2426 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 110:
+#line 1216 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPointInPolygon(Polygon, NU ((yyvsp[-2].measure)), NU ((yyvsp[-1].measure)));
+			}
+#line 2434 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 116:
+#line 1287 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, NoFlags(),
+					(yyvsp[-6].string), (yyvsp[-5].string), NULL, OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), (yyvsp[-2].integer), 100, NoFlags(), pcb_false);
+				free ((yyvsp[-6].string));
+				free ((yyvsp[-5].string));
+				pin_num = 1;
+			}
+#line 2446 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 117:
+#line 1295 "parse_y.y" /* yacc.c:1646  */
+    {
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+#line 2454 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 118:
+#line 1305 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, OldFlags((yyvsp[-9].integer)),
+					(yyvsp[-8].string), (yyvsp[-7].string), NULL, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), IV ((yyvsp[-4].measure)), IV ((yyvsp[-3].measure)), OldFlags((yyvsp[-2].integer)), pcb_false);
+				free ((yyvsp[-8].string));
+				free ((yyvsp[-7].string));
+				pin_num = 1;
+			}
+#line 2466 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 119:
+#line 1313 "parse_y.y" /* yacc.c:1646  */
+    {
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+#line 2474 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 120:
+#line 1323 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, OldFlags((yyvsp[-10].integer)),
+					(yyvsp[-9].string), (yyvsp[-8].string), (yyvsp[-7].string), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), IV ((yyvsp[-4].measure)), IV ((yyvsp[-3].measure)), OldFlags((yyvsp[-2].integer)), pcb_false);
+				free ((yyvsp[-9].string));
+				free ((yyvsp[-8].string));
+				free ((yyvsp[-7].string));
+				pin_num = 1;
+			}
+#line 2487 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 121:
+#line 1332 "parse_y.y" /* yacc.c:1646  */
+    {
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+#line 2495 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 122:
+#line 1343 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, OldFlags((yyvsp[-12].integer)),
+					(yyvsp[-11].string), (yyvsp[-10].string), (yyvsp[-9].string), OU ((yyvsp[-8].measure)) + OU ((yyvsp[-6].measure)), OU ((yyvsp[-7].measure)) + OU ((yyvsp[-5].measure)),
+					(yyvsp[-4].number), (yyvsp[-3].number), OldFlags((yyvsp[-2].integer)), pcb_false);
+				yyElement->MarkX = OU ((yyvsp[-8].measure));
+				yyElement->MarkY = OU ((yyvsp[-7].measure));
+				free ((yyvsp[-11].string));
+				free ((yyvsp[-10].string));
+				free ((yyvsp[-9].string));
+			}
+#line 2510 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 123:
+#line 1354 "parse_y.y" /* yacc.c:1646  */
+    {
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+#line 2518 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 124:
+#line 1365 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, (yyvsp[-12].flagtype),
+					(yyvsp[-11].string), (yyvsp[-10].string), (yyvsp[-9].string), NU ((yyvsp[-8].measure)) + NU ((yyvsp[-6].measure)), NU ((yyvsp[-7].measure)) + NU ((yyvsp[-5].measure)),
+					(yyvsp[-4].number), (yyvsp[-3].number), (yyvsp[-2].flagtype), pcb_false);
+				yyElement->MarkX = NU ((yyvsp[-8].measure));
+				yyElement->MarkY = NU ((yyvsp[-7].measure));
+				free ((yyvsp[-11].string));
+				free ((yyvsp[-10].string));
+				free ((yyvsp[-9].string));
+			}
+#line 2533 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 125:
+#line 1376 "parse_y.y" /* yacc.c:1646  */
+    {
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+#line 2541 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 133:
+#line 1456 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineInElement(yyElement, NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)), NU ((yyvsp[-3].measure)), NU ((yyvsp[-2].measure)), NU ((yyvsp[-1].measure)));
+			}
+#line 2549 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 134:
+#line 1461 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineInElement(yyElement, OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), OU ((yyvsp[-2].measure)), OU ((yyvsp[-1].measure)));
+			}
+#line 2557 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 135:
+#line 1466 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewArcInElement(yyElement, NU ((yyvsp[-7].measure)), NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)), (yyvsp[-3].number), (yyvsp[-2].number), NU ((yyvsp[-1].measure)));
+			}
+#line 2565 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 136:
+#line 1471 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewArcInElement(yyElement, OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), (yyvsp[-3].number), (yyvsp[-2].number), OU ((yyvsp[-1].measure)));
+			}
+#line 2573 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 137:
+#line 1476 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement->MarkX = NU ((yyvsp[-2].measure));
+				yyElement->MarkY = NU ((yyvsp[-1].measure));
+			}
+#line 2582 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 138:
+#line 1481 "parse_y.y" /* yacc.c:1646  */
+    {
+				yyElement->MarkX = OU ((yyvsp[-2].measure));
+				yyElement->MarkY = OU ((yyvsp[-1].measure));
+			}
+#line 2591 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 139:
+#line 1485 "parse_y.y" /* yacc.c:1646  */
+    { attr_list = & yyElement->Attributes; }
+#line 2597 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 147:
+#line 1500 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineInElement(yyElement, NU ((yyvsp[-5].measure)) + yyElement->MarkX,
+					NU ((yyvsp[-4].measure)) + yyElement->MarkY, NU ((yyvsp[-3].measure)) + yyElement->MarkX,
+					NU ((yyvsp[-2].measure)) + yyElement->MarkY, NU ((yyvsp[-1].measure)));
+			}
+#line 2607 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 148:
+#line 1506 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineInElement(yyElement, OU ((yyvsp[-5].measure)) + yyElement->MarkX,
+					OU ((yyvsp[-4].measure)) + yyElement->MarkY, OU ((yyvsp[-3].measure)) + yyElement->MarkX,
+					OU ((yyvsp[-2].measure)) + yyElement->MarkY, OU ((yyvsp[-1].measure)));
+			}
+#line 2617 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 149:
+#line 1513 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewArcInElement(yyElement, NU ((yyvsp[-7].measure)) + yyElement->MarkX,
+					NU ((yyvsp[-6].measure)) + yyElement->MarkY, NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)), (yyvsp[-3].number), (yyvsp[-2].number), NU ((yyvsp[-1].measure)));
+			}
+#line 2626 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 150:
+#line 1518 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewArcInElement(yyElement, OU ((yyvsp[-7].measure)) + yyElement->MarkX,
+					OU ((yyvsp[-6].measure)) + yyElement->MarkY, OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), (yyvsp[-3].number), (yyvsp[-2].number), OU ((yyvsp[-1].measure)));
+			}
+#line 2635 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 151:
+#line 1522 "parse_y.y" /* yacc.c:1646  */
+    { attr_list = & yyElement->Attributes; }
+#line 2641 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 153:
+#line 1564 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPin(yyElement, NU ((yyvsp[-9].measure)) + yyElement->MarkX,
+					NU ((yyvsp[-8].measure)) + yyElement->MarkY, NU ((yyvsp[-7].measure)), NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)), (yyvsp[-3].string),
+					(yyvsp[-2].string), (yyvsp[-1].flagtype));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2653 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 154:
+#line 1576 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPin(yyElement, OU ((yyvsp[-9].measure)) + yyElement->MarkX,
+					OU ((yyvsp[-8].measure)) + yyElement->MarkY, OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), (yyvsp[-3].string),
+					(yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2665 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 155:
+#line 1588 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPin(yyElement, OU ((yyvsp[-7].measure)), OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), 2*GROUNDPLANEFRAME,
+					OU ((yyvsp[-5].measure)) + 2*MASKFRAME, OU ((yyvsp[-4].measure)), (yyvsp[-3].string), (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2676 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 156:
+#line 1599 "parse_y.y" /* yacc.c:1646  */
+    {
+				char	p_number[8];
+
+				sprintf(p_number, "%d", pin_num++);
+				CreateNewPin(yyElement, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), 2*GROUNDPLANEFRAME,
+					OU ((yyvsp[-4].measure)) + 2*MASKFRAME, OU ((yyvsp[-3].measure)), (yyvsp[-2].string), p_number, OldFlags((yyvsp[-1].integer)));
+
+				free ((yyvsp[-2].string));
+			}
+#line 2690 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 157:
+#line 1615 "parse_y.y" /* yacc.c:1646  */
+    {
+				Coord	hole = OU ((yyvsp[-3].measure)) * DEFAULT_DRILLINGHOLE;
+				char	p_number[8];
+
+					/* make sure that there's enough copper left */
+				if (OU ((yyvsp[-3].measure)) - hole < MIN_PINORVIACOPPER &&
+					OU ((yyvsp[-3].measure)) > MIN_PINORVIACOPPER)
+					hole = OU ((yyvsp[-3].measure)) - MIN_PINORVIACOPPER;
+
+				sprintf(p_number, "%d", pin_num++);
+				CreateNewPin(yyElement, OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), 2*GROUNDPLANEFRAME,
+					OU ((yyvsp[-3].measure)) + 2*MASKFRAME, hole, (yyvsp[-2].string), p_number, OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2709 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 158:
+#line 1669 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPad(yyElement, NU ((yyvsp[-10].measure)) + yyElement->MarkX,
+					NU ((yyvsp[-9].measure)) + yyElement->MarkY,
+					NU ((yyvsp[-8].measure)) + yyElement->MarkX,
+					NU ((yyvsp[-7].measure)) + yyElement->MarkY, NU ((yyvsp[-6].measure)), NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)),
+					(yyvsp[-3].string), (yyvsp[-2].string), (yyvsp[-1].flagtype));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2723 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 159:
+#line 1683 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPad(yyElement,OU ((yyvsp[-10].measure)) + yyElement->MarkX,
+					OU ((yyvsp[-9].measure)) + yyElement->MarkY, OU ((yyvsp[-8].measure)) + yyElement->MarkX,
+					OU ((yyvsp[-7].measure)) + yyElement->MarkY, OU ((yyvsp[-6].measure)), OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)),
+					(yyvsp[-3].string), (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2736 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 160:
+#line 1696 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewPad(yyElement,OU ((yyvsp[-8].measure)),OU ((yyvsp[-7].measure)),OU ((yyvsp[-6].measure)),OU ((yyvsp[-5].measure)),OU ((yyvsp[-4].measure)), 2*GROUNDPLANEFRAME,
+					OU ((yyvsp[-4].measure)) + 2*MASKFRAME, (yyvsp[-3].string), (yyvsp[-2].string), OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2747 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 161:
+#line 1707 "parse_y.y" /* yacc.c:1646  */
+    {
+				char		p_number[8];
+
+				sprintf(p_number, "%d", pin_num++);
+				CreateNewPad(yyElement,OU ((yyvsp[-7].measure)),OU ((yyvsp[-6].measure)),OU ((yyvsp[-5].measure)),OU ((yyvsp[-4].measure)),OU ((yyvsp[-3].measure)), 2*GROUNDPLANEFRAME,
+					OU ((yyvsp[-3].measure)) + 2*MASKFRAME, (yyvsp[-2].string),p_number, OldFlags((yyvsp[-1].integer)));
+				free ((yyvsp[-2].string));
+			}
+#line 2760 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 162:
+#line 1717 "parse_y.y" /* yacc.c:1646  */
+    { (yyval.flagtype) = OldFlags((yyvsp[0].integer)); }
+#line 2766 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 163:
+#line 1718 "parse_y.y" /* yacc.c:1646  */
+    { (yyval.flagtype) = string_to_flags ((yyvsp[0].string), yyerror); free((yyvsp[0].string)); }
+#line 2772 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 167:
+#line 1748 "parse_y.y" /* yacc.c:1646  */
+    {
+				if ((yyvsp[-3].integer) <= 0 || (yyvsp[-3].integer) > MAX_FONTPOSITION)
+				{
+					yyerror("fontposition out of range");
+					YYABORT;
+				}
+				Symbol = &yyFont->Symbol[(yyvsp[-3].integer)];
+				if (Symbol->Valid)
+				{
+					yyerror("symbol ID used twice");
+					YYABORT;
+				}
+				Symbol->Valid = pcb_true;
+				Symbol->Delta = NU ((yyvsp[-2].measure));
+			}
+#line 2792 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 168:
+#line 1764 "parse_y.y" /* yacc.c:1646  */
+    {
+				if ((yyvsp[-3].integer) <= 0 || (yyvsp[-3].integer) > MAX_FONTPOSITION)
+				{
+					yyerror("fontposition out of range");
+					YYABORT;
+				}
+				Symbol = &yyFont->Symbol[(yyvsp[-3].integer)];
+				if (Symbol->Valid)
+				{
+					yyerror("symbol ID used twice");
+					YYABORT;
+				}
+				Symbol->Valid = pcb_true;
+				Symbol->Delta = OU ((yyvsp[-2].measure));
+			}
+#line 2812 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 174:
+#line 1811 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineInSymbol(Symbol, OU ((yyvsp[-5].measure)), OU ((yyvsp[-4].measure)), OU ((yyvsp[-3].measure)), OU ((yyvsp[-2].measure)), OU ((yyvsp[-1].measure)));
+			}
+#line 2820 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 175:
+#line 1818 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewLineInSymbol(Symbol, NU ((yyvsp[-5].measure)), NU ((yyvsp[-4].measure)), NU ((yyvsp[-3].measure)), NU ((yyvsp[-2].measure)), NU ((yyvsp[-1].measure)));
+			}
+#line 2828 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 183:
+#line 1873 "parse_y.y" /* yacc.c:1646  */
+    {
+				Menu = CreateNewNet((LibraryTypePtr)&yyPCB->NetlistLib, (yyvsp[-3].string), (yyvsp[-2].string));
+				free ((yyvsp[-3].string));
+				free ((yyvsp[-2].string));
+			}
+#line 2838 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 189:
+#line 1908 "parse_y.y" /* yacc.c:1646  */
+    {
+				CreateNewConnection(Menu, (yyvsp[-1].string));
+				free ((yyvsp[-1].string));
+			}
+#line 2847 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 197:
+#line 1955 "parse_y.y" /* yacc.c:1646  */
+    { rats_patch_append(yyPCB, RATP_ADD_CONN, (yyvsp[-2].string), (yyvsp[-1].string), NULL); free((yyvsp[-2].string)); free((yyvsp[-1].string)); }
+#line 2853 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 198:
+#line 1956 "parse_y.y" /* yacc.c:1646  */
+    { rats_patch_append(yyPCB, RATP_DEL_CONN, (yyvsp[-2].string), (yyvsp[-1].string), NULL); free((yyvsp[-2].string)); free((yyvsp[-1].string)); }
+#line 2859 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 199:
+#line 1957 "parse_y.y" /* yacc.c:1646  */
+    { rats_patch_append(yyPCB, RATP_CHANGE_ATTRIB, (yyvsp[-3].string), (yyvsp[-2].string), (yyvsp[-1].string)); free((yyvsp[-3].string)); free((yyvsp[-2].string)); free((yyvsp[-1].string)); }
+#line 2865 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 200:
+#line 1985 "parse_y.y" /* yacc.c:1646  */
+    {
+			  CreateNewAttribute (attr_list, (yyvsp[-2].string), (yyvsp[-1].string) ? (yyvsp[-1].string) : (char *)"");
+				free ((yyvsp[-2].string));
+				free ((yyvsp[-1].string));
+			}
+#line 2875 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 201:
+#line 1992 "parse_y.y" /* yacc.c:1646  */
+    { (yyval.string) = (yyvsp[0].string); }
+#line 2881 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 202:
+#line 1993 "parse_y.y" /* yacc.c:1646  */
+    { (yyval.string) = 0; }
+#line 2887 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 203:
+#line 1997 "parse_y.y" /* yacc.c:1646  */
+    { (yyval.number) = (yyvsp[0].number); }
+#line 2893 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 204:
+#line 1998 "parse_y.y" /* yacc.c:1646  */
+    { (yyval.number) = (yyvsp[0].integer); }
+#line 2899 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 205:
+#line 2003 "parse_y.y" /* yacc.c:1646  */
+    { do_measure(&(yyval.measure), (yyvsp[0].number), PCB_MIL_TO_COORD ((yyvsp[0].number)) / 100.0, 0); }
+#line 2905 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 206:
+#line 2004 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MIL_TO_COORD ((yyvsp[-1].number)) / 100000.0); }
+#line 2911 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 207:
+#line 2005 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MIL_TO_COORD ((yyvsp[-1].number)) / 100.0); }
+#line 2917 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 208:
+#line 2006 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MIL_TO_COORD ((yyvsp[-1].number))); }
+#line 2923 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 209:
+#line 2007 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_INCH_TO_COORD ((yyvsp[-1].number))); }
+#line 2929 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 210:
+#line 2008 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MM_TO_COORD ((yyvsp[-1].number)) / 1000000.0); }
+#line 2935 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 211:
+#line 2009 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MM_TO_COORD ((yyvsp[-1].number)) / 1000.0); }
+#line 2941 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 212:
+#line 2010 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MM_TO_COORD ((yyvsp[-1].number))); }
+#line 2947 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 213:
+#line 2011 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MM_TO_COORD ((yyvsp[-1].number)) * 1000.0); }
+#line 2953 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+  case 214:
+#line 2012 "parse_y.y" /* yacc.c:1646  */
+    { M ((yyval.measure), (yyvsp[-1].number), PCB_MM_TO_COORD ((yyvsp[-1].number)) * 1000000.0); }
+#line 2959 "parse_y.c" /* yacc.c:1646  */
+    break;
+
+
+#line 2963 "parse_y.c" /* yacc.c:1646  */
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == YYEOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval);
+          yychar = YYEMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYTERROR;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  return yyresult;
+}
+#line 2015 "parse_y.y" /* yacc.c:1906  */
+
+
+/* ---------------------------------------------------------------------------
+ * error routine called by parser library
+ */
+int yyerror(const char * s)
+{
+	Message(PCB_MSG_ERROR, "ERROR parsing file '%s'\n"
+		"    line:        %i\n"
+		"    description: '%s'\n",
+		yyfilename, pcb_lineno, s);
+	return(0);
+}
+
+int pcb_wrap()
+{
+  return 1;
+}
+
+static int
+check_file_version (int ver)
+{
+  if ( ver > PCB_FILE_VERSION ) {
+    Message (PCB_MSG_DEFAULT, "ERROR:  The file you are attempting to load is in a format\n"
+	     "which is too new for this version of pcb.  To load this file\n"
+	     "you need a version of pcb which is >= %d.  If you are\n"
+	     "using a version built from git source, the source date\n"
+	     "must be >= %d.  This copy of pcb can only read files\n"
+	     "up to file version %d.\n", ver, ver, PCB_FILE_VERSION);
+    return 1;
+  }
+
+  return 0;
+}
+
+static void
+do_measure (PLMeasure *m, Coord i, double d, int u)
+{
+  m->ival = i;
+  m->bval = pcb_round (d);
+  m->dval = d;
+  m->has_units = u;
+}
+
+static int
+integer_value (PLMeasure m)
+{
+  if (m.has_units)
+    yyerror("units ignored here");
+  return m.ival;
+}
+
+static Coord
+old_units (PLMeasure m)
+{
+  if (m.has_units)
+    return m.bval;
+  return pcb_round (PCB_MIL_TO_COORD (m.ival));
+}
+
+static Coord
+new_units (PLMeasure m)
+{
+  if (m.has_units)
+    return m.bval;
+  /* if there's no unit m.dval already contains the converted value */
+  return pcb_round (m.dval);
+}
diff --git a/src_plugins/io_pcb/parse_y.h b/src_plugins/io_pcb/parse_y.h
new file mode 100644
index 0000000..758b2ca
--- /dev/null
+++ b/src_plugins/io_pcb/parse_y.h
@@ -0,0 +1,123 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY_PCB_PARSE_Y_H_INCLUDED
+# define YY_PCB_PARSE_Y_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int pcb_debug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    FLOATING = 258,
+    INTEGER = 259,
+    CHAR_CONST = 260,
+    STRING = 261,
+    T_FILEVERSION = 262,
+    T_PCB = 263,
+    T_LAYER = 264,
+    T_VIA = 265,
+    T_RAT = 266,
+    T_LINE = 267,
+    T_ARC = 268,
+    T_RECTANGLE = 269,
+    T_TEXT = 270,
+    T_ELEMENTLINE = 271,
+    T_ELEMENT = 272,
+    T_PIN = 273,
+    T_PAD = 274,
+    T_GRID = 275,
+    T_FLAGS = 276,
+    T_SYMBOL = 277,
+    T_SYMBOLLINE = 278,
+    T_CURSOR = 279,
+    T_ELEMENTARC = 280,
+    T_MARK = 281,
+    T_GROUPS = 282,
+    T_STYLES = 283,
+    T_POLYGON = 284,
+    T_POLYGON_HOLE = 285,
+    T_NETLIST = 286,
+    T_NET = 287,
+    T_CONN = 288,
+    T_NETLISTPATCH = 289,
+    T_ADD_CONN = 290,
+    T_DEL_CONN = 291,
+    T_CHANGE_ATTRIB = 292,
+    T_AREA = 293,
+    T_THERMAL = 294,
+    T_DRC = 295,
+    T_ATTRIBUTE = 296,
+    T_UMIL = 297,
+    T_CMIL = 298,
+    T_MIL = 299,
+    T_IN = 300,
+    T_NM = 301,
+    T_UM = 302,
+    T_MM = 303,
+    T_M = 304,
+    T_KM = 305
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 118 "parse_y.y" /* yacc.c:1909  */
+
+	int		integer;
+	double		number;
+	char		*string;
+	FlagType	flagtype;
+	PLMeasure	measure;
+
+#line 113 "parse_y.h" /* yacc.c:1909  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE pcb_lval;
+
+int pcb_parse (void);
+
+#endif /* !YY_PCB_PARSE_Y_H_INCLUDED  */
diff --git a/src_plugins/io_pcb/parse_y.y b/src_plugins/io_pcb/parse_y.y
new file mode 100644
index 0000000..8fae060
--- /dev/null
+++ b/src_plugins/io_pcb/parse_y.y
@@ -0,0 +1,2082 @@
+
+/*
+ * ************************** README *******************
+ *
+ * If the file format is modified in any way, update
+ * PCB_FILE_VERSION in file.h
+ *
+ * ************************** README *******************
+ */
+
+%{
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* grammar to parse ASCII input of PCB description
+ */
+
+#include "config.h"
+#include "conf_core.h"
+#include "global.h"
+#include "layer.h"
+#include "create.h"
+#include "data.h"
+#include "error.h"
+#include "file.h"
+#include "mymem.h"
+#include "misc.h"
+#include "parse_l.h"
+#include "polygon.h"
+#include "remove.h"
+#include "rtree.h"
+#include "strflags.h"
+#include "thermal.h"
+#include "rats_patch.h"
+#include "flags.h"
+#include "route_style.h"
+#include "compat_misc.h"
+
+#ifdef HAVE_LIBDMALLOC
+# include <dmalloc.h> /* see http://dmalloc.com */
+#endif
+
+
+
+static	LayerTypePtr	Layer;
+static	PolygonTypePtr	Polygon;
+static	SymbolTypePtr	Symbol;
+static	int		pin_num;
+static	LibraryMenuTypePtr	Menu;
+static	pcb_bool			LayerFlag[MAX_LAYER + 2];
+
+extern	char			*yytext;		/* defined by LEX */
+extern	PCBTypePtr		yyPCB;
+extern	DataTypePtr		yyData;
+extern	ElementTypePtr	yyElement;
+extern	FontTypePtr		yyFont;
+extern	int				pcb_lineno;		/* linenumber */
+extern	char			*yyfilename;	/* in this file */
+extern	conf_role_t yy_settings_dest;
+extern FlagType yy_pcb_flags;
+
+static char *layer_group_string;
+
+static AttributeListTypePtr attr_list;
+
+int yyerror(const char *s);
+int yylex();
+static int check_file_version (int);
+
+static void do_measure (PLMeasure *m, Coord i, double d, int u);
+#define M(r,f,d) do_measure (&(r), f, d, 1)
+
+/* Macros for interpreting what "measure" means - integer value only,
+   old units (mil), or new units (cmil).  */
+#define IV(m) integer_value (m)
+#define OU(m) old_units (m)
+#define NU(m) new_units (m)
+
+static int integer_value (PLMeasure m);
+static Coord old_units (PLMeasure m);
+static Coord new_units (PLMeasure m);
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+
+#include "parse_y.h"
+
+%}
+
+%name-prefix "pcb_"
+
+%verbose
+
+%union									/* define YYSTACK type */
+{
+	int		integer;
+	double		number;
+	char		*string;
+	FlagType	flagtype;
+	PLMeasure	measure;
+}
+
+%token	<number>	FLOATING		/* line thickness, coordinates ... */
+%token	<integer>	INTEGER	CHAR_CONST	/* flags ... */
+%token	<string>	STRING			/* element names ... */
+
+%token	T_FILEVERSION T_PCB T_LAYER T_VIA T_RAT T_LINE T_ARC T_RECTANGLE T_TEXT T_ELEMENTLINE
+%token	T_ELEMENT T_PIN T_PAD T_GRID T_FLAGS T_SYMBOL T_SYMBOLLINE T_CURSOR
+%token	T_ELEMENTARC T_MARK T_GROUPS T_STYLES T_POLYGON T_POLYGON_HOLE T_NETLIST T_NET T_CONN
+%token	T_NETLISTPATCH T_ADD_CONN T_DEL_CONN T_CHANGE_ATTRIB
+%token	T_AREA T_THERMAL T_DRC T_ATTRIBUTE
+%token	T_UMIL T_CMIL T_MIL T_IN T_NM T_UM T_MM T_M T_KM
+%type	<integer>	symbolid
+%type	<string>	opt_string
+%type	<flagtype>	flags
+%type	<number>	number
+%type	<measure>	measure
+
+%%
+
+parse
+		: parsepcb
+		| parsedata
+		| parsefont
+		| error { YYABORT; }
+		;
+
+/* %start-doc pcbfile 00pcb
+ at nodetype subsection
+ at nodename %s syntax
+
+A special note about units: Older versions of @code{pcb} used mils
+(1/1000 inch) as the base unit; a value of 500 in the file meant
+half an inch.  Newer versions uses a "high resolution" syntax,
+where the base unit is 1/100 of a mil (0.000010 inch); a value of 500 in
+the file means 5 mils.  As a general rule, the variants of each entry
+listed below which use square brackets are the high resolution formats
+and use the 1/100 mil units, and the ones with parentheses are the older
+variants and use 1 mil units.  Note that when multiple variants
+are listed, the most recent (and most preferred) format is the first
+listed.
+
+Symbolic and numeric flags (SFlags and NFlags) are described in
+ at ref{Object Flags}.
+
+%end-doc */
+
+parsepcb
+		:	{
+					/* reset flags for 'used layers';
+					 * init font and data pointers
+					 */
+				int	i;
+
+				if (!yyPCB)
+				{
+					Message(PCB_MSG_ERROR, "illegal fileformat\n");
+					YYABORT;
+				}
+				for (i = 0; i < MAX_LAYER + 2; i++)
+					LayerFlag[i] = pcb_false;
+				yyFont = &yyPCB->Font;
+				yyData = yyPCB->Data;
+				yyData->pcb = yyPCB;
+				yyData->LayerN = 0;
+				yyPCB->NetlistPatches = yyPCB->NetlistPatchLast = NULL;
+				layer_group_string = NULL;
+			}
+		  pcbfileversion
+		  pcbname
+		  pcbgrid
+		  pcbcursor
+		  polyarea
+		  pcbthermal
+		  pcbdrc
+		  pcbflags
+		  pcbgroups
+		  pcbstyles
+		  pcbfont
+		  pcbdata
+		  pcbnetlist
+		  pcbnetlistpatch
+			{
+			  PCBTypePtr pcb_save = PCB;
+			  if ((yy_settings_dest != CFR_invalid) && (layer_group_string != NULL))
+					conf_set(yy_settings_dest, "design/groups", -1, layer_group_string, POL_OVERWRITE);
+			  CreateNewPCBPost (yyPCB, 0);
+			  if (ParseGroupString(layer_group_string, &yyPCB->LayerGroups, yyData->LayerN))
+			    {
+			      Message(PCB_MSG_ERROR, "illegal layer-group string\n");
+			      YYABORT;
+			    }
+			/* initialize the polygon clipping now since
+			 * we didn't know the layer grouping before.
+			 */
+			free(layer_group_string);
+			PCB = yyPCB;
+			ALLPOLYGON_LOOP (yyData);
+			{
+			  InitClip (yyData, layer, polygon);
+			}
+			ENDALL_LOOP;
+			PCB = pcb_save;
+			}
+
+		| { PreLoadElementPCB ();
+		    layer_group_string = NULL; }
+		  element
+		  { LayerFlag[0] = pcb_true;
+		    LayerFlag[1] = pcb_true;
+		    yyData->LayerN = 2;
+		    PostLoadElementPCB ();
+		  }
+		;
+
+parsedata
+		:	{
+					/* reset flags for 'used layers';
+					 * init font and data pointers
+					 */
+				int	i;
+
+				if (!yyData || !yyFont)
+				{
+					Message(PCB_MSG_ERROR, "illegal fileformat\n");
+					YYABORT;
+				}
+				for (i = 0; i < MAX_LAYER + 2; i++)
+					LayerFlag[i] = pcb_false;
+				yyData->LayerN = 0;
+			}
+		 pcbdata
+		;
+
+pcbfont
+		: parsefont
+		|
+		;
+
+parsefont
+		:
+			{
+					/* mark all symbols invalid */
+				int	i;
+
+				if (!yyFont)
+				{
+					Message(PCB_MSG_ERROR, "illegal fileformat\n");
+					YYABORT;
+				}
+				yyFont->Valid = pcb_false;
+				for (i = 0; i <= MAX_FONTPOSITION; i++)
+					free (yyFont->Symbol[i].Line);
+				memset(yyFont->Symbol, 0, sizeof(yyFont->Symbol));
+			}
+		  symbols
+			{
+				yyFont->Valid = pcb_true;
+		  		SetFontInfo(yyFont);
+			}
+		;
+
+/* %start-doc pcbfile FileVersion
+
+ at syntax
+FileVersion[Version]
+ at end syntax
+
+ at table @var
+ at item Version
+File format version.  This version number represents the date when the pcb file
+format was last changed.
+ at end table
+
+Any version of pcb build from sources equal to or newer
+than this number should be able to read the file.  If this line is not present
+in the input file then file format compatibility is not checked.
+
+
+%end-doc */
+
+pcbfileversion
+: |
+T_FILEVERSION '[' INTEGER ']'
+{
+  if (check_file_version ($3) != 0)
+    {
+      YYABORT;
+    }
+}
+;
+
+/* %start-doc pcbfile PCB
+
+ at syntax
+PCB ["Name" Width Height]
+PCB ("Name" Width Height]
+PCB ("Name")
+ at end syntax
+
+ at table @var
+ at item Name
+Name of the PCB project
+ at item Width Height
+Size of the board
+ at end table
+
+If you don't specify the size of the board, a very large default is
+chosen.
+
+%end-doc */
+
+pcbname
+		: T_PCB '(' STRING ')'
+			{
+				yyPCB->Name = $3;
+				yyPCB->MaxWidth = MAX_COORD;
+				yyPCB->MaxHeight = MAX_COORD;
+			}
+		| T_PCB '(' STRING measure measure ')'
+			{
+				yyPCB->Name = $3;
+				yyPCB->MaxWidth = OU ($4);
+				yyPCB->MaxHeight = OU ($5);
+			}
+		| T_PCB '[' STRING measure measure ']'
+			{
+				yyPCB->Name = $3;
+				yyPCB->MaxWidth = NU ($4);
+				yyPCB->MaxHeight = NU ($5);
+			}
+		;
+
+/* %start-doc pcbfile Grid
+
+ at syntax
+Grid [Step OffsetX OffsetY Visible]
+Grid (Step OffsetX OffsetY Visible)
+Grid (Step OffsetX OffsetY)
+ at end syntax
+
+ at table @var
+ at item Step
+Distance from one grid point to adjacent points.  This value may be a
+floating point number for the first two variants.
+ at item OffsetX OffsetY
+The "origin" of the grid.  Normally zero.
+ at item Visible
+If non-zero, the grid will be visible on the screen.
+ at end table
+
+%end-doc */
+
+pcbgrid
+		: pcbgridold
+		| pcbgridnew
+		| pcbhigrid
+		;
+pcbgridold
+		: T_GRID '(' measure measure measure ')'
+			{
+				yyPCB->Grid = OU ($3);
+				yyPCB->GridOffsetX = OU ($4);
+				yyPCB->GridOffsetY = OU ($5);
+			}
+		;
+pcbgridnew
+		: T_GRID '(' measure measure measure INTEGER ')'
+			{
+				yyPCB->Grid = OU ($3);
+				yyPCB->GridOffsetX = OU ($4);
+				yyPCB->GridOffsetY = OU ($5);
+				if (yy_settings_dest != CFR_invalid) {
+					if ($6)
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "true", POL_OVERWRITE);
+					else
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "false", POL_OVERWRITE);
+				}
+			}
+		;
+
+pcbhigrid
+		: T_GRID '[' measure measure measure INTEGER ']'
+			{
+				yyPCB->Grid = NU ($3);
+				yyPCB->GridOffsetX = NU ($4);
+				yyPCB->GridOffsetY = NU ($5);
+				if (yy_settings_dest != CFR_invalid) {
+					if ($6)
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "true", POL_OVERWRITE);
+					else
+						conf_set(yy_settings_dest, "editor/draw_grid", -1, "false", POL_OVERWRITE);
+				}
+			}
+		;
+
+/* %start-doc pcbfile Cursor
+
+ at syntax
+Cursor [X Y Zoom]
+Cursor (X Y Zoom)
+ at end syntax
+
+ at table @var
+ at item X Y
+Location of the cursor when the board was saved.
+ at item Zoom
+The current zoom factor.  Note that a zoom factor of "0" means 1 mil
+per screen pixel, N means @math{2^N} mils per screen pixel, etc.  The
+first variant accepts floating point numbers.  The special value
+"1000" means "zoom to fit"
+ at end table
+
+%end-doc */
+
+pcbcursor
+		: T_CURSOR '(' measure measure number ')'
+			{
+				yyPCB->CursorX = OU ($3);
+				yyPCB->CursorY = OU ($4);
+				yyPCB->Zoom = $5*2;
+			}
+		| T_CURSOR '[' measure measure number ']'
+			{
+				yyPCB->CursorX = NU ($3);
+				yyPCB->CursorY = NU ($4);
+				yyPCB->Zoom = $5;
+			}
+		|
+		;
+
+/* %start-doc pcbfile PolyArea
+
+ at syntax
+PolyArea [Area]
+ at end syntax
+
+ at table @var
+ at item Area
+Minimum area of polygon island to retain. If a polygon has clearances that cause an isolated island to be created, then will only be retained if the area exceeds this minimum area.
+ at end table
+
+%end-doc */
+
+polyarea
+		:
+		| T_AREA '[' number ']'
+			{
+				/* Read in cmil^2 for now; in future this should be a noop. */
+				yyPCB->IsleArea = PCB_MIL_TO_COORD (PCB_MIL_TO_COORD ($3) / 100.0) / 100.0;
+			}
+		;
+
+
+/* %start-doc pcbfile Thermal
+
+ at syntax
+Thermal [Scale]
+ at end syntax
+
+ at table @var
+ at item Scale
+Relative size of thermal fingers.  A value of 1.0 makes the finger
+width twice the clearance gap width (measured across the gap, not
+diameter).  The normal value is 0.5, which results in a finger width
+the same as the clearance gap width.
+ at end table
+
+%end-doc */
+
+
+pcbthermal
+		:
+		| T_THERMAL '[' number ']'
+			{
+				yyPCB->ThermScale = $3;
+			}
+		;
+
+/* %start-doc pcbfile DRC
+
+ at syntax
+DRC [Bloat Shrink Line Silk Drill Ring]
+DRC [Bloat Shrink Line Silk]
+DRC [Bloat Shrink Line]
+ at end syntax
+
+ at table @var
+ at item Bloat
+Minimum spacing between copper.
+ at item Shrink
+Minimum copper overlap to guarantee connectivity.
+ at item Line
+Minimum line thickness.
+ at item Silk
+Minimum silk thickness.
+ at item Drill
+Minimum drill size.
+ at item Ring
+Minimum width of the annular ring around pins and vias.
+ at end table
+
+%end-doc */
+
+pcbdrc
+		:
+		| pcbdrc1
+		| pcbdrc2
+		| pcbdrc3
+		;
+
+pcbdrc1
+                : T_DRC '[' measure measure measure ']'
+		        {
+				yyPCB->Bloat = NU ($3);
+				yyPCB->Shrink = NU ($4);
+				yyPCB->minWid = NU ($5);
+				yyPCB->minRing = NU ($5);
+			}
+		;
+
+pcbdrc2
+                : T_DRC '[' measure measure measure measure ']'
+		        {
+				yyPCB->Bloat = NU ($3);
+				yyPCB->Shrink = NU ($4);
+				yyPCB->minWid = NU ($5);
+				yyPCB->minSlk = NU ($6);
+				yyPCB->minRing = NU ($5);
+			}
+		;
+
+pcbdrc3
+                : T_DRC '[' measure measure measure measure measure measure ']'
+		        {
+				yyPCB->Bloat = NU ($3);
+				yyPCB->Shrink = NU ($4);
+				yyPCB->minWid = NU ($5);
+				yyPCB->minSlk = NU ($6);
+				yyPCB->minDrill = NU ($7);
+				yyPCB->minRing = NU ($8);
+			}
+		;
+
+/* %start-doc pcbfile Flags
+
+ at syntax
+Flags(Number)
+ at end syntax
+
+ at table @var
+ at item Number
+A number, whose value is normally given in hex, individual bits of which
+represent pcb-wide flags as defined in @ref{PCBFlags}.
+
+ at end table
+
+%end-doc */
+
+pcbflags
+		: T_FLAGS '(' INTEGER ')'
+			{
+				yy_pcb_flags = MakeFlags ($3 & PCB_FLAGS);
+			}
+		| T_FLAGS '(' STRING ')'
+			{
+			  yy_pcb_flags = string_to_pcbflags ($3, yyerror);
+			  free($3);
+			}
+		|
+		;
+
+/* %start-doc pcbfile Groups
+
+ at syntax
+Groups("String")
+ at end syntax
+
+ at table @var
+ at item String
+
+Encodes the layer grouping information.  Each group is separated by a
+colon, each member of each group is separated by a comma.  Group
+members are either numbers from @code{1}.. at var{N} for each layer, and
+the letters @code{c} or @code{s} representing the component side and
+solder side of the board.  Including @code{c} or @code{s} marks that
+group as being the top or bottom side of the board.
+
+ at example
+Groups("1,2,c:3:4:5,6,s:7,8")
+ at end example
+
+ at end table
+
+%end-doc */
+
+pcbgroups
+		: T_GROUPS '(' STRING ')'
+			{
+			  layer_group_string = $3;
+			}
+		|
+		;
+
+/* %start-doc pcbfile Styles
+
+ at syntax
+Styles("String")
+ at end syntax
+
+ at table @var
+ at item String
+
+Encodes the four routing styles @code{pcb} knows about.  The four styles
+are separated by colons.  Each style consists of five parameters as follows:
+
+ at table @var
+ at item Name
+The name of the style.
+ at item Thickness
+Width of lines and arcs.
+ at item Diameter
+Copper diameter of pins and vias.
+ at item Drill
+Drill diameter of pins and vias.
+ at item Keepaway
+Minimum spacing to other nets.  If omitted, 10 mils is the default.
+
+ at end table
+
+ at end table
+
+ at example
+Styles("Signal,10,40,20:Power,25,60,35:Fat,40,60,35:Skinny,8,36,20")
+Styles["Logic,1000,3600,2000,1000:Power,2500,6000,3500,1000:
+@ @ @ Line,4000,6000,3500,1000:Breakout,600,2402,1181,600"]
+ at end example
+
+ at noindent
+Note that strings in actual files cannot span lines; the above example
+is split across lines only to make it readable.
+
+%end-doc */
+
+pcbstyles
+		: T_STYLES '(' STRING ')'
+			{
+				if (ParseRouteString($3, &yyPCB->RouteStyle, "mil"))
+				{
+					Message(PCB_MSG_ERROR, "illegal route-style string\n");
+					YYABORT;
+				}
+				free($3);
+			}
+		| T_STYLES '[' STRING ']'
+			{
+				if (ParseRouteString(($3 == NULL ? "" : $3), &yyPCB->RouteStyle, "cmil"))
+				{
+					Message(PCB_MSG_ERROR, "illegal route-style string\n");
+					YYABORT;
+				}
+				free($3);
+			}
+		|
+		;
+
+pcbdata
+		: pcbdefinitions
+		|
+		;
+
+pcbdefinitions
+		: pcbdefinition
+		| pcbdefinitions pcbdefinition
+		;
+
+pcbdefinition
+		: via
+		| { attr_list = & yyPCB->Attributes; } attribute
+		| rats
+		| layer
+		|
+			{
+					/* clear pointer to force memory allocation by
+					 * the appropriate subroutine
+					 */
+				yyElement = NULL;
+			}
+		  element
+		| error { YYABORT; }
+		;
+
+via
+		: via_hi_format
+		| via_2.0_format
+		| via_1.7_format
+		| via_newformat
+		| via_oldformat
+		;
+
+/* %start-doc pcbfile Via
+
+ at syntax
+Via [X Y Thickness Clearance Mask Drill "Name" SFlags]
+Via (X Y Thickness Clearance Mask Drill "Name" NFlags)
+Via (X Y Thickness Clearance Drill "Name" NFlags)
+Via (X Y Thickness Drill "Name" NFlags)
+Via (X Y Thickness "Name" NFlags)
+ at end syntax
+
+ at table @var
+ at item X Y
+coordinates of center
+ at item Thickness
+outer diameter of copper annulus
+ at item Clearance
+add to thickness to get clearance diameter
+ at item Mask
+diameter of solder mask opening
+ at item Drill
+diameter of drill
+ at item Name
+string, name of via (vias have names?)
+ at item SFlags
+symbolic or numerical flags
+ at item NFlags
+numerical flags only
+ at end table
+
+%end-doc */
+
+via_hi_format
+			/* x, y, thickness, clearance, mask, drilling-hole, name, flags */
+		: T_VIA '[' measure measure measure measure measure measure STRING flags ']'
+			{
+				CreateNewVia(yyData, NU ($3), NU ($4), NU ($5), NU ($6), NU ($7),
+				                     NU ($8), $9, $10);
+				free ($9);
+			}
+		;
+
+via_2.0_format
+			/* x, y, thickness, clearance, mask, drilling-hole, name, flags */
+		: T_VIA '(' measure measure measure measure measure measure STRING INTEGER ')'
+			{
+				CreateNewVia(yyData, OU ($3), OU ($4), OU ($5), OU ($6), OU ($7), OU ($8), $9,
+					OldFlags($10));
+				free ($9);
+			}
+		;
+
+
+via_1.7_format
+			/* x, y, thickness, clearance, drilling-hole, name, flags */
+		: T_VIA '(' measure measure measure measure measure STRING INTEGER ')'
+			{
+				CreateNewVia(yyData, OU ($3), OU ($4), OU ($5), OU ($6),
+					     OU ($5) + OU($6), OU ($7), $8, OldFlags($9));
+				free ($8);
+			}
+		;
+
+via_newformat
+			/* x, y, thickness, drilling-hole, name, flags */
+		: T_VIA '(' measure measure measure measure STRING INTEGER ')'
+			{
+				CreateNewVia(yyData, OU ($3), OU ($4), OU ($5), 2*GROUNDPLANEFRAME,
+					OU($5) + 2*MASKFRAME,  OU ($6), $7, OldFlags($8));
+				free ($7);
+			}
+		;
+
+via_oldformat
+			/* old format: x, y, thickness, name, flags */
+		: T_VIA '(' measure measure measure STRING INTEGER ')'
+			{
+				Coord	hole = (OU($5) * DEFAULT_DRILLINGHOLE);
+
+					/* make sure that there's enough copper left */
+				if (OU($5) - hole < MIN_PINORVIACOPPER &&
+					OU($5) > MIN_PINORVIACOPPER)
+					hole = OU($5) - MIN_PINORVIACOPPER;
+
+				CreateNewVia(yyData, OU ($3), OU ($4), OU ($5), 2*GROUNDPLANEFRAME,
+					OU($5) + 2*MASKFRAME, hole, $6, OldFlags($7));
+				free ($6);
+			}
+		;
+
+/* %start-doc pcbfile Rat
+
+ at syntax
+Rat [X1 Y1 Group1 X2 Y2 Group2 SFlags]
+Rat (X1 Y1 Group1 X2 Y2 Group2 NFlags)
+ at end syntax
+
+ at table @var
+ at item X1 Y1 X2 Y2
+The endpoints of the rat line.
+ at item Group1 Group2
+The layer group each end is connected on.
+ at item SFlags
+Symbolic or numeric flags.
+ at item NFlags
+Numeric flags.
+ at end table
+
+%end-doc */
+
+rats
+		: T_RAT '[' measure measure INTEGER measure measure INTEGER flags ']'
+			{
+				CreateNewRat(yyData, NU ($3), NU ($4), NU ($6), NU ($7), $5, $8,
+					conf_core.appearance.rat_thickness, $9);
+			}
+		| T_RAT '(' measure measure INTEGER measure measure INTEGER INTEGER ')'
+			{
+				CreateNewRat(yyData, OU ($3), OU ($4), OU ($6), OU ($7), $5, $8,
+					conf_core.appearance.rat_thickness, OldFlags($9));
+			}
+		;
+
+/* %start-doc pcbfile Layer
+
+ at syntax
+Layer (LayerNum "Name") (
+@ @ @ @dots{} contents @dots{}
+)
+ at end syntax
+
+ at table @var
+ at item LayerNum
+The layer number.  Layers are numbered sequentially, starting with 1.
+The last two layers (9 and 10 by default) are solder-side silk and
+component-side silk, in that order.
+ at item Name
+The layer name.
+ at item contents
+The contents of the layer, which may include attributes, lines, arcs, rectangles,
+text, and polygons.
+ at end table
+
+%end-doc */
+
+layer
+			/* name */
+		: T_LAYER '(' INTEGER STRING opt_string ')' '('
+			{
+				if ($3 <= 0 || $3 > MAX_LAYER + 2)
+				{
+					yyerror("Layernumber out of range");
+					YYABORT;
+				}
+				if (LayerFlag[$3-1])
+				{
+					yyerror("Layernumber used twice");
+					YYABORT;
+				}
+				Layer = &yyData->Layer[$3-1];
+
+					/* memory for name is already allocated */
+				if (Layer->Name != NULL)
+					free((char*)Layer->Name);
+				Layer->Name = $4;   /* shouldn't this be strdup()'ed ? */
+				LayerFlag[$3-1] = pcb_true;
+				if (yyData->LayerN + 2 < $3)
+				  yyData->LayerN = $3 - 2;
+				if ($5 != NULL)
+					free($5);
+			}
+		  layerdata ')'
+		;
+
+layerdata
+		: layerdefinitions
+		|
+		;
+
+layerdefinitions
+		: layerdefinition
+		| layerdefinitions layerdefinition
+		;
+
+layerdefinition
+		: line_hi_format
+		| line_1.7_format
+		| line_oldformat
+		| arc_hi_format
+		| arc_1.7_format
+		| arc_oldformat
+			/* x1, y1, x2, y2, flags */
+		| T_RECTANGLE '(' measure measure measure measure INTEGER ')'
+			{
+				CreateNewPolygonFromRectangle(Layer,
+					OU ($3), OU ($4), OU ($3) + OU ($5), OU ($4) + OU ($6), OldFlags($7));
+			}
+		| text_hi_format
+		| text_newformat
+		| text_oldformat
+		| { attr_list = & Layer->Attributes; } attribute
+		| polygon_format
+
+/* %start-doc pcbfile Line
+
+ at syntax
+Line [X1 Y1 X2 Y2 Thickness Clearance SFlags]
+Line (X1 Y1 X2 Y2 Thickness Clearance NFlags)
+Line (X1 Y1 X2 Y2 Thickness NFlags)
+ at end syntax
+
+ at table @var
+ at item X1 Y1 X2 Y2
+The end points of the line
+ at item Thickness
+The width of the line
+ at item Clearance
+The amount of space cleared around the line when the line passes
+through a polygon.  The clearance is added to the thickness to get the
+thickness of the clear; thus the space between the line and the
+polygon is @math{Clearance/2} wide.
+ at item SFlags
+Symbolic or numeric flags
+ at item NFlags
+Numeric flags.
+ at end table
+
+%end-doc */
+
+line_hi_format
+			/* x1, y1, x2, y2, thickness, clearance, flags */
+		: T_LINE '[' measure measure measure measure measure measure flags ']'
+			{
+				CreateNewLineOnLayer(Layer, NU ($3), NU ($4), NU ($5), NU ($6),
+				                            NU ($7), NU ($8), $9);
+			}
+		;
+
+line_1.7_format
+			/* x1, y1, x2, y2, thickness, clearance, flags */
+		: T_LINE '(' measure measure measure measure measure measure INTEGER ')'
+			{
+				CreateNewLineOnLayer(Layer, OU ($3), OU ($4), OU ($5), OU ($6),
+						     OU ($7), OU ($8), OldFlags($9));
+			}
+		;
+
+line_oldformat
+			/* x1, y1, x2, y2, thickness, flags */
+		: T_LINE '(' measure measure measure measure measure measure ')'
+			{
+				/* eliminate old-style rat-lines */
+			if ((IV ($8) & PCB_FLAG_RAT) == 0)
+				CreateNewLineOnLayer(Layer, OU ($3), OU ($4), OU ($5), OU ($6), OU ($7),
+					200*GROUNDPLANEFRAME, OldFlags(IV ($8)));
+			}
+		;
+
+/* %start-doc pcbfile Arc
+
+ at syntax
+Arc [X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]
+Arc (X Y Width Height Thickness Clearance StartAngle DeltaAngle NFlags)
+Arc (X Y Width Height Thickness StartAngle DeltaAngle NFlags)
+ at end syntax
+
+ at table @var
+ at item X Y
+Coordinates of the center of the arc.
+ at item Width Height
+The width and height, from the center to the edge.  The bounds of the
+circle of which this arc is a segment, is thus @math{2*Width} by
+ at math{2*Height}.
+ at item Thickness
+The width of the copper trace which forms the arc.
+ at item Clearance
+The amount of space cleared around the arc when the line passes
+through a polygon.  The clearance is added to the thickness to get the
+thickness of the clear; thus the space between the arc and the polygon
+is @math{Clearance/2} wide.
+ at item StartAngle
+The angle of one end of the arc, in degrees.  In PCB, an angle of zero
+points left (negative X direction), and 90 degrees points down
+(positive Y direction).
+ at item DeltaAngle
+The sweep of the arc.  This may be negative.  Positive angles sweep
+counterclockwise.
+ at item SFlags
+Symbolic or numeric flags.
+ at item NFlags
+Numeric flags.
+ at end table
+
+%end-doc */
+
+arc_hi_format
+			/* x, y, width, height, thickness, clearance, startangle, delta, flags */
+		: T_ARC '[' measure measure measure measure measure measure number number flags ']'
+			{
+			  CreateNewArcOnLayer(Layer, NU ($3), NU ($4), NU ($5), NU ($6), $9, $10,
+			                             NU ($7), NU ($8), $11);
+			}
+		;
+
+arc_1.7_format
+			/* x, y, width, height, thickness, clearance, startangle, delta, flags */
+		: T_ARC '(' measure measure measure measure measure measure number number INTEGER ')'
+			{
+				CreateNewArcOnLayer(Layer, OU ($3), OU ($4), OU ($5), OU ($6), $9, $10,
+						    OU ($7), OU ($8), OldFlags($11));
+			}
+		;
+
+arc_oldformat
+			/* x, y, width, height, thickness, startangle, delta, flags */
+		: T_ARC '(' measure measure measure measure measure measure number INTEGER ')'
+			{
+				CreateNewArcOnLayer(Layer, OU ($3), OU ($4), OU ($5), OU ($5), IV ($8), $9,
+					OU ($7), 200*GROUNDPLANEFRAME, OldFlags($10));
+			}
+		;
+
+/* %start-doc pcbfile Text
+
+ at syntax
+Text [X Y Direction Scale "String" SFlags]
+Text (X Y Direction Scale "String" NFlags)
+Text (X Y Direction "String" NFlags)
+ at end syntax
+
+ at table @var
+ at item X Y
+The location of the upper left corner of the text.
+ at item Direction
+0 means text is drawn left to right, 1 means up, 2 means right to left
+(i.e. upside down), and 3 means down.
+ at item Scale
+Size of the text, as a percentage of the ``default'' size of of the
+font (the default font is about 40 mils high).  Default is 100 (40
+mils).
+ at item String
+The string to draw.
+ at item SFlags
+Symbolic or numeric flags.
+ at item NFlags
+Numeric flags.
+ at end table
+
+%end-doc */
+
+text_oldformat
+			/* x, y, direction, text, flags */
+		: T_TEXT '(' measure measure number STRING INTEGER ')'
+			{
+					/* use a default scale of 100% */
+				CreateNewText(Layer,yyFont,OU ($3), OU ($4), $5, 100, $6, OldFlags($7));
+				free ($6);
+			}
+		;
+
+text_newformat
+			/* x, y, direction, scale, text, flags */
+		: T_TEXT '(' measure measure number number STRING INTEGER ')'
+			{
+				if ($8 & PCB_FLAG_ONSILK)
+				{
+					LayerTypePtr lay = &yyData->Layer[yyData->LayerN +
+						(($8 & PCB_FLAG_ONSOLDER) ? SOLDER_LAYER : COMPONENT_LAYER)];
+
+					CreateNewText(lay ,yyFont, OU ($3), OU ($4), $5, $6, $7,
+						      OldFlags($8));
+				}
+				else
+					CreateNewText(Layer, yyFont, OU ($3), OU ($4), $5, $6, $7,
+						      OldFlags($8));
+				free ($7);
+			}
+		;
+text_hi_format
+			/* x, y, direction, scale, text, flags */
+		: T_TEXT '[' measure measure number number STRING flags ']'
+			{
+				/* FIXME: shouldn't know about .f */
+				/* I don't think this matters because anything with hi_format
+				 * will have the silk on its own layer in the file rather
+				 * than using the PCB_FLAG_ONSILK and having it in a copper layer.
+				 * Thus there is no need for anything besides the 'else'
+				 * part of this code.
+				 */
+				if ($8.f & PCB_FLAG_ONSILK)
+				{
+					LayerTypePtr lay = &yyData->Layer[yyData->LayerN +
+						(($8.f & PCB_FLAG_ONSOLDER) ? SOLDER_LAYER : COMPONENT_LAYER)];
+
+					CreateNewText(lay, yyFont, NU ($3), NU ($4), $5, $6, $7, $8);
+				}
+				else
+					CreateNewText(Layer, yyFont, NU ($3), NU ($4), $5, $6, $7, $8);
+				free ($7);
+			}
+		;
+
+/* %start-doc pcbfile Polygon
+
+ at syntax
+Polygon (SFlags) (
+@ @ @ @dots{} (X Y) @dots{}
+@ @ @ @dots{} [X Y] @dots{}
+@ @ @ Hole (
+@ @ @ @ @ @ @dots{} (X Y) @dots{}
+@ @ @ @ @ @ @dots{} [X Y] @dots{}
+@ @ @ )
+@ @ @ @dots{}
+)
+ at end syntax
+
+ at table @var
+ at item SFlags
+Symbolic or numeric flags.
+ at item X Y
+Coordinates of each vertex.  You must list at least three coordinates.
+ at item Hole (...)
+Defines a hole within the polygon's outer contour. There may be zero or more such sections.
+ at end table
+
+%end-doc */
+
+polygon_format
+		: /* flags are passed in */
+		T_POLYGON '(' flags ')' '('
+			{
+				Polygon = CreateNewPolygon(Layer, $3);
+			}
+		  polygonpoints
+		  polygonholes ')'
+			{
+				pcb_cardinal_t contour, contour_start, contour_end;
+				pcb_bool bad_contour_found = pcb_false;
+				/* ignore junk */
+				for (contour = 0; contour <= Polygon->HoleIndexN; contour++)
+				  {
+				    contour_start = (contour == 0) ?
+						      0 : Polygon->HoleIndex[contour - 1];
+				    contour_end = (contour == Polygon->HoleIndexN) ?
+						 Polygon->PointN :
+						 Polygon->HoleIndex[contour];
+				    if (contour_end - contour_start < 3)
+				      bad_contour_found = pcb_true;
+				  }
+
+				if (bad_contour_found)
+				  {
+				    Message(PCB_MSG_WARNING, "WARNING parsing file '%s'\n"
+					    "    line:        %i\n"
+					    "    description: 'ignored polygon (< 3 points in a contour)'\n",
+					    yyfilename, pcb_lineno);
+				    DestroyObject(yyData, PCB_TYPE_POLYGON, Layer, Polygon, Polygon);
+				  }
+				else
+				  {
+				    SetPolygonBoundingBox (Polygon);
+				    if (!Layer->polygon_tree)
+				      Layer->polygon_tree = r_create_tree (NULL, 0, 0);
+				    r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
+				  }
+			}
+		;
+
+polygonholes
+		: /* empty */
+		| polygonholes polygonhole
+		;
+
+polygonhole
+		: T_POLYGON_HOLE '('
+			{
+				CreateNewHoleInPolygon (Polygon);
+			}
+		  polygonpoints ')'
+		;
+
+polygonpoints
+		: /* empty */
+		| polygonpoint polygonpoints
+		;
+
+polygonpoint
+			/* xcoord ycoord */
+		: '(' measure measure ')'
+			{
+				CreateNewPointInPolygon(Polygon, OU ($2), OU ($3));
+			}
+		| '[' measure measure ']'
+			{
+				CreateNewPointInPolygon(Polygon, NU ($2), NU ($3));
+			}
+		;
+
+/* %start-doc pcbfile Element
+
+ at syntax
+Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] (
+Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags) (
+Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags) (
+Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags) (
+Element ("Desc" "Name" TX TY TDir TScale TNFlags) (
+@ @ @ @dots{} contents @dots{}
+)
+ at end syntax
+
+ at table @var
+ at item SFlags
+Symbolic or numeric flags, for the element as a whole.
+ at item NFlags
+Numeric flags, for the element as a whole.
+ at item Desc
+The description of the element.  This is one of the three strings
+which can be displayed on the screen.
+ at item Name
+The name of the element, usually the reference designator.
+ at item Value
+The value of the element.
+ at item MX MY
+The location of the element's mark.  This is the reference point
+for placing the element and its pins and pads.
+ at item TX TY
+The upper left corner of the text (one of the three strings).
+ at item TDir
+The relative direction of the text.  0 means left to right for
+an unrotated element, 1 means up, 2 left, 3 down.
+ at item TScale
+Size of the text, as a percentage of the ``default'' size of of the
+font (the default font is about 40 mils high).  Default is 100 (40
+mils).
+ at item TSFlags
+Symbolic or numeric flags, for the text.
+ at item TNFlags
+Numeric flags, for the text.
+ at end table
+
+Elements may contain pins, pads, element lines, element arcs,
+attributes, and (for older elements) an optional mark.  Note that
+element definitions that have the mark coordinates in the element
+line, only support pins and pads which use relative coordinates.  The
+pin and pad coordinates are relative to the mark.  Element definitions
+which do not include the mark coordinates in the element line, may
+have a Mark definition in their contents, and only use pin and pad
+definitions which use absolute coordinates.
+
+%end-doc */
+
+element
+		: element_oldformat
+		| element_1.3.4_format
+		| element_newformat
+		| element_1.7_format
+		| element_hi_format
+		;
+
+element_oldformat
+			/* element_flags, description, pcb-name,
+			 * text_x, text_y, text_direction, text_scale, text_flags
+			 */
+		: T_ELEMENT '(' STRING STRING measure measure INTEGER ')' '('
+			{
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, NoFlags(),
+					$3, $4, NULL, OU ($5), OU ($6), $7, 100, NoFlags(), pcb_false);
+				free ($3);
+				free ($4);
+				pin_num = 1;
+			}
+		  elementdefinitions ')'
+			{
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+		;
+
+element_1.3.4_format
+			/* element_flags, description, pcb-name,
+			 * text_x, text_y, text_direction, text_scale, text_flags
+			 */
+		: T_ELEMENT '(' INTEGER STRING STRING measure measure measure measure INTEGER ')' '('
+			{
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, OldFlags($3),
+					$4, $5, NULL, OU ($6), OU ($7), IV ($8), IV ($9), OldFlags($10), pcb_false);
+				free ($4);
+				free ($5);
+				pin_num = 1;
+			}
+		  elementdefinitions ')'
+			{
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+		;
+
+element_newformat
+			/* element_flags, description, pcb-name, value,
+			 * text_x, text_y, text_direction, text_scale, text_flags
+			 */
+		: T_ELEMENT '(' INTEGER STRING STRING STRING measure measure measure measure INTEGER ')' '('
+			{
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, OldFlags($3),
+					$4, $5, $6, OU ($7), OU ($8), IV ($9), IV ($10), OldFlags($11), pcb_false);
+				free ($4);
+				free ($5);
+				free ($6);
+				pin_num = 1;
+			}
+		  elementdefinitions ')'
+			{
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+		;
+
+element_1.7_format
+			/* element_flags, description, pcb-name, value, mark_x, mark_y,
+			 * text_x, text_y, text_direction, text_scale, text_flags
+			 */
+		: T_ELEMENT '(' INTEGER STRING STRING STRING measure measure
+			measure measure number number INTEGER ')' '('
+			{
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, OldFlags($3),
+					$4, $5, $6, OU ($7) + OU ($9), OU ($8) + OU ($10),
+					$11, $12, OldFlags($13), pcb_false);
+				yyElement->MarkX = OU ($7);
+				yyElement->MarkY = OU ($8);
+				free ($4);
+				free ($5);
+				free ($6);
+			}
+		  relementdefs ')'
+			{
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+		;
+
+element_hi_format
+			/* element_flags, description, pcb-name, value, mark_x, mark_y,
+			 * text_x, text_y, text_direction, text_scale, text_flags
+			 */
+		: T_ELEMENT '[' flags STRING STRING STRING measure measure
+			measure measure number number flags ']' '('
+			{
+				yyElement = CreateNewElement(yyData, yyElement, yyFont, $3,
+					$4, $5, $6, NU ($7) + NU ($9), NU ($8) + NU ($10),
+					$11, $12, $13, pcb_false);
+				yyElement->MarkX = NU ($7);
+				yyElement->MarkY = NU ($8);
+				free ($4);
+				free ($5);
+				free ($6);
+			}
+		  relementdefs ')'
+			{
+				SetElementBoundingBox(yyData, yyElement, yyFont);
+			}
+		;
+
+/* %start-doc pcbfile ElementLine
+
+ at syntax
+ElementLine [X1 Y1 X2 Y2 Thickness]
+ElementLine (X1 Y1 X2 Y2 Thickness)
+ at end syntax
+
+ at table @var
+ at item X1 Y1 X2 Y2
+Coordinates of the endpoints of the line.  These are relative to the
+Element's mark point for new element formats, or absolute for older
+formats.
+ at item Thickness
+The width of the silk for this line.
+ at end table
+
+%end-doc */
+
+/* %start-doc pcbfile ElementArc
+
+ at syntax
+ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
+ElementArc (X Y Width Height StartAngle DeltaAngle Thickness)
+ at end syntax
+
+ at table @var
+ at item X Y
+Coordinates of the center of the arc.  These are relative to the
+Element's mark point for new element formats, or absolute for older
+formats.
+ at item Width Height
+The width and height, from the center to the edge.  The bounds of the
+circle of which this arc is a segment, is thus @math{2*Width} by
+ at math{2*Height}.
+ at item StartAngle
+The angle of one end of the arc, in degrees.  In PCB, an angle of zero
+points left (negative X direction), and 90 degrees points down
+(positive Y direction).
+ at item DeltaAngle
+The sweep of the arc.  This may be negative.  Positive angles sweep
+counterclockwise.
+ at item Thickness
+The width of the silk line which forms the arc.
+ at end table
+
+%end-doc */
+
+/* %start-doc pcbfile Mark
+
+ at syntax
+Mark [X Y]
+Mark (X Y)
+ at end syntax
+
+ at table @var
+ at item X Y
+Coordinates of the Mark, for older element formats that don't have
+the mark as part of the Element line.
+ at end table
+
+%end-doc */
+
+elementdefinitions
+		: elementdefinition
+		| elementdefinitions elementdefinition
+		;
+
+elementdefinition
+		: pin_1.6.3_format
+		| pin_newformat
+		| pin_oldformat
+		| pad_newformat
+		| pad
+			/* x1, y1, x2, y2, thickness */
+		| T_ELEMENTLINE '[' measure measure measure measure measure ']'
+			{
+				CreateNewLineInElement(yyElement, NU ($3), NU ($4), NU ($5), NU ($6), NU ($7));
+			}
+			/* x1, y1, x2, y2, thickness */
+		| T_ELEMENTLINE '(' measure measure measure measure measure ')'
+			{
+				CreateNewLineInElement(yyElement, OU ($3), OU ($4), OU ($5), OU ($6), OU ($7));
+			}
+			/* x, y, width, height, startangle, anglediff, thickness */
+		| T_ELEMENTARC '[' measure measure measure measure number number measure ']'
+			{
+				CreateNewArcInElement(yyElement, NU ($3), NU ($4), NU ($5), NU ($6), $7, $8, NU ($9));
+			}
+			/* x, y, width, height, startangle, anglediff, thickness */
+		| T_ELEMENTARC '(' measure measure measure measure number number measure ')'
+			{
+				CreateNewArcInElement(yyElement, OU ($3), OU ($4), OU ($5), OU ($6), $7, $8, OU ($9));
+			}
+			/* x, y position */
+		| T_MARK '[' measure measure ']'
+			{
+				yyElement->MarkX = NU ($3);
+				yyElement->MarkY = NU ($4);
+			}
+		| T_MARK '(' measure measure ')'
+			{
+				yyElement->MarkX = OU ($3);
+				yyElement->MarkY = OU ($4);
+			}
+		| { attr_list = & yyElement->Attributes; } attribute
+		;
+
+relementdefs
+		: relementdef
+		| relementdefs relementdef
+		;
+
+relementdef
+		: pin_1.7_format
+		| pin_hi_format
+		| pad_1.7_format
+		| pad_hi_format
+			/* x1, y1, x2, y2, thickness */
+		| T_ELEMENTLINE '[' measure measure measure measure measure ']'
+			{
+				CreateNewLineInElement(yyElement, NU ($3) + yyElement->MarkX,
+					NU ($4) + yyElement->MarkY, NU ($5) + yyElement->MarkX,
+					NU ($6) + yyElement->MarkY, NU ($7));
+			}
+		| T_ELEMENTLINE '(' measure measure measure measure measure ')'
+			{
+				CreateNewLineInElement(yyElement, OU ($3) + yyElement->MarkX,
+					OU ($4) + yyElement->MarkY, OU ($5) + yyElement->MarkX,
+					OU ($6) + yyElement->MarkY, OU ($7));
+			}
+			/* x, y, width, height, startangle, anglediff, thickness */
+		| T_ELEMENTARC '[' measure measure measure measure number number measure ']'
+			{
+				CreateNewArcInElement(yyElement, NU ($3) + yyElement->MarkX,
+					NU ($4) + yyElement->MarkY, NU ($5), NU ($6), $7, $8, NU ($9));
+			}
+		| T_ELEMENTARC '(' measure measure measure measure number number measure ')'
+			{
+				CreateNewArcInElement(yyElement, OU ($3) + yyElement->MarkX,
+					OU ($4) + yyElement->MarkY, OU ($5), OU ($6), $7, $8, OU ($9));
+			}
+		| { attr_list = & yyElement->Attributes; } attribute
+		;
+
+/* %start-doc pcbfile Pin
+
+ at syntax
+Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
+Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
+Pin (aX aY Thickness Drill "Name" "Number" NFlags)
+Pin (aX aY Thickness Drill "Name" NFlags)
+Pin (aX aY Thickness "Name" NFlags)
+ at end syntax
+
+ at table @var
+ at item rX rY
+coordinates of center, relative to the element's mark
+ at item aX aY
+absolute coordinates of center.
+ at item Thickness
+outer diameter of copper annulus
+ at item Clearance
+add to thickness to get clearance diameter
+ at item Mask
+diameter of solder mask opening
+ at item Drill
+diameter of drill
+ at item Name
+name of pin
+ at item Number
+number of pin
+ at item SFlags
+symbolic or numerical flags
+ at item NFlags
+numerical flags only
+ at end table
+
+%end-doc */
+
+pin_hi_format
+			/* x, y, thickness, clearance, mask, drilling hole, name,
+			   number, flags */
+		: T_PIN '[' measure measure measure measure measure measure STRING STRING flags ']'
+			{
+				CreateNewPin(yyElement, NU ($3) + yyElement->MarkX,
+					NU ($4) + yyElement->MarkY, NU ($5), NU ($6), NU ($7), NU ($8), $9,
+					$10, $11);
+				free ($9);
+				free ($10);
+			}
+		;
+pin_1.7_format
+			/* x, y, thickness, clearance, mask, drilling hole, name,
+			   number, flags */
+		: T_PIN '(' measure measure measure measure measure measure STRING STRING INTEGER ')'
+			{
+				CreateNewPin(yyElement, OU ($3) + yyElement->MarkX,
+					OU ($4) + yyElement->MarkY, OU ($5), OU ($6), OU ($7), OU ($8), $9,
+					$10, OldFlags($11));
+				free ($9);
+				free ($10);
+			}
+		;
+
+pin_1.6.3_format
+			/* x, y, thickness, drilling hole, name, number, flags */
+		: T_PIN '(' measure measure measure measure STRING STRING INTEGER ')'
+			{
+				CreateNewPin(yyElement, OU ($3), OU ($4), OU ($5), 2*GROUNDPLANEFRAME,
+					OU ($5) + 2*MASKFRAME, OU ($6), $7, $8, OldFlags($9));
+				free ($7);
+				free ($8);
+			}
+		;
+
+pin_newformat
+			/* x, y, thickness, drilling hole, name, flags */
+		: T_PIN '(' measure measure measure measure STRING INTEGER ')'
+			{
+				char	p_number[8];
+
+				sprintf(p_number, "%d", pin_num++);
+				CreateNewPin(yyElement, OU ($3), OU ($4), OU ($5), 2*GROUNDPLANEFRAME,
+					OU ($5) + 2*MASKFRAME, OU ($6), $7, p_number, OldFlags($8));
+
+				free ($7);
+			}
+		;
+
+pin_oldformat
+			/* old format: x, y, thickness, name, flags
+			 * drilling hole is 40% of the diameter
+			 */
+		: T_PIN '(' measure measure measure STRING INTEGER ')'
+			{
+				Coord	hole = OU ($5) * DEFAULT_DRILLINGHOLE;
+				char	p_number[8];
+
+					/* make sure that there's enough copper left */
+				if (OU ($5) - hole < MIN_PINORVIACOPPER &&
+					OU ($5) > MIN_PINORVIACOPPER)
+					hole = OU ($5) - MIN_PINORVIACOPPER;
+
+				sprintf(p_number, "%d", pin_num++);
+				CreateNewPin(yyElement, OU ($3), OU ($4), OU ($5), 2*GROUNDPLANEFRAME,
+					OU ($5) + 2*MASKFRAME, hole, $6, p_number, OldFlags($7));
+				free ($6);
+			}
+		;
+
+/* %start-doc pcbfile Pad
+
+ at syntax
+Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
+Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
+Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
+Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
+ at end syntax
+
+ at table @var
+ at item rX1 rY1 rX2 rY2
+Coordinates of the endpoints of the pad, relative to the element's
+mark.  Note that the copper extends beyond these coordinates by half
+the thickness.  To make a square or round pad, specify the same
+coordinate twice.
+ at item aX1 aY1 aX2 aY2
+Same, but absolute coordinates of the endpoints of the pad.
+ at item Thickness
+width of the pad.
+ at item Clearance
+add to thickness to get clearance width.
+ at item Mask
+width of solder mask opening.
+ at item Name
+name of pin
+ at item Number
+number of pin
+ at item SFlags
+symbolic or numerical flags
+ at item NFlags
+numerical flags only
+ at end table
+
+%end-doc */
+
+pad_hi_format
+			/* x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags */
+		: T_PAD '[' measure measure measure measure measure measure measure STRING STRING flags ']'
+			{
+				CreateNewPad(yyElement, NU ($3) + yyElement->MarkX,
+					NU ($4) + yyElement->MarkY,
+					NU ($5) + yyElement->MarkX,
+					NU ($6) + yyElement->MarkY, NU ($7), NU ($8), NU ($9),
+					$10, $11, $12);
+				free ($10);
+				free ($11);
+			}
+		;
+
+pad_1.7_format
+			/* x1, y1, x2, y2, thickness, clearance, mask, name , pad number, flags */
+		: T_PAD '(' measure measure measure measure measure measure measure STRING STRING INTEGER ')'
+			{
+				CreateNewPad(yyElement,OU ($3) + yyElement->MarkX,
+					OU ($4) + yyElement->MarkY, OU ($5) + yyElement->MarkX,
+					OU ($6) + yyElement->MarkY, OU ($7), OU ($8), OU ($9),
+					$10, $11, OldFlags($12));
+				free ($10);
+				free ($11);
+			}
+		;
+
+pad_newformat
+			/* x1, y1, x2, y2, thickness, name , pad number, flags */
+		: T_PAD '(' measure measure measure measure measure STRING STRING INTEGER ')'
+			{
+				CreateNewPad(yyElement,OU ($3),OU ($4),OU ($5),OU ($6),OU ($7), 2*GROUNDPLANEFRAME,
+					OU ($7) + 2*MASKFRAME, $8, $9, OldFlags($10));
+				free ($8);
+				free ($9);
+			}
+		;
+
+pad
+			/* x1, y1, x2, y2, thickness, name and flags */
+		: T_PAD '(' measure measure measure measure measure STRING INTEGER ')'
+			{
+				char		p_number[8];
+
+				sprintf(p_number, "%d", pin_num++);
+				CreateNewPad(yyElement,OU ($3),OU ($4),OU ($5),OU ($6),OU ($7), 2*GROUNDPLANEFRAME,
+					OU ($7) + 2*MASKFRAME, $8,p_number, OldFlags($9));
+				free ($8);
+			}
+		;
+
+flags		: INTEGER	{ $$ = OldFlags($1); }
+		| STRING	{ $$ = string_to_flags ($1, yyerror); free($1); }
+		;
+
+symbols
+		: symbol
+		| symbols symbol
+		;
+
+/* %start-doc pcbfile Symbol
+
+ at syntax
+Symbol [Char Delta] (
+Symbol (Char Delta) (
+@ @ @ @dots{} symbol lines @dots{}
+)
+ at end syntax
+
+ at table @var
+ at item Char
+The character or numerical character value this symbol represents.
+Characters must be in single quotes.
+ at item Delta
+Additional space to allow after this character.
+ at end table
+
+%end-doc */
+
+symbol : symbolhead symboldata ')'
+
+symbolhead	: T_SYMBOL '[' symbolid measure ']' '('
+			{
+				if ($3 <= 0 || $3 > MAX_FONTPOSITION)
+				{
+					yyerror("fontposition out of range");
+					YYABORT;
+				}
+				Symbol = &yyFont->Symbol[$3];
+				if (Symbol->Valid)
+				{
+					yyerror("symbol ID used twice");
+					YYABORT;
+				}
+				Symbol->Valid = pcb_true;
+				Symbol->Delta = NU ($4);
+			}
+		| T_SYMBOL '(' symbolid measure ')' '('
+			{
+				if ($3 <= 0 || $3 > MAX_FONTPOSITION)
+				{
+					yyerror("fontposition out of range");
+					YYABORT;
+				}
+				Symbol = &yyFont->Symbol[$3];
+				if (Symbol->Valid)
+				{
+					yyerror("symbol ID used twice");
+					YYABORT;
+				}
+				Symbol->Valid = pcb_true;
+				Symbol->Delta = OU ($4);
+			}
+		;
+
+symbolid
+		: INTEGER
+		| CHAR_CONST
+		;
+
+symboldata
+		: /* empty */
+		| symboldata symboldefinition
+		| symboldata hiressymbol
+		;
+
+/* %start-doc pcbfile SymbolLine
+
+ at syntax
+SymbolLine [X1 Y1 X2 Y1 Thickness]
+SymbolLine (X1 Y1 X2 Y1 Thickness)
+ at end syntax
+
+ at table @var
+ at item X1 Y1 X2 Y2
+The endpoints of this line.
+ at item Thickness
+The width of this line.
+ at end table
+
+%end-doc */
+
+symboldefinition
+			/* x1, y1, x2, y2, thickness */
+		: T_SYMBOLLINE '(' measure measure measure measure measure ')'
+			{
+				CreateNewLineInSymbol(Symbol, OU ($3), OU ($4), OU ($5), OU ($6), OU ($7));
+			}
+		;
+hiressymbol
+			/* x1, y1, x2, y2, thickness */
+		: T_SYMBOLLINE '[' measure measure measure measure measure ']'
+			{
+				CreateNewLineInSymbol(Symbol, NU ($3), NU ($4), NU ($5), NU ($6), NU ($7));
+			}
+		;
+
+/* %start-doc pcbfile Netlist
+
+ at syntax
+Netlist ( ) (
+@ @ @ @dots{} nets @dots{}
+)
+ at end syntax
+
+%end-doc */
+
+pcbnetlist	: pcbnetdef
+		|
+		;
+pcbnetdef
+			/* net(...) net(...) ... */
+		: T_NETLIST '(' ')' '('
+		  nets ')'
+		;
+
+nets
+		: netdefs
+		|
+		;
+
+netdefs
+		: net
+		| netdefs net
+		;
+
+
+/* %start-doc pcbfile Net
+
+ at syntax
+Net ("Name" "Style") (
+@ @ @ @dots{} connects @dots{}
+)
+ at end syntax
+
+ at table @var
+ at item Name
+The name of this net.
+ at item Style
+The routing style that should be used when autorouting this net.
+ at end table
+
+%end-doc */
+
+net
+			/* name style pin pin ... */
+		: T_NET '(' STRING STRING ')' '('
+			{
+				Menu = CreateNewNet((LibraryTypePtr)&yyPCB->NetlistLib, $3, $4);
+				free ($3);
+				free ($4);
+			}
+		 connections ')'
+		;
+
+connections
+		: conndefs
+		|
+		;
+
+conndefs
+		: conn
+		| conndefs conn
+		;
+
+/* %start-doc pcbfile Connect
+
+ at syntax
+Connect ("PinPad")
+ at end syntax
+
+ at table @var
+ at item PinPad
+The name of a pin or pad which is included in this net.  Pin and Pad
+names are named by the refdes and pin name, like @code{"U14-7"} for
+pin 7 of U14, or @code{"T4-E"} for pin E of T4.
+ at end table
+
+%end-doc */
+
+conn
+		: T_CONN '(' STRING ')'
+			{
+				CreateNewConnection(Menu, $3);
+				free ($3);
+			}
+		;
+
+/* %start-doc pcbfile Netlistpatch
+
+ at syntax
+NetListPatch ( ) (
+@ @ @ @dots{} netpatch @dots{}
+)
+ at end syntax
+
+%end-doc */
+
+pcbnetlistpatch	: pcbnetpatchdef
+		|
+		;
+pcbnetpatchdef
+			/* net(...) net(...) ... */
+		: T_NETLISTPATCH '(' ')' '('
+		  netpatches ')'
+		;
+
+netpatches
+		: netpatchdefs
+		|
+		;
+
+netpatchdefs
+		: netpatch
+		| netpatchdefs netpatch
+		;
+
+/* %start-doc pcbfile NetPatch
+
+ at syntax
+op (arg1 arg2 ...) (
+@ @ @ @dots{} netlist patch directive @dots{}
+)
+ at end syntax
+
+%end-doc */
+
+netpatch
+			/* name style pin pin ... */
+		: T_ADD_CONN      '(' STRING STRING ')'         { rats_patch_append(yyPCB, RATP_ADD_CONN, $3, $4, NULL); free($3); free($4); }
+		| T_DEL_CONN      '(' STRING STRING ')'         { rats_patch_append(yyPCB, RATP_DEL_CONN, $3, $4, NULL); free($3); free($4); }
+		| T_CHANGE_ATTRIB '(' STRING STRING STRING ')'  { rats_patch_append(yyPCB, RATP_CHANGE_ATTRIB, $3, $4, $5); free($3); free($4); free($5); }
+		;
+
+
+/* %start-doc pcbfile Attribute
+
+ at syntax
+Attribute ("Name" "Value")
+ at end syntax
+
+Attributes allow boards and elements to have arbitrary data attached
+to them, which is not directly used by PCB itself but may be of use by
+other programs or users.
+
+ at table @var
+ at item Name
+The name of the attribute
+
+ at item Value
+The value of the attribute.  Values are always stored as strings, even
+if the value is interpreted as, for example, a number.
+
+ at end table
+
+%end-doc */
+
+attribute
+		: T_ATTRIBUTE '(' STRING STRING ')'
+			{
+			  CreateNewAttribute (attr_list, $3, $4 ? $4 : (char *)"");
+				free ($3);
+				free ($4);
+			}
+		;
+
+opt_string	: STRING { $$ = $1; }
+		| /* empty */ { $$ = 0; }
+		;
+
+number
+		: FLOATING	{ $$ = $1; }
+		| INTEGER	{ $$ = $1; }
+		;
+
+measure
+		/* Default unit (no suffix) is cmil */
+		: number	{ do_measure(&$$, $1, PCB_MIL_TO_COORD ($1) / 100.0, 0); }
+		| number T_UMIL	{ M ($$, $1, PCB_MIL_TO_COORD ($1) / 100000.0); }
+		| number T_CMIL	{ M ($$, $1, PCB_MIL_TO_COORD ($1) / 100.0); }
+		| number T_MIL	{ M ($$, $1, PCB_MIL_TO_COORD ($1)); }
+		| number T_IN	{ M ($$, $1, PCB_INCH_TO_COORD ($1)); }
+		| number T_NM	{ M ($$, $1, PCB_MM_TO_COORD ($1) / 1000000.0); }
+		| number T_UM	{ M ($$, $1, PCB_MM_TO_COORD ($1) / 1000.0); }
+		| number T_MM	{ M ($$, $1, PCB_MM_TO_COORD ($1)); }
+		| number T_M	{ M ($$, $1, PCB_MM_TO_COORD ($1) * 1000.0); }
+		| number T_KM	{ M ($$, $1, PCB_MM_TO_COORD ($1) * 1000000.0); }
+		;
+
+%%
+
+/* ---------------------------------------------------------------------------
+ * error routine called by parser library
+ */
+int yyerror(const char * s)
+{
+	Message(PCB_MSG_ERROR, "ERROR parsing file '%s'\n"
+		"    line:        %i\n"
+		"    description: '%s'\n",
+		yyfilename, pcb_lineno, s);
+	return(0);
+}
+
+int pcb_wrap()
+{
+  return 1;
+}
+
+static int
+check_file_version (int ver)
+{
+  if ( ver > PCB_FILE_VERSION ) {
+    Message (PCB_MSG_DEFAULT, "ERROR:  The file you are attempting to load is in a format\n"
+	     "which is too new for this version of pcb.  To load this file\n"
+	     "you need a version of pcb which is >= %d.  If you are\n"
+	     "using a version built from git source, the source date\n"
+	     "must be >= %d.  This copy of pcb can only read files\n"
+	     "up to file version %d.\n", ver, ver, PCB_FILE_VERSION);
+    return 1;
+  }
+
+  return 0;
+}
+
+static void
+do_measure (PLMeasure *m, Coord i, double d, int u)
+{
+  m->ival = i;
+  m->bval = pcb_round (d);
+  m->dval = d;
+  m->has_units = u;
+}
+
+static int
+integer_value (PLMeasure m)
+{
+  if (m.has_units)
+    yyerror("units ignored here");
+  return m.ival;
+}
+
+static Coord
+old_units (PLMeasure m)
+{
+  if (m.has_units)
+    return m.bval;
+  return pcb_round (PCB_MIL_TO_COORD (m.ival));
+}
+
+static Coord
+new_units (PLMeasure m)
+{
+  if (m.has_units)
+    return m.bval;
+  /* if there's no unit m.dval already contains the converted value */
+  return pcb_round (m.dval);
+}
diff --git a/src_plugins/jostle/Makefile b/src_plugins/jostle/Makefile
new file mode 100644
index 0000000..ebdadff
--- /dev/null
+++ b/src_plugins/jostle/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_jostle
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/jostle/Plug.tmpasm b/src_plugins/jostle/Plug.tmpasm
new file mode 100644
index 0000000..e69ecb4
--- /dev/null
+++ b/src_plugins/jostle/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {jostle}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/jostle/jostle.o @]
+
+switch /local/pcb/jostle/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/jostle/README b/src_plugins/jostle/README
new file mode 100644
index 0000000..9ba50d8
--- /dev/null
+++ b/src_plugins/jostle/README
@@ -0,0 +1,5 @@
+Pushes lines out of the way.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/jostle/jostle.c b/src_plugins/jostle/jostle.c
new file mode 100644
index 0000000..2e4fa57
--- /dev/null
+++ b/src_plugins/jostle/jostle.c
@@ -0,0 +1,563 @@
+/*!
+ * \file jostle.c
+ *
+ * \brief jostle plug-in for PCB.
+ *
+ * \author Copyright (C) 2007 Ben Jackson <ben at ben.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ *
+ * Pushes lines out of the way.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "rats.h"
+#include "polygon.h"
+#include "remove.h"
+#include "error.h"
+#include "set.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "layer.h"
+#include "conf_core.h"
+#include "misc_util.h"
+
+/*#define DEBUG_POLYAREA*/
+
+double vect_dist2(Vector v1, Vector v2);
+#define Vcpy2(r,a)              {(r)[0] = (a)[0]; (r)[1] = (a)[1];}
+#define Vswp2(a,b) { long t; \
+        t = (a)[0], (a)[0] = (b)[0], (b)[0] = t; \
+        t = (a)[1], (a)[1] = (b)[1], (b)[1] = t; \
+}
+
+/*{if (!Marked.status && side==NORTHWEST) { DrawMark(pcb_true); Marked.status = True; Marked.X = p[0]; Marked.Y = p[1]; DrawMark(False);} }*/
+
+enum {
+	NORTH,
+	NORTHEAST,
+	EAST,
+	SOUTHEAST,
+	SOUTH,
+	SOUTHWEST,
+	WEST,
+	NORTHWEST
+};
+
+const char *dirnames[] = {
+	"NORTH",
+	"NORTHEAST",
+	"EAST",
+	"SOUTHEAST",
+	"SOUTH",
+	"SOUTHWEST",
+	"WEST",
+	"NORTHWEST"
+};
+
+#define ARG(n) (argc > (n) ? argv[n] : 0)
+
+static const char jostle_syntax[] = "Jostle(diameter)";
+
+/* DEBUG */
+static void DebugPOLYAREA(POLYAREA * s, char *color)
+{
+	int *x, *y, n, i = 0;
+	PLINE *pl;
+	VNODE *v;
+	POLYAREA *p;
+	HID *ddraw;
+	hidGC ddgc;
+
+#ifndef DEBUG_POLYAREA
+	return;
+#endif
+	ddraw = gui->request_debug_draw();
+	ddgc = ddraw->make_gc();
+
+	p = s;
+	do {
+		for (pl = p->contours; pl; pl = pl->next) {
+			n = pl->Count;
+			x = (int *) malloc(n * sizeof(int));
+			y = (int *) malloc(n * sizeof(int));
+			for (v = &pl->head; i < n; v = v->next) {
+				x[i] = v->point[0];
+				y[i++] = v->point[1];
+			}
+			if (1) {
+				gui->set_color(ddgc, color ? color : PCB->ConnectedColor);
+				gui->set_line_width(ddgc, 1);
+				for (i = 0; i < n - 1; i++) {
+					gui->draw_line(ddgc, x[i], y[i], x[i + 1], y[i + 1]);
+					/*  gui->fill_circle (ddgc, x[i], y[i], 30);*/
+				}
+				gui->draw_line(ddgc, x[n - 1], y[n - 1], x[0], y[0]);
+			}
+			free(x);
+			free(y);
+		}
+	} while ((p = p->f) != s);
+	ddraw->flush_debug_draw();
+	hid_action("Busy");
+	sleep(3);
+	ddraw->finish_debug_draw();
+}
+
+/*!
+ * \brief Find the bounding box of a POLYAREA.
+ *
+ * POLYAREAs linked by ->f/b are outlines.\n
+ * n->contours->next would be the start of the inner holes (irrelevant
+ * for bounding box).
+ */
+static BoxType POLYAREA_boundingBox(POLYAREA * a)
+{
+	POLYAREA *n;
+	PLINE *pa;
+	BoxType box;
+	int first = 1;
+
+	n = a;
+	do {
+		pa = n->contours;
+		if (first) {
+			box.X1 = pa->xmin;
+			box.X2 = pa->xmax + 1;
+			box.Y1 = pa->ymin;
+			box.Y2 = pa->ymax + 1;
+			first = 0;
+		}
+		else {
+			MAKEMIN(box.X1, pa->xmin);
+			MAKEMAX(box.X2, pa->xmax + 1);
+			MAKEMIN(box.Y1, pa->ymin);
+			MAKEMAX(box.Y2, pa->ymax + 1);
+		}
+	} while ((n = n->f) != a);
+	return box;
+}
+
+/*!
+ * Given a polygon and a side of it (a direction north/northeast/etc),
+ * find a line tangent to that side, offset by clearance, and return it
+ * as a pair of vectors PQ.\n
+ * Make it long so it will intersect everything in the area.
+ */
+static void POLYAREA_findXmostLine(POLYAREA * a, int side, Vector p, Vector q, int clearance)
+{
+	int extra;
+	p[0] = p[1] = 0;
+	q[0] = q[1] = 0;
+	extra = a->contours->xmax - a->contours->xmin + a->contours->ymax - a->contours->ymin;
+	switch (side) {
+	case NORTH:
+		p[1] = q[1] = a->contours->ymin - clearance;
+		p[0] = a->contours->xmin - extra;
+		q[0] = a->contours->xmax + extra;
+		break;
+	case SOUTH:
+		p[1] = q[1] = a->contours->ymax + clearance;
+		p[0] = a->contours->xmin - extra;
+		q[0] = a->contours->xmax + extra;
+		break;
+	case EAST:
+		p[0] = q[0] = a->contours->xmax + clearance;
+		p[1] = a->contours->ymin - extra;
+		q[1] = a->contours->ymax + extra;
+		break;
+	case WEST:
+		p[0] = q[0] = a->contours->xmin - clearance;
+		p[1] = a->contours->ymin - extra;
+		q[1] = a->contours->ymax + extra;
+		break;
+	default:											/* diagonal case */
+		{
+			int kx, ky, minmax, dq, ckx, cky;
+			Coord mm[2] = { MAX_COORD, -MAX_COORD };
+			Vector mmp[2];
+			VNODE *v;
+
+			switch (side) {
+			case NORTHWEST:
+				kx = 1;									/* new_x = kx * x + ky * y */
+				ky = 1;
+				dq = -1;								/* extend line in +x, dq*y */
+				ckx = cky = -1;					/* clear line in ckx*clear, cky*clear */
+				minmax = 0;							/* min or max */
+				break;
+			case SOUTHWEST:
+				kx = 1;
+				ky = -1;
+				dq = 1;
+				ckx = -1;
+				cky = 1;
+				minmax = 0;
+				break;
+			case NORTHEAST:
+				kx = 1;
+				ky = -1;
+				dq = 1;
+				ckx = 1;
+				cky = -1;
+				minmax = 1;
+				break;
+			case SOUTHEAST:
+				kx = ky = 1;
+				dq = -1;
+				ckx = cky = 1;
+				minmax = 1;
+				break;
+			default:
+				Message(PCB_MSG_ERROR, "jostle: aiee, what side?");
+				return;
+			}
+			v = &a->contours->head;
+			do {
+				int test = kx * v->point[0] + ky * v->point[1];
+				if (test < mm[0]) {
+					mm[0] = test;
+					mmp[0][0] = v->point[0];
+					mmp[0][1] = v->point[1];
+				}
+				if (test > mm[1]) {
+					mm[1] = test;
+					mmp[1][0] = v->point[0];
+					mmp[1][1] = v->point[1];
+				}
+			} while ((v = v->next) != &a->contours->head);
+			Vcpy2(p, mmp[minmax]);
+			/* add clearance in the right direction */
+			clearance *= 0.707123;		/* = cos(45) = sqrt(2)/2 */
+			p[0] += ckx * clearance;
+			p[1] += cky * clearance;
+			/* now create a tangent line to that point */
+			Vcpy2(q, p);
+			p[0] += -extra;
+			p[1] += -extra * dq;
+			q[0] += extra;
+			q[1] += extra * dq;
+		}
+	}
+}
+
+/*!
+ * Given a 'side' from the NORTH/SOUTH/etc enum, rotate it by n.
+ */
+static int rotateSide(int side, int n)
+{
+	return (side + n + 8) % 8;
+}
+
+/*!
+ * Wrapper for CreateNewLineOnLayer that takes vectors and deals with Undo
+ */
+static LineType *CreateVectorLineOnLayer(LayerType * layer, Vector a, Vector b, int thickness, int clearance, FlagType flags)
+{
+	LineType *line;
+
+	line = CreateNewLineOnLayer(layer, a[0], a[1], b[0], b[1], thickness, clearance, flags);
+	if (line) {
+		AddObjectToCreateUndoList(PCB_TYPE_LINE, layer, line, line);
+	}
+	return line;
+}
+
+static LineType *MakeBypassLine(LayerType * layer, Vector a, Vector b, LineType * orig, POLYAREA ** expandp)
+{
+	LineType *line;
+
+	line = CreateVectorLineOnLayer(layer, a, b, orig->Thickness, orig->Clearance, orig->Flags);
+	if (line && expandp) {
+		POLYAREA *p = LinePoly(line, line->Thickness + line->Clearance);
+		poly_Boolean_free(*expandp, p, expandp, PBO_UNITE);
+	}
+	return line;
+}
+
+/*!
+ * Given a 'brush' that's pushing things out of the way (possibly already
+ * cut down to just the part relevant to our line) and a line that
+ * intersects it on some layer, find the 45/90 lines required to go around
+ * the brush on the named side.  Create them and remove the original.
+ *
+ * Imagine side = north:
+ * <pre>
+                 /      \
+            ----b##FLAT##c----
+               Q          P
+   lA-ORIG####a            d####ORIG-lB
+             /              \
+ * </pre>
+ * First find the extended three lines that go around the brush.
+ * Then intersect them with each other and the original to find
+ * points a, b, c, d.  Finally connect the dots and remove the
+ * old straight line.
+ */
+static int MakeBypassingLines(POLYAREA * brush, LayerType * layer, LineType * line, int side, POLYAREA ** expandp)
+{
+	Vector pA, pB, flatA, flatB, qA, qB;
+	Vector lA, lB;
+	Vector a, b, c, d, junk;
+	int hits;
+
+	SET_FLAG(PCB_FLAG_DRC, line);			/* will cause sublines to inherit */
+	lA[0] = line->Point1.X;
+	lA[1] = line->Point1.Y;
+	lB[0] = line->Point2.X;
+	lB[1] = line->Point2.Y;
+
+	POLYAREA_findXmostLine(brush, side, flatA, flatB, line->Thickness / 2);
+	POLYAREA_findXmostLine(brush, rotateSide(side, 1), pA, pB, line->Thickness / 2);
+	POLYAREA_findXmostLine(brush, rotateSide(side, -1), qA, qB, line->Thickness / 2);
+	hits = vect_inters2(lA, lB, qA, qB, a, junk) +
+		vect_inters2(qA, qB, flatA, flatB, b, junk) +
+		vect_inters2(pA, pB, flatA, flatB, c, junk) + vect_inters2(lA, lB, pA, pB, d, junk);
+	if (hits != 4) {
+		return 0;
+	}
+	/* flip the line endpoints to match up with a/b */
+	if (vect_dist2(lA, d) < vect_dist2(lA, a)) {
+		Vswp2(lA, lB);
+	}
+	MakeBypassLine(layer, lA, a, line, NULL);
+	MakeBypassLine(layer, a, b, line, expandp);
+	MakeBypassLine(layer, b, c, line, expandp);
+	MakeBypassLine(layer, c, d, line, expandp);
+	MakeBypassLine(layer, d, lB, line, NULL);
+	RemoveLine(layer, line);
+	return 1;
+}
+
+struct info {
+	BoxType box;
+	POLYAREA *brush;
+	LayerType *layer;
+	POLYAREA *smallest;
+	/*!< after cutting brush with line, the smallest chunk, which we
+	 * will go around on 'side'.
+	 */
+	LineType *line;
+	int side;
+	double centroid;
+	/*!< smallest difference between slices of brush after cutting with
+	 * line, trying to find the line closest to the centroid to process
+	 * first
+	 */
+};
+
+/*!
+ * Process lines that intersect our 'brush'.
+ */
+static r_dir_t jostle_callback(const BoxType * targ, void *private)
+{
+	LineType *line = (LineType *) targ;
+	struct info *info = private;
+	POLYAREA *lp, *copy, *tmp, *n, *smallest = NULL;
+	Vector p;
+	int inside = 0, side, r;
+	double small, big;
+	int nocentroid = 0;
+
+	if (TEST_FLAG(PCB_FLAG_DRC, line)) {
+		return 0;
+	}
+	fprintf(stderr, "hit! %p\n", (void *)line);
+	p[0] = line->Point1.X;
+	p[1] = line->Point1.Y;
+	if (poly_InsideContour(info->brush->contours, p)) {
+		pcb_fprintf(stderr, "\tinside1 %ms,%ms\n", p[0], p[1]);
+		inside++;
+	}
+	p[0] = line->Point2.X;
+	p[1] = line->Point2.Y;
+	if (poly_InsideContour(info->brush->contours, p)) {
+		pcb_fprintf(stderr, "\tinside2 %ms,%ms\n", p[0], p[1]);
+		inside++;
+	}
+	lp = LinePoly(line, line->Thickness);
+	if (!Touching(lp, info->brush)) {
+		/* not a factor */
+		return 0;
+	}
+	poly_Free(&lp);
+	if (inside) {
+		/* XXX not done!
+		   XXX if this is part of a series of lines passing
+		   XXX through, need to process as a group.
+		   XXX if it just ends in here, shorten it?? */
+		return 0;
+	}
+	/*
+	 * Cut the brush with the line to figure out which side to go
+	 * around.  Use a very fine line.  XXX can still graze.
+	 */
+	lp = LinePoly(line, 1);
+	if (!poly_M_Copy0(&copy, info->brush))
+		return 0;
+	r = poly_Boolean_free(copy, lp, &tmp, PBO_SUB);
+	if (r != err_ok) {
+		pcb_fprintf(stderr, "Error while jostling PBO_SUB: %d\n", r);
+		return 0;
+	}
+	if (tmp == tmp->f) {
+		/* it didn't slice, must have glanced. intersect instead
+		 * to get the glancing sliver??
+		 */
+		pcb_fprintf(stderr, "try isect??\n");
+		lp = LinePoly(line, line->Thickness);
+		r = poly_Boolean_free(tmp, lp, &tmp, PBO_ISECT);
+		if (r != err_ok) {
+			fprintf(stderr, "Error while jostling PBO_ISECT: %d\n", r);
+			return 0;
+		}
+		nocentroid = 1;
+	}
+	/* XXX if this operation did not create two chunks, bad things are about to happen */
+	if (!tmp)
+		return 0;
+	n = tmp;
+	small = big = tmp->contours->area;
+	do {
+		pcb_fprintf(stderr, "\t\tarea %g, %ms,%ms %ms,%ms\n", n->contours->area, n->contours->xmin, n->contours->ymin,
+								n->contours->xmax, n->contours->ymax);
+		if (n->contours->area <= small) {
+			smallest = n;
+			small = n->contours->area;
+		}
+		if (n->contours->area >= big) {
+			big = n->contours->area;
+		}
+	} while ((n = n->f) != tmp);
+	if (line->Point1.X == line->Point2.X) {	/* | */
+		if (info->box.X2 - smallest->contours->xmax > smallest->contours->xmin - info->box.X1) {
+			side = WEST;
+		}
+		else {
+			side = EAST;
+		}
+	}
+	else if (line->Point1.Y == line->Point2.Y) {	/* - */
+		if (info->box.Y2 - smallest->contours->ymax > smallest->contours->ymin - info->box.Y1) {
+			side = NORTH;
+		}
+		else {
+			side = SOUTH;
+		}
+	}
+	else if ((line->Point1.X > line->Point2.X) == (line->Point1.Y > line->Point2.Y)) {	/* \ */
+		if (info->box.X2 - smallest->contours->xmax > smallest->contours->xmin - info->box.X1) {
+			side = SOUTHWEST;
+		}
+		else {
+			side = NORTHEAST;
+		}
+	}
+	else {												/* / */
+		if (info->box.X2 - smallest->contours->xmax > smallest->contours->xmin - info->box.X1) {
+			side = NORTHWEST;
+		}
+		else {
+			side = SOUTHEAST;
+		}
+	}
+	pcb_fprintf(stderr, "\t%s\n", dirnames[side]);
+	if (info->line == NULL || (!nocentroid && (big - small) < info->centroid)) {
+		pcb_fprintf(stderr, "\tkeep it!\n");
+		if (info->smallest) {
+			poly_Free(&info->smallest);
+		}
+		info->centroid = nocentroid ? DBL_MAX : (big - small);
+		info->side = side;
+		info->line = line;
+		info->smallest = smallest;
+		return 1;
+	}
+	return 0;
+}
+
+static int jostle(int argc, const char **argv, Coord x, Coord y)
+{
+	pcb_bool rel;
+	POLYAREA *expand;
+	float value;
+	struct info info;
+	int found;
+
+	if (argc == 2) {
+		pcb_bool succ;
+		value = GetValue(ARG(0), ARG(1), &rel, &succ);
+		if (!succ) {
+			Message(PCB_MSG_ERROR, "Failed to convert size\n");
+			return -1;
+		}
+	}
+	else {
+		value = conf_core.design.via_thickness + (PCB->Bloat + 1) * 2 + 50;
+	}
+	x = Crosshair.X;
+	y = Crosshair.Y;
+	fprintf(stderr, "%d, %d, %f\n", (int) x, (int) y, value);
+	info.brush = CirclePoly(x, y, value / 2);
+	info.layer = CURRENT;
+	LINE_LOOP(info.layer);
+	{
+		CLEAR_FLAG(PCB_FLAG_DRC, line);
+	}
+	END_LOOP;
+	do {
+		info.box = POLYAREA_boundingBox(info.brush);
+		DebugPOLYAREA(info.brush, NULL);
+		pcb_fprintf(stderr, "search (%ms,%ms)->(%ms,%ms):\n", info.box.X1, info.box.Y1, info.box.X2, info.box.Y2);
+		info.line = NULL;
+		info.smallest = NULL;
+		r_search(info.layer->line_tree, &info.box, NULL, jostle_callback, &info, &found);
+		if (found) {
+			expand = NULL;
+			MakeBypassingLines(info.smallest, info.layer, info.line, info.side, &expand);
+			poly_Free(&info.smallest);
+			poly_Boolean_free(info.brush, expand, &info.brush, PBO_UNITE);
+		}
+	} while (found);
+	SetChangedFlag(pcb_true);
+	IncrementUndoSerialNumber();
+	return 0;
+}
+
+static HID_Action jostle_action_list[] = {
+	{"jostle", NULL, jostle, "Move lines out of the way", jostle_syntax},
+};
+
+char *jostle_cookie = "jostle plugin";
+
+REGISTER_ACTIONS(jostle_action_list, jostle_cookie)
+
+static void hid_jostle_uninit(void)
+{
+	hid_remove_actions_by_cookie(jostle_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_jostle_init()
+{
+	REGISTER_ACTIONS(jostle_action_list, jostle_cookie);
+	return hid_jostle_uninit;
+}
diff --git a/src_plugins/lib_gensexpr/Makefile b/src_plugins/lib_gensexpr/Makefile
new file mode 100644
index 0000000..deeabca
--- /dev/null
+++ b/src_plugins/lib_gensexpr/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_lib_gensexpr
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/lib_gensexpr/Plug.tmpasm b/src_plugins/lib_gensexpr/Plug.tmpasm
new file mode 100644
index 0000000..6b72f31
--- /dev/null
+++ b/src_plugins/lib_gensexpr/Plug.tmpasm
@@ -0,0 +1,12 @@
+put /local/pcb/mod {lib_gensexpr}
+put /local/pcb/mod/OBJS [@
+	$(PLUGDIR)/lib_gensexpr/lib_gensexpr.o
+	$(SRC_3RD_DIR)/gensexpr/gsxl.o
+	$(SRC_3RD_DIR)/gensexpr/gsx_parse.o
+@]
+
+switch /local/pcb/lib_gensexpr/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/lib_gensexpr/README b/src_plugins/lib_gensexpr/README
new file mode 100644
index 0000000..17fb251
--- /dev/null
+++ b/src_plugins/lib_gensexpr/README
@@ -0,0 +1,5 @@
+S-expression parser lib
+
+#state: works
+#default: disabled
+#implements: (lib)
diff --git a/src_plugins/lib_gensexpr/lib_gensexpr.c b/src_plugins/lib_gensexpr/lib_gensexpr.c
new file mode 100644
index 0000000..a3c6503
--- /dev/null
+++ b/src_plugins/lib_gensexpr/lib_gensexpr.c
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+#include "plugins.h"
+
+pcb_uninit_t hid_lib_gensexpr_init(void)
+{
+	return NULL;
+}
diff --git a/src_plugins/lib_legacy_func/Makefile b/src_plugins/lib_legacy_func/Makefile
new file mode 100644
index 0000000..87807f6
--- /dev/null
+++ b/src_plugins/lib_legacy_func/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_lib_legacy_func
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/lib_legacy_func/Plug.tmpasm b/src_plugins/lib_legacy_func/Plug.tmpasm
new file mode 100644
index 0000000..650e48f
--- /dev/null
+++ b/src_plugins/lib_legacy_func/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {lib_legacy_func}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/lib_legacy_func/lib_legacy_func.o @]
+
+switch /local/pcb/lib_legacy_func/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/lib_legacy_func/README b/src_plugins/lib_legacy_func/README
new file mode 100644
index 0000000..992584b
--- /dev/null
+++ b/src_plugins/lib_legacy_func/README
@@ -0,0 +1,8 @@
+Random collection of old/obsolete (legacy) functions.
+3rd party plugins may depend on them.
+This module implements C functions and variables and does not register
+actions or flags.
+
+#state: works
+#default: buildin
+#implements: (lib)
diff --git a/src_plugins/lib_legacy_func/lib_legacy_func.c b/src_plugins/lib_legacy_func/lib_legacy_func.c
new file mode 100644
index 0000000..766aca4
--- /dev/null
+++ b/src_plugins/lib_legacy_func/lib_legacy_func.c
@@ -0,0 +1,114 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include <genvector/gds_char.h>
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "plugins.h"
+
+char *ExpandFilename(char *Dirname, char *Filename)
+{
+	gds_t answer;
+	char *command;
+	FILE *pipe;
+	int c;
+
+	/* allocate memory for commandline and build it */
+	gds_init(&answer);
+	if (Dirname) {
+		command = (char *) calloc(strlen(Filename) + strlen(Dirname) + 7, sizeof(char));
+		sprintf(command, "echo %s/%s", Dirname, Filename);
+	}
+	else {
+		command = (char *) calloc(strlen(Filename) + 6, sizeof(char));
+		sprintf(command, "echo %s", Filename);
+	}
+
+	/* execute it with shell */
+	if ((pipe = popen(command, "r")) != NULL) {
+		/* discard all but the first returned line */
+		for (;;) {
+			if ((c = fgetc(pipe)) == EOF || c == '\n' || c == '\r')
+				break;
+			else
+				gds_append(&answer, c);
+		}
+
+		free(command);
+		if (pclose(pipe)) {
+			gds_uninit(&answer);
+			return NULL;
+		}
+		else
+			return answer.array;
+	}
+
+	/* couldn't be expanded by the shell */
+	PopenErrorMessage(command);
+	free(command);
+	gds_uninit(&answer);
+	return NULL;
+}
+
+void CreateQuotedString(gds_t *DS, char *S)
+{
+	gds_truncate(DS, 0);
+	gds_append(DS, '"');
+	while (*S) {
+		if (*S == '"' || *S == '\\')
+			gds_append(DS, '\\');
+		gds_append(DS, *S++);
+	}
+	gds_append(DS, '"');
+}
+
+int FileExists(const char *name)
+{
+	FILE *f;
+	f = fopen(name, "r");
+	if (f) {
+		fclose(f);
+		return 1;
+	}
+	return 0;
+}
+
+
+pcb_uninit_t hid_lib_legacy_func_init(void)
+{
+	return NULL;
+}
+
diff --git a/src_plugins/lib_legacy_func/lib_legacy_func.h b/src_plugins/lib_legacy_func/lib_legacy_func.h
new file mode 100644
index 0000000..1761685
--- /dev/null
+++ b/src_plugins/lib_legacy_func/lib_legacy_func.h
@@ -0,0 +1,42 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+/* concatenates directory and filename if directory != NULL,
+ * expands them with a shell and returns the found name(s) or NULL
+ */
+char *ExpandFilename(char *dirname, char *filename);
+
+/*
+ * writes a string to the passed file pointer
+ * some special characters are quoted
+ */
+void CreateQuotedString(gds_t *, char *);
+
+/*
+ * Return nonzero if the given file exists and is readable.
+ */
+int FileExists(const char *name)
diff --git a/src_plugins/loghid/Makefile b/src_plugins/loghid/Makefile
new file mode 100644
index 0000000..42f703a
--- /dev/null
+++ b/src_plugins/loghid/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_loghid
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/loghid/Plug.tmpasm b/src_plugins/loghid/Plug.tmpasm
new file mode 100644
index 0000000..4aadc31
--- /dev/null
+++ b/src_plugins/loghid/Plug.tmpasm
@@ -0,0 +1,10 @@
+put /local/pcb/mod {loghid}
+put /local/pcb/mod/OBJS [@
+	$(PLUGDIR)/loghid/loghid.o
+@]
+
+switch /local/pcb/loghid/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/loghid/README b/src_plugins/loghid/README
new file mode 100644
index 0000000..09f61f0
--- /dev/null
+++ b/src_plugins/loghid/README
@@ -0,0 +1,6 @@
+Sits between a HID (or exporter) and the core and logs all core->plugin calls
+made through the HID structure.
+
+#state: WIP
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/loghid/hid-logger.c b/src_plugins/loghid/hid-logger.c
new file mode 100644
index 0000000..6bddf05
--- /dev/null
+++ b/src_plugins/loghid/hid-logger.c
@@ -0,0 +1,232 @@
+/*
+ * a HID mostly for debugging.
+ */
+#include "hid-logger.h"
+#include "pcb-printf.h"
+#include "global_objs.h"
+
+#define ENUM_LOG_TEXT(e) case e: txt = #e; break
+
+/*
+ * Just having one instance for now. Keeping it
+ * local is fine.
+ */
+static HID logging_hid_;
+static HID *delegatee_ = NULL;
+static FILE *out_ = NULL;
+
+static HID_Attribute *log_get_export_options(int *ret) {
+	HID_Attribute *result = delegatee_->get_export_options(ret);
+	pcb_fprintf(out_, "get_export_options(ret) -> ret=%d\n", *ret);
+	return result;
+}
+
+static void log_do_exit(HID *hid) {
+	pcb_fprintf(out_, "do_exit()\n");
+	delegatee_->do_exit(delegatee_);
+}
+
+static void log_parse_arguments(int *argc, char ***argv) {
+	pcb_fprintf(out_, "parse_arguments()\n");
+	delegatee_->parse_arguments(argc, argv);
+}
+
+static void log_invalidate_lr(int left, int right, int top, int bottom) {
+	pcb_fprintf(out_, "invalidate_lr(%mm, %mm, %mm, %mm)\n", left, right, top, bottom);
+	delegatee_->invalidate_lr(left, right, top, bottom);
+}
+
+static void log_invalidate_all(void) {
+	pcb_fprintf(out_, "invalidate_all()\n");
+	delegatee_->invalidate_all();
+}
+
+static void log_notify_mark_change(pcb_bool changes_complete) {
+	pcb_fprintf(out_, "notify_mark_change(%s)\n", changes_complete ? "true" : "false");
+	delegatee_->notify_mark_change(changes_complete);
+}
+
+static int log_set_layer(const char *name, int group, int empty) {
+	pcb_fprintf(out_, "set_layer(name=%s, group=%d, empty=%s", name, group, empty ? "true" : "false");
+	return delegatee_->set_layer(name, group, empty);
+}
+
+static void log_end_layer() {
+	pcb_fprintf(out_, "end_layer()\n");
+	delegatee_->end_layer();
+}
+
+static hidGC log_make_gc() {
+	pcb_fprintf(out_, "make_gc()\n");
+	return delegatee_->make_gc();
+}
+
+static void log_destroy_gc(hidGC gc) {
+	pcb_fprintf(out_, "detroy_gc()\n");
+	delegatee_->destroy_gc(gc);
+}
+
+static void log_use_mask(int use_it) {
+	pcb_fprintf(out_, "use_mask(%x)\n", use_it);
+	delegatee_->use_mask(use_it);
+}
+
+static void log_set_color(hidGC gc, const char *name) {
+	pcb_fprintf(out_, "set_color(gc, %s)\n", name);
+	delegatee_->set_color(gc, name);
+}
+
+static void log_set_line_cap(hidGC gc, EndCapStyle style) {
+	const char *txt = "unknown";
+	switch (style) {
+		ENUM_LOG_TEXT(Trace_Cap);
+		ENUM_LOG_TEXT(Square_Cap);
+		ENUM_LOG_TEXT(Round_Cap);
+		ENUM_LOG_TEXT(Beveled_Cap);
+	}
+	pcb_fprintf(out_, "set_line_cap(gc, %s)\n", txt);
+	delegatee_->set_line_cap(gc, style);
+}
+
+static void log_set_line_width(hidGC gc, Coord width) {
+	pcb_fprintf(out_, "set_line_width(gc, %d)\n", width);
+	delegatee_->set_line_width(gc, width);
+}
+
+static void log_set_draw_xor(hidGC gc, int xor) {
+	pcb_fprintf(out_, "set_draw_xor(gc, %s)\n", xor ? "true" : "false");
+	delegatee_->set_draw_xor(gc, xor);
+}
+
+static void log_set_draw_faded(hidGC gc, int faded) {
+	pcb_fprintf(out_, "set_draw_faded(gc, %s)\n", faded ? "true" : "false");
+	delegatee_->set_draw_faded(gc, faded);
+}
+
+static void log_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) {
+	pcb_fprintf(out_, "draw_line(gc, %mm, %mm, %mm, %mm)\n", x1, y1, x2, y2);
+	delegatee_->draw_line(gc, x1, y1, x2, y2);
+}
+
+static void log_draw_arc(hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle) {
+	pcb_fprintf(out_, "draw_arc(gc, %mm, %mm, rx=%mm, ry=%mm, start_angle=%.1f, delta_a=%.1f)\n",
+					cx, cy, xradius, yradius, start_angle, delta_angle);
+	delegatee_->draw_arc(gc, cx, cy, xradius, yradius, start_angle, delta_angle);
+}
+
+static void log_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) {
+	pcb_fprintf(out_, "draw_rect(gc, %mm, %mm, %mm, %mm)\n", x1, y1, x2, y2);
+	delegatee_->draw_rect(gc, x1, y1, x2, y2);
+}
+
+static void log_fill_circle(hidGC gc, Coord x, Coord y, Coord r) {
+	pcb_fprintf(out_, "fill_circle(gc, %mm, %mm, %mm)\n", x, y, r);
+	delegatee_->fill_circle(gc, x, y, r);
+}
+
+static void log_fill_polygon(hidGC gc, int n_coords, Coord *x, Coord *y) {
+	int i;
+	pcb_fprintf(out_, "fill_polygon(gc, %d", n_coords);
+	for (i = 0; i < n_coords; ++i) {
+		pcb_fprintf(out_, ", (%mm, %mm)", x[i], y[i]);
+	}
+	pcb_fprintf(out_, ")\n");
+	delegatee_->fill_polygon(gc, n_coords, x, y);
+}
+
+static void log_fill_pcb_polygon(hidGC gc, PolygonType *poly, const BoxType *clip_box) {
+	pcb_fprintf(out_, "fill_pcb_polygon(gc, poly->PointN=%d, ...)\n", poly->PointN);
+	delegatee_->fill_pcb_polygon(gc, poly, clip_box);
+}
+
+static void log_thindraw_pcb_polygon(hidGC gc, PolygonType *poly, const BoxType *clip_box) {
+	pcb_fprintf(out_, "thindraw_pcb_polygon(gc, poly->PointN=%d, ...)\n", poly->PointN);
+	delegatee_->thindraw_pcb_polygon(gc, poly, clip_box);
+}
+
+static void log_fill_pcb_pad(hidGC gc, PadType *pad, pcb_bool clip, pcb_bool mask) {
+	pcb_fprintf(out_, "fill_pcb_pad(gc, pad->name=%s, clip=%s, mask=%s)\n",
+							pad->Name, clip ? "true" : "false", mask ? "true" : "false");
+	delegatee_->fill_pcb_pad(gc, pad, clip, mask);
+}
+
+static void log_thindraw_pcb_pad(hidGC gc, PadType *pad, pcb_bool clip, pcb_bool mask) {
+	pcb_fprintf(out_, "thindraw_pcb_pad(gc, pad->name=%s, clip=%s, mask=%s)\n",
+							pad->Name, clip ? "true" : "false", mask ? "true" : "false");
+	delegatee_->thindraw_pcb_pad(gc, pad, clip, mask);
+}
+
+static void log_fill_pcb_pv(hidGC fg, hidGC bg, PinType *pv, pcb_bool drawHole, pcb_bool mask) {
+	pcb_fprintf(out_, "fill_pcb_pv(fg, bg, (%mm, %mm), draw_hole=%s, mask=%s)\n",
+							pv->X, pv->Y, drawHole ? "true" : "false", mask ? "true" : "false");
+	delegatee_->fill_pcb_pv(fg, bg, pv, drawHole, mask);
+}
+
+static void log_thindraw_pcb_pv(hidGC fg, hidGC bg, PinType *pv, pcb_bool drawHole, pcb_bool mask) {
+	pcb_fprintf(out_, "thindraw_pcb_pv(fg, bg, (%mm, %mm), draw_hole=%s, mask=%s)\n",
+							pv->X, pv->Y, drawHole ? "true" : "false", mask ? "true" : "false");
+	delegatee_->thindraw_pcb_pv(fg, bg, pv, drawHole, mask);
+}
+
+static void log_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) {
+	pcb_fprintf(out_, "fill_rect(gc, %mm, %mm, %mm, %mm)\n", x1, y1, x2, y2);
+	delegatee_->fill_rect(gc, x1, y1, x2, y2);
+}
+
+static void log_report_dialog(const char *title, const char *msg) {
+	pcb_fprintf(out_, "report_dialog(\"%s\", \"%s\")\n", title, msg);
+	delegatee_->report_dialog(title, msg);
+}
+
+static void log_beep() {
+	pcb_fprintf(out_, "beep();   BEEEP   .... BEEEEEEEP\n");
+	delegatee_->beep();
+}
+
+HID *create_log_hid(FILE *log_out, HID *delegatee) {
+	out_ = log_out;
+	delegatee_ = delegatee;
+
+	/*
+	 * Setting up the output hid with a copy from the original, then
+	 * replace the functions we want to log.
+	 * We only log 'interesting' functions for now.
+	 */
+	logging_hid_ = *delegatee;
+
+#define REGISTER_IF_NOT_NULL(fun) logging_hid_.fun = delegatee->fun ? log_##fun : NULL
+	REGISTER_IF_NOT_NULL(get_export_options);
+	REGISTER_IF_NOT_NULL(do_exit);
+	REGISTER_IF_NOT_NULL(parse_arguments);
+	REGISTER_IF_NOT_NULL(invalidate_lr);
+	REGISTER_IF_NOT_NULL(invalidate_all);
+	REGISTER_IF_NOT_NULL(notify_mark_change);
+	REGISTER_IF_NOT_NULL(set_layer);
+	REGISTER_IF_NOT_NULL(end_layer);
+	REGISTER_IF_NOT_NULL(make_gc);
+	REGISTER_IF_NOT_NULL(destroy_gc);
+	REGISTER_IF_NOT_NULL(use_mask);
+	REGISTER_IF_NOT_NULL(set_color);
+	REGISTER_IF_NOT_NULL(set_line_cap);
+	REGISTER_IF_NOT_NULL(set_line_width);
+	REGISTER_IF_NOT_NULL(set_draw_xor);
+	REGISTER_IF_NOT_NULL(set_draw_faded);
+	REGISTER_IF_NOT_NULL(draw_line);
+	REGISTER_IF_NOT_NULL(draw_arc);
+	REGISTER_IF_NOT_NULL(draw_rect);
+	REGISTER_IF_NOT_NULL(fill_circle);
+	REGISTER_IF_NOT_NULL(fill_polygon);
+	REGISTER_IF_NOT_NULL(fill_pcb_polygon);
+	REGISTER_IF_NOT_NULL(thindraw_pcb_polygon);
+	REGISTER_IF_NOT_NULL(fill_pcb_pad);
+	REGISTER_IF_NOT_NULL(thindraw_pcb_pad);
+	REGISTER_IF_NOT_NULL(fill_pcb_pv);
+	REGISTER_IF_NOT_NULL(thindraw_pcb_pv);
+	REGISTER_IF_NOT_NULL(fill_rect);
+	REGISTER_IF_NOT_NULL(report_dialog);
+	REGISTER_IF_NOT_NULL(beep);
+
+#undef REGISTER_IF_NOT_NULL
+
+	return &logging_hid_;
+}
diff --git a/src_plugins/loghid/hid-logger.h b/src_plugins/loghid/hid-logger.h
new file mode 100644
index 0000000..9bdb078
--- /dev/null
+++ b/src_plugins/loghid/hid-logger.h
@@ -0,0 +1,14 @@
+#ifndef PCB_HID_LOGGER_H
+#define PCB_HID_LOGGER_H
+
+#include "hid.h"
+
+#include <stdio.h>
+
+/*
+ * Create a delegating HID that sends all calls to the
+ * delegatee but also logs the calls.
+ */
+HID *create_log_hid(FILE *log_out, HID *delegatee);
+
+#endif
diff --git a/src_plugins/loghid/loghid.c b/src_plugins/loghid/loghid.c
new file mode 100644
index 0000000..8d1d44f
--- /dev/null
+++ b/src_plugins/loghid/loghid.c
@@ -0,0 +1,139 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "global.h"
+#include "conf.h"
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "plugins.h"
+#include "hid_init.h"
+#include "hid_attrib.h"
+
+static const char *loghid_cookie = "loghid plugin";
+
+HID_Attribute loghid_attribute_list[] = {
+	{"target-hid", "the real GUI or export HID to relay calls to",
+	 HID_String, 0, 0, {0, 0, 0}, 0, 0}
+#define HA_target_hid 0
+};
+#define NUM_OPTIONS sizeof(loghid_attribute_list) / sizeof(loghid_attribute_list[0])
+
+static void loghid_parse_arguments_real(int *argc, char ***argv, int is_gui)
+{
+	HID *target;
+	const char *target_name;
+
+	hid_register_attributes(loghid_attribute_list, NUM_OPTIONS, loghid_cookie, 0);
+	hid_parse_command_line(argc, argv);
+
+	target_name = loghid_attribute_list[HA_target_hid].default_val.str_value;
+
+	if (is_gui)
+		target = hid_find_gui(target_name);
+	else
+		target = hid_find_exporter(target_name);
+
+#warning TODO:
+	fprintf(stderr, "Initialize for delegatee: '%s' -> %p\n", target_name, (void *)target);
+	exit(1); /* to avoid a segfault due to uninitialized hid fields now */
+}
+
+static void loghid_parse_arguments_gui(int *argc, char ***argv)
+{
+	loghid_parse_arguments_real(argc, argv, 1);
+}
+
+static void loghid_parse_arguments_exp(int *argc, char ***argv)
+{
+	loghid_parse_arguments_real(argc, argv, 0);
+}
+
+
+static int loghid_usage(const char *topic)
+{
+	fprintf(stderr, "\nhidlog command line arguments:\n\n");
+	hid_usage(loghid_attribute_list, NUM_OPTIONS);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Usage: pcb-rnd [generic_options] --gui hidlog-gui --target-hid gtk foo.pcb\n");
+	fprintf(stderr, "Usage: pcb-rnd [generic_options] --x hidlog-exp --target-hid png foo.pcb\n");
+	fprintf(stderr, "\n");
+	return 0;
+}
+
+REGISTER_ATTRIBUTES(loghid_attribute_list, loghid_cookie)
+
+static HID_Attribute *loghid_get_export_options(int *n)
+{
+/*	loghid_attribute_list[HA_psfile] = pcb_strdup("default?");*/
+
+	if (n)
+		*n = (sizeof(loghid_attribute_list)/sizeof(loghid_attribute_list[0]));
+	return loghid_attribute_list;
+}
+
+
+#include "dolists.h"
+
+static void hid_loghid_uninit(void)
+{
+}
+
+static HID loghid_gui;
+static HID loghid_exp;
+
+pcb_uninit_t hid_loghid_init(void)
+{
+	memset(&loghid_gui, 0, sizeof(HID));
+	memset(&loghid_exp, 0, sizeof(HID));
+
+	/* gui version */
+	loghid_gui.struct_size = sizeof(HID);
+	loghid_gui.name = "loghid-gui";
+	loghid_gui.description = "log GUI HID calls";
+	loghid_gui.gui = 1;
+
+	loghid_gui.usage = loghid_usage;
+	loghid_gui.parse_arguments = loghid_parse_arguments_gui;
+
+	hid_register_hid(&loghid_gui);
+
+	/* export version */
+	loghid_exp.struct_size = sizeof(HID);
+	loghid_exp.name = "loghid-exp";
+	loghid_exp.description = "log export HID calls";
+	loghid_exp.exporter = 1;
+
+	loghid_exp.usage = loghid_usage;
+	loghid_exp.parse_arguments = loghid_parse_arguments_exp;
+
+	hid_register_hid(&loghid_exp);
+
+	return hid_loghid_uninit;
+}
diff --git a/src_plugins/mincut/Makefile b/src_plugins/mincut/Makefile
new file mode 100644
index 0000000..ff909df
--- /dev/null
+++ b/src_plugins/mincut/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_mincut
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/mincut/Plug.tmpasm b/src_plugins/mincut/Plug.tmpasm
new file mode 100644
index 0000000..b7cc036
--- /dev/null
+++ b/src_plugins/mincut/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {mincut}
+append /local/pcb/mod/OBJS [@ $(PLUGDIR)/mincut/rats_mincut.o $(PLUGDIR)/mincut/pcb-mincut/graph.o $(PLUGDIR)/mincut/pcb-mincut/solve.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/mincut/rats_mincut_conf.h}
+
+switch /local/pcb/mincut/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/mincut/README b/src_plugins/mincut/README
new file mode 100644
index 0000000..e28991a
--- /dev/null
+++ b/src_plugins/mincut/README
@@ -0,0 +1,7 @@
+Use the minimal cut algorithm to indicate shorts: instead of highlighting
+two random pins/pads, try to highlight the least number of objects that
+connect the two networks.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/mincut/pcb-mincut/COPYING b/src_plugins/mincut/pcb-mincut/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src_plugins/mincut/pcb-mincut/H.in b/src_plugins/mincut/pcb-mincut/H.in
new file mode 100644
index 0000000..f7a05cf
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/H.in
@@ -0,0 +1,8 @@
+# a-1-b
+#   |
+# C-2-D
+
+net1 a b
+net2 C D
+neut 1 2
+conn C-2 D-2 a-1 b-1 1-2
diff --git a/src_plugins/mincut/pcb-mincut/Makefile b/src_plugins/mincut/pcb-mincut/Makefile
new file mode 100644
index 0000000..74f91e5
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/Makefile
@@ -0,0 +1,14 @@
+CFLAGS = -g -Wall -I../../../src_3rd/liblihata -I../../../src -I../../..
+LDFLAGS = -lm
+OBJS = main.o solve.o load.o graph.o ../../../src_3rd/liblihata/genht/htsi.o ../../../src_3rd/liblihata/genht/hash.o
+
+all: main
+
+main: $(OBJS)
+
+test:
+	cd test_cases && make
+
+
+clean:
+	rm $(OBJS) 2>/dev/null ; true
diff --git a/src_plugins/mincut/pcb-mincut/README b/src_plugins/mincut/pcb-mincut/README
new file mode 100644
index 0000000..32ecb06
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/README
@@ -0,0 +1,21 @@
+This mini-project demonstrates a minimal cut implementation tailored
+to solve geda/pcb "how to highlight shorts" problem.
+
+It should compile out of the box on GNU/Linux using make. Running
+a test case:
+
+./main < test_cases/H3.in
+
+If graphviz is installed, input graphs are drawn. Changing the
+#defines in solve.c causes more debug messages printed and/or
+debug graphs drawn.
+
+The code is not portable at all, since it is a proof-of-concept
+prototype.
+
+load.c is for easier testing; if the code ever gets merge in PCB it
+would use something native and load.c would be omitted. genht is a
+dependency of load.c.
+
+
+
diff --git a/src_plugins/mincut/pcb-mincut/graph.c b/src_plugins/mincut/pcb-mincut/graph.c
new file mode 100644
index 0000000..d195f1a
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/graph.c
@@ -0,0 +1,140 @@
+/*    pcb-mincut, a prototype project demonstrating how to highlight shorts
+ *    Copyright (C) 2012 Tibor 'Igor2' Palinkas
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License along
+ *   with this program; if not, write to the Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include "graph.h"
+
+/* allocate a new graph of n nodes with no edges */
+gr_t *gr_alloc(int n)
+{
+	gr_t *g = malloc(sizeof(gr_t));
+	g->n = n;
+	g->adj = calloc(n * n, sizeof(int));
+	return g;
+}
+
+/* create a new graph by cloning graph g */
+gr_t *gr_clone(gr_t *g)
+{
+	int size = g->n * g->n * sizeof(int);
+	gr_t *o = malloc(sizeof(gr_t));
+
+	o->n = g->n;
+	o->adj = malloc(size);
+	memcpy(o->adj, g->adj, size);
+	o->node2name = g->node2name;
+	return o;
+}
+
+/* free all resouces used by the graph */
+void gr_free(gr_t *g)
+{
+	free(g->adj);
+	free(g);
+}
+
+void gr_merge_nodes(gr_t *g, int n1, int n2)
+{
+	int n;
+	gr_bound_chk(g, n1, n2);
+
+	for(n = 0; n < g->n; n++) {
+		if (n != n2)
+			gr_add_(g, n, n1, gr_set_(g, n, n2, 0));
+		else
+			gr_add_(g, n1, n1, gr_set_(g, n2, n2, 0)/2);
+	}
+}
+
+void gr_print(gr_t *g, FILE *f, const char *prefix)
+{
+	int x, y;
+
+	fprintf(f, "%s      ", prefix);
+	for(x = 0; x < g->n; x++)
+		fprintf(f, "% 4d ", x);
+	fprintf(f, "\n");
+
+	for(y = 0; y < g->n; y++) {
+		fprintf(f, "%s % 4d ", prefix, y);
+		for(x = 0; x < g->n; x++)
+			fprintf(f, "% 4d ", gr_get_(g, x, y));
+		fprintf(f, "\n");
+	}
+	fprintf(f, "%s\n", prefix);
+	if (g->node2name != NULL) {
+	fprintf(f, "%snames:\n", prefix);
+		for(x = 0; x < g->n; x++)
+			fprintf(f, "%s % 4d=%s\n", prefix, x, g->node2name[x]);
+	}
+}
+
+int gr_draw(gr_t *g, const char *name, const char *type)
+{
+	char *cmd;
+	FILE *f;
+	int x, y;
+
+	if (type == NULL)
+		type = "png";
+
+	cmd = malloc(strlen(type)*2 + strlen(name) + 64);
+	sprintf(cmd, "dot -T%s -o %s.%s", type, name, type);
+	f = popen(cmd, "w");
+	if (f == NULL)
+		return -1;
+
+	fprintf(f, "graph %s {\n", name);
+
+	if (g->node2name != NULL)
+		for(x = 0; x < g->n; x++)
+			fprintf(f, "\t% 4d[label=\"%d\\n%s\"]\n", x, x, g->node2name[x] == NULL ? "NULL" : g->node2name[x]);
+
+	for(y = 0; y < g->n; y++) {
+		for(x = y; x < g->n; x++) {
+
+#ifdef MULTI_EDGE
+			int n;
+			for(n = gr_get_(g, x, y); n > 0; n--)
+				fprintf(f, "\t% 4d -- % 4d\n", x, y);
+#else
+			if (gr_get_(g, x, y) > 0)
+				fprintf(f, "\t% 4d -- % 4d [label=\"*%d\"]\n", x, y, gr_get_(g, x, y));
+#endif
+		}
+	}
+
+	fprintf(f, "}\n");
+	pclose(f);
+	return 0;
+}
+
+int gr_node_edges(gr_t *g, int node)
+{
+	int n, sum;
+	sum = 0;
+	gr_bound_chk(g, 0, node);
+	for(n = 0; n < g->n; n++)
+		sum += gr_get_(g, n, node);
+	return sum;
+}
diff --git a/src_plugins/mincut/pcb-mincut/graph.h b/src_plugins/mincut/pcb-mincut/graph.h
new file mode 100644
index 0000000..9afb404
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/graph.h
@@ -0,0 +1,98 @@
+/*    pcb-mincut, a prototype project demonstrating how to highlight shorts
+ *    Copyright (C) 2012 Tibor 'Igor2' Palinkas
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License along
+ *   with this program; if not, write to the Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef GRAPH_H
+#define GRAPH_H
+#include <stdio.h>
+#include <assert.h>
+#include "../../../config.h"
+
+typedef struct gr_s {
+	int n;     /* number of nodes */
+	int *adj;  /* adjacency matrix of n*n ints */
+	char **node2name; /* optional node names */
+} gr_t;
+
+/* allocate a new graph of n nodes with no edges */
+gr_t *gr_alloc(int n);
+
+/* create a new graph by cloning graph g */
+gr_t *gr_clone(gr_t *g);
+
+/* free all resouces used by the graph */
+void gr_free(gr_t *g);
+
+
+static inline PCB_FUNC_UNUSED void gr_bound_chk(gr_t *g, int n1, int n2)
+{
+	assert((n1 >= 0) && (n1 < g->n));
+	assert((n2 >= 0) && (n2 < g->n));
+}
+
+/* return number of edges between nodes n1 and n2 - without checks */
+static inline PCB_FUNC_UNUSED int gr_get_(gr_t *g, int n1, int n2)
+{
+	return g->adj[n1 * g->n + n2];
+}
+
+/* return number of edges between nodes n1 and n2 - with checks */
+static inline PCB_FUNC_UNUSED int gr_get(gr_t *g, int n1, int n2)
+{
+	gr_bound_chk(g, n1, n2);
+	return gr_get_(g, n1, n2);
+}
+
+/* return old number of edges between nodes n1 and n2 and change it to newnum - no check*/
+static inline PCB_FUNC_UNUSED int gr_set_(gr_t *g, int n1, int n2, int newnum)
+{
+	int old;
+	old = g->adj[n1 * g->n + n2];
+	g->adj[n1 * g->n + n2] = newnum;
+	g->adj[n2 * g->n + n1] = newnum;
+	return old;
+}
+
+/* return old number of edges between nodes n1 and n2 and increase it by newnum - no check*/
+static inline PCB_FUNC_UNUSED int gr_add_(gr_t *g, int n1, int n2, int newnum)
+{
+	int old;
+	old = g->adj[n1 * g->n + n2];
+	g->adj[n1 * g->n + n2] += newnum;
+	g->adj[n2 * g->n + n1] += newnum;
+	return old;
+}
+
+/* return old number of edges between nodes n1 and n2 and change it to newnum - check*/
+static inline PCB_FUNC_UNUSED int gr_set(gr_t *g, int n1, int n2, int newnum)
+{
+	gr_bound_chk(g, n1, n2);
+	return gr_set_(g, n1, n2, newnum);
+}
+
+/* merge two nodes n1 and n2 (contraction) */
+void gr_merge_nodes(gr_t *g, int n1, int n2);
+
+/* print the connection matrix */
+void gr_print(gr_t *g, FILE *f, const char *prefix);
+
+/* draw graph using graphviz/dot */
+int gr_draw(gr_t *g, const char *name, const char *type);
+
+/* return total number of edges of a node */
+int gr_node_edges(gr_t *g, int node);
+#endif
diff --git a/src_plugins/mincut/pcb-mincut/load.c b/src_plugins/mincut/pcb-mincut/load.c
new file mode 100644
index 0000000..2d4aac5
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/load.c
@@ -0,0 +1,262 @@
+/*    pcb-mincut, a prototype project demonstrating how to highlight shorts
+ *    Copyright (C) 2012 Tibor 'Igor2' Palinkas
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License along
+ *   with this program; if not, write to the Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "genht/htsi.h"
+#include "genht/hash.h"
+#include "graph.h"
+#include "compat_misc.h"
+
+/*#define DEBUG_GR*/
+
+/* maximum number of nodes */
+#define MAXNODES 1024
+
+/* maximum length of a name */
+#define NAMELEN 128
+
+/* how many edges net nodes are connected with - this would balance
+   mincut to avoid cutting such connections */
+#define NET_EDGES 8
+
+#define isspc(c) (((c) == ' ') || ((c) == '\t'))
+#define isid(c) ((((c) >= 'a') && ((c) <= 'z')) || (((c) >= 'A') && ((c) <= 'Z')) || (((c) >= '0') && ((c) <= '9')) || ((c) == '_'))
+
+htsi_t *name2node = NULL;              /* hash for looking up known node name -> node id pairs */
+char *node2name[MAXNODES] = {NULL};
+int num_nodes;
+
+static unsigned int keyhash(char *key) {
+	unsigned char *p = (unsigned char *)key;
+	unsigned int hash = 0;
+
+	while (*p)
+		hash += (hash << 2) + *p++;
+	return hash;
+}
+
+gr_t *load(FILE *f)
+{
+	int lineno;
+	int nets[2][MAXNODES];  /* nodes in nets */
+	int neut[MAXNODES];     /* neutral nodes */
+	int nn_net[2];          /* number of nodes in nets */
+	int nn_neut;            /* number of nodes in nets */
+	int nodes_fin;          /* 1 after reading the first conn line */
+	gr_t *g = NULL;
+
+/* peek at next char and return if it is a newline */
+#define return_at_eol \
+	do { \
+		char c; \
+		c = fgetc(f); \
+		if ((c == '\r') || (c == '\n')) \
+			return; \
+		ungetc(c, f); \
+	} while(0)
+
+
+	/* eat up space and tabs */
+	void eat_space(void)
+	{
+		int c;
+		do {
+			c = fgetc(f);
+		} while(isspc(c));
+		ungetc(c, f);
+	}
+
+	/* load next token of type id */
+	const char *token_id(void)
+	{
+		static char id[NAMELEN];
+		char *s;
+		int n;
+
+		n = sizeof(id) - 1;
+		s = id-1;
+		do {
+			s++;
+			*s = fgetc(f);
+			n--;
+			if (n <= 0) {
+				*s = '\0';
+				fprintf(stderr, "Error: name too long: %s...\n", id);
+			}
+		} while(isid(*s));
+		ungetc(*s, f);
+		*s = '\0';
+		return id;
+	}
+
+	/* read next id token and look it up as a node name; if it does not
+	   exist and alloc is 1, allocate it; if it does not exist and
+	   alloc is 0, abort with error */
+	int token_node(int alloc)
+	{
+		const char *id = token_id();
+		if (htsi_has(name2node, (char *)id))
+			return htsi_get(name2node, (char *)id);
+		if (alloc) {
+			node2name[num_nodes] = pcb_strdup(id);
+			htsi_set(name2node, node2name[num_nodes], num_nodes);
+			return num_nodes++;
+		}
+		fprintf(stderr, "Error: unknown node %s\n", id);
+		abort();
+	}
+
+	/* load a net1 or net2 statement */
+	void load_net(int net)
+	{
+		int node;
+		if (nodes_fin) {
+			fprintf(stderr, "Error: net%d after conn\n", net);
+			abort();
+		}
+		/* load each node and append */
+		while(1) {
+			eat_space();
+			return_at_eol;
+			node = token_node(1);
+			nets[net][nn_net[net]] = node;
+			nn_net[net]++;
+		}
+	}
+
+	/* load a neut statement */
+	void load_neut(void)
+	{
+		int node;
+		if (nodes_fin) {
+			fprintf(stderr, "Error: neut after conn\n");
+			abort();
+		}
+		/* load each node and append */
+		while(1) {
+			eat_space();
+			return_at_eol;
+			node = token_node(1);
+			neut[nn_neut] = node;
+			nn_neut++;
+		}
+	}
+
+	/* load a list of connections */
+	void load_conn(void)
+	{
+		int n1, n2;
+		char c;
+
+		/* if this is the first conn, create the graph */
+		if (!nodes_fin) {
+			g = gr_alloc(num_nodes);
+			g->node2name = node2name;
+		}
+		nodes_fin = 1;
+
+		/* load each n1-n2 pair */
+		while(1) {
+			eat_space();
+			return_at_eol;
+			n1 = token_node(0);
+			c = fgetc(f);
+			if (c != '-') {
+				fprintf(stderr, "Error: expected '-' between nodes in conn list (got '%c' instead)\n", c);
+				abort();
+			}
+			n2 = token_node(0);
+			gr_add_(g, n1, n2, 1);
+		}
+	}
+
+	name2node = htsi_alloc(keyhash, strkeyeq);
+	node2name[0] = pcb_strdup("(S)"); htsi_set(name2node, node2name[0], 0);
+	node2name[1] = pcb_strdup("(T)"); htsi_set(name2node, node2name[1], 1);
+	num_nodes = 2;
+	nn_net[0] = 0;
+	nn_net[1] = 0;
+	nn_neut = 0;
+
+	nodes_fin = 0;
+	lineno = 0;
+	while(!(feof(f))) {
+		char cmd[6];
+		lineno++;
+		eat_space();
+		*cmd = '\0';
+		fgets(cmd, 5, f);
+		if (*cmd == '#') {
+			char s[1024]; /* will break for comment lines longer than 1k */
+			fgets(s, sizeof(s), f);
+			continue;
+		}
+		if ((*cmd == '\n') || (*cmd == '\t') || (*cmd == '\0'))
+			continue;
+		if (strcmp(cmd, "net1") == 0)
+			load_net(0);
+		else if (strcmp(cmd, "net2") == 0)
+			load_net(1);
+		else if (strcmp(cmd, "neut") == 0)
+			load_neut();
+		else if (strcmp(cmd, "conn") == 0)
+			load_conn();
+	}
+
+
+#ifdef DEBUG_GR
+	gr_print(g, stdout, "(input0) ");
+	gr_draw(g, "input0", "png");
+#endif
+
+	/* preprocessing nets */
+	{
+		int net, node;
+
+#if 0
+/* this optimization looked like a good idea but fails for O.in because
+   it's always cheaper to cut T-> and S-> bounding than anything else */
+		/* merge net nodes into (S) and (T) */
+		for(net = 0; net < 2; net++)
+			for(node = 0; node < nn_net[net]; node++)
+				gr_merge_nodes(g, net, nets[net][node]);
+
+		/* make sure (S) and (T) connections are heavy */
+		for(net = 0; net < 2; net++)
+			for(node = 0; node < g->n; node++)
+				if (gr_get_(g, net, node))
+					gr_add_(g, net, node, NET_EDGES);
+#endif
+
+		/* make sure (S) and (T) connections are heavy */
+		for(net = 0; net < 2; net++)
+			for(node = 0; node < nn_net[net]; node++)
+				gr_add_(g, net, nets[net][node], NET_EDGES);
+	}
+
+#ifdef DEBUG_GR
+	gr_print(g, stdout, "(input1) ");
+	gr_draw(g, "input1", "png");
+#endif
+
+	return g;
+}
+
diff --git a/src_plugins/mincut/pcb-mincut/load.h b/src_plugins/mincut/pcb-mincut/load.h
new file mode 100644
index 0000000..9167a9c
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/load.h
@@ -0,0 +1,5 @@
+#include <stdio.h>
+#include "graph.h"
+
+/* load example input from FILE f and return the preprocessed graph */
+gr_t *load(FILE *f);
diff --git a/src_plugins/mincut/pcb-mincut/main.c b/src_plugins/mincut/pcb-mincut/main.c
new file mode 100644
index 0000000..f764aaf
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/main.c
@@ -0,0 +1,34 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "graph.h"
+#include "load.h"
+#include "solve.h"
+
+#define strempty(s) ((s) == NULL ? "" : (s))
+
+#undef strdup
+
+char *pcb_strdup(const char *s) { return strdup(s); }
+int pcb_rand() { return rand(); }
+
+int main()
+{
+	int *best;
+	gr_t *g;
+	int n;
+
+	g = load(stdin);
+	if (g == NULL) {
+		fprintf(stderr, "Failed to load input, exiting\n");
+		exit(1);
+	}
+	best = solve(g);
+	for(n = 0; best[n*2] != -1; n++)
+		printf("%s-%s\n", strempty(g->node2name[best[n*2+0]]), strempty(g->node2name[best[n*2+1]]));
+
+	gr_free(g);
+	free(best);
+
+	return 0;
+}
diff --git a/src_plugins/mincut/pcb-mincut/proposals.txt b/src_plugins/mincut/pcb-mincut/proposals.txt
new file mode 100644
index 0000000..da04502
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/proposals.txt
@@ -0,0 +1,99 @@
+I start to lose track of all the diverse ideas. This post is an effort to 
+structure the major directions. May it be incomplete, feel free to complete 
+it.
+
+In case there is a short...
+
+I. check whether we have history, since this way the qeustion is "what
+  user modification introduced the short" which might be more useful
+  than answer "which object(s) cause(s) the short at the moment".
+
+ 1. bisect using the undo buffer (as Kai-Martin Knaak does manually) -
+    does not work accross sessions (restart/load) and as Markus
+    Hitter pointed it out, fails when new netlist is loaded
+
+ 2. tag objects according to their first connection as suggested by Peter
+    Clifton. This info could be easily saved, making it immune to reload.
+    With further improvments by John Doty and Levente Kovacs with detailed
+    method described by Chris Smith.
+
+ 3. separate connection/netlist history (saved with the PCB). No details
+    yet. Might be the same as I/2.
+
+ 4. Markus Hitter's proposal of pinning down rat lines later converted to
+    to copper. (This is not strictly about keeping track of object
+    history, unless we consider pinned down rats as history of a later copper
+    trace)
+
+II. no history available, try to highlight objects that are most likely to
+    help the user resolving the short. Nodes of the graph include
+    junctions, thermals, etc., much more verbose then the netlist.
+
+ 1. propagate nets from all nodes as suggested by Peter Clifton. Doing
+    this in parallel may cause a collision close to the "real place
+    of the short"
+
+ 2. find a minimal cut in a way the resulting graphs will reflect the
+    netlist and highlight only those cutting edge. To the end user this
+    means we find the smallest modification (deletion) that would fix the
+    problem (with or without leaving new rat lines). Sounds like an NP
+    hard problem, no working solution has been proposed in the thread yet.
+ 
+ 3. Peter Clifton's remove-edges-and-see-how-that-improves-the-situation.
+    A good metric is needed to make sure we can measure small improvements
+    in cases where multiple edges must be removed to resolve the short.
+    Likely to select more edges than the minimum.
+
+ 4.
+   stage 1
+    classify nodes/edges: each belongs to one of the affected nets or is
+    neutral (could be in multiple nets or could be removed without
+    breaking only short, not legal redundant connection in a net). Assume
+    only neutral nodes/edges may participate in the short. Question is how
+    to do the classification properly:
+
+    a. A modified version of Peter Clifton's propagation idea might work,
+       needs more thoughts.
+    b. A similar problem may be known in graph theory; Finding Steiner
+       tree for a net and trying to fit our nodes/edges on it would keep
+       the minimal amount (or length) of objects to form the net properly,
+       and take the rest as neutral. This Breaks badly with redundant
+       connections in a net. Needs more work.
+
+   stage 2
+    from stage 1 we already have sections with multiple nodes/edges that
+    are neutral and can be blamed for the short. If the user breaks each
+    such section, the short is resolved.
+
+    a. highlight these sections and let the user break each wherever
+       (s)he wants (need a way to differentiate between sections)
+    b. try to find the best place to cut each section
+       A. middle of the section
+       B. smallest modification (however we measure that)
+       C. heat up the section with the modified verison of Joshua Lansford
+          idea; this may be used to highlight the shortest/smallest
+          object
+
+
+ 5. minimal cut (proposed by Britton Kerin) with the S->T modified
+    connection graph; creating the modified graph is trival and there
+    should be pseudo code and/or libraries available for calculating
+    minimal cut. With uniformly wieghted edges it could reliably find the
+    smallest amount of cuts resolving the short.
+
+ 6. pick a random path from a random pin/pad of net1 to another random
+    pin/pad of net2 and let the user resolve this short. Once this is done,
+    the process can be repeated until all shorts are eliminated. Proposed
+    by Russell Dill.
+
+III. manual net tagging
+
+ 1. Do not use I. or II., require(?) the user to manually select a net
+    for each object placed and highlight shorts, as proposed by John Doty
+    and Levente Kovacs.
+
+ 2. I/2 combined with III/1; by default it follows I/2 but the user can
+    chose to manually edit tags as in III/1.
+
+
+
diff --git a/src_plugins/mincut/pcb-mincut/solve.c b/src_plugins/mincut/pcb-mincut/solve.c
new file mode 100644
index 0000000..fa77455
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/solve.c
@@ -0,0 +1,254 @@
+/*    pcb-mincut, a prototype project demonstrating how to highlight shorts
+ *    Copyright (C) 2012 Tibor 'Igor2' Palinkas
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License along
+ *   with this program; if not, write to the Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "solve.h"
+#include "compat_misc.h"
+
+/* Karger's algorithm as described in the wikipedia article
+   http://en.wikipedia.org/wiki/Karger%27s_algorithm
+*/
+
+#define BAD 1000000
+
+/*#define DEBUG_MERGES*/
+/*#define DEBUG_TAGS*/
+/*#define DEBUG_SOLVE*/
+
+typedef struct {
+	gr_t *g;
+	int *avail;    /* nodes IDs still avaialble for merging */
+	int *neigh;    /* neighbor list */
+	int *tag;
+	int num_avail; /* number of nodes still avaialble for merging */
+} sstate_t;
+
+static int pick_del(sstate_t *st)
+{
+	int idx, ret, size;
+	idx = pcb_rand() % st->num_avail;
+	ret = st->avail[idx];
+	size = (st->num_avail-idx-1) * sizeof(int);
+	if (size > 0)
+		memmove(&st->avail[idx], &st->avail[idx+1], size);
+	st->num_avail--;
+	return ret;
+}
+
+static int pick_neigh(sstate_t *st, int node)
+{
+	int n, num_neigh;
+
+	num_neigh = 0;
+	for(n = 0; n < st->g->n; n++) {
+		if ((n != node) && (gr_get_(st->g, n, node) > 0)) {
+			st->neigh[num_neigh] = n;
+			num_neigh++;
+		}
+	}
+	return st->neigh[pcb_rand() % num_neigh];
+}
+
+static void retag(sstate_t *st, int from, int to)
+{
+	int n;
+	for(n = 0; n < st->g->n; n++)
+		if (st->tag[n] == from)
+			st->tag[n] = to;
+}
+
+
+/* clone graph and do a randon contraction */
+int solve_(gr_t *g_, int *cuts)
+{
+	sstate_t st;
+	int n, result, tags;
+	static int solution = -1;
+#ifdef DEBUG_MERGES
+	int cnt = 0;
+	char fn[512];
+#endif
+
+	solution++;
+	st.g = gr_clone(g_);
+	st.avail = malloc(sizeof(int) * st.g->n);
+	st.neigh = malloc(sizeof(int) * st.g->n);
+	st.tag = malloc(sizeof(int) * st.g->n);
+
+#define FREE_ALL() \
+	gr_free(st.g); \
+	free(st.avail); \
+	free(st.neigh); \
+	free(st.tag);
+
+	for(n = 2; n < st.g->n; n++)
+		st.tag[n] = -1;
+	st.tag[0] = 0;
+	st.tag[1] = 1;
+	tags = 2;
+
+	st.num_avail = 0;
+	for(n = 0; n < st.g->n; n++) {
+		if (gr_node_edges(st.g, n) > 0) {
+			st.avail[st.num_avail] = n;
+			st.num_avail++;
+		}
+	}
+
+	while(st.num_avail > 2) {
+		int n1, n2;
+		n2 = pick_del(&st);
+		n1 = pick_neigh(&st, n2);
+#ifndef DEBUG_MERGES
+#ifdef DEBUG_SOLVE
+			printf("Merge %d (%s) into %d (%s)\n", n2, st.g->node2name[n2], n1, st.g->node2name[n1]);
+#endif
+#endif
+		assert(n2 != n1);
+
+		/* propagate tags */
+		if ((st.tag[n1] != -1) && (st.tag[n2] == -1))
+			st.tag[n2] = st.tag[n1];
+		else if ((st.tag[n1] == -1) && (st.tag[n2] != -1))
+			st.tag[n1] = st.tag[n2];
+		else if ((st.tag[n1] == -1) && (st.tag[n2] == -1))
+			st.tag[n1] = st.tag[n2] = tags++;
+		else if ((st.tag[n1] != -1) && (st.tag[n2] != -1)) {
+			if ((st.tag[n1] > 1) && (st.tag[n2] <= 1))
+				retag(&st, st.tag[n1], st.tag[n2]);
+			else if ((st.tag[n2] > 1) && (st.tag[n1] <= 1))
+				retag(&st, st.tag[n2], st.tag[n1]);
+			else {
+				/* tag collision means we won't be able to distinguish between our
+				   two groups and our cut won't resolve the short anyway */
+#ifdef DEBUG_TAGS
+				printf("Tag collision!\n");
+#endif
+				FREE_ALL();
+				return BAD;
+			}
+		}
+
+		gr_merge_nodes(st.g, n1, n2);
+
+#ifdef DEBUG_MERGES
+			sprintf(fn, "contraction_%02d_%02d", solution, cnt);
+			cnt++;
+			gr_draw(st.g, fn, "png");
+			printf("Merge %d into %d, result in %s leaving %d available nodes\n", n2, n1, fn, st.num_avail);
+#endif
+	}
+
+#ifdef DEBUG_SOLVE
+	{
+		char fn[128];
+		sprintf(fn, "contraction_%02d", solution);
+		gr_draw(st.g, fn, "png");
+	}
+#endif
+
+	result = gr_get(st.g, st.avail[0], st.avail[1]);
+
+#ifdef DEBUG_TAGS
+	{
+		int t, n;
+		printf("Groups:\n");
+		for(t = 0; t < 2; t++) {
+			printf("  [%d] is", t);
+			for(n = 0; n < st.g->n; n++) {
+				if (st.tag[n] == t)
+					printf(" %s", st.g->node2name[n]);
+			}
+			printf("\n");
+		}
+	}
+#endif
+	{
+		int x, y, num_cuts = 0;
+		for(y = 0; y < st.g->n; y++)
+			for(x = y+1; x < st.g->n; x++)
+				if ((gr_get_(g_, x, y) > 0) && (st.tag[x] != st.tag[y])) {
+#ifdef DEBUG_TAGS
+					printf("CUT %s-%s\n", st.g->node2name[x], st.g->node2name[y]);
+#endif
+					cuts[num_cuts*2+0] = x;
+					cuts[num_cuts*2+1] = y;
+					num_cuts++;
+				}
+		cuts[num_cuts*2+0] = -1;
+		cuts[num_cuts*2+1] = -1;
+	}
+	FREE_ALL();
+	return result;
+}
+
+#define strempty(s) ((s) == NULL ? "" : (s))
+int *solve(gr_t *g)
+{
+	int n, best, res, till, cuts_size;
+	double nd;
+	int *cuts, *best_cuts;
+
+	/* count how many nodes we really have - the ones not cut down from the graph by the preprocessor */
+	nd = 0;
+	for(n = 0; n < g->n; n++)
+		if (gr_node_edges(g, n) > 0)
+			nd++;
+
+	till = (int)(nd * (nd-1.0) / 2.0 * log(nd))+1;
+#ifdef DEBUG_SOLVE
+	printf("Running solver at most %d times for %d relevant nodes\n", till, (int)nd);
+#endif
+
+	cuts_size = ((nd * nd) + 1) * sizeof(int);
+	cuts = malloc(cuts_size);
+	best_cuts = malloc(cuts_size);
+
+	best = BAD;
+	for(n = 0; n < till; n++) {
+		res = solve_(g, cuts);
+#ifdef DEBUG_SOLVE
+		printf("solution %d=%d\n", n, res);
+#endif
+		if (res < best) {
+			best = res;
+			memcpy(best_cuts, cuts, cuts_size);
+		}
+		if (best == 1) /* we won't find a better solution ever */
+			break;
+	}
+
+#ifdef DEBUG_SOLVE
+	printf("Best solution cuts %d edge%s:", best, best == 1 ? "" : "s");
+	for(n = 0; best_cuts[n*2] != -1; n++) {
+		printf(" %d:%s-%d:%s", best_cuts[n*2+0], strempty(g->node2name[best_cuts[n*2+0]]), best_cuts[n*2+1], strempty(g->node2name[best_cuts[n*2+1]]));
+	}
+	printf("\n");
+#endif
+	if (best == BAD) {
+		free(best_cuts);
+		free(cuts);
+		return NULL;
+	}
+	free(cuts);
+	return best_cuts;
+}
+
+
diff --git a/src_plugins/mincut/pcb-mincut/solve.h b/src_plugins/mincut/pcb-mincut/solve.h
new file mode 100644
index 0000000..bc5fbc5
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/solve.h
@@ -0,0 +1,5 @@
+#include "graph.h"
+
+/* returns a list of object ID pairs (each nth and n+1th element) terminated
+   by a -1;-1. Cutting these vertices would separate g. */
+int *solve(gr_t *g);
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/C.in b/src_plugins/mincut/pcb-mincut/test_cases/C.in
new file mode 100644
index 0000000..5648ab9
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/C.in
@@ -0,0 +1,9 @@
+# a---b
+# |
+# C---D
+
+net1 a b
+net2 C D
+conn a-b C-D
+conn a-C
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/C.ref b/src_plugins/mincut/pcb-mincut/test_cases/C.ref
new file mode 100644
index 0000000..ddd3d3b
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/C.ref
@@ -0,0 +1 @@
+C-a
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H.in b/src_plugins/mincut/pcb-mincut/test_cases/H.in
new file mode 100644
index 0000000..f7a05cf
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H.in
@@ -0,0 +1,8 @@
+# a-1-b
+#   |
+# C-2-D
+
+net1 a b
+net2 C D
+neut 1 2
+conn C-2 D-2 a-1 b-1 1-2
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H.ref b/src_plugins/mincut/pcb-mincut/test_cases/H.ref
new file mode 100644
index 0000000..d7cd238
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H.ref
@@ -0,0 +1 @@
+2-1
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H2.in b/src_plugins/mincut/pcb-mincut/test_cases/H2.in
new file mode 100644
index 0000000..26cf87a
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H2.in
@@ -0,0 +1,16 @@
+# a-1-2-3-4-5-b
+#       |
+#       6
+#       |
+#       7
+#       |
+# C-8-9-10-11-D
+
+net1 a b
+net2 C D
+neut 1 2 3 4 5 6 7 8 9 10 11
+conn a-1 1-2 2-3 3-4 4-5 5-b
+conn C-8 8-9 9-10 10-11 11-D
+conn 3-6 6-7 7-10
+
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H2.ref b/src_plugins/mincut/pcb-mincut/test_cases/H2.ref
new file mode 100644
index 0000000..b2c8a18
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H2.ref
@@ -0,0 +1 @@
+7-6
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H3.in b/src_plugins/mincut/pcb-mincut/test_cases/H3.in
new file mode 100644
index 0000000..dda6d33
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H3.in
@@ -0,0 +1,18 @@
+# a-1-2-3-4-5-b
+#       |
+#       6-+
+#       | |
+#       7-+
+#       |/
+# C-8-9-10-11-D
+
+net1 a b
+net2 C D
+neut 1 2 3 4 5 6 7 8 9 10 11
+conn a-1 1-2 2-3 3-4 4-5 5-b
+conn C-8 8-9 9-10 10-11 11-D
+conn 3-6 6-7 7-10
+
+#redundancy - cut 3-6 is the best:
+conn 6-7 7-10
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H3.ref b/src_plugins/mincut/pcb-mincut/test_cases/H3.ref
new file mode 100644
index 0000000..c587cba
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H3.ref
@@ -0,0 +1 @@
+6-3
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H4.in b/src_plugins/mincut/pcb-mincut/test_cases/H4.in
new file mode 100644
index 0000000..ee28425
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H4.in
@@ -0,0 +1,12 @@
+# a   b
+# |   |
+# 1---2
+# |   |
+# C   D
+
+net1 a b
+net2 C D
+neut 1 2
+conn a-1 b-2
+conn C-1 D-2
+conn 1-2
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/H4.ref b/src_plugins/mincut/pcb-mincut/test_cases/H4.ref
new file mode 100644
index 0000000..50c2b79
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/H4.ref
@@ -0,0 +1,2 @@
+1-C
+2-D
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/Makefile b/src_plugins/mincut/pcb-mincut/test_cases/Makefile
new file mode 100644
index 0000000..09f6400
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/Makefile
@@ -0,0 +1,12 @@
+TESTS = C.diff H.diff H2.diff H3.diff H4.diff O.diff \
+        rats1.diff rats2.diff rats3.diff
+
+all: $(TESTS)
+
+.SUFFIXES: .in .out .diff .ref
+
+.out.diff:
+	@diff -u $*.out $*.ref
+
+.in.out:
+	@../main < $*.in >$*.out
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/O.in b/src_plugins/mincut/pcb-mincut/test_cases/O.in
new file mode 100644
index 0000000..3ff344e
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/O.in
@@ -0,0 +1,9 @@
+# a---b
+# |   |
+# C---D
+
+net1 a b
+net2 C D
+conn a-b C-D
+conn a-C b-D
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/O.ref b/src_plugins/mincut/pcb-mincut/test_cases/O.ref
new file mode 100644
index 0000000..130f8aa
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/O.ref
@@ -0,0 +1,2 @@
+C-a
+D-b
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/rats1.in b/src_plugins/mincut/pcb-mincut/test_cases/rats1.in
new file mode 100644
index 0000000..54544d6
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/rats1.in
@@ -0,0 +1,12 @@
+# a   b
+# |   |
+# 1   2
+# |   |
+# C   D
+
+net1 a b
+net2 C D
+neut 1 2
+conn a-1 b-2
+conn C-1 D-2
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/rats1.ref b/src_plugins/mincut/pcb-mincut/test_cases/rats1.ref
new file mode 100644
index 0000000..50c2b79
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/rats1.ref
@@ -0,0 +1,2 @@
+1-C
+2-D
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/rats2.in b/src_plugins/mincut/pcb-mincut/test_cases/rats2.in
new file mode 100644
index 0000000..ed0135d
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/rats2.in
@@ -0,0 +1,8 @@
+# a---C---b---D
+
+net1 a b
+net2 C D
+conn a-C C-b b-D
+
+
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/rats2.ref b/src_plugins/mincut/pcb-mincut/test_cases/rats2.ref
new file mode 100644
index 0000000..8dc7e51
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/rats2.ref
@@ -0,0 +1,3 @@
+C-a
+C-b
+D-b
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/rats3.in b/src_plugins/mincut/pcb-mincut/test_cases/rats3.in
new file mode 100644
index 0000000..97f8526
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/rats3.in
@@ -0,0 +1,9 @@
+# C---a---b---D
+
+net1 a b
+net2 C D
+conn C-a a-b b-D
+
+
+
+
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/rats3.ref b/src_plugins/mincut/pcb-mincut/test_cases/rats3.ref
new file mode 100644
index 0000000..130f8aa
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/rats3.ref
@@ -0,0 +1,2 @@
+C-a
+D-b
diff --git a/src_plugins/mincut/pcb-mincut/test_cases/redundant.in b/src_plugins/mincut/pcb-mincut/test_cases/redundant.in
new file mode 100644
index 0000000..5ec9d07
--- /dev/null
+++ b/src_plugins/mincut/pcb-mincut/test_cases/redundant.in
@@ -0,0 +1,20 @@
+# 12----13---14
+# |           |
+# a-1-2-3-4-5-b
+#       |
+#       6-+
+#       | |
+#       7-+
+#       |
+# C-8-9-10-11-D
+
+net1 a b
+net2 C D
+neut 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+conn a-1 1-2 2-3 3-4 4-5 5-b
+conn C-8 8-9 9-10 10-11 11-D
+conn 3-6 6-7 7-10
+conn a-12 12-13 13-14 14-b
+conn 6-7
+
+
diff --git a/src_plugins/mincut/rats_mincut.c b/src_plugins/mincut/rats_mincut.c
new file mode 100644
index 0000000..bd64f91
--- /dev/null
+++ b/src_plugins/mincut/rats_mincut.c
@@ -0,0 +1,419 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2013..2015 Tibor 'Igor2' Palinkas
+ *
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "global.h"
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "plug_io.h"
+#include "find.h"
+#include "misc.h"
+#include "mymem.h"
+#include "polygon.h"
+#include "rats.h"
+#include "search.h"
+#include "set.h"
+#include "undo.h"
+#include "plugins.h"
+#include "compat_misc.h"
+
+#include "rats.h"
+#include "pcb-mincut/graph.h"
+#include "pcb-mincut/solve.h"
+
+#include "conf.h"
+#include "rats_mincut_conf.h"
+conf_mincut_t conf_mincut;
+
+static void debprintf(const char *fmt, ...) {}
+
+typedef struct short_conn_s short_conn_t;
+struct short_conn_s {
+	int gid;											/* id in the graph */
+	int from_type;
+/*	AnyObjectType *from;*/
+	int from_id;
+	int to_type;
+	int edges;										/* number of edges */
+	AnyObjectType *to;
+	found_conn_type_t type;
+	short_conn_t *next;
+};
+
+static short_conn_t *short_conns = NULL;
+static int num_short_conns = 0;
+static int short_conns_maxid = 0;
+
+static void proc_short_cb(int current_type, void *current_obj, int from_type, void *from_obj, found_conn_type_t type)
+{
+	AnyObjectType *curr = current_obj, *from = from_obj;
+	short_conn_t *s;
+
+	s = malloc(sizeof(short_conn_t));
+	if (from != NULL) {
+		s->from_type = from_type;
+		s->from_id = from->ID;
+	}
+	else {
+		s->from_type = 0;
+		s->from_id = -1;
+	}
+	s->to_type = current_type;
+	s->to = curr;
+	s->type = type;
+	s->edges = 0;
+	s->next = short_conns;
+	short_conns = s;
+	if (curr->ID > short_conns_maxid)
+		short_conns_maxid = curr->ID;
+	num_short_conns++;
+
+	debprintf(" found %d %d/%p type %d from %d\n", current_type, curr->ID, (void *)current_obj, type, from == NULL ? -1 : from->ID);
+}
+
+/* returns 0 on succes */
+static int proc_short(PinType * pin, PadType * pad, int ignore)
+{
+	find_callback_t old_cb;
+	Coord x, y;
+	short_conn_t *n, **lut_by_oid, **lut_by_gid, *next;
+	int gids;
+	gr_t *g;
+	void *S, *T;
+	int *solution;
+	int i, maxedges;
+	int bad_gr = 0;
+
+	if (!conf_mincut.plugins.mincut.enable)
+		return bad_gr;
+
+	/* only one should be set, but one must be set */
+	assert((pin != NULL) || (pad != NULL));
+	assert((pin == NULL) || (pad == NULL));
+
+	if (pin != NULL) {
+		debprintf("short on pin!\n");
+		SET_FLAG(PCB_FLAG_WARN, pin);
+		x = pin->X;
+		y = pin->Y;
+	}
+	else if (pad != NULL) {
+		debprintf("short on pad!\n");
+		SET_FLAG(PCB_FLAG_WARN, pad);
+		if (TEST_FLAG(PCB_FLAG_EDGE2, pad)) {
+			x = pad->Point2.X;
+			y = pad->Point2.Y;
+		}
+		else {
+			x = pad->Point1.X;
+			y = pad->Point1.Y;
+		}
+	}
+
+	/* run only if net is not ignored */
+	if (ignore)
+		return 0;
+
+	short_conns = NULL;
+	num_short_conns = 0;
+	short_conns_maxid = 0;
+
+	/* perform a search using PCB_FLAG_MINCUT, calling back proc_short_cb() with the connections */
+	old_cb = find_callback;
+	find_callback = proc_short_cb;
+	SaveFindFlag(PCB_FLAG_MINCUT);
+	LookupConnection(x, y, pcb_false, 1, PCB_FLAG_MINCUT);
+
+	debprintf("- alloced for %d\n", (short_conns_maxid + 1));
+	lut_by_oid = calloc(sizeof(short_conn_t *), (short_conns_maxid + 1));
+	lut_by_gid = calloc(sizeof(short_conn_t *), (num_short_conns + 3));
+
+	g = gr_alloc(num_short_conns + 2);
+
+	g->node2name = calloc(sizeof(char *), (num_short_conns + 2));
+
+	/* conn 0 is S and conn 1 is T and set up lookup arrays */
+	for (n = short_conns, gids = 2; n != NULL; n = n->next, gids++) {
+		char *s;
+		const char *typ;
+		ElementType *parent;
+		n->gid = gids;
+		debprintf(" {%d} found %d %d/%p type %d from %d\n", n->gid, n->to_type, n->to->ID, (void *)n->to, n->type, n->from_id);
+		lut_by_oid[n->to->ID] = n;
+		lut_by_gid[n->gid] = n;
+
+		s = malloc(256);
+		parent = NULL;
+		switch (n->to_type) {
+		case PCB_TYPE_PIN:
+			typ = "pin";
+			parent = ((PinType *) (n->to))->Element;
+			break;
+		case PCB_TYPE_VIA:
+			typ = "via";
+			parent = ((PinType *) (n->to))->Element;
+			break;
+		case PCB_TYPE_PAD:
+			typ = "pad";
+			parent = ((PadType *) (n->to))->Element;
+			break;
+		case PCB_TYPE_LINE:
+			typ = "line";
+			break;
+		default:
+			typ = "other";
+			break;
+		}
+		if (parent != NULL) {
+			TextType *name;
+			name = &parent->Name[1];
+			if ((name->TextString == NULL) || (*name->TextString == '\0'))
+				sprintf(s, "%s #%ld \\nof #%ld", typ, n->to->ID, parent->ID);
+			else
+				sprintf(s, "%s #%ld \\nof %s", typ, n->to->ID, name->TextString);
+		}
+		else
+			sprintf(s, "%s #%ld", typ, n->to->ID);
+		g->node2name[n->gid] = s;
+	}
+	g->node2name[0] = pcb_strdup("S");
+	g->node2name[1] = pcb_strdup("T");
+
+	/* calculate how many edges each node has and the max edge count */
+	maxedges = 0;
+	for (n = short_conns; n != NULL; n = n->next) {
+		short_conn_t *from;
+
+		n->edges++;
+		if (n->edges > maxedges)
+			maxedges = n->edges;
+
+		if (n->from_id >= 0) {
+			from = lut_by_oid[n->from_id];
+			if (from == NULL) {
+				/* no from means broken graph (multiple components) */
+				if (n->from_id >= 2) {	/* ID 0 and 1 are start/stop, there won't be from for them */
+					fprintf(stderr, "rats_mincut.c error: graph has multiple components, bug in find.c (n->from_id=%d)!\n", n->from_id);
+					bad_gr = 1;
+				}
+				continue;
+			}
+			from->edges++;
+			if (from->edges > maxedges)
+				maxedges = from->edges;
+		}
+	}
+
+
+	S = NULL;
+	T = NULL;
+	for (n = short_conns; n != NULL; n = n->next) {
+		void *spare;
+
+		spare = NULL;
+		if (n->to_type == PCB_TYPE_PIN)
+			spare = ((PinType *) n->to)->Spare;
+		if (n->to_type == PCB_TYPE_PAD)
+			spare = ((PadType *) n->to)->Spare;
+		if (spare != NULL) {
+			void *net = &(((LibraryMenuTypePtr) spare)->Name[2]);
+			debprintf(" net=%s\n", net);
+			if (S == NULL) {
+				debprintf(" -> became S\n");
+				S = net;
+			}
+			else if ((T == NULL) && (net != S)) {
+				debprintf(" -> became T\n");
+				T = net;
+			}
+
+			if (net == S)
+				gr_add_(g, n->gid, 0, 100000);
+			else if (net == T)
+				gr_add_(g, n->gid, 1, 100000);
+		}
+		/* if we have a from object, look it up and make a connection between the two gids */
+		if (n->from_id >= 0) {
+			int weight;
+			short_conn_t *from = lut_by_oid[n->from_id];
+
+			from = lut_by_oid[n->from_id];
+			/* weight: 1 for connections we can break, large value for connections we shall not break */
+			if ((n->type == FCT_COPPER) || (n->type == FCT_START)) {
+				/* connection to a pin/pad is slightly stronger than the
+				   strongest obj-obj conn; obj-obj conns are weaker at junctions where many
+				   objects connect */
+				if ((n->from_type == PCB_TYPE_PIN) || (n->from_type == PCB_TYPE_PAD) || (n->to_type == PCB_TYPE_PIN) || (n->to_type == PCB_TYPE_PAD))
+					weight = maxedges * 2 + 2;
+				else
+					weight = maxedges * 2 - n->edges - from->edges + 1;
+			}
+			else
+				weight = 10000;
+			if (from != NULL) {
+				gr_add_(g, n->gid, from->gid, weight);
+				debprintf(" CONN %d %d\n", n->gid, from->gid);
+			}
+		}
+	}
+
+/*#define MINCUT_DRAW*/
+#ifdef MINCUT_DRAW
+	{
+		static int drw = 0;
+		char gfn[256];
+		drw++;
+		sprintf(gfn, "A_%d_a", drw);
+		debprintf("gfn=%s\n", gfn);
+		gr_draw(g, gfn, "png");
+	}
+#endif
+
+	if (!bad_gr) {
+		solution = solve(g);
+
+		if (solution != NULL) {
+			debprintf("Would cut:\n");
+			for (i = 0; solution[i] != -1; i++) {
+				short_conn_t *s;
+				debprintf("%d:", i);
+				s = lut_by_gid[solution[i]];
+				debprintf("%d %p", solution[i], (void *)s);
+				if (s != NULL) {
+					SET_FLAG(PCB_FLAG_WARN, s->to);
+					debprintf("  -> %d", s->to->ID);
+				}
+				debprintf("\n");
+			}
+
+			free(solution);
+		}
+		else {
+			fprintf(stderr, "mincut didn't find a solution, falling back to the old warn\n");
+			bad_gr = 1;
+		}
+	}
+	free(lut_by_oid);
+	free(lut_by_gid);
+
+	for (n = short_conns; n != NULL; n = next) {
+		next = n->next;
+		free(n);
+	}
+
+
+	ResetFoundLinesAndPolygons(pcb_false);
+	ResetFoundPinsViasAndPads(pcb_false);
+	RestoreFindFlag();
+
+	find_callback = old_cb;
+	return bad_gr;
+}
+
+typedef struct pinpad_s pinpad_t;
+struct pinpad_s {
+	int ignore;										/* if 1, changed our mind, do not check */
+	PinType *pin;
+	PadType *pad;
+	const char *with_net;					/* the name of the net this pin/pad is in short with */
+	pinpad_t *next;
+};
+
+static pinpad_t *shorts = NULL;
+
+void rat_found_short(PinType * pin, PadType * pad, const char *with_net)
+{
+	pinpad_t *pp;
+	pp = malloc(sizeof(pinpad_t));
+	pp->ignore = 0;
+	pp->pin = pin;
+	pp->pad = pad;
+	pp->with_net = with_net;
+	pp->next = shorts;
+	shorts = pp;
+}
+
+void rat_proc_shorts(void)
+{
+	pinpad_t *n, *i, *next;
+	int bad_gr = 0;
+	for (n = shorts; n != NULL; n = next) {
+		next = n->next;
+
+		if (n->pin != NULL)
+			SET_FLAG(PCB_FLAG_WARN, n->pin);
+		if (n->pad != NULL)
+			SET_FLAG(PCB_FLAG_WARN, n->pad);
+
+
+		/* run only if net is not ignored */
+		if ((!bad_gr) && (proc_short(n->pin, n->pad, n->ignore) != 0)) {
+			fprintf(stderr, "Can't run mincut :(\n");
+			bad_gr = 1;
+		}
+
+		if (!bad_gr) {
+			/* check if the rest of the shorts affect the same nets - ignore them if so */
+			for (i = n->next; i != NULL; i = i->next) {
+				LibraryMenuType *spn, *spi;
+				spn = (n->pin != NULL) ? n->pin->Spare : n->pad->Spare;
+				spi = (i->pin != NULL) ? i->pin->Spare : i->pad->Spare;
+
+				if ((spn == NULL) || (spi == NULL))
+					continue;
+
+				/* can compare by pointer - names are never pcb_strdup()'d */
+				if ((&spn->Name[2] == i->with_net) || (&spi->Name[2] == n->with_net))
+					i->ignore = 1;
+			}
+		}
+		free(n);
+	}
+	shorts = NULL;
+}
+
+void hid_mincut_uninit(void)
+{
+	conf_unreg_fields("plugins/mincut/");
+}
+
+#include "stub_mincut.h"
+pcb_uninit_t hid_mincut_init(void)
+{
+	stub_rat_found_short = rat_found_short;
+	stub_rat_proc_shorts = rat_proc_shorts;
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_mincut, field,isarray,type_name,cpath,cname,desc,flags);
+#include "rats_mincut_conf_fields.h"
+	return hid_mincut_uninit;
+}
diff --git a/src_plugins/mincut/rats_mincut.h b/src_plugins/mincut/rats_mincut.h
new file mode 100644
index 0000000..748ea7f
--- /dev/null
+++ b/src_plugins/mincut/rats_mincut.h
@@ -0,0 +1,27 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2013..2015 Tibor 'Igor2' Palinkas
+ * 
+ *  This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
+ *  this module is also subject to the GNU GPL as described below
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+void rat_found_short(PinType * pin, PadType * pad, const char *with_net);
+void rat_proc_shorts(void);
diff --git a/src_plugins/mincut/rats_mincut_conf.h b/src_plugins/mincut/rats_mincut_conf.h
new file mode 100644
index 0000000..19f3f65
--- /dev/null
+++ b/src_plugins/mincut/rats_mincut_conf.h
@@ -0,0 +1,14 @@
+#ifndef PCB_MINCUT_CONF_H
+#define PCB_MINCUT_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct mincut {
+			CFT_BOOLEAN enable;         /* Enable calculating mincut on shorts (rats_mincut.c) when non-zero */
+		} mincut;
+	} plugins;
+} conf_mincut_t;
+
+#endif
diff --git a/src_plugins/oldactions/Makefile b/src_plugins/oldactions/Makefile
new file mode 100644
index 0000000..7b0bb22
--- /dev/null
+++ b/src_plugins/oldactions/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_oldactions
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/oldactions/Plug.tmpasm b/src_plugins/oldactions/Plug.tmpasm
new file mode 100644
index 0000000..32df9c0
--- /dev/null
+++ b/src_plugins/oldactions/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {oldactions}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/oldactions/oldactions.o @]
+
+switch /local/pcb/oldactions/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/oldactions/README b/src_plugins/oldactions/README
new file mode 100644
index 0000000..1d3065c
--- /dev/null
+++ b/src_plugins/oldactions/README
@@ -0,0 +1,9 @@
+Random collection of old/obsolete actions. Bell(): audible feedback;
+DumpLibrary(): print footprint library on stdout; a set of debug actions
+useful for writing pcb scripts: Debug(), DebugXY(), Return(). Old
+plugin actions to toggle or set settings that are now accessible via
+the unified config system (vendordrill, djopt)
+
+#state: works
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/oldactions/oldactions.c b/src_plugins/oldactions/oldactions.c
new file mode 100644
index 0000000..dd5b918
--- /dev/null
+++ b/src_plugins/oldactions/oldactions.c
@@ -0,0 +1,320 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "global.h"
+#include "conf.h"
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "plug_footprint.h"
+
+
+static void conf_toggle(conf_role_t role, const char *path)
+{
+	conf_native_t *n = conf_get_field(path);
+	if (n == NULL) {
+		Message(PCB_MSG_DEFAULT, "Error: can't find config node %s to toggle\n", path);
+		return;
+	}
+	if (n->type != CFN_BOOLEAN) {
+		Message(PCB_MSG_DEFAULT, "Error: config node %s is not a boolean, can't toggle\n", path);
+		return;
+	}
+
+	conf_set(role, path, -1, n->val.boolean[0] ? "0" : "1", POL_OVERWRITE);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static const char dumplibrary_syntax[] = "DumpLibrary()";
+
+static const char dumplibrary_help[] = "Display the entire contents of the libraries.";
+
+/* %start-doc actions DumpLibrary
+
+
+%end-doc */
+static void ind(int level)
+{
+	static char inds[] = "                                                                               ";
+
+	if (level > sizeof(inds)-1)
+		return;
+	inds[level] = '\0';
+	printf("%s", inds);
+	inds[level] = ' ';
+}
+
+static void dump_lib_any(int level, library_t *l);
+
+static void dump_lib_dir(int level, library_t *l)
+{
+	pcb_cardinal_t n;
+
+	ind(level);
+	printf("%s/\n", l->name);
+	for(n = 0; n < vtlib_len(&l->data.dir.children); n++)
+		dump_lib_any(level+1, l->data.dir.children.array+n);
+}
+
+static void dump_lib_fp(int level, library_t *l)
+{
+	ind(level);
+	printf("%s", l->name);
+	switch(l->data.fp.type) {
+		case PCB_FP_INVALID:      printf(" type(??)"); break;
+		case PCB_FP_DIR:          printf(" type(DIR)"); break;
+		case PCB_FP_FILE:         printf(" type(file)"); break;
+		case PCB_FP_PARAMETRIC:   printf(" type(parametric)"); break;
+	}
+	printf(" loc_info(%s)\n", l->data.fp.loc_info);
+}
+
+static void dump_lib_any(int level, library_t *l)
+{
+	switch(l->type) {
+		case LIB_INVALID:     printf("??\n"); break;
+		case LIB_DIR:         dump_lib_dir(level, l); break;
+		case LIB_FOOTPRINT:   dump_lib_fp(level, l); break;
+	}
+}
+
+
+static int ActionDumpLibrary(int argc, const char **argv, Coord x, Coord y)
+{
+	dump_lib_any(0, &library);
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * no operation, just for testing purposes
+ * syntax: Bell(volume)
+ */
+static const char bell_syntax[] = "Bell()";
+
+static const char bell_help[] = "Attempt to produce audible notification (e.g. beep the speaker).";
+
+static int ActionBell(int argc, const char **argv, Coord x, Coord y)
+{
+	gui->beep();
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char debug_syntax[] = "Debug(...)";
+
+static const char debug_help[] = "Debug action.";
+
+/* %start-doc actions Debug
+
+This action exists to help debug scripts; it simply prints all its
+arguments to stdout.
+
+%end-doc */
+
+static const char debugxy_syntax[] = "DebugXY(...)";
+
+static const char debugxy_help[] = "Debug action, with coordinates";
+
+/* %start-doc actions DebugXY
+
+Like @code{Debug}, but requires a coordinate.  If the user hasn't yet
+indicated a location on the board, the user will be prompted to click
+on one.
+
+%end-doc */
+
+static int Debug(int argc, const char **argv, Coord x, Coord y)
+{
+	int i;
+	printf("Debug:");
+	for (i = 0; i < argc; i++)
+		printf(" [%d] `%s'", i, argv[i]);
+	pcb_printf(" x,y %$mD\n", x, y);
+	return 0;
+}
+
+static const char return_syntax[] = "Return(0|1)";
+
+static const char return_help[] = "Simulate a passing or failing action.";
+
+/* %start-doc actions Return
+
+This is for testing.  If passed a 0, does nothing and succeeds.  If
+passed a 1, does nothing but pretends to fail.
+
+%end-doc */
+
+static int Return(int argc, const char **argv, Coord x, Coord y)
+{
+	return atoi(argv[0]);
+}
+
+
+static const char djopt_sao_syntax[] = "OptAutoOnly()";
+
+static const char djopt_sao_help[] = "Toggles the optimize-only-autorouted flag.";
+
+/* %start-doc actions OptAutoOnly
+
+The original purpose of the trace optimizer was to clean up the traces
+created by the various autorouters that have been used with PCB.  When
+a board has a mix of autorouted and carefully hand-routed traces, you
+don't normally want the optimizer to move your hand-routed traces.
+But, sometimes you do.  By default, the optimizer only optimizes
+autorouted traces.  This action toggles that setting, so that you can
+optimize hand-routed traces also.
+
+%end-doc */
+
+
+
+int djopt_set_auto_only(int argc, const char **argv, Coord x, Coord y)
+{
+	conf_toggle(CFR_DESIGN, "plugins/djopt/auto_only");
+	return 0;
+}
+
+/* ************************************************************ */
+
+static const char toggle_vendor_syntax[] = "ToggleVendor()";
+
+static const char toggle_vendor_help[] = "Toggles the state of automatic drill size mapping.";
+
+/* %start-doc actions ToggleVendor
+
+ at cindex vendor map
+ at cindex vendor drill table
+ at findex ToggleVendor()
+
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.  To enable drill
+mapping, a vendor lihata file containing a drill table must be
+loaded first.
+
+%end-doc */
+
+int ActionToggleVendor(int argc, const char **argv, Coord x, Coord y)
+{
+	conf_toggle(CFR_DESIGN, "plugins/vendor/enable");
+	return 0;
+}
+
+/* ************************************************************ */
+
+static const char enable_vendor_syntax[] = "EnableVendor()";
+
+static const char enable_vendor_help[] = "Enables automatic drill size mapping.";
+
+/* %start-doc actions EnableVendor
+
+ at cindex vendor map
+ at cindex vendor drill table
+ at findex EnableVendor()
+
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.  To enable drill
+mapping, a vendor lihata file containing a drill table must be
+loaded first.
+
+%end-doc */
+
+int ActionEnableVendor(int argc, const char **argv, Coord x, Coord y)
+{
+	conf_set(CFR_DESIGN, "plugins/vendor/enable", -1, "1", POL_OVERWRITE);
+	return 0;
+}
+
+/* ************************************************************ */
+
+static const char disable_vendor_syntax[] = "DisableVendor()";
+
+static const char disable_vendor_help[] = "Disables automatic drill size mapping.";
+
+/* %start-doc actions DisableVendor
+
+ at cindex vendor map
+ at cindex vendor drill table
+ at findex DisableVendor()
+
+When drill mapping is enabled, new instances of pins and vias will
+have their drill holes mapped to one of the allowed drill sizes
+specified in the currently loaded vendor drill table.
+
+%end-doc */
+
+int ActionDisableVendor(int argc, const char **argv, Coord x, Coord y)
+{
+	conf_set(CFR_DESIGN, "plugins/vendor/enable", -1, "0", POL_OVERWRITE);
+	return 0;
+}
+
+
+HID_Action oldactions_action_list[] = {
+	{"DumpLibrary", 0, ActionDumpLibrary,
+	 dumplibrary_help, dumplibrary_syntax},
+	{"Bell", 0, ActionBell,
+	 bell_help, bell_syntax},
+	{"Debug", 0, Debug,
+	 debug_help, debug_syntax},
+	{"DebugXY", "Click X,Y for Debug", Debug,
+	 debugxy_help, debugxy_syntax},
+	{"Return", 0, Return,
+	 return_help, return_syntax},
+	{"OptAutoOnly", 0, djopt_set_auto_only,
+	 djopt_sao_help, djopt_sao_syntax},
+	{"ToggleVendor", 0, ActionToggleVendor,
+	 toggle_vendor_help, toggle_vendor_syntax},
+	{"EnableVendor", 0, ActionEnableVendor,
+	 enable_vendor_help, enable_vendor_syntax},
+	{"DisableVendor", 0, ActionDisableVendor,
+	 disable_vendor_help, disable_vendor_syntax}
+};
+
+static const char *oldactions_cookie = "oldactions plugin";
+
+REGISTER_ACTIONS(oldactions_action_list, oldactions_cookie)
+
+static void hid_oldactions_uninit(void)
+{
+	hid_remove_actions_by_cookie(oldactions_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_oldactions_init(void)
+{
+	REGISTER_ACTIONS(oldactions_action_list, oldactions_cookie)
+	return hid_oldactions_uninit;
+}
diff --git a/src_plugins/plugins_ALL.tmpasm b/src_plugins/plugins_ALL.tmpasm
new file mode 100644
index 0000000..eff3287
--- /dev/null
+++ b/src_plugins/plugins_ALL.tmpasm
@@ -0,0 +1,4 @@
+# List of all plugins
+include {../src_plugins/plugins_io.tmpasm}
+include {../src_plugins/plugins_feature.tmpasm}
+include {../src_plugins/plugins_fp.tmpasm}
diff --git a/src_plugins/plugins_feature.tmpasm b/src_plugins/plugins_feature.tmpasm
new file mode 100644
index 0000000..f66d5dc
--- /dev/null
+++ b/src_plugins/plugins_feature.tmpasm
@@ -0,0 +1,33 @@
+# Feature plugins
+
+include {../src_plugins/lib_gensexpr/Plug.tmpasm}
+include {../src_plugins/lib_legacy_func/Plug.tmpasm}
+
+include {../src_plugins/autocrop/Plug.tmpasm}
+include {../src_plugins/autoplace/Plug.tmpasm}
+include {../src_plugins/autoroute/Plug.tmpasm}
+include {../src_plugins/boardflip/Plug.tmpasm}
+include {../src_plugins/distalign/Plug.tmpasm}
+include {../src_plugins/distaligntext/Plug.tmpasm}
+include {../src_plugins/jostle/Plug.tmpasm}
+include {../src_plugins/polycombine/Plug.tmpasm}
+include {../src_plugins/polystitch/Plug.tmpasm}
+include {../src_plugins/teardrops/Plug.tmpasm}
+include {../src_plugins/smartdisperse/Plug.tmpasm}
+include {../src_plugins/vendordrill/Plug.tmpasm}
+include {../src_plugins/puller/Plug.tmpasm}
+include {../src_plugins/djopt/Plug.tmpasm}
+include {../src_plugins/mincut/Plug.tmpasm}
+include {../src_plugins/gpmi/Plug.tmpasm}
+include {../src_plugins/toporouter/Plug.tmpasm}
+include {../src_plugins/oldactions/Plug.tmpasm}
+include {../src_plugins/fontmode/Plug.tmpasm}
+include {../src_plugins/shand_cmd/Plug.tmpasm}
+include {../src_plugins/renumber/Plug.tmpasm}
+include {../src_plugins/stroke/Plug.tmpasm}
+include {../src_plugins/report/Plug.tmpasm}
+include {../src_plugins/dbus/Plug.tmpasm}
+include {../src_plugins/propedit/Plug.tmpasm}
+include {../src_plugins/diag/Plug.tmpasm}
+include {../src_plugins/loghid/Plug.tmpasm}
+include {../src_plugins/query/Plug.tmpasm}
diff --git a/src_plugins/plugins_fp.tmpasm b/src_plugins/plugins_fp.tmpasm
new file mode 100644
index 0000000..3c581ad
--- /dev/null
+++ b/src_plugins/plugins_fp.tmpasm
@@ -0,0 +1,6 @@
+# footprint plugins
+# This has to be separate from any other class because gsch2pcb depends on it
+# while it doesn't want to link the rest.
+
+include {../src_plugins/fp_fs/Plug.tmpasm}
+include {../src_plugins/fp_wget/Plug.tmpasm}
diff --git a/src_plugins/plugins_io.tmpasm b/src_plugins/plugins_io.tmpasm
new file mode 100644
index 0000000..2e5aaf3
--- /dev/null
+++ b/src_plugins/plugins_io.tmpasm
@@ -0,0 +1,31 @@
+# I/O plugins: import, export, UI
+
+include {../src_plugins/import_edif/Plug.tmpasm}
+include {../src_plugins/import_netlist/Plug.tmpasm}
+include {../src_plugins/import_sch/Plug.tmpasm}
+include {../src_plugins/import_dsn/Plug.tmpasm}
+
+include {../src_plugins/export_ps/Plug.tmpasm}
+include {../src_plugins/export_lpr/Plug.tmpasm}
+include {../src_plugins/export_gcode/Plug.tmpasm}
+include {../src_plugins/export_nelma/Plug.tmpasm}
+include {../src_plugins/export_png/Plug.tmpasm}
+include {../src_plugins/export_gerber/Plug.tmpasm}
+include {../src_plugins/export_bom/Plug.tmpasm}
+include {../src_plugins/export_xy/Plug.tmpasm}
+include {../src_plugins/export_dxf/Plug.tmpasm}
+include {../src_plugins/export_test/Plug.tmpasm}
+include {../src_plugins/export_bboard/Plug.tmpasm}
+include {../src_plugins/export_openscad/Plug.tmpasm}
+include {../src_plugins/export_dsn/Plug.tmpasm}
+include {../src_plugins/export_ipcd356/Plug.tmpasm}
+include {../src_plugins/export_svg/Plug.tmpasm}
+
+include {../src_plugins/hid_batch/Plug.tmpasm}
+include {../src_plugins/hid_gtk/Plug.tmpasm}
+include {../src_plugins/hid_lesstif/Plug.tmpasm}
+
+include {../src_plugins/io_lihata/Plug.tmpasm}
+include {../src_plugins/io_pcb/Plug.tmpasm}
+include {../src_plugins/io_kicad_legacy/Plug.tmpasm}
+include {../src_plugins/io_kicad/Plug.tmpasm}
diff --git a/src_plugins/polycombine/Makefile b/src_plugins/polycombine/Makefile
new file mode 100644
index 0000000..ba21c31
--- /dev/null
+++ b/src_plugins/polycombine/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_polycombine
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/polycombine/Plug.tmpasm b/src_plugins/polycombine/Plug.tmpasm
new file mode 100644
index 0000000..b2a253a
--- /dev/null
+++ b/src_plugins/polycombine/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {polycombine}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/polycombine/polycombine.o @]
+
+switch /local/pcb/polycombine/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/polycombine/README b/src_plugins/polycombine/README
new file mode 100644
index 0000000..a05441a
--- /dev/null
+++ b/src_plugins/polycombine/README
@@ -0,0 +1,6 @@
+The selected polygons are combined together according to the ordering
+of their points.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/polycombine/polycombine.c b/src_plugins/polycombine/polycombine.c
new file mode 100644
index 0000000..be905ff
--- /dev/null
+++ b/src_plugins/polycombine/polycombine.c
@@ -0,0 +1,362 @@
+/*!
+ * \file polycombine.c
+ *
+ * \brief PolyCombine plug-in for PCB.
+ *
+ * \author Copyright (C) 2010 Peter Clifton <pcjc2 at cam.ac.uk>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * Usage: PolyCombine()
+ *
+ * The selected polygons are combined together according to the ordering
+ * of their points.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "macro.h"
+#include "create.h"
+#include "remove.h"
+#include "hid.h"
+#include "error.h"
+#include "rtree.h"
+#include "polygon.h"
+#include "polyarea.h"
+#include "assert.h"
+#include "strflags.h"
+#include "find.h"
+#include "misc.h"
+#include "draw.h"
+#include "undo.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+static POLYAREA *original_poly(PolygonType *p, pcb_bool *forward)
+{
+	PLINE *contour = NULL;
+	POLYAREA *np = NULL;
+	pcb_cardinal_t n;
+	Vector v;
+	int hole = 0;
+
+	*forward = pcb_true;
+
+	if ((np = poly_Create()) == NULL)
+		return NULL;
+
+	/* first make initial polygon contour */
+	for (n = 0; n < p->PointN; n++) {
+		/* No current contour? Make a new one starting at point */
+		/*   (or) Add point to existing contour */
+
+		v[0] = p->Points[n].X;
+		v[1] = p->Points[n].Y;
+		if (contour == NULL) {
+			if ((contour = poly_NewContour(v)) == NULL)
+				return NULL;
+		}
+		else {
+			poly_InclVertex(contour->head.prev, poly_CreateNode(v));
+		}
+
+		/* Is current point last in contour? If so process it. */
+		if (n == p->PointN - 1 || (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1)) {
+			poly_PreContour(contour, pcb_true);
+
+			/* Log the direction in which the outer contour was specified */
+			if (hole == 0)
+				*forward = (contour->Flags.orient == PLF_DIR);
+
+			/* make sure it is a positive contour (outer) or negative (hole) */
+			if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
+				poly_InvContour(contour);
+			assert(contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
+
+			poly_InclContour(np, contour);
+			contour = NULL;
+			assert(poly_Valid(np));
+
+			hole++;
+		}
+	}
+	return np;
+}
+
+typedef struct poly_tree poly_tree;
+
+struct poly_tree {
+	PolygonType *polygon;
+	pcb_bool forward;
+	POLYAREA *polyarea;
+	poly_tree *parent;
+	poly_tree *child;
+	poly_tree *prev;
+	poly_tree *next;
+};
+
+/*!
+ * <pre>
+ *                      ______
+ *  ___________________|_  P6 |             +P1 ____ +P6
+ * | P1                | |    |              |
+ * |   _____      ____ |_|____|             -P2 ____ -P4 ____ -P5
+ * |  |P2   |    |P5  |  |                   |
+ * |  | []  |    |____|  |                  +P3
+ * |  |  P3 |            |
+ * |  |_____|            |
+ * |                     |
+ * |          ___        |
+ * |         |P4 |       |
+ * |         |___|       |
+ * |                     |
+ * |_____________________|
+ *
+ * </pre>
+ * As we encounter each polygon, it gets a record. We need to check
+ * whether it contains any of the polygons existing in our tree. If
+ * it does, it will become the parent of them. (Check breadth first).
+ *
+ * When processing, work top down (breadth first), although if the
+ * contours can be assumed not to overlap, we can drill down in this
+ * order: P1, P2, P3, P4, P5, P6.
+ */
+static pcb_bool PolygonContainsPolygon(POLYAREA *outer, POLYAREA *inner)
+{
+/*  int contours_isect;*/
+	/* Should check outer contours don't intersect? */
+/*  contours_isect = Touching (outer, inner);*/
+	/* Cheat and assume simple single contour polygons for now */
+/*  return contours_isect ?
+           0 : poly_ContourInContour (outer->contours, inner->contours);*/
+	return poly_ContourInContour(outer->contours, inner->contours);
+}
+
+
+static poly_tree *insert_node_recursive(poly_tree * start_point, poly_tree * to_insert)
+{
+	poly_tree *cur_node, *next = NULL;
+/*  bool to_insert_isects_cur_node;    Intersection */
+	pcb_bool to_insert_contains_cur_node;	/* Containment */
+	pcb_bool cur_node_contains_to_insert;	/* Containment */
+	pcb_bool placed_to_insert = pcb_false;
+
+	poly_tree *return_root = start_point;
+
+	if (start_point == NULL) {
+/*      printf ("start_point is NULL, so returning to_insert\n");
+		to_insert->parent = !!; UNDEFINED*/
+		return to_insert;
+	}
+
+	/* Investigate the start point and its peers first */
+	for (cur_node = start_point; cur_node != NULL; cur_node = next) {
+		next = cur_node->next;
+
+/*      to_insert_isects_cur_node = IsPolygonInPolygon (to_insert->polygon, cur_node->polygon);*/
+		to_insert_contains_cur_node = PolygonContainsPolygon(to_insert->polyarea, cur_node->polyarea);
+
+#if 0
+		printf("Inspecting polygon %ld %s, curnode is %ld %s: to_insert_isects_cur_node = %d, to_insert_contains_cur_node = %i\n",
+					 to_insert->polygon->ID,
+					 to_insert->forward ? "FWD" : "BWD",
+					 cur_node->polygon->ID, cur_node->forward ? "FWD" : "BWD", to_insert_isects_cur_node, to_insert_contains_cur_node);
+
+		if (to_insert_isects_cur_node) {	/* Place as peer of this node? */
+		}
+#endif
+
+		if (to_insert_contains_cur_node) {	/* Should be a parent of this node */
+			/* Remove cur_node from its peers */
+			if (cur_node->prev)
+				cur_node->prev->next = cur_node->next;
+			if (cur_node->next)
+				cur_node->next->prev = cur_node->prev;
+
+			/* If we've not yet got a home, insert the to_insert node where cur_node was previously */
+			if (!placed_to_insert) {
+				to_insert->parent = cur_node->parent;
+				to_insert->next = cur_node->next;
+				to_insert->prev = cur_node->prev;
+				if (to_insert->prev)
+					to_insert->prev->next = to_insert;
+				if (to_insert->next)
+					to_insert->next->prev = to_insert;
+				placed_to_insert = pcb_true;
+
+				if (cur_node == start_point)
+					return_root = to_insert;
+			}
+
+			/* Prepend cur_node to our list of children */
+			cur_node->parent = to_insert;
+
+			cur_node->prev = NULL;
+			cur_node->next = to_insert->child;
+			if (to_insert->child)
+				to_insert->child->prev = cur_node;
+			to_insert->child = cur_node;
+
+		}
+	}
+
+	if (placed_to_insert) {
+/*      printf ("Returning new root %ld\n", return_root->polygon->ID);*/
+		return return_root;
+	}
+/*    return (to_insert->parent == NULL) ? to_insert : to_insert->parent;*/
+
+	/* Ok, so we still didn't find anywhere which the to_insert contour contained,
+	 * we need to start looking at the children of the start_point and its peers.
+	 */
+/*  printf ("Looking at child nodes of the start_point\n");*/
+
+	/* Investigate the start point and its peers first */
+	for (cur_node = start_point; cur_node != NULL; cur_node = next) {
+		next = cur_node->next;
+
+		cur_node_contains_to_insert = PolygonContainsPolygon(cur_node->polyarea, to_insert->polyarea);
+
+#if 0
+		printf("Inspecting polygon %ld, curnode is %ld: cur_node_contains_to_insert = %d\n",
+					 to_insert->polygon->ID, cur_node->polygon->ID, cur_node_contains_to_insert);
+#endif
+
+		/* Need to look at the child, ONLY if we know it engulfs our to_insert polygon */
+		if (cur_node_contains_to_insert) {
+			/* Can't set the parent within the call, so: */
+			to_insert->parent = cur_node;
+			cur_node->child = insert_node_recursive(cur_node->child, to_insert);
+			return start_point;
+			/*return cur_node->parent;*/
+		}
+	}
+
+/*  if (!placed_to_insert)*/
+	/* prepend to_insert polygon to peer of start_point ? */
+/*  printf ("Prepending as peer to start_poly\n");*/
+	to_insert->parent = start_point->parent;
+	to_insert->prev = NULL;
+	to_insert->next = start_point;
+	to_insert->next->prev = to_insert;
+
+	return to_insert;
+}
+
+static POLYAREA *compute_polygon_recursive(poly_tree * root, POLYAREA * accumulate)
+{
+	POLYAREA *res;
+	poly_tree *cur_node;
+	for (cur_node = root; cur_node != NULL; cur_node = cur_node->next) {
+		/* Process this element */
+/*      printf ("Processing node %ld %s\n", cur_node->polygon->ID, cur_node->forward ? "FWD" : "BWD");*/
+		poly_Boolean_free(accumulate, cur_node->polyarea, &res, cur_node->forward ? PBO_UNITE : PBO_SUB);
+		accumulate = res;
+
+		/* And its children if it has them */
+		if (cur_node->child) {
+/*          printf ("Processing children\n");*/
+			accumulate = compute_polygon_recursive(cur_node->child, accumulate);
+		}
+	}
+	return accumulate;
+}
+
+static int polycombine(int argc, const char **argv, Coord x, Coord y)
+{
+	POLYAREA *res;
+	pcb_bool forward;
+	POLYAREA *np;
+/*  bool outer;
+    POLYAREA *pa;
+    PLINE *pline;
+    VNODE *node;
+    PolygonType *Polygon;*/
+	LayerType *Layer = NULL;
+	poly_tree *root = NULL;
+	poly_tree *this_node;
+
+	/* First pass to combine the forward and backward contours */
+	VISIBLEPOLYGON_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, polygon))
+			continue;
+
+		/* Pick the layer of the first polygon we find selected */
+		if (Layer == NULL)
+			Layer = layer;
+
+		/* Only combine polygons on the same layer */
+		if (Layer != layer)
+			continue;
+
+		np = original_poly(polygon, &forward);
+
+		/* Build a poly_tree record */
+		this_node = calloc(1, sizeof(poly_tree));
+		this_node->polygon = polygon;
+		this_node->forward = forward;
+		this_node->polyarea = np;
+
+		/* Check where we should place the node in the tree */
+		root = insert_node_recursive(root, this_node);
+
+		/*RemovePolygon (layer, polygon);*/
+	}
+	ENDALL_LOOP;
+
+	/* Now perform a traversal of the tree, computing a polygon */
+	res = compute_polygon_recursive(root, NULL);
+
+	SaveUndoSerialNumber();
+
+	/* Second pass to remove the input polygons */
+	VISIBLEPOLYGON_LOOP(PCB->Data);
+	{
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, polygon))
+			continue;
+
+		/* Only combine polygons on the same layer */
+		if (Layer != layer)
+			continue;
+
+		RemovePolygon(layer, polygon);
+	}
+	ENDALL_LOOP;
+
+	/* Now de-construct the resulting polygon into raw PCB polygons */
+	PolyToPolygonsOnLayer(PCB->Data, Layer, res, string_to_pcbflags("clearpoly", NULL));
+	RestoreUndoSerialNumber();
+	IncrementUndoSerialNumber();
+	Draw();
+
+	return 0;
+}
+
+static HID_Action polycombine_action_list[] = {
+	{"PolyCombine", "???", polycombine,
+	 NULL, NULL}
+};
+
+char *polycombine_cookie = "polycombine plugin";
+
+REGISTER_ACTIONS(polycombine_action_list, polycombine_cookie)
+
+static void hid_polycombine_uninit(void)
+{
+	hid_remove_actions_by_cookie(polycombine_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_polycombine_init()
+{
+	REGISTER_ACTIONS(polycombine_action_list, polycombine_cookie);
+	return hid_polycombine_uninit;
+}
diff --git a/src_plugins/polystitch/Makefile b/src_plugins/polystitch/Makefile
new file mode 100644
index 0000000..051fc9b
--- /dev/null
+++ b/src_plugins/polystitch/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_polystitch
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/polystitch/Plug.tmpasm b/src_plugins/polystitch/Plug.tmpasm
new file mode 100644
index 0000000..276370c
--- /dev/null
+++ b/src_plugins/polystitch/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {polystitch}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/polystitch/polystitch.o @]
+
+switch /local/pcb/polystitch/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/polystitch/README b/src_plugins/polystitch/README
new file mode 100644
index 0000000..6887c0d
--- /dev/null
+++ b/src_plugins/polystitch/README
@@ -0,0 +1,8 @@
+The polygon under the cursor (based on closest-corner) is stitched
+together with the polygon surrounding it on the same layer.
+Use with pstoedit conversions where there's a "hole" in the shape -
+select the hole.
+
+#state: segfaults
+#default: disable
+#implements: (feature)
diff --git a/src_plugins/polystitch/polystitch.c b/src_plugins/polystitch/polystitch.c
new file mode 100644
index 0000000..44906e1
--- /dev/null
+++ b/src_plugins/polystitch/polystitch.c
@@ -0,0 +1,262 @@
+/*!
+ * \file polystitch.c
+ *
+ * \brief PolyStitch plug-in for PCB.
+ *
+ * \author Copyright (C) 2010 DJ Delorie <dj at delorie.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public 
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * Original source: http://www.delorie.com/pcb/polystitch.c
+ *
+ * Usage: PolyStitch()
+ *
+ * The polygon under the cursor (based on closest-corner) is stitched
+ * together with the polygon surrounding it on the same layer.
+ * Use with pstoedit conversions where there's a "hole" in the shape -
+ * select the hole.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "macro.h"
+#include "create.h"
+#include "remove.h"
+#include "hid.h"
+#include "error.h"
+#include "rtree.h"
+#include "draw.h"
+#include "set.h"
+#include "polygon.h"
+#include "misc.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+static PolygonType *inner_poly, *outer_poly;
+static LayerType *poly_layer;
+
+static double ATAN2(PointType a, PointType b)
+{
+	if (a.X == b.X && a.Y == b.Y)
+		return 0;
+	return atan2((double) b.Y - a.Y, (double) b.X - a.X);
+}
+
+static double poly_winding(PolygonType * poly)
+{
+	double winding, turn;
+	double prev_angle, this_angle;
+	int i, n;
+
+	winding = 0;
+
+	prev_angle = ATAN2(poly->Points[0], poly->Points[1]);
+	n = poly->PointN;
+	for (i = 1; i <= n; i++) {
+		this_angle = ATAN2(poly->Points[i % n], poly->Points[(i + 1) % n]);
+		turn = this_angle - prev_angle;
+		if (turn < -M_PI)
+			turn += 2 * M_PI;
+		if (turn > M_PI)
+			turn -= 2 * M_PI;
+		winding += turn;
+		prev_angle = this_angle;
+	}
+	return winding;
+}
+
+/*!
+ * \brief Given the X,Y, find the polygon and set inner_poly and
+ * poly_layer.
+ */
+static void find_crosshair_poly(int x, int y)
+{
+	double best = 0, dist;
+
+	inner_poly = NULL;
+	poly_layer = NULL;
+
+	VISIBLEPOLYGON_LOOP(PCB->Data);
+	{
+		/* layer, polygon */
+		POLYGONPOINT_LOOP(polygon);
+		{
+			/* point */
+			int dx = x - point->X;
+			int dy = y - point->Y;
+			dist = (double) dx *dx + (double) dy *dy;
+			if (dist < best || inner_poly == NULL) {
+				inner_poly = polygon;
+				poly_layer = layer;
+				best = dist;
+			}
+		}
+		END_LOOP;
+	}
+	ENDALL_LOOP;
+	if (!inner_poly) {
+		Message(PCB_MSG_ERROR, "Cannot find any polygons");
+		return;
+	}
+}
+
+/*!
+ * \brief Set outer_poly to the enclosing poly. We assume there's only
+ * one.
+ */
+static void find_enclosing_poly()
+{
+	outer_poly = NULL;
+
+	POLYGON_LOOP(poly_layer);
+	{
+		if (polygon == inner_poly)
+			continue;
+		if (polygon->BoundingBox.X1 <= inner_poly->BoundingBox.X1
+				&& polygon->BoundingBox.X2 >= inner_poly->BoundingBox.X2
+				&& polygon->BoundingBox.Y1 <= inner_poly->BoundingBox.Y1 && polygon->BoundingBox.Y2 >= inner_poly->BoundingBox.Y2) {
+			outer_poly = polygon;
+			return;
+		}
+	}
+	END_LOOP;
+	Message(PCB_MSG_ERROR, "Cannot find a polygon enclosing the one you selected");
+}
+
+static void check_windings()
+{
+	double iw, ow;
+	int i, j;
+
+	iw = poly_winding(inner_poly);
+	ow = poly_winding(outer_poly);
+	if (iw * ow > 0) {
+		/* Wound in same direction, must reverse one.  */
+		for (i = 0, j = inner_poly->PointN - 1; i < j; i++, j--) {
+			PointType x = inner_poly->Points[i];
+			inner_poly->Points[i] = inner_poly->Points[j];
+			inner_poly->Points[j] = x;
+		}
+	}
+}
+
+/*!
+ * \brief Rotate the polygon point list around so that point N is the
+ * first one in the list.
+ */
+static void rotate_points(PolygonType * poly, int n)
+{
+	PointType *np;
+	int n2 = poly->PointN - n;
+
+	np = (PointType *) malloc(poly->PointN * sizeof(PointType));
+	memcpy(np, poly->Points + n, n2 * sizeof(PointType));
+	memcpy(np + n2, poly->Points, n * sizeof(PointType));
+	memcpy(poly->Points, np, poly->PointN * sizeof(PointType));
+	free(np);
+}
+
+/*!
+ * \brief Make sure the first and last point of the polygon are the same
+ * point, so we can stitch them properly.
+ */
+static void dup_endpoints(PolygonType * poly)
+{
+	int n = poly->PointN;
+	if (poly->Points[0].X == poly->Points[n - 1].X && poly->Points[0].Y == poly->Points[n - 1].Y)
+		return;
+	CreateNewPointInPolygon(poly, poly->Points[0].X, poly->Points[0].Y);
+}
+
+/*!
+ * \brief Find the two closest points between those polygons, and
+ * connect them. We assume pstoedit winds the two polygons in opposite
+ * directions.
+ */
+static void stitch_them()
+{
+	int i, o;
+	int ii, oo;
+	double best = -1, dist;
+
+	ErasePolygon(inner_poly);
+	ErasePolygon(outer_poly);
+
+	/* This is O(n^2) but there's not a lot we can do about that.  */
+	for (i = 0; i < inner_poly->PointN; i++)
+		for (o = 0; o < outer_poly->PointN; o++) {
+			int dx = inner_poly->Points[i].X - outer_poly->Points[o].X;
+			int dy = inner_poly->Points[i].Y - outer_poly->Points[o].Y;
+			dist = (double) dx *dx + (double) dy *dy;
+			if (dist < best || best < 0) {
+				ii = i;
+				oo = o;
+				best = dist;
+			}
+		}
+	if (ii != 0)
+		rotate_points(inner_poly, ii);
+	if (oo != 0)
+		rotate_points(outer_poly, oo);
+	dup_endpoints(inner_poly);
+	dup_endpoints(outer_poly);
+
+	r_delete_entry(poly_layer->polygon_tree, (BoxType *) inner_poly);
+	r_delete_entry(poly_layer->polygon_tree, (BoxType *) outer_poly);
+
+	for (i = 0; i < inner_poly->PointN; i++)
+		CreateNewPointInPolygon(outer_poly, inner_poly->Points[i].X, inner_poly->Points[i].Y);
+
+	SetChangedFlag(pcb_true);
+
+	outer_poly->NoHolesValid = 0;
+	SetPolygonBoundingBox(outer_poly);
+	r_insert_entry(poly_layer->polygon_tree, (BoxType *) outer_poly, 0);
+	RemoveExcessPolygonPoints(poly_layer, outer_poly);
+	InitClip(PCB->Data, poly_layer, outer_poly);
+	DrawPolygon(poly_layer, outer_poly);
+	Draw();
+
+	RemovePolygon(poly_layer, inner_poly);
+}
+
+static int polystitch(int argc, const char **argv, Coord x, Coord y)
+{
+	find_crosshair_poly(x, y);
+	if (inner_poly) {
+		find_enclosing_poly();
+		if (outer_poly) {
+			check_windings();
+			stitch_them();
+		}
+	}
+	return 0;
+}
+
+static HID_Action polystitch_action_list[] = {
+	{"PolyStitch", "Select a corner on the inner polygon", polystitch,
+	 NULL, NULL}
+};
+
+char *polystitch_cookie = "polystitch plugin";
+
+REGISTER_ACTIONS(polystitch_action_list, polystitch_cookie)
+
+static void hid_polystitch_uninit(void)
+{
+	hid_remove_actions_by_cookie(polystitch_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_polystitch_init()
+{
+	REGISTER_ACTIONS(polystitch_action_list, polystitch_cookie);
+	return hid_polystitch_uninit;
+}
diff --git a/src_plugins/propedit/Makefile b/src_plugins/propedit/Makefile
new file mode 100644
index 0000000..55f3aa9
--- /dev/null
+++ b/src_plugins/propedit/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_propedit
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/propedit/Plug.tmpasm b/src_plugins/propedit/Plug.tmpasm
new file mode 100644
index 0000000..47c31c9
--- /dev/null
+++ b/src_plugins/propedit/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {propedit}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/propedit/propedit.o $(PLUGDIR)/propedit/props.o $(PLUGDIR)/propedit/propsel.o @]
+
+switch /local/pcb/propedit/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/propedit/README b/src_plugins/propedit/README
new file mode 100644
index 0000000..f1bf403
--- /dev/null
+++ b/src_plugins/propedit/README
@@ -0,0 +1,5 @@
+List and edit properties of a group of objects.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/propedit/propedit.c b/src_plugins/propedit/propedit.c
new file mode 100644
index 0000000..e6222dd
--- /dev/null
+++ b/src_plugins/propedit/propedit.c
@@ -0,0 +1,93 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include "plugins.h"
+#include "props.h"
+#include "propsel.h"
+#include "hid_actions.h"
+#include "pcb-printf.h"
+#include "error.h"
+
+/* ************************************************************ */
+
+#warning TODO
+static const char propedit_syntax[] = "propedit()";
+static const char propedit_help[] = "Run the property editor";
+int propedit_action(int argc, const char **argv, Coord x, Coord y)
+{
+	pe_ctx_t ctx;
+	htsp_entry_t *pe;
+
+	if ((gui == NULL) || (gui->propedit_start == NULL)) {
+		Message(PCB_MSG_DEFAULT, "Error: there's no GUI or the active GUI can't edit properties.\n");
+		return 1;
+	}
+
+	ctx.core_props = pcb_props_init();
+	pcb_propsel_map_core(ctx.core_props);
+
+	gui->propedit_start(&ctx, ctx.core_props->fill, propedit_query);
+	for (pe = htsp_first(ctx.core_props); pe; pe = htsp_next(ctx.core_props, pe))
+		propedit_ins_prop(&ctx, pe);
+
+	gui->propedit_end(&ctx);
+	pcb_props_uninit(ctx.core_props);
+	return 0;
+}
+
+static const char propset_syntax[] = "propset(name, value)";
+static const char propset_help[] = "Change the named property of all selected objects to/by value";
+int propset_action(int argc, const char **argv, Coord x, Coord y)
+{
+	int res;
+/*
+	if (argc != 2)
+*/
+	printf("argc=%d '%s'\n", argc, argv[0]);
+	res = pcb_propsel_set(argv[0], argv[1]);
+	printf("res=%d\n", res);
+	return 0;
+}
+
+static const char *propedit_cookie = "propedit";
+
+HID_Action propedit_action_list[] = {
+	{"propedit", 0, propedit_action,
+	 propedit_help, propedit_syntax},
+	{"propset", 0, propset_action,
+	 propset_help, propset_syntax},
+};
+
+REGISTER_ACTIONS(propedit_action_list, propedit_cookie)
+
+static void hid_propedit_uninit(void)
+{
+	hid_remove_actions_by_cookie(propedit_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_propedit_init(void)
+{
+	REGISTER_ACTIONS(propedit_action_list, propedit_cookie)
+	return hid_propedit_uninit;
+}
diff --git a/src_plugins/propedit/props.c b/src_plugins/propedit/props.c
new file mode 100644
index 0000000..4ce2c17
--- /dev/null
+++ b/src_plugins/propedit/props.c
@@ -0,0 +1,319 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include "props.h"
+#include "propsel.h"
+#include "compat_misc.h"
+#include "pcb-printf.h"
+/*#define HT_INVALID_VALUE ((pcb_propval_t){PCB_PROPT_invalid, {0}})*/
+#define HT(x) htprop_ ## x
+#include <genht/ht.c>
+#undef HT
+
+/* Type names in text */
+static const char *type_names[] = { "invalid", "string", "coord", "angle", "int" };
+
+/* A hash function for each known type */
+static unsigned int prophash_coord(pcb_propval_t key)  { return longhash(key.coord); }
+static unsigned int prophash_angle(pcb_propval_t key)  { return longhash(key.angle); }
+static unsigned int prophash_int(pcb_propval_t key)    { return longhash(key.i); }
+static unsigned int prophash_string(pcb_propval_t key) { return key.string == NULL ? 0 : strhash(key.string); }
+
+typedef unsigned int (*prophash_ft)(pcb_propval_t key);
+static prophash_ft prophash[PCB_PROPT_max] = {
+	NULL, prophash_string, prophash_coord, prophash_angle, prophash_int
+};
+
+/* A keyeq function for each known type */
+static int propkeyeq_coord(pcb_propval_t a, pcb_propval_t b)  { return a.coord == b.coord; }
+static int propkeyeq_angle(pcb_propval_t a, pcb_propval_t b)  { return a.angle == b.angle; }
+static int propkeyeq_int(pcb_propval_t a, pcb_propval_t b)    { return a.i == b.i; }
+static int propkeyeq_string(pcb_propval_t a, pcb_propval_t b)
+{
+	if ((b.string == NULL) && (a.string == NULL))
+		return 1;
+	if ((b.string == NULL) || (a.string == NULL))
+		return 0;
+	return (strcmp(a.string, b.string) == 0);
+}
+
+
+typedef int (*propkeyeq_ft)(pcb_propval_t a, pcb_propval_t b);
+static propkeyeq_ft propkeyeq[PCB_PROPT_max] = {
+	NULL, propkeyeq_string, propkeyeq_coord, propkeyeq_angle, propkeyeq_int
+};
+
+
+/* Init & uninit */
+htsp_t *pcb_props_init(void)
+{
+	return htsp_alloc(strhash, strkeyeq);
+}
+
+void pcb_props_uninit(htsp_t *props)
+{
+#warning TODO
+}
+
+/* Retrieve values for a prop */
+pcb_props_t *pcb_props_get(htsp_t *props, const char *propname)
+{
+	return htsp_get(props, propname);
+}
+
+/* Store a new value */
+pcb_props_t *pcb_props_add(htsp_t *props, const char *propname, pcb_prop_type_t type, pcb_propval_t val)
+{
+	pcb_props_t *p;
+	htprop_entry_t *e;
+
+	if ((type <= PCB_PROPT_invalid) || (type >= PCB_PROPT_max))
+		return NULL;
+
+	/* look up or create the value list (p) associated with the property name */
+	p = htsp_get(props, propname);
+	if (p == NULL) {
+		p = malloc(sizeof(pcb_props_t));
+		p->type = type;
+		htprop_init(&p->values, prophash[type], propkeyeq[type]);
+		htsp_set(props, pcb_strdup(propname), p);
+	}
+	else {
+		if (type != p->type)
+			return NULL;
+	}
+
+	/* Check if we already have this value */
+	e = htprop_getentry(&p->values, val);
+	if (e == NULL)
+		htprop_set(&p->values, val, 1);
+	else
+		e->value++;
+
+	return p;
+}
+
+
+const char *pcb_props_type_name(pcb_prop_type_t type)
+{
+	if (((int)type < 0) || (type >= PCB_PROPT_max))
+		return NULL;
+
+	return type_names[type];
+}
+
+#define STAT(val, field, cnt) \
+do { \
+	if (val.field < minp.field) minp = val; \
+	if (val.field > maxp.field) maxp = val; \
+	avgp.field += val.field * cnt; \
+} while(0)
+
+pcb_props_t *pcb_props_stat(htsp_t *props, const char *propname, pcb_propval_t *most_common, pcb_propval_t *min, pcb_propval_t *max, pcb_propval_t *avg)
+{
+	pcb_props_t *p;
+	htprop_entry_t *e;
+	pcb_propval_t bestp, minp, maxp, avgp;
+	unsigned long best = 0, num_vals = 0;
+
+	if (most_common != NULL)
+		most_common->string = NULL;
+	if (min != NULL)
+		min->string = NULL;
+	if (max != NULL)
+		max->string = NULL;
+	if (avg != NULL)
+		avg->string = NULL;
+
+	p = htsp_get(props, propname);
+	if (p == NULL)
+		return NULL;
+
+	if ((p->type == PCB_PROPT_STRING) && ((min != NULL) || (max != NULL) || (avg != NULL)))
+		return NULL;
+
+	/* set up internal avg, min, max */
+	memset(&avgp, 0, sizeof(avgp));
+	switch(p->type) {
+		case PCB_PROPT_invalid: break;
+		case PCB_PROPT_max: break;
+		case PCB_PROPT_STRING: break;
+		case PCB_PROPT_COORD:  minp.coord = COORD_MAX; maxp.coord = -minp.coord;  break;
+		case PCB_PROPT_ANGLE:  minp.angle = 100000;    maxp.angle = -minp.angle; break;
+		case PCB_PROPT_INT:    minp.i     = INT_MAX;   maxp.i = -minp.i; break;
+	}
+
+	/* walk through all known values */
+	for (e = htprop_first(&p->values); e; e = htprop_next(&p->values, e)) {
+		if (e->value > best) {
+			best = e->value;
+			bestp = e->key;
+		}
+		num_vals += e->value;
+		switch(p->type) {
+			case PCB_PROPT_invalid: break;
+			case PCB_PROPT_max: break;
+			case PCB_PROPT_STRING: break;
+			case PCB_PROPT_COORD:  STAT(e->key, coord, e->value); break;
+			case PCB_PROPT_ANGLE:  STAT(e->key, angle, e->value); break;
+			case PCB_PROPT_INT:    STAT(e->key, i,     e->value); break;
+		}
+	}
+
+	/* generate the result */
+	if (num_vals != 0) {
+		switch(p->type) {
+			case PCB_PROPT_invalid: break;
+			case PCB_PROPT_max: break;
+			case PCB_PROPT_STRING: break;
+			case PCB_PROPT_COORD:  avgp.coord = avgp.coord/num_vals;  break;
+			case PCB_PROPT_ANGLE:  avgp.angle = avgp.angle/num_vals;  break;
+			case PCB_PROPT_INT:    avgp.i     = avgp.i/num_vals;  break;
+		}
+		if (avg != NULL) *avg = avgp;
+		if (min != NULL) *min = minp;
+		if (max != NULL) *max = maxp;
+		if (most_common != NULL) *most_common = bestp;
+	}
+
+	return p;
+}
+
+#undef STAT
+
+static char buff[8][128];
+static int buff_idx = 0;
+const char *propedit_sprint_val(pcb_prop_type_t type, pcb_propval_t val)
+{
+	char *b;
+	buff_idx++;
+	if (buff_idx > 7)
+		buff_idx = 0;
+	b = buff[buff_idx];
+	switch(type) {
+		case PCB_PROPT_STRING: return val.string; break;
+		case PCB_PROPT_COORD:  pcb_snprintf(b, 128, "%.06$mm\n%.06$ml", val.coord, val.coord); break;
+		case PCB_PROPT_ANGLE:  sprintf(b, "%.2f", val.angle); break;
+		case PCB_PROPT_INT:    sprintf(b, "%d", val.i); break;
+		default: strcpy(b, "<unknown type>");
+	}
+	return b;
+}
+
+/*****************************************************************************/
+
+void propedit_ins_prop(pe_ctx_t *ctx, htsp_entry_t *pe)
+{
+	htprop_entry_t *e;
+	void *rowid;
+	pcb_props_t *p = pe->value;
+	pcb_propval_t common, min, max, avg;
+
+	if (gui->propedit_add_prop != NULL)
+		rowid = gui->propedit_add_prop(ctx, pe->key, 1, p->values.fill);
+
+	if (gui->propedit_add_stat != NULL) {
+		if (p->type == PCB_PROPT_STRING) {
+			pcb_props_stat(ctx->core_props, pe->key, &common, NULL, NULL, NULL);
+			gui->propedit_add_stat(ctx, pe->key, rowid, propedit_sprint_val(p->type, common), NULL, NULL, NULL);
+		}
+		else {
+			pcb_props_stat(ctx->core_props, pe->key, &common, &min, &max, &avg);
+			gui->propedit_add_stat(ctx, pe->key, rowid, propedit_sprint_val(p->type, common), propedit_sprint_val(p->type, min), propedit_sprint_val(p->type, max), propedit_sprint_val(p->type, avg));
+		}
+	}
+
+	if (gui->propedit_add_value != NULL)
+		for (e = htprop_first(&p->values); e; e = htprop_next(&p->values, e))
+			gui->propedit_add_value(ctx, pe->key, rowid, propedit_sprint_val(p->type, e->key), e->value);
+}
+
+
+const char *propedit_query(void *pe, const char *cmd, const char *key, const char *val, int idx)
+{
+	pe_ctx_t *ctx = pe;
+	const char *s;
+	static const char *ok = "ok";
+	static char typ[2];
+
+	if (memcmp(cmd, "v1st", 4) == 0) { /* get the first value */
+		ctx->qprop = htsp_get(ctx->core_props, key);
+		if (ctx->qprop == NULL)
+			return NULL;
+
+		ctx->qprope = htprop_first(&ctx->qprop->values);
+		goto vnxt;
+	}
+
+	else if (memcmp(cmd, "vnxt", 4) == 0) { /* get the next value */
+		vnxt:;
+		if (ctx->qprope == NULL)
+			return NULL;
+		s = propedit_sprint_val(ctx->qprop->type, ctx->qprope->key);
+		ctx->qprope = htprop_next(&ctx->qprop->values, ctx->qprope);
+		return s;
+	}
+
+	else if (memcmp(cmd, "vset", 4) == 0) { /* set value */
+		if (pcb_propsel_set(key, val) > 0) {
+			htsp_entry_t *pe;
+			pcb_props_uninit(ctx->core_props);
+			ctx->core_props = pcb_props_init();
+			pcb_propsel_map_core(ctx->core_props);
+			pe = htsp_getentry(ctx->core_props, key);
+			if (pe != NULL) {
+				propedit_ins_prop(ctx, pe);
+				return ok;
+			}
+		}
+		return NULL;
+	}
+
+	else if (memcmp(cmd, "vdel", 4) == 0) { /* remove an attribute value */
+		if (pcb_propsel_del(key) > 0)
+			return ok;
+		return NULL;
+	}
+
+	else if (memcmp(cmd, "type", 4) == 0) { /* return type char */
+		htsp_entry_t *pe;
+		pcb_props_t *p;
+
+		pe = htsp_getentry(ctx->core_props, key);
+		if (pe == NULL)
+			return NULL;
+
+		typ[1] = '\0';
+		p = pe->value;
+		switch(p->type) {
+			case PCB_PROPT_invalid: return NULL;
+			case PCB_PROPT_STRING: typ[0] = 's'; return typ;
+			case PCB_PROPT_COORD:  typ[0] = 'c'; return typ;
+			case PCB_PROPT_ANGLE:  typ[0] = 'a'; return typ;
+			case PCB_PROPT_INT:    typ[0] = 'i'; return typ;
+		}
+		return NULL;
+	}
+
+	/* invalid query */
+	return NULL;
+}
+
diff --git a/src_plugins/propedit/props.h b/src_plugins/propedit/props.h
new file mode 100644
index 0000000..6fb3cec
--- /dev/null
+++ b/src_plugins/propedit/props.h
@@ -0,0 +1,112 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This API builds and maintains a collection of values for each named property.
+   A value can be added any time. All values ever seen for a property is stored.
+   Duplicate values per property are stored only once, but number of occurence
+   per value (per property) is kept track on. Typically at the end of
+   a query, but generally ny time, the caller may query for:
+    - all known properties (it's a htsp)
+    - all known values of a property
+    - statistics of values of a property
+*/
+
+#include "global.h"
+#include <genht/htsp.h>
+
+typedef enum {
+	PCB_PROPT_invalid,
+	PCB_PROPT_STRING,
+	PCB_PROPT_COORD,
+	PCB_PROPT_ANGLE,
+	PCB_PROPT_INT,
+	PCB_PROPT_max
+} pcb_prop_type_t;
+
+typedef union {
+	const char *string;
+	Coord coord;
+	Angle angle;
+	int i;
+} pcb_propval_t;
+
+typedef pcb_propval_t htprop_key_t;
+typedef unsigned long int htprop_value_t;
+#define HT(x) htprop_ ## x
+#include <genht/ht.h>
+#undef HT
+
+typedef struct {
+	pcb_prop_type_t type;
+	htprop_t values;
+	unsigned core:1;  /* 1 if it is a core property */
+} pcb_props_t;
+
+/* A property list (props) is a string->pcb_props_t. Each entry is a named
+   property with a value that's a type and a value hash (vhash). vhash's
+   key is each value that the property ever took, and vhash's value is an
+   integer value of how many times the given property is taken */
+htsp_t *pcb_props_init(void);
+void pcb_props_uninit(htsp_t *props);
+
+/* Add a value of a named property; if the value is already known, its counter
+   is increased. If propname didn't exist, create it. Returns NULL on error.
+   Error conditions:
+    - invalid type
+    - mismatching type for the property (all values of a given property must be the same)
+*/
+pcb_props_t *pcb_props_add(htsp_t *props, const char *propname, pcb_prop_type_t type, pcb_propval_t val);
+
+/* Retrieve values for a prop - returns NULL if propname doesn't exist */
+pcb_props_t *pcb_props_get(htsp_t *props, const char *propname);
+
+
+/* Return the type name of a property type or NULL on error. */
+const char *pcb_props_type_name(pcb_prop_type_t type);
+
+/* Look up property propname and calculate statistics for all values occured so far.
+   Any of most_common, min, max and avg can be NULL. Returns NULL if propname
+   doesn't exist or stat values that can not be calculated for the given type
+   are not NULL. Invalid type/stat combinations:
+     type=string   min, max, avg
+*/
+pcb_props_t *pcb_props_stat(htsp_t *props, const char *propname, pcb_propval_t *most_common, pcb_propval_t *min, pcb_propval_t *max, pcb_propval_t *avg);
+
+/* String query API for HIDs to call back without having to link */
+const char *propedit_query(void *pe, const char *cmd, const char *key, const char *val, int idx);
+
+
+/* Return the value of a property as string - there's a set of static buffers,
+   old values are discarded after 8 calls! */
+const char *propedit_sprint_val(pcb_prop_type_t type, pcb_propval_t val);
+
+/* for internal use */
+typedef struct {
+	htsp_t *core_props;
+
+	/* query */
+	pcb_props_t *qprop;
+	htprop_entry_t *qprope;
+} pe_ctx_t;
+
+/* add a row in the gui propedit table */
+void propedit_ins_prop(pe_ctx_t *ctx, htsp_entry_t *pe);
diff --git a/src_plugins/propedit/propsel.c b/src_plugins/propedit/propsel.c
new file mode 100644
index 0000000..b32ab8b
--- /dev/null
+++ b/src_plugins/propedit/propsel.c
@@ -0,0 +1,565 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "data.h"
+#include "props.h"
+#include "propsel.h"
+#include "change.h"
+#include "misc.h"
+#include "misc_util.h"
+#include "compat_misc.h"
+#include "undo.h"
+#include "rotate.h"
+
+/*********** map ***********/
+typedef struct {
+	htsp_t *props;
+} map_ctx_t;
+
+#define map_chk_skip(ctx, obj) \
+	if (!TEST_FLAG(PCB_FLAG_SELECTED, obj)) return;
+
+#define type2field_String string
+#define type2field_Coord coord
+#define type2field_Angle angle
+#define type2field_int i
+
+#define type2TYPE_String PCB_PROPT_STRING
+#define type2TYPE_Coord PCB_PROPT_COORD
+#define type2TYPE_Angle PCB_PROPT_ANGLE
+#define type2TYPE_int PCB_PROPT_INT
+
+#define map_add_prop(ctx, name, type, val) \
+do { \
+	pcb_propval_t v; \
+	v.type2field_ ## type = (val);  \
+	pcb_props_add(((map_ctx_t *)ctx)->props, name, type2TYPE_ ## type, v); \
+} while(0)
+
+static void map_attr(void *ctx, const AttributeListType *list)
+{
+	int i, bl = 0;
+	char small[256];
+	char *big = NULL;
+
+	small[0] = 'a';
+	small[1] = '/';
+
+	for (i = 0; i < list->Number; i++) {
+		int len = strlen(list->List[i].name);
+		char *nm;
+		if (len >= sizeof(small)-3) {
+			if (len > bl) {
+				bl = len + 128;
+				if (big != NULL)
+					free(big);
+				big = malloc(bl);
+				big[0] = 'a';
+				big[1] = '/';
+			}
+			nm = big;
+		}
+		else
+			nm = small;
+		strcpy(nm+2, list->List[i].name);
+		map_add_prop(ctx, nm, String, list->List[i].value);
+	}
+	if (big != NULL)
+		free(big);
+}
+
+static void map_line_cb(void *ctx, PCBType *pcb, LayerType *layer, LineType *line)
+{
+	map_chk_skip(ctx, line);
+	map_add_prop(ctx, "p/trace/thickness", Coord, line->Thickness);
+	map_add_prop(ctx, "p/trace/clearance", Coord, line->Clearance);
+	map_attr(ctx, &line->Attributes);
+}
+
+static void map_arc_cb(void *ctx, PCBType *pcb, LayerType *layer, ArcType *arc)
+{
+	map_chk_skip(ctx, arc);
+	map_add_prop(ctx, "p/trace/thickness", Coord, arc->Thickness);
+	map_add_prop(ctx, "p/trace/clearance", Coord, arc->Clearance);
+	map_add_prop(ctx, "p/arc/width",       Coord, arc->Width);
+	map_add_prop(ctx, "p/arc/height",      Coord, arc->Height);
+	map_add_prop(ctx, "p/arc/angle/start", Angle, arc->StartAngle);
+	map_add_prop(ctx, "p/arc/angle/delta", Angle, arc->Delta);
+	map_attr(ctx, &arc->Attributes);
+}
+
+static void map_text_cb(void *ctx, PCBType *pcb, LayerType *layer, TextType *text)
+{
+	map_chk_skip(ctx, text);
+	map_add_prop(ctx, "p/text/scale", int, text->Scale);
+	map_add_prop(ctx, "p/text/rotation", int, text->Direction);
+	map_add_prop(ctx, "p/text/string", String, text->TextString);
+	map_attr(ctx, &text->Attributes);
+}
+
+static void map_poly_cb(void *ctx, PCBType *pcb, LayerType *layer, PolygonType *poly)
+{
+	map_chk_skip(ctx, poly);
+	map_attr(ctx, &poly->Attributes);
+}
+
+static void map_eline_cb(void *ctx, PCBType *pcb, ElementType *element, LineType *line)
+{
+	map_chk_skip(ctx, line);
+	map_line_cb(ctx, pcb, NULL, line);
+	map_attr(ctx, &line->Attributes);
+}
+
+static void map_earc_cb(void *ctx, PCBType *pcb, ElementType *element, ArcType *arc)
+{
+	map_chk_skip(ctx, arc);
+	map_arc_cb(ctx, pcb, NULL, arc);
+	map_attr(ctx, &arc->Attributes);
+}
+
+static void map_etext_cb(void *ctx, PCBType *pcb, ElementType *element, TextType *text)
+{
+	map_chk_skip(ctx, text);
+	map_text_cb(ctx, pcb, NULL, text);
+	map_attr(ctx, &text->Attributes);
+}
+
+static void map_epin_cb(void *ctx, PCBType *pcb, ElementType *element, PinType *pin)
+{
+	map_chk_skip(ctx, pin);
+	map_add_prop(ctx, "p/pin/thickness", Coord, pin->Thickness);
+	map_add_prop(ctx, "p/pin/clearance", Coord, pin->Clearance);
+	map_add_prop(ctx, "p/pin/mask",      Coord, pin->Mask);
+	map_add_prop(ctx, "p/pin/hole",      Coord, pin->DrillingHole);
+	map_attr(ctx, &pin->Attributes);
+}
+
+static void map_epad_cb(void *ctx, PCBType *pcb, ElementType *element, PadType *pad)
+{
+	map_chk_skip(ctx, pad);
+	map_add_prop(ctx, "p/pad/mask",      Coord, pad->Mask);
+	map_attr(ctx, &pad->Attributes);
+}
+
+
+
+static void map_via_cb(void *ctx, PCBType *pcb, PinType *via)
+{
+	map_chk_skip(ctx, via);
+	map_add_prop(ctx, "p/via/thickness", Coord, via->Thickness);
+	map_add_prop(ctx, "p/via/clearance", Coord, via->Clearance);
+	map_add_prop(ctx, "p/via/mask",      Coord, via->Mask);
+	map_add_prop(ctx, "p/via/hole",      Coord, via->DrillingHole);
+	map_attr(ctx, &via->Attributes);
+}
+
+void pcb_propsel_map_core(htsp_t *props)
+{
+	map_ctx_t ctx;
+
+	ctx.props = props;
+	
+	pcb_loop_all(&ctx,
+		NULL, map_line_cb, map_arc_cb, map_text_cb, map_poly_cb,
+		NULL, map_eline_cb, map_earc_cb, map_etext_cb, map_epin_cb, map_epad_cb,
+		map_via_cb
+	);
+}
+
+/*******************/
+
+typedef struct set_ctx_s {
+	const char *name;
+	const char *value;
+	Coord c;
+	double d;
+	pcb_bool c_absolute, d_absolute, c_valid, d_valid, is_trace, is_attr;
+
+	int set_cnt;
+} set_ctx_t;
+
+static void set_attr(set_ctx_t *st, AttributeListType *list)
+{
+	const char *key = st->name+2;
+	const char *orig = AttributeGetFromList(list, key);
+
+	if ((orig != NULL) && (strcmp(orig, st->value) == 0))
+		return;
+
+	AttributePutToList(list, key, st->value, 1);
+	st->set_cnt++;
+}
+
+#define set_chk_skip(ctx, obj) \
+	if (!TEST_FLAG(PCB_FLAG_SELECTED, obj)) return;
+
+#define DONE { st->set_cnt++; RestoreUndoSerialNumber(); return; }
+
+static void set_line_cb(void *ctx, PCBType *pcb, LayerType *layer, LineType *line)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+	const char *pn = st->name + 8;
+
+	set_chk_skip(st, line);
+
+	if (st->is_attr) {
+		set_attr(st, &line->Attributes);
+		return;
+	}
+
+	if (st->is_trace && st->c_valid && (strcmp(pn, "thickness") == 0) &&
+	    ChangeObject1stSize(PCB_TYPE_LINE, layer, line, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->is_trace && st->c_valid && (strcmp(pn, "clearance") == 0) &&
+	    ChangeObjectClearSize(PCB_TYPE_LINE, layer, line, NULL, st->c, st->c_absolute)) DONE;
+}
+
+static void set_arc_cb(void *ctx, PCBType *pcb, LayerType *layer, ArcType *arc)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+	const char *pn = st->name + 8;
+
+	set_chk_skip(st, arc);
+
+	if (st->is_attr) {
+		set_attr(st, &arc->Attributes);
+		return;
+	}
+
+	if (st->is_trace && st->c_valid && (strcmp(pn, "thickness") == 0) &&
+	    ChangeObject1stSize(PCB_TYPE_ARC, layer, arc, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->is_trace && st->c_valid && (strcmp(pn, "clearance") == 0) &&
+	    ChangeObjectClearSize(PCB_TYPE_ARC, layer, arc, NULL, st->c, st->c_absolute)) DONE;
+
+	pn = st->name + 6;
+
+	if (!st->is_trace && st->c_valid && (strcmp(pn, "width") == 0) &&
+	    ChangeObjectRadius(PCB_TYPE_ARC, layer, arc, NULL, 0, st->c, st->c_absolute)) DONE;
+
+	if (!st->is_trace && st->c_valid && (strcmp(pn, "height") == 0) &&
+	    ChangeObjectRadius(PCB_TYPE_ARC, layer, arc, NULL, 1, st->c, st->c_absolute)) DONE;
+
+	if (!st->is_trace && st->d_valid && (strcmp(pn, "angle/start") == 0) &&
+	    ChangeObjectAngle(PCB_TYPE_ARC, layer, arc, NULL, 0, st->d, st->d_absolute)) DONE;
+
+	if (!st->is_trace && st->d_valid && (strcmp(pn, "angle/delta") == 0) &&
+	    ChangeObjectAngle(PCB_TYPE_ARC, layer, arc, NULL, 1, st->d, st->d_absolute)) DONE;
+}
+
+static void set_text_cb_any(void *ctx, PCBType *pcb, int type, void *layer_or_element, TextType *text)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+	const char *pn = st->name + 7;
+	char *old;
+
+	set_chk_skip(st, text);
+
+	if (st->is_attr) {
+		set_attr(st, &text->Attributes);
+		return;
+	}
+
+	if (st->d_valid && (strcmp(pn, "scale") == 0) &&
+	    ChangeObjectSize(type, layer_or_element, text, text, PCB_MIL_TO_COORD(st->d), st->d_absolute)) DONE;
+
+	if ((strcmp(pn, "string") == 0) &&
+	    (old = ChangeObjectName(type, layer_or_element, text, NULL, pcb_strdup(st->value)))) {
+		free(old);
+		DONE;
+	}
+
+	if (st->d_valid && (strcmp(pn, "rotation") == 0)) {
+		int delta;
+		if (st->d_absolute) {
+			delta = (st->d - text->Direction);
+			if (delta < 0)
+				delta += 4;
+			delta = (unsigned int)delta & 3;
+		}
+		else
+			delta = st->d;
+		if (RotateObject(type, layer_or_element, text, text, text->X, text->Y, delta) != NULL)
+			DONE;
+	}
+}
+
+static void set_text_cb(void *ctx, PCBType *pcb, LayerType *layer, TextType *text)
+{
+	set_text_cb_any(ctx, pcb, PCB_TYPE_TEXT, layer, text);
+}
+
+
+static void set_poly_cb(void *ctx, PCBType *pcb, LayerType *layer, PolygonType *poly)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+
+	set_chk_skip(st, poly);
+
+	if (st->is_attr) {
+		set_attr(st, &poly->Attributes);
+		return;
+	}
+}
+
+static void set_eline_cb(void *ctx, PCBType *pcb, ElementType *element, LineType *line)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+
+	set_chk_skip(st, line);
+
+	if (st->is_attr) {
+		set_attr(st, &line->Attributes);
+		return;
+	}
+}
+
+static void set_earc_cb(void *ctx, PCBType *pcb, ElementType *element, ArcType *arc)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+
+	set_chk_skip(st, arc);
+
+	if (st->is_attr) {
+		set_attr(st, &arc->Attributes);
+		return;
+	}
+}
+
+static void set_etext_cb(void *ctx, PCBType *pcb, ElementType *element, TextType *text)
+{
+	set_text_cb_any(ctx, pcb, PCB_TYPE_ELEMENT_NAME, element, text);
+}
+
+static void set_epin_cb(void *ctx, PCBType *pcb, ElementType *element, PinType *pin)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+	const char *pn = st->name + 6;
+
+	set_chk_skip(st, pin);
+
+	if (st->is_attr) {
+		set_attr(st, &pin->Attributes);
+		return;
+	}
+
+	if (st->c_valid && (strcmp(pn, "thickness") == 0) &&
+	    ChangeObject1stSize(PCB_TYPE_PIN, pin->Element, pin, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->c_valid && (strcmp(pn, "clearance") == 0) &&
+	    ChangeObjectClearSize(PCB_TYPE_PIN, pin->Element, pin, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->c_valid && (strcmp(pn, "mask") == 0) &&
+	    ChangeObjectMaskSize(PCB_TYPE_PIN, pin->Element, pin, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->c_valid && (strcmp(pn, "hole") == 0) &&
+	    ChangeObject2ndSize(PCB_TYPE_PIN, pin->Element, pin, NULL, st->c, st->c_absolute, pcb_false)) DONE;
+}
+
+static void set_epad_cb(void *ctx, PCBType *pcb, ElementType *element, PadType *pad)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+	const char *pn = st->name + 6;
+
+	set_chk_skip(st, pad);
+
+	if (st->is_attr) {
+		set_attr(st, &pad->Attributes);
+		return;
+	}
+
+	if (st->c_valid && (strcmp(pn, "mask") == 0) &&
+	    ChangeObjectMaskSize(PCB_TYPE_PAD, pad->Element, pad, NULL, st->c, st->c_absolute)) DONE;
+}
+
+static void set_via_cb(void *ctx, PCBType *pcb, PinType *via)
+{
+	set_ctx_t *st = (set_ctx_t *)ctx;
+	const char *pn = st->name + 6;
+
+	set_chk_skip(st, via);
+
+	if (st->is_attr) {
+		set_attr(st, &via->Attributes);
+		return;
+	}
+
+	if (st->c_valid && (strcmp(pn, "thickness") == 0) &&
+	    ChangeObject1stSize(PCB_TYPE_VIA, via, via, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->c_valid && (strcmp(pn, "clearance") == 0) &&
+	    ChangeObjectClearSize(PCB_TYPE_VIA, via, via, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->c_valid && (strcmp(pn, "mask") == 0) &&
+	    ChangeObjectMaskSize(PCB_TYPE_VIA, via, via, NULL, st->c, st->c_absolute)) DONE;
+
+	if (st->c_valid && (strcmp(pn, "hole") == 0) &&
+	    ChangeObject2ndSize(PCB_TYPE_VIA, via, via, NULL, st->c, st->c_absolute, pcb_false)) DONE;
+}
+
+/* use the callback if trc is true or prop matches a prefix or we are setting attributes, else NULL */
+#define MAYBE_PROP(trc, prefix, cb) \
+	(((ctx.is_attr) || (trc) || (strncmp(prop, (prefix), sizeof(prefix)-1) == 0) || (prop[0] == 'a')) ? (cb) : NULL)
+
+#define MAYBE_ATTR(cb) \
+	((ctx.is_attr) ? (cb) : NULL)
+
+int pcb_propsel_set(const char *prop, const char *value)
+{
+	set_ctx_t ctx;
+	char *end;
+	const char *start;
+
+	/* sanity checks for invalid props */
+	if (prop[1] != '/')
+		return 0;
+	if ((prop[0] != 'a') && (prop[0] != 'p'))
+		return 0;
+
+	ctx.is_trace = (strncmp(prop, "p/trace/", 8) == 0);
+	ctx.is_attr = (prop[0] == 'a');
+	ctx.name = prop;
+	ctx.value = value;
+	ctx.c = GetValueEx(value, NULL, &ctx.c_absolute, NULL, NULL, &ctx.c_valid);
+	ctx.d = strtod(value, &end);
+	ctx.d_valid = (*end == '\0');
+	start = value;
+	while(isspace(*start)) start++;
+	ctx.d_absolute = ((*start != '-') && (*start != '+'));
+	ctx.set_cnt = 0;
+
+	SaveUndoSerialNumber();
+
+	pcb_loop_all(&ctx,
+		NULL,
+		MAYBE_PROP(ctx.is_trace, "p/line/", set_line_cb),
+		MAYBE_PROP(ctx.is_trace, "p/arc/", set_arc_cb),
+		MAYBE_PROP(0, "p/text/", set_text_cb),
+		MAYBE_ATTR(set_poly_cb),
+		NULL,
+		MAYBE_ATTR(set_eline_cb),
+		MAYBE_ATTR(set_earc_cb),
+		MAYBE_PROP(0, "p/text/", set_etext_cb),
+		MAYBE_PROP(0, "p/pin/", set_epin_cb),
+		MAYBE_PROP(0, "p/pad/", set_epad_cb),
+		MAYBE_PROP(0, "p/via/", set_via_cb)
+	);
+	IncrementUndoSerialNumber();
+	return ctx.set_cnt;
+}
+
+#undef MAYBE_PROP
+#undef MAYBE_ATTR
+
+/*******************/
+
+typedef struct del_ctx_s {
+	const char *key;
+	int del_cnt;
+} del_ctx_t;
+
+static void del_attr(void *ctx, AttributeListType *list)
+{
+	del_ctx_t *st = (del_ctx_t *)ctx;
+	if (AttributeRemoveFromList(list, st->key+2))
+		st->del_cnt++;
+}
+
+static void del_line_cb(void *ctx, PCBType *pcb, LayerType *layer, LineType *line)
+{
+	map_chk_skip(ctx, line);
+	del_attr(ctx, &line->Attributes);
+}
+
+static void del_arc_cb(void *ctx, PCBType *pcb, LayerType *layer, ArcType *arc)
+{
+	map_chk_skip(ctx, arc);
+	del_attr(ctx, &arc->Attributes);
+}
+
+static void del_text_cb(void *ctx, PCBType *pcb, LayerType *layer, TextType *text)
+{
+	map_chk_skip(ctx, text);
+	del_attr(ctx, &text->Attributes);
+}
+
+static void del_poly_cb(void *ctx, PCBType *pcb, LayerType *layer, PolygonType *poly)
+{
+	map_chk_skip(ctx, poly);
+	del_attr(ctx, &poly->Attributes);
+}
+
+static void del_eline_cb(void *ctx, PCBType *pcb, ElementType *element, LineType *line)
+{
+	map_chk_skip(ctx, line);
+	del_attr(ctx, &line->Attributes);
+}
+
+static void del_earc_cb(void *ctx, PCBType *pcb, ElementType *element, ArcType *arc)
+{
+	map_chk_skip(ctx, arc);
+	del_attr(ctx, &arc->Attributes);
+}
+
+static void del_etext_cb(void *ctx, PCBType *pcb, ElementType *element, TextType *text)
+{
+	map_chk_skip(ctx, text);
+	del_attr(ctx, &text->Attributes);
+}
+
+static void del_epin_cb(void *ctx, PCBType *pcb, ElementType *element, PinType *pin)
+{
+	map_chk_skip(ctx, pin);
+	del_attr(ctx, &pin->Attributes);
+}
+
+static void del_epad_cb(void *ctx, PCBType *pcb, ElementType *element, PadType *pad)
+{
+	map_chk_skip(ctx, pad);
+	del_attr(ctx, &pad->Attributes);
+}
+
+static void del_via_cb(void *ctx, PCBType *pcb, PinType *via)
+{
+	map_chk_skip(ctx, via);
+	del_attr(ctx, &via->Attributes);
+}
+
+int pcb_propsel_del(const char *key)
+{
+	del_ctx_t st;
+
+	if ((key[0] != 'a') || (key[1] != '/')) /* do not attempt to remove anything but attributes */
+		return 0;
+
+	st.key = key;
+	st.del_cnt = 0;
+
+	pcb_loop_all(&st,
+		NULL, del_line_cb, del_arc_cb, del_text_cb, del_poly_cb,
+		NULL, del_eline_cb, del_earc_cb, del_etext_cb, del_epin_cb, del_epad_cb,
+		del_via_cb
+	);
+	return st.del_cnt;
+}
+
+
diff --git a/src_plugins/propedit/propsel.h b/src_plugins/propedit/propsel.h
new file mode 100644
index 0000000..aab79d0
--- /dev/null
+++ b/src_plugins/propedit/propsel.h
@@ -0,0 +1,26 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This API builds a prop list by looking at all selected objects on a design. */
+void pcb_propsel_map_core(htsp_t *props);
+int pcb_propsel_set(const char *prop, const char *value);
+int pcb_propsel_del(const char *attr_name);
diff --git a/src_plugins/puller/Makefile b/src_plugins/puller/Makefile
new file mode 100644
index 0000000..e389405
--- /dev/null
+++ b/src_plugins/puller/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_puller
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/puller/Plug.tmpasm b/src_plugins/puller/Plug.tmpasm
new file mode 100644
index 0000000..975fb46
--- /dev/null
+++ b/src_plugins/puller/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {puller}
+append /local/pcb/mod/OBJS_C99 [@ $(PLUGDIR)/puller/puller.o @]
+
+switch /local/pcb/puller/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/puller/README b/src_plugins/puller/README
new file mode 100644
index 0000000..97c3750
--- /dev/null
+++ b/src_plugins/puller/README
@@ -0,0 +1,5 @@
+Pull traces to minimize their length.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/puller/puller.c b/src_plugins/puller/puller.c
new file mode 100644
index 0000000..7ba0ab2
--- /dev/null
+++ b/src_plugins/puller/puller.c
@@ -0,0 +1,2406 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2006 DJ Delorie
+ *  Copyright (C) 2011 PCB Contributers (See ChangeLog for details)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+
+/* FIXME: Things that need to be fixed before this is "perfect".
+   Add to this list as we find things.
+
+   - respect the outline layer.
+
+   - don't consider points that are perpendicular to our start_arc.
+     I.e. when we have busses going around corners, we have a *lot* of
+     arcs and endpoints that are all in the same direction and all
+     equally "good", but rounding the arc angles to integers causes
+     all sorts of tiny differences that result in bumps, reversals,
+     and other messes.
+
+   - Store the X,Y values in our shadow struct so we don't fill up the
+     undo buffer with all our line reversals.
+
+   - at least check the other layers in our layer group.
+
+*/
+
+#include "config.h"
+#include "conf_core.h"
+
+#include "global.h"
+
+#include <math.h>
+#include <memory.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <glib.h>
+
+#include "create.h"
+#include "data.h"
+#include "draw.h"
+#include "misc.h"
+#include "move.h"
+#include "pcb-printf.h"
+#include "remove.h"
+#include "rtree.h"
+#include "strflags.h"
+#include "undo.h"
+#include "layer.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "misc_util.h"
+
+#define abort1() fprintf(stderr, "abort at line %d\n", __LINE__), abort()
+
+#define TRACE0 0
+#define TRACE1 0
+
+/* sine of one degree */
+#define SIN1D	0.0174524064372835
+
+static jmp_buf abort_buf;
+
+#define sqr(x) (1.0*(x)*(x))
+
+static int multi, line_exact, arc_exact;
+static LineTypePtr the_line;
+static ArcTypePtr the_arc;
+static double arc_dist;
+
+/* We canonicalize the arc and line such that the point to be moved is
+   always Point2 for the line, and at start+delta for the arc.  */
+
+static int x, y;								/* the point we're moving */
+static int cx, cy;							/* centerpoint of the arc */
+static int ex, ey;							/* fixed end of the line */
+
+/* 0 is left (-x), 90 is down (+y), 180 is right (+x), 270 is up (-y) */
+
+static int within(int x1, int y1, int x2, int y2, int r)
+{
+	return Distance(x1, y1, x2, y2) <= r / 2;
+}
+
+static int arc_endpoint_is(ArcTypePtr a, int angle, int x, int y)
+{
+	int ax = a->X, ay = a->Y;
+
+	if (angle % 90 == 0) {
+		int ai = (int) (angle / 90) & 3;
+		switch (ai) {
+		case 0:
+			ax -= a->Width;
+			break;
+		case 1:
+			ay += a->Height;
+			break;
+		case 2:
+			ax += a->Width;
+			break;
+		case 3:
+			ay -= a->Height;
+			break;
+		}
+	}
+	else {
+		double rad = angle * M_PI / 180;
+		ax -= a->Width * cos(rad);
+		ay += a->Width * sin(rad);
+	}
+#if TRACE1
+	pcb_printf(" - arc endpoint %#mD\n", ax, ay);
+#endif
+	arc_dist = Distance(ax, ay, x, y);
+	if (arc_exact)
+		return arc_dist < 2;
+	return arc_dist < a->Thickness / 2;
+}
+
+/* Cross c->u and c->v, return the magnitute */
+static double cross2d(int cx, int cy, int ux, int uy, int vx, int vy)
+{
+	ux -= cx;
+	uy -= cy;
+	vx -= cx;
+	vy -= cy;
+	return (double) ux *vy - (double) uy *vx;
+}
+
+/* Likewise, for dot product. */
+static double dot2d(int cx, int cy, int ux, int uy, int vx, int vy)
+{
+	ux -= cx;
+	uy -= cy;
+	vx -= cx;
+	vy -= cy;
+	return (double) ux *vx + (double) uy *vy;
+}
+
+#if 0
+/* angle of c->v, relative to c->u, in radians.  Range is -pi..pi */
+static double angle2d(int cx, int cy, int ux, int uy, int vx, int vy)
+{
+	double cross;
+	double magu, magv, sintheta;
+#if TRACE1
+	printf("angle2d %d,%d %d,%d %d,%d\n", cx, cy, ux, uy, vx, vy);
+#endif
+	ux -= cx;
+	uy -= cy;
+	vx -= cx;
+	vy -= cy;
+#if TRACE1
+	printf(" = %d,%d %d,%d\n", ux, uy, vx, vy);
+#endif
+	cross = (double) ux *vy - (double) uy *vx;
+	magu = sqrt((double) ux * ux + (double) uy * uy);
+	magv = sqrt((double) vx * vx + (double) vy * vy);
+	sintheta = cross / (magu * magv);
+#if TRACE1
+	printf(" = %f / (%f * %f) = %f\n", cross, magu, magv, sintheta);
+#endif
+	return asin(sintheta);
+}
+#endif
+
+static int same_sign(double a, double b)
+{
+	return (a * b >= 0);
+}
+
+static double r2d(double r)
+{
+	return 180.0 * r / M_PI;
+}
+
+static double d2r(double d)
+{
+	return M_PI * d / 180.0;
+}
+
+/* | a b |
+   | c d | */
+static double det(double a, double b, double c, double d)
+{
+	return a * d - b * c;
+}
+
+/* The lines are x1y1-x2y2 and x3y3-x4y4.  Returns pcb_true if they
+   intersect.  */
+static int intersection_of_lines(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int *xr, int *yr)
+{
+	double x, y, d;
+	d = det(x1 - x2, y1 - y2, x3 - x4, y3 - y4);
+	if (!d)
+		return 0;
+	x = (det(det(x1, y1, x2, y2), x1 - x2, det(x3, y3, x4, y4), x3 - x4) / d);
+	y = (det(det(x1, y1, x2, y2), y1 - y2, det(x3, y3, x4, y4), y3 - y4) / d);
+	*xr = (int) (x + 0.5);
+	*yr = (int) (y + 0.5);
+	return 1;
+}
+
+/* Same, for line segments.  Returns pcb_true if they intersect.  For this
+   function, xr and yr may be NULL if you don't need the values.  */
+static int intersection_of_linesegs(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int *xr, int *yr)
+{
+	double x, y, d;
+	d = det(x1 - x2, y1 - y2, x3 - x4, y3 - y4);
+	if (!d)
+		return 0;
+	x = (det(det(x1, y1, x2, y2), x1 - x2, det(x3, y3, x4, y4), x3 - x4) / d);
+	y = (det(det(x1, y1, x2, y2), y1 - y2, det(x3, y3, x4, y4), y3 - y4) / d);
+	if (MIN(x1, x2) > x || x > MAX(x1, x2)
+			|| MIN(y1, y2) > y || y > MAX(y1, y2))
+		return 0;
+	if (MIN(x3, x4) > x || x > MAX(x3, x4)
+			|| MIN(y3, y4) > y || y > MAX(y3, y4))
+		return 0;
+	if (xr)
+		*xr = (int) (x + 0.5);
+	if (yr)
+		*yr = (int) (y + 0.5);
+	return 1;
+}
+
+/* distance between a line and a point */
+static double dist_lp(int x1, int y1, int x2, int y2, int px, int py)
+{
+	double den = Distance(x1, y1, x2, y2);
+	double rv = (fabs(((double) x2 - x1) * ((double) y1 - py)
+										- ((double) x1 - px) * ((double) y2 - y1))
+							 / den);
+#if TRACE1
+	pcb_printf("dist %#mD-%#mD to %#mD is %f\n", x1, y1, x2, y2, px, py, rv);
+#endif
+	return rv;
+}
+
+/* distance between a line segment and a point */
+static double dist_lsp(int x1, int y1, int x2, int y2, int px, int py)
+{
+	double d;
+	if (dot2d(x1, y1, x2, y2, px, py) < 0)
+		return Distance(x1, y1, px, py);
+	if (dot2d(x2, y2, x1, y1, px, py) < 0)
+		return Distance(x2, y2, px, py);
+	d = (fabs(((double) x2 - x1) * ((double) y1 - py)
+						- ((double) x1 - px) * ((double) y2 - y1))
+			 / Distance(x1, y1, x2, y2));
+	return d;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*                       Single Point Puller                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+static r_dir_t line_callback(const BoxType * b, void *cl)
+{
+	/* LayerTypePtr layer = (LayerTypePtr)cl; */
+	LineTypePtr l = (LineTypePtr) b;
+	double d1, d2, t;
+#if TRACE1
+	pcb_printf("line %#mD .. %#mD\n", l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
+#endif
+	d1 = Distance(l->Point1.X, l->Point1.Y, x, y);
+	d2 = Distance(l->Point2.X, l->Point2.Y, x, y);
+	if ((d1 < 2 || d2 < 2) && !line_exact) {
+		line_exact = 1;
+		the_line = 0;
+	}
+	t = line_exact ? 2 : l->Thickness / 2;
+	if (d1 < t || d2 < t) {
+		if (the_line)
+			multi = 1;
+		the_line = l;
+#if TRACE1
+		printf("picked, exact %d\n", line_exact);
+#endif
+	}
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static r_dir_t arc_callback(const BoxType * b, void *cl)
+{
+	/* LayerTypePtr layer = (LayerTypePtr) cl; */
+	ArcTypePtr a = (ArcTypePtr) b;
+
+#if TRACE1
+	pcb_printf("arc a %#mD r %#mS sa %ld d %ld\n", a->X, a->Y, a->Width, a->StartAngle, a->Delta);
+#endif
+	if (!arc_endpoint_is(a, a->StartAngle, x, y)
+			&& !arc_endpoint_is(a, a->StartAngle + a->Delta, x, y))
+		return 1;
+	if (arc_dist < 2) {
+		if (!arc_exact) {
+			arc_exact = 1;
+			the_arc = 0;
+		}
+		if (the_arc)
+			multi = 1;
+		the_arc = a;
+#if TRACE1
+		printf("picked, exact %d\n", arc_exact);
+#endif
+	}
+	else if (!arc_exact) {
+		if (the_arc)
+			multi = 1;
+		the_arc = a;
+#if TRACE1
+		printf("picked, exact %d\n", arc_exact);
+#endif
+	}
+	return R_DIR_FOUND_CONTINUE;
+}
+
+static int find_pair(int Px, int Py)
+{
+	BoxType spot;
+
+#if TRACE1
+	pcb_printf("\nPuller find_pair at %#mD\n", Crosshair.X, Crosshair.Y);
+#endif
+
+	x = Px;
+	y = Py;
+	multi = 0;
+	line_exact = arc_exact = 0;
+	the_line = 0;
+	the_arc = 0;
+	spot.X1 = x - 1;
+	spot.Y1 = y - 1;
+	spot.X2 = x + 1;
+	spot.Y2 = y + 1;
+	r_search(CURRENT->line_tree, &spot, NULL, line_callback, CURRENT, NULL);
+	r_search(CURRENT->arc_tree, &spot, NULL, arc_callback, CURRENT, NULL);
+	if (the_line && the_arc && !multi)
+		return 1;
+	x = Px;
+	y = Py;
+	return 0;
+}
+
+
+static const char puller_syntax[] = "Puller()";
+
+static const char puller_help[] = "Pull an arc-line junction tight.";
+
+/* %start-doc actions Puller
+
+The @code{Puller()} action is a special-purpose optimization.  When
+invoked while the crosshair is over the junction of an arc and a line,
+it will adjust the arc's angle and the connecting line's endpoint such
+that the line intersects the arc at a tangent.  In the example below,
+the left side is ``before'' with the black target marking where to put
+the crosshair:
+
+ at center @image{puller,,,Example of how puller works,png}
+
+The right side is ``after'' with the black target marking where the
+arc-line intersection was moved to.
+
+%end-doc */
+
+static int Puller(int argc, const char **argv, Coord Ux, Coord Uy)
+{
+	double arc_angle, base_angle;
+#if TRACE1
+	double line_angle, rel_angle;
+#endif
+	double tangent;
+	int new_delta_angle;
+
+	if (!find_pair(Crosshair.X, Crosshair.Y))
+		if (!find_pair(Ux, Uy))
+			return 0;
+
+	if (within(the_line->Point1.X, the_line->Point1.Y, x, y, the_line->Thickness)) {
+		ex = the_line->Point2.X;
+		ey = the_line->Point2.Y;
+		the_line->Point2.X = the_line->Point1.X;
+		the_line->Point2.Y = the_line->Point1.Y;
+		the_line->Point1.X = ex;
+		the_line->Point1.Y = ey;
+	}
+	else if (!within(the_line->Point2.X, the_line->Point2.Y, x, y, the_line->Thickness)) {
+#if TRACE1
+		printf("Line endpoint not at cursor\n");
+#endif
+		return 1;
+	}
+	ex = the_line->Point1.X;
+	ey = the_line->Point1.Y;
+
+	cx = the_arc->X;
+	cy = the_arc->Y;
+	if (arc_endpoint_is(the_arc, the_arc->StartAngle, x, y)) {
+		ChangeArcAngles(CURRENT, the_arc, the_arc->StartAngle + the_arc->Delta, -the_arc->Delta);
+	}
+	else if (!arc_endpoint_is(the_arc, the_arc->StartAngle + the_arc->Delta, x, y)) {
+#if TRACE1
+		printf("arc not endpoints\n");
+#endif
+		return 1;
+	}
+
+	if (within(cx, cy, ex, ey, the_arc->Width * 2)) {
+#if TRACE1
+		printf("line ends inside arc\n");
+#endif
+		return 1;
+	}
+
+	if (the_arc->Delta > 0)
+		arc_angle = the_arc->StartAngle + the_arc->Delta + 90;
+	else
+		arc_angle = the_arc->StartAngle + the_arc->Delta - 90;
+	base_angle = r2d(atan2(ey - cy, cx - ex));
+
+	tangent = r2d(acos(the_arc->Width / Distance(cx, cy, ex, ey)));
+
+#if TRACE1
+	line_angle = r2d(atan2(ey - y, x - ex));
+	rel_angle = line_angle - arc_angle;
+	printf("arc %g line %g rel %g base %g\n", arc_angle, line_angle, rel_angle, base_angle);
+	printf("tangent %g\n", tangent);
+
+	printf("arc was start %ld end %ld\n", the_arc->StartAngle, the_arc->StartAngle + the_arc->Delta);
+#endif
+
+	if (the_arc->Delta > 0)
+		arc_angle = base_angle - tangent;
+	else
+		arc_angle = base_angle + tangent;
+#if TRACE1
+	printf("new end angle %g\n", arc_angle);
+#endif
+
+	new_delta_angle = arc_angle - the_arc->StartAngle;
+	if (new_delta_angle > 180)
+		new_delta_angle -= 360;
+	if (new_delta_angle < -180)
+		new_delta_angle += 360;
+	ChangeArcAngles(CURRENT, the_arc, the_arc->StartAngle, new_delta_angle);
+
+#if TRACE1
+	printf("arc now start %ld end %ld\n", the_arc->StartAngle, the_arc->StartAngle + new_delta_angle);
+#endif
+
+	arc_angle = the_arc->StartAngle + the_arc->Delta;
+	x = the_arc->X - the_arc->Width * cos(d2r(arc_angle)) + 0.5;
+	y = the_arc->Y + the_arc->Height * sin(d2r(arc_angle)) + 0.5;
+
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, the_line, &(the_line->Point2), x - the_line->Point2.X, y - the_line->Point2.Y);
+
+	gui->invalidate_all();
+	IncrementUndoSerialNumber();
+
+	return 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*                          Global Puller                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+static const char globalpuller_syntax[] = "GlobalPuller()";
+
+static const char globalpuller_help[] = "Pull all traces tight.";
+
+/* %start-doc actions GlobalPuller
+
+%end-doc */
+
+/* Ok, here's the deal.  We look for the intersection of two traces.
+   The triangle formed by those traces is searched for things we need
+   to avoid. From the other two corners of the triangle, we compute
+   the angle to each obstacle, and remember the ones closest to the
+   start angles.  If the two traces hit the same obstacle, we put in
+   the arc and we're done.  Else, we bring the traces up to the
+   obstacles and start again.
+
+   Note that we assume each start point is a tangent to an arc.  We
+   start with a radius of zero, but future steps use the arcs we
+   create as we go.
+
+   For obstacles, we list each round pin, pad, via, and line/arc
+   endpoints as points with a given radius.  For each square pin, pad,
+   via, and polygon points, we list each corner with a zero radius.
+   We also list arcs from their centerpoint.
+
+   We don't currently do anything to move vias, or intersections of
+   three or more traces.  In the future, three-way intersections will
+   be handles similarly to two-way - calculate the range of angles
+   valid from each of the three other endpoints, choose the angle
+   closest to making 120 degree angles at the center.  For four-way or
+   more intersections, we break them up into multiple three-way
+   intersections.
+
+   For simplicity, we only do the current layer at this time.  We will
+   also edit the lines and arcs in place so that the intersection is
+   always on the second point, and the other ends are always at
+   start+delta for arcs.
+
+   We also defer intersections which are blocked by other
+   intersections yet to be moved; the idea is to wait until those have
+   been moved so we don't end up with arcs that no longer wrap around
+   things.  At a later point, we may choose to pull arced corners in
+   also.
+
+   You'll see lots of variables of the form "foo_sign" which keep
+   track of which way things are pointing.  This is because everything
+   is relative to corners and arcs, not absolute directions.
+*/
+
+static int nloops, npulled;
+
+static void status()
+{
+	fprintf(stderr, "%6d loops, %d pulled   \r", nloops, npulled);
+}
+
+/* Extra data we need to temporarily attach to all lines and arcs.  */
+typedef struct End {
+	/* These point to "multi_next" if there are more than one.  */
+	struct Extra *next;
+	void *pin;
+	unsigned in_pin:1;
+	unsigned at_pin:1;
+	unsigned is_pad:1;
+	unsigned pending:1; /* set if this may be moved later */
+	int x, y;           /* arc endpoint */
+	/* If not NULL, points to End with pending==1 we're blocked on. */
+	struct End *waiting_for;
+} End;
+
+typedef struct Extra {
+	End start;
+	End end;
+	unsigned found:1;
+	unsigned deleted:1;
+	int type;
+	union {
+		LineType *line;
+		ArcType *arc;
+	} parent;
+} Extra;
+
+static Extra multi_next;
+static GHashTable *lines;
+static GHashTable *arcs;
+static int did_something;
+static int current_is_component, current_is_solder;
+
+/* If set, these are the pins/pads/vias that this path ends on.  */
+/* static void *start_pin_pad, *end_pin_pad; */
+
+#if TRACE1
+static void trace_paths();
+#endif
+static void mark_line_for_deletion(LineTypePtr);
+
+#define LINE2EXTRA(l)    ((Extra *)g_hash_table_lookup (lines, l))
+#define ARC2EXTRA(a)     ((Extra *)g_hash_table_lookup (arcs, a))
+#define EXTRA2LINE(e)    (e->parent.line)
+#define EXTRA2ARC(e)     (e->parent.arc)
+#define EXTRA_IS_LINE(e) (e->type == PCB_TYPE_LINE)
+#define EXTRA_IS_ARC(e)  (e->type == PCB_TYPE_ARC)
+
+static void unlink_end(Extra * x, Extra ** e)
+{
+	if (*e) {
+		if ((*e)->start.next == x) {
+#if TRACE1
+			printf("%d: unlink_end, was %p\n", __LINE__, (void *)(*e)->start.next);
+#endif
+			(*e)->start.next = &multi_next;
+		}
+		if ((*e)->end.next == x) {
+#if TRACE1
+			printf("%d: unlink_end, was %p\n", __LINE__, (void *)(*e)->start.next);
+#endif
+			(*e)->end.next = &multi_next;
+		}
+	}
+#if TRACE1
+	printf("%d: unlink_end, was %p\n", __LINE__, (void *)(*e));
+#endif
+	(*e) = &multi_next;
+}
+
+#if TRACE1
+
+static void clear_found_cb(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	extra->found = 0;
+}
+
+static void clear_found()
+{
+	g_hash_table_foreach(lines, (GHFunc) clear_found_cb, NULL);
+	g_hash_table_foreach(arcs, (GHFunc) clear_found_cb, NULL);
+}
+#endif
+
+static void fix_arc_extra(ArcTypePtr a, Extra * e)
+{
+#if TRACE1
+	printf("new arc angles %ld %ld\n", a->StartAngle, a->Delta);
+#endif
+	e->start.x = a->X - (a->Width * cos(d2r(a->StartAngle)) + 0.5);
+	e->start.y = a->Y + (a->Height * sin(d2r(a->StartAngle)) + 0.5);
+	e->end.x = a->X - (a->Width * cos(d2r(a->StartAngle + a->Delta)) + 0.5);
+	e->end.y = a->Y + (a->Height * sin(d2r(a->StartAngle + a->Delta)) + 0.5);
+#if TRACE1
+	pcb_printf("new X,Y is %#mD to %#mD\n", e->start.x, e->start.y, e->end.x, e->end.y);
+#endif
+}
+
+typedef struct {
+	void *me;
+	int x, y;
+	int is_arc;
+	Extra **extra_ptr;
+} FindPairCallbackStruct;
+
+#define NEAR(a,b) ((a) <= (b) + 2 && (a) >= (b) - 2)
+
+static r_dir_t find_pair_line_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+#if TRACE1
+	Extra *e = LINE2EXTRA(line);
+#endif
+	FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
+
+	if (line == fpcs->me)
+		return R_DIR_NOT_FOUND;
+#ifdef CHECK_LINE_PT_NEG
+	if (line->Point1.X < 0)
+		abort1();
+#endif
+#if TRACE1
+	pcb_printf(" - %p line %#mD or %#mD\n", (void *)e, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
+#endif
+	if ((NEAR(line->Point1.X, fpcs->x) && NEAR(line->Point1.Y, fpcs->y))
+			|| (NEAR(line->Point2.X, fpcs->x) && NEAR(line->Point2.Y, fpcs->y))) {
+		if (*fpcs->extra_ptr) {
+#if TRACE1
+			printf("multiple, was %p\n", (void *)*fpcs->extra_ptr);
+#endif
+			*fpcs->extra_ptr = &multi_next;
+		}
+		else {
+			*fpcs->extra_ptr = LINE2EXTRA(line);
+#if TRACE1
+			printf(" - next now %p\n", (void *)*fpcs->extra_ptr);
+#endif
+		}
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t find_pair_arc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	Extra *e = ARC2EXTRA(arc);
+	FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
+
+	if (arc == fpcs->me)
+		return R_DIR_NOT_FOUND;
+#if TRACE1
+	pcb_printf(" - %p arc %#mD or %#mD\n", (void *)e, e->start.x, e->start.y, e->end.x, e->end.y);
+#endif
+	if ((NEAR(e->start.x, fpcs->x) && NEAR(e->start.y, fpcs->y))
+			|| (NEAR(e->end.x, fpcs->x) && NEAR(e->end.y, fpcs->y))) {
+		if (*fpcs->extra_ptr) {
+#if TRACE1
+			printf("multiple, was %p\n", (void *)*fpcs->extra_ptr);
+#endif
+			*fpcs->extra_ptr = &multi_next;
+		}
+		else
+			*fpcs->extra_ptr = e;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static void find_pairs_1(void *me, Extra ** e, int x, int y)
+{
+	FindPairCallbackStruct fpcs;
+	BoxType b;
+
+	if (*e)
+		return;
+
+	fpcs.me = me;
+	fpcs.extra_ptr = e;
+	fpcs.x = x;
+	fpcs.y = y;
+#if TRACE1
+	pcb_printf("looking for %#mD\n", x, y);
+#endif
+	b.X1 = x - 10;
+	b.X2 = x + 10;
+	b.Y1 = y - 10;
+	b.Y2 = y + 10;
+	r_search(CURRENT->line_tree, &b, NULL, find_pair_line_callback, &fpcs, NULL);
+	r_search(CURRENT->arc_tree, &b, NULL, find_pair_arc_callback, &fpcs, NULL);
+}
+
+static int check_point_in_pin(PinTypePtr pin, int x, int y, End * e)
+{
+	int inside_p;
+	int t = (pin->Thickness + 1) / 2;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pin))
+		inside_p = (x >= pin->X - t && x <= pin->X + t && y >= pin->Y - t && y <= pin->Y + t);
+	else
+		inside_p = (Distance(pin->X, pin->Y, x, y) <= t);
+
+	if (inside_p) {
+		e->in_pin = 1;
+		if (pin->X == x && pin->Y == y)
+			e->at_pin = 1;
+		e->pin = pin;
+		return 1;
+	}
+	return 0;
+}
+
+static r_dir_t find_pair_pinline_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	PinTypePtr pin = (PinTypePtr) cl;
+	Extra *e = LINE2EXTRA(line);
+	int hits;
+
+#ifdef CHECK_LINE_PT_NEG
+	if (line->Point1.X < 0)
+		abort1();
+#endif
+
+	hits = check_point_in_pin(pin, line->Point1.X, line->Point1.Y, &(e->start));
+	hits += check_point_in_pin(pin, line->Point2.X, line->Point2.Y, &(e->end));
+
+	if (hits)
+		return R_DIR_NOT_FOUND;
+
+	/* See if the line passes through this pin.  */
+	/* FIXME: this assumes round pads, but it's good enough for square
+	   ones for now.  */
+	if (dist_lsp(line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, pin->X, pin->Y) <= pin->Thickness / 2) {
+#if TRACE1
+		pcb_printf("splitting line %#mD-%#mD because it passes through pin %#mD r%d\n",
+							 line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, pin->X, pin->Y, pin->Thickness / 2);
+#endif
+		unlink_end(e, &e->start.next);
+		unlink_end(e, &e->end.next);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t find_pair_pinarc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	PinTypePtr pin = (PinTypePtr) cl;
+	Extra *e = ARC2EXTRA(arc);
+	int hits;
+
+	hits = check_point_in_pin(pin, e->start.x, e->start.y, &(e->start));
+	hits += check_point_in_pin(pin, e->end.x, e->end.y, &(e->end));
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t check_point_in_pad(PadTypePtr pad, int x, int y, End * e)
+{
+	int inside_p;
+	int t;
+
+	pcb_printf("pad %#mD - %#mD t %#mS  vs  %#mD\n", pad->Point1.X, pad->Point1.Y,
+						 pad->Point2.X, pad->Point2.Y, pad->Thickness, x, y);
+	t = (pad->Thickness + 1) / 2;
+	if (TEST_FLAG(PCB_FLAG_SQUARE, pad)) {
+		inside_p = (x >= MIN(pad->Point1.X - t, pad->Point2.X - t)
+								&& x <= MAX(pad->Point1.X + t, pad->Point2.X + t)
+								&& y >= MIN(pad->Point1.Y - t, pad->Point2.Y - t)
+								&& y <= MAX(pad->Point1.Y + t, pad->Point2.Y + t));
+		printf(" - inside_p = %d\n", inside_p);
+	}
+	else {
+		if (pad->Point1.X == pad->Point2.X) {
+			inside_p = (x >= pad->Point1.X - t && x <= pad->Point1.X + t && y >= MIN(pad->Point1.Y, pad->Point2.Y)
+									&& y <= MAX(pad->Point1.Y, pad->Point2.Y));
+		}
+		else {
+			inside_p = (x >= MIN(pad->Point1.X, pad->Point2.X)
+									&& x <= MAX(pad->Point1.X, pad->Point2.X)
+									&& y >= pad->Point1.Y - t && y <= pad->Point1.Y + t);
+		}
+		if (!inside_p) {
+			if (Distance(pad->Point1.X, pad->Point1.Y, x, y) <= t || Distance(pad->Point2.X, pad->Point2.Y, x, y) <= t)
+				inside_p = 1;
+		}
+	}
+
+	if (inside_p) {
+		e->in_pin = 1;
+		if (pad->Point1.X == x && pad->Point1.Y == y)
+			e->at_pin = 1;
+		if (pad->Point2.X == x && pad->Point2.Y == y)
+			e->at_pin = 1;
+		e->pin = pad;
+		e->is_pad = 1;
+		return R_DIR_FOUND_CONTINUE;
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t find_pair_padline_callback(const BoxType * b, void *cl)
+{
+	LineTypePtr line = (LineTypePtr) b;
+	PadTypePtr pad = (PadTypePtr) cl;
+	Extra *e = LINE2EXTRA(line);
+	int hits;
+	double t;
+	int intersect;
+	double p1_d, p2_d;
+
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, pad)) {
+		if (!current_is_solder)
+			return R_DIR_NOT_FOUND;
+	}
+	else {
+		if (!current_is_component)
+			return R_DIR_NOT_FOUND;
+	}
+
+#ifdef CHECK_LINE_PT_NEG
+	if (line->Point1.X < 0)
+		abort1();
+#endif
+
+	hits = check_point_in_pad(pad, line->Point1.X, line->Point1.Y, &(e->start));
+	hits += check_point_in_pad(pad, line->Point2.X, line->Point2.Y, &(e->end));
+
+	if (hits)
+		return R_DIR_NOT_FOUND;
+
+	/* Ok, something strange.  The line intersects our space, but
+	   doesn't end in our space.  See if it just passes through us, and
+	   mark it anyway.  */
+
+	t = (pad->Thickness + 1) / 2;
+	/* FIXME: this is for round pads.  Good enough for now, but add
+	   square pad support later.  */
+	intersect = intersection_of_linesegs(pad->Point1.X, pad->Point1.Y,
+																			 pad->Point2.X, pad->Point2.Y,
+																			 line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, NULL, NULL);
+	p1_d = dist_lsp(line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, pad->Point1.X, pad->Point1.Y);
+	p2_d = dist_lsp(line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y, pad->Point2.X, pad->Point2.Y);
+
+	if (intersect || p1_d < t || p2_d < t) {
+		/* It does.  */
+		/* FIXME: we should split the line.  */
+#if TRACE1
+		pcb_printf("splitting line %#mD-%#mD because it passes through pad %#mD-%#mD r %#mS\n",
+							 line->Point1.X, line->Point1.Y,
+							 line->Point2.X, line->Point2.Y, pad->Point1.X, pad->Point1.Y, pad->Point2.X, pad->Point2.Y, pad->Thickness / 2);
+#endif
+		unlink_end(e, &e->start.next);
+		unlink_end(e, &e->end.next);
+	}
+
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t find_pair_padarc_callback(const BoxType * b, void *cl)
+{
+	ArcTypePtr arc = (ArcTypePtr) b;
+	PadTypePtr pad = (PadTypePtr) cl;
+	Extra *e = ARC2EXTRA(arc);
+	int hits;
+
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, pad)) {
+		if (!current_is_solder)
+			return R_DIR_NOT_FOUND;
+	}
+	else {
+		if (!current_is_component)
+			return R_DIR_NOT_FOUND;
+	}
+
+	hits = check_point_in_pad(pad, e->start.x, e->start.y, &(e->start));
+	hits += check_point_in_pad(pad, e->end.x, e->end.y, &(e->end));
+	return R_DIR_NOT_FOUND;
+}
+
+static void null_multi_next_ends(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	if (extra->start.next == &multi_next)
+		extra->start.next = NULL;
+
+	if (extra->end.next == &multi_next)
+		extra->end.next = NULL;
+}
+
+static Extra *new_line_extra(LineType * line)
+{
+	Extra *extra = g_slice_new0(Extra);
+	g_hash_table_insert(lines, line, extra);
+	extra->parent.line = line;
+	extra->type = PCB_TYPE_LINE;
+	return extra;
+}
+
+static Extra *new_arc_extra(ArcType * arc)
+{
+	Extra *extra = g_slice_new0(Extra);
+	g_hash_table_insert(arcs, arc, extra);
+	extra->parent.arc = arc;
+	extra->type = PCB_TYPE_ARC;
+	return extra;
+}
+
+static void find_pairs()
+{
+	ARC_LOOP(CURRENT); {
+		Extra *e = new_arc_extra(arc);
+		fix_arc_extra(arc, e);
+	} END_LOOP;
+
+	LINE_LOOP(CURRENT); {
+		new_line_extra(line);
+	} END_LOOP;
+
+	LINE_LOOP(CURRENT); {
+		Extra *e = LINE2EXTRA(line);
+		if (line->Point1.X >= 0) {
+			find_pairs_1(line, &e->start.next, line->Point1.X, line->Point1.Y);
+			find_pairs_1(line, &e->end.next, line->Point2.X, line->Point2.Y);
+		}
+	}
+	END_LOOP;
+
+	ARC_LOOP(CURRENT); {
+		Extra *e = ARC2EXTRA(arc);
+		if (!e->deleted) {
+			find_pairs_1(arc, &e->start.next, e->start.x, e->start.y);
+			find_pairs_1(arc, &e->end.next, e->end.x, e->end.y);
+		}
+	}
+	END_LOOP;
+
+	ALLPIN_LOOP(PCB->Data); {
+		BoxType box;
+		box.X1 = pin->X - pin->Thickness / 2;
+		box.Y1 = pin->Y - pin->Thickness / 2;
+		box.X2 = pin->X + pin->Thickness / 2;
+		box.Y2 = pin->Y + pin->Thickness / 2;
+		r_search(CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, pin, NULL);
+		r_search(CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, pin, NULL);
+	}
+	ENDALL_LOOP;
+
+	VIA_LOOP(PCB->Data); {
+		BoxType box;
+		box.X1 = via->X - via->Thickness / 2;
+		box.Y1 = via->Y - via->Thickness / 2;
+		box.X2 = via->X + via->Thickness / 2;
+		box.Y2 = via->Y + via->Thickness / 2;
+		r_search(CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, via, NULL);
+		r_search(CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, via, NULL);
+	}
+	END_LOOP;
+
+	ALLPAD_LOOP(PCB->Data); {
+		BoxType box;
+		box.X1 = MIN(pad->Point1.X, pad->Point2.X) - pad->Thickness / 2;
+		box.Y1 = MIN(pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2;
+		box.X2 = MAX(pad->Point1.X, pad->Point2.X) + pad->Thickness / 2;
+		box.Y2 = MAX(pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2;
+		r_search(CURRENT->line_tree, &box, NULL, find_pair_padline_callback, pad, NULL);
+		r_search(CURRENT->arc_tree, &box, NULL, find_pair_padarc_callback, pad, NULL);
+
+	}
+	ENDALL_LOOP;
+
+	g_hash_table_foreach(lines, (GHFunc) null_multi_next_ends, NULL);
+	g_hash_table_foreach(arcs, (GHFunc) null_multi_next_ends, NULL);
+}
+
+#define PROP_NEXT(e,n,f) 		\
+      if (f->next->start.next == e) {	\
+	e = f->next;			\
+	n = & e->start;			\
+	f = & e->end;			\
+      } else {				\
+	e = f->next;			\
+	n = & e->end;			\
+	f = & e->start; }
+
+static void propogate_ends_at(Extra * e, End * near, End * far)
+{
+	while (far->in_pin && far->pin == near->pin) {
+		far->in_pin = 0;
+		if (!far->next)
+			return;
+		PROP_NEXT(e, near, far);
+		near->in_pin = 0;
+	}
+}
+
+static void propogate_end_pin(Extra * e, End * near, End * far)
+{
+	void *pinpad = near->pin;
+	int ispad = near->is_pad;
+	while (far->next) {
+		PROP_NEXT(e, near, far);
+		if (near->pin == pinpad)
+			break;
+		near->pin = pinpad;
+		near->is_pad = ispad;
+	}
+}
+
+static void propogate_end_step1_cb(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	if (extra->start.next != NULL && extra->start.next == extra->end.next) {
+		extra->end.next = NULL;
+		mark_line_for_deletion((LineType *) ptr);
+	}
+
+	if (extra->start.at_pin)
+		propogate_ends_at(extra, &extra->start, &extra->end);
+
+	if (extra->end.at_pin)
+		propogate_ends_at(extra, &extra->end, &extra->start);
+}
+
+static void propogate_end_step2_cb(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	if (extra->start.in_pin) {
+#if TRACE1
+		printf("MULTI at %d: was %p\n", __LINE__, (void *)extra->start.next);
+#endif
+		extra->start.next = NULL;
+	}
+	if (extra->end.in_pin) {
+#if TRACE1
+		printf("MULTI at %d: was %p\n", __LINE__, (void *)extra->end.next);
+#endif
+		extra->end.next = NULL;
+	}
+}
+
+static void propogate_end_step3_cb(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	if (extra->start.next)
+		propogate_end_pin(extra, &extra->end, &extra->start);
+	if (extra->end.next)
+		propogate_end_pin(extra, &extra->start, &extra->end);
+}
+
+static void propogate_ends()
+{
+	/* First, shut of "in pin" when we have an "at pin".  We also clean
+	   up zero-length lines.  */
+	g_hash_table_foreach(lines, (GHFunc) propogate_end_step1_cb, NULL);
+
+	/* Now end all paths at pins/pads.  */
+	g_hash_table_foreach(lines, (GHFunc) propogate_end_step2_cb, NULL);
+
+	/* Now, propogate the pin/pad/vias along paths.  */
+	g_hash_table_foreach(lines, (GHFunc) propogate_end_step3_cb, NULL);
+}
+
+static Extra *last_pextra = 0;
+
+static void print_extra(Extra * e, Extra * prev)
+{
+	int which = 0;
+	if (e->start.next == last_pextra)
+		which = 1;
+	else if (e->end.next == last_pextra)
+		which = 2;
+	switch (which) {
+	case 0:
+		printf("%10p %10p %10p :", (void *)e, (void *)e->start.next, (void *)e->end.next);
+		break;
+	case 1:
+		printf("%10p \033[33m%10p\033[0m %10p :", (void *)e, (void *)e->start.next, (void *)e->end.next);
+		break;
+	case 2:
+		printf("%10p %10p \033[33m%10p\033[0m :", (void *)e, (void *)e->start.next, (void *)e->end.next);
+		break;
+	}
+	last_pextra = e;
+	printf(" %c%c", e->deleted ? 'd' : '-', e->found ? 'f' : '-');
+	printf(" s:%s%s%s%s",
+				 e->start.in_pin ? "I" : "-", e->start.at_pin ? "A" : "-", e->start.is_pad ? "P" : "-", e->start.pending ? "p" : "-");
+	printf(" e:%s%s%s%s ",
+				 e->end.in_pin ? "I" : "-", e->end.at_pin ? "A" : "-", e->end.is_pad ? "P" : "-", e->end.pending ? "p" : "-");
+
+	if (EXTRA_IS_LINE(e)) {
+		LineTypePtr line = EXTRA2LINE(e);
+		pcb_printf(" %p L %#mD-%#mD", (void *)line, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
+		printf("  %s %p %s %p\n", e->start.is_pad ? "pad" : "pin", (void *)e->start.pin, e->end.is_pad ? "pad" : "pin", (void *)e->end.pin);
+	}
+	else if (EXTRA_IS_ARC(e)) {
+		ArcTypePtr arc = EXTRA2ARC(e);
+		pcb_printf(" %p A %#mD-%#mD", (void *)arc, e->start.x, e->start.y, e->end.x, e->end.y);
+		pcb_printf(" at %#mD ang %ld,%ld\n", arc->X, arc->Y, arc->StartAngle, arc->Delta);
+	}
+	else if (e == &multi_next) {
+		printf("-- Multi-next\n");
+	}
+	else {
+		printf("-- Unknown extra: %p\n", (void *)e);
+	}
+}
+
+#if TRACE1
+static void trace_path(Extra * e)
+{
+	Extra *prev = 0;
+	if ((e->start.next && e->end.next)
+			|| (!e->start.next && !e->end.next))
+		return;
+	if (e->found)
+		return;
+	printf("- path -\n");
+	last_pextra = 0;
+	while (e) {
+		e->found = 1;
+		print_extra(e, prev);
+		if (e->start.next == prev) {
+			prev = e;
+			e = e->end.next;
+		}
+		else {
+			prev = e;
+			e = e->start.next;
+		}
+	}
+}
+
+static void trace_paths()
+{
+	Extra *e;
+
+	clear_found();
+	LINE_LOOP(CURRENT); {
+		e = LINE2EXTRA(line);
+		trace_path(e);
+	} END_LOOP;
+	ARC_LOOP(CURRENT); {
+		e = ARC2EXTRA(arc);
+		trace_path(e);
+	} END_LOOP;
+}
+#endif
+
+static void reverse_line(LineTypePtr line)
+{
+	Extra *e = LINE2EXTRA(line);
+	int x, y;
+	End etmp;
+
+	x = line->Point1.X;
+	y = line->Point1.Y;
+#if 1
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, line, &(line->Point1), line->Point2.X - line->Point1.X, line->Point2.Y - line->Point1.Y);
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, line, &(line->Point2), x - line->Point2.X, y - line->Point2.Y);
+#else
+	/* In theory, we should be using the above so that undo works.  */
+	line->Point1.X = line->Point2.X;
+	line->Point1.Y = line->Point2.Y;
+	line->Point2.X = x;
+	line->Point2.Y = y;
+#endif
+	memcpy(&etmp, &e->start, sizeof(End));
+	memcpy(&e->start, &e->end, sizeof(End));
+	memcpy(&e->end, &etmp, sizeof(End));
+}
+
+static void reverse_arc(ArcTypePtr arc)
+{
+	Extra *e = ARC2EXTRA(arc);
+	End etmp;
+
+#if 1
+	ChangeArcAngles(CURRENT, arc, arc->StartAngle + arc->Delta, -arc->Delta);
+#else
+	/* Likewise, see above.  */
+	arc->StartAngle += arc->Delta;
+	arc->Delta *= -1;
+#endif
+	memcpy(&etmp, &e->start, sizeof(End));
+	memcpy(&e->start, &e->end, sizeof(End));
+	memcpy(&e->end, &etmp, sizeof(End));
+}
+
+static void expand_box(BoxTypePtr b, int x, int y, int t)
+{
+	b->X1 = MIN(b->X1, x - t);
+	b->X2 = MAX(b->X2, x + t);
+	b->Y1 = MIN(b->Y1, y - t);
+	b->Y2 = MAX(b->Y2, y + t);
+}
+
+/* ---------------------------------------------------------------------- */
+/* These are the state variables for the intersection we're currently
+   working on. */
+
+/* what we're working with */
+static ArcTypePtr start_arc;
+static LineTypePtr start_line;
+static LineTypePtr end_line;
+static ArcTypePtr end_arc;
+static Extra *start_extra, *end_extra;
+static Extra *sarc_extra, *earc_extra;
+static void *start_pinpad, *end_pinpad;
+static int thickness;
+
+/* Pre-computed values.  Note that all values are computed according
+   to CARTESIAN coordinates, not PCB coordinates.  Do an up-down board
+   flip before wrapping your brain around the math.  */
+
+/* se_sign is positive when you make a right turn going from start to end. */
+/* sa_sign is positive when start's arc is on the same side of start as end.  */
+/* ea_sign is positive when end's arc is on the same side of end as start.  */
+/* sa_sign and ea_sign may be zero if there's no arc.  */
+static double se_sign, sa_sign, ea_sign;
+
+static double best_angle, start_angle, end_dist;
+/* arc radii are positive when they're on the same side as the things
+   we're interested in. */
+static int sa_r, ea_r;
+static int sa_x, sa_y;					/* start "arc" point */
+
+/* what we've found so far */
+static int fx, fy, fr, fp;
+static End *fp_end;
+static double fa;								/* relative angle */
+
+#define gp_point_(x,y,t,e, file,line) gp_point_2(x,y,t,e,0,0, file ":" #line)
+#define gp_point(x,y,t,e) gp_point_(x,y,t,e, __FILE__, __LINE__)
+
+static int gp_point_force(int x, int y, int t, End * e, int esa, int eda, int force, const char *name)
+{
+	double r, a, d;
+	int scx, scy, sr;
+	double base_angle, rel_angle, point_angle;
+
+#if TRACE1
+	pcb_printf("\033[34mgp_point_force %#mD %#mS via %s\033[0m\n", x, y, t, name);
+#endif
+
+	if (start_arc) {
+		scx = start_arc->X;
+		scy = start_arc->Y;
+		sr = start_arc->Width;
+	}
+	else {
+		scx = start_line->Point1.X;
+		scy = start_line->Point1.Y;
+		sr = 0;
+	}
+
+	r = t + thickness;
+
+	/* See if the point is inside our start arc. */
+	d = Distance(scx, scy, x, y);
+#if TRACE1
+	pcb_printf("%f = dist #mD to %#mD\n", d, scx, scy, x, y);
+	pcb_printf("sr %#mS r %f d %f\n", sr, r, d);
+#endif
+	if (d < sr - r) {
+#if TRACE1
+		printf("inside start arc, %f < %f\n", d, sr - r);
+#endif
+		return 0;
+	}
+	if (sr == 0 && d < r) {
+#if TRACE1
+		printf("start is inside arc, %f < %f\n", d, r);
+#endif
+		return 0;
+	}
+
+	/* Now for the same tricky math we needed for the single puller.
+	   sr and r are the radii for the two points scx,scy and x,y.  */
+
+	/* angle between points (NOT pcb arc angles) */
+	base_angle = atan2(y - scy, x - scx);
+#if TRACE1
+	pcb_printf("%.1f = atan2 (%#mS-%#mS = %#mS, %#mS-%#mS = %#mS)\n", r2d(base_angle), y, scy, y - scy, x, scx, x - scx);
+#endif
+
+	if ((sa_sign * sr - r) / d > 1 || (sa_sign * sr - r) / d < -1)
+		return 0;
+
+	/* Angle of tangent, relative to the angle between point centers.  */
+	rel_angle = se_sign * asin((sa_sign * sr - r) / d);
+#if TRACE1
+	printf("%.1f = %d * asin ((%d * %d - %f) / %f)\n", r2d(rel_angle), (int) se_sign, (int) sa_sign, sr, r, d);
+#endif
+
+	/* Absolute angle of tangent.  */
+	point_angle = base_angle + rel_angle;
+#if TRACE1
+	printf("base angle %.1f rel_angle %.1f point_angle %.1f\n", r2d(base_angle), r2d(rel_angle), r2d(point_angle));
+#endif
+
+	if (eda) {
+		/* Check arc angles */
+		double pa = point_angle;
+		double sa = d2r(180 - esa);
+		double da = d2r(-eda);
+
+		if (da < 0) {
+			sa = sa + da;
+			da = -da;
+		}
+
+		pa -= se_sign * M_PI / 2;
+		while (sa + da < pa)
+			sa += M_PI * 2;
+		while (sa > pa)
+			sa -= M_PI * 2;
+		if (sa + da < pa) {
+#if TRACE1
+			printf("arc doesn't apply: sa %.1f da %.1f pa %.1f\n", r2d(sa), r2d(da), r2d(pa));
+#endif
+			return 0;
+		}
+	}
+
+	a = point_angle - start_angle;
+	while (a > M_PI)
+		a -= M_PI * 2;
+	while (a < -M_PI)
+		a += M_PI * 2;
+#if TRACE1
+	printf(" - angle relative to S-E baseline is %.1f\n", r2d(a));
+#endif
+
+	if (!force && a * se_sign < -0.007) {
+		double new_r;
+#if TRACE1
+		printf("skipping, would increase angle (%f * %f)\n", a, se_sign);
+#endif
+		new_r = dist_lp(start_line->Point1.X, start_line->Point1.Y, start_line->Point2.X, start_line->Point2.Y, x, y);
+#if TRACE1
+		pcb_printf("point %#mD dist %#mS vs thickness %#mS\n", x, y, new_r, thickness);
+#endif
+		new_r -= thickness;
+		new_r = (int) new_r - 1;
+#if TRACE1
+		pcb_printf(" - new thickness %f old %#mS\n", new_r, t);
+#endif
+		if (new_r < t)
+			gp_point_force(x, y, new_r, e, esa, eda, 1, "gp_point_force");
+		return 0;
+	}
+
+#if TRACE1
+	printf("%f * %f < %f * %f ?\n", a, se_sign, best_angle, se_sign);
+#endif
+	if (a * se_sign == best_angle * se_sign) {
+		double old_d = Distance(start_line->Point1.X, start_line->Point1.Y,
+														fx, fy);
+		double new_d = Distance(start_line->Point1.X, start_line->Point1.Y,
+														x, y);
+		if (new_d > old_d) {
+			best_angle = a;
+			fx = x;
+			fy = y;
+			fr = r;
+			fa = a;
+			fp = e ? e->pending : 0;
+			fp_end = e;
+		}
+	}
+	else if (a * se_sign < best_angle * se_sign) {
+		best_angle = a;
+		fx = x;
+		fy = y;
+		fr = r;
+		fa = a;
+		fp = e ? e->pending : 0;
+		fp_end = e;
+	}
+
+	return 1;
+}
+
+static int gp_point_2(int x, int y, int t, End * e, int esa, int eda, const char *func)
+{
+	double sc, ec;
+	double sd, ed;
+
+	if (x == sa_x && y == sa_y)
+		return 0;
+
+#if TRACE1
+	pcb_printf("\033[34mgp_point %#mD %#mS via %s\033[0m\n", x, y, t, func);
+#endif
+
+	/* There are two regions we care about.  For points inside our
+	   triangle, we check the crosses against start_line and end_line to
+	   make sure the point is "inside" the triangle.  For points on the
+	   other side of the s-e line of the triangle, we check the dots to
+	   make sure it's between our endpoints.  */
+
+	/* See what side of the s-e line we're on */
+	sc = cross2d(start_line->Point1.X, start_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y, x, y);
+#if TRACE1
+	printf("s-e cross = %f\n", sc);
+#endif
+	if (t >= 0) {
+		if (same_sign(sc, se_sign)) {
+			/* Outside, check dots.  */
+
+			/* Ok, is it "in front of" our vectors? */
+			sd = dot2d(start_line->Point1.X, start_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y, x, y);
+#if TRACE1
+			printf("sd = %f\n", sd);
+#endif
+			if (sd <= 0)
+				return 0;
+
+			ed = dot2d(end_line->Point2.X, end_line->Point2.Y, start_line->Point1.X, start_line->Point1.Y, x, y);
+#if TRACE1
+			printf("ed = %f\n", ed);
+#endif
+			if (ed <= 0)
+				return 0;
+
+			sd = dist_lp(start_line->Point1.X, start_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y, x, y);
+			if (sd > t + thickness)
+				return 0;
+		}
+		else {
+			/* Inside, check crosses.  */
+
+			/* First off, is it on the correct side of the start line? */
+			sc = cross2d(start_line->Point1.X, start_line->Point1.Y, start_line->Point2.X, start_line->Point2.Y, x, y);
+#if TRACE1
+			printf("sc = %f\n", sc);
+#endif
+			if (!same_sign(sc, se_sign))
+				return 0;
+
+			/* Ok, is it on the correct side of the end line? */
+			ec = cross2d(end_line->Point1.X, end_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y, x, y);
+#if TRACE1
+			printf("ec = %f\n", ec);
+#endif
+			if (!same_sign(ec, se_sign))
+				return 0;
+		}
+	}
+
+#if TRACE1
+	printf("in range!\n");
+#endif
+
+	return gp_point_force(x, y, t, e, esa, eda, 0, func);
+}
+
+static r_dir_t gp_line_cb(const BoxType * b, void *cb)
+{
+	const LineTypePtr l = (LineTypePtr) b;
+	Extra *e = LINE2EXTRA(l);
+	if (l == start_line || l == end_line)
+		return R_DIR_NOT_FOUND;
+	if (e->deleted)
+		return R_DIR_NOT_FOUND;
+#ifdef CHECK_LINE_PT_NEG
+	if (l->Point1.X < 0)
+		abort1();
+#endif
+	if (!e->start.next || !EXTRA_IS_ARC(e->start.next))
+		gp_point(l->Point1.X, l->Point1.Y, l->Thickness / 2, &e->start);
+	if (!e->end.next || !EXTRA_IS_ARC(e->end.next))
+		gp_point(l->Point2.X, l->Point2.Y, l->Thickness / 2, &e->end);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t gp_arc_cb(const BoxType * b, void *cb)
+{
+	const ArcTypePtr a = (ArcTypePtr) b;
+	Extra *e = ARC2EXTRA(a);
+	if (a == start_arc || a == end_arc)
+		return R_DIR_NOT_FOUND;
+	if (e->deleted)
+		return R_DIR_NOT_FOUND;
+	gp_point_2(a->X, a->Y, a->Width + a->Thickness / 2, 0, a->StartAngle, a->Delta, "gp_arc_cb");
+	if (start_arc && a->X == start_arc->X && a->Y == start_arc->Y)
+		return R_DIR_NOT_FOUND;
+	if (end_arc && a->X != end_arc->X && a->Y != end_arc->Y)
+		return R_DIR_NOT_FOUND;
+
+	if (e->start.next || e->end.next)
+		return R_DIR_NOT_FOUND;
+
+	gp_point(e->start.x, e->start.y, a->Thickness / 2, 0);
+	gp_point(e->end.x, e->end.y, a->Thickness / 2, 0);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t gp_text_cb(const BoxType * b, void *cb)
+{
+	const TextTypePtr t = (TextTypePtr) b;
+	/* FIXME: drop in the actual text-line endpoints later. */
+	gp_point(t->BoundingBox.X1, t->BoundingBox.Y1, 0, 0);
+	gp_point(t->BoundingBox.X1, t->BoundingBox.Y2, 0, 0);
+	gp_point(t->BoundingBox.X2, t->BoundingBox.Y2, 0, 0);
+	gp_point(t->BoundingBox.X2, t->BoundingBox.Y1, 0, 0);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t gp_poly_cb(const BoxType * b, void *cb)
+{
+	int i;
+	const PolygonTypePtr p = (PolygonTypePtr) b;
+	for (i = 0; i < p->PointN; i++)
+		gp_point(p->Points[i].X, p->Points[i].Y, 0, 0);
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t gp_pin_cb(const BoxType * b, void *cb)
+{
+	const PinTypePtr p = (PinTypePtr) b;
+	int t2 = (p->Thickness + 1) / 2;
+
+	if (p == start_pinpad || p == end_pinpad)
+		return R_DIR_NOT_FOUND;
+
+	/* FIXME: we lump octagonal pins in with square; safe, but not
+	   optimal.  */
+	if (TEST_FLAG(PCB_FLAG_SQUARE, p) || TEST_FLAG(PCB_FLAG_OCTAGON, p)) {
+		gp_point(p->X - t2, p->Y - t2, 0, 0);
+		gp_point(p->X - t2, p->Y + t2, 0, 0);
+		gp_point(p->X + t2, p->Y + t2, 0, 0);
+		gp_point(p->X + t2, p->Y - t2, 0, 0);
+	}
+	else {
+		gp_point(p->X, p->Y, t2, 0);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static r_dir_t gp_pad_cb(const BoxType * b, void *cb)
+{
+	const PadTypePtr p = (PadTypePtr) b;
+	int t2 = (p->Thickness + 1) / 2;
+
+	if (p == start_pinpad || p == end_pinpad)
+		return R_DIR_NOT_FOUND;
+
+	if (TEST_FLAG(PCB_FLAG_ONSOLDER, p)) {
+		if (!current_is_solder)
+			return R_DIR_NOT_FOUND;
+	}
+	else {
+		if (!current_is_component)
+			return R_DIR_NOT_FOUND;
+	}
+
+	/* FIXME: we lump octagonal pads in with square; safe, but not
+	   optimal.  I don't think we even support octagonal pads.  */
+	if (TEST_FLAG(PCB_FLAG_SQUARE, p) || TEST_FLAG(PCB_FLAG_OCTAGON, p)) {
+		if (p->Point1.X == p->Point2.X) {
+			int y1 = MIN(p->Point1.Y, p->Point2.Y) - t2;
+			int y2 = MAX(p->Point1.Y, p->Point2.Y) + t2;
+
+			gp_point(p->Point1.X - t2, y1, 0, 0);
+			gp_point(p->Point1.X - t2, y2, 0, 0);
+			gp_point(p->Point1.X + t2, y1, 0, 0);
+			gp_point(p->Point1.X + t2, y2, 0, 0);
+		}
+		else {
+			int x1 = MIN(p->Point1.X, p->Point2.X) - t2;
+			int x2 = MAX(p->Point1.X, p->Point2.X) + t2;
+
+			gp_point(x1, p->Point1.Y - t2, 0, 0);
+			gp_point(x2, p->Point1.Y - t2, 0, 0);
+			gp_point(x1, p->Point1.Y + t2, 0, 0);
+			gp_point(x2, p->Point1.Y + t2, 0, 0);
+		}
+	}
+	else {
+		gp_point(p->Point1.X, p->Point1.Y, t2, 0);
+		gp_point(p->Point2.X, p->Point2.Y, t2, 0);
+	}
+	return R_DIR_NOT_FOUND;
+}
+
+static LineTypePtr create_line(LineTypePtr sample, int x1, int y1, int x2, int y2)
+{
+#if TRACE1
+	Extra *e;
+	pcb_printf("create_line from %#mD to %#mD\n", x1, y1, x2, y2);
+#endif
+	LineTypePtr line = CreateNewLineOnLayer(CURRENT, x1, y1, x2, y2,
+																					sample->Thickness, sample->Clearance, sample->Flags);
+	AddObjectToCreateUndoList(PCB_TYPE_LINE, CURRENT, line, line);
+
+#if TRACE1
+	e =
+#endif
+		new_line_extra(line);
+#if TRACE1
+	printf(" - line extra is %p\n", (void *)e);
+#endif
+	return line;
+}
+
+static ArcTypePtr create_arc(LineTypePtr sample, int x, int y, int r, int sa, int da)
+{
+	Extra *e;
+	ArcTypePtr arc;
+
+	if (r % 100 == 1)
+		r--;
+	if (r % 100 == 99)
+		r++;
+#if TRACE1
+	pcb_printf("create_arc at %#mD r %#mS sa %d delta %d\n", x, y, r, sa, da);
+#endif
+	arc = CreateNewArcOnLayer(CURRENT, x, y, r, r, sa, da, sample->Thickness, sample->Clearance, sample->Flags);
+	if (arc == 0) {
+		arc = CreateNewArcOnLayer(CURRENT, x, y, r, r, sa, da * 2, sample->Thickness, sample->Clearance, sample->Flags);
+	}
+	AddObjectToCreateUndoList(PCB_TYPE_ARC, CURRENT, arc, arc);
+
+	if (!arc)
+		longjmp(abort_buf, 1);
+
+	e = new_arc_extra(arc);
+#if TRACE1
+	printf(" - arc extra is %p\n", (void *)e);
+#endif
+	fix_arc_extra(arc, e);
+	return arc;
+}
+
+static void unlink_extras(Extra * e)
+{
+#if TRACE1
+	fprintf(stderr, "unlink %p\n", (void *)e);
+	print_extra(e, 0);
+#endif
+	if (e->start.next) {
+#if TRACE1
+		print_extra(e->start.next, 0);
+#endif
+		if (e->start.next->start.next == e) {
+#if TRACE1
+			fprintf(stderr, " - %p->start points to me\n", (void *)e->start.next);
+#endif
+			e->start.next->start.next = e->end.next;
+		}
+		else if (e->start.next->end.next == e) {
+#if TRACE1
+			fprintf(stderr, " - %p->end points to me\n", (void *)e->start.next);
+#endif
+			e->start.next->end.next = e->end.next;
+		}
+		else {
+			fprintf(stderr, " - %p doesn't point to me!\n", (void *)e->start.next);
+			abort();
+		}
+	}
+	if (e->end.next) {
+#if TRACE1
+		print_extra(e->end.next, 0);
+#endif
+		if (e->end.next->start.next == e) {
+#if TRACE1
+			fprintf(stderr, " - %p->end points to me\n", (void *)e->end.next);
+#endif
+			e->end.next->start.next = e->start.next;
+		}
+		else if (e->end.next->end.next == e) {
+#if TRACE1
+			fprintf(stderr, " - %p->end points to me\n", (void *)e->end.next);
+#endif
+			e->end.next->end.next = e->start.next;
+		}
+		else {
+			fprintf(stderr, " - %p doesn't point to me!\n", (void *)e->end.next);
+			abort();
+		}
+	}
+	e->start.next = e->end.next = 0;
+}
+
+static void mark_line_for_deletion(LineTypePtr l)
+{
+	Extra *e = LINE2EXTRA(l);
+	if (e->deleted) {
+		fprintf(stderr, "double delete?\n");
+		abort();
+	}
+	e->deleted = 1;
+	unlink_extras(e);
+#if TRACE1
+	pcb_printf("Marked line %p for deletion %#mD to %#mD\n", (void *)e, l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
+#endif
+#if 0
+	if (l->Point1.X < 0) {
+		fprintf(stderr, "double neg move?\n");
+		abort();
+	}
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, l, &(l->Point1), -1 - l->Point1.X, -1 - l->Point1.Y);
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, l, &(l->Point2), -1 - l->Point2.X, -1 - l->Point2.Y);
+#endif
+}
+
+static void mark_arc_for_deletion(ArcTypePtr a)
+{
+	Extra *e = ARC2EXTRA(a);
+	e->deleted = 1;
+	unlink_extras(e);
+#if TRACE1
+	printf("Marked arc %p for deletion %ld < %ld\n", (void *)e, a->StartAngle, a->Delta);
+#endif
+}
+
+/* Given a starting line, which may be attached to an arc, and which
+   intersects with an ending line, which also may be attached to an
+   arc, maybe pull them.  We assume start_line is attached to the arc
+   via Point1, and attached to the end line via Point2.  Likewise, we
+   make end_line attach to the start_line via Point1 and the arc via
+   Point 2.  We also make the arcs attach on the Delta end, not the
+   Start end.  Here's a picture:
+
+     S            S+D  P1            P2   P1          P2  S+D          S
+   *--- start_arc ---*--- start_line ---*--- end_line ---*--- end_arc ---*
+     S             E   S              E   S            E   E           S
+*/
+
+static void maybe_pull_1(LineTypePtr line)
+{
+	BoxType box;
+	/* Line half-thicknesses, including line space */
+	int ex, ey;
+	LineTypePtr new_line;
+	Extra *new_lextra;
+	ArcTypePtr new_arc;
+	Extra *new_aextra;
+	double abs_angle;
+
+	start_line = line;
+	start_extra = LINE2EXTRA(start_line);
+	end_extra = start_extra->end.next;
+	end_line = EXTRA2LINE(end_extra);
+	if (end_extra->deleted) {
+		start_extra->end.pending = 0;
+		return;
+	}
+
+	if (end_extra->end.next == start_extra)
+		reverse_line(end_line);
+
+	if (start_extra->start.next && EXTRA_IS_ARC(start_extra->start.next)) {
+		sarc_extra = start_extra->start.next;
+		start_arc = EXTRA2ARC(sarc_extra);
+		if (sarc_extra->start.next == start_extra)
+			reverse_arc(start_arc);
+	}
+	else {
+		start_arc = 0;
+		sarc_extra = 0;
+	}
+
+	if (end_extra->end.next && EXTRA_IS_ARC(end_extra->end.next)) {
+		earc_extra = end_extra->end.next;
+		end_arc = EXTRA2ARC(earc_extra);
+		if (earc_extra->start.next == end_extra)
+			reverse_arc(end_arc);
+	}
+	else {
+		end_arc = 0;
+		earc_extra = 0;
+	}
+
+#if TRACE1
+	printf("maybe_pull_1 %p %p %p %p\n", (void *)sarc_extra, (void *)start_extra, (void *)end_extra, (void *)earc_extra);
+	if (sarc_extra)
+		print_extra(sarc_extra, 0);
+	print_extra(start_extra, 0);
+	print_extra(end_extra, 0);
+	if (earc_extra)
+		print_extra(earc_extra, 0);
+
+	if (start_extra->deleted || end_extra->deleted || (sarc_extra && sarc_extra->deleted)
+			|| (earc_extra && earc_extra->deleted)) {
+		printf(" one is deleted?\n");
+		fflush(stdout);
+		abort();
+	}
+#endif
+
+	if (!start_extra->end.pending)
+		return;
+#if 0
+	if (start_extra->end.waiting_for && start_extra->end.waiting_for->pending)
+		return;
+#endif
+
+	if (start_line->Thickness != end_line->Thickness)
+		return;
+	thickness = (start_line->Thickness + 1) / 2 + PCB->Bloat;
+
+	/* At this point, our expectations are all met.  */
+
+	box.X1 = start_line->Point1.X - thickness;
+	box.X2 = start_line->Point1.X + thickness;
+	box.Y1 = start_line->Point1.Y - thickness;
+	box.Y2 = start_line->Point1.Y + thickness;
+	expand_box(&box, start_line->Point2.X, start_line->Point2.Y, thickness);
+	expand_box(&box, end_line->Point2.X, end_line->Point2.Y, thickness);
+	if (start_arc)
+		expand_box(&box, sarc_extra->start.x, sarc_extra->start.y, start_arc->Thickness / 2);
+	if (end_arc)
+		expand_box(&box, earc_extra->start.x, earc_extra->start.y, end_arc->Thickness / 2);
+
+
+	se_sign = copysign(1, cross2d(start_line->Point1.X, start_line->Point1.Y,
+																start_line->Point2.X, start_line->Point2.Y, end_line->Point2.X, end_line->Point2.Y));
+	best_angle = se_sign * M_PI;
+	if (start_arc) {
+		sa_sign = copysign(1, -start_arc->Delta);
+		sa_sign *= se_sign;
+	}
+	else
+		sa_sign = 0;
+	if (end_arc) {
+		ea_sign = copysign(1, -end_arc->Delta);
+		ea_sign *= -se_sign;
+	}
+	else
+		ea_sign = 0;
+
+	start_angle = atan2(start_line->Point2.Y - start_line->Point1.Y, start_line->Point2.X - start_line->Point1.X);
+#if TRACE1
+	printf("se_sign %f sa_sign %f ea_sign %f best_angle %f start_angle %f\n",
+				 se_sign, sa_sign, ea_sign, r2d(best_angle), r2d(start_angle));
+#endif
+
+	if (start_arc) {
+		sa_x = start_arc->X;
+		sa_y = start_arc->Y;
+		if (same_sign(start_arc->Delta, se_sign))
+			sa_r = -start_arc->Width;
+		else
+			sa_r = start_arc->Width;
+	}
+	else {
+		sa_x = start_line->Point1.X;
+		sa_y = start_line->Point1.Y;
+		sa_r = 0;
+	}
+
+	if (end_arc) {
+		if (ea_sign < 0)
+			ea_r = end_arc->Width;
+		else
+			ea_r = -end_arc->Width;
+	}
+	else
+		ea_r = 0;
+
+#if TRACE1
+	trace_path(sarc_extra ? sarc_extra : start_extra);
+#endif
+
+	if (end_arc) {
+		gp_point_force(end_arc->X, end_arc->Y, -ea_r - thickness, 0, 0, 0, 1, "end arc");
+		ex = end_arc->X;
+		ey = end_arc->Y;
+	}
+	else {
+		gp_point_force(end_line->Point2.X, end_line->Point2.Y, -thickness, 0, 0, 0, 1, "end arc");
+		ex = end_line->Point2.X;
+		ey = end_line->Point2.Y;
+	}
+
+	fx = ex;
+	fy = ey;
+	if (fx < 0) {
+		pcb_fprintf(stderr, "end line corrupt? f is %#mD\n", fx, fy);
+		print_extra(end_extra, 0);
+		if (earc_extra)
+			print_extra(earc_extra, 0);
+		abort();
+	}
+
+	end_dist = Distance(end_line->Point1.X, end_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y);
+
+	start_pinpad = start_extra->start.pin;
+	end_pinpad = start_extra->end.pin;
+	fp = 0;
+
+	r_search(CURRENT->line_tree, &box, NULL, gp_line_cb, 0, NULL);
+	r_search(CURRENT->arc_tree, &box, NULL, gp_arc_cb, 0, NULL);
+	r_search(CURRENT->text_tree, &box, NULL, gp_text_cb, 0, NULL);
+	r_search(CURRENT->polygon_tree, &box, NULL, gp_poly_cb, 0, NULL);
+	r_search(PCB->Data->pin_tree, &box, NULL, gp_pin_cb, 0, NULL);
+	r_search(PCB->Data->via_tree, &box, NULL, gp_pin_cb, 0, NULL);
+	r_search(PCB->Data->pad_tree, &box, NULL, gp_pad_cb, 0, NULL);
+
+	/* radians, absolute angle of (at the moment) the start_line */
+	abs_angle = fa + start_angle;
+
+#if TRACE1
+	pcb_printf("\033[43;30mBest: at %#mD r %#mS, angle %.1f fp %d\033[0m\n", fx, fy, fr, r2d(fa), fp);
+#endif
+
+#if 0
+	if (fa > M_PI / 2 || fa < -M_PI / 2) {
+		SET_FLAG(PCB_FLAG_FOUND, line);
+		longjmp(abort_buf, 1);
+	}
+#endif
+
+	if (fp) {
+		start_extra->end.waiting_for = fp_end;
+		return;
+	}
+	start_extra->end.pending = 0;
+
+	/* Step 0: check for merged arcs (special case).  */
+
+	if (fx == ex && fy == ey && start_arc && end_arc && start_arc->X == end_arc->X && start_arc->Y == end_arc->Y) {
+		/* Merge arcs */
+		int new_delta;
+
+		new_delta = end_arc->StartAngle - start_arc->StartAngle;
+		if (start_arc->Delta > 0) {
+			while (new_delta > 360)
+				new_delta -= 360;
+			while (new_delta < 0)
+				new_delta += 360;
+		}
+		else {
+			while (new_delta < -360)
+				new_delta += 360;
+			while (new_delta > 0)
+				new_delta -= 360;
+		}
+#if TRACE1
+		pcb_printf("merging arcs at %#mS nd %d\n", start_arc->X, start_arc->Y, new_delta);
+		print_extra(sarc_extra, 0);
+		print_extra(earc_extra, 0);
+#endif
+		mark_arc_for_deletion(end_arc);
+		mark_line_for_deletion(start_line);
+		mark_line_for_deletion(end_line);
+		ChangeArcAngles(CURRENT, start_arc, start_arc->StartAngle, new_delta);
+		fix_arc_extra(start_arc, sarc_extra);
+		did_something++;
+		return;
+	}
+
+	/* Step 1: adjust start_arc's angles and move start_line's Point1 to
+	   match it. */
+
+	if (start_arc) {
+		double new_delta;
+		int del_arc = 0;
+
+		/* We must always round towards "larger arcs", whichever way that is. */
+		new_delta = 180 - r2d(abs_angle);
+#if TRACE1
+		printf("new_delta starts at %d vs %d < %d\n", (int) new_delta, (int) start_arc->StartAngle, (int) start_arc->Delta);
+#endif
+		if (start_arc->Delta < 0)
+			new_delta = (int) (new_delta) + 90;
+		else
+			new_delta = (int) new_delta - 90;
+		new_delta = new_delta - start_arc->StartAngle;
+		while (new_delta > start_arc->Delta + 180)
+			new_delta -= 360;
+		while (new_delta < start_arc->Delta - 180)
+			new_delta += 360;
+#if TRACE1
+		printf("new_delta adjusts to %d\n", (int) new_delta);
+		printf("fa = %f, new_delta ends at %.1f vs start %d\n", fa, new_delta, (int) start_arc->StartAngle);
+#endif
+
+		if (new_delta * start_arc->Delta <= 0)
+			del_arc = 1;
+
+		ChangeArcAngles(CURRENT, start_arc, start_arc->StartAngle, new_delta);
+		fix_arc_extra(start_arc, sarc_extra);
+		MoveObject(PCB_TYPE_LINE_POINT, CURRENT, start_line, &(start_line->Point1),
+							 sarc_extra->end.x - start_line->Point1.X, sarc_extra->end.y - start_line->Point1.Y);
+
+		if (del_arc) {
+			MoveObject(PCB_TYPE_LINE_POINT, CURRENT, start_line, &(start_line->Point1),
+								 sarc_extra->start.x - start_line->Point1.X, sarc_extra->start.y - start_line->Point1.Y);
+			mark_arc_for_deletion(start_arc);
+		}
+	}
+
+	/* Step 1.5: If the "obstacle" is right at the end, ignore it.  */
+
+	{
+		double oa = start_angle + fa - M_PI / 2 * se_sign;
+		double ox = fx + fr * cos(oa);
+		double oy = fy + fr * sin(oa);
+#if TRACE1
+		pcb_printf("obstacle at %#mD angle %d = arc starts at %#mD\n", fx, fy, (int) r2d(oa), (int) ox, (int) oy);
+#endif
+
+		if (Distance(ox, oy, end_line->Point2.X, end_line->Point2.Y)
+				< fr * SIN1D) {
+			/* Pretend it doesn't exist.  */
+			fx = ex;
+			fy = ey;
+		}
+	}
+
+	/* Step 2: If we have no obstacles, connect start and end.  */
+
+#if TRACE1
+	pcb_printf("fx %#mS ex %#mS fy %#mS ey %#mS\n", fx, ex, fy, ey);
+#endif
+	if (fx == ex && fy == ey) {
+		/* No obstacles.  */
+#if TRACE1
+		printf("\033[32mno obstacles\033[0m\n");
+#endif
+		if (end_arc) {
+			int del_arc = 0;
+			int new_delta, end_angle, pcb_fa, end_change;
+
+			end_angle = end_arc->StartAngle + end_arc->Delta;
+			if (end_arc->Delta < 0)
+				end_angle -= 90;
+			else
+				end_angle += 90;
+
+			/* We must round so as to make the larger arc.  */
+			if (end_arc->Delta < 0)
+				pcb_fa = -r2d(start_angle + fa);
+			else
+				pcb_fa = 1 - r2d(start_angle + fa);
+			end_change = pcb_fa - end_angle;
+
+			while (end_change > 180)
+				end_change -= 360;
+			while (end_change < -180)
+				end_change += 360;
+			new_delta = end_arc->Delta + end_change;
+
+			if (new_delta * end_arc->Delta <= 0)
+				del_arc = 1;
+
+			ChangeArcAngles(CURRENT, end_arc, end_arc->StartAngle, new_delta);
+			fix_arc_extra(end_arc, earc_extra);
+			MoveObject(PCB_TYPE_LINE_POINT, CURRENT, start_line, &(start_line->Point2),
+								 earc_extra->end.x - start_line->Point2.X, earc_extra->end.y - start_line->Point2.Y);
+
+			if (del_arc) {
+				MoveObject(PCB_TYPE_LINE_POINT, CURRENT, start_line, &(start_line->Point2),
+									 earc_extra->start.x - start_line->Point2.X, earc_extra->start.y - start_line->Point2.Y);
+				mark_arc_for_deletion(end_arc);
+			}
+		}
+		else {
+			MoveObject(PCB_TYPE_LINE_POINT, CURRENT, start_line, &(start_line->Point2),
+								 end_line->Point2.X - start_line->Point2.X, end_line->Point2.Y - start_line->Point2.Y);
+		}
+		mark_line_for_deletion(end_line);
+		start_extra->end.pending = 1;
+
+#if TRACE1
+		printf("\033[35mdid_something: no obstacles\033[0m\n");
+#endif
+		did_something++;
+		npulled++;
+		status();
+		return;
+	}
+
+	/* Step 3: Compute the new intersection of start_line and end_line.  */
+
+	ex = start_line->Point1.X + cos(start_angle + fa) * 10000.0;
+	ey = start_line->Point1.Y + sin(start_angle + fa) * 10000.0;
+#if TRACE1
+	pcb_printf("temp point %#mS\n", ex, ey);
+	pcb_printf("intersect %#mS-%#mS with %#mS-%#mS\n",
+						 start_line->Point1.X, start_line->Point1.Y,
+						 ex, ey, end_line->Point1.X, end_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y);
+#endif
+	if (!intersection_of_lines(start_line->Point1.X, start_line->Point1.Y,
+														 ex, ey,
+														 end_line->Point1.X, end_line->Point1.Y, end_line->Point2.X, end_line->Point2.Y, &ex, &ey)) {
+		ex = end_line->Point2.X;
+		ey = end_line->Point2.Y;
+	}
+#if TRACE1
+	pcb_printf("new point %#mS\n", ex, ey);
+#endif
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, end_line, &(end_line->Point1), ex - end_line->Point1.X, ey - end_line->Point1.Y);
+
+	/* Step 4: Split start_line at the obstacle and insert a zero-delta
+	   arc at it.  */
+
+	new_arc = create_arc(start_line, fx, fy, fr, 90 - (int) (r2d(start_angle + fa) + 0.5) + 90 + 90 * se_sign, -se_sign);
+	new_aextra = ARC2EXTRA(new_arc);
+
+	if (start_arc)
+		sarc_extra = ARC2EXTRA(start_arc);
+	if (end_arc)
+		earc_extra = ARC2EXTRA(end_arc);
+
+	MoveObject(PCB_TYPE_LINE_POINT, CURRENT, start_line, &(start_line->Point2),
+						 new_aextra->start.x - start_line->Point2.X, new_aextra->start.y - start_line->Point2.Y);
+
+	new_line = create_line(start_line, new_aextra->end.x, new_aextra->end.y, ex, ey);
+	start_extra = LINE2EXTRA(start_line);
+	new_lextra = LINE2EXTRA(new_line);
+	end_extra = LINE2EXTRA(end_line);
+
+	new_lextra->start.pin = start_extra->start.pin;
+	new_lextra->end.pin = start_extra->end.pin;
+	new_lextra->start.pending = 1;
+	new_lextra->end.pending = 1;
+
+	start_extra->end.next = new_aextra;
+	new_aextra->start.next = start_extra;
+	new_aextra->end.next = new_lextra;
+	new_lextra->start.next = new_aextra;
+	new_lextra->end.next = end_extra;
+	end_extra->start.next = new_lextra;
+
+	/* Step 5: Recurse.  */
+
+	did_something++;
+	npulled++;
+	status();
+#if TRACE0
+	printf("\033[35mdid_something: recursing\033[0m\n");
+	{
+		int i = gui->confirm_dialog("recurse?", 0);
+		printf("confirm = %d\n", i);
+		if (i == 0)
+			return;
+	}
+	printf("\n\033[33mRECURSING\033[0m\n\n");
+	IncrementUndoSerialNumber();
+#endif
+	maybe_pull_1(new_line);
+}
+
+/* Given a line with a end_next, attempt to pull both ends.  */
+static void maybe_pull(LineTypePtr line, Extra * e)
+{
+#if TRACE0
+	printf("maybe_pull: ");
+	print_extra(e, 0);
+#endif
+	if (e->end.next && EXTRA_IS_LINE(e->end.next)) {
+		maybe_pull_1(line);
+	}
+	else {
+		e->end.pending = 0;
+		if (e->start.next && EXTRA_IS_LINE(e->start.next)) {
+			reverse_line(line);
+			maybe_pull_1(line);
+		}
+		else
+			e->start.pending = 0;
+	}
+}
+
+static void validate_pair(Extra * e, End * end)
+{
+	if (!end->next)
+		return;
+	if (end->next->start.next == e)
+		return;
+	if (end->next->end.next == e)
+		return;
+	fprintf(stderr, "no backlink!\n");
+	print_extra(e, 0);
+	print_extra(end->next, 0);
+	abort();
+}
+
+static void validate_pair_cb(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	validate_pair(extra, &extra->start);
+	validate_pair(extra, &extra->end);
+}
+
+static void validate_pairs()
+{
+	g_hash_table_foreach(lines, (GHFunc) validate_pair_cb, NULL);
+#if TRACE1
+	printf("\narcs\n");
+#endif
+	g_hash_table_foreach(arcs, (GHFunc) validate_pair_cb, NULL);
+}
+
+static void FreeExtra(Extra * extra)
+{
+	g_slice_free(Extra, extra);
+}
+
+static void mark_ends_pending(LineType * line, Extra * extra, void *userdata)
+{
+	int *select_flags = userdata;
+	if (TEST_FLAGS(*select_flags, line)) {
+		extra->start.pending = 1;
+		extra->end.pending = 1;
+	}
+}
+
+#if TRACE1
+static void trace_print_extra(AnyObjectType * ptr, Extra * extra, void *userdata)
+{
+	last_pextra = (Extra *) 1;
+	print_extra(extra, 0);
+}
+
+static void trace_print_lines_arcs(void)
+{
+	printf("\nlines\n");
+	g_hash_table_foreach(lines, (GHFunc) trace_print_extra, NULL);
+
+	printf("\narcs\n");
+	g_hash_table_foreach(arcs, (GHFunc) trace_print_extra, NULL);
+
+	printf("\n");
+}
+#endif
+
+static int GlobalPuller(int argc, const char **argv, Coord x, Coord y)
+{
+	int select_flags = 0;
+
+	setbuf(stdout, 0);
+	nloops = 0;
+	npulled = 0;
+	printf("puller! %s\n", argc > 0 ? argv[0] : "");
+
+	if (argc > 0 && strcasecmp(argv[0], "selected") == 0)
+		select_flags = PCB_FLAG_SELECTED;
+	if (argc > 0 && strcasecmp(argv[0], "found") == 0)
+		select_flags = PCB_FLAG_FOUND;
+
+	printf("optimizing...\n");
+	/* This canonicalizes all the lines, and cleans up near-misses.  */
+	/* hid_actionl ("djopt", "puller", 0); */
+
+	current_is_solder = (GetLayerGroupNumberByPointer(CURRENT)
+											 == GetLayerGroupNumberByNumber(solder_silk_layer));
+	current_is_component = (GetLayerGroupNumberByPointer(CURRENT)
+													== GetLayerGroupNumberByNumber(component_silk_layer));
+
+	lines = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify) FreeExtra);
+	arcs = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify) FreeExtra);
+
+	printf("pairing...\n");
+	find_pairs();
+	validate_pairs();
+
+	g_hash_table_foreach(lines, (GHFunc) mark_ends_pending, &select_flags);
+
+#if TRACE1
+	trace_print_lines_arcs();
+#endif
+
+	propogate_ends();
+
+#if TRACE1
+	trace_print_lines_arcs();
+	trace_paths();
+#endif
+
+	printf("pulling...\n");
+	if (setjmp(abort_buf) == 0) {
+#if TRACE0
+		int old_did_something = -1;
+#endif
+		did_something = 1;
+		while (did_something) {
+			nloops++;
+			status();
+			did_something = 0;
+			LINE_LOOP(CURRENT); {
+				Extra *e = LINE2EXTRA(line);
+				if (e->deleted)
+					continue;
+#ifdef CHECK_LINE_PT_NEG
+				if (line->Point1.X < 0)
+					abort1();
+#endif
+				if (e->start.next || e->end.next)
+					maybe_pull(line, e);
+#if TRACE0
+				if (did_something != old_did_something) {
+					IncrementUndoSerialNumber();
+					old_did_something = did_something;
+					if (gui->confirm_dialog("more?", 0) == 0) {
+						did_something = 0;
+						break;
+					}
+				}
+#endif
+				/*gui->progress(0,0,0); */
+			}
+			END_LOOP;
+		}
+	}
+
+#if TRACE0
+	printf("\nlines\n");
+	g_hash_table_foreach(lines, (GHFunc) trace_print_extra, NULL);
+	printf("\narcs\n");
+	g_hash_table_foreach(arcs, (GHFunc) trace_print_extra, NULL);
+	printf("\n");
+	printf("\nlines\n");
+#endif
+
+	LINE_LOOP(CURRENT);
+	{
+		if (LINE2EXTRA(line)->deleted)
+			RemoveLine(CURRENT, line);
+	}
+	END_LOOP;
+
+	ARC_LOOP(CURRENT);
+	{
+		if (ARC2EXTRA(arc)->deleted)
+			RemoveArc(CURRENT, arc);
+	}
+	END_LOOP;
+
+	g_hash_table_unref(lines);
+	g_hash_table_unref(arcs);
+
+	IncrementUndoSerialNumber();
+	return 0;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*                             Actions                                       */
+/*                                                                           */
+/*****************************************************************************/
+
+HID_Action puller_action_list[] = {
+	{"Puller", "Click on a line-arc intersection or line segment", Puller,
+	 puller_help, puller_syntax}
+	,
+	{"GlobalPuller", 0, GlobalPuller,
+	 globalpuller_help, globalpuller_syntax}
+};
+
+static const char *puller_cookie = "puller plugin";
+
+REGISTER_ACTIONS(puller_action_list, puller_cookie)
+
+static void hid_puller_uninit(void)
+{
+	hid_remove_actions_by_cookie(puller_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_puller_init(void)
+{
+	REGISTER_ACTIONS(puller_action_list, puller_cookie)
+	return hid_puller_uninit;
+}
diff --git a/src_plugins/query/Makefile b/src_plugins/query/Makefile
new file mode 100644
index 0000000..1ab3e10
--- /dev/null
+++ b/src_plugins/query/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_query
+	
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/query/Plug.tmpasm b/src_plugins/query/Plug.tmpasm
new file mode 100644
index 0000000..8022689
--- /dev/null
+++ b/src_plugins/query/Plug.tmpasm
@@ -0,0 +1,21 @@
+put /local/pcb/mod {query}
+put /local/pcb/mod/OBJS [@
+ $(PLUGDIR)/query/query.o
+ $(PLUGDIR)/query/query_access.o
+ $(PLUGDIR)/query/query_act.o
+ $(PLUGDIR)/query/query_exec.o
+ $(PLUGDIR)/query/query_l.o
+ $(PLUGDIR)/query/query_y.o
+ $(PLUGDIR)/query/basic_fnc.o
+ $(PLUGDIR)/query/fields_sphash.o
+@]
+put /local/pcb/mod/YACC {$(PLUGDIR)/query/query_y}
+put /local/pcb/mod/LEX  {$(PLUGDIR)/query/query_l}
+put /local/pcb/mod/SPHASH  {$(PLUGDIR)/query/fields.sphash}
+
+
+switch /local/pcb/query/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/query/README b/src_plugins/query/README
new file mode 100644
index 0000000..751264e
--- /dev/null
+++ b/src_plugins/query/README
@@ -0,0 +1,6 @@
+pcb-rnd query language: execute expressions on objects and rules for the
+programmed drc.
+
+#state: WIP
+#default: disable
+#implements: (feature)
diff --git a/src_plugins/query/basic_fnc.c b/src_plugins/query/basic_fnc.c
new file mode 100644
index 0000000..1d58b2e
--- /dev/null
+++ b/src_plugins/query/basic_fnc.c
@@ -0,0 +1,42 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include "global.h"
+#include "data.h"
+#include "query_access.h"
+#include "query_exec.h"
+
+/* Query language - basic functions */
+static int fnc_llen(int argc, pcb_qry_val_t *argv, pcb_qry_val_t *res)
+{
+	if (argc != 1)
+		return -1;
+	if (argv[0].type != PCBQ_VT_LST)
+		PCB_QRY_RET_INV(res);
+	PCB_QRY_RET_INT(res, pcb_objlist_length(&argv[0].data.lst));
+}
+
+
+
+void pcb_qry_basic_fnc_init(void)
+{
+	pcb_qry_fnc_reg("llen", fnc_llen);
+}
diff --git a/src_plugins/query/fields.sphash b/src_plugins/query/fields.sphash
new file mode 100644
index 0000000..568f7cd
--- /dev/null
+++ b/src_plugins/query/fields.sphash
@@ -0,0 +1,38 @@
+a
+p
+x
+y
+cx
+cy
+x1
+y1
+x2
+y2
+thickness
+clearance
+layer
+element
+width
+height
+name
+visible
+position
+type
+angle
+start
+delta
+scale
+rotation
+string
+points
+hole
+mask
+number
+description
+value
+ID
+bbox
+type
+area
+refdes
+length
diff --git a/src_plugins/query/query.c b/src_plugins/query/query.c
new file mode 100644
index 0000000..2bc9476
--- /dev/null
+++ b/src_plugins/query/query.c
@@ -0,0 +1,294 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - common code for the compiled tree and plugin administration */
+
+#include "config.h"
+#include "global.h"
+#include <genht/hash.h>
+#include <genht/htsi.h>
+#include "conf.h"
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "plugins.h"
+#include "hid_init.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+#include "query.h"
+#include "fptr_cast.h"
+
+/******** tree helper ********/
+
+const char *type_name[PCBQ_nodetype_max] = {
+	"PCBQ_RULE",
+	"PCBQ_RNAME",
+	"PCBQ_EXPR_PROG",
+	"PCBQ_EXPR",
+	"PCBQ_ITER_CTX",
+	"PCBQ_OP_AND",
+	"PCBQ_OP_OR",
+	"PCBQ_OP_EQ",
+	"PCBQ_OP_NEQ",
+	"PCBQ_OP_GTEQ",
+	"PCBQ_OP_LTEQ",
+	"PCBQ_OP_GT",
+	"PCBQ_OP_LT",
+	"PCBQ_OP_ADD",
+	"PCBQ_OP_SUB",
+	"PCBQ_OP_MUL",
+	"PCBQ_OP_DIV",
+	"PCBQ_OP_MATCH",
+	"PCBQ_OP_NOT",
+	"PCBQ_FIELD",
+	"PCBQ_FIELD_OF",
+	"PCBQ_LISTVAR",
+	"PCBQ_VAR",
+	"PCBQ_FNAME",
+	"PCBQ_FCALL",
+	"PCBQ_DATA_COORD",
+	"PCBQ_DATA_DOUBLE",
+	"PCBQ_DATA_STRING",
+	"PCBQ_DATA_REGEX",
+	"PCBQ_DATA_CONST",
+	"PCBQ_DATA_INVALID"
+};
+
+char *pcb_query_sprint_val(pcb_qry_val_t *val)
+{
+	switch(val->type) {
+		case PCBQ_VT_VOID:   return pcb_strdup("<void>");
+		case PCBQ_VT_COORD:  return pcb_strdup_printf("%mI=%$mH", val->data.crd, val->data.crd);
+		case PCBQ_VT_DOUBLE: return pcb_strdup_printf("%f", val->data.dbl);
+		case PCBQ_VT_STRING: return pcb_strdup_printf("\"%s\"", val->data.str);
+		case PCBQ_VT_OBJ:    return pcb_strdup("<obj>");
+		case PCBQ_VT_LST:    return pcb_strdup("<lst>");
+	}
+	return pcb_strdup("<invalid>");
+}
+
+const char *pcb_qry_nodetype_name(pcb_qry_nodetype_t ntype)
+{
+	int type = ntype;
+	if ((type < 0) || (type >= PCBQ_nodetype_max))
+		return "<invalid>";
+	return type_name[type];
+}
+
+pcb_qry_node_t *pcb_qry_n_alloc(pcb_qry_nodetype_t ntype)
+{
+	pcb_qry_node_t *nd = calloc(sizeof(pcb_qry_node_t), 1);
+	nd->type = ntype;
+	return nd;
+}
+
+pcb_qry_node_t *pcb_qry_n_insert(pcb_qry_node_t *parent, pcb_qry_node_t *ch)
+{
+	ch->next = parent->data.children;
+	parent->data.children = ch;
+	ch->parent = parent;
+	return parent;
+}
+
+static char ind[] = "                                                                                ";
+void pcb_qry_dump_tree_(const char *prefix, int level, pcb_qry_node_t *nd, pcb_query_iter_t *it_ctx)
+{
+	pcb_qry_node_t *n;
+	if (level < sizeof(ind))  ind[level] = '\0';
+	printf("%s%s%s    ", prefix, ind, pcb_qry_nodetype_name(nd->type));
+	switch(nd->type) {
+		case PCBQ_DATA_INVALID:pcb_printf("%s%s invalid (literal)\n", prefix, ind); break;
+		case PCBQ_DATA_COORD:  pcb_printf("%s%s %mI (%$mm)\n", prefix, ind, nd->data.crd, nd->data.crd); break;
+		case PCBQ_DATA_DOUBLE: pcb_printf("%s%s %f\n", prefix, ind, nd->data.dbl); break;
+		case PCBQ_DATA_CONST:  pcb_printf("%s%s %s\n", prefix, ind, nd->data.str); break;
+		case PCBQ_ITER_CTX:    pcb_printf("%s%s vars=%d\n", prefix, ind, nd->data.iter_ctx->num_vars); break;
+		case PCBQ_VAR:
+			pcb_printf("%s%s ", prefix, ind);
+			if ((it_ctx != NULL) && (nd->data.crd < it_ctx->num_vars)) {
+				if (it_ctx->it == NULL)
+					pcb_qry_iter_init(it_ctx);
+				printf("%s\n", it_ctx->vn[nd->data.crd]);
+			}
+			else
+				printf("<invalid:%d>\n", nd->data.crd);
+			break;
+		case PCBQ_FNAME:
+			{
+				const char *name = pcb_qry_fnc_name(nd->data.fnc);
+				if (name == NULL)
+					pcb_printf("%s%s <unknown>\n", prefix, ind);
+				else
+					pcb_printf("%s%s %s()\n", prefix, ind, name);
+			}
+			break;
+		case PCBQ_FIELD:
+		case PCBQ_LISTVAR:
+		case PCBQ_DATA_REGEX:
+		case PCBQ_DATA_STRING: pcb_printf("%s%s '%s'\n", prefix, ind, nd->data.str); break;
+		default:
+			printf("\n");
+			if (level < sizeof(ind))  ind[level] = ' ';
+			for(n = nd->data.children; n != NULL; n = n->next) {
+				if (n->parent != nd)
+					printf("#parent# ");
+				pcb_qry_dump_tree_(prefix, level+1, n, it_ctx);
+			}
+			return;
+	}
+	if (level < sizeof(ind))  ind[level] = ' ';
+}
+
+pcb_query_iter_t *pcb_qry_find_iter(pcb_qry_node_t *node)
+{
+	for(; node != NULL;node = node->parent) {
+		if (node->type == PCBQ_EXPR_PROG) {
+			if (node->data.children->type == PCBQ_ITER_CTX)
+				return node->data.children->data.iter_ctx;
+		}
+		if (node->type == PCBQ_EXPR_PROG) {
+			if (node->data.children->type == PCBQ_ITER_CTX)
+				return node->data.children->data.iter_ctx;
+		}
+	}
+
+	return NULL;
+}
+
+void pcb_qry_dump_tree(const char *prefix, pcb_qry_node_t *top)
+{
+	pcb_query_iter_t *iter_ctx = pcb_qry_find_iter(top);
+
+	if (iter_ctx == NULL)
+		printf("<can't find iter context>\n");
+
+	for(; top != NULL; top = top->next)
+		pcb_qry_dump_tree_(prefix, 0, top, iter_ctx);
+}
+
+/******** iter admin ********/
+pcb_query_iter_t *pcb_qry_iter_alloc(void)
+{
+	pcb_query_iter_t *it = calloc(1, sizeof(pcb_query_iter_t));
+	htsi_init(&it->names, strhash, strkeyeq);
+	return it;
+}
+
+
+int pcb_qry_iter_var(pcb_query_iter_t *it, const char *varname, int alloc)
+{
+	htsi_entry_t *e = htsi_getentry(&it->names, varname);
+
+	if (e != NULL)
+		return e->value;
+
+	if (!alloc)
+		return -1;
+
+	htsi_set(&it->names, pcb_strdup(varname), it->num_vars);
+	return it->num_vars++;
+}
+
+void pcb_qry_iter_init(pcb_query_iter_t *it)
+{
+	htsi_entry_t *e;
+
+	if (it->vn != NULL)
+		return;
+	it->it  = calloc(sizeof(pcb_obj_t *), it->num_vars);
+	it->lst = calloc(sizeof(pcb_qry_val_t), it->num_vars);
+
+	it->vn = malloc(sizeof(char *) * it->num_vars);
+	for (e = htsi_first(&it->names); e; e = htsi_next(&it->names, e))
+		it->vn[e->value] = e->key;
+	it->all_idx = -1;
+}
+
+/******** functions ********/
+static htsp_t *qfnc = NULL;
+
+
+int pcb_qry_fnc_reg(const char *name, pcb_qry_fnc_t fnc)
+{
+	if (qfnc == NULL)
+		qfnc = htsp_alloc(strhash, strkeyeq);
+	if (htsp_get(qfnc, name) != NULL)
+		return -1;
+
+	htsp_set(qfnc, pcb_strdup(name), pcb_cast_f2d((pcb_fptr_t)fnc));
+
+	return 0;
+}
+
+pcb_qry_fnc_t pcb_qry_fnc_lookup(const char *name)
+{
+	if (qfnc == NULL)
+		return NULL;
+
+	return (pcb_qry_fnc_t)pcb_cast_d2f(htsp_get(qfnc, name));
+}
+
+/* slow linear search: it's only for the dump */
+const char *pcb_qry_fnc_name(pcb_qry_fnc_t fnc)
+{
+	htsp_entry_t *e;
+	void *target = pcb_cast_f2d((pcb_fptr_t)fnc);
+
+	if (qfnc == NULL)
+		return NULL;
+
+	for(e = htsp_first(qfnc); e != NULL; e = htsp_next(qfnc, e))
+		if (e->value == target)
+			return e->key;
+	return NULL;
+}
+
+/******** parser helper ********/
+void qry_error(void *prog, const char *err)
+{
+	pcb_trace("qry_error: %s\n", err);
+}
+
+int qry_wrap()
+{
+	return 1;
+}
+
+/******** plugin helper ********/
+void query_action_reg(const char *cookie);
+
+static const char *query_cookie = "query plugin";
+
+static void hid_query_uninit(void)
+{
+	hid_remove_actions_by_cookie(query_cookie);
+}
+
+void pcb_qry_basic_fnc_init(void);
+
+pcb_uninit_t hid_query_init(void)
+{
+	pcb_qry_basic_fnc_init();
+	query_action_reg(query_cookie);
+	return hid_query_uninit;
+}
diff --git a/src_plugins/query/query.h b/src_plugins/query/query.h
new file mode 100644
index 0000000..c600dae
--- /dev/null
+++ b/src_plugins/query/query.h
@@ -0,0 +1,152 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_QUERY_H
+#define PCB_QUERY_H
+
+#include "obj_any.h"
+#include <genht/htsi.h>
+#include <genregex/regex_se.h>
+#include "fields_sphash.h"
+
+typedef struct pcb_qry_val_s pcb_qry_val_t;
+typedef struct pcb_query_iter_s  pcb_query_iter_t;
+
+typedef int (*pcb_qry_fnc_t)(int argc, pcb_qry_val_t *argv, pcb_qry_val_t *res);
+
+/* value of an expression */
+typedef enum pcb_qry_valtype_e {
+	PCBQ_VT_VOID,
+	PCBQ_VT_OBJ,
+	PCBQ_VT_LST,
+	PCBQ_VT_COORD,   /* considered int */
+	PCBQ_VT_DOUBLE,
+	PCBQ_VT_STRING
+} pcb_qry_valtype_t;
+
+struct pcb_qry_val_s {
+	pcb_qry_valtype_t type;
+	union {
+		pcb_obj_t obj;
+		pcb_objlist_t lst;
+		Coord crd;
+		double dbl;
+		const char *str;
+	} data;
+};
+
+/* Script parsed into a tree */
+typedef struct pcb_qry_node_s pcb_qry_node_t;
+typedef enum {
+	PCBQ_RULE,
+	PCBQ_RNAME,
+	PCBQ_EXPR_PROG,
+	PCBQ_EXPR,
+	PCBQ_ITER_CTX,
+
+	PCBQ_OP_AND,
+	PCBQ_OP_OR,
+	PCBQ_OP_EQ,
+	PCBQ_OP_NEQ,
+	PCBQ_OP_GTEQ,
+	PCBQ_OP_LTEQ,
+	PCBQ_OP_GT,
+	PCBQ_OP_LT,
+	PCBQ_OP_ADD,
+	PCBQ_OP_SUB,
+	PCBQ_OP_MUL,
+	PCBQ_OP_DIV,
+	PCBQ_OP_MATCH,
+	
+	PCBQ_OP_NOT,
+	PCBQ_FIELD,
+	PCBQ_FIELD_OF,
+	PCBQ_LISTVAR,
+	PCBQ_VAR,
+	PCBQ_FNAME,
+	PCBQ_FCALL,
+
+	PCBQ_DATA_COORD,   /* leaf */
+	PCBQ_DATA_DOUBLE,  /* leaf */
+	PCBQ_DATA_STRING,  /* leaf */
+	PCBQ_DATA_REGEX,   /* leaf */
+	PCBQ_DATA_CONST,   /* leaf */
+	PCBQ_DATA_INVALID, /* leaf */
+
+	PCBQ_nodetype_max
+} pcb_qry_nodetype_t;
+
+
+struct pcb_qry_node_s {
+	pcb_qry_nodetype_t type;
+	pcb_qry_node_t *next;         /* sibling on this level of the tree (or NULL) */
+	pcb_qry_node_t *parent;
+	union {                       /* field selection depends on ->type */
+		Coord crd;
+		double dbl;
+		const char *str;
+		pcb_query_iter_t *iter_ctx;
+		pcb_qry_node_t *children;   /* first child (NULL for a leaf node) */
+		pcb_qry_fnc_t fnc;
+	} data;
+	union {                       /* field selection depends on ->type */
+		query_fields_keys_t fld;    /* field_sphash value from str */
+		pcb_qry_val_t result;       /* of pure functions and subtrees */
+		re_se_t *regex;
+		long cnst;                  /* named constant */
+	} precomp;
+};
+
+
+pcb_qry_node_t *pcb_qry_n_alloc(pcb_qry_nodetype_t ntype);
+pcb_qry_node_t *pcb_qry_n_insert(pcb_qry_node_t *parent, pcb_qry_node_t *ch);
+
+void pcb_qry_dump_tree(const char *prefix, pcb_qry_node_t *top);
+
+void pcb_qry_set_input(const char *script);
+
+
+struct pcb_query_iter_s {
+	htsi_t names;         /* name->index hash */
+	int num_vars;
+
+	const char **vn;      /* pointers to the hash names so they can be indexed */
+	pcb_qry_val_t *lst;
+	pcb_obj_t **it;       /* iterator state for each variable - point into the correspoinding lst[] */
+
+	int all_idx;          /* index of the "@" variable or -1 */
+};
+
+pcb_query_iter_t *pcb_qry_iter_alloc(void);
+int pcb_qry_iter_var(pcb_query_iter_t *it, const char *varname, int alloc);
+void pcb_qry_iter_init(pcb_query_iter_t *it);
+
+pcb_query_iter_t *pcb_qry_find_iter(pcb_qry_node_t *node);
+
+char *pcb_query_sprint_val(pcb_qry_val_t *val);
+
+/* functions */
+int pcb_qry_fnc_reg(const char *name, pcb_qry_fnc_t fnc);
+pcb_qry_fnc_t pcb_qry_fnc_lookup(const char *name);
+const char *pcb_qry_fnc_name(pcb_qry_fnc_t fnc);
+
+#endif
diff --git a/src_plugins/query/query_access.c b/src_plugins/query/query_access.c
new file mode 100644
index 0000000..dff30b2
--- /dev/null
+++ b/src_plugins/query/query_access.c
@@ -0,0 +1,738 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - access to / extract core data */
+
+#include <math.h>
+#include "global.h"
+#include "data.h"
+#include "query_access.h"
+#include "query_exec.h"
+#include "misc.h"
+#include "layer.h"
+#include "fields_sphash.h"
+
+#define APPEND(_ctx_, _type_, _obj_, _parenttype_, _parent_) \
+do { \
+	pcb_objlist_t *lst = (pcb_objlist_t *)_ctx_; \
+	pcb_obj_t *o = malloc(sizeof(pcb_obj_t)); \
+	o->type = _type_; \
+	o->data.any = _obj_; \
+	o->parent_type = _parenttype_; \
+	o->parent.any = _parent_; \
+	pcb_objlist_append(lst, o); \
+} while(0)
+
+static int list_layer_cb(void *ctx, PCBType *pcb, LayerType *layer, int enter)
+{
+	if (enter)
+		APPEND(ctx, PCB_OBJ_LAYER, layer, PCB_PARENT_DATA, pcb->Data);
+	return 0;
+}
+
+static void list_line_cb(void *ctx, PCBType *pcb, LayerType *layer, LineType *line)
+{
+	APPEND(ctx, PCB_OBJ_LINE, line, PCB_PARENT_LAYER, layer);
+}
+
+static void list_arc_cb(void *ctx, PCBType *pcb, LayerType *layer, ArcType *arc)
+{
+	APPEND(ctx, PCB_OBJ_ARC, arc, PCB_PARENT_LAYER, layer);
+}
+
+static void list_text_cb(void *ctx, PCBType *pcb, LayerType *layer, TextType *text)
+{
+	APPEND(ctx, PCB_OBJ_TEXT, text, PCB_PARENT_LAYER, layer);
+}
+
+static void list_poly_cb(void *ctx, PCBType *pcb, LayerType *layer, PolygonType *poly)
+{
+	APPEND(ctx, PCB_OBJ_POLYGON, poly, PCB_PARENT_LAYER, layer);
+}
+
+static int list_element_cb(void *ctx, PCBType *pcb, ElementType *element, int enter)
+{
+	if (enter)
+		APPEND(ctx, PCB_OBJ_ELEMENT, element, PCB_PARENT_DATA, pcb->Data);
+	return 0;
+}
+
+static void list_eline_cb(void *ctx, PCBType *pcb, ElementType *element, LineType *line)
+{
+	APPEND(ctx, PCB_OBJ_ELINE, line, PCB_PARENT_ELEMENT, element);
+}
+
+static void list_earc_cb(void *ctx, PCBType *pcb, ElementType *element, ArcType *arc)
+{
+	APPEND(ctx, PCB_OBJ_EARC, arc, PCB_PARENT_ELEMENT, element);
+}
+
+static void list_etext_cb(void *ctx, PCBType *pcb, ElementType *element, TextType *text)
+{
+	APPEND(ctx, PCB_OBJ_ETEXT, text, PCB_PARENT_ELEMENT, element);
+}
+
+static void list_epin_cb(void *ctx, PCBType *pcb, ElementType *element, PinType *pin)
+{
+	APPEND(ctx, PCB_OBJ_PIN, pin, PCB_PARENT_ELEMENT, element);
+}
+
+static void list_epad_cb(void *ctx, PCBType *pcb, ElementType *element, PadType *pad)
+{
+	APPEND(ctx, PCB_OBJ_PAD, pad, PCB_PARENT_ELEMENT, element);
+}
+
+static void list_via_cb(void *ctx, PCBType *pcb, PinType *via)
+{
+	APPEND(ctx, PCB_OBJ_VIA, via, PCB_PARENT_DATA, pcb->Data);
+}
+
+void pcb_qry_list_all(pcb_qry_val_t *lst, pcb_objtype_t mask)
+{
+	assert(lst->type == PCBQ_VT_LST);
+	pcb_loop_all(&lst->data.lst,
+		(mask & PCB_OBJ_LAYER) ? list_layer_cb : NULL,
+		(mask & PCB_OBJ_LINE) ? list_line_cb : NULL,
+		(mask & PCB_OBJ_ARC) ? list_arc_cb : NULL,
+		(mask & PCB_OBJ_TEXT) ? list_text_cb : NULL,
+		(mask & PCB_OBJ_POLYGON) ? list_poly_cb : NULL,
+		(mask & PCB_OBJ_ELEMENT) ? list_element_cb : NULL,
+		(mask & PCB_OBJ_ELINE) ? list_eline_cb : NULL,
+		(mask & PCB_OBJ_EARC) ? list_earc_cb : NULL,
+		(mask & PCB_OBJ_ETEXT) ? list_etext_cb : NULL,
+		(mask & PCB_OBJ_PIN) ? list_epin_cb : NULL,
+		(mask & PCB_OBJ_PAD) ? list_epad_cb : NULL,
+		(mask & PCB_OBJ_VIA) ? list_via_cb : NULL
+	);
+}
+
+void pcb_qry_list_free(pcb_qry_val_t *lst_)
+{
+	pcb_objlist_t *lst = &lst_->data.lst;
+
+	assert(lst_->type == PCBQ_VT_LST);
+
+	for(;;) {
+		pcb_obj_t *n = pcb_objlist_first(lst);
+		if (n == NULL)
+			break;
+		pcb_objlist_remove(n);
+		free(n);
+	}
+}
+
+int pcb_qry_list_cmp(pcb_qry_val_t *lst1, pcb_qry_val_t *lst2)
+{
+	abort();
+}
+
+/***********************/
+
+/* set dst to point to the idxth field assuming field 0 is fld. Returns -1
+   if there are not enough fields */
+#define fld_nth_req(dst, fld, idx) \
+do { \
+	int __i__ = idx; \
+	pcb_qry_node_t *__f__; \
+	for(__f__ = (fld); __i__ > 0; __i__--, __f__ = __f__->next) { \
+		if (__f__ == NULL) \
+			return -1; \
+	} \
+	(dst) = __f__; \
+} while(0)
+
+#define fld_nth_opt(dst, fld, idx) \
+do { \
+	int __i__ = idx; \
+	pcb_qry_node_t *__f__; \
+	for(__f__ = (fld); (__i__ > 0) && (__f__ != NULL); __i__--, __f__ = __f__->next) ; \
+	(dst) = __f__; \
+} while(0)
+
+/* sets const char *s to point to the string data of the idxth field. Returns
+   -1 if tere are not enooung fields */
+#define fld2str_req(s, fld, idx) \
+do { \
+	pcb_qry_node_t *_f_; \
+	fld_nth_req(_f_, fld, idx); \
+	if ((_f_ == NULL) || (_f_->type != PCBQ_FIELD)) \
+		return -1; \
+	(s) = _f_->data.str; \
+} while(0)
+
+/* Same, but doesn't return -1 on missing field but sets s to NULL */
+#define fld2str_opt(s, fld, idx) \
+do { \
+	pcb_qry_node_t *_f_; \
+	const char *__res__; \
+	fld_nth_opt(_f_, fld, idx); \
+	if (_f_ != NULL) { \
+		if (_f_->type != PCBQ_FIELD) \
+			return -1; \
+		__res__ = _f_->data.str; \
+	} \
+	else \
+		__res__ = NULL; \
+	(s) = __res__; \
+} while(0)
+
+/* sets query_fields_keys_t h to point to the precomp field of the idxth field.
+   Returns -1 if tere are not enooung fields */
+#define fld2hash_req(h, fld, idx) \
+do { \
+	pcb_qry_node_t *_f_; \
+	fld_nth_req(_f_, fld, idx); \
+	if ((_f_ == NULL) || (_f_->type != PCBQ_FIELD)) \
+		return -1; \
+	(h) = _f_->precomp.fld; \
+} while(0)
+
+/* Same, but doesn't return -1 on missing field but sets s to query_fields_SPHASH_INVALID */
+#define fld2hash_opt(h, fld, idx) \
+do { \
+	pcb_qry_node_t *_f_; \
+	query_fields_keys_t *__res__; \
+	fld_nth_opt(_f_, fld, idx); \
+	if (_f_ != NULL) { \
+		if (_f_->type != PCBQ_FIELD) \
+			return -1; \
+		__res__ = _f_->precomp.fld; \
+	} \
+	else \
+		__res__ = query_fields_SPHASH_INVALID; \
+	(s) = __res__; \
+} while(0)
+
+static int field_layer(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	LayerType *l = obj->data.layer;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&l->Attributes, s2));
+	}
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_name:     PCB_QRY_RET_STR(res, l->Name);
+		case query_fields_visible:  PCB_QRY_RET_INT(res, l->On);
+		case query_fields_position: PCB_QRY_RET_INT(res, pcb_layer_flags(GetLayerNumber(PCB->Data, l)) & PCB_LYT_ANYWHERE);
+		case query_fields_type:     PCB_QRY_RET_INT(res, pcb_layer_flags(GetLayerNumber(PCB->Data, l)) & PCB_LYT_ANYTHING);
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_layer_from_ptr(LayerType *l, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	pcb_obj_t tmp;
+	tmp.type = PCB_OBJ_LAYER;
+	tmp.data.layer = l;
+	tmp.parent_type = PCB_PARENT_DATA;
+	tmp.parent.data = PCB->Data;
+	return field_layer(&tmp, fld, res);
+}
+
+/* process from .layer */
+static int layer_of_obj(pcb_qry_node_t *fld, pcb_qry_val_t *res, pcb_layer_type_t mask)
+{
+	int id;
+	const char *s1;
+
+	if (pcb_layer_list(mask, &id, 1) != 1)
+		PCB_QRY_RET_INV(res);
+
+	fld2str_req(s1, fld, 0);
+
+	if (s1 == NULL) {
+		res->type = PCBQ_VT_OBJ;
+		res->data.obj.type = PCB_OBJ_LAYER;
+		res->data.obj.data.layer = PCB->Data->Layer+id;
+		return 0;
+	}
+
+	return field_layer_from_ptr(PCB->Data->Layer+id, fld, res);
+}
+
+
+static int field_line(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	LineType *l = obj->data.line;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&l->Attributes, s2));
+	}
+
+	if (fh1 == query_fields_layer) {
+		if (obj->parent_type == PCB_PARENT_LAYER)
+			return field_layer_from_ptr(obj->parent.layer, fld->next, res);
+		else
+			PCB_QRY_RET_INV(res);
+	}
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_x1:         PCB_QRY_RET_INT(res, l->Point1.X);
+		case query_fields_y1:         PCB_QRY_RET_INT(res, l->Point1.Y);
+		case query_fields_x2:         PCB_QRY_RET_INT(res, l->Point2.X);
+		case query_fields_y2:         PCB_QRY_RET_INT(res, l->Point2.Y);
+		case query_fields_thickness:  PCB_QRY_RET_INT(res, l->Thickness);
+		case query_fields_clearance:  PCB_QRY_RET_INT(res, l->Clearance);
+		case query_fields_length:
+			{
+				double x = l->Point1.X - l->Point2.X;
+				double y = l->Point1.Y - l->Point2.Y;
+				double len = sqrt(x*x + y*y);
+				PCB_QRY_RET_INT(res, ((Coord)len));
+			}
+			break;
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_arc(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	ArcType *a = obj->data.arc;
+	query_fields_keys_t fh1, fh2;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&a->Attributes, s2));
+	}
+
+	if (fh1 == query_fields_angle) {
+		fld2hash_req(fh2, fld, 1);
+		switch(fh2) {
+			case query_fields_start: PCB_QRY_RET_INT(res, a->StartAngle);
+			case query_fields_delta: PCB_QRY_RET_INT(res, a->Delta);
+			default:;
+		}
+		PCB_QRY_RET_INV(res);
+	}
+
+	if (fh1 == query_fields_layer) {
+		if (obj->parent_type == PCB_PARENT_LAYER)
+			return field_layer_from_ptr(obj->parent.layer, fld->next, res);
+		else
+			PCB_QRY_RET_INV(res);
+	}
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_cx:
+		case query_fields_x:         PCB_QRY_RET_INT(res, a->X);
+		case query_fields_cy:
+		case query_fields_y:         PCB_QRY_RET_INT(res, a->Y);
+		case query_fields_thickness: PCB_QRY_RET_INT(res, a->Thickness);
+		case query_fields_clearance: PCB_QRY_RET_INT(res, a->Clearance);
+		case query_fields_length:
+			{
+#warning TODO: this breaks for elliptics; see http://tutorial.math.lamar.edu/Classes/CalcII/ArcLength.aspx
+				double r = (a->Width + a->Height)/2;
+				double len = r * M_PI / 180.0 * a->Delta;
+				PCB_QRY_RET_INT(res, ((Coord)len));
+			}
+			break;
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_text(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	TextType *t = obj->data.text;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&t->Attributes, s2));
+	}
+
+	if (fh1 == query_fields_layer) {
+		if (obj->parent_type == PCB_PARENT_LAYER)
+			return field_layer_from_ptr(obj->parent.layer, fld->next, res);
+		else
+			PCB_QRY_RET_INV(res);
+	}
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_x:        PCB_QRY_RET_INT(res, t->X);
+		case query_fields_y:        PCB_QRY_RET_INT(res, t->Y);
+		case query_fields_scale:    PCB_QRY_RET_INT(res, t->Scale);
+		case query_fields_rotation: PCB_QRY_RET_INT(res, t->Direction);
+		case query_fields_string:   PCB_QRY_RET_STR(res, t->TextString);
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_polygon(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	PolygonType *p = obj->data.polygon;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&p->Attributes, s2));
+	}
+
+	if (fh1 == query_fields_layer) {
+		if (obj->parent_type == PCB_PARENT_LAYER)
+			return field_layer_from_ptr(obj->parent.layer, fld->next, res);
+		else
+			PCB_QRY_RET_INV(res);
+	}
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_points: PCB_QRY_RET_INT(res, p->PointN);
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_rat(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+/*	const char *s1, *s2;
+
+	fld2str_req(s1, fld, 0);
+	fld2str_opt(s2, fld, 1);*/
+#warning TODO
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_via(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	PinType *p = obj->data.via;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&p->Attributes, s2));
+	}
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_x:         PCB_QRY_RET_INT(res, p->X);
+		case query_fields_y:         PCB_QRY_RET_INT(res, p->Y);
+		case query_fields_thickness: PCB_QRY_RET_INT(res, p->Thickness);
+		case query_fields_clearance: PCB_QRY_RET_INT(res, p->Clearance);
+		case query_fields_hole:      PCB_QRY_RET_INT(res, p->DrillingHole);
+		case query_fields_mask:      PCB_QRY_RET_INT(res, p->Mask);
+		case query_fields_name:      PCB_QRY_RET_STR(res, p->Name);
+		case query_fields_number:    PCB_QRY_RET_STR(res, p->Number);
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_element(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	ElementType *p = obj->data.element;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 1);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&p->Attributes, s2));
+	}
+
+	if (fh1 == query_fields_layer)
+		return layer_of_obj(fld->next, res, PCB_LYT_SILK | (TEST_FLAG(PCB_FLAG_ONSOLDER, p) ? PCB_LYT_BOTTOM : PCB_LYT_TOP));
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_x:            PCB_QRY_RET_INT(res, p->MarkX);
+		case query_fields_y:            PCB_QRY_RET_INT(res, p->MarkY);
+		case query_fields_refdes:       /* alias of: */
+		case query_fields_name:         PCB_QRY_RET_STR(res, p->Name[NAMEONPCB_INDEX].TextString);
+		case query_fields_description:  PCB_QRY_RET_STR(res, p->Name[DESCRIPTION_INDEX].TextString);
+		case query_fields_value:        PCB_QRY_RET_STR(res, p->Name[VALUE_INDEX].TextString);
+
+		default:;
+	}
+	PCB_QRY_RET_INV(res);
+}
+
+static int field_element_from_ptr(ElementType *e, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	pcb_obj_t tmp;
+	tmp.type = PCB_OBJ_ELEMENT;
+	tmp.data.element = e;
+	tmp.parent_type = PCB_PARENT_DATA;
+	tmp.parent.data = PCB->Data;
+	return field_element(&tmp, fld, res);
+}
+
+static int field_element_obj(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	const char *s1;
+
+	/* if parent is not an element (or not available) evaluate to invalid */
+	if (obj->parent_type != PCB_PARENT_ELEMENT)
+		PCB_QRY_RET_INV(res);
+
+	/* check subfield, if there's none, return the element object */
+	fld2str_opt(s1, fld, 0);
+	if (s1 == NULL) {
+		res->type = PCBQ_VT_OBJ;
+		res->data.obj.data.element = obj->parent.element;
+		res->data.obj.parent_type = PCB_PARENT_DATA;
+		res->data.obj.parent.data = PCB->Data;
+		return 0;
+	}
+
+	/* return subfields of the element */
+	return field_element_from_ptr(obj->parent.element, fld, res);
+}
+
+static int field_net(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+/*	const char *s1, *s2;
+
+	fld2str_req(s1, fld, 0);
+	fld2str_opt(s2, fld, 1);*/
+#warning TODO
+	PCB_QRY_RET_INV(res);
+}
+
+
+static int field_eline(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	LineType *l = obj->data.line;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	switch(fh1) {
+		case query_fields_layer:   return layer_of_obj(fld->next, res, PCB_LYT_SILK | (TEST_FLAG(PCB_FLAG_ONSOLDER, l) ? PCB_LYT_BOTTOM : PCB_LYT_TOP));
+		case query_fields_element: return field_element_obj(obj, fld->next, res);
+		default:;
+	}
+
+	return field_line(obj, fld, res);
+}
+
+static int field_earc(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	ArcType *a = obj->data.arc;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	switch(fh1) {
+		case query_fields_layer:   return layer_of_obj(fld->next, res, PCB_LYT_SILK | (TEST_FLAG(PCB_FLAG_ONSOLDER, a) ? PCB_LYT_BOTTOM : PCB_LYT_TOP));
+		case query_fields_element: return field_element_obj(obj, fld->next, res);
+		default:;
+	}
+
+	return field_arc(obj, fld, res);
+}
+
+static int field_etext(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	TextType *t = obj->data.text;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	switch(fh1) {
+		case query_fields_layer:   return layer_of_obj(fld->next, res, PCB_LYT_SILK | (TEST_FLAG(PCB_FLAG_ONSOLDER, t) ? PCB_LYT_BOTTOM : PCB_LYT_TOP));
+		case query_fields_element: return field_element_obj(obj, fld->next, res);
+		default:;
+	}
+
+	return field_text(obj, fld, res);
+}
+
+static int field_pin(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	switch(fh1) {
+		case query_fields_element: return field_element_obj(obj, fld->next, res);
+		default:;
+	}
+
+	return field_via(obj, fld, res);
+}
+
+static int field_pad(pcb_obj_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	PadType *p = obj->data.pad;
+	query_fields_keys_t fh1;
+
+	fld2hash_req(fh1, fld, 0);
+
+	switch(fh1) {
+		case query_fields_a:
+			{
+				const char *s2;
+				fld2str_req(s2, fld, 1);
+				PCB_QRY_RET_STR(res, AttributeGetFromList(&p->Attributes, s2));
+			}
+		case query_fields_layer:   return layer_of_obj(fld->next, res, PCB_LYT_COPPER | (TEST_FLAG(PCB_FLAG_ONSOLDER, p) ? PCB_LYT_BOTTOM : PCB_LYT_TOP));
+		case query_fields_element: return field_element_obj(obj, fld->next, res);
+		default:;
+	}
+
+
+	if (fld->next != NULL)
+		PCB_QRY_RET_INV(res);
+
+	switch(fh1) {
+		case query_fields_x1:        PCB_QRY_RET_INT(res, p->Point1.X);
+		case query_fields_y1:        PCB_QRY_RET_INT(res, p->Point1.Y);
+		case query_fields_x2:        PCB_QRY_RET_INT(res, p->Point2.X);
+		case query_fields_y2:        PCB_QRY_RET_INT(res, p->Point2.Y);
+		case query_fields_thickness: PCB_QRY_RET_INT(res, p->Thickness);
+		case query_fields_clearance: PCB_QRY_RET_INT(res, p->Clearance);
+		case query_fields_mask:      PCB_QRY_RET_INT(res, p->Mask);
+		case query_fields_name:      PCB_QRY_RET_STR(res, p->Name);
+		case query_fields_number:    PCB_QRY_RET_STR(res, p->Number);
+		default:;
+	}
+
+	PCB_QRY_RET_INV(res);
+}
+
+
+/***/
+
+int pcb_qry_obj_field(pcb_qry_val_t *objval, pcb_qry_node_t *fld, pcb_qry_val_t *res)
+{
+	pcb_obj_t *obj;
+	query_fields_keys_t fh1;
+
+	if (objval->type != PCBQ_VT_OBJ)
+		return -1;
+	obj = &objval->data.obj;
+
+	fld2hash_req(fh1, fld, 0);
+
+	if (fh1 == query_fields_a) {
+		const char *s2;
+		fld2str_req(s2, fld, 0);
+		if (!PCB_OBJ_IS_CLASS(obj->type, PCB_OBJ_CLASS_OBJ))
+			PCB_QRY_RET_INV(res);
+		PCB_QRY_RET_STR(res, AttributeGetFromList(&obj->data.anyobj->Attributes, s2));
+	}
+
+	if (fh1 == query_fields_ID) {
+		if (!PCB_OBJ_IS_CLASS(obj->type, PCB_OBJ_CLASS_OBJ))
+			PCB_QRY_RET_INV(res);
+		PCB_QRY_RET_INT(res, obj->data.anyobj->ID);
+	}
+
+	if (fh1 == query_fields_bbox) {
+		query_fields_keys_t fh2;
+
+		if (!PCB_OBJ_IS_CLASS(obj->type, PCB_OBJ_CLASS_OBJ))
+			PCB_QRY_RET_INV(res);
+
+		fld2hash_req(fh2, fld, 1);
+		switch(fh2) {
+			case query_fields_x1:     PCB_QRY_RET_INT(res, obj->data.anyobj->BoundingBox.X1);
+			case query_fields_y1:     PCB_QRY_RET_INT(res, obj->data.anyobj->BoundingBox.Y1);
+			case query_fields_x2:     PCB_QRY_RET_INT(res, obj->data.anyobj->BoundingBox.X2);
+			case query_fields_y2:     PCB_QRY_RET_INT(res, obj->data.anyobj->BoundingBox.Y2);
+			case query_fields_width:  PCB_QRY_RET_INT(res, obj->data.anyobj->BoundingBox.X2 - obj->data.anyobj->BoundingBox.X1);
+			case query_fields_height: PCB_QRY_RET_INT(res, obj->data.anyobj->BoundingBox.Y2 - obj->data.anyobj->BoundingBox.Y1);
+			case query_fields_area:   PCB_QRY_RET_DBL(res, (double)(obj->data.anyobj->BoundingBox.Y2 - obj->data.anyobj->BoundingBox.Y1) * (double)(obj->data.anyobj->BoundingBox.X2 - obj->data.anyobj->BoundingBox.X1));
+			default:;
+		}
+		PCB_QRY_RET_INV(res);
+	}
+
+	if (fh1 == query_fields_type)
+		PCB_QRY_RET_INT(res, obj->type);
+
+	switch(obj->type) {
+/*		case PCB_OBJ_POINT:    return field_point(obj, fld, res);*/
+		case PCB_OBJ_LINE:     return field_line(obj, fld, res);
+		case PCB_OBJ_TEXT:     return field_text(obj, fld, res);
+		case PCB_OBJ_POLYGON:  return field_polygon(obj, fld, res);
+		case PCB_OBJ_ARC:      return field_arc(obj, fld, res);
+		case PCB_OBJ_RAT:      return field_rat(obj, fld, res);
+		case PCB_OBJ_PAD:      return field_pad(obj, fld, res);
+		case PCB_OBJ_PIN:      return field_pin(obj, fld, res);
+		case PCB_OBJ_VIA:      return field_via(obj, fld, res);
+		case PCB_OBJ_ELEMENT:  return field_element(obj, fld, res);
+
+		case PCB_OBJ_NET:      return field_net(obj, fld, res);
+		case PCB_OBJ_LAYER:    return field_layer(obj, fld, res);
+
+		case PCB_OBJ_ELINE:    return field_eline(obj, fld, res);
+		case PCB_OBJ_EARC:     return field_earc(obj, fld, res);
+		case PCB_OBJ_ETEXT:    return field_etext(obj, fld, res);
+		default:;
+	}
+
+	return -1;
+}
+
diff --git a/src_plugins/query/query_access.h b/src_plugins/query/query_access.h
new file mode 100644
index 0000000..3e10360
--- /dev/null
+++ b/src_plugins/query/query_access.h
@@ -0,0 +1,41 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - access to / extract core data */
+
+#ifndef PCB_QUERY_ACCESS_H
+#define PCB_QUERY_ACCESS_H
+
+#include "query.h"
+#include "obj_any.h"
+
+/* Append objects with matching type to lst */
+void pcb_qry_list_all(pcb_qry_val_t *lst, pcb_objtype_t mask);
+
+int pcb_qry_list_cmp(pcb_qry_val_t *lst1, pcb_qry_val_t *lst2);
+
+void pcb_qry_list_free(pcb_qry_val_t *lst_);
+
+int pcb_qry_obj_field(pcb_qry_val_t *obj, pcb_qry_node_t *fld, pcb_qry_val_t *res);
+
+
+#endif
diff --git a/src_plugins/query/query_act.c b/src_plugins/query/query_act.c
new file mode 100644
index 0000000..a871f28
--- /dev/null
+++ b/src_plugins/query/query_act.c
@@ -0,0 +1,176 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - actions */
+
+#include "global.h"
+#include "query.h"
+#include "query_y.h"
+#include "query_exec.h"
+#include "set.h"
+#include "draw.h"
+#include "select.h"
+#include "macro.h"
+
+static const char query_action_syntax[] =
+	"query(dump, expr) - dry run: compile and dump an expression\n"
+	;
+static const char query_action_help[] = "Perform various queries on PCB data.";
+
+typedef struct {
+	int trues, falses;
+} eval_stat_t;
+
+static void eval_cb(void *user_ctx, pcb_qry_val_t *res, pcb_obj_t *current)
+{
+	eval_stat_t *st = (eval_stat_t *)user_ctx;
+	int t = pcb_qry_is_true(res);
+
+	printf(" %s", t ? "true" : "false");
+	if (t) {
+		char *resv;
+		resv = pcb_query_sprint_val(res);
+		printf(" (%s)\n", resv);
+		free(resv);
+		st->trues++;
+	}
+	else {
+		printf("\n");
+		st->falses++;
+	}
+}
+
+typedef struct {
+	pcb_cardinal_t cnt;
+	pcb_change_flag_t how;
+} select_t;
+
+static void select_cb(void *user_ctx, pcb_qry_val_t *res, pcb_obj_t *current)
+{
+	select_t *sel = (select_t *)user_ctx;
+	if (!pcb_qry_is_true(res))
+		return;
+	if (PCB_OBJ_IS_CLASS(current->type, PCB_OBJ_CLASS_OBJ)) {
+		int state_wanted = (sel->how == PCB_CHGFLG_SET);
+		int state_is     = TEST_FLAG(PCB_FLAG_SELECTED, current->data.anyobj);
+		if (state_wanted != state_is) {
+			if (current->type == PCB_OBJ_ELEMENT)
+				pcb_select_element(current->data.element, sel->how, 0);
+			else if (current->type == PCB_OBJ_ETEXT)
+				pcb_select_element_name(current->data.element, sel->how, 0);
+			else
+				CHANGE_FLAG(sel->how, PCB_FLAG_SELECTED, current->data.anyobj);
+			sel->cnt++;
+		}
+	}
+}
+
+static int run_script(const char *script, void (*cb)(void *user_ctx, pcb_qry_val_t *res, pcb_obj_t *current), void *user_ctx)
+{
+	pcb_qry_node_t *prg = NULL;
+
+	pcb_qry_set_input(script);
+	qry_parse(&prg);
+
+	if (prg == NULL) {
+		Message(PCB_MSG_ERROR, "Compilation error.\n");
+		return -1;
+	}
+
+	return pcb_qry_run(prg, cb, user_ctx);
+}
+
+static int query_action(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *cmd = argc > 0 ? argv[0] : 0;
+	select_t sel;
+
+	sel.cnt = 0;
+
+	if (cmd == NULL) {
+		return -1;
+	}
+
+	if (strcmp(cmd, "version") == 0)
+		return 0100; /* 1.0 */
+
+	if (strcmp(cmd, "dump") == 0) {
+		pcb_qry_node_t *prg = NULL;
+		printf("Script dump: '%s'\n", argv[1]);
+		pcb_qry_set_input(argv[1]);
+		qry_parse(&prg);
+		pcb_qry_dump_tree(" ", prg);
+		return 0;
+	}
+
+	if (strcmp(cmd, "eval") == 0) {
+		int errs;
+		eval_stat_t st;
+
+		memset(&st, 0, sizeof(st));
+		printf("Script eval: '%s'\n", argv[1]);
+		errs = run_script(argv[1], eval_cb, &st);
+
+		if (errs < 0)
+			printf("Failed to run the query\n");
+		else
+			printf("eval statistics: true=%d false=%d errors=%d\n", st.trues, st.falses, errs);
+		return 0;
+	}
+
+	if (strcmp(cmd, "select") == 0) {
+		sel.how = PCB_CHGFLG_SET;
+		if (run_script(argv[1], select_cb, &sel) < 0)
+			printf("Failed to run the query\n");
+		if (sel.cnt > 0) {
+			SetChangedFlag(pcb_true);
+			Redraw();
+		}
+		return 0;
+	}
+
+	if (strcmp(cmd, "unselect") == 0) {
+		sel.how = PCB_CHGFLG_CLEAR;
+		if (run_script(argv[1], select_cb, &sel) < 0)
+			printf("Failed to run the query\n");
+		if (sel.cnt > 0) {
+			SetChangedFlag(pcb_true);
+			Redraw();
+		}
+		return 0;
+	}
+
+	return -1;
+}
+
+HID_Action query_action_list[] = {
+	{"query", NULL, query_action,
+	 query_action_help, query_action_syntax}
+};
+
+REGISTER_ACTIONS(query_action_list, NULL)
+
+#include "dolists.h"
+void query_action_reg(const char *cookie)
+{
+	REGISTER_ACTIONS(query_action_list, cookie)
+}
diff --git a/src_plugins/query/query_exec.c b/src_plugins/query/query_exec.c
new file mode 100644
index 0000000..6147af4
--- /dev/null
+++ b/src_plugins/query/query_exec.c
@@ -0,0 +1,465 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - execution */
+
+#include "global.h"
+#include "data.h"
+#include "query.h"
+#include "query_exec.h"
+#include "query_access.h"
+
+void pcb_qry_init(pcb_qry_exec_t *ctx, pcb_qry_node_t *root)
+{
+	memset(ctx, 0, sizeof(pcb_qry_exec_t));
+	ctx->all.type = PCBQ_VT_LST;
+	pcb_qry_list_all(&ctx->all, PCB_OBJ_ANY);
+	ctx->root = root;
+	ctx->iter = NULL;
+}
+
+void pcb_qry_uninit(pcb_qry_exec_t *ctx)
+{
+	pcb_qry_list_free(&ctx->all);
+#warning TODO: free the iterator
+}
+
+int pcb_qry_run(pcb_qry_node_t *prg, void (*cb)(void *user_ctx, pcb_qry_val_t *res, pcb_obj_t *current), void *user_ctx)
+{
+	pcb_qry_exec_t ec;
+	pcb_qry_val_t res;
+	int errs = 0;
+
+	pcb_qry_init(&ec, prg);
+	if (pcb_qry_it_reset(&ec, prg) != 0)
+		return -1;
+
+	do {
+		if (pcb_qry_eval(&ec, prg, &res) == 0) {
+			pcb_obj_t *current = NULL;
+			if (ec.iter->all_idx >= 0)
+				current = ec.iter->it[ec.iter->all_idx];
+			cb(user_ctx, &res, current);
+		}
+		else
+			errs++;
+	} while(pcb_qry_it_next(&ec));
+	pcb_qry_uninit(&ec);
+	return errs;
+}
+
+
+/* load unary operand to o1 */
+#define UNOP() \
+do { \
+	if ((node->data.children == NULL) || (node->data.children->next != NULL)) \
+		return -1; \
+	if (pcb_qry_eval(ctx, node->data.children, &o1) < 0) \
+		return -1; \
+} while(0)
+
+/* load 1st binary operand to o1 */
+#define BINOPS1() \
+do { \
+	if ((node->data.children == NULL) || (node->data.children->next == NULL) || (node->data.children->next->next != NULL)) \
+		return -1; \
+	if (pcb_qry_eval(ctx, node->data.children, &o1) < 0) \
+		return -1; \
+} while(0)
+
+/* load 2nd binary operand to o2 */
+#define BINOPS2() \
+do { \
+	if (pcb_qry_eval(ctx, node->data.children->next, &o2) < 0) \
+		return -1; \
+} while(0)
+
+/* load binary operands to o1 and o2 */
+#define BINOPS() \
+do { \
+	BINOPS1(); \
+	BINOPS2(); \
+} while(0)
+
+static int promote(pcb_qry_val_t *a, pcb_qry_val_t *b)
+{
+	if ((a->type == PCBQ_VT_VOID) || (b->type == PCBQ_VT_VOID)) {
+		a->type = b->type = PCBQ_VT_VOID;
+		return 0;
+	}
+	if (a->type == b->type)
+		return 0;
+	switch(a->type) {
+		case PCBQ_VT_OBJ:   return -1;
+		case PCBQ_VT_LST:   return -1;
+		case PCBQ_VT_COORD:
+			if (b->type == PCBQ_VT_DOUBLE)  PCB_QRY_RET_DBL(a, a->data.crd);
+			return -1;
+		case PCBQ_VT_DOUBLE:
+			if (b->type == PCBQ_VT_COORD)  PCB_QRY_RET_DBL(b, b->data.crd);
+			return -1;
+		case PCBQ_VT_STRING:
+		case PCBQ_VT_VOID:
+			return -1;
+	}
+	return -1;
+}
+
+static const char *op2str(char *buff, int buff_size, pcb_qry_val_t *val)
+{
+	switch(val->type) {
+		case PCBQ_VT_COORD:
+			pcb_snprintf(buff, buff_size, "%mI", val->data.crd);
+			return buff;
+		case PCBQ_VT_DOUBLE:
+			pcb_snprintf(buff, buff_size, "%f", val->data.crd);
+			return buff;
+		case PCBQ_VT_STRING:
+			return val->data.str;
+		case PCBQ_VT_VOID:
+		case PCBQ_VT_OBJ:
+		case PCBQ_VT_LST:   return NULL;
+	}
+}
+
+int pcb_qry_is_true(pcb_qry_val_t *val)
+{
+	switch(val->type) {
+		case PCBQ_VT_VOID:     return 0;
+		case PCBQ_VT_OBJ:      return val->data.obj.type != PCB_OBJ_VOID;
+		case PCBQ_VT_LST:      return pcb_objlist_first(&val->data.lst) != NULL;
+		case PCBQ_VT_COORD:    return val->data.crd;
+		case PCBQ_VT_DOUBLE:   return val->data.dbl;
+		case PCBQ_VT_STRING:   return (val->data.str != NULL) && (*val->data.str != '\0');
+	}
+	return 0;
+}
+
+static void setup_iter_list(pcb_qry_exec_t *ctx, int var_id, pcb_qry_val_t *listval)
+{
+	ctx->iter->lst[var_id] = *listval;
+	assert(listval->type == PCBQ_VT_LST);
+	ctx->iter->it[var_id] = pcb_objlist_first(&listval->data.lst);
+}
+
+int pcb_qry_it_reset(pcb_qry_exec_t *ctx, pcb_qry_node_t *node)
+{
+	int n;
+	ctx->iter = pcb_qry_find_iter(node);
+	if (ctx->iter == NULL)
+		return -1;
+
+	pcb_qry_iter_init(ctx->iter);
+
+	for(n = 0; n < ctx->iter->num_vars; n++) {
+		if (strcmp(ctx->iter->vn[n], "@") == 0) {
+			setup_iter_list(ctx, n, &ctx->all);
+			ctx->iter->all_idx = n;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int pcb_qry_it_next(pcb_qry_exec_t *ctx)
+{
+	int i;
+	for(i = 0; i < ctx->iter->num_vars; i++) {
+		ctx->iter->it[i] = pcb_objlist_next(ctx->iter->it[i]);
+		if (ctx->iter->it[i] != NULL)
+			return 1;
+		ctx->iter->it[i] = pcb_objlist_first(&ctx->iter->lst[i].data.lst);
+	}
+	return 0;
+}
+
+
+int pcb_qry_eval(pcb_qry_exec_t *ctx, pcb_qry_node_t *node, pcb_qry_val_t *res)
+{
+	pcb_qry_val_t o1, o2;
+
+	switch(node->type) {
+		case PCBQ_EXPR:
+			return pcb_qry_eval(ctx, node->data.children, res);
+
+		case PCBQ_EXPR_PROG: {
+			pcb_qry_node_t *itn, *exprn;
+			itn = node->data.children;
+			if (itn == NULL)
+				return -1;
+			exprn = itn->next;
+			if (exprn == NULL)
+				return -1;
+			if (itn->type != PCBQ_ITER_CTX)
+				return -1;
+			if (ctx->iter != itn->data.iter_ctx)
+				return -1;
+			return pcb_qry_eval(ctx, exprn, res);
+		}
+
+
+		case PCBQ_OP_AND: /* lazy */
+			BINOPS1();
+			if (!pcb_qry_is_true(&o1))
+				PCB_QRY_RET_INT(res, 0);
+			BINOPS2();
+			if (!pcb_qry_is_true(&o2))
+				PCB_QRY_RET_INT(res, 0);
+			PCB_QRY_RET_INT(res, 1);
+
+		case PCBQ_OP_OR: /* lazy */
+			BINOPS1();
+			if (pcb_qry_is_true(&o1))
+				PCB_QRY_RET_INT(res, 1);
+			BINOPS2();
+			if (pcb_qry_is_true(&o2))
+				PCB_QRY_RET_INT(res, 1);
+			PCB_QRY_RET_INT(res, 0);
+
+		case PCBQ_OP_EQ:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_OBJ:      PCB_QRY_RET_INT(res, ((o1.data.obj.type) == (o2.data.obj.type)) && ((o1.data.obj.data.any) == (o2.data.obj.data.any)));
+				case PCBQ_VT_LST:      PCB_QRY_RET_INT(res, pcb_qry_list_cmp(&o1, &o2));
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd == o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_INT(res, o1.data.dbl == o2.data.dbl);
+				case PCBQ_VT_STRING:
+					if (o1.data.str == o2.data.str)
+						PCB_QRY_RET_INT(res, 1);
+					if ((o1.data.str == NULL) || (o2.data.str == NULL))
+						PCB_QRY_RET_INT(res, 0);
+					PCB_QRY_RET_INT(res, strcmp(o1.data.str, o2.data.str) == 0);
+			}
+			return -1;
+
+		case PCBQ_OP_NEQ:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_OBJ:      PCB_QRY_RET_INT(res, ((o1.data.obj.type) != (o2.data.obj.type)) || ((o1.data.obj.data.any) != (o2.data.obj.data.any)));
+				case PCBQ_VT_LST:      PCB_QRY_RET_INT(res, !pcb_qry_list_cmp(&o1, &o2));
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd != o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_INT(res, o1.data.dbl != o2.data.dbl);
+				case PCBQ_VT_STRING:
+					if (o1.data.str == o2.data.str)
+						PCB_QRY_RET_INT(res, 0);
+					if ((o1.data.str == NULL) || (o2.data.str == NULL))
+						PCB_QRY_RET_INT(res, 1);
+					PCB_QRY_RET_INT(res, strcmp(o1.data.str, o2.data.str) != 0);
+			}
+			return -1;
+
+		case PCBQ_OP_GTEQ:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd >= o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_INT(res, o1.data.dbl >= o2.data.dbl);
+				case PCBQ_VT_STRING:   PCB_QRY_RET_INT(res, strcmp(o1.data.str, o2.data.str) >= 0);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_LTEQ:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd <= o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_INT(res, o1.data.dbl <= o2.data.dbl);
+				case PCBQ_VT_STRING:   PCB_QRY_RET_INT(res, strcmp(o1.data.str, o2.data.str) <= 0);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_GT:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd > o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_INT(res, o1.data.dbl > o2.data.dbl);
+				case PCBQ_VT_STRING:   PCB_QRY_RET_INT(res, strcmp(o1.data.str, o2.data.str) > 0);
+				default:               return -1;
+			}
+			return -1;
+		case PCBQ_OP_LT:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd < o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_INT(res, o1.data.dbl < o2.data.dbl);
+				case PCBQ_VT_STRING:   PCB_QRY_RET_INT(res, strcmp(o1.data.str, o2.data.str) < 0);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_ADD:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd + o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_DBL(res, o1.data.dbl + o2.data.dbl);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_SUB:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd - o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_DBL(res, o1.data.dbl - o2.data.dbl);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_MUL:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd * o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_DBL(res, o1.data.dbl * o2.data.dbl);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_DIV:
+			BINOPS();
+			if (promote(&o1, &o2) != 0)
+				return -1;
+			switch(o1.type) {
+				case PCBQ_VT_VOID:     PCB_QRY_RET_INV(res);
+				case PCBQ_VT_COORD:    PCB_QRY_RET_INT(res, o1.data.crd / o2.data.crd);
+				case PCBQ_VT_DOUBLE:   PCB_QRY_RET_DBL(res, o1.data.dbl / o2.data.dbl);
+				default:               return -1;
+			}
+			return -1;
+
+		case PCBQ_OP_MATCH:
+			BINOPS1();
+			{
+				pcb_qry_node_t *o2n =node->data.children->next;
+				char buff[128];
+				const char *s;
+				if (o2n->type != PCBQ_DATA_REGEX)
+					return -1;
+				s = op2str(buff, sizeof(buff), &o1);
+				if (s != NULL)
+					PCB_QRY_RET_INT(res, re_se_exec(o2n->precomp.regex, s));
+			}
+			PCB_QRY_RET_INV(res);
+
+		case PCBQ_OP_NOT:
+			UNOP();
+			PCB_QRY_RET_INT(res, !pcb_qry_is_true(&o1));
+
+		case PCBQ_FIELD_OF:
+			if ((node->data.children == NULL) || (node->data.children->next == NULL))
+				return -1;
+			if (pcb_qry_eval(ctx, node->data.children, &o1) < 0)
+				return -1;
+			return pcb_qry_obj_field(&o1, node->data.children->next, res);
+
+		case PCBQ_VAR:
+			assert((node->data.crd >= 0) && (node->data.crd < ctx->iter->num_vars));
+			res->type = PCBQ_VT_OBJ;
+			res->data.obj = *ctx->iter->it[node->data.crd];
+			return 0;
+
+		case PCBQ_LISTVAR: {
+			int vi = pcb_qry_iter_var(ctx->iter, node->data.str, 0);
+			if ((vi < 0) && (strcmp(node->data.str, "@") == 0)) {
+				res->type = PCBQ_VT_LST;
+				res->data.lst = ctx->all.data.lst;
+				return 0;
+			}
+			if (vi >= 0) {
+				res->type = PCBQ_VT_LST;
+				res->data.lst = ctx->iter->lst[vi].data.lst;
+				return 0;
+			}
+		}
+		return -1;
+
+		case PCBQ_FNAME:
+			return -1; /* shall not eval such a node */
+
+		case PCBQ_FCALL: {
+			pcb_qry_val_t args[64];
+			int n;
+			pcb_qry_node_t *farg, *fname = node->data.children;
+			if (fname == NULL)
+				return -1;
+			farg = fname->next;
+			if ((fname->type !=  PCBQ_FNAME) || (fname->data.fnc == NULL))
+				return -1;
+			memset(args, 0, sizeof(args));
+			for(n = 0; (n < 64) && (farg != NULL); n++, farg = farg->next)
+				if (pcb_qry_eval(ctx, farg, &args[n]) < 0)
+					return -1;
+
+			if (farg != NULL) {
+				Message(PCB_MSG_ERROR, "too many function arguments\n");
+				return -1;
+			}
+			printf("CALL n=%d\n", n);
+			return fname->data.fnc(n, args, res);
+		}
+
+		case PCBQ_DATA_COORD:       PCB_QRY_RET_INT(res, node->data.crd);
+		case PCBQ_DATA_DOUBLE:      PCB_QRY_RET_DBL(res, node->data.dbl);
+		case PCBQ_DATA_STRING:      PCB_QRY_RET_STR(res, node->data.str);
+		case PCBQ_DATA_CONST:       PCB_QRY_RET_INT(res, node->precomp.cnst);
+		case PCBQ_DATA_INVALID:     PCB_QRY_RET_INV(res);
+
+		/* not yet implemented: */
+		case PCBQ_RULE:
+
+		/* must not meet these while executing a node */
+		case PCBQ_DATA_REGEX:
+		case PCBQ_nodetype_max:
+		case PCBQ_FIELD:
+		case PCBQ_RNAME:
+		case PCBQ_ITER_CTX:
+			return -1;
+	}
+	return -1;
+}
diff --git a/src_plugins/query/query_exec.h b/src_plugins/query/query_exec.h
new file mode 100644
index 0000000..a3068b5
--- /dev/null
+++ b/src_plugins/query/query_exec.h
@@ -0,0 +1,80 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - execution */
+
+#ifndef PCB_QUERY_EXEC_H
+#define PCB_QUERY_EXEC_H
+
+typedef struct pcb_qry_exec_s {
+	pcb_qry_node_t *root;
+	pcb_qry_val_t all;       /* a list of all objects */
+	pcb_query_iter_t *iter;  /* current iterator */
+} pcb_qry_exec_t;
+
+void pcb_qry_init(pcb_qry_exec_t *ctx, pcb_qry_node_t *root);
+void pcb_qry_uninit(pcb_qry_exec_t *ctx);
+
+int pcb_qry_run(pcb_qry_node_t *prg, void (*cb)(void *user_ctx, pcb_qry_val_t *res, pcb_obj_t *current), void *user_ctx);
+
+int pcb_qry_is_true(pcb_qry_val_t *val);
+
+int pcb_qry_eval(pcb_qry_exec_t *ctx, pcb_qry_node_t *node, pcb_qry_val_t *res);
+
+int pcb_qry_it_reset(pcb_qry_exec_t *ctx, pcb_qry_node_t *node);
+
+/* Returns 1 if context iterator is valid, 0 if the loop is over */
+int pcb_qry_it_next(pcb_qry_exec_t *ctx);
+
+
+
+/* Helper macros: load value o and return 0 */
+#define PCB_QRY_RET_INT(o, value) \
+do { \
+	o->type = PCBQ_VT_COORD; \
+	o->data.crd = value; \
+	return 0; \
+} while(0)
+
+#define PCB_QRY_RET_DBL(o, value) \
+do { \
+	o->type = PCBQ_VT_DOUBLE; \
+	o->data.dbl = value; \
+	return 0; \
+} while(0)
+
+#define PCB_QRY_RET_STR(o, value) \
+do { \
+	o->type = PCBQ_VT_STRING; \
+	o->data.str = value; \
+	return 0; \
+} while(0)
+
+/* The case when the operation couldn't be carried out, sort of NaN */
+#define PCB_QRY_RET_INV(o) \
+do { \
+	o->type = PCBQ_VT_VOID; \
+	return 0; \
+} while(0)
+
+
+#endif
diff --git a/src_plugins/query/query_l.c b/src_plugins/query/query_l.c
new file mode 100644
index 0000000..4454ff3
--- /dev/null
+++ b/src_plugins/query/query_l.c
@@ -0,0 +1,2248 @@
+#line 2 "query_l.c"
+
+#line 4 "query_l.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer qry__create_buffer
+#define yy_delete_buffer qry__delete_buffer
+#define yy_flex_debug qry__flex_debug
+#define yy_init_buffer qry__init_buffer
+#define yy_flush_buffer qry__flush_buffer
+#define yy_load_buffer_state qry__load_buffer_state
+#define yy_switch_to_buffer qry__switch_to_buffer
+#define yyin qry_in
+#define yyleng qry_leng
+#define yylex qry_lex
+#define yylineno qry_lineno
+#define yyout qry_out
+#define yyrestart qry_restart
+#define yytext qry_text
+#define yywrap qry_wrap
+#define yyalloc qry_alloc
+#define yyrealloc qry_realloc
+#define yyfree qry_free
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE qry_restart(qry_in  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int qry_leng;
+
+extern FILE *qry_in, *qry_out;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up qry_text. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up qry_text again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via qry_restart()), so that the user can continue scanning by
+	 * just pointing qry_in at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when qry_text is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int qry_leng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow qry_wrap()'s to do buffer switches
+ * instead of setting up a fresh qry_in.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void qry_restart (FILE *input_file  );
+void qry__switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE qry__create_buffer (FILE *file,int size  );
+void qry__delete_buffer (YY_BUFFER_STATE b  );
+void qry__flush_buffer (YY_BUFFER_STATE b  );
+void qry_push_buffer_state (YY_BUFFER_STATE new_buffer  );
+void qry_pop_buffer_state (void );
+
+static void qry_ensure_buffer_stack (void );
+static void qry__load_buffer_state (void );
+static void qry__init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER qry__flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE qry__scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE qry__scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE qry__scan_bytes (yyconst char *bytes,int len  );
+
+void *qry_alloc (yy_size_t  );
+void *qry_realloc (void *,yy_size_t  );
+void qry_free (void *  );
+
+#define yy_new_buffer qry__create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        qry_ensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            qry__create_buffer(qry_in,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        qry_ensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            qry__create_buffer(qry_in,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *qry_in = (FILE *) 0, *qry_out = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int qry_lineno;
+
+int qry_lineno = 1;
+
+extern char *qry_text;
+#define yytext_ptr qry_text
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up qry_text.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+	qry_leng = (size_t) (yy_cp - yy_bp); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+	(yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 62
+#define YY_END_OF_BUFFER 63
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[195] =
+    {   0,
+        0,    0,   63,   62,   61,   60,   59,   62,   62,   62,
+       59,   59,   55,   59,   62,   59,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   43,   58,   58,
+       58,   58,   62,   52,    0,    1,   50,    0,    2,   56,
+       57,   55,   54,   51,   53,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   32,   58,
+       27,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,    9,   58,   45,   58,   58,   58,   58,   42,
+       46,    8,   58,   44,   49,   57,   14,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   20,
+       31,   58,   16,   58,   17,   58,   58,   15,   58,   58,
+       33,   58,   18,   58,   28,   58,   58,   58,    3,   58,
+       47,   58,   58,   58,   23,   58,   58,   58,   58,   58,
+       58,   58,   11,   39,   58,   58,   58,   58,   38,   12,
+       25,   58,   58,   48,   58,    6,    5,   58,   58,   58,
+       22,   24,   29,   58,   58,   21,   58,   40,   10,   58,
+       58,   58,   58,   34,   37,   58,   35,   58,   58,   58,
+       58,    4,   58,   19,   58,   58,   41,   13,   26,    7,
+       36,   58,   30,    0
+
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    3,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    1,    1,    1,    6,    7,    8,
+        8,    8,    8,    8,    8,    9,    8,   10,   10,   10,
+       10,   10,   10,   10,   10,   10,   10,    1,    3,   11,
+       12,   13,    1,    8,   14,   15,   16,   17,   18,   19,
+       20,   21,   22,   21,   23,   24,   25,   26,   27,   28,
+       21,   29,   30,   31,   32,   33,   21,   34,   35,   21,
+        1,    1,    1,    1,   21,    1,   36,   21,   37,   38,
+
+       39,   21,   21,   40,   41,   21,   21,   42,   43,   44,
+       21,   45,   21,   46,   47,   48,   49,   50,   21,   21,
+       21,   21,    1,   51,    1,    8,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[52] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    2,
+        1,    1,    1,    2,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        1
+    } ;
+
+static yyconst flex_int16_t yy_base[198] =
+    {   0,
+        0,    0,  210,  211,  211,  211,  197,  203,  201,  199,
+      211,  195,   43,  192,  191,  190,  172,  173,  172,    0,
+       40,  184,  171,   41,  182,   38,   40,   46,  181,  172,
+       40,  171,  174,   48,  148,  146,   35,   34,  146,  179,
+      138,  143,  134,  211,  179,  211,  211,  176,  211,  172,
+      171,   52,  211,  211,  211,    0,  164,  148,  150,  148,
+       60,  158,  151,   48,  139,  147,  142,  140,    0,  151,
+        0,  138,   53,  142,   62,  136,  142,  131,  136,  131,
+       66,  132,  211,  114,    0,   48,  112,  112,  116,    0,
+        0,  211,  115,    0,  211,  146,    0,  124,  126,  137,
+
+      127,  125,  116,  119,  130,  125,  128,  127,  121,    0,
+        0,  119,    0,  111,    0,  115,  105,    0,  116,  107,
+        0,  119,    0,  114,    0,   96,   94,   97,    0,   84,
+        0,   92,  103,  111,    0,  110,  109,   95,  107,   95,
+       93,   93,    0,    0,   99,  102,   88,   98,    0,    0,
+        0,  102,   70,    0,   73,    0,    0,   89,   84,   86,
+        0,    0,    0,   85,   88,    0,   83,    0,    0,   81,
+       83,   58,   64,    0,    0,   73,   89,   82,   76,   67,
+       74,    0,   53,    0,   66,   65,    0,    0,    0,    0,
+        0,   70,    0,  211,   98,  100,   85
+
+    } ;
+
+static yyconst flex_int16_t yy_def[198] =
+    {   0,
+      194,    1,  194,  194,  194,  194,  194,  195,  194,  196,
+      194,  194,  194,  194,  194,  194,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  194,  194,  195,  194,  194,  196,  194,  194,
+      194,  194,  194,  194,  194,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  194,  197,  197,  197,  197,  197,  197,  197,
+      197,  194,  197,  197,  194,  194,  197,  197,  197,  197,
+
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,    0,  194,  194,  194
+
+    } ;
+
+static yyconst flex_int16_t yy_nxt[263] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       14,   15,   16,   17,   18,   19,   20,   21,   22,   20,
+       20,   23,   20,   24,   25,   26,   27,   28,   29,   30,
+       31,   20,   32,   20,   33,   34,   35,   20,   20,   20,
+       36,   37,   38,   39,   40,   41,   20,   20,   42,   20,
+       43,   51,   52,   60,   65,   68,   83,   78,   70,   73,
+       51,   52,   66,   61,   69,   71,   79,   74,   80,  113,
+       62,   72,   75,   87,   89,   88,   90,  101,  105,  123,
+      106,  102,  114,  116,  127,  117,   56,  193,  192,  191,
+      190,  189,  188,  187,   84,  124,  186,  128,   45,   45,
+
+       48,   48,  185,  184,  183,  182,  181,  180,  179,  178,
+      177,  176,  175,  174,  173,  172,  171,  170,  169,  168,
+      167,  166,  165,  164,  163,  162,  161,  160,  159,  158,
+      157,  156,  155,  154,  153,  152,  151,  150,  149,  148,
+      147,  146,  145,  144,  143,  142,  141,  140,  139,  138,
+      137,  136,  135,  134,  133,   96,  132,  131,  130,  129,
+      126,  125,  122,  121,  120,  119,  118,  115,  112,  111,
+      110,  109,  108,  107,  104,  103,  100,   99,   98,   97,
+       96,   50,   49,   46,   95,   94,   93,   92,   91,   86,
+       85,   82,   81,   77,   76,   67,   64,   63,   59,   58,
+
+       57,   55,   54,   53,   50,   49,   47,   46,   44,  194,
+        3,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194
+    } ;
+
+static yyconst flex_int16_t yy_chk[263] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,   13,   13,   21,   24,   26,   34,   31,   27,   28,
+       52,   52,   24,   21,   26,   27,   31,   28,   31,   73,
+       21,   27,   28,   37,   38,   37,   38,   61,   64,   81,
+       64,   61,   73,   75,   86,   75,  197,  192,  186,  185,
+      183,  181,  180,  179,   34,   81,  178,   86,  195,  195,
+
+      196,  196,  177,  176,  173,  172,  171,  170,  167,  165,
+      164,  160,  159,  158,  155,  153,  152,  148,  147,  146,
+      145,  142,  141,  140,  139,  138,  137,  136,  134,  133,
+      132,  130,  128,  127,  126,  124,  122,  120,  119,  117,
+      116,  114,  112,  109,  108,  107,  106,  105,  104,  103,
+      102,  101,  100,   99,   98,   96,   93,   89,   88,   87,
+       84,   82,   80,   79,   78,   77,   76,   74,   72,   70,
+       68,   67,   66,   65,   63,   62,   60,   59,   58,   57,
+       51,   50,   48,   45,   43,   42,   41,   40,   39,   36,
+       35,   33,   32,   30,   29,   25,   23,   22,   19,   18,
+
+       17,   16,   15,   14,   12,   10,    9,    8,    7,    3,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194,  194,  194,  194,  194,  194,  194,  194,  194,
+      194,  194
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int qry__flex_debug;
+int qry__flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *qry_text;
+#line 1 "query_l.l"
+#line 2 "query_l.l"
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - compiler: lexical analyzer */
+
+#include "global.h"
+#include "unit.h"
+#include "query.h"
+#include "query_y.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+static const char *pcb_qry_program, *pcb_qry_program_ptr;
+static int qry_yy_input(char *buf, int buflen);
+static pcb_qry_node_t *make_constant(char *str, long val);
+#define YY_INPUT(buf, res, buflen) (res = qry_yy_input(buf, buflen))
+#line 638 "query_l.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int qry_lex_destroy (void );
+
+int qry_get_debug (void );
+
+void qry_set_debug (int debug_flag  );
+
+YY_EXTRA_TYPE qry_get_extra (void );
+
+void qry_set_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *qry_get_in (void );
+
+void qry_set_in  (FILE * in_str  );
+
+FILE *qry_get_out (void );
+
+void qry_set_out  (FILE * out_str  );
+
+int qry_get_leng (void );
+
+char *qry_get_text (void );
+
+int qry_get_lineno (void );
+
+void qry_set_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int qry_wrap (void );
+#else
+extern int qry_wrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+    
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( qry_text, qry_leng, 1, qry_out )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( qry_in )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( qry_in ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, qry_in))==0 && ferror(qry_in)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(qry_in); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int qry_lex (void);
+
+#define YY_DECL int qry_lex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after qry_text and qry_leng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    
+#line 41 "query_l.l"
+
+#line 827 "query_l.c"
+
+	if ( !(yy_init) )
+		{
+		(yy_init) = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! qry_in )
+			qry_in = stdin;
+
+		if ( ! qry_out )
+			qry_out = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			qry_ensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				qry__create_buffer(qry_in,YY_BUF_SIZE );
+		}
+
+		qry__load_buffer_state( );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of qry_text. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = (yy_start);
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				(yy_last_accepting_state) = yy_current_state;
+				(yy_last_accepting_cpos) = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 195 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 211 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = (yy_hold_char);
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 42 "query_l.l"
+{ qry_lval.s = pcb_strdup(qry_text+1); qry_lval.s[strlen(qry_lval.s)-1] = '\0'; return T_QSTR; /*"*/ }
+	YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 43 "query_l.l"
+{ qry_lval.s = pcb_strdup(qry_text+1); qry_lval.s[strlen(qry_lval.s)-1] = '\0'; return T_QSTR; }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 45 "query_l.l"
+{ return T_LET; }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 46 "query_l.l"
+{ return T_ASSERT; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 47 "query_l.l"
+{ return T_RULE; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 48 "query_l.l"
+{ return T_LIST; }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 49 "query_l.l"
+{ return T_INVALID; }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 50 "query_l.l"
+{ return T_FLD_P; }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 51 "query_l.l"
+{ return T_FLD_A; }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 53 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_POINT); return T_CONST; }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 54 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_LINE); return T_CONST; }
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 55 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_TEXT); return T_CONST; }
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 56 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_POLYGON); return T_CONST; }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 57 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_ARC); return T_CONST; }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 58 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_RAT); return T_CONST; }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 59 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_PAD); return T_CONST; }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 60 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_PIN); return T_CONST; }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 61 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_VIA); return T_CONST; }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 62 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_ELEMENT); return T_CONST; }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 63 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_NET); return T_CONST; }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 64 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_LAYER); return T_CONST; }
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 65 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_ELINE); return T_CONST; }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 66 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_EARC); return T_CONST; }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 67 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_OBJ_ETEXT); return T_CONST; }
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 69 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 1); return T_CONST; }
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 70 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 1); return T_CONST; }
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 71 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 1); return T_CONST; }
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 72 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 1); return T_CONST; }
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 74 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 0); return T_CONST; }
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 75 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 0); return T_CONST; }
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 76 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 0); return T_CONST; }
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 77 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, 0); return T_CONST; }
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 79 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_TOP); return T_CONST; }
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 80 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_BOTTOM); return T_CONST; }
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 81 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_INTERN); return T_CONST; }
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 82 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_INTERN); return T_CONST; }
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 83 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_COPPER); return T_CONST; }
+	YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 84 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_SILK); return T_CONST; }
+	YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 85 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_MASK); return T_CONST; }
+	YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 86 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_PASTE); return T_CONST; }
+	YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 87 "query_l.l"
+{ qry_lval.n = make_constant(qry_text, PCB_LYT_OUTLINE); return T_CONST; }
+	YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 91 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_MM); return T_UNIT; }
+	YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 92 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_M); return T_UNIT; }
+	YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 93 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_UM); return T_UNIT; }
+	YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 94 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_CM); return T_UNIT; }
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 95 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_NM); return T_UNIT; }
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 96 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_MIL); return T_UNIT; }
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 97 "query_l.l"
+{ qry_lval.u = get_unit_struct_by_allow(ALLOW_IN); return T_UNIT; }
+	YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 99 "query_l.l"
+{ return T_OR; }
+	YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 100 "query_l.l"
+{ return T_AND; }
+	YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 101 "query_l.l"
+{ return T_EQ; }
+	YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 102 "query_l.l"
+{ return T_NEQ; }
+	YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 103 "query_l.l"
+{ return T_GTEQ; }
+	YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 104 "query_l.l"
+{ return T_LTEQ; }
+	YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 106 "query_l.l"
+{ qry_lval.c = strtol(qry_text, NULL, 10); return T_INT; }
+	YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 107 "query_l.l"
+{ qry_lval.d = strtod(qry_text, NULL); return T_DBL; }
+	YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 108 "query_l.l"
+{ qry_lval.d = strtod(qry_text, NULL); return T_DBL; }
+	YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 109 "query_l.l"
+{ qry_lval.s = pcb_strdup(qry_text); return T_STR; }
+	YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 111 "query_l.l"
+{ return *qry_text; }
+	YY_BREAK
+case 60:
+/* rule 60 can match eol */
+YY_RULE_SETUP
+#line 113 "query_l.l"
+{ return T_NL; }
+	YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 114 "query_l.l"
+{ continue; }
+	YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 116 "query_l.l"
+ECHO;
+	YY_BREAK
+#line 1223 "query_l.c"
+case YY_STATE_EOF(INITIAL):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed qry_in at a new source and called
+			 * qry_lex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = qry_in;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = (yy_c_buf_p);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( qry_wrap( ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * qry_text, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of qry_lex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+    	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = (yytext_ptr);
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					qry_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), (size_t) num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			qry_restart(qry_in  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) qry_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			(yy_last_accepting_state) = yy_current_state;
+			(yy_last_accepting_cpos) = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 195 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+	register int yy_is_jam;
+    	register char *yy_cp = (yy_c_buf_p);
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		(yy_last_accepting_state) = yy_current_state;
+		(yy_last_accepting_cpos) = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 195 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 194);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+	register char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+	/* undo effects of setting up qry_text */
+	*yy_cp = (yy_hold_char);
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = (yy_n_chars) + 2;
+		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		register char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+	(yytext_ptr) = yy_bp;
+	(yy_hold_char) = *yy_cp;
+	(yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+	int c;
+    
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (yy_c_buf_p) - (yytext_ptr);
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					qry_restart(qry_in );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( qry_wrap( ) )
+						return EOF;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve qry_text */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void qry_restart  (FILE * input_file )
+{
+    
+	if ( ! YY_CURRENT_BUFFER ){
+        qry_ensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            qry__create_buffer(qry_in,YY_BUF_SIZE );
+	}
+
+	qry__init_buffer(YY_CURRENT_BUFFER,input_file );
+	qry__load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void qry__switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		qry_pop_buffer_state();
+	 *		qry_push_buffer_state(new_buffer);
+     */
+	qry_ensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	qry__load_buffer_state( );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (qry_wrap()) processing, but the only time this flag
+	 * is looked at is after qry_wrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void qry__load_buffer_state  (void)
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	qry_in = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE qry__create_buffer  (FILE * file, int  size )
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) qry_alloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in qry__create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) qry_alloc(b->yy_buf_size + 2  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in qry__create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	qry__init_buffer(b,file );
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with qry__create_buffer()
+ * 
+ */
+    void qry__delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		qry_free((void *) b->yy_ch_buf  );
+
+	qry_free((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a qry_restart() or at EOF.
+ */
+    static void qry__init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+	int oerrno = errno;
+    
+	qry__flush_buffer(b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then qry__init_buffer was _probably_
+     * called from qry_restart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void qry__flush_buffer (YY_BUFFER_STATE  b )
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		qry__load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void qry_push_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+    	if (new_buffer == NULL)
+		return;
+
+	qry_ensure_buffer_stack();
+
+	/* This block is copied from qry__switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from qry__switch_to_buffer. */
+	qry__load_buffer_state( );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void qry_pop_buffer_state (void)
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	qry__delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		qry__load_buffer_state( );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void qry_ensure_buffer_stack (void)
+{
+	int num_to_alloc;
+    
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		(yy_buffer_stack) = (struct yy_buffer_state**)qry_alloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in qry_ensure_buffer_stack()" );
+								  
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)qry_realloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in qry_ensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE qry__scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) qry_alloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in qry__scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	qry__switch_to_buffer(b  );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to qry_lex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       qry__scan_bytes() instead.
+ */
+YY_BUFFER_STATE qry__scan_string (yyconst char * yystr )
+{
+    
+	return qry__scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to qry_lex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE qry__scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) qry_alloc(n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in qry__scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = qry__scan_buffer(buf,n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in qry__scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up qry_text. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		qry_text[qry_leng] = (yy_hold_char); \
+		(yy_c_buf_p) = qry_text + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		qry_leng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int qry_get_lineno  (void)
+{
+        
+    return qry_lineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *qry_get_in  (void)
+{
+        return qry_in;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *qry_get_out  (void)
+{
+        return qry_out;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int qry_get_leng  (void)
+{
+        return qry_leng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *qry_get_text  (void)
+{
+        return qry_text;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void qry_set_lineno (int  line_number )
+{
+    
+    qry_lineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see qry__switch_to_buffer
+ */
+void qry_set_in (FILE *  in_str )
+{
+        qry_in = in_str ;
+}
+
+void qry_set_out (FILE *  out_str )
+{
+        qry_out = out_str ;
+}
+
+int qry_get_debug  (void)
+{
+        return qry__flex_debug;
+}
+
+void qry_set_debug (int  bdebug )
+{
+        qry__flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from qry_lex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    qry_in = stdin;
+    qry_out = stdout;
+#else
+    qry_in = (FILE *) 0;
+    qry_out = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * qry_lex_init()
+     */
+    return 0;
+}
+
+/* qry_lex_destroy is for both reentrant and non-reentrant scanners. */
+int qry_lex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		qry__delete_buffer(YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		qry_pop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	qry_free((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * qry_lex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *qry_alloc (yy_size_t  size )
+{
+	return (void *) malloc( size );
+}
+
+void *qry_realloc  (void * ptr, yy_size_t  size )
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void qry_free (void * ptr )
+{
+	free( (char *) ptr );	/* see qry_realloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 116 "query_l.l"
+
+
+
+static int qry_yy_input(char *buf, int buflen)
+{
+	int len;
+	for(len = 0; (*pcb_qry_program_ptr != '\0') && (buflen > 0); len++,buflen--) {
+/*		printf("IN: '%c'\n",  *pcb_qry_program_ptr);*/
+		*buf = *pcb_qry_program_ptr;
+		buf++;
+		pcb_qry_program_ptr++;
+	}
+	return len;
+}
+
+void pcb_qry_set_input(const char *script)
+{
+	pcb_qry_program = pcb_qry_program_ptr = script;
+}
+
+static pcb_qry_node_t *make_constant(char *str, long val)
+{
+	pcb_qry_node_t *res = pcb_qry_n_alloc(PCBQ_DATA_CONST);
+	res->data.str = pcb_strdup(str);
+	res->precomp.cnst = val;
+	return res;
+}
+
diff --git a/src_plugins/query/query_l.h b/src_plugins/query/query_l.h
new file mode 100644
index 0000000..861ba3d
--- /dev/null
+++ b/src_plugins/query/query_l.h
@@ -0,0 +1,332 @@
+#ifndef qry_HEADER_H
+#define qry_HEADER_H 1
+#define qry_IN_HEADER 1
+
+#line 6 "query_l.h"
+
+#line 8 "query_l.h"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int qry_leng;
+
+extern FILE *qry_in, *qry_out;
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void qry_restart (FILE *input_file  );
+void qry__switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE qry__create_buffer (FILE *file,int size  );
+void qry__delete_buffer (YY_BUFFER_STATE b  );
+void qry__flush_buffer (YY_BUFFER_STATE b  );
+void qry_push_buffer_state (YY_BUFFER_STATE new_buffer  );
+void qry_pop_buffer_state (void );
+
+YY_BUFFER_STATE qry__scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE qry__scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE qry__scan_bytes (yyconst char *bytes,int len  );
+
+void *qry_alloc (yy_size_t  );
+void *qry_realloc (void *,yy_size_t  );
+void qry_free (void *  );
+
+/* Begin user sect3 */
+
+extern int qry_lineno;
+
+extern char *qry_text;
+#define yytext_ptr qry_text
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int qry_lex_destroy (void );
+
+int qry_get_debug (void );
+
+void qry_set_debug (int debug_flag  );
+
+YY_EXTRA_TYPE qry_get_extra (void );
+
+void qry_set_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *qry_get_in (void );
+
+void qry_set_in  (FILE * in_str  );
+
+FILE *qry_get_out (void );
+
+void qry_set_out  (FILE * out_str  );
+
+int qry_get_leng (void );
+
+char *qry_get_text (void );
+
+int qry_get_lineno (void );
+
+void qry_set_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int qry_wrap (void );
+#else
+extern int qry_wrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int qry_lex (void);
+
+#define YY_DECL int qry_lex (void)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 116 "query_l.l"
+
+
+#line 331 "query_l.h"
+#undef qry_IN_HEADER
+#endif /* qry_HEADER_H */
diff --git a/src_plugins/query/query_l.l b/src_plugins/query/query_l.l
new file mode 100644
index 0000000..62a4f9d
--- /dev/null
+++ b/src_plugins/query/query_l.l
@@ -0,0 +1,141 @@
+%{
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - compiler: lexical analyzer */
+
+#include "global.h"
+#include "unit.h"
+#include "query.h"
+#include "query_y.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+static const char *pcb_qry_program, *pcb_qry_program_ptr;
+static int qry_yy_input(char *buf, int buflen);
+static pcb_qry_node_t *make_constant(char *str, long val);
+#define YY_INPUT(buf, res, buflen) (res = qry_yy_input(buf, buflen))
+%}
+
+%option prefix="qry_"
+
+%%
+["][^"]*["]     { qry_lval.s = pcb_strdup(yytext+1); qry_lval.s[strlen(qry_lval.s)-1] = '\0'; return T_QSTR; /*"*/ }
+['][^']*[']     { qry_lval.s = pcb_strdup(yytext+1); qry_lval.s[strlen(qry_lval.s)-1] = '\0'; return T_QSTR; }
+
+let             { return T_LET; }
+assert          { return T_ASSERT; }
+rule            { return T_RULE; }
+list            { return T_LIST; }
+invalid         { return T_INVALID; }
+p[.]            { return T_FLD_P; }
+a[.]            { return T_FLD_A; }
+
+"POINT"         { qry_lval.n = make_constant(yytext, PCB_OBJ_POINT); return T_CONST; }
+"LINE"          { qry_lval.n = make_constant(yytext, PCB_OBJ_LINE); return T_CONST; }
+"TEXT"          { qry_lval.n = make_constant(yytext, PCB_OBJ_TEXT); return T_CONST; }
+"POLYGON"       { qry_lval.n = make_constant(yytext, PCB_OBJ_POLYGON); return T_CONST; }
+"ARC"           { qry_lval.n = make_constant(yytext, PCB_OBJ_ARC); return T_CONST; }
+"RAT"           { qry_lval.n = make_constant(yytext, PCB_OBJ_RAT); return T_CONST; }
+"PAD"           { qry_lval.n = make_constant(yytext, PCB_OBJ_PAD); return T_CONST; }
+"PIN"           { qry_lval.n = make_constant(yytext, PCB_OBJ_PIN); return T_CONST; }
+"VIA"           { qry_lval.n = make_constant(yytext, PCB_OBJ_VIA); return T_CONST; }
+"ELEMENT"       { qry_lval.n = make_constant(yytext, PCB_OBJ_ELEMENT); return T_CONST; }
+"NET"           { qry_lval.n = make_constant(yytext, PCB_OBJ_NET); return T_CONST; }
+"LAYER"         { qry_lval.n = make_constant(yytext, PCB_OBJ_LAYER); return T_CONST; }
+"ELINE"         { qry_lval.n = make_constant(yytext, PCB_OBJ_ELINE); return T_CONST; }
+"EARC"          { qry_lval.n = make_constant(yytext, PCB_OBJ_EARC); return T_CONST; }
+"ETEXT"         { qry_lval.n = make_constant(yytext, PCB_OBJ_ETEXT); return T_CONST; }
+
+"TRUE"          { qry_lval.n = make_constant(yytext, 1); return T_CONST; }
+"VISIBLE"       { qry_lval.n = make_constant(yytext, 1); return T_CONST; }
+"ON"            { qry_lval.n = make_constant(yytext, 1); return T_CONST; }
+"YES"           { qry_lval.n = make_constant(yytext, 1); return T_CONST; }
+
+"FALSE"         { qry_lval.n = make_constant(yytext, 0); return T_CONST; }
+"INVISIBLE"     { qry_lval.n = make_constant(yytext, 0); return T_CONST; }
+"OFF"           { qry_lval.n = make_constant(yytext, 0); return T_CONST; }
+"NO"            { qry_lval.n = make_constant(yytext, 0); return T_CONST; }
+
+"TOP"           { qry_lval.n = make_constant(yytext, PCB_LYT_TOP); return T_CONST; }
+"BOTTOM"        { qry_lval.n = make_constant(yytext, PCB_LYT_BOTTOM); return T_CONST; }
+"INTERN"        { qry_lval.n = make_constant(yytext, PCB_LYT_INTERN); return T_CONST; }
+"INTERNAL"      { qry_lval.n = make_constant(yytext, PCB_LYT_INTERN); return T_CONST; }
+"COPPER"        { qry_lval.n = make_constant(yytext, PCB_LYT_COPPER); return T_CONST; }
+"SILK"          { qry_lval.n = make_constant(yytext, PCB_LYT_SILK); return T_CONST; }
+"MASK"          { qry_lval.n = make_constant(yytext, PCB_LYT_MASK); return T_CONST; }
+"PASTE"         { qry_lval.n = make_constant(yytext, PCB_LYT_PASTE); return T_CONST; }
+"OUTLINE"       { qry_lval.n = make_constant(yytext, PCB_LYT_OUTLINE); return T_CONST; }
+
+
+
+mm              { qry_lval.u = get_unit_struct_by_allow(ALLOW_MM); return T_UNIT; }
+m               { qry_lval.u = get_unit_struct_by_allow(ALLOW_M); return T_UNIT; }
+um              { qry_lval.u = get_unit_struct_by_allow(ALLOW_UM); return T_UNIT; }
+cm              { qry_lval.u = get_unit_struct_by_allow(ALLOW_CM); return T_UNIT; }
+nm              { qry_lval.u = get_unit_struct_by_allow(ALLOW_NM); return T_UNIT; }
+mil             { qry_lval.u = get_unit_struct_by_allow(ALLOW_MIL); return T_UNIT; }
+inch            { qry_lval.u = get_unit_struct_by_allow(ALLOW_IN); return T_UNIT; }
+
+[|][|]          { return T_OR; }
+[&][&]          { return T_AND; }
+[=][=]          { return T_EQ; }
+[!][=]          { return T_NEQ; }
+[>][=]          { return T_GTEQ; }
+[<][=]          { return T_LTEQ; }
+
+[0-9]+                    { qry_lval.c = strtol(yytext, NULL, 10); return T_INT; }
+[.][0-9]+                 { qry_lval.d = strtod(yytext, NULL); return T_DBL; }
+[0-9]+[.][0-9]*           { qry_lval.d = strtod(yytext, NULL); return T_DBL; }
+[A-Za-z_][0-9A-Za-z_]*    { qry_lval.s = pcb_strdup(yytext); return T_STR; }
+
+[@().,<>!*+/~-] { return *yytext; }
+
+[;\r\n]         { return T_NL; }
+[ \t]           { continue; }
+
+%%
+
+static int qry_yy_input(char *buf, int buflen)
+{
+	int len;
+	for(len = 0; (*pcb_qry_program_ptr != '\0') && (buflen > 0); len++,buflen--) {
+/*		printf("IN: '%c'\n",  *pcb_qry_program_ptr);*/
+		*buf = *pcb_qry_program_ptr;
+		buf++;
+		pcb_qry_program_ptr++;
+	}
+	return len;
+}
+
+void pcb_qry_set_input(const char *script)
+{
+	pcb_qry_program = pcb_qry_program_ptr = script;
+}
+
+static pcb_qry_node_t *make_constant(char *str, long val)
+{
+	pcb_qry_node_t *res = pcb_qry_n_alloc(PCBQ_DATA_CONST);
+	res->data.str = pcb_strdup(str);
+	res->precomp.cnst = val;
+	return res;
+}
diff --git a/src_plugins/query/query_y.c b/src_plugins/query/query_y.c
new file mode 100644
index 0000000..f0148a0
--- /dev/null
+++ b/src_plugins/query/query_y.c
@@ -0,0 +1,2016 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "3.0.2"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names.  */
+#define yyparse         qry_parse
+#define yylex           qry_lex
+#define yyerror         qry_error
+#define yydebug         qry_debug
+#define yynerrs         qry_nerrs
+
+#define yylval          qry_lval
+#define yychar          qry_char
+
+/* Copy the first part of user declarations.  */
+#line 1 "query_y.y" /* yacc.c:339  */
+
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - compiler: grammar */
+
+#include <assert.h>
+#include "global.h"
+#include "unit.h"
+#include "query.h"
+#include "query_l.h"
+#include "compat_misc.h"
+#include "fields_sphash.h"
+
+#define UNIT_CONV(dst, negative, val, unit) \
+do { \
+	dst = val; \
+	if (negative) \
+		dst = -dst; \
+	if (unit != NULL) { \
+		if (unit->family == IMPERIAL) \
+			dst = PCB_MIL_TO_COORD(dst); \
+		else if (unit->family == METRIC) \
+			dst = PCB_MM_TO_COORD(dst); \
+		dst /= unit->scale_factor; \
+	} \
+} while(0)
+
+#define BINOP(dst, op1, operator, op2) \
+do { \
+	assert(op2->next == NULL); \
+	assert(op2->next == NULL); \
+	dst = pcb_qry_n_alloc(operator); \
+	pcb_qry_n_insert(dst, op2); \
+	pcb_qry_n_insert(dst, op1); \
+} while(0)
+
+#define UNOP(dst, operator, op) \
+do { \
+	assert(op->next == NULL); \
+	dst = pcb_qry_n_alloc(operator); \
+	pcb_qry_n_insert(dst, op); \
+} while(0)
+
+static pcb_query_iter_t *iter_ctx;
+
+static char *attrib_prepend_free(char *orig, char *prep, char sep)
+{
+	int l1 = strlen(orig), l2 = strlen(prep);
+	char *res = malloc(l1+l2+2);
+	memcpy(res, prep, l2);
+	res[l2] = sep;
+	memcpy(res+l2+1, orig, l1+1);
+	free(orig);
+	free(prep);
+	return res;
+}
+
+static pcb_qry_node_t *make_regex_free(char *str)
+{
+	pcb_qry_node_t *res = pcb_qry_n_alloc(PCBQ_DATA_REGEX);
+	res->data.str = str;
+	res->precomp.regex = re_se_comp(str);
+	if (res->precomp.regex == NULL)
+		yyerror(NULL, "Invalid regex\n");
+	return res;
+}
+
+
+
+#line 164 "query_y.c" /* yacc.c:339  */
+
+# ifndef YY_NULLPTR
+#  if defined __cplusplus && 201103L <= __cplusplus
+#   define YY_NULLPTR nullptr
+#  else
+#   define YY_NULLPTR 0
+#  endif
+# endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* In a future release of Bison, this section will be replaced
+   by #include "query_y.h".  */
+#ifndef YY_QRY_QUERY_Y_H_INCLUDED
+# define YY_QRY_QUERY_Y_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int qry_debug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    T_LET = 258,
+    T_ASSERT = 259,
+    T_RULE = 260,
+    T_LIST = 261,
+    T_INVALID = 262,
+    T_FLD_P = 263,
+    T_FLD_A = 264,
+    T_OR = 265,
+    T_AND = 266,
+    T_EQ = 267,
+    T_NEQ = 268,
+    T_GTEQ = 269,
+    T_LTEQ = 270,
+    T_NL = 271,
+    T_UNIT = 272,
+    T_STR = 273,
+    T_QSTR = 274,
+    T_INT = 275,
+    T_DBL = 276,
+    T_CONST = 277
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 95 "query_y.y" /* yacc.c:355  */
+
+	char *s;
+	Coord c;
+	double d;
+	const Unit *u;
+	pcb_qry_node_t *n;
+
+#line 235 "query_y.c" /* yacc.c:355  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE qry_lval;
+
+int qry_parse (pcb_qry_node_t **prg_out);
+
+#endif /* !YY_QRY_QUERY_Y_H_INCLUDED  */
+
+/* Copy the second part of user declarations.  */
+
+#line 250 "query_y.c" /* yacc.c:358  */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__                                               \
+      && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)))  \
+     || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+#  define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+#  define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE   YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+#  define _Noreturn __declspec (noreturn)
+# else
+#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+    _Pragma ("GCC diagnostic push") \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYSIZE_T yynewbytes;                                            \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYSIZE_T yyi;                         \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  9
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   231
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  36
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  18
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  57
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  93
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+   by yylex, with out-of-bounds checking.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   277
+
+#define YYTRANSLATE(YYX)                                                \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, without out-of-bounds checking.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    30,     2,     2,     2,     2,     2,     2,
+      31,    32,    27,    25,    35,    26,    29,    28,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      23,     2,    24,     2,    34,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,    33,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22
+};
+
+#if YYDEBUG
+  /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   133,   133,   134,   139,   139,   152,   153,   157,   169,
+     170,   174,   175,   176,   177,   178,   179,   180,   181,   182,
+     183,   184,   185,   186,   187,   188,   189,   190,   191,   192,
+     193,   194,   195,   196,   208,   209,   210,   211,   215,   219,
+     220,   224,   225,   226,   227,   231,   232,   233,   237,   238,
+     239,   243,   244,   248,   262,   263,   267,   268
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "T_LET", "T_ASSERT", "T_RULE", "T_LIST",
+  "T_INVALID", "T_FLD_P", "T_FLD_A", "T_OR", "T_AND", "T_EQ", "T_NEQ",
+  "T_GTEQ", "T_LTEQ", "T_NL", "T_UNIT", "T_STR", "T_QSTR", "T_INT",
+  "T_DBL", "T_CONST", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'.'",
+  "'!'", "'('", "')'", "'~'", "'@'", "','", "$accept", "program",
+  "program_expr", "$@1", "program_rules", "rule", "exprs", "expr",
+  "number", "string_literal", "maybe_unit", "fields", "attribs", "var",
+  "fcall", "fname", "fargs", "words", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+   (internal) symbol number NUM (which must be that of a token).  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,    60,    62,    43,    45,    42,    47,    46,
+      33,    40,    41,   126,    64,    44
+};
+# endif
+
+#define YYPACT_NINF -63
+
+#define yypact_value_is_default(Yystate) \
+  (!!((Yystate) == (-63)))
+
+#define YYTABLE_NINF -54
+
+#define yytable_value_is_error(Yytable_value) \
+  0
+
+  /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+     STATE-NUM.  */
+static const yytype_int16 yypact[] =
+{
+       3,   -17,     9,   -63,    40,   -63,     6,   -17,    -3,   -63,
+       0,   -63,     2,   -63,    10,    10,   -63,     5,    40,    40,
+     -63,   139,   -63,   -63,    19,   -63,    21,   -63,   -63,   -63,
+      23,   -63,   -63,   -63,    10,    10,    31,    91,    40,    40,
+      40,    40,    40,    40,    40,    40,    40,    40,    40,    40,
+      32,    45,    -2,    40,    33,   -63,   -63,   -63,   162,   179,
+     194,   194,   198,   198,   198,   198,   -21,   -21,    31,    31,
+     -63,   -63,    45,    37,    38,   -63,   -63,    65,    36,   115,
+     -63,   -63,    43,   -63,   -63,    45,    40,   -63,   -63,    37,
+     -63,   -63,   -63
+};
+
+  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+     Performed when YYTABLE does not specify something else to do.  Zero
+     means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       4,    56,     0,     3,     0,     2,     6,    56,     0,     1,
+       0,    14,    48,    38,    39,    39,    31,     0,     0,     0,
+      50,     5,    12,    13,    32,    11,     0,     7,    57,     9,
+       0,    40,    34,    35,    39,    39,    15,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     8,     0,    36,    37,    16,    18,    17,
+      19,    20,    21,    22,    24,    23,    25,    26,    27,    28,
+      29,    30,     0,     0,    41,    33,    52,    54,     0,     0,
+      49,    43,    45,    47,    44,     0,     0,    51,    10,     0,
+      42,    55,    46
+};
+
+  /* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -63,   -63,   -63,   -63,    63,   -63,   -63,    -4,   -63,   -63,
+     -13,   -62,   -16,   -63,   -63,   -63,    -5,    75
+};
+
+  /* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     2,     3,     4,     5,     6,    53,    77,    22,    23,
+      32,    75,    84,    24,    25,    26,    78,     8
+};
+
+  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+     positive, shift that token.  If negative, reduce the rule whose
+     number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_int8 yytable[] =
+{
+      21,     7,    33,    -6,    10,    11,    48,    49,     1,     9,
+      81,     1,    50,    29,    36,    37,    12,    13,    14,    15,
+      16,    55,    56,    90,    17,    34,    35,    31,    18,    19,
+      76,    30,    20,   -53,    58,    59,    60,    61,    62,    63,
+      64,    65,    66,    67,    68,    69,    10,    11,    51,    79,
+      70,    71,    52,    72,    73,    82,    83,    54,    12,    13,
+      14,    15,    16,    74,    50,    80,    17,    85,    87,    27,
+      18,    19,    89,    92,    20,    38,    39,    40,    41,    42,
+      43,    91,    28,     0,     0,     0,     0,     0,    44,    45,
+      46,    47,    48,    49,     0,     0,     0,     0,    50,     0,
+      86,    38,    39,    40,    41,    42,    43,     0,     0,     0,
+       0,     0,     0,     0,    44,    45,    46,    47,    48,    49,
+       0,     0,     0,    57,    50,    38,    39,    40,    41,    42,
+      43,    88,     0,     0,     0,     0,     0,     0,    44,    45,
+      46,    47,    48,    49,     0,     0,     0,     0,    50,    38,
+      39,    40,    41,    42,    43,     0,     0,     0,     0,     0,
+       0,     0,    44,    45,    46,    47,    48,    49,     0,     0,
+       0,     0,    50,    39,    40,    41,    42,    43,     0,     0,
+       0,     0,     0,     0,     0,    44,    45,    46,    47,    48,
+      49,    40,    41,    42,    43,    50,     0,     0,     0,     0,
+       0,     0,    44,    45,    46,    47,    48,    49,    42,    43,
+       0,     0,    50,     0,     0,     0,     0,    44,    45,    46,
+      47,    48,    49,    46,    47,    48,    49,    50,     0,     0,
+       0,    50
+};
+
+static const yytype_int8 yycheck[] =
+{
+       4,    18,    15,     0,     6,     7,    27,    28,     5,     0,
+      72,     5,    33,    16,    18,    19,    18,    19,    20,    21,
+      22,    34,    35,    85,    26,    20,    21,    17,    30,    31,
+      32,    31,    34,    31,    38,    39,    40,    41,    42,    43,
+      44,    45,    46,    47,    48,    49,     6,     7,    29,    53,
+      18,    19,    31,     8,     9,    18,    19,    34,    18,    19,
+      20,    21,    22,    18,    33,    32,    26,    29,    32,     6,
+      30,    31,    29,    89,    34,    10,    11,    12,    13,    14,
+      15,    86,     7,    -1,    -1,    -1,    -1,    -1,    23,    24,
+      25,    26,    27,    28,    -1,    -1,    -1,    -1,    33,    -1,
+      35,    10,    11,    12,    13,    14,    15,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    23,    24,    25,    26,    27,    28,
+      -1,    -1,    -1,    32,    33,    10,    11,    12,    13,    14,
+      15,    16,    -1,    -1,    -1,    -1,    -1,    -1,    23,    24,
+      25,    26,    27,    28,    -1,    -1,    -1,    -1,    33,    10,
+      11,    12,    13,    14,    15,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    23,    24,    25,    26,    27,    28,    -1,    -1,
+      -1,    -1,    33,    11,    12,    13,    14,    15,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    23,    24,    25,    26,    27,
+      28,    12,    13,    14,    15,    33,    -1,    -1,    -1,    -1,
+      -1,    -1,    23,    24,    25,    26,    27,    28,    14,    15,
+      -1,    -1,    33,    -1,    -1,    -1,    -1,    23,    24,    25,
+      26,    27,    28,    25,    26,    27,    28,    33,    -1,    -1,
+      -1,    33
+};
+
+  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+     symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     5,    37,    38,    39,    40,    41,    18,    53,     0,
+       6,     7,    18,    19,    20,    21,    22,    26,    30,    31,
+      34,    43,    44,    45,    49,    50,    51,    40,    53,    16,
+      31,    17,    46,    46,    20,    21,    43,    43,    10,    11,
+      12,    13,    14,    15,    23,    24,    25,    26,    27,    28,
+      33,    29,    31,    42,    34,    46,    46,    32,    43,    43,
+      43,    43,    43,    43,    43,    43,    43,    43,    43,    43,
+      18,    19,     8,     9,    18,    47,    32,    43,    52,    43,
+      32,    47,    18,    19,    48,    29,    35,    32,    16,    29,
+      47,    52,    48
+};
+
+  /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    36,    37,    37,    39,    38,    40,    40,    41,    42,
+      42,    43,    43,    43,    43,    43,    43,    43,    43,    43,
+      43,    43,    43,    43,    43,    43,    43,    43,    43,    43,
+      43,    43,    43,    43,    44,    44,    44,    44,    45,    46,
+      46,    47,    47,    47,    47,    48,    48,    48,    49,    49,
+      49,    50,    50,    51,    52,    52,    53,    53
+};
+
+  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     1,     1,     0,     2,     0,     2,     4,     0,
+       3,     1,     1,     1,     1,     2,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+       3,     1,     1,     3,     2,     2,     3,     3,     1,     0,
+       1,     1,     3,     2,     2,     1,     3,     1,     1,     4,
+       1,     4,     3,     1,     1,     3,     0,     2
+};
+
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+#define YYEMPTY         (-2)
+#define YYEOF           0
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                  \
+do                                                              \
+  if (yychar == YYEMPTY)                                        \
+    {                                                           \
+      yychar = (Token);                                         \
+      yylval = (Value);                                         \
+      YYPOPSTACK (yylen);                                       \
+      yystate = *yyssp;                                         \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    {                                                           \
+      yyerror (prg_out, YY_("syntax error: cannot back up")); \
+      YYERROR;                                                  \
+    }                                                           \
+while (0)
+
+/* Error token number */
+#define YYTERROR        1
+#define YYERRCODE       256
+
+
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Type, Value, prg_out); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT.  |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, pcb_qry_node_t **prg_out)
+{
+  FILE *yyo = yyoutput;
+  YYUSE (yyo);
+  YYUSE (prg_out);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+  YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, pcb_qry_node_t **prg_out)
+{
+  YYFPRINTF (yyoutput, "%s %s (",
+             yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, prg_out);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, pcb_qry_node_t **prg_out)
+{
+  unsigned long int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       yystos[yyssp[yyi + 1 - yynrhs]],
+                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                                              , prg_out);
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule, prg_out); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+        switch (*++yyp)
+          {
+          case '\'':
+          case ',':
+            goto do_not_strip_quotes;
+
+          case '\\':
+            if (*++yyp != '\\')
+              goto do_not_strip_quotes;
+            /* Fall through.  */
+          default:
+            if (yyres)
+              yyres[yyn] = *yyp;
+            yyn++;
+            break;
+
+          case '"':
+            if (yyres)
+              yyres[yyn] = '\0';
+            return yyn;
+          }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = YY_NULLPTR;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                {
+                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+                  if (! (yysize <= yysize1
+                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                    return 2;
+                  yysize = yysize1;
+                }
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  {
+    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+      return 2;
+    yysize = yysize1;
+  }
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, pcb_qry_node_t **prg_out)
+{
+  YYUSE (yyvaluep);
+  YYUSE (prg_out);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YYUSE (yytype);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (pcb_qry_node_t **prg_out)
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       'yyss': related to states.
+       'yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        YYSTYPE *yyvs1 = yyvs;
+        yytype_int16 *yyss1 = yyss;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * sizeof (*yyssp),
+                    &yyvs1, yysize * sizeof (*yyvsp),
+                    &yystacksize);
+
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yytype_int16 *yyss1 = yyss;
+        union yyalloc *yyptr =
+          (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+        if (! yyptr)
+          goto yyexhaustedlab;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = yylex ();
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 133 "query_y.y" /* yacc.c:1646  */
+    { *prg_out = (yyvsp[0].n); }
+#line 1418 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 3:
+#line 134 "query_y.y" /* yacc.c:1646  */
+    { *prg_out = (yyvsp[0].n); }
+#line 1424 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 4:
+#line 139 "query_y.y" /* yacc.c:1646  */
+    { iter_ctx = pcb_qry_iter_alloc(); }
+#line 1430 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 5:
+#line 140 "query_y.y" /* yacc.c:1646  */
+    {
+		(yyval.n) = pcb_qry_n_alloc(PCBQ_EXPR_PROG);
+		(yyval.n)->data.children = pcb_qry_n_alloc(PCBQ_ITER_CTX);
+		(yyval.n)->data.children->parent = (yyval.n);
+		(yyval.n)->data.children->data.iter_ctx = iter_ctx;
+		(yyval.n)->data.children->next = (yyvsp[0].n);
+		(yyvsp[0].n)->parent = (yyval.n);
+	}
+#line 1443 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 6:
+#line 152 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = NULL; }
+#line 1449 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 7:
+#line 153 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[-1].n); (yyvsp[-1].n)->next = (yyvsp[0].n); }
+#line 1455 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 8:
+#line 157 "query_y.y" /* yacc.c:1646  */
+    {
+		(yyval.n) = pcb_qry_n_alloc(PCBQ_RULE);
+		(yyval.n)->data.children = (yyvsp[-2].n);
+		(yyvsp[-2].n)->parent = (yyval.n);
+		(yyval.n)->data.children->next = pcb_qry_n_alloc(PCBQ_ITER_CTX);
+		(yyval.n)->data.children->next->data.iter_ctx = iter_ctx;
+		(yyval.n)->data.children->next->next = (yyvsp[0].n);
+		(yyvsp[0].n)->parent = (yyval.n);
+		}
+#line 1469 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 9:
+#line 169 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = NULL; }
+#line 1475 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 10:
+#line 170 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[-2].n); (yyvsp[-2].n)->next = (yyvsp[-1].n); }
+#line 1481 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 11:
+#line 174 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); }
+#line 1487 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 12:
+#line 175 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); }
+#line 1493 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 13:
+#line 176 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); }
+#line 1499 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 14:
+#line 177 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_DATA_INVALID); }
+#line 1505 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 15:
+#line 178 "query_y.y" /* yacc.c:1646  */
+    { UNOP((yyval.n), PCBQ_OP_NOT, (yyvsp[0].n)); }
+#line 1511 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 16:
+#line 179 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[-1].n); }
+#line 1517 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 17:
+#line 180 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_AND, (yyvsp[0].n)); }
+#line 1523 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 18:
+#line 181 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_OR, (yyvsp[0].n)); }
+#line 1529 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 19:
+#line 182 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_EQ, (yyvsp[0].n)); }
+#line 1535 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 20:
+#line 183 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_NEQ, (yyvsp[0].n)); }
+#line 1541 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 21:
+#line 184 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_GTEQ, (yyvsp[0].n)); }
+#line 1547 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 22:
+#line 185 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_LTEQ, (yyvsp[0].n)); }
+#line 1553 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 23:
+#line 186 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_GT, (yyvsp[0].n)); }
+#line 1559 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 24:
+#line 187 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_LT, (yyvsp[0].n)); }
+#line 1565 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 25:
+#line 188 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_ADD, (yyvsp[0].n)); }
+#line 1571 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 26:
+#line 189 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_SUB, (yyvsp[0].n)); }
+#line 1577 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 27:
+#line 190 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_MUL, (yyvsp[0].n)); }
+#line 1583 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 28:
+#line 191 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_DIV, (yyvsp[0].n)); }
+#line 1589 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 29:
+#line 192 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_MATCH, make_regex_free((yyvsp[0].s))); }
+#line 1595 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 30:
+#line 193 "query_y.y" /* yacc.c:1646  */
+    { BINOP((yyval.n), (yyvsp[-2].n), PCBQ_OP_MATCH, make_regex_free((yyvsp[0].s))); }
+#line 1601 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 31:
+#line 194 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); }
+#line 1607 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 32:
+#line 195 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); }
+#line 1613 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 33:
+#line 196 "query_y.y" /* yacc.c:1646  */
+    {
+		pcb_qry_node_t *n;
+		(yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD_OF);
+		(yyval.n)->data.children = (yyvsp[-2].n);
+		(yyvsp[-2].n)->next = (yyvsp[0].n);
+		(yyvsp[-2].n)->parent = (yyval.n);
+		for(n = (yyvsp[0].n); n != NULL; n = n->next)
+			n->parent = (yyval.n);
+		}
+#line 1627 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 34:
+#line 208 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_DATA_COORD);  UNIT_CONV((yyval.n)->data.crd, 0, (yyvsp[-1].c), (yyvsp[0].u)); }
+#line 1633 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 35:
+#line 209 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_DATA_DOUBLE); UNIT_CONV((yyval.n)->data.dbl, 0, (yyvsp[-1].d), (yyvsp[0].u)); }
+#line 1639 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 36:
+#line 210 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_DATA_COORD);  UNIT_CONV((yyval.n)->data.crd, 1, (yyvsp[-1].c), (yyvsp[0].u)); }
+#line 1645 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 37:
+#line 211 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_DATA_DOUBLE); UNIT_CONV((yyval.n)->data.dbl, 1, (yyvsp[-1].d), (yyvsp[0].u)); }
+#line 1651 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 38:
+#line 215 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_DATA_STRING);  (yyval.n)->data.str = (yyvsp[0].s); }
+#line 1657 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 39:
+#line 219 "query_y.y" /* yacc.c:1646  */
+    { (yyval.u) = NULL; }
+#line 1663 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 40:
+#line 220 "query_y.y" /* yacc.c:1646  */
+    { (yyval.u) = (yyvsp[0].u); }
+#line 1669 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 41:
+#line 224 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD); (yyval.n)->data.str = (yyvsp[0].s); (yyval.n)->precomp.fld = query_fields_sphash((yyvsp[0].s)); }
+#line 1675 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 42:
+#line 225 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD); (yyval.n)->data.str = (yyvsp[-2].s); (yyval.n)->precomp.fld = query_fields_sphash((yyvsp[-2].s)); (yyval.n)->next = (yyvsp[0].n); }
+#line 1681 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 43:
+#line 226 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); /* just ignore .p. */ }
+#line 1687 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 44:
+#line 227 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD); (yyval.n)->data.str = pcb_strdup("a"); (yyval.n)->precomp.fld = query_fields_sphash("a"); (yyval.n)->next = (yyvsp[0].n); }
+#line 1693 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 45:
+#line 231 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD); (yyval.n)->data.str = (yyvsp[0].s); }
+#line 1699 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 46:
+#line 232 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD); (yyval.n)->data.str = attrib_prepend_free((yyvsp[0].n)->data.str, (yyvsp[-2].s), '.'); }
+#line 1705 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 47:
+#line 233 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FIELD); (yyval.n)->data.str = (yyvsp[0].s); }
+#line 1711 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 48:
+#line 237 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_VAR); (yyval.n)->data.crd = pcb_qry_iter_var(iter_ctx, (yyvsp[0].s), 1); free((yyvsp[0].s)); }
+#line 1717 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 49:
+#line 238 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_LISTVAR); (yyval.n)->data.str = pcb_strdup("@"); }
+#line 1723 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 50:
+#line 239 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_VAR); (yyval.n)->data.crd = pcb_qry_iter_var(iter_ctx, "@", 1); }
+#line 1729 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 51:
+#line 243 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FCALL); (yyval.n)->data.children = (yyvsp[-3].n); (yyval.n)->data.children->next = (yyvsp[-1].n); (yyvsp[-3].n)->parent = (yyvsp[-1].n)->parent = (yyval.n); }
+#line 1735 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 52:
+#line 244 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_FCALL); (yyval.n)->data.children = (yyvsp[-2].n); (yyvsp[-2].n)->parent = (yyval.n); }
+#line 1741 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 53:
+#line 248 "query_y.y" /* yacc.c:1646  */
+    {
+		(yyval.n) = pcb_qry_n_alloc(PCBQ_FNAME);
+		(yyval.n)->data.fnc = pcb_qry_fnc_lookup((yyvsp[0].s));
+		if ((yyval.n)->data.fnc == NULL) {
+			yyerror("Unknown function");
+			free((yyvsp[0].s));
+			return -1;
+		}
+		free((yyvsp[0].s));
+	}
+#line 1756 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 54:
+#line 262 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[0].n); }
+#line 1762 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 55:
+#line 263 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = (yyvsp[-2].n); (yyval.n)->next = (yyvsp[0].n); }
+#line 1768 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 56:
+#line 267 "query_y.y" /* yacc.c:1646  */
+    { (yyval.n) = pcb_qry_n_alloc(PCBQ_RNAME); (yyval.n)->data.str = (const char *)pcb_strdup(""); }
+#line 1774 "query_y.c" /* yacc.c:1646  */
+    break;
+
+  case 57:
+#line 268 "query_y.y" /* yacc.c:1646  */
+    {
+			int l1 = strlen((yyvsp[0].n)->data.str), l2 = strlen((yyvsp[-1].s));
+			
+			(yyvsp[0].n)->data.str = (const char *)realloc((void *)(yyvsp[0].n)->data.str, l1+l2+2);
+			memcpy((char *)(yyvsp[0].n)->data.str+l1, (yyvsp[-1].s), l2+1);
+			free((yyvsp[-1].s));
+		}
+#line 1786 "query_y.c" /* yacc.c:1646  */
+    break;
+
+
+#line 1790 "query_y.c" /* yacc.c:1646  */
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (prg_out, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (prg_out, yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == YYEOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval, prg_out);
+          yychar = YYEMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYTERROR;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  yystos[yystate], yyvsp, prg_out);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (prg_out, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval, prg_out);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  yystos[*yyssp], yyvsp, prg_out);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  return yyresult;
+}
diff --git a/src_plugins/query/query_y.h b/src_plugins/query/query_y.h
new file mode 100644
index 0000000..8534068
--- /dev/null
+++ b/src_plugins/query/query_y.h
@@ -0,0 +1,95 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY_QRY_QUERY_Y_H_INCLUDED
+# define YY_QRY_QUERY_Y_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int qry_debug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    T_LET = 258,
+    T_ASSERT = 259,
+    T_RULE = 260,
+    T_LIST = 261,
+    T_INVALID = 262,
+    T_FLD_P = 263,
+    T_FLD_A = 264,
+    T_OR = 265,
+    T_AND = 266,
+    T_EQ = 267,
+    T_NEQ = 268,
+    T_GTEQ = 269,
+    T_LTEQ = 270,
+    T_NL = 271,
+    T_UNIT = 272,
+    T_STR = 273,
+    T_QSTR = 274,
+    T_INT = 275,
+    T_DBL = 276,
+    T_CONST = 277
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 95 "query_y.y" /* yacc.c:1909  */
+
+	char *s;
+	Coord c;
+	double d;
+	const Unit *u;
+	pcb_qry_node_t *n;
+
+#line 85 "query_y.h" /* yacc.c:1909  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE qry_lval;
+
+int qry_parse (pcb_qry_node_t **prg_out);
+
+#endif /* !YY_QRY_QUERY_Y_H_INCLUDED  */
diff --git a/src_plugins/query/query_y.y b/src_plugins/query/query_y.y
new file mode 100644
index 0000000..25ed59b
--- /dev/null
+++ b/src_plugins/query/query_y.y
@@ -0,0 +1,275 @@
+%{
+/*
+ *                            COPYRIGHT
+ *
+ *  pcb-rnd, interactive printed circuit board design
+ *  Copyright (C) 2016 Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Query language - compiler: grammar */
+
+#include <assert.h>
+#include "global.h"
+#include "unit.h"
+#include "query.h"
+#include "query_l.h"
+#include "compat_misc.h"
+#include "fields_sphash.h"
+
+#define UNIT_CONV(dst, negative, val, unit) \
+do { \
+	dst = val; \
+	if (negative) \
+		dst = -dst; \
+	if (unit != NULL) { \
+		if (unit->family == IMPERIAL) \
+			dst = PCB_MIL_TO_COORD(dst); \
+		else if (unit->family == METRIC) \
+			dst = PCB_MM_TO_COORD(dst); \
+		dst /= unit->scale_factor; \
+	} \
+} while(0)
+
+#define BINOP(dst, op1, operator, op2) \
+do { \
+	assert(op2->next == NULL); \
+	assert(op2->next == NULL); \
+	dst = pcb_qry_n_alloc(operator); \
+	pcb_qry_n_insert(dst, op2); \
+	pcb_qry_n_insert(dst, op1); \
+} while(0)
+
+#define UNOP(dst, operator, op) \
+do { \
+	assert(op->next == NULL); \
+	dst = pcb_qry_n_alloc(operator); \
+	pcb_qry_n_insert(dst, op); \
+} while(0)
+
+static pcb_query_iter_t *iter_ctx;
+
+static char *attrib_prepend_free(char *orig, char *prep, char sep)
+{
+	int l1 = strlen(orig), l2 = strlen(prep);
+	char *res = malloc(l1+l2+2);
+	memcpy(res, prep, l2);
+	res[l2] = sep;
+	memcpy(res+l2+1, orig, l1+1);
+	free(orig);
+	free(prep);
+	return res;
+}
+
+static pcb_qry_node_t *make_regex_free(char *str)
+{
+	pcb_qry_node_t *res = pcb_qry_n_alloc(PCBQ_DATA_REGEX);
+	res->data.str = str;
+	res->precomp.regex = re_se_comp(str);
+	if (res->precomp.regex == NULL)
+		yyerror(NULL, "Invalid regex\n");
+	return res;
+}
+
+
+%}
+
+%name-prefix "qry_"
+%error-verbose
+%parse-param {pcb_qry_node_t **prg_out}
+
+%union {
+	char *s;
+	Coord c;
+	double d;
+	const Unit *u;
+	pcb_qry_node_t *n;
+}
+
+%token     T_LET T_ASSERT T_RULE T_LIST T_INVALID T_FLD_P T_FLD_A
+%token     T_OR T_AND T_EQ T_NEQ T_GTEQ T_LTEQ
+%token     T_NL
+%token <u> T_UNIT
+%token <s> T_STR T_QSTR
+%token <c> T_INT
+%token <d> T_DBL
+%token <n> T_CONST
+
+/* the usual binary operators */
+%left T_OR
+%left T_AND
+%left T_EQ T_NEQ
+%left '<' '>' T_GTEQ T_LTEQ
+%left '+' '-'
+%left '*' '/'
+%left '.'
+
+/* Unary operators */
+%right '!'
+
+%left '('
+
+%type <n> number fields attribs var fname fcall fargs words string_literal
+%type <n> expr exprs program_expr program_rules rule
+%type <u> maybe_unit
+
+%%
+
+program:
+	  program_rules    { *prg_out = $1; }
+	| program_expr     { *prg_out = $1; }
+	;
+
+/* The program is a single expression - useful for search */
+program_expr:
+	{ iter_ctx = pcb_qry_iter_alloc(); }
+	expr               {
+		$$ = pcb_qry_n_alloc(PCBQ_EXPR_PROG);
+		$$->data.children = pcb_qry_n_alloc(PCBQ_ITER_CTX);
+		$$->data.children->parent = $$;
+		$$->data.children->data.iter_ctx = iter_ctx;
+		$$->data.children->next = $2;
+		$2->parent = $$;
+	}
+	;
+
+/* The program is a collection of rules - useful for the DRC */
+program_rules:
+	  /* empty */            { $$ = NULL; }
+	| rule program_rules     { $$ = $1; $1->next = $2; }
+	;
+
+rule:
+	T_RULE words T_NL exprs  {
+		$$ = pcb_qry_n_alloc(PCBQ_RULE);
+		$$->data.children = $2;
+		$2->parent = $$;
+		$$->data.children->next = pcb_qry_n_alloc(PCBQ_ITER_CTX);
+		$$->data.children->next->data.iter_ctx = iter_ctx;
+		$$->data.children->next->next = $4;
+		$4->parent = $$;
+		}
+	;
+
+exprs:
+	  /* empty */            { $$ = NULL; }
+	| exprs expr T_NL        { $$ = $1; $1->next = $2; }
+	;
+
+expr:
+	  fcall                  { $$ = $1; }
+	| number                 { $$ = $1; }
+	| string_literal         { $$ = $1; }
+	| T_INVALID              { $$ = pcb_qry_n_alloc(PCBQ_DATA_INVALID); }
+	| '!' expr               { UNOP($$, PCBQ_OP_NOT, $2); }
+	| '(' expr ')'           { $$ = $2; }
+	| expr T_AND expr        { BINOP($$, $1, PCBQ_OP_AND, $3); }
+	| expr T_OR expr         { BINOP($$, $1, PCBQ_OP_OR, $3); }
+	| expr T_EQ expr         { BINOP($$, $1, PCBQ_OP_EQ, $3); }
+	| expr T_NEQ expr        { BINOP($$, $1, PCBQ_OP_NEQ, $3); }
+	| expr T_GTEQ expr       { BINOP($$, $1, PCBQ_OP_GTEQ, $3); }
+	| expr T_LTEQ expr       { BINOP($$, $1, PCBQ_OP_LTEQ, $3); }
+	| expr '>' expr          { BINOP($$, $1, PCBQ_OP_GT, $3); }
+	| expr '<' expr          { BINOP($$, $1, PCBQ_OP_LT, $3); }
+	| expr '+' expr          { BINOP($$, $1, PCBQ_OP_ADD, $3); }
+	| expr '-' expr          { BINOP($$, $1, PCBQ_OP_SUB, $3); }
+	| expr '*' expr          { BINOP($$, $1, PCBQ_OP_MUL, $3); }
+	| expr '/' expr          { BINOP($$, $1, PCBQ_OP_DIV, $3); }
+	| expr '~' T_STR         { BINOP($$, $1, PCBQ_OP_MATCH, make_regex_free($3)); }
+	| expr '~' T_QSTR        { BINOP($$, $1, PCBQ_OP_MATCH, make_regex_free($3)); }
+	| T_CONST                { $$ = $1; }
+	| var                    { $$ = $1; }
+	| var '.' fields         {
+		pcb_qry_node_t *n;
+		$$ = pcb_qry_n_alloc(PCBQ_FIELD_OF);
+		$$->data.children = $1;
+		$1->next = $3;
+		$1->parent = $$;
+		for(n = $3; n != NULL; n = n->next)
+			n->parent = $$;
+		}
+	;
+
+number:
+	  T_INT maybe_unit       { $$ = pcb_qry_n_alloc(PCBQ_DATA_COORD);  UNIT_CONV($$->data.crd, 0, $1, $2); }
+	| T_DBL maybe_unit       { $$ = pcb_qry_n_alloc(PCBQ_DATA_DOUBLE); UNIT_CONV($$->data.dbl, 0, $1, $2); }
+	| '-' T_INT maybe_unit   { $$ = pcb_qry_n_alloc(PCBQ_DATA_COORD);  UNIT_CONV($$->data.crd, 1, $2, $3); }
+	| '-' T_DBL maybe_unit   { $$ = pcb_qry_n_alloc(PCBQ_DATA_DOUBLE); UNIT_CONV($$->data.dbl, 1, $2, $3); }
+	;
+
+string_literal:
+	T_QSTR                   { $$ = pcb_qry_n_alloc(PCBQ_DATA_STRING);  $$->data.str = $1; }
+	;
+
+maybe_unit:
+	  /* empty */            { $$ = NULL; }
+	| T_UNIT                 { $$ = $1; }
+	;
+
+fields:
+	  T_STR                  { $$ = pcb_qry_n_alloc(PCBQ_FIELD); $$->data.str = $1; $$->precomp.fld = query_fields_sphash($1); }
+	| T_STR '.' fields       { $$ = pcb_qry_n_alloc(PCBQ_FIELD); $$->data.str = $1; $$->precomp.fld = query_fields_sphash($1); $$->next = $3; }
+	| T_FLD_P fields         { $$ = $2; /* just ignore .p. */ }
+	| T_FLD_A attribs        { $$ = pcb_qry_n_alloc(PCBQ_FIELD); $$->data.str = pcb_strdup("a"); $$->precomp.fld = query_fields_sphash("a"); $$->next = $2; }
+	;
+
+attribs:
+	  T_STR                  { $$ = pcb_qry_n_alloc(PCBQ_FIELD); $$->data.str = $1; }
+	| T_STR '.' attribs      { $$ = pcb_qry_n_alloc(PCBQ_FIELD); $$->data.str = attrib_prepend_free($3->data.str, $1, '.'); }
+	| T_QSTR                 { $$ = pcb_qry_n_alloc(PCBQ_FIELD); $$->data.str = $1; }
+	;
+
+var:
+	  T_STR                  { $$ = pcb_qry_n_alloc(PCBQ_VAR); $$->data.crd = pcb_qry_iter_var(iter_ctx, $1, 1); free($1); }
+	| T_LIST '(' '@' ')'     { $$ = pcb_qry_n_alloc(PCBQ_LISTVAR); $$->data.str = pcb_strdup("@"); }
+	| '@'                    { $$ = pcb_qry_n_alloc(PCBQ_VAR); $$->data.crd = pcb_qry_iter_var(iter_ctx, "@", 1); }
+	;
+
+fcall:
+	  fname '(' fargs ')'    { $$ = pcb_qry_n_alloc(PCBQ_FCALL); $$->data.children = $1; $$->data.children->next = $3; $1->parent = $3->parent = $$; }
+	| fname '(' ')'          { $$ = pcb_qry_n_alloc(PCBQ_FCALL); $$->data.children = $1; $1->parent = $$; }
+	;
+
+fname:
+	T_STR   {
+		$$ = pcb_qry_n_alloc(PCBQ_FNAME);
+		$$->data.fnc = pcb_qry_fnc_lookup($1);
+		if ($$->data.fnc == NULL) {
+			yyerror("Unknown function");
+			free($1);
+			return -1;
+		}
+		free($1);
+	}
+	;
+
+
+fargs:
+	  expr                   { $$ = $1; }
+	| expr ',' fargs         { $$ = $1; $$->next = $3; }
+	;
+
+words:
+	  /* empty */            { $$ = pcb_qry_n_alloc(PCBQ_RNAME); $$->data.str = (const char *)pcb_strdup(""); }
+	| T_STR words            {
+			int l1 = strlen($2->data.str), l2 = strlen($1);
+			
+			$2->data.str = (const char *)realloc((void *)$2->data.str, l1+l2+2);
+			memcpy((char *)$2->data.str+l1, $1, l2+1);
+			free($1);
+		}
+	;
diff --git a/src_plugins/renumber/Makefile b/src_plugins/renumber/Makefile
new file mode 100644
index 0000000..a0c409d
--- /dev/null
+++ b/src_plugins/renumber/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_renumber
+	
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/renumber/Plug.tmpasm b/src_plugins/renumber/Plug.tmpasm
new file mode 100644
index 0000000..46fc7ef
--- /dev/null
+++ b/src_plugins/renumber/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {renumber}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/renumber/renumber.o $(PLUGDIR)/renumber/renumberblock.o @]
+
+switch /local/pcb/renumber/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/renumber/README b/src_plugins/renumber/README
new file mode 100644
index 0000000..a3e4f98
--- /dev/null
+++ b/src_plugins/renumber/README
@@ -0,0 +1,6 @@
+Renumber elements (renaming them) and generate a text file for
+back annotation.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/renumber/renumber.c b/src_plugins/renumber/renumber.c
new file mode 100644
index 0000000..604db8e
--- /dev/null
+++ b/src_plugins/renumber/renumber.c
@@ -0,0 +1,382 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "action_helper.h"
+#include "change.h"
+#include "error.h"
+#include "undo.h"
+#include "misc.h"
+#include "set.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "conf_core.h"
+#include "compat_misc.h"
+
+#include "pcb-printf.h"
+
+/* --------------------------------------------------------------------------- */
+
+static const char renumber_syntax[] = "Renumber()\n" "Renumber(filename)";
+
+static const char renumber_help[] =
+	"Renumber all elements.  The changes will be recorded to filename\n"
+	"for use in backannotating these changes to the schematic.";
+
+/* %start-doc actions Renumber
+
+%end-doc */
+
+#define WTF 0
+
+static int ActionRenumber(int argc, const char **argv, Coord x, Coord y)
+{
+	pcb_bool changed = pcb_false;
+	ElementTypePtr *element_list;
+	ElementTypePtr *locked_element_list;
+	unsigned int i, j, k, cnt, lock_cnt;
+	unsigned int tmpi;
+	size_t sz;
+	char *tmps;
+	const char *name;
+	FILE *out;
+	static char *default_file = NULL;
+	size_t cnt_list_sz = 100;
+	struct _cnt_list {
+		char *name;
+		unsigned int cnt;
+	} *cnt_list;
+	char **was, **is, *pin;
+	unsigned int c_cnt = 0, numele;
+	int ok;
+	pcb_bool free_name = pcb_false;
+
+	if (argc < 1) {
+		/*
+		 * We deal with the case where name already exists in this
+		 * function so the GUI doesn't need to deal with it
+		 */
+		name = gui->fileselect(_("Save Renumber Annotation File As ..."),
+													 _("Choose a file to record the renumbering to.\n"
+														 "This file may be used to back annotate the\n"
+														 "change to the schematics.\n"), default_file, ".eco", "eco", 0);
+
+		free_name = pcb_true;
+	}
+	else
+		name = argv[0];
+
+	if (default_file) {
+		free(default_file);
+		default_file = NULL;
+	}
+
+	if (name && *name) {
+		default_file = pcb_strdup(name);
+	}
+
+	if ((out = fopen(name, "r"))) {
+		fclose(out);
+		if (!gui->confirm_dialog(_("File exists!  Ok to overwrite?"), 0)) {
+			if (free_name && name)
+				free((char*)name);
+			return 0;
+		}
+	}
+
+	if ((out = fopen(name, "w")) == NULL) {
+		Message(PCB_MSG_DEFAULT, _("Could not open %s\n"), name);
+		if (free_name && name)
+			free((char*)name);
+		return 1;
+	}
+
+	if (free_name && name)
+		free((char*)name);
+
+	fprintf(out, "*COMMENT* PCB Annotation File\n");
+	fprintf(out, "*FILEVERSION* 20061031\n");
+
+	/*
+	 * Make a first pass through all of the elements and sort them out
+	 * by location on the board.  While here we also collect a list of
+	 * locked elements.
+	 *
+	 * We'll actually renumber things in the 2nd pass.
+	 */
+	numele = elementlist_length(&PCB->Data->Element);
+	element_list = (ElementType **) calloc(numele, sizeof(ElementTypePtr));
+	locked_element_list = (ElementType **) calloc(numele, sizeof(ElementTypePtr));
+	was = (char **) calloc(numele, sizeof(char *));
+	is = (char **) calloc(numele, sizeof(char *));
+	if (element_list == NULL || locked_element_list == NULL || was == NULL || is == NULL) {
+		fprintf(stderr, "calloc() failed in ActionRenumber\n");
+		exit(1);
+	}
+
+
+	cnt = 0;
+	lock_cnt = 0;
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_LOCK, element->Name) || TEST_FLAG(PCB_FLAG_LOCK, element)) {
+			/*
+			 * add to the list of locked elements which we won't try to
+			 * renumber and whose reference designators are now reserved.
+			 */
+			pcb_fprintf(out,
+									"*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
+									UNKNOWN(NAMEONPCB_NAME(element)), element->MarkX, element->MarkY);
+			locked_element_list[lock_cnt] = element;
+			lock_cnt++;
+		}
+
+		else {
+			/* count of devices which will be renumbered */
+			cnt++;
+
+			/* search for correct position in the list */
+			i = 0;
+			while (element_list[i] && element->MarkY > element_list[i]->MarkY)
+				i++;
+
+			/*
+			 * We have found the position where we have the first element that
+			 * has the same Y value or a lower Y value.  Now move forward if
+			 * needed through the X values
+			 */
+			while (element_list[i]
+						 && element->MarkY == element_list[i]->MarkY && element->MarkX > element_list[i]->MarkX)
+				i++;
+
+			for (j = cnt - 1; j > i; j--) {
+				element_list[j] = element_list[j - 1];
+			}
+			element_list[i] = element;
+		}
+	}
+	END_LOOP;
+
+
+	/*
+	 * Now that the elements are sorted by board position, we go through
+	 * and renumber them.
+	 */
+
+	/*
+	 * turn off the flag which requires unique names so it doesn't get
+	 * in our way.  When we're done with the renumber we will have unique
+	 * names.
+	 */
+
+	conf_force_set_bool(conf_core.editor.unique_names, 0);
+
+	cnt_list = (struct _cnt_list *) calloc(cnt_list_sz, sizeof(struct _cnt_list));
+	for (i = 0; i < cnt; i++) {
+		/* If there is no refdes, maybe just spit out a warning */
+		if (NAMEONPCB_NAME(element_list[i])) {
+			/* figure out the prefix */
+			tmps = pcb_strdup(NAMEONPCB_NAME(element_list[i]));
+			j = 0;
+			while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
+						 && tmps[j] != '?')
+				j++;
+			tmps[j] = '\0';
+
+			/* check the counter for this prefix */
+			for (j = 0; cnt_list[j].name && (strcmp(cnt_list[j].name, tmps) != 0)
+					 && j < cnt_list_sz; j++);
+
+			/* grow the list if needed */
+			if (j == cnt_list_sz) {
+				cnt_list_sz += 100;
+				cnt_list = (struct _cnt_list *) realloc(cnt_list, cnt_list_sz);
+				if (cnt_list == NULL) {
+					fprintf(stderr, "realloc() failed in ActionRenumber\n");
+					exit(1);
+				}
+				/* zero out the memory that we added */
+				for (tmpi = j; tmpi < cnt_list_sz; tmpi++) {
+					cnt_list[tmpi].name = NULL;
+					cnt_list[tmpi].cnt = 0;
+				}
+			}
+
+			/*
+			 * start a new counter if we don't have a counter for this
+			 * prefix
+			 */
+			if (!cnt_list[j].name) {
+				cnt_list[j].name = pcb_strdup(tmps);
+				cnt_list[j].cnt = 0;
+			}
+
+			/*
+			 * check to see if the new refdes is already used by a
+			 * locked element
+			 */
+			do {
+				ok = 1;
+				cnt_list[j].cnt++;
+				free(tmps);
+
+				/* space for the prefix plus 1 digit plus the '\0' */
+				sz = strlen(cnt_list[j].name) + 2;
+
+				/* and 1 more per extra digit needed to hold the number */
+				tmpi = cnt_list[j].cnt;
+				while (tmpi > 10) {
+					sz++;
+					tmpi = tmpi / 10;
+				}
+				tmps = (char *) malloc(sz * sizeof(char));
+				sprintf(tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
+
+				/*
+				 * now compare to the list of reserved (by locked
+				 * elements) names
+				 */
+				for (k = 0; k < lock_cnt; k++) {
+					if (strcmp(UNKNOWN(NAMEONPCB_NAME(locked_element_list[k])), tmps) == 0) {
+						ok = 0;
+						break;
+					}
+				}
+
+			}
+			while (!ok);
+
+			if (strcmp(tmps, NAMEONPCB_NAME(element_list[i])) != 0) {
+				fprintf(out, "*RENAME* \"%s\" \"%s\"\n", NAMEONPCB_NAME(element_list[i]), tmps);
+
+				/* add this rename to our table of renames so we can update the netlist */
+				was[c_cnt] = pcb_strdup(NAMEONPCB_NAME(element_list[i]));
+				is[c_cnt] = pcb_strdup(tmps);
+				c_cnt++;
+
+				AddObjectToChangeNameUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element_list[i], NAMEONPCB_NAME(element_list[i]));
+
+				ChangeObjectName(PCB_TYPE_ELEMENT, element_list[i], NULL, NULL, tmps);
+				changed = pcb_true;
+
+				/* we don't free tmps in this case because it is used */
+			}
+			else
+				free(tmps);
+		}
+		else {
+			pcb_fprintf(out, "*WARN* Element at %$md has no name.\n", element_list[i]->MarkX, element_list[i]->MarkY);
+		}
+
+	}
+
+	fclose(out);
+
+	/* restore the unique flag setting */
+	conf_update(NULL);
+
+	if (changed) {
+
+		/* update the netlist */
+		AddNetlistLibToUndoList(PCB->NetlistLib);
+
+		/* iterate over each net */
+		for (i = 0; i < PCB->NetlistLib[WTF].MenuN; i++) {
+
+			/* iterate over each pin on the net */
+			for (j = 0; j < PCB->NetlistLib[WTF].Menu[i].EntryN; j++) {
+
+				/* figure out the pin number part from strings like U3-21 */
+				tmps = pcb_strdup(PCB->NetlistLib[WTF].Menu[i].Entry[j].ListEntry);
+				for (k = 0; tmps[k] && tmps[k] != '-'; k++);
+				tmps[k] = '\0';
+				pin = tmps + k + 1;
+
+				/* iterate over the list of changed reference designators */
+				for (k = 0; k < c_cnt; k++) {
+					/*
+					 * if the pin needs to change, change it and quit
+					 * searching in the list.
+					 */
+					if (strcmp(tmps, was[k]) == 0) {
+						char *buffer;
+						free((char*)PCB->NetlistLib[WTF].Menu[i].Entry[j].ListEntry);
+						buffer = (char *) malloc((strlen(is[k]) + 1 + strlen(pin) + 1) * sizeof(char));
+						sprintf(buffer, "%s-%s", is[k], pin);
+						PCB->NetlistLib[WTF].Menu[i].Entry[j].ListEntry = buffer;
+						k = c_cnt;
+					}
+
+				}
+				free(tmps);
+			}
+		}
+		for (k = 0; k < c_cnt; k++) {
+			free(was[k]);
+			free(is[k]);
+		}
+
+		pcb_netlist_changed(0);
+		IncrementUndoSerialNumber();
+		SetChangedFlag(pcb_true);
+	}
+
+	free(locked_element_list);
+	free(element_list);
+	free(cnt_list);
+	return 0;
+}
+
+int action_renumber_block(int argc, const char **argv, Coord x, Coord y);
+int action_renumber_buffer(int argc, const char **argv, Coord x, Coord y);
+
+
+static const char *renumber_cookie = "renumber plugin";
+
+HID_Action renumber_action_list[] = {
+	{"Renumber", 0, ActionRenumber,
+	 renumber_help, renumber_syntax},
+	{"RenumberBlock", NULL, action_renumber_block,
+		NULL, NULL},
+	{"RenumberBuffer", NULL, action_renumber_buffer,
+		NULL, NULL}
+};
+
+static void hid_renumber_uninit(void)
+{
+	hid_remove_actions_by_cookie(renumber_cookie);
+}
+
+REGISTER_ACTIONS(renumber_action_list, renumber_cookie)
+
+#include "dolists.h"
+pcb_uninit_t hid_renumber_init(void)
+{
+	REGISTER_ACTIONS(renumber_action_list, renumber_cookie)
+	return hid_renumber_uninit;
+}
diff --git a/src_plugins/renumber/renumberblock.c b/src_plugins/renumber/renumberblock.c
new file mode 100644
index 0000000..fc76dde
--- /dev/null
+++ b/src_plugins/renumber/renumberblock.c
@@ -0,0 +1,121 @@
+/*!
+ * \file renumberblock.c
+ *
+ * \brief RenumberBlock plug-in for PCB.
+ *
+ * \author Copyright (C) 2006 DJ Delorie <dj at delorie.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Original source: http://www.delorie.com/pcb/renumberblock.c
+ *
+ * Usage: RenumberBlock(oldnum,newnum)
+ *
+ * All selected elements are renumbered by adding (newnum-oldnum) to
+ * the existing number.  I.e. RenumberBlock(100,200) will change R213
+ * to R313.
+ *
+ * Usage: RenumberBuffer(oldnum,newnum)
+ *
+ * Same, but the paste buffer is renumbered.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "error.h"
+#include "change.h"
+#include "conf_core.h"
+
+int action_renumber_block(int argc, const char **argv, Coord x, Coord y)
+{
+	char num_buf[15];
+	int old_base, new_base;
+
+	if (argc < 2) {
+		Message(PCB_MSG_ERROR, "Usage: RenumberBlock oldnum newnum");
+		return 1;
+	}
+
+	old_base = atoi(argv[0]);
+	new_base = atoi(argv[1]);
+
+	conf_set_editor(name_on_pcb, 1);
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		char *refdes_split, *cp;
+		char *old_ref, *new_ref;
+		int num;
+
+		if (!TEST_FLAG(PCB_FLAG_SELECTED, element))
+			continue;
+
+		old_ref = element->Name[1].TextString;
+		for (refdes_split = cp = old_ref; *cp; cp++)
+			if (!isdigit(*cp))
+				refdes_split = cp + 1;
+
+		num = atoi(refdes_split);
+		num += (new_base - old_base);
+		sprintf(num_buf, "%d", num);
+		new_ref = (char *) malloc(refdes_split - old_ref + strlen(num_buf) + 1);
+		memcpy(new_ref, old_ref, refdes_split - old_ref);
+		strcpy(new_ref + (refdes_split - old_ref), num_buf);
+
+		AddObjectToChangeNameUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element, NAMEONPCB_NAME(element));
+
+		ChangeObjectName(PCB_TYPE_ELEMENT, element, NULL, NULL, new_ref);
+	}
+	END_LOOP;
+	IncrementUndoSerialNumber();
+	return 0;
+}
+
+int action_renumber_buffer(int argc, const char **argv, Coord x, Coord y)
+{
+	char num_buf[15];
+	int old_base, new_base;
+
+	if (argc < 2) {
+		Message(PCB_MSG_ERROR, "Usage: RenumberBuffer oldnum newnum");
+		return 1;
+	}
+
+	old_base = atoi(argv[0]);
+	new_base = atoi(argv[1]);
+
+	conf_set_editor(name_on_pcb, 1);
+
+	ELEMENT_LOOP(PASTEBUFFER->Data);
+	{
+		char *refdes_split, *cp;
+		char *old_ref, *new_ref;
+		int num;
+
+		old_ref = element->Name[1].TextString;
+		for (refdes_split = cp = old_ref; *cp; cp++)
+			if (!isdigit(*cp))
+				refdes_split = cp + 1;
+
+		num = atoi(refdes_split);
+		num += (new_base - old_base);
+		sprintf(num_buf, "%d", num);
+		new_ref = (char *) malloc(refdes_split - old_ref + strlen(num_buf) + 1);
+		memcpy(new_ref, old_ref, refdes_split - old_ref);
+		strcpy(new_ref + (refdes_split - old_ref), num_buf);
+
+		ChangeObjectName(PCB_TYPE_ELEMENT, element, NULL, NULL, new_ref);
+	}
+	END_LOOP;
+	return 0;
+}
diff --git a/src_plugins/report/Makefile b/src_plugins/report/Makefile
new file mode 100644
index 0000000..61be1b8
--- /dev/null
+++ b/src_plugins/report/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_report
+	
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/report/Plug.tmpasm b/src_plugins/report/Plug.tmpasm
new file mode 100644
index 0000000..91b5230
--- /dev/null
+++ b/src_plugins/report/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {report}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/report/report.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/report/report_conf.h}
+
+switch /local/pcb/report/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/report/README b/src_plugins/report/README
new file mode 100644
index 0000000..e56ff74
--- /dev/null
+++ b/src_plugins/report/README
@@ -0,0 +1,5 @@
+Report() and ReportObject() actions - print a report about design objects.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/report/report.c b/src_plugins/report/report.c
new file mode 100644
index 0000000..d20f143
--- /dev/null
+++ b/src_plugins/report/report.c
@@ -0,0 +1,915 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
+ *
+ *  This module, report.c, was written and is Copyright (C) 1997 harry eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <math.h>
+
+#include "report.h"
+#include "crosshair.h"
+#include "data.h"
+#include "drill.h"
+#include "error.h"
+#include "search.h"
+#include "misc.h"
+#include "mymem.h"
+#include "rats.h"
+#include "rtree.h"
+#include "strflags.h"
+#include "macro.h"
+#include "undo.h"
+#include "find.h"
+#include "draw.h"
+#include "pcb-printf.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "misc_util.h"
+#include "report_conf.h"
+#include "compat_misc.h"
+#include "layer.h"
+
+#include <genregex/regex_sei.h>
+
+conf_report_t conf_report;
+
+#define USER_UNITMASK (conf_core.editor.grid_unit->allow)
+
+static int ReportDrills(int argc, const char **argv, Coord x, Coord y)
+{
+	DrillInfoTypePtr AllDrills;
+	pcb_cardinal_t n;
+	char *stringlist, *thestring;
+	int total_drills = 0;
+
+	AllDrills = GetDrillInfo(PCB->Data);
+	RoundDrillInfo(AllDrills, 100);
+
+	for (n = 0; n < AllDrills->DrillN; n++) {
+		total_drills += AllDrills->Drill[n].PinCount;
+		total_drills += AllDrills->Drill[n].ViaCount;
+		total_drills += AllDrills->Drill[n].UnplatedCount;
+	}
+
+	stringlist = (char *) malloc(512L + AllDrills->DrillN * 64L);
+
+	/* Use tabs for formatting since can't count on a fixed font anymore.
+	   |  And even that probably isn't going to work in all cases.
+	 */
+	sprintf(stringlist,
+					"There are %d different drill sizes used in this layout, %d holes total\n\n"
+					"Drill Diam. (%s)\t# of Pins\t# of Vias\t# of Elements\t# Unplated\n",
+					AllDrills->DrillN, total_drills, conf_core.editor.grid_unit->suffix);
+	thestring = stringlist;
+	while (*thestring != '\0')
+		thestring++;
+	for (n = 0; n < AllDrills->DrillN; n++) {
+		pcb_sprintf(thestring,
+								"%10m*\t\t%d\t\t%d\t\t%d\t\t%d\n",
+								conf_core.editor.grid_unit->suffix,
+								AllDrills->Drill[n].DrillSize,
+								AllDrills->Drill[n].PinCount, AllDrills->Drill[n].ViaCount,
+								AllDrills->Drill[n].ElementN, AllDrills->Drill[n].UnplatedCount);
+		while (*thestring != '\0')
+			thestring++;
+	}
+	FreeDrillInfo(AllDrills);
+	/* create dialog box */
+	gui->report_dialog("Drill Report", stringlist);
+
+	free(stringlist);
+	return 0;
+}
+
+
+static const char reportdialog_syntax[] = "ReportObject()";
+
+static const char reportdialog_help[] = "Report on the object under the crosshair";
+
+/* %start-doc actions ReportDialog
+
+This is a shortcut for @code{Report(Object)}.
+
+%end-doc */
+
+static int ReportDialog(int argc, const char **argv, Coord x, Coord y)
+{
+	void *ptr1, *ptr2, *ptr3;
+	int type;
+	char *report = NULL;
+
+	type = SearchScreen(x, y, REPORT_TYPES, &ptr1, &ptr2, &ptr3);
+	if (type == PCB_TYPE_NONE)
+		type = SearchScreen(x, y, REPORT_TYPES | PCB_TYPE_LOCKED, &ptr1, &ptr2, &ptr3);
+
+	switch (type) {
+	case PCB_TYPE_VIA:
+		{
+			PinTypePtr via;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				__r_dump_tree(PCB->Data->via_tree->root, 0);
+				return 0;
+			}
+#endif
+			via = (PinTypePtr) ptr2;
+			if (TEST_FLAG(PCB_FLAG_HOLE, via))
+				report = pcb_strdup_printf("%m+VIA ID# %ld; Flags:%s\n"
+										"(X,Y) = %$mD.\n"
+										"It is a pure hole of diameter %$mS.\n"
+										"Name = \"%s\"."
+										"%s", USER_UNITMASK, via->ID, flags_to_string(via->Flags, PCB_TYPE_VIA),
+										via->X, via->Y, via->DrillingHole, EMPTY(via->Name), TEST_FLAG(PCB_FLAG_LOCK, via) ? "It is LOCKED.\n" : "");
+			else
+				report = pcb_strdup_printf("%m+VIA ID# %ld;  Flags:%s\n"
+										"(X,Y) = %$mD.\n"
+										"Copper width = %$mS. Drill width = %$mS.\n"
+										"Clearance width in polygons = %$mS.\n"
+										"Annulus = %$mS.\n"
+										"Solder mask hole = %$mS (gap = %$mS).\n"
+										"Name = \"%s\"."
+										"%s", USER_UNITMASK, via->ID, flags_to_string(via->Flags, PCB_TYPE_VIA),
+										via->X, via->Y,
+										via->Thickness,
+										via->DrillingHole,
+										via->Clearance / 2,
+										(via->Thickness - via->DrillingHole) / 2,
+										via->Mask,
+										(via->Mask - via->Thickness) / 2, EMPTY(via->Name), TEST_FLAG(PCB_FLAG_LOCK, via) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_PIN:
+		{
+			PinTypePtr Pin;
+			ElementTypePtr element;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				__r_dump_tree(PCB->Data->pin_tree->root, 0);
+				return 0;
+			}
+#endif
+			Pin = (PinTypePtr) ptr2;
+			element = (ElementTypePtr) ptr1;
+
+			PIN_LOOP(element);
+			{
+				if (pin == Pin)
+					break;
+			}
+			END_LOOP;
+			if (TEST_FLAG(PCB_FLAG_HOLE, Pin))
+				report = pcb_strdup_printf("%m+PIN ID# %ld; Flags:%s\n"
+										"(X,Y) = %$mD.\n"
+										"It is a mounting hole. Drill width = %$mS.\n"
+										"It is owned by element %$mS.\n"
+										"%s", USER_UNITMASK, Pin->ID, flags_to_string(Pin->Flags, PCB_TYPE_PIN),
+										Pin->X, Pin->Y, Pin->DrillingHole,
+										EMPTY(element->Name[1].TextString), TEST_FLAG(PCB_FLAG_LOCK, Pin) ? "It is LOCKED.\n" : "");
+			else
+				report = pcb_strdup_printf(
+										"%m+PIN ID# %ld;  Flags:%s\n" "(X,Y) = %$mD.\n"
+										"Copper width = %$mS. Drill width = %$mS.\n"
+										"Clearance width to Polygon = %$mS.\n"
+										"Annulus = %$mS.\n"
+										"Solder mask hole = %$mS (gap = %$mS).\n"
+										"Name = \"%s\".\n"
+										"It is owned by element %s\n as pin number %s.\n"
+										"%s", USER_UNITMASK,
+										Pin->ID, flags_to_string(Pin->Flags, PCB_TYPE_PIN),
+										Pin->X, Pin->Y, Pin->Thickness,
+										Pin->DrillingHole,
+										Pin->Clearance / 2,
+										(Pin->Thickness - Pin->DrillingHole) / 2,
+										Pin->Mask,
+										(Pin->Mask - Pin->Thickness) / 2,
+										EMPTY(Pin->Name),
+										EMPTY(element->Name[1].TextString), EMPTY(Pin->Number), TEST_FLAG(PCB_FLAG_LOCK, Pin) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_LINE:
+		{
+			LineTypePtr line;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				LayerTypePtr layer = (LayerTypePtr) ptr1;
+				__r_dump_tree(layer->line_tree->root, 0);
+				return 0;
+			}
+#endif
+			line = (LineTypePtr) ptr2;
+			report = pcb_strdup_printf("%m+LINE ID# %ld;  Flags:%s\n"
+									"FirstPoint(X,Y)  = %$mD, ID = %ld.\n"
+									"SecondPoint(X,Y) = %$mD, ID = %ld.\n"
+									"Width = %$mS.\nClearance width in polygons = %$mS.\n"
+									"It is on layer %d\n"
+									"and has name \"%s\".\n"
+									"%s", USER_UNITMASK,
+									line->ID, flags_to_string(line->Flags, PCB_TYPE_LINE),
+									line->Point1.X, line->Point1.Y, line->Point1.ID,
+									line->Point2.X, line->Point2.Y, line->Point2.ID,
+									line->Thickness, line->Clearance / 2,
+									GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1),
+									UNKNOWN(line->Number), TEST_FLAG(PCB_FLAG_LOCK, line) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_RATLINE:
+		{
+			RatTypePtr line;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				__r_dump_tree(PCB->Data->rat_tree->root, 0);
+				return 0;
+			}
+#endif
+			line = (RatTypePtr) ptr2;
+			report = pcb_strdup_printf("%m+RAT-LINE ID# %ld;  Flags:%s\n"
+									"FirstPoint(X,Y)  = %$mD; ID = %ld; "
+									"connects to layer group %d.\n"
+									"SecondPoint(X,Y) = %$mD; ID = %ld; "
+									"connects to layer group %d.\n",
+									USER_UNITMASK, line->ID, flags_to_string(line->Flags, PCB_TYPE_LINE),
+									line->Point1.X, line->Point1.Y,
+									line->Point1.ID, line->group1, line->Point2.X, line->Point2.Y, line->Point2.ID, line->group2);
+			break;
+		}
+	case PCB_TYPE_ARC:
+		{
+			ArcTypePtr Arc;
+			BoxTypePtr box;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				LayerTypePtr layer = (LayerTypePtr) ptr1;
+				__r_dump_tree(layer->arc_tree->root, 0);
+				return 0;
+			}
+#endif
+			Arc = (ArcTypePtr) ptr2;
+			box = GetArcEnds(Arc);
+
+			report = pcb_strdup_printf("%m+ARC ID# %ld;  Flags:%s\n"
+									"CenterPoint(X,Y) = %$mD.\n"
+									"Radius = %$mS, Thickness = %$mS.\n"
+									"Clearance width in polygons = %$mS.\n"
+									"StartAngle = %ma degrees, DeltaAngle = %ma degrees.\n"
+									"Bounding Box is %$mD, %$mD.\n"
+									"That makes the end points at %$mD and %$mD.\n"
+									"It is on layer %d.\n"
+									"%s", USER_UNITMASK, Arc->ID, flags_to_string(Arc->Flags, PCB_TYPE_ARC),
+									Arc->X, Arc->Y,
+									Arc->Width, Arc->Thickness,
+									Arc->Clearance / 2, Arc->StartAngle, Arc->Delta,
+									Arc->BoundingBox.X1, Arc->BoundingBox.Y1,
+									Arc->BoundingBox.X2, Arc->BoundingBox.Y2,
+									box->X1, box->Y1,
+									box->X2, box->Y2,
+									GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1), TEST_FLAG(PCB_FLAG_LOCK, Arc) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_POLYGON:
+		{
+			PolygonTypePtr Polygon;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				LayerTypePtr layer = (LayerTypePtr) ptr1;
+				__r_dump_tree(layer->polygon_tree->root, 0);
+				return 0;
+			}
+#endif
+			Polygon = (PolygonTypePtr) ptr2;
+
+			report = pcb_strdup_printf("%m+POLYGON ID# %ld;  Flags:%s\n"
+									"Its bounding box is %$mD %$mD.\n"
+									"It has %d points and could store %d more\n"
+									"  without using more memory.\n"
+									"It has %d holes and resides on layer %d.\n"
+									"%s", USER_UNITMASK, Polygon->ID,
+									flags_to_string(Polygon->Flags, PCB_TYPE_POLYGON),
+									Polygon->BoundingBox.X1, Polygon->BoundingBox.Y1,
+									Polygon->BoundingBox.X2, Polygon->BoundingBox.Y2,
+									Polygon->PointN, Polygon->PointMax - Polygon->PointN,
+									Polygon->HoleIndexN,
+									GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1), TEST_FLAG(PCB_FLAG_LOCK, Polygon) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_PAD:
+		{
+			Coord len;
+			PadTypePtr Pad;
+			ElementTypePtr element;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				__r_dump_tree(PCB->Data->pad_tree->root, 0);
+				return 0;
+			}
+#endif
+			Pad = (PadTypePtr) ptr2;
+			element = (ElementTypePtr) ptr1;
+
+			PAD_LOOP(element);
+			{
+				{
+					if (pad == Pad)
+						break;
+				}
+			}
+			END_LOOP;
+			len = Distance(Pad->Point1.X, Pad->Point1.Y, Pad->Point2.X, Pad->Point2.Y);
+			report = pcb_strdup_printf("%m+PAD ID# %ld;  Flags:%s\n"
+									"FirstPoint(X,Y)  = %$mD; ID = %ld.\n"
+									"SecondPoint(X,Y) = %$mD; ID = %ld.\n"
+									"Width = %$mS.  Length = %$mS.\n"
+									"Clearance width in polygons = %$mS.\n"
+									"Solder mask = %$mS x %$mS (gap = %$mS).\n"
+									"Name = \"%s\".\n"
+									"It is owned by SMD element %s\n"
+									"  as pin number %s and is on the %s\n"
+									"side of the board.\n"
+									"%s", USER_UNITMASK, Pad->ID,
+									flags_to_string(Pad->Flags, PCB_TYPE_PAD),
+									Pad->Point1.X, Pad->Point1.Y, Pad->Point1.ID,
+									Pad->Point2.X, Pad->Point2.Y, Pad->Point2.ID,
+									Pad->Thickness, len + Pad->Thickness,
+									Pad->Clearance / 2,
+									Pad->Mask, len + Pad->Mask,
+									(Pad->Mask - Pad->Thickness) / 2,
+									EMPTY(Pad->Name),
+									EMPTY(element->Name[1].TextString),
+									EMPTY(Pad->Number),
+									TEST_FLAG(PCB_FLAG_ONSOLDER,
+														Pad) ? "solder (bottom)" : "component", TEST_FLAG(PCB_FLAG_LOCK, Pad) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_ELEMENT:
+		{
+			ElementTypePtr element;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				__r_dump_tree(PCB->Data->element_tree->root, 0);
+				return 0;
+			}
+#endif
+			element = (ElementTypePtr) ptr2;
+			report = pcb_strdup_printf("%m+ELEMENT ID# %ld;  Flags:%s\n"
+									"BoundingBox %$mD %$mD.\n"
+									"Descriptive Name \"%s\".\n"
+									"Name on board \"%s\".\n"
+									"Part number name \"%s\".\n"
+									"It is %$mS tall and is located at (X,Y) = %$mD %s.\n"
+									"Mark located at point (X,Y) = %$mD.\n"
+									"It is on the %s side of the board.\n"
+									"%s", USER_UNITMASK,
+									element->ID, flags_to_string(element->Flags, PCB_TYPE_ELEMENT),
+									element->BoundingBox.X1, element->BoundingBox.Y1,
+									element->BoundingBox.X2, element->BoundingBox.Y2,
+									EMPTY(element->Name[0].TextString),
+									EMPTY(element->Name[1].TextString),
+									EMPTY(element->Name[2].TextString),
+									PCB_SCALE_TEXT(FONT_CAPHEIGHT, element->Name[1].Scale),
+									element->Name[1].X, element->Name[1].Y,
+									TEST_FLAG(PCB_FLAG_HIDENAME, element) ? ",\n  but it's hidden" : "",
+									element->MarkX, element->MarkY,
+									TEST_FLAG(PCB_FLAG_ONSOLDER, element) ? "solder (bottom)" : "component",
+									TEST_FLAG(PCB_FLAG_LOCK, element) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_TEXT:
+#ifndef NDEBUG
+		if (gui->shift_is_pressed()) {
+			LayerTypePtr layer = (LayerTypePtr) ptr1;
+			__r_dump_tree(layer->text_tree->root, 0);
+			return 0;
+		}
+#endif
+	case PCB_TYPE_ELEMENT_NAME:
+		{
+			char laynum[32];
+			TextTypePtr text;
+#ifndef NDEBUG
+			if (gui->shift_is_pressed()) {
+				__r_dump_tree(PCB->Data->name_tree[NAME_INDEX()]->root, 0);
+				return 0;
+			}
+#endif
+			text = (TextTypePtr) ptr2;
+
+			if (type == PCB_TYPE_TEXT)
+				sprintf(laynum, "It is on layer %d.", GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1));
+			report = pcb_strdup_printf("%m+TEXT ID# %ld;  Flags:%s\n"
+									"Located at (X,Y) = %$mD.\n"
+									"Characters are %$mS tall.\n"
+									"Value is \"%s\".\n"
+									"Direction is %d.\n"
+									"The bounding box is %$mD %$mD.\n"
+									"%s\n"
+									"%s", USER_UNITMASK, text->ID, flags_to_string(text->Flags, PCB_TYPE_TEXT),
+									text->X, text->Y, PCB_SCALE_TEXT(FONT_CAPHEIGHT, text->Scale),
+									text->TextString, text->Direction,
+									text->BoundingBox.X1, text->BoundingBox.Y1,
+									text->BoundingBox.X2, text->BoundingBox.Y2,
+									(type == PCB_TYPE_TEXT) ? laynum : "It is an element name.", TEST_FLAG(PCB_FLAG_LOCK, text) ? "It is LOCKED.\n" : "");
+			break;
+		}
+	case PCB_TYPE_LINE_POINT:
+	case PCB_TYPE_POLYGON_POINT:
+		{
+			PointTypePtr point = (PointTypePtr) ptr2;
+			report = pcb_strdup_printf("%m+POINT ID# %ld.\n"
+									"Located at (X,Y) = %$mD.\n"
+									"It belongs to a %s on layer %d.\n", USER_UNITMASK, point->ID,
+									point->X, point->Y,
+									(type == PCB_TYPE_LINE_POINT) ? "line" : "polygon", GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1));
+			break;
+		}
+	case PCB_TYPE_NONE:
+		report = NULL;
+		break;
+
+	default:
+		report = pcb_strdup_printf("Unknown\n");
+		break;
+	}
+
+	if ((report == NULL) || (report == '\0')) {
+		Message(PCB_MSG_DEFAULT, _("Nothing found to report on\n"));
+		return 1;
+	}
+
+	if (report != NULL) {
+		/* create dialog box */
+		gui->report_dialog("Report", report);
+		free(report);
+	}
+	return 0;
+}
+
+static int ReportFoundPins(int argc, const char **argv, Coord x, Coord y)
+{
+	gds_t list;
+	int col = 0;
+
+	gds_init(&list);
+	gds_append_str(&list, "The following pins/pads are FOUND:\n");
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_FOUND, pin))
+				pcb_append_printf(&list, "%s-%s,%c", NAMEONPCB_NAME(element), pin->Number, ((col++ % (conf_report.plugins.report.columns + 1)) == conf_report.plugins.report.columns) ? '\n' : ' ');
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_FOUND, pad))
+				pcb_append_printf(&list, "%s-%s,%c", NAMEONPCB_NAME(element), pad->Number, ((col++ % (conf_report.plugins.report.columns + 1)) == conf_report.plugins.report.columns) ? '\n' : ' ');
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	gui->report_dialog("Report", list.array);
+	gds_uninit(&list);
+	return 0;
+}
+
+/* Assumes that we start with a blank connection state,
+ * e.g. ResetConnections() has been run.
+ * Does not add its own changes to the undo system
+ */
+static double XYtoNetLength(Coord x, Coord y, int *found)
+{
+	double length;
+
+	length = 0;
+	*found = 0;
+
+	/* NB: The third argument here, 'false' ensures LookupConnection
+	 *     does not add its changes to the undo system.
+	 */
+	LookupConnection(x, y, pcb_false, PCB->Grid, PCB_FLAG_FOUND);
+
+	ALLLINE_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, line)) {
+			double l;
+			int dx, dy;
+			dx = line->Point1.X - line->Point2.X;
+			dy = line->Point1.Y - line->Point2.Y;
+			l = sqrt((double) dx * dx + (double) dy * dy);
+			length += l;
+			*found = 1;
+		}
+	}
+	ENDALL_LOOP;
+
+	ALLARC_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_FOUND, arc)) {
+			double l;
+			/* FIXME: we assume width==height here */
+			l = M_PI * 2 * arc->Width * fabs(arc->Delta) / 360.0;
+			length += l;
+			*found = 1;
+		}
+	}
+	ENDALL_LOOP;
+
+	return length;
+}
+
+static int ReportAllNetLengths(int argc, const char **argv, Coord x, Coord y)
+{
+	int ni;
+	int found;
+
+	/* Reset all connection flags and save an undo-state to get back
+	 * to the state the board was in when we started this function.
+	 *
+	 * After this, we don't add any changes to the undo system, but
+	 * ensure we get back to a point where we can Undo() our changes
+	 * by resetting the connections with ResetConnections() before
+	 * calling Undo() at the end of the procedure.
+	 */
+	ResetConnections(pcb_true);
+	IncrementUndoSerialNumber();
+
+	for (ni = 0; ni < PCB->NetlistLib[NETLIST_EDITED].MenuN; ni++) {
+		const char *netname = PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Name + 2;
+		const char *list_entry = PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Entry[0].ListEntry;
+		char *ename;
+		char *pname;
+		pcb_bool got_one = 0;
+
+		ename = pcb_strdup(list_entry);
+		pname = strchr(ename, '-');
+		if (!pname) {
+			free(ename);
+			continue;
+		}
+		*pname++ = 0;
+
+		ELEMENT_LOOP(PCB->Data);
+		{
+			char *es = element->Name[NAMEONPCB_INDEX].TextString;
+			if (es && strcmp(es, ename) == 0) {
+				PIN_LOOP(element);
+				{
+					if (strcmp(pin->Number, pname) == 0) {
+						x = pin->X;
+						y = pin->Y;
+						got_one = 1;
+						break;
+					}
+				}
+				END_LOOP;
+				PAD_LOOP(element);
+				{
+					if (strcmp(pad->Number, pname) == 0) {
+						x = (pad->Point1.X + pad->Point2.X) / 2;
+						y = (pad->Point1.Y + pad->Point2.Y) / 2;
+						got_one = 1;
+						break;
+					}
+				}
+				END_LOOP;
+			}
+		}
+		END_LOOP;
+
+		if (got_one) {
+			char buf[50];
+			const char *units_name = argv[0];
+			Coord length;
+
+			if (argc < 1)
+				units_name = conf_core.editor.grid_unit->suffix;
+
+			length = XYtoNetLength(x, y, &found);
+
+			/* Reset connectors for the next lookup */
+			ResetConnections(pcb_false);
+
+			pcb_snprintf(buf, sizeof(buf), "%$m*", units_name, length);
+			gui->log("Net %s length %s\n", netname, buf);
+		}
+	}
+
+	ResetConnections(pcb_false);
+	Undo(pcb_true);
+	return 0;
+}
+
+static int ReportNetLength(int argc, const char **argv, Coord x, Coord y)
+{
+	Coord length = 0;
+	char *netname = 0;
+	int found = 0;
+
+	gui->get_coords("Click on a connection", &x, &y);
+
+	/* Reset all connection flags and save an undo-state to get back
+	 * to the state the board was in when we started this function.
+	 *
+	 * After this, we don't add any changes to the undo system, but
+	 * ensure we get back to a point where we can Undo() our changes
+	 * by resetting the connections with ResetConnections() before
+	 * calling Undo() at the end of the procedure.
+	 */
+	ResetConnections(pcb_true);
+	IncrementUndoSerialNumber();
+
+	length = XYtoNetLength(x, y, &found);
+
+	if (!found) {
+		ResetConnections(pcb_false);
+		Undo(pcb_true);
+		gui->log("No net under cursor.\n");
+		return 1;
+	}
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_FOUND, pin)) {
+				int ni, nei;
+				char *ename = element->Name[NAMEONPCB_INDEX].TextString;
+				char *pname = pin->Number;
+				char *n;
+
+				if (ename && pname) {
+					n = Concat(ename, "-", pname, NULL);
+					for (ni = 0; ni < PCB->NetlistLib[NETLIST_EDITED].MenuN; ni++)
+						for (nei = 0; nei < PCB->NetlistLib[NETLIST_EDITED].Menu[ni].EntryN; nei++) {
+							if (strcmp(PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Entry[nei].ListEntry, n) == 0) {
+								netname = PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Name + 2;
+								goto got_net_name;	/* four for loops deep */
+							}
+						}
+				}
+			}
+		}
+		END_LOOP;
+		PAD_LOOP(element);
+		{
+			if (TEST_FLAG(PCB_FLAG_FOUND, pad)) {
+				int ni, nei;
+				char *ename = element->Name[NAMEONPCB_INDEX].TextString;
+				char *pname = pad->Number;
+				char *n;
+
+				if (ename && pname) {
+					n = Concat(ename, "-", pname, NULL);
+					for (ni = 0; ni < PCB->NetlistLib[NETLIST_EDITED].MenuN; ni++)
+						for (nei = 0; nei < PCB->NetlistLib[NETLIST_EDITED].Menu[ni].EntryN; nei++) {
+							if (strcmp(PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Entry[nei].ListEntry, n) == 0) {
+								netname = PCB->NetlistLib[NETLIST_EDITED].Menu[ni].Name + 2;
+								goto got_net_name;	/* four for loops deep */
+							}
+						}
+				}
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+got_net_name:
+	ResetConnections(pcb_false);
+	Undo(pcb_true);
+
+	{
+		char buf[50];
+		pcb_snprintf(buf, sizeof(buf), "%$m*", conf_core.editor.grid_unit->suffix, length);
+		if (netname)
+			gui->log("Net \"%s\" length: %s\n", netname, buf);
+		else
+			gui->log("Net length: %s\n", buf);
+	}
+
+	return 0;
+}
+
+static int ReportNetLengthByName(const char *tofind, int x, int y)
+{
+	char *netname = 0;
+	Coord length = 0;
+	int found = 0;
+	int i;
+	LibraryMenuType *net;
+	ConnectionType conn;
+	int net_found = 0;
+	int use_re = 0;
+	re_sei_t *regex;
+
+	if (!PCB)
+		return 1;
+
+	if (!tofind)
+		return 1;
+
+	use_re = 1;
+	for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
+		net = PCB->NetlistLib[NETLIST_EDITED].Menu + i;
+		if (strcasecmp(tofind, net->Name + 2) == 0)
+			use_re = 0;
+	}
+	if (use_re) {
+		regex = re_sei_comp(tofind);
+		if (re_sei_errno(regex) != 0) {
+			Message(PCB_MSG_DEFAULT, _("regexp error: %s\n"), re_error_str(re_sei_errno(regex)));
+			re_sei_free(regex);
+			return (1);
+		}
+	}
+
+	for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
+		net = PCB->NetlistLib[NETLIST_EDITED].Menu + i;
+
+		if (use_re) {
+			if (re_sei_exec(regex, net->Name + 2) == 0)
+				continue;
+		}
+		else {
+			if (strcasecmp(net->Name + 2, tofind))
+				continue;
+		}
+
+		if (SeekPad(net->Entry, &conn, pcb_false)) {
+			switch (conn.type) {
+			case PCB_TYPE_PIN:
+				x = ((PinType *) (conn.ptr2))->X;
+				y = ((PinType *) (conn.ptr2))->Y;
+				net_found = 1;
+				break;
+			case PCB_TYPE_PAD:
+				x = ((PadType *) (conn.ptr2))->Point1.X;
+				y = ((PadType *) (conn.ptr2))->Point1.Y;
+				net_found = 1;
+				break;
+			}
+			if (net_found)
+				break;
+		}
+	}
+
+	if (!net_found) {
+		gui->log("No net named %s\n", tofind);
+		return 1;
+	}
+
+	if (use_re)
+		re_sei_free(regex);
+
+	/* Reset all connection flags and save an undo-state to get back
+	 * to the state the board was in when we started.
+	 *
+	 * After this, we don't add any changes to the undo system, but
+	 * ensure we get back to a point where we can Undo() our changes
+	 * by resetting the connections with ResetConnections() before
+	 * calling Undo() when we are finished.
+	 */
+	ResetConnections(pcb_true);
+	IncrementUndoSerialNumber();
+
+	length = XYtoNetLength(x, y, &found);
+	netname = net->Name + 2;
+
+	ResetConnections(pcb_false);
+	Undo(pcb_true);
+
+	if (!found) {
+		if (net_found)
+			gui->log("Net found, but no lines or arcs were flagged.\n");
+		else
+			gui->log("Net not found.\n");
+
+		return 1;
+	}
+
+	{
+		char buf[50];
+		pcb_snprintf(buf, sizeof(buf), "%$m*", conf_core.editor.grid_unit->suffix, length);
+		if (netname)
+			gui->log("Net \"%s\" length: %s\n", netname, buf);
+		else
+			gui->log("Net length: %s\n", buf);
+	}
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * reports on an object
+ * syntax:
+ */
+
+static const char report_syntax[] = "Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])";
+
+static const char report_help[] = "Produce various report.";
+
+/* %start-doc actions Report
+
+ at table @code
+
+ at item Object
+The object under the crosshair will be reported, describing various
+aspects of the object.
+
+ at item DrillReport
+A report summarizing the number of drill sizes used, and how many of
+each, will be produced.
+
+ at item FoundPins
+A report listing all pins and pads which are marked as ``found'' will
+be produced.
+
+ at item NetLength
+The name and length of the net under the crosshair will be reported to
+the message log.
+
+ at item AllNetLengths
+The name and length of the net under the crosshair will be reported to
+the message log.  An optional parameter specifies mm, mil, pcb, or in
+units
+
+ at end table
+
+%end-doc */
+
+static int Report(int argc, const char **argv, Coord x, Coord y)
+{
+	if ((argc < 1) || (argc > 2))
+		AUSAGE(report);
+	else if (strcasecmp(argv[0], "Object") == 0) {
+		gui->get_coords("Click on an object", &x, &y);
+		return ReportDialog(argc - 1, argv + 1, x, y);
+	}
+	else if (strcasecmp(argv[0], "DrillReport") == 0)
+		return ReportDrills(argc - 1, argv + 1, x, y);
+	else if (strcasecmp(argv[0], "FoundPins") == 0)
+		return ReportFoundPins(argc - 1, argv + 1, x, y);
+	else if ((strcasecmp(argv[0], "NetLength") == 0) && (argc == 1))
+		return ReportNetLength(argc - 1, argv + 1, x, y);
+	else if (strcasecmp(argv[0], "AllNetLengths") == 0)
+		return ReportAllNetLengths(argc - 1, argv + 1, x, y);
+	else if ((strcasecmp(argv[0], "NetLength") == 0) && (argc == 2))
+		return ReportNetLengthByName(argv[1], x, y);
+	else if (argc == 2)
+		AUSAGE(report);
+	else
+		AFAIL(report);
+	return 1;
+}
+
+HID_Action report_action_list[] = {
+	{"ReportObject", "Click on an object", ReportDialog,
+	 reportdialog_help, reportdialog_syntax}
+	,
+	{"Report", 0, Report,
+	 report_help, report_syntax}
+};
+
+static const char *report_cookie = "report plugin";
+
+REGISTER_ACTIONS(report_action_list, report_cookie)
+
+static void hid_report_uninit(void)
+{
+	hid_remove_actions_by_cookie(report_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_report_init(void)
+{
+	REGISTER_ACTIONS(report_action_list, report_cookie)
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_report, field,isarray,type_name,cpath,cname,desc,flags);
+#include "report_conf_fields.h"
+	return hid_report_uninit;
+}
diff --git a/src_plugins/report/report.h b/src_plugins/report/report.h
new file mode 100644
index 0000000..10e2deb
--- /dev/null
+++ b/src_plugins/report/report.h
@@ -0,0 +1,37 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+#ifndef PCB_REPORT_H
+#define PCB_REPORT_H
+
+#include "global.h"
+
+#define REPORT_TYPES \
+	(PCB_TYPE_VIA | PCB_TYPE_LINE | PCB_TYPE_TEXT | PCB_TYPE_POLYGON | PCB_TYPE_ELEMENT | \
+	 PCB_TYPE_RATLINE | PCB_TYPE_PIN | PCB_TYPE_PAD | PCB_TYPE_ELEMENT_NAME | PCB_TYPE_ARC \
+	 | PCB_TYPE_POLYGON_POINT | PCB_TYPE_LINE_POINT)
+
+#endif
diff --git a/src_plugins/report/report_conf.h b/src_plugins/report/report_conf.h
new file mode 100644
index 0000000..2d7e125
--- /dev/null
+++ b/src_plugins/report/report_conf.h
@@ -0,0 +1,16 @@
+#ifndef PCB_MINCUT_CONF_H
+#define PCB_MINCUT_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct report {
+			CFT_INTEGER columns;         /* @usage number of columns for found pin report  */
+		} report;
+	} plugins;
+} conf_report_t;
+
+extern conf_report_t conf_report;
+
+#endif
diff --git a/src_plugins/shand_cmd/Makefile b/src_plugins/shand_cmd/Makefile
new file mode 100644
index 0000000..35dccf5
--- /dev/null
+++ b/src_plugins/shand_cmd/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_shand_cmd
+	
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/shand_cmd/Plug.tmpasm b/src_plugins/shand_cmd/Plug.tmpasm
new file mode 100644
index 0000000..29fe1f7
--- /dev/null
+++ b/src_plugins/shand_cmd/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {shand_cmd}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/shand_cmd/command.o @]
+
+switch /local/pcb/shand_cmd/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/shand_cmd/README b/src_plugins/shand_cmd/README
new file mode 100644
index 0000000..740fa2a
--- /dev/null
+++ b/src_plugins/shand_cmd/README
@@ -0,0 +1,5 @@
+vi-like command shorthands (1..3 character long commands)
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/shand_cmd/command.c b/src_plugins/shand_cmd/command.c
new file mode 100644
index 0000000..8583c9e
--- /dev/null
+++ b/src_plugins/shand_cmd/command.c
@@ -0,0 +1,414 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996, 2005 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* executes commands from user
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "global.h"
+#include "action_helper.h"
+#include "buffer.h"
+#include "command.h"
+#include "data.h"
+#include "error.h"
+#include "plug_io.h"
+#include "mymem.h"
+#include "misc.h"
+#include "rats.h"
+#include "set.h"
+#include "plugins.h"
+#include "hid_actions.h"
+#include "compat_misc.h"
+
+/* ---------------------------------------------------------------------- */
+
+/*  %start-doc actions 00macros
+
+ at macro colonaction
+
+This is one of the command box helper actions.  While it is a regular
+action and can be used like any other action, its name and syntax are
+optimized for use with the command box (@code{:}) and thus the syntax
+is documented for that purpose.
+
+ at end macro
+
+%end-doc */
+
+/* ---------------------------------------------------------------------- */
+
+static const char h_syntax[] = "h";
+
+static const char h_help[] = "Print a help message for commands.";
+
+/* %start-doc actions h
+
+ at colonaction
+
+%end-doc */
+
+static int CommandHelp(int argc, const char **argv, Coord x, Coord y)
+{
+	Message(PCB_MSG_DEFAULT, "following commands are supported:\n"
+					"  Command()   execute an action command (too numerous to list)\n"
+					"              see the manual for the list of action commands\n"
+					"  h           display this help message\n"
+					"  l  [file]   load layout\n"
+					"  le [file]   load element to buffer\n"
+					"  m  [file]   load layout to buffer (merge)\n"
+					"  q           quits the application\n"
+					"  q!          quits without save warning\n"
+					"  rn [file]   read in a net-list file\n"
+					"  s  [file]   save layout\n" "  w  [file]   save layout\n" "  wq [file]   save layout and quit\n");
+	return (0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const char l_syntax[] = "l [name] [format]";
+
+static const char l_help[] = "Loads layout data.";
+
+/* %start-doc actions l
+
+Loads a new datafile (layout) and, if confirmed, overwrites any
+existing unsaved data.  The filename and the searchpath
+(@emph{filePath}) are passed to the command defined by
+ at emph{fileCommand}.  If no filename is specified a file select box
+will popup.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandLoadLayout(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *filename, *format = NULL;
+
+	switch (argc) {
+	case 2:
+		format = argv[1];
+	case 1: /* filename is passed in commandline */
+		filename = argv[0];
+		break;
+
+	default: /* usage */
+		Message(PCB_MSG_DEFAULT, "Usage: l [name]\n  loads layout data\n");
+		return (1);
+	}
+
+	if (!PCB->Changed || gui->confirm_dialog("OK to override layout data?", 0))
+		LoadPCB(filename, format, pcb_true, 0);
+	return (0);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char le_syntax[] = "le [name]";
+
+static const char le_help[] = "Loads an element into the current buffer.";
+
+/* %start-doc actions le
+
+The filename and the searchpath (@emph{elementSearchPaths}) are passed to the
+element loader.  If no filename is specified a file select box will popup.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandLoadElementToBuffer(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *filename;
+
+	switch (argc) {
+	case 1:											/* filename is passed in commandline */
+		filename = argv[0];
+		if (filename && LoadElementToBuffer(PASTEBUFFER, filename))
+			SetMode(PCB_MODE_PASTE_BUFFER);
+		break;
+
+	default:											/* usage */
+		Message(PCB_MSG_DEFAULT, pcb_false, "Usage: le [name]\n  loads element data to buffer\n");
+		return (1);
+	}
+	return (0);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char m_syntax[] = "m [name]";
+
+static const char m_help[] = "Loads a layout into the current buffer.";
+
+/* %start-doc actions m
+
+The filename and the searchpath (@emph{filePath}) are passed to the
+command defined by @emph{fileCommand}.
+If no filename is specified a file select box will popup.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandLoadLayoutToBuffer(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *filename, *format = NULL;
+
+	switch (argc) {
+	case 2:
+		format = argv[1];
+	case 1:  /* filename is passed in commandline */
+		filename = argv[0];
+		if (filename && LoadLayoutToBuffer(PASTEBUFFER, filename, format))
+			SetMode(PCB_MODE_PASTE_BUFFER);
+		break;
+
+	default:  /* usage */
+		Message(PCB_MSG_DEFAULT, "Usage: m [name]\n  loads layout data to buffer\n");
+		return (1);
+	}
+	return (0);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char q_syntax[] = "q";
+
+static const char q_help[] = "Quits the application after confirming.";
+
+/* %start-doc actions q
+
+If you have unsaved changes, you will be prompted to confirm (or
+save) before quitting.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandQuit(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!PCB->Changed || gui->close_confirm_dialog() == HID_CLOSE_CONFIRM_OK)
+		QuitApplication();
+	return 0;
+}
+
+static const char qreally_syntax[] = "q!";
+
+static const char qreally_help[] = "Quits the application without confirming.";
+
+/* %start-doc actions q!
+
+Note that this command neither saves your data nor prompts for
+confirmation.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandReallyQuit(int argc, const char **argv, Coord x, Coord y)
+{
+	QuitApplication();
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const char rn_syntax[] = "rn [name]";
+
+static const char rn_help[] = "Reads netlist.";
+
+/* %start-doc actions rn
+
+If no filename is given a file select box will pop up.  The file is
+read via the command defined by the @emph{RatCommand} resource. The
+command must send its output to @emph{stdout}.
+
+Netlists are used for generating rat's nests (see @ref{Rats Nest}) and
+for verifying the board layout (which is also accomplished by the
+ at emph{Ratsnest} command).
+
+ at colonaction
+
+%end-doc */
+
+static int CommandLoadNetlist(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *filename;
+
+	switch (argc) {
+	case 1:											/* filename is passed in commandline */
+		filename = argv[0];
+		break;
+
+	default:											/* usage */
+		Message(PCB_MSG_DEFAULT, "Usage: rn [name]\n  reads in a netlist file\n");
+		return (1);
+	}
+	if (PCB->Netlistname)
+		free(PCB->Netlistname);
+	PCB->Netlistname = StripWhiteSpaceAndDup(filename);
+
+	return (0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const char s_syntax[] = "s [name]";
+
+static const char s_help[] = "Saves layout data.";
+
+/* %start-doc actions s
+
+Data and the filename are passed to the command defined by the
+resource @emph{saveCommand}. It must read the layout data from
+ at emph{stdin}.  If no filename is entered, either the last one is used
+again or, if it is not available, a file select box will pop up.
+
+ at colonaction
+
+%end-doc */
+
+static const char w_syntax[] = "w [name]";
+
+static const char w_help[] = "Saves layout data.";
+
+/* %start-doc actions w
+
+This commands has been added for the convenience of @code{vi} users
+and has the same functionality as @code{s}.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandSaveLayout(int argc, const char **argv, Coord x, Coord y)
+{
+	switch (argc) {
+	case 0:
+		if (PCB->Filename) {
+			if (SavePCB(PCB->Filename, NULL) == 0)
+				SetChangedFlag(pcb_false);
+		}
+		else
+			Message(PCB_MSG_DEFAULT, "No filename to save to yet\n");
+		break;
+
+	case 1:
+		if (SavePCB(argv[0], NULL) == 0) {
+			SetChangedFlag(pcb_false);
+			free(PCB->Filename);
+			PCB->Filename = pcb_strdup(argv[0]);
+			if (gui->notify_filename_changed != NULL)
+				gui->notify_filename_changed();
+		}
+		break;
+
+	default:
+		Message(PCB_MSG_DEFAULT, "Usage: s [name] | w [name]\n  saves layout data\n");
+		return (1);
+	}
+	return (0);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static const char wq_syntax[] = "wq";
+
+static const char wq_help[] = "Saves the layout data and quits.";
+
+/* %start-doc actions wq
+
+This command has been added for the convenience of @code{vi} users and
+has the same functionality as @code{s} combined with @code{q}.
+
+ at colonaction
+
+%end-doc */
+
+static int CommandSaveLayoutAndQuit(int argc, const char **argv, Coord x, Coord y)
+{
+	if (!CommandSaveLayout(argc, argv, x, y))
+		return CommandQuit(0, 0, 0, 0);
+	return (1);
+}
+
+/* --------------------------------------------------------------------------- */
+
+HID_Action shand_cmd_action_list[] = {
+	{"h", 0, CommandHelp,
+	 h_help, h_syntax}
+	,
+	{"l", 0, CommandLoadLayout,
+	 l_help, l_syntax}
+	,
+	{"le", 0, CommandLoadElementToBuffer,
+	 le_help, le_syntax}
+	,
+	{"m", 0, CommandLoadLayoutToBuffer,
+	 m_help, m_syntax}
+	,
+	{"q", 0, CommandQuit,
+	 q_help, q_syntax}
+	,
+	{"q!", 0, CommandReallyQuit,
+	 qreally_help, qreally_syntax}
+	,
+	{"rn", 0, CommandLoadNetlist,
+	 rn_help, rn_syntax}
+	,
+	{"s", 0, CommandSaveLayout,
+	 s_help, s_syntax}
+	,
+	{"w", 0, CommandSaveLayout,
+	 w_help, w_syntax}
+	,
+	{"wq", 0, CommandSaveLayoutAndQuit,
+	 wq_help, wq_syntax}
+	,
+};
+
+static const char *shand_cmd_cookie = "shand_cmd plugin";
+
+REGISTER_ACTIONS(shand_cmd_action_list, shand_cmd_cookie)
+
+static void hid_shand_cmd_uninit(void)
+{
+	hid_remove_actions_by_cookie(shand_cmd_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_shand_cmd_init(void)
+{
+	REGISTER_ACTIONS(shand_cmd_action_list, shand_cmd_cookie)
+	return hid_shand_cmd_uninit;
+}
diff --git a/src_plugins/shand_cmd/command.h b/src_plugins/shand_cmd/command.h
new file mode 100644
index 0000000..d28760c
--- /dev/null
+++ b/src_plugins/shand_cmd/command.h
@@ -0,0 +1,37 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
+ *  Thomas.Nau at rz.uni-ulm.de
+ *
+ */
+
+/* prototypes for command execution */
+
+#ifndef	PCB_COMMAND_H
+#define	PCB_COMMAND_H
+
+#include "global.h"
+
+void ExecuteUserCommand(char *);
+void CallActionProc(char *action, char **arg, int argc);
+
+#endif
diff --git a/src_plugins/smartdisperse/Makefile b/src_plugins/smartdisperse/Makefile
new file mode 100644
index 0000000..869ea87
--- /dev/null
+++ b/src_plugins/smartdisperse/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_smartdisperse
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/smartdisperse/Plug.tmpasm b/src_plugins/smartdisperse/Plug.tmpasm
new file mode 100644
index 0000000..1199af8
--- /dev/null
+++ b/src_plugins/smartdisperse/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {smartdisperse}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/smartdisperse/smartdisperse.o @]
+
+switch /local/pcb/smartdisperse/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/smartdisperse/README b/src_plugins/smartdisperse/README
new file mode 100644
index 0000000..d21df19
--- /dev/null
+++ b/src_plugins/smartdisperse/README
@@ -0,0 +1,10 @@
+Improve the initial dispersion of elements by choosing an order based
+on the netlist, rather than the arbitrary element order.  This isn't
+the same as a global autoplace, it's more of a linear autoplace.  It
+might make some useful local groupings.  For example, you should not
+have to chase all over the board to find the resistor that goes with
+a given LED.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/smartdisperse/smartdisperse.c b/src_plugins/smartdisperse/smartdisperse.c
new file mode 100644
index 0000000..4fc8911
--- /dev/null
+++ b/src_plugins/smartdisperse/smartdisperse.c
@@ -0,0 +1,284 @@
+/*!
+ * \file smartdisperse.c
+ *
+ * \brief Smartdisperse plug-in for PCB.
+ *
+ * \author Copyright (C) 2007 Ben Jackson <ben at ben.com> based on
+ * teardrops.c by Copyright (C) 2006 DJ Delorie <dj at delorie.com> as well
+ * as the original action.c, and autoplace.c.
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * Improve the initial dispersion of elements by choosing an order based
+ * on the netlist, rather than the arbitrary element order.  This isn't
+ * the same as a global autoplace, it's more of a linear autoplace.  It
+ * might make some useful local groupings.  For example, you should not
+ * have to chase all over the board to find the resistor that goes with
+ * a given LED.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include <genht/htpi.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "rats.h"
+#include "error.h"
+#include "move.h"
+#include "draw.h"
+#include "set.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+#define GAP 10000
+static Coord minx;
+static Coord miny;
+static Coord maxx;
+static Coord maxy;
+
+/*!
+ * \brief Place one element.
+ *
+ * Must initialize statics above before calling for the first time.
+ *
+ * This is taken almost entirely from ActionDisperseElements, with cleanup
+ */
+static void place(ElementType * element)
+{
+	Coord dx, dy;
+
+	/* figure out how much to move the element */
+	dx = minx - element->BoundingBox.X1;
+	dy = miny - element->BoundingBox.Y1;
+
+	/* snap to the grid */
+	dx -= (element->MarkX + dx) % (long) (PCB->Grid);
+	dx += (long) (PCB->Grid);
+	dy -= (element->MarkY + dy) % (long) (PCB->Grid);
+	dy += (long) (PCB->Grid);
+
+	/*
+	 * and add one grid size so we make sure we always space by GAP or
+	 * more
+	 */
+	dx += (long) (PCB->Grid);
+
+	/* Figure out if this row has room.  If not, start a new row */
+	if (minx != GAP && GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth) {
+		miny = maxy + GAP;
+		minx = GAP;
+		place(element);							/* recurse can't loop, now minx==GAP */
+		return;
+	}
+
+	/* move the element */
+	MoveElementLowLevel(PCB->Data, element, dx, dy);
+
+	/* and add to the undo list so we can undo this operation */
+	AddObjectToMoveUndoList(PCB_TYPE_ELEMENT, NULL, NULL, element, dx, dy);
+
+	/* keep track of how tall this row is */
+	minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
+	if (maxy < element->BoundingBox.Y2) {
+		maxy = element->BoundingBox.Y2;
+	}
+}
+
+/*!
+ * \brief Return the X location of a connection's pad or pin within its
+ * element.
+ */
+static Coord padDX(ConnectionType * conn)
+{
+	ElementType *element = (ElementType *) conn->ptr1;
+	AnyLineObjectType *line = (AnyLineObjectType *) conn->ptr2;
+
+	return line->BoundingBox.X1 - (element->BoundingBox.X1 + element->BoundingBox.X2) / 2;
+}
+
+/*!
+ * \brief Return true if ea,eb would be the best order, else eb,ea,
+ * based on pad loc.
+ */
+static int padorder(ConnectionType * conna, ConnectionType * connb)
+{
+	Coord dxa, dxb;
+
+	dxa = padDX(conna);
+	dxb = padDX(connb);
+	/* there are other cases that merit rotation, ignore them for now */
+	if (dxa > 0 && dxb < 0)
+		return 1;
+	return 0;
+}
+
+/* ewww, these are actually arrays */
+#define ELEMENT_N(DATA,ELT)	((ELT) - (DATA)->Element)
+#define VISITED(ELT)		(visited[ELEMENT_N(PCB->Data, (ELT))])
+#define IS_ELEMENT(CONN)	((CONN)->type == PCB_TYPE_PAD || (CONN)->type == PCB_TYPE_PIN)
+
+#define ARG(n) (argc > (n) ? argv[n] : 0)
+
+static const char smartdisperse_syntax[] = "SmartDisperse([All|Selected])";
+
+#define set_visited(element) htpi_set(&visited, ((void *)(element)), 1)
+#define is_visited(element)  htpi_has(&visited, ((void *)(element)))
+
+
+static int smartdisperse(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *function = ARG(0);
+	NetListType *Nets;
+	htpi_t visited;
+/*  PointerListType stack = { 0, 0, NULL };*/
+	int all;
+/*  int changed = 0;
+  int i;*/
+
+	if (!function) {
+		all = 1;
+	}
+	else if (strcmp(function, "All") == 0) {
+		all = 1;
+	}
+	else if (strcmp(function, "Selected") == 0) {
+		all = 0;
+	}
+	else {
+		AFAIL(smartdisperse);
+	}
+
+	Nets = ProcNetlist(&PCB->NetlistLib[0]);
+	if (!Nets) {
+		Message(PCB_MSG_ERROR, _("Can't use SmartDisperse because no netlist is loaded.\n"));
+		return 0;
+	}
+
+	/* remember which elements we finish with */
+	htpi_init(&visited, ptrhash, ptrkeyeq);
+
+	/* if we're not doing all, mark the unselected elements as "visited" */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (!(all || TEST_FLAG(PCB_FLAG_SELECTED, element))) {
+			set_visited(element);
+		}
+	}
+	END_LOOP;
+
+	/* initialize variables for place() */
+	minx = GAP;
+	miny = GAP;
+	maxx = GAP;
+	maxy = GAP;
+
+	/*
+	 * Pick nets with two connections.  This is the start of a more
+	 * elaborate algorithm to walk serial nets, but the datastructures
+	 * are too gross so I'm going with the 80% solution.
+	 */
+	NET_LOOP(Nets);
+	{
+		ConnectionType *conna, *connb;
+		ElementType *ea, *eb;
+/*    ElementType *epp;*/
+
+		if (net->ConnectionN != 2)
+			continue;
+
+		conna = &net->Connection[0];
+		connb = &net->Connection[1];
+		if (!IS_ELEMENT(conna) || !IS_ELEMENT(conna))
+			continue;
+
+		ea = (ElementType *) conna->ptr1;
+		eb = (ElementType *) connb->ptr1;
+
+		/* place this pair if possible */
+		if (is_visited(ea) || is_visited(eb))
+			continue;
+		set_visited(ea);
+		set_visited(eb);
+
+		/* a weak attempt to get the linked pads side-by-side */
+		if (padorder(conna, connb)) {
+			place(ea);
+			place(eb);
+		}
+		else {
+			place(eb);
+			place(ea);
+		}
+	}
+	END_LOOP;
+
+	/* Place larger nets, still grouping by net */
+	NET_LOOP(Nets);
+	{
+		CONNECTION_LOOP(net);
+		{
+			ElementType *element;
+
+			if (!IS_ELEMENT(connection))
+				continue;
+
+			element = (ElementType *) connection->ptr1;
+
+			/* place this one if needed */
+			if (is_visited(element))
+				continue;
+			set_visited(element);
+			place(element);
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	/* Place up anything else */
+	ELEMENT_LOOP(PCB->Data);
+	{
+		if (!is_visited(element)) {
+			place(element);
+		}
+	}
+	END_LOOP;
+
+	htpi_uninit(&visited);
+
+	IncrementUndoSerialNumber();
+	Redraw();
+	SetChangedFlag(1);
+
+	return 0;
+}
+
+static HID_Action smartdisperse_action_list[] = {
+	{"smartdisperse", NULL, smartdisperse, NULL, NULL}
+};
+
+char *smartdisperse_cookie = "smartdisperse plugin";
+
+REGISTER_ACTIONS(smartdisperse_action_list, smartdisperse_cookie)
+
+static void hid_smartdisperse_uninit(void)
+{
+	hid_remove_actions_by_cookie(smartdisperse_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_smartdisperse_init()
+{
+	REGISTER_ACTIONS(smartdisperse_action_list, smartdisperse_cookie);
+	return hid_smartdisperse_uninit;
+}
diff --git a/src_plugins/stroke/Makefile b/src_plugins/stroke/Makefile
new file mode 100644
index 0000000..07b7c21
--- /dev/null
+++ b/src_plugins/stroke/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_stroke
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/stroke/Plug.tmpasm b/src_plugins/stroke/Plug.tmpasm
new file mode 100644
index 0000000..5307738
--- /dev/null
+++ b/src_plugins/stroke/Plug.tmpasm
@@ -0,0 +1,17 @@
+put /local/pcb/mod {stroke}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/stroke/stroke.o @]
+
+switch /local/pcb/stroke/controls
+	case {disable} end;
+	default
+		put /local/pcb/mod/LDFLAGS         libs/gui/libstroke/ldflags
+		put /local/pcb/mod/CFLAGS          libs/gui/libstroke/cflags
+		end
+end
+
+
+switch /local/pcb/stroke/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/stroke/README b/src_plugins/stroke/README
new file mode 100644
index 0000000..5c56732
--- /dev/null
+++ b/src_plugins/stroke/README
@@ -0,0 +1,5 @@
+Gesture recognition with libstroke.
+
+#state: partially works (doesn't work with lesstif; works with the gtk hid, but there's no zoom bindings)
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/stroke/stroke.c b/src_plugins/stroke/stroke.c
new file mode 100644
index 0000000..1e59986
--- /dev/null
+++ b/src_plugins/stroke/stroke.c
@@ -0,0 +1,167 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 1994,1995,1996 Thomas Nau
+ *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Contact addresses for paper mail and Email:
+ *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
+ *  haceaton at aplcomm.jhuapl.edu
+ *
+ */
+
+#include "config.h"
+#include <stroke.h>
+#include "global.h"
+#include "conf.h"
+#include "conf_core.h"
+#include "data.h"
+#include "crosshair.h"
+#include "stub_stroke.h"
+#include "rotate.h"
+#include "undo.h"
+#include "set.h"
+#include "error.h"
+#include "misc.h"
+#include "plugins.h"
+
+void FinishStroke(void);
+
+BoxType StrokeBox;
+
+/* ---------------------------------------------------------------------------
+ * FinishStroke - try to recognize the stroke sent
+ */
+static void real_stroke_finish(void)
+{
+	char msg[255];
+	unsigned long num;
+
+	mid_stroke = pcb_false;
+	if (stroke_trans(msg)) {
+		num = atoi(msg);
+		switch (num) {
+		case 456:
+			if (conf_core.editor.mode == PCB_MODE_LINE) {
+				SetMode(PCB_MODE_LINE);
+			}
+			break;
+		case 9874123:
+		case 74123:
+		case 987412:
+		case 8741236:
+		case 874123:
+			RotateScreenObject(StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
+			break;
+		case 7896321:
+		case 786321:
+		case 789632:
+		case 896321:
+			RotateScreenObject(StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
+			break;
+		case 258:
+			SetMode(PCB_MODE_LINE);
+			break;
+		case 852:
+			SetMode(PCB_MODE_ARROW);
+			break;
+		case 1478963:
+			ActionUndo(0, NULL, 0, 0);
+			break;
+		case 147423:
+		case 147523:
+		case 1474123:
+			Redo(pcb_true);
+			break;
+		case 148963:
+		case 147863:
+		case 147853:
+		case 145863:
+			SetMode(PCB_MODE_VIA);
+			break;
+		case 951:
+		case 9651:
+		case 9521:
+		case 9621:
+		case 9851:
+		case 9541:
+		case 96521:
+		case 96541:
+		case 98541:
+			PCB->Zoom = 1000;						/* special zoom extents */
+			break;
+		case 159:
+		case 1269:
+		case 1259:
+		case 1459:
+		case 1569:
+		case 1589:
+		case 12569:
+		case 12589:
+		case 14589:
+			{
+				Coord x = (StrokeBox.X1 + StrokeBox.X2) / 2;
+				Coord y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
+				double z;
+				/* XXX: PCB->MaxWidth and PCB->MaxHeight may be the wrong
+				 *      divisors below. The old code WAS broken, but this
+				 *      replacement has not been tested for correctness.
+				 */
+				z = 1 + log(fabs(StrokeBox.X2 - StrokeBox.X1) / PCB->MaxWidth) / log(2.0);
+				z = MAX(z, 1 + log(fabs(StrokeBox.Y2 - StrokeBox.Y1) / PCB->MaxHeight) / log(2.0));
+				PCB->Zoom = z;
+
+				CenterDisplay(x, y);
+				break;
+			}
+
+		default:
+			Message(PCB_MSG_DEFAULT, _("Unknown stroke %s\n"), msg);
+			break;
+		}
+	}
+	else
+		gui->beep();
+}
+
+static void real_stroke_record(int ev_x, int ev_y)
+{
+	StrokeBox.X2 = ev_x;
+	StrokeBox.Y2 = ev_y;
+	fprintf(stderr, "stroke: %d %d\n", ev_x >> 16, ev_y >> 16);
+	stroke_record(ev_x >> 16, ev_y >> 16);
+	return;
+}
+
+static void real_stroke_start(void)
+{
+	fprintf(stderr, "stroke: MIID!\n");
+	mid_stroke = pcb_true;
+	StrokeBox.X1 = Crosshair.X;
+	StrokeBox.Y1 = Crosshair.Y;
+}
+
+pcb_uninit_t hid_stroke_init(void)
+{
+	stroke_init();
+
+	stub_stroke_record = real_stroke_record;
+	stub_stroke_start = real_stroke_start;
+	stub_stroke_finish = real_stroke_finish;
+	return NULL;
+}
diff --git a/src_plugins/teardrops/Makefile b/src_plugins/teardrops/Makefile
new file mode 100644
index 0000000..f934ca0
--- /dev/null
+++ b/src_plugins/teardrops/Makefile
@@ -0,0 +1,5 @@
+all:
+	cd ../../src && make mod_teardrops
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
diff --git a/src_plugins/teardrops/Plug.tmpasm b/src_plugins/teardrops/Plug.tmpasm
new file mode 100644
index 0000000..6c00fea
--- /dev/null
+++ b/src_plugins/teardrops/Plug.tmpasm
@@ -0,0 +1,8 @@
+put /local/pcb/mod {teardrops}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/teardrops/teardrops.o @]
+
+switch /local/pcb/teardrops/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/teardrops/README b/src_plugins/teardrops/README
new file mode 100644
index 0000000..e7a9a7c
--- /dev/null
+++ b/src_plugins/teardrops/README
@@ -0,0 +1,5 @@
+Draw teardrops on pins.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/teardrops/teardrops.c b/src_plugins/teardrops/teardrops.c
new file mode 100644
index 0000000..8e9a1c2
--- /dev/null
+++ b/src_plugins/teardrops/teardrops.c
@@ -0,0 +1,329 @@
+/*!
+ * \file teardrops.c
+ *
+ * \brief Teardrops plug-in for PCB.
+ *
+ * \author Copyright (C) 2006 DJ Delorie <dj at delorie.com>
+ *
+ * \copyright Licensed under the terms of the GNU General Public
+ * License, version 2 or later.
+ *
+ * Ported to pcb-rnd by Tibor 'Igor2' Palinkas in 2016.
+ *
+ * Original source: http://www.delorie.com/pcb/teardrops/
+*/
+
+#include <stdio.h>
+#include <math.h>
+
+#include "config.h"
+#include "global.h"
+#include "data.h"
+#include "hid.h"
+#include "misc.h"
+#include "create.h"
+#include "rtree.h"
+#include "undo.h"
+#include "plugins.h"
+#include "hid_actions.h"
+
+#define MIN_LINE_LENGTH 700
+#define MAX_DISTANCE 700
+/* changed MAX_DISTANCE to 0.5 mm below */
+ /* 0.5mm */
+/* #define MAX_DISTANCE 500000 */
+/* #define MAX_DISTANCE 2000000 */
+ /* 1 mm */
+/* #define MAX_DISTANCE 1000000 */
+
+static PinType *pin;
+static PadType *pad;
+static int layer;
+static int px, py;
+static Coord thickness;
+static ElementType *element;
+
+static int new_arcs = 0;
+
+int distance_between_points(int x1, int y1, int x2, int y2)
+{
+	/* int a; */
+	/* int b; */
+	int distance;
+	/* a = (x1-x2); */
+	/* b = (y1-y2); */
+	distance = sqrt((pow(x1 - x2, 2)) + (pow(y1 - y2, 2)));
+	return distance;
+}
+
+static r_dir_t check_line_callback(const BoxType * box, void *cl)
+{
+	LayerType *lay = &PCB->Data->Layer[layer];
+	LineType *l = (LineType *) box;
+	int x1, x2, y1, y2;
+	double a, b, c, x, r, t;
+	double dx, dy, len;
+	double ax, ay, lx, ly, theta;
+	double ldist, adist, radius;
+	double vx, vy, vr, vl;
+	int delta, aoffset, count;
+	ArcType *arc;
+
+	fprintf(stderr, "...Line ((%.6f, %.6f), (%.6f, %.6f)): ",
+					PCB_COORD_TO_MM(l->Point1.X), PCB_COORD_TO_MM(l->Point1.Y), PCB_COORD_TO_MM(l->Point2.X), PCB_COORD_TO_MM(l->Point2.Y));
+
+	/* if our line is to short ignore it */
+	if (distance_between_points(l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y) < MIN_LINE_LENGTH) {
+		fprintf(stderr, "not within max line length\n");
+		return 1;
+	}
+
+	fprintf(stderr, "......Point (%.6f, %.6f): ", PCB_COORD_TO_MM(px), PCB_COORD_TO_MM(py));
+
+	if (distance_between_points(l->Point1.X, l->Point1.Y, px, py) < MAX_DISTANCE) {
+		x1 = l->Point1.X;
+		y1 = l->Point1.Y;
+		x2 = l->Point2.X;
+		y2 = l->Point2.Y;
+	}
+	else if (distance_between_points(l->Point2.X, l->Point2.Y, px, py) < MAX_DISTANCE) {
+		x1 = l->Point2.X;
+		y1 = l->Point2.Y;
+		x2 = l->Point1.X;
+		y2 = l->Point1.Y;
+	}
+	else {
+		fprintf(stderr, "not within max distance\n");
+		return 1;
+	}
+
+	/* r = pin->Thickness / 2.0; */
+	r = thickness / 2.0;
+	t = l->Thickness / 2.0;
+
+	if (t > r) {
+		fprintf(stderr, "t > r: t = %3.6f, r = %3.6f\n", PCB_COORD_TO_MM(t), PCB_COORD_TO_MM(r));
+		return 1;
+	}
+
+	a = 1;
+	b = 4 * t - 2 * r;
+	c = 2 * t * t - r * r;
+
+	x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
+
+	len = sqrt(((double) x2 - x1) * (x2 - x1) + ((double) y2 - y1) * (y2 - y1));
+
+	if (len > (x + t)) {
+		adist = ldist = x + t;
+		radius = x + t;
+		delta = 45;
+
+		if (radius < r || radius < t) {
+			fprintf(stderr,
+							"(radius < r || radius < t): radius = %3.6f, r = %3.6f, t = %3.6f\n",
+							PCB_COORD_TO_MM(radius), PCB_COORD_TO_MM(r), PCB_COORD_TO_MM(t));
+			return 1;
+		}
+	}
+	else if (len > r + t) {
+		/* special "short teardrop" code */
+
+		x = (len * len - r * r + t * t) / (2 * (r - t));
+		ldist = len;
+		adist = x + t;
+		radius = x + t;
+		delta = atan2(len, x + t) * 180.0 / M_PI;
+	}
+	else
+		return 1;
+
+	dx = ((double) x2 - x1) / len;
+	dy = ((double) y2 - y1) / len;
+	theta = atan2(y2 - y1, x1 - x2) * 180.0 / M_PI;
+
+	lx = px + dx * ldist;
+	ly = py + dy * ldist;
+
+	/* We need one up front to determine how many segments it will take
+	   to fill.  */
+	ax = lx - dy * adist;
+	ay = ly + dx * adist;
+	vl = sqrt(r * r - t * t);
+	vx = px + dx * vl;
+	vy = py + dy * vl;
+	vx -= dy * t;
+	vy += dx * t;
+	vr = sqrt((ax - vx) * (ax - vx) + (ay - vy) * (ay - vy));
+
+	aoffset = 0;
+	count = 0;
+	do {
+		if (++count > 5) {
+			fprintf(stderr, "......a %d,%d v %d,%d adist %g radius %g vr %g\n",
+							(int) ax, (int) ay, (int) vx, (int) vy, adist, radius, vr);
+			printf("a %d,%d v %d,%d adist %g radius %g vr %g\n", (int) ax, (int) ay, (int) vx, (int) vy, adist, radius, vr);
+			return 1;
+		}
+
+		ax = lx - dy * adist;
+		ay = ly + dx * adist;
+
+		arc = CreateNewArcOnLayer(lay, (int) ax, (int) ay, (int) radius,
+															(int) radius, (int) theta + 90 + aoffset, delta - aoffset, l->Thickness, l->Clearance, l->Flags);
+		if (arc)
+			AddObjectToCreateUndoList(PCB_TYPE_ARC, lay, arc, arc);
+
+		ax = lx + dy * (x + t);
+		ay = ly - dx * (x + t);
+
+		arc = CreateNewArcOnLayer(lay, (int) ax, (int) ay, (int) radius,
+															(int) radius, (int) theta - 90 - aoffset, -delta + aoffset, l->Thickness, l->Clearance, l->Flags);
+		if (arc)
+			AddObjectToCreateUndoList(PCB_TYPE_ARC, lay, arc, arc);
+
+		radius += t * 1.9;
+		aoffset = acos((double) adist / radius) * 180.0 / M_PI;
+
+		new_arcs++;
+	} while (vr > radius - t);
+
+	fprintf(stderr, "done arc'ing\n");
+	return 1;
+}
+
+static void check_pin(PinType * _pin)
+{
+	BoxType spot;
+
+	pin = _pin;
+
+	px = pin->X;
+	py = pin->Y;
+	thickness = pin->Thickness;
+
+	spot.X1 = px - 10;
+	spot.Y1 = py - 10;
+	spot.X2 = px + 10;
+	spot.Y2 = py + 10;
+
+	element = (ElementType *) pin->Element;
+
+	fprintf(stderr, "Pin %s (%s) at %.6f, %.6f (element %s, %s, %s)\n", EMPTY(pin->Number), EMPTY(pin->Name),
+					/* 0.01 * pin->X, 0.01 * pin->Y, */
+					PCB_COORD_TO_MM(pin->X), PCB_COORD_TO_MM(pin->Y),
+					EMPTY(NAMEONPCB_NAME(element)), EMPTY(VALUE_NAME(element)), EMPTY(DESCRIPTION_NAME(element)));
+
+	for (layer = 0; layer < max_copper_layer; layer++) {
+		LayerType *l = &(PCB->Data->Layer[layer]);
+		r_search(l->line_tree, &spot, NULL, check_line_callback, l, NULL);
+	}
+}
+
+static void check_via(PinType * _pin)
+{
+	BoxType spot;
+
+	pin = _pin;
+
+	px = pin->X;
+	py = pin->Y;
+
+	spot.X1 = px - 10;
+	spot.Y1 = py - 10;
+	spot.X2 = px + 10;
+	spot.Y2 = py + 10;
+
+	fprintf(stderr, "Via at %.6f, %.6f\n", PCB_COORD_TO_MM(pin->X), PCB_COORD_TO_MM(pin->Y));
+
+	for (layer = 0; layer < max_copper_layer; layer++) {
+		LayerType *l = &(PCB->Data->Layer[layer]);
+		r_search(l->line_tree, &spot, NULL, check_line_callback, l, NULL);
+	}
+}
+
+/*!
+ * \brief Draw teardrops for pads.
+ */
+static void check_pad(PadType * _pad)
+{
+	pad = _pad;
+
+	px = (pad->BoundingBox.X1 + pad->BoundingBox.X2) / 2;
+	py = (pad->BoundingBox.Y1 + pad->BoundingBox.Y2) / 2;
+	thickness = pad->Thickness;
+	element = (ElementType *) pad->Element;
+
+	fprintf(stderr,
+					"Pad %s (%s) at %.6f, %.6f (element %s, %s, %s) \n",
+					EMPTY(pad->Number), EMPTY(pad->Name),
+					PCB_COORD_TO_MM((pad->BoundingBox.X1 + pad->BoundingBox.X2) / 2),
+					PCB_COORD_TO_MM((pad->BoundingBox.Y1 + pad->BoundingBox.Y2) / 2),
+					EMPTY(NAMEONPCB_NAME(element)), EMPTY(VALUE_NAME(element)), EMPTY(DESCRIPTION_NAME(element)));
+
+	/* fprintf(stderr, */
+	/*   "Pad %s (%s) at ((%.6f, %.6f), (%.6f, %.6f)) (element %s, %s, %s) \n", */
+	/*           EMPTY (pad->Number), EMPTY (pad->Name), */
+	/*           PCB_COORD_TO_MM(pad->BoundingBox.X1), */
+	/*           PCB_COORD_TO_MM(pad->BoundingBox.Y1), */
+	/*           PCB_COORD_TO_MM(pad->BoundingBox.X2), */
+	/*           PCB_COORD_TO_MM(pad->BoundingBox.Y2), */
+	/*           EMPTY (NAMEONPCB_NAME (element)), */
+	/*           EMPTY (VALUE_NAME (element)), */
+	/*           EMPTY (DESCRIPTION_NAME (element))); */
+
+	for (layer = 0; layer < max_copper_layer; layer++) {
+		LayerType *l = &(PCB->Data->Layer[layer]);
+		r_search(l->line_tree, &(pad->BoundingBox), NULL, check_line_callback, l, NULL);
+	}
+}
+
+static int teardrops(int argc, const char **argv, Coord x, Coord y)
+{
+	new_arcs = 0;
+
+	VIA_LOOP(PCB->Data);
+	{
+		check_via(via);
+	}
+	END_LOOP;
+
+	ALLPIN_LOOP(PCB->Data);
+	{
+		check_pin(pin);
+	}
+	ENDALL_LOOP;
+
+	ALLPAD_LOOP(PCB->Data);
+	{
+		check_pad(pad);
+	}
+	ENDALL_LOOP;
+
+	gui->invalidate_all();
+
+	if (new_arcs)
+		IncrementUndoSerialNumber();
+
+	return 0;
+}
+
+static HID_Action teardrops_action_list[] = {
+	{"Teardrops", NULL, teardrops, NULL, NULL}
+};
+
+char *teardrops_cookie = "teardrops plugin";
+
+REGISTER_ACTIONS(teardrops_action_list, teardrops_cookie)
+
+static void hid_teardrops_uninit(void)
+{
+	hid_remove_actions_by_cookie(teardrops_cookie);
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_teardrops_init()
+{
+	REGISTER_ACTIONS(teardrops_action_list, teardrops_cookie);
+	return hid_teardrops_uninit;
+}
diff --git a/src_plugins/toporouter/Makefile b/src_plugins/toporouter/Makefile
new file mode 100644
index 0000000..4d838d9
--- /dev/null
+++ b/src_plugins/toporouter/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_toporouter
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/toporouter/Plug.tmpasm b/src_plugins/toporouter/Plug.tmpasm
new file mode 100644
index 0000000..2afd67e
--- /dev/null
+++ b/src_plugins/toporouter/Plug.tmpasm
@@ -0,0 +1,24 @@
+put /local/pcb/mod {toporouter}
+put /local/pcb/mod/OBJS [@ $(PLUGDIR)/toporouter/toporouter.o @]
+
+append /local/pcb/CFLAGS  {-I../src_3rd/gts}
+
+put /local/pcb/toporouter_rules [@
+../src_3rd/gts/libgts.a:
+	cd ../src_3rd/gts && make
+@]
+
+switch /local/pcb/toporouter/controls
+	case {buildin}
+		append /local/pcb/RULES           /local/pcb/toporouter_rules
+		append /local/pcb/LIBS            { ../src_3rd/gts/libgts.a }
+		append /local/pcb/EXEDEPS         { ../src_3rd/gts/libgts.a }
+		include /local/pcb/tmpasm/buildin
+		end;
+	case {plugin}
+		append /local/pcb/RULES           /local/pcb/toporouter_rules
+		append /local/pcb/toporouter/OBJS { ../src_3rd/gts/libgts.a }
+		include /local/pcb/tmpasm/plugin
+		end
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/toporouter/README b/src_plugins/toporouter/README
new file mode 100644
index 0000000..15ffcce
--- /dev/null
+++ b/src_plugins/toporouter/README
@@ -0,0 +1,7 @@
+Automatically route selected or all rats using a topological algorithm. This
+is the new autorouter from 2009.
+
+#state: fails
+#lstate: infinite loop in gts
+#default: disabled
+#implements: (feature)
diff --git a/src_plugins/toporouter/toporouter.c b/src_plugins/toporouter/toporouter.c
new file mode 100644
index 0000000..4e8ee50
--- /dev/null
+++ b/src_plugins/toporouter/toporouter.c
@@ -0,0 +1,8242 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  Topological Autorouter for 
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2009 Anthony Blake
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for email:
+ *  Anthony Blake, tonyb33 at gmail.com
+ *
+ *
+ *
+ *                 This is *EXPERIMENTAL* code.
+ *
+ *  As the code is experimental, the algorithms and code
+ *  are likely to change. Which means it isn't documented  
+ *  or optimized. If you would like to learn about Topological
+ *  Autorouters, the following papers are good starting points: 
+ *
+ * This file implements a topological autorouter, and uses techniques from the
+ * following publications:
+ *
+ * Dayan, T. and Dai, W.W.M., "Layer Assignment for a Rubber Band Router" Tech
+ * Report UCSC-CRL-92-50, Univ. of California, Santa Cruz, 1992.
+ *
+ * Dai, W.W.M and Dayan, T. and Staepelaere, D., "Topological Routing in SURF:
+ * Generating a Rubber-Band Sketch" Proc. 28th ACM/IEEE Design Automation
+ * Conference, 1991, pp. 39-44.
+ *
+ * David Staepelaere, Jeffrey Jue, Tal Dayan, Wayne Wei-Ming Dai, "SURF:
+ * Rubber-Band Routing System for Multichip Modules," IEEE Design and Test of
+ * Computers ,vol. 10, no. 4,  pp. 18-26, October/December, 1993.
+ *
+ * Dayan, T., "Rubber-band based topological router" PhD Thesis, Univ. of
+ * California, Santa Cruz, 1997.
+ *
+ * David Staepelaere, "Geometric transformations for a rubber-band sketch"
+ * Master's thesis, Univ. of California, Santa Cruz, September 1992.
+ *
+ */
+
+#include "toporouter.h"
+#include "pcb-printf.h"
+
+static void toporouter_edge_init(toporouter_edge_t * edge)
+{
+	edge->routing = NULL;
+	edge->flags = 0;
+}
+
+toporouter_edge_class_t *toporouter_edge_class(void)
+{
+	static toporouter_edge_class_t *klass = NULL;
+
+	if (klass == NULL) {
+		GtsObjectClassInfo constraint_info = {
+			"toporouter_edge_t",
+			sizeof(toporouter_edge_t),
+			sizeof(toporouter_edge_class_t),
+			(GtsObjectClassInitFunc) NULL,
+			(GtsObjectInitFunc) toporouter_edge_init,
+			(GtsArgSetFunc) NULL,
+			(GtsArgGetFunc) NULL
+		};
+		klass = (toporouter_edge_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_edge_class()), &constraint_info);
+	}
+
+	return klass;
+}
+
+static void toporouter_bbox_init(toporouter_bbox_t * box)
+{
+	box->data = NULL;
+	box->type = OTHER;
+	box->constraints = NULL;
+	box->cluster = NULL;
+}
+
+toporouter_bbox_class_t *toporouter_bbox_class(void)
+{
+	static toporouter_bbox_class_t *klass = NULL;
+
+	if (klass == NULL) {
+		GtsObjectClassInfo constraint_info = {
+			"toporouter_bbox_t",
+			sizeof(toporouter_bbox_t),
+			sizeof(toporouter_bbox_class_t),
+			(GtsObjectClassInitFunc) NULL,
+			(GtsObjectInitFunc) toporouter_bbox_init,
+			(GtsArgSetFunc) NULL,
+			(GtsArgGetFunc) NULL
+		};
+		klass = (toporouter_bbox_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_bbox_class()), &constraint_info);
+	}
+
+	return klass;
+}
+
+static void toporouter_vertex_class_init(toporouter_vertex_class_t * klass)
+{
+
+}
+
+static void toporouter_vertex_init(toporouter_vertex_t * vertex)
+{
+	vertex->bbox = NULL;
+	vertex->parent = NULL;
+	vertex->child = NULL;
+	vertex->flags = 0;
+	vertex->routingedge = NULL;
+	vertex->arc = NULL;
+	vertex->oproute = NULL;
+	vertex->route = NULL;
+
+	vertex->gcost = 0.;
+	vertex->hcost = 0.;
+	vertex->gn = 0;
+}
+
+toporouter_vertex_class_t *toporouter_vertex_class(void)
+{
+	static toporouter_vertex_class_t *klass = NULL;
+
+	if (klass == NULL) {
+		GtsObjectClassInfo constraint_info = {
+			"toporouter_vertex_t",
+			sizeof(toporouter_vertex_t),
+			sizeof(toporouter_vertex_class_t),
+			(GtsObjectClassInitFunc) toporouter_vertex_class_init,
+			(GtsObjectInitFunc) toporouter_vertex_init,
+			(GtsArgSetFunc) NULL,
+			(GtsArgGetFunc) NULL
+		};
+		klass = (toporouter_vertex_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_vertex_class()), &constraint_info);
+	}
+
+	return klass;
+}
+
+static void toporouter_constraint_class_init(toporouter_constraint_class_t * klass)
+{
+
+}
+
+static void toporouter_constraint_init(toporouter_constraint_t * constraint)
+{
+	constraint->box = NULL;
+	constraint->routing = NULL;
+}
+
+toporouter_constraint_class_t *toporouter_constraint_class(void)
+{
+	static toporouter_constraint_class_t *klass = NULL;
+
+	if (klass == NULL) {
+		GtsObjectClassInfo constraint_info = {
+			"toporouter_constraint_t",
+			sizeof(toporouter_constraint_t),
+			sizeof(toporouter_constraint_class_t),
+			(GtsObjectClassInitFunc) toporouter_constraint_class_init,
+			(GtsObjectInitFunc) toporouter_constraint_init,
+			(GtsArgSetFunc) NULL,
+			(GtsArgGetFunc) NULL
+		};
+		klass = (toporouter_constraint_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_constraint_class()), &constraint_info);
+	}
+
+	return klass;
+}
+
+static void toporouter_arc_init(toporouter_arc_t * arc)
+{
+	arc->x0 = -1.;
+	arc->y0 = -1.;
+	arc->x1 = -1.;
+	arc->y1 = -1.;
+	arc->centre = NULL;
+	arc->v = NULL;
+	arc->v1 = NULL;
+	arc->v2 = NULL;
+	arc->r = -1.;
+	arc->dir = 31337;
+	arc->clearance = NULL;
+	arc->oproute = NULL;
+}
+
+toporouter_arc_class_t *toporouter_arc_class(void)
+{
+	static toporouter_arc_class_t *klass = NULL;
+
+	if (klass == NULL) {
+		GtsObjectClassInfo constraint_info = {
+			"toporouter_arc_t",
+			sizeof(toporouter_arc_t),
+			sizeof(toporouter_arc_class_t),
+			(GtsObjectClassInitFunc) NULL,
+			(GtsObjectInitFunc) toporouter_arc_init,
+			(GtsArgSetFunc) NULL,
+			(GtsArgGetFunc) NULL
+		};
+		klass = (toporouter_arc_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_constraint_class()), &constraint_info);
+	}
+
+	return klass;
+}
+
+#define MARGIN 10.0f
+
+drawing_context_t *toporouter_output_init(int w, int h, char *filename)
+{
+	drawing_context_t *dc;
+
+	dc = (drawing_context_t *) malloc(sizeof(drawing_context_t));
+
+	dc->iw = w;
+	dc->ih = h;
+	dc->filename = filename;
+
+	/* Calculate scaling to maintain aspect ratio */
+	if (PCB->MaxWidth > PCB->MaxHeight) {
+		/* Scale board width to match image width minus 2xMARGIN */
+		dc->s = ((double) dc->iw - (2 * MARGIN)) / (double) PCB->MaxWidth;
+		dc->ih = (double) PCB->MaxHeight * dc->s + (2 * MARGIN);
+	}
+	else {
+		/* Scale board height to match image height minus 2xMARGIN */
+		dc->s = ((double) dc->ih - (2 * MARGIN)) / (double) PCB->MaxHeight;
+		dc->iw = (double) PCB->MaxWidth * dc->s + (2 * MARGIN);
+	}
+
+#if TOPO_OUTPUT_ENABLED
+	dc->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dc->iw, dc->ih);
+	dc->cr = cairo_create(dc->surface);
+
+	cairo_rectangle(dc->cr, 0, 0, dc->iw, dc->ih);
+	cairo_set_source_rgb(dc->cr, 0, 0, 0);
+	cairo_fill(dc->cr);
+
+#endif
+
+	return dc;
+}
+
+void toporouter_output_close(drawing_context_t * dc)
+{
+#if TOPO_OUTPUT_ENABLED
+	cairo_surface_write_to_png(dc->surface, dc->filename);
+	cairo_destroy(dc->cr);
+	cairo_surface_destroy(dc->surface);
+#endif
+}
+
+gdouble lookup_clearance(char *name)
+{
+	if (name) {
+		int idx = pcb_route_style_lookup(&PCB->RouteStyle, 0, 0, 0, 0, menu->Style);
+		if (idx >= 0)
+			return PCB->RouteStyle.array[idx].Clearance;
+	}
+	return Settings.Clearance;
+}
+
+gdouble lookup_thickness(char *name)
+{
+	if (name) {
+		int idx = pcb_route_style_lookup(&PCB->RouteStyle, 0, 0, 0, 0, menu->Style);
+		if (idx >= 0)
+			return PCB->RouteStyle.array[idx].Thick;
+	}
+	return Settings.LineThickness;
+}
+
+static inline gdouble cluster_clearance(toporouter_cluster_t * cluster)
+{
+	if (cluster)
+		return lookup_clearance(cluster->netlist->style);
+	return lookup_clearance(NULL);
+}
+
+static inline gdouble cluster_thickness(toporouter_cluster_t * cluster)
+{
+	if (cluster)
+		return lookup_thickness(cluster->netlist->style);
+	return lookup_thickness(NULL);
+}
+
+gint toporouter_draw_vertex(gpointer item, gpointer data)
+{
+#if TOPO_OUTPUT_ENABLED
+	drawing_context_t *dc = (drawing_context_t *) data;
+	toporouter_vertex_t *tv;
+	PinType *pin;
+	PadType *pad;
+	gdouble blue;
+
+	if (TOPOROUTER_IS_VERTEX((GtsObject *) item)) {
+		tv = TOPOROUTER_VERTEX((GtsObject *) item);
+
+		if (tv->flags & VERTEX_FLAG_RED) {
+			cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f);
+			cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+			cairo_fill(dc->cr);
+
+		}
+		else if (tv->flags & VERTEX_FLAG_GREEN) {
+			cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f);
+			cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+			cairo_fill(dc->cr);
+
+		}
+		else if (tv->flags & VERTEX_FLAG_BLUE) {
+			cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f);
+			cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+			cairo_fill(dc->cr);
+
+
+		}
+		/* printf("tv->type = %d\n", tv->type); */
+		if (!dc->mode) {
+			if (tv->bbox) {
+				pin = (PinType *) tv->bbox->data;
+				pad = (PadType *) tv->bbox->data;
+
+				blue = 0.0f;
+				switch (tv->bbox->type) {
+				case PIN:
+					cairo_set_source_rgba(dc->cr, 1.0f, 0., 0.0f, 0.2f);
+					cairo_arc(dc->cr,
+										tv->v.p.x * dc->s + MARGIN,
+										tv->v.p.y * dc->s + MARGIN,
+										(((gdouble) pin->Thickness / 2.0f) + (gdouble) lookup_clearance(pin->Name)) * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+					cairo_set_source_rgba(dc->cr, 1.0f, 0., 0., 0.4f);
+					cairo_arc(dc->cr,
+										tv->v.p.x * dc->s + MARGIN,
+										tv->v.p.y * dc->s + MARGIN, (gdouble) (pin->Thickness) / 2.0f * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+					break;
+				case VIA:
+					cairo_set_source_rgba(dc->cr, 0.0f, 0., 1., 0.2f);
+					cairo_arc(dc->cr,
+										tv->v.p.x * dc->s + MARGIN,
+										tv->v.p.y * dc->s + MARGIN,
+										(((gdouble) pin->Thickness / 2.0f) + (gdouble) lookup_clearance(pin->Name)) * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+					cairo_set_source_rgba(dc->cr, 0.0f, 0., 1., 0.4f);
+					cairo_arc(dc->cr,
+										tv->v.p.x * dc->s + MARGIN,
+										tv->v.p.y * dc->s + MARGIN, (gdouble) (pin->Thickness) / 2.0f * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+					break;
+				case PAD:
+					cairo_set_source_rgba(dc->cr, 0.0f, 1., 0., 0.5f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 400. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+					break;
+				default:
+					break;
+				}
+			}
+		}
+		else {
+			if (tv->flags & VERTEX_FLAG_BLUE) {
+				cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f);
+				cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+				cairo_fill(dc->cr);
+			}
+			else if (tv->flags & VERTEX_FLAG_RED) {
+				cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f);
+				cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+				cairo_fill(dc->cr);
+
+			}
+			else if (tv->flags & VERTEX_FLAG_GREEN) {
+				cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f);
+				cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+				cairo_fill(dc->cr);
+			}
+
+		}
+	}
+	else {
+		fprintf(stderr, "Unknown data passed to toporouter_draw_vertex, aborting foreach\n");
+		return -1;
+	}
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+gint toporouter_draw_edge(gpointer item, gpointer data)
+{
+#if TOPO_OUTPUT_ENABLED
+	drawing_context_t *dc = (drawing_context_t *) data;
+	toporouter_edge_t *te;
+	toporouter_constraint_t *tc;
+
+	if (TOPOROUTER_IS_EDGE((GtsObject *) item)) {
+		te = TOPOROUTER_EDGE((GtsObject *) item);
+		cairo_set_source_rgba(dc->cr, 1.0f, 1.0f, 1.0f, 0.5f);
+		cairo_move_to(dc->cr, te->e.segment.v1->p.x * dc->s + MARGIN, te->e.segment.v1->p.y * dc->s + MARGIN);
+		cairo_line_to(dc->cr, te->e.segment.v2->p.x * dc->s + MARGIN, te->e.segment.v2->p.y * dc->s + MARGIN);
+		cairo_stroke(dc->cr);
+	}
+	else if (TOPOROUTER_IS_CONSTRAINT((GtsObject *) item)) {
+		tc = TOPOROUTER_CONSTRAINT((GtsObject *) item);
+		if (tc->box) {
+			switch (tc->box->type) {
+			case BOARD:
+				cairo_set_source_rgba(dc->cr, 1.0f, 0.0f, 1.0f, 0.9f);
+				cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+				cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+				cairo_stroke(dc->cr);
+				break;
+			case PIN:
+			case PAD:
+				cairo_set_source_rgba(dc->cr, 1.0f, 0.0f, 0.0f, 0.9f);
+				cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+				cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+				cairo_stroke(dc->cr);
+				break;
+			case LINE:
+				cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.9f);
+				cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+				cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+				cairo_stroke(dc->cr);
+				break;
+
+			default:
+				cairo_set_source_rgba(dc->cr, 1.0f, 1.0f, 0.0f, 0.9f);
+				cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+				cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+				cairo_stroke(dc->cr);
+				break;
+			}
+		}
+		else {
+			printf("CONSTRAINT without box\n");
+
+		}
+	}
+	else {
+		fprintf(stderr, "Unknown data passed to toporouter_draw_edge, aborting foreach\n");
+		return -1;
+	}
+
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+
+/*#define vertex_bbox(v) (v->bbox)*/
+toporouter_bbox_t *vertex_bbox(toporouter_vertex_t * v)
+{
+	return v ? v->bbox : NULL;
+}
+
+
+char *vertex_netlist(toporouter_vertex_t * v)
+{
+	toporouter_bbox_t *box = vertex_bbox(v);
+
+	if (box && box->cluster)
+		return box->cluster->netlist->netlist;
+
+	return NULL;
+}
+
+char *constraint_netlist(toporouter_constraint_t * c)
+{
+	toporouter_bbox_t *box = c->box;
+
+	if (box && box->cluster)
+		return box->cluster->netlist->netlist;
+
+	return NULL;
+}
+
+static inline guint epsilon_equals(gdouble a, gdouble b)
+{
+	if (a > b - EPSILON && a < b + EPSILON)
+		return 1;
+	return 0;
+}
+
+void print_bbox(toporouter_bbox_t * box)
+{
+	printf("[BBOX ");
+	switch (box->type) {
+	case PAD:
+		printf("PAD ");
+		break;
+	case PIN:
+		printf("PIN ");
+		break;
+	case VIA:
+		printf("VIA ");
+		break;
+	case LINE:
+		printf("LINE ");
+		break;
+	case BOARD:
+		printf("BOARD ");
+		break;
+	case POLYGON:
+		printf("POLYGON ");
+		break;
+	default:
+		printf("UNKNOWN ");
+		break;
+	}
+
+	if (box->point)
+		printf("P: %f,%f,%f ", vx(box->point), vy(box->point), vz(box->point));
+	else
+		printf("P: NONE ");
+
+	printf("LAYER: %d ", box->layer);
+	printf("CLUSTER: %d]\n", box->cluster ? box->cluster->c : -1);
+
+}
+
+void print_vertex(toporouter_vertex_t * v)
+{
+	if (v)
+		printf("[V %f,%f,%f ", vx(v), vy(v), vz(v));
+	else
+		printf("[V (null) ");
+
+	printf("%s ", vertex_netlist(v));
+	if (v->route && v->route->netlist)
+		printf("%s ", v->route->netlist->netlist);
+
+	if (v->routingedge) {
+		guint n = g_list_length(edge_routing(v->routingedge));
+		guint pos = g_list_index(edge_routing(v->routingedge), v);
+
+		if (TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+			printf("[CONST ");
+		else
+			printf("[EDGE ");
+
+		printf("%d/%d] ", pos, n);
+
+	}
+
+
+	if (v->flags & VERTEX_FLAG_TEMP)
+		printf("TEMP ");
+	if (v->flags & VERTEX_FLAG_ROUTE)
+		printf("ROUTE ");
+	if (v->flags & VERTEX_FLAG_SPECCUT)
+		printf("SPECCUT ");
+	if (v->flags & VERTEX_FLAG_FAKE)
+		printf("FAKE ");
+
+	printf("]\n");
+
+}
+
+gdouble vertex_net_thickness(toporouter_vertex_t * v)
+{
+	toporouter_bbox_t *box = vertex_bbox(v);
+
+	if (!box) {
+
+		while (v && (v->flags & VERTEX_FLAG_TEMP || v->flags & VERTEX_FLAG_ROUTE)) {
+			v = v->parent;
+		}
+
+		box = vertex_bbox(v);
+
+	}
+	else {
+		if (box->type == PIN || box->type == VIA) {
+			PinType *pin = (PinType *) box->data;
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin) || TEST_FLAG(PCB_FLAG_OCTAGON, pin)) {
+				return 0.;
+			}
+/*      return ((PinType *)box->data)->Thickness + 1.;*/
+			return ((PinType *) box->data)->Thickness;
+		}
+		else if (box->type == PAD) {
+			PadType *pad = (PadType *) box->data;
+			if (pad->Point1.X == pad->Point2.X && pad->Point1.Y == pad->Point2.Y && !TEST_FLAG(PCB_FLAG_SQUARE, pad)) {
+				return pad->Thickness;
+			}
+			return 0.;
+		}
+		else if (box->type == BOARD) {
+			return 0.;
+		}
+		else if (box->type == LINE) {
+			LineType *line = (LineType *) box->data;
+			return line->Thickness;
+		}
+		else if (box->type == POLYGON) {
+			return 0.;
+		}
+
+		printf("Unrecognized type in thickness lookup..\n");
+	}
+
+/*  if(!box || !box->cluster) return Settings.LineThickness + 1.;*/
+	if (!box || !box->cluster)
+		return Settings.LineThickness;
+
+	return cluster_thickness(box->cluster);
+}
+
+gdouble vertex_net_clearance(toporouter_vertex_t * v)
+{
+	toporouter_bbox_t *box = vertex_bbox(v);
+	if (!box) {
+
+		while (v && (v->flags & VERTEX_FLAG_TEMP || v->flags & VERTEX_FLAG_ROUTE)) {
+			v = v->parent;
+		}
+		box = vertex_bbox(v);
+	}
+	else {
+		/*  if(box->type == PIN || box->type == VIA) 
+		   return ((PinType *)box->data)->Clearance;
+		   else if(box->type == PAD)
+		   return ((PadType *)box->data)->Clearance; */
+
+	}
+
+/*  if(!box || !box->cluster) return Settings.Clearance + 1.; */
+	if (!box || !box->cluster)
+		return Settings.Clearance;
+	return cluster_clearance(box->cluster);
+}
+
+/*
+void
+print_trace (void)
+{
+  void *array[10];
+  size_t size;
+  char **strings;
+  size_t i;
+
+  size = backtrace (array, 10);
+  strings = backtrace_symbols (array, size);
+
+  printf ("Obtained %zd stack frames.\n", size);
+
+  for (i = 0; i < size; i++)
+    printf ("%s\n", strings[i]);
+
+  free (strings);
+}
+*/
+/* fills in x and y with coordinates of point from a towards b of distance d */
+void point_from_point_to_point(toporouter_vertex_t * a, toporouter_vertex_t * b, gdouble d, gdouble * x, gdouble * y)
+{
+	gdouble dx = vx(b) - vx(a);
+	gdouble dy = vy(b) - vy(a);
+	gdouble theta = atan(fabs(dy / dx));
+
+/*#ifdef DEBUG_EXPORT  */
+	if (!finite(theta)) {
+/*    printf("!finte(theta): a = %f,%f b = %f,%f d = %f\n", vx(a), vy(a), vx(b), vy(b), d);
+      print_trace(); */
+		/*TODO: this shouldn't happen, fix the hack */
+		*x = vx(a);
+		*y = vy(a);
+		return;
+	}
+/*#endif*/
+
+	g_assert(finite(theta));
+
+	*x = vx(a);
+	*y = vy(a);
+
+	if (dx >= 0.) {
+
+		if (dy >= 0.) {
+			*x += d * cos(theta);
+			*y += d * sin(theta);
+		}
+		else {
+			*x += d * cos(theta);
+			*y -= d * sin(theta);
+		}
+
+	}
+	else {
+
+		if (dy >= 0.) {
+			*x -= d * cos(theta);
+			*y += d * sin(theta);
+		}
+		else {
+			*x -= d * cos(theta);
+			*y -= d * sin(theta);
+		}
+
+	}
+}
+
+
+static inline gint coord_wind(gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble cx, gdouble cy)
+{
+	gdouble rval, dx1, dx2, dy1, dy2;
+	dx1 = bx - ax;
+	dy1 = by - ay;
+	dx2 = cx - bx;
+	dy2 = cy - by;
+	rval = (dx1 * dy2) - (dy1 * dx2);
+	return (rval > EPSILON) ? 1 : ((rval < -EPSILON) ? -1 : 0);
+}
+
+/* wind_v:
+ * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively.
+ */
+int point_wind(GtsPoint * a, GtsPoint * b, GtsPoint * c)
+{
+	gdouble rval, dx1, dx2, dy1, dy2;
+	dx1 = b->x - a->x;
+	dy1 = b->y - a->y;
+	dx2 = c->x - b->x;
+	dy2 = c->y - b->y;
+	rval = (dx1 * dy2) - (dy1 * dx2);
+	return (rval > EPSILON) ? 1 : ((rval < -EPSILON) ? -1 : 0);
+}
+
+static inline int vertex_wind(GtsVertex * a, GtsVertex * b, GtsVertex * c)
+{
+	return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c));
+}
+
+static inline int tvertex_wind(toporouter_vertex_t * a, toporouter_vertex_t * b, toporouter_vertex_t * c)
+{
+	return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c));
+}
+
+int sloppy_point_wind(GtsPoint * a, GtsPoint * b, GtsPoint * c)
+{
+	gdouble rval, dx1, dx2, dy1, dy2;
+	dx1 = b->x - a->x;
+	dy1 = b->y - a->y;
+	dx2 = c->x - b->x;
+	dy2 = c->y - b->y;
+	rval = (dx1 * dy2) - (dy1 * dx2);
+	return (rval > 10.) ? 1 : ((rval < -10.) ? -1 : 0);
+}
+
+static inline int sloppy_vertex_wind(GtsVertex * a, GtsVertex * b, GtsVertex * c)
+{
+	return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c));
+}
+
+/* moves vertex v d units in the direction of vertex p */
+void coord_move_towards_coord_values(gdouble ax, gdouble ay, gdouble px, gdouble py, gdouble d, gdouble * x, gdouble * y)
+{
+	gdouble dx = px - ax;
+	gdouble dy = py - ay;
+	gdouble theta = atan(fabs(dy / dx));
+
+
+	if (!finite(theta)) {
+		printf("!finite(theta) a = %f,%f p = %f,%f d = %f\n", ax, ay, px, py, d);
+
+	}
+
+	g_assert(finite(theta));
+
+	if (dx >= 0.) {
+
+		if (dy >= 0.) {
+			*x = ax + (d * cos(theta));
+			*y = ay + (d * sin(theta));
+		}
+		else {
+			*x = ax + (d * cos(theta));
+			*y = ay - (d * sin(theta));
+		}
+
+	}
+	else {
+
+		if (dy >= 0.) {
+			*x = ax - (d * cos(theta));
+			*y = ay + (d * sin(theta));
+		}
+		else {
+			*x = ax - (d * cos(theta));
+			*y = ay - (d * sin(theta));
+		}
+
+	}
+
+}
+
+/* moves vertex v d units in the direction of vertex p */
+void vertex_move_towards_point_values(GtsVertex * v, gdouble px, gdouble py, gdouble d, gdouble * x, gdouble * y)
+{
+	gdouble dx = px - GTS_POINT(v)->x;
+	gdouble dy = py - GTS_POINT(v)->y;
+	gdouble theta = atan(fabs(dy / dx));
+
+	g_assert(finite(theta));
+
+	if (dx >= 0.) {
+
+		if (dy >= 0.) {
+			*x = GTS_POINT(v)->x + (d * cos(theta));
+			*y = GTS_POINT(v)->y + (d * sin(theta));
+		}
+		else {
+			*x = GTS_POINT(v)->x + (d * cos(theta));
+			*y = GTS_POINT(v)->y - (d * sin(theta));
+		}
+
+	}
+	else {
+
+		if (dy >= 0.) {
+			*x = GTS_POINT(v)->x - (d * cos(theta));
+			*y = GTS_POINT(v)->y + (d * sin(theta));
+		}
+		else {
+			*x = GTS_POINT(v)->x - (d * cos(theta));
+			*y = GTS_POINT(v)->y - (d * sin(theta));
+		}
+
+	}
+
+}
+
+/* moves vertex v d units in the direction of vertex p */
+void vertex_move_towards_vertex_values(GtsVertex * v, GtsVertex * p, gdouble d, gdouble * x, gdouble * y)
+{
+	gdouble dx = GTS_POINT(p)->x - GTS_POINT(v)->x;
+	gdouble dy = GTS_POINT(p)->y - GTS_POINT(v)->y;
+	gdouble theta = atan(fabs(dy / dx));
+
+	g_assert(finite(theta));
+
+	if (dx >= 0.) {
+
+		if (dy >= 0.) {
+			*x = GTS_POINT(v)->x + (d * cos(theta));
+			*y = GTS_POINT(v)->y + (d * sin(theta));
+		}
+		else {
+			*x = GTS_POINT(v)->x + (d * cos(theta));
+			*y = GTS_POINT(v)->y - (d * sin(theta));
+		}
+
+	}
+	else {
+
+		if (dy >= 0.) {
+			*x = GTS_POINT(v)->x - (d * cos(theta));
+			*y = GTS_POINT(v)->y + (d * sin(theta));
+		}
+		else {
+			*x = GTS_POINT(v)->x - (d * cos(theta));
+			*y = GTS_POINT(v)->y - (d * sin(theta));
+		}
+
+	}
+
+}
+
+#define tv_on_layer(v,l) (l == TOPOROUTER_BBOX(TOPOROUTER_VERTEX(v)->boxes->data)->layer)
+
+static inline gdouble min_spacing(toporouter_vertex_t * v1, toporouter_vertex_t * v2)
+{
+
+	gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms;
+/*  toporouter_edge_t *e = v1->routingedge;*/
+
+	v1halfthick = vertex_net_thickness(TOPOROUTER_VERTEX(v1)) / 2.;
+	v2halfthick = vertex_net_thickness(TOPOROUTER_VERTEX(v2)) / 2.;
+
+	v1clearance = vertex_net_clearance(TOPOROUTER_VERTEX(v1));
+	v2clearance = vertex_net_clearance(TOPOROUTER_VERTEX(v2));
+
+	ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance);
+
+#ifdef SPACING_DEBUG
+	printf("v1halfthick = %f v2halfthick = %f v1clearance = %f v2clearance = %f ms = %f\n",
+				 v1halfthick, v2halfthick, v1clearance, v2clearance, ms);
+#endif
+
+	return ms;
+}
+
+/* v1 is a vertex in the CDT, and v2 is a net... other way around?*/
+static inline gdouble min_vertex_net_spacing(toporouter_vertex_t * v1, toporouter_vertex_t * v2)
+{
+
+	gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms;
+
+	v1halfthick = vertex_net_thickness(TOPOROUTER_VERTEX(v1)) / 2.;
+	v2halfthick = cluster_thickness(vertex_bbox(v2)->cluster) / 2.;
+
+	v1clearance = vertex_net_clearance(TOPOROUTER_VERTEX(v1));
+	v2clearance = cluster_clearance(vertex_bbox(v2)->cluster);
+
+	ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance);
+
+	return ms;
+}
+
+static inline gdouble min_oproute_vertex_spacing(toporouter_oproute_t * oproute, toporouter_vertex_t * v2)
+{
+
+	gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms;
+
+	v1halfthick = lookup_thickness(oproute->style) / 2.;
+	v2halfthick = vertex_net_thickness(v2) / 2.;
+
+	v1clearance = lookup_clearance(oproute->style);
+	v2clearance = vertex_net_clearance(v2);
+
+	ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance);
+
+	return ms;
+}
+
+gdouble min_oproute_net_spacing(toporouter_oproute_t * oproute, toporouter_vertex_t * v2)
+{
+
+	gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms;
+
+	v1halfthick = lookup_thickness(oproute->style) / 2.;
+	v2halfthick = cluster_thickness(v2->route->src) / 2.;
+
+	v1clearance = lookup_clearance(oproute->style);
+	v2clearance = cluster_clearance(v2->route->src);
+
+	ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance);
+
+	return ms;
+}
+
+gdouble min_net_net_spacing(toporouter_vertex_t * v1, toporouter_vertex_t * v2)
+{
+
+	gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms;
+
+	v1halfthick = cluster_thickness(v1->route->src) / 2.;
+	v2halfthick = cluster_thickness(v2->route->src) / 2.;
+
+	v1clearance = cluster_clearance(v1->route->src);
+	v2clearance = cluster_clearance(v2->route->src);
+
+	ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance);
+
+	return ms;
+}
+
+void
+toporouter_draw_cluster(toporouter_t * r, drawing_context_t * dc, toporouter_cluster_t * cluster, gdouble red, gdouble green,
+												gdouble blue, guint layer)
+{
+#if TOPO_OUTPUT_ENABLED
+/*GList *i = cluster->i;
+
+  while(i) {
+    toporouter_bbox_t *box = TOPOROUTER_BBOX(i->data);
+
+    if(box->point && vz(box->point) == layer) {
+      cairo_set_source_rgba(dc->cr, red, green, blue, 0.8f);
+      cairo_arc(dc->cr, vx(box->point) * dc->s + MARGIN, vy(box->point) * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+      cairo_fill(dc->cr);
+    }
+
+    i = i->next;
+  }
+*/
+#endif
+}
+
+void
+toporouter_draw_surface(toporouter_t * r, GtsSurface * s, char *filename, int w, int h, int mode, GList * datas, int layer,
+												GList * candidatepoints)
+{
+#if TOPO_OUTPUT_ENABLED
+	drawing_context_t *dc;
+	GList *i;
+	toporouter_vertex_t *tv, *tv2 = NULL;
+
+	dc = toporouter_output_init(w, h, filename);
+	dc->mode = mode;
+	dc->data = NULL;
+
+	gts_surface_foreach_edge(s, toporouter_draw_edge, dc);
+	gts_surface_foreach_vertex(s, toporouter_draw_vertex, dc);
+
+	i = r->routednets;
+	while (i) {
+		GList *j = TOPOROUTER_ROUTE(i->data)->path;
+		tv2 = NULL;
+		while (j) {
+			tv = TOPOROUTER_VERTEX(j->data);
+			if (GTS_POINT(tv)->z == layer) {
+				if (tv && tv2) {
+					cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.8f);
+					cairo_move_to(dc->cr, GTS_POINT(tv)->x * dc->s + MARGIN, GTS_POINT(tv)->y * dc->s + MARGIN);
+					cairo_line_to(dc->cr, GTS_POINT(tv2)->x * dc->s + MARGIN, GTS_POINT(tv2)->y * dc->s + MARGIN);
+					cairo_stroke(dc->cr);
+				}
+
+				if (tv->flags & VERTEX_FLAG_RED) {
+					cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+				}
+				else if (tv->flags & VERTEX_FLAG_GREEN) {
+					cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+				}
+				else if (tv->flags & VERTEX_FLAG_BLUE) {
+					cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+				}
+				else {
+
+					cairo_set_source_rgba(dc->cr, 1., 1., 1., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+
+				}
+
+				if (tv->routingedge && !TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) {
+					gdouble tempx, tempy, nms, pms;
+					GList *i = g_list_find(edge_routing(tv->routingedge), tv);
+					toporouter_vertex_t *nextv, *prevv;
+
+					nextv = edge_routing_next(tv->routingedge, i);
+					prevv = edge_routing_prev(tv->routingedge, i);
+
+					nms = min_spacing(tv, nextv);
+					pms = min_spacing(tv, prevv);
+
+					g_assert(finite(nms));
+					g_assert(finite(pms));
+
+					point_from_point_to_point(tv, nextv, nms, &tempx, &tempy);
+
+					cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 1.0f, 0.8f);
+					cairo_move_to(dc->cr, vx(tv) * dc->s + MARGIN, vy(tv) * dc->s + MARGIN);
+					cairo_line_to(dc->cr, tempx * dc->s + MARGIN, tempy * dc->s + MARGIN);
+					cairo_stroke(dc->cr);
+
+					point_from_point_to_point(tv, prevv, pms, &tempx, &tempy);
+
+					cairo_move_to(dc->cr, vx(tv) * dc->s + MARGIN, vy(tv) * dc->s + MARGIN);
+					cairo_line_to(dc->cr, tempx * dc->s + MARGIN, tempy * dc->s + MARGIN);
+					cairo_stroke(dc->cr);
+
+
+
+
+				}
+
+
+			}
+			tv2 = tv;
+			j = j->next;
+		}
+		i = i->next;
+	}
+
+	while (datas) {
+		toporouter_route_t *routedata = (toporouter_route_t *) datas->data;
+
+		GList *i;										/*, *k; */
+
+		toporouter_draw_cluster(r, dc, routedata->src, 1., 0., 0., layer);
+		toporouter_draw_cluster(r, dc, routedata->dest, 0., 0., 1., layer);
+
+		tv2 = NULL;
+		i = routedata->path;
+		while (i) {
+			tv = TOPOROUTER_VERTEX(i->data);
+			if (GTS_POINT(tv)->z == layer) {
+				if (tv && tv2) {
+					cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.8f);
+					cairo_move_to(dc->cr, GTS_POINT(tv)->x * dc->s + MARGIN, GTS_POINT(tv)->y * dc->s + MARGIN);
+					cairo_line_to(dc->cr, GTS_POINT(tv2)->x * dc->s + MARGIN, GTS_POINT(tv2)->y * dc->s + MARGIN);
+					cairo_stroke(dc->cr);
+				}
+			}
+			tv2 = tv;
+			i = i->next;
+		}
+
+
+		if (routedata->alltemppoints) {
+			GList *i, *j;
+			i = j = g_hash_table_get_keys(routedata->alltemppoints);
+			while (i) {
+				toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+
+				if (GTS_POINT(tv)->z != layer) {
+					i = i->next;
+					continue;
+				}
+				if (tv->flags & VERTEX_FLAG_BLUE) {
+					cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+				}
+				else if (tv->flags & VERTEX_FLAG_RED) {
+					cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+
+				}
+				else if (tv->flags & VERTEX_FLAG_GREEN) {
+					cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+				}
+				else {
+					cairo_set_source_rgba(dc->cr, 1., 1., 1., 0.8f);
+					cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI);
+					cairo_fill(dc->cr);
+				}
+				i = i->next;
+			}
+			g_list_free(j);
+		}
+		datas = datas->next;
+	}
+	toporouter_output_close(dc);
+#endif
+}
+
+
+void toporouter_layer_free(toporouter_layer_t * l)
+{
+	g_list_free(l->vertices);
+	g_list_free(l->constraints);
+
+}
+
+guint groupcount(void)
+{
+	int group;
+	guint count = 0;
+
+	for (group = 0; group < max_group; group++) {
+		if (PCB->LayerGroups.Number[group] > 0)
+			count++;
+	}
+
+	return count;
+}
+
+void toporouter_free(toporouter_t * r)
+{
+	struct timeval endtime;
+	int secs, usecs;
+
+	int i;
+	for (i = 0; i < groupcount(); i++) {
+		toporouter_layer_free(&r->layers[i]);
+	}
+
+
+	gettimeofday(&endtime, NULL);
+
+	secs = (int) (endtime.tv_sec - r->starttime.tv_sec);
+	usecs = (int) ((endtime.tv_usec - r->starttime.tv_usec) / 1000);
+
+	if (usecs < 0) {
+		secs -= 1;
+		usecs += 1000;
+	}
+
+	Message(PCB_MSG_DEFAULT, _("Elapsed time: %d.%02d seconds\n"), secs, usecs);
+	free(r->layers);
+	free(r);
+
+}
+
+/* wind:
+ * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively.
+ */
+int wind(toporouter_spoint_t * p1, toporouter_spoint_t * p2, toporouter_spoint_t * p3)
+{
+	double rval, dx1, dx2, dy1, dy2;
+	dx1 = p2->x - p1->x;
+	dy1 = p2->y - p1->y;
+	dx2 = p3->x - p2->x;
+	dy2 = p3->y - p2->y;
+	rval = (dx1 * dy2) - (dy1 * dx2);
+	return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0);
+}
+
+/* wind_double:
+ * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively.
+ */
+int wind_double(gdouble p1_x, gdouble p1_y, gdouble p2_x, gdouble p2_y, gdouble p3_x, gdouble p3_y)
+{
+	double rval, dx1, dx2, dy1, dy2;
+	dx1 = p2_x - p1_x;
+	dy1 = p2_y - p1_y;
+	dx2 = p3_x - p2_x;
+	dy2 = p3_y - p2_y;
+	rval = (dx1 * dy2) - (dy1 * dx2);
+	return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0);
+}
+
+static inline void print_toporouter_constraint(toporouter_constraint_t * tc)
+{
+	printf("%f,%f -> %f,%f ",
+				 tc->c.edge.segment.v1->p.x, tc->c.edge.segment.v1->p.y, tc->c.edge.segment.v2->p.x, tc->c.edge.segment.v2->p.y);
+}
+
+static inline void print_toporouter_vertex(toporouter_vertex_t * tv)
+{
+	printf("%f,%f ", tv->v.p.x, tv->v.p.y);
+}
+
+
+/**
+ * vertices_on_line:
+ * Given vertex a, gradient m, and radius r: 
+ *
+ * Return vertices on line of a & m at r from a
+ */
+void vertices_on_line(toporouter_spoint_t * a, gdouble m, gdouble r, toporouter_spoint_t * b0, toporouter_spoint_t * b1)
+{
+
+	gdouble c, temp;
+
+	if (m == INFINITY || m == -INFINITY) {
+		b0->y = a->y + r;
+		b1->y = a->y - r;
+
+		b0->x = a->x;
+		b1->x = a->x;
+
+		return;
+	}
+
+	c = a->y - (m * a->x);
+
+	temp = sqrt(pow(r, 2) / (1 + pow(m, 2)));
+
+	b0->x = a->x + temp;
+	b1->x = a->x - temp;
+
+	b0->y = b0->x * m + c;
+	b1->y = b1->x * m + c;
+
+}
+
+/**
+ * vertices_on_line:
+ * Given vertex a, gradient m, and radius r: 
+ *
+ * Return vertices on line of a & m at r from a
+ */
+void coords_on_line(gdouble ax, gdouble ay, gdouble m, gdouble r, gdouble * b0x, gdouble * b0y, gdouble * b1x, gdouble * b1y)
+{
+
+	gdouble c, temp;
+
+	if (m == INFINITY || m == -INFINITY) {
+		*b0y = ay + r;
+		*b1y = ay - r;
+
+		*b0x = ax;
+		*b1x = ax;
+
+		return;
+	}
+
+	c = ay - (m * ax);
+
+	temp = sqrt(pow(r, 2) / (1 + pow(m, 2)));
+
+	*b0x = ax + temp;
+	*b1x = ax - temp;
+
+	*b0y = *b0x * m + c;
+	*b1y = *b1x * m + c;
+
+}
+
+/**
+ * vertices_on_line:
+ * Given vertex a, gradient m, and radius r: 
+ *
+ * Return vertices on line of a & m at r from a
+ */
+void points_on_line(GtsPoint * a, gdouble m, gdouble r, GtsPoint * b0, GtsPoint * b1)
+{
+
+	gdouble c, temp;
+
+	if (m == INFINITY || m == -INFINITY) {
+		b0->y = a->y + r;
+		b1->y = a->y - r;
+
+		b0->x = a->x;
+		b1->x = a->x;
+
+		return;
+	}
+
+	c = a->y - (m * a->x);
+
+	temp = sqrt(pow(r, 2) / (1 + pow(m, 2)));
+
+	b0->x = a->x + temp;
+	b1->x = a->x - temp;
+
+	b0->y = b0->x * m + c;
+	b1->y = b1->x * m + c;
+
+}
+
+/*
+ * Returns gradient of segment given by a & b
+ */
+gdouble vertex_gradient(toporouter_spoint_t * a, toporouter_spoint_t * b)
+{
+	if (a->x == b->x)
+		return INFINITY;
+
+	return ((b->y - a->y) / (b->x - a->x));
+}
+
+/*
+ * Returns gradient of segment given by (x0,y0) & (x1,y1)
+ */
+static inline gdouble cartesian_gradient(gdouble x0, gdouble y0, gdouble x1, gdouble y1)
+{
+	if (epsilon_equals(x0, x1))
+		return INFINITY;
+
+	return ((y1 - y0) / (x1 - x0));
+}
+
+/*
+ * Returns gradient of segment given by (x0,y0) & (x1,y1)
+ */
+static inline gdouble point_gradient(GtsPoint * a, GtsPoint * b)
+{
+	return cartesian_gradient(a->x, a->y, b->x, b->y);
+}
+
+gdouble segment_gradient(GtsSegment * s)
+{
+	return cartesian_gradient(GTS_POINT(s->v1)->x, GTS_POINT(s->v1)->y, GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y);
+}
+
+/*
+ * Returns gradient perpendicular to m
+ */
+gdouble perpendicular_gradient(gdouble m)
+{
+	if (isinf(m))
+		return 0.0f;
+	if (m < EPSILON && m > -EPSILON)
+		return INFINITY;
+	return -1.0f / m;
+}
+
+/*
+ * Returns the distance between two vertices in the x-y plane
+ */
+gdouble vertices_plane_distance(toporouter_spoint_t * a, toporouter_spoint_t * b)
+{
+	return sqrt(pow(a->x - b->x, 2) + pow(a->y - b->y, 2));
+}
+
+/*
+ * Finds the point p distance r away from a on the line segment of a & b 
+ */
+static inline void vertex_outside_segment(toporouter_spoint_t * a, toporouter_spoint_t * b, gdouble r, toporouter_spoint_t * p)
+{
+	toporouter_spoint_t temp[2];
+
+	vertices_on_line(a, vertex_gradient(a, b), r, &temp[0], &temp[1]);
+
+	if (vertices_plane_distance(&temp[0], b) > vertices_plane_distance(&temp[1], b)) {
+		p->x = temp[0].x;
+		p->y = temp[0].y;
+	}
+	else {
+		p->x = temp[1].x;
+		p->y = temp[1].y;
+	}
+
+}
+
+/* proper intersection:
+ * AB and CD must share a point interior to both segments.
+ * returns TRUE if AB properly intersects CD.
+ */
+gint coord_intersect_prop(gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble cx, gdouble cy, gdouble dx, gdouble dy)
+{
+	gint wind_abc = coord_wind(ax, ay, bx, by, cx, cy);
+	gint wind_abd = coord_wind(ax, ay, bx, by, dx, dy);
+	gint wind_cda = coord_wind(cx, cy, dx, dy, ax, ay);
+	gint wind_cdb = coord_wind(cx, cy, dx, dy, bx, by);
+
+	if (!wind_abc || !wind_abd || !wind_cda || !wind_cdb)
+		return 0;
+
+	return (wind_abc ^ wind_abd) && (wind_cda ^ wind_cdb);
+}
+
+/* proper intersection:
+ * AB and CD must share a point interior to both segments.
+ * returns TRUE if AB properly intersects CD.
+ */
+int point_intersect_prop(GtsPoint * a, GtsPoint * b, GtsPoint * c, GtsPoint * d)
+{
+
+	if (point_wind(a, b, c) == 0 || point_wind(a, b, d) == 0 || point_wind(c, d, a) == 0 || point_wind(c, d, b) == 0)
+		return 0;
+
+	return (point_wind(a, b, c) ^ point_wind(a, b, d)) && (point_wind(c, d, a) ^ point_wind(c, d, b));
+}
+
+static inline int vertex_intersect_prop(GtsVertex * a, GtsVertex * b, GtsVertex * c, GtsVertex * d)
+{
+	return point_intersect_prop(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c), GTS_POINT(d));
+}
+
+static inline int
+tvertex_intersect_prop(toporouter_vertex_t * a, toporouter_vertex_t * b, toporouter_vertex_t * c, toporouter_vertex_t * d)
+{
+	return point_intersect_prop(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c), GTS_POINT(d));
+}
+
+/*
+static inline int
+tvertex_intersect(toporouter_vertex_t *a, toporouter_vertex_t *b, toporouter_vertex_t *c, toporouter_vertex_t *d) 
+{
+  if( !point_wind(GTS_POINT(a), GTS_POINT(d), GTS_POINT(b)) || !point_wind(GTS_POINT(a), GTS_POINT(c), GTS_POINT(b)) ) return 1;
+
+  return 
+    ( point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c)) ^ point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(d)) ) && 
+    ( point_wind(GTS_POINT(c), GTS_POINT(d), GTS_POINT(a)) ^ point_wind(GTS_POINT(c), GTS_POINT(d), GTS_POINT(b)) );
+}
+*/
+
+/* intersection vertex:
+ * AB and CD must share a point interior to both segments.
+ * returns vertex at intersection of AB and CD.
+ */
+GtsVertex *vertex_intersect(GtsVertex * a, GtsVertex * b, GtsVertex * c, GtsVertex * d)
+{
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+	GtsVertex *rval;
+	gdouble ua_top, ua_bot, ua, rx, ry;
+
+	/* TODO: this could be done more efficiently without duplicating computation */
+	if (!vertex_intersect_prop(a, b, c, d))
+		return NULL;
+
+	ua_top = ((d->p.x - c->p.x) * (a->p.y - c->p.y)) - ((d->p.y - c->p.y) * (a->p.x - c->p.x));
+	ua_bot = ((d->p.y - c->p.y) * (b->p.x - a->p.x)) - ((d->p.x - c->p.x) * (b->p.y - a->p.y));
+	ua = ua_top / ua_bot;
+	rx = a->p.x + (ua * (b->p.x - a->p.x));
+	ry = a->p.y + (ua * (b->p.y - a->p.y));
+
+	rval = gts_vertex_new(vertex_class, rx, ry, 0.0f);
+
+	return rval;
+}
+
+/* intersection vertex:
+ * AB and CD must share a point interior to both segments.
+ * returns vertex at intersection of AB and CD.
+ */
+void
+coord_intersect(gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble cx, gdouble cy, gdouble dx, gdouble dy, gdouble * rx,
+								gdouble * ry)
+{
+	gdouble ua_top, ua_bot, ua;
+
+	ua_top = ((dx - cx) * (ay - cy)) - ((dy - cy) * (ax - cx));
+	ua_bot = ((dy - cy) * (bx - ax)) - ((dx - cx) * (by - ay));
+	ua = ua_top / ua_bot;
+	*rx = ax + (ua * (bx - ax));
+	*ry = ay + (ua * (by - ay));
+
+}
+
+
+/*
+ * returns true if c is between a and b 
+ */
+int point_between(GtsPoint * a, GtsPoint * b, GtsPoint * c)
+{
+	if (point_wind(a, b, c) != 0)
+		return 0;
+
+	if (a->x != b->x) {
+		return ((a->x <= c->x) && (c->x <= b->x)) || ((a->x >= c->x) && (c->x >= b->x));
+	}
+	return ((a->y <= c->y) && (c->y <= b->y)) || ((a->y >= c->y) && (c->y >= b->y));
+}
+
+static inline int vertex_between(GtsVertex * a, GtsVertex * b, GtsVertex * c)
+{
+	return point_between(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c));
+}
+
+void delaunay_create_from_vertices(GList * vertices, GtsSurface ** surface, GtsTriangle ** t)
+{
+	GList *i = vertices;
+	GtsVertex *v1, *v2, *v3;
+	GSList *vertices_slist = NULL;
+
+	while (i) {
+		vertices_slist = g_slist_prepend(vertices_slist, i->data);
+		i = i->next;
+	}
+
+	/* TODO: just work this out from the board outline */
+	*t = gts_triangle_enclosing(gts_triangle_class(), vertices_slist, 100000.0f);
+	gts_triangle_vertices(*t, &v1, &v2, &v3);
+
+	*surface = gts_surface_new(gts_surface_class(), gts_face_class(),
+														 GTS_EDGE_CLASS(toporouter_edge_class()), GTS_VERTEX_CLASS(toporouter_vertex_class()));
+
+	gts_surface_add_face(*surface, gts_face_new(gts_face_class(), (*t)->e1, (*t)->e2, (*t)->e3));
+
+	i = vertices;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(gts_delaunay_add_vertex(*surface, (GtsVertex *) i->data, NULL));
+
+		if (v) {
+			printf("ERROR: vertex could not be added to CDT ");
+			print_vertex(v);
+		}
+
+		i = i->next;
+	}
+
+	gts_allow_floating_vertices = TRUE;
+	gts_object_destroy(GTS_OBJECT(v1));
+	gts_object_destroy(GTS_OBJECT(v2));
+	gts_object_destroy(GTS_OBJECT(v3));
+	gts_allow_floating_vertices = FALSE;
+
+	g_slist_free(vertices_slist);
+/*  return surface;*/
+}
+
+GSList *list_to_slist(GList * i)
+{
+	GSList *rval = NULL;
+	while (i) {
+		rval = g_slist_prepend(rval, i->data);
+		i = i->next;
+	}
+	return rval;
+}
+
+toporouter_bbox_t *toporouter_bbox_create_from_points(int layer, GList * vertices, toporouter_term_t type, gpointer data)
+{
+	toporouter_bbox_t *bbox;
+	GSList *vertices_slist = list_to_slist(vertices);
+
+/*  delaunay_create_from_vertices(vertices, &s, &t);*/
+	bbox = TOPOROUTER_BBOX(gts_bbox_points(GTS_BBOX_CLASS(toporouter_bbox_class()), vertices_slist));
+	bbox->type = type;
+	bbox->data = data;
+
+	bbox->surface = NULL;
+	bbox->enclosing = NULL;
+
+	bbox->layer = layer;
+
+	bbox->point = NULL;
+	bbox->realpoint = NULL;
+
+	g_slist_free(vertices_slist);
+	return bbox;
+}
+
+toporouter_bbox_t *toporouter_bbox_create(int layer, GList * vertices, toporouter_term_t type, gpointer data)
+{
+	toporouter_bbox_t *bbox;
+	GtsSurface *s;
+	GtsTriangle *t;
+
+	delaunay_create_from_vertices(vertices, &s, &t);
+	bbox = TOPOROUTER_BBOX(gts_bbox_surface(GTS_BBOX_CLASS(toporouter_bbox_class()), s));
+	bbox->type = type;
+	bbox->data = data;
+
+	bbox->surface = s;
+	bbox->enclosing = t;
+
+	bbox->layer = layer;
+
+	return bbox;
+}
+
+GtsVertex *insert_vertex(toporouter_t * r, toporouter_layer_t * l, gdouble x, gdouble y, toporouter_bbox_t * box)
+{
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+	GtsVertex *v;
+	GList *i;
+
+	i = l->vertices;
+	while (i) {
+		v = (GtsVertex *) i->data;
+		if (v->p.x == x && v->p.y == y) {
+			TOPOROUTER_VERTEX(v)->bbox = box;
+			return v;
+		}
+		i = i->next;
+	}
+
+	v = gts_vertex_new(vertex_class, x, y, l - r->layers);
+	TOPOROUTER_VERTEX(v)->bbox = box;
+	l->vertices = g_list_prepend(l->vertices, v);
+
+	return v;
+}
+
+GList *insert_constraint_edge(toporouter_t * r, toporouter_layer_t * l, gdouble x1, gdouble y1, guint flags1,
+															gdouble x2, gdouble y2, guint flags2, toporouter_bbox_t * box)
+{
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+	GtsEdgeClass *edge_class = GTS_EDGE_CLASS(toporouter_constraint_class());
+	GtsVertex *p[2];
+	GtsVertex *v;
+	GList *i;
+	GtsEdge *e;
+
+	p[0] = p[1] = NULL;
+
+	/* insert or find points */
+
+	i = l->vertices;
+	while (i) {
+		v = (GtsVertex *) i->data;
+		if (v->p.x == x1 && v->p.y == y1)
+			p[0] = v;
+		if (v->p.x == x2 && v->p.y == y2)
+			p[1] = v;
+		i = i->next;
+	}
+
+	if (p[0] == NULL) {
+		p[0] = gts_vertex_new(vertex_class, x1, y1, l - r->layers);
+		TOPOROUTER_VERTEX(p[0])->bbox = box;
+		l->vertices = g_list_prepend(l->vertices, p[0]);
+	}
+	if (p[1] == NULL) {
+		p[1] = gts_vertex_new(vertex_class, x2, y2, l - r->layers);
+		TOPOROUTER_VERTEX(p[1])->bbox = box;
+		l->vertices = g_list_prepend(l->vertices, p[1]);
+	}
+
+	TOPOROUTER_VERTEX(p[0])->flags = flags1;
+	TOPOROUTER_VERTEX(p[1])->flags = flags2;
+
+	e = gts_edge_new(edge_class, p[0], p[1]);
+	TOPOROUTER_CONSTRAINT(e)->box = box;
+	l->constraints = g_list_prepend(l->constraints, e);
+/*  return insert_constraint_edge_rec(r, l, p, box);*/
+	return g_list_prepend(NULL, e);
+
+}
+
+void insert_constraints_from_list(toporouter_t * r, toporouter_layer_t * l, GList * vlist, toporouter_bbox_t * box)
+{
+	GList *i = vlist;
+	toporouter_vertex_t *pv = NULL, *v;
+
+	while (i) {
+		v = TOPOROUTER_VERTEX(i->data);
+
+		if (pv) {
+			box->constraints =
+				g_list_concat(box->constraints, insert_constraint_edge(r, l, vx(v), vy(v), v->flags, vx(pv), vy(pv), pv->flags, box));
+		}
+
+		pv = v;
+		i = i->next;
+	}
+
+	v = TOPOROUTER_VERTEX(vlist->data);
+	box->constraints =
+		g_list_concat(box->constraints, insert_constraint_edge(r, l, vx(v), vy(v), v->flags, vx(pv), vy(pv), pv->flags, box));
+
+}
+
+void insert_centre_point(toporouter_t * r, toporouter_layer_t * l, gdouble x, gdouble y)
+{
+	GList *i;
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+
+	i = l->vertices;
+	while (i) {
+		GtsPoint *p = GTS_POINT(i->data);
+		if (p->x == x && p->y == y)
+			return;
+		i = i->next;
+	}
+
+	l->vertices = g_list_prepend(l->vertices, gts_vertex_new(vertex_class, x, y, 0.0f));
+}
+
+GtsPoint *midpoint(GtsPoint * a, GtsPoint * b)
+{
+	return gts_point_new(gts_point_class(), (a->x + b->x) / 2., (a->y + b->y) / 2., 0.);
+}
+
+static inline gdouble pad_rad(PadType * pad)
+{
+	return (lookup_thickness(pad->Name) / 2.) + lookup_clearance(pad->Name);
+}
+
+static inline gdouble pin_rad(PinType * pin)
+{
+	return (lookup_thickness(pin->Name) / 2.) + lookup_clearance(pin->Name);
+}
+
+GList *rect_with_attachments(gdouble rad,
+														 gdouble x0, gdouble y0,
+														 gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble x3, gdouble y3, gdouble z)
+{
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+	GList *r = NULL, *rr = NULL, *i;
+	toporouter_vertex_t *curpoint, *temppoint;
+
+
+	curpoint = TOPOROUTER_VERTEX(gts_vertex_new(vertex_class, x0, y0, z));
+
+	r = g_list_prepend(NULL, curpoint);
+	r = g_list_prepend(r, gts_vertex_new(vertex_class, x1, y1, z));
+	r = g_list_prepend(r, gts_vertex_new(vertex_class, x2, y2, z));
+	r = g_list_prepend(r, gts_vertex_new(vertex_class, x3, y3, z));
+
+	i = r;
+	while (i) {
+		temppoint = TOPOROUTER_VERTEX(i->data);
+		rr = g_list_prepend(rr, curpoint);
+
+		curpoint = temppoint;
+		i = i->next;
+	}
+
+	g_list_free(r);
+
+	return rr;
+}
+
+#define VERTEX_CENTRE(x) TOPOROUTER_VERTEX( vertex_bbox(x)->point )
+
+/*
+ * Read pad data from layer into toporouter_layer_t struct
+ *
+ * Inserts points and constraints into GLists
+ */
+int read_pads(toporouter_t * r, toporouter_layer_t * l, guint layer)
+{
+	toporouter_spoint_t p[2], rv[5];
+	gdouble x[2], y[2], t, m;
+
+	GList *vlist = NULL;
+	toporouter_bbox_t *bbox = NULL;
+
+	guint front = GetLayerGroupNumberByNumber(component_silk_layer);
+	guint back = GetLayerGroupNumberByNumber(solder_silk_layer);
+
+/*  printf("read_pads: front = %d back = %d layer = %d\n", 
+       front, back, layer);*/
+
+	/* If its not the top or bottom layer, there are no pads to read */
+	if (l - r->layers != front && l - r->layers != back)
+		return 0;
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PAD_LOOP(element);
+		{
+			if ((l - r->layers == back && TEST_FLAG(PCB_FLAG_ONSOLDER, pad)) || (l - r->layers == front && !TEST_FLAG(PCB_FLAG_ONSOLDER, pad))) {
+
+				t = (gdouble) pad->Thickness / 2.0f;
+				x[0] = pad->Point1.X;
+				x[1] = pad->Point2.X;
+				y[0] = pad->Point1.Y;
+				y[1] = pad->Point2.Y;
+
+
+				if (TEST_FLAG(PCB_FLAG_SQUARE, pad)) {
+					/* Square or oblong pad. Four points and four constraint edges are
+					 * used */
+
+					if (x[0] == x[1] && y[0] == y[1]) {
+						/* Pad is square */
+
+/*            vlist = g_list_prepend(NULL, gts_vertex_new (vertex_class, x[0]-t, y[0]-t, 0.));
+              vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, x[0]-t, y[0]+t, 0.));
+              vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, x[0]+t, y[0]+t, 0.));
+              vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, x[0]+t, y[0]-t, 0.)); */
+						vlist = rect_with_attachments(pad_rad(pad),
+																					x[0] - t, y[0] - t,
+																					x[0] - t, y[0] + t, x[0] + t, y[0] + t, x[0] + t, y[0] - t, l - r->layers);
+						bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad);
+						r->bboxes = g_slist_prepend(r->bboxes, bbox);
+						insert_constraints_from_list(r, l, vlist, bbox);
+						g_list_free(vlist);
+
+						/*bbox->point = GTS_POINT( gts_vertex_new(vertex_class, x[0], y[0], 0.) ); */
+						bbox->point = GTS_POINT(insert_vertex(r, l, x[0], y[0], bbox));
+						g_assert(TOPOROUTER_VERTEX(bbox->point)->bbox == bbox);
+					}
+					else {
+						/* Pad is diagonal oblong or othogonal oblong */
+
+						m = cartesian_gradient(x[0], y[0], x[1], y[1]);
+
+						p[0].x = x[0];
+						p[0].y = y[0];
+						p[1].x = x[1];
+						p[1].y = y[1];
+
+						vertex_outside_segment(&p[0], &p[1], t, &rv[0]);
+						vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[1], &rv[2]);
+
+						vertex_outside_segment(&p[1], &p[0], t, &rv[0]);
+						vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[3], &rv[4]);
+
+						if (wind(&rv[1], &rv[2], &rv[3]) != wind(&rv[2], &rv[3], &rv[4])) {
+							rv[0].x = rv[3].x;
+							rv[0].y = rv[3].y;
+							rv[3].x = rv[4].x;
+							rv[3].y = rv[4].y;
+							rv[4].x = rv[0].x;
+							rv[4].y = rv[0].y;
+						}
+
+/*            vlist = g_list_prepend(NULL,  gts_vertex_new (vertex_class, rv[1].x, rv[1].y, 0.));
+              vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, rv[2].x, rv[2].y, 0.));
+              vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, rv[3].x, rv[3].y, 0.));
+              vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, rv[4].x, rv[4].y, 0.));*/
+						vlist = rect_with_attachments(pad_rad(pad),
+																					rv[1].x, rv[1].y,
+																					rv[2].x, rv[2].y, rv[3].x, rv[3].y, rv[4].x, rv[4].y, l - r->layers);
+						bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad);
+						r->bboxes = g_slist_prepend(r->bboxes, bbox);
+						insert_constraints_from_list(r, l, vlist, bbox);
+						g_list_free(vlist);
+
+						/*bbox->point = GTS_POINT( gts_vertex_new(vertex_class, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., 0.) ); */
+						bbox->point = GTS_POINT(insert_vertex(r, l, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., bbox));
+						g_assert(TOPOROUTER_VERTEX(bbox->point)->bbox == bbox);
+
+					}
+
+				}
+				else {
+					/* Either round pad or pad with curved edges */
+
+					if (x[0] == x[1] && y[0] == y[1]) {
+						/* One point */
+
+						/* bounding box same as square pad */
+						vlist = rect_with_attachments(pad_rad(pad),
+																					x[0] - t, y[0] - t,
+																					x[0] - t, y[0] + t, x[0] + t, y[0] + t, x[0] + t, y[0] - t, l - r->layers);
+						bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad);
+						r->bboxes = g_slist_prepend(r->bboxes, bbox);
+						g_list_free(vlist);
+
+						/*bbox->point = GTS_POINT( insert_vertex(r, l, x[0], y[0], bbox) ); */
+						bbox->point = GTS_POINT(insert_vertex(r, l, x[0], y[0], bbox));
+
+					}
+					else {
+						/* Two points and one constraint edge */
+
+						/* the rest is just for bounding box */
+						m = cartesian_gradient(x[0], y[0], x[1], y[1]);
+
+						p[0].x = x[0];
+						p[0].y = y[0];
+						p[1].x = x[1];
+						p[1].y = y[1];
+
+						vertex_outside_segment(&p[0], &p[1], t, &rv[0]);
+						vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[1], &rv[2]);
+
+						vertex_outside_segment(&p[1], &p[0], t, &rv[0]);
+						vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[3], &rv[4]);
+
+						if (wind(&rv[1], &rv[2], &rv[3]) != wind(&rv[2], &rv[3], &rv[4])) {
+							rv[0].x = rv[3].x;
+							rv[0].y = rv[3].y;
+							rv[3].x = rv[4].x;
+							rv[3].y = rv[4].y;
+							rv[4].x = rv[0].x;
+							rv[4].y = rv[0].y;
+						}
+
+						vlist = rect_with_attachments(pad_rad(pad),
+																					rv[1].x, rv[1].y,
+																					rv[2].x, rv[2].y, rv[3].x, rv[3].y, rv[4].x, rv[4].y, l - r->layers);
+						bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad);
+						r->bboxes = g_slist_prepend(r->bboxes, bbox);
+						insert_constraints_from_list(r, l, vlist, bbox);
+						g_list_free(vlist);
+
+						/*bbox->point = GTS_POINT( gts_vertex_new(vertex_class, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., 0.) ); */
+						bbox->point = GTS_POINT(insert_vertex(r, l, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., bbox));
+
+						/*bbox->constraints = g_list_concat(bbox->constraints, insert_constraint_edge(r, l, x[0], y[0], x[1], y[1], bbox)); */
+
+					}
+
+
+				}
+
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	return 0;
+}
+
+/*
+ * Read points data (all layers) into GList
+ *
+ * Inserts pin and via points
+ */
+int read_points(toporouter_t * r, toporouter_layer_t * l, int layer)
+{
+	gdouble x, y, t;
+
+	GList *vlist = NULL;
+	toporouter_bbox_t *bbox = NULL;
+
+	ELEMENT_LOOP(PCB->Data);
+	{
+		PIN_LOOP(element);
+		{
+
+			t = (gdouble) pin->Thickness / 2.0f;
+			x = pin->X;
+			y = pin->Y;
+
+			if (TEST_FLAG(PCB_FLAG_SQUARE, pin)) {
+
+				vlist = rect_with_attachments(pin_rad(pin), x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers);
+				bbox = toporouter_bbox_create(l - r->layers, vlist, PIN, pin);
+				r->bboxes = g_slist_prepend(r->bboxes, bbox);
+				insert_constraints_from_list(r, l, vlist, bbox);
+				g_list_free(vlist);
+				bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox));
+
+			}
+			else if (TEST_FLAG(PCB_FLAG_OCTAGON, pin)) {
+				/* TODO: Handle octagon pins */
+				fprintf(stderr, "No support for octagon pins yet\n");
+			}
+			else {
+				vlist = rect_with_attachments(pin_rad(pin), x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers);
+				bbox = toporouter_bbox_create(l - r->layers, vlist, PIN, pin);
+				r->bboxes = g_slist_prepend(r->bboxes, bbox);
+				g_list_free(vlist);
+				bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox));
+			}
+		}
+		END_LOOP;
+	}
+	END_LOOP;
+
+	VIA_LOOP(PCB->Data);
+	{
+
+		t = (gdouble) via->Thickness / 2.0f;
+		x = via->X;
+		y = via->Y;
+
+		if (TEST_FLAG(PCB_FLAG_SQUARE, via)) {
+
+			vlist = rect_with_attachments(pin_rad((PinType *) via),
+																		x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers);
+			bbox = toporouter_bbox_create(l - r->layers, vlist, VIA, via);
+			r->bboxes = g_slist_prepend(r->bboxes, bbox);
+			insert_constraints_from_list(r, l, vlist, bbox);
+			g_list_free(vlist);
+			bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox));
+
+		}
+		else if (TEST_FLAG(PCB_FLAG_OCTAGON, via)) {
+			/* TODO: Handle octagon vias */
+			fprintf(stderr, "No support for octagon vias yet\n");
+		}
+		else {
+
+			vlist = rect_with_attachments(pin_rad((PinType *) via),
+																		x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers);
+			bbox = toporouter_bbox_create(l - r->layers, vlist, VIA, via);
+			r->bboxes = g_slist_prepend(r->bboxes, bbox);
+			g_list_free(vlist);
+
+			bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox));
+
+		}
+	}
+	END_LOOP;
+	return 0;
+}
+
+/*
+ * Read line data from layer into toporouter_layer_t struct
+ *
+ * Inserts points and constraints into GLists
+ */
+int read_lines(toporouter_t * r, toporouter_layer_t * l, LayerType * layer, int ln)
+{
+	gdouble xs[2], ys[2];
+
+	GList *vlist = NULL;
+	toporouter_bbox_t *bbox = NULL;
+
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+
+	LINE_LOOP(layer);
+	{
+		xs[0] = line->Point1.X;
+		xs[1] = line->Point2.X;
+		ys[0] = line->Point1.Y;
+		ys[1] = line->Point2.Y;
+		if (!(xs[0] == xs[1] && ys[0] == ys[1])) {
+			vlist = g_list_prepend(NULL, gts_vertex_new(vertex_class, xs[0], ys[0], l - r->layers));
+			vlist = g_list_prepend(vlist, gts_vertex_new(vertex_class, xs[1], ys[1], l - r->layers));
+			/* TODO: replace this with surface version */
+			bbox = toporouter_bbox_create_from_points(GetLayerGroupNumberByNumber(ln), vlist, LINE, line);
+			r->bboxes = g_slist_prepend(r->bboxes, bbox);
+			/*new;;
+			   //insert_constraints_from_list(r, l, vlist, bbox); */
+			g_list_free(vlist);
+/*      bbox->point = GTS_POINT( insert_vertex(r, l, (xs[0]+xs[1])/2., (ys[0]+ys[1])/2., bbox) );*/
+
+			bbox->constraints =
+				g_list_concat(bbox->constraints, insert_constraint_edge(r, l, xs[0], ys[0], 0, xs[1], ys[1], 0, bbox));
+		}
+	}
+	END_LOOP;
+
+	return 0;
+}
+
+void create_board_edge(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble max, gint layer, GList ** vlist)
+{
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+	gdouble d = sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2));
+	guint n = d / max, count = 1;
+	gdouble inc = n ? d / n : d;
+	gdouble x = x0, y = y0;
+
+	*vlist = g_list_prepend(*vlist, gts_vertex_new(vertex_class, x0, y0, layer));
+
+	while (count < n) {
+		coord_move_towards_coord_values(x0, y0, x1, y1, inc, &x, &y);
+		*vlist = g_list_prepend(*vlist, gts_vertex_new(vertex_class, x, y, layer));
+
+		x0 = x;
+		y0 = y;
+		count++;
+	}
+
+}
+
+
+int read_board_constraints(toporouter_t * r, toporouter_layer_t * l, int layer)
+{
+/*  GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());*/
+	GList *vlist = NULL;
+	toporouter_bbox_t *bbox = NULL;
+
+	/* Add points for corners of board, and constrain those edges */
+/*  vlist = g_list_prepend(NULL, gts_vertex_new (vertex_class, 0., 0., layer));
+    vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, PCB->MaxWidth, 0., layer));
+    vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, PCB->MaxWidth, PCB->MaxHeight, layer));
+    vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, 0., PCB->MaxHeight, layer));*/
+
+	create_board_edge(0., 0., PCB->MaxWidth, 0., 10000., layer, &vlist);
+	create_board_edge(PCB->MaxWidth, 0., PCB->MaxWidth, PCB->MaxHeight, 10000., layer, &vlist);
+	create_board_edge(PCB->MaxWidth, PCB->MaxHeight, 0., PCB->MaxHeight, 10000., layer, &vlist);
+	create_board_edge(0., PCB->MaxHeight, 0., 0., 10000., layer, &vlist);
+
+	bbox = toporouter_bbox_create(layer, vlist, BOARD, NULL);
+	r->bboxes = g_slist_prepend(r->bboxes, bbox);
+	insert_constraints_from_list(r, l, vlist, bbox);
+	g_list_free(vlist);
+
+	return 0;
+}
+
+gdouble triangle_cost(GtsTriangle * t, gpointer * data)
+{
+
+	gdouble *min_quality = (gdouble *) data[0];
+	gdouble *max_area = (gdouble *) data[1];
+	gdouble quality = gts_triangle_quality(t);
+	gdouble area = gts_triangle_area(t);
+
+	if (quality < *min_quality || area > *max_area)
+		return quality;
+	return 0.0;
+}
+
+
+void print_constraint(toporouter_constraint_t * e)
+{
+	printf("CONSTRAINT:\n");
+	print_vertex(tedge_v1(e));
+	print_vertex(tedge_v2(e));
+}
+
+void print_edge(toporouter_edge_t * e)
+{
+	GList *i = edge_routing(e);
+
+	printf("EDGE:\n");
+
+	print_vertex(tedge_v1(e));
+
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+		print_vertex(v);
+		i = i->next;
+	}
+
+	print_vertex(tedge_v2(e));
+}
+
+static void pick_first_face(GtsFace * f, GtsFace ** first)
+{
+	if (*first == NULL)
+		*first = f;
+}
+
+void unconstrain(toporouter_layer_t * l, toporouter_constraint_t * c)
+{
+	toporouter_edge_t *e;
+
+	gts_allow_floating_vertices = TRUE;
+	e = TOPOROUTER_EDGE(gts_edge_new(GTS_EDGE_CLASS(toporouter_edge_class()), GTS_SEGMENT(c)->v1, GTS_SEGMENT(c)->v2));
+	gts_edge_replace(GTS_EDGE(c), GTS_EDGE(e));
+	l->constraints = g_list_remove(l->constraints, c);
+	c->box->constraints = g_list_remove(c->box->constraints, c);
+	c->box = NULL;
+	gts_object_destroy(GTS_OBJECT(c));
+	gts_allow_floating_vertices = FALSE;
+}
+
+void build_cdt(toporouter_t * r, toporouter_layer_t * l)
+{
+	/* TODO: generalize into surface *cdt_create(vertices, constraints) */
+	GList *i;
+	/*GtsEdge *temp;
+	   GtsVertex *v; */
+	GtsTriangle *t;
+	GtsVertex *v1, *v2, *v3;
+	GSList *vertices_slist;
+
+	vertices_slist = list_to_slist(l->vertices);
+
+	if (l->surface) {
+		GtsFace *first = NULL;
+		gts_surface_foreach_face(l->surface, (GtsFunc) pick_first_face, &first);
+		gts_surface_traverse_destroy(gts_surface_traverse_new(l->surface, first));
+	}
+
+	t = gts_triangle_enclosing(gts_triangle_class(), vertices_slist, 1000.0f);
+	gts_triangle_vertices(t, &v1, &v2, &v3);
+
+	g_slist_free(vertices_slist);
+
+	l->surface = gts_surface_new(gts_surface_class(), gts_face_class(),
+															 GTS_EDGE_CLASS(toporouter_edge_class()), GTS_VERTEX_CLASS(toporouter_vertex_class()));
+
+	gts_surface_add_face(l->surface, gts_face_new(gts_face_class(), t->e1, t->e2, t->e3));
+
+
+/*  fprintf(stderr, "ADDED VERTICES\n");*/
+/*
+  GtsFace *debugface;
+
+  if((debugface = gts_delaunay_check(l->surface))) {
+    fprintf(stderr, "WARNING: Delaunay check failed\n");
+    fprintf(stderr, "\tViolating triangle:\n");
+    fprintf(stderr, "\t%f,%f %f,%f\n",
+        debugface->triangle.e1->segment.v1->p.x,
+        debugface->triangle.e1->segment.v1->p.y,
+        debugface->triangle.e1->segment.v2->p.x,
+        debugface->triangle.e1->segment.v2->p.y
+        );
+    fprintf(stderr, "\t%f,%f %f,%f\n",
+        debugface->triangle.e2->segment.v1->p.x,
+        debugface->triangle.e2->segment.v1->p.y,
+        debugface->triangle.e2->segment.v2->p.x,
+        debugface->triangle.e2->segment.v2->p.y
+        );
+    fprintf(stderr, "\t%f,%f %f,%f\n",
+        debugface->triangle.e3->segment.v1->p.x,
+        debugface->triangle.e3->segment.v1->p.y,
+        debugface->triangle.e3->segment.v2->p.x,
+        debugface->triangle.e3->segment.v2->p.y
+        );
+/*    toporouter_draw_surface(r, l->surface, "debug.png", 4096, 4096); */
+	{
+		int i;
+		for (i = 0; i < groupcount(); i++) {
+			char buffer[256];
+			sprintf(buffer, "debug-%d.png", i);
+			toporouter_draw_surface(r, r->layers[i].surface, buffer, 2048, 2048, 2, NULL, i, NULL);
+		}
+	}
+
+/*  }*/
+
+check_cons_continuation:
+	i = l->constraints;
+	while (i) {
+		toporouter_constraint_t *c1 = TOPOROUTER_CONSTRAINT(i->data);
+		GList *j = i->next;
+		/* printf("adding cons: "); print_constraint(c1); */
+
+		while (j) {
+			toporouter_constraint_t *c2 = TOPOROUTER_CONSTRAINT(j->data);
+			guint rem = 0;
+			GList *temp;
+
+			/*  printf("\tconflict: "); print_constraint(c2); */
+			toporouter_bbox_t *c1box = c1->box, *c2box = c2->box;
+			toporouter_vertex_t *c1v1 = tedge_v1(c1);
+			toporouter_vertex_t *c1v2 = tedge_v2(c1);
+			toporouter_vertex_t *c2v1 = tedge_v1(c2);
+			toporouter_vertex_t *c2v2 = tedge_v2(c2);
+
+			if (gts_segments_are_intersecting(GTS_SEGMENT(c1), GTS_SEGMENT(c2)) == GTS_IN) {
+				toporouter_vertex_t *v;
+				unconstrain(l, c1);
+				unconstrain(l, c2);
+				rem = 1;
+				/* proper intersection */
+				v = TOPOROUTER_VERTEX(vertex_intersect(GTS_VERTEX(c1v1), GTS_VERTEX(c1v2), GTS_VERTEX(c2v1), GTS_VERTEX(c2v2)));
+
+				/* remove both constraints
+				   replace with 4x constraints
+				   insert new intersection vertex */
+				GTS_POINT(v)->z = vz(c1v1);
+
+				l->vertices = g_list_prepend(l->vertices, v);
+/*        gts_delaunay_add_vertex (l->surface, GTS_VERTEX(v), NULL); */
+
+				v->bbox = c1box;
+
+				temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(v), vy(v), 0, c1box);
+				c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+				temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(v), vy(v), 0, c1box);
+				c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+				temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(v), vy(v), 0, c2box);
+				c2box->constraints = g_list_concat(c2box->constraints, temp);
+
+				temp = insert_constraint_edge(r, l, vx(c2v2), vy(c2v2), 0, vx(v), vy(v), 0, c2box);
+				c2box->constraints = g_list_concat(c2box->constraints, temp);
+
+			}
+			else if (gts_segments_are_intersecting(GTS_SEGMENT(c1), GTS_SEGMENT(c2)) == GTS_ON ||
+							 gts_segments_are_intersecting(GTS_SEGMENT(c2), GTS_SEGMENT(c1)) == GTS_ON) {
+
+				if (vertex_between(edge_v1(c2), edge_v2(c2), edge_v1(c1)) && vertex_between(edge_v1(c2), edge_v2(c2), edge_v2(c1))) {
+					unconstrain(l, c1);
+					unconstrain(l, c2);
+					rem = 1;
+					/* remove c1 */
+					temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c2v2), vy(c2v2), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+
+				}
+				else if (vertex_between(edge_v1(c1), edge_v2(c1), edge_v1(c2)) && vertex_between(edge_v1(c1), edge_v2(c1), edge_v2(c2))) {
+					unconstrain(l, c1);
+					unconstrain(l, c2);
+					rem = 1;
+					/* remove c2 */
+					temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+					/*}else if(!vertex_wind(edge_v1(c1), edge_v2(c1), edge_v1(c2)) && !vertex_wind(edge_v1(c1), edge_v2(c1), edge_v2(c2))) { */
+					/*     }else if(vertex_between(edge_v1(c1), edge_v2(c1), edge_v1(c2)) || vertex_between(edge_v1(c1), edge_v2(c1), edge_v2(c2))) {
+					   unconstrain(l, c1); unconstrain(l, c2); 
+					   rem = 1;
+					   printf("all colinear\n");
+					   //   exit(1);
+					   temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box);
+					   c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+					   if(vertex_between(GTS_VERTEX(c1v1), GTS_VERTEX(c1v2), GTS_VERTEX(c2v2))) {
+					   // v2 of c2 is inner
+					   if(vertex_between(GTS_VERTEX(c2v1), GTS_VERTEX(c2v2), GTS_VERTEX(c1v2))) {
+					   // v2 of c1 is inner
+					   // c2 = c1.v2 -> c2.v1
+					   temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v1), vy(c2v1), 0, c2box);
+					   c2box->constraints = g_list_concat(c2box->constraints, temp);
+					   }else{
+					   // v1 of c1 is inner
+					   // c2 = c1.v1 -> c2.v1
+					   temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v1), vy(c2v1), 0, c2box);
+					   c2box->constraints = g_list_concat(c2box->constraints, temp);
+					   }
+					   }else{
+					   // v1 of c2 is inner
+					   if(vertex_between(GTS_VERTEX(c2v1), GTS_VERTEX(c2v2), GTS_VERTEX(c1v2))) {
+					   // v2 of c1 is inner
+					   // c2 = c1.v2 -> c2.v2
+					   temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v2), vy(c2v2), 0, c2box);
+					   c2box->constraints = g_list_concat(c2box->constraints, temp);
+					   }else{
+					   // v1 of c1 is inner
+					   // c2 = c1.v1 -> c2.v2
+					   temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v2), vy(c2v2), 0, c2box);
+					   c2box->constraints = g_list_concat(c2box->constraints, temp);
+					   }
+					   } */
+				}
+				else if (vertex_between(edge_v1(c2), edge_v2(c2), edge_v1(c1)) && c1v1 != c2v1 && c1v1 != c2v2) {
+					unconstrain(l, c1);
+					unconstrain(l, c2);
+					rem = 1;
+					/*v1 of c1 is on c2 */
+					printf("v1 of c1 on c2\n");
+
+					/* replace with 2x constraints */
+					temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c1v1), vy(c1v1), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+					temp = insert_constraint_edge(r, l, vx(c2v2), vy(c2v2), 0, vx(c1v1), vy(c1v1), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+
+					temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+					/* restore c1
+					   temp = insert_constraint_edge(r, l, vx(tedge_v2(c1)), vy(tedge_v2(c1)), 0, vx(tedge_v1(c1)), vy(tedge_v1(c1)), 0, c1->box);
+					   c2->box->constraints = g_list_concat(c2->box->constraints, temp); */
+
+				}
+				else if (vertex_between(edge_v1(c2), edge_v2(c2), edge_v2(c1)) && c1v2 != c2v1 && c1v2 != c2v2) {
+					unconstrain(l, c1);
+					unconstrain(l, c2);
+					rem = 1;
+					/*v2 of c1 is on c2 */
+					printf("v2 of c1 on c2\n");
+
+					/* replace with 2x constraints */
+					temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c1v2), vy(c1v2), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+					temp = insert_constraint_edge(r, l, vx(c2v2), vy(c2v2), 0, vx(c1v2), vy(c1v2), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+
+					temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+				}
+				else if (vertex_between(edge_v1(c1), edge_v2(c1), edge_v1(c2)) && c2v1 != c1v1 && c2v1 != c1v2) {
+					unconstrain(l, c1);
+					unconstrain(l, c2);
+					rem = 1;
+					/*v1 of c2 is on c1 */
+					printf("v1 of c2 on c1\n");
+
+					/* replace with 2x constraints */
+					temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v1), vy(c2v1), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+					temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v1), vy(c2v1), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+					temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c2v2), vy(c2v2), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+				}
+				else if (vertex_between(edge_v1(c1), edge_v2(c1), edge_v2(c2)) && c2v2 != c1v1 && c2v2 != c1v2) {
+					unconstrain(l, c1);
+					unconstrain(l, c2);
+					rem = 1;
+					/*v2 of c2 is on c1 */
+					printf("v2 of c2 on c1\n");
+
+					/* replace with 2x constraints */
+					temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v2), vy(c2v2), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+					temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v2), vy(c2v2), 0, c1box);
+					c1box->constraints = g_list_concat(c1box->constraints, temp);
+
+					temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c2v2), vy(c2v2), 0, c2box);
+					c2box->constraints = g_list_concat(c2box->constraints, temp);
+				}
+			}
+			if (rem)
+				goto check_cons_continuation;
+
+			j = j->next;
+		}
+
+		i = i->next;
+	}
+
+	i = l->vertices;
+	while (i) {
+		/*v = i->data;
+		   if(r->flags & TOPOROUTER_FLAG_DEBUG_CDTS) 
+		   fprintf(stderr, "\tadding vertex %f,%f\n", v->p.x, v->p.y); */
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(gts_delaunay_add_vertex(l->surface, (GtsVertex *) i->data, NULL));
+		if (v) {
+			printf("conflict: ");
+			print_vertex(v);
+		}
+
+		i = i->next;
+	}
+	i = l->constraints;
+	while (i) {
+
+		/* toporouter_constraint_t *c1 = TOPOROUTER_CONSTRAINT(i->data);
+		   printf("adding cons: "); print_constraint(c1); */
+
+		GSList *conflicts = gts_delaunay_add_constraint(l->surface, (GtsConstraint *) i->data);
+		GSList *j = conflicts;
+		while (j) {
+			if (TOPOROUTER_IS_CONSTRAINT(j->data)) {
+				toporouter_constraint_t *c2 = TOPOROUTER_CONSTRAINT(j->data);
+
+				printf("\tconflict: ");
+				print_constraint(c2);
+
+			}
+			j = j->next;
+		}
+		g_slist_free(conflicts);
+
+		i = i->next;
+	}
+
+/*  if(rerun) 
+          goto build_cdt_continuation;
+    fprintf(stderr, "ADDED CONSTRAINTS\n");*/
+	gts_allow_floating_vertices = TRUE;
+	gts_object_destroy(GTS_OBJECT(v1));
+	gts_object_destroy(GTS_OBJECT(v2));
+	gts_object_destroy(GTS_OBJECT(v3));
+	gts_allow_floating_vertices = FALSE;
+
+/*
+  {
+    gpointer data[2];
+    gdouble quality = 0.50, area = G_MAXDOUBLE;
+    guint num = gts_delaunay_conform(l->surface, -1, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL);
+
+    if (num == 0){
+      data[0] = &quality;
+      data[1] = &area;
+      num = gts_delaunay_refine(l->surface, -1, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL, (GtsKeyFunc) triangle_cost, data);
+    }
+  }
+*/
+#ifdef DEBUG_IMPORT
+	gts_surface_print_stats(l->surface, stderr);
+#endif
+
+#if 0
+	{
+		char buffer[64];
+		FILE *fout2;
+		sprintf(buffer, "surface%d.gts", l - r->layers);
+		fout2 = fopen(buffer, "w");
+		gts_surface_write(l->surface, fout2);
+	}
+#endif
+
+}
+
+gint visited_cmp(gconstpointer a, gconstpointer b)
+{
+	if (a < b)
+		return -1;
+	if (a > b)
+		return 1;
+	return 0;
+}
+
+gdouble coord_xangle(gdouble ax, gdouble ay, gdouble bx, gdouble by)
+{
+	gdouble dx, dy, theta;
+
+	dx = fabs(ax - bx);
+	dy = fabs(ay - by);
+
+	if (dx < EPSILON) {
+		theta = M_PI / 2.;
+	}
+	else
+		theta = atan(dy / dx);
+
+	if (by <= ay) {
+		if (bx < ax)
+			theta = M_PI - theta;
+	}
+	else {
+		if (bx < ax)
+			theta += M_PI;
+		else
+			theta = (2 * M_PI) - theta;
+	}
+
+	return theta;
+}
+
+gdouble point_xangle(GtsPoint * a, GtsPoint * b)
+{
+	gdouble dx, dy, theta;
+
+	dx = fabs(a->x - b->x);
+	dy = fabs(a->y - b->y);
+
+	if (dx < EPSILON) {
+		theta = M_PI / 2.;
+	}
+	else
+		theta = atan(dy / dx);
+
+	if (b->y >= a->y) {
+		if (b->x < a->x)
+			theta = M_PI - theta;
+	}
+	else {
+		if (b->x < a->x)
+			theta += M_PI;
+		else
+			theta = (2 * M_PI) - theta;
+	}
+
+	return theta;
+}
+
+
+GList *cluster_vertices(toporouter_t * r, toporouter_cluster_t * c)
+{
+	GList *rval = NULL;
+
+	if (!c)
+		return NULL;
+
+	FOREACH_CLUSTER(c->netlist->clusters) {
+		if ((r->flags & TOPOROUTER_FLAG_AFTERRUBIX && cluster->c == c->c)
+				|| (!(r->flags & TOPOROUTER_FLAG_AFTERRUBIX) && cluster == c)) {
+			FOREACH_BBOX(cluster->boxes) {
+				if (box->type == LINE) {
+					g_assert(box->constraints->data);
+					rval = g_list_prepend(rval, tedge_v1(box->constraints->data));
+					rval = g_list_prepend(rval, tedge_v2(box->constraints->data));
+				}
+				else if (box->point) {
+					rval = g_list_prepend(rval, TOPOROUTER_VERTEX(box->point));
+					/*g_assert(vertex_bbox(TOPOROUTER_VERTEX(box->point)) == box); */
+				}
+				else {
+					printf("WARNING: cluster_vertices: unhandled bbox type\n");
+				}
+
+			}
+			FOREACH_END;
+
+
+		}
+
+	}
+	FOREACH_END;
+
+	return rval;
+}
+
+void print_cluster(toporouter_cluster_t * c)
+{
+
+	if (!c) {
+		printf("[CLUSTER (NULL)]\n");
+		return;
+	}
+
+	printf("CLUSTER %d: NETLIST = %s STYLE = %s\n", c->c, c->netlist->netlist, c->netlist->style);
+
+	FOREACH_BBOX(c->boxes) {
+		print_bbox(box);
+	}
+	FOREACH_END;
+}
+
+
+toporouter_cluster_t *cluster_create(toporouter_t * r, toporouter_netlist_t * netlist)
+{
+	toporouter_cluster_t *c = (toporouter_cluster_t *) malloc(sizeof(toporouter_cluster_t));
+
+	c->c = c->pc = netlist->clusters->len;
+	g_ptr_array_add(netlist->clusters, c);
+	c->netlist = netlist;
+	c->boxes = g_ptr_array_new();
+
+	return c;
+}
+
+toporouter_bbox_t *toporouter_bbox_locate(toporouter_t * r, toporouter_term_t type, void *data, gdouble x, gdouble y,
+																					guint layergroup)
+{
+	GtsPoint *p = gts_point_new(gts_point_class(), x, y, layergroup);
+	GSList *boxes = gts_bb_tree_stabbed(r->bboxtree, p), *i = boxes;
+
+	gts_object_destroy(GTS_OBJECT(p));
+
+	while (i) {
+		toporouter_bbox_t *box = TOPOROUTER_BBOX(i->data);
+
+		if (box->type == type && box->data == data) {
+			g_slist_free(boxes);
+			return box;
+		}
+
+		i = i->next;
+	}
+
+	g_slist_free(boxes);
+	return NULL;
+}
+
+void cluster_join_bbox(toporouter_cluster_t * cluster, toporouter_bbox_t * box)
+{
+	if (box) {
+		g_ptr_array_add(cluster->boxes, box);
+		box->cluster = cluster;
+	}
+}
+
+toporouter_netlist_t *netlist_create(toporouter_t * r, char *netlist, char *style)
+{
+	toporouter_netlist_t *nl = (toporouter_netlist_t *) malloc(sizeof(toporouter_netlist_t));
+	nl->netlist = netlist;
+	nl->style = style;
+	nl->clusters = g_ptr_array_new();
+	nl->routes = g_ptr_array_new();
+	nl->routed = NULL;
+	nl->pair = NULL;
+	g_ptr_array_add(r->netlists, nl);
+	return nl;
+}
+
+void import_clusters(toporouter_t * r)
+{
+	NetListListType nets;
+	ResetConnections(pcb_false);
+	nets = CollectSubnets(pcb_false);
+	NETLIST_LOOP(&nets);
+	{
+		if (netlist->NetN > 0) {
+			toporouter_netlist_t *nl = netlist_create(r, netlist->Net->Connection->menu->Name, netlist->Net->Connection->menu->Style);
+
+			NET_LOOP(netlist);
+			{
+
+				toporouter_cluster_t *cluster = cluster_create(r, nl);
+#ifdef DEBUG_MERGING
+				printf("NET:\n");
+#endif
+				CONNECTION_LOOP(net);
+				{
+
+					if (connection->type == PCB_TYPE_LINE) {
+						LineType *line = (LineType *) connection->ptr2;
+						toporouter_bbox_t *box = toporouter_bbox_locate(r, LINE, line, connection->X, connection->Y, connection->group);
+						cluster_join_bbox(cluster, box);
+
+#ifdef DEBUG_MERGING
+						pcb_printf("\tLINE %#mD\n", connection->X, connection->Y);
+#endif
+					}
+					else if (connection->type == PCB_TYPE_PAD) {
+						PadType *pad = (PadType *) connection->ptr2;
+						toporouter_bbox_t *box = toporouter_bbox_locate(r, PAD, pad, connection->X, connection->Y, connection->group);
+						cluster_join_bbox(cluster, box);
+
+#ifdef DEBUG_MERGING
+						pcb_printf("\tPAD %#mD\n", connection->X, connection->Y);
+#endif
+					}
+					else if (connection->type == PCB_TYPE_PIN) {
+						guint m;
+						for (m = 0; m < groupcount(); m++) {
+							PinType *pin = (PinType *) connection->ptr2;
+							toporouter_bbox_t *box = toporouter_bbox_locate(r, PIN, pin, connection->X, connection->Y, m);
+							cluster_join_bbox(cluster, box);
+						}
+
+#ifdef DEBUG_MERGING
+						pcb_printf("\tPIN %#mD\n", connection->X, connection->Y);
+#endif
+					}
+					else if (connection->type == PCB_TYPE_VIA) {
+						guint m;
+						for (m = 0; m < groupcount(); m++) {
+							PinType *pin = (PinType *) connection->ptr2;
+							toporouter_bbox_t *box = toporouter_bbox_locate(r, VIA, pin, connection->X, connection->Y, m);
+							cluster_join_bbox(cluster, box);
+						}
+
+#ifdef DEBUG_MERGING
+						pcb_printf("\tVIA %#mD\n", connection->X, connection->Y);
+#endif
+					}
+					else if (connection->type == PCB_TYPE_POLYGON) {
+						PolygonType *polygon = (PolygonType *) connection->ptr2;
+						toporouter_bbox_t *box =
+							toporouter_bbox_locate(r, POLYGON, polygon, connection->X, connection->Y, connection->group);
+						cluster_join_bbox(cluster, box);
+
+#ifdef DEBUG_MERGING
+						pcb_printf("\tPOLYGON %#mD\n", connection->X, connection->Y);
+#endif
+
+					}
+				}
+				END_LOOP;
+#ifdef DEBUG_MERGING
+				printf("\n");
+#endif
+			}
+			END_LOOP;
+
+		}
+	}
+	END_LOOP;
+	FreeNetListListMemory(&nets);
+}
+
+void import_geometry(toporouter_t * r)
+{
+	toporouter_layer_t *cur_layer;
+
+	int group;
+
+#ifdef DEBUG_IMPORT
+	for (group = 0; group < max_group; group++) {
+		printf("Group %d: Number %d:\n", group, PCB->LayerGroups.Number[group]);
+
+		for (int entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) {
+			printf("\tEntry %d\n", PCB->LayerGroups.Entries[group][entry]);
+		}
+	}
+#endif
+	/* Allocate space for per layer struct */
+	cur_layer = r->layers = (toporouter_layer_t *) malloc(groupcount() * sizeof(toporouter_layer_t));
+
+	/* Foreach layer, read in pad vertices and constraints, and build CDT */
+	for (group = 0; group < max_group; group++) {
+#ifdef DEBUG_IMPORT
+		printf("*** LAYER GROUP %d ***\n", group);
+#endif
+		if (PCB->LayerGroups.Number[group] > 0) {
+			cur_layer->vertices = NULL;
+			cur_layer->constraints = NULL;
+
+#ifdef DEBUG_IMPORT
+			printf("reading board constraints from layer %d into group %d\n", PCB->LayerGroups.Entries[group][0], group);
+#endif
+			read_board_constraints(r, cur_layer, PCB->LayerGroups.Entries[group][0]);
+#ifdef DEBUG_IMPORT
+			printf("reading points from layer %d into group %d \n", PCB->LayerGroups.Entries[group][0], group);
+#endif
+			read_points(r, cur_layer, PCB->LayerGroups.Entries[group][0]);
+
+/*#ifdef DEBUG_IMPORT    
+        printf("reading pads from layer %d into group %d\n", number, group);
+  #endif*/
+			read_pads(r, cur_layer, group);
+
+			GROUP_LOOP(PCB->Data, group) {
+
+#ifdef DEBUG_IMPORT
+				printf("reading lines from layer %d into group %d\n", number, group);
+#endif
+				read_lines(r, cur_layer, layer, number);
+
+			}
+			END_LOOP;
+
+
+
+#ifdef DEBUG_IMPORT
+			printf("building CDT\n");
+#endif
+			build_cdt(r, cur_layer);
+			printf("finished\n");
+/*      {
+    int i;
+    for(i=0;i<groupcount();i++) {
+      char buffer[256];
+      sprintf(buffer, "build%d.png", i);
+      toporouter_draw_surface(r, r->layers[i].surface, buffer, 2048, 2048, 2, NULL, i, NULL);
+    }
+  }*/
+#ifdef DEBUG_IMPORT
+			printf("finished building CDT\n");
+#endif
+			cur_layer++;
+		}
+	}
+
+	r->bboxtree = gts_bb_tree_new(r->bboxes);
+
+	import_clusters(r);
+
+#ifdef DEBUG_IMPORT
+	printf("finished import!\n");
+#endif
+}
+
+
+gint compare_points(gconstpointer a, gconstpointer b)
+{
+	GtsPoint *i = GTS_POINT(a);
+	GtsPoint *j = GTS_POINT(b);
+
+	if (i->x == j->x) {
+		if (i->y == j->y)
+			return 0;
+		if (i->y < j->y)
+			return -1;
+		return 1;
+	}
+	if (i->x < j->x)
+		return -1;
+	return 1;
+}
+
+gint compare_segments(gconstpointer a, gconstpointer b)
+{
+	if (a == b)
+		return 0;
+	if (a < b)
+		return -1;
+	return 1;
+}
+
+#define DEBUG_CLUSTER_FIND 1
+toporouter_cluster_t *cluster_find(toporouter_t * r, gdouble x, gdouble y, gdouble z)
+{
+	GtsPoint *p = gts_point_new(gts_point_class(), x, y, z);
+	GSList *hits = gts_bb_tree_stabbed(r->bboxtree, p);
+	toporouter_cluster_t *rval = NULL;
+
+#ifdef DEBUG_CLUSTER_FIND
+	printf("FINDING %f,%f,%f\n\n", x, y, z);
+#endif
+
+	while (hits) {
+		toporouter_bbox_t *box = TOPOROUTER_BBOX(hits->data);
+
+#ifdef DEBUG_CLUSTER_FIND
+		printf("HIT BOX: ");
+		print_bbox(box);
+#endif
+
+		if (box->layer == (int) z) {
+			if (box->type != BOARD) {
+				if (box->type == LINE) {
+					LineType *line = (LineType *) box->data;
+					gint linewind = coord_wind(line->Point1.X, line->Point1.Y, x, y, line->Point2.X, line->Point2.Y);
+
+					if (line->Point1.X > x - EPSILON && line->Point1.X < x + EPSILON &&
+							line->Point1.Y > y - EPSILON && line->Point1.Y < y + EPSILON) {
+						rval = box->cluster;
+						/*  break; */
+					}
+					if (line->Point2.X > x - EPSILON && line->Point2.X < x + EPSILON &&
+							line->Point2.Y > y - EPSILON && line->Point2.Y < y + EPSILON) {
+						rval = box->cluster;
+						/*  break; */
+					}
+					if (!linewind) {
+						rval = box->cluster;
+						/*  break; */
+					}
+
+				}
+				else if (box->surface) {
+
+					if (gts_point_locate(p, box->surface, NULL)) {
+						rval = box->cluster;
+						break;
+					}
+
+				}
+			}
+		}
+		hits = hits->next;
+	}
+
+	gts_object_destroy(GTS_OBJECT(p));
+
+
+#ifdef DEBUG_CLUSTER_FIND
+	printf("cluster_find: %f,%f,%f: ", x, y, z);
+	print_cluster(rval);
+#endif
+
+	return rval;
+}
+
+gdouble simple_h_cost(toporouter_t * r, toporouter_vertex_t * curpoint, toporouter_vertex_t * destpoint)
+{
+	gdouble layerpenalty = (vz(curpoint) == vz(destpoint)) ? 0. : r->viacost;
+
+	return gts_point_distance(GTS_POINT(curpoint), GTS_POINT(destpoint)) + layerpenalty;
+}
+
+#define FCOST(x) (x->gcost + x->hcost)
+gdouble route_heap_cmp(gpointer item, gpointer data)
+{
+	return FCOST(TOPOROUTER_VERTEX(item));
+}
+
+#define closelist_insert(p) closelist = g_list_prepend(closelist, p)
+
+typedef struct {
+	toporouter_vertex_t *key;
+	toporouter_vertex_t *result;
+} toporouter_heap_search_data_t;
+
+void toporouter_heap_search(gpointer data, gpointer user_data)
+{
+	toporouter_vertex_t *v = TOPOROUTER_VERTEX(data);
+	toporouter_heap_search_data_t *heap_search_data = (toporouter_heap_search_data_t *) user_data;
+	if (v == heap_search_data->key)
+		heap_search_data->result = v;
+}
+
+/*
+void 
+toporouter_heap_color(gpointer data, gpointer user_data)
+{
+  toporouter_vertex_t *v = TOPOROUTER_VERTEX(data);
+  v->flags |= (guint) user_data;
+}
+*/
+static inline gdouble angle_span(gdouble a1, gdouble a2)
+{
+	if (a1 > a2)
+		return ((2 * M_PI) - a1 + a2);
+	return a2 - a1;
+}
+
+gdouble region_span(toporouter_vertex_region_t * region)
+{
+	gdouble a1, a2;
+
+	g_assert(region->v1 != NULL);
+	g_assert(region->v2 != NULL);
+	g_assert(region->origin != NULL);
+
+	a1 = point_xangle(GTS_POINT(region->origin), GTS_POINT(region->v1));
+	a2 = point_xangle(GTS_POINT(region->origin), GTS_POINT(region->v2));
+
+	return angle_span(a1, a2);
+}
+
+gdouble edge_capacity(toporouter_edge_t * e)
+{
+	return gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e)));
+}
+
+gdouble edge_flow(toporouter_edge_t * e, toporouter_vertex_t * v1, toporouter_vertex_t * v2, toporouter_vertex_t * dest)
+{
+	GList *i = edge_routing(e);
+	toporouter_vertex_t *pv = tedge_v1(e), *v = NULL;
+	gdouble flow = 0.;
+	guint waiting = 1;
+
+	if ((pv == v1 || pv == v2) && waiting) {
+		flow += min_vertex_net_spacing(pv, dest);
+		pv = dest;
+		waiting = 0;
+	}
+
+	g_assert(v1 != v2);
+
+	while (i) {
+		v = TOPOROUTER_VERTEX(i->data);
+
+
+		if (pv == dest)
+			flow += min_vertex_net_spacing(v, pv);
+		else
+			flow += min_spacing(v, pv);
+
+		if ((v == v1 || v == v2) && waiting) {
+			flow += min_vertex_net_spacing(v, dest);
+			pv = dest;
+			waiting = 0;
+		}
+		else {
+			pv = v;
+		}
+		i = i->next;
+	}
+
+	if (pv == dest)
+		flow += min_vertex_net_spacing(tedge_v2(e), pv);
+	else
+		flow += min_spacing(tedge_v2(e), pv);
+
+	return flow;
+}
+
+void print_path(GList * path)
+{
+	GList *i = path;
+
+	printf("PATH:\n");
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+/*    printf("[V %f,%f,%f]\n", vx(v), vy(v), vz(v));*/
+		print_vertex(v);
+
+		if (v->child && !g_list_find(path, v->child))
+			printf("\t CHILD NOT IN LIST\n");
+		if (v->parent && !g_list_find(path, v->parent))
+			printf("\t parent NOT IN LIST\n");
+		i = i->next;
+	}
+
+
+}
+
+GList *split_path(GList * path)
+{
+	toporouter_vertex_t *pv = NULL;
+	GList *curpath = NULL, *i, *paths = NULL;
+#ifdef DEBUG_ROUTE
+	printf("PATH:\n");
+#endif
+	i = path;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+#ifdef DEBUG_ROUTE
+		printf("v = %f,%f ", vx(v), vy(v));
+		if (v->parent)
+			printf("parent = %f,%f ", vx(v->parent), vy(v->parent));
+		if (v->child)
+			printf("child = %f,%f ", vx(v->child), vy(v->child));
+		printf("\n");
+#endif
+/*    printf("***\n");
+      if(v) printf("v = %f,%f\n", GTS_POINT(v)->x, GTS_POINT(v)->y);
+      if(pv) printf("pv = %f,%f\n", GTS_POINT(pv)->x, GTS_POINT(pv)->y);*/
+
+
+		if (pv)
+			if (GTS_POINT(v)->x == GTS_POINT(pv)->x && GTS_POINT(v)->y == GTS_POINT(pv)->y) {
+				if (g_list_length(curpath) > 1)
+					paths = g_list_prepend(paths, curpath);
+				curpath = NULL;
+
+				pv->child = NULL;
+				v->parent = NULL;
+			}
+
+		curpath = g_list_append(curpath, v);
+
+		pv = v;
+		i = i->next;
+	}
+
+	if (g_list_length(curpath) > 1)
+		paths = g_list_prepend(paths, curpath);
+
+	return paths;
+}
+
+
+
+#define edge_gradient(e) (cartesian_gradient(GTS_POINT(GTS_SEGMENT(e)->v1)->x, GTS_POINT(GTS_SEGMENT(e)->v1)->y, \
+    GTS_POINT(GTS_SEGMENT(e)->v2)->x, GTS_POINT(GTS_SEGMENT(e)->v2)->y))
+
+
+/* sorting into ascending distance from v1 */
+gint routing_edge_insert(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	GtsPoint *v1 = GTS_POINT(edge_v1(user_data));
+
+	if (gts_point_distance2(v1, GTS_POINT(a)) < gts_point_distance2(v1, GTS_POINT(b)) - EPSILON)
+		return -1;
+	if (gts_point_distance2(v1, GTS_POINT(a)) > gts_point_distance2(v1, GTS_POINT(b)) + EPSILON)
+		return 1;
+/*
+  printf("a = %x b = %x\n", (int) a, (int) b);
+
+  printf("WARNING: routing_edge_insert() with same points..\n \
+      v1 @ %f,%f\n\
+      a  @ %f,%f\n\
+      b  @ %f,%f\n", 
+      v1->x, v1->y,
+      vx(a), vy(a),
+      vx(a), vy(b));
+  printf("A: "); print_vertex(TOPOROUTER_VERTEX(a));
+  printf("B: "); print_vertex(TOPOROUTER_VERTEX(b));
+
+  TOPOROUTER_VERTEX(a)->flags |= VERTEX_FLAG_RED;
+  TOPOROUTER_VERTEX(b)->flags |= VERTEX_FLAG_RED;
+*/
+	return 0;
+}
+
+
+toporouter_vertex_t *new_temp_toporoutervertex(gdouble x, gdouble y, toporouter_edge_t * e)
+{
+	GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class());
+	GList *i = edge_routing(e);
+	toporouter_vertex_t *r;
+
+	while (i) {
+		r = TOPOROUTER_VERTEX(i->data);
+		if (epsilon_equals(vx(r), x) && epsilon_equals(vy(r), y)) {
+			if (r->flags & VERTEX_FLAG_TEMP)
+				return r;
+		}
+		i = i->next;
+	}
+
+	r = TOPOROUTER_VERTEX(gts_vertex_new(vertex_class, x, y, vz(edge_v1(e))));
+	r->flags |= VERTEX_FLAG_TEMP;
+	r->routingedge = e;
+
+	if (TOPOROUTER_IS_CONSTRAINT(e))
+		TOPOROUTER_CONSTRAINT(e)->routing = g_list_insert_sorted_with_data(edge_routing(e), r, routing_edge_insert, e);
+	else
+		e->routing = g_list_insert_sorted_with_data(edge_routing(e), r, routing_edge_insert, e);
+
+	return r;
+}
+
+
+/* create vertex on edge e at radius r from v, closest to ref */
+toporouter_vertex_t *new_temp_toporoutervertex_in_segment(toporouter_edge_t * e, toporouter_vertex_t * v, gdouble r,
+																													toporouter_vertex_t * ref)
+{
+	gdouble m = edge_gradient(e);
+	toporouter_spoint_t p, np1, np2;
+/*  toporouter_vertex_t *b = TOPOROUTER_VERTEX((GTS_VERTEX(v) == edge_v1(e)) ? edge_v2(e) : edge_v1(e)); */
+	toporouter_vertex_t *rval = NULL;
+	p.x = vx(v);
+	p.y = vy(v);
+
+	vertices_on_line(&p, m, r, &np1, &np2);
+
+	if ((pow(np1.x - vx(ref), 2) + pow(np1.y - vy(ref), 2)) < (pow(np2.x - vx(ref), 2) + pow(np2.y - vy(ref), 2)))
+		rval = new_temp_toporoutervertex(np1.x, np1.y, e);
+	else
+		rval = new_temp_toporoutervertex(np2.x, np2.y, e);
+
+	return rval;
+}
+
+gint vertex_keepout_test(toporouter_t * r, toporouter_vertex_t * v)
+{
+	GList *i = r->keepoutlayers;
+	while (i) {
+		gdouble keepout = *((double *) i->data);
+		if (vz(v) == keepout)
+			return 1;
+		i = i->next;
+	}
+	return 0;
+}
+
+void
+closest_cluster_pair(toporouter_t * r, GList * src_vertices, GList * dest_vertices, toporouter_vertex_t ** a,
+										 toporouter_vertex_t ** b)
+{
+	GList *i = src_vertices, *j = dest_vertices;
+
+	gdouble min = 0.;
+	*a = NULL;
+	*b = NULL;
+
+	i = src_vertices;
+	while (i) {
+		toporouter_vertex_t *v1 = TOPOROUTER_VERTEX(i->data);
+
+		if (vertex_keepout_test(r, v1)) {
+			i = i->next;
+			continue;
+		}
+
+		j = dest_vertices;
+		while (j) {
+			toporouter_vertex_t *v2 = TOPOROUTER_VERTEX(j->data);
+			if (vertex_keepout_test(r, v2) || vz(v2) != vz(v1)) {
+				j = j->next;
+				continue;
+			}
+
+			if (!*a) {
+				*a = v1;
+				*b = v2;
+				min = simple_h_cost(r, *a, *b);
+			}
+			else {
+				gdouble tempd = simple_h_cost(r, v1, v2);
+				if (r->flags & TOPOROUTER_FLAG_GOFAR && tempd > min) {
+					*a = v1;
+					*b = v2;
+					min = tempd;
+				}
+				else if (tempd < min) {
+					*a = v1;
+					*b = v2;
+					min = tempd;
+				}
+			}
+
+			j = j->next;
+		}
+
+		i = i->next;
+	}
+
+/*  g_list_free(src_vertices);
+    g_list_free(dest_vertices);*/
+}
+
+
+toporouter_vertex_t *closest_dest_vertex(toporouter_t * r, toporouter_vertex_t * v, toporouter_route_t * routedata)
+{
+	GList													/* *vertices = cluster_vertices(r, routedata->dest),  */
+		* i = routedata->destvertices;
+	toporouter_vertex_t *closest = NULL;
+	gdouble closest_distance = 0.;
+
+/*  if(routedata->flags & TOPOROUTER_FLAG_FLEX) i = r->destboxes; */
+
+	while (i) {
+		toporouter_vertex_t *cv = TOPOROUTER_VERTEX(i->data);
+
+		if (vz(cv) != vz(v)) {
+			i = i->next;
+			continue;
+		}
+
+		if (!closest) {
+			closest = cv;
+			closest_distance = simple_h_cost(r, v, closest);
+		}
+		else {
+			gdouble tempd = simple_h_cost(r, v, cv);
+			if (r->flags & TOPOROUTER_FLAG_GOFAR && tempd > closest_distance) {
+				closest = cv;
+				closest_distance = tempd;
+			}
+			else if (tempd < closest_distance) {
+				closest = cv;
+				closest_distance = tempd;
+			}
+		}
+		i = i->next;
+	}
+
+/*  g_list_free(vertices); */
+
+#ifdef DEBUG_ROUTE
+	printf("CLOSEST = %f,%f,%f\n", vx(closest), vy(closest), vz(closest));
+#endif
+	return closest;
+}
+
+#define toporouter_edge_gradient(e) (cartesian_gradient(vx(edge_v1(e)), vy(edge_v1(e)), vx(edge_v2(e)), vy(edge_v2(e))))
+
+
+/* returns the capacity of the triangle cut through v */
+gdouble triangle_interior_capacity(GtsTriangle * t, toporouter_vertex_t * v)
+{
+	toporouter_edge_t *e = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(v)));
+	gdouble x, y, m1, m2, c2, c1;
+#ifdef DEBUG_ROUTE
+	gdouble len;
+#endif
+
+	g_assert(e);
+
+	m1 = toporouter_edge_gradient(e);
+	m2 = perpendicular_gradient(m1);
+	c2 = (isinf(m2)) ? vx(v) : vy(v) - (m2 * vx(v));
+	c1 = (isinf(m1)) ? vx(edge_v1(e)) : vy(edge_v1(e)) - (m1 * vx(edge_v1(e)));
+
+	if (isinf(m2))
+		x = vx(v);
+	else if (isinf(m1))
+		x = vx(edge_v1(e));
+	else
+		x = (c2 - c1) / (m1 - m2);
+
+	y = (isinf(m2)) ? vy(edge_v1(e)) : (m2 * x) + c2;
+
+#ifdef DEBUG_ROUTE
+	len = gts_point_distance2(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e)));
+	printf("%f,%f len = %f v = %f,%f\n", x, y, len, vx(v), vy(v));
+#endif
+
+	if (epsilon_equals(x, vx(edge_v1(e))) && epsilon_equals(y, vy(edge_v1(e))))
+		return INFINITY;
+	if (epsilon_equals(x, vx(edge_v2(e))) && epsilon_equals(y, vy(edge_v2(e))))
+		return INFINITY;
+
+	if (x >= MIN(vx(edge_v1(e)), vx(edge_v2(e))) &&
+			x <= MAX(vx(edge_v1(e)), vx(edge_v2(e))) &&
+			y >= MIN(vy(edge_v1(e)), vy(edge_v2(e))) && y <= MAX(vy(edge_v1(e)), vy(edge_v2(e))))
+
+/*  if( (pow(vx(edge_v1(e)) - x, 2) + pow(vy(edge_v1(e)) - y, 2)) < len && (pow(vx(edge_v2(e)) - x, 2) + pow(vy(edge_v2(e)) - y, 2)) < len ) */
+		return sqrt(pow(vx(v) - x, 2) + pow(vy(v) - y, 2));
+
+	return INFINITY;
+}
+
+static inline toporouter_vertex_t *segment_common_vertex(GtsSegment * s1, GtsSegment * s2)
+{
+	if (!s1 || !s2)
+		return NULL;
+	if (s1->v1 == s2->v1)
+		return TOPOROUTER_VERTEX(s1->v1);
+	if (s1->v2 == s2->v1)
+		return TOPOROUTER_VERTEX(s1->v2);
+	if (s1->v1 == s2->v2)
+		return TOPOROUTER_VERTEX(s1->v1);
+	if (s1->v2 == s2->v2)
+		return TOPOROUTER_VERTEX(s1->v2);
+	return NULL;
+}
+
+static inline toporouter_vertex_t *route_vertices_common_vertex(toporouter_vertex_t * v1, toporouter_vertex_t * v2)
+{
+	return segment_common_vertex(GTS_SEGMENT(v1->routingedge), GTS_SEGMENT(v2->routingedge));
+}
+
+
+static inline guint edges_third_edge(GtsSegment * s1, GtsSegment * s2, toporouter_vertex_t ** v1, toporouter_vertex_t ** v2)
+{
+	if (!s1 || !s2)
+		return 0;
+	if (s1->v1 == s2->v1) {
+		*v1 = TOPOROUTER_VERTEX(s1->v2);
+		*v2 = TOPOROUTER_VERTEX(s2->v2);
+		return 1;
+	}
+	if (s1->v2 == s2->v1) {
+		*v1 = TOPOROUTER_VERTEX(s1->v1);
+		*v2 = TOPOROUTER_VERTEX(s2->v2);
+		return 1;
+	}
+	if (s1->v1 == s2->v2) {
+		*v1 = TOPOROUTER_VERTEX(s1->v2);
+		*v2 = TOPOROUTER_VERTEX(s2->v1);
+		return 1;
+	}
+	if (s1->v2 == s2->v2) {
+		*v1 = TOPOROUTER_VERTEX(s1->v1);
+		*v2 = TOPOROUTER_VERTEX(s2->v1);
+		return 1;
+	}
+	return 0;
+}
+
+/* returns the flow from e1 to e2, and the flow from the vertex oppisate e1 to
+ * e1 and the vertex oppisate e2 to e2 */
+gdouble
+flow_from_edge_to_edge(GtsTriangle * t, toporouter_edge_t * e1, toporouter_edge_t * e2,
+											 toporouter_vertex_t * common_v, toporouter_vertex_t * curpoint)
+{
+	gdouble r = 0.;
+	toporouter_vertex_t *pv = common_v, *v;
+	toporouter_edge_t *op_edge;
+
+	GList *i = edge_routing(e1);
+	while (i) {
+		v = TOPOROUTER_VERTEX(i->data);
+
+		if (v == curpoint) {
+			r += min_spacing(v, pv);
+			pv = v;
+			i = i->next;
+			continue;
+		}
+/*    if(!(v->flags & VERTEX_FLAG_TEMP)) { */
+		if ((v->flags & VERTEX_FLAG_ROUTE)) {
+			if (v->parent)
+				if (v->parent->routingedge == e2) {
+					r += min_spacing(v, pv);
+					pv = v;
+					i = i->next;
+					continue;
+				}
+
+			if (v->child)
+				if (v->child->routingedge == e2) {
+					r += min_spacing(v, pv);
+					pv = v;
+					i = i->next;
+					continue;
+				}
+		}
+		i = i->next;
+	}
+
+	op_edge = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(common_v)));
+
+	g_assert(op_edge);
+	g_assert(e1);
+	g_assert(e2);
+
+	v = segment_common_vertex(GTS_SEGMENT(e2), GTS_SEGMENT(op_edge));
+	g_assert(v);
+
+	/*v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e1))); */
+	if (v->flags & VERTEX_FLAG_ROUTE && v->parent && v->parent->routingedge) {
+		if (v->parent->routingedge == e1)
+			r += min_spacing(v, pv);
+	}
+
+	v = segment_common_vertex(GTS_SEGMENT(e1), GTS_SEGMENT(op_edge));
+	g_assert(v);
+
+	/*v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e2))); */
+	if (v->flags & VERTEX_FLAG_ROUTE && v->parent && v->parent->routingedge) {
+		if (v->parent->routingedge == e1)
+			r += min_spacing(v, pv);
+	}
+
+	if (TOPOROUTER_IS_CONSTRAINT(op_edge)) {
+		toporouter_bbox_t *box = vertex_bbox(TOPOROUTER_VERTEX(edge_v1(op_edge)));
+		r += vertex_net_thickness(v) / 2.;
+		if (box) {
+			r += MAX(vertex_net_clearance(v), cluster_clearance(box->cluster));
+			r += cluster_thickness(box->cluster) / 2.;
+		}
+		else {
+			r += vertex_net_clearance(v);
+
+		}
+	}
+
+	return r;
+}
+
+
+
+guint
+check_triangle_interior_capacity(GtsTriangle * t, toporouter_vertex_t * v, toporouter_vertex_t * curpoint,
+																 toporouter_edge_t * op_edge, toporouter_edge_t * adj_edge1, toporouter_edge_t * adj_edge2)
+{
+	gdouble ic = triangle_interior_capacity(t, v);
+	gdouble flow = flow_from_edge_to_edge(t, adj_edge1, adj_edge2, v, curpoint);
+
+	if (TOPOROUTER_IS_CONSTRAINT(adj_edge1) || TOPOROUTER_IS_CONSTRAINT(adj_edge2))
+		return 1;
+
+
+	if (flow > ic) {
+#ifdef DEBUG_ROUTE
+		printf("fail interior capacity flow = %f ic = %f\n", flow, ic);
+#endif
+		return 0;
+	}
+
+	return 1;
+}
+
+toporouter_vertex_t *edge_routing_next_not_temp(toporouter_edge_t * e, GList * list)
+{
+	if (!TOPOROUTER_IS_CONSTRAINT(e)) {
+		while (list) {
+			toporouter_vertex_t *v = TOPOROUTER_VERTEX(list->data);
+			if (!(v->flags & VERTEX_FLAG_TEMP))
+				return v;
+
+			list = list->next;
+		}
+	}
+	return tedge_v2(e);
+}
+
+toporouter_vertex_t *edge_routing_prev_not_temp(toporouter_edge_t * e, GList * list)
+{
+	if (!TOPOROUTER_IS_CONSTRAINT(e)) {
+		while (list) {
+			toporouter_vertex_t *v = TOPOROUTER_VERTEX(list->data);
+			if (!(v->flags & VERTEX_FLAG_TEMP))
+				return v;
+
+			list = list->prev;
+		}
+	}
+	return tedge_v1(e);
+}
+
+void
+edge_adjacent_vertices(toporouter_edge_t * e, toporouter_vertex_t * v, toporouter_vertex_t ** v1, toporouter_vertex_t ** v2)
+{
+	GList *r = g_list_find(edge_routing(e), v);
+
+	if (v == tedge_v1(e)) {
+		*v1 = NULL;
+		*v2 = edge_routing_next_not_temp(e, edge_routing(e));
+	}
+	else if (v == tedge_v2(e)) {
+		*v1 = edge_routing_prev_not_temp(e, g_list_last(edge_routing(e)));
+		*v2 = NULL;
+	}
+	else {
+/*    r = g_list_find(r, v);*/
+		*v1 = edge_routing_prev_not_temp(e, r);
+		*v2 = edge_routing_next_not_temp(e, r);
+
+	}
+
+}
+
+
+GList *candidate_vertices(toporouter_vertex_t * v1, toporouter_vertex_t * v2, toporouter_vertex_t * dest, toporouter_edge_t * e)
+{
+	gdouble totald, v1ms, v2ms, flow, capacity, ms;
+	GList *vs = NULL;
+
+	g_assert(v1);
+	g_assert(v2);
+	g_assert(dest);
+
+	g_assert(!(v1->flags & VERTEX_FLAG_TEMP));
+	g_assert(!(v2->flags & VERTEX_FLAG_TEMP));
+#ifdef DEBUG_ROUTE
+	printf("starting candidate vertices\n");
+	printf("v1 = %f,%f v2 = %f,%f dest = %f,%f\n", vx(v1), vy(v1), vx(v2), vy(v2), vx(dest), vy(dest));
+#endif
+	totald = gts_point_distance(GTS_POINT(v1), GTS_POINT(v2));
+	v1ms = min_spacing(v1, dest);
+	v2ms = min_spacing(v2, dest);
+	ms = min_spacing(dest, dest);
+	flow = TOPOROUTER_IS_CONSTRAINT(e) ? 0. : edge_flow(e, v1, v2, dest);
+	capacity = edge_capacity(e);
+
+#ifdef DEBUG_ROUTE
+	g_assert(totald > 0);
+
+	printf("v1ms = %f v2ms = %f totald = %f ms = %f capacity = %f flow = %f\n", v1ms, v2ms, totald, ms, capacity, flow);
+#endif
+
+	if (flow >= capacity)
+		return NULL;
+
+
+	if (v1ms + v2ms + ms >= totald) {
+		vs = g_list_prepend(vs, new_temp_toporoutervertex((vx(v1) + vx(v2)) / 2., (vy(v1) + vy(v2)) / 2., e));
+	}
+	else {
+		gdouble x0, y0, x1, y1, d;
+
+		vertex_move_towards_vertex_values(GTS_VERTEX(v1), GTS_VERTEX(v2), v1ms, &x0, &y0);
+
+		vs = g_list_prepend(vs, new_temp_toporoutervertex(x0, y0, e));
+
+		vertex_move_towards_vertex_values(GTS_VERTEX(v2), GTS_VERTEX(v1), v2ms, &x1, &y1);
+
+		vs = g_list_prepend(vs, new_temp_toporoutervertex(x1, y1, e));
+
+		d = sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2));
+
+		if (ms < d) {
+/*      guint nint = d / ms;
+        gdouble dif = d / (nint + 1); */
+			gdouble dif = d / 2;
+
+/*      for(guint j=0;j<nint;j++) {*/
+			gdouble x, y;
+
+/*        coord_move_towards_coord_values(x0, y0, x1, y1, dif * j, &x, &y);*/
+			coord_move_towards_coord_values(x0, y0, x1, y1, dif, &x, &y);
+
+			vs = g_list_prepend(vs, new_temp_toporoutervertex(x, y, e));
+
+/*      } */
+
+		}
+
+	}
+#ifdef DEBUG_ROUTE
+	printf("candidate vertices returning %d\n", g_list_length(vs));
+#endif
+	return vs;
+}
+
+GList *edge_routing_first_not_temp(toporouter_edge_t * e)
+{
+	GList *i = edge_routing(e);
+	toporouter_vertex_t *v;
+
+	while (i) {
+		v = TOPOROUTER_VERTEX(i->data);
+		if (!(v->flags & VERTEX_FLAG_TEMP))
+			return i;
+
+		i = i->next;
+	}
+
+	return NULL;
+}
+
+GList *edge_routing_last_not_temp(toporouter_edge_t * e)
+{
+	GList *i = edge_routing(e), *last = NULL;
+	toporouter_vertex_t *v;
+
+	while (i) {
+		v = TOPOROUTER_VERTEX(i->data);
+		if (!(v->flags & VERTEX_FLAG_TEMP))
+			last = i;
+
+		i = i->next;
+	}
+
+	return last;
+}
+
+void delete_vertex(toporouter_vertex_t * v)
+{
+
+	if (v->flags & VERTEX_FLAG_TEMP) {
+		if (v->routingedge) {
+			if (TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+				TOPOROUTER_CONSTRAINT(v->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(v->routingedge)->routing, v);
+			else
+				v->routingedge->routing = g_list_remove(v->routingedge->routing, v);
+		}
+
+		gts_object_destroy(GTS_OBJECT(v));
+	}
+}
+
+#define edge_is_blocked(e) (TOPOROUTER_IS_EDGE(e) ? (e->flags & EDGE_FLAG_DIRECTCONNECTION) : 0)
+
+GList *triangle_candidate_points_from_vertex(GtsTriangle * t, toporouter_vertex_t * v, toporouter_vertex_t * dest,
+																						 toporouter_route_t * routedata)
+{
+	toporouter_edge_t *op_e = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(v)));
+	toporouter_vertex_t *vv1, *vv2, *constraintv = NULL;
+	toporouter_edge_t *e1, *e2;
+	GList *i;
+	GList *rval = NULL;
+
+#ifdef DEBUG_ROUTE
+	printf("\tTRIANGLE CAND POINT FROM VERTEX\n");
+
+	g_assert(op_e);
+#endif
+
+	e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v), edge_v1(op_e)));
+	e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v), edge_v2(op_e)));
+
+
+	if (TOPOROUTER_IS_CONSTRAINT(op_e)) {
+		if (TOPOROUTER_CONSTRAINT(op_e)->box->type == BOARD) {
+#ifdef DEBUG_ROUTE
+			printf("BOARD constraint\n");
+#endif
+			return NULL;
+		}
+		if (constraint_netlist(TOPOROUTER_CONSTRAINT(op_e)) != vertex_netlist(dest)) {	/* || TOPOROUTER_CONSTRAINT(op_e)->routing) { */
+#ifdef DEBUG_ROUTE
+			printf("op_e routing:\n");
+			print_edge(op_e);
+#endif
+			return NULL;
+		}
+#ifdef DEBUG_ROUTE
+		printf("RETURNING CONSTRAINT POING\n");
+#endif
+		constraintv = new_temp_toporoutervertex_in_segment(op_e, TOPOROUTER_VERTEX(edge_v1(op_e)),
+																											 gts_point_distance(GTS_POINT(edge_v1(op_e)),
+																																					GTS_POINT(edge_v2(op_e))) / 2.,
+																											 TOPOROUTER_VERTEX(edge_v2(op_e)));
+/*    return g_list_prepend(NULL, vv1); */
+
+
+	}
+
+	if (edge_is_blocked(op_e)) {
+		goto triangle_candidate_points_from_vertex_exit;
+	}
+/*  v1 = tedge_v1(op_e); 
+    v2 = tedge_v2(op_e);*/
+
+	if (v == tedge_v1(e1)) {
+		i = edge_routing_first_not_temp(e1);
+	}
+	else {
+		i = edge_routing_last_not_temp(e1);
+	}
+
+	if (i) {
+		toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data);
+
+		if (temp->parent == tedge_v2(op_e) || temp->child == tedge_v2(op_e)) {
+#ifdef DEBUG_ROUTE
+			printf("temp -> op_e->v2\n");
+#endif
+			goto triangle_candidate_points_from_vertex_exit;
+		}
+		if (temp->parent->routingedge == op_e) {
+			vv1 = temp->parent;
+#ifdef DEBUG_ROUTE
+			printf("vv1->parent\n");
+#endif
+
+		}
+		else if (temp->child->routingedge == op_e) {
+			vv1 = temp->child;
+#ifdef DEBUG_ROUTE
+			printf("vv1->child\n");
+#endif
+
+		}
+		else {
+			/* must be to e2 */
+#ifdef DEBUG_ROUTE
+			printf("temp -> e2?\n");
+			printf("op_e = %f,%f\t\t%f,%f\n", vx(edge_v1(op_e)), vy(edge_v1(op_e)), vx(edge_v2(op_e)), vy(edge_v2(op_e)));
+			if (temp->parent->routingedge)
+				printf("temp->parent->routingedge = %f,%f \t\t %f,%f\n",
+							 vx(edge_v1(temp->parent->routingedge)), vy(edge_v1(temp->parent->routingedge)),
+							 vx(edge_v2(temp->parent->routingedge)), vy(edge_v2(temp->parent->routingedge))
+					);
+			else
+				printf("temp->parent->routingedge = NULL\n");
+
+			if (temp->child->routingedge)
+				printf("temp->child->routingedge = %f,%f \t\t %f,%f\n",
+							 vx(edge_v1(temp->child->routingedge)), vy(edge_v1(temp->child->routingedge)),
+							 vx(edge_v2(temp->child->routingedge)), vy(edge_v2(temp->child->routingedge))
+					);
+			else
+				printf("temp->child->routingedge = NULL\n");
+#endif
+			goto triangle_candidate_points_from_vertex_exit;
+		}
+
+	}
+	else {
+		vv1 = tedge_v1(op_e);
+#ifdef DEBUG_ROUTE
+		printf("nothing on e1\n");
+#endif
+	}
+
+	if (v == tedge_v1(e2)) {
+		i = edge_routing_first_not_temp(e2);
+	}
+	else {
+		i = edge_routing_last_not_temp(e2);
+	}
+
+	if (i) {
+		toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data);
+
+		if (temp->parent == tedge_v1(op_e) || temp->child == tedge_v1(op_e)) {
+#ifdef DEBUG_ROUTE
+			printf("temp -> op_e->v2\n");
+#endif
+			goto triangle_candidate_points_from_vertex_exit;
+		}
+
+		if (temp->parent->routingedge == op_e) {
+			vv2 = temp->parent;
+#ifdef DEBUG_ROUTE
+			printf("vv2->parent\n");
+#endif
+		}
+		else if (temp->child->routingedge == op_e) {
+			vv2 = temp->child;
+#ifdef DEBUG_ROUTE
+			printf("vv2->child\n");
+#endif
+
+		}
+		else {
+			/* must be to e1 */
+#ifdef DEBUG_ROUTE
+			printf("temp -> e1?\n");
+			printf("op_e = %f,%f\t\t%f,%f\n", vx(edge_v1(op_e)), vy(edge_v1(op_e)), vx(edge_v2(op_e)), vy(edge_v2(op_e)));
+			if (temp->parent->routingedge)
+				printf("temp->parent->routingedge = %f,%f \t\t %f,%f\n",
+							 vx(edge_v1(temp->parent->routingedge)), vy(edge_v1(temp->parent->routingedge)),
+							 vx(edge_v2(temp->parent->routingedge)), vy(edge_v2(temp->parent->routingedge))
+					);
+			else
+				printf("temp->parent->routingedge = NULL\n");
+
+			if (temp->child->routingedge)
+				printf("temp->child->routingedge = %f,%f \t\t %f,%f\n",
+							 vx(edge_v1(temp->child->routingedge)), vy(edge_v1(temp->child->routingedge)),
+							 vx(edge_v2(temp->child->routingedge)), vy(edge_v2(temp->child->routingedge))
+					);
+			else
+				printf("temp->child->routingedge = NULL\n");
+#endif
+			goto triangle_candidate_points_from_vertex_exit;
+		}
+
+	}
+	else {
+		vv2 = tedge_v2(op_e);
+#ifdef DEBUG_ROUTE
+		printf("nothing on e2\n");
+#endif
+	}
+
+#ifdef DEBUG_ROUTE
+	printf("size of e1 routing = %d e2 routing = %d op_e routing = %d\n",
+				 g_list_length(edge_routing(e1)), g_list_length(edge_routing(e2)), g_list_length(edge_routing(op_e)));
+#endif
+
+	if (constraintv) {
+#ifdef DEBUG_ROUTE
+		print_vertex(constraintv);
+		printf("constraintv %f,%f returning\n", vx(constraintv), vy(constraintv));
+#endif
+		return g_list_prepend(NULL, constraintv);
+	}
+
+	i = edge_routing(op_e);
+	while (i) {
+		toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data);
+
+		if (temp->parent == v || temp->child == v) {
+			rval = g_list_concat(rval, candidate_vertices(vv1, temp, dest, op_e));
+			vv1 = temp;
+		}
+
+		i = i->next;
+	}
+
+	rval = g_list_concat(rval, candidate_vertices(vv1, vv2, dest, op_e));
+
+	return rval;
+
+
+
+triangle_candidate_points_from_vertex_exit:
+	if (constraintv)							/*delete_vertex(constraintv);     */
+		g_hash_table_insert(routedata->alltemppoints, constraintv, constraintv);
+
+	g_list_free(rval);
+
+	return NULL;
+}
+
+void routedata_insert_temppoints(toporouter_route_t * data, GList * temppoints)
+{
+	GList *j = temppoints;
+	while (j) {
+		g_hash_table_insert(data->alltemppoints, j->data, j->data);
+		j = j->next;
+	}
+}
+
+
+static inline gint constraint_route_test(toporouter_constraint_t * c, toporouter_route_t * routedata)
+{
+	if (c->box->cluster && c->box->cluster->netlist == routedata->src->netlist) {
+		if (c->box->cluster->c == routedata->dest->c || c->box->cluster->c == routedata->src->c)
+			return 1;
+	}
+	return 0;
+}
+
+GList *all_candidates_on_edge(toporouter_edge_t * e, toporouter_route_t * routedata)
+{
+	GList *rval = NULL;
+	if (edge_is_blocked(e))
+		return NULL;
+
+	if (!TOPOROUTER_IS_CONSTRAINT(e)) {
+		GList *i = edge_routing(e);
+		toporouter_vertex_t *pv = tedge_v1(e);
+
+		while (i) {
+			toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+			if (!(v->flags & VERTEX_FLAG_TEMP)) {
+				rval = g_list_concat(rval, candidate_vertices(pv, v, TOPOROUTER_VERTEX(routedata->destvertices->data), e));
+				pv = v;
+			}
+			i = i->next;
+		}
+
+		rval = g_list_concat(rval, candidate_vertices(pv, tedge_v2(e), TOPOROUTER_VERTEX(routedata->destvertices->data), e));
+	}
+	else if (TOPOROUTER_CONSTRAINT(e)->box->type == BOARD) {
+		return NULL;
+	}
+	else if (constraint_route_test(TOPOROUTER_CONSTRAINT(e), routedata)) {
+		toporouter_vertex_t *consv =
+			new_temp_toporoutervertex_in_segment(e, tedge_v1(e), tvdistance(tedge_v1(e), tedge_v2(e)) / 2., tedge_v2(e));
+		rval = g_list_prepend(rval, consv);
+/*    g_hash_table_insert(routedata->alltemppoints, consv, consv);  */
+	}
+
+	return rval;
+}
+
+GList *triangle_all_candidate_points_from_vertex(GtsTriangle * t, toporouter_vertex_t * v, toporouter_route_t * routedata)
+{
+	toporouter_edge_t *op_e = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(v)));
+	return all_candidates_on_edge(op_e, routedata);
+}
+
+GList *triangle_all_candidate_points_from_edge(toporouter_t * r, GtsTriangle * t, toporouter_edge_t * e,
+																							 toporouter_route_t * routedata, toporouter_vertex_t ** dest,
+																							 toporouter_vertex_t * curpoint)
+{
+	toporouter_vertex_t *op_v;
+	toporouter_edge_t *e1, *e2;
+	GList *i, *rval = NULL, *rval2 = NULL;
+	toporouter_vertex_t *boxpoint = NULL;
+	guint e1intcap, e2intcap;
+
+	op_v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e)));
+
+
+	if (vertex_bbox(op_v))
+		boxpoint = TOPOROUTER_VERTEX(vertex_bbox(op_v)->point);
+
+	if (g_list_find(routedata->destvertices, op_v)) {
+		rval = g_list_prepend(rval, op_v);
+		*dest = op_v;
+		return rval;
+	}
+	else if (g_list_find(routedata->destvertices, boxpoint)) {
+		*dest = boxpoint;
+	}
+	else if (g_list_find(routedata->srcvertices, op_v)) {
+		rval = g_list_prepend(rval, op_v);
+	}
+
+	e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v1(e)));
+	e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v2(e)));
+
+	rval = g_list_concat(rval, all_candidates_on_edge(e1, routedata));
+	rval = g_list_concat(rval, all_candidates_on_edge(e2, routedata));
+
+	e1intcap = check_triangle_interior_capacity(t, tedge_v1(e), curpoint, e2, e, e1);
+	e2intcap = check_triangle_interior_capacity(t, tedge_v2(e), curpoint, e1, e, e2);
+
+	i = rval;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+		if (!v->routingedge)
+			rval2 = g_list_prepend(rval2, v);
+		else if (v->routingedge == e1 && !(!TOPOROUTER_IS_CONSTRAINT(e1) && !e1intcap))
+			rval2 = g_list_prepend(rval2, v);
+		else if (v->routingedge == e2 && !(!TOPOROUTER_IS_CONSTRAINT(e2) && !e2intcap))
+			rval2 = g_list_prepend(rval2, v);
+
+		i = i->next;
+	}
+	g_list_free(rval);
+
+	return rval2;
+}
+
+GList *triangle_candidate_points_from_edge(toporouter_t * r, GtsTriangle * t, toporouter_edge_t * e, toporouter_vertex_t * v,
+																					 toporouter_vertex_t ** dest, toporouter_route_t * routedata)
+{
+	toporouter_vertex_t *v1, *v2, *op_v, *vv = NULL, *e1constraintv = NULL, *e2constraintv = NULL;
+	toporouter_edge_t *e1, *e2;
+	GList *e1cands = NULL, *e2cands = NULL, *rval = NULL;
+	guint noe1 = 0, noe2 = 0;
+
+	op_v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e)));
+
+	e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v1(e)));
+	e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v2(e)));
+
+	g_assert(*dest);
+
+	/* v1 is prev dir, v2 is next dir */
+	edge_adjacent_vertices(e, v, &v1, &v2);
+
+	if (TOPOROUTER_IS_CONSTRAINT(e1)) {
+		GList *i = edge_routing(e1);
+
+		if (TOPOROUTER_CONSTRAINT(e1)->box->type == BOARD) {
+			noe1 = 1;
+		}
+		else if (!constraint_route_test(TOPOROUTER_CONSTRAINT(e1), routedata)) {
+			noe1 = 1;
+#ifdef DEBUG_ROUTE
+			printf("noe1 netlist\n");
+#endif
+		}
+		else
+		 if (v1 == tedge_v1(e) ||
+					 (v1->parent->routingedge && v1->parent->routingedge == e1) ||
+					 (v1->child->routingedge && v1->child->routingedge == e1)) {
+			e1constraintv =
+				new_temp_toporoutervertex_in_segment(e1, tedge_v1(e1),
+																						 gts_point_distance(GTS_POINT(edge_v1(e1)), GTS_POINT(edge_v2(e1))) / 2.,
+																						 tedge_v2(e1));
+		}
+
+		while (i) {
+			toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data);
+
+			if ((temp->child == tedge_v2(e) || temp->parent == tedge_v2(e)) && !(temp->flags & VERTEX_FLAG_TEMP))
+				noe2 = 1;
+
+			i = i->next;
+		}
+
+		goto triangle_candidate_points_e2;
+	}
+
+	if (edge_is_blocked(e1)) {
+		noe1 = 1;
+		goto triangle_candidate_points_e2;
+	}
+
+	if (v1 == tedge_v1(e)) {
+		/* continue up e1 */
+		toporouter_vertex_t *vv1, *vv2;
+		edge_adjacent_vertices(e1, v1, &vv1, &vv2);
+
+#ifdef DEBUG_ROUTE
+		printf("v1 == e->v1\n");
+#endif
+
+		if (vv1) {
+			/* candidates from v1 until vv1  */
+			vv = vv1;
+		}
+		else {
+			/* candidates from v1 until vv2 */
+			vv = vv2;
+		}
+
+		if (!e1constraintv)
+			e1cands = candidate_vertices(v1, vv, *dest, e1);
+
+		if (vv != op_v) {
+			if (vv->parent == tedge_v2(e) || vv->child == tedge_v2(e)) {
+#ifdef DEBUG_ROUTE
+				printf("noe2 0\n");
+#endif
+				noe2 = 1;
+			}
+		}
+
+	}
+	else if (v1->parent != op_v && v1->child != op_v) {
+		toporouter_vertex_t *vv1 = NULL, *vv2 = NULL;
+
+#ifdef DEBUG_ROUTE
+		printf("v1 != e->v1\n");
+#endif
+
+		if (v1->parent->routingedge == e1) {
+			vv1 = v1->parent;
+#ifdef DEBUG_ROUTE
+			printf("v1 parent = e1\n");
+#endif
+			if (op_v == tedge_v1(e1)) {
+				/* candidates from v1->parent until prev vertex  */
+				vv2 = edge_routing_prev_not_temp(e1, g_list_find(edge_routing(e1), v1->parent)->prev);
+			}
+			else {
+				/* candidates from v1->parent until next vertex  */
+				vv2 = edge_routing_next_not_temp(e1, g_list_find(edge_routing(e1), v1->parent)->next);
+			}
+
+		}
+		else if (v1->child->routingedge == e1) {
+			vv1 = v1->child;
+#ifdef DEBUG_ROUTE
+			printf("v1 child = e1\n");
+#endif
+			if (op_v == tedge_v1(e1)) {
+				/* candidates from v1->child until prev vertex  */
+				vv2 = edge_routing_prev_not_temp(e1, g_list_find(edge_routing(e1), v1->child)->prev);
+			}
+			else {
+				/* candidates from v1->child until next vertex */
+				vv2 = edge_routing_next_not_temp(e1, g_list_find(edge_routing(e1), v1->child)->next);
+			}
+
+		}
+		else {
+#ifdef DEBUG_ROUTE
+			printf("v1 ? \n");
+#endif
+			goto triangle_candidate_points_e2;
+		}
+
+		if (vv1 && vv2) {
+			if (vv2->parent == tedge_v2(e) || vv2->child == tedge_v2(e)) {
+#ifdef DEBUG_ROUTE
+				printf("noe2 1\n");
+#endif
+				noe2 = 1;
+			}
+
+			if (!e1constraintv)
+				e1cands = candidate_vertices(vv1, vv2, *dest, e1);
+
+			vv = vv2;
+		}
+	}
+
+	if (vv && vv == op_v) {
+		toporouter_vertex_t *boxpoint = NULL;
+
+		if (vertex_bbox(op_v))
+			boxpoint = TOPOROUTER_VERTEX(vertex_bbox(op_v)->point);
+
+		if (g_list_find(routedata->destvertices, op_v)) {
+			rval = g_list_prepend(rval, op_v);
+			*dest = op_v;
+		}
+		else if (g_list_find(routedata->destvertices, boxpoint)) {
+			*dest = boxpoint;
+		}
+		else if (g_list_find(routedata->srcvertices, op_v)) {
+			rval = g_list_prepend(rval, op_v);
+		}
+	}
+
+triangle_candidate_points_e2:
+
+	if (noe2) {
+/*    printf("noe2\n"); */
+		goto triangle_candidate_points_finish;
+	}
+
+	if (TOPOROUTER_IS_CONSTRAINT(e2)) {
+		GList *i = edge_routing(e2);
+
+		if (TOPOROUTER_CONSTRAINT(e2)->box->type == BOARD) {
+			noe2 = 1;
+/*      goto triangle_candidate_points_finish; */
+		}
+		else if (!constraint_route_test(TOPOROUTER_CONSTRAINT(e2), routedata)) {
+#ifdef DEBUG_ROUTE
+			printf("noe2 netlist\n");
+#endif
+			noe2 = 1;
+/*      goto triangle_candidate_points_finish; */
+		}
+		else if (v2 == tedge_v2(e) ||
+						 (v2->parent->routingedge && v2->parent->routingedge == e2) ||
+						 (v2->child->routingedge && v2->child->routingedge == e2)) {
+
+			e2constraintv =
+				new_temp_toporoutervertex_in_segment(e2, tedge_v1(e2),
+																						 gts_point_distance(GTS_POINT(edge_v1(e2)), GTS_POINT(edge_v2(e2))) / 2.,
+																						 tedge_v2(e2));
+
+		}
+
+		while (i) {
+			toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data);
+
+			if ((temp->child == tedge_v1(e) || temp->parent == tedge_v1(e)) && !(temp->flags & VERTEX_FLAG_TEMP))
+				noe1 = 1;
+
+			i = i->next;
+		}
+
+
+
+		goto triangle_candidate_points_finish;
+	}
+
+	if (edge_is_blocked(e2)) {
+		noe2 = 1;
+		goto triangle_candidate_points_finish;
+	}
+
+	if (v2 == tedge_v2(e)) {
+		/* continue up e2 */
+		toporouter_vertex_t *vv1 = NULL, *vv2 = NULL;
+		edge_adjacent_vertices(e2, v2, &vv1, &vv2);
+
+#ifdef DEBUG_ROUTE
+		printf("v2 == e->v2\n");
+#endif
+
+		if (vv1) {
+			/* candidates from v2 until vv1 */
+			vv = vv1;
+		}
+		else {
+			/* candidates from v2 until vv2 */
+			vv = vv2;
+		}
+
+		if (!e2constraintv)
+			e2cands = candidate_vertices(v2, vv, *dest, e2);
+
+		if (vv != op_v) {
+			if (vv->parent == tedge_v1(e) || vv->child == tedge_v1(e)) {
+#ifdef DEBUG_ROUTE
+				printf("noe1 0\n");
+#endif
+				noe1 = 1;
+			}
+		}
+
+	}
+	else if (v2->parent != op_v && v2->child != op_v) {
+		toporouter_vertex_t *vv1 = NULL, *vv2 = NULL;
+
+#ifdef DEBUG_ROUTE
+		printf("v2 == e->v2\n");
+#endif
+
+		if (v2->parent->routingedge == e2) {
+			vv1 = v2->parent;
+			if (op_v == tedge_v1(e2)) {
+				/* candidates from v2->parent until prev vertex  */
+				vv2 = edge_routing_prev_not_temp(e2, g_list_find(edge_routing(e2), vv1)->prev);
+			}
+			else {
+				/* candidates from v2->parent until next vertex  */
+				vv2 = edge_routing_next_not_temp(e2, g_list_find(edge_routing(e2), vv1)->next);
+			}
+
+		}
+		else if (v2->child->routingedge == e2) {
+			vv1 = v2->child;
+			if (op_v == tedge_v1(e2)) {
+				/* candidates from v2->child until prev vertex  */
+				vv2 = edge_routing_prev_not_temp(e2, g_list_find(edge_routing(e2), vv1)->prev);
+			}
+			else {
+				/* candidates from v2->child until next vertex  */
+				vv2 = edge_routing_next_not_temp(e2, g_list_find(edge_routing(e2), vv1)->next);
+			}
+
+		}
+		else {
+			goto triangle_candidate_points_finish;
+		}
+
+		if (vv1 && vv2) {
+			if (vv2->parent == tedge_v1(e) || vv2->child == tedge_v1(e)) {
+#ifdef DEBUG_ROUTE
+				printf("noe1 1\n");
+#endif
+				noe1 = 1;
+			}
+
+			if (!e2constraintv)
+				e2cands = candidate_vertices(vv1, vv2, *dest, e2);
+		}
+	}
+
+triangle_candidate_points_finish:
+
+	v1 = segment_common_vertex(GTS_SEGMENT(e), GTS_SEGMENT(e1));
+	v2 = segment_common_vertex(GTS_SEGMENT(e), GTS_SEGMENT(e2));
+
+	if (noe1 || !check_triangle_interior_capacity(t, v1, v, e2, e, e1)) {
+#ifdef DEBUG_ROUTE
+		printf("freeing e1cands\n");
+#endif
+		routedata_insert_temppoints(routedata, e1cands);
+		g_list_free(e1cands);
+		e1cands = NULL;
+	}
+
+	if (noe2 || !check_triangle_interior_capacity(t, v2, v, e1, e, e2)) {
+#ifdef DEBUG_ROUTE
+		printf("freeing e2cands\n");
+#endif
+		routedata_insert_temppoints(routedata, e2cands);
+		g_list_free(e2cands);
+		e2cands = NULL;
+	}
+
+	if (!noe1 && e1constraintv) {
+		e1cands = g_list_prepend(e1cands, e1constraintv);
+	}
+	else if (e1constraintv) {
+		g_hash_table_insert(routedata->alltemppoints, e1constraintv, e1constraintv);
+/*    delete_vertex(e1constraintv); */
+	}
+
+	if (!noe2 && e2constraintv) {
+		e2cands = g_list_prepend(e2cands, e2constraintv);
+	}
+	else if (e2constraintv) {
+		g_hash_table_insert(routedata->alltemppoints, e2constraintv, e2constraintv);
+/*    delete_vertex(e2constraintv); */
+	}
+
+	if (!noe1 && !noe2)
+		return g_list_concat(rval, g_list_concat(e1cands, e2cands));
+
+	return g_list_concat(e1cands, e2cands);
+}
+
+GList *compute_candidate_points(toporouter_t * tr, toporouter_layer_t * l, toporouter_vertex_t * curpoint,
+																toporouter_route_t * data, toporouter_vertex_t ** closestdest)
+{
+	GList *r = NULL, *j;
+	toporouter_edge_t *edge = curpoint->routingedge, *tempedge;
+
+	if (vertex_keepout_test(tr, curpoint))
+		goto compute_candidate_points_finish;
+
+	/* direct connection */
+/*  if(curpoint == TOPOROUTER_VERTEX(data->src->point)) */
+	if ((tempedge = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(curpoint), GTS_VERTEX(*closestdest))))) {
+
+		if (TOPOROUTER_IS_CONSTRAINT(tempedge)) {
+			goto compute_candidate_points_finish;
+		}
+		else {
+			if (!tempedge->routing) {
+				r = g_list_prepend(NULL, *closestdest);
+				tempedge->flags |= EDGE_FLAG_DIRECTCONNECTION;
+				goto compute_candidate_points_finish;
+			}
+			else {
+#ifdef DEBUG_ROUTE
+				printf("Direct connection, but has routing\n");
+#endif
+			}
+
+		}
+		/* if we get to here, there is routing blocking the direct connection,
+		 * continue as per normal */
+	}
+
+	/* a real point origin */
+	if (!(curpoint->flags & VERTEX_FLAG_TEMP)) {
+		GSList *triangles, *i;
+		i = triangles = gts_vertex_triangles(GTS_VERTEX(curpoint), NULL);
+#ifdef DEBUG_ROUTE
+		printf("triangle count = %d\n", g_slist_length(triangles));
+#endif
+		while (i) {
+			GtsTriangle *t = GTS_TRIANGLE(i->data);
+			GList *temppoints;
+
+			if (tr->flags & TOPOROUTER_FLAG_LEASTINVALID)
+				temppoints = triangle_all_candidate_points_from_vertex(t, curpoint, data);
+			else
+				temppoints = triangle_candidate_points_from_vertex(t, curpoint, *closestdest, data);
+
+#ifdef DEBUG_ROUTE
+			printf("\treturned %d points\n", g_list_length(temppoints));
+#endif
+			routedata_insert_temppoints(data, temppoints);
+
+			r = g_list_concat(r, temppoints);
+			i = i->next;
+		}
+		g_slist_free(triangles);
+	}
+	else {												/* a temp point */
+
+		int prevwind = vertex_wind(GTS_SEGMENT(edge)->v1, GTS_SEGMENT(edge)->v2, GTS_VERTEX(curpoint->parent));
+/*    printf("tempoint\n"); */
+
+		GSList *i = GTS_EDGE(edge)->triangles;
+
+		while (i) {
+			GtsVertex *oppv = gts_triangle_vertex_opposite(GTS_TRIANGLE(i->data), GTS_EDGE(edge));
+			if (prevwind != vertex_wind(GTS_SEGMENT(edge)->v1, GTS_SEGMENT(edge)->v2, oppv)) {
+				GList *temppoints;
+
+				if (tr->flags & TOPOROUTER_FLAG_LEASTINVALID)
+					temppoints = triangle_all_candidate_points_from_edge(tr, GTS_TRIANGLE(i->data), edge, data, closestdest, curpoint);
+				else
+					temppoints = triangle_candidate_points_from_edge(tr, GTS_TRIANGLE(i->data), edge, curpoint, closestdest, data);
+
+				j = temppoints;
+				while (j) {
+					toporouter_vertex_t *tempj = TOPOROUTER_VERTEX(j->data);
+					if (tempj->flags & VERTEX_FLAG_TEMP)
+						g_hash_table_insert(data->alltemppoints, j->data, j->data);
+#ifdef DEBUG_ROUTE
+					else
+						printf("got cand not a temp\n");
+#endif
+					j = j->next;
+				}
+				r = g_list_concat(r, temppoints);
+
+				break;
+			}
+			i = i->next;
+		}
+	}
+
+compute_candidate_points_finish:
+
+	if (vertex_bbox(curpoint) && vertex_bbox(curpoint)->cluster) {
+		if (vertex_bbox(curpoint)->cluster->c == data->src->c) {
+			r = g_list_concat(r, g_list_copy(data->srcvertices));
+		}
+	}
+
+	return r;
+}
+
+gboolean temp_point_clean(gpointer key, gpointer value, gpointer user_data)
+{
+	toporouter_vertex_t *tv = TOPOROUTER_VERTEX(value);
+	if (tv->flags & VERTEX_FLAG_TEMP) {
+		if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge))
+			TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv);
+		else
+			tv->routingedge->routing = g_list_remove(tv->routingedge->routing, tv);
+		gts_object_destroy(GTS_OBJECT(tv));
+	}
+	return TRUE;
+}
+
+void clean_routing_edges(toporouter_t * r, toporouter_route_t * data)
+{
+	g_hash_table_foreach_remove(data->alltemppoints, temp_point_clean, NULL);
+	g_hash_table_destroy(data->alltemppoints);
+	data->alltemppoints = NULL;
+}
+
+gdouble path_score(toporouter_t * r, GList * path)
+{
+	gdouble score = 0.;
+	toporouter_vertex_t *pv = NULL;
+	toporouter_vertex_t *v0 = NULL;
+
+	if (!path)
+		return INFINITY;
+
+	v0 = TOPOROUTER_VERTEX(path->data);
+
+	while (path) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(path->data);
+
+		if (pv) {
+			score += gts_point_distance(GTS_POINT(pv), GTS_POINT(v));
+			if (pv != v0 && vz(pv) != vz(v))
+				if (path->next)
+					score += r->viacost;
+
+		}
+
+		pv = v;
+		path = path->next;
+	}
+
+	return score;
+}
+
+void print_vertices(GList * vertices)
+{
+	while (vertices) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(vertices->data);
+		print_vertex(v);
+		print_bbox(vertex_bbox(v));
+		if (vertex_bbox(v)) {
+			printf("has bbox\n");
+			if (vertex_bbox(v)->cluster)
+				printf("has cluster\n");
+			else
+				printf("no cluster\n");
+		}
+		else
+			printf("no bbox\n");
+		vertices = vertices->next;
+	}
+}
+
+gint space_edge(gpointer item, gpointer data)
+{
+	toporouter_edge_t *e = TOPOROUTER_EDGE(item);
+	GList *i;
+	gdouble *forces;
+	guint j;
+
+	if (TOPOROUTER_IS_CONSTRAINT(e))
+		return 0;
+
+	if (!edge_routing(e) || !g_list_length(edge_routing(e)))
+		return 0;
+
+	forces = (gdouble *) malloc(sizeof(double) * g_list_length(edge_routing(e)));
+
+	for (j = 0; j < 100; j++) {
+		guint k = 0;
+		guint equilibrium = 1;
+
+		i = edge_routing(e);
+		while (i) {
+			toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+			gdouble ms, d;
+
+			if (i->prev) {
+/*        ms = min_net_net_spacing(TOPOROUTER_VERTEX(i->prev->data), v); */
+				ms = min_spacing(TOPOROUTER_VERTEX(i->prev->data), v);
+				d = gts_point_distance(GTS_POINT(i->prev->data), GTS_POINT(v));
+			}
+			else {
+/*        ms = min_vertex_net_spacing(v, tedge_v1(e)); */
+				ms = min_spacing(v, tedge_v1(e));
+				d = gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(v));
+			}
+
+			if (d < ms)
+				forces[k] = ms - d;
+			else
+				forces[k] = 0.;
+
+			if (i->next) {
+/*        ms = min_net_net_spacing(TOPOROUTER_VERTEX(i->next->data), v); */
+				ms = min_spacing(TOPOROUTER_VERTEX(i->next->data), v);
+				d = gts_point_distance(GTS_POINT(i->next->data), GTS_POINT(v));
+			}
+			else {
+/*        ms = min_vertex_net_spacing(v, tedge_v2(e)); */
+				ms = min_spacing(v, tedge_v2(e));
+				d = gts_point_distance(GTS_POINT(edge_v2(e)), GTS_POINT(v));
+			}
+
+			if (d < ms)
+				forces[k] += d - ms;
+
+			k++;
+			i = i->next;
+		}
+
+		k = 0;
+		i = edge_routing(e);
+		while (i) {
+			toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+			if (forces[k] > EPSILON || forces[k] < -EPSILON)
+				equilibrium = 0;
+			vertex_move_towards_vertex_values(GTS_VERTEX(v), edge_v2(e), forces[k] * 0.1, &(GTS_POINT(v)->x), &(GTS_POINT(v)->y));
+			k++;
+			i = i->next;
+		}
+
+		if (equilibrium) {
+/*      printf("reached equilibriium at %d\n", j); */
+			break;
+		}
+
+	}
+
+	free(forces);
+	return 0;
+}
+
+void swap_vertices(toporouter_vertex_t ** v1, toporouter_vertex_t ** v2)
+{
+	toporouter_vertex_t *tempv = *v1;
+	*v1 = *v2;
+	*v2 = tempv;
+}
+
+void split_edge_routing(toporouter_vertex_t * v, GList ** l1, GList ** l2)
+{
+	GList *base, *i;
+
+	g_assert(v);
+	g_assert(v->routingedge);
+
+	base = g_list_find(vrouting(v), v);
+
+	*l1 = g_list_prepend(*l1, tedge_v1(v->routingedge));
+	*l2 = g_list_prepend(*l2, tedge_v2(v->routingedge));
+
+	g_assert(base);
+
+	i = base->next;
+	while (i) {
+		if (!(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_TEMP))
+			*l2 = g_list_prepend(*l2, i->data);
+		i = i->next;
+	}
+
+	i = base->prev;
+	while (i) {
+		if (!(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_TEMP))
+			*l1 = g_list_prepend(*l1, i->data);
+		i = i->prev;
+	}
+}
+
+GList *vertices_routing_conflicts(toporouter_vertex_t * v, toporouter_vertex_t * pv)
+{
+	toporouter_edge_t *e;
+	GList *rval = NULL, *l1 = NULL, *l2 = NULL, *i;
+
+	if (vz(v) != vz(pv))
+		return NULL;
+	g_assert(v != pv);
+
+	if (!v->routingedge && !pv->routingedge) {
+		e = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v), GTS_VERTEX(pv)));
+		if (!e)
+			return NULL;
+		i = edge_routing(e);
+		while (i) {
+			rval = g_list_prepend(rval, TOPOROUTER_VERTEX(i->data)->route);
+			i = i->next;
+		}
+		return rval;
+	}
+
+	if (TOPOROUTER_IS_CONSTRAINT(v->routingedge) && TOPOROUTER_IS_CONSTRAINT(pv->routingedge))
+		return NULL;
+
+	if (TOPOROUTER_IS_CONSTRAINT(pv->routingedge))
+		swap_vertices(&pv, &v);
+
+	if (!v->routingedge)
+		swap_vertices(&pv, &v);
+
+	e = v->routingedge;
+
+	split_edge_routing(v, &l1, &l2);
+	g_assert(l2);
+	g_assert(l1);
+
+	if (!pv->routingedge) {
+		toporouter_edge_t *e1, *e2;
+		e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(pv), edge_v1(e)));
+		e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(pv), edge_v2(e)));
+
+		l1 = g_list_concat(l1, g_list_copy(edge_routing(e1)));
+		l2 = g_list_concat(l2, g_list_copy(edge_routing(e2)));
+
+	}
+	else {
+		GList *pvlist1 = NULL, *pvlist2 = NULL;
+		toporouter_vertex_t *commonv = route_vertices_common_vertex(v, pv);
+
+		g_assert(commonv);
+
+		split_edge_routing(pv, &pvlist1, &pvlist2);
+
+		if (commonv == tedge_v1(e)) {
+			toporouter_edge_t *ope;
+
+			if (commonv == tedge_v1(pv->routingedge)) {
+				l1 = g_list_concat(l1, pvlist1);
+				l2 = g_list_concat(l2, pvlist2);
+				ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v2(e), edge_v2(pv->routingedge)));
+			}
+			else {
+				l1 = g_list_concat(l1, pvlist2);
+				l2 = g_list_concat(l2, pvlist1);
+				ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v2(e), edge_v1(pv->routingedge)));
+			}
+			g_assert(ope);
+			l2 = g_list_concat(l2, g_list_copy(edge_routing(ope)));
+
+		}
+		else {
+			toporouter_edge_t *ope;
+			if (commonv == tedge_v1(pv->routingedge)) {
+				l1 = g_list_concat(l1, pvlist2);
+				l2 = g_list_concat(l2, pvlist1);
+				ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v1(e), edge_v2(pv->routingedge)));
+			}
+			else {
+				l1 = g_list_concat(l1, pvlist1);
+				l2 = g_list_concat(l2, pvlist2);
+				ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v1(e), edge_v1(pv->routingedge)));
+			}
+			g_assert(ope);
+			l1 = g_list_concat(l1, g_list_copy(edge_routing(ope)));
+		}
+	}
+
+	i = l1;
+	while (i) {
+		toporouter_vertex_t *curv = TOPOROUTER_VERTEX(i->data);
+
+		if (curv->flags & VERTEX_FLAG_ROUTE && (g_list_find(l2, curv->parent) || g_list_find(l2, curv->child))) {
+			if (!g_list_find(rval, curv->route))
+				rval = g_list_prepend(rval, curv->route);
+		}
+		i = i->next;
+	}
+	i = l2;
+	while (i) {
+		toporouter_vertex_t *curv = TOPOROUTER_VERTEX(i->data);
+
+		if (curv->flags & VERTEX_FLAG_ROUTE && (g_list_find(l1, curv->parent) || g_list_find(l1, curv->child))) {
+			if (!g_list_find(rval, curv->route))
+				rval = g_list_prepend(rval, curv->route);
+		}
+		i = i->next;
+	}
+
+	g_list_free(l1);
+	g_list_free(l2);
+
+	return rval;
+}
+
+gdouble vertices_routing_conflict_cost(toporouter_t * r, toporouter_vertex_t * v, toporouter_vertex_t * pv, guint * n)
+{
+	GList *conflicts = vertices_routing_conflicts(v, pv), *i;
+	gdouble penalty = 0.;
+
+	i = conflicts;
+	while (i) {
+		(*n) += 1;
+		penalty += TOPOROUTER_ROUTE(i->data)->score;
+		i = i->next;
+	}
+	g_list_free(conflicts);
+/*  if(penalty > 0.) printf("conflict penalty of %f with %f,%f %f,%f\n", penalty, vx(v), vy(v), vx(pv), vy(pv)); */
+	return penalty;
+}
+
+gdouble
+gcost(toporouter_t * r, toporouter_route_t * data, toporouter_vertex_t * srcv, toporouter_vertex_t * v,
+			toporouter_vertex_t * pv, guint * n, toporouter_netlist_t * pair)
+{
+	gdouble cost = 0., segcost;
+
+	*n = pv->gn;
+
+	if (g_list_find(data->srcvertices, v))
+		return 0.;
+
+	segcost = tvdistance(pv, v);
+
+	if (pair && !TOPOROUTER_IS_CONSTRAINT(v->routingedge) && v->routingedge) {
+		GList *list = g_list_find(v->routingedge->routing, v);
+		toporouter_vertex_t *pv = edge_routing_prev_not_temp(v->routingedge, list);
+		toporouter_vertex_t *nv = edge_routing_next_not_temp(v->routingedge, list);
+
+		if (pv->route && pv->route->netlist == pair) {
+		}
+		else if (nv->route && nv->route->netlist == pair) {
+		}
+		else {
+			segcost *= 10.;
+		}
+	}
+
+	cost = pv->gcost + segcost;
+
+	if (r->flags & TOPOROUTER_FLAG_LEASTINVALID) {
+		gdouble conflictcost = 0.;
+
+		if (pv && v != pv && vz(v) == vz(pv))
+			conflictcost = vertices_routing_conflict_cost(r, v, pv, n);
+
+		if (!(r->flags & TOPOROUTER_FLAG_DETOUR && *n == 1)) {
+			cost += conflictcost * (pow(*n, 2));
+		}
+	}
+
+	return cost;
+}
+
+#define vlayer(x) (&r->layers[(int)vz(x)])
+
+guint candidate_is_available(toporouter_vertex_t * pv, toporouter_vertex_t * v)
+{
+	/* TODO: still needed?   */
+	while (pv) {
+		if (pv == v)
+			return 0;
+		pv = pv->parent;
+	}
+
+	return 1;
+}
+
+GList *route(toporouter_t * r, toporouter_route_t * data, guint debug)
+{
+	GtsEHeap *openlist = gts_eheap_new(route_heap_cmp, NULL);
+	GList *closelist = NULL;
+	GList *i, *rval = NULL;
+	toporouter_netlist_t *pair = NULL;
+	gint count = 0;
+
+	toporouter_vertex_t *srcv = NULL, *destv = NULL, *curpoint = NULL;
+	toporouter_layer_t *cur_layer;	/*, *dest_layer; */
+
+	g_assert(data->src->c != data->dest->c);
+
+	if (data->destvertices)
+		g_list_free(data->destvertices);
+	if (data->srcvertices)
+		g_list_free(data->srcvertices);
+
+	data->destvertices = cluster_vertices(r, data->dest);
+	data->srcvertices = cluster_vertices(r, data->src);
+
+	closest_cluster_pair(r, data->srcvertices, data->destvertices, &curpoint, &destv);
+
+	if (!curpoint || !destv)
+		goto routing_return;
+
+	srcv = curpoint;
+	cur_layer = vlayer(curpoint);
+	/*dest_layer = vlayer(destv); */
+
+	data->path = NULL;
+
+	data->alltemppoints = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+	curpoint->parent = NULL;
+	curpoint->child = NULL;
+	curpoint->gcost = 0.;
+	curpoint->gn = 0;
+	curpoint->hcost = simple_h_cost(r, curpoint, destv);
+
+	if (data->netlist && data->netlist->pair) {
+		GList *i = r->routednets;
+		while (i) {
+			toporouter_route_t *curroute = TOPOROUTER_ROUTE(i->data);
+			if (curroute->netlist == data->netlist->pair) {
+				pair = data->netlist->pair;
+				break;
+			}
+			i = i->next;
+		}
+	}
+
+	gts_eheap_insert(openlist, curpoint);
+
+	while (gts_eheap_size(openlist) > 0) {
+		GList *candidatepoints;
+		data->curpoint = curpoint;
+		/*draw_route_status(r, closelist, openlist, curpoint, data, count++); */
+
+		curpoint = TOPOROUTER_VERTEX(gts_eheap_remove_top(openlist, NULL));
+		if (curpoint->parent && !(curpoint->flags & VERTEX_FLAG_TEMP)) {
+			if (vz(curpoint) != vz(destv)) {
+				toporouter_vertex_t *tempv;
+				cur_layer = vlayer(curpoint);	/*&r->layers[(int)vz(curpoint)]; */
+				tempv = closest_dest_vertex(r, curpoint, data);
+				if (tempv) {
+					destv = tempv;
+					/*dest_layer = vlayer(destv);//&r->layers[(int)vz(destv)]; */
+
+				}
+			}
+		}
+
+/*    destpoint = closest_dest_vertex(r, curpoint, data);
+      dest_layer = &r->layers[(int)vz(destpoint)];*/
+
+		if (g_list_find(data->destvertices, curpoint)) {
+			toporouter_vertex_t *temppoint = curpoint;
+			srcv = NULL;
+			destv = curpoint;
+
+			data->path = NULL;
+
+			while (temppoint) {
+				data->path = g_list_prepend(data->path, temppoint);
+				if (g_list_find(data->srcvertices, temppoint)) {
+					srcv = temppoint;
+					if (r->flags & TOPOROUTER_FLAG_AFTERORDER)
+						break;
+				}
+				temppoint = temppoint->parent;
+			}
+			rval = data->path;
+			data->score = path_score(r, data->path);
+#ifdef DEBUG_ROUTE
+			printf("ROUTE: path score = %f computation cost = %d\n", data->score, count);
+#endif
+
+			if (srcv->bbox->cluster != data->src) {
+				data->src = srcv->bbox->cluster;
+			}
+
+			if (destv->bbox->cluster != data->dest) {
+				data->dest = destv->bbox->cluster;
+			}
+			goto route_finish;
+		}
+		closelist_insert(curpoint);
+#ifdef DEBUG_ROUTE
+		printf("\n\n\n*** ROUTE COUNT = %d\n", count);
+#endif
+		candidatepoints = compute_candidate_points(r, cur_layer, curpoint, data, &destv);
+
+/*#ifdef DEBUG_ROUTE    */
+		/*********************
+    if(debug && !strcmp(data->dest->netlist, "  unnamed_net2")) 
+    {
+      unsigned int mask = ~(VERTEX_FLAG_RED | VERTEX_FLAG_GREEN | VERTEX_FLAG_BLUE); 
+      char buffer[256];
+      int j;
+
+      for(j=0;j<groupcount();j++) {
+        i = r->layers[j].vertices;
+        while(i) {
+          TOPOROUTER_VERTEX(i->data)->flags &= mask;
+          i = i->next;
+        }
+      }
+      
+      i = candidatepoints;
+      while(i) {
+        TOPOROUTER_VERTEX(i->data)->flags |= VERTEX_FLAG_GREEN;
+//        printf("flagged a candpoint @ %f,%f\n",
+//            vx(i->data), vy(i->data));
+        i = i->next;
+      }
+      
+      curpoint->flags |= VERTEX_FLAG_BLUE;
+      if(curpoint->parent) 
+        curpoint->parent->flags |= VERTEX_FLAG_RED;
+
+
+      for(j=0;j<groupcount();j++) {
+        GList *datas = g_list_prepend(NULL, data);
+        sprintf(buffer, "route-%d-%05d.png", j, count);
+        toporouter_draw_surface(r, r->layers[j].surface, buffer, 1024, 1024, 2, datas, j, candidatepoints);
+        g_list_free(datas);
+      }
+    }
+//#endif    
+    *********************/
+		count++;
+/*    if(count > 100) exit(0);*/
+		i = candidatepoints;
+		while (i) {
+			toporouter_vertex_t *temppoint = TOPOROUTER_VERTEX(i->data);
+			if (!g_list_find(closelist, temppoint) && candidate_is_available(curpoint, temppoint)) {	/*&& temppoint != curpoint) { */
+				toporouter_heap_search_data_t heap_search_data = { temppoint, NULL };
+
+				guint temp_gn;
+				gdouble temp_g_cost = gcost(r, data, srcv, temppoint, curpoint, &temp_gn, pair);
+
+
+				gts_eheap_foreach(openlist, toporouter_heap_search, &heap_search_data);
+
+				if (heap_search_data.result) {
+					if (temp_g_cost < temppoint->gcost) {
+
+						temppoint->gcost = temp_g_cost;
+						temppoint->gn = temp_gn;
+
+						temppoint->parent = curpoint;
+						curpoint->child = temppoint;
+
+						gts_eheap_update(openlist);
+					}
+				}
+				else {
+					temppoint->parent = curpoint;
+					curpoint->child = temppoint;
+
+					temppoint->gcost = temp_g_cost;
+					temppoint->gn = temp_gn;
+
+					temppoint->hcost = simple_h_cost(r, temppoint, destv);
+/*          if(cur_layer != dest_layer) temppoint->hcost += r->viacost;*/
+					gts_eheap_insert(openlist, temppoint);
+				}
+
+			}
+			i = i->next;
+		}
+		g_list_free(candidatepoints);
+
+	}
+#ifdef DEBUG_ROUTE
+	printf("ROUTE: could not find path!\n");
+#endif
+
+	data->score = INFINITY;
+	clean_routing_edges(r, data);
+
+	data->path = NULL;
+	/*TOPOROUTER_VERTEX(data->src->point)->parent = NULL;
+	   TOPOROUTER_VERTEX(data->src->point)->child  = NULL; */
+	goto routing_return;
+
+/* 
+  {
+    int i;
+    for(i=0;i<groupcount();i++) {
+      char buffer[256];
+      sprintf(buffer, "route-error-%d-%d.png", r->routecount, i);
+      toporouter_draw_surface(r, r->layers[i].surface, buffer, 1280, 1280, 2, data, i, NULL);
+    }
+    r->routecount++;
+  }
+    exit(0);
+*/
+route_finish:
+/*  printf(" * finished a*\n");*/
+/* 
+  {
+    int i;
+    for(i=0;i<groupcount();i++) {
+      char buffer[256];
+      sprintf(buffer, "route-preclean-%d-%d.png", i, r->routecount);
+      toporouter_draw_surface(r, r->layers[i].surface, buffer, 1024, 1024, 2, data, i, NULL);
+    }
+    r->routecount++;
+  }
+*/
+/*  {
+    i = data->path;
+    while(i) {
+      toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+      
+      if(tv->routingedge) {
+        GList *list = g_list_find(edge_routing(tv->routingedge), tv);
+        toporouter_vertex_t *restartv = NULL, *boxpoint;
+
+        g_assert(list);
+
+        if(!list->next) {
+          if(vertex_bbox(tedge_v2(tv->routingedge))) 
+            boxpoint = TOPOROUTER_VERTEX(vertex_bbox(tedge_v2(tv->routingedge))->point);
+          else
+            boxpoint = NULL;
+
+          if(tedge_v2(tv->routingedge) != srcv && g_list_find(data->srcvertices, tedge_v2(tv->routingedge))) 
+            restartv = tedge_v2(tv->routingedge);
+          else if(boxpoint != srcv && g_list_find(data->srcvertices, boxpoint)) 
+            restartv = boxpoint;
+        }
+        
+        if(!list->prev) {
+          if(vertex_bbox(tedge_v1(tv->routingedge))) 
+            boxpoint = TOPOROUTER_VERTEX(vertex_bbox(tedge_v1(tv->routingedge))->point);
+          else
+            boxpoint = NULL;
+
+          if(tedge_v1(tv->routingedge) != srcv && g_list_find(data->srcvertices, tedge_v1(tv->routingedge))) 
+            restartv = tedge_v1(tv->routingedge);
+          else if(boxpoint != srcv && g_list_find(data->srcvertices, boxpoint)) 
+            restartv = boxpoint;
+          
+        }
+
+        if(restartv) {
+          clean_routing_edges(r, data); 
+          gts_eheap_destroy(openlist);     
+          g_list_free(closelist);
+          openlist = gts_eheap_new(route_heap_cmp, NULL);
+          closelist = NULL;
+          g_list_free(data->path);
+          printf("ROUTING RESTARTING with new src %f,%f,%f\n", vx(restartv), vy(restartv), vz(restartv));
+          curpoint = restartv;
+          goto route_begin;
+        }
+      }
+      
+      i = i->next;
+    }
+  }*/
+
+	{
+		toporouter_vertex_t *pv = NULL;
+		GList *i = data->path;
+		while (i) {
+			toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+
+			if (pv && g_list_find(data->srcvertices, tv)) {
+				GList *temp = g_list_copy(i);
+				g_list_free(data->path);
+				data->path = temp;
+				i = data->path;
+			}
+			pv = tv;
+			i = i->next;
+		}
+	}
+
+	{
+		toporouter_vertex_t *pv = NULL;
+		GList *i = data->path;
+		while (i) {
+			toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+			if (tv->flags & VERTEX_FLAG_TEMP) {
+				tv->flags ^= VERTEX_FLAG_TEMP;
+				tv->flags |= VERTEX_FLAG_ROUTE;
+			}
+			if (pv)
+				pv->child = tv;
+
+			if (tv->routingedge)
+				tv->route = data;
+
+/*      if(tv->routingedge && !TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) space_edge(tv->routingedge, NULL);*/
+
+			pv = tv;
+			i = i->next;
+		}
+	}
+
+	{
+		toporouter_vertex_t *pv = NULL, *v = NULL;
+
+		GList *i = data->path;
+		while (i) {
+			v = TOPOROUTER_VERTEX(i->data);
+
+			if (pv) {
+				v->parent = pv;
+				pv->child = v;
+			}
+			else {
+				v->parent = NULL;
+			}
+
+			pv = v;
+			i = i->next;
+		}
+
+		if (v)
+			v->child = NULL;
+	}
+
+	clean_routing_edges(r, data);
+	{
+		GList *i = data->path;
+		while (i) {
+			toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+			if (v->routingedge && !TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+				space_edge(v->routingedge, NULL);
+			i = i->next;
+		}
+	}
+routing_return:
+
+	g_list_free(data->destvertices);
+	g_list_free(data->srcvertices);
+	data->destvertices = NULL;
+	data->srcvertices = NULL;
+	gts_eheap_destroy(openlist);
+	g_list_free(closelist);
+
+	data->alltemppoints = NULL;
+
+	return rval;
+}
+
+/* moves vertex v d units in the direction of vertex p */
+void vertex_move_towards_point(GtsVertex * v, gdouble px, gdouble py, gdouble d)
+{
+	gdouble dx = px - GTS_POINT(v)->x;
+	gdouble dy = py - GTS_POINT(v)->y;
+	gdouble theta = atan(fabs(dy / dx));
+
+	g_assert(finite(theta));
+
+	if (dx >= 0.) {
+
+		if (dy >= 0.) {
+			GTS_POINT(v)->x += d * cos(theta);
+			GTS_POINT(v)->y += d * sin(theta);
+		}
+		else {
+			GTS_POINT(v)->x += d * cos(theta);
+			GTS_POINT(v)->y -= d * sin(theta);
+		}
+
+	}
+	else {
+
+		if (dy >= 0.) {
+			GTS_POINT(v)->x -= d * cos(theta);
+			GTS_POINT(v)->y += d * sin(theta);
+		}
+		else {
+			GTS_POINT(v)->x -= d * cos(theta);
+			GTS_POINT(v)->y -= d * sin(theta);
+		}
+
+	}
+
+}
+
+/* moves vertex v d units in the direction of vertex p */
+void vertex_move_towards_vertex(GtsVertex * v, GtsVertex * p, gdouble d)
+{
+	gdouble dx = GTS_POINT(p)->x - GTS_POINT(v)->x;
+	gdouble dy = GTS_POINT(p)->y - GTS_POINT(v)->y;
+	gdouble theta = atan(fabs(dy / dx));
+
+	g_assert(finite(theta));
+
+	if (dx >= 0.) {
+
+		if (dy >= 0.) {
+			GTS_POINT(v)->x += d * cos(theta);
+			GTS_POINT(v)->y += d * sin(theta);
+		}
+		else {
+			GTS_POINT(v)->x += d * cos(theta);
+			GTS_POINT(v)->y -= d * sin(theta);
+		}
+
+	}
+	else {
+
+		if (dy >= 0.) {
+			GTS_POINT(v)->x -= d * cos(theta);
+			GTS_POINT(v)->y += d * sin(theta);
+		}
+		else {
+			GTS_POINT(v)->x -= d * cos(theta);
+			GTS_POINT(v)->y -= d * sin(theta);
+		}
+
+	}
+
+}
+
+
+gdouble pathvertex_arcing_through_constraint(toporouter_vertex_t * pathv, toporouter_vertex_t * arcv)
+{
+	toporouter_vertex_t *v = pathv->child;
+
+	if (!v || !v->routingedge)
+		return 0.;
+
+	while (v->flags & VERTEX_FLAG_ROUTE && (tedge_v1(v->routingedge) == arcv || tedge_v2(v->routingedge) == arcv)) {
+		if (TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+			return gts_point_distance(GTS_POINT(tedge_v1(v->routingedge)), GTS_POINT(tedge_v2(v->routingedge)));
+		v = v->child;
+	}
+
+	v = pathv->parent;
+	while (v->flags & VERTEX_FLAG_ROUTE && (tedge_v1(v->routingedge) == arcv || tedge_v2(v->routingedge) == arcv)) {
+		if (TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+			return gts_point_distance(GTS_POINT(tedge_v1(v->routingedge)), GTS_POINT(tedge_v2(v->routingedge)));
+		v = v->parent;
+	}
+
+	return 0.;
+}
+
+gint vertices_connected(toporouter_vertex_t * a, toporouter_vertex_t * b)
+{
+	return ((a->route->netlist == b->route->netlist && a->route->src->c == b->route->src->c) ? 1 : 0);
+}
+
+gdouble edge_min_spacing(GList * list, toporouter_edge_t * e, toporouter_vertex_t * v, guint debug)
+{
+	toporouter_vertex_t *origin;
+	GList *i = list;
+	gdouble space = 0.;
+	toporouter_vertex_t *nextv, *prevv;
+	/*toporouter_vertex_t *edgev;
+	   gdouble constraint_spacing; */
+
+	if (!list)
+		return INFINITY;
+
+/*  printf("\t CMS %f,%f - %f,%f\n", vx(tedge_v1(e)), vy(tedge_v1(e)), vx(tedge_v2(e)), vy(tedge_v2(e))); */
+
+	prevv = origin = TOPOROUTER_VERTEX(list->data);
+
+/*  print_edge(e); */
+
+	i = list;
+	if (gts_point_distance2(GTS_POINT(origin), GTS_POINT(edge_v1(e))) < gts_point_distance2(GTS_POINT(v), GTS_POINT(edge_v1(e)))) {
+
+		/* towards v2 */
+		while (i) {
+			nextv = edge_routing_next(e, i);
+			if (nextv->route && vertices_connected(nextv, prevv)) {
+				i = i->next;
+				continue;
+			}
+			if (!(nextv->flags & VERTEX_FLAG_TEMP)) {
+				gdouble ms = min_spacing(prevv, nextv);
+				if (nextv == tedge_v2(e)) {
+					gdouble cms = pathvertex_arcing_through_constraint(TOPOROUTER_VERTEX(i->data), tedge_v2(e));
+/*          printf("\t CMS to %f,%f = %f \t ms = %f\n", vx(tedge_v2(e)), vy(tedge_v2(e)), cms, ms);
+            if(vx(tedge_v2(e)) > -EPSILON && vx(tedge_v2(e)) < EPSILON) {
+              printf("\t\tPROB: ");
+              print_vertex(tedge_v2(e));
+            }*/
+					if (cms > EPSILON)
+						space += MIN(ms, cms / 2.);
+					else
+						space += ms;
+				}
+				else
+					space += ms;
+
+				prevv = nextv;
+			}
+/*      printf("%f ", space);*/
+			i = i->next;
+		}
+	}
+	else {
+
+		/* towards v1 */
+		while (i) {
+			nextv = edge_routing_prev(e, i);
+			if (nextv->route && vertices_connected(nextv, prevv)) {
+				i = i->prev;
+				continue;
+			}
+			if (!(nextv->flags & VERTEX_FLAG_TEMP)) {
+				gdouble ms = min_spacing(prevv, nextv);
+				if (nextv == tedge_v1(e)) {
+					gdouble cms = pathvertex_arcing_through_constraint(TOPOROUTER_VERTEX(i->data), tedge_v1(e));
+/*          printf("\t CMS to %f,%f = %f \t ms = %f\n", vx(tedge_v1(e)), vy(tedge_v1(e)), cms, ms);
+            if(vx(tedge_v1(e)) > -EPSILON && vx(tedge_v1(e)) < EPSILON) {
+              printf("\t\tPROB: ");
+              print_vertex(tedge_v1(e));
+            }*/
+					if (cms > EPSILON)
+						space += MIN(ms, cms / 2.);
+					else
+						space += ms;
+				}
+				else
+					space += ms;
+
+				prevv = nextv;
+			}
+/*      printf("%f ", space);*/
+			i = i->prev;
+		}
+	}
+
+	if (TOPOROUTER_IS_CONSTRAINT(e) && space > gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e))) / 2.)
+		space = gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e))) / 2.;
+
+/*  if(debug) printf("\tedge_min_spacing: %f\n", space);*/
+	return space;
+}
+
+/* line segment is 1 & 2, point is 3 
+   returns 0 if v3 is outside seg
+*/
+guint
+vertex_line_normal_intersection(gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble x3, gdouble y3, gdouble * x,
+																gdouble * y)
+{
+	gdouble m1 = cartesian_gradient(x1, y1, x2, y2);
+	gdouble m2 = perpendicular_gradient(m1);
+	gdouble c2 = (isinf(m2)) ? x3 : y3 - (m2 * x3);
+	gdouble c1 = (isinf(m1)) ? x1 : y1 - (m1 * x1);
+
+	if (isinf(m2))
+		*x = x3;
+	else if (isinf(m1))
+		*x = x1;
+	else
+		*x = (c2 - c1) / (m1 - m2);
+
+	*y = (isinf(m2)) ? y1 : (m2 * (*x)) + c2;
+
+	if (*x >= MIN(x1, x2) - EPSILON && *x <= MAX(x1, x2) + EPSILON && *y >= MIN(y1, y2) - EPSILON && *y <= MAX(y1, y2) + EPSILON)
+		return 1;
+	return 0;
+}
+
+void print_toporouter_arc(toporouter_arc_t * arc)
+{
+/*  GList *i = arc->vs;*/
+
+	printf("ARC CENTRE: %f,%f ", vx(arc->centre), vy(arc->centre));	/* print_vertex(arc->centre); */
+	printf("RADIUS: %f", arc->r);
+
+	if (arc->dir > 0)
+		printf(" COUNTERCLOCKWISE ");
+	else if (arc->dir < 0)
+		printf(" CLOCKWISE ");
+	else
+		printf(" COLINEAR(ERROR) ");
+/*  
+  printf("\n\tVS: ");
+
+  while(i) {
+    toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+    printf("%f,%f ", vx(v), vy(v));
+
+    i = i->next;
+  }
+*/
+}
+
+void toporouter_arc_remove(toporouter_oproute_t * oproute, toporouter_arc_t * arc)
+{
+	oproute->arcs = g_list_remove(oproute->arcs, arc);
+
+	if (arc->v)
+		arc->v->arc = NULL;
+}
+
+toporouter_arc_t *toporouter_arc_new(toporouter_oproute_t * oproute, toporouter_vertex_t * v1, toporouter_vertex_t * v2,
+																		 toporouter_vertex_t * centre, gdouble r, gint dir)
+{
+	toporouter_arc_t *arc = TOPOROUTER_ARC(gts_object_new(GTS_OBJECT_CLASS(toporouter_arc_class())));
+	arc->centre = centre;
+	arc->v = v1;
+	arc->v1 = v1;
+	arc->v2 = v2;
+	arc->r = r;
+	arc->dir = dir;
+
+	if (v1)
+		v1->arc = arc;
+	arc->oproute = oproute;
+
+	arc->clearance = NULL;
+
+	return arc;
+}
+
+void path_set_oproute(GList * path, toporouter_oproute_t * oproute)
+{
+	while (path) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(path->data);
+
+		if (v->flags & VERTEX_FLAG_ROUTE)
+			v->oproute = oproute;
+
+		path = path->next;
+	}
+}
+
+void print_oproute(toporouter_oproute_t * oproute)
+{
+	GList *i = oproute->arcs;
+
+	printf("Optimized Route:\n");
+	printf("\tNetlist:\t\t%s\n\tStyle:\t\t%s\n", oproute->netlist, oproute->style);
+	/* printf("%s\n", oproute->netlist); */
+/*
+  i = oproute->term1->zlink;
+  while(i) {
+    toporouter_vertex_t *thisv = TOPOROUTER_VERTEX(i->data);
+    printf("\tNetlist:\t\t%s\n\tStyle:\t\t%s\n", vertex_bbox(thisv)->netlist, vertex_bbox(thisv)->style);
+    i = i->next;
+  }
+*/
+	printf("\t");
+	print_vertex(oproute->term1);
+	printf("\n");
+	i = oproute->arcs;
+	while (i) {
+		toporouter_arc_t *arc = (toporouter_arc_t *) i->data;
+		printf("\t");
+		print_toporouter_arc(arc);
+		printf("\n");
+		i = i->next;
+	}
+	printf("\t");
+	print_vertex(oproute->term2);
+	printf("\n");
+}
+
+gdouble export_pcb_drawline(guint layer, guint x0, guint y0, guint x1, guint y1, guint thickness, guint clearance)
+{
+	gdouble d = 0.;
+	LineTypePtr line;
+	line = CreateDrawnLineOnLayer(LAYER_PTR(layer), x0, y0, x1, y1,
+																thickness, clearance, MakeFlags(PCB_FLAG_AUTO | (TEST_FLAG(CLEARNEWFLAG, PCB) ? PCB_FLAG_CLEARLINE : 0)));
+
+	if (line) {
+		AddObjectToCreateUndoList(PCB_TYPE_LINE, LAYER_PTR(layer), line, line);
+		d = coord_distance((double) x0, (double) y0, (double) x1, (double) y1) / 100.;
+	}
+	return d;
+}
+
+gdouble arc_angle(toporouter_arc_t * arc)
+{
+	gdouble x0, x1, y0, y1;
+
+	x0 = arc->x0 - vx(arc->centre);
+	x1 = arc->x1 - vx(arc->centre);
+	y0 = arc->y0 - vy(arc->centre);
+	y1 = arc->y1 - vy(arc->centre);
+
+	return fabs(acos(((x0 * x1) + (y0 * y1)) / (sqrt(pow(x0, 2) + pow(y0, 2)) * sqrt(pow(x1, 2) + pow(y1, 2)))));
+}
+
+gdouble export_pcb_drawarc(guint layer, toporouter_arc_t * a, guint thickness, guint clearance)
+{
+	gdouble sa, da, theta;
+	gdouble d = 0.;
+	ArcTypePtr arc;
+	gint wind;
+
+	wind = coord_wind(a->x0, a->y0, a->x1, a->y1, vx(a->centre), vy(a->centre));
+
+	sa = coord_xangle(a->x0, a->y0, vx(a->centre), vy(a->centre)) * 180. / M_PI;
+
+	theta = arc_angle(a);
+
+	if (!a->dir || !wind)
+		return 0.;
+
+	if (a->dir != wind)
+		theta = 2. * M_PI - theta;
+
+	da = -a->dir * theta * 180. / M_PI;
+
+	if (da < 1. && da > -1.)
+		return 0.;
+	if (da > 359. || da < -359.)
+		return 0.;
+
+	arc = CreateNewArcOnLayer(LAYER_PTR(layer), vx(a->centre), vy(a->centre), a->r, a->r,
+														sa, da, thickness, clearance,
+														MakeFlags(PCB_FLAG_AUTO | (TEST_FLAG(CLEARNEWFLAG, PCB) ? PCB_FLAG_CLEARLINE : 0)));
+
+	if (arc) {
+		AddObjectToCreateUndoList(PCB_TYPE_ARC, LAYER_PTR(layer), arc, arc);
+		d = a->r * theta / 100.;
+	}
+
+	return d;
+}
+
+void calculate_term_to_arc(toporouter_vertex_t * v, toporouter_arc_t * arc, guint dir)
+{
+	gdouble theta, a, b, bx, by, a0x, a0y, a1x, a1y;
+	gint winddir;
+
+	theta = acos(arc->r / gts_point_distance(GTS_POINT(v), GTS_POINT(arc->centre)));
+	a = arc->r * sin(theta);
+	b = arc->r * cos(theta);
+#ifdef DEBUG_EXPORT
+	printf("drawing arc with r %f theta %f d %f centre = %f,%f\n", arc->r, theta,
+				 gts_point_distance(GTS_POINT(v), GTS_POINT(arc->centre)), vx(arc->centre), vy(arc->centre));
+#endif
+	point_from_point_to_point(arc->centre, v, b, &bx, &by);
+
+	coords_on_line(bx, by, perpendicular_gradient(point_gradient(GTS_POINT(v), GTS_POINT(arc->centre))), a, &a0x, &a0y, &a1x,
+								 &a1y);
+
+	winddir = coord_wind(vx(v), vy(v), a0x, a0y, vx(arc->centre), vy(arc->centre));
+
+	if (!winddir) {
+		printf("!winddir @ v %f,%f arc->centre %f,%f\n", vx(v), vy(v), vx(arc->centre), vy(arc->centre));
+		/*TODO: fix hack: this shouldn't happen */
+		arc->x0 = vx(v);
+		arc->y0 = vy(v);
+		arc->x1 = vx(v);
+		arc->y1 = vy(v);
+		return;
+	}
+
+	g_assert(winddir);
+
+	if (dir)
+		winddir = -winddir;
+
+	if (winddir == arc->dir) {
+		if (!dir) {
+			arc->x0 = a0x;
+			arc->y0 = a0y;
+		}
+		else {
+			arc->x1 = a0x;
+			arc->y1 = a0y;
+		}
+	}
+	else {
+		if (!dir) {
+			arc->x0 = a1x;
+			arc->y0 = a1y;
+		}
+		else {
+			arc->x1 = a1x;
+			arc->y1 = a1y;
+		}
+	}
+
+}
+
+
+
+/* b1 is the projection in the direction of narc, while b2 is the perpendicular projection*/
+void arc_ortho_projections(toporouter_arc_t * arc, toporouter_arc_t * narc, gdouble * b1, gdouble * b2)
+{
+	gdouble nax, nay, ax, ay, alen2, c;
+	gdouble b1x, b1y, b2x, b2y;
+
+#ifdef DEBUG_EXPORT
+	printf("arc c = %f,%f narc c = %f,%f arc->0 = %f,%f\n",
+				 vx(arc->centre), vy(arc->centre), vx(narc->centre), vy(narc->centre), arc->x0, arc->y0);
+#endif
+
+	nax = vx(narc->centre) - vx(arc->centre);
+	nay = vy(narc->centre) - vy(arc->centre);
+	alen2 = pow(nax, 2) + pow(nay, 2);
+
+
+	ax = arc->x0 - vx(arc->centre);
+	ay = arc->y0 - vy(arc->centre);
+
+#ifdef DEBUG_EXPORT
+	printf("norm narc = %f,%f - %f\tA=%f,%f\n", nax, nay, sqrt(alen2), ax, ay);
+#endif
+
+	c = ((ax * nax) + (ay * nay)) / alen2;
+
+	b1x = c * nax;
+	b1y = c * nay;
+	b2x = ax - b1x;
+	b2y = ay - b1y;
+
+#ifdef DEBUG_EXPORT
+	printf("proj = %f,%f perp proj = %f,%f\n", b1x, b1y, b2x, b2y);
+#endif
+
+	*b1 = sqrt(pow(b1x, 2) + pow(b1y, 2));
+	*b2 = sqrt(pow(b2x, 2) + pow(b2y, 2));
+
+}
+
+guint calculate_arc_to_arc(toporouter_t * ar, toporouter_arc_t * parc, toporouter_arc_t * arc)
+{
+	gdouble theta, a, b, bx, by, a0x, a0y, a1x, a1y, m, preva, prevb;
+	gint winddir;
+	toporouter_arc_t *bigr, *smallr;
+
+	if (parc->r > arc->r) {
+		bigr = parc;
+		smallr = arc;
+	}
+	else {
+		bigr = arc;
+		smallr = parc;
+	}
+#ifdef DEBUG_EXPORT
+	printf("bigr centre = %f,%f smallr centre = %f,%f\n", vx(bigr->centre), vy(bigr->centre),
+				 vx(smallr->centre), vy(smallr->centre));
+#endif
+
+	m = perpendicular_gradient(point_gradient(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)));
+
+	if (bigr->centre == smallr->centre) {
+
+		printf("bigr->centre == smallr->centre @ %f,%f\n", vx(smallr->centre), vy(smallr->centre));
+	}
+
+	g_assert(bigr->centre != smallr->centre);
+
+	if (parc->dir == arc->dir) {
+/*export_arc_straight:*/
+
+		theta = acos((bigr->r - smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)));
+		a = bigr->r * sin(theta);
+		b = bigr->r * cos(theta);
+
+		point_from_point_to_point(bigr->centre, smallr->centre, b, &bx, &by);
+
+		coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y);
+
+		winddir = coord_wind(vx(smallr->centre), vy(smallr->centre), a0x, a0y, vx(bigr->centre), vy(bigr->centre));
+
+		arc_ortho_projections(parc, arc, &prevb, &preva);
+/*#ifdef DEBUG_EXPORT    */
+		if (!winddir) {
+
+			printf("STRAIGHT:\n");
+			printf("bigr centre = %f,%f smallr centre = %f,%f\n", vx(bigr->centre), vy(bigr->centre),
+						 vx(smallr->centre), vy(smallr->centre));
+			printf("theta = %f a = %f b = %f bigrr = %f d = %f po = %f\n", theta, a, b, bigr->r,
+						 gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)),
+						 bigr->r / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)));
+			printf("bigr-r = %f smallr-r = %f ratio = %f\n",
+						 bigr->r, smallr->r, (bigr->r - smallr->r) / gts_point_distance(GTS_POINT(bigr->centre),
+																																						GTS_POINT(smallr->centre)));
+			printf("preva = %f prevb = %f\n\n", preva, prevb);
+
+		}
+/*#endif*/
+		g_assert(winddir);
+
+		if (bigr == parc)
+			winddir = -winddir;
+
+		if (winddir == bigr->dir) {
+			if (bigr == arc) {
+				bigr->x0 = a0x;
+				bigr->y0 = a0y;
+			}
+			else {
+				bigr->x1 = a0x;
+				bigr->y1 = a0y;
+			}
+		}
+		else {
+			if (bigr == arc) {
+				bigr->x0 = a1x;
+				bigr->y0 = a1y;
+			}
+			else {
+				bigr->x1 = a1x;
+				bigr->y1 = a1y;
+			}
+		}
+
+		a = smallr->r * sin(theta);
+		b = smallr->r * cos(theta);
+
+#ifdef DEBUG_EXPORT
+		printf("a = %f b = %f\n", a, b);
+#endif
+		point_from_point_to_point(smallr->centre, bigr->centre, -b, &bx, &by);
+
+		coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y);
+
+		if (winddir == bigr->dir) {
+			if (bigr == arc) {
+				smallr->x1 = a0x;
+				smallr->y1 = a0y;
+			}
+			else {
+				smallr->x0 = a0x;
+				smallr->y0 = a0y;
+			}
+		}
+		else {
+			if (bigr == arc) {
+				smallr->x1 = a1x;
+				smallr->y1 = a1y;
+			}
+			else {
+				smallr->x0 = a1x;
+				smallr->y0 = a1y;
+			}
+		}
+
+	}
+	else {
+
+/*export_arc_twist:    */
+
+		theta = acos((bigr->r + smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)));
+		a = bigr->r * sin(theta);
+		b = bigr->r * cos(theta);
+
+		point_from_point_to_point(bigr->centre, smallr->centre, b, &bx, &by);
+
+		coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y);
+
+		winddir = coord_wind(vx(smallr->centre), vy(smallr->centre), a0x, a0y, vx(bigr->centre), vy(bigr->centre));
+/*#ifdef DEBUG_EXPORT   */
+		if (!winddir) {
+			printf("TWIST:\n");
+			printf("theta = %f a = %f b = %f r = %f d = %f po = %f\n", theta, a, b, bigr->r + smallr->r,
+						 gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)),
+						 (bigr->r + smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)));
+
+			printf("bigr centre = %f,%f smallr centre = %f,%f\n\n", vx(bigr->centre), vy(bigr->centre),
+						 vx(smallr->centre), vy(smallr->centre));
+
+			printf("big wind = %d small wind = %d\n", bigr->dir, smallr->dir);
+			return 1;
+		}
+/*#endif      */
+/*    if(!winddir) {
+      smallr->centre->flags |= VERTEX_FLAG_RED;
+      bigr->centre->flags |= VERTEX_FLAG_GREEN;
+      //bigr->centre->flags |= VERTEX_FLAG_RED;
+      {
+        int i;
+        for(i=0;i<groupcount();i++) {
+          char buffer[256];
+          sprintf(buffer, "wind%d.png", i);
+          toporouter_draw_surface(ar, ar->layers[i].surface, buffer, 2096, 2096, 2, NULL, i, NULL);
+        }
+      }
+      return; 
+    }
+*/
+		g_assert(winddir);
+
+		if (bigr == parc)
+			winddir = -winddir;
+
+		if (winddir == bigr->dir) {
+			if (bigr == arc) {
+				bigr->x0 = a0x;
+				bigr->y0 = a0y;
+			}
+			else {
+				bigr->x1 = a0x;
+				bigr->y1 = a0y;
+			}
+		}
+		else {
+			if (bigr == arc) {
+				bigr->x0 = a1x;
+				bigr->y0 = a1y;
+			}
+			else {
+				bigr->x1 = a1x;
+				bigr->y1 = a1y;
+			}
+		}
+
+		a = smallr->r * sin(theta);
+		b = smallr->r * cos(theta);
+
+		point_from_point_to_point(smallr->centre, bigr->centre, b, &bx, &by);
+
+		coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y);
+
+		winddir = coord_wind(vx(smallr->centre), vy(smallr->centre), a0x, a0y, vx(bigr->centre), vy(bigr->centre));
+
+		g_assert(winddir);
+
+		if (bigr == parc)
+			winddir = -winddir;
+
+		if (winddir == smallr->dir) {
+			if (bigr == arc) {
+				smallr->x1 = a0x;
+				smallr->y1 = a0y;
+			}
+			else {
+				smallr->x0 = a0x;
+				smallr->y0 = a0y;
+			}
+		}
+		else {
+			if (bigr == arc) {
+				smallr->x1 = a1x;
+				smallr->y1 = a1y;
+			}
+			else {
+				smallr->x0 = a1x;
+				smallr->y0 = a1y;
+			}
+		}
+
+	}
+
+	return 0;
+}
+
+void export_oproutes(toporouter_t * ar, toporouter_oproute_t * oproute)
+{
+	guint layer = PCB->LayerGroups.Entries[oproute->layergroup][0];
+	guint thickness = lookup_thickness(oproute->style);
+	guint clearance = lookup_clearance(oproute->style);
+	GList *arcs = oproute->arcs;
+	toporouter_arc_t *arc, *parc = NULL;
+
+	if (!arcs) {
+		ar->wiring_score +=
+			export_pcb_drawline(layer, vx(oproute->term1), vy(oproute->term1), vx(oproute->term2), vy(oproute->term2), thickness,
+													clearance);
+		return;
+	}
+
+
+/*  calculate_term_to_arc(oproute->term1, TOPOROUTER_ARC(arcs->data), 0, layer);*/
+
+	while (arcs) {
+		arc = TOPOROUTER_ARC(arcs->data);
+
+		if (parc && arc) {
+			ar->wiring_score += export_pcb_drawarc(layer, parc, thickness, clearance);
+			ar->wiring_score += export_pcb_drawline(layer, parc->x1, parc->y1, arc->x0, arc->y0, thickness, clearance);
+		}
+		else if (!parc) {
+			ar->wiring_score +=
+				export_pcb_drawline(layer, vx(oproute->term1), vy(oproute->term1), arc->x0, arc->y0, thickness, clearance);
+		}
+
+		parc = arc;
+		arcs = arcs->next;
+	}
+	ar->wiring_score += export_pcb_drawarc(layer, arc, thickness, clearance);
+	ar->wiring_score += export_pcb_drawline(layer, arc->x1, arc->y1, vx(oproute->term2), vy(oproute->term2), thickness, clearance);
+
+}
+
+
+
+void oproute_free(toporouter_oproute_t * oproute)
+{
+	GList *i = oproute->arcs;
+	while (i) {
+		toporouter_arc_t *arc = (toporouter_arc_t *) i->data;
+		if (arc->centre->flags & VERTEX_FLAG_TEMP)
+			gts_object_destroy(GTS_OBJECT(arc->centre));
+
+		i = i->next;
+	}
+
+	g_list_free(oproute->arcs);
+	free(oproute);
+}
+
+void oproute_calculate_tof(toporouter_oproute_t * oproute)
+{
+	GList *arcs = oproute->arcs;
+	toporouter_arc_t *parc = NULL, *arc;
+
+	oproute->tof = 0.;
+
+	if (!arcs) {
+		oproute->tof = gts_point_distance(GTS_POINT(oproute->term1), GTS_POINT(oproute->term2));
+		return;
+	}
+
+	while (arcs) {
+		arc = TOPOROUTER_ARC(arcs->data);
+
+		if (parc && arc) {
+			oproute->tof += arc_angle(parc) * parc->r;
+			oproute->tof += sqrt(pow(parc->x1 - arc->x0, 2) + pow(parc->y1 - arc->y0, 2));
+		}
+		else if (!parc) {
+			oproute->tof += sqrt(pow(arc->x0 - vx(oproute->term1), 2) + pow(arc->y0 - vy(oproute->term1), 2));
+		}
+
+		parc = arc;
+		arcs = arcs->next;
+	}
+
+	oproute->tof += arc_angle(parc) * parc->r;
+	oproute->tof += sqrt(pow(arc->x1 - vx(oproute->term2), 2) + pow(arc->y1 - vy(oproute->term2), 2));
+
+}
+
+gdouble
+line_line_distance_at_normal(gdouble line1_x1, gdouble line1_y1,
+														 gdouble line1_x2, gdouble line1_y2,
+														 gdouble line2_x1, gdouble line2_y1, gdouble line2_x2, gdouble line2_y2, gdouble x, gdouble y)
+{
+	gdouble m1 = perpendicular_gradient(cartesian_gradient(line1_x1, line1_y1, line1_x2, line1_y2));
+	gdouble m2 = cartesian_gradient(line2_x1, line2_y1, line2_x2, line2_y2);
+	gdouble c1 = (isinf(m1)) ? x : y - (m1 * x);
+	gdouble c2 = (isinf(m2)) ? line2_x1 : line2_y1 - (m2 * line2_x1);
+
+	gdouble intx, inty;
+
+	if (isinf(m2))
+		intx = line2_x1;
+	else if (isinf(m1))
+		intx = x;
+	else
+		intx = (c2 - c1) / (m1 - m2);
+
+	inty = (isinf(m2)) ? (m1 * intx) + c1 : (m2 * intx) + c2;
+
+	return sqrt(pow(x - intx, 2) + pow(y - inty, 2));
+}
+
+void calculate_serpintine(gdouble delta, gdouble r, gdouble initiala, gdouble * a, guint * nhalfcycles)
+{
+	gdouble lhalfcycle = 2. * (initiala - r) + (M_PI * r);
+	guint n;
+
+	printf("lhalfcycle = %f r = %f\n", lhalfcycle, r);
+
+	n = (delta - M_PI * r) / (lhalfcycle - 2. * r) + 1;
+	*a = (delta + 4. * n * r - n * M_PI * r + 4. * r - M_PI * r) / (2. * n);
+	*nhalfcycles = n;
+}
+
+gdouble oproute_min_spacing(toporouter_oproute_t * a, toporouter_oproute_t * b)
+{
+	return lookup_thickness(a->style) / 2. + lookup_thickness(b->style) / 2. + MAX(lookup_clearance(a->style),
+																																								 lookup_clearance(b->style));
+}
+
+gdouble vector_angle(gdouble ox, gdouble oy, gdouble ax, gdouble ay, gdouble bx, gdouble by)
+{
+	gdouble alen = sqrt(pow(ax - ox, 2) + pow(ay - oy, 2));
+	gdouble blen = sqrt(pow(bx - ox, 2) + pow(by - oy, 2));
+	return acos(((ax - ox) * (bx - ox) + (ay - oy) * (by - oy)) / (alen * blen));
+}
+
+toporouter_serpintine_t *toporouter_serpintine_new(gdouble x, gdouble y, gdouble x0, gdouble y0, gdouble x1, gdouble y1,
+																									 gpointer start, gdouble halfa, gdouble radius, guint nhalfcycles)
+{
+	toporouter_serpintine_t *serp = (toporouter_serpintine_t *) malloc(sizeof(toporouter_serpintine_t));
+	serp->x = x;
+	serp->y = y;
+	serp->x0 = x0;
+	serp->y0 = y0;
+	serp->x1 = x1;
+	serp->y1 = y1;
+	serp->start = start;
+	serp->halfa = halfa;
+	serp->radius = radius;
+	serp->nhalfcycles = nhalfcycles;
+	serp->arcs = NULL;
+	return serp;
+}
+
+/*#define DEBUG_RUBBERBAND 1*/
+
+gdouble
+check_non_intersect_vertex(gdouble x0, gdouble y0, gdouble x1, gdouble y1, toporouter_vertex_t * pathv,
+													 toporouter_vertex_t * arcv, toporouter_vertex_t * opv, gint wind, gint * arcwind, gdouble * arcr,
+													 guint debug)
+{
+	gdouble ms, line_int_x, line_int_y, x, y, d = 0., m;
+	gdouble tx0, ty0, tx1, ty1;
+	gint wind1, wind2;
+
+	g_assert(pathv->routingedge);
+
+	if (TOPOROUTER_IS_CONSTRAINT(pathv->routingedge)) {
+		gdouble d = tvdistance(tedge_v1(pathv->routingedge), tedge_v2(pathv->routingedge)) / 2.;
+		ms = min_spacing(pathv, arcv);
+		if (ms > d)
+			ms = d;
+	}
+	else {
+		ms = edge_min_spacing(g_list_find(edge_routing(pathv->routingedge), pathv), pathv->routingedge, arcv, debug);
+	}
+
+
+	if (!vertex_line_normal_intersection(x0, y0, x1, y1, vx(arcv), vy(arcv), &line_int_x, &line_int_y)) {
+
+		if (coord_distance2(x0, y0, line_int_x, line_int_y) < coord_distance2(x1, y1, line_int_x, line_int_y)) {
+			line_int_x = x0;
+			line_int_y = y0;
+		}
+		else {
+			line_int_x = x1;
+			line_int_y = y1;
+		}
+
+		m = perpendicular_gradient(cartesian_gradient(vx(arcv), vy(arcv), line_int_x, line_int_y));
+	}
+	else {
+		m = cartesian_gradient(x0, y0, x1, y1);
+	}
+
+	coords_on_line(vx(arcv), vy(arcv), m, 100., &tx0, &ty0, &tx1, &ty1);
+
+	wind1 = coord_wind(tx0, ty0, tx1, ty1, line_int_x, line_int_y);
+	wind2 = coord_wind(tx0, ty0, tx1, ty1, vx(opv), vy(opv));
+
+	if (!wind2 || wind1 == wind2)
+		return -1.;
+
+	if (!wind) {
+		coords_on_line(line_int_x, line_int_y, perpendicular_gradient(m), ms, &tx0, &ty0, &tx1, &ty1);
+		if (coord_distance2(tx0, ty0, vx(opv), vy(opv)) < coord_distance2(tx1, ty1, vx(opv), vy(opv))) {
+			x = tx0;
+			y = ty0;
+		}
+		else {
+			x = tx1;
+			y = ty1;
+		}
+	}
+	else {
+		toporouter_vertex_t *parent = pathv->parent, *child = pathv->child;
+		guint windtests = 0;
+
+		d = coord_distance(vx(arcv), vy(arcv), line_int_x, line_int_y);
+		coord_move_towards_coord_values(line_int_x, line_int_y, vx(arcv), vy(arcv), ms + d, &x, &y);
+	rewind_test:
+		wind1 = coord_wind(line_int_x, line_int_y, x, y, vx(parent), vy(parent));
+		wind2 = coord_wind(line_int_x, line_int_y, x, y, vx(child), vy(child));
+		if (wind1 && wind2 && wind1 == wind2) {
+/*      return -1.;*/
+			if (windtests++ == 2)
+				return -1.;
+
+			if (parent->flags & VERTEX_FLAG_ROUTE)
+				parent = parent->parent;
+			if (child->flags & VERTEX_FLAG_ROUTE)
+				child = child->child;
+			goto rewind_test;
+		}
+	}
+
+
+	*arcr = ms;
+	*arcwind = tvertex_wind(pathv->parent, pathv, arcv);
+
+#ifdef DEBUG_RUBBERBAND
+/*if(debug) 
+    printf("non-int check %f,%f ms %f d %f arcv %f,%f opv %f,%f\n", vx(arcv), vy(arcv), ms, d + ms,
+      vx(arcv), vy(arcv), vx(opv), vy(opv));*/
+#endif
+
+	return d + ms;
+}
+
+gdouble
+check_intersect_vertex(gdouble x0, gdouble y0, gdouble x1, gdouble y1, toporouter_vertex_t * pathv, toporouter_vertex_t * arcv,
+											 toporouter_vertex_t * opv, gint wind, gint * arcwind, gdouble * arcr, guint debug)
+{
+	gdouble ms, line_int_x, line_int_y, x, y, d = 0.;
+
+	if (TOPOROUTER_IS_CONSTRAINT(pathv->routingedge)) {
+		gdouble d = tvdistance(tedge_v1(pathv->routingedge), tedge_v2(pathv->routingedge)) / 2.;
+		ms = min_spacing(pathv, arcv);
+		if (ms > d)
+			ms = d;
+	}
+	else {
+		ms = edge_min_spacing(g_list_find(edge_routing(pathv->routingedge), pathv), pathv->routingedge, arcv, debug);
+	}
+
+	if (!vertex_line_normal_intersection(x0, y0, x1, y1, vx(arcv), vy(arcv), &line_int_x, &line_int_y))
+		return -1.;
+
+	d = coord_distance(line_int_x, line_int_y, vx(arcv), vy(arcv));
+
+
+	if (d > ms - EPSILON)
+		return -1.;
+
+	coord_move_towards_coord_values(vx(arcv), vy(arcv), line_int_x, line_int_y, ms, &x, &y);
+
+	*arcr = ms;
+	*arcwind = tvertex_wind(pathv->parent, pathv, arcv);
+/*  *arcwind = coord_wind(x0, y0, x, y, x1, y1);*/
+#ifdef DEBUG_RUBBERBAND
+/*if(debug) 
+    printf("int check %f,%f ms %f d %f arcv %f,%f opv %f,%f\n", vx(arcv), vy(arcv), ms, ms - d,
+      vx(arcv), vy(arcv), vx(opv), vy(opv));*/
+#endif
+
+	return ms - d;
+}
+
+/* returns non-zero if arc has loops */
+guint check_arc_for_loops(gpointer t1, toporouter_arc_t * arc, gpointer t2)
+{
+	gdouble x0, y0, x1, y1;
+
+	if (TOPOROUTER_IS_VERTEX(t1)) {
+		x0 = vx(TOPOROUTER_VERTEX(t1));
+		y0 = vy(TOPOROUTER_VERTEX(t1));
+	}
+	else {
+		x0 = TOPOROUTER_ARC(t1)->x1;
+		y0 = TOPOROUTER_ARC(t1)->y1;
+	}
+
+	if (TOPOROUTER_IS_VERTEX(t2)) {
+		x1 = vx(TOPOROUTER_VERTEX(t2));
+		y1 = vy(TOPOROUTER_VERTEX(t2));
+	}
+	else {
+		x1 = TOPOROUTER_ARC(t2)->x0;
+		y1 = TOPOROUTER_ARC(t2)->y0;
+	}
+
+	if (coord_intersect_prop(x0, y0, arc->x0, arc->y0, arc->x1, arc->y1, x1, y1)) {
+/*  || 
+     (arc->x0 > arc->x1 - EPSILON && arc->x0 < arc->x1 + EPSILON &&
+       arc->y0 > arc->y1 - EPSILON && arc->y0 < arc->y1 + EPSILON)
+      ) {*/
+#ifdef DEBUG_RUBBERBAND
+		printf("LOOPS %f %f -> %f %f & %f %f -> %f %f\n", x0, y0, arc->x0, arc->y0, arc->x1, arc->y1, x1, y1);
+#endif
+		return 1;
+	}
+	return 0;
+}
+
+toporouter_rubberband_arc_t *new_rubberband_arc(toporouter_vertex_t * pathv, toporouter_vertex_t * arcv, gdouble r, gdouble d,
+																								gint wind, GList * list)
+{
+	toporouter_rubberband_arc_t *rba = (toporouter_rubberband_arc_t *) malloc(sizeof(toporouter_rubberband_arc_t));
+	rba->pathv = pathv;
+	rba->arcv = arcv;
+	rba->r = r;
+	rba->d = d;
+	rba->wind = wind;
+	rba->list = list;
+	return rba;
+}
+
+gint compare_rubberband_arcs(toporouter_rubberband_arc_t * a, toporouter_rubberband_arc_t * b)
+{
+	return b->d - a->d;
+}
+
+void free_list_elements(gpointer data, gpointer user_data)
+{
+	free(data);
+}
+
+
+/* returns the edge opposite v from the triangle facing (x,y), or NULL if v is colinear with an edge between v and a neighbor */
+/*
+GtsEdge *
+vertex_edge_facing_vertex(GtsVertex *v, gdouble x, gdouble y)
+{
+  GSList *ts = gts_vertex_triangles(GTS_VERTEX(n), NULL);
+  GSList *i = ts;
+
+  while(i) {
+    GtsTriangle *t = GTS_TRIANGLE(i->data);
+    GtsEdge *e = gts_triangle_edge_opposite(t, v);
+
+    if(coord_wind(vx(edge_v1(e)), vy(edge_v1(e)), vx(v), vy(v), x, y) == vertex_wind(edge_v1(e), v, edge_v2(e)) && 
+       coord_wind(vx(edge_v2(e)), vy(edge_v2(e)), vx(v), vy(v), x, y) == vertex_wind(edge_v2(e), v, edge_v1(e))
+       ) {
+     g_slist_free(ts);
+     return e;
+    }
+
+    i = i->next;
+  }
+
+  g_slist_free(ts);
+  return NULL;
+}
+*/
+
+gdouble
+check_adj_pushing_vertex(toporouter_oproute_t * oproute, gdouble x0, gdouble y0, gdouble x1, gdouble y1,
+												 toporouter_vertex_t * v, gdouble * arcr, gint * arcwind, toporouter_vertex_t ** arc)
+{
+	GSList *ns = gts_vertex_neighbors(GTS_VERTEX(v), NULL, NULL);
+	GSList *i = ns;
+	gdouble maxd = 0.;
+
+	while (i) {
+		toporouter_vertex_t *n = TOPOROUTER_VERTEX(i->data);
+		gdouble segintx, seginty;
+		if (vertex_line_normal_intersection(x0, y0, x1, y1, vx(n), vy(n), &segintx, &seginty)) {
+			toporouter_edge_t *e = tedge(n, v);
+			gdouble ms = 0., d = coord_distance(segintx, seginty, vx(n), vy(n));
+			/*toporouter_vertex_t *a; */
+			toporouter_vertex_t *b;
+			GList *closestnet = NULL;
+
+			g_assert(e);
+
+			if (v == tedge_v1(e)) {
+				/*a = tedge_v1(e); */
+				b = tedge_v2(e);
+				closestnet = edge_routing(e);
+			}
+			else {
+				/*a = tedge_v2(e); */
+				b = tedge_v1(e);
+				closestnet = g_list_last(edge_routing(e));
+			}
+
+			if (closestnet) {
+				ms = edge_min_spacing(closestnet, e, b, 0);
+				ms += min_oproute_net_spacing(oproute, TOPOROUTER_VERTEX(closestnet->data));
+			}
+			else {
+				ms = min_oproute_vertex_spacing(oproute, b);
+			}
+
+			if (ms - d > maxd) {
+				*arcr = ms;
+				*arc = n;
+				maxd = ms - d;
+				if (vx(v) == x0 && vy(v) == y0) {
+					*arcwind = coord_wind(x0, y0, vx(n), vy(n), x1, y1);
+				}
+				else if (vx(v) == x1 && vy(v) == y1) {
+					*arcwind = coord_wind(x1, y1, vx(n), vy(n), x0, y0);
+				}
+				else {
+					fprintf(stderr, "ERROR: check_adj_pushing_vertex encountered bad vertex v (coordinates don't match)\n");
+				}
+			}
+		}
+
+		i = i->next;
+	}
+
+	g_slist_free(ns);
+	return maxd;
+}
+
+
+/* path is t1 path*/
+GList *oproute_rubberband_segment(toporouter_t * r, toporouter_oproute_t * oproute, GList * path, gpointer t1, gpointer t2,
+																	guint debug)
+{
+	gdouble x0, y0, x1, y1;
+	toporouter_vertex_t *v1, *v2, *av1, *av2;	/* v{1,2} are the vertex terminals of the segment, or arc terminal centres */
+	toporouter_arc_t *arc1 = NULL, *arc2 = NULL, *newarc = NULL;	/* arc{1,2} are the arc terminals of the segment, if they exist */
+	GList *i = path;
+	GList *list1, *list2;
+
+	GList *arcs = NULL;
+	toporouter_rubberband_arc_t *max = NULL;
+
+	gdouble d, arcr;
+	gint v1wind, v2wind, arcwind;
+
+	if (TOPOROUTER_IS_VERTEX(t1)) {
+		v1 = TOPOROUTER_VERTEX(t1);
+		x0 = vx(v1);
+		y0 = vy(v1);
+	}
+	else {
+		g_assert(TOPOROUTER_IS_ARC(t1));
+		arc1 = TOPOROUTER_ARC(t1);
+		v1 = TOPOROUTER_VERTEX(arc1->v1);
+		x0 = arc1->x1;
+		y0 = arc1->y1;
+	}
+
+	if (TOPOROUTER_IS_VERTEX(t2)) {
+		v2 = TOPOROUTER_VERTEX(t2);
+		x1 = vx(v2);
+		y1 = vy(v2);
+	}
+	else {
+		g_assert(TOPOROUTER_IS_ARC(t2));
+		arc2 = TOPOROUTER_ARC(t2);
+		v2 = TOPOROUTER_VERTEX(arc2->v2);
+		x1 = arc2->x0;
+		y1 = arc2->y0;
+	}
+
+#define TEST_AND_INSERT(z) if(d > EPSILON) arcs = g_list_prepend(arcs, new_rubberband_arc(v, z, arcr, d, arcwind, i));
+#define ARC_CHECKS(z) (!(arc1 && arc1->centre == z) && !(arc2 && arc2->centre == z) && \
+  !(TOPOROUTER_IS_VERTEX(t1) && z == v1) && !(TOPOROUTER_IS_VERTEX(t2) && z == v2))
+
+	if (v1 == v2 || !i->next || TOPOROUTER_VERTEX(i->data) == v2)
+		return NULL;
+
+/*#ifdef DEBUG_RUBBERBAND*/
+	if (debug) {
+		printf("\nRB: line %f,%f %f,%f v1 = %f,%f v2 = %f,%f \n ", x0, y0, x1, y1, vx(v1), vy(v1), vx(v2), vy(v2));
+/*    if(v1->routingedge) print_edge(v1->routingedge);
+      if(v2->routingedge) print_edge(v2->routingedge);*/
+
+	}
+/*#endif*/
+
+	/* check the vectices adjacent to the terminal vectices for push against the segment */
+/*if(TOPOROUTER_IS_VERTEX(t1)) {
+    toporouter_vertex_t *arcc = NULL;
+    d = check_adj_pushing_vertex(oproute, x0, y0, x1, y1, v1, &arcr, &arcwind, &arcc); 
+    g_assert(arcc != v1);
+    if(ARC_CHECKS(arcc) && d > EPSILON) arcs = g_list_prepend(arcs, new_rubberband_arc(v1, arcc, arcr, d, arcwind, path->next));
+  }
+
+  if(TOPOROUTER_IS_VERTEX(t2)) {
+    toporouter_vertex_t *arcc = NULL;
+    d = check_adj_pushing_vertex(oproute, x0, y0, x1, y1, v2, &arcr, &arcwind, &arcc);
+    g_assert(arcc != v2);
+    if(ARC_CHECKS(arcc) && d > EPSILON) arcs = g_list_prepend(arcs, new_rubberband_arc(v2, arcc, arcr, d, arcwind, g_list_last(path)->prev));
+  }*/
+
+	i = i->next;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+		if (v == v2 || v == v1 || !v->routingedge)
+			break;
+
+#ifdef DEBUG_RUBBERBAND
+/*  if(debug) 
+    printf("current v %f,%f - edge %f,%f %f,%f\n", vx(v), vy(v),
+      vx(tedge_v1(v->routingedge)), vy(tedge_v1(v->routingedge)),
+      vx(tedge_v2(v->routingedge)), vy(tedge_v2(v->routingedge))
+      );*/
+#endif
+		g_assert(v->routingedge);
+
+		v1wind = coord_wind(x0, y0, x1, y1, vx(tedge_v1(v->routingedge)), vy(tedge_v1(v->routingedge)));
+		v2wind = coord_wind(x0, y0, x1, y1, vx(tedge_v2(v->routingedge)), vy(tedge_v2(v->routingedge)));
+/*    if(debug) printf("\twinds: %d %d\n", v1wind, v2wind);*/
+		if (!v1wind && !v2wind) {
+			i = i->next;
+			continue;
+		}
+
+
+		if (v1wind && v2wind && v1wind != v2wind) {	/* edge is cutting through the current segment */
+
+			if (ARC_CHECKS(tedge_v1(v->routingedge))) {	/* edge v1 is not the centre of an arc terminal */
+				d =
+					check_intersect_vertex(x0, y0, x1, y1, v, tedge_v1(v->routingedge), tedge_v2(v->routingedge), v1wind, &arcwind, &arcr,
+																 debug);
+				TEST_AND_INSERT(tedge_v1(v->routingedge));
+			}
+
+			if (ARC_CHECKS(tedge_v2(v->routingedge))) {	/* edge v2 is not the centre of an arc terminal */
+				d =
+					check_intersect_vertex(x0, y0, x1, y1, v, tedge_v2(v->routingedge), tedge_v1(v->routingedge), v2wind, &arcwind, &arcr,
+																 debug);
+				TEST_AND_INSERT(tedge_v2(v->routingedge));
+			}
+		}
+		else {											/* edge is on one side of the segment */
+
+			if (ARC_CHECKS(tedge_v1(v->routingedge))) {	/* edge v1 is not the centre of an arc terminal */
+				d =
+					check_non_intersect_vertex(x0, y0, x1, y1, v, tedge_v1(v->routingedge), tedge_v2(v->routingedge), v1wind, &arcwind,
+																		 &arcr, debug);
+				TEST_AND_INSERT(tedge_v1(v->routingedge));
+			}
+
+			if (ARC_CHECKS(tedge_v2(v->routingedge))) {	/* edge v2 is not the centre of an arc terminal */
+				d =
+					check_non_intersect_vertex(x0, y0, x1, y1, v, tedge_v2(v->routingedge), tedge_v1(v->routingedge), v2wind, &arcwind,
+																		 &arcr, debug);
+				TEST_AND_INSERT(tedge_v2(v->routingedge));
+			}
+		}
+
+		i = i->next;
+	}
+
+	arcs = g_list_sort(arcs, (GCompareFunc) compare_rubberband_arcs);
+/*rubberband_insert_maxarc:*/
+	if (!arcs)
+		return NULL;
+	max = TOPOROUTER_RUBBERBAND_ARC(arcs->data);
+
+	av2 = max->pathv;
+	i = max->list->next;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+		if (v->routingedge && (tedge_v1(v->routingedge) == max->arcv || tedge_v2(v->routingedge) == max->arcv)) {
+			av2 = v;
+			i = i->next;
+			continue;
+		}
+		break;
+	}
+
+	av1 = max->pathv;
+	i = max->list->prev;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+		if (v->routingedge && (tedge_v1(v->routingedge) == max->arcv || tedge_v2(v->routingedge) == max->arcv)) {
+			av1 = v;
+			i = i->prev;
+			continue;
+		}
+		break;
+	}
+/*#ifdef DEBUG_RUBBERBAND*/
+	if (debug)
+		printf("newarc @ %f,%f \t v1 = %f,%f v2 = %f,%f r = %f\n", vx(max->arcv), vy(max->arcv), vx(av1), vy(av1), vx(av2), vy(av2),
+					 max->r);
+/*#endif*/
+	newarc = toporouter_arc_new(oproute, av1, av2, max->arcv, max->r, max->wind);
+
+	if (TOPOROUTER_IS_VERTEX(t1))
+		calculate_term_to_arc(TOPOROUTER_VERTEX(t1), newarc, 0);
+	else if (calculate_arc_to_arc(r, TOPOROUTER_ARC(t1), newarc)) {
+		printf("\tERROR: best:  r = %f d = %f\n", max->r, max->d);
+		printf("\tOPROUTE: %s\n", oproute->netlist);
+		print_vertex(oproute->term1);
+		print_vertex(oproute->term2);
+		return NULL;
+	}
+
+	if (TOPOROUTER_IS_VERTEX(t2))
+		calculate_term_to_arc(TOPOROUTER_VERTEX(t2), newarc, 1);
+	else if (calculate_arc_to_arc(r, newarc, TOPOROUTER_ARC(t2))) {
+		printf("\tERROR: best: r = %f d = %f\n", max->r, max->d);
+		printf("\tOPROUTE: %s\n", oproute->netlist);
+		print_vertex(oproute->term1);
+		print_vertex(oproute->term2);
+		return NULL;
+	}
+
+/*if(check_arc_for_loops(t1, newarc, t2)) {
+    if(arc1 && arc2) calculate_arc_to_arc(r, arc1, arc2);
+    else if(arc1) calculate_term_to_arc(TOPOROUTER_VERTEX(t2), arc1, 1);
+    else if(arc2) calculate_term_to_arc(TOPOROUTER_VERTEX(t1), arc2, 0);*/
+
+/*#ifdef DEBUG_RUBBERBAND
+    printf("REMOVING NEW ARC @ %f,%f\n", vx(newarc->centre), vy(newarc->centre));
+      TODO: properly remove newarc
+  #endif*/
+
+/*  arcs = g_list_remove(arcs, max);
+    free(max);
+    goto rubberband_insert_maxarc;
+  }*/
+
+
+	list1 = oproute_rubberband_segment(r, oproute, path, t1, newarc, debug);
+	list2 = oproute_rubberband_segment(r, oproute, i->next, newarc, t2, debug);
+
+	if (list1) {
+		GList *list = g_list_last(list1);
+		toporouter_arc_t *testarc = TOPOROUTER_ARC(list->data);
+		toporouter_arc_t *parc = list->prev ? TOPOROUTER_ARC(list->prev->data) : arc1;
+		gdouble px = parc ? parc->x1 : vx(TOPOROUTER_VERTEX(t1)), py = parc ? parc->y1 : vy(TOPOROUTER_VERTEX(t1));
+
+		if (coord_intersect_prop(px, py, testarc->x0, testarc->y0, testarc->x1, testarc->y1, newarc->x0, newarc->y0)) {
+			list1 = g_list_remove(list1, testarc);
+			if (parc)
+				calculate_arc_to_arc(r, parc, newarc);
+			else
+				calculate_term_to_arc(TOPOROUTER_VERTEX(t1), newarc, 0);
+/*#ifdef DEBUG_RUBBERBAND*/
+			if (debug)
+				printf("REMOVING ARC @ %f,%f\n", vx(testarc->centre), vy(testarc->centre));
+/*#endif*/
+		}
+	}
+	if (list2) {
+		toporouter_arc_t *testarc = TOPOROUTER_ARC(list2->data);
+		toporouter_arc_t *narc = list2->next ? TOPOROUTER_ARC(list2->next->data) : arc2;
+		gdouble nx = narc ? narc->x0 : vx(TOPOROUTER_VERTEX(t2)), ny = narc ? narc->y0 : vy(TOPOROUTER_VERTEX(t2));
+
+		if (coord_intersect_prop(newarc->x1, newarc->y1, testarc->x0, testarc->y0, testarc->x1, testarc->y1, nx, ny)) {
+			list2 = g_list_remove(list2, testarc);
+			if (narc)
+				calculate_arc_to_arc(r, newarc, narc);
+			else
+				calculate_term_to_arc(TOPOROUTER_VERTEX(t2), newarc, 1);
+
+/*#ifdef DEBUG_RUBBERBAND*/
+			if (debug)
+				printf("REMOVING ARC @ %f,%f\n", vx(testarc->centre), vy(testarc->centre));
+/*#endif*/
+		}
+	}
+
+	g_list_foreach(arcs, free_list_elements, NULL);
+	g_list_free(arcs);
+
+	return g_list_concat(list1, g_list_prepend(list2, newarc));
+}
+
+void oproute_check_all_loops(toporouter_t * r, toporouter_oproute_t * oproute)
+{
+	GList *i;
+	gpointer t1;
+
+loopcheck_restart:
+	t1 = oproute->term1;
+	i = oproute->arcs;
+	while (i) {
+		toporouter_arc_t *arc = TOPOROUTER_ARC(i->data);
+		gpointer t2 = i->next ? i->next->data : oproute->term2;
+
+		if (check_arc_for_loops(t1, arc, t2)) {
+
+			if (TOPOROUTER_IS_ARC(t1) && TOPOROUTER_IS_ARC(t2))
+				calculate_arc_to_arc(r, TOPOROUTER_ARC(t1), TOPOROUTER_ARC(t2));
+			else if (TOPOROUTER_IS_ARC(t1))
+				calculate_term_to_arc(TOPOROUTER_VERTEX(t2), TOPOROUTER_ARC(t1), 1);
+			else if (TOPOROUTER_IS_ARC(t2))
+				calculate_term_to_arc(TOPOROUTER_VERTEX(t1), TOPOROUTER_ARC(t2), 0);
+
+			oproute->arcs = g_list_remove(oproute->arcs, arc);
+			goto loopcheck_restart;
+		}
+
+		t1 = arc;
+
+		i = i->next;
+	}
+
+}
+
+GtsTriangle *opposite_triangle(GtsTriangle * t, toporouter_edge_t * e)
+{
+	GSList *i = GTS_EDGE(e)->triangles;
+
+	g_assert(e && t);
+
+	while (i) {
+		if (GTS_TRIANGLE(i->data) != t)
+			return GTS_TRIANGLE(i->data);
+		i = i->next;
+	}
+
+	return NULL;
+}
+
+
+void speccut_edge_routing_from_edge(GList * i, toporouter_edge_t * e)
+{
+	g_assert(TOPOROUTER_IS_EDGE(e));
+	while (i) {
+		toporouter_vertex_t *curv = TOPOROUTER_VERTEX(i->data);
+
+		if (!(curv->flags & VERTEX_FLAG_TEMP)) {
+			toporouter_vertex_t *newv = tvertex_intersect(curv, curv->parent, tedge_v1(e), tedge_v2(e));
+
+/*      printf("\nCURV:\n");
+        print_vertex(curv);
+
+        printf("CURV child:\n");
+        if(curv->child) 
+          print_vertex(curv->child);
+        else 
+          printf("NULL\n");
+
+        printf("CURV parent:\n");
+        if(curv->parent)
+          print_vertex(curv->parent);
+        else 
+          printf("NULL\n");*/
+
+			if (newv) {
+				gint index;
+				newv->flags |= VERTEX_FLAG_ROUTE;
+				newv->flags |= VERTEX_FLAG_SPECCUT;
+				e->routing = g_list_insert_sorted_with_data(e->routing, newv, routing_edge_insert, e);
+				newv->route = curv->route;
+				newv->oproute = curv->oproute;
+				newv->routingedge = e;
+				GTS_POINT(newv)->z = vz(curv);
+
+				newv->parent = curv->parent;
+				newv->child = curv;
+
+/*        curv->parent = newv;*/
+
+				index = g_list_index(newv->route->path, curv);
+
+				newv->route->path = g_list_insert(newv->route->path, newv, index);
+
+
+				if (newv->oproute)
+					newv->oproute->path = newv->route->path;
+			}
+
+			if (!(curv->child->routingedge)) {
+				newv = tvertex_intersect(curv, curv->child, tedge_v1(e), tedge_v2(e));
+
+				if (newv) {
+					gint index;
+					newv->flags |= VERTEX_FLAG_ROUTE;
+					newv->flags |= VERTEX_FLAG_SPECCUT;
+					e->routing = g_list_insert_sorted_with_data(e->routing, newv, routing_edge_insert, e);
+					newv->route = curv->route;
+					newv->oproute = curv->oproute;
+					newv->routingedge = e;
+					GTS_POINT(newv)->z = vz(curv);
+
+					newv->parent = curv;
+					newv->child = curv->child;
+
+/*          curv->child = newv;*/
+
+					index = g_list_index(newv->route->path, curv);
+
+					newv->route->path = g_list_insert(newv->route->path, newv, index + 1);
+
+
+					if (newv->oproute)
+						newv->oproute->path = newv->route->path;
+				}
+
+			}
+
+		}
+		i = i->next;
+	}
+
+}
+
+void speccut_edge_patch_links(toporouter_edge_t * e)
+{
+	GList *i = e->routing;
+	g_assert(TOPOROUTER_IS_EDGE(e));
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+		v->parent->child = v;
+		v->child->parent = v;
+		i = i->next;
+	}
+}
+
+gint
+check_speccut(toporouter_oproute_t * oproute, toporouter_vertex_t * v1, toporouter_vertex_t * v2, toporouter_edge_t * e,
+							toporouter_edge_t * e1, toporouter_edge_t * e2)
+{
+	GtsTriangle *t, *opt;
+	toporouter_vertex_t *opv, *opv2;
+	toporouter_edge_t *ope1, *ope2;
+	gdouble cap, flow, line_int_x, line_int_y;
+
+	if (TOPOROUTER_IS_CONSTRAINT(e))
+		return 0;
+
+	if (!(t = gts_triangle_use_edges(GTS_EDGE(e), GTS_EDGE(e1), GTS_EDGE(e2)))) {
+		printf("check_speccut: NULL t\n");
+		return 0;
+	}
+
+	if (!(opt = opposite_triangle(t, e))) {
+/*    printf("check_speccut: NULL opt\n");*/
+		return 0;
+	}
+
+	if (!(opv = segment_common_vertex(GTS_SEGMENT(e1), GTS_SEGMENT(e2)))) {
+		printf("check_speccut: NULL opv\n");
+		return 0;
+	}
+
+	if (!(opv2 = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(opt, GTS_EDGE(e))))) {
+		printf("check_speccut: NULL opv2\n");
+		return 0;
+	}
+
+	/*TODO: shifting it out of the way would be better */
+	if (e->routing) {
+		GList *i = e->routing;
+		while (i) {
+			toporouter_vertex_t *ev = TOPOROUTER_VERTEX(i->data);
+			if (!tvertex_wind(opv, ev, opv2))
+				return 0;
+			i = i->next;
+		}
+
+	}
+
+	ope1 = tedge(opv2, tedge_v1(e));
+	ope2 = tedge(opv2, tedge_v2(e));
+
+	/*this fixes the weird pad exits in r8c board
+	   if(TOPOROUTER_IS_CONSTRAINT(ope1)) return 0; */
+	if (TOPOROUTER_IS_CONSTRAINT(ope2))
+		return 0;
+
+	if (!tvertex_wind(opv2, tedge_v1(e), opv))
+		return 0;
+	if (!tvertex_wind(opv2, tedge_v2(e), opv))
+		return 0;
+
+	if (!vertex_line_normal_intersection(vx(tedge_v1(e)), vy(tedge_v1(e)),
+																			 vx(tedge_v2(e)), vy(tedge_v2(e)), vx(opv2), vy(opv2), &line_int_x, &line_int_y))
+		return 0;
+
+
+/*  return 0;
+  if(vertex_line_normal_intersection(tev1x(e), tev1y(e), tev2x(e), tev2y(e), vx(opv), vy(opv), &line_int_x, &line_int_y))
+    return 0;*/
+
+	g_assert(opt && opv2);
+
+	/* this is just temp, for the purposes of determining flow */
+	if (tedge_v1(ope1) == opv2) {
+		if (TOPOROUTER_IS_CONSTRAINT(ope1))
+			TOPOROUTER_CONSTRAINT(ope1)->routing = g_list_append(TOPOROUTER_CONSTRAINT(ope1)->routing, v1);
+		else
+			ope1->routing = g_list_append(ope1->routing, v1);
+	}
+	else {
+		if (TOPOROUTER_IS_CONSTRAINT(ope1))
+			TOPOROUTER_CONSTRAINT(ope1)->routing = g_list_prepend(TOPOROUTER_CONSTRAINT(ope1)->routing, v1);
+		else
+			ope1->routing = g_list_prepend(ope1->routing, v1);
+	}
+
+	cap = triangle_interior_capacity(opt, opv2);
+	flow = flow_from_edge_to_edge(opt, tedge(opv2, tedge_v1(e)), tedge(opv2, tedge_v2(e)), opv2, v1);
+
+	/* temp v1 removed */
+	if (TOPOROUTER_IS_CONSTRAINT(ope1))
+		TOPOROUTER_CONSTRAINT(ope1)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(ope1)->routing, v1);
+	else
+		ope1->routing = g_list_remove(ope1->routing, v1);
+
+	if (flow >= cap) {
+		toporouter_edge_t *newe =
+			TOPOROUTER_EDGE(gts_edge_new(GTS_EDGE_CLASS(toporouter_edge_class()), GTS_VERTEX(opv), GTS_VERTEX(opv2)));
+
+		speccut_edge_routing_from_edge(edge_routing(e1), newe);
+		speccut_edge_routing_from_edge(edge_routing(e2), newe);
+		speccut_edge_routing_from_edge(edge_routing(ope1), newe);
+		speccut_edge_routing_from_edge(edge_routing(ope2), newe);
+
+		speccut_edge_patch_links(newe);
+/*
+    printf("SPECCUT WITH v %f,%f for seg %f,%f %f,%f detected\n", vx(opv2), vy(opv2),
+        vx(v1), vy(v1), 
+        vx(v2), vy(v2));
+    printf("\tflow %f cap %f\n", flow, cap);
+    print_edge(newe);
+  */
+		if (newe->routing)
+			return 1;
+	}
+
+
+	return 0;
+}
+
+
+gint oproute_path_speccut(toporouter_oproute_t * oproute)
+{
+	GList *i;
+	toporouter_vertex_t *pv;
+path_speccut_restart:
+	i = oproute->path;
+	pv = NULL;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+
+		if (pv && (v->routingedge || pv->routingedge) && !(pv->flags & VERTEX_FLAG_SPECCUT) && !(v->flags & VERTEX_FLAG_SPECCUT)) {
+
+			if (!v->routingedge) {
+				if (check_speccut
+						(oproute, pv, v, tedge(tedge_v1(pv->routingedge), v), pv->routingedge, tedge(tedge_v2(pv->routingedge), v)))
+					goto path_speccut_restart;
+				if (check_speccut
+						(oproute, pv, v, tedge(tedge_v2(pv->routingedge), v), pv->routingedge, tedge(tedge_v1(pv->routingedge), v)))
+					goto path_speccut_restart;
+			}
+			else if (!pv->routingedge) {
+				if (check_speccut
+						(oproute, v, pv, tedge(tedge_v1(v->routingedge), pv), v->routingedge, tedge(tedge_v2(v->routingedge), pv)))
+					goto path_speccut_restart;
+				if (check_speccut
+						(oproute, v, pv, tedge(tedge_v2(v->routingedge), pv), v->routingedge, tedge(tedge_v1(v->routingedge), pv)))
+					goto path_speccut_restart;
+			}
+			else {
+				toporouter_vertex_t *v1 = NULL, *v2 = NULL;
+				edges_third_edge(GTS_SEGMENT(v->routingedge), GTS_SEGMENT(pv->routingedge), &v1, &v2);
+				if (check_speccut(oproute, v, pv, tedge(v1, v2), v->routingedge, pv->routingedge))
+					goto path_speccut_restart;
+			}
+		}
+
+
+		pv = v;
+		i = i->next;
+	}
+
+	return 0;
+}
+
+toporouter_oproute_t *oproute_rubberband(toporouter_t * r, GList * path)
+{
+	toporouter_oproute_t *oproute = (toporouter_oproute_t *) malloc(sizeof(toporouter_oproute_t));
+
+	g_assert(path);
+
+	oproute->term1 = TOPOROUTER_VERTEX(path->data);
+	oproute->term2 = TOPOROUTER_VERTEX(g_list_last(path)->data);
+	oproute->arcs = NULL;
+	oproute->style = vertex_bbox(oproute->term1)->cluster->netlist->style;
+	oproute->netlist = vertex_bbox(oproute->term1)->cluster->netlist->netlist;
+	oproute->layergroup = vz(oproute->term1);
+	oproute->path = path;
+	oproute->serp = NULL;
+
+	oproute->term1->parent = NULL;
+	oproute->term2->child = NULL;
+
+	path_set_oproute(path, oproute);
+
+/*  if(!strcmp(oproute->netlist, "  unnamed_net1")) */
+	oproute_path_speccut(oproute);
+
+#ifdef DEBUG_RUBBERBAND
+	if (!strcmp(oproute->netlist, "  VCC3V3") && vx(oproute->term1) == 95700. && vy(oproute->term1) == 70800. &&
+			vx(oproute->term2) == 196700. && vy(oproute->term2) == 67300.) {
+/*    printf("OPROUTE %s - %f,%f %f,%f\n", oproute->netlist, vx(oproute->term1), vy(oproute->term1), vx(oproute->term2), vy(oproute->term2));
+      print_path(path);*/
+		oproute->arcs = oproute_rubberband_segment(r, oproute, path, oproute->term1, oproute->term2, 1);
+	}
+	else
+#endif
+		oproute->arcs = oproute_rubberband_segment(r, oproute, path, oproute->term1, oproute->term2, 0);
+
+	oproute_check_all_loops(r, oproute);
+	return oproute;
+
+}
+
+void toporouter_export(toporouter_t * r)
+{
+	GList *i = r->routednets;
+	GList *oproutes = NULL;
+
+	while (i) {
+		toporouter_route_t *routedata = TOPOROUTER_ROUTE(i->data);
+		toporouter_oproute_t *oproute = oproute_rubberband(r, routedata->path);
+		oproutes = g_list_prepend(oproutes, oproute);
+		i = i->next;
+	}
+
+	i = oproutes;
+	while (i) {
+		toporouter_oproute_t *oproute = (toporouter_oproute_t *) i->data;
+		export_oproutes(r, oproute);
+		oproute_free(oproute);
+		i = i->next;
+	}
+
+	Message(PCB_MSG_DEFAULT, _("Reticulating splines... successful\n\n"));
+	Message(PCB_MSG_DEFAULT, _("Wiring cost: %f inches\n"), r->wiring_score / 1000.);
+	printf("Wiring cost: %f inches\n", r->wiring_score / 1000.);
+
+	g_list_free(oproutes);
+
+}
+
+toporouter_route_t *routedata_create(void)
+{
+	toporouter_route_t *routedata = (toporouter_route_t *) malloc(sizeof(toporouter_route_t));
+	routedata->netlist = NULL;
+	routedata->alltemppoints = NULL;
+	routedata->path = NULL;
+	routedata->curpoint = NULL;
+	routedata->pscore = routedata->score = 0.;
+	routedata->flags = 0;
+	routedata->src = routedata->dest = NULL;
+	routedata->psrc = routedata->pdest = NULL;
+	routedata->ppath = routedata->topopath = NULL;
+
+	routedata->ppathindices = NULL;
+
+	routedata->destvertices = routedata->srcvertices = NULL;
+	return routedata;
+}
+
+/*
+void
+print_routedata(toporouter_route_t *routedata)
+{
+  GList *srcvertices = cluster_vertices(routedata->src);
+  GList *destvertices = cluster_vertices(routedata->dest);
+
+  printf("ROUTEDATA:\n");
+  printf("SRCVERTICES:\n");
+  print_vertices(srcvertices);
+  printf("DESTVERTICES:\n");
+  print_vertices(destvertices);
+
+  g_list_free(srcvertices);
+  g_list_free(destvertices);
+}*/
+
+toporouter_route_t *import_route(toporouter_t * r, RatType * line)
+{
+	toporouter_route_t *routedata = routedata_create();
+
+	routedata->src = cluster_find(r, line->Point1.X, line->Point1.Y, line->group1);
+	routedata->dest = cluster_find(r, line->Point2.X, line->Point2.Y, line->group2);
+
+	if (!routedata->src)
+		printf("couldn't locate src\n");
+	if (!routedata->dest)
+		printf("couldn't locate dest\n");
+
+	if (!routedata->src || !routedata->dest) {
+		pcb_printf("PROBLEM: couldn't locate rat src or dest for rat %#mD, %d -> %#mD, %d\n",
+							 line->Point1.X, line->Point1.Y, line->group1, line->Point2.X, line->Point2.Y, line->group2);
+		free(routedata);
+		return NULL;
+	}
+
+	routedata->netlist = routedata->src->netlist;
+
+	g_assert(routedata->src->netlist == routedata->dest->netlist);
+
+	g_ptr_array_add(r->routes, routedata);
+	g_ptr_array_add(routedata->netlist->routes, routedata);
+
+	r->failednets = g_list_prepend(r->failednets, routedata);
+
+	return routedata;
+}
+
+void delete_route(toporouter_route_t * routedata, guint destroy)
+{
+	GList *i = routedata->path;
+	toporouter_vertex_t *pv = NULL;
+
+	while (i) {
+		toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+
+		g_assert(tv);
+
+		if (tv && pv && !(tv->flags & VERTEX_FLAG_ROUTE) && !(pv->flags & VERTEX_FLAG_ROUTE)) {
+			toporouter_edge_t *e = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(tv), GTS_VERTEX(pv)));
+
+			if (e && (e->flags & EDGE_FLAG_DIRECTCONNECTION)) {
+				e->flags ^= EDGE_FLAG_DIRECTCONNECTION;
+			}
+		}
+		pv = tv;
+		i = i->next;
+	}
+
+	i = routedata->path;
+	while (i) {
+		toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+
+		tv->parent = NULL;
+		tv->child = NULL;
+
+		if (tv->routingedge) {
+			if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge))
+				TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv);
+			else
+				tv->routingedge->routing = g_list_remove(tv->routingedge->routing, tv);
+			if (destroy)
+				gts_object_destroy(GTS_OBJECT(tv));
+		}
+
+		i = i->next;
+	}
+
+	if (routedata->path)
+		g_list_free(routedata->path);
+	routedata->path = NULL;
+	routedata->curpoint = NULL;
+	routedata->score = INFINITY;
+	routedata->alltemppoints = NULL;
+}
+
+/* remove route can be later reapplied */
+void remove_route(GList * path)
+{
+	GList *i = path;
+
+	while (i) {
+		toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+
+		tv->parent = NULL;
+		tv->child = NULL;
+
+/*    if(tv->flags & VERTEX_FLAG_ROUTE) g_assert(tv->route == routedata);*/
+
+		if (tv->routingedge) {
+
+			if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge))
+				TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv);
+			else
+				tv->routingedge->routing = g_list_remove(tv->routingedge->routing, tv);
+		}
+		i = i->next;
+	}
+
+}
+
+gint apply_route(GList * path, toporouter_route_t * routedata)
+{
+	GList *i = path;
+	toporouter_vertex_t *pv = NULL;
+	gint count = 0;
+
+	if (!path)
+		return 0;
+/*  g_assert(path);*/
+
+	while (i) {
+		toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data);
+
+		if (tv->routingedge) {
+			if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge))
+				TOPOROUTER_CONSTRAINT(tv->routingedge)->routing =
+					g_list_insert_sorted_with_data(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv, routing_edge_insert,
+																				 tv->routingedge);
+			else
+				tv->routingedge->routing = g_list_insert_sorted_with_data(tv->routingedge->routing,
+																																	tv, routing_edge_insert, tv->routingedge);
+
+			count++;
+		}
+
+		if (pv) {
+			pv->child = tv;
+			tv->parent = pv;
+		}
+
+		if (tv->flags & VERTEX_FLAG_ROUTE)
+			g_assert(tv->route == routedata);
+
+		pv = tv;
+		i = i->next;
+	}
+
+	TOPOROUTER_VERTEX(path->data)->parent = NULL;
+	pv->child = NULL;
+
+	return count;
+}
+
+
+gint compare_routedata_ascending(gconstpointer a, gconstpointer b)
+{
+	toporouter_route_t *ra = (toporouter_route_t *) a;
+	toporouter_route_t *rb = (toporouter_route_t *) b;
+	return ra->score - rb->score;
+}
+
+void print_costmatrix(gdouble * m, guint n)
+{
+	guint i;
+	printf("COST MATRIX:\n");
+	for (i = 0; i < n; i++) {
+		guint j;
+		for (j = 0; j < n; j++) {
+			printf("%f ", m[(i * n) + j]);
+		}
+		printf("\n");
+	}
+}
+
+
+static inline void init_cost_matrix(gdouble * m, guint n)
+{
+	guint i;
+	for (i = 0; i < n; i++) {
+		guint j;
+		for (j = 0; j < n; j++) {
+			m[(i * n) + j] = INFINITY;
+		}
+	}
+}
+
+
+toporouter_netscore_t *netscore_create(toporouter_t * r, toporouter_route_t * routedata, guint n, guint id)
+{
+	toporouter_netscore_t *netscore = (toporouter_netscore_t *) malloc(sizeof(toporouter_netscore_t));
+	GList *path = route(r, routedata, 0);
+	guint i;
+
+	netscore->id = id;
+
+	netscore->routedata = routedata;
+	routedata->detourscore = netscore->score = routedata->score;
+
+	if (!finite(routedata->detourscore)) {
+		printf("WARNING: !finite(detourscore)\n");
+		print_cluster(routedata->src);
+		print_cluster(routedata->dest);
+		return NULL;
+	}
+
+	netscore->pairwise_nodetour = (guint *) malloc(n * sizeof(guint));
+
+	for (i = 0; i < n; i++) {
+		netscore->pairwise_nodetour[i] = 0;
+	}
+
+	netscore->pairwise_detour_sum = 0.;
+	netscore->pairwise_fails = 0;
+
+	netscore->r = r;
+
+	if (path) {
+		routedata->topopath = g_list_copy(routedata->path);
+		delete_route(routedata, 0);
+	}
+
+	return netscore;
+}
+
+static inline void netscore_destroy(toporouter_netscore_t * netscore)
+{
+	free(netscore->pairwise_nodetour);
+	free(netscore);
+}
+
+void print_netscores(GPtrArray * netscores)
+{
+	toporouter_netscore_t **i;
+	printf("NETSCORES: \n\n");
+	printf("     %15s %15s %15s\n----------------------------------------------------\n", "Score", "Detour Sum",
+				 "Pairwise Fails");
+
+	for (i = (toporouter_netscore_t **) netscores->pdata; i < (toporouter_netscore_t **) netscores->pdata + netscores->len; i++) {
+#ifdef DEBUG_NETSCORES
+		printf("%4d %15f %15f %15d %15x\n", (*i)->id, (*i)->score, (*i)->pairwise_detour_sum, (*i)->pairwise_fails, (guint) * i);
+#endif
+	}
+
+	printf("\n");
+}
+
+void netscore_pairwise_calculation(toporouter_netscore_t * netscore, GPtrArray * netscores)
+{
+	toporouter_netscore_t **i;
+	toporouter_netscore_t **netscores_base = (toporouter_netscore_t **) (netscores->pdata);
+	toporouter_route_t *temproutedata = routedata_create();
+
+	/*route(netscore->r, netscore->routedata, 0); */
+	apply_route(netscore->routedata->topopath, netscore->routedata);
+
+	for (i = netscores_base; i < netscores_base + netscores->len; i++) {
+
+		if (!netscore->pairwise_nodetour[i - netscores_base] && *i != netscore
+				&& (*i)->routedata->netlist != netscore->routedata->netlist) {
+
+			temproutedata->src = (*i)->routedata->src;
+			temproutedata->dest = (*i)->routedata->dest;
+
+			route(netscore->r, temproutedata, 0);
+
+			if (temproutedata->score == (*i)->score) {
+				netscore->pairwise_nodetour[i - netscores_base] = 1;
+				(*i)->pairwise_nodetour[netscore->id] = 1;
+			}
+			else if (!finite(temproutedata->score)) {
+				netscore->pairwise_fails += 1;
+			}
+			else {
+				netscore->pairwise_detour_sum += temproutedata->score - (*i)->score;
+			}
+
+			delete_route(temproutedata, 1);
+		}
+
+	}
+
+/*  delete_route(netscore->routedata, 1);*/
+	remove_route(netscore->routedata->topopath);
+
+	free(temproutedata);
+}
+
+gint netscore_pairwise_size_compare(toporouter_netscore_t ** a, toporouter_netscore_t ** b)
+{
+	/* infinite scores are last */
+	if (!finite((*a)->score) && !finite((*b)->score))
+		return 0;
+	if (finite((*a)->score) && !finite((*b)->score))
+		return -1;
+	if (finite((*b)->score) && !finite((*a)->score))
+		return 1;
+
+	/* order by pairwise fails */
+	if ((*a)->pairwise_fails < (*b)->pairwise_fails)
+		return -1;
+	if ((*b)->pairwise_fails < (*a)->pairwise_fails)
+		return 1;
+
+	/* order by pairwise detour */
+	if ((*a)->pairwise_detour_sum < (*b)->pairwise_detour_sum)
+		return -1;
+	if ((*b)->pairwise_detour_sum < (*a)->pairwise_detour_sum)
+		return 1;
+
+	/* order by score */
+	if ((*a)->score < (*b)->score)
+		return -1;
+	if ((*b)->score < (*a)->score)
+		return 1;
+
+	return 0;
+}
+
+gint netscore_pairwise_compare(toporouter_netscore_t ** a, toporouter_netscore_t ** b)
+{
+	/* infinite scores are last */
+	if (!finite((*a)->score) && !finite((*b)->score))
+		return 0;
+	if (finite((*a)->score) && !finite((*b)->score))
+		return -1;
+	if (finite((*b)->score) && !finite((*a)->score))
+		return 1;
+
+	/* order by pairwise fails */
+	if ((*a)->pairwise_fails < (*b)->pairwise_fails)
+		return -1;
+	if ((*b)->pairwise_fails < (*a)->pairwise_fails)
+		return 1;
+
+	/* order by pairwise detour */
+	if ((*a)->pairwise_detour_sum < (*b)->pairwise_detour_sum)
+		return -1;
+	if ((*b)->pairwise_detour_sum < (*a)->pairwise_detour_sum)
+		return 1;
+
+	return 0;
+}
+
+guint order_nets_preroute_greedy(toporouter_t * r, GList * nets, GList ** rnets)
+{
+	gint len = g_list_length(nets);
+	GPtrArray *netscores = g_ptr_array_sized_new(len);
+	guint failcount = 0;
+
+	while (nets) {
+		toporouter_netscore_t *ns = netscore_create(r, TOPOROUTER_ROUTE(nets->data), len, failcount++);
+		if (ns)
+			g_ptr_array_add(netscores, ns);
+		nets = nets->next;
+	}
+
+	failcount = 0;
+
+	g_ptr_array_foreach(netscores, (GFunc) netscore_pairwise_calculation, netscores);
+
+	g_ptr_array_sort(netscores, (GCompareFunc) r->netsort);
+
+#ifdef DEBUG_ORDERING
+	print_netscores(netscores);
+#endif
+
+	*rnets = NULL;
+	FOREACH_NETSCORE(netscores) {
+		*rnets = g_list_prepend(*rnets, netscore->routedata);
+		if (!finite(netscore->score))
+			failcount++;
+		netscore_destroy(netscore);
+	}
+	FOREACH_END;
+
+	g_ptr_array_free(netscores, TRUE);
+
+	return failcount;
+}
+
+toporouter_vertex_t *edge_closest_vertex(toporouter_edge_t * e, toporouter_vertex_t * v)
+{
+	GList *i = v->routingedge ? edge_routing(v->routingedge) : NULL;
+	gdouble closestd = 0.;
+	toporouter_vertex_t *closestv = NULL;
+
+	while (i) {
+		toporouter_vertex_t *ev = TOPOROUTER_VERTEX(i->data);
+		gdouble tempd = gts_point_distance2(GTS_POINT(ev), GTS_POINT(v));
+
+		if (!closestv || (tempd < closestd)) {
+			closestd = tempd;
+			closestv = ev;
+		}
+
+		i = i->next;
+	}
+
+	return closestv;
+}
+
+void snapshot(toporouter_t * r, char *name, GList * datas)
+{
+	{
+		int i;
+		for (i = 0; i < groupcount(); i++) {
+			char buffer[256];
+			sprintf(buffer, "route-%s-%d.png", name, i);
+			toporouter_draw_surface(r, r->layers[i].surface, buffer, 2048, 2048, 2, datas, i, NULL);
+		}
+	}
+}
+
+/*
+gdouble
+route_conflict(toporouter_t *r, toporouter_route_t *route, guint *n)
+{
+  GList *i = route->path;
+  toporouter_vertex_t *pv = NULL;
+  gdouble cost = 0.;
+
+  while(i) {
+    toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+    if(pv && vz(v) == vz(pv))
+      cost += vertices_routing_conflict_cost(r, v, pv, n);
+    pv = v;
+    i = i->next;
+  }
+
+  return cost;
+}
+*/
+GList *route_conflicts(toporouter_route_t * route)
+{
+	GList *conflicts = NULL, *i = route->path;
+	toporouter_vertex_t *pv = NULL;
+
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+		if (pv && vz(pv) == vz(v)) {
+			GList *temp = vertices_routing_conflicts(pv, v), *j;
+
+			j = temp;
+			while (j) {
+				toporouter_route_t *conroute = TOPOROUTER_ROUTE(j->data);
+				if (!g_list_find(conflicts, conroute))
+					conflicts = g_list_prepend(conflicts, conroute);
+				j = j->next;
+			}
+
+			if (temp)
+				g_list_free(temp);
+		}
+
+		pv = v;
+		i = i->next;
+	}
+	return conflicts;
+}
+
+gint spread_edge(gpointer item, gpointer data)
+{
+	toporouter_edge_t *e = TOPOROUTER_EDGE(item);
+	toporouter_vertex_t *v;
+	gdouble spacing, s;
+	GList *i;
+
+	if (TOPOROUTER_IS_CONSTRAINT(e))
+		return 0;
+
+	i = edge_routing(e);
+
+	if (!g_list_length(i))
+		return 0;
+
+	if (g_list_length(i) == 1) {
+		v = TOPOROUTER_VERTEX(i->data);
+		GTS_POINT(v)->x = (vx(edge_v1(e)) + vx(edge_v2(e))) / 2.;
+		GTS_POINT(v)->y = (vy(edge_v1(e)) + vy(edge_v2(e))) / 2.;
+		return 0;
+	}
+
+	s = spacing = (gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e)))) / (g_list_length(i) + 1);
+
+	while (i) {
+		v = TOPOROUTER_VERTEX(i->data);
+		vertex_move_towards_vertex_values(edge_v1(e), edge_v2(e), s, &(GTS_POINT(v)->x), &(GTS_POINT(v)->y));
+
+		s += spacing;
+		i = i->next;
+	}
+
+	return 0;
+}
+
+void route_checkpoint(toporouter_route_t * route, toporouter_route_t * temproute)
+{
+	GList *i = g_list_last(route->path);
+	gint n = g_list_length(route->path);
+
+	if (route->ppathindices)
+		free(route->ppathindices);
+	route->ppathindices = (gint *) malloc(sizeof(gint) * n);
+
+/*  n = 0;*/
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+		n--;
+
+		if (v->routingedge) {
+			GList *j = g_list_find(edge_routing(v->routingedge), v)->prev;
+			gint tempindex = g_list_index(edge_routing(v->routingedge), v);
+
+			while (j) {
+				if (TOPOROUTER_VERTEX(j->data)->route == temproute)
+					tempindex--;
+				j = j->prev;
+			}
+
+			route->ppathindices[n] = tempindex;
+
+			if (TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+				TOPOROUTER_CONSTRAINT(v->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(v->routingedge)->routing, v);
+			else
+				v->routingedge->routing = g_list_remove(v->routingedge->routing, v);
+		}
+
+		i = i->prev;
+	}
+
+	route->pscore = route->score;
+	route->ppath = route->path;
+	remove_route(route->path);
+	route->path = NULL;
+	route->psrc = route->src;
+	route->pdest = route->dest;
+/*route->src->pc = route->src->c;
+  route->dest->pc = route->dest->c;*/
+}
+
+void route_restore(toporouter_route_t * route)
+{
+	GList *i;
+	toporouter_vertex_t *pv = NULL;
+	gint n = 0;
+
+	g_assert(route->ppath);
+	g_assert(route->ppathindices);
+
+	route->path = route->ppath;
+	i = route->ppath;
+	while (i) {
+		toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data);
+
+		if (v->routingedge) {
+			if (TOPOROUTER_IS_CONSTRAINT(v->routingedge))
+				TOPOROUTER_CONSTRAINT(v->routingedge)->routing =
+					g_list_insert(TOPOROUTER_CONSTRAINT(v->routingedge)->routing, v, route->ppathindices[n]);
+			else
+				v->routingedge->routing = g_list_insert(v->routingedge->routing, v, route->ppathindices[n]);
+
+			/*      space_edge(v->routingedge, NULL); */
+		}
+
+		if (pv) {
+			pv->child = v;
+			v->parent = pv;
+		}
+
+		n++;
+		pv = v;
+		i = i->next;
+	}
+
+	route->score = route->pscore;
+	route->src = route->psrc;
+	route->dest = route->pdest;
+/*route->src->c = route->src->pc;
+  route->dest->c = route->dest->pc;*/
+
+}
+
+void cluster_merge(toporouter_route_t * routedata)
+{
+	gint oldc = routedata->dest->c, newc = routedata->src->c;
+
+	FOREACH_CLUSTER(routedata->netlist->clusters) {
+		if (cluster->c == oldc)
+			cluster->c = newc;
+	}
+	FOREACH_END;
+
+}
+
+void netlist_recalculate(toporouter_netlist_t * netlist, GList * ignore)
+{
+	GList *i = g_list_last(netlist->routed);
+	gint n = netlist->clusters->len - 1;
+
+	FOREACH_CLUSTER(netlist->clusters) {
+		cluster->c = n--;
+	} FOREACH_END;
+
+	while (i) {
+		if (!ignore || !g_list_find(ignore, i->data))
+			cluster_merge(TOPOROUTER_ROUTE(i->data));
+		i = i->prev;
+	}
+
+}
+
+void netlists_recalculate(GList * netlists, GList * ignore)
+{
+	GList *i = netlists;
+	while (i) {
+		netlist_recalculate(TOPOROUTER_NETLIST(i->data), ignore);
+		i = i->next;
+	}
+}
+
+void netlists_rollback(GList * netlists)
+{
+/*  netlists_recalculate(netlists, NULL);*/
+	while (netlists) {
+		toporouter_netlist_t *netlist = TOPOROUTER_NETLIST(netlists->data);
+
+		FOREACH_CLUSTER(netlist->clusters) {
+			cluster->c = cluster->pc;
+		}
+		FOREACH_END;
+
+		netlists = netlists->next;
+	}
+}
+
+void print_netlist(toporouter_netlist_t * netlist)
+{
+
+	printf("NETLIST %s: ", netlist->netlist);
+
+	FOREACH_CLUSTER(netlist->clusters) {
+		printf("%d ", cluster->c);
+
+	} FOREACH_END;
+	printf("\n");
+}
+
+#define REMOVE_ROUTING(x) x->netlist->routed = g_list_remove(x->netlist->routed, x); \
+  r->routednets = g_list_remove(r->routednets, x); \
+  r->failednets = g_list_prepend(r->failednets, x)
+
+#define INSERT_ROUTING(x) x->netlist->routed = g_list_prepend(x->netlist->routed, x); \
+  r->routednets = g_list_prepend(r->routednets, x); \
+  r->failednets = g_list_remove(r->failednets, x)
+
+gint roar_route(toporouter_t * r, toporouter_route_t * routedata, gint threshold)
+{
+	gint intfails = 0;
+	GList *netlists = NULL, *routed = NULL;
+
+	g_assert(!routedata->path);
+
+	if (routedata->src->c == routedata->dest->c) {
+		printf("ERROR: attempt to route already complete route\n");
+		g_assert(routedata->src->c != routedata->dest->c);
+	}
+
+	routedata->src->pc = routedata->src->c;
+	routedata->dest->pc = routedata->dest->c;
+	routedata->psrc = routedata->src;
+	routedata->pdest = routedata->dest;
+
+	r->flags |= TOPOROUTER_FLAG_LEASTINVALID;
+	if (route(r, routedata, 0)) {
+		GList *conflicts, *j;
+
+		INSERT_ROUTING(routedata);
+
+		conflicts = route_conflicts(routedata);
+		cluster_merge(routedata);
+
+		r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID;
+
+		j = conflicts;
+		while (j) {
+			toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data);
+			if (!g_list_find(netlists, conflict->netlist))
+				netlists = g_list_prepend(netlists, conflict->netlist);
+
+			route_checkpoint(conflict, routedata);
+
+			REMOVE_ROUTING(conflict);
+			j = j->next;
+		}
+
+		netlists = g_list_prepend(netlists, routedata->netlist);
+		netlists_recalculate(netlists, NULL);
+
+		j = conflicts;
+		while (j) {
+			toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data);
+			g_assert(conflict->src->c != conflict->dest->c);
+			if (route(r, conflict, 0)) {
+				cluster_merge(conflict);
+
+				routed = g_list_prepend(routed, conflict);
+
+				INSERT_ROUTING(conflict);
+
+				netlist_recalculate(conflict->netlist, NULL);
+
+			}
+			else {
+				if (++intfails >= threshold) {
+					GList *i = routed;
+					while (i) {
+						toporouter_route_t *intconflict = TOPOROUTER_ROUTE(i->data);
+						REMOVE_ROUTING(intconflict);
+						delete_route(intconflict, 1);
+						i = i->next;
+					}
+					delete_route(routedata, 1);
+					i = g_list_last(conflicts);
+					while (i) {
+						toporouter_route_t *intconflict = TOPOROUTER_ROUTE(i->data);
+
+						route_restore(intconflict);
+						INSERT_ROUTING(intconflict);
+
+						i = i->prev;
+					}
+					REMOVE_ROUTING(routedata);
+					intfails = 0;
+					netlists_recalculate(netlists, NULL);
+					goto roar_route_end;
+				}
+
+			}
+			j = j->next;
+		}
+
+
+		netlists_recalculate(netlists, NULL);
+
+		intfails--;
+	roar_route_end:
+		g_list_free(conflicts);
+		g_list_free(netlists);
+
+	}
+	else {
+		r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID;
+	}
+
+	g_list_free(routed);
+	return intfails;
+}
+
+gint roar_router(toporouter_t * r, gint failcount, gint threshold)
+{
+	guint j;
+	gint pfailcount = failcount + 1;
+
+	Message(PCB_MSG_DEFAULT, _("ROAR router: "));
+	for (j = 0; j < 6; j++) {
+		GList *failed = g_list_copy(r->failednets), *k = failed;
+
+		k = failed;
+		while (k) {
+			failcount += roar_route(r, TOPOROUTER_ROUTE(k->data), threshold);
+			k = k->next;
+		}
+		g_list_free(failed);
+
+		printf("\tROAR pass %d - %d routed -  %d failed\n", j, g_list_length(r->routednets), g_list_length(r->failednets));
+
+		if (!failcount || failcount >= pfailcount) {
+			Message(PCB_MSG_DEFAULT, _("%d nets remaining\n"), failcount);
+			break;
+		}
+		Message(PCB_MSG_DEFAULT, _("%d -> "), failcount);
+		pfailcount = failcount;
+	}
+
+	return failcount;
+}
+
+gint route_detour_compare(toporouter_route_t ** a, toporouter_route_t ** b)
+{
+	return ((*b)->score - (*b)->detourscore) - ((*a)->score - (*a)->detourscore);
+}
+
+
+
+void roar_detour_route(toporouter_t * r, toporouter_route_t * data)
+{
+	gdouble pscore = data->score, nscore = 0.;
+	GList *netlists = NULL;
+
+	route_checkpoint(data, NULL);
+
+	REMOVE_ROUTING(data);
+
+	netlists = g_list_prepend(NULL, data->netlist);
+	netlists_recalculate(netlists, NULL);
+
+	r->flags |= TOPOROUTER_FLAG_LEASTINVALID;
+	if (route(r, data, 0)) {
+		GList *conflicts, *j;
+
+		nscore = data->score;
+		conflicts = route_conflicts(data);
+
+		INSERT_ROUTING(data);
+
+		r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID;
+
+		j = conflicts;
+		while (j) {
+			toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data);
+
+			if (!g_list_find(netlists, conflict->netlist))
+				netlists = g_list_prepend(netlists, conflict->netlist);
+			pscore += conflict->score;
+
+			route_checkpoint(conflict, NULL);
+			REMOVE_ROUTING(conflict);
+
+			j = j->next;
+		}
+		netlists_recalculate(netlists, NULL);
+
+		j = conflicts;
+		while (j) {
+			toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data);
+
+			if (route(r, conflict, 0)) {
+				cluster_merge(conflict);
+				INSERT_ROUTING(conflict);
+				nscore += conflict->score;
+			}
+			else {
+				j = j->prev;
+				goto roar_detour_route_rollback_int;
+			}
+			j = j->next;
+		}
+
+		if (nscore > pscore) {
+			j = g_list_last(conflicts);
+		roar_detour_route_rollback_int:
+			REMOVE_ROUTING(data);
+
+			while (j) {
+				toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data);
+				REMOVE_ROUTING(conflict);
+				delete_route(conflict, 1);
+				j = j->prev;
+			}
+
+			j = g_list_last(conflicts);
+			while (j) {
+				toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data);
+				route_restore(conflict);
+				INSERT_ROUTING(conflict);
+				j = j->prev;
+			}
+			delete_route(data, 1);
+
+			goto roar_detour_route_rollback_exit;
+
+		}
+
+		g_list_free(conflicts);
+	}
+	else {
+		r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID;
+	roar_detour_route_rollback_exit:
+		route_restore(data);
+		INSERT_ROUTING(data);
+	}
+	netlists_recalculate(netlists, NULL);
+
+	g_list_free(netlists);
+}
+
+void detour_router(toporouter_t * r)
+{
+	GList *i = r->routednets;
+	guint n = g_list_length(r->routednets);
+	GPtrArray *scores = g_ptr_array_sized_new(n);
+
+	while (i) {
+		toporouter_route_t *curroute = TOPOROUTER_ROUTE(i->data);
+		curroute->score = path_score(r, curroute->path);
+		g_ptr_array_add(scores, i->data);
+		i = i->next;
+	}
+
+	g_ptr_array_sort(scores, (GCompareFunc) route_detour_compare);
+
+	r->flags |= TOPOROUTER_FLAG_DETOUR;
+
+	{
+		toporouter_route_t **i;
+		for (i = (toporouter_route_t **) scores->pdata; i < (toporouter_route_t **) scores->pdata + scores->len; i++) {
+			toporouter_route_t *curroute = (*i);
+
+			if (finite(curroute->score) && finite(curroute->detourscore)) {
+/*    printf("%15s %15f \t %8f,%8f - %8f,%8f\n", (*i)->src->netlist + 2, (*i)->score - (*i)->detourscore,
+          vx(curroute->mergebox1->point), vy(curroute->mergebox1->point),
+          vx(curroute->mergebox2->point), vy(curroute->mergebox2->point));*/
+
+				if (curroute->score - curroute->detourscore > 1000.) {
+					roar_detour_route(r, curroute);
+				}
+				else
+					break;
+
+			}
+		}
+	}
+	printf("\n");
+
+	r->flags ^= TOPOROUTER_FLAG_DETOUR;
+
+	g_ptr_array_free(scores, TRUE);
+
+}
+
+gint rubix_router(toporouter_t * r, gint failcount)
+{
+	GList *i, *ordering;
+	order_nets_preroute_greedy(r, r->failednets, &ordering);
+
+	i = ordering;
+	while (i) {
+		toporouter_route_t *data = TOPOROUTER_ROUTE(i->data);
+
+		if (route(r, data, 0)) {
+			INSERT_ROUTING(data);
+			cluster_merge(data);
+			failcount--;
+		}
+
+		i = i->next;
+	}
+
+	g_list_free(ordering);
+
+	return failcount;
+}
+
+guint hybrid_router(toporouter_t * r)
+{
+	guint i;
+	gint failcount = g_list_length(r->failednets);
+	r->flags |= TOPOROUTER_FLAG_AFTERORDER;
+	r->flags |= TOPOROUTER_FLAG_AFTERRUBIX;
+	failcount = rubix_router(r, failcount);
+
+	Message(PCB_MSG_DEFAULT, _("RUBIX router: %d nets remaining\n"), failcount);
+	printf("RUBIX router: %d nets remaining\n", failcount);
+
+	r->flags |= TOPOROUTER_FLAG_GOFAR;
+
+	for (i = 0; i < 6 && failcount; i++) {
+		if (i % 2 == 1) {
+			failcount = roar_router(r, failcount, 5);
+			/*    printf("THRESH 5\n"); */
+		}
+		else {
+			failcount = roar_router(r, failcount, 2);
+			/*    printf("THRESH 2\n"); */
+		}
+
+		detour_router(r);
+	}
+
+	failcount = roar_router(r, failcount, 2);
+	detour_router(r);
+
+	return failcount;
+}
+
+void parse_arguments(toporouter_t * r, int argc, char **argv)
+{
+	int i, tempint;
+	guint group;
+
+	for (i = 0; i < argc; i++) {
+		if (sscanf(argv[i], "viacost=%d", &tempint)) {
+			r->viacost = (double) tempint;
+		}
+		else if (sscanf(argv[i], "l%d", &tempint)) {
+			gdouble *layer = (gdouble *) malloc(sizeof(gdouble));
+			*layer = (double) tempint;
+			r->keepoutlayers = g_list_prepend(r->keepoutlayers, layer);
+		}
+	}
+
+	for (group = 0; group < max_group; group++)
+		for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
+			if ((PCB->LayerGroups.Entries[group][i] < max_copper_layer) && !(PCB->Data->Layer[PCB->LayerGroups.Entries[group][i]].On)) {
+				gdouble *layer = (gdouble *) malloc(sizeof(gdouble));
+				*layer = (double) group;
+				r->keepoutlayers = g_list_prepend(r->keepoutlayers, layer);
+			}
+
+}
+
+toporouter_t *toporouter_new(void)
+{
+	toporouter_t *r = (toporouter_t *) calloc(1, sizeof(toporouter_t));
+	time_t ltime;
+
+	gettimeofday(&r->starttime, NULL);
+
+	r->netsort = netscore_pairwise_compare;
+
+	r->destboxes = NULL;
+	r->consumeddestboxes = NULL;
+
+	r->paths = NULL;
+
+	r->layers = NULL;
+	r->flags = 0;
+	r->viamax = 3;
+	r->viacost = 10000.;
+	r->stublength = 300.;
+	r->serpintine_half_amplitude = 1500.;
+
+	r->wiring_score = 0.;
+
+	r->bboxes = NULL;
+	r->bboxtree = NULL;
+
+	r->netlists = g_ptr_array_new();
+	r->routes = g_ptr_array_new();
+
+	r->keepoutlayers = NULL;
+
+	r->routednets = NULL;
+	r->failednets = NULL;
+
+	ltime = time(NULL);
+
+	gts_predicates_init();
+
+	Message(PCB_MSG_DEFAULT, _("Topological Autorouter\n"));
+	Message(PCB_MSG_DEFAULT, _("Started %s"), asctime(localtime(&ltime)));
+	Message(PCB_MSG_DEFAULT, _("-------------------------------------\n"));
+	Message(PCB_MSG_DEFAULT, _("Copyright 2009 Anthony Blake (tonyb33 at gmail.com)\n\n"));
+	return r;
+}
+
+void acquire_twonets(toporouter_t * r)
+{
+	RAT_LOOP(PCB->Data);
+	if (TEST_FLAG(PCB_FLAG_SELECTED, line))
+		import_route(r, line);
+	END_LOOP;
+
+	if (!r->routes->len) {
+		RAT_LOOP(PCB->Data);
+		import_route(r, line);
+		END_LOOP;
+	}
+}
+
+toporouter_netlist_t *find_netlist_by_name(toporouter_t * r, char *name)
+{
+	FOREACH_NETLIST(r->netlists) {
+		if (!strcmp(netlist->netlist, name))
+			return netlist;
+	}
+	FOREACH_END;
+	return NULL;
+}
+
+gint toporouter_set_pair(toporouter_t * r, toporouter_netlist_t * n1, toporouter_netlist_t * n2)
+{
+	if (!n1 || !n2)
+		return 0;
+	n1->pair = n2;
+	n2->pair = n1;
+	return 1;
+}
+
+static int toporouter(int argc, char **argv, Coord x, Coord y)
+{
+	toporouter_t *r = toporouter_new();
+	parse_arguments(r, argc, argv);
+	import_geometry(r);
+	acquire_twonets(r);
+
+/*if(!toporouter_set_pair(r, find_netlist_by_name(r, "  DRAM_DQS_N"), find_netlist_by_name(r, "  DRAM_DQS"))) {
+    printf("Couldn't associate pair\n");
+  }*/
+
+	hybrid_router(r);
+/*
+  for(gint i=0;i<groupcount();i++) {
+   gts_surface_foreach_edge(r->layers[i].surface, space_edge, NULL);
+  }
+  {
+    int i;
+    for(i=0;i<groupcount();i++) {
+      char buffer[256];
+      sprintf(buffer, "route%d.png", i);
+      toporouter_draw_surface(r, r->layers[i].surface, buffer, 1024, 1024, 2, NULL, i, NULL);
+    }
+  }
+*/
+	toporouter_export(r);
+	toporouter_free(r);
+
+	SaveUndoSerialNumber();
+	DeleteRats(pcb_false);
+	RestoreUndoSerialNumber();
+	AddAllRats(pcb_false, NULL);
+	RestoreUndoSerialNumber();
+	IncrementUndoSerialNumber();
+	Redraw();
+
+	return 0;
+}
+
+static int escape(int argc, char **argv, Coord x, Coord y)
+{
+	guint dir, viax, viay;
+	gdouble pitch, length, dx, dy;
+
+	if (argc != 1)
+		return 0;
+
+	dir = atoi(argv[0]);
+
+
+	ALLPAD_LOOP(PCB->Data);
+	{
+		if (TEST_FLAG(PCB_FLAG_SELECTED, pad)) {
+			PinTypePtr via;
+			LineTypePtr line;
+
+			PadType *pad0 = element->Pad->data;
+			PadType *pad1 = g_list_next(element->Pad)->data;
+
+			pitch = sqrt(pow(abs(pad0->Point1.X - pad1->Point1.X), 2) + pow(abs(pad0->Point1.Y - pad1->Point1.Y), 2));
+			length = sqrt(pow(pitch, 2) + pow(pitch, 2)) / 2.;
+
+			dx = length * sin(M_PI / 4.);
+			dy = length * cos(M_PI / 4.);
+
+			switch (dir) {
+			case 1:
+				viax = pad->Point1.X - dx;
+				viay = pad->Point1.Y + dy;
+				break;
+			case 3:
+				viax = pad->Point1.X + dx;
+				viay = pad->Point1.Y + dy;
+				break;
+			case 9:
+				viax = pad->Point1.X + dx;
+				viay = pad->Point1.Y - dy;
+				break;
+			case 7:
+				viax = pad->Point1.X - dx;
+				viay = pad->Point1.Y - dy;
+				break;
+			case 2:
+				viax = pad->Point1.X;
+				viay = pad->Point1.Y + (pitch / 2);
+				break;
+			case 8:
+				viax = pad->Point1.X;
+				viay = pad->Point1.Y - (pitch / 2);
+				break;
+			case 4:
+				viax = pad->Point1.X - (pitch / 2);
+				viay = pad->Point1.Y;
+				break;
+			case 6:
+				viax = pad->Point1.X + (pitch / 2);
+				viay = pad->Point1.Y;
+				break;
+			default:
+				printf("ERROR: escape() with bad direction (%d)\n", dir);
+				return 1;
+			}
+
+			if ((via = CreateNewVia(PCB->Data, viax, viay,
+															Settings.ViaThickness, 2 * Settings.Clearance,
+															0, Settings.ViaDrillingHole, NULL, NoFlags())) != NULL) {
+				AddObjectToCreateUndoList(PCB_TYPE_VIA, via, via, via);
+/*        if (gui->shift_is_pressed ())
+            ChangeObjectThermal (PCB_TYPE_VIA, via, via, via, PCB->ThermStyle);*/
+				DrawVia(via);
+				if ((line = CreateDrawnLineOnLayer(CURRENT, pad->Point1.X + 1., pad->Point1.Y + 1., viax + 1., viay + 1.,
+																					 Settings.LineThickness, 2 * Settings.Clearance, NoFlags()))) {
+
+					AddObjectToCreateUndoList(PCB_TYPE_LINE, CURRENT, line, line);
+					DrawLine(CURRENT, line);
+
+				}
+
+			}
+
+		}
+	}
+	END_LOOP;
+	END_LOOP;
+
+	IncrementUndoSerialNumber();
+	Draw();
+	return 0;
+}
+
+static HID_Action toporouter_action_list[] = {
+	{"Escape", "Select a set of pads", escape, "Pad escape", "Escape()"},
+	{"Toporouter", "Select net(s)", toporouter, "Topological autorouter", "Toporouter()"}
+};
+
+REGISTER_ACTIONS(toporouter_action_list, toporouter_cookie)
+
+static void hid_toporouter_uninit(void)
+{
+	hid_remove_actions_by_cookie(toporouter_cookie);
+}
+
+pcb_uninit_t hid_toporouter_init()
+{
+	register_toporouter_action_list();
+	return hid_toporouter_uninit;
+}
diff --git a/src_plugins/toporouter/toporouter.h b/src_plugins/toporouter/toporouter.h
new file mode 100644
index 0000000..882e118
--- /dev/null
+++ b/src_plugins/toporouter/toporouter.h
@@ -0,0 +1,492 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  Topological Autorouter for 
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2009 Anthony Blake
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for email:
+ *  Anthony Blake, tonyb33 at gmail.com
+ *
+ */
+
+#ifndef PCB_TOPOROUTER_H
+#define PCB_TOPOROUTER_H
+
+#include <assert.h>
+#include <glib.h>
+#include "data.h"
+#include "macro.h"
+#include "../autoroute/autoroute.h"
+#include "box.h"
+#include "create.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "heap.h"
+#include "rtree.h"
+#include "misc.h"
+#include "mymem.h"
+#include "polygon.h"
+#include "rats.h"
+#include "remove.h"
+#include "thermal.h"
+#include "undo.h"
+#include "global.h"
+
+#include "gts.h"
+
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <sys/time.h>
+
+#define TOPOROUTER_FLAG_VERBOSE       (1<<0)
+#define TOPOROUTER_FLAG_HARDDEST      (1<<1)
+#define TOPOROUTER_FLAG_HARDSRC       (1<<2)
+#define TOPOROUTER_FLAG_MATCH         (1<<3)
+#define TOPOROUTER_FLAG_LAYERHINT     (1<<4)
+#define TOPOROUTER_FLAG_LEASTINVALID  (1<<5)
+#define TOPOROUTER_FLAG_AFTERORDER    (1<<6)
+#define TOPOROUTER_FLAG_AFTERRUBIX    (1<<7)
+#define TOPOROUTER_FLAG_GOFAR         (1<<8)
+#define TOPOROUTER_FLAG_DETOUR        (1<<9)
+
+/* Define to 1 to enable toporouter graphical output - wait, we don't link cairo?! */
+#define TOPO_OUTPUT_ENABLED 0
+
+
+#if TOPO_OUTPUT_ENABLED
+#include <cairo.h>
+#endif
+
+#define EPSILON 0.0001f
+
+/*#define DEBUG_ROAR 1*/
+
+#define coord_distance(a,b,c,d) sqrt(pow(a-c,2)+pow(b-d,2))
+#define coord_distance2(a,b,c,d) (pow(a-c,2)+pow(b-d,2))
+
+#define tvdistance(a,b) sqrt(pow(vx(a)-vx(b),2)+pow(vy(a)-vy(b),2))
+#define tvdistance2(a,b) (pow(vx(a)-vx(b),2)+pow(vy(a)-vy(b),2))
+
+#define edge_v1(e) (GTS_SEGMENT(e)->v1)
+#define edge_v2(e) (GTS_SEGMENT(e)->v2)
+#define tedge_v1(e) (TOPOROUTER_VERTEX(GTS_SEGMENT(e)->v1))
+#define tedge_v2(e) (TOPOROUTER_VERTEX(GTS_SEGMENT(e)->v2))
+
+#define tedge(v1,v2) TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v1), GTS_VERTEX(v2)))
+
+#define edge_routing(e) (TOPOROUTER_IS_CONSTRAINT(e) ? TOPOROUTER_CONSTRAINT(e)->routing : e->routing)
+#define vrouting(v) (edge_routing(v->routingedge))
+
+#define edge_routing_next(e,list) ((list->next) ? TOPOROUTER_VERTEX(list->next->data) : TOPOROUTER_VERTEX(edge_v2(e)))
+#define edge_routing_prev(e,list) ((list->prev) ? TOPOROUTER_VERTEX(list->prev->data) : TOPOROUTER_VERTEX(edge_v1(e)))
+
+#define vx(v) (GTS_POINT(v)->x)
+#define vy(v) (GTS_POINT(v)->y)
+#define vz(v) (GTS_POINT(v)->z)
+
+#define close_enough_xy(a,b) (vx(a) > vx(b) - EPSILON && vx(a) < vx(b) + EPSILON && vy(a) > vy(b) - EPSILON && vy(a) < vy(b) + EPSILON)
+
+#define tev1x(e) (vx(tedge_v1(e))
+#define tev1y(e) (vy(tedge_v1(e))
+#define tev1z(e) (vz(tedge_v1(e))
+#define tev2x(e) (vx(tedge_v2(e))
+#define tev2y(e) (vy(tedge_v2(e))
+#define tev2z(e) (vz(tedge_v2(e))
+
+#define tvertex_intersect(a,b,c,d) (TOPOROUTER_VERTEX(vertex_intersect(GTS_VERTEX(a),GTS_VERTEX(b),GTS_VERTEX(c),GTS_VERTEX(d))))
+
+#define TOPOROUTER_IS_BBOX(obj)      (gts_object_is_from_class (obj, toporouter_bbox_class ()))
+#define TOPOROUTER_BBOX(obj)          GTS_OBJECT_CAST (obj, toporouter_bbox_t, toporouter_bbox_class ())
+#define TOPOROUTER_BBOX_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass, toporouter_bbox_class_t, toporouter_bbox_class ())
+
+typedef enum {
+	PAD,
+	PIN,
+	VIA,
+	ARC,
+	VIA_SHADOW,
+	LINE,
+	OTHER,
+	BOARD,
+	EXPANSION_AREA,
+	POLYGON,
+	TEMP
+} toporouter_term_t;
+
+struct _toporouter_bbox_t {
+	GtsBBox b;
+
+	toporouter_term_t type;
+	void *data;
+	int layer;
+
+	GtsSurface *surface;
+	GtsTriangle *enclosing;
+
+	GList *constraints;
+	GtsPoint *point, *realpoint;
+
+/*  char *netlist, *style;*/
+
+	struct _toporouter_cluster_t *cluster;
+
+};
+
+struct _toporouter_bbox_class_t {
+	GtsBBoxClass parent_class;
+};
+
+typedef struct _toporouter_bbox_t toporouter_bbox_t;
+typedef struct _toporouter_bbox_class_t toporouter_bbox_class_t;
+
+#define TOPOROUTER_IS_EDGE(obj)      (gts_object_is_from_class (obj, toporouter_edge_class ()))
+#define TOPOROUTER_EDGE(obj)          GTS_OBJECT_CAST (obj, toporouter_edge_t, toporouter_edge_class ())
+#define TOPOROUTER_EDGE_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass, toporouter_edge_class_t, toporouter_edge_class ())
+
+#define EDGE_FLAG_DIRECTCONNECTION (1<<0)
+
+struct _toporouter_edge_t {
+	GtsEdge e;
+	/*NetListType *netlist; */
+
+	guint flags;
+
+	GList *routing;
+};
+
+struct _toporouter_edge_class_t {
+	GtsEdgeClass parent_class;
+};
+
+typedef struct _toporouter_edge_t toporouter_edge_t;
+typedef struct _toporouter_edge_class_t toporouter_edge_class_t;
+
+#define TOPOROUTER_IS_VERTEX(obj)      (gts_object_is_from_class (obj, toporouter_vertex_class ()))
+#define TOPOROUTER_VERTEX(obj)          GTS_OBJECT_CAST (obj, toporouter_vertex_t, toporouter_vertex_class ())
+#define TOPOROUTER_VERTEX_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass, toporouter_vertex_class_t, toporouter_vertex_class ())
+
+#define VERTEX_FLAG_VIZ      (1<<1)
+#define VERTEX_FLAG_CCW      (1<<2)
+#define VERTEX_FLAG_CW       (1<<3)
+#define VERTEX_FLAG_RED      (1<<4)
+#define VERTEX_FLAG_GREEN    (1<<5)
+#define VERTEX_FLAG_BLUE     (1<<6)
+#define VERTEX_FLAG_TEMP     (1<<7)
+#define VERTEX_FLAG_ROUTE    (1<<8)
+#define VERTEX_FLAG_FAKE     (1<<10)
+#define VERTEX_FLAG_SPECCUT  (1<<11)
+
+struct _toporouter_vertex_t {
+	GtsVertex v;
+	/*GList *boxes; */
+	struct _toporouter_bbox_t *bbox;
+
+	struct _toporouter_vertex_t *parent;
+	struct _toporouter_vertex_t *child;
+
+	toporouter_edge_t *routingedge;
+
+	guint flags;
+
+	gdouble gcost, hcost;
+	guint gn;
+
+	struct _toporouter_arc_t *arc;
+
+	struct _toporouter_oproute_t *oproute;
+	struct _toporouter_route_t *route;
+
+	gdouble thickness;
+
+};
+
+struct _toporouter_vertex_class_t {
+	GtsVertexClass parent_class;
+};
+
+typedef struct _toporouter_vertex_t toporouter_vertex_t;
+typedef struct _toporouter_vertex_class_t toporouter_vertex_class_t;
+
+#define TOPOROUTER_IS_CONSTRAINT(obj)      (gts_object_is_from_class (obj, toporouter_constraint_class ()))
+#define TOPOROUTER_CONSTRAINT(obj)          GTS_OBJECT_CAST (obj, toporouter_constraint_t, toporouter_constraint_class ())
+#define TOPOROUTER_CONSTRAINT_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass, toporouter_constraint_class_t, toporouter_constraint_class ())
+
+struct _toporouter_constraint_t {
+	GtsConstraint c;
+	toporouter_bbox_t *box;
+	GList *routing;
+};
+
+struct _toporouter_constraint_class_t {
+	GtsConstraintClass parent_class;
+};
+
+typedef struct {
+	gdouble x, y;
+} toporouter_spoint_t;
+
+typedef struct _toporouter_constraint_t toporouter_constraint_t;
+typedef struct _toporouter_constraint_class_t toporouter_constraint_class_t;
+
+typedef struct {
+	GtsSurface *surface;
+/*  GtsTriangle *t;*/
+/*  GtsVertex *v1, *v2, *v3;*/
+
+	GList *vertices;
+	GList *constraints;
+	GList *edges;
+
+} toporouter_layer_t;
+
+#define TOPOROUTER_VERTEX_REGION(x) ((toporouter_vertex_region_t *)x)
+typedef struct {
+
+	GList *points;
+	toporouter_vertex_t *v1, *v2;
+	toporouter_vertex_t *origin;
+
+} toporouter_vertex_region_t;
+
+struct _toporouter_rubberband_arc_t {
+	toporouter_vertex_t *pathv, *arcv;
+	gdouble r, d;
+	gint wind;
+	GList *list;
+};
+
+typedef struct _toporouter_rubberband_arc_t toporouter_rubberband_arc_t;
+#define TOPOROUTER_RUBBERBAND_ARC(x) ((toporouter_rubberband_arc_t *)x)
+
+struct _toporouter_route_t {
+
+	struct _toporouter_netlist_t *netlist;
+
+	struct _toporouter_cluster_t *src, *dest;
+	struct _toporouter_cluster_t *psrc, *pdest;
+
+	gdouble score, detourscore;
+
+	toporouter_vertex_t *curpoint;
+	GHashTable *alltemppoints;
+
+	GList *path;
+
+	guint flags;
+
+	GList *destvertices, *srcvertices;
+
+	GList *topopath;
+
+	gdouble pscore;
+	GList *ppath;
+
+	gint *ppathindices;
+};
+
+typedef struct _toporouter_route_t toporouter_route_t;
+
+#define TOPOROUTER_ROUTE(x) ((toporouter_route_t *)x)
+
+struct _toporouter_netlist_t {
+	GPtrArray *clusters, *routes;
+	char *netlist, *style;
+	GList *routed;
+
+	struct _toporouter_netlist_t *pair;
+};
+
+typedef struct _toporouter_netlist_t toporouter_netlist_t;
+
+#define TOPOROUTER_NETLIST(x) ((toporouter_netlist_t *)x)
+
+struct _toporouter_cluster_t {
+	gint c, pc;
+	GPtrArray *boxes;
+	toporouter_netlist_t *netlist;
+};
+
+typedef struct _toporouter_cluster_t toporouter_cluster_t;
+
+#define TOPOROUTER_CLUSTER(x) ((toporouter_cluster_t *)x)
+
+#define TOPOROUTER_OPROUTE(x) ((toporouter_oproute_t *)x)
+
+#define oproute_next(a,b) (b->next ? TOPOROUTER_ARC(b->next->data) : a->term2)
+#define oproute_prev(a,b) (b->prev ? TOPOROUTER_ARC(b->prev->data) : a->term1)
+
+#define TOPOROUTER_SERPINTINE(x) ((toporouter_serpintine_t *)x)
+
+struct _toporouter_serpintine_t {
+	GList *arcs;
+	gdouble x, y;
+	gdouble x0, y0, x1, y1;
+
+	gpointer start;
+	gdouble halfa, radius;
+	guint nhalfcycles;
+
+};
+typedef struct _toporouter_serpintine_t toporouter_serpintine_t;
+
+struct _toporouter_oproute_t {
+	GList *arcs;
+	toporouter_vertex_t *term1, *term2;
+	char *style;
+	char *netlist;
+	guint layergroup;
+	gdouble tof;
+	GList *path;
+
+	toporouter_serpintine_t *serp;
+};
+
+typedef struct _toporouter_oproute_t toporouter_oproute_t;
+
+
+#define TOPOROUTER_IS_ARC(obj)           (gts_object_is_from_class (obj, toporouter_arc_class()))
+#define TOPOROUTER_ARC(obj)              GTS_OBJECT_CAST (obj, toporouter_arc_t, toporouter_arc_class())
+#define TOPOROUTER_ARC_CLASS(klass)      GTS_OBJECT_CLASS_CAST (klass, toporouter_arc_class_t, toporouter_arc_class())
+
+struct _toporouter_arc_t {
+	GtsObject object;
+
+	gdouble x0, y0, x1, y1;
+	toporouter_vertex_t *centre, *v;
+	gdouble r;
+	gint dir;
+
+	GList *clearance;
+
+	toporouter_oproute_t *oproute;
+
+	toporouter_vertex_t *v1, *v2;
+};
+
+struct _toporouter_arc_class_t {
+	GtsObjectClass parent_class;
+	gboolean binary;
+};
+
+typedef struct _toporouter_arc_t toporouter_arc_t;
+typedef struct _toporouter_arc_class_t toporouter_arc_class_t;
+
+typedef struct _toporouter_t toporouter_t;
+
+
+
+typedef struct {
+	guint id;
+
+	guint *pairwise_nodetour;
+	gdouble pairwise_detour_sum;
+	gdouble score;
+	guint pairwise_fails;
+
+	toporouter_route_t *routedata;
+
+	toporouter_t *r;
+
+} toporouter_netscore_t;
+
+#define TOPOROUTER_NETSCORE(x) ((toporouter_netscore_t *)x)
+
+struct _toporouter_t {
+	GSList *bboxes;
+	GNode *bboxtree;
+
+	toporouter_layer_t *layers;
+
+	GList *paths;
+
+	GList *keepoutlayers;
+
+	guint flags;
+
+	GList *destboxes, *consumeddestboxes;
+
+	/* settings: */
+	guint viamax;
+	gdouble viacost;
+	gdouble stublength;
+	gdouble serpintine_half_amplitude;
+
+	gdouble wiring_score;
+
+	GPtrArray *routes;
+	GPtrArray *netlists;
+
+	GList *routednets, *failednets;
+
+	  gint(*netsort) (toporouter_netscore_t **, toporouter_netscore_t **);
+
+	struct timeval starttime;
+
+	FILE *debug;
+};
+
+typedef gint(*oproute_adjseg_func)
+ 
+	(toporouter_t *,
+	 GList **, GList **, guint *, gdouble, gdouble, gdouble, gdouble, toporouter_oproute_t *, toporouter_oproute_t *);
+
+typedef struct {
+#ifdef CAIRO_H
+	cairo_t *cr;
+	cairo_surface_t *surface;
+#endif
+
+	double s;											/* scale factor */
+
+	int mode;
+	void *data;
+
+	char *filename;
+	double iw, ih;								/* image dimensions */
+} drawing_context_t;
+
+#define FOREACH_CLUSTER(clusters) do { \
+  toporouter_cluster_t **i; \
+  for(i = ((toporouter_cluster_t **)clusters->pdata) + clusters->len - 1; i >= (toporouter_cluster_t **)clusters->pdata && clusters->len > 0; --i) { \
+    toporouter_cluster_t *cluster = *i;
+
+#define FOREACH_BBOX(boxes) do { \
+  toporouter_bbox_t **i; \
+  for(i = ((toporouter_bbox_t **)boxes->pdata) + boxes->len - 1; i >= (toporouter_bbox_t **)boxes->pdata && boxes->len > 0; --i) { \
+    toporouter_bbox_t *box = *i;
+
+#define FOREACH_ROUTE(routes) do { \
+  toporouter_route_t **i; \
+  for(i = ((toporouter_route_t **)routes->pdata) + routes->len - 1; i >= (toporouter_route_t **)routes->pdata && routes->len > 0; --i) { \
+    toporouter_route_t *routedata = *i;
+
+#define FOREACH_NETSCORE(netscores) do { \
+  toporouter_netscore_t **i; \
+  for(i = ((toporouter_netscore_t **)netscores->pdata) + netscores->len - 1; i >= (toporouter_netscore_t **)netscores->pdata && netscores->len > 0; --i) { \
+    toporouter_netscore_t *netscore = *i;
+
+#define FOREACH_NETLIST(netlists) do { \
+  toporouter_netlist_t **i; \
+  for(i = ((toporouter_netlist_t **)netlists->pdata) + netlists->len - 1; i >= (toporouter_netlist_t **)netlists->pdata && netlists->len > 0; --i) { \
+    toporouter_netlist_t *netlist = *i;
+
+#define FOREACH_END }} while(0)
+
+#endif /* PCB_TOPOROUTER_H */
diff --git a/src_plugins/vendordrill/Makefile b/src_plugins/vendordrill/Makefile
new file mode 100644
index 0000000..402f804
--- /dev/null
+++ b/src_plugins/vendordrill/Makefile
@@ -0,0 +1,6 @@
+all:
+	cd ../../src && make mod_vendordrill
+
+clean:
+	rm *.o *.so 2>/dev/null ; true
+
diff --git a/src_plugins/vendordrill/Plug.tmpasm b/src_plugins/vendordrill/Plug.tmpasm
new file mode 100644
index 0000000..439d417
--- /dev/null
+++ b/src_plugins/vendordrill/Plug.tmpasm
@@ -0,0 +1,9 @@
+put /local/pcb/mod {vendordrill}
+append /local/pcb/mod/OBJS [@ $(PLUGDIR)/vendordrill/vendor.o @]
+put /local/pcb/mod/CONF {$(PLUGDIR)/vendordrill/vendor_conf.h}
+
+switch /local/pcb/vendordrill/controls
+	case {buildin}   include /local/pcb/tmpasm/buildin; end;
+	case {plugin}    include /local/pcb/tmpasm/plugin; end;
+	case {disable}   include /local/pcb/tmpasm/disable; end;
+end
diff --git a/src_plugins/vendordrill/README b/src_plugins/vendordrill/README
new file mode 100644
index 0000000..5b9bf4a
--- /dev/null
+++ b/src_plugins/vendordrill/README
@@ -0,0 +1,5 @@
+Vendor drill mapping.
+
+#state: works
+#default: buildin
+#implements: (feature)
diff --git a/src_plugins/vendordrill/vendor.c b/src_plugins/vendordrill/vendor.c
new file mode 100644
index 0000000..38a9375
--- /dev/null
+++ b/src_plugins/vendordrill/vendor.c
@@ -0,0 +1,698 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2004, 2007 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "conf_core.h"
+
+#include <ctype.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <genregex/regex_sei.h>
+
+#include "change.h"
+#include "data.h"
+#include "draw.h"
+#include "error.h"
+#include "global.h"
+#include "set.h"
+#include "undo.h"
+#include "vendor.h"
+#include "stub_vendor.h"
+#include "plugins.h"
+#include "hid_flags.h"
+#include "hid_actions.h"
+#include "hid_cfg.h"
+#include "vendor_conf.h"
+#include "compat_misc.h"
+#include <liblihata/lihata.h>
+#include <liblihata/tree.h>
+
+conf_vendor_t conf_vendor;
+
+static void add_to_drills(char *);
+static void apply_vendor_map(void);
+static void process_skips(lht_node_t *);
+static pcb_bool rematch(const char *, const char *);
+static void vendor_free_all(void);
+
+/* list of vendor drills and a count of them */
+static int *vendor_drills = NULL;
+static int n_vendor_drills = 0;
+
+static int cached_drill = -1;
+static int cached_map = -1;
+
+/* lists of elements to ignore */
+static char **ignore_refdes = NULL;
+static int n_refdes = 0;
+static char **ignore_value = NULL;
+static int n_value = 0;
+static char **ignore_descr = NULL;
+static int n_descr = 0;
+
+/* vendor name */
+static char *vendor_name = NULL;
+
+/* resource file to PCB units scale factor */
+static double sf;
+
+
+/* type of drill mapping */
+#define CLOSEST 1
+#define ROUND_UP 0
+static int rounding_method = ROUND_UP;
+
+#define FREE(x) if((x) != NULL) { free (x) ; (x) = NULL; }
+
+/* ************************************************************ */
+
+static const char apply_vendor_syntax[] = "ApplyVendor()";
+
+static const char apply_vendor_help[] = "Applies the currently loaded vendor drill table to the current design.";
+
+/* %start-doc actions ApplyVendor
+ at cindex vendor map
+ at cindex vendor drill table
+ at findex ApplyVendor()
+
+This will modify all of your drill holes to match the list of allowed
+sizes for your vendor.
+%end-doc */
+
+int ActionApplyVendor(int argc, const char **argv, Coord x, Coord y)
+{
+	hid_action("Busy");
+	apply_vendor_map();
+	return 0;
+}
+
+/* ************************************************************ */
+
+static const char unload_vendor_syntax[] = "UnloadVendor()";
+
+static const char unload_vendor_help[] = "Unloads the current vendor drill mapping table.";
+
+/* %start-doc actions UnloadVendor
+
+ at cindex vendor map
+ at cindex vendor drill table
+ at findex UnloadVendor()
+
+%end-doc */
+
+int ActionUnloadVendor(int argc, const char **argv, Coord x, Coord y)
+{
+	cached_drill = -1;
+
+	vendor_free_all();
+	return 0;
+}
+
+/* ************************************************************ */
+
+static const char load_vendor_syntax[] = "LoadVendorFrom(filename)";
+
+static const char load_vendor_help[] = "Loads the specified vendor lihata file.";
+
+/* %start-doc actions LoadVendorFrom
+
+ at cindex vendor map
+ at cindex vendor drill table
+ at findex LoadVendorFrom()
+
+ at table @var
+ at item filename
+Name of the vendor lihata file.  If not specified, the user will
+be prompted to enter one.
+ at end table
+
+%end-doc */
+
+int ActionLoadVendorFrom(int argc, const char **argv, Coord x, Coord y)
+{
+	const char *fname = NULL;
+	static char *default_file = NULL;
+	const char *sval;
+	lht_doc_t *doc;
+	lht_node_t *drlres;
+	pcb_bool free_fname = pcb_false;
+
+	cached_drill = -1;
+
+	fname = argc ? argv[0] : NULL;
+
+	if (!fname || !*fname) {
+		fname = gui->fileselect(_("Load Vendor Resource File..."),
+														_("Picks a vendor resource file to load.\n"
+															"This file can contain drc settings for a\n"
+															"particular vendor as well as a list of\n"
+															"predefined drills which are allowed."), default_file, ".res", "vendor", HID_FILESELECT_READ);
+		if (fname == NULL)
+			AFAIL(load_vendor);
+
+		free_fname = pcb_true;
+
+		free(default_file);
+		default_file = NULL;
+
+		if (fname && *fname)
+			default_file = pcb_strdup(fname);
+	}
+
+	vendor_free_all();
+
+	/* load the resource file */
+	doc = hid_cfg_load_lht(fname);
+	if (doc == NULL) {
+		Message(PCB_MSG_DEFAULT, _("Could not load vendor resource file \"%s\"\n"), fname);
+		return 1;
+	}
+
+	/* figure out the vendor name, if specified */
+	vendor_name = (char *) UNKNOWN(hid_cfg_text_value(doc, "vendor"));
+
+	/* figure out the units, if specified */
+	sval = hid_cfg_text_value(doc, "/units");
+	if (sval == NULL) {
+		sf = PCB_MIL_TO_COORD(1);
+	}
+	else if ((NSTRCMP(sval, "mil") == 0) || (NSTRCMP(sval, "mils") == 0)) {
+		sf = PCB_MIL_TO_COORD(1);
+	}
+	else if ((NSTRCMP(sval, "inch") == 0) || (NSTRCMP(sval, "inches") == 0)) {
+		sf = PCB_INCH_TO_COORD(1);
+	}
+	else if (NSTRCMP(sval, "mm") == 0) {
+		sf = PCB_MM_TO_COORD(1);
+	}
+	else {
+		Message(PCB_MSG_DEFAULT, "\"%s\" is not a supported units.  Defaulting to inch\n", sval);
+		sf = PCB_INCH_TO_COORD(1);
+	}
+
+	/* default to ROUND_UP */
+	rounding_method = ROUND_UP;
+	sval = hid_cfg_text_value(doc, "/round");
+	if (sval != NULL) {
+		if (NSTRCMP(sval, "up") == 0) {
+			rounding_method = ROUND_UP;
+		}
+		else if (NSTRCMP(sval, "nearest") == 0) {
+			rounding_method = CLOSEST;
+		}
+		else {
+			Message(PCB_MSG_DEFAULT, _("\"%s\" is not a valid rounding type.  Defaulting to up\n"), sval);
+			rounding_method = ROUND_UP;
+		}
+	}
+
+	process_skips(lht_tree_path(doc, "/", "/skips", 1, NULL));
+
+	/* extract the drillmap resource */
+	drlres = lht_tree_path(doc, "/", "/drillmap", 1, NULL);
+	if (drlres != NULL) {
+		if (drlres->type == LHT_LIST) {
+			lht_node_t *n;
+			for(n = drlres->data.list.first; n != NULL; n = n->next) {
+				if (n->type != LHT_TEXT)
+					hid_cfg_error(n, "Broken drillmap: /drillmap should contain text children only\n");
+				else
+					add_to_drills(n->data.text.value);
+			}
+		}
+		Message(PCB_MSG_DEFAULT, _("Broken drillmap: /drillmap should be a list\n"));
+	}
+	else
+		Message(PCB_MSG_DEFAULT, _("No drillmap resource found\n"));
+
+	sval = hid_cfg_text_value(doc, "/drc/copper_space");
+	if (sval != NULL) {
+		PCB->Bloat = floor(sf * atof(sval) + 0.5);
+		Message(PCB_MSG_DEFAULT, _("Set DRC minimum copper spacing to %.2f mils\n"), 0.01 * PCB->Bloat);
+	}
+
+	sval = hid_cfg_text_value(doc, "/drc/copper_overlap");
+	if (sval != NULL) {
+		PCB->Shrink = floor(sf * atof(sval) + 0.5);
+		Message(PCB_MSG_DEFAULT, _("Set DRC minimum copper overlap to %.2f mils\n"), 0.01 * PCB->Shrink);
+	}
+
+	sval = hid_cfg_text_value(doc, "/drc/copper_width");
+	if (sval != NULL) {
+		PCB->minWid = floor(sf * atof(sval) + 0.5);
+		Message(PCB_MSG_DEFAULT, _("Set DRC minimum copper spacing to %.2f mils\n"), 0.01 * PCB->minWid);
+	}
+
+	sval = hid_cfg_text_value(doc, "/drc/silk_width");
+	if (sval != NULL) {
+		PCB->minSlk = floor(sf * atof(sval) + 0.5);
+		Message(PCB_MSG_DEFAULT, _("Set DRC minimum silk width to %.2f mils\n"), 0.01 * PCB->minSlk);
+	}
+
+	sval = hid_cfg_text_value(doc, "/drc/min_drill");
+	if (sval != NULL) {
+		PCB->minDrill = floor(sf * atof(sval) + 0.5);
+		Message(PCB_MSG_DEFAULT, _("Set DRC minimum drill diameter to %.2f mils\n"), 0.01 * PCB->minDrill);
+	}
+
+	sval = hid_cfg_text_value(doc, "/drc/min_ring");
+	if (sval != NULL) {
+		PCB->minRing = floor(sf * atof(sval) + 0.5);
+		Message(PCB_MSG_DEFAULT, _("Set DRC minimum annular ring to %.2f mils\n"), 0.01 * PCB->minRing);
+	}
+
+	Message(PCB_MSG_DEFAULT, _("Loaded %d vendor drills from %s\n"), n_vendor_drills, fname);
+	Message(PCB_MSG_DEFAULT, _("Loaded %d RefDes skips, %d Value skips, %d Descr skips\n"), n_refdes, n_value, n_descr);
+
+	conf_set(CFR_DESIGN, "plugins/vendor/enable", -1, "0", POL_OVERWRITE);
+
+	apply_vendor_map();
+	if (free_fname)
+		free((char*)fname);
+	lht_dom_uninit(doc);
+	return 0;
+}
+
+static void apply_vendor_map(void)
+{
+	int changed, tot;
+	pcb_bool state;
+
+	state = conf_vendor.plugins.vendor.enable;
+
+	/* enable mapping */
+	conf_force_set_bool(conf_vendor.plugins.vendor.enable, 1);
+
+	/* reset our counts */
+	changed = 0;
+	tot = 0;
+
+	/* If we have loaded vendor drills, then apply them to the design */
+	if (n_vendor_drills > 0) {
+
+		/* first all the vias */
+		VIA_LOOP(PCB->Data);
+		{
+			tot++;
+			if (via->DrillingHole != vendorDrillMap(via->DrillingHole)) {
+				/* only change unlocked vias */
+				if (!TEST_FLAG(PCB_FLAG_LOCK, via)) {
+					if (ChangeObject2ndSize(PCB_TYPE_VIA, via, NULL, NULL, vendorDrillMap(via->DrillingHole), pcb_true, pcb_false))
+						changed++;
+					else {
+						Message(PCB_MSG_DEFAULT, _
+										("Via at %.2f, %.2f not changed.  Possible reasons:\n"
+										 "\t- pad size too small\n"
+										 "\t- new size would be too large or too small\n"), 0.01 * via->X, 0.01 * via->Y);
+					}
+				}
+				else {
+					Message(PCB_MSG_DEFAULT, _("Locked via at %.2f, %.2f not changed.\n"), 0.01 * via->X, 0.01 * via->Y);
+				}
+			}
+		}
+		END_LOOP;
+
+		/* and now the pins */
+		ELEMENT_LOOP(PCB->Data);
+		{
+			/*
+			 * first figure out if this element should be skipped for some
+			 * reason
+			 */
+			if (vendorIsElementMappable(element)) {
+				/* the element is ok to modify, so iterate over its pins */
+				PIN_LOOP(element);
+				{
+					tot++;
+					if (pin->DrillingHole != vendorDrillMap(pin->DrillingHole)) {
+						if (!TEST_FLAG(PCB_FLAG_LOCK, pin)) {
+							if (ChangeObject2ndSize(PCB_TYPE_PIN, element, pin, NULL, vendorDrillMap(pin->DrillingHole), pcb_true, pcb_false))
+								changed++;
+							else {
+								Message(PCB_MSG_DEFAULT, _
+												("Pin %s (%s) at %.2f, %.2f (element %s, %s, %s) not changed.\n"
+												 "\tPossible reasons:\n"
+												 "\t- pad size too small\n"
+												 "\t- new size would be too large or too small\n"),
+												UNKNOWN(pin->Number), UNKNOWN(pin->Name),
+												0.01 * pin->X, 0.01 * pin->Y,
+												UNKNOWN(NAMEONPCB_NAME(element)), UNKNOWN(VALUE_NAME(element)), UNKNOWN(DESCRIPTION_NAME(element)));
+							}
+						}
+						else {
+							Message(PCB_MSG_DEFAULT, _("Locked pin at %-6.2f, %-6.2f not changed.\n"), 0.01 * pin->X, 0.01 * pin->Y);
+						}
+					}
+				}
+				END_LOOP;
+			}
+		}
+		END_LOOP;
+
+		Message(PCB_MSG_DEFAULT, _("Updated %d drill sizes out of %d total\n"), changed, tot);
+
+#warning TODO: this should not happen; modify some local setting?
+#if 0
+		/* Update the current Via */
+		if (conf_core.design.via_drilling_hole != vendorDrillMap(Settings.ViaDrillingHole)) {
+			changed++;
+			Settings.ViaDrillingHole = vendorDrillMap(Settings.ViaDrillingHole);
+			Message(PCB_MSG_DEFAULT, _("Adjusted active via hole size to be %6.2f mils\n"), 0.01 * Settings.ViaDrillingHole);
+		}
+
+		/* and update the vias for the various routing styles */
+		for (i = 0; i < NUM_STYLES; i++) {
+			if (PCB->RouteStyle[i].Hole != vendorDrillMap(PCB->RouteStyle[i].Hole)) {
+				changed++;
+				PCB->RouteStyle[i].Hole = vendorDrillMap(PCB->RouteStyle[i].Hole);
+				Message(PCB_MSG_DEFAULT, _
+								("Adjusted %s routing style via hole size to be %6.2f mils\n"),
+								PCB->RouteStyle[i].Name, 0.01 * PCB->RouteStyle[i].Hole);
+				if (PCB->RouteStyle[i].Diameter < PCB->RouteStyle[i].Hole + MIN_PINORVIACOPPER) {
+					PCB->RouteStyle[i].Diameter = PCB->RouteStyle[i].Hole + MIN_PINORVIACOPPER;
+					Message(PCB_MSG_DEFAULT, _
+									("Increased %s routing style via diameter to %6.2f mils\n"),
+									PCB->RouteStyle[i].Name, 0.01 * PCB->RouteStyle[i].Diameter);
+				}
+			}
+		}
+#endif
+		/*
+		 * if we've changed anything, indicate that we need to save the
+		 * file, redraw things, and make sure we can undo.
+		 */
+		if (changed) {
+			SetChangedFlag(pcb_true);
+			Redraw();
+			IncrementUndoSerialNumber();
+		}
+	}
+
+	/* restore mapping on/off */
+	conf_force_set_bool(conf_vendor.plugins.vendor.enable, state);
+}
+
+/* for a given drill size, find the closest vendor drill size */
+int vendorDrillMap(int in)
+{
+	int i, min, max;
+
+	if (in == cached_drill)
+		return cached_map;
+	cached_drill = in;
+
+	/* skip the mapping if we don't have a vendor drill table */
+	if ((n_vendor_drills == 0) || (vendor_drills == NULL)
+			|| (!conf_vendor.plugins.vendor.enable)) {
+		cached_map = in;
+		return in;
+	}
+
+	/* are we smaller than the smallest drill? */
+	if (in <= vendor_drills[0]) {
+		cached_map = vendor_drills[0];
+		return vendor_drills[0];
+	}
+
+	/* are we larger than the largest drill? */
+	if (in > vendor_drills[n_vendor_drills - 1]) {
+		Message(PCB_MSG_DEFAULT, _("Vendor drill list does not contain a drill >= %6.2f mil\n"
+							"Using %6.2f mil instead.\n"), 0.01 * in, 0.01 * vendor_drills[n_vendor_drills - 1]);
+		cached_map = vendor_drills[n_vendor_drills - 1];
+		return vendor_drills[n_vendor_drills - 1];
+	}
+
+	/* figure out which 2 drills are closest in size */
+	min = 0;
+	max = n_vendor_drills - 1;
+	while (max - min > 1) {
+		i = (max + min) / 2;
+		if (in > vendor_drills[i])
+			min = i;
+		else
+			max = i;
+	}
+	i = max;
+
+	/* now round per the rounding mode */
+	if (rounding_method == CLOSEST) {
+		/* find the closest drill size */
+		if ((in - vendor_drills[i - 1]) > (vendor_drills[i] - in)) {
+			cached_map = vendor_drills[i];
+			return vendor_drills[i];
+		}
+		else {
+			cached_map = vendor_drills[i - 1];
+			return vendor_drills[i - 1];
+		}
+	}
+	else {
+		/* always round up */
+		cached_map = vendor_drills[i];
+		return vendor_drills[i];
+	}
+
+}
+
+/* add a drill size to the vendor drill list */
+static void add_to_drills(char *sval)
+{
+	double tmpd;
+	int val;
+	int k, j;
+
+	/* increment the count and make sure we have memory */
+	n_vendor_drills++;
+	if ((vendor_drills = (int *) realloc(vendor_drills, n_vendor_drills * sizeof(int))) == NULL) {
+		fprintf(stderr, "realloc() failed to allocate %ld bytes\n", (unsigned long) n_vendor_drills * sizeof(int));
+		return;
+	}
+
+	/* string to a value with the units scale factor in place */
+	tmpd = atof(sval);
+	val = floor(sf * tmpd + 0.5);
+
+	/*
+	 * We keep the array of vendor drills sorted to make it easier to
+	 * do the rounding later.  The algorithm used here is not so efficient,
+	 * but we're not dealing with much in the way of data.
+	 */
+
+	/* figure out where to insert the value to keep the array sorted.  */
+	k = 0;
+	while ((k < n_vendor_drills - 1) && (vendor_drills[k] < val))
+		k++;
+
+	if (k == n_vendor_drills - 1) {
+		vendor_drills[n_vendor_drills - 1] = val;
+	}
+	else {
+		/* move up the existing drills to make room */
+		for (j = n_vendor_drills - 1; j > k; j--) {
+			vendor_drills[j] = vendor_drills[j - 1];
+		}
+
+		vendor_drills[k] = val;
+	}
+}
+
+/* deal with the "skip" subresource */
+static void process_skips(lht_node_t *res)
+{
+	char *sval;
+	int *cnt;
+	char ***lst = NULL;
+	lht_node_t *n;
+
+	if (res == NULL)
+		return;
+
+	if (res->type != LHT_LIST)
+		hid_cfg_error(res, "skips must be a list.\n");
+
+	for(n = res->data.list.first; n != NULL; n = n->next) {
+		if (n->type == LHT_TEXT) {
+			if (NSTRCMP(n->name, "refdes") == 0) {
+				cnt = &n_refdes;
+				lst = &ignore_refdes;
+			}
+			else if (NSTRCMP(n->name, "value") == 0) {
+				cnt = &n_value;
+				lst = &ignore_value;
+			}
+			else if (NSTRCMP(n->name, "descr") == 0) {
+				cnt = &n_descr;
+				lst = &ignore_descr;
+			}
+			else {
+				hid_cfg_error(n, "invalid skip name; must be one of refdes, value, descr");
+				continue;
+			}
+			/* add the entry to the appropriate list */
+			sval = n->data.text.value;
+			(*cnt)++;
+			if ((*lst = (char **) realloc(*lst, (*cnt) * sizeof(char *))) == NULL) {
+				fprintf(stderr, "realloc() failed\n");
+				exit(-1);
+			}
+			(*lst)[*cnt - 1] = pcb_strdup(sval);
+		}
+		else
+			hid_cfg_error(n, "invalid skip type; must be text");
+	}
+}
+
+pcb_bool vendorIsElementMappable(ElementTypePtr element)
+{
+	int i;
+	int noskip;
+
+	if (!conf_vendor.plugins.vendor.enable)
+		return pcb_false;
+
+	noskip = 1;
+	for (i = 0; i < n_refdes; i++) {
+		if ((NSTRCMP(UNKNOWN(NAMEONPCB_NAME(element)), ignore_refdes[i]) == 0)
+				|| rematch(ignore_refdes[i], UNKNOWN(NAMEONPCB_NAME(element)))) {
+			Message(PCB_MSG_DEFAULT, _("Vendor mapping skipped because refdes = %s matches %s\n"), UNKNOWN(NAMEONPCB_NAME(element)), ignore_refdes[i]);
+			noskip = 0;
+		}
+	}
+	if (noskip)
+		for (i = 0; i < n_value; i++) {
+			if ((NSTRCMP(UNKNOWN(VALUE_NAME(element)), ignore_value[i]) == 0)
+					|| rematch(ignore_value[i], UNKNOWN(VALUE_NAME(element)))) {
+				Message(PCB_MSG_DEFAULT, _("Vendor mapping skipped because value = %s matches %s\n"), UNKNOWN(VALUE_NAME(element)), ignore_value[i]);
+				noskip = 0;
+			}
+		}
+
+	if (noskip)
+		for (i = 0; i < n_descr; i++) {
+			if ((NSTRCMP(UNKNOWN(DESCRIPTION_NAME(element)), ignore_descr[i])
+					 == 0)
+					|| rematch(ignore_descr[i], UNKNOWN(DESCRIPTION_NAME(element)))) {
+				Message(PCB_MSG_DEFAULT, _
+								("Vendor mapping skipped because descr = %s matches %s\n"),
+								UNKNOWN(DESCRIPTION_NAME(element)), ignore_descr[i]);
+				noskip = 0;
+			}
+		}
+
+	if (noskip && TEST_FLAG(PCB_FLAG_LOCK, element)) {
+		Message(PCB_MSG_DEFAULT, _("Vendor mapping skipped because element %s is locked\n"), UNKNOWN(NAMEONPCB_NAME(element)));
+		noskip = 0;
+	}
+
+	if (noskip)
+		return pcb_true;
+	else
+		return pcb_false;
+}
+
+static pcb_bool rematch(const char *re, const char *s)
+{
+	int result;
+	re_sei_t *regex;
+
+	/* compile the regular expression */
+	regex = re_sei_comp(re);
+	if (re_sei_errno(regex) != 0) {
+		Message(PCB_MSG_DEFAULT, _("regexp error: %s\n"), re_error_str(re_sei_errno(regex)));
+		re_sei_free(regex);
+		return pcb_false;
+	}
+
+	result = re_sei_exec(regex, s);
+	re_sei_free(regex);
+
+	if (result != 0)
+		return pcb_true;
+	else
+		return pcb_false;
+}
+
+static const char *vendor_cookie = "vendor drill mapping";
+
+HID_Action vendor_action_list[] = {
+	{"ApplyVendor", 0, ActionApplyVendor,
+	 apply_vendor_help, apply_vendor_syntax}
+	,
+	{"UnloadVendor", 0, ActionUnloadVendor,
+	 unload_vendor_help, unload_vendor_syntax}
+	,
+	{"LoadVendorFrom", 0, ActionLoadVendorFrom,
+	 load_vendor_help, load_vendor_syntax}
+};
+
+REGISTER_ACTIONS(vendor_action_list, vendor_cookie)
+
+static char **vendor_free_vect(char **lst, int *len)
+{
+	if (lst != NULL) {
+		int n;
+		for(n = 0; n < *len; n++)
+			if (lst[n] != NULL)
+				free(lst[n]);
+		free(lst);
+	}
+	*len = 0;
+	return NULL;
+}
+
+static void vendor_free_all(void)
+{
+	ignore_refdes = vendor_free_vect(ignore_refdes, &n_refdes);
+	ignore_value = vendor_free_vect(ignore_value, &n_value);
+	ignore_descr = vendor_free_vect(ignore_descr, &n_descr);
+	if (vendor_drills != NULL) {
+		free(vendor_drills);
+		vendor_drills = NULL;
+		n_vendor_drills = 0;
+	}
+	cached_drill = -1;
+}
+
+static void hid_vendordrill_uninit(void)
+{
+	hid_remove_actions_by_cookie(vendor_cookie);
+	vendor_free_all();
+	conf_unreg_fields("plugins/vendor/");
+}
+
+#include "dolists.h"
+pcb_uninit_t hid_vendordrill_init(void)
+{
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_vendor, field,isarray,type_name,cpath,cname,desc,flags);
+#include "vendor_conf_fields.h"
+
+	stub_vendorDrillMap = vendorDrillMap;
+	stub_vendorIsElementMappable = vendorIsElementMappable;
+
+	REGISTER_ACTIONS(vendor_action_list, vendor_cookie)
+	return hid_vendordrill_uninit;
+}
diff --git a/src_plugins/vendordrill/vendor.example.lht b/src_plugins/vendordrill/vendor.example.lht
new file mode 100644
index 0000000..7b31d53
--- /dev/null
+++ b/src_plugins/vendordrill/vendor.example.lht
@@ -0,0 +1,51 @@
+ha:vendor_drill_map {
+	# Optional name of the vendor
+	vendor = Vendor Name
+
+	# units for dimensions in this file.
+	# Allowed values:  mil/inch/mm
+	units = mil
+
+	# When mapping drill sizes, select the nearest size
+	# or always round up.  Allowed values:  up/nearest
+	round = up
+
+	# drill table
+	li:drillmap = {
+		# The list of vendor drill sizes.  Units are as specified
+		# above.
+		20
+		28
+		35
+		38
+		42
+		52
+		59.5
+		86
+		125
+		152
+	}
+
+	# optional section for skipping mapping of certain elements
+	# based on reference designator, value, or description
+	# this is useful for critical parts where you may not
+	# want to change the drill size.  Note that the strings
+	# are regular expressions.
+	li:skips = {
+		refdes = {^J3$}                   # Skip J3.
+		refdes = {J3}                     # Skip anything with J3 as part of the refdes.
+		refdes = {^U[1-3]$}               # Skip U1, U2, U3
+		refdes = {^X.*}                   # Skip anything starting with X.
+		value  = {^JOHNSTECH_.*}          # Skip all Johnstech footprints based on the value of a part.
+		descr  = {^AMP_MICTOR_767054_1$}  # Skip based on the description.
+	}
+
+	# If specified, this section will change the current DRC
+	# settings for the design.  Units are as specified above.
+	ha:drc = {
+		copper_space = 7
+		copper_width = 7
+		silk_width = 10
+		copper_overlap = 4
+	}
+}
diff --git a/src_plugins/vendordrill/vendor.h b/src_plugins/vendordrill/vendor.h
new file mode 100644
index 0000000..9d4c454
--- /dev/null
+++ b/src_plugins/vendordrill/vendor.h
@@ -0,0 +1,29 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2004 Dan McMahill
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCB_VENDOR_H
+#define PCB_VENDOR_H
+
+int vendorDrillMap(int);
+pcb_bool vendorIsElementMappable(ElementTypePtr);
+
+#endif /* __VENDOR_H__ */
diff --git a/src_plugins/vendordrill/vendor_conf.h b/src_plugins/vendordrill/vendor_conf.h
new file mode 100644
index 0000000..5212cb6
--- /dev/null
+++ b/src_plugins/vendordrill/vendor_conf.h
@@ -0,0 +1,14 @@
+#ifndef PCB_VENDOR_CONF_H
+#define PCB_VENDOR_CONF_H
+
+#include "conf.h"
+
+typedef struct {
+	const struct plugins {
+		const struct vendor {
+			CFT_BOOLEAN enable;         /* Enable vendor mapping */
+		} vendor;
+	} plugins;
+} conf_vendor_t;
+
+#endif
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..a6098e9
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,7 @@
+test:
+	cd pcbflags && make all && make test
+	cd conf && make all && make test
+	cd strflags && make all && make test
+	cd pcb-printf && make all && make test
+	cd uniq_name && make all && make test
+	cd propedit && make all && make test
diff --git a/tests/conf/Makefile b/tests/conf/Makefile
new file mode 100644
index 0000000..3663b61
--- /dev/null
+++ b/tests/conf/Makefile
@@ -0,0 +1,55 @@
+CFLAGS = -Wall -g -I../.. -I../../src_3rd -I../../src
+LDFLAGS = -lm
+
+LIB_PCB_OBJS= \
+	../../src/hid_cfg.o \
+	../../src/misc_util.o \
+	../../src/unit.o \
+	../../src/list_conf.o \
+	../../src/paths.o \
+	../../src/conf.o \
+	../../src/conf_core.o \
+	../../src/conf_hid.o \
+	../../src/conf_internal.o \
+	../../src/pcb-printf.o \
+	../../src/compat_misc.o \
+	../../src/compat_fs.o
+
+LIB_PLUGIN_OBJS=\
+	../../src_plugins/debug/debug_conf.o
+
+LIB_3RD_OBJS= \
+	../../src_3rd/liblihata/parser.o \
+	../../src_3rd/liblihata/dom.o \
+	../../src_3rd/liblihata/dom_list.o \
+	../../src_3rd/liblihata/dom_hash.o \
+	../../src_3rd/liblihata/dom_table.o \
+	../../src_3rd/liblihata/lihata.o \
+	../../src_3rd/liblihata/hash_str.o \
+	../../src_3rd/liblihata/tree.o \
+	../../src_3rd/liblihata/tree_list.o \
+	../../src_3rd/liblihata/tree_hash.o \
+	../../src_3rd/liblihata/tree_table.o \
+	../../src_3rd/liblihata/tree_symlink.o \
+	../../src_3rd/liblihata/tree_path.o \
+	../../src_3rd/liblihata/genht/hash.o \
+	../../src_3rd/liblihata/genht/htsp.o \
+	../../src_3rd/liblihata/genht/htpi.o \
+	../../src_3rd/liblihata/genht/htip.o \
+	../../src_3rd/liblihata/genht/htpp.o \
+	../../src_3rd/genvector/gds_char.o \
+	../../src_3rd/genvector/vtp0.o
+
+all: conftest
+
+conftest: conftest.o help.o $(LIB_3RD_OBJS) $(LIB_PCB_OBJS) $(LIB_PLUGIN_OBJS)
+
+conftest.o: conftest.c
+
+help.o: help.c
+
+$(LIB_3RD_OBJS) $(LIB_PCB_OBJS):
+	cd ../../src && make
+
+test:
+	cd tests && make all
diff --git a/tests/conf/conftest.c b/tests/conf/conftest.c
new file mode 100644
index 0000000..269196e
--- /dev/null
+++ b/tests/conf/conftest.c
@@ -0,0 +1,381 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include "conf.h"
+#include "conf_hid.h"
+#include "conf_core.h"
+#include "compat_misc.h"
+#include "src_plugins/debug/debug_conf.h"
+
+int lineno = 0;
+int global_notify = 0;
+conf_hid_id_t hid_id;
+const char *hid_cookie = "conftest cookie";
+
+void Message(const char *Format, ...)
+{
+	va_list args;
+
+	fprintf(stderr, "Error in line %d: ", lineno);
+
+	va_start(args, Format);
+	vfprintf(stderr, Format, args);
+	va_end(args);
+
+	fprintf(stderr, "\n");
+}
+
+void watch_pre(conf_native_t *cfg)
+{
+	printf("watch_pre:  '%s' old value\n", cfg->hash_path);
+}
+
+void watch_post(conf_native_t *cfg)
+{
+	printf("watch_post: '%s' new value\n", cfg->hash_path);
+}
+
+void notify_pre(conf_native_t *cfg)
+{
+	if (global_notify)
+		printf("notify_pre:  '%s' old value\n", cfg->hash_path);
+}
+
+void notify_post(conf_native_t *cfg)
+{
+	if (global_notify)
+		printf("notify_post: '%s' new value\n", cfg->hash_path);
+}
+
+conf_hid_callbacks_t watch_cbs = {watch_pre, watch_post, NULL, NULL};
+conf_hid_callbacks_t global_cbs = {notify_pre, notify_post, NULL, NULL};
+
+
+extern lht_doc_t *conf_root[];
+void cmd_dump(char *arg)
+{
+	if (arg == NULL) {
+		Message("Need an arg: native or lihata");
+		return;
+	}
+	if (strncmp(arg, "native", 6) == 0) {
+		arg+=7;
+		while(isspace(*arg)) arg++;
+		conf_dump(stdout, "", 1, arg);
+	}
+	else if (strncmp(arg, "lihata", 6) == 0) {
+		arg+=7;
+		while(isspace(*arg)) arg++;
+		conf_role_t role = conf_role_parse(arg);
+		if (role == CFR_invalid) {
+			Message("Invalid role: '%s'", arg);
+			return;
+		}
+		if (conf_root[role] != NULL)
+			lht_dom_export(conf_root[role]->root, stdout, "");
+		else
+			printf("<empty>\n");
+	}
+	else
+		Message("Invalid dump mode: '%s'", arg);
+}
+
+void cmd_print(char *arg)
+{
+	conf_native_t *node;
+	gds_t s;
+
+	if (arg == NULL) {
+		Message("Need an arg: a native path");
+		return;
+	}
+	node = conf_get_field(arg);
+	if (node == NULL) {
+		Message("No such path: '%s'", arg);
+		return;
+	}
+	gds_init(&s);
+	conf_print_native((conf_pfn)pcb_append_printf, &s, NULL, 0, node);
+	printf("%s='%s'\n", node->hash_path, s.array);
+	gds_uninit(&s);
+}
+
+void cmd_load(char *arg, int is_text)
+{
+	char *fn;
+	conf_role_t role ;
+
+	if (arg == NULL) {
+		help:;
+		Message("Need 2 args: role and %s", (is_text ? "lihata text" : "file name"));
+		return;
+	}
+
+	if (*arg == '*') {
+		conf_load_all(NULL, NULL);
+		return;
+	}
+
+	fn = strchr(arg, ' ');
+	if (fn == NULL)
+		goto help;
+	*fn = '\0';
+	fn++;
+	while(isspace(*fn)) fn++;
+
+	role = conf_role_parse(arg);
+	if (role == CFR_invalid) {
+		Message("Invalid role: '%s'", arg);
+		return;
+	}
+	printf("Result: %d\n", conf_load_as(role, fn, is_text));
+	conf_update(NULL);
+}
+
+conf_policy_t current_policy = POL_OVERWRITE;
+conf_role_t current_role = CFR_DESIGN;
+
+void cmd_policy(char *arg)
+{
+	conf_policy_t np = conf_policy_parse(arg);
+	if (np == POL_invalid)
+		Message("Invalid/unknown policy: '%s'", arg);
+	else
+		current_policy = np;
+}
+
+void cmd_role(char *arg)
+{
+	conf_role_t nr = conf_role_parse(arg);
+	if (nr == CFR_invalid)
+		Message("Invalid/unknown role: '%s'", arg);
+	else
+		current_role = nr;
+}
+
+void cmd_chprio(char *arg)
+{
+	char *end;
+	int np = strtol(arg == NULL ? "" : arg, &end, 10);
+	lht_node_t *first;
+
+	if ((*end != '\0') || (np < 0)) {
+		Message("Invalid integer prio: '%s'", arg);
+		return;
+	}
+	first = conf_lht_get_first(current_role);
+	if (first != NULL) {
+		char tmp[128];
+		char *end;
+		end = strchr(first->name, '-');
+		if (end != NULL)
+			*end = '\0';
+		sprintf(tmp, "%s-%d", first->name, np);
+		free(first->name);
+		first->name = pcb_strdup(tmp);
+		conf_update(NULL);
+	}
+}
+
+void cmd_chpolicy(char *arg)
+{
+	conf_policy_t np;
+	lht_node_t *first;
+
+	if (arg == NULL) {
+		Message("need a policy", arg);
+		return;
+	}
+	np = conf_policy_parse(arg);
+	if (np == POL_invalid) {
+		Message("Invalid integer policy: '%s'", arg);
+		return;
+	}
+
+	first = conf_lht_get_first(current_role);
+	if (first != NULL) {
+		char tmp[128];
+		char *end;
+		end = strchr(first->name, '-');
+		if (end != NULL) {
+			sprintf(tmp, "%s%s", arg, end);
+			free(first->name);
+			first->name = pcb_strdup(tmp);
+		}
+		else {
+			free(first->name);
+			first->name = pcb_strdup(arg);
+		}
+		conf_update(NULL);
+	}
+}
+
+void cmd_set(char *arg)
+{
+	char *path, *val;
+	int res;
+
+	path = arg;
+	val = strpbrk(path, " \t=");
+	if (val == NULL) {
+		Message("set needs a value");
+		return;
+	}
+	*val = '\0';
+	val++;
+	while(isspace(*val) || (*val == '=')) val++;
+
+	res = conf_set(current_role, path, -1, val, current_policy);
+	if (res != 0)
+		printf("set error: %d\n", res);
+}
+
+void cmd_watch(char *arg, int add)
+{
+	conf_native_t *n = conf_get_field(arg);
+	if (n == NULL) {
+		Message("unknown path");
+		return;
+	}
+	conf_hid_set_cb(n, hid_id, (add ? &watch_cbs : NULL));
+}
+
+void cmd_notify(char *arg)
+{
+	if (arg != NULL) {
+		if (strcmp(arg, "on") == 0)
+			global_notify = 1;
+		else if (strcmp(arg, "off") == 0)
+			global_notify = 0;
+	}
+	printf("Notify is %s\n", global_notify ? "on" : "off");
+}
+
+void cmd_echo(char *arg)
+{
+	if (arg != NULL)
+		printf("%s\n", arg);
+}
+
+void cmd_reset(char *arg)
+{
+	if (arg == NULL) {
+		conf_reset(current_role, "<cmd_reset current>");
+	}
+	else if (*arg == '*') {
+		int n;
+		for(n = 0; n < CFR_max_real; n++)
+			conf_reset(n, "<cmd_reset *>");
+	}
+	else {
+		conf_role_t role = conf_role_parse(arg);
+		if (role == CFR_invalid) {
+			Message("Invalid role: '%s'", arg);
+			return;
+		}
+		conf_reset(role, "<cmd_reset role>");
+	}
+	conf_update(NULL);
+}
+
+extern void cmd_help(char *arg);
+
+
+char line[8192];
+
+/* returns 1 if there's more to read */
+int getline_cont(FILE *f)
+{
+	char *end = line + strlen(line) - 1;
+	int offs = 0, cont;
+
+	if (feof(f))
+		return 0;
+
+	do {
+		int remain = sizeof(line)-offs;
+		assert(remain > 0);
+		cont = 0;
+		if (fgets(line+offs, remain, f)) {
+			char *start = line+offs;
+			int len = strlen(start);
+			lineno++;
+			end = start + len - 1;
+			while((end >= start) && ((*end == '\n') || (*end == '\r'))) {
+				*end = '\0';
+				end--;
+			}
+			if ((end >= start) && (*end == '\\')) {
+				cont = 1;
+				*end = '\n';
+			}
+			offs += len-1;
+		}
+		else {
+			if (offs == 0)
+				return 0;
+		}
+	} while(cont);
+	return 1;
+}
+
+int main()
+{
+
+	hid_id = conf_hid_reg(hid_cookie, &global_cbs);
+
+	conf_init();
+	conf_core_init();
+	conf_core_postproc();
+	conf_reset(CFR_SYSTEM, "<main>");
+	conf_reset(CFR_USER, "<main>");
+
+	while(getline_cont(stdin)) {
+		char *arg, *cmd = line;
+		while(isspace(*cmd)) cmd++;
+		if ((*cmd == '#') || (*cmd == '\0'))
+			continue;
+		arg = strpbrk(cmd, " \t");
+		if (arg != NULL) {
+			*arg = '\0';
+			arg++;
+			while(isspace(*arg)) arg++;
+		}
+
+		if (strcmp(cmd, "dump") == 0)
+			cmd_dump(arg);
+		else if (strcmp(cmd, "print") == 0)
+			cmd_print(arg);
+		else if (strcmp(cmd, "load") == 0)
+			cmd_load(arg, 0);
+		else if (strcmp(cmd, "paste") == 0)
+			cmd_load(arg, 1);
+		else if (strcmp(cmd, "reset") == 0)
+			cmd_reset(arg);
+		else if (strcmp(cmd, "set") == 0)
+			cmd_set(arg);
+		else if (strcmp(cmd, "policy") == 0)
+			cmd_policy(arg);
+		else if (strcmp(cmd, "chprio") == 0)
+			cmd_chprio(arg);
+		else if (strcmp(cmd, "chpolicy") == 0)
+			cmd_chpolicy(arg);
+		else if (strcmp(cmd, "role") == 0)
+			cmd_role(arg);
+		else if (strcmp(cmd, "watch") == 0)
+			cmd_watch(arg, 1);
+		else if (strcmp(cmd, "unwatch") == 0)
+			cmd_watch(arg, 0);
+		else if (strcmp(cmd, "notify") == 0)
+			cmd_notify(arg);
+		else if (strcmp(cmd, "echo") == 0)
+			cmd_echo(arg);
+		else if (strcmp(cmd, "help") == 0)
+			cmd_help(arg);
+		else
+			Message("unknown command '%s'", cmd);
+	}
+
+	conf_hid_unreg(hid_cookie);
+	conf_uninit();
+}
diff --git a/tests/conf/help.c b/tests/conf/help.c
new file mode 100644
index 0000000..cdcd502
--- /dev/null
+++ b/tests/conf/help.c
@@ -0,0 +1,90 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static const char *help[] = {
+	"dump", "native",
+	"Dump all settings as ended up in the native database after the last merge/udpate.",
+
+	"dump", "lihata role",
+	"Dump all in-memory lihata settings of a given role. For roles, see: help roles",
+
+	"load", "role filename",
+	"Load a lihata file as role",
+
+	"load", "*",
+	"Load all roles from disk - the same way as it happens on a normal pcb-rnd startup.",
+
+	"paste", "role lhttxt",
+	"Paste in-line lihata document lhttxt as role, like if it was loaded from a file",
+
+	"policy", "pol",
+	"Change current set-policy to pol. This only affects whether a set command inserts, appends or overwrites list-type settings. For valid policies, see: help policies",
+
+	"role", "rol",
+	"Change current set-policy to rol. This only affects the destination of subsequent set commands. For valid roles, see: help roles",
+
+	"chprio", "prio",
+	"Change the priority of the first confroot of the current role's in-memory lihata document to prio and merge. Prio is an integer value.",
+
+	"chpolicy", "pol",
+	"Change the policy of the first confroot of the current role's in-memory lihata document to pol and merge. Pol is a policy, see: help policies",
+
+	"set", "path value",
+	"Call conf_set() on a given path with the given value, using the current set-role and the current set-policy. See also: help role; help policy.",
+
+	"watch", "path",
+	"Announce changes of a given path. See also: help unwatch",
+
+	"unwatch", "path",
+	"Stop announcing changes of a given path. See also: help watch",
+
+	"notify", "on",
+	"Turn on global notification on config changes.",
+
+	"notify", "off",
+	"Turn off global notification on config changes.",
+
+	"echo", "text...",
+	"Print multi-word text.",
+
+	"reset", "role",
+	"Reset (make empty) the in-memory lihata document of role; see also: help roles",
+
+	"reset", "*",
+	"Reset (make empty) all in-memory lihata documents",
+
+/* misc */
+	"roles", NULL,
+	"Valid roles: system, defaultpcb, user, env, project, design, cli",
+
+	"policies", NULL,
+	"Valid policies: prepend, append, overwrite",
+
+	NULL, NULL, NULL
+};
+
+void cmd_help(char *arg)
+{
+	const char **s;
+
+	printf("\n");
+
+	if (arg == NULL) {
+		const char *last = "";
+		printf("Try help topic. Available topics:\n");
+		for(s = help; s[2] != NULL; s+=3) {
+			if (strcmp(last, s[0]) != 0)
+				printf("  %s\n", s[0]);
+			last = s[0];
+		}
+		return;
+	}
+
+	for(s = help; s[2] != NULL; s+=3) {
+		if (strcmp(arg, s[0]) == 0) {
+			printf("%s %s\n", s[0], (s[1] == NULL ? "" : s[1]));
+			printf("%s\n\n", s[2]);
+		}
+	}
+}
diff --git a/tests/conf/tests/Makefile b/tests/conf/tests/Makefile
new file mode 100644
index 0000000..06b6b93
--- /dev/null
+++ b/tests/conf/tests/Makefile
@@ -0,0 +1,24 @@
+TESTS =  \
+	list_merge.diff \
+	list_set.diff \
+	arr_merge.diff \
+	arr_set.diff \
+	scalar.diff
+
+.SUFFIXES: .ref .test .out .diff .REF
+
+all: $(TESTS)
+	@echo "*** QC pass. ***"
+
+
+.test.out:
+	@../conftest < $*.test >$*.out
+
+.test.REF:
+	@../conftest < $*.test >$*.ref
+	@echo $*.ref generated, please validate the content
+
+.out.diff:
+	@diff -u $*.ref $*.out
+
+
diff --git a/tests/conf/tests/arr_merge.ref b/tests/conf/tests/arr_merge.ref
new file mode 100644
index 0000000..468e88a
--- /dev/null
+++ b/tests/conf/tests/arr_merge.ref
@@ -0,0 +1,31 @@
+=== initial state: empty ===
+ I design/default_layer_name[] = <empty>
+=== import 3 levels ===
+Result: 0
+ I design/default_layer_name[0] = sys1 <<prio=200 from=embedded:3>> conf_rev=1
+ I design/default_layer_name[1] = sys2 <<prio=200 from=embedded:4>> conf_rev=1
+Result: 0
+ I design/default_layer_name[0] = user1 <<prio=400 from=embedded:3>> conf_rev=2
+ I design/default_layer_name[1] = user2 <<prio=400 from=embedded:4>> conf_rev=2
+Result: 0
+ I design/default_layer_name[0] = design1 <<prio=700 from=embedded:3>> conf_rev=3
+ I design/default_layer_name[1] = design2 <<prio=700 from=embedded:4>> conf_rev=3
+=== change policy: design->prepend (design, user) ===
+ I design/default_layer_name[0] = design1 <<prio=700 from=embedded:3>> conf_rev=4
+ I design/default_layer_name[1] = design2 <<prio=700 from=embedded:4>> conf_rev=4
+ I design/default_layer_name[2] = user1 <<prio=400 from=embedded:3>> conf_rev=4
+ I design/default_layer_name[3] = user2 <<prio=400 from=embedded:4>> conf_rev=4
+=== change policy: user->append (design, sys, user) ===
+ I design/default_layer_name[0] = design1 <<prio=700 from=embedded:3>> conf_rev=5
+ I design/default_layer_name[1] = design2 <<prio=700 from=embedded:4>> conf_rev=5
+ I design/default_layer_name[2] = sys1 <<prio=200 from=embedded:3>> conf_rev=5
+ I design/default_layer_name[3] = sys2 <<prio=200 from=embedded:4>> conf_rev=5
+ I design/default_layer_name[4] = user1 <<prio=400 from=embedded:3>> conf_rev=5
+ I design/default_layer_name[5] = user2 <<prio=400 from=embedded:4>> conf_rev=5
+=== change policy: design->append (sys, user, design) ===
+ I design/default_layer_name[0] = sys1 <<prio=200 from=embedded:3>> conf_rev=6
+ I design/default_layer_name[1] = sys2 <<prio=200 from=embedded:4>> conf_rev=6
+ I design/default_layer_name[2] = user1 <<prio=400 from=embedded:3>> conf_rev=6
+ I design/default_layer_name[3] = user2 <<prio=400 from=embedded:4>> conf_rev=6
+ I design/default_layer_name[4] = design1 <<prio=700 from=embedded:3>> conf_rev=6
+ I design/default_layer_name[5] = design2 <<prio=700 from=embedded:4>> conf_rev=6
diff --git a/tests/conf/tests/arr_merge.test b/tests/conf/tests/arr_merge.test
new file mode 100644
index 0000000..d087905
--- /dev/null
+++ b/tests/conf/tests/arr_merge.test
@@ -0,0 +1,49 @@
+echo === initial state: empty ===
+reset *
+dump native design/default_layer_name
+
+echo === import 3 levels ===
+paste system \
+	li:pcb-rnd-conf-v1 { \
+		ha:overwrite { \
+			ha:design { \
+				li:default_layer_name = { sys1; sys2 } \
+			} \
+		} \
+	}
+dump native design/default_layer_name
+
+paste user \
+	li:pcb-rnd-conf-v1 { \
+		ha:overwrite { \
+			ha:design { \
+				li:default_layer_name = { user1; user2 } \
+			} \
+		} \
+	}
+dump native design/default_layer_name
+
+paste design \
+	li:pcb-rnd-conf-v1 { \
+		ha:overwrite { \
+			ha:design { \
+				li:default_layer_name = { design1; design2 } \
+			} \
+		} \
+	}
+dump native design/default_layer_name
+
+echo === change policy: design->prepend (design, user) ===
+role design
+chpolicy prepend
+dump native design/default_layer_name
+
+echo === change policy: user->append (design, sys, user) ===
+role user
+chpolicy append
+dump native design/default_layer_name
+
+echo === change policy: design->append (sys, user, design) ===
+role design
+chpolicy append
+dump native design/default_layer_name
diff --git a/tests/conf/tests/arr_set.ref b/tests/conf/tests/arr_set.ref
new file mode 100644
index 0000000..16f8225
--- /dev/null
+++ b/tests/conf/tests/arr_set.ref
@@ -0,0 +1,34 @@
+=== initial state: empty ===
+ I design/default_layer_name[] = <empty>
+=== append two items ===
+ I design/default_layer_name[0] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=1
+ I design/default_layer_name[0] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=2
+ I design/default_layer_name[1] = bar <<prio=700 from=<cmd_reset *>:0>> conf_rev=2
+=== prepend two items ===
+ I design/default_layer_name[0] = pre1 <<prio=700 from=<cmd_reset *>:0>> conf_rev=3
+ I design/default_layer_name[1] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=3
+ I design/default_layer_name[2] = bar <<prio=700 from=<cmd_reset *>:0>> conf_rev=3
+ I design/default_layer_name[0] = pre2 <<prio=700 from=<cmd_reset *>:0>> conf_rev=4
+ I design/default_layer_name[1] = pre1 <<prio=700 from=<cmd_reset *>:0>> conf_rev=4
+ I design/default_layer_name[2] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=4
+ I design/default_layer_name[3] = bar <<prio=700 from=<cmd_reset *>:0>> conf_rev=4
+=== overwrite item ===
+ I design/default_layer_name[0] = pre2 <<prio=700 from=<cmd_reset *>:0>> conf_rev=5
+ I design/default_layer_name[1] = NEW1 <<prio=700 from=<cmd_reset *>:0>> conf_rev=5
+ I design/default_layer_name[2] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=5
+ I design/default_layer_name[3] = bar <<prio=700 from=<cmd_reset *>:0>> conf_rev=5
+ I design/default_layer_name[0] = pre2 <<prio=700 from=<cmd_reset *>:0>> conf_rev=6
+ I design/default_layer_name[1] = NEW1 <<prio=700 from=<cmd_reset *>:0>> conf_rev=6
+ I design/default_layer_name[2] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=6
+ I design/default_layer_name[3] = bar <<prio=700 from=<cmd_reset *>:0>> conf_rev=6
+ I design/default_layer_name[4] = NEW2 <<prio=700 from=<cmd_reset *>:0>> conf_rev=6
+ I design/default_layer_name[0] = pre2 <<prio=700 from=<cmd_reset *>:0>> conf_rev=7
+ I design/default_layer_name[1] = NEW1 <<prio=700 from=<cmd_reset *>:0>> conf_rev=7
+ I design/default_layer_name[2] = foo <<prio=700 from=<cmd_reset *>:0>> conf_rev=7
+ I design/default_layer_name[3] = bar <<prio=700 from=<cmd_reset *>:0>> conf_rev=7
+ I design/default_layer_name[4] = NEW2 <<prio=700 from=<cmd_reset *>:0>> conf_rev=7
+ I design/default_layer_name[5] = <NULL> <<prio=700 from=(null):0>> conf_rev=7
+ I design/default_layer_name[6] = <NULL> <<prio=700 from=(null):0>> conf_rev=7
+ I design/default_layer_name[7] = NEW3 <<prio=700 from=<cmd_reset *>:0>> conf_rev=7
+=== overwrite array ===
+ I design/default_layer_name[0] = new <<prio=700 from=<cmd_reset *>:0>> conf_rev=8
diff --git a/tests/conf/tests/arr_set.test b/tests/conf/tests/arr_set.test
new file mode 100644
index 0000000..4b34723
--- /dev/null
+++ b/tests/conf/tests/arr_set.test
@@ -0,0 +1,42 @@
+role design
+
+echo === initial state: empty ===
+reset *
+dump native design/default_layer_name
+
+echo === append two items ===
+policy append
+
+set design/default_layer_name foo
+dump native design/default_layer_name
+
+set design/default_layer_name bar
+dump native design/default_layer_name
+
+echo === prepend two items ===
+policy prepend
+
+set design/default_layer_name pre1
+dump native design/default_layer_name
+
+set design/default_layer_name pre2
+dump native design/default_layer_name
+
+echo === overwrite item ===
+policy overwrite
+
+set design/default_layer_name[1] NEW1
+dump native design/default_layer_name
+
+set design/default_layer_name[4] NEW2
+dump native design/default_layer_name
+
+set design/default_layer_name[7] NEW3
+dump native design/default_layer_name
+
+
+echo === overwrite array ===
+policy overwrite
+
+set design/default_layer_name new
+dump native design/default_layer_name
diff --git a/tests/conf/tests/list_merge.ref b/tests/conf/tests/list_merge.ref
new file mode 100644
index 0000000..41c5999
--- /dev/null
+++ b/tests/conf/tests/list_merge.ref
@@ -0,0 +1,13 @@
+=== initial state: empty ===
+ I rc/library_search_paths = <empty list> <<prio=0>> conf_rev=0
+=== import 3 levels ===
+Result: 0
+ I rc/library_search_paths = {sys1 <<prio=200 from=embedded:3>>;sys2 <<prio=200 from=embedded:4>>;} <<prio=0>> conf_rev=1
+Result: 0
+ I rc/library_search_paths = {user1 <<prio=400 from=embedded:3>>;user2 <<prio=400 from=embedded:4>>;} <<prio=0>> conf_rev=2
+Result: 0
+ I rc/library_search_paths = {design1 <<prio=700 from=embedded:3>>;design2 <<prio=700 from=embedded:4>>;} <<prio=0>> conf_rev=3
+=== change policies ===
+ I rc/library_search_paths = {design2 <<prio=700 from=embedded:4>>;design1 <<prio=700 from=embedded:3>>;user1 <<prio=400 from=embedded:3>>;user2 <<prio=400 from=embedded:4>>;} <<prio=0>> conf_rev=4
+ I rc/library_search_paths = {design2 <<prio=700 from=embedded:4>>;design1 <<prio=700 from=embedded:3>>;sys1 <<prio=200 from=embedded:3>>;sys2 <<prio=200 from=embedded:4>>;user1 <<prio=400 from=embedded:3>>;user2 <<prio=400 from=embedded:4>>;} <<prio=0>> conf_rev=5
+ I rc/library_search_paths = {sys1 <<prio=200 from=embedded:3>>;sys2 <<prio=200 from=embedded:4>>;user1 <<prio=400 from=embedded:3>>;user2 <<prio=400 from=embedded:4>>;design1 <<prio=700 from=embedded:3>>;design2 <<prio=700 from=embedded:4>>;} <<prio=0>> conf_rev=6
diff --git a/tests/conf/tests/list_merge.test b/tests/conf/tests/list_merge.test
new file mode 100644
index 0000000..ff634a4
--- /dev/null
+++ b/tests/conf/tests/list_merge.test
@@ -0,0 +1,47 @@
+echo === initial state: empty ===
+reset *
+dump native rc/library_search_paths
+
+echo === import 3 levels ===
+paste system \
+	li:pcb-rnd-conf-v1 { \
+		ha:overwrite { \
+			ha:rc { \
+				li:library_search_paths = { sys1; sys2 } \
+			} \
+		} \
+	}
+dump native rc/library_search_paths
+
+paste user \
+	li:pcb-rnd-conf-v1 { \
+		ha:overwrite { \
+			ha:rc { \
+				li:library_search_paths = { user1; user2 } \
+			} \
+		} \
+	}
+dump native rc/library_search_paths
+
+paste design \
+	li:pcb-rnd-conf-v1 { \
+		ha:overwrite { \
+			ha:rc { \
+				li:library_search_paths = { design1; design2 } \
+			} \
+		} \
+	}
+dump native rc/library_search_paths
+
+echo === change policies ===
+role design
+chpolicy prepend
+dump native rc/library_search_paths
+
+role user
+chpolicy append
+dump native rc/library_search_paths
+
+role design
+chpolicy append
+dump native rc/library_search_paths
diff --git a/tests/conf/tests/list_set.ref b/tests/conf/tests/list_set.ref
new file mode 100644
index 0000000..137ea32
--- /dev/null
+++ b/tests/conf/tests/list_set.ref
@@ -0,0 +1,10 @@
+=== initial state: empty ===
+ I rc/library_search_paths = <empty list> <<prio=0>> conf_rev=0
+=== append two items ===
+ I rc/library_search_paths = {foo <<prio=700 from=<cmd_reset *>:0>>;} <<prio=0>> conf_rev=1
+ I rc/library_search_paths = {foo <<prio=700 from=<cmd_reset *>:0>>;bar <<prio=700 from=<cmd_reset *>:0>>;} <<prio=0>> conf_rev=2
+=== prepend two items ===
+ I rc/library_search_paths = {pre1 <<prio=700 from=<cmd_reset *>:0>>;foo <<prio=700 from=<cmd_reset *>:0>>;bar <<prio=700 from=<cmd_reset *>:0>>;} <<prio=0>> conf_rev=3
+ I rc/library_search_paths = {pre2 <<prio=700 from=<cmd_reset *>:0>>;pre1 <<prio=700 from=<cmd_reset *>:0>>;foo <<prio=700 from=<cmd_reset *>:0>>;bar <<prio=700 from=<cmd_reset *>:0>>;} <<prio=0>> conf_rev=4
+=== overwrite ===
+ I rc/library_search_paths = {new <<prio=700 from=<cmd_reset *>:0>>;} <<prio=0>> conf_rev=5
diff --git a/tests/conf/tests/list_set.test b/tests/conf/tests/list_set.test
new file mode 100644
index 0000000..a02c6c7
--- /dev/null
+++ b/tests/conf/tests/list_set.test
@@ -0,0 +1,29 @@
+role design
+
+echo === initial state: empty ===
+reset *
+dump native rc/library_search_paths
+
+echo === append two items ===
+policy append
+
+set rc/library_search_paths foo
+dump native rc/library_search_paths
+
+set rc/library_search_paths bar
+dump native rc/library_search_paths
+
+echo === prepend two items ===
+policy prepend
+
+set rc/library_search_paths pre1
+dump native rc/library_search_paths
+
+set rc/library_search_paths pre2
+dump native rc/library_search_paths
+
+echo === overwrite ===
+policy overwrite
+
+set rc/library_search_paths new
+dump native rc/library_search_paths
diff --git a/tests/conf/tests/scalar.ref b/tests/conf/tests/scalar.ref
new file mode 100644
index 0000000..c3dd4a4
--- /dev/null
+++ b/tests/conf/tests/scalar.ref
@@ -0,0 +1,11 @@
+=== initial state ===
+ I rc/library_shell = <NULL> <<prio=0>> conf_rev=0
+=== setting new values on higher prio should change the final render ===
+ I rc/library_shell = from_user <<prio=400 from=<main>:0>> conf_rev=0
+ I rc/library_shell = from_design <<prio=700 from=<null-design>:0>> conf_rev=1
+ I rc/library_shell = from_cli <<prio=800 from=<commandline>:0>> conf_rev=2
+=== setting new values on lower prio should NOT change the final render ===
+ I rc/library_shell = from_cli <<prio=800 from=<commandline>:0>> conf_rev=3
+ I rc/library_shell = from_cli <<prio=800 from=<commandline>:0>> conf_rev=4
+=== change prio to reorder ===
+ I rc/library_shell = from_design2 <<prio=700 from=<null-design>:0>> conf_rev=5
diff --git a/tests/conf/tests/scalar.test b/tests/conf/tests/scalar.test
new file mode 100644
index 0000000..2ecaa4a
--- /dev/null
+++ b/tests/conf/tests/scalar.test
@@ -0,0 +1,29 @@
+echo === initial state ===
+dump native rc/library_shell
+
+echo === setting new values on higher prio should change the final render ===
+role user
+set rc/library_shell from_user
+dump native rc/library_shell
+
+role design
+set rc/library_shell from_design
+dump native rc/library_shell
+
+role cli
+set rc/library_shell from_cli
+dump native rc/library_shell
+
+echo === setting new values on lower prio should NOT change the final render ===
+role user
+set rc/library_shell from_user2
+dump native rc/library_shell
+
+role design
+set rc/library_shell from_design2
+dump native rc/library_shell
+
+echo === change prio to reorder ===
+role cli
+chprio 1
+dump native rc/library_shell
diff --git a/tests/gsch2pcb-rnd/Makefile.common b/tests/gsch2pcb-rnd/Makefile.common
new file mode 100644
index 0000000..33cc9c5
--- /dev/null
+++ b/tests/gsch2pcb-rnd/Makefile.common
@@ -0,0 +1,9 @@
+CONF = \
+ -c rc/library_search_paths+=$(TRUNK)/pcblib \
+ -c rc/default_pcb_file=$(TRUNK)/src/default.pcb
+
+GSCH2PCB = $(TRUNK)/util/gsch2pcb-rnd/gsch2pcb-rnd
+
+DIFF = diff -u
+
+FORCE:
diff --git a/tests/gsch2pcb-rnd/simple/Makefile b/tests/gsch2pcb-rnd/simple/Makefile
new file mode 100644
index 0000000..d0283b3
--- /dev/null
+++ b/tests/gsch2pcb-rnd/simple/Makefile
@@ -0,0 +1,9 @@
+TRUNK=../../..
+all:
+
+include ../Makefile.common
+
+test: FORCE
+	@rm main.pcb main.pcb.new main.cmd main.net main.out 2>/dev/null; true
+	@$(GSCH2PCB) $(CONF) main.sch > main.out
+	@$(DIFF) main.pcb.ref main.pcb
diff --git a/tests/gsch2pcb-rnd/simple/main.cmd.ref b/tests/gsch2pcb-rnd/simple/main.cmd.ref
new file mode 100644
index 0000000..a1f8a6c
--- /dev/null
+++ b/tests/gsch2pcb-rnd/simple/main.cmd.ref
@@ -0,0 +1,26 @@
+# Pin name action command file
+
+# Start of element C3
+ChangePinName(C3, 2, 2)
+ChangePinName(C3, 1, 1)
+
+# Start of element CONN2
+ChangePinName(CONN2, 2, 2)
+ChangePinName(CONN2, 1, 1)
+
+# Start of element CONN1
+ChangePinName(CONN1, 2, 2)
+ChangePinName(CONN1, 1, 1)
+
+# Start of element C1
+ChangePinName(C1, 2, -)
+ChangePinName(C1, 1, +)
+
+# Start of element C2
+ChangePinName(C2, 2, 2)
+ChangePinName(C2, 1, 1)
+
+# Start of element U1
+ChangePinName(U1, 3, 3)
+ChangePinName(U1, 2, 2)
+ChangePinName(U1, 1, 1)
diff --git a/tests/gsch2pcb-rnd/simple/main.net.ref b/tests/gsch2pcb-rnd/simple/main.net.ref
new file mode 100644
index 0000000..19c3915
--- /dev/null
+++ b/tests/gsch2pcb-rnd/simple/main.net.ref
@@ -0,0 +1,3 @@
+unnamed_net2	C3-2 CONN2-1 U1-3 
+GND	C3-1 CONN2-2 CONN1-2 C1-2 C2-1 U1-2 
+unnamed_net1	C2-2 C1-1 CONN1-1 U1-1 
diff --git a/tests/gsch2pcb-rnd/simple/main.out.ref b/tests/gsch2pcb-rnd/simple/main.out.ref
new file mode 100644
index 0000000..31609bc
--- /dev/null
+++ b/tests/gsch2pcb-rnd/simple/main.out.ref
@@ -0,0 +1,20 @@
+  ../../../src/default.pcb -> 0
+
+----------------------------------
+Done processing.  Work performed:
+6 file elements added to main.pcb.
+
+Next step:
+1.  Run pcb on your file main.pcb.
+    You will find all your footprints in a bundle ready for you to place
+    or disperse with "Select -> Disperse all elements" in PCB.
+
+2.  From within PCB, select "File -> Load netlist file" and select 
+    main.net to load the netlist.
+
+3.  From within PCB, enter
+
+           :ExecuteFile(main.cmd)
+
+    to propagate the pin names of all footprints to the layout.
+
diff --git a/tests/gsch2pcb-rnd/simple/main.pcb.ref b/tests/gsch2pcb-rnd/simple/main.pcb.ref
new file mode 100644
index 0000000..456853b
--- /dev/null
+++ b/tests/gsch2pcb-rnd/simple/main.pcb.ref
@@ -0,0 +1,136 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 600000 500000]
+
+Grid[2500.0 0 0 1]
+Cursor[2500 62500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Attribute("PCB::grid::unit" "mil")
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
+
+Element[0x00000000 "1206" "C3" "unknown" 0 0 -3150 -3150 0 100 ""]
+(
+	Pad[-5905 -1181
+		-5905 1181
+		5118 2000 5718 "1" "1" "square"]
+	    Pad[5905 -1181
+		5905 1181
+		5118 2000 5718 "2" "2" "square"]
+	ElementLine[-2362 -3740 2362 -3740 800]
+	    ElementLine[-2362 3740 2362 3740 800]
+)
+Element["" "connector(1,2)" "CONN2" "unknown" 0 0 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine[-5000 -5000 -5000 15000 1000]
+	ElementLine[-5000 -5000 5000 -5000 1000]
+	ElementLine[5000 15000 -5000 15000 1000]
+	ElementLine[5000 15000 5000 -5000 1000]
+	ElementLine[-5000 5000 5000 5000 1000]
+	ElementLine[5000 -5000 5000 5000 1000]
+)
+Element["" "connector(1,2)" "CONN1" "unknown" 0 0 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine[-5000 -5000 -5000 15000 1000]
+	ElementLine[-5000 -5000 5000 -5000 1000]
+	ElementLine[5000 15000 -5000 15000 1000]
+	ElementLine[5000 15000 5000 -5000 1000]
+	ElementLine[-5000 5000 5000 5000 1000]
+	ElementLine[5000 -5000 5000 5000 1000]
+)
+
+Element(0x00 "TANT_A" "C1" "unknown" 0 0 106 0 3 100 0x00)
+(
+	ElementLine(-96 -43 -96 43 20)
+	    ElementLine(-96 43 -72 55 10)
+	    ElementLine(-72 55 86 55 10)
+	    ElementLine(86 55 86 -55 10)
+	    ElementLine(86 -55 -72 -55 10)
+	    ElementLine(-72 -55 -96 -43 10)
+	Pad(-50 -18 
+		-50 18
+		49 20 55 "1" "1" 0x00000100)
+	    Pad(50 -18 
+		50 18
+		49 20 55 "2" "2" 0x00000100)
+)
+
+Element[0x00000000 "1206" "C2" "unknown" 0 0 -3150 -3150 0 100 ""]
+(
+	Pad[-5905 -1181
+		-5905 1181
+		5118 2000 5718 "1" "1" "square"]
+	    Pad[5905 -1181
+		5905 1181
+		5118 2000 5718 "2" "2" "square"]
+	ElementLine[-2362 -3740 2362 -3740 800]
+	    ElementLine[-2362 3740 2362 3740 800]
+)
+Element(0x00 "TO220" "U1" "unknown" 0 0 1 100 0x00)
+(
+	Pin(100 800 90 60 "1" 0x101)
+	Pin(200 800 90 60 "2" 0x01)
+	Pin(300 800 90 60 "3" 0x01)
+	Pin(200 130 150 130 "4" 0x01)
+	ElementLine(100 800 100 620 30)
+	ElementLine(200 800 200 620 30)
+	ElementLine(300 800 300 620 30)
+	ElementLine(  0 620 400 620 20)
+	ElementLine(400 620 400 245 20)
+	ElementLine(400 245   0 245 20)
+	ElementLine(  0 245   0 620 20)
+	ElementLine(  0 245 400 245 20)
+	ElementLine(400 245 400 120 20)
+	ElementLine(400 120 385 120 20)
+	ElementLine(385 120 385  50 20)
+	ElementLine(385  50 400  50 20)
+	ElementLine(400  50 400  10 20)
+	ElementLine(400  10   0  10 20)
+	ElementLine(  0  10   0  50 20)
+	ElementLine(  0  50  15  50 20)
+	ElementLine( 15  50  15 120 20)
+	ElementLine( 15 120   0 120 20)
+	ElementLine(  0 120   0 245 20)
+	Mark(200 800)
+)
diff --git a/tests/gsch2pcb-rnd/simple/main.sch b/tests/gsch2pcb-rnd/simple/main.sch
new file mode 100644
index 0000000..60197b1
--- /dev/null
+++ b/tests/gsch2pcb-rnd/simple/main.sch
@@ -0,0 +1,80 @@
+v 20130925 2
+C 27800 50500 1 0 0 lm7805-1.sym
+{
+T 29400 51800 5 10 0 0 0 0 1
+device=7805
+T 29200 51500 5 10 1 1 0 6 1
+refdes=U1
+T 28100 51500 5 10 1 1 0 0 1
+footprint=TO220
+}
+C 27700 50000 1 90 0 capacitor-1.sym
+{
+T 27000 50200 5 10 0 0 90 0 1
+device=CAPACITOR
+T 27800 50600 5 10 1 1 90 0 1
+refdes=C2
+T 26800 50200 5 10 0 0 90 0 1
+symversion=0.1
+T 27800 49900 5 10 1 1 90 0 1
+footprint=1206
+}
+C 26500 50900 1 270 0 capacitor-2.sym
+{
+T 27200 50700 5 10 0 0 270 0 1
+device=POLARIZED_CAPACITOR
+T 26400 49700 5 10 1 1 90 0 1
+refdes=C1
+T 27400 50700 5 10 0 0 270 0 1
+symversion=0.1
+T 26400 50100 5 10 1 1 90 0 1
+footprint=TANT_A
+}
+C 25700 50300 1 0 1 connector2-2.sym
+{
+T 25000 51600 5 10 1 1 0 0 1
+refdes=CONN1
+T 25400 51550 5 10 0 0 0 6 1
+device=CONNECTOR_2
+T 25400 51750 5 10 0 0 0 6 1
+footprint=connector(1,2)
+}
+N 25700 51100 27800 51100 4
+C 31700 50300 1 0 0 connector2-2.sym
+{
+T 32400 51600 5 10 1 1 0 6 1
+refdes=CONN2
+T 32000 51550 5 10 0 0 0 0 1
+device=CONNECTOR_2
+T 32000 51750 5 10 0 0 0 0 1
+footprint=connector(1,2)
+}
+N 31700 51100 29400 51100 4
+C 30100 50000 1 90 0 capacitor-1.sym
+{
+T 29400 50200 5 10 0 0 90 0 1
+device=CAPACITOR
+T 30200 50600 5 10 1 1 90 0 1
+refdes=C3
+T 29200 50200 5 10 0 0 90 0 1
+symversion=0.1
+T 30200 49900 5 10 1 1 90 0 1
+footprint=1206
+}
+N 25700 50700 26000 50700 4
+N 26000 50700 26000 49300 4
+N 31700 50700 31500 50700 4
+N 31500 50700 31500 49300 4
+N 26700 50900 26700 51100 4
+N 27500 50900 27500 51100 4
+N 29900 50900 29900 51100 4
+N 28600 50500 28600 49300 4
+C 28500 49000 1 0 0 gnd-1.sym
+C 29800 49000 1 0 0 gnd-1.sym
+C 31400 49000 1 0 0 gnd-1.sym
+C 27400 49000 1 0 0 gnd-1.sym
+C 26600 49000 1 0 0 gnd-1.sym
+C 25900 49000 1 0 0 gnd-1.sym
+N 26700 49300 26700 50000 4
+N 27500 49300 27500 50000 4
+N 29900 49300 29900 50000 4
diff --git a/tests/orig/README.txt b/tests/orig/README.txt
new file mode 100644
index 0000000..a6d8064
--- /dev/null
+++ b/tests/orig/README.txt
@@ -0,0 +1,106 @@
+Please read this file before making any modifications to the test suite.
+
+**********************************************************************
+**********************************************************************
+* Overview
+**********************************************************************
+**********************************************************************
+
+The test suite is based on a shell script, 'run_tests.sh', which
+calls pcb to export various test case layouts to different output
+formats.  The tests to be run are defined in the file 'tests.list'.
+The 'tests.list' file defines the export command line options passed
+to pcb, the name of the input .pcb file, and the names and file types
+for the expected output files.
+
+After a particular test is run, the output files are compared against
+a set of "golden" files which are in the golden/ subdirectory.
+ALL CHANGES TO THE GOLDEN FILES MUST BE HAND VERIFIED.  This point
+can not be emphasized too much.  
+
+While this testsuite is clearly not comprehensive (the GUI is totally
+left out of the testing for example), it is still better than nothing
+and can help detect newly introduced bugs.
+
+**********************************************************************
+**********************************************************************
+* Running an existing test
+**********************************************************************
+**********************************************************************
+
+To run all of the tests defined in tests.list run:
+
+  ./run_tests.sh
+
+To only run a specific test or tests, then simply list them by name
+on the command line like:
+
+  ./run_tests.sh test_one test_two ... 
+
+**********************************************************************
+**********************************************************************
+* Updating existing "golden" files
+**********************************************************************
+**********************************************************************
+
+./run_tests.sh --regen <testname>
+
+will regenerate the golden file for <testname>.  If you are generating
+ASCII output such as BOMs or RS-274X files, then use the diff(3) program
+to examine all differences.  If you are generating a graphics file
+such as a PNG, then I suggest saving off a copy and using ImageMagick
+to look for the differences visually.  The run_tests.sh script has
+examples of comparing .png files.  Make sure the changes are only
+the expected ones and then check the new files back into svn.  Do
+not blindly update these files as that defeats the purpose of the tests.
+
+**********************************************************************
+**********************************************************************
+* Adding New Tests
+**********************************************************************
+**********************************************************************
+
+----------------------------------------------------------------------
+Create input files
+----------------------------------------------------------------------
+
+Create a *small* layout input file and put it in the inputs/
+directory.  The goal is to have a file which tests one particular aspect
+of the capabilities of pcb.
+
+----------------------------------------------------------------------
+Add to tests.list
+----------------------------------------------------------------------
+
+Add an entry to the tests.list file for your new tests.  Use existing
+entries as an example.
+
+----------------------------------------------------------------------
+Generate the reference files
+----------------------------------------------------------------------
+
+Generate the reference files for your new tests using the following
+
+./run_tests.sh --regen <new_test_name> 
+
+where <new_test_name> is the name of your new test from tests.list.  If you
+are adding multiple tests, then you can list them all on the same
+command line.
+
+*IMPORTANT*
+Verify that the generated .png files for your new tests are correct.  These
+files will have been placed in the golden/ subdirectory.
+
+----------------------------------------------------------------------
+Update Makefile.am's
+----------------------------------------------------------------------
+
+Update inputs/Makefile.am and golden/Makefile.am to include your new
+files.  If you added new Makefile.am's then be sure to also update
+configure.ac at the top level of the source tree.
+
+----------------------------------------------------------------------
+Add the new files to svn
+----------------------------------------------------------------------
+
+
diff --git a/tests/orig/golden/hid_bom1/bom_general.bom b/tests/orig/golden/hid_bom1/bom_general.bom
new file mode 100644
index 0000000..e069ea1
--- /dev/null
+++ b/tests/orig/golden/hid_bom1/bom_general.bom
@@ -0,0 +1,10 @@
+# $Id$
+# PcbBOM Version 1.0
+# Date: Wed Jun 24 21:42:21 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB BOM
+# Quantity, Description, Value, RefDes
+# --------------------------------------------
+8,"Standard SMT resistor, capacitor etc","RESC3216N",R90_TOP R180_TOP R270_TOP R0_TOP R270_BOT R180_BOT R90_BOT R0_BOT 
+8,"Dual in-line package, narrow (300 mil)","DIP8",UDIP90_TOP UDIP180_TOP UDIP270_TOP UDIP0_TOP UDIP270_BOT UDIP180_BOT UDIP90_BOT UDIP0_BOT 
+8,"Small outline package, narrow (150mil)","SO8",USO90_TOP USO180_TOP USO270_TOP USO0_TOP USO270_BOT USO180_BOT USO90_BOT USO0_BOT 
diff --git a/tests/orig/golden/hid_bom1/bom_general.xy b/tests/orig/golden/hid_bom1/bom_general.xy
new file mode 100644
index 0000000..dfcd7de
--- /dev/null
+++ b/tests/orig/golden/hid_bom1/bom_general.xy
@@ -0,0 +1,32 @@
+# $Id$
+# PcbXY Version 1.0
+# Date: Wed Jun 24 21:42:21 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB X-Y
+# RefDes, Description, Value, X, Y, rotation, top/bottom
+# X,Y in mils.  rotation in degrees.
+# --------------------------------------------
+R90_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1700.00,4300.00,90,top
+R180_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1300.00,4300.00,180,top
+R270_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1000.00,4300.00,270,top
+R0_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",600.00,4300.00,0,top
+UDIP90_TOP,"Dual in-line package, narrow (300 mil)","DIP8",2650.00,1450.00,180,top
+UDIP180_TOP,"Dual in-line package, narrow (300 mil)","DIP8",1950.00,1450.00,270,top
+UDIP270_TOP,"Dual in-line package, narrow (300 mil)","DIP8",1250.00,1450.00,0,top
+UDIP0_TOP,"Dual in-line package, narrow (300 mil)","DIP8",550.00,1450.00,90,top
+USO90_TOP,"Small outline package, narrow (150mil)","SO8",1700.00,3200.00,180,top
+USO180_TOP,"Small outline package, narrow (150mil)","SO8",1300.00,3200.00,270,top
+USO270_TOP,"Small outline package, narrow (150mil)","SO8",1000.00,3200.00,0,top
+USO0_TOP,"Small outline package, narrow (150mil)","SO8",600.00,3200.00,90,top
+UDIP270_BOT,"Dual in-line package, narrow (300 mil)","DIP8",2650.00,650.00,270,bottom
+UDIP180_BOT,"Dual in-line package, narrow (300 mil)","DIP8",1950.00,650.00,180,bottom
+UDIP90_BOT,"Dual in-line package, narrow (300 mil)","DIP8",1250.00,650.00,90,bottom
+UDIP0_BOT,"Dual in-line package, narrow (300 mil)","DIP8",550.00,650.00,0,bottom
+USO270_BOT,"Small outline package, narrow (150mil)","SO8",1700.00,2600.00,270,bottom
+USO180_BOT,"Small outline package, narrow (150mil)","SO8",1300.00,2600.00,180,bottom
+USO90_BOT,"Small outline package, narrow (150mil)","SO8",1000.00,2600.00,90,bottom
+USO0_BOT,"Small outline package, narrow (150mil)","SO8",600.00,2600.00,0,bottom
+R270_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1700.00,3900.00,270,bottom
+R180_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1300.00,3900.00,180,bottom
+R90_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1000.00,3900.00,90,bottom
+R0_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",600.00,3900.00,0,bottom
diff --git a/tests/orig/golden/hid_bom2/bom_general.xy b/tests/orig/golden/hid_bom2/bom_general.xy
new file mode 100644
index 0000000..c2c9ea2
--- /dev/null
+++ b/tests/orig/golden/hid_bom2/bom_general.xy
@@ -0,0 +1,32 @@
+# $Id$
+# PcbXY Version 1.0
+# Date: Wed Jun 24 21:42:22 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB X-Y
+# RefDes, Description, Value, X, Y, rotation, top/bottom
+# X,Y in mils.  rotation in degrees.
+# --------------------------------------------
+R90_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1700.00,4300.00,90,top
+R180_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1300.00,4300.00,180,top
+R270_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1000.00,4300.00,270,top
+R0_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",600.00,4300.00,0,top
+UDIP90_TOP,"Dual in-line package, narrow (300 mil)","DIP8",2650.00,1450.00,180,top
+UDIP180_TOP,"Dual in-line package, narrow (300 mil)","DIP8",1950.00,1450.00,270,top
+UDIP270_TOP,"Dual in-line package, narrow (300 mil)","DIP8",1250.00,1450.00,0,top
+UDIP0_TOP,"Dual in-line package, narrow (300 mil)","DIP8",550.00,1450.00,90,top
+USO90_TOP,"Small outline package, narrow (150mil)","SO8",1700.00,3200.00,180,top
+USO180_TOP,"Small outline package, narrow (150mil)","SO8",1300.00,3200.00,270,top
+USO270_TOP,"Small outline package, narrow (150mil)","SO8",1000.00,3200.00,0,top
+USO0_TOP,"Small outline package, narrow (150mil)","SO8",600.00,3200.00,90,top
+UDIP270_BOT,"Dual in-line package, narrow (300 mil)","DIP8",2650.00,650.00,270,bottom
+UDIP180_BOT,"Dual in-line package, narrow (300 mil)","DIP8",1950.00,650.00,180,bottom
+UDIP90_BOT,"Dual in-line package, narrow (300 mil)","DIP8",1250.00,650.00,90,bottom
+UDIP0_BOT,"Dual in-line package, narrow (300 mil)","DIP8",550.00,650.00,0,bottom
+USO270_BOT,"Small outline package, narrow (150mil)","SO8",1700.00,2600.00,270,bottom
+USO180_BOT,"Small outline package, narrow (150mil)","SO8",1300.00,2600.00,180,bottom
+USO90_BOT,"Small outline package, narrow (150mil)","SO8",1000.00,2600.00,90,bottom
+USO0_BOT,"Small outline package, narrow (150mil)","SO8",600.00,2600.00,0,bottom
+R270_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1700.00,3900.00,270,bottom
+R180_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1300.00,3900.00,180,bottom
+R90_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1000.00,3900.00,90,bottom
+R0_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",600.00,3900.00,0,bottom
diff --git a/tests/orig/golden/hid_bom2/test.bom b/tests/orig/golden/hid_bom2/test.bom
new file mode 100644
index 0000000..c2677f5
--- /dev/null
+++ b/tests/orig/golden/hid_bom2/test.bom
@@ -0,0 +1,10 @@
+# $Id$
+# PcbBOM Version 1.0
+# Date: Wed Jun 24 21:42:22 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB BOM
+# Quantity, Description, Value, RefDes
+# --------------------------------------------
+8,"Standard SMT resistor, capacitor etc","RESC3216N",R90_TOP R180_TOP R270_TOP R0_TOP R270_BOT R180_BOT R90_BOT R0_BOT 
+8,"Dual in-line package, narrow (300 mil)","DIP8",UDIP90_TOP UDIP180_TOP UDIP270_TOP UDIP0_TOP UDIP270_BOT UDIP180_BOT UDIP90_BOT UDIP0_BOT 
+8,"Small outline package, narrow (150mil)","SO8",USO90_TOP USO180_TOP USO270_TOP USO0_TOP USO270_BOT USO180_BOT USO90_BOT USO0_BOT 
diff --git a/tests/orig/golden/hid_bom3/bom_general.bom b/tests/orig/golden/hid_bom3/bom_general.bom
new file mode 100644
index 0000000..c2677f5
--- /dev/null
+++ b/tests/orig/golden/hid_bom3/bom_general.bom
@@ -0,0 +1,10 @@
+# $Id$
+# PcbBOM Version 1.0
+# Date: Wed Jun 24 21:42:22 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB BOM
+# Quantity, Description, Value, RefDes
+# --------------------------------------------
+8,"Standard SMT resistor, capacitor etc","RESC3216N",R90_TOP R180_TOP R270_TOP R0_TOP R270_BOT R180_BOT R90_BOT R0_BOT 
+8,"Dual in-line package, narrow (300 mil)","DIP8",UDIP90_TOP UDIP180_TOP UDIP270_TOP UDIP0_TOP UDIP270_BOT UDIP180_BOT UDIP90_BOT UDIP0_BOT 
+8,"Small outline package, narrow (150mil)","SO8",USO90_TOP USO180_TOP USO270_TOP USO0_TOP USO270_BOT USO180_BOT USO90_BOT USO0_BOT 
diff --git a/tests/orig/golden/hid_bom3/test.xy b/tests/orig/golden/hid_bom3/test.xy
new file mode 100644
index 0000000..c2c9ea2
--- /dev/null
+++ b/tests/orig/golden/hid_bom3/test.xy
@@ -0,0 +1,32 @@
+# $Id$
+# PcbXY Version 1.0
+# Date: Wed Jun 24 21:42:22 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB X-Y
+# RefDes, Description, Value, X, Y, rotation, top/bottom
+# X,Y in mils.  rotation in degrees.
+# --------------------------------------------
+R90_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1700.00,4300.00,90,top
+R180_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1300.00,4300.00,180,top
+R270_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",1000.00,4300.00,270,top
+R0_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",600.00,4300.00,0,top
+UDIP90_TOP,"Dual in-line package, narrow (300 mil)","DIP8",2650.00,1450.00,180,top
+UDIP180_TOP,"Dual in-line package, narrow (300 mil)","DIP8",1950.00,1450.00,270,top
+UDIP270_TOP,"Dual in-line package, narrow (300 mil)","DIP8",1250.00,1450.00,0,top
+UDIP0_TOP,"Dual in-line package, narrow (300 mil)","DIP8",550.00,1450.00,90,top
+USO90_TOP,"Small outline package, narrow (150mil)","SO8",1700.00,3200.00,180,top
+USO180_TOP,"Small outline package, narrow (150mil)","SO8",1300.00,3200.00,270,top
+USO270_TOP,"Small outline package, narrow (150mil)","SO8",1000.00,3200.00,0,top
+USO0_TOP,"Small outline package, narrow (150mil)","SO8",600.00,3200.00,90,top
+UDIP270_BOT,"Dual in-line package, narrow (300 mil)","DIP8",2650.00,650.00,270,bottom
+UDIP180_BOT,"Dual in-line package, narrow (300 mil)","DIP8",1950.00,650.00,180,bottom
+UDIP90_BOT,"Dual in-line package, narrow (300 mil)","DIP8",1250.00,650.00,90,bottom
+UDIP0_BOT,"Dual in-line package, narrow (300 mil)","DIP8",550.00,650.00,0,bottom
+USO270_BOT,"Small outline package, narrow (150mil)","SO8",1700.00,2600.00,270,bottom
+USO180_BOT,"Small outline package, narrow (150mil)","SO8",1300.00,2600.00,180,bottom
+USO90_BOT,"Small outline package, narrow (150mil)","SO8",1000.00,2600.00,90,bottom
+USO0_BOT,"Small outline package, narrow (150mil)","SO8",600.00,2600.00,0,bottom
+R270_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1700.00,3900.00,270,bottom
+R180_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1300.00,3900.00,180,bottom
+R90_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",1000.00,3900.00,90,bottom
+R0_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",600.00,3900.00,0,bottom
diff --git a/tests/orig/golden/hid_bom4/bom_general.bom b/tests/orig/golden/hid_bom4/bom_general.bom
new file mode 100644
index 0000000..bb246f5
--- /dev/null
+++ b/tests/orig/golden/hid_bom4/bom_general.bom
@@ -0,0 +1,10 @@
+# $Id$
+# PcbBOM Version 1.0
+# Date: Wed Jun 24 21:42:23 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB BOM
+# Quantity, Description, Value, RefDes
+# --------------------------------------------
+8,"Standard SMT resistor, capacitor etc","RESC3216N",R90_TOP R180_TOP R270_TOP R0_TOP R270_BOT R180_BOT R90_BOT R0_BOT 
+8,"Dual in-line package, narrow (300 mil)","DIP8",UDIP90_TOP UDIP180_TOP UDIP270_TOP UDIP0_TOP UDIP270_BOT UDIP180_BOT UDIP90_BOT UDIP0_BOT 
+8,"Small outline package, narrow (150mil)","SO8",USO90_TOP USO180_TOP USO270_TOP USO0_TOP USO270_BOT USO180_BOT USO90_BOT USO0_BOT 
diff --git a/tests/orig/golden/hid_bom4/bom_general.xy b/tests/orig/golden/hid_bom4/bom_general.xy
new file mode 100644
index 0000000..fb1350e
--- /dev/null
+++ b/tests/orig/golden/hid_bom4/bom_general.xy
@@ -0,0 +1,32 @@
+# $Id$
+# PcbXY Version 1.0
+# Date: Wed Jun 24 21:42:23 2009 UTC
+# Author: Dan McMahill
+# Title: Basic BOM/XY Test - PCB X-Y
+# RefDes, Description, Value, X, Y, rotation, top/bottom
+# X,Y in mm.  rotation in degrees.
+# --------------------------------------------
+R90_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",43.18,109.22,90,top
+R180_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",33.02,109.22,180,top
+R270_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",25.40,109.22,270,top
+R0_TOP,"Standard SMT resistor, capacitor etc","RESC3216N",15.24,109.22,0,top
+UDIP90_TOP,"Dual in-line package, narrow (300 mil)","DIP8",67.31,36.83,180,top
+UDIP180_TOP,"Dual in-line package, narrow (300 mil)","DIP8",49.53,36.83,270,top
+UDIP270_TOP,"Dual in-line package, narrow (300 mil)","DIP8",31.75,36.83,0,top
+UDIP0_TOP,"Dual in-line package, narrow (300 mil)","DIP8",13.97,36.83,90,top
+USO90_TOP,"Small outline package, narrow (150mil)","SO8",43.18,81.28,180,top
+USO180_TOP,"Small outline package, narrow (150mil)","SO8",33.02,81.28,270,top
+USO270_TOP,"Small outline package, narrow (150mil)","SO8",25.40,81.28,0,top
+USO0_TOP,"Small outline package, narrow (150mil)","SO8",15.24,81.28,90,top
+UDIP270_BOT,"Dual in-line package, narrow (300 mil)","DIP8",67.31,16.51,270,bottom
+UDIP180_BOT,"Dual in-line package, narrow (300 mil)","DIP8",49.53,16.51,180,bottom
+UDIP90_BOT,"Dual in-line package, narrow (300 mil)","DIP8",31.75,16.51,90,bottom
+UDIP0_BOT,"Dual in-line package, narrow (300 mil)","DIP8",13.97,16.51,0,bottom
+USO270_BOT,"Small outline package, narrow (150mil)","SO8",43.18,66.04,270,bottom
+USO180_BOT,"Small outline package, narrow (150mil)","SO8",33.02,66.04,180,bottom
+USO90_BOT,"Small outline package, narrow (150mil)","SO8",25.40,66.04,90,bottom
+USO0_BOT,"Small outline package, narrow (150mil)","SO8",15.24,66.04,0,bottom
+R270_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",43.18,99.06,270,bottom
+R180_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",33.02,99.06,180,bottom
+R90_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",25.40,99.06,90,bottom
+R0_BOT,"Standard SMT resistor, capacitor etc","RESC3216N",15.24,99.06,0,bottom
diff --git a/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..2031270
--- /dev/null
+++ b/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:31:54 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.546667    (start point)
+G1 Z#101
+G1 X27.559000 Y13.462000
+G1 X27.305000 Y13.292667
+G1 X7.450667 Y13.292667
+G1 X7.196667 Y13.081000
+G1 X6.985000 Y12.827000
+G1 X6.985000 Y12.530667
+G1 X7.196667 Y12.276667
+G1 X7.450667 Y12.065000
+G1 X27.305000 Y12.065000
+G1 X27.643667 Y11.853333
+G1 X28.194000 Y11.853333
+G1 X28.532667 Y12.065000
+G1 X28.744333 Y12.403667
+G1 X28.744333 Y12.954000
+G1 X28.532667 Y13.292667
+G1 X28.194000 Y13.504333
+G1 X27.770667 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..63f6a2d
--- /dev/null
+++ b/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:31:54 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..afd6cb2
--- /dev/null
+++ b/tests/orig/golden/hid_gcode1/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:31:51 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.546667    (start point)
+G1 Z#101
+G1 X22.521333 Y13.462000
+G1 X22.267333 Y13.292667
+G1 X2.413000 Y13.292667
+G1 X2.159000 Y13.081000
+G1 X1.947333 Y12.827000
+G1 X1.947333 Y12.530667
+G1 X2.159000 Y12.276667
+G1 X2.413000 Y12.065000
+G1 X22.267333 Y12.065000
+G1 X22.606000 Y11.853333
+G1 X23.156333 Y11.853333
+G1 X23.495000 Y12.065000
+G1 X23.706667 Y12.403667
+G1 X23.706667 Y12.954000
+G1 X23.495000 Y13.292667
+G1 X23.156333 Y13.504333
+G1 X22.733000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..19a703d
--- /dev/null
+++ b/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:32 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=0.002000  (safe Z)
+#101=-0.000050  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.462000    (start point)
+G1 Z#101
+G1 X27.516667 Y13.335000
+G1 X27.347333 Y13.208000
+G1 X7.408333 Y13.165667
+G1 X7.112000 Y12.869333
+G1 X7.112000 Y12.488333
+G1 X7.408333 Y12.192000
+G1 X27.347333 Y12.149667
+G1 X27.643667 Y11.938000
+G1 X27.940000 Y11.895667
+G1 X28.278667 Y11.980333
+G1 X28.532667 Y12.192000
+G1 X28.702000 Y12.530667
+G1 X28.702000 Y12.827000
+G1 X28.532667 Y13.165667
+G1 X28.321000 Y13.335000
+G1 X28.024667 Y13.462000
+G1 X27.770667 Y13.462000
+G0 Z#100
+(polygon end, distance 44.84)
+(end, total distance 44.84mm = 1.77in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..7deace0
--- /dev/null
+++ b/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:45:32 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=0.002000  (safe Z)
+#101=-0.002000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..a1d48bd
--- /dev/null
+++ b/tests/orig/golden/hid_gcode10/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:28 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=0.002000  (safe Z)
+#101=-0.000050  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.462000    (start point)
+G1 Z#101
+G1 X22.479000 Y13.335000
+G1 X22.309667 Y13.208000
+G1 X2.370667 Y13.165667
+G1 X2.074333 Y12.869333
+G1 X2.074333 Y12.488333
+G1 X2.370667 Y12.192000
+G1 X22.309667 Y12.149667
+G1 X22.606000 Y11.938000
+G1 X22.902333 Y11.895667
+G1 X23.241000 Y11.980333
+G1 X23.495000 Y12.192000
+G1 X23.664333 Y12.530667
+G1 X23.664333 Y12.827000
+G1 X23.495000 Y13.165667
+G1 X23.283333 Y13.335000
+G1 X22.987000 Y13.462000
+G1 X22.733000 Y13.462000
+G0 Z#100
+(polygon end, distance 44.84)
+(end, total distance 44.84mm = 1.77in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..c144b05
--- /dev/null
+++ b/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,49 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:55 2010 )
+(600 dpi)
+(Unit: inch)
+(Board size: 2.00x1.00 inches)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G20 G90 G64 P0.003 M3 S3000 M7 F1
+G0 Z#100
+(polygon 1)
+G0 X1.085000 Y0.630000    (start point)
+G1 Z#101
+G1 X1.063333 Y0.625000
+G1 X1.055000 Y0.621667
+G1 X0.276667 Y0.618333
+G1 X0.251667 Y0.610000
+G1 X0.230000 Y0.598333
+G1 X0.200000 Y0.568333
+G1 X0.188333 Y0.546667
+G1 X0.180000 Y0.521667
+G1 X0.180000 Y0.476667
+G1 X0.188333 Y0.451667
+G1 X0.200000 Y0.430000
+G1 X0.230000 Y0.400000
+G1 X0.251667 Y0.388333
+G1 X0.276667 Y0.380000
+G1 X1.055000 Y0.376667
+G1 X1.068333 Y0.371667
+G1 X1.093333 Y0.368333
+G1 X1.121667 Y0.370000
+G1 X1.140000 Y0.375000
+G1 X1.171667 Y0.390000
+G1 X1.191667 Y0.406667
+G1 X1.208333 Y0.426667
+G1 X1.223333 Y0.458333
+G1 X1.228333 Y0.478333
+G1 X1.228333 Y0.520000
+G1 X1.223333 Y0.540000
+G1 X1.208333 Y0.571667
+G1 X1.191667 Y0.591667
+G1 X1.171667 Y0.608333
+G1 X1.140000 Y0.623333
+G1 X1.121667 Y0.628333
+G1 X1.085000 Y0.630000
+G0 Z#100
+(polygon end, distance 2.39)
+(end, total distance 60.74mm = 2.39in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..37b9668
--- /dev/null
+++ b/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:45:55 2010 )
+(Unit: inch)
+(Board size: 2.00x1.00 inches)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G20 G90 G64 P0.003 M3 S3000 M7 F1
+G81 X1.100000 Y0.500000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..ddf7394
--- /dev/null
+++ b/tests/orig/golden/hid_gcode11/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,45 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:43 2010 )
+(600 dpi)
+(Unit: inch)
+(Board size: 2.00x1.00 inches)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G20 G90 G64 P0.003 M3 S3000 M7 F1
+G0 Z#100
+(polygon 1)
+G0 X0.886667 Y0.630000    (start point)
+G1 Z#101
+G1 X0.865000 Y0.625000
+G1 X0.856667 Y0.621667
+G1 X0.078333 Y0.618333
+G1 X0.053333 Y0.610000
+G1 X0.031667 Y0.598333
+G1 X0.000000 Y0.566667
+G1 X0.000000 Y0.431667
+G1 X0.031667 Y0.400000
+G1 X0.053333 Y0.388333
+G1 X0.078333 Y0.380000
+G1 X0.856667 Y0.376667
+G1 X0.870000 Y0.371667
+G1 X0.895000 Y0.368333
+G1 X0.923333 Y0.370000
+G1 X0.941667 Y0.375000
+G1 X0.973333 Y0.390000
+G1 X0.993333 Y0.406667
+G1 X1.010000 Y0.426667
+G1 X1.025000 Y0.458333
+G1 X1.030000 Y0.478333
+G1 X1.030000 Y0.520000
+G1 X1.025000 Y0.540000
+G1 X1.010000 Y0.571667
+G1 X0.993333 Y0.591667
+G1 X0.973333 Y0.608333
+G1 X0.941667 Y0.623333
+G1 X0.923333 Y0.628333
+G1 X0.886667 Y0.630000
+G0 Z#100
+(polygon end, distance 2.38)
+(end, total distance 60.56mm = 2.38in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode2/out.bottom.cnc b/tests/orig/golden/hid_gcode2/out.bottom.cnc
new file mode 100644
index 0000000..c3b0526
--- /dev/null
+++ b/tests/orig/golden/hid_gcode2/out.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:35:19 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.546667    (start point)
+G1 Z#101
+G1 X27.559000 Y13.462000
+G1 X27.305000 Y13.292667
+G1 X7.450667 Y13.292667
+G1 X7.196667 Y13.081000
+G1 X6.985000 Y12.827000
+G1 X6.985000 Y12.530667
+G1 X7.196667 Y12.276667
+G1 X7.450667 Y12.065000
+G1 X27.305000 Y12.065000
+G1 X27.643667 Y11.853333
+G1 X28.194000 Y11.853333
+G1 X28.532667 Y12.065000
+G1 X28.744333 Y12.403667
+G1 X28.744333 Y12.954000
+G1 X28.532667 Y13.292667
+G1 X28.194000 Y13.504333
+G1 X27.770667 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode2/out.drill.cnc b/tests/orig/golden/hid_gcode2/out.drill.cnc
new file mode 100644
index 0000000..f7c6d78
--- /dev/null
+++ b/tests/orig/golden/hid_gcode2/out.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:35:19 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode2/out.top.cnc b/tests/orig/golden/hid_gcode2/out.top.cnc
new file mode 100644
index 0000000..ca2b079
--- /dev/null
+++ b/tests/orig/golden/hid_gcode2/out.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:35:16 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.546667    (start point)
+G1 Z#101
+G1 X22.521333 Y13.462000
+G1 X22.267333 Y13.292667
+G1 X2.413000 Y13.292667
+G1 X2.159000 Y13.081000
+G1 X1.947333 Y12.827000
+G1 X1.947333 Y12.530667
+G1 X2.159000 Y12.276667
+G1 X2.413000 Y12.065000
+G1 X22.267333 Y12.065000
+G1 X22.606000 Y11.853333
+G1 X23.156333 Y11.853333
+G1 X23.495000 Y12.065000
+G1 X23.706667 Y12.403667
+G1 X23.706667 Y12.954000
+G1 X23.495000 Y13.292667
+G1 X23.156333 Y13.504333
+G1 X22.733000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..e67b085
--- /dev/null
+++ b/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,40 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:36:04 2010 )
+(1200 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.813000 Y13.546667    (start point)
+G1 Z#101
+G1 X27.664833 Y13.504333
+G1 X27.453167 Y13.398500
+G1 X27.326167 Y13.292667
+G1 X7.535333 Y13.292667
+G1 X7.323667 Y13.229167
+G1 X7.069667 Y12.975167
+G1 X7.006167 Y12.763500
+G1 X7.006167 Y12.615333
+G1 X7.069667 Y12.403667
+G1 X7.323667 Y12.149667
+G1 X7.535333 Y12.086167
+G1 X27.326167 Y12.086167
+G1 X27.495500 Y11.959167
+G1 X27.728333 Y11.853333
+G1 X28.130500 Y11.853333
+G1 X28.448000 Y12.001500
+G1 X28.638500 Y12.213167
+G1 X28.765500 Y12.488333
+G1 X28.765500 Y12.890500
+G1 X28.638500 Y13.165667
+G1 X28.448000 Y13.377333
+G1 X28.130500 Y13.525500
+G1 X27.813000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.35)
+(end, total distance 45.35mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..e7be01d
--- /dev/null
+++ b/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:36:04 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..d7c8c9f
--- /dev/null
+++ b/tests/orig/golden/hid_gcode3/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,40 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:35:46 2010 )
+(1200 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.754167 Y13.546667    (start point)
+G1 Z#101
+G1 X22.606000 Y13.504333
+G1 X22.394333 Y13.398500
+G1 X22.267333 Y13.292667
+G1 X2.476500 Y13.292667
+G1 X2.264833 Y13.229167
+G1 X2.010833 Y12.975167
+G1 X1.947333 Y12.763500
+G1 X1.947333 Y12.615333
+G1 X2.010833 Y12.403667
+G1 X2.264833 Y12.149667
+G1 X2.476500 Y12.086167
+G1 X22.267333 Y12.086167
+G1 X22.436667 Y11.959167
+G1 X22.669500 Y11.853333
+G1 X23.071667 Y11.853333
+G1 X23.389167 Y12.001500
+G1 X23.579667 Y12.213167
+G1 X23.706667 Y12.488333
+G1 X23.706667 Y12.890500
+G1 X23.579667 Y13.165667
+G1 X23.389167 Y13.377333
+G1 X23.071667 Y13.525500
+G1 X22.754167 Y13.546667
+G0 Z#100
+(polygon end, distance 45.35)
+(end, total distance 45.35mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..d049972
--- /dev/null
+++ b/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:36:12 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=5.000000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.546667    (start point)
+G1 Z#101
+G1 X27.559000 Y13.462000
+G1 X27.305000 Y13.292667
+G1 X7.450667 Y13.292667
+G1 X7.196667 Y13.081000
+G1 X6.985000 Y12.827000
+G1 X6.985000 Y12.530667
+G1 X7.196667 Y12.276667
+G1 X7.450667 Y12.065000
+G1 X27.305000 Y12.065000
+G1 X27.643667 Y11.853333
+G1 X28.194000 Y11.853333
+G1 X28.532667 Y12.065000
+G1 X28.744333 Y12.403667
+G1 X28.744333 Y12.954000
+G1 X28.532667 Y13.292667
+G1 X28.194000 Y13.504333
+G1 X27.770667 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..c648655
--- /dev/null
+++ b/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:36:12 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..78e64a5
--- /dev/null
+++ b/tests/orig/golden/hid_gcode4/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:36:08 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=5.000000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.546667    (start point)
+G1 Z#101
+G1 X22.521333 Y13.462000
+G1 X22.267333 Y13.292667
+G1 X2.413000 Y13.292667
+G1 X2.159000 Y13.081000
+G1 X1.947333 Y12.827000
+G1 X1.947333 Y12.530667
+G1 X2.159000 Y12.276667
+G1 X2.413000 Y12.065000
+G1 X22.267333 Y12.065000
+G1 X22.606000 Y11.853333
+G1 X23.156333 Y11.853333
+G1 X23.495000 Y12.065000
+G1 X23.706667 Y12.403667
+G1 X23.706667 Y12.954000
+G1 X23.495000 Y13.292667
+G1 X23.156333 Y13.504333
+G1 X22.733000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..8041f98
--- /dev/null
+++ b/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 18:06:42 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=10.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.546667    (start point)
+G1 Z#101
+G1 X27.559000 Y13.462000
+G1 X27.305000 Y13.292667
+G1 X7.450667 Y13.292667
+G1 X7.196667 Y13.081000
+G1 X6.985000 Y12.827000
+G1 X6.985000 Y12.530667
+G1 X7.196667 Y12.276667
+G1 X7.450667 Y12.065000
+G1 X27.305000 Y12.065000
+G1 X27.643667 Y11.853333
+G1 X28.194000 Y11.853333
+G1 X28.532667 Y12.065000
+G1 X28.744333 Y12.403667
+G1 X28.744333 Y12.954000
+G1 X28.532667 Y13.292667
+G1 X28.194000 Y13.504333
+G1 X27.770667 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..77d8d60
--- /dev/null
+++ b/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 18:06:42 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=10.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..e3bbd7e
--- /dev/null
+++ b/tests/orig/golden/hid_gcode5/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 18:06:40 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=10.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.546667    (start point)
+G1 Z#101
+G1 X22.521333 Y13.462000
+G1 X22.267333 Y13.292667
+G1 X2.413000 Y13.292667
+G1 X2.159000 Y13.081000
+G1 X1.947333 Y12.827000
+G1 X1.947333 Y12.530667
+G1 X2.159000 Y12.276667
+G1 X2.413000 Y12.065000
+G1 X22.267333 Y12.065000
+G1 X22.606000 Y11.853333
+G1 X23.156333 Y11.853333
+G1 X23.495000 Y12.065000
+G1 X23.706667 Y12.403667
+G1 X23.706667 Y12.954000
+G1 X23.495000 Y13.292667
+G1 X23.156333 Y13.504333
+G1 X22.733000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..7d33799
--- /dev/null
+++ b/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,43 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:05 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X0.000000 Y25.400000    (start point)
+G1 Z#101
+G1 X0.000000 Y0.000000
+G1 X37.295667 Y0.000000
+G1 X38.057667 Y0.635000
+G1 X39.073667 Y1.524000
+G1 X39.962667 Y2.540000
+G1 X40.597667 Y3.302000
+G1 X41.486667 Y4.656667
+G1 X41.952333 Y5.503333
+G1 X42.418000 Y6.477000
+G1 X42.883667 Y7.704667
+G1 X43.264667 Y9.017000
+G1 X43.518667 Y10.329333
+G1 X43.645667 Y11.514667
+G1 X43.645667 Y13.843000
+G1 X43.518667 Y15.028333
+G1 X43.264667 Y16.340667
+G1 X42.883667 Y17.653000
+G1 X42.418000 Y18.880667
+G1 X41.952333 Y19.854333
+G1 X41.486667 Y20.701000
+G1 X40.597667 Y22.055667
+G1 X39.962667 Y22.817667
+G1 X39.073667 Y23.833667
+G1 X38.057667 Y24.722667
+G1 X37.253333 Y25.400000
+G1 X0.000000 Y25.400000
+G0 Z#100
+(polygon end, distance 129.45)
+(end, total distance 129.45mm = 5.10in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..6b44265
--- /dev/null
+++ b/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:45:05 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..7dfd6a0
--- /dev/null
+++ b/tests/orig/golden/hid_gcode6/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,43 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:39:31 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X0.000000 Y25.400000    (start point)
+G1 Z#101
+G1 X0.000000 Y0.000000
+G1 X32.258000 Y0.000000
+G1 X33.020000 Y0.635000
+G1 X34.036000 Y1.524000
+G1 X34.925000 Y2.540000
+G1 X35.560000 Y3.302000
+G1 X36.449000 Y4.656667
+G1 X36.914667 Y5.503333
+G1 X37.380333 Y6.477000
+G1 X37.846000 Y7.704667
+G1 X38.227000 Y9.017000
+G1 X38.481000 Y10.329333
+G1 X38.608000 Y11.514667
+G1 X38.608000 Y13.843000
+G1 X38.481000 Y15.028333
+G1 X38.227000 Y16.340667
+G1 X37.846000 Y17.653000
+G1 X37.380333 Y18.880667
+G1 X36.914667 Y19.854333
+G1 X36.449000 Y20.701000
+G1 X35.560000 Y22.055667
+G1 X34.925000 Y22.817667
+G1 X34.036000 Y23.833667
+G1 X33.020000 Y24.722667
+G1 X32.215667 Y25.400000
+G1 X0.000000 Y25.400000
+G0 Z#100
+(polygon end, distance 119.38)
+(end, total distance 119.38mm = 4.70in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..b92e532
--- /dev/null
+++ b/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:12 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.546667    (start point)
+G1 Z#101
+G1 X27.559000 Y13.462000
+G1 X27.305000 Y13.292667
+G1 X7.450667 Y13.292667
+G1 X7.196667 Y13.081000
+G1 X6.985000 Y12.827000
+G1 X6.985000 Y12.530667
+G1 X7.196667 Y12.276667
+G1 X7.450667 Y12.065000
+G1 X27.305000 Y12.065000
+G1 X27.643667 Y11.853333
+G1 X28.194000 Y11.853333
+G1 X28.532667 Y12.065000
+G1 X28.744333 Y12.403667
+G1 X28.744333 Y12.954000
+G1 X28.532667 Y13.292667
+G1 X28.194000 Y13.504333
+G1 X27.770667 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..e8c41bf
--- /dev/null
+++ b/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:45:12 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=70.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..4c64804
--- /dev/null
+++ b/tests/orig/golden/hid_gcode7/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:08 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.546667    (start point)
+G1 Z#101
+G1 X22.521333 Y13.462000
+G1 X22.267333 Y13.292667
+G1 X2.413000 Y13.292667
+G1 X2.159000 Y13.081000
+G1 X1.947333 Y12.827000
+G1 X1.947333 Y12.530667
+G1 X2.159000 Y12.276667
+G1 X2.413000 Y12.065000
+G1 X22.267333 Y12.065000
+G1 X22.606000 Y11.853333
+G1 X23.156333 Y11.853333
+G1 X23.495000 Y12.065000
+G1 X23.706667 Y12.403667
+G1 X23.706667 Y12.954000
+G1 X23.495000 Y13.292667
+G1 X23.156333 Y13.504333
+G1 X22.733000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..286cab5
--- /dev/null
+++ b/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:19 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X27.770667 Y13.546667    (start point)
+G1 Z#101
+G1 X27.559000 Y13.462000
+G1 X27.305000 Y13.292667
+G1 X7.450667 Y13.292667
+G1 X7.196667 Y13.081000
+G1 X6.985000 Y12.827000
+G1 X6.985000 Y12.530667
+G1 X7.196667 Y12.276667
+G1 X7.450667 Y12.065000
+G1 X27.305000 Y12.065000
+G1 X27.643667 Y11.853333
+G1 X28.194000 Y11.853333
+G1 X28.532667 Y12.065000
+G1 X28.744333 Y12.403667
+G1 X28.744333 Y12.954000
+G1 X28.532667 Y13.292667
+G1 X28.194000 Y13.504333
+G1 X27.770667 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..77ac526
--- /dev/null
+++ b/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:45:19 2010 )
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-2.000000  (drill depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G81 X27.940000 Y12.700000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..a57aeb7
--- /dev/null
+++ b/tests/orig/golden/hid_gcode8/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:16 2010 )
+(600 dpi)
+(Unit: mm)
+(Board size: 50.80x25.40 mm)
+#100=2.000000  (safe Z)
+#101=-0.050000  (cutting depth)
+(---------------------------------)
+G17 G21 G90 G64 P0.003 M3 S3000 M7 F25
+G0 Z#100
+(polygon 1)
+G0 X22.733000 Y13.546667    (start point)
+G1 Z#101
+G1 X22.521333 Y13.462000
+G1 X22.267333 Y13.292667
+G1 X2.413000 Y13.292667
+G1 X2.159000 Y13.081000
+G1 X1.947333 Y12.827000
+G1 X1.947333 Y12.530667
+G1 X2.159000 Y12.276667
+G1 X2.413000 Y12.065000
+G1 X22.267333 Y12.065000
+G1 X22.606000 Y11.853333
+G1 X23.156333 Y11.853333
+G1 X23.495000 Y12.065000
+G1 X23.706667 Y12.403667
+G1 X23.706667 Y12.954000
+G1 X23.495000 Y13.292667
+G1 X23.156333 Y13.504333
+G1 X22.733000 Y13.546667
+G0 Z#100
+(polygon end, distance 45.38)
+(end, total distance 45.38mm = 1.79in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.bottom.cnc b/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.bottom.cnc
new file mode 100644
index 0000000..49ebcd5
--- /dev/null
+++ b/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.bottom.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:25 2010 )
+(600 dpi)
+(Unit: inch)
+(Board size: 2.00x1.00 inches)
+#100=0.002000  (safe Z)
+#101=-0.000050  (cutting depth)
+(---------------------------------)
+G17 G20 G90 G64 P0.003 M3 S3000 M7 F1
+G0 Z#100
+(polygon 1)
+G0 X1.093333 Y0.530000    (start point)
+G1 Z#101
+G1 X1.083333 Y0.525000
+G1 X1.076667 Y0.520000
+G1 X0.291667 Y0.518333
+G1 X0.280000 Y0.506667
+G1 X0.280000 Y0.491667
+G1 X0.291667 Y0.480000
+G1 X1.076667 Y0.478333
+G1 X1.088333 Y0.470000
+G1 X1.100000 Y0.468333
+G1 X1.113333 Y0.471667
+G1 X1.123333 Y0.480000
+G1 X1.130000 Y0.493333
+G1 X1.130000 Y0.505000
+G1 X1.123333 Y0.518333
+G1 X1.115000 Y0.525000
+G1 X1.103333 Y0.530000
+G1 X1.093333 Y0.530000
+G0 Z#100
+(polygon end, distance 1.77)
+(end, total distance 44.84mm = 1.77in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.drill.cnc b/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.drill.cnc
new file mode 100644
index 0000000..c4b730a
--- /dev/null
+++ b/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.drill.cnc
@@ -0,0 +1,12 @@
+(Created by G-code exporter)
+(drill file: 1 drills)
+( Tue Mar  9 17:45:25 2010 )
+(Unit: inch)
+(Board size: 2.00x1.00 inches)
+#100=0.002000  (safe Z)
+#101=-0.002000  (drill depth)
+(---------------------------------)
+G17 G20 G90 G64 P0.003 M3 S3000 M7 F1
+G81 X1.100000 Y0.500000 Z#101 R#100
+M5 M9 M2
+(end, total distance 0.00mm = 0.00in)
diff --git a/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.top.cnc b/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.top.cnc
new file mode 100644
index 0000000..638cec1
--- /dev/null
+++ b/tests/orig/golden/hid_gcode9/gcode_oneline.gcode.top.cnc
@@ -0,0 +1,34 @@
+(Created by G-code exporter)
+( Tue Mar  9 17:45:22 2010 )
+(600 dpi)
+(Unit: inch)
+(Board size: 2.00x1.00 inches)
+#100=0.002000  (safe Z)
+#101=-0.000050  (cutting depth)
+(---------------------------------)
+G17 G20 G90 G64 P0.003 M3 S3000 M7 F1
+G0 Z#100
+(polygon 1)
+G0 X0.895000 Y0.530000    (start point)
+G1 Z#101
+G1 X0.885000 Y0.525000
+G1 X0.878333 Y0.520000
+G1 X0.093333 Y0.518333
+G1 X0.081667 Y0.506667
+G1 X0.081667 Y0.491667
+G1 X0.093333 Y0.480000
+G1 X0.878333 Y0.478333
+G1 X0.890000 Y0.470000
+G1 X0.901667 Y0.468333
+G1 X0.915000 Y0.471667
+G1 X0.925000 Y0.480000
+G1 X0.931667 Y0.493333
+G1 X0.931667 Y0.505000
+G1 X0.925000 Y0.518333
+G1 X0.916667 Y0.525000
+G1 X0.905000 Y0.530000
+G1 X0.895000 Y0.530000
+G0 Z#100
+(polygon end, distance 1.77)
+(end, total distance 44.84mm = 1.77in)
+M5 M9 M2
diff --git a/tests/orig/golden/hid_gerber1/gerber_oneline.bottom.gbr b/tests/orig/golden/hid_gerber1/gerber_oneline.bottom.gbr
new file mode 100644
index 0000000..b5b70e8
--- /dev/null
+++ b/tests/orig/golden/hid_gerber1/gerber_oneline.bottom.gbr
@@ -0,0 +1,17 @@
+G04 start of page 3 for group 1 idx 1 *
+G04 Title: Basic Single Trace RS274-X Test, solder *
+G04 Creator: pcb 1.99y *
+G04 CreationDate: Wed Jun 24 21:42:24 2009 UTC *
+G04 For: dan *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 200000 100000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNBACK*%
+%ADD11C,0.0400*%
+%ADD12C,0.0600*%
+%ADD13C,0.0350*%
+G54D11*X170000Y50000D02*X90000D01*
+G54D12*D03*
+G54D13*M02*
diff --git a/tests/orig/golden/hid_gerber1/gerber_oneline.fab.gbr b/tests/orig/golden/hid_gerber1/gerber_oneline.fab.gbr
new file mode 100644
index 0000000..a20714d
--- /dev/null
+++ b/tests/orig/golden/hid_gerber1/gerber_oneline.fab.gbr
@@ -0,0 +1,1734 @@
+G04 start of page 5 for group -3984 idx -3984 *
+G04 Title: Basic Single Trace RS274-X Test, fab *
+G04 Creator: pcb 1.99y *
+G04 CreationDate: Wed Jun 24 21:42:24 2009 UTC *
+G04 For: dan *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 200000 100000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNFAB*%
+%ADD14C,0.0080*%
+%ADD15C,0.0060*%
+%ADD16C,0.0100*%
+G54D14*X90000Y50000D02*Y48400D01*
+Y50000D02*X91386Y50800D01*
+X90000Y50000D02*X88614Y50800D01*
+X15000Y156250D02*Y154650D01*
+Y156250D02*X16386Y157050D01*
+X15000Y156250D02*X13614Y157050D01*
+G54D15*X135000Y158500D02*Y157750D01*
+X136500Y156250D01*
+X138000Y157750D01*
+Y158500D02*Y157750D01*
+X136500Y156250D02*Y152500D01*
+X139801Y155500D02*X142051D01*
+X139801Y152500D02*X142801D01*
+X139801Y158500D02*Y152500D01*
+Y158500D02*X142801D01*
+X147603D02*X148353Y157750D01*
+X145353Y158500D02*X147603D01*
+X144603Y157750D02*X145353Y158500D01*
+X144603Y157750D02*Y156250D01*
+X145353Y155500D01*
+X147603D01*
+X148353Y154750D01*
+Y153250D01*
+X147603Y152500D02*X148353Y153250D01*
+X145353Y152500D02*X147603D01*
+X144603Y153250D02*X145353Y152500D01*
+X135000Y149249D02*X150154D01*
+X98750Y152500D02*X100250D01*
+X99500Y158500D02*Y152500D01*
+X98000Y157000D02*X99500Y158500D01*
+X98000Y149249D02*X102051D01*
+X45000Y153250D02*X45750Y152500D01*
+X45000Y157750D02*Y153250D01*
+Y157750D02*X45750Y158500D01*
+X47250D01*
+X48000Y157750D01*
+Y153250D01*
+X47250Y152500D02*X48000Y153250D01*
+X45750Y152500D02*X47250D01*
+X45000Y154000D02*X48000Y157000D01*
+X49801Y152500D02*X50551D01*
+X52353Y153250D02*X53103Y152500D01*
+X52353Y157750D02*Y153250D01*
+Y157750D02*X53103Y158500D01*
+X54603D01*
+X55353Y157750D01*
+Y153250D01*
+X54603Y152500D02*X55353Y153250D01*
+X53103Y152500D02*X54603D01*
+X52353Y154000D02*X55353Y157000D01*
+X57154Y157750D02*X57904Y158500D01*
+X59404D01*
+X60154Y157750D01*
+Y153250D01*
+X59404Y152500D02*X60154Y153250D01*
+X57904Y152500D02*X59404D01*
+X57154Y153250D02*X57904Y152500D01*
+Y155500D02*X60154D01*
+X61956Y158500D02*X64956D01*
+X61956D02*Y155500D01*
+X62706Y156250D01*
+X64206D01*
+X64956Y155500D01*
+Y153250D01*
+X64206Y152500D02*X64956Y153250D01*
+X62706Y152500D02*X64206D01*
+X61956Y153250D02*X62706Y152500D01*
+X45000Y149249D02*X66757D01*
+X3000Y173500D02*X3750Y172750D01*
+X750Y173500D02*X3000D01*
+X0Y172750D02*X750Y173500D01*
+X0Y172750D02*Y171250D01*
+X750Y170500D01*
+X3000D01*
+X3750Y169750D01*
+Y168250D01*
+X3000Y167500D02*X3750Y168250D01*
+X750Y167500D02*X3000D01*
+X0Y168250D02*X750Y167500D01*
+X5551Y170500D02*Y168250D01*
+X6301Y167500D01*
+X8551Y170500D02*Y166000D01*
+X7801Y165250D02*X8551Y166000D01*
+X6301Y165250D02*X7801D01*
+X5551Y166000D02*X6301Y165250D01*
+Y167500D02*X7801D01*
+X8551Y168250D01*
+X11103Y169750D02*Y167500D01*
+Y169750D02*X11853Y170500D01*
+X12603D01*
+X13353Y169750D01*
+Y167500D01*
+Y169750D02*X14103Y170500D01*
+X14853D01*
+X15603Y169750D01*
+Y167500D01*
+X10353Y170500D02*X11103Y169750D01*
+X17404Y173500D02*Y167500D01*
+Y168250D02*X18154Y167500D01*
+X19654D01*
+X20404Y168250D01*
+Y169750D02*Y168250D01*
+X19654Y170500D02*X20404Y169750D01*
+X18154Y170500D02*X19654D01*
+X17404Y169750D02*X18154Y170500D01*
+X22206Y169750D02*Y168250D01*
+Y169750D02*X22956Y170500D01*
+X24456D01*
+X25206Y169750D01*
+Y168250D01*
+X24456Y167500D02*X25206Y168250D01*
+X22956Y167500D02*X24456D01*
+X22206Y168250D02*X22956Y167500D01*
+X27007Y173500D02*Y168250D01*
+X27757Y167500D01*
+X41750Y173500D02*Y167500D01*
+X44000Y173500D02*X44750Y172750D01*
+Y168250D01*
+X44000Y167500D02*X44750Y168250D01*
+X41000Y167500D02*X44000D01*
+X41000Y173500D02*X44000D01*
+X46551Y172000D02*Y171250D01*
+Y169750D02*Y167500D01*
+X50303Y170500D02*X51053Y169750D01*
+X48803Y170500D02*X50303D01*
+X48053Y169750D02*X48803Y170500D01*
+X48053Y169750D02*Y168250D01*
+X48803Y167500D01*
+X51053Y170500D02*Y168250D01*
+X51803Y167500D01*
+X48803D02*X50303D01*
+X51053Y168250D01*
+X54354Y169750D02*Y167500D01*
+Y169750D02*X55104Y170500D01*
+X55854D01*
+X56604Y169750D01*
+Y167500D01*
+Y169750D02*X57354Y170500D01*
+X58104D01*
+X58854Y169750D01*
+Y167500D01*
+X53604Y170500D02*X54354Y169750D01*
+X60656Y167500D02*X61406D01*
+X65907Y168250D02*X66657Y167500D01*
+X65907Y172750D02*X66657Y173500D01*
+X65907Y172750D02*Y168250D01*
+X68459Y173500D02*X69959D01*
+X69209D02*Y167500D01*
+X68459D02*X69959D01*
+X72510Y169750D02*Y167500D01*
+Y169750D02*X73260Y170500D01*
+X74010D01*
+X74760Y169750D01*
+Y167500D01*
+X71760Y170500D02*X72510Y169750D01*
+X77312Y170500D02*X79562D01*
+X76562Y169750D02*X77312Y170500D01*
+X76562Y169750D02*Y168250D01*
+X77312Y167500D01*
+X79562D01*
+X81363Y173500D02*Y167500D01*
+Y169750D02*X82113Y170500D01*
+X83613D01*
+X84363Y169750D01*
+Y167500D01*
+X86165Y173500D02*X86915Y172750D01*
+Y168250D01*
+X86165Y167500D02*X86915Y168250D01*
+X95750Y167500D02*X98000D01*
+X95000Y168250D02*X95750Y167500D01*
+X95000Y172750D02*Y168250D01*
+Y172750D02*X95750Y173500D01*
+X98000D01*
+X99801Y169750D02*Y168250D01*
+Y169750D02*X100551Y170500D01*
+X102051D01*
+X102801Y169750D01*
+Y168250D01*
+X102051Y167500D02*X102801Y168250D01*
+X100551Y167500D02*X102051D01*
+X99801Y168250D02*X100551Y167500D01*
+X104603Y170500D02*Y168250D01*
+X105353Y167500D01*
+X106853D01*
+X107603Y168250D01*
+Y170500D02*Y168250D01*
+X110154Y169750D02*Y167500D01*
+Y169750D02*X110904Y170500D01*
+X111654D01*
+X112404Y169750D01*
+Y167500D01*
+X109404Y170500D02*X110154Y169750D01*
+X114956Y173500D02*Y168250D01*
+X115706Y167500D01*
+X114206Y171250D02*X115706D01*
+X130750Y173500D02*Y167500D01*
+X130000Y173500D02*X133000D01*
+X133750Y172750D01*
+Y171250D01*
+X133000Y170500D02*X133750Y171250D01*
+X130750Y170500D02*X133000D01*
+X135551Y173500D02*Y168250D01*
+X136301Y167500D01*
+X140053Y170500D02*X140803Y169750D01*
+X138553Y170500D02*X140053D01*
+X137803Y169750D02*X138553Y170500D01*
+X137803Y169750D02*Y168250D01*
+X138553Y167500D01*
+X140803Y170500D02*Y168250D01*
+X141553Y167500D01*
+X138553D02*X140053D01*
+X140803Y168250D01*
+X144104Y173500D02*Y168250D01*
+X144854Y167500D01*
+X143354Y171250D02*X144854D01*
+X147106Y167500D02*X149356D01*
+X146356Y168250D02*X147106Y167500D01*
+X146356Y169750D02*Y168250D01*
+Y169750D02*X147106Y170500D01*
+X148606D01*
+X149356Y169750D01*
+X146356Y169000D02*X149356D01*
+Y169750D02*Y169000D01*
+X154157Y173500D02*Y167500D01*
+X153407D02*X154157Y168250D01*
+X151907Y167500D02*X153407D01*
+X151157Y168250D02*X151907Y167500D01*
+X151157Y169750D02*Y168250D01*
+Y169750D02*X151907Y170500D01*
+X153407D01*
+X154157Y169750D01*
+X157459Y170500D02*Y169750D01*
+Y168250D02*Y167500D01*
+X155959Y172750D02*Y172000D01*
+Y172750D02*X156709Y173500D01*
+X158209D01*
+X158959Y172750D01*
+Y172000D01*
+X157459Y170500D02*X158959Y172000D01*
+X0Y188500D02*X3000D01*
+X1500D02*Y182500D01*
+X4801Y188500D02*Y182500D01*
+Y184750D02*X5551Y185500D01*
+X7051D01*
+X7801Y184750D01*
+Y182500D01*
+X10353D02*X12603D01*
+X9603Y183250D02*X10353Y182500D01*
+X9603Y184750D02*Y183250D01*
+Y184750D02*X10353Y185500D01*
+X11853D01*
+X12603Y184750D01*
+X9603Y184000D02*X12603D01*
+Y184750D02*Y184000D01*
+X15154Y184750D02*Y182500D01*
+Y184750D02*X15904Y185500D01*
+X17404D01*
+X14404D02*X15154Y184750D01*
+X19956Y182500D02*X22206D01*
+X19206Y183250D02*X19956Y182500D01*
+X19206Y184750D02*Y183250D01*
+Y184750D02*X19956Y185500D01*
+X21456D01*
+X22206Y184750D01*
+X19206Y184000D02*X22206D01*
+Y184750D02*Y184000D01*
+X28957Y185500D02*X29707Y184750D01*
+X27457Y185500D02*X28957D01*
+X26707Y184750D02*X27457Y185500D01*
+X26707Y184750D02*Y183250D01*
+X27457Y182500D01*
+X29707Y185500D02*Y183250D01*
+X30457Y182500D01*
+X27457D02*X28957D01*
+X29707Y183250D01*
+X33009Y184750D02*Y182500D01*
+Y184750D02*X33759Y185500D01*
+X35259D01*
+X32259D02*X33009Y184750D01*
+X37810Y182500D02*X40060D01*
+X37060Y183250D02*X37810Y182500D01*
+X37060Y184750D02*Y183250D01*
+Y184750D02*X37810Y185500D01*
+X39310D01*
+X40060Y184750D01*
+X37060Y184000D02*X40060D01*
+Y184750D02*Y184000D01*
+X45312Y182500D02*X46812D01*
+X46062Y188500D02*Y182500D01*
+X44562Y187000D02*X46062Y188500D01*
+X54313D02*Y182500D01*
+X53563D02*X54313Y183250D01*
+X52063Y182500D02*X53563D01*
+X51313Y183250D02*X52063Y182500D01*
+X51313Y184750D02*Y183250D01*
+Y184750D02*X52063Y185500D01*
+X53563D01*
+X54313Y184750D01*
+X56115Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X58366Y187750D02*Y182500D01*
+Y187750D02*X59116Y188500D01*
+X59866D01*
+X57616Y185500D02*X59116D01*
+X62118Y187750D02*Y182500D01*
+Y187750D02*X62868Y188500D01*
+X63618D01*
+X61368Y185500D02*X62868D01*
+X65869Y182500D02*X68119D01*
+X65119Y183250D02*X65869Y182500D01*
+X65119Y184750D02*Y183250D01*
+Y184750D02*X65869Y185500D01*
+X67369D01*
+X68119Y184750D01*
+X65119Y184000D02*X68119D01*
+Y184750D02*Y184000D01*
+X70671Y184750D02*Y182500D01*
+Y184750D02*X71421Y185500D01*
+X72921D01*
+X69921D02*X70671Y184750D01*
+X75472Y182500D02*X77722D01*
+X74722Y183250D02*X75472Y182500D01*
+X74722Y184750D02*Y183250D01*
+Y184750D02*X75472Y185500D01*
+X76972D01*
+X77722Y184750D01*
+X74722Y184000D02*X77722D01*
+Y184750D02*Y184000D01*
+X80274Y184750D02*Y182500D01*
+Y184750D02*X81024Y185500D01*
+X81774D01*
+X82524Y184750D01*
+Y182500D01*
+X79524Y185500D02*X80274Y184750D01*
+X85075Y188500D02*Y183250D01*
+X85825Y182500D01*
+X84325Y186250D02*X85825D01*
+X93027Y188500D02*Y182500D01*
+X92277D02*X93027Y183250D01*
+X90777Y182500D02*X92277D01*
+X90027Y183250D02*X90777Y182500D01*
+X90027Y184750D02*Y183250D01*
+Y184750D02*X90777Y185500D01*
+X92277D01*
+X93027Y184750D01*
+X95578D02*Y182500D01*
+Y184750D02*X96328Y185500D01*
+X97828D01*
+X94828D02*X95578Y184750D01*
+X99630Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X101131Y188500D02*Y183250D01*
+X101881Y182500D01*
+X103383Y188500D02*Y183250D01*
+X104133Y182500D01*
+X109084D02*X111334D01*
+X112084Y183250D01*
+X111334Y184000D02*X112084Y183250D01*
+X109084Y184000D02*X111334D01*
+X108334Y184750D02*X109084Y184000D01*
+X108334Y184750D02*X109084Y185500D01*
+X111334D01*
+X112084Y184750D01*
+X108334Y183250D02*X109084Y182500D01*
+X113886Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X115387Y185500D02*X118387D01*
+X115387Y182500D02*X118387Y185500D01*
+X115387Y182500D02*X118387D01*
+X120939D02*X123189D01*
+X120189Y183250D02*X120939Y182500D01*
+X120189Y184750D02*Y183250D01*
+Y184750D02*X120939Y185500D01*
+X122439D01*
+X123189Y184750D01*
+X120189Y184000D02*X123189D01*
+Y184750D02*Y184000D01*
+X125740Y182500D02*X127990D01*
+X128740Y183250D01*
+X127990Y184000D02*X128740Y183250D01*
+X125740Y184000D02*X127990D01*
+X124990Y184750D02*X125740Y184000D01*
+X124990Y184750D02*X125740Y185500D01*
+X127990D01*
+X128740Y184750D01*
+X124990Y183250D02*X125740Y182500D01*
+X133242Y185500D02*Y183250D01*
+X133992Y182500D01*
+X135492D01*
+X136242Y183250D01*
+Y185500D02*Y183250D01*
+X138793Y182500D02*X141043D01*
+X141793Y183250D01*
+X141043Y184000D02*X141793Y183250D01*
+X138793Y184000D02*X141043D01*
+X138043Y184750D02*X138793Y184000D01*
+X138043Y184750D02*X138793Y185500D01*
+X141043D01*
+X141793Y184750D01*
+X138043Y183250D02*X138793Y182500D01*
+X144345D02*X146595D01*
+X143595Y183250D02*X144345Y182500D01*
+X143595Y184750D02*Y183250D01*
+Y184750D02*X144345Y185500D01*
+X145845D01*
+X146595Y184750D01*
+X143595Y184000D02*X146595D01*
+Y184750D02*Y184000D01*
+X151396Y188500D02*Y182500D01*
+X150646D02*X151396Y183250D01*
+X149146Y182500D02*X150646D01*
+X148396Y183250D02*X149146Y182500D01*
+X148396Y184750D02*Y183250D01*
+Y184750D02*X149146Y185500D01*
+X150646D01*
+X151396Y184750D01*
+X155898Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X158149Y184750D02*Y182500D01*
+Y184750D02*X158899Y185500D01*
+X159649D01*
+X160399Y184750D01*
+Y182500D01*
+X157399Y185500D02*X158149Y184750D01*
+X165651Y188500D02*Y183250D01*
+X166401Y182500D01*
+X164901Y186250D02*X166401D01*
+X167902Y188500D02*Y182500D01*
+Y184750D02*X168652Y185500D01*
+X170152D01*
+X170902Y184750D01*
+Y182500D01*
+X172704Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X174955D02*X177205D01*
+X177955Y183250D01*
+X177205Y184000D02*X177955Y183250D01*
+X174955Y184000D02*X177205D01*
+X174205Y184750D02*X174955Y184000D01*
+X174205Y184750D02*X174955Y185500D01*
+X177205D01*
+X177955Y184750D01*
+X174205Y183250D02*X174955Y182500D01*
+X182457Y188500D02*Y183250D01*
+X183207Y182500D01*
+X186958Y185500D02*X187708Y184750D01*
+X185458Y185500D02*X186958D01*
+X184708Y184750D02*X185458Y185500D01*
+X184708Y184750D02*Y183250D01*
+X185458Y182500D01*
+X187708Y185500D02*Y183250D01*
+X188458Y182500D01*
+X185458D02*X186958D01*
+X187708Y183250D01*
+X190260Y185500D02*Y183250D01*
+X191010Y182500D01*
+X193260Y185500D02*Y181000D01*
+X192510Y180250D02*X193260Y181000D01*
+X191010Y180250D02*X192510D01*
+X190260Y181000D02*X191010Y180250D01*
+Y182500D02*X192510D01*
+X193260Y183250D01*
+X195061Y184750D02*Y183250D01*
+Y184750D02*X195811Y185500D01*
+X197311D01*
+X198061Y184750D01*
+Y183250D01*
+X197311Y182500D02*X198061Y183250D01*
+X195811Y182500D02*X197311D01*
+X195061Y183250D02*X195811Y182500D01*
+X199863Y185500D02*Y183250D01*
+X200613Y182500D01*
+X202113D01*
+X202863Y183250D01*
+Y185500D02*Y183250D01*
+X205414Y188500D02*Y183250D01*
+X206164Y182500D01*
+X204664Y186250D02*X206164D01*
+X207666Y181000D02*X209166Y182500D01*
+X214417D02*X215917D01*
+X215167Y188500D02*Y182500D01*
+X213667Y187000D02*X215167Y188500D01*
+X220419D02*Y182500D01*
+Y184750D02*X221169Y185500D01*
+X222669D01*
+X223419Y184750D01*
+Y182500D01*
+X225220Y184750D02*Y183250D01*
+Y184750D02*X225970Y185500D01*
+X227470D01*
+X228220Y184750D01*
+Y183250D01*
+X227470Y182500D02*X228220Y183250D01*
+X225970Y182500D02*X227470D01*
+X225220Y183250D02*X225970Y182500D01*
+X230022Y188500D02*Y183250D01*
+X230772Y182500D01*
+X233023D02*X235273D01*
+X232273Y183250D02*X233023Y182500D01*
+X232273Y184750D02*Y183250D01*
+Y184750D02*X233023Y185500D01*
+X234523D01*
+X235273Y184750D01*
+X232273Y184000D02*X235273D01*
+Y184750D02*Y184000D01*
+X237825Y182500D02*X240075D01*
+X240825Y183250D01*
+X240075Y184000D02*X240825Y183250D01*
+X237825Y184000D02*X240075D01*
+X237075Y184750D02*X237825Y184000D01*
+X237075Y184750D02*X237825Y185500D01*
+X240075D01*
+X240825Y184750D01*
+X237075Y183250D02*X237825Y182500D01*
+X246076Y188500D02*Y183250D01*
+X246826Y182500D01*
+X245326Y186250D02*X246826D01*
+X248328Y184750D02*Y183250D01*
+Y184750D02*X249078Y185500D01*
+X250578D01*
+X251328Y184750D01*
+Y183250D01*
+X250578Y182500D02*X251328Y183250D01*
+X249078Y182500D02*X250578D01*
+X248328Y183250D02*X249078Y182500D01*
+X253879Y188500D02*Y183250D01*
+X254629Y182500D01*
+X253129Y186250D02*X254629D01*
+X258381Y185500D02*X259131Y184750D01*
+X256881Y185500D02*X258381D01*
+X256131Y184750D02*X256881Y185500D01*
+X256131Y184750D02*Y183250D01*
+X256881Y182500D01*
+X259131Y185500D02*Y183250D01*
+X259881Y182500D01*
+X256881D02*X258381D01*
+X259131Y183250D01*
+X261682Y188500D02*Y183250D01*
+X262432Y182500D01*
+G54D16*X0Y100000D02*X200000D01*
+X0D02*Y0D01*
+X200000Y100000D02*Y0D01*
+X0D02*X200000D01*
+G54D15*Y113500D02*Y107500D01*
+Y113500D02*X202250Y111250D01*
+X204500Y113500D01*
+Y107500D01*
+X208551Y110500D02*X209301Y109750D01*
+X207051Y110500D02*X208551D01*
+X206301Y109750D02*X207051Y110500D01*
+X206301Y109750D02*Y108250D01*
+X207051Y107500D01*
+X209301Y110500D02*Y108250D01*
+X210051Y107500D01*
+X207051D02*X208551D01*
+X209301Y108250D01*
+X211853Y110500D02*X214853Y107500D01*
+X211853D02*X214853Y110500D01*
+X216654Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X218906Y109750D02*Y107500D01*
+Y109750D02*X219656Y110500D01*
+X220406D01*
+X221156Y109750D01*
+Y107500D01*
+Y109750D02*X221906Y110500D01*
+X222656D01*
+X223406Y109750D01*
+Y107500D01*
+X218156Y110500D02*X218906Y109750D01*
+X225207Y110500D02*Y108250D01*
+X225957Y107500D01*
+X227457D01*
+X228207Y108250D01*
+Y110500D02*Y108250D01*
+X230759Y109750D02*Y107500D01*
+Y109750D02*X231509Y110500D01*
+X232259D01*
+X233009Y109750D01*
+Y107500D01*
+Y109750D02*X233759Y110500D01*
+X234509D01*
+X235259Y109750D01*
+Y107500D01*
+X230009Y110500D02*X230759Y109750D01*
+X240510Y113500D02*Y107500D01*
+X242760Y113500D02*X243510Y112750D01*
+Y108250D01*
+X242760Y107500D02*X243510Y108250D01*
+X239760Y107500D02*X242760D01*
+X239760Y113500D02*X242760D01*
+X245312Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X247563Y109750D02*Y107500D01*
+Y109750D02*X248313Y110500D01*
+X249063D01*
+X249813Y109750D01*
+Y107500D01*
+Y109750D02*X250563Y110500D01*
+X251313D01*
+X252063Y109750D01*
+Y107500D01*
+X246813Y110500D02*X247563Y109750D01*
+X254615Y107500D02*X256865D01*
+X253865Y108250D02*X254615Y107500D01*
+X253865Y109750D02*Y108250D01*
+Y109750D02*X254615Y110500D01*
+X256115D01*
+X256865Y109750D01*
+X253865Y109000D02*X256865D01*
+Y109750D02*Y109000D01*
+X259416Y109750D02*Y107500D01*
+Y109750D02*X260166Y110500D01*
+X260916D01*
+X261666Y109750D01*
+Y107500D01*
+X258666Y110500D02*X259416Y109750D01*
+X264218Y107500D02*X266468D01*
+X267218Y108250D01*
+X266468Y109000D02*X267218Y108250D01*
+X264218Y109000D02*X266468D01*
+X263468Y109750D02*X264218Y109000D01*
+X263468Y109750D02*X264218Y110500D01*
+X266468D01*
+X267218Y109750D01*
+X263468Y108250D02*X264218Y107500D01*
+X269019Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X270521Y109750D02*Y108250D01*
+Y109750D02*X271271Y110500D01*
+X272771D01*
+X273521Y109750D01*
+Y108250D01*
+X272771Y107500D02*X273521Y108250D01*
+X271271Y107500D02*X272771D01*
+X270521Y108250D02*X271271Y107500D01*
+X276072Y109750D02*Y107500D01*
+Y109750D02*X276822Y110500D01*
+X277572D01*
+X278322Y109750D01*
+Y107500D01*
+X275322Y110500D02*X276072Y109750D01*
+X280874Y107500D02*X283124D01*
+X283874Y108250D01*
+X283124Y109000D02*X283874Y108250D01*
+X280874Y109000D02*X283124D01*
+X280124Y109750D02*X280874Y109000D01*
+X280124Y109750D02*X280874Y110500D01*
+X283124D01*
+X283874Y109750D01*
+X280124Y108250D02*X280874Y107500D01*
+X285675Y111250D02*X286425D01*
+X285675Y109750D02*X286425D01*
+X290927Y112750D02*X291677Y113500D01*
+X293927D01*
+X294677Y112750D01*
+Y111250D01*
+X290927Y107500D02*X294677Y111250D01*
+X290927Y107500D02*X294677D01*
+X296478Y108250D02*X297228Y107500D01*
+X296478Y112750D02*Y108250D01*
+Y112750D02*X297228Y113500D01*
+X298728D01*
+X299478Y112750D01*
+Y108250D01*
+X298728Y107500D02*X299478Y108250D01*
+X297228Y107500D02*X298728D01*
+X296478Y109000D02*X299478Y112000D01*
+X301280Y108250D02*X302030Y107500D01*
+X301280Y112750D02*Y108250D01*
+Y112750D02*X302030Y113500D01*
+X303530D01*
+X304280Y112750D01*
+Y108250D01*
+X303530Y107500D02*X304280Y108250D01*
+X302030Y107500D02*X303530D01*
+X301280Y109000D02*X304280Y112000D01*
+X306081Y108250D02*X306831Y107500D01*
+X306081Y112750D02*Y108250D01*
+Y112750D02*X306831Y113500D01*
+X308331D01*
+X309081Y112750D01*
+Y108250D01*
+X308331Y107500D02*X309081Y108250D01*
+X306831Y107500D02*X308331D01*
+X306081Y109000D02*X309081Y112000D01*
+X314333Y109750D02*Y107500D01*
+Y109750D02*X315083Y110500D01*
+X315833D01*
+X316583Y109750D01*
+Y107500D01*
+Y109750D02*X317333Y110500D01*
+X318083D01*
+X318833Y109750D01*
+Y107500D01*
+X313583Y110500D02*X314333Y109750D01*
+X320634Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X322136Y113500D02*Y108250D01*
+X322886Y107500D01*
+X325137D02*X327387D01*
+X328137Y108250D01*
+X327387Y109000D02*X328137Y108250D01*
+X325137Y109000D02*X327387D01*
+X324387Y109750D02*X325137Y109000D01*
+X324387Y109750D02*X325137Y110500D01*
+X327387D01*
+X328137Y109750D01*
+X324387Y108250D02*X325137Y107500D01*
+X332639Y110500D02*Y108250D01*
+X333389Y107500D01*
+X334139D01*
+X334889Y108250D01*
+Y110500D02*Y108250D01*
+X335639Y107500D01*
+X336389D01*
+X337139Y108250D01*
+Y110500D02*Y108250D01*
+X338940Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X343442Y113500D02*Y107500D01*
+X342692D02*X343442Y108250D01*
+X341192Y107500D02*X342692D01*
+X340442Y108250D02*X341192Y107500D01*
+X340442Y109750D02*Y108250D01*
+Y109750D02*X341192Y110500D01*
+X342692D01*
+X343442Y109750D01*
+X345993Y107500D02*X348243D01*
+X345243Y108250D02*X345993Y107500D01*
+X345243Y109750D02*Y108250D01*
+Y109750D02*X345993Y110500D01*
+X347493D01*
+X348243Y109750D01*
+X345243Y109000D02*X348243D01*
+Y109750D02*Y109000D01*
+X350045Y106000D02*X351545Y107500D01*
+X356796D02*X358296D01*
+X357546Y113500D02*Y107500D01*
+X356046Y112000D02*X357546Y113500D01*
+X360098Y108250D02*X360848Y107500D01*
+X360098Y112750D02*Y108250D01*
+Y112750D02*X360848Y113500D01*
+X362348D01*
+X363098Y112750D01*
+Y108250D01*
+X362348Y107500D02*X363098Y108250D01*
+X360848Y107500D02*X362348D01*
+X360098Y109000D02*X363098Y112000D01*
+X364899Y108250D02*X365649Y107500D01*
+X364899Y112750D02*Y108250D01*
+Y112750D02*X365649Y113500D01*
+X367149D01*
+X367899Y112750D01*
+Y108250D01*
+X367149Y107500D02*X367899Y108250D01*
+X365649Y107500D02*X367149D01*
+X364899Y109000D02*X367899Y112000D01*
+X369701Y108250D02*X370451Y107500D01*
+X369701Y112750D02*Y108250D01*
+Y112750D02*X370451Y113500D01*
+X371951D01*
+X372701Y112750D01*
+Y108250D01*
+X371951Y107500D02*X372701Y108250D01*
+X370451Y107500D02*X371951D01*
+X369701Y109000D02*X372701Y112000D01*
+X377952Y109750D02*Y107500D01*
+Y109750D02*X378702Y110500D01*
+X379452D01*
+X380202Y109750D01*
+Y107500D01*
+Y109750D02*X380952Y110500D01*
+X381702D01*
+X382452Y109750D01*
+Y107500D01*
+X377202Y110500D02*X377952Y109750D01*
+X384254Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X385755Y113500D02*Y108250D01*
+X386505Y107500D01*
+X388757D02*X391007D01*
+X391757Y108250D01*
+X391007Y109000D02*X391757Y108250D01*
+X388757Y109000D02*X391007D01*
+X388007Y109750D02*X388757Y109000D01*
+X388007Y109750D02*X388757Y110500D01*
+X391007D01*
+X391757Y109750D01*
+X388007Y108250D02*X388757Y107500D01*
+X396258Y113500D02*Y107500D01*
+Y109750D02*X397008Y110500D01*
+X398508D01*
+X399258Y109750D01*
+Y107500D01*
+X401060Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X404811Y110500D02*X405561Y109750D01*
+X403311Y110500D02*X404811D01*
+X402561Y109750D02*X403311Y110500D01*
+X402561Y109750D02*Y108250D01*
+X403311Y107500D01*
+X404811D01*
+X405561Y108250D01*
+X402561Y106000D02*X403311Y105250D01*
+X404811D01*
+X405561Y106000D01*
+Y110500D02*Y106000D01*
+X407363Y113500D02*Y107500D01*
+Y109750D02*X408113Y110500D01*
+X409613D01*
+X410363Y109750D01*
+Y107500D01*
+X0Y-9500D02*X3000D01*
+X3750Y-8750D01*
+Y-7250D02*Y-8750D01*
+X3000Y-6500D02*X3750Y-7250D01*
+X750Y-6500D02*X3000D01*
+X750Y-3500D02*Y-9500D01*
+X0Y-3500D02*X3000D01*
+X3750Y-4250D01*
+Y-5750D01*
+X3000Y-6500D02*X3750Y-5750D01*
+X5551Y-7250D02*Y-8750D01*
+Y-7250D02*X6301Y-6500D01*
+X7801D01*
+X8551Y-7250D01*
+Y-8750D01*
+X7801Y-9500D02*X8551Y-8750D01*
+X6301Y-9500D02*X7801D01*
+X5551Y-8750D02*X6301Y-9500D01*
+X12603Y-6500D02*X13353Y-7250D01*
+X11103Y-6500D02*X12603D01*
+X10353Y-7250D02*X11103Y-6500D01*
+X10353Y-7250D02*Y-8750D01*
+X11103Y-9500D01*
+X13353Y-6500D02*Y-8750D01*
+X14103Y-9500D01*
+X11103D02*X12603D01*
+X13353Y-8750D01*
+X16654Y-7250D02*Y-9500D01*
+Y-7250D02*X17404Y-6500D01*
+X18904D01*
+X15904D02*X16654Y-7250D01*
+X23706Y-3500D02*Y-9500D01*
+X22956D02*X23706Y-8750D01*
+X21456Y-9500D02*X22956D01*
+X20706Y-8750D02*X21456Y-9500D01*
+X20706Y-7250D02*Y-8750D01*
+Y-7250D02*X21456Y-6500D01*
+X22956D01*
+X23706Y-7250D01*
+X28207D02*Y-8750D01*
+Y-7250D02*X28957Y-6500D01*
+X30457D01*
+X31207Y-7250D01*
+Y-8750D01*
+X30457Y-9500D02*X31207Y-8750D01*
+X28957Y-9500D02*X30457D01*
+X28207Y-8750D02*X28957Y-9500D01*
+X33009Y-6500D02*Y-8750D01*
+X33759Y-9500D01*
+X35259D01*
+X36009Y-8750D01*
+Y-6500D02*Y-8750D01*
+X38560Y-3500D02*Y-8750D01*
+X39310Y-9500D01*
+X37810Y-5750D02*X39310D01*
+X40812Y-3500D02*Y-8750D01*
+X41562Y-9500D01*
+X43063Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X45315Y-7250D02*Y-9500D01*
+Y-7250D02*X46065Y-6500D01*
+X46815D01*
+X47565Y-7250D01*
+Y-9500D01*
+X44565Y-6500D02*X45315Y-7250D01*
+X50116Y-9500D02*X52366D01*
+X49366Y-8750D02*X50116Y-9500D01*
+X49366Y-7250D02*Y-8750D01*
+Y-7250D02*X50116Y-6500D01*
+X51616D01*
+X52366Y-7250D01*
+X49366Y-8000D02*X52366D01*
+Y-7250D02*Y-8000D01*
+X56868Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X59119D02*X61369D01*
+X62119Y-8750D01*
+X61369Y-8000D02*X62119Y-8750D01*
+X59119Y-8000D02*X61369D01*
+X58369Y-7250D02*X59119Y-8000D01*
+X58369Y-7250D02*X59119Y-6500D01*
+X61369D01*
+X62119Y-7250D01*
+X58369Y-8750D02*X59119Y-9500D01*
+X67371Y-3500D02*Y-8750D01*
+X68121Y-9500D01*
+X66621Y-5750D02*X68121D01*
+X69622Y-3500D02*Y-9500D01*
+Y-7250D02*X70372Y-6500D01*
+X71872D01*
+X72622Y-7250D01*
+Y-9500D01*
+X75174D02*X77424D01*
+X74424Y-8750D02*X75174Y-9500D01*
+X74424Y-7250D02*Y-8750D01*
+Y-7250D02*X75174Y-6500D01*
+X76674D01*
+X77424Y-7250D01*
+X74424Y-8000D02*X77424D01*
+Y-7250D02*Y-8000D01*
+X82675Y-6500D02*X84925D01*
+X81925Y-7250D02*X82675Y-6500D01*
+X81925Y-7250D02*Y-8750D01*
+X82675Y-9500D01*
+X84925D01*
+X87477D02*X89727D01*
+X86727Y-8750D02*X87477Y-9500D01*
+X86727Y-7250D02*Y-8750D01*
+Y-7250D02*X87477Y-6500D01*
+X88977D01*
+X89727Y-7250D01*
+X86727Y-8000D02*X89727D01*
+Y-7250D02*Y-8000D01*
+X92278Y-7250D02*Y-9500D01*
+Y-7250D02*X93028Y-6500D01*
+X93778D01*
+X94528Y-7250D01*
+Y-9500D01*
+X91528Y-6500D02*X92278Y-7250D01*
+X97080Y-3500D02*Y-8750D01*
+X97830Y-9500D01*
+X96330Y-5750D02*X97830D01*
+X100081Y-9500D02*X102331D01*
+X99331Y-8750D02*X100081Y-9500D01*
+X99331Y-7250D02*Y-8750D01*
+Y-7250D02*X100081Y-6500D01*
+X101581D01*
+X102331Y-7250D01*
+X99331Y-8000D02*X102331D01*
+Y-7250D02*Y-8000D01*
+X104883Y-7250D02*Y-9500D01*
+Y-7250D02*X105633Y-6500D01*
+X107133D01*
+X104133D02*X104883Y-7250D01*
+X108934Y-3500D02*Y-8750D01*
+X109684Y-9500D01*
+X111186Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X113437Y-7250D02*Y-9500D01*
+Y-7250D02*X114187Y-6500D01*
+X114937D01*
+X115687Y-7250D01*
+Y-9500D01*
+X112687Y-6500D02*X113437Y-7250D01*
+X118239Y-9500D02*X120489D01*
+X117489Y-8750D02*X118239Y-9500D01*
+X117489Y-7250D02*Y-8750D01*
+Y-7250D02*X118239Y-6500D01*
+X119739D01*
+X120489Y-7250D01*
+X117489Y-8000D02*X120489D01*
+Y-7250D02*Y-8000D01*
+X124990Y-7250D02*Y-8750D01*
+Y-7250D02*X125740Y-6500D01*
+X127240D01*
+X127990Y-7250D01*
+Y-8750D01*
+X127240Y-9500D02*X127990Y-8750D01*
+X125740Y-9500D02*X127240D01*
+X124990Y-8750D02*X125740Y-9500D01*
+X130542Y-4250D02*Y-9500D01*
+Y-4250D02*X131292Y-3500D01*
+X132042D01*
+X129792Y-6500D02*X131292D01*
+X136993Y-3500D02*Y-8750D01*
+X137743Y-9500D01*
+X136243Y-5750D02*X137743D01*
+X139245Y-3500D02*Y-9500D01*
+Y-7250D02*X139995Y-6500D01*
+X141495D01*
+X142245Y-7250D01*
+Y-9500D01*
+X144046Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X146298D02*X148548D01*
+X149298Y-8750D01*
+X148548Y-8000D02*X149298Y-8750D01*
+X146298Y-8000D02*X148548D01*
+X145548Y-7250D02*X146298Y-8000D01*
+X145548Y-7250D02*X146298Y-6500D01*
+X148548D01*
+X149298Y-7250D01*
+X145548Y-8750D02*X146298Y-9500D01*
+X154549D02*X156049D01*
+X155299Y-3500D02*Y-9500D01*
+X153799Y-5000D02*X155299Y-3500D01*
+X157851Y-8750D02*X158601Y-9500D01*
+X157851Y-4250D02*Y-8750D01*
+Y-4250D02*X158601Y-3500D01*
+X160101D01*
+X160851Y-4250D01*
+Y-8750D01*
+X160101Y-9500D02*X160851Y-8750D01*
+X158601Y-9500D02*X160101D01*
+X157851Y-8000D02*X160851Y-5000D01*
+X166102Y-7250D02*Y-9500D01*
+Y-7250D02*X166852Y-6500D01*
+X167602D01*
+X168352Y-7250D01*
+Y-9500D01*
+Y-7250D02*X169102Y-6500D01*
+X169852D01*
+X170602Y-7250D01*
+Y-9500D01*
+X165352Y-6500D02*X166102Y-7250D01*
+X172404Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X173905Y-3500D02*Y-8750D01*
+X174655Y-9500D01*
+X179607Y-7250D02*Y-9500D01*
+Y-7250D02*X180357Y-6500D01*
+X181857D01*
+X178857D02*X179607Y-7250D01*
+X184408Y-9500D02*X186658D01*
+X183658Y-8750D02*X184408Y-9500D01*
+X183658Y-7250D02*Y-8750D01*
+Y-7250D02*X184408Y-6500D01*
+X185908D01*
+X186658Y-7250D01*
+X183658Y-8000D02*X186658D01*
+Y-7250D02*Y-8000D01*
+X189210Y-6500D02*X191460D01*
+X188460Y-7250D02*X189210Y-6500D01*
+X188460Y-7250D02*Y-8750D01*
+X189210Y-9500D01*
+X191460D01*
+X194011Y-3500D02*Y-8750D01*
+X194761Y-9500D01*
+X193261Y-5750D02*X194761D01*
+X198513Y-6500D02*X199263Y-7250D01*
+X197013Y-6500D02*X198513D01*
+X196263Y-7250D02*X197013Y-6500D01*
+X196263Y-7250D02*Y-8750D01*
+X197013Y-9500D01*
+X199263Y-6500D02*Y-8750D01*
+X200013Y-9500D01*
+X197013D02*X198513D01*
+X199263Y-8750D01*
+X202564Y-7250D02*Y-9500D01*
+Y-7250D02*X203314Y-6500D01*
+X204064D01*
+X204814Y-7250D01*
+Y-9500D01*
+X201814Y-6500D02*X202564Y-7250D01*
+X208866Y-6500D02*X209616Y-7250D01*
+X207366Y-6500D02*X208866D01*
+X206616Y-7250D02*X207366Y-6500D01*
+X206616Y-7250D02*Y-8750D01*
+X207366Y-9500D01*
+X208866D01*
+X209616Y-8750D01*
+X206616Y-11000D02*X207366Y-11750D01*
+X208866D01*
+X209616Y-11000D01*
+Y-6500D02*Y-11000D01*
+X211417Y-3500D02*Y-8750D01*
+X212167Y-9500D01*
+X214419D02*X216669D01*
+X213669Y-8750D02*X214419Y-9500D01*
+X213669Y-7250D02*Y-8750D01*
+Y-7250D02*X214419Y-6500D01*
+X215919D01*
+X216669Y-7250D01*
+X213669Y-8000D02*X216669D01*
+Y-7250D02*Y-8000D01*
+X221170Y-6500D02*X224170D01*
+X228672Y-8750D02*X229422Y-9500D01*
+X228672Y-4250D02*Y-8750D01*
+Y-4250D02*X229422Y-3500D01*
+X230922D01*
+X231672Y-4250D01*
+Y-8750D01*
+X230922Y-9500D02*X231672Y-8750D01*
+X229422Y-9500D02*X230922D01*
+X228672Y-8000D02*X231672Y-5000D01*
+X233473Y-11000D02*X234973Y-9500D01*
+X236775Y-8750D02*X237525Y-9500D01*
+X236775Y-4250D02*Y-8750D01*
+Y-4250D02*X237525Y-3500D01*
+X239025D01*
+X239775Y-4250D01*
+Y-8750D01*
+X239025Y-9500D02*X239775Y-8750D01*
+X237525Y-9500D02*X239025D01*
+X236775Y-8000D02*X239775Y-5000D01*
+X245026Y-3500D02*Y-8750D01*
+X245776Y-9500D01*
+X244276Y-5750D02*X245776D01*
+X247278Y-7250D02*Y-8750D01*
+Y-7250D02*X248028Y-6500D01*
+X249528D01*
+X250278Y-7250D01*
+Y-8750D01*
+X249528Y-9500D02*X250278Y-8750D01*
+X248028Y-9500D02*X249528D01*
+X247278Y-8750D02*X248028Y-9500D01*
+X254779Y-4250D02*X255529Y-3500D01*
+X257779D01*
+X258529Y-4250D01*
+Y-5750D01*
+X254779Y-9500D02*X258529Y-5750D01*
+X254779Y-9500D02*X258529D01*
+X260331Y-8750D02*X261081Y-9500D01*
+X260331Y-4250D02*Y-8750D01*
+Y-4250D02*X261081Y-3500D01*
+X262581D01*
+X263331Y-4250D01*
+Y-8750D01*
+X262581Y-9500D02*X263331Y-8750D01*
+X261081Y-9500D02*X262581D01*
+X260331Y-8000D02*X263331Y-5000D01*
+X265132Y-8750D02*X265882Y-9500D01*
+X265132Y-4250D02*Y-8750D01*
+Y-4250D02*X265882Y-3500D01*
+X267382D01*
+X268132Y-4250D01*
+Y-8750D01*
+X267382Y-9500D02*X268132Y-8750D01*
+X265882Y-9500D02*X267382D01*
+X265132Y-8000D02*X268132Y-5000D01*
+X269934Y-8750D02*X270684Y-9500D01*
+X269934Y-4250D02*Y-8750D01*
+Y-4250D02*X270684Y-3500D01*
+X272184D01*
+X272934Y-4250D01*
+Y-8750D01*
+X272184Y-9500D02*X272934Y-8750D01*
+X270684Y-9500D02*X272184D01*
+X269934Y-8000D02*X272934Y-5000D01*
+X274735Y-11000D02*X276235Y-9500D01*
+X278787D02*X280287D01*
+X279537Y-3500D02*Y-9500D01*
+X278037Y-5000D02*X279537Y-3500D01*
+X282088Y-8750D02*X282838Y-9500D01*
+X282088Y-4250D02*Y-8750D01*
+Y-4250D02*X282838Y-3500D01*
+X284338D01*
+X285088Y-4250D01*
+Y-8750D01*
+X284338Y-9500D02*X285088Y-8750D01*
+X282838Y-9500D02*X284338D01*
+X282088Y-8000D02*X285088Y-5000D01*
+X286890Y-8750D02*X287640Y-9500D01*
+X286890Y-4250D02*Y-8750D01*
+Y-4250D02*X287640Y-3500D01*
+X289140D01*
+X289890Y-4250D01*
+Y-8750D01*
+X289140Y-9500D02*X289890Y-8750D01*
+X287640Y-9500D02*X289140D01*
+X286890Y-8000D02*X289890Y-5000D01*
+X291691Y-8750D02*X292441Y-9500D01*
+X291691Y-4250D02*Y-8750D01*
+Y-4250D02*X292441Y-3500D01*
+X293941D01*
+X294691Y-4250D01*
+Y-8750D01*
+X293941Y-9500D02*X294691Y-8750D01*
+X292441Y-9500D02*X293941D01*
+X291691Y-8000D02*X294691Y-5000D01*
+X299943Y-7250D02*Y-9500D01*
+Y-7250D02*X300693Y-6500D01*
+X301443D01*
+X302193Y-7250D01*
+Y-9500D01*
+Y-7250D02*X302943Y-6500D01*
+X303693D01*
+X304443Y-7250D01*
+Y-9500D01*
+X299193Y-6500D02*X299943Y-7250D01*
+X306244Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X307746Y-3500D02*Y-8750D01*
+X308496Y-9500D01*
+X310747D02*X312997D01*
+X313747Y-8750D01*
+X312997Y-8000D02*X313747Y-8750D01*
+X310747Y-8000D02*X312997D01*
+X309997Y-7250D02*X310747Y-8000D01*
+X309997Y-7250D02*X310747Y-6500D01*
+X312997D01*
+X313747Y-7250D01*
+X309997Y-8750D02*X310747Y-9500D01*
+X200750Y128500D02*Y122500D01*
+X203000Y128500D02*X203750Y127750D01*
+Y123250D01*
+X203000Y122500D02*X203750Y123250D01*
+X200000Y122500D02*X203000D01*
+X200000Y128500D02*X203000D01*
+X207801Y125500D02*X208551Y124750D01*
+X206301Y125500D02*X207801D01*
+X205551Y124750D02*X206301Y125500D01*
+X205551Y124750D02*Y123250D01*
+X206301Y122500D01*
+X208551Y125500D02*Y123250D01*
+X209301Y122500D01*
+X206301D02*X207801D01*
+X208551Y123250D01*
+X211853Y128500D02*Y123250D01*
+X212603Y122500D01*
+X211103Y126250D02*X212603D01*
+X214854Y122500D02*X217104D01*
+X214104Y123250D02*X214854Y122500D01*
+X214104Y124750D02*Y123250D01*
+Y124750D02*X214854Y125500D01*
+X216354D01*
+X217104Y124750D01*
+X214104Y124000D02*X217104D01*
+Y124750D02*Y124000D01*
+X218906Y126250D02*X219656D01*
+X218906Y124750D02*X219656D01*
+X224157Y128500D02*Y122500D01*
+X226407Y124750D01*
+X228657Y122500D01*
+Y128500D02*Y122500D01*
+X231209D02*X233459D01*
+X230459Y123250D02*X231209Y122500D01*
+X230459Y124750D02*Y123250D01*
+Y124750D02*X231209Y125500D01*
+X232709D01*
+X233459Y124750D01*
+X230459Y124000D02*X233459D01*
+Y124750D02*Y124000D01*
+X238260Y128500D02*Y122500D01*
+X237510D02*X238260Y123250D01*
+X236010Y122500D02*X237510D01*
+X235260Y123250D02*X236010Y122500D01*
+X235260Y124750D02*Y123250D01*
+Y124750D02*X236010Y125500D01*
+X237510D01*
+X238260Y124750D01*
+X242762Y128500D02*X245012D01*
+Y123250D01*
+X244262Y122500D02*X245012Y123250D01*
+X243512Y122500D02*X244262D01*
+X242762Y123250D02*X243512Y122500D01*
+X246813Y125500D02*Y123250D01*
+X247563Y122500D01*
+X249063D01*
+X249813Y123250D01*
+Y125500D02*Y123250D01*
+X252365Y124750D02*Y122500D01*
+Y124750D02*X253115Y125500D01*
+X253865D01*
+X254615Y124750D01*
+Y122500D01*
+X251615Y125500D02*X252365Y124750D01*
+X259116Y127750D02*X259866Y128500D01*
+X262116D01*
+X262866Y127750D01*
+Y126250D01*
+X259116Y122500D02*X262866Y126250D01*
+X259116Y122500D02*X262866D01*
+X264668Y125500D02*X267668Y128500D01*
+X264668Y125500D02*X268418D01*
+X267668Y128500D02*Y122500D01*
+X272919Y127750D02*X273669Y128500D01*
+X275919D01*
+X276669Y127750D01*
+Y126250D01*
+X272919Y122500D02*X276669Y126250D01*
+X272919Y122500D02*X276669D01*
+X279221D02*X280721D01*
+X279971Y128500D02*Y122500D01*
+X278471Y127000D02*X279971Y128500D01*
+X282522Y126250D02*X283272D01*
+X282522Y124750D02*X283272D01*
+X285074Y125500D02*X288074Y128500D01*
+X285074Y125500D02*X288824D01*
+X288074Y128500D02*Y122500D01*
+X290625Y127750D02*X291375Y128500D01*
+X293625D01*
+X294375Y127750D01*
+Y126250D01*
+X290625Y122500D02*X294375Y126250D01*
+X290625Y122500D02*X294375D01*
+X296177Y126250D02*X296927D01*
+X296177Y124750D02*X296927D01*
+X298728Y127750D02*X299478Y128500D01*
+X301728D01*
+X302478Y127750D01*
+Y126250D01*
+X298728Y122500D02*X302478Y126250D01*
+X298728Y122500D02*X302478D01*
+X304280Y125500D02*X307280Y128500D01*
+X304280Y125500D02*X308030D01*
+X307280Y128500D02*Y122500D01*
+X312531Y127750D02*X313281Y128500D01*
+X315531D01*
+X316281Y127750D01*
+Y126250D01*
+X312531Y122500D02*X316281Y126250D01*
+X312531Y122500D02*X316281D01*
+X318083Y123250D02*X318833Y122500D01*
+X318083Y127750D02*Y123250D01*
+Y127750D02*X318833Y128500D01*
+X320333D01*
+X321083Y127750D01*
+Y123250D01*
+X320333Y122500D02*X321083Y123250D01*
+X318833Y122500D02*X320333D01*
+X318083Y124000D02*X321083Y127000D01*
+X322884Y123250D02*X323634Y122500D01*
+X322884Y127750D02*Y123250D01*
+Y127750D02*X323634Y128500D01*
+X325134D01*
+X325884Y127750D01*
+Y123250D01*
+X325134Y122500D02*X325884Y123250D01*
+X323634Y122500D02*X325134D01*
+X322884Y124000D02*X325884Y127000D01*
+X327686Y122500D02*X330686Y125500D01*
+Y127750D02*Y125500D01*
+X329936Y128500D02*X330686Y127750D01*
+X328436Y128500D02*X329936D01*
+X327686Y127750D02*X328436Y128500D01*
+X327686Y127750D02*Y126250D01*
+X328436Y125500D01*
+X330686D01*
+X335187Y128500D02*Y123250D01*
+X335937Y122500D01*
+X337437D01*
+X338187Y123250D01*
+Y128500D02*Y123250D01*
+X339989Y128500D02*X342989D01*
+X341489D02*Y122500D01*
+X345540D02*X347790D01*
+X344790Y123250D02*X345540Y122500D01*
+X344790Y127750D02*Y123250D01*
+Y127750D02*X345540Y128500D01*
+X347790D01*
+X200000Y142750D02*Y137500D01*
+Y142750D02*X200750Y143500D01*
+X203000D01*
+X203750Y142750D01*
+Y137500D01*
+X200000Y140500D02*X203750D01*
+X205551D02*Y138250D01*
+X206301Y137500D01*
+X207801D01*
+X208551Y138250D01*
+Y140500D02*Y138250D01*
+X211103Y143500D02*Y138250D01*
+X211853Y137500D01*
+X210353Y141250D02*X211853D01*
+X213354Y143500D02*Y137500D01*
+Y139750D02*X214104Y140500D01*
+X215604D01*
+X216354Y139750D01*
+Y137500D01*
+X218156Y139750D02*Y138250D01*
+Y139750D02*X218906Y140500D01*
+X220406D01*
+X221156Y139750D01*
+Y138250D01*
+X220406Y137500D02*X221156Y138250D01*
+X218906Y137500D02*X220406D01*
+X218156Y138250D02*X218906Y137500D01*
+X223707Y139750D02*Y137500D01*
+Y139750D02*X224457Y140500D01*
+X225957D01*
+X222957D02*X223707Y139750D01*
+X227759Y141250D02*X228509D01*
+X227759Y139750D02*X228509D01*
+X233760Y143500D02*Y137500D01*
+X236010Y143500D02*X236760Y142750D01*
+Y138250D01*
+X236010Y137500D02*X236760Y138250D01*
+X233010Y137500D02*X236010D01*
+X233010Y143500D02*X236010D01*
+X240812Y140500D02*X241562Y139750D01*
+X239312Y140500D02*X240812D01*
+X238562Y139750D02*X239312Y140500D01*
+X238562Y139750D02*Y138250D01*
+X239312Y137500D01*
+X241562Y140500D02*Y138250D01*
+X242312Y137500D01*
+X239312D02*X240812D01*
+X241562Y138250D01*
+X244863Y139750D02*Y137500D01*
+Y139750D02*X245613Y140500D01*
+X246363D01*
+X247113Y139750D01*
+Y137500D01*
+X244113Y140500D02*X244863Y139750D01*
+X251615Y143500D02*Y137500D01*
+Y143500D02*X253865Y141250D01*
+X256115Y143500D01*
+Y137500D01*
+X258666Y140500D02*X260916D01*
+X257916Y139750D02*X258666Y140500D01*
+X257916Y139750D02*Y138250D01*
+X258666Y137500D01*
+X260916D01*
+X262718Y143500D02*Y137500D01*
+Y143500D02*X264968Y141250D01*
+X267218Y143500D01*
+Y137500D01*
+X271269Y140500D02*X272019Y139750D01*
+X269769Y140500D02*X271269D01*
+X269019Y139750D02*X269769Y140500D01*
+X269019Y139750D02*Y138250D01*
+X269769Y137500D01*
+X272019Y140500D02*Y138250D01*
+X272769Y137500D01*
+X269769D02*X271269D01*
+X272019Y138250D01*
+X274571Y143500D02*Y137500D01*
+Y139750D02*X275321Y140500D01*
+X276821D01*
+X277571Y139750D01*
+Y137500D01*
+X279372Y142000D02*Y141250D01*
+Y139750D02*Y137500D01*
+X280874Y143500D02*Y138250D01*
+X281624Y137500D01*
+X283125Y143500D02*Y138250D01*
+X283875Y137500D01*
+X200000Y158500D02*X203000D01*
+X201500D02*Y152500D01*
+X204801Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X207053Y158500D02*Y153250D01*
+X207803Y152500D01*
+X206303Y156250D02*X207803D01*
+X209304Y158500D02*Y153250D01*
+X210054Y152500D01*
+X212306D02*X214556D01*
+X211556Y153250D02*X212306Y152500D01*
+X211556Y154750D02*Y153250D01*
+Y154750D02*X212306Y155500D01*
+X213806D01*
+X214556Y154750D01*
+X211556Y154000D02*X214556D01*
+Y154750D02*Y154000D01*
+X216357Y156250D02*X217107D01*
+X216357Y154750D02*X217107D01*
+X221609Y152500D02*X224609D01*
+X225359Y153250D01*
+Y154750D02*Y153250D01*
+X224609Y155500D02*X225359Y154750D01*
+X222359Y155500D02*X224609D01*
+X222359Y158500D02*Y152500D01*
+X221609Y158500D02*X224609D01*
+X225359Y157750D01*
+Y156250D01*
+X224609Y155500D02*X225359Y156250D01*
+X229410Y155500D02*X230160Y154750D01*
+X227910Y155500D02*X229410D01*
+X227160Y154750D02*X227910Y155500D01*
+X227160Y154750D02*Y153250D01*
+X227910Y152500D01*
+X230160Y155500D02*Y153250D01*
+X230910Y152500D01*
+X227910D02*X229410D01*
+X230160Y153250D01*
+X233462Y152500D02*X235712D01*
+X236462Y153250D01*
+X235712Y154000D02*X236462Y153250D01*
+X233462Y154000D02*X235712D01*
+X232712Y154750D02*X233462Y154000D01*
+X232712Y154750D02*X233462Y155500D01*
+X235712D01*
+X236462Y154750D01*
+X232712Y153250D02*X233462Y152500D01*
+X238263Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X240515Y155500D02*X242765D01*
+X239765Y154750D02*X240515Y155500D01*
+X239765Y154750D02*Y153250D01*
+X240515Y152500D01*
+X242765D01*
+X250266Y158500D02*X251016Y157750D01*
+X248016Y158500D02*X250266D01*
+X247266Y157750D02*X248016Y158500D01*
+X247266Y157750D02*Y156250D01*
+X248016Y155500D01*
+X250266D01*
+X251016Y154750D01*
+Y153250D01*
+X250266Y152500D02*X251016Y153250D01*
+X248016Y152500D02*X250266D01*
+X247266Y153250D02*X248016Y152500D01*
+X252818Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X255069Y154750D02*Y152500D01*
+Y154750D02*X255819Y155500D01*
+X256569D01*
+X257319Y154750D01*
+Y152500D01*
+X254319Y155500D02*X255069Y154750D01*
+X261371Y155500D02*X262121Y154750D01*
+X259871Y155500D02*X261371D01*
+X259121Y154750D02*X259871Y155500D01*
+X259121Y154750D02*Y153250D01*
+X259871Y152500D01*
+X261371D01*
+X262121Y153250D01*
+X259121Y151000D02*X259871Y150250D01*
+X261371D01*
+X262121Y151000D01*
+Y155500D02*Y151000D01*
+X263922Y158500D02*Y153250D01*
+X264672Y152500D01*
+X266924D02*X269174D01*
+X266174Y153250D02*X266924Y152500D01*
+X266174Y154750D02*Y153250D01*
+Y154750D02*X266924Y155500D01*
+X268424D01*
+X269174Y154750D01*
+X266174Y154000D02*X269174D01*
+Y154750D02*Y154000D01*
+X273675Y158500D02*X276675D01*
+X275175D02*Y152500D01*
+X279227Y154750D02*Y152500D01*
+Y154750D02*X279977Y155500D01*
+X281477D01*
+X278477D02*X279227Y154750D01*
+X285528Y155500D02*X286278Y154750D01*
+X284028Y155500D02*X285528D01*
+X283278Y154750D02*X284028Y155500D01*
+X283278Y154750D02*Y153250D01*
+X284028Y152500D01*
+X286278Y155500D02*Y153250D01*
+X287028Y152500D01*
+X284028D02*X285528D01*
+X286278Y153250D01*
+X289580Y155500D02*X291830D01*
+X288830Y154750D02*X289580Y155500D01*
+X288830Y154750D02*Y153250D01*
+X289580Y152500D01*
+X291830D01*
+X294381D02*X296631D01*
+X293631Y153250D02*X294381Y152500D01*
+X293631Y154750D02*Y153250D01*
+Y154750D02*X294381Y155500D01*
+X295881D01*
+X296631Y154750D01*
+X293631Y154000D02*X296631D01*
+Y154750D02*Y154000D01*
+X301133Y158500D02*X304133D01*
+X304883Y157750D01*
+Y156250D01*
+X304133Y155500D02*X304883Y156250D01*
+X301883Y155500D02*X304133D01*
+X301883Y158500D02*Y152500D01*
+Y155500D02*X304883Y152500D01*
+X309684Y158500D02*X310434Y157750D01*
+X307434Y158500D02*X309684D01*
+X306684Y157750D02*X307434Y158500D01*
+X306684Y157750D02*Y156250D01*
+X307434Y155500D01*
+X309684D01*
+X310434Y154750D01*
+Y153250D01*
+X309684Y152500D02*X310434Y153250D01*
+X307434Y152500D02*X309684D01*
+X306684Y153250D02*X307434Y152500D01*
+X312236Y157750D02*X312986Y158500D01*
+X315236D01*
+X315986Y157750D01*
+Y156250D01*
+X312236Y152500D02*X315986Y156250D01*
+X312236Y152500D02*X315986D01*
+X317787D02*X321537Y156250D01*
+Y158500D02*Y156250D01*
+X317787Y158500D02*X321537D01*
+X323339Y155500D02*X326339Y158500D01*
+X323339Y155500D02*X327089D01*
+X326339Y158500D02*Y152500D01*
+X328890Y155500D02*X331890D01*
+X333692Y158500D02*Y157750D01*
+X337442Y154000D01*
+Y152500D01*
+X333692Y154000D02*Y152500D01*
+Y154000D02*X337442Y157750D01*
+Y158500D02*Y157750D01*
+X341943Y158500D02*X344943D01*
+X343443D02*Y152500D01*
+X347495D02*X349745D01*
+X346745Y153250D02*X347495Y152500D01*
+X346745Y154750D02*Y153250D01*
+Y154750D02*X347495Y155500D01*
+X348995D01*
+X349745Y154750D01*
+X346745Y154000D02*X349745D01*
+Y154750D02*Y154000D01*
+X352296Y152500D02*X354546D01*
+X355296Y153250D01*
+X354546Y154000D02*X355296Y153250D01*
+X352296Y154000D02*X354546D01*
+X351546Y154750D02*X352296Y154000D01*
+X351546Y154750D02*X352296Y155500D01*
+X354546D01*
+X355296Y154750D01*
+X351546Y153250D02*X352296Y152500D01*
+X357848Y158500D02*Y153250D01*
+X358598Y152500D01*
+X357098Y156250D02*X358598D01*
+X362799Y155500D02*X365799D01*
+X370301Y158500D02*Y152500D01*
+Y158500D02*X373301D01*
+X370301Y155500D02*X372551D01*
+X377352D02*X378102Y154750D01*
+X375852Y155500D02*X377352D01*
+X375102Y154750D02*X375852Y155500D01*
+X375102Y154750D02*Y153250D01*
+X375852Y152500D01*
+X378102Y155500D02*Y153250D01*
+X378852Y152500D01*
+X375852D02*X377352D01*
+X378102Y153250D01*
+X380654Y158500D02*Y152500D01*
+Y153250D02*X381404Y152500D01*
+X382904D01*
+X383654Y153250D01*
+Y154750D02*Y153250D01*
+X382904Y155500D02*X383654Y154750D01*
+X381404Y155500D02*X382904D01*
+X380654Y154750D02*X381404Y155500D01*
+X386205Y154750D02*Y152500D01*
+Y154750D02*X386955Y155500D01*
+X388455D01*
+X385455D02*X386205Y154750D01*
+X390257Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X392508Y155500D02*X394758D01*
+X391758Y154750D02*X392508Y155500D01*
+X391758Y154750D02*Y153250D01*
+X392508Y152500D01*
+X394758D01*
+X398810Y155500D02*X399560Y154750D01*
+X397310Y155500D02*X398810D01*
+X396560Y154750D02*X397310Y155500D01*
+X396560Y154750D02*Y153250D01*
+X397310Y152500D01*
+X399560Y155500D02*Y153250D01*
+X400310Y152500D01*
+X397310D02*X398810D01*
+X399560Y153250D01*
+X402861Y158500D02*Y153250D01*
+X403611Y152500D01*
+X402111Y156250D02*X403611D01*
+X405113Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X406614Y154750D02*Y153250D01*
+Y154750D02*X407364Y155500D01*
+X408864D01*
+X409614Y154750D01*
+Y153250D01*
+X408864Y152500D02*X409614Y153250D01*
+X407364Y152500D02*X408864D01*
+X406614Y153250D02*X407364Y152500D01*
+X412166Y154750D02*Y152500D01*
+Y154750D02*X412916Y155500D01*
+X413666D01*
+X414416Y154750D01*
+Y152500D01*
+X411416Y155500D02*X412166Y154750D01*
+X419667Y158500D02*Y152500D01*
+X421917Y158500D02*X422667Y157750D01*
+Y153250D01*
+X421917Y152500D02*X422667Y153250D01*
+X418917Y152500D02*X421917D01*
+X418917Y158500D02*X421917D01*
+X425219Y154750D02*Y152500D01*
+Y154750D02*X425969Y155500D01*
+X427469D01*
+X424469D02*X425219Y154750D01*
+X431520Y155500D02*X432270Y154750D01*
+X430020Y155500D02*X431520D01*
+X429270Y154750D02*X430020Y155500D01*
+X429270Y154750D02*Y153250D01*
+X430020Y152500D01*
+X432270Y155500D02*Y153250D01*
+X433020Y152500D01*
+X430020D02*X431520D01*
+X432270Y153250D01*
+X434822Y155500D02*Y153250D01*
+X435572Y152500D01*
+X436322D01*
+X437072Y153250D01*
+Y155500D02*Y153250D01*
+X437822Y152500D01*
+X438572D01*
+X439322Y153250D01*
+Y155500D02*Y153250D01*
+X441123Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X443375Y154750D02*Y152500D01*
+Y154750D02*X444125Y155500D01*
+X444875D01*
+X445625Y154750D01*
+Y152500D01*
+X442625Y155500D02*X443375Y154750D01*
+X449676Y155500D02*X450426Y154750D01*
+X448176Y155500D02*X449676D01*
+X447426Y154750D02*X448176Y155500D01*
+X447426Y154750D02*Y153250D01*
+X448176Y152500D01*
+X449676D01*
+X450426Y153250D01*
+X447426Y151000D02*X448176Y150250D01*
+X449676D01*
+X450426Y151000D01*
+Y155500D02*Y151000D01*
+M02*
diff --git a/tests/orig/golden/hid_gerber1/gerber_oneline.plated-drill.cnc b/tests/orig/golden/hid_gerber1/gerber_oneline.plated-drill.cnc
new file mode 100644
index 0000000..5456a91
--- /dev/null
+++ b/tests/orig/golden/hid_gerber1/gerber_oneline.plated-drill.cnc
@@ -0,0 +1,7 @@
+M48
+INCH
+T13C0.035
+%
+T13
+X009000Y005000
+M30
diff --git a/tests/orig/golden/hid_gerber1/gerber_oneline.top.gbr b/tests/orig/golden/hid_gerber1/gerber_oneline.top.gbr
new file mode 100644
index 0000000..c24a8fe
--- /dev/null
+++ b/tests/orig/golden/hid_gerber1/gerber_oneline.top.gbr
@@ -0,0 +1,17 @@
+G04 start of page 2 for group 0 idx 0 *
+G04 Title: Basic Single Trace RS274-X Test, component *
+G04 Creator: pcb 1.99y *
+G04 CreationDate: Wed Jun 24 21:42:24 2009 UTC *
+G04 For: dan *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 200000 100000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNFRONT*%
+%ADD11C,0.0400*%
+%ADD12C,0.0600*%
+%ADD13C,0.0350*%
+G54D11*X10000Y50000D02*X90000D01*
+G54D12*D03*
+G54D13*M02*
diff --git a/tests/orig/golden/hid_gerber2/out.bottom.gbr b/tests/orig/golden/hid_gerber2/out.bottom.gbr
new file mode 100644
index 0000000..5e9aa06
--- /dev/null
+++ b/tests/orig/golden/hid_gerber2/out.bottom.gbr
@@ -0,0 +1,17 @@
+G04 start of page 3 for group 1 idx 1 *
+G04 Title: Basic Single Trace RS274-X Test, solder *
+G04 Creator: pcb 1.99y *
+G04 CreationDate: Wed Jun 24 22:05:35 2009 UTC *
+G04 For: dan *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 200000 100000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNBACK*%
+%ADD11C,0.0400*%
+%ADD12C,0.0600*%
+%ADD13C,0.0350*%
+G54D11*X170000Y50000D02*X90000D01*
+G54D12*D03*
+G54D13*M02*
diff --git a/tests/orig/golden/hid_gerber2/out.fab.gbr b/tests/orig/golden/hid_gerber2/out.fab.gbr
new file mode 100644
index 0000000..f1629a4
--- /dev/null
+++ b/tests/orig/golden/hid_gerber2/out.fab.gbr
@@ -0,0 +1,1754 @@
+G04 start of page 5 for group -3984 idx -3984 *
+G04 Title: Basic Single Trace RS274-X Test, fab *
+G04 Creator: pcb 1.99y *
+G04 CreationDate: Wed Jun 24 22:05:35 2009 UTC *
+G04 For: dan *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 200000 100000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNFAB*%
+%ADD14C,0.0080*%
+%ADD15C,0.0060*%
+%ADD16C,0.0100*%
+G54D14*X90000Y50000D02*Y48400D01*
+Y50000D02*X91386Y50800D01*
+X90000Y50000D02*X88614Y50800D01*
+X15000Y156250D02*Y154650D01*
+Y156250D02*X16386Y157050D01*
+X15000Y156250D02*X13614Y157050D01*
+G54D15*X135000Y158500D02*Y157750D01*
+X136500Y156250D01*
+X138000Y157750D01*
+Y158500D02*Y157750D01*
+X136500Y156250D02*Y152500D01*
+X139801Y155500D02*X142051D01*
+X139801Y152500D02*X142801D01*
+X139801Y158500D02*Y152500D01*
+Y158500D02*X142801D01*
+X147603D02*X148353Y157750D01*
+X145353Y158500D02*X147603D01*
+X144603Y157750D02*X145353Y158500D01*
+X144603Y157750D02*Y156250D01*
+X145353Y155500D01*
+X147603D01*
+X148353Y154750D01*
+Y153250D01*
+X147603Y152500D02*X148353Y153250D01*
+X145353Y152500D02*X147603D01*
+X144603Y153250D02*X145353Y152500D01*
+X135000Y149249D02*X150154D01*
+X98750Y152500D02*X100250D01*
+X99500Y158500D02*Y152500D01*
+X98000Y157000D02*X99500Y158500D01*
+X98000Y149249D02*X102051D01*
+X45000Y153250D02*X45750Y152500D01*
+X45000Y157750D02*Y153250D01*
+Y157750D02*X45750Y158500D01*
+X47250D01*
+X48000Y157750D01*
+Y153250D01*
+X47250Y152500D02*X48000Y153250D01*
+X45750Y152500D02*X47250D01*
+X45000Y154000D02*X48000Y157000D01*
+X49801Y152500D02*X50551D01*
+X52353Y153250D02*X53103Y152500D01*
+X52353Y157750D02*Y153250D01*
+Y157750D02*X53103Y158500D01*
+X54603D01*
+X55353Y157750D01*
+Y153250D01*
+X54603Y152500D02*X55353Y153250D01*
+X53103Y152500D02*X54603D01*
+X52353Y154000D02*X55353Y157000D01*
+X57154Y157750D02*X57904Y158500D01*
+X59404D01*
+X60154Y157750D01*
+Y153250D01*
+X59404Y152500D02*X60154Y153250D01*
+X57904Y152500D02*X59404D01*
+X57154Y153250D02*X57904Y152500D01*
+Y155500D02*X60154D01*
+X61956Y158500D02*X64956D01*
+X61956D02*Y155500D01*
+X62706Y156250D01*
+X64206D01*
+X64956Y155500D01*
+Y153250D01*
+X64206Y152500D02*X64956Y153250D01*
+X62706Y152500D02*X64206D01*
+X61956Y153250D02*X62706Y152500D01*
+X45000Y149249D02*X66757D01*
+X3000Y173500D02*X3750Y172750D01*
+X750Y173500D02*X3000D01*
+X0Y172750D02*X750Y173500D01*
+X0Y172750D02*Y171250D01*
+X750Y170500D01*
+X3000D01*
+X3750Y169750D01*
+Y168250D01*
+X3000Y167500D02*X3750Y168250D01*
+X750Y167500D02*X3000D01*
+X0Y168250D02*X750Y167500D01*
+X5551Y170500D02*Y168250D01*
+X6301Y167500D01*
+X8551Y170500D02*Y166000D01*
+X7801Y165250D02*X8551Y166000D01*
+X6301Y165250D02*X7801D01*
+X5551Y166000D02*X6301Y165250D01*
+Y167500D02*X7801D01*
+X8551Y168250D01*
+X11103Y169750D02*Y167500D01*
+Y169750D02*X11853Y170500D01*
+X12603D01*
+X13353Y169750D01*
+Y167500D01*
+Y169750D02*X14103Y170500D01*
+X14853D01*
+X15603Y169750D01*
+Y167500D01*
+X10353Y170500D02*X11103Y169750D01*
+X17404Y173500D02*Y167500D01*
+Y168250D02*X18154Y167500D01*
+X19654D01*
+X20404Y168250D01*
+Y169750D02*Y168250D01*
+X19654Y170500D02*X20404Y169750D01*
+X18154Y170500D02*X19654D01*
+X17404Y169750D02*X18154Y170500D01*
+X22206Y169750D02*Y168250D01*
+Y169750D02*X22956Y170500D01*
+X24456D01*
+X25206Y169750D01*
+Y168250D01*
+X24456Y167500D02*X25206Y168250D01*
+X22956Y167500D02*X24456D01*
+X22206Y168250D02*X22956Y167500D01*
+X27007Y173500D02*Y168250D01*
+X27757Y167500D01*
+X41750Y173500D02*Y167500D01*
+X44000Y173500D02*X44750Y172750D01*
+Y168250D01*
+X44000Y167500D02*X44750Y168250D01*
+X41000Y167500D02*X44000D01*
+X41000Y173500D02*X44000D01*
+X46551Y172000D02*Y171250D01*
+Y169750D02*Y167500D01*
+X50303Y170500D02*X51053Y169750D01*
+X48803Y170500D02*X50303D01*
+X48053Y169750D02*X48803Y170500D01*
+X48053Y169750D02*Y168250D01*
+X48803Y167500D01*
+X51053Y170500D02*Y168250D01*
+X51803Y167500D01*
+X48803D02*X50303D01*
+X51053Y168250D01*
+X54354Y169750D02*Y167500D01*
+Y169750D02*X55104Y170500D01*
+X55854D01*
+X56604Y169750D01*
+Y167500D01*
+Y169750D02*X57354Y170500D01*
+X58104D01*
+X58854Y169750D01*
+Y167500D01*
+X53604Y170500D02*X54354Y169750D01*
+X60656Y167500D02*X61406D01*
+X65907Y168250D02*X66657Y167500D01*
+X65907Y172750D02*X66657Y173500D01*
+X65907Y172750D02*Y168250D01*
+X68459Y173500D02*X69959D01*
+X69209D02*Y167500D01*
+X68459D02*X69959D01*
+X72510Y169750D02*Y167500D01*
+Y169750D02*X73260Y170500D01*
+X74010D01*
+X74760Y169750D01*
+Y167500D01*
+X71760Y170500D02*X72510Y169750D01*
+X77312Y170500D02*X79562D01*
+X76562Y169750D02*X77312Y170500D01*
+X76562Y169750D02*Y168250D01*
+X77312Y167500D01*
+X79562D01*
+X81363Y173500D02*Y167500D01*
+Y169750D02*X82113Y170500D01*
+X83613D01*
+X84363Y169750D01*
+Y167500D01*
+X86165Y173500D02*X86915Y172750D01*
+Y168250D01*
+X86165Y167500D02*X86915Y168250D01*
+X95750Y167500D02*X98000D01*
+X95000Y168250D02*X95750Y167500D01*
+X95000Y172750D02*Y168250D01*
+Y172750D02*X95750Y173500D01*
+X98000D01*
+X99801Y169750D02*Y168250D01*
+Y169750D02*X100551Y170500D01*
+X102051D01*
+X102801Y169750D01*
+Y168250D01*
+X102051Y167500D02*X102801Y168250D01*
+X100551Y167500D02*X102051D01*
+X99801Y168250D02*X100551Y167500D01*
+X104603Y170500D02*Y168250D01*
+X105353Y167500D01*
+X106853D01*
+X107603Y168250D01*
+Y170500D02*Y168250D01*
+X110154Y169750D02*Y167500D01*
+Y169750D02*X110904Y170500D01*
+X111654D01*
+X112404Y169750D01*
+Y167500D01*
+X109404Y170500D02*X110154Y169750D01*
+X114956Y173500D02*Y168250D01*
+X115706Y167500D01*
+X114206Y171250D02*X115706D01*
+X130750Y173500D02*Y167500D01*
+X130000Y173500D02*X133000D01*
+X133750Y172750D01*
+Y171250D01*
+X133000Y170500D02*X133750Y171250D01*
+X130750Y170500D02*X133000D01*
+X135551Y173500D02*Y168250D01*
+X136301Y167500D01*
+X140053Y170500D02*X140803Y169750D01*
+X138553Y170500D02*X140053D01*
+X137803Y169750D02*X138553Y170500D01*
+X137803Y169750D02*Y168250D01*
+X138553Y167500D01*
+X140803Y170500D02*Y168250D01*
+X141553Y167500D01*
+X138553D02*X140053D01*
+X140803Y168250D01*
+X144104Y173500D02*Y168250D01*
+X144854Y167500D01*
+X143354Y171250D02*X144854D01*
+X147106Y167500D02*X149356D01*
+X146356Y168250D02*X147106Y167500D01*
+X146356Y169750D02*Y168250D01*
+Y169750D02*X147106Y170500D01*
+X148606D01*
+X149356Y169750D01*
+X146356Y169000D02*X149356D01*
+Y169750D02*Y169000D01*
+X154157Y173500D02*Y167500D01*
+X153407D02*X154157Y168250D01*
+X151907Y167500D02*X153407D01*
+X151157Y168250D02*X151907Y167500D01*
+X151157Y169750D02*Y168250D01*
+Y169750D02*X151907Y170500D01*
+X153407D01*
+X154157Y169750D01*
+X157459Y170500D02*Y169750D01*
+Y168250D02*Y167500D01*
+X155959Y172750D02*Y172000D01*
+Y172750D02*X156709Y173500D01*
+X158209D01*
+X158959Y172750D01*
+Y172000D01*
+X157459Y170500D02*X158959Y172000D01*
+X0Y188500D02*X3000D01*
+X1500D02*Y182500D01*
+X4801Y188500D02*Y182500D01*
+Y184750D02*X5551Y185500D01*
+X7051D01*
+X7801Y184750D01*
+Y182500D01*
+X10353D02*X12603D01*
+X9603Y183250D02*X10353Y182500D01*
+X9603Y184750D02*Y183250D01*
+Y184750D02*X10353Y185500D01*
+X11853D01*
+X12603Y184750D01*
+X9603Y184000D02*X12603D01*
+Y184750D02*Y184000D01*
+X15154Y184750D02*Y182500D01*
+Y184750D02*X15904Y185500D01*
+X17404D01*
+X14404D02*X15154Y184750D01*
+X19956Y182500D02*X22206D01*
+X19206Y183250D02*X19956Y182500D01*
+X19206Y184750D02*Y183250D01*
+Y184750D02*X19956Y185500D01*
+X21456D01*
+X22206Y184750D01*
+X19206Y184000D02*X22206D01*
+Y184750D02*Y184000D01*
+X28957Y185500D02*X29707Y184750D01*
+X27457Y185500D02*X28957D01*
+X26707Y184750D02*X27457Y185500D01*
+X26707Y184750D02*Y183250D01*
+X27457Y182500D01*
+X29707Y185500D02*Y183250D01*
+X30457Y182500D01*
+X27457D02*X28957D01*
+X29707Y183250D01*
+X33009Y184750D02*Y182500D01*
+Y184750D02*X33759Y185500D01*
+X35259D01*
+X32259D02*X33009Y184750D01*
+X37810Y182500D02*X40060D01*
+X37060Y183250D02*X37810Y182500D01*
+X37060Y184750D02*Y183250D01*
+Y184750D02*X37810Y185500D01*
+X39310D01*
+X40060Y184750D01*
+X37060Y184000D02*X40060D01*
+Y184750D02*Y184000D01*
+X45312Y182500D02*X46812D01*
+X46062Y188500D02*Y182500D01*
+X44562Y187000D02*X46062Y188500D01*
+X54313D02*Y182500D01*
+X53563D02*X54313Y183250D01*
+X52063Y182500D02*X53563D01*
+X51313Y183250D02*X52063Y182500D01*
+X51313Y184750D02*Y183250D01*
+Y184750D02*X52063Y185500D01*
+X53563D01*
+X54313Y184750D01*
+X56115Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X58366Y187750D02*Y182500D01*
+Y187750D02*X59116Y188500D01*
+X59866D01*
+X57616Y185500D02*X59116D01*
+X62118Y187750D02*Y182500D01*
+Y187750D02*X62868Y188500D01*
+X63618D01*
+X61368Y185500D02*X62868D01*
+X65869Y182500D02*X68119D01*
+X65119Y183250D02*X65869Y182500D01*
+X65119Y184750D02*Y183250D01*
+Y184750D02*X65869Y185500D01*
+X67369D01*
+X68119Y184750D01*
+X65119Y184000D02*X68119D01*
+Y184750D02*Y184000D01*
+X70671Y184750D02*Y182500D01*
+Y184750D02*X71421Y185500D01*
+X72921D01*
+X69921D02*X70671Y184750D01*
+X75472Y182500D02*X77722D01*
+X74722Y183250D02*X75472Y182500D01*
+X74722Y184750D02*Y183250D01*
+Y184750D02*X75472Y185500D01*
+X76972D01*
+X77722Y184750D01*
+X74722Y184000D02*X77722D01*
+Y184750D02*Y184000D01*
+X80274Y184750D02*Y182500D01*
+Y184750D02*X81024Y185500D01*
+X81774D01*
+X82524Y184750D01*
+Y182500D01*
+X79524Y185500D02*X80274Y184750D01*
+X85075Y188500D02*Y183250D01*
+X85825Y182500D01*
+X84325Y186250D02*X85825D01*
+X93027Y188500D02*Y182500D01*
+X92277D02*X93027Y183250D01*
+X90777Y182500D02*X92277D01*
+X90027Y183250D02*X90777Y182500D01*
+X90027Y184750D02*Y183250D01*
+Y184750D02*X90777Y185500D01*
+X92277D01*
+X93027Y184750D01*
+X95578D02*Y182500D01*
+Y184750D02*X96328Y185500D01*
+X97828D01*
+X94828D02*X95578Y184750D01*
+X99630Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X101131Y188500D02*Y183250D01*
+X101881Y182500D01*
+X103383Y188500D02*Y183250D01*
+X104133Y182500D01*
+X109084D02*X111334D01*
+X112084Y183250D01*
+X111334Y184000D02*X112084Y183250D01*
+X109084Y184000D02*X111334D01*
+X108334Y184750D02*X109084Y184000D01*
+X108334Y184750D02*X109084Y185500D01*
+X111334D01*
+X112084Y184750D01*
+X108334Y183250D02*X109084Y182500D01*
+X113886Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X115387Y185500D02*X118387D01*
+X115387Y182500D02*X118387Y185500D01*
+X115387Y182500D02*X118387D01*
+X120939D02*X123189D01*
+X120189Y183250D02*X120939Y182500D01*
+X120189Y184750D02*Y183250D01*
+Y184750D02*X120939Y185500D01*
+X122439D01*
+X123189Y184750D01*
+X120189Y184000D02*X123189D01*
+Y184750D02*Y184000D01*
+X125740Y182500D02*X127990D01*
+X128740Y183250D01*
+X127990Y184000D02*X128740Y183250D01*
+X125740Y184000D02*X127990D01*
+X124990Y184750D02*X125740Y184000D01*
+X124990Y184750D02*X125740Y185500D01*
+X127990D01*
+X128740Y184750D01*
+X124990Y183250D02*X125740Y182500D01*
+X133242Y185500D02*Y183250D01*
+X133992Y182500D01*
+X135492D01*
+X136242Y183250D01*
+Y185500D02*Y183250D01*
+X138793Y182500D02*X141043D01*
+X141793Y183250D01*
+X141043Y184000D02*X141793Y183250D01*
+X138793Y184000D02*X141043D01*
+X138043Y184750D02*X138793Y184000D01*
+X138043Y184750D02*X138793Y185500D01*
+X141043D01*
+X141793Y184750D01*
+X138043Y183250D02*X138793Y182500D01*
+X144345D02*X146595D01*
+X143595Y183250D02*X144345Y182500D01*
+X143595Y184750D02*Y183250D01*
+Y184750D02*X144345Y185500D01*
+X145845D01*
+X146595Y184750D01*
+X143595Y184000D02*X146595D01*
+Y184750D02*Y184000D01*
+X151396Y188500D02*Y182500D01*
+X150646D02*X151396Y183250D01*
+X149146Y182500D02*X150646D01*
+X148396Y183250D02*X149146Y182500D01*
+X148396Y184750D02*Y183250D01*
+Y184750D02*X149146Y185500D01*
+X150646D01*
+X151396Y184750D01*
+X155898Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X158149Y184750D02*Y182500D01*
+Y184750D02*X158899Y185500D01*
+X159649D01*
+X160399Y184750D01*
+Y182500D01*
+X157399Y185500D02*X158149Y184750D01*
+X165651Y188500D02*Y183250D01*
+X166401Y182500D01*
+X164901Y186250D02*X166401D01*
+X167902Y188500D02*Y182500D01*
+Y184750D02*X168652Y185500D01*
+X170152D01*
+X170902Y184750D01*
+Y182500D01*
+X172704Y187000D02*Y186250D01*
+Y184750D02*Y182500D01*
+X174955D02*X177205D01*
+X177955Y183250D01*
+X177205Y184000D02*X177955Y183250D01*
+X174955Y184000D02*X177205D01*
+X174205Y184750D02*X174955Y184000D01*
+X174205Y184750D02*X174955Y185500D01*
+X177205D01*
+X177955Y184750D01*
+X174205Y183250D02*X174955Y182500D01*
+X182457Y188500D02*Y183250D01*
+X183207Y182500D01*
+X186958Y185500D02*X187708Y184750D01*
+X185458Y185500D02*X186958D01*
+X184708Y184750D02*X185458Y185500D01*
+X184708Y184750D02*Y183250D01*
+X185458Y182500D01*
+X187708Y185500D02*Y183250D01*
+X188458Y182500D01*
+X185458D02*X186958D01*
+X187708Y183250D01*
+X190260Y185500D02*Y183250D01*
+X191010Y182500D01*
+X193260Y185500D02*Y181000D01*
+X192510Y180250D02*X193260Y181000D01*
+X191010Y180250D02*X192510D01*
+X190260Y181000D02*X191010Y180250D01*
+Y182500D02*X192510D01*
+X193260Y183250D01*
+X195061Y184750D02*Y183250D01*
+Y184750D02*X195811Y185500D01*
+X197311D01*
+X198061Y184750D01*
+Y183250D01*
+X197311Y182500D02*X198061Y183250D01*
+X195811Y182500D02*X197311D01*
+X195061Y183250D02*X195811Y182500D01*
+X199863Y185500D02*Y183250D01*
+X200613Y182500D01*
+X202113D01*
+X202863Y183250D01*
+Y185500D02*Y183250D01*
+X205414Y188500D02*Y183250D01*
+X206164Y182500D01*
+X204664Y186250D02*X206164D01*
+X207666Y181000D02*X209166Y182500D01*
+X214417D02*X215917D01*
+X215167Y188500D02*Y182500D01*
+X213667Y187000D02*X215167Y188500D01*
+X220419D02*Y182500D01*
+Y184750D02*X221169Y185500D01*
+X222669D01*
+X223419Y184750D01*
+Y182500D01*
+X225220Y184750D02*Y183250D01*
+Y184750D02*X225970Y185500D01*
+X227470D01*
+X228220Y184750D01*
+Y183250D01*
+X227470Y182500D02*X228220Y183250D01*
+X225970Y182500D02*X227470D01*
+X225220Y183250D02*X225970Y182500D01*
+X230022Y188500D02*Y183250D01*
+X230772Y182500D01*
+X233023D02*X235273D01*
+X232273Y183250D02*X233023Y182500D01*
+X232273Y184750D02*Y183250D01*
+Y184750D02*X233023Y185500D01*
+X234523D01*
+X235273Y184750D01*
+X232273Y184000D02*X235273D01*
+Y184750D02*Y184000D01*
+X237825Y182500D02*X240075D01*
+X240825Y183250D01*
+X240075Y184000D02*X240825Y183250D01*
+X237825Y184000D02*X240075D01*
+X237075Y184750D02*X237825Y184000D01*
+X237075Y184750D02*X237825Y185500D01*
+X240075D01*
+X240825Y184750D01*
+X237075Y183250D02*X237825Y182500D01*
+X246076Y188500D02*Y183250D01*
+X246826Y182500D01*
+X245326Y186250D02*X246826D01*
+X248328Y184750D02*Y183250D01*
+Y184750D02*X249078Y185500D01*
+X250578D01*
+X251328Y184750D01*
+Y183250D01*
+X250578Y182500D02*X251328Y183250D01*
+X249078Y182500D02*X250578D01*
+X248328Y183250D02*X249078Y182500D01*
+X253879Y188500D02*Y183250D01*
+X254629Y182500D01*
+X253129Y186250D02*X254629D01*
+X258381Y185500D02*X259131Y184750D01*
+X256881Y185500D02*X258381D01*
+X256131Y184750D02*X256881Y185500D01*
+X256131Y184750D02*Y183250D01*
+X256881Y182500D01*
+X259131Y185500D02*Y183250D01*
+X259881Y182500D01*
+X256881D02*X258381D01*
+X259131Y183250D01*
+X261682Y188500D02*Y183250D01*
+X262432Y182500D01*
+G54D16*X0Y100000D02*X200000D01*
+X0D02*Y0D01*
+X200000Y100000D02*Y0D01*
+X0D02*X200000D01*
+G54D15*Y113500D02*Y107500D01*
+Y113500D02*X202250Y111250D01*
+X204500Y113500D01*
+Y107500D01*
+X208551Y110500D02*X209301Y109750D01*
+X207051Y110500D02*X208551D01*
+X206301Y109750D02*X207051Y110500D01*
+X206301Y109750D02*Y108250D01*
+X207051Y107500D01*
+X209301Y110500D02*Y108250D01*
+X210051Y107500D01*
+X207051D02*X208551D01*
+X209301Y108250D01*
+X211853Y110500D02*X214853Y107500D01*
+X211853D02*X214853Y110500D01*
+X216654Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X218906Y109750D02*Y107500D01*
+Y109750D02*X219656Y110500D01*
+X220406D01*
+X221156Y109750D01*
+Y107500D01*
+Y109750D02*X221906Y110500D01*
+X222656D01*
+X223406Y109750D01*
+Y107500D01*
+X218156Y110500D02*X218906Y109750D01*
+X225207Y110500D02*Y108250D01*
+X225957Y107500D01*
+X227457D01*
+X228207Y108250D01*
+Y110500D02*Y108250D01*
+X230759Y109750D02*Y107500D01*
+Y109750D02*X231509Y110500D01*
+X232259D01*
+X233009Y109750D01*
+Y107500D01*
+Y109750D02*X233759Y110500D01*
+X234509D01*
+X235259Y109750D01*
+Y107500D01*
+X230009Y110500D02*X230759Y109750D01*
+X240510Y113500D02*Y107500D01*
+X242760Y113500D02*X243510Y112750D01*
+Y108250D01*
+X242760Y107500D02*X243510Y108250D01*
+X239760Y107500D02*X242760D01*
+X239760Y113500D02*X242760D01*
+X245312Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X247563Y109750D02*Y107500D01*
+Y109750D02*X248313Y110500D01*
+X249063D01*
+X249813Y109750D01*
+Y107500D01*
+Y109750D02*X250563Y110500D01*
+X251313D01*
+X252063Y109750D01*
+Y107500D01*
+X246813Y110500D02*X247563Y109750D01*
+X254615Y107500D02*X256865D01*
+X253865Y108250D02*X254615Y107500D01*
+X253865Y109750D02*Y108250D01*
+Y109750D02*X254615Y110500D01*
+X256115D01*
+X256865Y109750D01*
+X253865Y109000D02*X256865D01*
+Y109750D02*Y109000D01*
+X259416Y109750D02*Y107500D01*
+Y109750D02*X260166Y110500D01*
+X260916D01*
+X261666Y109750D01*
+Y107500D01*
+X258666Y110500D02*X259416Y109750D01*
+X264218Y107500D02*X266468D01*
+X267218Y108250D01*
+X266468Y109000D02*X267218Y108250D01*
+X264218Y109000D02*X266468D01*
+X263468Y109750D02*X264218Y109000D01*
+X263468Y109750D02*X264218Y110500D01*
+X266468D01*
+X267218Y109750D01*
+X263468Y108250D02*X264218Y107500D01*
+X269019Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X270521Y109750D02*Y108250D01*
+Y109750D02*X271271Y110500D01*
+X272771D01*
+X273521Y109750D01*
+Y108250D01*
+X272771Y107500D02*X273521Y108250D01*
+X271271Y107500D02*X272771D01*
+X270521Y108250D02*X271271Y107500D01*
+X276072Y109750D02*Y107500D01*
+Y109750D02*X276822Y110500D01*
+X277572D01*
+X278322Y109750D01*
+Y107500D01*
+X275322Y110500D02*X276072Y109750D01*
+X280874Y107500D02*X283124D01*
+X283874Y108250D01*
+X283124Y109000D02*X283874Y108250D01*
+X280874Y109000D02*X283124D01*
+X280124Y109750D02*X280874Y109000D01*
+X280124Y109750D02*X280874Y110500D01*
+X283124D01*
+X283874Y109750D01*
+X280124Y108250D02*X280874Y107500D01*
+X285675Y111250D02*X286425D01*
+X285675Y109750D02*X286425D01*
+X290927Y112750D02*X291677Y113500D01*
+X293927D01*
+X294677Y112750D01*
+Y111250D01*
+X290927Y107500D02*X294677Y111250D01*
+X290927Y107500D02*X294677D01*
+X296478Y108250D02*X297228Y107500D01*
+X296478Y112750D02*Y108250D01*
+Y112750D02*X297228Y113500D01*
+X298728D01*
+X299478Y112750D01*
+Y108250D01*
+X298728Y107500D02*X299478Y108250D01*
+X297228Y107500D02*X298728D01*
+X296478Y109000D02*X299478Y112000D01*
+X301280Y108250D02*X302030Y107500D01*
+X301280Y112750D02*Y108250D01*
+Y112750D02*X302030Y113500D01*
+X303530D01*
+X304280Y112750D01*
+Y108250D01*
+X303530Y107500D02*X304280Y108250D01*
+X302030Y107500D02*X303530D01*
+X301280Y109000D02*X304280Y112000D01*
+X306081Y108250D02*X306831Y107500D01*
+X306081Y112750D02*Y108250D01*
+Y112750D02*X306831Y113500D01*
+X308331D01*
+X309081Y112750D01*
+Y108250D01*
+X308331Y107500D02*X309081Y108250D01*
+X306831Y107500D02*X308331D01*
+X306081Y109000D02*X309081Y112000D01*
+X314333Y109750D02*Y107500D01*
+Y109750D02*X315083Y110500D01*
+X315833D01*
+X316583Y109750D01*
+Y107500D01*
+Y109750D02*X317333Y110500D01*
+X318083D01*
+X318833Y109750D01*
+Y107500D01*
+X313583Y110500D02*X314333Y109750D01*
+X320634Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X322136Y113500D02*Y108250D01*
+X322886Y107500D01*
+X325137D02*X327387D01*
+X328137Y108250D01*
+X327387Y109000D02*X328137Y108250D01*
+X325137Y109000D02*X327387D01*
+X324387Y109750D02*X325137Y109000D01*
+X324387Y109750D02*X325137Y110500D01*
+X327387D01*
+X328137Y109750D01*
+X324387Y108250D02*X325137Y107500D01*
+X332639Y110500D02*Y108250D01*
+X333389Y107500D01*
+X334139D01*
+X334889Y108250D01*
+Y110500D02*Y108250D01*
+X335639Y107500D01*
+X336389D01*
+X337139Y108250D01*
+Y110500D02*Y108250D01*
+X338940Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X343442Y113500D02*Y107500D01*
+X342692D02*X343442Y108250D01*
+X341192Y107500D02*X342692D01*
+X340442Y108250D02*X341192Y107500D01*
+X340442Y109750D02*Y108250D01*
+Y109750D02*X341192Y110500D01*
+X342692D01*
+X343442Y109750D01*
+X345993Y107500D02*X348243D01*
+X345243Y108250D02*X345993Y107500D01*
+X345243Y109750D02*Y108250D01*
+Y109750D02*X345993Y110500D01*
+X347493D01*
+X348243Y109750D01*
+X345243Y109000D02*X348243D01*
+Y109750D02*Y109000D01*
+X350045Y106000D02*X351545Y107500D01*
+X356796D02*X358296D01*
+X357546Y113500D02*Y107500D01*
+X356046Y112000D02*X357546Y113500D01*
+X360098Y108250D02*X360848Y107500D01*
+X360098Y112750D02*Y108250D01*
+Y112750D02*X360848Y113500D01*
+X362348D01*
+X363098Y112750D01*
+Y108250D01*
+X362348Y107500D02*X363098Y108250D01*
+X360848Y107500D02*X362348D01*
+X360098Y109000D02*X363098Y112000D01*
+X364899Y108250D02*X365649Y107500D01*
+X364899Y112750D02*Y108250D01*
+Y112750D02*X365649Y113500D01*
+X367149D01*
+X367899Y112750D01*
+Y108250D01*
+X367149Y107500D02*X367899Y108250D01*
+X365649Y107500D02*X367149D01*
+X364899Y109000D02*X367899Y112000D01*
+X369701Y108250D02*X370451Y107500D01*
+X369701Y112750D02*Y108250D01*
+Y112750D02*X370451Y113500D01*
+X371951D01*
+X372701Y112750D01*
+Y108250D01*
+X371951Y107500D02*X372701Y108250D01*
+X370451Y107500D02*X371951D01*
+X369701Y109000D02*X372701Y112000D01*
+X377952Y109750D02*Y107500D01*
+Y109750D02*X378702Y110500D01*
+X379452D01*
+X380202Y109750D01*
+Y107500D01*
+Y109750D02*X380952Y110500D01*
+X381702D01*
+X382452Y109750D01*
+Y107500D01*
+X377202Y110500D02*X377952Y109750D01*
+X384254Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X385755Y113500D02*Y108250D01*
+X386505Y107500D01*
+X388757D02*X391007D01*
+X391757Y108250D01*
+X391007Y109000D02*X391757Y108250D01*
+X388757Y109000D02*X391007D01*
+X388007Y109750D02*X388757Y109000D01*
+X388007Y109750D02*X388757Y110500D01*
+X391007D01*
+X391757Y109750D01*
+X388007Y108250D02*X388757Y107500D01*
+X396258Y113500D02*Y107500D01*
+Y109750D02*X397008Y110500D01*
+X398508D01*
+X399258Y109750D01*
+Y107500D01*
+X401060Y112000D02*Y111250D01*
+Y109750D02*Y107500D01*
+X404811Y110500D02*X405561Y109750D01*
+X403311Y110500D02*X404811D01*
+X402561Y109750D02*X403311Y110500D01*
+X402561Y109750D02*Y108250D01*
+X403311Y107500D01*
+X404811D01*
+X405561Y108250D01*
+X402561Y106000D02*X403311Y105250D01*
+X404811D01*
+X405561Y106000D01*
+Y110500D02*Y106000D01*
+X407363Y113500D02*Y107500D01*
+Y109750D02*X408113Y110500D01*
+X409613D01*
+X410363Y109750D01*
+Y107500D01*
+X0Y-9500D02*X3000D01*
+X3750Y-8750D01*
+Y-7250D02*Y-8750D01*
+X3000Y-6500D02*X3750Y-7250D01*
+X750Y-6500D02*X3000D01*
+X750Y-3500D02*Y-9500D01*
+X0Y-3500D02*X3000D01*
+X3750Y-4250D01*
+Y-5750D01*
+X3000Y-6500D02*X3750Y-5750D01*
+X5551Y-7250D02*Y-8750D01*
+Y-7250D02*X6301Y-6500D01*
+X7801D01*
+X8551Y-7250D01*
+Y-8750D01*
+X7801Y-9500D02*X8551Y-8750D01*
+X6301Y-9500D02*X7801D01*
+X5551Y-8750D02*X6301Y-9500D01*
+X12603Y-6500D02*X13353Y-7250D01*
+X11103Y-6500D02*X12603D01*
+X10353Y-7250D02*X11103Y-6500D01*
+X10353Y-7250D02*Y-8750D01*
+X11103Y-9500D01*
+X13353Y-6500D02*Y-8750D01*
+X14103Y-9500D01*
+X11103D02*X12603D01*
+X13353Y-8750D01*
+X16654Y-7250D02*Y-9500D01*
+Y-7250D02*X17404Y-6500D01*
+X18904D01*
+X15904D02*X16654Y-7250D01*
+X23706Y-3500D02*Y-9500D01*
+X22956D02*X23706Y-8750D01*
+X21456Y-9500D02*X22956D01*
+X20706Y-8750D02*X21456Y-9500D01*
+X20706Y-7250D02*Y-8750D01*
+Y-7250D02*X21456Y-6500D01*
+X22956D01*
+X23706Y-7250D01*
+X28207D02*Y-8750D01*
+Y-7250D02*X28957Y-6500D01*
+X30457D01*
+X31207Y-7250D01*
+Y-8750D01*
+X30457Y-9500D02*X31207Y-8750D01*
+X28957Y-9500D02*X30457D01*
+X28207Y-8750D02*X28957Y-9500D01*
+X33009Y-6500D02*Y-8750D01*
+X33759Y-9500D01*
+X35259D01*
+X36009Y-8750D01*
+Y-6500D02*Y-8750D01*
+X38560Y-3500D02*Y-8750D01*
+X39310Y-9500D01*
+X37810Y-5750D02*X39310D01*
+X40812Y-3500D02*Y-8750D01*
+X41562Y-9500D01*
+X43063Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X45315Y-7250D02*Y-9500D01*
+Y-7250D02*X46065Y-6500D01*
+X46815D01*
+X47565Y-7250D01*
+Y-9500D01*
+X44565Y-6500D02*X45315Y-7250D01*
+X50116Y-9500D02*X52366D01*
+X49366Y-8750D02*X50116Y-9500D01*
+X49366Y-7250D02*Y-8750D01*
+Y-7250D02*X50116Y-6500D01*
+X51616D01*
+X52366Y-7250D01*
+X49366Y-8000D02*X52366D01*
+Y-7250D02*Y-8000D01*
+X56868Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X59119D02*X61369D01*
+X62119Y-8750D01*
+X61369Y-8000D02*X62119Y-8750D01*
+X59119Y-8000D02*X61369D01*
+X58369Y-7250D02*X59119Y-8000D01*
+X58369Y-7250D02*X59119Y-6500D01*
+X61369D01*
+X62119Y-7250D01*
+X58369Y-8750D02*X59119Y-9500D01*
+X67371Y-3500D02*Y-8750D01*
+X68121Y-9500D01*
+X66621Y-5750D02*X68121D01*
+X69622Y-3500D02*Y-9500D01*
+Y-7250D02*X70372Y-6500D01*
+X71872D01*
+X72622Y-7250D01*
+Y-9500D01*
+X75174D02*X77424D01*
+X74424Y-8750D02*X75174Y-9500D01*
+X74424Y-7250D02*Y-8750D01*
+Y-7250D02*X75174Y-6500D01*
+X76674D01*
+X77424Y-7250D01*
+X74424Y-8000D02*X77424D01*
+Y-7250D02*Y-8000D01*
+X82675Y-6500D02*X84925D01*
+X81925Y-7250D02*X82675Y-6500D01*
+X81925Y-7250D02*Y-8750D01*
+X82675Y-9500D01*
+X84925D01*
+X87477D02*X89727D01*
+X86727Y-8750D02*X87477Y-9500D01*
+X86727Y-7250D02*Y-8750D01*
+Y-7250D02*X87477Y-6500D01*
+X88977D01*
+X89727Y-7250D01*
+X86727Y-8000D02*X89727D01*
+Y-7250D02*Y-8000D01*
+X92278Y-7250D02*Y-9500D01*
+Y-7250D02*X93028Y-6500D01*
+X93778D01*
+X94528Y-7250D01*
+Y-9500D01*
+X91528Y-6500D02*X92278Y-7250D01*
+X97080Y-3500D02*Y-8750D01*
+X97830Y-9500D01*
+X96330Y-5750D02*X97830D01*
+X100081Y-9500D02*X102331D01*
+X99331Y-8750D02*X100081Y-9500D01*
+X99331Y-7250D02*Y-8750D01*
+Y-7250D02*X100081Y-6500D01*
+X101581D01*
+X102331Y-7250D01*
+X99331Y-8000D02*X102331D01*
+Y-7250D02*Y-8000D01*
+X104883Y-7250D02*Y-9500D01*
+Y-7250D02*X105633Y-6500D01*
+X107133D01*
+X104133D02*X104883Y-7250D01*
+X108934Y-3500D02*Y-8750D01*
+X109684Y-9500D01*
+X111186Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X113437Y-7250D02*Y-9500D01*
+Y-7250D02*X114187Y-6500D01*
+X114937D01*
+X115687Y-7250D01*
+Y-9500D01*
+X112687Y-6500D02*X113437Y-7250D01*
+X118239Y-9500D02*X120489D01*
+X117489Y-8750D02*X118239Y-9500D01*
+X117489Y-7250D02*Y-8750D01*
+Y-7250D02*X118239Y-6500D01*
+X119739D01*
+X120489Y-7250D01*
+X117489Y-8000D02*X120489D01*
+Y-7250D02*Y-8000D01*
+X124990Y-7250D02*Y-8750D01*
+Y-7250D02*X125740Y-6500D01*
+X127240D01*
+X127990Y-7250D01*
+Y-8750D01*
+X127240Y-9500D02*X127990Y-8750D01*
+X125740Y-9500D02*X127240D01*
+X124990Y-8750D02*X125740Y-9500D01*
+X130542Y-4250D02*Y-9500D01*
+Y-4250D02*X131292Y-3500D01*
+X132042D01*
+X129792Y-6500D02*X131292D01*
+X136993Y-3500D02*Y-8750D01*
+X137743Y-9500D01*
+X136243Y-5750D02*X137743D01*
+X139245Y-3500D02*Y-9500D01*
+Y-7250D02*X139995Y-6500D01*
+X141495D01*
+X142245Y-7250D01*
+Y-9500D01*
+X144046Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X146298D02*X148548D01*
+X149298Y-8750D01*
+X148548Y-8000D02*X149298Y-8750D01*
+X146298Y-8000D02*X148548D01*
+X145548Y-7250D02*X146298Y-8000D01*
+X145548Y-7250D02*X146298Y-6500D01*
+X148548D01*
+X149298Y-7250D01*
+X145548Y-8750D02*X146298Y-9500D01*
+X154549D02*X156049D01*
+X155299Y-3500D02*Y-9500D01*
+X153799Y-5000D02*X155299Y-3500D01*
+X157851Y-8750D02*X158601Y-9500D01*
+X157851Y-4250D02*Y-8750D01*
+Y-4250D02*X158601Y-3500D01*
+X160101D01*
+X160851Y-4250D01*
+Y-8750D01*
+X160101Y-9500D02*X160851Y-8750D01*
+X158601Y-9500D02*X160101D01*
+X157851Y-8000D02*X160851Y-5000D01*
+X166102Y-7250D02*Y-9500D01*
+Y-7250D02*X166852Y-6500D01*
+X167602D01*
+X168352Y-7250D01*
+Y-9500D01*
+Y-7250D02*X169102Y-6500D01*
+X169852D01*
+X170602Y-7250D01*
+Y-9500D01*
+X165352Y-6500D02*X166102Y-7250D01*
+X172404Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X173905Y-3500D02*Y-8750D01*
+X174655Y-9500D01*
+X179607Y-7250D02*Y-9500D01*
+Y-7250D02*X180357Y-6500D01*
+X181857D01*
+X178857D02*X179607Y-7250D01*
+X184408Y-9500D02*X186658D01*
+X183658Y-8750D02*X184408Y-9500D01*
+X183658Y-7250D02*Y-8750D01*
+Y-7250D02*X184408Y-6500D01*
+X185908D01*
+X186658Y-7250D01*
+X183658Y-8000D02*X186658D01*
+Y-7250D02*Y-8000D01*
+X189210Y-6500D02*X191460D01*
+X188460Y-7250D02*X189210Y-6500D01*
+X188460Y-7250D02*Y-8750D01*
+X189210Y-9500D01*
+X191460D01*
+X194011Y-3500D02*Y-8750D01*
+X194761Y-9500D01*
+X193261Y-5750D02*X194761D01*
+X198513Y-6500D02*X199263Y-7250D01*
+X197013Y-6500D02*X198513D01*
+X196263Y-7250D02*X197013Y-6500D01*
+X196263Y-7250D02*Y-8750D01*
+X197013Y-9500D01*
+X199263Y-6500D02*Y-8750D01*
+X200013Y-9500D01*
+X197013D02*X198513D01*
+X199263Y-8750D01*
+X202564Y-7250D02*Y-9500D01*
+Y-7250D02*X203314Y-6500D01*
+X204064D01*
+X204814Y-7250D01*
+Y-9500D01*
+X201814Y-6500D02*X202564Y-7250D01*
+X208866Y-6500D02*X209616Y-7250D01*
+X207366Y-6500D02*X208866D01*
+X206616Y-7250D02*X207366Y-6500D01*
+X206616Y-7250D02*Y-8750D01*
+X207366Y-9500D01*
+X208866D01*
+X209616Y-8750D01*
+X206616Y-11000D02*X207366Y-11750D01*
+X208866D01*
+X209616Y-11000D01*
+Y-6500D02*Y-11000D01*
+X211417Y-3500D02*Y-8750D01*
+X212167Y-9500D01*
+X214419D02*X216669D01*
+X213669Y-8750D02*X214419Y-9500D01*
+X213669Y-7250D02*Y-8750D01*
+Y-7250D02*X214419Y-6500D01*
+X215919D01*
+X216669Y-7250D01*
+X213669Y-8000D02*X216669D01*
+Y-7250D02*Y-8000D01*
+X221170Y-6500D02*X224170D01*
+X228672Y-8750D02*X229422Y-9500D01*
+X228672Y-4250D02*Y-8750D01*
+Y-4250D02*X229422Y-3500D01*
+X230922D01*
+X231672Y-4250D01*
+Y-8750D01*
+X230922Y-9500D02*X231672Y-8750D01*
+X229422Y-9500D02*X230922D01*
+X228672Y-8000D02*X231672Y-5000D01*
+X233473Y-11000D02*X234973Y-9500D01*
+X236775Y-8750D02*X237525Y-9500D01*
+X236775Y-4250D02*Y-8750D01*
+Y-4250D02*X237525Y-3500D01*
+X239025D01*
+X239775Y-4250D01*
+Y-8750D01*
+X239025Y-9500D02*X239775Y-8750D01*
+X237525Y-9500D02*X239025D01*
+X236775Y-8000D02*X239775Y-5000D01*
+X245026Y-3500D02*Y-8750D01*
+X245776Y-9500D01*
+X244276Y-5750D02*X245776D01*
+X247278Y-7250D02*Y-8750D01*
+Y-7250D02*X248028Y-6500D01*
+X249528D01*
+X250278Y-7250D01*
+Y-8750D01*
+X249528Y-9500D02*X250278Y-8750D01*
+X248028Y-9500D02*X249528D01*
+X247278Y-8750D02*X248028Y-9500D01*
+X254779Y-4250D02*X255529Y-3500D01*
+X257779D01*
+X258529Y-4250D01*
+Y-5750D01*
+X254779Y-9500D02*X258529Y-5750D01*
+X254779Y-9500D02*X258529D01*
+X260331Y-8750D02*X261081Y-9500D01*
+X260331Y-4250D02*Y-8750D01*
+Y-4250D02*X261081Y-3500D01*
+X262581D01*
+X263331Y-4250D01*
+Y-8750D01*
+X262581Y-9500D02*X263331Y-8750D01*
+X261081Y-9500D02*X262581D01*
+X260331Y-8000D02*X263331Y-5000D01*
+X265132Y-8750D02*X265882Y-9500D01*
+X265132Y-4250D02*Y-8750D01*
+Y-4250D02*X265882Y-3500D01*
+X267382D01*
+X268132Y-4250D01*
+Y-8750D01*
+X267382Y-9500D02*X268132Y-8750D01*
+X265882Y-9500D02*X267382D01*
+X265132Y-8000D02*X268132Y-5000D01*
+X269934Y-8750D02*X270684Y-9500D01*
+X269934Y-4250D02*Y-8750D01*
+Y-4250D02*X270684Y-3500D01*
+X272184D01*
+X272934Y-4250D01*
+Y-8750D01*
+X272184Y-9500D02*X272934Y-8750D01*
+X270684Y-9500D02*X272184D01*
+X269934Y-8000D02*X272934Y-5000D01*
+X274735Y-11000D02*X276235Y-9500D01*
+X278787D02*X280287D01*
+X279537Y-3500D02*Y-9500D01*
+X278037Y-5000D02*X279537Y-3500D01*
+X282088Y-8750D02*X282838Y-9500D01*
+X282088Y-4250D02*Y-8750D01*
+Y-4250D02*X282838Y-3500D01*
+X284338D01*
+X285088Y-4250D01*
+Y-8750D01*
+X284338Y-9500D02*X285088Y-8750D01*
+X282838Y-9500D02*X284338D01*
+X282088Y-8000D02*X285088Y-5000D01*
+X286890Y-8750D02*X287640Y-9500D01*
+X286890Y-4250D02*Y-8750D01*
+Y-4250D02*X287640Y-3500D01*
+X289140D01*
+X289890Y-4250D01*
+Y-8750D01*
+X289140Y-9500D02*X289890Y-8750D01*
+X287640Y-9500D02*X289140D01*
+X286890Y-8000D02*X289890Y-5000D01*
+X291691Y-8750D02*X292441Y-9500D01*
+X291691Y-4250D02*Y-8750D01*
+Y-4250D02*X292441Y-3500D01*
+X293941D01*
+X294691Y-4250D01*
+Y-8750D01*
+X293941Y-9500D02*X294691Y-8750D01*
+X292441Y-9500D02*X293941D01*
+X291691Y-8000D02*X294691Y-5000D01*
+X299943Y-7250D02*Y-9500D01*
+Y-7250D02*X300693Y-6500D01*
+X301443D01*
+X302193Y-7250D01*
+Y-9500D01*
+Y-7250D02*X302943Y-6500D01*
+X303693D01*
+X304443Y-7250D01*
+Y-9500D01*
+X299193Y-6500D02*X299943Y-7250D01*
+X306244Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X307746Y-3500D02*Y-8750D01*
+X308496Y-9500D01*
+X310747D02*X312997D01*
+X313747Y-8750D01*
+X312997Y-8000D02*X313747Y-8750D01*
+X310747Y-8000D02*X312997D01*
+X309997Y-7250D02*X310747Y-8000D01*
+X309997Y-7250D02*X310747Y-6500D01*
+X312997D01*
+X313747Y-7250D01*
+X309997Y-8750D02*X310747Y-9500D01*
+X200750Y128500D02*Y122500D01*
+X203000Y128500D02*X203750Y127750D01*
+Y123250D01*
+X203000Y122500D02*X203750Y123250D01*
+X200000Y122500D02*X203000D01*
+X200000Y128500D02*X203000D01*
+X207801Y125500D02*X208551Y124750D01*
+X206301Y125500D02*X207801D01*
+X205551Y124750D02*X206301Y125500D01*
+X205551Y124750D02*Y123250D01*
+X206301Y122500D01*
+X208551Y125500D02*Y123250D01*
+X209301Y122500D01*
+X206301D02*X207801D01*
+X208551Y123250D01*
+X211853Y128500D02*Y123250D01*
+X212603Y122500D01*
+X211103Y126250D02*X212603D01*
+X214854Y122500D02*X217104D01*
+X214104Y123250D02*X214854Y122500D01*
+X214104Y124750D02*Y123250D01*
+Y124750D02*X214854Y125500D01*
+X216354D01*
+X217104Y124750D01*
+X214104Y124000D02*X217104D01*
+Y124750D02*Y124000D01*
+X218906Y126250D02*X219656D01*
+X218906Y124750D02*X219656D01*
+X224157Y128500D02*Y122500D01*
+X226407Y124750D01*
+X228657Y122500D01*
+Y128500D02*Y122500D01*
+X231209D02*X233459D01*
+X230459Y123250D02*X231209Y122500D01*
+X230459Y124750D02*Y123250D01*
+Y124750D02*X231209Y125500D01*
+X232709D01*
+X233459Y124750D01*
+X230459Y124000D02*X233459D01*
+Y124750D02*Y124000D01*
+X238260Y128500D02*Y122500D01*
+X237510D02*X238260Y123250D01*
+X236010Y122500D02*X237510D01*
+X235260Y123250D02*X236010Y122500D01*
+X235260Y124750D02*Y123250D01*
+Y124750D02*X236010Y125500D01*
+X237510D01*
+X238260Y124750D01*
+X242762Y128500D02*X245012D01*
+Y123250D01*
+X244262Y122500D02*X245012Y123250D01*
+X243512Y122500D02*X244262D01*
+X242762Y123250D02*X243512Y122500D01*
+X246813Y125500D02*Y123250D01*
+X247563Y122500D01*
+X249063D01*
+X249813Y123250D01*
+Y125500D02*Y123250D01*
+X252365Y124750D02*Y122500D01*
+Y124750D02*X253115Y125500D01*
+X253865D01*
+X254615Y124750D01*
+Y122500D01*
+X251615Y125500D02*X252365Y124750D01*
+X259116Y127750D02*X259866Y128500D01*
+X262116D01*
+X262866Y127750D01*
+Y126250D01*
+X259116Y122500D02*X262866Y126250D01*
+X259116Y122500D02*X262866D01*
+X264668Y125500D02*X267668Y128500D01*
+X264668Y125500D02*X268418D01*
+X267668Y128500D02*Y122500D01*
+X272919Y127750D02*X273669Y128500D01*
+X275919D01*
+X276669Y127750D01*
+Y126250D01*
+X272919Y122500D02*X276669Y126250D01*
+X272919Y122500D02*X276669D01*
+X278471Y127750D02*X279221Y128500D01*
+X281471D01*
+X282221Y127750D01*
+Y126250D01*
+X278471Y122500D02*X282221Y126250D01*
+X278471Y122500D02*X282221D01*
+X284022Y126250D02*X284772D01*
+X284022Y124750D02*X284772D01*
+X286574Y123250D02*X287324Y122500D01*
+X286574Y127750D02*Y123250D01*
+Y127750D02*X287324Y128500D01*
+X288824D01*
+X289574Y127750D01*
+Y123250D01*
+X288824Y122500D02*X289574Y123250D01*
+X287324Y122500D02*X288824D01*
+X286574Y124000D02*X289574Y127000D01*
+X291375Y128500D02*X294375D01*
+X291375D02*Y125500D01*
+X292125Y126250D01*
+X293625D01*
+X294375Y125500D01*
+Y123250D01*
+X293625Y122500D02*X294375Y123250D01*
+X292125Y122500D02*X293625D01*
+X291375Y123250D02*X292125Y122500D01*
+X296177Y126250D02*X296927D01*
+X296177Y124750D02*X296927D01*
+X298728Y127750D02*X299478Y128500D01*
+X300978D01*
+X301728Y127750D01*
+Y123250D01*
+X300978Y122500D02*X301728Y123250D01*
+X299478Y122500D02*X300978D01*
+X298728Y123250D02*X299478Y122500D01*
+Y125500D02*X301728D01*
+X303530Y128500D02*X306530D01*
+X303530D02*Y125500D01*
+X304280Y126250D01*
+X305780D01*
+X306530Y125500D01*
+Y123250D01*
+X305780Y122500D02*X306530Y123250D01*
+X304280Y122500D02*X305780D01*
+X303530Y123250D02*X304280Y122500D01*
+X311031Y127750D02*X311781Y128500D01*
+X314031D01*
+X314781Y127750D01*
+Y126250D01*
+X311031Y122500D02*X314781Y126250D01*
+X311031Y122500D02*X314781D01*
+X316583Y123250D02*X317333Y122500D01*
+X316583Y127750D02*Y123250D01*
+Y127750D02*X317333Y128500D01*
+X318833D01*
+X319583Y127750D01*
+Y123250D01*
+X318833Y122500D02*X319583Y123250D01*
+X317333Y122500D02*X318833D01*
+X316583Y124000D02*X319583Y127000D01*
+X321384Y123250D02*X322134Y122500D01*
+X321384Y127750D02*Y123250D01*
+Y127750D02*X322134Y128500D01*
+X323634D01*
+X324384Y127750D01*
+Y123250D01*
+X323634Y122500D02*X324384Y123250D01*
+X322134Y122500D02*X323634D01*
+X321384Y124000D02*X324384Y127000D01*
+X326186Y122500D02*X329186Y125500D01*
+Y127750D02*Y125500D01*
+X328436Y128500D02*X329186Y127750D01*
+X326936Y128500D02*X328436D01*
+X326186Y127750D02*X326936Y128500D01*
+X326186Y127750D02*Y126250D01*
+X326936Y125500D01*
+X329186D01*
+X333687Y128500D02*Y123250D01*
+X334437Y122500D01*
+X335937D01*
+X336687Y123250D01*
+Y128500D02*Y123250D01*
+X338489Y128500D02*X341489D01*
+X339989D02*Y122500D01*
+X344040D02*X346290D01*
+X343290Y123250D02*X344040Y122500D01*
+X343290Y127750D02*Y123250D01*
+Y127750D02*X344040Y128500D01*
+X346290D01*
+X200000Y142750D02*Y137500D01*
+Y142750D02*X200750Y143500D01*
+X203000D01*
+X203750Y142750D01*
+Y137500D01*
+X200000Y140500D02*X203750D01*
+X205551D02*Y138250D01*
+X206301Y137500D01*
+X207801D01*
+X208551Y138250D01*
+Y140500D02*Y138250D01*
+X211103Y143500D02*Y138250D01*
+X211853Y137500D01*
+X210353Y141250D02*X211853D01*
+X213354Y143500D02*Y137500D01*
+Y139750D02*X214104Y140500D01*
+X215604D01*
+X216354Y139750D01*
+Y137500D01*
+X218156Y139750D02*Y138250D01*
+Y139750D02*X218906Y140500D01*
+X220406D01*
+X221156Y139750D01*
+Y138250D01*
+X220406Y137500D02*X221156Y138250D01*
+X218906Y137500D02*X220406D01*
+X218156Y138250D02*X218906Y137500D01*
+X223707Y139750D02*Y137500D01*
+Y139750D02*X224457Y140500D01*
+X225957D01*
+X222957D02*X223707Y139750D01*
+X227759Y141250D02*X228509D01*
+X227759Y139750D02*X228509D01*
+X233760Y143500D02*Y137500D01*
+X236010Y143500D02*X236760Y142750D01*
+Y138250D01*
+X236010Y137500D02*X236760Y138250D01*
+X233010Y137500D02*X236010D01*
+X233010Y143500D02*X236010D01*
+X240812Y140500D02*X241562Y139750D01*
+X239312Y140500D02*X240812D01*
+X238562Y139750D02*X239312Y140500D01*
+X238562Y139750D02*Y138250D01*
+X239312Y137500D01*
+X241562Y140500D02*Y138250D01*
+X242312Y137500D01*
+X239312D02*X240812D01*
+X241562Y138250D01*
+X244863Y139750D02*Y137500D01*
+Y139750D02*X245613Y140500D01*
+X246363D01*
+X247113Y139750D01*
+Y137500D01*
+X244113Y140500D02*X244863Y139750D01*
+X251615Y143500D02*Y137500D01*
+Y143500D02*X253865Y141250D01*
+X256115Y143500D01*
+Y137500D01*
+X258666Y140500D02*X260916D01*
+X257916Y139750D02*X258666Y140500D01*
+X257916Y139750D02*Y138250D01*
+X258666Y137500D01*
+X260916D01*
+X262718Y143500D02*Y137500D01*
+Y143500D02*X264968Y141250D01*
+X267218Y143500D01*
+Y137500D01*
+X271269Y140500D02*X272019Y139750D01*
+X269769Y140500D02*X271269D01*
+X269019Y139750D02*X269769Y140500D01*
+X269019Y139750D02*Y138250D01*
+X269769Y137500D01*
+X272019Y140500D02*Y138250D01*
+X272769Y137500D01*
+X269769D02*X271269D01*
+X272019Y138250D01*
+X274571Y143500D02*Y137500D01*
+Y139750D02*X275321Y140500D01*
+X276821D01*
+X277571Y139750D01*
+Y137500D01*
+X279372Y142000D02*Y141250D01*
+Y139750D02*Y137500D01*
+X280874Y143500D02*Y138250D01*
+X281624Y137500D01*
+X283125Y143500D02*Y138250D01*
+X283875Y137500D01*
+X200000Y158500D02*X203000D01*
+X201500D02*Y152500D01*
+X204801Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X207053Y158500D02*Y153250D01*
+X207803Y152500D01*
+X206303Y156250D02*X207803D01*
+X209304Y158500D02*Y153250D01*
+X210054Y152500D01*
+X212306D02*X214556D01*
+X211556Y153250D02*X212306Y152500D01*
+X211556Y154750D02*Y153250D01*
+Y154750D02*X212306Y155500D01*
+X213806D01*
+X214556Y154750D01*
+X211556Y154000D02*X214556D01*
+Y154750D02*Y154000D01*
+X216357Y156250D02*X217107D01*
+X216357Y154750D02*X217107D01*
+X221609Y152500D02*X224609D01*
+X225359Y153250D01*
+Y154750D02*Y153250D01*
+X224609Y155500D02*X225359Y154750D01*
+X222359Y155500D02*X224609D01*
+X222359Y158500D02*Y152500D01*
+X221609Y158500D02*X224609D01*
+X225359Y157750D01*
+Y156250D01*
+X224609Y155500D02*X225359Y156250D01*
+X229410Y155500D02*X230160Y154750D01*
+X227910Y155500D02*X229410D01*
+X227160Y154750D02*X227910Y155500D01*
+X227160Y154750D02*Y153250D01*
+X227910Y152500D01*
+X230160Y155500D02*Y153250D01*
+X230910Y152500D01*
+X227910D02*X229410D01*
+X230160Y153250D01*
+X233462Y152500D02*X235712D01*
+X236462Y153250D01*
+X235712Y154000D02*X236462Y153250D01*
+X233462Y154000D02*X235712D01*
+X232712Y154750D02*X233462Y154000D01*
+X232712Y154750D02*X233462Y155500D01*
+X235712D01*
+X236462Y154750D01*
+X232712Y153250D02*X233462Y152500D01*
+X238263Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X240515Y155500D02*X242765D01*
+X239765Y154750D02*X240515Y155500D01*
+X239765Y154750D02*Y153250D01*
+X240515Y152500D01*
+X242765D01*
+X250266Y158500D02*X251016Y157750D01*
+X248016Y158500D02*X250266D01*
+X247266Y157750D02*X248016Y158500D01*
+X247266Y157750D02*Y156250D01*
+X248016Y155500D01*
+X250266D01*
+X251016Y154750D01*
+Y153250D01*
+X250266Y152500D02*X251016Y153250D01*
+X248016Y152500D02*X250266D01*
+X247266Y153250D02*X248016Y152500D01*
+X252818Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X255069Y154750D02*Y152500D01*
+Y154750D02*X255819Y155500D01*
+X256569D01*
+X257319Y154750D01*
+Y152500D01*
+X254319Y155500D02*X255069Y154750D01*
+X261371Y155500D02*X262121Y154750D01*
+X259871Y155500D02*X261371D01*
+X259121Y154750D02*X259871Y155500D01*
+X259121Y154750D02*Y153250D01*
+X259871Y152500D01*
+X261371D01*
+X262121Y153250D01*
+X259121Y151000D02*X259871Y150250D01*
+X261371D01*
+X262121Y151000D01*
+Y155500D02*Y151000D01*
+X263922Y158500D02*Y153250D01*
+X264672Y152500D01*
+X266924D02*X269174D01*
+X266174Y153250D02*X266924Y152500D01*
+X266174Y154750D02*Y153250D01*
+Y154750D02*X266924Y155500D01*
+X268424D01*
+X269174Y154750D01*
+X266174Y154000D02*X269174D01*
+Y154750D02*Y154000D01*
+X273675Y158500D02*X276675D01*
+X275175D02*Y152500D01*
+X279227Y154750D02*Y152500D01*
+Y154750D02*X279977Y155500D01*
+X281477D01*
+X278477D02*X279227Y154750D01*
+X285528Y155500D02*X286278Y154750D01*
+X284028Y155500D02*X285528D01*
+X283278Y154750D02*X284028Y155500D01*
+X283278Y154750D02*Y153250D01*
+X284028Y152500D01*
+X286278Y155500D02*Y153250D01*
+X287028Y152500D01*
+X284028D02*X285528D01*
+X286278Y153250D01*
+X289580Y155500D02*X291830D01*
+X288830Y154750D02*X289580Y155500D01*
+X288830Y154750D02*Y153250D01*
+X289580Y152500D01*
+X291830D01*
+X294381D02*X296631D01*
+X293631Y153250D02*X294381Y152500D01*
+X293631Y154750D02*Y153250D01*
+Y154750D02*X294381Y155500D01*
+X295881D01*
+X296631Y154750D01*
+X293631Y154000D02*X296631D01*
+Y154750D02*Y154000D01*
+X301133Y158500D02*X304133D01*
+X304883Y157750D01*
+Y156250D01*
+X304133Y155500D02*X304883Y156250D01*
+X301883Y155500D02*X304133D01*
+X301883Y158500D02*Y152500D01*
+Y155500D02*X304883Y152500D01*
+X309684Y158500D02*X310434Y157750D01*
+X307434Y158500D02*X309684D01*
+X306684Y157750D02*X307434Y158500D01*
+X306684Y157750D02*Y156250D01*
+X307434Y155500D01*
+X309684D01*
+X310434Y154750D01*
+Y153250D01*
+X309684Y152500D02*X310434Y153250D01*
+X307434Y152500D02*X309684D01*
+X306684Y153250D02*X307434Y152500D01*
+X312236Y157750D02*X312986Y158500D01*
+X315236D01*
+X315986Y157750D01*
+Y156250D01*
+X312236Y152500D02*X315986Y156250D01*
+X312236Y152500D02*X315986D01*
+X317787D02*X321537Y156250D01*
+Y158500D02*Y156250D01*
+X317787Y158500D02*X321537D01*
+X323339Y155500D02*X326339Y158500D01*
+X323339Y155500D02*X327089D01*
+X326339Y158500D02*Y152500D01*
+X328890Y155500D02*X331890D01*
+X333692Y158500D02*Y157750D01*
+X337442Y154000D01*
+Y152500D01*
+X333692Y154000D02*Y152500D01*
+Y154000D02*X337442Y157750D01*
+Y158500D02*Y157750D01*
+X341943Y158500D02*X344943D01*
+X343443D02*Y152500D01*
+X347495D02*X349745D01*
+X346745Y153250D02*X347495Y152500D01*
+X346745Y154750D02*Y153250D01*
+Y154750D02*X347495Y155500D01*
+X348995D01*
+X349745Y154750D01*
+X346745Y154000D02*X349745D01*
+Y154750D02*Y154000D01*
+X352296Y152500D02*X354546D01*
+X355296Y153250D01*
+X354546Y154000D02*X355296Y153250D01*
+X352296Y154000D02*X354546D01*
+X351546Y154750D02*X352296Y154000D01*
+X351546Y154750D02*X352296Y155500D01*
+X354546D01*
+X355296Y154750D01*
+X351546Y153250D02*X352296Y152500D01*
+X357848Y158500D02*Y153250D01*
+X358598Y152500D01*
+X357098Y156250D02*X358598D01*
+X362799Y155500D02*X365799D01*
+X370301Y158500D02*Y152500D01*
+Y158500D02*X373301D01*
+X370301Y155500D02*X372551D01*
+X377352D02*X378102Y154750D01*
+X375852Y155500D02*X377352D01*
+X375102Y154750D02*X375852Y155500D01*
+X375102Y154750D02*Y153250D01*
+X375852Y152500D01*
+X378102Y155500D02*Y153250D01*
+X378852Y152500D01*
+X375852D02*X377352D01*
+X378102Y153250D01*
+X380654Y158500D02*Y152500D01*
+Y153250D02*X381404Y152500D01*
+X382904D01*
+X383654Y153250D01*
+Y154750D02*Y153250D01*
+X382904Y155500D02*X383654Y154750D01*
+X381404Y155500D02*X382904D01*
+X380654Y154750D02*X381404Y155500D01*
+X386205Y154750D02*Y152500D01*
+Y154750D02*X386955Y155500D01*
+X388455D01*
+X385455D02*X386205Y154750D01*
+X390257Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X392508Y155500D02*X394758D01*
+X391758Y154750D02*X392508Y155500D01*
+X391758Y154750D02*Y153250D01*
+X392508Y152500D01*
+X394758D01*
+X398810Y155500D02*X399560Y154750D01*
+X397310Y155500D02*X398810D01*
+X396560Y154750D02*X397310Y155500D01*
+X396560Y154750D02*Y153250D01*
+X397310Y152500D01*
+X399560Y155500D02*Y153250D01*
+X400310Y152500D01*
+X397310D02*X398810D01*
+X399560Y153250D01*
+X402861Y158500D02*Y153250D01*
+X403611Y152500D01*
+X402111Y156250D02*X403611D01*
+X405113Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X406614Y154750D02*Y153250D01*
+Y154750D02*X407364Y155500D01*
+X408864D01*
+X409614Y154750D01*
+Y153250D01*
+X408864Y152500D02*X409614Y153250D01*
+X407364Y152500D02*X408864D01*
+X406614Y153250D02*X407364Y152500D01*
+X412166Y154750D02*Y152500D01*
+Y154750D02*X412916Y155500D01*
+X413666D01*
+X414416Y154750D01*
+Y152500D01*
+X411416Y155500D02*X412166Y154750D01*
+X419667Y158500D02*Y152500D01*
+X421917Y158500D02*X422667Y157750D01*
+Y153250D01*
+X421917Y152500D02*X422667Y153250D01*
+X418917Y152500D02*X421917D01*
+X418917Y158500D02*X421917D01*
+X425219Y154750D02*Y152500D01*
+Y154750D02*X425969Y155500D01*
+X427469D01*
+X424469D02*X425219Y154750D01*
+X431520Y155500D02*X432270Y154750D01*
+X430020Y155500D02*X431520D01*
+X429270Y154750D02*X430020Y155500D01*
+X429270Y154750D02*Y153250D01*
+X430020Y152500D01*
+X432270Y155500D02*Y153250D01*
+X433020Y152500D01*
+X430020D02*X431520D01*
+X432270Y153250D01*
+X434822Y155500D02*Y153250D01*
+X435572Y152500D01*
+X436322D01*
+X437072Y153250D01*
+Y155500D02*Y153250D01*
+X437822Y152500D01*
+X438572D01*
+X439322Y153250D01*
+Y155500D02*Y153250D01*
+X441123Y157000D02*Y156250D01*
+Y154750D02*Y152500D01*
+X443375Y154750D02*Y152500D01*
+Y154750D02*X444125Y155500D01*
+X444875D01*
+X445625Y154750D01*
+Y152500D01*
+X442625Y155500D02*X443375Y154750D01*
+X449676Y155500D02*X450426Y154750D01*
+X448176Y155500D02*X449676D01*
+X447426Y154750D02*X448176Y155500D01*
+X447426Y154750D02*Y153250D01*
+X448176Y152500D01*
+X449676D01*
+X450426Y153250D01*
+X447426Y151000D02*X448176Y150250D01*
+X449676D01*
+X450426Y151000D01*
+Y155500D02*Y151000D01*
+M02*
diff --git a/tests/orig/golden/hid_gerber2/out.plated-drill.cnc b/tests/orig/golden/hid_gerber2/out.plated-drill.cnc
new file mode 100644
index 0000000..5456a91
--- /dev/null
+++ b/tests/orig/golden/hid_gerber2/out.plated-drill.cnc
@@ -0,0 +1,7 @@
+M48
+INCH
+T13C0.035
+%
+T13
+X009000Y005000
+M30
diff --git a/tests/orig/golden/hid_gerber2/out.top.gbr b/tests/orig/golden/hid_gerber2/out.top.gbr
new file mode 100644
index 0000000..37f79a8
--- /dev/null
+++ b/tests/orig/golden/hid_gerber2/out.top.gbr
@@ -0,0 +1,17 @@
+G04 start of page 2 for group 0 idx 0 *
+G04 Title: Basic Single Trace RS274-X Test, component *
+G04 Creator: pcb 1.99y *
+G04 CreationDate: Wed Jun 24 22:05:35 2009 UTC *
+G04 For: dan *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 200000 100000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNFRONT*%
+%ADD11C,0.0400*%
+%ADD12C,0.0600*%
+%ADD13C,0.0350*%
+G54D11*X10000Y50000D02*X90000D01*
+G54D12*D03*
+G54D13*M02*
diff --git a/tests/orig/golden/hid_gerber3/arcs.bottom.gbr b/tests/orig/golden/hid_gerber3/arcs.bottom.gbr
new file mode 100644
index 0000000..f35703b
--- /dev/null
+++ b/tests/orig/golden/hid_gerber3/arcs.bottom.gbr
@@ -0,0 +1,23 @@
+G04 start of page 5 for group 5 idx 5 *
+G04 Title: (unknown), bottom *
+G04 Creator: pcb 1.99z *
+G04 CreationDate: Fri Jun 17 03:18:51 2011 UTC *
+G04 For: apoelstra *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 3000000 2000000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNBOTTOM*%
+%ADD32C,0.2500*%
+%ADD31C,0.5000*%
+%ADD30C,1.0000*%
+%ADD29C,0.6500*%
+%ADD28C,0.7500*%
+G54D28*X600000Y450000D03*
+G54D29*Y290000D03*
+G54D30*X450000Y600000D03*
+X600000D03*
+G54D29*X450000Y290000D03*
+G54D28*Y450000D03*
+G54D31*G54D32*G54D31*G54D32*G54D31*M02*
diff --git a/tests/orig/golden/hid_gerber3/arcs.fab.gbr b/tests/orig/golden/hid_gerber3/arcs.fab.gbr
new file mode 100644
index 0000000..d5b02d3
--- /dev/null
+++ b/tests/orig/golden/hid_gerber3/arcs.fab.gbr
@@ -0,0 +1,2025 @@
+G04 start of page 7 for group -3984 idx -3984 *
+G04 Title: (unknown), fab *
+G04 Creator: pcb 1.99z *
+G04 CreationDate: Fri Jun 17 03:18:51 2011 UTC *
+G04 For: apoelstra *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 3000000 2000000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNFAB*%
+%ADD37C,0.0100*%
+%ADD36C,0.0060*%
+%ADD35C,0.0080*%
+G54D35*X600000Y451600D02*Y448400D01*
+X598400Y450000D02*X601600D01*
+X450000Y601600D02*Y598400D01*
+X448400Y600000D02*X451600D01*
+X600000Y601600D02*Y598400D01*
+X598400Y600000D02*X601600D01*
+X450000Y451600D02*Y448400D01*
+X448400Y450000D02*X451600D01*
+X15000Y2042850D02*Y2039650D01*
+X13400Y2041250D02*X16600D01*
+G54D36*X135000Y2043500D02*Y2042750D01*
+X136500Y2041250D01*
+X138000Y2042750D01*
+Y2043500D02*Y2042750D01*
+X136500Y2041250D02*Y2037500D01*
+X139801Y2040500D02*X142051D01*
+X139801Y2037500D02*X142801D01*
+X139801Y2043500D02*Y2037500D01*
+Y2043500D02*X142801D01*
+X147603D02*X148353Y2042750D01*
+X145353Y2043500D02*X147603D01*
+X144603Y2042750D02*X145353Y2043500D01*
+X144603Y2042750D02*Y2041250D01*
+X145353Y2040500D01*
+X147603D01*
+X148353Y2039750D01*
+Y2038250D01*
+X147603Y2037500D02*X148353Y2038250D01*
+X145353Y2037500D02*X147603D01*
+X144603Y2038250D02*X145353Y2037500D01*
+X135000Y2034249D02*X150154D01*
+X98000Y2040500D02*X101000Y2043500D01*
+X98000Y2040500D02*X101750D01*
+X101000Y2043500D02*Y2037500D01*
+X98000Y2034249D02*X103551D01*
+X45000Y2038250D02*X45750Y2037500D01*
+X45000Y2042750D02*Y2038250D01*
+Y2042750D02*X45750Y2043500D01*
+X47250D01*
+X48000Y2042750D01*
+Y2038250D01*
+X47250Y2037500D02*X48000Y2038250D01*
+X45750Y2037500D02*X47250D01*
+X45000Y2039000D02*X48000Y2042000D01*
+X49801Y2037500D02*X50551D01*
+X52353Y2043500D02*X55353D01*
+X52353D02*Y2040500D01*
+X53103Y2041250D01*
+X54603D01*
+X55353Y2040500D01*
+Y2038250D01*
+X54603Y2037500D02*X55353Y2038250D01*
+X53103Y2037500D02*X54603D01*
+X52353Y2038250D02*X53103Y2037500D01*
+X57154Y2038250D02*X57904Y2037500D01*
+X57154Y2042750D02*Y2038250D01*
+Y2042750D02*X57904Y2043500D01*
+X59404D01*
+X60154Y2042750D01*
+Y2038250D01*
+X59404Y2037500D02*X60154Y2038250D01*
+X57904Y2037500D02*X59404D01*
+X57154Y2039000D02*X60154Y2042000D01*
+X61956Y2038250D02*X62706Y2037500D01*
+X61956Y2042750D02*Y2038250D01*
+Y2042750D02*X62706Y2043500D01*
+X64206D01*
+X64956Y2042750D01*
+Y2038250D01*
+X64206Y2037500D02*X64956Y2038250D01*
+X62706Y2037500D02*X64206D01*
+X61956Y2039000D02*X64956Y2042000D01*
+X45000Y2034249D02*X66757D01*
+X600000Y290000D02*Y288400D01*
+Y290000D02*X601386Y290800D01*
+X600000Y290000D02*X598614Y290800D01*
+X450000Y290000D02*Y288400D01*
+Y290000D02*X451386Y290800D01*
+X450000Y290000D02*X448614Y290800D01*
+X15000Y2056250D02*Y2054650D01*
+Y2056250D02*X16386Y2057050D01*
+X15000Y2056250D02*X13614Y2057050D01*
+X135000Y2058500D02*Y2057750D01*
+X136500Y2056250D01*
+X138000Y2057750D01*
+Y2058500D02*Y2057750D01*
+X136500Y2056250D02*Y2052500D01*
+X139801Y2055500D02*X142051D01*
+X139801Y2052500D02*X142801D01*
+X139801Y2058500D02*Y2052500D01*
+Y2058500D02*X142801D01*
+X147603D02*X148353Y2057750D01*
+X145353Y2058500D02*X147603D01*
+X144603Y2057750D02*X145353Y2058500D01*
+X144603Y2057750D02*Y2056250D01*
+X145353Y2055500D01*
+X147603D01*
+X148353Y2054750D01*
+Y2053250D01*
+X147603Y2052500D02*X148353Y2053250D01*
+X145353Y2052500D02*X147603D01*
+X144603Y2053250D02*X145353Y2052500D01*
+X135000Y2049249D02*X150154D01*
+X98000Y2057750D02*X98750Y2058500D01*
+X101000D01*
+X101750Y2057750D01*
+Y2056250D01*
+X98000Y2052500D02*X101750Y2056250D01*
+X98000Y2052500D02*X101750D01*
+X98000Y2049249D02*X103551D01*
+X45000Y2053250D02*X45750Y2052500D01*
+X45000Y2057750D02*Y2053250D01*
+Y2057750D02*X45750Y2058500D01*
+X47250D01*
+X48000Y2057750D01*
+Y2053250D01*
+X47250Y2052500D02*X48000Y2053250D01*
+X45750Y2052500D02*X47250D01*
+X45000Y2054000D02*X48000Y2057000D01*
+X49801Y2052500D02*X50551D01*
+X52353Y2057750D02*X53103Y2058500D01*
+X55353D01*
+X56103Y2057750D01*
+Y2056250D01*
+X52353Y2052500D02*X56103Y2056250D01*
+X52353Y2052500D02*X56103D01*
+X57904Y2058500D02*X60904D01*
+X57904D02*Y2055500D01*
+X58654Y2056250D01*
+X60154D01*
+X60904Y2055500D01*
+Y2053250D01*
+X60154Y2052500D02*X60904Y2053250D01*
+X58654Y2052500D02*X60154D01*
+X57904Y2053250D02*X58654Y2052500D01*
+X62706Y2053250D02*X63456Y2052500D01*
+X62706Y2057750D02*Y2053250D01*
+Y2057750D02*X63456Y2058500D01*
+X64956D01*
+X65706Y2057750D01*
+Y2053250D01*
+X64956Y2052500D02*X65706Y2053250D01*
+X63456Y2052500D02*X64956D01*
+X62706Y2054000D02*X65706Y2057000D01*
+X45000Y2049249D02*X67507D01*
+X3000Y2073500D02*X3750Y2072750D01*
+X750Y2073500D02*X3000D01*
+X0Y2072750D02*X750Y2073500D01*
+X0Y2072750D02*Y2071250D01*
+X750Y2070500D01*
+X3000D01*
+X3750Y2069750D01*
+Y2068250D01*
+X3000Y2067500D02*X3750Y2068250D01*
+X750Y2067500D02*X3000D01*
+X0Y2068250D02*X750Y2067500D01*
+X5551Y2070500D02*Y2068250D01*
+X6301Y2067500D01*
+X8551Y2070500D02*Y2066000D01*
+X7801Y2065250D02*X8551Y2066000D01*
+X6301Y2065250D02*X7801D01*
+X5551Y2066000D02*X6301Y2065250D01*
+Y2067500D02*X7801D01*
+X8551Y2068250D01*
+X11103Y2069750D02*Y2067500D01*
+Y2069750D02*X11853Y2070500D01*
+X12603D01*
+X13353Y2069750D01*
+Y2067500D01*
+Y2069750D02*X14103Y2070500D01*
+X14853D01*
+X15603Y2069750D01*
+Y2067500D01*
+X10353Y2070500D02*X11103Y2069750D01*
+X17404Y2073500D02*Y2067500D01*
+Y2068250D02*X18154Y2067500D01*
+X19654D01*
+X20404Y2068250D01*
+Y2069750D02*Y2068250D01*
+X19654Y2070500D02*X20404Y2069750D01*
+X18154Y2070500D02*X19654D01*
+X17404Y2069750D02*X18154Y2070500D01*
+X22206Y2069750D02*Y2068250D01*
+Y2069750D02*X22956Y2070500D01*
+X24456D01*
+X25206Y2069750D01*
+Y2068250D01*
+X24456Y2067500D02*X25206Y2068250D01*
+X22956Y2067500D02*X24456D01*
+X22206Y2068250D02*X22956Y2067500D01*
+X27007Y2073500D02*Y2068250D01*
+X27757Y2067500D01*
+X41750Y2073500D02*Y2067500D01*
+X44000Y2073500D02*X44750Y2072750D01*
+Y2068250D01*
+X44000Y2067500D02*X44750Y2068250D01*
+X41000Y2067500D02*X44000D01*
+X41000Y2073500D02*X44000D01*
+X46551Y2072000D02*Y2071250D01*
+Y2069750D02*Y2067500D01*
+X50303Y2070500D02*X51053Y2069750D01*
+X48803Y2070500D02*X50303D01*
+X48053Y2069750D02*X48803Y2070500D01*
+X48053Y2069750D02*Y2068250D01*
+X48803Y2067500D01*
+X51053Y2070500D02*Y2068250D01*
+X51803Y2067500D01*
+X48803D02*X50303D01*
+X51053Y2068250D01*
+X54354Y2069750D02*Y2067500D01*
+Y2069750D02*X55104Y2070500D01*
+X55854D01*
+X56604Y2069750D01*
+Y2067500D01*
+Y2069750D02*X57354Y2070500D01*
+X58104D01*
+X58854Y2069750D01*
+Y2067500D01*
+X53604Y2070500D02*X54354Y2069750D01*
+X60656Y2067500D02*X61406D01*
+X65907Y2068250D02*X66657Y2067500D01*
+X65907Y2072750D02*X66657Y2073500D01*
+X65907Y2072750D02*Y2068250D01*
+X68459Y2073500D02*X69959D01*
+X69209D02*Y2067500D01*
+X68459D02*X69959D01*
+X72510Y2069750D02*Y2067500D01*
+Y2069750D02*X73260Y2070500D01*
+X74010D01*
+X74760Y2069750D01*
+Y2067500D01*
+X71760Y2070500D02*X72510Y2069750D01*
+X77312Y2070500D02*X79562D01*
+X76562Y2069750D02*X77312Y2070500D01*
+X76562Y2069750D02*Y2068250D01*
+X77312Y2067500D01*
+X79562D01*
+X81363Y2073500D02*Y2067500D01*
+Y2069750D02*X82113Y2070500D01*
+X83613D01*
+X84363Y2069750D01*
+Y2067500D01*
+X86165Y2073500D02*X86915Y2072750D01*
+Y2068250D01*
+X86165Y2067500D02*X86915Y2068250D01*
+X95750Y2067500D02*X98000D01*
+X95000Y2068250D02*X95750Y2067500D01*
+X95000Y2072750D02*Y2068250D01*
+Y2072750D02*X95750Y2073500D01*
+X98000D01*
+X99801Y2069750D02*Y2068250D01*
+Y2069750D02*X100551Y2070500D01*
+X102051D01*
+X102801Y2069750D01*
+Y2068250D01*
+X102051Y2067500D02*X102801Y2068250D01*
+X100551Y2067500D02*X102051D01*
+X99801Y2068250D02*X100551Y2067500D01*
+X104603Y2070500D02*Y2068250D01*
+X105353Y2067500D01*
+X106853D01*
+X107603Y2068250D01*
+Y2070500D02*Y2068250D01*
+X110154Y2069750D02*Y2067500D01*
+Y2069750D02*X110904Y2070500D01*
+X111654D01*
+X112404Y2069750D01*
+Y2067500D01*
+X109404Y2070500D02*X110154Y2069750D01*
+X114956Y2073500D02*Y2068250D01*
+X115706Y2067500D01*
+X114206Y2071250D02*X115706D01*
+X130750Y2073500D02*Y2067500D01*
+X130000Y2073500D02*X133000D01*
+X133750Y2072750D01*
+Y2071250D01*
+X133000Y2070500D02*X133750Y2071250D01*
+X130750Y2070500D02*X133000D01*
+X135551Y2073500D02*Y2068250D01*
+X136301Y2067500D01*
+X140053Y2070500D02*X140803Y2069750D01*
+X138553Y2070500D02*X140053D01*
+X137803Y2069750D02*X138553Y2070500D01*
+X137803Y2069750D02*Y2068250D01*
+X138553Y2067500D01*
+X140803Y2070500D02*Y2068250D01*
+X141553Y2067500D01*
+X138553D02*X140053D01*
+X140803Y2068250D01*
+X144104Y2073500D02*Y2068250D01*
+X144854Y2067500D01*
+X143354Y2071250D02*X144854D01*
+X147106Y2067500D02*X149356D01*
+X146356Y2068250D02*X147106Y2067500D01*
+X146356Y2069750D02*Y2068250D01*
+Y2069750D02*X147106Y2070500D01*
+X148606D01*
+X149356Y2069750D01*
+X146356Y2069000D02*X149356D01*
+Y2069750D02*Y2069000D01*
+X154157Y2073500D02*Y2067500D01*
+X153407D02*X154157Y2068250D01*
+X151907Y2067500D02*X153407D01*
+X151157Y2068250D02*X151907Y2067500D01*
+X151157Y2069750D02*Y2068250D01*
+Y2069750D02*X151907Y2070500D01*
+X153407D01*
+X154157Y2069750D01*
+X157459Y2070500D02*Y2069750D01*
+Y2068250D02*Y2067500D01*
+X155959Y2072750D02*Y2072000D01*
+Y2072750D02*X156709Y2073500D01*
+X158209D01*
+X158959Y2072750D01*
+Y2072000D01*
+X157459Y2070500D02*X158959Y2072000D01*
+X0Y2088500D02*X3000D01*
+X1500D02*Y2082500D01*
+X4801Y2088500D02*Y2082500D01*
+Y2084750D02*X5551Y2085500D01*
+X7051D01*
+X7801Y2084750D01*
+Y2082500D01*
+X10353D02*X12603D01*
+X9603Y2083250D02*X10353Y2082500D01*
+X9603Y2084750D02*Y2083250D01*
+Y2084750D02*X10353Y2085500D01*
+X11853D01*
+X12603Y2084750D01*
+X9603Y2084000D02*X12603D01*
+Y2084750D02*Y2084000D01*
+X15154Y2084750D02*Y2082500D01*
+Y2084750D02*X15904Y2085500D01*
+X17404D01*
+X14404D02*X15154Y2084750D01*
+X19956Y2082500D02*X22206D01*
+X19206Y2083250D02*X19956Y2082500D01*
+X19206Y2084750D02*Y2083250D01*
+Y2084750D02*X19956Y2085500D01*
+X21456D01*
+X22206Y2084750D01*
+X19206Y2084000D02*X22206D01*
+Y2084750D02*Y2084000D01*
+X28957Y2085500D02*X29707Y2084750D01*
+X27457Y2085500D02*X28957D01*
+X26707Y2084750D02*X27457Y2085500D01*
+X26707Y2084750D02*Y2083250D01*
+X27457Y2082500D01*
+X29707Y2085500D02*Y2083250D01*
+X30457Y2082500D01*
+X27457D02*X28957D01*
+X29707Y2083250D01*
+X33009Y2084750D02*Y2082500D01*
+Y2084750D02*X33759Y2085500D01*
+X35259D01*
+X32259D02*X33009Y2084750D01*
+X37810Y2082500D02*X40060D01*
+X37060Y2083250D02*X37810Y2082500D01*
+X37060Y2084750D02*Y2083250D01*
+Y2084750D02*X37810Y2085500D01*
+X39310D01*
+X40060Y2084750D01*
+X37060Y2084000D02*X40060D01*
+Y2084750D02*Y2084000D01*
+X44562Y2087750D02*X45312Y2088500D01*
+X47562D01*
+X48312Y2087750D01*
+Y2086250D01*
+X44562Y2082500D02*X48312Y2086250D01*
+X44562Y2082500D02*X48312D01*
+X55813Y2088500D02*Y2082500D01*
+X55063D02*X55813Y2083250D01*
+X53563Y2082500D02*X55063D01*
+X52813Y2083250D02*X53563Y2082500D01*
+X52813Y2084750D02*Y2083250D01*
+Y2084750D02*X53563Y2085500D01*
+X55063D01*
+X55813Y2084750D01*
+X57615Y2087000D02*Y2086250D01*
+Y2084750D02*Y2082500D01*
+X59866Y2087750D02*Y2082500D01*
+Y2087750D02*X60616Y2088500D01*
+X61366D01*
+X59116Y2085500D02*X60616D01*
+X63618Y2087750D02*Y2082500D01*
+Y2087750D02*X64368Y2088500D01*
+X65118D01*
+X62868Y2085500D02*X64368D01*
+X67369Y2082500D02*X69619D01*
+X66619Y2083250D02*X67369Y2082500D01*
+X66619Y2084750D02*Y2083250D01*
+Y2084750D02*X67369Y2085500D01*
+X68869D01*
+X69619Y2084750D01*
+X66619Y2084000D02*X69619D01*
+Y2084750D02*Y2084000D01*
+X72171Y2084750D02*Y2082500D01*
+Y2084750D02*X72921Y2085500D01*
+X74421D01*
+X71421D02*X72171Y2084750D01*
+X76972Y2082500D02*X79222D01*
+X76222Y2083250D02*X76972Y2082500D01*
+X76222Y2084750D02*Y2083250D01*
+Y2084750D02*X76972Y2085500D01*
+X78472D01*
+X79222Y2084750D01*
+X76222Y2084000D02*X79222D01*
+Y2084750D02*Y2084000D01*
+X81774Y2084750D02*Y2082500D01*
+Y2084750D02*X82524Y2085500D01*
+X83274D01*
+X84024Y2084750D01*
+Y2082500D01*
+X81024Y2085500D02*X81774Y2084750D01*
+X86575Y2088500D02*Y2083250D01*
+X87325Y2082500D01*
+X85825Y2086250D02*X87325D01*
+X94527Y2088500D02*Y2082500D01*
+X93777D02*X94527Y2083250D01*
+X92277Y2082500D02*X93777D01*
+X91527Y2083250D02*X92277Y2082500D01*
+X91527Y2084750D02*Y2083250D01*
+Y2084750D02*X92277Y2085500D01*
+X93777D01*
+X94527Y2084750D01*
+X97078D02*Y2082500D01*
+Y2084750D02*X97828Y2085500D01*
+X99328D01*
+X96328D02*X97078Y2084750D01*
+X101130Y2087000D02*Y2086250D01*
+Y2084750D02*Y2082500D01*
+X102631Y2088500D02*Y2083250D01*
+X103381Y2082500D01*
+X104883Y2088500D02*Y2083250D01*
+X105633Y2082500D01*
+X110584D02*X112834D01*
+X113584Y2083250D01*
+X112834Y2084000D02*X113584Y2083250D01*
+X110584Y2084000D02*X112834D01*
+X109834Y2084750D02*X110584Y2084000D01*
+X109834Y2084750D02*X110584Y2085500D01*
+X112834D01*
+X113584Y2084750D01*
+X109834Y2083250D02*X110584Y2082500D01*
+X115386Y2087000D02*Y2086250D01*
+Y2084750D02*Y2082500D01*
+X116887Y2085500D02*X119887D01*
+X116887Y2082500D02*X119887Y2085500D01*
+X116887Y2082500D02*X119887D01*
+X122439D02*X124689D01*
+X121689Y2083250D02*X122439Y2082500D01*
+X121689Y2084750D02*Y2083250D01*
+Y2084750D02*X122439Y2085500D01*
+X123939D01*
+X124689Y2084750D01*
+X121689Y2084000D02*X124689D01*
+Y2084750D02*Y2084000D01*
+X127240Y2082500D02*X129490D01*
+X130240Y2083250D01*
+X129490Y2084000D02*X130240Y2083250D01*
+X127240Y2084000D02*X129490D01*
+X126490Y2084750D02*X127240Y2084000D01*
+X126490Y2084750D02*X127240Y2085500D01*
+X129490D01*
+X130240Y2084750D01*
+X126490Y2083250D02*X127240Y2082500D01*
+X134742Y2085500D02*Y2083250D01*
+X135492Y2082500D01*
+X136992D01*
+X137742Y2083250D01*
+Y2085500D02*Y2083250D01*
+X140293Y2082500D02*X142543D01*
+X143293Y2083250D01*
+X142543Y2084000D02*X143293Y2083250D01*
+X140293Y2084000D02*X142543D01*
+X139543Y2084750D02*X140293Y2084000D01*
+X139543Y2084750D02*X140293Y2085500D01*
+X142543D01*
+X143293Y2084750D01*
+X139543Y2083250D02*X140293Y2082500D01*
+X145845D02*X148095D01*
+X145095Y2083250D02*X145845Y2082500D01*
+X145095Y2084750D02*Y2083250D01*
+Y2084750D02*X145845Y2085500D01*
+X147345D01*
+X148095Y2084750D01*
+X145095Y2084000D02*X148095D01*
+Y2084750D02*Y2084000D01*
+X152896Y2088500D02*Y2082500D01*
+X152146D02*X152896Y2083250D01*
+X150646Y2082500D02*X152146D01*
+X149896Y2083250D02*X150646Y2082500D01*
+X149896Y2084750D02*Y2083250D01*
+Y2084750D02*X150646Y2085500D01*
+X152146D01*
+X152896Y2084750D01*
+X157398Y2087000D02*Y2086250D01*
+Y2084750D02*Y2082500D01*
+X159649Y2084750D02*Y2082500D01*
+Y2084750D02*X160399Y2085500D01*
+X161149D01*
+X161899Y2084750D01*
+Y2082500D01*
+X158899Y2085500D02*X159649Y2084750D01*
+X167151Y2088500D02*Y2083250D01*
+X167901Y2082500D01*
+X166401Y2086250D02*X167901D01*
+X169402Y2088500D02*Y2082500D01*
+Y2084750D02*X170152Y2085500D01*
+X171652D01*
+X172402Y2084750D01*
+Y2082500D01*
+X174204Y2087000D02*Y2086250D01*
+Y2084750D02*Y2082500D01*
+X176455D02*X178705D01*
+X179455Y2083250D01*
+X178705Y2084000D02*X179455Y2083250D01*
+X176455Y2084000D02*X178705D01*
+X175705Y2084750D02*X176455Y2084000D01*
+X175705Y2084750D02*X176455Y2085500D01*
+X178705D01*
+X179455Y2084750D01*
+X175705Y2083250D02*X176455Y2082500D01*
+X183957Y2088500D02*Y2083250D01*
+X184707Y2082500D01*
+X188458Y2085500D02*X189208Y2084750D01*
+X186958Y2085500D02*X188458D01*
+X186208Y2084750D02*X186958Y2085500D01*
+X186208Y2084750D02*Y2083250D01*
+X186958Y2082500D01*
+X189208Y2085500D02*Y2083250D01*
+X189958Y2082500D01*
+X186958D02*X188458D01*
+X189208Y2083250D01*
+X191760Y2085500D02*Y2083250D01*
+X192510Y2082500D01*
+X194760Y2085500D02*Y2081000D01*
+X194010Y2080250D02*X194760Y2081000D01*
+X192510Y2080250D02*X194010D01*
+X191760Y2081000D02*X192510Y2080250D01*
+Y2082500D02*X194010D01*
+X194760Y2083250D01*
+X196561Y2084750D02*Y2083250D01*
+Y2084750D02*X197311Y2085500D01*
+X198811D01*
+X199561Y2084750D01*
+Y2083250D01*
+X198811Y2082500D02*X199561Y2083250D01*
+X197311Y2082500D02*X198811D01*
+X196561Y2083250D02*X197311Y2082500D01*
+X201363Y2085500D02*Y2083250D01*
+X202113Y2082500D01*
+X203613D01*
+X204363Y2083250D01*
+Y2085500D02*Y2083250D01*
+X206914Y2088500D02*Y2083250D01*
+X207664Y2082500D01*
+X206164Y2086250D02*X207664D01*
+X209166Y2081000D02*X210666Y2082500D01*
+X217417Y2088500D02*X218167Y2087750D01*
+X215917Y2088500D02*X217417D01*
+X215167Y2087750D02*X215917Y2088500D01*
+X215167Y2087750D02*Y2083250D01*
+X215917Y2082500D01*
+X217417Y2085500D02*X218167Y2084750D01*
+X215167Y2085500D02*X217417D01*
+X215917Y2082500D02*X217417D01*
+X218167Y2083250D01*
+Y2084750D02*Y2083250D01*
+X222669Y2088500D02*Y2082500D01*
+Y2084750D02*X223419Y2085500D01*
+X224919D01*
+X225669Y2084750D01*
+Y2082500D01*
+X227470Y2084750D02*Y2083250D01*
+Y2084750D02*X228220Y2085500D01*
+X229720D01*
+X230470Y2084750D01*
+Y2083250D01*
+X229720Y2082500D02*X230470Y2083250D01*
+X228220Y2082500D02*X229720D01*
+X227470Y2083250D02*X228220Y2082500D01*
+X232272Y2088500D02*Y2083250D01*
+X233022Y2082500D01*
+X235273D02*X237523D01*
+X234523Y2083250D02*X235273Y2082500D01*
+X234523Y2084750D02*Y2083250D01*
+Y2084750D02*X235273Y2085500D01*
+X236773D01*
+X237523Y2084750D01*
+X234523Y2084000D02*X237523D01*
+Y2084750D02*Y2084000D01*
+X240075Y2082500D02*X242325D01*
+X243075Y2083250D01*
+X242325Y2084000D02*X243075Y2083250D01*
+X240075Y2084000D02*X242325D01*
+X239325Y2084750D02*X240075Y2084000D01*
+X239325Y2084750D02*X240075Y2085500D01*
+X242325D01*
+X243075Y2084750D01*
+X239325Y2083250D02*X240075Y2082500D01*
+X248326Y2088500D02*Y2083250D01*
+X249076Y2082500D01*
+X247576Y2086250D02*X249076D01*
+X250578Y2084750D02*Y2083250D01*
+Y2084750D02*X251328Y2085500D01*
+X252828D01*
+X253578Y2084750D01*
+Y2083250D01*
+X252828Y2082500D02*X253578Y2083250D01*
+X251328Y2082500D02*X252828D01*
+X250578Y2083250D02*X251328Y2082500D01*
+X256129Y2088500D02*Y2083250D01*
+X256879Y2082500D01*
+X255379Y2086250D02*X256879D01*
+X260631Y2085500D02*X261381Y2084750D01*
+X259131Y2085500D02*X260631D01*
+X258381Y2084750D02*X259131Y2085500D01*
+X258381Y2084750D02*Y2083250D01*
+X259131Y2082500D01*
+X261381Y2085500D02*Y2083250D01*
+X262131Y2082500D01*
+X259131D02*X260631D01*
+X261381Y2083250D01*
+X263932Y2088500D02*Y2083250D01*
+X264682Y2082500D01*
+G54D37*X0Y2000000D02*X3000000D01*
+X0D02*Y0D01*
+X3000000Y2000000D02*Y0D01*
+X0D02*X3000000D01*
+G54D36*X200000Y2013500D02*Y2007500D01*
+Y2013500D02*X202250Y2011250D01*
+X204500Y2013500D01*
+Y2007500D01*
+X208551Y2010500D02*X209301Y2009750D01*
+X207051Y2010500D02*X208551D01*
+X206301Y2009750D02*X207051Y2010500D01*
+X206301Y2009750D02*Y2008250D01*
+X207051Y2007500D01*
+X209301Y2010500D02*Y2008250D01*
+X210051Y2007500D01*
+X207051D02*X208551D01*
+X209301Y2008250D01*
+X211853Y2010500D02*X214853Y2007500D01*
+X211853D02*X214853Y2010500D01*
+X216654Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X218906Y2009750D02*Y2007500D01*
+Y2009750D02*X219656Y2010500D01*
+X220406D01*
+X221156Y2009750D01*
+Y2007500D01*
+Y2009750D02*X221906Y2010500D01*
+X222656D01*
+X223406Y2009750D01*
+Y2007500D01*
+X218156Y2010500D02*X218906Y2009750D01*
+X225207Y2010500D02*Y2008250D01*
+X225957Y2007500D01*
+X227457D01*
+X228207Y2008250D01*
+Y2010500D02*Y2008250D01*
+X230759Y2009750D02*Y2007500D01*
+Y2009750D02*X231509Y2010500D01*
+X232259D01*
+X233009Y2009750D01*
+Y2007500D01*
+Y2009750D02*X233759Y2010500D01*
+X234509D01*
+X235259Y2009750D01*
+Y2007500D01*
+X230009Y2010500D02*X230759Y2009750D01*
+X240510Y2013500D02*Y2007500D01*
+X242760Y2013500D02*X243510Y2012750D01*
+Y2008250D01*
+X242760Y2007500D02*X243510Y2008250D01*
+X239760Y2007500D02*X242760D01*
+X239760Y2013500D02*X242760D01*
+X245312Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X247563Y2009750D02*Y2007500D01*
+Y2009750D02*X248313Y2010500D01*
+X249063D01*
+X249813Y2009750D01*
+Y2007500D01*
+Y2009750D02*X250563Y2010500D01*
+X251313D01*
+X252063Y2009750D01*
+Y2007500D01*
+X246813Y2010500D02*X247563Y2009750D01*
+X254615Y2007500D02*X256865D01*
+X253865Y2008250D02*X254615Y2007500D01*
+X253865Y2009750D02*Y2008250D01*
+Y2009750D02*X254615Y2010500D01*
+X256115D01*
+X256865Y2009750D01*
+X253865Y2009000D02*X256865D01*
+Y2009750D02*Y2009000D01*
+X259416Y2009750D02*Y2007500D01*
+Y2009750D02*X260166Y2010500D01*
+X260916D01*
+X261666Y2009750D01*
+Y2007500D01*
+X258666Y2010500D02*X259416Y2009750D01*
+X264218Y2007500D02*X266468D01*
+X267218Y2008250D01*
+X266468Y2009000D02*X267218Y2008250D01*
+X264218Y2009000D02*X266468D01*
+X263468Y2009750D02*X264218Y2009000D01*
+X263468Y2009750D02*X264218Y2010500D01*
+X266468D01*
+X267218Y2009750D01*
+X263468Y2008250D02*X264218Y2007500D01*
+X269019Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X270521Y2009750D02*Y2008250D01*
+Y2009750D02*X271271Y2010500D01*
+X272771D01*
+X273521Y2009750D01*
+Y2008250D01*
+X272771Y2007500D02*X273521Y2008250D01*
+X271271Y2007500D02*X272771D01*
+X270521Y2008250D02*X271271Y2007500D01*
+X276072Y2009750D02*Y2007500D01*
+Y2009750D02*X276822Y2010500D01*
+X277572D01*
+X278322Y2009750D01*
+Y2007500D01*
+X275322Y2010500D02*X276072Y2009750D01*
+X280874Y2007500D02*X283124D01*
+X283874Y2008250D01*
+X283124Y2009000D02*X283874Y2008250D01*
+X280874Y2009000D02*X283124D01*
+X280124Y2009750D02*X280874Y2009000D01*
+X280124Y2009750D02*X280874Y2010500D01*
+X283124D01*
+X283874Y2009750D01*
+X280124Y2008250D02*X280874Y2007500D01*
+X285675Y2011250D02*X286425D01*
+X285675Y2009750D02*X286425D01*
+X290927Y2012750D02*X291677Y2013500D01*
+X293177D01*
+X293927Y2012750D01*
+Y2008250D01*
+X293177Y2007500D02*X293927Y2008250D01*
+X291677Y2007500D02*X293177D01*
+X290927Y2008250D02*X291677Y2007500D01*
+Y2010500D02*X293927D01*
+X295728Y2008250D02*X296478Y2007500D01*
+X295728Y2012750D02*Y2008250D01*
+Y2012750D02*X296478Y2013500D01*
+X297978D01*
+X298728Y2012750D01*
+Y2008250D01*
+X297978Y2007500D02*X298728Y2008250D01*
+X296478Y2007500D02*X297978D01*
+X295728Y2009000D02*X298728Y2012000D01*
+X300530Y2008250D02*X301280Y2007500D01*
+X300530Y2012750D02*Y2008250D01*
+Y2012750D02*X301280Y2013500D01*
+X302780D01*
+X303530Y2012750D01*
+Y2008250D01*
+X302780Y2007500D02*X303530Y2008250D01*
+X301280Y2007500D02*X302780D01*
+X300530Y2009000D02*X303530Y2012000D01*
+X305331Y2008250D02*X306081Y2007500D01*
+X305331Y2012750D02*Y2008250D01*
+Y2012750D02*X306081Y2013500D01*
+X307581D01*
+X308331Y2012750D01*
+Y2008250D01*
+X307581Y2007500D02*X308331Y2008250D01*
+X306081Y2007500D02*X307581D01*
+X305331Y2009000D02*X308331Y2012000D01*
+X310133Y2008250D02*X310883Y2007500D01*
+X310133Y2012750D02*Y2008250D01*
+Y2012750D02*X310883Y2013500D01*
+X312383D01*
+X313133Y2012750D01*
+Y2008250D01*
+X312383Y2007500D02*X313133Y2008250D01*
+X310883Y2007500D02*X312383D01*
+X310133Y2009000D02*X313133Y2012000D01*
+X314934Y2007500D02*X315684D01*
+X317486Y2008250D02*X318236Y2007500D01*
+X317486Y2012750D02*Y2008250D01*
+Y2012750D02*X318236Y2013500D01*
+X319736D01*
+X320486Y2012750D01*
+Y2008250D01*
+X319736Y2007500D02*X320486Y2008250D01*
+X318236Y2007500D02*X319736D01*
+X317486Y2009000D02*X320486Y2012000D01*
+X322287Y2008250D02*X323037Y2007500D01*
+X322287Y2012750D02*Y2008250D01*
+Y2012750D02*X323037Y2013500D01*
+X324537D01*
+X325287Y2012750D01*
+Y2008250D01*
+X324537Y2007500D02*X325287Y2008250D01*
+X323037Y2007500D02*X324537D01*
+X322287Y2009000D02*X325287Y2012000D01*
+X327089Y2008250D02*X327839Y2007500D01*
+X327089Y2012750D02*Y2008250D01*
+Y2012750D02*X327839Y2013500D01*
+X329339D01*
+X330089Y2012750D01*
+Y2008250D01*
+X329339Y2007500D02*X330089Y2008250D01*
+X327839Y2007500D02*X329339D01*
+X327089Y2009000D02*X330089Y2012000D01*
+X331890Y2008250D02*X332640Y2007500D01*
+X331890Y2012750D02*Y2008250D01*
+Y2012750D02*X332640Y2013500D01*
+X334140D01*
+X334890Y2012750D01*
+Y2008250D01*
+X334140Y2007500D02*X334890Y2008250D01*
+X332640Y2007500D02*X334140D01*
+X331890Y2009000D02*X334890Y2012000D01*
+X336692Y2008250D02*X337442Y2007500D01*
+X336692Y2012750D02*Y2008250D01*
+Y2012750D02*X337442Y2013500D01*
+X338942D01*
+X339692Y2012750D01*
+Y2008250D01*
+X338942Y2007500D02*X339692Y2008250D01*
+X337442Y2007500D02*X338942D01*
+X336692Y2009000D02*X339692Y2012000D01*
+X341493Y2008250D02*X342243Y2007500D01*
+X341493Y2012750D02*Y2008250D01*
+Y2012750D02*X342243Y2013500D01*
+X343743D01*
+X344493Y2012750D01*
+Y2008250D01*
+X343743Y2007500D02*X344493Y2008250D01*
+X342243Y2007500D02*X343743D01*
+X341493Y2009000D02*X344493Y2012000D01*
+X349745Y2009750D02*Y2007500D01*
+Y2009750D02*X350495Y2010500D01*
+X351245D01*
+X351995Y2009750D01*
+Y2007500D01*
+Y2009750D02*X352745Y2010500D01*
+X353495D01*
+X354245Y2009750D01*
+Y2007500D01*
+X348995Y2010500D02*X349745Y2009750D01*
+X356046Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X357548Y2013500D02*Y2008250D01*
+X358298Y2007500D01*
+X360549D02*X362799D01*
+X363549Y2008250D01*
+X362799Y2009000D02*X363549Y2008250D01*
+X360549Y2009000D02*X362799D01*
+X359799Y2009750D02*X360549Y2009000D01*
+X359799Y2009750D02*X360549Y2010500D01*
+X362799D01*
+X363549Y2009750D01*
+X359799Y2008250D02*X360549Y2007500D01*
+X368051Y2010500D02*Y2008250D01*
+X368801Y2007500D01*
+X369551D01*
+X370301Y2008250D01*
+Y2010500D02*Y2008250D01*
+X371051Y2007500D01*
+X371801D01*
+X372551Y2008250D01*
+Y2010500D02*Y2008250D01*
+X374352Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X378854Y2013500D02*Y2007500D01*
+X378104D02*X378854Y2008250D01*
+X376604Y2007500D02*X378104D01*
+X375854Y2008250D02*X376604Y2007500D01*
+X375854Y2009750D02*Y2008250D01*
+Y2009750D02*X376604Y2010500D01*
+X378104D01*
+X378854Y2009750D01*
+X381405Y2007500D02*X383655D01*
+X380655Y2008250D02*X381405Y2007500D01*
+X380655Y2009750D02*Y2008250D01*
+Y2009750D02*X381405Y2010500D01*
+X382905D01*
+X383655Y2009750D01*
+X380655Y2009000D02*X383655D01*
+Y2009750D02*Y2009000D01*
+X385457Y2006000D02*X386957Y2007500D01*
+X391458Y2012750D02*X392208Y2013500D01*
+X394458D01*
+X395208Y2012750D01*
+Y2011250D01*
+X391458Y2007500D02*X395208Y2011250D01*
+X391458Y2007500D02*X395208D01*
+X397010Y2008250D02*X397760Y2007500D01*
+X397010Y2012750D02*Y2008250D01*
+Y2012750D02*X397760Y2013500D01*
+X399260D01*
+X400010Y2012750D01*
+Y2008250D01*
+X399260Y2007500D02*X400010Y2008250D01*
+X397760Y2007500D02*X399260D01*
+X397010Y2009000D02*X400010Y2012000D01*
+X401811Y2008250D02*X402561Y2007500D01*
+X401811Y2012750D02*Y2008250D01*
+Y2012750D02*X402561Y2013500D01*
+X404061D01*
+X404811Y2012750D01*
+Y2008250D01*
+X404061Y2007500D02*X404811Y2008250D01*
+X402561Y2007500D02*X404061D01*
+X401811Y2009000D02*X404811Y2012000D01*
+X406613Y2008250D02*X407363Y2007500D01*
+X406613Y2012750D02*Y2008250D01*
+Y2012750D02*X407363Y2013500D01*
+X408863D01*
+X409613Y2012750D01*
+Y2008250D01*
+X408863Y2007500D02*X409613Y2008250D01*
+X407363Y2007500D02*X408863D01*
+X406613Y2009000D02*X409613Y2012000D01*
+X411414Y2008250D02*X412164Y2007500D01*
+X411414Y2012750D02*Y2008250D01*
+Y2012750D02*X412164Y2013500D01*
+X413664D01*
+X414414Y2012750D01*
+Y2008250D01*
+X413664Y2007500D02*X414414Y2008250D01*
+X412164Y2007500D02*X413664D01*
+X411414Y2009000D02*X414414Y2012000D01*
+X416216Y2007500D02*X416966D01*
+X418767Y2008250D02*X419517Y2007500D01*
+X418767Y2012750D02*Y2008250D01*
+Y2012750D02*X419517Y2013500D01*
+X421017D01*
+X421767Y2012750D01*
+Y2008250D01*
+X421017Y2007500D02*X421767Y2008250D01*
+X419517Y2007500D02*X421017D01*
+X418767Y2009000D02*X421767Y2012000D01*
+X423569Y2008250D02*X424319Y2007500D01*
+X423569Y2012750D02*Y2008250D01*
+Y2012750D02*X424319Y2013500D01*
+X425819D01*
+X426569Y2012750D01*
+Y2008250D01*
+X425819Y2007500D02*X426569Y2008250D01*
+X424319Y2007500D02*X425819D01*
+X423569Y2009000D02*X426569Y2012000D01*
+X428370Y2008250D02*X429120Y2007500D01*
+X428370Y2012750D02*Y2008250D01*
+Y2012750D02*X429120Y2013500D01*
+X430620D01*
+X431370Y2012750D01*
+Y2008250D01*
+X430620Y2007500D02*X431370Y2008250D01*
+X429120Y2007500D02*X430620D01*
+X428370Y2009000D02*X431370Y2012000D01*
+X433172Y2008250D02*X433922Y2007500D01*
+X433172Y2012750D02*Y2008250D01*
+Y2012750D02*X433922Y2013500D01*
+X435422D01*
+X436172Y2012750D01*
+Y2008250D01*
+X435422Y2007500D02*X436172Y2008250D01*
+X433922Y2007500D02*X435422D01*
+X433172Y2009000D02*X436172Y2012000D01*
+X437973Y2008250D02*X438723Y2007500D01*
+X437973Y2012750D02*Y2008250D01*
+Y2012750D02*X438723Y2013500D01*
+X440223D01*
+X440973Y2012750D01*
+Y2008250D01*
+X440223Y2007500D02*X440973Y2008250D01*
+X438723Y2007500D02*X440223D01*
+X437973Y2009000D02*X440973Y2012000D01*
+X442775Y2008250D02*X443525Y2007500D01*
+X442775Y2012750D02*Y2008250D01*
+Y2012750D02*X443525Y2013500D01*
+X445025D01*
+X445775Y2012750D01*
+Y2008250D01*
+X445025Y2007500D02*X445775Y2008250D01*
+X443525Y2007500D02*X445025D01*
+X442775Y2009000D02*X445775Y2012000D01*
+X451026Y2009750D02*Y2007500D01*
+Y2009750D02*X451776Y2010500D01*
+X452526D01*
+X453276Y2009750D01*
+Y2007500D01*
+Y2009750D02*X454026Y2010500D01*
+X454776D01*
+X455526Y2009750D01*
+Y2007500D01*
+X450276Y2010500D02*X451026Y2009750D01*
+X457328Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X458829Y2013500D02*Y2008250D01*
+X459579Y2007500D01*
+X461831D02*X464081D01*
+X464831Y2008250D01*
+X464081Y2009000D02*X464831Y2008250D01*
+X461831Y2009000D02*X464081D01*
+X461081Y2009750D02*X461831Y2009000D01*
+X461081Y2009750D02*X461831Y2010500D01*
+X464081D01*
+X464831Y2009750D01*
+X461081Y2008250D02*X461831Y2007500D01*
+X469332Y2013500D02*Y2007500D01*
+Y2009750D02*X470082Y2010500D01*
+X471582D01*
+X472332Y2009750D01*
+Y2007500D01*
+X474134Y2012000D02*Y2011250D01*
+Y2009750D02*Y2007500D01*
+X477885Y2010500D02*X478635Y2009750D01*
+X476385Y2010500D02*X477885D01*
+X475635Y2009750D02*X476385Y2010500D01*
+X475635Y2009750D02*Y2008250D01*
+X476385Y2007500D01*
+X477885D01*
+X478635Y2008250D01*
+X475635Y2006000D02*X476385Y2005250D01*
+X477885D01*
+X478635Y2006000D01*
+Y2010500D02*Y2006000D01*
+X480437Y2013500D02*Y2007500D01*
+Y2009750D02*X481187Y2010500D01*
+X482687D01*
+X483437Y2009750D01*
+Y2007500D01*
+X1292034Y-9500D02*X1295034D01*
+X1295784Y-8750D01*
+Y-7250D02*Y-8750D01*
+X1295034Y-6500D02*X1295784Y-7250D01*
+X1292784Y-6500D02*X1295034D01*
+X1292784Y-3500D02*Y-9500D01*
+X1292034Y-3500D02*X1295034D01*
+X1295784Y-4250D01*
+Y-5750D01*
+X1295034Y-6500D02*X1295784Y-5750D01*
+X1297585Y-7250D02*Y-8750D01*
+Y-7250D02*X1298335Y-6500D01*
+X1299835D01*
+X1300585Y-7250D01*
+Y-8750D01*
+X1299835Y-9500D02*X1300585Y-8750D01*
+X1298335Y-9500D02*X1299835D01*
+X1297585Y-8750D02*X1298335Y-9500D01*
+X1304637Y-6500D02*X1305387Y-7250D01*
+X1303137Y-6500D02*X1304637D01*
+X1302387Y-7250D02*X1303137Y-6500D01*
+X1302387Y-7250D02*Y-8750D01*
+X1303137Y-9500D01*
+X1305387Y-6500D02*Y-8750D01*
+X1306137Y-9500D01*
+X1303137D02*X1304637D01*
+X1305387Y-8750D01*
+X1308688Y-7250D02*Y-9500D01*
+Y-7250D02*X1309438Y-6500D01*
+X1310938D01*
+X1307938D02*X1308688Y-7250D01*
+X1315740Y-3500D02*Y-9500D01*
+X1314990D02*X1315740Y-8750D01*
+X1313490Y-9500D02*X1314990D01*
+X1312740Y-8750D02*X1313490Y-9500D01*
+X1312740Y-7250D02*Y-8750D01*
+Y-7250D02*X1313490Y-6500D01*
+X1314990D01*
+X1315740Y-7250D01*
+X1320241D02*Y-8750D01*
+Y-7250D02*X1320991Y-6500D01*
+X1322491D01*
+X1323241Y-7250D01*
+Y-8750D01*
+X1322491Y-9500D02*X1323241Y-8750D01*
+X1320991Y-9500D02*X1322491D01*
+X1320241Y-8750D02*X1320991Y-9500D01*
+X1325043Y-6500D02*Y-8750D01*
+X1325793Y-9500D01*
+X1327293D01*
+X1328043Y-8750D01*
+Y-6500D02*Y-8750D01*
+X1330594Y-3500D02*Y-8750D01*
+X1331344Y-9500D01*
+X1329844Y-5750D02*X1331344D01*
+X1332846Y-3500D02*Y-8750D01*
+X1333596Y-9500D01*
+X1335097Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X1337349Y-7250D02*Y-9500D01*
+Y-7250D02*X1338099Y-6500D01*
+X1338849D01*
+X1339599Y-7250D01*
+Y-9500D01*
+X1336599Y-6500D02*X1337349Y-7250D01*
+X1342150Y-9500D02*X1344400D01*
+X1341400Y-8750D02*X1342150Y-9500D01*
+X1341400Y-7250D02*Y-8750D01*
+Y-7250D02*X1342150Y-6500D01*
+X1343650D01*
+X1344400Y-7250D01*
+X1341400Y-8000D02*X1344400D01*
+Y-7250D02*Y-8000D01*
+X1348902Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X1351153D02*X1353403D01*
+X1354153Y-8750D01*
+X1353403Y-8000D02*X1354153Y-8750D01*
+X1351153Y-8000D02*X1353403D01*
+X1350403Y-7250D02*X1351153Y-8000D01*
+X1350403Y-7250D02*X1351153Y-6500D01*
+X1353403D01*
+X1354153Y-7250D01*
+X1350403Y-8750D02*X1351153Y-9500D01*
+X1359405Y-3500D02*Y-8750D01*
+X1360155Y-9500D01*
+X1358655Y-5750D02*X1360155D01*
+X1361656Y-3500D02*Y-9500D01*
+Y-7250D02*X1362406Y-6500D01*
+X1363906D01*
+X1364656Y-7250D01*
+Y-9500D01*
+X1367208D02*X1369458D01*
+X1366458Y-8750D02*X1367208Y-9500D01*
+X1366458Y-7250D02*Y-8750D01*
+Y-7250D02*X1367208Y-6500D01*
+X1368708D01*
+X1369458Y-7250D01*
+X1366458Y-8000D02*X1369458D01*
+Y-7250D02*Y-8000D01*
+X1374709Y-6500D02*X1376959D01*
+X1373959Y-7250D02*X1374709Y-6500D01*
+X1373959Y-7250D02*Y-8750D01*
+X1374709Y-9500D01*
+X1376959D01*
+X1379511D02*X1381761D01*
+X1378761Y-8750D02*X1379511Y-9500D01*
+X1378761Y-7250D02*Y-8750D01*
+Y-7250D02*X1379511Y-6500D01*
+X1381011D01*
+X1381761Y-7250D01*
+X1378761Y-8000D02*X1381761D01*
+Y-7250D02*Y-8000D01*
+X1384312Y-7250D02*Y-9500D01*
+Y-7250D02*X1385062Y-6500D01*
+X1385812D01*
+X1386562Y-7250D01*
+Y-9500D01*
+X1383562Y-6500D02*X1384312Y-7250D01*
+X1389114Y-3500D02*Y-8750D01*
+X1389864Y-9500D01*
+X1388364Y-5750D02*X1389864D01*
+X1392115Y-9500D02*X1394365D01*
+X1391365Y-8750D02*X1392115Y-9500D01*
+X1391365Y-7250D02*Y-8750D01*
+Y-7250D02*X1392115Y-6500D01*
+X1393615D01*
+X1394365Y-7250D01*
+X1391365Y-8000D02*X1394365D01*
+Y-7250D02*Y-8000D01*
+X1396917Y-7250D02*Y-9500D01*
+Y-7250D02*X1397667Y-6500D01*
+X1399167D01*
+X1396167D02*X1396917Y-7250D01*
+X1400968Y-3500D02*Y-8750D01*
+X1401718Y-9500D01*
+X1403220Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X1405471Y-7250D02*Y-9500D01*
+Y-7250D02*X1406221Y-6500D01*
+X1406971D01*
+X1407721Y-7250D01*
+Y-9500D01*
+X1404721Y-6500D02*X1405471Y-7250D01*
+X1410273Y-9500D02*X1412523D01*
+X1409523Y-8750D02*X1410273Y-9500D01*
+X1409523Y-7250D02*Y-8750D01*
+Y-7250D02*X1410273Y-6500D01*
+X1411773D01*
+X1412523Y-7250D01*
+X1409523Y-8000D02*X1412523D01*
+Y-7250D02*Y-8000D01*
+X1417024Y-7250D02*Y-8750D01*
+Y-7250D02*X1417774Y-6500D01*
+X1419274D01*
+X1420024Y-7250D01*
+Y-8750D01*
+X1419274Y-9500D02*X1420024Y-8750D01*
+X1417774Y-9500D02*X1419274D01*
+X1417024Y-8750D02*X1417774Y-9500D01*
+X1422576Y-4250D02*Y-9500D01*
+Y-4250D02*X1423326Y-3500D01*
+X1424076D01*
+X1421826Y-6500D02*X1423326D01*
+X1429027Y-3500D02*Y-8750D01*
+X1429777Y-9500D01*
+X1428277Y-5750D02*X1429777D01*
+X1431279Y-3500D02*Y-9500D01*
+Y-7250D02*X1432029Y-6500D01*
+X1433529D01*
+X1434279Y-7250D01*
+Y-9500D01*
+X1436080Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X1438332D02*X1440582D01*
+X1441332Y-8750D01*
+X1440582Y-8000D02*X1441332Y-8750D01*
+X1438332Y-8000D02*X1440582D01*
+X1437582Y-7250D02*X1438332Y-8000D01*
+X1437582Y-7250D02*X1438332Y-6500D01*
+X1440582D01*
+X1441332Y-7250D01*
+X1437582Y-8750D02*X1438332Y-9500D01*
+X1445833Y-8750D02*X1446583Y-9500D01*
+X1445833Y-7250D02*Y-8750D01*
+Y-7250D02*X1446583Y-6500D01*
+X1448083D01*
+X1448833Y-7250D01*
+Y-8750D01*
+X1448083Y-9500D02*X1448833Y-8750D01*
+X1446583Y-9500D02*X1448083D01*
+X1445833Y-5750D02*X1446583Y-6500D01*
+X1445833Y-4250D02*Y-5750D01*
+Y-4250D02*X1446583Y-3500D01*
+X1448083D01*
+X1448833Y-4250D01*
+Y-5750D01*
+X1448083Y-6500D02*X1448833Y-5750D01*
+X1450635Y-9500D02*X1451385D01*
+X1453186Y-8750D02*X1453936Y-9500D01*
+X1453186Y-4250D02*Y-8750D01*
+Y-4250D02*X1453936Y-3500D01*
+X1455436D01*
+X1456186Y-4250D01*
+Y-8750D01*
+X1455436Y-9500D02*X1456186Y-8750D01*
+X1453936Y-9500D02*X1455436D01*
+X1453186Y-8000D02*X1456186Y-5000D01*
+X1457988Y-8750D02*X1458738Y-9500D01*
+X1457988Y-4250D02*Y-8750D01*
+Y-4250D02*X1458738Y-3500D01*
+X1460238D01*
+X1460988Y-4250D01*
+Y-8750D01*
+X1460238Y-9500D02*X1460988Y-8750D01*
+X1458738Y-9500D02*X1460238D01*
+X1457988Y-8000D02*X1460988Y-5000D01*
+X1462789Y-8750D02*X1463539Y-9500D01*
+X1462789Y-4250D02*Y-8750D01*
+Y-4250D02*X1463539Y-3500D01*
+X1465039D01*
+X1465789Y-4250D01*
+Y-8750D01*
+X1465039Y-9500D02*X1465789Y-8750D01*
+X1463539Y-9500D02*X1465039D01*
+X1462789Y-8000D02*X1465789Y-5000D01*
+X1467591Y-8750D02*X1468341Y-9500D01*
+X1467591Y-4250D02*Y-8750D01*
+Y-4250D02*X1468341Y-3500D01*
+X1469841D01*
+X1470591Y-4250D01*
+Y-8750D01*
+X1469841Y-9500D02*X1470591Y-8750D01*
+X1468341Y-9500D02*X1469841D01*
+X1467591Y-8000D02*X1470591Y-5000D01*
+X1472392Y-8750D02*X1473142Y-9500D01*
+X1472392Y-4250D02*Y-8750D01*
+Y-4250D02*X1473142Y-3500D01*
+X1474642D01*
+X1475392Y-4250D01*
+Y-8750D01*
+X1474642Y-9500D02*X1475392Y-8750D01*
+X1473142Y-9500D02*X1474642D01*
+X1472392Y-8000D02*X1475392Y-5000D01*
+X1477194Y-8750D02*X1477944Y-9500D01*
+X1477194Y-4250D02*Y-8750D01*
+Y-4250D02*X1477944Y-3500D01*
+X1479444D01*
+X1480194Y-4250D01*
+Y-8750D01*
+X1479444Y-9500D02*X1480194Y-8750D01*
+X1477944Y-9500D02*X1479444D01*
+X1477194Y-8000D02*X1480194Y-5000D01*
+X1485445Y-7250D02*Y-9500D01*
+Y-7250D02*X1486195Y-6500D01*
+X1486945D01*
+X1487695Y-7250D01*
+Y-9500D01*
+Y-7250D02*X1488445Y-6500D01*
+X1489195D01*
+X1489945Y-7250D01*
+Y-9500D01*
+X1484695Y-6500D02*X1485445Y-7250D01*
+X1491747Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X1493248Y-3500D02*Y-8750D01*
+X1493998Y-9500D01*
+X1498950Y-7250D02*Y-9500D01*
+Y-7250D02*X1499700Y-6500D01*
+X1501200D01*
+X1498200D02*X1498950Y-7250D01*
+X1503751Y-9500D02*X1506001D01*
+X1503001Y-8750D02*X1503751Y-9500D01*
+X1503001Y-7250D02*Y-8750D01*
+Y-7250D02*X1503751Y-6500D01*
+X1505251D01*
+X1506001Y-7250D01*
+X1503001Y-8000D02*X1506001D01*
+Y-7250D02*Y-8000D01*
+X1508553Y-6500D02*X1510803D01*
+X1507803Y-7250D02*X1508553Y-6500D01*
+X1507803Y-7250D02*Y-8750D01*
+X1508553Y-9500D01*
+X1510803D01*
+X1513354Y-3500D02*Y-8750D01*
+X1514104Y-9500D01*
+X1512604Y-5750D02*X1514104D01*
+X1517856Y-6500D02*X1518606Y-7250D01*
+X1516356Y-6500D02*X1517856D01*
+X1515606Y-7250D02*X1516356Y-6500D01*
+X1515606Y-7250D02*Y-8750D01*
+X1516356Y-9500D01*
+X1518606Y-6500D02*Y-8750D01*
+X1519356Y-9500D01*
+X1516356D02*X1517856D01*
+X1518606Y-8750D01*
+X1521907Y-7250D02*Y-9500D01*
+Y-7250D02*X1522657Y-6500D01*
+X1523407D01*
+X1524157Y-7250D01*
+Y-9500D01*
+X1521157Y-6500D02*X1521907Y-7250D01*
+X1528209Y-6500D02*X1528959Y-7250D01*
+X1526709Y-6500D02*X1528209D01*
+X1525959Y-7250D02*X1526709Y-6500D01*
+X1525959Y-7250D02*Y-8750D01*
+X1526709Y-9500D01*
+X1528209D01*
+X1528959Y-8750D01*
+X1525959Y-11000D02*X1526709Y-11750D01*
+X1528209D01*
+X1528959Y-11000D01*
+Y-6500D02*Y-11000D01*
+X1530760Y-3500D02*Y-8750D01*
+X1531510Y-9500D01*
+X1533762D02*X1536012D01*
+X1533012Y-8750D02*X1533762Y-9500D01*
+X1533012Y-7250D02*Y-8750D01*
+Y-7250D02*X1533762Y-6500D01*
+X1535262D01*
+X1536012Y-7250D01*
+X1533012Y-8000D02*X1536012D01*
+Y-7250D02*Y-8000D01*
+X1540513Y-6500D02*X1543513D01*
+X1548015Y-8750D02*X1548765Y-9500D01*
+X1548015Y-4250D02*Y-8750D01*
+Y-4250D02*X1548765Y-3500D01*
+X1550265D01*
+X1551015Y-4250D01*
+Y-8750D01*
+X1550265Y-9500D02*X1551015Y-8750D01*
+X1548765Y-9500D02*X1550265D01*
+X1548015Y-8000D02*X1551015Y-5000D01*
+X1552816Y-11000D02*X1554316Y-9500D01*
+X1556118Y-8750D02*X1556868Y-9500D01*
+X1556118Y-4250D02*Y-8750D01*
+Y-4250D02*X1556868Y-3500D01*
+X1558368D01*
+X1559118Y-4250D01*
+Y-8750D01*
+X1558368Y-9500D02*X1559118Y-8750D01*
+X1556868Y-9500D02*X1558368D01*
+X1556118Y-8000D02*X1559118Y-5000D01*
+X1564369Y-3500D02*Y-8750D01*
+X1565119Y-9500D01*
+X1563619Y-5750D02*X1565119D01*
+X1566621Y-7250D02*Y-8750D01*
+Y-7250D02*X1567371Y-6500D01*
+X1568871D01*
+X1569621Y-7250D01*
+Y-8750D01*
+X1568871Y-9500D02*X1569621Y-8750D01*
+X1567371Y-9500D02*X1568871D01*
+X1566621Y-8750D02*X1567371Y-9500D01*
+X1574122Y-4250D02*X1574872Y-3500D01*
+X1576372D01*
+X1577122Y-4250D01*
+Y-8750D01*
+X1576372Y-9500D02*X1577122Y-8750D01*
+X1574872Y-9500D02*X1576372D01*
+X1574122Y-8750D02*X1574872Y-9500D01*
+Y-6500D02*X1577122D01*
+X1578924Y-8750D02*X1579674Y-9500D01*
+X1578924Y-4250D02*Y-8750D01*
+Y-4250D02*X1579674Y-3500D01*
+X1581174D01*
+X1581924Y-4250D01*
+Y-8750D01*
+X1581174Y-9500D02*X1581924Y-8750D01*
+X1579674Y-9500D02*X1581174D01*
+X1578924Y-8000D02*X1581924Y-5000D01*
+X1583725Y-8750D02*X1584475Y-9500D01*
+X1583725Y-4250D02*Y-8750D01*
+Y-4250D02*X1584475Y-3500D01*
+X1585975D01*
+X1586725Y-4250D01*
+Y-8750D01*
+X1585975Y-9500D02*X1586725Y-8750D01*
+X1584475Y-9500D02*X1585975D01*
+X1583725Y-8000D02*X1586725Y-5000D01*
+X1588527Y-8750D02*X1589277Y-9500D01*
+X1588527Y-4250D02*Y-8750D01*
+Y-4250D02*X1589277Y-3500D01*
+X1590777D01*
+X1591527Y-4250D01*
+Y-8750D01*
+X1590777Y-9500D02*X1591527Y-8750D01*
+X1589277Y-9500D02*X1590777D01*
+X1588527Y-8000D02*X1591527Y-5000D01*
+X1593328Y-8750D02*X1594078Y-9500D01*
+X1593328Y-4250D02*Y-8750D01*
+Y-4250D02*X1594078Y-3500D01*
+X1595578D01*
+X1596328Y-4250D01*
+Y-8750D01*
+X1595578Y-9500D02*X1596328Y-8750D01*
+X1594078Y-9500D02*X1595578D01*
+X1593328Y-8000D02*X1596328Y-5000D01*
+X1598130Y-9500D02*X1598880D01*
+X1600681Y-8750D02*X1601431Y-9500D01*
+X1600681Y-4250D02*Y-8750D01*
+Y-4250D02*X1601431Y-3500D01*
+X1602931D01*
+X1603681Y-4250D01*
+Y-8750D01*
+X1602931Y-9500D02*X1603681Y-8750D01*
+X1601431Y-9500D02*X1602931D01*
+X1600681Y-8000D02*X1603681Y-5000D01*
+X1605483Y-8750D02*X1606233Y-9500D01*
+X1605483Y-4250D02*Y-8750D01*
+Y-4250D02*X1606233Y-3500D01*
+X1607733D01*
+X1608483Y-4250D01*
+Y-8750D01*
+X1607733Y-9500D02*X1608483Y-8750D01*
+X1606233Y-9500D02*X1607733D01*
+X1605483Y-8000D02*X1608483Y-5000D01*
+X1610284Y-8750D02*X1611034Y-9500D01*
+X1610284Y-4250D02*Y-8750D01*
+Y-4250D02*X1611034Y-3500D01*
+X1612534D01*
+X1613284Y-4250D01*
+Y-8750D01*
+X1612534Y-9500D02*X1613284Y-8750D01*
+X1611034Y-9500D02*X1612534D01*
+X1610284Y-8000D02*X1613284Y-5000D01*
+X1615086Y-8750D02*X1615836Y-9500D01*
+X1615086Y-4250D02*Y-8750D01*
+Y-4250D02*X1615836Y-3500D01*
+X1617336D01*
+X1618086Y-4250D01*
+Y-8750D01*
+X1617336Y-9500D02*X1618086Y-8750D01*
+X1615836Y-9500D02*X1617336D01*
+X1615086Y-8000D02*X1618086Y-5000D01*
+X1619887Y-8750D02*X1620637Y-9500D01*
+X1619887Y-4250D02*Y-8750D01*
+Y-4250D02*X1620637Y-3500D01*
+X1622137D01*
+X1622887Y-4250D01*
+Y-8750D01*
+X1622137Y-9500D02*X1622887Y-8750D01*
+X1620637Y-9500D02*X1622137D01*
+X1619887Y-8000D02*X1622887Y-5000D01*
+X1624689Y-8750D02*X1625439Y-9500D01*
+X1624689Y-4250D02*Y-8750D01*
+Y-4250D02*X1625439Y-3500D01*
+X1626939D01*
+X1627689Y-4250D01*
+Y-8750D01*
+X1626939Y-9500D02*X1627689Y-8750D01*
+X1625439Y-9500D02*X1626939D01*
+X1624689Y-8000D02*X1627689Y-5000D01*
+X1629490Y-11000D02*X1630990Y-9500D01*
+X1632792Y-4250D02*X1633542Y-3500D01*
+X1635792D01*
+X1636542Y-4250D01*
+Y-5750D01*
+X1632792Y-9500D02*X1636542Y-5750D01*
+X1632792Y-9500D02*X1636542D01*
+X1638343Y-8750D02*X1639093Y-9500D01*
+X1638343Y-4250D02*Y-8750D01*
+Y-4250D02*X1639093Y-3500D01*
+X1640593D01*
+X1641343Y-4250D01*
+Y-8750D01*
+X1640593Y-9500D02*X1641343Y-8750D01*
+X1639093Y-9500D02*X1640593D01*
+X1638343Y-8000D02*X1641343Y-5000D01*
+X1643145Y-8750D02*X1643895Y-9500D01*
+X1643145Y-4250D02*Y-8750D01*
+Y-4250D02*X1643895Y-3500D01*
+X1645395D01*
+X1646145Y-4250D01*
+Y-8750D01*
+X1645395Y-9500D02*X1646145Y-8750D01*
+X1643895Y-9500D02*X1645395D01*
+X1643145Y-8000D02*X1646145Y-5000D01*
+X1647946Y-8750D02*X1648696Y-9500D01*
+X1647946Y-4250D02*Y-8750D01*
+Y-4250D02*X1648696Y-3500D01*
+X1650196D01*
+X1650946Y-4250D01*
+Y-8750D01*
+X1650196Y-9500D02*X1650946Y-8750D01*
+X1648696Y-9500D02*X1650196D01*
+X1647946Y-8000D02*X1650946Y-5000D01*
+X1652748Y-8750D02*X1653498Y-9500D01*
+X1652748Y-4250D02*Y-8750D01*
+Y-4250D02*X1653498Y-3500D01*
+X1654998D01*
+X1655748Y-4250D01*
+Y-8750D01*
+X1654998Y-9500D02*X1655748Y-8750D01*
+X1653498Y-9500D02*X1654998D01*
+X1652748Y-8000D02*X1655748Y-5000D01*
+X1657549Y-9500D02*X1658299D01*
+X1660101Y-8750D02*X1660851Y-9500D01*
+X1660101Y-4250D02*Y-8750D01*
+Y-4250D02*X1660851Y-3500D01*
+X1662351D01*
+X1663101Y-4250D01*
+Y-8750D01*
+X1662351Y-9500D02*X1663101Y-8750D01*
+X1660851Y-9500D02*X1662351D01*
+X1660101Y-8000D02*X1663101Y-5000D01*
+X1664902Y-8750D02*X1665652Y-9500D01*
+X1664902Y-4250D02*Y-8750D01*
+Y-4250D02*X1665652Y-3500D01*
+X1667152D01*
+X1667902Y-4250D01*
+Y-8750D01*
+X1667152Y-9500D02*X1667902Y-8750D01*
+X1665652Y-9500D02*X1667152D01*
+X1664902Y-8000D02*X1667902Y-5000D01*
+X1669704Y-8750D02*X1670454Y-9500D01*
+X1669704Y-4250D02*Y-8750D01*
+Y-4250D02*X1670454Y-3500D01*
+X1671954D01*
+X1672704Y-4250D01*
+Y-8750D01*
+X1671954Y-9500D02*X1672704Y-8750D01*
+X1670454Y-9500D02*X1671954D01*
+X1669704Y-8000D02*X1672704Y-5000D01*
+X1674505Y-8750D02*X1675255Y-9500D01*
+X1674505Y-4250D02*Y-8750D01*
+Y-4250D02*X1675255Y-3500D01*
+X1676755D01*
+X1677505Y-4250D01*
+Y-8750D01*
+X1676755Y-9500D02*X1677505Y-8750D01*
+X1675255Y-9500D02*X1676755D01*
+X1674505Y-8000D02*X1677505Y-5000D01*
+X1679307Y-8750D02*X1680057Y-9500D01*
+X1679307Y-4250D02*Y-8750D01*
+Y-4250D02*X1680057Y-3500D01*
+X1681557D01*
+X1682307Y-4250D01*
+Y-8750D01*
+X1681557Y-9500D02*X1682307Y-8750D01*
+X1680057Y-9500D02*X1681557D01*
+X1679307Y-8000D02*X1682307Y-5000D01*
+X1684108Y-8750D02*X1684858Y-9500D01*
+X1684108Y-4250D02*Y-8750D01*
+Y-4250D02*X1684858Y-3500D01*
+X1686358D01*
+X1687108Y-4250D01*
+Y-8750D01*
+X1686358Y-9500D02*X1687108Y-8750D01*
+X1684858Y-9500D02*X1686358D01*
+X1684108Y-8000D02*X1687108Y-5000D01*
+X1692360Y-7250D02*Y-9500D01*
+Y-7250D02*X1693110Y-6500D01*
+X1693860D01*
+X1694610Y-7250D01*
+Y-9500D01*
+Y-7250D02*X1695360Y-6500D01*
+X1696110D01*
+X1696860Y-7250D01*
+Y-9500D01*
+X1691610Y-6500D02*X1692360Y-7250D01*
+X1698661Y-5000D02*Y-5750D01*
+Y-7250D02*Y-9500D01*
+X1700163Y-3500D02*Y-8750D01*
+X1700913Y-9500D01*
+X1703164D02*X1705414D01*
+X1706164Y-8750D01*
+X1705414Y-8000D02*X1706164Y-8750D01*
+X1703164Y-8000D02*X1705414D01*
+X1702414Y-7250D02*X1703164Y-8000D01*
+X1702414Y-7250D02*X1703164Y-6500D01*
+X1705414D01*
+X1706164Y-7250D01*
+X1702414Y-8750D02*X1703164Y-9500D01*
+X200750Y2028500D02*Y2022500D01*
+X203000Y2028500D02*X203750Y2027750D01*
+Y2023250D01*
+X203000Y2022500D02*X203750Y2023250D01*
+X200000Y2022500D02*X203000D01*
+X200000Y2028500D02*X203000D01*
+X207801Y2025500D02*X208551Y2024750D01*
+X206301Y2025500D02*X207801D01*
+X205551Y2024750D02*X206301Y2025500D01*
+X205551Y2024750D02*Y2023250D01*
+X206301Y2022500D01*
+X208551Y2025500D02*Y2023250D01*
+X209301Y2022500D01*
+X206301D02*X207801D01*
+X208551Y2023250D01*
+X211853Y2028500D02*Y2023250D01*
+X212603Y2022500D01*
+X211103Y2026250D02*X212603D01*
+X214854Y2022500D02*X217104D01*
+X214104Y2023250D02*X214854Y2022500D01*
+X214104Y2024750D02*Y2023250D01*
+Y2024750D02*X214854Y2025500D01*
+X216354D01*
+X217104Y2024750D01*
+X214104Y2024000D02*X217104D01*
+Y2024750D02*Y2024000D01*
+X218906Y2026250D02*X219656D01*
+X218906Y2024750D02*X219656D01*
+X224157Y2028500D02*Y2022500D01*
+Y2028500D02*X227157D01*
+X224157Y2025500D02*X226407D01*
+X229709Y2024750D02*Y2022500D01*
+Y2024750D02*X230459Y2025500D01*
+X231959D01*
+X228959D02*X229709Y2024750D01*
+X233760Y2027000D02*Y2026250D01*
+Y2024750D02*Y2022500D01*
+X237962Y2028500D02*X240212D01*
+Y2023250D01*
+X239462Y2022500D02*X240212Y2023250D01*
+X238712Y2022500D02*X239462D01*
+X237962Y2023250D02*X238712Y2022500D01*
+X242013Y2025500D02*Y2023250D01*
+X242763Y2022500D01*
+X244263D01*
+X245013Y2023250D01*
+Y2025500D02*Y2023250D01*
+X247565Y2024750D02*Y2022500D01*
+Y2024750D02*X248315Y2025500D01*
+X249065D01*
+X249815Y2024750D01*
+Y2022500D01*
+X246815Y2025500D02*X247565Y2024750D01*
+X255066Y2022500D02*X256566D01*
+X255816Y2028500D02*Y2022500D01*
+X254316Y2027000D02*X255816Y2028500D01*
+X258368Y2022500D02*X262118Y2026250D01*
+Y2028500D02*Y2026250D01*
+X258368Y2028500D02*X262118D01*
+X266619Y2023250D02*X267369Y2022500D01*
+X266619Y2027750D02*Y2023250D01*
+Y2027750D02*X267369Y2028500D01*
+X268869D01*
+X269619Y2027750D01*
+Y2023250D01*
+X268869Y2022500D02*X269619Y2023250D01*
+X267369Y2022500D02*X268869D01*
+X266619Y2024000D02*X269619Y2027000D01*
+X271421Y2027750D02*X272171Y2028500D01*
+X273671D01*
+X274421Y2027750D01*
+Y2023250D01*
+X273671Y2022500D02*X274421Y2023250D01*
+X272171Y2022500D02*X273671D01*
+X271421Y2023250D02*X272171Y2022500D01*
+Y2025500D02*X274421D01*
+X276222Y2026250D02*X276972D01*
+X276222Y2024750D02*X276972D01*
+X279524Y2022500D02*X281024D01*
+X280274Y2028500D02*Y2022500D01*
+X278774Y2027000D02*X280274Y2028500D01*
+X282825Y2023250D02*X283575Y2022500D01*
+X282825Y2024750D02*Y2023250D01*
+Y2024750D02*X283575Y2025500D01*
+X285075D01*
+X285825Y2024750D01*
+Y2023250D01*
+X285075Y2022500D02*X285825Y2023250D01*
+X283575Y2022500D02*X285075D01*
+X282825Y2026250D02*X283575Y2025500D01*
+X282825Y2027750D02*Y2026250D01*
+Y2027750D02*X283575Y2028500D01*
+X285075D01*
+X285825Y2027750D01*
+Y2026250D01*
+X285075Y2025500D02*X285825Y2026250D01*
+X287627D02*X288377D01*
+X287627Y2024750D02*X288377D01*
+X290178Y2028500D02*X293178D01*
+X290178D02*Y2025500D01*
+X290928Y2026250D01*
+X292428D01*
+X293178Y2025500D01*
+Y2023250D01*
+X292428Y2022500D02*X293178Y2023250D01*
+X290928Y2022500D02*X292428D01*
+X290178Y2023250D02*X290928Y2022500D01*
+X295730D02*X297230D01*
+X296480Y2028500D02*Y2022500D01*
+X294980Y2027000D02*X296480Y2028500D01*
+X301731Y2027750D02*X302481Y2028500D01*
+X304731D01*
+X305481Y2027750D01*
+Y2026250D01*
+X301731Y2022500D02*X305481Y2026250D01*
+X301731Y2022500D02*X305481D01*
+X307283Y2023250D02*X308033Y2022500D01*
+X307283Y2027750D02*Y2023250D01*
+Y2027750D02*X308033Y2028500D01*
+X309533D01*
+X310283Y2027750D01*
+Y2023250D01*
+X309533Y2022500D02*X310283Y2023250D01*
+X308033Y2022500D02*X309533D01*
+X307283Y2024000D02*X310283Y2027000D01*
+X312834Y2022500D02*X314334D01*
+X313584Y2028500D02*Y2022500D01*
+X312084Y2027000D02*X313584Y2028500D01*
+X316886Y2022500D02*X318386D01*
+X317636Y2028500D02*Y2022500D01*
+X316136Y2027000D02*X317636Y2028500D01*
+X322887D02*Y2023250D01*
+X323637Y2022500D01*
+X325137D01*
+X325887Y2023250D01*
+Y2028500D02*Y2023250D01*
+X327689Y2028500D02*X330689D01*
+X329189D02*Y2022500D01*
+X333240D02*X335490D01*
+X332490Y2023250D02*X333240Y2022500D01*
+X332490Y2027750D02*Y2023250D01*
+Y2027750D02*X333240Y2028500D01*
+X335490D01*
+X200000Y2042750D02*Y2037500D01*
+Y2042750D02*X200750Y2043500D01*
+X203000D01*
+X203750Y2042750D01*
+Y2037500D01*
+X200000Y2040500D02*X203750D01*
+X205551D02*Y2038250D01*
+X206301Y2037500D01*
+X207801D01*
+X208551Y2038250D01*
+Y2040500D02*Y2038250D01*
+X211103Y2043500D02*Y2038250D01*
+X211853Y2037500D01*
+X210353Y2041250D02*X211853D01*
+X213354Y2043500D02*Y2037500D01*
+Y2039750D02*X214104Y2040500D01*
+X215604D01*
+X216354Y2039750D01*
+Y2037500D01*
+X218156Y2039750D02*Y2038250D01*
+Y2039750D02*X218906Y2040500D01*
+X220406D01*
+X221156Y2039750D01*
+Y2038250D01*
+X220406Y2037500D02*X221156Y2038250D01*
+X218906Y2037500D02*X220406D01*
+X218156Y2038250D02*X218906Y2037500D01*
+X223707Y2039750D02*Y2037500D01*
+Y2039750D02*X224457Y2040500D01*
+X225957D01*
+X222957D02*X223707Y2039750D01*
+X227759Y2041250D02*X228509D01*
+X227759Y2039750D02*X228509D01*
+X233010Y2043500D02*Y2037500D01*
+Y2043500D02*X236010D01*
+X233010Y2040500D02*X235260D01*
+X240062D02*X240812Y2039750D01*
+X238562Y2040500D02*X240062D01*
+X237812Y2039750D02*X238562Y2040500D01*
+X237812Y2039750D02*Y2038250D01*
+X238562Y2037500D01*
+X240812Y2040500D02*Y2038250D01*
+X241562Y2037500D01*
+X238562D02*X240062D01*
+X240812Y2038250D01*
+X243363Y2043500D02*Y2037500D01*
+Y2038250D02*X244113Y2037500D01*
+X245613D01*
+X246363Y2038250D01*
+Y2039750D02*Y2038250D01*
+X245613Y2040500D02*X246363Y2039750D01*
+X244113Y2040500D02*X245613D01*
+X243363Y2039750D02*X244113Y2040500D01*
+X248165Y2037500D02*X251165D01*
+X252966Y2042750D02*Y2037500D01*
+Y2042750D02*X253716Y2043500D01*
+X255966D01*
+X256716Y2042750D01*
+Y2037500D01*
+X252966Y2040500D02*X256716D01*
+X258518D02*Y2038250D01*
+X259268Y2037500D01*
+X260768D01*
+X261518Y2038250D01*
+Y2040500D02*Y2038250D01*
+X264069Y2043500D02*Y2038250D01*
+X264819Y2037500D01*
+X263319Y2041250D02*X264819D01*
+X266321Y2043500D02*Y2037500D01*
+Y2039750D02*X267071Y2040500D01*
+X268571D01*
+X269321Y2039750D01*
+Y2037500D01*
+X271122Y2039750D02*Y2038250D01*
+Y2039750D02*X271872Y2040500D01*
+X273372D01*
+X274122Y2039750D01*
+Y2038250D01*
+X273372Y2037500D02*X274122Y2038250D01*
+X271872Y2037500D02*X273372D01*
+X271122Y2038250D02*X271872Y2037500D01*
+X276674Y2039750D02*Y2037500D01*
+Y2039750D02*X277424Y2040500D01*
+X278924D01*
+X275924D02*X276674Y2039750D01*
+X200000Y2058500D02*X203000D01*
+X201500D02*Y2052500D01*
+X204801Y2057000D02*Y2056250D01*
+Y2054750D02*Y2052500D01*
+X207053Y2058500D02*Y2053250D01*
+X207803Y2052500D01*
+X206303Y2056250D02*X207803D01*
+X209304Y2058500D02*Y2053250D01*
+X210054Y2052500D01*
+X212306D02*X214556D01*
+X211556Y2053250D02*X212306Y2052500D01*
+X211556Y2054750D02*Y2053250D01*
+Y2054750D02*X212306Y2055500D01*
+X213806D01*
+X214556Y2054750D01*
+X211556Y2054000D02*X214556D01*
+Y2054750D02*Y2054000D01*
+X216357Y2056250D02*X217107D01*
+X216357Y2054750D02*X217107D01*
+X221609Y2053250D02*X222359Y2052500D01*
+X221609Y2057750D02*X222359Y2058500D01*
+X221609Y2057750D02*Y2053250D01*
+X224160Y2055500D02*Y2053250D01*
+X224910Y2052500D01*
+X226410D01*
+X227160Y2053250D01*
+Y2055500D02*Y2053250D01*
+X229712Y2054750D02*Y2052500D01*
+Y2054750D02*X230462Y2055500D01*
+X231212D01*
+X231962Y2054750D01*
+Y2052500D01*
+X228962Y2055500D02*X229712Y2054750D01*
+X233763Y2058500D02*Y2052500D01*
+Y2054750D02*X236013Y2052500D01*
+X233763Y2054750D02*X235263Y2056250D01*
+X238565Y2054750D02*Y2052500D01*
+Y2054750D02*X239315Y2055500D01*
+X240065D01*
+X240815Y2054750D01*
+Y2052500D01*
+X237815Y2055500D02*X238565Y2054750D01*
+X242616D02*Y2053250D01*
+Y2054750D02*X243366Y2055500D01*
+X244866D01*
+X245616Y2054750D01*
+Y2053250D01*
+X244866Y2052500D02*X245616Y2053250D01*
+X243366Y2052500D02*X244866D01*
+X242616Y2053250D02*X243366Y2052500D01*
+X247418Y2055500D02*Y2053250D01*
+X248168Y2052500D01*
+X248918D01*
+X249668Y2053250D01*
+Y2055500D02*Y2053250D01*
+X250418Y2052500D01*
+X251168D01*
+X251918Y2053250D01*
+Y2055500D02*Y2053250D01*
+X254469Y2054750D02*Y2052500D01*
+Y2054750D02*X255219Y2055500D01*
+X255969D01*
+X256719Y2054750D01*
+Y2052500D01*
+X253719Y2055500D02*X254469Y2054750D01*
+X258521Y2058500D02*X259271Y2057750D01*
+Y2053250D01*
+X258521Y2052500D02*X259271Y2053250D01*
+X263772Y2055500D02*X266772D01*
+X271274Y2058500D02*Y2052500D01*
+Y2058500D02*X274274D01*
+X271274Y2055500D02*X273524D01*
+X278325D02*X279075Y2054750D01*
+X276825Y2055500D02*X278325D01*
+X276075Y2054750D02*X276825Y2055500D01*
+X276075Y2054750D02*Y2053250D01*
+X276825Y2052500D01*
+X279075Y2055500D02*Y2053250D01*
+X279825Y2052500D01*
+X276825D02*X278325D01*
+X279075Y2053250D01*
+X281627Y2058500D02*Y2052500D01*
+Y2053250D02*X282377Y2052500D01*
+X283877D01*
+X284627Y2053250D01*
+Y2054750D02*Y2053250D01*
+X283877Y2055500D02*X284627Y2054750D01*
+X282377Y2055500D02*X283877D01*
+X281627Y2054750D02*X282377Y2055500D01*
+X287178Y2054750D02*Y2052500D01*
+Y2054750D02*X287928Y2055500D01*
+X289428D01*
+X286428D02*X287178Y2054750D01*
+X291230Y2057000D02*Y2056250D01*
+Y2054750D02*Y2052500D01*
+X293481Y2055500D02*X295731D01*
+X292731Y2054750D02*X293481Y2055500D01*
+X292731Y2054750D02*Y2053250D01*
+X293481Y2052500D01*
+X295731D01*
+X299783Y2055500D02*X300533Y2054750D01*
+X298283Y2055500D02*X299783D01*
+X297533Y2054750D02*X298283Y2055500D01*
+X297533Y2054750D02*Y2053250D01*
+X298283Y2052500D01*
+X300533Y2055500D02*Y2053250D01*
+X301283Y2052500D01*
+X298283D02*X299783D01*
+X300533Y2053250D01*
+X303834Y2058500D02*Y2053250D01*
+X304584Y2052500D01*
+X303084Y2056250D02*X304584D01*
+X306086Y2057000D02*Y2056250D01*
+Y2054750D02*Y2052500D01*
+X307587Y2054750D02*Y2053250D01*
+Y2054750D02*X308337Y2055500D01*
+X309837D01*
+X310587Y2054750D01*
+Y2053250D01*
+X309837Y2052500D02*X310587Y2053250D01*
+X308337Y2052500D02*X309837D01*
+X307587Y2053250D02*X308337Y2052500D01*
+X313139Y2054750D02*Y2052500D01*
+Y2054750D02*X313889Y2055500D01*
+X314639D01*
+X315389Y2054750D01*
+Y2052500D01*
+X312389Y2055500D02*X313139Y2054750D01*
+X320640Y2058500D02*Y2052500D01*
+X322890Y2058500D02*X323640Y2057750D01*
+Y2053250D01*
+X322890Y2052500D02*X323640Y2053250D01*
+X319890Y2052500D02*X322890D01*
+X319890Y2058500D02*X322890D01*
+X326192Y2054750D02*Y2052500D01*
+Y2054750D02*X326942Y2055500D01*
+X328442D01*
+X325442D02*X326192Y2054750D01*
+X332493Y2055500D02*X333243Y2054750D01*
+X330993Y2055500D02*X332493D01*
+X330243Y2054750D02*X330993Y2055500D01*
+X330243Y2054750D02*Y2053250D01*
+X330993Y2052500D01*
+X333243Y2055500D02*Y2053250D01*
+X333993Y2052500D01*
+X330993D02*X332493D01*
+X333243Y2053250D01*
+X335795Y2055500D02*Y2053250D01*
+X336545Y2052500D01*
+X337295D01*
+X338045Y2053250D01*
+Y2055500D02*Y2053250D01*
+X338795Y2052500D01*
+X339545D01*
+X340295Y2053250D01*
+Y2055500D02*Y2053250D01*
+X342096Y2057000D02*Y2056250D01*
+Y2054750D02*Y2052500D01*
+X344348Y2054750D02*Y2052500D01*
+Y2054750D02*X345098Y2055500D01*
+X345848D01*
+X346598Y2054750D01*
+Y2052500D01*
+X343598Y2055500D02*X344348Y2054750D01*
+X350649Y2055500D02*X351399Y2054750D01*
+X349149Y2055500D02*X350649D01*
+X348399Y2054750D02*X349149Y2055500D01*
+X348399Y2054750D02*Y2053250D01*
+X349149Y2052500D01*
+X350649D01*
+X351399Y2053250D01*
+X348399Y2051000D02*X349149Y2050250D01*
+X350649D01*
+X351399Y2051000D01*
+Y2055500D02*Y2051000D01*
+M02*
diff --git a/tests/orig/golden/hid_gerber3/arcs.group1.gbr b/tests/orig/golden/hid_gerber3/arcs.group1.gbr
new file mode 100644
index 0000000..a47422e
--- /dev/null
+++ b/tests/orig/golden/hid_gerber3/arcs.group1.gbr
@@ -0,0 +1,28 @@
+G04 start of page 3 for group 1 idx 1 *
+G04 Title: (unknown), ground *
+G04 Creator: pcb 1.99z *
+G04 CreationDate: Fri Jun 17 03:18:51 2011 UTC *
+G04 For: apoelstra *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 3000000 2000000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNGROUP1*%
+%ADD22C,0.6500*%
+%ADD21C,0.7500*%
+%ADD20C,1.0000*%
+%ADD19C,0.5000*%
+%ADD18C,0.2500*%
+%ADD17C,0.1000*%
+G54D17*X1497000Y1791000D02*G75*G03X2430000Y858000I933000J0D01*G01*
+G54D18*X1861000Y1776000D02*G75*G02X2415000Y1222000I0J-554000D01*G01*
+G54D19*X1845000Y1608000D02*G75*G02X2203000Y1250000I0J-358000D01*G01*
+G54D20*X1845000Y1455000D02*G75*G02X2029000Y1271000I0J-184000D01*G01*
+G54D21*X600000Y450000D03*
+G54D22*Y290000D03*
+G54D20*X450000Y600000D03*
+X600000D03*
+G54D22*X450000Y290000D03*
+G54D21*Y450000D03*
+G54D19*G54D18*G54D19*G54D18*G54D19*M02*
diff --git a/tests/orig/golden/hid_gerber3/arcs.group4.gbr b/tests/orig/golden/hid_gerber3/arcs.group4.gbr
new file mode 100644
index 0000000..b25d74b
--- /dev/null
+++ b/tests/orig/golden/hid_gerber3/arcs.group4.gbr
@@ -0,0 +1,48 @@
+G04 start of page 4 for group 4 idx 4 *
+G04 Title: (unknown), power *
+G04 Creator: pcb 1.99z *
+G04 CreationDate: Fri Jun 17 03:18:51 2011 UTC *
+G04 For: apoelstra *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 3000000 2000000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNGROUP4*%
+%ADD27C,0.5000*%
+%ADD26C,1.0000*%
+%ADD25C,0.6500*%
+%ADD24C,0.7500*%
+%ADD23C,0.2500*%
+G54D23*X1300000Y630000D02*X1302367Y604884D01*
+X1309462Y579867D01*
+X1321255Y555048D01*
+X1337700Y530525D01*
+X1358732Y506394D01*
+X1384268Y482751D01*
+X1414207Y459689D01*
+X1448431Y437299D01*
+X1486806Y415670D01*
+X1529179Y394886D01*
+X1575384Y375031D01*
+X1625237Y356182D01*
+X1678543Y338413D01*
+X1735091Y321795D01*
+X1794657Y306394D01*
+X1857007Y292269D01*
+X1921895Y279478D01*
+X1989064Y268070D01*
+X2058250Y258090D01*
+X2129179Y249578D01*
+X2201572Y242567D01*
+X2275142Y237086D01*
+X2349600Y233155D01*
+X2424651Y230790D01*
+X2499999Y230000D01*
+G54D24*X600000Y450000D03*
+G54D25*Y290000D03*
+G54D26*X450000Y600000D03*
+X600000D03*
+G54D25*X450000Y290000D03*
+G54D24*Y450000D03*
+G54D27*G54D23*G54D27*G54D23*G54D27*M02*
diff --git a/tests/orig/golden/hid_gerber3/arcs.plated-drill.cnc b/tests/orig/golden/hid_gerber3/arcs.plated-drill.cnc
new file mode 100644
index 0000000..e6f7163
--- /dev/null
+++ b/tests/orig/golden/hid_gerber3/arcs.plated-drill.cnc
@@ -0,0 +1,14 @@
+M48
+INCH
+T37C0.250
+T36C0.500
+%
+T37
+X060000Y029000
+X045000Y029000
+T36
+X060000Y045000
+X045000Y060000
+X060000Y060000
+X045000Y045000
+M30
diff --git a/tests/orig/golden/hid_gerber3/arcs.top.gbr b/tests/orig/golden/hid_gerber3/arcs.top.gbr
new file mode 100644
index 0000000..a15e824
--- /dev/null
+++ b/tests/orig/golden/hid_gerber3/arcs.top.gbr
@@ -0,0 +1,28 @@
+G04 start of page 2 for group 0 idx 0 *
+G04 Title: (unknown), top *
+G04 Creator: pcb 1.99z *
+G04 CreationDate: Fri Jun 17 03:18:51 2011 UTC *
+G04 For: apoelstra *
+G04 Format: Gerber/RS-274X *
+G04 PCB-Dimensions: 3000000 2000000 *
+G04 PCB-Coordinate-Origin: lower left *
+%MOIN*%
+%FSLAX25Y25*%
+%LNTOP*%
+%ADD16C,0.2500*%
+%ADD15C,0.5000*%
+%ADD14C,1.0000*%
+%ADD13C,0.6500*%
+%ADD12C,0.7500*%
+%ADD11C,0.0100*%
+G54D11*X1266000Y1791000D02*G75*G02X775000Y1300000I-491000J0D01*G01*
+X772000Y1303000D02*G75*G03X286000Y817000I0J-486000D01*G01*
+X285999D02*G75*G03X772000Y1303000I0J486000D01*G01*
+X775000Y1300000D02*G75*G02X1266000Y1791000I491000J0D01*G01*
+G54D12*X600000Y450000D03*
+G54D13*Y290000D03*
+G54D14*X450000Y600000D03*
+X600000D03*
+G54D13*X450000Y290000D03*
+G54D12*Y450000D03*
+G54D15*G54D16*G54D15*G54D16*G54D15*M02*
diff --git a/tests/orig/golden/hid_png1/gerber_oneline.png b/tests/orig/golden/hid_png1/gerber_oneline.png
new file mode 100644
index 0000000..6f30ea9
Binary files /dev/null and b/tests/orig/golden/hid_png1/gerber_oneline.png differ
diff --git a/tests/orig/golden/hid_png2/myfile.png b/tests/orig/golden/hid_png2/myfile.png
new file mode 100644
index 0000000..6f30ea9
Binary files /dev/null and b/tests/orig/golden/hid_png2/myfile.png differ
diff --git a/tests/orig/golden/hid_png3/gerber_oneline.png b/tests/orig/golden/hid_png3/gerber_oneline.png
new file mode 100644
index 0000000..9c5e865
Binary files /dev/null and b/tests/orig/golden/hid_png3/gerber_oneline.png differ
diff --git a/tests/orig/inputs/bom_general.pcb b/tests/orig/inputs/bom_general.pcb
new file mode 100644
index 0000000..d11ffe8
--- /dev/null
+++ b/tests/orig/inputs/bom_general.pcb
@@ -0,0 +1,1211 @@
+# release: pcb 1.99y
+
+# To read pcb files, the pcb version (or the cvs source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["Basic BOM/XY Test" 600000 500000]
+
+Grid[10000.000000 0 0 1]
+Cursor[0 500000 0.000000]
+PolyArea[200000000.000000]
+Thermal[0.500000]
+DRC[1000 1000 1000 1000 1500 1000]
+Flags("nameonpcb,uniquename,clearnew,snappin")
+Groups("1,c:2,s:3:4:5:6:7:8")
+Styles["Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"]
+
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 45 0 50 8)
+	SymbolLine(0 10 0 35 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 10 0 20 8)
+	SymbolLine(10 10 10 20 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 35 20 35 8)
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(15 20 15 40 8)
+	SymbolLine(5 20 5 40 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 40 8)
+	SymbolLine(15 45 20 40 8)
+	SymbolLine(5 45 15 45 8)
+	SymbolLine(0 40 5 45 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(10 25 15 20 8)
+	SymbolLine(5 25 10 25 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(0 50 40 10 8)
+	SymbolLine(35 50 40 45 8)
+	SymbolLine(40 40 40 45 8)
+	SymbolLine(35 35 40 40 8)
+	SymbolLine(30 35 35 35 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(25 40 25 45 8)
+	SymbolLine(25 45 30 50 8)
+	SymbolLine(30 50 35 50 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 35 15 20 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(0 25 25 50 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(0 35 0 45 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 60 10 50 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 30 20 30 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 45 30 15 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(10 10 10 50 8)
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 25 30 8)
+	SymbolLine(20 10 20 50 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 10 0 30 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(5 25 15 25 8)
+	SymbolLine(15 25 20 30 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(25 10 25 25 8)
+	SymbolLine(0 10 25 10 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 25 8)
+	SymbolLine(15 30 20 25 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(20 15 20 30 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 25 5 25 8)
+	SymbolLine(0 35 5 35 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 50 10 40 8)
+	SymbolLine(10 25 10 30 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 30 10 20 8)
+	SymbolLine(0 30 10 40 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 35 20 35 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 20 10 30 8)
+	SymbolLine(0 40 10 30 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 30 10 35 8)
+	SymbolLine(10 45 10 50 8)
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 20 8)
+	SymbolLine(10 30 20 20 8)
+)
+Symbol('@' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 40 50 8)
+	SymbolLine(50 35 50 10 8)
+	SymbolLine(50 10 40 0 8)
+	SymbolLine(40 0 10 0 8)
+	SymbolLine(10 0 0 10 8)
+	SymbolLine(15 20 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 30 35 8)
+	SymbolLine(30 35 35 30 8)
+	SymbolLine(35 30 40 35 8)
+	SymbolLine(35 30 35 15 8)
+	SymbolLine(35 20 30 15 8)
+	SymbolLine(20 15 30 15 8)
+	SymbolLine(20 15 15 20 8)
+	SymbolLine(40 35 50 35 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine(0 15 0 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 30 15 30 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(10 30 20 30 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(25 10 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 10 10 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 10 50 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine(0 10 15 10 8)
+	SymbolLine(15 10 15 45 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 20 50 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 15 25 8)
+	SymbolLine(15 25 30 10 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 10 25 50 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(10 40 20 50 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(5 30 25 50 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 10 20 45 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 10 20 40 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 15 35 8)
+	SymbolLine(15 35 30 50 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 40 25 50 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(25 10 25 15 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 10 25 8)
+	SymbolLine(10 25 20 15 8)
+	SymbolLine(20 10 20 15 8)
+	SymbolLine(10 25 10 50 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine(0 10 25 10 8)
+	SymbolLine(25 10 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 15 30 45 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 15 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(20 45 25 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 10 20 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(20 40 20 35 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 15 5 50 8)
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(10 10 15 10 8)
+	SymbolLine(0 30 10 30 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(20 30 20 60 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 35 0 50 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 20 5 25 8)
+	SymbolLine(5 35 5 60 8)
+	SymbolLine(0 65 5 60 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 15 50 8)
+	SymbolLine(0 35 10 25 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(20 35 25 30 8)
+	SymbolLine(25 30 30 30 8)
+	SymbolLine(30 30 35 35 8)
+	SymbolLine(35 35 35 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 35 5 65 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(10 50 20 50 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 35 20 65 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(20 40 25 45 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 10 5 45 8)
+	SymbolLine(5 45 10 50 8)
+	SymbolLine(0 25 10 25 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 30 20 45 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 30 20 40 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(15 30 15 45 8)
+	SymbolLine(15 45 20 50 8)
+	SymbolLine(20 50 25 50 8)
+	SymbolLine(25 50 30 45 8)
+	SymbolLine(30 30 30 45 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 30 20 50 8)
+	SymbolLine(0 50 20 30 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 60 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 10 0 50 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(5 25 10 30 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 10 30 8)
+	SymbolLine(10 30 15 35 8)
+	SymbolLine(15 35 20 35 8)
+	SymbolLine(20 35 25 30 8)
+)
+
+Element["onsolder" "Standard SMT resistor, capacitor etc" "R0_BOT" "RESC3216N" 60000 110000 -13150 13150 0 100 "auto"]
+(
+	Pad[-5905 -984 -5905 984 5118 2000 5718 "1" "1" "onsolder,square"]
+	Pad[5905 -984 5905 984 5118 2000 5718 "2" "2" "onsolder,square"]
+	ElementLine [-1968 -3543 1968 -3543 800]
+	ElementLine [-1968 3543 1968 3543 800]
+
+	)
+
+Element["onsolder" "Standard SMT resistor, capacitor etc" "R90_BOT" "RESC3216N" 100000 110000 -13150 -13150 1 100 "auto"]
+(
+	Pad[-984 -5905 984 -5905 5118 2000 5718 "1" "1" "onsolder,square"]
+	Pad[-984 5905 984 5905 5118 2000 5718 "2" "2" "onsolder,square"]
+	ElementLine [3543 1968 3543 -1968 800]
+	ElementLine [-3543 1968 -3543 -1968 800]
+
+	)
+
+Element["onsolder" "Standard SMT resistor, capacitor etc" "R180_BOT" "RESC3216N" 130000 110000 13150 6850 2 100 "auto"]
+(
+	Pad[5905 -984 5905 984 5118 2000 5718 "1" "1" "onsolder,square"]
+	Pad[-5905 -984 -5905 984 5118 2000 5718 "2" "2" "onsolder,square"]
+	ElementLine [-1968 3543 1968 3543 800]
+	ElementLine [-1968 -3543 1968 -3543 800]
+
+	)
+
+Element["onsolder" "Standard SMT resistor, capacitor etc" "R270_BOT" "RESC3216N" 170000 110000 -6850 13150 3 100 "auto"]
+(
+	Pad[-984 5905 984 5905 5118 2000 5718 "1" "1" "onsolder,square"]
+	Pad[-984 -5905 984 -5905 5118 2000 5718 "2" "2" "onsolder,square"]
+	ElementLine [-3543 1968 -3543 -1968 800]
+	ElementLine [3543 1968 3543 -1968 800]
+
+	)
+
+Element["onsolder" "Small outline package, narrow (150mil)" "USO0_BOT" "SO8" 60000 240000 -17500 13500 0 100 "auto"]
+(
+	Pad[-13500 7500 -7000 7500 2000 1000 3000 "1" "1" "onsolder,square"]
+	Pad[-13500 2500 -7000 2500 2000 1000 3000 "2" "2" "onsolder,square"]
+	Pad[-13500 -2500 -7000 -2500 2000 1000 3000 "3" "3" "onsolder,square"]
+	Pad[-13500 -7500 -7000 -7500 2000 1000 3000 "4" "4" "onsolder,square"]
+	Pad[7000 -7500 13500 -7500 2000 1000 3000 "5" "5" "onsolder,square,edge2"]
+	Pad[7000 -2500 13500 -2500 2000 1000 3000 "6" "6" "onsolder,square,edge2"]
+	Pad[7000 2500 13500 2500 2000 1000 3000 "7" "7" "onsolder,square,edge2"]
+	Pad[7000 7500 13500 7500 2000 1000 3000 "8" "8" "onsolder,square,edge2"]
+	ElementLine [15500 9500 2500 9500 1000]
+	ElementLine [-15500 9500 -2500 9500 1000]
+	ElementLine [15500 -9500 15500 9500 1000]
+	ElementLine [-15500 -9500 15500 -9500 1000]
+	ElementLine [-15500 9500 -15500 -9500 1000]
+	ElementArc [0 9500 2500 2500 180 180 1000]
+
+	)
+
+Element["onsolder" "Small outline package, narrow (150mil)" "USO90_BOT" "SO8" 100000 240000 -21500 -3000 1 100 "auto"]
+(
+	Pad[-7500 -13500 -7500 -7000 2000 1000 3000 "1" "1" "onsolder,square"]
+	Pad[-2500 -13500 -2500 -7000 2000 1000 3000 "2" "2" "onsolder,square"]
+	Pad[2500 -13500 2500 -7000 2000 1000 3000 "3" "3" "onsolder,square"]
+	Pad[7500 -13500 7500 -7000 2000 1000 3000 "4" "4" "onsolder,square"]
+	Pad[7500 7000 7500 13500 2000 1000 3000 "5" "5" "onsolder,square,edge2"]
+	Pad[2500 7000 2500 13500 2000 1000 3000 "6" "6" "onsolder,square,edge2"]
+	Pad[-2500 7000 -2500 13500 2000 1000 3000 "7" "7" "onsolder,square,edge2"]
+	Pad[-7500 7000 -7500 13500 2000 1000 3000 "8" "8" "onsolder,square,edge2"]
+	ElementLine [-9500 15500 -9500 2500 1000]
+	ElementLine [-9500 -2500 -9500 -15500 1000]
+	ElementLine [-9500 15500 9500 15500 1000]
+	ElementLine [9500 15500 9500 -15500 1000]
+	ElementLine [-9500 -15500 9500 -15500 1000]
+	ElementArc [-9500 0 2500 2500 90 180 1000]
+
+	)
+
+Element["onsolder" "Small outline package, narrow (150mil)" "USO180_BOT" "SO8" 130000 240000 22000 -16000 2 100 "auto"]
+(
+	Pad[7000 -7500 13500 -7500 2000 1000 3000 "1" "1" "onsolder,square,edge2"]
+	Pad[7000 -2500 13500 -2500 2000 1000 3000 "2" "2" "onsolder,square,edge2"]
+	Pad[7000 2500 13500 2500 2000 1000 3000 "3" "3" "onsolder,square,edge2"]
+	Pad[7000 7500 13500 7500 2000 1000 3000 "4" "4" "onsolder,square,edge2"]
+	Pad[-13500 7500 -7000 7500 2000 1000 3000 "5" "5" "onsolder,square"]
+	Pad[-13500 2500 -7000 2500 2000 1000 3000 "6" "6" "onsolder,square"]
+	Pad[-13500 -2500 -7000 -2500 2000 1000 3000 "7" "7" "onsolder,square"]
+	Pad[-13500 -7500 -7000 -7500 2000 1000 3000 "8" "8" "onsolder,square"]
+	ElementLine [-15500 -9500 -2500 -9500 1000]
+	ElementLine [2500 -9500 15500 -9500 1000]
+	ElementLine [-15500 9500 -15500 -9500 1000]
+	ElementLine [-15500 9500 15500 9500 1000]
+	ElementLine [15500 9500 15500 -9500 1000]
+	ElementArc [0 -9500 2500 2500 0 180 1000]
+
+	)
+
+Element["onsolder" "Small outline package, narrow (150mil)" "USO270_BOT" "SO8" 170000 240000 24000 18000 3 100 "auto"]
+(
+	Pad[7500 7000 7500 13500 2000 1000 3000 "1" "1" "onsolder,square,edge2"]
+	Pad[2500 7000 2500 13500 2000 1000 3000 "2" "2" "onsolder,square,edge2"]
+	Pad[-2500 7000 -2500 13500 2000 1000 3000 "3" "3" "onsolder,square,edge2"]
+	Pad[-7500 7000 -7500 13500 2000 1000 3000 "4" "4" "onsolder,square,edge2"]
+	Pad[-7500 -13500 -7500 -7000 2000 1000 3000 "5" "5" "onsolder,square"]
+	Pad[-2500 -13500 -2500 -7000 2000 1000 3000 "6" "6" "onsolder,square"]
+	Pad[2500 -13500 2500 -7000 2000 1000 3000 "7" "7" "onsolder,square"]
+	Pad[7500 -13500 7500 -7000 2000 1000 3000 "8" "8" "onsolder,square"]
+	ElementLine [9500 -2500 9500 -15500 1000]
+	ElementLine [9500 15500 9500 2500 1000]
+	ElementLine [-9500 -15500 9500 -15500 1000]
+	ElementLine [-9500 15500 -9500 -15500 1000]
+	ElementLine [-9500 15500 9500 15500 1000]
+	ElementArc [9500 0 2500 2500 270 180 1000]
+
+	)
+
+Element["onsolder" "Dual in-line package, narrow (300 mil)" "UDIP0_BOT" "DIP8" 40000 450000 17000 -5000 3 100 "auto"]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 -10000 6000 3000 6600 2800 "2" "2" ""]
+	Pin[0 -20000 6000 3000 6600 2800 "3" "3" ""]
+	Pin[0 -30000 6000 3000 6600 2800 "4" "4" ""]
+	Pin[30000 -30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[30000 -20000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[30000 -10000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[30000 0 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [20000 5000 35000 5000 1000]
+	ElementLine [-5000 5000 10000 5000 1000]
+	ElementLine [35000 -35000 35000 5000 1000]
+	ElementLine [-5000 -35000 35000 -35000 1000]
+	ElementLine [-5000 5000 -5000 -35000 1000]
+	ElementArc [15000 5000 5000 5000 180 180 1000]
+
+	)
+
+Element["onsolder" "Dual in-line package, narrow (300 mil)" "UDIP90_BOT" "DIP8" 110000 420000 5000 17000 0 100 "auto"]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[10000 0 6000 3000 6600 2800 "2" "2" ""]
+	Pin[20000 0 6000 3000 6600 2800 "3" "3" ""]
+	Pin[30000 0 6000 3000 6600 2800 "4" "4" ""]
+	Pin[30000 30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[20000 30000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[10000 30000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[0 30000 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [-5000 35000 -5000 20000 1000]
+	ElementLine [-5000 10000 -5000 -5000 1000]
+	ElementLine [-5000 35000 35000 35000 1000]
+	ElementLine [35000 35000 35000 -5000 1000]
+	ElementLine [-5000 -5000 35000 -5000 1000]
+	ElementArc [-5000 15000 5000 5000 90 180 1000]
+
+	)
+
+Element["onsolder" "Dual in-line package, narrow (300 mil)" "UDIP180_BOT" "DIP8" 210000 420000 -17000 5000 1 100 "auto"]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 10000 6000 3000 6600 2800 "2" "2" ""]
+	Pin[0 20000 6000 3000 6600 2800 "3" "3" ""]
+	Pin[0 30000 6000 3000 6600 2800 "4" "4" ""]
+	Pin[-30000 30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[-30000 20000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[-30000 10000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[-30000 0 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [-35000 -5000 -20000 -5000 1000]
+	ElementLine [-10000 -5000 5000 -5000 1000]
+	ElementLine [-35000 35000 -35000 -5000 1000]
+	ElementLine [-35000 35000 5000 35000 1000]
+	ElementLine [5000 35000 5000 -5000 1000]
+	ElementArc [-15000 -5000 5000 5000 0 180 1000]
+
+	)
+
+Element["onsolder" "Dual in-line package, narrow (300 mil)" "UDIP270_BOT" "DIP8" 280000 450000 -5000 -17000 2 100 "auto"]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[-10000 0 6000 3000 6600 2800 "2" "2" ""]
+	Pin[-20000 0 6000 3000 6600 2800 "3" "3" ""]
+	Pin[-30000 0 6000 3000 6600 2800 "4" "4" ""]
+	Pin[-30000 -30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[-20000 -30000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[-10000 -30000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[0 -30000 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [5000 -20000 5000 -35000 1000]
+	ElementLine [5000 5000 5000 -10000 1000]
+	ElementLine [-35000 -35000 5000 -35000 1000]
+	ElementLine [-35000 5000 -35000 -35000 1000]
+	ElementLine [-35000 5000 5000 5000 1000]
+	ElementArc [5000 -15000 5000 5000 270 180 1000]
+
+	)
+
+Element["" "Small outline package, narrow (150mil)" "USO0_TOP" "SO8" 60000 180000 -16000 -22500 0 100 ""]
+(
+	Pad[-13500 -7500 -7000 -7500 2000 1000 3000 "1" "1" "square"]
+	Pad[-13500 -2500 -7000 -2500 2000 1000 3000 "2" "2" "square"]
+	Pad[-13500 2500 -7000 2500 2000 1000 3000 "3" "3" "square"]
+	Pad[-13500 7500 -7000 7500 2000 1000 3000 "4" "4" "square"]
+	Pad[7000 7500 13500 7500 2000 1000 3000 "5" "5" "square,edge2"]
+	Pad[7000 2500 13500 2500 2000 1000 3000 "6" "6" "square,edge2"]
+	Pad[7000 -2500 13500 -2500 2000 1000 3000 "7" "7" "square,edge2"]
+	Pad[7000 -7500 13500 -7500 2000 1000 3000 "8" "8" "square,edge2"]
+	ElementLine [15500 -9500 2500 -9500 1000]
+	ElementLine [-15500 -9500 -2500 -9500 1000]
+	ElementLine [15500 9500 15500 -9500 1000]
+	ElementLine [-15500 9500 15500 9500 1000]
+	ElementLine [-15500 -9500 -15500 9500 1000]
+	ElementArc [0 -9500 2500 2500 0 180 1000]
+
+	)
+
+Element["" "Small outline package, narrow (150mil)" "USO270_TOP" "SO8" 100000 180000 -16000 22000 1 100 ""]
+(
+	Pad[-7500 7000 -7500 13500 2000 1000 3000 "1" "1" "square,edge2"]
+	Pad[-2500 7000 -2500 13500 2000 1000 3000 "2" "2" "square,edge2"]
+	Pad[2500 7000 2500 13500 2000 1000 3000 "3" "3" "square,edge2"]
+	Pad[7500 7000 7500 13500 2000 1000 3000 "4" "4" "square,edge2"]
+	Pad[7500 -13500 7500 -7000 2000 1000 3000 "5" "5" "square"]
+	Pad[2500 -13500 2500 -7000 2000 1000 3000 "6" "6" "square"]
+	Pad[-2500 -13500 -2500 -7000 2000 1000 3000 "7" "7" "square"]
+	Pad[-7500 -13500 -7500 -7000 2000 1000 3000 "8" "8" "square"]
+	ElementLine [-9500 -15500 -9500 -2500 1000]
+	ElementLine [-9500 2500 -9500 15500 1000]
+	ElementLine [-9500 -15500 9500 -15500 1000]
+	ElementLine [9500 -15500 9500 15500 1000]
+	ElementLine [-9500 15500 9500 15500 1000]
+	ElementArc [-9500 0 2500 2500 90 180 1000]
+
+	)
+
+Element["" "Small outline package, narrow (150mil)" "USO180_TOP" "SO8" 130000 180000 9000 17000 2 100 ""]
+(
+	Pad[7000 7500 13500 7500 2000 1000 3000 "1" "1" "square,edge2"]
+	Pad[7000 2500 13500 2500 2000 1000 3000 "2" "2" "square,edge2"]
+	Pad[7000 -2500 13500 -2500 2000 1000 3000 "3" "3" "square,edge2"]
+	Pad[7000 -7500 13500 -7500 2000 1000 3000 "4" "4" "square,edge2"]
+	Pad[-13500 -7500 -7000 -7500 2000 1000 3000 "5" "5" "square"]
+	Pad[-13500 -2500 -7000 -2500 2000 1000 3000 "6" "6" "square"]
+	Pad[-13500 2500 -7000 2500 2000 1000 3000 "7" "7" "square"]
+	Pad[-13500 7500 -7000 7500 2000 1000 3000 "8" "8" "square"]
+	ElementLine [-15500 9500 -2500 9500 1000]
+	ElementLine [2500 9500 15500 9500 1000]
+	ElementLine [-15500 -9500 -15500 9500 1000]
+	ElementLine [-15500 -9500 15500 -9500 1000]
+	ElementLine [15500 -9500 15500 9500 1000]
+	ElementArc [0 9500 2500 2500 180 180 1000]
+
+	)
+
+Element["" "Small outline package, narrow (150mil)" "USO90_TOP" "SO8" 170000 180000 15500 -13500 3 100 ""]
+(
+	Pad[7500 -13500 7500 -7000 2000 1000 3000 "1" "1" "square"]
+	Pad[2500 -13500 2500 -7000 2000 1000 3000 "2" "2" "square"]
+	Pad[-2500 -13500 -2500 -7000 2000 1000 3000 "3" "3" "square"]
+	Pad[-7500 -13500 -7500 -7000 2000 1000 3000 "4" "4" "square"]
+	Pad[-7500 7000 -7500 13500 2000 1000 3000 "5" "5" "square,edge2"]
+	Pad[-2500 7000 -2500 13500 2000 1000 3000 "6" "6" "square,edge2"]
+	Pad[2500 7000 2500 13500 2000 1000 3000 "7" "7" "square,edge2"]
+	Pad[7500 7000 7500 13500 2000 1000 3000 "8" "8" "square,edge2"]
+	ElementLine [9500 2500 9500 15500 1000]
+	ElementLine [9500 -15500 9500 -2500 1000]
+	ElementLine [-9500 15500 9500 15500 1000]
+	ElementLine [-9500 -15500 -9500 15500 1000]
+	ElementLine [-9500 -15500 9500 -15500 1000]
+	ElementArc [9500 0 2500 2500 270 180 1000]
+
+	)
+
+Element["" "Dual in-line package, narrow (300 mil)" "UDIP0_TOP" "DIP8" 40000 340000 17000 -35000 3 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 10000 6000 3000 6600 2800 "2" "2" ""]
+	Pin[0 20000 6000 3000 6600 2800 "3" "3" ""]
+	Pin[0 30000 6000 3000 6600 2800 "4" "4" ""]
+	Pin[30000 30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[30000 20000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[30000 10000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[30000 0 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [20000 -5000 35000 -5000 1000]
+	ElementLine [-5000 -5000 10000 -5000 1000]
+	ElementLine [35000 35000 35000 -5000 1000]
+	ElementLine [-5000 35000 35000 35000 1000]
+	ElementLine [-5000 -5000 -5000 35000 1000]
+	ElementArc [15000 -5000 5000 5000 0 180 1000]
+
+	)
+
+Element["" "Dual in-line package, narrow (300 mil)" "UDIP270_TOP" "DIP8" 110000 370000 5000 -17000 0 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[10000 0 6000 3000 6600 2800 "2" "2" ""]
+	Pin[20000 0 6000 3000 6600 2800 "3" "3" ""]
+	Pin[30000 0 6000 3000 6600 2800 "4" "4" ""]
+	Pin[30000 -30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[20000 -30000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[10000 -30000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[0 -30000 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [-5000 -35000 -5000 -20000 1000]
+	ElementLine [-5000 -10000 -5000 5000 1000]
+	ElementLine [-5000 -35000 35000 -35000 1000]
+	ElementLine [35000 -35000 35000 5000 1000]
+	ElementLine [-5000 5000 35000 5000 1000]
+	ElementArc [-5000 -15000 5000 5000 90 180 1000]
+
+	)
+
+Element["" "Dual in-line package, narrow (300 mil)" "UDIP180_TOP" "DIP8" 210000 370000 -17000 -5000 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 -10000 6000 3000 6600 2800 "2" "2" ""]
+	Pin[0 -20000 6000 3000 6600 2800 "3" "3" ""]
+	Pin[0 -30000 6000 3000 6600 2800 "4" "4" ""]
+	Pin[-30000 -30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[-30000 -20000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[-30000 -10000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[-30000 0 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [-35000 5000 -20000 5000 1000]
+	ElementLine [-10000 5000 5000 5000 1000]
+	ElementLine [-35000 -35000 -35000 5000 1000]
+	ElementLine [-35000 -35000 5000 -35000 1000]
+	ElementLine [5000 -35000 5000 5000 1000]
+	ElementArc [-15000 5000 5000 5000 180 180 1000]
+
+	)
+
+Element["" "Dual in-line package, narrow (300 mil)" "UDIP90_TOP" "DIP8" 280000 340000 -5000 17000 2 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[-10000 0 6000 3000 6600 2800 "2" "2" ""]
+	Pin[-20000 0 6000 3000 6600 2800 "3" "3" ""]
+	Pin[-30000 0 6000 3000 6600 2800 "4" "4" ""]
+	Pin[-30000 30000 6000 3000 6600 2800 "5" "5" ""]
+	Pin[-20000 30000 6000 3000 6600 2800 "6" "6" ""]
+	Pin[-10000 30000 6000 3000 6600 2800 "7" "7" ""]
+	Pin[0 30000 6000 3000 6600 2800 "8" "8" ""]
+	ElementLine [5000 20000 5000 35000 1000]
+	ElementLine [5000 -5000 5000 10000 1000]
+	ElementLine [-35000 35000 5000 35000 1000]
+	ElementLine [-35000 -5000 -35000 35000 1000]
+	ElementLine [-35000 -5000 5000 -5000 1000]
+	ElementArc [5000 15000 5000 5000 270 180 1000]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R0_TOP" "RESC3216N" 60000 70000 -11960 -10150 0 100 ""]
+(
+	Pad[-5905 -984 -5905 984 5118 2000 5718 "1" "1" "square"]
+	Pad[5905 -984 5905 984 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-1968 3543 1968 3543 800]
+	ElementLine [-1968 -3543 1968 -3543 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R270_TOP" "RESC3216N" 100000 70000 -13150 13150 1 100 ""]
+(
+	Pad[-984 5905 984 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-984 -5905 984 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [3543 -1968 3543 1968 800]
+	ElementLine [-3543 -1968 -3543 1968 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R180_TOP" "RESC3216N" 130000 70000 13150 13150 2 100 ""]
+(
+	Pad[5905 -984 5905 984 5118 2000 5718 "1" "1" "square"]
+	Pad[-5905 -984 -5905 984 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-1968 -3543 1968 -3543 800]
+	ElementLine [-1968 3543 1968 3543 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "R90_TOP" "RESC3216N" 170000 70000 15261 -7039 3 100 ""]
+(
+	Pad[-984 -5905 984 -5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-984 5905 984 5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3543 -1968 -3543 1968 800]
+	ElementLine [3543 -1968 3543 1968 800]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "signal4")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Text[270000 80000 0 100 "2-pin surface mount elements" ""]
+	Text[270000 90000 0 100 "0/90/180/270 degree rotations" ""]
+	Text[270000 100000 0 100 "both sides ofthe board" ""]
+	Text[270000 190000 0 100 "multi-pin surface mount elements" ""]
+	Text[270000 200000 0 100 "0/90/180/270 degree rotations" ""]
+	Text[270000 210000 0 100 "both sides ofthe board" ""]
+	Text[310000 380000 0 100 "multi-pin leaded elements" "selected"]
+	Text[310000 390000 0 100 "0/90/180/270 degree rotations" ""]
+	Text[310000 400000 0 100 "both sides ofthe board" ""]
+)
diff --git a/tests/orig/inputs/gcode_oneline.pcb b/tests/orig/inputs/gcode_oneline.pcb
new file mode 100644
index 0000000..6788491
--- /dev/null
+++ b/tests/orig/inputs/gcode_oneline.pcb
@@ -0,0 +1,829 @@
+# release: pcb 1.99y
+
+# To read pcb files, the pcb version (or the cvs source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["Basic Single Trace RS274-X Test" 200000 100000]
+
+Grid[10000.000000 0 0 1]
+Cursor[0 500000 0.000000]
+PolyArea[200000000.000000]
+Thermal[0.500000]
+DRC[1000 1000 1000 1000 1500 1000]
+Flags("nameonpcb,uniquename,clearnew,snappin")
+Groups("1,c:2,s:3:4:5:6:7:8")
+Styles["Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"]
+
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 45 0 50 8)
+	SymbolLine(0 10 0 35 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 10 0 20 8)
+	SymbolLine(10 10 10 20 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 35 20 35 8)
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(15 20 15 40 8)
+	SymbolLine(5 20 5 40 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 40 8)
+	SymbolLine(15 45 20 40 8)
+	SymbolLine(5 45 15 45 8)
+	SymbolLine(0 40 5 45 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(10 25 15 20 8)
+	SymbolLine(5 25 10 25 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(0 50 40 10 8)
+	SymbolLine(35 50 40 45 8)
+	SymbolLine(40 40 40 45 8)
+	SymbolLine(35 35 40 40 8)
+	SymbolLine(30 35 35 35 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(25 40 25 45 8)
+	SymbolLine(25 45 30 50 8)
+	SymbolLine(30 50 35 50 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 35 15 20 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(0 25 25 50 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(0 35 0 45 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 60 10 50 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 30 20 30 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 45 30 15 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(10 10 10 50 8)
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 25 30 8)
+	SymbolLine(20 10 20 50 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 10 0 30 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(5 25 15 25 8)
+	SymbolLine(15 25 20 30 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(25 10 25 25 8)
+	SymbolLine(0 10 25 10 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 25 8)
+	SymbolLine(15 30 20 25 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(20 15 20 30 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 25 5 25 8)
+	SymbolLine(0 35 5 35 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 50 10 40 8)
+	SymbolLine(10 25 10 30 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 30 10 20 8)
+	SymbolLine(0 30 10 40 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 35 20 35 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 20 10 30 8)
+	SymbolLine(0 40 10 30 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 30 10 35 8)
+	SymbolLine(10 45 10 50 8)
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 20 8)
+	SymbolLine(10 30 20 20 8)
+)
+Symbol('@' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 40 50 8)
+	SymbolLine(50 35 50 10 8)
+	SymbolLine(50 10 40 0 8)
+	SymbolLine(40 0 10 0 8)
+	SymbolLine(10 0 0 10 8)
+	SymbolLine(15 20 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 30 35 8)
+	SymbolLine(30 35 35 30 8)
+	SymbolLine(35 30 40 35 8)
+	SymbolLine(35 30 35 15 8)
+	SymbolLine(35 20 30 15 8)
+	SymbolLine(20 15 30 15 8)
+	SymbolLine(20 15 15 20 8)
+	SymbolLine(40 35 50 35 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine(0 15 0 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 30 15 30 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(10 30 20 30 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(25 10 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 10 10 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 10 50 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine(0 10 15 10 8)
+	SymbolLine(15 10 15 45 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 20 50 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 15 25 8)
+	SymbolLine(15 25 30 10 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 10 25 50 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(10 40 20 50 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(5 30 25 50 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 10 20 45 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 10 20 40 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 15 35 8)
+	SymbolLine(15 35 30 50 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 40 25 50 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(25 10 25 15 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 10 25 8)
+	SymbolLine(10 25 20 15 8)
+	SymbolLine(20 10 20 15 8)
+	SymbolLine(10 25 10 50 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine(0 10 25 10 8)
+	SymbolLine(25 10 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 15 30 45 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 15 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(20 45 25 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 10 20 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(20 40 20 35 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 15 5 50 8)
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(10 10 15 10 8)
+	SymbolLine(0 30 10 30 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(20 30 20 60 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 35 0 50 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 20 5 25 8)
+	SymbolLine(5 35 5 60 8)
+	SymbolLine(0 65 5 60 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 15 50 8)
+	SymbolLine(0 35 10 25 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(20 35 25 30 8)
+	SymbolLine(25 30 30 30 8)
+	SymbolLine(30 30 35 35 8)
+	SymbolLine(35 35 35 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 35 5 65 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(10 50 20 50 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 35 20 65 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(20 40 25 45 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 10 5 45 8)
+	SymbolLine(5 45 10 50 8)
+	SymbolLine(0 25 10 25 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 30 20 45 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 30 20 40 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(15 30 15 45 8)
+	SymbolLine(15 45 20 50 8)
+	SymbolLine(20 50 25 50 8)
+	SymbolLine(25 50 30 45 8)
+	SymbolLine(30 30 30 45 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 30 20 50 8)
+	SymbolLine(0 50 20 30 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 60 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 10 0 50 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(5 25 10 30 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 10 30 8)
+	SymbolLine(10 30 15 35 8)
+	SymbolLine(15 35 20 35 8)
+	SymbolLine(20 35 25 30 8)
+)
+Via[90000 50000 6000 2000 0 3500 "" ""]
+Layer(1 "component")
+(
+	Line[10000 50000 90000 50000 4000 2000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[170000 50000 90000 50000 4000 2000 "clearline"]
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "signal4")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/tests/orig/inputs/gerber_arcs.pcb b/tests/orig/inputs/gerber_arcs.pcb
new file mode 100644
index 0000000..8e73a8e
--- /dev/null
+++ b/tests/orig/inputs/gerber_arcs.pcb
@@ -0,0 +1,844 @@
+# release: pcb 20100929
+# date:    Thu Jun 16 20:10:40 2011
+# user:    username ()
+# host:    titanic.lan
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20100606]
+
+PCB["" 3000000 2000000]
+
+Grid[10000.000000 0 0 0]
+Cursor[0 0 0.000000]
+PolyArea[200000000.000000]
+Thermal[0.500000]
+DRC[14941 14941 14941 14941 22411 14941]
+Flags("nameonpcb,uniquename,clearnew,snappin")
+Groups("1,c:2:3:4:5:6,s:7:8")
+Styles["Signal,1000,3600,2000,1000:Power,50000,65000,25000,1000:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"]
+
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 45 0 50 8)
+	SymbolLine(0 10 0 35 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 10 0 20 8)
+	SymbolLine(10 10 10 20 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 35 20 35 8)
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(15 20 15 40 8)
+	SymbolLine(5 20 5 40 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 40 8)
+	SymbolLine(15 45 20 40 8)
+	SymbolLine(5 45 15 45 8)
+	SymbolLine(0 40 5 45 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(10 25 15 20 8)
+	SymbolLine(5 25 10 25 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(0 50 40 10 8)
+	SymbolLine(35 50 40 45 8)
+	SymbolLine(40 40 40 45 8)
+	SymbolLine(35 35 40 40 8)
+	SymbolLine(30 35 35 35 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(25 40 25 45 8)
+	SymbolLine(25 45 30 50 8)
+	SymbolLine(30 50 35 50 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 35 15 20 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(0 25 25 50 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(0 35 0 45 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 60 10 50 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 30 20 30 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 45 30 15 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(10 10 10 50 8)
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 25 30 8)
+	SymbolLine(20 10 20 50 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 10 0 30 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(5 25 15 25 8)
+	SymbolLine(15 25 20 30 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(25 10 25 25 8)
+	SymbolLine(0 10 25 10 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 25 8)
+	SymbolLine(15 30 20 25 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(20 15 20 30 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 25 5 25 8)
+	SymbolLine(0 35 5 35 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 50 10 40 8)
+	SymbolLine(10 25 10 30 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 30 10 20 8)
+	SymbolLine(0 30 10 40 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 35 20 35 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 20 10 30 8)
+	SymbolLine(0 40 10 30 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 30 10 35 8)
+	SymbolLine(10 45 10 50 8)
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 20 8)
+	SymbolLine(10 30 20 20 8)
+)
+Symbol('@' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 40 50 8)
+	SymbolLine(50 35 50 10 8)
+	SymbolLine(50 10 40 0 8)
+	SymbolLine(40 0 10 0 8)
+	SymbolLine(10 0 0 10 8)
+	SymbolLine(15 20 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 30 35 8)
+	SymbolLine(30 35 35 30 8)
+	SymbolLine(35 30 40 35 8)
+	SymbolLine(35 30 35 15 8)
+	SymbolLine(35 20 30 15 8)
+	SymbolLine(20 15 30 15 8)
+	SymbolLine(20 15 15 20 8)
+	SymbolLine(40 35 50 35 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine(0 15 0 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 30 15 30 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(10 30 20 30 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(25 10 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 10 10 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 10 50 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine(0 10 15 10 8)
+	SymbolLine(15 10 15 45 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 20 50 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 15 25 8)
+	SymbolLine(15 25 30 10 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 10 25 50 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(10 40 20 50 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(5 30 25 50 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 10 20 45 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 10 20 40 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 15 35 8)
+	SymbolLine(15 35 30 50 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 40 25 50 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(25 10 25 15 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 10 25 8)
+	SymbolLine(10 25 20 15 8)
+	SymbolLine(20 10 20 15 8)
+	SymbolLine(10 25 10 50 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine(0 10 25 10 8)
+	SymbolLine(25 10 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 15 30 45 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 15 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(20 45 25 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 10 20 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(20 40 20 35 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 15 5 50 8)
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(10 10 15 10 8)
+	SymbolLine(0 30 10 30 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(20 30 20 60 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 35 0 50 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 20 5 25 8)
+	SymbolLine(5 35 5 60 8)
+	SymbolLine(0 65 5 60 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 15 50 8)
+	SymbolLine(0 35 10 25 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(20 35 25 30 8)
+	SymbolLine(25 30 30 30 8)
+	SymbolLine(30 30 35 35 8)
+	SymbolLine(35 35 35 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 35 5 65 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(10 50 20 50 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 35 20 65 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(20 40 25 45 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 10 5 45 8)
+	SymbolLine(5 45 10 50 8)
+	SymbolLine(0 25 10 25 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 30 20 45 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 30 20 40 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(15 30 15 45 8)
+	SymbolLine(15 45 20 50 8)
+	SymbolLine(20 50 25 50 8)
+	SymbolLine(25 50 30 45 8)
+	SymbolLine(30 30 30 45 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 30 20 50 8)
+	SymbolLine(0 50 20 30 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 60 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 10 0 50 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(5 25 10 30 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 10 30 8)
+	SymbolLine(10 30 15 35 8)
+	SymbolLine(15 35 20 35 8)
+	SymbolLine(20 35 25 30 8)
+)
+Via[600000 1550000 75000 2000 0 50000 "" ""]
+Via[600000 1710000 65000 2000 0 25000 "" ""]
+Via[450000 1400000 100000 2000 0 50000 "" ""]
+Via[600000 1400000 100000 2000 0 50000 "" ""]
+Via[450000 1710000 65000 2000 0 25000 "" ""]
+Via[450000 1550000 75000 2000 0 50000 "" ""]
+Layer(1 "top")
+(
+	Arc[775000 209000 491000 491000 1000 2000 180 -90 "clearline"]
+	Arc[772000 1183000 486000 486000 1000 2000 -90 90 "clearline"]
+	Arc[286000 697000 486000 486000 1000 2000 90 90 "clearline"]
+	Arc[1266000 700000 491000 491000 1000 2000 0 -90 "clearline"]
+)
+Layer(2 "ground")
+(
+	Arc[2430000 209000 933000 933000 10000 2000 0 90 "clearline"]
+	Arc[1861000 778000 554000 554000 25000 2000 -90 -90 "clearline"]
+	Arc[1845000 750000 358000 358000 50000 2000 -90 -90 "clearline"]
+	Arc[1845000 729000 184000 184000 100000 2000 -90 -90 "clearline"]
+)
+Layer(3 "signal2")
+(
+)
+Layer(4 "signal3")
+(
+)
+Layer(5 "power")
+(
+	Arc[2500000 1370000 1200000 400000 25000 1000 0 90 "clearline"]
+)
+Layer(6 "bottom")
+(
+)
+Layer(7 "outline")
+(
+)
+Layer(8 "spare")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/tests/orig/inputs/gerber_oneline.pcb b/tests/orig/inputs/gerber_oneline.pcb
new file mode 100644
index 0000000..6788491
--- /dev/null
+++ b/tests/orig/inputs/gerber_oneline.pcb
@@ -0,0 +1,829 @@
+# release: pcb 1.99y
+
+# To read pcb files, the pcb version (or the cvs source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["Basic Single Trace RS274-X Test" 200000 100000]
+
+Grid[10000.000000 0 0 1]
+Cursor[0 500000 0.000000]
+PolyArea[200000000.000000]
+Thermal[0.500000]
+DRC[1000 1000 1000 1000 1500 1000]
+Flags("nameonpcb,uniquename,clearnew,snappin")
+Groups("1,c:2,s:3:4:5:6:7:8")
+Styles["Signal,1000,3600,2000,1000:Power,2500,6000,3500,1000:Fat,4000,6000,3500,1000:Skinny,600,2402,1181,600"]
+
+Symbol(' ' 18)
+(
+)
+Symbol('!' 12)
+(
+	SymbolLine(0 45 0 50 8)
+	SymbolLine(0 10 0 35 8)
+)
+Symbol('"' 12)
+(
+	SymbolLine(0 10 0 20 8)
+	SymbolLine(10 10 10 20 8)
+)
+Symbol('#' 12)
+(
+	SymbolLine(0 35 20 35 8)
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(15 20 15 40 8)
+	SymbolLine(5 20 5 40 8)
+)
+Symbol('$' 12)
+(
+	SymbolLine(15 15 20 20 8)
+	SymbolLine(5 15 15 15 8)
+	SymbolLine(0 20 5 15 8)
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 40 8)
+	SymbolLine(15 45 20 40 8)
+	SymbolLine(5 45 15 45 8)
+	SymbolLine(0 40 5 45 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('%' 12)
+(
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(10 25 15 20 8)
+	SymbolLine(5 25 10 25 8)
+	SymbolLine(0 20 5 25 8)
+	SymbolLine(0 50 40 10 8)
+	SymbolLine(35 50 40 45 8)
+	SymbolLine(40 40 40 45 8)
+	SymbolLine(35 35 40 40 8)
+	SymbolLine(30 35 35 35 8)
+	SymbolLine(25 40 30 35 8)
+	SymbolLine(25 40 25 45 8)
+	SymbolLine(25 45 30 50 8)
+	SymbolLine(30 50 35 50 8)
+)
+Symbol('&' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 35 15 20 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(0 25 25 50 8)
+	SymbolLine(5 10 10 10 8)
+	SymbolLine(10 10 15 15 8)
+	SymbolLine(15 15 15 20 8)
+	SymbolLine(0 35 0 45 8)
+)
+Symbol(''' 12)
+(
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('(' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+)
+Symbol(')' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('*' 12)
+(
+	SymbolLine(0 20 20 40 8)
+	SymbolLine(0 40 20 20 8)
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol('+' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(10 20 10 40 8)
+)
+Symbol(',' 12)
+(
+	SymbolLine(0 60 10 50 8)
+)
+Symbol('-' 12)
+(
+	SymbolLine(0 30 20 30 8)
+)
+Symbol('.' 12)
+(
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('/' 12)
+(
+	SymbolLine(0 45 30 15 8)
+)
+Symbol('0' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 40 20 20 8)
+)
+Symbol('1' 12)
+(
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(10 10 10 50 8)
+	SymbolLine(0 20 10 10 8)
+)
+Symbol('2' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('3' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('4' 12)
+(
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 25 30 8)
+	SymbolLine(20 10 20 50 8)
+)
+Symbol('5' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 10 0 30 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(5 25 15 25 8)
+	SymbolLine(15 25 20 30 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('6' 12)
+(
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+)
+Symbol('7' 12)
+(
+	SymbolLine(0 50 25 25 8)
+	SymbolLine(25 10 25 25 8)
+	SymbolLine(0 10 25 10 8)
+)
+Symbol('8' 12)
+(
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 25 8)
+	SymbolLine(15 30 20 25 8)
+)
+Symbol('9' 12)
+(
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(20 15 20 30 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol(':' 12)
+(
+	SymbolLine(0 25 5 25 8)
+	SymbolLine(0 35 5 35 8)
+)
+Symbol(';' 12)
+(
+	SymbolLine(0 50 10 40 8)
+	SymbolLine(10 25 10 30 8)
+)
+Symbol('<' 12)
+(
+	SymbolLine(0 30 10 20 8)
+	SymbolLine(0 30 10 40 8)
+)
+Symbol('=' 12)
+(
+	SymbolLine(0 25 20 25 8)
+	SymbolLine(0 35 20 35 8)
+)
+Symbol('>' 12)
+(
+	SymbolLine(0 20 10 30 8)
+	SymbolLine(0 40 10 30 8)
+)
+Symbol('?' 12)
+(
+	SymbolLine(10 30 10 35 8)
+	SymbolLine(10 45 10 50 8)
+	SymbolLine(0 15 0 20 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 20 8)
+	SymbolLine(10 30 20 20 8)
+)
+Symbol('@' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 40 50 8)
+	SymbolLine(50 35 50 10 8)
+	SymbolLine(50 10 40 0 8)
+	SymbolLine(40 0 10 0 8)
+	SymbolLine(10 0 0 10 8)
+	SymbolLine(15 20 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 30 35 8)
+	SymbolLine(30 35 35 30 8)
+	SymbolLine(35 30 40 35 8)
+	SymbolLine(35 30 35 15 8)
+	SymbolLine(35 20 30 15 8)
+	SymbolLine(20 15 30 15 8)
+	SymbolLine(20 15 15 20 8)
+	SymbolLine(40 35 50 35 8)
+)
+Symbol('A' 12)
+(
+	SymbolLine(0 15 0 50 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('B' 12)
+(
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+)
+Symbol('C' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 20 10 8)
+)
+Symbol('D' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('E' 12)
+(
+	SymbolLine(0 30 15 30 8)
+	SymbolLine(0 50 20 50 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+)
+Symbol('F' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(0 30 15 30 8)
+)
+Symbol('G' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(10 30 20 30 8)
+)
+Symbol('H' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(25 10 25 50 8)
+	SymbolLine(0 30 25 30 8)
+)
+Symbol('I' 12)
+(
+	SymbolLine(0 10 10 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 10 50 8)
+)
+Symbol('J' 12)
+(
+	SymbolLine(0 10 15 10 8)
+	SymbolLine(15 10 15 45 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('K' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 30 20 10 8)
+	SymbolLine(0 30 20 50 8)
+)
+Symbol('L' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('M' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 15 25 8)
+	SymbolLine(15 25 30 10 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('N' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 10 25 50 8)
+)
+Symbol('O' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('P' 12)
+(
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+)
+Symbol('Q' 12)
+(
+	SymbolLine(0 15 0 45 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 15 10 8)
+	SymbolLine(15 10 20 15 8)
+	SymbolLine(20 15 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(10 40 20 50 8)
+)
+Symbol('R' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(25 15 25 25 8)
+	SymbolLine(20 30 25 25 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(5 30 25 50 8)
+)
+Symbol('S' 12)
+(
+	SymbolLine(20 10 25 15 8)
+	SymbolLine(5 10 20 10 8)
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(0 15 0 25 8)
+	SymbolLine(0 25 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('T' 12)
+(
+	SymbolLine(0 10 20 10 8)
+	SymbolLine(10 10 10 50 8)
+)
+Symbol('U' 12)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 10 20 45 8)
+)
+Symbol('V' 12)
+(
+	SymbolLine(0 10 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 10 20 40 8)
+)
+Symbol('W' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 15 35 8)
+	SymbolLine(15 35 30 50 8)
+	SymbolLine(30 10 30 50 8)
+)
+Symbol('X' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 25 40 8)
+	SymbolLine(25 40 25 50 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(25 10 25 15 8)
+)
+Symbol('Y' 12)
+(
+	SymbolLine(0 10 0 15 8)
+	SymbolLine(0 15 10 25 8)
+	SymbolLine(10 25 20 15 8)
+	SymbolLine(20 10 20 15 8)
+	SymbolLine(10 25 10 50 8)
+)
+Symbol('Z' 12)
+(
+	SymbolLine(0 10 25 10 8)
+	SymbolLine(25 10 25 15 8)
+	SymbolLine(0 40 25 15 8)
+	SymbolLine(0 40 0 50 8)
+	SymbolLine(0 50 25 50 8)
+)
+Symbol('[' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('\' 12)
+(
+	SymbolLine(0 15 30 45 8)
+)
+Symbol(']' 12)
+(
+	SymbolLine(0 10 5 10 8)
+	SymbolLine(5 10 5 50 8)
+	SymbolLine(0 50 5 50 8)
+)
+Symbol('^' 12)
+(
+	SymbolLine(0 15 5 10 8)
+	SymbolLine(5 10 10 15 8)
+)
+Symbol('_' 12)
+(
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('a' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 45 8)
+	SymbolLine(20 45 25 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('b' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+)
+Symbol('c' 12)
+(
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 20 50 8)
+)
+Symbol('d' 12)
+(
+	SymbolLine(20 10 20 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+)
+Symbol('e' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(0 40 20 40 8)
+	SymbolLine(20 40 20 35 8)
+)
+Symbol('f' 10)
+(
+	SymbolLine(5 15 5 50 8)
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(10 10 15 10 8)
+	SymbolLine(0 30 10 30 8)
+)
+Symbol('g' 12)
+(
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(20 30 20 60 8)
+)
+Symbol('h' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+)
+Symbol('i' 10)
+(
+	SymbolLine(0 20 0 25 8)
+	SymbolLine(0 35 0 50 8)
+)
+Symbol('j' 10)
+(
+	SymbolLine(5 20 5 25 8)
+	SymbolLine(5 35 5 60 8)
+	SymbolLine(0 65 5 60 8)
+)
+Symbol('k' 12)
+(
+	SymbolLine(0 10 0 50 8)
+	SymbolLine(0 35 15 50 8)
+	SymbolLine(0 35 10 25 8)
+)
+Symbol('l' 10)
+(
+	SymbolLine(0 10 0 45 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('m' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(20 35 25 30 8)
+	SymbolLine(25 30 30 30 8)
+	SymbolLine(30 30 35 35 8)
+	SymbolLine(35 35 35 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('n' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 50 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('o' 12)
+(
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(20 35 20 45 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('p' 12)
+(
+	SymbolLine(5 35 5 65 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(25 35 25 45 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(10 50 20 50 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('q' 12)
+(
+	SymbolLine(20 35 20 65 8)
+	SymbolLine(15 30 20 35 8)
+	SymbolLine(5 30 15 30 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(0 35 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('r' 12)
+(
+	SymbolLine(5 35 5 50 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(10 30 20 30 8)
+	SymbolLine(0 30 5 35 8)
+)
+Symbol('s' 12)
+(
+	SymbolLine(5 50 20 50 8)
+	SymbolLine(20 50 25 45 8)
+	SymbolLine(20 40 25 45 8)
+	SymbolLine(5 40 20 40 8)
+	SymbolLine(0 35 5 40 8)
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 20 30 8)
+	SymbolLine(20 30 25 35 8)
+	SymbolLine(0 45 5 50 8)
+)
+Symbol('t' 10)
+(
+	SymbolLine(5 10 5 45 8)
+	SymbolLine(5 45 10 50 8)
+	SymbolLine(0 25 10 25 8)
+)
+Symbol('u' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+	SymbolLine(20 30 20 45 8)
+)
+Symbol('v' 12)
+(
+	SymbolLine(0 30 0 40 8)
+	SymbolLine(0 40 10 50 8)
+	SymbolLine(10 50 20 40 8)
+	SymbolLine(20 30 20 40 8)
+)
+Symbol('w' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(5 50 10 50 8)
+	SymbolLine(10 50 15 45 8)
+	SymbolLine(15 30 15 45 8)
+	SymbolLine(15 45 20 50 8)
+	SymbolLine(20 50 25 50 8)
+	SymbolLine(25 50 30 45 8)
+	SymbolLine(30 30 30 45 8)
+)
+Symbol('x' 12)
+(
+	SymbolLine(0 30 20 50 8)
+	SymbolLine(0 50 20 30 8)
+)
+Symbol('y' 12)
+(
+	SymbolLine(0 30 0 45 8)
+	SymbolLine(0 45 5 50 8)
+	SymbolLine(20 30 20 60 8)
+	SymbolLine(15 65 20 60 8)
+	SymbolLine(5 65 15 65 8)
+	SymbolLine(0 60 5 65 8)
+	SymbolLine(5 50 15 50 8)
+	SymbolLine(15 50 20 45 8)
+)
+Symbol('z' 12)
+(
+	SymbolLine(0 30 20 30 8)
+	SymbolLine(0 50 20 30 8)
+	SymbolLine(0 50 20 50 8)
+)
+Symbol('{' 12)
+(
+	SymbolLine(5 15 10 10 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(0 30 5 25 8)
+	SymbolLine(0 30 5 35 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(5 45 10 50 8)
+)
+Symbol('|' 12)
+(
+	SymbolLine(0 10 0 50 8)
+)
+Symbol('}' 12)
+(
+	SymbolLine(0 10 5 15 8)
+	SymbolLine(5 15 5 25 8)
+	SymbolLine(5 25 10 30 8)
+	SymbolLine(5 35 10 30 8)
+	SymbolLine(5 35 5 45 8)
+	SymbolLine(0 50 5 45 8)
+)
+Symbol('~' 12)
+(
+	SymbolLine(0 35 5 30 8)
+	SymbolLine(5 30 10 30 8)
+	SymbolLine(10 30 15 35 8)
+	SymbolLine(15 35 20 35 8)
+	SymbolLine(20 35 25 30 8)
+)
+Via[90000 50000 6000 2000 0 3500 "" ""]
+Layer(1 "component")
+(
+	Line[10000 50000 90000 50000 4000 2000 "clearline"]
+)
+Layer(2 "solder")
+(
+	Line[170000 50000 90000 50000 4000 2000 "clearline"]
+)
+Layer(3 "GND")
+(
+)
+Layer(4 "power")
+(
+)
+Layer(5 "signal1")
+(
+)
+Layer(6 "signal2")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "signal4")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/tests/orig/run_tests.sh b/tests/orig/run_tests.sh
new file mode 100755
index 0000000..61a5f60
--- /dev/null
+++ b/tests/orig/run_tests.sh
@@ -0,0 +1,681 @@
+#!/bin/sh
+#
+# $Id: run_tests.sh,v 1.8 2009/02/17 00:42:31 danmc Exp $
+#
+# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Dan McMahill
+
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of version 2 of the GNU General Public License as
+#  published by the Free Software Foundation
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
+# All rights reserved.
+#
+# This code was derived from code written by Dan McMahill as part of the
+# latex-mk testsuite.  The original code was covered by a BSD license
+# but the copyright holder (Dan McMahill) has re-released the code under
+# the GPL.
+
+if test -z `which gerbv`
+then
+	echo "Please install gerbv before running the tests." >&2
+	exit 1
+fi
+
+if test -z `which convert`
+then
+	echo "Please install imagemagick before running the tests." >&2
+	exit 1
+fi
+
+magic_test_skip=${PCB_MAGIC_TEST_SKIP:-no}
+
+if test "x${magic_test_skip}" = "xyes" ; then
+	cat << EOF
+
+The environment variable PCB_MAGIC_TEST_SKIP is set to yes.
+This causes the testsuite to skip all tests and report no errors.
+This is used for certain debugging *only*.  The primary use is to 
+allow testing of the 'distcheck' target without including the effects
+of the testsuite. The reason this is useful is that due to minor differences
+in library versions and perhaps roundoff in different CPU's, the testsuite
+may falsely report failures on some systems.  These reported failures
+prevent using 'distcheck' for verifying the rest of the build system.
+These comments only apply to the tests which try to compare image files
+like PNG files.
+
+EOF
+
+	exit 0
+fi
+
+regen=no
+
+usage() {
+cat <<EOF
+
+$0 -- Run pcb regression tests
+
+$0 -h|--help
+$0 [-d | --debug] [-g | --golden dir] [-r|--regen] [testname1 [testname2[ ...]]]
+
+OVERVIEW
+
+The $0 script is used both for running the pcb regression testsuite
+as well as maintaining it.  The way the test suite works is a number
+of different layouts are exported using the various export HID's.
+
+The resulting output files are examined in various ways to make sure
+they are correct.  The exact details of how they are compared varies.
+For example, the PNG outputs are compared using tools from the ImageMagick
+suite while the ASCII centroid and bill of materials files are normalized
+with awk and then compared with the standard diff utility.  The normalization
+removes things like a comment line which contains the creation date.
+
+OPTIONS
+
+-d | --debug              Enables extra debug output
+
+-g | --golden <dir>    :  Specifies that <dir> should be used for the
+                          reference files. 
+
+LIMITATIONS
+
+- The GUI interface is not checked via the regression testsuite.
+
+- Currently actions are also not exercised
+
+EOF
+}
+
+show_sep() {
+    echo "----------------------------------------------------------------------"
+}
+
+##########################################################################
+#
+# debug print out
+#
+
+do_debug=no
+debug() {
+    if test $do_debug = yes ; then
+	cat <<EOF
+$*
+EOF
+    fi
+}
+
+##########################################################################
+#
+# command line processing
+#
+
+
+all_tests=""
+while test -n "$1"
+  do
+  case "$1"
+      in
+      
+      -d|--debug)
+	  do_debug=yes
+	  shift
+	  ;;
+
+      -h|--help)
+	  usage
+	  exit 0
+	  ;;
+      
+      -g|--golden)
+	# set the 'golden' directory.
+	  REFDIR="$2"
+	  shift 2
+	  ;;
+      
+      -r|--regen)
+	# regenerate the 'golden' output files.  Use this with caution.
+	# In particular, all differences should be noted and understood.
+	  regen=yes
+	  shift
+	  ;;
+      
+      -*)
+	  echo "unknown option: $1"
+	  exit 1
+	  ;;
+      
+      *)
+	  break
+	  ;;
+      
+  esac
+done
+
+##########################################################################
+#
+# set up various tools
+#
+
+
+all_tests="$*"
+
+# Source directory
+srcdir=${srcdir:-.}
+top_srcdir=${top_srcdir:-.}
+
+# The pcb wrapper script we want to test
+#
+# by default we will be running it from $(top_builddir)/tests/outputs/testname
+# so we need to look 3 levels up and then down to src
+PCB=${PCB:-../../../../src/pcb-rnd}
+
+# The gerbv executable 
+GERBV=${GERBV:-gerbv}
+GERBV_DEFAULT_FLAGS=${GERBV_DEFAULT_FLAGS:---export=png --window=640x480}
+
+# various ImageMagick tools
+IM_ANIMATE=${IM_ANIMATE:-animate}
+IM_COMPARE=${IM_COMPARE:-compare}
+IM_COMPOSITE=${IM_COMPOSITE:-composite}
+IM_CONVERT=${IM_CONVERT:-convert}
+IM_DISPLAY=${IM_DISPLAY:-display}
+IM_MONTAGE=${IM_MONTAGE:-montage}
+
+# golden directories
+INDIR=${INDIR:-${srcdir}/inputs}
+OUTDIR=outputs
+REFDIR=${REFDIR:-${srcdir}/golden}
+
+# some system tools
+AWK=${AWK:-awk}
+
+# the list of tests to run
+TESTLIST=${srcdir}/tests.list
+
+if test "X$regen" = "Xyes" ; then
+    OUTDIR="${REFDIR}"
+fi
+
+# create output directory
+if test ! -d $OUTDIR ; then
+    mkdir -p $OUTDIR
+    if test $? -ne 0 ; then
+	echo "Failed to create output directory ${OUTDIR}"
+	exit 1
+    fi
+fi
+
+if test -z "$all_tests" ; then
+    if test ! -f ${TESTLIST} ; then
+	echo "ERROR: ($0)  Test list $TESTLIST does not exist"
+	exit 1
+    fi
+    all_tests=`${AWK} 'BEGIN{FS="|"} /^#/{next} {print $1}' ${TESTLIST} | sed 's; ;;g'`
+fi
+
+if test -z "${all_tests}" ; then
+    echo "$0:  No tests specified"
+    exit 0
+fi
+
+
+# fail/pass/total counts
+fail=0
+pass=0
+skip=0
+tot=0
+
+##########################################################################
+#
+# config summary
+#
+
+
+cat << EOF
+
+srcdir                ${srcdir}
+top_srcdir            ${top_srcdir}
+
+AWK                   ${AWK}
+PCB                   ${PCB}
+INDIR                 ${INDIR}
+OUTDIR                ${OUTDIR}
+REFDIR                ${REFDIR}
+TESTLIST              ${TESTLIST}
+
+Gerbv:
+
+GERBV                 ${GERBV}
+GERBV_DEFAULT_FLAGS   ${GERBV_DEFAULT_FLAGS}
+
+ImageMagick Tools:
+
+IM_ANIMATE               ${IM_ANIMATE}
+IM_COMPARE               ${IM_COMPARE}
+IM_COMPOSITE             ${IM_COMPOSITE}
+IM_CONVERT               ${IM_CONVERT}
+IM_DISPLAY               ${IM_DISPLAY}
+IM_MONTAGE               ${IM_MONTAGE}
+
+EOF
+
+tmpd=/tmp/pcb_tests.$$
+mkdir -p -m 0700 $tmpd
+rc=$?
+if test $rc -ne 0 ; then
+    echo "$0:  ERROR:  could not create $tmpd"
+    exit 1
+fi
+
+##########################################################################
+#
+# utility functions for comparison
+#
+
+# Usage:
+#  compare_check "test_name" "file1" "file2"
+#
+# Makes sure that file1 and file2 both exist.  If not, mark the current
+# test as skipped and give an error message
+#
+compare_check() {
+    fn="$1"
+    f1="$2"
+    f2="$3"
+
+    if test ! -f "$f2" ; then 
+	echo "$0:  ${fn}(): $f1 does not exist"
+	test_skipped=yes
+	return 1
+    fi
+
+    if test ! -f "$f2" ; then 
+	echo "$0:  ${fn}(): $f2 does not exist"
+	test_skipped=yes
+	return 1
+    fi
+    return 0
+}
+
+##########################################################################
+#
+# ASCII file comparison routines
+#
+
+# Usage:
+#   run_diff "file1" "file2"
+#
+run_diff() {
+    f1="$1"
+    f2="$2"
+    diff -U 2 $f1 $f2
+    if test $? -ne 0 ; then return 1 ; fi
+    return 0
+}
+
+##########################################################################
+#
+# BOM comparison routines
+#
+
+# used to remove things like creation date from BOM files
+normalize_bom() {
+    f1="$1"
+    f2="$2"
+    $AWK '
+	/^# Date:/ {print "# Date: today"; next}
+	/^# Author:/ {print "# Author: PCB"; next}
+	{print}' \
+	$f1 > $f2
+}
+
+# top level function to compare BOM output
+compare_bom() {
+    f1="$1"
+    f2="$2"
+    compare_check "compare_bom" "$f1" "$f2" || return 1
+
+    # an example BOM file is:
+
+    #  # $Id$
+    #  # PcbBOM Version 1.0
+    #  # Date: Wed Jun 17 14:41:43 2009 UTC
+    #  # Author: Dan McMahill
+    #  # Title: Basic BOM/XY Test - PCB BOM
+    #  # Quantity, Description, Value, RefDes
+    #  # --------------------------------------------
+    #  8,"Standard SMT resistor, capacitor etc","RESC3216N",R90_TOP R180_TOP R270_TOP R0_TOP R270_BOT R180_BOT R90_BOT R0_BOT 
+    #  8,"Dual in-line package, narrow (300 mil)","DIP8",UDIP90_TOP UDIP180_TOP UDIP270_TOP UDIP0_TOP UDIP270_BOT UDIP180_BOT UDIP90_BOT UDIP0_BOT 
+    #  8,"Small outline package, narrow (150mil)","SO8",USO90_TOP USO180_TOP USO270_TOP USO0_TOP USO270_BOT USO180_BOT USO90_BOT USO0_BOT 
+
+    #  For comparison, we need to ignore changes in the Date and Author lines.
+    cf1=${tmpd}/`basename $f1` 
+    cf2=${tmpd}/`basename $f2` 
+
+    normalize_bom $f1 $cf1
+    normalize_bom $f2 $cf2
+    run_diff $cf1 $cf2 || test_failed=yes
+}
+
+##########################################################################
+#
+# X-Y (centroid) comparison routines
+#
+
+# used to remove things like creation date from BOM files
+normalize_xy() {
+    f1="$1"
+    f2="$2"
+    $AWK '
+	/^# Date:/ {print "# Date: today"; next}
+	/^# Author:/ {print "# Author: PCB"; next}
+	{print}' \
+	$f1 > $f2
+}
+
+compare_xy() {
+    f1="$1"
+    f2="$2"
+    compare_check "compare_xy" "$f1" "$f2" || return 1
+    normalize_xy "$f1" "$cf1"
+    normalize_xy "$f2" "$cf2"
+    run_diff "$cf1" "$cf2" || test_failed=yes
+}
+
+##########################################################################
+#
+# GCODE comparison routines
+#
+
+# used to remove things like creation date from gcode files
+normalize_gcode() {
+    f1="$1"
+    f2="$2"
+    $AWK '
+	d == 1 {gsub(/ .* /, "Creation Date and Time"); d = 0;}
+	/^\(Created by G-code exporter\)/ {d=1}
+	{print}' \
+	$f1 > $f2
+}
+
+compare_gcode() {
+    f1="$1"
+    f2="$2"
+    compare_check "compare_gcode" "$f1" "$f2" || return 1
+
+    #  For comparison, we need to ignore changes in the Date and Author lines.
+    cf1=${tmpd}/`basename $f1` 
+    cf2=${tmpd}/`basename $f2` 
+
+    normalize_gcode $f1 $cf1
+    normalize_gcode $f2 $cf2
+    run_diff "$cf1" "$cf2" || test_failed=yes
+}
+
+##########################################################################
+#
+# RS274-X and Excellon comparison
+#
+
+compare_rs274x() {
+    f1="$1"
+    f2="$2"
+    compare_check "compare_rs274x" "$f1" "$f2" || return 1
+
+    # use gerbv to export our reference RS274-X file and our generated
+    # RS274-X file to png.  Then we'll use ImageMagick to see
+    # if there are any differences in the resulting files
+    pngdir=${rundir}/png
+    mkdir -p ${pngdir}
+    nb=`basename $f1`
+    png1=${pngdir}/${nb}-ref.png
+    png2=${pngdir}/${nb}-out.png
+
+    debug "${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png1} ${f1}"
+    ${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png1} ${f1}
+
+    debug "${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png2} ${f2}"
+    ${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png2} ${f2}
+
+    compare_image ${png1} ${png2}
+
+}
+
+compare_cnc() {
+    compare_rs274x $*
+}
+
+##########################################################################
+#
+# GIF/JPEG/PNG comparison routines
+#
+
+compare_image() {
+    f1="$1"
+    f2="$2"
+    compare_check "compare_image" "$f1" "$f2" || return 1
+
+    # now see if the image files are the same
+    debug "${IM_COMPARE} -metric MAE ${f1} ${f2}  null:"
+    same=`${IM_COMPARE} -metric MAE ${f1} ${f2}  null: 2>&1 | \
+          ${AWK} '{if($1 == 0){print "yes"} else {print "no"}}'`
+    debug "compare_image():  same = $same"
+
+    if test "$same" != yes ; then
+	test_failed=yes
+	echo "FAILED:  See ${errdir}"
+	mkdir -p ${errdir}
+	${IM_COMPARE} ${f1} ${f2} ${errdir}/compare.png
+	${IM_COMPOSITE} ${f1} ${f2} -compose difference ${errdir}/composite.png
+	${IM_CONVERT} ${f1} ${f2} -compose difference -composite  -colorspace gray   ${errdir}/gray.png
+	cat > ${errdir}/animate.sh << EOF
+#!/bin/sh
+${IM_CONVERT} -label "%f" ${f1} ${f2} miff:- | \
+${IM_MONTAGE} - -geometry +0+0 -tile 1x1 miff:- | \
+${IM_ANIMATE} -delay 0.5 -loop 0 -
+EOF
+	chmod a+x ${errdir}/animate.sh
+    fi
+}
+
+##########################################################################
+#
+# The main program loop
+#
+
+for t in $all_tests ; do
+    show_sep
+    echo "Test:  $t"
+
+    tot=`expr $tot + 1`
+
+    ######################################################################
+    #
+    # extract the details for the test
+    #
+
+    pcb_flags="${PCB_DEFAULT_FLAGS}"
+
+    rundir="${OUTDIR}/${t}"
+    refdir="${REFDIR}/${t}"
+    errdir=${rundir}/mismatch
+
+    # test_name | layout file(s) | [export hid name] | [optional arguments to pcb] |  [mismatch] 
+    # | output files
+    name=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $1}'`
+    files=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $2}'`
+    hid=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {gsub(/[ \t]*/, ""); print $3}'`
+    args=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $4}'`
+    mismatch=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {if($5 == "mismatch"){print "yes"}else{print "no"}}'`
+    out_files=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $6}'`
+   
+    if test "X${name}" = "X" ; then
+	echo "ERROR:  Specified test ${t} does not appear to exist"
+	skip=`expr $skip + 1`
+	continue
+    fi
+    
+    if test "X${args}" != "X" ; then
+	pcb_flags="${args}"
+    fi
+
+    if test "X${hid}" = "Xgerber" ; then
+	pcb_flags="--fab-author Fab_Author ${pcb_flags}"
+    fi
+
+    ######################################################################
+    #
+    # Set up the run directory
+    #
+
+    test -d ${rundir} && rm -fr ${rundir}
+    mkdir -p ${rundir}
+    if test $? -ne 0 ; then
+	echo "$0:  Could not create run directory ${rundir}"
+	skip=`expr $skip + 1`
+	continue
+    fi
+
+
+    ######################################################################
+    #
+    # check to see if the files we need exist and copy them to the run
+    # directory
+    #
+
+    missing_files=no
+    path_files=""
+    for f in $files ; do
+	if test ! -f ${INDIR}/${f} ; then
+	    echo "ERROR:  File $f specified as part of the $t test does not exist"
+	    missing_files=yes
+	else
+	    path_files="${path_files} ${INDIR}/${f}"
+	    cp "${INDIR}/${f}" "${rundir}"
+	fi
+    done
+
+    if test "$missing_files" = "yes" ; then
+	echo "${t} had missing input files.  Skipping test"
+	skip=`expr $skip + 1`
+	continue
+    fi
+    
+    ######################################################################
+    #
+    # run PCB
+    #
+
+    echo "cd ${rundir} && ${PCB} -x ${hid} ${pcb_flags} ${files}"
+    (cd ${rundir} && ${PCB} -x ${hid} ${pcb_flags} ${files})
+    pcb_rc=$?
+
+    if test $pcb_rc -ne 0 ; then
+	echo "${PCB} returned ${pcb_rc}.  This is a failure."
+	fail=`expr $fail + 1`
+	continue
+    fi
+
+    # and clean up the input files we'd copied over
+    for f in $files ; do
+	rm -f ${rundir}/${f}
+    done
+
+    ######################################################################
+    #
+    # check the result
+    #
+
+    if test "X$regen" = "Xyes" ; then
+	echo "Regenerated ${t}"
+    else
+	# compare the result to our reference file
+	test_failed=no
+	test_skipped=no
+	for f in $out_files ; do
+	    debug "processing $f"
+	    # break apart type:fn into the type and file name
+	    type=`echo $f | sed 's;:.*;;g'`
+	    fn=`echo $f | sed 's;.*:;;g'`
+
+	    case $type in
+		# BOM HID
+		bom)
+		    compare_bom ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+	        xy)
+		    compare_xy ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		# GCODE HID
+		gcode)
+		    compare_gcode ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		# GERBER HID
+		cnc)
+		    compare_cnc ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		gbx)
+		    compare_rs274x ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		# PNG HID
+	        gif)
+		    compare_image ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		jpg)
+		    compare_image ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		png)
+		    compare_image ${refdir}/${fn} ${rundir}/${fn}
+		    ;;
+
+		# unknown
+		*)
+		    echo "internal error:  $type is not a known file type"
+		    exit 1
+		    ;;
+	    esac
+
+	done
+
+
+	if test $test_failed = yes ; then
+	    echo "FAIL"
+	    fail=`expr $fail + 1`
+	elif test $test_skipped = yes ; then
+	    echo "SKIPPED"
+	    skip=`expr $skip + 1`
+	else
+	    echo "PASSED"
+	    pass=`expr $pass + 1`
+	fi
+
+    fi
+    
+done
+
+show_sep
+echo "Passed $pass, failed $fail, skipped $skip out of $tot tests."
+
+rc=0
+if test $pass -ne $tot ; then
+    rc=1
+fi
+
+exit $rc
+
diff --git a/tests/orig/tests.list b/tests/orig/tests.list
new file mode 100644
index 0000000..7468b45
--- /dev/null
+++ b/tests/orig/tests.list
@@ -0,0 +1,166 @@
+# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Dan McMahill
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of version 2 of the GNU General Public License as
+#  published by the Free Software Foundation
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
+#  All rights reserved.
+#
+# This code was derived from code written by Dan McMahill as part of the
+# latex-mk testsuite.  The original code was covered by a BSD license
+# but the copyright holder is releasing the version for pcb under the GPL.
+#
+#
+# Format:
+#
+# test_name | layout file(s) | [export hid name] | [optional arguments to pcb] |  [mismatch]
+# | output file(s)
+#
+# test_name - a single string with no spaces, tabs, *, +, ? (i.e. any "special"
+# characters) that identifies the test.
+#
+# layout file(s) - a list of layout files.  Files listed are relative to
+# the $(top_srcdir)/tests/inputs directory.
+#
+# [export hid name] - the name of the export HID to use.  This is used both for
+# running pcb as well as determining how we process the output
+#
+# [optional arguments to pcb] - a list of additional arguments to be passed to pcb.
+# This is where one would specify additional options which are specific to a particular
+# export HID.
+#
+# [mismatch] If specified as "mismatch" (no quotes), then the result 
+# should *not* match the reference.  This can be thought of as a test
+# on the testsuite to make sure we can properly detect when a change has
+# occurred.
+#
+# output file(s) - a list of output files and their associated types.  For
+# example:
+#  bom:bom_general.bom xy:test.txt
+# specifies that "bom_general.bom" is created and it is a bill of materials file
+# and that "test.txt" is created and it is a centroid (X-Y) file.
+#
+# File types grouped by which HID produces them are:
+#
+# BOM
+#
+#    bom -- PCB bill of materials file
+#    xy  -- PCB centroid file
+#
+# GCODE
+#
+#    gcode -- G-Code file.  Note that these typically have .cnc as the 
+#             extension but we're already using the 'cnc' type for 
+#             Excellon drill files.
+#
+# GERBER
+#
+#    cnc -- Excellon drill file
+#    gbx -- RS274-X (Gerber) file
+#
+# PNG
+#
+#    gif -- GIF file
+#    jpg -- JPEG file
+#    png -- Portable network graphics (PNG) file
+#
+######################################################################
+# ---------------------------------------------
+# BOM export HID
+# ---------------------------------------------
+######################################################################
+#
+# options:
+#  --bomfile <string>             BOM output file
+#  --xyfile <string>              XY output file
+#  --xy-in-mm                     XY dimensions in mm instead of mils
+#
+#
+# Produces a bill of materials (BOM) file and a centroid (XY) file
+#
+hid_bom1 | bom_general.pcb | bom | | | bom:bom_general.bom xy:bom_general.xy
+hid_bom2 | bom_general.pcb | bom | --bomfile test.bom | | bom:test.bom xy:bom_general.xy
+hid_bom3 | bom_general.pcb | bom | --xyfile  test.xy  | | bom:bom_general.bom xy:test.xy
+hid_bom4 | bom_general.pcb | bom | --xy-in-mm | |  bom:bom_general.bom xy:bom_general.xy
+#
+######################################################################
+# ---------------------------------------------
+# Gcode export HID
+# ---------------------------------------------
+######################################################################
+#
+# options:
+# --basename <string>            File name prefix
+# --dpi <num>                    Resolution of intermediate image (pixels/inch).
+# --mill depth <num>             Milling depth.
+# --safe Z <num>                 Safe Z for traverse move.
+# --tool radius <num>            Milling tool radius compensation.
+# --drill depth <num>            Drilling depth.
+# --measurement unit <mm|mil|um|inch>    Measurement unit
+#
+hid_gcode1 | gcode_oneline.pcb | gcode | | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode2 | gcode_oneline.pcb | gcode | --basename out | | gcode:out.top.cnc gcode:out.bottom.cnc gcode:out.drill.cnc
+hid_gcode3 | gcode_oneline.pcb | gcode | --dpi 1200 | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode4 | gcode_oneline.pcb | gcode | --mill-depth 5 | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode5 | gcode_oneline.pcb | gcode | --safe-Z 10 | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode6 | gcode_oneline.pcb | gcode | --tool-radius 15 | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode7 | gcode_oneline.pcb | gcode | --drill-depth 70 | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode8 | gcode_oneline.pcb | gcode | --measurement-unit mm | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode9 | gcode_oneline.pcb | gcode | --measurement-unit mil | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode10 | gcode_oneline.pcb | gcode | --measurement-unit um | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+hid_gcode11 | gcode_oneline.pcb | gcode | --measurement-unit inch | | gcode:gcode_oneline.gcode.top.cnc gcode:gcode_oneline.gcode.bottom.cnc gcode:gcode_oneline.gcode.drill.cnc
+#
+######################################################################
+# ---------------------------------------------
+# Gerber export HID
+# ---------------------------------------------
+######################################################################
+#
+# options:
+#  --gerberfile <string>          Basename for output file
+#
+# Produces RS274-X (a.k.a. gerber) photo plot files and Excellon drill files
+#
+# we can't include gbx:gerber_oneline.fab.gbr yet because it has a name and a date stamp
+hid_gerber1 | gerber_oneline.pcb | gerber | | | gbx:gerber_oneline.bottom.gbr  gbx:gerber_oneline.top.gbr cnc:gerber_oneline.plated-drill.cnc
+hid_gerber2 | gerber_oneline.pcb | gerber | --gerberfile out | | gbx:out.bottom.gbr  gbx:out.top.gbr cnc:out.plated-drill.cnc
+hid_gerber3 | gerber_arcs.pcb | gerber | --gerberfile arcs | | gbx:arcs.bottom.gbr gbx:arcs.top.gbr gbx:arcs.group1.gbr gbx:arcs.group4.gbr cnc:arcs.plated-drill.cnc
+#
+
+
+######################################################################
+# ---------------------------------------------
+# PNG export HID
+# ---------------------------------------------
+######################################################################
+#
+# options:
+#   --outfile <string>             Graphics output file
+#   --dpi <num>                    Scale factor (pixels/inch). 0 to scale to fix specified size
+#   --x-max <num>                  Maximum width (pixels).  0 to not constrain.
+#   --y-max <num>                  Maximum height (pixels).  0 to not constrain.
+#   --xy-max <num>                 Maximum width and height (pixels).  0 to not constrain.
+#   --as-shown                     Export layers as shown on screen
+#   --monochrome                   Convert to monochrome
+#   --only-visible                 Limit the bounds of the PNG file to the visible items
+#   --use-alpha                    Make the bottomground and any holes transparent
+#   --format <GIF|JPEG|PNG>        Graphics file format
+#   --photo-mode                   Photo-realistic mode
+#   --photo-flip-x                 Show reverse side of the board, left-right flip
+#   --photo-flip-y                 Show reverse side of the board, up-down flip
+#
+# Produces GIF/JPEG/PNG (image) files
+#
+hid_png1 | gerber_oneline.pcb | png | | | png:gerber_oneline.png
+hid_png2 | gerber_oneline.pcb | png | --outfile myfile.png | | png:myfile.png
+#hid_png3 | gerber_oneline.pcb | png | --dpi 300 | | png:gerber_oneline.png
+#
+
diff --git a/tests/pcb-printf/Makefile b/tests/pcb-printf/Makefile
new file mode 100644
index 0000000..2cb5a0c
--- /dev/null
+++ b/tests/pcb-printf/Makefile
@@ -0,0 +1,44 @@
+TRUNK = ../..
+SRC=$(TRUNK)/src
+CFLAGS_OP = -O3
+CFLAGS = -I$(TRUNK) -I$(SRC) -I$(TRUNK)/src_3rd -I$(TRUNK)/src_3rd/liblihata
+LDFLAGS = -lm
+
+GDS= $(TRUNK)/src_3rd/genvector/gds_char.o
+
+all: tester_spd prcli
+
+test: tester.diff
+	@echo "*** all ok, QC PASS ***"
+	@rm tester.stdout ; true
+
+prcli: prcli.o $(SRC)/pcb-printf.o $(SRC)/unit.o $(GDS) $(SRC)/misc_util.o $(GDS)
+
+prcli.o: prcli.c
+
+tester: tester.o $(SRC)/pcb-printf.o $(SRC)/unit.o $(GDS)
+
+tester_spd: tester_spd.o $(SRC)/pcb-printf_spd.o $(SRC)/unit.o $(GDS)
+
+tester.o: tester.c
+	$(CC) -c $(CFLAGS) -o $@ tester.c
+
+tester_spd.o: tester.c
+	$(CC) -c $(CFLAGS_OP) -DSPEED $(CFLAGS) -o $@ tester.c
+
+tester.stdout: tester
+	./tester > tester.stdout
+
+tester.diff: tester.stdout
+	diff -u tester.ref tester.stdout
+
+$(SRC)/pcb-printf.o: $(SRC)/pcb-printf.c $(SRC)/pcb-printf.h
+
+$(SRC)/unit.o: $(SRC)/unit.c $(SRC)/unit.h
+
+$(SRC)/pcb-printf_spd.o: $(SRC)/pcb-printf.c $(SRC)/pcb-printf.h
+	$(CC) -c $(CFLAGS_OP) -DSPEED $(CFLAGS) -o $@ $(SRC)/pcb-printf.c
+
+clean:
+	rm tester tester.o 2>/dev/null ; true
+
diff --git a/tests/pcb-printf/prcli.c b/tests/pcb-printf/prcli.c
new file mode 100644
index 0000000..a83b8c4
--- /dev/null
+++ b/tests/pcb-printf/prcli.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include "misc_util.h"
+#include "pcb-printf.h"
+#include "pcb_bool.h"
+
+int main(int argc, char *argv[])
+{
+	const char *fmt = argv[1];
+	Coord crd;
+	int n;
+
+	pcb_printf_slot[0] = "%mr";
+
+	for(n = 2; n < argc; n++) {
+		pcb_bool success;
+		double val = GetValueEx(argv[n], NULL, NULL, NULL, "", &success);
+		if (!success) {
+			fprintf(stderr, "Unable to convert '%s' to coord\n", argv[n]);
+			return 1;
+		}
+		crd = val;
+	}
+
+	pcb_fprintf(stdout, fmt, crd, 70000, 70000, 70000, 70000);
+
+	printf("\n");
+
+	return 0;
+}
\ No newline at end of file
diff --git a/tests/pcb-printf/tester.c b/tests/pcb-printf/tester.c
new file mode 100644
index 0000000..5f1c6a8
--- /dev/null
+++ b/tests/pcb-printf/tester.c
@@ -0,0 +1,71 @@
+#include <time.h>
+#include <locale.h>
+#include "config.h"
+#include "global.h"
+#include "pcb-printf.h"
+
+#ifdef SPEED
+	char buff[8192];
+	int pc = 0;
+#	define NUMREP 4000
+#	define PCB_PRINTF(fmt, ...) \
+	do { \
+		pc++; \
+		pcb_snprintf(buff, sizeof(buff), fmt, __VA_ARGS__); \
+	} while(0)
+#else
+#	define NUMREP 1
+#	define PCB_PRINTF pcb_printf
+#endif
+
+int main()
+{
+	Coord c[] = {0, 1, 1024, 1024*1024, 1024*1024*1024};
+	char *fmt[] = {
+	"%mI",  "%mm",  "%mM",  "%ml",  "%mL",  "%ms",  "%mS",  "%md",  "%mD",  "%m3",  "%mr",
+	"%$mI", "%$mm", "%$mM", "%$ml", "%$mL", "%$ms", "%$mS", "%$md", "%$mD", "%$m3", "%$mr",
+	NULL };
+	char **f;
+	int n, rep;
+
+	setlocale(LC_ALL, "");
+
+	for(rep = 0; rep < NUMREP; rep++) {
+		for(n = 0; n < sizeof(c) / sizeof(c[0]); n++) {
+#ifndef SPEED
+			printf("---------------\n");
+#endif
+			/* common format strings */
+			for(f = fmt; *f != NULL; f++) {
+				char fmt_tmp[32];
+				sprintf(fmt_tmp, "%s=%s\n", (*f)+1, *f);
+				PCB_PRINTF(fmt_tmp, c[n], c[n], c[n]);
+			}
+
+			/* special: custom argument list */
+			PCB_PRINTF("m*=%m* mil\n", "mil", c[n]);
+			PCB_PRINTF("m*=%m* mm\n", "mm", c[n]);
+
+			PCB_PRINTF("ma=%ma\n", 1.1234);
+			PCB_PRINTF("ma=%ma\n", 130.1234);
+			PCB_PRINTF("ma=%ma\n", 555.1234);
+
+			PCB_PRINTF("m+=%m+%mS mil\n", ALLOW_MIL, c[n]);
+			PCB_PRINTF("m+=%m+%mS mm\n", ALLOW_MM, c[n]);
+			PCB_PRINTF("m+=%m+%mS m\n", ALLOW_M, c[n]);
+			PCB_PRINTF("m+=%m+%mS mm or m\n", ALLOW_MM | ALLOW_M, c[n]);
+		}
+	}
+
+#ifdef SPEED
+	{
+		double spent = (double)clock() / (double)CLOCKS_PER_SEC;
+		printf("total number of pcb_printf calls:  %d\n", pc);
+		printf("total number of CPU seconds spent: %.4f\n", spent);
+		printf("Average prints per second: %.4f\n", (double)pc/spent);
+	}
+#endif
+	return 0;
+}
+
+
diff --git a/tests/pcb-printf/tester.ref b/tests/pcb-printf/tester.ref
new file mode 100644
index 0000000..eb6811c
--- /dev/null
+++ b/tests/pcb-printf/tester.ref
@@ -0,0 +1,160 @@
+---------------
+mI=0
+mm=0.0000
+mM=0
+ml=0.00
+mL=0
+ms=0.0000
+mS=0
+md=(0.0000, 0.0000)
+mD=(0, 0)
+m3=(0, 0, 0)
+mr=0
+$mI=0
+$mm=0.0000
+$mM=0
+$ml=0.00
+$mL=0
+$ms=0.0000
+$mS=0
+$md=(0.0000, 0.0000) mm
+$mD=(0, 0) nm
+$m3=(0, 0, 0) nm
+$mr=0
+m*=0.00 mil
+m*=0.0000 mm
+ma=1
+ma=130
+ma=555
+m+=0.00 mil
+m+=0.0000 mm
+m+=0.00000 m
+m+=0.0000 mm or m
+---------------
+mI=1
+mm=0.0000
+mM=1
+ml=0.00
+mL=0
+ms=0.0000
+mS=1
+md=(0.0000, 0.0000)
+mD=(1, 1)
+m3=(1, 1, 1)
+mr=0
+$mI=1
+$mm=0.0000 mm
+$mM=1 nm
+$ml=0.00 mil
+$mL=0 cmil
+$ms=0.0000 mm
+$mS=1 nm
+$md=(0.0000, 0.0000) mm
+$mD=(1, 1) nm
+$m3=(1, 1, 1) nm
+$mr=0
+m*=0.00 mil
+m*=0.0000 mm
+ma=1
+ma=130
+ma=555
+m+=0.00 mil
+m+=0.0000 mm
+m+=0.00000 m
+m+=0.0000 mm or m
+---------------
+mI=1024
+mm=0.0010
+mM=1.02
+ml=0.04
+mL=4
+ms=0.0010
+mS=1.02
+md=(0.0010, 0.0010)
+mD=(1.02, 1.02)
+m3=(1.02, 1.02, 1.02)
+mr=4
+$mI=1024
+$mm=0.0010 mm
+$mM=1.02 um
+$ml=0.04 mil
+$mL=4 cmil
+$ms=0.0010 mm
+$mS=1.02 um
+$md=(0.0010, 0.0010) mm
+$mD=(1.02, 1.02) um
+$m3=(1.02, 1.02, 1.02) um
+$mr=4
+m*=0.04 mil
+m*=0.0010 mm
+ma=1
+ma=130
+ma=555
+m+=0.04 mil
+m+=0.0010 mm
+m+=0.00000 m
+m+=0.0010 mm or m
+---------------
+mI=1048576
+mm=1.0486
+mM=1.0486
+ml=41.28
+mL=41.28
+ms=1.0486
+mS=1.0486
+md=(1.0486, 1.0486)
+mD=(1.0486, 1.0486)
+m3=(1.0486, 1.0486, 1.0486)
+mr=4128
+$mI=1048576
+$mm=1.0486 mm
+$mM=1.0486 mm
+$ml=41.28 mil
+$mL=41.28 mil
+$ms=1.0486 mm
+$mS=1.0486 mm
+$md=(1.0486, 1.0486) mm
+$mD=(1.0486, 1.0486) mm
+$m3=(1.0486, 1.0486, 1.0486) mm
+$mr=4128
+m*=41.28 mil
+m*=1.0486 mm
+ma=1
+ma=130
+ma=555
+m+=41.28 mil
+m+=1.0486 mm
+m+=0.00105 m
+m+=1.0486 mm or m
+---------------
+mI=1073741824
+mm=1073.7418
+mM=1.07374
+ml=42273.30
+mL=42.27330
+ms=1073.7418
+mS=1.07374
+md=(1073.7418, 1073.7418)
+mD=(1.07374, 1.07374)
+m3=(1.07374, 1.07374, 1.07374)
+mr=4227330
+$mI=1073741824
+$mm=1073.7418 mm
+$mM=1.07374 m
+$ml=42273.30 mil
+$mL=42.27330 in
+$ms=1073.7418 mm
+$mS=1.07374 m
+$md=(1073.7418, 1073.7418) mm
+$mD=(1.07374, 1.07374) m
+$m3=(1.07374, 1.07374, 1.07374) m
+$mr=4227330
+m*=42273.30 mil
+m*=1073.7418 mm
+ma=1
+ma=130
+ma=555
+m+=42273.30 mil
+m+=1073.7418 mm
+m+=1.07374 m
+m+=1.07374 mm or m
diff --git a/tests/pcbflags/Makefile b/tests/pcbflags/Makefile
new file mode 100644
index 0000000..b5c8c46
--- /dev/null
+++ b/tests/pcbflags/Makefile
@@ -0,0 +1,4 @@
+all:
+
+test:
+	@./test.sh
diff --git a/tests/pcbflags/readme.txt b/tests/pcbflags/readme.txt
new file mode 100644
index 0000000..b70e83a
--- /dev/null
+++ b/tests/pcbflags/readme.txt
@@ -0,0 +1,2 @@
+Test whether chaning design configuration flags one by one result in saving
+the right pcb flags in the original file format.
diff --git a/tests/pcbflags/test.sh b/tests/pcbflags/test.sh
new file mode 100755
index 0000000..40b307d
--- /dev/null
+++ b/tests/pcbflags/test.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+src_dir=../../src
+pwd=`pwd`
+
+# $1 must be a .pcb file name
+run_pcb()
+{
+	local fn
+	if test ! -z "$1"
+	then
+		fn="$pwd/$1"
+	fi
+	shift 1
+	(
+		cd "$src_dir"
+		if test -z "$fn"
+		then
+			./pcb-rnd --gui batch "$@"
+		else
+			./pcb-rnd --gui batch "$fn" "$@"
+		fi
+	)
+}
+
+
+test_flag()
+{
+	local name="`basename $1`" newf expect="Flags(\"$3\")" res
+	if test -z "$name"
+	then
+		name="0"
+	fi
+	rm $name 2>/dev/null
+	echo "
+conf(set, $1, $2, design)
+SaveTo(LayoutAs, $pwd/$name.pcb)
+dumpconf(lihata,design)
+" | run_pcb vanilla.pcb >$name.log 2>&1
+	newf=`grep ^Flags $name.pcb`
+	test "$newf" = "$expect"
+	res=$?
+	if test $res != 0
+	then
+		echo "$1 save broken: expected '$expect' got '$newf'"
+		return 1
+	fi
+
+
+	echo "###### load test" >> $name.log
+	echo "
+conf(reset, system)
+conf(reset, project)
+LoadFrom(Layout, $pwd/$name.pcb)
+SaveTo(LayoutAs, $pwd/$name.2.pcb)
+dumpconf(lihata,design)
+" | run_pcb >>$name.log 2>&1
+
+	newf=`grep ^Flags $name.2.pcb`
+	test "$newf" = "$expect"
+	res=$?
+	if test $res != 0
+	then
+		echo "$1 load broken: expected '$expect' got '$newf'"
+		return 1
+	fi
+
+	return 0
+}
+
+
+test_flag "plugins/mincut/enable" "true" "enablemincut"
+test_flag "editor/show_number" "true" "shownumber"
+test_flag "editor/show_drc" "true" "showdrc"
+test_flag "editor/rubber_band_mode" "true" "rubberband"
+test_flag "editor/auto_drc" "true" "autodrc"
+test_flag "editor/all_direction_lines" "true" "alldirection"
+test_flag "editor/swap_start_direction" "true" "swapstartdir"
+test_flag "editor/unique_names" "true" "uniquename"
+test_flag "editor/clear_line" "true" "clearnew"
+test_flag "editor/full_poly" "true" "newfullpoly"
+test_flag "editor/snap_pin" "true" "snappin"
+test_flag "editor/orthogonal_moves" "true" "orthomove"
+test_flag "editor/live_routing" "true" "liveroute"
+test_flag "editor/enable_stroke" "true" "enablestroke"
+test_flag "editor/only_names" "true" "onlynames"
+test_flag "editor/lock_names" "true" "locknames"
+test_flag "editor/hide_names" "true" "hidenames"
+test_flag "editor/thin_draw" "true" "thindraw"
+test_flag "editor/thin_draw_poly" "true" "thindrawpoly"
+test_flag "editor/local_ref" "true" "localref"
+test_flag "editor/check_planes" "true" "checkplanes"
+test_flag "editor/description" "true" "description"
+test_flag "editor/name_on_pcb" "true" "nameonpcb"
+test_flag "editor/show_mask" "true" "showmask"
+
diff --git a/tests/pcbflags/vanilla.pcb b/tests/pcbflags/vanilla.pcb
new file mode 100644
index 0000000..6a6899a
--- /dev/null
+++ b/tests/pcbflags/vanilla.pcb
@@ -0,0 +1,15 @@
+# release: pcb-rnd 1.0.7
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 600000 500000]
+
+Grid[2500.0 0 0 1]
+Cursor[282500 172500 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("")
+Groups("")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,20000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
diff --git a/tests/propedit/Makefile b/tests/propedit/Makefile
new file mode 100644
index 0000000..99ceb50
--- /dev/null
+++ b/tests/propedit/Makefile
@@ -0,0 +1,32 @@
+ROOT=../..
+PED=$(ROOT)/src_plugins/propedit
+LHT=$(ROOT)/src_3rd/liblihata
+SRC=$(ROOT)/src
+
+CFLAGS = -g -Wall -I$(PED) -I$(LHT) -I$(SRC) -I$(ROOT) -I$(ROOT)/src_3rd
+LIB_OBJS = \
+ $(LHT)/genht/htsp.o \
+ $(LHT)/genht/hash.o \
+ $(SRC)/compat_misc.o \
+ $(SRC)/pcb-printf.o \
+ $(SRC)/unit.o \
+ $(ROOT)/src_3rd/genvector/gds_char.o
+
+all: tester
+
+test: tester.diff
+	@echo "*** All ok, QC passed ***"
+	@rm tester.out
+
+tester: tester.o $(PED)/props.o $(LIB_OBJS)
+
+tester.o: tester.c $(PED)/props.h
+
+$(PED)/props.o: $(PED)/props.c $(PED)/props.h
+
+tester.diff: tester.ref tester.out
+	@diff -u tester.ref tester.out
+
+tester.out: tester
+	./tester > tester.out
+
diff --git a/tests/propedit/tester.c b/tests/propedit/tester.c
new file mode 100644
index 0000000..cdcb382
--- /dev/null
+++ b/tests/propedit/tester.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <assert.h>
+#include "props.h"
+
+static void print_val(pcb_prop_type_t type, pcb_propval_t val)
+{
+	switch(type) {
+		case PCB_PROPT_STRING: printf("%s", val.string); break;
+		case PCB_PROPT_COORD:  printf("%d", val.coord); break;
+		case PCB_PROPT_ANGLE:  printf("%f", val.angle); break;
+		case PCB_PROPT_INT:    printf("%d", val.i); break;
+		default: printf(" ???");
+	}
+}
+
+static void print_all(htsp_t *props)
+{
+	htsp_entry_t *pe;
+	for (pe = htsp_first(props); pe; pe = htsp_next(props, pe)) {
+		htprop_entry_t *e;
+		pcb_props_t *p = pe->value;
+		printf("%s [%s]\n", pe->key, pcb_props_type_name(p->type));
+		for (e = htprop_first(&p->values); e; e = htprop_next(&p->values, e)) {
+			printf(" ");
+			print_val(p->type, e->key);
+			printf(" (%lu)\n", e->value);
+		}
+	}
+}
+
+static pcb_props_t *print_stat(htsp_t *props, const char *name, int all)
+{
+	pcb_propval_t most_common, min, max, avg;
+	pcb_props_t *p;
+
+	if (all)
+		p = pcb_props_stat(props, name, &most_common, &min, &max, &avg);
+	else
+		p = pcb_props_stat(props, name, &most_common, NULL, NULL, NULL);
+
+	if (p == NULL)
+		return NULL;
+
+	printf("Stats %s:", name);
+	printf("  common: "); print_val(p->type, most_common);
+	if (all) {
+		printf("  min: "); print_val(p->type, min);
+		printf("  max: "); print_val(p->type, max);
+		printf("  avg: "); print_val(p->type, avg);
+	}
+	printf("\n");
+
+	return p;
+}
+
+int main()
+{
+	htsp_t *props = pcb_props_init();
+	assert(props != NULL);
+	pcb_propval_t v;
+
+	/* --- add a few items properly - should work --- */
+	
+	/* coord */
+	v.coord = 42; assert(pcb_props_add(props, "crd", PCB_PROPT_COORD, v) != NULL);
+	v.coord = 10; assert(pcb_props_add(props, "crd", PCB_PROPT_COORD, v) != NULL);
+	v.coord = 42; assert(pcb_props_add(props, "crd", PCB_PROPT_COORD, v) != NULL);
+	v.coord = 42; assert(pcb_props_add(props, "crd", PCB_PROPT_COORD, v) != NULL);
+
+	/* int */
+	v.i = 42; assert(pcb_props_add(props, "num", PCB_PROPT_INT, v) != NULL);
+	v.i = 10; assert(pcb_props_add(props, "num", PCB_PROPT_INT, v) != NULL);
+	v.i = 42; assert(pcb_props_add(props, "num", PCB_PROPT_INT, v) != NULL);
+	v.i = 42; assert(pcb_props_add(props, "num", PCB_PROPT_INT, v) != NULL);
+
+	/* angle */
+	v.angle = 42.0; assert(pcb_props_add(props, "ang", PCB_PROPT_ANGLE, v) != NULL);
+	v.angle = 10.5; assert(pcb_props_add(props, "ang", PCB_PROPT_ANGLE, v) != NULL);
+	v.angle = 42.0; assert(pcb_props_add(props, "ang", PCB_PROPT_ANGLE, v) != NULL);
+	v.angle = 42.0; assert(pcb_props_add(props, "ang", PCB_PROPT_ANGLE, v) != NULL);
+
+	/* string */
+	v.string = "foo"; assert(pcb_props_add(props, "str", PCB_PROPT_STRING, v) != NULL);
+	v.string = "bar"; assert(pcb_props_add(props, "str", PCB_PROPT_STRING, v) != NULL);
+	v.string = "BAZ"; assert(pcb_props_add(props, "str", PCB_PROPT_STRING, v) != NULL);
+	v.string = "foo"; assert(pcb_props_add(props, "str", PCB_PROPT_STRING, v) != NULL);
+	v.string = "foo"; assert(pcb_props_add(props, "str", PCB_PROPT_STRING, v) != NULL);
+
+	/* --- add a items with the wrong type - should fail --- */
+	v.i = 42; assert(pcb_props_add(props, "crd", PCB_PROPT_INT, v) == NULL);
+	v.i = 42; assert(pcb_props_add(props, "crd", 1234, v) == NULL);
+	v.i = 42; assert(pcb_props_add(props, "ang", 1234, v) == NULL);
+
+	print_all(props);
+
+	/* --- get some stats --- */
+
+	/* these should work */
+	assert(print_stat(props, "crd", 1) != NULL);
+	assert(print_stat(props, "num", 1) != NULL);
+	assert(print_stat(props, "ang", 1) != NULL);
+	assert(print_stat(props, "str", 0) != NULL);
+
+	/* these should fail */
+	assert(print_stat(props, "str", 1) == NULL);
+	assert(print_stat(props, "HAH", 1) == NULL);
+
+	return 0;
+}
diff --git a/tests/propedit/tester.ref b/tests/propedit/tester.ref
new file mode 100644
index 0000000..66d167a
--- /dev/null
+++ b/tests/propedit/tester.ref
@@ -0,0 +1,17 @@
+num [int]
+ 42 (3)
+ 10 (1)
+ang [angle]
+ 42.000000 (3)
+ 10.500000 (1)
+crd [coord]
+ 42 (3)
+ 10 (1)
+str [string]
+ foo (3)
+ bar (1)
+ BAZ (1)
+Stats crd:  common: 42  min: 10  max: 42  avg: 34
+Stats num:  common: 42  min: 10  max: 42  avg: 34
+Stats ang:  common: 42.000000  min: 10.500000  max: 42.000000  avg: 34.125000
+Stats str:  common: foo
diff --git a/tests/strflags/Makefile b/tests/strflags/Makefile
new file mode 100644
index 0000000..fc44613
--- /dev/null
+++ b/tests/strflags/Makefile
@@ -0,0 +1,25 @@
+TRUNK=../..
+IO=$(TRUNK)/src_plugins/io_pcb
+CFLAGS = -O3 -I$(TRUNK) -I$(TRUNK)/src -I$(IO) -I$(TRUNK)/src_3rd
+GDS= $(TRUNK)/src_3rd/genvector/gds_char.o
+
+all: tester
+
+test: tester.diff
+	@echo "*** all ok, QC PASS ***"
+	@rm tester.stdout
+
+tester: tester.o $(GDS)
+
+tester.o: tester.c
+	$(CC) -c $(CFLAGS) -o $@ tester.c
+
+tester.stdout: tester
+	./tester > tester.stdout
+
+tester.diff: tester.stdout
+	diff -u tester.ref tester.stdout
+
+clean:
+	rm tester tester.o 2>/dev/null ; true
+
diff --git a/tests/strflags/tester.c b/tests/strflags/tester.c
new file mode 100644
index 0000000..4eabe32
--- /dev/null
+++ b/tests/strflags/tester.c
@@ -0,0 +1,162 @@
+/*
+ *                            COPYRIGHT
+ *
+ *  PCB, interactive printed circuit board design
+ *  Copyright (C) 2005 DJ Delorie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Contact addresses for paper mail and Email:
+ *  DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
+ *  dj at delorie.com
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "compat_misc.h"
+
+#define FLAG_TEST
+#include "strflags.c"
+#include "flags.c"
+#include "compat_misc.c"
+
+static void dump_flag(FlagType * f)
+{
+	int l;
+	printf("F:%08x T:[", f->f);
+	for (l = 0; l < (MAX_LAYER + 7) / 8; l++)
+		printf(" %02x", f->t[l]);
+	printf("]");
+}
+
+
+int mem_any_set(unsigned char *ptr, int bytes)
+{
+	while (bytes--)
+		if (*ptr++)
+			return 1;
+	return 0;
+}
+
+int main()
+{
+	time_t now;
+	int i;
+	int errors = 0, count = 0;
+
+	time(&now);
+	srandom((unsigned int) now + getpid());
+
+	grow_layer_list(0);
+	for (i = 0; i < 16; i++) {
+		int j;
+		char *p;
+		if (i != 1 && i != 4 && i != 5 && i != 9)
+			set_layer_list(i, 1);
+		else
+			set_layer_list(i, 0);
+		p = print_layer_list();
+		printf("%2d : %20s =", i, p);
+		parse_layer_list(p + 1, 0);
+		for (j = 0; j < num_layers; j++)
+			printf(" %d", layers[j]);
+		printf("\n");
+	}
+
+	while (count < 1000000) {
+		FlagHolder fh;
+		char *str;
+		FlagType new_flags;
+		int i;
+		int otype;
+
+		otype = PCB_TYPEMASK_ALL;
+		fh.Flags = empty_flags;
+		for (i = 0; i < ENTRIES(object_flagbits); i++) {
+			if (TEST_FLAG(object_flagbits[i].mask, &fh))
+				continue;
+			if ((otype & object_flagbits[i].object_types) == 0)
+				continue;
+			if ((random() & 4) == 0)
+				continue;
+
+			otype &= object_flagbits[i].object_types;
+			SET_FLAG(object_flagbits[i].mask, &fh);
+		}
+
+		if (otype & PCB_TYPEMASK_PIN)
+			for (i = 0; i < MAX_LAYER; i++)
+				if (random() & 4)
+					ASSIGN_THERM(i, 3, &fh);
+
+		str = flags_to_string(fh.Flags, otype);
+		new_flags = string_to_flags(str, 0);
+
+		count++;
+		if (FLAGS_EQUAL(fh.Flags, new_flags))
+			continue;
+		dump_flag(&fh.Flags);
+		printf(" ");
+		dump_flag(&new_flags);
+		printf("\n");
+		if (++errors == 5)
+			goto bad;
+	}
+
+	while (count < 1000000) {
+		FlagHolder fh;
+		char *str;
+		FlagType new_flags;
+		int i;
+		int otype;
+
+		otype = PCB_TYPEMASK_ALL;
+		fh.Flags = empty_flags;
+		for (i = 0; i < ENTRIES(pcb_flagbits); i++) {
+			if (TEST_FLAG(pcb_flagbits[i].mask, &fh))
+				continue;
+			if ((random() & 4) == 0)
+				continue;
+
+			otype &= pcb_flagbits[i].object_types;
+			SET_FLAG(pcb_flagbits[i].mask, &fh);
+		}
+
+		str = pcbflags_to_string(fh.Flags);
+		new_flags = string_to_pcbflags(str, 0);
+
+		count++;
+		if (FLAGS_EQUAL(fh.Flags, new_flags))
+			continue;
+
+		dump_flag(&fh.Flags);
+		printf(" ");
+		dump_flag(&new_flags);
+		printf("\n");
+
+		if (++errors == 5)
+			goto bad;
+	}
+	printf("%d out of %d failed\n", errors, count);
+	return errors;
+
+	bad:;
+	printf("%d out of %d FAILED\n", errors, count);
+	return errors;
+}
+
diff --git a/tests/strflags/tester.ref b/tests/strflags/tester.ref
new file mode 100644
index 0000000..20cc086
--- /dev/null
+++ b/tests/strflags/tester.ref
@@ -0,0 +1,17 @@
+ 0 :                  (0) = 1
+ 1 :                  (0) = 1
+ 2 :                (0,2) = 1 0 1
+ 3 :              (0,2,3) = 1 0 1 1
+ 4 :              (0,2,3) = 1 0 1 1
+ 5 :              (0,2,3) = 1 0 1 1
+ 6 :            (0,2,3,6) = 1 0 1 1 0 0 1
+ 7 :          (0,2,3,6,7) = 1 0 1 1 0 0 1 1
+ 8 :          (0,2,3,6-8) = 1 0 1 1 0 0 1 1 1
+ 9 :          (0,2,3,6-8) = 1 0 1 1 0 0 1 1 1
+10 :       (0,2,3,6-8,10) = 1 0 1 1 0 0 1 1 1 0 1
+11 :    (0,2,3,6-8,10,11) = 1 0 1 1 0 0 1 1 1 0 1 1
+12 :    (0,2,3,6-8,10-12) = 1 0 1 1 0 0 1 1 1 0 1 1 1
+13 :    (0,2,3,6-8,10-13) = 1 0 1 1 0 0 1 1 1 0 1 1 1 1
+14 :    (0,2,3,6-8,10-14) = 1 0 1 1 0 0 1 1 1 0 1 1 1 1 1
+15 :    (0,2,3,6-8,10-15) = 1 0 1 1 0 0 1 1 1 0 1 1 1 1 1 1
+0 out of 1000000 failed
diff --git a/tests/uniq_name/Makefile b/tests/uniq_name/Makefile
new file mode 100644
index 0000000..a6265fb
--- /dev/null
+++ b/tests/uniq_name/Makefile
@@ -0,0 +1,26 @@
+ROOT=../..
+UNM=$(ROOT)/src_plugins/io_kicad_legacy
+LHT=$(ROOT)/src_3rd/liblihata
+SRC=$(ROOT)/src
+
+CFLAGS = -Wall -I$(UNM) -I$(LHT) -I$(SRC) -I$(ROOT)
+LIB_OBJS=$(LHT)/genht/htsp.o $(LHT)/genht/hash.o $(SRC)/compat_misc.o
+
+all: tester
+
+test: tester.diff
+	@echo "*** All ok, QC passed ***"
+	@rm tester.out
+
+tester: tester.o $(UNM)/uniq_name.o $(LIB_OBJS)
+
+tester.o: tester.c $(UNM)/uniq_name.h
+
+$(UNM)/uniq_name.o: $(UNM)/uniq_name.c $(UNM)/uniq_name.h
+
+tester.diff: tester.ref tester.out
+	@diff -u tester.ref tester.out
+
+tester.out: tester
+	./tester > tester.out
+
diff --git a/tests/uniq_name/tester.c b/tests/uniq_name/tester.c
new file mode 100644
index 0000000..0a540c0
--- /dev/null
+++ b/tests/uniq_name/tester.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include "uniq_name.h"
+
+int main()
+{
+	unm_t group1;
+
+	/* Initialize the group with defaults */
+	unm_init(&group1);
+
+	printf("'%s'\n", unm_name(&group1, "foo", (void *)0x01));
+	printf("'%s'\n", unm_name(&group1, "bar", (void *)0x42));
+	printf("'%s'\n", unm_name(&group1, "bar", (void *)0x66));
+	printf("'%s'\n", unm_name(&group1, "baz", NULL));
+
+	/* test forced collision */
+	printf("'%s'\n", unm_name(&group1, "forced", NULL));
+	printf("'%s'\n", unm_name(&group1, "forced_dup1", NULL));
+	printf("'%s'\n", unm_name(&group1, "forced_dup2", NULL));
+	printf("'%s'\n", unm_name(&group1, "forced", NULL)); /* this would end up as forced_dup3 */
+	printf("'%s'\n", unm_name(&group1, "forced", NULL)); /* ... _dup4 */
+
+	/* test global dup ctr */
+	printf("'%s'\n", unm_name(&group1, "foo", NULL)); /* ... _dup5 */
+
+	/* unknown name gets named */
+	printf("'%s'\n", unm_name(&group1, NULL, NULL)); /* this is the first unnamed item, so it won't get a suffix */
+	printf("'%s'\n", unm_name(&group1, NULL, NULL)); /* gets suffixed because of the collision */
+
+	/* empty name is like unknown name too */
+	printf("'%s'\n", unm_name(&group1, "", NULL));
+
+
+	/* Optional: change configuration, even on the fly: */
+	group1.unnamed = "anonymous";
+	group1.suffix_sep = "::";
+
+	/* Note: it is safe to reset the counter - worst case the collision loop
+	   will run some more before it finds a free name, but there won't be
+	   a collision */
+	group1.ctr = 0;
+
+	/* Adding three more unnamed items will demonstrate how the new config settings take effect */
+	printf("'%s'\n", unm_name(&group1, NULL, NULL));
+	printf("'%s'\n", unm_name(&group1, NULL, NULL));
+	printf("'%s'\n", unm_name(&group1, NULL, NULL));
+
+	/* Optional: iterate over all items once more, e.g. to generate an index
+	   Warning: order is not guaranteed. */
+	{
+		htsp_entry_t *e;
+		for (e = htsp_first(&group1.seen); e; e = htsp_next(&group1.seen, e))
+			printf("name='%s' user_data='%p'\n", e->key, e->value);
+	}
+
+	/* Release all memory */
+	unm_uninit(&group1);
+}
diff --git a/tests/uniq_name/tester.ref b/tests/uniq_name/tester.ref
new file mode 100644
index 0000000..ea3cb54
--- /dev/null
+++ b/tests/uniq_name/tester.ref
@@ -0,0 +1,32 @@
+'foo'
+'bar'
+'bar_dup0'
+'baz'
+'forced'
+'forced_dup1'
+'forced_dup2'
+'forced_dup3'
+'forced_dup4'
+'foo_dup5'
+'unnamed'
+'unnamed_dup6'
+'unnamed_dup7'
+'anonymous'
+'anonymous::0'
+'anonymous::1'
+name='forced_dup1' user_data='(nil)'
+name='forced_dup2' user_data='(nil)'
+name='forced_dup3' user_data='(nil)'
+name='forced_dup4' user_data='(nil)'
+name='foo' user_data='0x1'
+name='baz' user_data='(nil)'
+name='bar_dup0' user_data='0x66'
+name='unnamed_dup6' user_data='(nil)'
+name='forced' user_data='(nil)'
+name='unnamed_dup7' user_data='(nil)'
+name='unnamed' user_data='(nil)'
+name='anonymous' user_data='(nil)'
+name='anonymous::0' user_data='(nil)'
+name='anonymous::1' user_data='(nil)'
+name='bar' user_data='0x42'
+name='foo_dup5' user_data='(nil)'
diff --git a/util/Makefile b/util/Makefile
new file mode 100644
index 0000000..772f210
--- /dev/null
+++ b/util/Makefile
@@ -0,0 +1,30 @@
+# plain old hand crafted Makefile
+
+SCMDIR=$(DATADIR)/../gEDA/scheme
+
+all:
+	cd gsch2pcb-rnd && make all
+
+include ../Makefile.conf
+
+clean:
+	cd gsch2pcb-rnd && make clean
+
+install_:
+	$(MKDIR) "$(BINDIR)" "$(LIBDIR)" "$(SCMDIR)"
+	$(CPC) "`pwd`/fp2anim" "$(BINDIR)/fp2anim"
+	$(CPC) "`pwd`/pcb-strip" "$(BINDIR)/pcb-strip"
+	$(CPC) "`pwd`/gnet-pcbrndfwd.scm" "$(SCMDIR)/gnet-pcbrndfwd.scm"
+	cd gsch2pcb-rnd && make install_ CPC="$(CPC)"
+
+install:
+	make install_ CPC="$(CP)"
+
+linstall:
+	make install_ CPC="$(LN)"
+
+uninstall:
+	$(RM) "$(BINDIR)/fp2anim"
+	$(RM) "$(BINDIR)/pcb-strip"
+	$(RM) "$(SCMDIR)/gnet-pcbrndfwd.scm"
+	cd gsch2pcb-rnd && make uninstall
diff --git a/util/README b/util/README
new file mode 100644
index 0000000..f7b2c82
--- /dev/null
+++ b/util/README
@@ -0,0 +1,3 @@
+Programs and scripts that are not integral part of the pcb executable.
+
+These are typically used as standalone utilities along with pcb-rnd.
diff --git a/util/cgi_common.sh b/util/cgi_common.sh
new file mode 100644
index 0000000..57ce469
--- /dev/null
+++ b/util/cgi_common.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+# shell lib
+
+url_decode()
+{
+awk '
+	function cd(n)
+	{
+		chr=sprintf("%c", n);
+		if (chr == "&")
+			chr = "\\&"
+		code="%" sprintf("%02x", n);
+		CODE[tolower(code)] = chr;
+		CODE[toupper(code)] = chr;
+	}
+
+	BEGIN {
+		for(n = 1; n < 256; n++)
+			cd(n);
+	}
+
+	{
+		tmp = $0;
+		gsub("[+]", " ", tmp);
+		for(c in CODE) {
+			gsub(c, CODE[c], tmp)
+		}
+		print tmp
+	}
+'
+}
+
+error()
+{
+	echo "Content-type: text/plain"
+	echo ""
+	echo "Error: $*"
+	exit 0
+}
+
+radio()
+{
+	local chk
+	if test "$3" = "$2"
+	then
+		chk=" checked=\"true\""
+	fi
+	echo "<input type=\"radio\" name=\"$1\" value=\"$2\"$chk>"
+}
+
+checked()
+{
+	if test ! -z "$1"
+	then
+		echo " checked=\"true\""
+	fi
+}
+
+fix_ltgt()
+{
+	sed "s/</\</g;s/>/\>/g"
+}
+
+cgi_png()
+{
+	echo "Content-type: image/png"
+	echo ""
+	cparm=""
+	if test ! -z "$QS_mm"
+	then
+		cparm="$cparm --mm"
+	fi
+	if test ! -z "$QS_grid"
+	then
+		cparm="$cparm --grid-unit $QS_grid"
+	fi
+	if test ! -z "$QS_annotation"
+	then
+		annot=$QS_annotation
+	fi
+	if test ! -z "$QS_diamond"
+	then
+		cparm="$cparm --diamond"
+	fi
+	if test ! -z "$QS_photo"
+	then
+		cparm="$cparm --photo"
+	fi
+	if test ! -z "$QS_dimvalue"
+	then
+		annot="$annot:dimvalue"
+	fi
+	if test ! -z "$QS_dimname"
+	then
+		annot="$annot:dimname"
+	fi
+	if test ! -z "$QS_pins"
+	then
+		annot="$annot:pins"
+	fi
+	if test ! -z "$QS_background"
+	then
+		annot="$annot:background"
+	fi
+	case "$QS_thumb"
+	in
+		1) animarg="-x 64 -y 48" ;;
+		2) animarg="-x 128 -y 96" ;;
+		3) animarg="-x 192 -y 144" ;;
+		*) animarg="" ;;
+	esac
+	if test ! -z  "$annot"
+	then
+			cparm="$cparm --annotation $annot"
+	fi
+	(echo "$fptext" | $fp2anim $cparm; echo 'screenshot "/dev/stdout"') | $animator -H $animarg
+}
diff --git a/util/devhelpers/chgstat.sh b/util/devhelpers/chgstat.sh
new file mode 100755
index 0000000..7576683
--- /dev/null
+++ b/util/devhelpers/chgstat.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#   chgstat - svn statistics on lines changed since the fork
+#   Copyright (C) 2016 Tibor 'Igor2' Palinkas
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program; if not, write to the Free Software Foundation, Inc.,
+#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#   http://repo.hu/projects/pcb-rnd
+
+trunk=../..
+dirs="$trunk/src $trunk/src_plugins"
+import=2
+exclude=""
+
+echo "Updating blame files..."
+for d in $dirs
+do
+	for f in `find $d -name '*.[chly]'`
+	do
+		case "$f" in
+			*parse_y.c|*parse_y.h|*parse_l.c|*parse_l.h|*_sphash*) ;;
+			*)
+				src_date=`stat -c %Y $f`
+				if test -f $f.blm
+				then
+					blm_date=`stat -c %Y $f.blm`
+				else
+					blm_date=0
+				fi
+				if test $src_date -gt $blm_date
+				then
+					echo "blame: $f"
+					svn blame $f > $f.blm
+				fi
+				;;
+		esac
+	done
+done
+
+echo "Calculating stats..."
+for d in $dirs
+do
+	cat `find $d -name '*.blm'` 
+done| awk -v import=$import '
+		{
+			rev=int($1)
+			if (((rev >= 3871) && (rev <= 3914)) || ((rev >= 4065) && (rev <= 4068)) || (rev == 4023) || (rev == 4033)) {
+# old plugins and export plugin import
+				old++
+			}
+			else if ((rev <= import) || (rev == 1022) || (rev == 3539))
+				old++
+			else
+				new++
+		}
+		END {
+			print "old: " old
+			print "new: " new
+			print new/(old+new) * 100
+		}
+	'
diff --git a/util/devhelpers/chlog.sh b/util/devhelpers/chlog.sh
new file mode 100755
index 0000000..02a2c33
--- /dev/null
+++ b/util/devhelpers/chlog.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+svn log -r$1:HEAD | grep -v "^\(-*$\|r[0-9]\+\)"
diff --git a/util/devhelpers/copyright_miss.sh b/util/devhelpers/copyright_miss.sh
new file mode 100755
index 0000000..577a03f
--- /dev/null
+++ b/util/devhelpers/copyright_miss.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# This file is placed in the Public Domain.
+
+# Print the path of source files that may miss the copyright banner among
+# with their length in line
+
+find . -name '*.[chly]' -exec sh -c 'grep "^ [*]  Copyright [(]C[)]" {} >/dev/null || wc -l "{}"' \;
diff --git a/util/devhelpers/deblist.sh b/util/devhelpers/deblist.sh
new file mode 100755
index 0000000..859f0f7
--- /dev/null
+++ b/util/devhelpers/deblist.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# Process the debian/control file and produce a html table that explains
+# the purpose of each package
+
+echo '
+<html>
+<!-- THIS FILE IS GENERATED BY deblist.sh, DO NOT EDIT -->
+<head>
+	<title> pcb-rnd - Debian package list </title>
+<!--AUTO head begin-->
+
+<!--AUTO head end-->
+</head>
+<body>
+
+<!--AUTO navbar begin-->
+
+<!--AUTO navbar end-->
+
+<h1> pcb-rnd - Debian package list </h1>
+<table border=1 cellspacing=0>
+<tr><th>name <th> internal dependencies <th> description
+'
+
+awk '
+function out()
+{
+	if (pkg == "")
+		return
+	print "<tr><td><b>" pkg "</b><td>" deps "<td>" desc
+	pkg=""
+	deps=""
+	desc=""
+	para=0
+}
+
+/^Package:/ {
+	out();
+	pkg=$2
+	para=0
+}
+#Depends: ${misc:Depends}, ${shlibs:Depends}, pcb-rnd-core, pcb-rnd-gtk
+/^Depends:/ {
+	deps=$0
+	sub("Depends: *", "", deps)
+	gsub("[$][{][^}]*[}],?", "", deps)
+}
+
+/^ [.]/ {
+	if (para > 0)
+		desc = desc "<p>"
+	para++
+	next
+}
+
+(para > 0) {
+	desc = desc " " $0
+}
+
+END {
+	out()
+}
+'
+
+
+echo '
+</table>
+'
\ No newline at end of file
diff --git a/util/devhelpers/inc_weed_out.sh b/util/devhelpers/inc_weed_out.sh
new file mode 100755
index 0000000..3123730
--- /dev/null
+++ b/util/devhelpers/inc_weed_out.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#   inc_weed_out - list #includes that can be removed without introducing warnings
+#   Copyright (C) 2016 Tibor 'Igor2' Palinkas
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program; if not, write to the Free Software Foundation, Inc.,
+#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#   http://repo.hu/projects/pcb-rnd
+
+# include weed-out: attempt to compile an object with each #include lines
+# commented out one by one; if commenting an #include doesn't change compiler
+# warnings, that include is potentially unneeded.
+
+# Usage:
+#  1. compile the whole project so that all dependencies of the target file are compiled
+#  2. inc_weed_out.sh foo.c
+#  3. check the output, remove #includes
+#  4. compile by hand to check
+#
+#  NOTE: the script runs 'make foo.o' - your local Makefile needs to be able
+#        to compile foo.o
+#  NOTE: sometimes disabling multiple #includes have different effect than
+#        disabling them one by one - always check manually.
+#  NOTE: conditional code also requires manual examination - the script may
+#        find an #include can be removed with the current settings but the
+#        same include may be needed with different ./configure settings
+
+# set up file names
+fn_c=$1
+fn_o=${1%.c}.o
+fn_c_backup=$fn_c.inc_weed_out
+fn_refo=$1.refo
+fn_tmpo=$1.tmpo
+
+# comment one #include (index is $1, save output in $2, print the #include line to stdout)
+weed_out()
+{
+	awk -v "target=$1" -v "outf=$2" '
+		/^#[ \t]*include/ {
+			count++
+			if (count == target) {
+				print "/*" $0 "*/" > outf
+				print $0
+				found=1
+				next
+			}
+		}
+		{ print $0 > outf }
+		END {
+			exit(found!=1)
+		}
+	'
+}
+
+# make a backup
+cp $fn_c $fn_c_backup
+
+# generate initial/reference warning text
+touch $fn_c
+make $fn_o >/dev/null 2>$fn_refo
+
+# loop over #include indices
+cnt=1
+while true
+do
+	# comment out the next #include or break if there's no more
+	name=`weed_out $cnt < $fn_c_backup $fn_c`
+	if test -z "$name"
+	then
+		break
+	fi
+
+	# test compile to see if we got new warnings compared to the reference
+	make $fn_o 2>$fn_tmpo >/dev/null
+	diff $fn_refo $fn_tmpo >/dev/null && echo REMOVE: $fn_c $cnt $name
+
+	# start over
+	cnt=$(($cnt+1))
+done
+
+# clean up
+mv $fn_c_backup $fn_c
+rm $fn_refo $fn_tmpo
diff --git a/util/devhelpers/pcb-rtrip b/util/devhelpers/pcb-rtrip
new file mode 100755
index 0000000..9b18de3
--- /dev/null
+++ b/util/devhelpers/pcb-rtrip
@@ -0,0 +1,119 @@
+#!/bin/sh
+#   pcb-rtrip - load-save-load round trip tester
+#   Copyright (C) 2016 Tibor 'Igor2' Palinkas
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program; if not, write to the Free Software Foundation, Inc.,
+#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#   http://repo.hu/projects/pcb-rnd
+
+pcb_rtrip()
+{
+	rm -f "$fn.new.pcb"
+
+	if test ! -z "$remtgt"
+	then
+		rm -f "$fn.trgt.$ext"
+	fi
+
+echo '
+SaveTo(LayoutAs, '$fn'.orig.pcb, pcb)
+LoadFrom(Layout, '$fn'.orig.pcb)
+SaveTo(LayoutAs, '$fn'.trgt.'$ext', '$fmt')
+LoadFrom(Layout, '$fn'.trgt.'$ext', '$fmt')
+SaveTo(LayoutAs, '$fn'.new.pcb, pcb)
+' | $valg $bin --gui batch "$fn"
+
+	if test -f "$fn.orig.pcb" -a -f "$fn.new.pcb"
+	then
+		diff -u "$fn.orig.pcb" "$fn.new.pcb"
+	else
+		if test ! -f "$fn.orig.pcb"
+		then
+			echo "Failed to load $fn"
+		else
+			echo "Failed to load $fn.trgt.$ext"
+		fi
+	fi
+}
+
+set_ext()
+{
+	case "$fmt"
+	in
+		lihata) ext="lht";;
+		pcb) ext="pcb";;
+		*) ext="$fmt";;
+	esac
+}
+
+help()
+{
+	echo "pcb-rtrip - load-save-load round trip tester"
+	echo ""
+	echo "Load a board, save it (*.orig.pcb), load it, save it in the target"
+	echo "format (*.trgt.*), load it again and save it (*.new.pcb). Calculate"
+	echo "the diff between *.orig.pcb and *.new.pcb; if load-save-load round trip"
+	echo "of a target format is lossles, the diff is empty."
+	echo ""
+	echo "Invocation: $0 [-f fmt] [input.pcb]"
+	echo "Switches:"
+	echo " -f fmt   format (e.g. lihata)"
+	echo " -r       remove target before overwrite"
+	echo " -b bin   run bin instead of \"pcb-rnd\""
+	echo " -V       wrap the binary in valgrind"
+	echo ""
+}
+
+
+# defaults
+fmt="lihata"
+fn=""
+remtgt=""
+bin="pcb-rnd"
+valg=""
+
+while test $# -gt 0
+do
+	case "$1"
+	in
+		--help) help "$0"; exit 0;;
+		-f) fmt=$2; shift 1;;
+		-b) bin=$2; shift 1;;
+		-V) valg="valgrind -v";;
+		-r) remtgt=1;;
+		*)
+			if test -z "$fn"
+			then
+				fn="$1"
+			else
+				echo "unknown switch $1; try --help" >&2
+				exit 1
+			fi
+			;;
+		-*) echo "unknown switch $1; try --help" >&2; exit 1;;
+	esac
+	shift 1
+done
+
+if test -z "$fn"
+then
+	echo "no fn"
+	exit 1
+	fn="pcb-rtrip.pcb"
+	cat >$fn
+fi
+
+set_ext
+pcb_rtrip
diff --git a/util/devhelpers/subst/README b/util/devhelpers/subst/README
new file mode 100644
index 0000000..46f158d
--- /dev/null
+++ b/util/devhelpers/subst/README
@@ -0,0 +1,3 @@
+Scripts to convert mainline code to pcb-rnd after the namespace cleanups in
+trunk/
+
diff --git a/util/devhelpers/subst/subst_const.sh b/util/devhelpers/subst/subst_const.sh
new file mode 100755
index 0000000..15c89c1
--- /dev/null
+++ b/util/devhelpers/subst/subst_const.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+for n in `find . -name "*.[chly]"`
+do
+sed '
+{
+
+s/COORD_TO_MIL/PCB_COORD_TO_MIL/g
+s/MIL_TO_COORD/PCB_MIL_TO_COORD/g
+s/COORD_TO_MM/PCB_COORD_TO_MM/g
+s/MM_TO_COORD/PCB_MM_TO_COORD/g
+s/COORD_TO_INCH/PCB_COORD_TO_INCH/g
+s/INCH_TO_COORD/PCB_INCH_TO_COORD/g
+
+s/RAD_TO_DEG/PCB_RAD_TO_DEG/g
+s/TAN_22_5_DEGREE_2/PCB_TAN_22_5_DEGREE_2/g
+s/COS_22_5_DEGREE/PCB_COS_22_5_DEGREE/g
+s/TAN_30_DEGREE/PCB_TAN_30_DEGREE/g
+s/TAN_60_DEGREE/PCB_TAN_60_DEGREE/g
+s/LN_2_OVER_2/PCB_LN_2_OVER_2/g
+s/UNSCALE_TEXT/PCB_UNSCALE_TEXT/g
+s/SCALE_TEXT/PCB_SCALE_TEXT/g
+s/M180/PCB_M180/g
+}
+' -i $n
+done
diff --git a/util/devhelpers/subst/subst_flags.sh b/util/devhelpers/subst/subst_flags.sh
new file mode 100755
index 0000000..fb29600
--- /dev/null
+++ b/util/devhelpers/subst/subst_flags.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+for n in `find . -name "*.[chly]"`
+do
+sed '
+{
+s/NOPASTEFLAG/PCB_FLAG_NOPASTE/g
+s/PININPOLYFLAG/PCB_FLAG_PININPOLY/g
+s/CLEARPOLYFLAG/PCB_FLAG_CLEARPOLY/g
+s/HIDENAMEFLAG/PCB_FLAG_HIDENAME/g
+s/DISPLAYNAMEFLAG/PCB_FLAG_DISPLAYNAME/g
+s/CLEARLINEFLAG/PCB_FLAG_CLEARLINE/g
+s/FULLPOLYFLAG/PCB_FLAG_FULLPOLY/g
+s/SELECTEDFLAG/PCB_FLAG_SELECTED/g
+s/ONSOLDERFLAG/PCB_FLAG_ONSOLDER/g
+s/SQUAREFLAG/PCB_FLAG_SQUARE/g
+s/RUBBERENDFLAG/PCB_FLAG_RUBBEREND/g
+s/USETHERMALFLAG/PCB_FLAG_USETHERMAL/g
+s/ONSILKFLAG/PCB_FLAG_ONSILK/g
+s/OCTAGONFLAG/PCB_FLAG_OCTAGON/g
+s/LOCKFLAG/PCB_FLAG_LOCK/g
+s/EDGE2FLAG/PCB_FLAG_EDGE2/g
+s/VISITFLAG/PCB_FLAG_VISIT/g
+s/NONETLISTFLAG/PCB_FLAG_NONETLIST/g
+s/MINCUTFLAG/PCB_FLAG_MINCUT/g
+s/ONPOINTFLAG/PCB_FLAG_ONPOINT/g
+s/NOCOPY_FLAGS/PCB_FLAG_NOCOPY/g
+s/FOUNDFLAG/PCB_FLAG_FOUND/g
+s/HOLEFLAG/PCB_FLAG_HOLE/g
+s/AUTOFLAG/PCB_FLAG_AUTO/g
+s/WARNFLAG/PCB_FLAG_WARN/g
+s/RATFLAG/PCB_FLAG_RAT/g
+s/PINFLAG/PCB_FLAG_PIN/g
+s/VIAFLAG/PCB_FLAG_VIA/g
+s/DRCFLAG/PCB_FLAG_DRC/g
+s/NOFLAG/PCB_FLAG_NO/g
+}
+' -i $n
+done
diff --git a/util/devhelpers/subst/subst_mode.sh b/util/devhelpers/subst/subst_mode.sh
new file mode 100755
index 0000000..b149aad
--- /dev/null
+++ b/util/devhelpers/subst/subst_mode.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+for n in `find . -name "*.[chly]"`
+do
+sed '
+{
+	s/RECTANGLE_MODE/PCB_MODE_RECTANGLE/g
+	s/POLYGON_MODE/PCB_MODE_POLYGON/g
+	s/PASTEBUFFER_MODE/PCB_MODE_PASTE_BUFFER/g
+	s/ROTATE_MODE/PCB_MODE_ROTATE/g
+	s/REMOVE_MODE/PCB_MODE_REMOVE/g
+	s/INSERTPOINT_MODE/PCB_MODE_INSERT_POINT/g
+	s/RUBBERBANDMOVE_MODE/PCB_MODE_RUBBERBAND_MOVE/g
+	s/THERMAL_MODE/PCB_MODE_THERMAL/g
+	s/ARROW_MODE/PCB_MODE_ARROW/g
+	s/LOCK_MODE/PCB_MODE_LOCK/g
+	s/POLYGONHOLE_MODE/PCB_MODE_POLYGON_HOLE/g
+	s/ARC_MODE/PCB_MODE_ARC/g
+	s/PAN_MODE/PCB_MODE_PAN/g
+	s/MOVE_MODE/PCB_MODE_MOVE/g
+	s/COPY_MODE/PCB_MODE_COPY/g
+	s/TEXT_MODE/PCB_MODE_TEXT/g
+	s/NO_MODE/PCB_MODE_NO/g
+	s/VIA_MODE/PCB_MODE_VIA/g
+	s/LINE_MODE/PCB_MODE_LINE/g
+}
+' -i $n
+done
diff --git a/util/devhelpers/subst/subst_objtype.sh b/util/devhelpers/subst/subst_objtype.sh
new file mode 100755
index 0000000..1f8f489
--- /dev/null
+++ b/util/devhelpers/subst/subst_objtype.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+for n in `find . -name *.[chly]`
+do
+sed '
+{
+s/PIN_TYPES/PCB_TYPEMASK_PIN/g;
+s/LOCK_TYPES/PCB_TYPEMASK_LOCK/g;
+s/ALL_TYPES/PCB_TYPEMASK_ALL/g;
+
+s/RATLINE_TYPE/PCB_TYPE_RATLINE/g;
+s/ELEMENTNAME_TYPE/PCB_TYPE_ELEMENT_NAME/g;
+s/POLYGONPOINT_TYPE/PCB_TYPE_POLYGON_POINT/g;
+s/LINEPOINT_TYPE/PCB_TYPE_LINE_POINT/g;
+s/ELEMENTLINE_TYPE/PCB_TYPE_ELEMENT_LINE/g;
+s/ELEMENTARC_TYPE/PCB_TYPE_ELEMENT_ARC/g;
+
+s/NO_TYPE/PCB_TYPE_NONE/g;
+s/VIA_TYPE/PCB_TYPE_VIA/g;
+s/ELEMENT_TYPE/PCB_TYPE_ELEMENT/g;
+s/LINE_TYPE/PCB_TYPE_LINE/g;
+s/POLYGON_TYPE/PCB_TYPE_POLYGON/g;
+s/TEXT_TYPE/PCB_TYPE_TEXT/g;
+s/PIN_TYPE/PCB_TYPE_PIN/g;
+s/PAD_TYPE/PCB_TYPE_PAD/g;
+s/ARC_TYPE/PCB_TYPE_ARC/g;
+s/LOCKED_TYPE/PCB_TYPE_LOCKED/g;
+s/NET_TYPE/PCB_TYPE_NET/g;
+}
+' -i $n
+done
diff --git a/util/fp2anim b/util/fp2anim
new file mode 100755
index 0000000..d0f2f0a
--- /dev/null
+++ b/util/fp2anim
@@ -0,0 +1,703 @@
+#!/bin/sh
+
+photo=0
+grid_unit="mil"
+
+annotation=pins
+
+while test $# -gt 0
+do
+	case $1 in
+		--photo|p) photo=1;;
+		--grid-unit|g) grid_unit=$2; shift 1;;
+		--mm) grid_unit="mm";;
+		--diamond) diamond=1;;
+		--annotation) annotation=$2; shift 1;;
+	esac
+	shift 1
+done
+
+
+
+awk -v "photo=$photo" -v "grid_unit=$grid_unit" -v "diamond=$diamond" -v "annotation=$annotation" '
+
+BEGIN {
+	diamond_x = 0
+	diamond_y = 0
+
+	dim_arrow_len = 2000
+
+	q="\""
+
+	ds = 100*20
+	offs_3d = 150
+
+	if (photo) {
+		clr_board[-1] = "#003300"
+		clr_board[0]  = "#006600"
+		clr_board[1]  = "#22AA22"
+
+		clr_plated[-1] = "#555555"
+		clr_plated[0]  = "#777777"
+		clr_plated[1]  = "#999999"
+
+		clr_silk[-1] = "#AAAAAA"
+		clr_silk[0] = "#CCCCCC"
+		clr_silk[1] = "#FFFFFF"
+
+		clr_solderstop[0] = "#00BB00"
+
+
+		clr_hole[0] = "black"
+		clr_grid[0] = "blue"
+	}
+	else {
+		clr_board[0] = "darkgreen"
+		clr_plated[0] = "#777777"
+		clr_hole[0] = "white"
+		clr_silk[0] = "black"
+		clr_solderstop[0] = "white"
+		clr_grid[0] = "blue"
+	}
+
+	clr_annotation[0] = "#ff0000"
+	clr_annotationbg[0] = "#ffffff"
+
+	marg=100000
+
+	if (annotation != "none") {
+		enable_annot = 1
+		if (annotation ~ "pins")
+			enable_pinnum = 1
+		if (annotation ~ "dimname")
+			enable_dim_name = 1
+		if (annotation ~ "dimvalue")
+			enable_dim_value = 1
+		if (annotation ~ "background")
+			enable_annotbg = 1
+	}
+
+	if (grid_unit == "mil") {
+		gstep = 10000
+		gmul = 1/100
+	}
+	else {
+		gstep = 3937
+		gmul = 1/3937
+	}
+
+	vf_data = vf_load("/usr/share/animator/romansimplexmono")
+}
+
+### vector font ###
+function vf_load(fn,       s)
+{
+	while((getline < fn) == 1) {
+		if ($1 == "!")
+			glyphw[$2] = $3
+		else if ($1 == "!font")
+			for(i=32; i<=127; i++)
+				font[$2 sprintf("%c", i)] = $(i-29)
+		else
+			s = s "\n" $0
+	}
+	close(fn)
+	return s
+}
+
+function vf_textwidth(fnt, text,                           x,k) {
+	x=0
+	for(k=1; k<=length(text); k+=1)
+		x += glyphw[font[fnt substr(text,k,1)]]
+	return x
+}
+
+function vf_show(fnt, text,                                  k,s) {
+	for(k=1; k<=length(text); k+=1)
+		s = s "\ninvoke hf" font[fnt substr(text,k,1)]
+	return s
+}
+
+###
+
+function directive(term     ,tmp)
+{
+	while(!($0 ~ term "[ \t]*$")) {
+		if ((getline tmp) < 1) {
+			print "Premature end of file looking for " term " of " $1 > "/dev/stderr"
+			exit 1
+		}
+		$0 = $0 " " tmp
+	}
+#	print "directive: |" term "|" $0 "|" > "/dev/stderr"
+}
+
+function edges(x, y)
+{
+	if ((xmin == "") || (x < xmin))
+		xmin = x
+	if ((ymin == "") || (y < ymin))
+		ymin = y
+	if ((xmax == "") || (x > xmax))
+		xmax = x
+	if ((ymax == "") || (y > ymax))
+		ymax = y
+}
+
+# draw a line with rounded ends
+# do not draw end circles if omit_ends is non-zero
+# extend both ends by length_bloat
+function rline(x1, y1, x2, y2, width    ,omit_ends , length_bloat    ,nx,ny,vx,vy, len)
+{
+
+	nx = y2-y1
+	ny = -(x2-x1)
+
+	len=sqrt(nx*nx+ny*ny)
+	if (len != 0) {
+		nx /= len
+		ny /= len
+		vx = -ny
+		vy = nx
+
+		if (length_bloat != 0) {
+			x1 -= length_bloat * vx
+			x2 += length_bloat * vx
+			y1 -= length_bloat * vy
+			y2 += length_bloat * vy
+		}
+
+# if there are no end circles, we are square and should extend by extra width/2
+		if (omit_ends) {
+			x1 -= (width/2) * vx
+			x2 += (width/2) * vx
+			y1 -= (width/2) * vy
+			y2 += (width/2) * vy
+		}
+
+		print "poly", x1+nx*width/2, y1+ny*width/2, x2+nx*width/2, y2+ny*width/2, x2-nx*width/2, y2-ny*width/2, x1-nx*width/2, y1-ny*width/2
+		edges(x1+nx*width/2, y1+ny*width/2)
+		edges(x2+nx*width/2, y2+ny*width/2)
+		edges(x2-nx*width/2, y2-ny*width/2)
+		edges(x1-nx*width/2, y1-ny*width/2)
+	}
+
+	if (!omit_ends) {
+		print "fillcircle", x1, y1, width/2, width/10
+		if ((x1 != x2) || (y1 != y2))
+			print "fillcircle", x2, y2, width/2, width/10
+
+		edges(x1-width/2, y1-width/2)
+		edges(x2+width/2, y2+width/2)
+	}
+
+	edges(x1, y1)
+	edges(x2, y2)
+}
+
+function deg2rad(d)
+{
+	return d/180*3.141592654
+}
+
+function rarc(cx, cy, rx, ry, a_start, a_delta, width   ,a1, a2)
+{
+	if ((rx < 0) || (ry < 0))
+		return
+
+	if (a_delta < 0) {
+		a_start = 180-a_start
+		a_delta = -a_delta
+	}
+
+	step = a_delta * 100 * 2 / (((rx > ry) ? rx : ry) + 1)
+	if (step > 45)
+		step = 45
+
+	for(a1 = a_start; a1 < a_start + a_delta - step; a1 += step) {
+		a2 = a1+step
+		rline( cx - rx * cos(deg2rad(a1)), cy + ry * sin(deg2rad(a1)),
+		       cx - rx * cos(deg2rad(a2)), cy + ry * sin(deg2rad(a2)),
+		       width, 0)
+	}
+
+	a1 = a2;
+	a2 = a_start+a_delta
+	rline( cx - rx * cos(deg2rad(a1)), cy + ry * sin(deg2rad(a1)),
+	       cx - rx * cos(deg2rad(a2)), cy + ry * sin(deg2rad(a2)),
+	       width, 0)
+
+}
+
+function rsquare(x1, y1, x2, y2, r   ,tmp)
+{
+	if (x1 < x2) {
+		tmp=x1
+		x1 = x2
+		x2 = tmp
+	}
+	if (y1 < y2) {
+		tmp=y1
+		y1 = y2
+		y2 = tmp
+	}
+
+	print "poly", x1, y1-r,x1-r, y1,  x2+r,y1, x2, y1-r,    x2, y2+r,   x2+r,y2,     x1-r, y2,  x1, y2+r
+	print "fillcircle", x1-r, y1-r, r, r/10
+	print "fillcircle", x2+r, y2+r, r, r/10
+	print "fillcircle", x2+r, y1-r, r, r/10
+	print "fillcircle", x1-r, y2+r, r, r/10
+}
+
+function flash(x, y, dia, square, roundr)
+{
+	if (square) {
+		if (roundr != "")
+			rsquare(x-dia/2, y-dia/2, x+dia/2, y+dia/2, roundr)
+		else
+			print "fillrect", x-dia/2, y-dia/2, dia, dia
+	}
+	else
+		print "fillcircle", x, y, dia/2, dia/10
+}
+
+function hole(x, y, ring, clr, mask, drill, name, square)
+{
+	if (photo) {
+		print "macro push mask"
+		flash(x, y, (ring+clr), square, (ring+clr)/10)
+		print "endmacro"
+	}
+
+	print "macro push copper"
+	flash(x, y, ring, square)
+	print "endmacro"
+
+	has_hole=1
+	print "macro push hole"
+	flash(x, y, drill, 0)
+	print "endmacro"
+
+	if (enable_pinnum) {
+		gsub("[\"]", "", name)
+		annot(x, y, name, 0, 0, 1)
+	}
+
+	edges(x-(ring+clr)/2, y-(ring+clr)/2)
+	edges(x+(ring+clr)/2, y+(ring+clr)/2)
+}
+
+function pad(x1, y1, x2, y2, thickness, clr, mask, name, square)
+{
+	if (photo) {
+		print "macro push mask"
+		rline(x1, y1, x2, y2, thickness+clr, square)
+		print "endmacro"
+	}
+
+
+	print "macro push copper"
+	rline(x1, y1, x2, y2, thickness, square)
+	print "endmacro"
+
+	if (enable_pinnum) {
+		gsub("[\"]", "", name)
+		annot((x2+x1)/2, (y2+y1)/2, name, 0, 0, 1)
+	}
+}
+
+function vf_text(x, y, txt, rot, elev, vcenter   ,TXT,v,n,W,maxw,s,sep,bg,maxh)
+{
+	gsub("[|]", "", txt)
+
+	v = split(txt, TXT, "[|]")
+	sep = " "
+
+	s = s "\n" "push"
+	s = s "\n" "shift "  x sep y
+	if (rot)
+		s = s "\n" "shift " (-1*elev) sep 0
+	else
+		s = s "\n" "shift " 0 sep (-1*elev)
+
+	s = s "\n" "scale %textscale% %textscale%"
+	s = s "\n" "scale 1 -1"
+
+	for(n = 1; n <= v; n++) {
+		W[n] = vf_textwidth("roman-simplex-mono", TXT[n])
+		if (W[n] > maxw)
+			maxw = W[n]
+		maxh++
+	}
+
+	if (vcenter)
+		s = s "\n" "shift 0 " (-maxh/2)
+
+	if (rot)
+		s = s "\n" "rotate 90"
+
+	for(n = v; n > 0; n--) {
+		s = s "\n" "!TXT " TXT[n]
+		s = s "\n" "push"
+
+		s = s "\n" "shift " ((maxw - W[n])/(2)) - maxw/2 sep 0
+
+		if (enable_annotbg) {
+			s = s "\n" "color " clr_annotationbg[0]
+			s = s "\n" "alpha 0.2"
+			s = s "\n" "fillrect " W[n]*-0.1 sep -0.1 "-" sep W[n]*1.1 sep 1.1
+			s = s "\n" "alpha 0.1"
+			s = s "\n" "fillrect " W[n]*-0.17 sep -0.17 "-" sep W[n]*1.17 sep 1.17
+			s = s "\n" "alpha 1"
+			s = s "\n" "color " clr_annotation[0]
+		}
+
+#		s = s "\n" "thick %textthick%"
+		s = s "\n" vf_show("roman-simplex-mono", TXT[n])
+#		s = s "\n" "thick 1"
+		s = s "\n" "pop"
+		s = s "\n" "shift 0 1.15"
+	}
+	s = s "\n" "pop"
+	return s
+}
+
+
+
+function annot(x, y, txt, rot, elev, hcenter)
+{
+	annot_pend = annot_pend "\n" vf_text(x, y, txt, rot, elev, hcenter)
+}
+
+
+function dimension(x1, y1, x2, y2, dist, name, value     ,vx,vy,nx,ny,X1,Y1,X2,Y2,len,alen,awidth,tmp, ang,negdist,cx,cy,dx,dy,D)
+{
+	alen = dim_arrow_len
+
+	vx = x2-x1
+	vy = y2-y1
+	len = vx*vx+vy*vy
+	if (len == 0)
+		return
+	len = sqrt(len)
+	vx = vx/len
+	vy = vy/len
+	nx = vy
+	ny = -vx
+
+	if (dist ~ "^[@]") {
+		cx = (x1+x2)/2
+		cy = (y1+y2)/2
+		sub("@", "", dist)
+		split(dist, D, ";")
+		nx = D[1] - cx
+		ny = D[2] - cy
+
+		X1 = x1+nx
+		Y1 = y1+ny
+		X2 = x2+nx
+		Y2 = y2+ny
+
+		dist = nx*nx+ny*ny
+		if (dist > 0)
+			dist = sqrt(dist)
+
+		nx /= dist
+		ny /= dist
+	}
+	else {
+		if (dist < 0) {
+			tmp = x1
+			x1 = x2
+			x2 = tmp
+			tmp = y1
+			y1 = y2
+			y2 = tmp
+			dist = -dist
+
+			vx = -vx
+			vy = -vy
+			nx = -nx
+			ny = -ny
+		}
+
+		X1 = x1+nx*dist
+		Y1 = y1+ny*dist
+		X2 = x2+nx*dist
+		Y2 = y2+ny*dist
+	}
+
+
+	if (alen > len/4)
+		alen=len/4
+
+	awidth=alen/6
+
+	ang = atan2(vx, vy)
+
+	print "macro push annotation"
+	print "line", x1, y1, X1+nx*awidth, Y1+ny*awidth
+	print "line", x2, y2, X2+nx*awidth, Y2+ny*awidth
+	print "line", X1, Y1, X2, Y2
+
+	print "poly", X1, Y1,  X1+vx*alen+nx*awidth, Y1+vy*alen+ny*awidth ,  X1+vx*alen-nx*awidth, Y1+vy*alen-ny*awidth
+	print "poly", X2, Y2,  X2-vx*alen+nx*awidth, Y2-vy*alen+ny*awidth ,  X2-vx*alen-nx*awidth, Y2-vy*alen-ny*awidth
+	print "endmacro"
+
+	if (enable_dim_value) {
+		if (value == "")
+			value = len
+		else if (value == "!")
+			value = ""
+		if (value != "")
+			value *= gmul
+	}
+	else
+		value = ""
+
+	if (!enable_dim_name)
+		name = ""
+
+	if ((name != "") && (value != ""))
+		name = name "=|" value
+	else if ((value != "") && (name == ""))
+		name = value
+
+	if (name != "") {
+		if (ang < 0)
+			ang = -ang
+		if ((ang < 3.1415*3/4) && (ang > 3.1415*1/4))
+			annot((X1+X2)/2, (Y1+Y2)/2, name, 0, awidth*1.1,0)
+		else
+			annot((X1+X2)/2, (Y1+Y2)/2, name, 1, awidth*1.1,0)
+	}
+
+	edges(X1+nx*awidth, Y1+ny*awidth)
+	edges(X2+nx*awidth, Y2+ny*awidth)
+}
+
+/^[ \t]*#dimension/ {
+	if ((enable_dim_name) || (enable_dim_value))
+		dimension($2, $3, $4, $5, $6, $7, $8)
+}
+
+/^[ \t]*#/ { next }
+
+/ElementLine[ \t]*[[]/ {
+	directive("]")
+	sub(".*ElementLine[ \t]*[[]", "", $0)
+	sub("]$", "", $0)
+
+	print "macro push silk"
+	rline($1, $2, $3, $4, $5)
+	print "endmacro"
+
+	next
+}
+
+/ElementLine[ \t]*[(]/ {
+	directive(")")
+	sub(".*ElementLine[ \t]*[(]", "", $0)
+	sub(")$", "", $0)
+
+	print "macro push silk"
+	rline($1*100, $2*100, $3*100, $4*100, $5*100)
+	print "endmacro"
+
+	next
+}
+
+
+
+/ElementArc[ \t]*[[]/ {
+	directive("]")
+	sub(".*ElementArc[ \t]*[[]", "", $0)
+	sub("]$", "", $0)
+
+	print "macro push silk"
+	rarc($1, $2,  $3, $4,  $5, $6,  $7)
+	print "endmacro"
+
+	next
+}
+
+#   ElementArc(0 0 59 59    45  90 10)
+/ElementArc[ \t]*[(]/ {
+	directive(")")
+	sub(".*ElementArc[ \t]*[(]", "", $0)
+	sub(")$", "", $0)
+
+	print "macro push silk"
+	rarc($1*100, $2*100,  $3*100, $4*100,  $5, $6,  $7*100)
+	print "endmacro"
+
+	next
+}
+
+
+#	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+/Pin[ \t]*[[]/ {
+	directive("]")
+	sub(".*Pin[ \t]*[[]", "", $0)
+	sub("]$", "", $0)
+	hole($1, $2, $3, $4, $5, $6, $8, ($9 ~ "square"))
+}
+
+#	Pin(300 800 90 60 "3" 0x01)
+/Pin[ \t]*[(]/ {
+	directive(")")
+	sub(".*Pin[ \t]*[(]", "", $0)
+	sub("]$", "", $0)
+	hole($1*100, $2*100, $3*100, $4*100, 3000, 2000, $5, ($6 ~ "0x10"))
+}
+
+#	Pad[ 0 0 0 0 0 5000 8600 "" "4" ""]
+/Pad[ \t]*[[]/ {
+	directive("]")
+	sub(".*Pad[ \t]*[[]", "", $0)
+	sub("]$", "", $0)
+
+	pad($1, $2, $3, $4, $5, $6, $7, $9,   ($10 ~ "square"))
+}
+
+/Pad[ \t]*[(]/ {
+	directive(")")
+	sub(".*Pad[ \t]*[(]", "", $0)
+	sub(")$", "", $0)
+
+	if (NF > 8)
+		pad($1*100, $2*100, $3*100, $4*100, $5*100, $6*100, $7*100, $9,   1)
+	else
+		pad($1*100, $2*100, $3*100, $4*100, $5*100, $6*100, $7*100, $6,   1)
+
+}
+
+/Mark[ \t]*[(]/ {
+	directive(")")
+	sub(".*Mark[ \t]*[(]", "", $0)
+	sub(")$", "", $0)
+
+	diamond_x = $1*100
+	diamond_y = $2*100
+}
+
+
+function layer_3d(name, color,  offs)
+{
+	if ((offs == "") || (offs == 1))
+		offs = offs_3d
+	else if (offs == -1)
+		offs = -offs_3d
+
+	if (1 in color) {
+		print "push"
+		print "shift", (-1*offs), (-1*offs)
+		print "color", color[1]
+		print "invoke ", name
+		print "pop"
+	}
+
+	if (-1 in color) {
+		print "push"
+		print "shift", (offs), (offs)
+		print "color", color[-1]
+		print "invoke ", name
+		print "pop"
+	}
+
+	if (0 in color) {
+		print "color", color[0]
+		print "invoke ", name
+	}
+}
+
+function draw_grid()
+{
+	while ((size/gstep) > 10)
+		gstep=gstep*2
+
+	for(x = 0 - int(marg/gstep)*gstep; x < size+marg; x+=gstep) {
+		print "line", x, ymin-marg, x, ymin+size+marg
+		print "line", xmin-marg, x, xmin+size+marg, x
+		print "text", x, ymax+gstep/2, q x*gmul "\\n" grid_unit q
+		print "text", xmax+gstep/2, x, q x*gmul "\\n" grid_unit q
+	}
+
+}
+
+END {
+	print "frame"
+	print vf_data
+
+# make sure all macros exist even if the footprint does not use them
+	print "macro push silk\nendmacro"
+	print "macro push copper\nendmacro"
+	print "macro push mask\nendmacro"
+	print "macro push annotation\nendmacro"
+
+	if (photo) {
+		xmin -= offs_3d*2
+		ymin -= offs_3d*2
+		xmax += offs_3d*2
+		ymax += offs_3d*2
+	}
+
+	sx = xmax-xmin
+	sy = ymax-ymin
+
+
+	size = sx > sy ? sx : sy
+
+	print "scale 1 -1"
+	print "viewport", xmin, ymin, "-", xmin+size, ymin+size
+	print "bg", clr_solderstop[0]
+	print "shift", (size-sx)/2, (size-sy)/2
+	print "scale 0.85"
+
+
+	print "dash 0xaaaa"
+	print "color", clr_grid[0]
+
+	if ((grid_unit != "") == (grid_unit != "none"))
+		draw_grid()
+
+	print "dash 0xffff"
+
+	if (diamond) {
+		print "macro push annotation"
+		print "lines", diamond_x-ds, diamond_y,   diamond_x,diamond_y+ds,   diamond_x+ds,diamond_y,   diamond_x,diamond_y-ds,   diamond_x-ds,diamond_y
+		print "endmacro"
+	}
+
+
+	if (photo) {
+		layer_3d("mask", clr_board, -1)
+	}
+
+
+	print "color", clr_plated[0]
+	print "invoke copper"
+	layer_3d("copper", clr_plated, 1)
+	if (has_hole) {
+		layer_3d("hole", clr_hole, -1)
+	}
+
+
+	layer_3d("silk", clr_silk)
+
+	if (enable_annot) {
+		gsub("%textscale%", size/40, annot_pend)
+#		gsub("%textthick%", size/15000, annot_pend)
+
+		print "macro push annotation"
+		print annot_pend
+		print "endmacro"
+
+		print "color", clr_annotation[0]
+		print "invoke annotation"
+	}
+
+	print "flush"
+}
+
+'
diff --git a/util/genref.sh b/util/genref.sh
new file mode 100755
index 0000000..4aca403
--- /dev/null
+++ b/util/genref.sh
@@ -0,0 +1,226 @@
+#!/bin/sh
+
+# generate reference html from gpmi headers
+# requires c99tree (probably svn HEAD version)
+
+prinit='
+#define GPMI_GPMI_PACKAGE_H
+#define multiple __attribute__((multiple))
+#define dynamic __attribute__((dynamic))
+#define direct __attribute__((direct))
+#define nowrap __attribute__((nowrap))
+
+#define gpmi_define_event(name) void GPMI_EVENT__ ## name
+'
+
+fn="$1"
+root="$2"
+refname="$3"
+shift 3
+
+pkgfn=`basename $fn`
+pkg=${pkgfn%%.h}
+echo "<html>
+<body>
+<h1>PCB GPMI</h1>
+<h2>Reference manual for package $pkg</h2>
+<small>Automatically generated from $pkgfn</small>
+"
+
+
+c99tree awk -I$root -I$root/src_3rd "$@" \
+  --gtx 'events=([] /+  d=[type == DECL]) && (d / i=[name ~ "GPMI_EVENT"]) && (i .+ [type == TYPE] . a=[type  == ARGLIST] ) && (d : [loc_is_local == "1"])' \
+  --gtx 'funcs=([] /+  d=[type == DECL]) && (d / i=[!name ~ "GPMI_EVENT"]) && (i .+ [type == TYPE] . [] >* a=[type  == ARGLIST] ) && (d : [loc_is_local == "1"])' \
+  --gtx 'enums=([] /+  i=[type == ENUM]) && (i : [loc_is_local == "1"])' \
+  --paste "$prinit" $fn --awk-s '
+
+function load(fn,    s)
+{
+	while((getline line < fn) > 0)
+		s = s line "\n"
+	return s
+}
+
+#function getsrc(source, uid   ,L)
+#{
+#	print TREE[uid, C99F_LOCSTR]
+#	if (split(TREE[uid, C99F_LOCSTR], L, ":") != 2)
+#		return "???"
+#
+#	print L[1] "-" L[2] 
+#	return substr(source, L[1], L[2]-L[1])
+#}
+
+function print_tree(TREE, uid, ind,    jump_next,s)
+{
+	while(uid != "") {
+#print "U", ind, uid, "ty=" C99SYM[TREE[uid, C99F_TYPE]] " nm=" TREE[uid, C99F_NAME]
+		s = s ind C99SYM[TREE[uid, C99F_TYPE]] " " TREE[uid, C99F_NAME] " [0:0] (-1)\n"
+		s = s print_tree(TREE, TREE[uid, C99F_CHILD, 0], ind " ", 1)
+		if (!jump_next)
+			break;
+		uid = TREE[uid, C99F_NEXT]
+	}
+	return s
+}
+
+function to_c(TREE, uid   ,tr,line,all)
+{
+	tr = "FILE  (%l)\n GRAMMAR_TREE  [0:0] (-1)\n"
+	tr = tr print_tree(TREE, uid, "  ")
+
+	cmd = "echo \"" tr "\" | c99tree-cc -tind -Tc"
+	close(cmd)
+	while((cmd | getline line) > 0)
+		all = all line " "
+	close(cmd)
+	return all
+}
+
+function get_any_comment(TREE, uid, bump_uid, dir   ,cmt_uid,cmt)
+{
+	cmt_uid = TREE[uid, C99F_TWIN_PARENT]
+	bump_uid = TREE[bump_uid, C99F_TWIN_PARENT]
+	while(cmt_uid != "") {
+		cmt_uid = TREE[cmt_uid, dir]
+		if (cmt_uid == bump_uid) {
+			cmt = cmt "<comment missing in the header>"
+			break
+		}
+		if (TREE[cmt_uid, C99F_TYPE] == C99T_COMMENT) {
+			cmt = TREE[cmt_uid, C99F_NAME]
+			break
+		}
+	}
+	sub("^[/][*]", "", cmt)
+	sub("[*][/]$", "", cmt)
+	return cmt
+}
+
+function get_pre_comment(TREE, uid, bump_uid)
+{
+	return get_any_comment(TREE, uid, bump_uid, C99F_PREV)
+}
+
+function get_post_comment(TREE, uid, bump_uid)
+{
+	return get_any_comment(TREE, uid, bump_uid, C99F_NEXT)
+}
+
+function genid(type, id)
+{
+	print "<a id=\"" id "\">"
+	print type, id, refname "#" id > fn_ref
+}
+
+BEGIN {
+	pkg="'$pkg'"
+	c99tree_unknown(TREE)
+	gtx_init(TREE)
+	pkgfn="'$pkgfn'"
+	fn_ref = "'$pkgfn'"
+	sub(".h$", "", fn_ref)
+	fn_ref = "REF." fn_ref
+	refname="'$refname'"
+
+
+#	source=load("'$fn'")
+
+	enums = gtx_find_results(TREE, "enums")
+	if (TREE[enums, C99F_CHILDREN] > 0) {
+	print "<h3> Enums </h3>"
+	print "<dl>"
+	print "<p>Enum values should be passed on as strings."
+	for(r = 0; 1; r++) {
+		if (gtx_get_map(TREE, MAP, enums, r) == "")
+			break
+		id = TREE[MAP["i"], C99F_NAME]
+
+		genid("enum", id);
+		print "<H4> " id "</H4>"
+		print "<pre>"
+		print get_pre_comment(TREE, MAP["i"])
+		print "</pre>"
+
+		print "<table border=1>"
+		print "<tr><th>value <th>meaning"
+		for(c = 0; c < TREE[MAP["i"], C99F_CHILDREN]; c++) {
+			uid = TREE[MAP["i"], C99F_CHILD, c]
+			nuid = TREE[MAP["i"], C99F_CHILD, c+1]
+			if (nuid == "")
+				nuid=TREE[TREE[MAP["i"], C99F_PARENT], C99F_NEXT]
+			print "<tr><td>", TREE[uid, C99F_NAME], "<td>", get_post_comment(TREE, uid, nuid)
+		}
+		print "</table>"
+	}
+	print "</dl>"
+	}
+
+	events = gtx_find_results(TREE, "events")
+	if (TREE[events, C99F_CHILDREN] > 0) {
+	print "<h3> Events </h3>"
+	print "<dl>"
+	print "<p>Events do not have return value. The first argument is always <a href=\"event_id.html\">the even id</a>. Event handlers defined in scripts get all event arguments converted to string (types below are informational)."
+	for(r = 0; 1; r++) {
+		if (gtx_get_map(TREE, MAP, events, r) == "")
+			break
+		id = TREE[MAP["i"], C99F_NAME]
+		sub("^GPMI_EVENT__", "", id)
+		proto = to_c(TREE, MAP["d"])
+		sub("^[ \t]*void[ \t]*GPMI_EVENT__", "", proto)
+		sub("[(]", "(int event_id,", proto)
+# proto = getsrc(source, MAP["d"])
+
+		genid("event", id);
+		print "<H4> " proto "</H4>"
+		print "<pre>"
+		luid=TREE[MAP["d"], C99F_PREV]
+		print get_pre_comment(TREE, MAP["a"], luid)
+		print "</pre>"
+	}
+	print "</dl>"
+	}
+
+	funcs = gtx_find_results(TREE, "funcs")
+	if (TREE[funcs, C99F_CHILDREN] > 0) {
+	print "<h3> Functions </h3>"
+	print "<dl>"
+	print "<p>The following functions are registered in script context."
+	for(r = 0; 1; r++) {
+		if (gtx_get_map(TREE, MAP, funcs, r) == "")
+			break
+		id = TREE[MAP["i"], C99F_NAME]
+		if (id ~ "^package_" pkg "_")
+			continue
+		if (id ~ "^pkg_" pkg "_")
+			continue
+
+		sub("^GPMI_EVENT__", "", id)
+#		print "<i>" getsrc(source, MAP["d"]) "</i>"
+		proto = to_c(TREE, MAP["d"])
+# proto = getsrc(source, MAP["d"])
+
+		gsub("[(][ \t]*", "(", proto)
+		genid("function", id);
+		print "<H4>", proto, "</H4>"
+		print "<pre>"
+		luid=TREE[MAP["d"], C99F_PREV]
+		if (TREE[luid, C99F_TWIN_PARENT] == "")
+			luid = TREE[luid, C99F_CHILD, 0]
+		if (TREE[luid, C99F_TWIN_PARENT] == "")
+			luid = TREE[luid, C99F_CHILD, 0]
+
+		print get_pre_comment(TREE, MAP["a"], luid)
+		print "</pre>"
+	}
+	print "</dl>"
+	}
+}
+'
+
+echo "
+</body>
+</html>
+"
+
+
diff --git a/util/gnet-pcbrndfwd.scm b/util/gnet-pcbrndfwd.scm
new file mode 100644
index 0000000..8e84fbd
--- /dev/null
+++ b/util/gnet-pcbrndfwd.scm
@@ -0,0 +1,125 @@
+;;; gEDA - GPL Electronic Design Automation
+;;; gnetlist - gEDA Netlist
+;;; Copyright (C) 1998-2008 Ales Hvezda
+;;; Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;; PCB forward annotation script
+
+(use-modules (ice-9 regex))
+(use-modules (ice-9 format))
+
+;; This is a list of attributes which are propogated to the pcb
+;; elements.  Note that refdes, value, and footprint need not be
+;; listed here.
+(define pcbrndfwd:element-attrs
+  '("device"
+    "manufacturer"
+    "manufacturer_part_number"
+    "vendor"
+    "vendor_part_number"
+    ))
+
+(define (pcbrndfwd:quote-string s)
+  (string-append "\""
+		 (regexp-substitute/global #f "\"" s 'pre "\\\"" 'post)
+		 "\"")
+  )
+
+(define (pcbrndfwd:pinfmt pin)
+  (format #f "~a-~a" (car pin) (car (cdr pin)))
+  )
+
+(define (pcbrndfwd:each-pin net pins port)
+  (if (not (null? pins))
+      (let ((pin (car pins)))
+	(format port "Netlist(Add,~a,~a)~%" net (pcbrndfwd:pinfmt pin))
+	(pcbrndfwd:each-pin net (cdr pins) port))))
+
+(define (pcbrndfwd:each-net netnames port)
+  (if (not (null? netnames))
+      (let ((netname (car netnames)))
+	(pcbrndfwd:each-pin netname (gnetlist:get-all-connections netname) port)
+	(pcbrndfwd:each-net (cdr netnames) port))))
+
+(define (pcbrndfwd:each-attr refdes attrs port)
+  (if (not (null? attrs))
+      (let ((attr (car attrs)))
+	(format port "ElementSetAttr(~a,~a,~a)~%"
+		(pcbrndfwd:quote-string refdes)
+		(pcbrndfwd:quote-string attr)
+		(pcbrndfwd:quote-string (gnetlist:get-package-attribute refdes attr)))
+	(pcbrndfwd:each-attr refdes (cdr attrs) port))))
+
+;; write out the pins for a particular component
+(define pcbrndfwd:component_pins
+  (lambda (port package pins)
+    (if (and (not (null? package)) (not (null? pins)))
+	(begin
+	  (let (
+		(pin (car pins))
+		(label #f)
+		(pinnum #f)
+		)
+	    (display "ChangePinName(" port)
+	    (display (pcbrndfwd:quote-string package) port)
+	    (display ", " port)
+
+	    (set! pinnum (gnetlist:get-attribute-by-pinnumber package pin "pinnumber"))
+
+	    (display pinnum port)
+	    (display ", " port)
+
+	    (set! label (gnetlist:get-attribute-by-pinnumber package pin "pinlabel"))
+	    (if (string=? label "unknown") 
+		(set! label pinnum)
+		)
+	    (display (pcbrndfwd:quote-string label) port)
+	    (display ")\n" port)
+	    )
+	  (pcbrndfwd:component_pins port package (cdr pins))
+	  )
+	)
+    )
+  )
+
+(define (pcbrndfwd:each-element elements port)
+  (if (not (null? elements))
+      (let* ((refdes (car elements))
+	     (value (gnetlist:get-package-attribute refdes "value"))
+	     (footprint (gnetlist:get-package-attribute refdes "footprint"))
+	     )
+
+	(format port "ElementList(Need,~a,~a,~a)~%"
+		(pcbrndfwd:quote-string refdes)
+		(pcbrndfwd:quote-string footprint)
+		(pcbrndfwd:quote-string value))
+	(pcbrndfwd:each-attr refdes pcbrndfwd:element-attrs port)
+	(pcbrndfwd:component_pins port refdes (gnetlist:get-pins refdes))
+
+	(pcbrndfwd:each-element (cdr elements) port))))
+
+(define (pcbrndfwd output-filename)
+  (let ((port (open-output-file output-filename)))
+    (format port "Netlist(Freeze)\n")
+    (format port "Netlist(Clear)\n")
+    (pcbrndfwd:each-net (gnetlist:get-all-unique-nets "dummy") port)
+    (format port "Netlist(Sort)\n")
+    (format port "Netlist(Thaw)\n")
+    (format port "ElementList(Start)\n")
+    (pcbrndfwd:each-element packages port)
+    (format port "ElementList(Done)\n")
+    (close-output-port port)))
diff --git a/util/gsch2pcb-rnd/Makefile.in b/util/gsch2pcb-rnd/Makefile.in
new file mode 100644
index 0000000..cd240bc
--- /dev/null
+++ b/util/gsch2pcb-rnd/Makefile.in
@@ -0,0 +1,138 @@
+put /local/pcb/CFLAGS {-I../.. -I../../src_3rd -I../../src_3rd/liblihata -DSCMDIR="\\"$(LIBDIR)\\""}
+include {../scconfig/template/debug.tmpasm}
+
+print [@
+PLUGDIR=../../src_plugins
+CFLAGS=@/local/pcb/c89flags@ @/local/pcb/CFLAGS@
+LDFLAGS=-lm
+CC=@cc/cc@
+
+HASHOBJ=../../src_3rd/liblihata/genht/hash.o ../../src_3rd/liblihata/genht/htsp.o ../../src_3rd/liblihata/genht/htpp.o
+VECTOBJ=../../src_3rd/genvector/gds_char.o ../../src_3rd/genvector/vtp0.o ../../src_3rd/genvector/vts0.o
+LISTOBJ=../../src_3rd/genlist/genadlist.o ../../src_3rd/genlist/genlistalloc.o
+SCCFOBJ=../../scconfig/src/default/str.o
+QPARSE=../../src_3rd/qparse/qparse.o
+LHTOBJ= \
+	../../src_3rd/liblihata/parser.o \
+	../../src_3rd/liblihata/dom.o \
+	../../src_3rd/liblihata/dom_list.o \
+	../../src_3rd/liblihata/dom_hash.o \
+	../../src_3rd/liblihata/dom_table.o \
+	../../src_3rd/liblihata/lihata.o \
+	../../src_3rd/liblihata/hash_str.o \
+	../../src_3rd/liblihata/tree.o \
+	../../src_3rd/liblihata/tree_list.o \
+	../../src_3rd/liblihata/tree_hash.o \
+	../../src_3rd/liblihata/tree_table.o \
+	../../src_3rd/liblihata/tree_symlink.o \
+	../../src_3rd/liblihata/tree_path.o
+@]
+
+put /local/pcb/tmpasm/buildin  {../src_plugins/Buildin.tmpasm}
+put /local/pcb/tmpasm/plugin   {../src_plugins/Plugin.tmpasm}
+put /local/pcb/tmpasm/disable  {../src_plugins/Disable.tmpasm}
+
+put /local/pcb/RULES {}
+put /local/pcb/OBJS {}
+put /local/pcb/LDFLAGS {}
+put /local/pcb/CFLAGS {}
+put /local/pcb/buildin_init_code {}
+put /local/pcb/buildin_init_extern {}
+
+include {../src_plugins/plugins_fp.tmpasm}
+
+print [@
+
+FP_OBJS    = @/local/pcb/OBJS@ ../../src/plug_footprint.o
+CONF_OBJS  = ../../src/vtlibrary.o ../../src/compat_fs.o ../../src/paths.o ../../src/conf.o ../../src/conf_core.o ../../src/hid_cfg.o ../../src/misc_util.o ../../src/unit.o ../../src/conf_internal.o ../../src/list_conf.o ../../src/conf_hid.o ../../src/pcb-printf.o ../../src/compat_misc.o
+FP_LDFLAGS = @/local/pcb/LDFLAGS@
+FP_CFLAGS  = @/local/pcb/CFLAGS@
+OBJS=gsch2pcb.o help.o
+
+all:
+	make revcheck
+	make gsch2pcb-rnd$(EXE)
+
+revcheck:
+	cd ../../scconfig && ./revtest Rev.stamp < Rev.tab
+
+gsch2pcb-rnd$(EXE): $(OBJS)  $(CONF_OBJS) $(HASHOBJ) $(VECTOBJ) $(LISTOBJ) $(SCCFOBJ) $(QPARSE) $(FP_OBJS) $(LHTOBJ)
+	$(CC) $(OBJS) $(CONF_OBJS)  $(FP_OBJS) $(HASHOBJ) $(VECTOBJ) $(LISTOBJ) $(SCCFOBJ) $(QPARSE) $(LHTOBJ) -o gsch2pcb-rnd$(EXE) $(LDFLAGS) $(FP_LDFLAGS)
+
+gsch2pcb.o: gsch2pcb.c ../../config.h
+	$(CC) -c $(CFLAGS) $(FP_CFLAGS) gsch2pcb.c -o gsch2pcb.o
+
+../../src/pcb-rnd:
+	cd ../../src && make
+
+$(HASHOBJ): ../../src/pcb-rnd
+
+$(QPARSE): ../../src/pcb-rnd
+
+../../src_3rd/genvector/gds_char.o:
+	$(CC) -c $(CFLAGS) ../../src_3rd/genvector/gds_char.c -o ../../src_3rd/genvector/gds_char.o
+
+../../src_3rd/genvector/vtp0.o:
+	$(CC) -c $(CFLAGS) ../../src_3rd/genvector/vtp0.c -o ../../src_3rd/genvector/vtp0.o
+
+../../src_3rd/genvector/vts0.o:
+	$(CC) -c $(CFLAGS) ../../src_3rd/genvector/vts0.c -o ../../src_3rd/genvector/vts0.o
+
+../../src_3rd/genadlist/genadlist.o:
+	$(CC) -c $(CFLAGS) ../../src_3rd/genlist/genadlist.c -o ../../src_3rd/genlist/genadlist.o
+
+../../src_3rd/genadlist/genlistalloc.o:
+	$(CC) -c $(CFLAGS) ../../src_3rd/genlist/genlistalloc.c -o ../../src_3rd/genlist/genlistalloc.o
+
+
+
+
+##### module rules begin #####
+@/local/pcb/RULES@
+##### module rules end #####
+@]
+
+# generate explicit rules for .c -> .o
+put /local/comp/OBJS /local/pcb/OBJS
+include {../scconfig/Makefile.comp.inc}
+
+print [@
+
+gsch2pcb.o: gsch2pcb_rnd_conf_fields.h
+
+gsch2pcb_rnd_conf_fields.h: gsch2pcb_rnd_conf.h
+	AWK=@/host/fstools/awk@ ../../scconfig/gen_conf.sh < gsch2pcb_rnd_conf.h > gsch2pcb_rnd_conf_fields.h
+
+install_:
+	$(MKDIR) "$(BINDIR)" "$(LIBDIR)"
+	$(CPC) "`pwd`/gsch2pcb-rnd$(EXE)" "$(BINDIR)/gsch2pcb-rnd$(EXE)"
+	$(CPC) "`pwd`/gnet-gsch2pcb-rnd.scm" "$(LIBDIR)/gnet-gsch2pcb-rnd.scm"
+
+install:
+	make install_ CPC="$(CP)"
+
+linstall:
+	make install_ CPC="$(LN)"
+
+uninstall:
+	$(RM) "$(BINDIR)/gsch2pcb-rnd$(EXE)"
+	$(RM) "$(LIBDIR)/gnet-gsch2pcb-rnd.scm"
+
+
+clean:
+	$(RM) gsch2pcb-rnd$(EXE) $(OBJS) gsch2pcb_rnd_conf_fields.h
+
+include ../../Makefile.conf
+
+@]
+
+redir {gsch2pcb-rnd/fp_init.c}
+print [@
+@?/local/pcb/buildin_init_code@
+@]
+
+redir {gsch2pcb-rnd/fp_init.h}
+print [@
+@/local/pcb/buildin_init_extern@
+@]
+
diff --git a/util/gsch2pcb-rnd/gnet-gsch2pcb-rnd.scm b/util/gsch2pcb-rnd/gnet-gsch2pcb-rnd.scm
new file mode 100644
index 0000000..cd116cb
--- /dev/null
+++ b/util/gsch2pcb-rnd/gnet-gsch2pcb-rnd.scm
@@ -0,0 +1,56 @@
+;;; -*-scheme-*-
+;;;
+
+;;; gEDA - GPL Electronic Design Automation
+;;; gnetlist - gEDA Netlist
+;;; Copyright (C) 1998-2010 Ales Hvezda
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+;;; MA 02111-1301 USA.
+
+;;  gsch2pcb format  (based on PCBboard format by JM Routoure & Stefan Petersen)
+;;  Bill Wilson    billw at wt.net
+;;  6/17/2003
+
+;; Simplified (thrown out m4 support) specially for Igor2 by vzh
+
+;; Splits a string with space separated words and returns a list
+;; of the words (still as strings).
+(define (gsch2pcb-rnd:split-to-list the-string)
+  (filter!
+   (lambda (x) (not (string=? "" x)))
+   (string-split the-string #\space)))
+
+;; Write the footprint for the package `refdes' to `port'.
+(define (gsch2pcb-rnd:write-value-footprint refdes port)
+
+  (let ((value (gnetlist:get-package-attribute refdes "value"))
+         (footprint (gsch2pcb-rnd:split-to-list
+                     (gnetlist:get-package-attribute refdes "footprint"))))
+
+    (format port "PKG(~A,~A,~A)\n" (string-join footprint " ") refdes value)))
+
+;; Write the footprints for all the refdes' in `lst'.
+(define (gsch2pcb-rnd:write-value-footprints port lst)
+  (for-each (lambda (x) (gsch2pcb-rnd:write-value-footprint x port)) lst))
+
+
+(define (gsch2pcb-rnd output-filename)
+  (begin
+;;    (set-current-output-port (gnetlist:output-port output-filename))
+    (set-current-output-port (open-output-file output-filename))
+
+    ;; don't use m4
+    (gsch2pcb-rnd:write-value-footprints (current-output-port) packages)))
diff --git a/util/gsch2pcb-rnd/gsch2pcb.c b/util/gsch2pcb-rnd/gsch2pcb.c
new file mode 100644
index 0000000..bbe70a2
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb.c
@@ -0,0 +1,1351 @@
+/* gsch2pcb-rnd
+ *
+ *  Original version: Bill Wilson    billw at wt.net
+ *  rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software which I release under the GNU General Public
+ *  License. You may redistribute and/or modify this program under the terms
+ *  of that license as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.  Version 2 is in the
+ *  COPYRIGHT file in the top level directory of this distribution.
+ *
+ *  To get a copy of the GNU General Puplic License, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ * Retrieved from the official git (2015.07.15)
+
+ Behavior different from the original:
+  - use getenv() instead of g_getenv(): on windows this won't do recursive variable expansion
+  - use rnd-specific .scm
+  - use pcb-rnd's conf system
+  - use popen() instead of glib's spawn (stderr is always printed to stderr)
+ */
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "../src/plug_footprint.h"
+#include "../src/paths.h"
+#include "../src/conf.h"
+#include "../src/conf_core.h"
+#include "../src_3rd/genvector/vts0.h"
+#include "../src_3rd/genlist/gendlist.h"
+#include "../src_3rd/genlist/genadlist.h"
+#include "../src_3rd/qparse/qparse.h"
+#include "../config.h"
+#include "../src/error.h"
+#include "../src/plugins.h"
+#include "../src/plug_footprint.h"
+#include "../src/compat_misc.h"
+#include "help.h"
+#include "gsch2pcb_rnd_conf.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define GSCH2PCB_RND_VERSION "1.0.1"
+
+#define DEFAULT_PCB_INC "pcb.inc"
+
+#define SEP_STRING "--------\n"
+
+/* from scconfig str lib: */
+char *str_concat(const char *sep, ...);
+
+typedef struct {
+	char *refdes, *value, *description, *changed_description, *changed_value;
+	char *flags, *tail;
+	char *x, *y;
+	char *pkg_name_fix;
+	char res_char;
+
+	gdl_elem_t all_elems;
+
+	unsigned still_exists:1;
+	unsigned new_format:1;
+	unsigned hi_res_format:1;
+	unsigned quoted_flags:1;
+	unsigned omit_PKG:1;
+	unsigned nonetlist:1;
+
+} PcbElement;
+
+typedef struct {
+	char *part_number, *element_name;
+} ElementMap;
+
+gdl_list_t pcb_element_list; /* initialized to 0 */
+gadl_list_t schematics, extra_gnetlist_arg_list, extra_gnetlist_list;
+
+static int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new,
+           n_PKG_removed_old, n_preserved, n_changed_value, n_not_found,
+           n_unknown, n_none, n_empty;
+
+static int bak_done, need_PKG_purge;
+
+conf_gsch2pcb_rnd_t conf_g2pr;
+
+static const char *element_search_path = NULL; /* queried once from the config, when the config is already stable */
+
+static char *loc_strndup(const char *str, size_t len)
+{
+	char *s;
+	int l;
+
+	if (str == NULL)
+		return NULL;
+	l = strlen(str);
+	if (l < len)
+		len = l;
+	s = malloc(len+1);
+	memcpy(s, str, len);
+	s[len] = '\0';
+	return s;
+}
+
+/* Return a pointer to the suffix if inp ends in that suffix */
+static char *loc_str_has_suffix(char *inp, const char *suffix, int suff_len)
+{
+	int len = strlen(inp);
+	if ((len >= suff_len) && (strcmp(inp + len - suff_len, suffix) == 0))
+		return inp + len - suff_len;
+	return NULL;
+}
+
+/* Checks if a file exists and is readable */
+static int file_exists(const char *fn)
+{
+	FILE *f;
+	f = fopen(fn, "r");
+	if (f == NULL)
+		return 0;
+	fclose(f);
+	return 1;
+}
+
+void ChdirErrorMessage(const char *DirName)
+{
+	fprintf(stderr, "gsch2pcb-rnd: warning: can't cd to %s\n", DirName);
+}
+
+void OpendirErrorMessage(const char *DirName)
+{
+	fprintf(stderr, "gsch2pcb-rnd: warning: can't opendir %s\n", DirName);
+}
+
+void PopenErrorMessage(const char *cmd)
+{
+	fprintf(stderr, "gsch2pcb-rnd: warning: can't popen %s\n", cmd);
+}
+
+void Message(enum pcb_message_level level, const char *fmt, ...)
+{
+	va_list args;
+	fprintf(stderr, "gsch2pcb-rnd: ");
+	va_start(args, fmt);
+	vfprintf(stderr, fmt, args);
+	va_end(args);
+	fprintf(stderr, "\n");
+}
+
+void pcb_trace(const char *Format, ...)
+{
+#ifndef NDEBUG
+	va_list args;
+	va_start(args, Format);
+	vfprintf(stderr, Format, args);
+	va_end(args);
+#endif
+}
+
+
+/**
+ * Build and run a command. No redirection or error handling is
+ * done.  Format string is split on whitespace. Specifiers %l and %s
+ * are replaced with contents of positional args. To be recognized,
+ * specifiers must be separated from other arguments in the format by
+ * whitespace.
+ *  - %L expects a gadl_list_t vith char * payload, contents used as separate arguments
+ *  - %s expects a char*, contents used as a single argument (omitted if NULL)
+ * @param[in] format  used to specify command to be executed
+ * @param[in] ...     positional parameters
+ */
+static int build_and_run_command(const char * format_, ...)
+{
+	va_list vargs;
+	int result = FALSE;
+	vts0_t args;
+	char *format, *s, *start;
+
+	/* Translate the format string; args elements point to const char *'s
+	   within a copy of the format string. The format string is copied so
+	   that these parts can be terminated by overwriting whitepsace with \0 */
+	va_start(vargs, format_);
+	format = pcb_strdup(format_);
+	vts0_init(&args);
+	for(s = start = format; *s != '\0'; s++) {
+		/* if word separator is reached, save the previous word */
+		if (isspace(s[0])) {
+			if (start == s) { /* empty word - skip */
+				start++;
+				continue;
+			}
+			*s = '\0';
+			vts0_append(&args, start);
+			start = s+1;
+			continue;
+		}
+
+		/* check if current word is a format */
+		if ((s == start) && (s[0] == '%') && (s[1] != '\0') && ((s[2] == '\0') || isspace(s[2]))) {
+			switch(s[1]) {
+				case 'L': /* append contents of char * gadl_list_t */
+					{
+						gadl_list_t *list = va_arg(vargs, gadl_list_t *);
+						gadl_iterator_t it;
+						char **s;
+						gadl_foreach(list, &it, s) {
+							vts0_append(&args, *s);
+						}
+					}
+					start = s+2;
+					s++;
+					continue;
+				case 's':
+					{
+						char *arg = va_arg(vargs, char *);
+						if (arg != NULL)
+							vts0_append(&args, arg);
+						start = s+2;
+						s++;
+					}
+					continue;
+			}
+		}
+	}
+	va_end(vargs);
+
+	if (args.used > 0) {
+		int i, l;
+		char *cmd, *end, line[1024];
+		FILE *f;
+
+		l = 0;
+		for (i = 0; i < args.used; i++)
+			l += strlen(args.array[i]) + 3;
+
+		end = cmd = malloc(l+1);
+		for (i = 0; i < args.used; i++) {
+			l = strlen(args.array[i]);
+			*end = '"'; end++;
+			memcpy(end, args.array[i], l);
+			end += l;
+			*end = '"'; end++;
+			*end = ' '; end++;
+		}
+		end--;
+		*end = '\0';
+
+		/* we have something in the list, build & call command */
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose) {
+			printf("Running command:\n\t%s\n", cmd);
+			printf("%s", SEP_STRING);
+		}
+
+		f = popen(cmd, "r");
+		while(fgets(line, sizeof(line), f) != NULL) {
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				fputs(line, stdout);
+		}
+
+		if (pclose(f) == 0)
+			result = TRUE;
+		else
+			fprintf(stderr, "Failed to execute external program\n");
+		free(cmd);
+
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+			printf("\n%s", SEP_STRING);
+	}
+
+	free(format);
+	vts0_uninit(&args);
+	return result;
+}
+
+/* Run gnetlist to generate a netlist and a PCB board file.  gnetlist
+ * has exit status of 0 even if it's given an invalid arg, so do some
+ * stat() hoops to decide if gnetlist successfully generated the PCB
+ * board file (only gnetlist >= 20030901 recognizes -m).
+ */
+static int run_gnetlist(const char * pins_file, const char * net_file, const char * pcb_file, const char * basename, const gadl_list_t *largs)
+{
+	struct stat st;
+	time_t mtime;
+	static const char *gnetlist = NULL;
+	gadl_iterator_t it;
+	char **sp;
+	char *verbose_str = NULL;
+
+	/* Allow the user to specify a full path or a different name for
+	 * the gnetlist command.  Especially useful if multiple copies
+	 * are installed at once.
+	 */
+	if (gnetlist == NULL)
+		gnetlist = getenv("GNETLIST");
+	if (gnetlist == NULL)
+		gnetlist = "gnetlist";
+
+	if (!conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		verbose_str = "-q";
+
+	if (!build_and_run_command("%s %s -g pcbpins -o %s %L %L", gnetlist, verbose_str, pins_file, &extra_gnetlist_arg_list, largs))
+		return FALSE;
+
+	if (!build_and_run_command("%s %s -g PCB -o %s %L %L", gnetlist, verbose_str, net_file, &extra_gnetlist_arg_list, largs))
+		return FALSE;
+
+	mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0;
+
+	if (!build_and_run_command("%s %s -L " SCMDIR " -g gsch2pcb-rnd -o %s %L %L",
+														 gnetlist, verbose_str, pcb_file, &extra_gnetlist_arg_list, largs)) {
+		if (stat(pcb_file, &st) != 0 || mtime == st.st_mtime) {
+			fprintf(stderr, "gsch2pcb: gnetlist command failed, `%s' not updated\n", pcb_file);
+			return FALSE;
+		}
+		return FALSE;
+	}
+
+	gadl_foreach(&extra_gnetlist_list, &it, sp) {
+		const char *s = *sp;
+		const char *s2 = strstr(s, " -o ");
+		char *out_file;
+		char *backend;
+		if (!s2) {
+			out_file = str_concat(NULL, basename, ".", s, NULL);
+			backend = pcb_strdup(s);
+		}
+		else {
+			out_file = pcb_strdup(s2 + 4);
+			backend = loc_strndup(s, s2 - s);
+		}
+
+		if (!build_and_run_command("%s %s -g %s -o %s %L %L",
+															 gnetlist, verbose_str, backend, out_file, &extra_gnetlist_arg_list, largs))
+			return FALSE;
+		free(out_file);
+		free(backend);
+	}
+
+	return TRUE;
+}
+
+static char *token(char * string, char ** next, int * quoted_ret, int parenth)
+{
+	static char *str;
+	char *s, *ret;
+	int quoted = FALSE;
+
+	if (string)
+		str = string;
+	if (!str || !*str) {
+		if (next)
+			*next = str;
+		return pcb_strdup("");
+	}
+	while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
+		++str;
+
+	if (*str == '"') {
+		quoted = TRUE;
+		if (quoted_ret)
+			*quoted_ret = TRUE;
+		++str;
+		for (s = str; *s && *s != '"' && *s != '\n'; ++s);
+	}
+	else {
+		if (quoted_ret)
+			*quoted_ret = FALSE;
+		for (s = str; *s != '\0'; ++s) {
+			if ((parenth) && (*s == '(')) {
+				quoted = TRUE;
+				if (quoted_ret)
+					*quoted_ret = TRUE;
+				for (; *s && *s != ')' && *s != '\n'; ++s);
+				/* preserve closing ')' */
+				if (*s == ')')
+					s++;
+				break;
+			}
+			if (*s == ' ' || *s == '\t' || *s == ',' || *s == '\n')
+				break;
+		}
+	}
+	ret = loc_strndup(str, s - str);
+	str = (quoted && *s) ? s + 1 : s;
+	if (next)
+		*next = str;
+	return ret;
+}
+
+static char *fix_spaces(char * str)
+{
+	char *s;
+
+	if (!str)
+		return NULL;
+	for (s = str; *s; ++s)
+		if (*s == ' ' || *s == '\t')
+			*s = '_';
+	return str;
+}
+
+	/* As of 1/9/2004 CVS hi_res Element[] line format:
+	 *   Element[element_flags, description, pcb-name, value, mark_x, mark_y,
+	 *       text_x, text_y, text_direction, text_scale, text_flags]
+	 *   New PCB 1.7 / 1.99 Element() line format:
+	 *   Element(element_flags, description, pcb-name, value, mark_x, mark_y,
+	 *       text_x, text_y, text_direction, text_scale, text_flags)
+	 *   Old PCB 1.6 Element() line format:
+	 *   Element(element_flags, description, pcb-name, value,
+	 *       text_x, text_y, text_direction, text_scale, text_flags)
+	 *
+	 *   (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
+	 *   is the description text position which is absolute in pre 1.7 and
+	 *   is now relative.  The hi_res mark_x,mark_y and text_x,text_y resolutions
+	 *   are 100x the other formats.
+	 */
+PcbElement *pcb_element_line_parse(char * line)
+{
+	PcbElement *el = NULL;
+	char *s, *t, close_char;
+	int state = 0, elcount = 0, tmp;
+
+	if (strncmp(line, "Element", 7))
+		return NULL;
+
+	el = calloc(sizeof(PcbElement), 1);
+
+	s = line + 7;
+	while (*s == ' ' || *s == '\t')
+		++s;
+
+	if (*s == '[')
+		el->hi_res_format = TRUE;
+	else if (*s != '(') {
+		free(el);
+		return NULL;
+	}
+
+	el->res_char = el->hi_res_format ? '[' : '(';
+	close_char = el->hi_res_format ? ']' : ')';
+
+	el->flags = token(s + 1, NULL, &tmp, 0); el->quoted_flags = tmp;
+	el->description = token(NULL, NULL, NULL, 0);
+	el->refdes = token(NULL, NULL, NULL, 0);
+	el->value = token(NULL, NULL, NULL, 0);
+
+	el->x = token(NULL, NULL, NULL, 0);
+	el->y = token(NULL, &t, NULL, 0);
+
+	el->tail = pcb_strdup(t ? t : "");
+	if ((s = strrchr(el->tail, (int) '\n')) != NULL)
+		*s = '\0';
+
+	/* Count the tokens in tail to decide if it's new or old format.
+	 * Old format will have 3 tokens, new format will have 5 tokens.
+	 */
+	for (s = el->tail; *s && *s != close_char; ++s) {
+		if (*s != ' ') {
+			if (state == 0)
+				++elcount;
+			state = 1;
+		}
+		else
+			state = 0;
+	}
+	el->nonetlist = 0;
+	if (elcount > 4) {
+		el->new_format = TRUE;
+		if (strstr(el->tail, "nonetlist") != NULL)
+			el->nonetlist = 1;
+	}
+
+	fix_spaces(el->description);
+	fix_spaces(el->refdes);
+	fix_spaces(el->value);
+
+	/* Don't allow elements with no refdes to ever be deleted because
+	 * they may be desired pc board elements not in schematics.  So
+	 * initialize still_exists to TRUE if empty or non-alphanumeric
+	 * refdes.
+	 */
+	if (!*el->refdes || !isalnum((int) (*el->refdes)))
+		el->still_exists = TRUE;
+
+	return el;
+}
+
+static void pcb_element_free(PcbElement * el)
+{
+	if (!el)
+		return;
+	free(el->flags);
+	free(el->description);
+	free(el->changed_description);
+	free(el->changed_value);
+	free(el->refdes);
+	free(el->value);
+	free(el->x);
+	free(el->y);
+	free(el->tail);
+	free(el->pkg_name_fix);
+	free(el);
+}
+
+static void get_pcb_element_list(char * pcb_file)
+{
+	FILE *f;
+	PcbElement *el;
+	char *s, buf[1024];
+
+	if ((f = fopen(pcb_file, "r")) == NULL)
+		return;
+	while ((fgets(buf, sizeof(buf), f)) != NULL) {
+		for (s = buf; *s == ' ' || *s == '\t'; ++s);
+		if (!strncmp(s, "PKG_", 4)) {
+			need_PKG_purge = TRUE;
+			continue;
+		}
+		if ((el = pcb_element_line_parse(s)) == NULL)
+			continue;
+		gdl_append(&pcb_element_list, el, all_elems);
+	}
+	fclose(f);
+}
+
+static PcbElement *pcb_element_exists(PcbElement * el_test, int record)
+{
+	PcbElement *el;
+	gdl_iterator_t it;
+
+	gdl_foreach(&pcb_element_list, &it, el) {
+		if (strcmp(el_test->refdes, el->refdes))
+			continue;
+		if (strcmp(el_test->description, el->description)) {	/* footprint */
+			if (record)
+				el->changed_description = pcb_strdup(el_test->description);
+		}
+		else {
+			if (record) {
+				if (strcmp(el_test->value, el->value))
+					el->changed_value = pcb_strdup(el_test->value);
+				el->still_exists = TRUE;
+			}
+			return el;
+		}
+	}
+	return NULL;
+}
+
+/* A problem is that new PCB 1.7 file elements have the
+ * (mark_x,mark_y) value set to wherever the element was created and
+ * no equivalent of a gschem translate symbol was done.
+ *
+ * So, file elements inserted can be scattered over a big area and
+ * this is bad when loading a file.new.pcb into an existing PC
+ * board.  So, do a simple translate if (mark_x,mark_y) is
+ * (arbitrarily) over 1000.  I'll assume that for values < 1000 the
+ * element creator was concerned with a sane initial element
+ * placement.  Unless someone has a better idea?  Don't bother with
+ * pre PCB 1.7 formats as that would require parsing the mark().
+ */
+static void simple_translate(PcbElement * el)
+{
+	if (el->x != NULL)
+		free(el->x);
+	if (el->y != NULL)
+		free(el->y);
+	el->x = pcb_strdup("0");
+	el->y = pcb_strdup("0");
+}
+
+static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value)
+{
+	PcbElement *el;
+	char *fmt, *s, buf[1024];
+	int retval = FALSE;
+
+	/* Copy the file element lines.  Substitute new parameters into the
+	 * Element() or Element[] line and strip comments.
+	 */
+	while ((fgets(buf, sizeof(buf), f_elem)) != NULL) {
+		for (s = buf; *s == ' ' || *s == '\t'; ++s);
+		if ((el = pcb_element_line_parse(s)) != NULL) {
+			simple_translate(el);
+			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
+
+			fprintf(f_out, fmt, el->res_char, el->flags, footprint, refdes, value, el->x, el->y, el->tail);
+			retval = TRUE;
+		}
+		else if (*s != '#')
+			fputs(buf, f_out);
+		pcb_element_free(el);
+	}
+	return retval;
+}
+
+
+char *search_element(PcbElement * el)
+{
+	char *elname = NULL, *path = NULL;
+
+	if (!elname)
+		elname = pcb_strdup(el->description);
+
+	if (!strcmp(elname, "unknown")) {
+		free(elname);
+		return NULL;
+	}
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
+		printf("\tSearching directories looking for file element: %s\n", elname);
+	free(elname);
+	return path;
+}
+
+/* The gnetlist backend gnet-gsch2pcb-rnd.scm generates PKG lines:
+ *
+ *        PKG(footprint,refdes,value)
+ *
+ */
+static PcbElement *pkg_to_element(FILE * f, char * pkg_line)
+{
+	PcbElement *el;
+	char *s, *end, *refdes, *fp, *value;
+
+/*fprintf(stderr, "--- %s\n", pkg_line);*/
+
+	if (strncmp(pkg_line, "PKG", 3)
+			|| (s = strchr(pkg_line, (int) '(')) == NULL)
+		return NULL;
+
+/* remove trailing ")" */
+	end = s + strlen(s) - 2;
+	if (*end == ')')
+		*end = '\0';
+
+/* tokenize the line keeping () */
+	fp = token(s + 1, NULL, NULL, 1);
+	refdes = token(NULL, NULL, NULL, 1);
+	value = token(NULL, NULL, NULL, 1);
+
+
+/*fprintf(stderr, "refdes: %s\n", refdes);
+fprintf(stderr, "    fp: %s\n", fp);
+fprintf(stderr, "   val: %s\n", value);*/
+
+
+	if (!refdes || !fp || !value) {
+		if (refdes != NULL)
+			free(refdes);
+		if (fp != NULL)
+			free(fp);
+		if (value != NULL)
+			free(value);
+		fprintf(stderr, "Bad package line: %s\n", pkg_line);
+		return NULL;
+	}
+
+	fix_spaces(refdes);
+	fix_spaces(value);
+
+	el = calloc(sizeof(PcbElement), 1);
+	el->description = fp;
+	el->refdes = refdes;
+	el->value = value;
+
+/*
+// wtf?
+//  if ((s = strchr (el->value, (int) ')')) != NULL)
+//    *s = '\0';
+*/
+
+	if (conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name && !strcmp(el->description, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name)) {
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+			printf("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
+		n_empty += 1;
+		el->omit_PKG = TRUE;
+	}
+	else if (!strcmp(el->description, "none")) {
+		fprintf(stderr, "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
+		n_none += 1;
+		el->omit_PKG = TRUE;
+	}
+	else if (!strcmp(el->description, "unknown")) {
+		fprintf(stderr, "WARNING: %s has no footprint attribute so won't be in the layout.\n", el->refdes);
+		n_unknown += 1;
+		el->omit_PKG = TRUE;
+	}
+	return el;
+}
+
+/* Copies the content of fn to fout and returns 0 on success. */
+static int CatPCB(FILE * fout, const char *fn)
+{
+	FILE *fin;
+	fin = fopen(fn, "r");
+	if (fin == NULL)
+		return -1;
+
+	for (;;) {
+		char buff[1024];
+		int len;
+
+		len = fread(buff, 1, sizeof(buff), fin);
+		if (len <= 0)
+			break;
+
+		fwrite(buff, len, 1, fout);
+	}
+
+	fclose(fin);
+	return 0;
+}
+
+/* Process the newly created pcb file which is the output from
+ *     gnetlist -g gsch2pcb-rnd ...
+ *
+ * Insert elements for PKG_ lines if they be found by external element query.
+ * If there was an existing pcb file, strip out any elements if they are
+ * already present so that the new pcb file will only have new elements.
+ */
+static int add_elements(char * pcb_file)
+{
+	FILE *f_in, *f_out, *fp;
+	PcbElement *el = NULL;
+	char *tmp_file, *s, buf[1024];
+	int total, paren_level = 0;
+	int skipping = FALSE;
+	int dpcb;
+	fp_fopen_ctx_t fctx;
+
+	if ((f_in = fopen(pcb_file, "r")) == NULL)
+		return 0;
+	tmp_file = str_concat(NULL, pcb_file, ".tmp", NULL);
+	if ((f_out = fopen(tmp_file, "wb")) == NULL) {
+		fclose(f_in);
+		free(tmp_file);
+		return 0;
+	}
+
+	if (conf_g2pr.utils.gsch2pcb_rnd.default_pcb == NULL) {
+		dpcb = -1;
+		conf_list_foreach_path_first(dpcb, &conf_core.rc.default_pcb_file, CatPCB(f_out, __path__));
+		if (dpcb != 0) {
+			fprintf(stderr, "ERROR: can't load default pcb (using the configured search paths)\n");
+			exit(1);
+		}
+	}
+	else {
+		if (CatPCB(f_out, conf_g2pr.utils.gsch2pcb_rnd.default_pcb) != 0) {
+			fprintf(stderr, "ERROR: can't load default pcb (using user defined %s)\n", conf_g2pr.utils.gsch2pcb_rnd.default_pcb);
+			exit(1);
+		}
+	}
+
+	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
+		for (s = buf; *s == ' ' || *s == '\t'; ++s);
+		if (skipping) {
+			if (*s == '(')
+				++paren_level;
+			else if (*s == ')' && --paren_level <= 0)
+				skipping = FALSE;
+			continue;
+		}
+		el = pkg_to_element(f_out, s);
+		if (el && pcb_element_exists(el, TRUE)) {
+			pcb_element_free(el);
+			continue;
+		}
+		if (!el || el->omit_PKG) {
+			if (el) {
+
+			}
+			else
+				fputs(buf, f_out);
+			continue;
+		}
+
+		{
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				printf("%s: need new element for footprint  %s (value=%s)\n", el->refdes, el->description, el->value);
+
+			fp = fp_fopen(element_search_path, el->description, &fctx);
+
+			if (fp == NULL && conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				printf("\tNo file element found.\n");
+
+			if ((fp != NULL) && insert_element(f_out, fp, el->description, el->refdes, el->value)) {
+				++n_added_ef;
+				if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+					printf("%s: added new element for footprint %s (value=%s)\n", el->refdes, el->description, el->value);
+			}
+			else {
+				fprintf(stderr, "%s: can't find PCB element for footprint %s (value=%s)\n", el->refdes, el->description, el->value);
+				if (conf_g2pr.utils.gsch2pcb_rnd.remove_unfound_elements && !conf_g2pr.utils.gsch2pcb_rnd.fix_elements) {
+					fprintf(stderr, "So device %s will not be in the layout.\n", el->refdes);
+					++n_PKG_removed_new;
+				}
+				else {
+					++n_not_found;
+					fputs(buf, f_out);		/* Copy PKG_ line */
+				}
+			}
+			if (fp != NULL)
+				fp_fclose(fp, &fctx);
+		}
+
+		pcb_element_free(el);
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+			printf("----\n");
+	}
+	fclose(f_in);
+	fclose(f_out);
+
+	total = n_added_ef + n_not_found;
+	if (total == 0)
+		build_and_run_command("rm %s", tmp_file);
+	else
+		build_and_run_command("mv %s %s", tmp_file, pcb_file);
+	free(tmp_file);
+	return total;
+}
+
+static void update_element_descriptions(char * pcb_file, char * bak)
+{
+	FILE *f_in, *f_out;
+	PcbElement *el, *el_exists;
+	char *fmt, *tmp, *s, buf[1024];
+	gdl_iterator_t it;
+
+	gdl_foreach(&pcb_element_list, &it, el) {
+		if (el->changed_description)
+			++n_fixed;
+	}
+	if (!pcb_element_list.length || n_fixed == 0) {
+		fprintf(stderr, "Could not find any elements to fix.\n");
+		return;
+	}
+	if ((f_in = fopen(pcb_file, "r")) == NULL)
+		return;
+	tmp = str_concat(NULL, pcb_file, ".tmp", NULL);
+	if ((f_out = fopen(tmp, "wb")) == NULL) {
+		fclose(f_in);
+		return;
+	}
+	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
+		for (s = buf; *s == ' ' || *s == '\t'; ++s);
+		if ((el = pcb_element_line_parse(s)) != NULL
+				&& (el_exists = pcb_element_exists(el, FALSE)) != NULL && el_exists->changed_description) {
+			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
+			fprintf(f_out, fmt,
+							el->res_char, el->flags, el_exists->changed_description, el->refdes, el->value, el->x, el->y, el->tail);
+			printf("%s: updating element Description: %s -> %s\n", el->refdes, el->description, el_exists->changed_description);
+			el_exists->still_exists = TRUE;
+		}
+		else
+			fputs(buf, f_out);
+		pcb_element_free(el);
+	}
+	fclose(f_in);
+	fclose(f_out);
+
+	if (!bak_done) {
+		build_and_run_command("mv %s %s", pcb_file, bak);
+		bak_done = TRUE;
+	}
+
+	build_and_run_command("mv %s %s", tmp, pcb_file);
+	free(tmp);
+}
+
+static void prune_elements(char * pcb_file, char * bak)
+{
+	FILE *f_in, *f_out;
+	PcbElement *el, *el_exists;
+	char *fmt, *tmp, *s, buf[1024];
+	int paren_level = 0;
+	int skipping = FALSE;
+	gdl_iterator_t it;
+
+	gdl_foreach(&pcb_element_list, &it, el) {
+		if (!el->still_exists) {
+			if ((conf_g2pr.utils.gsch2pcb_rnd.preserve) || (el->nonetlist)) {
+				++n_preserved;
+				if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
+					fprintf(stderr,
+									"Preserving PCB element not in the schematic:    %s (element   %s) %s\n",
+									el->refdes, el->description, el->nonetlist ? "nonetlist" : "");
+			}
+			else
+				++n_deleted;
+		}
+		else if (el->changed_value)
+			++n_changed_value;
+	}
+	if ((pcb_element_list.length == 0) || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)) {
+		return;
+	}
+	if ((f_in = fopen(pcb_file, "r")) == NULL) {
+		fprintf(stderr, "error: can not read %s\n", pcb_file);
+		return;
+	}
+	tmp = str_concat(NULL, pcb_file, ".tmp", NULL);
+	if ((f_out = fopen(tmp, "wb")) == NULL) {
+		fprintf(stderr, "error: can not write %s\n", tmp);
+		fclose(f_in);
+		return;
+	}
+
+	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
+		for (s = buf; *s == ' ' || *s == '\t'; ++s);
+		if (skipping) {
+			if (*s == '(')
+				++paren_level;
+			else if (*s == ')' && --paren_level <= 0)
+				skipping = FALSE;
+			continue;
+		}
+		el_exists = NULL;
+		if ((el = pcb_element_line_parse(s)) != NULL
+				&& (el_exists = pcb_element_exists(el, FALSE)) != NULL && !el_exists->still_exists && !conf_g2pr.utils.gsch2pcb_rnd.preserve && !el->nonetlist) {
+			skipping = TRUE;
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				printf("%s: deleted element %s (value=%s)\n", el->refdes, el->description, el->value);
+			pcb_element_free(el);
+			continue;
+		}
+		if (el_exists && el_exists->changed_value) {
+			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
+			fprintf(f_out, fmt,
+							el->res_char, el->flags, el->description, el->refdes, el_exists->changed_value, el->x, el->y, el->tail);
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				printf("%s: changed element %s value: %s -> %s\n", el->refdes, el->description, el->value, el_exists->changed_value);
+		}
+		else if (!strncmp(s, "PKG_", 4))
+			++n_PKG_removed_old;
+		else
+			fputs(buf, f_out);
+		pcb_element_free(el);
+	}
+	fclose(f_in);
+	fclose(f_out);
+
+	if (!bak_done) {
+		build_and_run_command("mv %s %s", pcb_file, bak);
+		bak_done = TRUE;
+	}
+
+	build_and_run_command("mv %s %s", tmp, pcb_file);
+	free(tmp);
+}
+
+static void add_schematic(char * sch)
+{
+	char **n;
+	n = gadl_new(&schematics);
+	*n = pcb_strdup(sch);
+	gadl_append(&schematics, n);
+	if (!conf_g2pr.utils.gsch2pcb_rnd.sch_basename) {
+		char *suff = loc_str_has_suffix(sch, ".sch", 4);
+		if (suff != NULL) {
+			char *tmp = loc_strndup(sch, suff - sch);
+			conf_set(CFR_CLI, "utils/gsch2pcb_rnd/sch_basename", -1, tmp, POL_OVERWRITE);
+			free(tmp);
+		}
+	}
+}
+
+static void add_multiple_schematics(const char * sch)
+{
+	/* parse the string using shell semantics */
+	int count;
+	char **args;
+	char *tmp = pcb_strdup(sch);
+
+	count = qparse(tmp, &args);
+	free(tmp);
+	if (count > 0) {
+		int i;
+		for (i = 0; i < count; ++i)
+			if (*args[i] != '\0')
+				add_schematic(args[i]);
+		qparse_free(count, &args);
+	}
+	else {
+		fprintf(stderr, "invalid `schematics' option: %s\n", sch);
+	}
+}
+
+static int parse_config(char * config, char * arg)
+{
+	char *s;
+
+	/* remove trailing white space otherwise strange things can happen */
+	if ((arg != NULL) && (strlen(arg) >= 1)) {
+		s = arg + strlen(arg) - 1;
+		while ((*s == ' ' || *s == '\t') && (s != arg))
+			s--;
+		s++;
+		*s = '\0';
+	}
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		printf("    %s \"%s\"\n", config, arg ? arg : "");
+
+	if (!strcmp(config, "remove-unfound") || !strcmp(config, "r")) {
+		/* This is default behavior set in header section */
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/remove_unfound_elements", -1, "1", POL_OVERWRITE);
+		return 0;
+	}
+	if (!strcmp(config, "keep-unfound") || !strcmp(config, "k")) {
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/remove_unfound_elements", -1, "0", POL_OVERWRITE);
+		return 0;
+	}
+	if (!strcmp(config, "quiet") || !strcmp(config, "q")) {
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/quiet_mode", -1, "1", POL_OVERWRITE);
+		return 0;
+	}
+	if (!strcmp(config, "preserve") || !strcmp(config, "p")) {
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/preserve", -1, "1", POL_OVERWRITE);
+		return 0;
+	}
+	if (!strcmp(config, "elements-shell") || !strcmp(config, "s"))
+		conf_set(CFR_CLI, "rc/library_shell", -1, arg, POL_OVERWRITE);
+	else if (!strcmp(config, "elements-dir") || !strcmp(config, "d")) {
+		static int warned = 0;
+		if (!warned) {
+			fprintf(stderr, "WARNING: using elements-dir from %s - this overrides the normal pcb-rnd configured library search paths\n", config);
+			warned = 1;
+		}
+		conf_set(CFR_CLI, "rc/library_search_paths", -1, arg, POL_PREPEND);
+	}
+	else if (!strcmp(config, "output-name") || !strcmp(config, "o"))
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/sch_base", -1, arg, POL_OVERWRITE);
+	else if (!strcmp(config, "default-pcb") || !strcmp(config, "P"))
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/default_pcb", -1, arg, POL_OVERWRITE);
+	else if (!strcmp(config, "schematics"))
+		add_multiple_schematics(arg);
+	else if (!strcmp(config, "gnetlist")) {
+		char **n;
+		n = gadl_new(&extra_gnetlist_list);
+		*n = pcb_strdup(arg);
+		gadl_append(&extra_gnetlist_list, n);
+	}
+	else if (!strcmp(config, "empty-footprint"))
+		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/empty_footprint_name", -1, arg, POL_OVERWRITE);
+	else
+		return -1;
+
+	return 1;
+}
+
+static void load_project(char * path)
+{
+	FILE *f;
+	char *s, buf[1024], config[32], arg[768];
+
+	f = fopen(path, "r");
+	if (!f)
+		return;
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		printf("Reading project file: %s\n", path);
+	while (fgets(buf, sizeof(buf), f)) {
+		for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
+		if (!*s || *s == '#' || *s == '/' || *s == ';')
+			continue;
+		arg[0] = '\0';
+		sscanf(s, "%31s %767[^\n]", config, arg);
+		parse_config(config, arg);
+	}
+	fclose(f);
+}
+
+static void load_extra_project_files(void)
+{
+	char *path;
+	static int done = FALSE;
+
+	if (done)
+		return;
+
+	load_project("/etc/gsch2pcb");
+	load_project("/usr/local/etc/gsch2pcb");
+
+	path = str_concat(PCB_DIR_SEPARATOR_S, conf_core.rc.path.home, ".gEDA", "gsch2pcb", NULL);
+	load_project(path);
+	free(path);
+
+	done = TRUE;
+}
+
+static void get_args(int argc, char ** argv)
+{
+	char *opt, *arg;
+	int i, r;
+
+	for (i = 1; i < argc; ++i) {
+		opt = argv[i];
+		arg = argv[i + 1];
+		if (*opt == '-') {
+			++opt;
+			if (*opt == '-')
+				++opt;
+			if (!strcmp(opt, "version") || !strcmp(opt, "V")) {
+				printf("gsch2pcb-rnd %s\n", GSCH2PCB_RND_VERSION);
+				exit(0);
+			}
+			else if (!strcmp(opt, "verbose") || !strcmp(opt, "v")) {
+				char tmp[16];
+				sprintf(tmp, "%ld", conf_g2pr.utils.gsch2pcb_rnd.verbose + 1);
+				conf_set(CFR_CLI, "utils/gsch2pcb_rnd/verbose", -1, tmp, POL_OVERWRITE);
+				continue;
+			}
+			else if (!strcmp(opt, "c") || !strcmp(opt, "conf")) {
+				const char *stmp;
+				if (conf_set_from_cli(NULL, arg, NULL, &stmp) != 0) {
+					fprintf(stderr, "Error: failed to set config %s: %s\n", arg, stmp);
+					exit(1);
+				}
+				i++;
+				continue;
+			}
+			else if (!strcmp(opt, "fix-elements")) {
+				conf_set(CFR_CLI, "utils/gsch2pcb_rnd/fix_elements", -1, "1", POL_OVERWRITE);
+				continue;
+			}
+			else if (!strcmp(opt, "gnetlist-arg")) {
+				char **n;
+				n = gadl_new(&extra_gnetlist_arg_list);
+				*n = pcb_strdup(arg);
+				gadl_append(&extra_gnetlist_arg_list, n);
+				i++;
+				continue;
+			}
+			else if (!strcmp(opt, "help") || !strcmp(opt, "h"))
+				usage();
+			else if (i < argc && ((r = parse_config(opt, (i < argc - 1) ? arg : NULL))
+														>= 0)
+				) {
+				i += r;
+				continue;
+			}
+			printf("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
+			usage();
+		}
+		else {
+			if (loc_str_has_suffix(argv[i], ".sch", 4) == NULL) {
+				load_extra_project_files();
+				load_project(argv[i]);
+			}
+			else
+				add_schematic(argv[i]);
+		}
+	}
+}
+
+void plugin_register(const char *name, const char *path, void *handle, int dynamic_loaded, void (*uninit)(void))
+{
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		printf("Plugin loaded: %s (%s)\n", name, path);
+}
+
+void free_strlist(gadl_list_t *lst)
+{
+	char **s;
+
+	while((s = gadl_first(lst)) != NULL) {
+		char *str = *s;
+		gadl_free(s);
+		free(str);
+	}
+}
+
+#include "fp_init.h"
+
+/************************ main ***********************/
+char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name;
+int main(int argc, char ** argv)
+{
+	int i;
+	int initial_pcb = TRUE;
+	int created_pcb_file = TRUE;
+
+	if (argc < 2)
+		usage();
+
+
+	conf_init();
+	conf_core_init();
+
+
+	gadl_list_init(&schematics, sizeof(char *), NULL, NULL);
+	gadl_list_init(&extra_gnetlist_arg_list, sizeof(char *), NULL, NULL);
+	gadl_list_init(&extra_gnetlist_list, sizeof(char *), NULL, NULL);
+
+#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
+	conf_reg_field(conf_g2pr, field,isarray,type_name,cpath,cname,desc,flags);
+#include "gsch2pcb_rnd_conf_fields.h"
+
+	get_args(argc, argv);
+
+	conf_load_all(NULL, NULL);
+	conf_update(NULL);
+
+	{
+		pcb_uninit_t uninit_func;
+#		include "fp_init.c"
+	}
+
+	load_extra_project_files();
+
+	conf_update(NULL); /* because of CLI changes */
+
+	fp_init();
+
+	element_search_path = fp_default_search_path();
+
+	if (gadl_length(&schematics) == 0)
+		usage();
+
+	pins_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL);
+	net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL);
+	pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL);
+
+	conf_load_project(NULL, pcb_file_name);
+	conf_update(NULL); /* because of the project file */
+
+	{ /* set bak_file_name, finding the first number that results in a non-existing bak */
+		int len;
+		char *end;
+
+		len = strlen(conf_g2pr.utils.gsch2pcb_rnd.sch_basename);
+		bak_file_name = malloc(len+8+64); /* make room for ".pcb.bak" and an integer */
+		memcpy(bak_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, len);
+		end = bak_file_name + len;
+		strcpy(end, ".pcb.bak");
+		end += 8;
+
+		for (i = 0; file_exists(bak_file_name); ++i)
+			sprintf(end, "%d", i);
+	}
+
+	if (file_exists(pcb_file_name)) {
+		initial_pcb = FALSE;
+		pcb_new_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".new.pcb", NULL);
+		get_pcb_element_list(pcb_file_name);
+	}
+	else
+		pcb_new_file_name = pcb_strdup(pcb_file_name);
+
+	if (!run_gnetlist(pins_file_name, net_file_name, pcb_new_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, &schematics)) {
+		fprintf(stderr, "Failed to run gnetlist\n");
+		exit(1);
+	}
+
+	if (add_elements(pcb_new_file_name) == 0) {
+		build_and_run_command("rm %s", pcb_new_file_name);
+		if (initial_pcb) {
+			printf("No elements found, so nothing to do.\n");
+			exit(0);
+		}
+	}
+
+	if (conf_g2pr.utils.gsch2pcb_rnd.fix_elements)
+		update_element_descriptions(pcb_file_name, bak_file_name);
+	prune_elements(pcb_file_name, bak_file_name);
+
+	/* Report work done during processing */
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		printf("\n");
+	printf("\n----------------------------------\n");
+	printf("Done processing.  Work performed:\n");
+	if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
+		printf("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
+	if (pcb_element_list.length && n_deleted > 0)
+		printf("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
+
+	if (n_added_ef > 0)
+		printf("%d file elements added to %s.\n", n_added_ef, pcb_new_file_name);
+	else if (n_not_found == 0) {
+		printf("No elements to add so not creating %s\n", pcb_new_file_name);
+		created_pcb_file = FALSE;
+	}
+
+	if (n_not_found > 0) {
+		printf("%d not found elements added to %s.\n", n_not_found, pcb_new_file_name);
+	}
+	if (n_unknown > 0)
+		printf("%d components had no footprint attribute and are omitted.\n", n_unknown);
+	if (n_none > 0)
+		printf("%d components with footprint \"none\" omitted from %s.\n", n_none, pcb_new_file_name);
+	if (n_empty > 0)
+		printf("%d components with empty footprint \"%s\" omitted from %s.\n", n_empty, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name, pcb_new_file_name);
+	if (n_changed_value > 0)
+		printf("%d elements had a value change in %s.\n", n_changed_value, pcb_file_name);
+	if (n_fixed > 0)
+		printf("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
+	if (n_PKG_removed_old > 0) {
+		printf("%d elements could not be found.", n_PKG_removed_old);
+		if (created_pcb_file)
+			printf("  So %s is incomplete.\n", pcb_file_name);
+		else
+			printf("\n");
+	}
+	if (n_PKG_removed_new > 0) {
+		printf("%d elements could not be found.", n_PKG_removed_new);
+		if (created_pcb_file)
+			printf("  So %s is incomplete.\n", pcb_new_file_name);
+		else
+			printf("\n");
+	}
+	if (n_preserved > 0)
+		printf("%d elements not in the schematic preserved in %s.\n", n_preserved, pcb_file_name);
+
+	/* Tell user what to do next */
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		printf("\n");
+
+	if (n_added_ef > 0)
+		next_steps(initial_pcb, conf_g2pr.utils.gsch2pcb_rnd.quiet_mode);
+
+	free(net_file_name);
+	free(pins_file_name);
+	free(pcb_file_name);
+	free(bak_file_name);
+
+	conf_uninit();
+
+	free_strlist(&schematics);
+	free_strlist(&extra_gnetlist_arg_list);
+	free_strlist(&extra_gnetlist_list);
+
+	if (pcb_new_file_name != NULL)
+		free(pcb_new_file_name);
+
+	return 0;
+}
diff --git a/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h b/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h
new file mode 100644
index 0000000..3854424
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h
@@ -0,0 +1,22 @@
+#ifndef PCB_MINCUT_CONF_H
+#define PCB_MINCUT_CONF_H
+
+#include "../src/conf.h"
+
+typedef struct {
+	const struct utils {
+		const struct gsch2pcb_rnd {
+			CFT_BOOLEAN remove_unfound_elements; /*  = TRUE */
+			CFT_BOOLEAN quiet_mode; /*  = FALSE */
+			CFT_INTEGER verbose;
+			CFT_BOOLEAN preserve;
+			CFT_BOOLEAN fix_elements;
+			CFT_STRING sch_basename;
+			CFT_STRING default_pcb;            /* override default pcb with a given file */
+			CFT_STRING empty_footprint_name;
+		} gsch2pcb_rnd;
+	} utils;
+} conf_gsch2pcb_rnd_t;
+
+extern conf_gsch2pcb_rnd_t conf_g2pr;
+#endif
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/Makefile b/util/gsch2pcb-rnd/gsch2pcb_scm/Makefile
new file mode 100644
index 0000000..effaf11
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/Makefile
@@ -0,0 +1,17 @@
+names = c1 c2 c3
+schematics = $(names:%=%.sch)
+netlists   = $(names:%=%.net)
+goldens    = $(names:%=golden/%.net)
+diffs      = $(names:%=%.diff)
+
+.PHONY: all clean $(netlists)
+all: $(diffs)
+
+$(diffs): %.diff: %.net golden/%.net
+	diff $^ > $@
+
+$(netlists): %.net: %.sch
+	gnetlist -g gsch2pcb-rnd -o $@ -m ../gnet-gsch2pcb-rnd.scm $<
+
+clean: 
+	-rm -rf $(diffs) $(netlists)
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/c1.sch b/util/gsch2pcb-rnd/gsch2pcb_scm/c1.sch
new file mode 100644
index 0000000..e19af07
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/c1.sch
@@ -0,0 +1,13 @@
+v 20130925 2
+C 18400 56000 1 0 0 connector3-2.sym
+{
+T 19100 57700 5 10 1 1 0 6 1
+refdes=CONN1
+T 18700 57650 5 10 0 0 0 0 1
+device=CONNECTOR_3
+T 18700 57850 5 10 0 0 0 0 1
+footprint=CONNECTOR 3 1
+}
+N 18400 57200 17800 57200 4
+N 17800 56400 17800 57200 4
+N 17800 56400 18400 56400 4
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/c2.sch b/util/gsch2pcb-rnd/gsch2pcb_scm/c2.sch
new file mode 100644
index 0000000..dcbb82c
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/c2.sch
@@ -0,0 +1,15 @@
+v 20130925 2
+C 18400 56000 1 0 0 connector3-2.sym
+{
+T 19100 57700 5 10 1 1 0 6 1
+refdes=CONN1
+T 18700 57650 5 10 0 0 0 0 1
+device=CONNECTOR_3
+T 18700 57850 5 10 0 0 0 0 1
+footprint=CONNECTOR 3 1
+T 19300 56200 5 10 1 0 90 0 1
+value=val123
+}
+N 18400 57200 17800 57200 4
+N 17800 56400 17800 57200 4
+N 17800 56400 18400 56400 4
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/c3.sch b/util/gsch2pcb-rnd/gsch2pcb_scm/c3.sch
new file mode 100644
index 0000000..9140516
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/c3.sch
@@ -0,0 +1,15 @@
+v 20130925 2
+C 18400 56000 1 0 0 connector3-2.sym
+{
+T 19100 57700 5 10 1 1 0 6 1
+refdes=CONN1
+T 18700 57650 5 10 0 0 0 0 1
+device=CONNECTOR_3
+T 18700 57850 5 10 0 0 0 0 1
+footprint=CONNECTOR(3, 1, silkmark=external)
+T 19300 56200 5 10 1 0 90 0 1
+value=val123
+}
+N 18400 57200 17800 57200 4
+N 17800 56400 17800 57200 4
+N 17800 56400 18400 56400 4
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c1.net b/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c1.net
new file mode 100644
index 0000000..ce926e8
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c1.net
@@ -0,0 +1 @@
+PKG(CONNECTOR 3 1,CONN1,unknown)
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c2.net b/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c2.net
new file mode 100644
index 0000000..92bad88
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c2.net
@@ -0,0 +1 @@
+PKG(CONNECTOR 3 1,CONN1,val123)
diff --git a/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c3.net b/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c3.net
new file mode 100644
index 0000000..a62b563
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb_scm/golden/c3.net
@@ -0,0 +1 @@
+PKG(CONNECTOR(3, 1, silkmark=external),CONN1,val123)
diff --git a/util/gsch2pcb-rnd/help.c b/util/gsch2pcb-rnd/help.c
new file mode 100644
index 0000000..97c1092
--- /dev/null
+++ b/util/gsch2pcb-rnd/help.c
@@ -0,0 +1,148 @@
+/* gsch2pcb-rnd
+ *
+ *  Original version: Bill Wilson    billw at wt.net
+ *  rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software which I release under the GNU General Public
+ *  License. You may redistribute and/or modify this program under the terms
+ *  of that license as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.  Version 2 is in the
+ *  COPYRIGHT file in the top level directory of this distribution.
+ *
+ *  To get a copy of the GNU General Puplic License, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ * Retrieved from the official git (2015.07.15)
+
+ Behavior different from the original:
+  - use getenv() instead of g_getenv(): on windows this won't do recursive variable expansion
+  - use rnd-specific .scm
+  - use popen() instead of glib's spawn (stderr is always printed to stderr)
+ */
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+extern char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name;
+
+static char *usage_string0 =
+	"usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
+	"\n"
+	"Generate a PCB layout file from a set of gschem schematics.\n"
+	"   gnetlist -g PCB is run to generate foo.net from the schematics.\n" "\n"
+/* TODO */
+/*  "   gnetlist -g gsch2pcb is run to get PCB elements which\n"
+  "   match schematic footprints.  For schematic footprints which don't match\n"
+  "   any PCB layout elements, search a set of file element directories in\n"
+  "   an attempt to find matching PCB file elements.\n"*/
+	"   Output to foo.pcb if it doesn't exist.  If there is a current foo.pcb,\n"
+	"   output only new elements to foo.new.pcb.\n"
+	"   If any elements with a non-empty element name in the current foo.pcb\n"
+	"   have no matching schematic component, then remove those elements from\n"
+	"   foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
+	"\n"
+	"   gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
+	"   of the pins in a .pcb file to match pin names from the schematic.\n"
+	"\n"
+	"   \"project\" is a file (not ending in .sch) containing a list of\n"
+	"   schematics to process and some options.  A schematics line is like:\n"
+	"       schematics foo1.sch foo2.sch ...\n"
+	"   Options in a project file are like command line args without the \"-\":\n"
+	"       output-name myproject\n"
+	"\n"
+	"options (may be included in a project file):\n"
+	"   -d, --elements-dir D    Search D for PCB file elements.  These defaults\n"
+	"                           are searched if they exist. See the default\n"
+	"                           search paths at the end of this text."
+	"   -c, --elements-dir-clr  Clear the elements dir. Useful before a series\n"
+	"                           if -d's to flush defaults.\n"
+	"   -s, --elements-shell S  Use S as a prefix for running parametric footrint\n"
+	"                           generators. It is useful on systems where popen()\n"
+	"                           doesn't do the right thing or the process should\n"
+	"                           be wrapped. Example -s \"/bin/sh -c\"\n"
+	"   -P, --default-pcb       Change the default PCB file's name; this file is\n"
+	"                           inserted on top of the *.new.pcb generated, for\n"
+	"                           PCB default settings\n"
+	"   -o, --output-name N     Use output file names N.net, N.pcb, and N.new.pcb\n"
+	"                           instead of foo.net, ... where foo is the basename\n"
+	"                           of the first command line .sch file.\n"
+	"   -r, --remove-unfound    Don't include references to unfound elements in\n"
+	"                           the generated .pcb files.  Use if you want PCB to\n"
+	"                           be able to load the (incomplete) .pcb file.\n"
+	"                           This is the default behavior.\n"
+	"   -k, --keep-unfound      Keep include references to unfound elements in\n"
+	"                           the generated .pcb files.  Use if you want to hand\n"
+	"                           edit or otherwise preprocess the generated .pcb file\n"
+	"                           before running pcb.\n"
+	"   -p, --preserve          Preserve elements in PCB files which are not found\n"
+	"                           in the schematics.  Note that elements with an empty\n"
+	"                           element name (schematic refdes) are never deleted,\n"
+	"                           so you really shouldn't need this option.\n"
+	"   -q, --quiet             Don't tell the user what to do next after running\n"
+	"                           gsch2pcb-rnd.\n" "\n";
+
+static char *usage_string1 =
+	"   --gnetlist backend    A convenience run of extra gnetlist -g commands.\n"
+	"                         Example:  gnetlist partslist3\n"
+	"                         Creates:  myproject.partslist3\n"
+	" --empty-footprint name  See the project.sample file.\n"
+	"\n"
+	"options (not recognized in a project file):\n"
+	"   --gnetlist-arg arg    Allows additional arguments to be passed to gnetlist.\n"
+	"       --fix-elements    If a schematic component footprint is not equal\n"
+	"                         to its PCB element Description, update the\n"
+	"                         Description instead of replacing the element.\n"
+	"                         Do this the first time gsch2pcb is used with\n"
+	"                         PCB files originally created with gschem2pcb.\n"
+	"   -v, --verbose         Use -v -v for additional file element debugging.\n"
+	"   -V, --version\n\n"
+	"environment variables:\n"
+	"   GNETLIST              If set, this specifies the name of the gnetlist program\n"
+	"                         to execute.\n"
+	"\n"
+	"Additional Resources:\n"
+	"\n"
+	"  gnetlist user guide:  http://wiki.geda-project.org/geda:gnetlist_ug\n"
+	"  gEDA homepage:        http://www.geda-project.org\n"
+	"  PCB homepage:         http://pcb.geda-project.org\n" "  pcb-rnd homepage:     http://repo.hu/projects/pcb-rnd\n" "\n";
+
+
+void usage(void)
+{
+	puts(usage_string0);
+	puts(usage_string1);
+	exit(0);
+}
+
+void next_steps(int initial_pcb, int quiet_mode)
+{
+	if (initial_pcb) {
+		printf("\nNext step:\n");
+		printf("1.  Run pcb on your file %s.\n", pcb_file_name);
+		printf("    You will find all your footprints in a bundle ready for you to place\n");
+		printf("    or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
+		printf("2.  From within PCB, select \"File -> Load netlist file\" and select \n");
+		printf("    %s to load the netlist.\n\n", net_file_name);
+		printf("3.  From within PCB, enter\n\n");
+		printf("           :ExecuteFile(%s)\n\n", pins_file_name);
+		printf("    to propagate the pin names of all footprints to the layout.\n\n");
+	}
+	else if (!quiet_mode) {
+		printf("\nNext steps:\n");
+		printf("1.  Run pcb on your file %s.\n", pcb_file_name);
+		printf("2.  From within PCB, select \"File -> Load layout data to paste buffer\"\n");
+		printf("    and select %s to load the new footprints into your existing layout.\n", pcb_new_file_name);
+		printf("3.  From within PCB, select \"File -> Load netlist file\" and select \n");
+		printf("    %s to load the updated netlist.\n\n", net_file_name);
+		printf("4.  From within PCB, enter\n\n");
+		printf("           :ExecuteFile(%s)\n\n", pins_file_name);
+		printf("    to update the pin names of all footprints.\n\n");
+	}
+}
diff --git a/util/gsch2pcb-rnd/help.h b/util/gsch2pcb-rnd/help.h
new file mode 100644
index 0000000..d9039c4
--- /dev/null
+++ b/util/gsch2pcb-rnd/help.h
@@ -0,0 +1,2 @@
+void usage(void);
+void next_steps(int initial_pcb, int quiet_mode);
diff --git a/util/keylist.sh b/util/keylist.sh
new file mode 100755
index 0000000..985f17e
--- /dev/null
+++ b/util/keylist.sh
@@ -0,0 +1,227 @@
+#!/bin/sh
+#   keylist - list hotkey->actions found in .lht files in a html table, per HID
+#   Copyright (C) 2015..2016 Tibor 'Igor2' Palinkas
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program; if not, write to the Free Software Foundation, Inc.,
+#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#   http://repo.hu/projects/pcb-rnd
+
+AWK=awk
+
+export LANG=C
+
+need_lht()
+{
+	echo "lhtflat not found. Please install lihata to run this script. The" >&2
+	echo "svn extern in src_3rd/ is not enough, this script requires a full" >&2
+	echo "installation from svn://repo.hu/lihata/trunk" >&2
+	exit 1
+}
+
+# make sure lhtflat is installed
+echo "" | lhtflat || need_lht
+
+
+if test -z "$*"
+then
+	echo ""
+	echo "$0: Generate a html table from pcb menu lht files."
+	echo "Usage: $0 file1 [file2 [file3 ... [fileN]]]"
+	echo ""
+	exit
+else
+	res_files="$@"
+fi
+
+extract_from_lht()
+{
+	lhtflat | $AWK -F '[\t]' -v "fn=$1" '
+
+#data	text	//main_menu/1::Edit/submenu/11::Edit name of/submenu/1::pin on layout/a	Shift Ctrl<Key>n
+#data	text	//main_menu/1::Edit/submenu/11::Edit name of/submenu/1::pin on layout/action	ChangeName(Object, Number) 
+
+	{
+		tmp = $3
+		if ($3 ~ "/a/[0-9]*::$") {
+			# li:a = {}
+			sub("/[0-9]*::$", "", tmp)
+		}
+		parent=tmp
+		sub("/[^/]*$","", parent)
+		node=tmp
+		sub("^.*/","", node)
+	}
+
+
+	(($1 == "data") && ($2 == "text")) {
+		# simple text node: accel key
+		if (node == "a") {
+			seq=$0
+			sub("[^\t]*[\t]*[^\t]*[\t]*[^\t]*[\t]*", "", seq)
+			gsub(" ", "", seq)
+			v = split(tolower(seq), S, "[;]")
+			ktmp = ""
+			for(n = 1; n <= v; n++) {
+				split(S[n], K, "<key>")
+				if (K[1] != "") {
+					mods = ""
+					if (K[1] ~ "alt")   mods = mods "-alt"
+					if (K[1] ~ "ctrl")  mods = mods "-ctrl"
+					if (K[1] ~ "shift") mods = mods "-shift"
+				}
+				else
+					mods = ""
+				if (ktmp == "")
+					ktmp = K[2] mods
+				else
+					ktmp = ktmp ";" K[2] mods
+			}
+			if (KEY[parent] == "")
+				KEY[parent] = ktmp
+			else
+				KEY[parent] = KEY[parent] SUBSEP ktmp
+		}
+
+		# simple text node: action
+		if (node == "action")
+			ACTION[parent] = $4
+
+		# list item: action
+		if ($3 ~ "/action/[0-9]+::$") {
+			parent = $3
+			sub("/action/[^/]*$", "", parent)
+			if (ACTION[parent] != "")
+				ACTION[parent] = ACTION[parent] ";" $4
+			else
+				ACTION[parent] = $4
+		}
+	}
+
+	END {
+		for(n in KEY) {
+			menuname = n
+			sub(".*::", "", menuname)
+			v = split(KEY[n], K, "[" SUBSEP "]")
+			for(i = 1; i <= v; i++)
+				print K[i] "\t" fn "\t" ACTION[n] "\t" menuname
+		}
+	}
+	'
+}
+
+# convert a "key src action" to a html table with rowspans for base keys
+gen_html()
+{
+	$AWK -F '[\t]' '
+	BEGIN {
+		CLR[0] = "#FFFFFF"
+		CLR[1] = "#DDFFFF"
+		key_combos = 0
+	}
+	function to_base_key(combo)
+	{
+		sub("-.*", "", combo)
+		return combo
+	}
+
+	function to_mods(combo)
+	{
+		if (!(combo ~ ".-"))
+			return ""
+		sub("^[^-]*[-]", "", combo)
+		return combo
+	}
+
+	{
+		k = $1
+		if (last != k) {
+			LIST[key_combos++] = k
+#			ROWSPAN[to_base_key(k)]++
+		}
+		ACTION[$2, k] = $3
+		MENUNAME[$2, k] = $4
+		HIDS[$2]++
+		last = k
+
+		v = split(k, K, ";")
+		p = ""
+		for(n = 1; n <= v; n++) {
+			p = p K[n] ";"
+			if (($2, p) in PREFIX) {
+				err = err "<br>" $2 ": " k " vs. " p
+				ERR[$2, p]++
+				ERR[$2, k]++
+			}
+		}
+		p = k ";"
+		PREFIX[$2, p]++
+	}
+
+	END {
+		print "<html><body>"
+		print "<h1> Key to action bindings </h1>"
+		print "<table border=1 cellspacing=0>"
+		printf("<tr><th> key")
+		colspan = 2
+		for(h in HIDS) {
+			printf(" <th>%s", h)
+			colspan++
+		}
+		print ""
+		for(n = 0; n < key_combos; n++) {
+			clr_cnt++
+			key = LIST[n]
+			print "<tr bgcolor=" CLR[clr_cnt % 2] ">"
+
+			kv = split(key, K, ";")
+			keystr = ""
+			ind=""
+			for(kn = 1; kn <= kv; kn++) {
+				if (kn > 1)
+					keystr = keystr "<br>"
+				keystr = keystr ind K[kn]
+				ind = ind " "
+			}
+			
+
+			print "	<th align=left>" keystr
+			for(h in HIDS) {
+				mn = MENUNAME[h, key]
+				act = ACTION[h, key]
+				if ((act == "") && (mn == ""))
+					act = " "
+				else {
+					gsub(");", "); ", act)
+					if (mn != "")
+						act = "<I>" mn "</I>" "<br>" act
+				}
+				print "	<td>", act
+				if ((h, key) in ERR)
+					print "<br> <b>Error: key prefix collision</b>"
+			}
+			last_base = base
+		}
+		print "</table>"
+		print err
+		print "</body></html>"
+	}
+	'
+}
+
+for n in $res_files 
+do
+	extract_from_lht "`basename $n`" < $n
+done | sort | gen_html
+
diff --git a/util/keylist_old_res.sh b/util/keylist_old_res.sh
new file mode 100755
index 0000000..887a46a
--- /dev/null
+++ b/util/keylist_old_res.sh
@@ -0,0 +1,263 @@
+#!/bin/sh
+#   keylist - list hotkey->actions found in .res files in a html table, per HID
+#   Copyright (C) 2015 Tibor 'Igor2' Palinkas
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program; if not, write to the Free Software Foundation, Inc.,
+#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#   http://repo.hu/projects/pcb-rnd
+
+
+# WARNING this is the old version of the script that works from .res files.
+# It's kept here for compatibility. pcb-rnd has switched from res to lihata,
+# the new version of the script works from lihata files.
+
+AWK=awk
+
+export LANG=C
+
+if test -z "$*"
+then
+	echo ""
+	echo "$0: Generate a html table from pcb menu res files."
+	echo "Usage: $0 file1 [file2 [file3 ... [fileN]]]"
+	echo ""
+	exit
+else
+	res_files="$@"
+fi
+
+# split the file into one token per line using a state machine
+# tokens are:
+#  - quoted strings ("")
+#  - control characters: {,  }, =
+#  - other words
+tokenize()
+{
+	$AWK '
+		BEGIN {
+			q = "\""
+		}
+
+# echo a character and remember if it was a newline
+		function echo(c) {
+			had_nl = (c == "\n")
+			printf "%s", c
+		}
+
+# echo a newline if the previous character written was not a newline
+		function nl() {
+			if (!had_nl)
+				echo("\n")
+		}
+
+# parse state machine: eats input character by character
+		function parse(c) {
+#			ignore comments
+			if (in_comment)
+				return
+
+#			quote state machine
+			if (in_quote) {
+				if (bslash) {
+					echo(c)
+					bslash = 0
+					return
+				}
+				if (c == "\\")
+					bslash = 1
+				if (c == q) {
+					nl();
+					in_quote = 0
+					return
+				}
+				echo(c)
+				return
+			}
+
+			if (c == "#") {
+				in_comment = 1
+				return
+			}
+
+#			quote start
+			if (c == q) {
+				in_quote = 1
+				echo(c)
+				return
+			}
+
+#			whitespace are non-redundant newlines
+			if (c ~ "[ \t]") {
+				nl()
+				return
+			}
+
+#			"keywords"
+			if (c ~ "[{}=]") {
+				nl()
+				echo(c)
+				nl()
+				return
+			}
+
+#			anything else
+			echo(c)
+		}
+
+# each new line of the input is a set of characters
+# reset comment first, as it spanned to the previous newline
+		{
+			in_comment = 0
+			l = length($0)
+			for(n = 1; n <= l; n++)
+				parse(substr($0, n, 1))
+		}
+	'
+}
+
+# "grammar": read tokens and output "key src action" where
+#   key is in base-modified-modifier format, modifiers ordered
+#   src is the source res file we are working from, passed as $1
+#   action is the action given right after the key or before the key
+extract_from_res()
+{
+	tokenize | $AWK -v "src=$1" '
+		BEGIN {
+			sub(".*/", "", src)
+		}
+		/^=$/ {
+			if (last != "a") {
+				last = ""
+				getline t
+				next
+			}
+			last = ""
+
+			getline t
+			if (t != "{")
+				next
+			getline k1
+			getline k2
+			getline t
+			if (t != "}")
+				next
+			getline action
+			if (action == "}")
+				action = last_act
+			sub("^\"", "", k2)
+			sub("\"$", "", k2)
+			gsub(" \t", "", k2)
+			split(tolower(k2), K, "<key>")
+			if (K[1] != "") {
+				mods = ""
+				if (K[1] ~ "alt")   mods = mods "-alt"
+				if (K[1] ~ "ctrl")  mods = mods "-ctrl"
+				if (K[1] ~ "shift") mods = mods "-shift"
+			}
+			else
+				mods = ""
+			key = K[2] mods
+			gsub("[ \t]", "", key)
+			gsub("[ \t]", "", src)
+			gsub("[ \t]", "", action)
+			print key, src, action
+			last_act = ""
+			next
+		}
+		/[a-zA-Z]/ {
+			if (last != "")
+				last_act = last
+		}
+		{ last = $0 }
+	'
+}
+
+# convert a "key src action" to a html table with rowspans for base keys
+gen_html()
+{
+	$AWK '
+	BEGIN {
+		HIDNAMES["gpcb-menu.res"] = "gtk"
+		HIDNAMES["pcb-menu.res"]  = "lesstif"
+		CLR[0] = "#FFFFFF"
+		CLR[1] = "#DDFFFF"
+		key_combos = 0
+	}
+	function to_base_key(combo)
+	{
+		sub("-.*", "", combo)
+		return combo
+	}
+
+	function to_mods(combo)
+	{
+		if (!(combo ~ ".-"))
+			return ""
+		sub("^[^-]*[-]", "", combo)
+		return combo
+	}
+
+	{
+		if (last != $1) {
+			LIST[key_combos++] = $1
+			ROWSPAN[to_base_key($1)]++
+		}
+		ACTION[$2, $1] = $3
+		HIDS[$2]++
+		last = $1
+	}
+
+	END {
+		print "<html><body>"
+		print "<h1> Key to action bindings </h1>"
+		print "<table border=1 cellspacing=0>"
+		printf("<tr><th> key <th> modifiers")
+		colspan = 2
+		for(h in HIDS) {
+			printf(" <th>%s<br>%s", h, HIDNAMES[h])
+			colspan++
+		}
+		print ""
+		for(n = 0; n < key_combos; n++) {
+			key = LIST[n]
+			base = to_base_key(key)
+			mods = to_mods(key)
+			if (base != last_base)
+				clr_cnt++
+			print "<tr bgcolor=" CLR[clr_cnt % 2] ">"
+			if (base != last_base)
+				print "	<th rowspan=" ROWSPAN[base] ">" base
+			if (mods == "")
+				mods = " "
+			print "	<td>" mods
+			for(h in HIDS) {
+				act = ACTION[h, key]
+				if (act == "")
+					act = " "
+				print "	<td>", act
+			}
+			last_base = base
+		}
+		print "</table>"
+		print "</body></html>"
+	}
+	'
+}
+
+for n in $res_files 
+do
+	extract_from_res "$n" < $n
+done | sort | gen_html
+
diff --git a/util/pcb-strip b/util/pcb-strip
new file mode 100755
index 0000000..2f7f3d6
--- /dev/null
+++ b/util/pcb-strip
@@ -0,0 +1,87 @@
+#!/bin/sh
+#   pcb-strip - remove sections of a .pcb file
+#   Copyright (C) 2016 Tibor 'Igor2' Palinkas
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program; if not, write to the Free Software Foundation, Inc.,
+#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#   http://repo.hu/projects/pcb-rnd
+
+pcb_strip()
+{
+	awk -v "strip_sym=$strip_sym" -v "strip_attr=$strip_attr" '
+		BEGIN {
+			symst = 0
+		}
+
+		strip_attr && /^[ \t]*Attribute/ { next }
+
+		strip_sym && (symst == 0) && /^[ \t]*Symbol/ { symst = 1 }
+		strip_sym && (symst == 1) && /^[ \t]*[(]/ { symst = 2 }
+		strip_sym && (symst == 2) && /^[ \t]*[)]/ { symst = 0; next }
+
+		(symst == 0) { print $0 }
+	'
+}
+
+help()
+{
+	echo "pcb-strip - remove sections of a pcb file"
+	echo "Invocation:"
+	echo "     $0 [-a] [-s] <foo.pcb >bar.pcb"
+	echo " or  $0 [-a] [-s] foo.pcb"
+	echo ""
+	echo "Switches:"
+	echo " -a       remove Attributes"
+	echo " -s       remove symbols"
+	echo ""
+}
+
+strip_sym=0
+strip_attr=0
+fn=""
+while test $# -gt 0
+do
+	case "$1"
+	in
+		--help) help "$0"; exit 0;;
+		-s) strip_sym=1;;
+		-a) strip_attr=1;;
+		-*) echo "unknown switch $1; try --help" >&2; exit 1;;
+		*)
+			if test -z "$fn"
+			then
+				fn="$1"
+			else
+				echo "unknown switch $1; try --help" >&2
+				exit
+			fi
+			;;
+	esac
+	shift 1
+done
+
+if test -z "$fn"
+then
+# operate on stdio
+	pcb_strip
+else
+# use a file by name
+	tmp=`mktemp`
+	if test -z "$tmp"
+	then
+		tmp="$fn.strip"
+	fi
+	mv $fn $tmp && pcb_strip < $tmp > $fn && rm $tmp
+fi
diff --git a/util/pcblib-map/Makefile b/util/pcblib-map/Makefile
new file mode 100644
index 0000000..03a3454
--- /dev/null
+++ b/util/pcblib-map/Makefile
@@ -0,0 +1,16 @@
+OUT = map.html conn.html parametric.html smd2.html smd3.html smdN.html \
+      trh2.html trh3.html trhN.html cache
+
+all: $(OUT)
+
+map.html map.png: map.pcb
+	./imgmap_page.sh map
+
+%.html %.png: %.pcb
+	./imgmap_fp.sh $*
+
+cache:
+	./cache.sh > cache
+
+clean:
+	rm $(OUT)
diff --git a/util/pcblib-map/cache.sh b/util/pcblib-map/cache.sh
new file mode 100755
index 0000000..33b783e
--- /dev/null
+++ b/util/pcblib-map/cache.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+digest()
+{
+	local fn bn
+	fn=$1
+
+	bn=${fn#./}
+
+	if test -d "$fn"
+	then
+		echo "D $bn"
+		return
+	fi
+
+	prm=`grep "@@purpose" $fn`
+	if test -z "$prm"
+	then
+		tags=`awk '
+		/^##/ {
+			tag=$0
+			sub("^##","", tag)
+			printf " %s", tag
+		}
+		' < $fn`
+		echo "S $bn $tags"
+	else
+		tags=`awk '
+		/^@@tag/ {
+			tag=$0
+			sub("^@@tag *","", tag)
+			printf " %s", tag
+		}
+		' < $fn`
+		echo "P $bn $tags"
+	fi
+
+}
+
+(
+cd ../../pcblib
+for n in `find .`
+do
+	case $n in
+		*.svn*) ;;
+		.) ;;
+		*)
+			digest $n
+	esac
+done
+)
diff --git a/util/pcblib-map/conn.pcb b/util/pcblib-map/conn.pcb
new file mode 100644
index 0000000..8618dbf
--- /dev/null
+++ b/util/pcblib-map/conn.pcb
@@ -0,0 +1,1342 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 270000 500000]
+
+Grid[10000.0 0 0 1]
+Cursor[0 30000 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[270000 500000 7874 4000 0 3150 "" ""]
+
+Element["" "DSUB connector, female/male" "" "DB15F" 20000 50000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[0 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[0 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[0 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[11200 48600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[11200 59400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[11200 70200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[5600 102600 25000 3000 25600 12500 "C1" "16" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "17" ""]
+	ElementLine [39100 114600 42100 114600 1000]
+	ElementLine [39100 -39000 39100 114600 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 114600 1000]
+	ElementLine [39100 108600 42100 108600 1000]
+	ElementLine [39100 96600 42100 96600 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 91600 39100 91600 2000]
+	ElementLine [28600 -16000 28600 91600 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 91600 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [4000 54000 28600 54000 2000]
+	ElementLine [4000 64800 28600 64800 2000]
+	ElementLine [4000 75600 28600 75600 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+	ElementLine [15200 48600 28600 48600 2000]
+	ElementLine [15200 59400 28600 59400 2000]
+	ElementLine [15200 70200 28600 70200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB15M" 110000 50000 -5000 75600 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[600 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[600 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[600 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[-10600 48600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[-10600 59400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[-10600 70200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "16" ""]
+	Pin[-5000 102600 25000 3000 25600 12500 "C2" "17" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 114600 1000]
+	ElementLine [-38500 114600 -41500 114600 1000]
+	ElementLine [-41500 114600 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 108600 -38500 108600 1000]
+	ElementLine [-41500 96600 -38500 96600 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 91600 2000]
+	ElementLine [-28000 91600 -38500 91600 2000]
+	ElementLine [-38500 91600 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-3400 54000 -28000 54000 2000]
+	ElementLine [-3400 64800 -28000 64800 2000]
+	ElementLine [-3400 75600 -28000 75600 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+	ElementLine [-14600 48600 -28000 48600 2000]
+	ElementLine [-14600 59400 -28000 59400 2000]
+	ElementLine [-14600 70200 -28000 70200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB25F" 160000 50000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[0 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[0 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[0 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[0 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[0 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[0 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[0 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[0 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[11200 48600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[11200 59400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[11200 70200 6000 3000 6600 3500 "20" "20" ""]
+	Pin[11200 81000 6000 3000 6600 3500 "21" "21" ""]
+	Pin[11200 91800 6000 3000 6600 3500 "22" "22" ""]
+	Pin[11200 102600 6000 3000 6600 3500 "23" "23" ""]
+	Pin[11200 113400 6000 3000 6600 3500 "24" "24" ""]
+	Pin[11200 124200 6000 3000 6600 3500 "25" "25" ""]
+	Pin[5600 156600 25000 3000 25600 12500 "C1" "26" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "27" ""]
+	ElementLine [39100 168600 42100 168600 1000]
+	ElementLine [39100 -39000 39100 168600 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 168600 1000]
+	ElementLine [39100 162600 42100 162600 1000]
+	ElementLine [39100 150600 42100 150600 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 145600 39100 145600 2000]
+	ElementLine [28600 -16000 28600 145600 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 145600 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [4000 54000 28600 54000 2000]
+	ElementLine [4000 64800 28600 64800 2000]
+	ElementLine [4000 75600 28600 75600 2000]
+	ElementLine [4000 86400 28600 86400 2000]
+	ElementLine [4000 97200 28600 97200 2000]
+	ElementLine [4000 108000 28600 108000 2000]
+	ElementLine [4000 118800 28600 118800 2000]
+	ElementLine [4000 129600 28600 129600 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+	ElementLine [15200 48600 28600 48600 2000]
+	ElementLine [15200 59400 28600 59400 2000]
+	ElementLine [15200 70200 28600 70200 2000]
+	ElementLine [15200 81000 28600 81000 2000]
+	ElementLine [15200 91800 28600 91800 2000]
+	ElementLine [15200 102600 28600 102600 2000]
+	ElementLine [15200 113400 28600 113400 2000]
+	ElementLine [15200 124200 28600 124200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB25M" 250000 50000 -5000 129600 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[600 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[600 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[600 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[600 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[600 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[600 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[600 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[600 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[-10600 48600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[-10600 59400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[-10600 70200 6000 3000 6600 3500 "20" "20" ""]
+	Pin[-10600 81000 6000 3000 6600 3500 "21" "21" ""]
+	Pin[-10600 91800 6000 3000 6600 3500 "22" "22" ""]
+	Pin[-10600 102600 6000 3000 6600 3500 "23" "23" ""]
+	Pin[-10600 113400 6000 3000 6600 3500 "24" "24" ""]
+	Pin[-10600 124200 6000 3000 6600 3500 "25" "25" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "26" ""]
+	Pin[-5000 156600 25000 3000 25600 12500 "C2" "27" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 168600 1000]
+	ElementLine [-38500 168600 -41500 168600 1000]
+	ElementLine [-41500 168600 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 162600 -38500 162600 1000]
+	ElementLine [-41500 150600 -38500 150600 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 145600 2000]
+	ElementLine [-28000 145600 -38500 145600 2000]
+	ElementLine [-38500 145600 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-3400 54000 -28000 54000 2000]
+	ElementLine [-3400 64800 -28000 64800 2000]
+	ElementLine [-3400 75600 -28000 75600 2000]
+	ElementLine [-3400 86400 -28000 86400 2000]
+	ElementLine [-3400 97200 -28000 97200 2000]
+	ElementLine [-3400 108000 -28000 108000 2000]
+	ElementLine [-3400 118800 -28000 118800 2000]
+	ElementLine [-3400 129600 -28000 129600 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+	ElementLine [-14600 48600 -28000 48600 2000]
+	ElementLine [-14600 59400 -28000 59400 2000]
+	ElementLine [-14600 70200 -28000 70200 2000]
+	ElementLine [-14600 81000 -28000 81000 2000]
+	ElementLine [-14600 91800 -28000 91800 2000]
+	ElementLine [-14600 102600 -28000 102600 2000]
+	ElementLine [-14600 113400 -28000 113400 2000]
+	ElementLine [-14600 124200 -28000 124200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB37F" 20000 260000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[0 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[0 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[0 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[0 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[0 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[0 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[0 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[0 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[0 140400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[0 151200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[0 162000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[0 172800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[0 183600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[0 194400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "20" "20" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "21" "21" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "22" "22" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "23" "23" ""]
+	Pin[11200 48600 6000 3000 6600 3500 "24" "24" ""]
+	Pin[11200 59400 6000 3000 6600 3500 "25" "25" ""]
+	Pin[11200 70200 6000 3000 6600 3500 "26" "26" ""]
+	Pin[11200 81000 6000 3000 6600 3500 "27" "27" ""]
+	Pin[11200 91800 6000 3000 6600 3500 "28" "28" ""]
+	Pin[11200 102600 6000 3000 6600 3500 "29" "29" ""]
+	Pin[11200 113400 6000 3000 6600 3500 "30" "30" ""]
+	Pin[11200 124200 6000 3000 6600 3500 "31" "31" ""]
+	Pin[11200 135000 6000 3000 6600 3500 "32" "32" ""]
+	Pin[11200 145800 6000 3000 6600 3500 "33" "33" ""]
+	Pin[11200 156600 6000 3000 6600 3500 "34" "34" ""]
+	Pin[11200 167400 6000 3000 6600 3500 "35" "35" ""]
+	Pin[11200 178200 6000 3000 6600 3500 "36" "36" ""]
+	Pin[11200 189000 6000 3000 6600 3500 "37" "37" ""]
+	Pin[5600 221400 25000 3000 25600 12500 "C1" "38" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "39" ""]
+	ElementLine [39100 233400 42100 233400 1000]
+	ElementLine [39100 -39000 39100 233400 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 233400 1000]
+	ElementLine [39100 227400 42100 227400 1000]
+	ElementLine [39100 215400 42100 215400 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 210400 39100 210400 2000]
+	ElementLine [28600 -16000 28600 210400 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 210400 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [4000 54000 28600 54000 2000]
+	ElementLine [4000 64800 28600 64800 2000]
+	ElementLine [4000 75600 28600 75600 2000]
+	ElementLine [4000 86400 28600 86400 2000]
+	ElementLine [4000 97200 28600 97200 2000]
+	ElementLine [4000 108000 28600 108000 2000]
+	ElementLine [4000 118800 28600 118800 2000]
+	ElementLine [4000 129600 28600 129600 2000]
+	ElementLine [4000 140400 28600 140400 2000]
+	ElementLine [4000 151200 28600 151200 2000]
+	ElementLine [4000 162000 28600 162000 2000]
+	ElementLine [4000 172800 28600 172800 2000]
+	ElementLine [4000 183600 28600 183600 2000]
+	ElementLine [4000 194400 28600 194400 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+	ElementLine [15200 48600 28600 48600 2000]
+	ElementLine [15200 59400 28600 59400 2000]
+	ElementLine [15200 70200 28600 70200 2000]
+	ElementLine [15200 81000 28600 81000 2000]
+	ElementLine [15200 91800 28600 91800 2000]
+	ElementLine [15200 102600 28600 102600 2000]
+	ElementLine [15200 113400 28600 113400 2000]
+	ElementLine [15200 124200 28600 124200 2000]
+	ElementLine [15200 135000 28600 135000 2000]
+	ElementLine [15200 145800 28600 145800 2000]
+	ElementLine [15200 156600 28600 156600 2000]
+	ElementLine [15200 167400 28600 167400 2000]
+	ElementLine [15200 178200 28600 178200 2000]
+	ElementLine [15200 189000 28600 189000 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB37M" 110000 260000 -5000 194400 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[600 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[600 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[600 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[600 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[600 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[600 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[600 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[600 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[600 140400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[600 151200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[600 162000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[600 172800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[600 183600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[600 194400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "20" "20" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "21" "21" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "22" "22" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "23" "23" ""]
+	Pin[-10600 48600 6000 3000 6600 3500 "24" "24" ""]
+	Pin[-10600 59400 6000 3000 6600 3500 "25" "25" ""]
+	Pin[-10600 70200 6000 3000 6600 3500 "26" "26" ""]
+	Pin[-10600 81000 6000 3000 6600 3500 "27" "27" ""]
+	Pin[-10600 91800 6000 3000 6600 3500 "28" "28" ""]
+	Pin[-10600 102600 6000 3000 6600 3500 "29" "29" ""]
+	Pin[-10600 113400 6000 3000 6600 3500 "30" "30" ""]
+	Pin[-10600 124200 6000 3000 6600 3500 "31" "31" ""]
+	Pin[-10600 135000 6000 3000 6600 3500 "32" "32" ""]
+	Pin[-10600 145800 6000 3000 6600 3500 "33" "33" ""]
+	Pin[-10600 156600 6000 3000 6600 3500 "34" "34" ""]
+	Pin[-10600 167400 6000 3000 6600 3500 "35" "35" ""]
+	Pin[-10600 178200 6000 3000 6600 3500 "36" "36" ""]
+	Pin[-10600 189000 6000 3000 6600 3500 "37" "37" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "38" ""]
+	Pin[-5000 221400 25000 3000 25600 12500 "C2" "39" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 233400 1000]
+	ElementLine [-38500 233400 -41500 233400 1000]
+	ElementLine [-41500 233400 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 227400 -38500 227400 1000]
+	ElementLine [-41500 215400 -38500 215400 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 210400 2000]
+	ElementLine [-28000 210400 -38500 210400 2000]
+	ElementLine [-38500 210400 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-3400 54000 -28000 54000 2000]
+	ElementLine [-3400 64800 -28000 64800 2000]
+	ElementLine [-3400 75600 -28000 75600 2000]
+	ElementLine [-3400 86400 -28000 86400 2000]
+	ElementLine [-3400 97200 -28000 97200 2000]
+	ElementLine [-3400 108000 -28000 108000 2000]
+	ElementLine [-3400 118800 -28000 118800 2000]
+	ElementLine [-3400 129600 -28000 129600 2000]
+	ElementLine [-3400 140400 -28000 140400 2000]
+	ElementLine [-3400 151200 -28000 151200 2000]
+	ElementLine [-3400 162000 -28000 162000 2000]
+	ElementLine [-3400 172800 -28000 172800 2000]
+	ElementLine [-3400 183600 -28000 183600 2000]
+	ElementLine [-3400 194400 -28000 194400 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+	ElementLine [-14600 48600 -28000 48600 2000]
+	ElementLine [-14600 59400 -28000 59400 2000]
+	ElementLine [-14600 70200 -28000 70200 2000]
+	ElementLine [-14600 81000 -28000 81000 2000]
+	ElementLine [-14600 91800 -28000 91800 2000]
+	ElementLine [-14600 102600 -28000 102600 2000]
+	ElementLine [-14600 113400 -28000 113400 2000]
+	ElementLine [-14600 124200 -28000 124200 2000]
+	ElementLine [-14600 135000 -28000 135000 2000]
+	ElementLine [-14600 145800 -28000 145800 2000]
+	ElementLine [-14600 156600 -28000 156600 2000]
+	ElementLine [-14600 167400 -28000 167400 2000]
+	ElementLine [-14600 178200 -28000 178200 2000]
+	ElementLine [-14600 189000 -28000 189000 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB9F" 160000 290000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "6" "6" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "7" "7" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "8" "8" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "9" "9" ""]
+	Pin[5600 70200 25000 3000 25600 12500 "C1" "10" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "11" ""]
+	ElementLine [39100 82200 42100 82200 1000]
+	ElementLine [39100 -39000 39100 82200 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 82200 1000]
+	ElementLine [39100 76200 42100 76200 1000]
+	ElementLine [39100 64200 42100 64200 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 59200 39100 59200 2000]
+	ElementLine [28600 -16000 28600 59200 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 59200 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB9M" 250000 290000 -5000 43200 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "6" "6" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "7" "7" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "8" "8" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "9" "9" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "10" ""]
+	Pin[-5000 70200 25000 3000 25600 12500 "C2" "11" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 82200 1000]
+	ElementLine [-38500 82200 -41500 82200 1000]
+	ElementLine [-41500 82200 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 76200 -38500 76200 1000]
+	ElementLine [-41500 64200 -38500 64200 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 59200 2000]
+	ElementLine [-28000 59200 -38500 59200 2000]
+	ElementLine [-38500 59200 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+
+	)
+
+Element["" "right angle BNC" "" "BNC_LAY" 150000 440000 20000 0 3 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 -10000 6000 3000 6600 3500 "2" "2" ""]
+	Pin[20000 -20000 10000 3000 10600 8100 "m1" "3" ""]
+	Pin[20000 20000 10000 3000 10600 8100 "m2" "4" ""]
+	ElementLine [-6000 -29000 49000 -29000 1000]
+	ElementLine [49000 -29000 49000 29000 1000]
+	ElementLine [49000 29000 -6000 29000 1000]
+	ElementLine [-6000 29000 -6000 -29000 1000]
+
+	)
+
+Element["" "" "" "pwrjack" 240000 470000 0 0 0 100 ""]
+(
+	Pin[0 0 19685 2000 20285 11811 "" "2" ""]
+	Pin[0 -27559 19685 2000 20285 11811 "" "1" ""]
+	Pin[-17717 -13779 19685 2000 20285 11811 "" "3" ""]
+	ElementLine [-17717 -55118 -17717 0 1000]
+	ElementLine [-17717 0 17716 0 1000]
+	ElementLine [17716 0 17716 -55118 1000]
+	ElementLine [17716 -55118 -17717 -55118 1000]
+	ElementLine [-17717 -43307 17716 -43307 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/util/pcblib-map/imgmap_fp.sh b/util/pcblib-map/imgmap_fp.sh
new file mode 100755
index 0000000..6c8f5e2
--- /dev/null
+++ b/util/pcblib-map/imgmap_fp.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+pcb=$1.pcb
+
+pcb -x png --dpi 300 --photo-mode $pcb
+png=$1.png
+html=$1.html
+
+png_dims=`file $png | awk -F "[,]" '{ sub("x", " ", $2); print $2}'`
+
+
+
+awk -v "png_dims=$png_dims" -v "png_url=$png" '
+BEGIN {
+	q = "\""
+	sub("^ *", "", png_dims)
+	sub(" *$", "", png_dims)
+	split(png_dims, A, " +")
+	png_sx = A[1]
+	png_sy = A[2]
+	ne = 0
+}
+
+function bump(idx, x, y)
+{
+	if ((E[idx, "minx"] == "") || (x < E[idx, "minx"]))
+		E[idx, "minx"] = x
+	if ((E[idx, "maxx"] == "") || (x > E[idx, "maxx"]))
+		E[idx, "maxx"] = x
+	if ((E[idx, "miny"] == "") || (y < E[idx, "miny"]))
+		E[idx, "miny"] = y
+	if ((E[idx, "maxy"] == "") || (y > E[idx, "maxy"]))
+		E[idx, "maxy"] = y
+}
+
+# Element["" "rcy(150, bar-sign)" "C1" "acy150" 22500 95000 -2000 -4500 1 100 ""]
+/^Element *[[]/ {
+	coords=$0
+	sub("Element *[[]", "", coords)
+	for(n = 1; n <= 4; n++) {
+		match(coords, "[\"][^\"]*[\"]")
+		P[n] = substr(coords, RSTART+1, RLENGTH-2)
+		sub("[\"][^\"]*[\"]", "", coords)
+	}
+	split(coords, A, " ")
+	E[ne, "cmd"] = P[2]
+	E[ne, "cx"] = A[1]
+	E[ne, "cy"] = A[2]
+	E[ne, "file"] = P[4]
+	ne++
+	next
+}
+
+#ElementLine [-11811 -13006 -11811 -11250 3937]
+/^[ \t]*ElementLine *[[]/ {
+	sub("ElementLine *[[]", "", $0)
+	bump(ne-1, $1, $2)
+	bump(ne-1, $3, $4)
+	next
+}
+
+#ElementArc [-11811 -13006 -11811 -11250 3937]
+/^[ \t]*ElementArc *[[]/ {
+	sub("ElementArc *[[]", "", $0)
+	x = $1
+	y = $2
+	rx = $3
+	ry = $4
+	bump(ne-1, x-rx, y-ry)
+	bump(ne-1, x+rx, y+ry)
+	next
+}
+
+#Via[260000 120000 7874 4000 0 3150 "" ""]
+/^[ \t]*Via *[[]/ {
+	sub("Via *[[]", "", $0)
+	pcb_sx = $1
+	pcb_sy = $2
+	next
+}
+
+
+END {
+	scale_x = png_sx/pcb_sx
+	scale_y = png_sy/pcb_sy
+	print "<html><body>"
+	print "<img src=" q png_url q " usemap=\"#fpmap\">"
+	print "<map name=\"fpmap\">"
+	for(n = 0; n < ne; n++) {
+#		print E[n, "minx"], E[n, "maxx"], E[n, "miny"], E[n, "maxy"]
+		x1 = int((E[n, "minx"] + E[n, "cx"]) * scale_x)
+		x2 = int((E[n, "maxx"] + E[n, "cx"]) * scale_x)
+		y1 = int((E[n, "miny"] + E[n, "cy"]) * scale_y)
+		y2 = int((E[n, "maxy"] + E[n, "cy"]) * scale_y)
+		if (x1 < 0)
+			x1 = 0
+		if (y1 < 0)
+			y1 = 0
+		if (x1 > x2) {
+			tmp = x1
+			x1 = x2
+			x2 = tmp
+		}
+		if (y1 > y2) {
+			tmp = y1
+			y1 = y2
+			y2 = tmp
+		}
+		x1 -= 5
+		y1 -= 5
+		x2 += 5
+		y2 += 5
+#		print n, x1, y1, x2, y2, E[n, "cmd"]
+		cmd =  E[n, "cmd"]
+		if (cmd ~ "^[^ ]*[(]")
+			url="http://igor2.repo.hu/cgi-bin/pcblib-param.cgi?cmd=" cmd
+		else
+			url="http://igor2.repo.hu/cgi-bin/pcblib-static.cgi?fp=" E[n, "file"]
+		gsub(" ", "+", url)
+		print "<area shape=\"rect\" coords=" q x1 "," y1 "," x2 "," y2 q " href=" q url q " alt=" q E[n, "cmd"] q ">"
+	}
+	print "</map>"
+	print "</body></html>"
+}
+
+' < $pcb > $html
+
+
diff --git a/util/pcblib-map/imgmap_page.sh b/util/pcblib-map/imgmap_page.sh
new file mode 100755
index 0000000..5119c52
--- /dev/null
+++ b/util/pcblib-map/imgmap_page.sh
@@ -0,0 +1,131 @@
+#!/bin/sh
+pcb=$1.pcb
+
+pcb -x png --dpi 100 --photo-mode $pcb
+png=$1.png
+
+png_dims=`file $png | awk -F "[,]" '{ sub("x", " ", $2); print $2}'`
+
+awk -v "png_dims=$png_dims" -v "png_url=$png" '
+BEGIN {
+	q = "\""
+	sub("^ *", "", png_dims)
+	sub(" *$", "", png_dims)
+	split(png_dims, A, " +")
+	png_sx = A[1]
+	png_sy = A[2]
+	ne = 0
+	te = 0
+}
+
+
+#	Polygon("clearpoly")
+#	(
+#		[5000 2500] [277500 2500] [277500 547500] [5000 547500] 
+#	)
+
+function bump(idx, x, y)
+{
+	if ((E[idx, "minx"] == "") || (x < E[idx, "minx"]))
+		E[idx, "minx"] = x
+	if ((E[idx, "maxx"] == "") || (x > E[idx, "maxx"]))
+		E[idx, "maxx"] = x
+	if ((E[idx, "miny"] == "") || (y < E[idx, "miny"]))
+		E[idx, "miny"] = y
+	if ((E[idx, "maxy"] == "") || (y > E[idx, "maxy"]))
+		E[idx, "maxy"] = y
+}
+
+/^[ \t]*Polygon[(]/ {
+	getline
+	getline
+	gsub("[[\\]]", "", $0)
+	for(n = 1; n < NF; n+=2)
+		bump(ne, $n, $(n+1))
+	ne++
+	next
+}
+
+#	Text[296338 119704 0 670 "PARAMETRIC" "clearline"]
+/^[ \t]*Text[[]/ {
+	sub("^[ \t]*Text[[]", "", $0)
+	T[te, "x"] = $1
+	T[te, "y"] = $2
+	T[te, "text"] = $5
+	gsub("[\"]", "", T[te, "text"])
+	te++
+	next
+}
+
+#Via[260000 120000 7874 4000 0 3150 "" ""]
+/^[ \t]*Via *[[]/ {
+	sub("Via *[[]", "", $0)
+	pcb_sx = $1
+	pcb_sy = $2
+	next
+}
+
+
+END {
+	scale_x = png_sx/pcb_sx
+	scale_y = png_sy/pcb_sy
+
+	print "<html><body>"
+	print "<img src=" q png_url q " usemap=\"#fpmap\">"
+	print "<map name=\"fpmap\">"
+	for(n = 0; n < ne; n++) {
+		x1=(E[n, "minx"] + E[n, "cx"])
+		x2=(E[n, "maxx"] + E[n, "cx"])
+		y1=(E[n, "miny"] + E[n, "cy"])
+		y2=(E[n, "maxy"] + E[n, "cy"])
+		t = ""
+
+
+		for(i = 0; i < te; i++) {
+			x=T[i, "x"]
+			y=T[i, "y"]
+			if ((x>=x1) && (x<=x2) && (y>=y1) && (y<=y2)) {
+				t = i
+				break;
+			}
+		}
+#		print n, x1, y1, x2, y2, "|", t, T[t, "x"], T[t, "y"], T[t, "text"] > "/dev/stderr"
+
+		x1 = int(x1 * scale_x)
+		x2 = int(x2 * scale_x)
+		y1 = int(y1 * scale_y)
+		y2 = int(y2 * scale_y)
+		if (x1 < 0)
+			x1 = 0
+		if (y1 < 0)
+			y1 = 0
+		if (x1 > x2) {
+			tmp = x1
+			x1 = x2
+			x2 = tmp
+		}
+		if (y1 > y2) {
+			tmp = y1
+			y1 = y2
+			y2 = tmp
+		}
+		x1 -= 5
+		y1 -= 5
+		x2 += 5
+		y2 += 5
+		if (x1 < 0)
+			x1 = 0
+		if (y1 < 0)
+			y1 = 0
+		url="http://igor2.repo.hu/tmp/pcblib/" tolower(T[t, "text"]) ".html"
+		gsub(" ", "+", url)
+		print "<area shape=\"rect\" coords=" q x1 "," y1 "," x2 "," y2 q " href=" q url q " alt=" q T[t, "text"] q ">"
+		T[t, "text"] = "-"
+	}
+	print "</map>"
+	print "</body></html>"
+}
+
+' < $pcb > map.html
+
+
diff --git a/util/pcblib-map/map.pcb b/util/pcblib-map/map.pcb
new file mode 100644
index 0000000..2f93971
--- /dev/null
+++ b/util/pcblib-map/map.pcb
@@ -0,0 +1,2737 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 540000 1120030]
+
+Grid[2500.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[540000 1120000 7874 4000 0 3150 "" ""]
+
+Element["" "DSUB connector, female/male" "" "DB15F" 30000 50000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[0 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[0 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[0 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[11200 48600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[11200 59400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[11200 70200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[5600 102600 25000 3000 25600 12500 "C1" "16" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "17" ""]
+	ElementLine [39100 114600 42100 114600 1000]
+	ElementLine [39100 -39000 39100 114600 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 114600 1000]
+	ElementLine [39100 108600 42100 108600 1000]
+	ElementLine [39100 96600 42100 96600 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 91600 39100 91600 2000]
+	ElementLine [28600 -16000 28600 91600 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 91600 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [4000 54000 28600 54000 2000]
+	ElementLine [4000 64800 28600 64800 2000]
+	ElementLine [4000 75600 28600 75600 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+	ElementLine [15200 48600 28600 48600 2000]
+	ElementLine [15200 59400 28600 59400 2000]
+	ElementLine [15200 70200 28600 70200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB15M" 120000 50000 -5000 75600 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[600 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[600 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[600 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[-10600 48600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[-10600 59400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[-10600 70200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "16" ""]
+	Pin[-5000 102600 25000 3000 25600 12500 "C2" "17" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 114600 1000]
+	ElementLine [-38500 114600 -41500 114600 1000]
+	ElementLine [-41500 114600 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 108600 -38500 108600 1000]
+	ElementLine [-41500 96600 -38500 96600 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 91600 2000]
+	ElementLine [-28000 91600 -38500 91600 2000]
+	ElementLine [-38500 91600 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-3400 54000 -28000 54000 2000]
+	ElementLine [-3400 64800 -28000 64800 2000]
+	ElementLine [-3400 75600 -28000 75600 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+	ElementLine [-14600 48600 -28000 48600 2000]
+	ElementLine [-14600 59400 -28000 59400 2000]
+	ElementLine [-14600 70200 -28000 70200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB25F" 170000 50000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[0 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[0 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[0 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[0 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[0 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[0 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[0 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[0 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[11200 48600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[11200 59400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[11200 70200 6000 3000 6600 3500 "20" "20" ""]
+	Pin[11200 81000 6000 3000 6600 3500 "21" "21" ""]
+	Pin[11200 91800 6000 3000 6600 3500 "22" "22" ""]
+	Pin[11200 102600 6000 3000 6600 3500 "23" "23" ""]
+	Pin[11200 113400 6000 3000 6600 3500 "24" "24" ""]
+	Pin[11200 124200 6000 3000 6600 3500 "25" "25" ""]
+	Pin[5600 156600 25000 3000 25600 12500 "C1" "26" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "27" ""]
+	ElementLine [39100 168600 42100 168600 1000]
+	ElementLine [39100 -39000 39100 168600 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 168600 1000]
+	ElementLine [39100 162600 42100 162600 1000]
+	ElementLine [39100 150600 42100 150600 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 145600 39100 145600 2000]
+	ElementLine [28600 -16000 28600 145600 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 145600 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [4000 54000 28600 54000 2000]
+	ElementLine [4000 64800 28600 64800 2000]
+	ElementLine [4000 75600 28600 75600 2000]
+	ElementLine [4000 86400 28600 86400 2000]
+	ElementLine [4000 97200 28600 97200 2000]
+	ElementLine [4000 108000 28600 108000 2000]
+	ElementLine [4000 118800 28600 118800 2000]
+	ElementLine [4000 129600 28600 129600 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+	ElementLine [15200 48600 28600 48600 2000]
+	ElementLine [15200 59400 28600 59400 2000]
+	ElementLine [15200 70200 28600 70200 2000]
+	ElementLine [15200 81000 28600 81000 2000]
+	ElementLine [15200 91800 28600 91800 2000]
+	ElementLine [15200 102600 28600 102600 2000]
+	ElementLine [15200 113400 28600 113400 2000]
+	ElementLine [15200 124200 28600 124200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB25M" 260000 50000 -5000 129600 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[600 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[600 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[600 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[600 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[600 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[600 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[600 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[600 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[-10600 48600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[-10600 59400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[-10600 70200 6000 3000 6600 3500 "20" "20" ""]
+	Pin[-10600 81000 6000 3000 6600 3500 "21" "21" ""]
+	Pin[-10600 91800 6000 3000 6600 3500 "22" "22" ""]
+	Pin[-10600 102600 6000 3000 6600 3500 "23" "23" ""]
+	Pin[-10600 113400 6000 3000 6600 3500 "24" "24" ""]
+	Pin[-10600 124200 6000 3000 6600 3500 "25" "25" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "26" ""]
+	Pin[-5000 156600 25000 3000 25600 12500 "C2" "27" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 168600 1000]
+	ElementLine [-38500 168600 -41500 168600 1000]
+	ElementLine [-41500 168600 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 162600 -38500 162600 1000]
+	ElementLine [-41500 150600 -38500 150600 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 145600 2000]
+	ElementLine [-28000 145600 -38500 145600 2000]
+	ElementLine [-38500 145600 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-3400 54000 -28000 54000 2000]
+	ElementLine [-3400 64800 -28000 64800 2000]
+	ElementLine [-3400 75600 -28000 75600 2000]
+	ElementLine [-3400 86400 -28000 86400 2000]
+	ElementLine [-3400 97200 -28000 97200 2000]
+	ElementLine [-3400 108000 -28000 108000 2000]
+	ElementLine [-3400 118800 -28000 118800 2000]
+	ElementLine [-3400 129600 -28000 129600 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+	ElementLine [-14600 48600 -28000 48600 2000]
+	ElementLine [-14600 59400 -28000 59400 2000]
+	ElementLine [-14600 70200 -28000 70200 2000]
+	ElementLine [-14600 81000 -28000 81000 2000]
+	ElementLine [-14600 91800 -28000 91800 2000]
+	ElementLine [-14600 102600 -28000 102600 2000]
+	ElementLine [-14600 113400 -28000 113400 2000]
+	ElementLine [-14600 124200 -28000 124200 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB37F" 30000 260000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[0 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[0 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[0 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[0 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[0 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[0 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[0 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[0 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[0 140400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[0 151200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[0 162000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[0 172800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[0 183600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[0 194400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "20" "20" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "21" "21" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "22" "22" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "23" "23" ""]
+	Pin[11200 48600 6000 3000 6600 3500 "24" "24" ""]
+	Pin[11200 59400 6000 3000 6600 3500 "25" "25" ""]
+	Pin[11200 70200 6000 3000 6600 3500 "26" "26" ""]
+	Pin[11200 81000 6000 3000 6600 3500 "27" "27" ""]
+	Pin[11200 91800 6000 3000 6600 3500 "28" "28" ""]
+	Pin[11200 102600 6000 3000 6600 3500 "29" "29" ""]
+	Pin[11200 113400 6000 3000 6600 3500 "30" "30" ""]
+	Pin[11200 124200 6000 3000 6600 3500 "31" "31" ""]
+	Pin[11200 135000 6000 3000 6600 3500 "32" "32" ""]
+	Pin[11200 145800 6000 3000 6600 3500 "33" "33" ""]
+	Pin[11200 156600 6000 3000 6600 3500 "34" "34" ""]
+	Pin[11200 167400 6000 3000 6600 3500 "35" "35" ""]
+	Pin[11200 178200 6000 3000 6600 3500 "36" "36" ""]
+	Pin[11200 189000 6000 3000 6600 3500 "37" "37" ""]
+	Pin[5600 221400 25000 3000 25600 12500 "C1" "38" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "39" ""]
+	ElementLine [39100 233400 42100 233400 1000]
+	ElementLine [39100 -39000 39100 233400 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 233400 1000]
+	ElementLine [39100 227400 42100 227400 1000]
+	ElementLine [39100 215400 42100 215400 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 210400 39100 210400 2000]
+	ElementLine [28600 -16000 28600 210400 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 210400 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [4000 54000 28600 54000 2000]
+	ElementLine [4000 64800 28600 64800 2000]
+	ElementLine [4000 75600 28600 75600 2000]
+	ElementLine [4000 86400 28600 86400 2000]
+	ElementLine [4000 97200 28600 97200 2000]
+	ElementLine [4000 108000 28600 108000 2000]
+	ElementLine [4000 118800 28600 118800 2000]
+	ElementLine [4000 129600 28600 129600 2000]
+	ElementLine [4000 140400 28600 140400 2000]
+	ElementLine [4000 151200 28600 151200 2000]
+	ElementLine [4000 162000 28600 162000 2000]
+	ElementLine [4000 172800 28600 172800 2000]
+	ElementLine [4000 183600 28600 183600 2000]
+	ElementLine [4000 194400 28600 194400 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+	ElementLine [15200 48600 28600 48600 2000]
+	ElementLine [15200 59400 28600 59400 2000]
+	ElementLine [15200 70200 28600 70200 2000]
+	ElementLine [15200 81000 28600 81000 2000]
+	ElementLine [15200 91800 28600 91800 2000]
+	ElementLine [15200 102600 28600 102600 2000]
+	ElementLine [15200 113400 28600 113400 2000]
+	ElementLine [15200 124200 28600 124200 2000]
+	ElementLine [15200 135000 28600 135000 2000]
+	ElementLine [15200 145800 28600 145800 2000]
+	ElementLine [15200 156600 28600 156600 2000]
+	ElementLine [15200 167400 28600 167400 2000]
+	ElementLine [15200 178200 28600 178200 2000]
+	ElementLine [15200 189000 28600 189000 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB37M" 120000 260000 -5000 194400 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[600 54000 6000 3000 6600 3500 "6" "6" ""]
+	Pin[600 64800 6000 3000 6600 3500 "7" "7" ""]
+	Pin[600 75600 6000 3000 6600 3500 "8" "8" ""]
+	Pin[600 86400 6000 3000 6600 3500 "9" "9" ""]
+	Pin[600 97200 6000 3000 6600 3500 "10" "10" ""]
+	Pin[600 108000 6000 3000 6600 3500 "11" "11" ""]
+	Pin[600 118800 6000 3000 6600 3500 "12" "12" ""]
+	Pin[600 129600 6000 3000 6600 3500 "13" "13" ""]
+	Pin[600 140400 6000 3000 6600 3500 "14" "14" ""]
+	Pin[600 151200 6000 3000 6600 3500 "15" "15" ""]
+	Pin[600 162000 6000 3000 6600 3500 "16" "16" ""]
+	Pin[600 172800 6000 3000 6600 3500 "17" "17" ""]
+	Pin[600 183600 6000 3000 6600 3500 "18" "18" ""]
+	Pin[600 194400 6000 3000 6600 3500 "19" "19" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "20" "20" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "21" "21" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "22" "22" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "23" "23" ""]
+	Pin[-10600 48600 6000 3000 6600 3500 "24" "24" ""]
+	Pin[-10600 59400 6000 3000 6600 3500 "25" "25" ""]
+	Pin[-10600 70200 6000 3000 6600 3500 "26" "26" ""]
+	Pin[-10600 81000 6000 3000 6600 3500 "27" "27" ""]
+	Pin[-10600 91800 6000 3000 6600 3500 "28" "28" ""]
+	Pin[-10600 102600 6000 3000 6600 3500 "29" "29" ""]
+	Pin[-10600 113400 6000 3000 6600 3500 "30" "30" ""]
+	Pin[-10600 124200 6000 3000 6600 3500 "31" "31" ""]
+	Pin[-10600 135000 6000 3000 6600 3500 "32" "32" ""]
+	Pin[-10600 145800 6000 3000 6600 3500 "33" "33" ""]
+	Pin[-10600 156600 6000 3000 6600 3500 "34" "34" ""]
+	Pin[-10600 167400 6000 3000 6600 3500 "35" "35" ""]
+	Pin[-10600 178200 6000 3000 6600 3500 "36" "36" ""]
+	Pin[-10600 189000 6000 3000 6600 3500 "37" "37" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "38" ""]
+	Pin[-5000 221400 25000 3000 25600 12500 "C2" "39" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 233400 1000]
+	ElementLine [-38500 233400 -41500 233400 1000]
+	ElementLine [-41500 233400 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 227400 -38500 227400 1000]
+	ElementLine [-41500 215400 -38500 215400 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 210400 2000]
+	ElementLine [-28000 210400 -38500 210400 2000]
+	ElementLine [-38500 210400 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-3400 54000 -28000 54000 2000]
+	ElementLine [-3400 64800 -28000 64800 2000]
+	ElementLine [-3400 75600 -28000 75600 2000]
+	ElementLine [-3400 86400 -28000 86400 2000]
+	ElementLine [-3400 97200 -28000 97200 2000]
+	ElementLine [-3400 108000 -28000 108000 2000]
+	ElementLine [-3400 118800 -28000 118800 2000]
+	ElementLine [-3400 129600 -28000 129600 2000]
+	ElementLine [-3400 140400 -28000 140400 2000]
+	ElementLine [-3400 151200 -28000 151200 2000]
+	ElementLine [-3400 162000 -28000 162000 2000]
+	ElementLine [-3400 172800 -28000 172800 2000]
+	ElementLine [-3400 183600 -28000 183600 2000]
+	ElementLine [-3400 194400 -28000 194400 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+	ElementLine [-14600 48600 -28000 48600 2000]
+	ElementLine [-14600 59400 -28000 59400 2000]
+	ElementLine [-14600 70200 -28000 70200 2000]
+	ElementLine [-14600 81000 -28000 81000 2000]
+	ElementLine [-14600 91800 -28000 91800 2000]
+	ElementLine [-14600 102600 -28000 102600 2000]
+	ElementLine [-14600 113400 -28000 113400 2000]
+	ElementLine [-14600 124200 -28000 124200 2000]
+	ElementLine [-14600 135000 -28000 135000 2000]
+	ElementLine [-14600 145800 -28000 145800 2000]
+	ElementLine [-14600 156600 -28000 156600 2000]
+	ElementLine [-14600 167400 -28000 167400 2000]
+	ElementLine [-14600 178200 -28000 178200 2000]
+	ElementLine [-14600 189000 -28000 189000 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB9F" 170000 290000 5600 0 3 150 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[0 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[0 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[0 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[11200 5400 6000 3000 6600 3500 "6" "6" ""]
+	Pin[11200 16200 6000 3000 6600 3500 "7" "7" ""]
+	Pin[11200 27000 6000 3000 6600 3500 "8" "8" ""]
+	Pin[11200 37800 6000 3000 6600 3500 "9" "9" ""]
+	Pin[5600 70200 25000 3000 25600 12500 "C1" "10" ""]
+	Pin[5600 -27000 25000 3000 25600 12500 "C2" "11" ""]
+	ElementLine [39100 82200 42100 82200 1000]
+	ElementLine [39100 -39000 39100 82200 1000]
+	ElementLine [39100 -39000 42100 -39000 1000]
+	ElementLine [42100 -39000 42100 82200 1000]
+	ElementLine [39100 76200 42100 76200 1000]
+	ElementLine [39100 64200 42100 64200 1000]
+	ElementLine [39100 -33000 42100 -33000 1000]
+	ElementLine [39100 -21000 42100 -21000 1000]
+	ElementLine [28600 59200 39100 59200 2000]
+	ElementLine [28600 -16000 28600 59200 2000]
+	ElementLine [28600 -16000 39100 -16000 2000]
+	ElementLine [39100 -16000 39100 59200 1000]
+	ElementLine [4000 0 28600 0 2000]
+	ElementLine [4000 10800 28600 10800 2000]
+	ElementLine [4000 21600 28600 21600 2000]
+	ElementLine [4000 32400 28600 32400 2000]
+	ElementLine [4000 43200 28600 43200 2000]
+	ElementLine [15200 5400 28600 5400 2000]
+	ElementLine [15200 16200 28600 16200 2000]
+	ElementLine [15200 27000 28600 27000 2000]
+	ElementLine [15200 37800 28600 37800 2000]
+
+	)
+
+Element["" "DSUB connector, female/male" "" "DB9M" 260000 290000 -5000 43200 1 150 ""]
+(
+	Pin[600 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[600 10800 6000 3000 6600 3500 "2" "2" ""]
+	Pin[600 21600 6000 3000 6600 3500 "3" "3" ""]
+	Pin[600 32400 6000 3000 6600 3500 "4" "4" ""]
+	Pin[600 43200 6000 3000 6600 3500 "5" "5" ""]
+	Pin[-10600 5400 6000 3000 6600 3500 "6" "6" ""]
+	Pin[-10600 16200 6000 3000 6600 3500 "7" "7" ""]
+	Pin[-10600 27000 6000 3000 6600 3500 "8" "8" ""]
+	Pin[-10600 37800 6000 3000 6600 3500 "9" "9" ""]
+	Pin[-5000 -27000 25000 3000 25600 12500 "C1" "10" ""]
+	Pin[-5000 70200 25000 3000 25600 12500 "C2" "11" ""]
+	ElementLine [-41500 -39000 -38500 -39000 1000]
+	ElementLine [-38500 -39000 -38500 82200 1000]
+	ElementLine [-38500 82200 -41500 82200 1000]
+	ElementLine [-41500 82200 -41500 -39000 1000]
+	ElementLine [-41500 -33000 -38500 -33000 1000]
+	ElementLine [-41500 -21000 -38500 -21000 1000]
+	ElementLine [-41500 76200 -38500 76200 1000]
+	ElementLine [-41500 64200 -38500 64200 1000]
+	ElementLine [-38500 -16000 -28000 -16000 2000]
+	ElementLine [-28000 -16000 -28000 59200 2000]
+	ElementLine [-28000 59200 -38500 59200 2000]
+	ElementLine [-38500 59200 -38500 -16000 1000]
+	ElementLine [-3400 0 -28000 0 2000]
+	ElementLine [-3400 10800 -28000 10800 2000]
+	ElementLine [-3400 21600 -28000 21600 2000]
+	ElementLine [-3400 32400 -28000 32400 2000]
+	ElementLine [-3400 43200 -28000 43200 2000]
+	ElementLine [-14600 5400 -28000 5400 2000]
+	ElementLine [-14600 16200 -28000 16200 2000]
+	ElementLine [-14600 27000 -28000 27000 2000]
+	ElementLine [-14600 37800 -28000 37800 2000]
+
+	)
+
+Element["" "right angle BNC" "" "BNC_LAY" 152500 442500 20000 0 3 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3500 "1" "1" "square"]
+	Pin[0 -10000 6000 3000 6600 3500 "2" "2" ""]
+	Pin[20000 -20000 10000 3000 10600 8100 "m1" "3" ""]
+	Pin[20000 20000 10000 3000 10600 8100 "m2" "4" ""]
+	ElementLine [-6000 -29000 49000 -29000 1000]
+	ElementLine [49000 -29000 49000 29000 1000]
+	ElementLine [49000 29000 -6000 29000 1000]
+	ElementLine [-6000 29000 -6000 -29000 1000]
+
+	)
+
+Element["" "rcy(150, bar-sign)" "C1" "acy150" 302500 95000 -2000 -4500 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -15000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -28500 0 -24500 1000]
+	ElementLine [0 9500 0 13500 1000]
+	ElementLine [-2000 11500 2000 11500 1000]
+	ElementLine [7874 -17883 7874 -11250 3937]
+	ElementLine [-7874 -17883 -7874 -11250 3937]
+	ElementLine [9842 -16040 9842 -11250 3937]
+	ElementLine [-9842 -16040 -9842 -11250 3937]
+	ElementLine [11811 -13006 11811 -11250 3937]
+	ElementLine [-11811 -13006 -11811 -11250 3937]
+	ElementArc [0 -7500 15000 15000 90 360 1000]
+
+	)
+
+Element["" "so(8)" "" "8*250" 337500 82500 -10000 0 1 100 ""]
+(
+	Pad[-7500 7000 -7500 13500 2000 1000 3000 "" "1" "square,edge2"]
+	Pad[-7500 -13500 -7500 -7000 2000 1000 3000 "" "8" "square"]
+	Pad[-2500 7000 -2500 13500 2000 1000 3000 "" "2" "square,edge2"]
+	Pad[-2500 -13500 -2500 -7000 2000 1000 3000 "" "7" "square"]
+	Pad[2500 7000 2500 13500 2000 1000 3000 "" "3" "square,edge2"]
+	Pad[2500 -13500 2500 -7000 2000 1000 3000 "" "6" "square"]
+	Pad[7500 7000 7500 13500 2000 1000 3000 "" "4" "square,edge2"]
+	Pad[7500 -13500 7500 -7000 2000 1000 3000 "" "5" "square"]
+	ElementLine [-10000 16000 10000 16000 1000]
+	ElementLine [10000 -16000 10000 16000 1000]
+	ElementLine [-10000 -16000 10000 -16000 1000]
+	ElementLine [-10000 2500 -10000 16000 1000]
+	ElementLine [-10000 -16000 -10000 -2500 1000]
+	ElementArc [-10000 0 2500 2500 90 180 1000]
+
+	)
+
+Element["" "dip(4)" "" "4*300" 365000 102500 -10000 0 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 0 8000 5000 8600 3937 "" "2" ""]
+	Pin[10000 -30000 8000 5000 8600 3937 "" "3" ""]
+	ElementLine [-5000 5000 15000 5000 1000]
+	ElementLine [15000 -35000 15000 5000 1000]
+	ElementLine [-5000 -35000 15000 -35000 1000]
+	ElementLine [-5000 -10000 -5000 5000 1000]
+	ElementLine [-5000 -35000 -5000 -20000 1000]
+	ElementArc [-5000 -15000 5000 5000 90 180 1000]
+
+	)
+
+Element["" "tssop(8)" "" "8*6.4mm" 397500 82500 -10000 0 1 100 ""]
+(
+	Pad[-3838 10236 -3838 14173 1771 1000 3000 "" "1" "square,edge2"]
+	Pad[-3838 -14173 -3838 -10236 1771 1000 3000 "" "8" "square"]
+	Pad[-1278 10236 -1278 14173 1771 1000 3000 "" "2" "square,edge2"]
+	Pad[-1278 -14173 -1278 -10236 1771 1000 3000 "" "7" "square"]
+	Pad[1280 10236 1280 14173 1771 1000 3000 "" "3" "square,edge2"]
+	Pad[1280 -14173 1280 -10236 1771 1000 3000 "" "6" "square"]
+	Pad[3839 10236 3839 14173 1771 1000 3000 "" "4" "square,edge2"]
+	Pad[3839 -14173 3839 -10236 1771 1000 3000 "" "5" "square"]
+	ElementLine [-6038 15973 6039 15973 1000]
+	ElementLine [6039 -15973 6039 15973 1000]
+	ElementLine [-6038 -15973 6039 -15973 1000]
+	ElementLine [-6038 2500 -6038 15973 1000]
+	ElementLine [-6038 -15973 -6038 -2500 1000]
+	ElementArc [-6038 0 2500 2500 90 180 1000]
+
+	)
+
+Element["" "connector(2,3)" "" "2*3" 422500 72500 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	Pin[0 20000 8000 5000 8600 3937 "" "3" ""]
+	Pin[10000 0 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 10000 8000 5000 8600 3937 "" "5" ""]
+	Pin[10000 20000 8000 5000 8600 3937 "" "6" ""]
+	ElementLine [-5000 -5000 -5000 25000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [15000 25000 -5000 25000 1000]
+	ElementLine [15000 25000 15000 -5000 1000]
+	ElementLine [-5000 5000 5000 5000 1000]
+	ElementLine [5000 -5000 5000 5000 1000]
+
+	)
+
+Element["" "connector(2,3,eshift=x)" "" "2*3" 455000 72500 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	Pin[0 20000 8000 5000 8600 3937 "" "3" ""]
+	Pin[10000 5000 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 15000 8000 5000 8600 3937 "" "5" ""]
+	Pin[10000 25000 8000 5000 8600 3937 "" "6" ""]
+	ElementLine [-5000 -5000 -5000 30000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [15000 30000 -5000 30000 1000]
+	ElementLine [15000 30000 15000 -5000 1000]
+	ElementLine [-5000 5000 5000 5000 1000]
+	ElementLine [5000 -5000 5000 5000 1000]
+
+	)
+
+Element["" "connector(2,3,eshift=x,etrunc=1)" "" "2*3" 487500 72500 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	Pin[0 20000 8000 5000 8600 3937 "" "3" ""]
+	Pin[10000 5000 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 15000 8000 5000 8600 3937 "" "5" ""]
+	ElementLine [-5000 -5000 -5000 25000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [15000 25000 -5000 25000 1000]
+	ElementLine [15000 25000 15000 -5000 1000]
+	ElementLine [-5000 5000 5000 5000 1000]
+	ElementLine [5000 -5000 5000 5000 1000]
+
+	)
+
+Element["" "acy(300)" "" "acy300" 300000 50000 -12000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+
+	)
+
+Element["" "acy(300, endcap)" "" "acy300" 320000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [2500 -20625 2500 -9375 1000]
+	ElementLine [2500 -9375 3000 -9166 1000]
+	ElementLine [2500 -20625 3000 -20833 1000]
+	ElementLine [-2500 -20625 -2500 -9375 1000]
+	ElementLine [-2500 -9375 -3000 -9166 1000]
+	ElementLine [-2500 -20625 -3000 -20833 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-3000 -9166 -3000 -8000 1000]
+	ElementLine [3000 -9166 3000 -8000 1000]
+	ElementLine [-3000 -22000 -3000 -20833 1000]
+	ElementLine [3000 -22000 3000 -20833 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementArc [-2500 -8000 500 500 0 90 1000]
+	ElementArc [2500 -8000 500 500 90 90 1000]
+	ElementArc [-2500 -22000 500 500 270 90 1000]
+	ElementArc [2500 -22000 500 500 180 90 1000]
+
+	)
+
+Element["" "acy(300, coil, dot)" "" "acy300" 340000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementArc [0 -9375 1875 1875 270 180 1000]
+	ElementArc [0 -13125 1875 1875 270 180 1000]
+	ElementArc [0 -16875 1875 1875 270 180 1000]
+	ElementArc [0 -20625 1875 1875 270 180 1000]
+	ElementArc [-1250 -5500 666 666 90 360 1000]
+
+	)
+
+Element["" "acy(300, pol=bar)" "" "acy300" 360000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [-2500 -6500 2500 -6500 1000]
+	ElementLine [-2500 -8500 -2500 -6500 1000]
+	ElementLine [2500 -8500 2500 -6500 1000]
+	ElementLine [-2500 -8500 2500 -8500 1000]
+
+	)
+
+Element["" "acy(300, wiper=aarrow)" "" "acy300" 380000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 9000 5000 9600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [5000 -12500 -8000 -17500 1000]
+	ElementLine [-8000 -17500 -6492 -15848 1000]
+	ElementLine [-8000 -17500 -5774 -17715 1000]
+	ElementLine [-5774 -17715 -6492 -15848 1000]
+
+	)
+
+Element["" "acy(300, wiper=parrow)" "" "acy300" 400000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [-8000 -15000 -2500 -15000 1000]
+	ElementLine [-2500 -15000 -4500 -16000 1000]
+	ElementLine [-2500 -15000 -4500 -14000 1000]
+	ElementLine [-4500 -16000 -4500 -14000 1000]
+
+	)
+
+Element["" "acy(300, wiper=looparrow)" "" "acy300" 420000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [-8000 -15000 -2500 -15000 1000]
+	ElementLine [-2500 -15000 -4500 -16000 1000]
+	ElementLine [-2500 -15000 -4500 -14000 1000]
+	ElementLine [-4500 -16000 -4500 -14000 1000]
+	ElementLine [-8000 -24375 -8000 -15000 1000]
+	ElementLine [-8000 -24375 0 -24375 1000]
+
+	)
+
+Element["" "acy(300, zigzag)" "" "acy300" 440000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [0 -7500 -2500 -8750 1000]
+	ElementLine [-2500 -8750 2500 -11250 1000]
+	ElementLine [2500 -11250 0 -12500 1000]
+	ElementLine [0 -12500 -2500 -13750 1000]
+	ElementLine [-2500 -13750 2500 -16250 1000]
+	ElementLine [2500 -16250 0 -17500 1000]
+	ElementLine [0 -17500 -2500 -18750 1000]
+	ElementLine [-2500 -18750 2500 -21250 1000]
+	ElementLine [2500 -21250 0 -22500 1000]
+
+	)
+
+Element["" "acy(300, core)" "" "acy300" 460000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-4875 -22500 -4875 -7500 1000]
+	ElementArc [0 -9375 1875 1875 270 180 1000]
+	ElementArc [0 -13125 1875 1875 270 180 1000]
+	ElementArc [0 -16875 1875 1875 270 180 1000]
+	ElementArc [0 -20625 1875 1875 270 180 1000]
+
+	)
+
+Element["" "acy(200, standing, pol=sign, dia=100)" "" "acy200" 520000 90000 -2000 -6000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -20000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -5000 0 0 1000]
+	ElementLine [0 -20000 0 -15000 1000]
+	ElementLine [0 -20000 0 0 1000]
+	ElementLine [2200 -6100 2200 -4100 1000]
+	ElementLine [2200 -15900 2200 -13900 1000]
+	ElementLine [1200 -14900 3200 -14900 1000]
+	ElementArc [0 0 10000 10000 90 360 1000]
+
+	)
+
+Element["" "alf(300,schottky)" "" "acy300" 480000 50000 -5000 -22000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -10000 0 0 1000]
+	ElementLine [0 -30000 0 -20000 1000]
+	ElementLine [-5000 -20000 5000 -20000 1000]
+	ElementLine [-5000 -20000 0 -10000 1000]
+	ElementLine [5000 -20000 0 -10000 1000]
+	ElementLine [-3500 -10000 3500 -10000 1000]
+	ElementArc [-3500 -11500 1500 1500 270 180 1000]
+	ElementArc [3500 -8500 1500 1500 90 180 1000]
+
+	)
+
+Element["" "alf(300,tunnel)" "" "acy300" 500000 50000 -5000 -22000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -10000 0 0 1000]
+	ElementLine [0 -30000 0 -20000 1000]
+	ElementLine [-5000 -20000 5000 -20000 1000]
+	ElementLine [-5000 -20000 0 -10000 1000]
+	ElementLine [5000 -20000 0 -10000 1000]
+	ElementLine [-5000 -10000 5000 -10000 1000]
+	ElementLine [5000 -11500 5000 -10000 1000]
+	ElementLine [-5000 -11500 -5000 -10000 1000]
+
+	)
+
+Element["" "alf(300,zener)" "" "acy300" 520000 50000 -5000 -22000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -10000 0 0 1000]
+	ElementLine [0 -30000 0 -20000 1000]
+	ElementLine [-5000 -20000 5000 -20000 1000]
+	ElementLine [-5000 -20000 0 -10000 1000]
+	ElementLine [5000 -20000 0 -10000 1000]
+	ElementLine [-5000 -10000 5000 -10000 1000]
+	ElementLine [5000 -10000 5000 -8500 1000]
+	ElementLine [-5000 -11500 -5000 -10000 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SC70_3" 315000 420000 9300 -9400 3 100 ""]
+(
+	Pad[0 -300 0 300 2900 3000 3500 "1" "1" "square,edge2"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[2600 -7300 2600 -6700 2900 3000 3500 "3" "3" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SC90" 330000 420000 7600 -7800 3 100 ""]
+(
+	Pad[0 -200 0 200 2400 3000 3000 "1" "1" "square,edge2"]
+	Pad[3900 -200 3900 200 2400 3000 3000 "2" "2" "square,edge2"]
+	Pad[1900 -6100 1900 -5700 2400 3000 3000 "3" "3" "square"]
+	ElementLine [-1700 -7800 -1700 2000 1000]
+	ElementLine [-1700 2000 5600 2000 1000]
+	ElementLine [5600 2000 5600 -7800 1000]
+	ElementLine [5600 -7800 -1700 -7800 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SOT23" 311100 453200 12300 -11000 3 100 ""]
+(
+	Pad[0 -300 0 300 3400 3000 4000 "1" "1" "square,edge2"]
+	Pad[7800 -300 7800 300 3400 3000 4000 "2" "2" "square,edge2"]
+	Pad[3900 -8500 3900 -7900 3400 3000 4000 "3" "3" "square"]
+	ElementLine [-2500 -11000 -2500 2900 1000]
+	ElementLine [-2500 2900 10300 2900 1000]
+	ElementLine [10300 2900 10300 -11000 1000]
+	ElementLine [10300 -11000 -2500 -11000 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOT23D" 311100 473200 12300 -11000 3 100 ""]
+(
+	Pad[0 -300 0 300 3400 3000 4000 "2" "2" "square,edge2"]
+	Pad[7800 -300 7800 300 3400 3000 4000 "3" "3" "square,edge2"]
+	Pad[3900 -8500 3900 -7900 3400 3000 4000 "1" "1" "square"]
+	ElementLine [-2500 -11000 -2500 2900 1000]
+	ElementLine [-2500 2900 10300 2900 1000]
+	ElementLine [10300 2900 10300 -11000 1000]
+	ElementLine [10300 -11000 -2500 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SOT323" 332400 452000 9300 -9400 3 100 ""]
+(
+	Pad[0 -300 0 300 2900 3000 3500 "1" "1" "square,edge2"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[2600 -7300 2600 -6700 2900 3000 3500 "3" "3" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOT323D" 332400 472000 9300 -9400 3 100 ""]
+(
+	Pad[0 -300 0 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "3" "3" "square,edge2"]
+	Pad[2600 -7300 2600 -6700 2900 3000 3500 "1" "1" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SC70_4" 415000 435000 9300 -9400 3 100 ""]
+(
+	Pad[300 0 600 0 2900 3000 3500 "1" "1" "square"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[5100 -7300 5100 -6700 2900 3000 3500 "3" "3" "square"]
+	Pad[0 -7300 0 -6700 2900 3000 3500 "4" "4" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "Pressure transducer" "" "MPAK" 485000 480000 20300 -49700 3 100 ""]
+(
+	Pad[0 -2800 0 2800 3100 3000 3700 "1" "1" "square,edge2"]
+	Pad[5000 -2800 5000 2800 3100 3000 3700 "2" "2" "square,edge2"]
+	Pad[10000 -2800 10000 2800 3100 3000 3700 "3" "3" "square,edge2"]
+	Pad[15000 -2800 15000 2800 3100 3000 3700 "4" "4" "square,edge2"]
+	Pad[3800 -43700 11200 -43700 8700 3000 9300 "5" "5" "square"]
+	ElementLine [-3200 -49700 -3200 6100 1000]
+	ElementLine [-3200 6100 18300 6100 1000]
+	ElementLine [18300 6100 18300 -49700 1000]
+	ElementLine [18300 -49700 -3200 -49700 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SOT143" 430000 435000 11900 -11000 3 100 ""]
+(
+	Pad[300 0 600 0 3400 3000 4000 "1" "1" "square"]
+	Pad[7400 -300 7400 300 3400 3000 4000 "2" "2" "square,edge2"]
+	Pad[7400 -8500 7400 -7900 3400 3000 4000 "3" "3" "square"]
+	Pad[0 -8500 0 -7900 3400 3000 4000 "4" "4" "square"]
+	ElementLine [-2500 -11000 -2500 2900 1000]
+	ElementLine [-2500 2900 9900 2900 1000]
+	ElementLine [9900 2900 9900 -11000 1000]
+	ElementLine [9900 -11000 -2500 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SOT223" 450000 480000 25300 -32900 3 100 ""]
+(
+	Pad[0 -3300 0 3300 5600 3000 6200 "1" "1" "square,edge2"]
+	Pad[9000 -3300 9000 3300 5600 3000 6200 "2" "2" "square,edge2"]
+	Pad[18100 -3300 18100 3300 5600 3000 6200 "3" "3" "square,edge2"]
+	Pad[4500 -24400 13500 -24400 12200 3000 12800 "4" "4" "square"]
+	ElementLine [-5200 -32900 -5200 8500 1000]
+	ElementLine [-5200 8500 23300 8500 1000]
+	ElementLine [23300 8500 23300 -32900 1000]
+	ElementLine [23300 -32900 -5200 -32900 1000]
+
+	)
+
+Element["" "SMT transistor, 5 pins" "" "SOT25" 445000 435000 11800 -11000 3 100 ""]
+(
+	Pad[0 -800 0 800 2400 3000 3000 "1" "1" "square,edge2"]
+	Pad[7800 -800 7800 800 2400 3000 3000 "2" "2" "square,edge2"]
+	Pad[7800 -9000 7800 -7400 2400 3000 3000 "3" "3" "square"]
+	Pad[3900 -9000 3900 -7400 2400 3000 3000 "4" "4" "square"]
+	Pad[0 -9000 0 -7400 2400 3000 3000 "5" "5" "square"]
+	ElementLine [-2000 -11000 -2000 2900 1000]
+	ElementLine [-2000 2900 9800 2900 1000]
+	ElementLine [9800 2900 9800 -11000 1000]
+	ElementLine [9800 -11000 -2000 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 6 pins" "" "SOT26" 461100 435000 11800 -11000 3 100 ""]
+(
+	Pad[0 -800 0 800 2400 3000 3000 "1" "1" "square,edge2"]
+	Pad[3900 -800 3900 800 2400 3000 3000 "2" "2" "square,edge2"]
+	Pad[7800 -800 7800 800 2400 3000 3000 "3" "3" "square,edge2"]
+	Pad[7800 -9000 7800 -7400 2400 3000 3000 "4" "4" "square"]
+	Pad[3900 -9000 3900 -7400 2400 3000 3000 "5" "5" "square"]
+	Pad[0 -9000 0 -7400 2400 3000 3000 "6" "6" "square"]
+	ElementLine [-2000 -11000 -2000 2900 1000]
+	ElementLine [-2000 2900 9800 2900 1000]
+	ElementLine [9800 2900 9800 -11000 1000]
+	ElementLine [9800 -11000 -2000 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 5 pins" "" "SOT325" 419900 485000 8600 -9400 3 100 ""]
+(
+	Pad[0 -1000 0 1000 1500 3000 2100 "1" "1" "square,edge2"]
+	Pad[5100 -1000 5100 1000 1500 3000 2100 "2" "2" "square,edge2"]
+	Pad[5100 -8000 5100 -6000 1500 3000 2100 "3" "3" "square"]
+	Pad[2600 -8000 2600 -6000 1500 3000 2100 "4" "4" "square"]
+	Pad[0 -8000 0 -6000 1500 3000 2100 "5" "5" "square"]
+	ElementLine [-1400 -9400 -1400 2500 1000]
+	ElementLine [-1400 2500 6600 2500 1000]
+	ElementLine [6600 2500 6600 -9400 1000]
+	ElementLine [6600 -9400 -1400 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 6 pins" "" "SOT326" 432400 485000 8600 -9400 3 100 ""]
+(
+	Pad[0 -1000 0 1000 1500 3000 2100 "1" "1" "square,edge2"]
+	Pad[2600 -1000 2600 1000 1500 3000 2100 "2" "2" "square,edge2"]
+	Pad[5100 -1000 5100 1000 1500 3000 2100 "3" "3" "square,edge2"]
+	Pad[5100 -8000 5100 -6000 1500 3000 2100 "4" "4" "square"]
+	Pad[2600 -8000 2600 -6000 1500 3000 2100 "5" "5" "square"]
+	Pad[0 -8000 0 -6000 1500 3000 2100 "6" "6" "square"]
+	ElementLine [-1400 -9400 -1400 2500 1000]
+	ElementLine [-1400 2500 6600 2500 1000]
+	ElementLine [6600 2500 6600 -9400 1000]
+	ElementLine [6600 -9400 -1400 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SOT89" 418900 465000 17300 -16400 3 100 ""]
+(
+	Pad[0 -1200 0 1200 3700 3000 4300 "1" "1" "square,edge2"]
+	Pad[6100 -1200 6100 1200 3700 3000 4300 "2" "2" "square,edge2"]
+	Pad[12200 -1200 12200 1200 3700 3000 4300 "3" "3" "square,edge2"]
+	Pad[3100 -12200 9100 -12200 6100 3000 6700 "4" "4" "square"]
+	ElementLine [-3000 -16400 -3000 4300 1000]
+	ElementLine [-3000 4300 15300 4300 1000]
+	ElementLine [15300 4300 15300 -16400 1000]
+	ElementLine [15300 -16400 -3000 -16400 1000]
+
+	)
+
+Element["" "Crystals" "" "HC49U_3" 30000 620000 -15100 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square"]
+	Pin[0 -9600 6000 3000 6600 3200 "2" "2" ""]
+	Pin[0 -19200 6000 3000 6600 3200 "3" "3" ""]
+	ElementLine [-9100 -22300 -9100 3000 2000]
+	ElementLine [9200 -22300 9200 3000 2000]
+	ElementArc [0 -22300 9100 9100 180 180 2000]
+	ElementArc [0 3000 9100 9100 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49U_3H" 110000 620000 -67500 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square,edge2"]
+	Pin[0 -9600 6000 3000 6600 3200 "2" "2" "edge2"]
+	Pin[0 -19200 6000 3000 6600 3200 "3" "3" "edge2"]
+	ElementLine [-61500 -31400 -61500 12100 2000]
+	ElementLine [-61500 -31400 -10000 -31400 2000]
+	ElementLine [-10000 -31400 -10000 12100 2000]
+	ElementLine [-61500 12100 -10000 12100 2000]
+
+	)
+
+Element["" "Transistor" "" "TO126" 140000 790000 -2000 -12000 1 100 ""]
+(
+	Pin[1000 0 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[10000 0 8000 3000 8600 5200 "2" "2" ""]
+	Pin[19000 0 8000 3000 8600 5200 "3" "3" ""]
+	Pin[10000 -43000 13000 3000 13600 11000 "4" "4" ""]
+	ElementLine [0 -10000 0 0 3000]
+	ElementLine [10000 -10000 10000 0 3000]
+	ElementLine [20000 -10000 20000 0 3000]
+	ElementLine [-5000 -10000 25000 -10000 2000]
+	ElementLine [25000 -53000 25000 -10000 2000]
+	ElementLine [-5000 -53000 25000 -53000 2000]
+	ElementLine [-5000 -53000 -5000 -10000 2000]
+
+	)
+
+Element["" "Transistor" "" "TO126S" 199000 790000 -2000 -12000 1 100 ""]
+(
+	Pin[1000 0 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[10000 10000 8000 3000 8600 5200 "2" "2" ""]
+	Pin[19000 0 8000 3000 8600 5200 "3" "3" ""]
+	Pin[10000 -43000 13000 3000 13600 11000 "4" "4" ""]
+	ElementLine [0 -10000 0 0 3000]
+	ElementLine [10000 -10000 10000 10000 3000]
+	ElementLine [20000 -10000 20000 0 3000]
+	ElementLine [-5000 -10000 25000 -10000 2000]
+	ElementLine [25000 -53000 25000 -10000 2000]
+	ElementLine [-5000 -53000 25000 -53000 2000]
+	ElementLine [-5000 -53000 -5000 -10000 2000]
+
+	)
+
+Element["" "Transistor" "" "TO126SW" 40000 680000 7000 -17000 1 100 ""]
+(
+	Pin[0 -1000 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[10000 -10000 8000 3000 8600 5200 "2" "2" ""]
+	Pin[0 -19000 8000 3000 8600 5200 "3" "3" ""]
+	ElementLine [5000 -10000 10000 -10000 3000]
+	ElementLine [-5000 -25000 -5000 5000 2000]
+	ElementLine [-5000 -25000 5000 -25000 2000]
+	ElementLine [5000 -25000 5000 5000 2000]
+	ElementLine [-5000 5000 5000 5000 2000]
+	ElementLine [-5000 -5000 5000 -5000 1000]
+	ElementLine [-5000 -15000 5000 -15000 1000]
+
+	)
+
+Element["" "Transistor" "" "TO126W" 70000 680000 7000 4000 1 100 ""]
+(
+	Pin[0 -1000 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[0 -10000 8000 3000 8600 5200 "2" "2" ""]
+	Pin[0 -19000 8000 3000 8600 5200 "3" "3" ""]
+	ElementLine [-5000 -25000 -5000 5000 2000]
+	ElementLine [-5000 -25000 5000 -25000 2000]
+	ElementLine [5000 -25000 5000 5000 2000]
+	ElementLine [-5000 5000 5000 5000 2000]
+
+	)
+
+Element["" "Transistor" "" "TO18" 165000 600000 6000 7000 0 100 ""]
+(
+	Pin[0 -5000 5500 3000 6100 3500 "1" "1" ""]
+	Pin[-5000 0 5500 3000 6100 3500 "2" "2" ""]
+	Pin[0 5000 5500 3000 6100 3500 "3" "3" ""]
+	ElementLine [6700 -7900 9400 -10600 1000]
+	ElementLine [7300 -7300 10000 -10000 1000]
+	ElementLine [7900 -6700 10600 -9400 1000]
+	ElementLine [9400 -10600 10600 -9400 1000]
+	ElementArc [0 0 9800 9800 0 360 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO218" 224000 691900 -21000 -58700 0 100 ""]
+(
+	Pin[-14000 0 10000 3000 10600 6000 "1" "1" "square"]
+	Pin[-14000 -21900 10000 3000 10600 6000 "2" "2" ""]
+	Pin[-14000 -43800 10000 3000 10600 6000 "3" "3" ""]
+	ElementLine [-26000 8800 -6000 8800 2000]
+	ElementLine [-6000 -52700 -6000 8800 2000]
+	ElementLine [-26000 -52700 -6000 -52700 2000]
+	ElementLine [-26000 -52700 -26000 8800 2000]
+	ElementLine [-21000 -52700 -21000 8800 1000]
+	ElementLine [-26000 -14400 -21000 -14400 1000]
+	ElementLine [-26000 -29400 -21000 -29400 1000]
+
+	)
+
+Element["" "Transistor" "" "TO220" 90000 790000 -15000 -23000 1 100 ""]
+(
+	Pin[-10000 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[0 0 9000 3000 9600 6000 "2" "2" ""]
+	Pin[10000 0 9000 3000 9600 6000 "3" "3" ""]
+	Pin[0 -67000 15000 3000 15600 13000 "4" "4" ""]
+	ElementLine [-10000 -18000 -10000 0 3000]
+	ElementLine [0 -18000 0 0 3000]
+	ElementLine [10000 -18000 10000 0 3000]
+	ElementLine [-20000 -18000 20000 -18000 2000]
+	ElementLine [20000 -55500 20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [-20000 -55500 -20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [20000 -68000 20000 -55500 2000]
+	ElementLine [18500 -68000 20000 -68000 2000]
+	ElementLine [18500 -75000 18500 -68000 2000]
+	ElementLine [18500 -75000 20000 -75000 2000]
+	ElementLine [20000 -79000 20000 -75000 2000]
+	ElementLine [-20000 -79000 20000 -79000 2000]
+	ElementLine [-20000 -79000 -20000 -75000 2000]
+	ElementLine [-20000 -75000 -18500 -75000 2000]
+	ElementLine [-18500 -75000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -20000 -55500 2000]
+
+	)
+
+Element["" "Transistor" "" "TO220S" 30000 790000 -15000 -23000 1 100 ""]
+(
+	Pin[-10000 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[0 10000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[10000 0 9000 3000 9600 6000 "3" "3" ""]
+	Pin[0 -67000 15000 3000 15600 13000 "4" "4" ""]
+	ElementLine [-10000 -18000 -10000 0 3000]
+	ElementLine [0 -18000 0 10000 3000]
+	ElementLine [10000 -18000 10000 0 3000]
+	ElementLine [-20000 -18000 20000 -18000 2000]
+	ElementLine [20000 -55500 20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [-20000 -55500 -20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [20000 -68000 20000 -55500 2000]
+	ElementLine [18500 -68000 20000 -68000 2000]
+	ElementLine [18500 -75000 18500 -68000 2000]
+	ElementLine [18500 -75000 20000 -75000 2000]
+	ElementLine [20000 -79000 20000 -75000 2000]
+	ElementLine [-20000 -79000 20000 -79000 2000]
+	ElementLine [-20000 -79000 -20000 -75000 2000]
+	ElementLine [-20000 -75000 -18500 -75000 2000]
+	ElementLine [-18500 -75000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -20000 -55500 2000]
+
+	)
+
+Element["" "Transistor" "" "TO220SW" 130000 680000 -19000 10000 1 100 ""]
+(
+	Pin[0 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[10000 -10000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[0 -20000 9000 3000 9600 6000 "3" "3" ""]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 6000 -30000 2000]
+	ElementLine [6000 -30000 6000 10000 2000]
+	ElementLine [-12000 10000 6000 10000 2000]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 -6000 -30000 2000]
+	ElementLine [-6000 -30000 -6000 10000 2000]
+	ElementLine [-12000 10000 -6000 10000 2000]
+	ElementLine [-12000 -3000 -6000 -3000 1000]
+	ElementLine [-12000 -17000 -6000 -17000 1000]
+	ElementLine [6000 -10000 10000 -10000 3000]
+
+	)
+
+Element["" "Transistor" "" "TO220W" 100000 680000 -19000 10000 1 100 ""]
+(
+	Pin[0 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[0 -10000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[0 -20000 9000 3000 9600 6000 "3" "3" ""]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 6000 -30000 2000]
+	ElementLine [6000 -30000 6000 10000 2000]
+	ElementLine [-12000 10000 6000 10000 2000]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 -6000 -30000 2000]
+	ElementLine [-6000 -30000 -6000 10000 2000]
+	ElementLine [-12000 10000 -6000 10000 2000]
+	ElementLine [-12000 -3000 -6000 -3000 1000]
+	ElementLine [-12000 -17000 -6000 -17000 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO247" 184000 691900 -22000 -59400 0 100 ""]
+(
+	Pin[-14000 0 10000 3000 10600 6000 "1" "1" "square"]
+	Pin[-14000 -21900 10000 3000 10600 6000 "2" "2" ""]
+	Pin[-14000 -43800 10000 3000 10600 6000 "3" "3" ""]
+	ElementLine [-27000 9600 -6000 9600 2000]
+	ElementLine [-6000 -53400 -6000 9600 2000]
+	ElementLine [-27000 -53400 -6000 -53400 2000]
+	ElementLine [-27000 -53400 -27000 9600 2000]
+	ElementLine [-22000 -53400 -22000 9600 1000]
+	ElementLine [-27000 -14400 -22000 -14400 1000]
+	ElementLine [-27000 -29400 -22000 -29400 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO251" 19000 679000 -9000 -28300 0 100 ""]
+(
+	Pin[-9000 0 7000 3000 7600 4000 "1" "1" "square"]
+	Pin[-9000 -9000 7000 3000 7600 4000 "2" "2" ""]
+	Pin[-9000 -18000 7000 3000 7600 4000 "3" "3" ""]
+	ElementLine [-14000 4200 -4000 4200 2000]
+	ElementLine [-4000 -22300 -4000 4200 2000]
+	ElementLine [-14000 -22300 -4000 -22300 2000]
+	ElementLine [-14000 -22300 -14000 4200 2000]
+	ElementLine [-9000 -22300 -9000 4200 1000]
+	ElementLine [-14000 -1500 -9000 -1500 1000]
+	ElementLine [-14000 -16500 -9000 -16500 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO264" 264000 691900 -22000 -67900 0 100 ""]
+(
+	Pin[-14000 0 10000 3000 10600 6000 "1" "1" "square"]
+	Pin[-14000 -21900 10000 3000 10600 6000 "2" "2" ""]
+	Pin[-14000 -43800 10000 3000 10600 6000 "3" "3" ""]
+	ElementLine [-27000 18100 -6000 18100 2000]
+	ElementLine [-6000 -61900 -6000 18100 2000]
+	ElementLine [-27000 -61900 -6000 -61900 2000]
+	ElementLine [-27000 -61900 -27000 18100 2000]
+	ElementLine [-22000 -61900 -22000 18100 1000]
+	ElementLine [-27000 -14400 -22000 -14400 1000]
+	ElementLine [-27000 -29400 -22000 -29400 1000]
+
+	)
+
+Element["" "Transistor" "" "TO39" 210000 600000 6000 7000 0 100 ""]
+(
+	Pin[0 -10000 5500 3000 6100 3500 "1" "1" "square"]
+	Pin[-10000 0 5500 3000 6100 3500 "2" "2" ""]
+	Pin[0 10000 5500 3000 6100 3500 "3" "3" ""]
+	ElementLine [12700 -13900 14800 -16000 1000]
+	ElementLine [13300 -13300 15400 -15400 1000]
+	ElementLine [13900 -12700 16000 -14800 1000]
+	ElementLine [16000 -14800 14800 -16000 1000]
+	ElementArc [0 0 18300 18300 0 360 1000]
+
+	)
+
+Element["" "Transistor" "" "TO92" 250000 610000 -13000 -1000 1 100 ""]
+(
+	Pin[0 -20000 7200 3000 7800 4200 "1" "1" "square"]
+	Pin[0 -10000 7200 3000 7800 4200 "2" "2" ""]
+	Pin[0 0 7200 3000 7800 4200 "3" "3" ""]
+	ElementLine [-7000 -17000 -7000 -3000 1000]
+	ElementArc [0 -10000 10000 10000 45 270 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO247_2" 360000 1050000 59400 -22000 3 100 ""]
+(
+	Pin[0 -14000 10000 3000 10600 6000 "1" "1" "square,edge2"]
+	Pin[43800 -14000 10000 3000 10600 6000 "2" "2" "edge2"]
+	ElementLine [-9600 -27000 -9600 -6000 2000]
+	ElementLine [-9600 -6000 53400 -6000 2000]
+	ElementLine [53400 -6000 53400 -27000 2000]
+	ElementLine [53400 -27000 -9600 -27000 2000]
+	ElementLine [-9600 -22000 53400 -22000 1000]
+	ElementLine [14400 -27000 14400 -22000 1000]
+	ElementLine [29400 -27000 29400 -22000 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO220ACSTAND" 300000 1052000 36000 -17000 3 100 ""]
+(
+	Pin[0 -12000 8000 3000 8600 4000 "1" "1" "square,edge2"]
+	Pin[20000 -12000 8000 3000 8600 4000 "2" "2" "edge2"]
+	ElementLine [-10000 -22000 -10000 -4000 2000]
+	ElementLine [-10000 -4000 30000 -4000 2000]
+	ElementLine [30000 -4000 30000 -22000 2000]
+	ElementLine [30000 -22000 -10000 -22000 2000]
+	ElementLine [-10000 -17000 30000 -17000 1000]
+	ElementLine [2500 -22000 2500 -17000 1000]
+	ElementLine [17500 -22000 17500 -17000 1000]
+
+	)
+
+Element["" "LED, size in mm (pin 1 is +, 2 is -)" "" "LED5" 200000 1035000 7000 -10000 1 100 ""]
+(
+	Pin[0 5000 6500 3000 7100 4300 "1" "1" "square"]
+	Pin[0 -5000 6500 3000 7100 4300 "2" "2" ""]
+	ElementArc [0 0 11800 11800 90 360 1000]
+	ElementArc [0 0 13800 13800 90 360 1000]
+
+	)
+
+Element["" "LED, size in mm (pin 1 is +, 2 is -)" "" "LED3" 160000 1035000 7000 -10000 1 100 ""]
+(
+	Pin[0 5000 6500 3000 7100 4300 "1" "1" "square"]
+	Pin[0 -5000 6500 3000 7100 4300 "2" "2" ""]
+	ElementArc [0 0 5900 5900 135 90 1000]
+	ElementArc [0 0 5900 5900 315 90 1000]
+	ElementArc [0 0 7900 7900 135 90 1000]
+	ElementArc [0 0 7900 7900 315 90 1000]
+
+	)
+
+Element["" "Crystals" "" "HC51UH" 420000 980000 -103500 13600 1 100 ""]
+(
+	Pin[0 0 8000 3000 8600 4000 "1" "1" "square,edge2"]
+	Pin[0 -48500 8000 3000 8600 4000 "2" "2" "edge2"]
+	ElementLine [-97500 -62100 -97500 13600 2000]
+	ElementLine [-97500 -62100 -20000 -62100 2000]
+	ElementLine [-20000 -62100 -20000 13600 2000]
+	ElementLine [-97500 13600 -20000 13600 2000]
+
+	)
+
+Element["" "Crystals" "" "HC51U" 220000 978500 -23600 13600 1 100 ""]
+(
+	Pin[0 0 8000 3000 8600 4000 "1" "1" "square"]
+	Pin[0 -48500 8000 3000 8600 4000 "2" "2" ""]
+	ElementLine [-17600 -44500 -17600 -4000 2000]
+	ElementLine [17600 -44500 17600 -4000 2000]
+	ElementArc [0 -44500 17600 17600 180 180 2000]
+	ElementArc [0 -4000 17600 17600 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49UH" 310000 960000 -67500 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square,edge2"]
+	Pin[0 -19200 6000 3000 6600 3200 "2" "2" "edge2"]
+	ElementLine [-61500 -31400 -61500 12100 2000]
+	ElementLine [-61500 -31400 -10000 -31400 2000]
+	ElementLine [-10000 -31400 -10000 12100 2000]
+	ElementLine [-61500 12100 -10000 12100 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49U" 180000 949200 -15100 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square"]
+	Pin[0 -19200 6000 3000 6600 3200 "2" "2" ""]
+	ElementLine [-9100 -22300 -9100 3000 2000]
+	ElementLine [9200 -22300 9200 3000 2000]
+	ElementArc [0 -22300 9100 9100 180 180 2000]
+	ElementArc [0 3000 9100 9100 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49" 150000 950000 -11000 5000 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 -20000 6000 3000 6600 2800 "2" "2" ""]
+	ElementLine [-5000 -20000 -5000 0 2000]
+	ElementLine [5000 -20000 5000 0 2000]
+	ElementArc [0 -20000 5000 5000 180 180 2000]
+	ElementArc [0 0 5000 5000 0 180 2000]
+
+	)
+
+Element["" "" "" "" 245000 467500 0 0 0 100 ""]
+(
+	Pin[0 0 19685 2000 20285 11811 "" "2" ""]
+	Pin[0 -27559 19685 2000 20285 11811 "" "1" ""]
+	Pin[-17717 -13779 19685 2000 20285 11811 "" "3" ""]
+	ElementLine [-17717 -55118 -17717 0 1000]
+	ElementLine [-17717 0 17716 0 1000]
+	ElementLine [17716 0 17716 -55118 1000]
+	ElementLine [17716 -55118 -17717 -55118 1000]
+	ElementLine [-17717 -43307 17716 -43307 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "HEPTAWATT" 441400 635000 -19900 -41500 0 100 ""]
+(
+	Pin[6100 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[-13900 -5000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[6100 -10000 9000 3000 9600 6000 "3" "3" ""]
+	Pin[-13900 -15000 9000 3000 9600 6000 "4" "4" ""]
+	Pin[6100 -20000 9000 3000 9600 6000 "5" "5" ""]
+	Pin[-13900 -25000 9000 3000 9600 6000 "6" "6" ""]
+	Pin[6100 -30000 9000 3000 9600 6000 "7" "7" ""]
+	ElementLine [-24900 5400 -6000 5400 2000]
+	ElementLine [-6000 -35500 -6000 5400 2000]
+	ElementLine [-24900 -35500 -6000 -35500 2000]
+	ElementLine [-24900 -35500 -24900 5400 2000]
+	ElementLine [-19900 -35500 -19900 5400 1000]
+	ElementLine [-24900 -7500 -19900 -7500 1000]
+	ElementLine [-24900 -22500 -19900 -22500 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "MULTIWATT11" 317500 628000 79500 -21000 3 100 ""]
+(
+	Pin[0 12000 9000 3000 9600 6000 "1" "1" "square,edge2"]
+	Pin[6700 -8000 9000 3000 9600 6000 "2" "2" "edge2"]
+	Pin[13400 12000 9000 3000 9600 6000 "3" "3" "edge2"]
+	Pin[20100 -8000 9000 3000 9600 6000 "4" "4" "edge2"]
+	Pin[26800 12000 9000 3000 9600 6000 "5" "5" "edge2"]
+	Pin[33500 -8000 9000 3000 9600 6000 "6" "6" "edge2"]
+	Pin[40200 12000 9000 3000 9600 6000 "7" "7" "edge2"]
+	Pin[46900 -8000 9000 3000 9600 6000 "8" "8" "edge2"]
+	Pin[53600 12000 9000 3000 9600 6000 "9" "9" "edge2"]
+	Pin[60300 -8000 9000 3000 9600 6000 "10" "10" "edge2"]
+	Pin[67000 12000 9000 3000 9600 6000 "11" "11" "edge2"]
+	ElementLine [-6500 -26000 -6500 -6000 2000]
+	ElementLine [-6500 -6000 73500 -6000 2000]
+	ElementLine [73500 -6000 73500 -26000 2000]
+	ElementLine [73500 -26000 -6500 -26000 2000]
+	ElementLine [-6500 -21000 73500 -21000 1000]
+	ElementLine [26000 -26000 26000 -21000 1000]
+	ElementLine [41000 -26000 41000 -21000 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "MULTIWATT15" 317500 688000 81000 -21000 3 100 ""]
+(
+	Pin[0 12000 9000 3000 9600 6000 "1" "1" "square,edge2"]
+	Pin[5000 -8000 9000 3000 9600 6000 "2" "2" "edge2"]
+	Pin[10000 12000 9000 3000 9600 6000 "3" "3" "edge2"]
+	Pin[15000 -8000 9000 3000 9600 6000 "4" "4" "edge2"]
+	Pin[20000 12000 9000 3000 9600 6000 "5" "5" "edge2"]
+	Pin[25000 -8000 9000 3000 9600 6000 "6" "6" "edge2"]
+	Pin[30000 12000 9000 3000 9600 6000 "7" "7" "edge2"]
+	Pin[35000 -8000 9000 3000 9600 6000 "8" "8" "edge2"]
+	Pin[40000 12000 9000 3000 9600 6000 "9" "9" "edge2"]
+	Pin[45000 -8000 9000 3000 9600 6000 "10" "10" "edge2"]
+	Pin[50000 12000 9000 3000 9600 6000 "11" "11" "edge2"]
+	Pin[55000 -8000 9000 3000 9600 6000 "12" "12" "edge2"]
+	Pin[60000 12000 9000 3000 9600 6000 "13" "13" "edge2"]
+	Pin[65000 -8000 9000 3000 9600 6000 "14" "14" "edge2"]
+	Pin[70000 12000 9000 3000 9600 6000 "15" "15" "edge2"]
+	ElementLine [-5000 -26000 -5000 -6000 2000]
+	ElementLine [-5000 -6000 75000 -6000 2000]
+	ElementLine [75000 -6000 75000 -26000 2000]
+	ElementLine [75000 -26000 -5000 -26000 2000]
+	ElementLine [-5000 -21000 75000 -21000 1000]
+	ElementLine [27500 -26000 27500 -21000 1000]
+	ElementLine [42500 -26000 42500 -21000 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "MULTIWATT8" 317500 754500 81000 -21000 3 100 ""]
+(
+	Pin[0 -14500 9000 3000 9600 6000 "1" "1" "square,edge2"]
+	Pin[10000 -14500 9000 3000 9600 6000 "2" "2" "edge2"]
+	Pin[20000 -14500 9000 3000 9600 6000 "3" "3" "edge2"]
+	Pin[30000 -14500 9000 3000 9600 6000 "4" "4" "edge2"]
+	Pin[40000 -14500 9000 3000 9600 6000 "5" "5" "edge2"]
+	Pin[50000 -14500 9000 3000 9600 6000 "6" "6" "edge2"]
+	Pin[60000 -14500 9000 3000 9600 6000 "7" "7" "edge2"]
+	Pin[70000 -14500 9000 3000 9600 6000 "8" "8" "edge2"]
+	ElementLine [-5000 -26000 -5000 -6000 2000]
+	ElementLine [-5000 -6000 75000 -6000 2000]
+	ElementLine [75000 -6000 75000 -26000 2000]
+	ElementLine [75000 -26000 -5000 -26000 2000]
+	ElementLine [-5000 -21000 75000 -21000 1000]
+	ElementLine [27500 -26000 27500 -21000 1000]
+	ElementLine [42500 -26000 42500 -21000 1000]
+
+	)
+
+Element["" "Crystal oscillator" "" "OSC14" 427500 700000 20000 -17000 0 100 ""]
+(
+	Pin[0 0 5000 3000 5600 2800 "NC" "1" "edge2"]
+	Pin[60000 0 5000 3000 5600 2800 "GND" "2" "edge2"]
+	Pin[60000 -30000 5000 3000 5600 2800 "CLK" "3" "edge2"]
+	Pin[0 -30000 5000 3000 5600 2800 "VCC" "4" "edge2"]
+	ElementLine [-9500 -30000 -9500 9500 1000]
+	ElementLine [0 -39500 60000 -39500 1000]
+	ElementLine [69500 -30000 69500 0 1000]
+	ElementLine [-9500 9500 60000 9500 1000]
+	ElementLine [-4000 -30000 -4000 0 1000]
+	ElementLine [0 -34000 60000 -34000 1000]
+	ElementLine [64000 -30000 64000 0 1000]
+	ElementLine [0 4000 60000 4000 1000]
+	ElementArc [0 -30000 9500 9500 270 90 1000]
+	ElementArc [60000 -30000 9500 9500 180 90 1000]
+	ElementArc [60000 0 9500 9500 90 90 1000]
+	ElementArc [0 -30000 4000 4000 270 90 1000]
+	ElementArc [60000 -30000 4000 4000 180 90 1000]
+	ElementArc [60000 0 4000 4000 90 90 1000]
+	ElementArc [0 0 4000 4000 0 90 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "PENTAWATT" 494700 636700 -19900 -39900 0 100 ""]
+(
+	Pin[8500 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[-7200 -6700 9000 3000 9600 6000 "2" "2" ""]
+	Pin[8500 -13400 9000 3000 9600 6000 "3" "3" ""]
+	Pin[-7200 -20100 9000 3000 9600 6000 "4" "4" ""]
+	Pin[8500 -26800 9000 3000 9600 6000 "5" "5" ""]
+	ElementLine [-24900 7000 -6000 7000 2000]
+	ElementLine [-6000 -33900 -6000 7000 2000]
+	ElementLine [-24900 -33900 -6000 -33900 2000]
+	ElementLine [-24900 -33900 -24900 7000 2000]
+	ElementLine [-19900 -33900 -19900 7000 1000]
+	ElementLine [-24900 -5900 -19900 -5900 1000]
+	ElementLine [-24900 -20900 -19900 -20900 1000]
+
+	)
+
+Element["" "" "" "" 457500 740000 -12500 -9000 0 100 ""]
+(
+	Pin[13091 9000 9000 5000 9600 3937 "" "4" "edge2,intconn(2)"]
+	Pin[-12500 9000 9000 5000 9600 3937 "" "3" "edge2,intconn(2)"]
+	Pin[13091 -9000 9000 5000 9600 3937 "" "2" "edge2,intconn(1)"]
+	Pin[-12500 -9000 9000 5000 9600 3937 "" "1" "edge2,intconn(1)"]
+	ElementLine [12181 11736 12181 -11886 787]
+	ElementLine [-11441 11736 12181 11736 787]
+	ElementLine [-11441 -11886 -11441 11736 787]
+	ElementLine [-11441 -11886 12181 -11886 787]
+	ElementArc [7815 -8031 2756 2756 90 90 787]
+	ElementArc [7815 -8031 2756 2756 0 90 787]
+	ElementArc [7815 -8031 2756 2756 270 90 787]
+	ElementArc [7815 -8031 2756 2756 180 90 787]
+	ElementArc [7815 7717 2756 2756 0 90 787]
+	ElementArc [7815 7717 2756 2756 270 90 787]
+	ElementArc [7815 7717 2756 2756 180 90 787]
+	ElementArc [7815 7717 2756 2756 90 90 787]
+	ElementArc [-7933 7717 2756 2756 270 90 787]
+	ElementArc [-7933 7717 2756 2756 180 90 787]
+	ElementArc [-7933 7717 2756 2756 90 90 787]
+	ElementArc [-7933 7717 2756 2756 0 90 787]
+	ElementArc [-7933 -8031 2756 2756 180 90 787]
+	ElementArc [-7933 -8031 2756 2756 90 90 787]
+	ElementArc [-7933 -8031 2756 2756 0 90 787]
+	ElementArc [-7933 -8031 2756 2756 270 90 787]
+	ElementArc [-59 -157 6693 6693 270 90 787]
+	ElementArc [-59 -157 6693 6693 180 90 787]
+	ElementArc [-59 -157 6693 6693 90 90 787]
+	ElementArc [-59 -157 6693 6693 0 90 787]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "01005" 330000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-19 807 19 807 984 2000 1584 "1" "1" "square"]
+	Pad[-19 -807 19 -807 984 2000 1584 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0201" 335000 195000 -3150 3150 1 100 ""]
+(
+	Pad[0 1181 0 1181 1574 2000 2174 "1" "1" "square"]
+	Pad[0 -1181 0 -1181 1574 2000 2174 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0402" 340000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-393 1574 393 1574 1968 2000 2568 "1" "1" "square"]
+	Pad[-393 -1574 393 -1574 1968 2000 2568 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0603" 350000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-492 2559 492 2559 2952 2000 3552 "1" "1" "square"]
+	Pad[-492 -2559 492 -2559 2952 2000 3552 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0805" 360000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-393 3543 393 3543 5118 2000 5718 "1" "1" "square"]
+	Pad[-393 -3543 393 -3543 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2755 -393 -2755 393 800]
+	ElementLine [2755 -393 2755 393 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1008" 375000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-2362 5118 2362 5118 4330 2000 4930 "1" "1" "square"]
+	Pad[-2362 -5118 2362 -5118 4330 2000 4930 "2" "2" "square"]
+	ElementLine [-4527 -1377 -4527 1377 800]
+	ElementLine [4527 -1377 4527 1377 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1206" 390000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1210" 405000 195000 -3150 3150 1 100 ""]
+(
+	Pad[-2755 5905 2755 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-2755 -5905 2755 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-5314 -1968 -5314 1968 800]
+	ElementLine [5314 -1968 5314 1968 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1806" 335000 220000 -3150 3150 1 100 ""]
+(
+	Pad[-3543 7874 3543 7874 6299 2000 6899 "1" "1" "square"]
+	Pad[-3543 -7874 3543 -7874 6299 2000 6899 "2" "2" "square"]
+	ElementLine [-6692 -3149 -6692 3149 800]
+	ElementLine [6692 -3149 6692 3149 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1825" 360000 220000 -3150 3150 1 100 ""]
+(
+	Pad[-10236 7874 10236 7874 6299 2000 6899 "1" "1" "square"]
+	Pad[-10236 -7874 10236 -7874 6299 2000 6899 "2" "2" "square"]
+	ElementLine [-13385 -3149 -13385 3149 800]
+	ElementLine [13385 -3149 13385 3149 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "2706" 395000 220000 -17900 0 1 100 ""]
+(
+	Pad[10800 -300 10800 300 7800 2000 8400 "1" "1" "square"]
+	Pad[-10800 -300 -10800 300 7800 2000 8400 "2" "2" "square"]
+	ElementLine [15900 -5400 15900 5400 1000]
+	ElementLine [-15900 -5400 15900 -5400 1000]
+	ElementLine [-15900 -5400 -15900 5400 1000]
+	ElementLine [-15900 5400 15900 5400 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "DO214" 470000 270000 0 22100 2 100 ""]
+(
+	Pad[-1900 -10600 1900 -10600 14000 2000 14600 "1" "1" "square"]
+	Pad[-1900 10600 1900 10600 14000 2000 14600 "2" "2" "square"]
+	ElementLine [-8900 -21100 8900 -21100 2000]
+	ElementLine [-8900 -21100 -11400 -14100 1000]
+	ElementLine [-11400 -14100 -11400 20100 1000]
+	ElementLine [-11400 20100 11400 20100 1000]
+	ElementLine [11400 -14100 11400 20100 1000]
+	ElementLine [11400 -14100 8900 -21100 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "DO214AB" 470000 320000 0 22700 2 100 ""]
+(
+	Pad[-2000 -10900 2000 -10900 14500 2000 15100 "1" "1" "square"]
+	Pad[-2000 10900 2000 10900 14500 2000 15100 "2" "2" "square"]
+	ElementLine [-9200 -21700 9200 -21700 2000]
+	ElementLine [-9200 -21700 -11800 -14500 1000]
+	ElementLine [-11800 -14500 -11800 20700 1000]
+	ElementLine [-11800 20700 11800 20700 1000]
+	ElementLine [11800 -14500 11800 20700 1000]
+	ElementLine [11800 -14500 9200 -21700 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD106A" 425000 265000 0 16600 2 100 ""]
+(
+	Pad[-1700 -7600 1700 -7600 10200 2000 10800 "1" "1" "square"]
+	Pad[-1700 7600 1700 7600 10200 2000 10800 "2" "2" "square"]
+	ElementLine [-6800 -15600 6800 -15600 2000]
+	ElementLine [-6800 -15600 -8700 -10500 1000]
+	ElementLine [-8700 -10500 -8700 14600 1000]
+	ElementLine [-8700 14600 8700 14600 1000]
+	ElementLine [8700 -10500 8700 14600 1000]
+	ElementLine [8700 -10500 6800 -15600 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD110" 335000 265000 0 8300 2 100 ""]
+(
+	Pad[-1500 -2900 1500 -2900 4600 2000 5200 "1" "1" "square"]
+	Pad[-1500 2900 1500 2900 4600 2000 5200 "2" "2" "square"]
+	ElementLine [-3800 -7300 3800 -7300 2000]
+	ElementLine [-3800 -7300 -4900 -5000 1000]
+	ElementLine [-4900 -5000 -4900 6300 1000]
+	ElementLine [-4900 6300 4900 6300 1000]
+	ElementLine [4900 -5000 4900 6300 1000]
+	ElementLine [4900 -5000 3800 -7300 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD123" 380000 265000 0 12000 2 100 ""]
+(
+	Pad[-600 -5500 600 -5500 6900 2000 7500 "1" "1" "square"]
+	Pad[-600 5500 600 5500 6900 2000 7500 "2" "2" "square"]
+	ElementLine [-4000 -11000 4000 -11000 2000]
+	ElementLine [-4000 -11000 -5100 -7600 1000]
+	ElementLine [-5100 -7600 -5100 10000 1000]
+	ElementLine [-5100 10000 5100 10000 1000]
+	ElementLine [5100 -7600 5100 10000 1000]
+	ElementLine [5100 -7600 4000 -11000 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD323" 350000 265000 0 9300 2 100 ""]
+(
+	Pad[-1000 -3700 1000 -3700 5100 2000 5700 "1" "1" "square"]
+	Pad[-1000 3700 1000 3700 5100 2000 5700 "2" "2" "square"]
+	ElementLine [-3500 -8300 3500 -8300 2000]
+	ElementLine [-3500 -8300 -4500 -5800 1000]
+	ElementLine [-4500 -5800 -4500 7300 1000]
+	ElementLine [-4500 7300 4500 7300 1000]
+	ElementLine [4500 -5800 4500 7300 1000]
+	ElementLine [4500 -5800 3500 -8300 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD80" 365000 265000 0 11600 2 100 ""]
+(
+	Pad[-1600 -5800 1600 -5800 5300 2000 5900 "1" "1" "square"]
+	Pad[-1600 5800 1600 5800 5300 2000 5900 "2" "2" "square"]
+	ElementLine [-4300 -10600 4300 -10600 2000]
+	ElementLine [-4300 -10600 -5500 -8000 1000]
+	ElementLine [-5500 -8000 -5500 9600 1000]
+	ElementLine [-5500 9600 5500 9600 1000]
+	ElementLine [5500 -8000 5500 9600 1000]
+	ElementLine [5500 -8000 4300 -10600 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD87" 400000 265000 0 12400 2 100 ""]
+(
+	Pad[-2600 -5800 2600 -5800 6100 2000 6700 "1" "1" "square"]
+	Pad[-2600 5800 2600 5800 6100 2000 6700 "2" "2" "square"]
+	ElementLine [-5700 -11400 5700 -11400 2000]
+	ElementLine [-5700 -11400 -7300 -8400 1000]
+	ElementLine [-7300 -8400 -7300 10400 1000]
+	ElementLine [-7300 10400 7300 10400 1000]
+	ElementLine [7300 -8400 7300 10400 1000]
+	ElementLine [7300 -8400 5700 -11400 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_A" 430000 325000 0 10600 2 100 ""]
+(
+	Pad[-1800 -5000 1800 -5000 4900 2000 5500 "1" "1" "square"]
+	Pad[-1800 5000 1800 5000 4900 2000 5500 "2" "2" "square"]
+	ElementLine [-4300 -9600 4300 -9600 2000]
+	ElementLine [-4300 -9600 -5500 -7200 1000]
+	ElementLine [-5500 -7200 -5500 8600 1000]
+	ElementLine [-5500 8600 5500 8600 1000]
+	ElementLine [5500 -7200 5500 8600 1000]
+	ElementLine [5500 -7200 4300 -9600 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_B" 405000 325000 0 13200 2 100 ""]
+(
+	Pad[-4100 -5500 4100 -5500 7100 2000 7700 "1" "1" "square"]
+	Pad[-4100 5500 4100 5500 7100 2000 7700 "2" "2" "square"]
+	ElementLine [-7700 -12200 7700 -12200 2000]
+	ElementLine [-7700 -12200 -9900 -8700 1000]
+	ElementLine [-9900 -8700 -9900 11200 1000]
+	ElementLine [-9900 11200 9900 11200 1000]
+	ElementLine [9900 -8700 9900 11200 1000]
+	ElementLine [9900 -8700 7700 -12200 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_C" 375000 320000 0 18800 2 100 ""]
+(
+	Pad[-3900 -9400 3900 -9400 9700 2000 10300 "1" "1" "square"]
+	Pad[-3900 9400 3900 9400 9700 2000 10300 "2" "2" "square"]
+	ElementLine [-8700 -17800 8700 -17800 2000]
+	ElementLine [-8700 -17800 -11200 -13000 1000]
+	ElementLine [-11200 -13000 -11200 16800 1000]
+	ElementLine [-11200 16800 11200 16800 1000]
+	ElementLine [11200 -13000 11200 16800 1000]
+	ElementLine [11200 -13000 8700 -17800 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_D" 343500 316500 0 22900 2 100 ""]
+(
+	Pad[-5600 -11500 5600 -11500 12300 2000 12900 "1" "1" "square"]
+	Pad[-5600 11500 5600 11500 12300 2000 12900 "2" "2" "square"]
+	ElementLine [-11700 -21900 11700 -21900 2000]
+	ElementLine [-11700 -21900 -15000 -15800 1000]
+	ElementLine [-15000 -15800 -15000 20900 1000]
+	ElementLine [-15000 20900 15000 20900 1000]
+	ElementLine [15000 -15800 15000 20900 1000]
+	ElementLine [15000 -15800 11700 -21900 1000]
+
+	)
+
+Element["" "" "" "" 455000 207500 12913 3720 1 70 ""]
+(
+	Pad[-984 -7087 984 -7087 5118 5000 7618 "" "1" "square"]
+	Pad[-984 7087 984 7087 5118 5000 7618 "" "2" "square"]
+	ElementLine [-3150 -3937 -3150 3937 787]
+	ElementLine [3150 -3937 3150 3937 787]
+
+	)
+Layer(1 "component")
+(
+	Line[280000 400000 540000 400000 8000 5000 "clearline"]
+	Line[280000 0 280000 870000 8000 5000 "clearline"]
+	Line[0 550000 540000 550000 8000 5000 "clearline"]
+	Line[390000 550000 390000 400000 8000 5000 "clearline"]
+	Line[280000 160000 540000 160000 8000 5000 "clearline"]
+	Line[540000 0 540000 1120000 8000 5000 "clearline"]
+	Line[0 1120000 0 0 8000 5000 "clearline"]
+	Line[0 0 540000 0 8000 5000 "clearline"]
+	Line[540000 870000 0 870000 8000 5000 "clearline"]
+	Line[540000 1120000 0 1120000 8000 5000 "clearline"]
+	Text[296338 119704 0 670 "PARAMETRIC" "clearline"]
+	Text[108838 502204 0 670 "CONN" "clearline"]
+	Text[366338 349704 0 670 "SMD2" "clearline"]
+	Text[286338 499704 0 670 "SMD3" "clearline"]
+	Text[416338 499704 0 670 "SMDN" "clearline"]
+	Text[86338 819704 0 670 "TRH3" "clearline"]
+	Text[376338 809704 0 670 "TRHN" "clearline"]
+	Text[256338 1069704 0 670 "TRH2" "clearline"]
+)
+Layer(2 "solder")
+(
+	Polygon("clearpoly")
+	(
+		[5000 2500] [277500 2500] [277500 547500] [5000 547500] 
+	)
+	Polygon("clearpoly")
+	(
+		[282500 2500] [537500 2500] [537500 157500] [282500 157500] 
+	)
+	Polygon("clearpoly")
+	(
+		[282500 162500] [537500 162500] [537500 397500] [282500 397500] 
+	)
+	Polygon("clearpoly")
+	(
+		[282500 402500] [387500 402500] [387500 547500] [282500 547500] 
+	)
+	Polygon("clearpoly")
+	(
+		[392500 402500] [537500 402500] [537500 547500] [392500 547500] 
+	)
+	Polygon("clearpoly")
+	(
+		[2500 552500] [277500 552500] [277500 867500] [2500 867500] 
+	)
+	Polygon("clearpoly")
+	(
+		[282500 552500] [537500 552500] [537500 867500] [282500 867500] 
+	)
+	Polygon("clearpoly")
+	(
+		[2500 872500] [540000 872500] [540000 1120000] [2500 1120000] 
+	)
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[305000 430000 360000 430000 1000 4000 "clearline"]
+	Line[0 640000 150000 640000 1000 4000 "clearline"]
+	Line[150000 640000 150000 570000 1000 4000 "clearline"]
+	Line[260000 1010000 260000 1060000 1000 4000 "clearline"]
+	Line[130000 1010000 460000 1010000 1000 4000 "clearline"]
+	Line[325000 240000 495000 240000 1000 4000 "clearline"]
+	Line[325000 285000 455000 285000 1000 4000 "clearline"]
+	Line[420000 185000 420000 240000 1000 4000 "clearline"]
+	Line[455000 240000 455000 345000 1000 4000 "clearline"]
+	Text[340000 410000 0 200 "SC" "clearline"]
+	Text[345000 465000 1 200 "SOT" "clearline"]
+	Text[130000 600000 0 200 "HC" "clearline"]
+	Text[250000 760000 0 200 "TO" "clearline"]
+	Text[430000 1030000 0 200 "TO" "clearline"]
+	Text[230000 1030000 0 200 "LED" "clearline"]
+	Text[440000 950000 0 200 "HC" "clearline"]
+	Text[440000 330000 1 200 "TANT" "clearline"]
+	Text[440000 275000 1 200 "SOD" "clearline"]
+	Text[482500 300000 1 200 "DO" "clearline"]
+	Text[437500 222500 0 200 "minimelf" "clearline"]
+)
diff --git a/util/pcblib-map/parametric.pcb b/util/pcblib-map/parametric.pcb
new file mode 100644
index 0000000..08fd63d
--- /dev/null
+++ b/util/pcblib-map/parametric.pcb
@@ -0,0 +1,1153 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 260000 120000]
+
+Grid[10000.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin,onlynames")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[260000 120000 7874 4000 0 3150 "" ""]
+
+Element["" "rcy(150, bar-sign)" "C1" "acy150" 22500 95000 -2000 -4500 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -15000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -28500 0 -24500 1000]
+	ElementLine [0 9500 0 13500 1000]
+	ElementLine [-2000 11500 2000 11500 1000]
+	ElementLine [7874 -17883 7874 -11250 3937]
+	ElementLine [-7874 -17883 -7874 -11250 3937]
+	ElementLine [9842 -16040 9842 -11250 3937]
+	ElementLine [-9842 -16040 -9842 -11250 3937]
+	ElementLine [11811 -13006 11811 -11250 3937]
+	ElementLine [-11811 -13006 -11811 -11250 3937]
+	ElementArc [0 -7500 15000 15000 90 360 1000]
+
+	)
+
+Element["" "so(8)" "" "8*250" 57500 82500 -10000 0 1 100 ""]
+(
+	Pad[-7500 7000 -7500 13500 2000 1000 3000 "" "1" "square,edge2"]
+	Pad[-7500 -13500 -7500 -7000 2000 1000 3000 "" "8" "square"]
+	Pad[-2500 7000 -2500 13500 2000 1000 3000 "" "2" "square,edge2"]
+	Pad[-2500 -13500 -2500 -7000 2000 1000 3000 "" "7" "square"]
+	Pad[2500 7000 2500 13500 2000 1000 3000 "" "3" "square,edge2"]
+	Pad[2500 -13500 2500 -7000 2000 1000 3000 "" "6" "square"]
+	Pad[7500 7000 7500 13500 2000 1000 3000 "" "4" "square,edge2"]
+	Pad[7500 -13500 7500 -7000 2000 1000 3000 "" "5" "square"]
+	ElementLine [-10000 16000 10000 16000 1000]
+	ElementLine [10000 -16000 10000 16000 1000]
+	ElementLine [-10000 -16000 10000 -16000 1000]
+	ElementLine [-10000 2500 -10000 16000 1000]
+	ElementLine [-10000 -16000 -10000 -2500 1000]
+	ElementArc [-10000 0 2500 2500 90 180 1000]
+
+	)
+
+Element["" "dip(4)" "" "4*300" 85000 102500 -10000 0 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 0 8000 5000 8600 3937 "" "2" ""]
+	Pin[10000 -30000 8000 5000 8600 3937 "" "3" ""]
+	ElementLine [-5000 5000 15000 5000 1000]
+	ElementLine [15000 -35000 15000 5000 1000]
+	ElementLine [-5000 -35000 15000 -35000 1000]
+	ElementLine [-5000 -10000 -5000 5000 1000]
+	ElementLine [-5000 -35000 -5000 -20000 1000]
+	ElementArc [-5000 -15000 5000 5000 90 180 1000]
+
+	)
+
+Element["" "tssop(8)" "" "8*6.4mm" 117500 82500 -10000 0 1 100 ""]
+(
+	Pad[-3838 10236 -3838 14173 1771 1000 3000 "" "1" "square,edge2"]
+	Pad[-3838 -14173 -3838 -10236 1771 1000 3000 "" "8" "square"]
+	Pad[-1278 10236 -1278 14173 1771 1000 3000 "" "2" "square,edge2"]
+	Pad[-1278 -14173 -1278 -10236 1771 1000 3000 "" "7" "square"]
+	Pad[1280 10236 1280 14173 1771 1000 3000 "" "3" "square,edge2"]
+	Pad[1280 -14173 1280 -10236 1771 1000 3000 "" "6" "square"]
+	Pad[3839 10236 3839 14173 1771 1000 3000 "" "4" "square,edge2"]
+	Pad[3839 -14173 3839 -10236 1771 1000 3000 "" "5" "square"]
+	ElementLine [-6038 15973 6039 15973 1000]
+	ElementLine [6039 -15973 6039 15973 1000]
+	ElementLine [-6038 -15973 6039 -15973 1000]
+	ElementLine [-6038 2500 -6038 15973 1000]
+	ElementLine [-6038 -15973 -6038 -2500 1000]
+	ElementArc [-6038 0 2500 2500 90 180 1000]
+
+	)
+
+Element["" "connector(2,3)" "" "2*3" 142500 72500 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	Pin[0 20000 8000 5000 8600 3937 "" "3" ""]
+	Pin[10000 0 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 10000 8000 5000 8600 3937 "" "5" ""]
+	Pin[10000 20000 8000 5000 8600 3937 "" "6" ""]
+	ElementLine [-5000 -5000 -5000 25000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [15000 25000 -5000 25000 1000]
+	ElementLine [15000 25000 15000 -5000 1000]
+	ElementLine [-5000 5000 5000 5000 1000]
+	ElementLine [5000 -5000 5000 5000 1000]
+
+	)
+
+Element["" "connector(2,3,eshift=x)" "" "2*3" 175000 72500 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	Pin[0 20000 8000 5000 8600 3937 "" "3" ""]
+	Pin[10000 5000 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 15000 8000 5000 8600 3937 "" "5" ""]
+	Pin[10000 25000 8000 5000 8600 3937 "" "6" ""]
+	ElementLine [-5000 -5000 -5000 30000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [15000 30000 -5000 30000 1000]
+	ElementLine [15000 30000 15000 -5000 1000]
+	ElementLine [-5000 5000 5000 5000 1000]
+	ElementLine [5000 -5000 5000 5000 1000]
+
+	)
+
+Element["" "connector(2,3,eshift=x,etrunc=1)" "" "2*3" 207500 72500 0 -10000 0 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 10000 8000 5000 8600 3937 "" "2" ""]
+	Pin[0 20000 8000 5000 8600 3937 "" "3" ""]
+	Pin[10000 5000 8000 5000 8600 3937 "" "4" ""]
+	Pin[10000 15000 8000 5000 8600 3937 "" "5" ""]
+	ElementLine [-5000 -5000 -5000 25000 1000]
+	ElementLine [-5000 -5000 15000 -5000 1000]
+	ElementLine [15000 25000 -5000 25000 1000]
+	ElementLine [15000 25000 15000 -5000 1000]
+	ElementLine [-5000 5000 5000 5000 1000]
+	ElementLine [5000 -5000 5000 5000 1000]
+
+	)
+
+Element["" "acy(300)" "" "acy300" 20000 50000 -12000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+
+	)
+
+Element["" "acy(300, endcap)" "" "acy300" 40000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [2500 -20625 2500 -9375 1000]
+	ElementLine [2500 -9375 3000 -9166 1000]
+	ElementLine [2500 -20625 3000 -20833 1000]
+	ElementLine [-2500 -20625 -2500 -9375 1000]
+	ElementLine [-2500 -9375 -3000 -9166 1000]
+	ElementLine [-2500 -20625 -3000 -20833 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-3000 -9166 -3000 -8000 1000]
+	ElementLine [3000 -9166 3000 -8000 1000]
+	ElementLine [-3000 -22000 -3000 -20833 1000]
+	ElementLine [3000 -22000 3000 -20833 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementArc [-2500 -8000 500 500 0 90 1000]
+	ElementArc [2500 -8000 500 500 90 90 1000]
+	ElementArc [-2500 -22000 500 500 270 90 1000]
+	ElementArc [2500 -22000 500 500 180 90 1000]
+
+	)
+
+Element["" "acy(300, coil, dot)" "" "acy300" 60000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementArc [0 -9375 1875 1875 270 180 1000]
+	ElementArc [0 -13125 1875 1875 270 180 1000]
+	ElementArc [0 -16875 1875 1875 270 180 1000]
+	ElementArc [0 -20625 1875 1875 270 180 1000]
+	ElementArc [-1250 -5500 666 666 90 360 1000]
+
+	)
+
+Element["" "acy(300, pol=bar)" "" "acy300" 80000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [-2500 -6500 2500 -6500 1000]
+	ElementLine [-2500 -8500 -2500 -6500 1000]
+	ElementLine [2500 -8500 2500 -6500 1000]
+	ElementLine [-2500 -8500 2500 -8500 1000]
+
+	)
+
+Element["" "acy(300, wiper=aarrow)" "" "acy300" 100000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [5000 -12500 -8000 -17500 1000]
+	ElementLine [-8000 -17500 -6492 -15848 1000]
+	ElementLine [-8000 -17500 -5774 -17715 1000]
+	ElementLine [-5774 -17715 -6492 -15848 1000]
+
+	)
+
+Element["" "acy(300, wiper=parrow)" "" "acy300" 120000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [-8000 -15000 -2500 -15000 1000]
+	ElementLine [-2500 -15000 -4500 -16000 1000]
+	ElementLine [-2500 -15000 -4500 -14000 1000]
+	ElementLine [-4500 -16000 -4500 -14000 1000]
+
+	)
+
+Element["" "acy(300, wiper=looparrow)" "" "acy300" 140000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-2500 -7500 2500 -7500 1000]
+	ElementLine [-2500 -22500 -2500 -7500 1000]
+	ElementLine [2500 -22500 2500 -7500 1000]
+	ElementLine [-2500 -22500 2500 -22500 1000]
+	ElementLine [-8000 -15000 -2500 -15000 1000]
+	ElementLine [-2500 -15000 -4500 -16000 1000]
+	ElementLine [-2500 -15000 -4500 -14000 1000]
+	ElementLine [-4500 -16000 -4500 -14000 1000]
+	ElementLine [-8000 -24375 -8000 -15000 1000]
+	ElementLine [-8000 -24375 0 -24375 1000]
+
+	)
+
+Element["" "acy(300, zigzag)" "" "acy300" 160000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [0 -7500 -2500 -8750 1000]
+	ElementLine [-2500 -8750 2500 -11250 1000]
+	ElementLine [2500 -11250 0 -12500 1000]
+	ElementLine [0 -12500 -2500 -13750 1000]
+	ElementLine [-2500 -13750 2500 -16250 1000]
+	ElementLine [2500 -16250 0 -17500 1000]
+	ElementLine [0 -17500 -2500 -18750 1000]
+	ElementLine [-2500 -18750 2500 -21250 1000]
+	ElementLine [2500 -21250 0 -22500 1000]
+
+	)
+
+Element["" "acy(300, core)" "" "acy300" 180000 50000 -2000 -9000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -7500 0 0 1000]
+	ElementLine [0 -30000 0 -22500 1000]
+	ElementLine [-4875 -22500 -4875 -7500 1000]
+	ElementArc [0 -9375 1875 1875 270 180 1000]
+	ElementArc [0 -13125 1875 1875 270 180 1000]
+	ElementArc [0 -16875 1875 1875 270 180 1000]
+	ElementArc [0 -20625 1875 1875 270 180 1000]
+
+	)
+
+Element["" "acy(200, standing, pol=sign, dia=100)" "" "acy200" 240000 90000 -2000 -6000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -20000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -5000 0 0 1000]
+	ElementLine [0 -20000 0 -15000 1000]
+	ElementLine [0 -20000 0 0 1000]
+	ElementLine [2200 -6100 2200 -4100 1000]
+	ElementLine [2200 -15900 2200 -13900 1000]
+	ElementLine [1200 -14900 3200 -14900 1000]
+	ElementArc [0 0 10000 10000 90 360 1000]
+
+	)
+
+Element["" "alf(300,schottky)" "" "acy300" 200000 50000 -5000 -22000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -10000 0 0 1000]
+	ElementLine [0 -30000 0 -20000 1000]
+	ElementLine [-5000 -20000 5000 -20000 1000]
+	ElementLine [-5000 -20000 0 -10000 1000]
+	ElementLine [5000 -20000 0 -10000 1000]
+	ElementLine [-3500 -10000 3500 -10000 1000]
+	ElementArc [-3500 -11500 1500 1500 270 180 1000]
+	ElementArc [3500 -8500 1500 1500 90 180 1000]
+
+	)
+
+Element["" "alf(300,tunnel)" "" "acy300" 220000 50000 -5000 -22000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -10000 0 0 1000]
+	ElementLine [0 -30000 0 -20000 1000]
+	ElementLine [-5000 -20000 5000 -20000 1000]
+	ElementLine [-5000 -20000 0 -10000 1000]
+	ElementLine [5000 -20000 0 -10000 1000]
+	ElementLine [-5000 -10000 5000 -10000 1000]
+	ElementLine [5000 -11500 5000 -10000 1000]
+	ElementLine [-5000 -11500 -5000 -10000 1000]
+
+	)
+
+Element["" "alf(300,zener)" "" "acy300" 240000 50000 -5000 -22000 1 100 ""]
+(
+	Pin[0 0 8000 5000 8600 3937 "" "1" "square"]
+	Pin[0 -30000 8000 5000 8600 3937 "" "2" ""]
+	ElementLine [0 -10000 0 0 1000]
+	ElementLine [0 -30000 0 -20000 1000]
+	ElementLine [-5000 -20000 5000 -20000 1000]
+	ElementLine [-5000 -20000 0 -10000 1000]
+	ElementLine [5000 -20000 0 -10000 1000]
+	ElementLine [-5000 -10000 5000 -10000 1000]
+	ElementLine [5000 -10000 5000 -8500 1000]
+	ElementLine [-5000 -11500 -5000 -10000 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/util/pcblib-map/smd2.pcb b/util/pcblib-map/smd2.pcb
new file mode 100644
index 0000000..88e6ff5
--- /dev/null
+++ b/util/pcblib-map/smd2.pcb
@@ -0,0 +1,1086 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 170000 160000]
+
+Grid[2500.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[170000 160000 7874 4000 0 3150 "" ""]
+
+Element["" "Standard SMT resistor, capacitor etc" "" "01005" 5000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-19 807 19 807 984 2000 1584 "1" "1" "square"]
+	Pad[-19 -807 19 -807 984 2000 1584 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0201" 10000 10000 -3150 3150 1 100 ""]
+(
+	Pad[0 1181 0 1181 1574 2000 2174 "1" "1" "square"]
+	Pad[0 -1181 0 -1181 1574 2000 2174 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0402" 15000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-393 1574 393 1574 1968 2000 2568 "1" "1" "square"]
+	Pad[-393 -1574 393 -1574 1968 2000 2568 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0603" 25000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-492 2559 492 2559 2952 2000 3552 "1" "1" "square"]
+	Pad[-492 -2559 492 -2559 2952 2000 3552 "2" "2" "square"]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "0805" 35000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-393 3543 393 3543 5118 2000 5718 "1" "1" "square"]
+	Pad[-393 -3543 393 -3543 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-2755 -393 -2755 393 800]
+	ElementLine [2755 -393 2755 393 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1008" 50000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-2362 5118 2362 5118 4330 2000 4930 "1" "1" "square"]
+	Pad[-2362 -5118 2362 -5118 4330 2000 4930 "2" "2" "square"]
+	ElementLine [-4527 -1377 -4527 1377 800]
+	ElementLine [4527 -1377 4527 1377 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1206" 65000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-1181 5905 1181 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-1181 -5905 1181 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-3740 -2362 -3740 2362 800]
+	ElementLine [3740 -2362 3740 2362 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1210" 80000 10000 -3150 3150 1 100 ""]
+(
+	Pad[-2755 5905 2755 5905 5118 2000 5718 "1" "1" "square"]
+	Pad[-2755 -5905 2755 -5905 5118 2000 5718 "2" "2" "square"]
+	ElementLine [-5314 -1968 -5314 1968 800]
+	ElementLine [5314 -1968 5314 1968 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1806" 10000 35000 -3150 3150 1 100 ""]
+(
+	Pad[-3543 7874 3543 7874 6299 2000 6899 "1" "1" "square"]
+	Pad[-3543 -7874 3543 -7874 6299 2000 6899 "2" "2" "square"]
+	ElementLine [-6692 -3149 -6692 3149 800]
+	ElementLine [6692 -3149 6692 3149 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "1825" 35000 35000 -3150 3150 1 100 ""]
+(
+	Pad[-10236 7874 10236 7874 6299 2000 6899 "1" "1" "square"]
+	Pad[-10236 -7874 10236 -7874 6299 2000 6899 "2" "2" "square"]
+	ElementLine [-13385 -3149 -13385 3149 800]
+	ElementLine [13385 -3149 13385 3149 800]
+
+	)
+
+Element["" "Standard SMT resistor, capacitor etc" "" "2706" 70000 35000 -17900 0 1 100 ""]
+(
+	Pad[10800 -300 10800 300 7800 2000 8400 "1" "1" "square"]
+	Pad[-10800 -300 -10800 300 7800 2000 8400 "2" "2" "square"]
+	ElementLine [15900 -5400 15900 5400 1000]
+	ElementLine [-15900 -5400 15900 -5400 1000]
+	ElementLine [-15900 -5400 -15900 5400 1000]
+	ElementLine [-15900 5400 15900 5400 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "DO214" 145000 85000 0 22100 2 100 ""]
+(
+	Pad[-1900 -10600 1900 -10600 14000 2000 14600 "1" "1" "square"]
+	Pad[-1900 10600 1900 10600 14000 2000 14600 "2" "2" "square"]
+	ElementLine [-8900 -21100 8900 -21100 2000]
+	ElementLine [-8900 -21100 -11400 -14100 1000]
+	ElementLine [-11400 -14100 -11400 20100 1000]
+	ElementLine [-11400 20100 11400 20100 1000]
+	ElementLine [11400 -14100 11400 20100 1000]
+	ElementLine [11400 -14100 8900 -21100 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "DO214AB" 145000 135000 0 22700 2 100 ""]
+(
+	Pad[-2000 -10900 2000 -10900 14500 2000 15100 "1" "1" "square"]
+	Pad[-2000 10900 2000 10900 14500 2000 15100 "2" "2" "square"]
+	ElementLine [-9200 -21700 9200 -21700 2000]
+	ElementLine [-9200 -21700 -11800 -14500 1000]
+	ElementLine [-11800 -14500 -11800 20700 1000]
+	ElementLine [-11800 20700 11800 20700 1000]
+	ElementLine [11800 -14500 11800 20700 1000]
+	ElementLine [11800 -14500 9200 -21700 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD106A" 100000 80000 0 16600 2 100 ""]
+(
+	Pad[-1700 -7600 1700 -7600 10200 2000 10800 "1" "1" "square"]
+	Pad[-1700 7600 1700 7600 10200 2000 10800 "2" "2" "square"]
+	ElementLine [-6800 -15600 6800 -15600 2000]
+	ElementLine [-6800 -15600 -8700 -10500 1000]
+	ElementLine [-8700 -10500 -8700 14600 1000]
+	ElementLine [-8700 14600 8700 14600 1000]
+	ElementLine [8700 -10500 8700 14600 1000]
+	ElementLine [8700 -10500 6800 -15600 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD110" 10000 80000 0 8300 2 100 ""]
+(
+	Pad[-1500 -2900 1500 -2900 4600 2000 5200 "1" "1" "square"]
+	Pad[-1500 2900 1500 2900 4600 2000 5200 "2" "2" "square"]
+	ElementLine [-3800 -7300 3800 -7300 2000]
+	ElementLine [-3800 -7300 -4900 -5000 1000]
+	ElementLine [-4900 -5000 -4900 6300 1000]
+	ElementLine [-4900 6300 4900 6300 1000]
+	ElementLine [4900 -5000 4900 6300 1000]
+	ElementLine [4900 -5000 3800 -7300 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD123" 55000 80000 0 12000 2 100 ""]
+(
+	Pad[-600 -5500 600 -5500 6900 2000 7500 "1" "1" "square"]
+	Pad[-600 5500 600 5500 6900 2000 7500 "2" "2" "square"]
+	ElementLine [-4000 -11000 4000 -11000 2000]
+	ElementLine [-4000 -11000 -5100 -7600 1000]
+	ElementLine [-5100 -7600 -5100 10000 1000]
+	ElementLine [-5100 10000 5100 10000 1000]
+	ElementLine [5100 -7600 5100 10000 1000]
+	ElementLine [5100 -7600 4000 -11000 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD323" 25000 80000 0 9300 2 100 ""]
+(
+	Pad[-1000 -3700 1000 -3700 5100 2000 5700 "1" "1" "square"]
+	Pad[-1000 3700 1000 3700 5100 2000 5700 "2" "2" "square"]
+	ElementLine [-3500 -8300 3500 -8300 2000]
+	ElementLine [-3500 -8300 -4500 -5800 1000]
+	ElementLine [-4500 -5800 -4500 7300 1000]
+	ElementLine [-4500 7300 4500 7300 1000]
+	ElementLine [4500 -5800 4500 7300 1000]
+	ElementLine [4500 -5800 3500 -8300 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD80" 40000 80000 0 11600 2 100 ""]
+(
+	Pad[-1600 -5800 1600 -5800 5300 2000 5900 "1" "1" "square"]
+	Pad[-1600 5800 1600 5800 5300 2000 5900 "2" "2" "square"]
+	ElementLine [-4300 -10600 4300 -10600 2000]
+	ElementLine [-4300 -10600 -5500 -8000 1000]
+	ElementLine [-5500 -8000 -5500 9600 1000]
+	ElementLine [-5500 9600 5500 9600 1000]
+	ElementLine [5500 -8000 5500 9600 1000]
+	ElementLine [5500 -8000 4300 -10600 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOD87" 75000 80000 0 12400 2 100 ""]
+(
+	Pad[-2600 -5800 2600 -5800 6100 2000 6700 "1" "1" "square"]
+	Pad[-2600 5800 2600 5800 6100 2000 6700 "2" "2" "square"]
+	ElementLine [-5700 -11400 5700 -11400 2000]
+	ElementLine [-5700 -11400 -7300 -8400 1000]
+	ElementLine [-7300 -8400 -7300 10400 1000]
+	ElementLine [-7300 10400 7300 10400 1000]
+	ElementLine [7300 -8400 7300 10400 1000]
+	ElementLine [7300 -8400 5700 -11400 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_A" 105000 140000 0 10600 2 100 ""]
+(
+	Pad[-1800 -5000 1800 -5000 4900 2000 5500 "1" "1" "square"]
+	Pad[-1800 5000 1800 5000 4900 2000 5500 "2" "2" "square"]
+	ElementLine [-4300 -9600 4300 -9600 2000]
+	ElementLine [-4300 -9600 -5500 -7200 1000]
+	ElementLine [-5500 -7200 -5500 8600 1000]
+	ElementLine [-5500 8600 5500 8600 1000]
+	ElementLine [5500 -7200 5500 8600 1000]
+	ElementLine [5500 -7200 4300 -9600 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_B" 80000 140000 0 13200 2 100 ""]
+(
+	Pad[-4100 -5500 4100 -5500 7100 2000 7700 "1" "1" "square"]
+	Pad[-4100 5500 4100 5500 7100 2000 7700 "2" "2" "square"]
+	ElementLine [-7700 -12200 7700 -12200 2000]
+	ElementLine [-7700 -12200 -9900 -8700 1000]
+	ElementLine [-9900 -8700 -9900 11200 1000]
+	ElementLine [-9900 11200 9900 11200 1000]
+	ElementLine [9900 -8700 9900 11200 1000]
+	ElementLine [9900 -8700 7700 -12200 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_C" 50000 135000 0 18800 2 100 ""]
+(
+	Pad[-3900 -9400 3900 -9400 9700 2000 10300 "1" "1" "square"]
+	Pad[-3900 9400 3900 9400 9700 2000 10300 "2" "2" "square"]
+	ElementLine [-8700 -17800 8700 -17800 2000]
+	ElementLine [-8700 -17800 -11200 -13000 1000]
+	ElementLine [-11200 -13000 -11200 16800 1000]
+	ElementLine [-11200 16800 11200 16800 1000]
+	ElementLine [11200 -13000 11200 16800 1000]
+	ElementLine [11200 -13000 8700 -17800 1000]
+
+	)
+
+Element["" "Tantalum SMT capacitor (pin 1 is +)" "" "TANT_D" 18500 131500 0 22900 2 100 ""]
+(
+	Pad[-5600 -11500 5600 -11500 12300 2000 12900 "1" "1" "square"]
+	Pad[-5600 11500 5600 11500 12300 2000 12900 "2" "2" "square"]
+	ElementLine [-11700 -21900 11700 -21900 2000]
+	ElementLine [-11700 -21900 -15000 -15800 1000]
+	ElementLine [-15000 -15800 -15000 20900 1000]
+	ElementLine [-15000 20900 15000 20900 1000]
+	ElementLine [15000 -15800 15000 20900 1000]
+	ElementLine [15000 -15800 11700 -21900 1000]
+
+	)
+
+Element["" "" "" "" 130000 22500 12913 3720 1 70 ""]
+(
+	Pad[-984 -7087 984 -7087 5118 5000 7618 "" "1" "square"]
+	Pad[-984 7087 984 7087 5118 5000 7618 "" "2" "square"]
+	ElementLine [-3150 -3937 -3150 3937 787]
+	ElementLine [3150 -3937 3150 3937 787]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[0 55000 170000 55000 1000 4000 "clearline"]
+	Line[0 100000 130000 100000 1000 4000 "clearline"]
+	Line[95000 0 95000 55000 1000 4000 "clearline"]
+	Line[130000 55000 130000 160000 1000 4000 "clearline"]
+	Text[115000 145000 1 200 "TANT" "clearline"]
+	Text[115000 90000 1 200 "SOD" "clearline"]
+	Text[157500 115000 1 200 "DO" "clearline"]
+	Text[112500 37500 0 200 "minimelf" "clearline"]
+)
diff --git a/util/pcblib-map/smd3.pcb b/util/pcblib-map/smd3.pcb
new file mode 100644
index 0000000..82b4d60
--- /dev/null
+++ b/util/pcblib-map/smd3.pcb
@@ -0,0 +1,895 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 55000 75000]
+
+Grid[5000.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[55000 75000 7874 4000 0 3150 "" ""]
+
+Element["" "SMT transistor, 3 pins" "" "SC70_3" 10000 15000 9300 -9400 3 100 ""]
+(
+	Pad[0 -300 0 300 2900 3000 3500 "1" "1" "square,edge2"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[2600 -7300 2600 -6700 2900 3000 3500 "3" "3" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SC90" 25000 15000 7600 -7800 3 100 ""]
+(
+	Pad[0 -200 0 200 2400 3000 3000 "1" "1" "square,edge2"]
+	Pad[3900 -200 3900 200 2400 3000 3000 "2" "2" "square,edge2"]
+	Pad[1900 -6100 1900 -5700 2400 3000 3000 "3" "3" "square"]
+	ElementLine [-1700 -7800 -1700 2000 1000]
+	ElementLine [-1700 2000 5600 2000 1000]
+	ElementLine [5600 2000 5600 -7800 1000]
+	ElementLine [5600 -7800 -1700 -7800 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SOT23" 6100 48200 12300 -11000 3 100 ""]
+(
+	Pad[0 -300 0 300 3400 3000 4000 "1" "1" "square,edge2"]
+	Pad[7800 -300 7800 300 3400 3000 4000 "2" "2" "square,edge2"]
+	Pad[3900 -8500 3900 -7900 3400 3000 4000 "3" "3" "square"]
+	ElementLine [-2500 -11000 -2500 2900 1000]
+	ElementLine [-2500 2900 10300 2900 1000]
+	ElementLine [10300 2900 10300 -11000 1000]
+	ElementLine [10300 -11000 -2500 -11000 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOT23D" 6100 68200 12300 -11000 3 100 ""]
+(
+	Pad[0 -300 0 300 3400 3000 4000 "2" "2" "square,edge2"]
+	Pad[7800 -300 7800 300 3400 3000 4000 "3" "3" "square,edge2"]
+	Pad[3900 -8500 3900 -7900 3400 3000 4000 "1" "1" "square"]
+	ElementLine [-2500 -11000 -2500 2900 1000]
+	ElementLine [-2500 2900 10300 2900 1000]
+	ElementLine [10300 2900 10300 -11000 1000]
+	ElementLine [10300 -11000 -2500 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 3 pins" "" "SOT323" 27400 47000 9300 -9400 3 100 ""]
+(
+	Pad[0 -300 0 300 2900 3000 3500 "1" "1" "square,edge2"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[2600 -7300 2600 -6700 2900 3000 3500 "3" "3" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "SMT diode (pin 1 is cathode)" "" "SOT323D" 27400 67000 9300 -9400 3 100 ""]
+(
+	Pad[0 -300 0 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "3" "3" "square,edge2"]
+	Pad[2600 -7300 2600 -6700 2900 3000 3500 "1" "1" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[0 25000 55000 25000 1000 4000 "clearline"]
+	Text[35000 5000 0 200 "SC" "clearline"]
+	Text[40000 60000 1 200 "SOT" "clearline"]
+)
diff --git a/util/pcblib-map/smdN.pcb b/util/pcblib-map/smdN.pcb
new file mode 100644
index 0000000..93cdd8e
--- /dev/null
+++ b/util/pcblib-map/smdN.pcb
@@ -0,0 +1,944 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 100000 75000]
+
+Grid[5000.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[100000 75000 7874 4000 0 3150 "" ""]
+
+Element["" "SMT transistor, 4 pins" "" "SC70_4" 5000 15000 9300 -9400 3 100 ""]
+(
+	Pad[300 0 600 0 2900 3000 3500 "1" "1" "square"]
+	Pad[5100 -300 5100 300 2900 3000 3500 "2" "2" "square,edge2"]
+	Pad[5100 -7300 5100 -6700 2900 3000 3500 "3" "3" "square"]
+	Pad[0 -7300 0 -6700 2900 3000 3500 "4" "4" "square"]
+	ElementLine [-2100 -9400 -2100 2500 1000]
+	ElementLine [-2100 2500 7300 2500 1000]
+	ElementLine [7300 2500 7300 -9400 1000]
+	ElementLine [7300 -9400 -2100 -9400 1000]
+
+	)
+
+Element["" "Pressure transducer" "" "MPAK" 75000 60000 20300 -49700 3 100 ""]
+(
+	Pad[0 -2800 0 2800 3100 3000 3700 "1" "1" "square,edge2"]
+	Pad[5000 -2800 5000 2800 3100 3000 3700 "2" "2" "square,edge2"]
+	Pad[10000 -2800 10000 2800 3100 3000 3700 "3" "3" "square,edge2"]
+	Pad[15000 -2800 15000 2800 3100 3000 3700 "4" "4" "square,edge2"]
+	Pad[3800 -43700 11200 -43700 8700 3000 9300 "5" "5" "square"]
+	ElementLine [-3200 -49700 -3200 6100 1000]
+	ElementLine [-3200 6100 18300 6100 1000]
+	ElementLine [18300 6100 18300 -49700 1000]
+	ElementLine [18300 -49700 -3200 -49700 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SOT143" 20000 15000 11900 -11000 3 100 ""]
+(
+	Pad[300 0 600 0 3400 3000 4000 "1" "1" "square"]
+	Pad[7400 -300 7400 300 3400 3000 4000 "2" "2" "square,edge2"]
+	Pad[7400 -8500 7400 -7900 3400 3000 4000 "3" "3" "square"]
+	Pad[0 -8500 0 -7900 3400 3000 4000 "4" "4" "square"]
+	ElementLine [-2500 -11000 -2500 2900 1000]
+	ElementLine [-2500 2900 9900 2900 1000]
+	ElementLine [9900 2900 9900 -11000 1000]
+	ElementLine [9900 -11000 -2500 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SOT223" 40000 60000 25300 -32900 3 100 ""]
+(
+	Pad[0 -3300 0 3300 5600 3000 6200 "1" "1" "square,edge2"]
+	Pad[9000 -3300 9000 3300 5600 3000 6200 "2" "2" "square,edge2"]
+	Pad[18100 -3300 18100 3300 5600 3000 6200 "3" "3" "square,edge2"]
+	Pad[4500 -24400 13500 -24400 12200 3000 12800 "4" "4" "square"]
+	ElementLine [-5200 -32900 -5200 8500 1000]
+	ElementLine [-5200 8500 23300 8500 1000]
+	ElementLine [23300 8500 23300 -32900 1000]
+	ElementLine [23300 -32900 -5200 -32900 1000]
+
+	)
+
+Element["" "SMT transistor, 5 pins" "" "SOT25" 35000 15000 11800 -11000 3 100 ""]
+(
+	Pad[0 -800 0 800 2400 3000 3000 "1" "1" "square,edge2"]
+	Pad[7800 -800 7800 800 2400 3000 3000 "2" "2" "square,edge2"]
+	Pad[7800 -9000 7800 -7400 2400 3000 3000 "3" "3" "square"]
+	Pad[3900 -9000 3900 -7400 2400 3000 3000 "4" "4" "square"]
+	Pad[0 -9000 0 -7400 2400 3000 3000 "5" "5" "square"]
+	ElementLine [-2000 -11000 -2000 2900 1000]
+	ElementLine [-2000 2900 9800 2900 1000]
+	ElementLine [9800 2900 9800 -11000 1000]
+	ElementLine [9800 -11000 -2000 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 6 pins" "" "SOT26" 51100 15000 11800 -11000 3 100 ""]
+(
+	Pad[0 -800 0 800 2400 3000 3000 "1" "1" "square,edge2"]
+	Pad[3900 -800 3900 800 2400 3000 3000 "2" "2" "square,edge2"]
+	Pad[7800 -800 7800 800 2400 3000 3000 "3" "3" "square,edge2"]
+	Pad[7800 -9000 7800 -7400 2400 3000 3000 "4" "4" "square"]
+	Pad[3900 -9000 3900 -7400 2400 3000 3000 "5" "5" "square"]
+	Pad[0 -9000 0 -7400 2400 3000 3000 "6" "6" "square"]
+	ElementLine [-2000 -11000 -2000 2900 1000]
+	ElementLine [-2000 2900 9800 2900 1000]
+	ElementLine [9800 2900 9800 -11000 1000]
+	ElementLine [9800 -11000 -2000 -11000 1000]
+
+	)
+
+Element["" "SMT transistor, 5 pins" "" "SOT325" 9900 65000 8600 -9400 3 100 ""]
+(
+	Pad[0 -1000 0 1000 1500 3000 2100 "1" "1" "square,edge2"]
+	Pad[5100 -1000 5100 1000 1500 3000 2100 "2" "2" "square,edge2"]
+	Pad[5100 -8000 5100 -6000 1500 3000 2100 "3" "3" "square"]
+	Pad[2600 -8000 2600 -6000 1500 3000 2100 "4" "4" "square"]
+	Pad[0 -8000 0 -6000 1500 3000 2100 "5" "5" "square"]
+	ElementLine [-1400 -9400 -1400 2500 1000]
+	ElementLine [-1400 2500 6600 2500 1000]
+	ElementLine [6600 2500 6600 -9400 1000]
+	ElementLine [6600 -9400 -1400 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 6 pins" "" "SOT326" 22400 65000 8600 -9400 3 100 ""]
+(
+	Pad[0 -1000 0 1000 1500 3000 2100 "1" "1" "square,edge2"]
+	Pad[2600 -1000 2600 1000 1500 3000 2100 "2" "2" "square,edge2"]
+	Pad[5100 -1000 5100 1000 1500 3000 2100 "3" "3" "square,edge2"]
+	Pad[5100 -8000 5100 -6000 1500 3000 2100 "4" "4" "square"]
+	Pad[2600 -8000 2600 -6000 1500 3000 2100 "5" "5" "square"]
+	Pad[0 -8000 0 -6000 1500 3000 2100 "6" "6" "square"]
+	ElementLine [-1400 -9400 -1400 2500 1000]
+	ElementLine [-1400 2500 6600 2500 1000]
+	ElementLine [6600 2500 6600 -9400 1000]
+	ElementLine [6600 -9400 -1400 -9400 1000]
+
+	)
+
+Element["" "SMT transistor, 4 pins" "" "SOT89" 8900 45000 17300 -16400 3 100 ""]
+(
+	Pad[0 -1200 0 1200 3700 3000 4300 "1" "1" "square,edge2"]
+	Pad[6100 -1200 6100 1200 3700 3000 4300 "2" "2" "square,edge2"]
+	Pad[12200 -1200 12200 1200 3700 3000 4300 "3" "3" "square,edge2"]
+	Pad[3100 -12200 9100 -12200 6100 3000 6700 "4" "4" "square"]
+	ElementLine [-3000 -16400 -3000 4300 1000]
+	ElementLine [-3000 4300 15300 4300 1000]
+	ElementLine [15300 4300 15300 -16400 1000]
+	ElementLine [15300 -16400 -3000 -16400 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/util/pcblib-map/trh2.pcb b/util/pcblib-map/trh2.pcb
new file mode 100644
index 0000000..4ef84d4
--- /dev/null
+++ b/util/pcblib-map/trh2.pcb
@@ -0,0 +1,928 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 330000 150000]
+
+Grid[10000.0 0 0 1]
+Cursor[10000 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[330000 150000 7874 4000 0 3150 "" ""]
+
+Element["" "Crystals" "" "HC49" 20000 40000 -11000 5000 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 2800 "1" "1" "square"]
+	Pin[0 -20000 6000 3000 6600 2800 "2" "2" ""]
+	ElementLine [-5000 -20000 -5000 0 2000]
+	ElementLine [5000 -20000 5000 0 2000]
+	ElementArc [0 -20000 5000 5000 180 180 2000]
+	ElementArc [0 0 5000 5000 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49U" 50000 39200 -15100 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square"]
+	Pin[0 -19200 6000 3000 6600 3200 "2" "2" ""]
+	ElementLine [-9100 -22300 -9100 3000 2000]
+	ElementLine [9200 -22300 9200 3000 2000]
+	ElementArc [0 -22300 9100 9100 180 180 2000]
+	ElementArc [0 3000 9100 9100 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49UH" 180000 50000 -67500 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square,edge2"]
+	Pin[0 -19200 6000 3000 6600 3200 "2" "2" "edge2"]
+	ElementLine [-61500 -31400 -61500 12100 2000]
+	ElementLine [-61500 -31400 -10000 -31400 2000]
+	ElementLine [-10000 -31400 -10000 12100 2000]
+	ElementLine [-61500 12100 -10000 12100 2000]
+
+	)
+
+Element["" "Crystals" "" "HC51U" 90000 68500 -23600 13600 1 100 ""]
+(
+	Pin[0 0 8000 3000 8600 4000 "1" "1" "square"]
+	Pin[0 -48500 8000 3000 8600 4000 "2" "2" ""]
+	ElementLine [-17600 -44500 -17600 -4000 2000]
+	ElementLine [17600 -44500 17600 -4000 2000]
+	ElementArc [0 -44500 17600 17600 180 180 2000]
+	ElementArc [0 -4000 17600 17600 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC51UH" 290000 70000 -103500 13600 1 100 ""]
+(
+	Pin[0 0 8000 3000 8600 4000 "1" "1" "square,edge2"]
+	Pin[0 -48500 8000 3000 8600 4000 "2" "2" "edge2"]
+	ElementLine [-97500 -62100 -97500 13600 2000]
+	ElementLine [-97500 -62100 -20000 -62100 2000]
+	ElementLine [-20000 -62100 -20000 13600 2000]
+	ElementLine [-97500 13600 -20000 13600 2000]
+
+	)
+
+Element["" "LED, size in mm (pin 1 is +, 2 is -)" "" "LED3" 30000 125000 7000 -10000 1 100 ""]
+(
+	Pin[0 5000 6500 3000 7100 4300 "1" "1" "square"]
+	Pin[0 -5000 6500 3000 7100 4300 "2" "2" ""]
+	ElementArc [0 0 5900 5900 135 90 1000]
+	ElementArc [0 0 5900 5900 315 90 1000]
+	ElementArc [0 0 7900 7900 135 90 1000]
+	ElementArc [0 0 7900 7900 315 90 1000]
+
+	)
+
+Element["" "LED, size in mm (pin 1 is +, 2 is -)" "" "LED5" 70000 125000 7000 -10000 1 100 ""]
+(
+	Pin[0 5000 6500 3000 7100 4300 "1" "1" "square"]
+	Pin[0 -5000 6500 3000 7100 4300 "2" "2" ""]
+	ElementArc [0 0 11800 11800 90 360 1000]
+	ElementArc [0 0 13800 13800 90 360 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO220ACSTAND" 170000 142000 36000 -17000 3 100 ""]
+(
+	Pin[0 -12000 8000 3000 8600 4000 "1" "1" "square,edge2"]
+	Pin[20000 -12000 8000 3000 8600 4000 "2" "2" "edge2"]
+	ElementLine [-10000 -22000 -10000 -4000 2000]
+	ElementLine [-10000 -4000 30000 -4000 2000]
+	ElementLine [30000 -4000 30000 -22000 2000]
+	ElementLine [30000 -22000 -10000 -22000 2000]
+	ElementLine [-10000 -17000 30000 -17000 1000]
+	ElementLine [2500 -22000 2500 -17000 1000]
+	ElementLine [17500 -22000 17500 -17000 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO247_2" 230000 140000 59400 -22000 3 100 ""]
+(
+	Pin[0 -14000 10000 3000 10600 6000 "1" "1" "square,edge2"]
+	Pin[43800 -14000 10000 3000 10600 6000 "2" "2" "edge2"]
+	ElementLine [-9600 -27000 -9600 -6000 2000]
+	ElementLine [-9600 -6000 53400 -6000 2000]
+	ElementLine [53400 -6000 53400 -27000 2000]
+	ElementLine [53400 -27000 -9600 -27000 2000]
+	ElementLine [-9600 -22000 53400 -22000 1000]
+	ElementLine [14400 -27000 14400 -22000 1000]
+	ElementLine [29400 -27000 29400 -22000 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[0 100000 330000 100000 1000 4000 "clearline"]
+	Line[130000 100000 130000 150000 1000 4000 "clearline"]
+	Text[310000 40000 0 200 "HC" "clearline"]
+	Text[100000 120000 0 200 "LED" "clearline"]
+	Text[300000 120000 0 200 "TO" "clearline"]
+)
diff --git a/util/pcblib-map/trh3.pcb b/util/pcblib-map/trh3.pcb
new file mode 100644
index 0000000..dbf123a
--- /dev/null
+++ b/util/pcblib-map/trh3.pcb
@@ -0,0 +1,1096 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 270000 240000]
+
+Grid[10000.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[270000 240000 7874 4000 0 3150 "" ""]
+
+Element["" "Crystals" "" "HC49U_3" 30000 50000 -15100 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square"]
+	Pin[0 -9600 6000 3000 6600 3200 "2" "2" ""]
+	Pin[0 -19200 6000 3000 6600 3200 "3" "3" ""]
+	ElementLine [-9100 -22300 -9100 3000 2000]
+	ElementLine [9200 -22300 9200 3000 2000]
+	ElementArc [0 -22300 9100 9100 180 180 2000]
+	ElementArc [0 3000 9100 9100 0 180 2000]
+
+	)
+
+Element["" "Crystals" "" "HC49U_3H" 110000 50000 -67500 12100 1 100 ""]
+(
+	Pin[0 0 6000 3000 6600 3200 "1" "1" "square,edge2"]
+	Pin[0 -9600 6000 3000 6600 3200 "2" "2" "edge2"]
+	Pin[0 -19200 6000 3000 6600 3200 "3" "3" "edge2"]
+	ElementLine [-61500 -31400 -61500 12100 2000]
+	ElementLine [-61500 -31400 -10000 -31400 2000]
+	ElementLine [-10000 -31400 -10000 12100 2000]
+	ElementLine [-61500 12100 -10000 12100 2000]
+
+	)
+
+Element["" "Transistor" "" "TO126" 140000 220000 -2000 -12000 1 100 ""]
+(
+	Pin[1000 0 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[10000 0 8000 3000 8600 5200 "2" "2" ""]
+	Pin[19000 0 8000 3000 8600 5200 "3" "3" ""]
+	Pin[10000 -43000 13000 3000 13600 11000 "4" "4" ""]
+	ElementLine [0 -10000 0 0 3000]
+	ElementLine [10000 -10000 10000 0 3000]
+	ElementLine [20000 -10000 20000 0 3000]
+	ElementLine [-5000 -10000 25000 -10000 2000]
+	ElementLine [25000 -53000 25000 -10000 2000]
+	ElementLine [-5000 -53000 25000 -53000 2000]
+	ElementLine [-5000 -53000 -5000 -10000 2000]
+
+	)
+
+Element["" "Transistor" "" "TO126S" 199000 220000 -2000 -12000 1 100 ""]
+(
+	Pin[1000 0 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[10000 10000 8000 3000 8600 5200 "2" "2" ""]
+	Pin[19000 0 8000 3000 8600 5200 "3" "3" ""]
+	Pin[10000 -43000 13000 3000 13600 11000 "4" "4" ""]
+	ElementLine [0 -10000 0 0 3000]
+	ElementLine [10000 -10000 10000 10000 3000]
+	ElementLine [20000 -10000 20000 0 3000]
+	ElementLine [-5000 -10000 25000 -10000 2000]
+	ElementLine [25000 -53000 25000 -10000 2000]
+	ElementLine [-5000 -53000 25000 -53000 2000]
+	ElementLine [-5000 -53000 -5000 -10000 2000]
+
+	)
+
+Element["" "Transistor" "" "TO126SW" 40000 110000 7000 -17000 1 100 ""]
+(
+	Pin[0 -1000 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[10000 -10000 8000 3000 8600 5200 "2" "2" ""]
+	Pin[0 -19000 8000 3000 8600 5200 "3" "3" ""]
+	ElementLine [5000 -10000 10000 -10000 3000]
+	ElementLine [-5000 -25000 -5000 5000 2000]
+	ElementLine [-5000 -25000 5000 -25000 2000]
+	ElementLine [5000 -25000 5000 5000 2000]
+	ElementLine [-5000 5000 5000 5000 2000]
+	ElementLine [-5000 -5000 5000 -5000 1000]
+	ElementLine [-5000 -15000 5000 -15000 1000]
+
+	)
+
+Element["" "Transistor" "" "TO126W" 70000 110000 7000 4000 1 100 ""]
+(
+	Pin[0 -1000 8000 3000 8600 5200 "1" "1" "square"]
+	Pin[0 -10000 8000 3000 8600 5200 "2" "2" ""]
+	Pin[0 -19000 8000 3000 8600 5200 "3" "3" ""]
+	ElementLine [-5000 -25000 -5000 5000 2000]
+	ElementLine [-5000 -25000 5000 -25000 2000]
+	ElementLine [5000 -25000 5000 5000 2000]
+	ElementLine [-5000 5000 5000 5000 2000]
+
+	)
+
+Element["" "Transistor" "" "TO18" 165000 30000 6000 7000 0 100 ""]
+(
+	Pin[0 -5000 5500 3000 6100 3500 "1" "1" ""]
+	Pin[-5000 0 5500 3000 6100 3500 "2" "2" ""]
+	Pin[0 5000 5500 3000 6100 3500 "3" "3" ""]
+	ElementLine [6700 -7900 9400 -10600 1000]
+	ElementLine [7300 -7300 10000 -10000 1000]
+	ElementLine [7900 -6700 10600 -9400 1000]
+	ElementLine [9400 -10600 10600 -9400 1000]
+	ElementArc [0 0 9800 9800 0 360 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO218" 224000 121900 -21000 -58700 0 100 ""]
+(
+	Pin[-14000 0 10000 3000 10600 6000 "1" "1" "square"]
+	Pin[-14000 -21900 10000 3000 10600 6000 "2" "2" ""]
+	Pin[-14000 -43800 10000 3000 10600 6000 "3" "3" ""]
+	ElementLine [-26000 8800 -6000 8800 2000]
+	ElementLine [-6000 -52700 -6000 8800 2000]
+	ElementLine [-26000 -52700 -6000 -52700 2000]
+	ElementLine [-26000 -52700 -26000 8800 2000]
+	ElementLine [-21000 -52700 -21000 8800 1000]
+	ElementLine [-26000 -14400 -21000 -14400 1000]
+	ElementLine [-26000 -29400 -21000 -29400 1000]
+
+	)
+
+Element["" "Transistor" "" "TO220" 90000 220000 -15000 -23000 1 100 ""]
+(
+	Pin[-10000 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[0 0 9000 3000 9600 6000 "2" "2" ""]
+	Pin[10000 0 9000 3000 9600 6000 "3" "3" ""]
+	Pin[0 -67000 15000 3000 15600 13000 "4" "4" ""]
+	ElementLine [-10000 -18000 -10000 0 3000]
+	ElementLine [0 -18000 0 0 3000]
+	ElementLine [10000 -18000 10000 0 3000]
+	ElementLine [-20000 -18000 20000 -18000 2000]
+	ElementLine [20000 -55500 20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [-20000 -55500 -20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [20000 -68000 20000 -55500 2000]
+	ElementLine [18500 -68000 20000 -68000 2000]
+	ElementLine [18500 -75000 18500 -68000 2000]
+	ElementLine [18500 -75000 20000 -75000 2000]
+	ElementLine [20000 -79000 20000 -75000 2000]
+	ElementLine [-20000 -79000 20000 -79000 2000]
+	ElementLine [-20000 -79000 -20000 -75000 2000]
+	ElementLine [-20000 -75000 -18500 -75000 2000]
+	ElementLine [-18500 -75000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -20000 -55500 2000]
+
+	)
+
+Element["" "Transistor" "" "TO220S" 30000 220000 -15000 -23000 1 100 ""]
+(
+	Pin[-10000 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[0 10000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[10000 0 9000 3000 9600 6000 "3" "3" ""]
+	Pin[0 -67000 15000 3000 15600 13000 "4" "4" ""]
+	ElementLine [-10000 -18000 -10000 0 3000]
+	ElementLine [0 -18000 0 10000 3000]
+	ElementLine [10000 -18000 10000 0 3000]
+	ElementLine [-20000 -18000 20000 -18000 2000]
+	ElementLine [20000 -55500 20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [-20000 -55500 -20000 -18000 2000]
+	ElementLine [-20000 -55500 20000 -55500 2000]
+	ElementLine [20000 -68000 20000 -55500 2000]
+	ElementLine [18500 -68000 20000 -68000 2000]
+	ElementLine [18500 -75000 18500 -68000 2000]
+	ElementLine [18500 -75000 20000 -75000 2000]
+	ElementLine [20000 -79000 20000 -75000 2000]
+	ElementLine [-20000 -79000 20000 -79000 2000]
+	ElementLine [-20000 -79000 -20000 -75000 2000]
+	ElementLine [-20000 -75000 -18500 -75000 2000]
+	ElementLine [-18500 -75000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -18500 -68000 2000]
+	ElementLine [-20000 -68000 -20000 -55500 2000]
+
+	)
+
+Element["" "Transistor" "" "TO220SW" 130000 110000 -19000 10000 1 100 ""]
+(
+	Pin[0 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[10000 -10000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[0 -20000 9000 3000 9600 6000 "3" "3" ""]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 6000 -30000 2000]
+	ElementLine [6000 -30000 6000 10000 2000]
+	ElementLine [-12000 10000 6000 10000 2000]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 -6000 -30000 2000]
+	ElementLine [-6000 -30000 -6000 10000 2000]
+	ElementLine [-12000 10000 -6000 10000 2000]
+	ElementLine [-12000 -3000 -6000 -3000 1000]
+	ElementLine [-12000 -17000 -6000 -17000 1000]
+	ElementLine [6000 -10000 10000 -10000 3000]
+
+	)
+
+Element["" "Transistor" "" "TO220W" 100000 110000 -19000 10000 1 100 ""]
+(
+	Pin[0 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[0 -10000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[0 -20000 9000 3000 9600 6000 "3" "3" ""]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 6000 -30000 2000]
+	ElementLine [6000 -30000 6000 10000 2000]
+	ElementLine [-12000 10000 6000 10000 2000]
+	ElementLine [-12000 -30000 -12000 10000 2000]
+	ElementLine [-12000 -30000 -6000 -30000 2000]
+	ElementLine [-6000 -30000 -6000 10000 2000]
+	ElementLine [-12000 10000 -6000 10000 2000]
+	ElementLine [-12000 -3000 -6000 -3000 1000]
+	ElementLine [-12000 -17000 -6000 -17000 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO247" 184000 121900 -22000 -59400 0 100 ""]
+(
+	Pin[-14000 0 10000 3000 10600 6000 "1" "1" "square"]
+	Pin[-14000 -21900 10000 3000 10600 6000 "2" "2" ""]
+	Pin[-14000 -43800 10000 3000 10600 6000 "3" "3" ""]
+	ElementLine [-27000 9600 -6000 9600 2000]
+	ElementLine [-6000 -53400 -6000 9600 2000]
+	ElementLine [-27000 -53400 -6000 -53400 2000]
+	ElementLine [-27000 -53400 -27000 9600 2000]
+	ElementLine [-22000 -53400 -22000 9600 1000]
+	ElementLine [-27000 -14400 -22000 -14400 1000]
+	ElementLine [-27000 -29400 -22000 -29400 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO251" 19000 109000 -9000 -28300 0 100 ""]
+(
+	Pin[-9000 0 7000 3000 7600 4000 "1" "1" "square"]
+	Pin[-9000 -9000 7000 3000 7600 4000 "2" "2" ""]
+	Pin[-9000 -18000 7000 3000 7600 4000 "3" "3" ""]
+	ElementLine [-14000 4200 -4000 4200 2000]
+	ElementLine [-4000 -22300 -4000 4200 2000]
+	ElementLine [-14000 -22300 -4000 -22300 2000]
+	ElementLine [-14000 -22300 -14000 4200 2000]
+	ElementLine [-9000 -22300 -9000 4200 1000]
+	ElementLine [-14000 -1500 -9000 -1500 1000]
+	ElementLine [-14000 -16500 -9000 -16500 1000]
+
+	)
+
+Element["" "diode in TO220" "" "TO264" 264000 121900 -22000 -67900 0 100 ""]
+(
+	Pin[-14000 0 10000 3000 10600 6000 "1" "1" "square"]
+	Pin[-14000 -21900 10000 3000 10600 6000 "2" "2" ""]
+	Pin[-14000 -43800 10000 3000 10600 6000 "3" "3" ""]
+	ElementLine [-27000 18100 -6000 18100 2000]
+	ElementLine [-6000 -61900 -6000 18100 2000]
+	ElementLine [-27000 -61900 -6000 -61900 2000]
+	ElementLine [-27000 -61900 -27000 18100 2000]
+	ElementLine [-22000 -61900 -22000 18100 1000]
+	ElementLine [-27000 -14400 -22000 -14400 1000]
+	ElementLine [-27000 -29400 -22000 -29400 1000]
+
+	)
+
+Element["" "Transistor" "" "TO39" 210000 30000 6000 7000 0 100 ""]
+(
+	Pin[0 -10000 5500 3000 6100 3500 "1" "1" "square"]
+	Pin[-10000 0 5500 3000 6100 3500 "2" "2" ""]
+	Pin[0 10000 5500 3000 6100 3500 "3" "3" ""]
+	ElementLine [12700 -13900 14800 -16000 1000]
+	ElementLine [13300 -13300 15400 -15400 1000]
+	ElementLine [13900 -12700 16000 -14800 1000]
+	ElementLine [16000 -14800 14800 -16000 1000]
+	ElementArc [0 0 18300 18300 0 360 1000]
+
+	)
+
+Element["" "Transistor" "" "TO92" 250000 40000 -13000 -1000 1 100 ""]
+(
+	Pin[0 -20000 7200 3000 7800 4200 "1" "1" "square"]
+	Pin[0 -10000 7200 3000 7800 4200 "2" "2" ""]
+	Pin[0 0 7200 3000 7800 4200 "3" "3" ""]
+	ElementLine [-7000 -17000 -7000 -3000 1000]
+	ElementArc [0 -10000 10000 10000 45 270 1000]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+	Line[0 70000 150000 70000 1000 4000 "clearline"]
+	Line[150000 70000 150000 0 1000 4000 "clearline"]
+	Text[130000 30000 0 200 "HC" "clearline"]
+	Text[250000 190000 0 200 "TO" "clearline"]
+)
diff --git a/util/pcblib-map/trhN.pcb b/util/pcblib-map/trhN.pcb
new file mode 100644
index 0000000..1b9c2fe
--- /dev/null
+++ b/util/pcblib-map/trhN.pcb
@@ -0,0 +1,983 @@
+# release: pcb 20110918
+
+# To read pcb files, the pcb version (or the git source date) must be >= the file version
+FileVersion[20070407]
+
+PCB["" 220000 170000]
+
+Grid[10000.0 0 0 1]
+Cursor[0 0 0.000000]
+PolyArea[3100.006200]
+Thermal[0.500000]
+DRC[1200 900 1000 700 1500 1000]
+Flags("nameonpcb,clearnew,snappin")
+Groups("1,3,4,c:2,5,6,s:7:8")
+Styles["Signal,1000,7874,3150,2000:Power,2000,8661,3937,2000:Fat,8000,13780,4724,2500:Sig-tight,1000,6400,3150,1200"]
+
+Symbol[' ' 1800]
+(
+)
+Symbol['!' 1200]
+(
+	SymbolLine[0 4500 0 5000 800]
+	SymbolLine[0 1000 0 3500 800]
+)
+Symbol['"' 1200]
+(
+	SymbolLine[0 1000 0 2000 800]
+	SymbolLine[1000 1000 1000 2000 800]
+)
+Symbol['#' 1200]
+(
+	SymbolLine[0 3500 2000 3500 800]
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[1500 2000 1500 4000 800]
+	SymbolLine[500 2000 500 4000 800]
+)
+Symbol['$' 1200]
+(
+	SymbolLine[1500 1500 2000 2000 800]
+	SymbolLine[500 1500 1500 1500 800]
+	SymbolLine[0 2000 500 1500 800]
+	SymbolLine[0 2000 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4000 800]
+	SymbolLine[1500 4500 2000 4000 800]
+	SymbolLine[500 4500 1500 4500 800]
+	SymbolLine[0 4000 500 4500 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['%' 1200]
+(
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[1000 2500 1500 2000 800]
+	SymbolLine[500 2500 1000 2500 800]
+	SymbolLine[0 2000 500 2500 800]
+	SymbolLine[0 5000 4000 1000 800]
+	SymbolLine[3500 5000 4000 4500 800]
+	SymbolLine[4000 4000 4000 4500 800]
+	SymbolLine[3500 3500 4000 4000 800]
+	SymbolLine[3000 3500 3500 3500 800]
+	SymbolLine[2500 4000 3000 3500 800]
+	SymbolLine[2500 4000 2500 4500 800]
+	SymbolLine[2500 4500 3000 5000 800]
+	SymbolLine[3000 5000 3500 5000 800]
+)
+Symbol['&' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 3500 1500 2000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[0 2500 2500 5000 800]
+	SymbolLine[500 1000 1000 1000 800]
+	SymbolLine[1000 1000 1500 1500 800]
+	SymbolLine[1500 1500 1500 2000 800]
+	SymbolLine[0 3500 0 4500 800]
+)
+Symbol[''' 1200]
+(
+	SymbolLine[0 2000 1000 1000 800]
+)
+Symbol['(' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+)
+Symbol[')' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['*' 1200]
+(
+	SymbolLine[0 2000 2000 4000 800]
+	SymbolLine[0 4000 2000 2000 800]
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol['+' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[1000 2000 1000 4000 800]
+)
+Symbol[',' 1200]
+(
+	SymbolLine[0 6000 1000 5000 800]
+)
+Symbol['-' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+)
+Symbol['.' 1200]
+(
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['/' 1200]
+(
+	SymbolLine[0 4500 3000 1500 800]
+)
+Symbol['0' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4000 2000 2000 800]
+)
+Symbol['1' 1200]
+(
+	SymbolLine[0 1800 800 1000 800]
+	SymbolLine[800 1000 800 5000 800]
+	SymbolLine[0 5000 1500 5000 800]
+)
+Symbol['2' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[0 5000 2500 2500 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['3' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 2800 1500 2800 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[2000 3300 2000 4500 800]
+	SymbolLine[2000 3300 1500 2800 800]
+	SymbolLine[2000 2300 1500 2800 800]
+)
+Symbol['4' 1200]
+(
+	SymbolLine[0 3500 2000 1000 800]
+	SymbolLine[0 3500 2500 3500 800]
+	SymbolLine[2000 1000 2000 5000 800]
+)
+Symbol['5' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[500 2500 1500 2500 800]
+	SymbolLine[1500 2500 2000 3000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['6' 1200]
+(
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1500 2800 2000 3300 800]
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3300 2000 4500 800]
+)
+Symbol['7' 1200]
+(
+	SymbolLine[500 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 1000 800]
+)
+Symbol['8' 1200]
+(
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3700 0 4500 800]
+	SymbolLine[0 3700 700 3000 800]
+	SymbolLine[700 3000 1300 3000 800]
+	SymbolLine[1300 3000 2000 3700 800]
+	SymbolLine[2000 3700 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 2300 700 3000 800]
+	SymbolLine[0 1500 0 2300 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2300 800]
+	SymbolLine[1300 3000 2000 2300 800]
+)
+Symbol['9' 1200]
+(
+	SymbolLine[500 5000 2000 3000 800]
+	SymbolLine[2000 1500 2000 3000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol[':' 1200]
+(
+	SymbolLine[0 2500 500 2500 800]
+	SymbolLine[0 3500 500 3500 800]
+)
+Symbol[';' 1200]
+(
+	SymbolLine[0 5000 1000 4000 800]
+	SymbolLine[1000 2500 1000 3000 800]
+)
+Symbol['<' 1200]
+(
+	SymbolLine[0 3000 1000 2000 800]
+	SymbolLine[0 3000 1000 4000 800]
+)
+Symbol['=' 1200]
+(
+	SymbolLine[0 2500 2000 2500 800]
+	SymbolLine[0 3500 2000 3500 800]
+)
+Symbol['>' 1200]
+(
+	SymbolLine[0 2000 1000 3000 800]
+	SymbolLine[0 4000 1000 3000 800]
+)
+Symbol['?' 1200]
+(
+	SymbolLine[1000 3000 1000 3500 800]
+	SymbolLine[1000 4500 1000 5000 800]
+	SymbolLine[0 1500 0 2000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 2000 800]
+	SymbolLine[1000 3000 2000 2000 800]
+)
+Symbol['@' 1200]
+(
+	SymbolLine[0 1000 0 4000 800]
+	SymbolLine[0 4000 1000 5000 800]
+	SymbolLine[1000 5000 4000 5000 800]
+	SymbolLine[5000 3500 5000 1000 800]
+	SymbolLine[5000 1000 4000 0 800]
+	SymbolLine[4000 0 1000 0 800]
+	SymbolLine[1000 0 0 1000 800]
+	SymbolLine[1500 2000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 3000 3500 800]
+	SymbolLine[3000 3500 3500 3000 800]
+	SymbolLine[3500 3000 4000 3500 800]
+	SymbolLine[3500 3000 3500 1500 800]
+	SymbolLine[3500 2000 3000 1500 800]
+	SymbolLine[2000 1500 3000 1500 800]
+	SymbolLine[2000 1500 1500 2000 800]
+	SymbolLine[4000 3500 5000 3500 800]
+)
+Symbol['A' 1200]
+(
+	SymbolLine[0 2000 0 5000 800]
+	SymbolLine[0 2000 700 1000 800]
+	SymbolLine[700 1000 1800 1000 800]
+	SymbolLine[1800 1000 2500 2000 800]
+	SymbolLine[2500 2000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['B' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3300 2500 4500 800]
+	SymbolLine[2000 2800 2500 3300 800]
+	SymbolLine[500 2800 2000 2800 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2300 800]
+	SymbolLine[2000 2800 2500 2300 800]
+)
+Symbol['C' 1200]
+(
+	SymbolLine[700 5000 2000 5000 800]
+	SymbolLine[0 4300 700 5000 800]
+	SymbolLine[0 1700 0 4300 800]
+	SymbolLine[0 1700 700 1000 800]
+	SymbolLine[700 1000 2000 1000 800]
+)
+Symbol['D' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1800 1000 2500 1700 800]
+	SymbolLine[2500 1700 2500 4300 800]
+	SymbolLine[1800 5000 2500 4300 800]
+	SymbolLine[0 5000 1800 5000 800]
+	SymbolLine[0 1000 1800 1000 800]
+)
+Symbol['E' 1200]
+(
+	SymbolLine[0 2800 1500 2800 800]
+	SymbolLine[0 5000 2000 5000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+)
+Symbol['F' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[0 2800 1500 2800 800]
+)
+Symbol['G' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[1000 3000 2000 3000 800]
+)
+Symbol['H' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+	SymbolLine[0 3000 2500 3000 800]
+)
+Symbol['I' 1200]
+(
+	SymbolLine[0 1000 1000 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 1000 5000 800]
+)
+Symbol['J' 1200]
+(
+	SymbolLine[700 1000 1500 1000 800]
+	SymbolLine[1500 1000 1500 4500 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 4500 0 4000 800]
+)
+Symbol['K' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3000 2000 1000 800]
+	SymbolLine[0 3000 2000 5000 800]
+)
+Symbol['L' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['M' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 1500 3000 800]
+	SymbolLine[1500 3000 3000 1000 800]
+	SymbolLine[3000 1000 3000 5000 800]
+)
+Symbol['N' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 1000 2500 5000 800]
+	SymbolLine[2500 1000 2500 5000 800]
+)
+Symbol['O' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['P' 1200]
+(
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+)
+Symbol['Q' 1200]
+(
+	SymbolLine[0 1500 0 4500 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1500 1000 800]
+	SymbolLine[1500 1000 2000 1500 800]
+	SymbolLine[2000 1500 2000 4000 800]
+	SymbolLine[1000 5000 2000 4000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[1000 3500 2000 5000 800]
+)
+Symbol['R' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[2500 1500 2500 2500 800]
+	SymbolLine[2000 3000 2500 2500 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[1300 3000 2500 5000 800]
+)
+Symbol['S' 1200]
+(
+	SymbolLine[2000 1000 2500 1500 800]
+	SymbolLine[500 1000 2000 1000 800]
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[0 1500 0 2500 800]
+	SymbolLine[0 2500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['T' 1200]
+(
+	SymbolLine[0 1000 2000 1000 800]
+	SymbolLine[1000 1000 1000 5000 800]
+)
+Symbol['U' 1200]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 1000 2000 4500 800]
+)
+Symbol['V' 1200]
+(
+	SymbolLine[0 1000 1000 5000 800]
+	SymbolLine[1000 5000 2000 1000 800]
+)
+Symbol['W' 1200]
+(
+	SymbolLine[0 1000 0 3000 800]
+	SymbolLine[0 3000 500 5000 800]
+	SymbolLine[500 5000 1500 3000 800]
+	SymbolLine[1500 3000 2500 5000 800]
+	SymbolLine[2500 5000 3000 3000 800]
+	SymbolLine[3000 3000 3000 1000 800]
+)
+Symbol['X' 1200]
+(
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 1000 2500 5000 800]
+)
+Symbol['Y' 1200]
+(
+	SymbolLine[0 1000 1000 3000 800]
+	SymbolLine[1000 3000 2000 1000 800]
+	SymbolLine[1000 3000 1000 5000 800]
+)
+Symbol['Z' 1200]
+(
+	SymbolLine[0 1000 2500 1000 800]
+	SymbolLine[0 5000 2500 1000 800]
+	SymbolLine[0 5000 2500 5000 800]
+)
+Symbol['[' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['\' 1200]
+(
+	SymbolLine[0 1500 3000 4500 800]
+)
+Symbol[']' 1200]
+(
+	SymbolLine[0 1000 500 1000 800]
+	SymbolLine[500 1000 500 5000 800]
+	SymbolLine[0 5000 500 5000 800]
+)
+Symbol['^' 1200]
+(
+	SymbolLine[0 1500 500 1000 800]
+	SymbolLine[500 1000 1000 1500 800]
+)
+Symbol['_' 1200]
+(
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['a' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 4500 800]
+	SymbolLine[2000 4500 2500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['b' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+)
+Symbol['c' 1200]
+(
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 2000 5000 800]
+)
+Symbol['d' 1200]
+(
+	SymbolLine[2000 1000 2000 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+)
+Symbol['e' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[0 4000 2000 4000 800]
+	SymbolLine[2000 4000 2000 3500 800]
+)
+Symbol['f' 1000]
+(
+	SymbolLine[500 1500 500 5000 800]
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[1000 1000 1500 1000 800]
+	SymbolLine[0 3000 1000 3000 800]
+)
+Symbol['g' 1200]
+(
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+)
+Symbol['h' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+)
+Symbol['i' 1000]
+(
+	SymbolLine[0 2000 0 2100 1000]
+	SymbolLine[0 3500 0 5000 800]
+)
+Symbol['j' 1000]
+(
+	SymbolLine[500 2000 500 2100 1000]
+	SymbolLine[500 3500 500 6000 800]
+	SymbolLine[0 6500 500 6000 800]
+)
+Symbol['k' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+	SymbolLine[0 3500 1500 5000 800]
+	SymbolLine[0 3500 1000 2500 800]
+)
+Symbol['l' 1000]
+(
+	SymbolLine[0 1000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['m' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[2000 3500 2500 3000 800]
+	SymbolLine[2500 3000 3000 3000 800]
+	SymbolLine[3000 3000 3500 3500 800]
+	SymbolLine[3500 3500 3500 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['n' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 5000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['o' 1200]
+(
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[2000 3500 2000 4500 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['p' 1200]
+(
+	SymbolLine[500 3500 500 6500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[2500 3500 2500 4500 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[1000 5000 2000 5000 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['q' 1200]
+(
+	SymbolLine[2000 3500 2000 6500 800]
+	SymbolLine[1500 3000 2000 3500 800]
+	SymbolLine[500 3000 1500 3000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[0 3500 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['r' 1200]
+(
+	SymbolLine[500 3500 500 5000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[1000 3000 2000 3000 800]
+	SymbolLine[0 3000 500 3500 800]
+)
+Symbol['s' 1200]
+(
+	SymbolLine[500 5000 2000 5000 800]
+	SymbolLine[2000 5000 2500 4500 800]
+	SymbolLine[2000 4000 2500 4500 800]
+	SymbolLine[500 4000 2000 4000 800]
+	SymbolLine[0 3500 500 4000 800]
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 2000 3000 800]
+	SymbolLine[2000 3000 2500 3500 800]
+	SymbolLine[0 4500 500 5000 800]
+)
+Symbol['t' 1000]
+(
+	SymbolLine[500 1000 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+	SymbolLine[0 2500 1000 2500 800]
+)
+Symbol['u' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+	SymbolLine[2000 3000 2000 4500 800]
+)
+Symbol['v' 1200]
+(
+	SymbolLine[0 3000 1000 5000 800]
+	SymbolLine[2000 3000 1000 5000 800]
+)
+Symbol['w' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[500 5000 1000 5000 800]
+	SymbolLine[1000 5000 1500 4500 800]
+	SymbolLine[1500 3000 1500 4500 800]
+	SymbolLine[1500 4500 2000 5000 800]
+	SymbolLine[2000 5000 2500 5000 800]
+	SymbolLine[2500 5000 3000 4500 800]
+	SymbolLine[3000 3000 3000 4500 800]
+)
+Symbol['x' 1200]
+(
+	SymbolLine[0 3000 2000 5000 800]
+	SymbolLine[0 5000 2000 3000 800]
+)
+Symbol['y' 1200]
+(
+	SymbolLine[0 3000 0 4500 800]
+	SymbolLine[0 4500 500 5000 800]
+	SymbolLine[2000 3000 2000 6000 800]
+	SymbolLine[1500 6500 2000 6000 800]
+	SymbolLine[500 6500 1500 6500 800]
+	SymbolLine[0 6000 500 6500 800]
+	SymbolLine[500 5000 1500 5000 800]
+	SymbolLine[1500 5000 2000 4500 800]
+)
+Symbol['z' 1200]
+(
+	SymbolLine[0 3000 2000 3000 800]
+	SymbolLine[0 5000 2000 3000 800]
+	SymbolLine[0 5000 2000 5000 800]
+)
+Symbol['{' 1200]
+(
+	SymbolLine[500 1500 1000 1000 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[0 3000 500 2500 800]
+	SymbolLine[0 3000 500 3500 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[500 4500 1000 5000 800]
+)
+Symbol['|' 1200]
+(
+	SymbolLine[0 1000 0 5000 800]
+)
+Symbol['}' 1200]
+(
+	SymbolLine[0 1000 500 1500 800]
+	SymbolLine[500 1500 500 2500 800]
+	SymbolLine[500 2500 1000 3000 800]
+	SymbolLine[500 3500 1000 3000 800]
+	SymbolLine[500 3500 500 4500 800]
+	SymbolLine[0 5000 500 4500 800]
+)
+Symbol['~' 1200]
+(
+	SymbolLine[0 3500 500 3000 800]
+	SymbolLine[500 3000 1000 3000 800]
+	SymbolLine[1000 3000 1500 3500 800]
+	SymbolLine[1500 3500 2000 3500 800]
+	SymbolLine[2000 3500 2500 3000 800]
+)
+Attribute("PCB::grid::unit" "mil")
+Via[220000 170000 7874 4000 0 3150 "" ""]
+
+Element["" "Power IC, as in MULTIWATT15" "" "HEPTAWATT" 143900 45000 -19900 -41500 0 100 ""]
+(
+	Pin[6100 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[-13900 -5000 9000 3000 9600 6000 "2" "2" ""]
+	Pin[6100 -10000 9000 3000 9600 6000 "3" "3" ""]
+	Pin[-13900 -15000 9000 3000 9600 6000 "4" "4" ""]
+	Pin[6100 -20000 9000 3000 9600 6000 "5" "5" ""]
+	Pin[-13900 -25000 9000 3000 9600 6000 "6" "6" ""]
+	Pin[6100 -30000 9000 3000 9600 6000 "7" "7" ""]
+	ElementLine [-24900 5400 -6000 5400 2000]
+	ElementLine [-6000 -35500 -6000 5400 2000]
+	ElementLine [-24900 -35500 -6000 -35500 2000]
+	ElementLine [-24900 -35500 -24900 5400 2000]
+	ElementLine [-19900 -35500 -19900 5400 1000]
+	ElementLine [-24900 -7500 -19900 -7500 1000]
+	ElementLine [-24900 -22500 -19900 -22500 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "MULTIWATT11" 20000 38000 79500 -21000 3 100 ""]
+(
+	Pin[0 12000 9000 3000 9600 6000 "1" "1" "square,edge2"]
+	Pin[6700 -8000 9000 3000 9600 6000 "2" "2" "edge2"]
+	Pin[13400 12000 9000 3000 9600 6000 "3" "3" "edge2"]
+	Pin[20100 -8000 9000 3000 9600 6000 "4" "4" "edge2"]
+	Pin[26800 12000 9000 3000 9600 6000 "5" "5" "edge2"]
+	Pin[33500 -8000 9000 3000 9600 6000 "6" "6" "edge2"]
+	Pin[40200 12000 9000 3000 9600 6000 "7" "7" "edge2"]
+	Pin[46900 -8000 9000 3000 9600 6000 "8" "8" "edge2"]
+	Pin[53600 12000 9000 3000 9600 6000 "9" "9" "edge2"]
+	Pin[60300 -8000 9000 3000 9600 6000 "10" "10" "edge2"]
+	Pin[67000 12000 9000 3000 9600 6000 "11" "11" "edge2"]
+	ElementLine [-6500 -26000 -6500 -6000 2000]
+	ElementLine [-6500 -6000 73500 -6000 2000]
+	ElementLine [73500 -6000 73500 -26000 2000]
+	ElementLine [73500 -26000 -6500 -26000 2000]
+	ElementLine [-6500 -21000 73500 -21000 1000]
+	ElementLine [26000 -26000 26000 -21000 1000]
+	ElementLine [41000 -26000 41000 -21000 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "MULTIWATT15" 20000 98000 81000 -21000 3 100 ""]
+(
+	Pin[0 12000 9000 3000 9600 6000 "1" "1" "square,edge2"]
+	Pin[5000 -8000 9000 3000 9600 6000 "2" "2" "edge2"]
+	Pin[10000 12000 9000 3000 9600 6000 "3" "3" "edge2"]
+	Pin[15000 -8000 9000 3000 9600 6000 "4" "4" "edge2"]
+	Pin[20000 12000 9000 3000 9600 6000 "5" "5" "edge2"]
+	Pin[25000 -8000 9000 3000 9600 6000 "6" "6" "edge2"]
+	Pin[30000 12000 9000 3000 9600 6000 "7" "7" "edge2"]
+	Pin[35000 -8000 9000 3000 9600 6000 "8" "8" "edge2"]
+	Pin[40000 12000 9000 3000 9600 6000 "9" "9" "edge2"]
+	Pin[45000 -8000 9000 3000 9600 6000 "10" "10" "edge2"]
+	Pin[50000 12000 9000 3000 9600 6000 "11" "11" "edge2"]
+	Pin[55000 -8000 9000 3000 9600 6000 "12" "12" "edge2"]
+	Pin[60000 12000 9000 3000 9600 6000 "13" "13" "edge2"]
+	Pin[65000 -8000 9000 3000 9600 6000 "14" "14" "edge2"]
+	Pin[70000 12000 9000 3000 9600 6000 "15" "15" "edge2"]
+	ElementLine [-5000 -26000 -5000 -6000 2000]
+	ElementLine [-5000 -6000 75000 -6000 2000]
+	ElementLine [75000 -6000 75000 -26000 2000]
+	ElementLine [75000 -26000 -5000 -26000 2000]
+	ElementLine [-5000 -21000 75000 -21000 1000]
+	ElementLine [27500 -26000 27500 -21000 1000]
+	ElementLine [42500 -26000 42500 -21000 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "MULTIWATT8" 20000 164500 81000 -21000 3 100 ""]
+(
+	Pin[0 -14500 9000 3000 9600 6000 "1" "1" "square,edge2"]
+	Pin[10000 -14500 9000 3000 9600 6000 "2" "2" "edge2"]
+	Pin[20000 -14500 9000 3000 9600 6000 "3" "3" "edge2"]
+	Pin[30000 -14500 9000 3000 9600 6000 "4" "4" "edge2"]
+	Pin[40000 -14500 9000 3000 9600 6000 "5" "5" "edge2"]
+	Pin[50000 -14500 9000 3000 9600 6000 "6" "6" "edge2"]
+	Pin[60000 -14500 9000 3000 9600 6000 "7" "7" "edge2"]
+	Pin[70000 -14500 9000 3000 9600 6000 "8" "8" "edge2"]
+	ElementLine [-5000 -26000 -5000 -6000 2000]
+	ElementLine [-5000 -6000 75000 -6000 2000]
+	ElementLine [75000 -6000 75000 -26000 2000]
+	ElementLine [75000 -26000 -5000 -26000 2000]
+	ElementLine [-5000 -21000 75000 -21000 1000]
+	ElementLine [27500 -26000 27500 -21000 1000]
+	ElementLine [42500 -26000 42500 -21000 1000]
+
+	)
+
+Element["" "Crystal oscillator" "" "OSC14" 130000 110000 20000 -17000 0 100 ""]
+(
+	Pin[0 0 5000 3000 5600 2800 "NC" "1" "edge2"]
+	Pin[60000 0 5000 3000 5600 2800 "GND" "2" "edge2"]
+	Pin[60000 -30000 5000 3000 5600 2800 "CLK" "3" "edge2"]
+	Pin[0 -30000 5000 3000 5600 2800 "VCC" "4" "edge2"]
+	ElementLine [-9500 -30000 -9500 9500 1000]
+	ElementLine [0 -39500 60000 -39500 1000]
+	ElementLine [69500 -30000 69500 0 1000]
+	ElementLine [-9500 9500 60000 9500 1000]
+	ElementLine [-4000 -30000 -4000 0 1000]
+	ElementLine [0 -34000 60000 -34000 1000]
+	ElementLine [64000 -30000 64000 0 1000]
+	ElementLine [0 4000 60000 4000 1000]
+	ElementArc [0 -30000 9500 9500 270 90 1000]
+	ElementArc [60000 -30000 9500 9500 180 90 1000]
+	ElementArc [60000 0 9500 9500 90 90 1000]
+	ElementArc [0 -30000 4000 4000 270 90 1000]
+	ElementArc [60000 -30000 4000 4000 180 90 1000]
+	ElementArc [60000 0 4000 4000 90 90 1000]
+	ElementArc [0 0 4000 4000 0 90 1000]
+
+	)
+
+Element["" "Power IC, as in MULTIWATT15" "" "PENTAWATT" 197200 46700 -19900 -39900 0 100 ""]
+(
+	Pin[8500 0 9000 3000 9600 6000 "1" "1" "square"]
+	Pin[-7200 -6700 9000 3000 9600 6000 "2" "2" ""]
+	Pin[8500 -13400 9000 3000 9600 6000 "3" "3" ""]
+	Pin[-7200 -20100 9000 3000 9600 6000 "4" "4" ""]
+	Pin[8500 -26800 9000 3000 9600 6000 "5" "5" ""]
+	ElementLine [-24900 7000 -6000 7000 2000]
+	ElementLine [-6000 -33900 -6000 7000 2000]
+	ElementLine [-24900 -33900 -6000 -33900 2000]
+	ElementLine [-24900 -33900 -24900 7000 2000]
+	ElementLine [-19900 -33900 -19900 7000 1000]
+	ElementLine [-24900 -5900 -19900 -5900 1000]
+	ElementLine [-24900 -20900 -19900 -20900 1000]
+
+	)
+
+Element["" "" "" "" 160000 150000 -12500 -9000 0 100 ""]
+(
+	Pin[13091 9000 9000 5000 9600 3937 "" "4" "edge2,intconn(2)"]
+	Pin[-12500 9000 9000 5000 9600 3937 "" "3" "edge2,intconn(2)"]
+	Pin[13091 -9000 9000 5000 9600 3937 "" "2" "edge2,intconn(1)"]
+	Pin[-12500 -9000 9000 5000 9600 3937 "" "1" "edge2,intconn(1)"]
+	ElementLine [12181 11736 12181 -11886 787]
+	ElementLine [-11441 11736 12181 11736 787]
+	ElementLine [-11441 -11886 -11441 11736 787]
+	ElementLine [-11441 -11886 12181 -11886 787]
+	ElementArc [7815 -8031 2756 2756 90 90 787]
+	ElementArc [7815 -8031 2756 2756 0 90 787]
+	ElementArc [7815 -8031 2756 2756 270 90 787]
+	ElementArc [7815 -8031 2756 2756 180 90 787]
+	ElementArc [7815 7717 2756 2756 0 90 787]
+	ElementArc [7815 7717 2756 2756 270 90 787]
+	ElementArc [7815 7717 2756 2756 180 90 787]
+	ElementArc [7815 7717 2756 2756 90 90 787]
+	ElementArc [-7933 7717 2756 2756 270 90 787]
+	ElementArc [-7933 7717 2756 2756 180 90 787]
+	ElementArc [-7933 7717 2756 2756 90 90 787]
+	ElementArc [-7933 7717 2756 2756 0 90 787]
+	ElementArc [-7933 -8031 2756 2756 180 90 787]
+	ElementArc [-7933 -8031 2756 2756 90 90 787]
+	ElementArc [-7933 -8031 2756 2756 0 90 787]
+	ElementArc [-7933 -8031 2756 2756 270 90 787]
+	ElementArc [-59 -157 6693 6693 270 90 787]
+	ElementArc [-59 -157 6693 6693 180 90 787]
+	ElementArc [-59 -157 6693 6693 90 90 787]
+	ElementArc [-59 -157 6693 6693 0 90 787]
+
+	)
+Layer(1 "component")
+(
+)
+Layer(2 "solder")
+(
+)
+Layer(3 "comp-GND")
+(
+)
+Layer(4 "comp-power")
+(
+)
+Layer(5 "sold-GND")
+(
+)
+Layer(6 "sold-power")
+(
+)
+Layer(7 "signal3")
+(
+)
+Layer(8 "outline")
+(
+)
+Layer(9 "silk")
+(
+)
+Layer(10 "silk")
+(
+)
diff --git a/util/pcblib-param.cgi b/util/pcblib-param.cgi
new file mode 100755
index 0000000..3ddc425
--- /dev/null
+++ b/util/pcblib-param.cgi
@@ -0,0 +1,479 @@
+#!/bin/bash
+
+ulimit -t 5
+ulimit -v 80000
+
+# read the config
+. /etc/pcblib.cgi.conf
+CGI=$CGI_param
+
+# import the lib
+. $pcb_rnd_util/cgi_common.sh
+
+gen()
+{
+	cd $gendir
+	params=`echo $QS_cmd | sed "s/.*[(]//;s/[)]//" `
+	./$gen "$params"
+}
+
+help_params()
+{
+awk  -v "CGI=$CGI" "$@" '
+BEGIN {
+	prm=0
+	q="\""
+	fp_base=fp
+	sub("[(].*", "", fp_base)
+	thumbsize=1
+}
+
+function urlencode(s)
+{
+	gsub("[#]", "%23", s)
+	return s
+}
+
+function proc_include(fn)
+{
+	close(fn)
+	while(getline < fn)
+		proc_line()
+	close(fn)
+}
+
+function proc_line()
+{
+	if (/@@include/) {
+		proc_include(gendir $2)
+		return
+	}
+
+	if ((match($0, "@@desc") || match($0, "@@params") || match($0, "@@purpose") || match($0, "@@example"))) {
+		txt=$0
+		key=substr($0, RSTART, RLENGTH)
+		sub(".*" key "[ \t]*", "", txt)
+		HELP[key] = HELP[key] " " txt
+		next
+	}
+
+	if (/@@thumbsize/) {
+		sub(".*@@thumbsize", "", $0)
+		if ($1 ~ ":") {
+			sub("^:", "", $1)
+			PDATA[$1,"thumbsize"] = $2
+		}
+		else
+			thumbsize=$1
+		return
+	}
+
+	if (/@@thumbnum/) {
+		sub(".*@@thumbnum", "", $0)
+		if ($1 ~ ":") {
+			sub("^:", "", $1)
+			PDATA[$1,"thumbnum"] = $2
+		}
+		return
+	}
+
+	if (/@@param:/) {
+		sub(".*@@param:", "", $0)
+		p=$1
+		sub(p "[\t ]*", "", $0)
+		PARAM[prm] = p 
+		PDESC[prm] = $0
+		prm++
+		return
+	}
+
+# build PPROP[paramname,propname], e.g. PPROP[pin_mask, dim]=1
+	if (/@@optional:/ || /@@dim:/ || /@@bool:/) {
+		key = $0
+		sub("^.*@@", "", key)
+		val = key
+		sub(":.*", "", key)
+		sub("^[^:]*:", "", val)
+		PPROP[val,key] = 1
+		return
+	}
+
+# build PDATA[paramname,propname], e.g. PDATA[pin_mask, default]=123
+	if ((/@@default:/) || (/@@preview_args:/)) {
+		key = $1
+
+		txt = $0
+		txt = substr(txt, length(key)+1, length(txt))
+		sub("^[ \t]*", "", txt)
+
+		sub("^.*@@", "", key)
+		val = key
+		sub(":.*", "", key)
+		sub("^[^:]*:", "", val)
+		PDATA[val,key] = txt
+		return
+	}
+
+# build:
+#  PDATAK[paramname,n]=key (name of the value)
+#  PDATAV[paramname,n]=description (of the given value)
+#  PDATAN[paramname]=n number of parameter values
+	if (/@@enum:/) {
+		key = $1
+
+		txt = $0
+		txt = substr(txt, length(key)+1, length(txt))
+		sub("^[ \t]*", "", txt)
+
+		sub("^.*@@enum:", "", key)
+		val = key
+		sub(":.*", "", key)
+		sub("^[^:]*:", "", val)
+		idx = int(PDATAN[key])
+		PDATAK[key,idx] = val
+		PDATAV[key,idx] = txt
+		PDATAN[key]++
+		return
+	}
+}
+
+{ proc_line() }
+
+function thumb(prv, gthumbsize, lthumbsize, thumbnum    ,lnk,lnk_gen, thumbsize,ann)
+{
+	if (lthumbsize != "")
+		thumbsize = lthumbsize
+	else
+		thumbsize = gthumbsize
+	if (!thumbnum)
+		ann="&annotation=none"
+	lnk=q CGI "?cmd=" prv "&output=png&grid=none" ann "&thumb=" thumbsize q
+	lnk_gen=q CGI "?cmd=" prv q
+	print "<a href=" lnk_gen ">"
+	print "<img src=" lnk ">"
+	print "</a>"
+}
+
+END {
+	if (header) {
+		print "<b>" HELP["@@purpose"] "</b>"
+		print "<p>"
+		print HELP["@@desc"]
+	}
+
+	if (content)
+		print "<h4>" content "</h4>"
+
+	if ((print_params) && (HELP["@@params"] != ""))
+		print "<p>Ordered list (positions): " HELP["@@params"]
+
+
+	if (content) {
+		print "<table border=1 cellspacing=0 cellpadding=10>"
+		print "<tr><th align=left>name <th align=left>man<br>dat<br>ory<th align=left>description <th align=left>value (bold: default)"
+		for(p = 0; p < prm; p++) {
+			name=PARAM[p]
+			print "<tr><td>" name
+			if (PPROP[name, "optional"])
+				print "<td>  "
+			else
+				print "<td> yes"
+			print "<td>" PDESC[p]
+			print "<td>"
+			vdone=0
+			if (PDATAN[name] > 0) {
+				print "<table border=1 cellpadding=10 cellspacing=0>"
+				for(v = 0; v < PDATAN[name]; v++) {
+					print "<tr><td>"
+					isdef = (PDATA[name, "default"] == PDATAK[name, v])
+					if (isdef)
+						print "<b>"
+					print PDATAK[name, v]
+					if (isdef)
+						print "</b>"
+
+					print "<td>" PDATAV[name, v]
+					if (PDATA[name, "preview_args"] != "") {
+						prv= fp_base "(" PDATA[name, "preview_args"] "," name "=" PDATAK[name, v] ")"
+						print "<td>"
+						thumb(prv, thumbsize, PDATA[name, "thumbsize"], PDATA[name, "thumbnum"])
+					}
+				}
+				print "</table>"
+				vdone++
+			}
+			if (PPROP[name, "dim"]) {
+				print "Dimension: a number with an optional unit (mm or mil, default is mil)"
+				if (PDATA[name, "default"] != "")
+					print "<br>Default: <b>" PDATA[name, "default"] "</b>"
+				vdone++
+			}
+			if (PPROP[name, "bool"]) {
+				print "Boolean: yes/no, true/false, 1/0"
+				if (PDATA[name, "default"] != "")
+					print "; Default: <b>" PDATA[name, "default"] "</b>"
+				if (PDATA[name, "preview_args"] != "") {
+					print "<br>"
+					print "<table border=0>"
+					print "<tr><td>true:<td>"
+					thumb(fp_base "(" PDATA[name, "preview_args"] "," name "=" 1 ")", thumbsize, PDATA[name, "thumbsize"], PDATA[name, "thumbnum"])
+					print "<td>false:<td>"
+					thumb(fp_base "(" PDATA[name, "preview_args"] "," name "=" 0 ")", thumbsize, PDATA[name, "thumbsize"], PDATA[name, "thumbnum"])
+					print "</table>"
+				}
+			}
+			if (!vdone)
+				print " "
+		}
+		print "</table>"
+	}
+
+	if (footer) {
+		print "<h3>Example</h3>"
+		print "<a href=" q CGI "?cmd=" urlencode(HELP["@@example"]) q ">"
+		print HELP["@@example"]
+		print "</a>"
+		print "</body></html>"
+	}
+}
+'
+}
+
+help()
+{
+	local incl n
+	echo "
+<html>
+<body>
+<h1> pcblib-param help for $QS_cmd() </h1>
+"
+
+	incl=`tempfile`
+
+
+# generate the table
+	help_params -v "fp=$QS_cmd" -v "header=1" -v "content=$gen parameters" -v "print_params=1" -v "gendir=$gendir" < $gendir/$gen
+
+# generate the footer
+	help_params -v "fp=$QS_cmd" -v "footer=1" -v "gendir=$gendir" < $gendir/$gen 2>/dev/null
+
+}
+
+list_gens()
+{
+	awk -v "CGI=$CGI" '
+		BEGIN {
+			q="\""
+		}
+		/@@purpose/ {
+			sub(".*@@purpose", "", $0)
+			PURPOSE[FILENAME] = PURPOSE[FILENAME] $0
+			next
+		}
+		/@@example/ {
+			sub(".*@@example", "", $0)
+			EXAMPLE[FILENAME] = EXAMPLE[FILENAME] $0
+			next
+		}
+
+		/@@params/ {
+			sub(".*@@params", "", $0)
+			PARAMS[FILENAME] = PARAMS[FILENAME] $0
+			next
+		}
+
+		function urlencode(s)
+		{
+			gsub("[#]", "%23", s)
+			return s
+		}
+
+		END {
+			for(fn in PURPOSE) {
+				gn=fn
+				sub(".*/", "", gn)
+				params=PARAMS[fn]
+				sub("^[ \t]*", "", params)
+				sub("[ \t]*$", "", params)
+				print "<li> <a href=" q CGI "?cmd=" urlencode(EXAMPLE[fn]) q ">" gn "(<small>" params "</small>) </a> - " PURPOSE[fn]
+				print "- <a href=" q CGI "?cmd=" gn "&output=help"  q "> HELP </a>"
+			}
+		}
+	' $gendir/*
+}
+
+qs=`echo "$QUERY_STRING" | tr "&" "\n"`
+
+for n in $qs
+do
+	exp="QS_$n"
+	export $exp
+done
+
+export QS_cmd=`echo "$QS_cmd" | url_decode`
+
+if test -z "$QS_cmd"
+then
+	export QS_cmd='connector(2,3)'
+	export QS_diamond=1
+	gen=connector
+else
+	gen=`awk -v "n=$QS_cmd" '
+	BEGIN {
+		sub("[(].*", "", n)
+		gsub("[^a-zA-Z0-9_]", "", n)
+		print n
+	}
+	'`
+	
+	if test -z `grep "@@purpose" $gendir/$gen`
+	then
+		error "Invalid generator \"$gen\" (file)"
+	fi
+fi
+
+
+if test "$QS_output" = "help"
+then
+	echo "Content-type: text/html"
+	echo ""
+	help
+	exit
+fi
+
+fptext=`gen`
+if test ! "$?" = 0
+then
+	echo "Content-type: text/plain"
+	echo ""
+	echo "Error generating the footprint:"
+	gen 2>&1 | grep -i error
+	exit
+fi
+
+if test "$QS_output" = "text"
+then
+	echo "Content-type: text/plain"
+	echo ""
+	gen
+	exit
+fi
+
+if test "$QS_output" = "png"
+then
+	cgi_png
+	exit
+fi
+
+echo "Content-type: text/html"
+echo ""
+
+echo "<html>"
+echo "<body>"
+echo "<H1> pcblib-param test page </H1>"
+echo '
+
+<table border=1 cellpadding=10 cellspacing=0>
+<tr><td bgcolor="lightgrey">
+<i>
+"Do you remember the good old days? When I was
+<br> young, I said <b>footprint=CONNECTOR 2 13</b>
+<br> in gschem and got a 2*13 pin connector in PCB."</i>
+</table>
+
+<br>  <br>  
+<p>
+
+<table border=0  cellpadding=10 cellspacing=10>
+<tr>
+<td valign=top width=40%>
+<h3> Syntax </h3>
+<p>
+In <a href="http://repo.hu/projects/pcb-rnd">pcb-rnd</a>,
+automatic footprint generation on the fly is called "parametric
+footprints". It is a smaller and more generic infrastructure compared
+to the m4 footprints in pcb. The goal was to fix three problems.
+<ul>
+	<li> 1. Languages: m4 should not be mandatory for footprint generators - the user should be free to choose any language
+	<li> 2. Simplicity: references to m4 should not be hardwired in pcb, gsch2pcb and gsch2pcb.scm, but footprint generation should be generic, transparent and external
+	<li> 3. Unambiguity: gsch2pcb should not be guessing whether to use file elements or call a generator. Instead of complex heuristics, there should eb a simple, distinct syntax for parametric footprints.
+</ul>
+<p>
+The new syntax is that if a footprint attribute contains parenthesis, it is
+a parametric footprint, else it is a file footprint. Parametric footprints
+are sort of a function call. The actual syntax is similar to the on in
+<a href="http://www.openscad.org"> openscad </a>:
+parameters are separated by commas, and they are either positional (value)
+or named (name=value).
+<p>
+For example a simple pin-grid
+connector has the "prototype" of <i>connector(nx, ny, spacing)</i>, where
+nx and ny are the number of pins needed in X and Y directions, spacing is
+the distance between pin centers, with a default of 100 mil. The following
+calls will output the same 2*3 pin, 100 mil footprint:
+<ul>
+	<li> connector(2, 3)
+	<li> connector(2, 3, 100)
+	<li> connector(nx=2, ny=3, spacing=100)
+	<li> connector(spacing=100, nx=2, ny=3)
+	<li> connector(ny=3, spacing=100, nx=2)
+</ul>
+
+<td>    
+<td valign=top bgcolor="#eefeee">
+<h3>Generators available</h3>
+<ul>
+'
+list_gens
+echo '
+</ul>
+</table>
+'
+
+
+echo "<h2> Try it online </h2>"
+echo "<b>source:</b>"
+
+echo "<form action=\"$CGI\" method=get>"
+echo "<textarea id=\"cmd\" name=\"cmd\" cols=80 rows=3>$QS_cmd</textarea>"
+echo "<ul>"
+echo "	<li><input type=\"checkbox\" name=\"mm\" value=\"1\" `checked $QS_mm`> draw grid in mm"
+echo "	<li><input type=\"checkbox\" name=\"photo\" value=\"1\" `checked $QS_photo`> draw in \"photo mode\""
+echo "	<li><input type=\"checkbox\" name=\"diamond\" value=\"1\" `checked $QS_diamond`> diamond at origin"
+echo "	<li><input type=\"checkbox\" name=\"pins\" value=\"1\" `checked $QS_pins`> annotate pin names"
+echo "	<li><input type=\"checkbox\" name=\"dimname\" value=\"1\" `checked $QS_dimname`> annotate dimension names"
+echo "	<li><input type=\"checkbox\" name=\"dimvalue\" value=\"1\" `checked $QS_dimvalue`> annotate dimension values (in grid units)"
+echo "	<li><input type=\"checkbox\" name=\"background\" value=\"1\" `checked $QS_background`> lighten annotataion background"
+echo "</ul>"
+echo "<p>"
+echo "<input type=\"submit\" value=\"Generate my footprint!\">"
+echo "</form>"
+
+
+
+QS_format=${QS_cmd##*_}
+QS_cmd_=${QS_cmd%%_*}
+
+if test ! -z "$QS_cmd"
+then
+echo "<h2> Result </h2>"
+	echo "<h3> $QS_cmd </H3>"
+
+	echo "<table border=0>"
+	echo "<tr><td valign=top>"
+	echo "<img src=\"$CGI?$QUERY_STRING&output=png\">"
+
+	echo "<td>     "
+
+	echo "<td valign=top>"
+	echo "<pre>"
+	echo "$fptext"
+	echo "</pre>"
+	echo "<p>Downloads:"
+	echo "<br> <a href=\"$CGI?$QUERY_STRING&output=text\">footprint file</a>"
+	echo "</table>"
+fi
+
+
+echo "</body>"
+echo "</html>"
diff --git a/util/pcblib-static.cgi b/util/pcblib-static.cgi
new file mode 100755
index 0000000..288ce57
--- /dev/null
+++ b/util/pcblib-static.cgi
@@ -0,0 +1,237 @@
+#!/bin/bash
+
+ulimit -t 5
+ulimit -v 80000
+
+# read the config
+. /etc/pcblib.cgi.conf
+CGI=$CGI_static
+
+# import the lib
+. $pcb_rnd_util/cgi_common.sh
+
+find_fp()
+{
+	awk -v "fp=$QS_fp" -v "fpdir=$fpdir" '
+		BEGIN {
+			fp=tolower(fp)
+		}
+		($1 == "S") {
+			n=tolower($2)
+			sub("^.*/", "", n)
+			sub(".fp$", "", n)
+			sub(".ele$", "", n)
+			if (n == fp) {
+				print fpdir $2
+				exit
+			}
+		}
+	' < $sdir/cache
+}
+
+list_fps()
+{
+	find $fpdir | awk -v "fpdir=$fpdir" -v "CGI=$CGI" '
+		/.svn/ { next }
+		/parametric/ { next }
+
+		{
+			name=$0
+			sub(fpdir, "", name)
+			if (!(name ~ "/"))
+				next
+			dir=name
+			fn=name
+			sub("/.*", "", dir)
+			sub("^[^/]*/", "", fn)
+			vfn = fn
+			sub(".fp$", "", vfn)
+
+			LLEN[dir] += length(vfn)
+			
+			vfn = "<a href=" q CGI "?fp=" vfn q ">" vfn "</a>"
+			
+			if (LLEN[dir] > 8) {
+				LLEN[dir] = 0
+				sep = "<br>"
+			}
+			else
+				sep = " "
+			if (DIR[dir] != "")
+				DIR[dir] = DIR[dir] sep vfn
+			else
+				DIR[dir] = vfn
+		}
+
+		END {
+			print "<table border=1 cellpadding=20>"
+			print "<tr>"
+			for(n in DIR)
+				print "<th>" n
+			print "<tr>"
+			for(n in DIR)
+				print "<td valign=top>" DIR[n]
+			print "</table>"
+		}
+	'
+}
+
+qs=`echo "$QUERY_STRING" | tr "&" "\n"`
+
+for n in $qs
+do
+    exp="QS_$n"
+    export $exp
+done
+
+export QS_cmd=`echo "$QS_cmd" | url_decode`
+
+if test -z "$QS_fp"
+then
+	export QS_fp='TO220'
+	export QS_diamond=1
+	gen=connector
+fi
+
+fn=`find_fp`
+if test ! "$?" = 0
+then
+	echo "Content-type: text/plain"
+	echo ""
+	echo "Error: couldn't find $QS_fp"
+	exit
+fi
+
+fptext=`cat $fn`
+if test ! "$?" = 0
+then
+	echo "Content-type: text/plain"
+	echo ""
+	echo "Error reading footprint file for $QS_fp $fn"
+	exit
+fi
+
+if test "$QS_output" = "text"
+then
+	echo "Content-type: text/plain"
+	echo ""
+	echo "$fptext"
+	exit
+fi
+
+if test "$QS_output" = "png"
+then
+	echo "Content-type: image/png"
+	echo ""
+	cparm=""
+	if test ! -z "$QS_mm"
+	then
+		cparm="$cparm --mm"
+	fi
+	if test ! -z "$QS_diamond"
+	then
+		cparm="$cparm --diamond"
+	fi
+	if test ! -z "$QS_photo"
+	then
+		cparm="$cparm --photo"
+	fi
+	(echo "$fptext" | $fp2anim $cparm; echo 'screenshot "/dev/stdout"') | $animator -H
+	exit
+fi
+
+echo "Content-type: text/html"
+echo ""
+
+echo "<html>"
+echo "<body>"
+echo "<H1> pcblib-static browse page </H1>"
+echo '
+
+<table border=1 cellpadding=10 cellspacing=0>
+<tr><td bgcolor="lightgrey">
+<i>
+An outburst while looking for a powerjack connector <br>
+footprint in the stock library: <br>
+"What on earth is an <i>MSP430F1121 <u>footprint</u></i> and <br>
+why do I need it in the default library?!"</i>
+</table>
+
+<br>  <br>  
+<p>
+
+<table border=0  cellpadding=10 cellspacing=10>
+<tr>
+<td valign=top width=40%>
+<h3> pcblib </h3>
+<p>
+The default library in <a href="http://repo.hu/projects/pcb-rnd">pcb-rnd</a>
+is called the "pcblib" (lib and newlib are already used by vanilla pcb).
+Pcblib content consists of 
+<a href="http://igor2.repo.hu/cgi-bin/pcblib-param.cgi">parametric (generated) footprints</a>
+and static footprints ("file elements"). This page queries static footprints.
+<p>
+The goal of pcblib is to ship a minimalistic library of the real essential
+parts, targeting small projects and hobbysts. Pcblib assumes users can
+grow their own library by downloading footprints from various online sources
+(e.g. <a href="http://gedasymbols.org"> gedasymbols</a>) or draw their own
+as needed. Thus instead of trying to be a complete collection of footprints
+ever drawn, it tries to collect the common base so that it is likely that 90%
+of pcblib users will use 90% of the footprints in their projects.
+
+<td>    
+<td valign=top bgcolor="#eefeee">
+<h3>List of static footprints</h3>
+<ul>
+'
+
+
+list_fps
+
+echo '
+</ul>
+</table>
+'
+
+
+echo "<h2> Search and preview </h2>"
+
+echo "<form action=\"$CGI\" method=get>"
+echo "name: <input name=\"fp\" value=\"$QS_fp\">"
+echo "<ul>"
+echo "	<li><input type=\"checkbox\" name=\"mm\" value=\"1\" `checked $QS_mm`> draw grid in mm"
+echo "	<li><input type=\"checkbox\" name=\"photo\" value=\"1\" `checked $QS_photo`> draw in \"photo mode\""
+echo "	<li><input type=\"checkbox\" name=\"diamond\" value=\"1\" `checked $QS_diamond`> diamond at origin"
+echo "</ul>"
+echo "<p>"
+echo "<input type=\"submit\" value=\"Find my footprint!\">"
+echo "</form>"
+
+
+
+QS_format=${QS_cmd##*_}
+QS_fp_=${QS_cmd%%_*}
+
+if test ! -z "$QS_fp"
+then
+echo "<h2> Result </h2>"
+	echo "<h3> $QS_fp </H3>"
+
+	echo "<table border=0>"
+	echo "<tr><td valign=top>"
+	echo "<img src=\"$CGI?$QUERY_STRING&output=png\">"
+
+	echo "<td>     "
+
+	echo "<td valign=top>"
+	echo "<pre>"
+	echo "$fptext"
+	echo "</pre>"
+	echo "<p>Downloads:"
+	echo "<br> <a href=\"$CGI?fp=$QS_fp&output=text\">footprint file</a>"
+	echo "</table>"
+fi
+
+
+echo "</body>"
+echo "</html>"
diff --git a/util/pcblib.cgi.conf b/util/pcblib.cgi.conf
new file mode 100644
index 0000000..8fc8d05
--- /dev/null
+++ b/util/pcblib.cgi.conf
@@ -0,0 +1,34 @@
+# shell syntax
+
+### change these to reflect your installation ###
+
+# absolute path to the trunk directory of the pcb-rnd checkout from
+# svn://repo.hu/pcb-rnd/trunk
+pcb_rnd_trunk=/home/igor2/C/pcb-rnd
+
+# CGI url to use for self-reference when generating html pages;
+# this should be a relative URL
+CGI_param=/cgi-bin/pcblib-param.cgi
+CGI_static=/cgi-bin/pcblib-static.cgi
+
+# the executable file name of animator
+animator=/usr/local/bin/animator
+
+# pcblib-static.cgi: absolute path to the map directory that is put
+# online (svn://repo.hu/pcb-rnd/trunk/util/pcblib-map)
+# NOTE: run make there after the checkout or an update
+sdir=/var/www/tmp/pcblib
+
+### these are less likely to change ###
+
+# absolute path to util/ under the trunk checkout
+pcb_rnd_util=$pcb_rnd_trunk/util
+
+# absolute path to the footprint generators - has to end with /
+gendir=$pcb_rnd_trunk/pcblib/parametric/
+
+# absolute path to the root of the static footprint tree - has to end with /
+fpdir=$pcb_rnd_trunk/pcblib/
+
+# absolute path to the fp2anim script
+fp2anim=$pcb_rnd_util/fp2anim
diff --git a/util/workarounds/unwarn.sh b/util/workarounds/unwarn.sh
new file mode 100755
index 0000000..8031b5f
--- /dev/null
+++ b/util/workarounds/unwarn.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# This file is placed in the Public Domain.
+
+# Comment all #warnings in a file given as $1.
+# Useful on systems with CC with no support for #warning.
+
+sed '
+	/^#[ \t]*warning.*/ {
+		s@^@/*@
+		s@$@*/@
+	}
+' < "$1" > "$1.tmp" && mv "$1.tmp" "$1"
diff --git a/util/workarounds/unwarn_all.sh b/util/workarounds/unwarn_all.sh
new file mode 100755
index 0000000..8acea47
--- /dev/null
+++ b/util/workarounds/unwarn_all.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# This file is placed in the Public Domain.
+
+# Comment all #warnings in all .h and .c files, recursively.
+# Useful on systems with CC with no support for #warning.
+
+action=`echo "$0" | sed "s/_all//"`
+
+find . -name '*.[ch]' -exec $action {} \;
diff --git a/w32/Makefile.am b/w32/Makefile.am
new file mode 100644
index 0000000..af86e32
--- /dev/null
+++ b/w32/Makefile.am
@@ -0,0 +1,18 @@
+EXTRA_DIST= build-all.sh minipack.conf mpk README \
+  patches/gd patches/gd/0001-Export-sysbols-when-building-as-a-DLL.patch \
+  patches/gd/0002-Libtool-requires-no-undefined-to-build-a-DLL.patch \
+  patches/gd/0003-Avoid-conflit-between-libjpeg-and-windows-headers.patch \
+  patches/gettext/01-revert-sed-string.patch \
+  patches/gettext/02-Avoid-missing-open-argument-error.patch \
+  patches/gtk+ patches/gtk+/01-mousewheel.patch \
+  patches/zlib patches/zlib/01-shared-lib-support.patch \
+  patches/zlib/02-cross-build.patch \
+  recipes/atk.recipe recipes/cairo.recipe recipes/gd.recipe \
+  recipes/gettext.recipe recipes/glib.recipe recipes/gtk+.recipe \
+  recipes/jpeg.recipe recipes/libiconv.recipe recipes/libpng.recipe \
+  recipes/pango.recipe recipes/pcb.recipe recipes/pixman.recipe \
+  recipes/tiff.recipe recipes/zlib.recipe \
+  tools/mpk-build tools/mpk-clean tools/mpk-config.guess \
+  tools/mpk-help tools/mpk-install tools/mpk-remove tools/mpk-shell \
+  tools/mpk-source tools/mpk-unpack tools/mpk-version \
+  tools/tool.template
diff --git a/w32/README b/w32/README
new file mode 100644
index 0000000..e1c4322
--- /dev/null
+++ b/w32/README
@@ -0,0 +1,54 @@
+Cross compile pcb-rnd on windows.
+
+Inherited from the original pcb code, haven't been tested yet.
+
+========
+Minipack
+========
+
+Homepage: http://code.google.com/p/minipack
+This minipack distribution was customized for the PCB project.
+
+Minipack is an automated build tool. Given a set of recipes and patches, it
+downloads the sources, unpacks them, applies local patches, builds them and
+installs them. Its primary use is for cross-compiling free software to
+Windows, but minipack is more general-purpose than that.
+
+To install, unpack it in any directory, and optionally set the PATH to it.
+
+The tool can be run from anywhere, but it looks for a minipack.conf file
+in the current directory or in a parent directory.
+
+Some recipes for cross-compiling to Windows are included in the distribution:
+* libraries: gtk+, guile, gd, freetype, libpng, jpeg, zlib, gettext.
+* applications: gEDA/gaf, PCB.
+See the 'recipes' directory for a full list.
+
+When cross-compiling, you need a suitable cross-compiler for the host you are
+targeting. On Debian and derivatives, you can type 'sudo apt-get install mingw32'
+to get a MinGW cross-compiler. You can also build your own by using the build
+script provided by the MinGW project.
+
+Modify the host setting in the minipack.conf file to reflect the cross-compiler
+being used. It can be found as the prefix of the compiler, eg. i586-mingw32msvc.
+
+The build-all.sh script can be used to build all packages in one run. Edit the
+BUILD list to select only a subset. Beware, some recipes require a native
+version of pkg-config, others need the autotools and/or intltool as well.
+
+=======
+  mpk
+=======
+
+The main (and only) tool of minipack is called mpk:
+* mpk build <package>  - download, unpack, and build a package.
+* mpk unpack <package> - download and unpack a package
+* mpk source <package> - download a source package
+* mpk shell [package]  - enter a shell with a pre-defined build environment
+
+A cache of downloaded sources is kept in the 'sources' directory.
+
+To add more recipes to minipack, place the recipe in the 'recipes' directory
+and place the patches in a subdirectory of the 'patches' directory.
+
+Have fun!
diff --git a/w32/build-all.sh b/w32/build-all.sh
new file mode 100755
index 0000000..7c4c26f
--- /dev/null
+++ b/w32/build-all.sh
@@ -0,0 +1,51 @@
+NEVERBUILD="
+"
+
+NOBUILD="
+"
+
+BUILD="
+jpeg
+zlib
+libpng
+tiff
+pixman
+libiconv
+gettext
+glib
+atk
+cairo
+pango
+gdk-pixbuf
+gtk+
+gd
+pkg-config
+"
+
+fail()
+{
+  echo
+  echo "=================="
+  echo "Build failed."
+  echo "=================="
+  exit 1
+}
+
+succeed()
+{
+  echo
+  echo "====================="
+  echo "Build succeeded."
+  echo "====================="
+}
+
+for D in $BUILD; do
+  ./mpk source $D || fail
+done
+
+for D in $BUILD; do
+  ./mpk build $D || fail
+done
+
+succeed
+
diff --git a/w32/minipack.conf b/w32/minipack.conf
new file mode 100644
index 0000000..27c69ac
--- /dev/null
+++ b/w32/minipack.conf
@@ -0,0 +1,40 @@
+# Change the host for your toolchain.
+# Or comment to use native building.
+host=i586-mingw32msvc
+
+# Guess the build platform.
+build=`mpk-config.guess`
+
+# Some useful mingw 32-bit flags.
+CFLAGS='-O3 -s -mms-bitfields -march=i686'
+CXXFLAGS=$CFLAGS
+
+# Useful for multi-core CPUs
+default_make_opts=-j3
+
+# Needed (at least on Ubuntu 9.10) so mingw-runtime include files
+# can override certain ones provided by mingw-gcc.
+CPPFLAGS=-B/usr/i586-mingw32msvc
+
+# Place your favorite GNU mirror here.
+gnu_mirror=http://ftp.unicamp.br/pub/gnu
+
+# Full path to the source cache.
+# Default: $topdir/sources
+# sourcedir=
+
+# Full path to the directory to place the results.
+# Default: $topdir/result
+# resultdir=
+
+# Full path to the build directory.
+# Default: $topdir/build
+# builddir=
+
+# Full path to the build recipes directory.
+# Default: $prefix/recipes
+# recipedir=
+
+# Full path to the patches directory.
+# Default: $prefix/patches
+# patchdir=
diff --git a/w32/mpk b/w32/mpk
new file mode 100755
index 0000000..1652bc0
--- /dev/null
+++ b/w32/mpk
@@ -0,0 +1,130 @@
+#! /bin/sh
+
+# mpk - top level, user visible script.
+# Copyright (C) 2008,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Minipack. If not, see <http://www.gnu.org/licenses/>.
+
+bindir=$(dirname $0)
+prog=$(basename $0)
+case $bindir in
+/*) ;;
+*) bindir=$PWD/$bindir;;
+esac
+mpk=$bindir/$prog
+prefix=$(cd $bindir && pwd)
+tooldir=$prefix/tools
+recipedir=$prefix/recipes
+patchdir=$prefix/patches
+
+MPK_VERSION=1.1.0
+
+export PATH=$tooldir:$PATH
+
+test -f ~/minipack.conf && . ~/minipack.conf
+
+cwd=$PWD
+topdir=$cwd
+until [ "$topdir" = "/" ]; do
+  if [ -f $topdir/minipack.conf ]; then
+    break;
+  fi
+  cd ..
+  topdir=$PWD
+done
+cd $cwd
+
+if [ "$topdir" = "/" ]; then
+  echo Warning: no minipack.conf found.
+  topdir=$cwd
+fi
+
+builddir=$topdir/build
+resultdir=$topdir/result
+sourcedir=$topdir/sources
+local_tooldir=$resultdir/lib/mpk/tools
+
+# Export resultdir in case we need to call our newly built tools.
+export resultdir
+
+test -f $topdir/minipack.conf && . $topdir/minipack.conf
+
+# Setup environment variables
+export PATH=$local_tooldir:$PATH
+export ACLOCAL="aclocal -I $resultdir/share/aclocal"  
+export ACLOCAL_FLAGS="-I $resultdir/share/aclocal"
+export PKG_CONFIG_LIBDIR=$resultdir/lib/pkgconfig
+
+get_recipe_name()
+{
+  name=$recipedir/$1.recipe
+  if [ ! -f $name ]; then
+    echo >&2 "Recipe for \"$1\" not found."
+    exit 1
+  fi
+  echo $name
+}
+
+setup_configure_options()
+{
+  pkg_configure_opt="$def_configure_opt $configure_opt"
+  pkg_configure_opt="$pkg_configure_opt --prefix=$resultdir"
+
+  if [ -n "$host" ]; then
+    pkg_configure_opt="$pkg_configure_opt --host=$host"
+  fi
+  if [ -n "$build" ]; then
+    pkg_configure_opt="$pkg_configure_opt --build=$build"
+  fi
+
+  # Local compiler/linker search path
+  CPPFLAGS="$CPPFLAGS -I$resultdir/include"
+  LDFLAGS="$LDFLAGS -L$resultdir/lib"
+  
+  test -n "$configure_no_more_flags" && return
+  
+  # Add search paths to the configure line.
+  pkg_configure_opt="$pkg_configure_opt CPPFLAGS=\"$CPPFLAGS\""
+  pkg_configure_opt="$pkg_configure_opt LDFLAGS=\"$LDFLAGS\""
+    
+  # Add extra flags to the configure line, if present.
+  if [ -n "$CFLAGS" ]; then
+    pkg_configure_opt="$pkg_configure_opt CFLAGS=\"$CFLAGS\""
+  fi
+  if [ -n "$CXXFLAGS" ]; then
+    pkg_configure_opt="$pkg_configure_opt CXXFLAGS=\"$CXXFLAGS\""
+  fi
+}
+
+if [ -n "$1" ]; then
+  cmd=$1
+  shift
+else
+  echo "$prog: missing argument."
+  cmd=help
+fi
+
+case $cmd in
+  --version)
+    cmd=version ;;
+  --help)
+    cmd=help ;;
+esac
+tool=$tooldir/mpk-$cmd
+if [ -f $tool ]; then
+  . $tool "$@"
+else
+  echo $prog: Invalid command: $cmd
+fi
+
diff --git a/w32/patches/gd/0001-Export-sysbols-when-building-as-a-DLL.patch b/w32/patches/gd/0001-Export-sysbols-when-building-as-a-DLL.patch
new file mode 100644
index 0000000..16bc912
--- /dev/null
+++ b/w32/patches/gd/0001-Export-sysbols-when-building-as-a-DLL.patch
@@ -0,0 +1,35 @@
+From 1173eecef02205a48e4d70535fca91bfdcee3dd9 Mon Sep 17 00:00:00 2001
+From: Cesar Strauss <cestrauss at gmail.com>
+Date: Fri, 24 Oct 2008 20:07:43 -0200
+Subject: [PATCH] Export symbols when building as a DLL.
+
+libtool defines DLL_EXPORT when building a DLL,
+so we use dllexport in that case.
+---
+ gd.h |    8 ++++++++
+ 1 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/src/gd.h b/src/gd.h
+index f136ea6..4916373 100644
+--- a/gd.h
++++ b/gd.h
+@@ -26,8 +26,16 @@ extern "C" {
+ 	wish to build gd as a static library or by directly including
+ 	the gd sources in a project. */
+ 
++/* libtool defines DLL_EXPORT when building a DLL, so we use dllexport
++   in that case.
++*/
++
+ #ifndef WIN32
+ #define NONDLL 1
++#else
++#ifdef DLL_EXPORT
++#define BGDWIN32 1
++#endif
+ #endif /* WIN32 */
+ 
+ #ifdef NONDLL
+-- 
+1.5.4.3
+
diff --git a/w32/patches/gd/0002-Libtool-requires-no-undefined-to-build-a-DLL.patch b/w32/patches/gd/0002-Libtool-requires-no-undefined-to-build-a-DLL.patch
new file mode 100644
index 0000000..7180dc2
--- /dev/null
+++ b/w32/patches/gd/0002-Libtool-requires-no-undefined-to-build-a-DLL.patch
@@ -0,0 +1,26 @@
+From 59069ae7191d091fae75bb57d12651281d6bdee6 Mon Sep 17 00:00:00 2001
+From: Cesar Strauss <cestrauss at gmail.com>
+Date: Fri, 24 Oct 2008 20:09:39 -0200
+Subject: [PATCH] Libtool requires -no-undefined to build a DLL.
+
+---
+ configure.ac |    3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index bc7b83c..ed9b052 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -33,6 +33,9 @@ case "$target" in
+   *-*-cygwin*) XTRA_LDFLAGS="-no-undefined"
+                os_cygwin=yes
+                ;;
++  *-*-mingw*)  XTRA_LDFLAGS="-no-undefined"
++               os_cygwin=no
++               ;;
+            *)  XTRA_LDFLAGS=
+                os_cygwin=no
+                ;;
+-- 
+1.5.4.3
+
diff --git a/w32/patches/gd/0003-Avoid-conflit-between-libjpeg-and-windows-headers.patch b/w32/patches/gd/0003-Avoid-conflit-between-libjpeg-and-windows-headers.patch
new file mode 100644
index 0000000..dbd23c1
--- /dev/null
+++ b/w32/patches/gd/0003-Avoid-conflit-between-libjpeg-and-windows-headers.patch
@@ -0,0 +1,33 @@
+From 600059ab3efa924b0a8f3753dce3da2ed0ab89b1 Mon Sep 17 00:00:00 2001
+From: Cesar Strauss <cestrauss at gmail.com>
+Date: Fri, 24 Oct 2008 20:12:22 -0200
+Subject: [PATCH] Avoid conflit between libjpeg and windows headers.
+
+---
+ gdhelpers.h |    4 ++++
+ 1 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/src/gdhelpers.h b/src/gdhelpers.h
+index 0c45ad3..6c4b52f 100644
+--- a/gdhelpers.h
++++ b/gdhelpers.h
+@@ -31,12 +31,16 @@ int overflow2(int a, int b);
+ 
+ #ifdef WIN32
+ /* 2.0.18: must include windows.h to get CRITICAL_SECTION. */
++#define INT32 INT32_W32
++#define boolean boolean_W32
+ #include <windows.h>
+ #define gdMutexDeclare(x) CRITICAL_SECTION x
+ #define gdMutexSetup(x) InitializeCriticalSection(&x)
+ #define gdMutexShutdown(x) DeleteCriticalSection(&x)
+ #define gdMutexLock(x) EnterCriticalSection(&x)
+ #define gdMutexUnlock(x) LeaveCriticalSection(&x)
++#undef INT32
++#undef boolean
+ #else
+ #ifdef HAVE_PTHREAD
+ #include <pthread.h>
+-- 
+1.5.4.3
+
diff --git a/w32/patches/gettext/01-revert-sed-string.patch b/w32/patches/gettext/01-revert-sed-string.patch
new file mode 100644
index 0000000..701d24c
--- /dev/null
+++ b/w32/patches/gettext/01-revert-sed-string.patch
@@ -0,0 +1,21 @@
+diff -Nraup gettext-0.17/gettext-runtime/intl/Makefile.in gettext-0.17-1/gettext-runtime/intl/Makefile.in
+--- gettext-0.17/gettext-runtime/intl/Makefile.in	2007-11-04 19:21:12.000000000 -0200
++++ gettext-0.17-1/gettext-runtime/intl/Makefile.in	2008-04-20 23:40:22.000000000 -0300
+@@ -280,10 +280,12 @@ intl-compat.lo: $(srcdir)/intl-compat.c
+ # so that they work with the sed-3.02 that is shipped with MSYS. We can use
+ # GNU bash's $'\n' syntax to obtain such a newline.
+ libintl.res: $(srcdir)/libintl.rc
+-	nl=$$'\n'; \
+-	sed_extract_major='/^[0-9]/{'$${nl}'s/^\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
+-	sed_extract_minor='/^[0-9][0-9]*[.][0-9]/{'$${nl}'s/^[0-9]*[.]\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
+-	sed_extract_subminor='/^[0-9][0-9]*[.][0-9][0-9]*[.][0-9]/{'$${nl}'s/^[0-9]*[.][0-9]*[.]\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
++	sed_extract_major='/^[0-9]/{s/^\([0-9]*\).*/\1/p;q};a\
++	0'; \
++	sed_extract_minor='/^[0-9][0-9]*[.][0-9]/{s/^[0-9]*[.]\([0-9]*\).*/\1/p;q};a\
++	0'; \
++	sed_extract_subminor='/^[0-9][0-9]*[.][0-9][0-9]*[.][0-9]/{s/^[0-9]*[.][0-9]*[.]\([0-9]*\).*/\1/p;q};a\
++	0'; \
+ 	$(WINDRES) \
+ 	  "-DPACKAGE_VERSION_STRING=\\\"$(VERSION)\\\"" \
+ 	  "-DPACKAGE_VERSION_MAJOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_major"` \
+
diff --git a/w32/patches/gettext/02-Avoid-missing-open-argument-error.patch b/w32/patches/gettext/02-Avoid-missing-open-argument-error.patch
new file mode 100644
index 0000000..af88a3a
--- /dev/null
+++ b/w32/patches/gettext/02-Avoid-missing-open-argument-error.patch
@@ -0,0 +1,15 @@
+diff --git a/gettext-tools/src/write-catalog.c b/gettext-tools/src/write-catalog.c
+index 0447744..ea4f9cf 100644
+--- a/gettext-tools/src/write-catalog.c
++++ b/gettext-tools/src/write-catalog.c
+@@ -220,7 +220,9 @@ message catalog has plural form translations, but the output format does not sup
+       /* Open the output file.  */
+       if (!to_stdout)
+ 	{
+-	  fd = open (filename, O_WRONLY | O_CREAT);
++	  fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC,
++		     /* 0666 in portable POSIX notation: */
++		     S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ 	  if (fd < 0)
+ 	    {
+ 	      const char *errno_description = strerror (errno);
diff --git a/w32/patches/gtk+/02-example.patch b/w32/patches/gtk+/02-example.patch
new file mode 100644
index 0000000..df1a615
--- /dev/null
+++ b/w32/patches/gtk+/02-example.patch
@@ -0,0 +1,50 @@
+--- a/demos/gtk-demo/main.c	2011-08-16 04:30:51.000000000 +0200
++++ b/demos/gtk-demo/main.c	2016-08-13 20:15:10.629440222 +0200
+@@ -664,12 +664,13 @@
+ 
+   g_string_free (buffer, TRUE);
+ }
+-
+ void
+ row_activated_cb (GtkTreeView       *tree_view,
+                   GtkTreePath       *path,
+ 		  GtkTreeViewColumn *column)
+ {
++#if 0
++
+   GtkTreeIter iter;
+   PangoStyle style;
+   GDoDemoFunc func;
+@@ -705,6 +706,7 @@
+ 			    G_CALLBACK (window_closed_cb), cbdata);
+ 	}
+     }
++#endif
+ }
+ 
+ static void
+@@ -775,6 +777,7 @@
+ static GtkWidget *
+ create_tree (void)
+ {
++#if 0
+   GtkTreeSelection *selection;
+   GtkCellRenderer *cell;
+   GtkWidget *tree_view;
+@@ -870,6 +873,7 @@
+    g_object_unref (model);
+ 
+   return box;
++#endif
+ }
+ 
+ static void
+@@ -1005,7 +1009,7 @@
+   gtk_widget_show_all (window);
+   
+ 
+-  load_file (testgtk_demos[0].filename);
++//  load_file (testgtk_demos[0].filename);
+   
+   gtk_main ();
+ 
diff --git a/w32/patches/zlib/01-shared-lib-support.patch b/w32/patches/zlib/01-shared-lib-support.patch
new file mode 100644
index 0000000..02eb2ff
--- /dev/null
+++ b/w32/patches/zlib/01-shared-lib-support.patch
@@ -0,0 +1,249 @@
+diff --git a/Makefile.in b/Makefile.in
+index 2fd6e45..eb980dc 100755
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -28,10 +28,14 @@ LDFLAGS=libz.a
+ LDSHARED=$(CC)
+ CPP=$(CC) -E
+ 
+-LIBS=libz.a
++STATICLIB=libz.a
+ SHAREDLIB=libz.so
+ SHAREDLIBV=libz.so.1.2.3
+ SHAREDLIBM=libz.so.1
++IMPORTLIB=
++SHAREDLIBPOST='(rm -f $(SHAREDLIB) $(SHAREDLIBM); ln -s $@ $(SHAREDLIB) ; ln -s $@ $(SHAREDLIBM) )'
++SHAREDTARGET=$(SHAREDLIBV)
++LIBS=$(STATICLIB)
+ 
+ AR=ar rc
+ RANLIB=ranlib
+@@ -42,6 +46,7 @@ EXE=
+ prefix = /usr/local
+ exec_prefix = ${prefix}
+ libdir = ${exec_prefix}/lib
++bindir = ${exec_prefix}/bin
+ includedir = ${prefix}/include
+ mandir = ${prefix}/share/man
+ man3dir = ${mandir}/man3
+@@ -67,7 +72,7 @@ test: all
+ 	  echo '		*** zlib test FAILED ***'; \
+ 	fi
+ 
+-libz.a: $(OBJS) $(OBJA)
++$(STATICLIB): $(OBJS) $(OBJA)
+ 	$(AR) $@ $(OBJS) $(OBJA)
+ 	-@ ($(RANLIB) $@ || true) >/dev/null 2>&1
+ 
+@@ -77,11 +82,9 @@ match.o: match.S
+ 	mv _match.o match.o
+ 	rm -f _match.s
+ 
+-$(SHAREDLIBV): $(OBJS)
++$(SHAREDTARGET): $(OBJS)
+ 	$(LDSHARED) -o $@ $(OBJS)
+-	rm -f $(SHAREDLIB) $(SHAREDLIBM)
+-	ln -s $@ $(SHAREDLIB)
+-	ln -s $@ $(SHAREDLIBM)
++	$(SHAREDLIBPOST)
+ 
+ example$(EXE): example.o $(LIBS)
+ 	$(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
+@@ -90,37 +93,58 @@ minigzip$(EXE): minigzip.o $(LIBS)
+ 	$(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
+ 
+ install: $(LIBS)
+-	- at if [ ! -d $(exec_prefix) ]; then mkdir -p $(exec_prefix); fi
+-	- at if [ ! -d $(includedir)  ]; then mkdir -p $(includedir); fi
+-	- at if [ ! -d $(libdir)      ]; then mkdir -p $(libdir); fi
+-	- at if [ ! -d $(man3dir)     ]; then mkdir -p $(man3dir); fi
+-	cp zlib.h zconf.h $(includedir)
+-	chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h
+-	cp $(LIBS) $(libdir)
+-	cd $(libdir); chmod 755 $(LIBS)
+-	-@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1
+-	cd $(libdir); if test -f $(SHAREDLIBV); then \
+-	  rm -f $(SHAREDLIB) $(SHAREDLIBM); \
+-	  ln -s $(SHAREDLIBV) $(SHAREDLIB); \
+-	  ln -s $(SHAREDLIBV) $(SHAREDLIBM); \
+-	  (ldconfig || true)  >/dev/null 2>&1; \
+-	fi
+-	cp zlib.3 $(man3dir)
+-	chmod 644 $(man3dir)/zlib.3
++	- at if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi
++	- at if [ ! -d $(DESTDIR)$(includedir)  ]; then mkdir -p $(DESTDIR)$(includedir); fi
++	- at if [ ! -d $(DESTDIR)$(libdir)      ]; then mkdir -p $(DESTDIR)$(libdir); fi
++	- at if [ ! -d $(DESTDIR)$(bindir)      ]; then mkdir -p $(DESTDIR)$(bindir); fi
++	- at if [ ! -d $(DESTDIR)$(man3dir)     ]; then mkdir -p $(DESTDIR)$(man3dir); fi
++	cp zlib.h zconf.h $(DESTDIR)$(includedir)
++	chmod 644 $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h
++	cp zlib.3 $(DESTDIR)$(man3dir)
++	chmod 644 $(DESTDIR)$(man3dir)/zlib.3
++# Install static lib (and import lib, if set) into libdir
+ # The ranlib in install is needed on NeXTSTEP which checks file times
++	-cp $(STATICLIB) $(IMPORTLIB) $(DESTDIR)$(libdir)
++	-@(cd $(DESTDIR)$(libdir); \
++	$(RANLIB) libz.a || true; \
++	chmod 755 $(STATICLIB) $(IMPORTLIB)) >/dev/null 2>&1
++# Install shared lib -- if IMPORTLIB is set, then sharedlib goes into bindir
+ # ldconfig is for Linux
++	if test -z "$(IMPORTLIB)" ; then \
++	  if test -n "$(SHAREDTARGET)" ; then \
++	    if test -f "$(SHAREDTARGET)" ; then \
++	      cp $(SHAREDTARGET) $(DESTDIR)$(libdir); \
++	    fi; \
++	  fi; \
++	  cd $(DESTDIR)$(libdir); \
++	  if test -n "$(SHAREDLIBV)" ; then \
++	    if test -f "$(SHAREDLIBV)" ; then \
++	      rm -f $(SHAREDLIB) $(SHAREDLIBM); \
++	      ln -s $(SHAREDLIBV) $(SHAREDLIB); \
++	      ln -s $(SHAREDLIBV) $(SHAREDLIBM); \
++	      (ldconfig || true) >/dev/null 2>&1; \
++	    fi; \
++	  fi; \
++	else \
++	  cp $(SHAREDTARGET) $(DESTDIR)$(bindir); \
++	  (cd $(DESTDIR)$(bindir); chmod 755 $(SHAREDTARGET)); \
++	fi
+ 
+ uninstall:
+-	cd $(includedir); \
+-	cd $(libdir); rm -f libz.a; \
+-	if test -f $(SHAREDLIBV); then \
+-	  rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \
++	cd $(DESTDIR)$(includedir); \
++	cd $(DESTDIR)$(libdir); rm -f $(STATICLIB) $(IMPORTLIB); \
++	if test -f "$(SHAREDLIBV)"; then \
++	  if test -f "$(SHAREDLIBV)"; then \
++	    rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \
++	  fi \
+ 	fi
+-	cd $(man3dir); rm -f zlib.3
++	cd $(DESTDIR)$(man3dir); rm -f zlib.3
++	cd $(DESTDIR)$(bindir); (rm -f $(SHAREDLIB) || true) > /dev/null 2>&1
+ 
+ mostlyclean: clean
+ clean:
+ 	rm -f *.o *~ example$(EXE) minigzip$(EXE) \
++	   $(STATICLIB) $(IMPORTLIB) $(SHAREDLIBV) $(SHAREDLIBV) $(SHAREDLIBM) \
+ 	   libz.* foo.gz so_locations \
+ 	   _match.s maketree contrib/infback9/*.o
+ 
+diff --git a/configure b/configure
+index d7ffdc3..b81ed63 100755
+--- a/configure
++++ b/configure
+@@ -18,8 +18,9 @@
+ # If you have problems, try without defining CC and CFLAGS before reporting
+ # an error.
+ 
+-LIBS=libz.a
+-LDFLAGS="-L. ${LIBS}"
++STATICLIB=libz.a
++old_ldflags="$LDFLAGS"
++LDFLAGS="-L. ${STATICLIB}"
+ VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
+ VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h`
+ VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < zlib.h`
+@@ -28,6 +29,7 @@ RANLIB=${RANLIB-"ranlib"}
+ prefix=${prefix-/usr/local}
+ exec_prefix=${exec_prefix-'${prefix}'}
+ libdir=${libdir-'${exec_prefix}/lib'}
++bindir=${bindir-'${exec_prefix}/bin'}
+ includedir=${includedir-'${prefix}/include'}
+ mandir=${mandir-'${prefix}/share/man'}
+ shared_ext='.so'
+@@ -71,13 +73,32 @@ case "$cc" in
+   *gcc*) gcc=1;;
+ esac
+ 
++BUILDPLATFORM=`(uname -s || echo unknown) 2>/dev/null`;
++TARGETPLATFORM=${target-${BUILDPLATFORM}}
++
+ if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+   CC="$cc"
+   SFLAGS=${CFLAGS-"-fPIC -O3"}
+   CFLAGS="$cflags"
+-  case `(uname -s || echo unknown) 2>/dev/null` in
++  case ${TARGETPLATFORM} in
+   Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1"};;
+-  CYGWIN* | Cygwin* | cygwin* | OS/2* )
++  OS/2* )
++             EXE='.exe';;
++  *CYGWIN* | *Cygwin* | *cygwin* )
++             SFLAGS=${CFLAGS}
++             LDFLAGS="-L. -lz ${old_ldflags}"
++             shared_ext='.dll'
++             SHAREDLIB='cygz.dll'
++             IMPORTLIB='libz.dll.a'
++             LDSHARED=${LDSHARED-"${CC} -shared -Wl,-export-all -Wl,--enable-auto-image-base -Wl,--out-implib=${IMPORTLIB}"}
++             EXE='.exe';;
++  *MINGW* | *Mingw* | *mingw* ) 
++             SFLAGS=${CFLAGS}
++             LDFLAGS="-L. -lz ${old_ldflags}"
++             shared_ext='.dll'
++             SHAREDLIB='libz.dll'
++             IMPORTLIB='libz.dll.a'
++             LDSHARED=${LDSHARED-"${CC} -shared -Wl,-export-all -Wl,--enable-auto-image-base -Wl,--out-implib=${IMPORTLIB}"}
+              EXE='.exe';;
+   QNX*)  # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4
+          # (alain.bonnefoy at icbt.com)
+@@ -163,6 +184,20 @@ else
+              LDSHARED=${LDSHARED-"cc -shared"};;
+   esac
+ fi
++case ${TARGETPLATFORM} in
++  *CYGWIN* | *Cygwin* | *cygwin* )
++    # On cygwin, we always build both shared and static libs
++    LIBS="${SHAREDLIB} ${STATICLIB}"
++    SHAREDLIBPOST='/bin/true'
++    SHAREDTARGET=${SHAREDLIB}
++    ;;
++  *MINGW* | *Mingw* | *mingw* ) 
++    # On Mingw-cygwin-special, we always build both shared and static libs
++    LIBS="${SHAREDLIB} ${STATICLIB}"
++    SHAREDLIBPOST='/bin/true'
++    SHAREDTARGET=${SHAREDLIB}
++    ;;
++  *)
+ 
+ SHAREDLIB=${SHAREDLIB-"libz$shared_ext"}
+ SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"}
+@@ -186,10 +221,14 @@ if test $shared -eq 1; then
+ fi
+ if test $shared -eq 0; then
+   LDSHARED="$CC"
+-  echo Building static library $LIBS version $VER with $CC.
++  LIBS="$STATICLIB"
++  LDFLAGS="-L. ${STATICLIB}"
++  echo Building static library $STATICLIB version $VER with $CC.
+ else
+   LDFLAGS="-L. ${SHAREDLIBV}"
+ fi
++  ;;
++esac
+ 
+ cat > $test.c <<EOF
+ #include <unistd.h>
+@@ -444,15 +483,20 @@ sed < Makefile.in "
+ /^CPP *=/s#=.*#=$CPP#
+ /^LDSHARED *=/s#=.*#=$LDSHARED#
+ /^LIBS *=/s#=.*#=$LIBS#
++/^STATICLIB *=/s#=.*#=$STATICLIB#
+ /^SHAREDLIB *=/s#=.*#=$SHAREDLIB#
+ /^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV#
+ /^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM#
++/^IMPORTLIB *=/s#=.*#=$IMPORTLIB#
++/^SHAREDLIBPOST *=/s#=.*#=$SHAREDLIBPOST#
++/^SHAREDTARGET *=/s#=.*#=$SHAREDTARGET#
+ /^AR *=/s#=.*#=$AR#
+ /^RANLIB *=/s#=.*#=$RANLIB#
+ /^EXE *=/s#=.*#=$EXE#
+ /^prefix *=/s#=.*#=$prefix#
+ /^exec_prefix *=/s#=.*#=$exec_prefix#
+ /^libdir *=/s#=.*#=$libdir#
++/^bindir *=/s#=.*#=$bindir#
+ /^includedir *=/s#=.*#=$includedir#
+ /^mandir *=/s#=.*#=$mandir#
+ /^LDFLAGS *=/s#=.*#=$LDFLAGS#
diff --git a/w32/patches/zlib/02-cross-build.patch b/w32/patches/zlib/02-cross-build.patch
new file mode 100644
index 0000000..9e8ae86
--- /dev/null
+++ b/w32/patches/zlib/02-cross-build.patch
@@ -0,0 +1,61 @@
+diff --git a/configure b/configure
+index b81ed63..864bba2 100755
+--- a/configure
++++ b/configure
+@@ -41,10 +41,10 @@ old_cflags="$CFLAGS"
+ while test $# -ge 1
+ do
+ case "$1" in
+-    -h* | --h*)
++    -h | -help | --help)
+       echo 'usage:'
+       echo '  configure [--shared] [--prefix=PREFIX]  [--exec_prefix=EXPREFIX]'
+-      echo '     [--libdir=LIBDIR] [--includedir=INCLUDEDIR]'
++      echo '     [--libdir=LIBDIR] [--includedir=INCLUDEDIR] [--host=HOST]'
+         exit 0;;
+     -p*=* | --p*=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+     -e*=* | --e*=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+@@ -55,6 +55,8 @@ case "$1" in
+     -l* | --l*) libdir="$2"; shift; shift;;
+     -i* | --i*) includedir="$2"; shift; shift;;
+     -s* | --s*) shared=1; shift;;
++    -host=* | --host=*) host=`echo $1 | sed 's/[-a-z_]*=//'`;shift;;
++    -host | --host) host="$2"; shift; shift;;
+     *) echo "unknown option: $1"; echo "$0 --help for help"; exit 1;;
+     esac
+ done
+@@ -66,7 +68,8 @@ int hello() {return getchar();}
+ EOF
+ 
+ test -z "$CC" && echo Checking for gcc...
+-cc=${CC-gcc}
++test -n "$host" && cross_gcc=$host-gcc
++cc=${CC-${cross_gcc-gcc}}
+ cflags=${CFLAGS-"-O3"}
+ # to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
+ case "$cc" in
+@@ -74,13 +77,13 @@ case "$cc" in
+ esac
+ 
+ BUILDPLATFORM=`(uname -s || echo unknown) 2>/dev/null`;
+-TARGETPLATFORM=${target-${BUILDPLATFORM}}
++HOSTPLATFORM=${host-${BUILDPLATFORM}}
+ 
+ if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+   CC="$cc"
+   SFLAGS=${CFLAGS-"-fPIC -O3"}
+   CFLAGS="$cflags"
+-  case ${TARGETPLATFORM} in
++  case ${HOSTPLATFORM} in
+   Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1"};;
+   OS/2* )
+              EXE='.exe';;
+@@ -184,7 +187,7 @@ else
+              LDSHARED=${LDSHARED-"cc -shared"};;
+   esac
+ fi
+-case ${TARGETPLATFORM} in
++case ${HOSTPLATFORM} in
+   *CYGWIN* | *Cygwin* | *cygwin* )
+     # On cygwin, we always build both shared and static libs
+     LIBS="${SHAREDLIB} ${STATICLIB}"
diff --git a/w32/recipes/atk.recipe b/w32/recipes/atk.recipe
new file mode 100644
index 0000000..5be1e3b
--- /dev/null
+++ b/w32/recipes/atk.recipe
@@ -0,0 +1,6 @@
+name=atk
+major=1.33
+minor=6
+release=1
+version=$major.$minor
+download=http://ftp.gnome.org/pub/gnome/sources/$name/$major
diff --git a/w32/recipes/cairo.recipe b/w32/recipes/cairo.recipe
new file mode 100644
index 0000000..8654d5c
--- /dev/null
+++ b/w32/recipes/cairo.recipe
@@ -0,0 +1,8 @@
+name=cairo
+version=1.8.8
+release=1
+download=http://www.cairographics.org/releases
+
+case "$host" in
+  *mingw*) configure_opt=--without-x ;;
+esac
diff --git a/w32/recipes/gd.recipe b/w32/recipes/gd.recipe
new file mode 100644
index 0000000..07877d9
--- /dev/null
+++ b/w32/recipes/gd.recipe
@@ -0,0 +1,30 @@
+name=gd
+version=2.1.0
+release=1
+download=https://github.com/libgd/libgd/releases/download/gd-2.1.0
+#https://github.com/libgd/libgd/releases/download/gd-2.1.0/libgd-2.1.0.tar.gz
+
+configure_opt="
+  --disable-static
+  --enable-shared
+  --without-x
+"
+
+src_prep()
+{
+  autoreconf -ivf
+}
+
+# Compatibility with existing binary package
+src_install()
+{
+  do_install
+  
+  case "$host" in
+    *mingw*) ln -f $resultdir/lib/libgd.dll.a $resultdir/lib/bgd.lib ;;
+  esac
+  
+  mkdir -p $local_tooldir
+  cp $tooldir/tool.template $local_tooldir/gdlib-config
+}
+
diff --git a/w32/recipes/gettext.recipe b/w32/recipes/gettext.recipe
new file mode 100644
index 0000000..49b35f7
--- /dev/null
+++ b/w32/recipes/gettext.recipe
@@ -0,0 +1,8 @@
+name=gettext
+version=0.17
+release=1
+origin=gnu
+configure_opt=--enable-relocatable
+
+# Solve an incompatibility with newer MinGW runtimes
+export ac_cv_func_getopt_long_only=no
diff --git a/w32/recipes/glib.recipe b/w32/recipes/glib.recipe
new file mode 100644
index 0000000..3af4820
--- /dev/null
+++ b/w32/recipes/glib.recipe
@@ -0,0 +1,6 @@
+name=glib
+major=2.28
+minor=1
+release=1
+version=$major.$minor
+download=http://ftp.gnome.org/pub/gnome/sources/$name/$major
diff --git a/w32/recipes/gtk+.recipe b/w32/recipes/gtk+.recipe
new file mode 100644
index 0000000..9aeb0d5
--- /dev/null
+++ b/w32/recipes/gtk+.recipe
@@ -0,0 +1,12 @@
+name=gtk+
+major=2.24
+minor=13
+release=1
+version=$major.$minor
+download=http://ftp.gnome.org/pub/gnome/sources/$name/$major
+source_package_type=tar.xz
+
+configure_opt="
+  --disable-cups
+  --with-included-immodules
+"
diff --git a/w32/recipes/jpeg.recipe b/w32/recipes/jpeg.recipe
new file mode 100644
index 0000000..b305399
--- /dev/null
+++ b/w32/recipes/jpeg.recipe
@@ -0,0 +1,5 @@
+name=jpeg
+version=7
+release=1
+download=http://www.ijg.org/files
+source=jpegsrc.v7.tar.gz
diff --git a/w32/recipes/libiconv.recipe b/w32/recipes/libiconv.recipe
new file mode 100644
index 0000000..747de81
--- /dev/null
+++ b/w32/recipes/libiconv.recipe
@@ -0,0 +1,4 @@
+name=libiconv
+version=1.13.1
+release=1
+origin=gnu
diff --git a/w32/recipes/libpng.recipe b/w32/recipes/libpng.recipe
new file mode 100644
index 0000000..a621866
--- /dev/null
+++ b/w32/recipes/libpng.recipe
@@ -0,0 +1,13 @@
+name=libpng
+version=1.2.40
+release=1
+origin=sf
+make_opts="SYMBOL_PREFIX="
+
+src_install()
+{
+  do_install
+  mkdir -p $local_tooldir
+  cp $tooldir/tool.template $local_tooldir/libpng-config
+  cp $tooldir/tool.template $local_tooldir/libpng12-config
+}
diff --git a/w32/recipes/pango.recipe b/w32/recipes/pango.recipe
new file mode 100644
index 0000000..5cd9212
--- /dev/null
+++ b/w32/recipes/pango.recipe
@@ -0,0 +1,9 @@
+name=pango
+version=1.26.0
+release=1
+download=http://ftp.gnome.org/pub/gnome/sources/pango/1.26
+
+configure_opt="
+  --with-included-modules
+  CXX=${host:+$host-g++}  
+"
diff --git a/w32/recipes/pcb.recipe b/w32/recipes/pcb.recipe
new file mode 100644
index 0000000..629883e
--- /dev/null
+++ b/w32/recipes/pcb.recipe
@@ -0,0 +1,18 @@
+name=pcb
+
+pkg_builddir=$(cd $bindir/.. && pwd)
+
+configure_opt="--disable-update-desktop-database --disable-update-mime-database --disable-dbus"
+
+# configure-time test fails because of stdcall signature
+export ac_cv_func_gdImageGif=yes
+export ac_cv_func_gdImageJpeg=yes
+export ac_cv_func_gdImagePng=yes
+
+src_configure()
+{
+  if [ ! -f configure ]; then
+    ./autogen.sh || return 1
+  fi
+  do_configure
+}
diff --git a/w32/recipes/pixman.recipe b/w32/recipes/pixman.recipe
new file mode 100644
index 0000000..73fa26c
--- /dev/null
+++ b/w32/recipes/pixman.recipe
@@ -0,0 +1,4 @@
+name=pixman
+version=0.16.2
+release=1
+download=http://www.cairographics.org/releases
diff --git a/w32/recipes/pkg-config.recipe b/w32/recipes/pkg-config.recipe
new file mode 100644
index 0000000..80a17e1
--- /dev/null
+++ b/w32/recipes/pkg-config.recipe
@@ -0,0 +1,4 @@
+name=pkg-config
+version=0.29
+release=1
+download=https://pkgconfig.freedesktop.org/releases/
diff --git a/w32/recipes/tiff.recipe b/w32/recipes/tiff.recipe
new file mode 100644
index 0000000..44c0ffd
--- /dev/null
+++ b/w32/recipes/tiff.recipe
@@ -0,0 +1,4 @@
+name=tiff
+version=3.9.4
+release=1
+download=ftp://ftp.remotesensing.org/pub/libtiff
diff --git a/w32/recipes/zlib.recipe b/w32/recipes/zlib.recipe
new file mode 100644
index 0000000..c143197
--- /dev/null
+++ b/w32/recipes/zlib.recipe
@@ -0,0 +1,6 @@
+name=zlib
+version=1.2.3
+release=1
+pkg_download=http://prdownloads.sourceforge.net/libpng/zlib-1.2.3.tar.gz?download
+unset build
+configure_no_more_flags=true
diff --git a/w32/todo.tmpasm b/w32/todo.tmpasm
new file mode 100644
index 0000000..9ac2030
--- /dev/null
+++ b/w32/todo.tmpasm
@@ -0,0 +1,14 @@
+# We are building on win32; compile in some icons for the
+# desktop and application toolbar
+
+append /local/pcb/OBJS  {pcb_icon.o}
+
+append /local/pcb/RULES  [@
+# win32
+pcb_icon.o : pcb_icon.ico $(srcdir)/hid/gtk/pcb.rc
+	$(WINDRES) $(srcdir)/hid/gtk/pcb.rc $@
+
+pcb_icon.ico: $(top_srcdir)/data/pcb_icon.ico
+	cp $(top_srcdir)/data/pcb_icon.ico $@
+
+@]
\ No newline at end of file
diff --git a/w32/tools/mpk-build b/w32/tools/mpk-build
new file mode 100644
index 0000000..d90f658
--- /dev/null
+++ b/w32/tools/mpk-build
@@ -0,0 +1,123 @@
+# mpk-build - Build packages.
+# Copyright (C) 2008,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+build_source()
+{
+  : ${top_srcdir:=$name-$version}
+
+  : ${pkg_builddir:=$builddir/$name-$version-$release/$top_srcdir}
+
+  if [ ! -d $pkg_builddir ]; then
+    $mpk unpack $pkg
+    if [ $? != 0 -o ! -d $pkg_builddir ]; then
+      echo >&2 "$(basename $0) build: Could not unpack $pkg"
+      exit 1
+    fi
+  fi
+  
+  cd $pkg_builddir
+  
+  src_build && success
+}
+
+src_build()
+{
+  do_build
+}
+
+src_configure()
+{
+  do_configure
+}
+
+src_compile()
+{
+  do_compile
+}
+
+src_install()
+{
+  do_install
+}
+
+do_build()
+{
+  src_configure || fail
+  src_compile || fail
+  src_install || fail
+}
+
+do_configure()
+{
+  setup_configure_options
+
+  echo "Configuring $pkg..."
+  mkdir -p ../logs
+  echo "$pkg_configure_opt" | xargs ./configure > ../logs/configure.log
+}
+
+do_compile()
+{
+  echo "Compiling $pkg..."
+  mkdir -p ../logs
+  make $default_make_opts $make_opts > ../logs/make.log
+}
+
+do_install()
+{
+  echo "Installing $pkg..."
+  mkdir -p ../logs
+  make install > ../logs/install.log
+}
+
+fail()
+{
+  echo
+  echo "================="
+  echo "$pkg:"
+  echo "  Build failed."
+  echo "================="
+  echo
+  exit 1
+}
+
+success()
+{
+  echo
+  echo "===================="
+  echo "$pkg:"
+  echo "  Build succeeded."
+  echo "===================="
+  echo
+}
+
+if [ -z "$1" ]; then
+  echo "Usage: $(basename $0) build package-name"
+  exit 1
+fi
+pkg=$1
+
+if [ -z "$pkg" ]; then
+  echo "Usage: $(basename $0) build package-name ..."
+  exit 1
+fi
+
+recipe=$(get_recipe_name $pkg) || exit 1
+
+. $recipe
+
+build_source
+
diff --git a/w32/tools/mpk-clean b/w32/tools/mpk-clean
new file mode 100644
index 0000000..08cc285
--- /dev/null
+++ b/w32/tools/mpk-clean
@@ -0,0 +1,38 @@
+# mpk-clean - Clean build directory.
+# Copyright (C) 2009,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+clean()
+{
+  buildroot=$builddir/$name-$version-$release
+  rm -rf "$buildroot"  
+}
+
+if [ -z "$1" ]; then
+  echo "Usage: $(basename $0) clean package-name"
+  exit 1
+fi
+pkg=$1
+
+if [ -z "$pkg" ]; then
+  echo "Usage: $(basename $0) clean package-name ..."
+  exit 1
+fi
+
+recipe=$(get_recipe_name $pkg) || exit 1
+
+. $recipe
+
+clean
diff --git a/w32/tools/mpk-config.guess b/w32/tools/mpk-config.guess
new file mode 100755
index 0000000..278f9e9
--- /dev/null
+++ b/w32/tools/mpk-config.guess
@@ -0,0 +1,1516 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+#   Inc.
+
+timestamp='2007-07-22'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per at bothner.com>.
+# Please send patches to <config-patches at gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches at gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+	for c in cc gcc c89 c99 ; do
+	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi at noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	sysctl="sysctl -n hw.machine_arch"
+	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	case "${UNAME_MACHINE_ARCH}" in
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE_ARCH}" in
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case "${UNAME_VERSION}" in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit ;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+	exit ;;
+    *:ekkoBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+	exit ;;
+    *:SolidBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+	exit ;;
+    macppc:MirBSD:*:*)
+	echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+	exit ;;
+    *:MirBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+	exit ;;
+    alpha:OSF1:*:*)
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+	        UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case "$ALPHA_CPU_TYPE" in
+	    "EV4 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE="alphaev5" ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE="alphaev56" ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE="alphapca56" ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE="alphapca57" ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE="alphaev6" ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE="alphaev67" ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE="alphaev69" ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE="alphaev7" ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE="alphaev79" ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-morphos
+	exit ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit ;;
+    *:z/VM:*:*)
+	echo s390-ibm-zvmoe
+	exit ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+	exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+	echo arm-unknown-riscos
+	exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee at wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit ;;
+    DRS?6000:unix:4.0:6*)
+	echo sparc-icl-nx6
+	exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) echo sparc-icl-nx7; exit ;;
+	esac ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit ;;
+    m68k:machten:*:*)
+	echo m68k-apple-machten${UNAME_RELEASE}
+	exit ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c &&
+	  dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`$dummy $dummyarg` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit ;;
+    Motorola:*:4.3:PL8-*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval $set_cc_for_build
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+		then
+			echo "$SYSTEM_NAME"
+		else
+			echo rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if [ -x /usr/bin/getconf ]; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+		fi
+		if [ "${HP_ARCH}" = "" ]; then
+		    eval $set_cc_for_build
+		    sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if [ ${HP_ARCH} = "hppa2.0w" ]
+	then
+	    eval $set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep __LP64__ >/dev/null
+	    then
+		HP_ARCH="hppa2.0w"
+	    else
+		HP_ARCH="hppa64"
+	    fi
+	fi
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit ;;
+    3050*:HI-UX:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	echo unknown-hitachi-hiuxwe2
+	exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    *:UNICOS/mp:*:*)
+	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit ;;
+    *:FreeBSD:*:*)
+	case ${UNAME_MACHINE} in
+	    pc98)
+		echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	    amd64)
+		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	    *)
+		echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	esac
+	exit ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit ;;
+    *:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit ;;
+    i*:windows32*:*)
+    	# uname -m includes "-pc" on this system.
+    	echo ${UNAME_MACHINE}-mingw32
+	exit ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit ;;
+    *:Interix*:[3456]*)
+    	case ${UNAME_MACHINE} in
+	    x86)
+		echo i586-pc-interix${UNAME_RELEASE}
+		exit ;;
+	    EM64T | authenticamd)
+		echo x86_64-unknown-interix${UNAME_RELEASE}
+		exit ;;
+	esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+	echo i${UNAME_MACHINE}-pc-mks
+	exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i586-pc-interix
+	exit ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	echo x86_64-unknown-cygwin
+	exit ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    *:GNU:*:*)
+	# the GNU system
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit ;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+	exit ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    avr32*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    cris:Linux:*:*)
+	echo cris-axis-linux-gnu
+	exit ;;
+    crisv32:Linux:*:*)
+	echo crisv32-axis-linux-gnu
+	exit ;;
+    frv:Linux:*:*)
+    	echo frv-unknown-linux-gnu
+	exit ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    m32r*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    mips:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=mipsel
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=mips
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+	    /^CPU/{
+		s: ::g
+		p
+	    }'`"
+	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+	;;
+    mips64:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef mips64
+	#undef mips64el
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=mips64el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=mips64
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+	    /^CPU/{
+		s: ::g
+		p
+	    }'`"
+	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+	;;
+    or32:Linux:*:*)
+	echo or32-unknown-linux-gnu
+	exit ;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-gnu
+	exit ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit ;;
+    sh64*:Linux:*:*)
+    	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    vax:Linux:*:*)
+	echo ${UNAME_MACHINE}-dec-linux-gnu
+	exit ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit ;;
+    xtensa:Linux:*:*)
+    	echo xtensa-unknown-linux-gnu
+	exit ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	# Set LC_ALL=C to ensure ld outputs messages in English.
+	ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit ;;
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <features.h>
+	#ifdef __ELF__
+	# ifdef __GLIBC__
+	#  if __GLIBC__ >= 2
+	LIBC=gnu
+	#  else
+	LIBC=gnulibc1
+	#  endif
+	# else
+	LIBC=gnulibc1
+	# endif
+	#else
+	#if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+	LIBC=gnu
+	#else
+	LIBC=gnuaout
+	#endif
+	#endif
+	#ifdef __dietlibc__
+	LIBC=dietlibc
+	#endif
+EOF
+	eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+	    /^LIBC/{
+		s: ::g
+		p
+	    }'`"
+	test x"${LIBC}" != x && {
+		echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+		exit
+	}
+	test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit ;;
+    i*86:XTS-300:*:STOP)
+	echo ${UNAME_MACHINE}-unknown-stop
+	exit ;;
+    i*86:atheos:*:*)
+	echo ${UNAME_MACHINE}-unknown-atheos
+	exit ;;
+    i*86:syllable:*:*)
+	echo ${UNAME_MACHINE}-pc-syllable
+	exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit ;;
+    i*86:*:5:[678]*)
+    	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	echo m68k-convergent-sysv
+	exit ;;
+    M680?0:D-NIX:5.3:*)
+	echo m68k-diab-dnix
+	exit ;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && { echo i486-ncr-sysv4; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel at ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes at openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit ;;
+    *:*:*:FTX*)
+	# From seanf at swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit ;;
+    i*86:VOS:*:*)
+	# From Paul.Green at stratus.com.
+	echo ${UNAME_MACHINE}-stratus-vos
+	exit ;;
+    *:VOS:*:*)
+	# From Paul.Green at stratus.com.
+	echo hppa1.1-stratus-vos
+	exit ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-6:SUPER-UX:*:*)
+	echo sx6-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-7:SUPER-UX:*:*)
+	echo sx7-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-8:SUPER-UX:*:*)
+	echo sx8-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-8R:SUPER-UX:*:*)
+	echo sx8r-nec-superux${UNAME_RELEASE}
+	exit ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit ;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+	case $UNAME_PROCESSOR in
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+	exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = "x86"; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+	exit ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+	echo nse-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+	exit ;;
+    *:DragonFly:*:*)
+	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit ;;
+    *:*VMS:*:*)
+    	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case "${UNAME_MACHINE}" in
+	    A*) echo alpha-dec-vms ; exit ;;
+	    I*) echo ia64-dec-vms ; exit ;;
+	    V*) echo vax-dec-vms ; exit ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	echo i386-pc-xenix
+	exit ;;
+    i*86:skyos:*:*)
+	echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+	exit ;;
+    i*86:rdos:*:*)
+	echo ${UNAME_MACHINE}-pc-rdos
+	exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+	{ echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    c34*)
+	echo c34-convex-bsd
+	exit ;;
+    c38*)
+	echo c38-convex-bsd
+	exit ;;
+    c4*)
+	echo c4-convex-bsd
+	exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches at gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/w32/tools/mpk-help b/w32/tools/mpk-help
new file mode 100644
index 0000000..e74236a
--- /dev/null
+++ b/w32/tools/mpk-help
@@ -0,0 +1,13 @@
+echo "Usage:
+* $prog build <package>   - Download, unpack, and build a package.
+* $prog unpack <package>  - Download and unpack a package.
+* $prog source <package>  - Download a source package.
+* $prog remove <package>  - Remove an installed package.
+* $prog install <package> - Install a previously built package.
+* $prog clean <package>   - Clean the build directory of a package.
+* $prog shell [package]   - Enters a shell with a pre-defined environment.
+* $prog --version         - Show the current Minipack version.
+* $prog --help            - Show this screen.
+
+Report bugs to http://code.google.com/p/minipack/issues/list"
+
diff --git a/w32/tools/mpk-install b/w32/tools/mpk-install
new file mode 100644
index 0000000..04f32be
--- /dev/null
+++ b/w32/tools/mpk-install
@@ -0,0 +1,61 @@
+# mpk-install - Install packages.
+# Copyright (C) 2009,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+install()
+{
+  : ${top_srcdir:=$name-$version}
+
+  pkg_builddir=$builddir/$name-$version-$release/$top_srcdir
+
+  if [ ! -d $pkg_builddir ]; then
+    echo >&2 "$(basename $0) install: Could not find the build location of $pkg."
+    fail
+  fi
+  
+  cd $pkg_builddir
+  
+  if [ ! -f Makefile ]; then
+    echo >&2 "$(basename $0) install: $pkg not build yet."
+  fi
+  
+  (make install > ../logs/install.log) || fail
+}
+
+fail()
+{
+  echo
+  echo "================="
+  echo "Install failed."
+  echo "================="
+  exit 1
+}
+
+if [ -z "$1" ]; then
+  echo "Usage: $(basename $0) install package-name"
+  exit 1
+fi
+pkg=$1
+
+if [ -z "$pkg" ]; then
+  echo "Usage: $(basename $0) install package-name ..."
+  exit 1
+fi
+
+recipe=$(get_recipe_name $pkg) || exit 1
+
+. $recipe
+
+install
diff --git a/w32/tools/mpk-remove b/w32/tools/mpk-remove
new file mode 100644
index 0000000..008cc35
--- /dev/null
+++ b/w32/tools/mpk-remove
@@ -0,0 +1,61 @@
+# mpk-remove - Uninstall packages.
+# Copyright (C) 2008,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+remove()
+{
+  : ${top_srcdir:=$name-$version}
+
+  pkg_builddir=$builddir/$name-$version-$release/$top_srcdir
+
+  if [ ! -d $pkg_builddir ]; then
+    echo >&2 "$(basename $0) remove: Could not find the build location of $pkg."
+    fail
+  fi
+  
+  cd $pkg_builddir
+  
+  if [ ! -f Makefile ]; then
+    echo >&2 "$(basename $0) remove: $pkg not build yet."
+  fi
+  
+  (make uninstall > ../logs/remove.log) || fail
+}
+
+fail()
+{
+  echo
+  echo "================="
+  echo "Removal failed."
+  echo "================="
+  exit 1
+}
+
+if [ -z "$1" ]; then
+  echo "Usage: $(basename $0) remove package-name"
+  exit 1
+fi
+pkg=$1
+
+if [ -z "$pkg" ]; then
+  echo "Usage: $(basename $0) remove package-name ..."
+  exit 1
+fi
+
+recipe=$(get_recipe_name $pkg) || exit 1
+
+. $recipe
+
+remove
diff --git a/w32/tools/mpk-shell b/w32/tools/mpk-shell
new file mode 100644
index 0000000..207884a
--- /dev/null
+++ b/w32/tools/mpk-shell
@@ -0,0 +1,69 @@
+# mpk-shell - enters a shell with a pre-defined environment.
+# Copyright (C) 2008,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+create_shell()
+{
+  setup_configure_options
+  
+  export mpk_opt="$pkg_configure_opt"
+  export topdir builddir resultdir
+
+  : ${SHELL:=/bin/sh}
+
+  echo "
+==============
+Minipack shell
+==============
+
+The following variables are set:
+
+mpk_opt=\""
+
+for opt in $mpk_opt; do
+  echo "   $opt"
+done
+
+echo "\"
+topdir=$topdir
+builddir=$builddir
+resultdir=$resultdir
+
+Most software can be built like this:
+
+   echo \"\$mpk_opt\" | xargs ./configure
+   make
+   make install
+
+[To exit the Minipack shell, type 'exit'].
+"
+
+$SHELL --login -i
+
+echo "
+========================
+Minipack shell finished.
+========================
+"
+}
+
+pkg=$1
+
+if [ -n "$pkg" ]; then
+  recipe=$(get_recipe_name $pkg) || exit 1
+  . $recipe
+fi
+
+create_shell
diff --git a/w32/tools/mpk-source b/w32/tools/mpk-source
new file mode 100644
index 0000000..7d6e363
--- /dev/null
+++ b/w32/tools/mpk-source
@@ -0,0 +1,85 @@
+# mpk-source - downloads source code.
+# Copyright (C) 2008,2010 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+get_source()
+{
+  do_get_source
+}
+
+do_get_source()
+{
+  : ${source_package_type:=tar.gz}
+  : ${source:=$name-$version.$source_package_type}
+  : ${gnu_mirror:=http://ftp.gnu.org/pub/gnu}
+
+  if [ -z "$force" -a -e $sourcedir/$source ]; then
+    exit 0
+  fi
+
+  if [ -n "$origin" ]; then
+    case $origin in
+      gnu) pkg_download="$gnu_mirror/$name/$source";;
+      sf) pkg_download="http://downloads.sourceforge.net/sourceforge/$name/$source";;
+      *) echo >&2 "$name: Unknown origin."; exit 1;;
+    esac
+  fi
+
+  if test -z "$download"
+  then
+    exit 0
+  fi
+
+  if [ -n "$download" ]; then
+    pkg_download=$download/$source
+  fi
+
+  if [ -z "$pkg_download" ]; then
+    echo >&2 "$name: download location not found."
+    exit 1
+  fi
+
+  mkdir -p $sourcedir/partial
+  cd $sourcedir/partial
+  pkg_download_nm=`echo "$pkg_download" | sed "s/[?].*$//;s_^.*/__"`
+  echo Downloading $name from $pkg_download as $pkg_download_nm ...
+  wget --no-check-certificate -c $pkg_download -O $pkg_download_nm
+
+  if [ $? != 0 ]; then
+    echo >&2 "$(basename $0) source: Failed to download $name"
+    exit 1
+  fi
+  
+  mv $sourcedir/partial/$source $sourcedir
+}
+
+case $1 in
+  -f|--force) force=yes; shift;;
+  -*) echo "$(basename $0) source: unrecognized option"; exit 1;;
+esac
+
+pkg=$1
+
+if [ -z "$pkg" ]; then
+  echo "Usage: $(basename $0) source package-name ..."
+  exit 1
+fi
+
+recipe=$(get_recipe_name $pkg) || exit 1
+
+. $recipe
+
+get_source
+
diff --git a/w32/tools/mpk-unpack b/w32/tools/mpk-unpack
new file mode 100644
index 0000000..021adf6
--- /dev/null
+++ b/w32/tools/mpk-unpack
@@ -0,0 +1,100 @@
+# mpk-unpack - unpacks source code.
+# Copyright (C) 2008 Cesar Strauss
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+unpack_source()
+{
+  : ${source_package_type:=tar.gz}
+  : ${source:=$name-$version.$source_package_type}
+  : ${top_srcdir:=$name-$version}
+
+  buildroot=$builddir/$name-$version-$release
+  
+  if [ -d $buildroot/$top_srcdir ]; then
+    exit 0
+  fi
+  
+  sourcename=$sourcedir/$source
+  if [ ! -e $sourcename ]; then
+    $mpk source $pkg
+    if [ ! -e $sourcename ]; then
+      echo >&2 "$(basename $0) unpack: Could not find source for $pkg"
+      exit 1
+    fi
+  fi
+
+  mkdir -p $buildroot
+  cd $buildroot
+  
+  echo "Unpacking $pkg..."
+
+  case $source_package_type in
+    tar.*)
+      tar -xf $sourcename ;;
+    zip)
+      unzip $sourcename ;;
+  esac
+
+  if [ $? != 0 ]; then
+    echo >&2 "$(basename $0) unpack: Failed to unpack $sourcename"
+    exit 1
+  fi
+  
+  cd ${top_srcdir}
+  
+  pkg_patchdir=$patchdir/$name
+  
+  if [ -d $pkg_patchdir ]; then
+    patches=`find $pkg_patchdir -name '*.patch' | sort`
+    for p in $patches; do
+      patch -p1 -i "$p"
+      if [ $? != 0 ]; then
+        echo >&2 "$(basename $0) unpack: Failed apply patches for $sourcename"
+        exit 1
+      fi
+    done
+  fi
+
+  src_prep
+
+  if [ $? != 0 ]; then
+    echo >&2 "$(basename $0) unpack: Failed to prepare build directory for $sourcename"
+    exit 1
+  fi
+  
+}
+
+src_prep()
+{
+  do_prep
+}
+
+do_prep()
+{
+  :
+}
+
+pkg=$1
+
+if [ -z "$pkg" ]; then
+  echo "Usage: $(basename $0) unpack package-name ..."
+  exit 1
+fi
+
+recipe=$(get_recipe_name $pkg) || exit 1
+
+. $recipe
+
+unpack_source
diff --git a/w32/tools/mpk-version b/w32/tools/mpk-version
new file mode 100644
index 0000000..0a7ff61
--- /dev/null
+++ b/w32/tools/mpk-version
@@ -0,0 +1,5 @@
+echo "$prog (Minipack) $MPK_VERSION
+Copyright (C) 2008,2010 Cesar Strauss
+License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law."
diff --git a/w32/tools/tool.template b/w32/tools/tool.template
new file mode 100755
index 0000000..60bec87
--- /dev/null
+++ b/w32/tools/tool.template
@@ -0,0 +1,4 @@
+#! /bin/sh
+
+prog=`basename $0`
+${resultdir}/bin/$prog "$@"

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-electronics/pcb-rnd.git



More information about the Pkg-electronics-commits mailing list